[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 4\nend_of_line = lf\ntrim_trailing_whitespace = true\n\n[{Makefile,*.terminfo,*.go}]\nindent_style = tab\n\n# Autogenerated files with tabs below this line.\n\n[kitty/char-props-data.h]\nindent_style = tab\n\n[kittens/unicode_input/names.h]\nindent_style = tab\n\n[glfw/wayland-*-protocol.{c,h}]\nindent_style = tab\n"
  },
  {
    "path": ".gitattributes",
    "content": "kitty/terminfo.h linguist-generated=true\nterminfo/kitty.termcap linguist-generated=true\nterminfo/kitty.terminfo linguist-generated=true\nterminfo/x/xterm-kitty linguist-generated=true\nkitty/char-props-data.h linguist-generated=true\nkitty_tests/GraphemeBreakTest.json linguist-generated=true\nkitty/charsets.c linguist-generated=true\nkitty/key_encoding.py linguist-generated=true\nkitty/rowcolumn-diacritics.c linguist-generated=true\nkitty/rgb.py linguist-generated=true\nkitty/srgb_gamma.* linguist-generated=true\nkitty/gl-wrapper.* linguist-generated=true\nkitty/glfw-wrapper.* linguist-generated=true\nkitty/color-names.h linguist-generated=true\nkitty/parse-graphics-command.h linguist-generated=true\nkitty/parse-multicell-command.h linguist-generated=true\nkitty/options/types.py linguist-generated=true\nkitty/options/parse.py linguist-generated=true\nkitty/options/to-c-generated.h linguist-generated=true\nkittens/diff/options/types.py linguist-generated=true\nkittens/diff/options/parse.py linguist-generated=true\nglfw/*.c linguist-vendored=true\nglfw/*.h linguist-vendored=true\n3rdparty/** linguist-vendored=true\nkittens/unicode_input/names.h linguist-generated=true\ntools/wcswidth/char-props-data.go linguist-generated=true\ntools/unicode_names/names.txt linguist-generated=true\nterminfo/kitty.term* linguist-generated=true\nterminfo/x/* linguist-generated=true\n*_generated.* linguist-generated=true\n*_generated_test.* linguist-generated=true\n\n*.py  text diff=python\n*.m  text diff=objc\n*.go text diff=go\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: kovidgoyal\npatreon: kovidgoyal\nliberapay: kovidgoyal\ncustom: https://sw.kovidgoyal.net/kitty/support.html\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a bug report\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. XXX\n2. YYY\n3. ZZZ\n4. See error\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Environment details**\n```\nPress Ctrl+Shift+F6 (cmd+option+comma on macOS) in kitty, to copy debug output about kitty and its\nconfiguration to the clipboard and paste it here.\n\nOn older versions of kitty, run kitty --debug-config instead\n```\n\n**Additional context**\nTry to reproduce the problem with `kitty --config NONE` if you cannot then post a minimal kitty.conf that reproduces the problem. If the problem involves interaction with some other terminal program post a minimal config for that program to reproduce the problem as well.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/copilot-instructions.md",
    "content": "Before implementing any code changes or responding to a request, run the\nfollowing three commands:\n\nsudo apt-get install -y libgl1-mesa-dev libxi-dev libxrandr-dev libxinerama-dev ca-certificates libxcursor-dev libxcb-xkb-dev libdbus-1-dev libxkbcommon-dev libharfbuzz-dev libx11-xcb-dev zsh libpng-dev liblcms2-dev libfontconfig-dev libxkbcommon-x11-dev libcanberra-dev libxxhash-dev uuid-dev libsimde-dev libsystemd-dev libcairo2-dev zsh bash dash systemd-coredump gdb\nsudo chmod -R og-w /usr/share/zsh\n./dev.sh build\n\nThis will download needed dependencies, then create all generated files\nand build the project, making it ready for inspection.\n\n# Repository Build & Test Instructions\n\n## Build Procedures\n- **Build command:** Run `./dev.sh build` to build the project\n- Run `gen/config.py` to update generated config parsing code for both kitty\n  and kitten. The `gen/go_code.py` generator is run automatically by the build\n  command to keep generated Go code files up to date.\n- To build individual kittens use the build command above **do not** try to run go build\n  yourself.\n\nOnce a build is done, the kitty and kitten binaries will be in the `kitty/launcher` directory.\nNote that the kitty binary can run python files using `kitty +launch file.py`.\nWhen it does so the kitty fast_data_types module is available to the python\ncode.\n\n## Test Procedures\n- To run the complete test suite, run `./test.py`\n- To run a specific test, run `./test.py test-name` t\n  `test-name` is the name of the test without the\n  leading `test_` for Python tests and without the leading `Test` for Go tests.\n- Do not use go test or ./setup.py test to run tests\n\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"gomod\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"weekly\"\n    groups:\n      all-go-deps:\n        patterns:\n          - \"*\"  # group all non-security update PRs\n    cooldown:\n      default-days: 7\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      actions:\n        patterns:\n          - \"*\"\n    cooldown:\n      default-days: 7"
  },
  {
    "path": ".github/workflows/ci.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport glob\nimport io\nimport lzma\nimport os\nimport shlex\nimport shutil\nimport subprocess\nimport sys\nimport tarfile\nimport time\nfrom urllib.request import Request, urlopen\n\nBUNDLE_URL = 'https://download.calibre-ebook.com/ci/kitty/{}-64.tar.xz'\nFONTS_URL = 'https://download.calibre-ebook.com/ci/fonts.tar.xz'\nNERD_URL = 'https://github.com/ryanoasis/nerd-fonts/releases/latest/download/NerdFontsSymbolsOnly.tar.xz'\nis_bundle = os.environ.get('KITTY_BUNDLE') == '1'\nis_codeql = os.environ.get('KITTY_CODEQL') == '1'\nis_macos = 'darwin' in sys.platform.lower()\nSW = ''\n\n\ndef do_print_crash_reports() -> None:\n    print('Printing available crash reports...')\n    if is_macos:\n        end_time = time.monotonic() + 90\n        while time.monotonic() < end_time:\n            time.sleep(1)\n            items = glob.glob(os.path.join(os.path.expanduser('~/Library/Logs/DiagnosticReports'), 'kitty-*.ips'))\n            if items:\n                break\n        if items:\n            time.sleep(1)\n            print(os.path.basename(items[0]))\n            sdir = os.path.dirname(os.path.abspath(__file__))\n            subprocess.check_call([sys.executable, os.path.join(sdir, 'macos_crash_report.py'), items[0]])\n    else:\n        run('sh -c \"echo bt | coredumpctl debug\"')\n    print(flush=True)\n\n\ndef run(*a: str, print_crash_reports: bool = False) -> None:\n    if len(a) == 1:\n        a = tuple(shlex.split(a[0]))\n    cmd = ' '.join(map(shlex.quote, a))\n    print(cmd)\n    sys.stdout.flush()\n    ret = subprocess.Popen(a).wait()\n    if ret != 0:\n        if ret < 0:\n            import signal\n            try:\n                sig = signal.Signals(-ret)\n            except ValueError:\n                pass\n            else:\n                if print_crash_reports:\n                    do_print_crash_reports()\n                raise SystemExit(f'The following process was killed by signal: {sig.name}:\\n{cmd}')\n        raise SystemExit(f'The following process failed with exit code: {ret}:\\n{cmd}')\n\n\ndef download_with_retry(url: str | Request, count: int = 5) -> bytes:\n    for i in range(count):\n        try:\n            print('Downloading', getattr(url, 'full_url', url), flush=True)\n            with urlopen(url) as f:\n                ans: bytes = f.read()\n            return ans\n        except Exception as err:\n            if getattr(err, 'code', -1) == 403:\n                raise\n            if i >= count - 1:\n                raise\n            print(f'Download failed with error {err} retrying...', file=sys.stderr)\n            time.sleep(1)\n    return b''\n\n\ndef install_fonts() -> None:\n    data = download_with_retry(FONTS_URL)\n    fonts_dir = os.path.expanduser('~/Library/Fonts' if is_macos else '~/.local/share/fonts')\n    os.makedirs(fonts_dir, exist_ok=True)\n    with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as tf:\n        try:\n            tf.extractall(fonts_dir, filter='fully_trusted')\n        except TypeError:\n            tf.extractall(fonts_dir)\n    data = download_with_retry(NERD_URL)\n    with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as tf:\n        try:\n            tf.extractall(fonts_dir, filter='fully_trusted')\n        except TypeError:\n            tf.extractall(fonts_dir)\n\n\ndef install_deps() -> None:\n    print('Installing kitty dependencies...')\n    sys.stdout.flush()\n    if is_macos:\n        if not is_codeql:  # for some reason brew fails on CodeQL we dont need it anyway\n            items = [x.split()[1].strip('\"') for x in open('Brewfile').readlines() if x.strip().startswith('brew ')]\n            openssl = 'openssl'\n            items.remove('go')  # already installed by ci.yml\n            import ssl\n            if ssl.OPENSSL_VERSION_INFO[0] == 1:\n                openssl += '@1.1'\n            run('brew', 'install', 'fish', openssl, *items)\n    else:\n        run('sudo apt-get update')\n        run('sudo apt-get install -y libgl1-mesa-dev libxi-dev libxrandr-dev libxinerama-dev ca-certificates'\n            ' libxcursor-dev libxcb-xkb-dev libdbus-1-dev libxkbcommon-dev libharfbuzz-dev libx11-xcb-dev zsh'\n            ' libpng-dev liblcms2-dev libfontconfig-dev libxkbcommon-x11-dev libcanberra-dev libxxhash-dev uuid-dev'\n            ' libsimde-dev libsystemd-dev libcairo2-dev zsh bash dash systemd-coredump gdb')\n        # for some reason these directories are world writable which causes zsh\n        # compinit to break\n        run('sudo chmod -R og-w /usr/share/zsh')\n    if is_bundle:\n        install_bundle()\n    else:\n        cmd = 'python3 -m pip install Pillow pygments'\n        if sys.version_info[:2] < (3, 7):\n            cmd += ' importlib-resources dataclasses'\n        run(cmd)\n    install_fonts()\n\n\ndef build_kitty() -> None:\n    python = shutil.which('python3') if is_bundle else sys.executable\n    cmd = f'{python} setup.py build --verbose'\n    if is_macos:\n        cmd += ' --debug'  # for better crash report to debug SIGILL issue\n    if os.environ.get('KITTY_SANITIZE') == '1':\n        cmd += ' --debug --sanitize'\n    run(cmd)\n\n\ndef test_kitty() -> None:\n    if is_macos:\n        run('ulimit -c unlimited')\n        run('sudo chmod -R 777 /cores')\n    run('./test.py', print_crash_reports=True)\n\n\ndef package_kitty() -> None:\n    python = 'python3' if is_macos else 'python'\n    run(f'{python} setup.py linux-package --update-check-interval=0 --verbose')\n    if is_macos:\n        run('python3 setup.py kitty.app --update-check-interval=0 --verbose')\n        run('kitty.app/Contents/MacOS/kitty +runpy \"from kitty.constants import *; print(kitty_exe())\"')\n\n\ndef replace_in_file(path: str, src: str, dest: str) -> None:\n    with open(path, 'r+') as f:\n        n = f.read().replace(src, dest)\n        f.seek(0), f.truncate()\n        f.write(n)\n\n\ndef setup_bundle_env() -> None:\n    global SW\n    os.environ['SW'] = SW = '/Users/Shared/kitty-build/sw/sw' if is_macos else os.path.join(os.environ['GITHUB_WORKSPACE'], 'sw')\n    os.environ['PKG_CONFIG_PATH'] = os.path.join(SW, 'lib', 'pkgconfig')\n    if is_macos:\n        os.environ['PATH'] = '{}:{}'.format('/usr/local/opt/sphinx-doc/bin', os.environ['PATH'])\n    else:\n        os.environ['LD_LIBRARY_PATH'] = os.path.join(SW, 'lib')\n        os.environ['PYTHONHOME'] = SW\n    os.environ['PATH'] = '{}:{}'.format(os.path.join(SW, 'bin'), os.environ['PATH'])\n\n\ndef install_bundle(dest: str = '', which: str = '') -> None:\n    dest = dest or SW\n    cwd = os.getcwd()\n    os.makedirs(dest, exist_ok=True)\n    os.chdir(dest)\n    which = which or ('macos' if is_macos else 'linux')\n    data = download_with_retry(BUNDLE_URL.format(which))\n    with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as tf:\n        try:\n            tf.extractall(filter='fully_trusted')\n        except TypeError:\n            tf.extractall()\n    if not is_macos:\n        replaced = 0\n        for dirpath, dirnames, filenames in os.walk('.'):\n            for f in filenames:\n                if f.endswith('.pc') or (f.endswith('.py') and f.startswith('_sysconfig')):\n                    replace_in_file(os.path.join(dirpath, f), '/sw/sw', dest)\n                    replaced += 1\n        if replaced < 2:\n            raise SystemExit('Failed to replace path to SW in bundle')\n    os.chdir(cwd)\n\n\ndef install_grype(exe: str = '/tmp/grype') -> str:\n    raw = download_with_retry('https://download.calibre-ebook.com/ci/grype.xz')\n    raw = lzma.decompress(raw)\n    with open(exe, 'wb') as f:\n        f.write(raw)\n        os.fchmod(f.fileno(), 0o755)\n    subprocess.check_call([exe, 'db', 'update'])\n    return exe\n\n\nIGNORED_DEPENDENCY_CVES = [\n    # Python stdlib\n    'CVE-2025-8194', # DoS in tarfile\n    'CVE-2025-6069', # DoS in HTMLParser\n    'CVE-2025-13836', # DoS in http client reading from malicious server\n    'CVE-2025-12084', # DoS in xml.dom.minidom unused in kitty\n    'CVE-2025-13837', # DoS in plistlib reading plist. We only use plistlib for writing\n    'CVE-2025-6075',  # Quadratic complexity in os.path.expandvars()\n    # python stdlib all these are erroneously marked as fixed in python 3.15\n    # when it hasnt even been released. Sigh.\n    'CVE-2026-1299',\n    'CVE-2026-0865',\n    'CVE-2025-15282',\n    'CVE-2026-0672',\n    'CVE-2025-15366',\n    'CVE-2025-15367',\n    'CVE-2025-12781',\n    'CVE-2025-11468',\n    'CVE-2026-2297',\n    'CVE-2026-3644',\n    'CVE-2026-4224',\n    # github.com/nwaples/rardecode/v2\n    'CVE-2025-11579', # rardecode is version 2.2.1, not vulnerable\n    'CVE-2026-2673',  # openssl fix not released\n]\n\n\ndef check_dependencies() -> None:\n    grype = install_grype()\n    with open((gc := os.path.expanduser('~/.grype.yml')), 'w') as f:\n        print('ignore:', file=f)\n        for x in IGNORED_DEPENDENCY_CVES:\n            print('  - vulnerability:', x, file=f)\n    dest = os.path.join(SW, 'linux')\n    os.makedirs(dest, exist_ok=True)\n    install_bundle(dest, os.path.basename(dest))\n    dest = os.path.join(SW, 'macos')\n    os.makedirs(dest, exist_ok=True)\n    install_bundle(dest, os.path.basename(dest))\n    cmdline = [grype, '--by-cve', '--config', gc, '--fail-on', 'medium', '--only-fixed', '--add-cpes-if-none']\n    if (subprocess.run(cmdline + ['dir:' + SW])).returncode != 0:\n        raise SystemExit('grype found problems during filesystem scan')\n    # Now test against the SBOM\n    import runpy\n    orig = sys.argv, sys.stdout\n    sys.argv = ['bypy', 'sbom', 'kovidgoyal/kitty', '1.0.0']\n    buf = io.StringIO()\n    sys.stdout = buf\n    runpy.run_path('bypy-src')\n    sys.argv, sys.stdout = orig\n    print(buf.getvalue())\n    if (subprocess.run(cmdline, input=buf.getvalue().encode())).returncode != 0:\n        raise SystemExit('grype found problems during SBOM scan')\n\n\ndef main() -> None:\n    if is_bundle:\n        setup_bundle_env()\n    else:\n        if not is_macos and 'pythonLocation' in os.environ:\n            os.environ['LD_LIBRARY_PATH'] = os.path.join(os.environ['pythonLocation'], 'lib')\n    action = sys.argv[-1]\n    if action in ('build', 'package'):\n        install_deps()\n    if action == 'build':\n        build_kitty()\n    elif action == 'package':\n        package_kitty()\n    elif action == 'test':\n        test_kitty()\n    elif action == 'test':\n        test_kitty()\n    elif action == 'govulncheck':\n        subprocess.check_call(['go', 'install', 'golang.org/x/vuln/cmd/govulncheck@latest'])\n        subprocess.check_call(['govulncheck', '-mode=binary', 'kitty/launcher/kitten'])\n        subprocess.check_call(['govulncheck', './...'])\n    elif action == 'gofmt':\n        q = subprocess.check_output('gofmt -s -l tools kittens'.split()).decode()\n        if q.strip():\n            q = '\\n'.join(filter(lambda x: not x.rstrip().endswith('_generated.go'), q.strip().splitlines())).strip()\n            if q:\n                raise SystemExit(q)\n    elif action == 'check-dependencies':\n        check_dependencies()\n    else:\n        raise SystemExit(f'Unknown action: {action}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\non: [push, pull_request]\nenv:\n    CI: 'true'\n    ASAN_OPTIONS: detect_leaks=0\n    LC_ALL: en_US.UTF-8\n    LANG: en_US.UTF-8\n\npermissions:\n  contents: read # to fetch code (actions/checkout)\n\njobs:\n    linux:\n        name: Linux (python=${{ matrix.pyver }} cc=${{ matrix.cc }} sanitize=${{ matrix.sanitize }})\n        runs-on: ubuntu-latest\n        env:\n            CC: ${{ matrix.cc }}\n            KITTY_SANITIZE: ${{ matrix.sanitize }}\n\n        strategy:\n            matrix:\n                python: [a, b, c]\n                cc: [gcc, clang]\n                include:\n                    - python: a\n                      pyver: \"3.10\"\n                      sanitize: 0\n\n                    - python: b\n                      pyver: \"3.11\"\n                      sanitize: 1\n\n                    - python: c\n                      pyver: \"3.12\"\n                      sanitize: 1\n\n\n                exclude:\n                    - python: a\n                      cc: clang\n                    - python: b\n                      cc: clang\n                    - python: c\n                      cc: gcc\n\n        steps:\n          - name: Checkout source code\n            uses: actions/checkout@v6\n            with:\n              fetch-depth: 10\n              persist-credentials: false\n\n          - name: Set up Python ${{ matrix.pyver }}\n            uses: actions/setup-python@v6\n            with:\n              python-version: ${{ matrix.pyver }}\n\n          - name: Install Go\n            uses: actions/setup-go@v6\n            with:\n              go-version-file: go.mod\n\n          - name: Build kitty\n            run: python .github/workflows/ci.py build\n\n          - name: Test kitty\n            run: python .github/workflows/ci.py test\n\n    linux-package:\n        name: Linux package\n        runs-on: ubuntu-latest\n        env:\n            CFLAGS: -funsigned-char\n        steps:\n          - name: Checkout source code\n            uses: actions/checkout@v6\n            with:\n              fetch-depth: 0  # needed for :commit: docs role\n              persist-credentials: false\n\n          - name: Test for trailing whitespace\n            run: if grep -Inr '\\s$' kitty kitty_tests kittens docs *.py *.asciidoc *.rst *.go .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi\n\n          - name: Test for bad code block formatting\n            run: if grep -Inr ':code:`\\s' kitty kitty_tests kittens docs *.py *.asciidoc *.rst *.go .gitattributes .gitignore; then echo Space at code block start found, aborting.; exit 1; fi\n\n          - name: Set up Python\n            uses: actions/setup-python@v6\n            with:\n              python-version: \"3.13\"\n\n          - name: Install Go\n            uses: actions/setup-go@v6\n            with:\n              go-version-file: go.mod\n              cache: false\n\n          - name: Cache Go build artifacts separately\n            uses: actions/cache@v5\n            with:\n              path: |\n                ~/.cache/go-build\n                ~/go/pkg/mod\n              key: ${{ runner.os }}-golang-static-${{ hashFiles('**/go.sum') }}\n              restore-keys: |\n                ${{ runner.os }}-golang-static-\n\n          - name: Install build-only deps\n            run: python -m pip install -r docs/requirements.txt ruff mypy types-requests types-docutils types-Pygments\n\n          - name: Run ruff\n            run: ruff check .\n\n          - name: Run gofmt\n            run: go version && python .github/workflows/ci.py gofmt\n\n          - name: Build kitty package\n            run: python .github/workflows/ci.py package\n\n          - name: Build kitty\n            run: python setup.py build --debug\n\n          - name: Run mypy\n            run: which python && python -m mypy --version && ./test.py mypy\n\n          - name: Run go vet\n            run: go version && go vet -tags testing ./...\n\n          - name: Build man page\n            run: make FAIL_WARN=1 man\n\n          - name: Build HTML docs\n            run: make FAIL_WARN=1 html\n\n          - name: Build static kittens\n            run: python setup.py build-static-binaries\n\n    bundle:\n        name: Bundle test (${{ matrix.os }})\n        runs-on: ${{ matrix.os }}\n        strategy:\n            matrix:\n                os: [ubuntu-latest, macos-latest]\n        env:\n            KITTY_BUNDLE: 1\n        steps:\n          - name: Checkout source code\n            uses: actions/checkout@v6\n            with:\n              fetch-depth: 10\n              persist-credentials: false\n\n          - name: Install Go\n            uses: actions/setup-go@v6\n            with:\n              go-version-file: go.mod\n\n          - name: Build kitty\n            run: which python3 && python3 .github/workflows/ci.py build\n\n          - name: Test kitty\n            run: python3 .github/workflows/ci.py test\n\n    brew:\n        name: macOS Brew\n        runs-on: macos-latest\n        steps:\n          - name: Checkout source code\n            uses: actions/checkout@v6\n            with:\n              fetch-depth: 0  # needed for :commit: docs role\n              persist-credentials: false\n\n          - name: Set up Python\n            uses: actions/setup-python@v6\n            with:\n              python-version: \"3.11\"\n\n          - name: Install Go\n            uses: actions/setup-go@v6\n            with:\n              go-version-file: go.mod\n\n          - name: Build kitty\n            run: python3 .github/workflows/ci.py build\n\n          - name: Test kitty\n            run: python3 .github/workflows/ci.py test\n\n          - name: Install deps for docs\n            run: python3 -m pip install -r docs/requirements.txt\n\n          - name: Builds docs\n            run: make FAIL_WARN=1 docs\n\n          - name: Build kitty package\n            run: python3 .github/workflows/ci.py package\n\n          - name: Run benchmarks\n            run: ./benchmark.py\n\n    linux-dev:\n        name: Test ./dev.sh and benchmark\n        runs-on: ubuntu-latest\n        steps:\n          - name: Checkout source code\n            uses: actions/checkout@v6\n            with:\n              fetch-depth: 10\n              persist-credentials: false\n\n          - name: Install build deps\n            run: sudo apt-get update && sudo apt-get install -y curl xz-utils build-essential git pkg-config libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl1-mesa-dev libxkbcommon-x11-dev libfontconfig-dev libx11-xcb-dev libdbus-1-dev\n\n          - name: Install Go\n            uses: actions/setup-go@v6\n            with:\n              go-version-file: go.mod\n\n          - name: Build kitty\n            run: ./dev.sh build\n\n          - name: Run benchmarks\n            run: ./benchmark.py\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "name: \"CodeQL\"\n\non:\n    push:\n        branches: [master]\n    pull_request:\n        # The branches below must be a subset of the branches above\n        branches: [master]\n    schedule:\n        - cron: '0 22 * * 5'\n\njobs:\n    CodeQL-Build:\n\n        permissions:\n            contents: read # to fetch code (actions/checkout)\n            security-events: write # to upload SARIF results (github/codeql-action/analyze)\n\n        strategy:\n          fail-fast: false\n          matrix:\n            include:\n               - language: python\n                 os: ubuntu-latest\n               - language: c\n                 os: ubuntu-latest\n               - language: c\n                 os: macos-latest\n               - language: go\n                 os: ubuntu-latest\n               - language: actions\n                 os: ubuntu-latest\n\n        runs-on: ${{ matrix.os }}\n        env:\n            KITTY_BUNDLE: 1\n            KITTY_CODEQL: 1\n\n        steps:\n\n        - name: Checkout repository\n          uses: actions/checkout@v6\n          with:\n              # We must fetch at least the immediate parents so that if this is\n              # a pull request then we can checkout the head.\n              fetch-depth: 2\n              persist-credentials: false\n\n        - name: Install Go\n          if: matrix.language == 'c' || matrix.language == 'go'\n          uses: actions/setup-go@v6\n          with:\n              go-version-file: go.mod\n\n        # Initializes the CodeQL tools for scanning.\n        - name: Initialize CodeQL\n          uses: github/codeql-action/init@v4\n          with:\n              languages: ${{ matrix.language }}\n              trap-caching: false\n\n        - name: Build kitty\n          if: matrix.language == 'c' || matrix.language == 'go'\n          run: python3 .github/workflows/ci.py build\n\n        - name: Perform CodeQL Analysis\n          uses: github/codeql-action/analyze@v4\n\n        - name: Run govulncheck\n          if: matrix.language == 'go'\n          run: python3 .github/workflows/ci.py govulncheck\n"
  },
  {
    "path": ".github/workflows/depscan.yml",
    "content": "name: Depscan\non:\n    push:\n        branches: [master]\n    schedule:\n        - cron: '0 12 * * 5'\n\nenv:\n    CI: 'true'\n    ASAN_OPTIONS: detect_leaks=0\n    LC_ALL: en_US.UTF-8\n    LANG: en_US.UTF-8\n\npermissions:\n  contents: read # to fetch code (actions/checkout)\n\njobs:\n    dependecy-scanner:\n        name: Scan dependencies for vulnerabilities\n        runs-on: ubuntu-latest\n        env:\n            KITTY_BUNDLE: 1\n        steps:\n          - name: Checkout source code\n            uses: actions/checkout@v6\n            with:\n              fetch-depth: 10\n              persist-credentials: false\n\n          - name: Checkout bypy\n            uses: actions/checkout@v6\n            with:\n              fetch-depth: 1\n              persist-credentials: false\n              repository: kovidgoyal/bypy\n              path: bypy-src\n\n          - name: Check dependencies\n            run: python3 .github/workflows/ci.py check-dependencies\n"
  },
  {
    "path": ".github/workflows/macos_crash_report.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport posixpath\nimport sys\nfrom collections import namedtuple\nfrom datetime import datetime\nfrom enum import Enum\nfrom functools import cached_property\nfrom typing import IO, List, Mapping, Optional\n\nFrame = namedtuple('Frame', 'image_name image_base image_offset symbol symbol_offset')\nRegister = namedtuple('Register', 'name value')\n\n\ndef surround(x: str, start: int, end: int) -> str:\n    if sys.stdout.isatty():\n        x = f'\\033[{start}m{x}\\033[{end}m'\n    return x\n\n\ndef cyan(x: str) -> str:\n    return surround(x, 96, 39)\n\n\ndef bold(x: str) -> str:\n    return surround(x, 1, 22)\n\n\nclass BugType(Enum):\n    WatchdogTimeout = '28'\n    BasebandStats = '195'\n    GPUEvent = '284'\n    Sandbox = '187'\n    TerminatingStackshot = '509'\n    ServiceWatchdogTimeout = '29'\n    Session = '179'\n    LegacyStackshot = '188'\n    MACorrelation = '197'\n    iMessages = '189'\n    log_power = '278'\n    PowerLog = 'powerlog'\n    DuetKnowledgeCollector2 = '58'\n    BridgeRestore = '83'\n    LegacyJetsam = '198'\n    ExcResource_385 = '385'\n    Modem = '199'\n    Stackshot = '288'\n    SystemInformation = 'system_profile'\n    Jetsam_298 = '298'\n    MemoryResource = '30'\n    Bridge = '31'\n    DifferentialPrivacy = 'diff_privacy'\n    FirmwareIntegrity = '32'\n    CoreAnalytics_33 = '33'\n    AutoBugCapture = '34'\n    EfiFirmwareIntegrity = '35'\n    SystemStats = '36'\n    AnonSystemStats = '37'\n    Crash_9 = '9'\n    Jetsam_98 = '98'\n    LDCM = '100'\n    Panic_10 = '10'\n    Spin = '11'\n    CLTM = '101'\n    Hang = '12'\n    Panic_110 = '110'\n    ConnectionFailure = '13'\n    MessageTracer = '14'\n    LowBattery = '120'\n    Siri = '201'\n    ShutdownStall = '17'\n    Panic_210 = '210'\n    SymptomsCPUUsage = '202'\n    AssumptionViolation = '18'\n    CoreHandwriting = 'chw'\n    IOMicroStackShot = '44'\n    CoreAnalytics_211 = '211'\n    SiriAppPrediction = '203'\n    spin_45 = '45'\n    PowerMicroStackshots = '220'\n    BTMetadata = '212'\n    SystemMemoryReset = '301'\n    ResetCount = '115'\n    AutoBugCapture_204 = '204'\n    WifiCrashBinary = '221'\n    MicroRunloopHang = '310'\n    Rosetta = '213'\n    glitchyspin = '302'\n    System = '116'\n    IOPowerSources = '141'\n    PanicStats = '205'\n    PowerLog_230 = '230'\n    LongRunloopHang = '222'\n    HomeProductsAnalytics = '311'\n    DifferentialPrivacy_150 = '150'\n    Rhodes = '214'\n    ProactiveEventTrackerTransparency = '303'\n    WiFi = '117'\n    SymptomsCPUWakes = '142'\n    SymptomsCPUUsageFatal = '206'\n    Crash_109 = '109'\n    ShortRunloopHang = '223'\n    CoreHandwriting_231 = '231'\n    ForceReset = '151'\n    SiriAppSelection = '215'\n    PrivateFederatedLearning = '304'\n    Bluetooth = '118'\n    SCPMotion = '143'\n    HangSpin = '207'\n    StepCount = '160'\n    RTCTransparency = '224'\n    DiagnosticRequest = '312'\n    MemorySnapshot = '152'\n    Rosetta_B = '216'\n    AudioAccessory = '305'\n    General = '119'\n    HotSpotIOMicroSS = '144'\n    GeoServicesTransparency = '233'\n    MotionState = '161'\n    AppStoreTransparency = '225'\n    SiriSearchFeedback = '313'\n    BearTrapReserved = '153'\n    Portrait = '217'\n    AWDMetricLog = 'metriclog'\n    SymptomsIO = '145'\n    SubmissionReserved = '170'\n    WifiCrash = '209'\n    Natalies = '162'\n    SecurityTransparency = '226'\n    BiomeMapReduce = '234'\n    MemoryGraph = '154'\n    MultichannelAudio = '218'\n    honeybee_payload = '146'\n    MesaReserved = '171'\n    WifiSensing = '235'\n    SiriMiss = '163'\n    ExcResourceThreads_227 = '227'\n    TestA = 'T01'\n    NetworkUsage = '155'\n    WifiReserved = '180'\n    SiriActionPrediction = '219'\n    honeybee_heartbeat = '147'\n    ECCEvent = '172'\n    KeyTransparency = '236'\n    SubDiagHeartBeat = '164'\n    ThirdPartyHang = '228'\n    OSFault = '308'\n    CoreTime = '156'\n    WifiDriverReserved = '181'\n    Crash_309 = '309'\n    honeybee_issue = '148'\n    CellularPerfReserved = '173'\n    TestB = 'T02'\n    StorageStatus = '165'\n    SiriNotificationTransparency = '229'\n    TestC = 'T03'\n    CPUMicroSS = '157'\n    AccessoryUpdate = '182'\n    xprotect = '20'\n    MultitouchFirmware = '149'\n    MicroStackshot = '174'\n    AppLaunchDiagnostics = '238'\n    KeyboardAccuracy = '166'\n    GPURestart = '21'\n    FaceTime = '191'\n    DuetKnowledgeCollector = '158'\n    OTASUpdate = '183'\n    ExcResourceThreads_327 = '327'\n    ExcResource_22 = '22'\n    DuetDB = '175'\n    ThirdPartyHangDeveloper = '328'\n    PrivacySettings = '167'\n    GasGauge = '192'\n    MicroStackShots = '23'\n    BasebandCrash = '159'\n    GPURestart_184 = '184'\n    SystemWatchdogCrash = '409'\n    FlashStatus = '176'\n    SleepWakeFailure = '24'\n    CarouselEvent = '168'\n    AggregateD = '193'\n    WakeupsMonitorViolation = '25'\n    DifferentialPrivacy_50 = '50'\n    ExcResource_185 = '185'\n    UIAutomation = '177'\n    ping = '26'\n    SiriTransaction = '169'\n    SURestore = '194'\n    KtraceStackshot = '186'\n    WirelessDiagnostics = '27'\n    PowerLogLite = '178'\n    SKAdNetworkAnalytics = '237'\n    HangWorkflowResponsiveness = '239'\n    CompositorClientHang = '243'\n\n\nclass CrashReportBase:\n    def __init__(self, metadata: Mapping, data: str, filename: str = None):\n        self.filename = filename\n        self._metadata = metadata\n        self._data = data\n        self._parse()\n\n    def _parse(self):\n        self._is_json = False\n        try:\n            modified_data = self._data\n            if '\\n  \\n' in modified_data:\n                modified_data, rest = modified_data.split('\\n  \\n', 1)\n                rest = '\",' + rest.split('\",', 1)[1]\n                modified_data += rest\n            self._data = json.loads(modified_data)\n            self._is_json = True\n        except json.decoder.JSONDecodeError:\n            pass\n\n    @cached_property\n    def bug_type(self) -> BugType:\n        return BugType(self.bug_type_str)\n\n    @cached_property\n    def bug_type_str(self) -> str:\n        return self._metadata['bug_type']\n\n    @cached_property\n    def incident_id(self):\n        return self._metadata.get('incident_id')\n\n    @cached_property\n    def timestamp(self) -> datetime:\n        timestamp = self._metadata.get('timestamp')\n        timestamp_without_timezone = timestamp.rsplit(' ', 1)[0]\n        return datetime.strptime(timestamp_without_timezone, '%Y-%m-%d %H:%M:%S.%f')\n\n    @cached_property\n    def name(self) -> str:\n        return self._metadata.get('name')\n\n    def __repr__(self) -> str:\n        filename = ''\n        if self.filename:\n            filename = f'FILENAME:{posixpath.basename(self.filename)} '\n        return f'<{self.__class__} {filename}TIMESTAMP:{self.timestamp}>'\n\n    def __str__(self) -> str:\n        filename = ''\n        if self.filename:\n            filename = self.filename\n\n        return cyan(f'{self.incident_id} {self.timestamp}\\n{filename}\\n\\n')\n\n\nclass UserModeCrashReport(CrashReportBase):\n    def _parse_field(self, name: str) -> str:\n        name += ':'\n        for line in self._data.split('\\n'):\n            if line.startswith(name):\n                field = line.split(name, 1)[1]\n                field = field.strip()\n                return field\n\n    @cached_property\n    def faulting_thread(self) -> int:\n        if self._is_json:\n            return self._data['faultingThread']\n        else:\n            return int(self._parse_field('Triggered by Thread'))\n\n    @cached_property\n    def frames(self) -> List[Frame]:\n        result = []\n        if self._is_json:\n            thread_index = self.faulting_thread\n            images = self._data['usedImages']\n            for frame in self._data['threads'][thread_index]['frames']:\n                image = images[frame['imageIndex']]\n                result.append(\n                    Frame(image_name=image.get('path'), image_base=image.get('base'), symbol=frame.get('symbol'),\n                          image_offset=frame.get('imageOffset'), symbol_offset=frame.get('symbolLocation')))\n        else:\n            in_frames = False\n            for line in self._data.split('\\n'):\n                if in_frames:\n                    splitted = line.split()\n\n                    if len(splitted) == 0:\n                        break\n\n                    assert splitted[-2] == '+'\n                    image_base = splitted[-3]\n                    if image_base.startswith('0x'):\n                        result.append(Frame(image_name=splitted[1], image_base=int(image_base, 16), symbol=None,\n                                            image_offset=int(splitted[-1]), symbol_offset=None))\n                    else:\n                        # symbolicated\n                        result.append(Frame(image_name=splitted[1], image_base=None, symbol=image_base,\n                                            image_offset=None, symbol_offset=int(splitted[-1])))\n\n                if line.startswith(f'Thread {self.faulting_thread} Crashed:'):\n                    in_frames = True\n\n        return result\n\n    @cached_property\n    def registers(self) -> List[Register]:\n        result = []\n        if self._is_json:\n            thread_index = self._data['faultingThread']\n            thread_state = self._data['threads'][thread_index]['threadState']\n\n            if 'x' in thread_state:\n                for i, reg_x in enumerate(thread_state['x']):\n                    result.append(Register(name=f'x{i}', value=reg_x['value']))\n\n            for i, (name, value) in enumerate(thread_state.items()):\n                if name == 'x':\n                    for j, reg_x in enumerate(value):\n                        result.append(Register(name=f'x{j}', value=reg_x['value']))\n                else:\n                    if isinstance(value, dict):\n                        result.append(Register(name=name, value=value['value']))\n        else:\n            in_frames = False\n            for line in self._data.split('\\n'):\n                if in_frames:\n                    splitted = line.split()\n\n                    if len(splitted) == 0:\n                        break\n\n                    for i in range(0, len(splitted), 2):\n                        register_name = splitted[i]\n                        if not register_name.endswith(':'):\n                            break\n\n                        register_name = register_name[:-1]\n                        register_value = int(splitted[i + 1], 16)\n\n                        result.append(Register(name=register_name, value=register_value))\n\n                if line.startswith(f'Thread {self.faulting_thread} crashed with ARM Thread State'):\n                    in_frames = True\n\n        return result\n\n    @cached_property\n    def exception_type(self):\n        if self._is_json:\n            return self._data['exception'].get('type')\n        else:\n            return self._parse_field('Exception Type')\n\n    @cached_property\n    def exception_subtype(self) -> Optional[str]:\n        if self._is_json:\n            return self._data['exception'].get('subtype')\n        else:\n            return self._parse_field('Exception Subtype')\n\n    @cached_property\n    def application_specific_information(self) -> Optional[str]:\n        result = ''\n        if self._is_json:\n            asi = self._data.get('asi')\n            if asi is None:\n                return None\n            return asi\n        else:\n            in_frames = False\n            for line in self._data.split('\\n'):\n                if in_frames:\n                    line = line.strip()\n                    if len(line) == 0:\n                        break\n\n                    result += line + '\\n'\n\n                if line.startswith('Application Specific Information:'):\n                    in_frames = True\n\n        result = result.strip()\n        if not result:\n            return None\n        return result\n\n    def __str__(self) -> str:\n        result = super().__str__()\n        result += bold(f'Exception: {self.exception_type}\\n')\n\n        if self.exception_subtype:\n            result += bold('Exception Subtype: ')\n            result += f'{self.exception_subtype}\\n'\n\n        if self.application_specific_information:\n            result += bold('Application Specific Information: ')\n            result += str(self.application_specific_information)\n\n        result += '\\n'\n\n        result += bold('Registers:')\n        for i, register in enumerate(self.registers):\n            if i % 4 == 0:\n                result += '\\n'\n\n            result += f'{register.name} = 0x{register.value:016x} '.rjust(30)\n\n        result += '\\n\\n'\n\n        result += bold('Frames:\\n')\n        for frame in self.frames:\n            image_base = '_HEADER'\n            if frame.image_base is not None:\n                image_base = f'0x{frame.image_base:x}'\n            result += f'\\t[{frame.image_name}] {image_base}'\n            if frame.image_offset:\n                result += f' + 0x{frame.image_offset:x}'\n            if frame.symbol is not None:\n                result += f' ({frame.symbol} + 0x{frame.symbol_offset:x})'\n            result += '\\n'\n\n        return result\n\n\ndef get_crash_report_from_file(crash_report_file: IO) -> CrashReportBase:\n    metadata = json.loads(crash_report_file.readline())\n\n    try:\n        bug_type = BugType(metadata['bug_type'])\n    except ValueError:\n        return CrashReportBase(metadata, crash_report_file.read(), crash_report_file.name)\n\n    bug_type_parsers = {\n        BugType.Crash_109: UserModeCrashReport,\n        BugType.Crash_309: UserModeCrashReport,\n        BugType.ExcResourceThreads_327: UserModeCrashReport,\n        BugType.ExcResource_385: UserModeCrashReport,\n    }\n\n    parser = bug_type_parsers.get(bug_type)\n    if parser is None:\n        return CrashReportBase(metadata, crash_report_file.read(), crash_report_file.name)\n\n    return parser(metadata, crash_report_file.read(), crash_report_file.name)\n\n\nif __name__ == '__main__':\n    with open(sys.argv[-1]) as f:\n        print(get_crash_report_from_file(f))\n"
  },
  {
    "path": ".gitignore",
    "content": "*.so\n*.pyc\n*.pyo\n*.bin\n*_stub.pyi\n*_generated.go\n*_generated.s\n*_generated_test.go\n*_generated_test.s\n*_generated.h\n/.dmypy.json\n/dependencies\n/tags\n/build/\n/fonts/\n/linux-package/\n/kitty.app/\n/glad/out/\n/kitty/launcher/kitt*\n/*.dSYM/\n__pycache__/\n/glfw/wayland-*-client-protocol.[ch]\n/docs/_build/\n/docs/generated/\n/tools/simdstring/simdstring.test\n/.mypy_cache\n/.ruff_cache\n.DS_Store\n.cache\nbypy/b\nbypy/virtual-machines.conf\n_codeql_detected_source_root\n\n"
  },
  {
    "path": "3rdparty/base64/LICENSE",
    "content": "Copyright (c) 2005-2007, Nick Galbreath\nCopyright (c) 2015-2018, Wojciech Muła\nCopyright (c) 2016-2017, Matthieu Darbois\nCopyright (c) 2013-2022, Alfred Klomp\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n- Redistributions of source code must retain the above copyright notice,\n  this list of conditions and the following disclaimer.\n\n- Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in the\n  documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "3rdparty/base64/README.md",
    "content": "# Fast Base64 stream encoder/decoder\n\n[![Build Status](https://github.com/aklomp/base64/actions/workflows/test.yml/badge.svg)](https://github.com/aklomp/base64/actions/workflows/test.yml)\n\nThis is an implementation of a base64 stream encoding/decoding library in C99\nwith SIMD (AVX2, AVX512, NEON, AArch64/NEON, SSSE3, SSE4.1, SSE4.2, AVX) and\n[OpenMP](http://www.openmp.org) acceleration. It also contains wrapper functions\nto encode/decode simple length-delimited strings. This library aims to be:\n\n- FAST;\n- easy to use;\n- elegant.\n\nOn x86, the library does runtime feature detection. The first time it's called,\nthe library will determine the appropriate encoding/decoding routines for the\nmachine. It then remembers them for the lifetime of the program. If your\nprocessor supports AVX2, SSSE3, SSE4.1, SSE4.2 or AVX instructions, the library\nwill pick an optimized codec that lets it encode/decode 12 or 24 bytes at a\ntime, which gives a speedup of four or more times compared to the \"plain\"\nbytewise codec.\n\nAVX512 support is only for encoding at present, utilizing the AVX512 VL and VBMI\ninstructions. Decoding part reused AVX2 implementations. For CPUs later than\nCannonlake (manufactured in 2018) supports these instructions.\n\nNEON support is hardcoded to on or off at compile time, because portable\nruntime feature detection is unavailable on ARM.\n\nEven if your processor does not support SIMD instructions, this is a very fast\nlibrary. The fallback routine can process 32 or 64 bits of input in one round,\ndepending on your processor's word width, which still makes it significantly\nfaster than naive bytewise implementations. On some 64-bit machines, the 64-bit\nroutines even outperform the SSSE3 ones.\n\nTo the author's knowledge, at the time of original release, this was the only\nBase64 library to offer SIMD acceleration. The author wrote\n[an article](http://www.alfredklomp.com/programming/sse-base64) explaining one\npossible SIMD approach to encoding/decoding Base64. The article can help figure\nout what the code is doing, and why.\n\nNotable features:\n\n- Really fast on x86 and ARM systems by using SIMD vector processing;\n- Can use [OpenMP](http://www.openmp.org) for even more parallel speedups;\n- Really fast on other 32 or 64-bit platforms through optimized routines;\n- Reads/writes blocks of streaming data;\n- Does not dynamically allocate memory;\n- Valid C99 that compiles with pedantic options on;\n- Re-entrant and threadsafe;\n- Unit tested;\n- Uses Duff's Device.\n\n## Acknowledgements\n\nThe original AVX2, NEON and Aarch64/NEON codecs were generously contributed by\n[Inkymail](https://github.com/inkymail/base64), who, in their fork, also\nimplemented some additional features. Their work is slowly being backported\ninto this project.\n\nThe SSSE3 and AVX2 codecs were substantially improved by using some very clever\noptimizations described by Wojciech Muła in a\n[series](http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html) of\n[articles](http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html).\nHis own code is [here](https://github.com/WojciechMula/toys/tree/master/base64).\n\nThe AVX512 encoder is based on code from Wojciech Muła's\n[base64simd](https://github.com/WojciechMula/base64simd) library.\n\nThe OpenMP implementation was added by Ferry Toth (@htot) from [Exalon Delft](http://www.exalondelft.nl).\n\n## Building\n\nThe `lib` directory contains the code for the actual library.\nTyping `make` in the toplevel directory will build `lib/libbase64.o` and `bin/base64`.\nThe first is a single, self-contained object file that you can link into your own project.\nThe second is a standalone test binary that works similarly to the `base64` system utility.\n\nThe matching header file needed to use this library is in `include/libbase64.h`.\n\nTo compile just the \"plain\" library without SIMD codecs, type:\n\n```sh\nmake lib/libbase64.o\n```\n\nOptional SIMD codecs can be included by specifying the `AVX2_CFLAGS`, `AVX512_CFLAGS`,\n`NEON32_CFLAGS`, `NEON64_CFLAGS`, `SSSE3_CFLAGS`, `SSE41_CFLAGS`, `SSE42_CFLAGS` and/or `AVX_CFLAGS` environment variables.\nA typical build invocation on x86 looks like this:\n\n```sh\nAVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.o\n```\n\n### AVX2\n\nTo build and include the AVX2 codec, set the `AVX2_CFLAGS` environment variable to a value that will turn on AVX2 support in your compiler, typically `-mavx2`.\nExample:\n\n```sh\nAVX2_CFLAGS=-mavx2 make\n```\n\n### AVX512\n\nTo build and include the AVX512 codec, set the `AVX512_CFLAGS` environment variable to a value that will turn on AVX512 support in your compiler, typically `-mavx512vl -mavx512vbmi`.\nExample:\n\n```sh\nAVX512_CFLAGS=\"-mavx512vl -mavx512vbmi\" make\n```\n\nThe codec will only be used if runtime feature detection shows that the target machine supports AVX2.\n\n### SSSE3\n\nTo build and include the SSSE3 codec, set the `SSSE3_CFLAGS` environment variable to a value that will turn on SSSE3 support in your compiler, typically `-mssse3`.\nExample:\n\n```sh\nSSSE3_CFLAGS=-mssse3 make\n```\n\nThe codec will only be used if runtime feature detection shows that the target machine supports SSSE3.\n\n### NEON\n\nThis library includes two NEON codecs: one for regular 32-bit ARM and one for the 64-bit AArch64 with NEON, which has double the amount of SIMD registers and can do full 64-byte table lookups.\nThese codecs encode in 48-byte chunks and decode in massive 64-byte chunks, so they had to be augmented with an uint32/64 codec to stay fast on smaller inputs!\n\nUse LLVM/Clang for compiling the NEON codecs.\nThe code generation of at least GCC 4.6 (the version shipped with Raspbian and used for testing) contains a bug when compiling `vstq4_u8()`, and the generated assembly code is of low quality.\nNEON intrinsics are a known weak area of GCC.\nClang does a better job.\n\nNEON support can unfortunately not be portably detected at runtime from userland (the `mrc` instruction is privileged), so the default value for using the NEON codec is determined at compile-time.\nBut you can do your own runtime detection.\nYou can include the NEON codec and make it the default, then do a runtime check if the CPU has NEON support, and if not, force a downgrade to non-NEON with `BASE64_FORCE_PLAIN`.\n\nThese are your options:\n\n1. Don't include NEON support;\n2. build NEON support and make it the default, but build all other code without NEON flags so that you can override the default at runtime with `BASE64_FORCE_PLAIN`;\n3. build everything with NEON support and make it the default;\n4. build everything with NEON support, but don't make it the default (which makes no sense).\n\nFor option 1, simply don't specify any NEON-specific compiler flags at all, like so:\n\n```sh\nCC=clang CFLAGS=\"-march=armv6\" make\n```\n\nFor option 2, keep your `CFLAGS` plain, but set the `NEON32_CFLAGS` environment variable to a value that will build NEON support.\nThe line below, for instance, will build all the code at ARMv6 level, except for the NEON codec, which is built at ARMv7.\nIt will also make the NEON codec the default.\nFor ARMv6 platforms, override that default at runtime with the `BASE64_FORCE_PLAIN` flag.\nNo ARMv7/NEON code will then be touched.\n\n```sh\nCC=clang CFLAGS=\"-march=armv6\" NEON32_CFLAGS=\"-march=armv7 -mfpu=neon\" make\n```\n\nFor option 3, put everything in your `CFLAGS` and use a stub, but non-empty, `NEON32_CFLAGS`.\nThis example works for the Raspberry Pi 2B V1.1, which has NEON support:\n\n```sh\nCC=clang CFLAGS=\"-march=armv7 -mtune=cortex-a7\" NEON32_CFLAGS=\"-mfpu=neon\" make\n```\n\nTo build and include the NEON64 codec, use `CFLAGS` as usual to define the platform and set `NEON64_CFLAGS` to a nonempty stub.\n(The AArch64 target has mandatory NEON64 support.)\nExample:\n\n```sh\nCC=clang CFLAGS=\"--target=aarch64-linux-gnu -march=armv8-a\" NEON64_CFLAGS=\" \" make\n```\n\n### OpenMP\n\nTo enable OpenMP on GCC you need to build with `-fopenmp`. This can be by setting the `OPENMP` environment variable to `1`.\n\nExample:\n\n```sh\nOPENMP=1 make\n```\n\nThis will let the compiler define `_OPENMP`, which in turn will include the OpenMP optimized `lib_openmp.c` into `lib.c`.\n\nBy default the number of parallel threads will be equal to the number of cores of the processor.\nOn a quad core with hyperthreading eight cores will be detected, but hyperthreading will not increase the performance.\n\nTo get verbose information about OpenMP start the program with `OMP_DISPLAY_ENV=VERBOSE`, for instance\n\n```sh\nOMP_DISPLAY_ENV=VERBOSE test/benchmark\n```\n\nTo put a limit on the number of threads, start the program with `OMP_THREAD_LIMIT=n`, for instance\n\n```sh\nOMP_THREAD_LIMIT=2 test/benchmark\n```\n\nAn example of running a benchmark with OpenMP, SSSE3 and AVX2 enabled:\n\n```sh\nmake clean && OPENMP=1 SSSE3_CFLAGS=-mssse3 AVX2_CFLAGS=-mavx2 make && OPENMP=1 make -C test\n```\n\n## API reference\n\nStrings are represented as a pointer and a length; they are not\nzero-terminated. This was a conscious design decision. In the decoding step,\nrelying on zero-termination would make no sense since the output could contain\nlegitimate zero bytes. In the encoding step, returning the length saves the\noverhead of calling `strlen()` on the output. If you insist on the trailing\nzero, you can easily add it yourself at the given offset.\n\n### Flags\n\nSome API calls take a `flags` argument.\nThat argument can be used to force the use of a specific codec, even if that codec is a no-op in the current build.\nMainly there for testing purposes, this is also useful on ARM where the only way to do runtime NEON detection is to ask the OS if it's available.\nThe following constants can be used:\n\n- `BASE64_FORCE_AVX2`\n- `BASE64_FORCE_AVX512`\n- `BASE64_FORCE_NEON32`\n- `BASE64_FORCE_NEON64`\n- `BASE64_FORCE_PLAIN`\n- `BASE64_FORCE_SSSE3`\n- `BASE64_FORCE_SSE41`\n- `BASE64_FORCE_SSE42`\n- `BASE64_FORCE_AVX`\n\nSet `flags` to `0` for the default behavior, which is runtime feature detection on x86, a compile-time fixed codec on ARM, and the plain codec on other platforms.\n\n### Encoding\n\n#### base64_encode\n\n```c\nvoid base64_encode\n    ( const char  *src\n    , size_t       srclen\n    , char        *out\n    , size_t      *outlen\n    , int          flags\n    ) ;\n```\n\nWrapper function to encode a plain string of given length.\nOutput is written to `out` without trailing zero.\nOutput length in bytes is written to `outlen`.\nThe buffer in `out` has been allocated by the caller and is at least 4/3 the size of the input.\n\n#### base64_stream_encode_init\n\n```c\nvoid base64_stream_encode_init\n    ( struct base64_state  *state\n    , int                   flags\n    ) ;\n```\n\nCall this before calling `base64_stream_encode()` to init the state.\n\n#### base64_stream_encode\n\n```c\nvoid base64_stream_encode\n    ( struct base64_state  *state\n    , const char           *src\n    , size_t                srclen\n    , char                 *out\n    , size_t               *outlen\n    ) ;\n```\n\nEncodes the block of data of given length at `src`, into the buffer at `out`.\nCaller is responsible for allocating a large enough out-buffer; it must be at least 4/3 the size of the in-buffer, but take some margin.\nPlaces the number of new bytes written into `outlen` (which is set to zero when the function starts).\nDoes not zero-terminate or finalize the output.\n\n#### base64_stream_encode_final\n\n```c\nvoid base64_stream_encode_final\n    ( struct base64_state  *state\n    , char                 *out\n    , size_t               *outlen\n    ) ;\n```\n\nFinalizes the output begun by previous calls to `base64_stream_encode()`.\nAdds the required end-of-stream markers if appropriate.\n`outlen` is modified and will contain the number of new bytes written at `out` (which will quite often be zero).\n\n### Decoding\n\n#### base64_decode\n\n```c\nint base64_decode\n    ( const char  *src\n    , size_t       srclen\n    , char        *out\n    , size_t      *outlen\n    , int          flags\n    ) ;\n```\n\nWrapper function to decode a plain string of given length.\nOutput is written to `out` without trailing zero. Output length in bytes is written to `outlen`.\nThe buffer in `out` has been allocated by the caller and is at least 3/4 the size of the input.\nReturns `1` for success, and `0` when a decode error has occured due to invalid input.\nReturns `-1` if the chosen codec is not included in the current build.\n\n#### base64_stream_decode_init\n\n```c\nvoid base64_stream_decode_init\n    ( struct base64_state  *state\n    , int                   flags\n    ) ;\n```\n\nCall this before calling `base64_stream_decode()` to init the state.\n\n#### base64_stream_decode\n\n```c\nint base64_stream_decode\n    ( struct base64_state  *state\n    , const char           *src\n    , size_t                srclen\n    , char                 *out\n    , size_t               *outlen\n    ) ;\n```\n\nDecodes the block of data of given length at `src`, into the buffer at `out`.\nCaller is responsible for allocating a large enough out-buffer; it must be at least 3/4 the size of the in-buffer, but take some margin.\nPlaces the number of new bytes written into `outlen` (which is set to zero when the function starts).\nDoes not zero-terminate the output.\nReturns 1 if all is well, and 0 if a decoding error was found, such as an invalid character.\nReturns -1 if the chosen codec is not included in the current build.\nUsed by the test harness to check whether a codec is available for testing.\n\n## Examples\n\nA simple example of encoding a static string to base64 and printing the output\nto stdout:\n\n```c\n#include <stdio.h>\t/* fwrite */\n#include \"libbase64.h\"\n\nint main ()\n{\n\tchar src[] = \"hello world\";\n\tchar out[20];\n\tsize_t srclen = sizeof(src) - 1;\n\tsize_t outlen;\n\n\tbase64_encode(src, srclen, out, &outlen, 0);\n\n\tfwrite(out, outlen, 1, stdout);\n\n\treturn 0;\n}\n```\n\nA simple example (no error checking, etc) of stream encoding standard input to\nstandard output:\n\n```c\n#include <stdio.h>\n#include \"libbase64.h\"\n\nint main ()\n{\n\tsize_t nread, nout;\n\tchar buf[12000], out[16000];\n\tstruct base64_state state;\n\n\t// Initialize stream encoder:\n\tbase64_stream_encode_init(&state, 0);\n\n\t// Read contents of stdin into buffer:\n\twhile ((nread = fread(buf, 1, sizeof(buf), stdin)) > 0) {\n\n\t\t// Encode buffer:\n\t\tbase64_stream_encode(&state, buf, nread, out, &nout);\n\n\t\t// If there's output, print it to stdout:\n\t\tif (nout) {\n\t\t\tfwrite(out, nout, 1, stdout);\n\t\t}\n\n\t\t// If an error occurred, exit the loop:\n\t\tif (feof(stdin)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Finalize encoding:\n\tbase64_stream_encode_final(&state, out, &nout);\n\n\t// If the finalizing resulted in extra output bytes, print them:\n\tif (nout) {\n\t\tfwrite(out, nout, 1, stdout);\n\t}\n\n\treturn 0;\n}\n```\n\nAlso see `bin/base64.c` for a simple re-implementation of the `base64` utility.\nA file or standard input is fed through the encoder/decoder, and the output is\nwritten to standard output.\n\n## Tests\n\nSee `tests/` for a small test suite. Testing is automated with\n[GitHub Actions](https://github.com/aklomp/base64/actions), which builds and\ntests the code across various architectures.\n\n## Benchmarks\n\nBenchmarks can be run with the built-in benchmark program as follows:\n\n```sh\nmake -C test benchmark <buildflags> && test/benchmark\n```\n\nIt will run an encoding and decoding benchmark for all of the compiled-in codecs.\n\nThe tables below contain some results on random machines. All numbers measured with a 10MB buffer in MB/sec, rounded to the nearest integer.\n\n\\*: Update needed\n\nx86 processors\n\n| Processor                                 | Plain enc | Plain dec | SSSE3 enc | SSSE3 dec | AVX enc | AVX dec | AVX2 enc | AVX2 dec |\n|-------------------------------------------|----------:|----------:|----------:|----------:|--------:|--------:|---------:|---------:|\n| i7-4771 @ 3.5 GHz                         | 833\\*     | 1111\\*    | 3333\\*    | 4444\\*    | TBD     | TBD     | 4999\\*   | 6666\\*   |\n| i7-4770 @ 3.4 GHz DDR1600                 | 1790\\*    | 3038\\*    | 4899\\*    | 4043\\*    | 4796\\*  | 5709\\*  | 4681\\*   | 6386\\*   |\n| i7-4770 @ 3.4 GHz DDR1600 OPENMP 1 thread | 1784\\*    | 3041\\*    | 4945\\*    | 4035\\*    | 4776\\*  | 5719\\*  | 4661\\*   | 6294\\*   |\n| i7-4770 @ 3.4 GHz DDR1600 OPENMP 2 thread | 3401\\*    | 5729\\*    | 5489\\*    | 7444\\*    | 5003\\*  | 8624\\*  | 5105\\*   | 8558\\*   |\n| i7-4770 @ 3.4 GHz DDR1600 OPENMP 4 thread | 4884\\*    | 7099\\*    | 4917\\*    | 7057\\*    | 4799\\*  | 7143\\*  | 4902\\*   | 7219\\*   |\n| i7-4770 @ 3.4 GHz DDR1600 OPENMP 8 thread | 5212\\*    | 8849\\*    | 5284\\*    | 9099\\*    | 5289\\*  | 9220\\*  | 4849\\*   | 9200\\*   |\n| i7-4870HQ @ 2.5 GHz                       | 1471\\*    | 3066\\*    | 6721\\*    | 6962\\*    | 7015\\*  | 8267\\*  | 8328\\*   | 11576\\*  |\n| i5-4590S @ 3.0 GHz                        | 3356      | 3197      | 4363      | 6104      | 4243\\*  | 6233    | 4160\\*   | 6344     |\n| Xeon X5570 @ 2.93 GHz                     | 2161      | 1508      | 3160      | 3915      | -       | -       | -        | -        |\n| Pentium4 @ 3.4 GHz                        | 896       | 740       | -         | -         | -       | -       | -        | -        |\n| Atom N270                                 | 243       | 266       | 508       | 387       | -       | -       | -        | -        |\n| AMD E-450                                 | 645       | 564       | 625       | 634       | -       | -       | -        | -        |\n| Intel Edison @ 500 MHz                    | 79\\*      | 92\\*      | 152\\*     | 172\\*     | -       | -       | -        | -        |\n| Intel Edison @ 500 MHz OPENMP 2 thread    | 158\\*     | 184\\*     | 300\\*     | 343\\*     | -       | -       | -        | -        |\n| Intel Edison @ 500 MHz (x86-64)           | 162       | 119       | 209       | 164       | -       | -       | -        | -        |\n| Intel Edison @ 500 MHz (x86-64) 2 thread  | 319       | 237       | 412       | 329       | -       | -       | -        | -        |\n\nARM processors\n\n| Processor                                 | Plain enc | Plain dec | NEON32 enc | NEON32 dec | NEON64 enc | NEON64 dec |\n|-------------------------------------------|----------:|----------:|-----------:|-----------:|-----------:|-----------:|\n| Raspberry PI B+ V1.2                      | 46\\*      | 40\\*      | -          | -          | -          | -          |\n| Raspberry PI 2 B V1.1                     | 85        | 141       | 300        | 225        | -          | -          |\n| Apple iPhone SE armv7                     | 1056\\*    | 895\\*     | 2943\\*     | 2618\\*     | -          | -          |\n| Apple iPhone SE arm64                     | 1061\\*    | 1239\\*    | -          | -          | 4098\\*     | 3983\\*     |\n\nPowerPC processors\n\n| Processor                                 | Plain enc | Plain dec |\n|-------------------------------------------|----------:|----------:|\n| PowerPC E6500 @ 1.8GHz                    | 270\\*     | 265\\*     |\n\n\nBenchmarks on i7-4770 @ 3.4 GHz DDR1600 with varrying buffer sizes:\n![Benchmarks](base64-benchmarks.png)\n\nNote: optimal buffer size to take advantage of the cache is in the range of 100 kB to 1 MB, leading to 12x faster AVX encoding/decoding compared to Plain, or a throughput of 24/27GB/sec.\nAlso note the performance degradation when the buffer size is less than 10 kB due to thread creation overhead.\nTo prevent this from happening `lib_openmp.c` defines `OMP_THRESHOLD 20000`, requiring at least a 20000 byte buffer to enable multithreading.\n\n## License\n\nThis repository is licensed under the\n[BSD 2-clause License](http://opensource.org/licenses/BSD-2-Clause). See the\nLICENSE file.\n"
  },
  {
    "path": "3rdparty/base64/config.h",
    "content": ""
  },
  {
    "path": "3rdparty/base64/include/libbase64.h",
    "content": "#ifndef LIBBASE64_H\n#define LIBBASE64_H\n\n#include <stddef.h>\t/* size_t */\n\n\n#if defined(_WIN32) || defined(__CYGWIN__)\n#define BASE64_SYMBOL_IMPORT __declspec(dllimport)\n#define BASE64_SYMBOL_EXPORT __declspec(dllexport)\n#define BASE64_SYMBOL_PRIVATE\n\n#elif __GNUC__ >= 4\n#define BASE64_SYMBOL_IMPORT   __attribute__ ((visibility (\"default\")))\n#define BASE64_SYMBOL_EXPORT   __attribute__ ((visibility (\"default\")))\n#define BASE64_SYMBOL_PRIVATE  __attribute__ ((visibility (\"hidden\")))\n\n#else\n#define BASE64_SYMBOL_IMPORT\n#define BASE64_SYMBOL_EXPORT\n#define BASE64_SYMBOL_PRIVATE\n#endif\n\n#if defined(BASE64_STATIC_DEFINE)\n#define BASE64_EXPORT\n#define BASE64_NO_EXPORT\n\n#else\n#if defined(BASE64_EXPORTS) // defined if we are building the shared library\n#define BASE64_EXPORT BASE64_SYMBOL_EXPORT\n\n#else\n#define BASE64_EXPORT BASE64_SYMBOL_IMPORT\n#endif\n\n#define BASE64_NO_EXPORT BASE64_SYMBOL_PRIVATE\n#endif\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* These are the flags that can be passed in the `flags` argument. The values\n * below force the use of a given codec, even if that codec is a no-op in the\n * current build. Used in testing. Set to 0 for the default behavior, which is\n * runtime feature detection on x86, a compile-time fixed codec on ARM, and\n * the plain codec on other platforms: */\n#define BASE64_FORCE_AVX2\t(1 << 0)\n#define BASE64_FORCE_NEON32\t(1 << 1)\n#define BASE64_FORCE_NEON64\t(1 << 2)\n#define BASE64_FORCE_PLAIN\t(1 << 3)\n#define BASE64_FORCE_SSSE3\t(1 << 4)\n#define BASE64_FORCE_SSE41\t(1 << 5)\n#define BASE64_FORCE_SSE42\t(1 << 6)\n#define BASE64_FORCE_AVX\t(1 << 7)\n#define BASE64_FORCE_AVX512\t(1 << 8)\n\nstruct base64_state {\n\tint eof;\n\tint bytes;\n\tint flags;\n\tunsigned char carry;\n};\n\n/* Wrapper function to encode a plain string of given length. Output is written\n * to *out without trailing zero. Output length in bytes is written to *outlen.\n * The buffer in `out` has been allocated by the caller and is at least 4/3 the\n * size of the input. See above for `flags`; set to 0 for default operation: */\nvoid BASE64_EXPORT base64_encode\n\t( const char\t\t*src\n\t, size_t\t\t srclen\n\t, char\t\t\t*out\n\t, size_t\t\t*outlen\n\t, int\t\t\t flags\n\t) ;\n\n/* Call this before calling base64_stream_encode() to init the state. See above\n * for `flags`; set to 0 for default operation: */\nvoid BASE64_EXPORT base64_stream_encode_init\n\t( struct base64_state\t*state\n\t, int\t\t\t flags\n\t) ;\n\n/* Encodes the block of data of given length at `src`, into the buffer at\n * `out`. Caller is responsible for allocating a large enough out-buffer; it\n * must be at least 4/3 the size of the in-buffer, but take some margin. Places\n * the number of new bytes written into `outlen` (which is set to zero when the\n * function starts). Does not zero-terminate or finalize the output. */\nvoid BASE64_EXPORT base64_stream_encode\n\t( struct base64_state\t*state\n\t, const char\t\t*src\n\t, size_t\t\t srclen\n\t, char\t\t\t*out\n\t, size_t\t\t*outlen\n\t) ;\n\n/* Finalizes the output begun by previous calls to `base64_stream_encode()`.\n * Adds the required end-of-stream markers if appropriate. `outlen` is modified\n * and will contain the number of new bytes written at `out` (which will quite\n * often be zero). */\nvoid BASE64_EXPORT base64_stream_encode_final\n\t( struct base64_state\t*state\n\t, char\t\t\t*out\n\t, size_t\t\t*outlen\n\t) ;\n\n/* Wrapper function to decode a plain string of given length. Output is written\n * to *out without trailing zero. Output length in bytes is written to *outlen.\n * The buffer in `out` has been allocated by the caller and is at least 3/4 the\n * size of the input. See above for `flags`, set to 0 for default operation: */\nint BASE64_EXPORT base64_decode\n\t( const char\t\t*src\n\t, size_t\t\t srclen\n\t, char\t\t\t*out\n\t, size_t\t\t*outlen\n\t, int\t\t\t flags\n\t) ;\n\n/* Call this before calling base64_stream_decode() to init the state. See above\n * for `flags`; set to 0 for default operation: */\nvoid BASE64_EXPORT base64_stream_decode_init\n\t( struct base64_state\t*state\n\t, int\t\t\t flags\n\t) ;\n\n/* Decodes the block of data of given length at `src`, into the buffer at\n * `out`. Caller is responsible for allocating a large enough out-buffer; it\n * must be at least 3/4 the size of the in-buffer, but take some margin. Places\n * the number of new bytes written into `outlen` (which is set to zero when the\n * function starts). Does not zero-terminate the output. Returns 1 if all is\n * well, and 0 if a decoding error was found, such as an invalid character.\n * Returns -1 if the chosen codec is not included in the current build. Used by\n * the test harness to check whether a codec is available for testing. */\nint BASE64_EXPORT base64_stream_decode\n\t( struct base64_state\t*state\n\t, const char\t\t*src\n\t, size_t\t\t srclen\n\t, char\t\t\t*out\n\t, size_t\t\t*outlen\n\t) ;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* LIBBASE64_H */\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#if HAVE_AVX\n#include <immintrin.h>\n\n// Only enable inline assembly on supported compilers and on 64-bit CPUs.\n#ifndef BASE64_AVX_USE_ASM\n# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64\n#  define BASE64_AVX_USE_ASM 1\n# else\n#  define BASE64_AVX_USE_ASM 0\n# endif\n#endif\n\n#include \"../ssse3/dec_reshuffle.c\"\n#include \"../ssse3/dec_loop.c\"\n\n#if BASE64_AVX_USE_ASM\n# include \"enc_loop_asm.c\"\n#else\n# include \"../ssse3/enc_translate.c\"\n# include \"../ssse3/enc_reshuffle.c\"\n# include \"../ssse3/enc_loop.c\"\n#endif\n\n#endif\t// HAVE_AVX\n\nBASE64_ENC_FUNCTION(avx)\n{\n#if HAVE_AVX\n\t#include \"../generic/enc_head.c\"\n\n\t// For supported compilers, use a hand-optimized inline assembly\n\t// encoder. Otherwise fall back on the SSSE3 encoder, but compiled with\n\t// AVX flags to generate better optimized AVX code.\n\n#if BASE64_AVX_USE_ASM\n\tenc_loop_avx(&s, &slen, &o, &olen);\n#else\n\tenc_loop_ssse3(&s, &slen, &o, &olen);\n#endif\n\n\t#include \"../generic/enc_tail.c\"\n#else\n\tBASE64_ENC_STUB\n#endif\n}\n\nBASE64_DEC_FUNCTION(avx)\n{\n#if HAVE_AVX\n\t#include \"../generic/dec_head.c\"\n\tdec_loop_ssse3(&s, &slen, &o, &olen);\n\t#include \"../generic/dec_tail.c\"\n#else\n\tBASE64_DEC_STUB\n#endif\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx/enc_loop_asm.c",
    "content": "// Apologies in advance for combining the preprocessor with inline assembly,\n// two notoriously gnarly parts of C, but it was necessary to avoid a lot of\n// code repetition. The preprocessor is used to template large sections of\n// inline assembly that differ only in the registers used. If the code was\n// written out by hand, it would become very large and hard to audit.\n\n// Generate a block of inline assembly that loads register R0 from memory. The\n// offset at which the register is loaded is set by the given round.\n#define LOAD(R0, ROUND) \\\n\t\"vlddqu (\"#ROUND\" * 12)(%[src]), %[\"R0\"] \\n\\t\"\n\n// Generate a block of inline assembly that deinterleaves and shuffles register\n// R0 using preloaded constants. Outputs in R0 and R1.\n#define SHUF(R0, R1, R2) \\\n\t\"vpshufb  %[lut0], %[\"R0\"], %[\"R1\"] \\n\\t\" \\\n\t\"vpand    %[\"R1\"], %[msk0], %[\"R2\"] \\n\\t\" \\\n\t\"vpand    %[\"R1\"], %[msk2], %[\"R1\"] \\n\\t\" \\\n\t\"vpmulhuw %[\"R2\"], %[msk1], %[\"R2\"] \\n\\t\" \\\n\t\"vpmullw  %[\"R1\"], %[msk3], %[\"R1\"] \\n\\t\" \\\n\t\"vpor     %[\"R1\"], %[\"R2\"], %[\"R1\"] \\n\\t\"\n\n// Generate a block of inline assembly that takes R0 and R1 and translates\n// their contents to the base64 alphabet, using preloaded constants.\n#define TRAN(R0, R1, R2) \\\n\t\"vpsubusb %[n51],  %[\"R1\"], %[\"R0\"] \\n\\t\" \\\n\t\"vpcmpgtb %[n25],  %[\"R1\"], %[\"R2\"] \\n\\t\" \\\n\t\"vpsubb   %[\"R2\"], %[\"R0\"], %[\"R0\"] \\n\\t\" \\\n\t\"vpshufb  %[\"R0\"], %[lut1], %[\"R2\"] \\n\\t\" \\\n\t\"vpaddb   %[\"R1\"], %[\"R2\"], %[\"R0\"] \\n\\t\"\n\n// Generate a block of inline assembly that stores the given register R0 at an\n// offset set by the given round.\n#define STOR(R0, ROUND) \\\n\t\"vmovdqu %[\"R0\"], (\"#ROUND\" * 16)(%[dst]) \\n\\t\"\n\n// Generate a block of inline assembly that generates a single self-contained\n// encoder round: fetch the data, process it, and store the result. Then update\n// the source and destination pointers.\n#define ROUND() \\\n\tLOAD(\"a\", 0) \\\n\tSHUF(\"a\", \"b\", \"c\") \\\n\tTRAN(\"a\", \"b\", \"c\") \\\n\tSTOR(\"a\", 0) \\\n\t\"add $12, %[src] \\n\\t\" \\\n\t\"add $16, %[dst] \\n\\t\"\n\n// Define a macro that initiates a three-way interleaved encoding round by\n// preloading registers a, b and c from memory.\n// The register graph shows which registers are in use during each step, and\n// is a visual aid for choosing registers for that step. Symbol index:\n//\n//  +  indicates that a register is loaded by that step.\n//  |  indicates that a register is in use and must not be touched.\n//  -  indicates that a register is decommissioned by that step.\n//  x  indicates that a register is used as a temporary by that step.\n//  V  indicates that a register is an input or output to the macro.\n//\n#define ROUND_3_INIT() \t\t\t/*  a b c d e f  */ \\\n\tLOAD(\"a\",   0)\t\t\t/*  +            */ \\\n\tSHUF(\"a\", \"d\", \"e\")\t\t/*  |     + x    */ \\\n\tLOAD(\"b\",   1)\t\t\t/*  | +   |      */ \\\n\tTRAN(\"a\", \"d\", \"e\")\t\t/*  | |   - x    */ \\\n\tLOAD(\"c\",   2)\t\t\t/*  V V V        */\n\n// Define a macro that translates, shuffles and stores the input registers A, B\n// and C, and preloads registers D, E and F for the next round.\n// This macro can be arbitrarily daisy-chained by feeding output registers D, E\n// and F back into the next round as input registers A, B and C. The macro\n// carefully interleaves memory operations with data operations for optimal\n// pipelined performance.\n\n#define ROUND_3(ROUND, A,B,C,D,E,F) \t/*  A B C D E F  */ \\\n\tLOAD(D, (ROUND + 3))\t\t/*  V V V +      */ \\\n\tSHUF(B, E, F)\t\t\t/*  | | | | + x  */ \\\n\tSTOR(A, (ROUND + 0))\t\t/*  - | | | |    */ \\\n\tTRAN(B, E, F)\t\t\t/*    | | | - x  */ \\\n\tLOAD(E, (ROUND + 4))\t\t/*    | | | +    */ \\\n\tSHUF(C, A, F)\t\t\t/*  + | | | | x  */ \\\n\tSTOR(B, (ROUND + 1))\t\t/*  | - | | |    */ \\\n\tTRAN(C, A, F)\t\t\t/*  -   | | | x  */ \\\n\tLOAD(F, (ROUND + 5))\t\t/*      | | | +  */ \\\n\tSHUF(D, A, B)\t\t\t/*  + x | | | |  */ \\\n\tSTOR(C, (ROUND + 2))\t\t/*  |   - | | |  */ \\\n\tTRAN(D, A, B)\t\t\t/*  - x   V V V  */\n\n// Define a macro that terminates a ROUND_3 macro by taking pre-loaded\n// registers D, E and F, and translating, shuffling and storing them.\n#define ROUND_3_END(ROUND, A,B,C,D,E,F)\t/*  A B C D E F  */ \\\n\tSHUF(E, A, B)\t\t\t/*  + x   V V V  */ \\\n\tSTOR(D, (ROUND + 3))\t\t/*  |     - | |  */ \\\n\tTRAN(E, A, B)\t\t\t/*  - x     | |  */ \\\n\tSHUF(F, C, D)\t\t\t/*      + x | |  */ \\\n\tSTOR(E, (ROUND + 4))\t\t/*      |   - |  */ \\\n\tTRAN(F, C, D)\t\t\t/*      - x   |  */ \\\n\tSTOR(F, (ROUND + 5))\t\t/*            -  */\n\n// Define a type A round. Inputs are a, b, and c, outputs are d, e, and f.\n#define ROUND_3_A(ROUND) \\\n\tROUND_3(ROUND, \"a\", \"b\", \"c\", \"d\", \"e\", \"f\")\n\n// Define a type B round. Inputs and outputs are swapped with regard to type A.\n#define ROUND_3_B(ROUND) \\\n\tROUND_3(ROUND, \"d\", \"e\", \"f\", \"a\", \"b\", \"c\")\n\n// Terminating macro for a type A round.\n#define ROUND_3_A_LAST(ROUND) \\\n\tROUND_3_A(ROUND) \\\n\tROUND_3_END(ROUND, \"a\", \"b\", \"c\", \"d\", \"e\", \"f\")\n\n// Terminating macro for a type B round.\n#define ROUND_3_B_LAST(ROUND) \\\n\tROUND_3_B(ROUND) \\\n\tROUND_3_END(ROUND, \"d\", \"e\", \"f\", \"a\", \"b\", \"c\")\n\n// Suppress clang's warning that the literal string in the asm statement is\n// overlong (longer than the ISO-mandated minimum size of 4095 bytes for C99\n// compilers). It may be true, but the goal here is not C99 portability.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Woverlength-strings\"\n\nstatic inline void\nenc_loop_avx (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\t// For a clearer explanation of the algorithm used by this function,\n\t// please refer to the plain (not inline assembly) implementation. This\n\t// function follows the same basic logic.\n\n\tif (*slen < 16) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 12 bytes at a time. Input is read in blocks of 16\n\t// bytes, so \"reserve\" four bytes from the input buffer to ensure that\n\t// we never read beyond the end of the input buffer.\n\tsize_t rounds = (*slen - 4) / 12;\n\n\t*slen -= rounds * 12;   // 12 bytes consumed per round\n\t*olen += rounds * 16;   // 16 bytes produced per round\n\n\t// Number of times to go through the 36x loop.\n\tsize_t loops = rounds / 36;\n\n\t// Number of rounds remaining after the 36x loop.\n\trounds %= 36;\n\n\t// Lookup tables.\n\tconst __m128i lut0 = _mm_set_epi8(\n\t\t10, 11,  9, 10,  7,  8,  6,  7,  4,  5,  3,  4,  1,  2,  0,  1);\n\n\tconst __m128i lut1 = _mm_setr_epi8(\n\t\t65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0);\n\n\t// Temporary registers.\n\t__m128i a, b, c, d, e, f;\n\n\t__asm__ volatile (\n\n\t\t// If there are 36 rounds or more, enter a 36x unrolled loop of\n\t\t// interleaved encoding rounds. The rounds interleave memory\n\t\t// operations (load/store) with data operations (table lookups,\n\t\t// etc) to maximize pipeline throughput.\n\t\t\"    test %[loops], %[loops] \\n\\t\"\n\t\t\"    jz   18f                \\n\\t\"\n\t\t\"    jmp  36f                \\n\\t\"\n\t\t\"                            \\n\\t\"\n\t\t\".balign 64                  \\n\\t\"\n\t\t\"36: \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A( 0)\n\t\t\"    \" ROUND_3_B( 3)\n\t\t\"    \" ROUND_3_A( 6)\n\t\t\"    \" ROUND_3_B( 9)\n\t\t\"    \" ROUND_3_A(12)\n\t\t\"    \" ROUND_3_B(15)\n\t\t\"    \" ROUND_3_A(18)\n\t\t\"    \" ROUND_3_B(21)\n\t\t\"    \" ROUND_3_A(24)\n\t\t\"    \" ROUND_3_B(27)\n\t\t\"    \" ROUND_3_A_LAST(30)\n\t\t\"    add $(12 * 36), %[src] \\n\\t\"\n\t\t\"    add $(16 * 36), %[dst] \\n\\t\"\n\t\t\"    dec %[loops]           \\n\\t\"\n\t\t\"    jnz 36b                \\n\\t\"\n\n\t\t// Enter an 18x unrolled loop for rounds of 18 or more.\n\t\t\"18: cmp $18, %[rounds] \\n\\t\"\n\t\t\"    jl  9f             \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A(0)\n\t\t\"    \" ROUND_3_B(3)\n\t\t\"    \" ROUND_3_A(6)\n\t\t\"    \" ROUND_3_B(9)\n\t\t\"    \" ROUND_3_A_LAST(12)\n\t\t\"    sub $18,        %[rounds] \\n\\t\"\n\t\t\"    add $(12 * 18), %[src]    \\n\\t\"\n\t\t\"    add $(16 * 18), %[dst]    \\n\\t\"\n\n\t\t// Enter a 9x unrolled loop for rounds of 9 or more.\n\t\t\"9:  cmp $9, %[rounds] \\n\\t\"\n\t\t\"    jl  6f            \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A(0)\n\t\t\"    \" ROUND_3_B_LAST(3)\n\t\t\"    sub $9,        %[rounds] \\n\\t\"\n\t\t\"    add $(12 * 9), %[src]    \\n\\t\"\n\t\t\"    add $(16 * 9), %[dst]    \\n\\t\"\n\n\t\t// Enter a 6x unrolled loop for rounds of 6 or more.\n\t\t\"6:  cmp $6, %[rounds] \\n\\t\"\n\t\t\"    jl  55f           \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A_LAST(0)\n\t\t\"    sub $6,        %[rounds] \\n\\t\"\n\t\t\"    add $(12 * 6), %[src]    \\n\\t\"\n\t\t\"    add $(16 * 6), %[dst]    \\n\\t\"\n\n\t\t// Dispatch the remaining rounds 0..5.\n\t\t\"55: cmp $3, %[rounds] \\n\\t\"\n\t\t\"    jg  45f           \\n\\t\"\n\t\t\"    je  3f            \\n\\t\"\n\t\t\"    cmp $1, %[rounds] \\n\\t\"\n\t\t\"    jg  2f            \\n\\t\"\n\t\t\"    je  1f            \\n\\t\"\n\t\t\"    jmp 0f            \\n\\t\"\n\n\t\t\"45: cmp $4, %[rounds] \\n\\t\"\n\t\t\"    je  4f            \\n\\t\"\n\n\t\t// Block of non-interlaced encoding rounds, which can each\n\t\t// individually be jumped to. Rounds fall through to the next.\n\t\t\"5: \" ROUND()\n\t\t\"4: \" ROUND()\n\t\t\"3: \" ROUND()\n\t\t\"2: \" ROUND()\n\t\t\"1: \" ROUND()\n\t\t\"0: \\n\\t\"\n\n\t\t// Outputs (modified).\n\t\t: [rounds] \"+r\"  (rounds),\n\t\t  [loops]  \"+r\"  (loops),\n\t\t  [src]    \"+r\"  (*s),\n\t\t  [dst]    \"+r\"  (*o),\n\t\t  [a]      \"=&x\" (a),\n\t\t  [b]      \"=&x\" (b),\n\t\t  [c]      \"=&x\" (c),\n\t\t  [d]      \"=&x\" (d),\n\t\t  [e]      \"=&x\" (e),\n\t\t  [f]      \"=&x\" (f)\n\n\t\t// Inputs (not modified).\n\t\t: [lut0] \"x\" (lut0),\n\t\t  [lut1] \"x\" (lut1),\n\t\t  [msk0] \"x\" (_mm_set1_epi32(0x0FC0FC00)),\n\t\t  [msk1] \"x\" (_mm_set1_epi32(0x04000040)),\n\t\t  [msk2] \"x\" (_mm_set1_epi32(0x003F03F0)),\n\t\t  [msk3] \"x\" (_mm_set1_epi32(0x01000010)),\n\t\t  [n51]  \"x\" (_mm_set1_epi8(51)),\n\t\t  [n25]  \"x\" (_mm_set1_epi8(25))\n\n\t\t// Clobbers.\n\t\t: \"cc\", \"memory\"\n\t);\n}\n\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx2/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#if HAVE_AVX2\n#include <immintrin.h>\n\n// Only enable inline assembly on supported compilers and on 64-bit CPUs.\n#ifndef BASE64_AVX2_USE_ASM\n# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64\n#  define BASE64_AVX2_USE_ASM 1\n# else\n#  define BASE64_AVX2_USE_ASM 0\n# endif\n#endif\n\n#include \"dec_reshuffle.c\"\n#include \"dec_loop.c\"\n\n#if BASE64_AVX2_USE_ASM\n# include \"enc_loop_asm.c\"\n#else\n# include \"enc_translate.c\"\n# include \"enc_reshuffle.c\"\n# include \"enc_loop.c\"\n#endif\n\n#endif\t// HAVE_AVX2\n\nBASE64_ENC_FUNCTION(avx2)\n{\n#if HAVE_AVX2\n\t#include \"../generic/enc_head.c\"\n\tenc_loop_avx2(&s, &slen, &o, &olen);\n\t#include \"../generic/enc_tail.c\"\n#else\n\tBASE64_ENC_STUB\n#endif\n}\n\nBASE64_DEC_FUNCTION(avx2)\n{\n#if HAVE_AVX2\n\t#include \"../generic/dec_head.c\"\n\tdec_loop_avx2(&s, &slen, &o, &olen);\n\t#include \"../generic/dec_tail.c\"\n#else\n\tBASE64_DEC_STUB\n#endif\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx2/dec_loop.c",
    "content": "static inline int\ndec_loop_avx2_inner (const uint8_t **s, uint8_t **o, size_t *rounds)\n{\n\tconst __m256i lut_lo = _mm256_setr_epi8(\n\t\t0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\n\t\t0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A,\n\t\t0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\n\t\t0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A);\n\n\tconst __m256i lut_hi = _mm256_setr_epi8(\n\t\t0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08,\n\t\t0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\n\t\t0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08,\n\t\t0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10);\n\n\tconst __m256i lut_roll = _mm256_setr_epi8(\n\t\t0,  16,  19,   4, -65, -65, -71, -71,\n\t\t0,   0,   0,   0,   0,   0,   0,   0,\n\t\t0,  16,  19,   4, -65, -65, -71, -71,\n\t\t0,   0,   0,   0,   0,   0,   0,   0);\n\n\tconst __m256i mask_2F = _mm256_set1_epi8(0x2F);\n\n\t// Load input:\n\t__m256i str = _mm256_loadu_si256((__m256i *) *s);\n\n\t// See the SSSE3 decoder for an explanation of the algorithm.\n\tconst __m256i hi_nibbles = _mm256_and_si256(_mm256_srli_epi32(str, 4), mask_2F);\n\tconst __m256i lo_nibbles = _mm256_and_si256(str, mask_2F);\n\tconst __m256i hi         = _mm256_shuffle_epi8(lut_hi, hi_nibbles);\n\tconst __m256i lo         = _mm256_shuffle_epi8(lut_lo, lo_nibbles);\n\n\tif (!_mm256_testz_si256(lo, hi)) {\n\t\treturn 0;\n\t}\n\n\tconst __m256i eq_2F = _mm256_cmpeq_epi8(str, mask_2F);\n\tconst __m256i roll  = _mm256_shuffle_epi8(lut_roll, _mm256_add_epi8(eq_2F, hi_nibbles));\n\n\t// Now simply add the delta values to the input:\n\tstr = _mm256_add_epi8(str, roll);\n\n\t// Reshuffle the input to packed 12-byte output format:\n\tstr = dec_reshuffle(str);\n\n\t// Store the output:\n\t_mm256_storeu_si256((__m256i *) *o, str);\n\n\t*s += 32;\n\t*o += 24;\n\t*rounds -= 1;\n\n\treturn 1;\n}\n\nstatic inline void\ndec_loop_avx2 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 45) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 32 bytes per round. Because 8 extra zero bytes are\n\t// written after the output, ensure that there will be at least 13\n\t// bytes of input data left to cover the gap. (11 data bytes and up to\n\t// two end-of-string markers.)\n\tsize_t rounds = (*slen - 13) / 32;\n\n\t*slen -= rounds * 32;\t// 32 bytes consumed per round\n\t*olen += rounds * 24;\t// 24 bytes produced per round\n\n\tdo {\n\t\tif (rounds >= 8) {\n\t\t\tif (dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tif (dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tif (dec_loop_avx2_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_avx2_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tdec_loop_avx2_inner(s, o, &rounds);\n\t\tbreak;\n\n\t} while (rounds > 0);\n\n\t// Adjust for any rounds that were skipped:\n\t*slen += rounds * 32;\n\t*olen -= rounds * 24;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx2/dec_reshuffle.c",
    "content": "static inline __m256i\ndec_reshuffle (const __m256i in)\n{\n\t// in, lower lane, bits, upper case are most significant bits, lower\n\t// case are least significant bits:\n\t// 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ\n\t// 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG\n\t// 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD\n\t// 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA\n\n\tconst __m256i merge_ab_and_bc = _mm256_maddubs_epi16(in, _mm256_set1_epi32(0x01400140));\n\t// 0000kkkk LLllllll 0000JJJJ JJjjKKKK\n\t// 0000hhhh IIiiiiii 0000GGGG GGggHHHH\n\t// 0000eeee FFffffff 0000DDDD DDddEEEE\n\t// 0000bbbb CCcccccc 0000AAAA AAaaBBBB\n\n\t__m256i out = _mm256_madd_epi16(merge_ab_and_bc, _mm256_set1_epi32(0x00011000));\n\t// 00000000 JJJJJJjj KKKKkkkk LLllllll\n\t// 00000000 GGGGGGgg HHHHhhhh IIiiiiii\n\t// 00000000 DDDDDDdd EEEEeeee FFffffff\n\t// 00000000 AAAAAAaa BBBBbbbb CCcccccc\n\n\t// Pack bytes together in each lane:\n\tout = _mm256_shuffle_epi8(out, _mm256_setr_epi8(\n\t\t2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1,\n\t\t2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1));\n\t// 00000000 00000000 00000000 00000000\n\t// LLllllll KKKKkkkk JJJJJJjj IIiiiiii\n\t// HHHHhhhh GGGGGGgg FFffffff EEEEeeee\n\t// DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa\n\n\t// Pack lanes:\n\treturn _mm256_permutevar8x32_epi32(out, _mm256_setr_epi32(0, 1, 2, 4, 5, 6, -1, -1));\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx2/enc_loop.c",
    "content": "static inline void\nenc_loop_avx2_inner_first (const uint8_t **s, uint8_t **o)\n{\n\t// First load is done at s - 0 to not get a segfault:\n\t__m256i src = _mm256_loadu_si256((__m256i *) *s);\n\n\t// Shift by 4 bytes, as required by enc_reshuffle:\n\tsrc = _mm256_permutevar8x32_epi32(src, _mm256_setr_epi32(0, 0, 1, 2, 3, 4, 5, 6));\n\n\t// Reshuffle, translate, store:\n\tsrc = enc_reshuffle(src);\n\tsrc = enc_translate(src);\n\t_mm256_storeu_si256((__m256i *) *o, src);\n\n\t// Subsequent loads will be done at s - 4, set pointer for next round:\n\t*s += 20;\n\t*o += 32;\n}\n\nstatic inline void\nenc_loop_avx2_inner (const uint8_t **s, uint8_t **o)\n{\n\t// Load input:\n\t__m256i src = _mm256_loadu_si256((__m256i *) *s);\n\n\t// Reshuffle, translate, store:\n\tsrc = enc_reshuffle(src);\n\tsrc = enc_translate(src);\n\t_mm256_storeu_si256((__m256i *) *o, src);\n\n\t*s += 24;\n\t*o += 32;\n}\n\nstatic inline void\nenc_loop_avx2 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 32) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 24 bytes at a time. Because blocks are loaded 32\n\t// bytes at a time an offset of -4, ensure that there will be at least\n\t// 4 remaining bytes after the last round, so that the final read will\n\t// not pass beyond the bounds of the input buffer:\n\tsize_t rounds = (*slen - 4) / 24;\n\n\t*slen -= rounds * 24;   // 24 bytes consumed per round\n\t*olen += rounds * 32;   // 32 bytes produced per round\n\n\t// The first loop iteration requires special handling to ensure that\n\t// the read, which is done at an offset, does not underflow the buffer:\n\tenc_loop_avx2_inner_first(s, o);\n\trounds--;\n\n\twhile (rounds > 0) {\n\t\tif (rounds >= 8) {\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\trounds -= 8;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\trounds -= 4;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\tenc_loop_avx2_inner(s, o);\n\t\t\trounds -= 2;\n\t\t\tcontinue;\n\t\t}\n\t\tenc_loop_avx2_inner(s, o);\n\t\tbreak;\n\t}\n\n\t// Add the offset back:\n\t*s += 4;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx2/enc_loop_asm.c",
    "content": "// Apologies in advance for combining the preprocessor with inline assembly,\n// two notoriously gnarly parts of C, but it was necessary to avoid a lot of\n// code repetition. The preprocessor is used to template large sections of\n// inline assembly that differ only in the registers used. If the code was\n// written out by hand, it would become very large and hard to audit.\n\n// Generate a block of inline assembly that loads register R0 from memory. The\n// offset at which the register is loaded is set by the given round and a\n// constant offset.\n#define LOAD(R0, ROUND, OFFSET) \\\n\t\"vlddqu (\"#ROUND\" * 24 + \"#OFFSET\")(%[src]), %[\"R0\"] \\n\\t\"\n\n// Generate a block of inline assembly that deinterleaves and shuffles register\n// R0 using preloaded constants. Outputs in R0 and R1.\n#define SHUF(R0, R1, R2) \\\n\t\"vpshufb  %[lut0], %[\"R0\"], %[\"R1\"] \\n\\t\" \\\n\t\"vpand    %[\"R1\"], %[msk0], %[\"R2\"] \\n\\t\" \\\n\t\"vpand    %[\"R1\"], %[msk2], %[\"R1\"] \\n\\t\" \\\n\t\"vpmulhuw %[\"R2\"], %[msk1], %[\"R2\"] \\n\\t\" \\\n\t\"vpmullw  %[\"R1\"], %[msk3], %[\"R1\"] \\n\\t\" \\\n\t\"vpor     %[\"R1\"], %[\"R2\"], %[\"R1\"] \\n\\t\"\n\n// Generate a block of inline assembly that takes R0 and R1 and translates\n// their contents to the base64 alphabet, using preloaded constants.\n#define TRAN(R0, R1, R2) \\\n\t\"vpsubusb %[n51],  %[\"R1\"], %[\"R0\"] \\n\\t\" \\\n\t\"vpcmpgtb %[n25],  %[\"R1\"], %[\"R2\"] \\n\\t\" \\\n\t\"vpsubb   %[\"R2\"], %[\"R0\"], %[\"R0\"] \\n\\t\" \\\n\t\"vpshufb  %[\"R0\"], %[lut1], %[\"R2\"] \\n\\t\" \\\n\t\"vpaddb   %[\"R1\"], %[\"R2\"], %[\"R0\"] \\n\\t\"\n\n// Generate a block of inline assembly that stores the given register R0 at an\n// offset set by the given round.\n#define STOR(R0, ROUND) \\\n\t\"vmovdqu %[\"R0\"], (\"#ROUND\" * 32)(%[dst]) \\n\\t\"\n\n// Generate a block of inline assembly that generates a single self-contained\n// encoder round: fetch the data, process it, and store the result. Then update\n// the source and destination pointers.\n#define ROUND() \\\n\tLOAD(\"a\", 0, -4) \\\n\tSHUF(\"a\", \"b\", \"c\") \\\n\tTRAN(\"a\", \"b\", \"c\") \\\n\tSTOR(\"a\", 0) \\\n\t\"add $24, %[src] \\n\\t\" \\\n\t\"add $32, %[dst] \\n\\t\"\n\n// Define a macro that initiates a three-way interleaved encoding round by\n// preloading registers a, b and c from memory.\n// The register graph shows which registers are in use during each step, and\n// is a visual aid for choosing registers for that step. Symbol index:\n//\n//  +  indicates that a register is loaded by that step.\n//  |  indicates that a register is in use and must not be touched.\n//  -  indicates that a register is decommissioned by that step.\n//  x  indicates that a register is used as a temporary by that step.\n//  V  indicates that a register is an input or output to the macro.\n//\n#define ROUND_3_INIT() \t\t\t/*  a b c d e f  */ \\\n\tLOAD(\"a\",   0,  -4)\t\t/*  +            */ \\\n\tSHUF(\"a\", \"d\", \"e\")\t\t/*  |     + x    */ \\\n\tLOAD(\"b\",   1,  -4)\t\t/*  | +   |      */ \\\n\tTRAN(\"a\", \"d\", \"e\")\t\t/*  | |   - x    */ \\\n\tLOAD(\"c\",   2,  -4)\t\t/*  V V V        */\n\n// Define a macro that translates, shuffles and stores the input registers A, B\n// and C, and preloads registers D, E and F for the next round.\n// This macro can be arbitrarily daisy-chained by feeding output registers D, E\n// and F back into the next round as input registers A, B and C. The macro\n// carefully interleaves memory operations with data operations for optimal\n// pipelined performance.\n\n#define ROUND_3(ROUND, A,B,C,D,E,F) \t/*  A B C D E F  */ \\\n\tLOAD(D, (ROUND + 3), -4)\t/*  V V V +      */ \\\n\tSHUF(B, E, F)\t\t\t/*  | | | | + x  */ \\\n\tSTOR(A, (ROUND + 0))\t\t/*  - | | | |    */ \\\n\tTRAN(B, E, F)\t\t\t/*    | | | - x  */ \\\n\tLOAD(E, (ROUND + 4), -4)\t/*    | | | +    */ \\\n\tSHUF(C, A, F)\t\t\t/*  + | | | | x  */ \\\n\tSTOR(B, (ROUND + 1))\t\t/*  | - | | |    */ \\\n\tTRAN(C, A, F)\t\t\t/*  -   | | | x  */ \\\n\tLOAD(F, (ROUND + 5), -4)\t/*      | | | +  */ \\\n\tSHUF(D, A, B)\t\t\t/*  + x | | | |  */ \\\n\tSTOR(C, (ROUND + 2))\t\t/*  |   - | | |  */ \\\n\tTRAN(D, A, B)\t\t\t/*  - x   V V V  */\n\n// Define a macro that terminates a ROUND_3 macro by taking pre-loaded\n// registers D, E and F, and translating, shuffling and storing them.\n#define ROUND_3_END(ROUND, A,B,C,D,E,F)\t/*  A B C D E F  */ \\\n\tSHUF(E, A, B)\t\t\t/*  + x   V V V  */ \\\n\tSTOR(D, (ROUND + 3))\t\t/*  |     - | |  */ \\\n\tTRAN(E, A, B)\t\t\t/*  - x     | |  */ \\\n\tSHUF(F, C, D)\t\t\t/*      + x | |  */ \\\n\tSTOR(E, (ROUND + 4))\t\t/*      |   - |  */ \\\n\tTRAN(F, C, D)\t\t\t/*      - x   |  */ \\\n\tSTOR(F, (ROUND + 5))\t\t/*            -  */\n\n// Define a type A round. Inputs are a, b, and c, outputs are d, e, and f.\n#define ROUND_3_A(ROUND) \\\n\tROUND_3(ROUND, \"a\", \"b\", \"c\", \"d\", \"e\", \"f\")\n\n// Define a type B round. Inputs and outputs are swapped with regard to type A.\n#define ROUND_3_B(ROUND) \\\n\tROUND_3(ROUND, \"d\", \"e\", \"f\", \"a\", \"b\", \"c\")\n\n// Terminating macro for a type A round.\n#define ROUND_3_A_LAST(ROUND) \\\n\tROUND_3_A(ROUND) \\\n\tROUND_3_END(ROUND, \"a\", \"b\", \"c\", \"d\", \"e\", \"f\")\n\n// Terminating macro for a type B round.\n#define ROUND_3_B_LAST(ROUND) \\\n\tROUND_3_B(ROUND) \\\n\tROUND_3_END(ROUND, \"d\", \"e\", \"f\", \"a\", \"b\", \"c\")\n\n// Suppress clang's warning that the literal string in the asm statement is\n// overlong (longer than the ISO-mandated minimum size of 4095 bytes for C99\n// compilers). It may be true, but the goal here is not C99 portability.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Woverlength-strings\"\n\nstatic inline void\nenc_loop_avx2 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\t// For a clearer explanation of the algorithm used by this function,\n\t// please refer to the plain (not inline assembly) implementation. This\n\t// function follows the same basic logic.\n\n\tif (*slen < 32) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 24 bytes at a time. Because blocks are loaded 32\n\t// bytes at a time an offset of -4, ensure that there will be at least\n\t// 4 remaining bytes after the last round, so that the final read will\n\t// not pass beyond the bounds of the input buffer.\n\tsize_t rounds = (*slen - 4) / 24;\n\n\t*slen -= rounds * 24;   // 24 bytes consumed per round\n\t*olen += rounds * 32;   // 32 bytes produced per round\n\n\t// Pre-decrement the number of rounds to get the number of rounds\n\t// *after* the first round, which is handled as a special case.\n\trounds--;\n\n\t// Number of times to go through the 36x loop.\n\tsize_t loops = rounds / 36;\n\n\t// Number of rounds remaining after the 36x loop.\n\trounds %= 36;\n\n\t// Lookup tables.\n\tconst __m256i lut0 = _mm256_set_epi8(\n\t\t10, 11,  9, 10,  7,  8,  6,  7,  4,  5,  3,  4,  1,  2,  0,  1,\n\t\t14, 15, 13, 14, 11, 12, 10, 11,  8,  9,  7,  8,  5,  6,  4,  5);\n\n\tconst __m256i lut1 = _mm256_setr_epi8(\n\t\t65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0,\n\t\t65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0);\n\n\t// Temporary registers.\n\t__m256i a, b, c, d, e;\n\n\t// Temporary register f doubles as the shift mask for the first round.\n\t__m256i f = _mm256_setr_epi32(0, 0, 1, 2, 3, 4, 5, 6);\n\n\t__asm__ volatile (\n\n\t\t// The first loop iteration requires special handling to ensure\n\t\t// that the read, which is normally done at an offset of -4,\n\t\t// does not underflow the buffer. Load the buffer at an offset\n\t\t// of 0 and permute the input to achieve the same effect.\n\t\tLOAD(\"a\", 0, 0)\n\t\t\"vpermd %[a], %[f], %[a] \\n\\t\"\n\n\t\t// Perform the standard shuffling and translation steps.\n\t\tSHUF(\"a\", \"b\", \"c\")\n\t\tTRAN(\"a\", \"b\", \"c\")\n\n\t\t// Store the result and increment the source and dest pointers.\n\t\t\"vmovdqu %[a], (%[dst]) \\n\\t\"\n\t\t\"add     $24,  %[src]   \\n\\t\"\n\t\t\"add     $32,  %[dst]   \\n\\t\"\n\n\t\t// If there are 36 rounds or more, enter a 36x unrolled loop of\n\t\t// interleaved encoding rounds. The rounds interleave memory\n\t\t// operations (load/store) with data operations (table lookups,\n\t\t// etc) to maximize pipeline throughput.\n\t\t\"    test %[loops], %[loops] \\n\\t\"\n\t\t\"    jz   18f                \\n\\t\"\n\t\t\"    jmp  36f                \\n\\t\"\n\t\t\"                            \\n\\t\"\n\t\t\".balign 64                  \\n\\t\"\n\t\t\"36: \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A( 0)\n\t\t\"    \" ROUND_3_B( 3)\n\t\t\"    \" ROUND_3_A( 6)\n\t\t\"    \" ROUND_3_B( 9)\n\t\t\"    \" ROUND_3_A(12)\n\t\t\"    \" ROUND_3_B(15)\n\t\t\"    \" ROUND_3_A(18)\n\t\t\"    \" ROUND_3_B(21)\n\t\t\"    \" ROUND_3_A(24)\n\t\t\"    \" ROUND_3_B(27)\n\t\t\"    \" ROUND_3_A_LAST(30)\n\t\t\"    add $(24 * 36), %[src] \\n\\t\"\n\t\t\"    add $(32 * 36), %[dst] \\n\\t\"\n\t\t\"    dec %[loops]           \\n\\t\"\n\t\t\"    jnz 36b                \\n\\t\"\n\n\t\t// Enter an 18x unrolled loop for rounds of 18 or more.\n\t\t\"18: cmp $18, %[rounds] \\n\\t\"\n\t\t\"    jl  9f             \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A(0)\n\t\t\"    \" ROUND_3_B(3)\n\t\t\"    \" ROUND_3_A(6)\n\t\t\"    \" ROUND_3_B(9)\n\t\t\"    \" ROUND_3_A_LAST(12)\n\t\t\"    sub $18,        %[rounds] \\n\\t\"\n\t\t\"    add $(24 * 18), %[src]    \\n\\t\"\n\t\t\"    add $(32 * 18), %[dst]    \\n\\t\"\n\n\t\t// Enter a 9x unrolled loop for rounds of 9 or more.\n\t\t\"9:  cmp $9, %[rounds] \\n\\t\"\n\t\t\"    jl  6f            \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A(0)\n\t\t\"    \" ROUND_3_B_LAST(3)\n\t\t\"    sub $9,        %[rounds] \\n\\t\"\n\t\t\"    add $(24 * 9), %[src]    \\n\\t\"\n\t\t\"    add $(32 * 9), %[dst]    \\n\\t\"\n\n\t\t// Enter a 6x unrolled loop for rounds of 6 or more.\n\t\t\"6:  cmp $6, %[rounds] \\n\\t\"\n\t\t\"    jl  55f           \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A_LAST(0)\n\t\t\"    sub $6,        %[rounds] \\n\\t\"\n\t\t\"    add $(24 * 6), %[src]    \\n\\t\"\n\t\t\"    add $(32 * 6), %[dst]    \\n\\t\"\n\n\t\t// Dispatch the remaining rounds 0..5.\n\t\t\"55: cmp $3, %[rounds] \\n\\t\"\n\t\t\"    jg  45f           \\n\\t\"\n\t\t\"    je  3f            \\n\\t\"\n\t\t\"    cmp $1, %[rounds] \\n\\t\"\n\t\t\"    jg  2f            \\n\\t\"\n\t\t\"    je  1f            \\n\\t\"\n\t\t\"    jmp 0f            \\n\\t\"\n\n\t\t\"45: cmp $4, %[rounds] \\n\\t\"\n\t\t\"    je  4f            \\n\\t\"\n\n\t\t// Block of non-interlaced encoding rounds, which can each\n\t\t// individually be jumped to. Rounds fall through to the next.\n\t\t\"5: \" ROUND()\n\t\t\"4: \" ROUND()\n\t\t\"3: \" ROUND()\n\t\t\"2: \" ROUND()\n\t\t\"1: \" ROUND()\n\t\t\"0: \\n\\t\"\n\n\t\t// Outputs (modified).\n\t\t: [rounds] \"+r\"  (rounds),\n\t\t  [loops]  \"+r\"  (loops),\n\t\t  [src]    \"+r\"  (*s),\n\t\t  [dst]    \"+r\"  (*o),\n\t\t  [a]      \"=&x\" (a),\n\t\t  [b]      \"=&x\" (b),\n\t\t  [c]      \"=&x\" (c),\n\t\t  [d]      \"=&x\" (d),\n\t\t  [e]      \"=&x\" (e),\n\t\t  [f]      \"+x\"  (f)\n\n\t\t// Inputs (not modified).\n\t\t: [lut0] \"x\" (lut0),\n\t\t  [lut1] \"x\" (lut1),\n\t\t  [msk0] \"x\" (_mm256_set1_epi32(0x0FC0FC00)),\n\t\t  [msk1] \"x\" (_mm256_set1_epi32(0x04000040)),\n\t\t  [msk2] \"x\" (_mm256_set1_epi32(0x003F03F0)),\n\t\t  [msk3] \"x\" (_mm256_set1_epi32(0x01000010)),\n\t\t  [n51]  \"x\" (_mm256_set1_epi8(51)),\n\t\t  [n25]  \"x\" (_mm256_set1_epi8(25))\n\n\t\t// Clobbers.\n\t\t: \"cc\", \"memory\"\n\t);\n}\n\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx2/enc_reshuffle.c",
    "content": "static inline __m256i\nenc_reshuffle (const __m256i input)\n{\n\t// Translation of the SSSE3 reshuffling algorithm to AVX2. This one\n\t// works with shifted (4 bytes) input in order to be able to work\n\t// efficiently in the two 128-bit lanes.\n\n\t// Input, bytes MSB to LSB:\n\t// 0 0 0 0 x w v u t s r q p o n m\n\t// l k j i h g f e d c b a 0 0 0 0\n\n\tconst __m256i in = _mm256_shuffle_epi8(input, _mm256_set_epi8(\n\t\t10, 11,  9, 10,\n\t\t 7,  8,  6,  7,\n\t\t 4,  5,  3,  4,\n\t\t 1,  2,  0,  1,\n\n\t\t14, 15, 13, 14,\n\t\t11, 12, 10, 11,\n\t\t 8,  9,  7,  8,\n\t\t 5,  6,  4,  5));\n\t// in, bytes MSB to LSB:\n\t// w x v w\n\t// t u s t\n\t// q r p q\n\t// n o m n\n\t// k l j k\n\t// h i g h\n\t// e f d e\n\t// b c a b\n\n\tconst __m256i t0 = _mm256_and_si256(in, _mm256_set1_epi32(0x0FC0FC00));\n\t// bits, upper case are most significant bits, lower case are least\n\t// significant bits.\n\t// 0000wwww XX000000 VVVVVV00 00000000\n\t// 0000tttt UU000000 SSSSSS00 00000000\n\t// 0000qqqq RR000000 PPPPPP00 00000000\n\t// 0000nnnn OO000000 MMMMMM00 00000000\n\t// 0000kkkk LL000000 JJJJJJ00 00000000\n\t// 0000hhhh II000000 GGGGGG00 00000000\n\t// 0000eeee FF000000 DDDDDD00 00000000\n\t// 0000bbbb CC000000 AAAAAA00 00000000\n\n\tconst __m256i t1 = _mm256_mulhi_epu16(t0, _mm256_set1_epi32(0x04000040));\n\t// 00000000 00wwwwXX 00000000 00VVVVVV\n\t// 00000000 00ttttUU 00000000 00SSSSSS\n\t// 00000000 00qqqqRR 00000000 00PPPPPP\n\t// 00000000 00nnnnOO 00000000 00MMMMMM\n\t// 00000000 00kkkkLL 00000000 00JJJJJJ\n\t// 00000000 00hhhhII 00000000 00GGGGGG\n\t// 00000000 00eeeeFF 00000000 00DDDDDD\n\t// 00000000 00bbbbCC 00000000 00AAAAAA\n\n\tconst __m256i t2 = _mm256_and_si256(in, _mm256_set1_epi32(0x003F03F0));\n\t// 00000000 00xxxxxx 000000vv WWWW0000\n\t// 00000000 00uuuuuu 000000ss TTTT0000\n\t// 00000000 00rrrrrr 000000pp QQQQ0000\n\t// 00000000 00oooooo 000000mm NNNN0000\n\t// 00000000 00llllll 000000jj KKKK0000\n\t// 00000000 00iiiiii 000000gg HHHH0000\n\t// 00000000 00ffffff 000000dd EEEE0000\n\t// 00000000 00cccccc 000000aa BBBB0000\n\n\tconst __m256i t3 = _mm256_mullo_epi16(t2, _mm256_set1_epi32(0x01000010));\n\t// 00xxxxxx 00000000 00vvWWWW 00000000\n\t// 00uuuuuu 00000000 00ssTTTT 00000000\n\t// 00rrrrrr 00000000 00ppQQQQ 00000000\n\t// 00oooooo 00000000 00mmNNNN 00000000\n\t// 00llllll 00000000 00jjKKKK 00000000\n\t// 00iiiiii 00000000 00ggHHHH 00000000\n\t// 00ffffff 00000000 00ddEEEE 00000000\n\t// 00cccccc 00000000 00aaBBBB 00000000\n\n\treturn _mm256_or_si256(t1, t3);\n\t// 00xxxxxx 00wwwwXX 00vvWWWW 00VVVVVV\n\t// 00uuuuuu 00ttttUU 00ssTTTT 00SSSSSS\n\t// 00rrrrrr 00qqqqRR 00ppQQQQ 00PPPPPP\n\t// 00oooooo 00nnnnOO 00mmNNNN 00MMMMMM\n\t// 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ\n\t// 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG\n\t// 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD\n\t// 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx2/enc_translate.c",
    "content": "static inline __m256i\nenc_translate (const __m256i in)\n{\n\t// A lookup table containing the absolute offsets for all ranges:\n\tconst __m256i lut = _mm256_setr_epi8(\n\t\t65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0,\n\t\t65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0);\n\n\t// Translate values 0..63 to the Base64 alphabet. There are five sets:\n\t// #  From      To         Abs    Index  Characters\n\t// 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ\n\t// 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz\n\t// 2  [52..61]  [48..57]    -4  [2..11]  0123456789\n\t// 3  [62]      [43]       -19       12  +\n\t// 4  [63]      [47]       -16       13  /\n\n\t// Create LUT indices from the input. The index for range #0 is right,\n\t// others are 1 less than expected:\n\t__m256i indices = _mm256_subs_epu8(in, _mm256_set1_epi8(51));\n\n\t// mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:\n\tconst __m256i mask = _mm256_cmpgt_epi8(in, _mm256_set1_epi8(25));\n\n\t// Subtract -1, so add 1 to indices for range #[1..4]. All indices are\n\t// now correct:\n\tindices = _mm256_sub_epi8(indices, mask);\n\n\t// Add offsets to input values:\n\treturn _mm256_add_epi8(in, _mm256_shuffle_epi8(lut, indices));\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx512/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#if HAVE_AVX512\n#include <immintrin.h>\n\n#include \"../avx2/dec_reshuffle.c\"\n#include \"../avx2/dec_loop.c\"\n#include \"enc_reshuffle_translate.c\"\n#include \"enc_loop.c\"\n\n#endif\t// HAVE_AVX512\n\nBASE64_ENC_FUNCTION(avx512)\n{\n#if HAVE_AVX512\n\t#include \"../generic/enc_head.c\"\n\tenc_loop_avx512(&s, &slen, &o, &olen);\n\t#include \"../generic/enc_tail.c\"\n#else\n\tBASE64_ENC_STUB\n#endif\n}\n\n// Reuse AVX2 decoding. Not supporting AVX512 at present\nBASE64_DEC_FUNCTION(avx512)\n{\n#if HAVE_AVX512\n\t#include \"../generic/dec_head.c\"\n\tdec_loop_avx2(&s, &slen, &o, &olen);\n\t#include \"../generic/dec_tail.c\"\n#else\n\tBASE64_DEC_STUB\n#endif\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx512/enc_loop.c",
    "content": "static inline void\nenc_loop_avx512_inner (const uint8_t **s, uint8_t **o)\n{\n\t// Load input.\n\t__m512i src = _mm512_loadu_si512((__m512i *) *s);\n\n\t// Reshuffle, translate, store.\n\tsrc = enc_reshuffle_translate(src);\n\t_mm512_storeu_si512((__m512i *) *o, src);\n\n\t*s += 48;\n\t*o += 64;\n}\n\nstatic inline void\nenc_loop_avx512 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 64) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 48 bytes at a time. Because blocks are loaded 64\n\t// bytes at a time, ensure that there will be at least 24 remaining\n\t// bytes after the last round, so that the final read will not pass\n\t// beyond the bounds of the input buffer.\n\tsize_t rounds = (*slen - 24) / 48;\n\n\t*slen -= rounds * 48;   // 48 bytes consumed per round\n\t*olen += rounds * 64;   // 64 bytes produced per round\n\n\twhile (rounds > 0) {\n\t\tif (rounds >= 8) {\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\trounds -= 8;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\trounds -= 4;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\tenc_loop_avx512_inner(s, o);\n\t\t\trounds -= 2;\n\t\t\tcontinue;\n\t\t}\n\t\tenc_loop_avx512_inner(s, o);\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/avx512/enc_reshuffle_translate.c",
    "content": "// AVX512 algorithm is based on permutevar and multishift. The code is based on\n// https://github.com/WojciechMula/base64simd which is under BSD-2 license.\n\nstatic inline __m512i\nenc_reshuffle_translate (const __m512i input)\n{\n\t// 32-bit input\n\t// [ 0  0  0  0  0  0  0  0|c1 c0 d5 d4 d3 d2 d1 d0|\n\t//  b3 b2 b1 b0 c5 c4 c3 c2|a5 a4 a3 a2 a1 a0 b5 b4]\n\t// output order  [1, 2, 0, 1]\n\t// [b3 b2 b1 b0 c5 c4 c3 c2|c1 c0 d5 d4 d3 d2 d1 d0|\n\t//  a5 a4 a3 a2 a1 a0 b5 b4|b3 b2 b1 b0 c3 c2 c1 c0]\n\n\tconst __m512i shuffle_input = _mm512_setr_epi32(0x01020001,\n\t                                                0x04050304,\n\t                                                0x07080607,\n\t                                                0x0a0b090a,\n\t                                                0x0d0e0c0d,\n\t                                                0x10110f10,\n\t                                                0x13141213,\n\t                                                0x16171516,\n\t                                                0x191a1819,\n\t                                                0x1c1d1b1c,\n\t                                                0x1f201e1f,\n\t                                                0x22232122,\n\t                                                0x25262425,\n\t                                                0x28292728,\n\t                                                0x2b2c2a2b,\n\t                                                0x2e2f2d2e);\n\n\t// Reorder bytes\n\t// [b3 b2 b1 b0 c5 c4 c3 c2|c1 c0 d5 d4 d3 d2 d1 d0|\n\t//  a5 a4 a3 a2 a1 a0 b5 b4|b3 b2 b1 b0 c3 c2 c1 c0]\n\tconst __m512i in = _mm512_permutexvar_epi8(shuffle_input, input);\n\n\t// After multishift a single 32-bit lane has following layout\n\t// [c1 c0 d5 d4 d3 d2 d1 d0|b1 b0 c5 c4 c3 c2 c1 c0|\n\t//  a1 a0 b5 b4 b3 b2 b1 b0|d1 d0 a5 a4 a3 a2 a1 a0]\n\t// (a = [10:17], b = [4:11], c = [22:27], d = [16:21])\n\n\t// 48, 54, 36, 42, 16, 22, 4, 10\n\tconst __m512i shifts = _mm512_set1_epi64(0x3036242a1016040alu);\n\t__m512i shuffled_in = _mm512_multishift_epi64_epi8(shifts, in);\n\n\t// Translate immediatedly after reshuffled.\n\tconst __m512i lookup = _mm512_loadu_si512(base64_table_enc_6bit);\n\n\t// Translation 6-bit values to ASCII.\n\treturn _mm512_permutexvar_epi8(shuffled_in, lookup);\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/generic/32/dec_loop.c",
    "content": "static inline int\ndec_loop_generic_32_inner (const uint8_t **s, uint8_t **o, size_t *rounds)\n{\n\tconst uint32_t str\n\t\t= base64_table_dec_32bit_d0[(*s)[0]]\n\t\t| base64_table_dec_32bit_d1[(*s)[1]]\n\t\t| base64_table_dec_32bit_d2[(*s)[2]]\n\t\t| base64_table_dec_32bit_d3[(*s)[3]];\n\n#if BASE64_LITTLE_ENDIAN\n\n\t// LUTs for little-endian set MSB in case of invalid character:\n\tif (str & UINT32_C(0x80000000)) {\n\t\treturn 0;\n\t}\n#else\n\t// LUTs for big-endian set LSB in case of invalid character:\n\tif (str & UINT32_C(1)) {\n\t\treturn 0;\n\t}\n#endif\n\t// Store the output:\n\tmemcpy(*o, &str, sizeof (str));\n\n\t*s += 4;\n\t*o += 3;\n\t*rounds -= 1;\n\n\treturn 1;\n}\n\nstatic inline void\ndec_loop_generic_32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 8) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 4 bytes per round. Because one extra zero byte is\n\t// written after the output, ensure that there will be at least 4 bytes\n\t// of input data left to cover the gap. (Two data bytes and up to two\n\t// end-of-string markers.)\n\tsize_t rounds = (*slen - 4) / 4;\n\n\t*slen -= rounds * 4;\t// 4 bytes consumed per round\n\t*olen += rounds * 3;\t// 3 bytes produced per round\n\n\tdo {\n\t\tif (rounds >= 8) {\n\t\t\tif (dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tif (dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tif (dec_loop_generic_32_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_generic_32_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tdec_loop_generic_32_inner(s, o, &rounds);\n\t\tbreak;\n\n\t} while (rounds > 0);\n\n\t// Adjust for any rounds that were skipped:\n\t*slen += rounds * 4;\n\t*olen -= rounds * 3;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/generic/32/enc_loop.c",
    "content": "static inline void\nenc_loop_generic_32_inner (const uint8_t **s, uint8_t **o)\n{\n\tuint32_t src;\n\n\t// Load input:\n\tmemcpy(&src, *s, sizeof (src));\n\n\t// Reorder to 32-bit big-endian, if not already in that format. The\n\t// workset must be in big-endian, otherwise the shifted bits do not\n\t// carry over properly among adjacent bytes:\n\tsrc = BASE64_HTOBE32(src);\n\n\t// Two indices for the 12-bit lookup table:\n\tconst size_t index0 = (src >> 20) & 0xFFFU;\n\tconst size_t index1 = (src >>  8) & 0xFFFU;\n\n\t// Table lookup and store:\n\tmemcpy(*o + 0, base64_table_enc_12bit + index0, 2);\n\tmemcpy(*o + 2, base64_table_enc_12bit + index1, 2);\n\n\t*s += 3;\n\t*o += 4;\n}\n\nstatic inline void\nenc_loop_generic_32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 4) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 3 bytes at a time. Because blocks are loaded 4\n\t// bytes at a time, ensure that there will be at least one remaining\n\t// byte after the last round, so that the final read will not pass\n\t// beyond the bounds of the input buffer:\n\tsize_t rounds = (*slen - 1) / 3;\n\n\t*slen -= rounds * 3;\t// 3 bytes consumed per round\n\t*olen += rounds * 4;\t// 4 bytes produced per round\n\n\tdo {\n\t\tif (rounds >= 8) {\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\trounds -= 8;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\trounds -= 4;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\tenc_loop_generic_32_inner(s, o);\n\t\t\trounds -= 2;\n\t\t\tcontinue;\n\t\t}\n\t\tenc_loop_generic_32_inner(s, o);\n\t\tbreak;\n\n\t} while (rounds > 0);\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/generic/64/enc_loop.c",
    "content": "static inline void\nenc_loop_generic_64_inner (const uint8_t **s, uint8_t **o)\n{\n\tuint64_t src;\n\n\t// Load input:\n\tmemcpy(&src, *s, sizeof (src));\n\n\t// Reorder to 64-bit big-endian, if not already in that format. The\n\t// workset must be in big-endian, otherwise the shifted bits do not\n\t// carry over properly among adjacent bytes:\n\tsrc = BASE64_HTOBE64(src);\n\n\t// Four indices for the 12-bit lookup table:\n\tconst size_t index0 = (src >> 52) & 0xFFFU;\n\tconst size_t index1 = (src >> 40) & 0xFFFU;\n\tconst size_t index2 = (src >> 28) & 0xFFFU;\n\tconst size_t index3 = (src >> 16) & 0xFFFU;\n\n\t// Table lookup and store:\n\tmemcpy(*o + 0, base64_table_enc_12bit + index0, 2);\n\tmemcpy(*o + 2, base64_table_enc_12bit + index1, 2);\n\tmemcpy(*o + 4, base64_table_enc_12bit + index2, 2);\n\tmemcpy(*o + 6, base64_table_enc_12bit + index3, 2);\n\n\t*s += 6;\n\t*o += 8;\n}\n\nstatic inline void\nenc_loop_generic_64 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 8) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 6 bytes at a time. Because blocks are loaded 8\n\t// bytes at a time, ensure that there will be at least 2 remaining\n\t// bytes after the last round, so that the final read will not pass\n\t// beyond the bounds of the input buffer:\n\tsize_t rounds = (*slen - 2) / 6;\n\n\t*slen -= rounds * 6;\t// 6 bytes consumed per round\n\t*olen += rounds * 8;\t// 8 bytes produced per round\n\n\tdo {\n\t\tif (rounds >= 8) {\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\trounds -= 8;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\trounds -= 4;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\tenc_loop_generic_64_inner(s, o);\n\t\t\trounds -= 2;\n\t\t\tcontinue;\n\t\t}\n\t\tenc_loop_generic_64_inner(s, o);\n\t\tbreak;\n\n\t} while (rounds > 0);\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/generic/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <string.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#if BASE64_WORDSIZE == 32\n#  include \"32/enc_loop.c\"\n#elif BASE64_WORDSIZE == 64\n#  include \"64/enc_loop.c\"\n#endif\n\n#if BASE64_WORDSIZE >= 32\n#  include \"32/dec_loop.c\"\n#endif\n\nBASE64_ENC_FUNCTION(plain)\n{\n\t#include \"enc_head.c\"\n#if BASE64_WORDSIZE == 32\n\tenc_loop_generic_32(&s, &slen, &o, &olen);\n#elif BASE64_WORDSIZE == 64\n\tenc_loop_generic_64(&s, &slen, &o, &olen);\n#endif\n\t#include \"enc_tail.c\"\n}\n\nBASE64_DEC_FUNCTION(plain)\n{\n\t#include \"dec_head.c\"\n#if BASE64_WORDSIZE >= 32\n\tdec_loop_generic_32(&s, &slen, &o, &olen);\n#endif\n\t#include \"dec_tail.c\"\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/generic/dec_head.c",
    "content": "int ret = 0;\nconst uint8_t *s = (const uint8_t *) src;\nuint8_t *o = (uint8_t *) out;\nuint8_t q;\n\n// Use local temporaries to avoid cache thrashing:\nsize_t olen = 0;\nsize_t slen = srclen;\nstruct base64_state st;\nst.eof = state->eof;\nst.bytes = state->bytes;\nst.carry = state->carry;\n\n// If we previously saw an EOF or an invalid character, bail out:\nif (st.eof) {\n\t*outlen = 0;\n\tret = 0;\n\t// If there was a trailing '=' to check, check it:\n\tif (slen && (st.eof == BASE64_AEOF)) {\n\t\tstate->bytes = 0;\n\t\tstate->eof = BASE64_EOF;\n\t\tret = ((base64_table_dec_8bit[*s++] == 254) && (slen == 1)) ? 1 : 0;\n\t}\n\treturn ret;\n}\n\n// Turn four 6-bit numbers into three bytes:\n// out[0] = 11111122\n// out[1] = 22223333\n// out[2] = 33444444\n\n// Duff's device again:\nswitch (st.bytes)\n{\n\tfor (;;)\n\t{\n\tcase 0:\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/generic/dec_tail.c",
    "content": "\t\tif (slen-- == 0) {\n\t\t\tret = 1;\n\t\t\tbreak;\n\t\t}\n\t\tif ((q = base64_table_dec_8bit[*s++]) >= 254) {\n\t\t\tst.eof = BASE64_EOF;\n\t\t\t// Treat character '=' as invalid for byte 0:\n\t\t\tbreak;\n\t\t}\n\t\tst.carry = q << 2;\n\t\tst.bytes++;\n\n\t\t// Deliberate fallthrough:\n\t\tBASE64_FALLTHROUGH\n\n\tcase 1:\tif (slen-- == 0) {\n\t\t\tret = 1;\n\t\t\tbreak;\n\t\t}\n\t\tif ((q = base64_table_dec_8bit[*s++]) >= 254) {\n\t\t\tst.eof = BASE64_EOF;\n\t\t\t// Treat character '=' as invalid for byte 1:\n\t\t\tbreak;\n\t\t}\n\t\t*o++ = st.carry | (q >> 4);\n\t\tst.carry = q << 4;\n\t\tst.bytes++;\n\t\tolen++;\n\n\t\t// Deliberate fallthrough:\n\t\tBASE64_FALLTHROUGH\n\n\tcase 2:\tif (slen-- == 0) {\n\t\t\tret = 1;\n\t\t\tbreak;\n\t\t}\n\t\tif ((q = base64_table_dec_8bit[*s++]) >= 254) {\n\t\t\tst.bytes++;\n\t\t\t// When q == 254, the input char is '='.\n\t\t\t// Check if next byte is also '=':\n\t\t\tif (q == 254) {\n\t\t\t\tif (slen-- != 0) {\n\t\t\t\t\tst.bytes = 0;\n\t\t\t\t\t// EOF:\n\t\t\t\t\tst.eof = BASE64_EOF;\n\t\t\t\t\tq = base64_table_dec_8bit[*s++];\n\t\t\t\t\tret = ((q == 254) && (slen == 0)) ? 1 : 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Almost EOF\n\t\t\t\t\tst.eof = BASE64_AEOF;\n\t\t\t\t\tret = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If we get here, there was an error:\n\t\t\tbreak;\n\t\t}\n\t\t*o++ = st.carry | (q >> 2);\n\t\tst.carry = q << 6;\n\t\tst.bytes++;\n\t\tolen++;\n\n\t\t// Deliberate fallthrough:\n\t\tBASE64_FALLTHROUGH\n\n\tcase 3:\tif (slen-- == 0) {\n\t\t\tret = 1;\n\t\t\tbreak;\n\t\t}\n\t\tif ((q = base64_table_dec_8bit[*s++]) >= 254) {\n\t\t\tst.bytes = 0;\n\t\t\tst.eof = BASE64_EOF;\n\t\t\t// When q == 254, the input char is '='. Return 1 and EOF.\n\t\t\t// When q == 255, the input char is invalid. Return 0 and EOF.\n\t\t\tret = ((q == 254) && (slen == 0)) ? 1 : 0;\n\t\t\tbreak;\n\t\t}\n\t\t*o++ = st.carry | q;\n\t\tst.carry = 0;\n\t\tst.bytes = 0;\n\t\tolen++;\n\t}\n}\n\nstate->eof = st.eof;\nstate->bytes = st.bytes;\nstate->carry = st.carry;\n*outlen = olen;\nreturn ret;\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/generic/enc_head.c",
    "content": "// Assume that *out is large enough to contain the output.\n// Theoretically it should be 4/3 the length of src.\nconst uint8_t *s = (const uint8_t *) src;\nuint8_t *o = (uint8_t *) out;\n\n// Use local temporaries to avoid cache thrashing:\nsize_t olen = 0;\nsize_t slen = srclen;\nstruct base64_state st;\nst.bytes = state->bytes;\nst.carry = state->carry;\n\n// Turn three bytes into four 6-bit numbers:\n// in[0] = 00111111\n// in[1] = 00112222\n// in[2] = 00222233\n// in[3] = 00333333\n\n// Duff's device, a for() loop inside a switch() statement. Legal!\nswitch (st.bytes)\n{\n\tfor (;;)\n\t{\n\tcase 0:\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/generic/enc_tail.c",
    "content": "\t\tif (slen-- == 0) {\n\t\t\tbreak;\n\t\t}\n\t\t*o++ = base64_table_enc_6bit[*s >> 2];\n\t\tst.carry = (*s++ << 4) & 0x30;\n\t\tst.bytes++;\n\t\tolen += 1;\n\n\t\t// Deliberate fallthrough:\n\t\tBASE64_FALLTHROUGH\n\n\tcase 1:\tif (slen-- == 0) {\n\t\t\tbreak;\n\t\t}\n\t\t*o++ = base64_table_enc_6bit[st.carry | (*s >> 4)];\n\t\tst.carry = (*s++ << 2) & 0x3C;\n\t\tst.bytes++;\n\t\tolen += 1;\n\n\t\t// Deliberate fallthrough:\n\t\tBASE64_FALLTHROUGH\n\n\tcase 2:\tif (slen-- == 0) {\n\t\t\tbreak;\n\t\t}\n\t\t*o++ = base64_table_enc_6bit[st.carry | (*s >> 6)];\n\t\t*o++ = base64_table_enc_6bit[*s++ & 0x3F];\n\t\tst.bytes = 0;\n\t\tolen += 2;\n\t}\n}\nstate->bytes = st.bytes;\nstate->carry = st.carry;\n*outlen = olen;\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon32/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <string.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#ifdef __arm__\n#  if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && HAVE_NEON32\n#    define BASE64_USE_NEON32\n#  endif\n#endif\n\n#ifdef BASE64_USE_NEON32\n#include <arm_neon.h>\n\n// Only enable inline assembly on supported compilers.\n#if defined(__GNUC__) || defined(__clang__)\n#define BASE64_NEON32_USE_ASM\n#endif\n\nstatic inline uint8x16_t\nvqtbl1q_u8 (const uint8x16_t lut, const uint8x16_t indices)\n{\n\t// NEON32 only supports 64-bit wide lookups in 128-bit tables. Emulate\n\t// the NEON64 `vqtbl1q_u8` intrinsic to do 128-bit wide lookups.\n\tuint8x8x2_t lut2;\n\tuint8x8x2_t result;\n\n\tlut2.val[0] = vget_low_u8(lut);\n\tlut2.val[1] = vget_high_u8(lut);\n\n\tresult.val[0] = vtbl2_u8(lut2, vget_low_u8(indices));\n\tresult.val[1] = vtbl2_u8(lut2, vget_high_u8(indices));\n\n\treturn vcombine_u8(result.val[0], result.val[1]);\n}\n\n#include \"../generic/32/dec_loop.c\"\n#include \"../generic/32/enc_loop.c\"\n#include \"dec_loop.c\"\n#include \"enc_reshuffle.c\"\n#include \"enc_translate.c\"\n#include \"enc_loop.c\"\n\n#endif\t// BASE64_USE_NEON32\n\n// Stride size is so large on these NEON 32-bit functions\n// (48 bytes encode, 32 bytes decode) that we inline the\n// uint32 codec to stay performant on smaller inputs.\n\nBASE64_ENC_FUNCTION(neon32)\n{\n#ifdef BASE64_USE_NEON32\n\t#include \"../generic/enc_head.c\"\n\tenc_loop_neon32(&s, &slen, &o, &olen);\n\tenc_loop_generic_32(&s, &slen, &o, &olen);\n\t#include \"../generic/enc_tail.c\"\n#else\n\tBASE64_ENC_STUB\n#endif\n}\n\nBASE64_DEC_FUNCTION(neon32)\n{\n#ifdef BASE64_USE_NEON32\n\t#include \"../generic/dec_head.c\"\n\tdec_loop_neon32(&s, &slen, &o, &olen);\n\tdec_loop_generic_32(&s, &slen, &o, &olen);\n\t#include \"../generic/dec_tail.c\"\n#else\n\tBASE64_DEC_STUB\n#endif\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon32/dec_loop.c",
    "content": "static inline int\nis_nonzero (const uint8x16_t v)\n{\n\tuint64_t u64;\n\tconst uint64x2_t v64 = vreinterpretq_u64_u8(v);\n\tconst uint32x2_t v32 = vqmovn_u64(v64);\n\n\tvst1_u64(&u64, vreinterpret_u64_u32(v32));\n\treturn u64 != 0;\n}\n\nstatic inline uint8x16_t\ndelta_lookup (const uint8x16_t v)\n{\n\tconst uint8x8_t lut = {\n\t\t0, 16, 19, 4, (uint8_t) -65, (uint8_t) -65, (uint8_t) -71, (uint8_t) -71,\n\t};\n\n\treturn vcombine_u8(\n\t\tvtbl1_u8(lut, vget_low_u8(v)),\n\t\tvtbl1_u8(lut, vget_high_u8(v)));\n}\n\nstatic inline uint8x16_t\ndec_loop_neon32_lane (uint8x16_t *lane)\n{\n\t// See the SSSE3 decoder for an explanation of the algorithm.\n\tconst uint8x16_t lut_lo = {\n\t\t0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\n\t\t0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A\n\t};\n\n\tconst uint8x16_t lut_hi = {\n\t\t0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08,\n\t\t0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10\n\t};\n\n\tconst uint8x16_t mask_0F = vdupq_n_u8(0x0F);\n\tconst uint8x16_t mask_2F = vdupq_n_u8(0x2F);\n\n\tconst uint8x16_t hi_nibbles = vshrq_n_u8(*lane, 4);\n\tconst uint8x16_t lo_nibbles = vandq_u8(*lane, mask_0F);\n\tconst uint8x16_t eq_2F      = vceqq_u8(*lane, mask_2F);\n\n\tconst uint8x16_t hi = vqtbl1q_u8(lut_hi, hi_nibbles);\n\tconst uint8x16_t lo = vqtbl1q_u8(lut_lo, lo_nibbles);\n\n\t// Now simply add the delta values to the input:\n\t*lane = vaddq_u8(*lane, delta_lookup(vaddq_u8(eq_2F, hi_nibbles)));\n\n\t// Return the validity mask:\n\treturn vandq_u8(lo, hi);\n}\n\nstatic inline void\ndec_loop_neon32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 64) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 64 bytes per round. Unlike the SSE codecs, no\n\t// extra trailing zero bytes are written, so it is not necessary to\n\t// reserve extra input bytes:\n\tsize_t rounds = *slen / 64;\n\n\t*slen -= rounds * 64;\t// 64 bytes consumed per round\n\t*olen += rounds * 48;\t// 48 bytes produced per round\n\n\tdo {\n\t\tuint8x16x3_t dec;\n\n\t\t// Load 64 bytes and deinterleave:\n\t\tuint8x16x4_t str = vld4q_u8(*s);\n\n\t\t// Decode each lane, collect a mask of invalid inputs:\n\t\tconst uint8x16_t classified\n\t\t\t= dec_loop_neon32_lane(&str.val[0])\n\t\t\t| dec_loop_neon32_lane(&str.val[1])\n\t\t\t| dec_loop_neon32_lane(&str.val[2])\n\t\t\t| dec_loop_neon32_lane(&str.val[3]);\n\n\t\t// Check for invalid input: if any of the delta values are\n\t\t// zero, fall back on bytewise code to do error checking and\n\t\t// reporting:\n\t\tif (is_nonzero(classified)) {\n\t\t\tbreak;\n\t\t}\n\n\t\t// Compress four bytes into three:\n\t\tdec.val[0] = vorrq_u8(vshlq_n_u8(str.val[0], 2), vshrq_n_u8(str.val[1], 4));\n\t\tdec.val[1] = vorrq_u8(vshlq_n_u8(str.val[1], 4), vshrq_n_u8(str.val[2], 2));\n\t\tdec.val[2] = vorrq_u8(vshlq_n_u8(str.val[2], 6), str.val[3]);\n\n\t\t// Interleave and store decoded result:\n\t\tvst3q_u8(*o, dec);\n\n\t\t*s += 64;\n\t\t*o += 48;\n\n\t} while (--rounds > 0);\n\n\t// Adjust for any rounds that were skipped:\n\t*slen += rounds * 64;\n\t*olen -= rounds * 48;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon32/enc_loop.c",
    "content": "#ifdef BASE64_NEON32_USE_ASM\nstatic inline void\nenc_loop_neon32_inner_asm (const uint8_t **s, uint8_t **o)\n{\n\t// This function duplicates the functionality of enc_loop_neon32_inner,\n\t// but entirely with inline assembly. This gives a significant speedup\n\t// over using NEON intrinsics, which do not always generate very good\n\t// code. The logic of the assembly is directly lifted from the\n\t// intrinsics version, so it can be used as a guide to this code.\n\n\t// Temporary registers, used as scratch space.\n\tuint8x16_t tmp0, tmp1, tmp2, tmp3;\n\tuint8x16_t mask0, mask1, mask2, mask3;\n\n\t// A lookup table containing the absolute offsets for all ranges.\n\tconst uint8x16_t lut = {\n\t\t  65U,  71U, 252U, 252U,\n\t\t 252U, 252U, 252U, 252U,\n\t\t 252U, 252U, 252U, 252U,\n\t\t 237U, 240U,   0U,   0U\n\t};\n\n\t// Numeric constants.\n\tconst uint8x16_t n51 = vdupq_n_u8(51);\n\tconst uint8x16_t n25 = vdupq_n_u8(25);\n\tconst uint8x16_t n63 = vdupq_n_u8(63);\n\n\t__asm__ (\n\n\t\t// Load 48 bytes and deinterleave. The bytes are loaded to\n\t\t// hard-coded registers q12, q13 and q14, to ensure that they\n\t\t// are contiguous. Increment the source pointer.\n\t\t\"vld3.8 {d24, d26, d28}, [%[src]]! \\n\\t\"\n\t\t\"vld3.8 {d25, d27, d29}, [%[src]]! \\n\\t\"\n\n\t\t// Reshuffle the bytes using temporaries.\n\t\t\"vshr.u8 %q[t0], q12,    #2      \\n\\t\"\n\t\t\"vshr.u8 %q[t1], q13,    #4      \\n\\t\"\n\t\t\"vshr.u8 %q[t2], q14,    #6      \\n\\t\"\n\t\t\"vsli.8  %q[t1], q12,    #4      \\n\\t\"\n\t\t\"vsli.8  %q[t2], q13,    #2      \\n\\t\"\n\t\t\"vand.u8 %q[t1], %q[t1], %q[n63] \\n\\t\"\n\t\t\"vand.u8 %q[t2], %q[t2], %q[n63] \\n\\t\"\n\t\t\"vand.u8 %q[t3], q14,    %q[n63] \\n\\t\"\n\n\t\t// t0..t3 are the reshuffled inputs. Create LUT indices.\n\t\t\"vqsub.u8 q12, %q[t0], %q[n51] \\n\\t\"\n\t\t\"vqsub.u8 q13, %q[t1], %q[n51] \\n\\t\"\n\t\t\"vqsub.u8 q14, %q[t2], %q[n51] \\n\\t\"\n\t\t\"vqsub.u8 q15, %q[t3], %q[n51] \\n\\t\"\n\n\t\t// Create the mask for range #0.\n\t\t\"vcgt.u8 %q[m0], %q[t0], %q[n25] \\n\\t\"\n\t\t\"vcgt.u8 %q[m1], %q[t1], %q[n25] \\n\\t\"\n\t\t\"vcgt.u8 %q[m2], %q[t2], %q[n25] \\n\\t\"\n\t\t\"vcgt.u8 %q[m3], %q[t3], %q[n25] \\n\\t\"\n\n\t\t// Subtract -1 to correct the LUT indices.\n\t\t\"vsub.u8 q12, %q[m0] \\n\\t\"\n\t\t\"vsub.u8 q13, %q[m1] \\n\\t\"\n\t\t\"vsub.u8 q14, %q[m2] \\n\\t\"\n\t\t\"vsub.u8 q15, %q[m3] \\n\\t\"\n\n\t\t// Lookup the delta values.\n\t\t\"vtbl.u8 d24, {%q[lut]}, d24 \\n\\t\"\n\t\t\"vtbl.u8 d25, {%q[lut]}, d25 \\n\\t\"\n\t\t\"vtbl.u8 d26, {%q[lut]}, d26 \\n\\t\"\n\t\t\"vtbl.u8 d27, {%q[lut]}, d27 \\n\\t\"\n\t\t\"vtbl.u8 d28, {%q[lut]}, d28 \\n\\t\"\n\t\t\"vtbl.u8 d29, {%q[lut]}, d29 \\n\\t\"\n\t\t\"vtbl.u8 d30, {%q[lut]}, d30 \\n\\t\"\n\t\t\"vtbl.u8 d31, {%q[lut]}, d31 \\n\\t\"\n\n\t\t// Add the delta values.\n\t\t\"vadd.u8 q12, %q[t0] \\n\\t\"\n\t\t\"vadd.u8 q13, %q[t1] \\n\\t\"\n\t\t\"vadd.u8 q14, %q[t2] \\n\\t\"\n\t\t\"vadd.u8 q15, %q[t3] \\n\\t\"\n\n\t\t// Store 64 bytes and interleave. Increment the dest pointer.\n\t\t\"vst4.8 {d24, d26, d28, d30}, [%[dst]]! \\n\\t\"\n\t\t\"vst4.8 {d25, d27, d29, d31}, [%[dst]]! \\n\\t\"\n\n\t\t// Outputs (modified).\n\t\t: [src] \"+r\"  (*s),\n\t\t  [dst] \"+r\"  (*o),\n\t\t  [t0]  \"=&w\" (tmp0),\n\t\t  [t1]  \"=&w\" (tmp1),\n\t\t  [t2]  \"=&w\" (tmp2),\n\t\t  [t3]  \"=&w\" (tmp3),\n\t\t  [m0]  \"=&w\" (mask0),\n\t\t  [m1]  \"=&w\" (mask1),\n\t\t  [m2]  \"=&w\" (mask2),\n\t\t  [m3]  \"=&w\" (mask3)\n\n\t\t// Inputs (not modified).\n\t\t: [lut] \"w\" (lut),\n\t\t  [n25] \"w\" (n25),\n\t\t  [n51] \"w\" (n51),\n\t\t  [n63] \"w\" (n63)\n\n\t\t// Clobbers.\n\t\t: \"d24\", \"d25\", \"d26\", \"d27\", \"d28\", \"d29\", \"d30\", \"d31\",\n\t\t  \"cc\", \"memory\"\n\t);\n}\n#endif\n\nstatic inline void\nenc_loop_neon32_inner (const uint8_t **s, uint8_t **o)\n{\n#ifdef BASE64_NEON32_USE_ASM\n\tenc_loop_neon32_inner_asm(s, o);\n#else\n\t// Load 48 bytes and deinterleave:\n\tuint8x16x3_t src = vld3q_u8(*s);\n\n\t// Reshuffle:\n\tuint8x16x4_t out = enc_reshuffle(src);\n\n\t// Translate reshuffled bytes to the Base64 alphabet:\n\tout = enc_translate(out);\n\n\t// Interleave and store output:\n\tvst4q_u8(*o, out);\n\n\t*s += 48;\n\t*o += 64;\n#endif\n}\n\nstatic inline void\nenc_loop_neon32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tsize_t rounds = *slen / 48;\n\n\t*slen -= rounds * 48;\t// 48 bytes consumed per round\n\t*olen += rounds * 64;\t// 64 bytes produced per round\n\n\twhile (rounds > 0) {\n\t\tif (rounds >= 8) {\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\trounds -= 8;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\trounds -= 4;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\tenc_loop_neon32_inner(s, o);\n\t\t\trounds -= 2;\n\t\t\tcontinue;\n\t\t}\n\t\tenc_loop_neon32_inner(s, o);\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon32/enc_reshuffle.c",
    "content": "static inline uint8x16x4_t\nenc_reshuffle (uint8x16x3_t in)\n{\n\tuint8x16x4_t out;\n\n\t// Input:\n\t// in[0]  = a7 a6 a5 a4 a3 a2 a1 a0\n\t// in[1]  = b7 b6 b5 b4 b3 b2 b1 b0\n\t// in[2]  = c7 c6 c5 c4 c3 c2 c1 c0\n\n\t// Output:\n\t// out[0] = 00 00 a7 a6 a5 a4 a3 a2\n\t// out[1] = 00 00 a1 a0 b7 b6 b5 b4\n\t// out[2] = 00 00 b3 b2 b1 b0 c7 c6\n\t// out[3] = 00 00 c5 c4 c3 c2 c1 c0\n\n\t// Move the input bits to where they need to be in the outputs. Except\n\t// for the first output, the high two bits are not cleared.\n\tout.val[0] = vshrq_n_u8(in.val[0], 2);\n\tout.val[1] = vshrq_n_u8(in.val[1], 4);\n\tout.val[2] = vshrq_n_u8(in.val[2], 6);\n\tout.val[1] = vsliq_n_u8(out.val[1], in.val[0], 4);\n\tout.val[2] = vsliq_n_u8(out.val[2], in.val[1], 2);\n\n\t// Clear the high two bits in the second, third and fourth output.\n\tout.val[1] = vandq_u8(out.val[1], vdupq_n_u8(0x3F));\n\tout.val[2] = vandq_u8(out.val[2], vdupq_n_u8(0x3F));\n\tout.val[3] = vandq_u8(in.val[2],  vdupq_n_u8(0x3F));\n\n\treturn out;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon32/enc_translate.c",
    "content": "static inline uint8x16x4_t\nenc_translate (const uint8x16x4_t in)\n{\n\t// A lookup table containing the absolute offsets for all ranges:\n\tconst uint8x16_t lut = {\n\t\t 65U,  71U, 252U, 252U,\n\t\t252U, 252U, 252U, 252U,\n\t\t252U, 252U, 252U, 252U,\n\t\t237U, 240U,   0U,   0U\n\t};\n\n\tconst uint8x16_t offset = vdupq_n_u8(51);\n\n\tuint8x16x4_t indices, mask, delta, out;\n\n\t// Translate values 0..63 to the Base64 alphabet. There are five sets:\n\t// #  From      To         Abs    Index  Characters\n\t// 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ\n\t// 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz\n\t// 2  [52..61]  [48..57]    -4  [2..11]  0123456789\n\t// 3  [62]      [43]       -19       12  +\n\t// 4  [63]      [47]       -16       13  /\n\n\t// Create LUT indices from input:\n\t// the index for range #0 is right, others are 1 less than expected:\n\tindices.val[0] = vqsubq_u8(in.val[0], offset);\n\tindices.val[1] = vqsubq_u8(in.val[1], offset);\n\tindices.val[2] = vqsubq_u8(in.val[2], offset);\n\tindices.val[3] = vqsubq_u8(in.val[3], offset);\n\n\t// mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:\n\tmask.val[0] = vcgtq_u8(in.val[0], vdupq_n_u8(25));\n\tmask.val[1] = vcgtq_u8(in.val[1], vdupq_n_u8(25));\n\tmask.val[2] = vcgtq_u8(in.val[2], vdupq_n_u8(25));\n\tmask.val[3] = vcgtq_u8(in.val[3], vdupq_n_u8(25));\n\n\t// Subtract -1, so add 1 to indices for range #[1..4], All indices are\n\t// now correct:\n\tindices.val[0] = vsubq_u8(indices.val[0], mask.val[0]);\n\tindices.val[1] = vsubq_u8(indices.val[1], mask.val[1]);\n\tindices.val[2] = vsubq_u8(indices.val[2], mask.val[2]);\n\tindices.val[3] = vsubq_u8(indices.val[3], mask.val[3]);\n\n\t// Lookup delta values:\n\tdelta.val[0] = vqtbl1q_u8(lut, indices.val[0]);\n\tdelta.val[1] = vqtbl1q_u8(lut, indices.val[1]);\n\tdelta.val[2] = vqtbl1q_u8(lut, indices.val[2]);\n\tdelta.val[3] = vqtbl1q_u8(lut, indices.val[3]);\n\n\t// Add delta values:\n\tout.val[0] = vaddq_u8(in.val[0], delta.val[0]);\n\tout.val[1] = vaddq_u8(in.val[1], delta.val[1]);\n\tout.val[2] = vaddq_u8(in.val[2], delta.val[2]);\n\tout.val[3] = vaddq_u8(in.val[3], delta.val[3]);\n\n\treturn out;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon64/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <string.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#ifdef __aarch64__\n#  if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && HAVE_NEON64\n#    define BASE64_USE_NEON64\n#  endif\n#endif\n\n#ifdef BASE64_USE_NEON64\n#include <arm_neon.h>\n\n// Only enable inline assembly on supported compilers.\n#if defined(__GNUC__) || defined(__clang__)\n#define BASE64_NEON64_USE_ASM\n#endif\n\nstatic inline uint8x16x4_t\nload_64byte_table (const uint8_t *p)\n{\n#ifdef BASE64_NEON64_USE_ASM\n\n\t// Force the table to be loaded into contiguous registers. GCC will not\n\t// normally allocate contiguous registers for a `uint8x16x4_t'. These\n\t// registers are chosen to not conflict with the ones in the enc loop.\n\tregister uint8x16_t t0 __asm__ (\"v8\");\n\tregister uint8x16_t t1 __asm__ (\"v9\");\n\tregister uint8x16_t t2 __asm__ (\"v10\");\n\tregister uint8x16_t t3 __asm__ (\"v11\");\n\n\t__asm__ (\n\t\t\"ld1 {%[t0].16b, %[t1].16b, %[t2].16b, %[t3].16b}, [%[src]], #64 \\n\\t\"\n\t\t: [src] \"+r\" (p),\n\t\t  [t0]  \"=w\" (t0),\n\t\t  [t1]  \"=w\" (t1),\n\t\t  [t2]  \"=w\" (t2),\n\t\t  [t3]  \"=w\" (t3)\n\t);\n\n\treturn (uint8x16x4_t) {\n\t\t.val[0] = t0,\n\t\t.val[1] = t1,\n\t\t.val[2] = t2,\n\t\t.val[3] = t3,\n\t};\n#else\n\treturn vld1q_u8_x4(p);\n#endif\n}\n\n#include \"../generic/32/dec_loop.c\"\n#include \"../generic/64/enc_loop.c\"\n#include \"dec_loop.c\"\n\n#ifdef BASE64_NEON64_USE_ASM\n# include \"enc_loop_asm.c\"\n#else\n# include \"enc_reshuffle.c\"\n# include \"enc_loop.c\"\n#endif\n\n#endif\t// BASE64_USE_NEON64\n\n// Stride size is so large on these NEON 64-bit functions\n// (48 bytes encode, 64 bytes decode) that we inline the\n// uint64 codec to stay performant on smaller inputs.\n\nBASE64_ENC_FUNCTION(neon64)\n{\n#ifdef BASE64_USE_NEON64\n\t#include \"../generic/enc_head.c\"\n\tenc_loop_neon64(&s, &slen, &o, &olen);\n\tenc_loop_generic_64(&s, &slen, &o, &olen);\n\t#include \"../generic/enc_tail.c\"\n#else\n\tBASE64_ENC_STUB\n#endif\n}\n\nBASE64_DEC_FUNCTION(neon64)\n{\n#ifdef BASE64_USE_NEON64\n\t#include \"../generic/dec_head.c\"\n\tdec_loop_neon64(&s, &slen, &o, &olen);\n\tdec_loop_generic_32(&s, &slen, &o, &olen);\n\t#include \"../generic/dec_tail.c\"\n#else\n\tBASE64_DEC_STUB\n#endif\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon64/dec_loop.c",
    "content": "// The input consists of five valid character sets in the Base64 alphabet,\n// which we need to map back to the 6-bit values they represent.\n// There are three ranges, two singles, and then there's the rest.\n//\n//   #  From       To        LUT  Characters\n//   1  [0..42]    [255]      #1  invalid input\n//   2  [43]       [62]       #1  +\n//   3  [44..46]   [255]      #1  invalid input\n//   4  [47]       [63]       #1  /\n//   5  [48..57]   [52..61]   #1  0..9\n//   6  [58..63]   [255]      #1  invalid input\n//   7  [64]       [255]      #2  invalid input\n//   8  [65..90]   [0..25]    #2  A..Z\n//   9  [91..96]   [255]      #2  invalid input\n//  10  [97..122]  [26..51]   #2  a..z\n//  11  [123..126] [255]      #2  invalid input\n// (12) Everything else => invalid input\n\n// The first LUT will use the VTBL instruction (out of range indices are set to\n// 0 in destination).\nstatic const uint8_t dec_lut1[] = {\n\t255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U,\n\t255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U,\n\t255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U,  62U, 255U, 255U, 255U,  63U,\n\t 52U,  53U,  54U,  55U,  56U,  57U,  58U,  59U,  60U,  61U, 255U, 255U, 255U, 255U, 255U, 255U,\n};\n\n// The second LUT will use the VTBX instruction (out of range indices will be\n// unchanged in destination). Input [64..126] will be mapped to index [1..63]\n// in this LUT. Index 0 means that value comes from LUT #1.\nstatic const uint8_t dec_lut2[] = {\n\t  0U, 255U,   0U,   1U,   2U,   3U,   4U,   5U,   6U,   7U,   8U,   9U,  10U,  11U,  12U,  13U,\n\t 14U,  15U,  16U,  17U,  18U,  19U,  20U,  21U,  22U,  23U,  24U,  25U, 255U, 255U, 255U, 255U,\n\t255U, 255U,  26U,  27U,  28U,  29U,  30U,  31U,  32U,  33U,  34U,  35U,  36U,  37U,  38U,  39U,\n\t 40U,  41U,  42U,  43U,  44U,  45U,  46U,  47U,  48U,  49U,  50U,  51U, 255U, 255U, 255U, 255U,\n};\n\n// All input values in range for the first look-up will be 0U in the second\n// look-up result. All input values out of range for the first look-up will be\n// 0U in the first look-up result. Thus, the two results can be ORed without\n// conflicts.\n//\n// Invalid characters that are in the valid range for either look-up will be\n// set to 255U in the combined result. Other invalid characters will just be\n// passed through with the second look-up result (using the VTBX instruction).\n// Since the second LUT is 64 bytes, those passed-through values are guaranteed\n// to have a value greater than 63U. Therefore, valid characters will be mapped\n// to the valid [0..63] range and all invalid characters will be mapped to\n// values greater than 63.\n\nstatic inline void\ndec_loop_neon64 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 64) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 64 bytes per round. Unlike the SSE codecs, no\n\t// extra trailing zero bytes are written, so it is not necessary to\n\t// reserve extra input bytes:\n\tsize_t rounds = *slen / 64;\n\n\t*slen -= rounds * 64;\t// 64 bytes consumed per round\n\t*olen += rounds * 48;\t// 48 bytes produced per round\n\n\tconst uint8x16x4_t tbl_dec1 = load_64byte_table(dec_lut1);\n\tconst uint8x16x4_t tbl_dec2 = load_64byte_table(dec_lut2);\n\n\tdo {\n\t\tconst uint8x16_t offset = vdupq_n_u8(63U);\n\t\tuint8x16x4_t dec1, dec2;\n\t\tuint8x16x3_t dec;\n\n\t\t// Load 64 bytes and deinterleave:\n\t\tuint8x16x4_t str = vld4q_u8((uint8_t *) *s);\n\n\t\t// Get indices for second LUT:\n\t\tdec2.val[0] = vqsubq_u8(str.val[0], offset);\n\t\tdec2.val[1] = vqsubq_u8(str.val[1], offset);\n\t\tdec2.val[2] = vqsubq_u8(str.val[2], offset);\n\t\tdec2.val[3] = vqsubq_u8(str.val[3], offset);\n\n\t\t// Get values from first LUT:\n\t\tdec1.val[0] = vqtbl4q_u8(tbl_dec1, str.val[0]);\n\t\tdec1.val[1] = vqtbl4q_u8(tbl_dec1, str.val[1]);\n\t\tdec1.val[2] = vqtbl4q_u8(tbl_dec1, str.val[2]);\n\t\tdec1.val[3] = vqtbl4q_u8(tbl_dec1, str.val[3]);\n\n\t\t// Get values from second LUT:\n\t\tdec2.val[0] = vqtbx4q_u8(dec2.val[0], tbl_dec2, dec2.val[0]);\n\t\tdec2.val[1] = vqtbx4q_u8(dec2.val[1], tbl_dec2, dec2.val[1]);\n\t\tdec2.val[2] = vqtbx4q_u8(dec2.val[2], tbl_dec2, dec2.val[2]);\n\t\tdec2.val[3] = vqtbx4q_u8(dec2.val[3], tbl_dec2, dec2.val[3]);\n\n\t\t// Get final values:\n\t\tstr.val[0] = vorrq_u8(dec1.val[0], dec2.val[0]);\n\t\tstr.val[1] = vorrq_u8(dec1.val[1], dec2.val[1]);\n\t\tstr.val[2] = vorrq_u8(dec1.val[2], dec2.val[2]);\n\t\tstr.val[3] = vorrq_u8(dec1.val[3], dec2.val[3]);\n\n\t\t// Check for invalid input, any value larger than 63:\n\t\tconst uint8x16_t classified\n\t\t\t= vcgtq_u8(str.val[0], vdupq_n_u8(63))\n\t\t\t| vcgtq_u8(str.val[1], vdupq_n_u8(63))\n\t\t\t| vcgtq_u8(str.val[2], vdupq_n_u8(63))\n\t\t\t| vcgtq_u8(str.val[3], vdupq_n_u8(63));\n\n\t\t// Check that all bits are zero:\n\t\tif (vmaxvq_u8(classified) != 0U) {\n\t\t\tbreak;\n\t\t}\n\n\t\t// Compress four bytes into three:\n\t\tdec.val[0] = vshlq_n_u8(str.val[0], 2) | vshrq_n_u8(str.val[1], 4);\n\t\tdec.val[1] = vshlq_n_u8(str.val[1], 4) | vshrq_n_u8(str.val[2], 2);\n\t\tdec.val[2] = vshlq_n_u8(str.val[2], 6) | str.val[3];\n\n\t\t// Interleave and store decoded result:\n\t\tvst3q_u8((uint8_t *) *o, dec);\n\n\t\t*s += 64;\n\t\t*o += 48;\n\n\t} while (--rounds > 0);\n\n\t// Adjust for any rounds that were skipped:\n\t*slen += rounds * 64;\n\t*olen -= rounds * 48;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon64/enc_loop.c",
    "content": "static inline void\nenc_loop_neon64_inner (const uint8_t **s, uint8_t **o, const uint8x16x4_t tbl_enc)\n{\n\t// Load 48 bytes and deinterleave:\n\tuint8x16x3_t src = vld3q_u8(*s);\n\n\t// Divide bits of three input bytes over four output bytes:\n\tuint8x16x4_t out = enc_reshuffle(src);\n\n\t// The bits have now been shifted to the right locations;\n\t// translate their values 0..63 to the Base64 alphabet.\n\t// Use a 64-byte table lookup:\n\tout.val[0] = vqtbl4q_u8(tbl_enc, out.val[0]);\n\tout.val[1] = vqtbl4q_u8(tbl_enc, out.val[1]);\n\tout.val[2] = vqtbl4q_u8(tbl_enc, out.val[2]);\n\tout.val[3] = vqtbl4q_u8(tbl_enc, out.val[3]);\n\n\t// Interleave and store output:\n\tvst4q_u8(*o, out);\n\n\t*s += 48;\n\t*o += 64;\n}\n\nstatic inline void\nenc_loop_neon64 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tsize_t rounds = *slen / 48;\n\n\t*slen -= rounds * 48;\t// 48 bytes consumed per round\n\t*olen += rounds * 64;\t// 64 bytes produced per round\n\n\t// Load the encoding table:\n\tconst uint8x16x4_t tbl_enc = load_64byte_table(base64_table_enc_6bit);\n\n\twhile (rounds > 0) {\n\t\tif (rounds >= 8) {\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\trounds -= 8;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\trounds -= 4;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\t\trounds -= 2;\n\t\t\tcontinue;\n\t\t}\n\t\tenc_loop_neon64_inner(s, o, tbl_enc);\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon64/enc_loop_asm.c",
    "content": "// Apologies in advance for combining the preprocessor with inline assembly,\n// two notoriously gnarly parts of C, but it was necessary to avoid a lot of\n// code repetition. The preprocessor is used to template large sections of\n// inline assembly that differ only in the registers used. If the code was\n// written out by hand, it would become very large and hard to audit.\n\n// Generate a block of inline assembly that loads three user-defined registers\n// A, B, C from memory and deinterleaves them, post-incrementing the src\n// pointer. The register set should be sequential.\n#define LOAD(A, B, C) \\\n\t\"ld3 {\"A\".16b, \"B\".16b, \"C\".16b}, [%[src]], #48 \\n\\t\"\n\n// Generate a block of inline assembly that takes three deinterleaved registers\n// and shuffles the bytes. The output is in temporary registers t0..t3.\n#define SHUF(A, B, C) \\\n\t\"ushr %[t0].16b, \"A\".16b,   #2         \\n\\t\" \\\n\t\"ushr %[t1].16b, \"B\".16b,   #4         \\n\\t\" \\\n\t\"ushr %[t2].16b, \"C\".16b,   #6         \\n\\t\" \\\n\t\"sli  %[t1].16b, \"A\".16b,   #4         \\n\\t\" \\\n\t\"sli  %[t2].16b, \"B\".16b,   #2         \\n\\t\" \\\n\t\"and  %[t1].16b, %[t1].16b, %[n63].16b \\n\\t\" \\\n\t\"and  %[t2].16b, %[t2].16b, %[n63].16b \\n\\t\" \\\n\t\"and  %[t3].16b, \"C\".16b,   %[n63].16b \\n\\t\"\n\n// Generate a block of inline assembly that takes temporary registers t0..t3\n// and translates them to the base64 alphabet, using a table loaded into\n// v8..v11. The output is in user-defined registers A..D.\n#define TRAN(A, B, C, D) \\\n\t\"tbl \"A\".16b, {v8.16b-v11.16b}, %[t0].16b \\n\\t\" \\\n\t\"tbl \"B\".16b, {v8.16b-v11.16b}, %[t1].16b \\n\\t\" \\\n\t\"tbl \"C\".16b, {v8.16b-v11.16b}, %[t2].16b \\n\\t\" \\\n\t\"tbl \"D\".16b, {v8.16b-v11.16b}, %[t3].16b \\n\\t\"\n\n// Generate a block of inline assembly that interleaves four registers and\n// stores them, post-incrementing the destination pointer.\n#define STOR(A, B, C, D) \\\n\t\"st4 {\"A\".16b, \"B\".16b, \"C\".16b, \"D\".16b}, [%[dst]], #64 \\n\\t\"\n\n// Generate a block of inline assembly that generates a single self-contained\n// encoder round: fetch the data, process it, and store the result.\n#define ROUND() \\\n\tLOAD(\"v12\", \"v13\", \"v14\") \\\n\tSHUF(\"v12\", \"v13\", \"v14\") \\\n\tTRAN(\"v12\", \"v13\", \"v14\", \"v15\") \\\n\tSTOR(\"v12\", \"v13\", \"v14\", \"v15\")\n\n// Generate a block of assembly that generates a type A interleaved encoder\n// round. It uses registers that were loaded by the previous type B round, and\n// in turn loads registers for the next type B round.\n#define ROUND_A() \\\n\tSHUF(\"v2\",  \"v3\",  \"v4\") \\\n\tLOAD(\"v12\", \"v13\", \"v14\") \\\n\tTRAN(\"v2\",  \"v3\",  \"v4\", \"v5\") \\\n\tSTOR(\"v2\",  \"v3\",  \"v4\", \"v5\")\n\n// Type B interleaved encoder round. Same as type A, but register sets swapped.\n#define ROUND_B() \\\n\tSHUF(\"v12\", \"v13\", \"v14\") \\\n\tLOAD(\"v2\",  \"v3\",  \"v4\") \\\n\tTRAN(\"v12\", \"v13\", \"v14\", \"v15\") \\\n\tSTOR(\"v12\", \"v13\", \"v14\", \"v15\")\n\n// The first type A round needs to load its own registers.\n#define ROUND_A_FIRST() \\\n\tLOAD(\"v2\", \"v3\", \"v4\") \\\n\tROUND_A()\n\n// The last type B round omits the load for the next step.\n#define ROUND_B_LAST() \\\n\tSHUF(\"v12\", \"v13\", \"v14\") \\\n\tTRAN(\"v12\", \"v13\", \"v14\", \"v15\") \\\n\tSTOR(\"v12\", \"v13\", \"v14\", \"v15\")\n\n// Suppress clang's warning that the literal string in the asm statement is\n// overlong (longer than the ISO-mandated minimum size of 4095 bytes for C99\n// compilers). It may be true, but the goal here is not C99 portability.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Woverlength-strings\"\n\nstatic inline void\nenc_loop_neon64 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tsize_t rounds = *slen / 48;\n\n\tif (rounds == 0) {\n\t\treturn;\n\t}\n\n\t*slen -= rounds * 48;\t// 48 bytes consumed per round.\n\t*olen += rounds * 64;\t// 64 bytes produced per round.\n\n\t// Number of times to go through the 8x loop.\n\tsize_t loops = rounds / 8;\n\n\t// Number of rounds remaining after the 8x loop.\n\trounds %= 8;\n\n\t// Temporary registers, used as scratch space.\n\tuint8x16_t tmp0, tmp1, tmp2, tmp3;\n\n\t__asm__ volatile (\n\n\t\t// Load the encoding table into v8..v11.\n\t\t\"    ld1 {v8.16b-v11.16b}, [%[tbl]] \\n\\t\"\n\n\t\t// If there are eight rounds or more, enter an 8x unrolled loop\n\t\t// of interleaved encoding rounds. The rounds interleave memory\n\t\t// operations (load/store) with data operations to maximize\n\t\t// pipeline throughput.\n\t\t\"    cbz %[loops], 4f \\n\\t\"\n\n\t\t// The SIMD instructions do not touch the flags.\n\t\t\"88: subs %[loops], %[loops], #1 \\n\\t\"\n\t\t\"    \" ROUND_A_FIRST()\n\t\t\"    \" ROUND_B()\n\t\t\"    \" ROUND_A()\n\t\t\"    \" ROUND_B()\n\t\t\"    \" ROUND_A()\n\t\t\"    \" ROUND_B()\n\t\t\"    \" ROUND_A()\n\t\t\"    \" ROUND_B_LAST()\n\t\t\"    b.ne 88b \\n\\t\"\n\n\t\t// Enter a 4x unrolled loop for rounds of 4 or more.\n\t\t\"4:  cmp  %[rounds], #4 \\n\\t\"\n\t\t\"    b.lt 30f           \\n\\t\"\n\t\t\"    \" ROUND_A_FIRST()\n\t\t\"    \" ROUND_B()\n\t\t\"    \" ROUND_A()\n\t\t\"    \" ROUND_B_LAST()\n\t\t\"    sub %[rounds], %[rounds], #4 \\n\\t\"\n\n\t\t// Dispatch the remaining rounds 0..3.\n\t\t\"30: cbz  %[rounds], 0f \\n\\t\"\n\t\t\"    cmp  %[rounds], #2 \\n\\t\"\n\t\t\"    b.eq 2f            \\n\\t\"\n\t\t\"    b.lt 1f            \\n\\t\"\n\n\t\t// Block of non-interlaced encoding rounds, which can each\n\t\t// individually be jumped to. Rounds fall through to the next.\n\t\t\"3:  \" ROUND()\n\t\t\"2:  \" ROUND()\n\t\t\"1:  \" ROUND()\n\t\t\"0:  \\n\\t\"\n\n\t\t// Outputs (modified).\n\t\t: [loops] \"+r\"  (loops),\n\t\t  [src]   \"+r\"  (*s),\n\t\t  [dst]   \"+r\"  (*o),\n\t\t  [t0]    \"=&w\" (tmp0),\n\t\t  [t1]    \"=&w\" (tmp1),\n\t\t  [t2]    \"=&w\" (tmp2),\n\t\t  [t3]    \"=&w\" (tmp3)\n\n\t\t// Inputs (not modified).\n\t\t: [rounds] \"r\" (rounds),\n\t\t  [tbl]    \"r\" (base64_table_enc_6bit),\n\t\t  [n63]    \"w\" (vdupq_n_u8(63))\n\n\t\t// Clobbers.\n\t\t: \"v2\",  \"v3\",  \"v4\",  \"v5\",\n\t\t  \"v8\",  \"v9\",  \"v10\", \"v11\",\n\t\t  \"v12\", \"v13\", \"v14\", \"v15\",\n\t\t  \"cc\", \"memory\"\n\t);\n}\n\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/neon64/enc_reshuffle.c",
    "content": "static inline uint8x16x4_t\nenc_reshuffle (const uint8x16x3_t in)\n{\n\tuint8x16x4_t out;\n\n\t// Input:\n\t// in[0]  = a7 a6 a5 a4 a3 a2 a1 a0\n\t// in[1]  = b7 b6 b5 b4 b3 b2 b1 b0\n\t// in[2]  = c7 c6 c5 c4 c3 c2 c1 c0\n\n\t// Output:\n\t// out[0] = 00 00 a7 a6 a5 a4 a3 a2\n\t// out[1] = 00 00 a1 a0 b7 b6 b5 b4\n\t// out[2] = 00 00 b3 b2 b1 b0 c7 c6\n\t// out[3] = 00 00 c5 c4 c3 c2 c1 c0\n\n\t// Move the input bits to where they need to be in the outputs. Except\n\t// for the first output, the high two bits are not cleared.\n\tout.val[0] = vshrq_n_u8(in.val[0], 2);\n\tout.val[1] = vshrq_n_u8(in.val[1], 4);\n\tout.val[2] = vshrq_n_u8(in.val[2], 6);\n\tout.val[1] = vsliq_n_u8(out.val[1], in.val[0], 4);\n\tout.val[2] = vsliq_n_u8(out.val[2], in.val[1], 2);\n\n\t// Clear the high two bits in the second, third and fourth output.\n\tout.val[1] = vandq_u8(out.val[1], vdupq_n_u8(0x3F));\n\tout.val[2] = vandq_u8(out.val[2], vdupq_n_u8(0x3F));\n\tout.val[3] = vandq_u8(in.val[2],  vdupq_n_u8(0x3F));\n\n\treturn out;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/sse41/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#if HAVE_SSE41\n#include <smmintrin.h>\n\n// Only enable inline assembly on supported compilers and on 64-bit CPUs.\n#ifndef BASE64_SSE41_USE_ASM\n# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64\n#  define BASE64_SSE41_USE_ASM 1\n# else\n#  define BASE64_SSE41_USE_ASM 0\n# endif\n#endif\n\n#include \"../ssse3/dec_reshuffle.c\"\n#include \"../ssse3/dec_loop.c\"\n\n#if BASE64_SSE41_USE_ASM\n# include \"../ssse3/enc_loop_asm.c\"\n#else\n# include \"../ssse3/enc_translate.c\"\n# include \"../ssse3/enc_reshuffle.c\"\n# include \"../ssse3/enc_loop.c\"\n#endif\n\n#endif\t// HAVE_SSE41\n\nBASE64_ENC_FUNCTION(sse41)\n{\n#if HAVE_SSE41\n\t#include \"../generic/enc_head.c\"\n\tenc_loop_ssse3(&s, &slen, &o, &olen);\n\t#include \"../generic/enc_tail.c\"\n#else\n\tBASE64_ENC_STUB\n#endif\n}\n\nBASE64_DEC_FUNCTION(sse41)\n{\n#if HAVE_SSE41\n\t#include \"../generic/dec_head.c\"\n\tdec_loop_ssse3(&s, &slen, &o, &olen);\n\t#include \"../generic/dec_tail.c\"\n#else\n\tBASE64_DEC_STUB\n#endif\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/sse42/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#if HAVE_SSE42\n#include <nmmintrin.h>\n\n// Only enable inline assembly on supported compilers and on 64-bit CPUs.\n#ifndef BASE64_SSE42_USE_ASM\n# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64\n#  define BASE64_SSE42_USE_ASM 1\n# else\n#  define BASE64_SSE42_USE_ASM 0\n# endif\n#endif\n\n#include \"../ssse3/dec_reshuffle.c\"\n#include \"../ssse3/dec_loop.c\"\n\n#if BASE64_SSE42_USE_ASM\n# include \"../ssse3/enc_loop_asm.c\"\n#else\n# include \"../ssse3/enc_translate.c\"\n# include \"../ssse3/enc_reshuffle.c\"\n# include \"../ssse3/enc_loop.c\"\n#endif\n\n#endif\t// HAVE_SSE42\n\nBASE64_ENC_FUNCTION(sse42)\n{\n#if HAVE_SSE42\n\t#include \"../generic/enc_head.c\"\n\tenc_loop_ssse3(&s, &slen, &o, &olen);\n\t#include \"../generic/enc_tail.c\"\n#else\n\tBASE64_ENC_STUB\n#endif\n}\n\nBASE64_DEC_FUNCTION(sse42)\n{\n#if HAVE_SSE42\n\t#include \"../generic/dec_head.c\"\n\tdec_loop_ssse3(&s, &slen, &o, &olen);\n\t#include \"../generic/dec_tail.c\"\n#else\n\tBASE64_DEC_STUB\n#endif\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/ssse3/codec.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"../../../include/libbase64.h\"\n#include \"../../tables/tables.h\"\n#include \"../../codecs.h\"\n#include \"config.h\"\n#include \"../../env.h\"\n\n#if HAVE_SSSE3\n#include <tmmintrin.h>\n\n// Only enable inline assembly on supported compilers and on 64-bit CPUs.\n// 32-bit CPUs with SSSE3 support, such as low-end Atoms, only have eight XMM\n// registers, which is not enough to run the inline assembly.\n#ifndef BASE64_SSSE3_USE_ASM\n# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64\n#  define BASE64_SSSE3_USE_ASM 1\n# else\n#  define BASE64_SSSE3_USE_ASM 0\n# endif\n#endif\n\n#include \"dec_reshuffle.c\"\n#include \"dec_loop.c\"\n\n#if BASE64_SSSE3_USE_ASM\n# include \"enc_loop_asm.c\"\n#else\n# include \"enc_reshuffle.c\"\n# include \"enc_translate.c\"\n# include \"enc_loop.c\"\n#endif\n\n#endif\t// HAVE_SSSE3\n\nBASE64_ENC_FUNCTION(ssse3)\n{\n#if HAVE_SSSE3\n\t#include \"../generic/enc_head.c\"\n\tenc_loop_ssse3(&s, &slen, &o, &olen);\n\t#include \"../generic/enc_tail.c\"\n#else\n\tBASE64_ENC_STUB\n#endif\n}\n\nBASE64_DEC_FUNCTION(ssse3)\n{\n#if HAVE_SSSE3\n\t#include \"../generic/dec_head.c\"\n\tdec_loop_ssse3(&s, &slen, &o, &olen);\n\t#include \"../generic/dec_tail.c\"\n#else\n\tBASE64_DEC_STUB\n#endif\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/ssse3/dec_loop.c",
    "content": "// The input consists of six character sets in the Base64 alphabet, which we\n// need to map back to the 6-bit values they represent. There are three ranges,\n// two singles, and then there's the rest.\n//\n//  #  From       To        Add  Characters\n//  1  [43]       [62]      +19  +\n//  2  [47]       [63]      +16  /\n//  3  [48..57]   [52..61]   +4  0..9\n//  4  [65..90]   [0..25]   -65  A..Z\n//  5  [97..122]  [26..51]  -71  a..z\n// (6) Everything else => invalid input\n//\n// We will use lookup tables for character validation and offset computation.\n// Remember that 0x2X and 0x0X are the same index for _mm_shuffle_epi8, this\n// allows to mask with 0x2F instead of 0x0F and thus save one constant\n// declaration (register and/or memory access).\n//\n// For offsets:\n// Perfect hash for lut = ((src >> 4) & 0x2F) + ((src == 0x2F) ? 0xFF : 0x00)\n// 0000 = garbage\n// 0001 = /\n// 0010 = +\n// 0011 = 0-9\n// 0100 = A-Z\n// 0101 = A-Z\n// 0110 = a-z\n// 0111 = a-z\n// 1000 >= garbage\n//\n// For validation, here's the table.\n// A character is valid if and only if the AND of the 2 lookups equals 0:\n//\n// hi \\ lo              0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111\n//      LUT             0x15 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x13 0x1A 0x1B 0x1B 0x1B 0x1A\n//\n// 0000 0x10 char        NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL   BS   HT   LF   VT   FF   CR   SO   SI\n//           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n//\n// 0001 0x10 char        DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB  CAN   EM  SUB  ESC   FS   GS   RS   US\n//           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n//\n// 0010 0x01 char               !    \"    #    $    %    &    '    (    )    *    +    ,    -    .    /\n//           andlut     0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x01 0x01 0x01 0x00\n//\n// 0011 0x02 char          0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?\n//           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x02 0x02 0x02 0x02 0x02\n//\n// 0100 0x04 char          @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O\n//           andlut     0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00\n//\n// 0101 0x08 char          P    Q    R    S    T    U    V    W    X    Y    Z    [    \\    ]    ^    _\n//           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08\n//\n// 0110 0x04 char          `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o\n//           andlut     0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00\n// 0111 0x08 char          p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~\n//           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08\n//\n// 1000 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n// 1001 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n// 1010 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n// 1011 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n// 1100 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n// 1101 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n// 1110 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n// 1111 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10\n\nstatic inline int\ndec_loop_ssse3_inner (const uint8_t **s, uint8_t **o, size_t *rounds)\n{\n\tconst __m128i lut_lo = _mm_setr_epi8(\n\t\t0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\n\t\t0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A);\n\n\tconst __m128i lut_hi = _mm_setr_epi8(\n\t\t0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08,\n\t\t0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10);\n\n\tconst __m128i lut_roll = _mm_setr_epi8(\n\t\t0,  16,  19,   4, -65, -65, -71, -71,\n\t\t0,   0,   0,   0,   0,   0,   0,   0);\n\n\tconst __m128i mask_2F = _mm_set1_epi8(0x2F);\n\n\t// Load input:\n\t__m128i str = _mm_loadu_si128((__m128i *) *s);\n\n\t// Table lookups:\n\tconst __m128i hi_nibbles = _mm_and_si128(_mm_srli_epi32(str, 4), mask_2F);\n\tconst __m128i lo_nibbles = _mm_and_si128(str, mask_2F);\n\tconst __m128i hi         = _mm_shuffle_epi8(lut_hi, hi_nibbles);\n\tconst __m128i lo         = _mm_shuffle_epi8(lut_lo, lo_nibbles);\n\n\t// Check for invalid input: if any \"and\" values from lo and hi are not\n\t// zero, fall back on bytewise code to do error checking and reporting:\n\tif (_mm_movemask_epi8(_mm_cmpgt_epi8(_mm_and_si128(lo, hi), _mm_setzero_si128())) != 0) {\n\t\treturn 0;\n\t}\n\n\tconst __m128i eq_2F = _mm_cmpeq_epi8(str, mask_2F);\n\tconst __m128i roll  = _mm_shuffle_epi8(lut_roll, _mm_add_epi8(eq_2F, hi_nibbles));\n\n\t// Now simply add the delta values to the input:\n\tstr = _mm_add_epi8(str, roll);\n\n\t// Reshuffle the input to packed 12-byte output format:\n\tstr = dec_reshuffle(str);\n\n\t// Store the output:\n\t_mm_storeu_si128((__m128i *) *o, str);\n\n\t*s += 16;\n\t*o += 12;\n\t*rounds -= 1;\n\n\treturn 1;\n}\n\nstatic inline void\ndec_loop_ssse3 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 24) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 16 bytes per round. Because 4 extra zero bytes are\n\t// written after the output, ensure that there will be at least 8 bytes\n\t// of input data left to cover the gap. (6 data bytes and up to two\n\t// end-of-string markers.)\n\tsize_t rounds = (*slen - 8) / 16;\n\n\t*slen -= rounds * 16;\t// 16 bytes consumed per round\n\t*olen += rounds * 12;\t// 12 bytes produced per round\n\n\tdo {\n\t\tif (rounds >= 8) {\n\t\t\tif (dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tif (dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tif (dec_loop_ssse3_inner(s, o, &rounds) &&\n\t\t\t    dec_loop_ssse3_inner(s, o, &rounds)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tdec_loop_ssse3_inner(s, o, &rounds);\n\t\tbreak;\n\n\t} while (rounds > 0);\n\n\t// Adjust for any rounds that were skipped:\n\t*slen += rounds * 16;\n\t*olen -= rounds * 12;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/ssse3/dec_reshuffle.c",
    "content": "static inline __m128i\ndec_reshuffle (const __m128i in)\n{\n\t// in, bits, upper case are most significant bits, lower case are least significant bits\n\t// 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ\n\t// 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG\n\t// 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD\n\t// 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA\n\n\tconst __m128i merge_ab_and_bc = _mm_maddubs_epi16(in, _mm_set1_epi32(0x01400140));\n\t// 0000kkkk LLllllll 0000JJJJ JJjjKKKK\n\t// 0000hhhh IIiiiiii 0000GGGG GGggHHHH\n\t// 0000eeee FFffffff 0000DDDD DDddEEEE\n\t// 0000bbbb CCcccccc 0000AAAA AAaaBBBB\n\n\tconst __m128i out = _mm_madd_epi16(merge_ab_and_bc, _mm_set1_epi32(0x00011000));\n\t// 00000000 JJJJJJjj KKKKkkkk LLllllll\n\t// 00000000 GGGGGGgg HHHHhhhh IIiiiiii\n\t// 00000000 DDDDDDdd EEEEeeee FFffffff\n\t// 00000000 AAAAAAaa BBBBbbbb CCcccccc\n\n\t// Pack bytes together:\n\treturn  _mm_shuffle_epi8(out, _mm_setr_epi8(\n\t\t 2,  1,  0,\n\t\t 6,  5,  4,\n\t\t10,  9,  8,\n\t\t14, 13, 12,\n\t\t-1, -1, -1, -1));\n\t// 00000000 00000000 00000000 00000000\n\t// LLllllll KKKKkkkk JJJJJJjj IIiiiiii\n\t// HHHHhhhh GGGGGGgg FFffffff EEEEeeee\n\t// DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/ssse3/enc_loop.c",
    "content": "static inline void\nenc_loop_ssse3_inner (const uint8_t **s, uint8_t **o)\n{\n\t// Load input:\n\t__m128i str = _mm_loadu_si128((__m128i *) *s);\n\n\t// Reshuffle:\n\tstr = enc_reshuffle(str);\n\n\t// Translate reshuffled bytes to the Base64 alphabet:\n\tstr = enc_translate(str);\n\n\t// Store:\n\t_mm_storeu_si128((__m128i *) *o, str);\n\n\t*s += 12;\n\t*o += 16;\n}\n\nstatic inline void\nenc_loop_ssse3 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\tif (*slen < 16) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 12 bytes at a time. Because blocks are loaded 16\n\t// bytes at a time, ensure that there will be at least 4 remaining\n\t// bytes after the last round, so that the final read will not pass\n\t// beyond the bounds of the input buffer:\n\tsize_t rounds = (*slen - 4) / 12;\n\n\t*slen -= rounds * 12;\t// 12 bytes consumed per round\n\t*olen += rounds * 16;\t// 16 bytes produced per round\n\n\tdo {\n\t\tif (rounds >= 8) {\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\trounds -= 8;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 4) {\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\trounds -= 4;\n\t\t\tcontinue;\n\t\t}\n\t\tif (rounds >= 2) {\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\tenc_loop_ssse3_inner(s, o);\n\t\t\trounds -= 2;\n\t\t\tcontinue;\n\t\t}\n\t\tenc_loop_ssse3_inner(s, o);\n\t\tbreak;\n\n\t} while (rounds > 0);\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/ssse3/enc_loop_asm.c",
    "content": "// Apologies in advance for combining the preprocessor with inline assembly,\n// two notoriously gnarly parts of C, but it was necessary to avoid a lot of\n// code repetition. The preprocessor is used to template large sections of\n// inline assembly that differ only in the registers used. If the code was\n// written out by hand, it would become very large and hard to audit.\n\n// Generate a block of inline assembly that loads register R0 from memory. The\n// offset at which the register is loaded is set by the given round.\n#define LOAD(R0, ROUND) \\\n\t\"lddqu (\"#ROUND\" * 12)(%[src]), %[\"R0\"] \\n\\t\"\n\n// Generate a block of inline assembly that deinterleaves and shuffles register\n// R0 using preloaded constants. Outputs in R0 and R1.\n#define SHUF(R0, R1) \\\n\t\"pshufb  %[lut0], %[\"R0\"] \\n\\t\" \\\n\t\"movdqa  %[\"R0\"], %[\"R1\"] \\n\\t\" \\\n\t\"pand    %[msk0], %[\"R0\"] \\n\\t\" \\\n\t\"pand    %[msk2], %[\"R1\"] \\n\\t\" \\\n\t\"pmulhuw %[msk1], %[\"R0\"] \\n\\t\" \\\n\t\"pmullw  %[msk3], %[\"R1\"] \\n\\t\" \\\n\t\"por     %[\"R1\"], %[\"R0\"] \\n\\t\"\n\n// Generate a block of inline assembly that takes R0 and R1 and translates\n// their contents to the base64 alphabet, using preloaded constants.\n#define TRAN(R0, R1, R2) \\\n\t\"movdqa  %[\"R0\"], %[\"R1\"] \\n\\t\" \\\n\t\"movdqa  %[\"R0\"], %[\"R2\"] \\n\\t\" \\\n\t\"psubusb %[n51],  %[\"R1\"] \\n\\t\" \\\n\t\"pcmpgtb %[n25],  %[\"R2\"] \\n\\t\" \\\n\t\"psubb   %[\"R2\"], %[\"R1\"] \\n\\t\" \\\n\t\"movdqa  %[lut1], %[\"R2\"] \\n\\t\" \\\n\t\"pshufb  %[\"R1\"], %[\"R2\"] \\n\\t\" \\\n\t\"paddb   %[\"R2\"], %[\"R0\"] \\n\\t\"\n\n// Generate a block of inline assembly that stores the given register R0 at an\n// offset set by the given round.\n#define STOR(R0, ROUND) \\\n\t\"movdqu %[\"R0\"], (\"#ROUND\" * 16)(%[dst]) \\n\\t\"\n\n// Generate a block of inline assembly that generates a single self-contained\n// encoder round: fetch the data, process it, and store the result. Then update\n// the source and destination pointers.\n#define ROUND() \\\n\tLOAD(\"a\", 0) \\\n\tSHUF(\"a\", \"b\") \\\n\tTRAN(\"a\", \"b\", \"c\") \\\n\tSTOR(\"a\", 0) \\\n\t\"add $12, %[src] \\n\\t\" \\\n\t\"add $16, %[dst] \\n\\t\"\n\n// Define a macro that initiates a three-way interleaved encoding round by\n// preloading registers a, b and c from memory.\n// The register graph shows which registers are in use during each step, and\n// is a visual aid for choosing registers for that step. Symbol index:\n//\n//  +  indicates that a register is loaded by that step.\n//  |  indicates that a register is in use and must not be touched.\n//  -  indicates that a register is decommissioned by that step.\n//  x  indicates that a register is used as a temporary by that step.\n//  V  indicates that a register is an input or output to the macro.\n//\n#define ROUND_3_INIT() \t\t\t/*  a b c d e f  */ \\\n\tLOAD(\"a\", 0)\t\t\t/*  +            */ \\\n\tSHUF(\"a\", \"d\")\t\t\t/*  |     +      */ \\\n\tLOAD(\"b\", 1)\t\t\t/*  | +   |      */ \\\n\tTRAN(\"a\", \"d\", \"e\")\t\t/*  | |   - x    */ \\\n\tLOAD(\"c\", 2)\t\t\t/*  V V V        */\n\n// Define a macro that translates, shuffles and stores the input registers A, B\n// and C, and preloads registers D, E and F for the next round.\n// This macro can be arbitrarily daisy-chained by feeding output registers D, E\n// and F back into the next round as input registers A, B and C. The macro\n// carefully interleaves memory operations with data operations for optimal\n// pipelined performance.\n\n#define ROUND_3(ROUND, A,B,C,D,E,F) \t/*  A B C D E F  */ \\\n\tLOAD(D, (ROUND + 3))\t\t/*  V V V +      */ \\\n\tSHUF(B, E)\t\t\t/*  | | | | +    */ \\\n\tSTOR(A, (ROUND + 0))\t\t/*  - | | | |    */ \\\n\tTRAN(B, E, F)\t\t\t/*    | | | - x  */ \\\n\tLOAD(E, (ROUND + 4))\t\t/*    | | | +    */ \\\n\tSHUF(C, A)\t\t\t/*  + | | | |    */ \\\n\tSTOR(B, (ROUND + 1))\t\t/*  | - | | |    */ \\\n\tTRAN(C, A, F)\t\t\t/*  -   | | | x  */ \\\n\tLOAD(F, (ROUND + 5))\t\t/*      | | | +  */ \\\n\tSHUF(D, A)\t\t\t/*  +   | | | |  */ \\\n\tSTOR(C, (ROUND + 2))\t\t/*  |   - | | |  */ \\\n\tTRAN(D, A, B)\t\t\t/*  - x   V V V  */\n\n// Define a macro that terminates a ROUND_3 macro by taking pre-loaded\n// registers D, E and F, and translating, shuffling and storing them.\n#define ROUND_3_END(ROUND, A,B,C,D,E,F)\t/*  A B C D E F  */ \\\n\tSHUF(E, A)\t\t\t/*  +     V V V  */ \\\n\tSTOR(D, (ROUND + 3))\t\t/*  |     - | |  */ \\\n\tTRAN(E, A, B)\t\t\t/*  - x     | |  */ \\\n\tSHUF(F, C)\t\t\t/*      +   | |  */ \\\n\tSTOR(E, (ROUND + 4))\t\t/*      |   - |  */ \\\n\tTRAN(F, C, D)\t\t\t/*      - x   |  */ \\\n\tSTOR(F, (ROUND + 5))\t\t/*            -  */\n\n// Define a type A round. Inputs are a, b, and c, outputs are d, e, and f.\n#define ROUND_3_A(ROUND) \\\n\tROUND_3(ROUND, \"a\", \"b\", \"c\", \"d\", \"e\", \"f\")\n\n// Define a type B round. Inputs and outputs are swapped with regard to type A.\n#define ROUND_3_B(ROUND) \\\n\tROUND_3(ROUND, \"d\", \"e\", \"f\", \"a\", \"b\", \"c\")\n\n// Terminating macro for a type A round.\n#define ROUND_3_A_LAST(ROUND) \\\n\tROUND_3_A(ROUND) \\\n\tROUND_3_END(ROUND, \"a\", \"b\", \"c\", \"d\", \"e\", \"f\")\n\n// Terminating macro for a type B round.\n#define ROUND_3_B_LAST(ROUND) \\\n\tROUND_3_B(ROUND) \\\n\tROUND_3_END(ROUND, \"d\", \"e\", \"f\", \"a\", \"b\", \"c\")\n\n// Suppress clang's warning that the literal string in the asm statement is\n// overlong (longer than the ISO-mandated minimum size of 4095 bytes for C99\n// compilers). It may be true, but the goal here is not C99 portability.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Woverlength-strings\"\n\nstatic inline void\nenc_loop_ssse3 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)\n{\n\t// For a clearer explanation of the algorithm used by this function,\n\t// please refer to the plain (not inline assembly) implementation. This\n\t// function follows the same basic logic.\n\n\tif (*slen < 16) {\n\t\treturn;\n\t}\n\n\t// Process blocks of 12 bytes at a time. Input is read in blocks of 16\n\t// bytes, so \"reserve\" four bytes from the input buffer to ensure that\n\t// we never read beyond the end of the input buffer.\n\tsize_t rounds = (*slen - 4) / 12;\n\n\t*slen -= rounds * 12;   // 12 bytes consumed per round\n\t*olen += rounds * 16;   // 16 bytes produced per round\n\n\t// Number of times to go through the 36x loop.\n\tsize_t loops = rounds / 36;\n\n\t// Number of rounds remaining after the 36x loop.\n\trounds %= 36;\n\n\t// Lookup tables.\n\tconst __m128i lut0 = _mm_set_epi8(\n\t\t10, 11,  9, 10,  7,  8,  6,  7,  4,  5,  3,  4,  1,  2,  0,  1);\n\n\tconst __m128i lut1 = _mm_setr_epi8(\n\t\t65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0);\n\n\t// Temporary registers.\n\t__m128i a, b, c, d, e, f;\n\n\t__asm__ volatile (\n\n\t\t// If there are 36 rounds or more, enter a 36x unrolled loop of\n\t\t// interleaved encoding rounds. The rounds interleave memory\n\t\t// operations (load/store) with data operations (table lookups,\n\t\t// etc) to maximize pipeline throughput.\n\t\t\"    test %[loops], %[loops] \\n\\t\"\n\t\t\"    jz   18f                \\n\\t\"\n\t\t\"    jmp  36f                \\n\\t\"\n\t\t\"                            \\n\\t\"\n\t\t\".balign 64                  \\n\\t\"\n\t\t\"36: \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A( 0)\n\t\t\"    \" ROUND_3_B( 3)\n\t\t\"    \" ROUND_3_A( 6)\n\t\t\"    \" ROUND_3_B( 9)\n\t\t\"    \" ROUND_3_A(12)\n\t\t\"    \" ROUND_3_B(15)\n\t\t\"    \" ROUND_3_A(18)\n\t\t\"    \" ROUND_3_B(21)\n\t\t\"    \" ROUND_3_A(24)\n\t\t\"    \" ROUND_3_B(27)\n\t\t\"    \" ROUND_3_A_LAST(30)\n\t\t\"    add $(12 * 36), %[src] \\n\\t\"\n\t\t\"    add $(16 * 36), %[dst] \\n\\t\"\n\t\t\"    dec %[loops]           \\n\\t\"\n\t\t\"    jnz 36b                \\n\\t\"\n\n\t\t// Enter an 18x unrolled loop for rounds of 18 or more.\n\t\t\"18: cmp $18, %[rounds] \\n\\t\"\n\t\t\"    jl  9f             \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A(0)\n\t\t\"    \" ROUND_3_B(3)\n\t\t\"    \" ROUND_3_A(6)\n\t\t\"    \" ROUND_3_B(9)\n\t\t\"    \" ROUND_3_A_LAST(12)\n\t\t\"    sub $18,        %[rounds] \\n\\t\"\n\t\t\"    add $(12 * 18), %[src]    \\n\\t\"\n\t\t\"    add $(16 * 18), %[dst]    \\n\\t\"\n\n\t\t// Enter a 9x unrolled loop for rounds of 9 or more.\n\t\t\"9:  cmp $9, %[rounds] \\n\\t\"\n\t\t\"    jl  6f            \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A(0)\n\t\t\"    \" ROUND_3_B_LAST(3)\n\t\t\"    sub $9,        %[rounds] \\n\\t\"\n\t\t\"    add $(12 * 9), %[src]    \\n\\t\"\n\t\t\"    add $(16 * 9), %[dst]    \\n\\t\"\n\n\t\t// Enter a 6x unrolled loop for rounds of 6 or more.\n\t\t\"6:  cmp $6, %[rounds] \\n\\t\"\n\t\t\"    jl  55f           \\n\\t\"\n\t\t\"    \" ROUND_3_INIT()\n\t\t\"    \" ROUND_3_A_LAST(0)\n\t\t\"    sub $6,        %[rounds] \\n\\t\"\n\t\t\"    add $(12 * 6), %[src]    \\n\\t\"\n\t\t\"    add $(16 * 6), %[dst]    \\n\\t\"\n\n\t\t// Dispatch the remaining rounds 0..5.\n\t\t\"55: cmp $3, %[rounds] \\n\\t\"\n\t\t\"    jg  45f           \\n\\t\"\n\t\t\"    je  3f            \\n\\t\"\n\t\t\"    cmp $1, %[rounds] \\n\\t\"\n\t\t\"    jg  2f            \\n\\t\"\n\t\t\"    je  1f            \\n\\t\"\n\t\t\"    jmp 0f            \\n\\t\"\n\n\t\t\"45: cmp $4, %[rounds] \\n\\t\"\n\t\t\"    je  4f            \\n\\t\"\n\n\t\t// Block of non-interlaced encoding rounds, which can each\n\t\t// individually be jumped to. Rounds fall through to the next.\n\t\t\"5: \" ROUND()\n\t\t\"4: \" ROUND()\n\t\t\"3: \" ROUND()\n\t\t\"2: \" ROUND()\n\t\t\"1: \" ROUND()\n\t\t\"0: \\n\\t\"\n\n\t\t// Outputs (modified).\n\t\t: [rounds] \"+r\"  (rounds),\n\t\t  [loops]  \"+r\"  (loops),\n\t\t  [src]    \"+r\"  (*s),\n\t\t  [dst]    \"+r\"  (*o),\n\t\t  [a]      \"=&x\" (a),\n\t\t  [b]      \"=&x\" (b),\n\t\t  [c]      \"=&x\" (c),\n\t\t  [d]      \"=&x\" (d),\n\t\t  [e]      \"=&x\" (e),\n\t\t  [f]      \"=&x\" (f)\n\n\t\t// Inputs (not modified).\n\t\t: [lut0] \"x\" (lut0),\n\t\t  [lut1] \"x\" (lut1),\n\t\t  [msk0] \"x\" (_mm_set1_epi32(0x0FC0FC00)),\n\t\t  [msk1] \"x\" (_mm_set1_epi32(0x04000040)),\n\t\t  [msk2] \"x\" (_mm_set1_epi32(0x003F03F0)),\n\t\t  [msk3] \"x\" (_mm_set1_epi32(0x01000010)),\n\t\t  [n51]  \"x\" (_mm_set1_epi8(51)),\n\t\t  [n25]  \"x\" (_mm_set1_epi8(25))\n\n\t\t// Clobbers.\n\t\t: \"cc\", \"memory\"\n\t);\n}\n\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/ssse3/enc_reshuffle.c",
    "content": "static inline __m128i\nenc_reshuffle (__m128i in)\n{\n\t// Input, bytes MSB to LSB:\n\t// 0 0 0 0 l k j i h g f e d c b a\n\n\tin = _mm_shuffle_epi8(in, _mm_set_epi8(\n\t\t10, 11,  9, 10,\n\t\t 7,  8,  6,  7,\n\t\t 4,  5,  3,  4,\n\t\t 1,  2,  0,  1));\n\t// in, bytes MSB to LSB:\n\t// k l j k\n\t// h i g h\n\t// e f d e\n\t// b c a b\n\n\tconst __m128i t0 = _mm_and_si128(in, _mm_set1_epi32(0x0FC0FC00));\n\t// bits, upper case are most significant bits, lower case are least significant bits\n\t// 0000kkkk LL000000 JJJJJJ00 00000000\n\t// 0000hhhh II000000 GGGGGG00 00000000\n\t// 0000eeee FF000000 DDDDDD00 00000000\n\t// 0000bbbb CC000000 AAAAAA00 00000000\n\n\tconst __m128i t1 = _mm_mulhi_epu16(t0, _mm_set1_epi32(0x04000040));\n\t// 00000000 00kkkkLL 00000000 00JJJJJJ\n\t// 00000000 00hhhhII 00000000 00GGGGGG\n\t// 00000000 00eeeeFF 00000000 00DDDDDD\n\t// 00000000 00bbbbCC 00000000 00AAAAAA\n\n\tconst __m128i t2 = _mm_and_si128(in, _mm_set1_epi32(0x003F03F0));\n\t// 00000000 00llllll 000000jj KKKK0000\n\t// 00000000 00iiiiii 000000gg HHHH0000\n\t// 00000000 00ffffff 000000dd EEEE0000\n\t// 00000000 00cccccc 000000aa BBBB0000\n\n\tconst __m128i t3 = _mm_mullo_epi16(t2, _mm_set1_epi32(0x01000010));\n\t// 00llllll 00000000 00jjKKKK 00000000\n\t// 00iiiiii 00000000 00ggHHHH 00000000\n\t// 00ffffff 00000000 00ddEEEE 00000000\n\t// 00cccccc 00000000 00aaBBBB 00000000\n\n\treturn _mm_or_si128(t1, t3);\n\t// 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ\n\t// 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG\n\t// 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD\n\t// 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/arch/ssse3/enc_translate.c",
    "content": "static inline __m128i\nenc_translate (const __m128i in)\n{\n\t// A lookup table containing the absolute offsets for all ranges:\n\tconst __m128i lut = _mm_setr_epi8(\n\t\t 65,  71, -4, -4,\n\t\t -4,  -4, -4, -4,\n\t\t -4,  -4, -4, -4,\n\t\t-19, -16,  0,  0\n\t);\n\n\t// Translate values 0..63 to the Base64 alphabet. There are five sets:\n\t// #  From      To         Abs    Index  Characters\n\t// 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ\n\t// 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz\n\t// 2  [52..61]  [48..57]    -4  [2..11]  0123456789\n\t// 3  [62]      [43]       -19       12  +\n\t// 4  [63]      [47]       -16       13  /\n\n\t// Create LUT indices from the input. The index for range #0 is right,\n\t// others are 1 less than expected:\n\t__m128i indices = _mm_subs_epu8(in, _mm_set1_epi8(51));\n\n\t// mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:\n\t__m128i mask = _mm_cmpgt_epi8(in, _mm_set1_epi8(25));\n\n\t// Subtract -1, so add 1 to indices for range #[1..4]. All indices are\n\t// now correct:\n\tindices = _mm_sub_epi8(indices, mask);\n\n\t// Add offsets to input values:\n\treturn _mm_add_epi8(in, _mm_shuffle_epi8(lut, indices));\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/codec_choose.c",
    "content": "#include <stdbool.h>\n#include <stdint.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n\n#include \"../include/libbase64.h\"\n#include \"codecs.h\"\n#include \"config.h\"\n#include \"env.h\"\n\n#if (__x86_64__ || __i386__ || _M_X86 || _M_X64)\n  #define BASE64_X86\n  #if (HAVE_SSSE3 || HAVE_SSE41 || HAVE_SSE42 || HAVE_AVX || HAVE_AVX2 || HAVE_AVX512)\n    #define BASE64_X86_SIMD\n  #endif\n#endif\n\n#ifdef BASE64_X86\n#ifdef _MSC_VER\n\t#include <intrin.h>\n\t#define __cpuid_count(__level, __count, __eax, __ebx, __ecx, __edx) \\\n\t{\t\t\t\t\t\t\\\n\t\tint info[4];\t\t\t\t\\\n\t\t__cpuidex(info, __level, __count);\t\\\n\t\t__eax = info[0];\t\t\t\\\n\t\t__ebx = info[1];\t\t\t\\\n\t\t__ecx = info[2];\t\t\t\\\n\t\t__edx = info[3];\t\t\t\\\n\t}\n\t#define __cpuid(__level, __eax, __ebx, __ecx, __edx) \\\n\t\t__cpuid_count(__level, 0, __eax, __ebx, __ecx, __edx)\n#else\n\t#include <cpuid.h>\n\t#if HAVE_AVX512 || HAVE_AVX2 || HAVE_AVX\n\t\t#if ((__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 2) || (__clang_major__ >= 3))\n\t\t\tstatic inline uint64_t _xgetbv (uint32_t index)\n\t\t\t{\n\t\t\t\tuint32_t eax, edx;\n\t\t\t\t__asm__ __volatile__(\"xgetbv\" : \"=a\"(eax), \"=d\"(edx) : \"c\"(index));\n\t\t\t\treturn ((uint64_t)edx << 32) | eax;\n\t\t\t}\n\t\t#else\n\t\t\t#error \"Platform not supported\"\n\t\t#endif\n\t#endif\n#endif\n\n#ifndef bit_AVX512vl\n#define bit_AVX512vl (1 << 31)\n#endif\n#ifndef bit_AVX512vbmi\n#define bit_AVX512vbmi (1 << 1)\n#endif\n#ifndef bit_AVX2\n#define bit_AVX2 (1 << 5)\n#endif\n#ifndef bit_SSSE3\n#define bit_SSSE3 (1 << 9)\n#endif\n#ifndef bit_SSE41\n#define bit_SSE41 (1 << 19)\n#endif\n#ifndef bit_SSE42\n#define bit_SSE42 (1 << 20)\n#endif\n#ifndef bit_AVX\n#define bit_AVX (1 << 28)\n#endif\n\n#define bit_XSAVE_XRSTORE (1 << 27)\n\n#ifndef _XCR_XFEATURE_ENABLED_MASK\n#define _XCR_XFEATURE_ENABLED_MASK 0\n#endif\n\n#define _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS 0x6\n#endif\n\n// Function declarations:\n#define BASE64_CODEC_FUNCS(arch)\t\\\n\tBASE64_ENC_FUNCTION(arch);\t\\\n\tBASE64_DEC_FUNCTION(arch);\t\\\n\nBASE64_CODEC_FUNCS(avx512)\nBASE64_CODEC_FUNCS(avx2)\nBASE64_CODEC_FUNCS(neon32)\nBASE64_CODEC_FUNCS(neon64)\nBASE64_CODEC_FUNCS(plain)\nBASE64_CODEC_FUNCS(ssse3)\nBASE64_CODEC_FUNCS(sse41)\nBASE64_CODEC_FUNCS(sse42)\nBASE64_CODEC_FUNCS(avx)\n\nstatic bool\ncodec_choose_forced (struct codec *codec, int flags)\n{\n\t// If the user wants to use a certain codec,\n\t// always allow it, even if the codec is a no-op.\n\t// For testing purposes.\n\n\tif (!(flags & 0xFFFF)) {\n\t\treturn false;\n\t}\n\n\tif (flags & BASE64_FORCE_AVX2) {\n\t\tcodec->enc = base64_stream_encode_avx2;\n\t\tcodec->dec = base64_stream_decode_avx2;\n\t\treturn true;\n\t}\n\tif (flags & BASE64_FORCE_NEON32) {\n\t\tcodec->enc = base64_stream_encode_neon32;\n\t\tcodec->dec = base64_stream_decode_neon32;\n\t\treturn true;\n\t}\n\tif (flags & BASE64_FORCE_NEON64) {\n\t\tcodec->enc = base64_stream_encode_neon64;\n\t\tcodec->dec = base64_stream_decode_neon64;\n\t\treturn true;\n\t}\n\tif (flags & BASE64_FORCE_PLAIN) {\n\t\tcodec->enc = base64_stream_encode_plain;\n\t\tcodec->dec = base64_stream_decode_plain;\n\t\treturn true;\n\t}\n\tif (flags & BASE64_FORCE_SSSE3) {\n\t\tcodec->enc = base64_stream_encode_ssse3;\n\t\tcodec->dec = base64_stream_decode_ssse3;\n\t\treturn true;\n\t}\n\tif (flags & BASE64_FORCE_SSE41) {\n\t\tcodec->enc = base64_stream_encode_sse41;\n\t\tcodec->dec = base64_stream_decode_sse41;\n\t\treturn true;\n\t}\n\tif (flags & BASE64_FORCE_SSE42) {\n\t\tcodec->enc = base64_stream_encode_sse42;\n\t\tcodec->dec = base64_stream_decode_sse42;\n\t\treturn true;\n\t}\n\tif (flags & BASE64_FORCE_AVX) {\n\t\tcodec->enc = base64_stream_encode_avx;\n\t\tcodec->dec = base64_stream_decode_avx;\n\t\treturn true;\n\t}\n\tif (flags & BASE64_FORCE_AVX512) {\n\t\tcodec->enc = base64_stream_encode_avx512;\n\t\tcodec->dec = base64_stream_decode_avx512;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic bool\ncodec_choose_arm (struct codec *codec)\n{\n#if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && ((defined(__aarch64__) && HAVE_NEON64) || HAVE_NEON32)\n\n\t// Unfortunately there is no portable way to check for NEON\n\t// support at runtime from userland in the same way that x86\n\t// has cpuid, so just stick to the compile-time configuration:\n\n\t#if defined(__aarch64__) && HAVE_NEON64\n\tcodec->enc = base64_stream_encode_neon64;\n\tcodec->dec = base64_stream_decode_neon64;\n\t#else\n\tcodec->enc = base64_stream_encode_neon32;\n\tcodec->dec = base64_stream_decode_neon32;\n\t#endif\n\n\treturn true;\n\n#else\n\t(void)codec;\n\treturn false;\n#endif\n}\n\nstatic bool\ncodec_choose_x86 (struct codec *codec)\n{\n#ifdef BASE64_X86_SIMD\n\n\tunsigned int eax, ebx = 0, ecx = 0, edx;\n\tunsigned int max_level;\n\n\t#ifdef _MSC_VER\n\tint info[4];\n\t__cpuidex(info, 0, 0);\n\tmax_level = info[0];\n\t#else\n\tmax_level = __get_cpuid_max(0, NULL);\n\t#endif\n\n\t#if HAVE_AVX512 || HAVE_AVX2 || HAVE_AVX\n\t// Check for AVX/AVX2/AVX512 support:\n\t// Checking for AVX requires 3 things:\n\t// 1) CPUID indicates that the OS uses XSAVE and XRSTORE instructions\n\t//    (allowing saving YMM registers on context switch)\n\t// 2) CPUID indicates support for AVX\n\t// 3) XGETBV indicates the AVX registers will be saved and restored on\n\t//    context switch\n\t//\n\t// Note that XGETBV is only available on 686 or later CPUs, so the\n\t// instruction needs to be conditionally run.\n\tif (max_level >= 1) {\n\t\t__cpuid_count(1, 0, eax, ebx, ecx, edx);\n\t\tif (ecx & bit_XSAVE_XRSTORE) {\n\t\t\tuint64_t xcr_mask;\n\t\t\txcr_mask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);\n\t\t\tif ((xcr_mask & _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS) == _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS) { // check multiple bits at once\n\t\t\t\t#if HAVE_AVX512\n\t\t\t\tif (max_level >= 7) {\n\t\t\t\t\t__cpuid_count(7, 0, eax, ebx, ecx, edx);\n\t\t\t\t\tif ((ebx & bit_AVX512vl) && (ecx & bit_AVX512vbmi)) {\n\t\t\t\t\t\tcodec->enc = base64_stream_encode_avx512;\n\t\t\t\t\t\tcodec->dec = base64_stream_decode_avx512;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t#endif\n\t\t\t\t#if HAVE_AVX2\n\t\t\t\tif (max_level >= 7) {\n\t\t\t\t\t__cpuid_count(7, 0, eax, ebx, ecx, edx);\n\t\t\t\t\tif (ebx & bit_AVX2) {\n\t\t\t\t\t\tcodec->enc = base64_stream_encode_avx2;\n\t\t\t\t\t\tcodec->dec = base64_stream_decode_avx2;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t#endif\n\t\t\t\t#if HAVE_AVX\n\t\t\t\t__cpuid_count(1, 0, eax, ebx, ecx, edx);\n\t\t\t\tif (ecx & bit_AVX) {\n\t\t\t\t\tcodec->enc = base64_stream_encode_avx;\n\t\t\t\t\tcodec->dec = base64_stream_decode_avx;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\t#endif\n\t\t\t}\n\t\t}\n\t}\n\t#endif\n\n\t#if HAVE_SSE42\n\t// Check for SSE42 support:\n\tif (max_level >= 1) {\n\t\t__cpuid(1, eax, ebx, ecx, edx);\n\t\tif (ecx & bit_SSE42) {\n\t\t\tcodec->enc = base64_stream_encode_sse42;\n\t\t\tcodec->dec = base64_stream_decode_sse42;\n\t\t\treturn true;\n\t\t}\n\t}\n\t#endif\n\n\t#if HAVE_SSE41\n\t// Check for SSE41 support:\n\tif (max_level >= 1) {\n\t\t__cpuid(1, eax, ebx, ecx, edx);\n\t\tif (ecx & bit_SSE41) {\n\t\t\tcodec->enc = base64_stream_encode_sse41;\n\t\t\tcodec->dec = base64_stream_decode_sse41;\n\t\t\treturn true;\n\t\t}\n\t}\n\t#endif\n\n\t#if HAVE_SSSE3\n\t// Check for SSSE3 support:\n\tif (max_level >= 1) {\n\t\t__cpuid(1, eax, ebx, ecx, edx);\n\t\tif (ecx & bit_SSSE3) {\n\t\t\tcodec->enc = base64_stream_encode_ssse3;\n\t\t\tcodec->dec = base64_stream_decode_ssse3;\n\t\t\treturn true;\n\t\t}\n\t}\n\t#endif\n\n#else\n\t(void)codec;\n#endif\n\n\treturn false;\n}\n\nvoid\ncodec_choose (struct codec *codec, int flags)\n{\n\t// User forced a codec:\n\tif (codec_choose_forced(codec, flags)) {\n\t\treturn;\n\t}\n\n\t// Runtime feature detection:\n\tif (codec_choose_arm(codec)) {\n\t\treturn;\n\t}\n\tif (codec_choose_x86(codec)) {\n\t\treturn;\n\t}\n\tcodec->enc = base64_stream_encode_plain;\n\tcodec->dec = base64_stream_decode_plain;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/codecs.h",
    "content": "#include <stdint.h>\n#include <stddef.h>\n\n#include \"../include/libbase64.h\"\n#include \"config.h\"\n\n// Function parameters for encoding functions:\n#define BASE64_ENC_PARAMS\t\t\t\\\n\t( struct base64_state\t*state\t\t\\\n\t, const char\t\t*src\t\t\\\n\t, size_t\t\t srclen\t\t\\\n\t, char\t\t\t*out\t\t\\\n\t, size_t\t\t*outlen\t\t\\\n\t)\n\n// Function parameters for decoding functions:\n#define BASE64_DEC_PARAMS\t\t\t\\\n\t( struct base64_state\t*state\t\t\\\n\t, const char\t\t*src\t\t\\\n\t, size_t\t\t srclen\t\t\\\n\t, char\t\t\t*out\t\t\\\n\t, size_t\t\t*outlen\t\t\\\n\t)\n\n// Function signature for encoding functions:\n#define BASE64_ENC_FUNCTION(arch)\t\t\\\n\tvoid\t\t\t\t\t\\\n\tbase64_stream_encode_ ## arch\t\t\\\n\tBASE64_ENC_PARAMS\n\n// Function signature for decoding functions:\n#define BASE64_DEC_FUNCTION(arch)\t\t\\\n\tint\t\t\t\t\t\\\n\tbase64_stream_decode_ ## arch\t\t\\\n\tBASE64_DEC_PARAMS\n\n// Cast away unused variable, silence compiler:\n#define UNUSED(x)\t\t((void)(x))\n\n// Stub function when encoder arch unsupported:\n#define BASE64_ENC_STUB\t\t\t\t\\\n\tUNUSED(state);\t\t\t\t\\\n\tUNUSED(src);\t\t\t\t\\\n\tUNUSED(srclen);\t\t\t\t\\\n\tUNUSED(out);\t\t\t\t\\\n\t\t\t\t\t\t\\\n\t*outlen = 0;\n\n// Stub function when decoder arch unsupported:\n#define BASE64_DEC_STUB\t\t\t\t\\\n\tUNUSED(state);\t\t\t\t\\\n\tUNUSED(src);\t\t\t\t\\\n\tUNUSED(srclen);\t\t\t\t\\\n\tUNUSED(out);\t\t\t\t\\\n\tUNUSED(outlen);\t\t\t\t\\\n\t\t\t\t\t\t\\\n\treturn -1;\n\nstruct codec\n{\n\tvoid (* enc) BASE64_ENC_PARAMS;\n\tint  (* dec) BASE64_DEC_PARAMS;\n};\n\nextern void codec_choose (struct codec *, int flags);\n"
  },
  {
    "path": "3rdparty/base64/lib/env.h",
    "content": "#ifndef BASE64_ENV_H\n#define BASE64_ENV_H\n\n// This header file contains macro definitions that describe certain aspects of\n// the compile-time environment. Compatibility and portability macros go here.\n\n// Define machine endianness. This is for GCC:\n#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)\n#  define BASE64_LITTLE_ENDIAN 1\n#else\n#  define BASE64_LITTLE_ENDIAN 0\n#endif\n\n// This is for Clang:\n#ifdef __LITTLE_ENDIAN__\n#  define BASE64_LITTLE_ENDIAN 1\n#endif\n\n#ifdef __BIG_ENDIAN__\n#  define BASE64_LITTLE_ENDIAN 0\n#endif\n\n// MSVC++ needs intrin.h for _byteswap_uint64 (issue #68):\n#if BASE64_LITTLE_ENDIAN && defined(_MSC_VER)\n#  include <intrin.h>\n#endif\n\n// Endian conversion functions:\n#if BASE64_LITTLE_ENDIAN\n#  ifdef _MSC_VER\n//   Microsoft Visual C++:\n#    define BASE64_HTOBE32(x)\t_byteswap_ulong(x)\n#    define BASE64_HTOBE64(x)\t_byteswap_uint64(x)\n#  else\n//   GCC and Clang:\n#    define BASE64_HTOBE32(x)\t__builtin_bswap32(x)\n#    define BASE64_HTOBE64(x)\t__builtin_bswap64(x)\n#  endif\n#else\n// No conversion needed:\n#  define BASE64_HTOBE32(x)\t(x)\n#  define BASE64_HTOBE64(x)\t(x)\n#endif\n\n// Detect word size:\n#if defined (__x86_64__)\n// This also works for the x32 ABI, which has a 64-bit word size.\n#  define BASE64_WORDSIZE 64\n#elif defined (_INTEGRAL_MAX_BITS)\n#  define BASE64_WORDSIZE _INTEGRAL_MAX_BITS\n#elif defined (__WORDSIZE)\n#  define BASE64_WORDSIZE __WORDSIZE\n#elif defined (__SIZE_WIDTH__)\n#  define BASE64_WORDSIZE __SIZE_WIDTH__\n#else\n#  error BASE64_WORDSIZE_NOT_DEFINED\n#endif\n\n// End-of-file definitions.\n// Almost end-of-file when waiting for the last '=' character:\n#define BASE64_AEOF 1\n// End-of-file when stream end has been reached or invalid input provided:\n#define BASE64_EOF 2\n\n// GCC 7 defaults to issuing a warning for fallthrough in switch statements,\n// unless the fallthrough cases are marked with an attribute. As we use\n// fallthrough deliberately, define an alias for the attribute:\n#if __GNUC__ >= 7\n#  define BASE64_FALLTHROUGH  __attribute__((fallthrough));\n#else\n#  define BASE64_FALLTHROUGH\n#endif\n\n#endif\t// BASE64_ENV_H\n"
  },
  {
    "path": "3rdparty/base64/lib/exports.txt",
    "content": "base64_encode\nbase64_stream_encode\nbase64_stream_encode_init\nbase64_stream_encode_final\nbase64_decode\nbase64_stream_decode\nbase64_stream_decode_init\n"
  },
  {
    "path": "3rdparty/base64/lib/lib.c",
    "content": "#include <stdint.h>\n#include <stddef.h>\n#ifdef _OPENMP\n#include <omp.h>\n#endif\n\n#include \"../include/libbase64.h\"\n#include \"tables/tables.h\"\n#include \"codecs.h\"\n#include \"env.h\"\n\n// These static function pointers are initialized once when the library is\n// first used, and remain in use for the remaining lifetime of the program.\n// The idea being that CPU features don't change at runtime.\nstatic struct codec codec = { NULL, NULL };\n\nvoid\nbase64_stream_encode_init (struct base64_state *state, int flags)\n{\n\t// If any of the codec flags are set, redo choice:\n\tif (codec.enc == NULL || flags & 0xFF) {\n\t\tcodec_choose(&codec, flags);\n\t}\n\tstate->eof = 0;\n\tstate->bytes = 0;\n\tstate->carry = 0;\n\tstate->flags = flags;\n}\n\nvoid\nbase64_stream_encode\n\t( struct base64_state\t*state\n\t, const char\t\t*src\n\t, size_t\t\t srclen\n\t, char\t\t\t*out\n\t, size_t\t\t*outlen\n\t)\n{\n\tcodec.enc(state, src, srclen, out, outlen);\n}\n\nvoid\nbase64_stream_encode_final\n\t( struct base64_state\t*state\n\t, char\t\t\t*out\n\t, size_t\t\t*outlen\n\t)\n{\n\tuint8_t *o = (uint8_t *)out;\n\n\tif (state->bytes == 1) {\n\t\t*o++ = base64_table_enc_6bit[state->carry];\n\t\t*o++ = '=';\n\t\t*o++ = '=';\n\t\t*outlen = 3;\n\t\treturn;\n\t}\n\tif (state->bytes == 2) {\n\t\t*o++ = base64_table_enc_6bit[state->carry];\n\t\t*o++ = '=';\n\t\t*outlen = 2;\n\t\treturn;\n\t}\n\t*outlen = 0;\n}\n\nvoid\nbase64_stream_decode_init (struct base64_state *state, int flags)\n{\n\t// If any of the codec flags are set, redo choice:\n\tif (codec.dec == NULL || flags & 0xFFFF) {\n\t\tcodec_choose(&codec, flags);\n\t}\n\tstate->eof = 0;\n\tstate->bytes = 0;\n\tstate->carry = 0;\n\tstate->flags = flags;\n}\n\nint\nbase64_stream_decode\n\t( struct base64_state\t*state\n\t, const char\t\t*src\n\t, size_t\t\t srclen\n\t, char\t\t\t*out\n\t, size_t\t\t*outlen\n\t)\n{\n\treturn codec.dec(state, src, srclen, out, outlen);\n}\n\n#ifdef _OPENMP\n\n\t// Due to the overhead of initializing OpenMP and creating a team of\n\t// threads, we require the data length to be larger than a threshold:\n\t#define OMP_THRESHOLD 20000\n\n\t// Conditionally include OpenMP-accelerated codec implementations:\n\t#include \"lib_openmp.c\"\n#endif\n\nvoid\nbase64_encode\n\t( const char\t*src\n\t, size_t\t srclen\n\t, char\t\t*out\n\t, size_t\t*outlen\n\t, int\t\t flags\n\t)\n{\n\tsize_t s;\n\tsize_t t;\n\tstruct base64_state state;\n\n\t#ifdef _OPENMP\n\tif (srclen >= OMP_THRESHOLD) {\n\t\tbase64_encode_openmp(src, srclen, out, outlen, flags);\n\t\treturn;\n\t}\n\t#endif\n\n\t// Init the stream reader:\n\tbase64_stream_encode_init(&state, flags);\n\n\t// Feed the whole string to the stream reader:\n\tbase64_stream_encode(&state, src, srclen, out, &s);\n\n\t// Finalize the stream by writing trailer if any:\n\tbase64_stream_encode_final(&state, out + s, &t);\n\n\t// Final output length is stream length plus tail:\n\t*outlen = s + t;\n}\n\nint\nbase64_decode\n\t( const char\t*src\n\t, size_t\t srclen\n\t, char\t\t*out\n\t, size_t\t*outlen\n\t, int\t\t flags\n\t)\n{\n\tint ret;\n\tstruct base64_state state;\n\n\t#ifdef _OPENMP\n\tif (srclen >= OMP_THRESHOLD) {\n\t\treturn base64_decode_openmp(src, srclen, out, outlen, flags);\n\t}\n\t#endif\n\n\t// Init the stream reader:\n\tbase64_stream_decode_init(&state, flags);\n\n\t// Feed the whole string to the stream reader:\n\tret = base64_stream_decode(&state, src, srclen, out, outlen);\n\n\t// If when decoding a whole block, we're still waiting for input then fail:\n\tif (ret && (state.bytes == 0)) {\n\t\treturn ret;\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/lib_openmp.c",
    "content": "// This code makes some assumptions on the implementation of\n// base64_stream_encode_init(), base64_stream_encode() and base64_stream_decode().\n// Basically these assumptions boil down to that when breaking the src into\n// parts, out parts can be written without side effects.\n// This is met when:\n// 1) base64_stream_encode() and base64_stream_decode() don't use globals;\n// 2) the shared variables src and out are not read or written outside of the\n//    bounds of their parts, i.e.  when base64_stream_encode() reads a multiple\n//    of 3 bytes, it must write no more then a multiple of 4 bytes, not even\n//    temporarily;\n// 3) the state flag can be discarded after base64_stream_encode() and\n//    base64_stream_decode() on the parts.\n\nstatic inline void\nbase64_encode_openmp\n\t( const char\t*src\n\t, size_t\t srclen\n\t, char\t\t*out\n\t, size_t\t*outlen\n\t, int\t\t flags\n\t)\n{\n\tsize_t s;\n\tsize_t t;\n\tsize_t sum = 0, len, last_len;\n\tstruct base64_state state, initial_state;\n\tint num_threads, i;\n\n\t// Request a number of threads but not necessarily get them:\n\t#pragma omp parallel\n\t{\n\t\t// Get the number of threads used from one thread only,\n\t\t// as num_threads is a shared var:\n\t\t#pragma omp single\n\t\t{\n\t\t\tnum_threads = omp_get_num_threads();\n\n\t\t\t// Split the input string into num_threads parts, each\n\t\t\t// part a multiple of 3 bytes. The remaining bytes will\n\t\t\t// be done later:\n\t\t\tlen = srclen / (num_threads * 3);\n\t\t\tlen *= 3;\n\t\t\tlast_len = srclen - num_threads * len;\n\n\t\t\t// Init the stream reader:\n\t\t\tbase64_stream_encode_init(&state, flags);\n\t\t\tinitial_state = state;\n\t\t}\n\n\t\t// Single has an implicit barrier for all threads to wait here\n\t\t// for the above to complete:\n\t\t#pragma omp for firstprivate(state) private(s) reduction(+:sum) schedule(static,1)\n\t\tfor (i = 0; i < num_threads; i++)\n\t\t{\n\t\t\t// Feed each part of the string to the stream reader:\n\t\t\tbase64_stream_encode(&state, src + i * len, len, out + i * len * 4 / 3, &s);\n\t\t\tsum += s;\n\t\t}\n\t}\n\n\t// As encoding should never fail and we encode an exact multiple\n\t// of 3 bytes, we can discard state:\n\tstate = initial_state;\n\n\t// Encode the remaining bytes:\n\tbase64_stream_encode(&state, src + num_threads * len, last_len, out + num_threads * len * 4 / 3, &s);\n\n\t// Finalize the stream by writing trailer if any:\n\tbase64_stream_encode_final(&state, out + num_threads * len * 4 / 3 + s, &t);\n\n\t// Final output length is stream length plus tail:\n\tsum += s + t;\n\t*outlen = sum;\n}\n\nstatic inline int\nbase64_decode_openmp\n\t( const char\t*src\n\t, size_t\t srclen\n\t, char\t\t*out\n\t, size_t\t*outlen\n\t, int\t\t flags\n\t)\n{\n\tint num_threads, result = 0, i;\n\tsize_t sum = 0, len, last_len, s;\n\tstruct base64_state state, initial_state;\n\n\t// Request a number of threads but not necessarily get them:\n\t#pragma omp parallel\n\t{\n\t\t// Get the number of threads used from one thread only,\n\t\t// as num_threads is a shared var:\n\t\t#pragma omp single\n\t\t{\n\t\t\tnum_threads = omp_get_num_threads();\n\n\t\t\t// Split the input string into num_threads parts, each\n\t\t\t// part a multiple of 4 bytes. The remaining bytes will\n\t\t\t// be done later:\n\t\t\tlen = srclen / (num_threads * 4);\n\t\t\tlen *= 4;\n\t\t\tlast_len = srclen - num_threads * len;\n\n\t\t\t// Init the stream reader:\n\t\t\tbase64_stream_decode_init(&state, flags);\n\n\t\t\tinitial_state = state;\n\t\t}\n\n\t\t// Single has an implicit barrier to wait here for the above to\n\t\t// complete:\n\t\t#pragma omp for firstprivate(state) private(s) reduction(+:sum, result) schedule(static,1)\n\t\tfor (i = 0; i < num_threads; i++)\n\t\t{\n\t\t\tint this_result;\n\n\t\t\t// Feed each part of the string to the stream reader:\n\t\t\tthis_result = base64_stream_decode(&state, src + i * len, len, out + i * len * 3 / 4, &s);\n\t\t\tsum += s;\n\t\t\tresult += this_result;\n\t\t}\n\t}\n\n\t// If `result' equals `-num_threads', then all threads returned -1,\n\t// indicating that the requested codec is not available:\n\tif (result == -num_threads) {\n\t\treturn -1;\n\t}\n\n\t// If `result' does not equal `num_threads', then at least one of the\n\t// threads hit a decode error:\n\tif (result != num_threads) {\n\t\treturn 0;\n\t}\n\n\t// So far so good, now decode whatever remains in the buffer. Reuse the\n\t// initial state, since we are at a 4-byte boundary:\n\tstate = initial_state;\n\tresult = base64_stream_decode(&state, src + num_threads * len, last_len, out + num_threads * len * 3 / 4, &s);\n\tsum += s;\n\t*outlen = sum;\n\n\t// If when decoding a whole block, we're still waiting for input then fail:\n\tif (result && (state.bytes == 0)) {\n\t\treturn result;\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/tables/.gitignore",
    "content": "table_generator\n"
  },
  {
    "path": "3rdparty/base64/lib/tables/Makefile",
    "content": ".PHONY: all clean\n\nTARGETS := table_dec_32bit.h table_enc_12bit.h table_generator\n\nall: $(TARGETS)\n\nclean:\n\t$(RM) $(TARGETS)\n\ntable_dec_32bit.h: table_generator\n\t./$^ > $@\n\ntable_enc_12bit.h: table_enc_12bit.py\n\t./$^ > $@\n\ntable_generator: table_generator.c\n\t$(CC) $(CFLAGS) -o $@ $^\n"
  },
  {
    "path": "3rdparty/base64/lib/tables/table_dec_32bit.h",
    "content": "#include <stdint.h>\n#define CHAR62 '+'\n#define CHAR63 '/'\n#define CHARPAD '='\n\n\n#if BASE64_LITTLE_ENDIAN\n\n\n/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */\n\nconst uint32_t base64_table_dec_32bit_d0[256] = {\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x000000f8, 0xffffffff, 0xffffffff, 0xffffffff, 0x000000fc,\n0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,\n0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,\n0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,\n0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,\n0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,\n0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,\n0x00000064, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,\n0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,\n0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,\n0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,\n0x000000c4, 0x000000c8, 0x000000cc, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\n};\n\n\nconst uint32_t base64_table_dec_32bit_d1[256] = {\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x0000e003, 0xffffffff, 0xffffffff, 0xffffffff, 0x0000f003,\n0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,\n0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,\n0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,\n0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,\n0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,\n0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,\n0x00009001, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,\n0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,\n0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,\n0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,\n0x00001003, 0x00002003, 0x00003003, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\n};\n\n\nconst uint32_t base64_table_dec_32bit_d2[256] = {\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x00800f00, 0xffffffff, 0xffffffff, 0xffffffff, 0x00c00f00,\n0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,\n0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,\n0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,\n0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,\n0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,\n0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,\n0x00400600, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,\n0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,\n0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,\n0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,\n0x00400c00, 0x00800c00, 0x00c00c00, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\n};\n\n\nconst uint32_t base64_table_dec_32bit_d3[256] = {\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x003e0000, 0xffffffff, 0xffffffff, 0xffffffff, 0x003f0000,\n0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,\n0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,\n0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,\n0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,\n0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,\n0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,\n0x00190000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,\n0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,\n0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,\n0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,\n0x00310000, 0x00320000, 0x00330000, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\n};\n\n\n#else\n\n\n/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */\n\nconst uint32_t base64_table_dec_32bit_d0[256] = {\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xf8000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfc000000,\n0xd0000000, 0xd4000000, 0xd8000000, 0xdc000000, 0xe0000000, 0xe4000000,\n0xe8000000, 0xec000000, 0xf0000000, 0xf4000000, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,\n0x04000000, 0x08000000, 0x0c000000, 0x10000000, 0x14000000, 0x18000000,\n0x1c000000, 0x20000000, 0x24000000, 0x28000000, 0x2c000000, 0x30000000,\n0x34000000, 0x38000000, 0x3c000000, 0x40000000, 0x44000000, 0x48000000,\n0x4c000000, 0x50000000, 0x54000000, 0x58000000, 0x5c000000, 0x60000000,\n0x64000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x68000000, 0x6c000000, 0x70000000, 0x74000000, 0x78000000,\n0x7c000000, 0x80000000, 0x84000000, 0x88000000, 0x8c000000, 0x90000000,\n0x94000000, 0x98000000, 0x9c000000, 0xa0000000, 0xa4000000, 0xa8000000,\n0xac000000, 0xb0000000, 0xb4000000, 0xb8000000, 0xbc000000, 0xc0000000,\n0xc4000000, 0xc8000000, 0xcc000000, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\n};\n\n\nconst uint32_t base64_table_dec_32bit_d1[256] = {\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x03e00000, 0xffffffff, 0xffffffff, 0xffffffff, 0x03f00000,\n0x03400000, 0x03500000, 0x03600000, 0x03700000, 0x03800000, 0x03900000,\n0x03a00000, 0x03b00000, 0x03c00000, 0x03d00000, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,\n0x00100000, 0x00200000, 0x00300000, 0x00400000, 0x00500000, 0x00600000,\n0x00700000, 0x00800000, 0x00900000, 0x00a00000, 0x00b00000, 0x00c00000,\n0x00d00000, 0x00e00000, 0x00f00000, 0x01000000, 0x01100000, 0x01200000,\n0x01300000, 0x01400000, 0x01500000, 0x01600000, 0x01700000, 0x01800000,\n0x01900000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x01a00000, 0x01b00000, 0x01c00000, 0x01d00000, 0x01e00000,\n0x01f00000, 0x02000000, 0x02100000, 0x02200000, 0x02300000, 0x02400000,\n0x02500000, 0x02600000, 0x02700000, 0x02800000, 0x02900000, 0x02a00000,\n0x02b00000, 0x02c00000, 0x02d00000, 0x02e00000, 0x02f00000, 0x03000000,\n0x03100000, 0x03200000, 0x03300000, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\n};\n\n\nconst uint32_t base64_table_dec_32bit_d2[256] = {\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x000f8000, 0xffffffff, 0xffffffff, 0xffffffff, 0x000fc000,\n0x000d0000, 0x000d4000, 0x000d8000, 0x000dc000, 0x000e0000, 0x000e4000,\n0x000e8000, 0x000ec000, 0x000f0000, 0x000f4000, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,\n0x00004000, 0x00008000, 0x0000c000, 0x00010000, 0x00014000, 0x00018000,\n0x0001c000, 0x00020000, 0x00024000, 0x00028000, 0x0002c000, 0x00030000,\n0x00034000, 0x00038000, 0x0003c000, 0x00040000, 0x00044000, 0x00048000,\n0x0004c000, 0x00050000, 0x00054000, 0x00058000, 0x0005c000, 0x00060000,\n0x00064000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x00068000, 0x0006c000, 0x00070000, 0x00074000, 0x00078000,\n0x0007c000, 0x00080000, 0x00084000, 0x00088000, 0x0008c000, 0x00090000,\n0x00094000, 0x00098000, 0x0009c000, 0x000a0000, 0x000a4000, 0x000a8000,\n0x000ac000, 0x000b0000, 0x000b4000, 0x000b8000, 0x000bc000, 0x000c0000,\n0x000c4000, 0x000c8000, 0x000cc000, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\n};\n\n\nconst uint32_t base64_table_dec_32bit_d3[256] = {\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x00003e00, 0xffffffff, 0xffffffff, 0xffffffff, 0x00003f00,\n0x00003400, 0x00003500, 0x00003600, 0x00003700, 0x00003800, 0x00003900,\n0x00003a00, 0x00003b00, 0x00003c00, 0x00003d00, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,\n0x00000100, 0x00000200, 0x00000300, 0x00000400, 0x00000500, 0x00000600,\n0x00000700, 0x00000800, 0x00000900, 0x00000a00, 0x00000b00, 0x00000c00,\n0x00000d00, 0x00000e00, 0x00000f00, 0x00001000, 0x00001100, 0x00001200,\n0x00001300, 0x00001400, 0x00001500, 0x00001600, 0x00001700, 0x00001800,\n0x00001900, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0x00001a00, 0x00001b00, 0x00001c00, 0x00001d00, 0x00001e00,\n0x00001f00, 0x00002000, 0x00002100, 0x00002200, 0x00002300, 0x00002400,\n0x00002500, 0x00002600, 0x00002700, 0x00002800, 0x00002900, 0x00002a00,\n0x00002b00, 0x00002c00, 0x00002d00, 0x00002e00, 0x00002f00, 0x00003000,\n0x00003100, 0x00003200, 0x00003300, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\n};\n\n\n#endif\n"
  },
  {
    "path": "3rdparty/base64/lib/tables/table_enc_12bit.h",
    "content": "#include <stdint.h>\n\nconst uint16_t base64_table_enc_12bit[] = {\n#if BASE64_LITTLE_ENDIAN\n\t0x4141U, 0x4241U, 0x4341U, 0x4441U, 0x4541U, 0x4641U, 0x4741U, 0x4841U,\n\t0x4941U, 0x4A41U, 0x4B41U, 0x4C41U, 0x4D41U, 0x4E41U, 0x4F41U, 0x5041U,\n\t0x5141U, 0x5241U, 0x5341U, 0x5441U, 0x5541U, 0x5641U, 0x5741U, 0x5841U,\n\t0x5941U, 0x5A41U, 0x6141U, 0x6241U, 0x6341U, 0x6441U, 0x6541U, 0x6641U,\n\t0x6741U, 0x6841U, 0x6941U, 0x6A41U, 0x6B41U, 0x6C41U, 0x6D41U, 0x6E41U,\n\t0x6F41U, 0x7041U, 0x7141U, 0x7241U, 0x7341U, 0x7441U, 0x7541U, 0x7641U,\n\t0x7741U, 0x7841U, 0x7941U, 0x7A41U, 0x3041U, 0x3141U, 0x3241U, 0x3341U,\n\t0x3441U, 0x3541U, 0x3641U, 0x3741U, 0x3841U, 0x3941U, 0x2B41U, 0x2F41U,\n\t0x4142U, 0x4242U, 0x4342U, 0x4442U, 0x4542U, 0x4642U, 0x4742U, 0x4842U,\n\t0x4942U, 0x4A42U, 0x4B42U, 0x4C42U, 0x4D42U, 0x4E42U, 0x4F42U, 0x5042U,\n\t0x5142U, 0x5242U, 0x5342U, 0x5442U, 0x5542U, 0x5642U, 0x5742U, 0x5842U,\n\t0x5942U, 0x5A42U, 0x6142U, 0x6242U, 0x6342U, 0x6442U, 0x6542U, 0x6642U,\n\t0x6742U, 0x6842U, 0x6942U, 0x6A42U, 0x6B42U, 0x6C42U, 0x6D42U, 0x6E42U,\n\t0x6F42U, 0x7042U, 0x7142U, 0x7242U, 0x7342U, 0x7442U, 0x7542U, 0x7642U,\n\t0x7742U, 0x7842U, 0x7942U, 0x7A42U, 0x3042U, 0x3142U, 0x3242U, 0x3342U,\n\t0x3442U, 0x3542U, 0x3642U, 0x3742U, 0x3842U, 0x3942U, 0x2B42U, 0x2F42U,\n\t0x4143U, 0x4243U, 0x4343U, 0x4443U, 0x4543U, 0x4643U, 0x4743U, 0x4843U,\n\t0x4943U, 0x4A43U, 0x4B43U, 0x4C43U, 0x4D43U, 0x4E43U, 0x4F43U, 0x5043U,\n\t0x5143U, 0x5243U, 0x5343U, 0x5443U, 0x5543U, 0x5643U, 0x5743U, 0x5843U,\n\t0x5943U, 0x5A43U, 0x6143U, 0x6243U, 0x6343U, 0x6443U, 0x6543U, 0x6643U,\n\t0x6743U, 0x6843U, 0x6943U, 0x6A43U, 0x6B43U, 0x6C43U, 0x6D43U, 0x6E43U,\n\t0x6F43U, 0x7043U, 0x7143U, 0x7243U, 0x7343U, 0x7443U, 0x7543U, 0x7643U,\n\t0x7743U, 0x7843U, 0x7943U, 0x7A43U, 0x3043U, 0x3143U, 0x3243U, 0x3343U,\n\t0x3443U, 0x3543U, 0x3643U, 0x3743U, 0x3843U, 0x3943U, 0x2B43U, 0x2F43U,\n\t0x4144U, 0x4244U, 0x4344U, 0x4444U, 0x4544U, 0x4644U, 0x4744U, 0x4844U,\n\t0x4944U, 0x4A44U, 0x4B44U, 0x4C44U, 0x4D44U, 0x4E44U, 0x4F44U, 0x5044U,\n\t0x5144U, 0x5244U, 0x5344U, 0x5444U, 0x5544U, 0x5644U, 0x5744U, 0x5844U,\n\t0x5944U, 0x5A44U, 0x6144U, 0x6244U, 0x6344U, 0x6444U, 0x6544U, 0x6644U,\n\t0x6744U, 0x6844U, 0x6944U, 0x6A44U, 0x6B44U, 0x6C44U, 0x6D44U, 0x6E44U,\n\t0x6F44U, 0x7044U, 0x7144U, 0x7244U, 0x7344U, 0x7444U, 0x7544U, 0x7644U,\n\t0x7744U, 0x7844U, 0x7944U, 0x7A44U, 0x3044U, 0x3144U, 0x3244U, 0x3344U,\n\t0x3444U, 0x3544U, 0x3644U, 0x3744U, 0x3844U, 0x3944U, 0x2B44U, 0x2F44U,\n\t0x4145U, 0x4245U, 0x4345U, 0x4445U, 0x4545U, 0x4645U, 0x4745U, 0x4845U,\n\t0x4945U, 0x4A45U, 0x4B45U, 0x4C45U, 0x4D45U, 0x4E45U, 0x4F45U, 0x5045U,\n\t0x5145U, 0x5245U, 0x5345U, 0x5445U, 0x5545U, 0x5645U, 0x5745U, 0x5845U,\n\t0x5945U, 0x5A45U, 0x6145U, 0x6245U, 0x6345U, 0x6445U, 0x6545U, 0x6645U,\n\t0x6745U, 0x6845U, 0x6945U, 0x6A45U, 0x6B45U, 0x6C45U, 0x6D45U, 0x6E45U,\n\t0x6F45U, 0x7045U, 0x7145U, 0x7245U, 0x7345U, 0x7445U, 0x7545U, 0x7645U,\n\t0x7745U, 0x7845U, 0x7945U, 0x7A45U, 0x3045U, 0x3145U, 0x3245U, 0x3345U,\n\t0x3445U, 0x3545U, 0x3645U, 0x3745U, 0x3845U, 0x3945U, 0x2B45U, 0x2F45U,\n\t0x4146U, 0x4246U, 0x4346U, 0x4446U, 0x4546U, 0x4646U, 0x4746U, 0x4846U,\n\t0x4946U, 0x4A46U, 0x4B46U, 0x4C46U, 0x4D46U, 0x4E46U, 0x4F46U, 0x5046U,\n\t0x5146U, 0x5246U, 0x5346U, 0x5446U, 0x5546U, 0x5646U, 0x5746U, 0x5846U,\n\t0x5946U, 0x5A46U, 0x6146U, 0x6246U, 0x6346U, 0x6446U, 0x6546U, 0x6646U,\n\t0x6746U, 0x6846U, 0x6946U, 0x6A46U, 0x6B46U, 0x6C46U, 0x6D46U, 0x6E46U,\n\t0x6F46U, 0x7046U, 0x7146U, 0x7246U, 0x7346U, 0x7446U, 0x7546U, 0x7646U,\n\t0x7746U, 0x7846U, 0x7946U, 0x7A46U, 0x3046U, 0x3146U, 0x3246U, 0x3346U,\n\t0x3446U, 0x3546U, 0x3646U, 0x3746U, 0x3846U, 0x3946U, 0x2B46U, 0x2F46U,\n\t0x4147U, 0x4247U, 0x4347U, 0x4447U, 0x4547U, 0x4647U, 0x4747U, 0x4847U,\n\t0x4947U, 0x4A47U, 0x4B47U, 0x4C47U, 0x4D47U, 0x4E47U, 0x4F47U, 0x5047U,\n\t0x5147U, 0x5247U, 0x5347U, 0x5447U, 0x5547U, 0x5647U, 0x5747U, 0x5847U,\n\t0x5947U, 0x5A47U, 0x6147U, 0x6247U, 0x6347U, 0x6447U, 0x6547U, 0x6647U,\n\t0x6747U, 0x6847U, 0x6947U, 0x6A47U, 0x6B47U, 0x6C47U, 0x6D47U, 0x6E47U,\n\t0x6F47U, 0x7047U, 0x7147U, 0x7247U, 0x7347U, 0x7447U, 0x7547U, 0x7647U,\n\t0x7747U, 0x7847U, 0x7947U, 0x7A47U, 0x3047U, 0x3147U, 0x3247U, 0x3347U,\n\t0x3447U, 0x3547U, 0x3647U, 0x3747U, 0x3847U, 0x3947U, 0x2B47U, 0x2F47U,\n\t0x4148U, 0x4248U, 0x4348U, 0x4448U, 0x4548U, 0x4648U, 0x4748U, 0x4848U,\n\t0x4948U, 0x4A48U, 0x4B48U, 0x4C48U, 0x4D48U, 0x4E48U, 0x4F48U, 0x5048U,\n\t0x5148U, 0x5248U, 0x5348U, 0x5448U, 0x5548U, 0x5648U, 0x5748U, 0x5848U,\n\t0x5948U, 0x5A48U, 0x6148U, 0x6248U, 0x6348U, 0x6448U, 0x6548U, 0x6648U,\n\t0x6748U, 0x6848U, 0x6948U, 0x6A48U, 0x6B48U, 0x6C48U, 0x6D48U, 0x6E48U,\n\t0x6F48U, 0x7048U, 0x7148U, 0x7248U, 0x7348U, 0x7448U, 0x7548U, 0x7648U,\n\t0x7748U, 0x7848U, 0x7948U, 0x7A48U, 0x3048U, 0x3148U, 0x3248U, 0x3348U,\n\t0x3448U, 0x3548U, 0x3648U, 0x3748U, 0x3848U, 0x3948U, 0x2B48U, 0x2F48U,\n\t0x4149U, 0x4249U, 0x4349U, 0x4449U, 0x4549U, 0x4649U, 0x4749U, 0x4849U,\n\t0x4949U, 0x4A49U, 0x4B49U, 0x4C49U, 0x4D49U, 0x4E49U, 0x4F49U, 0x5049U,\n\t0x5149U, 0x5249U, 0x5349U, 0x5449U, 0x5549U, 0x5649U, 0x5749U, 0x5849U,\n\t0x5949U, 0x5A49U, 0x6149U, 0x6249U, 0x6349U, 0x6449U, 0x6549U, 0x6649U,\n\t0x6749U, 0x6849U, 0x6949U, 0x6A49U, 0x6B49U, 0x6C49U, 0x6D49U, 0x6E49U,\n\t0x6F49U, 0x7049U, 0x7149U, 0x7249U, 0x7349U, 0x7449U, 0x7549U, 0x7649U,\n\t0x7749U, 0x7849U, 0x7949U, 0x7A49U, 0x3049U, 0x3149U, 0x3249U, 0x3349U,\n\t0x3449U, 0x3549U, 0x3649U, 0x3749U, 0x3849U, 0x3949U, 0x2B49U, 0x2F49U,\n\t0x414AU, 0x424AU, 0x434AU, 0x444AU, 0x454AU, 0x464AU, 0x474AU, 0x484AU,\n\t0x494AU, 0x4A4AU, 0x4B4AU, 0x4C4AU, 0x4D4AU, 0x4E4AU, 0x4F4AU, 0x504AU,\n\t0x514AU, 0x524AU, 0x534AU, 0x544AU, 0x554AU, 0x564AU, 0x574AU, 0x584AU,\n\t0x594AU, 0x5A4AU, 0x614AU, 0x624AU, 0x634AU, 0x644AU, 0x654AU, 0x664AU,\n\t0x674AU, 0x684AU, 0x694AU, 0x6A4AU, 0x6B4AU, 0x6C4AU, 0x6D4AU, 0x6E4AU,\n\t0x6F4AU, 0x704AU, 0x714AU, 0x724AU, 0x734AU, 0x744AU, 0x754AU, 0x764AU,\n\t0x774AU, 0x784AU, 0x794AU, 0x7A4AU, 0x304AU, 0x314AU, 0x324AU, 0x334AU,\n\t0x344AU, 0x354AU, 0x364AU, 0x374AU, 0x384AU, 0x394AU, 0x2B4AU, 0x2F4AU,\n\t0x414BU, 0x424BU, 0x434BU, 0x444BU, 0x454BU, 0x464BU, 0x474BU, 0x484BU,\n\t0x494BU, 0x4A4BU, 0x4B4BU, 0x4C4BU, 0x4D4BU, 0x4E4BU, 0x4F4BU, 0x504BU,\n\t0x514BU, 0x524BU, 0x534BU, 0x544BU, 0x554BU, 0x564BU, 0x574BU, 0x584BU,\n\t0x594BU, 0x5A4BU, 0x614BU, 0x624BU, 0x634BU, 0x644BU, 0x654BU, 0x664BU,\n\t0x674BU, 0x684BU, 0x694BU, 0x6A4BU, 0x6B4BU, 0x6C4BU, 0x6D4BU, 0x6E4BU,\n\t0x6F4BU, 0x704BU, 0x714BU, 0x724BU, 0x734BU, 0x744BU, 0x754BU, 0x764BU,\n\t0x774BU, 0x784BU, 0x794BU, 0x7A4BU, 0x304BU, 0x314BU, 0x324BU, 0x334BU,\n\t0x344BU, 0x354BU, 0x364BU, 0x374BU, 0x384BU, 0x394BU, 0x2B4BU, 0x2F4BU,\n\t0x414CU, 0x424CU, 0x434CU, 0x444CU, 0x454CU, 0x464CU, 0x474CU, 0x484CU,\n\t0x494CU, 0x4A4CU, 0x4B4CU, 0x4C4CU, 0x4D4CU, 0x4E4CU, 0x4F4CU, 0x504CU,\n\t0x514CU, 0x524CU, 0x534CU, 0x544CU, 0x554CU, 0x564CU, 0x574CU, 0x584CU,\n\t0x594CU, 0x5A4CU, 0x614CU, 0x624CU, 0x634CU, 0x644CU, 0x654CU, 0x664CU,\n\t0x674CU, 0x684CU, 0x694CU, 0x6A4CU, 0x6B4CU, 0x6C4CU, 0x6D4CU, 0x6E4CU,\n\t0x6F4CU, 0x704CU, 0x714CU, 0x724CU, 0x734CU, 0x744CU, 0x754CU, 0x764CU,\n\t0x774CU, 0x784CU, 0x794CU, 0x7A4CU, 0x304CU, 0x314CU, 0x324CU, 0x334CU,\n\t0x344CU, 0x354CU, 0x364CU, 0x374CU, 0x384CU, 0x394CU, 0x2B4CU, 0x2F4CU,\n\t0x414DU, 0x424DU, 0x434DU, 0x444DU, 0x454DU, 0x464DU, 0x474DU, 0x484DU,\n\t0x494DU, 0x4A4DU, 0x4B4DU, 0x4C4DU, 0x4D4DU, 0x4E4DU, 0x4F4DU, 0x504DU,\n\t0x514DU, 0x524DU, 0x534DU, 0x544DU, 0x554DU, 0x564DU, 0x574DU, 0x584DU,\n\t0x594DU, 0x5A4DU, 0x614DU, 0x624DU, 0x634DU, 0x644DU, 0x654DU, 0x664DU,\n\t0x674DU, 0x684DU, 0x694DU, 0x6A4DU, 0x6B4DU, 0x6C4DU, 0x6D4DU, 0x6E4DU,\n\t0x6F4DU, 0x704DU, 0x714DU, 0x724DU, 0x734DU, 0x744DU, 0x754DU, 0x764DU,\n\t0x774DU, 0x784DU, 0x794DU, 0x7A4DU, 0x304DU, 0x314DU, 0x324DU, 0x334DU,\n\t0x344DU, 0x354DU, 0x364DU, 0x374DU, 0x384DU, 0x394DU, 0x2B4DU, 0x2F4DU,\n\t0x414EU, 0x424EU, 0x434EU, 0x444EU, 0x454EU, 0x464EU, 0x474EU, 0x484EU,\n\t0x494EU, 0x4A4EU, 0x4B4EU, 0x4C4EU, 0x4D4EU, 0x4E4EU, 0x4F4EU, 0x504EU,\n\t0x514EU, 0x524EU, 0x534EU, 0x544EU, 0x554EU, 0x564EU, 0x574EU, 0x584EU,\n\t0x594EU, 0x5A4EU, 0x614EU, 0x624EU, 0x634EU, 0x644EU, 0x654EU, 0x664EU,\n\t0x674EU, 0x684EU, 0x694EU, 0x6A4EU, 0x6B4EU, 0x6C4EU, 0x6D4EU, 0x6E4EU,\n\t0x6F4EU, 0x704EU, 0x714EU, 0x724EU, 0x734EU, 0x744EU, 0x754EU, 0x764EU,\n\t0x774EU, 0x784EU, 0x794EU, 0x7A4EU, 0x304EU, 0x314EU, 0x324EU, 0x334EU,\n\t0x344EU, 0x354EU, 0x364EU, 0x374EU, 0x384EU, 0x394EU, 0x2B4EU, 0x2F4EU,\n\t0x414FU, 0x424FU, 0x434FU, 0x444FU, 0x454FU, 0x464FU, 0x474FU, 0x484FU,\n\t0x494FU, 0x4A4FU, 0x4B4FU, 0x4C4FU, 0x4D4FU, 0x4E4FU, 0x4F4FU, 0x504FU,\n\t0x514FU, 0x524FU, 0x534FU, 0x544FU, 0x554FU, 0x564FU, 0x574FU, 0x584FU,\n\t0x594FU, 0x5A4FU, 0x614FU, 0x624FU, 0x634FU, 0x644FU, 0x654FU, 0x664FU,\n\t0x674FU, 0x684FU, 0x694FU, 0x6A4FU, 0x6B4FU, 0x6C4FU, 0x6D4FU, 0x6E4FU,\n\t0x6F4FU, 0x704FU, 0x714FU, 0x724FU, 0x734FU, 0x744FU, 0x754FU, 0x764FU,\n\t0x774FU, 0x784FU, 0x794FU, 0x7A4FU, 0x304FU, 0x314FU, 0x324FU, 0x334FU,\n\t0x344FU, 0x354FU, 0x364FU, 0x374FU, 0x384FU, 0x394FU, 0x2B4FU, 0x2F4FU,\n\t0x4150U, 0x4250U, 0x4350U, 0x4450U, 0x4550U, 0x4650U, 0x4750U, 0x4850U,\n\t0x4950U, 0x4A50U, 0x4B50U, 0x4C50U, 0x4D50U, 0x4E50U, 0x4F50U, 0x5050U,\n\t0x5150U, 0x5250U, 0x5350U, 0x5450U, 0x5550U, 0x5650U, 0x5750U, 0x5850U,\n\t0x5950U, 0x5A50U, 0x6150U, 0x6250U, 0x6350U, 0x6450U, 0x6550U, 0x6650U,\n\t0x6750U, 0x6850U, 0x6950U, 0x6A50U, 0x6B50U, 0x6C50U, 0x6D50U, 0x6E50U,\n\t0x6F50U, 0x7050U, 0x7150U, 0x7250U, 0x7350U, 0x7450U, 0x7550U, 0x7650U,\n\t0x7750U, 0x7850U, 0x7950U, 0x7A50U, 0x3050U, 0x3150U, 0x3250U, 0x3350U,\n\t0x3450U, 0x3550U, 0x3650U, 0x3750U, 0x3850U, 0x3950U, 0x2B50U, 0x2F50U,\n\t0x4151U, 0x4251U, 0x4351U, 0x4451U, 0x4551U, 0x4651U, 0x4751U, 0x4851U,\n\t0x4951U, 0x4A51U, 0x4B51U, 0x4C51U, 0x4D51U, 0x4E51U, 0x4F51U, 0x5051U,\n\t0x5151U, 0x5251U, 0x5351U, 0x5451U, 0x5551U, 0x5651U, 0x5751U, 0x5851U,\n\t0x5951U, 0x5A51U, 0x6151U, 0x6251U, 0x6351U, 0x6451U, 0x6551U, 0x6651U,\n\t0x6751U, 0x6851U, 0x6951U, 0x6A51U, 0x6B51U, 0x6C51U, 0x6D51U, 0x6E51U,\n\t0x6F51U, 0x7051U, 0x7151U, 0x7251U, 0x7351U, 0x7451U, 0x7551U, 0x7651U,\n\t0x7751U, 0x7851U, 0x7951U, 0x7A51U, 0x3051U, 0x3151U, 0x3251U, 0x3351U,\n\t0x3451U, 0x3551U, 0x3651U, 0x3751U, 0x3851U, 0x3951U, 0x2B51U, 0x2F51U,\n\t0x4152U, 0x4252U, 0x4352U, 0x4452U, 0x4552U, 0x4652U, 0x4752U, 0x4852U,\n\t0x4952U, 0x4A52U, 0x4B52U, 0x4C52U, 0x4D52U, 0x4E52U, 0x4F52U, 0x5052U,\n\t0x5152U, 0x5252U, 0x5352U, 0x5452U, 0x5552U, 0x5652U, 0x5752U, 0x5852U,\n\t0x5952U, 0x5A52U, 0x6152U, 0x6252U, 0x6352U, 0x6452U, 0x6552U, 0x6652U,\n\t0x6752U, 0x6852U, 0x6952U, 0x6A52U, 0x6B52U, 0x6C52U, 0x6D52U, 0x6E52U,\n\t0x6F52U, 0x7052U, 0x7152U, 0x7252U, 0x7352U, 0x7452U, 0x7552U, 0x7652U,\n\t0x7752U, 0x7852U, 0x7952U, 0x7A52U, 0x3052U, 0x3152U, 0x3252U, 0x3352U,\n\t0x3452U, 0x3552U, 0x3652U, 0x3752U, 0x3852U, 0x3952U, 0x2B52U, 0x2F52U,\n\t0x4153U, 0x4253U, 0x4353U, 0x4453U, 0x4553U, 0x4653U, 0x4753U, 0x4853U,\n\t0x4953U, 0x4A53U, 0x4B53U, 0x4C53U, 0x4D53U, 0x4E53U, 0x4F53U, 0x5053U,\n\t0x5153U, 0x5253U, 0x5353U, 0x5453U, 0x5553U, 0x5653U, 0x5753U, 0x5853U,\n\t0x5953U, 0x5A53U, 0x6153U, 0x6253U, 0x6353U, 0x6453U, 0x6553U, 0x6653U,\n\t0x6753U, 0x6853U, 0x6953U, 0x6A53U, 0x6B53U, 0x6C53U, 0x6D53U, 0x6E53U,\n\t0x6F53U, 0x7053U, 0x7153U, 0x7253U, 0x7353U, 0x7453U, 0x7553U, 0x7653U,\n\t0x7753U, 0x7853U, 0x7953U, 0x7A53U, 0x3053U, 0x3153U, 0x3253U, 0x3353U,\n\t0x3453U, 0x3553U, 0x3653U, 0x3753U, 0x3853U, 0x3953U, 0x2B53U, 0x2F53U,\n\t0x4154U, 0x4254U, 0x4354U, 0x4454U, 0x4554U, 0x4654U, 0x4754U, 0x4854U,\n\t0x4954U, 0x4A54U, 0x4B54U, 0x4C54U, 0x4D54U, 0x4E54U, 0x4F54U, 0x5054U,\n\t0x5154U, 0x5254U, 0x5354U, 0x5454U, 0x5554U, 0x5654U, 0x5754U, 0x5854U,\n\t0x5954U, 0x5A54U, 0x6154U, 0x6254U, 0x6354U, 0x6454U, 0x6554U, 0x6654U,\n\t0x6754U, 0x6854U, 0x6954U, 0x6A54U, 0x6B54U, 0x6C54U, 0x6D54U, 0x6E54U,\n\t0x6F54U, 0x7054U, 0x7154U, 0x7254U, 0x7354U, 0x7454U, 0x7554U, 0x7654U,\n\t0x7754U, 0x7854U, 0x7954U, 0x7A54U, 0x3054U, 0x3154U, 0x3254U, 0x3354U,\n\t0x3454U, 0x3554U, 0x3654U, 0x3754U, 0x3854U, 0x3954U, 0x2B54U, 0x2F54U,\n\t0x4155U, 0x4255U, 0x4355U, 0x4455U, 0x4555U, 0x4655U, 0x4755U, 0x4855U,\n\t0x4955U, 0x4A55U, 0x4B55U, 0x4C55U, 0x4D55U, 0x4E55U, 0x4F55U, 0x5055U,\n\t0x5155U, 0x5255U, 0x5355U, 0x5455U, 0x5555U, 0x5655U, 0x5755U, 0x5855U,\n\t0x5955U, 0x5A55U, 0x6155U, 0x6255U, 0x6355U, 0x6455U, 0x6555U, 0x6655U,\n\t0x6755U, 0x6855U, 0x6955U, 0x6A55U, 0x6B55U, 0x6C55U, 0x6D55U, 0x6E55U,\n\t0x6F55U, 0x7055U, 0x7155U, 0x7255U, 0x7355U, 0x7455U, 0x7555U, 0x7655U,\n\t0x7755U, 0x7855U, 0x7955U, 0x7A55U, 0x3055U, 0x3155U, 0x3255U, 0x3355U,\n\t0x3455U, 0x3555U, 0x3655U, 0x3755U, 0x3855U, 0x3955U, 0x2B55U, 0x2F55U,\n\t0x4156U, 0x4256U, 0x4356U, 0x4456U, 0x4556U, 0x4656U, 0x4756U, 0x4856U,\n\t0x4956U, 0x4A56U, 0x4B56U, 0x4C56U, 0x4D56U, 0x4E56U, 0x4F56U, 0x5056U,\n\t0x5156U, 0x5256U, 0x5356U, 0x5456U, 0x5556U, 0x5656U, 0x5756U, 0x5856U,\n\t0x5956U, 0x5A56U, 0x6156U, 0x6256U, 0x6356U, 0x6456U, 0x6556U, 0x6656U,\n\t0x6756U, 0x6856U, 0x6956U, 0x6A56U, 0x6B56U, 0x6C56U, 0x6D56U, 0x6E56U,\n\t0x6F56U, 0x7056U, 0x7156U, 0x7256U, 0x7356U, 0x7456U, 0x7556U, 0x7656U,\n\t0x7756U, 0x7856U, 0x7956U, 0x7A56U, 0x3056U, 0x3156U, 0x3256U, 0x3356U,\n\t0x3456U, 0x3556U, 0x3656U, 0x3756U, 0x3856U, 0x3956U, 0x2B56U, 0x2F56U,\n\t0x4157U, 0x4257U, 0x4357U, 0x4457U, 0x4557U, 0x4657U, 0x4757U, 0x4857U,\n\t0x4957U, 0x4A57U, 0x4B57U, 0x4C57U, 0x4D57U, 0x4E57U, 0x4F57U, 0x5057U,\n\t0x5157U, 0x5257U, 0x5357U, 0x5457U, 0x5557U, 0x5657U, 0x5757U, 0x5857U,\n\t0x5957U, 0x5A57U, 0x6157U, 0x6257U, 0x6357U, 0x6457U, 0x6557U, 0x6657U,\n\t0x6757U, 0x6857U, 0x6957U, 0x6A57U, 0x6B57U, 0x6C57U, 0x6D57U, 0x6E57U,\n\t0x6F57U, 0x7057U, 0x7157U, 0x7257U, 0x7357U, 0x7457U, 0x7557U, 0x7657U,\n\t0x7757U, 0x7857U, 0x7957U, 0x7A57U, 0x3057U, 0x3157U, 0x3257U, 0x3357U,\n\t0x3457U, 0x3557U, 0x3657U, 0x3757U, 0x3857U, 0x3957U, 0x2B57U, 0x2F57U,\n\t0x4158U, 0x4258U, 0x4358U, 0x4458U, 0x4558U, 0x4658U, 0x4758U, 0x4858U,\n\t0x4958U, 0x4A58U, 0x4B58U, 0x4C58U, 0x4D58U, 0x4E58U, 0x4F58U, 0x5058U,\n\t0x5158U, 0x5258U, 0x5358U, 0x5458U, 0x5558U, 0x5658U, 0x5758U, 0x5858U,\n\t0x5958U, 0x5A58U, 0x6158U, 0x6258U, 0x6358U, 0x6458U, 0x6558U, 0x6658U,\n\t0x6758U, 0x6858U, 0x6958U, 0x6A58U, 0x6B58U, 0x6C58U, 0x6D58U, 0x6E58U,\n\t0x6F58U, 0x7058U, 0x7158U, 0x7258U, 0x7358U, 0x7458U, 0x7558U, 0x7658U,\n\t0x7758U, 0x7858U, 0x7958U, 0x7A58U, 0x3058U, 0x3158U, 0x3258U, 0x3358U,\n\t0x3458U, 0x3558U, 0x3658U, 0x3758U, 0x3858U, 0x3958U, 0x2B58U, 0x2F58U,\n\t0x4159U, 0x4259U, 0x4359U, 0x4459U, 0x4559U, 0x4659U, 0x4759U, 0x4859U,\n\t0x4959U, 0x4A59U, 0x4B59U, 0x4C59U, 0x4D59U, 0x4E59U, 0x4F59U, 0x5059U,\n\t0x5159U, 0x5259U, 0x5359U, 0x5459U, 0x5559U, 0x5659U, 0x5759U, 0x5859U,\n\t0x5959U, 0x5A59U, 0x6159U, 0x6259U, 0x6359U, 0x6459U, 0x6559U, 0x6659U,\n\t0x6759U, 0x6859U, 0x6959U, 0x6A59U, 0x6B59U, 0x6C59U, 0x6D59U, 0x6E59U,\n\t0x6F59U, 0x7059U, 0x7159U, 0x7259U, 0x7359U, 0x7459U, 0x7559U, 0x7659U,\n\t0x7759U, 0x7859U, 0x7959U, 0x7A59U, 0x3059U, 0x3159U, 0x3259U, 0x3359U,\n\t0x3459U, 0x3559U, 0x3659U, 0x3759U, 0x3859U, 0x3959U, 0x2B59U, 0x2F59U,\n\t0x415AU, 0x425AU, 0x435AU, 0x445AU, 0x455AU, 0x465AU, 0x475AU, 0x485AU,\n\t0x495AU, 0x4A5AU, 0x4B5AU, 0x4C5AU, 0x4D5AU, 0x4E5AU, 0x4F5AU, 0x505AU,\n\t0x515AU, 0x525AU, 0x535AU, 0x545AU, 0x555AU, 0x565AU, 0x575AU, 0x585AU,\n\t0x595AU, 0x5A5AU, 0x615AU, 0x625AU, 0x635AU, 0x645AU, 0x655AU, 0x665AU,\n\t0x675AU, 0x685AU, 0x695AU, 0x6A5AU, 0x6B5AU, 0x6C5AU, 0x6D5AU, 0x6E5AU,\n\t0x6F5AU, 0x705AU, 0x715AU, 0x725AU, 0x735AU, 0x745AU, 0x755AU, 0x765AU,\n\t0x775AU, 0x785AU, 0x795AU, 0x7A5AU, 0x305AU, 0x315AU, 0x325AU, 0x335AU,\n\t0x345AU, 0x355AU, 0x365AU, 0x375AU, 0x385AU, 0x395AU, 0x2B5AU, 0x2F5AU,\n\t0x4161U, 0x4261U, 0x4361U, 0x4461U, 0x4561U, 0x4661U, 0x4761U, 0x4861U,\n\t0x4961U, 0x4A61U, 0x4B61U, 0x4C61U, 0x4D61U, 0x4E61U, 0x4F61U, 0x5061U,\n\t0x5161U, 0x5261U, 0x5361U, 0x5461U, 0x5561U, 0x5661U, 0x5761U, 0x5861U,\n\t0x5961U, 0x5A61U, 0x6161U, 0x6261U, 0x6361U, 0x6461U, 0x6561U, 0x6661U,\n\t0x6761U, 0x6861U, 0x6961U, 0x6A61U, 0x6B61U, 0x6C61U, 0x6D61U, 0x6E61U,\n\t0x6F61U, 0x7061U, 0x7161U, 0x7261U, 0x7361U, 0x7461U, 0x7561U, 0x7661U,\n\t0x7761U, 0x7861U, 0x7961U, 0x7A61U, 0x3061U, 0x3161U, 0x3261U, 0x3361U,\n\t0x3461U, 0x3561U, 0x3661U, 0x3761U, 0x3861U, 0x3961U, 0x2B61U, 0x2F61U,\n\t0x4162U, 0x4262U, 0x4362U, 0x4462U, 0x4562U, 0x4662U, 0x4762U, 0x4862U,\n\t0x4962U, 0x4A62U, 0x4B62U, 0x4C62U, 0x4D62U, 0x4E62U, 0x4F62U, 0x5062U,\n\t0x5162U, 0x5262U, 0x5362U, 0x5462U, 0x5562U, 0x5662U, 0x5762U, 0x5862U,\n\t0x5962U, 0x5A62U, 0x6162U, 0x6262U, 0x6362U, 0x6462U, 0x6562U, 0x6662U,\n\t0x6762U, 0x6862U, 0x6962U, 0x6A62U, 0x6B62U, 0x6C62U, 0x6D62U, 0x6E62U,\n\t0x6F62U, 0x7062U, 0x7162U, 0x7262U, 0x7362U, 0x7462U, 0x7562U, 0x7662U,\n\t0x7762U, 0x7862U, 0x7962U, 0x7A62U, 0x3062U, 0x3162U, 0x3262U, 0x3362U,\n\t0x3462U, 0x3562U, 0x3662U, 0x3762U, 0x3862U, 0x3962U, 0x2B62U, 0x2F62U,\n\t0x4163U, 0x4263U, 0x4363U, 0x4463U, 0x4563U, 0x4663U, 0x4763U, 0x4863U,\n\t0x4963U, 0x4A63U, 0x4B63U, 0x4C63U, 0x4D63U, 0x4E63U, 0x4F63U, 0x5063U,\n\t0x5163U, 0x5263U, 0x5363U, 0x5463U, 0x5563U, 0x5663U, 0x5763U, 0x5863U,\n\t0x5963U, 0x5A63U, 0x6163U, 0x6263U, 0x6363U, 0x6463U, 0x6563U, 0x6663U,\n\t0x6763U, 0x6863U, 0x6963U, 0x6A63U, 0x6B63U, 0x6C63U, 0x6D63U, 0x6E63U,\n\t0x6F63U, 0x7063U, 0x7163U, 0x7263U, 0x7363U, 0x7463U, 0x7563U, 0x7663U,\n\t0x7763U, 0x7863U, 0x7963U, 0x7A63U, 0x3063U, 0x3163U, 0x3263U, 0x3363U,\n\t0x3463U, 0x3563U, 0x3663U, 0x3763U, 0x3863U, 0x3963U, 0x2B63U, 0x2F63U,\n\t0x4164U, 0x4264U, 0x4364U, 0x4464U, 0x4564U, 0x4664U, 0x4764U, 0x4864U,\n\t0x4964U, 0x4A64U, 0x4B64U, 0x4C64U, 0x4D64U, 0x4E64U, 0x4F64U, 0x5064U,\n\t0x5164U, 0x5264U, 0x5364U, 0x5464U, 0x5564U, 0x5664U, 0x5764U, 0x5864U,\n\t0x5964U, 0x5A64U, 0x6164U, 0x6264U, 0x6364U, 0x6464U, 0x6564U, 0x6664U,\n\t0x6764U, 0x6864U, 0x6964U, 0x6A64U, 0x6B64U, 0x6C64U, 0x6D64U, 0x6E64U,\n\t0x6F64U, 0x7064U, 0x7164U, 0x7264U, 0x7364U, 0x7464U, 0x7564U, 0x7664U,\n\t0x7764U, 0x7864U, 0x7964U, 0x7A64U, 0x3064U, 0x3164U, 0x3264U, 0x3364U,\n\t0x3464U, 0x3564U, 0x3664U, 0x3764U, 0x3864U, 0x3964U, 0x2B64U, 0x2F64U,\n\t0x4165U, 0x4265U, 0x4365U, 0x4465U, 0x4565U, 0x4665U, 0x4765U, 0x4865U,\n\t0x4965U, 0x4A65U, 0x4B65U, 0x4C65U, 0x4D65U, 0x4E65U, 0x4F65U, 0x5065U,\n\t0x5165U, 0x5265U, 0x5365U, 0x5465U, 0x5565U, 0x5665U, 0x5765U, 0x5865U,\n\t0x5965U, 0x5A65U, 0x6165U, 0x6265U, 0x6365U, 0x6465U, 0x6565U, 0x6665U,\n\t0x6765U, 0x6865U, 0x6965U, 0x6A65U, 0x6B65U, 0x6C65U, 0x6D65U, 0x6E65U,\n\t0x6F65U, 0x7065U, 0x7165U, 0x7265U, 0x7365U, 0x7465U, 0x7565U, 0x7665U,\n\t0x7765U, 0x7865U, 0x7965U, 0x7A65U, 0x3065U, 0x3165U, 0x3265U, 0x3365U,\n\t0x3465U, 0x3565U, 0x3665U, 0x3765U, 0x3865U, 0x3965U, 0x2B65U, 0x2F65U,\n\t0x4166U, 0x4266U, 0x4366U, 0x4466U, 0x4566U, 0x4666U, 0x4766U, 0x4866U,\n\t0x4966U, 0x4A66U, 0x4B66U, 0x4C66U, 0x4D66U, 0x4E66U, 0x4F66U, 0x5066U,\n\t0x5166U, 0x5266U, 0x5366U, 0x5466U, 0x5566U, 0x5666U, 0x5766U, 0x5866U,\n\t0x5966U, 0x5A66U, 0x6166U, 0x6266U, 0x6366U, 0x6466U, 0x6566U, 0x6666U,\n\t0x6766U, 0x6866U, 0x6966U, 0x6A66U, 0x6B66U, 0x6C66U, 0x6D66U, 0x6E66U,\n\t0x6F66U, 0x7066U, 0x7166U, 0x7266U, 0x7366U, 0x7466U, 0x7566U, 0x7666U,\n\t0x7766U, 0x7866U, 0x7966U, 0x7A66U, 0x3066U, 0x3166U, 0x3266U, 0x3366U,\n\t0x3466U, 0x3566U, 0x3666U, 0x3766U, 0x3866U, 0x3966U, 0x2B66U, 0x2F66U,\n\t0x4167U, 0x4267U, 0x4367U, 0x4467U, 0x4567U, 0x4667U, 0x4767U, 0x4867U,\n\t0x4967U, 0x4A67U, 0x4B67U, 0x4C67U, 0x4D67U, 0x4E67U, 0x4F67U, 0x5067U,\n\t0x5167U, 0x5267U, 0x5367U, 0x5467U, 0x5567U, 0x5667U, 0x5767U, 0x5867U,\n\t0x5967U, 0x5A67U, 0x6167U, 0x6267U, 0x6367U, 0x6467U, 0x6567U, 0x6667U,\n\t0x6767U, 0x6867U, 0x6967U, 0x6A67U, 0x6B67U, 0x6C67U, 0x6D67U, 0x6E67U,\n\t0x6F67U, 0x7067U, 0x7167U, 0x7267U, 0x7367U, 0x7467U, 0x7567U, 0x7667U,\n\t0x7767U, 0x7867U, 0x7967U, 0x7A67U, 0x3067U, 0x3167U, 0x3267U, 0x3367U,\n\t0x3467U, 0x3567U, 0x3667U, 0x3767U, 0x3867U, 0x3967U, 0x2B67U, 0x2F67U,\n\t0x4168U, 0x4268U, 0x4368U, 0x4468U, 0x4568U, 0x4668U, 0x4768U, 0x4868U,\n\t0x4968U, 0x4A68U, 0x4B68U, 0x4C68U, 0x4D68U, 0x4E68U, 0x4F68U, 0x5068U,\n\t0x5168U, 0x5268U, 0x5368U, 0x5468U, 0x5568U, 0x5668U, 0x5768U, 0x5868U,\n\t0x5968U, 0x5A68U, 0x6168U, 0x6268U, 0x6368U, 0x6468U, 0x6568U, 0x6668U,\n\t0x6768U, 0x6868U, 0x6968U, 0x6A68U, 0x6B68U, 0x6C68U, 0x6D68U, 0x6E68U,\n\t0x6F68U, 0x7068U, 0x7168U, 0x7268U, 0x7368U, 0x7468U, 0x7568U, 0x7668U,\n\t0x7768U, 0x7868U, 0x7968U, 0x7A68U, 0x3068U, 0x3168U, 0x3268U, 0x3368U,\n\t0x3468U, 0x3568U, 0x3668U, 0x3768U, 0x3868U, 0x3968U, 0x2B68U, 0x2F68U,\n\t0x4169U, 0x4269U, 0x4369U, 0x4469U, 0x4569U, 0x4669U, 0x4769U, 0x4869U,\n\t0x4969U, 0x4A69U, 0x4B69U, 0x4C69U, 0x4D69U, 0x4E69U, 0x4F69U, 0x5069U,\n\t0x5169U, 0x5269U, 0x5369U, 0x5469U, 0x5569U, 0x5669U, 0x5769U, 0x5869U,\n\t0x5969U, 0x5A69U, 0x6169U, 0x6269U, 0x6369U, 0x6469U, 0x6569U, 0x6669U,\n\t0x6769U, 0x6869U, 0x6969U, 0x6A69U, 0x6B69U, 0x6C69U, 0x6D69U, 0x6E69U,\n\t0x6F69U, 0x7069U, 0x7169U, 0x7269U, 0x7369U, 0x7469U, 0x7569U, 0x7669U,\n\t0x7769U, 0x7869U, 0x7969U, 0x7A69U, 0x3069U, 0x3169U, 0x3269U, 0x3369U,\n\t0x3469U, 0x3569U, 0x3669U, 0x3769U, 0x3869U, 0x3969U, 0x2B69U, 0x2F69U,\n\t0x416AU, 0x426AU, 0x436AU, 0x446AU, 0x456AU, 0x466AU, 0x476AU, 0x486AU,\n\t0x496AU, 0x4A6AU, 0x4B6AU, 0x4C6AU, 0x4D6AU, 0x4E6AU, 0x4F6AU, 0x506AU,\n\t0x516AU, 0x526AU, 0x536AU, 0x546AU, 0x556AU, 0x566AU, 0x576AU, 0x586AU,\n\t0x596AU, 0x5A6AU, 0x616AU, 0x626AU, 0x636AU, 0x646AU, 0x656AU, 0x666AU,\n\t0x676AU, 0x686AU, 0x696AU, 0x6A6AU, 0x6B6AU, 0x6C6AU, 0x6D6AU, 0x6E6AU,\n\t0x6F6AU, 0x706AU, 0x716AU, 0x726AU, 0x736AU, 0x746AU, 0x756AU, 0x766AU,\n\t0x776AU, 0x786AU, 0x796AU, 0x7A6AU, 0x306AU, 0x316AU, 0x326AU, 0x336AU,\n\t0x346AU, 0x356AU, 0x366AU, 0x376AU, 0x386AU, 0x396AU, 0x2B6AU, 0x2F6AU,\n\t0x416BU, 0x426BU, 0x436BU, 0x446BU, 0x456BU, 0x466BU, 0x476BU, 0x486BU,\n\t0x496BU, 0x4A6BU, 0x4B6BU, 0x4C6BU, 0x4D6BU, 0x4E6BU, 0x4F6BU, 0x506BU,\n\t0x516BU, 0x526BU, 0x536BU, 0x546BU, 0x556BU, 0x566BU, 0x576BU, 0x586BU,\n\t0x596BU, 0x5A6BU, 0x616BU, 0x626BU, 0x636BU, 0x646BU, 0x656BU, 0x666BU,\n\t0x676BU, 0x686BU, 0x696BU, 0x6A6BU, 0x6B6BU, 0x6C6BU, 0x6D6BU, 0x6E6BU,\n\t0x6F6BU, 0x706BU, 0x716BU, 0x726BU, 0x736BU, 0x746BU, 0x756BU, 0x766BU,\n\t0x776BU, 0x786BU, 0x796BU, 0x7A6BU, 0x306BU, 0x316BU, 0x326BU, 0x336BU,\n\t0x346BU, 0x356BU, 0x366BU, 0x376BU, 0x386BU, 0x396BU, 0x2B6BU, 0x2F6BU,\n\t0x416CU, 0x426CU, 0x436CU, 0x446CU, 0x456CU, 0x466CU, 0x476CU, 0x486CU,\n\t0x496CU, 0x4A6CU, 0x4B6CU, 0x4C6CU, 0x4D6CU, 0x4E6CU, 0x4F6CU, 0x506CU,\n\t0x516CU, 0x526CU, 0x536CU, 0x546CU, 0x556CU, 0x566CU, 0x576CU, 0x586CU,\n\t0x596CU, 0x5A6CU, 0x616CU, 0x626CU, 0x636CU, 0x646CU, 0x656CU, 0x666CU,\n\t0x676CU, 0x686CU, 0x696CU, 0x6A6CU, 0x6B6CU, 0x6C6CU, 0x6D6CU, 0x6E6CU,\n\t0x6F6CU, 0x706CU, 0x716CU, 0x726CU, 0x736CU, 0x746CU, 0x756CU, 0x766CU,\n\t0x776CU, 0x786CU, 0x796CU, 0x7A6CU, 0x306CU, 0x316CU, 0x326CU, 0x336CU,\n\t0x346CU, 0x356CU, 0x366CU, 0x376CU, 0x386CU, 0x396CU, 0x2B6CU, 0x2F6CU,\n\t0x416DU, 0x426DU, 0x436DU, 0x446DU, 0x456DU, 0x466DU, 0x476DU, 0x486DU,\n\t0x496DU, 0x4A6DU, 0x4B6DU, 0x4C6DU, 0x4D6DU, 0x4E6DU, 0x4F6DU, 0x506DU,\n\t0x516DU, 0x526DU, 0x536DU, 0x546DU, 0x556DU, 0x566DU, 0x576DU, 0x586DU,\n\t0x596DU, 0x5A6DU, 0x616DU, 0x626DU, 0x636DU, 0x646DU, 0x656DU, 0x666DU,\n\t0x676DU, 0x686DU, 0x696DU, 0x6A6DU, 0x6B6DU, 0x6C6DU, 0x6D6DU, 0x6E6DU,\n\t0x6F6DU, 0x706DU, 0x716DU, 0x726DU, 0x736DU, 0x746DU, 0x756DU, 0x766DU,\n\t0x776DU, 0x786DU, 0x796DU, 0x7A6DU, 0x306DU, 0x316DU, 0x326DU, 0x336DU,\n\t0x346DU, 0x356DU, 0x366DU, 0x376DU, 0x386DU, 0x396DU, 0x2B6DU, 0x2F6DU,\n\t0x416EU, 0x426EU, 0x436EU, 0x446EU, 0x456EU, 0x466EU, 0x476EU, 0x486EU,\n\t0x496EU, 0x4A6EU, 0x4B6EU, 0x4C6EU, 0x4D6EU, 0x4E6EU, 0x4F6EU, 0x506EU,\n\t0x516EU, 0x526EU, 0x536EU, 0x546EU, 0x556EU, 0x566EU, 0x576EU, 0x586EU,\n\t0x596EU, 0x5A6EU, 0x616EU, 0x626EU, 0x636EU, 0x646EU, 0x656EU, 0x666EU,\n\t0x676EU, 0x686EU, 0x696EU, 0x6A6EU, 0x6B6EU, 0x6C6EU, 0x6D6EU, 0x6E6EU,\n\t0x6F6EU, 0x706EU, 0x716EU, 0x726EU, 0x736EU, 0x746EU, 0x756EU, 0x766EU,\n\t0x776EU, 0x786EU, 0x796EU, 0x7A6EU, 0x306EU, 0x316EU, 0x326EU, 0x336EU,\n\t0x346EU, 0x356EU, 0x366EU, 0x376EU, 0x386EU, 0x396EU, 0x2B6EU, 0x2F6EU,\n\t0x416FU, 0x426FU, 0x436FU, 0x446FU, 0x456FU, 0x466FU, 0x476FU, 0x486FU,\n\t0x496FU, 0x4A6FU, 0x4B6FU, 0x4C6FU, 0x4D6FU, 0x4E6FU, 0x4F6FU, 0x506FU,\n\t0x516FU, 0x526FU, 0x536FU, 0x546FU, 0x556FU, 0x566FU, 0x576FU, 0x586FU,\n\t0x596FU, 0x5A6FU, 0x616FU, 0x626FU, 0x636FU, 0x646FU, 0x656FU, 0x666FU,\n\t0x676FU, 0x686FU, 0x696FU, 0x6A6FU, 0x6B6FU, 0x6C6FU, 0x6D6FU, 0x6E6FU,\n\t0x6F6FU, 0x706FU, 0x716FU, 0x726FU, 0x736FU, 0x746FU, 0x756FU, 0x766FU,\n\t0x776FU, 0x786FU, 0x796FU, 0x7A6FU, 0x306FU, 0x316FU, 0x326FU, 0x336FU,\n\t0x346FU, 0x356FU, 0x366FU, 0x376FU, 0x386FU, 0x396FU, 0x2B6FU, 0x2F6FU,\n\t0x4170U, 0x4270U, 0x4370U, 0x4470U, 0x4570U, 0x4670U, 0x4770U, 0x4870U,\n\t0x4970U, 0x4A70U, 0x4B70U, 0x4C70U, 0x4D70U, 0x4E70U, 0x4F70U, 0x5070U,\n\t0x5170U, 0x5270U, 0x5370U, 0x5470U, 0x5570U, 0x5670U, 0x5770U, 0x5870U,\n\t0x5970U, 0x5A70U, 0x6170U, 0x6270U, 0x6370U, 0x6470U, 0x6570U, 0x6670U,\n\t0x6770U, 0x6870U, 0x6970U, 0x6A70U, 0x6B70U, 0x6C70U, 0x6D70U, 0x6E70U,\n\t0x6F70U, 0x7070U, 0x7170U, 0x7270U, 0x7370U, 0x7470U, 0x7570U, 0x7670U,\n\t0x7770U, 0x7870U, 0x7970U, 0x7A70U, 0x3070U, 0x3170U, 0x3270U, 0x3370U,\n\t0x3470U, 0x3570U, 0x3670U, 0x3770U, 0x3870U, 0x3970U, 0x2B70U, 0x2F70U,\n\t0x4171U, 0x4271U, 0x4371U, 0x4471U, 0x4571U, 0x4671U, 0x4771U, 0x4871U,\n\t0x4971U, 0x4A71U, 0x4B71U, 0x4C71U, 0x4D71U, 0x4E71U, 0x4F71U, 0x5071U,\n\t0x5171U, 0x5271U, 0x5371U, 0x5471U, 0x5571U, 0x5671U, 0x5771U, 0x5871U,\n\t0x5971U, 0x5A71U, 0x6171U, 0x6271U, 0x6371U, 0x6471U, 0x6571U, 0x6671U,\n\t0x6771U, 0x6871U, 0x6971U, 0x6A71U, 0x6B71U, 0x6C71U, 0x6D71U, 0x6E71U,\n\t0x6F71U, 0x7071U, 0x7171U, 0x7271U, 0x7371U, 0x7471U, 0x7571U, 0x7671U,\n\t0x7771U, 0x7871U, 0x7971U, 0x7A71U, 0x3071U, 0x3171U, 0x3271U, 0x3371U,\n\t0x3471U, 0x3571U, 0x3671U, 0x3771U, 0x3871U, 0x3971U, 0x2B71U, 0x2F71U,\n\t0x4172U, 0x4272U, 0x4372U, 0x4472U, 0x4572U, 0x4672U, 0x4772U, 0x4872U,\n\t0x4972U, 0x4A72U, 0x4B72U, 0x4C72U, 0x4D72U, 0x4E72U, 0x4F72U, 0x5072U,\n\t0x5172U, 0x5272U, 0x5372U, 0x5472U, 0x5572U, 0x5672U, 0x5772U, 0x5872U,\n\t0x5972U, 0x5A72U, 0x6172U, 0x6272U, 0x6372U, 0x6472U, 0x6572U, 0x6672U,\n\t0x6772U, 0x6872U, 0x6972U, 0x6A72U, 0x6B72U, 0x6C72U, 0x6D72U, 0x6E72U,\n\t0x6F72U, 0x7072U, 0x7172U, 0x7272U, 0x7372U, 0x7472U, 0x7572U, 0x7672U,\n\t0x7772U, 0x7872U, 0x7972U, 0x7A72U, 0x3072U, 0x3172U, 0x3272U, 0x3372U,\n\t0x3472U, 0x3572U, 0x3672U, 0x3772U, 0x3872U, 0x3972U, 0x2B72U, 0x2F72U,\n\t0x4173U, 0x4273U, 0x4373U, 0x4473U, 0x4573U, 0x4673U, 0x4773U, 0x4873U,\n\t0x4973U, 0x4A73U, 0x4B73U, 0x4C73U, 0x4D73U, 0x4E73U, 0x4F73U, 0x5073U,\n\t0x5173U, 0x5273U, 0x5373U, 0x5473U, 0x5573U, 0x5673U, 0x5773U, 0x5873U,\n\t0x5973U, 0x5A73U, 0x6173U, 0x6273U, 0x6373U, 0x6473U, 0x6573U, 0x6673U,\n\t0x6773U, 0x6873U, 0x6973U, 0x6A73U, 0x6B73U, 0x6C73U, 0x6D73U, 0x6E73U,\n\t0x6F73U, 0x7073U, 0x7173U, 0x7273U, 0x7373U, 0x7473U, 0x7573U, 0x7673U,\n\t0x7773U, 0x7873U, 0x7973U, 0x7A73U, 0x3073U, 0x3173U, 0x3273U, 0x3373U,\n\t0x3473U, 0x3573U, 0x3673U, 0x3773U, 0x3873U, 0x3973U, 0x2B73U, 0x2F73U,\n\t0x4174U, 0x4274U, 0x4374U, 0x4474U, 0x4574U, 0x4674U, 0x4774U, 0x4874U,\n\t0x4974U, 0x4A74U, 0x4B74U, 0x4C74U, 0x4D74U, 0x4E74U, 0x4F74U, 0x5074U,\n\t0x5174U, 0x5274U, 0x5374U, 0x5474U, 0x5574U, 0x5674U, 0x5774U, 0x5874U,\n\t0x5974U, 0x5A74U, 0x6174U, 0x6274U, 0x6374U, 0x6474U, 0x6574U, 0x6674U,\n\t0x6774U, 0x6874U, 0x6974U, 0x6A74U, 0x6B74U, 0x6C74U, 0x6D74U, 0x6E74U,\n\t0x6F74U, 0x7074U, 0x7174U, 0x7274U, 0x7374U, 0x7474U, 0x7574U, 0x7674U,\n\t0x7774U, 0x7874U, 0x7974U, 0x7A74U, 0x3074U, 0x3174U, 0x3274U, 0x3374U,\n\t0x3474U, 0x3574U, 0x3674U, 0x3774U, 0x3874U, 0x3974U, 0x2B74U, 0x2F74U,\n\t0x4175U, 0x4275U, 0x4375U, 0x4475U, 0x4575U, 0x4675U, 0x4775U, 0x4875U,\n\t0x4975U, 0x4A75U, 0x4B75U, 0x4C75U, 0x4D75U, 0x4E75U, 0x4F75U, 0x5075U,\n\t0x5175U, 0x5275U, 0x5375U, 0x5475U, 0x5575U, 0x5675U, 0x5775U, 0x5875U,\n\t0x5975U, 0x5A75U, 0x6175U, 0x6275U, 0x6375U, 0x6475U, 0x6575U, 0x6675U,\n\t0x6775U, 0x6875U, 0x6975U, 0x6A75U, 0x6B75U, 0x6C75U, 0x6D75U, 0x6E75U,\n\t0x6F75U, 0x7075U, 0x7175U, 0x7275U, 0x7375U, 0x7475U, 0x7575U, 0x7675U,\n\t0x7775U, 0x7875U, 0x7975U, 0x7A75U, 0x3075U, 0x3175U, 0x3275U, 0x3375U,\n\t0x3475U, 0x3575U, 0x3675U, 0x3775U, 0x3875U, 0x3975U, 0x2B75U, 0x2F75U,\n\t0x4176U, 0x4276U, 0x4376U, 0x4476U, 0x4576U, 0x4676U, 0x4776U, 0x4876U,\n\t0x4976U, 0x4A76U, 0x4B76U, 0x4C76U, 0x4D76U, 0x4E76U, 0x4F76U, 0x5076U,\n\t0x5176U, 0x5276U, 0x5376U, 0x5476U, 0x5576U, 0x5676U, 0x5776U, 0x5876U,\n\t0x5976U, 0x5A76U, 0x6176U, 0x6276U, 0x6376U, 0x6476U, 0x6576U, 0x6676U,\n\t0x6776U, 0x6876U, 0x6976U, 0x6A76U, 0x6B76U, 0x6C76U, 0x6D76U, 0x6E76U,\n\t0x6F76U, 0x7076U, 0x7176U, 0x7276U, 0x7376U, 0x7476U, 0x7576U, 0x7676U,\n\t0x7776U, 0x7876U, 0x7976U, 0x7A76U, 0x3076U, 0x3176U, 0x3276U, 0x3376U,\n\t0x3476U, 0x3576U, 0x3676U, 0x3776U, 0x3876U, 0x3976U, 0x2B76U, 0x2F76U,\n\t0x4177U, 0x4277U, 0x4377U, 0x4477U, 0x4577U, 0x4677U, 0x4777U, 0x4877U,\n\t0x4977U, 0x4A77U, 0x4B77U, 0x4C77U, 0x4D77U, 0x4E77U, 0x4F77U, 0x5077U,\n\t0x5177U, 0x5277U, 0x5377U, 0x5477U, 0x5577U, 0x5677U, 0x5777U, 0x5877U,\n\t0x5977U, 0x5A77U, 0x6177U, 0x6277U, 0x6377U, 0x6477U, 0x6577U, 0x6677U,\n\t0x6777U, 0x6877U, 0x6977U, 0x6A77U, 0x6B77U, 0x6C77U, 0x6D77U, 0x6E77U,\n\t0x6F77U, 0x7077U, 0x7177U, 0x7277U, 0x7377U, 0x7477U, 0x7577U, 0x7677U,\n\t0x7777U, 0x7877U, 0x7977U, 0x7A77U, 0x3077U, 0x3177U, 0x3277U, 0x3377U,\n\t0x3477U, 0x3577U, 0x3677U, 0x3777U, 0x3877U, 0x3977U, 0x2B77U, 0x2F77U,\n\t0x4178U, 0x4278U, 0x4378U, 0x4478U, 0x4578U, 0x4678U, 0x4778U, 0x4878U,\n\t0x4978U, 0x4A78U, 0x4B78U, 0x4C78U, 0x4D78U, 0x4E78U, 0x4F78U, 0x5078U,\n\t0x5178U, 0x5278U, 0x5378U, 0x5478U, 0x5578U, 0x5678U, 0x5778U, 0x5878U,\n\t0x5978U, 0x5A78U, 0x6178U, 0x6278U, 0x6378U, 0x6478U, 0x6578U, 0x6678U,\n\t0x6778U, 0x6878U, 0x6978U, 0x6A78U, 0x6B78U, 0x6C78U, 0x6D78U, 0x6E78U,\n\t0x6F78U, 0x7078U, 0x7178U, 0x7278U, 0x7378U, 0x7478U, 0x7578U, 0x7678U,\n\t0x7778U, 0x7878U, 0x7978U, 0x7A78U, 0x3078U, 0x3178U, 0x3278U, 0x3378U,\n\t0x3478U, 0x3578U, 0x3678U, 0x3778U, 0x3878U, 0x3978U, 0x2B78U, 0x2F78U,\n\t0x4179U, 0x4279U, 0x4379U, 0x4479U, 0x4579U, 0x4679U, 0x4779U, 0x4879U,\n\t0x4979U, 0x4A79U, 0x4B79U, 0x4C79U, 0x4D79U, 0x4E79U, 0x4F79U, 0x5079U,\n\t0x5179U, 0x5279U, 0x5379U, 0x5479U, 0x5579U, 0x5679U, 0x5779U, 0x5879U,\n\t0x5979U, 0x5A79U, 0x6179U, 0x6279U, 0x6379U, 0x6479U, 0x6579U, 0x6679U,\n\t0x6779U, 0x6879U, 0x6979U, 0x6A79U, 0x6B79U, 0x6C79U, 0x6D79U, 0x6E79U,\n\t0x6F79U, 0x7079U, 0x7179U, 0x7279U, 0x7379U, 0x7479U, 0x7579U, 0x7679U,\n\t0x7779U, 0x7879U, 0x7979U, 0x7A79U, 0x3079U, 0x3179U, 0x3279U, 0x3379U,\n\t0x3479U, 0x3579U, 0x3679U, 0x3779U, 0x3879U, 0x3979U, 0x2B79U, 0x2F79U,\n\t0x417AU, 0x427AU, 0x437AU, 0x447AU, 0x457AU, 0x467AU, 0x477AU, 0x487AU,\n\t0x497AU, 0x4A7AU, 0x4B7AU, 0x4C7AU, 0x4D7AU, 0x4E7AU, 0x4F7AU, 0x507AU,\n\t0x517AU, 0x527AU, 0x537AU, 0x547AU, 0x557AU, 0x567AU, 0x577AU, 0x587AU,\n\t0x597AU, 0x5A7AU, 0x617AU, 0x627AU, 0x637AU, 0x647AU, 0x657AU, 0x667AU,\n\t0x677AU, 0x687AU, 0x697AU, 0x6A7AU, 0x6B7AU, 0x6C7AU, 0x6D7AU, 0x6E7AU,\n\t0x6F7AU, 0x707AU, 0x717AU, 0x727AU, 0x737AU, 0x747AU, 0x757AU, 0x767AU,\n\t0x777AU, 0x787AU, 0x797AU, 0x7A7AU, 0x307AU, 0x317AU, 0x327AU, 0x337AU,\n\t0x347AU, 0x357AU, 0x367AU, 0x377AU, 0x387AU, 0x397AU, 0x2B7AU, 0x2F7AU,\n\t0x4130U, 0x4230U, 0x4330U, 0x4430U, 0x4530U, 0x4630U, 0x4730U, 0x4830U,\n\t0x4930U, 0x4A30U, 0x4B30U, 0x4C30U, 0x4D30U, 0x4E30U, 0x4F30U, 0x5030U,\n\t0x5130U, 0x5230U, 0x5330U, 0x5430U, 0x5530U, 0x5630U, 0x5730U, 0x5830U,\n\t0x5930U, 0x5A30U, 0x6130U, 0x6230U, 0x6330U, 0x6430U, 0x6530U, 0x6630U,\n\t0x6730U, 0x6830U, 0x6930U, 0x6A30U, 0x6B30U, 0x6C30U, 0x6D30U, 0x6E30U,\n\t0x6F30U, 0x7030U, 0x7130U, 0x7230U, 0x7330U, 0x7430U, 0x7530U, 0x7630U,\n\t0x7730U, 0x7830U, 0x7930U, 0x7A30U, 0x3030U, 0x3130U, 0x3230U, 0x3330U,\n\t0x3430U, 0x3530U, 0x3630U, 0x3730U, 0x3830U, 0x3930U, 0x2B30U, 0x2F30U,\n\t0x4131U, 0x4231U, 0x4331U, 0x4431U, 0x4531U, 0x4631U, 0x4731U, 0x4831U,\n\t0x4931U, 0x4A31U, 0x4B31U, 0x4C31U, 0x4D31U, 0x4E31U, 0x4F31U, 0x5031U,\n\t0x5131U, 0x5231U, 0x5331U, 0x5431U, 0x5531U, 0x5631U, 0x5731U, 0x5831U,\n\t0x5931U, 0x5A31U, 0x6131U, 0x6231U, 0x6331U, 0x6431U, 0x6531U, 0x6631U,\n\t0x6731U, 0x6831U, 0x6931U, 0x6A31U, 0x6B31U, 0x6C31U, 0x6D31U, 0x6E31U,\n\t0x6F31U, 0x7031U, 0x7131U, 0x7231U, 0x7331U, 0x7431U, 0x7531U, 0x7631U,\n\t0x7731U, 0x7831U, 0x7931U, 0x7A31U, 0x3031U, 0x3131U, 0x3231U, 0x3331U,\n\t0x3431U, 0x3531U, 0x3631U, 0x3731U, 0x3831U, 0x3931U, 0x2B31U, 0x2F31U,\n\t0x4132U, 0x4232U, 0x4332U, 0x4432U, 0x4532U, 0x4632U, 0x4732U, 0x4832U,\n\t0x4932U, 0x4A32U, 0x4B32U, 0x4C32U, 0x4D32U, 0x4E32U, 0x4F32U, 0x5032U,\n\t0x5132U, 0x5232U, 0x5332U, 0x5432U, 0x5532U, 0x5632U, 0x5732U, 0x5832U,\n\t0x5932U, 0x5A32U, 0x6132U, 0x6232U, 0x6332U, 0x6432U, 0x6532U, 0x6632U,\n\t0x6732U, 0x6832U, 0x6932U, 0x6A32U, 0x6B32U, 0x6C32U, 0x6D32U, 0x6E32U,\n\t0x6F32U, 0x7032U, 0x7132U, 0x7232U, 0x7332U, 0x7432U, 0x7532U, 0x7632U,\n\t0x7732U, 0x7832U, 0x7932U, 0x7A32U, 0x3032U, 0x3132U, 0x3232U, 0x3332U,\n\t0x3432U, 0x3532U, 0x3632U, 0x3732U, 0x3832U, 0x3932U, 0x2B32U, 0x2F32U,\n\t0x4133U, 0x4233U, 0x4333U, 0x4433U, 0x4533U, 0x4633U, 0x4733U, 0x4833U,\n\t0x4933U, 0x4A33U, 0x4B33U, 0x4C33U, 0x4D33U, 0x4E33U, 0x4F33U, 0x5033U,\n\t0x5133U, 0x5233U, 0x5333U, 0x5433U, 0x5533U, 0x5633U, 0x5733U, 0x5833U,\n\t0x5933U, 0x5A33U, 0x6133U, 0x6233U, 0x6333U, 0x6433U, 0x6533U, 0x6633U,\n\t0x6733U, 0x6833U, 0x6933U, 0x6A33U, 0x6B33U, 0x6C33U, 0x6D33U, 0x6E33U,\n\t0x6F33U, 0x7033U, 0x7133U, 0x7233U, 0x7333U, 0x7433U, 0x7533U, 0x7633U,\n\t0x7733U, 0x7833U, 0x7933U, 0x7A33U, 0x3033U, 0x3133U, 0x3233U, 0x3333U,\n\t0x3433U, 0x3533U, 0x3633U, 0x3733U, 0x3833U, 0x3933U, 0x2B33U, 0x2F33U,\n\t0x4134U, 0x4234U, 0x4334U, 0x4434U, 0x4534U, 0x4634U, 0x4734U, 0x4834U,\n\t0x4934U, 0x4A34U, 0x4B34U, 0x4C34U, 0x4D34U, 0x4E34U, 0x4F34U, 0x5034U,\n\t0x5134U, 0x5234U, 0x5334U, 0x5434U, 0x5534U, 0x5634U, 0x5734U, 0x5834U,\n\t0x5934U, 0x5A34U, 0x6134U, 0x6234U, 0x6334U, 0x6434U, 0x6534U, 0x6634U,\n\t0x6734U, 0x6834U, 0x6934U, 0x6A34U, 0x6B34U, 0x6C34U, 0x6D34U, 0x6E34U,\n\t0x6F34U, 0x7034U, 0x7134U, 0x7234U, 0x7334U, 0x7434U, 0x7534U, 0x7634U,\n\t0x7734U, 0x7834U, 0x7934U, 0x7A34U, 0x3034U, 0x3134U, 0x3234U, 0x3334U,\n\t0x3434U, 0x3534U, 0x3634U, 0x3734U, 0x3834U, 0x3934U, 0x2B34U, 0x2F34U,\n\t0x4135U, 0x4235U, 0x4335U, 0x4435U, 0x4535U, 0x4635U, 0x4735U, 0x4835U,\n\t0x4935U, 0x4A35U, 0x4B35U, 0x4C35U, 0x4D35U, 0x4E35U, 0x4F35U, 0x5035U,\n\t0x5135U, 0x5235U, 0x5335U, 0x5435U, 0x5535U, 0x5635U, 0x5735U, 0x5835U,\n\t0x5935U, 0x5A35U, 0x6135U, 0x6235U, 0x6335U, 0x6435U, 0x6535U, 0x6635U,\n\t0x6735U, 0x6835U, 0x6935U, 0x6A35U, 0x6B35U, 0x6C35U, 0x6D35U, 0x6E35U,\n\t0x6F35U, 0x7035U, 0x7135U, 0x7235U, 0x7335U, 0x7435U, 0x7535U, 0x7635U,\n\t0x7735U, 0x7835U, 0x7935U, 0x7A35U, 0x3035U, 0x3135U, 0x3235U, 0x3335U,\n\t0x3435U, 0x3535U, 0x3635U, 0x3735U, 0x3835U, 0x3935U, 0x2B35U, 0x2F35U,\n\t0x4136U, 0x4236U, 0x4336U, 0x4436U, 0x4536U, 0x4636U, 0x4736U, 0x4836U,\n\t0x4936U, 0x4A36U, 0x4B36U, 0x4C36U, 0x4D36U, 0x4E36U, 0x4F36U, 0x5036U,\n\t0x5136U, 0x5236U, 0x5336U, 0x5436U, 0x5536U, 0x5636U, 0x5736U, 0x5836U,\n\t0x5936U, 0x5A36U, 0x6136U, 0x6236U, 0x6336U, 0x6436U, 0x6536U, 0x6636U,\n\t0x6736U, 0x6836U, 0x6936U, 0x6A36U, 0x6B36U, 0x6C36U, 0x6D36U, 0x6E36U,\n\t0x6F36U, 0x7036U, 0x7136U, 0x7236U, 0x7336U, 0x7436U, 0x7536U, 0x7636U,\n\t0x7736U, 0x7836U, 0x7936U, 0x7A36U, 0x3036U, 0x3136U, 0x3236U, 0x3336U,\n\t0x3436U, 0x3536U, 0x3636U, 0x3736U, 0x3836U, 0x3936U, 0x2B36U, 0x2F36U,\n\t0x4137U, 0x4237U, 0x4337U, 0x4437U, 0x4537U, 0x4637U, 0x4737U, 0x4837U,\n\t0x4937U, 0x4A37U, 0x4B37U, 0x4C37U, 0x4D37U, 0x4E37U, 0x4F37U, 0x5037U,\n\t0x5137U, 0x5237U, 0x5337U, 0x5437U, 0x5537U, 0x5637U, 0x5737U, 0x5837U,\n\t0x5937U, 0x5A37U, 0x6137U, 0x6237U, 0x6337U, 0x6437U, 0x6537U, 0x6637U,\n\t0x6737U, 0x6837U, 0x6937U, 0x6A37U, 0x6B37U, 0x6C37U, 0x6D37U, 0x6E37U,\n\t0x6F37U, 0x7037U, 0x7137U, 0x7237U, 0x7337U, 0x7437U, 0x7537U, 0x7637U,\n\t0x7737U, 0x7837U, 0x7937U, 0x7A37U, 0x3037U, 0x3137U, 0x3237U, 0x3337U,\n\t0x3437U, 0x3537U, 0x3637U, 0x3737U, 0x3837U, 0x3937U, 0x2B37U, 0x2F37U,\n\t0x4138U, 0x4238U, 0x4338U, 0x4438U, 0x4538U, 0x4638U, 0x4738U, 0x4838U,\n\t0x4938U, 0x4A38U, 0x4B38U, 0x4C38U, 0x4D38U, 0x4E38U, 0x4F38U, 0x5038U,\n\t0x5138U, 0x5238U, 0x5338U, 0x5438U, 0x5538U, 0x5638U, 0x5738U, 0x5838U,\n\t0x5938U, 0x5A38U, 0x6138U, 0x6238U, 0x6338U, 0x6438U, 0x6538U, 0x6638U,\n\t0x6738U, 0x6838U, 0x6938U, 0x6A38U, 0x6B38U, 0x6C38U, 0x6D38U, 0x6E38U,\n\t0x6F38U, 0x7038U, 0x7138U, 0x7238U, 0x7338U, 0x7438U, 0x7538U, 0x7638U,\n\t0x7738U, 0x7838U, 0x7938U, 0x7A38U, 0x3038U, 0x3138U, 0x3238U, 0x3338U,\n\t0x3438U, 0x3538U, 0x3638U, 0x3738U, 0x3838U, 0x3938U, 0x2B38U, 0x2F38U,\n\t0x4139U, 0x4239U, 0x4339U, 0x4439U, 0x4539U, 0x4639U, 0x4739U, 0x4839U,\n\t0x4939U, 0x4A39U, 0x4B39U, 0x4C39U, 0x4D39U, 0x4E39U, 0x4F39U, 0x5039U,\n\t0x5139U, 0x5239U, 0x5339U, 0x5439U, 0x5539U, 0x5639U, 0x5739U, 0x5839U,\n\t0x5939U, 0x5A39U, 0x6139U, 0x6239U, 0x6339U, 0x6439U, 0x6539U, 0x6639U,\n\t0x6739U, 0x6839U, 0x6939U, 0x6A39U, 0x6B39U, 0x6C39U, 0x6D39U, 0x6E39U,\n\t0x6F39U, 0x7039U, 0x7139U, 0x7239U, 0x7339U, 0x7439U, 0x7539U, 0x7639U,\n\t0x7739U, 0x7839U, 0x7939U, 0x7A39U, 0x3039U, 0x3139U, 0x3239U, 0x3339U,\n\t0x3439U, 0x3539U, 0x3639U, 0x3739U, 0x3839U, 0x3939U, 0x2B39U, 0x2F39U,\n\t0x412BU, 0x422BU, 0x432BU, 0x442BU, 0x452BU, 0x462BU, 0x472BU, 0x482BU,\n\t0x492BU, 0x4A2BU, 0x4B2BU, 0x4C2BU, 0x4D2BU, 0x4E2BU, 0x4F2BU, 0x502BU,\n\t0x512BU, 0x522BU, 0x532BU, 0x542BU, 0x552BU, 0x562BU, 0x572BU, 0x582BU,\n\t0x592BU, 0x5A2BU, 0x612BU, 0x622BU, 0x632BU, 0x642BU, 0x652BU, 0x662BU,\n\t0x672BU, 0x682BU, 0x692BU, 0x6A2BU, 0x6B2BU, 0x6C2BU, 0x6D2BU, 0x6E2BU,\n\t0x6F2BU, 0x702BU, 0x712BU, 0x722BU, 0x732BU, 0x742BU, 0x752BU, 0x762BU,\n\t0x772BU, 0x782BU, 0x792BU, 0x7A2BU, 0x302BU, 0x312BU, 0x322BU, 0x332BU,\n\t0x342BU, 0x352BU, 0x362BU, 0x372BU, 0x382BU, 0x392BU, 0x2B2BU, 0x2F2BU,\n\t0x412FU, 0x422FU, 0x432FU, 0x442FU, 0x452FU, 0x462FU, 0x472FU, 0x482FU,\n\t0x492FU, 0x4A2FU, 0x4B2FU, 0x4C2FU, 0x4D2FU, 0x4E2FU, 0x4F2FU, 0x502FU,\n\t0x512FU, 0x522FU, 0x532FU, 0x542FU, 0x552FU, 0x562FU, 0x572FU, 0x582FU,\n\t0x592FU, 0x5A2FU, 0x612FU, 0x622FU, 0x632FU, 0x642FU, 0x652FU, 0x662FU,\n\t0x672FU, 0x682FU, 0x692FU, 0x6A2FU, 0x6B2FU, 0x6C2FU, 0x6D2FU, 0x6E2FU,\n\t0x6F2FU, 0x702FU, 0x712FU, 0x722FU, 0x732FU, 0x742FU, 0x752FU, 0x762FU,\n\t0x772FU, 0x782FU, 0x792FU, 0x7A2FU, 0x302FU, 0x312FU, 0x322FU, 0x332FU,\n\t0x342FU, 0x352FU, 0x362FU, 0x372FU, 0x382FU, 0x392FU, 0x2B2FU, 0x2F2FU,\n#else\n\t0x4141U, 0x4142U, 0x4143U, 0x4144U, 0x4145U, 0x4146U, 0x4147U, 0x4148U,\n\t0x4149U, 0x414AU, 0x414BU, 0x414CU, 0x414DU, 0x414EU, 0x414FU, 0x4150U,\n\t0x4151U, 0x4152U, 0x4153U, 0x4154U, 0x4155U, 0x4156U, 0x4157U, 0x4158U,\n\t0x4159U, 0x415AU, 0x4161U, 0x4162U, 0x4163U, 0x4164U, 0x4165U, 0x4166U,\n\t0x4167U, 0x4168U, 0x4169U, 0x416AU, 0x416BU, 0x416CU, 0x416DU, 0x416EU,\n\t0x416FU, 0x4170U, 0x4171U, 0x4172U, 0x4173U, 0x4174U, 0x4175U, 0x4176U,\n\t0x4177U, 0x4178U, 0x4179U, 0x417AU, 0x4130U, 0x4131U, 0x4132U, 0x4133U,\n\t0x4134U, 0x4135U, 0x4136U, 0x4137U, 0x4138U, 0x4139U, 0x412BU, 0x412FU,\n\t0x4241U, 0x4242U, 0x4243U, 0x4244U, 0x4245U, 0x4246U, 0x4247U, 0x4248U,\n\t0x4249U, 0x424AU, 0x424BU, 0x424CU, 0x424DU, 0x424EU, 0x424FU, 0x4250U,\n\t0x4251U, 0x4252U, 0x4253U, 0x4254U, 0x4255U, 0x4256U, 0x4257U, 0x4258U,\n\t0x4259U, 0x425AU, 0x4261U, 0x4262U, 0x4263U, 0x4264U, 0x4265U, 0x4266U,\n\t0x4267U, 0x4268U, 0x4269U, 0x426AU, 0x426BU, 0x426CU, 0x426DU, 0x426EU,\n\t0x426FU, 0x4270U, 0x4271U, 0x4272U, 0x4273U, 0x4274U, 0x4275U, 0x4276U,\n\t0x4277U, 0x4278U, 0x4279U, 0x427AU, 0x4230U, 0x4231U, 0x4232U, 0x4233U,\n\t0x4234U, 0x4235U, 0x4236U, 0x4237U, 0x4238U, 0x4239U, 0x422BU, 0x422FU,\n\t0x4341U, 0x4342U, 0x4343U, 0x4344U, 0x4345U, 0x4346U, 0x4347U, 0x4348U,\n\t0x4349U, 0x434AU, 0x434BU, 0x434CU, 0x434DU, 0x434EU, 0x434FU, 0x4350U,\n\t0x4351U, 0x4352U, 0x4353U, 0x4354U, 0x4355U, 0x4356U, 0x4357U, 0x4358U,\n\t0x4359U, 0x435AU, 0x4361U, 0x4362U, 0x4363U, 0x4364U, 0x4365U, 0x4366U,\n\t0x4367U, 0x4368U, 0x4369U, 0x436AU, 0x436BU, 0x436CU, 0x436DU, 0x436EU,\n\t0x436FU, 0x4370U, 0x4371U, 0x4372U, 0x4373U, 0x4374U, 0x4375U, 0x4376U,\n\t0x4377U, 0x4378U, 0x4379U, 0x437AU, 0x4330U, 0x4331U, 0x4332U, 0x4333U,\n\t0x4334U, 0x4335U, 0x4336U, 0x4337U, 0x4338U, 0x4339U, 0x432BU, 0x432FU,\n\t0x4441U, 0x4442U, 0x4443U, 0x4444U, 0x4445U, 0x4446U, 0x4447U, 0x4448U,\n\t0x4449U, 0x444AU, 0x444BU, 0x444CU, 0x444DU, 0x444EU, 0x444FU, 0x4450U,\n\t0x4451U, 0x4452U, 0x4453U, 0x4454U, 0x4455U, 0x4456U, 0x4457U, 0x4458U,\n\t0x4459U, 0x445AU, 0x4461U, 0x4462U, 0x4463U, 0x4464U, 0x4465U, 0x4466U,\n\t0x4467U, 0x4468U, 0x4469U, 0x446AU, 0x446BU, 0x446CU, 0x446DU, 0x446EU,\n\t0x446FU, 0x4470U, 0x4471U, 0x4472U, 0x4473U, 0x4474U, 0x4475U, 0x4476U,\n\t0x4477U, 0x4478U, 0x4479U, 0x447AU, 0x4430U, 0x4431U, 0x4432U, 0x4433U,\n\t0x4434U, 0x4435U, 0x4436U, 0x4437U, 0x4438U, 0x4439U, 0x442BU, 0x442FU,\n\t0x4541U, 0x4542U, 0x4543U, 0x4544U, 0x4545U, 0x4546U, 0x4547U, 0x4548U,\n\t0x4549U, 0x454AU, 0x454BU, 0x454CU, 0x454DU, 0x454EU, 0x454FU, 0x4550U,\n\t0x4551U, 0x4552U, 0x4553U, 0x4554U, 0x4555U, 0x4556U, 0x4557U, 0x4558U,\n\t0x4559U, 0x455AU, 0x4561U, 0x4562U, 0x4563U, 0x4564U, 0x4565U, 0x4566U,\n\t0x4567U, 0x4568U, 0x4569U, 0x456AU, 0x456BU, 0x456CU, 0x456DU, 0x456EU,\n\t0x456FU, 0x4570U, 0x4571U, 0x4572U, 0x4573U, 0x4574U, 0x4575U, 0x4576U,\n\t0x4577U, 0x4578U, 0x4579U, 0x457AU, 0x4530U, 0x4531U, 0x4532U, 0x4533U,\n\t0x4534U, 0x4535U, 0x4536U, 0x4537U, 0x4538U, 0x4539U, 0x452BU, 0x452FU,\n\t0x4641U, 0x4642U, 0x4643U, 0x4644U, 0x4645U, 0x4646U, 0x4647U, 0x4648U,\n\t0x4649U, 0x464AU, 0x464BU, 0x464CU, 0x464DU, 0x464EU, 0x464FU, 0x4650U,\n\t0x4651U, 0x4652U, 0x4653U, 0x4654U, 0x4655U, 0x4656U, 0x4657U, 0x4658U,\n\t0x4659U, 0x465AU, 0x4661U, 0x4662U, 0x4663U, 0x4664U, 0x4665U, 0x4666U,\n\t0x4667U, 0x4668U, 0x4669U, 0x466AU, 0x466BU, 0x466CU, 0x466DU, 0x466EU,\n\t0x466FU, 0x4670U, 0x4671U, 0x4672U, 0x4673U, 0x4674U, 0x4675U, 0x4676U,\n\t0x4677U, 0x4678U, 0x4679U, 0x467AU, 0x4630U, 0x4631U, 0x4632U, 0x4633U,\n\t0x4634U, 0x4635U, 0x4636U, 0x4637U, 0x4638U, 0x4639U, 0x462BU, 0x462FU,\n\t0x4741U, 0x4742U, 0x4743U, 0x4744U, 0x4745U, 0x4746U, 0x4747U, 0x4748U,\n\t0x4749U, 0x474AU, 0x474BU, 0x474CU, 0x474DU, 0x474EU, 0x474FU, 0x4750U,\n\t0x4751U, 0x4752U, 0x4753U, 0x4754U, 0x4755U, 0x4756U, 0x4757U, 0x4758U,\n\t0x4759U, 0x475AU, 0x4761U, 0x4762U, 0x4763U, 0x4764U, 0x4765U, 0x4766U,\n\t0x4767U, 0x4768U, 0x4769U, 0x476AU, 0x476BU, 0x476CU, 0x476DU, 0x476EU,\n\t0x476FU, 0x4770U, 0x4771U, 0x4772U, 0x4773U, 0x4774U, 0x4775U, 0x4776U,\n\t0x4777U, 0x4778U, 0x4779U, 0x477AU, 0x4730U, 0x4731U, 0x4732U, 0x4733U,\n\t0x4734U, 0x4735U, 0x4736U, 0x4737U, 0x4738U, 0x4739U, 0x472BU, 0x472FU,\n\t0x4841U, 0x4842U, 0x4843U, 0x4844U, 0x4845U, 0x4846U, 0x4847U, 0x4848U,\n\t0x4849U, 0x484AU, 0x484BU, 0x484CU, 0x484DU, 0x484EU, 0x484FU, 0x4850U,\n\t0x4851U, 0x4852U, 0x4853U, 0x4854U, 0x4855U, 0x4856U, 0x4857U, 0x4858U,\n\t0x4859U, 0x485AU, 0x4861U, 0x4862U, 0x4863U, 0x4864U, 0x4865U, 0x4866U,\n\t0x4867U, 0x4868U, 0x4869U, 0x486AU, 0x486BU, 0x486CU, 0x486DU, 0x486EU,\n\t0x486FU, 0x4870U, 0x4871U, 0x4872U, 0x4873U, 0x4874U, 0x4875U, 0x4876U,\n\t0x4877U, 0x4878U, 0x4879U, 0x487AU, 0x4830U, 0x4831U, 0x4832U, 0x4833U,\n\t0x4834U, 0x4835U, 0x4836U, 0x4837U, 0x4838U, 0x4839U, 0x482BU, 0x482FU,\n\t0x4941U, 0x4942U, 0x4943U, 0x4944U, 0x4945U, 0x4946U, 0x4947U, 0x4948U,\n\t0x4949U, 0x494AU, 0x494BU, 0x494CU, 0x494DU, 0x494EU, 0x494FU, 0x4950U,\n\t0x4951U, 0x4952U, 0x4953U, 0x4954U, 0x4955U, 0x4956U, 0x4957U, 0x4958U,\n\t0x4959U, 0x495AU, 0x4961U, 0x4962U, 0x4963U, 0x4964U, 0x4965U, 0x4966U,\n\t0x4967U, 0x4968U, 0x4969U, 0x496AU, 0x496BU, 0x496CU, 0x496DU, 0x496EU,\n\t0x496FU, 0x4970U, 0x4971U, 0x4972U, 0x4973U, 0x4974U, 0x4975U, 0x4976U,\n\t0x4977U, 0x4978U, 0x4979U, 0x497AU, 0x4930U, 0x4931U, 0x4932U, 0x4933U,\n\t0x4934U, 0x4935U, 0x4936U, 0x4937U, 0x4938U, 0x4939U, 0x492BU, 0x492FU,\n\t0x4A41U, 0x4A42U, 0x4A43U, 0x4A44U, 0x4A45U, 0x4A46U, 0x4A47U, 0x4A48U,\n\t0x4A49U, 0x4A4AU, 0x4A4BU, 0x4A4CU, 0x4A4DU, 0x4A4EU, 0x4A4FU, 0x4A50U,\n\t0x4A51U, 0x4A52U, 0x4A53U, 0x4A54U, 0x4A55U, 0x4A56U, 0x4A57U, 0x4A58U,\n\t0x4A59U, 0x4A5AU, 0x4A61U, 0x4A62U, 0x4A63U, 0x4A64U, 0x4A65U, 0x4A66U,\n\t0x4A67U, 0x4A68U, 0x4A69U, 0x4A6AU, 0x4A6BU, 0x4A6CU, 0x4A6DU, 0x4A6EU,\n\t0x4A6FU, 0x4A70U, 0x4A71U, 0x4A72U, 0x4A73U, 0x4A74U, 0x4A75U, 0x4A76U,\n\t0x4A77U, 0x4A78U, 0x4A79U, 0x4A7AU, 0x4A30U, 0x4A31U, 0x4A32U, 0x4A33U,\n\t0x4A34U, 0x4A35U, 0x4A36U, 0x4A37U, 0x4A38U, 0x4A39U, 0x4A2BU, 0x4A2FU,\n\t0x4B41U, 0x4B42U, 0x4B43U, 0x4B44U, 0x4B45U, 0x4B46U, 0x4B47U, 0x4B48U,\n\t0x4B49U, 0x4B4AU, 0x4B4BU, 0x4B4CU, 0x4B4DU, 0x4B4EU, 0x4B4FU, 0x4B50U,\n\t0x4B51U, 0x4B52U, 0x4B53U, 0x4B54U, 0x4B55U, 0x4B56U, 0x4B57U, 0x4B58U,\n\t0x4B59U, 0x4B5AU, 0x4B61U, 0x4B62U, 0x4B63U, 0x4B64U, 0x4B65U, 0x4B66U,\n\t0x4B67U, 0x4B68U, 0x4B69U, 0x4B6AU, 0x4B6BU, 0x4B6CU, 0x4B6DU, 0x4B6EU,\n\t0x4B6FU, 0x4B70U, 0x4B71U, 0x4B72U, 0x4B73U, 0x4B74U, 0x4B75U, 0x4B76U,\n\t0x4B77U, 0x4B78U, 0x4B79U, 0x4B7AU, 0x4B30U, 0x4B31U, 0x4B32U, 0x4B33U,\n\t0x4B34U, 0x4B35U, 0x4B36U, 0x4B37U, 0x4B38U, 0x4B39U, 0x4B2BU, 0x4B2FU,\n\t0x4C41U, 0x4C42U, 0x4C43U, 0x4C44U, 0x4C45U, 0x4C46U, 0x4C47U, 0x4C48U,\n\t0x4C49U, 0x4C4AU, 0x4C4BU, 0x4C4CU, 0x4C4DU, 0x4C4EU, 0x4C4FU, 0x4C50U,\n\t0x4C51U, 0x4C52U, 0x4C53U, 0x4C54U, 0x4C55U, 0x4C56U, 0x4C57U, 0x4C58U,\n\t0x4C59U, 0x4C5AU, 0x4C61U, 0x4C62U, 0x4C63U, 0x4C64U, 0x4C65U, 0x4C66U,\n\t0x4C67U, 0x4C68U, 0x4C69U, 0x4C6AU, 0x4C6BU, 0x4C6CU, 0x4C6DU, 0x4C6EU,\n\t0x4C6FU, 0x4C70U, 0x4C71U, 0x4C72U, 0x4C73U, 0x4C74U, 0x4C75U, 0x4C76U,\n\t0x4C77U, 0x4C78U, 0x4C79U, 0x4C7AU, 0x4C30U, 0x4C31U, 0x4C32U, 0x4C33U,\n\t0x4C34U, 0x4C35U, 0x4C36U, 0x4C37U, 0x4C38U, 0x4C39U, 0x4C2BU, 0x4C2FU,\n\t0x4D41U, 0x4D42U, 0x4D43U, 0x4D44U, 0x4D45U, 0x4D46U, 0x4D47U, 0x4D48U,\n\t0x4D49U, 0x4D4AU, 0x4D4BU, 0x4D4CU, 0x4D4DU, 0x4D4EU, 0x4D4FU, 0x4D50U,\n\t0x4D51U, 0x4D52U, 0x4D53U, 0x4D54U, 0x4D55U, 0x4D56U, 0x4D57U, 0x4D58U,\n\t0x4D59U, 0x4D5AU, 0x4D61U, 0x4D62U, 0x4D63U, 0x4D64U, 0x4D65U, 0x4D66U,\n\t0x4D67U, 0x4D68U, 0x4D69U, 0x4D6AU, 0x4D6BU, 0x4D6CU, 0x4D6DU, 0x4D6EU,\n\t0x4D6FU, 0x4D70U, 0x4D71U, 0x4D72U, 0x4D73U, 0x4D74U, 0x4D75U, 0x4D76U,\n\t0x4D77U, 0x4D78U, 0x4D79U, 0x4D7AU, 0x4D30U, 0x4D31U, 0x4D32U, 0x4D33U,\n\t0x4D34U, 0x4D35U, 0x4D36U, 0x4D37U, 0x4D38U, 0x4D39U, 0x4D2BU, 0x4D2FU,\n\t0x4E41U, 0x4E42U, 0x4E43U, 0x4E44U, 0x4E45U, 0x4E46U, 0x4E47U, 0x4E48U,\n\t0x4E49U, 0x4E4AU, 0x4E4BU, 0x4E4CU, 0x4E4DU, 0x4E4EU, 0x4E4FU, 0x4E50U,\n\t0x4E51U, 0x4E52U, 0x4E53U, 0x4E54U, 0x4E55U, 0x4E56U, 0x4E57U, 0x4E58U,\n\t0x4E59U, 0x4E5AU, 0x4E61U, 0x4E62U, 0x4E63U, 0x4E64U, 0x4E65U, 0x4E66U,\n\t0x4E67U, 0x4E68U, 0x4E69U, 0x4E6AU, 0x4E6BU, 0x4E6CU, 0x4E6DU, 0x4E6EU,\n\t0x4E6FU, 0x4E70U, 0x4E71U, 0x4E72U, 0x4E73U, 0x4E74U, 0x4E75U, 0x4E76U,\n\t0x4E77U, 0x4E78U, 0x4E79U, 0x4E7AU, 0x4E30U, 0x4E31U, 0x4E32U, 0x4E33U,\n\t0x4E34U, 0x4E35U, 0x4E36U, 0x4E37U, 0x4E38U, 0x4E39U, 0x4E2BU, 0x4E2FU,\n\t0x4F41U, 0x4F42U, 0x4F43U, 0x4F44U, 0x4F45U, 0x4F46U, 0x4F47U, 0x4F48U,\n\t0x4F49U, 0x4F4AU, 0x4F4BU, 0x4F4CU, 0x4F4DU, 0x4F4EU, 0x4F4FU, 0x4F50U,\n\t0x4F51U, 0x4F52U, 0x4F53U, 0x4F54U, 0x4F55U, 0x4F56U, 0x4F57U, 0x4F58U,\n\t0x4F59U, 0x4F5AU, 0x4F61U, 0x4F62U, 0x4F63U, 0x4F64U, 0x4F65U, 0x4F66U,\n\t0x4F67U, 0x4F68U, 0x4F69U, 0x4F6AU, 0x4F6BU, 0x4F6CU, 0x4F6DU, 0x4F6EU,\n\t0x4F6FU, 0x4F70U, 0x4F71U, 0x4F72U, 0x4F73U, 0x4F74U, 0x4F75U, 0x4F76U,\n\t0x4F77U, 0x4F78U, 0x4F79U, 0x4F7AU, 0x4F30U, 0x4F31U, 0x4F32U, 0x4F33U,\n\t0x4F34U, 0x4F35U, 0x4F36U, 0x4F37U, 0x4F38U, 0x4F39U, 0x4F2BU, 0x4F2FU,\n\t0x5041U, 0x5042U, 0x5043U, 0x5044U, 0x5045U, 0x5046U, 0x5047U, 0x5048U,\n\t0x5049U, 0x504AU, 0x504BU, 0x504CU, 0x504DU, 0x504EU, 0x504FU, 0x5050U,\n\t0x5051U, 0x5052U, 0x5053U, 0x5054U, 0x5055U, 0x5056U, 0x5057U, 0x5058U,\n\t0x5059U, 0x505AU, 0x5061U, 0x5062U, 0x5063U, 0x5064U, 0x5065U, 0x5066U,\n\t0x5067U, 0x5068U, 0x5069U, 0x506AU, 0x506BU, 0x506CU, 0x506DU, 0x506EU,\n\t0x506FU, 0x5070U, 0x5071U, 0x5072U, 0x5073U, 0x5074U, 0x5075U, 0x5076U,\n\t0x5077U, 0x5078U, 0x5079U, 0x507AU, 0x5030U, 0x5031U, 0x5032U, 0x5033U,\n\t0x5034U, 0x5035U, 0x5036U, 0x5037U, 0x5038U, 0x5039U, 0x502BU, 0x502FU,\n\t0x5141U, 0x5142U, 0x5143U, 0x5144U, 0x5145U, 0x5146U, 0x5147U, 0x5148U,\n\t0x5149U, 0x514AU, 0x514BU, 0x514CU, 0x514DU, 0x514EU, 0x514FU, 0x5150U,\n\t0x5151U, 0x5152U, 0x5153U, 0x5154U, 0x5155U, 0x5156U, 0x5157U, 0x5158U,\n\t0x5159U, 0x515AU, 0x5161U, 0x5162U, 0x5163U, 0x5164U, 0x5165U, 0x5166U,\n\t0x5167U, 0x5168U, 0x5169U, 0x516AU, 0x516BU, 0x516CU, 0x516DU, 0x516EU,\n\t0x516FU, 0x5170U, 0x5171U, 0x5172U, 0x5173U, 0x5174U, 0x5175U, 0x5176U,\n\t0x5177U, 0x5178U, 0x5179U, 0x517AU, 0x5130U, 0x5131U, 0x5132U, 0x5133U,\n\t0x5134U, 0x5135U, 0x5136U, 0x5137U, 0x5138U, 0x5139U, 0x512BU, 0x512FU,\n\t0x5241U, 0x5242U, 0x5243U, 0x5244U, 0x5245U, 0x5246U, 0x5247U, 0x5248U,\n\t0x5249U, 0x524AU, 0x524BU, 0x524CU, 0x524DU, 0x524EU, 0x524FU, 0x5250U,\n\t0x5251U, 0x5252U, 0x5253U, 0x5254U, 0x5255U, 0x5256U, 0x5257U, 0x5258U,\n\t0x5259U, 0x525AU, 0x5261U, 0x5262U, 0x5263U, 0x5264U, 0x5265U, 0x5266U,\n\t0x5267U, 0x5268U, 0x5269U, 0x526AU, 0x526BU, 0x526CU, 0x526DU, 0x526EU,\n\t0x526FU, 0x5270U, 0x5271U, 0x5272U, 0x5273U, 0x5274U, 0x5275U, 0x5276U,\n\t0x5277U, 0x5278U, 0x5279U, 0x527AU, 0x5230U, 0x5231U, 0x5232U, 0x5233U,\n\t0x5234U, 0x5235U, 0x5236U, 0x5237U, 0x5238U, 0x5239U, 0x522BU, 0x522FU,\n\t0x5341U, 0x5342U, 0x5343U, 0x5344U, 0x5345U, 0x5346U, 0x5347U, 0x5348U,\n\t0x5349U, 0x534AU, 0x534BU, 0x534CU, 0x534DU, 0x534EU, 0x534FU, 0x5350U,\n\t0x5351U, 0x5352U, 0x5353U, 0x5354U, 0x5355U, 0x5356U, 0x5357U, 0x5358U,\n\t0x5359U, 0x535AU, 0x5361U, 0x5362U, 0x5363U, 0x5364U, 0x5365U, 0x5366U,\n\t0x5367U, 0x5368U, 0x5369U, 0x536AU, 0x536BU, 0x536CU, 0x536DU, 0x536EU,\n\t0x536FU, 0x5370U, 0x5371U, 0x5372U, 0x5373U, 0x5374U, 0x5375U, 0x5376U,\n\t0x5377U, 0x5378U, 0x5379U, 0x537AU, 0x5330U, 0x5331U, 0x5332U, 0x5333U,\n\t0x5334U, 0x5335U, 0x5336U, 0x5337U, 0x5338U, 0x5339U, 0x532BU, 0x532FU,\n\t0x5441U, 0x5442U, 0x5443U, 0x5444U, 0x5445U, 0x5446U, 0x5447U, 0x5448U,\n\t0x5449U, 0x544AU, 0x544BU, 0x544CU, 0x544DU, 0x544EU, 0x544FU, 0x5450U,\n\t0x5451U, 0x5452U, 0x5453U, 0x5454U, 0x5455U, 0x5456U, 0x5457U, 0x5458U,\n\t0x5459U, 0x545AU, 0x5461U, 0x5462U, 0x5463U, 0x5464U, 0x5465U, 0x5466U,\n\t0x5467U, 0x5468U, 0x5469U, 0x546AU, 0x546BU, 0x546CU, 0x546DU, 0x546EU,\n\t0x546FU, 0x5470U, 0x5471U, 0x5472U, 0x5473U, 0x5474U, 0x5475U, 0x5476U,\n\t0x5477U, 0x5478U, 0x5479U, 0x547AU, 0x5430U, 0x5431U, 0x5432U, 0x5433U,\n\t0x5434U, 0x5435U, 0x5436U, 0x5437U, 0x5438U, 0x5439U, 0x542BU, 0x542FU,\n\t0x5541U, 0x5542U, 0x5543U, 0x5544U, 0x5545U, 0x5546U, 0x5547U, 0x5548U,\n\t0x5549U, 0x554AU, 0x554BU, 0x554CU, 0x554DU, 0x554EU, 0x554FU, 0x5550U,\n\t0x5551U, 0x5552U, 0x5553U, 0x5554U, 0x5555U, 0x5556U, 0x5557U, 0x5558U,\n\t0x5559U, 0x555AU, 0x5561U, 0x5562U, 0x5563U, 0x5564U, 0x5565U, 0x5566U,\n\t0x5567U, 0x5568U, 0x5569U, 0x556AU, 0x556BU, 0x556CU, 0x556DU, 0x556EU,\n\t0x556FU, 0x5570U, 0x5571U, 0x5572U, 0x5573U, 0x5574U, 0x5575U, 0x5576U,\n\t0x5577U, 0x5578U, 0x5579U, 0x557AU, 0x5530U, 0x5531U, 0x5532U, 0x5533U,\n\t0x5534U, 0x5535U, 0x5536U, 0x5537U, 0x5538U, 0x5539U, 0x552BU, 0x552FU,\n\t0x5641U, 0x5642U, 0x5643U, 0x5644U, 0x5645U, 0x5646U, 0x5647U, 0x5648U,\n\t0x5649U, 0x564AU, 0x564BU, 0x564CU, 0x564DU, 0x564EU, 0x564FU, 0x5650U,\n\t0x5651U, 0x5652U, 0x5653U, 0x5654U, 0x5655U, 0x5656U, 0x5657U, 0x5658U,\n\t0x5659U, 0x565AU, 0x5661U, 0x5662U, 0x5663U, 0x5664U, 0x5665U, 0x5666U,\n\t0x5667U, 0x5668U, 0x5669U, 0x566AU, 0x566BU, 0x566CU, 0x566DU, 0x566EU,\n\t0x566FU, 0x5670U, 0x5671U, 0x5672U, 0x5673U, 0x5674U, 0x5675U, 0x5676U,\n\t0x5677U, 0x5678U, 0x5679U, 0x567AU, 0x5630U, 0x5631U, 0x5632U, 0x5633U,\n\t0x5634U, 0x5635U, 0x5636U, 0x5637U, 0x5638U, 0x5639U, 0x562BU, 0x562FU,\n\t0x5741U, 0x5742U, 0x5743U, 0x5744U, 0x5745U, 0x5746U, 0x5747U, 0x5748U,\n\t0x5749U, 0x574AU, 0x574BU, 0x574CU, 0x574DU, 0x574EU, 0x574FU, 0x5750U,\n\t0x5751U, 0x5752U, 0x5753U, 0x5754U, 0x5755U, 0x5756U, 0x5757U, 0x5758U,\n\t0x5759U, 0x575AU, 0x5761U, 0x5762U, 0x5763U, 0x5764U, 0x5765U, 0x5766U,\n\t0x5767U, 0x5768U, 0x5769U, 0x576AU, 0x576BU, 0x576CU, 0x576DU, 0x576EU,\n\t0x576FU, 0x5770U, 0x5771U, 0x5772U, 0x5773U, 0x5774U, 0x5775U, 0x5776U,\n\t0x5777U, 0x5778U, 0x5779U, 0x577AU, 0x5730U, 0x5731U, 0x5732U, 0x5733U,\n\t0x5734U, 0x5735U, 0x5736U, 0x5737U, 0x5738U, 0x5739U, 0x572BU, 0x572FU,\n\t0x5841U, 0x5842U, 0x5843U, 0x5844U, 0x5845U, 0x5846U, 0x5847U, 0x5848U,\n\t0x5849U, 0x584AU, 0x584BU, 0x584CU, 0x584DU, 0x584EU, 0x584FU, 0x5850U,\n\t0x5851U, 0x5852U, 0x5853U, 0x5854U, 0x5855U, 0x5856U, 0x5857U, 0x5858U,\n\t0x5859U, 0x585AU, 0x5861U, 0x5862U, 0x5863U, 0x5864U, 0x5865U, 0x5866U,\n\t0x5867U, 0x5868U, 0x5869U, 0x586AU, 0x586BU, 0x586CU, 0x586DU, 0x586EU,\n\t0x586FU, 0x5870U, 0x5871U, 0x5872U, 0x5873U, 0x5874U, 0x5875U, 0x5876U,\n\t0x5877U, 0x5878U, 0x5879U, 0x587AU, 0x5830U, 0x5831U, 0x5832U, 0x5833U,\n\t0x5834U, 0x5835U, 0x5836U, 0x5837U, 0x5838U, 0x5839U, 0x582BU, 0x582FU,\n\t0x5941U, 0x5942U, 0x5943U, 0x5944U, 0x5945U, 0x5946U, 0x5947U, 0x5948U,\n\t0x5949U, 0x594AU, 0x594BU, 0x594CU, 0x594DU, 0x594EU, 0x594FU, 0x5950U,\n\t0x5951U, 0x5952U, 0x5953U, 0x5954U, 0x5955U, 0x5956U, 0x5957U, 0x5958U,\n\t0x5959U, 0x595AU, 0x5961U, 0x5962U, 0x5963U, 0x5964U, 0x5965U, 0x5966U,\n\t0x5967U, 0x5968U, 0x5969U, 0x596AU, 0x596BU, 0x596CU, 0x596DU, 0x596EU,\n\t0x596FU, 0x5970U, 0x5971U, 0x5972U, 0x5973U, 0x5974U, 0x5975U, 0x5976U,\n\t0x5977U, 0x5978U, 0x5979U, 0x597AU, 0x5930U, 0x5931U, 0x5932U, 0x5933U,\n\t0x5934U, 0x5935U, 0x5936U, 0x5937U, 0x5938U, 0x5939U, 0x592BU, 0x592FU,\n\t0x5A41U, 0x5A42U, 0x5A43U, 0x5A44U, 0x5A45U, 0x5A46U, 0x5A47U, 0x5A48U,\n\t0x5A49U, 0x5A4AU, 0x5A4BU, 0x5A4CU, 0x5A4DU, 0x5A4EU, 0x5A4FU, 0x5A50U,\n\t0x5A51U, 0x5A52U, 0x5A53U, 0x5A54U, 0x5A55U, 0x5A56U, 0x5A57U, 0x5A58U,\n\t0x5A59U, 0x5A5AU, 0x5A61U, 0x5A62U, 0x5A63U, 0x5A64U, 0x5A65U, 0x5A66U,\n\t0x5A67U, 0x5A68U, 0x5A69U, 0x5A6AU, 0x5A6BU, 0x5A6CU, 0x5A6DU, 0x5A6EU,\n\t0x5A6FU, 0x5A70U, 0x5A71U, 0x5A72U, 0x5A73U, 0x5A74U, 0x5A75U, 0x5A76U,\n\t0x5A77U, 0x5A78U, 0x5A79U, 0x5A7AU, 0x5A30U, 0x5A31U, 0x5A32U, 0x5A33U,\n\t0x5A34U, 0x5A35U, 0x5A36U, 0x5A37U, 0x5A38U, 0x5A39U, 0x5A2BU, 0x5A2FU,\n\t0x6141U, 0x6142U, 0x6143U, 0x6144U, 0x6145U, 0x6146U, 0x6147U, 0x6148U,\n\t0x6149U, 0x614AU, 0x614BU, 0x614CU, 0x614DU, 0x614EU, 0x614FU, 0x6150U,\n\t0x6151U, 0x6152U, 0x6153U, 0x6154U, 0x6155U, 0x6156U, 0x6157U, 0x6158U,\n\t0x6159U, 0x615AU, 0x6161U, 0x6162U, 0x6163U, 0x6164U, 0x6165U, 0x6166U,\n\t0x6167U, 0x6168U, 0x6169U, 0x616AU, 0x616BU, 0x616CU, 0x616DU, 0x616EU,\n\t0x616FU, 0x6170U, 0x6171U, 0x6172U, 0x6173U, 0x6174U, 0x6175U, 0x6176U,\n\t0x6177U, 0x6178U, 0x6179U, 0x617AU, 0x6130U, 0x6131U, 0x6132U, 0x6133U,\n\t0x6134U, 0x6135U, 0x6136U, 0x6137U, 0x6138U, 0x6139U, 0x612BU, 0x612FU,\n\t0x6241U, 0x6242U, 0x6243U, 0x6244U, 0x6245U, 0x6246U, 0x6247U, 0x6248U,\n\t0x6249U, 0x624AU, 0x624BU, 0x624CU, 0x624DU, 0x624EU, 0x624FU, 0x6250U,\n\t0x6251U, 0x6252U, 0x6253U, 0x6254U, 0x6255U, 0x6256U, 0x6257U, 0x6258U,\n\t0x6259U, 0x625AU, 0x6261U, 0x6262U, 0x6263U, 0x6264U, 0x6265U, 0x6266U,\n\t0x6267U, 0x6268U, 0x6269U, 0x626AU, 0x626BU, 0x626CU, 0x626DU, 0x626EU,\n\t0x626FU, 0x6270U, 0x6271U, 0x6272U, 0x6273U, 0x6274U, 0x6275U, 0x6276U,\n\t0x6277U, 0x6278U, 0x6279U, 0x627AU, 0x6230U, 0x6231U, 0x6232U, 0x6233U,\n\t0x6234U, 0x6235U, 0x6236U, 0x6237U, 0x6238U, 0x6239U, 0x622BU, 0x622FU,\n\t0x6341U, 0x6342U, 0x6343U, 0x6344U, 0x6345U, 0x6346U, 0x6347U, 0x6348U,\n\t0x6349U, 0x634AU, 0x634BU, 0x634CU, 0x634DU, 0x634EU, 0x634FU, 0x6350U,\n\t0x6351U, 0x6352U, 0x6353U, 0x6354U, 0x6355U, 0x6356U, 0x6357U, 0x6358U,\n\t0x6359U, 0x635AU, 0x6361U, 0x6362U, 0x6363U, 0x6364U, 0x6365U, 0x6366U,\n\t0x6367U, 0x6368U, 0x6369U, 0x636AU, 0x636BU, 0x636CU, 0x636DU, 0x636EU,\n\t0x636FU, 0x6370U, 0x6371U, 0x6372U, 0x6373U, 0x6374U, 0x6375U, 0x6376U,\n\t0x6377U, 0x6378U, 0x6379U, 0x637AU, 0x6330U, 0x6331U, 0x6332U, 0x6333U,\n\t0x6334U, 0x6335U, 0x6336U, 0x6337U, 0x6338U, 0x6339U, 0x632BU, 0x632FU,\n\t0x6441U, 0x6442U, 0x6443U, 0x6444U, 0x6445U, 0x6446U, 0x6447U, 0x6448U,\n\t0x6449U, 0x644AU, 0x644BU, 0x644CU, 0x644DU, 0x644EU, 0x644FU, 0x6450U,\n\t0x6451U, 0x6452U, 0x6453U, 0x6454U, 0x6455U, 0x6456U, 0x6457U, 0x6458U,\n\t0x6459U, 0x645AU, 0x6461U, 0x6462U, 0x6463U, 0x6464U, 0x6465U, 0x6466U,\n\t0x6467U, 0x6468U, 0x6469U, 0x646AU, 0x646BU, 0x646CU, 0x646DU, 0x646EU,\n\t0x646FU, 0x6470U, 0x6471U, 0x6472U, 0x6473U, 0x6474U, 0x6475U, 0x6476U,\n\t0x6477U, 0x6478U, 0x6479U, 0x647AU, 0x6430U, 0x6431U, 0x6432U, 0x6433U,\n\t0x6434U, 0x6435U, 0x6436U, 0x6437U, 0x6438U, 0x6439U, 0x642BU, 0x642FU,\n\t0x6541U, 0x6542U, 0x6543U, 0x6544U, 0x6545U, 0x6546U, 0x6547U, 0x6548U,\n\t0x6549U, 0x654AU, 0x654BU, 0x654CU, 0x654DU, 0x654EU, 0x654FU, 0x6550U,\n\t0x6551U, 0x6552U, 0x6553U, 0x6554U, 0x6555U, 0x6556U, 0x6557U, 0x6558U,\n\t0x6559U, 0x655AU, 0x6561U, 0x6562U, 0x6563U, 0x6564U, 0x6565U, 0x6566U,\n\t0x6567U, 0x6568U, 0x6569U, 0x656AU, 0x656BU, 0x656CU, 0x656DU, 0x656EU,\n\t0x656FU, 0x6570U, 0x6571U, 0x6572U, 0x6573U, 0x6574U, 0x6575U, 0x6576U,\n\t0x6577U, 0x6578U, 0x6579U, 0x657AU, 0x6530U, 0x6531U, 0x6532U, 0x6533U,\n\t0x6534U, 0x6535U, 0x6536U, 0x6537U, 0x6538U, 0x6539U, 0x652BU, 0x652FU,\n\t0x6641U, 0x6642U, 0x6643U, 0x6644U, 0x6645U, 0x6646U, 0x6647U, 0x6648U,\n\t0x6649U, 0x664AU, 0x664BU, 0x664CU, 0x664DU, 0x664EU, 0x664FU, 0x6650U,\n\t0x6651U, 0x6652U, 0x6653U, 0x6654U, 0x6655U, 0x6656U, 0x6657U, 0x6658U,\n\t0x6659U, 0x665AU, 0x6661U, 0x6662U, 0x6663U, 0x6664U, 0x6665U, 0x6666U,\n\t0x6667U, 0x6668U, 0x6669U, 0x666AU, 0x666BU, 0x666CU, 0x666DU, 0x666EU,\n\t0x666FU, 0x6670U, 0x6671U, 0x6672U, 0x6673U, 0x6674U, 0x6675U, 0x6676U,\n\t0x6677U, 0x6678U, 0x6679U, 0x667AU, 0x6630U, 0x6631U, 0x6632U, 0x6633U,\n\t0x6634U, 0x6635U, 0x6636U, 0x6637U, 0x6638U, 0x6639U, 0x662BU, 0x662FU,\n\t0x6741U, 0x6742U, 0x6743U, 0x6744U, 0x6745U, 0x6746U, 0x6747U, 0x6748U,\n\t0x6749U, 0x674AU, 0x674BU, 0x674CU, 0x674DU, 0x674EU, 0x674FU, 0x6750U,\n\t0x6751U, 0x6752U, 0x6753U, 0x6754U, 0x6755U, 0x6756U, 0x6757U, 0x6758U,\n\t0x6759U, 0x675AU, 0x6761U, 0x6762U, 0x6763U, 0x6764U, 0x6765U, 0x6766U,\n\t0x6767U, 0x6768U, 0x6769U, 0x676AU, 0x676BU, 0x676CU, 0x676DU, 0x676EU,\n\t0x676FU, 0x6770U, 0x6771U, 0x6772U, 0x6773U, 0x6774U, 0x6775U, 0x6776U,\n\t0x6777U, 0x6778U, 0x6779U, 0x677AU, 0x6730U, 0x6731U, 0x6732U, 0x6733U,\n\t0x6734U, 0x6735U, 0x6736U, 0x6737U, 0x6738U, 0x6739U, 0x672BU, 0x672FU,\n\t0x6841U, 0x6842U, 0x6843U, 0x6844U, 0x6845U, 0x6846U, 0x6847U, 0x6848U,\n\t0x6849U, 0x684AU, 0x684BU, 0x684CU, 0x684DU, 0x684EU, 0x684FU, 0x6850U,\n\t0x6851U, 0x6852U, 0x6853U, 0x6854U, 0x6855U, 0x6856U, 0x6857U, 0x6858U,\n\t0x6859U, 0x685AU, 0x6861U, 0x6862U, 0x6863U, 0x6864U, 0x6865U, 0x6866U,\n\t0x6867U, 0x6868U, 0x6869U, 0x686AU, 0x686BU, 0x686CU, 0x686DU, 0x686EU,\n\t0x686FU, 0x6870U, 0x6871U, 0x6872U, 0x6873U, 0x6874U, 0x6875U, 0x6876U,\n\t0x6877U, 0x6878U, 0x6879U, 0x687AU, 0x6830U, 0x6831U, 0x6832U, 0x6833U,\n\t0x6834U, 0x6835U, 0x6836U, 0x6837U, 0x6838U, 0x6839U, 0x682BU, 0x682FU,\n\t0x6941U, 0x6942U, 0x6943U, 0x6944U, 0x6945U, 0x6946U, 0x6947U, 0x6948U,\n\t0x6949U, 0x694AU, 0x694BU, 0x694CU, 0x694DU, 0x694EU, 0x694FU, 0x6950U,\n\t0x6951U, 0x6952U, 0x6953U, 0x6954U, 0x6955U, 0x6956U, 0x6957U, 0x6958U,\n\t0x6959U, 0x695AU, 0x6961U, 0x6962U, 0x6963U, 0x6964U, 0x6965U, 0x6966U,\n\t0x6967U, 0x6968U, 0x6969U, 0x696AU, 0x696BU, 0x696CU, 0x696DU, 0x696EU,\n\t0x696FU, 0x6970U, 0x6971U, 0x6972U, 0x6973U, 0x6974U, 0x6975U, 0x6976U,\n\t0x6977U, 0x6978U, 0x6979U, 0x697AU, 0x6930U, 0x6931U, 0x6932U, 0x6933U,\n\t0x6934U, 0x6935U, 0x6936U, 0x6937U, 0x6938U, 0x6939U, 0x692BU, 0x692FU,\n\t0x6A41U, 0x6A42U, 0x6A43U, 0x6A44U, 0x6A45U, 0x6A46U, 0x6A47U, 0x6A48U,\n\t0x6A49U, 0x6A4AU, 0x6A4BU, 0x6A4CU, 0x6A4DU, 0x6A4EU, 0x6A4FU, 0x6A50U,\n\t0x6A51U, 0x6A52U, 0x6A53U, 0x6A54U, 0x6A55U, 0x6A56U, 0x6A57U, 0x6A58U,\n\t0x6A59U, 0x6A5AU, 0x6A61U, 0x6A62U, 0x6A63U, 0x6A64U, 0x6A65U, 0x6A66U,\n\t0x6A67U, 0x6A68U, 0x6A69U, 0x6A6AU, 0x6A6BU, 0x6A6CU, 0x6A6DU, 0x6A6EU,\n\t0x6A6FU, 0x6A70U, 0x6A71U, 0x6A72U, 0x6A73U, 0x6A74U, 0x6A75U, 0x6A76U,\n\t0x6A77U, 0x6A78U, 0x6A79U, 0x6A7AU, 0x6A30U, 0x6A31U, 0x6A32U, 0x6A33U,\n\t0x6A34U, 0x6A35U, 0x6A36U, 0x6A37U, 0x6A38U, 0x6A39U, 0x6A2BU, 0x6A2FU,\n\t0x6B41U, 0x6B42U, 0x6B43U, 0x6B44U, 0x6B45U, 0x6B46U, 0x6B47U, 0x6B48U,\n\t0x6B49U, 0x6B4AU, 0x6B4BU, 0x6B4CU, 0x6B4DU, 0x6B4EU, 0x6B4FU, 0x6B50U,\n\t0x6B51U, 0x6B52U, 0x6B53U, 0x6B54U, 0x6B55U, 0x6B56U, 0x6B57U, 0x6B58U,\n\t0x6B59U, 0x6B5AU, 0x6B61U, 0x6B62U, 0x6B63U, 0x6B64U, 0x6B65U, 0x6B66U,\n\t0x6B67U, 0x6B68U, 0x6B69U, 0x6B6AU, 0x6B6BU, 0x6B6CU, 0x6B6DU, 0x6B6EU,\n\t0x6B6FU, 0x6B70U, 0x6B71U, 0x6B72U, 0x6B73U, 0x6B74U, 0x6B75U, 0x6B76U,\n\t0x6B77U, 0x6B78U, 0x6B79U, 0x6B7AU, 0x6B30U, 0x6B31U, 0x6B32U, 0x6B33U,\n\t0x6B34U, 0x6B35U, 0x6B36U, 0x6B37U, 0x6B38U, 0x6B39U, 0x6B2BU, 0x6B2FU,\n\t0x6C41U, 0x6C42U, 0x6C43U, 0x6C44U, 0x6C45U, 0x6C46U, 0x6C47U, 0x6C48U,\n\t0x6C49U, 0x6C4AU, 0x6C4BU, 0x6C4CU, 0x6C4DU, 0x6C4EU, 0x6C4FU, 0x6C50U,\n\t0x6C51U, 0x6C52U, 0x6C53U, 0x6C54U, 0x6C55U, 0x6C56U, 0x6C57U, 0x6C58U,\n\t0x6C59U, 0x6C5AU, 0x6C61U, 0x6C62U, 0x6C63U, 0x6C64U, 0x6C65U, 0x6C66U,\n\t0x6C67U, 0x6C68U, 0x6C69U, 0x6C6AU, 0x6C6BU, 0x6C6CU, 0x6C6DU, 0x6C6EU,\n\t0x6C6FU, 0x6C70U, 0x6C71U, 0x6C72U, 0x6C73U, 0x6C74U, 0x6C75U, 0x6C76U,\n\t0x6C77U, 0x6C78U, 0x6C79U, 0x6C7AU, 0x6C30U, 0x6C31U, 0x6C32U, 0x6C33U,\n\t0x6C34U, 0x6C35U, 0x6C36U, 0x6C37U, 0x6C38U, 0x6C39U, 0x6C2BU, 0x6C2FU,\n\t0x6D41U, 0x6D42U, 0x6D43U, 0x6D44U, 0x6D45U, 0x6D46U, 0x6D47U, 0x6D48U,\n\t0x6D49U, 0x6D4AU, 0x6D4BU, 0x6D4CU, 0x6D4DU, 0x6D4EU, 0x6D4FU, 0x6D50U,\n\t0x6D51U, 0x6D52U, 0x6D53U, 0x6D54U, 0x6D55U, 0x6D56U, 0x6D57U, 0x6D58U,\n\t0x6D59U, 0x6D5AU, 0x6D61U, 0x6D62U, 0x6D63U, 0x6D64U, 0x6D65U, 0x6D66U,\n\t0x6D67U, 0x6D68U, 0x6D69U, 0x6D6AU, 0x6D6BU, 0x6D6CU, 0x6D6DU, 0x6D6EU,\n\t0x6D6FU, 0x6D70U, 0x6D71U, 0x6D72U, 0x6D73U, 0x6D74U, 0x6D75U, 0x6D76U,\n\t0x6D77U, 0x6D78U, 0x6D79U, 0x6D7AU, 0x6D30U, 0x6D31U, 0x6D32U, 0x6D33U,\n\t0x6D34U, 0x6D35U, 0x6D36U, 0x6D37U, 0x6D38U, 0x6D39U, 0x6D2BU, 0x6D2FU,\n\t0x6E41U, 0x6E42U, 0x6E43U, 0x6E44U, 0x6E45U, 0x6E46U, 0x6E47U, 0x6E48U,\n\t0x6E49U, 0x6E4AU, 0x6E4BU, 0x6E4CU, 0x6E4DU, 0x6E4EU, 0x6E4FU, 0x6E50U,\n\t0x6E51U, 0x6E52U, 0x6E53U, 0x6E54U, 0x6E55U, 0x6E56U, 0x6E57U, 0x6E58U,\n\t0x6E59U, 0x6E5AU, 0x6E61U, 0x6E62U, 0x6E63U, 0x6E64U, 0x6E65U, 0x6E66U,\n\t0x6E67U, 0x6E68U, 0x6E69U, 0x6E6AU, 0x6E6BU, 0x6E6CU, 0x6E6DU, 0x6E6EU,\n\t0x6E6FU, 0x6E70U, 0x6E71U, 0x6E72U, 0x6E73U, 0x6E74U, 0x6E75U, 0x6E76U,\n\t0x6E77U, 0x6E78U, 0x6E79U, 0x6E7AU, 0x6E30U, 0x6E31U, 0x6E32U, 0x6E33U,\n\t0x6E34U, 0x6E35U, 0x6E36U, 0x6E37U, 0x6E38U, 0x6E39U, 0x6E2BU, 0x6E2FU,\n\t0x6F41U, 0x6F42U, 0x6F43U, 0x6F44U, 0x6F45U, 0x6F46U, 0x6F47U, 0x6F48U,\n\t0x6F49U, 0x6F4AU, 0x6F4BU, 0x6F4CU, 0x6F4DU, 0x6F4EU, 0x6F4FU, 0x6F50U,\n\t0x6F51U, 0x6F52U, 0x6F53U, 0x6F54U, 0x6F55U, 0x6F56U, 0x6F57U, 0x6F58U,\n\t0x6F59U, 0x6F5AU, 0x6F61U, 0x6F62U, 0x6F63U, 0x6F64U, 0x6F65U, 0x6F66U,\n\t0x6F67U, 0x6F68U, 0x6F69U, 0x6F6AU, 0x6F6BU, 0x6F6CU, 0x6F6DU, 0x6F6EU,\n\t0x6F6FU, 0x6F70U, 0x6F71U, 0x6F72U, 0x6F73U, 0x6F74U, 0x6F75U, 0x6F76U,\n\t0x6F77U, 0x6F78U, 0x6F79U, 0x6F7AU, 0x6F30U, 0x6F31U, 0x6F32U, 0x6F33U,\n\t0x6F34U, 0x6F35U, 0x6F36U, 0x6F37U, 0x6F38U, 0x6F39U, 0x6F2BU, 0x6F2FU,\n\t0x7041U, 0x7042U, 0x7043U, 0x7044U, 0x7045U, 0x7046U, 0x7047U, 0x7048U,\n\t0x7049U, 0x704AU, 0x704BU, 0x704CU, 0x704DU, 0x704EU, 0x704FU, 0x7050U,\n\t0x7051U, 0x7052U, 0x7053U, 0x7054U, 0x7055U, 0x7056U, 0x7057U, 0x7058U,\n\t0x7059U, 0x705AU, 0x7061U, 0x7062U, 0x7063U, 0x7064U, 0x7065U, 0x7066U,\n\t0x7067U, 0x7068U, 0x7069U, 0x706AU, 0x706BU, 0x706CU, 0x706DU, 0x706EU,\n\t0x706FU, 0x7070U, 0x7071U, 0x7072U, 0x7073U, 0x7074U, 0x7075U, 0x7076U,\n\t0x7077U, 0x7078U, 0x7079U, 0x707AU, 0x7030U, 0x7031U, 0x7032U, 0x7033U,\n\t0x7034U, 0x7035U, 0x7036U, 0x7037U, 0x7038U, 0x7039U, 0x702BU, 0x702FU,\n\t0x7141U, 0x7142U, 0x7143U, 0x7144U, 0x7145U, 0x7146U, 0x7147U, 0x7148U,\n\t0x7149U, 0x714AU, 0x714BU, 0x714CU, 0x714DU, 0x714EU, 0x714FU, 0x7150U,\n\t0x7151U, 0x7152U, 0x7153U, 0x7154U, 0x7155U, 0x7156U, 0x7157U, 0x7158U,\n\t0x7159U, 0x715AU, 0x7161U, 0x7162U, 0x7163U, 0x7164U, 0x7165U, 0x7166U,\n\t0x7167U, 0x7168U, 0x7169U, 0x716AU, 0x716BU, 0x716CU, 0x716DU, 0x716EU,\n\t0x716FU, 0x7170U, 0x7171U, 0x7172U, 0x7173U, 0x7174U, 0x7175U, 0x7176U,\n\t0x7177U, 0x7178U, 0x7179U, 0x717AU, 0x7130U, 0x7131U, 0x7132U, 0x7133U,\n\t0x7134U, 0x7135U, 0x7136U, 0x7137U, 0x7138U, 0x7139U, 0x712BU, 0x712FU,\n\t0x7241U, 0x7242U, 0x7243U, 0x7244U, 0x7245U, 0x7246U, 0x7247U, 0x7248U,\n\t0x7249U, 0x724AU, 0x724BU, 0x724CU, 0x724DU, 0x724EU, 0x724FU, 0x7250U,\n\t0x7251U, 0x7252U, 0x7253U, 0x7254U, 0x7255U, 0x7256U, 0x7257U, 0x7258U,\n\t0x7259U, 0x725AU, 0x7261U, 0x7262U, 0x7263U, 0x7264U, 0x7265U, 0x7266U,\n\t0x7267U, 0x7268U, 0x7269U, 0x726AU, 0x726BU, 0x726CU, 0x726DU, 0x726EU,\n\t0x726FU, 0x7270U, 0x7271U, 0x7272U, 0x7273U, 0x7274U, 0x7275U, 0x7276U,\n\t0x7277U, 0x7278U, 0x7279U, 0x727AU, 0x7230U, 0x7231U, 0x7232U, 0x7233U,\n\t0x7234U, 0x7235U, 0x7236U, 0x7237U, 0x7238U, 0x7239U, 0x722BU, 0x722FU,\n\t0x7341U, 0x7342U, 0x7343U, 0x7344U, 0x7345U, 0x7346U, 0x7347U, 0x7348U,\n\t0x7349U, 0x734AU, 0x734BU, 0x734CU, 0x734DU, 0x734EU, 0x734FU, 0x7350U,\n\t0x7351U, 0x7352U, 0x7353U, 0x7354U, 0x7355U, 0x7356U, 0x7357U, 0x7358U,\n\t0x7359U, 0x735AU, 0x7361U, 0x7362U, 0x7363U, 0x7364U, 0x7365U, 0x7366U,\n\t0x7367U, 0x7368U, 0x7369U, 0x736AU, 0x736BU, 0x736CU, 0x736DU, 0x736EU,\n\t0x736FU, 0x7370U, 0x7371U, 0x7372U, 0x7373U, 0x7374U, 0x7375U, 0x7376U,\n\t0x7377U, 0x7378U, 0x7379U, 0x737AU, 0x7330U, 0x7331U, 0x7332U, 0x7333U,\n\t0x7334U, 0x7335U, 0x7336U, 0x7337U, 0x7338U, 0x7339U, 0x732BU, 0x732FU,\n\t0x7441U, 0x7442U, 0x7443U, 0x7444U, 0x7445U, 0x7446U, 0x7447U, 0x7448U,\n\t0x7449U, 0x744AU, 0x744BU, 0x744CU, 0x744DU, 0x744EU, 0x744FU, 0x7450U,\n\t0x7451U, 0x7452U, 0x7453U, 0x7454U, 0x7455U, 0x7456U, 0x7457U, 0x7458U,\n\t0x7459U, 0x745AU, 0x7461U, 0x7462U, 0x7463U, 0x7464U, 0x7465U, 0x7466U,\n\t0x7467U, 0x7468U, 0x7469U, 0x746AU, 0x746BU, 0x746CU, 0x746DU, 0x746EU,\n\t0x746FU, 0x7470U, 0x7471U, 0x7472U, 0x7473U, 0x7474U, 0x7475U, 0x7476U,\n\t0x7477U, 0x7478U, 0x7479U, 0x747AU, 0x7430U, 0x7431U, 0x7432U, 0x7433U,\n\t0x7434U, 0x7435U, 0x7436U, 0x7437U, 0x7438U, 0x7439U, 0x742BU, 0x742FU,\n\t0x7541U, 0x7542U, 0x7543U, 0x7544U, 0x7545U, 0x7546U, 0x7547U, 0x7548U,\n\t0x7549U, 0x754AU, 0x754BU, 0x754CU, 0x754DU, 0x754EU, 0x754FU, 0x7550U,\n\t0x7551U, 0x7552U, 0x7553U, 0x7554U, 0x7555U, 0x7556U, 0x7557U, 0x7558U,\n\t0x7559U, 0x755AU, 0x7561U, 0x7562U, 0x7563U, 0x7564U, 0x7565U, 0x7566U,\n\t0x7567U, 0x7568U, 0x7569U, 0x756AU, 0x756BU, 0x756CU, 0x756DU, 0x756EU,\n\t0x756FU, 0x7570U, 0x7571U, 0x7572U, 0x7573U, 0x7574U, 0x7575U, 0x7576U,\n\t0x7577U, 0x7578U, 0x7579U, 0x757AU, 0x7530U, 0x7531U, 0x7532U, 0x7533U,\n\t0x7534U, 0x7535U, 0x7536U, 0x7537U, 0x7538U, 0x7539U, 0x752BU, 0x752FU,\n\t0x7641U, 0x7642U, 0x7643U, 0x7644U, 0x7645U, 0x7646U, 0x7647U, 0x7648U,\n\t0x7649U, 0x764AU, 0x764BU, 0x764CU, 0x764DU, 0x764EU, 0x764FU, 0x7650U,\n\t0x7651U, 0x7652U, 0x7653U, 0x7654U, 0x7655U, 0x7656U, 0x7657U, 0x7658U,\n\t0x7659U, 0x765AU, 0x7661U, 0x7662U, 0x7663U, 0x7664U, 0x7665U, 0x7666U,\n\t0x7667U, 0x7668U, 0x7669U, 0x766AU, 0x766BU, 0x766CU, 0x766DU, 0x766EU,\n\t0x766FU, 0x7670U, 0x7671U, 0x7672U, 0x7673U, 0x7674U, 0x7675U, 0x7676U,\n\t0x7677U, 0x7678U, 0x7679U, 0x767AU, 0x7630U, 0x7631U, 0x7632U, 0x7633U,\n\t0x7634U, 0x7635U, 0x7636U, 0x7637U, 0x7638U, 0x7639U, 0x762BU, 0x762FU,\n\t0x7741U, 0x7742U, 0x7743U, 0x7744U, 0x7745U, 0x7746U, 0x7747U, 0x7748U,\n\t0x7749U, 0x774AU, 0x774BU, 0x774CU, 0x774DU, 0x774EU, 0x774FU, 0x7750U,\n\t0x7751U, 0x7752U, 0x7753U, 0x7754U, 0x7755U, 0x7756U, 0x7757U, 0x7758U,\n\t0x7759U, 0x775AU, 0x7761U, 0x7762U, 0x7763U, 0x7764U, 0x7765U, 0x7766U,\n\t0x7767U, 0x7768U, 0x7769U, 0x776AU, 0x776BU, 0x776CU, 0x776DU, 0x776EU,\n\t0x776FU, 0x7770U, 0x7771U, 0x7772U, 0x7773U, 0x7774U, 0x7775U, 0x7776U,\n\t0x7777U, 0x7778U, 0x7779U, 0x777AU, 0x7730U, 0x7731U, 0x7732U, 0x7733U,\n\t0x7734U, 0x7735U, 0x7736U, 0x7737U, 0x7738U, 0x7739U, 0x772BU, 0x772FU,\n\t0x7841U, 0x7842U, 0x7843U, 0x7844U, 0x7845U, 0x7846U, 0x7847U, 0x7848U,\n\t0x7849U, 0x784AU, 0x784BU, 0x784CU, 0x784DU, 0x784EU, 0x784FU, 0x7850U,\n\t0x7851U, 0x7852U, 0x7853U, 0x7854U, 0x7855U, 0x7856U, 0x7857U, 0x7858U,\n\t0x7859U, 0x785AU, 0x7861U, 0x7862U, 0x7863U, 0x7864U, 0x7865U, 0x7866U,\n\t0x7867U, 0x7868U, 0x7869U, 0x786AU, 0x786BU, 0x786CU, 0x786DU, 0x786EU,\n\t0x786FU, 0x7870U, 0x7871U, 0x7872U, 0x7873U, 0x7874U, 0x7875U, 0x7876U,\n\t0x7877U, 0x7878U, 0x7879U, 0x787AU, 0x7830U, 0x7831U, 0x7832U, 0x7833U,\n\t0x7834U, 0x7835U, 0x7836U, 0x7837U, 0x7838U, 0x7839U, 0x782BU, 0x782FU,\n\t0x7941U, 0x7942U, 0x7943U, 0x7944U, 0x7945U, 0x7946U, 0x7947U, 0x7948U,\n\t0x7949U, 0x794AU, 0x794BU, 0x794CU, 0x794DU, 0x794EU, 0x794FU, 0x7950U,\n\t0x7951U, 0x7952U, 0x7953U, 0x7954U, 0x7955U, 0x7956U, 0x7957U, 0x7958U,\n\t0x7959U, 0x795AU, 0x7961U, 0x7962U, 0x7963U, 0x7964U, 0x7965U, 0x7966U,\n\t0x7967U, 0x7968U, 0x7969U, 0x796AU, 0x796BU, 0x796CU, 0x796DU, 0x796EU,\n\t0x796FU, 0x7970U, 0x7971U, 0x7972U, 0x7973U, 0x7974U, 0x7975U, 0x7976U,\n\t0x7977U, 0x7978U, 0x7979U, 0x797AU, 0x7930U, 0x7931U, 0x7932U, 0x7933U,\n\t0x7934U, 0x7935U, 0x7936U, 0x7937U, 0x7938U, 0x7939U, 0x792BU, 0x792FU,\n\t0x7A41U, 0x7A42U, 0x7A43U, 0x7A44U, 0x7A45U, 0x7A46U, 0x7A47U, 0x7A48U,\n\t0x7A49U, 0x7A4AU, 0x7A4BU, 0x7A4CU, 0x7A4DU, 0x7A4EU, 0x7A4FU, 0x7A50U,\n\t0x7A51U, 0x7A52U, 0x7A53U, 0x7A54U, 0x7A55U, 0x7A56U, 0x7A57U, 0x7A58U,\n\t0x7A59U, 0x7A5AU, 0x7A61U, 0x7A62U, 0x7A63U, 0x7A64U, 0x7A65U, 0x7A66U,\n\t0x7A67U, 0x7A68U, 0x7A69U, 0x7A6AU, 0x7A6BU, 0x7A6CU, 0x7A6DU, 0x7A6EU,\n\t0x7A6FU, 0x7A70U, 0x7A71U, 0x7A72U, 0x7A73U, 0x7A74U, 0x7A75U, 0x7A76U,\n\t0x7A77U, 0x7A78U, 0x7A79U, 0x7A7AU, 0x7A30U, 0x7A31U, 0x7A32U, 0x7A33U,\n\t0x7A34U, 0x7A35U, 0x7A36U, 0x7A37U, 0x7A38U, 0x7A39U, 0x7A2BU, 0x7A2FU,\n\t0x3041U, 0x3042U, 0x3043U, 0x3044U, 0x3045U, 0x3046U, 0x3047U, 0x3048U,\n\t0x3049U, 0x304AU, 0x304BU, 0x304CU, 0x304DU, 0x304EU, 0x304FU, 0x3050U,\n\t0x3051U, 0x3052U, 0x3053U, 0x3054U, 0x3055U, 0x3056U, 0x3057U, 0x3058U,\n\t0x3059U, 0x305AU, 0x3061U, 0x3062U, 0x3063U, 0x3064U, 0x3065U, 0x3066U,\n\t0x3067U, 0x3068U, 0x3069U, 0x306AU, 0x306BU, 0x306CU, 0x306DU, 0x306EU,\n\t0x306FU, 0x3070U, 0x3071U, 0x3072U, 0x3073U, 0x3074U, 0x3075U, 0x3076U,\n\t0x3077U, 0x3078U, 0x3079U, 0x307AU, 0x3030U, 0x3031U, 0x3032U, 0x3033U,\n\t0x3034U, 0x3035U, 0x3036U, 0x3037U, 0x3038U, 0x3039U, 0x302BU, 0x302FU,\n\t0x3141U, 0x3142U, 0x3143U, 0x3144U, 0x3145U, 0x3146U, 0x3147U, 0x3148U,\n\t0x3149U, 0x314AU, 0x314BU, 0x314CU, 0x314DU, 0x314EU, 0x314FU, 0x3150U,\n\t0x3151U, 0x3152U, 0x3153U, 0x3154U, 0x3155U, 0x3156U, 0x3157U, 0x3158U,\n\t0x3159U, 0x315AU, 0x3161U, 0x3162U, 0x3163U, 0x3164U, 0x3165U, 0x3166U,\n\t0x3167U, 0x3168U, 0x3169U, 0x316AU, 0x316BU, 0x316CU, 0x316DU, 0x316EU,\n\t0x316FU, 0x3170U, 0x3171U, 0x3172U, 0x3173U, 0x3174U, 0x3175U, 0x3176U,\n\t0x3177U, 0x3178U, 0x3179U, 0x317AU, 0x3130U, 0x3131U, 0x3132U, 0x3133U,\n\t0x3134U, 0x3135U, 0x3136U, 0x3137U, 0x3138U, 0x3139U, 0x312BU, 0x312FU,\n\t0x3241U, 0x3242U, 0x3243U, 0x3244U, 0x3245U, 0x3246U, 0x3247U, 0x3248U,\n\t0x3249U, 0x324AU, 0x324BU, 0x324CU, 0x324DU, 0x324EU, 0x324FU, 0x3250U,\n\t0x3251U, 0x3252U, 0x3253U, 0x3254U, 0x3255U, 0x3256U, 0x3257U, 0x3258U,\n\t0x3259U, 0x325AU, 0x3261U, 0x3262U, 0x3263U, 0x3264U, 0x3265U, 0x3266U,\n\t0x3267U, 0x3268U, 0x3269U, 0x326AU, 0x326BU, 0x326CU, 0x326DU, 0x326EU,\n\t0x326FU, 0x3270U, 0x3271U, 0x3272U, 0x3273U, 0x3274U, 0x3275U, 0x3276U,\n\t0x3277U, 0x3278U, 0x3279U, 0x327AU, 0x3230U, 0x3231U, 0x3232U, 0x3233U,\n\t0x3234U, 0x3235U, 0x3236U, 0x3237U, 0x3238U, 0x3239U, 0x322BU, 0x322FU,\n\t0x3341U, 0x3342U, 0x3343U, 0x3344U, 0x3345U, 0x3346U, 0x3347U, 0x3348U,\n\t0x3349U, 0x334AU, 0x334BU, 0x334CU, 0x334DU, 0x334EU, 0x334FU, 0x3350U,\n\t0x3351U, 0x3352U, 0x3353U, 0x3354U, 0x3355U, 0x3356U, 0x3357U, 0x3358U,\n\t0x3359U, 0x335AU, 0x3361U, 0x3362U, 0x3363U, 0x3364U, 0x3365U, 0x3366U,\n\t0x3367U, 0x3368U, 0x3369U, 0x336AU, 0x336BU, 0x336CU, 0x336DU, 0x336EU,\n\t0x336FU, 0x3370U, 0x3371U, 0x3372U, 0x3373U, 0x3374U, 0x3375U, 0x3376U,\n\t0x3377U, 0x3378U, 0x3379U, 0x337AU, 0x3330U, 0x3331U, 0x3332U, 0x3333U,\n\t0x3334U, 0x3335U, 0x3336U, 0x3337U, 0x3338U, 0x3339U, 0x332BU, 0x332FU,\n\t0x3441U, 0x3442U, 0x3443U, 0x3444U, 0x3445U, 0x3446U, 0x3447U, 0x3448U,\n\t0x3449U, 0x344AU, 0x344BU, 0x344CU, 0x344DU, 0x344EU, 0x344FU, 0x3450U,\n\t0x3451U, 0x3452U, 0x3453U, 0x3454U, 0x3455U, 0x3456U, 0x3457U, 0x3458U,\n\t0x3459U, 0x345AU, 0x3461U, 0x3462U, 0x3463U, 0x3464U, 0x3465U, 0x3466U,\n\t0x3467U, 0x3468U, 0x3469U, 0x346AU, 0x346BU, 0x346CU, 0x346DU, 0x346EU,\n\t0x346FU, 0x3470U, 0x3471U, 0x3472U, 0x3473U, 0x3474U, 0x3475U, 0x3476U,\n\t0x3477U, 0x3478U, 0x3479U, 0x347AU, 0x3430U, 0x3431U, 0x3432U, 0x3433U,\n\t0x3434U, 0x3435U, 0x3436U, 0x3437U, 0x3438U, 0x3439U, 0x342BU, 0x342FU,\n\t0x3541U, 0x3542U, 0x3543U, 0x3544U, 0x3545U, 0x3546U, 0x3547U, 0x3548U,\n\t0x3549U, 0x354AU, 0x354BU, 0x354CU, 0x354DU, 0x354EU, 0x354FU, 0x3550U,\n\t0x3551U, 0x3552U, 0x3553U, 0x3554U, 0x3555U, 0x3556U, 0x3557U, 0x3558U,\n\t0x3559U, 0x355AU, 0x3561U, 0x3562U, 0x3563U, 0x3564U, 0x3565U, 0x3566U,\n\t0x3567U, 0x3568U, 0x3569U, 0x356AU, 0x356BU, 0x356CU, 0x356DU, 0x356EU,\n\t0x356FU, 0x3570U, 0x3571U, 0x3572U, 0x3573U, 0x3574U, 0x3575U, 0x3576U,\n\t0x3577U, 0x3578U, 0x3579U, 0x357AU, 0x3530U, 0x3531U, 0x3532U, 0x3533U,\n\t0x3534U, 0x3535U, 0x3536U, 0x3537U, 0x3538U, 0x3539U, 0x352BU, 0x352FU,\n\t0x3641U, 0x3642U, 0x3643U, 0x3644U, 0x3645U, 0x3646U, 0x3647U, 0x3648U,\n\t0x3649U, 0x364AU, 0x364BU, 0x364CU, 0x364DU, 0x364EU, 0x364FU, 0x3650U,\n\t0x3651U, 0x3652U, 0x3653U, 0x3654U, 0x3655U, 0x3656U, 0x3657U, 0x3658U,\n\t0x3659U, 0x365AU, 0x3661U, 0x3662U, 0x3663U, 0x3664U, 0x3665U, 0x3666U,\n\t0x3667U, 0x3668U, 0x3669U, 0x366AU, 0x366BU, 0x366CU, 0x366DU, 0x366EU,\n\t0x366FU, 0x3670U, 0x3671U, 0x3672U, 0x3673U, 0x3674U, 0x3675U, 0x3676U,\n\t0x3677U, 0x3678U, 0x3679U, 0x367AU, 0x3630U, 0x3631U, 0x3632U, 0x3633U,\n\t0x3634U, 0x3635U, 0x3636U, 0x3637U, 0x3638U, 0x3639U, 0x362BU, 0x362FU,\n\t0x3741U, 0x3742U, 0x3743U, 0x3744U, 0x3745U, 0x3746U, 0x3747U, 0x3748U,\n\t0x3749U, 0x374AU, 0x374BU, 0x374CU, 0x374DU, 0x374EU, 0x374FU, 0x3750U,\n\t0x3751U, 0x3752U, 0x3753U, 0x3754U, 0x3755U, 0x3756U, 0x3757U, 0x3758U,\n\t0x3759U, 0x375AU, 0x3761U, 0x3762U, 0x3763U, 0x3764U, 0x3765U, 0x3766U,\n\t0x3767U, 0x3768U, 0x3769U, 0x376AU, 0x376BU, 0x376CU, 0x376DU, 0x376EU,\n\t0x376FU, 0x3770U, 0x3771U, 0x3772U, 0x3773U, 0x3774U, 0x3775U, 0x3776U,\n\t0x3777U, 0x3778U, 0x3779U, 0x377AU, 0x3730U, 0x3731U, 0x3732U, 0x3733U,\n\t0x3734U, 0x3735U, 0x3736U, 0x3737U, 0x3738U, 0x3739U, 0x372BU, 0x372FU,\n\t0x3841U, 0x3842U, 0x3843U, 0x3844U, 0x3845U, 0x3846U, 0x3847U, 0x3848U,\n\t0x3849U, 0x384AU, 0x384BU, 0x384CU, 0x384DU, 0x384EU, 0x384FU, 0x3850U,\n\t0x3851U, 0x3852U, 0x3853U, 0x3854U, 0x3855U, 0x3856U, 0x3857U, 0x3858U,\n\t0x3859U, 0x385AU, 0x3861U, 0x3862U, 0x3863U, 0x3864U, 0x3865U, 0x3866U,\n\t0x3867U, 0x3868U, 0x3869U, 0x386AU, 0x386BU, 0x386CU, 0x386DU, 0x386EU,\n\t0x386FU, 0x3870U, 0x3871U, 0x3872U, 0x3873U, 0x3874U, 0x3875U, 0x3876U,\n\t0x3877U, 0x3878U, 0x3879U, 0x387AU, 0x3830U, 0x3831U, 0x3832U, 0x3833U,\n\t0x3834U, 0x3835U, 0x3836U, 0x3837U, 0x3838U, 0x3839U, 0x382BU, 0x382FU,\n\t0x3941U, 0x3942U, 0x3943U, 0x3944U, 0x3945U, 0x3946U, 0x3947U, 0x3948U,\n\t0x3949U, 0x394AU, 0x394BU, 0x394CU, 0x394DU, 0x394EU, 0x394FU, 0x3950U,\n\t0x3951U, 0x3952U, 0x3953U, 0x3954U, 0x3955U, 0x3956U, 0x3957U, 0x3958U,\n\t0x3959U, 0x395AU, 0x3961U, 0x3962U, 0x3963U, 0x3964U, 0x3965U, 0x3966U,\n\t0x3967U, 0x3968U, 0x3969U, 0x396AU, 0x396BU, 0x396CU, 0x396DU, 0x396EU,\n\t0x396FU, 0x3970U, 0x3971U, 0x3972U, 0x3973U, 0x3974U, 0x3975U, 0x3976U,\n\t0x3977U, 0x3978U, 0x3979U, 0x397AU, 0x3930U, 0x3931U, 0x3932U, 0x3933U,\n\t0x3934U, 0x3935U, 0x3936U, 0x3937U, 0x3938U, 0x3939U, 0x392BU, 0x392FU,\n\t0x2B41U, 0x2B42U, 0x2B43U, 0x2B44U, 0x2B45U, 0x2B46U, 0x2B47U, 0x2B48U,\n\t0x2B49U, 0x2B4AU, 0x2B4BU, 0x2B4CU, 0x2B4DU, 0x2B4EU, 0x2B4FU, 0x2B50U,\n\t0x2B51U, 0x2B52U, 0x2B53U, 0x2B54U, 0x2B55U, 0x2B56U, 0x2B57U, 0x2B58U,\n\t0x2B59U, 0x2B5AU, 0x2B61U, 0x2B62U, 0x2B63U, 0x2B64U, 0x2B65U, 0x2B66U,\n\t0x2B67U, 0x2B68U, 0x2B69U, 0x2B6AU, 0x2B6BU, 0x2B6CU, 0x2B6DU, 0x2B6EU,\n\t0x2B6FU, 0x2B70U, 0x2B71U, 0x2B72U, 0x2B73U, 0x2B74U, 0x2B75U, 0x2B76U,\n\t0x2B77U, 0x2B78U, 0x2B79U, 0x2B7AU, 0x2B30U, 0x2B31U, 0x2B32U, 0x2B33U,\n\t0x2B34U, 0x2B35U, 0x2B36U, 0x2B37U, 0x2B38U, 0x2B39U, 0x2B2BU, 0x2B2FU,\n\t0x2F41U, 0x2F42U, 0x2F43U, 0x2F44U, 0x2F45U, 0x2F46U, 0x2F47U, 0x2F48U,\n\t0x2F49U, 0x2F4AU, 0x2F4BU, 0x2F4CU, 0x2F4DU, 0x2F4EU, 0x2F4FU, 0x2F50U,\n\t0x2F51U, 0x2F52U, 0x2F53U, 0x2F54U, 0x2F55U, 0x2F56U, 0x2F57U, 0x2F58U,\n\t0x2F59U, 0x2F5AU, 0x2F61U, 0x2F62U, 0x2F63U, 0x2F64U, 0x2F65U, 0x2F66U,\n\t0x2F67U, 0x2F68U, 0x2F69U, 0x2F6AU, 0x2F6BU, 0x2F6CU, 0x2F6DU, 0x2F6EU,\n\t0x2F6FU, 0x2F70U, 0x2F71U, 0x2F72U, 0x2F73U, 0x2F74U, 0x2F75U, 0x2F76U,\n\t0x2F77U, 0x2F78U, 0x2F79U, 0x2F7AU, 0x2F30U, 0x2F31U, 0x2F32U, 0x2F33U,\n\t0x2F34U, 0x2F35U, 0x2F36U, 0x2F37U, 0x2F38U, 0x2F39U, 0x2F2BU, 0x2F2FU,\n#endif\n};\n"
  },
  {
    "path": "3rdparty/base64/lib/tables/table_enc_12bit.py",
    "content": "#!/usr/bin/python3\n\ndef tr(x):\n    \"\"\"Translate a 6-bit value to the Base64 alphabet.\"\"\"\n    s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \\\n      + 'abcdefghijklmnopqrstuvwxyz' \\\n      + '0123456789' \\\n      + '+/'\n    return ord(s[x])\n\ndef table(fn):\n    \"\"\"Generate a 12-bit lookup table.\"\"\"\n    ret = []\n    for n in range(0, 2**12):\n        pre = \"\\n\\t\" if n % 8 == 0 else \" \"\n        pre = \"\\t\" if n == 0 else pre\n        ret.append(\"{}0x{:04X}U,\".format(pre, fn(n)))\n    return \"\".join(ret)\n\ndef table_be():\n    \"\"\"Generate a 12-bit big-endian lookup table.\"\"\"\n    return table(lambda n: (tr(n & 0x3F) << 0) | (tr(n >> 6) << 8))\n\ndef table_le():\n    \"\"\"Generate a 12-bit little-endian lookup table.\"\"\"\n    return table(lambda n: (tr(n >> 6) << 0) | (tr(n & 0x3F) << 8))\n\ndef main():\n    \"\"\"Entry point.\"\"\"\n    lines = [\n        \"#include <stdint.h>\",\n        \"\",\n        \"const uint16_t base64_table_enc_12bit[] = {\",\n        \"#if BASE64_LITTLE_ENDIAN\",\n        table_le(),\n        \"#else\",\n        table_be(),\n        \"#endif\",\n        \"};\"\n    ]\n    for line in lines:\n        print(line)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "3rdparty/base64/lib/tables/table_generator.c",
    "content": "/**\n *\n * Copyright 2005, 2006 Nick Galbreath -- nickg [at] modp [dot] com\n * Copyright 2017 Matthieu Darbois\n * All rights reserved.\n *\n * http://modp.com/release/base64\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n *  this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n *   notice, this list of conditions and the following disclaimer in the\n *   documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n * IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n */\n\n/****************************/\n\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <inttypes.h>\n\nstatic uint8_t b64chars[64] = {\n\t'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n\t'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n\t'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',\n\t'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\n\t'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'\n};\n\nstatic uint8_t padchar = '=';\n\nstatic void printStart(void)\n{\n\tprintf(\"#include <stdint.h>\\n\");\n\tprintf(\"#define CHAR62 '%c'\\n\", b64chars[62]);\n\tprintf(\"#define CHAR63 '%c'\\n\", b64chars[63]);\n\tprintf(\"#define CHARPAD '%c'\\n\", padchar);\n}\n\nstatic void clearDecodeTable(uint32_t* ary)\n{\n\tint i = 0;\n\tfor (i = 0; i < 256; ++i) {\n\t\tary[i] = 0xFFFFFFFF;\n\t}\n}\n\n/* dump uint32_t as hex digits */\nvoid uint32_array_to_c_hex(const uint32_t* ary, size_t sz, const char* name)\n{\n\tsize_t i = 0;\n\n\tprintf(\"const uint32_t %s[%d] = {\\n\", name, (int)sz);\n\tfor (;;) {\n\t\tprintf(\"0x%08\" PRIx32, ary[i]);\n\t\t++i;\n\t\tif (i == sz)\n\t\t\tbreak;\n\t\tif (i % 6 == 0) {\n\t\t\tprintf(\",\\n\");\n\t\t} else {\n\t\t\tprintf(\", \");\n\t\t}\n\t}\n\tprintf(\"\\n};\\n\");\n}\n\nint main(int argc, char** argv)\n{\n\tuint32_t x;\n\tuint32_t i = 0;\n\tuint32_t ary[256];\n\n\t/*  over-ride standard alphabet */\n\tif (argc == 2) {\n\t\tuint8_t* replacements = (uint8_t*)argv[1];\n\t\tif (strlen((char*)replacements) != 3) {\n\t\t\tfprintf(stderr, \"input must be a string of 3 characters '-', '.' or '_'\\n\");\n\t\t\texit(1);\n\t\t}\n\t\tfprintf(stderr, \"fusing '%s' as replacements in base64 encoding\\n\", replacements);\n\t\tb64chars[62] = replacements[0];\n\t\tb64chars[63] = replacements[1];\n\t\tpadchar = replacements[2];\n\t}\n\n\tprintStart();\n\n\tprintf(\"\\n\\n#if BASE64_LITTLE_ENDIAN\\n\");\n\n\tprintf(\"\\n\\n/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */\\n\\n\");\n\n\tclearDecodeTable(ary);\n\tfor (i = 0; i < 64; ++i) {\n\t\tx = b64chars[i];\n\t\tary[x] = i << 2;\n\t}\n\tuint32_array_to_c_hex(ary, sizeof(ary) / sizeof(uint32_t), \"base64_table_dec_32bit_d0\");\n\tprintf(\"\\n\\n\");\n\n\tclearDecodeTable(ary);\n\tfor (i = 0; i < 64; ++i) {\n\t\tx = b64chars[i];\n\t\tary[x] = ((i & 0x30) >> 4) | ((i & 0x0F) << 12);\n\t}\n\tuint32_array_to_c_hex(ary, sizeof(ary) / sizeof(uint32_t), \"base64_table_dec_32bit_d1\");\n\tprintf(\"\\n\\n\");\n\n\tclearDecodeTable(ary);\n\tfor (i = 0; i < 64; ++i) {\n\t\tx = b64chars[i];\n\t\tary[x] = ((i & 0x03) << 22) | ((i & 0x3c) << 6);\n\t}\n\tuint32_array_to_c_hex(ary, sizeof(ary) / sizeof(uint32_t), \"base64_table_dec_32bit_d2\");\n\tprintf(\"\\n\\n\");\n\n\tclearDecodeTable(ary);\n\tfor (i = 0; i < 64; ++i) {\n\t\tx = b64chars[i];\n\t\tary[x] = i << 16;\n\t}\n\tuint32_array_to_c_hex(ary, sizeof(ary) / sizeof(uint32_t), \"base64_table_dec_32bit_d3\");\n\tprintf(\"\\n\\n\");\n\n\tprintf(\"#else\\n\");\n\n\tprintf(\"\\n\\n/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */\\n\\n\");\n\n\tclearDecodeTable(ary);\n\tfor (i = 0; i < 64; ++i) {\n\t\tx = b64chars[i];\n\t\tary[x] = i << 26;\n\t}\n\tuint32_array_to_c_hex(ary, sizeof(ary) / sizeof(uint32_t), \"base64_table_dec_32bit_d0\");\n\tprintf(\"\\n\\n\");\n\n\tclearDecodeTable(ary);\n\tfor (i = 0; i < 64; ++i) {\n\t\tx = b64chars[i];\n\t\tary[x] = i << 20;\n\t}\n\tuint32_array_to_c_hex(ary, sizeof(ary) / sizeof(uint32_t), \"base64_table_dec_32bit_d1\");\n\tprintf(\"\\n\\n\");\n\n\tclearDecodeTable(ary);\n\tfor (i = 0; i < 64; ++i) {\n\t\tx = b64chars[i];\n\t\tary[x] = i << 14;\n\t}\n\tuint32_array_to_c_hex(ary, sizeof(ary) / sizeof(uint32_t), \"base64_table_dec_32bit_d2\");\n\tprintf(\"\\n\\n\");\n\n\tclearDecodeTable(ary);\n\tfor (i = 0; i < 64; ++i) {\n\t\tx = b64chars[i];\n\t\tary[x] = i << 8;\n\t}\n\tuint32_array_to_c_hex(ary, sizeof(ary) / sizeof(uint32_t), \"base64_table_dec_32bit_d3\");\n\tprintf(\"\\n\\n\");\n\n\tprintf(\"#endif\\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "3rdparty/base64/lib/tables/tables.c",
    "content": "#include \"tables.h\"\n\nconst uint8_t\nbase64_table_enc_6bit[] =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\t\"abcdefghijklmnopqrstuvwxyz\"\n\t\"0123456789\"\n\t\"+/\";\n\n// In the lookup table below, note that the value for '=' (character 61) is\n// 254, not 255. This character is used for in-band signaling of the end of\n// the datastream, and we will use that later. The characters A-Z, a-z, 0-9\n// and + / are mapped to their \"decoded\" values. The other bytes all map to\n// the value 255, which flags them as \"invalid input\".\n\nconst uint8_t\nbase64_table_dec_8bit[] =\n{\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\t\t//   0..15\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\t\t//  16..31\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,\t\t//  32..47\n\t 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 254, 255, 255,\t\t//  48..63\n\t255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,\t\t//  64..79\n\t 15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,\t\t//  80..95\n\t255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,\t\t//  96..111\n\t 41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,\t\t// 112..127\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\t\t// 128..143\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n};\n\n#if BASE64_WORDSIZE >= 32\n#  include \"table_dec_32bit.h\"\n#  include \"table_enc_12bit.h\"\n#endif\n"
  },
  {
    "path": "3rdparty/base64/lib/tables/tables.h",
    "content": "#ifndef BASE64_TABLES_H\n#define BASE64_TABLES_H\n\n#include <stdint.h>\n\n#include \"../env.h\"\n\n// These tables are used by all codecs for fallback plain encoding/decoding:\nextern const uint8_t base64_table_enc_6bit[];\nextern const uint8_t base64_table_dec_8bit[];\n\n// These tables are used for the 32-bit and 64-bit generic decoders:\n#if BASE64_WORDSIZE >= 32\nextern const uint32_t base64_table_dec_32bit_d0[];\nextern const uint32_t base64_table_dec_32bit_d1[];\nextern const uint32_t base64_table_dec_32bit_d2[];\nextern const uint32_t base64_table_dec_32bit_d3[];\n\n// This table is used by the 32 and 64-bit generic encoders:\nextern const uint16_t base64_table_enc_12bit[];\n#endif\n\n#endif\t// BASE64_TABLES_H\n"
  },
  {
    "path": "3rdparty/ringbuf/ringbuf.c",
    "content": "/*\n * ringbuf.c - C ring buffer (FIFO) implementation.\n *\n * Written in 2011 by Drew Hess <dhess-src@bothan.net>.\n *\n * To the extent possible under law, the author(s) have dedicated all\n * copyright and related and neighboring rights to this software to\n * the public domain worldwide. This software is distributed without\n * any warranty.\n *\n * You should have received a copy of the CC0 Public Domain Dedication\n * along with this software. If not, see\n * <http://creativecommons.org/publicdomain/zero/1.0/>.\n */\n\n#ifndef KITTY_DEBUG_BUILD\n#define NDEBUG 1\n#endif\n#include \"ringbuf.h\"\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <sys/param.h>\n#include <assert.h>\n\nstatic size_t\nsize_t_min(size_t x, size_t y) {\n    return x > y ? y : x;\n}\n\n/*\n * The code is written for clarity, not cleverness or performance, and\n * contains many assert()s to enforce invariant assumptions and catch\n * bugs. Feel free to optimize the code and to remove asserts for use\n * in your own projects, once you're comfortable that it functions as\n * intended.\n */\n\nstruct ringbuf_t\n{\n    uint8_t *buf;\n    uint8_t *head, *tail;\n    size_t size;\n};\n\nringbuf_t\nringbuf_new(size_t capacity)\n{\n    ringbuf_t rb = malloc(sizeof(struct ringbuf_t));\n    if (rb) {\n\n        /* One byte is used for detecting the full condition. */\n        rb->size = capacity + 1;\n        rb->buf = malloc(rb->size);\n        if (rb->buf)\n            ringbuf_reset(rb);\n        else {\n            free(rb);\n            return 0;\n        }\n    }\n    return rb;\n}\n\nsize_t\nringbuf_buffer_size(const struct ringbuf_t *rb)\n{\n    return rb->size;\n}\n\nvoid\nringbuf_reset(ringbuf_t rb)\n{\n    rb->head = rb->tail = rb->buf;\n}\n\nvoid\nringbuf_free(ringbuf_t *rb)\n{\n    assert(rb && *rb);\n    free((*rb)->buf);\n    free(*rb);\n    *rb = 0;\n}\n\nsize_t\nringbuf_capacity(const struct ringbuf_t *rb)\n{\n    return ringbuf_buffer_size(rb) - 1;\n}\n\n/*\n * Return a pointer to one-past-the-end of the ring buffer's\n * contiguous buffer. You shouldn't normally need to use this function\n * unless you're writing a new ringbuf_* function.\n */\nstatic const uint8_t *\nringbuf_end(const struct ringbuf_t *rb)\n{\n    return rb->buf + ringbuf_buffer_size(rb);\n}\n\nsize_t\nringbuf_bytes_free(const struct ringbuf_t *rb)\n{\n    if (rb->head >= rb->tail)\n        return ringbuf_capacity(rb) - (rb->head - rb->tail);\n    else\n        return rb->tail - rb->head - 1;\n}\n\nsize_t\nringbuf_bytes_used(const struct ringbuf_t *rb)\n{\n    return ringbuf_capacity(rb) - ringbuf_bytes_free(rb);\n}\n\nint\nringbuf_is_full(const struct ringbuf_t *rb)\n{\n    return ringbuf_bytes_free(rb) == 0;\n}\n\nint\nringbuf_is_empty(const struct ringbuf_t *rb)\n{\n    return ringbuf_bytes_free(rb) == ringbuf_capacity(rb);\n}\n\nconst void *\nringbuf_tail(const struct ringbuf_t *rb)\n{\n    return rb->tail;\n}\n\nconst void *\nringbuf_head(const struct ringbuf_t *rb)\n{\n    return rb->head;\n}\n\n/*\n * Given a ring buffer rb and a pointer to a location within its\n * contiguous buffer, return the a pointer to the next logical\n * location in the ring buffer.\n */\nstatic uint8_t *\nringbuf_nextp(ringbuf_t rb, const uint8_t *p)\n{\n    /*\n     * The assert guarantees the expression (++p - rb->buf) is\n     * non-negative; therefore, the modulus operation is safe and\n     * portable.\n     */\n    assert((p >= rb->buf) && (p < ringbuf_end(rb)));\n    return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb));\n}\n\nsize_t\nringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset)\n{\n    const uint8_t *bufend = ringbuf_end(rb);\n    size_t bytes_used = ringbuf_bytes_used(rb);\n    if (offset >= bytes_used)\n        return bytes_used;\n\n    const uint8_t *start = rb->buf +\n        (((rb->tail - rb->buf) + offset) % ringbuf_buffer_size(rb));\n    assert(bufend > start);\n    size_t n = size_t_min(bufend - start, bytes_used - offset);\n    const uint8_t *found = memchr(start, c, n);\n    if (found)\n        return offset + (found - start);\n    else\n        return ringbuf_findchr(rb, c, offset + n);\n}\n\nsize_t\nringbuf_memset(ringbuf_t dst, int c, size_t len)\n{\n    const uint8_t *bufend = ringbuf_end(dst);\n    size_t nwritten = 0;\n    size_t count = size_t_min(len, ringbuf_buffer_size(dst));\n    int overflow = count > ringbuf_bytes_free(dst);\n\n    while (nwritten != count) {\n\n        /* don't copy beyond the end of the buffer */\n        assert(bufend > dst->head);\n        size_t n = size_t_min(bufend - dst->head, count - nwritten);\n        memset(dst->head, c, n);\n        dst->head += n;\n        nwritten += n;\n\n        /* wrap? */\n        if (dst->head == bufend)\n            dst->head = dst->buf;\n    }\n\n    if (overflow) {\n        dst->tail = ringbuf_nextp(dst, dst->head);\n        assert(ringbuf_is_full(dst));\n    }\n\n    return nwritten;\n}\n\nvoid *\nringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count)\n{\n    const uint8_t *u8src = src;\n    const uint8_t *bufend = ringbuf_end(dst);\n    int overflow = count > ringbuf_bytes_free(dst);\n    size_t nread = 0;\n\n    while (nread != count) {\n        /* don't copy beyond the end of the buffer */\n        assert(bufend > dst->head);\n        size_t n = size_t_min(bufend - dst->head, count - nread);\n        memcpy(dst->head, u8src + nread, n);\n        dst->head += n;\n        nread += n;\n\n        /* wrap? */\n        if (dst->head == bufend)\n            dst->head = dst->buf;\n    }\n\n    if (overflow) {\n        dst->tail = ringbuf_nextp(dst, dst->head);\n        assert(ringbuf_is_full(dst));\n    }\n\n    return dst->head;\n}\n\nssize_t\nringbuf_read(int fd, ringbuf_t rb, size_t count)\n{\n    const uint8_t *bufend = ringbuf_end(rb);\n    size_t nfree = ringbuf_bytes_free(rb);\n\n    /* don't write beyond the end of the buffer */\n    assert(bufend > rb->head);\n    count = size_t_min(bufend - rb->head, count);\n    ssize_t n = read(fd, rb->head, count);\n    if (n > 0) {\n        assert(rb->head + n <= bufend);\n        rb->head += n;\n\n        /* wrap? */\n        if (rb->head == bufend)\n            rb->head = rb->buf;\n\n        /* fix up the tail pointer if an overflow occurred */\n        if ((size_t)n > nfree) {\n            rb->tail = ringbuf_nextp(rb, rb->head);\n            assert(ringbuf_is_full(rb));\n        }\n    }\n\n    return n;\n}\n\nvoid *\nringbuf_memmove_from(void *dst, ringbuf_t src, size_t count)\n{\n    size_t bytes_used = ringbuf_bytes_used(src);\n    if (count > bytes_used)\n        return 0;\n\n    uint8_t *u8dst = dst;\n    const uint8_t *bufend = ringbuf_end(src);\n    size_t nwritten = 0;\n    while (nwritten != count) {\n        assert(bufend > src->tail);\n        size_t n = size_t_min(bufend - src->tail, count - nwritten);\n        memcpy(u8dst + nwritten, src->tail, n);\n        src->tail += n;\n        nwritten += n;\n\n        /* wrap ? */\n        if (src->tail == bufend)\n            src->tail = src->buf;\n    }\n\n    assert(count + ringbuf_bytes_used(src) == bytes_used);\n    return src->tail;\n}\n\nunsigned char\nringbuf_move_char(ringbuf_t src) {\n    assert(!ringbuf_is_empty(src));\n    const uint8_t *bufend = ringbuf_end(src);\n    assert(bufend > src->tail);\n    uint8_t ans = *src->tail;\n    src->tail += 1;\n    if (src->tail == bufend)\n        src->tail = src->buf;\n    return ans;\n}\n\nsize_t\nringbuf_memcpy_from(void *dst, const ringbuf_t src, size_t count)\n{\n    size_t bytes_used = ringbuf_bytes_used(src);\n    if (count > bytes_used) count = bytes_used;\n\n    uint8_t *u8dst = dst;\n    const uint8_t *bufend = ringbuf_end(src);\n    size_t nwritten = 0;\n    const uint8_t* tail = src->tail;\n    while (nwritten != count) {\n        assert(bufend > tail);\n        size_t n = size_t_min(bufend - tail, count - nwritten);\n        memcpy(u8dst + nwritten, tail, n);\n        tail += n;\n        nwritten += n;\n\n        /* wrap ? */\n        if (tail == bufend)\n            tail = src->buf;\n    }\n\n    assert(ringbuf_bytes_used(src) == bytes_used);\n    return count;\n}\n\n\nssize_t\nringbuf_write(int fd, ringbuf_t rb, size_t count)\n{\n    size_t bytes_used = ringbuf_bytes_used(rb);\n    if (count > bytes_used)\n        return 0;\n\n    const uint8_t *bufend = ringbuf_end(rb);\n    assert(bufend > rb->head);\n    count = size_t_min(bufend - rb->tail, count);\n    ssize_t n = write(fd, rb->tail, count);\n    if (n > 0) {\n        assert(rb->tail + n <= bufend);\n        rb->tail += n;\n\n        /* wrap? */\n        if (rb->tail == bufend)\n            rb->tail = rb->buf;\n\n        assert(n + ringbuf_bytes_used(rb) == bytes_used);\n    }\n\n    return n;\n}\n\nvoid *\nringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count)\n{\n    size_t src_bytes_used = ringbuf_bytes_used(src);\n    if (count > src_bytes_used)\n        return 0;\n    int overflow = count > ringbuf_bytes_free(dst);\n\n    const uint8_t *src_bufend = ringbuf_end(src);\n    const uint8_t *dst_bufend = ringbuf_end(dst);\n    size_t ncopied = 0;\n    while (ncopied != count) {\n        assert(src_bufend > src->tail);\n        size_t nsrc = size_t_min(src_bufend - src->tail, count - ncopied);\n        assert(dst_bufend > dst->head);\n        size_t n = size_t_min(dst_bufend - dst->head, nsrc);\n        memcpy(dst->head, src->tail, n);\n        src->tail += n;\n        dst->head += n;\n        ncopied += n;\n\n        /* wrap ? */\n        if (src->tail == src_bufend)\n            src->tail = src->buf;\n        if (dst->head == dst_bufend)\n            dst->head = dst->buf;\n    }\n\n    assert(count + ringbuf_bytes_used(src) == src_bytes_used);\n\n    if (overflow) {\n        dst->tail = ringbuf_nextp(dst, dst->head);\n        assert(ringbuf_is_full(dst));\n    }\n\n    return dst->head;\n}\n"
  },
  {
    "path": "3rdparty/ringbuf/ringbuf.h",
    "content": "#pragma once\n/*\n * ringbuf.h - C ring buffer (FIFO) interface.\n *\n * Written in 2011 by Drew Hess <dhess-src@bothan.net>.\n *\n * To the extent possible under law, the author(s) have dedicated all\n * copyright and related and neighboring rights to this software to\n * the public domain worldwide. This software is distributed without\n * any warranty.\n *\n * You should have received a copy of the CC0 Public Domain Dedication\n * along with this software. If not, see\n * <http://creativecommons.org/publicdomain/zero/1.0/>.\n */\n\n/*\n * A byte-addressable ring buffer FIFO implementation.\n *\n * The ring buffer's head pointer points to the starting location\n * where data should be written when copying data *into* the buffer\n * (e.g., with ringbuf_read). The ring buffer's tail pointer points to\n * the starting location where data should be read when copying data\n * *from* the buffer (e.g., with ringbuf_write).\n */\n\n#include <stddef.h>\n#include <sys/types.h>\n\ntypedef struct ringbuf_t *ringbuf_t;\n\n/*\n * Create a new ring buffer with the given capacity (usable\n * bytes). Note that the actual internal buffer size may be one or\n * more bytes larger than the usable capacity, for bookkeeping.\n *\n * Returns the new ring buffer object, or 0 if there's not enough\n * memory to fulfill the request for the given capacity.\n */\nringbuf_t\nringbuf_new(size_t capacity);\n\n/*\n * The size of the internal buffer, in bytes. One or more bytes may be\n * unusable in order to distinguish the \"buffer full\" state from the\n * \"buffer empty\" state.\n *\n * For the usable capacity of the ring buffer, use the\n * ringbuf_capacity function.\n */\nsize_t\nringbuf_buffer_size(const struct ringbuf_t *rb);\n\n/*\n * Deallocate a ring buffer, and, as a side effect, set the pointer to\n * 0.\n */\nvoid\nringbuf_free(ringbuf_t *rb);\n\n/*\n * Reset a ring buffer to its initial state (empty).\n */\nvoid\nringbuf_reset(ringbuf_t rb);\n\n/*\n * The usable capacity of the ring buffer, in bytes. Note that this\n * value may be less than the ring buffer's internal buffer size, as\n * returned by ringbuf_buffer_size.\n */\nsize_t\nringbuf_capacity(const struct ringbuf_t *rb);\n\n/*\n * The number of free/available bytes in the ring buffer. This value\n * is never larger than the ring buffer's usable capacity.\n */\nsize_t\nringbuf_bytes_free(const struct ringbuf_t *rb);\n\n/*\n * The number of bytes currently being used in the ring buffer. This\n * value is never larger than the ring buffer's usable capacity.\n */\nsize_t\nringbuf_bytes_used(const struct ringbuf_t *rb);\n\nint\nringbuf_is_full(const struct ringbuf_t *rb);\n\nint\nringbuf_is_empty(const struct ringbuf_t *rb);\n\n/*\n * Const access to the head and tail pointers of the ring buffer.\n */\nconst void *\nringbuf_tail(const struct ringbuf_t *rb);\n\nconst void *\nringbuf_head(const struct ringbuf_t *rb);\n\n/*\n * Locate the first occurrence of character c (converted to an\n * unsigned char) in ring buffer rb, beginning the search at offset\n * bytes from the ring buffer's tail pointer. The function returns the\n * offset of the character from the ring buffer's tail pointer, if\n * found. If c does not occur in the ring buffer, the function returns\n * the number of bytes used in the ring buffer.\n *\n * Note that the offset parameter and the returned offset are logical\n * offsets from the tail pointer, not necessarily linear offsets.\n */\nsize_t\nringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset);\n\n/*\n * Beginning at ring buffer dst's head pointer, fill the ring buffer\n * with a repeating sequence of len bytes, each of value c (converted\n * to an unsigned char). len can be as large as you like, but the\n * function will never write more than ringbuf_buffer_size(dst) bytes\n * in a single invocation, since that size will cause all bytes in the\n * ring buffer to be written exactly once each.\n *\n * Note that if len is greater than the number of free bytes in dst,\n * the ring buffer will overflow. When an overflow occurs, the state\n * of the ring buffer is guaranteed to be consistent, including the\n * head and tail pointers; old data will simply be overwritten in FIFO\n * fashion, as needed. However, note that, if calling the function\n * results in an overflow, the value of the ring buffer's tail pointer\n * may be different than it was before the function was called.\n *\n * Returns the actual number of bytes written to dst: len, if\n * len < ringbuf_buffer_size(dst), else ringbuf_buffer_size(dst).\n */\nsize_t\nringbuf_memset(ringbuf_t dst, int c, size_t len);\n\n/*\n * Copy n bytes from a contiguous memory area src into the ring buffer\n * dst. Returns the ring buffer's new head pointer.\n *\n * It is possible to copy more data from src than is available in the\n * buffer; i.e., it's possible to overflow the ring buffer using this\n * function. When an overflow occurs, the state of the ring buffer is\n * guaranteed to be consistent, including the head and tail pointers;\n * old data will simply be overwritten in FIFO fashion, as\n * needed. However, note that, if calling the function results in an\n * overflow, the value of the ring buffer's tail pointer may be\n * different than it was before the function was called.\n */\nvoid *\nringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count);\n\n/*\n * This convenience function calls read(2) on the file descriptor fd,\n * using the ring buffer rb as the destination buffer for the read,\n * and returns the value returned by read(2). It will only call\n * read(2) once, and may return a short count.\n *\n * It is possible to read more data from the file descriptor than is\n * available in the buffer; i.e., it's possible to overflow the ring\n * buffer using this function. When an overflow occurs, the state of\n * the ring buffer is guaranteed to be consistent, including the head\n * and tail pointers: old data will simply be overwritten in FIFO\n * fashion, as needed. However, note that, if calling the function\n * results in an overflow, the value of the ring buffer's tail pointer\n * may be different than it was before the function was called.\n */\nssize_t\nringbuf_read(int fd, ringbuf_t rb, size_t count);\n\n/*\n * Copy n bytes from the ring buffer src, starting from its tail\n * pointer, into a contiguous memory area dst. Returns the value of\n * src's tail pointer after the copy is finished.\n *\n * Note that this copy is destructive with respect to the ring buffer:\n * the n bytes copied from the ring buffer are no longer available in\n * the ring buffer after the copy is complete, and the ring buffer\n * will have n more free bytes than it did before the function was\n * called.\n *\n * This function will *not* allow the ring buffer to underflow. If\n * count is greater than the number of bytes used in the ring buffer,\n * no bytes are copied, and the function will return 0.\n */\nvoid *\nringbuf_memmove_from(void *dst, ringbuf_t src, size_t count);\n\n/* ringbuf_memmove_from() optimized for a single character.\n * Must only be called if the ringbuf is not empty */\nunsigned char\nringbuf_move_char(ringbuf_t src);\n\n/*\n * Same as ringbuf_memmove_from() except that it does not change the ringbuffer\n * and returns the actual number of bytes copied, which is the minimum of ringbuf_bytes_used\n * and count.\n */\nsize_t\nringbuf_memcpy_from(void *dst, const ringbuf_t src, size_t count);\n\n/*\n * This convenience function calls write(2) on the file descriptor fd,\n * using the ring buffer rb as the source buffer for writing (starting\n * at the ring buffer's tail pointer), and returns the value returned\n * by write(2). It will only call write(2) once, and may return a\n * short count.\n *\n * Note that this copy is destructive with respect to the ring buffer:\n * any bytes written from the ring buffer to the file descriptor are\n * no longer available in the ring buffer after the copy is complete,\n * and the ring buffer will have N more free bytes than it did before\n * the function was called, where N is the value returned by the\n * function (unless N is < 0, in which case an error occurred and no\n * bytes were written).\n *\n * This function will *not* allow the ring buffer to underflow. If\n * count is greater than the number of bytes used in the ring buffer,\n * no bytes are written to the file descriptor, and the function will\n * return 0.\n */\nssize_t\nringbuf_write(int fd, ringbuf_t rb, size_t count);\n\n/*\n * Copy count bytes from ring buffer src, starting from its tail\n * pointer, into ring buffer dst. Returns dst's new head pointer after\n * the copy is finished.\n *\n * Note that this copy is destructive with respect to the ring buffer\n * src: any bytes copied from src into dst are no longer available in\n * src after the copy is complete, and src will have 'count' more free\n * bytes than it did before the function was called.\n *\n * It is possible to copy more data from src than is available in dst;\n * i.e., it's possible to overflow dst using this function. When an\n * overflow occurs, the state of dst is guaranteed to be consistent,\n * including the head and tail pointers; old data will simply be\n * overwritten in FIFO fashion, as needed. However, note that, if\n * calling the function results in an overflow, the value dst's tail\n * pointer may be different than it was before the function was\n * called.\n *\n * It is *not* possible to underflow src; if count is greater than the\n * number of bytes used in src, no bytes are copied, and the function\n * returns 0.\n */\nvoid *\nringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count);\n"
  },
  {
    "path": "3rdparty/verstable.h",
    "content": "/*------------------------------------------------- VERSTABLE v2.1.1 ---------------------------------------------------\n\nVerstable is a C99-compatible, open-addressing hash table using quadratic probing and the following additions:\n\n* All keys that hash (i.e. \"belong\") to the same bucket (their \"home bucket\") are linked together by an 11-bit integer\n  specifying the quadratic displacement, relative to that bucket, of the next key in the chain.\n\n* If a chain of keys exists for a given bucket, then it always begins at that bucket. To maintain this policy, a 1-bit\n  flag is used to mark whether the key occupying a bucket belongs there. When inserting a new key, if the bucket it\n  belongs to is occupied by a key that does not belong there, then the occupying key is evicted and the new key takes\n  the bucket.\n\n* A 4-bit fragment of each key's hash code is also stored.\n\n* The aforementioned metadata associated with each bucket (the 4-bit hash fragment, the 1-bit flag, and the 11-bit link\n  to the next key in the chain) are stored together in a uint16_t array rather than in the bucket alongside the key and\n  (optionally) the value.\n\nOne way to conceptualize this scheme is as a chained hash table in which overflowing keys are stored not in separate\nmemory allocations but in otherwise unused buckets. In this regard, it shares similarities with Malte Skarupke's Bytell\nhash table (https://www.youtube.com/watch?v=M2fKMP47slQ) and traditional \"coalesced hashing\".\n\nAdvantages of this scheme include:\n\n* Fast lookups impervious to load factor: If the table contains any key belonging to the lookup key's home bucket, then\n  that bucket contains the first in a traversable chain of all keys belonging to it. Hence, only the home bucket and\n  other buckets containing keys belonging to it are ever probed. Moreover, the stored hash fragments allow skipping most\n  non-matching keys in the chain without accessing the actual buckets array or calling the (potentially expensive) key\n  comparison function.\n\n* Fast insertions: Insertions are faster than they are in other schemes that move keys around (e.g. Robin Hood) because\n  they only move, at most, one existing key.\n\n* Fast, tombstone-free deletions: Deletions, which usually require tombstones in quadratic-probing hash tables, are\n  tombstone-free and only move, at most, one existing key.\n\n* Fast iteration: The separate metadata array allows keys in sparsely populated tables to be found without incurring the\n  frequent cache misses that would result from traversing the buckets array.\n\nUsage example:\n\n  +---------------------------------------------------------+----------------------------------------------------------+\n  | Using the generic macro API (C11 and later):            | Using the prefixed functions API (C99 and later):        |\n  |---------------------------------------------------------+----------------------------------------------------------+\n  | #include <stdio.h>                                      | #include <stdio.h>                                       |\n  |                                                         |                                                          |\n  | // Instantiating a set template.                        | // Instantiating a set template.                         |\n  | #define NAME int_set                                    | #define NAME int_set                                     |\n  | #define KEY_TY int                                      | #define KEY_TY int                                       |\n  | #include \"verstable.h\"                                  | #define HASH_FN vt_hash_integer                          |\n  |                                                         | #define CMPR_FN vt_cmpr_integer                          |\n  | // Instantiating a map template.                        | #include \"verstable.h\"                                   |\n  | #define NAME int_int_map                                |                                                          |\n  | #define KEY_TY int                                      | // Instantiating a map template.                         |\n  | #define VAL_TY int                                      | #define NAME int_int_map                                 |\n  | #include \"verstable.h\"                                  | #define KEY_TY int                                       |\n  |                                                         | #define VAL_TY int                                       |\n  | int main( void )                                        | #define HASH_FN vt_hash_integer                          |\n  | {                                                       | #define CMPR_FN vt_cmpr_integer                          |\n  |   // Set.                                               | #include \"verstable.h\"                                   |\n  |                                                         |                                                          |\n  |   int_set our_set;                                      | int main( void )                                         |\n  |   vt_init( &our_set );                                  | {                                                        |\n  |                                                         |   // Set.                                                |\n  |   // Inserting keys.                                    |                                                          |\n  |   for( int i = 0; i < 10; ++i )                         |   int_set our_set;                                       |\n  |   {                                                     |   int_set_init( &our_set );                              |\n  |     int_set_itr itr = vt_insert( &our_set, i );         |                                                          |\n  |     if( vt_is_end( itr ) )                              |   // Inserting keys.                                     |\n  |     {                                                   |   for( int i = 0; i < 10; ++i )                          |\n  |       // Out of memory, so abort.                       |   {                                                      |\n  |       vt_cleanup( &our_set );                           |     int_set_itr itr =                                    |\n  |       return 1;                                         |       int_set_insert( &our_set, i );                     |\n  |     }                                                   |     if( int_set_is_end( itr ) )                          |\n  |   }                                                     |     {                                                    |\n  |                                                         |       // Out of memory, so abort.                        |\n  |   // Erasing keys.                                      |       int_set_cleanup( &our_set );                       |\n  |   for( int i = 0; i < 10; i += 3 )                      |       return 1;                                          |\n  |     vt_erase( &our_set, i );                            |     }                                                    |\n  |                                                         |   }                                                      |\n  |   // Retrieving keys.                                   |                                                          |\n  |   for( int i = 0; i < 10; ++i )                         |   // Erasing keys.                                       |\n  |   {                                                     |   for( int i = 0; i < 10; i += 3 )                       |\n  |     int_set_itr itr = vt_get( &our_set, i );            |     int_set_erase( &our_set, i );                        |\n  |     if( !vt_is_end( itr ) )                             |                                                          |\n  |       printf( \"%d \", itr.data->key );                   |   // Retrieving keys.                                    |\n  |   }                                                     |   for( int i = 0; i < 10; ++i )                          |\n  |   // Printed: 1 2 4 5 7 8                               |   {                                                      |\n  |                                                         |     int_set_itr itr = int_set_get( &our_set, i );        |\n  |   // Iteration.                                         |     if( !int_set_is_end( itr ) )                         |\n  |   for(                                                  |       printf( \"%d \", itr.data->key );                    |\n  |     int_set_itr itr = vt_first( &our_set );             |   }                                                      |\n  |     !vt_is_end( itr );                                  |   // Printed: 1 2 4 5 7 8                                |\n  |     itr = vt_next( itr )                                |                                                          |\n  |   )                                                     |   // Iteration.                                          |\n  |     printf( \"%d \", itr.data->key );                     |   for(                                                   |\n  |   // Printed: 2 4 7 1 5 8                               |     int_set_itr itr =                                    |\n  |                                                         |       int_set_first( &our_set );                         |\n  |   vt_cleanup( &our_set );                               |     !int_set_is_end( itr );                              |\n  |                                                         |     itr = int_set_next( itr )                            |\n  |   // Map.                                               |   )                                                      |\n  |                                                         |     printf( \"%d \", itr.data->key );                      |\n  |   int_int_map our_map;                                  |   // Printed: 2 4 7 1 5 8                                |\n  |   vt_init( &our_map );                                  |                                                          |\n  |                                                         |   int_set_cleanup( &our_set );                           |\n  |   // Inserting keys and values.                         |                                                          |\n  |   for( int i = 0; i < 10; ++i )                         |   // Map.                                                |\n  |   {                                                     |                                                          |\n  |     int_int_map_itr itr =                               |   int_int_map our_map;                                   |\n  |       vt_insert( &our_map, i, i + 1 );                  |   int_int_map_init( &our_map );                          |\n  |     if( vt_is_end( itr ) )                              |                                                          |\n  |     {                                                   |   // Inserting keys and values.                          |\n  |       // Out of memory, so abort.                       |   for( int i = 0; i < 10; ++i )                          |\n  |       vt_cleanup( &our_map );                           |   {                                                      |\n  |       return 1;                                         |     int_int_map_itr itr =                                |\n  |     }                                                   |       int_int_map_insert( &our_map, i, i + 1 );          |\n  |   }                                                     |     if( int_int_map_is_end( itr ) )                      |\n  |                                                         |     {                                                    |\n  |   // Erasing keys and values.                           |       // Out of memory, so abort.                        |\n  |   for( int i = 0; i < 10; i += 3 )                      |       int_int_map_cleanup( &our_map );                   |\n  |     vt_erase( &our_map, i );                            |       return 1;                                          |\n  |                                                         |     }                                                    |\n  |   // Retrieving keys and values.                        |   }                                                      |\n  |   for( int i = 0; i < 10; ++i )                         |                                                          |\n  |   {                                                     |   // Erasing keys and values.                            |\n  |     int_int_map_itr itr = vt_get( &our_map, i );        |   for( int i = 0; i < 10; i += 3 )                       |\n  |     if( !vt_is_end( itr ) )                             |     int_int_map_erase( &our_map, i );                    |\n  |       printf(                                           |                                                          |\n  |         \"%d:%d \",                                       |   // Retrieving keys and values.                         |\n  |         itr.data->key,                                  |   for( int i = 0; i < 10; ++i )                          |\n  |         itr.data->val                                   |   {                                                      |\n  |       );                                                |     int_int_map_itr itr =                                |\n  |   }                                                     |       int_int_map_get( &our_map, i );                    |\n  |   // Printed: 1:2 2:3 4:5 5:6 7:8 8:9                   |     if( !int_int_map_is_end( itr ) )                     |\n  |                                                         |       printf(                                            |\n  |   // Iteration.                                         |         \"%d:%d \",                                        |\n  |   for(                                                  |         itr.data->key,                                   |\n  |     int_int_map_itr itr = vt_first( &our_map );         |         itr.data->val                                    |\n  |     !vt_is_end( itr );                                  |     );                                                   |\n  |     itr = vt_next( itr )                                |   }                                                      |\n  |   )                                                     |   // Printed: 1:2 2:3 4:5 5:6 7:8 8:9                    |\n  |     printf(                                             |                                                          |\n  |       \"%d:%d \",                                         |   // Iteration.                                          |\n  |       itr.data->key,                                    |   for(                                                   |\n  |       itr.data->val                                     |     int_int_map_itr itr =                                |\n  |     );                                                  |       int_int_map_first( &our_map );                     |\n  |   // Printed: 2:3 4:5 7:8 1:2 5:6 8:9                   |     !int_int_map_is_end( itr );                          |\n  |                                                         |     itr = int_int_map_next( itr )                        |\n  |   vt_cleanup( &our_map );                               |   )                                                      |\n  | }                                                       |     printf(                                              |\n  |                                                         |       \"%d:%d \",                                          |\n  |                                                         |       itr.data->key,                                     |\n  |                                                         |       itr.data->val                                      |\n  |                                                         |     );                                                   |\n  |                                                         |   // Printed: 2:3 4:5 7:8 1:2 5:6 8:9                    |\n  |                                                         |                                                          |\n  |                                                         |   int_int_map_cleanup( &our_map );                       |\n  |                                                         | }                                                        |\n  |                                                         |                                                          |\n  +---------------------------------------------------------+----------------------------------------------------------+\n\nAPI:\n\n  Instantiating a hash table template:\n\n    Create a new hash table type in the following manner:\n\n      #define NAME   <your chosen type name>\n      #define KEY_TY <type>\n      #include \"verstable.h\"\n\n    The NAME macro specifies the name of hash table type that the library will declare, the prefix for the functions\n    associated with it, and the prefix for the associated iterator type.\n\n    The KEY_TY macro specifies the key type.\n\n    In C99, it is also always necessary to define HASH_FN and CMPR_FN (see below) before including the header.\n\n    The following macros may also be defined before including the header:\n\n      #define VAL_TY <type>\n\n        The type of the value associated with each key.\n        If this macro is defined, the hash table acts as a map associating keys with values.\n        Otherwise, it acts as a set containing only keys.\n\n      #define HASH_FN <function name>\n\n        The name of the existing function used to hash each key.\n        The function should have the signature uint64_t ( KEY_TY key ) and return a 64-bit hash code.\n        For best performance, the hash function should provide a high level of entropy across all bits.\n        There are two default hash functions: vt_hash_integer for all integer types up to 64 bits in size, and\n        vt_hash_string for NULL-terminated strings (i.e. char *).\n        When KEY_TY is one of such types and the compiler is in C11 mode or later, HASH_FN may be left undefined, in\n        which case the appropriate default function is inferred from KEY_TY.\n        Otherwise, HASH_FN must be defined.\n\n      #define CMPR_FN <function name>\n\n        The name of the existing function used to compare two keys.\n        The function should have the signature bool ( KEY_TY key_1, KEY_TY key_2 ) and return true if the two keys are\n        equal.\n        There are two default comparison functions: vt_cmpr_integer for all integer types up to 64 bits in size, and\n        vt_cmpr_string for NULL-terminated strings (i.e. char *).\n        As with the default hash functions, in C11 or later the appropriate default comparison function is inferred if\n        KEY_TY is one of such types and CMPR_FN is left undefined.\n        Otherwise, CMPR_FN must be defined.\n\n      #define MAX_LOAD <floating point value>\n\n        The floating-point load factor at which the hash table automatically doubles the size of its internal buckets\n        array.\n        The default is 0.9, i.e. 90%.\n\n      #define KEY_DTOR_FN <function name>\n\n        The name of the existing destructor function, with the signature void ( KEY_TY key ), called on a key when it is\n        erased from the table or replaced by a newly inserted key.\n        The API functions that may call the key destructor are NAME_insert, NAME_erase, NAME_erase_itr, NAME_clear,\n        and NAME_cleanup.\n\n      #define VAL_DTOR_FN <function name>\n\n        The name of the existing destructor function, with the signature void ( VAL_TY val ), called on a value when it\n        is erased from the table or replaced by a newly inserted value.\n        The API functions that may call the value destructor are NAME_insert, NAME_erase, NAME_erase_itr, NAME_clear,\n        and NAME_cleanup.\n\n      #define CTX_TY <type>\n\n        The type of the hash table type's ctx (context) member.\n        This member only exists if CTX_TY was defined.\n        It is intended to be used in conjunction with MALLOC_FN and FREE_FN (see below).\n\n      #define MALLOC_FN <function name>\n\n        The name of the existing function used to allocate memory.\n        If CTX_TY was defined, the signature should be void *( size_t size, CTX_TY *ctx ), where size is the number of\n        bytes to allocate and ctx points to the table's ctx member.\n        Otherwise, the signature should be void *( size_t size ).\n        The default wraps stdlib.h's malloc.\n\n      #define FREE_FN <function name>\n\n        The name of the existing function used to free memory.\n        If CTX_TY was defined, the signature should be void ( void *ptr, size_t size, CTX_TY *ctx ), where ptr points to\n        the memory to free, size is the number of bytes that were allocated, and ctx points to the table's ctx member.\n        Otherwise, the signature should be void ( void *ptr, size_t size ).\n        The default wraps stdlib.h's free.\n\n      #define HEADER_MODE\n      #define IMPLEMENTATION_MODE\n\n        By default, all hash table functions are defined as static inline functions, the intent being that a given hash\n        table template should be instantiated once per translation unit; for best performance, this is the recommended\n        way to use the library.\n        However, it is also possible separate the struct definitions and function declarations from the function\n        definitions such that one implementation can be shared across all translation units (as in a traditional header\n        and source file pair).\n        In that case, instantiate a template wherever it is needed by defining HEADER_MODE, along with only NAME,\n        KEY_TY, and (optionally) VAL_TY, CTX_TY, and header guards, and including the library, e.g.:\n\n          #ifndef INT_INT_MAP_H\n          #define INT_INT_MAP_H\n          #define NAME   int_int_map\n          #define KEY_TY int\n          #define VAL_TY int\n          #define HEADER_MODE\n          #include \"verstable.h\"\n          #endif\n\n        In one source file, define IMPLEMENTATION_MODE, along with NAME, KEY_TY, and any of the aforementioned optional\n        macros, and include the library, e.g.:\n\n          #define NAME     int_int_map\n          #define KEY_TY   int\n          #define VAL_TY   int\n          #define HASH_FN  vt_hash_integer // C99.\n          #define CMPR_FN  vt_cmpr_integer // C99.\n          #define MAX_LOAD 0.8\n          #define IMPLEMENTATION_MODE\n          #include \"verstable.h\"\n\n    Including the library automatically undefines all the aforementioned macros after they have been used to instantiate\n    the template.\n\n  Functions:\n\n    The functions associated with a hash table type are all prefixed with the name the user supplied via the NAME macro.\n    In C11 and later, the generic \"vt_\"-prefixed macros may be used to automatically select the correct version of the\n    specified function based on the arguments.\n\n    void NAME_init( NAME *table )\n    void NAME_init( NAME *table, CTX_TY ctx )\n    // C11 generic macro: vt_init.\n\n      Initializes the table for use.\n      If CTX_TY was defined, ctx sets the table's ctx member.\n\n    bool NAME_init_clone( NAME *table, NAME *source )\n    bool NAME_init_clone( NAME *table, NAME *source, CTX_TY ctx )\n    // C11 generic macro: vt_init_clone.\n\n      Initializes the table as a shallow copy of the specified source table.\n      If CTX_TY was defined, ctx sets the table's ctx member.\n      Returns false in the case of memory allocation failure.\n\n    size_t NAME_size( NAME *table ) // C11 generic macro: vt_size.\n\n      Returns the number of keys currently in the table.\n\n    size_t NAME_bucket_count( NAME *table ) // C11 generic macro: vt_bucket_count.\n\n      Returns the table's current bucket count.\n\n    NAME_itr NAME_insert( NAME *table, KEY_TY key )\n    NAME_itr NAME_insert( NAME *table, KEY_TY key, VAL_TY val )\n    // C11 generic macro: vt_insert.\n\n      Inserts the specified key (and value, if VAL_TY was defined) into the hash table.\n      If the same key already exists, then the new key (and value) replaces the existing key (and value).\n      Returns an iterator to the new key, or an end iterator in the case of memory allocation failure.\n\n    NAME_itr NAME_get_or_insert( NAME *table, KEY_TY key )\n    NAME_itr NAME_get_or_insert( NAME *table, KEY_TY key, VAL_TY val )\n    // C11 generic macro: vt_get_or_insert.\n\n      Inserts the specified key (and value, if VAL_TY was defined) if it does not already exist in the table.\n      Returns an iterator to the new key if it was inserted, or an iterator to the existing key, or an end iterator if\n      the key did not exist but the new key could not be inserted because of memory allocation failure.\n      Determine whether the key was inserted by comparing the table's size before and after the call.\n\n    NAME_itr NAME_get( NAME *table, KEY_TY key ) // C11 generic macro: vt_get.\n\n      Returns a iterator to the specified key, or an end iterator if no such key exists.\n\n    bool NAME_erase( NAME *table, KEY_TY key ) // C11 generic macro: vt_erase.\n\n      Erases the specified key (and associated value, if VAL_TY was defined), if it exists.\n      Returns true if a key was erased.\n\n    NAME_itr NAME_erase_itr( NAME *table, NAME_itr itr ) // C11 generic macro: vt_erase_itr.\n\n      Erases the key (and associated value, if VAL_TY was defined) pointed to by the specified iterator.\n      Returns an iterator to the next key in the table, or an end iterator if the erased key was the last one.\n\n    bool NAME_reserve( NAME *table, size_t size ) // C11 generic macro: vt_reserve.\n\n      Ensures that the bucket count is large enough to support the specified key count (i.e. size) without rehashing.\n      Returns false if unsuccessful due to memory allocation failure.\n\n    bool NAME_shrink( NAME *table ) // C11 generic macro: vt_shrink.\n\n      Shrinks the bucket count to best accommodate the current size.\n      Returns false if unsuccessful due to memory allocation failure.\n\n    NAME_itr NAME_first( NAME *table ) // C11 generic macro: vt_first.\n\n      Returns an iterator to the first key in the table, or an end iterator if the table is empty.\n\n    bool NAME_is_end( NAME *table, NAME_itr itr ) // C11 generic macro: vt_is_end.\n\n      Returns true if the iterator is an end iterator.\n\n    NAME_itr NAME_next( NAME_itr itr ) // C11 generic macro: vt_next.\n\n      Returns an iterator to the key after the one pointed to by the specified iterator, or an end iterator if the\n      specified iterator points to the last key in the table.\n\n    void NAME_clear( NAME *table ) // C11 generic macro: vt_clear.\n\n      Erases all keys (and values, if VAL_TY was defined) in the table.\n\n    void NAME_cleanup( NAME *table ) // C11 generic macro: vt_cleanup.\n\n      Erases all keys (and values, if VAL_TY was defined) in the table, frees all memory associated with it, and\n      initializes it for reuse.\n\n  Iterators:\n\n    Access the key (and value, if VAL_TY was defined) that an iterator points to using the NAME_itr struct's data\n    member:\n\n      itr.data->key\n      itr.data->val\n\n    Functions that may insert new keys (NAME_insert and NAME_get_or_insert), erase keys (NAME_erase and NAME_erase_itr),\n    or reallocate the internal bucket array (NAME_reserve and NAME_shrink) invalidate all exiting iterators.\n    To delete keys during iteration and resume iterating, use the return value of NAME_erase_itr.\n\nVersion history:\n\n  18/06/2024 2.1.1: Fixed a bug affecting iteration on big-endian platforms under MSVC.\n  27/05/2024 2.1.0: Replaced the Murmur3 mixer with the fast-hash mixer as the default integer hash function.\n                    Fixed a bug that could theoretically cause a crash on rehash (triggerable in testing using\n                    NAME_shrink with a maximum load factor significantly higher than 1.0).\n  06/02/2024 2.0.0: Improved custom allocator support by introducing the CTX_TY option and allowing user-supplied free\n                    functions to receive the allocation size.\n                    Improved documentation.\n                    Introduced various optimizations, including storing the buckets-array size mask instead of the\n                    bucket count, eliminating empty-table checks, combining the buckets memory and metadata memory into\n                    one allocation, and adding branch prediction macros.\n                    Fixed a bug that caused a key to be used after destruction during erasure.\n  12/12/2023 1.0.0: Initial release.\n\nLicense (MIT):\n\n  Copyright (c) 2023-2024 Jackson L. Allan\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n  documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\n  persons to whom the Software is furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n  Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n*/\n\n/*--------------------------------------------------------------------------------------------------------------------*/\n/*                                               Common header section                                                */\n/*--------------------------------------------------------------------------------------------------------------------*/\n\n#ifndef VERSTABLE_H\n#define VERSTABLE_H\n\n#include <limits.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\n// Two-way concatenation macro.\n#define VT_CAT_( a, b ) a##b\n#define VT_CAT( a, b ) VT_CAT_( a, b )\n\n// Branch optimization macros.\n#ifdef __GNUC__\n#define VT_LIKELY( expression )   __builtin_expect( (bool)( expression ), true )\n#define VT_UNLIKELY( expression ) __builtin_expect( (bool)( expression ), false )\n#else\n#define VT_LIKELY( expression )   ( expression )\n#define VT_UNLIKELY( expression ) ( expression )\n#endif\n\n// Masks for manipulating and extracting data from a bucket's uint16_t metadatum.\n#define VT_EMPTY               0x0000\n#define VT_HASH_FRAG_MASK      0xF000 // 0b1111000000000000.\n#define VT_IN_HOME_BUCKET_MASK 0x0800 // 0b0000100000000000.\n#define VT_DISPLACEMENT_MASK   0x07FF // 0b0000011111111111, also denotes the displacement limit. Set to VT_LOAD to 1.0\n                                      // to test proper handling of encroachment on the displacement limit during\n                                      // inserts.\n\n// Extracts a hash fragment from a uint64_t hash code.\n// We take the highest four bits so that keys that map (via modulo) to the same bucket have distinct hash fragments.\nstatic inline uint16_t vt_hashfrag( uint64_t hash )\n{\n  return ( hash >> 48 ) & VT_HASH_FRAG_MASK;\n}\n\n// Standard quadratic probing formula that guarantees that all buckets are visited when the bucket count is a power of\n// two (at least in theory, because the displacement limit could terminate the search early when the bucket count is\n// high).\nstatic inline size_t vt_quadratic( uint16_t displacement )\n{\n  return ( (size_t)displacement * displacement + displacement ) / 2;\n}\n\n#define VT_MIN_NONZERO_BUCKET_COUNT 8 // Must be a power of two.\n\n// Function to find the left-most non-zero uint16_t in a uint64_t.\n// This function is used when we scan four buckets at a time while iterating and relies on compiler intrinsics wherever\n// possible.\n\n#if defined( __GNUC__ ) && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF\n\nstatic inline int vt_first_nonzero_uint16( uint64_t val )\n{\n  const uint16_t endian_checker = 0x0001;\n  if( *(const char *)&endian_checker ) // Little-endian (the compiler will optimize away the check at -O1 and above).\n    return __builtin_ctzll( val ) / 16;\n\n  return __builtin_clzll( val ) / 16;\n}\n\n#elif defined( _MSC_VER ) && ( defined( _M_X64 ) || defined( _M_ARM64 ) )\n\n#include <intrin.h>\n#pragma intrinsic(_BitScanForward64)\n#pragma intrinsic(_BitScanReverse64)\n\nstatic inline int vt_first_nonzero_uint16( uint64_t val )\n{\n  unsigned long result;\n\n  const uint16_t endian_checker = 0x0001;\n  if( *(const char *)&endian_checker )\n    _BitScanForward64( &result, val );\n  else\n  {\n    _BitScanReverse64( &result, val );\n    result = 63 - result;\n  }\n\n  return result / 16;\n}\n\n#else\n\nstatic inline int vt_first_nonzero_uint16( uint64_t val )\n{\n  int result = 0;\n\n  uint32_t half;\n  memcpy( &half, &val, sizeof( uint32_t ) );\n  if( !half )\n    result += 2;\n\n  uint16_t quarter;\n  memcpy( &quarter, (char *)&val + result * sizeof( uint16_t ), sizeof( uint16_t ) );\n  if( !quarter )\n    result += 1;\n\n  return result;\n}\n\n#endif\n\n// When the bucket count is zero, setting the metadata pointer to point to a VT_EMPTY placeholder, rather than NULL,\n// allows us to avoid checking for a zero bucket count during insertion and lookup.\nstatic const uint16_t vt_empty_placeholder_metadatum = VT_EMPTY;\n\n// Default hash and comparison functions.\n\n// Fast-hash, as described by https://jonkagstrom.com/bit-mixer-construction and\n// https://code.google.com/archive/p/fast-hash.\n// In testing, this hash function provided slightly better performance than the Murmur3 mixer.\nstatic inline uint64_t vt_hash_integer( uint64_t key )\n{\n  key ^= key >> 23;\n  key *= 0x2127599bf4325c37ull;\n  key ^= key >> 47;\n  return key;\n}\n\n// FNV-1a.\nstatic inline uint64_t vt_hash_string( const char *key )\n{\n  uint64_t hash = 0xcbf29ce484222325ull;\n  while( *key )\n    hash = ( (unsigned char)*key++ ^ hash ) * 0x100000001b3ull;\n\n  return hash;\n}\n\nstatic inline bool vt_cmpr_integer( uint64_t key_1, uint64_t key_2 )\n{\n  return key_1 == key_2;\n}\n\nstatic inline bool vt_cmpr_string( const char *key_1, const char *key_2 )\n{\n  return strcmp( key_1, key_2 ) == 0;\n}\n\n// Default allocation and free functions.\n\nstatic inline void *vt_malloc( size_t size )\n{\n  return malloc( size );\n}\n\nstatic inline void vt_free( void *ptr, size_t size )\n{\n  (void)size;\n  free( ptr );\n}\n\nstatic inline void *vt_malloc_with_ctx( size_t size, void *ctx )\n{\n  (void)ctx;\n  return malloc( size );\n}\n\nstatic inline void vt_free_with_ctx( void *ptr, size_t size, void *ctx )\n{\n  (void)size;\n  (void)ctx;\n  free( ptr );\n}\n\n// The rest of the common header section pertains to the C11 generic macro API.\n// This interface is based on the extendible-_Generic mechanism documented in detail at\n// https://github.com/JacksonAllan/CC/blob/main/articles/Better_C_Generics_Part_1_The_Extendible_Generic.md.\n// In summary, instantiating a template also defines wrappers for the template's types and functions with names in the\n// pattern of vt_table_NNNN and vt_init_NNNN, where NNNN is an automatically generated integer unique to the template\n// instance in the current translation unit.\n// These wrappers plug in to _Generic-based API macros, which use preprocessor magic to automatically generate _Generic\n// slots for every existing template instance.\n#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined( VT_NO_C11_GENERIC_API )\n\n// Octal counter that supports up to 511 hash table templates.\n#define VT_TEMPLATE_COUNT_D1 0 // Digit 1, i.e. least significant digit.\n#define VT_TEMPLATE_COUNT_D2 0\n#define VT_TEMPLATE_COUNT_D3 0\n\n// Four-way concatenation macro.\n#define VT_CAT_4_( a, b, c, d ) a##b##c##d\n#define VT_CAT_4( a, b, c, d )  VT_CAT_4_( a, b, c, d )\n\n// Provides the current value of the counter as a three-digit octal number preceded by 0.\n#define VT_TEMPLATE_COUNT VT_CAT_4( 0, VT_TEMPLATE_COUNT_D3, VT_TEMPLATE_COUNT_D2, VT_TEMPLATE_COUNT_D1 )\n\n// _Generic-slot generation macros.\n\n#define VT_GENERIC_SLOT( ty, fn, n ) , VT_CAT( ty, n ): VT_CAT( fn, n )\n#define VT_R1_0( ty, fn, d3, d2 )\n#define VT_R1_1( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 0 ) )\n#define VT_R1_2( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 1 ) ) VT_R1_1( ty, fn, d3, d2 )\n#define VT_R1_3( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 2 ) ) VT_R1_2( ty, fn, d3, d2 )\n#define VT_R1_4( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 3 ) ) VT_R1_3( ty, fn, d3, d2 )\n#define VT_R1_5( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 4 ) ) VT_R1_4( ty, fn, d3, d2 )\n#define VT_R1_6( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 5 ) ) VT_R1_5( ty, fn, d3, d2 )\n#define VT_R1_7( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 6 ) ) VT_R1_6( ty, fn, d3, d2 )\n#define VT_R1_8( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 7 ) ) VT_R1_7( ty, fn, d3, d2 )\n#define VT_R2_0( ty, fn, d3 )\n#define VT_R2_1( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 0 )\n#define VT_R2_2( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 1 ) VT_R2_1( ty, fn, d3 )\n#define VT_R2_3( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 2 ) VT_R2_2( ty, fn, d3 )\n#define VT_R2_4( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 3 ) VT_R2_3( ty, fn, d3 )\n#define VT_R2_5( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 4 ) VT_R2_4( ty, fn, d3 )\n#define VT_R2_6( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 5 ) VT_R2_5( ty, fn, d3 )\n#define VT_R2_7( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 6 ) VT_R2_6( ty, fn, d3 )\n#define VT_R2_8( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 7 ) VT_R2_7( ty, fn, d3 )\n#define VT_R3_0( ty, fn )\n#define VT_R3_1( ty, fn ) VT_R2_8( ty, fn, 0 )\n#define VT_R3_2( ty, fn ) VT_R2_8( ty, fn, 1 ) VT_R3_1( ty, fn )\n#define VT_R3_3( ty, fn ) VT_R2_8( ty, fn, 2 ) VT_R3_2( ty, fn )\n#define VT_R3_4( ty, fn ) VT_R2_8( ty, fn, 3 ) VT_R3_3( ty, fn )\n#define VT_R3_5( ty, fn ) VT_R2_8( ty, fn, 4 ) VT_R3_4( ty, fn )\n#define VT_R3_6( ty, fn ) VT_R2_8( ty, fn, 5 ) VT_R3_5( ty, fn )\n#define VT_R3_7( ty, fn ) VT_R2_8( ty, fn, 6 ) VT_R3_6( ty, fn )\n\n#define VT_GENERIC_SLOTS( ty, fn )                                                           \\\nVT_CAT( VT_R1_, VT_TEMPLATE_COUNT_D1 )( ty, fn, VT_TEMPLATE_COUNT_D3, VT_TEMPLATE_COUNT_D2 ) \\\nVT_CAT( VT_R2_, VT_TEMPLATE_COUNT_D2 )( ty, fn, VT_TEMPLATE_COUNT_D3 )                       \\\nVT_CAT( VT_R3_, VT_TEMPLATE_COUNT_D3 )( ty, fn )                                             \\\n\n// Actual generic API macros.\n\n// vt_init must be handled as a special case because it could take one or two arguments, depending on whether CTX_TY\n// was defined.\n#define VT_ARG_3( _1, _2, _3, ... ) _3\n#define vt_init( ... ) VT_ARG_3( __VA_ARGS__, vt_init_with_ctx, vt_init_without_ctx, )( __VA_ARGS__ )\n#define vt_init_without_ctx( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_init_ ) )( table )\n#define vt_init_with_ctx( table, ... ) _Generic( *( table ) \\\n  VT_GENERIC_SLOTS( vt_table_, vt_init_ )                   \\\n)( table, __VA_ARGS__ )                                     \\\n\n#define vt_init_clone( table, ... ) _Generic( *( table ) \\\n  VT_GENERIC_SLOTS( vt_table_, vt_init_clone_ )          \\\n)( table, __VA_ARGS__ )                                  \\\n\n#define vt_size( table )_Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_size_ ) )( table )\n\n#define vt_bucket_count( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_bucket_count_ ) )( table )\n\n#define vt_is_end( itr ) _Generic( itr VT_GENERIC_SLOTS( vt_table_itr_, vt_is_end_ ) )( itr )\n\n#define vt_insert( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_insert_ ) )( table, __VA_ARGS__ )\n\n#define vt_get_or_insert( table, ... ) _Generic( *( table ) \\\n  VT_GENERIC_SLOTS( vt_table_, vt_get_or_insert_ )          \\\n)( table, __VA_ARGS__ )                                     \\\n\n#define vt_get( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_get_ ) )( table, __VA_ARGS__ )\n\n#define vt_erase( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_erase_ ) )( table, __VA_ARGS__ )\n\n#define vt_next( itr ) _Generic( itr VT_GENERIC_SLOTS( vt_table_itr_, vt_next_ ) )( itr )\n\n#define vt_erase_itr( table, ... ) _Generic( *( table ) \\\n  VT_GENERIC_SLOTS( vt_table_, vt_erase_itr_ )          \\\n)( table, __VA_ARGS__ )                                 \\\n\n#define vt_reserve( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_reserve_ ) )( table, __VA_ARGS__ )\n\n#define vt_shrink( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_shrink_ ) )( table )\n\n#define vt_first( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_first_ ) )( table )\n\n#define vt_clear( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_clear_ ) )( table )\n\n#define vt_cleanup( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_cleanup_ ) )( table )\n\n#endif\n\n#endif\n\n/*--------------------------------------------------------------------------------------------------------------------*/\n/*                                                  Prefixed structs                                                  */\n/*--------------------------------------------------------------------------------------------------------------------*/\n\n#ifndef IMPLEMENTATION_MODE\n\ntypedef struct\n{\n  KEY_TY key;\n  #ifdef VAL_TY\n  VAL_TY val;\n  #endif\n} VT_CAT( NAME, _bucket );\n\ntypedef struct\n{\n  VT_CAT( NAME, _bucket ) *data;\n  uint16_t *metadatum;\n  uint16_t *metadata_end; // Iterators carry an internal end pointer so that NAME_is_end does not need the table to be\n                          // passed in as an argument.\n                          // This also allows for the zero-bucket-count check to occur once in NAME_first, rather than\n                          // repeatedly in NAME_is_end.\n  size_t home_bucket; // SIZE_MAX if home bucket is unknown.\n} VT_CAT( NAME, _itr );\n\ntypedef struct\n{\n  size_t key_count;\n  size_t buckets_mask; // Rather than storing the bucket count directly, we store the bit mask used to reduce a hash\n                       // code or displacement-derived bucket index to the buckets array, i.e. the bucket count minus\n                       // one.\n                       // Consequently, a zero bucket count (i.e. when .metadata points to the placeholder) constitutes\n                       // a special case, represented by all bits unset (i.e. zero).\n  VT_CAT( NAME, _bucket ) *buckets;\n  uint16_t *metadata; // As described above, each metadatum consists of a 4-bit hash-code fragment (X), a 1-bit flag\n                      // indicating whether the key in this bucket begins a chain associated with the bucket (Y), and\n                      // an 11-bit value indicating the quadratic displacement of the next key in the chain (Z):\n                      // XXXXYZZZZZZZZZZZ.\n  #ifdef CTX_TY\n  CTX_TY ctx;\n  #endif\n} NAME;\n\n#endif\n\n/*--------------------------------------------------------------------------------------------------------------------*/\n/*                                                Function prototypes                                                 */\n/*--------------------------------------------------------------------------------------------------------------------*/\n\n#if defined( HEADER_MODE ) || defined( IMPLEMENTATION_MODE )\n#define VT_API_FN_QUALIFIERS\n#else\n#define VT_API_FN_QUALIFIERS static inline\n#endif\n\n#ifndef IMPLEMENTATION_MODE\n\nVT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )(\n  NAME *\n  #ifdef CTX_TY\n  , CTX_TY\n  #endif\n);\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _init_clone )(\n  NAME *,\n  NAME *\n  #ifdef CTX_TY\n  , CTX_TY\n  #endif\n);\n\nVT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _size )( const NAME * );\n\nVT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _bucket_count )( const NAME * );\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) );\n\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert )(\n  NAME *,\n  KEY_TY\n  #ifdef VAL_TY\n  , VAL_TY\n  #endif\n);\n\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get_or_insert )(\n  NAME *,\n  KEY_TY\n  #ifdef VAL_TY\n  , VAL_TY\n  #endif\n);\n\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get )(\n  NAME *table,\n  KEY_TY key\n);\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *, KEY_TY );\n\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _itr ) );\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _reserve )( NAME *, size_t );\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _shrink )( NAME * );\n\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _first )( NAME * );\n\nVT_API_FN_QUALIFIERS void VT_CAT( NAME, _clear )( NAME * );\n\nVT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME * );\n\n// Not an API function, but must be prototyped anyway because it is called by the inline NAME_erase_itr below.\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw ) ( NAME *, VT_CAT( NAME, _itr ) );\n\n// Erases the key pointed to by itr and returns an iterator to the next key in the table.\n// This function must be inlined to ensure that the compiler optimizes away the NAME_fast_forward call if the returned\n// iterator is discarded.\n#ifdef __GNUC__\nstatic inline __attribute__((always_inline))\n#elif defined( _MSC_VER )\nstatic __forceinline\n#else\nstatic inline\n#endif\nVT_CAT( NAME, _itr ) VT_CAT( NAME, _erase_itr )( NAME *table, VT_CAT( NAME, _itr ) itr )\n{\n  if( VT_CAT( NAME, _erase_itr_raw )( table, itr ) )\n    return VT_CAT( NAME, _next )( itr );\n\n  return itr;\n}\n\n#endif\n\n/*--------------------------------------------------------------------------------------------------------------------*/\n/*                                              Function implementations                                              */\n/*--------------------------------------------------------------------------------------------------------------------*/\n\n#ifndef HEADER_MODE\n\n// Default settings.\n\n#ifndef MAX_LOAD\n#define MAX_LOAD 0.9\n#endif\n\n#if !defined( MALLOC ) || !defined( FREE )\n#include <stdlib.h>\n#endif\n\n#ifndef MALLOC_FN\n#ifdef CTX_TY\n#define MALLOC_FN vt_malloc_with_ctx\n#else\n#define MALLOC_FN vt_malloc\n#endif\n#endif\n\n#ifndef FREE_FN\n#ifdef CTX_TY\n#define FREE_FN vt_free_with_ctx\n#else\n#define FREE_FN vt_free\n#endif\n#endif\n\n#ifndef HASH_FN\n#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n#ifdef _MSC_VER // In MSVC, the compound literal in the _Generic triggers a warning about unused local variables at /W4.\n#define HASH_FN                                                               \\\n_Pragma( \"warning( push )\" )                                                  \\\n_Pragma( \"warning( disable: 4189 )\" )                                         \\\n_Generic( ( KEY_TY ){ 0 }, char *: vt_hash_string, const char*: vt_hash_string, default: vt_hash_integer ) \\\n_Pragma( \"warning( pop )\" )\n#else\n#define HASH_FN _Generic( ( KEY_TY ){ 0 }, char *: vt_hash_string, const char*: vt_hash_string, default: vt_hash_integer )\n#endif\n#else\n#error Hash function inference is only available in C11 and later. In C99, you need to define HASH_FN manually to \\\nvt_hash_integer, vt_hash_string, or your own custom function with the signature uint64_t ( KEY_TY ).\n#endif\n#endif\n\n#ifndef CMPR_FN\n#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n#ifdef _MSC_VER\n#define CMPR_FN                                                               \\\n_Pragma( \"warning( push )\" )                                                  \\\n_Pragma( \"warning( disable: 4189 )\" )                                         \\\n_Generic( ( KEY_TY ){ 0 }, char *: vt_cmpr_string, const char*: vt_cmpr_string, default: vt_cmpr_integer ) \\\n_Pragma( \"warning( pop )\" )\n#else\n#define CMPR_FN _Generic( ( KEY_TY ){ 0 }, char *: vt_cmpr_string, const char*: vt_cmpr_string, default: vt_cmpr_integer )\n#endif\n#else\n#error Comparison function inference is only available in C11 and later. In C99, you need to define CMPR_FN manually \\\nto vt_cmpr_integer, vt_cmpr_string, or your own custom function with the signature bool ( KEY_TY, KEY_TY ).\n#endif\n#endif\n\nVT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )(\n  NAME *table\n  #ifdef CTX_TY\n  , CTX_TY ctx\n  #endif\n)\n{\n  table->key_count = 0;\n  table->buckets_mask = 0x0000000000000000ull;\n  table->buckets = NULL;\n  table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum;\n  #ifdef CTX_TY\n  table->ctx = ctx;\n  #endif\n}\n\n// For efficiency, especially in the case of a small table, the buckets array and metadata share the same dynamic memory\n// allocation:\n//   +-----------------------------+-----+----------------+--------+\n//   |           Buckets           | Pad |    Metadata    | Excess |\n//   +-----------------------------+-----+----------------+--------+\n// Any allocated metadata array requires four excess elements to ensure that iteration functions, which read four\n// metadata at a time, never read beyond the end of it.\n// This function returns the offset of the beginning of the metadata, i.e. the size of the buckets array plus the\n// (usually zero) padding.\n// It assumes that the bucket count is not zero.\nstatic inline size_t VT_CAT( NAME, _metadata_offset )( NAME *table )\n{\n  // Use sizeof, rather than alignof, for C99 compatibility.\n  return ( ( ( table->buckets_mask + 1 ) * sizeof( VT_CAT( NAME, _bucket ) ) + sizeof( uint16_t ) - 1 ) /\n    sizeof( uint16_t ) ) * sizeof( uint16_t );\n}\n\n// Returns the total allocation size, including the buckets array, padding, metadata, and excess metadata.\n// As above, this function assumes that the bucket count is not zero.\nstatic inline size_t VT_CAT( NAME, _total_alloc_size )( NAME *table )\n{\n  return VT_CAT( NAME, _metadata_offset )( table ) + ( table->buckets_mask + 1 + 4 ) * sizeof( uint16_t );\n}\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _init_clone )(\n  NAME *table,\n  NAME *source\n  #ifdef CTX_TY\n  , CTX_TY ctx\n  #endif\n)\n{\n  table->key_count = source->key_count;\n  table->buckets_mask = source->buckets_mask;\n  #ifdef CTX_TY\n  table->ctx = ctx;\n  #endif\n\n  if( !source->buckets_mask )\n  {\n    table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum;\n    table->buckets = NULL;\n    return true;\n  }\n\n  void *allocation = MALLOC_FN(\n    VT_CAT( NAME, _total_alloc_size )( table )\n    #ifdef CTX_TY\n    , &table->ctx\n    #endif\n  );\n\n  if( VT_UNLIKELY( !allocation ) )\n    return false;\n\n  table->buckets = (VT_CAT( NAME, _bucket ) *)allocation;\n  table->metadata = (uint16_t *)( (unsigned char *)allocation + VT_CAT( NAME, _metadata_offset )( table ) );\n  memcpy( allocation, source->buckets, VT_CAT( NAME, _total_alloc_size )( table ) );\n\n  return true;\n}\n\nVT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _size )( const NAME *table )\n{\n  return table->key_count;\n}\n\nVT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _bucket_count )( const NAME *table )\n{\n  // If the bucket count is zero, buckets_mask will be zero, not the bucket count minus one.\n  // We account for this special case by adding (bool)buckets_mask rather than one.\n  return table->buckets_mask + (bool)table->buckets_mask;\n}\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) itr )\n{\n  return itr.metadatum == itr.metadata_end;\n}\n\n// Finds the earliest empty bucket in which a key belonging to home_bucket can be placed, assuming that home_bucket\n// is already occupied.\n// The reason to begin the search at home_bucket, rather than the end of the existing chain, is that keys deleted from\n// other chains might have freed up buckets that could fall in this chain before the final key.\n// Returns true if an empty bucket within the range of the displacement limit was found, in which case the final two\n// pointer arguments contain the index of the empty bucket and its quadratic displacement from home_bucket.\nstatic inline bool VT_CAT( NAME, _find_first_empty )(\n  NAME *table,\n  size_t home_bucket,\n  size_t *empty,\n  uint16_t *displacement\n)\n{\n  *displacement = 1;\n  size_t linear_dispacement = 1;\n\n  while( true )\n  {\n    *empty = ( home_bucket + linear_dispacement ) & table->buckets_mask;\n    if( table->metadata[ *empty ] == VT_EMPTY )\n      return true;\n\n    if( VT_UNLIKELY( ++*displacement == VT_DISPLACEMENT_MASK ) )\n      return false;\n\n    linear_dispacement += *displacement;\n  }\n}\n\n// Finds the key in the chain beginning in home_bucket after which to link a new key with displacement_to_empty\n// quadratic displacement and returns the index of the bucket containing that key.\n// Although the new key could simply be linked to the end of the chain, keeping the chain ordered by displacement\n// theoretically improves cache locality during lookups.\nstatic inline size_t VT_CAT( NAME, _find_insert_location_in_chain )(\n  NAME *table,\n  size_t home_bucket,\n  uint16_t displacement_to_empty\n)\n{\n  size_t candidate = home_bucket;\n  while( true )\n  {\n    uint16_t displacement = table->metadata[ candidate ] & VT_DISPLACEMENT_MASK;\n\n    if( displacement > displacement_to_empty )\n      return candidate;\n\n    candidate = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask;\n  }\n}\n\n// Frees up a bucket occupied by a key not belonging there so that a new key belonging there can be placed there as the\n// beginning of a new chain.\n// This requires:\n// * Finding the previous key in the chain to which the occupying key belongs by rehashing it and then traversing the\n//   chain.\n// * Disconnecting the key from the chain.\n// * Finding the appropriate empty bucket to which to move the key.\n// * Moving the key (and value) data to the empty bucket.\n// * Re-linking the key to the chain.\n// Returns true if the eviction succeeded, or false if no empty bucket to which to evict the occupying key could be\n// found within the displacement limit.\nstatic inline bool VT_CAT( NAME, _evict )( NAME *table, size_t bucket )\n{\n  // Find the previous key in chain.\n  size_t home_bucket = HASH_FN( table->buckets[ bucket ].key ) & table->buckets_mask;\n  size_t prev = home_bucket;\n  while( true )\n  {\n    size_t next = ( home_bucket + vt_quadratic( table->metadata[ prev ] & VT_DISPLACEMENT_MASK ) ) &\n      table->buckets_mask;\n\n    if( next == bucket )\n      break;\n\n    prev = next;\n  }\n\n  // Disconnect the key from chain.\n  table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | ( table->metadata[ bucket ] &\n    VT_DISPLACEMENT_MASK );\n\n  // Find the empty bucket to which to move the key.\n  size_t empty;\n  uint16_t displacement;\n  if( VT_UNLIKELY( !VT_CAT( NAME, _find_first_empty )( table, home_bucket, &empty, &displacement ) ) )\n    return false;\n\n  // Find the key in the chain after which to link the moved key.\n  prev = VT_CAT( NAME, _find_insert_location_in_chain )( table, home_bucket, displacement );\n\n  // Move the key (and value) data.\n  table->buckets[ empty ] = table->buckets[ bucket ];\n\n  // Re-link the key to the chain from its new bucket.\n  table->metadata[ empty ] = ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) | ( table->metadata[ prev ] &\n    VT_DISPLACEMENT_MASK );\n  table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | displacement;\n\n  return true;\n}\n\n// Returns an end iterator, i.e. any iterator for which .metadatum == .metadata_end.\n// This function just cleans up the library code in functions that return an end iterator as a failure indicator.\nstatic inline VT_CAT( NAME, _itr ) VT_CAT( NAME, _end_itr )( void )\n{\n  VT_CAT( NAME, _itr ) itr = { NULL, NULL, NULL, 0 };\n  return itr;\n}\n\n// Inserts a key, optionally replacing the existing key if it already exists.\n// There are two main cases that must be handled:\n// * If the key's home bucket is empty or occupied by a key that does not belong there, then the key is inserted there,\n//   evicting the occupying key if there is one.\n// * Otherwise, the chain of keys beginning at the home bucket is (if unique is false) traversed in search of a matching\n//   key.\n//   If none is found, then the new key is inserted at the earliest available bucket, per quadratic probing from the\n//   home bucket, and then linked to the chain in a manner that maintains its quadratic order.\n// The unique argument tells the function whether to skip searching for the key before inserting it (on rehashing, this\n// step is unnecessary).\n// The replace argument tells the function whether to replace an existing key.\n// If replace is true, the function returns an iterator to the inserted key, or an end iterator if the key was not\n// inserted because of the maximum load factor or displacement limit constraints.\n// If replace is false, then the return value is as described above, except that if the key already exists, the function\n// returns an iterator to the existing key.\nstatic inline VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert_raw )(\n  NAME *table,\n  KEY_TY key,\n  #ifdef VAL_TY\n  VAL_TY *val,\n  #endif\n  bool unique,\n  bool replace\n)\n{\n  uint64_t hash = HASH_FN( key );\n  uint16_t hashfrag = vt_hashfrag( hash );\n  size_t home_bucket = hash & table->buckets_mask;\n\n  // Case 1: The home bucket is empty or contains a key that doesn't belong there.\n  // This case also implicitly handles the case of a zero bucket count, since home_bucket will be zero and metadata[ 0 ]\n  // will be the empty placeholder.\n  // In that scenario, the zero buckets_mask triggers the below load-factor check.\n  if( !( table->metadata[ home_bucket ] & VT_IN_HOME_BUCKET_MASK ) )\n  {\n    if(\n      // Load-factor check.\n      VT_UNLIKELY( table->key_count + 1 > VT_CAT( NAME, _bucket_count )( table ) * MAX_LOAD ) ||\n      // Vacate the home bucket if it contains a key.\n      ( table->metadata[ home_bucket ] != VT_EMPTY && VT_UNLIKELY( !VT_CAT( NAME, _evict )( table, home_bucket ) ) )\n    )\n      return VT_CAT( NAME, _end_itr )();\n\n    table->buckets[ home_bucket ].key = key;\n    #ifdef VAL_TY\n    table->buckets[ home_bucket ].val = *val;\n    #endif\n    table->metadata[ home_bucket ] = hashfrag | VT_IN_HOME_BUCKET_MASK | VT_DISPLACEMENT_MASK;\n\n    ++table->key_count;\n\n    VT_CAT( NAME, _itr ) itr = {\n      table->buckets + home_bucket,\n      table->metadata + home_bucket,\n      table->metadata + table->buckets_mask + 1, // Iteration stopper (i.e. the first of the four excess metadata).\n      home_bucket\n    };\n    return itr;\n  }\n\n  // Case 2: The home bucket contains the beginning of a chain.\n\n  // Optionally, check the existing chain.\n  if( !unique )\n  {\n    size_t bucket = home_bucket;\n    while( true )\n    {\n      if(\n        ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) == hashfrag &&\n        VT_LIKELY( CMPR_FN( table->buckets[ bucket ].key, key ) )\n      )\n      {\n        if( replace )\n        {\n          #ifdef KEY_DTOR_FN\n          KEY_DTOR_FN( table->buckets[ bucket ].key );\n          #endif\n          table->buckets[ bucket ].key = key;\n\n          #ifdef VAL_TY\n          #ifdef VAL_DTOR_FN\n          VAL_DTOR_FN( table->buckets[ bucket ].val );\n          #endif\n          table->buckets[ bucket ].val = *val;\n          #endif\n        }\n\n        VT_CAT( NAME, _itr ) itr = {\n          table->buckets + bucket,\n          table->metadata + bucket,\n          table->metadata + table->buckets_mask + 1,\n          home_bucket\n        };\n        return itr;\n      }\n\n      uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK;\n      if( displacement == VT_DISPLACEMENT_MASK )\n        break;\n\n      bucket = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask;\n    }\n  }\n\n  size_t empty;\n  uint16_t displacement;\n  if(\n    VT_UNLIKELY(\n      // Load-factor check.\n      table->key_count + 1 > VT_CAT( NAME, _bucket_count )( table ) * MAX_LOAD ||\n      // Find the earliest empty bucket, per quadratic probing.\n      !VT_CAT( NAME, _find_first_empty )( table, home_bucket, &empty, &displacement )\n    )\n  )\n    return VT_CAT( NAME, _end_itr )();\n\n  // Insert the new key (and value) in the empty bucket and link it to the chain.\n\n  size_t prev = VT_CAT( NAME, _find_insert_location_in_chain )( table, home_bucket, displacement );\n\n  table->buckets[ empty ].key = key;\n  #ifdef VAL_TY\n  table->buckets[ empty ].val = *val;\n  #endif\n  table->metadata[ empty ] = hashfrag | ( table->metadata[ prev ] & VT_DISPLACEMENT_MASK );\n  table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | displacement;\n\n  ++table->key_count;\n\n  VT_CAT( NAME, _itr ) itr = {\n    table->buckets + empty,\n    table->metadata + empty,\n    table->metadata + table->buckets_mask + 1,\n    home_bucket\n  };\n  return itr;\n}\n\n// Resizes the bucket array.\n// This function assumes that bucket_count is a power of two and large enough to accommodate all keys without violating\n// the maximum load factor.\n// Returns false in the case of allocation failure.\n// As this function is called very rarely in _insert and _get_or_insert, ideally it should not be inlined into those\n// functions.\n// In testing, the no-inline approach showed a performance benefit when inserting existing keys (i.e. replacing).\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wattributes\" // Silence warning about combining noinline with static inline.\n__attribute__((noinline)) static inline\n#elif defined( _MSC_VER )\n__declspec(noinline) static inline\n#else\nstatic inline\n#endif\nbool VT_CAT( NAME, _rehash )( NAME *table, size_t bucket_count )\n{\n  // The attempt to resize the bucket array and rehash the keys must occur inside a loop that incrementally doubles the\n  // target bucket count because a failure could theoretically occur at any load factor due to the displacement limit.\n  while( true )\n  {\n    NAME new_table =  {\n      0,\n      bucket_count - 1,\n      NULL,\n      NULL\n      #ifdef CTX_TY\n      , table->ctx\n      #endif\n    };\n\n    void *allocation = MALLOC_FN(\n      VT_CAT( NAME, _total_alloc_size )( &new_table )\n      #ifdef CTX_TY\n      , &new_table.ctx\n      #endif\n    );\n\n    if( VT_UNLIKELY( !allocation ) )\n      return false;\n\n    new_table.buckets = (VT_CAT( NAME, _bucket ) *)allocation;\n    new_table.metadata = (uint16_t *)( (unsigned char *)allocation + VT_CAT( NAME, _metadata_offset )( &new_table ) );\n\n    memset( new_table.metadata, 0x00, ( bucket_count + 4 ) * sizeof( uint16_t ) );\n\n    // Iteration stopper at the end of the actual metadata array (i.e. the first of the four excess metadata).\n    new_table.metadata[ bucket_count ] = 0x01;\n\n    for( size_t bucket = 0; bucket < VT_CAT( NAME, _bucket_count )( table ); ++bucket )\n      if( table->metadata[ bucket ] != VT_EMPTY )\n      {\n        VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )(\n          &new_table,\n          table->buckets[ bucket ].key,\n          #ifdef VAL_TY\n          &table->buckets[ bucket ].val,\n          #endif\n          true,\n          false\n        );\n\n        if( VT_UNLIKELY( VT_CAT( NAME, _is_end )( itr ) ) )\n          break;\n      }\n\n    // If a key could not be reinserted due to the displacement limit, double the bucket count and retry.\n    if( VT_UNLIKELY( new_table.key_count < table->key_count ) )\n    {\n      FREE_FN(\n        new_table.buckets,\n        VT_CAT( NAME, _total_alloc_size )( &new_table )\n        #ifdef CTX_TY\n        , &new_table.ctx\n        #endif\n      );\n\n      bucket_count *= 2;\n      continue;\n    }\n\n    if( table->buckets_mask )\n      FREE_FN(\n        table->buckets,\n        VT_CAT( NAME, _total_alloc_size )( table )\n        #ifdef CTX_TY\n        , &table->ctx\n        #endif\n      );\n\n    *table = new_table;\n    return true;\n  }\n}\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n\n// Inserts a key, replacing the existing key if it already exists.\n// This function wraps insert_raw in a loop that handles growing and rehashing the table if a new key cannot be inserted\n// because of the maximum load factor or displacement limit constraints.\n// Returns an iterator to the inserted key, or an end iterator in the case of allocation failure.\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert )(\n  NAME *table,\n  KEY_TY key\n  #ifdef VAL_TY\n  , VAL_TY val\n  #endif\n)\n{\n  while( true )\n  {\n    VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )(\n      table,\n      key,\n      #ifdef VAL_TY\n      &val,\n      #endif\n      false,\n      true\n    );\n\n    if(\n      // Lookup succeeded, in which case itr points to the found key.\n      VT_LIKELY( !VT_CAT( NAME, _is_end )( itr ) ) ||\n      // Lookup failed and rehash also fails, in which case itr is an end iterator.\n      VT_UNLIKELY(\n        !VT_CAT( NAME, _rehash )(\n          table, table->buckets_mask ? VT_CAT( NAME, _bucket_count )( table ) * 2 : VT_MIN_NONZERO_BUCKET_COUNT\n        )\n      )\n    )\n      return itr;\n  }\n}\n\n// Same as NAME_insert, except that if the key already exists, no insertion occurs and the function returns an iterator\n// to the existing key.\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get_or_insert )(\n  NAME *table,\n  KEY_TY key\n  #ifdef VAL_TY\n  , VAL_TY val\n  #endif\n)\n{\n  while( true )\n  {\n    VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )(\n      table,\n      key,\n      #ifdef VAL_TY\n      &val,\n      #endif\n      false,\n      false\n    );\n\n    if(\n      // Lookup succeeded, in which case itr points to the found key.\n      VT_LIKELY( !VT_CAT( NAME, _is_end )( itr ) ) ||\n      // Lookup failed and rehash also fails, in which case itr is an end iterator.\n      VT_UNLIKELY(\n        !VT_CAT( NAME, _rehash )(\n          table, table->buckets_mask ? VT_CAT( NAME, _bucket_count )( table ) * 2 : VT_MIN_NONZERO_BUCKET_COUNT\n        )\n      )\n    )\n      return itr;\n  }\n}\n\n// Returns an iterator pointing to the specified key, or an end iterator if the key does not exist.\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get )( NAME *table, KEY_TY key )\n{\n  uint64_t hash = HASH_FN( key );\n  size_t home_bucket = hash & table->buckets_mask;\n\n  // If the home bucket is empty or contains a key that does not belong there, then our key does not exist.\n  // This check also implicitly handles the case of a zero bucket count, since home_bucket will be zero and\n  // metadata[ 0 ] will be the empty placeholder.\n  if( !( table->metadata[ home_bucket ] & VT_IN_HOME_BUCKET_MASK ) )\n    return VT_CAT( NAME, _end_itr )();\n\n  // Traverse the chain of keys belonging to the home bucket.\n  uint16_t hashfrag = vt_hashfrag( hash );\n  size_t bucket = home_bucket;\n  while( true )\n  {\n    if(\n      ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) == hashfrag &&\n      VT_LIKELY( CMPR_FN( table->buckets[ bucket ].key, key ) )\n    )\n    {\n      VT_CAT( NAME, _itr ) itr = {\n        table->buckets + bucket,\n        table->metadata + bucket,\n        table->metadata + table->buckets_mask + 1,\n        home_bucket\n      };\n      return itr;\n    }\n\n    uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK;\n    if( displacement == VT_DISPLACEMENT_MASK )\n      return VT_CAT( NAME, _end_itr )();\n\n    bucket = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask;\n  }\n}\n\n// Erases the key pointed to by the specified iterator.\n// The erasure always occurs at the end of the chain to which the key belongs.\n// If the key to be erased is not the last in the chain, it is swapped with the last so that erasure occurs at the end.\n// This helps keep a chain's keys close to their home bucket for the sake of cache locality.\n// Returns true if, in the case of iteration from first to end, NAME_next should now be called on the iterator to find\n// the next key.\n// This return value is necessary because at the iterator location, the erasure could result in an empty bucket, a\n// bucket containing a moved key already visited during the iteration, or a bucket containing a moved key not yet\n// visited.\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw )( NAME *table, VT_CAT( NAME, _itr ) itr )\n{\n  --table->key_count;\n  size_t itr_bucket = itr.metadatum - table->metadata;\n\n  // For now, we only call the value's destructor because the key may need to be hashed below to determine the home\n  // bucket.\n  #ifdef VAL_DTOR_FN\n  VAL_DTOR_FN( table->buckets[ itr_bucket ].val );\n  #endif\n\n  // Case 1: The key is the only one in its chain, so just remove it.\n  if(\n    table->metadata[ itr_bucket ] & VT_IN_HOME_BUCKET_MASK &&\n    ( table->metadata[ itr_bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK\n  )\n  {\n    #ifdef KEY_DTOR_FN\n    KEY_DTOR_FN( table->buckets[ itr_bucket ].key );\n    #endif\n    table->metadata[ itr_bucket ] = VT_EMPTY;\n    return true;\n  }\n\n  // Case 2 and 3 require that we know the key's home bucket, which the iterator may not have recorded.\n  if( itr.home_bucket == SIZE_MAX )\n  {\n    if( table->metadata[ itr_bucket ] & VT_IN_HOME_BUCKET_MASK )\n      itr.home_bucket = itr_bucket;\n    else\n      itr.home_bucket = HASH_FN( table->buckets[ itr_bucket ].key ) & table->buckets_mask;\n  }\n\n  // The key can now be safely destructed for cases 2 and 3.\n  #ifdef KEY_DTOR_FN\n  KEY_DTOR_FN( table->buckets[ itr_bucket ].key );\n  #endif\n\n  // Case 2: The key is the last in a multi-key chain.\n  // Traverse the chain from the beginning and find the penultimate key.\n  // Then disconnect the key and erase.\n  if( ( table->metadata[ itr_bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK )\n  {\n    size_t bucket = itr.home_bucket;\n    while( true )\n    {\n      uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK;\n      size_t next = ( itr.home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask;\n      if( next == itr_bucket )\n      {\n        table->metadata[ bucket ] |= VT_DISPLACEMENT_MASK;\n        table->metadata[ itr_bucket ] = VT_EMPTY;\n        return true;\n      }\n\n      bucket = next;\n    }\n  }\n\n  // Case 3: The chain has multiple keys, and the key is not the last one.\n  // Traverse the chain from the key to be erased and find the last and penultimate keys.\n  // Disconnect the last key from the chain, and swap it with the key to erase.\n  size_t bucket = itr_bucket;\n  while( true )\n  {\n    size_t prev = bucket;\n    bucket = ( itr.home_bucket + vt_quadratic( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ) ) &\n      table->buckets_mask;\n\n    if( ( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK )\n    {\n      table->buckets[ itr_bucket ] = table->buckets[ bucket ];\n\n      table->metadata[ itr_bucket ] = ( table->metadata[ itr_bucket ] & ~VT_HASH_FRAG_MASK ) | (\n        table->metadata[ bucket ] & VT_HASH_FRAG_MASK );\n\n      table->metadata[ prev ] |= VT_DISPLACEMENT_MASK;\n      table->metadata[ bucket ] = VT_EMPTY;\n\n      // Whether the iterator should be advanced depends on whether the key moved to the iterator bucket came from\n      // before or after that bucket.\n      // In the former case, the iteration would already have hit the moved key, so the iterator should still be\n      // advanced.\n      if( bucket > itr_bucket )\n        return false;\n\n      return true;\n    }\n  }\n}\n\n// Erases the specified key, if it exists.\n// Returns true if a key was erased.\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *table, KEY_TY key )\n{\n  VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _get)( table, key );\n  if( VT_CAT( NAME, _is_end )( itr ) )\n    return false;\n\n  VT_CAT( NAME, _erase_itr_raw )( table, itr );\n  return true;\n}\n\n// Finds the first occupied bucket at or after the bucket pointed to by itr.\n// This function scans four buckets at a time, ideally using intrinsics.\nstatic inline void VT_CAT( NAME, _fast_forward )( VT_CAT( NAME, _itr ) *itr )\n{\n  while( true )\n  {\n    uint64_t metadata;\n    memcpy( &metadata, itr->metadatum, sizeof( uint64_t ) );\n    if( metadata )\n    {\n      int offset = vt_first_nonzero_uint16( metadata );\n      itr->data += offset;\n      itr->metadatum += offset;\n      itr->home_bucket = SIZE_MAX;\n      return;\n    }\n\n    itr->data += 4;\n    itr->metadatum += 4;\n  }\n}\n\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _itr ) itr )\n{\n  ++itr.data;\n  ++itr.metadatum;\n  VT_CAT( NAME, _fast_forward )( &itr );\n  return itr;\n}\n\n// Returns the minimum bucket count required to accommodate a certain number of keys, which is governed by the maximum\n// load factor.\nstatic inline size_t VT_CAT( NAME, _min_bucket_count_for_size )( size_t size )\n{\n  if( size == 0 )\n    return 0;\n\n  // Round up to a power of two.\n  size_t bucket_count = VT_MIN_NONZERO_BUCKET_COUNT;\n  while( size > bucket_count * MAX_LOAD )\n    bucket_count *= 2;\n\n  return bucket_count;\n}\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _reserve )( NAME *table, size_t size )\n{\n  size_t bucket_count = VT_CAT( NAME, _min_bucket_count_for_size )( size );\n\n  if( bucket_count <= VT_CAT( NAME, _bucket_count )( table ) )\n    return true;\n\n  return VT_CAT( NAME, _rehash )( table, bucket_count );\n}\n\nVT_API_FN_QUALIFIERS bool VT_CAT( NAME, _shrink )( NAME *table )\n{\n  size_t bucket_count = VT_CAT( NAME, _min_bucket_count_for_size )( table->key_count );\n\n  if( bucket_count == VT_CAT( NAME, _bucket_count )( table ) ) // Shrink unnecessary.\n    return true;\n\n  if( bucket_count == 0 )\n  {\n    FREE_FN(\n      table->buckets,\n      VT_CAT( NAME, _total_alloc_size )( table )\n      #ifdef CTX_TY\n      , &table->ctx\n      #endif\n    );\n\n    table->buckets_mask = 0x0000000000000000ull;\n    table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum;\n    return true;\n  }\n\n  return VT_CAT( NAME, _rehash )( table, bucket_count );\n}\n\nVT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _first )( NAME *table )\n{\n  if( !table->key_count )\n    return VT_CAT( NAME, _end_itr )();\n\n  VT_CAT( NAME, _itr ) itr = { table->buckets, table->metadata, table->metadata + table->buckets_mask + 1, SIZE_MAX };\n  VT_CAT( NAME, _fast_forward )( &itr );\n  return itr;\n}\n\nVT_API_FN_QUALIFIERS void VT_CAT( NAME, _clear )( NAME *table )\n{\n  if( !table->key_count )\n    return;\n\n  for( size_t i = 0; i < VT_CAT( NAME, _bucket_count )( table ); ++i )\n  {\n    if( table->metadata[ i ] != VT_EMPTY )\n    {\n      #ifdef KEY_DTOR_FN\n      KEY_DTOR_FN( table->buckets[ i ].key );\n      #endif\n      #ifdef VAL_DTOR_FN\n      VAL_DTOR_FN( table->buckets[ i ].val );\n      #endif\n    }\n\n    table->metadata[ i ] = VT_EMPTY;\n  }\n\n  table->key_count = 0;\n}\n\nVT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME *table )\n{\n  if( !table->buckets_mask )\n    return;\n\n  #if defined( KEY_DTOR_FN ) || defined( VAL_DTOR_FN )\n  VT_CAT( NAME, _clear )( table );\n  #endif\n\n  FREE_FN(\n    table->buckets,\n    VT_CAT( NAME, _total_alloc_size )( table )\n    #ifdef CTX_TY\n    , &table->ctx\n    #endif\n  );\n\n  VT_CAT( NAME, _init )(\n    table\n    #ifdef CTX_TY\n    , table->ctx\n    #endif\n  );\n}\n\n#endif\n\n/*--------------------------------------------------------------------------------------------------------------------*/\n/*                                Wrapper types and functions for the C11 generic API                                 */\n/*--------------------------------------------------------------------------------------------------------------------*/\n\n#if defined(__STDC_VERSION__) &&       \\\n    __STDC_VERSION__ >= 201112L &&     \\\n    !defined( IMPLEMENTATION_MODE ) && \\\n    !defined( VT_NO_C11_GENERIC_API )  \\\n\ntypedef NAME VT_CAT( vt_table_, VT_TEMPLATE_COUNT );\ntypedef VT_CAT( NAME, _itr ) VT_CAT( vt_table_itr_, VT_TEMPLATE_COUNT );\n\nstatic inline void VT_CAT( vt_init_, VT_TEMPLATE_COUNT )(\n  NAME *table\n  #ifdef CTX_TY\n  , CTX_TY ctx\n  #endif\n)\n{\n  VT_CAT( NAME, _init )(\n    table\n    #ifdef CTX_TY\n    , ctx\n    #endif\n  );\n}\n\nstatic inline bool VT_CAT( vt_init_clone_, VT_TEMPLATE_COUNT )(\n  NAME *table,\n  NAME* source\n  #ifdef CTX_TY\n  , CTX_TY ctx\n  #endif\n)\n{\n  return VT_CAT( NAME, _init_clone )(\n    table,\n    source\n    #ifdef CTX_TY\n    , ctx\n    #endif\n  );\n}\n\nstatic inline size_t VT_CAT( vt_size_, VT_TEMPLATE_COUNT )( const NAME *table )\n{\n  return VT_CAT( NAME, _size )( table );\n}\n\nstatic inline size_t VT_CAT( vt_bucket_count_, VT_TEMPLATE_COUNT )( const NAME *table )\n{\n  return VT_CAT( NAME, _bucket_count )( table );\n}\n\nstatic inline bool VT_CAT( vt_is_end_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr )\n{\n  return VT_CAT( NAME, _is_end )( itr );\n}\n\nstatic inline VT_CAT( NAME, _itr ) VT_CAT( vt_insert_, VT_TEMPLATE_COUNT )(\n  NAME *table,\n  KEY_TY key\n  #ifdef VAL_TY\n  , VAL_TY val\n  #endif\n)\n{\n  return VT_CAT( NAME, _insert )(\n    table,\n    key\n    #ifdef VAL_TY\n    , val\n    #endif\n  );\n}\n\nstatic inline VT_CAT( NAME, _itr ) VT_CAT( vt_get_or_insert_, VT_TEMPLATE_COUNT )(\n  NAME *table,\n  KEY_TY key\n  #ifdef VAL_TY\n  , VAL_TY val\n  #endif\n)\n{\n  return VT_CAT( NAME, _get_or_insert )(\n    table,\n    key\n    #ifdef VAL_TY\n    , val\n    #endif\n  );\n}\n\nstatic inline VT_CAT( NAME, _itr ) VT_CAT( vt_get_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key )\n{\n  return VT_CAT( NAME, _get )( table, key );\n}\n\nstatic inline bool VT_CAT( vt_erase_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key )\n{\n  return VT_CAT( NAME, _erase )( table, key );\n}\n\nstatic inline VT_CAT( NAME, _itr ) VT_CAT( vt_next_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr )\n{\n  return VT_CAT( NAME, _next )( itr );\n}\n\nstatic inline VT_CAT( NAME, _itr ) VT_CAT( vt_erase_itr_, VT_TEMPLATE_COUNT )( NAME *table, VT_CAT( NAME, _itr ) itr )\n{\n  return VT_CAT( NAME, _erase_itr )( table, itr );\n}\n\nstatic inline bool VT_CAT( vt_reserve_, VT_TEMPLATE_COUNT )( NAME *table, size_t bucket_count )\n{\n  return VT_CAT( NAME, _reserve )( table, bucket_count );\n}\n\nstatic inline bool VT_CAT( vt_shrink_, VT_TEMPLATE_COUNT )( NAME *table )\n{\n  return VT_CAT( NAME, _shrink )( table );\n}\n\nstatic inline VT_CAT( NAME, _itr ) VT_CAT( vt_first_, VT_TEMPLATE_COUNT )( NAME *table )\n{\n  return VT_CAT( NAME, _first )( table );\n}\n\nstatic inline void VT_CAT( vt_clear_, VT_TEMPLATE_COUNT )( NAME *table )\n{\n  VT_CAT( NAME, _clear )( table );\n}\n\nstatic inline void VT_CAT( vt_cleanup_, VT_TEMPLATE_COUNT )( NAME *table )\n{\n  VT_CAT( NAME, _cleanup )( table );\n}\n\n// Increment the template counter.\n#if     VT_TEMPLATE_COUNT_D1 == 0\n#undef  VT_TEMPLATE_COUNT_D1\n#define VT_TEMPLATE_COUNT_D1 1\n#elif   VT_TEMPLATE_COUNT_D1 == 1\n#undef  VT_TEMPLATE_COUNT_D1\n#define VT_TEMPLATE_COUNT_D1 2\n#elif   VT_TEMPLATE_COUNT_D1 == 2\n#undef  VT_TEMPLATE_COUNT_D1\n#define VT_TEMPLATE_COUNT_D1 3\n#elif   VT_TEMPLATE_COUNT_D1 == 3\n#undef  VT_TEMPLATE_COUNT_D1\n#define VT_TEMPLATE_COUNT_D1 4\n#elif   VT_TEMPLATE_COUNT_D1 == 4\n#undef  VT_TEMPLATE_COUNT_D1\n#define VT_TEMPLATE_COUNT_D1 5\n#elif   VT_TEMPLATE_COUNT_D1 == 5\n#undef  VT_TEMPLATE_COUNT_D1\n#define VT_TEMPLATE_COUNT_D1 6\n#elif   VT_TEMPLATE_COUNT_D1 == 6\n#undef  VT_TEMPLATE_COUNT_D1\n#define VT_TEMPLATE_COUNT_D1 7\n#elif   VT_TEMPLATE_COUNT_D1 == 7\n#undef  VT_TEMPLATE_COUNT_D1\n#define VT_TEMPLATE_COUNT_D1 0\n#if     VT_TEMPLATE_COUNT_D2 == 0\n#undef  VT_TEMPLATE_COUNT_D2\n#define VT_TEMPLATE_COUNT_D2 1\n#elif   VT_TEMPLATE_COUNT_D2 == 1\n#undef  VT_TEMPLATE_COUNT_D2\n#define VT_TEMPLATE_COUNT_D2 2\n#elif   VT_TEMPLATE_COUNT_D2 == 2\n#undef  VT_TEMPLATE_COUNT_D2\n#define VT_TEMPLATE_COUNT_D2 3\n#elif   VT_TEMPLATE_COUNT_D2 == 3\n#undef  VT_TEMPLATE_COUNT_D2\n#define VT_TEMPLATE_COUNT_D2 4\n#elif   VT_TEMPLATE_COUNT_D2 == 4\n#undef  VT_TEMPLATE_COUNT_D2\n#define VT_TEMPLATE_COUNT_D2 5\n#elif   VT_TEMPLATE_COUNT_D2 == 5\n#undef  VT_TEMPLATE_COUNT_D2\n#define VT_TEMPLATE_COUNT_D2 6\n#elif   VT_TEMPLATE_COUNT_D2 == 6\n#undef  VT_TEMPLATE_COUNT_D2\n#define VT_TEMPLATE_COUNT_D2 7\n#elif   VT_TEMPLATE_COUNT_D2 == 7\n#undef  VT_TEMPLATE_COUNT_D2\n#define VT_TEMPLATE_COUNT_D2 0\n#if     VT_TEMPLATE_COUNT_D3 == 0\n#undef  VT_TEMPLATE_COUNT_D3\n#define VT_TEMPLATE_COUNT_D3 1\n#elif   VT_TEMPLATE_COUNT_D3 == 1\n#undef  VT_TEMPLATE_COUNT_D3\n#define VT_TEMPLATE_COUNT_D3 2\n#elif   VT_TEMPLATE_COUNT_D3 == 2\n#undef  VT_TEMPLATE_COUNT_D3\n#define VT_TEMPLATE_COUNT_D3 3\n#elif   VT_TEMPLATE_COUNT_D3 == 3\n#undef  VT_TEMPLATE_COUNT_D3\n#define VT_TEMPLATE_COUNT_D3 4\n#elif   VT_TEMPLATE_COUNT_D3 == 4\n#undef  VT_TEMPLATE_COUNT_D3\n#define VT_TEMPLATE_COUNT_D3 5\n#elif   VT_TEMPLATE_COUNT_D3 == 5\n#undef  VT_TEMPLATE_COUNT_D3\n#define VT_TEMPLATE_COUNT_D3 6\n#elif   VT_TEMPLATE_COUNT_D3 == 6\n#undef  VT_TEMPLATE_COUNT_D3\n#define VT_TEMPLATE_COUNT_D3 7\n#elif   VT_TEMPLATE_COUNT_D3 == 7\n#error  Sorry, the number of template instances is limited to 511. Define VT_NO_C11_GENERIC_API globally and use the \\\nC99 prefixed function API to circumvent this restriction.\n#endif\n#endif\n#endif\n\n#endif\n\n#undef NAME\n#undef KEY_TY\n#undef VAL_TY\n#undef HASH_FN\n#undef CMPR_FN\n#undef MAX_LOAD\n#undef KEY_DTOR_FN\n#undef VAL_DTOR_FN\n#undef CTX_TY\n#undef MALLOC_FN\n#undef FREE_FN\n#undef HEADER_MODE\n#undef IMPLEMENTATION_MODE\n#undef VT_API_FN_QUALIFIERS\n"
  },
  {
    "path": "Brewfile",
    "content": "brew \"zlib\"\nbrew \"xxhash\"\nbrew \"simde\"\nbrew \"python\"\nbrew \"imagemagick\"\nbrew \"harfbuzz\"\nbrew \"sphinx-doc\"\nbrew \"go\"\n"
  },
  {
    "path": "CHANGELOG.rst",
    "content": "See https://sw.kovidgoyal.net/kitty/changelog/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "### Reporting bugs\n\nPlease first search existing bug reports (especially closed ones) for a report\nthat matches your issue.\n\nWhen reporting a bug, provide full details of your environment, that means, at\na minimum, kitty version, OS and OS version, kitty config (ideally a minimal\nconfig to reproduce the issue with).\n\nNote that bugs and feature requests are often closed quickly as they are either\nfixed or deemed wontfix/invalid. In my experience, this is the only scalable way to\nmanage a bug tracker. Feel free to continue to post to a closed bug report\nif you would like to discuss the issue further. Being closed does not mean you\nwill not get any more responses.\n\n### Contributing code\n\nInstall [the dependencies](https://sw.kovidgoyal.net/kitty/build/#dependencies)\nusing your favorite package manager. Build and run kitty [from\nsource](https://sw.kovidgoyal.net/kitty/build/#install-and-run-from-source).\n\nMake a fork, submit your Pull Request. If it's a large/controversial change, open an issue\nbeforehand to discuss it, so that you don't waste your time making a pull\nrequest that gets rejected.\n\nIf the code you are submitting is reasonably easily testable, please contribute\ntests as well (see the `kitty_tests/` sub-directory for existing tests, which\ncan be run with `./test.py`).\n\nThat's it.\n"
  },
  {
    "path": "INSTALL.md",
    "content": "[To build from source](https://sw.kovidgoyal.net/kitty/build/)\n\n[Pre-built binaries](https://sw.kovidgoyal.net/kitty/binary/)\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "Makefile",
    "content": "ifdef V\n\tVVAL=--verbose\nendif\nifdef VERBOSE\n\tVVAL=--verbose\nendif\n\nifdef FAIL_WARN\nexport FAIL_WARN\nendif\n\nall:\n\tpython3 setup.py $(VVAL)\n\ntest:\n\tpython3 setup.py $(VVAL) test\n\nclean:\n\tpython3 setup.py $(VVAL) clean\n\n# A debug build\ndebug:\n\tpython3 setup.py build $(VVAL) --debug\n\ndebug-event-loop:\n\tpython3 setup.py build $(VVAL) --debug --extra-logging=event-loop\n\n# Build with the ASAN and UBSAN sanitizers\nasan:\n\tpython3 setup.py build $(VVAL) --debug --sanitize\n\nprofile:\n\tpython3 setup.py build $(VVAL) --profile\n\napp:\n\tpython3 setup.py kitty.app $(VVAL)\n\nlinux-package: FORCE\n\trm -rf linux-package\n\tpython3 setup.py linux-package\n\nFORCE:\n\nman:\n\t$(MAKE) -C docs man\n\nhtml:\n\t$(MAKE) -C docs html\n\ndirhtml:\n\t$(MAKE) -C docs dirhtml\n\nlinkcheck:\n\t$(MAKE) -C docs linkcheck\n\nwebsite:\n\t./publish.py --only website\n\ndocs: man html\n\n\ndevelop-docs:\n\t$(MAKE) -C docs develop-docs\n\n\nprepare-for-cross-compile: clean all\n\tpython3 setup.py $(VVAL) clean --clean-for-cross-compile\n\ncross-compile:\n\tpython3 setup.py linux-package --skip-code-generation\n\t\n"
  },
  {
    "path": "README.asciidoc",
    "content": "= kitty - the fast, feature-rich, cross-platform, GPU based terminal\n\nIf you live in the terminal, *kitty* is made for **you**!\n\nSee https://sw.kovidgoyal.net/kitty/[the kitty website].\n\nimage:https://github.com/kovidgoyal/kitty/workflows/CI/badge.svg[\"Build status\", link=\"https://github.com/kovidgoyal/kitty/actions?query=workflow%3ACI\"]\n\nhttps://sw.kovidgoyal.net/kitty/faq/[Frequently Asked Questions]\n\nTo ask other questions about kitty usage, use either the https://github.com/kovidgoyal/kitty/discussions/[discussions on GitHub] or the\nhttps://www.reddit.com/r/KittyTerminal[Reddit community]\n\nPackaging status in various repositories:\n\nimage:https://repology.org/badge/vertical-allrepos/kitty-terminal.svg?columns=3&header=kitty[\"Packaging status\", link=\"https://repology.org/project/kitty-terminal/versions\"]\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nThere are no security specific releases of kitty. Security bugs are fixed\nand released just like all other bugs.\n\n## Reporting a vulnerability\n\nPreferably send an email to kovid at kovidgoyal.net or open a private security\nadvisory using the GitHub security advisory facility.\n\nNote that I will generally respond to security communication within 72 hours. Once\nthe bug is confirmed, it will be fixed or at least mitigated within another 72\nhours, at which time the fix will typically be committed to master and hence be\npublic. That timeline might be extended based on the severity of the issue and the\ncurrent state of master in terms of making a new release, if so, it will be\ndone in consultation with the issue reporter.\n"
  },
  {
    "path": "__main__.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nif __name__ == '__main__':\n    from kitty.entry_points import main\n    main()\n"
  },
  {
    "path": "benchmark.py",
    "content": "#!./kitty/launcher/kitty +launch\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport fcntl\nimport io\nimport os\nimport select\nimport signal\nimport struct\nimport sys\nimport termios\nimport time\nfrom pty import CHILD, fork\n\nfrom kitty.constants import kitten_exe\nfrom kitty.fast_data_types import Screen, safe_pipe\nfrom kitty.utils import read_screen_size\n\n\ndef run_parsing_benchmark(cell_width: int = 10, cell_height: int = 20, scrollback: int = 20000) -> None:\n    isatty = sys.stdout.isatty()\n    if isatty:\n        sz = read_screen_size()\n        columns, rows = sz.cols, sz.rows\n    else:\n        columns, rows = 80, 25\n    child_pid, master_fd = fork()\n    is_child = child_pid == CHILD\n    argv = [kitten_exe(), '__benchmark__', '--with-scrollback']\n    if is_child:\n        while read_screen_size().width != columns * cell_width:\n            time.sleep(0.01)\n        signal.pthread_sigmask(signal.SIG_SETMASK, ())\n        os.execvp(argv[0], argv)\n    # os.set_blocking(master_fd, False)\n    x_pixels = columns * cell_width\n    y_pixels = rows * cell_height\n    s = struct.pack('HHHH', rows, columns, x_pixels, y_pixels)\n    fcntl.ioctl(master_fd, termios.TIOCSWINSZ, s)\n\n    write_buf = b''\n    r_pipe, w_pipe = safe_pipe(True)\n    class ToChild:\n        def write(self, x: bytes | str) -> None:\n            nonlocal write_buf\n            if isinstance(x, str):\n                x = x.encode()\n            write_buf += x\n            os.write(w_pipe, b'1')\n\n    screen = Screen(None, rows, columns, scrollback, cell_width, cell_height, 0, ToChild())\n\n    def parse_bytes(data: bytes|memoryview) -> None:\n        data = memoryview(data)\n        while data:\n            dest = screen.test_create_write_buffer()\n            s = screen.test_commit_write_buffer(data, dest)\n            data = data[s:]\n            screen.test_parse_written_data()\n\n\n    while True:\n        rd, wd, _ = select.select([master_fd, r_pipe], [master_fd] if write_buf else [], [])\n        if r_pipe in rd:\n            os.read(r_pipe, 256)\n        if master_fd in rd:\n            try:\n                data = os.read(master_fd, io.DEFAULT_BUFFER_SIZE)\n            except OSError:\n                data = b''\n            if not data:\n                break\n            parse_bytes(data)\n        if master_fd in wd:\n            n = os.write(master_fd, write_buf)\n            write_buf = write_buf[n:]\n    if isatty:\n        lines: list[str] = []\n        screen.linebuf.as_ansi(lines.append)\n        sys.stdout.write(''.join(lines))\n    else:\n        sys.stdout.write(str(screen.linebuf))\n\n\ndef main() -> None:\n    run_parsing_benchmark()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "build-terminfo",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPL v3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport glob\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\n\n\ndef compile_terminfo(base):\n    with tempfile.TemporaryDirectory() as tdir:\n        proc = subprocess.run(['tic', '-x', f'-o{tdir}', 'terminfo/kitty.terminfo'], capture_output=True)\n        if proc.returncode != 0:\n            sys.stderr.buffer.write(proc.stderr)\n            raise SystemExit(proc.returncode)\n        tfiles = glob.glob(os.path.join(tdir, '*', 'xterm-kitty'))\n        if not tfiles:\n            raise SystemExit('tic failed to output the compiled kitty terminfo file')\n\n        tfile = tfiles[0]\n        directory, xterm_kitty = os.path.split(tfile)\n        _, directory = os.path.split(directory)\n        odir = os.path.join(base, directory)\n        os.makedirs(odir, exist_ok=True)\n        ofile = os.path.join(odir, xterm_kitty)\n        shutil.move(tfile, ofile)\n    return ofile\n\n\ndef generate_terminfo():\n    base = os.path.dirname(os.path.abspath(__file__))\n    os.chdir(base)\n    sys.path.insert(0, base)\n\n    from kitty.terminfo import generate_terminfo\n\n    with open('terminfo/kitty.terminfo', 'w') as f:\n        f.write(generate_terminfo())\n    proc = subprocess.run(['tic', '-CrT0', 'terminfo/kitty.terminfo'], capture_output=True)\n    if proc.returncode != 0:\n        sys.stderr.buffer.write(proc.stderr)\n        raise SystemExit(proc.returncode)\n    tcap = proc.stdout.decode('utf-8').splitlines()[-1]\n    with open('terminfo/kitty.termcap', 'w') as f:\n        f.write(tcap)\n\n    dbfile = compile_terminfo(os.path.join(base, 'terminfo'))\n    with open(dbfile, 'rb') as f:\n        data = f.read()\n    with open('kitty/terminfo.h', 'w') as f:\n        print(f'static const uint8_t terminfo_data[{len(data)}] = ''{', file=f)\n        for b in data:\n            print(b, end=', ', file=f)\n        print('};', file=f)\n\n\nif __name__ == '__main__':\n    generate_terminfo()\n"
  },
  {
    "path": "bypy/devenv.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nconst (\n\tfolder                     = \"dependencies\"\n\tfonts_folder               = \"fonts\"\n\tmacos_prefix               = \"/Users/Shared/kitty-build/sw/sw\"\n\tmacos_python               = \"python/Python.framework/Versions/Current/bin/python3\"\n\tmacos_python_framework     = \"python/Python.framework/Versions/Current/Python\"\n\tmacos_python_framework_exe = \"python/Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python\"\n\tNERD_URL                   = \"https://github.com/ryanoasis/nerd-fonts/releases/latest/download/NerdFontsSymbolsOnly.tar.xz\"\n)\n\nfunc root_dir() string {\n\tf, e := filepath.Abs(filepath.Join(folder, runtime.GOOS+\"-\"+runtime.GOARCH))\n\tif e != nil {\n\t\texit(e)\n\t}\n\treturn f\n}\n\nfunc fonts_dir() string {\n\tf, e := filepath.Abs(fonts_folder)\n\tif e != nil {\n\t\texit(e)\n\t}\n\treturn f\n}\n\nvar _ = fmt.Print\n\nfunc exit(x any) {\n\tswitch v := x.(type) {\n\tcase error:\n\t\tif v == nil {\n\t\t\tos.Exit(0)\n\t\t}\n\t\tvar ee *exec.ExitError\n\t\tif errors.As(v, &ee) {\n\t\t\tos.Exit(ee.ExitCode())\n\t\t}\n\tcase string:\n\t\tif v == \"\" {\n\t\t\tos.Exit(0)\n\t\t}\n\tcase int:\n\t\tos.Exit(v)\n\t}\n\tfmt.Fprintf(os.Stderr, \"\\x1b[31mError\\x1b[m: %s\\n\", x)\n\tos.Exit(1)\n}\n\n// download deps {{{\n\ntype dependency struct {\n\tpath     string\n\tbasename string\n\tis_id    bool\n}\n\nfunc lines(exe string, cmd ...string) []string {\n\tc := exec.Command(exe, cmd...)\n\tc.Stderr = os.Stderr\n\tout, err := c.Output()\n\tif err != nil {\n\t\texit(fmt.Errorf(\"Failed to run '%s' with error: %w\", strings.Join(append([]string{exe}, cmd...), \" \"), err))\n\t}\n\tans := []string{}\n\tfor s := bufio.NewScanner(bytes.NewReader(out)); s.Scan(); {\n\t\tans = append(ans, s.Text())\n\t}\n\treturn ans\n}\n\nfunc get_dependencies(path string) (ans []dependency) {\n\ta := lines(\"otool\", \"-D\", path)\n\tinstall_name := strings.TrimSpace(a[len(a)-1])\n\tfor _, line := range lines(\"otool\", \"-L\", path) {\n\t\tline = strings.TrimSpace(line)\n\t\tif strings.Contains(line, \"compatibility\") && !strings.HasSuffix(line, \":\") {\n\t\t\tidx := strings.IndexByte(line, '(')\n\t\t\tdep := strings.TrimSpace(line[:idx])\n\t\t\tans = append(ans, dependency{path: dep, is_id: dep == install_name})\n\t\t}\n\t}\n\treturn\n}\n\nfunc get_local_dependencies(path string) (ans []dependency) {\n\tfor _, dep := range get_dependencies(path) {\n\t\tfor _, y := range []string{filepath.Join(macos_prefix, \"lib\") + \"/\", filepath.Join(macos_prefix, \"python\", \"Python.framework\") + \"/\", \"@rpath/\"} {\n\t\t\tif strings.HasPrefix(dep.path, y) {\n\t\t\t\tif y == \"@rpath/\" {\n\t\t\t\t\tdep.basename = \"lib/\" + dep.path[len(y):]\n\t\t\t\t} else {\n\t\t\t\t\ty = macos_prefix + \"/\"\n\t\t\t\t\tdep.basename = dep.path[len(y):]\n\t\t\t\t}\n\t\t\t\tans = append(ans, dep)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc change_dep(path string, dep dependency) {\n\tcmd := []string{}\n\tfid := filepath.Join(root_dir(), dep.basename)\n\tif dep.is_id {\n\t\tcmd = append(cmd, \"-id\", fid)\n\t} else {\n\t\tcmd = append(cmd, \"-change\", dep.path, fid)\n\t}\n\tcmd = append(cmd, path)\n\tc := exec.Command(\"install_name_tool\", cmd...)\n\tc.Stdout = os.Stdout\n\tc.Stderr = os.Stderr\n\tif err := c.Run(); err != nil {\n\t\texit(fmt.Errorf(\"Failed to run command '%s' with error: %w\", strings.Join(c.Args, \" \"), err))\n\t}\n}\n\nfunc fix_dependencies_in_lib(path string) {\n\tpath, err := filepath.EvalSymlinks(path)\n\tif err != nil {\n\t\texit(err)\n\t}\n\tif s, err := os.Stat(path); err != nil {\n\t\texit(err)\n\t} else if err := os.Chmod(path, s.Mode().Perm()|0o200); err != nil {\n\t\texit(err)\n\t}\n\tfor _, dep := range get_local_dependencies(path) {\n\t\tchange_dep(path, dep)\n\t}\n\tif ldeps := get_local_dependencies(path); len(ldeps) > 0 {\n\t\texit(fmt.Errorf(\"Failed to fix local dependencies in: %s\", path))\n\t}\n}\n\nfunc cached_download(url string) string {\n\tfname := filepath.Base(url)\n\tfmt.Println(\"Downloading\", fname)\n\treq, err := http.NewRequest(\"GET\", url, nil)\n\tif err != nil {\n\t\texit(err)\n\t}\n\tetag_file := filepath.Join(folder, fname+\".etag\")\n\tif etag, err := os.ReadFile(etag_file); err == nil {\n\t\tif _, err := os.Stat(filepath.Join(folder, fname)); err == nil {\n\t\t\treq.Header.Add(\"If-None-Match\", string(etag))\n\t\t}\n\t}\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\texit(err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tif resp.StatusCode == http.StatusNotModified {\n\t\t\treturn filepath.Join(folder, fname)\n\t\t}\n\t\texit(fmt.Errorf(\"The server responded with the HTTP error: %s\", resp.Status))\n\t}\n\tf, err := os.Create(filepath.Join(folder, fname))\n\tif err != nil {\n\t\texit(err)\n\t}\n\tdefer f.Close()\n\tif _, err := io.Copy(f, resp.Body); err != nil {\n\t\texit(fmt.Errorf(\"Failed to download file with error: %w\", err))\n\t}\n\tif etag := resp.Header.Get(\"ETag\"); etag != \"\" {\n\t\tif err := os.WriteFile(etag_file, []byte(etag), 0o644); err != nil {\n\t\t\texit(err)\n\t\t}\n\t}\n\treturn f.Name()\n}\n\nfunc relocate_pkgconfig(path, old_prefix, new_prefix string) error {\n\traw, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnraw := bytes.ReplaceAll(raw, []byte(old_prefix), []byte(new_prefix))\n\treturn os.WriteFile(path, nraw, 0o644)\n}\n\nfunc chdir_to_base() {\n\t_, filename, _, _ := runtime.Caller(0)\n\tbase_dir := filepath.Dir(filepath.Dir(filename))\n\tif err := os.Chdir(base_dir); err != nil {\n\t\texit(err)\n\t}\n}\n\nfunc dependencies_for_docs() {\n\tfmt.Println(\"Downloading get-pip.py\")\n\trq, err := http.Get(\"https://bootstrap.pypa.io/get-pip.py\")\n\tif err != nil {\n\t\texit(err)\n\t}\n\tdefer rq.Body.Close()\n\tif rq.StatusCode != http.StatusOK {\n\t\texit(fmt.Errorf(\"Server responded with HTTP error: %s\", rq.Status))\n\t}\n\tgp, err := os.Create(filepath.Join(folder, \"get-pip.py\"))\n\tif err != nil {\n\t\texit(err)\n\t}\n\tdefer gp.Close()\n\tif _, err = io.Copy(gp, rq.Body); err != nil {\n\t\texit(err)\n\t}\n\tpython := setup_to_run_python()\n\n\trun := func(exe string, args ...string) {\n\t\tc := exec.Command(exe, args...)\n\t\tc.Stdout = os.Stdout\n\t\tc.Stderr = os.Stderr\n\t\tif err := c.Run(); err != nil {\n\t\t\texit(err)\n\t\t}\n\t}\n\trun(python, gp.Name())\n\trun(python, \"-m\", \"pip\", \"install\", \"-r\", \"docs/requirements.txt\")\n}\n\nfunc dependencies(args []string) {\n\tchdir_to_base()\n\tnf := flag.NewFlagSet(\"deps\", flag.ExitOnError)\n\tdocsptr := nf.Bool(\"for-docs\", false, \"download the dependencies needed to build the documentation\")\n\tif err := nf.Parse(args); err != nil {\n\t\texit(err)\n\t}\n\tif *docsptr {\n\t\tdependencies_for_docs()\n\t\tfmt.Println(\"Dependencies needed to generate documentation have been installed. Build docs with ./dev.sh docs\")\n\t\texit(0)\n\t}\n\tdata, err := os.ReadFile(\".github/workflows/ci.py\")\n\tif err != nil {\n\t\texit(err)\n\t}\n\tpat := regexp.MustCompile(\"BUNDLE_URL = '(.+?)'\")\n\tprefix := \"/sw/sw\"\n\tvar url string\n\tif m := pat.FindStringSubmatch(string(data)); len(m) < 2 {\n\t\texit(\"Failed to find BUNDLE_URL in ci.py\")\n\t} else {\n\t\turl = m[1]\n\t}\n\tvar which string\n\tswitch runtime.GOOS {\n\tcase \"darwin\":\n\t\tprefix = macos_prefix\n\t\twhich = \"macos\"\n\tcase \"linux\":\n\t\twhich = \"linux\"\n\t\tif runtime.GOARCH != \"amd64\" {\n\t\t\texit(\"Pre-built dependencies are only available for the amd64 CPU architecture\")\n\t\t}\n\t}\n\tif which == \"\" {\n\t\texit(\"Prebuilt dependencies are only available for Linux and macOS\")\n\t}\n\turl = strings.Replace(url, \"{}\", which, 1)\n\tif err := os.RemoveAll(root_dir()); err != nil {\n\t\texit(err)\n\t}\n\tif err := os.MkdirAll(folder, 0o755); err != nil {\n\t\texit(err)\n\t}\n\ttarfile, _ := filepath.Abs(cached_download(url))\n\troot := root_dir()\n\tif err := os.MkdirAll(root, 0o755); err != nil {\n\t\texit(err)\n\t}\n\tcmd := exec.Command(\"tar\", \"xf\", tarfile)\n\tcmd.Dir = root\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\tif err = cmd.Run(); err != nil {\n\t\texit(err)\n\t}\n\tif runtime.GOOS == \"darwin\" {\n\t\tfix_dependencies_in_lib(filepath.Join(root, macos_python))\n\t\tfix_dependencies_in_lib(filepath.Join(root, macos_python_framework))\n\t\tfix_dependencies_in_lib(filepath.Join(root, macos_python_framework_exe))\n\t}\n\tif err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif d.Type().IsRegular() {\n\t\t\tname := d.Name()\n\t\t\text := filepath.Ext(name)\n\t\t\tif ext == \".pc\" || (ext == \".py\" && strings.HasPrefix(name, \"_sysconfigdata_\")) {\n\t\t\t\terr = relocate_pkgconfig(path, prefix, root)\n\t\t\t}\n\t\t\t// remove libfontconfig so that we use the system one because\n\t\t\t// different distros stupidly use different fontconfig configuration dirs\n\t\t\tif strings.HasPrefix(name, \"libfontconfig.so\") {\n\t\t\t\tos.Remove(path)\n\t\t\t}\n\t\t\tif runtime.GOOS == \"darwin\" {\n\t\t\t\tif ext == \".so\" || ext == \".dylib\" {\n\t\t\t\t\tfix_dependencies_in_lib(path)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}); err != nil {\n\t\texit(err)\n\t}\n\ttarfile, _ = filepath.Abs(cached_download(NERD_URL))\n\troot = fonts_dir()\n\tif err := os.MkdirAll(root, 0o755); err != nil {\n\t\texit(err)\n\t}\n\tcmd = exec.Command(\"tar\", \"xf\", tarfile, \"SymbolsNerdFontMono-Regular.ttf\")\n\tcmd.Dir = root\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\tif err = cmd.Run(); err != nil {\n\t\texit(err)\n\t}\n\n\tfmt.Println(`Dependencies downloaded. Now build kitty with: ./dev.sh build`)\n}\n\n// }}}\n\nfunc prepend(env_var, path string) {\n\tval := os.Getenv(env_var)\n\tif val != \"\" {\n\t\tval = string(filepath.ListSeparator) + val\n\t}\n\tos.Setenv(env_var, path+val)\n}\n\nfunc setup_to_run_python() (python string) {\n\troot := root_dir()\n\tfor _, x := range os.Environ() {\n\t\tif strings.HasPrefix(x, \"PYTHON\") {\n\t\t\ta, _, _ := strings.Cut(x, \"=\")\n\t\t\tos.Unsetenv(a)\n\t\t}\n\t}\n\tswitch runtime.GOOS {\n\tcase \"linux\":\n\t\tprepend(\"LD_LIBRARY_PATH\", filepath.Join(root, \"lib\"))\n\t\tos.Setenv(\"PYTHONHOME\", root)\n\t\tpython = filepath.Join(root, \"bin\", \"python\")\n\tcase `darwin`:\n\t\tpython = filepath.Join(root, macos_python)\n\tdefault:\n\t\texit(\"Building is only supported on Linux and macOS\")\n\t}\n\treturn\n}\n\nfunc build(args []string) {\n\tchdir_to_base()\n\tif _, err := os.Stat(folder); err != nil {\n\t\tdependencies(nil)\n\t}\n\troot := root_dir()\n\tos.Setenv(\"DEVELOP_ROOT\", root)\n\tprepend(\"PKG_CONFIG_PATH\", filepath.Join(root, \"lib\", \"pkgconfig\"))\n\tif runtime.GOOS == \"darwin\" {\n\t\tos.Setenv(\"PKGCONFIG_EXE\", filepath.Join(root, \"bin\", \"pkg-config\"))\n\t}\n\tpython := setup_to_run_python()\n\targs = append([]string{\"setup.py\", \"develop\"}, args...)\n\tcmd := exec.Command(python, args...)\n\tcmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr\n\tif err := cmd.Run(); err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"The following build command failed:\", python, strings.Join(args, \" \"))\n\t\texit(err)\n\t}\n\tfmt.Println(\"Build successful. Run kitty as: kitty/launcher/kitty\")\n}\n\nfunc docs(args []string) {\n\tsetup_to_run_python()\n\tnf := flag.NewFlagSet(\"deps\", flag.ExitOnError)\n\tlivereload := nf.Bool(\"live-reload\", false, \"build the docs and make them available via s local server with live reloading for ease of development\")\n\tfailwarn := nf.Bool(\"fail-warn\", false, \"make warnings fatal when building the docs\")\n\tif err := nf.Parse(args); err != nil {\n\t\texit(err)\n\t}\n\texe := filepath.Join(root_dir(), \"bin\", \"sphinx-build\")\n\taexe := filepath.Join(root_dir(), \"bin\", \"sphinx-autobuild\")\n\ttarget := \"docs\"\n\n\tif *livereload {\n\t\ttarget = \"develop-docs\"\n\t}\n\tcmd := []string{target, \"SPHINXBUILD=\" + exe, \"SPHINXAUTOBUILD=\" + aexe}\n\tif *failwarn {\n\t\tcmd = append(cmd, \"FAILWARN=1\")\n\t}\n\tc := exec.Command(\"make\", cmd...)\n\tc.Stdout = os.Stdout\n\tc.Stderr = os.Stderr\n\terr := c.Run()\n\tif err != nil {\n\t\texit(err)\n\t}\n\tfmt.Println(\"docs successfully built\")\n}\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\texit(`Expected \"deps\" or \"build\" subcommands`)\n\t}\n\tswitch os.Args[1] {\n\tcase \"deps\":\n\t\tdependencies(os.Args[2:])\n\tcase \"build\":\n\t\tbuild(os.Args[2:])\n\tcase \"docs\":\n\t\tdocs(os.Args[2:])\n\tcase \"-h\", \"--help\":\n\t\tfmt.Fprintln(os.Stderr, \"Usage: ./dev.sh [build|deps|docs] [options...]\")\n\tdefault:\n\t\texit(`Expected \"deps\" or \"build\" subcommands`)\n\t}\n}\n"
  },
  {
    "path": "bypy/init_env.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nimport shlex\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nfrom contextlib import suppress\n\nfrom bypy.constants import LIBDIR, PREFIX, PYTHON, ismacos, worker_env\nfrom bypy.constants import SRC as KITTY_DIR\nfrom bypy.utils import run_shell, walk\n\n\ndef read_src_file(name):\n    with open(os.path.join(KITTY_DIR, 'kitty', name), 'rb') as f:\n        return f.read().decode('utf-8')\n\n\ndef initialize_constants():\n    kitty_constants = {}\n    src = read_src_file('constants.py')\n    nv = re.search(r'Version\\((\\d+), (\\d+), (\\d+)\\)', src)\n    kitty_constants['version'] = f'{nv.group(1)}.{nv.group(2)}.{nv.group(3)}'\n    kitty_constants['appname'] = re.search(\n            r'appname: str\\s+=\\s+(u{0,1})[\\'\"]([^\\'\"]+)[\\'\"]', src\n    ).group(2)\n    kitty_constants['cacerts_url'] = 'https://curl.haxx.se/ca/cacert.pem'\n    return kitty_constants\n\n\ndef run(*args, **extra_env):\n    env = os.environ.copy()\n    env.update(worker_env)\n    env.update(extra_env)\n    env['SW'] = PREFIX\n    env['LD_LIBRARY_PATH'] = LIBDIR\n    if ismacos:\n        env['PKGCONFIG_EXE'] = os.path.join(PREFIX, 'bin', 'pkg-config')\n    cwd = env.pop('cwd', KITTY_DIR)\n    print(' '.join(map(shlex.quote, args)), flush=True)\n    return subprocess.call(list(args), env=env, cwd=cwd)\n\n\nSETUP_CMD = [PYTHON, 'setup.py']\n\n\ndef build_frozen_launcher(extra_include_dirs):\n    inc_dirs = [f'--extra-include-dirs={x}' for x in extra_include_dirs]\n    cmd = SETUP_CMD + ['--prefix', build_frozen_launcher.prefix] + inc_dirs + ['build-frozen-launcher']\n    if run(*cmd, cwd=build_frozen_launcher.writeable_src_dir) != 0:\n        print('Building of frozen kitty launcher failed', file=sys.stderr)\n        os.chdir(KITTY_DIR)\n        run_shell()\n        raise SystemExit('Building of kitty launcher failed')\n    return build_frozen_launcher.writeable_src_dir\n\n\ndef run_tests(kitty_exe):\n    with tempfile.TemporaryDirectory() as tdir:\n        uenv = {\n            'KITTY_CONFIG_DIRECTORY': os.path.join(tdir, 'conf'),\n            'KITTY_CACHE_DIRECTORY': os.path.join(tdir, 'cache')\n        }\n        [os.mkdir(x) for x in uenv.values()]\n        env = os.environ.copy()\n        env.update(uenv)\n        cmd = [kitty_exe, '+runpy', 'from kitty_tests.main import run_tests; run_tests(report_env=True)']\n        print(*map(shlex.quote, cmd), flush=True)\n        if subprocess.call(cmd, env=env, cwd=build_frozen_launcher.writeable_src_dir) != 0:\n            print('Checking of kitty build failed, in directory:', build_frozen_launcher.writeable_src_dir, file=sys.stderr)\n            os.chdir(os.path.dirname(kitty_exe))\n            run_shell()\n            raise SystemExit('Checking of kitty build failed')\n\n\ndef build_frozen_tools(kitty_exe):\n    cmd = SETUP_CMD + ['--prefix', os.path.dirname(kitty_exe)] + ['build-frozen-tools']\n    if run(*cmd, cwd=build_frozen_launcher.writeable_src_dir) != 0:\n        print('Building of frozen kitten failed', file=sys.stderr)\n        os.chdir(KITTY_DIR)\n        run_shell()\n        raise SystemExit('Building of kitten launcher failed')\n\n\ndef sanitize_source_folder(path: str) -> None:\n    for q in walk(path):\n        if os.path.splitext(q)[1] not in ('.py', '.glsl', '.ttf', '.otf', '.json'):\n            os.unlink(q)\n\n\ndef build_c_extensions(ext_dir, args):\n    writeable_src_dir = os.path.join(ext_dir, 'src')\n    build_frozen_launcher.writeable_src_dir = writeable_src_dir\n    shutil.copytree(\n        KITTY_DIR, writeable_src_dir, symlinks=True,\n        ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o', '*.so', '*.dylib', '*.pyd'))\n\n    with suppress(FileNotFoundError):\n        os.unlink(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty'))\n\n    cmd = SETUP_CMD + ['macos-freeze' if ismacos else 'linux-freeze']\n    if args.dont_strip:\n        cmd.append('--debug')\n    if args.extra_program_data:\n        cmd.append(f'--vcs-rev={args.extra_program_data}')\n    dest = kitty_constants['appname'] + ('.app' if ismacos else '')\n    dest = build_frozen_launcher.prefix = os.path.join(ext_dir, dest)\n    cmd += ['--prefix', dest, '--full']\n    if run(*cmd, cwd=writeable_src_dir) != 0:\n        print('Building of kitty package failed', file=sys.stderr)\n        os.chdir(writeable_src_dir)\n        run_shell()\n        raise SystemExit('Building of kitty package failed')\n    return ext_dir\n\n\nif __name__ == 'program':\n    kitty_constants = initialize_constants()\n"
  },
  {
    "path": "bypy/linux/__main__.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport errno\nimport os\nimport shutil\nimport stat\nimport subprocess\nimport tarfile\nimport time\n\nfrom bypy.constants import OUTPUT_DIR, PREFIX, python_major_minor_version\nfrom bypy.freeze import extract_extension_modules, freeze_python, path_to_freeze_dir\nfrom bypy.utils import get_dll_path, mkdtemp, py_compile, walk\n\nj = os.path.join\nmachine = (os.uname()[4] or '').lower()\nself_dir = os.path.dirname(os.path.abspath(__file__))\npy_ver = '.'.join(map(str, python_major_minor_version()))\niv = globals()['init_env']\nkitty_constants = iv['kitty_constants']\n\n\ndef binary_includes():\n    return tuple(map(get_dll_path, (\n            'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16', 'lcms2', 'ssl', 'crypto', 'crypt',\n            'iconv', 'pcre2-8', 'graphite2', 'glib-2.0', 'freetype', 'xxhash',\n            'pixman-1', 'cairo', 'harfbuzz', 'xkbcommon', 'xkbcommon-x11',\n            # fontconfig is not bundled because in typical brain dead Linux\n            # distro fashion, different distros use different default config\n            # paths for fontconfig.\n            'ncursesw', 'readline', 'brotlicommon', 'brotlienc', 'brotlidec',\n            'wayland-client', 'wayland-cursor',\n        ))) + (\n                get_dll_path('bz2', 2),\n                get_dll_path(f'python{py_ver}', 2),\n        )\n\n\nclass Env:\n\n    def __init__(self, package_dir):\n        self.base = package_dir\n        self.lib_dir = j(self.base, 'lib')\n        self.py_dir = j(self.lib_dir, f'python{py_ver}')\n        os.makedirs(self.py_dir)\n        self.bin_dir = j(self.base, 'bin')\n        self.obj_dir = mkdtemp('launchers-')\n\n\ndef ignore_in_lib(base, items, ignored_dirs=None):\n    ans = []\n    if ignored_dirs is None:\n        ignored_dirs = {'.svn', '.bzr', '.git', 'test', 'tests', 'testing'}\n    for name in items:\n        path = j(base, name)\n        if os.path.isdir(path):\n            if name in ignored_dirs or not os.path.exists(j(path, '__init__.py')):\n                if name != 'plugins':\n                    ans.append(name)\n        else:\n            if name.rpartition('.')[-1] not in ('so', 'py'):\n                ans.append(name)\n    return ans\n\n\ndef import_site_packages(srcdir, dest):\n    if not os.path.exists(dest):\n        os.mkdir(dest)\n    for x in os.listdir(srcdir):\n        ext = x.rpartition('.')[-1]\n        f = j(srcdir, x)\n        if ext in ('py', 'so'):\n            shutil.copy2(f, dest)\n        elif ext == 'pth' and x != 'setuptools.pth':\n            for line in open(f):\n                src = os.path.abspath(j(srcdir, line))\n                if os.path.exists(src) and os.path.isdir(src):\n                    import_site_packages(src, dest)\n        elif os.path.exists(j(f, '__init__.py')):\n            shutil.copytree(f, j(dest, x), ignore=ignore_in_lib)\n\n\ndef copy_libs(env):\n    print('Copying libs...')\n\n    for x in binary_includes():\n        dest = env.bin_dir if '/bin/' in x else env.lib_dir\n        shutil.copy2(x, dest)\n        dest = os.path.join(dest, os.path.basename(x))\n        subprocess.check_call(['chrpath', '-d', dest])\n\n\ndef add_ca_certs(env):\n    print('Downloading CA certs...')\n    from urllib.request import urlopen\n    cdata = urlopen(kitty_constants['cacerts_url']).read()\n    dest = os.path.join(env.lib_dir, 'cacert.pem')\n    with open(dest, 'wb') as f:\n        f.write(cdata)\n\n\ndef copy_python(env):\n    print('Copying python...')\n    srcdir = j(PREFIX, f'lib/python{py_ver}')\n\n    for x in os.listdir(srcdir):\n        y = j(srcdir, x)\n        ext = os.path.splitext(x)[1]\n        if os.path.isdir(y) and x not in ('test', 'hotshot', 'distutils', 'tkinter', 'turtledemo',\n                                          'site-packages', 'idlelib', 'lib2to3', 'dist-packages'):\n            shutil.copytree(y, j(env.py_dir, x), ignore=ignore_in_lib)\n        if os.path.isfile(y) and ext in ('.py', '.so'):\n            shutil.copy2(y, env.py_dir)\n\n    srcdir = j(srcdir, 'site-packages')\n    import_site_packages(srcdir, env.py_dir)\n\n    pdir = os.path.join(env.lib_dir, 'kitty-extensions')\n    os.makedirs(pdir, exist_ok=True)\n    kitty_dir = os.path.join(env.lib_dir, 'kitty')\n    bases = ('kitty', 'kittens', 'kitty_tests')\n    for x in bases:\n        dest = os.path.join(env.py_dir, x)\n        os.rename(os.path.join(kitty_dir, x), dest)\n        if x == 'kitty':\n            shutil.rmtree(os.path.join(dest, 'launcher'))\n    os.rename(os.path.join(kitty_dir, '__main__.py'), os.path.join(env.py_dir, 'kitty_main.py'))\n    shutil.rmtree(os.path.join(kitty_dir, '__pycache__'))\n    print('Extracting extension modules from', env.py_dir, 'to', pdir)\n    ext_map = extract_extension_modules(env.py_dir, pdir)\n    shutil.copy(os.path.join(os.path.dirname(self_dir), 'site.py'), os.path.join(env.py_dir, 'site.py'))\n    for x in bases:\n        iv['sanitize_source_folder'](os.path.join(env.py_dir, x))\n    py_compile(env.py_dir)\n    freeze_python(env.py_dir, pdir, env.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM', remove_pyc_files=True)\n    shutil.rmtree(env.py_dir)\n\n\ndef build_launcher(env):\n    iv['build_frozen_launcher']([path_to_freeze_dir(), env.obj_dir])\n\n\ndef is_elf(path):\n    with open(path, 'rb') as f:\n        return f.read(4) == b'\\x7fELF'\n\n\ndef fix_permissions(files):\n    for path in files:\n        os.chmod(path, 0o755)\n\n\nSTRIPCMD = ['strip']\n\n\ndef find_binaries(env):\n    files = {j(env.bin_dir, x) for x in os.listdir(env.bin_dir)} | {\n        x for x in {\n            j(os.path.dirname(env.bin_dir), x) for x in os.listdir(env.bin_dir)} if os.path.exists(x)}\n    for x in walk(env.lib_dir):\n        x = os.path.realpath(x)\n        if x not in files and is_elf(x):\n            files.add(x)\n    return files\n\n\ndef strip_files(files, argv_max=(256 * 1024)):\n    \"\"\" Strip a list of files \"\"\"\n    while files:\n        cmd = list(STRIPCMD)\n        pathlen = sum(len(s) + 1 for s in cmd)\n        while pathlen < argv_max and files:\n            f = files.pop()\n            cmd.append(f)\n            pathlen += len(f) + 1\n        if len(cmd) > len(STRIPCMD):\n            all_files = cmd[len(STRIPCMD):]\n            unwritable_files = tuple(filter(None, (None if os.access(x, os.W_OK) else (x, os.stat(x).st_mode) for x in all_files)))\n            [os.chmod(x, stat.S_IWRITE | old_mode) for x, old_mode in unwritable_files]\n            subprocess.check_call(cmd)\n            [os.chmod(x, old_mode) for x, old_mode in unwritable_files]\n\n\ndef strip_binaries(files):\n    print(f'Stripping {len(files)} files...')\n    before = sum(os.path.getsize(x) for x in files)\n    strip_files(files)\n    after = sum(os.path.getsize(x) for x in files)\n    print('Stripped {:.1f} MB'.format((before - after) / (1024 * 1024.)))\n\n\ndef create_tarfile(env, compression_level='9'):\n    print('Creating archive...')\n    base = OUTPUT_DIR\n    arch = 'arm64' if 'arm64' in os.environ['BYPY_ARCH'] else ('i686' if 'i386' in os.environ['BYPY_ARCH'] else 'x86_64')\n    try:\n        shutil.rmtree(base)\n    except OSError as err:\n        if err.errno not in (errno.ENOENT, errno.EBUSY):  # EBUSY when the directory is mountpoint\n            raise\n    os.makedirs(base, exist_ok=True)\n    dist = os.path.join(base, f'{kitty_constants[\"appname\"]}-{kitty_constants[\"version\"]}-{arch}.tar')\n    with tarfile.open(dist, mode='w', format=tarfile.PAX_FORMAT) as tf:\n        cwd = os.getcwd()\n        os.chdir(env.base)\n        try:\n            for x in os.listdir('.'):\n                tf.add(x)\n        finally:\n            os.chdir(cwd)\n    print('Compressing archive...')\n    ans = f'{dist.rpartition(\".\")[0]}.txz'\n    start_time = time.time()\n    threads = 4 if arch == 'i686' else 0\n    subprocess.check_call(['xz', '--verbose', f'--threads={threads}', '-f', f'-{compression_level}', dist])\n    secs = time.time() - start_time\n    print('Compressed in {} minutes {} seconds'.format(secs // 60, secs % 60))\n    os.rename(f'{dist}.xz', ans)\n    print('Archive {} created: {:.2f} MB'.format(\n        os.path.basename(ans), os.stat(ans).st_size / (1024.**2)))\n\n\ndef main():\n    args = globals()['args']\n    ext_dir = globals()['ext_dir']\n    env = Env(os.path.join(ext_dir, kitty_constants['appname']))\n    copy_libs(env)\n    copy_python(env)\n    build_launcher(env)\n    files = find_binaries(env)\n    fix_permissions(files)\n    add_ca_certs(env)\n    kitty_exe = os.path.join(env.base, 'bin', 'kitty')\n    iv['build_frozen_tools'](kitty_exe)\n    if not args.dont_strip:\n        strip_binaries(files)\n    if not args.skip_tests:\n        iv['run_tests'](kitty_exe)\n    create_tarfile(env, args.compression_level)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "bypy/linux.conf",
    "content": "image 'https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-{}.img'\n\ndeps 'bison flex libxcursor-dev libxrandr-dev libxi-dev libxinerama-dev libgl1-mesa-dev libx11-xcb-dev libxcb-xkb-dev libfontconfig1-dev libdbus-1-dev libsystemd-dev'\n"
  },
  {
    "path": "bypy/macos/__main__.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport glob\nimport json\nimport os\nimport shutil\nimport stat\nimport subprocess\nimport sys\nimport tempfile\nimport zipfile\n\nfrom bypy.constants import PREFIX, PYTHON, SW, python_major_minor_version\nfrom bypy.freeze import extract_extension_modules, freeze_python, path_to_freeze_dir\nfrom bypy.macos_sign import codesign, create_entitlements_file, make_certificate_useable, notarize_app, verify_signature\nfrom bypy.utils import current_dir, mkdtemp, py_compile, run_shell, timeit, walk\n\niv = globals()['init_env']\nkitty_constants = iv['kitty_constants']\nself_dir = os.path.dirname(os.path.abspath(__file__))\njoin = os.path.join\nbasename = os.path.basename\ndirname = os.path.dirname\nabspath = os.path.abspath\nAPPNAME = kitty_constants['appname']\nVERSION = kitty_constants['version']\npy_ver = '.'.join(map(str, python_major_minor_version()))\n\n\ndef flush(func):\n    def ff(*args, **kwargs):\n        sys.stdout.flush()\n        sys.stderr.flush()\n        ret = func(*args, **kwargs)\n        sys.stdout.flush()\n        sys.stderr.flush()\n        return ret\n\n    return ff\n\n\ndef flipwritable(fn, mode=None):\n    \"\"\"\n    Flip the writability of a file and return the old mode. Returns None\n    if the file is already writable.\n    \"\"\"\n    if os.access(fn, os.W_OK):\n        return None\n    old_mode = os.stat(fn).st_mode\n    os.chmod(fn, stat.S_IWRITE | old_mode)\n    return old_mode\n\n\nSTRIPCMD = ('/usr/bin/strip', '-x', '-S', '-')\n\n\ndef strip_files(files, argv_max=(256 * 1024)):\n    \"\"\"\n    Strip a list of files\n    \"\"\"\n    tostrip = [(fn, flipwritable(fn)) for fn in files if os.path.exists(fn)]\n    while tostrip:\n        cmd = list(STRIPCMD)\n        flips = []\n        pathlen = sum(len(s) + 1 for s in cmd)\n        while pathlen < argv_max:\n            if not tostrip:\n                break\n            added, flip = tostrip.pop()\n            pathlen += len(added) + 1\n            cmd.append(added)\n            flips.append((added, flip))\n        else:\n            cmd.pop()\n            tostrip.append(flips.pop())\n        os.spawnv(os.P_WAIT, cmd[0], cmd)\n        for args in flips:\n            flipwritable(*args)\n\n\ndef files_in(folder):\n    for record in os.walk(folder):\n        for f in record[-1]:\n            yield join(record[0], f)\n\n\ndef expand_dirs(items, exclude=lambda x: x.endswith('.so')):\n    items = set(items)\n    dirs = set(x for x in items if os.path.isdir(x))\n    items.difference_update(dirs)\n    for x in dirs:\n        items.update({y for y in files_in(x) if not exclude(y)})\n    return items\n\n\n\ndef do_sign(app_dir: str) -> None:\n    with current_dir(join(app_dir, 'Contents')):\n        # Sign all .so files\n        so_files = {x for x in files_in('.') if x.endswith('.so')}\n        codesign(so_files)\n        # Sign everything else in Frameworks\n        with current_dir('Frameworks'):\n            fw = set(glob.glob('*.framework'))\n            codesign(fw)\n            items = set(os.listdir('.')) - fw\n            codesign(expand_dirs(items))\n        # Sign kitten\n        with current_dir('MacOS'):\n            codesign('kitten')\n        # Sign sub-apps\n        for x in os.listdir('.'):\n            if x.endswith('.app'):\n                codesign(x)\n\n    # Now sign the main app\n    codesign(app_dir)\n    verify_signature(app_dir)\n\n\ndef sign_app(app_dir, notarize):\n    # Copied from iTerm2: https://github.com/gnachman/iTerm2/blob/master/iTerm2.entitlements\n    create_entitlements_file({\n        'com.apple.security.automation.apple-events': True,\n        'com.apple.security.cs.allow-jit': True,\n        'com.apple.security.device.audio-input': True,\n        'com.apple.security.device.camera': True,\n        'com.apple.security.personal-information.addressbook': True,\n        'com.apple.security.personal-information.calendars': True,\n        'com.apple.security.personal-information.location': True,\n        'com.apple.security.personal-information.photos-library': True,\n    })\n    with make_certificate_useable():\n        do_sign(app_dir)\n        if notarize:\n            notarize_app(app_dir, 'kitty')\n\n\nclass Freeze(object):\n\n    FID = '@executable_path/../Frameworks'\n\n    def __init__(self, build_dir, dont_strip=False, sign_installers=False, notarize=False, skip_tests=False):\n        self.build_dir = build_dir\n        self.skip_tests = skip_tests\n        self.sign_installers = sign_installers\n        self.notarize = notarize\n        self.dont_strip = dont_strip\n        self.contents_dir = join(self.build_dir, 'Contents')\n        self.resources_dir = join(self.contents_dir, 'Resources')\n        self.frameworks_dir = join(self.contents_dir, 'Frameworks')\n        self.to_strip = []\n        self.warnings = []\n        self.py_ver = py_ver\n        self.python_stdlib = join(self.resources_dir, 'Python', 'lib', f'python{self.py_ver}')\n        self.site_packages = self.python_stdlib  # hack to avoid needing to add site-packages to path\n        self.obj_dir = mkdtemp('launchers-')\n\n        self.run()\n\n    def run_shell(self):\n        with current_dir(self.contents_dir):\n            run_shell()\n\n    def run(self):\n        ret = 0\n        self.add_python_framework()\n        self.add_site_packages()\n        self.add_stdlib()\n        self.add_misc_libraries()\n        self.freeze_python()\n        self.add_ca_certs()\n        self.build_frozen_tools()\n        self.complete_sub_bundles()\n        if not self.dont_strip:\n            self.strip_files()\n        if not self.skip_tests:\n            self.run_tests()\n        # self.run_shell()\n\n        ret = self.makedmg(self.build_dir, f'{APPNAME}-{VERSION}')\n\n        return ret\n\n    @flush\n    def complete_sub_bundles(self):\n        count = 0\n        for qapp in glob.glob(join(self.contents_dir, '*.app')):\n            for exe in glob.glob(join(self.contents_dir, 'MacOS', '*')):\n                os.symlink(f'../../../MacOS/{os.path.basename(exe)}', os.path.join(qapp, 'Contents', 'MacOS', os.path.basename(exe)))\n                count += 1\n        if count < 2:\n            raise SystemExit(f'Could not complete sub-bundles in {self.contents_dir}')\n\n    @flush\n    def add_ca_certs(self):\n        print('\\nDownloading CA certs...')\n        from urllib.request import urlopen\n        cdata = None\n        for i in range(5):\n            try:\n                cdata = urlopen(kitty_constants['cacerts_url']).read()\n                break\n            except Exception as e:\n                print(f'Downloading CA certs failed with error: {e}, retrying...')\n\n        if cdata is None:\n            raise SystemExit('Downloading C certs failed, giving up')\n        dest = join(self.contents_dir, 'Resources', 'cacert.pem')\n        with open(dest, 'wb') as f:\n            f.write(cdata)\n\n    @flush\n    def strip_files(self):\n        print('\\nStripping files...')\n        strip_files(self.to_strip)\n\n    @flush\n    def run_tests(self):\n        iv['run_tests'](join(self.contents_dir, 'MacOS', 'kitty'))\n\n    @flush\n    def set_id(self, path_to_lib, new_id):\n        old_mode = flipwritable(path_to_lib)\n        subprocess.check_call(\n            ['install_name_tool', '-id', new_id, path_to_lib])\n        if old_mode is not None:\n            flipwritable(path_to_lib, old_mode)\n\n    @flush\n    def get_dependencies(self, path_to_lib):\n        install_name = subprocess.check_output(\n            ['otool', '-D', path_to_lib]).decode('utf-8').splitlines()[-1].strip()\n        raw = subprocess.check_output(['otool', '-L', path_to_lib]).decode('utf-8')\n        for line in raw.splitlines():\n            if 'compatibility' not in line or line.strip().endswith(':'):\n                continue\n            idx = line.find('(')\n            path = line[:idx].strip()\n            yield path, path == install_name\n\n    @flush\n    def get_local_dependencies(self, path_to_lib):\n        for x, is_id in self.get_dependencies(path_to_lib):\n            for y in (f'{PREFIX}/lib/', f'{PREFIX}/python/Python.framework/', '@rpath/'):\n                if x.startswith(y):\n                    if y == f'{PREFIX}/python/Python.framework/':\n                        y = f'{PREFIX}/python/'\n                    yield x, x[len(y):], is_id\n                    break\n\n    @flush\n    def change_dep(self, old_dep, new_dep, is_id, path_to_lib):\n        cmd = ['-id', new_dep] if is_id else ['-change', old_dep, new_dep]\n        subprocess.check_call(['install_name_tool'] + cmd + [path_to_lib])\n\n    @flush\n    def fix_dependencies_in_lib(self, path_to_lib):\n        self.to_strip.append(path_to_lib)\n        old_mode = flipwritable(path_to_lib)\n        for dep, bname, is_id in self.get_local_dependencies(path_to_lib):\n            ndep = f'{self.FID}/{bname}'\n            self.change_dep(dep, ndep, is_id, path_to_lib)\n        ldeps = list(self.get_local_dependencies(path_to_lib))\n        if ldeps:\n            print('\\nFailed to fix dependencies in', path_to_lib)\n            print('Remaining local dependencies:', ldeps)\n            raise SystemExit(1)\n        if old_mode is not None:\n            flipwritable(path_to_lib, old_mode)\n\n    @flush\n    def add_python_framework(self):\n        print('\\nAdding Python framework')\n        src = join(f'{PREFIX}/python', 'Python.framework')\n        x = join(self.frameworks_dir, 'Python.framework')\n        curr = os.path.realpath(join(src, 'Versions', 'Current'))\n        currd = join(x, 'Versions', basename(curr))\n        rd = join(currd, 'Resources')\n        os.makedirs(rd)\n        shutil.copy2(join(curr, 'Resources', 'Info.plist'), rd)\n        shutil.copy2(join(curr, 'Python'), currd)\n        self.set_id(\n            join(currd, 'Python'),\n            f'{self.FID}/Python.framework/Versions/{basename(curr)}/Python')\n        # The following is needed for codesign\n        with current_dir(x):\n            os.symlink(basename(curr), 'Versions/Current')\n            for y in ('Python', 'Resources'):\n                os.symlink(f'Versions/Current/{y}', y)\n\n    @flush\n    def install_dylib(self, path, set_id=True):\n        shutil.copy2(path, self.frameworks_dir)\n        if set_id:\n            self.set_id(\n                join(self.frameworks_dir, basename(path)),\n                f'{self.FID}/{basename(path)}')\n        self.fix_dependencies_in_lib(join(self.frameworks_dir, basename(path)))\n\n    @flush\n    def add_misc_libraries(self):\n        for x in (\n                'sqlite3.0',\n                'z.1',\n                'harfbuzz.0',\n                'png16.16',\n                'lcms2.2',\n                'crypto.3',\n                'ssl.3',\n                'xxhash.0',\n        ):\n            print('\\nAdding', x)\n            x = f'lib{x}.dylib'\n            src = join(PREFIX, 'lib', x)\n            shutil.copy2(src, self.frameworks_dir)\n            dest = join(self.frameworks_dir, x)\n            self.set_id(dest, f'{self.FID}/{x}')\n            self.fix_dependencies_in_lib(dest)\n\n    @flush\n    def add_package_dir(self, x, dest=None):\n        def ignore(root, files):\n            ans = []\n            for y in files:\n                ext = os.path.splitext(y)[1]\n                if ext not in ('', '.py', '.so') or \\\n                        (not ext and not os.path.isdir(join(root, y))):\n                    ans.append(y)\n\n            return ans\n\n        if dest is None:\n            dest = self.site_packages\n        dest = join(dest, basename(x))\n        shutil.copytree(x, dest, symlinks=True, ignore=ignore)\n        for f in walk(dest):\n            if f.endswith('.so'):\n                self.fix_dependencies_in_lib(f)\n\n    @flush\n    def add_stdlib(self):\n        print('\\nAdding python stdlib')\n        src = f'{PREFIX}/python/Python.framework/Versions/Current/lib/python{self.py_ver}'\n        dest = self.python_stdlib\n        if not os.path.exists(dest):\n            os.makedirs(dest)\n        for x in os.listdir(src):\n            if x in ('site-packages', 'config', 'test', 'lib2to3', 'lib-tk',\n                     'lib-old', 'idlelib', 'plat-mac', 'plat-darwin',\n                     'site.py', 'distutils', 'turtledemo', 'tkinter'):\n                continue\n            x = join(src, x)\n            if os.path.isdir(x):\n                self.add_package_dir(x, dest)\n            elif os.path.splitext(x)[1] in ('.so', '.py'):\n                shutil.copy2(x, dest)\n                dest2 = join(dest, basename(x))\n                if dest2.endswith('.so'):\n                    self.fix_dependencies_in_lib(dest2)\n\n    @flush\n    def freeze_python(self):\n        print('\\nFreezing python')\n        kitty_dir = join(self.resources_dir, 'kitty')\n        bases = ('kitty', 'kittens', 'kitty_tests')\n        for x in bases:\n            dest = join(self.python_stdlib, x)\n            os.rename(join(kitty_dir, x), dest)\n            if x == 'kitty':\n                shutil.rmtree(join(dest, 'launcher'))\n        os.rename(join(kitty_dir, '__main__.py'), join(self.python_stdlib, 'kitty_main.py'))\n        shutil.rmtree(join(kitty_dir, '__pycache__'))\n        pdir = join(dirname(self.python_stdlib), 'kitty-extensions')\n        os.mkdir(pdir)\n        print('Extracting extension modules from', self.python_stdlib, 'to', pdir)\n        ext_map = extract_extension_modules(self.python_stdlib, pdir)\n        shutil.copy(join(os.path.dirname(self_dir), 'site.py'), join(self.python_stdlib, 'site.py'))\n        for x in bases:\n            iv['sanitize_source_folder'](join(self.python_stdlib, x))\n        self.compile_py_modules()\n        freeze_python(self.python_stdlib, pdir, self.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM', remove_pyc_files=True)\n        shutil.rmtree(self.python_stdlib)\n        iv['build_frozen_launcher']([path_to_freeze_dir(), self.obj_dir])\n        os.rename(join(dirname(self.contents_dir), 'bin', 'kitty'), join(self.contents_dir, 'MacOS', 'kitty'))\n        shutil.rmtree(join(dirname(self.contents_dir), 'bin'))\n        self.fix_dependencies_in_lib(join(self.contents_dir, 'MacOS', 'kitty'))\n        for f in glob.glob(join(self.contents_dir, '*.app', 'Contents', 'MacOS', '*')):\n            if not os.path.islink(f):\n                self.fix_dependencies_in_lib(f)\n        for f in walk(pdir):\n            if f.endswith('.so') or f.endswith('.dylib'):\n                self.fix_dependencies_in_lib(f)\n\n    @flush\n    def build_frozen_tools(self):\n        iv['build_frozen_tools'](join(self.contents_dir, 'MacOS', 'kitty'))\n\n    @flush\n    def add_site_packages(self):\n        print('\\nAdding site-packages')\n        os.makedirs(self.site_packages)\n        sys_path = json.loads(subprocess.check_output([\n            PYTHON, '-c', 'import sys, json; json.dump(sys.path, sys.stdout)']))\n        paths = reversed(tuple(map(abspath, [x for x in sys_path if x.startswith('/') and not x.startswith('/Library/')])))\n        upaths = []\n        for x in paths:\n            if x not in upaths and (x.endswith('.egg') or x.endswith('/site-packages')):\n                upaths.append(x)\n        for x in upaths:\n            print('\\t', x)\n            tdir = None\n            try:\n                if not os.path.isdir(x):\n                    zf = zipfile.ZipFile(x)\n                    tdir = tempfile.mkdtemp()\n                    zf.extractall(tdir)\n                    x = tdir\n                self.add_modules_from_dir(x)\n                self.add_packages_from_dir(x)\n            finally:\n                if tdir is not None:\n                    shutil.rmtree(tdir)\n        self.remove_bytecode(self.site_packages)\n\n    @flush\n    def add_modules_from_dir(self, src):\n        for x in glob.glob(join(src, '*.py')) + glob.glob(join(src, '*.so')):\n            shutil.copy2(x, self.site_packages)\n            if x.endswith('.so'):\n                self.fix_dependencies_in_lib(x)\n\n    @flush\n    def add_packages_from_dir(self, src):\n        for x in os.listdir(src):\n            x = join(src, x)\n            if os.path.isdir(x) and os.path.exists(join(x, '__init__.py')):\n                if self.filter_package(basename(x)):\n                    continue\n                self.add_package_dir(x)\n\n    @flush\n    def filter_package(self, name):\n        return name in ('Cython', 'modulegraph', 'macholib', 'py2app',\n                        'bdist_mpkg', 'altgraph')\n\n    @flush\n    def remove_bytecode(self, dest):\n        for x in os.walk(dest):\n            root = x[0]\n            for f in x[-1]:\n                if os.path.splitext(f) == '.pyc':\n                    os.remove(join(root, f))\n\n    @flush\n    def compile_py_modules(self):\n        self.remove_bytecode(join(self.resources_dir, 'Python'))\n        py_compile(join(self.resources_dir, 'Python'))\n\n    @flush\n    def makedmg(self, d, volname, format='ULMO'):\n        ''' Copy a directory d into a dmg named volname '''\n        print('\\nMaking dmg...')\n        sys.stdout.flush()\n        destdir = join(SW, 'dist')\n        try:\n            shutil.rmtree(destdir)\n        except FileNotFoundError:\n            pass\n        os.mkdir(destdir)\n        dmg = join(destdir, f'{volname}.dmg')\n        if os.path.exists(dmg):\n            os.unlink(dmg)\n        tdir = tempfile.mkdtemp()\n        appdir = join(tdir, os.path.basename(d))\n        shutil.copytree(d, appdir, symlinks=True)\n        if self.sign_installers:\n            with timeit() as times:\n                sign_app(appdir, self.notarize)\n            print('Signing completed in {} minutes {} seconds'.format(*times))\n        os.symlink('/Applications', join(tdir, 'Applications'))\n        size_in_mb = int(\n            subprocess.check_output(['du', '-s', '-k', tdir]).decode('utf-8')\n            .split()[0]) / 1024.\n        cmd = [\n            '/usr/bin/hdiutil', 'create', '-srcfolder', tdir, '-volname',\n            volname, '-format', format\n        ]\n        if 190 < size_in_mb < 250:\n            # We need -size 255m because of a bug in hdiutil. When the size of\n            # srcfolder is close to 200MB hdiutil fails with\n            # diskimages-helper: resize request is above maximum size allowed.\n            cmd += ['-size', '255m']\n        print('\\nCreating dmg...')\n        with timeit() as times:\n            subprocess.check_call(cmd + [dmg])\n        print('dmg created in {} minutes and {} seconds'.format(*times))\n        shutil.rmtree(tdir)\n        size = os.stat(dmg).st_size / (1024 * 1024.)\n        print(f'\\nInstaller size: {size:.2f}MB\\n')\n        return dmg\n\n\ndef main():\n    args = globals()['args']\n    ext_dir = globals()['ext_dir']\n    Freeze(\n        join(ext_dir, f'{kitty_constants[\"appname\"]}.app'),\n        dont_strip=args.dont_strip,\n        sign_installers=args.sign_installers,\n        notarize=args.notarize,\n        skip_tests=args.skip_tests\n    )\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "bypy/macos.conf",
    "content": "# Requires installation of XCode >= 10.3 and go 1.26 and Python 3 and\n# python3 -m pip install certifi meson ninja\n\nvm_name 'macos-kitty'\nroot '/Users/Shared/kitty-build'\npython '/usr/local/bin/python3'\nuniversal 'true'\ndeploy_target '12.0'\n"
  },
  {
    "path": "bypy/rsync.conf",
    "content": "to_vm_excludes '/dependencies /build /dist /kitty/launcher/kitty* /.build-cache /.cache /.mypy_cache /.ruff_cache /tags __pycache__ /*_commands.json *.so *.pyd *.pyc *_generated.go'\n"
  },
  {
    "path": "bypy/site.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport _sitebuiltins\nimport builtins\nimport sys\n\n\ndef set_quit() -> None:\n    eof = 'Ctrl-D (i.e. EOF)'\n    builtins.quit = _sitebuiltins.Quitter('quit', eof)\n    builtins.exit = _sitebuiltins.Quitter('exit', eof)\n\n\ndef set_helper() -> None:\n    builtins.help = _sitebuiltins._Helper()\n\n\ndef main() -> None:\n    sys.argv[0] = sys.calibre_basename\n    set_helper()\n    set_quit()\n    mod = __import__(sys.calibre_module, fromlist=[1])\n    func = getattr(mod, sys.calibre_function)\n    return func()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "bypy/sources.json",
    "content": "[\n    {\n        \"name\": \"zlib 1.3.2\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:d7a0654783a4da529d1bb793b7ad9c3318020af77667bcae35f95d0e42a792f3\",\n            \"urls\": [\"https://zlib.net/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"bzip2 1.0.8\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269\",\n            \"urls\": [\"https://www.sourceware.org/pub/{name}/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"pkg-config 0.29.2\",\n        \"type\": \"build\",\n        \"os\": \"macos\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591\",\n            \"urls\": [\"https://pkg-config.freedesktop.org/releases/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"openssl 3.5.5\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:b28c91532a8b65a1f983b4c28b7488174e4a01008e29ce8e69bd789f28bc2a89\",\n            \"urls\": [\"https://www.openssl.org/source/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"cmake 3.29.3\",\n        \"type\": \"build\",\n        \"os\": \"macos\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:252aee1448d49caa04954fd5e27d189dd51570557313e7b281636716a238bccb\",\n            \"urls\": [\"https://cmake.org/files/v{version_except_last}/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"expat 2.6.2\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:ee14b4c5d8908b1bec37ad937607eab183d4d9806a08adee472c3c3121d27364\",\n            \"urls\": [\"https://github.com/libexpat/libexpat/releases/download/R_{version_with_underscores}/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"libxml2 2.14.6\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:7ce458a0affeb83f0b55f1f4f9e0e55735dbfc1a9de124ee86fb4a66b597203a\",\n            \"urls\": [\"https://download.gnome.org/sources/libxml2/{version_except_last}/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"xkbcommon 1.7.0\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:65782f0a10a4b455af9c6baab7040e2f537520caa2ec2092805cdfd36863b247\",\n            \"urls\": [\"https://xkbcommon.org/download/lib{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"sqlite 3.50.4\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:a3db587a1b92ee5ddac2f66b3edb41b26f9c867275782d46c3a088977d6a5b18\",\n            \"urls\": [\"https://www.sqlite.org/2025/{name}-autoconf-3500400.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"libffi 3.4.6\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e\",\n            \"urls\": [\"https://github.com/libffi/libffi/releases/download/v{version}/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"ncurses 6.5\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:136d91bc269a9a5785e5f9e980bc76ab57428f604ce3e5a5a90cebc767971cc6\",\n            \"urls\": [\"https://ftp.gnu.org/gnu/ncurses/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"readline 8.2\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:3feb7171f16a84ee82ca18a36d7b9be109a52c04f492a053331d7d1095007c35\",\n            \"urls\": [\"https://ftp.gnu.org/gnu/readline/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"xz 5.8.1\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:507825b599356c10dca1cd720c9d0d0c9d5400b9de300af00e4d1ea150795543\",\n            \"urls\": [\"https://tukaani.org/xz/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"libxxhash 0.8.2\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:baee0c6afd4f03165de7a4e67988d16f0f2b257b51d0e3cb91909302a26a79c4\",\n            \"urls\": [\"https://github.com/Cyan4973/xxHash/archive/refs/tags/v{version}.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"xcrypt 4.4.36\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:b979838d5f1f238869d467484793b72b8bca64c4eae696fdbba0a9e0b6c28453\",\n            \"urls\": [\"https://github.com/besser82/libxcrypt/archive/v{version}.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"python 3.14.3\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:a97d5549e9ad81fe17159ed02c68774ad5d266c72f8d9a0b5a9c371fe85d902b\",\n            \"urls\": [\"https://www.python.org/ftp/python/{version}/Python-{version}.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"libpng 1.6.55\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:d925722864837ad5ae2a82070d4b2e0603dc72af44bd457c3962298258b8e82d\",\n            \"urls\": [\"https://downloads.sourceforge.net/sourceforge/libpng/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"lcms2 2.17\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:d11af569e42a1baa1650d20ad61d12e41af4fead4aa7964a01f93b08b53ab074\",\n            \"urls\": [\"https://github.com/mm2/Little-CMS/releases/download/lcms{version}/lcms2-{version}.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"graphite 1.3.14\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tgz\",\n            \"hash\": \"sha256:f99d1c13aa5fa296898a181dff9b82fb25f6cc0933dbaa7a475d8109bd54209d\",\n            \"urls\": [\"https://downloads.sourceforge.net/silgraphite/graphite2-{version}.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"pcre 10.43\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.bz2\",\n            \"hash\": \"sha256:e2a53984ff0b07dfdb5ae4486bbb9b21cca8e7df2434096cc9bf1b728c350bcb\",\n            \"urls\": [\"https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.43/pcre2-{version}.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"iconv 1.17\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:8f74213b56238c85a50a5329f77e06198771e70dd9a739779f4c02f65d971313\",\n            \"urls\": [\"https://ftp.gnu.org/pub/gnu/libiconv/libiconv-{version}.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"glib 2.86.3\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:b3211d8d34b9df5dca05787ef0ad5d7ca75dec998b970e1aab0001d229977c65\",\n            \"urls\": [\"https://download.gnome.org/sources/glib/{version_except_last}/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"libbrotli 1.1.0\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.gz\",\n            \"hash\": \"sha256:e720a6ca29428b803f4ad165371771f5398faba397edf6778837a18599ea13ff\",\n            \"urls\": [\"https://github.com/google/brotli/archive/v{version}/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"pixman 0.44.2\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:50baf820dde0c5ff9714d03d2df4970f606a3d3b1024f5404c0398a9821cc4b0\",\n            \"urls\": [\"https://www.cairographics.org/releases/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"freetype 2.13.2\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:12991c4e55c506dd7f9b765933e62fd2be2e06d421505d7950a132e4f1bb484d\",\n            \"urls\": [\"https://download.savannah.gnu.org/releases/freetype/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"fontconfig 2.13.1\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.bz2\",\n            \"hash\": \"sha256:f655dd2a986d7aa97e052261b36aa67b0a64989496361eca8d604e6414006741\",\n            \"urls\": [\"https://www.fontconfig.org/release/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"cairo 1.18.2\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:a62b9bb42425e844cc3d6ddde043ff39dbabedd1542eba57a2eb79f85889d45a\",\n            \"urls\": [\"https://www.cairographics.org/releases/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"harfbuzz 12.3.0\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:8660ebd3c27d9407fc8433b5d172bafba5f0317cb0bb4339f28e5370c93d42b7\",\n            \"urls\": [\"https://github.com/harfbuzz/harfbuzz/releases/download/{version}/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"simde 0.7.6\",\n        \"comment\": \"Cannot update till gcc in the build VM is updated as simde 0.8 requires newer gcc\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:703eac1f2af7de1f7e4aea2286130b98e1addcc0559426e78304c92e2b4eb5e1\",\n            \"urls\": [\"https://github.com/simd-everywhere/simde/releases/download/v{version}/simde-amalgamated-{version}.{file_extension}\"]\n        }\n    },\n\n    {\n        \"name\": \"wayland 1.24.0\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:82892487a01ad67b334eca83b54317a7c86a03a89cfadacfef5211f11a5d0536\",\n            \"urls\": [\"https://gitlab.freedesktop.org/wayland/wayland/-/releases/{version}/downloads/{filename}\"]\n        }\n    },\n\n    {\n        \"name\": \"wayland-protocols 1.45\",\n        \"os\": \"linux\",\n        \"unix\": {\n            \"file_extension\": \"tar.xz\",\n            \"hash\": \"sha256:4d2b2a9e3e099d017dc8107bf1c334d27bb87d9e4aff19a0c8d856d17cd41ef0\",\n            \"urls\": [\"https://gitlab.freedesktop.org/wayland/wayland-protocols/-/releases/{version}/downloads/{filename}\"]\n        }\n    }\n]\n"
  },
  {
    "path": "count-lines-of-code",
    "content": "#!/usr/bin/env python\n\nimport subprocess\n\nls_files = subprocess.check_output([ 'git', 'ls-files']).decode('utf-8')\nall_files = set(ls_files.splitlines())\nall_files.discard('')\nfor attr in ('linguist-generated', 'linguist-vendored'):\n    cp = subprocess.run(['git', 'check-attr', attr, '--stdin'],\n                        check=True, stdout=subprocess.PIPE, input='\\n'.join(all_files).encode('utf-8'))\n    for line in cp.stdout.decode().splitlines():\n        if line.endswith(' true'):\n            fname = line.split(':', 1)[0]\n            all_files.discard(fname)\n\nall_files -= {'gen/rowcolumn-diacritics.txt'}\ncp = subprocess.run(['cloc', '--list-file', '-'], input='\\n'.join(all_files).encode())\nraise SystemExit(cp.returncode)\n"
  },
  {
    "path": "dev.sh",
    "content": "#!/bin/sh\n#\n# dev.sh\n# Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n#\n# Distributed under terms of the GPLv3 license.\n#\n\nexec go run bypy/devenv.go \"$@\"\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\nifdef FAIL_WARN\noverride FAIL_WARN=-W\nendif\n\n# You can set these variables from the command line.\nSPHINXOPTS    = -n -q -j auto -T $(FAIL_WARN) $(OPTS)\nSPHINXBUILD   = sphinx-build\nSPHINXAUTOBUILD = sphinx-autobuild\nSPHINXPROJ    = kitty\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n\ndevelop-docs:\n\t$(SPHINXAUTOBUILD) --ignore \"$(abspath $(SOURCEDIR))/generated/*\" --watch ../kitty --watch ../kittens -b dirhtml \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS)\n"
  },
  {
    "path": "docs/_static/custom.css",
    "content": "/*\n * custom.css\n * Copyright (C) 2018 Kovid Goyal\n *\n * Distributed under terms of the MIT license.\n */\n\n.italic {\n    font-style: italic;\n}\n\n.sidebar-logo {\n    height: 128px;\n}\n\n.major-features li {\n    margin-bottom: 0.75ex;\n    margin-top: 0.75ex;\n}\n\n.sidebar-tree a.current {\n    font-style: italic;\n}\n\ndetails > summary {\n    color: var(--color-link);\n    cursor: pointer;\n    text-decoration-color: var(--color-link-underline);\n    text-decoration-line: underline;\n}\n\n/* pygments adds an underline to some white-space, this is particularly visible in\n * dark mode. Remove it. */\n.highlight .w {\n    text-decoration: none\n}\n"
  },
  {
    "path": "docs/_static/custom.js",
    "content": "/* vim:fileencoding=utf-8\n *\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPLv3 license\n */\n/*jshint esversion: 6 */\n\n(function() {\n\"use strict\";\n\nfunction get_sidebar_tree() {\n    return document.querySelector('.sidebar-tree');\n}\n\nfunction scroll_sidebar_node_into_view(a) {\n    var ss = get_sidebar_tree().closest('.sidebar-scroll');\n    if (!ss || !a) return;\n    ss.style.position = 'relative';\n    var pos = 0;\n    while (true) {\n        pos += a.offsetTop;\n        a = a.offsetParent;\n        if (!a || a == ss) break;\n    }\n    ss.scrollTo({top: pos, behavior: 'instant'});\n}\n\nfunction mark_current_link(sidebar_tree, a, onload) {\n    var li = a.closest('li.has-children');\n    while (li) {\n        li.querySelector('input[type=checkbox]').setAttribute('checked', 'checked');\n        li = li.parentNode.closest('li.has-children');\n    }\n    sidebar_tree.querySelectorAll('.current').forEach(function (elem) {\n        elem.classList.remove('current');\n    });\n    if (onload) scroll_sidebar_node_into_view(a);\n    a.classList.add('current');\n}\n\nfunction show_hash_in_sidebar(onload) {\n    const sidebar_tree = get_sidebar_tree();\n    if (document.location.hash.length > 1) {\n        var a = sidebar_tree.querySelector('a[href=\"' + document.location.hash + '\"]');\n        if (a) mark_current_link(sidebar_tree, a, onload);\n    } else {\n        if (onload) scroll_sidebar_node_into_view(sidebar_tree.querySelector('.current-page a'));\n    }\n}\n\nfunction init_sidebar() {\n    const sidebar_tree = document.querySelector('.sidebar-tree');\n    if (!sidebar_tree || sidebar_tree.dataset.inited === 'true') return;\n    sidebar_tree.dataset.inited = 'true';\n    show_hash_in_sidebar(true);\n    window.addEventListener('hashchange', show_hash_in_sidebar.bind(null, false));\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", init_sidebar);\ninit_sidebar();\n\n}());\n\n"
  },
  {
    "path": "docs/_static/timestamps.css",
    "content": ".video-with-timestamps dl {\n    display: grid;\n    grid-template-columns: max-content auto;\n}\n\n.video-with-timestamps dt {\n    grid-column-start: 1;\n}\n\n.video-with-timestamps dd {\n    grid-column-start: 2;\n    margin-left: 1em;\n}\n"
  },
  {
    "path": "docs/_static/timestamps.js",
    "content": "/*jshint esversion: 6 */\n\n(function() {\n    'use strict';\n\n    function init_timestamps() {\n        document.querySelectorAll('.video-with-timestamps').forEach(loc => {\n            if (loc.dataset.inited === 'true') return;\n            loc.dataset.inited = 'true';\n            const container_id = loc.id;\n            const dl = loc.querySelector('dl');\n            dl.querySelectorAll('dt').forEach(dt => {\n                dt.innerHTML = '<a href=\"javascript:void(0)\" style=\"text-decoration: none\"><time>' + dt.innerHTML + '</time></a>';\n                dt.style.display = 'inline';\n            });\n            dl.addEventListener('click', handle_timestamp_click.bind(null, container_id));\n        });\n    }\n\n    function handle_timestamp_click(container_id, e) {\n        if (e.target.tagName.toUpperCase() === 'TIME') {\n            const timestamp = e.target.textContent;\n            if (timestamp) {\n                const [minutes, seconds] = timestamp.split(':');\n                const total_seconds = parseInt(minutes) * 60 + parseInt(seconds);\n                const video = document.querySelector('#' + container_id + ' video');\n                video.currentTime = total_seconds;\n                video.play();\n            }\n        }\n    }\n\n    init_timestamps();\n    document.addEventListener('DOMContentloaded', init_timestamps);\n})();\n"
  },
  {
    "path": "docs/_templates/base.html",
    "content": "{% extends \"!base.html\" %}\n{% block extrahead %}\n\n  {{ super() }}\n\n  {%- if analytics_id %}\n  <!-- Global site tag (gtag.js) - Google Analytics -->\n    <script async src=\"https://www.googletagmanager.com/gtag/js?id={{ analytics_id }}\"></script>\n    <script>\n        window.dataLayer = window.dataLayer || [];\n        function gtag(){dataLayer.push(arguments);}\n        gtag('js', new Date());\n\n        gtag('config', '{{ analytics_id }}');\n    </script>\n  {% endif -%}\n\n{% endblock %}\n"
  },
  {
    "path": "docs/actions.rst",
    "content": "Mappable actions\n-----------------------\n\n.. highlight:: conf\n\nThe actions described below can be mapped to any key press or mouse action\nusing the ``map`` and ``mouse_map`` directives in :file:`kitty.conf`. For\nconfiguration examples, see the default shortcut links for each action.\nTo read about keyboard mapping in more detail, see :doc:`mapping`.\n\nYou can also browse and trigger these actions by pressing :sc:`command_palette`\nto run the command palette.\n\n.. include:: /generated/actions.rst\n"
  },
  {
    "path": "docs/basic.rst",
    "content": "Tabs and Windows\n-------------------\n\n|kitty| is capable of running multiple programs organized into tabs and windows.\nThe top level of organization is the :term:`OS window <os_window>`. Each OS\nwindow consists of one or more :term:`tabs <tab>`. Each tab consists of one or more\n:term:`kitty windows <window>`. The kitty windows can be arranged in multiple\ndifferent :term:`layouts <layout>`, like windows are organized in a tiling\nwindow manager. The keyboard controls (which are :ref:`all customizable\n<conf-kitty-shortcuts>`) for tabs and windows are:\n\nScrolling\n~~~~~~~~~~~~~~\n\n=========================   =======================\nAction                      Shortcut\n=========================   =======================\nLine up                     :sc:`scroll_line_up` (also :kbd:`⌥+⌘+⇞` and :kbd:`⌘+↑` on macOS)\nLine down                   :sc:`scroll_line_down` (also :kbd:`⌥+⌘+⇟` and :kbd:`⌘+↓` on macOS)\nPage up                     :sc:`scroll_page_up` (also :kbd:`⌘+⇞` on macOS)\nPage down                   :sc:`scroll_page_down` (also :kbd:`⌘+⇟` on macOS)\nTop                         :sc:`scroll_home` (also :kbd:`⌘+↖` on macOS)\nBottom                      :sc:`scroll_end` (also :kbd:`⌘+↘` on macOS)\nPrevious shell prompt       :sc:`scroll_to_previous_prompt` (see :ref:`shell_integration`)\nNext shell prompt           :sc:`scroll_to_next_prompt` (see :ref:`shell_integration`)\nBrowse scrollback in less   :sc:`show_scrollback`\nBrowse last cmd output      :sc:`show_last_command_output` (see :ref:`shell_integration`)\nSearch scrollback in less   :sc:`search_scrollback` (also :kbd:`⌘+F` on macOS)\n=========================   =======================\n\nThe scroll actions only take effect when the terminal is in the main screen.\nWhen the alternate screen is active (for example when using a full screen\nprogram like an editor) the key events are instead passed to program running in the\nterminal.\n\nTabs\n~~~~~~~~~~~\n\n========================    =======================\nAction                      Shortcut\n========================    =======================\nNew tab                     :sc:`new_tab` (also :kbd:`⌘+t` on macOS)\nClose tab                   :sc:`close_tab` (also :kbd:`⌘+w` on macOS)\nNext tab                    :sc:`next_tab` (also :kbd:`⇧+⌃+⇥` and :kbd:`⇧+⌘+]` on macOS)\nPrevious tab                :sc:`previous_tab` (also :kbd:`⇧+⌃+⇥` and :kbd:`⇧+⌘+[` on macOS)\nNext layout                 :sc:`next_layout`\nMove tab forward            :sc:`move_tab_forward`\nMove tab backward           :sc:`move_tab_backward`\nSet tab title               :sc:`set_tab_title` (also :kbd:`⇧+⌘+i` on macOS)\n========================    =======================\n\n\nWindows\n~~~~~~~~~~~~~~~~~~\n\n========================    =======================\nAction                      Shortcut\n========================    =======================\nNew window                  :sc:`new_window` (also :kbd:`⌘+↩` on macOS)\nNew OS window               :sc:`new_os_window` (also :kbd:`⌘+n` on macOS)\nClose window                :sc:`close_window` (also :kbd:`⇧+⌘+d` on macOS)\nResize window               :sc:`start_resizing_window` (also :kbd:`⌘+r` on macOS)\nNext window                 :sc:`next_window`\nPrevious window             :sc:`previous_window`\nMove window forward         :sc:`move_window_forward`\nMove window backward        :sc:`move_window_backward`\nMove window to top          :sc:`move_window_to_top`\nVisually focus window       :sc:`focus_visible_window`\nVisually swap window        :sc:`swap_with_window`\nFocus specific window       :sc:`first_window`, :sc:`second_window` ... :sc:`tenth_window`\n                            (also :kbd:`⌘+1`, :kbd:`⌘+2` ... :kbd:`⌘+9` on macOS)\n                            (clockwise from the top-left)\n========================    =======================\n\nAdditionally, you can define shortcuts in :file:`kitty.conf` to focus\nneighboring windows and move windows around (similar to window movement in\n:program:`vim`)::\n\n   map ctrl+left neighboring_window left\n   map shift+left move_window right\n   map ctrl+down neighboring_window down\n   map shift+down move_window up\n   ...\n\nYou can also define a shortcut to switch to the previously active window::\n\n   map ctrl+p nth_window -1\n\n:ac:`nth_window` will focus the nth window for positive numbers (starting from\nzero) and the previously active windows for negative numbers.\n\nTo switch to the nth OS window, you can define :ac:`nth_os_window`. Only\npositive numbers are accepted, starting from one.\n\n.. _detach_window:\n\nYou can define shortcuts to detach the current window and move it to another tab\nor another OS window::\n\n    # moves the window into a new OS window\n    map ctrl+f2 detach_window\n    # moves the window into a new tab\n    map ctrl+f3 detach_window new-tab\n    # moves the window into the previously active tab\n    map ctrl+f3 detach_window tab-prev\n    # moves the window into the tab at the left of the active tab\n    map ctrl+f3 detach_window tab-left\n    # moves the window into a new tab created to the left of the active tab\n    map ctrl+f3 detach_window new-tab-left\n    # asks which tab to move the window into\n    map ctrl+f4 detach_window ask\n\nSimilarly, you can detach the current tab, with::\n\n    # moves the tab into a new OS window\n    map ctrl+f2 detach_tab\n    # asks which OS Window to move the tab into\n    map ctrl+f4 detach_tab ask\n\nNote that tabs can be re-arranged, detached and moved to another OS Window in\nthe same kitty instance using drag and drop.\n\nFinally, you can define a shortcut to close all windows in a tab other than the\ncurrently active window::\n\n    map f9 close_other_windows_in_tab\n\n\nOther keyboard shortcuts\n----------------------------------\n\nThe full list of actions that can be mapped to key presses is available\n:doc:`here </actions>`. To learn how to do more sophisticated keyboard\nmappings, such as modal mappings, per application mappings, etc. see\n:doc:`mapping`.\n\n==================================  =======================\nAction                              Shortcut\n==================================  =======================\nShow this help                      :sc:`show_kitty_doc`\nCopy to clipboard                   :sc:`copy_to_clipboard` (also :kbd:`⌘+c` on macOS)\nPaste from clipboard                :sc:`paste_from_clipboard` (also :kbd:`⌘+v` on macOS)\nPaste from selection                :sc:`paste_from_selection`\nPass selection to program           :sc:`pass_selection_to_program`\nIncrease font size                  :sc:`increase_font_size` (also :kbd:`⌘++` on macOS)\nDecrease font size                  :sc:`decrease_font_size` (also :kbd:`⌘+-` on macOS)\nRestore font size                   :sc:`reset_font_size` (also :kbd:`⌘+0` on macOS)\nToggle fullscreen                   :sc:`toggle_fullscreen` (also :kbd:`⌃+⌘+f` on macOS)\nToggle maximized                    :sc:`toggle_maximized`\nInput Unicode character             :sc:`input_unicode_character` (also :kbd:`⌃+⌘+space` on macOS)\nOpen URL in web browser             :sc:`open_url`\nReset the terminal                  :sc:`reset_terminal` (also :kbd:`⌥+⌘+r` on macOS)\nEdit :file:`kitty.conf`             :sc:`edit_config_file` (also :kbd:`⌘+,` on macOS)\nReload :file:`kitty.conf`           :sc:`reload_config_file` (also :kbd:`⌃+⌘+,` on macOS)\nDebug :file:`kitty.conf`            :sc:`debug_config` (also :kbd:`⌥+⌘+,` on macOS)\nOpen a |kitty| shell                :sc:`kitty_shell`\nIncrease background opacity         :sc:`increase_background_opacity`\nDecrease background opacity         :sc:`decrease_background_opacity`\nFull background opacity             :sc:`full_background_opacity`\nReset background opacity            :sc:`reset_background_opacity`\n==================================  =======================\n"
  },
  {
    "path": "docs/binary.rst",
    "content": "Install kitty\n========================\n\nBinary install\n----------------\n\n.. highlight:: sh\n\nYou can install pre-built binaries of |kitty| if you are on macOS or Linux using\nthe following simple command:\n\n.. code-block:: sh\n\n    _kitty_install_cmd\n\n\nThe binaries will be installed in the standard location for your OS,\n:file:`/Applications/kitty.app` on macOS and :file:`~/.local/kitty.app` on\nLinux. The installer only touches files in that directory. To update kitty,\nsimply re-run the command.\n\n.. warning::\n   **Do not** copy the kitty binary out of the installation folder. If you want\n   to add it to your :envvar:`PATH`, create a symlink in :file:`~/.local/bin` or\n   :file:`/usr/bin` or wherever. You should create a symlink for the :file:`kitten`\n   binary as well. Whichever folder you choose to create the symlink in should\n   be in the **systemwide** PATH, a folder added to the PATH in your shell rc\n   files will not work when running kitty from your desktop environment.\n\n\nManually installing\n---------------------\n\nIf something goes wrong or you simply do not want to run the installer, you can\nmanually download and install |kitty| from the `GitHub releases page\n<https://github.com/kovidgoyal/kitty/releases>`__. If you are on macOS, download\nthe :file:`.dmg` and install as normal. If you are on Linux, download the\ntarball and extract it into a directory. The |kitty| executable will be in the\n:file:`bin` sub-directory.\n\n\nDesktop integration on Linux\n--------------------------------\n\nIf you want the kitty icon to appear in the taskbar and an entry for it to be\npresent in the menus, you will need to install the :file:`kitty.desktop` file.\nThe details of the following procedure may need to be adjusted for your\nparticular desktop, but it should work for most major desktop environments.\n\n.. code-block:: sh\n\n    # Create symbolic links to add kitty and kitten to PATH (assuming ~/.local/bin is in\n    # your system-wide PATH)\n    ln -sf ~/.local/kitty.app/bin/kitty ~/.local/kitty.app/bin/kitten ~/.local/bin/\n    # Place the kitty.desktop file somewhere it can be found by the OS\n    cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications/\n    # If you want to open text files and images in kitty via your file manager also add the kitty-open.desktop file\n    cp ~/.local/kitty.app/share/applications/kitty-open.desktop ~/.local/share/applications/\n    # Update the paths to the kitty and its icon in the kitty desktop file(s)\n    sed -i \"s|Icon=kitty|Icon=$(readlink -f ~)/.local/kitty.app/share/icons/hicolor/256x256/apps/kitty.png|g\" ~/.local/share/applications/kitty*.desktop\n    sed -i \"s|Exec=kitty|Exec=$(readlink -f ~)/.local/kitty.app/bin/kitty|g\" ~/.local/share/applications/kitty*.desktop\n    # Make xdg-terminal-exec (and hence desktop environments that support it use kitty)\n    echo 'kitty.desktop' > ~/.config/xdg-terminals.list\n\n.. note::\n    In :file:`kitty-open.desktop`, kitty is registered to handle some supported\n    MIME types. This will cause kitty to take precedence on some systems where\n    the default apps are not explicitly set. For example, if you expect to use\n    other GUI file managers to open dir paths when using commands such as\n    :program:`xdg-open`, you should configure the default opener for the MIME\n    type ``inode/directory``::\n\n        xdg-mime default org.kde.dolphin.desktop inode/directory\n\n.. note::\n    If you use the venerable `stow <https://www.gnu.org/software/stow/>`__\n    command to manage your manual installations, the following takes care of the\n    above for you (use with :code:`dest=~/.local/stow`)::\n\n        cd ~/.local/stow\n        stow -v kitty.app\n\n\nCustomizing the installation\n--------------------------------\n\n.. _nightly:\n\n* You can install the latest nightly kitty build with ``installer``:\n\n  .. code-block:: sh\n\n     _kitty_install_cmd \\\n         installer=nightly\n\n  If you want to install it in parallel to the released kitty specify a\n  different install locations with ``dest``:\n\n  .. code-block:: sh\n\n     _kitty_install_cmd \\\n         installer=nightly dest=/some/other/location\n\n* You can specify a specific version to install, with:\n\n  .. code-block:: sh\n\n     _kitty_install_cmd \\\n         installer=version-0.35.2\n\n* You can tell the installer not to launch |kitty| after installing it with\n  ``launch=n``:\n\n  .. code-block:: sh\n\n     _kitty_install_cmd \\\n         launch=n\n\n* You can use a previously downloaded dmg/tarball, with ``installer``:\n\n  .. code-block:: sh\n\n     _kitty_install_cmd \\\n         installer=/path/to/dmg or tarball\n\n\nUninstalling\n----------------\n\nAll the installer does is copy the kitty files into the install directory. To\nuninstall, simply delete that directory.\n\n\nBuilding from source\n------------------------\n\n|kitty| is easy to build from source, follow the :doc:`instructions <build>`.\n"
  },
  {
    "path": "docs/build.rst",
    "content": "Build from source\n==================\n\n.. image:: https://github.com/kovidgoyal/kitty/workflows/CI/badge.svg\n  :alt: Build status\n  :target: https://github.com/kovidgoyal/kitty/actions?query=workflow%3ACI\n\n.. note::\n   If you just want to test the latest changes to kitty, you don't need to build\n   from source. Instead install the :ref:`latest nightly build <nightly>`.\n\n.. highlight:: sh\n\n|kitty| is designed to run from source, for easy hack-ability. All you need to\nget started is a C compiler and the `go compiler\n<https://go.dev/doc/install>`__ (on Linux, the :ref:`X11 development libraries <x11-dev-libs>` as well).\nAfter installing those, run the following commands::\n\n    git clone https://github.com/kovidgoyal/kitty.git && cd kitty\n    ./dev.sh build\n\nThat's it, kitty will be built from source, magically. You can run it as\n:file:`kitty/launcher/kitty`.\n\nThis works, because the :code:`./dev.sh build` command downloads all the major\ndependencies of kitty as pre-built binaries for your platform and builds kitty\nto use these rather than system libraries. The few required system libraries\nare X11 and DBUS on Linux.\n\nIf you make changes to kitty code, simply re-run :code:`./dev.sh build`\nto build kitty with your changes.\n\n.. note::\n   If you plan to run kitty from source long-term, there are a couple of\n   caveats to be aware of. You should occasionally run ``./dev.sh deps``\n   to have the dependencies re-downloaded as they are updated periodically.\n   Also, the built kitty executable assumes it will find source in whatever\n   directory you first ran :code:`./dev.sh build` in. If you move/rename the\n   directory, run :code:`make clean && ./dev.sh build`. You should also create\n   symlinks to the :file:`kitty` and :file:`kitten` binaries from somewhere\n   in your PATH so that they can be conveniently launched.\n\n.. note::\n   On macOS, you can use :file:`kitty/launcher/kitty.app` to run kitty as well,\n   but note that this is an unsigned kitty.app so some functionality such as\n   notifications will not work as Apple disallows this.  If you need this\n   functionality, you can try signing the built :file:`kitty.app` with a self\n   signed certificate, see for example, `here\n   <https://stackoverflow.com/questions/27474751/how-can-i-codesign-an-app-without-being-in-the-mac-developer-program/27474942>`__.\n\nBuilding in debug mode\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe following will build with debug symbols::\n\n    ./dev.sh build --debug\n\nTo build with sanitizers and debug symbols::\n\n    ./dev.sh build --debug --sanitize\n\nFor more help on the various options supported by the build script::\n\n    ./dev.sh build -h\n\n\nBuilding the documentation\n-------------------------------------\n\nTo have the kitty documentation available locally, run::\n\n    ./dev.sh deps -for-docs && ./dev.sh docs\n\nTo develop the docs, with live reloading, use::\n\n    ./dev.sh deps -for-docs && ./dev.sh docs -live-reload\n\nDependencies\n----------------\n\nThese dependencies are needed when building against system libraries only.\n\nRun-time dependencies:\n\n* ``python``\n* ``harfbuzz`` >= 2.2.0\n* ``zlib``\n* ``libpng``\n* ``liblcms2``\n* ``libxxhash``\n* ``openssl``\n* ``pixman`` (not needed on macOS)\n* ``cairo`` (not needed on macOS)\n* ``freetype`` (not needed on macOS)\n* ``fontconfig`` (not needed on macOS)\n* ``libcanberra`` (not needed on macOS)\n* ``libsystemd`` (optional, not needed on non systemd systems)\n* ``ImageMagick`` (optional, needed to display uncommon image formats in the terminal)\n\n\nBuild-time dependencies:\n\n* ``gcc`` or ``clang``\n* ``simde``\n* ``go`` >= _build_go_version (see :file:`go.mod` for go packages used during building)\n* ``pkg-config``\n* Symbols NERD Font Mono either installed system-wide or placed in :file:`fonts/SymbolsNerdFontMono-Regular.ttf`\n* For building on Linux in addition to the above dependencies you might also\n  need to install the following packages, if they are not already installed by\n  your distro:\n\n  - ``liblcms2-dev``\n  - ``libfontconfig-dev``\n  - ``libssl-dev``\n  - ``libpython3-dev``\n  - ``libxxhash-dev``\n  - ``libsimde-dev``\n  - ``libcairo2-dev``\n\n  .. _x11-dev-libs:\n\n  Also, the X11 development libraries:\n\n  - ``libdbus-1-dev``\n  - ``libxcursor-dev``\n  - ``libxrandr-dev``\n  - ``libxi-dev``\n  - ``libxinerama-dev``\n  - ``libgl1-mesa-dev``\n  - ``libxkbcommon-x11-dev``\n  - ``libfontconfig-dev``\n  - ``libx11-xcb-dev``\n\n\n\nBuild and run from source with Nix\n-------------------------------------------\n\nOn NixOS or any other Linux or macOS system with the Nix package manager\ninstalled, execute `nix-shell\n<https://nixos.org/guides/nix-pills/developing-with-nix-shell.html>`__ to create\nthe correct environment to build kitty or use ``nix-shell --pure`` instead to\neliminate most of the influence of the outside system, e.g. globally installed\npackages. ``nix-shell`` will automatically fetch all required dependencies and\nmake them available in the newly spawned shell.\n\nThen proceed with ``make`` or ``make app`` according to the platform specific\ninstructions above.\n\n.. _packagers:\n\nNotes for Linux/macOS packagers\n----------------------------------\n\nThe released |kitty| source code is available as a `tarball`_ from\n`the GitHub releases page <https://github.com/kovidgoyal/kitty/releases>`__.\n\nWhile |kitty| does use Python, it is not a traditional Python package, so please\ndo not install it in site-packages.\nInstead run::\n\n    make linux-package\n\nThis will install |kitty| into the directory :file:`linux-package`. You can run\n|kitty| with :file:`linux-package/bin/kitty`. All the files needed to run kitty\nwill be in :file:`linux-package/lib/kitty`. The terminfo file will be installed\ninto :file:`linux-package/share/terminfo`. Simply copy these files into\n:file:`/usr` to install |kitty|. In other words, :file:`linux-package` is the\nstaging area into which |kitty| is installed. You can choose a different staging\narea, by passing the ``--prefix`` argument to :file:`setup.py`.\n\nYou should probably split |kitty| into three packages:\n\n:code:`kitty-terminfo`\n    Installs the terminfo file\n\n:code:`kitty-shell-integration`\n    Installs the shell integration scripts (the contents of the\n    shell-integration directory in the kitty source code), probably to\n    :file:`/usr/share/kitty/shell-integration`\n\n:code:`kitty`\n    Installs the main program\n\nThis allows users to install the terminfo and shell integration files on\nservers into which they ssh, without needing to install all of |kitty|. The\nshell integration files **must** still be present in\n:file:`lib/kitty/shell-integration` when installing the kitty main package as\nthe kitty program expects to find them there.\n\n.. note::\n   You need a couple of extra dependencies to build linux-package. :file:`tic`\n   to compile terminfo files, usually found in the development package of\n   :file:`ncurses`. Also, if you are building from a git checkout instead of the\n   released source code tarball, you will need to install the dependencies from\n   :file:`docs/requirements.txt` to build the kitty documentation. They can be\n   installed most easily with ``python -m pip -r docs/requirements.txt``.\n\nThis applies to creating packages for |kitty| for macOS package managers such as\nHomebrew or MacPorts as well.\n\nCross compilation\n-------------------\n\nWhile cross compilation is neither officially supported, nor recommended, as it\nmeans the test suite cannot be run for the cross compiled build, there is some\nsupport for cross compilation. Basically, run::\n\n    make prepare-for-cross-compile\n\nThen setup the cross compile environment (CC, CFLAGS, PATH, etc.) and run::\n\n    make cross-compile\n\nThis will create the cross compiled build in the :file:`linux-package`\ndirectory.\n"
  },
  {
    "path": "docs/changelog.rst",
    "content": "Changelog\n==============\n\n|kitty| is a feature-rich, cross-platform, *fast*, GPU based terminal.\nTo update |kitty|, :doc:`follow the instructions <binary>`.\n\n.. recent major features {{{\n\nRecent major new features\n---------------------------\n\nMousing [0.46]\n~~~~~~~~~~~~~~~\n\nkitty already had excellent mouse support, but now it is taking it to the next\nlevel. The kitty scrollback buffer grew support for :opt:`smooth scrolling\n<pixel_scroll>` and :opt:`momentum based scrolling <momentum_scroll>`\nfor a natural, smooth and kinetic scrolling experience.\n\nAdditionally, you can now :opt:`drag kitty tabs around <tab_bar_drag_threshold>` with the mouse\nto re-order them, move them to another kitty OS Window or even detach them into\ntheir own OS Window.\n\nFinally, a long requested feature, the ability to resize kitty windows (aka\nsplits) with the mouse was implemented.\n\nChoose files, fast [0.45]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nA new :doc:`kitten to select files at the speed of thought\n</kittens/choose-files>` with a keyboard first interface and support for\ncontent previews of text files with syntax highlighting, images, videos, e-books\nand more. Allows you to select files for use at the shell prompt or other\nterminal workflows with just a few keystrokes, similar to how fuzzy finders\nlike `fzf <https://github.com/junegunn/fzf/>`__ operate, but designed for\nfiles in particular, leveraging the various innovations of kitty such as image\ndisplay and variable sized text.\n\nOn Linux, it can even be used as a :doc:`drop in replacement </kittens/desktop-ui>`\nfor the File Open/Save dialog boxes in GUI programs.\n\n\nSessions [0.43]\n~~~~~~~~~~~~~~~~\n\nkitty has long had support for :doc:`sessions`, aka simple text files where you\ncan define what tabs, windows and programs you wish to run in kitty. Now in\naddition to that kitty has the ability to :ref:`create and switch between\nsessions <goto_session>` with a single keypress and also to manually setup some\ntabs/windows in kitty and :ref:`save it as a session file <complex_sessions>`,\nfor seamless and intuitive session file creation.\n\nA scrollbar for the kitty scrollback [0.43]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nA long requested feature, kitty has finally :pull:`gotten a scrollbar <8945>`\nthat can be used with the mouse for browsing its scrollback. The bar appear\nautomatically when you start scrolling backwards and is :opt:`extensively\nconfigurable <scrollbar>` in kitty.conf. Note that the old ``scrollback_indicator_opacity``\noption is deprecated.\n\nMultiple cursors [0.43]\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nkitty has pioneered a new :doc:`escape code protocol\n<multiple-cursors-protocol>` that allows terminal applications to use multiple\ncursors, rendered natively. These are typically used in editors to make the\nsame edit at multiple locations. Now terminal based editors can use properly\nrendered native cursors, just like their GUI cousins, at last.\n\nAccess kitty with a single keypress [0.42]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. include:: quake-screenshots.rst\n\nkitty now has a Quake like floating, translucent terminal window, so you can access\nall that kitty goodness instantly with a single keypress.\n\nSee the screenshots on the side and head over to the :doc:`kitten page for details\non how to set it up </kittens/quick-access-terminal>`.\n\n\nMultiple sized text [0.40]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nkitty is the first major terminal to introduce the concept of multiple sized\ntext. Terminal programs running in kitty can now opt-in to use and display text\nin multiple font sizes both larger and smaller than the base font size. This is\ndone in a backwards compatible, opt-in way that does not affect how traditional\nterminal programs work at all. For details on the new feature and how to use\nit, see :doc:`text-sizing-protocol`.\n\nCursor trails [0.37]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nShow an animated trail when the text cursor makes large jumps making it easy\nto follow cursor movements. Inspired by the similar feature in neovide, but\nworks with terminal multiplexers and kitty windows as well. See :pull:`the pull\nrequest <7970>` for a demonstration video. This feature is optional and must be\nturned on by the :opt:`cursor_trail` option in :file:`kitty.conf`.\n\n\nVariable font support [0.36]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTerminal aficionados spend all day staring at text, so getting text\nrendering just right is very important. In that spirit, kitty now supports\n`OpenType Variable fonts <https://en.wikipedia.org/wiki/Variable_font>`__.\nThese allow precise customisation of font characteristics, such as weight and\nspacing. Not only that, kitty now has a new :doc:`choose-fonts\n<kittens/choose-fonts>` kitten that provides a UI for choosing fonts with\nsupport for font features, variable fonts and previews of how the font will\nlook. This is in addition to its existing best-in-class font customization\nabilities, such as: :opt:`symbol_map`, :opt:`text_composition_strategy`,\n:opt:`font_features` and :opt:`modify_font`. kitty knows text rendering is\nimportant, and goes the extra mile for it.\n\nDesktop notifications [0.36]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n|kitty| now has a :doc:`notify </kittens/notify>` kitten that can be used to\ndisplay desktop notifications from the command line, even over SSH. It has\nsupport for icons, buttons, updating notifications, waiting till\nthe notification is closed, etc. The underlying :doc:`desktop-notifications`\nprotocol has been expanded to support all these features.\n\nWayland goodies [0.34]\n~~~~~~~~~~~~~~~~~~~~~~~\n\nWayland users should rejoice as kitty now comes with major Wayland\nquality-of-life improvements:\n\n* Draw GPU accelerated :doc:`desktop panels and background </kittens/panel>`\n  running arbitrary terminal programs. For example, run `btop\n  <https://github.com/aristocratos/btop/>`__ as your desktop background\n\n* Background blur for transparent windows is now supported under KDE\n  using a custom KDE specific protocol\n\n* The kitty window decorations in GNOME are now fully functional with buttons\n  and they follow system dark/light mode automatically\n\n* kitty now supports fractional scaling in Wayland which means pixel perfect\n  rendering when you use a fractional scale with no wasted performance on\n  resizing an overdrawn pixmap in the compositor\n\nWith this release kitty's Wayland support is now on par with X11, provided\nyou use a decent Wayland compositor.\n\nCheetah speed 🐆 [0.33]\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nkitty has grown up and become a cheetah. It now parses data it receives in\nparallel :iss:`using SIMD vector CPU instructions <7005>` for a 2x speedup in\nbenchmarks and a 10%-50% real world speedup depending on workload. There is a\nnew benchmarking kitten ``kitten __benchmark__`` that can be used to measure\nterminal throughput. There is also :ref:`a table <throughput>` showing kitty is\nmuch faster than other terminal emulators based on the benchmark kitten. While\nkitty was already so fast that its performance was never a bottleneck, this\nimprovement makes it even faster and more importantly reduces the energy\nconsumption to do the same tasks.\n\n.. }}}\n\nDetailed list of changes\n-------------------------------------\n\n0.47.0 [future]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Watchers: Add an `on_quit` event to global watchers (:iss:`9675`)\n\n- Wayland: Fix a crash on some compositors when dragging a tab between OS\n  Windows (:iss:`9677`)\n\n- Fix incorrect behavior when using the actions to move tab forward/backward\n  with a tab_bar_filter active (:iss:`9672`)\n\n- Prevent stacking of multiple rename tab windows (:iss:`9691`)\n\n- choose files kitten: Fix a regression that caused incorrect highlight of matched letters\n\n- macOS: When using :opt:`macos_traditional_fullscreen` do not render content under the notch (:pull:`9678`)\n\n- X11: Fix massive scroll when switching focus between kitty and another application (:iss:`9703`)\n\n- Markers: Fix marking not working for multicell characters (:iss:`9705`)\n\n- Fix a regression in 0.46 that broke drag select in unfocused windows\n  (:iss:`9713`)\n\n0.46.1 [2026-03-16]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- diff kitten: Highlight moved lines using a different background color (:opt:`kitten-diff.mark_moved_lines`) (:iss:`3241`)\n\n- Fix a regression that broke ``kitten update-self`` (:iss:`9642`)\n\n- macOS: Clear bell alert badge on dock icon on mouse/keyboard activity (:iss:`9640`)\n\n- Fix a regression that broke accept anyway shortcut in the paste confirmation dialog (:pull:`9640`)\n\n- Fix kitty hanging on startup on Intel macs (:iss:`9643`)\n\n- X11: Fix a regression that caused some high res scroll devices to be treated as line based scroll devices (:iss:`9649`)\n\n- Wayland: Fix momentum scrolling not working on compositors that send a stop frame with no axis information (:iss:`9653`)\n\n- Linux: Fix regression that broke drag and drop from GTK applications (:iss:`9656`)\n\n- macOS: Fix using Fn key for start dictation not working (:iss:`9661`)\n\n- Don't use neighboring tab colors for tab bar margins in translucent windows (:iss:`9663`)\n\n- macOS: Fix OS window focus not restored when switching spaces (:iss:`9665`)\n\n0.46.0 [2026-03-11]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Pixel scrolling for the kitty scrollback buffer controlled via :opt:`pixel_scroll` (:pull:`9330`)\n\n- Linux: momentum scrolling in the kitty scrollback buffer for touchpads and touchscreens, see :opt:`momentum_scroll`\n\n- X11: support high resolution scroll events from touchpads, etc\n\n- macOS: Implement support for Apple dictation to input text in kitty (:iss:`3732`)\n\n- Allow dragging tabs (opt:`tab_bar_drag_threshold`) in the tab bar to re-order, move to another OS Window or\n  detach (:pull:`9296`)\n\n- Allow dragging window borders to resize kitty windows in all the different\n  layouts, controlled by :opt:`window_drag_tolerance` (:pull:`9447`)\n\n- Allow showing :opt:`configurable window titles <window_title_bar>` for individual kitty\n  windows via a window title bar (:pull:`9450`)\n\n- A command palette (:sc:`command_palette`) to browse and trigger all mapped and unmapped actions\n  (:pull:`9545`)\n\n- choose-files kitten: Fix JXL image preview not working (:iss:`9323`)\n\n- Fix tab bar rendering glitches when using :opt:`tab_bar_filter` in some\n  circumstances (:iss:`9328`)\n\n- Add support for specifying colors in :file:`kitty.conf` in OKLCH and LAB\n  color spaces (:pull:`9325`)\n\n- Fix a regression that broke using line numbers with the edit-in-kitty command\n  (:pull:`9346`)\n\n- Key maps: Allow specifying a timeout for multi key mappings and keyboard modes (:pull:`9551`)\n\n- macOS: Fix changes to :opt:`macos_titlebar_color` while in full screen not being applied after exiting fullscreen (:iss:`9350`)\n\n- ncurses: Fix ncurses not using dim because it is missing from the sgr property\n  in terminfo even though it is present in the dim property.\n\n- Fix a regression in the previous release that caused moving between neighbors\n  in the vertical and horizontal layouts to go in the opposite direction (:iss:`9355`)\n\n- Fix :ac:`goto_session` not respecting the focus_tab session directive when\n  creating a session in an existing OS window (:iss:`9366`)\n\n- Wayland: Fix a regression in the previous release that caused doubled key\n  repeats on compositors that implement compositor side key repeat events\n  (:iss:`9374`)\n\n- icat: Fix a regression in the previous release when rendering GIF animations\n  with frames that dispose onto background with non-zero delay using the native\n  engine (:iss:`9376`)\n\n- Wayland: Remove usage of the Wayland color management protocol to inform\n  compositors of the color space used by kitty (:iss:`9341`)\n\n- Linux: Fix a regression in 0.40 that caused horizontal alignment for emoji to\n  be incorrect in some cases (:iss:`9395`)\n\n- icat kitten: When catting multiple images display the images in input order (:iss:`9413`)\n\n- kitten @: Fix relative paths for --password-file being resolved relative to\n  CWD instead of the kitty config directory\n\n- kitten choose-files: Add a new binding of :kbd:`Alt+Enter` to modify the name\n  of an existing file when choosing a save file name (:iss:`9387`)\n\n- kitten choose-files: Fix TAB completion in the choose save file name prompt\n  not working with respect to the current working directory (:iss:`9387`)\n\n- Fix line-at-once selection not extending wrapped lines into scrollback (:iss:`9437`)\n\n- ssh kitten: Restore keyboard mode even if the ssh connection drops\n\n- edit-in-kitty: Handle connection drop more gracefully (:pull:`9480`)\n\n- macOS: Fix changing window title with global menubar menu open causes menu to\n  get stuck (:pull:`9490`)\n\n- Fix :opt:`focus_follows_mouse` not working during a drag and drop (:iss:`9497`)\n\n- :ac:`goto_session`: Add a ``--active-only`` option to select from only active\n  sessions (:pull:`9503`)\n\n- Shell integration: Allow sending click events to shells using y co-ordinates\n  relative to prompts (:iss:`9500`)\n\n- A new action :ac:`copy_selection_or_last_command_output` (:pull:`9512`)\n\n- Wayland: Add support for the background blur extension (:iss:`9534`)\n\n- macOS: A new option :opt:`macos_dock_badge_on_bell` to show a badge on the\n  kitty dock icon when a bell occurs (:pull:`9529`)\n\n- macOS: Workaround for yet another Tahoe bug causing rendering to fail\n  (:pull:`9520`)\n\n- URL detection: Allow trailing asterisks in URLs (:iss:`9543`)\n\n- Wayland: Add support for :code:`titlebar-only` in :opt:`hide_window_decorations`\n  to hide the titlebar while keeping shadows for window resizing. (:pull:`9486`)\n\n- Text sizing protocol: Fix alignment/cropping issues when rendering text with\n  a fractional scale (:iss:`9471`)\n\n- macOS: Fix a crash when using :opt:`macos_traditional_fullscreen` with split\n  view (:pull:`9573`)\n\n- macOS: Fix flickering during OS Window resize (:disc:`9582`)\n\n- Cursor trail: Show a cursor trail when switching tabs (:pull:`9588`)\n\n- Make shift+left click extend the current selection instead of starting a new\n  selection when the mouse is not grabbed by the TUI application (:disc:`9608`)\n\n- Allow double clicking on a tab to rename it (:pull:`9609`)\n\n- :ac:`remote_control_script` resolve relative paths with respect to kitty\n  config directory (:iss:`9625`)\n\n- Splits layout: Add new mappable actions to maximize a window in the splits\n  layout (:iss:`9629`)\n\n\n0.45.0 [2025-12-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new :doc:`kitten to select files at the speed of thought </kittens/choose-files>` with a keyboard first interface and support for content previews of text files with syntax highlighting, images, videos, e-books and more (:iss:`9263`)\n\n- Add support for the `paste events protocol <https://rockorager.dev/misc/bracketed-paste-mime/>`__ (:iss:`9183`)\n\n- icat kitten: Add support for animated PNG and animated WebP, netPBM images, ICC color profiles and CCIP color space metadata to the builtin engine\n\n- icat kitten: Add a new flag :option:`kitty +kitten icat --fit` to control how images are scaled to fit the screen (:iss:`9201`)\n\n- icat kitten: The :option:`kitty +kitten icat --scale-up` flag now takes effect when not using :option:`kitty +kitten icat --place` as well\n\n- Add a mappable action :ac:`copy_last_command_output` to copy the output of the last\n  command to the clipboard (:pull:`9185`)\n\n- ssh kitten: Fix a bug where automatic login was not working (:iss:`9187`)\n\n- Graphics: Fix overwrite composition mode for animation frames not being honored\n\n- Automatic color scheme switching: Fix title bar and scroll bar colors not being updated (:iss:`9167`)\n\n- macOS: Fix cycle through OS windows only swapping between the two most recent\n  OS Windows. Also add a cycle through OS Windows backwards action.\n  (:iss:`9215`)\n\n- :ac:`goto_session`: allow specifying a directory to select a session file\n  from the directory (:pull:`9219`)\n\n- Have reloading config also reload the custom tab bar python modules (:disc:`9221`)\n\n- kitten @ ls: Also output the neighbors for every window (:disc:`9225`)\n\n- Have the :option:`kitty --start-as` flag be respected when used with\n  :option:`kitty --single-instance` (:iss:`9228`)\n\n- When expanding environment variables in :opt:`listen_on` allow the :opt:`env`\n  directive to take effect\n\n- macOS: Fix closing an OS Window when another OS Window is minimized causing\n  the minimized window to be un-minimized (:iss:`8913`)\n\n- Do not rewrap the text in the alternate screen buffer. Avoids flicker during\n  live resize with no :opt:`resize_debounce_time` (:disc:`9142`)\n\n- Add a default mapping :ac:`search_scrollback` to open the scrollback in a\n  pager in search mode. If any text is currently selected it is automatically\n  searched for.\n\n- Wayland: Fix spurious key repeat events when some user defined callback takes\n  a long time to execute (:iss:`9224`)\n\n- When moving windows to a new tab/OS Window fix overlay windows not being\n  grouped with their parent windows (:iss:`9266`)\n\n- Linux: Fix a bug causing colors to occasionally all go black when using mesa\n  >= 25.3.0 with nouveau GPU driver (:iss:`9235`)\n\n- Fix :opt:`tab_bar_min_tabs` not respecting :opt:`tab_bar_filter` (:iss:`9278`)\n\n- macOS: Workaround for regression in Tahoe 26.2 that breaks :option:`kitty --detach`\n  (:iss:`9288`)\n\n- macOS: Workaround for yet another Tahoe regression causing macOS to start an\n  AutoFill helper process and not shut it down on application exit (:iss:`9299`)\n\n0.44.0 [2025-11-03]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow kitty to read a specified set of environment variables from your\n  login shell at startup using the :opt:`env` directive in kitty.conf\n  (:iss:`9042`)\n\n- A new option :opt:`draw_window_borders_for_single_window` to force kitty to\n  always draw a window border even when only a single window is present\n  (:pull:`9112`)\n\n- Fix a regression in 0.43.0 that caused a black flicker when closing a tab in\n  the presence of a background image (:iss:`9060`)\n\n- Further improvements to rounded corner rendering, especially at low DPI\n  (:pull:`9091`)\n\n- Splits layout: Fix a bug that could cause a corrupted layout in some\n  circumstances (:iss:`9059`)\n\n- Fix a regression in the previous release that broke ``goto_session -1``\n\n- Fix rendering broken on ancient GPU drivers that do not support rendering to 16 bit textures (:iss:`9068`)\n\n- Fix tab bar sometimes showing incorrect tabs when it is filtered to show only\n  tabs from the current session (:iss:`9079`)\n\n- macOS: Workaround for bug in macOS Tahoe that caused OS Windows that are\n  fullscreen to crash kitty when returning from sleep on some machines (:iss:`8983`)\n\n- Graphics: Fix animated images sometimes not auto playing or auto playing at the wrong start frame if the same image id is used for a subsequent image\n\n- Fix a regression in 0.43.0 that caused high CPU usage when :opt:`disable_ligatures` was set to ``cursor`` and the tab bar was visible (:iss:`9071`)\n\n- macOS: Handle dropping of file promises into kitty in addition to file paths (:pull:`9084`)\n\n- macOS: Fix indeterminate progress bar displayed on dock icon increasing speed when indeterminate progress is set without being cleared first (:iss:`9114`)\n\n- macOS: Performance and power usage improvements of about 5-10% (:pull:`9131`)\n\n- macOS: Add an item to the global menu to Cycle through OS windows\n\n- macOS: Quick access terminal: Fix a crash when changing font size (:iss:`9178`)\n\n- Wayland: Fix ``center-sized`` panels not working on smithay based compositors (:pull:`9117`)\n\n- Wayland: Fix scrolling using some mouse wheels that produce \"VALUE120\" based\n  scroll events too fast on some compositors (:pull:`9128`)\n\n- Add support for Unicode 17\n\n- Fix a regression in 0.43.0 that caused :opt:`tab_bar_margin_width` to be\n  doubled on the right edge of the tab bar (:iss:`9154`)\n\n- Session files: Add a new ``focus_tab`` command to specify which tab should be\n  active when a session is loaded. Accepts either a plain number (0-based index)\n  or a match expression for flexible tab selection, allowing sessions to preserve\n  the active tab state (:doc:`sessions`)\n\n- :ac:`save_as_session`: Add ``--base-dir`` option to specify a base directory\n  for saving session files with relative paths, useful when the current working\n  directory is not the desired location (:doc:`sessions`)\n\n- Add ``state:focused_os_window`` match query to select all windows in the\n  currently focused OS window (:ref:`search_syntax`)\n\n- Session saving now preserves visual tab order and active tab rather than tab\n  activation history as this is generally more important. In the future may\n  have it save tab history as well (:pull:`9163`)\n\n- The :sc:`reset_terminal` shortcut to reset the terminal now also resets termios state\n\n0.43.1 [2025-10-01]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- ssh kitten: Allow specifying a password and/or TOTP authentication secret to\n  automate interactive logins in scenarios where public key authentication is\n  not supported (:pull:`9020`)\n\n- macOS: Fix a bug where the color of a transparent titlebar was off when\n  running in the release build versus the build from source. Also fix using a\n  transparent titlebar causing the background opacity to be doubled.\n\n- Fix a regression in the previous release that caused the incorrect tab to be\n  active when loading a session (:iss:`9025`)\n\n- macOS: Workaround for bug in macOS Tahoe that caused closed OS Windows to\n  remain as invisible rectangles that intercept mouse events (:iss:`8952`)\n\n- macOS: Fix a regression in the previous release that broke automatic\n  switching of dark/light mode when setting :opt:`macos_titlebar_color` to an\n  arbitrary color (:iss:`9034`)\n\n- :ac:`goto_session`: Add ``--sort-by=alphabetical`` to have the interactive session\n  picker list the sessions in a fixed order rather than by most recent\n  (:disc:`9033`)\n\n- Fix a regression in the previous release that caused the cursor trail to not\n  be hidden properly (:iss:`9039`)\n\n- Session files: Fix a regression in the previous release that broke matching on\n  windows in the current tab (:iss:`9037`)\n\n- Fix a regression in the previous release that broke clearing screen lines\n  when in margin mode (:iss:`9049`)\n\n\n0.43.0 [2025-09-28]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- New support for creating and switching to :doc:`sessions` easily, allowing\n  users to define and use sessions/projects efficiently (:iss:`8911`)\n\n- Add a configurable :opt:`scrollbar` for the kitty scrollback (:pull:`8945`)\n\n- A new protocol for :doc:`multiple cursors <multiple-cursors-protocol>` in the terminal (:iss:`8927`)\n\n- macOS: Allow the window title bar to be semi-transparent when\n  :opt:`background_opacity` is less than one and :opt:`macos_titlebar_color` is\n  set to ``background`` (:pull:`8906`)\n\n- A new :opt:`cursor_trail_color` setting to independently control the color of\n  cursor trails (:pull:`8830`)\n\n- macOS: Add the default :kbd:`Cmd+L` mapping from Terminal.app to erase the\n  last command and its output (:disc:`6040`)\n\n- Fix :opt:`background_opacity` being non-linear especially with light color themes.\n  Note that this might require you to adjust the value of this setting to get\n  back your current look. (:iss:`8869`)\n\n- **backward incompatibility**: :opt:`background_opacity` no longer applies to\n  :opt:`background_image` instead add an alpha channel to the image itself\n\n- Add support for blinking text. Text marked as blinking now blinks in exact\n  rhythm with the cursor. The blinking animation and max duration are\n  controlled by :opt:`cursor_blink_interval` and\n  :opt:`cursor_stop_blinking_after`. (:pull:`8551`)\n\n- Allow using a custom python function to draw tab titles in the tab bar, see\n  :opt:`tab_title_template`\n\n- Wayland: Fix incorrect window size calculation when transitioning from\n  full screen to non-full screen with client side decorations (:iss:`8826`)\n\n- macOS: Fix hiding quick access terminal window not restoring focus to\n  previously active application (:disc:`8840`)\n\n- macOS: Fix showing the quick access terminal on a space other than the space\n  it was last active on, after full screening some application causes the quick\n  access terminal to appear on the old space (:iss:`8740`)\n\n- macOS: When toggling open the quick access terminal move it to the currently\n  active monitor (the monitor with the mouse pointer on it) (:iss:`9003`)\n\n- macOS: Fix closing an OS Window when another OS Window is minimized causing\n  the minimized window to be un-minimized (:iss:`8913`)\n\n- Allow using backspace to move the cursor onto the previous line in cooked\n  mode. This is indicated by the `bw` property in kitty's terminfo\n  (:iss:`8841`)\n\n- Watchers: A new event for global watchers corresponding to the tab bar being changed (:disc:`8842`)\n\n- Fix a regression in 0.40.0 that broke handling of the VS16 variation selector\n  when it caused a character to flow to the next line (:iss:`8848`)\n\n- Fix rendering of underlines when using larger text sizes with the space and\n  en-space characters (:iss:`8950`)\n\n- Fix updating panel configuration on visibility toggle and via remote control\n  not working (:iss:`8984`)\n\n- Improve rendering of rounded rectangles (:pull:`9000`)\n\n- Wayland: Update bundled copy of libwayland to 1.24 from 1.23.1 because the\n  just released mesa 25.2.0 breaks with libwayland < 1.24 (:iss:`8884`)\n\n- macOS: Pass the :kbd:`Cmd+C` shortcut to the application running in the\n  terminal when no text is selected (:pull:`8946`)\n\n- macOS: Workaround for bug in macOS Tahoe that caused OS Windows that are\n  fullscreen on a monitor that is disconnected while macOS is asleep to crash kitty (:iss:`8983`)\n\n\n0.42.2 [2025-07-16]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new :ref:`protocol extension <mouse_leave_window>` to notify terminal programs that have turned on SGR Pixel mouse reporting when the mouse leaves the window (:disc:`8808`)\n\n- clipboard kitten: Can now optionally take a password to avoid repeated\n  permission prompts when accessing the clipboard. Based on a\n  :ref:`protocol extension <clipboard_repeated_permission>`. (:iss:`8789`)\n\n- A new :option:`launch --hold-after-ssh` to not close a launched window\n  that connects directly to a remote host because of\n  :option:`launch --cwd`:code:`=current` when the connection ends (:pull:`8807`)\n\n- Fix :opt:`remember_window_position` not working because of a stupid typo (:iss:`8646`)\n\n- A new :option:`kitty --grab-keyboard` that can be used to grab the keyboard so that global shortcuts are sent to kitty instead\n\n- Remote control: Fix holding a remote control socket open causing the kitty I/O thread to go into a loop and not respond on other remote control sockets (:disc:`8670`)\n\n- hints kitten: Preserve line breaks when the hint is over a line break (:iss:`8674`)\n\n- Fix a segfault when using the :ac:`copy_ansi_to_clipboard` action (:iss:`8682`)\n\n- Fix a crash when using linear easing curves for animations (:iss:`8692`)\n\n- Graphics protocol: Add a note clarifying image update behavior on re-transmission (:iss:`8701`)\n\n- Wayland GNOME: Fix incorrect OS Window tracking because GNOME has started\n  activating windows on non-current workspaces (:iss:`8716`)\n\n- Fix a regression in 0.40.0 that broke rendering of VS15 variation selectors in some circumstances (:iss:`8731`, :iss:`8794`)\n\n- Fix a regression in 0.40.0 that broke serialization of tab characters as ANSI text (:iss:`8741`)\n\n- Fix a regression in 0.40.0 that broke erasing of characters in a line in the presence of wide characters (:iss:`8758`)\n\n- Fix a regression in 0.40.0 that broke hyperlinking of wide characters (:iss:`8796`)\n\n- Fix a regression that broke using :kbd:`esc` to exit visual select window mode (:iss:`8767`)\n\n- kitten run-shell: Fix SIGINT blocked when execing the shell (:iss:`8754`)\n\n0.42.1 [2025-05-17]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix ambiguous width and private use characters not being rendered when used with variable width text-sizing protocol escape codes\n\n- Quick access terminal: Restore focus to previously active window when hiding the quick access terminal window on macOS (:iss:`8627`)\n\n- Wayland: Fix an abort if the terminal program sets a window title longer than 2KB that contains CSI escape sequences and multibyte UTF-8 (:iss:`8619`)\n\n- Quick access terminal: Allow toggling the window to full screen using the standard kitty :sc:`toggle_fullscreen` shortcut (:iss:`8626`)\n\n- Quick access terminal: Allow configuring the monitor to display the panel on in Wayland/X11 (:iss:`8630`)\n\n- A new setting :opt:`remember_window_position` to optionally use the position of the last closed kitty OS Window as the position of the first kitty OS Window when running a new kitty instance (:pull:`8601`)\n\n- Panel kitten: A new ``center-sized`` value for :option:`--edge <kitty +kitten panel --edge>` to allow easily creating sized and centered panels\n\n- Wayland: The `kitty --name` flag now sets the XDG *window tag* on compositors\n  that support the `xdg-toplevel-tag <https://wayland.app/protocols/xdg-toplevel-tag-v1>`__ protocol.\n\n\n0.42.0 [2025-05-11]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new kitten: :doc:`quick-access-terminal </kittens/quick-access-terminal>` to :ref:`quake`\n\n- The :doc:`panel kitten </kittens/panel>` works on macOS and X11 as well as Wayland (:iss:`2590`)\n\n- **Behavior change**: Now kitty does full grapheme segmentation following the\n  Unicode 16 spec when splitting text into cells (:iss:`8533`)\n\n- **Behavior change**: The :ref:`automatic color switching functionality <auto_color_scheme>` now also controls background image settings (:iss:`8603`)\n\n- panel kitten: Allow using :option:`kitty +kitten panel --single-instance` to create multiple panels in one process (:iss:`8549`)\n\n- launch: Allow creating desktop panels such as those created by the :doc:`panel kitten </kittens/panel>` (:iss:`8549`)\n\n- Remote control: Allow modifying desktop panels and showing/hiding OS Windows\n  using the ``kitten @ resize-os-window`` command (:iss:`8550`)\n\n- Remote control launch: Allow waiting for a program launched in a new window\n  to exit and get the exit code via the `kitty +launch\n  --wait-for-child-to-exit` command line flag (:disc:`8573`)\n\n- Allow starting kitty with the OS window hidden via :option:`kitty --start-as=hidden <kitty --start-as>`, useful for single instance mode (:iss:`3466`)\n\n- Allow configuring the mouse unhide behavior when using :opt:`mouse_hide_wait` (:pull:`8508`)\n\n- diff kitten: Add half page and full page scroll vim-like bindings (:pull:`8514`)\n\n- diff kitten: Allow diffing named pipes (:iss:`8597`)\n\n- Fix a regression that caused automatic color themes to not be re-applied after config file reload (:iss:`8530`)\n\n- Wayland: When the compositor supports the `xdg-system-bell\n  <https://wayland.app/protocols/xdg-system-bell-v1>`__ protocol use it to play\n  the default bell sound\n\n- panel kitten: Allow specifying panel size in pixels in addition to cells\n\n- Fix a regression in 0.36.0 that caused using = with single letter command\n  line flags to no longer work correctly (:iss:`8556`)\n\n- Single instance: Preserve environment variables from invoking environment in\n  newly created window (:disc:`8567`)\n\n- Single instance: Reset OS Window class and name in new single instance OS\n  windows (:disc:`8567`)\n\n- macOS: Fix text color in visual window select ignoring the color theme (:iss:`8579`)\n\n- Launch action: Allow using an env var that resolves to a full command-line as the program to launch (:pull:`8613`)\n\n- :ac:`change_font_size` allow multiplying/dividing the current font size in addition to incrementing it (:iss:`8616`)\n\n- Box drawing: Improve appearance of rounder corners, giving them a uniform line width (:iss:`8299`)\n\n0.41.1 [2025-04-03]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix a regression in the previous release that caused rendering of emoji using\n  the VS16 variation selector to fail with some fonts (:iss:`8495`)\n\n- Fix a regression in 0.40.0 that caused tab bar margins to not be properly blanked when\n  the tab bar is at the bottom (:iss:`8494`)\n\n- Wayland: panel kitten: Fix incorrect initial font size on compositors such as Hyprland\n  that set scale late in the window creation process (:iss:`8496`)\n\n- Fix a regression in 0.40.1 that caused hyperlink underline on hover to remain\n  on screen when the screen is scrolled\n\n\n0.41.0 [2025-03-29]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new mode of operation for :opt:`text_fg_override_threshold` to override\n  foreground colors so as to maintain a minimum contrast between foreground and\n  background text colors. Works in a perceptual color space for best color accuracy\n  (:pull:`8420`)\n\n- A 15% improvement in throughput when processing text thanks to using a\n  multi-stage table for Unicode property lookups\n\n- :ref:`kitty +open <launch_actions>`: Ask for confirmation by default when running executables\n  to work around some badly designed programs that try to open links in\n  documents that point to executable files. Can be overridden by specifying\n  your own :file:`launch-actions.conf`.\n\n- Fix a regression in version 0.40.0 causing a crash when the underline\n  thickness of the font is zero (:iss:`8443`)\n\n- Fix a regression in version 0.40.0 causing a hang on resizing with a wide\n  character at the right edge of a line that needs to be moved onto the next\n  line (:iss:`8464`)\n\n- Fix a regression in 0.40.1 that caused copying to clipboard via OSC 52 from\n  applications that don't specify a destination in the escape code not working\n  (:iss:`8459`)\n\n- Wayland: Fix a regression in the previous release that caused crashes on\n  compositors that don't support the xdg-toplevel-icon protocol and the user has\n  set a custom kitty icon (:iss:`8471`)\n\n0.40.1 [2025-03-18]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Do not count background processes by default for :opt:`confirm_os_window_close` (:iss:`8358`)\n\n- A new option :opt:`clear_selection_on_clipboard_loss` to clear selections when they no longer reflect the contents of the clipboard\n\n- Fix a regression in the previous release that caused empty lines to be skipped when copying text from a selection (:iss:`8435`)\n\n- Fix flickering of hyperlink underline when client program continuously\n  redraws on mouse movement (:iss:`8414`)\n\n- Wayland: Allow overriding the kitty OS Window icon on compositors that implement the xdg-toplevel-icon protocol\n\n- macOS: When the program running in kitty reports progress information for a task, show a progress bar on the kitty dock icon\n\n- macOS: Fix a regression causing a crash when using :opt:`focus_follows_mouse` (:iss:`8437`)\n\n- OSC 52: Fix specifying both clipboard and primary in OSC 52 requests not supported\n\n0.40.0 [2025-03-08]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- :doc:`Allow terminal programs to use text in different font sizes <text-sizing-protocol>` (:iss:`8226`)\n\n- When rendering underlines add gaps around text descenders (parts of the text\n  that overlap with the underline). Controlled by the new option :opt:`underline_exclusion` (:iss:`8226`)\n\n- Finally fix the issue of text-width mismatches that has been plaguing the\n  terminal ecosystem for decades by allowing terminal programs to specify how\n  many cells to render a piece of text in (:iss:`8226`)\n\n- **Behavior change**: The :opt:`notify_on_cmd_finish` option now uses OS\n  Window visibility instead of focus state when set to ``invisible`` on\n  platforms that support querying OS window visibility (:iss:`8320`)\n\n- launch: Add options :option:`launch --source-window` and :option:`launch --next-to` to allow\n  specifying which window is used as the data source and destination location independently of the\n  currently active window (:iss:`8295`)\n\n- Linux: Add support for `COLRv1 <https://nabla.typearture.com/whatisCOLRV1.html>`__ fonts. These are typically emoji fonts that use vector images for emoji\n\n- Add support for the octant box-drawing characters\n\n- Speed up rendering of box drawing characters by moving the implementation to native code\n\n- When confirming if a window should be closed consider it active if it has running background processes (:iss:`8358`)\n\n- Remote control: `kitten @ scroll-window`: Allow scrolling to previous/next prompt\n\n- macOS: Fix fallback font rendering for bold/italic text not working for some symbols that are present in the Menlo regular face but not the bold/italic faces (:iss:`8282`)\n\n- XTGETTCAP: Fix response invalid for empty string capabilities (:pull:`8304`)\n\n- ssh kitten: Fix incorrect copying of data files when using the python interpreter and also fix incorrect hard link detection (:disc:`8308`)\n\n- Fix a regression in the previous release that broke setting of nullable colors\n\n- Fix a regression in 0.39.0 that caused a crash on invalid Unicode with a\n  large number of combining characters in a single cell (:iss:`8318`)\n\n- Fix ``--hold`` always restoring cursor to block shape instead of respecting the value of :opt:`cursor_shape` (:disc:`8344`)\n\n- When dragging in rectangle select mode use a crosshair mouse cursor configurable via :opt:`pointer_shape_when_dragging`\n\n- macOS: notify kitten: Fix waiting for result from desktop notification not working (:disc:`8379`)\n\n- Wayland: Fix mouse pointer position update not being sent when focus regained (:iss`8397`, :iss:`8398`)\n\n- Fix cursor blink animation when :opt:`background_opacity` is less than one (:iss:`8401`)\n\n- Wayland: panel kitten: Add a :code:`center` mode for creating panels to ease\n  creation of centered popups in Wayland (:pull:`8411`)\n\n\n0.39.1 [2025-02-01]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Splits layout: Allow setting the bias of the current split using ``layout_action bias`` (:iss:`8222`)\n\n- hints kitten: Workaround for some broken light color themes that make the hints text color too low contrast to read (:iss:`7330`)\n\n- Wayland niri: Fix 250ms delay on startup when using scale 1 (:iss:`8236`)\n\n- :ref:`Watchers <watchers>`: Add a new event ``on_color_scheme_preference_change``  (:iss:`8246`)\n\n\n0.39.0 [2025-01-16]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- :doc:`diff kitten <kittens/diff>`: Automatically use dark/light color scheme based on the color scheme of the parent terminal. Can be controlled via the new :opt:`kitten-diff.color_scheme` option. Note that this is a **default behavior change** (:iss:`8170`)\n\n- Allow dynamically generating configuration by running an arbitrary program using the new :code:`geninclude` directive in :file:`kitty.conf`\n\n- When a program running in kitty reports progress of a task display it as a percentage in the tab title. Controlled by the :opt:`tab_title_template` option\n\n- When mapping a custom kitten allow using shell escaping for the kitten path (:iss:`8178`)\n\n- Fix border colors not being changed by auto light/dark themes at startup (:iss:`8180`)\n\n- ssh kitten: Fix kitten not being on PATH when SSHing into Debian systems (:iss:`7160`)\n\n- diff kitten: Abort when run inside a terminal that does not support the kitty keyboard protocol (:iss:`8185`)\n\n- :doc:`query kitten <kittens/query_terminal>`: Add support for reporting name of the OS the terminal emulator is running on (:iss:`8201`)\n\n- macOS: Allow using the Passwords app to autofill passwords via the Edit->Autofill menu mimicking other macOS applications (:pull:`8195`)\n\n- macOS: Add menu items to the Edit menu to clear the screen and scrollback\n\n- Fix the :ac:`clear_terminal scrollback <clear_terminal>` action also clearing screen, not just the scrollback\n\n- When reloading configuration fix auto color themes not being re-applied (:iss:`8203`)\n\n0.38.1 [2024-12-26]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix a regression in the previous release that broke rendering of Emoji using the VS16 variation selector (:iss:`8130`)\n\n- When automatically changing colors based on OS color preference, first reset\n  all colors to default before applying the new theme so that even colors not\n  specified in the theme are correct (:iss:`8124`)\n\n- Graphics: Fix deleted but not freed images without any placements being incorrectly freed on a subsequent delete command (:disc:`8129`)\n\n- Graphics: Fix deletion of images by id not working for images with no placements (:disc:`8129`)\n\n- Add support for `escape code protocol <https://github.com/contour-terminal/contour/blob/master/docs/vt-extensions/color-palette-update-notifications.md>`__ for notifying applications on dark/light color scheme change\n\n- Cursor trails: Fix pure vertical movement sometimes not triggering a trail and holding down a key in nvim causing the trail to be glitchy (:pull:`8152`, :pull:`8153`)\n\n- macOS: Fix mouse cursor shape not always being reset to text cursor when mouse re-enters kitty (:iss:`8155`)\n\n- clone-in-kitty: Fix :envvar:`KITTY_WINDOW_ID` being cloned and thus having incorrect value (:iss:`8161`)\n\n\n0.38.0 [2024-12-15]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow :ref:`specifying individual color themes <auto_color_scheme>` to use so that kitty changes colors automatically following the OS dark/light mode\n\n- :opt:`notify_on_cmd_finish`: Automatically remove notifications when the window gains focus or the next notification is shown. Clearing behavior can be configured (:pull:`8100`)\n\n- Discard OSC 9 notifications that start with :code:`4;` because some misguided software is using it for \"progress reporting\" (:iss:`8011`)\n\n- Wayland GNOME: Workaround bug in mutter causing double tap on titlebar to not always work (:iss:`8054`)\n\n- clipboard kitten: Fix a bug causing kitten to hang in filter mode when input data size is not divisible by 3 and larger than 8KB (:iss:`8059`)\n\n- Wayland: Fix an abort when a client program tries to set an invalid title containing interleaved escape codes and UTF-8 multi-byte characters (:iss:`8067`)\n\n- Graphics protocol: Fix delete by number not deleting newest image with the specified number (:iss:`8071`)\n\n- Fix dashed and dotted underlines not being drawn at the same y position as straight underlines at all font sizes (:iss:`8074`)\n\n- panel kitten: Allow creating floating and on-top panels with arbitrary placement and size on Wayland (:pull:`8068`)\n\n- :opt:`remote_control_password`: Fix using a password without any actions not working (:iss:`8082`)\n\n- Fix enlarging window when a long line is wrapped between the first line of the scrollback buffer and the screen inserting a spurious newline (:iss:`7033`)\n\n- When re-attaching a detached tab preserve internal layout state such as biases and orientations (:iss:`8106`)\n\n- hints/unicode_input kittens: Do not lose keypresses that are sent very rapidly via an automation tool immediately after the kitten is launched (:iss:`7089`)\n\n\n0.37.0 [2024-10-30]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new optional :opt:`text cursor movement animation <cursor_trail>` that\n  shows a \"trail\" following the movement of the cursor making it easy to follow\n  large cursor jumps (:pull:`7970`)\n\n- Custom kittens: Add :ref:`a framework <kitten_main_rc>` for easily and securely using remote control from within a kitten's :code:`main()` function\n\n- kitten icat: Fix the :option:`kitty +kitten icat --no-trailing-newline` not working when using unicode placeholders (:iss:`7948`)\n\n- :opt:`tab_title_template` allow using the 256 terminal colors for formatting (:disc:`7976`)\n\n- Fix resizing window when alternate screen is active does not preserve trailing blank output line in the main screen (:iss:`7978`)\n\n- Wayland: Fix :opt:`background_opacity` less than one causing flicker on startup when the Wayland compositor supports single pixel buffers (:iss:`7987`)\n\n- Fix background image flashing when closing a tab (:iss:`7999`)\n\n- When running a kitten that modifies the kitty config file if no config file exists create a commented out default config file and then modify it (:iss:`7991`)\n\n0.36.4 [2024-09-27]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix a regression in the previous release that caused window padding to be rendered opaque even when :opt:`background_opacity` is less than 1 (:iss:`7895`)\n\n- Wayland GNOME: Fix a crash when using multiple monitors with different scales and starting on or moving to the monitor with lower scale (:iss:`7894`)\n\n- macOS: Fix a regression in the previous release that caused junk to be rendered in font previews in the choose fonts kitten and crash on Intel macs (:iss:`7892`)\n\n\n0.36.3 [2024-09-25]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- The option ``second_transparent_bg`` has been removed and replaced by :opt:`transparent_background_colors` which allows setting up to seven additional colors that will be transparent, with individual opacities per color (:iss:`7646`)\n\n- Fix a regression in the previous release that broke use of the ``cd`` command in session files (:iss:`7829`)\n\n- macOS: Fix shortcuts that become entries in the global menubar being reported as removed shortcuts in the debug output\n\n- macOS: Fix :opt:`macos_option_as_alt` not working when :kbd:`caps lock` is engaged (:iss:`7836`)\n\n- Fix a regression when tinting of background images was introduced that caused window borders to have :opt:`background_opacity` applied to them (:iss:`7850`)\n\n- Fix a regression that broke writing to the clipboard using the OSC 5522 protocol (:iss:`7858`)\n\n- macOS: Fix a regression in the previous release that caused kitty to fail to run after an unclean shutdown/crash when using --single-instance (:iss:`7846`)\n\n- kitten @ ls: Fix the ``--self`` flag not working (:iss:`7864`)\n\n- Remote control: Fix ``--match state:self`` not working (:disc:`7886`)\n\n- Splits layout: Allow setting the ``split_axis`` option to ``auto`` so that all new windows have their split axis chosen automatically unless explicitly specified in the launch command (:iss:`7887`)\n\n0.36.2 [2024-09-06]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Linux: Fix a regression in 0.36.0 that caused font features defined via fontconfig to be ignored (:iss:`7773`)\n\n- :ac:`goto_tab`: Allow numbers less than ``-1`` to go to the Nth previously active tab\n\n- Wayland: Fix for upcoming explicit sync changes in Wayland compositors breaking kitty (:iss:`7767`)\n\n- Remote control: When listening on a UNIX domain socket only allow connections from processes having the same user id (:pull:`7777`)\n\n- kitten @: Fix a regression connecting to TCP sockets using plain IP addresses rather than hostnames (:iss:`7794`)\n\n- diff kitten: Fix a regression that broke diffing against remote files (:iss:`7797`)\n\n0.36.1 [2024-08-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow specifying that the :opt:`cursor shape for unfocused windows <cursor_shape_unfocused>` should remain unchanged (:pull:`7728`)\n\n- MacOS Intel: Fix a crash in the choose-fonts kitten when displaying previews of variable fonts (:iss:`7734`)\n\n- Remote control: Fix a regression causing an escape code to leak when using @ launch with ``--no-response`` over the TTY (:iss:`7752`)\n\n- OSC 52: Fix a regression in the previous release that broke handling of invalid base64 encoded data in OSC 52 requests (:iss:`7757`)\n\n- macOS: Fix a regression in the previous release that caused :option:`kitty --single-instance` to not work when using :file:`macos-launch-services-cmdline`\n\n0.36.0 [2024-08-17]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Support `OpenType Variable fonts <https://en.wikipedia.org/wiki/Variable_font>`__ (:iss:`3711`)\n\n- A new :doc:`choose-fonts </kittens/choose-fonts>` kitten that provides a UI with font previews to ease selection of fonts. Also has support for font features and variable fonts\n\n- Allow animating the blinking of the cursor. See :opt:`cursor_blink_interval` for how to configure it\n\n- Add NERD fonts builtin so that users don't have to install them to use NERD symbols in kitty. The builtin font is used only if the symbols are not available in some system font\n\n- launch command: A new :option:`launch --bias` option to adjust the size of newly created windows declaratively (:iss:`7634`)\n\n- A new option :opt:`transparent_background_colors` to make a second background color semi-transparent via :opt:`background_opacity`. Useful for things like cursor line highlight in editors (:iss:`7646`)\n\n- A new :doc:`notify </kittens/notify>` kitten to show desktop notifications\n  from the command line with support for icons, buttons and more.\n\n- Desktop notifications protocol: Add support for icons, buttons, closing of notifications, expiry of notifications, updating of notifications and querying if the terminal emulator supports the protocol (:iss:`7657`, :iss:`7658`, :iss:`7659`)\n\n- A new option :opt:`filter_notification` to filter out or perform arbitrary actions on desktop notifications based on sophisticated criteria (:iss:`7670`)\n\n- A new protocol to allow terminal applications to change colors in the terminal more robustly than with the legacy XTerm protocol (:ref:`color_control`)\n\n- Sessions: A new command ``focus_matching_window`` to shift focus to a specific window, useful when creating complex layouts with splits (:disc:`7635`)\n\n- Speed up loading of large background images by caching the decoded image data. Also allow using images in JPEG/WEBP/TIFF/GIF/BMP formats in addition to PNG\n\n- Wayland: Allow fractional scales less than one (:pull:`7549`)\n\n- Wayland: Fix specifying the output name for the panel kitten not working (:iss:`7573`)\n\n- icat kitten: Add an option :option:`kitty +kitten icat --no-trailing-newline` to leave the cursor to the right of the image (:iss:`7574`)\n\n- Speed up ``kitty --version`` and ``kitty --single-instance`` (for all subsequent instances). They are now the fastest of all terminal emulators with similar functionality\n\n- macOS: Fix rendering of the unicode hyphen (U+2010) character when using a font that does not include a glyph for it (:iss:`7525`)\n\n- macOS 15: Handle Fn modifier when detecting global shortcuts (:iss:`7582`)\n\n- Dispatch any clicks waiting for :opt:`click_interval` on key events (:iss:`7601`)\n\n- ``kitten run-shell``: Automatically add the directory containing the kitten binary to PATH if needed. Controlled via the ``--inject-self-onto-path`` option (`disc`:7668`)\n\n- Wayland: Fix an issue with mouse selections not being stopped when there are multiple OS windows (:iss:`7381`)\n\n- Splits layout: Fix the ``move_to_screen_edge`` action breaking when only a single window is present (:iss:`7621`)\n\n- Add support for in-band window resize notifications (:iss:`7642`)\n\n- Allow controlling the easing curves used for :opt:`visual_bell_duration`\n\n- New special rendering for font symbols useful in drawing commit graphs (:pull:`7681`)\n\n- diff kitten: Add bindings to jump to next and previous file (:pull:`7683`)\n\n- Wayland GNOME: Fix the font size in the OS Window title bar changing with the size of the text in the window (:disc:`7677`)\n\n- Wayland GNOME: Fix a small rendering artifact when docking a window at a screen edge or maximizing it (:iss:`7701`)\n\n- When :opt:`shell` is set to ``.`` respect the SHELL environment variable in the environment in which kitty is launched (:pull:`7714`)\n\n- macOS: Bump the minimum required macOS version to Catalina released five years ago.\n\n- Fix a regression in :opt:`notify_on_cmd_finish` that caused notifications to appear for every command after the first (:iss:`7725`)\n\n\n0.35.2 [2024-06-22]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new option, :opt:`window_logo_scale` to specify how window logos are scaled with respect to the size of the window containing the logo (:pull:`7534`)\n\n- A new option, :opt:`cursor_shape_unfocused` to specify the shape of the text cursor in unfocused OS windows (:pull:`7544`)\n\n- Remote control: Fix empty password not working (:iss:`7538`)\n\n- Wayland: Fix regression in 0.34.0 causing flickering on window resize on NVIDIA drivers (:iss:`7493`)\n\n- Wayland labwc: Fix kitty timing out waiting for compositor to quit fucking around with scales on labwc (:iss:`7540`)\n\n- Fix ``scrollback_indicator_opacity`` not actually controlling the opacity (:iss:`7557`)\n\n- URL detection: Fix IPv6 hostnames breaking URL detection (:iss:`7565`)\n\n0.35.1 [2024-05-31]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Wayland: Fix a regression in 0.34 that caused the tab bar to not render in second and subsequent OS Windows under Hyprland (:iss:`7413`)\n\n- Fix a regression in the previous release that caused horizontal scrolling via touchpad in fullscreen applications to be reversed on non-Wayland platforms (:iss:`7475`, :iss:`7481`)\n\n- Fix a regression in the previous release causing an error when setting background_opacity to zero (:iss:`7483`)\n\n- Image display: Fix cursor movement and image hit region incorrect for image placements that specify only a number of rows or columns to display in (:iss:`7479`)\n\n\n0.35.0 [2024-05-25]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- kitten @ run: A new remote control command to run a process on the machine kitty is running on and get its output (:iss:`7429`)\n\n- :opt:`notify_on_cmd_finish`: Show the actual command that was finished (:iss:`7420`)\n\n- hints kitten: Allow clicking on matched text to select it in addition to typing the hint\n\n- Shell integration: Make the currently executing cmdline available as a window variable in kitty\n\n- :opt:`paste_actions`: Fix ``replace-newline`` not working with ``confirm`` (:iss:`7374`)\n\n- Graphics: Fix aspect ratio of images not being preserved when only a single\n  dimension of the destination rectangle is specified (:iss:`7380`)\n\n- :ac:`focus_visible_window`: Fix selecting with mouse click leaving keyboard in unusable state (:iss:`7390`)\n\n- Wayland: Fix infinite loop causing bad performance when using IME via fcitx5 due to a change in fcitx5 (:iss:`7396`)\n\n- Desktop notifications protocol: Add support for specifying urgency\n\n- Improve rendering of Unicode shade character to avoid Moire patterns (:pull:`7401`)\n\n- kitten @ send-key: Fix some keys being sent in kitty keyboard protocol encoding when not using socket for remote control\n\n- Dont clear selections on erase in screen commands unless the erased region intersects a selection (:iss:`7408`)\n\n- Wayland: save energy by not rendering \"suspended\" windows on compositors that support that\n\n- Allow more types of alignment for :opt:`placement_strategy` (:pull:`7419`)\n\n- Add some more box-drawing characters from the \"Geometric shapes\" Unicode block (:iss:`7433`)\n\n- Linux: Run all child processes in their own systemd scope to prevent the OOM killer from harvesting kitty when a child process misbehaves (:iss:`7427`)\n\n- Mouse reporting: Fix horizontal scroll events inverted (:iss:`7439`)\n\n- Remote control: @ action: Fix some actions being performed on the active window instead of the matched window (:iss:`7438`)\n\n- Scrolling with mouse wheel when a selection is active should update the selection (:iss:`7453`)\n\n- Fix kitten @ set-background-opacity limited to min opacity of 0.1 instead of 0 (:iss:`7463`)\n\n- launch --hold: Fix hold not working if kernel signals process group with SIGINT (:iss:`7466`)\n\n- macOS: Fix --start-as=fullscreen not working when another window is already fullscreen (:iss:`7448`)\n\n- Add option :option:`kitten @ detach-window --stay-in-tab` to keep focus in the currently active tab when moving windows (:iss:`7468`)\n\n- macOS: Fix changing window chrome/colors while in traditional fullscreen causing the titlebar to become visible (:iss:`7469`)\n\n0.34.1 [2024-04-19]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Wayland KDE: Fix window background blur not adapting when window is grown. Also fix turning it on and off not working. (:iss:`7351`)\n\n- Wayland GNOME: Draw the titlebar buttons without using a font (:iss:`7349`)\n\n- Fix a regression in the previous release that caused incorrect font selection when using variable fonts on Linux (:iss:`7361`)\n\n0.34.0 [2024-04-15]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Wayland: :doc:`panel kitten <kittens/panel>`: Add support for drawing desktop background and bars\n  using the panel kitten for all compositors that support the `requisite Wayland\n  protocol <https://wayland.app/protocols/wlr-layer-shell-unstable-v1>`__ which is practically speaking all of them but GNOME (:pull:`2590`)\n\n- Show a small scrollback indicator along the right window edge when viewing\n  the scrollback to keep track of scroll position (:iss:`2502`)\n\n- Wayland: Support fractional scales so that there is no wasted drawing at larger scale followed by resizing in the compositor\n\n- Wayland KDE: Support :opt:`background_blur`\n\n- Wayland GNOME: The window titlebar now has buttons to minimize/maximize/close the window\n\n- Wayland GNOME: The window titlebar color now follows the system light/dark color scheme preference, see :opt:`wayland_titlebar_color`\n\n- Wayland KDE: Fix mouse cursor hiding not working in Plasma 6 (:iss:`7265`)\n\n- Wayland IME: Fix a bug with handling synthetic keypresses generated by ZMK keyboard + fcitx (:pull:`7283`)\n\n- A new option :opt:`terminfo_type` to allow passing the terminfo database embedded into the :envvar:`TERMINFO` env var directly instead of via a file\n\n- Mouse reporting: Fix drag release event outside the window not being reported in legacy mouse reporting modes (:iss:`7244`)\n\n- macOS: Fix a regression in the previous release that broke rendering of some symbols on some systems (:iss:`7249`)\n\n- Fix handling of tab character when cursor is at end of line and wrapping is enabled (:iss:`7250`)\n\n- Splits layout: Fix :ac:`move_window_forward` not working (:iss:`7264`)\n\n- macOS: Fix an abort due to an assertion when a program tries to set an invalid window title (:iss:`7271`)\n\n- fish shell integration: Fix clicking at the prompt causing autosuggestions to be accepted, needs fish >= 3.8.0 (:iss:`7168`)\n\n- Linux: Fix for a regression in 0.32.0 that caused some CJK fonts to not render glyphs (:iss:`7263`)\n\n- Wayland: Support preferred integer scales\n\n- Wayland: A new option :opt:`wayland_enable_ime` to turn off Input Method Extensions which add latency and create bugs\n\n- Wayland: Fix :opt:`hide_window_decorations` not working on non GNOME desktops\n\n- When asking for quit confirmation because of a running program, mention the program name (:iss:`7331`)\n\n- Fix flickering of prompt during window resize (:iss:`7324`)\n\n0.33.1 [2024-03-21]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix a regression in the previous release that caused requesting data from the clipboard via OSC 52 to instead return data from the primary selection (:iss:`7213`)\n\n- Splits layout: Allow resizing until one of the halves in a split is minimally sized (:iss:`7220`)\n\n- macOS: Fix text rendered with fallback fonts not respecting bold/italic styling (:disc:`7241`)\n\n- macOS: When CoreText fails to find a fallback font for a character in the first Private Use Unicode Area, preferentially use the NERD font, if available, for it (:iss:`6043`)\n\n\n0.33.0 [2024-03-12]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- :ref:`Cheetah speed <throughput>` with a redesigned render loop and a 2x faster escape code\n  parser that uses SIMD CPU vector instruction to parse data in parallel\n  (:iss:`7005`)\n\n- A new benchmark kitten (``kitten __benchmark__``) to measure terminal\n  throughput performance\n\n- Graphics protocol: Add a new delete mode for deleting images whose ids fall within a range. Useful for bulk deletion (:iss:`7080`)\n\n- Keyboard protocol: Fix the :kbd:`Enter`, :kbd:`Tab` and :kbd:`Backspace` keys\n  generating spurious release events even when report all keys as escape codes\n  is not set (:iss:`7136`)\n\n- macOS: The command line args from :file:`macos-launch-services-cmdline` are now\n  prefixed to any args from ``open --args`` rather than overwriting them (:iss:`7135`)\n\n- Allow specifying where the new tab is created for :ac:`detach_window` (:pull:`7134`)\n\n- hints kitten: The option to set the text color for hints now allows arbitrary\n  colors (:pull:`7150`)\n\n- icat kitten: Add a command line argument to override terminal window size detection (:iss:`7165`)\n\n- A new action :ac:`toggle_tab` to easily switch to and back from a tab with a single shortcut (:iss:`7203`)\n\n- When :ac:`clearing terminal <clear_terminal>` add a new type ``to_cursor_scroll`` which can be\n  used to clear to prompt while moving cleared lines into the scrollback\n\n- Fix a performance bottleneck when dealing with thousands of small images\n  (:iss:`7080`)\n\n- kitten @ ls: Return the timestamp at which the window was created (:iss:`7178`)\n\n- hints kitten: Use default editor rather than hardcoding vim to open file at specific line (:iss:`7186`)\n\n- Remote control: Fix ``--match`` argument not working for @ls, @send-key,\n  @set-background-image (:iss:`7192`)\n\n- Keyboard protocol: Do not deliver a fake key release events on OS window focus out for engaged modifiers (:iss:`7196`)\n\n- Ignore :opt:`startup_session` when kitty is invoked with command line options specifying a command to run (:pull:`7198`)\n\n- Box drawing: Specialize rendering for the Fira Code progress bar/spinner glyphs\n\n0.32.2 [2024-02-12]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- kitten @ load-config: Allow (re)loading kitty.conf via remote control\n\n- Remote control: Allow running mappable actions via remote control (`kitten @ action`)\n\n- kitten @ send-text: Add a new option to automatically wrap the sent text in\n  bracketed paste escape codes if the program in the destination window has\n  turned on bracketed paste.\n\n- Fix a single key mapping not overriding a previously defined multi-key mapping\n\n- macOS: Fix :code:`kitten @ select-window` leaving the keyboard in a partially functional state (:iss:`7074`)\n\n- Graphics protocol: Improve display of images using Unicode placeholders or\n  row/column boxes by resizing them using linear instead of nearest neighbor\n  interpolation on the GPU (:iss:`7070`)\n\n- When matching URLs use the definition of legal characters in URLs from the\n  `WHATWG spec <https://url.spec.whatwg.org/#url-code-points>`__ rather than older standards (:iss:`7095`)\n\n- hints kitten: Respect the kitty :opt:`url_excluded_characters` option\n  (:iss:`7075`)\n\n- macOS: Fix an abort when changing OS window chrome for a full screen window via remote control or the themes kitten (:iss:`7106`)\n\n- Special case rendering of some more box drawing characters using shades from the block of symbols for legacy computing (:iss:`7110`)\n\n- A new action :ac:`close_other_os_windows` to close non active OS windows (:disc:`7113`)\n\n0.32.1 [2024-01-26]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix a regression in the previous release that broke overriding keyboard shortcuts for actions present in the global menu bar (:iss:`7016`)\n\n- Fix a regression in the previous release that caused multi-key sequences to not abort when pressing an unknown key (:iss:`7022`)\n\n- Fix a regression in the previous release that caused `kitten @ launch --cwd=current` to fail over SSH (:iss:`7028`)\n\n- Fix a regression in the previous release that caused `kitten @ send-text` with a match tab parameter to send text twice to the active window (:iss:`7027`)\n\n- Fix a regression in the previous release that caused overriding of existing multi-key mappings to fail (:iss:`7044`, :iss:`7058`)\n\n- Wayland+NVIDIA: Do not request an sRGB output buffer as a bug in Wayland causes kitty to not start (:iss:`7021`)\n\n0.32.0 [2024-01-19]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- :ref:`conditional_mappings`\n\n- Support for :ref:`modal_mappings` such as in modal editors like vim\n\n- A new option :opt:`notify_on_cmd_finish` to show a desktop notification when a long running command finishes (:pull:`6817`)\n\n- A new action :ac:`send_key` to simplify mapping key presses to other keys without needing :ac:`send_text`\n\n- Allow focusing previously active OS windows via :ac:`nth_os_window` (:pull:`7009`)\n\n- Wayland: Fix a regression in the previous release that broke copying to clipboard under wl-roots based compositors in some circumstances\n  (:iss:`6890`)\n\n- macOS: Fix some combining characters not being rendered (:iss:`6898`)\n\n- macOS: Fix returning from full screen via the button when the titlebar is hidden not hiding the buttons (:iss:`6883`)\n\n- macOS: Fix newly created OS windows not always appearing on the \"active\" monitor (:pull:`6932`)\n\n- Font fallback: Fix the font used to render a character sometimes dependent on the order in which characters appear on screen (:iss:`6865`)\n\n- panel kitten: Fix rendering with non-zero margin/padding in kitty.conf (:iss:`6923`)\n\n- kitty keyboard protocol: Specify the behavior of the modifier bits during modifier key events (:iss:`6913`)\n\n- Wayland: Enable support for the new cursor-shape protocol so that the mouse cursor is always rendered at the correct size in compositors that support this protocol (:iss:`6914`)\n\n- GNOME Wayland: Fix remembered window size smaller than actual size (:iss:`6946`)\n\n- Mouse reporting: Fix incorrect position reported for windows with padding (:iss:`6950`)\n\n- Fix :ac:`focus_visible_window` not switching to other window in stack layout\n  when only two windows are present (:iss:`6970`)\n\n\n0.31.0 [2023-11-08]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow :ac:`easily running arbitrarily complex remote control scripts <remote_control_script>` without needing to turn on remote control (:iss:`6712`)\n\n- A new option :opt:`menu_map` that allows adding entries to the global menubar on macOS (:disc:`6680`)\n\n- A new :doc:`escape code <pointer-shapes>` that can be used by programs running in the terminal to change the shape of the mouse pointer (:iss:`6711`)\n\n- Graphics protocol: Support for positioning :ref:`images relative to other images <relative_image_placement>` (:iss:`6400`)\n\n- A new option :opt:`single_window_padding_width` to use a different padding when only a single window is visible (:iss:`6734`)\n\n- A new mouse action ``mouse_selection word_and_line_from_point`` to select the current word under the mouse cursor and extend to end of line (:pull:`6663`)\n\n- A new option :opt:`underline_hyperlinks` to control when hyperlinks are underlined (:iss:`6766`)\n\n- Allow using the full range of standard mouse cursor shapes when customizing the mouse cursor\n\n- macOS: When running the default shell with the login program fix :file:`~/.hushlogin` not being respected when opening windows not in the home directory (:iss:`6689`)\n\n- macOS: Fix poor performance when using ligatures with some fonts, caused by slow harfbuzz shaping (:iss:`6743`)\n\n- :option:`kitten @ set-background-opacity --toggle` - a new flag to easily switch opacity between the specified value and the default (:iss:`6691`)\n\n- Fix a regression caused by rewrite of kittens to Go that made various kittens reset colors in a terminal when the colors were changed by escape code (:iss:`6708`)\n\n- Fix trailing bracket not ignored when detecting a multi-line URL with the trailing bracket as the first character on the last line (:iss:`6710`)\n\n- Fix the :option:`kitten @ launch --copy-env` option not copying current environment variables (:iss:`6724`)\n\n- Fix a regression that broke :program:`kitten update-self` (:iss:`6729`)\n\n- Two new event types for :ref:`watchers <watchers>`, :code:`on_title_change` and :code:`on_set_user_var`\n\n- When pasting, if the text contains terminal control codes ask the user for permission. See :opt:`paste_actions` for details. Thanks to David Leadbeater for discovering this.\n\n- Render Private Use Unicode symbols using two cells if the second cell contains an en-space as well as a normal space\n\n- macOS: Fix a regression in the previous release that caused kitten @ ls to not report the environment variables for the default shell (:iss:`6749`)\n\n- :doc:`Desktop notification protocol </desktop-notifications>`: Allow applications sending notifications to specify that the notification should only be displayed if the window is currently unfocused (:iss:`6755`)\n\n- :doc:`unicode_input kitten </kittens/unicode_input>`: Fix a regression that broke the \"Emoticons\" tab (:iss:`6760`)\n\n- Shell integration: Fix ``sudo --edit`` not working and also fix completions for sudo not working in zsh (:iss:`6754`, :iss:`6771`)\n\n- A new action :ac:`set_window_title` to interactively change the title of the active window\n\n- ssh kitten: Fix a regression that broken :kbd:`ctrl+space` mapping in zsh (:iss:`6780`)\n\n- Wayland: Fix primary selections not working with the river compositor (:iss:`6785`)\n\n\n0.30.1 [2023-10-05]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Shell integration: Automatically alias sudo to make the kitty terminfo files available in the sudo environment. Can be turned off via :opt:`shell_integration`\n\n- ssh kitten: Fix a regression in 0.28.0 that caused using ``--kitten`` to\n  override :file:`ssh.conf` not inheriting settings from :file:`ssh.conf`\n  (:iss:`6639`)\n\n- themes kitten: Allow absolute paths for ``--config-file-name`` (:iss:`6638`)\n\n- Expand environment variables in the :opt:`shell` option (:iss:`6511`)\n\n- macOS: When running the default shell, run it via the login program so that calls to ``getlogin()`` work (:iss:`6511`)\n\n- X11: Fix a crash on startup when the ibus service returns errors and the GLFW_IM_MODULE env var is set to ibus (:iss:`6650`)\n\n\n0.30.0 [2023-09-18]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new :doc:`transfer kitten </kittens/transfer>` that can be used to transfer files efficiently over the TTY device\n\n- ssh kitten: A new configuration directive :opt:`to automatically forward the kitty remote control socket <kitten-ssh.forward_remote_control>`\n\n- Allow :doc:`easily building kitty from source </build>` needing the installation of only C and Go compilers.\n  All other dependencies are automatically vendored\n\n- kitten @ set-user-vars: New remote control command to set user variables on a\n  window (:iss:`6502`)\n\n- kitten @ ls: Add user variables set on windows to the output (:iss:`6502`)\n\n- kitten @ ls: Allow limiting output to matched windows/tabs (:iss:`6520`)\n\n- kitten icat: Fix image being displayed one cell to the right when using both ``--place`` and ``--unicode-placeholder`` (:iss:`6556`)\n\n- kitten run-shell: Make kitty terminfo database available if needed before starting the shell\n\n- macOS: Fix keyboard shortcuts in the Apple global menubar not being changed when reloading the config\n\n- Fix a crash when resizing an OS Window that is displaying more than one image and the new size is smaller than the image needs (:iss:`6555`)\n\n- Remote control: Allow using a random TCP port as the remote control socket and also allow using TCP sockets in :opt:`listen_on`\n\n- unicode_input kitten: Add an option to specify the startup tab (:iss:`6552`)\n\n- X11: Print an error to :file:`STDERR` instead of refusing to start when the user sets a custom window icon larger than 128x128 (:iss:`6507`)\n\n- Remote control: Allow matching by neighbor of active window. Useful for navigation plugins like vim-kitty-navigator\n\n- Fix a regression that caused changing :opt:`text_fg_override_threshold` or :opt:`text_composition_strategy` via config reload causing incorrect rendering (:iss:`6559`)\n\n- When running a shell for ``--hold`` set the env variable ``KITTY_HOLD=1`` to allow users to customize what happens (:disc:`6587`)\n\n- When multiple confirmable close requests are made focus the existing close confirmation window instead of opening a new one for each request (:iss:`6601`)\n\n- Config file format: allow splitting lines by starting subsequent lines with a backslash (:pull:`6603`)\n\n- ssh kitten: Fix a regression causing hostname directives in :file:`ssh.conf` not matching when username is specified (:disc:`6609`)\n\n- diff kitten: Add support for files that are identical apart from mode changes (:iss:`6611`)\n\n- Wayland: Do not request idle inhibition for full screen windows (:iss:`6613`)\n\n- Adjust the workaround for non-linear blending of transparent pixels in\n  compositors to hopefully further reduce fringing around text with certain\n  color issues (:iss:`6534`)\n\n\n0.29.2 [2023-07-27]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix a performance regression on M1 machines using outdated macOS versions (:iss:`6479`)\n\n- macOS: Disable OS window shadows for transparent windows as they cause rendering artifacts due to Cocoa bugs (:iss:`6439`)\n\n- Detect .tex and Makefiles as plain text files (:iss:`6492`)\n\n- unicode_input kitten: Fix scrolling over multiple screens not working (:iss:`6497`)\n\n0.29.1 [2023-07-17]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new value for :opt:`background_image_layout` to scale the background image while preserving its aspect ratio. Also have centered images work even for images larger than the window size (:pull:`6458`)\n\n- Fix a regression that caused using unicode placeholders to display images to break and also partially offscreen images to sometimes be slightly distorted (:iss:`6467`)\n\n- macOS: Fix a regression that caused rendering to hang when transitioning to full screen with :opt:`macos_colorspace` set to ``default`` (:iss:`6435`)\n\n- macOS: Fix a regression causing *burn-in* of text when resizing semi-transparent OS windows (:iss:`6439`)\n\n- macOS: Add a new value ``titlebar-and-corners`` for :opt:`hide_window_decorations` that emulates the behavior of ``hide_window_decorations yes`` in older versions of kitty\n\n- macOS: Fix a regression in the previous release that caused :opt:`hide_window_decorations` = ``yes`` to prevent window from being resizable (:iss:`6436`)\n\n- macOS: Fix a regression that caused the titlebar to be translucent even for non-translucent windows (:iss:`6450`)\n\n- GNOME: Fix :opt:`wayland_titlebar_color` not being applied until the color is changed at least once (:iss:`6447`)\n\n- Remote control launch: Fix ``--env`` not implemented when using ``--cwd=current`` with the SSH kitten (:iss:`6438`)\n\n- Allow using a custom OS window icon on X11 as well as macOS (:pull:`6475`)\n\n0.29.0 [2023-07-10]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new escape code ``<ESC>[22J`` that moves the current contents of the screen into the scrollback before clearing it\n\n- A new kitten :ref:`run-shell <run_shell>` to allow creating sub-shells with shell integration enabled\n\n- A new option :opt:`background_blur` to blur the background for transparent windows (:pull:`6135`)\n\n- The :option:`--hold` flag now holds the window open at a shell prompt instead of asking the user to press a key\n\n- A new option :opt:`text_fg_override_threshold` to force text colors to have high contrast regardless of color scheme (:pull:`6283`)\n\n- When resizing OS Windows make the animation less jerky. Also show the window size in cells during the resize (:iss:`6341`)\n\n- unicode_input kitten: Fix a regression in 0.28.0 that caused the order of recent and favorites entries to not be respected (:iss:`6214`)\n\n- unicode_input kitten: Fix a regression in 0.28.0 that caused editing of favorites to sometimes hang\n\n- clipboard kitten: Fix a bug causing the last MIME type available on the clipboard not being recognized when pasting\n\n- clipboard kitten: Dont set clipboard when getting clipboard in filter mode (:iss:`6302`)\n\n- Fix regression in 0.28.0 causing color fringing when rendering in transparent windows on light backgrounds (:iss:`6209`)\n\n- show_key kitten: In kitty mode show the actual bytes sent by the terminal rather than a re-encoding of the parsed key event\n\n- hints kitten: Fix a regression in 0.28.0 that broke using sub-groups in regexp captures (:iss:`6228`)\n\n- hints kitten: Fix a regression in 0.28.0 that broke using lookahead/lookbehind in regexp captures (:iss:`6265`)\n\n- diff kitten: Fix a regression in 0.28.0 that broke using relative paths as arguments to the kitten (:iss:`6325`)\n\n- Fix re-using the image id of an animated image for a still image causing a crash (:iss:`6244`)\n\n- kitty +open: Ask for permission before executing script files that are not marked as executable. This prevents accidental execution\n  of script files via MIME type association from programs that unconditionally \"open\" attachments/downloaded files\n\n- edit-in-kitty: Fix running edit-in-kitty with elevated privileges to edit a restricted file not working (:disc:`6245`)\n\n- ssh kitten: Fix a regression in 0.28.0 that caused interrupt during setup to not be handled gracefully (:iss:`6254`)\n\n- ssh kitten: Allow configuring the ssh kitten to skip some hosts via a new ``delegate`` config directive\n\n- Graphics: Move images up along with text when the window is shrunk vertically (:iss:`6278`)\n\n- Fix a regression in 0.28.0 that caused a buffer overflow when clearing the screen (:iss:`6306`, :pull:`6308`)\n\n- Fix a regression in 0.27.0 that broke setting of specific edge padding/margin via remote control (:iss:`6333`)\n\n- macOS: Fix window shadows not being drawn for transparent windows (:iss:`2827`, :pull:`6416`)\n\n- Do not echo invalid DECRQSS queries back, behavior inherited from xterm (CVE-2008-2383). Similarly, fix an echo\n  bug in the file transfer protocol due to insufficient sanitization of safe strings.\n\n\n0.28.1 [2023-04-21]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix a regression in the previous release that broke the remote file kitten (:iss:`6186`)\n\n- Fix a regression in the previous release that broke handling of some keyboard shortcuts in some kittens on some keyboard layouts (:iss:`6189`)\n\n- Fix a regression in the previous release that broke usage of custom themes (:iss:`6191`)\n\n0.28.0 [2023-04-15]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- **Text rendering change**: Use sRGB correct linear gamma blending for nicer font\n  rendering and better color accuracy with transparent windows.\n  See the option :opt:`text_composition_strategy` for details.\n  The obsolete :opt:`macos_thicken_font` will make the font too thick and needs to be removed manually\n  if it is configured. (:pull:`5969`)\n\n- icat kitten: Support display of images inside tmux >= 3.3 (:pull:`5664`)\n\n- Graphics protocol: Add support for displaying images inside programs that do not support the protocol such as vim and tmux (:pull:`5664`)\n\n- diff kitten: Add support for selecting multi-line text with the mouse\n\n- Fix a regression in 0.27.0 that broke ``kitty @ set-font-size 0`` (:iss:`5992`)\n\n- launch: When using ``--cwd=current`` for a remote system support running non shell commands as well (:disc:`5987`)\n\n- When changing the cursor color via escape codes or remote control to a fixed color, do not reset cursor_text_color (:iss:`5994`)\n\n- Input Method Extensions: Fix incorrect rendering of IME in-progress and committed text in some situations (:pull:`6049`, :pull:`6087`)\n\n- Linux: Reduce minimum required OpenGL version from 3.3 to 3.1 + extensions (:iss:`2790`)\n\n- Fix a regression that broke drawing of images below cell backgrounds (:iss:`6061`)\n\n- macOS: Fix the window buttons not being hidden after exiting the traditional full screen (:iss:`6009`)\n\n- When reloading configuration, also reload custom MIME types from :file:`mime.types` config file (:pull:`6012`)\n\n- launch: Allow specifying the state (full screen/maximized/minimized) for newly created OS Windows (:iss:`6026`)\n\n- Sessions: Allow specifying the OS window state via the ``os_window_state`` directive (:iss:`5863`)\n\n- macOS: Display the newly created OS window in specified state to avoid or reduce the window transition animations (:pull:`6035`)\n\n- macOS: Fix the maximized window not taking up full space when the title bar is hidden or when :opt:`resize_in_steps` is configured (:iss:`6021`)\n\n- Linux: A new option :opt:`linux_bell_theme` to control which sound theme is used for the bell sound (:pull:`4858`)\n\n- ssh kitten: Change the syntax of glob patterns slightly to match common usage\n  elsewhere. Now the syntax is the same as \"extendedglob\" in most shells.\n\n- hints kitten: Allow copying matches to named buffers (:disc:`6073`)\n\n- Fix overlay windows not inheriting the per-window padding and margin settings\n  of their parents (:iss:`6063`)\n\n- Wayland KDE: Fix selecting in un-focused OS window not working correctly (:iss:`6095`)\n\n- Linux X11: Fix a crash if the X server requests clipboard data after we have relinquished the clipboard (:iss:`5650`)\n\n- Allow stopping of URL detection at newlines via :opt:`url_excluded_characters` (:iss:`6122`)\n\n- Linux Wayland: Fix animated images not being animated continuously (:iss:`6126`)\n\n- Keyboard input: Fix text not being reported as unicode codepoints for multi-byte characters in the kitty keyboard protocol (:iss:`6167`)\n\n\n0.27.1 [2023-02-07]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix :opt:`modify_font` not working for strikethrough position (:iss:`5946`)\n\n- Fix a regression causing the ``edit-in-kitty`` command not working if :file:`kitten` is not added\n  to PATH (:iss:`5956`)\n\n- icat kitten: Fix a regression that broke display of animated GIFs over SSH (:iss:`5958`)\n\n- Wayland GNOME: Fix for ibus not working when using XWayland (:iss:`5967`)\n\n- Fix regression in previous release that caused incorrect entries in terminfo for modifier+F3 key combinations (:pull:`5970`)\n\n- Bring back the deprecated and removed ``kitty +complete`` and delegate it to :program:`kitten` for backward compatibility (:pull:`5977`)\n\n- Bump the version of Go needed to build kitty to ``1.20`` so we can use the Go stdlib ecdh package for crypto.\n\n\n0.27.0 [2023-01-31]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new statically compiled, standalone executable, ``kitten`` (written in Go)\n  that can be used on all UNIX-like servers for remote control (``kitten @``),\n  viewing images (``kitten icat``), manipulating the clipboard (``kitten clipboard``), etc.\n\n- :doc:`clipboard kitten </kittens/clipboard>`: Allow copying arbitrary data types to/from the clipboard, not just plain text\n\n- Speed up the ``kitty @`` executable by ~10x reducing the time for typical\n  remote control commands from ~50ms to ~5ms\n\n- icat kitten: Speed up by using POSIX shared memory when possible to transfer\n  image data to the terminal. Also support common image formats\n  GIF/PNG/JPEG/WEBP/TIFF/BMP out of the box without needing ImageMagick.\n\n- Option :opt:`show_hyperlink_targets` to show the target of terminal hyperlinks when hovering over them with the mouse (:pull:`5830`)\n\n- Keyboard protocol: Remove ``CSI R`` from the allowed encodings of the :kbd:`F3` key as it conflicts with the *Cursor Position Report* escape code (:disc:`5813`)\n\n- Allow using the cwd of the original process for :option:`launch --cwd` (:iss:`5672`)\n\n- Session files: Expand environment variables (:disc:`5917`)\n\n- Pass key events mapped to scroll actions to the program running in the terminal when the terminal is in alternate screen mode (:iss:`5839`)\n\n- Implement :ref:`edit-in-kitty <edit_file>` using the new ``kitten`` static executable (:iss:`5546`, :iss:`5630`)\n\n- Add an option :opt:`background_tint_gaps` to control background image tinting for window gaps (:iss:`5596`)\n\n- A new option :opt:`undercurl_style` to control the rendering of undercurls (:pull:`5883`)\n\n- Bash integration: Fix ``clone-in-kitty`` not working on bash >= 5.2 if environment variable values contain newlines or other special characters (:iss:`5629`)\n\n- A new :ac:`sleep` action useful in combine based mappings to make kitty sleep before executing the next action\n\n- Wayland GNOME: Workaround for latest mutter release breaking full screen for semi-transparent kitty windows (:iss:`5677`)\n\n- A new option :opt:`tab_title_max_length` to limit the length of tab (:iss:`5718`)\n\n- When drawing the tab bar have the default left and right margins drawn in a color matching the neighboring tab (:iss:`5719`)\n\n- When using the :code:`include` directive in :file:`kitty.conf` make the environment variable :envvar:`KITTY_OS` available for OS specific config\n\n- Wayland: Fix signal handling not working with some GPU drivers (:iss:`4636`)\n\n- Remote control: When matching windows allow using negative id numbers to match recently created windows (:iss:`5753`)\n\n- ZSH Integration: Bind :kbd:`alt+left` and :kbd:`alt+right` to move by word if not already bound. This mimics the default bindings in Terminal.app (:iss:`5793`)\n\n- macOS: Allow to customize :sc:`Hide <hide_macos_app>`, :sc:`Hide Others <hide_macos_other_apps>`, :sc:`Minimize <minimize_macos_window>`, and :sc:`Quit <quit>` global menu shortcuts. Note that :opt:`clear_all_shortcuts` will remove these shortcuts now (:iss:`948`)\n\n- When a multi-key sequence does not match any action, send all key events to the child program (:pull:`5841`)\n\n- broadcast kitten: Allow pressing a key to stop echoing of input into the broadcast window itself (:disc:`5868`)\n\n- When reporting unused activity in a window, ignore activity that occurs soon after a window resize (:iss:`5881`)\n\n- Fix using :opt:`cursor` = ``none`` not working on text that has reverse video (:iss:`5897`)\n\n- Fix ssh kitten not working on FreeBSD (:iss:`5928`)\n\n- macOS: Export kitty selected text to the system for use with services that accept it (patch by Sertaç Ö. Yıldız)\n\n\n0.26.5 [2022-11-07]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Splits layout: Add a new mappable action to move the active window to the screen edge (:iss:`5643`)\n\n- ssh kitten: Allow using absolute paths for the location of transferred data (:iss:`5607`)\n\n- Fix a regression in the previous release that caused a ``resize_draw_strategy`` of ``static`` to not work (:iss:`5601`)\n\n- Wayland KDE: Fix abort when pasting into Firefox (:iss:`5603`)\n\n- Wayland GNOME: Fix ghosting when using :opt:`background_tint` (:iss:`5605`)\n\n- Fix cursor position at x=0 changing to x=1 on resize (:iss:`5635`)\n\n- Wayland GNOME: Fix incorrect window size in some circumstances when switching between windows with window decorations disabled (:iss:`4802`)\n\n- Wayland: Fix high CPU usage when using some input methods (:pull:`5369`)\n\n- Remote control: When matching window by `state:focused` and no window currently has keyboard focus, match the window belonging to the OS window that was last focused (:iss:`5602`)\n\n\n0.26.4 [2022-10-17]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Allow changing the kitty icon by placing a custom icon in the kitty config folder (:pull:`5464`)\n\n- Allow centering the :opt:`background_image` (:iss:`5525`)\n\n- X11: Fix a regression in the previous release that caused pasting from GTK based applications to have extra newlines (:iss:`5528`)\n\n- Tab bar: Improve empty space management when some tabs have short titles, allocate the saved space to the active tab (:iss:`5548`)\n\n- Fix :opt:`background_tint` not applying to window margins and padding (:iss:`3933`)\n\n- Wayland: Fix background image scaling using tiled mode on high DPI screens\n\n- Wayland: Fix an abort when changing background colors with :opt:`wayland_titlebar_color` set to ``background`` (:iss:`5562`)\n\n- Update to Unicode 15.0 (:pull:`5542`)\n\n- GNOME Wayland: Fix a memory leak in gnome-shell when using client side decorations\n\n\n0.26.3 [2022-09-22]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Wayland: Mark windows in which a bell occurs as urgent on compositors that support the xdg-activation protocol\n\n- Allow passing null bytes through the system clipboard (:iss:`5483`)\n\n- ssh kitten: Fix :envvar:`KITTY_PUBLIC_KEY` not being encoded properly when transmitting (:iss:`5496`)\n\n- Sessions: Allow controlling which OS Window is active via the ``focus_os_window`` directive\n\n- Wayland: Fix for bug in NVIDIA drivers that prevents transparency working (:iss:`5479`)\n\n- Wayland: Fix for a bug that could cause kitty to become non-responsive when\n  using multiple OS windows in a single instance on some compositors (:iss:`5495`)\n\n- Wayland: Fix for a bug preventing kitty from starting on Hyprland when using a non-unit scale (:iss:`5467`)\n\n- Wayland: Generate a XDG_ACTIVATION_TOKEN when opening URLs or running programs in the background via the launch action\n\n- Fix a regression that caused kitty not to restore SIGPIPE after python nukes it when launching children. Affects bash which does not sanitize its signal mask. (:iss:`5500`)\n\n- Fix a use-after-free when handling fake mouse clicks and the action causes windows to be removed/re-allocated (:iss:`5506`)\n\n\n0.26.2 [2022-09-05]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow creating :code:`overlay-main` windows, which are treated as the active window unlike normal overlays (:iss:`5392`)\n\n- hints kitten: Allow using :doc:`launch` as the program to run, to open the result in a new kitty tab/window/etc. (:iss:`5462`)\n\n- hyperlinked_grep kitten: Allow control over which parts of ``rg`` output are hyperlinked (:pull:`5428`)\n\n- Fix regression in 0.26.0 that caused launching kitty without working STDIO handles to result in high CPU usage and prewarming failing (:iss:`5444`)\n\n- :doc:`/launch`: Allow setting the margin and padding for newly created windows (:iss:`5463`)\n\n- macOS: Fix regression in 0.26.0 that caused asking the user for a line of input such as for :ac:`set_tab_title` to not work (:iss:`5447`)\n\n- hints kitten: hyperlink matching: Fix hints occasionally matching text on subsequent line as part of hyperlink (:pull:`5450`)\n\n- Fix a regression in 0.26.0 that broke mapping of native keys whose key codes did not fit in 21 bits (:iss:`5452`)\n\n- Wayland: Fix remembering window size not accurate when client side decorations are present\n\n- Fix an issue where notification identifiers were not sanitized leading to\n  code execution if the user clicked on a notification popup from a malicious\n  source. Thanks to Carter Sande for discovering this vulnerability.\n\n\n0.26.1 [2022-08-30]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- ssh kitten: Fix executable permission missing from kitty bootstrap script (:iss:`5438`)\n\n- Fix a regression in 0.26.0 that caused kitty to no longer set the ``LANG`` environment variable on macOS (:iss:`5439`)\n\n- Allow specifying a title when using the :ac:`set_tab_title` action (:iss:`5441`)\n\n\n0.26.0 [2022-08-29]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new option :opt:`remote_control_password` to use fine grained permissions for what can be remote controlled (:disc:`5320`)\n\n- Reduce startup latency by ~30 milliseconds when running kittens via key bindings inside kitty (:iss:`5159`)\n\n- A new option :opt:`modify_font` to adjust various font metrics like underlines, cell sizes etc. (:pull:`5265`)\n\n- A new shortcut :sc:`show_kitty_doc` to display the kitty docs in a browser\n\n- Graphics protocol: Only delete temp files if they have the string\n  :code:`tty-graphics-protocol` in their file paths. This prevents deletion of arbitrary files in :file:`/tmp`.\n\n- Deprecate the ``adjust_baseline``, ``adjust_line_height`` and ``adjust_column_width`` options in favor of :opt:`modify_font`\n\n- Wayland: Fix a regression in the previous release that caused mouse cursor\n  animation and keyboard repeat to stop working when switching seats (:iss:`5188`)\n\n- Allow resizing windows created in session files (:pull:`5196`)\n\n- Fix horizontal wheel events not being reported to client programs when they grab the mouse (:iss:`2819`)\n\n- macOS: Remote control: Fix unable to launch a new OS window or background process when there is no OS window (:iss:`5210`)\n\n- macOS: Fix unable to open new tab or new window when there is no OS window (:iss:`5276`)\n\n- kitty @ set-colors: Fix changing inactive_tab_foreground not working (:iss:`5214`)\n\n- macOS: Fix a regression that caused switching keyboard input using Eisu and\n  Kana keys not working (:iss:`5232`)\n\n- Add a mappable action to toggle the mirrored setting for the tall and fat\n  layouts (:pull:`5344`)\n\n- Add a mappable action to switch between predefined bias values for the tall and fat\n  layouts (:pull:`5352`)\n\n- Wayland: Reduce flicker at startup by not using render frames immediately after a resize (:iss:`5235`)\n\n- Linux: Update cursor position after all key presses not just pre-edit text\n  changes (:iss:`5241`)\n\n- ssh kitten: Allow ssh kitten to work from inside tmux, provided the tmux\n  session inherits the correct KITTY env vars (:iss:`5227`)\n\n- ssh kitten: A new option :code:`--symlink-strategy` to control how symlinks\n  are copied to the remote machine (:iss:`5249`)\n\n- ssh kitten: Allow pressing :kbd:`Ctrl+C` to abort ssh before the connection is\n  completed (:iss:`5271`)\n\n- Bash integration: Fix declare not creating global variables in .bashrc (:iss:`5254`)\n\n- Bash integration: Fix the inherit_errexit option being set by shell integration (:iss:`5349`)\n\n- :command:`kitty @ scroll-window` allow scrolling by fractions of a screen\n  (:iss:`5294`)\n\n- remote files kitten: Fix working with files whose names have characters that\n  need to be quoted in shell scripts (:iss:`5313`)\n\n- Expand ~ in paths configured in :opt:`editor` and :opt:`exe_search_path` (:disc:`5298`)\n\n- Allow showing the working directory of the active window in tab titles\n  (:pull:`5314`)\n\n- ssh kitten: Allow completion of ssh options between the destination and command (:iss:`5322`)\n\n- macOS: Fix speaking selected text not working (:iss:`5357`)\n\n- Allow ignoring failure to close windows/tabs via rc commands (:disc:`5406`)\n\n- Fix hyperlinks not present when fetching text from the history buffer\n  (:iss:`5427`)\n\n\n0.25.2 [2022-06-07]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new command :command:`edit-in-kitty` to :ref:`edit_file`\n\n- Allow getting the last non-empty command output easily via an action or\n  remote control (:pull:`4973`)\n\n- Fix a bug that caused :opt:`macos_colorspace` to always be ``default`` regardless of its actual value (:iss:`5129`)\n\n- diff kitten: A new option :opt:`kitten-diff.ignore_name` to exclude files and directories from being scanned (:pull:`5171`)\n\n- ssh kitten: Fix bash not being executed as a login shell since kitty 0.25.0 (:iss:`5130`)\n\n- macOS: When pasting text and the clipboard has a filesystem path, paste the\n  full path instead of the text, which is sometimes just the file name (:pull:`5142`)\n\n- macOS: Allow opening executables without a file extension with kitty as well\n  (:iss:`5160`)\n\n- Themes kitten: Add a tab to show user defined custom color themes separately\n  (:pull:`5150`)\n\n- Iosevka: Fix incorrect rendering when there is a combining char that does not\n  group with its neighbors (:iss:`5153`)\n\n- Weston: Fix client side decorations flickering on slow computers during\n  window resize (:iss:`5162`)\n\n- Remote control: Fix commands with large or asynchronous payloads like\n  :command:`kitty @ set-backround-image`, :command:`kitty @ set-window-logo`\n  and :command:`kitty @ select-window` not working correctly\n  when using a socket (:iss:`5165`)\n\n- hints kitten: Fix surrounding quotes/brackets and embedded carriage returns\n  not being removed when using line number processing (:iss:`5170`)\n\n\n0.25.1 [2022-05-26]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Shell integration: Add a command to :ref:`clone_shell`\n\n- Remote control: Allow using :ref:`Boolean operators <search_syntax>` when constructing queries to match windows or tabs\n\n- Sessions: Fix :code:`os_window_size` and :code:`os_window_class` not applying to the first OS Window (:iss:`4957`)\n\n- Allow using the cwd of the oldest as well as the newest foreground process for :option:`launch --cwd` (:disc:`4869`)\n\n- Bash integration: Fix the value of :opt:`shell_integration` not taking effect if the integration script is sourced in bashrc (:pull:`4964`)\n\n- Fix a regression in the previous release that caused mouse move events to be incorrectly reported as drag events even when a button is not pressed (:iss:`4992`)\n\n- remote file kitten: Integrate with the ssh kitten for improved performance\n  and robustness. Re-uses the control master connection of the ssh kitten to\n  avoid round-trip latency.\n\n- Fix tab selection when closing a new tab not correct in some scenarios (:iss:`4987`)\n\n- A new action :ac:`open_url` to open the specified URL (:pull:`5004`)\n\n- A new option :opt:`select_by_word_characters_forward` that allows changing\n  which characters are considered part of a word to the right when double clicking to select\n  words (:pull:`5103`)\n\n- macOS: Make the global menu shortcut to open kitty website configurable (:pull:`5004`)\n\n- macOS: Add the :opt:`macos_colorspace` option to control what color space colors are rendered in (:iss:`4686`)\n\n- Fix reloading of config not working when :file:`kitty.conf` does not exist when kitty is launched (:iss:`5071`)\n\n- Fix deleting images by row not calculating image bounds correctly (:iss:`5081`)\n\n- Increase the max number of combining chars per cell from two to three, without increasing memory usage.\n\n- Linux: Load libfontconfig at runtime to allow the binaries to work for\n  running kittens on servers without FontConfig\n\n- GNOME: Fix for high CPU usage caused by GNOME's text input subsystem going\n  into an infinite loop when IME cursor position is updated after a done event\n  (:iss:`5105`)\n\n\n0.25.0 [2022-04-11]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- :doc:`kittens/ssh`: automatic shell integration when using SSH. Easily\n  clone local shell and editor configuration on remote machines, and automatic\n  re-use of existing connections to avoid connection setup latency.\n\n- When pasting URLs at shell prompts automatically quote them. Also allow filtering pasted text and confirm pastes. See :opt:`paste_actions` for details. (:iss:`4873`)\n\n- Change the default value of :opt:`confirm_os_window_close` to ask for confirmation when closing windows that are not sitting at shell prompts\n\n- A new value :code:`last_reported` for :option:`launch --cwd` to use the current working directory last reported by the program running in the terminal\n\n- macOS: When using Apple's less as the pager for viewing scrollback strip out OSC codes as it can't parse them (:iss:`4788`)\n\n- diff kitten: Fix incorrect rendering in rare circumstances when scrolling after changing the context size (:iss:`4831`)\n\n- icat kitten: Fix a regression that broke :option:`kitty +kitten icat --print-window-size` (:pull:`4818`)\n\n- Wayland: Fix :opt:`hide_window_decorations` causing docked windows to be resized on blur (:iss:`4797`)\n\n- Bash integration: Prevent shell integration code from running twice if user enables both automatic and manual integration\n\n- Bash integration: Handle existing PROMPT_COMMAND ending with a literal newline\n\n- Fix continued lines not having their continued status reset on line feed (:iss:`4837`)\n\n- macOS: Allow the New kitty Tab/Window Here services to open multiple selected folders.  (:pull:`4848`)\n\n- Wayland: Fix a regression that broke IME when changing windows/tabs (:iss:`4853`)\n\n- macOS: Fix Unicode paths not decoded correctly when dropping files (:pull:`4879`)\n\n- Avoid flicker when starting kittens such as the hints kitten (:iss:`4674`)\n\n- A new action :ac:`scroll_prompt_to_top` to move the current prompt to the top (:pull:`4891`)\n\n- :ac:`select_tab`: Use stable numbers when selecting the tab (:iss:`4792`)\n\n- Only check for updates in the official binary builds. Distro packages or source builds will no longer check for updates, regardless of the\n  value of :opt:`update_check_interval`.\n\n- Fix :opt:`inactive_text_alpha` still being applied to the cursor hidden window after focus (:iss:`4928`)\n\n- Fix resizing window that is extra tall/wide because of left-over cells not\n  working reliably (:iss:`4913`)\n\n- A new action :ac:`close_other_tabs_in_os_window` to close other tabs in the active OS window (:pull:`4944`)\n\n\n0.24.4 [2022-03-03]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Shell integration: Fix the default Bash :code:`$HISTFILE` changing to :file:`~/.sh_history` instead of :file:`~/.bash_history` (:iss:`4765`)\n\n- Linux binaries: Fix binaries not working on systems with older Wayland client libraries (:iss:`4760`)\n\n- Fix a regression in the previous release that broke kittens launched with :code:`STDIN` not connected to a terminal (:iss:`4763`)\n\n- Wayland: Fix surface configure events not being acknowledged before commit\n  the resized buffer (:pull:`4768`)\n\n\n0.24.3 [2022-02-28]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Bash integration: No longer modify :file:`~/.bashrc` to load :ref:`shell integration <shell_integration>`.\n  It is recommended to remove the lines used to load the shell integration from :file:`~/.bashrc` as they are no-ops.\n\n- macOS: Allow kitty to handle various URL types. Can be configured via\n  :ref:`launch_actions` (:pull:`4618`)\n\n- macOS: Add a new service ``Open with kitty`` to open file types that are not\n  recognized by the system (:pull:`4641`)\n\n- Splits layout: A new value for :option:`launch --location` to auto-select the split axis when splitting existing windows.\n  Wide windows are split side-by-side and tall windows are split one-above-the-other\n\n- hints kitten: Fix a regression that broke recognition of path:linenumber:colnumber (:iss:`4675`)\n\n- Fix a regression in the previous release that broke :opt:`active_tab_foreground` (:iss:`4620`)\n\n- Fix :ac:`show_last_command_output` not working when the output is stored\n  partially in the scrollback pager history buffer (:iss:`4435`)\n\n- When dropping URLs/files onto kitty at a shell prompt insert them appropriately quoted and space\n  separated (:iss:`4734`)\n\n- Improve CWD detection when there are multiple foreground processes in the TTY process group\n\n- A new option :opt:`narrow_symbols` to turn off opportunistic wide rendering of private use codepoints\n\n- ssh kitten: Fix location of generated terminfo files on NetBSD (:iss:`4622`)\n\n- A new action to clear the screen up to the line containing the cursor, see\n  :ac:`clear_terminal`\n\n- A new action :ac:`copy_ansi_to_clipboard` to copy the current selection with ANSI formatting codes\n  (:iss:`4665`)\n\n- Linux: Do not rescale fallback fonts to match the main font cell height, instead just\n  set the font size and let FreeType take care of it. This matches\n  rendering on macOS (:iss:`4707`)\n\n- macOS: Fix a regression in the previous release that broke switching input\n  sources by keyboard (:iss:`4621`)\n\n- macOS: Add the default shortcut :kbd:`cmd+k` to clear the terminal screen and\n  scrollback up to the cursor (:iss:`4625`)\n\n- Fix a regression in the previous release that broke strikethrough (:disc:`4632`)\n\n- A new action :ac:`scroll_prompt_to_bottom` to move the current prompt\n  to the bottom, filling in the window from the scrollback (:pull:`4634`)\n\n- Add two special arguments ``@first-line-on-screen`` and ``@last-line-on-screen``\n  for the :doc:`launch <launch>` command to be used for pager positioning.\n  (:iss:`4462`)\n\n- Linux: Fix rendering of emoji when using scalable fonts such as Segoe UI Emoji\n\n- Shell integration: bash: Dont fail if an existing PROMPT_COMMAND ends with a semi-colon (:iss:`4645`)\n\n- Shell integration: bash: Fix rendering of multiline prompts with more than two lines (:iss:`4681`)\n\n- Shell integration: fish: Check fish version 3.3.0+ and exit on outdated versions (:pull:`4745`)\n\n- Shell integration: fish: Fix pipestatus being overwritten (:pull:`4756`)\n\n- Linux: Fix fontconfig alias not being used if the aliased font is dual spaced instead of monospaced (:iss:`4649`)\n\n- macOS: Add an option :opt:`macos_menubar_title_max_length` to control the max length of the window title displayed in the global menubar (:iss:`2132`)\n\n- Fix :opt:`touch_scroll_multiplier` also taking effect in terminal programs such as vim that handle mouse events themselves (:iss:`4680`)\n\n- Fix symbol/PUA glyphs loaded via :opt:`symbol_map` instead of as fallbacks not using following spaces to render larger versions (:iss:`4670`)\n\n- macOS: Fix regression in previous release that caused Apple's global shortcuts to not work if they had never been configured on a particular machine (:iss:`4657`)\n\n- Fix a fast *click, move mouse, click* sequence causing the first click event to be discarded (:iss:`4603`)\n\n- Wayland: Fix wheel mice with line based scrolling being incorrectly handled as high precision devices (:iss:`4694`)\n\n- Wayland: Fix touchpads and high resolution wheels not scrolling at the same speed on monitors with different scales (:iss:`4703`)\n\n- Add an option :opt:`wheel_scroll_min_lines` to set the minimum number of lines for mouse wheel scrolling when using a mouse with a wheel that generates very small offsets when slow scrolling (:pull:`4710`)\n\n- macOS: Make the shortcut to toggle full screen configurable (:pull:`4714`)\n\n- macOS: Fix the mouse cursor being set to arrow after switching desktops or toggling full screen (:pull:`4716`)\n\n- Fix copying of selection after selection has been scrolled off history buffer raising an error (:iss:`4713`)\n\n\n0.24.2 [2022-02-03]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Allow opening text files, images and directories with kitty when\n  launched using \"Open with\" in Finder (:iss:`4460`)\n\n- Allow including config files matching glob patterns in :file:`kitty.conf`\n  (:iss:`4533`)\n\n- Shell integration: Fix bash integration not working when ``PROMPT_COMMAND``\n  is used to change the prompt variables (:iss:`4476`)\n\n- Shell integration: Fix cursor shape not being restored to default when\n  running commands in the shell\n\n- Improve the UI of the ask kitten (:iss:`4545`)\n\n- Allow customizing the placement and formatting of the\n  :opt:`tab_activity_symbol` and :opt:`bell_on_tab` symbols\n  by adding them to the :opt:`tab_title_template` (:iss:`4581`, :pull:`4507`)\n\n- macOS: Persist \"Secure Keyboard Entry\" across restarts to match the behavior\n  of Terminal.app (:iss:`4471`)\n\n- hints kitten: Fix common single letter extension files not being detected\n  (:iss:`4491`)\n\n- Support dotted and dashed underline styles (:pull:`4529`)\n\n- For the vertical and horizontal layouts have the windows arranged on a ring\n  rather than a plane. This means the first and last window are considered\n  neighbors (:iss:`4494`)\n\n- A new action to clear the current selection (:iss:`4600`)\n\n- Shell integration: fish: Fix cursor shape not working with fish's vi mode\n  (:iss:`4508`)\n\n- Shell integration: fish: Dont override fish's native title setting functionality.\n  See `discussion <https://github.com/fish-shell/fish-shell/issues/8641>`__.\n\n- macOS: Fix hiding via :kbd:`cmd+h` not working on macOS 10.15.7 (:iss:`4472`)\n\n- Draw the dots for braille characters more evenly spaced at all font sizes (:iss:`4499`)\n\n- icat kitten: Add options to mirror images and remove their transparency\n  before displaying them (:iss:`4513`)\n\n- macOS: Respect the users system-wide global keyboard shortcut preferences\n  (:iss:`4501`)\n\n- macOS: Fix a few key-presses causing beeps from Cocoa's text input system\n  (:iss:`4489`)\n\n- macOS: Fix using shortcuts from the global menu bar as subsequent key presses\n  in a multi key mapping not working (:iss:`4519`)\n\n- Fix getting last command output not working correctly when the screen is\n  scrolled (:pull:`4522`)\n\n- Show number of windows per tab in the :ac:`select_tab` action (:pull:`4523`)\n\n- macOS: Fix the shift key not clearing pre-edit text in IME (:iss:`4541`)\n\n- Fix clicking in a window to focus it and typing immediately sometimes having\n  unexpected effects if at a shell prompt (:iss:`4128`)\n\n- themes kitten: Allow writing to a different file than :file:`kitty.conf`.\n\n\n0.24.1 [2022-01-06]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Shell integration: Work around conflicts with some zsh plugins (:iss:`4428`)\n\n- Have the zero width space and various other characters from the *Other,\n  formatting* Unicode category be treated as combining characters (:iss:`4439`)\n\n- Fix using ``--shell-integration`` with :file:`setup.py` broken (:iss:`4434`)\n\n- Fix showing debug information not working if kitty's :file:`STDIN` is not a tty\n  (:iss:`4424`)\n\n- Linux: Fix a regression that broke rendering of emoji with variation selectors\n  (:iss:`4444`)\n\n\n0.24.0 [2022-01-04]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Integrate kitty closely with common shells such as zsh, fish and bash.\n  This allows lots of niceties such as jumping to previous prompts, opening the\n  output of the last command in a new window, etc. See :ref:`shell_integration`\n  for details. Packagers please read :ref:`packagers`.\n\n- A new shortcut :sc:`focus_visible_window` to visually focus a window using\n  the keyboard. Pressing it causes numbers to appear over each visible window\n  and you can press the number to focus the corresponding window (:iss:`4110`)\n\n- A new facility :opt:`window_logo_path` to draw an arbitrary PNG image as\n  logo in the corner of a kitty window (:pull:`4167`)\n\n- Allow rendering the cursor with a *reverse video* effect. See :opt:`cursor`\n  for details (:iss:`126`)\n\n- Allow rendering the mouse selection with a *reverse video* effect. See\n  :opt:`selection_foreground` (:iss:`646`)\n\n- A new option :opt:`tab_bar_align` to draw the tab bar centered or right\n  aligned (:iss:`3946`)\n\n- Allow the user to supply a custom Python function to draw tab bar. See\n  :opt:`tab_bar_style`\n\n- A new remote control command to :program:`change the tab color <kitty @\n  set-tab-color>` (:iss:`1287`)\n\n- A new remote control command to :program:`visually select a window <kitty @\n  select-window>` (:iss:`4165`)\n\n- Add support for reporting mouse events with pixel coordinates using the\n  ``SGR_PIXEL_PROTOCOL`` introduced in xterm 359\n\n- When programs ask to read from the clipboard prompt, ask the user to allow\n  the request by default instead of denying it by default. See\n  :opt:`clipboard_control` for details (:iss:`4022`)\n\n- A new mappable action ``swap_with_window`` to swap the current window with another window in the tab, visually\n\n- A new :program:`remote control command <kitty @ set-enabled-layouts>` to change\n  the enabled layouts in a tab (:iss:`4129`)\n\n- A new option :opt:`bell_path` to specify the path to a sound file\n  to use as the bell sound\n\n- A new option :opt:`exe_search_path` to modify the locations kitty searches\n  for executables to run (:iss:`4324`)\n\n- broadcast kitten: Show a \"fake\" cursor in all windows being broadcast too\n  (:iss:`4225`)\n\n- Allow defining :opt:`aliases <action_alias>` for more general actions, not just kittens\n  (:pull:`4260`)\n\n- Fix a regression that caused :option:`kitty --title` to not work when\n  opening new OS windows using :option:`kitty --single-instance` (:iss:`3893`)\n\n- icat kitten: Fix display of JPEG images that are rotated via EXIF data and\n  larger than available screen size (:iss:`3949`)\n\n- macOS: Fix SIGUSR1 quitting kitty instead of reloading the config file (:iss:`3952`)\n\n- Launch command: Allow specifying the OS window title\n\n- broadcast kitten: Allow broadcasting :kbd:`ctrl+c` (:pull:`3956`)\n\n- Fix space ligatures not working with Iosevka for some characters in the\n  Enclosed Alphanumeric Supplement (:iss:`3954`)\n\n- hints kitten: Fix a regression that caused using the default open program\n  to trigger open actions instead of running the program (:iss:`3968`)\n\n- Allow deleting environment variables in :opt:`env` by specifying\n  just the variable name, without a value\n\n- Fix :opt:`active_tab_foreground` not being honored when :opt:`tab_bar_style`\n  is ``slant`` (:iss:`4053`)\n\n- When a :opt:`tab_bar_background` is specified it should extend to the edges\n  of the OS window (:iss:`4054`)\n\n- Linux: Fix IME with fcitx5 not working after fcitx5 is restarted\n  (:pull:`4059`)\n\n- Various improvements to IME integration (:iss:`4219`)\n\n- Remote file transfer: Fix transfer not working if custom ssh port or identity\n  is specified on the command line (:iss:`4067`)\n\n- Unicode input kitten: Implement scrolling when more results are found than\n  the available display space (:pull:`4068`)\n\n- Allow middle clicking on a tab to close it (:iss:`4151`)\n\n- The command line option ``--watcher`` has been deprecated in favor of the\n  :opt:`watcher` option in :file:`kitty.conf`. It has the advantage of\n  applying to all windows, not just the initially created ones. Note that\n  ``--watcher`` now also applies to all windows, not just initially created ones.\n\n- **Backward incompatibility**: No longer turn on the kitty extended keyboard\n  protocol's disambiguate mode when the client sends the XTMODKEYS escape code.\n  Applications must use the dedicated escape code to turn on the protocol.\n  (:iss:`4075`)\n\n- Fix soft hyphens not being preserved when round tripping text through the\n  terminal\n\n- macOS: Fix :kbd:`ctrl+shift` with :kbd:`Esc` or :kbd:`F1` - :kbd:`F12` not working\n  (:iss:`4109`)\n\n- macOS: Fix :opt:`resize_in_steps` not working correctly on high DPI screens\n  (:iss:`4114`)\n\n- Fix the :program:`resize OS Windows <kitty @ resize-os-window>` setting a\n  slightly incorrect size on high DPI screens (:iss:`4114`)\n\n- :program:`kitty @ launch` - when creating tabs with the ``--match`` option create\n  the tab in the OS Window containing the result of the match rather than\n  the active OS Window (:iss:`4126`)\n\n- Linux X11: Add support for 10bit colors (:iss:`4150`)\n\n- Fix various issues with changing :opt:`tab_bar_background` by remote control\n  (:iss:`4152`)\n\n- A new option :opt:`tab_bar_margin_color` to control the color of the tab bar\n  margins\n\n- A new option :opt:`visual_bell_color` to customize the color of the visual bell\n  (:pull:`4181`)\n\n- Add support for OSC 777 based desktop notifications\n\n- Wayland: Fix pasting from applications that use a MIME type of \"text/plain\"\n  rather than \"text/plain;charset=utf-8\" not working (:iss:`4183`)\n\n- A new mappable action to close windows with a confirmation (:iss:`4195`)\n\n- When remembering OS window sizes for full screen windows use the size before\n  the window became fullscreen (:iss:`4221`)\n\n- macOS: Fix keyboard input not working after toggling fullscreen till the\n  window is clicked in\n\n- A new mappable action ``nth_os_window`` to focus the specified nth OS\n  window. (:pull:`4316`)\n\n- macOS: The kitty window can be scrolled by the mouse wheel when OS window not\n  in focus. (:pull:`4371`)\n\n- macOS: Light or dark system appearance can be specified in\n  :opt:`macos_titlebar_color` and used in kitty themes. (:pull:`4378`)\n\n\n0.23.1 [2021-08-17]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix themes kitten failing to download themes because of missing SSL\n  root certificates (:iss:`3936`)\n\n- A new option :opt:`clipboard_max_size` to control the maximum size\n  of data that kitty will transmit to the system clipboard on behalf of\n  programs running inside it (:iss:`3937`)\n\n- When matching windows/tabs in kittens or using remote control, allow matching\n  by recency. ``recent:0`` matches the active window/tab, ``recent:1`` matches\n  the previous window/tab and so on\n\n- themes kitten: Fix only the first custom theme file being loaded correctly\n  (:iss:`3938`)\n\n\n0.23.0 [2021-08-16]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new :doc:`themes kitten </kittens/themes>` to easily change kitty themes.\n  Choose from almost two hundred themes in the `kitty themes repository\n  <https://github.com/kovidgoyal/kitty-themes>`_\n\n- A new style for the tab bar that makes tabs looks like the tabs in a physical\n  tabbed file, see :opt:`tab_bar_style`\n\n- Make the visual bell flash more gentle, especially on dark themes\n  (:pull:`2937`)\n\n- Fix :option:`kitty --title` not overriding the OS Window title when multiple\n  tabs are present. Also this option is no longer used as the default title for\n  windows, allowing individual tabs/windows to have their own titles, even when\n  the OS Window has a fixed overall title (:iss:`3893`)\n\n- Linux: Fix some very long ligatures being rendered incorrectly at some font\n  sizes (:iss:`3896`)\n\n- Fix shift+middle click to paste sending a mouse press event but no release\n  event which breaks some applications that grab the mouse but can't handle\n  mouse events (:iss:`3902`)\n\n- macOS: When the language is set to English and the country to one for which\n  an English locale does not exist, set :envvar:`LANG` to ``en_US.UTF-8``\n  (:iss:`3899`)\n\n- terminfo: Fix \"cnorm\" the property for setting the cursor to normal using a\n  solid block rather than a blinking block cursor (:iss:`3906`)\n\n- Add :opt:`clear_all_mouse_actions` to clear all mouse actions defined to\n  that point (:iss:`3907`)\n\n- Fix the remote file kitten not working when using -- with ssh. The ssh kitten\n  was recently changed to do this (:iss:`3929`)\n\n- When dragging word or line selections, ensure the initially selected item is\n  never deselected. This matches behavior in most other programs (:iss:`3930`)\n\n- hints kitten: Make copy/paste with the :option:`kitty +kitten hints\n  --program` option work when using the ``self``\n  :option:`kitty +kitten hints --linenum-action` (:iss:`3931`)\n\n\n0.22.2 [2021-08-02]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix a long standing bug that could cause kitty windows to stop\n  updating, that got worse in the previous release (:iss:`3890` and\n  :iss:`2016`)\n\n- Wayland: A better fix for compositors like sway that can toggle client side\n  decorations on and off (:iss:`3888`)\n\n\n0.22.1 [2021-07-31]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix a regression in the previous release that broke ``kitty --help`` (:iss:`3869`)\n\n- Graphics protocol: Fix composing onto currently displayed frame not updating the frame on the GPU (:iss:`3874`)\n\n- Fix switching to previously active tab after detaching a tab not working (:pull:`3871`)\n\n- macOS: Fix an error on Apple silicon when enumerating monitors (:pull:`3875`)\n\n- detach_window: Allow specifying the previously active tab or the tab to the left/right of\n  the active tab (:disc:`3877`)\n\n- broadcast kitten: Fix a regression in ``0.20.0`` that broke sending of some\n  keys, such as backspace\n\n- Linux binary: Remove any RPATH build artifacts from bundled libraries\n\n- Wayland: If the compositor turns off server side decorations after turning\n  them on do not draw client side decorations (:iss:`3888`)\n\n\n0.22.0 [2021-07-26]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add a new :ac:`toggle_layout` action to easily zoom/unzoom a window\n\n- When right clicking to extend a selection, move the nearest selection\n  boundary rather than the end of the selection. To restore previous behavior\n  use ``mouse_map right press ungrabbed mouse_selection move-end``.\n\n- When opening hyperlinks, allow defining open actions for directories\n  (:pull:`3836`)\n\n- When using the OSC 52 escape code to copy to clipboard allow large\n  copies (up to 8MB) without needing a kitty specific chunking protocol.\n  Note that if you used the chunking protocol in the past, it will no longer\n  work and you should switch to using the unmodified protocol which has the\n  advantage of working with all terminal emulators.\n\n- Fix a bug in the implementation of the synchronized updates escape code that\n  could cause incorrect parsing if either the pending buffer capacity or the\n  pending timeout were exceeded (:iss:`3779`)\n\n- A new remote control command to :program:`resize the OS Window <kitty @\n  resize-os-window>`\n\n- Graphics protocol: Add support for composing rectangles from one animation\n  frame onto another (:iss:`3809`)\n\n- diff kitten: Remove limit on max line length of 4096 characters (:iss:`3806`)\n\n- Fix turning off cursor blink via escape codes not working (:iss:`3808`)\n\n- Allow using neighboring window operations in the stack layout. The previous\n  window is considered the left and top neighbor and the next window is\n  considered the bottom and right neighbor (:iss:`3778`)\n\n- macOS: Render colors in the sRGB colorspace to match other macOS terminal\n  applications (:iss:`2249`)\n\n- Add a new variable ``{num_window_groups}`` for the :opt:`tab_title_template`\n  (:iss:`3837`)\n\n- Wayland: Fix :opt:`initial_window_width/height <remember_window_size>` specified\n  in cells not working on High DPI screens (:iss:`3834`)\n\n- A new theme for the kitty website with support for dark mode.\n\n- Render ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ with spaces at the edges. Matches rendering in\n  most other programs and allows long chains of them to look better\n  (:iss:`3844`)\n\n- hints kitten: Detect paths and hashes that appear over multiple lines.\n  Note that this means that all line breaks in the text are no longer \\n\n  soft breaks are instead \\r. If you use a custom regular expression that\n  is meant to match over line breaks, you will need to match over both.\n  (:iss:`3845`)\n\n- Allow leading or trailing spaces in :opt:`tab_activity_symbol`\n\n- Fix mouse actions not working when caps lock or num lock are engaged\n  (:iss:`3859`)\n\n- macOS: Fix automatic detection of bold/italic faces for fonts that\n  use the family name as the full face name of the regular font not working\n  (:iss:`3861`)\n\n- clipboard kitten: fix copies to clipboard not working without the\n  :option:`kitty +kitten clipboard --wait-for-completion` option\n\n\n0.21.2 [2021-06-28]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new ``adjust_baseline`` option to adjust the vertical alignment of text\n  inside a line (:pull:`3734`)\n\n- A new :opt:`url_excluded_characters` option to exclude additional characters\n  when detecting URLs under the mouse (:pull:`3738`)\n\n- Fix a regression in 0.21.0 that broke rendering of private use Unicode symbols followed\n  by spaces, when they also exist not followed by spaces (:iss:`3729`)\n\n- ssh kitten: Support systems where the login shell is a non-POSIX shell\n  (:iss:`3405`)\n\n- ssh kitten: Add completion (:iss:`3760`)\n\n- ssh kitten: Fix \"Connection closed\" message being printed by ssh when running\n  remote commands\n\n- Add support for the XTVERSION escape code\n\n- macOS: Fix a regression in 0.21.0 that broke middle-click to paste from clipboard (:iss:`3730`)\n\n- macOS: Fix shortcuts in the global menu bar responding slowly when cursor blink\n  is disabled/timed out (:iss:`3693`)\n\n- When displaying scrollback ensure that the window does not quit if the amount\n  of scrollback is less than a screen and the user has the ``--quit-if-one-screen``\n  option enabled for less (:iss:`3740`)\n\n- Linux: Fix Emoji/bitmapped fonts not use able in symbol_map\n\n- query terminal kitten: Allow querying font face and size information\n  (:iss:`3756`)\n\n- hyperlinked grep kitten: Fix context options not generating contextual output (:iss:`3759`)\n\n- Allow using superscripts in tab titles (:iss:`3763`)\n\n- Unicode input kitten: Fix searching when a word has more than 1024 matches (:iss:`3773`)\n\n\n0.21.1 [2021-06-14]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix a regression in the previous release that broke rendering of\n  strikeout (:iss:`3717`)\n\n- macOS: Fix a crash when rendering ligatures larger than 128 characters\n  (:iss:`3724`)\n\n- Fix a regression in the previous release that could cause a crash when\n  changing layouts and mousing (:iss:`3713`)\n\n\n0.21.0 [2021-06-12]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow reloading the :file:`kitty.conf` config file by pressing\n  :sc:`reload_config_file`. (:iss:`1292`)\n\n- Allow clicking URLs to open them without needing to also hold\n  :kbd:`ctrl+shift`\n\n- Allow remapping all mouse button press/release events to perform arbitrary\n  actions. :ref:`See details <conf-kitty-mouse.mousemap>` (:iss:`1033`)\n\n- Support infinite length ligatures (:iss:`3504`)\n\n- **Backward incompatibility**: The options to control which modifiers keys to\n  press for various mouse actions have been removed, if you used these options,\n  you will need to replace them with configuration using the new\n  :ref:`mouse actions framework <conf-kitty-mouse.mousemap>` as they will be\n  ignored. The options were: ``terminal_select_modifiers``,\n  ``rectangle_select_modifiers`` and ``open_url_modifiers``.\n\n- Add a configurable mouse action (:kbd:`ctrl+alt+triplepress` to select from the\n  clicked point to the end of the line. (:iss:`3585`)\n\n- Add the ability to un-scroll the screen to the ``kitty @ scroll-window``\n  remote control command (:iss:`3604`)\n\n- A new option, :opt:`tab_bar_margin_height` to add margins around the\n  top and bottom edges of the tab bar (:iss:`3247`)\n\n- Unicode input kitten: Fix a regression in 0.20.0 that broke keyboard handling\n  when the NumLock or CapsLock modifiers were engaged. (:iss:`3587`)\n\n- Fix a regression in 0.20.0 that sent incorrect bytes for the :kbd:`F1`-:kbd:`F4` keys\n  in rmkx mode (:iss:`3586`)\n\n- macOS: When the Apple Color Emoji font lacks an emoji glyph search for it in other\n  installed fonts (:iss:`3591`)\n\n- macOS: Fix rendering getting stuck on some machines after sleep/screensaver\n  (:iss:`2016`)\n\n- macOS: Add a new ``Shell`` menu to the global menubar with some commonly used\n  actions (:pull:`3653`)\n\n- macOS: Fix the baseline for text not matching other CoreText based\n  applications for some fonts (:iss:`2022`)\n\n- Add a few more special commandline arguments for the launch command. Now all\n  ``KITTY_PIPE_DATA`` is also available via command line argument substitution\n  (:iss:`3593`)\n\n- Fix dynamically changing the background color in a window causing rendering\n  artifacts in the tab bar (:iss:`3595`)\n\n- Fix passing STDIN to launched background processes causing them to not inherit\n  environment variables (:pull:`3603`)\n\n- Fix deleting windows that are not the last window via remote control leaving\n  no window focused (:iss:`3619`)\n\n- Add an option :option:`kitten @ get-text --add-cursor` to also get the current\n  cursor position and state as ANSI escape codes (:iss:`3625`)\n\n- Add an option :option:`kitten @ get-text --add-wrap-markers` to add line wrap\n  markers to the output (:pull:`3633`)\n\n- Improve rendering of curly underlines on HiDPI screens (:pull:`3637`)\n\n- ssh kitten: Mimic behavior of ssh command line client more closely by\n  executing any command specified on the command line via the users' shell\n  just as ssh does (:iss:`3638`)\n\n- Fix trailing parentheses in URLs not being detected (:iss:`3688`)\n\n- Tab bar: Use a lower contrast color for tab separators (:pull:`3666`)\n\n- Fix a regression that caused using the ``title`` command in session files\n  to stop working (:iss:`3676`)\n\n- macOS: Fix a rare crash on exit (:iss:`3686`)\n\n- Fix ligatures not working with the `Iosevka\n  <https://github.com/be5invis/Iosevka>`_ font (requires Iosevka >= 7.0.4)\n  (:iss:`297`)\n\n- Remote control: Allow matching tabs by index number in currently active OS\n  Window (:iss:`3708`)\n\n- ssh kitten: Fix non-standard properties in terminfo such as the ones used for\n  true color not being copied (:iss:`312`)\n\n\n0.20.3 [2021-05-06]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Distribute universal binaries with both ARM and Intel architectures\n\n- A new ``show_key`` kitten to easily see the bytes generated by the terminal\n  for key presses in the various keyboard modes (:pull:`3556`)\n\n- Linux: Fix keyboard layout change keys defined via compose rules not being\n  ignored\n\n- macOS: Fix Spotlight search of global menu not working in non-English locales\n  (:pull:`3567`)\n\n- Fix tab activity symbol not appearing if no other changes happen in tab bar even when\n  there is activity in a tab (:iss:`3571`)\n\n- Fix focus changes not being sent to windows when focused window changes\n  because of the previously focused window being closed (:iss:`3571`)\n\n\n0.20.2 [2021-04-28]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new protocol extension to :ref:`unscroll <unscroll>` text from the\n  scrollback buffer onto the screen. Useful, for example, to restore\n  the screen after showing completions below the shell prompt.\n\n- A new remote control command :ref:`at-env` to change the default\n  environment passed to newly created windows (:iss:`3529`)\n\n- Linux: Fix binary kitty builds not able to load fonts in WOFF2 format\n  (:iss:`3506`)\n\n- macOS: Prevent :kbd:`option` based shortcuts for being used for global menu\n  actions (:iss:`3515`)\n\n- Fix ``kitty @ close-tab`` not working with pipe based remote control\n  (:iss:`3510`)\n\n- Fix removal of inactive tab that is before the currently active tab causing\n  the highlighted tab to be incorrect (:iss:`3516`)\n\n- icat kitten: Respect EXIF orientation when displaying JPEG images\n  (:iss:`3518`)\n\n- GNOME: Fix maximize state not being remembered when focus changes and window\n  decorations are hidden (:iss:`3507`)\n\n- GNOME: Add a new :opt:`wayland_titlebar_color` option to control the color of the\n  kitty window title bar\n\n- Fix reading :option:`kitty --session` from ``STDIN`` not working when the\n  :code:`kitty --detach` option is used (:iss:`3523`)\n\n- Special case rendering of the few remaining Powerline box drawing chars\n  (:iss:`3535`)\n\n- Fix ``kitty @ set-colors`` not working for the :opt:`active_tab_foreground`.\n\n\n0.20.1 [2021-04-19]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- icat: Fix some broken GIF images with no frame delays not being animated\n  (:iss:`3498`)\n\n- hints kitten: Fix sending hyperlinks to their default handler not working\n  (:pull:`3500`)\n\n- Wayland: Fix regression in previous release causing window decorations to\n  be drawn even when compositor supports server side decorations (:iss:`3501`)\n\n\n0.20.0 [2021-04-19]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Support display of animated images ``kitty +kitten icat animation.gif``. See\n  :ref:`animation_protocol` for details on animation support in the kitty\n  graphics protocol.\n\n- A new keyboard reporting protocol with various advanced features that can be\n  used by full screen terminal programs and even games, see\n  :doc:`keyboard-protocol` (:iss:`3248`)\n\n- **Backward incompatibility**: Session files now use the full :doc:`launch <launch>`\n  command with all its capabilities. However, the syntax of the command is\n  slightly different from before. In particular watchers are now specified\n  directly on launch and environment variables are set using ``--env``.\n\n- Allow setting colors when creating windows using the :doc:`launch <launch>` command.\n\n- A new option :opt:`tab_powerline_style` to control the appearance of the tab\n  bar when using the powerline tab bar style.\n\n- A new option :opt:`scrollback_fill_enlarged_window` to fill extra lines in\n  the window when the window is expanded with lines from the scrollback\n  (:pull:`3371`)\n\n- diff kitten: Implement recursive diff over SSH (:iss:`3268`)\n\n- ssh kitten: Allow using python instead of the shell on the server, useful if\n  the shell used is a non-POSIX compliant one, such as fish (:iss:`3277`)\n\n- Add support for the color settings stack that XTerm copied from us without\n  acknowledgement and decided to use incompatible escape codes for.\n\n- Add entries to the terminfo file for some user capabilities that are shared\n  with XTerm (:pull:`3193`)\n\n- The launch command now does more sophisticated resolving of executables to\n  run. The system-wide PATH is used first, then system specific default paths,\n  and finally the PATH inside the shell.\n\n- Double clicking on empty tab bar area now opens a new tab (:iss:`3201`)\n\n- kitty @ ls: Show only environment variables that are different for each\n  window, by default.\n\n- When passing a directory or a non-executable file as the program to run to\n  kitty opens it with the shell or by parsing the shebang, instead of just failing.\n\n- Linux: Fix rendering of emoji followed by the graphics variation selector not\n  being colored with some fonts (:iss:`3211`)\n\n- Unicode input: Fix using index in select by name mode not working for indices\n  larger than 16. Also using an index does not filter the list of matches. (:pull:`3219`)\n\n- Wayland: Add support for the text input protocol (:iss:`3410`)\n\n- Wayland: Fix mouse handling when using client side decorations\n\n- Wayland: Fix un-maximizing a window not restoring its size to what it was\n  before being maximized\n\n- GNOME/Wayland: Improve window decorations the titlebar now shows the window\n  title. Allow running under Wayland on GNOME by default. (:iss:`3284`)\n\n- Panel kitten: Allow setting WM_CLASS (:iss:`3233`)\n\n- macOS: Add menu items to close the OS window and the current tab (:pull:`3240`, :iss:`3246`)\n\n- macOS: Allow opening script and command files with kitty (:iss:`3366`)\n\n- Also detect ``gemini://`` URLs when hovering with the mouse (:iss:`3370`)\n\n- When using a non-US keyboard layout and pressing :kbd:`ctrl+key` when\n  the key matches an English key, send that to the program running in the\n  terminal automatically (:iss:`2000`)\n\n- When matching shortcuts, also match on shifted keys, so a shortcut defined as\n  :kbd:`ctrl+plus` will match a keyboard where you have to press\n  :kbd:`shift+equal` to get the plus key (:iss:`2000`)\n\n- Fix extra space at bottom of OS window when using the fat layout with the tab bar at the\n  top (:iss:`3258`)\n\n- Fix window icon not working on X11 with 64bits (:iss:`3260`)\n\n- Fix OS window sizes under 100px resulting in scaled display (:iss:`3307`)\n\n- Fix rendering of ligatures in the latest release of Cascadia code, which for\n  some reason puts empty glyphs after the ligature glyph rather than before it\n  (:iss:`3313`)\n\n- Improve handling of infinite length ligatures in newer versions of FiraCode\n  and CascadiaCode. Now such ligatures are detected based on glyph naming\n  convention. This removes the gap in the ligatures at cell boundaries (:iss:`2695`)\n\n- macOS: Disable the native operating system tabs as they are non-functional\n  and can be confusing (:iss:`3325`)\n\n- hints kitten: When using the linenumber action with a background action,\n  preserve the working directory (:iss:`3352`)\n\n- Graphics protocol: Fix suppression of responses not working for chunked\n  transmission (:iss:`3375`)\n\n- Fix inactive tab closing causing active tab to change (:iss:`3398`)\n\n- Fix a crash on systems using musl as libc (:iss:`3395`)\n\n- Improve rendering of rounded corners by using a rectircle equation rather\n  than a cubic bezier (:iss:`3409`)\n\n- Graphics protocol: Add a control to allow clients to specify that the cursor\n  should not move when displaying an image (:iss:`3411`)\n\n- Fix marking of text not working on lines that contain zero cells\n  (:iss:`3403`)\n\n- Fix the selection getting changed if the screen contents scroll while\n  the selection is in progress (:iss:`3431`)\n\n- X11: Fix :opt:`resize_in_steps` being applied even when window is maximized\n  (:iss:`3473`)\n\n\n0.19.3 [2020-12-19]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Happy holidays to all kitty users!\n\n- A new :doc:`broadcast <kittens/broadcast>` kitten to type in all kitty windows\n  simultaneously (:iss:`1569`)\n\n- Add a new mappable `select_tab` action to choose a tab to switch to even\n  when the tab bar is hidden (:iss:`3115`)\n\n- Allow specifying text formatting in :opt:`tab_title_template` (:iss:`3146`)\n\n- Linux: Read :opt:`font_features` from the FontConfig database as well, so\n  that they can be configured in a single, central location (:pull:`3174`)\n\n- Graphics protocol: Add support for giving individual image placements their\n  own ids and for asking the terminal emulator to assign ids for images. Also\n  allow suppressing responses from the terminal to commands.\n  These are backwards compatible protocol extensions. (:iss:`3133`,\n  :iss:`3163`)\n\n- Distribute extra pixels among all eight-blocks rather than adding them\n  all to the last block (:iss:`3097`)\n\n- Fix drawing of a few sextant characters incorrect (:pull:`3105`)\n\n- macOS: Fix minimize not working for chromeless windows (:iss:`3112`)\n\n- Preserve lines in the scrollback if a scrolling region is defined that\n  is contiguous with the top of the screen (:iss:`3113`)\n\n- Wayland: Fix key repeat being stopped by the release of an unrelated key\n  (:iss:`2191`)\n\n- Add an option, :opt:`detect_urls` to control whether kitty will detect URLs\n  when the mouse moves over them (:pull:`3118`)\n\n- Graphics protocol: Dont return filename in the error message when opening file\n  fails, since filenames can contain control characters (:iss:`3128`)\n\n- macOS: Partial fix for traditional fullscreen not working on Big Sur\n  (:iss:`3100`)\n\n- Fix one ANSI formatting escape code not being removed from the pager history\n  buffer when piping it as plain text (:iss:`3132`)\n\n- Match the save/restore cursor behavior of other terminals, for the sake of\n  interoperability. This means that doing a DECRC without a prior DECSC is now\n  undefined (:iss:`1264`)\n\n- Fix mapping ``remote_control send-text`` not working (:iss:`3147`)\n\n- Add a ``right`` option for :opt:`tab_switch_strategy` (:pull:`3155`)\n\n- Fix a regression in 0.19.0 that caused a rare crash when using the optional\n  :opt:`scrollback_pager_history_size` (:iss:`3049`)\n\n- Full screen kittens: Fix incorrect cursor position after kitten quits\n  (:iss:`3176`)\n\n\n0.19.2 [2020-11-13]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new :doc:`kittens/query_terminal` kitten to easily query the running kitty\n  via escape codes to detect its version, and the values of\n  configuration options that enable or disable terminal features.\n\n- Options to control mouse pointer shape, :opt:`default_pointer_shape`, and\n  :opt:`pointer_shape_when_dragging` (:pull:`3041`)\n\n- Font independent rendering for braille characters, which ensures they are properly\n  aligned at all font sizes.\n\n- Fix a regression in 0.19.0 that caused borders not to be drawn when setting\n  :opt:`window_margin_width` and keeping :opt:`draw_minimal_borders` on\n  (:iss:`3017`)\n\n- Fix a regression in 0.19.0 that broke rendering of one-eight bar unicode\n  characters at very small font sizes (:iss:`3025`)\n\n- Wayland: Fix a crash under GNOME when using multiple OS windows\n  (:pull:`3066`)\n\n- Fix selections created by dragging upwards not being auto-cleared when\n  screen contents change (:pull:`3028`)\n\n- macOS: Fix kitty not being added to PATH automatically when using pre-built\n  binaries (:iss:`3063`)\n\n- Allow adding MIME definitions to kitty by placing a ``mime.types`` file in\n  the kitty config directory (:iss:`3056`)\n\n- Dont ignore :option:`--title` when using a session file that defines no\n  windows (:iss:`3055`)\n\n- Fix the send_text action not working in URL handlers (:iss:`3081`)\n\n- Fix last character of URL not being detected if it is the only character on a\n  new line (:iss:`3088`)\n\n- Don't restrict the ICH,DCH,REP control codes to only the current scroll region  (:iss:`3090`, :iss:`3096`)\n\n\n0.19.1 [2020-10-06]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- hints kitten: Add an ``ip`` type for easy selection of IP addresses\n  (:pull:`3009`)\n\n- Fix a regression that caused a segfault when using\n  :opt:`scrollback_pager_history_size` and it needs to be expanded (:iss:`3011`)\n\n- Fix update available notifications repeating (:pull:`3006`)\n\n\n0.19.0 [2020-10-04]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add support for `hyperlinks from terminal programs\n  <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`_.\n  Controlled via :opt:`allow_hyperlinks` (:iss:`68`)\n\n- Add support for easily editing or downloading files over SSH sessions\n  without the need for any special software, see :doc:`kittens/remote_file`\n\n- A new :doc:`kittens/hyperlinked_grep` kitten to easily search files and open\n  the results at the matched line by clicking on them.\n\n- Allow customizing the :doc:`actions kitty takes <open_actions>` when clicking on URLs\n\n- Improve rendering of borders when using minimal borders. Use less space and\n  do not display a box around active windows\n\n- Add a new extensible escape code to allow terminal programs to trigger\n  desktop notifications. See :ref:`notifications_on_the_desktop` (:iss:`1474`)\n\n- Implement special rendering for various characters from the set of \"Symbols\n  for Legacy Computing\" from the Unicode 13 standard\n\n- Unicode input kitten: Allow choosing symbols from the NERD font as well.\n  These are mostly Private Use symbols not in any standard, however are common. (:iss:`2972`)\n\n- Allow specifying border sizes in either pts or pixels. Change the default to\n  0.5pt borders as this works best with the new minimal border style\n\n- Add support for displaying correct colors with non-sRGB PNG files (Adds a\n  dependency on liblcms2)\n\n- hints kitten: Add a new :option:`kitty +kitten hints --type` of ``hyperlink`` useful\n  for activating hyperlinks using just the keyboard\n\n- Allow tracking focus change events in watchers (:iss:`2918`)\n\n- Allow specifying watchers in session files and via a command line argument\n  (:iss:`2933`)\n\n- Add a setting :opt:`tab_activity_symbol` to show a symbol in the tab title\n  if one of the windows has some activity after it was last focused\n  (:iss:`2515`)\n\n- macOS: Switch to using the User Notifications framework for notifications.\n  The current notifications framework has been deprecated in Big Sur. The new\n  framework only allows notifications from signed and notarized applications,\n  so people using kitty from homebrew/source are out of luck. Complain to\n  Apple.\n\n- When in the main screen and a program grabs the mouse, do not use the scroll\n  wheel events to scroll the scrollback buffer, instead send them to the\n  program (:iss:`2939`)\n\n- Fix unfocused windows in which a bell occurs not changing their border color\n  to red until a relayout\n\n- Linux: Fix automatic detection of bold/italic faces for fonts such as IBM\n  Plex Mono that have the regular face with a full name that is the same as the\n  family name (:iss:`2951`)\n\n- Fix a regression that broke :opt:`kitten_alias` (:iss:`2952`)\n\n- Fix a regression that broke the ``move_window_to_top`` action (:pull:`2953`)\n\n- Fix a memory leak when changing font sizes\n\n- Fix some lines in the scrollback buffer not being properly rendered after a\n  window resize/font size change (:iss:`2619`)\n\n\n0.18.3 [2020-08-11]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- hints kitten: Allow customizing hint colors (:pull:`2894`)\n\n- Wayland: Fix a typo in the previous release that broke reading mouse cursor size (:iss:`2895`)\n\n- Fix a regression in the previous release that could cause an exception during\n  startup in rare circumstances (:iss:`2896`)\n\n- Fix image leaving behind a black rectangle when switch away and back to\n  alternate screen (:iss:`2901`)\n\n- Fix one pixel misalignment of rounded corners when either the cell\n  dimensions or the thickness of the line is an odd number of pixels\n  (:iss:`2907`)\n\n- Fix a regression that broke specifying OS window size in the session file\n  (:iss:`2908`)\n\n\n0.18.2 [2020-07-28]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- X11: Improve handling of multiple keyboards. Now pressing a modifier key in\n  one keyboard and a normal key in another works (:iss:`2362`). Don't rebuild\n  keymaps on new keyboard events that only change geometry (:iss:`2787`).\n  Better handling of multiple keyboards with incompatible layouts (:iss:`2726`)\n\n- Improve anti-aliasing of triangular box drawing characters, noticeable on\n  low-resolution screens (:iss:`2844`)\n\n- Fix ``kitty @ send-text`` not working reliably when using a socket for remote\n  control (:iss:`2852`)\n\n- Implement support for box drawing rounded-corners characters (:iss:`2240`)\n\n- Allow setting the class for new OS windows in a session file\n\n- When a character from the Unicode Dingbat block is followed by a space, use\n  the extra space to render a larger version of the character (:iss:`2850`)\n\n- macOS: Fix the LC_CTYPE env var being set to UTF-8 on systems in which the\n  language and country code do not form a valid locale (:iss:`1233`)\n\n- macOS: Fix :kbd:`cmd+plus` not changing font size (:iss:`2839`)\n\n- Make neighboring window selection in grid and splits layouts more intelligent\n  (:pull:`2840`)\n\n- Allow passing the current selection to kittens (:iss:`2796`)\n\n- Fix pre-edit text not always being cleared with ibus input (:iss:`2862`)\n\n- Allow setting the :opt:`background_opacity` of new OS windows created via\n  :option:`kitty --single-instance` using the :option:`kitty --override` command line\n  argument (:iss:`2806`)\n\n- Fix the CSI J (Erase in display ED) escape code not removing line continued\n  markers (:iss:`2809`)\n\n- hints kitten: In linenumber mode expand paths that starts with ~\n  (:iss:`2822`)\n\n- Fix ``launch --location=last`` not working (:iss:`2841`)\n\n- Fix incorrect centering when a PUA or symbol glyph is followed by more than one space\n\n- Have the :opt:`confirm_os_window_close` option also apply when closing tabs\n  with multiple windows (:iss:`2857`)\n\n- Add support for legacy DECSET codes 47, 1047 and 1048 (:pull:`2871`)\n\n- macOS: no longer render emoji 20% below the baseline. This caused some emoji\n  to be cut-off and also look misaligned with very high cells (:iss:`2873`)\n\n- macOS: Make the window id of OS windows available in the ``WINDOWID``\n  environment variable (:pull:`2877`)\n\n- Wayland: Fix a regression in 0.18.0 that could cause crashes related to mouse\n  cursors in some rare circumstances (:iss:`2810`)\n\n- Fix change in window size that does not change number of cells not being\n  reported to the kernel (:iss:`2880`)\n\n\n0.18.1 [2020-06-23]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix for diff kitten not working with python 3.8 (:iss:`2780`)\n\n\n0.18.0 [2020-06-20]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow multiple overlay windows per normal window\n\n- Add an option :opt:`confirm_os_window_close` to ask for confirmation\n  when closing an OS window with multiple kitty windows.\n\n- Tall and Fat layouts: Add a ``mirrored`` option to put the full size window\n  on the opposite edge of the screen (:iss:`2654`)\n\n- Tall and Fat layouts: Add mappable actions to increase or decrease the number\n  of full size windows (:iss:`2688`)\n\n- Allow sending arbitrary signals to the current foreground process in a window\n  using either a mapping in kitty.conf or via remote control (:iss:`2778`)\n\n- Allow sending the back and forward mouse buttons to terminal applications\n  (:pull:`2742`)\n\n- **Backwards incompatibility**: The numbers used to encode mouse buttons\n  for the ``send_mouse_event`` function that can be used in kittens have\n  been changed (see :ref:`send_mouse_event`).\n\n- Add a new mappable ``quit`` action to quit kitty completely.\n\n- Fix marks using different colors with regexes using only a single color\n  (:pull:`2663`)\n\n- Linux: Workaround for broken Nvidia drivers for old cards (:iss:`456`)\n\n- Wayland: Fix kitty being killed on some Wayland compositors if a hidden window\n  has a lot of output (:iss:`2329`)\n\n- BSD: Fix controlling terminal not being established (:pull:`2686`)\n\n- Add support for the CSI REP escape code (:pull:`2702`)\n\n- Wayland: Fix mouse cursor rendering on HiDPI screens (:pull:`2709`)\n\n- X11: Recompile keymaps on XkbNewKeyboardNotify events (:iss:`2726`)\n\n- X11: Reduce startup time by ~25% by only querying GLX for framebuffer\n  configurations once (:iss:`2754`)\n\n- macOS: Notarize the kitty application bundle (:iss:`2040`)\n\n- Fix the kitty shell launched via a mapping needlessly requiring\n  :opt:`allow_remote_control` to be turned on.\n\n\n0.17.4 [2020-05-09]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow showing the name of the current layout and the number of windows\n  in tab titles (:iss:`2634`)\n\n- macOS: Fix a regression in the previous release that caused ligatures to be\n  not be centered horizontally (:iss:`2591`)\n\n- By default, double clicking no longer considers the : as part of words, see\n  :opt:`select_by_word_characters` (:iss:`2602`)\n\n- Fix a regression that caused clicking in the padding/margins of windows in\n  the stack layout to switch the window to the first window (:iss:`2604`)\n\n- macOS: Fix a regression that broke drag and drop (:iss:`2605`)\n\n- Report modifier key state when sending wheel events to the terminal program\n\n- Fix kitty @ send-text not working with text larger than 1024 bytes when using\n  :option:`kitty --listen-on` (:iss:`2607`)\n\n- Wayland: Fix OS window title not updating for hidden windows (:iss:`2629`)\n\n- Fix :opt:`background_tint` making the window semi-transparent (:iss:`2618`)\n\n\n0.17.3 [2020-04-23]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow individually setting margins and padding for each edge (left, right,\n  top, bottom). Margins can also be controlled per window via remote control\n  (:iss:`2546`)\n\n- Fix reverse video not being rendered correctly when using transparency or a\n  background image (:iss:`2419`)\n\n- Allow mapping arbitrary remote control commands to key presses in\n  :file:`kitty.conf`\n\n- X11: Fix crash when doing drag and drop from some applications (:iss:`2505`)\n\n- Fix :option:`launch --stdin-add-formatting` not working (:iss:`2512`)\n\n- Update to Unicode 13.0 (:iss:`2513`)\n\n- Render country flags designated by a pair of unicode codepoints\n  in two cells instead of four.\n\n- diff kitten: New option to control the background color for filler lines in\n  the margin (:iss:`2518`)\n\n- Fix specifying options for layouts in the startup session file not working\n  (:iss:`2520`)\n\n- macOS: Fix incorrect horizontal positioning of some full-width East Asian characters\n  (:iss:`1457`)\n\n- macOS: Render multi-cell PUA characters centered, matching behavior on other\n  platforms\n\n- Linux: Ignore keys if they are designated as layout/group/mode switch keys\n  (:iss:`2519`)\n\n- Marks: Fix marks not handling wide characters and tab characters correctly\n  (:iss:`2534`)\n\n- Add a new :opt:`listen_on` option in kitty.conf to set :option:`kitty --listen-on`\n  globally. Also allow using environment variables in this option (:iss:`2569`).\n\n- Allow sending mouse events in kittens (:pull:`2538`)\n\n- icat kitten: Fix display of 16-bit depth images (:iss:`2542`)\n\n- Add ncurses specific terminfo definitions for strikethrough (:pull:`2567`)\n\n- Fix a regression in 0.17 that broke displaying graphics over SSH\n  (:iss:`2568`)\n\n- Fix :option:`--title` not being applied at window creation time (:iss:`2570`)\n\n\n0.17.2 [2020-03-29]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add a :option:`launch --watcher` option that allows defining callbacks\n  that are called for various events in the window's life-cycle (:iss:`2440`)\n\n- Fix a regression in 0.17 that broke drawing of borders with non-minimal\n  borders (:iss:`2474`)\n\n- Hints kitten: Allow copying to primary selection as well as clipboard\n  (:pull:`2487`)\n\n- Add a new mappable action ``close_other_windows_in_tab`` to close all but the\n  active window (:iss:`2484`)\n\n- Hints kitten: Adjust the default regex used to detect line numbers to handle\n  line+column numbers (:iss:`2268`)\n\n- Fix blank space at the start of tab bar in the powerline style when first tab is\n  inactive (:iss:`2478`)\n\n- Fix regression causing incorrect rendering of separators in tab bar when\n  defining a tab bar background color (:pull:`2480`)\n\n- Fix a regression in 0.17 that broke the kitty @ launch remote command and\n  also broke the --tab-title option when creating a new tab. (:iss:`2488`)\n\n- Linux: Fix selection of fonts with multiple width variants not preferring\n  the normal width faces (:iss:`2491`)\n\n\n0.17.1 [2020-03-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix :opt:`cursor_underline_thickness` not working (:iss:`2465`)\n\n- Fix a regression in 0.17 that caused tab bar background to be rendered after\n  the last tab as well (:iss:`2464`)\n\n- macOS: Fix a regression in 0.17 that caused incorrect variants to be\n  automatically selected for some fonts (:iss:`2462`)\n\n- Fix a regression in 0.17 that caused kitty @ set-colors to require setting\n  cursor_text_color (:iss:`2470`)\n\n\n0.17.0 [2020-03-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- :ref:`splits_layout` to arrange windows in arbitrary splits\n  (:iss:`2308`)\n\n- Add support for specifying a background image, see :opt:`background_image`\n  (:iss:`163` and :pull:`2326`; thanks to Fredrick Brennan.)\n\n- A new :opt:`background_tint` option to darken the background under the text\n  area when using background images and/or transparent windows.\n\n- Allow selection of single cells with the mouse. Also improve mouse selection\n  to follow semantics common to most programs (:iss:`945`)\n\n- New options :opt:`cursor_beam_thickness` and :opt:`cursor_underline_thickness` to control the thickness of the\n  beam and underline cursors (:iss:`2337` and :pull:`2342`)\n\n- When the application running in the terminal grabs the mouse, pass middle\n  clicks to the application unless `terminal_select_modifiers` are\n  pressed (:iss:`2368`)\n\n- A new ``copy_and_clear_or_interrupt`` function (:iss:`2403`)\n\n- X11: Fix arrow mouse cursor using right pointing instead of the default left\n  pointing arrow (:iss:`2341`)\n\n- Allow passing the currently active kitty window id in the launch command\n  (:iss:`2391`)\n\n- unicode input kitten: Allow pressing :kbd:`ctrl+tab` to change the input mode\n  (:iss:`2343`)\n\n- Fix a bug that prevented using custom functions with the new marks feature\n  (:iss:`2344`)\n\n- Make the set of URL prefixes that are recognized while hovering with the\n  mouse configurable (:iss:`2416`)\n\n- Fix border/margin/padding sizes not being recalculated on DPI change\n  (:iss:`2346`)\n\n- diff kitten: Fix directory diffing with removed binary files failing\n  (:iss:`2378`)\n\n- macOS: Fix menubar title not updating on OS Window focus change (:iss:`2350`)\n\n- Fix rendering of combining characters with fonts that have glyphs for\n  precomposed characters but not decomposed versions (:iss:`2365`)\n\n- Fix incorrect rendering of selection when using rectangular select and\n  scrolling (:iss:`2351`)\n\n- Allow setting WM_CLASS and WM_NAME when creating new OS windows with the\n  launch command (:option:`launch --os-window-class`)\n\n- macOS: When switching input method while a pending multi-key input is in\n  progress, clear the pending input (:iss:`2358`)\n\n- Fix a regression in the previous release that broke switching to neighboring windows\n  in the Grid layout when there are less than four windows (:iss:`2377`)\n\n- Fix colors in scrollback pager off if the window has redefined terminal\n  colors using escape codes (:iss:`2381`)\n\n- Fix selection not updating properly while scrolling (:iss:`2442`)\n\n- Allow extending selections by dragging with right button pressed\n  (:iss:`2445`)\n\n- Workaround for bug in less that causes colors to reset at wrapped lines\n  (:iss:`2381`)\n\n- X11/Wayland: Allow drag and drop of text/plain in addition to text/uri-list\n  (:iss:`2441`)\n\n- Dont strip :code:`&` and :code:`-` from the end of URLs (:iss:`2436`)\n\n- Fix ``@selection`` placeholder not working with launch command (:iss:`2417`)\n\n- Drop support for python 3.5\n\n- Wayland: Fix a crash when drag and dropping into kitty (:iss:`2432`)\n\n- diff kitten: Fix images lingering as blank rectangles after the kitten quits\n  (:iss:`2449`)\n\n- diff kitten: Fix images losing position when scrolling using mouse\n  wheel/touchpad\n\n\n0.16.0 [2020-01-28]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new :doc:`marks` feature that allows highlighting and scrolling to arbitrary\n  text in the terminal window.\n\n- hints kitten: Allow pressing :sc:`goto_file_line` to quickly open\n  the selected file at the selected line in vim or a configurable editor (:iss:`2268`)\n\n- Allow having more than one full height window in the :code:`tall` layout\n  (:iss:`2276`)\n\n- Allow choosing OpenType features for individual fonts via the\n  :opt:`font_features` option. (:pull:`2248`)\n\n- Wayland: Fix a freeze in rare circumstances when having multiple OS Windows\n  (:iss:`2307` and :iss:`1722`)\n\n- Wayland: Fix window titles being set to very long strings on the order of 8KB\n  causing a crash (:iss:`1526`)\n\n- Add an option :opt:`force_ltr` to turn off the display of text in RTL scripts\n  in right-to-left order (:pull:`2293`)\n\n- Allow opening new tabs/windows before the current tab/window as well as after\n  it with the :option:`launch --location` option.\n\n- Add a :opt:`resize_in_steps` option that can be used to resize the OS window\n  in steps as large as character cells (:pull:`2131`)\n\n- When triple-click+dragging to select multiple lines, extend the selection\n  of the first line to match the rest on the left (:pull:`2284`)\n\n- macOS: Add a :code:`titlebar-only` setting to\n  :opt:`hide_window_decorations` to only hide the title bar (:pull:`2286`)\n\n- Fix a segfault when using ``--debug-config`` with maps (:iss:`2270`)\n\n- ``goto_tab`` now maps numbers larger than the last tab to the last tab\n  (:iss:`2291`)\n\n- Fix URL detection not working for urls of the form scheme:///url\n  (:iss:`2292`)\n\n- When windows are semi-transparent and all contain graphics, correctly render\n  them. (:iss:`2310`)\n\n\n0.15.1 [2019-12-21]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix a crash/incorrect rendering when detaching a window in some circumstances\n  (:iss:`2173`)\n\n- hints kitten: Add an option :option:`kitty +kitten hints --ascending` to\n  control if the hints numbers increase or decrease from top to bottom\n\n- Fix :opt:`background_opacity` incorrectly applying to selected text and\n  reverse video text (:iss:`2177`)\n\n- Add a new option :opt:`tab_bar_background` to specify a different color\n  for the tab bar (:iss:`2198`)\n\n- Add a new option :opt:`active_tab_title_template` to specify a different\n  template for active tab titles (:iss:`2198`)\n\n- Fix lines at the edge of the window at certain windows sizes when drawing\n  images on a transparent window (:iss:`2079`)\n\n- Fix window not being rendered for the first time until some input has been\n  received from child process (:iss:`2216`)\n\n\n0.15.0 [2019-11-27]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add a new action :ref:`detach_window <detach_window>` that can be used to move the current\n  window into a different tab (:iss:`1310`)\n\n- Add a new action :doc:`launch <launch>` that unifies launching of processes\n  in new kitty windows/tabs.\n\n- Add a new style ``powerline`` for tab bar rendering, see :opt:`tab_bar_style` (:pull:`2021`)\n\n- Allow changing colors by mapping a keyboard shortcut to read a kitty config\n  file with color definitions. See the :doc:`FAQ <faq>` for details\n  (:iss:`2083`)\n\n- hints kitten: Allow completely customizing the matching and actions performed\n  by the kitten using your own script (:iss:`2124`)\n\n- Wayland: Fix key repeat not being stopped when focus leaves window. This is\n  expected behavior on Wayland, apparently (:iss:`2014`)\n\n- When drawing unicode symbols that are followed by spaces, use multiple cells\n  to avoid resized or cut-off glyphs (:iss:`1452`)\n\n- diff kitten: Allow diffing remote files easily via ssh (:iss:`727`)\n\n- unicode input kitten: Add an option :option:`kitty +kitten unicode_input\n  --emoji-variation` to control the presentation variant of selected emojis\n  (:iss:`2139`)\n\n- Add specialised rendering for a few more box powerline and unicode symbols\n  (:pull:`2074` and :pull:`2021`)\n\n- Add a new socket only mode for :opt:`allow_remote_control`. This makes\n  it possible for programs running on the local machine to control kitty\n  but not programs running over ssh.\n\n- hints kitten: Allow using named groups in the regular expression. The named\n  groups are passed to the invoked program for further processing.\n\n- Fix a regression in 0.14.5 that caused rendering of private use glyphs\n  with and without spaces to be identical (:iss:`2117`)\n\n- Wayland: Fix incorrect scale used when first creating an OS window\n  (:iss:`2133`)\n\n- macOS: Disable mouse hiding by default as getting it to work robustly\n  on Cocoa is too much effort (:iss:`2158`)\n\n\n0.14.6 [2019-09-25]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix a regression in the previous release that caused a crash when\n  pressing a unprintable key, such as the POWER key (:iss:`1997`)\n\n- Fix a regression in the previous release that caused kitty to not always\n  respond to DPI changes (:pull:`1999`)\n\n\n0.14.5 [2019-09-23]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Implement a hack to (mostly) preserve tabs when cat-ting a file with them and then\n  copying the text or passing screen contents to another program (:iss:`1829`)\n\n- When all visible windows have the same background color, use that as the\n  color for the global padding, instead of the configured background color\n  (:iss:`1957`)\n\n- When resetting the terminal, also reset parser state, this allows easy\n  recovery from incomplete escape codes (:iss:`1961`)\n\n- Allow mapping keys commonly found on European keyboards (:pull:`1928`)\n\n- Fix incorrect rendering of some symbols when followed by a space while using\n  the PowerLine font which does not have a space glyph (:iss:`1225`)\n\n- Linux: Allow using fonts with spacing=90 in addition to fonts with\n  spacing=100 (:iss:`1968`)\n\n- Use selection foreground color for underlines as well (:iss:`1982`)\n\n\n0.14.4 [2019-08-31]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- hints kitten: Add a :option:`kitty +kitten hints --alphabet` option to\n  control what alphabets are used for hints (:iss:`1879`)\n\n- hints kitten: Allow specifying :option:`kitty +kitten hints --program`\n  multiple times to run multiple programs  (:iss:`1879`)\n\n- Add a :opt:`kitten_alias` option that can be used to alias kitten invocation\n  for brevity and to change kitten option defaults globally (:iss:`1879`)\n\n- macOS: Add an option :opt:`macos_show_window_title_in` to control\n  showing the window title in the menubar/titlebar (:pull:`1837`)\n\n- macOS: Allow drag and drop of text from other applications into kitty\n  (:pull:`1921`)\n\n- When running kittens, use the colorscheme of the current window\n  rather than the configured colorscheme (:iss:`1906`)\n\n- Don't fail to start if running the shell to read the EDITOR env var fails\n  (:iss:`1869`)\n\n- Disable the ``liga`` and ``dlig`` OpenType features for broken fonts\n  such as Nimbus Mono.\n\n- Fix a regression that broke setting background_opacity via remote control\n  (:iss:`1895`)\n\n- Fix piping PNG images into the icat kitten not working (:iss:`1920`)\n\n- When the OS returns a fallback font that does not actually contain glyphs\n  for the text, do not exhaust the list of fallback fonts (:iss:`1918`)\n\n- Fix formatting attributes not reset across line boundaries when passing\n  buffer as ANSI (:iss:`1924`)\n\n\n0.14.3 [2019-07-29]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Remote control: Add a command `kitty @ scroll-window` to scroll windows\n\n- Allow passing a ``!neighbor`` argument to the new_window mapping to open a\n  new window next to the active window (:iss:`1746`)\n\n- Document the kitty remote control protocol (:iss:`1646`)\n\n- Add a new option :opt:`pointer_shape_when_grabbed` that allows you to control\n  the mouse pointer shape when the terminal programs grabs the pointer\n  (:iss:`1808`)\n\n- Add an option `terminal_select_modifiers` to control which modifiers\n  are used to override mouse selection even when a terminal application has\n  grabbed the mouse (:iss:`1774`)\n\n- When piping data to a child in the pipe command do it in a thread so as not\n  to block the UI (:iss:`1708`)\n\n- unicode_input kitten: Fix a regression that broke using indices to select\n  recently used symbols.\n\n- Fix a regression that caused closing an overlay window to focus\n  the previously focused window rather than the underlying window (:iss:`1720`)\n\n- macOS: Reduce energy consumption when idle by shutting down Apple's display\n  link thread after 30 second of inactivity (:iss:`1763`)\n\n- Linux: Fix incorrect scaling for fallback fonts when the font has an\n  underscore that renders out of bounds (:iss:`1713`)\n\n- macOS: Fix finding fallback font for private use unicode symbols not working\n  reliably (:iss:`1650`)\n\n- Fix an out of bounds read causing a crash when selecting text with the mouse\n  in the alternate screen mode (:iss:`1578`)\n\n- Linux: Use the system \"bell\" sound for the terminal bell. Adds libcanberra\n  as a new dependency to play the system sound.\n\n- macOS: Fix a rare deadlock causing kitty to hang (:iss:`1779`)\n\n- Linux: Fix a regression in 0.14.0 that caused the event loop to tick\n  continuously, wasting CPU even when idle (:iss:`1782`)\n\n- ssh kitten: Make argument parsing more like ssh (:iss:`1787`)\n\n- When using :opt:`strip_trailing_spaces` do not remove empty lines\n  (:iss:`1802`)\n\n- Fix a crash when displaying very large number of images (:iss:`1825`)\n\n\n0.14.2 [2019-06-09]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add an option :opt:`placement_strategy` to control how the cell area is\n  aligned inside the window when the window size is not an exact multiple\n  of the cell size (:pull:`1670`)\n\n- hints kitten: Add a :option:`kitty +kitten hints --multiple-joiner` option to\n  control how multiple selections are serialized when copying to clipboard\n  or inserting into the terminal. You can have them on separate lines,\n  separated by arbitrary characters, or even serialized as JSON (:iss:`1665`)\n\n- macOS: Fix a regression in the previous release that broke using\n  :kbd:`ctrl+shift+tab` (:iss:`1671`)\n\n- panel kitten: Fix the contents of the panel kitten not being positioned\n  correctly on the vertical axis\n\n- icat kitten: Fix a regression that broke passing directories to icat\n  (:iss:`1683`)\n\n- clipboard kitten: Add a :option:`kitty +kitten clipboard --wait-for-completion`\n  option to have the kitten wait till copying to clipboard is complete\n  (:iss:`1693`)\n\n- Allow using the :doc:`pipe <pipe>` command to send screen and scrollback\n  contents directly to the clipboard (:iss:`1693`)\n\n- Linux: Disable the Wayland backend on GNOME by default as GNOME has no\n  support for server side decorations. Can be controlled by\n  :opt:`linux_display_server`.\n\n- Add an option to control the default :opt:`update_check_interval` when\n  building kitty packages\n\n- Wayland: Fix resizing the window on a compositor that does not provide\n  server side window decorations, such a GNOME or Weston not working\n  correctly (:iss:`1659`)\n\n- Wayland: Fix crash when enabling disabling monitors on sway (:iss:`1696`)\n\n\n0.14.1 [2019-05-29]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add an option :opt:`command_on_bell` to run an arbitrary command when\n  a bell occurs (:iss:`1660`)\n\n- Add a shortcut to toggle maximized window state :sc:`toggle_maximized`\n\n- Add support for the underscore key found in some keyboard layouts\n  (:iss:`1639`)\n\n- Fix a missing newline when using the pipe command between the\n  scrollback and screen contents (:iss:`1642`)\n\n- Fix colors not being preserved when using the pipe command with\n  the pager history buffer (:pull:`1657`)\n\n- macOS: Fix a regression that could cause rendering of a kitty window\n  to occasionally freeze in certain situations, such as moving it between\n  monitors or transitioning from/to fullscreen (:iss:`1641`)\n\n- macOS: Fix a regression that caused :kbd:`cmd+v` to double up in the dvorak\n  keyboard layout (:iss:`1652`)\n\n- When resizing and only a single window is present in the current layout,\n  use that window's background color to fill in the blank areas.\n\n- Linux: Automatically increase cell height if the font being used is broken\n  and draws the underscore outside the bounding box (:iss:`690`)\n\n- Wayland: Fix maximizing the window on a compositor that does not provide\n  server side window decorations, such a GNOME or Weston not working\n  (:iss:`1662`)\n\n\n0.14.0 [2019-05-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: The default behavior of the Option key has changed. It now generates\n  unicode characters rather than acting as the :kbd:`Alt` modifier. See\n  :opt:`macos_option_as_alt`.\n\n- Support for an arbitrary number of internal clipboard buffers to copy/paste\n  from, see (:ref:`cpbuf`)\n\n- Allow using the new private internal clipboard buffers with the\n  :opt:`copy_on_select` option (:iss:`1390`)\n\n- macOS: Allow opening new kitty tabs/top-level windows from Finder\n  (:pull:`1350`)\n\n- Add an option :opt:`disable_ligatures` to disable\n  multi-character ligatures under the cursor to make editing easier\n  or disable them completely (:iss:`461`)\n\n- Allow creating new OS windows in session files (:iss:`1514`)\n\n- Allow setting OS window size in session files\n\n- Add an option :opt:`tab_switch_strategy` to control which\n  tab becomes active when the current tab is closed (:pull:`1524`)\n\n- Allow specifying a value of ``none`` for the :opt:`selection_foreground`\n  which will cause kitty to not change text color in selections (:iss:`1358`)\n\n- Make live resizing of OS windows smoother and add an option\n  ``resize_draw_strategy`` to control what is drawn while a\n  resize is in progress.\n\n- macOS: Improve handling of IME extended input. Compose characters\n  are now highlighted and the IME panel moves along with the text\n  (:pull:`1586`). Also fixes handling of delete key in Chinese IME\n  (:iss:`1461`)\n\n- When a window is closed, switch focus to the previously active window (if\n  any) instead of picking the previous window in the layout (:iss:`1450`)\n\n- icat kitten: Add support for displaying images at http(s) URLs (:iss:`1340`)\n\n- A new option :opt:`strip_trailing_spaces` to optionally remove trailing\n  spaces from lines when copying to clipboard.\n\n- A new option :opt:`tab_bar_min_tabs` to control how many tabs must be\n  present before the tab-bar is shown (:iss:`1382`)\n\n- Automatically check for new releases and notify when an update is available,\n  via the system notification facilities. Can be controlled by\n  :opt:`update_check_interval` (:iss:`1342`)\n\n- macOS: Fix :kbd:`cmd+period` key not working (:iss:`1318`)\n\n- macOS: Add an option `macos_show_window_title_in_menubar` to not\n  show the current window title in the menu-bar (:iss:`1066`)\n\n- macOS: Workaround for cocoa bug that could cause the mouse cursor to become\n  hidden in other applications in rare circumstances (:iss:`1218`)\n\n- macOS: Allow assigning only the left or right :kbd:`Option` key to work as the\n  :kbd:`Alt` key. See :opt:`macos_option_as_alt` for details (:iss:`1022`)\n\n- Fix using remote control to set cursor text color causing errors when\n  creating new windows (:iss:`1326`)\n\n- Fix window title for minimized windows not being updated (:iss:`1332`)\n\n- macOS: Fix using multi-key sequences to input text ignoring the\n  first few key presses if the sequence is aborted (:iss:`1311`)\n\n- macOS: Add a number of common macOS keyboard shortcuts\n\n- macOS: Reduce energy consumption by not rendering occluded windows\n\n- Fix scrollback pager history not being cleared when clearing the\n  main scrollback buffer (:iss:`1387`)\n\n- macOS: When closing a top-level window only switch focus to the previous kitty\n  window if it is on the same workspace (:iss:`1379`)\n\n- macOS: Fix :opt:`sync_to_monitor` not working on Mojave.\n\n- macOS: Use the system cursor blink interval by default\n  :opt:`cursor_blink_interval`.\n\n- Wayland: Use the kitty Wayland backend by default. Can be switched back\n  to using XWayland by setting the environment variable:\n  ``KITTY_DISABLE_WAYLAND=1``\n\n- Add a ``no-append`` setting to :opt:`clipboard_control` to disable\n  the kitty copy concatenation protocol extension for OSC 52.\n\n- Update to using the Unicode 12 standard\n\n- Unicode input kitten: Allow using the arrow keys in code mode to go to next\n  and previous unicode symbol.\n\n- macOS: Fix specifying initial window size in cells not working correctly on\n  Retina screens (:iss:`1444`)\n\n- Fix a regression in version 0.13.0 that caused background colors of space\n  characters after private use unicode characters to not be respected\n  (:iss:`1455`)\n\n- Only update the selected text to clipboard when the selection is finished,\n  not continuously as it is updated. (:iss:`1460`)\n\n- Allow setting :opt:`active_border_color` to ``none`` to not draw a border\n  around the active window (:iss:`805`)\n\n- Use negative values for :opt:`mouse_hide_wait` to hide the mouse cursor\n  immediately when pressing a key (:iss:`1534`)\n\n- When encountering errors in :file:`kitty.conf` report them to the user\n  instead of failing to start.\n\n- Allow the user to control the resize debounce time via\n  :opt:`resize_debounce_time`.\n\n- Remote control: Make the :ref:`at-set-font-size` command more capable.\n  It can now increment font size and reset it. It also only acts on the\n  active top-level window, by default (:iss:`1581`)\n\n- When launching child processes set the :code:`PWD` environment variable\n  (:iss:`1595`)\n\n- X11: use the window manager's native full-screen implementation when\n  making windows full-screen (:iss:`1605`)\n\n- Mouse selection: When extending by word, fix extending selection to non-word\n  characters not working well (:iss:`1616`)\n\n\n0.13.3 [2019-01-19]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- icat kitten: Add a ``--stdin`` option to control if image data is read from\n  STDIN (:iss:`1308`)\n\n- hints kitten: Start hints numbering at one instead of zero by default. Added\n  an option ``--hints-offset`` to control it. (:iss:`1289`)\n\n- Fix a regression in the previous release that broke using ``background`` for\n  :opt:`cursor_text_color` (:iss:`1288`)\n\n- macOS: Fix dragging kitty window tabs in traditional full screen mode causing\n  crashes (:iss:`1296`)\n\n- macOS: Ensure that when running from a bundle, the bundle kitty exe is\n  preferred over any kitty in PATH (:iss:`1280`)\n\n- macOS: Fix a regression that broke mapping of :kbd:`ctrl+tab` (:iss:`1304`)\n\n- Add a list of user-created kittens to the docs\n\n- Fix a regression that broke changing mouse wheel scroll direction with\n  negative :opt:`wheel_scroll_multiplier` values in full-screen applications\n  like vim (:iss:`1299`)\n\n- Fix :opt:`background_opacity` not working with pure white backgrounds\n  (:iss:`1285`)\n\n- macOS: Fix \"New OS Window\" dock action not working when kitty is not focused\n  (:iss:`1312`)\n\n- macOS: Add aliases for close window and new tab actions that conform to common\n  Apple shortcuts for these actions (:iss:`1313`)\n\n- macOS: Fix some kittens causing 100% CPU usage\n\n\n0.13.2 [2019-01-04]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add a new option :opt:`tab_title_template` to control how tab titles\n  are formatted. In particular the template can be used to display\n  the tab number next to the title (:iss:`1223`)\n\n- Report the current foreground processes as well as the original child process,\n  when using `kitty @ ls`\n\n- Use the current working directory of the foreground process for the\n  `*_with_cwd` actions that open a new window with the current working\n  directory.\n\n- Add a new ``copy_or_interrupt`` action that can be mapped to kbd:`ctrl+c`. It\n  will copy if there is a selection and interrupt otherwise (:iss:`1286`)\n\n- Fix setting :opt:`background_opacity` causing window margins/padding to be slightly\n  different shade from background (:iss:`1221`)\n\n- Handle keyboards with a \"+\" key (:iss:`1224`)\n\n- Fix Private use Unicode area characters followed by spaces at the end of text\n  not being rendered correctly (:iss:`1210`)\n\n- macOS: Add an entry to the dock menu to open a new OS window (:iss:`1242`)\n\n- macOS: Fix scrolling very slowly with wheel mice not working (:iss:`1238`)\n\n- Fix changing :opt:`cursor_text_color` via remote control not working\n  (:iss:`1229`)\n\n- Add an action to resize windows that can be mapped to shortcuts in :file:`kitty.conf`\n  (:pull:`1245`)\n\n- Fix using the ``new_tab !neighbor`` action changing the order of the\n  non-neighboring tabs (:iss:`1256`)\n\n- macOS: Fix momentum scrolling continuing when changing the active window/tab\n  (:iss:`1267`)\n\n\n0.13.1 [2018-12-06]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix passing input via the pipe action to a program without a window not\n  working.\n\n- Linux: Fix a regression in the previous release that caused automatic\n  selection of bold/italic fonts when using aliases such as \"monospace\" to not\n  work (:iss:`1209`)\n\n- Fix resizing window smaller and then restoring causing some wrapped lines to not\n  be properly unwrapped (:iss:`1206`)\n\n\n0.13.0 [2018-12-05]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add an option :opt:`scrollback_pager_history_size` to tell kitty to store\n  extended scrollback to use when viewing the scrollback buffer in a pager\n  (:iss:`970`)\n\n- Modify the kittens sub-system to allow creating custom kittens without any\n  user interface. This is useful for creating more complex actions that can\n  be bound to key presses in :file:`kitty.conf`. See\n  doc:`kittens/custom`. (:iss:`870`)\n\n- Add a new ``nth_window`` action that can be used to go to the nth window and\n  also previously active windows, using negative numbers. Similarly,\n  ``goto_tab`` now accepts negative numbers to go to previously active tabs\n  (:iss:`1040`)\n\n- Allow hiding the tab bar completely, by setting :opt:`tab_bar_style` to\n  ``hidden``. (:iss:`1014`)\n\n- Allow private use unicode characters to stretch over more than a single\n  neighboring space (:pull:`1036`)\n\n- Add a new :opt:`touch_scroll_multiplier` option to modify the amount\n  scrolled by high precision scrolling devices such as touchpads (:pull:`1129`)\n\n- icat kitten: Implement reading image data from STDIN, if STDIN is not\n  connected to a terminal (:iss:`1130`)\n\n- hints kitten: Insert trailing spaces after matches when using the\n  ``--multiple`` option. Also add a separate ``--add-trailing-space``\n  option to control this behavior (:pull:`1132`)\n\n- Fix the ``*_with_cwd`` actions using the cwd of the overlay window rather\n  than the underlying window's cwd (:iss:`1045`)\n\n- Fix incorrect key repeat rate on wayland (:pull:`1055`)\n\n- macOS: Fix drag and drop of files not working on Mojave (:iss:`1058`)\n\n- macOS: Fix IME input for East Asian languages (:iss:`910`)\n\n- macOS: Fix rendering frames-per-second very low when processing\n  large amounts of input in small chunks (:pull:`1082`)\n\n- macOS: Fix incorrect text sizes calculated when using an external display\n  that is set to mirror the main display (:iss:`1056`)\n\n- macOS: Use the system default double click interval (:pull:`1090`)\n\n- macOS: Fix touch scrolling sensitivity low on retina screens (:iss:`1112`)\n\n- Linux: Fix incorrect rendering of some fonts when hinting is disabled at\n  small sizes (:iss:`1173`)\n\n- Linux: Fix match rules used as aliases in Fontconfig configuration not being\n  respected (:iss:`1085`)\n\n- Linux: Fix a crash when using the GNU Unifont as a fallback font\n  (:iss:`1087`)\n\n- Wayland: Fix copying from hidden kitty windows hanging (:iss:`1051`)\n\n- Wayland: Add support for the primary selection protocol\n  implemented by some compositors (:pull:`1095`)\n\n- Fix expansion of env vars not working in the :opt:`env` directive\n  (:iss:`1075`)\n\n- Fix :opt:`mouse_hide_wait` only taking effect after an event such as cursor\n  blink or key press (:iss:`1073`)\n\n- Fix the ``set_background_opacity`` action not working correctly\n  (:pull:`1147`)\n\n- Fix second cell of emoji created using variation selectors not having\n  the same attributes as the first cell (:iss:`1109`)\n\n- Fix focusing neighboring windows in the grid layout with less than 4 windows\n  not working (:iss:`1115`)\n\n- Fix :kbd:`ctrl+shift+special` key not working in normal and application keyboard\n  modes (:iss:`1114`)\n\n- Add a terminfo entry for full keyboard mode.\n\n- Fix incorrect text-antialiasing when using very low background opacity\n  (:iss:`1005`)\n\n- When double or triple clicking ignore clicks if they are \"far\" from each\n  other (:iss:`1093`)\n\n- Follow xterm's behavior for the menu key (:iss:`597`)\n\n- Fix hover detection of URLs not working when hovering over the first colon\n  and slash characters in short URLs (:iss:`1201`)\n\n\n0.12.3 [2018-09-29]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- macOS: Fix kitty window not being rendered on macOS Mojave until the window is\n  moved or resized at least once (:iss:`887`)\n\n- Unicode input: Fix an error when searching for the string 'fir' (:iss:`1035`)\n\n\n0.12.2 [2018-09-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new ``last_used_layout`` function that can be mapped to a shortcut to\n  switch to the previously used window layout (:iss:`870`)\n\n- New ``neighboring_window`` and ``move_window`` functions to switch to\n  neighboring windows in the current layout, and move them around, similar to\n  window movement in vim (:iss:`916`)\n\n- A new ``pipe`` function that can be used to pipe the contents of the screen\n  and scrollback buffer to any desired program running in a new window, tab or\n  overlay window. (:iss:`933`)\n\n- Add a new :option:`kitty --start-as` command line flag to start kitty\n  full-screen/maximized/minimized. This replaces the ``--start-in-fullscreen``\n  flag introduced in the previous release (:iss:`935`)\n\n- When mapping the ``new_tab`` action allow specifying that the tab should open\n  next to the current tab instead of at the end of the tabs list (:iss:`979`)\n\n- macOS: Add a new :opt:`macos_thicken_font` to make text rendering\n  on macs thicker, which makes it similar to the result of\n  sub-pixel antialiasing (:pull:`950`)\n\n- macOS: Add an option :opt:`macos_traditional_fullscreen` to make\n  full-screening of kitty windows much faster, but less pretty. (:iss:`911`)\n\n- Fix a bug causing incorrect line ordering when viewing the scrollback buffer\n  if the scrollback buffer is full (:iss:`960`)\n\n- Fix drag-scrolling not working when the mouse leaves the window confines\n  (:iss:`917`)\n\n- Workaround for broken editors like nano that cannot handle newlines in pasted text\n  (:iss:`994`)\n\n- Linux: Ensure that the python embedded in the kitty binary build uses\n  UTF-8 mode to process command-line arguments (:iss:`924`)\n\n- Linux: Handle fonts that contain monochrome bitmaps (such as the Terminus TTF\n  font) (:pull:`934`)\n\n- Have the :option:`kitty --title` flag apply to all windows created\n  using :option:`kitty --session` (:iss:`921`)\n\n- Revert change for backspacing of wide characters in the previous release,\n  as it breaks backspacing in some wide character aware programs (:iss:`875`)\n\n- Fix kitty @set-colors not working for tab backgrounds when using the `fade` tabbar style\n  (:iss:`937`)\n\n- macOS: Fix resizing semi-transparent windows causing the windows to be\n  invisible during the resize (:iss:`941`)\n\n- Linux: Fix window icon not set on X11 for the first OS window (:iss:`961`)\n\n- macOS: Add an :opt:`macos_custom_beam_cursor` option to use a special\n  mouse cursor image that can be seen on both light and dark backgrounds\n  (:iss:`359`)\n\n- Remote control: Fix the ``focus_window`` command not focusing the\n  top-level OS window of the specified kitty window (:iss:`1003`)\n\n- Fix using :opt:`focus_follows_mouse` causing text selection with the\n  mouse to malfunction when using multiple kitty windows (:iss:`1002`)\n\n\n0.12.1 [2018-09-08]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add a new ``--start-in-fullscreen`` command line flag to start\n  kitty in full screen mode (:iss:`856`)\n\n- macOS: Fix a character that cannot be rendered in any font causing\n  font fallback for all subsequent characters that cannot be rendered in the\n  main font to fail (:iss:`799`)\n\n- Linux: Do not enable IME input via ibus unless the ``GLFW_IM_MODULE=ibus``\n  environment variable is set. IME causes key processing latency and even\n  missed keystrokes for many people, so it is now off by default.\n\n- Fix backspacing of wide characters in wide-character unaware programs not working (:iss:`875`)\n\n- Linux: Fix number pad arrow keys not working when Numlock is off (:iss:`857`)\n\n- Wayland: Implement support for clipboard copy/paste (:iss:`855`)\n\n- Allow mapping shortcuts using the raw key code from the OS (:iss:`848`)\n\n- Allow mapping of individual key-presses without modifiers as shortcuts\n\n- Fix legacy invocation of icat as `kitty icat` not working (:iss:`850`)\n\n- Improve rendering of wavy underline at small font sizes (:iss:`853`)\n\n- Fix a regression in 0.12.0 that broke dynamic resizing of layouts (:iss:`860`)\n\n- Wayland: Allow using the :code:`kitty --class` command line flag\n  to set the app id (:iss:`862`)\n\n- Add completion of the kitty command for the fish shell (:pull:`829`)\n\n- Linux: Fix XCompose rules with no defined symbol not working (:iss:`880`)\n\n- Linux: Fix crash with some Nvidia drivers when creating tabs in the first\n  top level-window after creating a second top-level window. (:iss:`873`)\n\n- macOS: Diff kitten: Fix syntax highlighting not working because of\n  a bug in the 0.12.0 macOS package\n\n\n0.12.0 [2018-09-01]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Preserve the mouse selection even when the contents of the screen are\n  scrolled or overwritten provided the new text does not intersect the\n  selected lines.\n\n- Linux: Implement support for Input Method Extensions (multilingual input\n  using standard keyboards) via `IBus\n  <https://github.com/ibus/ibus/wiki/ReadMe>`_ (:iss:`469`)\n\n- Implement completion for the kitty command in bash and zsh. See\n  :ref:`shell_integration`.\n\n- Render the text under the cursor in a fixed color, configurable via\n  the option :opt:`cursor_text_color` (:iss:`126`)\n\n- Add an option :opt:`env` to set environment variables in child processes\n  from kitty.conf\n\n- Add an action to the ``clear_terminal`` function to scroll the screen\n  contents into the scrollback buffer (:iss:`1113`)\n\n- Implement high precision scrolling with the trackpad on platforms such as\n  macOS and Wayland that implement it. (:pull:`819`)\n\n- macOS: Allow scrolling window contents using mouse wheel/trackpad even when the\n  window is not the active window (:iss:`729`)\n\n- Remote control: Allow changing the current window layout with a new\n  :ref:`at-goto-layout` command (:iss:`845`)\n\n- Remote control: Allow matching windows by the environment variables of their\n  child process as well\n\n- Allow running kittens via the remote control system (:iss:`738`)\n\n- Allow enabling remote control in only some kitty windows\n\n- Add a keyboard shortcut to reset the terminal (:sc:`reset_terminal`). It\n  takes parameters so you can define your own shortcuts to clear the\n  screen/scrollback also (:iss:`747`)\n\n- Fix one-pixel line appearing at window edges at some window sizes when\n  displaying images with background opacity enabled (:iss:`741`)\n\n- diff kitten: Fix error when right hand side file is binary and left hand side\n  file is text (:pull:`752`)\n\n- kitty @ new-window: Add a new option :option:`kitten @ new-window --window-type`\n  to create top-level OS windows (:iss:`770`)\n\n- macOS: The :opt:`focus_follows_mouse` option now also works across top-level kitty OS windows\n  (:iss:`754`)\n\n- Fix detection of URLs in HTML source code (URLs inside quotes) (:iss:`785`)\n\n- Implement support for emoji skin tone modifiers (:iss:`787`)\n\n- Round-trip the zwj unicode character. Rendering of sequences containing zwj\n  is still not implemented, since it can cause the collapse of an unbounded\n  number of characters into a single cell. However, kitty at least preserves\n  the zwj by storing it as a combining character.\n\n- macOS: Disable the custom mouse cursor. Using a custom cursor fails on dual\n  GPU machines. I give up, Apple users will just have to live with the\n  limitations of their choice of OS. (:iss:`794`)\n\n- macOS: Fix control+tab key combination not working (:iss:`801`)\n\n- Linux: Fix slow startup on some systems caused by GLFW searching for\n  joysticks. Since kitty does not use joysticks, disable joystick support.\n  (:iss:`830`)\n\n\n0.11.3 [2018-07-10]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Draw only the minimum borders needed for inactive windows. That is only the borders\n  that separate the inactive window from a neighbor. Note that setting\n  a non-zero window margin overrides this and causes all borders to be drawn.\n  The old behavior of drawing all borders can be restored via the\n  :opt:`draw_minimal_borders` setting in kitty.conf. (:iss:`699`)\n\n- macOS: Add an option :opt:`macos_window_resizable` to control if kitty\n  top-level windows are resizable using the mouse or not (:iss:`698`)\n\n- macOS: Use a custom mouse cursor that shows up well on both light and dark backgrounds\n  (:iss:`359`)\n\n- macOS: Workaround for switching from fullscreen to windowed mode with the\n  titlebar hidden causing window resizing to not work. (:iss:`711`)\n\n- Fix triple-click to select line not working when the entire line is filled\n  (:iss:`703`)\n\n- When dragging to select with the mouse \"grab\" the mouse so that if it strays\n  into neighboring windows, the selection is still updated (:pull:`624`)\n\n- When clicking in the margin/border area of a window, map the click to the\n  nearest cell in the window. Avoids selection with the mouse failing when\n  starting the selection just outside the window.\n\n- When drag-scrolling stop the scroll when the mouse button is released.\n\n- Fix a regression in the previous release that caused pasting large amounts\n  of text to be duplicated (:iss:`709`)\n\n\n0.11.2 [2018-07-01]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Linux: Allow using XKB key names to bind shortcuts to keys not supported by GLFW (:pull:`665`)\n\n- kitty shell: Ignore failure to read readline history file. Happens if the\n  user migrates their kitty cache directory between systems with incompatible\n  readline implementations.\n\n- macOS: Fix an error in remote control when using --listen-on (:iss:`679`)\n\n- hints kitten: Add a :option:`kitty +kitten hints --multiple` option to select\n  multiple items (:iss:`687`)\n\n- Fix pasting large amounts of text very slow (:iss:`682`)\n\n- Add an option :opt:`single_window_margin_width` to allow different margins\n  when only a single window is visible in the layout (:iss:`688`)\n\n- Add a :option:`kitty --hold` command line option to stay open after the child process exits (:iss:`667`)\n\n- diff kitten: When triggering a search scroll to the first match automatically\n\n- :option:`kitty --debug-font-fallback` also prints out what basic fonts were matched\n\n- When closing a kitty window reset the mouse cursor to its default shape and ensure it is visible (:iss:`655`).\n\n- Remote control: Speed-up reading of command responses\n\n- Linux installer: Fix installer failing on systems with python < 3.5\n\n- Support \"-T\" as an alias for \"--title\" (:pull:`659`)\n\n- Fix a regression in the previous release that broke using\n  ``--debug-config`` with custom key mappings (:iss:`695`)\n\n\n0.11.1 [2018-06-17]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- diff kitten: Implement searching for text in the diff (:iss:`574`)\n\n- Add an option :opt:`startup_session` to :file:`kitty.conf` to specify a\n  default startup session (:iss:`641`)\n\n- Add a command line option :option:`kitty --wait-for-single-instance-window-close`\n  to make :option:`kitty --single-instance` wait for the closing of the newly opened\n  window before quitting (:iss:`630`)\n\n- diff kitten: Allow theming the selection background/foreground as well\n\n- diff kitten: Display CRLF line endings using the unicode return symbol\n  instead of <d> as it is less intrusive (:iss:`638`)\n\n- diff kitten: Fix default foreground/background colors not being restored when\n  kitten quits (:iss:`637`)\n\n- Fix :option:`kitten @ set-colors --all` not working when more than one window\n  present (:iss:`632`)\n\n- Fix a regression that broke the legacy increase/decrease_font_size actions\n\n- Clear scrollback on reset (:iss:`631`)\n\n\n0.11.0 [2018-06-12]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new tab bar style \"fade\" in which each tab's edges fade into the background.\n  See :opt:`tab_bar_style` and :opt:`tab_fade` for details. The old look can be\n  restored by setting :opt:`tab_bar_style` to :code:`separator`.\n\n- :doc:`Pre-compiled binaries <binary>` with all bundled dependencies for Linux\n  (:iss:`595`)\n\n- A :doc:`new kitten <kittens/panel>` to create dock panels on X11 desktops\n  showing the output from arbitrary terminal programs.\n\n- Reduce data sent to the GPU per render by 30% (:commit:`8dea5b3`)\n\n- Implement changing the font size for individual top level (OS) windows\n  (:iss:`408`)\n\n- When viewing the scrollback in less using :sc:`show_scrollback` and kitty\n  is currently scrolled, position the scrollback in less to match kitty's\n  scroll position. (:iss:`148`)\n\n- ssh kitten: Support all SSH options. It can now be aliased directly to ssh\n  for convenience. (:pull:`591`)\n\n- icat kitten: Add :option:`kitty +kitten icat --print-window-size` to easily\n  detect the window size in pixels from scripting languages (:iss:`581`)\n\n- hints kitten: Allow selecting hashes from the terminal with\n  :sc:`insert_selected_hash` useful for git commits. (:pull:`604`)\n\n- Allow specifying initial window size in number of cells in addition to pixels\n  (:iss:`436`)\n\n- Add a setting to control the margins to the left and right of the tab-bar\n  (:iss:`584`)\n\n- When closing a tab switch to the last active tab instead of the right-most\n  tab (:iss:`585`)\n\n- Wayland: Fix kitty not starting when using wl_roots based compositors\n  (:iss:`157`)\n\n- Wayland: Fix mouse wheel/touchpad scrolling in opposite direction to other apps (:iss:`594`)\n\n- macOS: Fix the new OS window keyboard shortcut (:sc:`new_os_window`) not\n  working if no kitty window currently has focus. (:iss:`524`)\n\n- macOS: Keep kitty running even when the last window is closed. This is in\n  line with how applications are supposed to behave on macOS (:iss:`543`).\n  There is a new option (:opt:`macos_quit_when_last_window_closed`) to control\n  this.\n\n- macOS: Add macOS standard shortcuts for copy, paste and new OS window\n  (⌘+C, ⌘+V, ⌘+N)\n\n- Add a config option (:opt:`editor`) to set the EDITOR kitty uses (:iss:`580`)\n\n- Add a config option (``x11_hide_window_decorations``) to hide window\n  decorations under X11/Wayland (:iss:`607`)\n\n- Add an option to @set-window-title to make the title change non-permanent\n  (:iss:`592`)\n\n- Add support for the CSI t escape code to query window and cell sizes\n  (:iss:`581`)\n\n- Linux: When using layouts that map the keys to non-ascii characters,\n  map shortcuts using the ascii equivalents, from the default layout.\n  (:iss:`606`)\n\n- Linux: Fix fonts not being correctly read from TrueType Collection\n  (.ttc) files (:iss:`577`)\n\n- Fix :opt:`inactive_text_alpha` also applying to the tab bar (:iss:`612`)\n\n- :doc:`hints kitten <kittens/hints>`: Fix a regression that caused some blank lines to be not\n  be displayed.\n\n- Linux: Include a man page and the HTML docs when building the linux-package\n\n- Remote control: Fix kitty @ sometimes failing to read the response from\n  kitty. (:iss:`614`)\n\n- Fix `kitty @ set-colors` not working with the window border colors.\n  (:iss:`623`)\n\n- Fix a regression in 0.10 that caused incorrect rendering of the status bar in\n  irssi when used inside screen. (:iss:`621`)\n\n\n0.10.1 [2018-05-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add a kitten to easily ssh into servers that automatically copies the\n  terminfo files over. ``kitty +kitten ssh myserver``.\n\n- diff kitten: Make the keyboard shortcuts configurable (:iss:`563`)\n\n- Allow controlling *background_opacity* via either keyboard shortcuts or\n  remote control. Note that you must set *dynamic_background_opacity yes* in\n  kitty.conf first. (:iss:`569`)\n\n- diff kitten: Add keybindings to scroll by page\n\n- diff kitten: Fix incorrect syntax highlighting for a few file formats such as\n  yaml\n\n- macOS: Fix regression that caused the *macos_option_as_alt* setting to always\n  be disabled for all OS windows in a kitty instance after the first window\n  (:iss:`571`)\n\n- Fix Ctrl+Alt+Space not working in normal and application keyboard modes\n  (:iss:`562`)\n\n\n0.10.0 [2018-05-21]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A diff kitten to show side-by-side diffs with syntax highlighting and support\n  for images. See :doc:`diff kitten <kittens/diff>`.\n\n- Make windows in the various kitty layouts manually resizable. See\n  :ref:`layouts` for details.\n\n- Implement support for the SGR *faint* escape code to make text blend\n  into the background (:iss:`446`).\n\n- Make the hints kitten a little smarter (:commit:`ad1109b`)\n  so that URLs that stretch over multiple lines are detected. Also improve\n  detection of surrounding brackets/quotes.\n\n- Make the kitty window id available as the environment variable\n  ``KITTY_WINDOW_ID`` (:iss:`532`).\n\n- Add a \"fat\" layout that is similar to the \"tall\" layout but vertically\n  oriented.\n\n- Expand environment variables in config file include directives\n\n- Allow programs running in kitty to read/write from the clipboard (:commit:`889ca77`).\n  By default only writing is allowed. This feature is supported in many\n  terminals, search for `OSC 52 clipboard` to find out more about using it.\n\n- Fix moving cursor outside a defined page area incorrectly causing the cursor\n  to be placed inside the page area. Caused incorrect rendering in neovim, in\n  some situations (:iss:`542`).\n\n- Render a couple more powerline symbols directly, bypassing the font\n  (:iss:`550`).\n\n- Fix ctrl+alt+<special> not working in normal and application keyboard (:iss:`548`).\n\n- Partial fix for rendering Right-to-left languages like Arabic. Rendering of\n  Arabic is never going to be perfect, but now it is at least readable.\n\n- Fix Ctrl+backspace acting as plain backspace in normal and application\n  keyboard modes (:iss:`538`).\n\n- Have the paste_from_selection action paste from the clipboard on platforms\n  that do not have a primary selection such as Wayland and macOS\n  (:iss:`529`)\n\n- Fix cursor_stop_blinking_after=0 not working (:iss:`530`)\n\n\n0.9.1 [2018-05-05]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Show a bell symbol on the tab if a bell occurs in one of the windows in the tab and\n  the window is not the currently focused window\n\n- Change the window border color if a bell occurs in an unfocused window. Can\n  be disabled by setting the bell_border_color to be the same as the\n  inactive_border_color.\n\n- macOS: Add support for dead keys\n\n- Unicode input: When searching by name search for prefix matches as well as\n  whole word matches\n\n- Dynamically allocate the memory used for the scrollback history buffer.\n  Reduces startup memory consumption when using very large scrollback\n  buffer sizes.\n\n- Add an option to not request window attention on bell.\n\n- Remote control: Allow matching windows by number (visible position).\n\n- macOS: Fix changing tab title and kitty shell not working\n\n- When triple-clicking select all wrapped lines belonging to a single logical line.\n\n- hints kitten: Detect bracketed URLs and don't include the closing bracket in the URL.\n\n- When calling pass_selection_to_program use the current directory of the child\n  process as the cwd of the program.\n\n- Add macos_hide_from_tasks option to hide kitty from the macOS task switcher\n\n- macOS: When the macos_titlebar_color is set to background change the titlebar\n  colors to match the current background color of the active kitty window\n\n- Add a setting to clear all shortcuts defined up to that point in the config\n  file(s)\n\n- Add a setting (kitty_mod) to change the modifier used by all the default\n  kitty shortcuts, globally\n\n- Fix Shift+function key not working\n\n- Support the F13 to F25 function keys\n\n- Don't fail to start if the user deletes the hintstyle key from their\n  fontconfig configuration.\n\n- When rendering a private use unicode codepoint and a space as a two cell\n  ligature, set the foreground colors of the space cell to match the colors of\n  the first cell. Works around applications like powerline that use different\n  colors for the two cells.\n\n- Fix passing @text to other programs such as when viewing the scrollback\n  buffer not working correctly if kitty is itself scrolled up.\n\n- Fix window focus gained/lost events not being reported to child programs when\n  switching windows/tabs using the various keyboard shortcuts.\n\n- Fix tab title not changing to reflect the window title when switching between different windows in a tab\n\n- Ignore -e if it is specified on the command line. This is for compatibility\n  with broken software that assumes terminals should run with an -e option to\n  execute commands instead of just passing the commands as arguments.\n\n\n0.9.0 [2018-04-15]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A new kitty command shell to allow controlling kitty via commands. Press\n  `ctrl+shift+escape` to run the shell.\n\n- The hints kitten has become much more powerful. Now in addition to URLs you\n  can use it to select word, paths, filenames, lines, etc. from the screen.\n  These can be inserted into the terminal, copied to clipboard or sent to\n  external programs.\n\n- Linux: Switch to libxkbcommon for keyboard handling. It allows kitty to\n  support XCompose and dead keys and also react to keyboard remapping/layout\n  change without needing a restart.\n\n- Add support for multiple-key-sequence shortcuts\n\n- A new remote control command `set-colors` to change the current and/or\n  configured colors.\n\n- When double-clicking to select a word, select words that continue onto the\n  next/prev line as well.\n\n- Add an `include` directive for the config files to read multiple config files\n\n- Improve mouse selection for windows with padding. Moving the mouse into the\n  padding area now acts as if the mouse is over the nearest cell.\n\n- Allow setting all 256 terminal colors in the config file\n\n- Fix using `kitty --single-instance` to open a new window in a running kitty\n  instance, not respecting the `--directory` flag\n\n- URL hints: Exclude trailing punctuation from URLs\n\n- URL hints: Launch the browser from the kitty parent process rather than the\n  hints kitten. Fixes launching on some systems where xdg-open doesn't like\n  being run from a kitten.\n\n- Allow using rectangle select mode by pressing shift in addition to the\n  rectangle select modifiers even when the terminal program has grabbed the\n  mouse.\n\n\n0.8.4 [2018-03-31]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix presence of XDG_CONFIG_DIRS and absence of XDG_CONFIG_HOME preventing\n  kitty from starting\n\n- Revert change in last release to cell width calculation. Instead just clip\n  the right edges of characters that overflow the cell by at most two pixels\n\n\n0.8.3 [2018-03-29]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix a regression that broke the visual bell and invert screen colors escape\n  code\n\n- Allow double-click and triple-click + drag to extend selections word at a\n  time or line at a time\n\n- Add a keyboard shortcut to set the tab title\n\n- Fix setting window title to empty via OSC escape code not working correctly\n\n- Linux: Fix cell width calculation incorrect for some fonts (cell widths are\n  now calculated by actually rendering bitmaps, which is slower but more\n  accurate)\n\n- Allow specifying a system wide kitty config file, for all users\n\n- Add a --debug-config command line flag to output data about the system and\n  kitty configuration.\n\n- Wayland: Fix auto-repeat of keys not working\n\n\n0.8.2 [2018-03-17]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow extending existing selections by right clicking\n\n- Add a configurable keyboard shortcut and remote command to set the font size to a specific value\n\n- Add an option to have kitty close the window when the main processes running in it exits, even if there are still background processes writing to that terminal\n\n- Add configurable keyboard shortcuts to switch to a specific layout\n\n- Add a keyboard shortcut to edit the kitty config file easily\n\n- macOS: Fix restoring of window size not correct on Retina screens\n\n- macOS: Add a facility to specify command line arguments when running kitty from the GUI\n\n- Add a focus-tab remote command\n\n- Fix screen not being refreshed immediately after moving a window.\n\n- Fix a crash when getting the contents of the scrollback buffer as text\n\n\n0.8.1 [2018-03-09]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Extend kitty's remote control feature to work over both UNIX and TCP sockets,\n  so now you can control kitty from across the internet, if you want to.\n\n- Render private use unicode characters that are followed by a space as a two\n  character ligature. This fixes rendering for applications that misuse\n  private-use characters to display square symbols.\n\n- Fix Unicode emoji presentation variant selector causing new a fallback font\n  instance to be created\n\n- Fix a rare error that prevented the Unicode input kitten from working\n  sometimes\n\n- Allow using Ctrl+Alt+letter in legacy keyboard modes by outputting them as Ctrl+letter and Alt+letter.\n  This matches other terminals' behavior.\n\n- Fix cursor position off-by-one on horizontal axis when resizing the terminal\n\n- Wayland: Fix auto-repeat of keys not working\n\n- Wayland: Add support for window decorations provided by the Wayland shell\n\n- macOS: Fix URL hints not working\n\n- macOS: Fix shell not starting in login mode on some computers\n\n- macOS: Output errors into console.app when running as a bundle\n\n\n0.8.0 [2018-02-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- A framework for kittens, that is, small terminal programs designed to run\n  inside kitty and extend its capabilities. Examples include unicode input and\n  selecting URLs with the keyboard.\n\n- Input arbitrary unicode characters by pressing Ctrl+Shift+u. You can choose\n  characters by name, by hex code, by recently used, etc. There is even and\n  editable Favorites list.\n\n- Open URLs using only the keyboard. kitty has a new \"hints mode\". Press\n  Ctrl+Shift+e and all detected URLs on the screen are highlighted with a key\n  to press to open them. The facility is customizable so you can change\n  what is detected as a URL and which program is used to open it.\n\n- Add an option to change the titlebar color of kitty windows on macOS\n\n- Only consider Emoji characters with default Emoji presentation to be two\n  cells wide. This matches the standard. Also add support for the Unicode Emoji\n  variation presentation selector.\n\n- Prevent video tearing during high speed scrolling by syncing draws\n  to the monitor's refresh rate. There is a new configuration option to\n  control this ``sync_to_monitor``.\n\n- When displaying only a single window, use the default background color of the\n  window (which can be changed via escape codes) as the color for the margin\n  and padding of the window.\n\n- Add some non standard terminfo capabilities used by neovim and tmux.\n\n- Fix large drop in performance when using multiple top-level windows on macOS\n\n- Fix save/restore of window sizes not working correctly.\n\n- Remove option to use system wcwidth(). Now always use a wcwidth() based on\n  the Unicode standard. Only sane way.\n\n- Fix a regression that caused a few ligature glyphs to not render correctly in\n  rare circumstances.\n\n- Browsing the scrollback buffer now happens in an overlay window instead of a\n  new window/tab.\n\n\n0.7.1 [2018-01-31]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add an option to adjust the width of character cells\n\n- Fix selecting text with the mouse in the scrollback buffer selecting text\n  from the line above the actually selected line\n\n- Fix some italic fonts having the right edge of characters cut-off,\n  unnecessarily\n\n\n0.7.0 [2018-01-24]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Allow controlling kitty from the shell prompt/scripts. You can\n  open/close/rename windows and tabs and even send input to specific windows.\n  See the README for details.\n\n- Add option to put tab bar at the top instead of the bottom\n\n- Add option to override the default shell\n\n- Add \"Horizontal\" and \"Vertical\" window layouts\n\n- Sessions: Allow setting titles and working directories for individual windows\n\n- Option to copy to clipboard on mouse select\n\n- Fix incorrect reporting of mouse move events when using the SGR protocol\n\n- Make alt+backspace delete the previous word\n\n- Take the mouse wheel multiplier option in to account when generating fake key\n  scroll events\n\n- macOS: Fix closing top-level window does not transfer focus to other\n  top-level windows.\n\n- macOS: Fix alt+arrow keys not working when disabling the macos_option_as_alt\n  config option.\n\n- kitty icat: Workaround for bug in ImageMagick that would cause some images\n  to fail to display at certain sizes.\n\n- Fix rendering of text with ligature fonts that do not use dummy glyphs\n\n- Fix a regression that caused copying of the selection to clipboard to only\n  copy the visible part of the selection\n\n- Fix incorrect handling of some unicode combining marks that are not re-ordered\n\n- Fix handling on non-BMP combining characters\n\n- Drop the dependency on libunistring\n\n\n0.6.1 [2017-12-28]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add an option to fade the text in inactive windows\n\n- Add new actions to open windows/tabs/etc. with the working directory set to\n  the working directory of the current window.\n\n- Automatically adjust cell size when DPI changes, for example when kitty is\n  moved from one monitor to another with a different DPI\n\n- Ensure underlines are rendered even for fonts with very poor metrics\n\n- Fix some emoji glyphs not colored on Linux\n\n- Internal wcwidth() implementation is now auto-generated from the unicode\n  standard database\n\n- Allow configuring the modifiers to use for rectangular selection with the\n  mouse.\n\n- Fix incorrect minimum wayland version in the build script\n\n- Fix a crash when detecting a URL that ends at the end of the line\n\n- Fix regression that broke drawing of hollow cursor when window loses focus\n\n\n0.6.0 [2017-12-18]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Support background transparency via the background_opacity option. Provided\n  that your OS/window manager supports transparency, you can now have kitty\n  render pixels that have only the default background color as\n  semi-transparent.\n\n- Support multiple top level (OS) windows. These windows all share the sprite\n  texture cache on the GPU, further reducing overall resource usage. Use\n  the shortcut `ctrl+shift+n` to open a new top-level window.\n\n- Add support for a *daemon* mode using the `--single-instance` command line\n  option. With this option you can have only a single kitty instance running.\n  All future invocations simply open new top-level windows in the existing\n  instance.\n\n- Support colored emoji\n\n- Use CoreText instead of FreeType to render text on macOS\n\n- Support running on the \"low power\" GPU on dual GPU macOS machines\n\n- Add a new \"grid\" window layout\n\n- Drop the dependency on glfw (kitty now uses a modified, bundled copy of glfw)\n\n- Add an option to control the audio bell volume on X11 systems\n\n- Add a command line switch to set the name part of the WM_CLASS window\n  property independently.\n\n- Add a command line switch to set the window title.\n\n- Add more options to customize the tab-bar's appearance (font styles and\n  separator)\n\n- Allow drag and drop of files into kitty. On drop kitty will paste the\n  file path to the running program.\n\n- Add an option to control the underline style for URL highlighting on hover\n\n- X11: Set the WINDOWID environment variable\n\n- Fix middle and right buttons swapped when sending mouse events to child\n  processes\n\n- Allow selecting in a rectangle by holding down Ctrl+Alt while dragging with\n  the mouse.\n\n\n0.5.1 [2017-12-01]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add an option to control the thickness of lines in box drawing characters\n\n- Increase max. allowed ligature length to nine characters\n\n- Fix text not vertically centered when adjusting line height\n\n- Fix unicode block characters not being rendered properly\n\n- Fix shift+up/down not generating correct escape codes\n\n- Image display: Fix displaying images taller than two screen heights not\n  scrolling properly\n\n\n0.5.0 [2017-11-19]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Add support for ligature fonts such as Fira Code, Hasklig, etc. kitty now\n  uses harfbuzz for text shaping which allow it to support advanced OpenType\n  features such as contextual alternates/ligatures/combining glyphs/etc.\n\n- Make it easy to select fonts by allowing listing of monospace fonts using:\n  kitty list-fonts\n\n- Add an option to have window focus follow mouse\n\n- Add a keyboard shortcut (ctrl+shift+f11) to toggle fullscreen mode\n\n- macOS: Fix handling of option key. It now behaves just like the alt key on\n  Linux. There is an option to make it type unicode characters instead.\n\n- Linux: Add support for startup notification on X11 desktops. kitty will\n  now inform the window manager when its startup is complete.\n\n- Fix shell prompt being duplicated when window is resized\n\n- Fix crash when displaying more than 64 images in the same session\n\n- Add support for colons in SGR color codes. These are generated by some\n  applications such as neovim when they mistakenly identify kitty as a libvte\n  based terminal.\n\n- Fix mouse interaction not working in apps using obsolete mouse interaction\n  protocols\n\n- Linux: no longer require glew as a dependency\n\n\n0.4.2 [2017-10-23]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Fix a regression in 0.4.0 that broke custom key mappings\n\n- Fix a regression in 0.4.0 that broke support for non-QWERTY keyboard layouts\n\n- Avoid using threads to reap zombie child processes. Also prevent kitty from\n  hanging if the open program hangs when clicking on a URL.\n\n\n0.4.0 [2017-10-22]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- Support for drawing arbitrary raster graphics (images) in the terminal via a\n  new graphics protocol. kitty can draw images with full 32-bit color using both\n  ssh connections and files/shared memory (when available) for better\n  performance. The drawing primitives support alpha blending and z-index.\n  Images can be drawn both above and below text. See :doc:`graphics-protocol`.\n  for details.\n\n- Refactor kitty's internals to make it even faster and more efficient. The CPU\n  usage of kitty + X server while doing intensive tasks such as scrolling a\n  file continuously in less has been reduced by 50%. There are now two\n  configuration options ``repaint_delay`` and ``input_delay`` you can use to\n  fine tune kitty's performance vs CPU usage profile. The CPU usage of kitty +\n  X when scrolling in less is now significantly better than most (all?) other\n  terminals. See :doc:`performance`.\n\n- Hovering over URLs with the mouse now underlines them to indicate they\n  can be clicked. Hold down Ctrl+Shift while clicking to open the URL.\n\n- Selection using the mouse is now more intelligent. It does not add\n  blank cells (i.e. cells that have no content) after the end of text in a\n  line to the selection.\n\n- The block cursor in now fully opaque but renders the character under it in\n  the background color, for enhanced visibility.\n\n- Allow combining multiple independent actions into a single shortcut\n\n- Add a new shortcut to pass the current selection to an external program\n\n- Allow creating shortcuts to open new windows running arbitrary commands. You\n  can also pass the current selection to the command as an arguments and the\n  contents of the screen + scrollback buffer as stdin to the command.\n"
  },
  {
    "path": "docs/clipboard.rst",
    "content": "Copying all data types to the clipboard\n==============================================\n\nThere already exists an escape code to allow terminal programs to\nread/write plain text data from the system clipboard, *OSC 52*.\nkitty introduces a more advanced protocol that supports:\n\n* Copy arbitrary data including images, rich text documents, etc.\n* Allow terminals to ask the user for permission to access the clipboard and\n  report permission denied\n\nThe escape code is *OSC 5522*, an extension of *OSC 52*. The basic format\nof the escape code is::\n\n    <OSC>5522;metadata;payload<ST>\n\nHere, *metadata* is a colon separated list of key-value pairs and payload is\nbase64 encoded data. :code:`OSC` is :code:`<ESC>[`.\n:code:`ST` is the string terminator, :code:`<ESC>\\\\`.\n\nReading data from the system clipboard\n----------------------------------------\n\nTo read data from the system clipboard, the escape code is::\n\n    <OSC>5522;type=read;<base 64 encoded space separated list of mime types to read><ST>\n\nFor example, to read plain text and PNG data, the payload would be::\n\n    text/plain image/png\n\nencoded as base64. To read from the primary selection instead of the\nclipboard, add the key ``loc=primary`` to the metadata section.\n\nTo get the list of MIME types available on the clipboard the payload must be\njust a period (``.``), encoded as base64.\n\nThe terminal emulator will reply with a sequence of escape codes of the form::\n\n    <OSC>5522;type=read:status=OK<ST>\n    <OSC>5522;type=read:status=DATA:mime=<base 64 encoded mime type>;<base64 encoded data><ST>\n    <OSC>5522;type=read:status=DATA:mime=<base 64 encoded mime type>;<base64 encoded data><ST>\n    .\n    .\n    .\n    <OSC>5522;type=read:status=DONE<ST>\n\nHere, the ``status=DATA`` packets deliver the data (as base64 encoded bytes)\nassociated with each MIME type. The terminal emulator should chunk up the data\nfor an individual type, into chunks of size **no more** than 4096 bytes (4096\nis the size of a chunk *before* base64 encoding). All\nthe chunks for a given type must be transmitted sequentially and only once they\nare done the chunks for the next type, if any, should be sent. The end of data\nis indicated by a ``status=DONE`` packet.\n\nIf an error occurs, instead of the opening ``status=OK`` packet the terminal\nmust send a ``status=ERRORCODE`` packet. The error code must be one of:\n\n``status=ENOSYS``\n    Sent if the requested clipboard type is not available. For example, primary\n    selection is not available on all systems and ``loc=primary`` was used.\n\n``status=EPERM``\n    Sent if permission to read from the clipboard was denied by the system or\n    the user.\n\n``status=EBUSY``\n    Sent if there is some temporary problem, such as multiple clients in a\n    multiplexer trying to access the clipboard simultaneously.\n\nTerminals should ask the user for permission before allowing a read request.\nHowever, if a read request only wishes to list the available data types on the\nclipboard, it should be allowed without a permission prompt. This is so that\nthe user is not presented with a double permission prompt for reading the\navailable MIME types and then reading the actual data.\n\n\nWriting data to the system clipboard\n----------------------------------------\n\nTo write data to the system clipboard, the terminal programs sends the\nfollowing sequence of packets::\n\n    <OSC>5522;type=write<ST>\n    <OSC>5522;type=wdata:mime=<base64 encoded mime type>;<base 64 encoded chunk of data for this type><ST>\n    <OSC>5522;type=wdata:mime=<base64 encoded mime type>;<base 64 encoded chunk of data for this type><ST>\n    .\n    .\n    .\n    <OSC>5522;type=wdata<ST>\n\nThe final packet with no mime and no data indicates end of transmission. The\ndata for every MIME type should be split into chunks of no more than 4096\nbytes (4096 is the size of the data before base64 encoding).\nAll the chunks for a given MIME type must be sent sequentially, before\nsending chunks for the next MIME type. After the transmission is complete, the\nterminal replies with a single packet indicating success::\n\n    <OSC>5522;type=write:status=DONE<ST>\n\nIf an error occurs the terminal can, at any time, send an error packet of the\nform::\n\n    <OSC>5522;type=write:status=ERRORCODE<ST>\n\nHere ``ERRORCODE`` must be one of:\n\n``status=EIO``\n    An I/O error occurred while processing the data\n``status=EINVAL``\n    One of the packets was invalid, usually because of invalid base64 encoding.\n``status=ENOSYS``\n    The client asked to write to the primary selection with (``loc=primary``) and that is not\n    available on the system\n``status=EPERM``\n    Sent if permission to write to the clipboard was denied by the system or\n    the user.\n``status=EBUSY``\n    Sent if there is some temporary problem, such as multiple clients in a\n    multiplexer trying to access the clipboard simultaneously.\n\nOnce an error occurs, the terminal must ignore all further OSC 5522 write related packets until it\nsees the start of a new write with a ``type=write`` packet.\n\nThe client can send to the primary selection instead of the clipboard by adding\n``loc=primary`` to the initial ``type=write`` packet.\n\nFinally, clients have the ability to *alias* MIME types when sending data to\nthe clipboard. To do that, the client must send a ``type=walias`` packet of the\nform::\n\n    <OSC>5522;type=walias;mime=<base64 encoded target MIME type>;<base64 encoded, space separated list of aliases><ST>\n\nThe effect of an alias is that the system clipboard will make available all the\naliased MIME types, with the same data as was transmitted for the target MIME\ntype. This saves bandwidth, allowing the client to only transmit one copy of\nthe data, but create multiple references to it in the system clipboard. Alias\npackets can be sent anytime after the initial write packet and before the end\nof data packet.\n\n.. _clipboard_repeated_permission:\n\nAvoiding repeated permission prompts\n--------------------------------------\n\n.. versionadded:: 0.42.2\n     using a password to avoid repeated confirmations\n\nIf a program like an editor wants to make use of the system clipboard, by\ndefault, the user is prompted on every read request. This can become quite\nfatiguing. To avoid this situation, this protocol allows sending a password\nand human friendly name with ``type=write`` and ``type=read`` requests. The\nterminal can then ask the user to allow all future requests using that\npassword. If the user agrees, future requests on the same tty will be\nautomatically allowed by the terminal. The editor or other program using\nthis facility should ideally use a password randomly generated at startup,\nsuch as a UUID4. However, terminals may implement permanent/stored passwords.\nUsers can then configure terminal programs they trust to use these password.\n\nThe password and the human name are encoded using the ``pw`` and ``name`` keys\nin the metadata. The values are UTF-8 strings that are base64 encoded.\nSpecifying a password without a human friendly name is equivalent to not\nspecifying a password and the terminal must treat the request as though\nit had no password.\n\nAllowing terminal applications to respond to paste events\n--------------------------------------------------------------\n\n.. versionadded:: 0.44.1\n     paste events via the 5522 mode\n\nIf a TUI application wants to handle paste events (like the user pressing the\npaste key shortcut used by the terminal or selecting paste from a terminal UI menu)\nit can enable the *paste events* private mode (5522), as described in this `ancillary\nspecification <https://rockorager.dev/misc/bracketed-paste-mime/>`__. When that\nmode is set, the terminal will send the application a list of MIME types on the\nclipboard every time the user triggers a paste action. The application is then\nfree to request whatever MIME data it wants from the list of types.\n\nThe mode can be enabled using the standard DECSET or DECRST control sequences.\n``CSI ? 5522 h`` to enable the mode. ``CSI ? 5522 l`` to disable the mode.\n\nThe terminal *should* send a one time password with the list of mime\ntypes, as the ``pw`` key (base64 encoded). The application can then use this\npassword to request data from the clipboard without needing a permission\nprompt. The human name *should* be set to ``Paste event`` (base64 encoded) when\nthe application uses this one time password.\n\nDetecting support for this protocol\n-----------------------------------------\n\nApplications can detect if a terminal supports this protocol with a standard\nDECRQM query:\n\n.. code::\n\n    CSI ? 5522 $ p\n\nTo which the terminal will respond with a DECRPM response:\n\n.. code::\n\n    CSI ? 5522 ; Ps $ y\n\nA Ps value of 0 or 4 means the mode is not supported.\n\n\nSupport for terminal multiplexers\n------------------------------------\n\nSince this protocol involves two way communication between the terminal\nemulator and the client program, multiplexers need a way to know which window\nto send responses from the terminal to. In order to make this possible, the\nmetadata portion of this escape code includes an optional ``id`` field. If\npresent the terminal emulator must send it back unchanged with every response.\nValid ids must include only characters from the set: ``[a-zA-Z0-9-_+.]``. Any\nother characters must be stripped out from the id by the terminal emulator\nbefore retransmitting it.\n\nNote that when using a terminal multiplexer it is possible for two different\nprograms to overwrite each other's clipboard requests. This is fundamentally\nunavoidable since the system clipboard is a single global shared resource.\nHowever, there is an additional complication where responses from this protocol\ncould get lost if, for instance, multiple write requests are received\nsimultaneously. It is up to well designed multiplexers to ensure that only a\nsingle request is in flight at a time. The multiplexer can abort requests by\nsending back the ``EBUSY`` error code indicating some other window is trying\nto access the clipboard.\n\nWhen the terminal sends an unsolicited paste event because the user triggered\na paste and the 5522 mode is enabled, there will be no associated id. In this\ncase, the multiplexer must forward the event to the currently active window.\n"
  },
  {
    "path": "docs/color-stack.rst",
    "content": "Color control\n====================\n\nSaving and restoring colors\n------------------------------\n\nIt is often useful for a full screen application with its own color themes to\nset the default foreground, background, selection and cursor colors and the ANSI\ncolor table. This allows for various performance optimizations when drawing the\nscreen. The problem is that if the user previously used the escape codes to\nchange these colors themselves, then running the full screen application will\nlose those changes even after it exits. To avoid this, kitty introduces a new\npair of *OSC* escape codes to push and pop the current color values from a\nstack::\n\n    <ESC>]30001<ESC>\\  # push onto stack\n    <ESC>]30101<ESC>\\  # pop from stack\n\nThese escape codes save/restore the colors, default background, default\nforeground, selection background, selection foreground and cursor color and the\n256 colors of the ANSI color table.\n\n.. note:: In July 2020, after several years, xterm copied this protocol\n   extension, without acknowledgement, and using incompatible escape codes\n   (XTPUSHCOLORS, XTPOPCOLORS, XTREPORTCOLORS). And they decided to save not\n   just the dynamic colors but the entire ANSI color table. In the interests of\n   promoting interoperability, kitty added support for xterm's escape codes as\n   well, and changed this extension to also save/restore the entire ANSI color\n   table.\n\n.. _color_control:\n\nSetting and querying colors\n-------------------------------\n\nWhile there exists a legacy protocol developed by XTerm for querying and\nsetting colors, as with most XTerm protocols it suffers from the usual design\nlimitations of being under specified and in-sufficient. XTerm implements\nquerying of colors using OSC 4,5,6,10-19,104,105,106,110-119. This absurd\nprofusion of numbers is completely unnecessary, redundant and requires adding\ntwo new numbers for every new color. Also XTerm's protocol doesn't handle the\ncase of colors that are unknown to the terminal or that are not a set value,\nfor example, many terminals implement selection as a reverse video effect not a\nfixed color. The XTerm protocol has no way to query for this condition. The\nprotocol also doesn't actually specify the format in which colors are reported,\ndeferring to a man page for X11!\n\nInstead kitty has developed a single number based protocol that addresses all\nthese shortcomings and is future proof by virtue of using string keys rather\nthan numbers. The syntax of the escape code is::\n\n    <OSC> 21 ; key=value ; key=value ; ... <ST>\n\nThe spaces in the above definition are for reading clarity and should be ignored.\nHere, ``<OSC>`` is the two bytes ``0x1b (ESC)`` and ``0x5d (])``. ``<ST>`` is\neither ``0x07 (BEL)`` or the two bytes ``0x1b (ESC)`` and ``0x5c (\\\\)``.\n\n``key`` is a number from 0-255 to query or set the color values from the\nterminals ANSI color table, or one of the strings in the table below for\nspecial colors:\n\n================================= =============================================== ===============================\nkey                               meaning                                         dynamic\n================================= =============================================== ===============================\nforeground                        The default foreground text color               Not applicable\nbackground                        The default background text color               Not applicable\nselection_background              The background color of selections              Reverse video\nselection_foreground              The foreground color of selections              Reverse video\ncursor                            The color of the text cursor                    Foreground color\ncursor_text                       The color of text under the cursor              Background color\nvisual_bell                       The color of a visual bell                      Automatic color selection based on current screen colors\ntransparent_background_color1..7  A background color that is rendered             Unset\n                                  with the specified opacity in cells that have\n                                  the specified background color. An opacity\n                                  value less than zero means, use the\n                                  :opt:`background_opacity` value.\n================================= =============================================== ===============================\n\nIn this table the third column shows what effect setting the color to *dynamic*\nhas in kitty and many other terminal emulators. It is advisory only, terminal\nemulators may not support dynamic colors for these or they may have other\neffects. Setting the ANSI color table colors to dynamic is not allowed.\n\nQuerying current color values\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo query colors values, the client program sends this escape code with the\n``value`` field set to ``?`` (the byte ``0x3f``). The terminal then responds\nwith the same escape code, but with the ``?`` replaced by the :ref:`encoded\ncolor value <color_control_color_encoding>`. If the queried color is one that\ndoes not have a defined value, for example, if the terminal is using a reverse\nvideo effect or a gradient or similar, then the value must be empty, that is\nthe response contains only the key and ``=``, no value. For example, if the\nclient sends::\n\n    <OSC> 21 ; foreground=? ; cursor=? <ST>\n\nThe terminal responds::\n\n    <OSC> 21 ; foreground=rgb:ff/00/00 ; cursor= <ST>\n\nThis indicates that the foreground color is red and the cursor color is\nundefined (typically the cursor takes the color of the text under it and the\ntext takes the color of the background).\n\nIf the terminal does not know a field that a client sends to it for a query it\nmust respond back with the ``field=?``, that is, it must send back a question\nmark as the value.\n\n\nSetting color values\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo set a color value, the client program sends this escape code with the\n``value`` field set to either an :ref:`encoded color value\n<color_control_color_encoding>` or the empty value. The empty value means\nthe terminal should use a dynamic color for example reverse video for\nselections or similar. To reset a color to its default value (i.e. the value it\nwould have if it was never set) the client program should send just the key\nname with no ``=`` and no value. For example::\n\n    <OSC> 21 ; foreground=green ; cursor= ; background <ST>\n\nThis sets the foreground to the color green, sets the cursor color to dynamic\n(usually meaning the cursor takes the color of the text under it) and resets\nthe background color to its default value.\n\nTo check if setting succeeded, the client can simply query the color, in fact\nthe two can be combined into a single escape code, for example::\n\n    <OSC> 21 ; foreground=white ; foreground=? <ST>\n\nThe terminal will change the foreground color and reply with the new foreground\ncolor.\n\n\n.. _color_control_color_encoding:\n\nColor value encoding\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe color encoding is inherited from the scheme used by XTerm, for\ncompatibility, but a sane, rigorously specified subset is chosen.\n\nRGB colors are encoded in one of three forms:\n\n``rgb:<red>/<green>/<blue>``\n    | <red>, <green>, <blue> := h | hh | hhh | hhhh\n    | h := single hexadecimal digits (case insignificant)\n    | Note that h indicates the value scaled in 4 bits, hh the value scaled in 8 bits,\n      hhh the value scaled in 12 bits, and hhhh the value scaled in 16 bits, respectively.\n\n``#<h...>``\n    | h := single hexadecimal digits (case insignificant)\n    | #RGB            (4 bits each)\n    | #RRGGBB         (8 bits each)\n    | #RRRGGGBBB      (12 bits each)\n    | #RRRRGGGGBBBB   (16 bits each)\n    | The R, G, and B represent single hexadecimal digits.  When fewer than 16 bits\n      each are specified, they represent the most significant bits of the value\n      (unlike the “rgb:” syntax, in which values are scaled). For example,\n      the string ``#3a7`` is the same as ``#3000a0007000``.\n\n``rgbi:<red>/<green>/<blue>``\n    red, green, and blue are floating-point values between 0.0 and 1.0, inclusive. The input format for these values is an optional\n    sign, a string of numbers possibly containing a decimal point, and an optional exponent field containing an E or e followed by a possibly\n    signed integer string. Values outside the ``0 - 1`` range must be clipped to be within the range.\n\nIf a color should have an alpha component, it must be suffixed to the color\nspecification in the form :code:`@number between zero and one`. For example::\n\n    red@0.5 rgb:ff0000@0.1 #ff0000@0.3\n\nThe syntax for the floating point alpha component is the same as used for the\ncomponents of ``rgbi`` defined above. When not specified, the default alpha\nvalue is ``1.0``. Values outside the range ``0 - 1`` must be clipped\nto be within the range, negative values may have special context dependent\nmeaning.\n\nIn addition, the following color names are accepted (case-insensitively) corresponding to the\nspecified RGB values.\n\n.. include:: generated/color-names.rst\n"
  },
  {
    "path": "docs/conf.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n#\n# Configuration file for the Sphinx documentation builder.\n#\n# This file does only contain a selection of the most common options. For a\n# full list see the documentation:\n# https://www.sphinx-doc.org/en/master/config\n\nimport glob\nimport os\nimport re\nimport subprocess\nimport sys\nimport time\nfrom functools import lru_cache, partial\nfrom typing import Any, Callable, Dict, Iterable, Iterator, List, Tuple\n\nfrom docutils import nodes\nfrom docutils.parsers.rst.roles import set_classes\nfrom pygments.lexer import RegexLexer\nfrom pygments.lexer import bygroups as untyped_bygroups\nfrom pygments.token import Comment, Error, Keyword, Literal, Name, Number, String, Whitespace\nfrom sphinx import addnodes, version_info\nfrom sphinx.util.logging import getLogger\n\nkitty_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\nif kitty_src not in sys.path:\n    sys.path.insert(0, kitty_src)\n\nfrom kitty.conf.types import Definition, expand_opt_references  # noqa\nfrom kitty.constants import str_version, website_url # noqa\nfrom kitty.fast_data_types import Shlex, TEXT_SIZE_CODE  # noqa\n\n# config {{{\n# -- Project information -----------------------------------------------------\n\nproject = 'kitty'\ncopyright = time.strftime('%Y, Kovid Goyal')\nauthor = 'Kovid Goyal'\nbuilding_man_pages = 'man' in sys.argv\n\n# The short X.Y version\nversion = str_version\n# The full version, including alpha/beta/rc tags\nrelease = str_version\nlogger = getLogger(__name__)\n\n\n# -- General configuration ---------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\nneeds_sphinx = '1.7'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.ifconfig',\n    'sphinx.ext.viewcode',\n    'sphinx.ext.githubpages',\n    'sphinx.ext.extlinks',\n    'sphinx_copybutton',\n    'sphinx_inline_tabs',\n    'sphinxext.opengraph',\n]\n\n# URL for OpenGraph tags\nogp_site_url = website_url()\n# OGP needs a PNG image because of: https://github.com/wpilibsuite/sphinxext-opengraph/issues/96\nogp_social_cards = {\n    'image': '../logo/kitty.png'\n}\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage: str = 'en'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path .\nexclude_patterns = [\n    '_build', 'Thumbs.db', '.DS_Store', 'basic.rst',\n    'generated/cli-*.rst', 'generated/conf-*.rst', 'generated/actions.rst'\n]\n\nrst_prolog = '''\n.. |kitty| replace:: *kitty*\n.. |version| replace:: VERSION\n.. _tarball: https://github.com/kovidgoyal/kitty/releases/download/vVERSION/kitty-VERSION.tar.xz\n.. role:: italic\n\n'''.replace('VERSION', str_version)\nsmartquotes_action = 'qe'  # educate quotes and ellipses but not dashes\n\ndef go_version(go_mod_path: str) -> str:  # {{{\n    with open(go_mod_path) as f:\n        for line in f:\n            if line.startswith('go '):\n                return line.strip().split()[1]\n    raise SystemExit(f'No Go version in {go_mod_path}')\n# }}}\n\nstring_replacements = {\n    '_kitty_install_cmd': 'curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin',\n    '_build_go_version': go_version('../go.mod'),\n    '_text_size_code': str(TEXT_SIZE_CODE),\n}\n\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'furo'\nhtml_title = 'kitty'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\ngithub_icon_path = 'M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z'  # noqa\n\nhtml_theme_options: Dict[str, Any] = {\n    'sidebar_hide_name': True,\n    'navigation_with_keys': True,\n    'footer_icons': [\n        {\n            \"name\": \"GitHub\",\n            \"url\": \"https://github.com/kovidgoyal/kitty\",\n            \"html\": f\"\"\"\n                <svg stroke=\"currentColor\" fill=\"currentColor\" stroke-width=\"0\" viewBox=\"0 0 16 16\">\n                    <path fill-rule=\"evenodd\" d=\"{github_icon_path}\"></path>\n                </svg>\n            \"\"\",\n            \"class\": \"\",\n        },\n    ],\n}\n\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\nhtml_favicon = html_logo = '../logo/kitty.svg'\nhtml_css_files = ['custom.css', 'timestamps.css']\nhtml_js_files = ['custom.js', 'timestamps.js']\n\n# Custom sidebar templates, must be a dictionary that maps document names\n# to template names.\n#\n# The default sidebars (for documents that don't match any pattern) are\n# defined by theme itself.  Builtin themes are using these templates by\n# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',\n# 'searchbox.html']``.\n#\nhtml_show_sourcelink = False\nhtml_show_sphinx = False\nmanpages_url = 'https://man7.org/linux/man-pages/man{section}/{page}.{section}.html'\n\n# -- Options for manual page output ------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('invocation', 'kitty', 'The fast, feature rich terminal emulator', [author], 1),\n    ('conf', 'kitty.conf', 'Configuration file for kitty', [author], 5)\n]\n\n\n# -- Options for Texinfo output ----------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'kitty', 'kitty Documentation',\n     author, 'kitty', 'Cross-platform, fast, feature-rich, GPU based terminal',\n     'Miscellaneous'),\n]\n# }}}\n\n\n# GitHub linking inline roles {{{\n\nextlinks = {\n    'iss': ('https://github.com/kovidgoyal/kitty/issues/%s', '#%s'),\n    'pull': ('https://github.com/kovidgoyal/kitty/pull/%s', '#%s'),\n    'disc': ('https://github.com/kovidgoyal/kitty/discussions/%s', '#%s'),\n}\n\n\ndef commit_role(\n    name: str, rawtext: str, text: str, lineno: int, inliner: Any, options: Any = {}, content: Any = []\n) -> Tuple[List[nodes.reference], List[nodes.problematic]]:\n    ' Link to a github commit '\n    try:\n        commit_id = subprocess.check_output(\n            f'git rev-list --max-count=1 {text}'.split()).decode('utf-8').strip()\n    except Exception:\n        msg = inliner.reporter.error(\n            f'git commit id \"{text}\" not recognized.', line=lineno)\n        prb = inliner.problematic(rawtext, rawtext, msg)\n        return [prb], [msg]\n    url = f'https://github.com/kovidgoyal/kitty/commit/{commit_id}'\n    set_classes(options)\n    short_id = subprocess.check_output(\n        f'git rev-list --max-count=1 --abbrev-commit {commit_id}'.split()).decode('utf-8').strip()\n    node = nodes.reference(rawtext, f'commit: {short_id}', refuri=url, **options)\n    return [node], []\n# }}}\n\n\n# CLI docs {{{\ndef write_cli_docs(all_kitten_names: Iterable[str]) -> None:\n    from kittens.ssh.main import copy_message, option_text\n    from kitty.cli import option_spec_as_rst\n    with open('generated/ssh-copy.rst', 'w') as f:\n        f.write(option_spec_as_rst(\n            appname='copy', ospec=option_text, heading_char='^',\n            usage='file-or-dir-to-copy ...', message=copy_message\n        ))\n    del sys.modules['kittens.ssh.main']\n    from kitty.session import save_as_session_message, save_as_session_options\n    with open('generated/save-as-session.rst', 'w') as f:\n        f.write(option_spec_as_rst(\n            appname='save_as_session', ospec=save_as_session_options, heading_char='^',\n            usage='[path-to-save-session-file-at]',\n            message=save_as_session_message,\n        ))\n\n    from kitty.launch import options_spec as launch_options_spec\n    with open('generated/launch.rst', 'w') as f:\n        f.write(option_spec_as_rst(\n            appname='launch', ospec=launch_options_spec, heading_char='_',\n            message='''\\\nLaunch an arbitrary program in a new kitty window/tab. Note that\nif you specify a program-to-run you can use the special placeholder\n:code:`@selection` which will be replaced by the current selection.\n'''\n        ))\n    with open('generated/cli-kitty.rst', 'w') as f:\n        f.write(option_spec_as_rst(appname='kitty').replace(\n            'kitty --to', 'kitty @ --to'))\n    as_rst = partial(option_spec_as_rst, heading_char='_')\n    from kitty.rc.base import all_command_names, command_for_name\n    from kitty.remote_control import cli_msg, global_options_spec\n    with open('generated/cli-kitten-at.rst', 'w') as f:\n        p = partial(print, file=f)\n        p('kitten @')\n        p('-' * 80)\n        p('.. program::', 'kitten @')\n        p('\\n\\n' + as_rst(\n            global_options_spec, message=cli_msg, usage='command ...', appname='kitten @'))\n        from kitty.rc.base import cli_params_for\n        for cmd_name in sorted(all_command_names()):\n            func = command_for_name(cmd_name)\n            p(f'.. _at-{func.name}:\\n')\n            p('kitten @', func.name)\n            p('-' * 120)\n            p('.. program::', 'kitten @', func.name)\n            p('\\n\\n' + as_rst(*cli_params_for(func)))\n    from kittens.runner import get_kitten_cli_docs\n\n    for kitten in all_kitten_names:\n        data = get_kitten_cli_docs(kitten)\n        if data:\n            with open(f'generated/cli-kitten-{kitten}.rst', 'w') as f:\n                p = partial(print, file=f)\n                p('.. program::', 'kitty +kitten', kitten)\n                p('\\nSource code for', kitten)\n                p('-' * 72)\n                scurl = f'https://github.com/kovidgoyal/kitty/tree/master/kittens/{kitten}'\n                p(f'\\nThe source code for this kitten is `available on GitHub <{scurl}>`_.')\n                p('\\nCommand Line Interface')\n                p('-' * 72)\n                appname = f'kitten {kitten}'\n                if kitten in ('panel', 'broadcast', 'remote_file'):\n                    appname = 'kitty +' + appname\n                p('\\n\\n' + option_spec_as_rst(\n                    data['options'], message=data['help_text'], usage=data['usage'], appname=appname, heading_char='^'))\n\n# }}}\n\n\ndef write_color_names_table() -> None: # {{{\n    from kitty.fast_data_types import all_color_names\n    def s(c: Any) -> str:\n        return f'{c.red:02x}/{c.green:02x}/{c.blue:02x}'\n    with open('generated/color-names.rst', 'w') as f:\n        p = partial(print, file=f)\n        p('=' * 50, '=' * 20)\n        p('Name'.ljust(50), 'RGB value')\n        p('=' * 50, '=' * 20)\n        for name, col in all_color_names():\n            p(name.ljust(50), s(col))\n        p('=' * 50, '=' * 20)\n# }}}\n\ndef write_remote_control_protocol_docs() -> None:  # {{{\n    from kitty.rc.base import RemoteCommand, all_command_names, command_for_name\n    field_pat = re.compile(r'\\s*([^:]+?)\\s*:\\s*(.+)')\n\n    def format_cmd(p: Callable[..., None], name: str, cmd: RemoteCommand) -> None:\n        p(name)\n        p('-' * 80)\n        lines = (cmd.__doc__ or '').strip().splitlines()\n        fields = []\n        for line in lines:\n            m = field_pat.match(line)\n            if m is None:\n                p(line)\n            else:\n                fields.append((m.group(1).split('/')[0], m.group(2)))\n        if fields:\n            p('\\nFields are:\\n')\n            for (name, desc) in fields:\n                if '+' in name:\n                    title = name.replace('+', ' (required)')\n                else:\n                    title = name\n                    defval = cmd.get_default(name.replace('-', '_'), cmd)\n                    if defval is not cmd:\n                        title = f'{title} (default: {defval})'\n                    else:\n                        title = f'{title} (optional)'\n                p(f':code:`{title}`')\n                p(' ', desc)\n                p()\n        p()\n        p()\n\n    with open('generated/rc.rst', 'w') as f:\n        p = partial(print, file=f)\n        for name in sorted(all_command_names()):\n            cmd = command_for_name(name)\n            if not cmd.__doc__:\n                continue\n            name = name.replace('_', '-')\n            format_cmd(p, name, cmd)\n# }}}\n\n\ndef replace_string(app: Any, docname: str, source: List[str]) -> None:  # {{{\n    src = source[0]\n    for k, v in string_replacements.items():\n        src = src.replace(k, v)\n    source[0] = src\n# }}}\n\n# config file docs {{{\n\n\ndef bygroups(*args: Any) -> Any:\n    return untyped_bygroups(*args)  # type: ignore[no-untyped-call]\n\n\nclass ConfLexer(RegexLexer):\n    name = 'Conf'\n    aliases = ['conf']\n    filenames = ['*.conf']\n\n    def map_flags(self: RegexLexer, val: str, start_pos: int) -> Iterator[Tuple[int, Any, str]]:\n        expecting_arg = ''\n        s = Shlex(val)\n        from kitty.options.utils import allowed_key_map_options\n        last_pos = 0\n        while (tok := s.next_word())[0] > -1:\n            x = tok[1]\n            if tok[0] > last_pos:\n                yield start_pos + last_pos, Whitespace, ' ' * (tok[0] - last_pos)\n            last_pos = tok[0] + len(x)\n            tok_start = start_pos + tok[0]\n            if expecting_arg:\n                yield tok_start, String, x\n                expecting_arg = ''\n            elif x.startswith('--'):\n                expecting_arg = x[2:]\n                k, sep, v = expecting_arg.partition('=')\n                k = k.replace('-', '_')\n                expecting_arg = k\n                if expecting_arg not in allowed_key_map_options:\n                    yield tok_start, Error, x\n                elif sep == '=':\n                    expecting_arg = ''\n                    yield tok_start, Name, x\n                else:\n                    yield tok_start, Name, x\n            else:\n                break\n\n    def mapargs(self: 'ConfLexer', match: 're.Match[str]') -> Iterator[Tuple[int, Any, str]]:\n        start_pos = match.start()\n        val = match.group()\n        parts = val.split(maxsplit=1)\n        if parts[0].startswith('--'):\n            seen = 0\n            for (pos, token, text) in self.map_flags(val, start_pos):\n                yield pos, token, text\n                seen += len(text)\n            start_pos += seen\n            val = val[seen:]\n            parts = val.split(maxsplit=1)\n\n        if not val:\n            return\n        yield start_pos, Literal, parts[0]  # key spec\n        if len(parts) == 1:\n            return\n        start_pos += len(parts[0])\n        val = val[len(parts[0]):]\n        m = re.match(r'(\\s+)(\\S+)', val)\n        if m is None:\n            return\n        yield start_pos, Whitespace, m.group(1)\n        yield start_pos + m.start(2), Name.Function, m.group(2)  # action function\n        yield start_pos + m.end(2), String, val[m.end(2):]\n\n    tokens = {\n        'root': [\n            (r'#.*?$', Comment.Single),\n            (r'\\s+$', Whitespace),\n            (r'\\s+', Whitespace),\n            (r'(include)(\\s+)(.+?)$', bygroups(Comment.Preproc, Whitespace, Name.Namespace)),\n            (r'(map)(\\s+)', bygroups(\n                Keyword.Declaration, Whitespace), 'mapargs'),\n            (r'(mouse_map)(\\s+)(\\S+)(\\s+)(\\S+)(\\s+)(\\S+)(\\s+)', bygroups(\n                Keyword.Declaration, Whitespace, String, Whitespace, Name.Variable, Whitespace, String, Whitespace), 'action'),\n            (r'(symbol_map)(\\s+)(\\S+)(\\s+)(.+?)$', bygroups(\n                Keyword.Declaration, Whitespace, String, Whitespace, Literal)),\n            (r'([a-zA-Z_0-9]+)(\\s+)', bygroups(\n                Name.Variable, Whitespace), 'args'),\n        ],\n        'action': [\n            (r'[a-z_0-9]+$', Name.Function, 'root'),\n            (r'[a-z_0-9]+', Name.Function, 'args'),\n        ],\n        'mapargs': [\n            (r'.+$', mapargs, 'root'),\n        ],\n        'args': [\n            (r'\\s+', Whitespace, 'args'),\n            (r'\\b(yes|no)\\b$', Number.Bin, 'root'),\n            (r'\\b(yes|no)\\b', Number.Bin, 'args'),\n            (r'[+-]?[0-9]+\\s*$', Number.Integer, 'root'),\n            (r'[+-]?[0-9.]+\\s*$', Number.Float, 'root'),\n            (r'[+-]?[0-9]+', Number.Integer, 'args'),\n            (r'[+-]?[0-9.]+', Number.Float, 'args'),\n            (r'#[a-fA-F0-9]{3,6}\\s*$', String, 'root'),\n            (r'#[a-fA-F0-9]{3,6}\\s*', String, 'args'),\n            (r'.+', String, 'root'),\n        ],\n    }\n\n\nclass SessionLexer(RegexLexer):\n    name = 'Session'\n    aliases = ['session']\n    filenames = ['*.session']\n\n    tokens = {\n        'root': [\n            (r'#.*?$', Comment.Single),\n            (r'[a-z][a-z0-9_]+', Name.Function, 'args'),\n        ],\n        'args': [\n            (r'.*?$', Literal, 'root'),\n        ]\n    }\n\n\ndef link_role(\n    name: str, rawtext: str, text: str, lineno: int, inliner: Any, options: Any = {}, content: Any = []\n) -> Tuple[List[nodes.reference], List[nodes.problematic]]:\n    text = text.replace('\\n', ' ')\n    m = re.match(r'(.+)\\s+<(.+?)>', text)\n    if m is None:\n        msg = inliner.reporter.error(f'link \"{text}\" not recognized', line=lineno)\n        prb = inliner.problematic(rawtext, rawtext, msg)\n        return [prb], [msg]\n    text, url = m.group(1, 2)\n    url = url.replace(' ', '')\n    set_classes(options)\n    node = nodes.reference(rawtext, text, refuri=url, **options)\n    return [node], []\n\n\nopt_aliases: Dict[str, str] = {}\nshortcut_slugs: Dict[str, Tuple[str, str]] = {}\n\n\ndef parse_opt_node(env: Any, sig: str, signode: Any) -> str:\n    \"\"\"Transform an option description into RST nodes.\"\"\"\n    count = 0\n    firstname = ''\n    for potential_option in sig.split(', '):\n        optname = potential_option.strip()\n        if count:\n            signode += addnodes.desc_addname(', ', ', ')\n        text = optname.split('.', 1)[-1]\n        signode += addnodes.desc_name(text, text)\n        if not count:\n            firstname = optname\n            signode['allnames'] = [optname]\n        else:\n            signode['allnames'].append(optname)\n            opt_aliases[optname] = firstname\n        count += 1\n    if not firstname:\n        raise ValueError(f'{sig} is not a valid opt')\n    return firstname\n\n\ndef parse_shortcut_node(env: Any, sig: str, signode: Any) -> str:\n    \"\"\"Transform a shortcut description into RST nodes.\"\"\"\n    conf_name, text = sig.split('.', 1)\n    signode += addnodes.desc_name(text, text)\n    return sig\n\n\ndef parse_action_node(env: Any, sig: str, signode: Any) -> str:\n    \"\"\"Transform an action description into RST nodes.\"\"\"\n    signode += addnodes.desc_name(sig, sig)\n    return sig\n\n\ndef process_opt_link(env: Any, refnode: Any, has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:\n    conf_name, opt = target.partition('.')[::2]\n    if not opt:\n        conf_name, opt = 'kitty', conf_name\n    full_name = f'{conf_name}.{opt}'\n    return title, opt_aliases.get(full_name, full_name)\n\n\ndef process_action_link(env: Any, refnode: Any, has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:\n    return title, target\n\n\ndef process_shortcut_link(env: Any, refnode: Any, has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:\n    conf_name, slug = target.partition('.')[::2]\n    if not slug:\n        conf_name, slug = 'kitty', conf_name\n    full_name = f'{conf_name}.{slug}'\n    try:\n        target, stitle = shortcut_slugs[full_name]\n    except KeyError:\n        logger.warning(f'Unknown shortcut: {target}', location=refnode)\n    else:\n        if not has_explicit_title:\n            title = stitle\n    return title, target\n\n\ndef write_conf_docs(app: Any, all_kitten_names: Iterable[str]) -> None:\n    app.add_lexer('conf', ConfLexer() if version_info[0] < 3 else ConfLexer)\n    app.add_object_type(\n        'opt', 'opt',\n        indextemplate=\"pair: %s; Config Setting\",\n        parse_node=parse_opt_node,\n    )\n    # Warn about opt references that could not be resolved\n    opt_role = app.registry.domain_roles['std']['opt']\n    opt_role.warn_dangling = True\n    opt_role.process_link = process_opt_link\n\n    app.add_object_type(\n        'shortcut', 'sc',\n        indextemplate=\"pair: %s; Keyboard Shortcut\",\n        parse_node=parse_shortcut_node,\n    )\n    sc_role = app.registry.domain_roles['std']['sc']\n    sc_role.warn_dangling = True\n    sc_role.process_link = process_shortcut_link\n    shortcut_slugs.clear()\n\n    app.add_object_type(\n        'action', 'ac',\n        indextemplate=\"pair: %s; Action\",\n        parse_node=parse_action_node,\n    )\n    ac_role = app.registry.domain_roles['std']['ac']\n    ac_role.warn_dangling = True\n    ac_role.process_link = process_action_link\n\n    def generate_default_config(definition: Definition, name: str) -> None:\n        with open(f'generated/conf-{name}.rst', 'w', encoding='utf-8') as f:\n            print('.. highlight:: conf\\n', file=f)\n            f.write('\\n'.join(definition.as_rst(name, shortcut_slugs)))\n\n        conf_name = re.sub(r'^kitten-', '', name) + '.conf'\n        with open(f'generated/conf/{conf_name}', 'w', encoding='utf-8') as f:\n            text = '\\n'.join(definition.as_conf(commented=True))\n            print(text, file=f)\n\n    from kitty.options.definition import definition\n    generate_default_config(definition, 'kitty')\n\n    from kittens.runner import get_kitten_conf_docs\n    for kitten in all_kitten_names:\n        defn = get_kitten_conf_docs(kitten)\n        if defn is not None:\n            generate_default_config(defn, f'kitten-{kitten}')\n\n    from kitty.actions import as_rst\n    with open('generated/actions.rst', 'w', encoding='utf-8') as f:\n        f.write(as_rst())\n\n    from kitty.rc.base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION\n    with open('generated/matching.rst', 'w') as f:\n        print('Matching windows', file=f)\n        print('______________________________', file=f)\n        w = 'm' + MATCH_WINDOW_OPTION[MATCH_WINDOW_OPTION.find('Match') + 1:]\n        print('When matching windows,', w, file=f)\n        print('Matching tabs', file=f)\n        print('______________________________', file=f)\n        w = 'm' + MATCH_TAB_OPTION[MATCH_TAB_OPTION.find('Match') + 1:]\n        print('When matching tabs,', w, file=f)\n\n# }}}\n\n\ndef add_html_context(app: Any, pagename: str, templatename: str, context: Any, doctree: Any, *args: Any) -> None:\n    context['analytics_id'] = app.config.analytics_id\n    if 'toctree' in context:\n        # this is needed with furo to use all titles from pages\n        # in the sidebar (global) toc\n        original_toctee_function = context['toctree']\n\n        def include_sub_headings(**kwargs: Any) -> Any:\n            kwargs['titles_only'] = False\n            return original_toctee_function(**kwargs)\n\n        context['toctree'] = include_sub_headings\n\n\n@lru_cache\ndef monkeypatch_man_writer() -> None:\n    '''\n    Monkeypatch the docutils man translator to be nicer\n    '''\n    from docutils.nodes import figure\n    from docutils.writers.manpage import Translator\n    from sphinx.writers.manpage import ManualPageTranslator\n\n\n    # Improve header generation\n    def header(self: ManualPageTranslator) -> str:\n        di = getattr(self, '_docinfo')\n        di['ktitle'] = di['title'].replace('_', '-')\n        th = (\".TH \\\"%(ktitle)s\\\" %(manual_section)s\"\n              \" \\\"%(date)s\\\" \\\"%(version)s\\\"\") % di\n        if di[\"manual_group\"]:\n            th += \" \\\"%(manual_group)s\\\"\" % di\n        th += \"\\n\"\n        sh_tmpl: str = (\".SH Name\\n\"\n                   \"%(ktitle)s \\\\- %(subtitle)s\\n\")\n        return th + sh_tmpl % di  # type: ignore\n\n    setattr(ManualPageTranslator, 'header', header)\n\n    def visit_image(self: ManualPageTranslator, node: figure) -> None:\n        pass\n\n    def depart_image(self: ManualPageTranslator, node: figure) -> None:\n        pass\n\n    def depart_figure(self: ManualPageTranslator, node: figure) -> None:\n        self.body.append(' (images not supported)\\n')\n        Translator.depart_figure(self, node)\n\n    setattr(ManualPageTranslator, 'visit_image', visit_image)\n    setattr(ManualPageTranslator, 'depart_image', depart_image)\n    setattr(ManualPageTranslator, 'depart_figure', depart_figure)\n\n    orig_astext = getattr(ManualPageTranslator, 'astext')\n    def astext(self: ManualPageTranslator) -> Any:\n        b = []\n        for line in self.body:\n            if line.startswith('.SH'):\n                x, y = line.split(' ', 1)\n                parts = y.splitlines(keepends=True)\n                parts[0] = parts[0].capitalize()\n                line = x + ' ' + '\\n'.join(parts)\n            b.append(line)\n        setattr(self, 'body', b)\n        return orig_astext(self)\n    setattr(ManualPageTranslator, 'astext', astext)\n\n\ndef setup_man_pages() -> None:\n    from kittens.runner import get_kitten_cli_docs\n    base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n    for x in glob.glob(os.path.join(base, 'docs/kittens/*.rst')):\n        kn = os.path.basename(x).rpartition('.')[0]\n        if kn in ('custom', 'developing-builtin-kittens'):\n            continue\n        cd = get_kitten_cli_docs(kn) or {}\n        khn = kn.replace('_', '-')\n        man_pages.append((f'kittens/{kn}', 'kitten-' + khn, cd.get('short_desc', 'kitten Documentation'), [author], 1))\n    monkeypatch_man_writer()\n\n\ndef build_extra_man_pages() -> None:\n    base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n    kitten = os.environ.get('KITTEN_EXE_FOR_DOCS', os.path.join(base, 'kitty/launcher/kitten'))\n    if not os.path.exists(kitten):\n        kitten = os.path.join(base, 'kitty/launcher/kitty.app/Contents/MacOS/kitten')\n        if not os.path.exists(kitten):\n            subprocess.call(['find', os.path.join(base, 'kitty/launcher')])\n            raise Exception(f'The kitten binary {kitten} is not built cannot generate man pages')\n    raw = subprocess.check_output([kitten, '-h']).decode()\n    started = 0\n    names = set()\n    for line in raw.splitlines():\n        if line.strip() == '@':\n            started = len(line.rstrip()[:-1])\n        q = line.strip()\n        if started and len(q.split()) == 1 and not q.startswith('-') and ':' not in q:\n            if len(line) - len(line.lstrip()) == started:\n                if not os.path.exists(os.path.join(base, f'docs/kittens/{q}.rst')):\n                    names.add(q)\n    cwd = os.path.join(base, 'docs/_build/man')\n    subprocess.check_call([kitten, '__generate_man_pages__'], cwd=cwd)\n    subprocess.check_call([kitten, '__generate_man_pages__'] + list(names), cwd=cwd)\n\n\nif building_man_pages:\n    setup_man_pages()\n\n\ndef build_finished(*a: Any, **kw: Any) -> None:\n    if building_man_pages:\n        build_extra_man_pages()\n\n\ndef setup(app: Any) -> None:\n    os.makedirs('generated/conf', exist_ok=True)\n    from kittens.runner import all_kitten_names\n    kn = all_kitten_names()\n    write_cli_docs(kn)\n    write_remote_control_protocol_docs()\n    write_color_names_table()\n    write_conf_docs(app, kn)\n    app.connect('source-read', replace_string)\n    app.add_config_value('analytics_id', '', 'env')\n    app.connect('html-page-context', add_html_context)\n    app.connect('build-finished', build_finished)\n    app.add_lexer('session', SessionLexer() if version_info[0] < 3 else SessionLexer)\n    app.add_role('link', link_role)\n    app.add_role('commit', commit_role)\n"
  },
  {
    "path": "docs/conf.rst",
    "content": "kitty.conf\n================\n\n.. highlight:: conf\n\n\n.. only:: man\n\n    Overview\n    --------------\n\n\n|kitty| is highly customizable, everything from keyboard shortcuts, to rendering\nframes-per-second. See below for an overview of all customization possibilities.\n\nYou can open the config file within |kitty| by pressing :sc:`edit_config_file`\n(:kbd:`⌘+,` on macOS). A :file:`kitty.conf` with commented default\nconfigurations and descriptions will be created if the file does not exist.\nYou can reload the config file within |kitty| by pressing\n:sc:`reload_config_file` (:kbd:`⌃+⌘+,` on macOS) or sending |kitty| the\n``SIGUSR1`` signal with ``kill -SIGUSR1 $KITTY_PID``. You can also display the\ncurrent configuration by pressing :sc:`debug_config` (:kbd:`⌥+⌘+,` on macOS).\n\n.. _confloc:\n\n|kitty| looks for a config file in the OS config directories (usually\n:file:`~/.config/kitty/kitty.conf`) but you can pass a specific path via the\n:option:`kitty --config` option or use the :envvar:`KITTY_CONFIG_DIRECTORY`\nenvironment variable. See :option:`kitty --config` for full details.\n\n**Comments** can be added to the config file as lines starting with the ``#``\ncharacter. This works only if the ``#`` character is the first character in the\nline.\n\n**Lines can be split** by starting the next line with the ``\\`` character.\nAll leading whitespace and the ``\\`` character are removed.\n\n.. _include:\n\nYou can **include secondary config files** via the :code:`include` directive. If\nyou use a relative path for :code:`include`, it is resolved with respect to the\nlocation of the current config file. Note that environment variables are\nexpanded, so :code:`${USER}.conf` becomes :file:`name.conf` if\n:code:`USER=name`. A special environment variable :envvar:`KITTY_OS` is available,\nto detect the operating system. It is ``linux``, ``macos`` or ``bsd``.\nAlso, you can use :code:`globinclude` to include files\nmatching a shell glob pattern and :code:`envinclude` to include configuration\nfrom environment variables. Finally, you can dynamically generate configuration\nby running a program using :code:`geninclude`. For example::\n\n     # Include other.conf\n     include other.conf\n     # Include *.conf files from all subdirs of kitty.d inside the kitty config dir\n     globinclude kitty.d/**/*.conf\n     # Include the *contents* of all env vars starting with KITTY_CONF_\n     envinclude KITTY_CONF_*\n     # Run the script dynamic.py placed in the same directory as this config file\n     # and include its :file:`STDOUT`. Note that Python scripts are fastest\n     # as they use the embedded Python interpreter, but any executable script\n     # or program is supported, in any language. Remember to mark the script\n     # file executable.\n     geninclude dynamic.py\n\n\n.. include:: /generated/conf-kitty.rst\n\n\nSample kitty.conf\n--------------------\n\n.. only:: html\n\n    You can download a sample :file:`kitty.conf` file with all default settings\n    and comments describing each setting by clicking: :download:`sample\n    kitty.conf </generated/conf/kitty.conf>`.\n\n.. only:: man\n\n   You can edit a fully commented sample kitty.conf by pressing the\n   :sc:`edit_config_file` shortcut in kitty. This will generate a config file\n   with full documentation and all settings commented out. If you have a\n   pre-existing :file:`kitty.conf`, then that will be used instead, delete it to\n   see the sample file.\n\nA default configuration file can also be generated by running::\n\n    kitty +runpy 'from kitty.config import *; print(commented_out_default_config())'\n\nThis will print the commented out default config file to :file:`STDOUT`.\n\nAll mappable actions\n------------------------\n\nSee the :doc:`list of all the things you can make |kitty| can do </actions>`.\n\n.. toctree::\n   :hidden:\n\n   actions\n   wide-gamut-colors\n"
  },
  {
    "path": "docs/deccara.rst",
    "content": "Setting text styles/colors in arbitrary regions of the screen\n------------------------------------------------------------------\n\nThere already exists an escape code to set *some* text attributes in arbitrary\nregions of the screen, `DECCARA\n<https://vt100.net/docs/vt510-rm/DECCARA.html>`__. However, it is limited to\nonly a few attributes. |kitty| extends this to work with *all* SGR attributes.\nSo, for example, this can be used to set the background color in an arbitrary\nregion of the screen.\n\nThe motivation for this extension is the various problems with the existing\nsolution for erasing to background color, namely the *background color erase\n(bce)* capability. See :iss:`this discussion <160#issuecomment-346470545>`\nand `this FAQ <https://invisible-island.net/ncurses/ncurses.faq.html#bce_mismatches>`__\nfor a summary of problems with *bce*.\n\nFor example, to set the background color to blue in a rectangular region of the\nscreen from (3, 4) to (10, 11), you use::\n\n    <ESC>[2*x<ESC>[4;3;11;10;44$r<ESC>[*x\n"
  },
  {
    "path": "docs/desktop-notifications.rst",
    "content": ".. _notifications_on_the_desktop:\n\n\nDesktop notifications\n=======================\n\n|kitty| implements an extensible escape code (OSC 99) to show desktop\nnotifications. It is easy to use from shell scripts and fully extensible to show\ntitle and body. Clicking on the notification can optionally focus the window it\ncame from, and/or send an escape code back to the application running in that\nwindow.\n\nThe design of the escape code is partially based on the discussion in the\ndefunct `terminal-wg <https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/13>`__\n\nThe escape code has the form::\n\n    <OSC> 99 ; metadata ; payload <terminator>\n\nHere ``<OSC>`` is :code:`<ESC>]` and ``<terminator>`` is\n:code:`<ESC><backslash>`. The ``metadata`` is a section of colon separated\n:code:`key=value` pairs. Every key must be a single character from the set\n:code:`a-zA-Z` and every value must be a word consisting of characters from\nthe set :code:`a-zA-Z0-9-_/\\+.,(){}[]*&^%$#@!`~`. The payload must be\ninterpreted based on the metadata section. The two semi-colons *must* always be\npresent even when no metadata is present.\n\nBefore going into details, let's see how one can display a simple, single line\nnotification from a shell script::\n\n    printf '\\x1b]99;;Hello world\\x1b\\\\'\n\nTo show a message with a title and a body::\n\n    printf '\\x1b]99;i=1:d=0;Hello world\\x1b\\\\'\n    printf '\\x1b]99;i=1:p=body;This is cool\\x1b\\\\'\n\n.. tip::\n\n   |kitty| also comes with its own :doc:`statically compiled command line tool </kittens/notify>` to easily display\n   notifications, with all their advanced features. For example:\n\n   .. code-block:: sh\n\n        kitten notify \"Hello world\" A good day to you\n\nThe most important key in the metadata is the ``p`` key, it controls how the\npayload is interpreted. A value of ``title`` means the payload is setting the\ntitle for the notification. A value of ``body`` means it is setting the body,\nand so on, see the table below for full details.\n\nThe design of the escape code is fundamentally chunked, this is because\ndifferent terminal emulators have different limits on how large a single escape\ncode can be. Chunking is accomplished by the ``i`` and ``d`` keys. The ``i``\nkey is the *notification id* which is an :ref:`identifier`.\nThe ``d`` key stands for *done* and can only take the\nvalues ``0`` and ``1``. A value of ``0`` means the notification is not yet done\nand the terminal emulator should hold off displaying it. A non-zero value means\nthe notification is done, and should be displayed. You can specify the title or\nbody multiple times and the terminal emulator will concatenate them, thereby\nallowing arbitrarily long text (terminal emulators are free to impose a sensible\nlimit to avoid Denial-of-Service attacks). The size of the payload must be no\nlonger than ``2048`` bytes, *before being encoded* or ``4096`` encoded bytes.\n\nBoth the ``title`` and ``body`` payloads must be either :ref:`safe_utf8` text\nor UTF-8 text that is :ref:`base64` encoded, in which case there must be an\n``e=1`` key in the metadata to indicate the payload is :ref:`base64`\nencoded. No HTML or other markup in the plain text is allowed. It is strictly\nplain text, to be interpreted as such.\n\nAllowing users to filter notifications\n-------------------------------------------------------\n\n.. versionadded:: 0.36.0\n   Specifying application name and notification type\n\nWell behaved applications should identify themselves to the terminal\nby means of two keys ``f`` which is the application name and ``t``\nwhich is the notification type. These are free form keys, they can contain\nany values, their purpose is to allow users to easily filter out\nnotifications they do not want. Both keys must have :ref:`base64`\nencoded UTF-8 text as their values. The ``t`` key can be specified multiple\ntimes, as notifications can have more than one type. See the `freedesktop.org\nspec\n<https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html#categories>`__\nfor examples of notification types.\n\n.. note::\n   The application name should generally be set to the filename of the\n   applications `desktop file\n   <https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#file-naming>`__\n   (without the ``.desktop`` part) or the bundle identifier for a macOS\n   application. While not strictly necessary, this allows the terminal\n   emulator to deduce an icon for the notification when one is not specified.\n\n.. tip::\n\n   |kitty| has sophisticated notification filtering and management\n   capabilities via :opt:`filter_notification`.\n\n\nBeing informed when user activates the notification\n-------------------------------------------------------\n\nWhen the user clicks the notification, a couple of things can happen, the\nterminal emulator can focus the window from which the notification came, and/or\nit can send back an escape code to the application indicating the notification\nwas activated. This is controlled by the ``a`` key which takes a comma separated\nset of values, ``report`` and ``focus``. The value ``focus`` means focus the\nwindow from which the notification was issued and is the default. ``report``\nmeans send an escape code back to the application. The format of the returned\nescape code is::\n\n    <OSC> 99 ; i=identifier ; <terminator>\n\nThe value of ``identifier`` comes from the ``i`` key in the escape code sent by\nthe application. If the application sends no identifier, then the terminal\n*must* use ``i=0``. (Ideally ``i`` should have been left out from the response,\nbut for backwards compatibility ``i=0`` is used). Actions can be preceded by a\nnegative sign to turn them off, so for example if you do not want any action,\nturn off the default ``focus`` action with::\n\n    a=-focus\n\nComplete specification of all the metadata keys is in the :ref:`table below <keys_in_notificatons_protocol>`.\nIf a terminal emulator encounters a key in the metadata it does not understand,\nthe key *must* be ignored, to allow for future extensibility of this escape\ncode. Similarly if values for known keys are unknown, the terminal emulator\n*should* either ignore the entire escape code or perform a best guess effort to\ndisplay it based on what it does understand.\n\n\nBeing informed when a notification is closed\n------------------------------------------------\n\n.. versionadded:: 0.36.0\n   Notifications of close events\n\nIf you wish to be informed when a notification is closed, you can specify\n``c=1`` when sending the notification. For example::\n\n    <OSC> 99 ; i=mynotification : c=1 ; hello world <terminator>\n\nThen, the terminal will send the following\nescape code to inform when the notification is closed::\n\n    <OSC> 99 ; i=mynotification : p=close ; <terminator>\n\nIf no notification id was specified ``i=0`` will be used in the response\n\nIf ``a=report`` is specified and the notification is activated/clicked on\nthen both the activation report and close notification are sent. If the notification\nis updated then the close event is not sent unless the updated notification\nalso requests a close notification.\n\nNote that on some platforms, such as macOS, the OS does not inform applications\nwhen notifications are closed, on such platforms, terminals reply with::\n\n    <OSC> 99 ; i=mynotification : p=close ; untracked <terminator>\n\nThis means that the terminal has no way of knowing when the notification is\nclosed. Instead, applications can poll the terminal to determine which\nnotifications are still alive (not closed), with::\n\n    <OSC> 99 ; i=myid : p=alive ; <terminator>\n\nThe terminal will reply with::\n\n    <OSC> 99 ; i=myid : p=alive ; id1,id2,id3 <terminator>\n\nHere, ``myid`` is present for multiplexer support. The response from the terminal\ncontains a comma separated list of ids that are still alive.\n\n\nUpdating or closing an existing notification\n----------------------------------------------\n\n.. versionadded:: 0.36.0\n   The ability to update and close a previous notification\n\nTo update a previous notification simply send a new notification with the same\n*notification id* (``i`` key) as the one you want to update. If the original\nnotification is still displayed it will be replaced, otherwise a new\nnotification is displayed. This can be used, for example, to show progress of\nan operation. How smoothly the existing notification is replaced\ndepends on the underlying OS, for example, on Linux the replacement is usually flicker\nfree, on macOS it isn't, because of Apple's design choices.\nNote that if no ``i`` key is specified, no updating must take place, even if\nthere is a previous notification without an identifier. The terminal must\ntreat these as being two unique *unidentified* notifications.\n\nTo close a previous notification, send::\n\n    <OSC> i=<notification id> : p=close ; <terminator>\n\nThis will close a previous notification with the specified id. If no such\nnotification exists (perhaps because it was already closed or it was activated)\nthen the request is ignored. If no ``i`` key is specified, this must be a no-op.\n\n\nAutomatically expiring notifications\n-------------------------------------\n\nA notification can be marked as expiring (being closed) automatically after\na specified number of milliseconds using the ``w`` key. The default if\nunspecified is ``-1`` which means to use whatever expiry policy the OS has for\nnotifications. A value of ``0`` means the notification should never expire.\nValues greater than zero specify the number of milliseconds after which the\nnotification should be auto-closed. Note that the value of ``0``\nis best effort, some platforms honor it and some do not. Positive values\nare robust, since they can be implemented by the terminal emulator itself,\nby manually closing the notification after the expiry time. The notification\ncould still be closed before the expiry time by user interaction or OS policy,\nbut it is guaranteed to be closed once the expiry time has passed.\n\n\nAdding icons to notifications\n--------------------------------\n\n.. versionadded:: 0.36.0\n   Custom icons in notifications\n\nApplications can specify a custom icon to be displayed with a notification.\nThis can be the application's logo or a symbol such as error or warning\nsymbols. The simplest way to specify an icon is by *name*, using the ``n``\nkey. The value of this key is :ref:`base64` encoded UTF-8 text. Names\ncan be either application names, or symbol names. The terminal emulator\nwill try to resolve the name based on icons and applications available\non the computer it is running on. The following list of well defined names\nmust be supported by any terminal emulator implementing this spec.\nThe ``n`` key can be specified multiple times, the terminal will go through\nthe list in order and use the first icon that it finds available on the\nsystem.\n\n.. table:: Universally available icon names\n\n   ======================== ==============================================\n   Name                     Description\n   ======================== ==============================================\n   ``error``                An error symbol\n   ``warn``, ``warning``    A warning symbol\n   ``info``                 A symbol denoting an informational message\n   ``question``             A symbol denoting asking the user a question\n   ``help``                 A symbol denoting a help message\n   ``file-manager``         A symbol denoting a generic file manager application\n   ``system-monitor``       A symbol denoting a generic system monitoring/information application\n   ``text-editor``          A symbol denoting a generic text editor application\n   ======================== ==============================================\n\nIf an icon name is an application name it should be an application identifier,\nsuch as the filename of the application's :file:`.desktop` file on Linux or its\nbundle identifier on macOS. For example if the cross-platform application\nFooBar has a desktop file named: :file:`foo-bar.desktop` and a bundle\nidentifier of ``net.foo-bar-website.foobar`` then it should use the icon names\n``net.foo-bar-website.foobar`` *and* ``foo-bar`` so that terminals running on\nboth platforms can find the application icon.\n\nIf no icon is specified, but the ``f`` key (application name) is specified, the\nterminal emulator should use the value of the ``f`` key to try to find a\nsuitable icon.\n\nAdding icons by transmitting icon data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis can be done by using the ``p=icon`` key. Then, the payload is the icon\nimage in any of the ``PNG``, ``JPEG`` or ``GIF`` image formats. It is recommended\nto use an image size of ``256x256`` for icons. Since icons are binary data,\nthey must be transmitted encoded, with ``e=1``.\n\nWhen both an icon name and an image are specified, the terminal emulator must\nfirst try to find a locally available icon matching the name and only if one\nis not found, fallback to the provided image. This is so that users are\npresented with icons from their current icon theme, where possible.\n\nTransmitted icon data can be cached using the ``g`` key. The value of the ``g``\nkey must be a random globally unique UUID like :ref:`identifier`. Then, the\nterminal emulator will cache the transmitted data using that key. The cache\nshould exist for as long as the terminal emulator remains running. Thus, in\nfuture notifications, the application can simply send the ``g`` key to display\na previously cached icon image with needing to re-transmit the actual data with\n``p=icon``. The ``g`` key refers only to the icon data, multiple different\nnotifications with different icon or application names can use the same ``g``\nkey to refer to the same icon. Terminal multiplexers must cache icon data\nthemselves and refresh it in the underlying terminal implementation when\ndetaching and then re-attaching. This means that applications once started\nneed to transmit icon data only once until they are quit.\n\n.. note::\n   To avoid DoS attacks terminal implementations can impose a reasonable max size\n   on the icon cache and evict icons in order of last used. Thus theoretically,\n   a previously cached icon may become unavailable, but given that icons are\n   small images, practically this is not an issue in all but the most resource\n   constrained environments, and the failure mode is simply that the icon is not\n   displayed.\n\n.. note::\n   How the icon is displayed depends on the underlying OS notifications\n   implementation. For example, on Linux, typically a single icon is displayed.\n   On macOS, both the terminal emulator's icon and the specified custom icon\n   are displayed.\n\n\nAdding buttons to the notification\n---------------------------------------\n\nButtons can be added to the notification using the *buttons* payload, with ``p=buttons``.\nButtons are a list of UTF-8 text separated by the Unicode Line Separator\ncharacter (U+2028) which is the UTF-8 bytes ``0xe2 0x80 0xa8``. They can be\nsent either as :ref:`safe_utf8` or :ref:`base64`. When the user clicks on one\nof the buttons, and reporting is enabled with ``a=report``, the terminal will\nsend an escape code of the form::\n\n    <OSC> 99 ; i=identifier ; button_number <terminator>\n\nHere, `button_number` is a number from 1 onwards, where 1 corresponds\nto the first button, two to the second and so on. If the user activates the\nnotification as a whole, and not a specific button, the response, as described\nabove is::\n\n    <OSC> 99 ; i=identifier ; <terminator>\n\nIf no identifier was specified when creating the notification, ``i=0`` is used.\nThe terminal *must not* send a response unless report is requested with\n``a=report``.\n\n.. note::\n\n   The appearance of the buttons depends on the underlying OS implementation.\n   On most Linux systems, the buttons appear as individual buttons on the\n   notification. On macOS they appear as a drop down menu that is accessible\n   when hovering the notification. Generally, using more than two or three\n   buttons is not a good idea.\n\n.. _notifications_query:\n\nPlaying a sound with notifications\n-----------------------------------------\n\n.. versionadded:: 0.36.0\n   The ability to control the sound played with notifications\n\nBy default, notifications may or may not have a sound associated with them\ndepending on the policies of the OS notifications service. Sometimes it\nmight be useful to ensure a notification is not accompanied by a sound.\nThis can be done by using the ``s`` key which accepts :ref:`base64` encoded\nUTF-8 text as its value. The set of known sounds names is in the table below,\nany other names are implementation dependent, for instance, on Linux, terminal emulators will\nprobably support the `standard sound names\n<https://specifications.freedesktop.org/sound-naming-spec/latest/#names>`__\n\n.. table:: Standard sound names\n\n   ======================== ==============================================\n   Name                     Description\n   ======================== ==============================================\n   ``system``               The default system sound for a notification, which may be some kind of beep or just silence\n   ``silent``               No sound must accompany the notification\n   ``error``                A sound associated with error messages\n   ``warn``, ``warning``    A sound associated with warning messages\n   ``info``                 A sound associated with information messages\n   ``question``             A sound associated with questions\n   ======================== ==============================================\n\nSupport for sound names can be queried as described below.\n\n\nQuerying for support\n-------------------------\n\n.. versionadded:: 0.36.0\n   The ability to query for support\n\nAn application can query the terminal emulator for support of this protocol, by\nsending the following escape code::\n\n    <OSC> 99 ; i=<some identifier> : p=? ; <terminator>\n\nA conforming terminal must respond with an escape code of the form::\n\n    <OSC> 99 ; i=<some identifier> : p=? ; key=value : key=value <terminator>\n\nThe identifier is present to support terminal multiplexers, so that they know\nwhich window to redirect the query response too.\n\nHere, the ``key=value`` parts specify details about what the terminal\nimplementation supports. Currently, the following keys are defined:\n\n=======  ================================================================================\nKey      Value\n=======  ================================================================================\n``a``    Comma separated list of actions from the ``a`` key that the terminal\n         implements. If no actions are supported, the ``a`` key must be absent from the\n         query response.\n\n``c``    ``c=1`` if the terminal supports close events, otherwise the ``c``\n         must be omitted.\n\n``o``    Comma separated list of occasions from the ``o`` key that the\n         terminal implements. If no occasions are supported, the value\n         ``o=always`` must be sent in the query response.\n\n``p``    Comma separated list of supported payload types (i.e. values of the\n         ``p`` key that the terminal implements). These must contain at least\n         ``title``.\n\n``s``    Comma separated list of sound names from the table of standard sound names above.\n         Terminals will report the list of standard sound names they support.\n         Terminals *should* support at least ``system`` and ``silent``.\n\n``u``    Comma separated list of urgency values that the terminal implements.\n         If urgency is not supported, the ``u`` key must be absent from the\n         query response.\n\n``w``    ``w=1`` if the terminal supports auto expiring of notifications.\n=======  ================================================================================\n\nIn the future, if this protocol expands, more keys might be added. Clients must\nignore keys they do not understand in the query response.\n\nTo check if a terminal emulator supports this notifications protocol the best way is to\nsend the above *query action* followed by a request for the `primary device\nattributes <https://vt100.net/docs/vt510-rm/DA1.html>`_. If you get back an\nanswer for the device attributes without getting back an answer for the *query\naction* the terminal emulator does not support this notifications protocol.\n\n.. _keys_in_notificatons_protocol:\n\nSpecification of all keys used in the protocol\n--------------------------------------------------\n\n=======  ====================  ========== =================\nKey      Value                 Default    Description\n=======  ====================  ========== =================\n``a``    Comma separated list  ``focus``  What action to perform when the\n         of ``report``,                   notification is clicked\n         ``focus``, with\n         optional leading\n         ``-``\n\n``c``    ``0`` or ``1``        ``0``      When non-zero an escape code is sent to the application when the notification is closed.\n\n``d``    ``0`` or ``1``        ``1``      Indicates if the notification is\n                                          complete or not. A non-zero value\n                                          means it is complete.\n\n``e``    ``0`` or ``1``        ``0``      If set to ``1`` means the payload is :ref:`base64` encoded UTF-8,\n                                          otherwise it is plain UTF-8 text with no C0 control codes in it\n\n``f``    :ref:`base64`         ``unset``  The name of the application sending the notification. Can be used to filter out notifications.\n         encoded UTF-8\n         application name\n\n``g``    :ref:`identifier`     ``unset``  Identifier for icon data. Make these globally unique,\n                                          like an UUID.\n\n``i``    :ref:`identifier`     ``unset``  Identifier for the notification. Make these globally unique,\n                                          like an UUID, so that terminal multiplexers can\n                                          direct responses to the correct window. Note that for backwards\n                                          compatibility reasons i=0 is special and should not be used.\n\n``n``    :ref:`base64`         ``unset``  Icon name. Can be specified multiple times.\n         encoded UTF-8\n         application name\n\n``o``    One of ``always``,    ``always`` When to honor the notification request. ``unfocused`` means when the window\n         ``unfocused`` or                 the notification is sent on does not have keyboard focus. ``invisible``\n         ``invisible``                    means the window both is unfocused\n                                          and not visible to the user, for example, because it is in an inactive tab or\n                                          its OS window is not currently active.\n                                          ``always`` is the default and always honors the request.\n\n``p``    One of ``title``,     ``title``  Type of the payload. If a notification has no title, the body will be used as title.\n         ``body``,                        A notification with not title and no body is ignored. Terminal\n         ``close``,                       emulators should ignore payloads of unknown type to allow for future\n         ``icon``,                        expansion of this protocol.\n         ``?``, ``alive``,\n         ``buttons``\n\n``s``    :ref:`base64`         ``system`` The sound name to play with the notification. ``silent`` means no sound.\n         encoded sound                    ``system`` means to play the default sound, if any, of the platform notification service.\n         name                             Other names are implementation dependent.\n\n``t``    :ref:`base64`         ``unset``  The type of the notification. Used to filter out notifications. Can be specified multiple times.\n         encoded UTF-8\n         notification type\n\n``u``    ``0, 1 or 2``         ``unset``  The *urgency* of the notification. ``0`` is low, ``1`` is normal and ``2`` is critical.\n                                          If not specified normal is used.\n\n\n``w``    ``>=-1``              ``-1``     The number of milliseconds to auto-close the notification after.\n=======  ====================  ========== =================\n\n\n.. versionadded:: 0.35.0\n   Support for the ``u`` key to specify urgency\n\n.. versionadded:: 0.31.0\n   Support for the ``o`` key to prevent notifications from focused windows\n\n\n.. note::\n   |kitty| also supports the `legacy OSC 9 protocol developed by iTerm2\n   <https://iterm2.com/documentation-escape-codes.html>`__ for desktop\n   notifications.\n\n\n.. _base64:\n\nBase64\n---------------\n\nThe base64 encoding used in the this specification is the one defined in\n:rfc:`4648`. When a base64 payload is chunked, either the chunking should be\ndone before encoding or after. When the chunking is done before encoding, no\nmore than 2048 bytes of data should be encoded per chunk and the encoded data\n**must** include the base64 padding bytes, if any. When the chunking is done\nafter encoding, each encoded chunk must be no more than 4096 bytes in size.\nThere may or may not be padding bytes at the end of the last chunk, terminals\nmust handle either case.\n\n\n.. _safe_utf8:\n\nEscape code safe UTF-8\n--------------------------\n\nThis must be valid UTF-8 as per the spec in :rfc:`3629`. In addition, in order\nto make it safe for transmission embedded inside an escape code, it must\ncontain none of the C0 and C1 control characters, that is, the Unicode\ncharacters: U+0000 (NUL) - U+1F (Unit separator), U+7F (DEL) and U+80 (PAD) - U+9F\n(APC). Note that in particular, this means that no newlines, carriage returns,\ntabs, etc. are allowed.\n\n\n.. _identifier:\n\nIdentifier\n----------------\n\nAny string consisting solely of characters from the set ``[a-zA-Z0-9_-+.]``,\nthat is, the letters ``a-z``, ``A-Z``, the underscore, the hyphen, the plus\nsign and the period. Applications should make these globally unique, like a\nUUID for maximum robustness.\n\n\n.. important::\n   Terminals **must** sanitize ids received from client programs before sending\n   them back in responses, to mitigate input injection based attacks. That is, they must\n   either reject ids containing characters not from the above set, or remove\n   bad characters when reading ids sent to them.\n"
  },
  {
    "path": "docs/extract-rst-targets.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nfrom typing import Dict, Iterator\n\ntgt_pat = re.compile(r'^.. _(\\S+?):$', re.MULTILINE)\ntitle_pat = re.compile('^(.+)\\n[-=^#*]{5,}$', re.MULTILINE)\n\n\ndef find_explicit_targets(text: str) -> Iterator[str]:\n    for m in tgt_pat.finditer(text):\n        yield m.group(1)\n\n\ndef find_page_title(text: str) -> str:\n    for m in title_pat.finditer(text):\n        return m.group(1)\n    return ''\n\n\ndef main() -> Dict[str, Dict[str, str]]:\n    refs = {}\n    docs = {}\n    base = os.path.dirname(os.path.abspath(__file__))\n    for dirpath, dirnames, filenames in os.walk(base):\n        if 'generated' in dirnames:\n            dirnames.remove('generated')\n        for f in filenames:\n            if f.endswith('.rst'):\n                with open(os.path.join(dirpath, f)) as stream:\n                    raw = stream.read()\n                href = os.path.relpath(stream.name, base).replace(os.sep, '/')\n                href = href.rpartition('.')[0] + '/'\n                docs[href.rstrip('/')] = find_page_title(raw)\n                first_line = raw.lstrip('\\n').partition('\\n')[0]\n                first_target_added = False\n                for explicit_target in find_explicit_targets(raw):\n                    # Shorten the reference link to the top of the page.\n                    # Note that anchor links should still be used in HTML docs\n                    # to allow jumping within the same page.\n                    if not first_target_added:\n                        first_target_added = True\n                        if first_line.startswith(f'.. _{explicit_target}:'):\n                            refs[explicit_target] = href\n                            continue\n                    refs[explicit_target] = href + f'#{explicit_target.replace(\"_\", \"-\")}'\n    return {'ref': refs, 'doc': docs}\n\n\nif __name__ == '__main__':\n    import json\n    print(json.dumps(main(), indent=2))\n"
  },
  {
    "path": "docs/faq.rst",
    "content": "Frequently Asked Questions\n==============================\n\n.. highlight:: sh\n\nSome special symbols are rendered small/truncated in kitty?\n-----------------------------------------------------------\n\nThe number of cells a Unicode character takes up are controlled by the Unicode\nstandard. All characters are rendered in a single cell unless the Unicode\nstandard says they should be rendered in two cells. When a symbol does not fit,\nit will either be rescaled to be smaller or truncated (depending on how much\nextra space it needs). This is often different from other terminals which just\nlet the character overflow into neighboring cells, which is fine if the\nneighboring cell is empty, but looks terrible if it is not.\n\nSome programs, like Powerline, vim with fancy gutter symbols/status-bar, etc.\nuse Unicode characters from the private use area to represent symbols. Often\nthese symbols are wide and should be rendered in two cells. However, since\nprivate use area symbols all have their width set to one in the Unicode\nstandard, |kitty| renders them either smaller or truncated. The exception is if\nthese characters are followed by a space or en-space (U+2002) in which case\nkitty makes use of the extra cell to render them in two cells. This behavior\ncan be turned off for specific symbols using :opt:`narrow_symbols`.\n\nAs of version 0.40 kitty has innovated a :doc:`new protocol\n<text-sizing-protocol>` that allows programs running in the terminal to control\nhow many cells a character is rendered in thereby solving the issue of\ncharacter width once and for all.\n\nSimilarly, some monospaced font families are buggy and have bold or italic\nfaces that have characters wider than the width of the normal face, these\nwill also result in clipping. Such issues should be reported to the font\ndeveloper. Monospaced font families must have all their characters rendered\nwithin a fixed width across all faces of the font, otherwise they aren't really\nmonospaced.\n\n\nUsing a color theme with a background color does not work well in vim?\n-----------------------------------------------------------------------\n\nFirst, be sure to `use a color scheme in vim <https://github.com/kovidgoyal/kitty/discussions/8196#discussioncomment-11739991>`__\ninstead of relying on the terminal theme. Otherwise, background and text selection colours\nmay be difficult to read.\n\nSadly, vim has very poor out-of-the-box detection for modern terminal features.\nFurthermore, it `recently broke detection even more <https://github.com/vim/vim/issues/11729>`__.\nIt kind of, but not really, supports terminfo, except it overrides it with its own hard-coded\nvalues when it feels like it. Worst of all, it has no ability to detect modern\nfeatures not present in terminfo, at all, even security sensitive ones like\nbracketed paste.\n\nThankfully, probably as a consequence of this lack of detection, vim allows users to\nconfigure these low level details. So, to make vim work well with any modern\nterminal, including kitty, add the following to your :file:`~/.vimrc`.\n\n.. code-block:: vim\n\n    \" Mouse support\n    set mouse=a\n    set ttymouse=sgr\n    set balloonevalterm\n    \" Styled and colored underline support\n    let &t_AU = \"\\e[58:5:%dm\"\n    let &t_8u = \"\\e[58:2:%lu:%lu:%lum\"\n    let &t_Us = \"\\e[4:2m\"\n    let &t_Cs = \"\\e[4:3m\"\n    let &t_ds = \"\\e[4:4m\"\n    let &t_Ds = \"\\e[4:5m\"\n    let &t_Ce = \"\\e[4:0m\"\n    \" Strikethrough\n    let &t_Ts = \"\\e[9m\"\n    let &t_Te = \"\\e[29m\"\n    \" Truecolor support\n    let &t_8f = \"\\e[38:2:%lu:%lu:%lum\"\n    let &t_8b = \"\\e[48:2:%lu:%lu:%lum\"\n    let &t_RF = \"\\e]10;?\\e\\\\\"\n    let &t_RB = \"\\e]11;?\\e\\\\\"\n    \" Bracketed paste\n    let &t_BE = \"\\e[?2004h\"\n    let &t_BD = \"\\e[?2004l\"\n    let &t_PS = \"\\e[200~\"\n    let &t_PE = \"\\e[201~\"\n    \" Cursor control\n    let &t_RC = \"\\e[?12$p\"\n    let &t_SH = \"\\e[%d q\"\n    let &t_RS = \"\\eP$q q\\e\\\\\"\n    let &t_SI = \"\\e[5 q\"\n    let &t_SR = \"\\e[3 q\"\n    let &t_EI = \"\\e[1 q\"\n    let &t_VS = \"\\e[?12l\"\n    \" Focus tracking\n    let &t_fe = \"\\e[?1004h\"\n    let &t_fd = \"\\e[?1004l\"\n    execute \"set <FocusGained>=\\<Esc>[I\"\n    execute \"set <FocusLost>=\\<Esc>[O\"\n    \" Window title\n    let &t_ST = \"\\e[22;2t\"\n    let &t_RT = \"\\e[23;2t\"\n\n    \" vim hardcodes background color erase even if the terminfo file does\n    \" not contain bce. This causes incorrect background rendering when\n    \" using a color theme with a background color in terminals such as\n    \" kitty that do not support background color erase.\n    let &t_ut=''\n\nThese settings must be placed **before** setting the ``colorscheme``. It is\nalso important that the value of the vim ``term`` variable is not changed\nafter these settings.\n\nI get errors about the terminal being unknown or opening the terminal failing or functional keys like arrow keys don't work?\n-------------------------------------------------------------------------------------------------------------------------------\n\nThese issues all have the same root cause: the kitty terminfo files not being\navailable. The most common way this happens is SSHing into a computer that does\nnot have the kitty terminfo files. The simplest fix for that is running::\n\n    kitten ssh myserver\n\nIt will automatically copy over the terminfo files and also magically enable\n:doc:`shell integration </shell-integration>` on the remote machine.\n\nThis :doc:`ssh kitten <kittens/ssh>` takes all the same command line arguments\nas :program:`ssh`, you can alias it to something small in your shell's rc files\nto avoid having to type it each time::\n\n    alias s=\"kitten ssh\"\n\nIf this does not work, see :ref:`manual_terminfo_copy` for alternative ways to\nget the kitty terminfo files onto a remote computer.\n\nThe next most common reason for this is if you are running commands as root\nusing :program:`sudo` or :program:`su`. These programs often filter the\n:envvar:`TERMINFO` environment variable which is what points to the kitty\nterminfo files.\n\nFirst, make sure the :envvar:`TERM` is set to ``xterm-kitty`` in the sudo\nenvironment. By default, it should be automatically copied over.\n\nIf you are using a well maintained Linux distribution, it will have a\n``kitty-terminfo`` package that you can simply install to make the kitty\nterminfo files available system-wide. Then the problem will no longer occur.\n\nAlternately, you can configure :program:`sudo` to preserve :envvar:`TERMINFO`\nby running ``sudo visudo`` and adding the following line::\n\n    Defaults env_keep += \"TERM TERMINFO\"\n\nIf none of these are suitable for you, you can run sudo as ::\n\n    sudo TERMINFO=\"$TERMINFO\"\n\nThis will make :envvar:`TERMINFO` available\nin the sudo environment. Create an alias in your shell rc files to make this\nconvenient::\n\n    alias sudo=\"sudo TERMINFO=\\\"$TERMINFO\\\"\"\n\nIf you have double width characters in your prompt, you may also need to\nexplicitly set a UTF-8 locale, like::\n\n    export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8\n\n\nI cannot use the key combination X in program Y?\n-------------------------------------------------------\n\nFirst, run::\n\n    kitten show-key -m kitty\n\nPress the key combination X. If the kitten reports the key press\nthat means kitty is correctly sending the key press to terminal programs.\nYou need to report the issue to the developer of the terminal program. Most\nlikely they have not added support for :doc:`/keyboard-protocol`.\n\nIf the kitten does not report it, it means that the key is bound to some action\nin kitty. You can unbind it in :file:`kitty.conf` with:\n\n.. code-block:: conf\n\n   map X no_op\n\nHere X is the keys you press on the keyboard. So for example\n:kbd:`ctrl+shift+1`.\n\n\nHow do I change the colors in a running kitty instance?\n------------------------------------------------------------\n\nThe easiest way to do it is to use the :doc:`themes kitten </kittens/themes>`,\nto choose a new color theme. Simply run::\n\n    kitten themes\n\nAnd choose your theme from the list.\n\nYou can also define keyboard shortcuts to set colors, for example::\n\n    map f1 set_colors --configured /path/to/some/config/file/colors.conf\n\nOr you can enable :doc:`remote control <remote-control>` for |kitty| and use\n:ref:`at-set-colors`. The shortcut mapping technique has the same syntax as the\nremote control command, for details, see :ref:`at-set-colors`.\n\nTo change colors when SSHing into a remote host, use the :opt:`color_scheme\n<kitten-ssh.color_scheme>` setting for the :doc:`ssh kitten <kittens/ssh>`.\n\nAdditionally, you can use the escape code described in :doc:`color-stack`\nto set colors in a single window.\nExamples of using OSC escape codes to set colors::\n\n    Change the default foreground color:\n    printf '\\x1b]21;foreground=#ff0000\\x1b\\\\'\n    Change the default background color:\n    printf '\\x1b]21;background=blue\\x1b\\\\'\n    Change the cursor color:\n    printf '\\x1b]21;cursor=blue\\x1b\\\\'\n    Change the selection background color:\n    printf '\\x1b]21;selection_background=blue\\x1b\\\\'\n    Change the selection foreground color:\n    printf '\\x1b]21;selection_foreground=blue\\x1b\\\\'\n    Change the nth color (0 - 255):\n    printf '\\x1b]21;n=green\\x1b\\\\'\n\nSee :doc:`color-stack` for details on the syntax for specifying colors and\nhow to query current colors.\n\n\nHow do I specify command line options for kitty on macOS?\n---------------------------------------------------------------\n\nApple does not want you to use command line options with GUI applications. To\nworkaround that limitation, |kitty| will read command line options from the file\n:file:`<kitty config dir>/macos-launch-services-cmdline` when it is launched\nfrom the GUI, i.e. by clicking the |kitty| application icon or using\n``open -a kitty``. Note that this file is *only read* when running via the GUI.\nThe contents of the file are assumed to be the command line to pass to kitty in\nshell syntax, for example::\n\n    --single-instance --override background=red\n\nYou can, of course, also run |kitty| from a terminal with command line options,\nusing: :file:`/Applications/kitty.app/Contents/MacOS/kitty`.\n\nAnd within |kitty| itself, you can always run |kitty| using just ``kitty`` as it\ncleverly adds itself to the :envvar:`PATH`.\n\n\nI catted a binary file and now kitty is hung?\n-----------------------------------------------\n\n**Never** output unknown binary data directly into a terminal.\n\nTerminals have a single channel for both data and control. Certain bytes\nare control codes. Some of these control codes are of arbitrary length, so if\nthe binary data you output into the terminal happens to contain the starting\nsequence for one of these control codes, the terminal will hang waiting for the\nclosing sequence. Press :sc:`reset_terminal` to reset the terminal.\n\nIf you do want to cat unknown data, use ``cat -v``.\n\n\nkitty is not able to use my favorite font?\n---------------------------------------------\n\n|kitty| achieves its stellar performance by caching alpha masks of each rendered\ncharacter on the GPU, and rendering them all in parallel. This means it is a\nstrictly character cell based display. As such it can use only monospace fonts,\nsince every cell in the grid has to be the same size. Furthermore, it needs\nfonts to be freely resizable, so it does not support bitmapped fonts.\n\n.. note::\n   If you are trying to use a font patched with `Nerd Fonts\n   <https://nerdfonts.com/>`__ symbols, don't do that as patching destroys\n   fonts. There is no need, kitty has a builtin NERD font and will use it for\n   symbols not found in any other font on your system.\n   If you have patched fonts on your system they might be used instead for NERD\n   symbols, so to force kitty to use the pure NERD font for NERD symbols,\n   add the following line to :file:`kitty.conf`::\n\n        # Nerd Fonts v3.4.0\n\n        symbol_map U+e000-U+e00a,U+e0a0-U+e0a2,U+e0a3,U+e0b0-U+e0b3,U+e0b4-U+e0c8,U+e0ca,U+e0cc-U+e0d7,U+e200-U+e2a9,U+e300-U+e3e3,U+e5fa-U+e6b7,U+e700-U+e8ef,U+ea60-U+ec1e,U+ed00-U+efce,U+f000-U+f2ff,U+f300-U+f381,U+f400-U+f533,U+f0001-U+f1af0 Symbols Nerd Font Mono\n\n   Those Unicode symbols not in the `Unicode private use areas\n   <https://en.wikipedia.org/wiki/Private_Use_Areas>`__ are\n   not included.\n\nIf your font is not listed in ``kitten choose-fonts`` it means that it is not\nmonospace or is a bitmapped font. On Linux you can list all monospace fonts\nwith::\n\n    fc-list : family spacing outline scalable | grep -e spacing=100 -e spacing=90 | grep -e outline=True | grep -e scalable=True\n\nOn macOS, you can open *Font Book* and look in the :guilabel:`Fixed width`\ncollection to see all monospaced fonts on your system.\n\nNote that **on Linux**, the spacing property is calculated by fontconfig based on actual glyph\nwidths in the font. If for some reason fontconfig concludes your favorite\nmonospace font does not have ``spacing=100`` you can override it by using the\nfollowing :file:`~/.config/fontconfig/fonts.conf`::\n\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n    <fontconfig>\n    <match target=\"scan\">\n        <test name=\"family\">\n            <string>Your Font Family Name</string>\n        </test>\n        <edit name=\"spacing\">\n            <int>100</int>\n        </edit>\n    </match>\n    </fontconfig>\n\nAfter creating (or modifying) this file, you may need to run the following\ncommand to rebuild your fontconfig cache::\n\n    fc-cache -r\n\nThen, the font will be available in ``kitten choose-fonts``.\n\n\nHow can I assign a single global shortcut to bring up the kitty terminal?\n-----------------------------------------------------------------------------\n\nUse the :ref:`panel kitten <quake>`, this allows you to use kitty as a quick\naccess Quake like terminal and even to use kitty as the desktop background, if\nso desired.\n\n\nI do not like the kitty icon!\n-------------------------------\n\nThe kitty icon was created as tribute to my cat of nine years who passed away,\nas such it is not going to change. However, if you do not like it, there are\nmany alternate icons available, click on an icon to visit its homepage:\n\n.. image:: https://github.com/k0nserv/kitty-icon/raw/main/kitty.iconset/icon_256x256.png\n   :target: https://github.com/k0nserv/kitty-icon\n   :width: 256\n\n.. image:: https://github.com/DinkDonk/kitty-icon/raw/main/kitty-dark.png\n   :target: https://github.com/DinkDonk/kitty-icon\n   :width: 256\n\n.. image:: https://github.com/DinkDonk/kitty-icon/raw/main/kitty-light.png\n   :target: https://github.com/DinkDonk/kitty-icon\n   :width: 256\n\n.. image:: https://github.com/hristost/kitty-alternative-icon/raw/main/kitty_icon.png\n   :target: https://github.com/hristost/kitty-alternative-icon\n   :width: 256\n\n.. image:: https://github.com/igrmk/whiskers/raw/main/whiskers.svg\n   :target: https://github.com/igrmk/whiskers\n   :width: 256\n\n.. image:: https://github.com/samholmes/whiskers/raw/main/whiskers.png\n   :target: https://github.com/samholmes/whiskers\n   :width: 256\n\n.. image:: https://github.com/user-attachments/assets/a37d7830-4a8c-45a8-988a-3e98a41ea541\n   :target: https://github.com/diegobit/kitty-icon\n   :width: 256\n\n.. image:: https://github.com/eccentric-j/eccentric-icons/raw/main/icons/kitty-terminal/2d/kitty-preview.png\n   :target: https://github.com/eccentric-j/eccentric-icons\n   :width: 256\n\n.. image:: https://github.com/eccentric-j/eccentric-icons/raw/main/icons/kitty-terminal/3d/kitty-preview.png\n   :target: https://github.com/eccentric-j/eccentric-icons\n   :width: 256\n\n.. image:: https://github.com/sodapopcan/kitty-icon/raw/main/kitty.app.png\n   :target: https://github.com/sodapopcan/kitty-icon\n   :width: 256\n\n.. image:: https://github.com/sfsam/some_icons/raw/main/kitty.app.iconset/icon_128x128@2x.png\n   :target: https://github.com/sfsam/some_icons\n   :width: 256\n\n.. image:: https://github.com/igrmk/twiskers/raw/main/icon/twiskers.svg\n   :target: https://github.com/igrmk/twiskers\n   :width: 256\n\n.. image:: https://github.com/mtklr/kitty-nyan-icon/raw/main/kitty-nyan.svg\n   :target: https://github.com/mtklr/kitty-nyan-icon\n   :width: 256\n\nYou can put :file:`kitty.app.icns` (macOS only) or :file:`kitty.app.png` in the\n:ref:`kitty configuration directory <confloc>`, and this icon will be applied\nautomatically at startup. On X11 and Wayland, this will set the icon for kitty windows.\nNote that not all Wayland compositors support the `protocol needed <https://wayland.app/protocols/xdg-toplevel-icon-v1>`__\nfor changing window icons.\n\nUnfortunately, on macOS, Apple's Dock does not change its cached icon so the\ncustom icon will revert when kitty is quit. Run the following to force the Dock\nto update its cached icons:\n\n.. code-block:: sh\n\n    rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock\n\nIf you prefer not to keep a custom icon in the kitty config folder, on macOS, you can\nalso set it with the following command:\n\n.. code-block:: sh\n\n    # Set kitty.icns as the icon for currently running kitty\n    kitty +runpy 'from kitty.fast_data_types import cocoa_set_app_icon; import sys; cocoa_set_app_icon(*sys.argv[1:]); print(\"OK\")' kitty.icns\n\n    # Set the icon for app bundle specified by the path\n    kitty +runpy 'from kitty.fast_data_types import cocoa_set_app_icon; import sys; cocoa_set_app_icon(*sys.argv[1:]); print(\"OK\")' /path/to/icon.png /Applications/kitty.app\n\nYou can also change the icon manually by following the steps:\n\n.. tab:: macOS\n\n    #. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+I`\n    #. Drag :file:`kitty.icns` onto the application icon in the kitty info pane\n    #. Delete the icon cache and restart Dock::\n\n        rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock\n\n.. tab:: Linux\n\n   #. Copy :file:`kitty.desktop` from the installation location (usually\n      :file:`/usr/share/applications` to :file:`~/.local/share/applications`\n   #. Edit the copied desktop file changing the ``Icon`` line to have\n      the absolute path to your desired icon.\n\n\nHow do I map key presses in kitty to different keys in the terminal program?\n--------------------------------------------------------------------------------------\n\nThis is accomplished by using ``map`` with :ac:`send_key` in :file:`kitty.conf`.\nFor example::\n\n    map alt+s send_key ctrl+s\n    map ctrl+alt+2 combine : send_key ctrl+c : send_key h : send_key a\n\nThis causes the program running in kitty to receive the :kbd:`ctrl+s` key when\nyou press the :kbd:`alt+s` key and several keystrokes when you press\n:kbd:`ctrl+alt+2`. To see this in action, run::\n\n    kitten show-key -m kitty\n\nWhich will print out what key events it receives. To send arbitrary text rather\nthan a key press, see :sc:`send_text <send_text>` instead.\n\n\nHow do I open a new window or tab with the same working directory as the current window?\n--------------------------------------------------------------------------------------------\n\nIn :file:`kitty.conf` add the following::\n\n    map f1 launch --cwd=current\n    map f2 launch --cwd=current --type=tab\n\nPressing :kbd:`F1` will open a new kitty window with the same working directory\nas the current window. The :doc:`launch command <launch>` is very powerful,\nexplore :doc:`its documentation <launch>`.\n\n\nThings behave differently when running kitty from system launcher vs. from another terminal?\n-----------------------------------------------------------------------------------------------\n\nThis will be because of environment variables. When you run kitty from the\nsystem launcher, it gets a default set of system environment variables. When\nyou run kitty from another terminal, you are actually running it from a shell,\nand the shell's rc files will have setup a whole different set of environment\nvariables which kitty will now inherit.\n\nYou need to make sure that the environment variables you define in your shell's\nrc files are either also defined system wide or via the :opt:`env` directive in\n:file:`kitty.conf`. Common environment variables that cause issues are those\nrelated to localization, such as :envvar:`LANG`, ``LC_*`` and loading of\nconfiguration files such as ``XDG_*``, :envvar:`KITTY_CONFIG_DIRECTORY` and,\nmost importantly, ``PATH`` to locate binaries.\n\nThe simplest way to fix this is to have kitty load the environment variables\nfrom your shell configuration at startup using the :opt:`env` directive,\nadding the following to :file:`kitty.conf`::\n\n    env read_from_shell=PATH LANG LC_* XDG_* EDITOR VISUAL\n\nThis works for POSIX compliant shells and the fish shell. Note that it\ndoes add significantly to kitty startup time, so use only if really necessary.\nThis feature was added in version ``0.43.2``.\n\nTo see the environment variables that kitty sees, you can add the following\nmapping to :file:`kitty.conf`::\n\n    map f1 show_kitty_env_vars\n\nthen pressing :kbd:`F1` will show you the environment variables kitty sees.\n\nThis problem is most common on macOS, as Apple makes it exceedingly difficult to\nsetup environment variables system-wide, so people end up putting them in all\nsorts of places where they may or may not work.\n\n\nI am using tmux/zellij and have a problem\n----------------------------------------------\n\nFirst, terminal multiplexers are :iss:`a bad idea <391#issuecomment-638320745>`,\ndo not use them, if at all possible. kitty contains features that do all of what\ntmux does, but better, with the exception of remote persistence (:iss:`391`).\nIf you still want to use tmux, read on.\n\nUsing ancient versions of tmux such as 1.8 will cause gibberish on screen when\npressing keys (:iss:`3541`).\n\nIf you are using tmux with multiple terminals or you start it under one terminal\nand then switch to another and these terminals have different :envvar:`TERM`\nvariables, tmux will break. You will need to restart it as tmux does not support\nmultiple terminfo definitions.\n\nDisplaying images while inside programs such as nvim or ranger may not work\ndepending on whether those programs have adopted support for the :ref:`unicode\nplaceholders <graphics_unicode_placeholders>` workaround that kitty created\nfor tmux refusing to support images.\n\nIf you use any of the advanced features that kitty has innovated, such as\n:doc:`styled underlines </underlines>`, :doc:`desktop notifications\n</desktop-notifications>`, :doc:`variable sized text </text-sizing-protocol>`,\n:doc:`extended keyboard support </keyboard-protocol>`,\n:doc:`file transfer </kittens/transfer>`, :doc:`the ssh kitten </kittens/ssh>`,\n:doc:`shell integration </shell-integration>` etc. they may or may not work,\ndepending on the whims of tmux's maintainer, your version of tmux, etc.\n\n\nI opened and closed a lot of windows/tabs and top shows kitty's memory usage is very high?\n-------------------------------------------------------------------------------------------\n\n:program:`top` is not a good way to measure process memory usage. That is\nbecause on modern systems, when allocating memory to a process, the C library\nfunctions will typically allocate memory in large blocks, and give the process\nchunks of these blocks. When the process frees a chunk, the C library will not\nnecessarily release the underlying block back to the OS. So even though the\napplication has released the memory, :program:`top` will still claim the process\nis using it.\n\nTo check for memory leaks, instead use a tool like `Valgrind\n<https://valgrind.org/>`__. Run::\n\n    PYTHONMALLOC=malloc valgrind --tool=massif kitty\n\nNow open lots of tabs/windows, generate lots of output using tools like find/yes\netc. Then close all but one window. Do some random work for a few seconds in\nthat window, maybe run yes or find again. Then quit kitty and run::\n\n    massif-visualizer massif.out.*\n\nYou will see the allocations graph goes up when you opened the windows, then\ngoes back down when you closed them, indicating there were no memory leaks.\n\nFor those interested, you can get a similar profile out of :program:`valgrind`\nas you get with :program:`top` by adding ``--pages-as-heap=yes`` then you will\nsee that memory allocated in malloc is not freed in free. This can be further\nrefined if you use ``glibc`` as your C library by setting the environment\nvariable ``MALLOC_MMAP_THRESHOLD_=64``. This will cause free to actually free\nmemory allocated in sizes of more than 64 bytes. With this set, memory usage\nwill climb high, then fall when closing windows, but not fall all the way back.\nThe remaining used memory can be investigated using valgrind again, and it will\ncome from arenas in the GPU drivers and the per thread arenas glibc's malloc\nmaintains. These too allocate memory in large blocks and don't release it back\nto the OS immediately.\n\n\nWhy does kitty sometimes start slowly on my Linux system?\n-------------------------------------------------------------------------------------------\n\n|kitty| takes no longer (within 100ms) to start than other similar GPU terminal\nemulators, (and may be faster than some). If |kitty| occasionally takes a long\ntime to start, it could be a power management issue with the graphics card. On\na multi-GPU system (which many modern laptops are, having a power efficient GPU\nthat's built into the processor and a power hungry dedicated one that's usually\noff), even if the answer of the GPU will only be \"don't use me\".\n\nFor example, if you have a system with an AMD CPU and an NVIDIA GPU, and you\nknow that you want to use the lower powered card to save battery life and\nbecause kitty does not require a powerful GPU to function, you can choose not\nto wake up the dedicated card, which has been reported on at least one system\n(:iss:`4292`) to take ≈2 seconds, by running |kitty| as::\n\n    MESA_LOADER_DRIVER_OVERRIDE=radeonsi __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json kitty\n\nThe correct command will depend on your situation and hardware.\n``__EGL_VENDOR_LIBRARY_FILENAMES`` instructs the GL dispatch library to use\n:file:`libEGL_mesa.so` and ignore :file:`libEGL_nvidia.so` also available on the\nsystem, which will wake the NVIDIA card during device enumeration.\n``MESA_LOADER_DRIVER_OVERRIDE`` also assures that Mesa won't offer any NVIDIA\ncard during enumeration, and will instead just use :file:`radeonsi_dri.so`.\n"
  },
  {
    "path": "docs/file-transfer-protocol.rst",
    "content": "File transfer over the TTY\n===============================\n\nThere are sometimes situations where the TTY is the only convenient pipe\nbetween two connected systems, for example, nested SSH sessions, a serial\nline, etc. In such scenarios, it is useful to be able to transfer files\nover the TTY.\n\nThis protocol provides the ability to transfer regular files, directories and\nlinks (both symbolic and hard) preserving most of their metadata. It can\noptionally use compression and transmit only binary diffs to speed up\ntransfers. However, since all data is base64 encoded for transmission over the\nTTY, this protocol will never be competitive with more direct file transfer\nmechanisms.\n\nOverall design\n----------------\n\nThe basic design of this protocol is around transfer \"sessions\". Since\nuntrusted software should not be able to read/write to another machines\nfilesystem, a session must be approved by the user in the terminal emulator\nbefore any actual data is transmitted, unless a :ref:`pre-shared password is\nprovided <bypass_auth>`.\n\nThere can be either send or receive sessions. In send sessions files are sent\nfrom remote client to the terminal emulator and vice versa for receive sessions.\nEvery session basically consists of sending metadata for the files first and\nthen sending the actual data. The session is a series of commands, every command\ncarrying the session id (which should be a random unique-ish identifier, to\navoid conflicts). The session is bi-directional with commands going both to and\nfrom the terminal emulator. Every command in a session also carries an\n``action`` field that specifies what the command does. The remaining fields in\nthe command are dependent on the nature of the command.\n\nLet's look at some simple examples of sessions to get a feel for the protocol.\n\n\nSending files to the computer running the terminal emulator\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe client starts by sending a start send command::\n\n    → action=send id=someid\n\nIt then waits for a status message from the terminal either\nallowing the transfer or refusing it. Until this message is received\nthe client is not allowed to send any more commands for the session.\nThe terminal emulator should drop a session if it receives any commands\nbefore sending an ``OK`` response. If the user accepts the transfer,\nthe terminal will send::\n\n    ← action=status id=someid status=OK\n\nOr if the transfer is refused::\n\n    ← action=status id=someid status=EPERM:User refused the transfer\n\nThe client then sends one or more ``file`` commands with the metadata of the file it wants\nto transfer::\n\n    → action=file id=someid file_id=f1 name=/path/to/destination\n    → action=file id=someid file_id=f2 name=/path/to/destination2 ftype=directory\n\nThe terminal responds with either ``OK`` for directories or ``STARTED`` for\nfiles::\n\n    ← action=status id=someid file_id=f1 status=STARTED\n    ← action=status id=someid file_id=f2 status=OK\n\nIf there was an error with the file, for example, if the terminal does not have\npermission to write to the specified location, it will instead respond with an\nerror, such as::\n\n    ← action=status id=someid file_id=f1 status=EPERM:No permission\n\nThe client sends data for files using ``data`` commands. It does not need to\nwait for the ``STARTED`` from the terminal for this, the terminal must discard data\nfor files that are not ``STARTED``. Data for a file is sent in individual\nchunks of no larger than ``4096`` bytes. For example::\n\n\n    → action=data id=someid file_id=f1 data=chunk of bytes\n    → action=data id=someid file_id=f1 data=chunk of bytes\n    ...\n    → action=end_data id=someid file_id=f1 data=chunk of bytes\n\nThe sequence of data transmission for a file is ended with an ``end_data``\ncommand. After each data packet is received the terminal replies with\nan acknowledgement of the form::\n\n    ← action=status id=someid file_id=f1 status=PROGRESS size=bytes written\n\nAfter ``end_data`` the terminal replies with::\n\n    ← action=status id=someid file_id=f1 status=OK size=bytes written\n\nIf an error occurs while writing the data, the terminal replies with an error\ncode and ignores further commands about that file, for example::\n\n    ← action=status id=someid file_id=f1 status=EIO:Failed to write to file\n\nOnce the client has finished sending as many files as it wants to, it ends\nthe session with::\n\n    → action=finish id=someid\n\nAt this point the terminal commits the session, applying file metadata,\ncreating links, etc. If any errors occur it responds with an error message,\nsuch as::\n\n    ← action=status id=someid status=Some error occurred\n\n\nReceiving files from the computer running terminal emulator\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe client starts by sending a start receive command::\n\n    → action=receive id=someid size=num_of_paths\n\nIt then sends a list of ``num_of_paths`` paths it is interested in\nreceiving::\n\n    → action=file id=someid file_id=f1 name=/some/path\n    → action=file id=someid file_id=f2 name=/some/path2\n    ...\n\nThe client must then wait for responses from the terminal emulator. It\nis an error to send anymore commands to the terminal until an ``OK``\nresponse is received from the terminal. The terminal waits for the user to accept\nthe request. If accepted, it sends::\n\n    ← action=status id=someid status=OK\n\nIf permission is denied it sends::\n\n    ← action=status id=someid status=EPERM:User refused the transfer\n\nThe terminal then sends the metadata for all requested files. If any of them\nare directories, it traverses the directories recursively, listing all files.\nNote that symlinks must not be followed, but sent as symlinks::\n\n    ← action=file id=someid file_id=f1 mtime=XXX permissions=XXX name=/absolute/path status=file_id1 size=size_in_bytes file_type=type parent=file_id of parent\n    ← action=file id=someid file_id=f1 mtime=XXX permissions=XXX name=/absolute/path2 status=file_id2 size=size_in_bytes file_type=type parent=file_id of parent\n    ...\n\nHere the ``file_id`` field is set to the ``file_id`` value sent from the client\nand the ``status`` field is set to the actual file id for each file. This is\nbecause a file query sent from the client can result in multiple actual files if\nit is a directory. The ``parent`` field is the actual ``file_id`` of the directory\ncontaining this file and is set for entries that are generated from client\nrequests that match directories. This allows the client to build an unambiguous picture\nof the file tree.\n\nOnce all the files are listed, the terminal sends an ``OK`` response that also\nspecifies the absolute path to the home directory for the user account running\nthe terminal::\n\n    ← action=status id=someid status=OK name=/path/to/home\n\nIf an error occurs while listing any of the files asked for by the client,\nthe terminal will send an error response like::\n\n    ← action=status id=someid file_id=f1 status=ENOENT: Does not exist\n\nHere, ``file_id`` is the same as was sent by the client in its initial query.\n\nNow, the client can send requests for file data using the paths sent by the\nterminal emulator::\n\n    → action=file id=someid file_id=f1 name=/some/path\n    ...\n\nThe client must not send requests for directories and absolute symlinks.\nThe terminal emulator replies with the data for the files, as a sequence of\n``data`` commands each with a chunk of data no larger than ``4096`` bytes,\nfor each file (the terminal emulator must send the data for\none file at a time)::\n\n\n    ← action=data id=someid file_id=f1 data=chunk of bytes\n    ...\n    ← action=end_data id=someid file_id=f1 data=chunk of bytes\n\nIf any errors occur reading file data, the terminal emulator sends an error\nmessage for the file, for example::\n\n    ← action=status id=someid file_id=f1 status=EIO:Could not read\n\nOnce the client is done reading data for all the files it expects, it\nterminates the session with::\n\n    → action=finished id=someid\n\nCanceling a session\n----------------------\n\nA client can decide to cancel a session at any time (for example if the user\npresses :kbd:`ctrl+c`). To cancel a session it sends a ``cancel`` action to the\nterminal emulator::\n\n    → action=cancel id=someid\n\nThe terminal emulator drops the session and sends a cancel acknowledgement::\n\n    ← action=status id=someid status=CANCELED\n\nThe client **must** wait for the canceled response from the emulator discarding\nany other responses till the cancel is received. If it does not wait, after\nit quits the responses might end up being printed to screen.\n\nQuieting responses from the terminal\n-------------------------------------\n\nThe above protocol includes lots of messages from the terminal acknowledging\nreceipt of data, granting permission etc., acknowledging cancel requests, etc.\nFor extremely simple clients like shell scripts, it might be useful to suppress\nthese responses, which can be done by adding the ``quiet`` key to the start\nsession command::\n\n    → action=send id=someid quiet=1\n\nThe key can take the values ``1`` - meaning suppress acknowledgement responses\nor ``2`` - meaning suppress all responses including errors. Only actual data\nresponses are sent. Note that in particular this means acknowledgement of\npermission for the transfer to go ahead is suppressed, so this is typically\nuseful only with :ref:`bypass_auth`.\n\n.. _file_metadata:\n\nFile metadata\n-----------------\n\nFile metadata includes file paths, permissions and modification times. They are\nsomewhat tricky as different operating systems support different kinds of\nmetadata. This specification defines a common minimum set which should work\nacross most operating systems.\n\nFile paths\n    File paths must be valid UTF-8 encoded POSIX paths (i.e. using the forward slash\n    ``/`` as a separator). Linux systems allow non UTF-8 file paths, these\n    are not supported. A leading ``~/`` means a path is relative to the\n    ``HOME`` directory. All path must be either absolute (i.e. with a leading\n    ``/``) or relative to the HOME directory. Individual components of the\n    path must be no longer than 255 UTF-8 bytes. Total path length must be no\n    more than 4096 bytes. Paths from Windows systems must use the forward slash\n    as the separator, the first path component must be the drive letter with a\n    colon. For example: :file:`C:\\\\some\\\\file.txt` is represented as\n    :file:`/C:/some/file.txt`. For maximum portability, the following\n    characters *should* be omitted from paths (however implementations are free\n    to try to support them returning errors for non-representable paths)::\n\n        \\ * : < > ? | /\n\nFile modification times\n    Must be represented as the number of nanoseconds since the UNIX epoch. An\n    individual file system may not store file metadata with this level of\n    accuracy in which case it should use the closest possible approximation.\n\nFile permissions\n    Represented as a number with the usual UNIX read, write and execute bits.\n    In addition, the sticky, set-group-id and set-user-id bits may be present.\n    Implementations should make a best effort to preserve as many bits as\n    possible. On Windows, there is only a read-only bit. When reading file\n    metadata all the ``WRITE`` bits should be set if the read only bit is clear\n    and cleared if it is set. When writing files, the read-only bit should be\n    set if the bit indicating write permission for the user is clear. The other\n    UNIX bits must be ignored when writing. When reading, all the ``READ`` bits\n    should always be set and all the ``EXECUTE`` bits should be set if the file is\n    directly executable by the Windows Operating system. There is no attempt to\n    map Window's ACLs to permission bits.\n\n\nSymbolic and hard links\n---------------------------\n\nSymbolic and hard links can be preserved by this protocol.\n\n.. note::\n   In the following when target paths of symlinks are sent as actual paths, they must be\n   encoded in the same way as discussed in :ref:`file_metadata`. It is up to\n   the receiving side to translate them into appropriate paths for the local\n   operating system. This may not always be possible, in which case either the\n   symlink should not be created or a broken symlink should be created.\n\n\nSending links to the terminal emulator\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen sending files to the terminal emulator, the file command has the form::\n\n    → action=file id=someid file_id=f1 name=/path/to/link file_type=link\n    → action=file id=someid file_id=f2 name=/path/to/symlink file_type=symlink\n\nThen, when the client is sending data for the files, for hardlinks, the data\nwill be the ``file_id`` of the target file (assuming the target file is also\nbeing transmitted, otherwise the hard link should be transmitted as a plain\nfile)::\n\n    → action=end_data id=someid file_id=f1 data=target_file_id_encoded_as_utf8\n\nFor symbolic links, the data is a little more complex. If the symbolic link is\nto a destination being transmitted, the data has the form::\n\n    → action=end_data id=someid file_id=f1 data=fid:target_file_id_encoded_as_utf8\n    → action=end_data id=someid file_id=f1 data=fid_abs:target_file_id_encoded_as_utf8\n\nThe ``fid_abs`` form is used if the symlink uses an absolute path, ``fid`` if\nit uses a relative path. If the symlink is to a destination that is not being\ntransmitted, then the prefix ``path:`` and the actual path in the symlink is\ntransmitted.\n\nReceiving links from the terminal emulator\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen receiving files from the terminal emulator, link data is transmitted in\ntwo parts. First when the emulator sends the initial file listing to the\nclient, the ``file_type`` is set to the link type and the ``data`` field is set\nto file_id of the target file if the target file is included in the listing.\nFor example::\n\n    ← action=file id=someid file_id=f1 status=file_id1 ...\n    ← action=file id=someid file_id=f1 status=file_id2 file_type=symlink data=file_id1 ...\n\nHere the rest of the metadata has been left out for clarity. Notice that the\nsecond file is symlink whose ``data`` field is set to the file id of the first\nfile (the value of the ``status`` field of the first file). The same technique\nis used for hard links.\n\nThe client should not request data for hard links, instead creating them\ndirectly after transmission is complete. For symbolic links the terminal\nmust send the actual symbolic link target as a UTF-8 encoded path in the\ndata field. The client can use this path either as-is (when the target is not\na transmitted file) or to decide whether to create the symlink with a relative\nor absolute path when the target is a transmitted file.\n\n\nTransmitting binary deltas\n-----------------------------\n\nRepeated transfer of large files that have only changed a little between\nthe receiving and sending side can be sped up significantly by transmitting\nbinary deltas of only the changed portions. This protocol has built-in support\nfor doing that. This support uses the `rsync algorithm\n<https://rsync.samba.org/tech_report/tech_report.html>`__. In this algorithm, first the\nreceiving side sends a file signature that contains hashes of blocks\nin the file. Then the sending side sends only those blocks that have changed.\nThe receiving side applies these deltas to the file to update it till it matches\nthe file on the sending side.\n\nThe modification to the basic protocol consists of setting the\n``transmission_type`` key to ``rsync`` when requesting a file. This triggers\ntransmission of signatures and deltas instead of file data. The details are\ndifferent for sending and receiving.\n\nSending to the terminal emulator\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen sending the metadata of the file it wants to transfer, the client adds the\n``transmission_type`` key::\n\n    → action=file id=someid file_id=f1 name=/path/to/destination transmission_type=rsync\n\nThe ``STARTED`` response from the terminal will have ``transmission_type`` set\nto ``rsync`` if the file exists and the terminal is able to send signature data::\n\n    ← action=status id=someid file_id=f1 status=STARTED transmission_type=rsync\n\nThe terminal then transmits the signature using ``data`` commands::\n\n    ← action=data id=someid file_id=f1 data=...\n    ...\n    ← action=end_data id=someid file_id=f1 data=...\n\nOnce the client receives and processes the full signature, it transmits the\nfile delta to the terminal as ``data`` commands::\n\n    → action=data id=someid file_id=f1 data=...\n    → action=data id=someid file_id=f1 data=...\n    ...\n    → action=end_data id=someid file_id=f1 data=...\n\nThe terminal then uses this delta to update the file.\n\nReceiving from the terminal emulator\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen the client requests file data from the terminal emulator, it can\nadd the ``transmission_type=rsync`` key to indicate it will be sending\na signature for that file::\n\n    → action=file id=someid file_id=f1 name=/some/path transmission_type=rsync\n\nThe client then sends the signature using ``data`` commands::\n\n    → action=data id=someid file_id=f1 data=...\n    ...\n    → action=end_data id=someid file_id=f1 data=...\n\nAfter receiving the signature the terminal replies with the delta as a series\nof ``data`` commands::\n\n    ← action=data id=someid file_id=f1 data=...\n    ...\n    ← action=end_data id=someid file_id=f1 data=...\n\nThe client then uses this delta to update the file.\n\nThe format of signatures and deltas\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn what follows, all integers must be encoded in little-endian format,\nregardless of the architecture of the machines involved. The XXH3 hash family\nrefers to `the xxHash algorithm\n<https://github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md>`__.\n\nA signature first has a 12 byte header of the form:\n\n.. code::\n\n    uint16 version\n    uint16 checksum_type\n    uint16 strong_hash_type\n    uint16 weak_hash_type\n    uint32 block_size\n\nThese fields define the parameters to the rsync algorithm. Allowed values are\ncurrently all zero except for ``block_size``, which is usually the square root\nof the file size, but implementations are free to use any algorithm they like\nto arrive at the block size.\n\n``checksum_type`` must be ``0`` which indicates using the XXH3-128 bit hash\nto verify file integrity after transmission.\n\n``strong_hash_type`` must be ``0`` which indicates using the XXH3-64 bit hash\nto identify blocks.\n\n``weak_hash_type`` must be ``0`` which indicates using the `rsync rolling\nchecksum hash <https://rsync.samba.org/tech_report/node3.html>`__ to identify\nblocks, weakly.\n\nAfter the header comes the list of block signatures. The number of blocks is\nunknown allowing for streaming, the transfer protocol takes care of indicating\nend-of-stream via an ``action=end_data`` packet. Each signature in the list is of the form:\n\n.. code::\n\n   uint64 index\n   uint32 weak_hash\n   uint64 strong_hash\n\nHere, ``index`` is the zero-based block number. ``weak_hash`` is the weak, but easy\nto calculate hash of the block and strong hash is a stronger hash of the block\nthat is very unlikely to collide.\n\nThe algorithms used for these hashes are specified by the signature header\nabove. Given the ``block_size`` from the header and ``index`` the position of a\nblock in the file is: ``index * block_size``.\n\nOnce the sending side receives the signature, it calculates a *delta* based on\nthe actual file contents and transmits that delta to the receiving side. The delta\nis of the form of a list of *operations*. An operation is a single byte\ndenoting the operation type followed by variable length data depending on the\ntype. The types of operations are:\n\n``Block (type=0)``\n    Followed by an 8 byte ``uint64`` that is the block index. It means copy the\n    specified block from the existing file to the output, unmodified.\n\n``Data (type=1)``\n    Followed by a 4 byte ``uint32`` that is the size of the payload and then the\n    payload itself. The payload must be written to the output.\n\n``Hash (type=2)``\n    Followed by a 2 byte ``uint16`` specifying the size of the hash checksum and\n    then the checksum itself. The checksum of the output file must match this\n    checksum. The algorithm used to calculate the checksum is specified in the\n    signature header.\n\n``BlockRange (type=3)``\n    Followed by an 8 byte ``uint64`` that is the starting block index and then\n    a 4 byte ``uint32`` (``N``) that is the number of additional blocks. Works just\n    like ``Block`` above, except that after copying the block an additional (``N``) more\n    blocks must be copied.\n\n\nCompression\n--------------\n\nIndividual files can be transmitted compressed if needed.\nCurrently, only :rfc:`1950` ZLIB based deflate compression is\nsupported, which is specified using the ``compression=zlib`` key when\nrequesting a file. For example when sending files to the terminal emulator,\nwhen sending the file metadata the ``compression`` key can also be\nspecified::\n\n    → action=file id=someid file_id=f1 name=/path/to/destination compression=zlib\n\nSimilarly when receiving files from the terminal emulator, the final file\ncommand that the client sends to the terminal requesting the start of the\ntransfer of data for the file can include the ``compression`` key::\n\n    → action=file id=someid file_id=f1 name=/some/path compression=zlib\n\n.. _bypass_auth:\n\nBypassing explicit user authorization\n------------------------------------------\n\nIn order to bypass the requirement of interactive user authentication,\nthis protocol has the ability to use a pre-shared secret (password).\nWhen initiating a transfer session the client sends a hash of the password and\nthe session id::\n\n    → action=send id=someid bypass=sha256:hash_value\n\nFor example, suppose that the session id is ``mysession`` and the\nshared secret is ``mypassword``. Then the value of the ``bypass``\nkey above is ``sha256:SHA256(\"mysession\" + \";\" + \"mypassword\")``, which\nis::\n\n    → action=send id=mysession bypass=sha256:192bd215915eeaa8c2b2a4c0f8f851826497d12b30036d8b5b1b4fc4411caf2c\n\nThe value of ``bypass`` is of the form ``hash_function_name : hash_value``\n(without spaces). Currently, only the SHA256 hash function is supported.\n\n.. warning::\n   Hashing does not effectively hide the value of the password. So this\n   functionality should only be used in secure/trusted contexts. While there\n   exist hash functions harder to compute than SHA256, they are unsuitable as\n   they will introduce a lot of latency to starting a session and in any case\n   there is no mathematical proof that **any** hash function is not brute-forceable.\n\nTerminal implementations are free to use their own more advanced hashing\nschemes, with prefixes other than those starting with ``sha``, which are\nreserved. For instance, kitty uses a scheme based on public key encryption\nvia :envvar:`KITTY_PUBLIC_KEY`. For details of this scheme, see the\n``check_bypass()`` function in the kitty source code.\n\nEncoding of transfer commands as escape codes\n------------------------------------------------\n\nTransfer commands are encoded as ``OSC`` escape codes of the form::\n\n    <OSC> 5113 ; key=value ; key=value ... <ST>\n\nHere ``OSC`` is the bytes ``0x1b 0x5d`` and ``ST`` is the bytes\n``0x1b 0x5c``. Keys are words containing only the characters ``[a-zA-Z0-9_]``\nand ``value`` is arbitrary data, whose encoding is dependent on the value of\n``key``. Unknown keys **must** be ignored when decoding a command.\nThe number ``5113`` is a constant and is unused by any known OSC codes. It is\nthe numeralization of the word ``file``.\n\n\n.. table:: The keys and value types for this protocol\n    :align: left\n\n    ================= ======== ============== =======================================================================\n    Key               Key name Value type     Notes\n    ================= ======== ============== =======================================================================\n    action            ac       enum           send, file, data, end_data, receive, cancel, status, finish\n    compression       zip      enum           none, zlib\n    file_type         ft       enum           regular, directory, symlink, link\n    transmission_type tt       enum           simple, rsync\n    id                id       safe_string    A unique-ish value, to avoid collisions\n    file_id           fid      safe_string    Must be unique per file in a session\n    bypass            pw       safe_string    hash of the bypass password and the session id\n    quiet             q        integer        0 - verbose, 1 - only errors, 2 - totally silent\n    mtime             mod      integer        the modification time of file in nanoseconds since the UNIX epoch\n    permissions       prm      integer        the UNIX file permissions bits\n    size              sz       integer        size in bytes\n    name              n        base64_string  The path to a file\n    status            st       base64_string  Status messages\n    parent            pr       safe_string    The file id of the parent directory\n    data              d        base64_bytes   Binary data\n    ================= ======== ============== =======================================================================\n\nThe ``Key name`` is the actual serialized name of the key sent in the escape\ncode. So for example, ``permissions=123`` is serialized as ``prm=123``. This\nis done to reduce overhead.\n\nThe value types are:\n\nenum\n    One from a permitted set of values, for example::\n\n        ac=file\n\nsafe_string\n    A string consisting only of characters from the set ``[0-9a-zA-Z_:./@-]``\n    Note that the semi-colon is missing from this set.\n\ninteger\n    A base-10 number composed of the characters ``[0-9]`` with a possible\n    leading ``-`` sign. When missing the value is zero.\n\nbase64_string\n    A base64 encoded UTF-8 string using the standard base64 encoding\n\nbase64_bytes\n    Binary data encoded using the standard base64 encoding\n\n\nAn example of serializing an escape code is shown below::\n\n    action=send id=test name=somefile size=3 data=01 02 03\n\nbecomes::\n\n    <OSC> 5113 ; ac=send ; id=test ; n=c29tZWZpbGU= ; sz=3 ; d=AQID <ST>\n\nHere ``c29tZWZpbGU`` is the base64 encoded form of somefile and ``AQID`` is the\nbase64 encoded form of the bytes ``0x01 0x02 0x03``. The spaces in the encoded\nform are present for clarity and should be ignored.\n"
  },
  {
    "path": "docs/glossary.rst",
    "content": ":orphan:\n\nGlossary\n=========\n\n.. glossary::\n\n   os_window\n     kitty has two kinds of windows. Operating System windows, referred to as :term:`OS\n     Window <os_window>`, and *kitty windows*. An OS Window consists of one or more kitty\n     :term:`tabs <tab>`. Each tab in turn consists of one or more *kitty\n     windows* organized in a :term:`layout`.\n\n   tab\n     A *tab* refers to a group of :term:`kitty windows <window>`, organized in\n     a :term:`layout`. Every :term:`OS Window <os_window>` contains one or more tabs.\n\n   layout\n     A *layout* is a system of organizing :term:`kitty windows <window>` in\n     groups inside a tab. The layout automatically maintains the size and\n     position of the windows, think of a layout as a tiling window manager for\n     the terminal. See :doc:`layouts` for details.\n\n   window\n     kitty has two kinds of windows. Operating System windows, referred to as :term:`OS\n     Window <os_window>`, and *kitty windows*. An OS Window consists of one or more kitty\n     :term:`tabs <tab>`. Each tab in turn consists of one or more *kitty\n     windows* organized in a :term:`layout`.\n\n   overlay\n      An *overlay window* is a :term:`kitty window <window>` that is placed on\n      top of an existing kitty window, entirely covering it. Overlays are used\n      throughout kitty, for example, to display the :ref:`the scrollback buffer <scrollback>`,\n      to display :doc:`hints </kittens/hints>`, for :doc:`unicode input\n      </kittens/unicode_input>` etc. Normal overlays are meant for short\n      duration popups and so are not considered the :italic:`active window`\n      when determining the current working directory or getting input text for\n      kittens, launch commands, etc. To create an overlay considered as a\n      :italic:`main window` use the :code:`overlay-main` argument to\n      :doc:`launch`.\n\n   hyperlinks\n      Terminals can have hyperlinks, just like the internet. In kitty you can\n      :doc:`control exactly what happens <open_actions>` when clicking on a\n      hyperlink, based on the type of link and its URL. See also `Hyperlinks in terminal\n      emulators <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`__.\n\n   kittens\n      Small, independent statically compiled command line programs that are designed to run\n      inside kitty windows and provide it with lots of powerful and flexible\n      features such as viewing images, connecting conveniently to remote\n      computers, transferring files, inputting unicode characters, etc.\n      They can also be written by users in Python and used to customize and\n      extend kitty functionality, see :doc:`kittens_intro` for details.\n\n   easing function\n      A function that controls how an animation progresses over time. kitty\n      support the `CSS syntax for easing functions\n      <https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function>`__.\n      Commonly used easing functions are :code:`linear` for a constant rate\n      animation and :code:`ease-in-out` for an animation that starts slow,\n      becomes fast in the middle and ends slowly. These are used to control\n      various animations in kitty, such as :opt:`cursor_blink_interval` and\n      :opt:`visual_bell_duration`.\n\n.. _env_vars:\n\nEnvironment variables\n------------------------\n\nVariables that influence kitty behavior\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. envvar:: KITTY_CONFIG_DIRECTORY\n\n   Controls where kitty looks for :file:`kitty.conf` and other configuration\n   files. Defaults to :file:`~/.config/kitty`. For full details of the config\n   directory lookup mechanism see, :option:`kitty --config`.\n\n.. envvar:: KITTY_CACHE_DIRECTORY\n\n   Controls where kitty stores cache files. Defaults to :file:`~/.cache/kitty`\n   or :file:`~/Library/Caches/kitty` on macOS.\n\n.. envvar:: KITTY_RUNTIME_DIRECTORY\n\n   Controls where kitty stores runtime files like sockets. Defaults to\n   the :code:`XDG_RUNTIME_DIR` environment variable if that is defined\n   otherwise the run directory inside the kitty cache directory is used.\n\n.. envvar:: VISUAL\n\n   The terminal based text editor (such as :program:`vi` or :program:`nano`)\n   kitty uses, when, for instance, opening :file:`kitty.conf` in response to\n   :sc:`edit_config_file`.\n\n.. envvar:: EDITOR\n\n   Same as :envvar:`VISUAL`. Used if :envvar:`VISUAL` is not set.\n\n.. envvar:: SHELL\n\n   Specifies the default shell kitty will run when :opt:`shell` is set to\n   :code:`.`.\n\n.. envvar:: GLFW_IM_MODULE\n\n   Set this to ``ibus`` to enable support for IME under X11.\n\n.. envvar:: KITTY_WAYLAND_DETECT_MODIFIERS\n\n   When set to a non-empty value, kitty attempts to autodiscover XKB modifiers\n   under Wayland. This is useful if using non-standard modifiers like hyper. It\n   is possible for the autodiscovery to fail; the default Wayland XKB mappings\n   are used in this case. See :pull:`3943` for details.\n\n.. envvar:: SSH_ASKPASS\n\n   Specify the program for SSH to ask for passwords. When this is set, :doc:`ssh\n   kitten </kittens/ssh>` will use this environment variable by default. See\n   :opt:`askpass <kitten-ssh.askpass>` for details.\n\n.. envvar:: KITTY_CLONE_SOURCE_CODE\n\n   Set this to some shell code that will be executed in the cloned window with\n   :code:`eval` when :ref:`clone-in-kitty <clone_shell>` is used.\n\n.. envvar:: KITTY_CLONE_SOURCE_PATH\n\n   Set this to the path of a file that will be sourced in the cloned window when\n   :ref:`clone-in-kitty <clone_shell>` is used.\n\n.. envvar:: KITTY_DEVELOP_FROM\n\n   Set this to the directory path of the kitty source code and its Python code\n   will be loaded from there. Only works with official binary builds.\n\n.. envvar:: KITTY_RC_PASSWORD\n\n   Set this to a pass phrase to use the ``kitten @`` remote control command with\n   :opt:`remote_control_password`.\n\n\nVariables that kitty sets when running child programs\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. envvar:: LANG\n\n   This is only set on macOS. If the country and language from the macOS user\n   settings form an invalid locale, it will be set to :code:`en_US.UTF-8`.\n\n.. envvar:: PATH\n\n   kitty prepends itself to the PATH of its own environment to ensure the\n   functions calling :program:`kitty` will work properly.\n\n.. envvar:: KITTY_WINDOW_ID\n\n   An integer that is the id for the kitty :term:`window` the program is running in.\n   Can be used with the :doc:`kitty remote control facility <remote-control>`.\n\n.. envvar:: KITTY_PID\n\n   An integer that is the process id for the kitty process in which the program\n   is running. Allows programs to tell kitty to reload its config by sending it\n   the SIGUSR1 signal.\n\n.. envvar:: KITTY_PUBLIC_KEY\n\n   A public key that programs can use to communicate securely with kitty using\n   the remote control protocol. The format is: :code:`protocol:key data`.\n\n.. envvar:: WINDOWID\n\n   The id for the :term:`OS Window <os_window>` the program is running in. Only available\n   on platforms that have ids for their windows, such as X11 and macOS.\n\n.. envvar:: TERM\n\n   The name of the terminal, defaults to ``xterm-kitty``. See :opt:`term`.\n\n.. envvar:: TERMINFO\n\n   Path to a directory containing the kitty terminfo database. Or the terminfo\n   database itself encoded in base64. See :opt:`terminfo_type`.\n\n.. envvar:: KITTY_INSTALLATION_DIR\n\n   Path to the kitty installation directory.\n\n.. envvar:: COLORTERM\n\n   Set to the value ``truecolor`` to indicate that kitty supports 16 million\n   colors.\n\n.. envvar:: KITTY_LISTEN_ON\n\n   Set when the :doc:`remote control <remote-control>` facility is enabled and\n   the a socket is used for control via :option:`kitty --listen-on` or :opt:`listen_on`.\n   Contains the path to the socket. Avoid the need to use :option:`kitten @ --to` when\n   issuing remote control commands. Can also be a file descriptor of the form\n   fd:num instead of a socket address, in which case, remote control\n   communication should proceed over the specified file descriptor.\n\n.. envvar:: KITTY_PIPE_DATA\n\n   Set to data describing the layout of the screen when running child\n   programs using :option:`launch --stdin-source` with the contents of the\n   screen/scrollback piped to them.\n\n.. envvar:: KITTY_CHILD_CMDLINE\n\n   Set to the command line of the child process running in the kitty\n   window when calling the notification callback program on terminal bell, see\n   :opt:`command_on_bell`.\n\n.. envvar:: KITTY_COMMON_OPTS\n\n   Set with the values of some common kitty options when running\n   kittens, so kittens can use them without needing to load :file:`kitty.conf`.\n\n.. envvar:: KITTY_SHELL_INTEGRATION\n\n   Set when enabling :ref:`shell_integration`. It is automatically removed by\n   the shell integration scripts.\n\n.. envvar:: KITTY_SI_RUN_COMMAND_AT_STARTUP\n\n   Set this to an expression that the kitty shell integration scripts will\n   ``eval`` after the shell is started. Note that this environment variable\n   is ignored when present in the environment in which kitty itself is launched\n   in. It is most useful with the ``--env`` flag for the :doc:`launch <launch>`\n   action.\n\n.. envvar:: ZDOTDIR\n\n   Set when enabling :ref:`shell_integration` with :program:`zsh`, allowing\n   :program:`zsh` to automatically load the integration script.\n\n.. envvar:: XDG_DATA_DIRS\n\n   Set when enabling :ref:`shell_integration` with :program:`fish`, allowing\n   :program:`fish` to automatically load the integration script.\n\n.. envvar:: ENV\n\n   Set when enabling :ref:`shell_integration` with :program:`bash`, allowing\n   :program:`bash` to automatically load the integration script.\n\n.. envvar:: KITTY_OS\n\n   Set when using the include directive in kitty.conf. Can take values:\n   ``linux``, ``macos``, ``bsd``.\n\n.. envvar:: KITTY_HOLD\n\n   Set to ``1`` when kitty is running a shell because of the ``--hold`` flag. Can\n   be used to specialize shell behavior in the shell rc files as desired.\n\n.. envvar:: KITTY_SIMD\n\n   Set it to ``128`` to use 128 bit vector registers, ``256`` to use 256 bit\n   vector registers or any other value to prevent kitty from using SIMD CPU\n   vector instructions. Warning, this overrides CPU capability detection so\n   will cause kitty to crash with SIGILL if your CPU does not support the\n   necessary SIMD extensions.\n"
  },
  {
    "path": "docs/graphics-protocol.rst",
    "content": "Terminal graphics protocol\n=================================\n\nThe goal of this specification is to create a flexible and performant protocol\nthat allows the program running in the terminal, hereafter called the *client*,\nto render arbitrary pixel (raster) graphics to the screen of the terminal\nemulator. The major design goals are:\n\n* Should not require terminal emulators to understand image formats.\n* Should allow specifying graphics to be drawn at individual pixel positions.\n* The graphics should integrate with the text, in particular it should be possible to draw graphics\n  below as well as above the text, with alpha blending. The graphics should also scroll with the text, automatically.\n* Should use optimizations when the client is running on the same computer as the terminal emulator.\n\nFor some discussion regarding the design choices, see :iss:`33`.\n\nTo see a quick demo, inside a |kitty| terminal run::\n\n    kitten icat path/to/some/image.png\n\nYou can also see a screenshot with more sophisticated features such as\nalpha-blending and text over graphics.\n\n.. image:: https://user-images.githubusercontent.com/1308621/31647475-1188ab66-b326-11e7-8d26-24b937f1c3e8.png\n    :alt: Demo of graphics rendering in kitty\n    :align: center\n\nSome applications that use the kitty graphics protocol:\n\n* `awrit <https://github.com/chase/awrit>`_ - Chromium-based web browser rendered in Kitty with mouse and keyboard support\n* `blackcat <https://github.com/j-c-m/blackcat>`_ - a modern compatible cat with image support\n* `broot <https://dystroy.org/broot/>`_ - a terminal file explorer and manager, with preview of images, SVG, PDF, etc.\n* `chafa <https://github.com/hpjansson/chafa>`_  - a terminal image viewer\n* :doc:`kitty-diff <kittens/diff>` - a side-by-side terminal diff program with support for images\n* `fzf <https://github.com/junegunn/fzf/commit/d8188fce7b7bea982e7f9050c35e488e49fb8fd0>`_ - A command line fuzzy finder\n* `mpv <https://github.com/mpv-player/mpv/commit/874e28f4a41a916bb567a882063dd2589e9234e1>`_ - A video player that can play videos in the terminal\n* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system information tool\n* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol\n* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with image previews\n* `termpdf.py <https://github.com/dsanson/termpdf.py>`_ - a terminal PDF/DJVU/CBR viewer\n* `timg <https://github.com/hzeller/timg>`_ - a terminal image and video viewer\n* `tpix <https://github.com/jesvedberg/tpix>`_ - a statically compiled binary that can be used to display images and easily installed on remote servers without root access\n* `twitch-tui <https://github.com/Xithrius/twitch-tui>`_ - Twitch chat in the terminal\n* `vat <https://github.com/jzbrooks/vat>`_ - a terminal image viewer for vector graphics, including Android Vector Drawables\n* `viu <https://github.com/atanunq/viu>`_ - a terminal image viewer\n* `Yazi <https://github.com/sxyazi/yazi>`_ - Blazing fast terminal file manager written in Rust, based on async I/O\n\nLibraries:\n\n* `ctx.graphics <https://ctx.graphics/>`_ - Library for drawing graphics\n* `notcurses <https://github.com/dankamongmen/notcurses>`_ - C library for terminal graphics with bindings for C++, Rust and Python\n* `rasterm <https://github.com/BourgeoisBear/rasterm>`_  - Go library to display images in the terminal\n* `hologram.nvim <https://github.com/edluffy/hologram.nvim>`_  - view images inside nvim\n* `image.nvim <https://github.com/3rd/image.nvim>`_ - Bringing images to neovim\n* `image_preview.nvim <https://github.com/adelarsq/image_preview.nvim/>`_ - Image preview for neovim\n* `kui.nvim <https://github.com/romgrk/kui.nvim>`_  - Build sophisticated UIs inside neovim using the kitty graphics protocol\n* `kitty-graphics.el <https://github.com/cashmeredev/kitty-graphics.el>`_ - Images in emacs\n* `term-image <https://github.com/AnonymouX47/term-image>`_  - A Python library, CLI and TUI to display and browse images in the terminal\n* `glkitty <https://github.com/michaeljclark/glkitty>`_ - C library to draw OpenGL shaders in the terminal with a glgears demo\n\nOther terminals that have implemented the graphics protocol:\n\n* `Ghostty <https://ghostty.org>`_\n* `Konsole <https://invent.kde.org/utilities/konsole/-/merge_requests/594>`_\n* `st (with a patch) <https://st.suckless.org/patches/kitty-graphics-protocol>`_\n* `Warp <https://docs.warp.dev/getting-started/changelog#id-2025.03.26-v0.2025.03.26.08.10>`_\n* `wayst <https://github.com/91861/wayst>`_\n* `WezTerm <https://github.com/wez/wezterm/issues/986>`_\n* `iTerm2 <https://github.com/gnachman/iTerm2/commit/4fe5b2173193b6c3e45234b6b2ab7a144a5cfa01>`_\n* `xterm.js <https://github.com/xtermjs/xterm.js/discussions/5683>`_\n\n\nGetting the window size\n-------------------------\n\nIn order to know what size of images to display and how to position them, the\nclient must be able to get the window size in pixels and the number of cells\nper row and column. The cell width is then simply the window size divided by the\nnumber of rows. This can be done by using the ``TIOCGWINSZ`` ioctl. Some\ncode to demonstrate its use\n\n.. tab:: C\n\n    .. code-block:: c\n\n        #include <stdio.h>\n        #include <sys/ioctl.h>\n\n        int main(int argc, char **argv) {\n            struct winsize sz;\n            ioctl(0, TIOCGWINSZ, &sz);\n            printf(\n                \"number of rows: %i, number of columns: %i, screen width: %i, screen height: %i\\n\",\n                sz.ws_row, sz.ws_col, sz.ws_xpixel, sz.ws_ypixel);\n            return 0;\n        }\n\n\n.. tab:: Python\n\n    .. code-block:: python\n\n        import array, fcntl, sys, termios\n        buf = array.array('H', [0, 0, 0, 0])\n        fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)\n        print((\n            'number of rows: {} number of columns: {} '\n            'screen width: {} screen height: {}').format(*buf))\n\n.. tab:: Go\n\n    .. code-block:: go\n\n        package main\n\n        import (\n            \"fmt\"\n            \"os\"\n\n            \"golang.org/x/sys/unix\"\n        )\n\n        func main() {\n            var err error\n            var f *os.File\n            if f, err = os.OpenFile(\"/dev/tty\", unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666); err == nil {\n                var sz *unix.Winsize\n                if sz, err = unix.IoctlGetWinsize(int(f.Fd()), unix.TIOCGWINSZ); err == nil {\n                    fmt.Printf(\"rows: %v columns: %v width: %v height %v\\n\", sz.Row, sz.Col, sz.Xpixel, sz.Ypixel)\n                    return\n                }\n            }\n            fmt.Fprintln(os.Stderr, err)\n            os.Exit(1)\n        }\n\n\n.. tab:: POSIX sh\n\n    .. code-block:: sh\n\n        #!/bin/sh\n\n        read rows cols <<EOF\n        $(command stty size)\n        EOF\n\n        oldstty=$(command stty -g)\n        command stty raw -echo\n        printf \"\\033[14t\"\n        response=\"\"\n        while : ; do\n            char=$(command dd bs=1 count=1 2>/dev/null)\n            [ \"$char\" = \"t\" ] && break\n            response=\"${response}${char}\"\n        done\n        command stty \"$oldstty\"\n        h=$(echo \"$response\" | cut -d';' -f2)\n        w=$(echo \"$response\" | cut -d';' -f3)\n        printf \"number of rows: %d number of columns: %d\" \"$rows\" \"$cols\"\n        printf \" screen width: %d screen height: %d\\n\" \"$w\" \"$h\"\n\n\nNote that some terminals return ``0`` for the width and height values. Such\nterminals should be modified to return the correct values.  Examples of\nterminals that return correct values: ``kitty, xterm``\n\nYou can also use the *CSI t* escape code to get the screen size. Send\n``<ESC>[14t`` to ``STDOUT`` and kitty will reply on ``STDIN`` with\n``<ESC>[4;<height>;<width>t`` where ``height`` and ``width`` are the window\nsize in pixels. This escape code is supported in many terminals, not just\nkitty. A more precise version of this escape code, which is however supported\nin less terminals is ``<ESC>[16t`` which causes the terminal to reply with the\npixel dimensions of a single cell.\n\nA minimal example\n------------------\n\nSome minimal code to display PNG images in kitty, using the most basic\nfeatures of the graphics protocol:\n\n.. tab:: POSIX sh\n\n    .. code-block:: sh\n\n        #!/bin/sh\n\n        send_chunked() {\n            first=\"y\"\n            while IFS= read -r chunk; do\n                metadata=\"\"; [ \"$first\" = \"y\" ] && { metadata=\"a=T,f=100,\"; first=\"n\"; }\n                printf \"\\033_G%sm=1;%s\\033\\\\\" \"${metadata}\" \"${chunk}\"\n            done\n            [ \"$first\" = \"n\" ] && { printf \"\\033_Gm=0;\\033\\\\\"; return 0; }\n            return 1\n        }\n\n        transmit_png() {\n            # Different systems have different or missing base64 executables.\n            # The sed command below adds a trailing newline which openssl\n            # base64 does not produce and is needed for reading via read -r\n            { command base64 -w 4096 \"$1\" 2>/dev/null | send_chunked; } || \\\n            { command base64 -b 4096 \"$1\" 2>/dev/null | send_chunked; } || \\\n            { command openssl base64 -e -A -in \"$1\" | command sed '$a\\' | command fold -b -w 4096 | send_chunked; }\n        }\n\n        transmit_png \"$1\"\n\n\n.. tab:: Python\n\n    .. code-block:: python\n\n        #!/usr/bin/env python\n        import sys\n        from base64 import standard_b64encode\n\n        first, eof, buf = True, False, memoryview(bytearray(3 * 4096 // 4))\n        w = sys.stdout.buffer.write\n        with open(sys.argv[-1], 'rb') as f:\n            while not eof:\n                p = buf[:]\n                while p and not eof:\n                    n = f.readinto1(p)\n                    p, eof = p[n:], n == 0\n                encoded = standard_b64encode(buf[:len(buf)-len(p)])\n                metadata, first = \"a=T,f=100,\" if first else \"\", False\n                w(f'\\x1b_G{metadata}m={0 if eof else 1};'.encode('ascii'))\n                w(encoded)\n                w(b'\\x1b\\\\')\n\n\nSave this script as :file:`send-png`, then you can use it to display any PNG\nfile in kitty as::\n\n    chmod +x send-png\n    ./send-png file.png\n\n\nThe graphics escape code\n---------------------------\n\nAll graphics escape codes are of the form::\n\n    <ESC>_G<control data>;<payload><ESC>\\\n\nThis is a so-called *Application Programming Command (APC)*. Most terminal\nemulators ignore APC codes, making it safe to use.\n\nThe control data is a comma-separated list of ``key=value`` pairs.  The payload\nis arbitrary binary data, :rfc:`base64 <4648>` encoded to prevent interoperation problems\nwith legacy terminals that get confused by control codes within an APC code.\nThe meaning of the payload is interpreted based on the control data.\n\nThe first step is to transmit the actual image data.\n\n.. _transferring_pixel_data:\n\nTransferring pixel data\n--------------------------\n\nThe first consideration when transferring data between the client and the\nterminal emulator is the format in which to do so. Since there is a vast and\ngrowing number of image formats in existence, it does not make sense to have\nevery terminal emulator implement support for them. Instead, the client should\nsend simple pixel data to the terminal emulator. The obvious downside to this\nis performance, especially when the client is running on a remote machine.\nTechniques for remedying this limitation are discussed later. The terminal\nemulator must understand pixel data in three formats, 24-bit RGB, 32-bit RGBA and\nPNG. This is specified using the ``f`` key in the control data. ``f=32`` (which is the\ndefault) indicates 32-bit RGBA data and ``f=24`` indicates 24-bit RGB data and ``f=100``\nindicates PNG data. The PNG format is supported both for convenience, and as a compact way\nof transmitting paletted images.\n\nRGB and RGBA data\n~~~~~~~~~~~~~~~~~~~\n\nIn these formats the pixel data is stored directly as 3 or 4 bytes per pixel,\nrespectively. The colors in the data **must** be in the *sRGB color space*.  When\nspecifying images in this format, the image dimensions **must** be sent in the\ncontrol data. For example::\n\n    <ESC>_Gf=24,s=10,v=20;<payload><ESC>\\\n\nHere the width and height are specified using the ``s`` and ``v`` keys respectively. Since\n``f=24`` there are three bytes per pixel and therefore the pixel data must be ``3 * 10 * 20 = 600``\nbytes.\n\nPNG data\n~~~~~~~~~~~~~~~\n\nIn this format any PNG image can be transmitted directly.  For example::\n\n    <ESC>_Gf=100;<payload><ESC>\\\n\n\nThe PNG format is specified using the ``f=100`` key. The width and height of\nthe image will be read from the PNG data itself. Note that if you use both PNG and\ncompression, then you must provide the ``S`` key with the size of the PNG data.\n\n\nCompression\n~~~~~~~~~~~~~\n\nThe client can send compressed image data to the terminal emulator, by\nspecifying the ``o`` key. Currently, only :rfc:`1950` ZLIB based deflate\ncompression is supported, which is specified using ``o=z``. For example::\n\n    <ESC>_Gf=24,s=10,v=20,o=z;<payload><ESC>\\\n\nThis is the same as the example from the RGB data section, except that the\npayload is now compressed using deflate (this occurs prior to\n:rfc:`base64 <4648>` encoding).\nThe terminal emulator will decompress it before rendering. You can specify\ncompression for any format. The terminal emulator will decompress before\ninterpreting the pixel data.\n\n\nThe transmission medium\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe transmission medium is specified using the ``t`` key. The ``t`` key defaults to ``d``\nand can take the values:\n\n==================    ============\nValue of `t`          Meaning\n==================    ============\n``d``                 Direct (the data is transmitted within the escape code itself)\n``f``                 A simple file (regular files only, not named pipes, device files, etc.)\n``t``                 A temporary file, the terminal emulator will delete the file after reading the pixel data. For security reasons\n                      the terminal emulator should only delete the file if it\n                      is in a known temporary directory, such as :file:`/tmp`,\n                      :file:`/dev/shm`, :file:`TMPDIR env var if present` and any platform\n                      specific temporary directories and the file has the\n                      string :code:`tty-graphics-protocol` in its full file path.\n``s``                 A *shared memory object*, which on POSIX systems is a\n                      `POSIX shared memory object <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html>`_\n                      and on Windows is a\n                      `Named shared memory object <https://docs.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory>`_.\n                      The terminal emulator must read the data from the memory\n                      object and then unlink and close it on POSIX and just\n                      close it on Windows.\n==================    ============\n\nWhen opening files, the terminal emulator must follow symlinks. In case of\nsymlink loops or too many symlinks, it should fail and respond with an error,\nsimilar to reporting any other kind of I/O error. Since the file paths come\nfrom potentially untrusted sources, terminal emulators **must** refuse to read\nany device/socket/etc. special files. Only regular files are allowed.\nAdditionally, terminal emulators may refuse to read files in *sensitive*\nparts of the filesystem, such as :file:`/proc`, :file:`/sys`, :file:`/dev`, etc.\n\nLocal client\n^^^^^^^^^^^^^^\n\nFirst let us consider the local client techniques (files and shared memory). Some examples::\n\n    <ESC>_Gf=100,t=f;<encoded /path/to/file.png><ESC>\\\n\nHere we tell the terminal emulator to read PNG data from the specified file of\nthe specified size::\n\n    <ESC>_Gs=10,v=2,t=s,o=z;<encoded /some-shared-memory-name><ESC>\\\n\nHere we tell the terminal emulator to read compressed image data from\nthe specified shared memory object.\n\nThe client can also specify a size and offset to tell the terminal emulator\nto only read a part of the specified file. This is done using the ``S`` and ``O``\nkeys respectively. For example::\n\n    <ESC>_Gs=10,v=2,t=s,S=80,O=10;<encoded /some-shared-memory-name><ESC>\\\n\nThis tells the terminal emulator to read ``80`` bytes starting from the offset ``10``\ninside the specified shared memory buffer.\n\n\nRemote client\n^^^^^^^^^^^^^^^^\n\nRemote clients, those that are unable to use the filesystem/shared memory to\ntransmit data, must send the pixel data directly using escape codes. Since\nescape codes are of limited maximum length, the data will need to be chunked up\nfor transfer. This is done using the ``m`` key. The pixel data must first be\n:rfc:`base64 <4648>` encoded then chunked up into chunks no larger than ``4096`` bytes. All\nchunks, except the last, must have a size that is a multiple of 4. The client\nthen sends the graphics escape code as usual, with the addition of an ``m`` key\nthat must have the value ``1`` for all but the last chunk, where it must be\n``0``. For example, if the data is split into three chunks, the client would\nsend the following sequence of escape codes to the terminal emulator::\n\n    <ESC>_Gs=100,v=30,m=1;<encoded pixel data first chunk><ESC>\\\n    <ESC>_Gm=1;<encoded pixel data second chunk><ESC>\\\n    <ESC>_Gm=0;<encoded pixel data last chunk><ESC>\\\n\nNote that only the first escape code needs to have the full set of control\ncodes such as width, height, format, etc. Subsequent chunks **must** have only\nthe ``m`` and optionally ``q`` keys. When sending animation frame data, subsequent\nchunks **must** also specify the ``a=f`` key. The client **must** finish sending\nall chunks for a single image before sending any other graphics related escape\ncodes. Note that the cursor position used to display the image **must** be the\nposition when the final chunk is received. Finally, terminals must not display\nanything, until the entire sequence is received and validated.\n\n\nQuerying support and available transmission mediums\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSince a client has no a-priori knowledge of whether it shares a filesystem/shared memory\nwith the terminal emulator, it can send an id with the control data, using the ``i`` key\n(which can be an arbitrary positive integer up to 4294967295, it must not be zero).\nIf it does so, the terminal emulator will reply after trying to load the image, saying\nwhether loading was successful or not. For example::\n\n    <ESC>_Gi=31,s=10,v=2,t=s;<encoded /some-shared-memory-name><ESC>\\\n\nto which the terminal emulator will reply (after trying to load the data)::\n\n    <ESC>_Gi=31;error message or OK<ESC>\\\n\nHere the ``i`` value will be the same as was sent by the client in the original\nrequest.  The message data will be a ASCII encoded string containing only\nprintable characters and spaces. The string will be ``OK`` if reading the pixel\ndata succeeded or an error message.\n\nSometimes, using an id is not appropriate, for example, if you do not want to\nreplace a previously sent image with the same id, or if you are sending a dummy\nimage and do not want it stored by the terminal emulator. In that case, you can\nuse the *query action*, set ``a=q``. Then the terminal emulator will try to load\nthe image and respond with either OK or an error, as above, but it will not\nreplace an existing image with the same id, nor will it store the image.\n\nWe intend that any terminal emulator that wishes to support it can do so. To\ncheck if a terminal emulator supports the graphics protocol the best way is to\nsend the above *query action* followed by a request for the `primary device\nattributes <https://vt100.net/docs/vt510-rm/DA1.html>`_. If you get back an\nanswer for the device attributes without getting back an answer for the *query\naction* the terminal emulator does not support the graphics protocol.\n\nThis means that terminal emulators that support the graphics protocol, **must**\nreply to *query actions* immediately without processing other input. Most\nterminal emulators handle input in a FIFO manner, anyway.\n\nSo for example, you could send::\n\n      <ESC>_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA<ESC>\\<ESC>[c\n\nIf you get back a response to the graphics query, the terminal emulator supports\nthe protocol, if you get back a response to the device attributes query without\na response to the graphics query, it does not.\n\n\nDisplay images on screen\n-----------------------------\n\nEvery transmitted image can be displayed an arbitrary number of times on the\nscreen, in different locations, using different parts of the source image, as\nneeded. Each such display of an image is called a *placement*.  You can either\nsimultaneously transmit and display an image using the action ``a=T``, or first\ntransmit the image with a id, such as ``i=10`` and then display it with\n``a=p,i=10`` which will display the previously transmitted image at the current\ncursor position. When specifying an image id, the terminal emulator will reply\nto the placement request with an acknowledgement code, which will be either::\n\n    <ESC>_Gi=<id>;OK<ESC>\\\n\nwhen the image referred to by id was found, or::\n\n    <ESC>_Gi=<id>;ENOENT:<some detailed error msg><ESC>\\\n\nwhen the image with the specified id was not found. This is similar to the\nscheme described above for querying available transmission media, except that\nhere we are querying if the image with the specified id is available or needs to\nbe re-transmitted.\n\nSince there can be many placements per image, you can also give placements an\nid. To do so add the ``p`` key with a number between ``1`` and ``4294967295``.\nWhen you specify a placement id, it will be added to the acknowledgement code\nabove. Every placement is uniquely identified by the pair of the ``image id``\nand the ``placement id``. If you specify a placement id for an image that does\nnot have an id (i.e. has id=0), it will be ignored, i.e. the placement will not\nget an id. In particular this means there can exist multiple images with\n``image id=0, placement id=0``. Not specifying a placement id or using ``p=0``\nfor multiple put commands (``a=p``) with the same non-zero image id results in\nmultiple placements the image.\n\nAn example response::\n\n    <ESC>_Gi=<image id>,p=<placement id>;OK<ESC>\\\n\nIf you send two placements with the same ``image id`` and ``placement id`` the\nsecond one will replace the first. This can be used to resize or move\nplacements around the screen, without flicker.\n\n\n.. note::\n   When re-transmitting image data for a specific id, the existing image and\n   all its placements must be deleted. The new data replaces the old image data\n   but is not actually displayed until a placement for it is created. This is\n   to avoid divergent behavior in the case when unrelated programs happen to re-use\n   image ids in the same session.\n\n\n.. versionadded:: 0.19.3\n   Support for specifying placement ids (see :doc:`kittens/query_terminal` to query kitty version)\n\n\nControlling displayed image layout\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe image is rendered at the current cursor position, from the upper left corner of\nthe current cell. You can also specify extra ``X=3`` and ``Y=4`` pixel offsets to display from\na different origin within the cell. Note that the offsets must be smaller than the size of the cell.\n\nBy default, the entire image will be displayed (images wider than the available\nwidth will be truncated on the right edge). You can choose a source rectangle (in pixels)\nas the part of the image to display. This is done with the keys: ``x, y, w, h`` which specify\nthe top-left corner, width and height of the source rectangle. The displayed\narea is the intersection of the specified rectangle with the source image\nrectangle.\n\nYou can also ask the terminal emulator to display the image in a specified rectangle\n(num of columns / num of lines), using the control codes ``c,r``. ``c`` is the number of columns\nand `r` the number of rows. The image will be scaled (enlarged/shrunk) as needed to fit\nthe specified area. Note that if you specify a start cell offset via the ``X,Y`` keys, it is not\nadded to the number of rows/columns. If only one of either ``r`` or ``c`` is\nspecified, the other one is computed based on the source image aspect ratio, so\nthat the image is displayed without distortion.\n\nFinally, you can specify the image *z-index*, i.e. the vertical stacking order. Images\nplaced in the same location with different z-index values will be blended if\nthey are semi-transparent. You can specify z-index values using the ``z`` key.\nNegative z-index values mean that the images will be drawn under the text. This\nallows rendering of text on top of images. Negative z-index values below\nINT32_MIN/2 (-1,073,741,824) will be drawn under cells with non-default background\ncolors. If two images with the same z-index overlap then the image with the\nlower id is considered to have the lower z-index. If the images have the same\nz-index and the same id, then the behavior is undefined.\n\n.. note:: After placing an image on the screen the cursor must be moved to the\n   right by the number of cols in the image placement rectangle and down by the\n   number of rows in the image placement rectangle. If either of these cause\n   the cursor to leave either the screen or the scroll area, the exact\n   positioning of the cursor is undefined, and up to implementations.\n   The client can ask the terminal emulator to not move the cursor at all\n   by specifying ``C=1`` in the command, which sets the cursor movement policy\n   to no movement for placing the current image.\n\n.. versionadded:: 0.20.0\n   Support for the C=1 cursor movement policy\n\n\n.. _graphics_unicode_placeholders:\n\nUnicode placeholders\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. versionadded:: 0.28.0\n   Support for image display via Unicode placeholders\n\nYou can also use a special Unicode character ``U+10EEEE`` as a placeholder for\nan image. This approach is less flexible, but it allows using images inside\nany host application that supports Unicode, foreground colors (tmux, vim, weechat, etc.),\nand a way to pass escape codes through to the underlying terminal.\n\nThe central idea is that we use a single *Private Use* Unicode character as a\n*placeholder* to indicate to the terminal that an image is supposed to be\ndisplayed at that cell. Since this character is just normal text, Unicode aware\napplication will move it around as needed when they redraw their screens,\nthereby automatically moving the displayed image as well, even though they know\nnothing about the graphics protocol. So an image is first created using the\nnormal graphics protocol escape codes (albeit in quiet mode (``q=2``) so that there are\nno responses from the terminal that could confuse the host application). Then,\nthe actual image is displayed by getting the host application to emit normal\ntext consisting of ``U+10EEEE`` and various diacritics (Unicode combining\ncharacters) and colors.\n\nTo use it, first create an image as you would normally with the graphics\nprotocol with (``q=2``), but do not create a placement for it, that is, do not\ndisplay it. Then, create a *virtual image placement* by specifying ``U=1`` and\nthe desired number of lines and columns::\n\n    <ESC>_Ga=p,U=1,i=<image_id>,c=<columns>,r=<rows><ESC>\\\n\nThe creation of the placement need not be a separate escape code, it can be\ncombined with ``a=T`` to both transmit and create the virtual placement with a\nsingle code.\n\nThe image will eventually be fit to the specified rectangle, its aspect ratio\npreserved. Finally, the image can be actually displayed by using the\nplaceholder character, encoding the image ID in its foreground color. The row\nand column values are specified with diacritics listed in\n:download:`rowcolumn-diacritics.txt <../gen/rowcolumn-diacritics.txt>`.  For\nexample, here is how you can print a ``2x2`` placeholder for image ID ``42``:\n\n.. code-block:: sh\n\n    printf \"\\e[38;5;42m\\U10EEEE\\U0305\\U0305\\U10EEEE\\U0305\\U030D\\e[39m\\n\"\n    printf \"\\e[38;5;42m\\U10EEEE\\U030D\\U0305\\U10EEEE\\U030D\\U030D\\e[39m\\n\"\n\nHere, ``U+305`` is the diacritic corresponding to the number ``0``\nand ``U+30D`` corresponds to ``1``. So these two commands create the following\n``2x2`` placeholder:\n\n========== ==========\n(0, 0)     (0, 1)\n(1, 0)     (1, 1)\n========== ==========\n\nThis will cause the image with ID ``42`` to be displayed in a ``2x2`` grid.\nIdeally, you would print out as many cells as the number of rows and columns\nspecified when creating the virtual placement, but in case of a mismatch only\npart of the image will be displayed.\n\nBy using only the foreground color for image ID you are limited to either 8-bit IDs in 256 color\nmode or 24-bit IDs in true color mode. Since IDs are in a global namespace\nthere can easily be collisions. If you need more bits for the image\nID, you can specify the most significant byte via a third diacritic. For\nexample, this is the placeholder for the image ID ``33554474 = 42 + (2 << 24)``:\n\n.. code-block:: sh\n\n    printf \"\\e[38;5;42m\\U10EEEE\\U0305\\U0305\\U030E\\U10EEEE\\U0305\\U030D\\U030E\\n\"\n    printf \"\\e[38;5;42m\\U10EEEE\\U030D\\U0305\\U030E\\U10EEEE\\U030D\\U030D\\U030E\\n\"\n\nHere, ``U+30E`` is the diacritic corresponding to the number ``2``.\n\nYou can also specify a placement ID using the underline color (if it's omitted\nor zero, the terminal may choose any virtual placement of the given image). The\nbackground color is interpreted as the background color, visible if the image is\ntransparent. Other text attributes are reserved for future use.\n\nRow, column and most significant byte diacritics may also be omitted, in which\ncase the placeholder cell will inherit the missing values from the placeholder\ncell to the left, following the algorithm:\n\n- If no diacritics are present, and the previous placeholder cell has the same\n  foreground and underline colors, then the row of the current cell will be the\n  row of the cell to the left, the column will be the column of the cell to the\n  left plus one, and the most significant image ID byte will be the most\n  significant image ID byte of the cell to the left.\n- If only the row diacritic is present, and the previous placeholder cell has\n  the same row and the same foreground and underline colors, then the column of\n  the current cell will be the column of the cell to the left plus one, and the\n  most significant image ID byte will be the most significant image ID byte of\n  the cell to the left.\n- If only the row and column diacritics are present, and the previous\n  placeholder cell has the same row, the same foreground and underline colors,\n  and its column is one less than the current column, then the most significant\n  image ID byte of the current cell will be the most significant image ID byte\n  of the cell to the left.\n\nThese rules are applied left-to-right, which allows specifying only row\ndiacritics of the first column, i.e. here is a 2 rows by 3 columns placeholder:\n\n.. code-block:: sh\n\n    printf \"\\e[38;5;42m\\U10EEEE\\U0305\\U10EEEE\\U10EEEE\\n\"\n    printf \"\\e[38;5;42m\\U10EEEE\\U030D\\U10EEEE\\U10EEEE\\n\"\n\nThis will not work for horizontal scrolling and overlapping images since the two\ngiven rules will fail to guess the missing information. In such cases, the\nterminal may apply other heuristics (but it doesn't have to).\n\nIt is important to distinguish between virtual image placements and real images\ndisplayed on top of Unicode placeholders. Virtual placements are invisible and only play\nthe role of prototypes for real images. Virtual placements can be deleted by a\ndeletion command only when the `d` key is equal to ``i``, ``I``, ``r``, ``R``, ``n`` or ``N``.\nThe key values ``a``, ``c``, ``p``, ``q``, ``x``, ``y``, ``z`` and their capital\nvariants never affect virtual placements because they do not have a physical\nlocation on the screen.\n\nReal images displayed on top of Unicode placeholders are not considered\nplacements from the protocol perspective. They cannot be manipulated using\ngraphics commands, instead they should be moved, deleted, or modified by\nmanipulating the underlying Unicode placeholder as normal text.\n\n.. _relative_image_placement:\n\nRelative placements\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. versionadded:: 0.31.0\n   Support for positioning images relative to other images\n\nYou can specify that a placement is positioned relative to another placement.\nThis is particularly useful in combination with\n:ref:`graphics_unicode_placeholders` above. It can be used to specify a single\ntransparent pixel image using a Unicode placeholder, which moves around\nnaturally with the text, the real image(s) can base their position relative to\nthe placeholder.\n\nTo specify that a placement should be relative to another, use the\n``P=<image_id>,Q=<placement_id>`` keys, when creating the relative placement.\nFor example::\n\n    <ESC>_Ga=p,i=<image_id>,p=<placement_id>,P=<parent_img_id>,Q=<parent_placement_id><ESC>\\\n\nThis will create a *relative placement* that refers to the *parent placement*\nspecified by the ``P`` and ``Q`` keys. When the parent placement moves, the\nrelative placement moves along with it. The relative placement can be offset\nfrom the parent's location by a specified number of cells, using the ``H`` and\n``V`` keys for horizontal and vertical displacement. Positive values move right\nand down. Negative values move left and up. The origin is the top left cell of\nthe parent placement.\n\nThe lifetime of a relative placement is tied to the lifetime of its parent. If\nits parent is deleted, it is deleted as well. If the image that the relative\nplacement is a placement of, has no more placements, the image is deleted as\nwell. Thus, a parent and its relative placements form a *group* that is managed\ntogether.\n\nA relative placement can refer to another relative placement as its parent.\nThus the relative placements can form a chain. It is implementation dependent\nhow long a chain of such placements is allowed, but implementation must allow\na chain of length at least 8. If the implementation max depth is exceeded, the\nterminal must respond with the ``ETOODEEP`` error code.\n\nVirtual placements created for Unicode placeholder based images cannot also be\nrelative placements. However, a relative placement can refer to a virtual\nplacement as its parent. When a virtual placement is the parent, its position\nis derived from all the actual Unicode placeholder images that refer to it.\nThe x position is the minimum of all the placeholder x positions and the y\nposition is the minimum of all the placeholder y positions. If a client\nattempts to make a virtual placement relative the terminal must respond with\nthe ``EINVAL`` error code.\n\nTerminals are required to reject the creation of a relative placement\nthat would create a cycle, such as when A is relative to B and B is relative to\nC and C is relative to A. In such cases, the terminal must respond with the\n``ECYCLE`` error code.\n\nIf a client attempts to create a reference to a placement that does not exist\nthe terminal must respond with the ``ENOPARENT`` error code.\n\n.. note::\n   Since a relative placement gets its position specified based on another\n   placement, instead of the cursor, the cursor must not move after a relative\n   position, regardless of the value of the ``C`` key to control cursor\n   movement.\n\n\nDeleting images\n---------------------\n\nImages can be deleted by using the delete action ``a=d``. If specified without any\nother keys, it will delete all images visible on screen. To delete specific images,\nuse the `d` key as described in the table below. Note that each value of d has\nboth a lowercase and an uppercase variant. The lowercase variant only deletes the\nimages without necessarily freeing up the stored image data, so that the images can be\nre-displayed without needing to resend the data. The uppercase variants will delete\nthe image data as well, provided that the image is not referenced elsewhere, such as in the\nscrollback buffer. The values of the ``x`` and ``y`` keys are the same as cursor positions (i.e.\n``x=1, y=1`` is the top left cell).\n\n=================    ============\nValue of ``d``       Meaning\n=================    ============\n``a`` or ``A``       Delete all placements visible on screen\n``i`` or ``I``       Delete all images with the specified id, specified using the ``i`` key. If you specify a ``p`` key for the placement                          id as well, then only the placement with the specified image id and placement id will be deleted.\n``n`` or ``N``       Delete newest image with the specified number, specified using the ``I`` key. If you specify a ``p`` key for the\n                     placement id as well, then only the placement with the specified number and placement id will be deleted.\n``c`` or ``C``       Delete all placements that intersect with the current cursor position.\n``f`` or ``F``       Delete animation frames.\n``p`` or ``P``       Delete all placements that intersect a specific cell, the cell is specified using the ``x`` and ``y`` keys\n``q`` or ``Q``       Delete all placements that intersect a specific cell having a specific z-index. The cell and z-index is specified using the ``x``, ``y`` and ``z`` keys.\n``r`` or ``R``       Delete all images whose id is greater than or equal to the value of the ``x`` key and less than or equal to the value of the ``y`` (added in kitty version 0.33.0).\n``x`` or ``X``       Delete all placements that intersect the specified column, specified using the ``x`` key.\n``y`` or ``Y``       Delete all placements that intersect the specified row, specified using the ``y`` key.\n``z`` or ``Z``       Delete all placements that have the specified z-index, specified using the ``z`` key.\n=================    ============\n\n\nNote when all placements for an image have been deleted, the image is also\ndeleted, if the capital letter form above is specified. Also, when the terminal\nis running out of quota space for new images, existing images without\nplacements will be preferentially deleted.\n\nIf an image is being loaded in chunks and the upload is not complete when any\ndelete command is received, the partial upload must be aborted.\n\nSome examples::\n\n    <ESC>_Ga=d<ESC>\\              # delete all visible placements\n    <ESC>_Ga=d,d=i,i=10<ESC>\\     # delete the image with id=10, without freeing data\n    <ESC>_Ga=d,d=i,i=10,p=7<ESC>\\ # delete the image with id=10 and placement id=7, without freeing data\n    <ESC>_Ga=d,d=Z,z=-1<ESC>\\     # delete the placements with z-index -1, also freeing up image data\n    <ESC>_Ga=d,d=p,x=3,y=4<ESC>\\  # delete all placements that intersect the cell at (3, 4), without freeing data\n\n\nSuppressing responses from the terminal\n-------------------------------------------\n\nIf you are using the graphics protocol from a limited client, such as a shell\nscript, it might be useful to avoid having to process responses from the\nterminal. For this, you can use the ``q`` key. Set it to ``1`` to suppress\n``OK`` responses and to ``2`` to suppress failure responses.\n\n.. versionadded:: 0.19.3\n   The ability to suppress responses (see :doc:`kittens/query_terminal` to query kitty version)\n\n\nRequesting image ids from the terminal\n-------------------------------------------\n\nIf you are writing a program that is going to share the screen with other\nprograms and you still want to use image ids, it is not possible to know\nwhat image ids are free to use. In this case, instead of using the ``i``\nkey to specify an image id use the ``I`` key to specify an image number\ninstead. These numbers are not unique.\nWhen creating a new image, even if an existing image has the same number a new\none is created. And the terminal will reply with the id of the newly created\nimage. For example, when creating an image with ``I=13``, the terminal will\nsend the response::\n\n    <ESC>_Gi=99,I=13;OK<ESC>\\\n\nHere, the value of ``i`` is the id for the newly created image and the value of\n``I`` is the same as was sent in the creation command.\n\nAll future commands that refer to images using the image number, such as\ncreating placements or deleting images, will act on only the newest image with\nthat number. This allows the client program to send a bunch of commands dealing\nwith an image by image number without waiting for a response from the terminal\nwith the image id. Once such a response is received, the client program should\nuse the ``i`` key with the image id for all future communication.\n\n.. note:: Specifying both ``i`` and ``I`` keys in any command is an error. The\n   terminal must reply with an EINVAL error message, unless silenced.\n\n.. versionadded:: 0.19.3\n   The ability to use image numbers (see :doc:`kittens/query_terminal` to query kitty version)\n\n\n.. _animation_protocol:\n\nAnimation\n-------------------------------------------\n\n.. versionadded:: 0.20.0\n   Animation support (see :doc:`kittens/query_terminal` to query kitty version)\n\nWhen designing support for animation, the two main considerations were:\n\n#. There should be a way for both client and terminal driven animations.\n   Since there is unknown and variable latency between client and terminal,\n   especially over SSH, client driven animations are not sufficient.\n\n#. Animations often consist of small changes from one frame to the next, the\n   protocol should thus allow transmitting these deltas for efficiency and\n   performance reasons.\n\nAnimation support is added to the protocol by adding two new modes for the\n``a`` (action) key. A ``f`` mode for transmitting frame data and an ``a`` mode\nfor controlling the animation of an image. Animation proceeds in two steps,\nfirst a normal image is created as described earlier. Then animation frames are\nadded to the image to make it into an animation. Since every animation is\nassociated with a single image, all animation escape codes must specify either\nthe ``i`` or ``I`` keys to identify the image being operated on.\n\n\nTransferring animation frame data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTransferring animation frame data is very similar to\n:ref:`transferring_pixel_data` above. The main difference is that the image\nthe frame belongs to must be specified and it is possible to transmit data for\nonly part of a frame, declaring the rest of the frame to be filled in by data\nfrom a previous frame, or left blank. To transfer frame data the ``a=f``\nkey must be used in all escape codes.\n\nFirst, to transfer a simple frame that has data for the full image area, the\nescape codes used are exactly the same as for transferring image data, with the\naddition of: ``a=f,i=<image id>`` or ``a=f,I=<image number>``.\n\nIf the frame has data for only a part of the image, you can specify the\nrectangle for it using the ``x, y, s, v`` keys, for example::\n\n    x=10,y=5,s=100,v=200  # A 100x200 rectangle with its top left corner at (10, 5)\n\nFrames are created by composing the transmitted data onto a background canvas.\nThis canvas can be either a single color, or the pixels from a previous frame.\nThe composition can be of two types, either a simple replacement (``X=1``) key\nor a full alpha blend (the default).\n\nTo use a background color for the canvas, specify the ``Y`` key as a 32-bit\nRGBA color. For example::\n\n    Y=4278190335 # 0xff0000ff opaque red\n    Y=16711816   # 0x00ff0088 translucent green (alpha=0.53)\n\nThe default background color when none is specified is ``0`` i.e. a black,\ntransparent pixel.\n\nTo use the data from a previous frame, specify the ``c`` key which is a 1-based\nframe number. Thus ``c=1`` refers to the root frame (the base image data),\n``c=2`` refers to the second frame and so on.\n\nIf the frame is composed of multiple rectangular blocks, these can be expressed\nby using the ``r`` key. When specifying the ``r`` key the data for an existing\nframe is edited. The same composition operation as above happens, but now the\nbackground canvas is the existing frame itself. ``r`` is a 1-based index, so\n``r=1`` is the root frame (base image data), ``r=2`` is the second frame and so\non.\n\nFinally, while transferring frame data, the frame *gap* can also be specified\nusing the ``z`` key. The gap is the number of milliseconds to wait before\ndisplaying the next frame when the animation is running. A value of ``z=0`` is\nignored (acts as though ``z`` was unspecified), ``z=positive number`` sets the\ngap to the specified number of milliseconds and ``z=negative number`` creates a\n*gapless* frame. Gapless frames are not displayed to the user since they are\ninstantly skipped over, however they can be useful as the base data for\nsubsequent frames. For example, for an animation where the background remains\nthe same and a small object or two move.\n\nControlling animations\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nClients can control animations by using the ``a=a`` key in the escape code sent\nto the terminal.\n\nThe simplest is client driven animations, where the client transmits the frame\ndata and then also instructs the terminal to make a particular frame the current\nframe.  To change the current frame, use the ``c`` key::\n\n    <ESC>_Ga=a,i=3,c=7<ESC>\\\n\nThis will make the seventh frame in the image with id ``3`` the current frame.\n\nHowever, client driven animations can be sub-optimal, since the latency between\nthe client and terminal is unknown and variable especially over the network.\nAlso they require the client to remain running for the lifetime of the\nanimation, which is not desirable for cat like utilities.\n\nTerminal driven animations are achieved by the client specifying *gaps* (time\nin milliseconds) between frames and instructing the terminal to stop or start\nthe animation.\n\nThe animation state is controlled by the ``s`` key. ``s=1`` stops the\nanimation. ``s=2`` runs the animation, but in *loading* mode, in this mode when\nreaching the last frame, instead of looping, the terminal will wait for the\narrival of more frames. ``s=3`` runs the animation normally, after the last\nframe, the terminal loops back to the first frame. The number of loops can be\ncontrolled by the ``v`` key. ``v=0`` is ignored (acts as though ``v`` was not\nspecified), ``v=1`` is loop infinitely, and any other positive number is loop\n``number - 1`` times. Note that stopping the animation resets the loop counter.\n\nFinally, the *gap* for frames can be set using the ``z`` key. This can be\nspecified either when the frame is created as part of the transmit escape code\nor separately using the animation control escape code. The *gap* is the time in\nmilliseconds to wait before displaying the next frame in the animation.\nFor example::\n\n    <ESC>_Ga=a,i=7,r=3,z=48<ESC>\\\n\nThis sets the gap for the third frame of the image with id ``7`` to ``48``\nmilliseconds. Note that *gapless* frames are not displayed to the user since\nthe next frame comes immediately, however they can be useful to store base data\nfor subsequent frames, such as in an animation with an object moving against a\nstatic background.\n\nIn particular, the first frame or *root frame* is created with the base image\ndata and has no gap, so its gap must be set using this control code.\n\nComposing animation frames\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. versionadded:: 0.22.0\n   Support for frame composition\n\nClients can *compose* animation frames, this means that they can compose pixels\nin rectangular regions from one frame onto another frame. This allows for fast\nand low band-width modification of frames.\n\nTo achieve this use the ``a=c`` key. The source frame is specified with\n``r=frame number`` and the destination frame as ``c=frame number``. The size of\nthe rectangle is specified as ``w=width,h=height`` pixels. If unspecified, the\nfull image width and height are used. The offset of the rectangle from the\ntop-left corner for the source frame is specified by the ``x,y`` keys and the\ndestination frame by the ``X,Y`` keys. The composition operation is specified\nby the ``C`` key with the default being to alpha blend the source rectangle\nonto the destination rectangle. With ``C=1`` it will be a simple replacement\nof pixels. For example::\n\n    <ESC>_Ga=c,i=1,r=7,c=9,w=23,h=27,X=4,Y=8,x=1,y=3<ESC>\\\n\nWill compose a ``23x27`` rectangle located at ``(4, 8)`` in the ``7th frame``\nonto the rectangle located at ``(1, 3)`` in the ``9th frame``. These will be\nin the image with ``id=1``.\n\nIf the frames or the image are not found the terminal emulator must\nrespond with `ENOENT`. If the rectangles go out of bounds of the image\nthe terminal must respond with `EINVAL`. If the source and destination frames are\nthe same and the rectangles overlap, the terminal must respond with `EINVAL`.\n\n\n.. note::\n   In kitty, doing a composition will cause a frame to be *fully rendered*\n   potentially increasing its storage requirements, when the frame was previously\n   stored as a set of operations on other frames. If this happens and there\n   is not enough storage space, kitty will respond with ENOSPC.\n\n\nImage persistence and storage quotas\n-----------------------------------------\n\nIn order to avoid *Denial-of-Service* attacks, terminal emulators should have a\nmaximum storage quota for image data. It should allow at least a few full\nscreen images.  For example the quota in kitty is 320MB per buffer. When adding\na new image, if the total size exceeds the quota, the terminal emulator should\ndelete older images to make space for the new one. In kitty, for animations,\nthe additional frame data is stored on disk and has a separate, larger quota of\nfive times the base quota.\n\n\nControl data reference\n---------------------------\n\nThe table below shows all the control data keys as well as what values they can\ntake, and the default value they take when missing. All integers are 32-bit.\n\n=======  ====================  =========  =================\nKey      Value                 Default    Description\n=======  ====================  =========  =================\n``a``    Single character.     ``t``      The overall action this graphics command is performing.\n         ``(a, c, d, f,                   ``t`` - transmit data, ``T`` - transmit data and display image,\n         p, q, t, T)``                    ``q`` - query terminal, ``p`` - put (display) previous transmitted image,\n                                          ``d`` - delete image, ``f`` - transmit data for animation frames,\n                                          ``a`` - control animation, ``c`` - compose animation frames\n\n``q``    ``0, 1, 2``           ``0``      Suppress responses from the terminal to this graphics command.\n\n**Keys for image transmission**\n-----------------------------------------------------------\n``f``    Positive integer.     ``32``     The format in which the image data is sent.\n         ``(24, 32, 100)``.\n``t``    Single character.     ``d``      The transmission medium used.\n         ``(d, f, t, s)``.\n``s``    Positive integer.     ``0``      The width of the image being sent.\n``v``    Positive integer.     ``0``      The height of the image being sent.\n``S``    Positive integer.     ``0``      The size of data to read from a file.\n``O``    Positive integer.     ``0``      The offset from which to read data from a file.\n``i``    Positive integer.\n         ``(0 - 4294967295)``  ``0``      The image id\n``I``    Positive integer.\n         ``(0 - 4294967295)``  ``0``      The image number\n``p``    Positive integer.\n         ``(0 - 4294967295)``  ``0``      The placement id\n``o``    Single character.     ``null``   The type of data compression.\n         ``only z``\n``m``    zero or one           ``0``      Whether there is more chunked data available.\n\n**Keys for image display**\n-----------------------------------------------------------\n``x``    Positive integer      ``0``      The left edge (in pixels) of the image area to display\n``y``    Positive integer      ``0``      The top edge (in pixels) of the image area to display\n``w``    Positive integer      ``0``      The width (in pixels) of the image area to display. By default, the entire width is used\n``h``    Positive integer      ``0``      The height (in pixels) of the image area to display. By default, the entire height is used\n``X``    Positive integer      ``0``      The x-offset within the first cell at which to start displaying the image\n``Y``    Positive integer      ``0``      The y-offset within the first cell at which to start displaying the image\n``c``    Positive integer      ``0``      The number of columns to display the image over\n``r``    Positive integer      ``0``      The number of rows to display the image over\n``C``    Positive integer      ``0``      Cursor movement policy. ``0`` is the default, to move the cursor to after the image.\n                                          ``1`` is to not move the cursor at all when placing the image.\n``U``    Positive integer      ``0``      Set to ``1`` to create a virtual placement for a Unicode placeholder.\n``z``    32-bit integer        ``0``      The *z-index* vertical stacking order of the image\n``P``    Positive integer      ``0``      The id of a parent image for relative placement\n``Q``    Positive integer      ``0``      The id of a placement in the parent image for relative placement\n``H``    32-bit integer        ``0``      The offset in cells in the horizontal direction for relative placement\n``V``    32-bit integer        ``0``      The offset in cells in the vertical direction for relative placement\n\n**Keys for animation frame loading**\n-----------------------------------------------------------\n``x``    Positive integer      ``0``      The left edge (in pixels) of where the frame data should be updated\n``y``    Positive integer      ``0``      The top edge (in pixels) of where the frame data should be updated\n``c``    Positive integer      ``0``      The 1-based frame number of the frame whose image data serves as the base data\n                                          when creating a new frame, by default the base data is black, fully transparent pixels\n``r``    Positive integer      ``0``      The 1-based frame number of the frame that is being edited. By default, a new frame is created\n``z``    32-bit integer        ``0``      The gap (in milliseconds) of this frame from the next one. A value of\n                                          zero is ignored. Negative values create a *gapless* frame. If not specified,\n                                          frames have a default gap of ``40ms``. The root frame defaults to zero gap.\n``X``    Positive integer      ``0``      The composition mode for blending pixels when creating a new frame or\n                                          editing a frame's data. The default is full alpha blending. ``1`` means a\n                                          simple overwrite.\n``Y``    Positive integer      ``0``      The background color for pixels not\n                                          specified in the frame data. Must be in 32-bit RGBA format\n\n**Keys for animation frame composition**\n-----------------------------------------------------------\n\n``c``    Positive integer      ``0``      The 1-based frame number of the frame whose image data serves as the overlaid data\n``r``    Positive integer      ``0``      The 1-based frame number of the frame that is being edited.\n``x``    Positive integer      ``0``      The left edge (in pixels) of the destination rectangle\n``y``    Positive integer      ``0``      The top edge (in pixels) of the destination rectangle\n``w``    Positive integer      ``0``      The width (in pixels) of the source and destination rectangles. By default, the entire width is used\n``h``    Positive integer      ``0``      The height (in pixels) of the source and destination rectangles. By default, the entire height is used\n``X``    Positive integer      ``0``      The left edge (in pixels) of the source rectangle\n``Y``    Positive integer      ``0``      The top edge (in pixels) of the source rectangle\n``C``    Positive integer      ``0``      The composition mode for blending\n                                          pixels. Default is full alpha blending. ``1`` means a simple overwrite.\n\n\n**Keys for animation control**\n-----------------------------------------------------------\n``s``    Positive integer      ``0``      ``1`` - stop animation, ``2`` - run animation, but wait for new frames, ``3`` - run animation\n``r``    Positive integer      ``0``      The 1-based frame number of the frame that is being affected\n``z``    32-bit integer        ``0``      The gap (in milliseconds) of this frame from the next one. A value of\n                                          zero is ignored. Negative values create a *gapless* frame.\n``c``    Positive integer      ``0``      The 1-based frame number of the frame that should be made the current frame\n``v``    Positive integer      ``0``      The number of loops to play. ``0`` is\n                                          ignored, ``1`` is play infinite and is the default and larger number\n                                          means play that number ``-1`` loops\n\n\n**Keys for deleting images**\n-----------------------------------------------------------\n``d``    Single character.     ``a``      What to delete.\n         ``(\n         a, A, c, C, n, N,\n         i, I, p, P, q, Q, r,\n         R, x, X, y, Y, z, Z\n         )``.\n=======  ====================  =========  =================\n\n\nInteraction with other terminal actions\n--------------------------------------------\n\nWhen resetting the terminal, all images that are visible on the screen must be\ncleared.  When switching from the main screen to the alternate screen buffer\n(1049 private mode) all images in the alternate screen must be cleared, just as\nall text is cleared. The clear screen escape code (usually ``<ESC>[2J``) should\nalso clear all images. This is so that the clear command works.\n\nThe other commands to erase text must have no effect on graphics.\nThe dedicated delete graphics commands must be used for those.\n\nWhen scrolling the screen (such as when using index cursor movement commands,\nor scrolling through the history buffer), images must be scrolled along with\ntext. When page margins are defined and the index commands are used, only\nimages that are entirely within the page area (between the margins) must be\nscrolled. When scrolling them would cause them to extend outside the page area,\nthey must be clipped.\n"
  },
  {
    "path": "docs/index.rst",
    "content": "kitty\n==========================================================\n\n*If you live in the terminal, kitty is made for YOU!*\n\nThe fast, feature-rich, GPU based terminal emulator.\n\n.. toctree::\n    :hidden:\n\n    quickstart\n    overview\n    faq\n    support\n    sessions\n    performance\n    changelog\n    integrations\n    protocol-extensions\n    press-mentions\n\n\n.. tab:: Fast\n\n   * Uses GPU and SIMD vector CPU instructions for :doc:`best in class performance <performance>`\n   * Uses threaded rendering for :iss:`absolutely minimal latency <2701#issuecomment-636497270>`\n   * Performance tradeoffs can be :ref:`tuned <conf-kitty-performance>`\n\n.. tab:: Capable\n\n   * Graphics, with :doc:`images and animations <graphics-protocol>`\n   * Ligatures, emoji with :opt:`per glyph font substitution <symbol_map>` and :doc:`variable fonts and font features </kittens/choose-fonts>`\n   * :term:`Hyperlinks<hyperlinks>`, with :doc:`configurable actions <open_actions>`\n\n.. tab:: Scriptable\n\n   * Control from :doc:`scripts or the shell <remote-control>`\n   * Extend with :ref:`kittens <kittens>` using the Python language\n   * Use :ref:`startup sessions <sessions>` to specify working environments\n\n.. tab:: Composable\n\n   * Programmable tabs, :ref:`splits <splits_layout>` and multiple :doc:`layouts <layouts>` to manage windows\n   * Browse the :ref:`entire history <scrollback>` or the :sc:`output from the last command <show_last_command_output>`\n     comfortably in pagers and editors\n   * Edit or download :doc:`remote files <kittens/remote_file>` in an existing SSH session\n\n.. tab:: Cross-platform\n\n   * Linux\n   * macOS\n   * Various BSDs\n\n.. tab:: Innovative\n\n   Pioneered various extensions to move the entire terminal ecosystem forward\n\n   * :doc:`graphics-protocol`\n   * :doc:`keyboard-protocol`\n   * Lots more in :doc:`protocol-extensions`\n\n\nTo get started see :doc:`quickstart`.\n\n.. only:: dirhtml\n\n   .. include:: intro_vid.rst\n"
  },
  {
    "path": "docs/installer.sh",
    "content": "#!/bin/sh\n# Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n#\n# Distributed under terms of the GPLv3 license.\n\n{ \\unalias command; \\unset -f command; } >/dev/null 2>&1\ntdir=''\ncleanup() {\n    [ -n \"$tdir\" ] && {\n        command rm -rf \"$tdir\"\n        tdir=''\n    }\n}\n\ndie() {\n    cleanup\n    printf \"\\033[31m%s\\033[m\\n\\r\" \"$*\" > /dev/stderr;\n    exit 1;\n}\n\ndetect_network_tool() {\n    if command -v curl 2> /dev/null > /dev/null; then\n        fetch() {\n            command curl -fL \"$1\"\n        }\n        fetch_quiet() {\n            command curl -fsSL \"$1\"\n        }\n    elif command -v wget 2> /dev/null > /dev/null; then\n        fetch() {\n            command wget -O- \"$1\"\n        }\n        fetch_quiet() {\n            command wget --quiet -O- \"$1\"\n        }\n    else\n        die \"Neither curl nor wget available, cannot download kitty\"\n    fi\n}\n\n\ndetect_os() {\n    arch=\"\"\n    case \"$(command uname)\" in\n        'Darwin') OS=\"macos\";;\n        'Linux')\n            OS=\"linux\"\n            case \"$(command uname -m)\" in\n                amd64|x86_64) arch=\"x86_64\";;\n                aarch64*) arch=\"arm64\";;\n                armv8*) arch=\"arm64\";;\n                *) die \"kitty binaries not available for architecture $(command uname -m)\";;\n            esac\n            ;;\n        *) die \"kitty binaries are not available for $(command uname)\"\n    esac\n}\n\nexpand_tilde() {\n    tilde_less=\"${1#\\~/}\"\n    [ \"$1\" != \"$tilde_less\" ] && tilde_less=\"$HOME/$tilde_less\"\n    printf '%s' \"$tilde_less\"\n}\n\nparse_args() {\n    dest='~/.local'\n    [ \"$OS\" = \"macos\" ] && dest=\"/Applications\"\n    launch='y'\n    installer=''\n    while :; do\n        case \"$1\" in\n            dest=*) dest=\"${1#*=}\";;\n            launch=*) launch=\"${1#*=}\";;\n            installer=*) installer=\"${1#*=}\";;\n            \"\") break;;\n            *) die \"Unrecognized command line option: $1\";;\n        esac\n        shift\n    done\n    dest=$(expand_tilde \"${dest}\")\n    [ \"$launch\" != \"y\" -a \"$launch\" != \"n\" ] && die \"Unrecognized command line option: launch=$launch\"\n    dest=\"$dest/kitty.app\"\n}\n\n\nget_file_url() {\n    url=\"https://github.com/kovidgoyal/kitty/releases/download/$1/kitty-$2\"\n    if [ \"$OS\" = \"macos\" ]; then\n        url=\"$url.dmg\"\n    else\n        url=\"$url-$arch.txz\"\n    fi\n}\n\nget_release_url() {\n    release_version=$(fetch_quiet \"https://sw.kovidgoyal.net/kitty/current-version.txt\")\n    [ $? -ne 0 -o -z \"$release_version\" ] && die \"Could not get kitty latest release version\"\n    get_file_url \"v$release_version\" \"$release_version\"\n}\n\nget_version_url() {\n    get_file_url \"v$1\" \"$1\"\n}\n\nget_nightly_url() {\n    get_file_url \"nightly\" \"nightly\"\n}\n\nget_download_url() {\n    installer_is_file=\"n\"\n    case \"$installer\" in\n        \"nightly\") get_nightly_url ;;\n        \"\") get_release_url ;;\n        version-*) get_version_url \"${installer#*-}\";;\n        *) installer_is_file=\"y\" ;;\n    esac\n}\n\ndownload_installer() {\n    tdir=$(command mktemp -d \"/tmp/kitty-install-XXXXXXXXXXXX\")\n    [ \"$installer_is_file\" != \"y\" ] && {\n        printf '%s\\n\\n' \"Downloading from: $url\"\n        if [ \"$OS\" = \"macos\" ]; then\n            installer=\"$tdir/kitty.dmg\"\n        else\n            installer=\"$tdir/kitty.txz\"\n        fi\n        fetch \"$url\" > \"$installer\" || die \"Failed to download: $url\"\n        installer_is_file=\"y\"\n    }\n}\n\nensure_dest() {\n    printf \"%s\\n\" \"Installing to $dest\"\n    command rm -rf \"$dest\" || die \"Failed to delete $dest\"\n    command mkdir -p \"$dest\" || die \"Failed to mkdir -p $dest\"\n    command rm -rf \"$dest\" || die \"Failed to delete $dest\"\n}\n\nlinux_install() {\n    command mkdir \"$tdir/mp\"\n    command tar -C \"$tdir/mp\" \"-xJof\" \"$installer\" || die \"Failed to extract kitty tarball\"\n    ensure_dest\n    command mv \"$tdir/mp\" \"$dest\" || die \"Failed to move kitty.app to $dest\"\n}\n\nmacos_install() {\n    command mkdir \"$tdir/mp\"\n    command hdiutil attach \"$installer\" \"-mountpoint\" \"$tdir/mp\" || die \"Failed to mount kitty.dmg\"\n    ensure_dest\n    command ditto -v \"$tdir/mp/kitty.app\" \"$dest\"\n    rc=\"$?\"\n    command hdiutil detach \"$tdir/mp\"\n    [ \"$rc\" != \"0\" ] && die \"Failed to copy kitty.app from mounted dmg\"\n}\n\nexec_kitty() {\n    if [ \"$OS\" = \"macos\" ]; then\n        exec \"open\" \"$dest\"\n    else\n        exec \"$dest/bin/kitty\" \"--detach\"\n    fi\n    die \"Failed to launch kitty\"\n}\n\nmain() {\n    detect_os\n    parse_args \"$@\"\n    detect_network_tool\n    get_download_url\n    download_installer\n    if [ \"$OS\" = \"macos\" ]; then\n        macos_install\n    else\n        linux_install\n    fi\n    cleanup\n    [ \"$launch\" = \"y\" ] && exec_kitty\n    exit 0\n}\n\nmain \"$@\"\n"
  },
  {
    "path": "docs/integrations.rst",
    "content": ":tocdepth: 2\n\nIntegrations with other tools\n================================\n\nkitty provides extremely powerful interfaces such as :doc:`remote-control` and\n:doc:`kittens/custom` and :doc:`kittens/icat` that allow it to be integrated\nwith other tools seamlessly.\n\n\nImage and document viewers\n----------------------------\n\nPowered by kitty's :doc:`graphics-protocol` there exist many tools for viewing\nimages and other types of documents directly in your terminal, even over SSH.\n\n.. _tool_bookokrat:\n\n`bookokrat <https://github.com/bugzmanov/bookokrat>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA terminal PDF/EPUB viewer\n\n.. _tool_termpdf:\n\n`termpdf.py <https://github.com/dsanson/termpdf.py>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA terminal PDF/DJVU/CBR viewer\n\n.. _tool_tdf:\n\n`tdf <https://github.com/itsjunetime/tdf>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA terminal PDF viewer\n\n.. _tool_fancy_cat:\n\n`fancy-cat <https://github.com/freref/fancy-cat>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA terminal PDF viewer\n\n.. _tool_meowpdf:\n\n`meowpdf <https://github.com/monoamine11231/meowpdf>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA terminal PDF viewer with GUI-like usage and Vim-like keybindings written in Rust\n\n.. _tool_mcat:\n\n`mcat <https://github.com/Skardyy/mcat>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nDisplay various types of files nicely formatted with images in the terminal\n\n`dawn <https://github.com/andrewmd5/dawn>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nA markdown editor that uses the text-sizing protocol for large headings and\nthe graphics protocol for images.\n\n.. _tool_presentterm:\n\n`presenterm <https://github.com/mfontanini/presenterm>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nShow markdown based slides with images in your terminal, powered by the\nkitty graphics protocol.\n\n.. _tool_mdfried:\n\n`mdfried <https://github.com/benjajaja/mdfried>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nMarkdown viewer that can render big headers with the text-sizing-protocol, and\nalso render images with the kitty graphics protocol.\n\n.. _tool_term_image:\n\n`term-image <https://github.com/AnonymouX47/term-image>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nTool to browse images in a terminal using kitty's graphics protocol.\n\n.. _tool_koneko:\n\n`koneko <https://github.com/twenty5151/koneko>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nBrowse images from the pixiv artist community directly in kitty.\n\n.. _tool_viu:\n\n`viu <https://github.com/atanunq/viu>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nView images in the terminal, similar to kitty's icat.\n\n.. _tool_nb:\n\n\n`nb <https://github.com/xwmx/nb>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nCommand line and local web note-taking, bookmarking, archiving, and knowledge\nbase application that uses kitty's graphics protocol for images.\n\n.. _tool_w3m:\n\n`w3m <https://github.com/tats/w3m>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA text mode WWW browser that supports kitty's graphics protocol to display\nimages.\n\n.. _tool_awrit:\n\n`awrit <https://github.com/chase/awrit>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA full Chromium based web browser running in the terminal using kitty's\ngraphics protocol.\n\n.. _tool_chawan:\n\n`chawan <https://sr.ht/~bptato/chawan/>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA text mode WWW browser that supports kitty's graphics protocol to display\nimages.\n\n.. _tool_mpv:\n\n`mpv <https://github.com/mpv-player/mpv/commit/874e28f4a41a916bb567a882063dd2589e9234e1>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA video player that can play videos in the terminal.\n\n.. code-block:: sh\n\n    mpv --profile=sw-fast --vo=kitty --vo-kitty-use-shm=yes --really-quiet video.mkv\n\n.. _tool_timg:\n\n`timg <https://github.com/hzeller/timg>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA terminal image and video viewer, that displays static and animated images or\nplays videos. Fast multi-threaded loading, JPEG exif rotation, grid view and\nconnecting to the webcam make it a versatile terminal utility.\n\n\nFile managers\n-------------------\n.. _tool_ranger:\n\n`ranger <https://github.com/ranger/ranger>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA terminal file manager, with previews of file contents powered by kitty's\ngraphics protocol.\n\n.. _tool_nnn:\n\n`nnn <https://github.com/jarun/nnn/>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAnother terminal file manager, with previews of file contents powered by kitty's\ngraphics protocol.\n\n.. _tool_yazi:\n\n`Yazi <https://github.com/sxyazi/yazi>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nBlazing fast terminal file manager, with built-in kitty graphics protocol support\n(implemented both Classic protocol and Unicode placeholders).\n\n.. _tool_clifm:\n\n`clifm <https://github.com/leo-arch/clifm>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nThe shell-like, command line terminal file manager, uses the kitty graphics and\nkeyboard protocols.\n\n.. _tool_hunter:\n\n`hunter <https://github.com/rabite0/hunter>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAnother terminal file manager, with previews of file contents powered by kitty's\ngraphics protocol.\n\n.. _tool_far2l:\n\n`far2l <https://github.com/elfmz/far2l>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nText-mode dual panel (orthodox) file manager and also terminal emulator, uses\nthe kitty graphics and keyboard protocols (both as client and as terminal)\n\n\nSystem and data visualisation tools\n---------------------------------------\n\n.. _tool_neofetch:\n\n`neofetch <https://github.com/dylanaraps/neofetch>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA command line system information tool that shows images using kitty's graphics\nprotocol\n\n.. _tool_matplotlib:\n\nmatplotlib\n^^^^^^^^^^^^^^\n\nThere exist multiple backends for matplotlib to draw images directly in kitty.\n\n* `matplotlib-backend-kitty <https://github.com/jktr/matplotlib-backend-kitty>`__\n* `kitcat <https://github.com/mil-ad/kitcat>`__\n\n.. _tool_KittyTerminalImage:\n\n`KittyTerminalImages.jl <https://github.com/simonschoelly/KittyTerminalImages.jl>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nShow images from Julia directly in kitty\n\n.. _tool_euporie:\n\n`euporie <https://github.com/joouha/euporie>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA text-based user interface for running and editing Jupyter notebooks, powered\nby kitty's graphics protocol for displaying plots\n\n.. _tool_gnuplot:\n\n`gnuplot <http://www.gnuplot.info/>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nA graphing and data visualization tool that has support for the kitty graphics\nprotocol, with its ``kittygd`` and ``kittycairo`` backends.\n\n.. _tool_k-nine:\n\n`k-nine <https://github.com/talwrii/kitty-plotnine>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nA wrapper around the :code:`plotnine` library which lets you plot data from the command-line with bash one-liners.\n\n.. tool_tgutui:\n\n`tgutui <https://github.com/tgu-ltd/tgutui>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA Terminal Operating Test hardware equipment\n\n.. tool_onefetch:\n\n`onefetch <https://github.com/o2sh/onefetch>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA tool to fetch information about your git repositories\n\n.. tool_patat:\n\n`patat <https://github.com/jaspervdj/patat>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nTerminal based presentations using pandoc and kitty's image protocol for\nimages\n\n.. tool_wttr:\n\n`wttr.in <https://github.com/chubin/wttr.in>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA tool to display weather information in your terminal with curl\n\n.. tool_wl_clipboard:\n\n`wl-clipboard-manager <https://github.com/maximbaz/wl-clipboard-manager>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nView and manage the system clipboard under Wayland in your kitty terminal\n\n.. tool_nemu:\n\n`NEMU <https://github.com/nemuTUI/nemu>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nTUI for QEMU used to manage virtual machines, can display the Virtual Machine\nin the terminal using the kitty graphics protocol.\n\n\nEditor integration\n-----------------------\n\n|kitty| can be integrated into many different terminal based text editors to add\nfeatures such a split windows, previews, REPLs etc.\n\n.. tool_kakoune:\n\n`kakoune <https://kakoune.org/>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nIntegrates with kitty to use native kitty windows for its windows/panels and\nREPLs.\n\n.. tool_vim_slime:\n\n`vim-slime <https://github.com/jpalardy/vim-slime#kitty>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nUses kitty remote control for a Lisp REPL.\n\n.. tool_vim_kitty_navigator:\n\n`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAllows you to navigate seamlessly between vim and kitty splits using a\nconsistent set of hotkeys.\n\n.. tool_vim_test:\n\n`vim-test <https://github.com/vim-test/vim-test>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAllows easily running tests in a terminal window\n\n.. tool_nvim_image_viewers:\n\nVarious image viewing plugins for editors\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n* `snacks.nvim <https://github.com/folke/snacks.nvim>`__ - Enables seamless inline images in various file formats within nvim\n* `image.nvim <https://github.com/3rd/image.nvim>`_ - Bringing images to neovim\n* `image_preview.nvim <https://github.com/adelarsq/image_preview.nvim/>`_ - Image preview for neovim\n* `hologram.nvim <https://github.com/edluffy/hologram.nvim>`_  - view images inside nvim\n* `kitty-graphics.el <https://github.com/cashmeredev/kitty-graphics.el>`_ - view images in emacs\n\nScrollback manipulation\n-------------------------\n\n.. tool_kitty_scrollback_nvim:\n\n`kitty-scrollback.nvim <https://github.com/mikesmithgh/kitty-scrollback.nvim>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nBrowse the scrollback buffer with Neovim, with simple key actions for efficient\ncopy/paste and even execution of commands.\n\n.. tool_kitty_search:\n\n`kitty-search <https://github.com/trygveaa/kitty-kitten-search>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nLive incremental search of the scrollback buffer.\n\n.. tool_kitty_grab:\n\n`kitty-grab <https://github.com/yurikhan/kitty_grab>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nKeyboard based text selection for the kitty scrollback buffer.\n\nDesktop panels\n-------------------------\n\n`kitty panel <https://github.com/5hubham5ingh/kitty-panel>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA system panel for Kitty terminal that displays real-time system metrics using terminal-based utilities.\n\n\n`pawbar <https://github.com/codelif/pawbar>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA kitten-panel based desktop panel for your desktop\n\nPassword managers\n---------------------\n\n`1password <https://github.com/mm-zacharydavison/kitty-kitten-1password>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAllow injecting passwords from 1Password into kitty.\n\n`BitWarden <https://github.com/dnanhkhoa/kitty-password-manager>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nInject passwords from BitWarden into kitty\n\nMiscellaneous\n------------------\n\n.. tool_doom:\n\nDOOM\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nPlay the classic shooter DOOM in `kitty <https://github.com/cryptocode/terminal-doom>`__ or even inside `neovim inside kitty\n<https://github.com/seandewar/actually-doom.nvim>`__.\n\n.. tool_gattino:\n\n`gattino <https://github.com/salvozappa/gattino>`__\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIntegrate kitty with an LLM to convert plain language prompts into shell\ncommands.\n\n.. tool_kitty_smart_tab:\n\n`kitty-smart-tab <https://github.com/yurikhan/kitty-smart-tab>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nUse keys to either control tabs or pass them onto running applications if no\ntabs are present\n\n.. tool_kitty_smart_scroll:\n\n`kitty-smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nUse keys to either scroll or pass them onto running applications if no\nscrollback buffer is present\n\n.. tool_kitti3:\n\n`kitti3 <https://github.com/LandingEllipse/kitti3>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAllow using kitty as a drop-down terminal under the i3 window manager\n\n.. tool_weechat_hints:\n\n`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nURL hints kitten for WeeChat that works without having to use WeeChat's\nraw-mode.\n\n.. tool_glkitty:\n\n`glkitty <https://github.com/michaeljclark/glkitty>`_\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nC library to draw OpenGL shaders in the terminal with a glgears demo\n"
  },
  {
    "path": "docs/intro_vid.rst",
    "content": ".. raw:: html\n\n    <div id=\"intro-video-container\" class=\"video-with-timestamps\">\n\n    <video controls width=\"640\" height=\"360\" poster=\"_static/poster.png\">\n        <source src=\"https://download.calibre-ebook.com/videos/kitty.mp4\" type=\"video/mp4\">\n        <source src=\"https://download.calibre-ebook.com/videos/kitty.webm\" type=\"video/webm\">\n    </video>\n\n.. rst-class:: caption caption-text\n\n    Watch kitty in action!\n\nTimestamps for the above video:\n\n00:00\n    Intro\n00:39\n    Pager: View command output in same window: :kbd:`Ctrl+Shift+g`\n01:43\n    Pager: View command output in a separate window\n02:14\n    Pager: Uses shell integration in kitty\n02:27\n    Tab text: The output of cwd and last cmd\n03:03\n    Open files from ls output with mouse: :kbd:`Ctrl+Shift+Right-click`\n04:04\n    Open files from ls output with keyboard: :kbd:`Ctrl+Shift+P>y`\n04:26\n    Open files on click: ``ls --hyperlink=auto``\n05:03\n    Open files on click: Filetype settings in open-actions.conf\n05:45\n    hyperlinked-grep kitten: Open grep output in editor\n07:18\n    Remote-file kitten: View remote files locally\n08:31\n    Remote-file kitten: Edit remote files locally\n10:01\n    icat kitten: View images directly\n10:36\n    icat kitten: Download & display image/gif from internet\n11:03\n    Kitty Graphics Protocol: Live image preview in ranger\n11:25\n    icat kitten: Display image from remote server\n12:04\n    unicode-input kitten: Emojis in terminal\n12:54\n    Windows: Intro\n13:36\n    Windows: Switch focus: :kbd:`Ctrl+Shift+win_nr`\n13:48\n    Windows: Visual selection: :kbd:`Ctrl+Shift+F7`\n13:58\n    Windows: Simultaneous input\n14:15\n    Interactive Kitty Shell: :kbd:`Ctrl+Shift+Esc`\n14:36\n    Broadcast text: ``launch --allow-remote-control kitten broadcast``\n15:18\n    Kitty Remote Control Protocol\n15:52\n    Interactive Kitty Shell: Help\n16:34\n    Choose theme interactively: ``kitten themes -h``\n17:23\n    Choose theme by name: ``kitten themes [options] [theme_name]``\n\n.. raw:: html\n\n    </div>\n"
  },
  {
    "path": "docs/invocation.rst",
    "content": ":orphan:\n\nThe kitty command line interface\n====================================\n\n.. program:: kitty\n\n.. include:: generated/cli-kitty.rst\n\n.. include:: basic.rst\n\nSee also\n-----------\n\nSee kitty.conf(5)\n"
  },
  {
    "path": "docs/keyboard-protocol.rst",
    "content": "Comprehensive keyboard handling in terminals\n==============================================\n\nThere are various problems with the current state of keyboard handling in\nterminals. They include:\n\n* No way to use modifiers other than ``ctrl`` and ``alt``\n\n* No way to reliably use multiple modifier keys, other than, ``shift+alt`` and\n  ``ctrl+alt``.\n\n* Many of the existing escape codes used to encode these events are ambiguous\n  with different key presses mapping to the same escape code.\n\n* No way to handle different types of keyboard events, such as press, release or repeat\n\n* No reliable way to distinguish single ``Esc`` key presses from the start of a\n  escape sequence. Currently, client programs use fragile timing related hacks\n  for this, leading to bugs, for example:\n  `neovim #2035 <https://github.com/neovim/neovim/issues/2035>`_.\n\nTo solve these issues and others, kitty has created a new keyboard protocol,\nthat is backward compatible but allows applications to opt-in to support more\nadvanced usages. The protocol is based on initial work in `fixterms\n<http://www.leonerd.org.uk/hacks/fixterms/>`_, however, it corrects various\nissues in that proposal, listed at the :ref:`bottom of this document\n<fixterms_bugs>`. For public discussion of this spec, see :iss:`3248`.\n\nYou can see this protocol with all enhancements in action by running::\n\n    kitten show-key -m kitty\n\ninside the kitty terminal to report key events.\n\nIn addition to kitty, this protocol is also implemented in:\n\n* The `alacritty terminal <https://github.com/alacritty/alacritty/pull/7125>`__\n* The `foot terminal <https://codeberg.org/dnkl/foot/issues/319>`__\n* The `ghostty terminal <https://ghostty.org>`__\n* The `iTerm2 terminal <https://gitlab.com/gnachman/iterm2/-/issues/10017>`__\n* The `Microsoft terminal <https://github.com/microsoft/terminal/pull/19817>`__\n* The `rio terminal <https://github.com/raphamorim/rio/commit/cd463ca37677a0fc48daa8795ea46dadc92b1e95>`__\n* The `TuiOS terminal (multiplexer) <https://github.com/Gaurav-Gosain/tuios/issues/26>`__\n* The `Warp terminal <https://github.com/warpdotdev/Warp/issues/8462#issuecomment-3857779488>`__\n* The `WezTerm terminal <https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html>`__\n* The `xterm.js terminal <https://github.com/xtermjs/xterm.js/pull/5600>`__\n\nLibraries implementing this protocol:\n\n* The `notcurses library <https://github.com/dankamongmen/notcurses/issues/2131>`__\n* The `crossterm library <https://github.com/crossterm-rs/crossterm/pull/688>`__\n* The `textual library <https://github.com/Textualize/textual/pull/4631>`__\n* The vaxis library `go <https://sr.ht/~rockorager/vaxis/>`__ and `zig <https://github.com/rockorager/libvaxis/>`__\n* The `bubbletea library <https://github.com/charmbracelet/bubbletea/issues/869>`__\n\nPrograms implementing this protocol:\n\n* The `Vim text editor <https://github.com/vim/vim/commit/63a2e360cca2c70ab0a85d14771d3259d4b3aafa>`__\n* The `Emacs text editor via the kkp package <https://github.com/benjaminor/kkp>`__\n* The `Neovim text editor <https://github.com/neovim/neovim/pull/18181>`__\n* The `kakoune text editor <https://github.com/mawww/kakoune/issues/4103>`__\n* The `dte text editor <https://gitlab.com/craigbarnes/dte/-/issues/138>`__\n* The `Helix text editor <https://github.com/helix-editor/helix/pull/4939>`__\n* The `Flow control editor <https://github.com/neurocyte/flow?tab=readme-ov-file#requirements>`__\n* The `far2l file manager <https://github.com/elfmz/far2l/commit/e1f2ee0ef2b8332e5fa3ad7f2e4afefe7c96fc3b>`__\n* The `Yazi file manager <https://github.com/sxyazi/yazi>`__\n* The `awrit web browser <https://github.com/chase/awrit>`__\n* The `Turbo Vision <https://github.com/magiblot/tvision/commit/6e5a7b46c6634079feb2ac98f0b890bbed59f1ba>`__/`Free Vision <https://gitlab.com/freepascal.org/fpc/source/-/issues/40673#note_2061428120>`__ IDEs\n* The `aerc email client <https://git.sr.ht/~rjarry/aerc/commit/d73cf33c2c6c3e564ce8aff04acc329a06eafc54>`__\n\nShells implementing this protocol:\n\n* The `nushell shell <https://github.com/nushell/nushell/pull/10540>`__\n* The `fish shell <https://github.com/fish-shell/fish-shell/commit/8bf8b10f685d964101f491b9cc3da04117a308b4>`__\n\n.. versionadded:: 0.20.0\n\nQuickstart\n---------------\n\nIf you are an application or library developer just interested in using this\nprotocol to make keyboard handling simpler and more robust in your application,\nwithout too many changes, do the following:\n\n#. Emit the escape code ``CSI > 1 u`` at application startup if using the main\n   screen or when entering alternate screen mode, if using the alternate\n   screen.\n#. All key events will now be sent in only a few forms to your application,\n   that are easy to parse unambiguously.\n#. Emit the escape sequence ``CSI < u`` at application exit if using the main\n   screen or just before leaving alternate screen mode if using the alternate screen,\n   to restore whatever the keyboard mode was before step 1.\n\nKey events will all be delivered to your application either as plain UTF-8\ntext, or using the following escape codes, for those keys that do not produce\ntext (``CSI`` is the bytes ``0x1b 0x5b``)::\n\n    CSI number ; modifiers [u~]\n    CSI 1; modifiers [ABCDEFHPQS]\n    0x0d - for the Enter key\n    0x7f or 0x08 - for Backspace\n    0x09 - for Tab\n\nThe ``number`` in the first form above will be either the Unicode codepoint for a\nkey, such as ``97`` for the :kbd:`a` key, or one of the numbers from the\n:ref:`functional` table below. The ``modifiers`` optional parameter encodes any\nmodifiers active for the key event. The encoding is described in the\n:ref:`modifiers` section.\n\nThe second form is used for a few functional keys, such as the :kbd:`Home`,\n:kbd:`End`, :kbd:`Arrow` keys and :kbd:`F1` ... :kbd:`F4`, they are enumerated in\nthe :ref:`functional` table below.  Note that if no modifiers are present the\nparameters are omitted entirely giving an escape code of the form ``CSI\n[ABCDEFHPQS]``.\n\nIf you want support for more advanced features such as repeat and release\nevents, alternate keys for shortcut matching et cetera, these can be turned on\nusing :ref:`progressive_enhancement` as documented in the rest of this\nspecification.\n\nAn overview\n------------------\n\nKey events are divided into two types, those that produce text and those that\ndo not. When a key event produces text, the text is sent directly as UTF-8\nencoded bytes. This is safe as UTF-8 contains no C0 control codes.\nWhen the key event does not have text, the key event is encoded as an escape code. In\nlegacy compatibility mode (the default) this uses legacy escape codes, so old terminal\napplications continue to work. For more advanced features, such as release/repeat\nreporting etc., applications can tell the terminal they want this information by\nsending an escape code to :ref:`progressively enhance <progressive_enhancement>` the data reported for\nkey events.\n\nThe central escape code used to encode key events is::\n\n    CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u\n\nSpaces in the above definition are present for clarity and should be ignored.\n``CSI`` is the bytes ``0x1b 0x5b``. All parameters are decimal numbers. Fields\nare separated by the semi-colon and sub-fields by the colon. Only the\n``unicode-key-code`` field is mandatory, everything else is optional. The\nescape code is terminated by the ``u`` character (the byte ``0x75``).\n\n\n.. _key_codes:\n\nKey codes\n~~~~~~~~~~~~~~\n\nThe ``unicode-key-code`` above is the Unicode codepoint representing the key, as a\ndecimal number. For example, the :kbd:`A` key is represented as ``97`` which is\nthe unicode code for lowercase ``a``. Note that the codepoint used is *always*\nthe lower-case (or more technically, un-shifted) version of the key. If the\nuser presses, for example, :kbd:`ctrl+shift+a` the escape code would be ``CSI\n97;modifiers u``. It *must not* be ``CSI 65; modifiers u``.\n\nIf *alternate key reporting* is requested by the program running in the\nterminal, the terminal can send two additional Unicode codepoints, the *shifted\nkey* and *base layout key*, separated by colons. The shifted key is simply the\nupper-case version of ``unicode-codepoint``, or more technically, the shifted\nversion, in the currently active keyboard layout. So `a` becomes `A` and so on,\nbased on the current keyboard layout. This is needed to be able to match\nagainst a shortcut such as :kbd:`ctrl+plus` which depending on the type of\nkeyboard could be either :kbd:`ctrl+shift+equal` or :kbd:`ctrl+plus`. Note that\nthe shifted key must be present only if shift is also present in the modifiers.\n\nThe *base layout key* is the key corresponding to the physical key in the\nstandard PC-101 key layout. So for example, if the user is using a Cyrillic\nkeyboard with a Cyrillic keyboard layout pressing the :kbd:`ctrl+С` key will\nbe :kbd:`ctrl+c` in the standard layout. So the terminal should send the *base\nlayout key* as ``99`` corresponding to the ``c`` key.\n\nIf only one alternate key is present, it is the *shifted key*. If the terminal\nwants to send only a base layout key but no shifted key, it must use an empty\nsub-field for the shifted key, like this::\n\n  CSI unicode-key-code::base-layout-key\n\n\n.. _modifiers:\n\nModifiers\n~~~~~~~~~~~~~~\n\nThis protocol supports six modifier keys, :kbd:`shift`, :kbd:`alt`,\n:kbd:`ctrl`, :kbd:`super`, :kbd:`hyper`, :kbd:`meta`, :kbd:`num_lock` and\n:kbd:`caps_lock`. Here :kbd:`super` is either the *Windows/Linux* key or the\n:kbd:`command` key on mac keyboards. The :kbd:`alt` key is the :kbd:`option`\nkey on mac keyboards. :kbd:`hyper` and :kbd:`meta` are typically present only\non X11/Wayland based systems with special XKB rules. Modifiers are encoded as a\nbit field with::\n\n    shift     0b1         (1)\n    alt       0b10        (2)\n    ctrl      0b100       (4)\n    super     0b1000      (8)\n    hyper     0b10000     (16)\n    meta      0b100000    (32)\n    caps_lock 0b1000000   (64)\n    num_lock  0b10000000  (128)\n\nIn the escape code, the modifier value is encoded as a decimal number which is\n``1 + actual modifiers``. So to represent :kbd:`shift` only, the value would be\n``1 + 1 = 2``, to represent :kbd:`ctrl+shift` the value would be ``1 + 0b101 =\n6`` and so on. If the modifier field is not present in the escape code, its\ndefault value is ``1`` which means no modifiers. If a modifier is *active* when\nthe key event occurs, i.e. if the key is pressed or the lock (for caps lock/num\nlock) is enabled, the key event must have the bit for that modifier set.\n\nWhen the key event is related to an actual modifier key, the corresponding\nmodifier's bit must be set to the modifier state including the effect for the\ncurrent event. For example, when pressing the :kbd:`LEFT_CONTROL` key, the\n``ctrl`` bit must be set and when releasing it, it must be reset. When both\nleft and right control keys are pressed and one is released, the release event\nmust have the ``ctrl`` bit set. See :iss:`6913` for discussion of this design.\n\n.. _event_types:\n\nEvent types\n~~~~~~~~~~~~~~~~\n\nThere are three key event types: ``press, repeat and release``. They are\nreported (if requested ``0b10``) as a sub-field of the modifiers field\n(separated by a colon). If no modifiers are present, the modifiers field must\nhave the value ``1`` and the event type sub-field the type of event. The\n``press`` event type has value ``1`` and is the default if no event type sub\nfield is present. The ``repeat`` type is ``2`` and the ``release`` type is\n``3``. So for example::\n\n    CSI key-code             # this is a press event\n    CSI key-code;modifier    # this is a press event\n    CSI key-code;modifier:1  # this is a press event\n    CSI key-code;modifier:2  # this is a repeat event\n    CSI key-code;modifier:3  # this is a release event\n\n\n.. note:: Key events that result in text are reported as plain UTF-8 text, so\n   events are not supported for them, unless the application requests *key\n   report mode*, see below.\n\n.. _text_as_codepoints:\n\nText as code points\n~~~~~~~~~~~~~~~~~~~~~\n\nThe terminal can optionally send the text associated with key events as a\nsequence of Unicode code points. This behavior is opt-in by the :ref:`progressive\nenhancement <progressive_enhancement>` mechanism described below. Some examples::\n\n    shift+a -> CSI 97 ; 2 ; 65 u   # The text 'A' is reported as 65\n    alt+a   -> CSI  0 ;   ; 229 u  # The text 'å' is reported as 229\n\nIf multiple code points are present, they must be separated by colons.  If no\nknown key is associated with the text the key number ``0`` must be used. The\nassociated text must not contain control codes (control codes are code points\nbelow U+0020 and codepoints in the C0 and C1 blocks). In the above example, the\n:kbd:`alt` modifier is consumed by the OS itself to produce the text å and not\nsent to the terminal emulator, which gets only a \"text input\" event and no\ninformation about modifiers, thus the event gets encoded with no modifiers.\nThe exact behavior in these situations depends on the OS, keyboard layout, IME\nsystem in use and so on. In general, if the terminal emulator receives no key\ninformation, the key number 0 must be used to indicate a pure \"text event\".\n\n\nNon-Unicode keys\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are many keys that don't correspond to letters from human languages, and\nthus aren't represented in Unicode. Think of functional keys, such as\n:kbd:`Escape`, :kbd:`Play`, :kbd:`Pause`, :kbd:`F1`, :kbd:`Home`, etc. These\nare encoded using Unicode code points from the Private Use Area (``57344 -\n63743``). The mapping of key names to code points for these keys is in the\n:ref:`Functional key definition table below <functional>`.\n\n\n.. _progressive_enhancement:\n\nProgressive enhancement\n--------------------------\n\nWhile, in theory, every key event could be completely represented by this\nprotocol and all would be hunk-dory, in reality there is a vast universe of\nexisting terminal programs that expect legacy control codes for key events and\nthat are not likely to ever be updated. To support these, in default mode,\nthe terminal will emit legacy escape codes for compatibility. If a terminal\nprogram wants more robust key handling, it can request it from the terminal,\nvia the mechanism described here. Each enhancement is described in detail\nbelow. The escape code for requesting enhancements is::\n\n    CSI = flags ; mode u\n\nHere ``flags`` is a decimal encoded integer to specify a set of bit-flags. The\nmeanings of the flags are given below. The second, ``mode`` parameter is\noptional (defaulting to ``1``) and specifies how the flags are applied.\nThe value ``1`` means all set bits are set and all unset bits are reset.\nThe value ``2`` means all set bits are set, unset bits are left unchanged.\nThe value ``3`` means all set bits are reset, unset bits are left unchanged.\n\n.. csv-table:: The progressive enhancement flags\n   :header: \"Bit\", \"Meaning\"\n\n   \"0b1 (1)\", \":ref:`disambiguate`\"\n   \"0b10 (2)\", \":ref:`report_events`\"\n   \"0b100 (4)\", \":ref:`report_alternates`\"\n   \"0b1000 (8)\", \":ref:`report_all_keys`\"\n   \"0b10000 (16)\", \":ref:`report_text`\"\n\nThe program running in the terminal can query the terminal for the\ncurrent values of the flags by sending::\n\n    CSI ? u\n\nThe terminal will reply with::\n\n    CSI ? flags u\n\nThe program can also push/pop the current flags onto a stack in the\nterminal with::\n\n    CSI > flags u  # for push, if flags omitted default to zero\n    CSI < number u # to pop number entries, defaulting to 1 if unspecified\n\nTerminals should limit the size of the stack as appropriate, to prevent\nDenial-of-Service attacks. Terminals must maintain separate stacks for the main\nand alternate screens. If a pop request is received that empties the stack,\nall flags are reset. If a push request is received and the stack is full, the\noldest entry from the stack must be evicted.\n\n.. note:: The main and alternate screens in the terminal emulator must maintain\n   their own, independent, keyboard mode stacks. This is so that a program that\n   uses the alternate screen such as an editor, can change the keyboard mode\n   in the alternate screen only, without affecting the mode in the main screen\n   or even knowing what that mode is. Without this, and if no stack is\n   implemented for keyboard modes (such as in some legacy terminal emulators)\n   the editor would have to somehow know what the keyboard mode of the main\n   screen is and restore to that mode on exit.\n\n.. _disambiguate:\n\nDisambiguate escape codes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis type of progressive enhancement (``0b1``) fixes the problem of some legacy key press\nencodings overlapping with other control codes. For instance, pressing the\n:kbd:`Esc` key generates the byte ``0x1b`` which also is used to indicate the\nstart of an escape code. Similarly pressing the key :kbd:`alt+[` will generate\nthe bytes used for CSI control codes.\n\nTurning on this flag will cause the terminal to report the :kbd:`Esc`, :kbd:`alt+key`,\n:kbd:`ctrl+key`, :kbd:`ctrl+alt+key`, :kbd:`shift+alt+key` keys using ``CSI u`` sequences instead\nof legacy ones. Here key is any ASCII key as described in :ref:`legacy_text`.\nAdditionally, all non text keypad keys will be reported as separate keys with ``CSI u``\nencoding, using dedicated numbers from the :ref:`table below <functional>`.\n\nWith this flag turned on, all key events that do not generate text are\nrepresented in one of the following two forms::\n\n    CSI number; modifier u\n    CSI 1; modifier [~ABCDEFHPQS]\n\nThis makes it very easy to parse key events in an application. In particular,\n:kbd:`ctrl+c` will no longer generate the ``SIGINT`` signal, but instead be\ndelivered as a ``CSI u`` escape code. This has the nice side effect of making it\nmuch easier to integrate into the application event loop. The only exceptions\nare the :kbd:`Enter`, :kbd:`Tab` and :kbd:`Backspace` keys which still generate the same\nbytes as in legacy mode this is to allow the user to type and execute commands\nin the shell such as ``reset`` after a program that sets this mode crashes\nwithout clearing it. Note that the Lock modifiers are not reported for text\nproducing keys, to keep them usable in legacy programs. To get lock modifiers\nfor all keys use the :ref:`report_all_keys` enhancement.\n\n.. _report_events:\n\nReport event types\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis progressive enhancement (``0b10``) causes the terminal to report key repeat\nand key release events. Normally only key press events are reported and key\nrepeat events are treated as key press events. See :ref:`event_types` for\ndetails on how these are reported.\n\n.. note::\n\n   The :kbd:`Enter`, :kbd:`Tab` and :kbd:`Backspace` keys will not have release\n   events unless :ref:`report_all_keys` is also set, so that the user can still\n   type reset at a shell prompt when a program that sets this mode ends without\n   resetting it.\n\n.. _report_alternates:\n\nReport alternate keys\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis progressive enhancement (``0b100``) causes the terminal to report\nalternate key values *in addition* to the main value, to aid in shortcut\nmatching. See :ref:`key_codes` for details on how these are reported. Note that\nthis flag is a pure enhancement to the form of the escape code used to\nrepresent key events, only key events represented as escape codes due to the\nother enhancements in effect will be affected by this enhancement. In other\nwords, only if a key event was already going to be represented as an escape\ncode due to one of the other enhancements will this enhancement affect it.\n\n.. _report_all_keys:\n\nReport all keys as escape codes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nKey events that generate text, such as plain key presses without modifiers,\nresult in just the text being sent, in the legacy protocol. There is no way to\nbe notified of key repeat/release events. These types of events are needed for\nsome applications, such as games (think of movement using the ``WASD`` keys).\n\nThis progressive enhancement (``0b1000``) turns on key reporting even for key\nevents that generate text. When it is enabled, text will not be sent, instead\nonly key events are sent. If the text is needed as well, combine with the\nReport associated text enhancement below.\n\nAdditionally, with this mode, events for pressing modifier keys are reported.\nNote that *all* keys are reported as escape codes, including :kbd:`Enter`,\n:kbd:`Tab`, :kbd:`Backspace` etc. Note that this enhancement implies all keys\nare automatically disambiguated as well, since they are represented in their\ncanonical escape code form.\n\n.. _report_text:\n\nReport associated text\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis progressive enhancement (``0b10000``) *additionally* causes key events that\ngenerate text to be reported as ``CSI u`` escape codes with the text embedded\nin the escape code. See :ref:`text_as_codepoints` above for details on the\nmechanism. Note that this flag is an enhancement to :ref:`report_all_keys`\nand is undefined if used without it.\n\n.. _detection:\n\nDetection of support for this protocol\n------------------------------------------\n\nAn application can query the terminal for support of this protocol by sending\nthe escape code querying for the :ref:`current progressive enhancement\n<progressive_enhancement>` status\nfollowed by request for the `primary device attributes\n<https://vt100.net/docs/vt510-rm/DA1.html>`__. If an answer for the device\nattributes is received without getting back an answer for the progressive\nenhancement the terminal does not support this protocol.\n\n.. note::\n   Terminal implementations of this protocol are **strongly** encouraged to\n   implement all progressive enhancements. It does not make sense to\n   implement only a subset. Nonetheless, there are likely to be some terminal\n   implementations that do not do so, applications can detect such\n   implementations by first setting the desired progressive enhancements and\n   then querying for the :ref:`current progressive enhancement <progressive_enhancement>`\n\nLegacy key event encoding\n--------------------------------\n\nIn the default mode, the terminal uses a legacy encoding for key events. In\nthis encoding, only key press and repeat events are sent and there is no\nway to distinguish between them. Text is sent directly as UTF-8 bytes.\n\nAny key events not described in this section are sent using the standard\n``CSI u`` encoding. This includes keys that are not encodable in the legacy\nencoding, thereby increasing the space of usable key combinations even without\nprogressive enhancement.\n\nLegacy functional keys\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nThese keys are encoded using three schemes::\n\n    CSI number ; modifier ~\n    CSI 1 ; modifier {ABCDEFHPQS}\n    SS3 {ABCDEFHPQRS}\n\nIn the above, if there are no modifiers, the modifier parameter is omitted.\nThe modifier value is encoded as described in the :ref:`modifiers` section,\nabove, except that lock keys (such as :kbd:`Num lock` and :kbd:`Caps lock`)\nare not encoded as the legacy mode has no encoding for them.\n\nWhen the second form is used, the number is always ``1`` and must be\nomitted if the modifiers field is also absent. The third form becomes the\nsecond form when modifiers are present (``SS3 is the bytes 0x1b 0x4f``).\n\nThese sequences must match entries in the terminfo database for maximum\ncompatibility. The table below lists the key, its terminfo entry name and\nthe escape code used for it by kitty. A different terminal would use whatever\nescape code is present in its terminfo database for the key.\nSome keys have an alternate representation when the terminal is in *cursor key\nmode* (the ``smkx/rmkx`` terminfo capabilities). This form is used only in\n*cursor key mode* and only when no modifiers are present.\n\n.. csv-table:: Legacy functional encoding\n   :header: \"Name\", \"Terminfo name\", \"Escape code\"\n\n    \"INSERT\",    \"kich1\",      \"CSI 2 ~\"\n    \"DELETE\",    \"kdch1\",      \"CSI 3 ~\"\n    \"PAGE_UP\",   \"kpp\",        \"CSI 5 ~\"\n    \"PAGE_DOWN\", \"knp\",        \"CSI 6 ~\"\n    \"UP\",        \"cuu1,kcuu1\", \"CSI A, SS3 A\"\n    \"DOWN\",      \"cud1,kcud1\", \"CSI B, SS3 B\"\n    \"RIGHT\",     \"cuf1,kcuf1\", \"CSI C, SS3 C\"\n    \"LEFT\",      \"cub1,kcub1\", \"CSI D, SS3 D\"\n    \"HOME\",      \"home,khome\", \"CSI H, SS3 H\"\n    \"END\",       \"-,kend\",     \"CSI F, SS3 F\"\n    \"F1\",        \"kf1\",        \"SS3 P\"\n    \"F2\",        \"kf2\",        \"SS3 Q\"\n    \"F3\",        \"kf3\",        \"SS3 R\"\n    \"F4\",        \"kf4\",        \"SS3 S\"\n    \"F5\",        \"kf5\",        \"CSI 15 ~\"\n    \"F6\",        \"kf6\",        \"CSI 17 ~\"\n    \"F7\",        \"kf7\",        \"CSI 18 ~\"\n    \"F8\",        \"kf8\",        \"CSI 19 ~\"\n    \"F9\",        \"kf9\",        \"CSI 20 ~\"\n    \"F10\",       \"kf10\",       \"CSI 21 ~\"\n    \"F11\",       \"kf11\",       \"CSI 23 ~\"\n    \"F12\",       \"kf12\",       \"CSI 24 ~\"\n    \"MENU\",      \"kf16\",       \"CSI 29 ~\"\n\nThere are a few more functional keys that have special cased legacy encodings.\nThese are present because they are commonly used and for the sake of legacy\nterminal applications that get confused when seeing CSI u escape codes:\n\n.. csv-table:: C0 controls\n    :header: \"Key\", \"No mods\", \"Ctrl\", \"Alt\", \"Shift\", \"Ctrl + Shift\", \"Alt + Shift\", \"Ctrl + Alt\"\n\n    \"Enter\",     \"0xd\",  \"0xd\",  \"0x1b 0xd\",  \"0xd\",   \"0xd\",   \"0x1b 0xd\",   \"0x1b 0xd\"\n    \"Escape\",    \"0x1b\", \"0x1b\", \"0x1b 0x1b\", \"0x1b\",  \"0x1b\",  \"0x1b 0x1b\",  \"0x1b 0x1b\"\n    \"Backspace\", \"0x7f\", \"0x8\",  \"0x1b 0x7f\", \"0x7f\",  \"0x8\",   \"0x1b 0x7f\",  \"0x1b 0x8\"\n    \"Tab\",       \"0x9\",  \"0x9\",  \"0x1b 0x9\",  \"CSI Z\", \"CSI Z\", \"0x1b CSI Z\", \"0x1b 0x9\"\n    \"Space\",     \"0x20\", \"0x0\",  \"0x1b 0x20\", \"0x20\",  \"0x0\",   \"0x1b 0x20\",  \"0x1b 0x0\"\n\nNote that :kbd:`Backspace` and :kbd:`ctrl+Backspace` are swapped in some\nterminals, this can be detected using the ``kbs`` terminfo property that\nmust correspond to the :kbd:`Backspace` key.\n\nAll keypad keys are reported as their equivalent non-keypad keys. To\ndistinguish these, use the :ref:`disambiguate <disambiguate>` flag.\n\nTerminals may choose what they want to do about functional keys that have no\nlegacy encoding. kitty chooses to encode these using ``CSI u`` encoding even in\nlegacy mode, so that they become usable even in programs that do not\nunderstand the full kitty keyboard protocol. However, terminals may instead choose to\nignore such keys in legacy mode instead, or have an option to control this behavior.\n\n.. _legacy_text:\n\nLegacy text keys\n~~~~~~~~~~~~~~~~~~~\n\nFor legacy compatibility, the keys :kbd:`a`-:kbd:`z` :kbd:`0`-:kbd:`9`\n:kbd:`\\`` :kbd:`-` :kbd:`=` :kbd:`[` :kbd:`]` :kbd:`\\\\` :kbd:`;` :kbd:`'`\n:kbd:`,` :kbd:`.` :kbd:`/` with the modifiers :kbd:`shift`, :kbd:`alt`,\n:kbd:`ctrl`, :kbd:`shift+alt`, :kbd:`ctrl+alt` are output using the following\nalgorithm:\n\n#. If the :kbd:`alt` key is pressed output the byte for ``ESC (0x1b)``\n#. If the :kbd:`ctrl` modifier is pressed map the key using the table\n   in :ref:`ctrl_mapping`.\n#. Otherwise, if the :kbd:`shift` modifier is pressed, output the shifted key,\n   for example, ``A`` for ``a`` and ``$`` for ``4``.\n#. Otherwise, output the key unmodified\n\nAdditionally, :kbd:`ctrl+space` is output as the NULL byte ``(0x0)``.\n\nAny other combination of modifiers with these keys is output as the appropriate\n``CSI u`` escape code.\n\n.. csv-table:: Example encodings\n   :header: \"Key\", \"Plain\", \"shift\", \"alt\", \"ctrl\", \"shift+alt\", \"alt+ctrl\", \"ctrl+shift\"\n\n    \"i\", \"i (105)\", \"I (73)\", \"ESC i\", \"\\t (9)\", \"ESC I\", \"ESC \\t\", \"CSI 105; 6 u\"\n    \"3\", \"3 (51)\", \"# (35)\", \"ESC 3\", \"ESC (27)\", \"ESC #\", \"ESC ESC\", \"CSI 51; 6 u\"\n    \";\", \"; (59)\", \": (58)\", \"ESC ;\", \"; (59)\", \"ESC :\", \"ESC ;\", \"CSI 59; 6 u\"\n\n.. note::\n   Many of the legacy escape codes are ambiguous with multiple different key\n   presses yielding the same escape code(s), for example, :kbd:`ctrl+i` is the\n   same as :kbd:`tab`, :kbd:`ctrl+m` is the same as :kbd:`Enter`, :kbd:`ctrl+r`\n   is the same :kbd:`ctrl+shift+r`, etc. To resolve these use the\n   :ref:`disambiguate progressive enhancement <disambiguate>`.\n\n\n.. _functional:\n\nFunctional key definitions\n----------------------------\n\nAll numbers are in the Unicode Private Use Area (``57344 - 63743``) except\nfor a handful of keys that use numbers under 32 and 127 (C0 control codes) for legacy\ncompatibility reasons.\n\n.. {{{\n.. start functional key table (auto generated by gen-key-constants.py do not edit)\n\n.. csv-table:: Functional key codes\n   :header: \"Name\", \"CSI\", \"Name\", \"CSI\"\n\n   \"ESCAPE\", \"``27 u``\", \"ENTER\", \"``13 u``\"\n   \"TAB\", \"``9 u``\", \"BACKSPACE\", \"``127 u``\"\n   \"INSERT\", \"``2 ~``\", \"DELETE\", \"``3 ~``\"\n   \"LEFT\", \"``1 D``\", \"RIGHT\", \"``1 C``\"\n   \"UP\", \"``1 A``\", \"DOWN\", \"``1 B``\"\n   \"PAGE_UP\", \"``5 ~``\", \"PAGE_DOWN\", \"``6 ~``\"\n   \"HOME\", \"``1 H or 7 ~``\", \"END\", \"``1 F or 8 ~``\"\n   \"CAPS_LOCK\", \"``57358 u``\", \"SCROLL_LOCK\", \"``57359 u``\"\n   \"NUM_LOCK\", \"``57360 u``\", \"PRINT_SCREEN\", \"``57361 u``\"\n   \"PAUSE\", \"``57362 u``\", \"MENU\", \"``57363 u``\"\n   \"F1\", \"``1 P or 11 ~``\", \"F2\", \"``1 Q or 12 ~``\"\n   \"F3\", \"``13 ~``\", \"F4\", \"``1 S or 14 ~``\"\n   \"F5\", \"``15 ~``\", \"F6\", \"``17 ~``\"\n   \"F7\", \"``18 ~``\", \"F8\", \"``19 ~``\"\n   \"F9\", \"``20 ~``\", \"F10\", \"``21 ~``\"\n   \"F11\", \"``23 ~``\", \"F12\", \"``24 ~``\"\n   \"F13\", \"``57376 u``\", \"F14\", \"``57377 u``\"\n   \"F15\", \"``57378 u``\", \"F16\", \"``57379 u``\"\n   \"F17\", \"``57380 u``\", \"F18\", \"``57381 u``\"\n   \"F19\", \"``57382 u``\", \"F20\", \"``57383 u``\"\n   \"F21\", \"``57384 u``\", \"F22\", \"``57385 u``\"\n   \"F23\", \"``57386 u``\", \"F24\", \"``57387 u``\"\n   \"F25\", \"``57388 u``\", \"F26\", \"``57389 u``\"\n   \"F27\", \"``57390 u``\", \"F28\", \"``57391 u``\"\n   \"F29\", \"``57392 u``\", \"F30\", \"``57393 u``\"\n   \"F31\", \"``57394 u``\", \"F32\", \"``57395 u``\"\n   \"F33\", \"``57396 u``\", \"F34\", \"``57397 u``\"\n   \"F35\", \"``57398 u``\", \"KP_0\", \"``57399 u``\"\n   \"KP_1\", \"``57400 u``\", \"KP_2\", \"``57401 u``\"\n   \"KP_3\", \"``57402 u``\", \"KP_4\", \"``57403 u``\"\n   \"KP_5\", \"``57404 u``\", \"KP_6\", \"``57405 u``\"\n   \"KP_7\", \"``57406 u``\", \"KP_8\", \"``57407 u``\"\n   \"KP_9\", \"``57408 u``\", \"KP_DECIMAL\", \"``57409 u``\"\n   \"KP_DIVIDE\", \"``57410 u``\", \"KP_MULTIPLY\", \"``57411 u``\"\n   \"KP_SUBTRACT\", \"``57412 u``\", \"KP_ADD\", \"``57413 u``\"\n   \"KP_ENTER\", \"``57414 u``\", \"KP_EQUAL\", \"``57415 u``\"\n   \"KP_SEPARATOR\", \"``57416 u``\", \"KP_LEFT\", \"``57417 u``\"\n   \"KP_RIGHT\", \"``57418 u``\", \"KP_UP\", \"``57419 u``\"\n   \"KP_DOWN\", \"``57420 u``\", \"KP_PAGE_UP\", \"``57421 u``\"\n   \"KP_PAGE_DOWN\", \"``57422 u``\", \"KP_HOME\", \"``57423 u``\"\n   \"KP_END\", \"``57424 u``\", \"KP_INSERT\", \"``57425 u``\"\n   \"KP_DELETE\", \"``57426 u``\", \"KP_BEGIN\", \"``1 E or 57427 ~``\"\n   \"MEDIA_PLAY\", \"``57428 u``\", \"MEDIA_PAUSE\", \"``57429 u``\"\n   \"MEDIA_PLAY_PAUSE\", \"``57430 u``\", \"MEDIA_REVERSE\", \"``57431 u``\"\n   \"MEDIA_STOP\", \"``57432 u``\", \"MEDIA_FAST_FORWARD\", \"``57433 u``\"\n   \"MEDIA_REWIND\", \"``57434 u``\", \"MEDIA_TRACK_NEXT\", \"``57435 u``\"\n   \"MEDIA_TRACK_PREVIOUS\", \"``57436 u``\", \"MEDIA_RECORD\", \"``57437 u``\"\n   \"LOWER_VOLUME\", \"``57438 u``\", \"RAISE_VOLUME\", \"``57439 u``\"\n   \"MUTE_VOLUME\", \"``57440 u``\", \"LEFT_SHIFT\", \"``57441 u``\"\n   \"LEFT_CONTROL\", \"``57442 u``\", \"LEFT_ALT\", \"``57443 u``\"\n   \"LEFT_SUPER\", \"``57444 u``\", \"LEFT_HYPER\", \"``57445 u``\"\n   \"LEFT_META\", \"``57446 u``\", \"RIGHT_SHIFT\", \"``57447 u``\"\n   \"RIGHT_CONTROL\", \"``57448 u``\", \"RIGHT_ALT\", \"``57449 u``\"\n   \"RIGHT_SUPER\", \"``57450 u``\", \"RIGHT_HYPER\", \"``57451 u``\"\n   \"RIGHT_META\", \"``57452 u``\", \"ISO_LEVEL3_SHIFT\", \"``57453 u``\"\n   \"ISO_LEVEL5_SHIFT\", \"``57454 u``\"\n\n.. end functional key table\n.. }}}\n\n.. note::\n    The escape codes above of the form ``CSI 1 letter`` will omit the\n    ``1`` if there are no modifiers, since ``1`` is the default value.\n\n.. note::\n   The original version of this specification allowed F3 to be encoded as both\n   CSI R and CSI ~. However, CSI R conflicts with the Cursor Position Report,\n   so it was removed.\n\n.. _ctrl_mapping:\n\nLegacy :kbd:`ctrl` mapping of ASCII keys\n------------------------------------------\n\nWhen the :kbd:`ctrl` key and another key are pressed on the keyboard, terminals\nmap the result *for some keys* to a *C0 control code* i.e. an value from ``0 -\n31``. This mapping was historically dependent on the layout of hardware\nterminal keyboards and is not specified anywhere, completely. The best known\nreference is `Table 3-5 in the VT-100 docs <https://vt100.net/docs/vt100-ug/chapter3.html>`_.\n\nThe table below provides a mapping that is a commonly used superset of the table above.\nAny ASCII keys not in the table must be left untouched by :kbd:`ctrl`.\n\n.. {{{\n.. start ctrl mapping (auto generated by gen-key-constants.py do not edit)\n.. csv-table:: Emitted bytes when :kbd:`ctrl` is held down and a key is pressed\n   :header: \"Key\", \"Byte\", \"Key\", \"Byte\", \"Key\", \"Byte\"\n\n   \"SPC \", \"0\", \"/\", \"31\", \"0\", \"48\"\n   \"1\", \"49\", \"2\", \"0\", \"3\", \"27\"\n   \"4\", \"28\", \"5\", \"29\", \"6\", \"30\"\n   \"7\", \"31\", \"8\", \"127\", \"9\", \"57\"\n   \"?\", \"127\", \"@\", \"0\", \"[\", \"27\"\n   \"\\\\\", \"28\", \"]\", \"29\", \"^\", \"30\"\n   \"_\", \"31\", \"a\", \"1\", \"b\", \"2\"\n   \"c\", \"3\", \"d\", \"4\", \"e\", \"5\"\n   \"f\", \"6\", \"g\", \"7\", \"h\", \"8\"\n   \"i\", \"9\", \"j\", \"10\", \"k\", \"11\"\n   \"l\", \"12\", \"m\", \"13\", \"n\", \"14\"\n   \"o\", \"15\", \"p\", \"16\", \"q\", \"17\"\n   \"r\", \"18\", \"s\", \"19\", \"t\", \"20\"\n   \"u\", \"21\", \"v\", \"22\", \"w\", \"23\"\n   \"x\", \"24\", \"y\", \"25\", \"z\", \"26\"\n   \"~\", \"30\"\n\n.. end ctrl mapping\n.. }}}\n\n.. _fixterms_bugs:\n\nBugs in fixterms\n-------------------\n\nThe following is a list of errata in the `original fixterms proposal\n<http://www.leonerd.org.uk/hacks/fixterms/>`_, corrected in this\nspecification.\n\n* No way to disambiguate :kbd:`Esc` key presses, other than using 8-bit controls\n  which are undesirable for other reasons\n\n* Incorrectly claims special keys are sometimes encoded using ``CSI letter`` encodings when it\n  is actually ``SS3 letter`` in all terminals newer than a VT-52, which is\n  pretty much everything.\n\n* :kbd:`ctrl+shift+tab` should be ``CSI 9 ; 6 u`` not ``CSI 1 ; 5 Z``\n  (shift+tab is not a separate key from tab)\n\n* No support for the :kbd:`super` modifier.\n\n* Makes no mention of cursor key mode and how it changes encodings\n\n* Incorrectly encoding shifted keys when shift modifier is used, for instance,\n  for :kbd:`ctrl+shift+i` is encoded as :kbd:`ctrl+I`.\n\n* No way to have non-conflicting escape codes for :kbd:`alt+letter`,\n  :kbd:`ctrl+letter`, :kbd:`ctrl+alt+letter` key presses\n\n* No way to specify both shifted and unshifted keys for robust shortcut\n  matching (think matching :kbd:`ctrl+shift+equal` and :kbd:`ctrl+plus`)\n\n* No way to specify alternate layout key. This is useful for keyboard layouts\n  such as Cyrillic where you want the shortcut :kbd:`ctrl+c` to work when\n  pressing the :kbd:`ctrl+С` on the keyboard.\n\n* No way to report repeat and release key events, only key press events\n\n* No way to report key events for presses that generate text, useful for\n  gaming. Think of using the :kbd:`WASD` keys to control movement.\n\n* Only a small subset of all possible functional keys are assigned numbers.\n\n* Claims the ``CSI u`` escape code has no fixed meaning, but has been used for\n  decades as ``SCORC`` for instance by xterm and ansi.sys and `DECSMBV\n  <https://vt100.net/docs/vt510-rm/DECSMBV.html>`_ by the VT-510 hardware\n  terminal. This doesn't really matter since these uses are for communication\n  to the terminal not from the terminal.\n\n* Handwaves that :kbd:`ctrl` *tends to* mask with ``0x1f``. In actual fact it\n  does this only for some keys. The action of :kbd:`ctrl` is not specified and\n  varies between terminals, historically because of different keyboard layouts.\n\n\nWhy xterm's modifyOtherKeys should not be used\n---------------------------------------------------\n\n* Does not support release events\n\n* Does not fix the issue of :kbd:`Esc` key presses not being distinguishable from\n  escape codes.\n\n* Does not fix the issue of some keypresses generating identical bytes and thus\n  being indistinguishable\n\n* There is no robust way to query it or manage its state from a program running\n  in the terminal.\n\n* No support for shifted keys.\n\n* No support for alternate keyboard layouts.\n\n* No support for modifiers beyond the basic four.\n\n* No support for lock keys like Num lock and Caps lock.\n\n* Is completely unspecified. The most discussion of it available anywhere is\n  `here <https://invisible-island.net/xterm/modified-keys.html>`__\n  And it contains no specification of what numbers to assign to what function\n  keys beyond running a Perl script on an X11 system!!\n"
  },
  {
    "path": "docs/kittens/broadcast.rst",
    "content": "broadcast\n==================================================\n\n.. only:: man\n\n    Overview\n    --------------\n\n*Type text in all kitty windows simultaneously*\n\nThe ``broadcast`` kitten can be used to type text simultaneously in all\n:term:`kitty windows <window>` (or a subset as desired).\n\nTo use it, simply create a mapping in :file:`kitty.conf` such as::\n\n    map f1 launch --allow-remote-control kitty +kitten broadcast\n\nThen press the :kbd:`F1` key and whatever you type in the newly created window\nwill be sent to all kitty windows.\n\nYou can use the options described below to control which windows are selected.\n\nFor example, only broadcast to other windows in the current tab::\n\n    map f1 launch --allow-remote-control kitty +kitten broadcast --match-tab state:focused\n\n.. program:: kitty +kitten broadcast\n\n\n.. include:: /generated/cli-kitten-broadcast.rst\n"
  },
  {
    "path": "docs/kittens/choose-files.rst",
    "content": "Selecting files, fast\n========================\n\n.. only:: man\n\n    Overview\n    --------------\n\n.. versionadded:: 0.45.0\n\n.. only:: not man\n\n    .. figure:: /screenshots/choose-files.webp\n        :alt: The choose files kitten, showing metadata and title from an e-book file\n        :align: center\n        :width: 100%\n\n\nThe choose-files kitten is designed to allow you to select files, very fast,\nwith just a few key strokes. It operates like `fzf\n<https://github.com/junegunn/fzf/>`__ and similar fuzzy finders, except that\nit is specialised for finding files. As such it supports features such as\nfiltering by file type, file type icons, content previews and\nso on, out of the box. It can be used as a drop in (but much more efficient and\nkeyboard friendly) replacement for the :guilabel:`File open and save`\ndialog boxes common to GUI programs. On Linux, with the help of the\n:doc:`desktop-ui </kittens/desktop-ui>` kitten, you can even convince\nmost GUI programs on your computer to use this kitten instead of regular file\ndialogs.\n\nSimply run it as::\n\n    kitten choose-files\n\nto select a single file from the tree rooted at the current working directory.\n\nType a few letters from the filename and once it becomes the top selection,\npress :kbd:`Enter`. You can change the current directory by selecting a\ndirectory and pressing the :kbd:`Tab` key. :kbd:`Shift+Tab` goes up one\ndirectory level.\n\nIf you want to choose a file and insert it into your shell prompt at the\ncurrent cursor position, press :sc:`insert_chosen_file` for files or\n:sc:`insert_chosen_directory` for directories. Similarly, to have a file\nchosen in a command line, use, for example::\n\n    some-command $(kitten choose-file)\n\nNote that the above may not work in a complicated pipeline as it performs\nterminal I/O and needs exclusive access to the tty device while choosing a\nfile.\n\n.. note:: For content previews, this kitten uses some external programs. In\n   particular `ffmpeg <https://www.ffmpeg.org/>`__ is needed for video\n   previews and `calibre <https://calibre-ebook.com>`__ is needed\n   for ebook metadata and cover preiews.\n\nCreating shortcuts to favorite/frequently used directories\n------------------------------------------------------------\n\nYou can create keyboard shortcuts to quickly switch to any directory in\n:file:`choose-files.conf`. For example:\n\n.. code-block:: conf\n\n   map ctrl+t cd /tmp\n   map alt+p  cd ~/my/project\n\nSelecting multiple files\n-----------------------------\n\nWhen you wish to select multiple files, start the kitten with :option:`--mode\n<kitty +kitten choose_files --mode>`:code:`=files`. Then instead of pressing\n:kbd:`Enter`, press :kbd:`Shift+Enter` instead and the file will be added to the list\nof selections. You can also hold the :kbd:`Ctrl` key and click on files to add\nthem to the selections. Similarly, you can hold the :kbd:`Alt` key and click to\nselect ranges of files (similar to using :kbd:`Shift+click` in a GUI app).\nPress :kbd:`Enter` on the last selected file to finish. The list of selected\nfiles is displayed at the bottom of the kitten and you can click on them\nto deselect a file. Similarly, pressing :kbd:`Shift+Enter` will un-select a\npreviously selected file.\n\n\nHidden and ignored files\n--------------------------\n\nBy default, the kitten does not process hidden files and directories (whose\nnames start with a period). This can be :opt:`changed in the configuration <kitten-choose_files.show_hidden>`\nand also at runtime via the clickable link to the right of the search input.\n\nSimilarly, the kitten respects both :file:`.gitignore` and :file:`.ignore`\nfiles, by default. This can also be changed both :opt:`in configuration\n<kitten-choose_files.respect_ignores>` or at runtime. Note that\n:file:`.gitignore` files are only respected if there is also a :file:`.git`\ndirectory present. The kitten also supports the global :file:`.gitignore` file,\nthough it applies only inside git working trees. You can specify :opt:`global ignore\npatterns <kitten-choose_files.ignore>`, that apply everywhere in :file:`choose-files.conf`.\n\n\nSelecting non-existent files (save file names)\n-------------------------------------------------\n\nThis kitten can also be used to select non-existent files, that is a new file\nfor a :guilabel:`Save file` type of dialog using :option:`--mode <kitty +kitten\nchoose_files --mode>`:code:`=save-file`. Once you have changed to the directory\nyou want the file to be in (using the :kbd:`Tab` key),\npress :kbd:`Ctrl+Enter` and you will be able to type in the file name. If you\nwish to modify an existing file name use :kbd:`Alt+Enter` to modify the\nfilename of the current top match instead.\n\n\nSelecting directories\n---------------------------\n\nThis kitten can also be used to select directories,\nfor an :guilabel:`Open directory` type of dialog using :option:`--mode <kitty +kitten\nchoose_files --mode>`:code:`=dir`. Once you have changed to the directory\nyou want, press :kbd:`Ctrl+Enter` to accept it. Or if you are in a parent\ndirectory you can select a descendant directory by pressing :kbd:`Enter`, the\nsame as you would for selecting a file to open.\n\n\nConfiguration\n------------------------\n\nYou can configure various aspects of the kitten's operation by creating a\n:file:`choose-files.conf` in your :ref:`kitty config folder <confloc>`.\nSee below for the supported configuration directives.\n\n\n.. include:: /generated/conf-kitten-choose_files.rst\n\n\n.. include:: /generated/cli-kitten-choose_files.rst\n\n\n"
  },
  {
    "path": "docs/kittens/choose-fonts.rst",
    "content": "Changing kitty fonts\n========================\n\n.. only:: man\n\n    Overview\n    --------------\n\nTerminal aficionados spend all day staring at text, as such, getting text\nrendering just right is very important. kitty has extremely powerful facilities\nfor fine-tuning text rendering. It supports `OpenType features\n<https://en.wikipedia.org/wiki/List_of_typographic_features>`__ to select\nalternate glyph shapes, and `Variable fonts\n<https://en.wikipedia.org/wiki/Variable_font>`__ to control the weight or\nspacing of a font precisely. You can also :opt:`select which font is used to\nrender particular unicode codepoints <symbol_map>` and you can :opt:`modify\nfont metrics <modify_font>` and even :opt:`adjust the gamma curves\n<text_composition_strategy>` used for rendering text onto the background color.\n\nThe first step is to select the font faces kitty will use for rendering\nregular, bold and italic text. kitty comes with a convenient UI for choosing fonts,\nin the form of the *choose-fonts* kitten. Simply run::\n\n    kitten choose-fonts\n\nand follow the on screen prompts.\n\nFirst, choose the family you want, the list of families can be easily filtered by\ntyping a few letters from the family name you are looking for. The family\nselection screen shows you a preview of how the family looks.\n\n.. image:: ../screenshots/family-selection.png\n   :alt: Choosing a family with the choose fonts kitten\n   :width: 600\n\nOnce you select a family by pressing the :kbd:`Enter` key, you\nare shown previews of what the regular, bold and italic faces look like\nfor that family. You can choose to fine tune any of the faces. Start with\nfine-tuning the regular face by pressing the :kbd:`R` key. The other styles\nwill be automatically adjusted based on what you select for the regular face.\n\n.. image:: ../screenshots/font-fine-tune.png\n   :alt: Fine tune a font by choosing a precise weight and features\n   :width: 600\n\nYou can choose a specific style or font feature by clicking on it. A precise\nvalue for any variable axes can be selected using the slider, in the screenshot\nabove, the font supports precise weight adjustment. If you are lucky the font\ndesigner has included descriptive names for font features, which will be\ndisplayed, if not, consult the documentation of the font to see what each feature does.\n\n.. _font_spec_syntax:\n\nThe font specification syntax\n--------------------------------\n\nIf you don't like the choose fonts kitten or simply want to understand and\nwrite font selection options into :file:`kitty.conf` yourself, read on.\n\nThere are four font face selection keys: `font_family`, `bold_font`,\n`italic_font` and `bold_italic_font`. Each of these supports the syntax\ndescribed below. Their values can be of three types, either a\nfont family name, the keyword ``auto`` or an extended ``key=value`` syntax\nfor specifying font selection precisely.\n\nIf a font family name is specified kitty will use Operating System APIs to\nsearch for a matching font. The keyword ``auto`` means kitty will choose a font\ncompletely automatically, typically this is used for automatically selecting\nbold/italic variants once the :opt:`font_family` is set. The bold and italic\nvariants will then automatically use the same set of features as the main face.\n\nTo specify font face selection more precisely, a ``key=value`` syntax is used.\nFirst, let's look at a few examples::\n\n    # Select by family only, actual face selection is automatic\n    font_family family=\"Fira Code\"\n    # Select an exact face by Postscript name\n    font_family postscript_name=FiraCode\n    # Select an exact face by family with features and variable weight\n    font_family family=SourceCodeVF variable_name=SourceCodeUpright features=\"+zero cv01=2\" wght=380\n\nThe following are the known keys, any other keys are names of *variable axes*,\nthat is, they are used to set the variable value for some font characteristic.\n\n``family``\n    A font family name. A family typically has multiple actual font faces, such\n    as bold and italic variants. One or more of the faces can even be variable,\n    allowing fine tuning of font characteristics.\n\n``style``\n    A style name to choose a particular font from a given family. Useful only\n    with the ``family`` key, when no more precise methods for face selection\n    are specified. Can also be used to specify a named variable style for\n    variable fonts.\n\n``postscript_name``\n    The actual postscript name for a font face. This allows selecting a\n    particular variant within a font family. But note that postscript names\n    are usually insufficient for selecting variable fonts.\n\n``full_name``\n    This can be used to select a particular font face in a family. However, it\n    is less precise than ``postscript_name`` and should not generally be used.\n\n``variable_name``\n    Some families with variable fonts actually contain multiple font files. For\n    example, a family could have variable weights with one font file containing\n    upright variable weight faces and another containing italic variable weight\n    faces. Well designed fonts use a *variable name* to distinguish between\n    such files. Should be used in conjunction with ``family`` to select a\n    particular variable font file.\n\n``features``\n    A space separated list of OpenType font features to enable/disable or\n    select a value of, for this font. Consult the documentation for the font\n    family to see what features it supports and their effects. The exact syntax\n    for specifying features is `documented by HarfBuzz\n    <https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`__\n\n``system``\n    This can be used to pass an arbitrary string, usually a family or full name\n    to the OS font selection APIs. Should not be used in conjunction with any\n    other keys. Is the same as specifying just the font name without any keys.\n\n\nIn addition to these keys, any four letter key is treated as the name of a\nvariable characteristic of the font. Its value is used to set the value for\nthe name.\n"
  },
  {
    "path": "docs/kittens/clipboard.rst",
    "content": "clipboard\n==================================================\n\n.. only:: man\n\n    Overview\n    --------------\n\n*Copy/paste to the system clipboard from shell scripts*\n\n.. highlight:: sh\n\n\nThe ``clipboard`` kitten can be used to read or write to the system clipboard\nfrom the shell. It even works over SSH. Using it is as simple as::\n\n    echo hooray | kitten clipboard\n\nAll text received on :file:`STDIN` is copied to the clipboard.\n\nTo get text from the clipboard::\n\n    kitten clipboard --get-clipboard\n\nThe text will be written to :file:`STDOUT`. Note that by default kitty asks for\npermission when a program attempts to read the clipboard. This can be\ncontrolled via :opt:`clipboard_control`.\n\n.. versionadded:: 0.27.0\n   Support for copying arbitrary data types\n\nThe clipboard kitten can be used to send/receive\nmore than just plain text from the system clipboard. You can transfer arbitrary\ndata types. Best illustrated with some examples::\n\n    # Copy an image to the clipboard:\n    kitten clipboard picture.png\n\n    # Copy an image and some text to the clipboard:\n    kitten clipboard picture.jpg text.txt\n\n    # Copy text from STDIN and an image to the clipboard:\n    echo hello | kitten clipboard picture.png /dev/stdin\n\n    # Copy any raster image available on the clipboard to a PNG file:\n    kitten clipboard -g picture.png\n\n    # Copy an image to a file and text to STDOUT:\n    kitten clipboard -g picture.png /dev/stdout\n\n    # List the formats available on the system clipboard\n    kitten clipboard -g -m . /dev/stdout\n\nNormally, the kitten guesses MIME types based on the file names. To control the\nMIME types precisely, use the :option:`--mime <kitty +kitten clipboard --mime>` option.\n\nThis kitten uses a new protocol developed by kitty to function, for details,\nsee :doc:`/clipboard`.\n\n.. program:: kitty +kitten clipboard\n\n\n.. include:: /generated/cli-kitten-clipboard.rst\n"
  },
  {
    "path": "docs/kittens/custom.rst",
    "content": "Custom kittens\n=================\n\nYou can easily create your own kittens to extend kitty. They are just terminal\nprograms written in Python. When launching a kitten, kitty will open an overlay\nwindow over the current window and optionally pass the contents of the current\nwindow/scrollback to the kitten over its :file:`STDIN`. The kitten can then\nperform whatever actions it likes, just as a normal terminal program. After\nexecution of the kitten is complete, it has access to the running kitty instance\nso it can perform arbitrary actions such as closing windows, pasting text, etc.\n\nLet's see a simple example of creating a kitten. It will ask the user for some\ninput and paste it into the terminal window.\n\nCreate a file in the kitty config directory, :file:`~/.config/kitty/mykitten.py`\n(you might need to adjust the path to wherever the :ref:`kitty config directory\n<confloc>` is on your machine).\n\n\n.. code-block:: python\n\n    from kitty.boss import Boss\n\n    def main(args: list[str]) -> str:\n        # this is the main entry point of the kitten, it will be executed in\n        # the overlay window when the kitten is launched\n        answer = input('Enter some text: ')\n        # whatever this function returns will be available in the\n        # handle_result() function\n        return answer\n\n    def handle_result(args: list[str], answer: str, target_window_id: int, boss: Boss) -> None:\n        # get the kitty window into which to paste answer\n        w = boss.window_id_map.get(target_window_id)\n        if w is not None:\n            w.paste_text(answer)\n\n\nNow in :file:`kitty.conf` add the lines::\n\n    map ctrl+k kitten mykitten.py\n\n\nStart kitty and press :kbd:`Ctrl+K` and you should see the kitten running.\nThe best way to develop your own kittens is to modify one of the built-in\nkittens. Look in the `kittens sub-directory\n<https://github.com/kovidgoyal/kitty/tree/master/kittens>`__ of the kitty source\ncode for those. Or see below for a list of :ref:`third-party kittens\n<external_kittens>`, that other kitty users have created.\n\nkitty API to use with kittens\n-------------------------------\n\nKittens have full access to internal kitty APIs. However these are neither\nentirely stable nor documented. You can instead use the kitty\n:doc:`Remote control API </remote-control>`. Simply call\n:code:`boss.call_remote_control()`, with the same arguments you\nwould pass to ``kitten @``. For example:\n\n.. code-block:: python\n\n    def handle_result(args: list[str], answer: str, target_window_id: int, boss: Boss) -> None:\n        # get the kitty window to which to send text\n        w = boss.window_id_map.get(target_window_id)\n        if w is not None:\n            boss.call_remote_control(w, ('send-text', f'--match=id:{w.id}', 'hello world'))\n\n.. note::\n   Inside handle_result() the active window is still the window in which the\n   kitten was run, therefore when using call_remote_control() be sure to pass\n   the appropriate option to select the target window, usually ``--match`` as\n   shown above or ``--self``.\n\n\nRun, ``kitten @ --help`` in a kitty terminal, to see all the remote control\ncommands available to you.\n\nPassing arguments to kittens\n------------------------------\n\nYou can pass arguments to kittens by defining them in the map directive in\n:file:`kitty.conf`. For example::\n\n    map ctrl+k kitten mykitten.py arg1 arg2\n\nThese will be available as the ``args`` parameter in the ``main()`` and\n``handle_result()`` functions. Note also that the current working directory\nof the kitten is set to the working directory of whatever program is running in\nthe active kitty window. The special argument ``@selection`` is replaced by the\ncurrently selected text in the active kitty window.\n\n\nPassing the contents of the screen to the kitten\n---------------------------------------------------\n\nIf you would like your kitten to have access to the contents of the screen\nand/or the scrollback buffer, you just need to add an annotation to the\n``handle_result()`` function, telling kitty what kind of input your kitten would\nlike. For example:\n\n.. code-block:: py\n\n    from kitty.boss import Boss\n\n    # in main, STDIN is for the kitten process and will contain\n    # the contents of the screen\n    def main(args: list[str]) -> str:\n        return sys.stdin.read()\n\n    # in handle_result, STDIN is for the kitty process itself, rather\n    # than the kitten process and should not be read from.\n    from kittens.tui.handler import result_handler\n    @result_handler(type_of_input='text')\n    def handle_result(args: list[str], stdin_data: str, target_window_id: int, boss: Boss) -> None:\n        pass\n\n\nThis will send the plain text of the active window to the kitten's\n:file:`STDIN`. There are many other types of input you can ask for, described in\nthe table below:\n\n.. table:: Types of input to kittens\n    :align: left\n\n    =========================== ========================================================================================\n    Keyword                     Type of :file:`STDIN` input\n    =========================== ========================================================================================\n    ``text``                    Plain text of active window\n    ``ansi``                    Formatted text of active window\n    ``screen``                  Plain text of active window with line wrap markers\n    ``screen-ansi``             Formatted text of active window with line wrap markers\n\n    ``history``                 Plain text of active window and its scrollback\n    ``ansi-history``            Formatted text of active window and its scrollback\n    ``screen-history``          Plain text of active window and its scrollback with line wrap markers\n    ``screen-ansi-history``     Formatted text of active window and its scrollback with line wrap markers\n\n    ``output``                  Plain text of the output from the last run command\n    ``output-screen``           Plain text of the output from the last run command with wrap markers\n    ``output-ansi``             Formatted text of the output from the last run command\n    ``output-screen-ansi``      Formatted text of the output from the last run command with wrap markers\n\n    ``selection``               The text currently selected with the mouse\n    =========================== ========================================================================================\n\nIn addition to ``output``, that gets the output of the last run command,\n``last_visited_output`` gives the output of the command last jumped to\nand ``first_output`` gives the output of the first command currently on screen.\nThese can also be combined with ``screen`` and ``ansi`` for formatting.\n\n.. note::\n   For the types based on the output of a command, :ref:`shell_integration` is\n   required.\n\n\nUsing kittens to script kitty, without any terminal UI\n-----------------------------------------------------------\n\nIf you would like your kitten to script kitty, without bothering to write a\nterminal program, you can tell the kittens system to run the ``handle_result()``\nfunction without first running the ``main()`` function.\n\nFor example, here is a kitten that \"zooms in/zooms out\" the current terminal\nwindow by switching to the stack layout or back to the previous layout. This is\nequivalent to the builtin :ac:`toggle_layout` action.\n\nCreate a Python file in the :ref:`kitty config directory <confloc>`,\n:file:`~/.config/kitty/zoom_toggle.py`\n\n.. code-block:: py\n\n    from kitty.boss import Boss\n\n    def main(args: list[str]) -> str:\n        pass\n\n    from kittens.tui.handler import result_handler\n    @result_handler(no_ui=True)\n    def handle_result(args: list[str], answer: str, target_window_id: int, boss: Boss) -> None:\n        tab = boss.active_tab\n        if tab is not None:\n            if tab.current_layout.name == 'stack':\n                tab.last_used_layout()\n            else:\n                tab.goto_layout('stack')\n\n\nNow in :file:`kitty.conf` add::\n\n    map f11 kitten zoom_toggle.py\n\nPressing :kbd:`F11` will now act as a zoom toggle function. You can get even\nmore fancy, switching the kitty OS window to fullscreen as well as changing the\nlayout, by simply adding the line::\n\n    boss.toggle_fullscreen()\n\n\nto the ``handle_result()`` function, above.\n\n\n.. _send_mouse_event:\n\nSending mouse events\n--------------------\n\nIf the program running in a window is receiving mouse events, you can simulate\nthose using::\n\n    from kitty.fast_data_types import send_mouse_event\n    send_mouse_event(screen, x, y, button, action, mods)\n\n``screen`` is the ``screen`` attribute of the window you want to send the event\nto. ``x`` and ``y`` are the 0-indexed coordinates. ``button`` is a number using\nthe same numbering as X11 (left: ``1``, middle: ``2``, right: ``3``, scroll up:\n``4``, scroll down: ``5``, scroll left: ``6``, scroll right: ``7``, back:\n``8``, forward: ``9``). ``action`` is one of ``PRESS``, ``RELEASE``, ``DRAG``\nor ``MOVE``. ``mods`` is a bitmask of ``GLFW_MOD_{mod}`` where ``{mod}`` is one\nof ``SHIFT``, ``CONTROL`` or ``ALT``. All the mentioned constants are imported\nfrom ``kitty.fast_data_types``.\n\nFor example, to send a left click at position x: 2, y: 3 to the active window::\n\n    from kitty.fast_data_types import send_mouse_event, PRESS\n    send_mouse_event(boss.active_window.screen, 2, 3, 1, PRESS, 0)\n\nThe function will only send the event if the program is receiving events of\nthat type, and will return ``True`` if it sent the event, and ``False`` if not.\n\n\n.. _kitten_main_rc:\n\nUsing remote control inside the main() kitten function\n------------------------------------------------------------\n\nYou can use kitty's remote control features inside the main() function of a\nkitten, even without enabling remote control. This is useful if you want to\nprobe kitty for more information before presenting some UI to the user or if\nyou want the user to be able to control kitty from within your kitten's UI\nrather than after it has finished running. To enable it, simply tell kitty your kitten\nrequires remote control, as shown in the example below::\n\n    import json\n    import sys\n    from pprint import pprint\n\n    from kittens.tui.handler import kitten_ui\n\n    @kitten_ui(allow_remote_control=True)\n    def main(args: list[str]) -> str:\n        # get the result of running kitten @ ls\n        cp = main.remote_control(['ls'], capture_output=True)\n        if cp.returncode != 0:\n            sys.stderr.buffer.write(cp.stderr)\n            raise SystemExit(cp.returncode)\n        output = json.loads(cp.stdout)\n        pprint(output)\n        # open a new tab with a title specified by the user\n        title = input('Enter the name of tab: ')\n        window_id = main.remote_control(['launch', '--type=tab', '--tab-title', title], check=True, capture_output=True).stdout.decode()\n        return window_id\n\n:code:`allow_remote_control=True` tells kitty to run this kitten with remote\ncontrol enabled, regardless of whether it is enabled globally or not.\nTo run a remote control command use the :code:`main.remote_control()` function\nwhich is a thin wrapper around Python's :code:`subprocess.run` function. Note\nthat by default, for security, child processes launched by your kitten cannot use remote\ncontrol, thus it is necessary to use :code:`main.remote_control()`. If you wish\nto enable child processes to use remote control, call\n:code:`main.allow_indiscriminate_remote_control()`.\n\nRemote control access can be further secured by using\n:code:`kitten_ui(allow_remote_control=True, remote_control_password='ls set-colors')`.\nThis will use a secure generated password to restrict remote control.\nYou can specify a space separated list of remote control commands to allow, see\n:opt:`remote_control_password` for details. The password value is accessible\nas :code:`main.password` and is used by :code:`main.remote_control()`\nautomatically.\n\n\nDebugging kittens\n--------------------\n\nThe part of the kitten that runs in ``main()`` is just a normal program and the\noutput of print statements will be visible in the kitten window. Or alternately,\nyou can use::\n\n    from kittens.tui.loop import debug\n    debug('whatever')\n\nThe ``debug()`` function is just like ``print()`` except that the output will\nappear in the ``STDOUT`` of the kitty process inside which the kitten is\nrunning.\n\nThe ``handle_result()`` part of the kitten runs inside the kitty process.\nThe output of print statements will go to the ``STDOUT`` of the kitty process.\nSo if you run kitty from another kitty instance, the output will be visible\nin the first kitty instance.\n\n\nDeveloping builtin kittens for inclusion with kitty\n----------------------------------------------------------\n\nThere is documentation for :doc:`developing-builtin-kittens` which are written in the Go\nlanguage.\n\n\n.. _external_kittens:\n\nKittens created by kitty users\n---------------------------------------------\n\n`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_\n    Allows you to navigate seamlessly between vim and kitty splits using a\n    consistent set of hotkeys.\n\n`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_\n    Makes the kitty scroll bindings work in full screen applications\n\n`kitty-tab-switcher <https://github.com/OsiPog/kitty-tab-switcher>`__\n    Fuzzy finder for kitty tabs with previews\n\n`gattino <https://github.com/salvozappa/gattino>`__\n    Integrate kitty with an LLM to convert plain language prompts into shell commands.\n\n:iss:`insert password <1222>`\n    Insert a password from a CLI password manager, taking care to only do it at\n    a password prompt.\n\n`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_\n    URL hints kitten for WeeChat that works without having to use WeeChat's\n    raw-mode.\n"
  },
  {
    "path": "docs/kittens/desktop-ui.rst",
    "content": "Using terminal programs to provide Linux desktop components\n===============================================================\n\n.. only:: man\n\n    Overview\n    --------------\n\n.. versionadded:: 0.43.0\n\nPower users of terminals on Linux also often like to use bare bones window\nmanagers instead of full fledged desktop environments. This kitten helps\nprovide parts of the desktop environment that are missing from such setups,\nand does so using keyboard friendly, terminal first, UI components. Some of its\nfeatures are:\n\n* Replace the typical File Open/Save dialogs used in GUI programs with the\n  fast and keyboard centric :doc:`choose-files </kittens/choose-files>` kitten\n  running in a semi-transparent kitty overlay.\n\n* Allow simple command line based management of the desktop light/dark modes.\n\n\nHow to install\n-------------------\n\n.. note::\n\n   This kitten relies on the :doc:`panel kitten </kittens/panel>`\n   under the hood to supply UI components. Check :ref:`the documentation <panel_compat>`\n   of that kitten to see if your window manager works with it.\n\nFirst, run::\n\n    kitten desktop-ui enable-portal\n\nThen, set the following two environment variables, *system wide*, that means in\n:file:`/etc/environment` or the equivalent for your distribution::\n\n    QT_QPA_PLATFORMTHEME=xdgdesktopportal\n    GTK_USE_PORTAL=1\n\n\nFinally, reboot. Now, when you open a file dialog in most GUI applications, it\nshould open the :doc:`choose-files kitten </kittens/choose-files>` instead\nof a normal file open dialog. You can change the current light/dark mode of\nyour desktop by running::\n\n    kitten desktop-ui set-color-scheme dark\n    kitten desktop-ui set-color-scheme light\n\nCheck the current value using::\n\n    dbus-send --session --print-reply --dest=org.freedesktop.portal.Desktop /org/freedesktop/portal/desktop org.freedesktop.portal.Settings.Read string:org.freedesktop.appearance string:color-scheme\n\nHow it works\n----------------\n\nModern Linux desktops have so called `portals\n<https://flatpak.github.io/xdg-desktop-portal/docs/index.html>`__ that were\ninvented for sandboxed applications and provide various facilities to such\napplications over DBUS, including file open dialogs, common desktop settings,\netc. This kitten works by implementing a backend for some of these services.\n\nNormal GUI applications can then be told to make use of these services, thereby\nallowing us to replace parts of the desktop experience as needed.\n\nThere are multiple competing implementations of the backends. Each desktop\nenvironment like KDE or GNOME has it's own backend and many window managers\nprovide implementations for some backends as well. Service discovery and\nconfiguring which backend to use happens via the :file:`xdg-desktop-portal`\nprogram, usually found at :file:`/usr/lib/xdg-desktop-portal`.\n\nIt can be configured by files in :file:`~/.local/share/xdg-desktop-portal`. See\n`man portals.conf <https://man.archlinux.org/man/portals.conf.5>`__. The\n``kitten desktop-ui enable-portal`` command takes care of the setup for you\nautomatically. If you want to customize exactly which services to use this\nkitten for, run the command and then edit the conf file that the command says\nit has patched.\n\n\nTroubleshooting\n-------------------\n\nFirst, ensure that DBUS is able to auto-start the kitten when it is needed. If\nthe kitten is not already running, try the following command::\n\n    dbus-send --session --print-reply --dest=org.freedesktop.impl.portal.desktop.kitty \\\n        /net/kovidgoyal/kitty/portal org.freedesktop.DBus.Properties.GetAll \\\n        string:net.kovidgoyal.kitty.settings\n\nIf DBUS is able to start the kitten or if it is already running it will print\nout the version property, otherwise it will fail with an error. If it fails,\ncheck the file\n:file:`~/.local/share/dbus-1/services/org.freedesktop.impl.portal.desktop.kitty.service`\nthat should have been created by the ``enable-portal`` command. It's ``Exec``\nkey must point to the full path to the kitten executable.\n\nNext, check that the XDG portal system is actually using this kitten for its\nsettings backend. Run::\n\n    dbus-send --session --print-reply --dest=org.freedesktop.portal.Desktop \\\n        /org/freedesktop/portal/desktop org.freedesktop.portal.Settings.Read \\\n        string:net.kovidgoyal.kitty string:status\n\nIf this returns a reply then the kitten is being used, as expected. If it\nreturns a not found error, then some other backend is being used for settings.\n\nRead the ``portals.conf`` man page and run::\n\n    /usr/lib/xdg-desktop-portal -r v\n\nthis will output a lot of debug information, which should tell you which\nbackend is chosen for which service. Read the debug output carefully to\ndetermine why the kitten is not being selected.\n\nIf some GUI applications are not using the choose-files kitten for their file\nselect dialogs, then make sure the environment variables mentioned above are\nset, you can also try running the GUI application with them set explicitly,\nas::\n\n    QT_QPA_PLATFORMTHEME=xdgdesktopportal GTK_USE_PORTAL=1 my-gui-app\n\nNote that not all applications use portals, so if some particular application\nis failing to use the portal but others work, report the issue to that\napplications' developers.\n\nConfiguration\n------------------------\n\nYou can configure various aspects of the kitten's operation by creating a\n:file:`desktop-ui-portal.conf` in your :ref:`kitty config folder <confloc>`.\nSee below for the supported configuration directives.\n\n\n.. include:: /generated/conf-kitten-desktop_ui.rst\n"
  },
  {
    "path": "docs/kittens/developing-builtin-kittens.rst",
    "content": "Developing builtin kittens\n=============================\n\nBuiltin kittens in kitty are written in the Go language, with small Python\nwrapper scripts to define command line options and handle UI integration.\n\nGetting started\n-----------------------\n\nTo get started with creating a builtin kitten, one that will become part of kitty\nand be available as ``kitten my-kitten``, create a directory named\n:file:`my_kitten` in the :file:`kittens` directory. Then, in this directory\nadd three, files: :file:`__init__.py` (an empty file), :file:`__main__.py` and\n:file:`main.go`.\n\nTemplate for `main.py`\n^^^^^^^^^^^^^^^^^^^^^^\n\nThe file :file:`main.py` contains the command line option definitions for your kitten. Change the actual options and help text below as needed.\n\n.. code-block:: python\n\n    #!/usr/bin/env python\n    # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\n    import sys\n\n    # See the file kitty/cli.py in the kitty sourcecode for more examples of\n    # the syntax for defining options\n    OPTIONS = r'''\n    --some-string-option -s\n    default=my_default_value\n    Help text for a simple option taking a string value.\n\n\n    --some-boolean-option -b\n    type=bool-set\n    Help text for a boolean option defaulting to false.\n\n\n    --some-inverted-boolean-option\n    type=bool-unset\n    Help text for a boolean option defaulting to true.\n\n\n    --an-integer-option\n    type=int\n    default=13\n    bla bla\n\n\n    --an-enum-option\n    choices=a,b,c,d\n    default=a\n    This option can only take the values a, b, c, or d\n    '''.format\n\n    help_text = '''\\\n    The introductory help text for your kitten.\n\n    Can contain multiple paragraphs with :bold:`bold`\n    :green:`colored`, :code:`code`, :link:`links <http://url>` etc.\n    formatting.\n\n    Option help strings can also use this formatting.\n    '''\n\n    # The usage string for your kitten\n    usage = 'TITLE [BODY ...]'\n    short_description = 'some short description of your kitten it will show up when running kitten without arguments to list all kittens`\n\n    if __name__ == '__main__':\n        raise SystemExit('This should be run as kitten my-kitten')\n    elif __name__ == '__doc__':\n        cd = sys.cli_docs  # type: ignore\n        cd['usage'] = usage\n        cd['options'] = OPTIONS\n        cd['help_text'] = help_text\n        cd['short_desc'] = short_description\n\n\nTemplate for `main.go`\n^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: go\n\n    package my_kitten\n\n    import (\n        \"fmt\"\n\n        \"kitty/tools/cli\"\n    )\n\n    var _ = fmt.Print\n\n    func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {\n        // Here rc is the exit code for the kitten which should be 1 or higher if err is not nil\n        fmt.Println(\"Hello world!\")\n        fmt.Println(args)\n        fmt.Println(fmt.Sprintf(\"%#v\", opts))\n        return\n    }\n\n    func EntryPoint(parent *cli.Command) {\n        create_cmd(parent, main)\n    }\n\nEdit :file:`tools/cmd/tool/main.go`\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAdd the entry point of the kitten into :file:`tools/cmd/tool/main.go`.\n\nFirst, import the kitten into this file. To do this, add :code:`\"kitty/kittens/my_kitten\"` into the :code:`import ( ... )` section at the top.\nThen, add ``my_kitten.EntryPoint(root)`` into ``func KittyToolEntryPoints(root *cli.Command)`` and you are done. After running make you should\nbe able to test your kitten by running::\n\n    kitten my-kitten\n\n"
  },
  {
    "path": "docs/kittens/diff.rst",
    "content": "kitty-diff\n================================================================================\n\n*A fast side-by-side diff tool with syntax highlighting and images*\n\n.. highlight:: sh\n\nMajor Features\n-----------------\n\n.. container:: major-features\n\n    * Displays diffs side-by-side in the kitty terminal\n\n    * Does syntax highlighting of the displayed diffs, asynchronously, for\n      maximum speed\n\n    * Displays images as well as text diffs, even over SSH\n\n    * Does recursive directory diffing\n\n\n.. figure:: ../screenshots/diff.png\n   :alt: Screenshot, showing a sample diff\n   :align: center\n   :width: 100%\n\n   Screenshot, showing a sample diff\n\n\nInstallation\n---------------\n\nSimply :ref:`install kitty <quickstart>`.\n\n\nUsage\n--------\n\nIn the kitty terminal, run::\n\n    kitten diff file1 file2\n\nto see the diff between :file:`file1` and :file:`file2`.\n\nCreate an alias in your shell's startup file to shorten the command, for\nexample:\n\n.. code-block:: sh\n\n    alias d=\"kitten diff\"\n\nNow all you need to do to diff two files is::\n\n    d file1 file2\n\nYou can also pass directories instead of files to see the recursive diff of the\ndirectory contents.\n\n\nKeyboard controls\n----------------------\n\n===========================       ===========================\nAction                            Shortcut\n===========================       ===========================\nQuit                              :kbd:`Q`\nScroll line up                    :kbd:`K`, :kbd:`Up`\nScroll line down                  :kbd:`J`, :kbd:`Down`\nScroll page up                    :kbd:`PgUp`\nScroll page down                  :kbd:`PgDn`\nScroll to top                     :kbd:`Home`\nScroll to bottom                  :kbd:`End`\nScroll to next page               :kbd:`Space`, :kbd:`PgDn`, :kbd:`Ctrl+F`\nScroll to previous page           :kbd:`PgUp`, :kbd:`Ctrl+B`\nScroll down half page             :kbd:`Ctrl+D`\nScroll up half page               :kbd:`Ctrl+U`\nScroll to next change             :kbd:`N`\nScroll to previous change         :kbd:`P`\nIncrease lines of context         :kbd:`+`\nDecrease lines of context         :kbd:`-`\nAll lines of context              :kbd:`A`\nRestore default context           :kbd:`=`\nSearch forwards                   :kbd:`/`\nSearch backwards                  :kbd:`?`\nClear search or exit              :kbd:`Esc`\nScroll to next match              :kbd:`>`, :kbd:`.`\nScroll to previous match          :kbd:`<`, :kbd:`,`\nCopy selection to clipboard       :kbd:`y`\nCopy selection or exit            :kbd:`Ctrl+C`\n===========================       ===========================\n\n\nIntegrating with git\n-----------------------\n\nAdd the following to :file:`~/.gitconfig`:\n\n.. code-block:: ini\n\n    [diff]\n        tool = kitty\n        guitool = kitty.gui\n    [difftool]\n        prompt = false\n        trustExitCode = true\n    [difftool \"kitty\"]\n        cmd = kitten diff $LOCAL $REMOTE\n    [difftool \"kitty.gui\"]\n        cmd = kitten diff $LOCAL $REMOTE\n\nNow to use kitty-diff to view git diffs, you can simply do::\n\n    git difftool --no-symlinks --dir-diff\n\nOnce again, creating an alias for this command is useful.\n\n\nWhy does this work only in kitty?\n----------------------------------------\n\nThe diff kitten makes use of various features that are :doc:`kitty only\n</protocol-extensions>`, such as the :doc:`kitty graphics protocol\n</graphics-protocol>`, the :doc:`extended keyboard protocol\n</keyboard-protocol>`, etc. It also leverages terminal program infrastructure\nI created for all of kitty's other kittens to reduce the amount of code needed\n(the entire implementation is under 3000 lines of code).\n\nAnd fundamentally, it's kitty only because I wrote it for myself, and I am\nhighly unlikely to use any other terminals :)\n\n\nConfiguration\n------------------------\n\nYou can configure the colors used, keyboard shortcuts, the diff implementation,\nthe default lines of context, etc. by creating a :file:`diff.conf` file in your\n:ref:`kitty config folder <confloc>`. See below for the supported configuration\ndirectives.\n\n\n.. include:: /generated/conf-kitten-diff.rst\n\n\n.. include:: /generated/cli-kitten-diff.rst\n\n\nSample diff.conf\n-----------------\n\nYou can download a sample :file:`diff.conf` file with all default settings and\ncomments describing each setting by clicking: :download:`sample diff.conf\n</generated/conf/diff.conf>`.\n"
  },
  {
    "path": "docs/kittens/hints.rst",
    "content": "Hints\n==========\n\n.. only:: man\n\n    Overview\n    --------------\n\n\n|kitty| has a *hints mode* to select and act on arbitrary text snippets\ncurrently visible on the screen.  For example, you can press :sc:`open_url`\nto choose any URL visible on the screen and then open it using your default web\nbrowser.\n\n.. figure:: ../screenshots/hints_mode.png\n    :alt: URL hints mode\n    :align: center\n    :width: 100%\n\n    URL hints mode\n\nSimilarly, you can press :sc:`insert_selected_path` to select anything that\nlooks like a path or filename and then insert it into the terminal, very useful\nfor picking files from the output of a :program:`git` or :program:`ls` command\nand adding them to the command line for the next command.\n\nYou can also press :sc:`goto_file_line` to select anything that looks like a\npath or filename followed by a colon and a line number and open the file in\nyour default editor at the specified line number (opening at line number will\nwork only if your editor supports the +linenum command line syntax or is a\n\"known\" editor). The patterns and editor to be used can be modified using\noptions passed to the kitten. For example::\n\n    map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} {path}\n\nwill open the selected file in a new tab inside `Neovim <https://neovim.io/>`__\nwhen you press :kbd:`Ctrl+G`.\n\nPressing :sc:`open_selected_hyperlink` will open :term:`hyperlinks`, i.e. a URL\nthat has been marked as such by the program running in the terminal,\nfor example, by ``ls --hyperlink=auto``. If :program:`ls` comes with your OS\ndoes not support hyperlink, you may need to install `GNU Coreutils\n<https://www.gnu.org/software/coreutils/>`__.\n\nYou can also :doc:`customize what actions are taken for different types of URLs\n<../open_actions>`.\n\n.. note:: If there are more hints than letters, hints will use multiple\n   letters. In this case, when you press the first letter, only hints\n   starting with that letter are displayed. Pressing the second letter will\n   select that hint or press :kbd:`Enter` or :kbd:`Space` to select the empty\n   hint.\n\nFor mouse lovers, the hints kitten also allows you to click on any matched text to\nselect it instead of typing the hint character.\n\nThe hints kitten is very powerful to see more detailed help on its various\noptions and modes of operation, see below. You can use these options to\ncreate mappings in :file:`kitty.conf` to select various different text\nsnippets. See :sc:`insert_selected_path <insert_selected_path>` for examples.\n\n\nCompletely customizing the matching and actions of the kitten\n---------------------------------------------------------------\n\nThe hints kitten supports writing simple Python scripts that can be used to\ncompletely customize how it finds matches and what happens when a match is\nselected. This allows the hints kitten to provide the user interface, while you\ncan provide the logic for finding matches and performing actions on them. This\nis best illustrated with an example. Create the file :file:`custom-hints.py` in\nthe :ref:`kitty config directory <confloc>` with the following contents:\n\n.. code-block:: python\n\n    import re\n\n    def mark(text, args, Mark, extra_cli_args, *a):\n        # This function is responsible for finding all\n        # matching text. extra_cli_args are any extra arguments\n        # passed on the command line when invoking the kitten.\n        # We mark all individual word for potential selection\n        for idx, m in enumerate(re.finditer(r'\\w+', text)):\n            start, end = m.span()\n            mark_text = text[start:end].replace('\\n', '').replace('\\0', '')\n            # The empty dictionary below will be available as groupdicts\n            # in handle_result() and can contain string keys and arbitrary JSON\n            # serializable values.\n            yield Mark(idx, start, end, mark_text, {})\n\n\n    def handle_result(args, data, target_window_id, boss, extra_cli_args, *a):\n        # This function is responsible for performing some\n        # action on the selected text.\n        # matches is a list of the selected entries and groupdicts contains\n        # the arbitrary data associated with each entry in mark() above\n        matches, groupdicts = [], []\n        for m, g in zip(data['match'], data['groupdicts']):\n            if m:\n                matches.append(m), groupdicts.append(g)\n        for word, match_data in zip(matches, groupdicts):\n            # Lookup the word in a dictionary, the open_url function\n            # will open the provided url in the system browser\n            boss.open_url(f'https://www.google.com/search?q=define:{word}')\n\nNow run kitty with::\n\n    kitty -o 'map f1 kitten hints --customize-processing custom-hints.py'\n\nWhen you press the :kbd:`F1` key you will be able to select a word to\nlook it up in the Google dictionary.\n\n.. include:: ../generated/cli-kitten-hints.rst\n\n.. note::\n\n    To avoid having to specify the same command line options on every\n    invocation, you can use the :opt:`action_alias` option in\n    :file:`kitty.conf`, creating aliases that have common sets of options.\n    For example::\n\n        action_alias myhints kitten hints --alphabet qfjdkslaureitywovmcxzpq1234567890\n        map f1 myhints --customize-processing custom-hints.py\n"
  },
  {
    "path": "docs/kittens/hyperlinked_grep.rst",
    "content": "Hyperlinked grep\n=================\n\n.. only:: man\n\n    Overview\n    --------------\n\n\n.. note::\n\n   As of ripgrep versions newer that 13.0 it supports hyperlinks\n   natively so you can just add the following alias in your shell rc file:\n   ``alias rg=\"rg --hyperlink-format=kitty\"`` no need to use this kitten.\n   But, see below for instructions on how to customize kitty to have it open\n   the hyperlinks from ripgrep in your favorite editor.\n\nThis kitten allows you to search your files using `ripgrep\n<https://github.com/BurntSushi/ripgrep>`__ and open the results directly in your\nfavorite editor in the terminal, at the line containing the search result,\nsimply by clicking on the result you want.\n\n.. versionadded:: 0.19.0\n\nTo set it up, first create :file:`~/.config/kitty/open-actions.conf` with the\nfollowing contents:\n\n.. code:: conf\n\n    # Open any file with a fragment in vim, fragments are generated\n    # by the hyperlink-grep kitten and nothing else so far.\n    protocol file\n    fragment_matches [0-9]+\n    action launch --type=overlay --cwd=current vim +${FRAGMENT} -- ${FILE_PATH}\n\n    # Open text files without fragments in the editor\n    protocol file\n    mime text/*\n    action launch --type=overlay --cwd=current -- ${EDITOR} -- ${FILE_PATH}\n\nNow, run a search with::\n\n    kitten hyperlinked-grep something\n\nHold down the :kbd:`Ctrl+Shift` keys and click on any of the result lines, to\nopen the file in :program:`vim` at the matching line. If you use some editor\nother than :program:`vim`, you should adjust the :file:`open-actions.conf` file\naccordingly. TO open links with the keyboard instead, use\n:sc:`open_selected_hyperlink`.\n\nFinally, add an alias to your shell's rc files to invoke the kitten as\n:command:`hg`::\n\n    alias hg=\"kitten hyperlinked-grep\"\n\n\nYou can now run searches with::\n\n    hg some-search-term\n\nTo learn more about kitty's powerful framework for customizing URL click\nactions, see :doc:`here </open_actions>`.\n\nBy default, this kitten adds hyperlinks for several parts of ripgrep output:\nthe per-file header, match context lines, and match lines. You can control\nwhich items are linked with a :code:`--kitten hyperlink` flag. For example,\n:code:`--kitten hyperlink=matching_lines` will only add hyperlinks to the\nmatch lines. :code:`--kitten hyperlink=file_headers,context_lines` will link\nfile headers and context lines but not match lines. :code:`--kitten\nhyperlink=none` will cause the command line to be passed to directly to\n:command:`rg` so no hyperlinking will be performed. :code:`--kitten hyperlink`\nmay be specified multiple times.\n\nHopefully, someday this functionality will make it into some `upstream grep\n<https://github.com/BurntSushi/ripgrep/issues/665>`__ program directly removing\nthe need for this kitten.\n\n\n.. note::\n   While you can pass any of ripgrep's command line options to the kitten and\n   they will be forwarded to :program:`rg`, do not use options that change the\n   output formatting as the kitten works by parsing the output from ripgrep.\n   The unsupported options are: :code:`--context-separator`,\n   :code:`--field-context-separator`, :code:`--field-match-separator`,\n   :code:`--json`, :code:`-I --no-filename`, :code:`-0 --null`,\n   :code:`--null-data`, :code:`--path-separator`. If you specify options via\n   configuration file, then any changes to the default output format will not be\n   supported, not just the ones listed.\n"
  },
  {
    "path": "docs/kittens/icat.rst",
    "content": "icat\n========================================\n\n.. only:: man\n\n    Overview\n    --------------\n\n\n*Display images in the terminal*\n\nThe ``icat`` kitten can be used to display arbitrary images in the |kitty|\nterminal. Using it is as simple as::\n\n    kitten icat image.jpeg\n\nIt supports all image types supported by `ImageMagick\n<https://www.imagemagick.org>`__. It even works over SSH. For details, see the\n:doc:`kitty graphics protocol </graphics-protocol>`.\n\nYou might want to create an alias in your shell's configuration files::\n\n   alias icat=\"kitten icat\"\n\nThen you can simply use ``icat image.png`` to view images.\n\n.. note::\n\n    `ImageMagick <https://www.imagemagick.org>`__ must be installed for the\n    full range of image types. Without it only PNG/JPG/GIF/BMP/TIFF/WEBP are\n    supported.\n\n.. note::\n\n    kitty's image display protocol may not work when used within a terminal\n    multiplexer such as :program:`screen` or :program:`tmux`, depending on\n    whether the multiplexer has added support for it or not.\n\n\n.. program:: kitty +kitten icat\n\n\nThe ``icat`` kitten has various command line arguments to allow it to be used\nfrom inside other programs to display images. In particular, :option:`--place`,\n:option:`--detect-support` and :option:`--print-window-size`.\n\nIf you are trying to integrate icat into a complex program like a file manager\nor editor, there are a few things to keep in mind. icat normally works by communicating\nover the TTY device, it both writes to and reads from the TTY. So it is\nimperative that while it is running the host program does not do any TTY I/O.\nAny key presses or other input from the user on the TTY device will be\ndiscarded. If you would instead like to use it just as a backend to generate\nthe escape codes for image display, you need to pass it options to tell it the\nwindow dimensions, where to place the image in the window and the transfer mode\nto use. If you do that, it will not try to communicate with the TTY device at\nall. The requisite options are: :option:`--use-window-size`, :option:`--place`\nand :option:`--transfer-mode`, :option:`--stdin=no`.\nFor example, to demonstrate usage without access to the TTY:\n\n.. code:: sh\n\n   zsh -c 'setsid kitten icat --stdin=no --use-window-size $COLUMNS,$LINES,3000,2000 --transfer-mode=file myimage.png'\n\nHere, ``setsid`` ensures icat has no access to the TTY device.\nThe values, 3000, 2000 are made up. They are the window width and height in\npixels, to obtain which access to the TTY is needed.\n\nTo be really robust you should consider writing proper support for the\n:doc:`kitty graphics protocol </graphics-protocol>` in the program instead.\nNowadays there are many libraries that have support for it.\n\n\n.. include:: /generated/cli-kitten-icat.rst\n"
  },
  {
    "path": "docs/kittens/notify.rst",
    "content": "notify\n==================================================\n\n.. only:: man\n\n    Overview\n    --------------\n\nShow pop-up system notifications.\n\n.. highlight:: sh\n\n.. versionadded:: 0.36.0\n   The notify kitten\n\nThe ``notify`` kitten can be used to show pop-up system notifications\nfrom the shell. It even works over SSH. Using it is as simple as::\n\n    kitten notify \"Good morning\" Hello world, it is a nice day!\n\nTo add an icon, use::\n\n    kitten notify --icon-path /path/to/some/image.png \"Good morning\" Hello world, it is a nice day!\n    kitten notify --icon firefox \"Good morning\" Hello world, it is a nice day!\n\n\nTo be informed when the notification is activated::\n\n    kitten notify --wait-for-completion \"Good morning\" Hello world, it is a nice day!\n\nThen, the kitten will wait till the notification is either closed or activated.\nIf activated, a ``0`` is printed to :file:`STDOUT`. You can press the\n:kbd:`Esc` or :kbd:`Ctrl+c` keys to abort, closing the notification.\n\nTo add buttons to the notification::\n\n    kitten notify --wait-for-completion --button One --button Two \"Good morning\" Hello world, it is a nice day!\n\n.. program:: kitty +kitten notify\n\n.. tip:: Learn about the underlying :doc:`/desktop-notifications` escape code protocol.\n\n.. include:: /generated/cli-kitten-notify.rst\n"
  },
  {
    "path": "docs/kittens/panel.rst",
    "content": "Draw a GPU accelerated dock panel on your desktop\n====================================================================================================\n\n.. highlight:: sh\n\n.. only:: man\n\n    Overview\n    --------------\n\n.. include:: ../quake-screenshots.rst\n\nDraw the desktop wallpaper or docks and panels using arbitrary\nterminal programs, For example, have `btop\n<https://github.com/aristocratos/btop>`__ or `cava\n<https://github.com/karlstav/cava/>`__ be your desktop wallpaper.\n\nIt is useful for showing status information or notifications on your desktop\nusing terminal programs instead of GUI toolkits.\n\n\nThe screenshot to the side shows some uses of the panel kitten to draw various\ndesktop components such as the background, a quick access floating terminal and\na dock panel showing system information (Linux only).\n\n.. versionadded:: 0.42.0\n\n   Support for macOS, see :ref:`compatibility matrix <panel_compat>` for details.\n   and X11 (background and overlay).\n\n.. versionadded:: 0.34.0\n\n   Support for Wayland. See :ref:`below <panel_compat>` for which\n   Wayland compositors work.\n\nUsing this kitten is simple, for example::\n\n    kitten panel sh -c 'printf \"\\n\\n\\nHello, world.\"; sleep 5s'\n\nThis will show ``Hello, world.`` at the top edge of your screen for five\nseconds. Here, the terminal program we are running is :program:`sh` with a script\nto print out ``Hello, world!``. You can make the terminal program as complex as\nyou like, as demonstrated in the screenshots.\n\nIf you are on Wayland or macOS, you can, for instance, run::\n\n    kitten panel --edge=background htop\n\nto display ``htop`` as your desktop background. Remember this works in everything\nbut GNOME and also, in sway, you have to disable the background wallpaper as\nsway renders that over the panel kitten surface.\n\nThere are projects that make use of this facility to implement generalised\npanels and desktop components:\n\n.. _panel_projects:\n\n    * `kitty panel <https://github.com/5hubham5ingh/kitty-panel>`__\n    * `pawbar <https://github.com/codelif/pawbar>`__\n\n\n.. _remote_control_panel:\n\nControlling panels via remote control\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou can control panels via the kitty :doc:`remote control </remote-control>` facility. Create a panel\nwith remote control enabled::\n\n    kitten panel -o allow_remote_control=socket-only --lines=2 \\\n        --listen-on=unix:/tmp/panel kitten run-shell\n\n\nNow you can control this panel using remote control, for example to show/hide\nit, use::\n\n    kitten @ --to=unix:/tmp/panel resize-os-window --action=toggle-visibility\n\nTo move the panel to the bottom of the screen and increase its height::\n\n    kitten @ --to=unix:/tmp/panel resize-os-window --action=os-panel \\\n        --incremental edge=bottom lines=4\n\nTo create a new panel running the program top, in the same instance\n(like creating a new OS window)::\n\n    kitten @ --to=unix:/tmp/panel launch --type=os-panel --os-panel edge=top \\\n        --os-panel lines=8 top\n\n\n.. include:: ../generated/cli-kitten-panel.rst\n\n\n.. _quake_ss:\n\nHow the screenshots were generated\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe system statistics in the background were created using::\n\n    kitten panel --edge=background -o background_opacity=0.2 -o background=black btop\n\nThis creates a kitty background window and inside it runs the `btop\n<https://github.com/aristocratos/btop>`__ program to display the statistics.\n\nThe floating quick access window was created by running::\n\n    kitten quick-access-terminal kitten run-shell \\\n       zsh -c 'printf \"\\e]66;s=4;Quick access kitty in Hyprland\\a\\n\\n\\n\\nAlso uses kitty to draw desktop background\\n\"'\n\nThis starts the quick access window and inside it runs ``kitten run-shell``, which\nin turn first runs ``zsh`` to print out the message and then starts the users login\nshell.\n\nThe Linux dock panel was::\n\n    wm bar\n\nThis is a custom program I wrote for my personal use. It uses kitty's kitten\ninfrastructure to implement the bar in a `few hundred lines of code\n<https://github.com/kovidgoyal/wm/blob/master/bar/main.go>`__.\nThis was designed for my personal use only, but, there are :ref:`public projects implementing\ngeneral purpose panels using kitty <panel_projects>`.\n\n\n.. _panel_compat:\n\nCompatibility with various platforms\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. only:: man\n\n   See the HTML documentation for the compatibility matrix.\n\n.. only:: not man\n\n    Generated with the help of the :file:`panels.py` test script.\n\n    .. tab:: Wayland\n\n        Below is a list of the status of various Wayland compositors. The panel kitten\n        relies on the `wlr layer shell protocol\n        <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#compositor-support>`__,\n        which is technically supported by almost all Wayland compositors, but the\n        implementation in some of them is quite buggy.\n\n        🟢 **Hyprland**\n           Fully working, no known issues\n\n        🟢 **labwc**\n           Fully working, no known issues\n\n        🟢 **niri**\n           Fully working, no known issues\n\n        🟢 **river**\n           Fully working, no known issues\n\n        🟢 **Xfce**\n           Fully working, no known issues\n\n        🟠 **KDE** (kwin)\n           Mostly working, except that clicks outside background panels cause kwin to :iss:`erroneously hide the panel <8715>`. KDE uses an `undocumented mapping <https://invent.kde.org/plasma/kwin/-/blob/3dc5cee6b34792486b343098e55e7f2b90dfcd00/src/layershellv1window.cpp#L24>`__ under Wayland to set the window type from the :code:`kitten panel --app-id` flag. You might want to use :code:`--app-id=dock` so that KDE treats the window as a dock panel, and disables window appearing/disappearing animations for it.\n\n        🟠 **Sway**\n           Renders its configured background over the background window instead of\n           under it. This is because it uses the wlr protocol for backgrounds itself.\n\n        🔴 **GNOME** (mutter)\n           Does not implement the wlr protocol at all, nothing works.\n\n    .. tab:: macOS\n\n        Mostly everything works, with the notable exception that dock panels do not\n        prevent other windows from covering them. This is because Apple does not\n        provide and way to do this in their APIs.\n\n    .. tab:: X11\n\n        Support is highly dependent on the quirks of individual window\n        managers. See the matrix below:\n\n        .. list-table:: Compatibility matrix\n           :header-rows: 1\n           :stub-columns: 1\n\n           * - WM\n             - Desktop\n             - Dock\n             - Quick\n             - Notes\n\n           * - KDE\n             - 🟠\n             - 🟢\n             - 🟢\n             - transparency does not work for :option:`--edge=background <--edge>`\n\n           * - GNOME\n             - 🟢\n             - 🟢\n             - 🟢\n             -\n\n           * - XFCE\n             - 🟢\n             - 🟢\n             - 🟢\n             -\n\n           * - i3\n             - 🔴\n             - 🟠\n             - 🔴\n             - only top and bottom dock panels, without transparency\n\n           * - xmonad\n             - 🔴\n             - 🔴\n             - 🔴\n             - doesn't support the needed NET_WM protocols\n"
  },
  {
    "path": "docs/kittens/query_terminal.rst",
    "content": "Query terminal\n=================\n\n.. only:: man\n\n    Overview\n    --------------\n\n\nThis kitten is used to query |kitty| from terminal programs about version, values\nof various runtime options controlling its features, etc.\n\nThe querying is done using the (*semi*) standard XTGETTCAP escape sequence\npioneered by xterm, so it works over SSH as well. The downside is that it is\nslow, since it requires a roundtrip to the terminal emulator and back.\n\nIf you want to do some of the same querying in your terminal program without\ndepending on the kitten, you can do so, by processing the same escape codes.\nSearch `this page <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html>`__\nfor *XTGETTCAP* to see the syntax for the escape code. The kitty specific keys\nare all documented below, when sent via escape code they must be prefixed with\n``kitty-query-``.\n\n\n.. include:: ../generated/cli-kitten-query_terminal.rst\n"
  },
  {
    "path": "docs/kittens/quick-access-terminal.rst",
    "content": ".. _quake:\n\nMake a Quake like quick access terminal\n====================================================================================================\n\n.. highlight:: sh\n\n.. only:: man\n\n    Overview\n    --------------\n\n\n.. include:: ../quake-screenshots.rst\n\n.. versionadded:: 0.42.0\n   See :ref:`here for what platforms it works on <panel_compat>`.\n\nThis kitten can be used to make a quick access terminal, that appears and\ndisappears at a key press. To do so use the following command:\n\n.. code-block:: sh\n\n    kitten quick-access-terminal\n\nRun this command in a terminal, and a quick access kitty window will show up at\nthe top of your screen. Run it again, and the window will be hidden.\n\nTo make the terminal appear and disappear at a key press:\n\n.. |macOs| replace:: :guilabel:`System Preferences->Keyboard->Keyboard Shortcuts->Services->General`\n\n.. only:: not man\n\n    .. tab:: Linux\n\n        Simply bind the above command to some key press in your window manager or desktop\n        environment settings and then you have a quick access terminal at a single key press.\n\n    .. tab:: macOS\n\n        In kitty, run the above command to show the quick access window, then close\n        it by running the command again or pressing :kbd:`ctrl+d`. Now go to |macOS| and set a shortcut for\n        the :guilabel:`Quick access to kitty` entry.\n\n.. only:: man\n\n    In Linux, simply assign the above command to a global shortcut in your\n    window manager. In macOS, go to |macOS| and set a shortcut\n    for the :guilabel:`Quick access to kitty` entry.\n\nConfiguration\n------------------------\n\nYou can configure the appearance and behavior of the quick access window\nby creating a :file:`quick-access-terminal.conf` file in your\n:ref:`kitty config folder <confloc>`. In particular, you can use the\n:opt:`kitty_conf <kitten-quick_access_terminal.kitty_conf>` option to change\nvarious kitty settings, just for the quick access window.\n\n.. note::\n\n   This kitten uses the :doc:`panel kitten </kittens/panel>` under the\n   hood. You can use the :ref:`techniques described there <remote_control_panel>`\n   for remote controlling the quick access window, remember to add\n   ``kitty_override allow_remote_control=socket-only`` and ``kitty_override\n   listen_on=unix:/tmp/whatever`` to\n   :file:`quick-access-terminal.conf`.\n\nSee below for the supported configuration directives:\n\n\n.. include:: /generated/conf-kitten-quick_access_terminal.rst\n\n\n.. include:: /generated/cli-kitten-quick_access_terminal.rst\n\n\nSample quick-access-terminal.conf\n---------------------------------------\n\nYou can download a sample :file:`quick-access-terminal.conf` file with all default settings and\ncomments describing each setting by clicking: :download:`sample quick-access-terminal.conf\n</generated/conf/quick_access_terminal.conf>`.\n"
  },
  {
    "path": "docs/kittens/remote_file.rst",
    "content": "Remote files\n==============\n\n.. only:: man\n\n    Overview\n    --------------\n\n\n|kitty| has the ability to easily *Edit*, *Open* or *Download* files from a\ncomputer into which you are SSHed. In your SSH session run::\n\n    ls --hyperlink=auto\n\nThen hold down :kbd:`Ctrl+Shift` and click the name of the file.\n\n.. figure:: ../screenshots/remote_file.png\n    :alt: Remote file actions\n    :align: center\n    :width: 100%\n\n    Remote file actions\n\n|kitty| will ask you what you want to do with the remote file. You can choose\nto *Edit* it in which case kitty will download it and open it locally in your\n:envvar:`EDITOR`. As you make changes to the file, they are automatically\ntransferred to the remote computer. Note that this happens without needing\nto install *any* special software on the server, beyond :program:`ls` that\nsupports hyperlinks.\n\n.. seealso:: See the :ref:`edit-in-kitty <edit_file>` command\n\n.. seealso:: See the :doc:`transfer` kitten\n\n.. versionadded:: 0.19.0\n\n.. note::\n   For best results, use this kitten with the :doc:`ssh kitten <./ssh>`.\n   Otherwise, nested SSH sessions are not supported. The kitten will always try to copy\n   remote files from the first SSH host. This is because, without the ssh\n   kitten, there is no way for\n   |kitty| to detect and follow a nested SSH session robustly. Use the\n   :doc:`transfer` kitten for such situations.\n\n.. note::\n   If you have not setup automatic password-less SSH access, and are not using\n   the ssh kitten, then, when editing\n   starts you will be asked to enter your password just once, thereafter the SSH\n   connection will be re-used.\n\nSimilarly, you can choose to save the file to the local computer or download\nand open it in its default file handler.\n"
  },
  {
    "path": "docs/kittens/ssh.rst",
    "content": "Truly convenient SSH\n=========================================\n\n.. only:: man\n\n   Overview\n   ----------------\n\n* Automatic :ref:`shell_integration` on remote hosts\n\n* Easily :ref:`clone local shell/editor config <real_world_ssh_kitten_config>` on remote hosts\n\n* Automatic :opt:`re-use of existing connections <kitten-ssh.share_connections>` to avoid connection setup latency\n\n* Make the kitten binary available in the remote host :opt:`on demand <kitten-ssh.remote_kitty>`\n\n* Easily :opt:`change terminal colors <kitten-ssh.color_scheme>` when connecting to remote hosts\n\n* Automatically :opt:`forward the kitty remote control socket <kitten-ssh.forward_remote_control>` to configured hosts\n\n.. versionadded:: 0.25.0\n   Automatic shell integration, file transfer and reuse of connections\n\n.. versionadded:: 0.30.0\n   Automatic forwarding of remote control sockets\n\nThe ssh kitten allows you to login easily to remote hosts, and automatically\nsetup the environment there to be as comfortable as your local shell. You can\nspecify environment variables to set on the remote host and files to copy there,\nmaking your remote experience just like your local shell. Additionally, it\nautomatically sets up :ref:`shell_integration` on the remote host and copies the\nkitty terminfo database there.\n\nThe ssh kitten is a thin wrapper around the traditional `ssh <https://man.openbsd.org/ssh>`__\ncommand line program and supports all the same options and arguments and configuration.\nIn interactive usage scenarios it is a drop in replacement for :program:`ssh`.\nTo try it out, simply run:\n\n.. code-block:: sh\n\n    kitten ssh some-hostname-to-connect-to\n\nYou should end up at a shell prompt on the remote host, with shell integration\nenabled. If you like it you can add an alias to it in your shell's rc files:\n\n.. code-block:: sh\n\n    alias s=\"kitten ssh\"\n\nSo now you can just type ``s hostname`` to connect.\n\nIf you define a mapping in :file:`kitty.conf` such as::\n\n    map f1 new_window_with_cwd\n\nThen, pressing :kbd:`F1` will open a new window automatically logged into the\nsame host using the ssh kitten, at the same directory.\n\nThe ssh kitten can be configured using the :file:`~/.config/kitty/ssh.conf` file\nwhere you can specify environment variables to set on the remote host and files\nto copy from the local to the remote host. Let's see a quick example:\n\n.. code-block:: conf\n\n   # Copy the files and directories needed to setup some common tools\n   copy .zshrc .vimrc .vim\n   # Setup some environment variables\n   env SOME_VAR=x\n   # COPIED_VAR will have the same value on the remote host as it does locally\n   env COPIED_VAR=_kitty_copy_env_var_\n\n   # Create some per hostname settings\n   hostname someserver-*\n   copy env-files\n   env SOMETHING=else\n\n   hostname someuser@somehost\n   copy --dest=foo/bar some-file\n   copy --glob some/files.*\n\n\nSee below for full details on the syntax and options of :file:`ssh.conf`.\nAdditionally, you can pass config options on the command line:\n\n.. code-block:: sh\n\n   kitten ssh --kitten interpreter=python servername\n\nThe :code:`--kitten` argument can be specified multiple times, with directives\nfrom :file:`ssh.conf`. These override the final options used for the matched host, as if they\nhad been appended to the end of the matching section for that host in\n:file:`ssh.conf`. They apply only to the host being SSHed to by this invocation,\nso any :opt:`hostname <kitten-ssh.hostname>` directives are ignored.\n\n.. warning::\n\n   Due to limitations in the design of SSH, any typing you do before the\n   shell prompt appears may be lost. So ideally don't start typing till you see\n   the shell prompt. 😇\n\n\n.. _real_world_ssh_kitten_config:\n\nA real world example\n----------------------\n\nSuppose you often SSH into a production server, and you would like to setup\nyour shell and editor there using your custom settings. However, other people\ncould SSH in as well and you don't want to clobber their settings. Here is how\nthis could be achieved using the ssh kitten with :program:`zsh` and\n:program:`vim` as the shell and editor, respectively:\n\n.. code-block:: conf\n\n   # Have these settings apply to servers in my organization\n   hostname myserver-*\n\n   # Setup zsh to read its files from my-conf/zsh\n   env ZDOTDIR=$HOME/my-conf/zsh\n   copy --dest my-conf/zsh/.zshrc .zshrc\n   copy --dest my-conf/zsh/.zshenv .zshenv\n   # If you use other zsh init files add them in a similar manner\n\n   # Setup vim to read its config from my-conf/vim\n   env VIMINIT=$HOME/my-conf/vim/vimrc\n   env VIMRUNTIME=$HOME/my-conf/vim\n   copy --dest my-conf/vim .vim\n   copy --dest my-conf/vim/vimrc .vimrc\n\n\nHow it works\n----------------\n\nThe ssh kitten works by having SSH transmit and execute a POSIX sh (or\n:opt:`optionally <kitten-ssh.interpreter>` Python) bootstrap script on the\nremote host using an :opt:`interpreter <kitten-ssh.interpreter>`. This script\nreads setup data over the TTY device, which kitty sends as a Base64 encoded\ncompressed tarball. The script extracts it and places the :opt:`files <kitten-ssh.copy>`\nand sets the :opt:`environment variables <kitten-ssh.env>` before finally\nlaunching the :opt:`login shell <kitten-ssh.login_shell>` with :opt:`shell\nintegration <kitten-ssh.shell_integration>` enabled. The data is requested by\nthe kitten over the TTY with a random one time password. kitty reads the request\nand if the password matches a password pre-stored in shared memory on the\nlocalhost by the kitten, the transmission is allowed. If your local\n`OpenSSH <https://www.openssh.com/>`__ version is >= 8.4 then the data is\ntransmitted instantly without any roundtrip delay.\n\n.. note::\n\n   When connecting to BSD hosts, it is possible the bootstrap script will fail\n   or run slowly, because the default shells are crippled in various ways.\n   Your best bet is to install Python on the remote, make sure the login shell\n   is something POSIX sh compliant, and use :code:`python` as the\n   :opt:`interpreter <kitten-ssh.interpreter>` in :file:`ssh.conf`.\n\n\n.. note::\n\n   This may or may not work when using terminal multiplexers, depending on\n   whether they passthrough the escape codes and if the values of the\n   environment variables :envvar:`KITTY_PID` and :envvar:`KITTY_WINDOW_ID` are\n   correct in the current session (they can be wrong when connecting to a tmux\n   session running in a different window) and the ssh kitten is run in the\n   currently active multiplexer window.\n\n.. include:: /generated/conf-kitten-ssh.rst\n\n\n.. _ssh_copy_command:\n\nThe copy command\n--------------------\n\n.. include:: /generated/ssh-copy.rst\n\n\n.. _manual_terminfo_copy:\n\nCopying terminfo files manually\n-------------------------------------\n\nSometimes, the ssh kitten can fail, or maybe you dont like to use it. In such\ncases, the terminfo files can be copied over manually to a server with the\nfollowing one liner::\n\n    infocmp -a xterm-kitty | ssh myserver tic -x -o \\~/.terminfo /dev/stdin\n\nIf you are behind a proxy (like Balabit) that prevents this, or you are SSHing\ninto macOS where the :program:`tic` does not support reading from :file:`STDIN`,\nyou must redirect the first command to a file, copy that to the server and run :program:`tic`\nmanually. If you connect to a server, embedded, or Android system that doesn't\nhave :program:`tic`, copy over your local file terminfo to the other system as\n:file:`~/.terminfo/x/xterm-kitty`.\n\nIf the server is running a relatively modern Linux distribution and you have\nroot access to it, you could simply install the ``kitty-terminfo`` package on\nthe server to make the terminfo files available.\n\nReally, the correct solution for this is to convince the OpenSSH maintainers to\nhave :program:`ssh` do this automatically, if possible, when connecting to a\nserver, so that all terminals work transparently.\n\nIf the server is running FreeBSD, or another system that relies on termcap\nrather than terminfo, you will need to convert the terminfo file on your local\nmachine by running (on local machine with |kitty|)::\n\n    infocmp -CrT0 xterm-kitty\n\nThe output of this command is the termcap description, which should be appended\nto :file:`/usr/share/misc/termcap` on the remote server. Then run the following\ncommand to apply your change (on the server)::\n\n    cap_mkdb /usr/share/misc/termcap\n"
  },
  {
    "path": "docs/kittens/themes.rst",
    "content": "Changing kitty colors\n========================\n\n.. only:: man\n\n    Overview\n    --------------\n\n\nThe themes kitten allows you to easily change color themes, from a collection of\nover three hundred pre-built themes available at `kitty-themes\n<https://github.com/kovidgoyal/kitty-themes>`_. To use it, simply run::\n\n    kitten themes\n\n\n.. image:: ../screenshots/themes.png\n   :alt: The themes kitten in action\n   :width: 600\n\nThe kitten allows you to pick a theme, with live previews of the colors. You can\nchoose between light and dark themes and search by theme name by just typing a\nfew characters from the name.\n\nThe kitten maintains a list of recently used themes to allow quick switching.\n\nIf you want to restore the colors to default, you can do so by choosing the\n``Default`` theme.\n\n.. versionadded:: 0.23.0\n   The themes kitten\n\n\nHow it works\n----------------\n\nA theme in kitty is just a :file:`.conf` file containing kitty settings.\nWhen you select a theme, the kitten simply copies the :file:`.conf` file\nto :file:`~/.config/kitty/current-theme.conf` and adds an include for\n:file:`current-theme.conf` to :file:`kitty.conf`. It also comments out any\nexisting color settings in :file:`kitty.conf` so they do not interfere.\n\nOnce that's done, the kitten sends kitty a signal to make it reload its config.\n\n\n.. note::\n\n   If you want to have some color settings in your :file:`kitty.conf` that the\n   theme kitten does not override, move them into a separate conf file and\n   ``include`` it into kitty.conf. The include should be placed after the\n   inclusion of :file:`current-theme.conf` so that the settings in it override\n   conflicting settings from :file:`current-theme.conf`.\n\n\n.. _auto_color_scheme:\n\nChange color themes automatically when the OS switches between light and dark\n--------------------------------------------------------------------------------\n\n.. versionadded:: 0.38.0\n\nYou can have kitty automatically change its color theme when the OS switches\nbetween dark, light and no-preference modes. In order to do this, run the theme\nkitten as normal and at the final screen select the option to save your chosen\ntheme as either light, dark, or no-preference. Repeat until you have chosen\na theme for each of the three modes. Then, once you restart kitty, it will\nautomatically use your chosen themes depending on the OS color scheme.\n\nThis works by creating three files: :file:`dark-theme.auto.conf`,\n:file:`light-theme.auto.conf` and :file:`no-preference-theme.auto.conf` in the\nkitty config directory. When these files exist, kitty queries the OS for its color scheme\nand uses the appropriate file. Note that the colors in these files override all other\ncolors, and also all background image settings,\neven those specified using the :option:`kitty --override` command line flag.\nkitty will also automatically change colors when the OS color scheme changes,\nfor example, during night/day transitions.\n\nWhen using these colors, you can still dynamically change colors, but the next\ntime the OS changes its color mode, any dynamic changes will be overridden.\n\n\n.. note::\n\n   On the GNOME desktop, the desktop reports the color preference as no-preference\n   when the \"Dark style\" is not enabled. So use :file:`no-preference-theme.auto.conf` to\n   select colors for light mode on GNOME. You can manually enable light style\n   with ``gsettings set org.gnome.desktop.interface color-scheme prefer-light``\n   in which case GNOME will report the color scheme as light and kitty will use\n   :file:`light-theme.auto.conf`.\n\n\nUsing your own themes\n-----------------------\n\nYou can also create your own themes as :file:`.conf` files. Put them in the\n:file:`themes` sub-directory of the :ref:`kitty config directory <confloc>`,\nusually, :file:`~/.config/kitty/themes`. The kitten will automatically add them\nto the list of themes. You can use this to modify the builtin themes, by giving\nthe conf file the name :file:`Some theme name.conf` to override the builtin\ntheme of that name. Here, ``Some theme name`` is the actual builtin theme name, not\nits file name. Note that after doing so you have to run the kitten and\nchoose that theme once for your changes to be applied.\n\n\nContributing new themes\n-------------------------\n\nIf you wish to contribute a new theme to the kitty theme repository, start by\ngoing to the `kitty-themes <https://github.com/kovidgoyal/kitty-themes>`__\nrepository. `Fork it\n<https://docs.github.com/en/get-started/quickstart/fork-a-repo>`__, and use the\nfile :download:`template.conf\n<https://github.com/kovidgoyal/kitty-themes/raw/master/template.conf>` as a\ntemplate when creating your theme. Once you are satisfied with how it looks,\n`submit a pull request\n<https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request>`__\nto have your theme merged into the `kitty-themes\n<https://github.com/kovidgoyal/kitty-themes>`__ repository, which will make it\navailable in this kitten automatically.\n\n\nChanging the theme non-interactively\n---------------------------------------\n\nYou can specify the theme name as an argument when invoking the kitten to have\nit change to that theme instantly. For example::\n\n    kitten themes --reload-in=all Dimmed Monokai\n\nWill change the theme to ``Dimmed Monokai`` in all running kitty instances. See\nbelow for more details on non-interactive operation.\n\n.. include:: ../generated/cli-kitten-themes.rst\n"
  },
  {
    "path": "docs/kittens/transfer.rst",
    "content": "Transfer files\n================\n\n.. only:: man\n\n    Overview\n    --------------\n\n\n.. versionadded:: 0.30.0\n\n.. _rsync: https://en.wikipedia.org/wiki/Rsync\n\nTransfer files to and from remote computers over the ``TTY`` device itself.\nThis means that file transfer works over nested SSH sessions, serial links,\netc. Anywhere you have a terminal device, you can transfer files.\n\n.. image:: ../screenshots/transfer.png\n   :alt: The transfer kitten at work\n\nThis kitten supports transferring entire directory trees, preserving soft and\nhard links, file permissions, times, etc. It even supports the rsync_ protocol\nto transfer only changes to large files.\n\n.. seealso:: See the :doc:`remote_file` kitten\n\nBasic usage\n---------------\n\nSimply ssh into a remote computer using the :doc:`ssh kitten </kittens/ssh>` and run the this kitten\n(which the ssh kitten makes available for you on the remote computer\nautomatically). Some illustrative examples are below. To copy a file from a\nremote computer::\n\n    <local computer>  $ kitten ssh my-remote-computer\n    <remote computer> $ kitten transfer some-file /path/on/local/computer\n\nThis, will copy :file:`some-file` from the computer into which you have SSHed\nto your local computer at :file:`/path/on/local/computer`. kitty will ask you\nfor confirmation before allowing the transfer, so that the file transfer\nprotocol cannot be abused to read/write files on your computer.\n\nTo copy a file from your local computer to the remote computer::\n\n    <local computer>  $ kitten ssh my-remote-computer\n    <remote computer> $ kitten transfer --direction=upload /path/on/local/computer remote-file\n\nFor more detailed usage examples, see the command line interface section below.\n\n.. note::\n   If you dont want to use the ssh kitten, you can install the kitten binary on\n   the remote machine yourself, it is a standalone, statically compiled binary\n   available from the `kitty releases page\n   <https://github.com/kovidgoyal/kitty/releases>`__. Or you can write your own\n   script/program to use the underlying :doc:`file transfer protocol\n   </file-transfer-protocol>`.\n\nAvoiding the confirmation prompt\n------------------------------------\n\nNormally, when you start a file transfer kitty will prompt you for confirmation.\nThis is to ensure that hostile programs running on a remote machine cannot\nread/write files on your computer without your permission. If the remote machine\nis trusted, then you can disable the confirmation prompt by:\n\n#. Setting the :opt:`file_transfer_confirmation_bypass` option to some password.\n\n#. When invoking the kitten use the :option:`--permissions-bypass\n   <kitty +kitten transfer --permissions-bypass>` to supply the password you set\n   in step one.\n\n.. warning:: Using a password to bypass confirmation means any software running\n   on the remote machine could potentially learn that password and use it to\n   gain full access to your computer.\n\n\nDelta transfers\n-----------------------------------\n\nThis kitten has the ability to use the rsync_ protocol to only transfer the\ndifferences between files. To turn it on use the :option:`--transmit-deltas\n<kitty +kitten transfer --transmit-deltas>` option. Note that this will\nactually be slower when transferring small files or on a very fast network, because\nof round trip overhead, so use with care.\n\n\n.. include:: ../generated/cli-kitten-transfer.rst\n"
  },
  {
    "path": "docs/kittens/unicode_input.rst",
    "content": "Unicode input\n================\n\n.. only:: man\n\n    Overview\n    --------------\n\n\nYou can input Unicode characters by name, hex code, recently used and even an\neditable favorites list. Press :sc:`input_unicode_character` to start the\nunicode input kitten, shown below.\n\n.. figure:: ../screenshots/unicode.png\n    :alt: A screenshot of the unicode input kitten\n    :align: center\n    :width: 100%\n\n    A screenshot of the unicode input kitten\n\nIn :guilabel:`Code` mode, you enter a Unicode character by typing in the hex\ncode for the character and pressing :kbd:`Enter`. For example, type in ``2716``\nand press :kbd:`Enter` to get ``✖``. You can also choose a character from the\nlist of recently used characters by typing a leading period ``.`` and then the\ntwo character index and pressing :kbd:`Enter`.\nThe :kbd:`Up` and :kbd:`Down` arrow keys can be used to choose the previous and\nnext Unicode symbol respectively.\n\nIn :guilabel:`Name` mode you instead type words from the character name and use\nthe :kbd:`ArrowKeys` / :kbd:`Tab` to select the character from the displayed\nmatches. You can also type a space followed by a period and the index for the\nmatch if you don't like to use arrow keys.\n\nYou can switch between modes using either the keys :kbd:`F1` ... :kbd:`F4` or\n:kbd:`Ctrl+1` ... :kbd:`Ctrl+4` or by pressing :kbd:`Ctrl+[` and :kbd:`Ctrl+]`\nor by pressing :kbd:`Ctrl+Tab` and :kbd:`Ctrl+Shift+Tab`.\n\n\n.. include:: ../generated/cli-kitten-unicode_input.rst\n"
  },
  {
    "path": "docs/kittens_intro.rst",
    "content": ".. _kittens:\n\nExtend with kittens\n-----------------------\n\n.. toctree::\n   :hidden:\n   :glob:\n\n   kittens/icat\n   kittens/diff\n   kittens/unicode_input\n   kittens/themes\n   kittens/choose-fonts\n   kittens/hints\n   kittens/quick-access-terminal\n   kittens/choose-files\n   kittens/panel\n   kittens/remote_file\n   kittens/hyperlinked_grep\n   kittens/transfer\n   kittens/ssh\n   kittens/custom\n   kittens/*\n\n|kitty| has a framework for easily creating terminal programs that make use of\nits advanced features. These programs are called kittens. They are used both to\nadd features to |kitty| itself and to create useful standalone programs.\nSome prominent kittens:\n\n:doc:`icat <kittens/icat>`\n    Display images in the terminal.\n\n\n:doc:`diff <kittens/diff>`\n    A fast, side-by-side diff for the terminal with syntax highlighting and\n    images.\n\n\n:doc:`Unicode input <kittens/unicode_input>`\n    Easily input arbitrary Unicode characters in |kitty| by name or hex code.\n\n\n:doc:`Themes <kittens/themes>`\n    Preview and quick switch between over three hundred color themes.\n\n\n:doc:`Fonts <kittens/choose-fonts>`\n    Preview, fine-tune and quick switch the fonts used by kitty.\n\n\n:doc:`Hints <kittens/hints>`\n    Select and open/paste/insert arbitrary text snippets such as URLs,\n    filenames, words, lines, etc. from the terminal screen.\n\n\n:doc:`Quick access terminal <kittens/quick-access-terminal>`\n    Get access to a quick access floating, semi-transparent kitty window\n    with a single keypress.\n\n\n:doc:`Panel <kittens/panel>`\n    Draw the desktop wallpaper or docks and panels using arbitrary\n    terminal programs.\n\n\n:doc:`Choose files <kittens/choose-files>`\n    Preview and select files at the speed of thought\n\n\n:doc:`Remote file <kittens/remote_file>`\n    Edit, open, or download remote files over SSH easily, by simply clicking on\n    the filename.\n\n\n:doc:`Transfer files <kittens/transfer>`\n    Transfer files and directories seamlessly and easily from remote machines\n    over your existing SSH sessions with a simple command.\n\n\n:doc:`Hyperlinked grep <kittens/hyperlinked_grep>`\n    Search your files using `ripgrep <https://github.com/BurntSushi/ripgrep>`__\n    and open the results directly in your favorite editor in the terminal,\n    at the line containing the search result, simply by clicking on the result\n    you want.\n\n\n:doc:`Broadcast <kittens/broadcast>`\n    Type in one :term:`kitty window <window>` and have it broadcast to all (or a\n    subset) of other :term:`kitty windows <window>`.\n\n\n:doc:`SSH <kittens/ssh>`\n    SSH with automatic :ref:`shell integration <shell_integration>`, connection\n    re-use for low latency and easy cloning of local shell and editor\n    configuration to the remote host.\n\n\n:doc:`Clipboard <kittens/clipboard>`\n    Copy/paste to the clipboard from shell scripts, even over SSH.\n\nYou can also :doc:`Learn to create your own kittens <kittens/custom>`.\n"
  },
  {
    "path": "docs/launch.rst",
    "content": "The :command:`launch` command\n--------------------------------\n\n.. program:: launch\n\n\n|kitty| has a :code:`launch` action that can be used to run arbitrary programs\nin new windows/tabs. It can be mapped to user defined shortcuts in\n:file:`kitty.conf`. It is very powerful and allows sending the contents of the\ncurrent window to the launched program, as well as many other options.\n\nIn the simplest form, you can use it to open a new kitty window running the\nshell, as shown below::\n\n    map f1 launch\n\nTo run a different program simply pass the command line as arguments to launch::\n\n    map f1 launch vim path/to/some/file\n\nTo open a new window with the same working directory as the currently active\nwindow::\n\n    map f1 launch --cwd=current\n\nTo open the new window in a new tab::\n\n    map f1 launch --type=tab\n\nTo run multiple commands in a shell, use::\n\n    map f1 launch sh -c \"ls && exec zsh\"\n\nTo pass the contents of the current screen and scrollback to the started\nprocess::\n\n    map f1 launch --stdin-source=@screen_scrollback less\n\nThere are many more powerful options, refer to the complete list below.\n\n.. note::\n    To avoid duplicating launch actions with frequently used parameters, you can\n    use :opt:`action_alias` to define launch action aliases. For example::\n\n        action_alias launch_tab launch --cwd=current --type=tab\n        map f1 launch_tab vim\n        map f2 launch_tab emacs\n\n    The :kbd:`F1` key will now open :program:`vim` in a new tab with the current\n    windows working directory.\n\n\nThe piping environment\n--------------------------\n\nWhen using :option:`launch --stdin-source`, the program to which the data is\npiped has a special environment variable declared, :envvar:`KITTY_PIPE_DATA`\nwhose contents are::\n\n   KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}\n\nwhere ``scrolled_by`` is the number of lines kitty is currently scrolled by,\n``cursor_(x|y)`` is the position of the cursor on the screen with ``(1,1)``\nbeing the top left corner and ``{lines},{columns}`` being the number of rows\nand columns of the screen.\n\n\nSpecial arguments\n-------------------\n\nThere are a few special placeholder arguments that can be specified as part of\nthe command line:\n\n\n``@selection``\n    Replaced by the currently selected text.\n\n``@active-kitty-window-id``\n    Replaced by the id of the currently active kitty window.\n\n``@line-count``\n    Replaced by the number of lines in STDIN. Only present when passing some\n    data to STDIN.\n\n``@input-line-number``\n    Replaced by the number of lines a pager should scroll to match the current\n    scroll position in kitty. See :opt:`scrollback_pager` for details.\n\n``@scrolled-by``\n    Replaced by the number of lines kitty is currently scrolled by.\n\n``@cursor-x``\n    Replaced by the current cursor x position with 1 being the leftmost cell.\n\n``@cursor-y``\n    Replaced by the current cursor y position with 1 being the topmost cell.\n\n``@first-line-on-screen``\n    Replaced by the first line on screen. Can be used for pager positioning.\n\n``@last-line-on-screen``\n    Replaced by the last line on screen. Can be used for pager positioning.\n\n\nFor example::\n\n    map f1 launch my-program @active-kitty-window-id\n\n\n.. _watchers:\n\nWatching launched windows\n---------------------------\n\nThe :option:`launch --watcher` option allows you to specify Python functions\nthat will be called at specific events, such as when the window is resized or\nclosed. Note that you can also specify watchers that are loaded for all windows,\nvia :opt:`watcher`. To create a watcher, specify the path to a Python module\nthat specifies callback functions for the events you are interested in, for\ncreate :file:`~/.config/kitty/mywatcher.py` and use :option:`launch --watcher` = :file:`mywatcher.py`:\n\n.. code-block:: python\n\n    # ~/.config/kitty/mywatcher.py\n    from typing import Any\n\n    from kitty.boss import Boss\n    from kitty.window import Window\n\n\n    def on_load(boss: Boss, data: dict[str, Any]) -> None:\n        # This is a special function that is called just once when this watcher\n        # module is first loaded, can be used to perform any initialization/one\n        # time setup. Any exceptions in this function are printed to kitty's\n        # STDERR but otherwise ignored.\n        ...\n\n    def on_resize(boss: Boss, window: Window, data: dict[str, Any]) -> None:\n        # Here data will contain old_geometry and new_geometry\n        # Note that resize is also called the first time a window is created\n        # which can be detected as old_geometry will have all zero values, in\n        # particular, old_geometry.xnum and old_geometry.ynum will be zero.\n        ...\n\n    def on_focus_change(boss: Boss, window: Window, data: dict[str, Any])-> None:\n        # Here data will contain focused\n        ...\n\n    def on_close(boss: Boss, window: Window, data: dict[str, Any])-> None:\n        # called when window is closed, typically when the program running in\n        # it exits\n        ...\n\n    def on_set_user_var(boss: Boss, window: Window, data: dict[str, Any]) -> None:\n        # called when a \"user variable\" is set or deleted on a window. Here\n        # data will contain key and value\n        ...\n\n    def on_title_change(boss: Boss, window: Window, data: dict[str, Any]) -> None:\n        # called when the window title is changed on a window. Here\n        # data will contain title and from_child. from_child will be True\n        # when a title change was requested via escape code from the program\n        # running in the terminal\n        ...\n\n    def on_cmd_startstop(boss: Boss, window: Window, data: dict[str, Any]) -> None:\n        # called when the shell starts/stops executing a command. Here\n        # data will contain is_start, cmdline and time.\n        ...\n\n    def on_color_scheme_preference_change(boss: Boss, window: Window, data: dict[str, Any]) -> None:\n        # called when the color scheme preference of this window changes from\n        # light to dark or vice versa. data contains is_dark and via_escape_code\n        # the latter will be true if the color scheme was changed via escape\n        # code received from the program running in the window\n        ...\n\n    def on_tab_bar_dirty(boss: Boss, window: Window, data: dict[str, Any]) -> None:\n        # called when any changes happen to the tab bar, such a new tabs being\n        # created, tab titles changing, tabs moving, etc. Useful to display the\n        # tab bar externally to kitty. This is called even if the tab bar is\n        # hidden. Note that this is called only in *global watchers*, that is\n        # watchers defined in kitty.conf or using the --watcher command line\n        # flag. data contains tab_manager which is the object responsible for\n        # managing all tabs in a single OS Window.\n        ...\n\n    def on_quit(boss: Boss, window: Window, data: dict[str, Any]) -> None:\n        # called when kitty is about to quit. This is called in *global watchers*\n        # only. It is called twice: once before the quit confirmation dialog is\n        # shown (data['confirmed'] will be False) and once after the user has\n        # confirmed quitting (data['confirmed'] will be True). Setting\n        # data['aborted'] to True will abort the quit in both cases.\n        ...\n\n\nEvery callback is passed a reference to the global ``Boss`` object as well as\nthe ``Window`` object the action is occurring on. The ``data`` object is a dict\nthat contains event dependent data. You have full access to kitty internals in\nthe watcher scripts, however kitty internals are not documented/stable so for\nmost things you are better off using the kitty :doc:`Remote control API </remote-control>`.\nSimply call :code:`boss.call_remote_control()`, with the same arguments you\nwould pass to ``kitten @``. For example:\n\n.. code-block:: python\n\n    def on_resize(boss: Boss, window: Window, data: dict[str, Any]) -> None:\n        # send some text to the resized window\n        boss.call_remote_control(window, ('send-text', f'--match=id:{window.id}', 'hello world'))\n\nRun, ``kitten @ --help`` in a kitty terminal, to see all the remote control\ncommands available to you.\n\n\nFinding executables\n-----------------------\n\nWhen you specify a command to run as just a name rather than an absolute path,\nit is searched for in the system-wide :envvar:`PATH` environment variable. Note\nthat this **may not** be the value of :envvar:`PATH` inside a shell, as shell\nstartup scripts often change the value of this variable. If it is not found\nthere, then a system specific list of default paths is searched. If it is still\nnot found, then your shell is run and the value of :envvar:`PATH` inside the\nshell is used.\n\nSee :opt:`exe_search_path` for details and how to control this.\n\nSyntax reference\n------------------\n\n.. include:: /generated/launch.rst\n"
  },
  {
    "path": "docs/layouts.rst",
    "content": "Arrange windows\n-------------------\n\nkitty has the ability to define its own windows that can be tiled next to each\nother in arbitrary arrangements, based on *Layouts*, see below for examples:\n\n\n.. figure:: screenshots/screenshot.png\n    :alt: Screenshot, showing three programs in the 'Tall' layout\n    :align: center\n    :width: 100%\n\n    Screenshot, showing :program:`vim`, :program:`tig` and :program:`git`\n    running in |kitty| with the *Tall* layout\n\n\n.. figure:: screenshots/splits.png\n    :alt: Screenshot, showing windows in the 'Splits' layout\n    :align: center\n    :width: 100%\n\n    Screenshot, showing windows with arbitrary arrangement in the *Splits*\n    layout\n\n\nThere are many different layouts available. They are all enabled by default, you\ncan switch layouts using :ac:`next_layout` (:sc:`next_layout` by default). To\ncontrol which layouts are available use :opt:`enabled_layouts`, the first listed\nlayout becomes the default. Individual layouts and how to use them are described\nbelow.\n\n\nThe Stack Layout\n------------------\n\nThis is the simplest layout. It displays a single window using all available\nspace, other windows are hidden behind it. This layout has no options::\n\n    enabled_layouts stack\n\n\nThe Tall Layout\n------------------\n\nDisplays one (or optionally more) full-height windows on the left half of the\nscreen. Remaining windows are tiled vertically on the right half of the screen.\nThere are options to control how the screen is split horizontally ``bias``\n(an integer between ``10`` and ``90``) and options to control how many\nfull-height windows there are ``full_size`` (a positive integer). The\n``mirrored`` option when set to ``true`` will cause the full-height windows to\nbe on the right side of the screen instead of the left. The syntax\nfor the options is::\n\n    enabled_layouts tall:bias=50;full_size=1;mirrored=false\n\n    ┌──────────────┬───────────────┐\n    │              │               │\n    │              │               │\n    │              │               │\n    │              ├───────────────┤\n    │              │               │\n    │              │               │\n    │              │               │\n    │              ├───────────────┤\n    │              │               │\n    │              │               │\n    │              │               │\n    └──────────────┴───────────────┘\n\nIn addition, you can map keys to increase or decrease the number of full-height\nwindows, or toggle the mirrored setting, for example::\n\n   map ctrl+[ layout_action decrease_num_full_size_windows\n   map ctrl+] layout_action increase_num_full_size_windows\n   map ctrl+/ layout_action mirror toggle\n   map ctrl+y layout_action mirror true\n   map ctrl+n layout_action mirror false\n\nYou can also map a key to change the bias by providing a list of percentages\nand it will rotate through the list as you press the key. If you only provide\none number it'll toggle between that percentage and 50, for example::\n\n   map ctrl+. layout_action bias 50 62 70\n   map ctrl+, layout_action bias 62\n\nThe Fat Layout\n----------------\n\nDisplays one (or optionally more) full-width windows on the top half of the\nscreen. Remaining windows are tiled horizontally on the bottom half of the\nscreen. There are options to control how the screen is split vertically ``bias``\n(an integer between ``10`` and ``90``) and options to control how many\nfull-width windows there are ``full_size`` (a positive integer). The\n``mirrored`` option when set to ``true`` will cause the full-width windows to be\non the bottom of the screen instead of the top. The syntax for the options is::\n\n    enabled_layouts fat:bias=50;full_size=1;mirrored=false\n\n    ┌──────────────────────────────┐\n    │                              │\n    │                              │\n    │                              │\n    │                              │\n    ├─────────┬──────────┬─────────┤\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    └─────────┴──────────┴─────────┘\n\n\nThis layout also supports the same layout actions as the *Tall* layout, shown above.\n\n\nThe Grid Layout\n--------------------\n\nDisplay windows in a balanced grid with all windows the same size except the\nlast column if there are not enough windows to fill the grid. This layout has no\noptions::\n\n    enabled_layouts grid\n\n    ┌─────────┬──────────┬─────────┐\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    ├─────────┼──────────┼─────────┤\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    └─────────┴──────────┴─────────┘\n\n\n.. _splits_layout:\n\nThe Splits Layout\n--------------------\n\nThis is the most flexible layout. You can create any arrangement of windows\nby splitting existing windows repeatedly. To best use this layout you should\ndefine a few extra key bindings in :file:`kitty.conf`::\n\n    # Create a new window splitting the space used by the existing one so that\n    # the two windows are placed one above the other\n    map f5 launch --location=hsplit\n\n    # Create a new window splitting the space used by the existing one so that\n    # the two windows are placed side by side\n    map f6 launch --location=vsplit\n\n    # Create a new window splitting the space used by the existing one so that\n    # the two windows are placed side by side if the existing window is wide or\n    # one above the other if the existing window is tall.\n    map f4 launch --location=split\n\n    # Rotate the current split, changing its split axis from vertical to\n    # horizontal or vice versa\n    map f7 layout_action rotate\n\n    # Move the active window in the indicated direction\n    map shift+up move_window up\n    map shift+left move_window left\n    map shift+right move_window right\n    map shift+down move_window down\n\n    # Move the active window to the indicated screen edge\n    map ctrl+shift+up layout_action move_to_screen_edge top\n    map ctrl+shift+left layout_action move_to_screen_edge left\n    map ctrl+shift+right layout_action move_to_screen_edge right\n    map ctrl+shift+down layout_action move_to_screen_edge bottom\n\n    # Switch focus to the neighboring window in the indicated direction\n    map ctrl+left neighboring_window left\n    map ctrl+right neighboring_window right\n    map ctrl+up neighboring_window up\n    map ctrl+down neighboring_window down\n\n    # Set the bias of the split containing the currently focused window. The\n    # currently focused window will take up the specified percent of its parent\n    # window's size.\n    map ctrl+. layout_action bias 80\n\n    # Maximize the active window along the horizontal axis (fill full width),\n    # keeping other windows visible in their vertical positions. Press again to\n    # restore the original layout.\n    map ctrl+shift+right layout_action maximize horizontal\n\n    # Maximize the active window along the vertical axis (fill full height),\n    # keeping other windows visible in their horizontal positions. Press again\n    # to restore the original layout.\n    map ctrl+shift+up layout_action maximize vertical\n\n\nWindows can be resized using :ref:`window_resizing`. You can swap the windows\nin a split using the ``rotate`` action with an argument of ``180`` and rotate\nand swap with an argument of ``270``. The ``maximize`` action expands the active\nwindow to fill the maximum available space along a single axis while keeping\nthe rest of the layout intact. Use ``maximize horizontal`` to fill the full\nwidth and ``maximize vertical`` to fill the full height. Calling it again\nrestores the original split sizes.\n\nThis layout takes one option, ``split_axis`` that controls whether new windows\nare placed into vertical or horizontal splits when a :option:`--location\n<launch --location>` is not specified. A value of ``horizontal`` (same as\n``--location=vsplit``) means when a new split is created the two windows will\nbe placed side by side and a value of ``vertical`` (same as\n``--location=hsplit``) means the two windows will be placed one on top of the\nother. A value of ``auto`` means the axis of the split is chosen automatically\n(same as ``--location=split``). By default::\n\n    enabled_layouts splits:split_axis=horizontal\n\n    ┌──────────────┬───────────────┐\n    │              │               │\n    │              │               │\n    │              │               │\n    │              ├───────┬───────┤\n    │              │       │       │\n    │              │       │       │\n    │              │       │       │\n    │              ├───────┴───────┤\n    │              │               │\n    │              │               │\n    │              │               │\n    └──────────────┴───────────────┘\n\n.. versionadded:: 0.17.0\n    The Splits layout\n\n\nThe Horizontal Layout\n------------------------\n\nAll windows are shown side by side. This layout has no options::\n\n    enabled_layouts horizontal\n\n    ┌─────────┬──────────┬─────────┐\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    │         │          │         │\n    └─────────┴──────────┴─────────┘\n\n\nThe Vertical Layout\n-----------------------\n\nAll windows are shown one below the other. This layout has no options::\n\n    enabled_layouts vertical\n\n    ┌──────────────────────────────┐\n    │                              │\n    │                              │\n    │                              │\n    ├──────────────────────────────┤\n    │                              │\n    │                              │\n    │                              │\n    ├──────────────────────────────┤\n    │                              │\n    │                              │\n    │                              │\n    └──────────────────────────────┘\n\n\n.. _window_resizing:\n\nResizing windows\n------------------\n\nYou can resize windows inside layouts. The easiest method is to simply drag the\nwindow borders using a mouse, controlled by the option :opt:`window_drag_tolerance`.\nNote that technically this resizes layout slots not actual windows, so it\ndoes not work exactly like resizing OS Windows on your desktop. Instead, the\nlayout is changed and potentially multiple windows get resized when dragging a\nsingle border.\n\nFor keyboard friendly resizing, press :sc:`start_resizing_window` (also\n:kbd:`⌘+r` on macOS) to enter resizing mode and follow the on-screen\ninstructions. In a given window layout only some operations may be possible for\na particular window. For example, in the *Tall* layout you can make the first\nwindow wider/narrower, but not taller/shorter. Note that what you are resizing\nis actually not a window, but a row/column in the layout, all windows in that\nrow/column will be resized.\n\nYou can also define shortcuts in :file:`kitty.conf` to make the active window\nwider, narrower, taller, or shorter by mapping to the :ac:`resize_window`\naction, for example::\n\n   map ctrl+left resize_window narrower\n   map ctrl+right resize_window wider\n   map ctrl+up resize_window taller\n   map ctrl+down resize_window shorter 3\n   # reset all windows in the tab to default sizes\n   map ctrl+home resize_window reset\n\nThe :ac:`resize_window` action has a second optional argument to control\nthe resizing increment (a positive integer that defaults to 1).\n\nSome layouts take options to control their behavior. For example, the *Fat*\nand *Tall* layouts accept the ``bias`` and ``full_size`` options to control\nhow the available space is split up. To specify the option, in :opt:`kitty.conf\n<enabled_layouts>` use::\n\n    enabled_layouts tall:bias=70;full_size=2\n\nThis will have ``2`` instead of a single tall window, that occupy ``70%``\ninstead of ``50%`` of available width. ``bias`` can be any number between ``10``\nand ``90``.\n\nWriting a new layout only requires about two hundred lines of code, so if there\nis some layout you want, take a look at one of the existing layouts in the\n`layout <https://github.com/kovidgoyal/kitty/tree/master/kitty/layout>`__\npackage and submit a pull request!\n"
  },
  {
    "path": "docs/mapping.rst",
    "content": ":orphan:\n\nMaking your keyboard dance\n==============================\n\n.. highlight:: conf\n\nkitty has extremely powerful facilities for mapping keyboard actions.\nThings like combining actions, multi-key mappings, modal mappings,\nmappings that send arbitrary text, and mappings dependent on the program\ncurrently running in kitty.\n\nLet's start with the basics. You can map a key press to an action in kitty using\nthe following syntax::\n\n    map ctrl+a new_window_with_cwd\n\nThis will map the key press :kbd:`Ctrl+a` to open a new :term:`window`\nwith the working directory set to the working directory of the current window.\nThis is the basic operation of the map directive, the tip of the iceberg, for\nmore read the sections below.\n\n\nCombining multiple actions on a single keypress\n-----------------------------------------------------\n\nMultiple actions can be combined on a single keypress, like a macro. To do this\nmap the key press to the :ac:`combine` action::\n\n    map key combine <separator> action1 <separator> action2 <separator> action3 ...\n\nFor example::\n\n    map kitty_mod+e combine : new_window : next_layout\n\nThis will create a new window and switch to the next available layout. You can\nalso run arbitrarily powerful scripts on a key press. There are two major\ntechniques for doing this, using remote control scripts or using kittens.\n\nRemote control scripts\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThese can be written in any language and use the \"kitten\" binary to control\nkitty via its extensive :doc:`Remote control <remote-control>` API. First,\nif you just want to run a single remote control command on a key press,\nyou can just do::\n\n    map f1 remote_control set-spacing margin=30\n\nThis will run the ``set-spacing`` command, changing window margins to 30 pixels. For\nmore complex scripts, write a script file in any language you like and save it\nsomewhere, preferably in the kitty configuration directory. Do not forget to make it\nexecutable. In the script file you run remote control commands by running the\n\"kitten\" binary, for example:\n\n.. code-block:: sh\n\n   #!/bin/sh\n\n   kitten @ set-spacing margin=30\n   kitten @ new_window\n   ...\n\nThe script can perform arbitrarily complex logic and actions, limited only by\nthe remote control API, that you can browse by running ``kitten @ --help``.\nTo run the script you created on a key press, use::\n\n    map f1 remote_control_script /path/to/myscript\n\n\nKittens\n^^^^^^^^^^^^^\n\nHere, kittens refer to Python scripts. The scripts have two parts, one that\nruns as a regular command line program inside a kitty window to, for example,\nask the user for some input and a second part that runs inside the kitty\nprocess itself and can perform any operation on the kitty UI, which is itself\nimplemented in Python. However, the kitty internal API is not documented and\ncan (very rarely) change, so kittens are harder to get started with than remote\ncontrol scripts. To run a kitten on a key press::\n\n    map f1 kitten mykitten.py\n\nMany of kitty's features are themselves implemented as kittens, for example,\n:doc:`/kittens/unicode_input`, :doc:`/kittens/hints` and\n:doc:`/kittens/themes`. To learn about writing your own kittens, see\n:doc:`/kittens/custom`.\n\nSyntax for specifying keys\n-----------------------------\n\nA mapping maps a key press to some action. In their most basic form, keypresses\nare :code:`modifier+key`. Keys are identified simply by their lowercase Unicode\ncharacters. For example: :code:`a` for the :kbd:`A` key, :code:`[` for the left\nsquare bracket key, etc.  For functional keys, such as :kbd:`Enter` or\n:kbd:`Escape`, the names are present at :ref:`Functional key definitions\n<functional>`. For modifier keys, the names are :kbd:`ctrl` (:kbd:`control`,\n:kbd:`⌃`), :kbd:`shift` (:kbd:`⇧`), :kbd:`alt` (:kbd:`opt`, :kbd:`option`,\n:kbd:`⌥`), :kbd:`super` (:kbd:`cmd`, :kbd:`command`, :kbd:`⌘`).\n\nAdditionally, you can use the name :opt:`kitty_mod` as a modifier, the default\nvalue of which is :kbd:`ctrl+shift`. The default kitty shortcuts are defined\nusing this value, so by changing it in :file:`kitty.conf` you can change\nall the modifiers used by all the default shortcuts.\n\nOn Linux, you can also use XKB names for functional keys that don't have kitty\nnames. See :link:`XKB keys\n<https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h>`\nfor a list of key names. The name to use is the part after the :code:`XKB_KEY_`\nprefix. Note that you can only use an XKB key name for keys that are not known\nas kitty keys.\n\nFinally, you can use raw system key codes to map keys, again only for keys that\nare not known as kitty keys. To see the system key code for a key, start kitty\nwith the :option:`kitty --debug-input` option, kitty will output some debug text\nfor every key event. In that text look for :code:`native_code`, the value\nof that becomes the key name in the shortcut. For example:\n\n.. code-block:: none\n\n    on_key_input: glfw key: 0x61 native_code: 0x61 action: PRESS mods: none text: 'a'\n\nHere, the key name for the :kbd:`A` key is :code:`0x61` and you can use it with::\n\n    map ctrl+0x61 something\n\nThis maps :kbd:`Ctrl+A` to something.\n\n\nMulti-key mappings\n--------------------\n\nA mapping in kitty can involve pressing multiple keys in sequence, with the\nsyntax shown below::\n\n    map key1>key2>key3 action\n\nFor example::\n\n    map ctrl+f>2 set_font_size 20\n\nThe default mappings to run the :doc:`hints kitten </kittens/hints>` to select text on the screen are\nexamples of multi-key mappings.\n\nUnmapping default shortcuts\n-----------------------------\n\nkitty comes with dozens of default keyboard mappings for common operations. See\n:doc:`actions` for the full list of actions and the default shortcuts that map\nto them. You can unmap an individual shortcut, so that it is passed on to the\nprogram running inside kitty, by mapping it to nothing, for example::\n\n    map kitty_mod+enter\n\nThis unmaps the default shortcut :sc:`new_window` to open a new window. Almost\nall default shortcuts are of the form ``modifier + key`` where the\nmodifier defaults to :kbd:`Ctrl+Shift` and can be changed using the :opt:`kitty_mod` setting\nin :file:`kitty.conf`.\n\nIf you want to clear all default shortcuts, you can use\n:opt:`clear_all_shortcuts` in :file:`kitty.conf`.\n\nIf you would like kitty to completely ignore a key event, not even sending it to\nthe program running in the terminal, map it to :ac:`discard_event`::\n\n    map kitty_mod+f1 discard_event\n\n.. _conditional_mappings:\n\nConfiguring a timeout\n----------------------\n\nYou can also set a timeout for keyboard modes and multi-key mappings. If a\ntimeout is set and you don't complete the key sequence or exit the mode within\nthe specified time, the mode will be automatically cancelled. This is useful\nfor multi-key mappings where you might accidentally press the first key and\nthen change your mind. The timeout is specified in seconds and can be set\nglobally using the :opt:`map_timeout` option or per-mode using ``--timeout``::\n\n    # Set a global 2 second timeout for all multi-key and modal mappings\n    map_timeout 2.0\n\n    # This mode will have a 5 second timeout (overrides global setting)\n    map --new-mode resize --timeout 5.0 kitty_mod+r\n    map --mode resize h resize_window narrower\n    map --mode resize l resize_window wider\n    # ... more mappings\n\n    # Multi-key mapping with the global timeout\n    map ctrl+a>h new_window\n\nWhen a timeout occurs, the mode is exited and any buffered keys are discarded.\nA timeout value of zero disables the timeout. For multi-key sequences, the\ntimeout is restarted after each valid key press in the sequence.\n\n\nConditional mappings depending on the state of the focused window\n----------------------------------------------------------------------\n\nSometimes, you may want different mappings to be active when running a\nparticular program in kitty, perhaps because it has some native functionality\nthat duplicates kitty functions or there is a conflict, etc. kitty has the\nability to create mappings that work only when the currently focused window\nmatches some criteria, such as when it has a particular title or user variable.\n\nLet's see some examples::\n\n    map --when-focus-on title:keyboard.protocol kitty_mod+t\n\nThis will cause :kbd:`kitty_mod+t` (the default shortcut for opening a new tab)\nto be unmapped only when the focused window\nhas :code:`keyboard protocol` in its title. Run the show-key kitten as::\n\n    kitten show-key -m kitty\n\nPress :kbd:`ctrl+shift+t` and instead of a new tab opening, you will\nsee the key press being reported by the kitten. :code:`--when-focus-on` can test\nthe focused window using very powerful criteria, see :ref:`search_syntax` for\ndetails. A more practical example unmaps the key when the focused window is\nrunning an editor::\n\n    map --when-focus-on var:in_editor kitty_mod+c\n\nIn order to make this work, you need to configure your editor as shown below:\n\n.. tab:: vim\n\n   In :file:`~/.vimrc` add:\n    .. code-block:: vim\n\n        let &t_ti = &t_ti . \"\\033]1337;SetUserVar=in_editor=MQ==\\007\"\n        let &t_te = &t_te . \"\\033]1337;SetUserVar=in_editor\\007\"\n\n.. tab:: neovim\n\n   In :file:`~/.config/nvim/init.lua` add:\n\n    .. code-block:: lua\n\n        vim.api.nvim_create_autocmd({ \"VimEnter\", \"VimResume\", \"UIEnter\" }, {\n            group = vim.api.nvim_create_augroup(\"KittySetVarVimEnter\", { clear = true }),\n            callback = function()\n                if vim.api.nvim_ui_send then\n                    vim.api.nvim_ui_send(\"\\x1b]1337;SetUserVar=in_editor=MQ==\\007\")\n                else\n                    io.stdout:write(\"\\x1b]1337;SetUserVar=in_editor=MQ==\\007\")\n                end\n            end,\n        })\n\n        vim.api.nvim_create_autocmd({ \"VimLeave\", \"VimSuspend\" }, {\n            group = vim.api.nvim_create_augroup(\"KittyUnsetVarVimLeave\", { clear = true }),\n            callback = function()\n                if vim.api.nvim_ui_send then\n                    vim.api.nvim_ui_send(\"\\x1b]1337;SetUserVar=in_editor\\007\")\n                else\n                    io.stdout:write(\"\\x1b]1337;SetUserVar=in_editor\\007\")\n                end\n            end,\n        })\n\nThese cause the editor to set the :code:`in_editor` variable in kitty and unset it when exiting.\nAs a result, the :kbd:`ctrl+shift+c` key will be passed to the editor instead of\ncopying to clipboard. In the editor, you can map it to copy to the clipboard,\nthereby allowing use of a common shortcut both inside and outside the editor\nfor copying to clipboard.\n\n.. note::\n\n   When using multi-key mappings, of the form :kbd:`k1>k2` or similar, the\n   condition applies to the first key and you can have only one condition per\n   key, the last in kitty.conf wins. In particular, this means you cannot have\n   multiple conditions applying to multi-key mappings with the same first key\n   and you cannot have mappings with and without conditions applying to multi-keys\n   with the same first key.\n\nSending arbitrary text or keys to the program running in kitty\n--------------------------------------------------------------------------------\n\nThis is accomplished by using ``map`` with :sc:`send_text <send_text>` in :file:`kitty.conf`.\nFor example::\n\n    map f1 send_text normal,application Hello, world!\n\nNow, pressing :kbd:`f1` will cause ``Hello, world!`` to show up at your shell\nprompt. To have the shell execute a command sent via ``send_text`` you need to\nalso simulate pressing the enter key which is ``\\r``. For example::\n\n    map f1 send_text normal,application echo Hello, world!\\r\n\nNow, if you press :kbd:`f1` when at shell prompt it will run the ``echo Hello,\nworld!`` command.\n\nTo have one key press send another key press, use :ac:`send_key`::\n\n    map alt+s send_key ctrl+s\n\nThis causes the program running in kitty to receive the :kbd:`ctrl+s` key when\nyou press the :kbd:`alt+s` key. To see this in action, run::\n\n    kitten show-key -m kitty\n\nWhich will print out what key events it receives.\n\n.. _modal_mappings:\n\nModal mappings\n--------------------------\n\nkitty has the ability, like vim, to use *modal* key maps. Except that unlike\nvim it allows you to define your own arbitrary number of modes. To create a new\nmode, use ``map --new-mode <my mode name> <shortcut to enter mode>``. For\nexample, lets create a mode to manage windows: switching focus, moving the window, etc.::\n\n    # Create a new \"manage windows\" mode (mw)\n    map --new-mode mw kitty_mod+f7\n\n    # Switch focus to the neighboring window in the indicated direction using arrow keys\n    map --mode mw left neighboring_window left\n    map --mode mw right neighboring_window right\n    map --mode mw up neighboring_window up\n    map --mode mw down neighboring_window down\n\n    # Move the active window in the indicated direction\n    map --mode mw shift+up move_window up\n    map --mode mw shift+left move_window left\n    map --mode mw shift+right move_window right\n    map --mode mw shift+down move_window down\n\n    # Resize the active window\n    map --mode mw n resize_window narrower\n    map --mode mw w resize_window wider\n    map --mode mw t resize_window taller\n    map --mode mw s resize_window shorter\n\n    # Exit the manage window mode\n    map --mode mw esc pop_keyboard_mode\n\nNow, if you run kitty as:\n\n.. code-block:: sh\n\n    kitty -o enabled_layouts=vertical --session <(echo \"launch\\nlaunch\\nlaunch\")\n\nPress :kbd:`Ctrl+Shift+F7` to enter the mode and then press the up and\ndown arrow keys to focus the next/previous window. Press :kbd:`Shift+Up` or\n:kbd:`Shift+Down` to move the active window up and down. Press :kbd:`t` to make\nthe active window taller and :kbd:`s` to make it shorter. To exit the mode\npress :kbd:`Esc`.\n\nPressing an unknown key while in a custom keyboard mode by default\nbeeps. This can be controlled by the ``map --on-unknown`` option as shown\nbelow::\n\n    # Beep on unknown keys\n    map --new-mode XXX --on-unknown beep ...\n    # Ignore unknown keys silently\n    map --new-mode XXX --on-unknown ignore ...\n    # Beep and exit the keyboard mode on unknown key\n    map --new-mode XXX --on-unknown end ...\n    # Pass unknown keys to the program running in the active window\n    map --new-mode XXX --on-unknown passthrough ...\n\nWhen a key matches an action in a custom keyboard mode, the action is performed\nand the custom keyboard mode remains in effect. If you would rather have the\nkeyboard mode end after the action you can use ``map --on-action`` as shown\nbelow::\n\n    # Have this keyboard mode automatically exit after performing any action\n    map --new-mode XXX --on-action end ...\n\n\nAll mappable actions\n------------------------\n\nThere is a list of :doc:`all mappable actions <actions>`.\n\nDebugging mapping issues\n------------------------------\n\nTo debug mapping issues, kitty has several facilities. First, when you run\nkitty with the ``--debug-input`` command line flag it outputs details\nabout all key events it receives form the system and how they are handled.\n\nTo see what key events are sent to applications, run kitty like this::\n\n    kitty kitten show-key\n\nPress the keys you want to debug and the kitten will print out the bytes it\nreceives. Note that this uses the legacy terminal keyboard protocol that does\nnot support all keys and key events. To debug the :doc:`full kitty keyboard\nprotocol that <keyboard-protocol>` that is nowadays being adopted by more and\nmore programs, use::\n\n    kitty kitten show-key -m kitty\n"
  },
  {
    "path": "docs/marks.rst",
    "content": "Mark text on screen\n---------------------\n\n\nkitty has the ability to mark text on the screen based on regular expressions.\nThis can be useful to highlight words or phrases when browsing output from long\nrunning programs or similar. Lets start with a few examples:\n\nExamples\n----------\n\nSuppose we want to be able to highlight the word :code:`ERROR` in the current\nwindow. Add the following to :file:`kitty.conf`::\n\n    map f1 toggle_marker text 1 ERROR\n\nNow when you press :kbd:`F1`, all instances of the word :code:`ERROR` will be\nhighlighted. To turn off the highlighting, press :kbd:`F1` again.\nIf you want to make it case-insensitive, use::\n\n    map f1 toggle_marker itext 1 ERROR\n\nTo make it match only complete words, use::\n\n    map f1 toggle_marker regex 1 \\\\bERROR\\\\b\n\nSuppose you want to highlight both :code:`ERROR` and :code:`WARNING`, case\ninsensitively::\n\n    map f1 toggle_marker iregex 1 \\\\bERROR\\\\b 2 \\\\bWARNING\\\\b\n\nkitty supports up to 3 mark groups (the numbers in the commands above). You\ncan control the colors used for these groups in :file:`kitty.conf` with::\n\n    mark1_foreground red\n    mark1_background gray\n    mark2_foreground green\n    ...\n\n\n.. note::\n    For performance reasons, matching is done per line only, and only when that\n    line is altered in any way. So you cannot match text that stretches across\n    multiple lines.\n\n\nCreating markers dynamically\n---------------------------------\n\nIf you want to create markers dynamically rather than pre-defining them in\n:file:`kitty.conf`, you can do so as follows::\n\n    map f1 create_marker\n    map f2 remove_marker\n\nThen pressing :kbd:`F1` will allow you to enter the marker definition and set it\nand pressing :kbd:`F2` will remove the marker. :ac:`create_marker` accepts the\nsame syntax as :ac:`toggle_marker` above. Note that while creating markers, the\nprompt has history so you can easily re-use previous marker expressions.\n\nYou can also use the facilities for :doc:`remote-control` to dynamically add or\nremove markers.\n\n\nScrolling to marks\n--------------------\n\nkitty has a :ac:`scroll_to_mark` action to scroll to the next line that contains\na mark. You can use it by mapping it to some shortcut in :file:`kitty.conf`::\n\n    map ctrl+p scroll_to_mark prev\n    map ctrl+n scroll_to_mark next\n\nThen pressing :kbd:`Ctrl+P` will scroll to the first line in the scrollback\nbuffer above the current top line that contains a mark. Pressing :kbd:`Ctrl+N`\nwill scroll to show the first line below the current last line that contains\na mark. If you wish to jump to a mark of a specific type, you can add that to\nthe mapping::\n\n    map ctrl+1 scroll_to_mark prev 1\n\nWhich will scroll only to marks of type 1.\n\n\nThe full syntax for creating marks\n-------------------------------------\n\nThe syntax of the :ac:`toggle_marker` action is::\n\n    toggle_marker <marker-type> <specification>\n\nHere :code:`marker-type` is one of:\n\n* :code:`text` - Simple substring matching\n* :code:`itext` - Case-insensitive substring matching\n* :code:`regex` - A Python regular expression\n* :code:`iregex` - A case-insensitive Python regular expression\n* :code:`function` - An arbitrary function defined in a Python file, see :ref:`marker_funcs`.\n\n.. _marker_funcs:\n\nArbitrary marker functions\n-----------------------------\n\nYou can create your own marker functions. Create a Python file named\n:file:`mymarker.py` and in it create a :code:`marker` function. This function\nreceives the text of the line as input and must yield three numbers,\nthe starting character position, the ending character position and the mark\ngroup (1-3). For example:\n\n.. code-block::\n\n    def marker(text):\n        # Function to highlight the letter X\n        for i, ch in enumerate(text):\n            if ch.lower() == 'x':\n                yield i, i, 3\n\n\nSave this file somewhere and in :file:`kitty.conf`, use::\n\n    map f1 toggle_marker function /path/to/mymarker.py\n\nIf you save the file in the :ref:`kitty config directory <confloc>`, you can\nuse::\n\n    map f1 toggle_marker function mymarker.py\n"
  },
  {
    "path": "docs/misc-protocol.rst",
    "content": "Miscellaneous protocol extensions\n==============================================\n\nThese are a few small protocol extensions kitty implements, primarily for use\nby its own kitten, they are documented here for completeness.\n\n\nSimple save/restore of all terminal modes\n--------------------------------------------\n\nXTerm has the XTSAVE/XTRESTORE escape codes to save and restore terminal\nprivate modes. However, they require specifying an explicit list of modes to\nsave/restore. kitty extends this protocol to specify that when no modes are\nspecified, all side-effect free modes should be saved/restored. By side-effects\nwe mean things that can affect other terminal state such as cursor position or\nscreen contents. Examples of modes that have side effects are: `DECOM\n<https://vt100.net/docs/vt510-rm/DECOM.html>`__ and `DECCOLM\n<https://vt100.net/docs/vt510-rm/DECCOLM.html>`__.\n\nThis allows TUI applications to easily save and restore emulator state without\nneeding to maintain lists of modes.\n\n\nIndependent control of bold and faint SGR properties\n-------------------------------------------------------\n\nIn common terminal usage, bold is set via SGR 1 and faint by SGR 2. However,\nthere is only one number to reset these attributes, SGR 22, which resets both.\nThere is no way to reset one and not the other. kitty uses 221 and 222 to reset\nbold and faint independently.\n\n.. _mouse_leave_window:\n\nReporting when the mouse leaves the window\n----------------------------------------------\n\nkitty extends the SGR Pixel mouse reporting protocol created by xterm to\nalso report when the mouse leaves the window. This event is delivered\nencoded as a normal SGR pixel event except that the eight bit is set on the\nfirst number. Additionally, bit 5 is set to indicate this is a motion related event.\nThe remaining bits 1-7 (except 5) are used to encode button and modifier information.\nWhen bit 8 is set it means the event is a mouse has left the window event,\nand all other bits should be ignored. The pixel position values must also\nbe ignored as they may not be accurate.\n\nAn escape code to move the contents of the screen into the scrollback\n-------------------------------------------------------------------------------------\n\nThe escape code is ``\\x1b [ 22 J`` (ignoring spaces present for clarity). It\nmoves all screen contents (text and images) into the scrollback leaving the\nscreen in the same state as it would be if the standard screen clear escape\ncode had been used ``\\x1b [ 2 J``.\n\n\nkitty specific private escape codes\n---------------------------------------\n\nThese are a family of escape codes used by kitty for various things including\nremote control. They are all DCS (Device Control String) escape codes starting\nwith ``\\x1b P @ kitty-`` (ignoring spaces present for clarity).\n"
  },
  {
    "path": "docs/multiple-cursors-protocol.rst",
    "content": "The multiple cursors protocol\n==============================================\n\n.. versionadded:: 0.43.0\n\nMany editors support something called *multiple cursors* in which you can make\nthe same changes at multiple locations in a file and the editor shows you\ncursors at each of the locations. In a terminal context editors typically\nimplement this by showing some Unicode glyph at each location instead of the\nactual cursor. This is sub-optimal since actual cursors implemented by the\nterminal have many niceties like smooth animation [anim]_, auto adjust colors [rv]_,\netc. To address this and other use cases, this protocol allows terminal programs to\nrequest that the terminal display multiple cursors at specific locations on the\nscreen.\n\nQuickstart\n----------------\n\nAn example, showing how to use the protocol:\n\n.. code-block:: sh\n\n    # Show cursors of the same shape as the main cursor at y=4, x=5\n    printf \"\\e[>29;2:4:5 q\"\n    # Show more cursors on the seventh line, of various shapes, the underline shape is shown twice\n    printf \"\\e[>1;2:7:1 q\\e[>2;2:7:3 q\\e[>3;2:7:5;2:7:7 q\"\n\n\nThe escape code to show a cursor has the following structure (ignore spaces\nthey are present for readability only)::\n\n    CSI > SHAPE;CO-ORD TYPE : CO-ORDINATES ; CO-ORD TYPE : CO-ORDINATES ... TRAILER\n\nHere ``CSI`` is the two bytes ESC (``0x1b``) and [ (``0x5b``). ``SHAPE`` can be\none of:\n\n* ``0``: No cursor\n* ``1``: Block cursor\n* ``2``: Beam cursor\n* ``3``: Underline cursor\n* ``29``: Follow the shape of the main cursor\n* ``30``: Change the color of text under extra cursors\n* ``40``: Change the color of extra cursors\n* ``100``: Used for querying currently set cursors\n\n``CO-ORD TYPE`` can be one of:\n\n* ``0``: This refers to the position of the main cursor and has no following\n  co-ordinates.\n\n* ``2``: In this case the following co-ordinates are pairs of numbers pointing\n  to cells in the form ``y:x`` with the origin in the top left corner at\n  ``1,1``. There can be any number of pairs, the terminal must treat each pair\n  as a new location to set a cursor.\n\n* ``4``: In this case the following co-ordinates are sets of four numbers that\n  define a rectangle in the same co-ordinate system as above of the form:\n  ``top:left:bottom:right``. The shape is set on every cell in the rectangle\n  from the top left cell to the bottom right cell, inclusive. If no numbers\n  are provided, the rectangle is the full screen. There can be any number of\n  rectangles, the terminal must treat each set of four numbers as a new\n  rectangle.\n\nThe sequence of ``CO-ORD TYPE : CO-ORDINATES`` can be repeated any number of\ntimes separated by ``;``. The ``SHAPE`` will be set on the cells indicated by\neach such group. For example: ``-1;2:3:4;4:5:6:7:8`` will set the shape ``-1``\nat the cell ``(3, 2)`` and in the rectangle ``(6, 5)`` to ``(8, 7)`` inclusive.\n\nFinally, the ``TRAILER`` terminates the sequence and is the bytes SPACE\n(``0x20``) and q (``0x71``).\n\nTerminals **must** ignore cells that fall outside the screen. That means, for\nrectangle co-ordinates only the intersection of the rectangle with the screen\nmust be considered, and point co-ordinates that fall outside of the screen are\nsimply ignored, with no effect.\n\nTerminals **must** ignore extra co-ordinates, that means if an odd number of\nco-ordinates are specified for type ``2`` the last co-ordinate is ignored.\nSimilarly for type ``4`` if the number of co-ordinates is not a multiple of\nfour, the last ``1 <= n <= 3`` co-ordinates are ignored, as if they were not\nspecified.\n\nQuerying for support\n-------------------------\n\nA terminal program can query the terminal emulator for support of this\nprotocol by sending the escape code::\n\n    CSI > TRAILER\n\nIn this case a supporting terminal must reply with::\n\n    CSI > 1;2;3;29;30;40;100;101 TRAILER\n\nHere, the list of numbers indicates the cursor shapes and other operations\nthe terminal supports and can be any subset of the above. No numbers\nindicates the protocol is not supported. To avoid having to wait with a\ntimeout for a response from the terminal, the client should send this\nquery code immediately followed by a request for the\n`primary device attributes <https://vt100.net/docs/vt510-rm/DA1.html>`_.\nIf the terminal responds with an answer for the device attributes without\nan answer for the *query* the terminal emulator does not support this protocol at all.\n\nTerminals **must** respond to these queries in FIFO order, so that\nmultiplexers that split a single screen know which split to send responses too.\n\nClearing previously set multi-cursors\n------------------------------------------\n\nThe cursor at a cell is cleared by setting its shape to ``0``.\nThe most common operation is to clear all previously set multi-cursors. This is\neasily done using the *rectangle* co-ordinate system above, like this::\n\n    CSI > 0;4 TRAILER\n\nFor more precise control different co-ordinate types can be used. This is\nparticularly important for multiplexers that split up the screen and therefore\nneed to re-write these escape codes.\n\n.. _extra_cursor_color:\n\nChanging the color of extra cursors\n---------------------------------------\n\nIn order to visually distinguish extra cursors from the main cursor, it is\npossible to specify a color pair for extra cursors. Note that for performance\nreasons, there is only a single color pair that all extra cursors share.\nThe color pair consists of the cursor color and the color for text in the cell\nthe cursor is on.\n\nTo change this color pair use an escape code of the form::\n\n    CSI > WHICH ; COLOR_SPACE : COLOR_PARAMETER1 : COLOR_PARAMETER2 : ... TRAILER\n\nHere, ``WHICH`` is ``30`` to set the color of text under the cursor and ``40``\nto set the color of the cursor itself (these numbers mimic the SGR codes for\nforeground and background respectively).\n\nThe ``COLOR_SPACE`` parameter sets the type of color, it can take values:\n\n``0`` - unset color is same as for main cursor. No color parameters.\n``1`` - *special* which typically means some kind of reverse video effect, see below\n``2`` - sRGB color, with three color parameters, red, green and blue as numbers\nfrom 0 to 255\n``5`` - Indexed color with one color parameter which is an index into the color\ntable from 0 to 255\n\nWhen the cursor color is set to *special* via ``40`` it means the block cursor\nmust be rendered with a reverse video effect where the cursor color becomes the\nforeground color of the cell under the cursor and the foreground color of the\ncell becomes its background color. Implementations are free to adjust these\ncolors to ensure suitable contrast levels. In this case the text color set by\n``30`` must be ignored.\n\nWhen the cursor color is not set to *special* but the text color via ``30`` is\nset to special, then that means the foreground color of the cell with the\ncursor must be changed to its background color for a partial reverse video\neffect.\n\nWhen unset, aka, set to ``0`` the cursors must be the same color as the main\ncursor. In particular if the main color is using a reverse video effect, the\nextra cursors must use the exact same colors as the main cursor, not the colors\nof the cells they are on.\n\nQuerying for already set cursors\n--------------------------------------\n\nPrograms can ask the terminal what extra cursors are currently set, by sending\nthe escape code::\n\n    CSI > 100 TRAILER\n\nThe terminal must respond with **one** escape code::\n\n    CSI > 100; SHAPE:CO-ORDINATE TYPE:CO-ORDINATES ; ... TRAILER\n\nHere, the ``SHAPE:CO-ORDINATE TYPE:CO-ORDINATES`` block can be repeated any\nnumber of times, separated by ``;``. This response gives the set of shapes and\npositions currently active. If no cursors are currently active, there will be\nno blocks, just an empty response of the form::\n\n    CSI > 100 TRAILER\n\nAgain, terminals **must** respond in FIFO order so that multiplexers know where\nto direct the responses.\n\nQuerying for extra cursor colors\n-------------------------------------\n\nPrograms can ask the terminal what cursor colors are currently set, by sending\nescape code::\n\n    CSI > 101 TRAILER\n\nThe terminal must respond with **one** escape code::\n\n    CSI > 101 ; 30 : COLOR_SPACE : COLOR_PARAMETERS ; 40 : COLOR_SPACE : COLOR_PARAMETERS TRAILER\n\nThe number and type of ``COLOR_PARAMETERS`` depends on the preceding\n``COLOR_SPACE`` and can be omitted for some ``COLOR_SPACE`` values. See the\nsection :ref:`extra_cursor_color` for details.\n\n\nInteraction with other terminal controls and state\n-------------------------------------------------------\n\n**The main cursor**\n    The extra cursors must all have the same color and opacity and blink state\n    as the main cursor. The main cursor's visibility must not affect the\n    visibility of the extra cursors. Their visibility and shape are controlled\n    only by this protocol.\n\n**Clearing the screen**\n    The escape codes used to clear the screen (`ED <https://vt100.net/docs/vt510-rm/ED.html>`__)\n    with parameters 2, 3 and 22 must remove all extra cursors,\n    this is so that the clear command can be used by users to clear the screen of extra cursors.\n\n**Reset***\n    This must remove all extra cursors.\n\n**Alternate screen***\n    Switching between the main and alternate screens must remove all extra\n    cursors.\n\n**Scrolling**\n    The index (IND) and reverse index (RI) escape codes that cause screen\n    contents to scroll into scrollback or off screen must not affect\n    the extra cursors in any way. They remain at exactly the same position.\n    It is up to applications to manage extra cursor positions when using these\n    escape codes if needed. There are not a lot of use cases for scrolling\n    extra cursors with screen content, since extra cursors are meant to be\n    ephemeral and on screen only, not in scrollback. This allows terminals\n    to avoid the extra overhead of adjusting positions of the extra cursors\n    on every scroll.\n\n\nFootnotes\n-------------\n\n.. [anim] kitty allows the cursor blink to be :opt:`animated\n   <cursor_blink_interval>` using any CSS easing function. This cannot be\n   implemented using fake cursors.\n\n.. [rv] kitty has a special \"reverse video\" color mode for cursors where the\n   color of the cursor and the text under the cursor is adjusted based on the\n   color of the cell under the cursor. This also cannot be implemented using\n   fake cursors.\n"
  },
  {
    "path": "docs/notifications.py",
    "content": "#!/usr/bin/env python\n\n# A sample script to process notifications. Save it as\n# ~/.config/kitty/notifications.py\n\nimport subprocess\n\nfrom kitty.notifications import NotificationCommand, Urgency\n\n\ndef log_notification(nc: NotificationCommand) -> None:\n    # Log notifications to /tmp/notifications-log.txt\n    with open('/tmp/notifications-log.txt', 'a') as log:\n        print(f'title: {nc.title}', file=log)\n        print(f'body: {nc.body}', file=log)\n        print(f'app: {nc.application_name}', file=log)\n        print(f'types: {nc.notification_types}', file=log)\n        print('\\n', file=log)\n\n\ndef on_notification_activated(nc: NotificationCommand, which: int) -> None:\n    # do something when this notification is activated (clicked on)\n    # remember to assign this to the on_activation field in main()\n    pass\n\n\ndef main(nc: NotificationCommand) -> bool:\n    '''\n    This function should return True to filter out the notification\n    '''\n    log_notification(nc)\n\n    # filter out notifications with 'unwanted' in their titles\n    if 'unwanted' in nc.title.lower():\n        return True\n\n    # force the notification to be silent\n    nc.sound_name = 'silent'\n\n    # filter out notifications from the application badapp\n    if nc.application_name == 'badapp':\n        return True\n\n    # filter out low urgency notifications\n    if nc.urgency is Urgency.Low:\n        return True\n\n    # replace some bad text in the notification body\n    nc.body = nc.body.replace('bad text', 'good text')\n\n    # run a script if this notification is from myapp and has\n    # type foo, passing in the title and body as command line args\n    # to the script.\n    if nc.application_name == 'myapp' and 'foo' in nc.notification_types:\n        subprocess.Popen(['/path/to/my/script', nc.title, nc.body])\n\n    # do some arbitrary actions when this notification is activated\n    nc.on_activation = on_notification_activated\n\n    # dont filter out this notification\n    return False\n"
  },
  {
    "path": "docs/open_actions.rst",
    "content": "Scripting the mouse click\n======================================================\n\n|kitty| has support for :term:`terminal hyperlinks <hyperlinks>`. These are\ngenerated by many terminal programs, such as ``ls``, ``gcc``, ``systemd``,\n:ref:`tool_mcat`, etc. You can customize exactly what happens when clicking on\nthese hyperlinks in |kitty|.\n\nYou can tell kitty to take arbitrarily many, complex actions when a link is\nclicked. Let us illustrate with some examples, first. Create the file\n:file:`~/.config/kitty/open-actions.conf` with the following:\n\n.. code:: conf\n\n    # Open any image in the full kitty window by clicking on it\n    protocol file\n    mime image/*\n    action launch --type=overlay kitten icat --hold -- ${FILE_PATH}\n\nNow, run ``ls --hyperlink=auto`` in kitty and click on the filename of an\nimage, holding down :kbd:`ctrl+shift`. It will be opened over the current\nwindow. Press any key to close it.\n\n.. note::\n\n    The :program:`ls` comes with macOS does not support hyperlink, you need to\n    install `GNU Coreutils <https://www.gnu.org/software/coreutils/>`__. If you\n    install it via `Homebrew <https://formulae.brew.sh/formula/coreutils>`__, it\n    will be :program:`gls`.\n\nEach entry in :file:`open-actions.conf` consists of one or more\n:ref:`matching_criteria`, such as ``protocol`` and ``mime`` and one or more\n``action`` entries. In the example above kitty uses the :doc:`launch <launch>`\naction which can be used to run external programs. Entries are separated by\nblank lines.\n\nActions are very powerful, anything that you can map to a key combination in\n:file:`kitty.conf` can be used as an action. You can specify more than one\naction per entry if you like, for example:\n\n\n.. code:: conf\n\n    # Tail a log file (*.log) in a new OS Window and reduce its font size\n    protocol file\n    ext log\n    action launch --title ${FILE} --type=os-window tail -f -- ${FILE_PATH}\n    action change_font_size current -2\n\n\nIn the launch specification you can expand environment variables, as shown in\nthe examples above. In addition to regular environment variables, there are\nsome special variables, documented below:\n\n``URL``\n    The full URL being opened\n\n``FILE_PATH``\n    The path portion of the URL (unquoted)\n\n``FILE``\n    The file portion of the path of the URL (unquoted)\n\n``FRAGMENT``\n    The fragment (unquoted), if any of the URL or the empty string.\n\n``NETLOC``\n    The net location aka hostname (unquoted), if any of the URL or the empty string.\n\n``URL_PATH``\n    The path, query and fragment portions of the URL, without any\n    unquoting.\n\n``EDITOR``\n    The terminal based text editor. The configured :opt:`editor` in\n    :file:`kitty.conf` is preferred.\n\n``SHELL``\n    The path to the shell. The configured :opt:`shell` in :file:`kitty.conf` is\n    preferred, without arguments.\n\n.. note::\n   You can use the :opt:`action_alias` option just as in :file:`kitty.conf` to\n   define aliases for frequently used actions.\n\n\n.. _matching_criteria:\n\nMatching criteria\n------------------\n\nAn entry in :file:`open-actions.conf` must have one or more matching criteria.\nURLs that match all criteria for an entry will trigger that entry's actions.\nProcessing stops at the first matching entry, so put more specific matching\ncriteria at the start of the list. Entries in the file are separated by blank\nlines. The various available criteria are:\n\n``protocol``\n    A comma separated list of protocols, for example: ``http, https``. If\n    absent, there is no constraint on protocol.\n\n``url``\n    A regular expression that must match against the entire (unquoted) URL\n\n``fragment_matches``\n    A regular expression that must match against the fragment (part after #) in\n    the URL\n\n``mime``\n    A comma separated list of MIME types, for example: ``text/*, image/*,\n    application/pdf``. You can add MIME types to kitty by creating a file named\n    :file:`mime.types` in the :ref:`kitty configuration directory <confloc>`.\n    Useful if your system MIME database does not have definitions you need. This\n    file is in the standard format of one definition per line, like:\n    ``text/plain rst md``. Note that the MIME type for directories is\n    ``inode/directory``. MIME types are detected based on file extension, not\n    file contents.\n\n``ext``\n    A comma separated list of file extensions, for example: ``jpeg, tar.gz``\n\n``file``\n    A shell glob pattern that must match the filename, for example:\n    ``image-??.png``\n\n\n.. _launch_actions:\n\nScripting the opening of files with kitty\n-------------------------------------------------------\n\nOn macOS you can use :guilabel:`Open With` in Finder or drag and drop files and\nURLs onto the kitty dock icon to open them with kitty. Similarly on Linux, you\ncan associate certain files types to open in kitty. The default actions are:\n\n* Open text files in your editor and images using the icat kitten.\n* Run shell scripts in a shell\n* Open SSH urls using the ssh command\n\nThese actions can also be executed from the command line by running::\n\n    kitty +open file_or_url another_url ...\n\n    # macOS only\n    open -a kitty.app file_or_url another_url ...\n\nSince macOS lacks an official interface to set default URL scheme handlers,\nkitty has a command you can use for it. The first argument for is the URL\nscheme, and the second optional argument is the bundle id of the app, which\ndefaults to kitty, if not specified. For example:\n\n.. code-block:: sh\n\n    # Set kitty as the handler for ssh:// URLs\n    kitty +runpy 'from kitty.fast_data_types import cocoa_set_url_handler; import sys; cocoa_set_url_handler(*sys.argv[1:]); print(\"OK\")' ssh\n    # Set someapp as the handler for xyz:// URLs\n    kitty +runpy 'from kitty.fast_data_types import cocoa_set_url_handler; import sys; cocoa_set_url_handler(*sys.argv[1:]); print(\"OK\")' xyz someapp.bundle.identifier\n\nYou can customize these actions by creating a :file:`launch-actions.conf` file\nin the :ref:`kitty config directory <confloc>`, just like the\n:file:`open-actions.conf` file above. For example:\n\n.. literalinclude:: ../kitty/open_actions.py\n   :language: conf\n   :start-at: # Open script files\n   :end-before: '''.splitlines()))\n"
  },
  {
    "path": "docs/overview.rst",
    "content": "Overview\n==============\n\nDesign philosophy\n-------------------\n\n|kitty| is designed for power keyboard users. To that end all its controls work\nwith the keyboard (although it fully supports mouse interactions as well). Its\nconfiguration is a simple, human editable, single file for easy reproducibility\n(I like to store configuration in source control).\n\nThe code in |kitty| is designed to be simple, modular and hackable. It is\nwritten in a mix of C (for performance sensitive parts), Python (for easy\nextensibility and flexibility of the UI) and Go (for the command line\n:term:`kittens`).  It does not depend on any large and complex UI toolkit,\nusing only OpenGL for rendering everything.\n\nFinally, |kitty| is designed from the ground up to support all modern terminal\nfeatures, such as Unicode, true color, bold/italic fonts, text formatting, etc.\nIt even extends existing text formatting escape codes, to add support for\nfeatures not available elsewhere, such as colored and styled (curly) underlines.\nOne of the design goals of |kitty| is to be easily extensible so that new\nfeatures can be added in the future with relatively little effort.\n\n.. include:: basic.rst\n\n\nConfiguring kitty\n-------------------\n\n|kitty| is highly configurable, everything from keyboard shortcuts to painting\nframes-per-second. Press :sc:`edit_config_file` in kitty to open its fully\ncommented sample config file in your text editor. For details see the\n:doc:`configuration docs <conf>`.\n\nYou can quickly browse all available mappable actions by pressing\n:sc:`command_palette`.\n\n.. toctree::\n   :hidden:\n\n   conf\n\n\n.. _layouts:\n\nLayouts\n----------\n\nA :term:`layout` is an arrangement of multiple :term:`kitty windows <window>`\ninside a top-level :term:`OS window <os_window>`. The layout manages all its\nwindows automatically, resizing and moving them as needed. You can create a new\n:term:`window` using the :sc:`new_window` key combination.\n\nCurrently, there are seven layouts available:\n\n* **Fat** -- One (or optionally more) windows are shown full width on the top,\n  the rest of the windows are shown side-by-side on the bottom\n\n* **Grid** -- All windows are shown in a grid\n\n* **Horizontal** -- All windows are shown side-by-side\n\n* **Splits** -- Windows arranged in arbitrary patterns created using horizontal\n  and vertical splits\n\n* **Stack** -- Only a single maximized window is shown at a time\n\n* **Tall** -- One (or optionally more) windows are shown full height on the\n  left, the rest of the windows are shown one below the other on the right\n\n* **Vertical** -- All windows are shown one below the other\n\nBy default, all layouts are enabled and you can switch between layouts using\nthe :sc:`next_layout` key combination. You can also create shortcuts to select\nparticular layouts, and choose which layouts you want to enable, see\n:ref:`conf-kitty-shortcuts.layout` for examples. The first layout listed in\n:opt:`enabled_layouts` becomes the default layout.\n\nFor more details on the layouts and how to use them see :doc:`the documentation\n<layouts>`.\n\n.. toctree::\n   :hidden:\n\n   layouts\n\nExtending kitty\n------------------\n\nkitty has a powerful framework for scripting. You can create small terminal\nprograms called :doc:`kittens <kittens_intro>`. These can be used to add features\nto kitty, for example, :doc:`editing remote files <kittens/remote_file>` or\n:doc:`inputting Unicode characters <kittens/unicode_input>`. They can also be\nused to create programs that leverage kitty's powerful features, for example,\n:doc:`viewing images <kittens/icat>` or :doc:`diffing files with image support\n<kittens/diff>`.\n\nYou can :doc:`create your own kittens to scratch your own itches\n<kittens/custom>`.\n\nFor a list of all the builtin kittens, run ``kitten`` in kitty, or to browse\nsome of the more prominent ones, see :ref:`see here <kittens>`.\n\nAdditionally, you can use the :ref:`watchers <Watchers>` framework\nto create Python scripts that run in response to various events such as windows\nbeing resized, closing, having their titles changed, etc.\n\nFinally, there is remote control which allows you to control kitty from\nanywhere, even across a network! See below for more about remote control.\n\n.. toctree::\n   :hidden:\n\n   kittens_intro\n\n\nRemote control\n------------------\n\n|kitty| has a very powerful system that allows you to control it from the\n:doc:`shell prompt, even over SSH <remote-control>`. You can change colors,\nfonts, open new :term:`windows <window>`, :term:`tabs <tab>`, set their titles,\nchange window layout, get text from one window and send text to another, etc.\nThe possibilities are endless. See the :doc:`tutorial <remote-control>` to get\nstarted.\n\n.. toctree::\n   :hidden:\n\n   remote-control\n\n\nSessions\n------------------\n\nYou can control the :term:`tabs <tab>`, :term:`kitty window <window>` layout,\nworking directory, startup programs, etc. by creating a *session* file and using\nthe :option:`kitty --session` command line flag or the :opt:`startup_session`\noption in :file:`kitty.conf`. You can also easily switch between sessions with\na keypress. See :doc:`sessions` for details.\n\n\nCreating tabs/windows\n-------------------------------\n\nkitty can be told to run arbitrary programs in new :term:`tabs <tab>`,\n:term:`windows <window>` or :term:`overlays <overlay>` at a keypress.\nTo learn how to do this, see :doc:`here <launch>`.\n\n.. toctree::\n   :hidden:\n\n   launch\n\n\nMouse features\n-------------------\n\n* You can click on a URL to open it in a browser.\n* You can double click to select a word and then drag to select more words.\n* You can triple click to select a line and then drag to select more lines.\n* You can triple click while holding :kbd:`Ctrl+Alt` to select from clicked\n  point to end of line.\n* You can right click to extend a previous selection.\n* You can hold down :kbd:`Ctrl+Alt` and drag with the mouse to select in\n  columns.\n* Selecting text automatically copies it to the primary clipboard (on platforms\n  with a primary clipboard).\n* You can middle click to paste from the primary clipboard (on platforms with a\n  primary clipboard).\n* You can right click while holding :kbd:`Ctrl+Shift` to open the output of the\n  clicked on command in a pager (requires :ref:`shell_integration`)\n* You can select text with kitty even when a terminal program has grabbed the\n  mouse by holding down the :kbd:`Shift` key\n\nAll these actions can be customized in :file:`kitty.conf` as described\n:ref:`here <conf-kitty-mouse.mousemap>`.\n\nYou can also customize what happens when clicking on :term:`hyperlinks` in\nkitty, having it open files in your editor, download remote files, open things\nin your browser, etc. For details, see :doc:`here <open_actions>`.\n\nAdditionally, various bits of the kitty UI itself work with the mouse. You can\ndrag and drop tabs in the tab bar to re-order them or move them from one OS\nWindow to another, or even pop them out into a new OS Window.\nYou can drag window borders to resize windows. You can double click on empty regions\nof the tab bar to create new tabs or double click on an existing tab to rename\nit.\n\n.. toctree::\n   :hidden:\n\n   open_actions\n\nFont control\n-----------------\n\n|kitty| has extremely flexible and powerful font selection features. You can\nspecify individual families for the regular, bold, italic and bold+italic fonts.\nYou can even specify specific font families for specific ranges of Unicode\ncharacters. This allows precise control over text rendering. It can come in\nhandy for applications like powerline, without the need to use patched fonts.\nSee the various font related configuration directives in\n:ref:`conf-kitty-fonts`.\n\n\n.. _scrollback:\n\nThe scrollback buffer\n-----------------------\n\n|kitty| supports scrolling back to view history, just like most terminals. You\ncan use either keyboard shortcuts or the mouse scroll wheel to do so. |kitty|\ndisplays an interactive :opt:`scrollbar` along the right edge\nof the window that shows your current position in the scrollback. You can click\nand drag the scrollbar to quickly navigate through the history.\n\nHowever, |kitty| has an extra, neat feature. Sometimes you need to explore the scrollback\nbuffer in more detail, maybe search for some text or refer to it side-by-side\nwhile typing in a follow-up command. |kitty| allows you to do this by pressing\nthe :sc:`show_scrollback` shortcut, which will open the scrollback buffer in\nyour favorite pager program (which is :program:`less` by default). Colors and\ntext formatting are preserved. You can explore the scrollback buffer comfortably\nwithin the pager.\n\nAdditionally, you can pipe the contents of the scrollback buffer to an\narbitrary, command running in a new :term:`window`, :term:`tab` or\n:term:`overlay`. For example::\n\n   map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting less +G -R\n\nWould open the scrollback buffer in a new :term:`window` when you press the\n:kbd:`F1` key. See :sc:`show_scrollback <show_scrollback>` for details.\n\nIf you want to use it with an editor such as :program:`nvim` to get more powerful\nfeatures, see for example, `kitty-scrollback.nvim\n<https://github.com/mikesmithgh/kitty-scrollback.nvim>`__ or `kitty-grab <https://github.com/yurikhan/kitty_grab>`__\nor see more tips for using various editor programs, in :iss:`this thread <719>`.\n\nIf you wish to store very large amounts of scrollback to view using the piping\nor :sc:`show_scrollback <show_scrollback>` features, you can use the\n:opt:`scrollback_pager_history_size` option.\n\n\nIntegration with shells\n---------------------------------\n\nkitty has the ability to integrate closely within common shells, such as `zsh\n<https://www.zsh.org/>`__, `fish <https://fishshell.com>`__ and `bash\n<https://www.gnu.org/software/bash/>`__ to enable features such as jumping to\nprevious prompts in the scrollback, viewing the output of the last command in\n:program:`less`, using the mouse to move the cursor while editing prompts, etc.\nSee :doc:`shell-integration` for details.\n\n.. toctree::\n   :hidden:\n\n   shell-integration\n\n.. _cpbuf:\n\nMultiple copy/paste buffers\n-----------------------------\n\nIn addition to being able to copy/paste from the system clipboard, in |kitty|\nyou can also setup an arbitrary number of copy paste buffers. To do so, simply\nadd something like the following to your :file:`kitty.conf`::\n\n   map f1 copy_to_buffer a\n   map f2 paste_from_buffer a\n\nThis will allow you to press :kbd:`F1` to copy the current selection to an\ninternal buffer named ``a`` and :kbd:`F2` to paste from that buffer. The buffer\nnames are arbitrary strings, so you can define as many such buffers as you need.\n\n\nMarks\n-------------\n\nkitty has the ability to mark text on the screen based on regular expressions.\nThis can be useful to highlight words or phrases when browsing output from long\nrunning programs or similar. To learn how this feature works, see :doc:`marks`.\n\n.. toctree::\n   :hidden:\n\n   marks\n"
  },
  {
    "path": "docs/performance.rst",
    "content": "Performance\n===================\n\nThe main goals for |kitty| performance are user perceived latency while typing\nand \"smoothness\" while scrolling as well as CPU usage. |kitty| tries hard to\nfind an optimum balance for these. To that end it keeps a cache of each\nrendered glyph in video RAM so that font rendering is not a bottleneck.\nInteraction with child programs takes place in a separate thread from\nrendering, to improve smoothness. Parsing of the byte stream is done using\n`vector CPU instructions\n<https://en.wikipedia.org/wiki/Single_instruction,_multiple_data>`__ for\nmaximum performance. Updates to the screen typically require sending just a few\nbytes to the GPU.\n\nThere are two config options you can tune to adjust the performance,\n:opt:`repaint_delay` and :opt:`input_delay`. These control the artificial delays\nintroduced into the render loop to reduce CPU usage. See\n:ref:`conf-kitty-performance` for details. See also the :opt:`sync_to_monitor`\noption to further decrease latency at the cost of some `screen tearing\n<https://en.wikipedia.org/wiki/Screen_tearing>`__ while scrolling.\n\nBenchmarks\n-------------\n\nMeasuring terminal emulator performance is fairly subtle, there are three main\naxes on which performance is measured: Energy usage for typical tasks,\nKeyboard to screen latency, and throughput (processing large amounts of data).\n\nKeyboard to screen latency\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThis is measured either with dedicated hardware, or software such as `Typometer\n<https://pavelfatin.com/typometer/>`__. Third party measurements comparing\nkitty with other terminal emulators on various systems show kitty has best in\nclass keyboard to screen latency.\n\nNote that to minimize latency at the expense of more energy usage, use the\nfollowing settings in kitty.conf::\n\n    input_delay 0\n    repaint_delay 2\n    sync_to_monitor no\n    wayland_enable_ime no\n\n`Hardware based measurement on macOS\n<https://thume.ca/2020/05/20/making-a-latency-tester/>`__ show that kitty and\nApple's Terminal.app share the crown for best latency. These\nmeasurements were done with :opt:`input_delay` at its default value of ``3 ms``\nwhich means kitty's actual numbers would be even lower.\n\n`Typometer based measurements on Linux\n<https://github.com/kovidgoyal/kitty/issues/2701#issuecomment-911089374>`__\nshow that kitty has far and away the best latency of the terminals tested.\n\n.. _throughput:\n\nThroughput\n^^^^^^^^^^^^^^^^\n\nkitty has a builtin kitten to measure throughput, it works by dumping large\namounts of data of different types into the tty device and measuring how fast\nthe terminal parses and responds to it. The measurements below were taken with\nthe same font, font size and window size for all terminals, and default\nsettings, on the same computer. They clearly show kitty has the fastest\nthroughput. To run the tests yourself, run ``kitten __benchmark__`` in the\nterminal emulator you want to test, where the kitten binary is part of the\nkitty install.\n\nThe numbers are megabytes per second of data that the terminal\nprocesses. Measurements were taken under Linux/X11 with an ``AMD Ryzen 7 PRO\n5850U``. Entries are in order of decreasing performance. kitty is twice\nas fast as the next best.\n\n================   ======  ======= ===== ====== =======\nTerminal           ASCII   Unicode CSI   Images Average\n================   ======  ======= ===== ====== =======\nkitty 0.33         121.8   105.0   59.8  251.6  134.55\ngnometerm 3.50.1   33.4    55.0    16.1  142.8  61.83\nalacritty 0.13.1   43.1    46.5    32.5  94.1   54.05\nwezterm 20230712   16.4    26.0    11.1  140.5  48.5\nxterm 389          47.7    18.3    0.6   56.3   30.72\nkonsole 23.08.04   25.2    37.7    23.6  23.4   27.48\nalacritty+tmux     30.3    7.8     14.7  46.1   24.73\n================   ======  ======= ===== ====== =======\n\nIn this table, each column represents different types of data. The CSI column\nis for data consisting of a mix of typical formatting escape codes and some\nASCII only text.\n\n.. note::\n\n   By default, the benchmark kitten suppresses actual rendering, to better\n   focus on parser speed, you can pass it the ``--render`` flag to not suppress\n   rendering. However, modern terminals typically render asynchronously,\n   therefore the numbers are not really useful for comparison, as it is just a\n   game about how much input to *batch* before rendering the next frame.\n   However, even with rendering enabled kitty is still faster than all the\n   rest. For brevity those numbers are not included.\n\n.. note::\n\n   foot, iterm2 and Terminal.app are left out as they do not run under X11.\n   Alacritty+tmux is included just to show the effect of putting a terminal\n   multiplexer into the mix (halving throughput) and because alacritty isn't\n   remotely comparable to any of the other terminals feature wise without tmux.\n\n.. note::\n\n   konsole, gnome-terminal and xterm do not support the `Synchronized update\n   <https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec>`__\n   escape code used to suppress rendering, if and when they gain support for it\n   their numbers are likely to improve by ``20 - 50%``, depending on how well they\n   implement it.\n\n\nEnergy usage\n^^^^^^^^^^^^^^^^^\n\nSadly, I do not have the infrastructure to measure actual energy usage so CPU\nusage will have to stand in for it. Here are some CPU usage numbers for the\ntask of scrolling a file continuously in :program:`less`. The CPU usage is for\nthe terminal process and X together and is measured using :program:`htop`. The\nmeasurements are taken at the same font and window size for all terminals on a\n``Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz`` CPU with a ``Advanced Micro\nDevices, Inc. [AMD/ATI] Cape Verde XT [Radeon HD 7770/8760 / R7 250X]`` GPU.\n\n==============   =========================\nTerminal         CPU usage (X + terminal)\n==============   =========================\n|kitty|          6 - 8%\nxterm            5 - 7% (but scrolling was extremely janky)\ntermite          10 - 13%\nurxvt            12 - 14%\ngnome-terminal   15 - 17%\nkonsole          29 - 31%\n==============   =========================\n\nAs you can see, |kitty| uses much less CPU than all terminals, except xterm, but\nits scrolling \"smoothness\" is much better than that of xterm (at least to my,\nadmittedly biased, eyes).\n\nInstrumenting kitty\n-----------------------\n\nYou can generate detailed per-function performance data using\n`gperftools <https://github.com/gperftools/gperftools>`__. Build |kitty| with\n``make profile``. Run kitty and perform the task you want to analyse, for\nexample, scrolling a large file with :program:`less`. After you quit, function\ncall statistics will be displayed in *KCachegrind*. Hence, profiling is best done\non Linux which has these tools easily available.\n"
  },
  {
    "path": "docs/pipe.rst",
    "content": ":orphan:\n\nWorking with the screen and history buffer contents\n======================================================\n\n.. warning::\n    The pipe action has been deprecated in favor of the\n    :doc:`launch <launch>` action which is more powerful.\n\nYou can pipe the contents of the current screen and history buffer as\n:file:`STDIN` to an arbitrary program using the ``pipe`` function. The program\ncan be displayed in a kitty window or overlay.\n\nFor example, the following in :file:`kitty.conf` will open the scrollback\nbuffer in less in an overlay window, when you press :kbd:`F1`::\n\n    map f1 pipe @ansi overlay less +G -R\n\nThe syntax of the ``pipe`` function is::\n\n   pipe <input placeholder> <destination window type> <command line to run>\n\n\nThe piping environment\n--------------------------\n\nThe program to which the data is piped has a special environment variable\ndeclared, ``KITTY_PIPE_DATA`` whose contents are::\n\n   KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}\n\nwhere ``scrolled_by`` is the number of lines kitty is currently scrolled by,\n``cursor_(x|y)`` is the position of the cursor on the screen with ``(1,1)``\nbeing the top left corner and ``{lines},{columns}`` being the number of rows\nand columns of the screen.\n\nYou can choose where to run the pipe program:\n\n``overlay``\n   An overlay window over the current kitty window\n\n``window``\n   A new kitty window\n\n``os_window``\n   A new top-level window\n\n``tab``\n   A new window in a new tab\n\n``clipboard, primary``\n   Copy the text directly to the clipboard. In this case the specified program\n   is not run, so use some dummy program name for it.\n\n``none``\n   Run it in the background\n\n\nInput placeholders\n--------------------\n\nThere are various different kinds of placeholders\n\n``@selection``\n   Plain text, currently selected text\n\n``@text``\n   Plain text, current screen + scrollback buffer\n\n``@ansi``\n   Text with formatting, current screen + scrollback buffer\n\n``@screen``\n   Plain text, only current screen\n\n``@ansi_screen``\n   Text with formatting, only current screen\n\n``@alternate``\n   Plain text, secondary screen. The secondary screen is the screen not currently displayed. For\n   example if you run a fullscreen terminal application, the secondary screen will\n   be the screen you return to when quitting the application.\n\n``@ansi_alternate``\n   Text with formatting, secondary screen.\n\n``@alternate_scrollback``\n   Plain text, secondary screen + scrollback, if any.\n\n``@ansi_alternate_scrollback``\n   Text with formatting, secondary screen + scrollback, if any.\n\n``none``\n   No input\n\n\nYou can also add the suffix ``_wrap`` to the placeholder, in which case kitty\nwill insert the carriage return at every line wrap location (where long lines\nare wrapped at screen edges). This is useful if you want to pipe to program\nthat wants to duplicate the screen layout of the screen.\n"
  },
  {
    "path": "docs/pointer-shapes.rst",
    "content": "Mouse pointer shapes\n=======================\n\n.. versionadded:: 0.31.0\n\nThis is a simple escape code that can be used by terminal programs to change\nthe shape of the mouse pointer. This is useful for buttons/links, dragging to\nresize panes, etc. It is based on the original escape code proposal from xterm\nhowever, it properly specifies names for the different shapes in a system\nindependent manner, adds a stack for easy push/pop of shapes, allows programs\nto query support and specifies interaction with other terminal state.\n\nThe escape code is of the form::\n\n    <OSC> 22 ; <optional first char> <comma-separates list of shape names> <ESC>\\\n\nHere, ``<OSC>`` is the bytes ``<ESC>]`` and ``<ESC>`` is the byte ``0x1b``.\nSpaces in the above are present for clarity only and should not be actually used.\n\nFirst some examples::\n\n    # Set the pointer to a pointing hand\n    <OSC> 22 ; pointer <ESC>\\\n    # Reset the pointer to default\n    <OSC> 22 ; <ESC>\\\n    # Push a shape onto the stack making it the current shape\n    <OSC> 22 ; >wait <ESC>\\\n    # Pop a shape off the stack restoring to the previous shape\n    <OSC> 22 ; < <ESC>\\\n    # Query the terminal for what the currently set shape is\n    <OSC> 22 ; ?__current__ <ESC>\\\n\nTo demo the various shapes, simply run the following command inside kitty::\n\n    kitten mouse-demo\n\nFor more details see below.\n\nSetting the pointer shape\n-------------------------------\n\nFor set operations, the optional first char can be either ``=`` or omitted.\nFollow the first char with the name of the shape. See the\n:ref:`pointer_shape_names` table.\n\n\nPushing and popping shapes onto the stack\n---------------------------------------------\n\nThe terminal emulator maintains a stack of shapes. To add shapes to the stack,\nthe optional first char must be ``>`` followed by a comma separated list of\nshape names. See the :ref:`pointer_shape_names` table. All the specified names\nare added to the stack, with the last name being the top of the stack and the\ncurrent shape. If the stack is full, the entry at the bottom of the stack is\nevicted. Terminal implementations are free to choose an appropriate maximum\nstack size, with a minimum stack size of 16.\n\nTo pop shapes of the top of the stack the optional first char must be ``<``.\nThe comma separated list of names is ignored. Once the stack is empty further\npops have no effect. An empty stack means the terminal is free to use whatever\npointer shape it likes.\n\n\nQuerying support\n-------------------\n\nTerminal programs can ask the terminal about this feature by setting the\noptional first char to ``?``. The comma separated list of names is then\nconsidered the query to which the terminal must respond with an OSC 22 code.\nFor example::\n\n    <OSC> 22 ; ?__current__ <ESC>\\\n    results in\n    <OSC> 22 ; shape_name <ESC>\\\n\nHere, ``shape_name`` will be a name from the table of shape names below or ``0``\nif the stack is empty, i.e., no shape is currently set.\n\nTo check if the terminal supports some shapes, pass the shape names and the\nterminal will reply with a comma separated list of zeros and ones where 1 means\nthe shape name is supported and zero means it is not. For example::\n\n    <OSC> 22 ; ?pointer,crosshair,no-such-name,wait <ESC>\\\n    results in\n    <OSC> 22 ; 1,1,0,1 <ESC>\\\n\nIn addition to ``__current__`` there are a couple of other special names::\n\n    __default__ - The terminal responds with the shape name of the shape used by default\n    __grabbed__ - The terminal responds with the shape name of the shape used when the mouse is \"grabbed\"\n\n\nInteraction with other terminal features\n---------------------------------------------\n\nThe terminal must maintain separate shape stacks for the *main* and *alternate*\nscreens. This allows full screen programs, which are likely to be the main\nconsumers of this feature, to easily temporarily switch back from the alternate screen,\nwithout needing to worry about pointer shape state. Think of suspending a\nterminal editor to get back to the shell, for example.\n\nResetting the terminal must empty both the shape stacks.\n\nWhen dragging to select text, the terminal is free to ignore any mouse pointer\nshape specified using this escape code in favor of one appropriate for\ndragging.  Similarly, when hovering over a URL or OSC 8 based hyperlink, the\nterminal may choose to change the mouse pointer regardless of the value set by\nthis escape code.\n\nThis feature is independent of mouse reporting. The changed pointer shapes apply\nregardless of whether the terminal program has enabled mouse reporting or not.\n\n\n.. _pointer_shape_names:\n\nPointer shape names\n----------------------------------\n\nThere is a well defined set of shape names that all conforming terminal\nemulators must support. The list is based on the names used by the `cursor\nproperty in the CSS standard\n<https://developer.mozilla.org/en-US/docs/Web/CSS/cursor>`__, click the link to\nsee representative images for the names. Valid names must consist of only the\ncharacters from the set ``a-z0-9_-``.\n\n.. start list of shape css names (auto generated by gen-key-constants.py do not edit)\n\n#. alias\n#. cell\n#. copy\n#. crosshair\n#. default\n#. e-resize\n#. ew-resize\n#. grab\n#. grabbing\n#. help\n#. move\n#. n-resize\n#. ne-resize\n#. nesw-resize\n#. no-drop\n#. not-allowed\n#. ns-resize\n#. nw-resize\n#. nwse-resize\n#. pointer\n#. progress\n#. s-resize\n#. se-resize\n#. sw-resize\n#. text\n#. vertical-text\n#. w-resize\n#. wait\n#. zoom-in\n#. zoom-out\n\n.. end list of shape css names\n\nTo demo the various shapes, simply run the following command inside kitty::\n\n    kitten mouse-demo\n\nLegacy xterm compatibility\n----------------------------\n\nThe original xterm proposal for this escape code used shape names from the\n:file:`X11/cursorfont.h` header on X11 based systems. Terminal implementations\nwishing to maintain compatibility with xterm can also implement these names as\naliases for the CSS based names defined in the :ref:`pointer_shape_names` table.\n\nThe simplest mode of operation of this escape code, which is no leading\noptional char and a single shape name is compatible with xterm.\n"
  },
  {
    "path": "docs/press-mentions.rst",
    "content": "Press mentions of kitty\n========================\n\n`Python Bytes 272 <https://youtu.be/8HKliSbA-gQ?t=815>`__ (Feb 2022)\n    A podcast demoing some of kitty's coolness\n\n`Console #88 <https://console.substack.com/p/console-88>`__ (Jan 2022)\n    An interview with Kovid about kitty\n\n\nVideo reviews\n--------------\n\n`Review (Jan 2021) <https://www.youtube.com/watch?v=TTzP2zYJn2k>`__\n    A kitty review by distrotube\n\n`Review (Dec 2020) <https://www.youtube.com/watch?v=KUMkLhFeBrI>`__\n    A kitty review/intro by TechHut\n"
  },
  {
    "path": "docs/protocol-extensions.rst",
    "content": "Terminal protocol extensions\n===================================\n\n|kitty| has extensions to the legacy terminal protocol, to enable advanced\nfeatures. These are typically in the form of new or re-purposed escape codes.\nWhile these extensions are currently |kitty| specific, it would be nice to get\nsome of them adopted more broadly, to push the state of terminal emulators\nforward.\n\nThe goal of these extensions is to be as small and unobtrusive as possible,\nwhile filling in some gaps in the existing xterm protocol. In particular, one of\nthe goals of this specification is explicitly not to \"re-imagine\" the TTY. The\nTTY should remain what it is -- a device for efficiently processing text\nreceived as a simple byte stream. Another objective is to only move the minimum\npossible amount of extra functionality into the terminal program itself. This is\nto make it as easy to implement these protocol extensions as possible, thereby\nhopefully encouraging their widespread adoption.\n\nIf you wish to discuss these extensions, propose additions or changes to them,\nplease do so by opening issues in the `GitHub bug tracker\n<https://github.com/kovidgoyal/kitty/issues>`__.\n\n\n.. toctree::\n   :maxdepth: 1\n\n   underlines\n   graphics-protocol\n   keyboard-protocol\n   text-sizing-protocol\n   multiple-cursors-protocol\n   file-transfer-protocol\n   desktop-notifications\n   pointer-shapes\n   unscroll\n   color-stack\n   deccara\n   clipboard\n   misc-protocol\n"
  },
  {
    "path": "docs/quake-screenshots.rst",
    "content": "\n.. sidebar::\n\n    .. only:: not man\n\n        **Screenshots**\n\n        .. figure:: /screenshots/quake-macos.webp\n            :alt: Screenshot, showing the kitty floating quick access terminal above the background which is the program btop, running inside kitty, on macOS\n            :align: center\n            :width: 100%\n\n            macOS\n\n\n        .. figure:: /screenshots/quake-hypr.webp\n            :alt: Screenshot, showing the kitty floating quick access terminal above the background which is the program btop, running inside kitty, on Hyprland in Linux\n            :align: center\n            :width: 100%\n\n            Linux\n\n        .. figure:: /screenshots/panel.png\n            :alt: Screenshot, showing a sample panel\n            :align: center\n            :width: 100%\n\n            A sample panel on Linux\n\n        How the screenshots :ref:`were generated <quake_ss>`.\n"
  },
  {
    "path": "docs/quickstart.rst",
    "content": ".. _quickstart:\n\nQuickstart\n===========\n\n.. toctree::\n   :hidden:\n\n   binary\n   build\n\nPre-built binaries of |kitty| are available for both macOS and Linux. See the\n:doc:`binary install instructions </binary>`. You can also :doc:`build from\nsource </build>`.\n\nAdditionally, you can use your favorite package manager to install the |kitty|\npackage, but note that some Linux distribution packages are woefully outdated.\n|kitty| is available in a vast number of package repositories for macOS\nand Linux.\n\n.. image:: https://repology.org/badge/tiny-repos/kitty-terminal.svg\n   :target: https://repology.org/project/kitty-terminal/versions\n   :alt: Number of repositories kitty is available in\n\nSee :doc:`Configuring kitty <conf>` for help on configuring |kitty| and\n:doc:`Invocation <invocation>` for the command line arguments |kitty| supports.\n\nFor a tour of kitty's design and features, see the :doc:`overview`.\n"
  },
  {
    "path": "docs/rc_protocol.rst",
    "content": "The kitty remote control protocol\n==================================\n\nThe kitty remote control protocol is a simple protocol that involves sending\ndata to kitty in the form of JSON. Any individual command of kitty has the\nform::\n\n    <ESC>P@kitty-cmd<JSON object><ESC>\\\n\nWhere ``<ESC>`` is the byte ``0x1b``. The JSON object has the form:\n\n.. code-block:: json\n\n    {\n        \"cmd\": \"command name\",\n        \"version\": \"<kitty version>\",\n        \"no_response\": \"<Optional Boolean>\",\n        \"kitty_window_id\": \"<Optional value of the KITTY_WINDOW_ID env var>\",\n        \"payload\": \"<Optional JSON object>\"\n    }\n\nThe ``version`` above is an array of the form :code:`[0, 14, 2]`. If you are\ndeveloping a standalone client, use the kitty version that you are developing\nagainst. Using a version greater than the version of the kitty instance you are\ntalking to, will cause a failure.\n\nSet ``no_response`` to ``true`` if you don't want a response from kitty.\n\nThe optional payload is a JSON object that is specific to the actual command\nbeing sent. The fields in the object for every command are documented below.\n\nAs a quick example showing how easy to use this protocol is, we will implement\nthe ``@ ls`` command from the shell using only shell tools.\n\nFirst, run kitty as::\n\n    kitty -o allow_remote_control=socket-only --listen-on unix:/tmp/test\n\nNow, in a different terminal, you can get the pretty printed ``@ ls`` output\nwith the following command line::\n\n    echo -en '\\eP@kitty-cmd{\"cmd\":\"ls\",\"version\":[0,14,2]}\\e\\\\' | socat - unix:/tmp/test | awk '{ print substr($0, 13, length($0) - 14) }' | jq -c '.data | fromjson' | jq .\n\nThere is also the statically compiled stand-alone executable ``kitten``\nthat can be used for this, available from the `kitty releases\n<https://github.com/kovidgoyal/kitty/releases>`__ page::\n\n    kitten @ --help\n\n.. _rc_crypto:\n\nEncrypted communication\n--------------------------\n\n.. versionadded:: 0.26.0\n\nWhen using the :opt:`remote_control_password` option communication to the\nterminal is encrypted to keep the password secure. A public key is used from\nthe :envvar:`KITTY_PUBLIC_KEY` environment variable. Currently, only one\nencryption protocol is supported. The protocol number is present in\n:envvar:`KITTY_PUBLIC_KEY` as ``1``. The key data in this environment variable\nis :rfc:`Base-85 <1924>` encoded.  The algorithm used is `Elliptic Curve Diffie\nHelman <https://en.wikipedia.org/wiki/Elliptic-curve_Diffie–Hellman>`__ with\nthe `X25519 curve <https://en.wikipedia.org/wiki/Curve25519>`__. A time based\nnonce is used to minimise replay attacks. The original JSON command has the\nfields: ``password`` and ``timestamp`` added. The timestamp is the number of\nnanoseconds since the epoch, excluding leap seconds. Commands with a timestamp\nmore than 5 minutes from the current time are rejected. The command is then\nencrypted using AES-256-GCM in authenticated encryption mode, with a symmetric\nkey that is derived from the ECDH key-pair by running the shared secret through\nSHA-256 hashing, once.  An IV of at least 96 bits of CSPRNG data is used. The\ntag for authenticated encryption **must** be at least 128 bits long.  The tag\n**must** authenticate only the value of the ``encrypted`` field. A new command\nis created and transmitted that contains the fields:\n\n.. code-block:: json\n\n    {\n        \"version\": \"<kitty version>\",\n        \"iv\": \"base85 encoded IV\",\n        \"tag\": \"base85 encoded AEAD tag\",\n        \"pubkey\": \"base85 encoded ECDH public key of sender\",\n        \"encrypted\": \"The original command encrypted and base85 encoded\"\n    }\n\nAsync and streaming requests\n---------------------------------\n\nSome remote control commands require asynchronous communication, that is, the\nresponse from the terminal can happen after an arbitrary amount of time. For\nexample, the :code:`select-window` command requires the user to select a window\nbefore a response can be sent. Such command must set the field :code:`async`\nin the JSON block above to a random string that serves as a unique id. The\nclient can cancel an async request in flight by adding the :code:`cancel_async`\nfield to the JSON block. A async response remains in flight until the terminal\nsends a response to the request. Note that cancellation requests dont need to\nbe encrypted as users must not be prompted for these and the worst a malicious\ncancellation request can do is prevent another sync request from getting a\nresponse.\n\nSimilar to async requests are *streaming* requests. In these the client has to\nsend a large amount of data to the terminal and so the request is split into\nchunks. In every chunk the JSON block must contain the field ``stream`` set to\n``true`` and ``stream_id`` set to a random long string, that should be the same for\nall chunks in a request. End of data is indicated by sending a chunk with no data.\n\n.. include:: generated/rc.rst\n"
  },
  {
    "path": "docs/remote-control.rst",
    "content": "Control kitty from scripts\n----------------------------\n\n.. highlight:: sh\n\n|kitty| can be controlled from scripts or the shell prompt. You can open new\nwindows, send arbitrary text input to any window, change the title of windows\nand tabs, etc.\n\nLet's walk through a few examples of controlling |kitty|.\n\n\nTutorial\n------------\n\nStart by running |kitty| as::\n\n    kitty -o allow_remote_control=yes -o enabled_layouts=tall\n\nIn order for control to work, :opt:`allow_remote_control` or\n:opt:`remote_control_password` must be enabled in :file:`kitty.conf`. Here we\nturn it on explicitly at the command line.\n\nNow, in the new |kitty| window, enter the command::\n\n    kitten @ launch --title Output --keep-focus cat\n\nThis will open a new window, running the :program:`cat` program that will appear\nnext to the current window.\n\nLet's send some text to this new window::\n\n    kitten @ send-text --match cmdline:cat Hello, World\n\nThis will make ``Hello, World`` show up in the window running the :program:`cat`\nprogram. The :option:`kitten @ send-text --match` option is very powerful, it\nallows selecting windows by their titles, the command line of the program\nrunning in the window, the working directory of the program running in the\nwindow, etc. See :ref:`kitten @ send-text --help <at-send-text>` for details.\n\nMore usefully, you can pipe the output of a command running in one window to\nanother window, for example::\n\n    ls | kitten @ send-text --match 'title:^Output' --stdin\n\nThis will show the output of :program:`ls` in the output window instead of the\ncurrent window. You can use this technique to, for example, show the output of\nrunning :program:`make` in your editor in a different window. The possibilities\nare endless.\n\nYou can even have things you type show up in a different window. Run::\n\n    kitten @ send-text --match 'title:^Output' --stdin\n\nAnd type some text, it will show up in the output window, instead of the current\nwindow. Type :kbd:`Ctrl+D` when you are ready to stop.\n\nNow, let's open a new tab::\n\n   kitten @ launch --type=tab --tab-title \"My Tab\" --keep-focus bash\n\nThis will open a new tab running the bash shell with the title \"My Tab\".\nWe can change the title of the tab to \"New Title\" with::\n\n   kitten @ set-tab-title --match 'title:^My' New Title\n\nLet's change the title of the current tab::\n\n   kitten @ set-tab-title Master Tab\n\nNow lets switch to the newly opened tab::\n\n   kitten @ focus-tab --match 'title:^New'\n\nSimilarly, to focus the previously opened output window (which will also switch\nback to the old tab, automatically)::\n\n   kitten @ focus-window --match 'title:^Output'\n\nYou can get a listing of available tabs and windows, by running::\n\n   kitten @ ls\n\nThis outputs a tree of data in JSON format. The top level of the tree is all\n:term:`OS windows <os_window>`. Each OS window has an id and a list of\n:term:`tabs <tab>`. Each tab has its own id, a title and a list of :term:`kitty\nwindows <window>`. Each window has an id, title, current working directory,\nprocess id (PID) and command-line of the process running in the window. You can\nuse this information with :option:`kitten @ focus-window --match` to control\nindividual windows.\n\nAs you can see, it is very easy to control |kitty| using the ``kitten @``\nmessaging system. This tutorial touches only the surface of what is possible.\nSee ``kitten @ --help`` for more details.\n\nIn the example's above, ``kitten @`` messaging works only when run\ninside a |kitty| window, not anywhere. But, within a |kitty| window it even\nworks over SSH. If you want to control |kitty| from programs/scripts not running\ninside a |kitty| window, see the section on :ref:`using a socket for remote control <rc_via_socket>`\nbelow.\n\nNote that if all you want to do is run a single |kitty| \"daemon\" and have\nsubsequent |kitty| invocations appear as new top-level windows, you can use the\nsimpler :option:`kitty --single-instance` option, see ``kitty --help`` for that.\n\n\n.. _rc_via_socket:\n\nRemote control via a socket\n--------------------------------\nTo control kitty from outside kitty, it is necessary to setup a socket to\ncommunicate with kitty. First, start |kitty| as::\n\n    kitty -o allow_remote_control=yes --listen-on unix:/tmp/mykitty\n\nThe :option:`kitty --listen-on` option tells |kitty| to listen for control\nmessages at the specified UNIX-domain socket. See ``kitty --help`` for details.\nNow you can control this instance of |kitty| using the :option:`kitten @ --to`\ncommand line argument to ``kitten @``. For example::\n\n    kitten @ --to unix:/tmp/mykitty ls\n\n\nThe builtin kitty shell\n--------------------------\n\nYou can explore the |kitty| command language more easily using the builtin\n|kitty| shell. Run ``kitten @`` with no arguments and you will be dropped into\nthe |kitty| shell with completion for |kitty| command names and options.\n\nYou can even open the |kitty| shell inside a running |kitty| using a simple\nkeyboard shortcut (:sc:`kitty_shell` by default).\n\n.. note:: Using the keyboard shortcut has the added advantage that you don't need to use\n   :opt:`allow_remote_control` to make it work.\n\n\nAllowing only some windows to control kitty\n----------------------------------------------\n\nIf you do not want to allow all programs running in |kitty| to control it, you\ncan selectively enable remote control for only some |kitty| windows. Simply\ncreate a shortcut such as::\n\n    map ctrl+k launch --allow-remote-control some_program\n\nThen programs running in windows created with that shortcut can use ``kitten @``\nto control kitty. Note that any program with the right level of permissions can\nstill write to the pipes of any other program on the same computer and therefore\ncan control |kitty|. It can, however, be useful to block programs running on\nother computers (for example, over SSH) or as other users.\n\n.. note:: You don't need :opt:`allow_remote_control` to make this work as it is\n   limited to only programs running in that specific window. Be careful with\n   what programs you run in such windows, since they can effectively control\n   kitty, as if you were running with :opt:`allow_remote_control` turned on.\n\n    You can further restrict what is allowed in these windows by using\n    :option:`kitten @ launch --remote-control-password`.\n\n\nFine grained permissions for remote control\n----------------------------------------------\n\n.. versionadded:: 0.26.0\n\nThe :opt:`allow_remote_control` option discussed so far is a blunt\ninstrument, granting the ability to any program running on your computer\nor even on remote computers via SSH the ability to use remote control.\n\nYou can instead define remote control passwords that can be used to grant\ndifferent levels of control to different places. You can even write your\nown script to decide which remote control requests are allowed. This is\ndone using the :opt:`remote_control_password` option in :file:`kitty.conf`.\nSet :opt:`allow_remote_control` to :code:`password` to use this feature.\nLet's see some examples:\n\n.. code-block:: conf\n\n   remote_control_password \"control colors\" get-colors set-colors\n\nNow, using this password, you can, in scripts run the command::\n\n    kitten @ --password=\"control colors\" set-colors background=red\n\nAny script with access to the password can now change colors in kitty using\nremote control, but only that and nothing else. You can even supply the\npassword via the :envvar:`KITTY_RC_PASSWORD` environment variable, or the\nfile :file:`~/.config/kitty/rc-pass` to avoid having to type it repeatedly.\nSee :option:`kitten @ --password-file` and :option:`kitten @ --password-env`.\n\nThe :opt:`remote_control_password` can be specified multiple times to create\ndifferent passwords with different capabilities. Run the following to get a\nlist of all action names::\n\n    kitten @ --help\n\nYou can even use glob patterns to match action names, for example:\n\n.. code-block:: conf\n\n   remote_control_password \"control colors\" *-colors\n\nIf no action names are specified, all actions are allowed.\n\nIf ``kitten @`` is run with a password that is not present in\n:file:`kitty.conf`, then kitty will interactively prompt the user to allow or\ndisallow the remote control request. The user can choose to allow or disallow\neither just that request or all requests using that password. The user's\ndecision is remembered for the duration of that kitty instance.\n\n.. note::\n   For password based authentication to work over SSH, you must pass the\n   :envvar:`KITTY_PUBLIC_KEY` environment variable to the remote host. The\n   :doc:`ssh kitten <kittens/ssh>` does this for you automatically. When\n   using a password, :ref:`rc_crypto` is used to ensure the password\n   is kept secure. This does mean that using password based authentication\n   is slower as the entire command is encrypted before transmission. This\n   can be noticeable when using a command like ``kitten @ set-background-image``\n   which transmits large amounts of image data. Also, the clock on the remote\n   system must match (within a few minutes) the clock on the local system.\n   kitty uses a time based nonce to minimise the potential for replay attacks.\n\n.. _rc_custom_auth:\n\nCustomizing authorization with your own program\n____________________________________________________________\n\nIf the ability to control access by action names is not fine grained enough,\nyou can define your own Python script to examine every remote control command\nand allow/disallow it. To do so create a file in the kitty configuration\ndirectory, :file:`~/.config/kitty/my_rc_auth.py` and add the following\nto :file:`kitty.conf`:\n\n.. code-block:: conf\n\n    remote_control_password \"testing custom auth\" my_rc_auth.py\n\n:file:`my_rc_auth.py` should define a :code:`is_cmd_allowed` function\nas shown below:\n\n.. code-block:: py\n\n    def is_cmd_allowed(pcmd, window, from_socket, extra_data):\n        cmd_name = pcmd['cmd']  # the name of the command\n        cmd_payload = pcmd['payload']  # the arguments to the command\n        # examine the cmd_name and cmd_payload and return True to allow\n        # the command or False to disallow it. Return None to have no\n        # effect on the command.\n\n        # The command payload will vary from command to command, see\n        # the rc protocol docs for details. Below is an example of\n        # restricting the launch command to allow only running the\n        # default shell.\n\n        if cmd_name != 'launch':\n            return None\n        if cmd_payload.get('args') or cmd_payload.get('env') or cmd_payload.get('copy_cmdline') or cmd_payload.get('copy_env'):\n            return False\n        # prints in this function go to the parent kitty process STDOUT\n        print('Allowing launch command:', cmd_payload)\n        return True\n\n\n.. note::\n\n    The payloads for the different remote control commands are documented in the\n    :doc:`remote control protocol specification <rc_protocol>`.\n\n\n.. _rc_mapping:\n\nMapping key presses to remote control commands\n--------------------------------------------------\n\nIf you wish to trigger a remote control command easily with just a keypress,\nyou can map it in :file:`kitty.conf`. For example::\n\n    map f1 remote_control set-spacing margin=30\n\nThen pressing the :kbd:`F1` key will set the active window margins to\n:code:`30`. The syntax for what follows :ac:`remote_control` is exactly the same\nas the syntax for what follows :code:`kitten @` above.\n\nIf you wish to ignore errors from the command, prefix the command with an\n``!``. For example, the following will not return an error when no windows\nare matched::\n\n    map f1 remote_control !focus-window --match XXXXXX\n\nIf you wish to run a more complex script, you can use::\n\n    map f1 remote_control_script /path/to/myscript\n\nIn this script you can use ``kitten @`` to run as many remote\ncontrol commands as you like and process their output.\n:ac:`remote_control_script` is similar to the\n:ac:`launch` command with ``--type=background --allow-remote-control``.\nFor more advanced usage, including fine grained permissions, setting\nenv vars, command line interpolation, passing data to STDIN, etc.\nthe :doc:`launch <launch>` command should be used. Relative paths to scripts\nare interpreted with respect to the kitty config directory.\n\n.. note:: You do not need :opt:`allow_remote_control` to use these mappings,\n   as they are not actual remote programs, but are simply a way to reuse the\n   remote control infrastructure via keybings.\n\n\nBroadcasting what you type to all kitty windows\n--------------------------------------------------\n\nAs a simple illustration of the power of remote control, lets\nhave what we type sent to all open kitty windows. To do that define the\nfollowing mapping in :file:`kitty.conf`::\n\n    map f1 launch --allow-remote-control kitty +kitten broadcast\n\nNow press :kbd:`F1` and start typing, what you type will be sent to all windows,\nlive, as you type it.\n\n\nThe remote control protocol\n-----------------------------------------------\n\nIf you wish to develop your own client to talk to |kitty|, you can use the\n:doc:`remote control protocol specification <rc_protocol>`. Note that there\nis a statically compiled, standalone executable, ``kitten`` available that\ncan be used as a remote control client on any UNIX like computer. This can be\ndownloaded and used directly from the `kitty releases\n<https://github.com/kovidgoyal/kitty/releases>`__ page::\n\n    kitten @ --help\n\n\n.. _search_syntax:\n\nMatching windows and tabs\n----------------------------\n\nMany remote control operations operate on windows or tabs. To select these, the\n:code:`--match` option is often used. This allows matching using various\nsophisticated criteria such as title, ids, command lines, etc. These criteria are\nexpressions of the form :code:`field:query`. Where :italic:`field` is the field\nagainst which to match and :italic:`query` is the expression to match. They can\nbe further combined using Boolean operators, best illustrated with some\nexamples::\n\n    title:\"My special window\" or id:43\n    title:bash and env:USER=kovid\n    not id:1\n    (id:2 or id:3) and title:something\n\n.. include:: generated/matching.rst\n\n.. toctree::\n   :hidden:\n\n   rc_protocol\n\n.. include:: generated/cli-kitten-at.rst\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "# sphinx-inline-tabs breaks with sphinx >= 9\nsphinx <= 8.2.3\nfuro\nsphinx-copybutton\nsphinxext-opengraph\nsphinx-inline-tabs\nsphinx-autobuild\nmatplotlib\n"
  },
  {
    "path": "docs/sessions.rst",
    "content": ".. _sessions:\n\nSessions\n=============\n\nkitty has robust support for sessions. A session is basically a simple text\nfile where you can define kitty windows, tabs and what programs to run in them\nas well as how to layout the windows. kitty also supports actions to easily\n:ac:`create and switch between existing sessions <goto_session>`, so that you\ncan move seamlessly from working on one project to another with a couple of keystrokes.\n\nLet's see a quick example to get a feel of how easy it is to create sessions. First,\na session file to develop a project:\n\n.. code-block:: session\n\n    # Set the layout for the current tab\n    layout tall\n    # Set the working directory for windows in the current tab\n    cd ~/path/to/myproject\n    # Create the \"main\" window and run an editor in it to edit the project files\n    launch --title \"Edit My Project\" /usr/bin/nvim\n    # Create a side window to run a shell to build or test project\n    launch --title \"Build My Project\"\n    # Create another side window to keep an eye on some useful log file\n    launch --title \"Log for my project\" /usr/bin/tail -f /path/to/project/log/file\n\nSave this file as :file:`~/path/to/myproject/launch.kitty-session`. Now when\nyou want to work on the project, simply run:\n\n.. code-block:: sh\n\n    kitty --session ~/path/to/myproject/launch.kitty-session\n\nYou can also set the session in :file:`kitty.conf` via :opt:`startup_session`.\n\nThus, it is very easy to create sessions and work on projects. To learn how to\ncreate more complex sessions, see :ref:`complex_sessions`.\n\n\n.. _goto_session:\n\nCreating/Switching to sessions with a keypress\n------------------------------------------------\n\nIf you like to manage multiple sessions within a single terminal and\neasily swap between them, kitty has you covered. You can use the\n:ac:`goto_session` action in kitty.conf, like this:\n\n.. code-block:: conf\n\n   # Press F7 and then c to jump to the \"cool\" project\n   map f7>c goto_session ~/path/to/cool/cool.kitty-session\n   # Press F7 and then h to jump to the \"hot\" project\n   map f7>h goto_session ~/path/to/hot/hot.kitty-session\n   # Browse and select from the list of known projects defined via goto_session commands\n   map f7>/ goto_session\n   # Browse and select from the list of active projects defined via goto_session commands\n   map f7>/ goto_session --active-only [=no]\n   # Same as above, but the sessions are listed alphabetically instead of by most recent\n   map f7>/ goto_session --sort-by=alphabetical\n   # Browse session files inside a directory and pick one\n   map f7>p goto_session ~/.local/share/kitty/sessions\n   # Go to the previously active session (larger negative numbers jump further back in history)\n   map f7>- goto_session -1\n\nIn this manner you can define as many projects/sessions as you like and easily\nswitch between them with a keypress.\n\nWhen a directory path is supplied to :ac:`goto_session`, kitty scans it for\nfiles ending in ``.kitty-session``, ``.kitty_session`` or ``.session`` and\npresents an interactive list. The ``--sort-by`` option controls the ordering of that list just like it does\nfor globally known sessions.\n\nYou can also close sessions using the :ac:`close_session` action, which closes\nall windows in the session with a single keypress.\n\n\nDisplaying the currently active session name\n----------------------------------------------\n\nYou can display the name of the currently active session file in the kitty tab\nbar using :opt:`tab_title_template`. For example, using the value::\n\n    {session_name} {title}\n\nwill show you the name of the session file the current tab was loaded from, as\nwell as the normal tab title. Or alternatively, you can set the tab title\ndirectly to a project name in the session file itself when creating the tab,\nlike this::\n\n    new_tab My Project Name\n\n.. _complex_sessions:\n\nMore complex sessions\n-------------------------\n\nIf you want to create more complex sessions, with sophisticated layouts, such\nas :ref:`splits_layout`, the easiest way is to set up the state you want to\nsave manually by first starting kitty like this:\n\n.. code-block:: sh\n\n    kitty -o 'map f1 save_as_session --use-foreground-process --relocatable'\n\nNow create whatever splits and tabs you need and start whatever programs such\nas editors, REPLs, debuggers, etc. you want to start in each of them. Once\nkitty is the way you want it, press the :kbd:`F1` key, and you will be prompted\nfor a path at which to save the session file. Specify the path and the session\nwill be saved there with the exact setup you created. The saved file will even\nbe opened in your editor for you to review, automatically.\n\n.. tip::\n   If you want session files to be saved to a specific directory regardless of\n   your current working directory, use the ``--base-dir`` option. For example::\n\n       map f7>s save_as_session --use-foreground-process --base-dir ~/.local/share/kitty/sessions\n\n   This is particularly useful when kitty is launched from system-wide shortcuts\n   where the working directory might not be your home directory. Note that\n   ``--relocatable`` is typically not used with ``--base-dir``, since relocatable\n   is meant for session files that are co-located with their project directories.\n\nIf instead, you want to create these by hand, see the example below which shows\nall the major keywords you can use in kitty session files:\n\n.. code-block:: session\n\n    # Set the layout for the current tab\n    layout tall\n    # Set the working directory for windows in the current tab. Relative paths\n    # are resolved with respect to the location of this session file.\n    cd ~\n    # Create a window and run the specified command in it\n    launch zsh\n    # Create a window with some environment variables set and run vim in it\n    launch --env FOO=BAR vim\n    # Set the title for the next window\n    launch --title \"Chat with x\" irssi --profile x\n    # Run a short lived command and see its output\n    launch --hold message-of-the-day\n\n    # Create a new tab\n    # The part after new_tab is the optional tab title which will be displayed in\n    # the tab bar, if omitted, the title of the active window will be used instead.\n    new_tab my tab\n    cd somewhere\n    # Set the layouts allowed in this tab\n    enabled_layouts tall,stack\n    # Set the current layout\n    layout stack\n    launch zsh\n\n    # Create a new OS window\n    # Any definitions specified before the first new_os_window will apply to first OS window.\n    new_os_window\n    # Set new window size to 80x24 cells\n    os_window_size 80c 24c\n    # Set the --title for the new OS window\n    os_window_title my fancy os window\n    # Set the --class for the new OS window\n    os_window_class mywindow\n    # Set the --name for the new OS window\n    os_window_name myname\n    # Change the OS window state to normal, fullscreen, maximized or minimized\n    os_window_state normal\n    launch sh\n    # Resize the current window (see the resize_window action for details)\n    resize_window wider 2\n    # Make the current window the active (focused) window in its tab\n    focus\n    # Make the current OS Window the globally active window\n    focus_os_window\n    launch emacs\n\n    # Create another tab\n    new_tab logs\n    launch tail -f /var/log/syslog\n\n    # Focus the first tab (index 0) when the session loads\n    # You can also use a match expression like: focus_tab title:logs\n    focus_tab 0\n\n    # Create a complex layout using multiple splits. Creates two columns of\n    # windows with two windows in each column. The windows in the first column are\n    # split 50:50. In the second column the windows are not evenly split.\n    new_tab complex tab\n    layout splits\n    # First window, set a user variable on it so we can focus it later\n    launch --var window=first\n    # Create the second column by splitting the first window vertically\n    launch --location=vsplit\n    # Create the third window in the second column by splitting the second window horizontally\n    # Make it take 40% of the height instead of 50%\n    launch --location=hsplit --bias=40\n    # Go back to focusing the first window, so that we can split it\n    focus_matching_window var:window=first\n    # Create the final window in the first column\n    launch --location=hsplit\n\n\n.. note::\n    The :doc:`launch <launch>` command when used in a session file cannot create\n    new OS windows, or tabs.\n\n.. note::\n    Environment variables of the form :code:`${NAME}` or :code:`$NAME` are\n    expanded in the session file, except in the *arguments* (not options) to the\n    launch command. For example:\n\n    .. code-block:: sh\n\n        launch --cwd=$THIS_IS_EXPANDED some-program $THIS_IS_NOT_EXPANDED\n\n\nMaking newly created windows join an existing session\n---------------------------------------------------------\n\nNormally, after activating a session, if you create new windows/tabs\nthey don't belong to the session. If you would prefer to have them belong\nto the currently active session, you can use the :ac:`new_window_with_cwd`\nand :ac:`new_tab_with_cwd` actions instead, like this::\n\n    map kitty_mod+enter new_window_with_cwd\n    map kitty_mod+t new_tab_with_cwd\n    map kitty_mod+n new_os_window_with_cwd\n\nThis will cause newly created windows and tabs to belong to the currently active\nsession, if any. Note that adding a window to a session in this way is\ntemporary, it does not edit the session file. If you wish to update the\nsession file of the currently active session, you can use the following\nmapping for it::\n\n    map f5 save_as_session --relocatable --use-foreground-process --match=session:. .\n\nThe two can be combined, using the :ac:`combine` action.\nFor even more control of what session a window is added to use\nthe :doc:`launch <launch>` command with the :option:`launch --add-to-session`\nflag.\n\n\nSessions with remote connections\n-------------------------------------\n\nIf you use the :doc:`ssh kitten </kittens/ssh>` to connect to remote computers,\n:ac:`save_as_session` is smart enough to save the ssh kitten invocation to your\nsession file, preserving the remote working directory and even the currently\nrunning program on the remote host! Try it, run kitty with::\n\n    kitty -o 'map f1 save_as_session --use-foreground-process --relocatable' --session <(echo \"layout vertical\\nlaunch\\nlaunch\")\n\nNow in both windows, run::\n\n    kitten ssh localhost\n\nTo connect them both to a remote computer (replace ``localhost`` with another\ncomputer if you like). In one window change the directory to /tmp and in the\nother start some program. Then press :kbd:`F1` to save the session file.\nWhen you run the session file in another kitty instance you will see both\nwindows re-created, as expected with the correct working directories and\nrunning programs.\n\nManaging multi tab sessions in a single OS Window\n----------------------------------------------------\n\nThe natural way to organise sessions in kitty is one per :term:`os_window`.\nHowever, if you prefer to manage multiple sessions in a single OS Window, you\ncan configure the kitty tab bar to only show tabs that belong to the currently\nactive session. To do so, use :opt:`tab_bar_filter` in :file:`kitty.conf` set::\n\n    tab_bar_filter session:~ or session:^$\n\nThis will restrict the tab bar to only showing tabs from the currently active\nsession as well tabs that do not belong to any session. Furthermore, when you\nare in a window or tab that does not belong to any session, the tab bar will\nshow the tabs from the most recent active session, to maintain context.\n\nKeyword reference\n---------------------\n\nBelow is the list of all supported keywords in session files along with\ndocumentation for them.\n\n``cd [path]``\n    Change the working directory for all windows in the current tab to\n    ``path``. Relative paths are resolved with respect to the directory\n    containing the session file.\n\n``focus``\n    Give keyboard focus to the window created by the previous launch command\n\n``focus_matching_window``\n    Give keyboard focus to window that matches the specified expression. See\n    :ref:`search_syntax` for the syntax for matching expressions.\n\n``focus_os_window``\n    Give keyboard focus to the current OS Window. This is guaranteed to work\n    only is some other OS Window in the current kitty process has focus,\n    otherwise the window manager might block changing focus to prevent *focus\n    stealing*.\n\n``focus_tab [tab specifier]``\n    Set which tab should be active (focused) in the current OS Window. The tab\n    specifier can be either a plain number (treated as a 0-based index) or a\n    match expression. For example, ``focus_tab 0`` will focus the first tab,\n    ``focus_tab 1`` the second tab, and ``focus_tab title:logs`` will focus the\n    tab whose title matches \"logs\". See :ref:`search_syntax` for the full syntax\n    of match expressions. This is useful for session files that create multiple\n    tabs and want to ensure a specific tab is active when the session is loaded.\n\n``enabled_layouts comma separated list of layout names``\n    Set the layouts allowed in the current tab. Same syntax as\n    :opt:`enabled_layouts`.\n\n``launch``\n    Create a new window running the specified command or the default shell if\n    no command is specified. See :doc:`launch` for details. Note that creating\n    tabs and OS Windows using launch is not supported in session files, use the\n    dedicated keywords for these.\n\n``layout name``\n    Set the layout for the current tab to the specified layout, including any\n    specified options, see :doc:`layouts` for the available layouts and\n    options.\n\n``new_os_window``\n    Create a new OS Window. Any OS window related keywords specified before the\n    first ``new_os_window`` will apply to the first OS Window.\n\n``new_tab [tab title]``\n    Create a new tab with the specified title. If no title is specified, the\n    title behaves just as for a regular tab in kitty.\n\n``os_window_title``\n    Set the title for the current OS Window. The OS Window will then always\n    have this title, it will not change based on the title of the currently active\n    window inside the OS Window.\n\n``os_window_class``\n    Set the class part of WM_CLASS or Wayland Application Id for the current OS Window\n\n``os_window_name``\n    Set the name part of WM_CLASS or Wayland Window tag for the current OS Window\n\n``os_window_size``\n    Set the size of the current OS Window, can be specified in pixels or cells.\n    For example: 80c 24c is a window of width 80 cells by 24 cells.\n\n``os_window_state``\n    Set the state of the current OS Window, can be: ``normal``, ``fullscreen``, ``maximized`` or ``minimized``\n\n``resize_window``\n    Resize the current window. See the :ac:`resize_window` action for details.\n    For example: resize_window wider 2\n\n``set_layout_state``\n    This keyword is only used in session files generated by the\n    :ac:`save_as_session` action, it's syntax is undocumented and for internal\n    use only.\n\n``title``\n    Set the title for the next window. Deprecated, use ``launch --title``\n    instead.\n\n\n.. _save_as_session:\n\nThe save_as_session action\n------------------------------\n\nThis action can be mapped to a key press in :file:`kitty.conf`. It will save\nthe currently open OS Windows, tabs, windows, running programs, working\ndirectories, etc. into a session file. It is a convenient way to\n:ref:`complex_sessions`. The options this action takes are documented below.\n\n.. include:: generated/save-as-session.rst\n"
  },
  {
    "path": "docs/shell-integration.rst",
    "content": ".. _shell_integration:\n\nShell integration\n-------------------\n\nkitty has the ability to integrate closely within common shells, such as `zsh\n<https://www.zsh.org/>`__, `fish <https://fishshell.com>`__ and `bash\n<https://www.gnu.org/software/bash/>`__ to enable features such as jumping to\nprevious prompts in the scrollback, viewing the output of the last command in\n:program:`less`, using the mouse to move the cursor while editing prompts, etc.\n\n.. versionadded:: 0.24.0\n\nFeatures\n-------------\n\n* Open the output of the last command in a pager such as :program:`less`\n  (:sc:`show_last_command_output`)\n\n* Jump to the previous/next prompt in the scrollback\n  (:sc:`scroll_to_previous_prompt` /  :sc:`scroll_to_next_prompt`)\n\n* Click with the mouse anywhere in the current command to move the cursor there\n\n* Hold :kbd:`Ctrl+Shift` and right-click on any command output in the scrollback\n  to view it in a pager\n\n* The current working directory or the command being executed are automatically\n  displayed in the kitty window titlebar/tab title\n\n* The text cursor is changed to a bar when editing commands at the shell prompt\n\n* :ref:`clone_shell` with all environment variables and the working directory\n  copied\n\n* :ref:`Edit files in new kitty windows <edit_file>` even over SSH\n\n* Glitch free window resizing even with complex prompts. Achieved by erasing\n  the prompt on resize and allowing the shell to redraw it cleanly.\n\n* Sophisticated completion for the :program:`kitty` command in the shell.\n\n* When confirming a quit command if a window is sitting at a shell prompt,\n  it is not counted (for details, see :opt:`confirm_os_window_close`)\n\n\nConfiguration\n---------------\n\nShell integration is controlled by the :opt:`shell_integration` option. By\ndefault, all integration features are enabled. Individual features can be turned\noff or it can be disabled entirely as well. The :opt:`shell_integration` option\ntakes a space separated list of keywords:\n\ndisabled\n    Turn off all shell integration. The shell's launch environment is not\n    modified and :envvar:`KITTY_SHELL_INTEGRATION` is not set. Useful for\n    :ref:`manual integration <manual_shell_integration>`.\n\nno-rc\n    Do not modify the shell's launch environment to enable integration. Useful\n    if you prefer to load the kitty shell integration code yourself, either as\n    part of :ref:`manual integration <manual_shell_integration>` or because\n    you have some other software that sets up shell integration.\n    This will still set the :envvar:`KITTY_SHELL_INTEGRATION` environment\n    variable when kitty runs the shell.\n\nno-cursor\n    Turn off changing of the text cursor to a bar when editing shell command\n    line.\n\nno-title\n    Turn off setting the kitty window/tab title based on shell state.\n    Note that for the fish shell kitty relies on fish's native title setting\n    functionality instead.\n\nno-cwd\n    Turn off reporting the current working directory. This is used to allow\n    :ac:`new_window_with_cwd` and similar to open windows logged into remote\n    machines using the :doc:`ssh kitten <kittens/ssh>` automatically with the\n    same working directory as the current window.\n    Note that for the fish shell this will not disable its built-in current\n    working directory reporting.\n\nno-prompt-mark\n    Turn off marking of prompts. This disables jumping to prompt, browsing\n    output of last command and click to move cursor functionality.\n    Note that for the fish shell this does not take effect, since fish always\n    marks prompts.\n\nno-complete\n    Turn off completion for the kitty command.\n    Note that for the fish shell this does not take effect, since fish already\n    comes with a kitty completion script.\n\nno-sudo\n    Do not alias :program:`sudo` to ensure the kitty terminfo files are\n    available in the sudo environment. This is needed if you have sudo\n    configured to disable setting of environment variables on the command line.\n    By default, if sudo is configured to allow all commands for the current\n    user, setting of environment variables at the command line is also allowed.\n    Only if commands are restricted is this needed.\n\n\nMore ways to browse command output\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou can add further key and mouse bindings to browse the output of commands\neasily. For example to select the output of a command by right clicking the\nmouse on the output, define the following in :file:`kitty.conf`:\n\n.. code:: conf\n\n    mouse_map right press ungrabbed mouse_select_command_output\n\nNow, when you right click on the output, the entire output is selected, ready\nto be copied.\n\nThe feature to jump to previous prompts (\n:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) and mouse\nactions (:ac:`mouse_select_command_output` and :ac:`mouse_show_command_output`)\ncan be integrated with browsing command output as well. For example, define the\nfollowing mapping in :file:`kitty.conf`:\n\n.. code:: conf\n\n    map f1 show_last_visited_command_output\n\nNow, pressing :kbd:`F1` will cause the output of the last jumped to command or\nthe last mouse clicked command output to be opened in a pager for easy browsing.\n\nIn addition, You can define shortcut to get the first command output on screen.\nFor example, define the following in :file:`kitty.conf`:\n\n.. code:: conf\n\n    map f1 show_first_command_output_on_screen\n\nNow, pressing :kbd:`F1` will cause the output of the first command output on\nscreen to be opened in a pager.\n\nYou can also add shortcut to scroll to the last jumped position. For example,\ndefine the following in :file:`kitty.conf`:\n\n.. code:: conf\n\n    map f1 scroll_to_prompt 0\n\n\nHow it works\n-----------------\n\nAt startup, kitty detects if the shell you have configured (either system wide\nor the :opt:`shell` option in :file:`kitty.conf`) is a supported shell. If so,\nkitty injects some shell specific code into the shell, to enable shell\nintegration. How it does so varies for different shells.\n\n\n.. tab:: zsh\n\n   For zsh, kitty sets the :envvar:`ZDOTDIR` environment variable to make zsh\n   load kitty's :file:`.zshenv` which restores the original value of\n   :envvar:`ZDOTDIR` and sources the original :file:`.zshenv`. It then loads\n   the shell integration code. The remainder of zsh's startup process proceeds\n   as normal.\n\n.. tab:: fish\n\n    For fish, to make it automatically load the integration code provided by\n    kitty, the integration script directory path is prepended to the\n    :envvar:`XDG_DATA_DIRS` environment variable. This is only applied to the\n    fish process and will be cleaned up by the integration script after startup.\n    No files are added or modified.\n\n.. tab:: bash\n\n    For bash, kitty starts bash in POSIX mode, using the environment variable\n    :envvar:`ENV` to load the shell integration script. This prevents bash from\n    loading any startup files itself. The loading of the startup files is done\n    by the integration script, after disabling POSIX mode. From the perspective\n    of those scripts there should be no difference to running vanilla bash.\n\n\nThen, when launching the shell, kitty sets the environment variable\n:envvar:`KITTY_SHELL_INTEGRATION` to the value of the :opt:`shell_integration`\noption. The shell integration code reads the environment variable, turns on the\nspecified integration functionality and then unsets the variable so as to not\npollute the system.\n\nThe actual shell integration code uses hooks provided by each shell to send\nspecial escape codes to kitty, to perform the various tasks. You can see the\ncode used for each shell below:\n\n.. raw:: html\n\n    <details>\n    <summary>Click to toggle shell integration code</summary>\n\n.. tab:: zsh\n\n    .. literalinclude:: ../shell-integration/zsh/kitty-integration\n        :language: zsh\n\n\n.. tab:: fish\n\n    .. literalinclude:: ../shell-integration/fish/vendor_conf.d/kitty-shell-integration.fish\n        :language: fish\n        :force:\n\n.. tab:: bash\n\n    .. literalinclude:: ../shell-integration/bash/kitty.bash\n        :language: bash\n\n.. raw:: html\n\n   </details>\n\n\nShell integration over SSH\n----------------------------\n\nThe easiest way to have shell integration work when SSHing into remote systems\nis to use the :doc:`ssh kitten <kittens/ssh>`. Simply run::\n\n    kitten ssh hostname\n\nAnd, by magic, you will be logged into the remote system with fully functional\nshell integration. Alternately, you can :ref:`setup shell integration manually\n<manual_shell_integration>`, by copying the kitty shell integration scripts to\nthe remote server and editing the shell rc files there, as described below.\n\n\nShell integration in a container\n----------------------------------\n\nInstall the kitten `standalone binary\n<https://github.com/kovidgoyal/kitty/releases/latest/download/kitten-linux-amd64>`__ in the container\nsomewhere in the PATH, then you can log into the container with:\n\n.. code-block:: sh\n\n   docker exec -ti container-id kitten run-shell --shell=/path/to/your/shell/in/the/container\n\nThe kitten will even take care of making the kitty terminfo database available\nin the container automatically.\n\n.. _clone_shell:\n\nClone the current shell into a new window\n-----------------------------------------------\n\nYou can clone the current shell into a new kitty window by simply running the\n:command:`clone-in-kitty` command, for example:\n\n.. code-block:: sh\n\n    clone-in-kitty\n    clone-in-kitty --type=tab\n    clone-in-kitty --title \"I am a clone\"\n\nThis will open a new window running a new shell instance but with all\nenvironment variables and the current working directory copied. This even works\nover SSH when using :doc:`kittens/ssh`.\n\nThe :command:`clone-in-kitty` command takes almost all the same arguments as the\n:doc:`launch <launch>` command, so you can open a new tab instead or a new OS\nwindow, etc. Arguments of launch that can cause code execution or that don't\nmake sense when cloning are ignored. Most prominently, the following options are\nignored: :option:`--allow-remote-control <launch --allow-remote-control>`,\n:option:`--copy-cmdline <launch --copy-cmdline>`, :option:`--copy-env <launch\n--copy-env>`, :option:`--stdin-source <launch --stdin-source>`,\n:option:`--marker <launch --marker>` and :option:`--watcher <launch --watcher>`.\n\n:command:`clone-in-kitty` can be configured to source arbitrary code in the\ncloned window using environment variables. It will automatically clone virtual\nenvironments created by the :link:`Python venv module\n<https://docs.python.org/3/library/venv.html>` or :link:`Conda\n<https://conda.io/>`. In addition, setting the\nenv var :envvar:`KITTY_CLONE_SOURCE_CODE` to some shell code will cause that\ncode to be run in the cloned window with :code:`eval`. Similarly, setting\n:envvar:`KITTY_CLONE_SOURCE_PATH` to the path of a file will cause that file to\nbe sourced in the cloned window. This can be controlled by\n:opt:`clone_source_strategies`.\n\n:command:`clone-in-kitty` works by asking the shell to serialize its internal\nstate (mainly CWD and env vars) and this state is transmitted to kitty and\nrestored by the shell integration scripts in the cloned window.\n\n\n.. _edit_file:\n\nEdit files in new kitty windows even over SSH\n------------------------------------------------\n\n.. code-block:: sh\n\n   edit-in-kitty myfile.txt\n   edit-in-kitty --type tab --title \"Editing My File\" myfile.txt\n   # open myfile.txt at line 75 (works with vim, neovim, emacs, nano, micro)\n   edit-in-kitty +75 myfile.txt\n\nThe :command:`edit-in-kitty` command allows you to seamlessly edit files\nin your default :opt:`editor` in new kitty windows. This works even over\nSSH (if you use the :doc:`ssh kitten <kittens/ssh>`), allowing you\nto easily edit remote files in your local editor with all its bells and\nwhistles.\n\nThe :command:`edit-in-kitty` command takes almost all the same arguments as the\n:doc:`launch <launch>` command, so you can open a new tab instead or a new OS\nwindow, etc. Not all arguments are supported, see the discussion in the\n:ref:`clone_shell` section above.\n\nIn order to avoid remote code execution, kitty will only execute the configured\neditor and pass the file path to edit to it.\n\n.. note:: To edit files using sudo the best method is to set the\n   :code:`SUDO_EDITOR` environment variable to ``kitten edit-in-kitty`` and\n   then edit the file using the ``sudoedit`` or ``sudo -e`` commands.\n\n\n.. _run_shell:\n\nUsing shell integration in sub-shells, containers, etc.\n-----------------------------------------------------------\n\n.. versionadded:: 0.29.0\n\nTo start a sub-shell with shell integration automatically setup, simply run::\n\n    kitten run-shell\n\nThis will start a sub-shell using the same binary as the currently running\nshell, with shell-integration enabled. To start a particular shell use::\n\n    kitten run-shell --shell=/bin/bash\n\nTo run a command before starting the shell use::\n\n    kitten run-shell ls .\n\nThis will run ``ls .`` before starting the shell.\n\nThis will even work on remote systems where kitty itself is not installed,\nprovided you use the :doc:`SSH kitten <kittens/ssh>` to connect to the system.\nUse ``kitten run-shell --help`` to learn more.\n\n.. _manual_shell_integration:\n\nManual shell integration\n----------------------------\n\nThe automatic shell integration is designed to be minimally intrusive, as such\nit won't work for sub-shells, terminal multiplexers, containers, etc.\nFor such systems, you should either use the :ref:`run-shell <run_shell>` command described above or\nsetup manual shell integration by adding some code to your shells startup files to load the shell integration script.\n\nFirst, in :file:`kitty.conf` set:\n\n.. code-block:: conf\n\n    shell_integration disabled\n\nThen in your shell's rc file, add the lines:\n\n.. tab:: zsh\n\n    .. code-block:: sh\n\n        if test -n \"$KITTY_INSTALLATION_DIR\"; then\n            export KITTY_SHELL_INTEGRATION=\"enabled\"\n            autoload -Uz -- \"$KITTY_INSTALLATION_DIR\"/shell-integration/zsh/kitty-integration\n            kitty-integration\n            unfunction kitty-integration\n        fi\n\n.. tab:: fish\n\n    .. code-block:: fish\n\n        if set -q KITTY_INSTALLATION_DIR\n            set --global KITTY_SHELL_INTEGRATION enabled\n            source \"$KITTY_INSTALLATION_DIR/shell-integration/fish/vendor_conf.d/kitty-shell-integration.fish\"\n            set --prepend fish_complete_path \"$KITTY_INSTALLATION_DIR/shell-integration/fish/vendor_completions.d\"\n        end\n\n\n.. tab:: bash\n\n    .. code-block:: sh\n\n        if test -n \"$KITTY_INSTALLATION_DIR\"; then\n            export KITTY_SHELL_INTEGRATION=\"enabled\"\n            source \"$KITTY_INSTALLATION_DIR/shell-integration/bash/kitty.bash\"\n        fi\n\nThe value of :envvar:`KITTY_SHELL_INTEGRATION` is the same as that for\n:opt:`shell_integration`, except if you want to disable shell integration\ncompletely, in which case simply do not set the\n:envvar:`KITTY_SHELL_INTEGRATION` variable at all.\n\nIn a container, you will need to install the kitty shell integration scripts\nand make sure the :envvar:`KITTY_INSTALLATION_DIR` environment variable is set\nto point to the location of the scripts.\n\nIntegration with other shells\n-------------------------------\n\nThere exist third-party integrations to use these features for various other\nshells:\n\n* Jupyter console and IPython via a patch (:iss:`4475`)\n* `xonsh <https://github.com/xonsh/xonsh/issues/4623>`__\n* `Nushell <https://github.com/nushell/nushell/discussions/12065>`__: Set ``$env.config.shell_integration = true`` in your ``config.nu`` to enable it.\n\nNotes for shell developers\n-----------------------------\n\nThe protocol used for marking the prompt is very simple. You should consider\nadding it to your shell as a builtin. Many modern terminals make use of it, for\nexample: kitty, iTerm2, WezTerm, DomTerm\n\nJust before starting to draw the PS1 prompt send the escape code:\n\n.. code-block:: none\n\n    <OSC>133;A<ST>\n\nJust before starting to draw the PS2 prompt send the escape code:\n\n.. code-block:: none\n\n    <OSC>133;A;k=s<ST>\n\nJust before running a command/program, send the escape code:\n\n.. code-block:: none\n\n    <OSC>133;C<ST>\n\nOptionally, when a command is finished its \"exit status\" can be reported as:\n\n.. code-block:: none\n\n    <OSC>133;D;exit status as base 10 integer<ST>\n\nHere ``<OSC>`` is the bytes ``0x1b 0x5d`` and ``<ST>`` is the bytes ``0x1b\n0x5c``. This is exactly what is needed for shell integration in kitty. For the\nfull protocol, that also marks the command region, see `the iTerm2 docs\n<https://iterm2.com/documentation-escape-codes.html>`_.\n\nkitty additionally supports several extra fields for the ``<OSC>133;A`` command\nto control its behavior, separated by semi-colons. They are:\n\n\n``redraw=0``\n    this tells kitty that the shell will not redraw the prompt on\n    resize so it should not erase it\n\n``special_key=1``\n    this tells kitty to use a special key instead of arrow keys\n    to move the cursor on mouse click. Useful if arrow keys have side-effects\n    like triggering auto complete. The shell integration script then binds the\n    special key, as needed.\n\n``k=s``\n    this tells kitty that the secondary (PS2) prompt is starting at the\n    current line.\n\n``click_events=1|2``\n    this tells kitty that the shell is capable of handling\n    mouse click events. kitty will thus send a click event to the shell when\n    the user clicks somewhere in the prompt. The shell can then move the cursor\n    to that position or perform some other appropriate action. Without this,\n    kitty will instead generate a number of fake key events to move the cursor\n    to the clicked location, which is not fully robust. A value of ``1`` will\n    cause the click events to have absolute y co-ordinates, a value of ``2``\n    will cause them to have y-coordinates relative to the top line of the\n    current prompt. In relative mode, y is zero for cells on the top line of\n    the current prompt. The current prompt here is either the secondary (PS2) or\n    primary prompt. If the secondary prompt is on the same line or above the\n    mouse position, then the reported y will be with respect to that, otherwise\n    with respect to the primary prompt. The click event is encoded in the SGR\n    encoding from xterm.\n\nkitty also optionally supports sending the cmdline going to be executed with ``<OSC>133;C`` as:\n\n.. code-block:: none\n\n    <OSC>133;C;cmdline=cmdline encoded by %q<ST>\n    or\n    <OSC>133;C;cmdline_url=cmdline as UTF-8 URL %-escaped text<ST>\n\n\nHere, *encoded by %q* means the encoding produced by the %q format to printf in\nbash and similar shells. Which is basically shell escaping with the addition of\nusing `ANSI C quoting\n<https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting>`__\nfor control characters (``$''`` quoting).\n"
  },
  {
    "path": "docs/support.html",
    "content": "<style>\n#support-buttons {\n    display: flex;\n    flex-wrap: wrap;\n}\n\n.support-button {\n    box-sizing: border-box;\n    border-radius: 6px;\n    display: inline-block;\n    cursor: pointer;\n    color: rgb(51, 51, 51);\n    font-size: inherit;\n    font-family: Arial;\n    font-weight: bold;\n    padding: 8px 24px;\n    text-decoration: none;\n    margin-left: 1em;\n    outline: 0;\n    border-width: 0;\n}\n\n.support-button:hover {\n    transform: scale(1.2);\n    border: solid 1px rgb(92, 184, 92);\n}\n\n.support-button:visited {\n    color: rgb(51, 51, 51);\n    outline: 0;\n}\n\n#support-buttons > div {\n    margin-top: 3ex;\n    text-align: center;\n}\n\n#paypal input[type=submit] {\n    background: linear-gradient(rgb(92, 184, 92) 5%, rgb(62, 142, 62) 100%) rgb(92, 184, 92);\n    padding-top: 10px; padding-bottom: 10px;\n    color: rgb(51, 51, 51);\n    border: 1px solid rgb(92, 184, 92);\n}\n\na.support-button {\n    background: linear-gradient(rgb(92, 184, 92) 5%, rgb(62, 142, 62) 100%) rgb(92, 184, 92);\n    color: rgb(51, 51, 51);\n}\n\n</style>\n\n<div id=\"support-buttons\">\n\n<div id=\"github\">\n    <a class=\"support-button\" href=\"https://github.com/sponsors/kovidgoyal\">Patronage via GitHub</a>\n</div>\n\n\n<div id=\"patreon\">\n    <a class=\"support-button\" href=\"https://www.patreon.com/bePatron?u=917933\">Patronage via Patreon</a>\n</div>\n\n<div id=\"liberapay\">\n    <a class=\"support-button\" href=\"https://liberapay.com/kovidgoyal/donate\">Patronage via Liberapay</a>\n</div>\n\n<div>\n    <form id=\"paypal\" action=\"https://www.paypal.com/cgi-bin/webscr\" method=\"post\" title=\"Contribute to support calibre development\">\n        <input type=\"hidden\" name=\"cmd\" value=\"_s-xclick\" />\n        <input type=\"hidden\" name=\"hosted_button_id\" value=\"FEELASHWEL5HS\" />\n        <input class=\"support-button\" type=\"submit\" name=\"submit\" value=\"One time support via PayPal\" />\n        <img alt=\"\" border=\"0\" src=\"https://www.paypalobjects.com/en_GB/i/scr/pixel.gif\" width=\"1\" height=\"1\" />\n    </form>\n</div>\n\n</div>\n\n"
  },
  {
    "path": "docs/support.rst",
    "content": "Support kitty development ❤️\n==============================\n\nMy goal with |kitty| is to move the stagnant terminal ecosystem forward.  To that\nend kitty has many foundational features, such as: :doc:`image support\n<graphics-protocol>`, :doc:`superlative performance <performance>`,\n:doc:`various enhancements to the terminal protocol <protocol-extensions>`,\netc. These features allow the development of rich terminal applications, such\nas :doc:`Side-by-side diff <kittens/diff>` and :doc:`Unicode input\n<kittens/unicode_input>`.\n\nIf you wish to support this mission and see the terminal ecosystem evolve,\nconsider donating so that I can devote more time to |kitty| development.\nI have personally written `almost all kitty code\n<https://github.com/kovidgoyal/kitty/graphs/contributors>`_.\n\nYou can choose to make either a one-time payment via PayPal or become a\n*patron* of kitty development via one of the services below:\n\n\n.. raw:: html\n    :file: support.html\n"
  },
  {
    "path": "docs/text-sizing-protocol.rst",
    "content": "The text sizing protocol\n==============================================\n\n.. versionadded:: 0.40.0\n\nClassically, because the terminal is a grid of equally sized characters, only\na single text size was supported in terminals, with one minor exception, some\ncharacters were allowed to be rendered in two cells, to accommodate East Asian\nsquare aspect ratio characters and Emoji. Here, by single text size we mean the\nfont size of all text on the screen is the same.\n\nThis protocol allows text to be displayed in the terminal in different sizes\nboth larger and smaller than the base text. It also solves the long standing\nproblem of robustly determining the width (in cells) a character should have.\nApplications can interleave text of different sizes on the screen allowing for\ntypographic niceties like headlines, superscripts, etc.\n\nNote that this protocol is fully backwards compatible, terminals that implement\nit will continue to work just the same with applications that do not use it.\nBecause of this, it is not fully flexible in the font sizes it allows, as it\nstill has to work with the character cell grid based fundamental nature of the\nterminal. Public discussion of this protocol is :iss:`here <8226>`.\n\nQuickstart\n--------------\n\nUsing this protocol to display different sized text is very simple, let's\nillustrate with a few examples to give us a flavor:\n\n.. code-block:: sh\n\n   printf \"\\e]_text_size_code;s=2;Double sized text\\a\\n\\n\"\n   printf \"\\e]_text_size_code;s=3;Triple sized text\\a\\n\\n\\n\"\n   printf \"\\e]_text_size_code;n=1:d=2;Half sized text\\a\\n\"\n\nNote that the last example, of half sized text, has half height characters, but\nthey still each take one cell, this can be fixed with a little more work:\n\n.. code-block:: sh\n\n   printf \"\\e]_text_size_code;n=1:d=2:w=1;Ha\\a\\e]66;n=1:d=2:w=1;lf\\a\\n\"\n\nThe ``w=1`` mechanism allows the program to tell the terminal what width the text\nshould take. This not only fixes using smaller text but also solves the long\nstanding terminal ecosystem bugs caused by the client program not knowing how\nmany cells the terminal will render some text in.\n\n\nThe escape code\n-----------------\n\nThere is a single escape code used by this protocol. It is sent by client\nprograms to the terminal emulator to tell it to render the specified text\nat the specified size. It is an ``OSC`` code of the form::\n\n    <OSC> _text_size_code ; metadata ; text <terminator>\n\nHere, ``OSC`` is the bytes ``ESC ] (0x1b 0x5b)``. The ``metadata`` is a colon\nseparated list of ``key=value`` pairs. The final part of the escape code is the\ntext which is simply plain text encoded as :ref:`safe_utf8`, the text must be\nno longer than ``4096`` bytes. Longer strings than that must be broken up into\nmultiple escape codes. Spaces in this definition are for clarity only and\nshould be ignored. The ``terminator`` is either the byte ``BEL (0x7)`` or the\nbytes ``ESC ST (0x1b 0x5c)``.\n\nThere are only a handful of metadata keys, defined in the table below:\n\n\n.. csv-table:: The text sizing metadata keys\n   :header: \"Key\", \"Value\", \"Default\", \"Description\"\n\n    \"s\", \"Integer from 1 to 7\",  \"1\", \"The overall scale, the text will be rendered in a block of ``s * w`` by ``s`` cells\"\n\n    \"w\", \"Integer from 0 to 7\",  \"0\", \"The width, in cells, in which the text should be rendered. When zero, the terminal should calculate the width as it would for normal text, splitting it up into scaled cells.\"\n\n    \"n\", \"Integer from 0 to 15\", \"0\", \"The numerator for the fractional scale.\"\n\n    \"d\", \"Integer from 0 to 15\", \"0\", \"The denominator for the fractional scale. Must be ``> n`` when non-zero.\"\n\n    \"v\", \"Integer from 0 to 2\",  \"0\", \"The vertical alignment to use for fractionally scaled text (n < d). ``0`` - top, ``1`` - bottom, ``2`` - centered\"\n\n    \"h\", \"Integer from 0 to 2\",  \"0\", \"The horizontal alignment to use for fractionally scaled text (n < d). ``0`` - left, ``1`` - right, ``2`` - centered\"\n\n\nHow it works\n------------------\n\nThis protocol works by allowing the client program to tell the terminal to\nrender text in multiple cells. The terminal can then adjust the actual font\nsize used to render the specified text as appropriate for the specified space.\n\nThe space to render is controlled by four metadata keys, ``s (scale)``, ``w (width)``, ``n (numerator)``\nand ``d (denominator)``. The most important are the ``s`` and ``w`` keys. The text\nwill be rendered in a block of ``s * w`` by ``s`` cells. A special case is ``w=0``\n(the default), which means the terminal splits up the text into cells as it\nwould normally without this protocol, but now each cell is an ``s by s`` block of\ncells instead. So, for example, if the text is ``abc`` and ``s=2`` the terminal would normally\nsplit it into three cells::\n\n    │a│b│c│\n\nBut, because ``s=2`` it instead gets split as::\n\n    │a░│b░│c░│\n    │░░│░░│░░│\n\nThe terminal multiplies the font size by ``s`` when rendering these\ncharacters and thus ends up rendering text at twice the base size.\n\nWhen ``w`` is a non-zero value, it specifies the width in scaled cells of the\nfollowing text. Note that **all** the text in that escape code must be rendered\nin ``s * w`` cells. When both ``s`` and ``w`` are present, the resulting multicell\ncontains all the text in the escape code rendered in a grid of ``(s * w, s)``\ncells, i.e. the multicell is ``s*w`` cells wide and ``s`` cells high.\n\nIf the text does not fit, the terminal is free to do whatever it\nfeels is best, including truncating the text or downsizing the font size when\nrendering it. It is up to client applications to use the ``w`` key wisely and not\ntry to render too much text in too few cells. When sending a string of text\nwith non zero ``w`` to the terminal emulator, the way to do it is to split up the\ntext into chunks that fit in ``w`` cells and send one escape code per chunk. So\nfor the string: ``cool-🐈`` the actual escape codes would be (ignoring the header\nand trailers)::\n\n   w=1;c w=1;o w=1;o w=1;l w=1;- w=2:🐈\n\nNote, in particular, how the last character, the cat emoji, ``🐈`` has ``w=2``.\nIn practice client applications can assume that terminal emulators get the\nwidth of all ASCII code points correct and use the ``w=0`` form for efficient\ntransmission, so that the above becomes::\n\n   cool- w=2:🐈\n\nThe use of non-zero ``w`` should mainly be restricted to non-ASCII characters and\nwhen using fractional scaling, as described below.\n\n.. note:: Text sizes specified by scale are relative to the base font size,\n   thus if the base font size is changed, these sizes are changed as well.\n   So if the terminal emulator is using a base font size of ``11pt``, then\n   ``s=2`` will be rendered in approximately ``22pt`` (approx. because the\n   terminal may need to slightly adjust font size to ensure it fits as not all\n   fonts scale sizes linearly). If the user changes the base font size of the\n   terminal emulator to ``12pt`` then the scaled font size becomes ``~24pt``\n   and so on.\n\n\nFractional scaling\n^^^^^^^^^^^^^^^^^^^^^^^\n\nUsing the main scale parameter (``s``) gives us only 7 font sizes. Fortunately,\nthis protocol allows specifying fractional scaling, fractional scaling is\napplied on top of the main scale specified by ``s``. It allows niceties like:\n\n* Normal sized text but with half a line of blank space above and half a line below (``s=2:n=1:d=2:v=2``)\n* Superscripts (``n=1:d=2``)\n* Subscripts (``n=1:d=2:v=1``)\n* ...\n\nThe fractional scale **does not** affect the number of cells the text occupies,\ninstead, it just adjusts the rendered font size within those cells.\nThe fraction is specified using an integer numerator and denominator (``n`` and\n``d``). In addition, by using the ``v`` key one can vertically align the\nfractionally scaled render area at top, bottom or middle. Similarly, the ``h`` key\ndoes horizontal alignment — left, right or centered. Note that alignment\nhere is not actual text alignment, it refers to how the fractionally scaled\nrender area fits inside the full render area of size ``s * w`` by ``s`` cells.\nThus, alignment only applies when ``n < d``.\n\nWhen using fractional scaling one often wants to fit more than a single\ncharacter per cell. To accommodate that, there is the ``w`` key. This specifies\nthe number of cells in which to render the text. For example, for a superscript\none would typically split the string into pairs of characters and use the\nfollowing for each pair::\n\n    OSC _text_size_code ; n=1:d=2:w=1 ; ab <terminator>\n    ... repeat for each pair of characters\n\n\nFixing the character width issue for the terminal ecosystem\n---------------------------------------------------------------------\n\nTerminals create user interfaces using text displayed in a cell grid. For\nterminal software that creates sophisticated user interfaces it is particularly\nimportant that the client program running in the terminal and the terminal\nitself agree on how many cells a particular string should be rendered in. If\nthe two disagree, then the entire user interface can be broken, leading to\ncatastrophic failures.\n\nFundamentally, this is a co-ordination problem. Both the client program and the\nterminal have to somehow share the same database of character properties and\nthe same algorithm for computing string lengths in cells based on that shared\ndatabase. Sadly, there is no such shared database in reality. The closest we\nhave is the Unicode standard. Unfortunately, the Unicode standard has a new\nversion almost every year and actually changes the width assigned to some\ncharacters in different versions. Furthermore, to actually get the \"correct\"\nwidth for a string using that standard one has to do grapheme segmentation,\nwhich is a :ref:`complex algorithm, specified below <gseg>`.\nExpecting all terminals and all terminal programs to have both up-to-date\ncharacter databases and a bug free implementation of this algorithm is not\nrealistic.\n\nSo instead, this protocol solves the issue robustly by removing the\nco-ordination problem and putting only one actor in charge of determining\nstring width. The client becomes responsible for doing whatever level of\ngrapheme segmentation it is comfortable with using whatever Unicode database is\nat its disposal and then it can transmit the segmented string to the terminal\nwith the appropriate ``w`` values so that the terminal renders the text in the\nexact number of cells the client expects.\n\n.. note::\n   It is possible for a terminal to implement only the width part of this spec\n   and ignore the scale part. This escape code works with only the `w` key as\n   well, as a means of specifying how many cells each piece of text occupies.\n   In such cases ``s`` defaults to 1.\n   See the section on :ref:`detect_text_sizing` on how client applications can\n   query for terminal emulator support.\n\n\nWrapping and overwriting behavior\n-------------------------------------\n\nIf the multicell block (``s * w by s`` cells) is larger than the screen size in either\ndimension, the terminal must discard the character. Note that in particular\nthis means that resizing a terminal screen so that it is too small to fit a\nmulticell character can cause the character to be lost.\n\nWhen drawing a multicell character, if wrapping is enabled (DECAWM is set) and\nthe character's width (``s * w``) does not fit on the current line, the cursor is\nmoved to the start of the next line and the character is drawn there.\nIf wrapping is disabled and the character's width does not fit on the current\nline, the cursor is moved back as far as needed to fit ``s * w`` cells and then\nthe character is drawn, following the overwriting rules described below.\n\nWhen drawing text either normal text or text specified via this escape code,\nand this text would overwrite an existing multicell character, the following\nrules must be followed, in decreasing order of precedence:\n\n#. If the text is a combining character it is added to the existing multicell\n   character\n#. If the text will overwrite the top-left cell of the multicell character, the\n   entire multicell character must be erased\n#. If the text will overwrite any cell in the topmost row of the multicell\n   character, the entire multicell character must be replaced by spaces (this\n   rule is present for backwards compatibility with how overwriting works for\n   wide characters)\n#. If the text will overwrite cells from a row after the first row, then cursor should be moved past the\n   cells of the multicell character on that row and only then the text should be\n   written. Note that this behavior is independent of the value of DECAWM. This\n   is done for simplicity of implementation.\n\nThe skipping behavior of the last rule can be complex requiring the terminal to\nskip over lots of cells, but it is needed to allow wrapping in the presence of\nmulticell characters that extend over more than a single line.\n\n.. _detect_text_sizing:\n\nDetecting if the terminal supports this protocol\n-----------------------------------------------------\n\nTo detect support for this protocol use the `CPR (Cursor Position Report)\n<https://vt100.net/docs/vt510-rm/CPR.html>`__ escape code. Send a ``CR``\n(carriage return) followed by ``CPR`` followed by ``\\e]_text_size_code;w=2; \\a``\nwhich will draw a space character in two cells, followed by another ``CPR``.\nThen send ``\\e]_text_size_code;s=2; \\a`` which will draw a space in a ``2 by 2``\nblock of cells, followed by another ``CPR``.\n\nThen wait for the three responses from the terminal to the three CPR queries.\nIf the cursor position in the three responses is the same, the terminal does\nnot support this protocol at all, if the second response has the cursor\nmoved by two cells, then the width part is supported and if the third response has the\ncursor moved by another two cells, then the scale part is supported.\n\n\nInteraction with other terminal controls\n--------------------------------------------------\n\nThis protocol does not change the character grid based nature of the terminal.\nMost terminal controls assume one character per cell so it is important to\nspecify how these controls interact with the multicell characters created by\nthis protocol.\n\nCursor movement\n^^^^^^^^^^^^^^^^^^^\n\nCursor movement is unaffected by multicell characters, all cursor movement\ncommands move the cursor position by single cell increments, as has always been\nthe case for terminals. This means that the cursor can be placed at any\nindividual single cell inside a larger multicell character.\n\nWhen a multicell character is created using this protocol, the cursor moves\n`s * w` cells to the right, in the same row it was in.\n\nTerminals *should* display a large cursor covering the entire multicell block\nwhen the actual cursor position is on any cell within the block. Block cursors\ncover all the cells of the multicell character, bar cursors appear in all the\ncells in the first column of the character and so on.\n\n\nEditing controls\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThere are many controls used to edit existing screen content such as\ninserting characters, deleting characters and lines, etc. These were all\noriginally specified for the one character per cell paradigm. Here we specify\ntheir interactions with multicell characters.\n\n**Insert characters** (``CSI @`` aka ``ICH``)\n    When inserting ``n`` characters at cursor position ``x, y`` all characters\n    after ``x`` on line ``y`` are supposed to be right shifted. This means\n    that any multi-line character that intersects with the cells on line ``y`` at ``x``\n    and beyond must be erased. Any single line multicell character that is\n    split by the cells at ``x`` and ``x + n - 1`` must also be erased.\n\n**Delete characters** (``CSI P`` aka ``DCH``)\n    When deleting ``n`` characters at cursor position ``x, y`` all characters\n    after ``x`` on line ``y`` are supposed to be left shifted. This means\n    that any multi-line character that intersects with the cells on line ``y`` at ``x``\n    and beyond must be erased. Any single line multicell character that is\n    split by the cells at ``x`` and ``x + n - 1`` must also be erased.\n\n**Erase characters** (``CSI X`` aka ``ECH``)\n    When erasing ``n`` characters at cursor position ``x, y`` the ``n`` cells\n    starting at ``x`` are supposed to be cleared. This means that any multicell\n    character that intersects with the ``n`` cells starting at ``x`` must be\n    erased.\n\n**Erase display** (``CSI J`` aka ``ED``)\n    Any multicell character intersecting with the erased region of the screen\n    must be erased. When using mode ``22`` the contents of the screen are first\n    copied into the history, including all multicell characters.\n\n**Erase in line** (``CSI K`` aka ``EL``)\n    Works just like erase characters above. Any multicell character\n    intersecting with the erased cells in the line is erased.\n\n**Insert lines** (``CSI L`` aka ``IL``)\n    When inserting ``n`` lines at cursor position ``y`` any multi-line\n    characters that are split at the line ``y`` must be erased. A split happens\n    when the second or subsequent row of the multi-line character is on the line\n    ``y``. The insertion causes ``n`` lines to be removed from the bottom of\n    the screen, any multi-line characters are split at the bottom of the screen\n    must be erased. A split is when any row of the multi-line character except\n    the last row is on the last line of the screen after the insertion of ``n``\n    lines.\n\n**Delete lines** (``CSI M`` aka ``DL``)\n    When deleting ``n`` lines at cursor position ``y`` any multicell character\n    that intersects the deleted lines must be erased.\n\n\n.. _gseg:\n\nThe algorithm for splitting text into cells\n------------------------------------------------\n\n.. note::\n   kitty comes with a utility to test terminal compliance with this algorithm.\n   Install kitty and run: ``kitten __width_test__`` in any terminal to test it.\n   This uses tests published by the Unicode consortium, `GraphemeBreakTest.txt\n   <https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.txt>`__.\n\n.. warning::\n   This algorithm is under public discussion in :iss:`8533`. If serious issues\n   are brought to light in that discussion, there may be small changes to the\n   algorithm to address them. Additionally, in the future if the Unicode standard\n   changes in ways that affect this algorithm, it will be updated. Currently the\n   algorithm is based on Unicode version 16.\n\nHere, we specify how a terminal must split up text into cells, where a cell is\na width one unit in the character grid the terminal displays.\n\nThe basis for the algorithm is the\n`Grapheme segmentation algorithm <https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries>`__\nfrom the Unicode standard. However, that algorithm alone is insufficient to\nfully specify text handling for terminals. The full algorithm is specified below.\n\nA terminal using this algorithm must decode the bytes they receive\ninto Unicode scalar values (i.e., code points except surrogates) using UTF-8.\nWhen it encounters any UTF-8 ill-formed subsequences,\nit must be replace each\n`maximal subpart of the ill-formed subsequence <https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G66453>`__\nwith a :code:`U+FFFD REPLACEMENT CHARACTER` (�).\n\nFor each decoded code point:\n\n#. First check if the code point is an ASCII control code, and handle it\n   appropriately. ASCII control codes are the code points less than :code:`U+0032` and the\n   code point :code:`U+0127 DEL`. The code point :code:`U+0000 NUL` must be discarded.\n\n#. Next, check if the code point is *invalid*, and if it is, discard it\n   and finish processing. Invalid code points are code points with Unicode category :code:`Cc or Cs`\n   and 66 additional code points: :code:`[0xfdd0, 0xfdef]`, :code:`[0xfffe, 0x10ffff-1, 0x10000]`\n   and :code:`[0xffff, 0x10ffff, 0x10000]`.\n\n#. Next, check if there is a previous cell before the\n   current cursor position. This means either the cursor is at x > 0 in which\n   case the previous cell is at x-1 on the same line, or the previous cell is\n   the last cell of the previous line, provided there is no line break\n   between the previous and current lines.\n\n#. Next, calculate the width in cells of the received code point,\n   which can be 0, 1, or 2 depending on the code point properties in\n   the Unicode standard.\n\n#. If there is no previous cell and the code point's width is zero,\n   the code point is discarded and its processing is finished.\n\n#. If there is a previous cell, the\n   `Grapheme segmentation algorithm UAX29-C1-1 <https://www.unicode.org/reports/tr29/#C1-1>`__\n   is used to determine if there is a grapheme boundary between the previous cell\n   and the current code point.\n\n#. If there is no boundary, the current code point is added to the previous\n   cell and processing of the code point is finished. See the :ref:`var_select`\n   section below for handling of Unicode Variation selectors.\n\n#. If there is a boundary, but the width of the current code point is zero,\n   it is added to the previous cell and processing is finished.\n\n#. The code point is added to the current cell and the cursor is moved forward\n   (right) by either 1 or 2 cells depending on the width of the code point.\n\n\nIt remains to specify how to calculate the width in cells of a code point.\nTo do this, code points are divided into various classes, as\ndescribed by the rules below, in order of decreasing priority:\n\n.. note::\n   Notation: :code:`[start, stop, step]` means the integers from :code:`start`\n   to :code:`stop` in increments of :code:`step`. When the step is not\n   specified, it defaults to one.\n\n#. *Regional indicators*: 26 code points starting at :code:`0x1F1E6`. These all\n   have width 2\n\n#. *Doublewidth*: Parse `EastAsianWidth.txt\n   <https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt>`__ from\n   the Unicode standard. All code points marked :code:`W` or :code:`F` have\n   width two. All code points in the following ranges have width two *unless*\n   they are marked as :code:`A` in :code:`EastAsianWidth.txt`: :code:`[0x3400,\n   0x4DBF], [0x4E00, 0x9FFF], [0xF900, 0xFAFF], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD]`\n\n#. *Wide emoji*: Parse `emoji-sequences.txt\n   <https://www.unicode.org/Public/emoji/latest/emoji-sequences.txt>`__ from\n   the Unicode standard. All :code:`Basic_Emoji` have width two unless they are\n   followed by :code:`FE0F` in the file. The leading codepoints in all\n   :code:`RGI_Emoji_Modifier_Sequence` and :code:`RGI_Emoji_Tag_Sequence` have width two.\n   All code points in :code:`RGI_Emoji_Flag_Sequence` have width two.\n\n#. *Marks*: These are all zero width code points. They are code points with Unicode\n   categories whose first letter is :code:`M` or :code:`S`. Additionally,\n   code points with Unicode category: :code:`Cf`. Finally, they include\n   all modifier code points from :code:`RGI_Emoji_Modifier_Sequence` in the\n   *Wide emoji* rule above.\n\n#. All remaining code points have a width of one cell.\n\n.. _var_select:\n\nUnicode variation selectors\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThere are two code points (:code:`U+FE0E` and :code:`U+FE0F`) that can actually\nalter the width of the previous code point. When adding a code point to the\nprevious cell these have to be handled specially.\n\n``U+FE0E`` - Variation Selector 15\n  When the previous cell has width two and the last code point in the previous\n  cell is one of the ``Basic_Emoji`` code points from the *Wide emoji* rule above\n  that is *not* followed by ``FEOF`` then the width of the previous cell is\n  decreased to one.\n\n``U+FE0F`` - Variation Selector 16\n  When the previous cell has width one and the last code point in the previous\n  cell is one of the ``Basic_Emoji`` code points from the *Wide emoji* rule above\n  that is followed by ``FEOF`` then the width of the\n  previous cell is increased to two.\n\nNote that the rule for ``U+FE0E`` is particularly problematic for terminals as\nit means that the width of a string cannot be determined without knowing the\nwidth of the screen it will be rendered on. This is because when there is only\none cell left on the current line and a wide emoji is received it wraps onto\nthe next line. If subsequently a ``U+FE0E`` is received, the emoji becomes one\ncell wide but it is *not* moved back to the previous line.\n\nTo avoid this issue, it is recommended applications detect when ``U+FE0E`` is\npresent and in such cases use the width part of the text sizing protocol\nto control rendering.\n"
  },
  {
    "path": "docs/underlines.rst",
    "content": "Colored and styled underlines\n================================\n\n|kitty| supports colored and styled (wavy) underlines. This is of particular use\nin terminal based text editors such as :program:`vim` and :program:`emacs` to\ndisplay red, wavy underlines under mis-spelled words and/or syntax errors. This\nis done by re-purposing some SGR escape codes that are not used in modern\nterminals (`CSI codes <https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences>`__)\n\nTo set the underline style::\n\n    <ESC>[4:0m  # no underline\n    <ESC>[4:1m  # straight underline\n    <ESC>[4:2m  # double underline\n    <ESC>[4:3m  # curly underline\n    <ESC>[4:4m  # dotted underline\n    <ESC>[4:5m  # dashed underline\n    <ESC>[4m    # straight underline (for backwards compat)\n    <ESC>[24m   # no underline (for backwards compat)\n\nTo set the underline color (this is reserved and as far as I can tell not\nactually used for anything)::\n\n    <ESC>[58...m\n\nThis works exactly like the codes ``38, 48`` that are used to set foreground and\nbackground color respectively.\n\nTo reset the underline color (also previously reserved and unused)::\n\n    <ESC>[59m\n\nThe underline color must remain the same under reverse video, if it has a color,\nif not, it should follow the foreground color.\n\nTo detect support for this feature in a terminal emulator, query the terminfo\ndatabase for the ``Su`` boolean capability.\n"
  },
  {
    "path": "docs/unscroll.rst",
    "content": ".. _unscroll:\n\nUnscrolling the screen\n========================\n\nThis is a small extension to the `SD (Pan up) escape code\n<https://vt100.net/docs/vt510-rm/SD.html>`_ from the VT-420 terminal. The ``SD``\nescape code normally causes the text on screen to scroll down by the specified\nnumber of lines, with empty lines appearing at the top of the screen. This\nextension allows the new lines to be filled in from the scrollback buffer\ninstead of being blank.\n\nThe motivation for this is that many modern shells will show completions in a\nblock of lines under the cursor, this causes some of the on-screen text to be\nlost even after the completion is completed, because it has scrolled off\nscreen. This escape code allows that text to be restored.\n\nIf the scrollback buffer is empty or there is no scrollback buffer, such as for\nthe alternate screen, then the newly inserted lines must be empty, just as with\nthe original ``SD`` escape code. The maximum number of lines that can be\nscrolled down is implementation defined, but must be at least one screen worth.\n\nThe syntax of the escape code is identical to that of ``SD`` except that it has\na trailing ``+`` modifier. This is legal under the `ECMA 48 standard\n<https://www.ecma-international.org/publications-and-standards/standards/ecma-48/>`__\nand unused for any other purpose as far as I can tell. So for example, to\nunscroll three lines, the escape code would be::\n\n    CSI 3 + T\n\nSee `discussion here\n<https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/30>`__.\n\n.. versionadded:: 0.20.2\n\nAlso supported by the terminals:\n\n* `mintty <https://github.com/mintty/mintty/releases/tag/3.5.2>`__\n"
  },
  {
    "path": "docs/wide-gamut-colors.rst",
    "content": "Wide gamut color formats\n=========================\n\nkitty supports modern wide gamut color formats for precise color specification.\nThese formats can be used anywhere a color value is accepted in the configuration\n(foreground, background, color0-color255, etc.).\n\nOKLCH Colors\n------------\n\nOKLCH is a perceptually uniform color space, ideal for creating color themes.\nThe format is::\n\n    foreground oklch(0.9 0.05 140)\n    color1     oklch(0.7 0.25 25)\n\nParameters:\n\n- **L** (Lightness): 0 to 1, where 0 is black and 1 is white\n- **C** (Chroma): 0 to approximately 0.4, represents color saturation\n- **H** (Hue): 0 to 360 degrees (0=red, 120=green, 240=blue)\n\nBenefits:\n\n- Perceptually uniform - equal changes produce equal perceived differences\n- Adjusting lightness preserves hue (unlike HSL)\n- Industry standard for modern color design\n\nExample::\n\n    foreground oklch(0.9 0.05 140)\n    color1     oklch(0.65 0.25 29)    # Vibrant red-orange\n    color2     oklch(0.65 0.25 142)   # Vibrant green\n    color3     oklch(0.70 0.19 90)    # Warm yellow\n\nCIE LAB Colors\n--------------\n\nCIE LAB is a device-independent color space designed to approximate human vision.\n\nThe format is::\n\n    background lab(20 5 -10)\n    color4     lab(50 0 -50)\n\nParameters:\n\n- **L**: Lightness, 0 to 100 (0 = black, 100 = white)\n- **a**: Green (-) to red (+), typically -100 to +100\n- **b**: Blue (-) to yellow (+), typically -100 to +100\n\nExample::\n\n    background lab(10 0 0)           # Very dark neutral gray\n    foreground lab(90 0 0)           # Very light neutral gray\n    color1     lab(50 60 40)         # Red\n    color4     lab(50 0 -50)         # Blue\n\nGamut Mapping\n-------------\n\nWhen you specify colors in OKLCH or CIE LAB formats that are outside the sRGB\ncolor gamut, kitty automatically converts them using the CSS Color Module Level 4\ngamut mapping algorithm:\n\n- Preserves the original lightness and hue as much as possible\n- Reduces chroma (saturation) until the color fits within the displayable range\n- Uses perceptual color difference (deltaE OK) to minimize visible changes\n- Maximizes color saturation while staying in gamut\n\nThis ensures that wide gamut colors gracefully degrade on standard sRGB displays while\ntaking full advantage of wide gamut displays when available. The mapping happens\nautomatically - you don't need to do anything special.\n\nFor example, :code:`oklch(0.7 0.4 25)` might be too saturated for sRGB but will be\nautomatically adjusted to fit while preserving the perceived hue and lightness.\n\nReferences\n----------\n\n- `CSS Color Module Level 4 <https://www.w3.org/TR/css-color-4/>`_\n- `OKLCH Color Space <https://bottosson.github.io/posts/oklab/>`_\n"
  },
  {
    "path": "embeds.go",
    "content": "package kitty\n\nimport (\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\n//go:embed logo/kitty.png\nvar KittyLogoAsPNGData []byte\n\n//go:embed kitty_tests/GraphemeBreakTest.json\nvar grapheme_break_test_data []byte\n\ntype GraphemeBreakTest struct {\n\tData    []string `json:\"data\"`\n\tComment string   `json:\"comment\"`\n}\n\nfunc LoadGraphemeBreakTests() (ans []GraphemeBreakTest, err error) {\n\tif err := json.Unmarshal(grapheme_break_test_data, &ans); err != nil {\n\t\treturn nil, fmt.Errorf(\"Failed to parse GraphemeBreakTest JSON with error: %s\", err)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "gen/README.rst",
    "content": "Scripts to generate code for various things like keys, mouse cursors, unicode\ndata etc. Some of these generate code that is checked into version control.\nSome generate ephemeral code used during builds. Ephemeral code is in files\nwith a _generated.[h|go|c] extension.\n"
  },
  {
    "path": "gen/__init__.py",
    "content": ""
  },
  {
    "path": "gen/__main__.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport sys\n\n\ndef main(args: list[str]=sys.argv) -> None:\n    os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n    sys.path.insert(0, os.getcwd())\n    if len(args) == 1:\n        raise SystemExit('usage: python gen which')\n    which = args[1]\n    del args[1]\n    if which == 'apc-parsers':\n        from gen.apc_parsers import main\n        main(args)\n    elif which == 'config':\n        from gen.config import main\n        main(args)\n    elif which == 'srgb-lut':\n        from gen.srgb_lut import main\n        main(args)\n    elif which == 'key-constants':\n        from gen.key_constants import main\n        main(args)\n    elif which == 'go-code':\n        from gen.go_code import main\n        main(args)\n    elif which == 'wcwidth':\n        from gen.wcwidth import main\n        main(args)\n    elif which == 'cursors':\n        from gen.cursors import main\n        main(args)\n    elif which == 'color-names':\n        from gen.color_names import main\n        main(args)\n    else:\n        raise SystemExit(f'Unknown which: {which}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "gen/apc_parsers.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport subprocess\nimport sys\nfrom collections import defaultdict\nfrom typing import Any, DefaultDict, Union\n\nif __name__ == '__main__' and not __package__:\n    import __main__\n    __main__.__package__ = 'gen'\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\nKeymapType = dict[str, tuple[str, Union[frozenset[str], str]]]\n\n\ndef resolve_keys(keymap: KeymapType) -> DefaultDict[str, list[str]]:\n    ans: DefaultDict[str, list[str]] = defaultdict(list)\n    for ch, (attr, atype) in keymap.items():\n        if isinstance(atype, str) and atype in ('int', 'uint'):\n            q = atype\n        else:\n            q = 'flag'\n        ans[q].append(ch)\n    return ans\n\n\ndef enum(keymap: KeymapType) -> str:\n    lines = []\n    for ch, (attr, atype) in keymap.items():\n        lines.append(f\"{attr}='{ch}'\")\n    return '''\n    enum KEYS {{\n        {}\n    }};\n    '''.format(',\\n'.join(lines))\n\n\ndef parse_key(keymap: KeymapType) -> str:\n    lines = []\n    for attr, atype in keymap.values():\n        vs = atype.upper() if isinstance(atype, str) and atype in ('uint', 'int') else 'FLAG'\n        lines.append(f'case {attr}: value_state = {vs}; break;')\n    return '        \\n'.join(lines)\n\n\ndef parse_flag(keymap: KeymapType, type_map: dict[str, Any], command_class: str) -> str:\n    lines = []\n    for ch in type_map['flag']:\n        attr, allowed_values = keymap[ch]\n        q = ' && '.join(f\"g.{attr} != '{x}'\" for x in sorted(allowed_values))\n        lines.append(f'''\n            case {attr}: {{\n                g.{attr} = parser_buf[pos++];\n                if ({q}) {{\n                    REPORT_ERROR(\"Malformed {command_class} control block, unknown flag value for {attr}: 0x%x\", g.{attr});\n                    return;\n                }};\n            }}\n            break;\n        ''')\n    return '        \\n'.join(lines)\n\n\ndef parse_number(keymap: KeymapType) -> tuple[str, str]:\n    int_keys = [f'I({attr})' for attr, atype in keymap.values() if atype == 'int']\n    uint_keys = [f'U({attr})' for attr, atype in keymap.values() if atype == 'uint']\n    return '; '.join(int_keys), '; '.join(uint_keys)\n\n\ndef cmd_for_report(report_name: str, keymap: KeymapType, type_map: dict[str, Any], payload_allowed: bool, payload_is_base64: bool) -> str:\n    def group(atype: str, conv: str) -> tuple[str, str]:\n        flag_fmt, flag_attrs = [], []\n        cv = {'flag': 'c', 'int': 'i', 'uint': 'I'}[atype]\n        for ch in type_map[atype]:\n            flag_fmt.append(f's{cv}')\n            attr = keymap[ch][0]\n            flag_attrs.append(f'\"{attr}\", {conv}g.{attr}')\n        return ' '.join(flag_fmt), ', '.join(flag_attrs)\n\n    flag_fmt, flag_attrs = group('flag', '')\n    int_fmt, int_attrs = group('int', '(int)')\n    uint_fmt, uint_attrs = group('uint', '(unsigned int)')\n\n    fmt = f'{flag_fmt} {uint_fmt} {int_fmt}'\n    if payload_allowed:\n        ans = [f'REPORT_VA_COMMAND(\"K s {{{fmt} ss#}}\", self->window_id, \"{report_name}\",\\n']\n    else:\n        ans = [f'REPORT_VA_COMMAND(\"K s {{{fmt}}}\", self->window_id, \"{report_name}\",\\n']\n    if flag_attrs:\n        ans.append(f'{flag_attrs},\\n')\n    if uint_attrs:\n        ans.append(f'{uint_attrs},\\n')\n    if int_attrs:\n        ans.append(f'{int_attrs},\\n')\n    if payload_allowed:\n        if payload_is_base64:\n            ans.append('\"\", (char*)parser_buf, g.payload_sz')\n        else:\n            ans.append('\"\", (char*)parser_buf + payload_start, g.payload_sz')\n    ans.append(');')\n    return '\\n'.join(ans)\n\n\ndef generate(\n    function_name: str,\n    callback_name: str,\n    report_name: str,\n    keymap: KeymapType,\n    command_class: str,\n    initial_key: str = 'a',\n    payload_allowed: bool = True,\n    payload_is_base64: bool = True,\n    start_parsing_at: int = 1,\n    field_sep: str = ',',\n) -> str:\n    type_map = resolve_keys(keymap)\n    keys_enum = enum(keymap)\n    handle_key = parse_key(keymap)\n    flag_keys = parse_flag(keymap, type_map, command_class)\n    int_keys, uint_keys = parse_number(keymap)\n    report_cmd = cmd_for_report(report_name, keymap, type_map, payload_allowed, payload_is_base64)\n    extra_init = ''\n    if payload_allowed:\n        payload_after_value = \"case ';': state = PAYLOAD; break;\"\n        payload = ', PAYLOAD'\n        if payload_is_base64:\n            payload_case = f'''\n                case PAYLOAD: {{\n                    sz = parser_buf_pos - pos;\n                    g.payload_sz = MAX(BUF_EXTRA, sz);\n                    if (!base64_decode8(parser_buf + pos, sz, parser_buf, &g.payload_sz)) {{\n                        g.payload_sz = MAX(BUF_EXTRA, sz);\n                        REPORT_ERROR(\"Failed to parse {command_class} command payload with error: \\\n    invalid base64 data in chunk of size: %zu with output buffer size: %zu\", sz, g.payload_sz); return; }}\n                    pos = parser_buf_pos;\n                    }} break;\n            '''\n            callback = f'{callback_name}(self->screen, &g, parser_buf)'\n        else:\n            payload_case = '''\n                case PAYLOAD: {\n                    sz = parser_buf_pos - pos;\n                    payload_start = pos;\n                    g.payload_sz = sz;\n                    pos = parser_buf_pos;\n                } break;\n            '''\n            extra_init = 'size_t payload_start = 0;'\n            callback = f'{callback_name}(self->screen, &g, parser_buf + payload_start)'\n\n    else:\n        payload_after_value = payload = payload_case = ''\n        callback = f'{callback_name}(self->screen, &g)'\n\n    return f'''\n    #include \"base64.h\"\n\nstatic inline void\n{function_name}(PS *self, uint8_t *parser_buf, const size_t parser_buf_pos) {{\n    unsigned int pos = {start_parsing_at};\n    {extra_init}\n    enum PARSER_STATES {{ KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE {payload} }};\n    enum PARSER_STATES state = KEY, value_state = FLAG;\n    {command_class} g = {{0}};\n    unsigned int i, code;\n    uint64_t lcode; int64_t accumulator;\n    bool is_negative; (void)is_negative;\n    size_t sz;\n    {keys_enum}\n    enum KEYS key = '{initial_key}';\n    if (parser_buf[pos] == ';') state = AFTER_VALUE;\n\n    while (pos < parser_buf_pos) {{\n        switch(state) {{\n            case KEY:\n                key = parser_buf[pos++];\n                state = EQUAL;\n                switch(key) {{\n                    {handle_key}\n                    default:\n                        REPORT_ERROR(\"Malformed {command_class} control block, invalid key character: 0x%x\", key);\n                        return;\n                }}\n                break;\n\n            case EQUAL:\n                if (parser_buf[pos++] != '=') {{\n                    REPORT_ERROR(\"Malformed {command_class} control block, no = after key, found: 0x%x instead\", parser_buf[pos-1]);\n                    return;\n                }}\n                state = value_state;\n                break;\n\n            case FLAG:\n                switch(key) {{\n                    {flag_keys}\n                    default:\n                        break;\n                }}\n                state = AFTER_VALUE;\n                break;\n\n            case INT:\n#define READ_UINT \\\\\n                for (i = pos, accumulator=0; i < MIN(parser_buf_pos, pos + 10); i++) {{ \\\\\n                    int64_t n = parser_buf[i] - '0'; if (n < 0 || n > 9) break; \\\\\n                    accumulator += n * digit_multipliers[i - pos]; \\\\\n                }} \\\\\n                if (i == pos) {{ REPORT_ERROR(\"Malformed {command_class} control block, expecting an integer value for key: %c\", key & 0xFF); return; }} \\\\\n                lcode = accumulator / digit_multipliers[i - pos - 1]; pos = i; \\\\\n                if (lcode > UINT32_MAX) {{ REPORT_ERROR(\"Malformed {command_class} control block, number is too large\"); return; }} \\\\\n                code = lcode;\n\n                is_negative = false;\n                if(parser_buf[pos] == '-') {{ is_negative = true; pos++; }}\n#define I(x) case x: g.x = is_negative ? 0 - (int32_t)code : (int32_t)code; break\n                READ_UINT;\n                switch(key) {{\n                    {int_keys};\n                    default: break;\n                }}\n                state = AFTER_VALUE;\n                break;\n#undef I\n            case UINT:\n                READ_UINT;\n#define U(x) case x: g.x = code; break\n                switch(key) {{\n                    {uint_keys};\n                    default: break;\n                }}\n                state = AFTER_VALUE;\n                break;\n#undef U\n#undef READ_UINT\n\n            case AFTER_VALUE:\n                switch (parser_buf[pos++]) {{\n                    default:\n                        REPORT_ERROR(\"Malformed {command_class} control block, expecting a {field_sep} or semi-colon after a value, found: 0x%x\",\n                                     parser_buf[pos - 1]);\n                        return;\n                    case '{field_sep}':\n                        state = KEY;\n                        break;\n                    {payload_after_value}\n                }}\n                break;\n\n            {payload_case}\n\n        }} // end switch\n    }} // end while\n\n    switch(state) {{\n        case EQUAL:\n            REPORT_ERROR(\"Malformed {command_class} control block, no = after key\"); return;\n        case INT:\n        case UINT:\n            REPORT_ERROR(\"Malformed {command_class} control block, expecting an integer value\"); return;\n        case FLAG:\n            REPORT_ERROR(\"Malformed {command_class} control block, expecting a flag value\"); return;\n        default:\n            break;\n    }}\n\n    {report_cmd}\n\n    {callback};\n}}\n    '''\n\n\ndef write_header(text: str, path: str) -> None:\n    with open(path, 'w') as f:\n        print(f'// This file is generated by {os.path.basename(__file__)} do not edit!', file=f, end='\\n\\n')\n        print('#pragma once', file=f)\n        print(text, file=f)\n    subprocess.check_call(['clang-format', '-i', path])\n\n\ndef parsers() -> None:\n    flag = frozenset\n    keymap: KeymapType = {\n        'a': ('action', flag('tTqpdfac')),\n        'd': ('delete_action', flag('aAiIcCfFnNpPqQrRxXyYzZ')),\n        't': ('transmission_type', flag('dfts')),\n        'o': ('compressed', flag('z')),\n        'f': ('format', 'uint'),\n        'm': ('more', 'uint'),\n        'i': ('id', 'uint'),\n        'I': ('image_number', 'uint'),\n        'p': ('placement_id', 'uint'),\n        'q': ('quiet', 'uint'),\n        'w': ('width', 'uint'),\n        'h': ('height', 'uint'),\n        'x': ('x_offset', 'uint'),\n        'y': ('y_offset', 'uint'),\n        'v': ('data_height', 'uint'),\n        's': ('data_width', 'uint'),\n        'S': ('data_sz', 'uint'),\n        'O': ('data_offset', 'uint'),\n        'c': ('num_cells', 'uint'),\n        'r': ('num_lines', 'uint'),\n        'X': ('cell_x_offset', 'uint'),\n        'Y': ('cell_y_offset', 'uint'),\n        'z': ('z_index', 'int'),\n        'C': ('cursor_movement', 'uint'),\n        'U': ('unicode_placement', 'uint'),\n        'P': ('parent_id', 'uint'),\n        'Q': ('parent_placement_id', 'uint'),\n        'H': ('offset_from_parent_x', 'int'),\n        'V': ('offset_from_parent_y', 'int'),\n    }\n    text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')\n    write_header(text, 'kitty/parse-graphics-command.h')\n    keymap = {\n        'w': ('width', 'uint'),\n        's': ('scale', 'uint'),\n        'n': ('subscale_n', 'uint'),\n        'd': ('subscale_d', 'uint'),\n        'v': ('vertical_align', 'uint'),\n        'h': ('horizontal_align', 'uint'),\n    }\n    text = generate(\n        'parse_multicell_code', 'screen_handle_multicell_command', 'multicell_command', keymap, 'MultiCellCommand',\n        payload_is_base64=False, start_parsing_at=0, field_sep=':')\n    write_header(text, 'kitty/parse-multicell-command.h')\n\n\ndef main(args: list[str]=sys.argv) -> None:\n    parsers()\n\n\nif __name__ == '__main__':\n    import runpy\n    m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))\n    m['main']([sys.executable, 'apc-parsers'])\n"
  },
  {
    "path": "gen/bitfields.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nfrom typing import NamedTuple\n\n\nclass BitField(NamedTuple):\n     name: str\n     bits: int\n\n\ndef typename_for_bitsize(bits: int) -> str:\n     if bits <= 8:\n        return 'uint8'\n     if bits <= 16:\n        return 'uint16'\n     if bits <= 32:\n        return 'uint32'\n     return 'uint64'\n\n\ndef make_bitfield(dest: str, typename: str, *fields_: str, add_package: bool = True) -> tuple[str, str]:\n    output_path = os.path.join(dest, f'{typename.lower()}_generated.go')\n    ans = [f'package {os.path.basename(dest)}', '']\n    a = ans.append\n    if not add_package:\n        del ans[0]\n\n    def fieldify(spec: str) -> BitField:\n        name, num = spec.partition(' ')[::2]\n        return BitField(name, int(num))\n\n    fields = tuple(map(fieldify, fields_))\n    total_size = sum(x.bits for x in fields)\n    if total_size > 64:\n        raise ValueError(f'Total size of bit fields: {total_size} for {typename} is larger than 64 bits')\n    a(f'// Total number of bits used: {total_size}')\n    itype = typename_for_bitsize(total_size)\n    a(f'type {typename} {itype}')\n    a('')\n    shift = 0\n    for bf in reversed(fields):\n        tn = typename_for_bitsize(bf.bits)\n        mask = '0b' + '1' * bf.bits\n        a(f'func (s {typename}) {bf.name.capitalize()}() {tn} {{')  # }}\n        if shift:\n            a(f'    return {tn}((s >> {shift}) & {mask})')\n        else:\n            a(f'    return {tn}(s & {mask})')\n        a('}')\n        a('')\n        a(f'func (s *{typename}) Set_{bf.name}(val {tn}) {{')  # }}\n        if shift:\n            a(f'    *s &^= {mask} << {shift}')\n            a(f'    *s |= {typename}(val&{mask}) << {shift}')\n        else:\n            a(f'    *s &^= {mask}')\n            a(f'    *s |= {typename}(val & {mask})')\n        a('}')\n        a('')\n        shift += bf.bits\n\n    return output_path, '\\n'.join(ans)\n"
  },
  {
    "path": "gen/color_names.py",
    "content": "#!./kitty/launcher/kitty +launch\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport subprocess\nimport sys\nimport tempfile\n\nif __name__ == '__main__' and not __package__:\n    import __main__\n    __main__.__package__ = 'gen'\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\n\ndef Color(r: int, g: int, b: int) -> int:\n    return (r << 16) | (g << 8) | b\n\n# BEGIN_DATA_SECTION {{{\ncolor_names = {\n 'alice blue': Color(240, 248, 255),\n 'aliceblue': Color(240, 248, 255),\n 'antique white': Color(250, 235, 215),\n 'antiquewhite': Color(250, 235, 215),\n 'antiquewhite1': Color(255, 239, 219),\n 'antiquewhite2': Color(238, 223, 204),\n 'antiquewhite3': Color(205, 192, 176),\n 'antiquewhite4': Color(139, 131, 120),\n 'aquamarine': Color(127, 255, 212),\n 'aquamarine1': Color(127, 255, 212),\n 'aquamarine2': Color(118, 238, 198),\n 'aquamarine3': Color(102, 205, 170),\n 'aquamarine4': Color(69, 139, 116),\n 'azure': Color(240, 255, 255),\n 'azure1': Color(240, 255, 255),\n 'azure2': Color(224, 238, 238),\n 'azure3': Color(193, 205, 205),\n 'azure4': Color(131, 139, 139),\n 'beige': Color(245, 245, 220),\n 'bisque': Color(255, 228, 196),\n 'bisque1': Color(255, 228, 196),\n 'bisque2': Color(238, 213, 183),\n 'bisque3': Color(205, 183, 158),\n 'bisque4': Color(139, 125, 107),\n 'black': Color(0, 0, 0),\n 'blanched almond': Color(255, 235, 205),\n 'blanchedalmond': Color(255, 235, 205),\n 'blue': Color(0, 0, 255),\n 'blue violet': Color(138, 43, 226),\n 'blue1': Color(0, 0, 255),\n 'blue2': Color(0, 0, 238),\n 'blue3': Color(0, 0, 205),\n 'blue4': Color(0, 0, 139),\n 'blueviolet': Color(138, 43, 226),\n 'brown': Color(165, 42, 42),\n 'brown1': Color(255, 64, 64),\n 'brown2': Color(238, 59, 59),\n 'brown3': Color(205, 51, 51),\n 'brown4': Color(139, 35, 35),\n 'burlywood': Color(222, 184, 135),\n 'burlywood1': Color(255, 211, 155),\n 'burlywood2': Color(238, 197, 145),\n 'burlywood3': Color(205, 170, 125),\n 'burlywood4': Color(139, 115, 85),\n 'cadet blue': Color(95, 158, 160),\n 'cadetblue': Color(95, 158, 160),\n 'cadetblue1': Color(152, 245, 255),\n 'cadetblue2': Color(142, 229, 238),\n 'cadetblue3': Color(122, 197, 205),\n 'cadetblue4': Color(83, 134, 139),\n 'chartreuse': Color(127, 255, 0),\n 'chartreuse1': Color(127, 255, 0),\n 'chartreuse2': Color(118, 238, 0),\n 'chartreuse3': Color(102, 205, 0),\n 'chartreuse4': Color(69, 139, 0),\n 'chocolate': Color(210, 105, 30),\n 'chocolate1': Color(255, 127, 36),\n 'chocolate2': Color(238, 118, 33),\n 'chocolate3': Color(205, 102, 29),\n 'chocolate4': Color(139, 69, 19),\n 'coral': Color(255, 127, 80),\n 'coral1': Color(255, 114, 86),\n 'coral2': Color(238, 106, 80),\n 'coral3': Color(205, 91, 69),\n 'coral4': Color(139, 62, 47),\n 'cornflower blue': Color(100, 149, 237),\n 'cornflowerblue': Color(100, 149, 237),\n 'cornsilk': Color(255, 248, 220),\n 'cornsilk1': Color(255, 248, 220),\n 'cornsilk2': Color(238, 232, 205),\n 'cornsilk3': Color(205, 200, 177),\n 'cornsilk4': Color(139, 136, 120),\n 'cyan': Color(0, 255, 255),\n 'cyan1': Color(0, 255, 255),\n 'cyan2': Color(0, 238, 238),\n 'cyan3': Color(0, 205, 205),\n 'cyan4': Color(0, 139, 139),\n 'dark blue': Color(0, 0, 139),\n 'dark cyan': Color(0, 139, 139),\n 'dark goldenrod': Color(184, 134, 11),\n 'dark gray': Color(169, 169, 169),\n 'dark green': Color(0, 100, 0),\n 'dark grey': Color(169, 169, 169),\n 'dark khaki': Color(189, 183, 107),\n 'dark magenta': Color(139, 0, 139),\n 'dark olive green': Color(85, 107, 47),\n 'dark orange': Color(255, 140, 0),\n 'dark orchid': Color(153, 50, 204),\n 'dark red': Color(139, 0, 0),\n 'dark salmon': Color(233, 150, 122),\n 'dark sea green': Color(143, 188, 143),\n 'dark slate blue': Color(72, 61, 139),\n 'dark slate gray': Color(47, 79, 79),\n 'dark slate grey': Color(47, 79, 79),\n 'dark turquoise': Color(0, 206, 209),\n 'dark violet': Color(148, 0, 211),\n 'darkblue': Color(0, 0, 139),\n 'darkcyan': Color(0, 139, 139),\n 'darkgoldenrod': Color(184, 134, 11),\n 'darkgoldenrod1': Color(255, 185, 15),\n 'darkgoldenrod2': Color(238, 173, 14),\n 'darkgoldenrod3': Color(205, 149, 12),\n 'darkgoldenrod4': Color(139, 101, 8),\n 'darkgray': Color(169, 169, 169),\n 'darkgreen': Color(0, 100, 0),\n 'darkgrey': Color(169, 169, 169),\n 'darkkhaki': Color(189, 183, 107),\n 'darkmagenta': Color(139, 0, 139),\n 'darkolivegreen': Color(85, 107, 47),\n 'darkolivegreen1': Color(202, 255, 112),\n 'darkolivegreen2': Color(188, 238, 104),\n 'darkolivegreen3': Color(162, 205, 90),\n 'darkolivegreen4': Color(110, 139, 61),\n 'darkorange': Color(255, 140, 0),\n 'darkorange1': Color(255, 127, 0),\n 'darkorange2': Color(238, 118, 0),\n 'darkorange3': Color(205, 102, 0),\n 'darkorange4': Color(139, 69, 0),\n 'darkorchid': Color(153, 50, 204),\n 'darkorchid1': Color(191, 62, 255),\n 'darkorchid2': Color(178, 58, 238),\n 'darkorchid3': Color(154, 50, 205),\n 'darkorchid4': Color(104, 34, 139),\n 'darkred': Color(139, 0, 0),\n 'darksalmon': Color(233, 150, 122),\n 'darkseagreen': Color(143, 188, 143),\n 'darkseagreen1': Color(193, 255, 193),\n 'darkseagreen2': Color(180, 238, 180),\n 'darkseagreen3': Color(155, 205, 155),\n 'darkseagreen4': Color(105, 139, 105),\n 'darkslateblue': Color(72, 61, 139),\n 'darkslategray': Color(47, 79, 79),\n 'darkslategray1': Color(151, 255, 255),\n 'darkslategray2': Color(141, 238, 238),\n 'darkslategray3': Color(121, 205, 205),\n 'darkslategray4': Color(82, 139, 139),\n 'darkslategrey': Color(47, 79, 79),\n 'darkturquoise': Color(0, 206, 209),\n 'darkviolet': Color(148, 0, 211),\n 'debianred': Color(215, 7, 81),\n 'deep pink': Color(255, 20, 147),\n 'deep sky blue': Color(0, 191, 255),\n 'deeppink': Color(255, 20, 147),\n 'deeppink1': Color(255, 20, 147),\n 'deeppink2': Color(238, 18, 137),\n 'deeppink3': Color(205, 16, 118),\n 'deeppink4': Color(139, 10, 80),\n 'deepskyblue': Color(0, 191, 255),\n 'deepskyblue1': Color(0, 191, 255),\n 'deepskyblue2': Color(0, 178, 238),\n 'deepskyblue3': Color(0, 154, 205),\n 'deepskyblue4': Color(0, 104, 139),\n 'dim gray': Color(105, 105, 105),\n 'dim grey': Color(105, 105, 105),\n 'dimgray': Color(105, 105, 105),\n 'dimgrey': Color(105, 105, 105),\n 'dodger blue': Color(30, 144, 255),\n 'dodgerblue': Color(30, 144, 255),\n 'dodgerblue1': Color(30, 144, 255),\n 'dodgerblue2': Color(28, 134, 238),\n 'dodgerblue3': Color(24, 116, 205),\n 'dodgerblue4': Color(16, 78, 139),\n 'firebrick': Color(178, 34, 34),\n 'firebrick1': Color(255, 48, 48),\n 'firebrick2': Color(238, 44, 44),\n 'firebrick3': Color(205, 38, 38),\n 'firebrick4': Color(139, 26, 26),\n 'floral white': Color(255, 250, 240),\n 'floralwhite': Color(255, 250, 240),\n 'forest green': Color(34, 139, 34),\n 'forestgreen': Color(34, 139, 34),\n 'gainsboro': Color(220, 220, 220),\n 'ghost white': Color(248, 248, 255),\n 'ghostwhite': Color(248, 248, 255),\n 'gold': Color(255, 215, 0),\n 'gold1': Color(255, 215, 0),\n 'gold2': Color(238, 201, 0),\n 'gold3': Color(205, 173, 0),\n 'gold4': Color(139, 117, 0),\n 'goldenrod': Color(218, 165, 32),\n 'goldenrod1': Color(255, 193, 37),\n 'goldenrod2': Color(238, 180, 34),\n 'goldenrod3': Color(205, 155, 29),\n 'goldenrod4': Color(139, 105, 20),\n 'gray': Color(190, 190, 190),\n 'gray0': Color(0, 0, 0),\n 'gray1': Color(3, 3, 3),\n 'gray10': Color(26, 26, 26),\n 'gray100': Color(255, 255, 255),\n 'gray11': Color(28, 28, 28),\n 'gray12': Color(31, 31, 31),\n 'gray13': Color(33, 33, 33),\n 'gray14': Color(36, 36, 36),\n 'gray15': Color(38, 38, 38),\n 'gray16': Color(41, 41, 41),\n 'gray17': Color(43, 43, 43),\n 'gray18': Color(46, 46, 46),\n 'gray19': Color(48, 48, 48),\n 'gray2': Color(5, 5, 5),\n 'gray20': Color(51, 51, 51),\n 'gray21': Color(54, 54, 54),\n 'gray22': Color(56, 56, 56),\n 'gray23': Color(59, 59, 59),\n 'gray24': Color(61, 61, 61),\n 'gray25': Color(64, 64, 64),\n 'gray26': Color(66, 66, 66),\n 'gray27': Color(69, 69, 69),\n 'gray28': Color(71, 71, 71),\n 'gray29': Color(74, 74, 74),\n 'gray3': Color(8, 8, 8),\n 'gray30': Color(77, 77, 77),\n 'gray31': Color(79, 79, 79),\n 'gray32': Color(82, 82, 82),\n 'gray33': Color(84, 84, 84),\n 'gray34': Color(87, 87, 87),\n 'gray35': Color(89, 89, 89),\n 'gray36': Color(92, 92, 92),\n 'gray37': Color(94, 94, 94),\n 'gray38': Color(97, 97, 97),\n 'gray39': Color(99, 99, 99),\n 'gray4': Color(10, 10, 10),\n 'gray40': Color(102, 102, 102),\n 'gray41': Color(105, 105, 105),\n 'gray42': Color(107, 107, 107),\n 'gray43': Color(110, 110, 110),\n 'gray44': Color(112, 112, 112),\n 'gray45': Color(115, 115, 115),\n 'gray46': Color(117, 117, 117),\n 'gray47': Color(120, 120, 120),\n 'gray48': Color(122, 122, 122),\n 'gray49': Color(125, 125, 125),\n 'gray5': Color(13, 13, 13),\n 'gray50': Color(127, 127, 127),\n 'gray51': Color(130, 130, 130),\n 'gray52': Color(133, 133, 133),\n 'gray53': Color(135, 135, 135),\n 'gray54': Color(138, 138, 138),\n 'gray55': Color(140, 140, 140),\n 'gray56': Color(143, 143, 143),\n 'gray57': Color(145, 145, 145),\n 'gray58': Color(148, 148, 148),\n 'gray59': Color(150, 150, 150),\n 'gray6': Color(15, 15, 15),\n 'gray60': Color(153, 153, 153),\n 'gray61': Color(156, 156, 156),\n 'gray62': Color(158, 158, 158),\n 'gray63': Color(161, 161, 161),\n 'gray64': Color(163, 163, 163),\n 'gray65': Color(166, 166, 166),\n 'gray66': Color(168, 168, 168),\n 'gray67': Color(171, 171, 171),\n 'gray68': Color(173, 173, 173),\n 'gray69': Color(176, 176, 176),\n 'gray7': Color(18, 18, 18),\n 'gray70': Color(179, 179, 179),\n 'gray71': Color(181, 181, 181),\n 'gray72': Color(184, 184, 184),\n 'gray73': Color(186, 186, 186),\n 'gray74': Color(189, 189, 189),\n 'gray75': Color(191, 191, 191),\n 'gray76': Color(194, 194, 194),\n 'gray77': Color(196, 196, 196),\n 'gray78': Color(199, 199, 199),\n 'gray79': Color(201, 201, 201),\n 'gray8': Color(20, 20, 20),\n 'gray80': Color(204, 204, 204),\n 'gray81': Color(207, 207, 207),\n 'gray82': Color(209, 209, 209),\n 'gray83': Color(212, 212, 212),\n 'gray84': Color(214, 214, 214),\n 'gray85': Color(217, 217, 217),\n 'gray86': Color(219, 219, 219),\n 'gray87': Color(222, 222, 222),\n 'gray88': Color(224, 224, 224),\n 'gray89': Color(227, 227, 227),\n 'gray9': Color(23, 23, 23),\n 'gray90': Color(229, 229, 229),\n 'gray91': Color(232, 232, 232),\n 'gray92': Color(235, 235, 235),\n 'gray93': Color(237, 237, 237),\n 'gray94': Color(240, 240, 240),\n 'gray95': Color(242, 242, 242),\n 'gray96': Color(245, 245, 245),\n 'gray97': Color(247, 247, 247),\n 'gray98': Color(250, 250, 250),\n 'gray99': Color(252, 252, 252),\n 'green': Color(0, 255, 0),\n 'green yellow': Color(173, 255, 47),\n 'green1': Color(0, 255, 0),\n 'green2': Color(0, 238, 0),\n 'green3': Color(0, 205, 0),\n 'green4': Color(0, 139, 0),\n 'greenyellow': Color(173, 255, 47),\n 'grey': Color(190, 190, 190),\n 'grey0': Color(0, 0, 0),\n 'grey1': Color(3, 3, 3),\n 'grey10': Color(26, 26, 26),\n 'grey100': Color(255, 255, 255),\n 'grey11': Color(28, 28, 28),\n 'grey12': Color(31, 31, 31),\n 'grey13': Color(33, 33, 33),\n 'grey14': Color(36, 36, 36),\n 'grey15': Color(38, 38, 38),\n 'grey16': Color(41, 41, 41),\n 'grey17': Color(43, 43, 43),\n 'grey18': Color(46, 46, 46),\n 'grey19': Color(48, 48, 48),\n 'grey2': Color(5, 5, 5),\n 'grey20': Color(51, 51, 51),\n 'grey21': Color(54, 54, 54),\n 'grey22': Color(56, 56, 56),\n 'grey23': Color(59, 59, 59),\n 'grey24': Color(61, 61, 61),\n 'grey25': Color(64, 64, 64),\n 'grey26': Color(66, 66, 66),\n 'grey27': Color(69, 69, 69),\n 'grey28': Color(71, 71, 71),\n 'grey29': Color(74, 74, 74),\n 'grey3': Color(8, 8, 8),\n 'grey30': Color(77, 77, 77),\n 'grey31': Color(79, 79, 79),\n 'grey32': Color(82, 82, 82),\n 'grey33': Color(84, 84, 84),\n 'grey34': Color(87, 87, 87),\n 'grey35': Color(89, 89, 89),\n 'grey36': Color(92, 92, 92),\n 'grey37': Color(94, 94, 94),\n 'grey38': Color(97, 97, 97),\n 'grey39': Color(99, 99, 99),\n 'grey4': Color(10, 10, 10),\n 'grey40': Color(102, 102, 102),\n 'grey41': Color(105, 105, 105),\n 'grey42': Color(107, 107, 107),\n 'grey43': Color(110, 110, 110),\n 'grey44': Color(112, 112, 112),\n 'grey45': Color(115, 115, 115),\n 'grey46': Color(117, 117, 117),\n 'grey47': Color(120, 120, 120),\n 'grey48': Color(122, 122, 122),\n 'grey49': Color(125, 125, 125),\n 'grey5': Color(13, 13, 13),\n 'grey50': Color(127, 127, 127),\n 'grey51': Color(130, 130, 130),\n 'grey52': Color(133, 133, 133),\n 'grey53': Color(135, 135, 135),\n 'grey54': Color(138, 138, 138),\n 'grey55': Color(140, 140, 140),\n 'grey56': Color(143, 143, 143),\n 'grey57': Color(145, 145, 145),\n 'grey58': Color(148, 148, 148),\n 'grey59': Color(150, 150, 150),\n 'grey6': Color(15, 15, 15),\n 'grey60': Color(153, 153, 153),\n 'grey61': Color(156, 156, 156),\n 'grey62': Color(158, 158, 158),\n 'grey63': Color(161, 161, 161),\n 'grey64': Color(163, 163, 163),\n 'grey65': Color(166, 166, 166),\n 'grey66': Color(168, 168, 168),\n 'grey67': Color(171, 171, 171),\n 'grey68': Color(173, 173, 173),\n 'grey69': Color(176, 176, 176),\n 'grey7': Color(18, 18, 18),\n 'grey70': Color(179, 179, 179),\n 'grey71': Color(181, 181, 181),\n 'grey72': Color(184, 184, 184),\n 'grey73': Color(186, 186, 186),\n 'grey74': Color(189, 189, 189),\n 'grey75': Color(191, 191, 191),\n 'grey76': Color(194, 194, 194),\n 'grey77': Color(196, 196, 196),\n 'grey78': Color(199, 199, 199),\n 'grey79': Color(201, 201, 201),\n 'grey8': Color(20, 20, 20),\n 'grey80': Color(204, 204, 204),\n 'grey81': Color(207, 207, 207),\n 'grey82': Color(209, 209, 209),\n 'grey83': Color(212, 212, 212),\n 'grey84': Color(214, 214, 214),\n 'grey85': Color(217, 217, 217),\n 'grey86': Color(219, 219, 219),\n 'grey87': Color(222, 222, 222),\n 'grey88': Color(224, 224, 224),\n 'grey89': Color(227, 227, 227),\n 'grey9': Color(23, 23, 23),\n 'grey90': Color(229, 229, 229),\n 'grey91': Color(232, 232, 232),\n 'grey92': Color(235, 235, 235),\n 'grey93': Color(237, 237, 237),\n 'grey94': Color(240, 240, 240),\n 'grey95': Color(242, 242, 242),\n 'grey96': Color(245, 245, 245),\n 'grey97': Color(247, 247, 247),\n 'grey98': Color(250, 250, 250),\n 'grey99': Color(252, 252, 252),\n 'honeydew': Color(240, 255, 240),\n 'honeydew1': Color(240, 255, 240),\n 'honeydew2': Color(224, 238, 224),\n 'honeydew3': Color(193, 205, 193),\n 'honeydew4': Color(131, 139, 131),\n 'hot pink': Color(255, 105, 180),\n 'hotpink': Color(255, 105, 180),\n 'hotpink1': Color(255, 110, 180),\n 'hotpink2': Color(238, 106, 167),\n 'hotpink3': Color(205, 96, 144),\n 'hotpink4': Color(139, 58, 98),\n 'indian red': Color(205, 92, 92),\n 'indianred': Color(205, 92, 92),\n 'indianred1': Color(255, 106, 106),\n 'indianred2': Color(238, 99, 99),\n 'indianred3': Color(205, 85, 85),\n 'indianred4': Color(139, 58, 58),\n 'ivory': Color(255, 255, 240),\n 'ivory1': Color(255, 255, 240),\n 'ivory2': Color(238, 238, 224),\n 'ivory3': Color(205, 205, 193),\n 'ivory4': Color(139, 139, 131),\n 'khaki': Color(240, 230, 140),\n 'khaki1': Color(255, 246, 143),\n 'khaki2': Color(238, 230, 133),\n 'khaki3': Color(205, 198, 115),\n 'khaki4': Color(139, 134, 78),\n 'lavender': Color(230, 230, 250),\n 'lavender blush': Color(255, 240, 245),\n 'lavenderblush': Color(255, 240, 245),\n 'lavenderblush1': Color(255, 240, 245),\n 'lavenderblush2': Color(238, 224, 229),\n 'lavenderblush3': Color(205, 193, 197),\n 'lavenderblush4': Color(139, 131, 134),\n 'lawn green': Color(124, 252, 0),\n 'lawngreen': Color(124, 252, 0),\n 'lemon chiffon': Color(255, 250, 205),\n 'lemonchiffon': Color(255, 250, 205),\n 'lemonchiffon1': Color(255, 250, 205),\n 'lemonchiffon2': Color(238, 233, 191),\n 'lemonchiffon3': Color(205, 201, 165),\n 'lemonchiffon4': Color(139, 137, 112),\n 'light blue': Color(173, 216, 230),\n 'light coral': Color(240, 128, 128),\n 'light cyan': Color(224, 255, 255),\n 'light goldenrod': Color(238, 221, 130),\n 'light goldenrod yellow': Color(250, 250, 210),\n 'light gray': Color(211, 211, 211),\n 'light green': Color(144, 238, 144),\n 'light grey': Color(211, 211, 211),\n 'light pink': Color(255, 182, 193),\n 'light salmon': Color(255, 160, 122),\n 'light sea green': Color(32, 178, 170),\n 'light sky blue': Color(135, 206, 250),\n 'light slate blue': Color(132, 112, 255),\n 'light slate gray': Color(119, 136, 153),\n 'light slate grey': Color(119, 136, 153),\n 'light steel blue': Color(176, 196, 222),\n 'light yellow': Color(255, 255, 224),\n 'lightblue': Color(173, 216, 230),\n 'lightblue1': Color(191, 239, 255),\n 'lightblue2': Color(178, 223, 238),\n 'lightblue3': Color(154, 192, 205),\n 'lightblue4': Color(104, 131, 139),\n 'lightcoral': Color(240, 128, 128),\n 'lightcyan': Color(224, 255, 255),\n 'lightcyan1': Color(224, 255, 255),\n 'lightcyan2': Color(209, 238, 238),\n 'lightcyan3': Color(180, 205, 205),\n 'lightcyan4': Color(122, 139, 139),\n 'lightgoldenrod': Color(238, 221, 130),\n 'lightgoldenrod1': Color(255, 236, 139),\n 'lightgoldenrod2': Color(238, 220, 130),\n 'lightgoldenrod3': Color(205, 190, 112),\n 'lightgoldenrod4': Color(139, 129, 76),\n 'lightgoldenrodyellow': Color(250, 250, 210),\n 'lightgray': Color(211, 211, 211),\n 'lightgreen': Color(144, 238, 144),\n 'lightgrey': Color(211, 211, 211),\n 'lightpink': Color(255, 182, 193),\n 'lightpink1': Color(255, 174, 185),\n 'lightpink2': Color(238, 162, 173),\n 'lightpink3': Color(205, 140, 149),\n 'lightpink4': Color(139, 95, 101),\n 'lightsalmon': Color(255, 160, 122),\n 'lightsalmon1': Color(255, 160, 122),\n 'lightsalmon2': Color(238, 149, 114),\n 'lightsalmon3': Color(205, 129, 98),\n 'lightsalmon4': Color(139, 87, 66),\n 'lightseagreen': Color(32, 178, 170),\n 'lightskyblue': Color(135, 206, 250),\n 'lightskyblue1': Color(176, 226, 255),\n 'lightskyblue2': Color(164, 211, 238),\n 'lightskyblue3': Color(141, 182, 205),\n 'lightskyblue4': Color(96, 123, 139),\n 'lightslateblue': Color(132, 112, 255),\n 'lightslategray': Color(119, 136, 153),\n 'lightslategrey': Color(119, 136, 153),\n 'lightsteelblue': Color(176, 196, 222),\n 'lightsteelblue1': Color(202, 225, 255),\n 'lightsteelblue2': Color(188, 210, 238),\n 'lightsteelblue3': Color(162, 181, 205),\n 'lightsteelblue4': Color(110, 123, 139),\n 'lightyellow': Color(255, 255, 224),\n 'lightyellow1': Color(255, 255, 224),\n 'lightyellow2': Color(238, 238, 209),\n 'lightyellow3': Color(205, 205, 180),\n 'lightyellow4': Color(139, 139, 122),\n 'lime green': Color(50, 205, 50),\n 'limegreen': Color(50, 205, 50),\n 'linen': Color(250, 240, 230),\n 'magenta': Color(255, 0, 255),\n 'magenta1': Color(255, 0, 255),\n 'magenta2': Color(238, 0, 238),\n 'magenta3': Color(205, 0, 205),\n 'magenta4': Color(139, 0, 139),\n 'maroon': Color(176, 48, 96),\n 'maroon1': Color(255, 52, 179),\n 'maroon2': Color(238, 48, 167),\n 'maroon3': Color(205, 41, 144),\n 'maroon4': Color(139, 28, 98),\n 'medium aquamarine': Color(102, 205, 170),\n 'medium blue': Color(0, 0, 205),\n 'medium orchid': Color(186, 85, 211),\n 'medium purple': Color(147, 112, 219),\n 'medium sea green': Color(60, 179, 113),\n 'medium slate blue': Color(123, 104, 238),\n 'medium spring green': Color(0, 250, 154),\n 'medium turquoise': Color(72, 209, 204),\n 'medium violet red': Color(199, 21, 133),\n 'mediumaquamarine': Color(102, 205, 170),\n 'mediumblue': Color(0, 0, 205),\n 'mediumorchid': Color(186, 85, 211),\n 'mediumorchid1': Color(224, 102, 255),\n 'mediumorchid2': Color(209, 95, 238),\n 'mediumorchid3': Color(180, 82, 205),\n 'mediumorchid4': Color(122, 55, 139),\n 'mediumpurple': Color(147, 112, 219),\n 'mediumpurple1': Color(171, 130, 255),\n 'mediumpurple2': Color(159, 121, 238),\n 'mediumpurple3': Color(137, 104, 205),\n 'mediumpurple4': Color(93, 71, 139),\n 'mediumseagreen': Color(60, 179, 113),\n 'mediumslateblue': Color(123, 104, 238),\n 'mediumspringgreen': Color(0, 250, 154),\n 'mediumturquoise': Color(72, 209, 204),\n 'mediumvioletred': Color(199, 21, 133),\n 'midnight blue': Color(25, 25, 112),\n 'midnightblue': Color(25, 25, 112),\n 'mint cream': Color(245, 255, 250),\n 'mintcream': Color(245, 255, 250),\n 'misty rose': Color(255, 228, 225),\n 'mistyrose': Color(255, 228, 225),\n 'mistyrose1': Color(255, 228, 225),\n 'mistyrose2': Color(238, 213, 210),\n 'mistyrose3': Color(205, 183, 181),\n 'mistyrose4': Color(139, 125, 123),\n 'moccasin': Color(255, 228, 181),\n 'navajo white': Color(255, 222, 173),\n 'navajowhite': Color(255, 222, 173),\n 'navajowhite1': Color(255, 222, 173),\n 'navajowhite2': Color(238, 207, 161),\n 'navajowhite3': Color(205, 179, 139),\n 'navajowhite4': Color(139, 121, 94),\n 'navy': Color(0, 0, 128),\n 'navy blue': Color(0, 0, 128),\n 'navyblue': Color(0, 0, 128),\n 'old lace': Color(253, 245, 230),\n 'oldlace': Color(253, 245, 230),\n 'olive drab': Color(107, 142, 35),\n 'olivedrab': Color(107, 142, 35),\n 'olivedrab1': Color(192, 255, 62),\n 'olivedrab2': Color(179, 238, 58),\n 'olivedrab3': Color(154, 205, 50),\n 'olivedrab4': Color(105, 139, 34),\n 'orange': Color(255, 165, 0),\n 'orange red': Color(255, 69, 0),\n 'orange1': Color(255, 165, 0),\n 'orange2': Color(238, 154, 0),\n 'orange3': Color(205, 133, 0),\n 'orange4': Color(139, 90, 0),\n 'orangered': Color(255, 69, 0),\n 'orangered1': Color(255, 69, 0),\n 'orangered2': Color(238, 64, 0),\n 'orangered3': Color(205, 55, 0),\n 'orangered4': Color(139, 37, 0),\n 'orchid': Color(218, 112, 214),\n 'orchid1': Color(255, 131, 250),\n 'orchid2': Color(238, 122, 233),\n 'orchid3': Color(205, 105, 201),\n 'orchid4': Color(139, 71, 137),\n 'pale goldenrod': Color(238, 232, 170),\n 'pale green': Color(152, 251, 152),\n 'pale turquoise': Color(175, 238, 238),\n 'pale violet red': Color(219, 112, 147),\n 'palegoldenrod': Color(238, 232, 170),\n 'palegreen': Color(152, 251, 152),\n 'palegreen1': Color(154, 255, 154),\n 'palegreen2': Color(144, 238, 144),\n 'palegreen3': Color(124, 205, 124),\n 'palegreen4': Color(84, 139, 84),\n 'paleturquoise': Color(175, 238, 238),\n 'paleturquoise1': Color(187, 255, 255),\n 'paleturquoise2': Color(174, 238, 238),\n 'paleturquoise3': Color(150, 205, 205),\n 'paleturquoise4': Color(102, 139, 139),\n 'palevioletred': Color(219, 112, 147),\n 'palevioletred1': Color(255, 130, 171),\n 'palevioletred2': Color(238, 121, 159),\n 'palevioletred3': Color(205, 104, 137),\n 'palevioletred4': Color(139, 71, 93),\n 'papaya whip': Color(255, 239, 213),\n 'papayawhip': Color(255, 239, 213),\n 'peach puff': Color(255, 218, 185),\n 'peachpuff': Color(255, 218, 185),\n 'peachpuff1': Color(255, 218, 185),\n 'peachpuff2': Color(238, 203, 173),\n 'peachpuff3': Color(205, 175, 149),\n 'peachpuff4': Color(139, 119, 101),\n 'peru': Color(205, 133, 63),\n 'pink': Color(255, 192, 203),\n 'pink1': Color(255, 181, 197),\n 'pink2': Color(238, 169, 184),\n 'pink3': Color(205, 145, 158),\n 'pink4': Color(139, 99, 108),\n 'plum': Color(221, 160, 221),\n 'plum1': Color(255, 187, 255),\n 'plum2': Color(238, 174, 238),\n 'plum3': Color(205, 150, 205),\n 'plum4': Color(139, 102, 139),\n 'powder blue': Color(176, 224, 230),\n 'powderblue': Color(176, 224, 230),\n 'purple': Color(160, 32, 240),\n 'purple1': Color(155, 48, 255),\n 'purple2': Color(145, 44, 238),\n 'purple3': Color(125, 38, 205),\n 'purple4': Color(85, 26, 139),\n 'red': Color(255, 0, 0),\n 'red1': Color(255, 0, 0),\n 'red2': Color(238, 0, 0),\n 'red3': Color(205, 0, 0),\n 'red4': Color(139, 0, 0),\n 'rosy brown': Color(188, 143, 143),\n 'rosybrown': Color(188, 143, 143),\n 'rosybrown1': Color(255, 193, 193),\n 'rosybrown2': Color(238, 180, 180),\n 'rosybrown3': Color(205, 155, 155),\n 'rosybrown4': Color(139, 105, 105),\n 'royal blue': Color(65, 105, 225),\n 'royalblue': Color(65, 105, 225),\n 'royalblue1': Color(72, 118, 255),\n 'royalblue2': Color(67, 110, 238),\n 'royalblue3': Color(58, 95, 205),\n 'royalblue4': Color(39, 64, 139),\n 'saddle brown': Color(139, 69, 19),\n 'saddlebrown': Color(139, 69, 19),\n 'salmon': Color(250, 128, 114),\n 'salmon1': Color(255, 140, 105),\n 'salmon2': Color(238, 130, 98),\n 'salmon3': Color(205, 112, 84),\n 'salmon4': Color(139, 76, 57),\n 'sandy brown': Color(244, 164, 96),\n 'sandybrown': Color(244, 164, 96),\n 'sea green': Color(46, 139, 87),\n 'seagreen': Color(46, 139, 87),\n 'seagreen1': Color(84, 255, 159),\n 'seagreen2': Color(78, 238, 148),\n 'seagreen3': Color(67, 205, 128),\n 'seagreen4': Color(46, 139, 87),\n 'seashell': Color(255, 245, 238),\n 'seashell1': Color(255, 245, 238),\n 'seashell2': Color(238, 229, 222),\n 'seashell3': Color(205, 197, 191),\n 'seashell4': Color(139, 134, 130),\n 'sienna': Color(160, 82, 45),\n 'sienna1': Color(255, 130, 71),\n 'sienna2': Color(238, 121, 66),\n 'sienna3': Color(205, 104, 57),\n 'sienna4': Color(139, 71, 38),\n 'sky blue': Color(135, 206, 235),\n 'skyblue': Color(135, 206, 235),\n 'skyblue1': Color(135, 206, 255),\n 'skyblue2': Color(126, 192, 238),\n 'skyblue3': Color(108, 166, 205),\n 'skyblue4': Color(74, 112, 139),\n 'slate blue': Color(106, 90, 205),\n 'slate gray': Color(112, 128, 144),\n 'slate grey': Color(112, 128, 144),\n 'slateblue': Color(106, 90, 205),\n 'slateblue1': Color(131, 111, 255),\n 'slateblue2': Color(122, 103, 238),\n 'slateblue3': Color(105, 89, 205),\n 'slateblue4': Color(71, 60, 139),\n 'slategray': Color(112, 128, 144),\n 'slategray1': Color(198, 226, 255),\n 'slategray2': Color(185, 211, 238),\n 'slategray3': Color(159, 182, 205),\n 'slategray4': Color(108, 123, 139),\n 'slategrey': Color(112, 128, 144),\n 'snow': Color(255, 250, 250),\n 'snow1': Color(255, 250, 250),\n 'snow2': Color(238, 233, 233),\n 'snow3': Color(205, 201, 201),\n 'snow4': Color(139, 137, 137),\n 'spring green': Color(0, 255, 127),\n 'springgreen': Color(0, 255, 127),\n 'springgreen1': Color(0, 255, 127),\n 'springgreen2': Color(0, 238, 118),\n 'springgreen3': Color(0, 205, 102),\n 'springgreen4': Color(0, 139, 69),\n 'steel blue': Color(70, 130, 180),\n 'steelblue': Color(70, 130, 180),\n 'steelblue1': Color(99, 184, 255),\n 'steelblue2': Color(92, 172, 238),\n 'steelblue3': Color(79, 148, 205),\n 'steelblue4': Color(54, 100, 139),\n 'tan': Color(210, 180, 140),\n 'tan1': Color(255, 165, 79),\n 'tan2': Color(238, 154, 73),\n 'tan3': Color(205, 133, 63),\n 'tan4': Color(139, 90, 43),\n 'thistle': Color(216, 191, 216),\n 'thistle1': Color(255, 225, 255),\n 'thistle2': Color(238, 210, 238),\n 'thistle3': Color(205, 181, 205),\n 'thistle4': Color(139, 123, 139),\n 'tomato': Color(255, 99, 71),\n 'tomato1': Color(255, 99, 71),\n 'tomato2': Color(238, 92, 66),\n 'tomato3': Color(205, 79, 57),\n 'tomato4': Color(139, 54, 38),\n 'turquoise': Color(64, 224, 208),\n 'turquoise1': Color(0, 245, 255),\n 'turquoise2': Color(0, 229, 238),\n 'turquoise3': Color(0, 197, 205),\n 'turquoise4': Color(0, 134, 139),\n 'violet': Color(238, 130, 238),\n 'violet red': Color(208, 32, 144),\n 'violetred': Color(208, 32, 144),\n 'violetred1': Color(255, 62, 150),\n 'violetred2': Color(238, 58, 140),\n 'violetred3': Color(205, 50, 120),\n 'violetred4': Color(139, 34, 82),\n 'wheat': Color(245, 222, 179),\n 'wheat1': Color(255, 231, 186),\n 'wheat2': Color(238, 216, 174),\n 'wheat3': Color(205, 186, 150),\n 'wheat4': Color(139, 126, 102),\n 'white': Color(255, 255, 255),\n 'white smoke': Color(245, 245, 245),\n 'whitesmoke': Color(245, 245, 245),\n 'yellow': Color(255, 255, 0),\n 'yellow green': Color(154, 205, 50),\n 'yellow1': Color(255, 255, 0),\n 'yellow2': Color(238, 238, 0),\n 'yellow3': Color(205, 205, 0),\n 'yellow4': Color(139, 139, 0),\n 'yellowgreen': Color(154, 205, 50)}\n# END_DATA_SECTION }}}\n\ndef main(args: list[str]=sys.argv) -> None:\n    with tempfile.TemporaryFile(suffix='.gperf', mode='w+') as tf:\n        print('struct Keyword { int name, value; };\\n%%', file=tf)\n        names = sorted(color_names)\n        for name in names:\n            print(f'{name}, {color_names[name]}', file=tf)\n        print('%%', file=tf)\n        tf.flush()\n        tf.seek(0)\n        with open('kitty/color-names.h', 'w') as header:\n            subprocess.check_call(\n                'gperf -m 2000 --struct-type --includes --readonly-tables --lookup-function-name in_color_name_set'\n                ' --global-table --null-strings --hash-function-name color_name_hash /dev/stdin'\n                ' --word-array-name color_names --pic --compare-strncmp'.split(), stdout=header, stdin=tf)\n\n\nif __name__ == '__main__':\n    import runpy\n    m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))\n    m['main']([sys.executable, 'color-names'])\n"
  },
  {
    "path": "gen/config.py",
    "content": "#!./kitty/launcher/kitty +launch\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport re\nimport subprocess\nimport sys\n\nfrom kitty.conf.generate import write_output\n\nif __name__ == '__main__' and not __package__:\n    import __main__\n    __main__.__package__ = 'gen'\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\n\ndef patch_color_list(path: str, colors: list[str], name: str, spc: str = '    ') -> None:\n    with open(path, 'r+') as f:\n        raw = f.read()\n        colors = sorted(colors)\n        if path.endswith('.go'):\n            spc = '\\t'\n            nraw = re.sub(\n                fr'(// {name}_COLORS_START).+?(\\s+// {name}_COLORS_END)',\n                r'\\1' + f'\\n{spc}' + f'\\n{spc}'.join(map(lambda x: f'\"{x}\":true,', colors)) + r'\\2',\n                raw, flags=re.DOTALL | re.MULTILINE)\n        else:\n            nraw = re.sub(\n                fr'(# {name}_COLORS_START).+?(\\s+# {name}_COLORS_END)',\n                r'\\1' + f'\\n{spc}' + f'\\n{spc}'.join(map(lambda x: f'{x!r},', colors)) + r'\\2',\n                raw, flags=re.DOTALL | re.MULTILINE)\n        if nraw != raw:\n            f.seek(0)\n            f.truncate()\n            f.write(nraw)\n            f.flush()\n            if path.endswith('.go'):\n                subprocess.check_call(['gofmt', '-w', path])\n\n\ndef main(args: list[str]=sys.argv) -> None:\n    from kitty.options.definition import definition\n    nullable_colors = []\n    all_colors = []\n    special_colors = []\n    for opt in definition.iter_all_options():\n        if callable(opt.parser_func):\n            match opt.parser_func.__name__:\n                case 'to_color':\n                    all_colors.append(opt.name)\n                case 'macos_titlebar_color' | 'titlebar_color' | 'scrollbar_color':\n                    all_colors.append(opt.name)\n                    special_colors.append(opt.name)\n                case 'to_color_or_none' | 'cursor_text_color':\n                    all_colors.append(opt.name)\n                    nullable_colors.append(opt.name)\n\n    patch_color_list('tools/cmd/at/set_colors.go', nullable_colors, 'NULLABLE')\n    patch_color_list('tools/themes/collection.go', all_colors, 'ALL')\n    nc = ',\\n    '.join(f'{x!r}' for x in nullable_colors)\n    sc = ',\\n    '.join(f'{x!r}' for x in special_colors)\n    write_output('kitty', definition,\n                 f'\\nnullable_colors = frozenset({{\\n    {nc}\\n}})\\n'\n                 f'\\nspecial_colors = frozenset({{\\n    {sc}\\n}})\\n'\n    )\n\n\nif __name__ == '__main__':\n    import runpy\n    m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))\n    m['main']([sys.executable, 'config'])\n"
  },
  {
    "path": "gen/cursors.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport subprocess\nimport sys\n\nif __name__ == '__main__' and not __package__:\n    import __main__\n    __main__.__package__ = 'gen'\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\nfrom .key_constants import patch_file\n\n# References for these names:\n# CSS:choices_for_{option.name} https://developer.mozilla.org/en-US/docs/Web/CSS/cursor\n# XCursor: https://tronche.com/gui/x/xlib/appendix/b/ + Absolute chaos\n# Wayland: https://wayland.app/protocols/cursor-shape-v1\n# Cocoa: https://developer.apple.com/documentation/appkit/nscursor + secret apple selectors + SDL_cocoamouse.m\n\n# kitty_names CSS_name       XCursor_names                             Wayland_name    Cocoa_name\ncursors = '''\\\narrow         default        default,!left_ptr                         default         arrowCursor\nbeam,text     text           text,!xterm,ibeam                         text            IBeamCursor\npointer,hand  pointer        pointing_hand,pointer,!hand2,hand         pointer         pointingHandCursor\nhelp          help           help,!question_arrow,whats_this           help            help:arrowCursor\nwait          wait           wait,!clock,watch                         wait            busybutclickable:arrowCursor\nprogress      progress       progress,half-busy,left_ptr_watch         progress        busybutclickable:arrowCursor\ncrosshair     crosshair      crosshair,!tcross                         crosshair       crosshairCursor\ncell          cell           cell,!plus,!cross                         cell            cell:crosshairCursor\nvertical-text vertical-text  vertical-text                             vertical-text   IBeamCursorForVerticalLayout\nmove          move           move,!fleur,pointer-move                  move            move:openHandCursor\n\ne-resize      e-resize       e-resize,!right_side                      e_resize        resizeRightCursor\nne-resize     ne-resize      ne-resize,!top_right_corner               ne_resize       resizenortheast:_windowResizeNorthEastSouthWestCursor\nnw-resize     nw-resize      nw-resize,!top_left_corner                nw_resize       resizenorthwest:_windowResizeNorthWestSouthEastCursor\nn-resize      n-resize       n-resize,!top_side                        n_resize        resizeUpCursor\nse-resize     se-resize      se-resize,!bottom_right_corner            se_resize       resizesoutheast:_windowResizeNorthWestSouthEastCursor\nsw-resize     sw-resize      sw-resize,!bottom_left_corner             sw_resize       resizesouthwest:_windowResizeNorthEastSouthWestCursor\ns-resize      s-resize       s-resize,!bottom_side                     s_resize        resizeDownCursor\nw-resize      w-resize       w-resize,!left_side                       w_resize        resizeLeftCursor\n\new-resize     ew-resize      ew-resize,!sb_h_double_arrow,split_h      ew_resize       resizeLeftRightCursor\nns-resize     ns-resize      ns-resize,!sb_v_double_arrow,split_v      ns_resize       resizeUpDownCursor\nnesw-resize   nesw-resize    nesw-resize,size_bdiag,size-bdiag         nesw_resize     _windowResizeNorthEastSouthWestCursor\nnwse-resize   nwse-resize    nwse-resize,size_fdiag,size-fdiag         nwse_resize     _windowResizeNorthWestSouthEastCursor\n\nzoom-in       zoom-in        zoom-in,zoom_in                           zoom_in         zoomin:arrowCursor\nzoom-out      zoom-out       zoom-out,zoom_out                         zoom_out        zoomout:arrowCursor\n\nalias         alias          dnd-link                                  alias           dragLinkCursor\ncopy          copy           dnd-copy                                  copy            dragCopyCursor\nnot-allowed   not-allowed    not-allowed,forbidden,crossed_circle      not_allowed     operationNotAllowedCursor\nno-drop       no-drop        no-drop,dnd-no-drop                       no_drop         operationNotAllowedCursor\ngrab          grab           grab,openhand,!hand1                      grab            openHandCursor\ngrabbing      grabbing       grabbing,closedhand,dnd-none              grabbing        closedHandCursor\n'''\n\n\ndef main(args: list[str]=sys.argv) -> None:\n    glfw_enum = []\n    css_names = []\n    glfw_xc_map = {}\n    glfw_xfont_map = []\n    kitty_to_enum_map = {}\n    enum_to_glfw_map = {}\n    enum_to_css_map = {}\n    glfw_cocoa_map = {}\n    glfw_css_map = {}\n    css_to_enum = {}\n    xc_to_enum = {}\n    glfw_wayland = {}\n    for line in cursors.splitlines():\n        line = line.strip()\n        if line:\n            names_, css, xc_, wayland, cocoa = line.split()\n            names, xc = names_.split(','), xc_.split(',')\n            base = css.replace('-', '_').upper()\n            glfw_name = 'GLFW_' + base + '_CURSOR'\n            enum_name = base + '_POINTER'\n            enum_to_glfw_map[enum_name] = glfw_name\n            enum_to_css_map[enum_name] = css\n            glfw_css_map[glfw_name] = css\n            css_to_enum[css] = enum_name\n            css_names.append(css)\n            glfw_wayland[glfw_name] = 'WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_' + wayland.replace('-', '_').upper()\n            for n in names:\n                kitty_to_enum_map[n] = enum_name\n            glfw_enum.append(glfw_name)\n            glfw_xc_map[glfw_name] = ', '.join(f'''\"{x.replace('!', '')}\"''' for x in xc)\n            for x in xc:\n                if x.startswith('!'):\n                    glfw_xfont_map.append(f\"case {glfw_name}: return set_cursor_from_font(cursor, {'XC_' + x[1:]});\")\n                    break\n            else:\n                items = tuple('\"' + x.replace('!', '') + '\"' for x in xc)\n                glfw_xfont_map.append(f'case {glfw_name}: return try_cursor_names(cursor, {len(items)}, {\", \".join(items)});')\n            for x in xc:\n                x = x.lstrip('!')\n                xc_to_enum[x] = enum_name\n            parts = cocoa.split(':', 1)\n            if len(parts) == 1:\n                if parts[0].startswith('_'):\n                    glfw_cocoa_map[glfw_name] = f'U({glfw_name}, {parts[0]});'\n                else:\n                    glfw_cocoa_map[glfw_name] = f'C({glfw_name}, {parts[0]});'\n            else:\n                glfw_cocoa_map[glfw_name] = f'S({glfw_name}, {parts[0]}, {parts[1]});'\n\n    for x, v in xc_to_enum.items():\n        if x not in css_to_enum:\n            css_to_enum[x] = v\n\n    glfw_enum.append('GLFW_INVALID_CURSOR')\n    patch_file('glfw/glfw3.h', 'mouse cursor shapes', '\\n'.join(f'    {x},' for x in glfw_enum))\n    patch_file('glfw/wl_window.c', 'glfw to wayland mapping', '\\n'.join(f'        C({g}, {x});' for g, x in glfw_wayland.items()))\n    patch_file('glfw/wl_window.c', 'glfw to xc mapping', '\\n'.join(f'        C({g}, {x});' for g, x in glfw_xc_map.items()))\n    patch_file('glfw/x11_window.c', 'glfw to xc mapping', '\\n'.join(f'        {x}' for x in glfw_xfont_map))\n    patch_file('kitty/data-types.h', 'mouse shapes', '\\n'.join(f'    {x},' for x in enum_to_glfw_map))\n    patch_file(\n        'kitty/options/utils.py', 'pointer shape names', '\\n'.join(f'    {x!r},' for x in kitty_to_enum_map),\n        start_marker='# ', end_marker='',\n    )\n    patch_file('kitty/options/to-c.h', 'pointer shapes', '\\n'.join(\n        f'    else if (strcmp(name, \"{k}\") == 0) return {v};' for k, v in kitty_to_enum_map.items()))\n    patch_file('kitty/glfw.c', 'enum to glfw', '\\n'.join(\n        f'        case {k}: set_glfw_mouse_cursor(w, {v}); break;' for k, v in enum_to_glfw_map.items()))\n    patch_file('kitty/glfw.c', 'name to glfw', '\\n'.join(\n        f'    if (strcmp(name, \"{k}\") == 0) return {enum_to_glfw_map[v]};' for k, v in kitty_to_enum_map.items()))\n    patch_file('kitty/glfw.c', 'glfw to css', '\\n'.join(\n        f'        case {g}: return \"{c}\";' for g, c in glfw_css_map.items()\n    ))\n    patch_file('kitty/screen.c', 'enum to css', '\\n'.join(\n        f'        case {e}: ans = \"{c}\"; break;' for e, c in enum_to_css_map.items()))\n    patch_file('kitty/screen.c', 'css to enum', '\\n'.join(\n        f'        else if (strcmp(\"{c}\", css_name) == 0) s = {e};' for c, e in css_to_enum.items()))\n    patch_file('glfw/cocoa_window.m', 'glfw to cocoa', '\\n'.join(f'        {x}' for x in glfw_cocoa_map.values()))\n    patch_file('docs/pointer-shapes.rst', 'list of shape css names', '\\n'.join(\n        f'#. {x}' if x else '' for x in [''] + sorted(css_names) + ['']), start_marker='.. ', end_marker='')\n    patch_file('tools/tui/loop/mouse.go', 'pointer shape enum', '\\n'.join(\n        f'\\t{x} PointerShape = {i}' for i, x in enumerate(enum_to_glfw_map)), start_marker='// ', end_marker='')\n    patch_file('tools/tui/loop/mouse.go', 'pointer shape tostring', '\\n'.join(\n        f'''\\tcase {x}: return \"{x.lower().rpartition('_')[0].replace('_', '-')}\"''' for x in enum_to_glfw_map), start_marker='// ', end_marker='')\n    patch_file('tools/cmd/mouse_demo/main.go', 'all pointer shapes', '\\n'.join(\n        f'\\tloop.{x},' for x in enum_to_glfw_map), start_marker='// ', end_marker='')\n\n    subprocess.check_call(['glfw/glfw.py'])\n\n\nif __name__ == '__main__':\n    import runpy\n    m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))\n    m['main']([sys.executable, 'cursors'])\n"
  },
  {
    "path": "gen/go_code.py",
    "content": "#!./kitty/launcher/kitty +launch\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport argparse\nimport bz2\nimport io\nimport json\nimport os\nimport re\nimport shlex\nimport struct\nimport subprocess\nimport sys\nimport tarfile\nfrom collections.abc import Iterator, Sequence\nfrom contextlib import contextmanager, suppress\nfrom functools import lru_cache\nfrom itertools import chain\nfrom typing import (\n    Any,\n    BinaryIO,\n    Optional,\n    TextIO,\n    Union,\n)\n\nimport kitty.constants as kc\nfrom kittens.tui.operations import Mode\nfrom kittens.tui.spinners import spinners\nfrom kitty.actions import get_all_actions\nfrom kitty.cli import (\n    GoOption,\n    go_options_for_seq,\n)\nfrom kitty.conf.generate import gen_go_code\nfrom kitty.conf.types import Definition\nfrom kitty.config import commented_out_default_config\nfrom kitty.fast_data_types import all_color_names\nfrom kitty.guess_mime_type import known_extensions, text_mimes\nfrom kitty.key_encoding import config_mod_map\nfrom kitty.key_names import character_key_name_aliases, functional_key_name_aliases\nfrom kitty.options.types import Options\nfrom kitty.rc.base import RemoteCommand, all_command_names, command_for_name\nfrom kitty.remote_control import global_options_spec\nfrom kitty.simple_cli_definitions import CompletionSpec, parse_option_spec, serialize_as_go_string\n\nif __name__ == '__main__' and not __package__:\n    import __main__\n    __main__.__package__ = 'gen'\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\n\nchanged: list[str] = []\n\n\ndef newer(dest: str, *sources: str) -> bool:\n    try:\n        dtime = os.path.getmtime(dest)\n    except OSError:\n        return True\n    for s in chain(sources, (__file__,)):\n        with suppress(FileNotFoundError):\n            if os.path.getmtime(s) >= dtime:\n                return True\n    return False\n\n\n\n# Utils {{{\n\ndef serialize_go_dict(x: Union[dict[str, int], dict[int, str], dict[int, int], dict[str, str]]) -> str:\n    ans = []\n\n    def s(x: Union[int, str]) -> str:\n        if isinstance(x, int):\n            return str(x)\n        return f'\"{serialize_as_go_string(x)}\"'\n\n    for k, v in x.items():\n        ans.append(f'{s(k)}: {s(v)}')\n    return '{' + ', '.join(ans) + '}'\n\n\ndef replace(template: str, **kw: str) -> str:\n    for k, v in kw.items():\n        template = template.replace(k, v)\n    return template\n# }}}\n\n# {{{  Stringer\n\n\n@lru_cache(maxsize=1)\ndef enum_parser() -> argparse.ArgumentParser:\n    p = argparse.ArgumentParser()\n    p.add_argument('--from-string-func-name')\n    return p\n\n\ndef stringify_file(path: str) -> None:\n    with open(path) as f:\n        src = f.read()\n    types = {}\n    constant_name_maps = {}\n    for m in re.finditer(r'^type +(\\S+) +\\S+ +// *enum *(.*?)$', src, re.MULTILINE):\n        args = m.group(2)\n        types[m.group(1)] = enum_parser().parse_args(args=shlex.split(args) if args else [])\n\n    def get_enum_def(src: str) -> None:\n        type_name = q = ''\n        constants = {}\n        for line in src.splitlines():\n            line = line.strip()\n            if not line:\n                continue\n            parts = line.split()\n            if not type_name:\n                if len(parts) < 2 or parts[1] not in types:\n                    return\n                type_name = parts[1]\n                q = type_name + '_'\n            constant_name = parts[0]\n            a, sep, b = line.partition('//')\n            if sep:\n                string_val = b.strip()\n            else:\n                string_val = constant_name\n                if constant_name.startswith(q):\n                    string_val = constant_name[len(q):]\n            constants[constant_name] = serialize_as_go_string(string_val)\n        if constants and type_name:\n            constant_name_maps[type_name] = constants\n\n    for m in re.finditer(r'^const +\\((.+?)^\\)', src, re.MULTILINE|re.DOTALL):\n        get_enum_def(m.group(1))\n\n    with replace_if_needed(path.replace('.go', '_stringer_generated.go')):\n        print('package', os.path.basename(os.path.dirname(path)))\n        print ('import \"fmt\"')\n        print ('import \"encoding/json\"')\n        print()\n        for type_name, constant_map in constant_name_maps.items():\n            print(f'func (self {type_name}) String() string ''{')\n            print('switch self {')\n            is_first = True\n            for constant_name, string_val in constant_map.items():\n                if is_first:\n                    print(f'default: return \"{string_val}\"')\n                    is_first = False\n                else:\n                    print(f'case {constant_name}: return \"{string_val}\"')\n            print('}}')\n            print(f'func (self {type_name}) MarshalJSON() ([]byte, error) {{ return json.Marshal(self.String()) }}')\n            fsname = types[type_name].from_string_func_name or (type_name + '_from_string')\n            print(f'func {fsname}(x string) (ans {type_name}, err error) ''{')\n            print('switch x {')\n            for constant_name, string_val in constant_map.items():\n                print(f'case \"{string_val}\": return {constant_name}, nil')\n            print('}')\n            print(f'err = fmt.Errorf(\"unknown value for enum {type_name}: %#v\", x)')\n            print('return')\n            print('}')\n            print(f'func (self *{type_name}) SetString(x string) error ''{')\n            print(f's, err := {fsname}(x); if err == nil {{ *self = s }}; return err''}')\n            print(f'func (self *{type_name}) UnmarshalJSON(data []byte) (err error)''{')\n            print('var x string')\n            print('if err = json.Unmarshal(data, &x); err != nil {return err}')\n            print('return self.SetString(x)}')\n\n\ndef stringify() -> None:\n    for path in (\n        'tools/tui/graphics/command.go',\n        'tools/rsync/algorithm.go',\n        'kittens/transfer/ftc.go',\n    ):\n        stringify_file(path)\n# }}}\n\n# {{{ Bitfields\n\ndef make_bitfields() -> None:\n    from kitty.fast_data_types import SCALE_BITS, SUBSCALE_BITS, WIDTH_BITS\n\n    from .bitfields import make_bitfield\n\n    def mb(*args: str) -> None:\n        output_path, ans = make_bitfield(*args)\n        with replace_if_needed(output_path) as buf:\n            print(ans, file=buf)\n\n    mb(\n        'tools/vt', 'CellAttrs',\n        'decoration 3', 'bold 1', 'italic 1', 'reverse 1', 'strike 1', 'dim 1', 'hyperlink_id 16',\n    )\n    mb('tools/vt', 'Ch', 'is_idx 1', 'ch_or_idx 31')\n    mb(\n        'tools/vt', 'MultiCell',\n        'is_multicell 1', 'natural_width 1', f'scale {SCALE_BITS}', f'subscale_n {SUBSCALE_BITS}', f'subscale_d {SUBSCALE_BITS}',\n        f'width {WIDTH_BITS}', f'x {WIDTH_BITS + SCALE_BITS + 1}', f'y {SCALE_BITS + 1}', 'vertical_align 3',\n    )\n    mb('tools/vt', 'CellColor', 'is_idx 1', 'red 8', 'green 8', 'blue 8')\n    mb('tools/vt', 'LineAttrs', 'prompt_kind 2',)\n    mb('kittens/choose_files', 'CombinedScore', 'score 16', 'length 16', 'index 32')\n# }}}\n\n# Completions {{{\n\n@lru_cache\ndef kitten_cli_docs(kitten: str) -> Any:\n    from kittens.runner import get_kitten_cli_docs\n    return get_kitten_cli_docs(kitten)\n\n\n@lru_cache\ndef go_options_for_kitten(kitten: str) -> tuple[Sequence[GoOption], Optional[CompletionSpec]]:\n    kcd = kitten_cli_docs(kitten)\n    if kcd:\n        ospec = kcd['options']\n        return (tuple(go_options_for_seq(parse_option_spec(ospec())[0])), kcd.get('args_completion'))\n    return (), None\n\n\ndef generate_kittens_completion() -> None:\n    from kittens.runner import all_kitten_names, get_kitten_wrapper_of\n    for kitten in sorted(all_kitten_names()):\n        kn = 'kitten_' + kitten\n        print(f'{kn} := plus_kitten.AddSubCommand(&cli.Command{{Name:\"{kitten}\", Group: \"Kittens\"}})')\n        wof = get_kitten_wrapper_of(kitten)\n        if wof:\n            print(f'{kn}.ArgCompleter = cli.CompletionForWrapper(\"{serialize_as_go_string(wof)}\")')\n            print(f'{kn}.OnlyArgsAllowed = true')\n            continue\n        gopts, ac = go_options_for_kitten(kitten)\n        if gopts or ac:\n            for opt in gopts:\n                print(opt.as_option(kn))\n            if ac is not None:\n                print(''.join(ac.as_go_code(kn + '.ArgCompleter', ' = ')))\n        else:\n            print(f'{kn}.HelpText = \"\"')\n\n\n@lru_cache\ndef clone_safe_launch_opts() -> Sequence[GoOption]:\n    from kitty.launch import clone_safe_opts, options_spec\n    ans = []\n    allowed = clone_safe_opts()\n    for o in go_options_for_seq(parse_option_spec(options_spec())[0]):\n        if o.obj_defn.name in allowed:\n            ans.append(o)\n    return tuple(ans)\n\n\ndef completion_for_launch_wrappers(*names: str) -> None:\n    for o in clone_safe_launch_opts():\n        for name in names:\n            print(o.as_option(name))\n\n\ndef generate_completions_for_kitty() -> None:\n    print('package completion\\n')\n    print('import \"github.com/kovidgoyal/kitty/tools/cli\"')\n    print('import \"github.com/kovidgoyal/kitty/tools/cmd/tool\"')\n    print('import \"github.com/kovidgoyal/kitty/tools/cmd/at\"')\n\n    print('func kitty(root *cli.Command) {')\n\n    # The kitty exe\n    print('k := root.AddSubCommand(&cli.Command{'\n          'Name:\"kitty\", SubCommandIsOptional: true, ArgCompleter: cli.CompleteExecutableFirstArg, SubCommandMustBeFirst: true })')\n    print('kt := root.AddSubCommand(&cli.Command{Name:\"kitten\", SubCommandMustBeFirst: true })')\n    print('tool.KittyToolEntryPoints(kt)')\n    for opt in go_options_for_seq(parse_option_spec()[0]):\n        print(opt.as_option('k'))\n\n    # kitty +\n    print('plus := k.AddSubCommand(&cli.Command{Name:\"+\", Group:\"Entry points\", ShortDescription: \"Various special purpose tools and kittens\"})')\n\n    # kitty +launch\n    print('plus_launch := plus.AddSubCommand(&cli.Command{'\n          'Name:\"launch\", Group:\"Entry points\", ShortDescription: \"Launch Python scripts\", ArgCompleter: complete_plus_launch})')\n    print('k.AddClone(\"\", plus_launch).Name = \"+launch\"')\n\n    # kitty +list-fonts\n    print('plus_list_fonts := plus.AddSubCommand(&cli.Command{'\n          'Name:\"list-fonts\", Group:\"Entry points\", ShortDescription: \"List all available monospaced fonts\"})')\n    print('k.AddClone(\"\", plus_list_fonts).Name = \"+list-fonts\"')\n\n    # kitty +runpy\n    print('plus_runpy := plus.AddSubCommand(&cli.Command{'\n          'Name: \"runpy\", Group:\"Entry points\", ArgCompleter: complete_plus_runpy, ShortDescription: \"Run Python code\"})')\n    print('k.AddClone(\"\", plus_runpy).Name = \"+runpy\"')\n\n    # kitty +open\n    print('plus_open := plus.AddSubCommand(&cli.Command{'\n          'Name:\"open\", Group:\"Entry points\", ArgCompleter: complete_plus_open, ShortDescription: \"Open files and URLs\"})')\n    print('for _, og := range k.OptionGroups { plus_open.OptionGroups = append(plus_open.OptionGroups, og.Clone(plus_open)) }')\n    print('k.AddClone(\"\", plus_open).Name = \"+open\"')\n\n    # kitty +kitten\n    print('plus_kitten := plus.AddSubCommand(&cli.Command{Name:\"kitten\", Group:\"Kittens\", SubCommandMustBeFirst: true})')\n    generate_kittens_completion()\n    print('k.AddClone(\"\", plus_kitten).Name = \"+kitten\"')\n\n    # @\n    print('at.EntryPoint(k)')\n\n    # clone-in-kitty, edit-in-kitty\n    print('cik := root.AddSubCommand(&cli.Command{Name:\"clone-in-kitty\"})')\n    completion_for_launch_wrappers('cik')\n\n    print('}')\n    print('func init() {')\n    print('cli.RegisterExeForCompletion(kitty)')\n    print('}')\n# }}}\n\n\n# rc command wrappers {{{\njson_field_types: dict[str, str] = {\n    'bool': 'bool', 'str': 'escaped_string', 'list.str': '[]escaped_string', 'dict.str': 'map[escaped_string]escaped_string', 'float': 'float64', 'int': 'int',\n    'scroll_amount': 'any', 'spacing': 'any', 'colors': 'any',\n}\n\n\ndef go_field_type(json_field_type: str) -> str:\n    json_field_type = json_field_type.partition('=')[0]\n    q = json_field_types.get(json_field_type)\n    if q:\n        return q\n    if json_field_type.startswith('choices.'):\n        return 'string'\n    if '.' in json_field_type:\n        p, r = json_field_type.split('.', 1)\n        p = {'list': '[]', 'dict': 'map[string]'}[p]\n        return p + go_field_type(r)\n    raise TypeError(f'Unknown JSON field type: {json_field_type}')\n\n\nclass JSONField:\n\n    def __init__(self, line: str, field_to_option_map: dict[str, str], option_map: dict[str, GoOption]) -> None:\n        field_def = line.split(':', 1)[0]\n        self.required = False\n        self.field, self.field_type = field_def.split('/', 1)\n        self.go_option_name = field_to_option_map.get(self.field, self.field)\n        self.go_option_name = ''.join(x.capitalize() for x in self.go_option_name.split('_'))\n        self.omitempty = True\n        if fo := option_map.get(self.go_option_name):\n            if fo.type in ('int', 'float') and float(fo.default or 0) != 0:\n                self.omitempty = False\n        self.field_type, self.special_parser = self.field_type.partition('=')[::2]\n        if self.field.endswith('+'):\n            self.required = True\n            self.field = self.field[:-1]\n        self.struct_field_name = self.field[0].upper() + self.field[1:]\n\n    def go_declaration(self) -> str:\n        omitempty = ',omitempty' if self.omitempty else ''\n        return self.struct_field_name + ' ' + go_field_type(self.field_type) + f'`json:\"{self.field}{omitempty}\"`'\n\n\ndef go_code_for_remote_command(name: str, cmd: RemoteCommand, template: str) -> str:\n    template = '\\n' + template[len('//go:build exclude'):]\n    af: list[str] = []\n    a = af.append\n    af.extend(cmd.args.as_go_completion_code('ans'))\n    od: list[str] = []\n    option_map: dict[str, GoOption] = {}\n    for o in rc_command_options(name):\n        option_map[o.go_var_name] = o\n        a(o.as_option('ans'))\n        if o.go_var_name in ('NoResponse', 'ResponseTimeout'):\n            continue\n        od.append(o.struct_declaration())\n    jd: list[str] = []\n    json_fields = []\n    field_types: dict[str, str] = {}\n    for line in cmd.protocol_spec.splitlines():\n        line = line.strip()\n        if ':' not in line:\n            continue\n        f = JSONField(line, cmd.field_to_option_map or {}, option_map)\n        json_fields.append(f)\n        field_types[f.field] = f.field_type\n        jd.append(f.go_declaration())\n    jc: list[str] = []\n    handled_fields: set[str] = set()\n    jc.extend(cmd.args.as_go_code(name, field_types, handled_fields))\n\n    unhandled = {}\n    used_options = set()\n    for field in json_fields:\n        if field.go_option_name in option_map:\n            o = option_map[field.go_option_name]\n            used_options.add(field.go_option_name)\n            optstring = f'options_{name}.{o.go_var_name}'\n            if field.special_parser:\n                optstring = f'{field.special_parser}({optstring})'\n            if field.field_type == 'str':\n                jc.append(f'payload.{field.struct_field_name} = escaped_string({optstring})')\n            elif field.field_type == 'list.str':\n                jc.append(f'payload.{field.struct_field_name} = escape_list_of_strings({optstring})')\n            elif field.field_type == 'dict.str':\n                jc.append(f'payload.{field.struct_field_name} = escape_dict_of_strings({optstring})')\n            else:\n                jc.append(f'payload.{field.struct_field_name} = {optstring}')\n        elif field.field in handled_fields:\n            pass\n        else:\n            unhandled[field.field] = field\n    for x in tuple(unhandled):\n        if x == 'match_window' and 'Match' in option_map and 'Match' not in used_options:\n            used_options.add('Match')\n            o = option_map['Match']\n            field = unhandled[x]\n            if field.field_type == 'str':\n                jc.append(f'payload.{field.struct_field_name} = escaped_string(options_{name}.{o.go_var_name})')\n            else:\n                jc.append(f'payload.{field.struct_field_name} = options_{name}.{o.go_var_name}')\n            del unhandled[x]\n    if unhandled:\n        raise SystemExit(f'Cant map fields: {\", \".join(unhandled)} for cmd: {name}')\n    if name != 'send_text':\n        unused_options = set(option_map) - used_options - {'NoResponse', 'ResponseTimeout'}\n        if unused_options:\n            raise SystemExit(f'Unused options: {\", \".join(unused_options)} for command: {name}')\n\n    argspec = cmd.args.spec\n    if argspec:\n        argspec = ' ' + argspec\n    NO_RESPONSE = 'true' if cmd.disallow_responses else 'false'\n    ans = replace(\n        template,\n        CMD_NAME=name, __FILE__=__file__, CLI_NAME=name.replace('_', '-'),\n        SHORT_DESC=serialize_as_go_string(cmd.short_desc),\n        LONG_DESC=serialize_as_go_string(cmd.desc.strip()),\n        IS_ASYNC='true' if cmd.is_asynchronous else 'false',\n        NO_RESPONSE_BASE=NO_RESPONSE, ADD_FLAGS_CODE='\\n'.join(af),\n        WAIT_TIMEOUT=str(cmd.response_timeout),\n        OPTIONS_DECLARATION_CODE='\\n'.join(od),\n        JSON_DECLARATION_CODE='\\n'.join(jd),\n        JSON_INIT_CODE='\\n'.join(jc), ARGSPEC=argspec,\n        STRING_RESPONSE_IS_ERROR='true' if cmd.string_return_is_error else 'false',\n        STREAM_WANTED='true' if cmd.reads_streaming_data else 'false',\n    )\n    return ans\n# }}}\n\n\n# kittens {{{\n\n\ndef generate_conf_parser(kitten: str, defn: Definition) -> None:\n    with replace_if_needed(f'kittens/{kitten}/conf_generated.go'):\n        print(f'package {kitten}')\n        print(gen_go_code(defn))\n\n\ndef generate_extra_cli_parser(name: str, spec: str) -> None:\n    print('import \"github.com/kovidgoyal/kitty/tools/cli\"')\n    go_opts = tuple(go_options_for_seq(parse_option_spec(spec)[0]))\n    print(f'type {name}_options struct ''{')\n    for opt in go_opts:\n        print(opt.struct_declaration())\n    print('}')\n    print(f'func parse_{name}_args(args []string) (*{name}_options, []string, error) ''{')\n    print(f'root := cli.Command{{Name: `{name}` }}')\n    for opt in go_opts:\n        print(opt.as_option('root'))\n    print('cmd, err := root.ParseArgs(args)')\n    print('if err != nil { return nil, nil, err }')\n    print(f'var opts {name}_options')\n    print('err = cmd.GetOptionValues(&opts)')\n    print('if err != nil { return nil, nil, err }')\n    print('return &opts, cmd.Args, nil')\n    print('}')\n\n\ndef kittens_needing_cli_parsers() -> Iterator[str]:\n    for d in os.scandir('kittens'):\n        if not d.is_dir(follow_symlinks=False):\n            continue\n        if os.path.exists(os.path.join(d.path, 'main.py')) and os.path.exists(os.path.join(d.path, 'main.go')):\n            with open(os.path.join(d.path, 'main.py')) as f:\n                raw = f.read()\n            if 'options' in raw:\n                yield d.name\n\n\ndef kitten_clis() -> None:\n    from kittens.runner import get_kitten_conf_docs, get_kitten_extra_cli_parsers\n    for kitten in kittens_needing_cli_parsers():\n        defn = get_kitten_conf_docs(kitten)\n        if defn is not None:\n            generate_conf_parser(kitten, defn)\n        ecp = get_kitten_extra_cli_parsers(kitten)\n        if ecp:\n            for name, spec in ecp.items():\n                with replace_if_needed(f'kittens/{kitten}/{name}_cli_generated.go'):\n                    print(f'package {kitten}')\n                    generate_extra_cli_parser(name, spec)\n\n        with replace_if_needed(f'kittens/{kitten}/cli_generated.go'):\n            od = []\n            ser = []\n            kcd = kitten_cli_docs(kitten)\n            has_underscore = '_' in kitten\n            print(f'package {kitten}')\n            print('import \"fmt\"')\n            print('import \"github.com/kovidgoyal/kitty/tools/cli\"')\n            print('var _ = fmt.Sprintf')\n            print('func create_cmd(root *cli.Command, run_func func(*cli.Command, *Options, []string)(int, error)) {')\n            print('ans := root.AddSubCommand(&cli.Command{')\n            print(f'Name: \"{kitten}\",')\n            if kcd:\n                print(f'ShortDescription: \"{serialize_as_go_string(kcd[\"short_desc\"])}\",')\n                if kcd['usage']:\n                    print(f'Usage: \"[options] {serialize_as_go_string(kcd[\"usage\"])}\",')\n                print(f'HelpText: \"{serialize_as_go_string(kcd[\"help_text\"])}\",')\n            print('Run: func(cmd *cli.Command, args []string) (int, error) {')\n            print('opts := Options{}')\n            print('err := cmd.GetOptionValues(&opts)')\n            print('if err != nil { return 1, err }')\n            print('return run_func(cmd, &opts, args)},')\n            if has_underscore:\n                print('Hidden: true,')\n            print('})')\n            gopts, ac = go_options_for_kitten(kitten)\n            for opt in gopts:\n                print(opt.as_option('ans'))\n                od.append(opt.struct_declaration())\n                ser.append('\\n'.join(opt.as_string_for_commandline()))\n            if ac is not None:\n                print(''.join(ac.as_go_code('ans.ArgCompleter', ' = ')))\n            if not kcd:\n                print('specialize_command(ans)')\n            if has_underscore:\n                print(\"clone := root.AddClone(ans.Group, ans)\")\n                print('clone.Hidden = false')\n                print(f'clone.Name = \"{serialize_as_go_string(kitten.replace(\"_\", \"-\"))}\"')\n            print('}')\n            print('type Options struct {')\n            print('\\n'.join(od))\n            print('}')\n            print('func (opts Options) AsCommandLine() (ans []string) {')\n            if ser:\n                print('\\t sval := \"\"')\n                print('\\t _ = sval')\n                for x in ser:\n                    print('\\t' + x)\n            print('return')\n            print('}')\n\n# }}}\n\n\n# Constants {{{\n\ndef generate_spinners() -> str:\n    ans = ['package tui', 'import \"time\"', 'func NewSpinner(name string) *Spinner {', 'var ans *Spinner', 'switch name {']\n    a = ans.append\n    for name, spinner in spinners.items():\n        a(f'case \"{serialize_as_go_string(name)}\":')\n        a('ans = &Spinner{')\n        a(f'Name: \"{serialize_as_go_string(name)}\",')\n        a(f'interval: {spinner[\"interval\"]},')\n        frames = ', '.join(f'\"{serialize_as_go_string(x)}\"' for x in spinner['frames'])\n        a(f'frames: []string{{{frames}}},')\n        a('}')\n    a('}')\n    a('if ans != nil {')\n    a('ans.interval *= time.Millisecond')\n    a('ans.current_frame = -1')\n    a('ans.last_change_at = time.Now().Add(-ans.interval)')\n    a('}')\n    a('return ans}')\n    return '\\n'.join(ans)\n\n\ndef generate_color_names() -> str:\n    selfg = \"\" if Options.selection_foreground is None else Options.selection_foreground.as_sharp\n    selbg = \"\" if Options.selection_background is None else Options.selection_background.as_sharp\n    cursor = \"\" if Options.cursor is None else Options.cursor.as_sharp\n    return 'package style\\n\\nvar ColorNames = map[string]RGBA{' + '\\n'.join(\n        f'\\t\"{name}\": RGBA{{ Red:{val.red}, Green:{val.green}, Blue:{val.blue} }},'\n        for name, val in all_color_names()\n    ) + '\\n}' + '\\n\\nvar ColorTable = [256]uint32{' + ', '.join(\n        f'{x}' for x in Options.color_table) + '}\\n' + f'''\nvar DefaultColors = struct {{\nForeground, Background, Cursor, SelectionFg, SelectionBg string\n}}{{\nForeground: \"{Options.foreground.as_sharp}\",\nBackground: \"{Options.background.as_sharp}\",\nCursor: \"{cursor}\",\nSelectionFg: \"{selfg}\",\nSelectionBg: \"{selbg}\",\n}}\n'''\n\n\ndef load_ref_map() -> dict[str, dict[str, str]]:\n    with open('kitty/docs_ref_map_generated.h') as f:\n        raw = f.read()\n    raw = raw.split('{', 1)[1].split('}', 1)[0]\n    data = json.loads(bytes(bytearray(json.loads(f'[{raw}]'))))\n    return data  # type: ignore\n\n\ndef generate_constants() -> str:\n    from kittens.hints.main import DEFAULT_REGEX\n    from kittens.query_terminal.main import all_queries\n    from kitty.colors import ThemeFile\n    from kitty.config import option_names_for_completion\n    from kitty.fast_data_types import FILE_TRANSFER_CODE\n    from kitty.options.utils import allowed_shell_integration_values, url_style_map\n    from kitty.simple_cli_definitions import CONFIG_HELP\n    del sys.modules['kittens.hints.main']\n    del sys.modules['kittens.query_terminal.main']\n    ref_map = load_ref_map()\n    with open('kitty/data-types.h') as dt:\n        m = re.search(r'^#define IMAGE_PLACEHOLDER_CHAR (\\S+)', dt.read(), flags=re.M)\n        assert m is not None\n        placeholder_char = int(m.group(1), 16)\n    dp = \", \".join(map(lambda x: f'\"{serialize_as_go_string(x)}\"', kc.default_pager_for_help))\n    url_prefixes = ','.join(f'\"{x}\"' for x in Options.url_prefixes)\n    option_names = '`' + '\\n'.join(option_names_for_completion()) + '`'\n    url_style = {v:k for k, v in url_style_map.items()}[Options.url_style]\n    query_names = ', '.join(f'\"{name}\"' for name in all_queries)\n    return f'''\\\npackage kitty\n\ntype VersionType struct {{\n    Major, Minor, Patch int\n}}\nconst VersionString string = \"{kc.str_version}\"\nconst WebsiteBaseURL string = \"{kc.website_base_url}\"\nconst FileTransferCode int = {FILE_TRANSFER_CODE}\nconst ImagePlaceholderChar rune = {placeholder_char}\nconst SSHControlMasterTemplate = \"{kc.ssh_control_master_template}\"\nconst RC_ENCRYPTION_PROTOCOL_VERSION string = \"{kc.RC_ENCRYPTION_PROTOCOL_VERSION}\"\nvar VCSRevision string = \"\"\nvar IsFrozenBuild string = \"\"\nvar IsStandaloneBuild string = \"\"\nconst HandleTermiosSignals = {Mode.HANDLE_TERMIOS_SIGNALS.value[0]}\nconst HintsDefaultRegex = `{DEFAULT_REGEX}`\nconst DefaultTermName = `{Options.term}`\nconst DefaultUrlStyle = `{url_style}`\nconst DefaultUrlColor = `{Options.url_color.as_sharp}`\nconst ConfigHelp = \"{serialize_as_go_string(CONFIG_HELP)}\"\nvar Version VersionType = VersionType{{Major: {kc.version.major}, Minor: {kc.version.minor}, Patch: {kc.version.patch},}}\nvar DefaultPager []string = []string{{ {dp} }}\nvar FunctionalKeyNameAliases = map[string]string{serialize_go_dict(functional_key_name_aliases)}\nvar CharacterKeyNameAliases = map[string]string{serialize_go_dict(character_key_name_aliases)}\nvar ConfigModMap = map[string]uint16{serialize_go_dict(config_mod_map)}\nvar RefMap = map[string]string{serialize_go_dict(ref_map['ref'])}\nvar DocTitleMap = map[string]string{serialize_go_dict(ref_map['doc'])}\nvar AllowedShellIntegrationValues = []string{{ {str(sorted(allowed_shell_integration_values))[1:-1].replace(\"'\", '\"')} }}\nvar QueryNames = []string{{ {query_names} }}\nvar CommentedOutDefaultConfig = \"{serialize_as_go_string(commented_out_default_config())}\"\nvar KittyConfigDefaults = struct {{\nTerm, Shell_integration, Select_by_word_characters, Url_excluded_characters, Shell string\nWheel_scroll_multiplier float64\nUrl_prefixes []string\n}}{{\nTerm: \"{Options.term}\", Shell_integration: \"{' '.join(Options.shell_integration)}\", Url_prefixes: []string{{ {url_prefixes} }},\nSelect_by_word_characters: `{Options.select_by_word_characters}`, Wheel_scroll_multiplier: {Options.wheel_scroll_multiplier},\nShell: \"{Options.shell}\", Url_excluded_characters: \"{Options.url_excluded_characters}\",\n}}\nconst OptionNames = {option_names}\nconst DarkThemeFileName = \"{ThemeFile.dark.value}\"\nconst LightThemeFileName = \"{ThemeFile.light.value}\"\nconst NoPreferenceThemeFileName = \"{ThemeFile.no_preference.value}\"\n'''  # }}}\n\n\n# Boilerplate {{{\n\n@contextmanager\ndef replace_if_needed(path: str, show_diff: bool = False) -> Iterator[io.StringIO]:\n    buf = io.StringIO()\n    origb = sys.stdout\n    sys.stdout = buf\n    try:\n        yield buf\n    finally:\n        sys.stdout = origb\n    orig = ''\n    with suppress(FileNotFoundError), open(path) as f:\n        orig = f.read()\n    new = buf.getvalue()\n    new = f'// Code generated by {os.path.basename(__file__)}; DO NOT EDIT.\\n\\n' + new\n    if orig != new:\n        changed.append(path)\n        if show_diff:\n            with open(path + '.new', 'w') as f:\n                f.write(new)\n                subprocess.run(['diff', '-Naurp', path, f.name], stdout=open('/dev/tty', 'w'))\n                os.remove(f.name)\n        with open(path, 'w') as f:\n            f.write(new)\n\n\n@lru_cache(maxsize=256)\ndef rc_command_options(name: str) -> tuple[GoOption, ...]:\n    cmd = command_for_name(name)\n    return tuple(go_options_for_seq(parse_option_spec(cmd.options_spec or '\\n\\n')[0]))\n\n\ndef update_at_commands() -> None:\n    with open('tools/cmd/at/template.go') as f:\n        template = f.read()\n    for name in all_command_names():\n        cmd = command_for_name(name)\n        code = go_code_for_remote_command(name, cmd, template)\n        dest = f'tools/cmd/at/cmd_{name}_generated.go'\n        with replace_if_needed(dest) as f:\n            f.write(code)\n    struct_def = []\n    opt_def = []\n    for o in go_options_for_seq(parse_option_spec(global_options_spec())[0]):\n        struct_def.append(o.struct_declaration())\n        opt_def.append(o.as_option(depth=1, group=\"Global options\"))\n    sdef = '\\n'.join(struct_def)\n    odef = '\\n'.join(opt_def)\n    code = f'''\npackage at\nimport \"github.com/kovidgoyal/kitty/tools/cli\"\ntype rc_global_options struct {{\n{sdef}\n}}\nvar rc_global_opts rc_global_options\n\nfunc add_rc_global_opts(cmd *cli.Command) {{\n{odef}\n}}\n'''\n    with replace_if_needed('tools/cmd/at/global_opts_generated.go') as f:\n        f.write(code)\n\n\ndef update_completion() -> None:\n    with replace_if_needed('tools/cmd/completion/kitty_generated.go'):\n        generate_completions_for_kitty()\n\n    with replace_if_needed('tools/cmd/at/kitty_actions_generated.go'):\n        print(\"package at\")\n        print(\"const KittyActionNames = `\", end='')\n        for grp, actions in get_all_actions().items():\n            for ac in actions:\n                print(ac.name)\n        print('`')\n\n    with replace_if_needed('tools/cmd/edit_in_kitty/launch_generated.go'):\n        print('package edit_in_kitty')\n        print('import \"github.com/kovidgoyal/kitty/tools/cli\"')\n        print('func AddCloneSafeOpts(cmd *cli.Command) {')\n        completion_for_launch_wrappers('cmd')\n        print(''.join(CompletionSpec.from_string('type:file mime:text/* group:\"Text files\"').as_go_code('cmd.ArgCompleter', ' = ')))\n        print('}')\n\n\ndef define_enum(package_name: str, type_name: str, items: str, underlying_type: str = 'uint') -> str:\n    actions = []\n    for x in items.splitlines():\n        x = x.strip()\n        if x:\n            actions.append(x)\n    ans = [f'package {package_name}', 'import \"strconv\"', f'type {type_name} {underlying_type}', 'const (']\n    stringer = [f'func (ac {type_name}) String() string ''{', 'switch(ac) {']\n    for i, ac in enumerate(actions):\n        stringer.append(f'case {ac}: return \"{ac}\"')\n        if i == 0:\n            ac = ac + f' {type_name} = iota'\n        ans.append(ac)\n    ans.append(')')\n    stringer.append('}\\nreturn strconv.Itoa(int(ac)) }')\n    return '\\n'.join(ans + stringer)\n\n\ndef generate_readline_actions() -> str:\n    return define_enum('readline', 'Action', '''\\\n        ActionNil\n\n        ActionBackspace\n        ActionDelete\n        ActionMoveToStartOfLine\n        ActionMoveToEndOfLine\n        ActionMoveToStartOfDocument\n        ActionMoveToEndOfDocument\n        ActionMoveToEndOfWord\n        ActionMoveToStartOfWord\n        ActionCursorLeft\n        ActionCursorRight\n        ActionEndInput\n        ActionAcceptInput\n        ActionCursorUp\n        ActionHistoryPreviousOrCursorUp\n        ActionCursorDown\n        ActionHistoryNextOrCursorDown\n        ActionHistoryNext\n        ActionHistoryPrevious\n        ActionHistoryFirst\n        ActionHistoryLast\n        ActionHistoryIncrementalSearchBackwards\n        ActionHistoryIncrementalSearchForwards\n        ActionTerminateHistorySearchAndApply\n        ActionTerminateHistorySearchAndRestore\n        ActionClearScreen\n        ActionAddText\n        ActionAbortCurrentLine\n\n        ActionStartKillActions\n        ActionKillToEndOfLine\n        ActionKillToStartOfLine\n        ActionKillNextWord\n        ActionKillPreviousWord\n        ActionKillPreviousSpaceDelimitedWord\n        ActionEndKillActions\n        ActionYank\n        ActionPopYank\n\n        ActionNumericArgumentDigit0\n        ActionNumericArgumentDigit1\n        ActionNumericArgumentDigit2\n        ActionNumericArgumentDigit3\n        ActionNumericArgumentDigit4\n        ActionNumericArgumentDigit5\n        ActionNumericArgumentDigit6\n        ActionNumericArgumentDigit7\n        ActionNumericArgumentDigit8\n        ActionNumericArgumentDigit9\n        ActionNumericArgumentDigitMinus\n\n        ActionCompleteForward\n        ActionCompleteBackward\n    ''')\n\n\ndef generate_mimetypes() -> str:\n    import mimetypes\n    if not mimetypes.inited:\n        mimetypes.init()\n    ans = ['package utils', 'import \"sync\"', 'var only_once sync.Once', 'var builtin_types_map map[string]string',\n           'func set_builtins() {', 'builtin_types_map = map[string]string{',]\n    for k, v in mimetypes.types_map.items():\n        ans.append(f'  \"{serialize_as_go_string(k)}\": \"{serialize_as_go_string(v)}\",')\n    ans.append('}}')\n    return '\\n'.join(ans)\n\n\ndef generate_textual_mimetypes() -> str:\n    ans = ['package utils', 'var KnownTextualMimes = map[string]bool{',]\n    for k in text_mimes:\n        ans.append(f'  \"{serialize_as_go_string(k)}\": true,')\n    ans.append('}')\n    ans.append('var KnownExtensions = map[string]string{')\n    for k, v in known_extensions.items():\n        ans.append(f'  \".{serialize_as_go_string(k)}\": \"{serialize_as_go_string(v)}\",')\n    ans.append('}')\n    return '\\n'.join(ans)\n\n\ndef write_compressed_data(data: bytes, d: BinaryIO) -> None:\n    d.write(struct.pack('<I', len(data)))\n    d.write(bz2.compress(data))\n\n\ndef generate_unicode_names(src: TextIO, dest: BinaryIO) -> None:\n    num_names, num_of_words = map(int, next(src).split())\n    gob = io.BytesIO()\n    gob.write(struct.pack('<II', num_names, num_of_words))\n    for line in src:\n        line = line.strip()\n        if line:\n            a, aliases = line.partition('\\t')[::2]\n            cp, name = a.partition(' ')[::2]\n            ename = name.encode()\n            record = struct.pack('<IH', int(cp), len(ename)) + ename\n            if aliases:\n                record += aliases.encode()\n            gob.write(struct.pack('<H', len(record)) + record)\n    write_compressed_data(gob.getvalue(), dest)\n\n\ndef generate_ssh_kitten_data() -> None:\n    files = {\n        'terminfo/kitty.terminfo', 'terminfo/x/' + Options.term,\n    }\n    for dirpath, dirnames, filenames in os.walk('shell-integration'):\n        for f in filenames:\n            path = os.path.join(dirpath, f)\n            files.add(path.replace(os.sep, '/'))\n    dest = 'tools/tui/shell_integration/data_generated.bin'\n\n    def normalize(t: tarfile.TarInfo) -> tarfile.TarInfo:\n        t.uid = t.gid = 0\n        t.uname = t.gname = ''\n        t.mtime = 0\n        return t\n\n    if newer(dest, *files):\n        buf = io.BytesIO()\n        with tarfile.open(fileobj=buf, mode='w') as tf:\n            for f in sorted(files):\n                tf.add(f, filter=normalize)\n        with open(dest, 'wb') as d:\n            write_compressed_data(buf.getvalue(), d)\n\n\ndef start_simdgen() -> 'subprocess.Popen[bytes]':\n    return subprocess.Popen(['go', 'run', 'generate.go'], cwd='tools/simdstring', stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n\n\ndef main(args: list[str]=sys.argv) -> None:\n    simdgen_process = start_simdgen()\n    with replace_if_needed('constants_generated.go') as f:\n        f.write(generate_constants())\n    with replace_if_needed('tools/utils/style/color-names_generated.go') as f:\n        f.write(generate_color_names())\n    with replace_if_needed('tools/tui/readline/actions_generated.go') as f:\n        f.write(generate_readline_actions())\n    with replace_if_needed('tools/tui/spinners_generated.go') as f:\n        f.write(generate_spinners())\n    with replace_if_needed('tools/utils/mimetypes_generated.go') as f:\n        f.write(generate_mimetypes())\n    with replace_if_needed('tools/utils/mimetypes_textual_generated.go') as f:\n        f.write(generate_textual_mimetypes())\n    if newer('tools/unicode_names/data_generated.bin', 'tools/unicode_names/names.txt'):\n        with open('tools/unicode_names/data_generated.bin', 'wb') as dest, open('tools/unicode_names/names.txt') as src:\n            generate_unicode_names(src, dest)\n    generate_ssh_kitten_data()\n\n    update_completion()\n    update_at_commands()\n    kitten_clis()\n    stringify()\n    make_bitfields()\n    print(json.dumps(changed, indent=2))\n    stdout, stderr = simdgen_process.communicate()\n    if simdgen_process.wait() != 0:\n        print('Failed to generate SIMD ASM', file=sys.stderr)\n        sys.stdout.buffer.write(stdout)\n        sys.stderr.buffer.write(stderr)\n        raise SystemExit(simdgen_process.returncode)\n\n\nif __name__ == '__main__':\n    import runpy\n    m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))\n    m['main']([sys.executable, 'go-code'])\n# }}}\n"
  },
  {
    "path": "gen/key_constants.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport string\nimport subprocess\nimport sys\nfrom pprint import pformat\nfrom typing import Any, Union\n\nif __name__ == '__main__' and not __package__:\n    import __main__\n    __main__.__package__ = 'gen'\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\nfunctional_key_defs = '''# {{{\n# kitty                     XKB                         macVK  macU\nescape                      Escape                      0x35   -\nenter                       Return                      0x24   NSCarriageReturnCharacter\ntab                         Tab                         0x30   NSTabCharacter\nbackspace                   BackSpace                   0x33   NSBackspaceCharacter\ninsert                      Insert                      0x72   Insert\ndelete                      Delete                      0x75   Delete\nleft                        Left                        0x7B   LeftArrow\nright                       Right                       0x7C   RightArrow\nup                          Up                          0x7E   UpArrow\ndown                        Down                        0x7D   DownArrow\npage_up                     Page_Up                     0x74   PageUp\npage_down                   Page_Down                   0x79   PageDown\nhome                        Home                        0x73   Home\nend                         End                         0x77   End\ncaps_lock                   Caps_Lock                   0x39   -\nscroll_lock                 Scroll_Lock                 -      ScrollLock\nnum_lock                    Num_Lock                    0x47   ClearLine\nprint_screen                Print                       -      PrintScreen\npause                       Pause                       -      Pause\nmenu                        Menu                        0x6E   Menu\nf1                          F1                          0x7A   F1\nf2                          F2                          0x78   F2\nf3                          F3                          0x63   F3\nf4                          F4                          0x76   F4\nf5                          F5                          0x60   F5\nf6                          F6                          0x61   F6\nf7                          F7                          0x62   F7\nf8                          F8                          0x64   F8\nf9                          F9                          0x65   F9\nf10                         F10                         0x6D   F10\nf11                         F11                         0x67   F11\nf12                         F12                         0x6F   F12\nf13                         F13                         0x69   F13\nf14                         F14                         0x6B   F14\nf15                         F15                         0x71   F15\nf16                         F16                         0x6A   F16\nf17                         F17                         0x40   F17\nf18                         F18                         0x4F   F18\nf19                         F19                         0x50   F19\nf20                         F20                         0x5A   F20\nf21                         F21                         -      F21\nf22                         F22                         -      F22\nf23                         F23                         -      F23\nf24                         F24                         -      F24\nf25                         F25                         -      F25\nf26                         F26                         -      F26\nf27                         F27                         -      F27\nf28                         F28                         -      F28\nf29                         F29                         -      F29\nf30                         F30                         -      F30\nf31                         F31                         -      F31\nf32                         F32                         -      F32\nf33                         F33                         -      F33\nf34                         F34                         -      F34\nf35                         F35                         -      F35\nkp_0                        KP_0                        0x52   -\nkp_1                        KP_1                        0x53   -\nkp_2                        KP_2                        0x54   -\nkp_3                        KP_3                        0x55   -\nkp_4                        KP_4                        0x56   -\nkp_5                        KP_5                        0x57   -\nkp_6                        KP_6                        0x58   -\nkp_7                        KP_7                        0x59   -\nkp_8                        KP_8                        0x5B   -\nkp_9                        KP_9                        0x5C   -\nkp_decimal                  KP_Decimal                  0x41   -\nkp_divide                   KP_Divide                   0x4B   -\nkp_multiply                 KP_Multiply                 0x43   -\nkp_subtract                 KP_Subtract                 0x4E   -\nkp_add                      KP_Add                      0x45   -\nkp_enter                    KP_Enter                    0x4C   NSEnterCharacter\nkp_equal                    KP_Equal                    0x51   -\nkp_separator                KP_Separator                -      -\nkp_left                     KP_Left                     -      -\nkp_right                    KP_Right                    -      -\nkp_up                       KP_Up                       -      -\nkp_down                     KP_Down                     -      -\nkp_page_up                  KP_Page_Up                  -      -\nkp_page_down                KP_Page_Down                -      -\nkp_home                     KP_Home                     -      -\nkp_end                      KP_End                      -      -\nkp_insert                   KP_Insert                   -      -\nkp_delete                   KP_Delete                   -      -\nkp_begin                    KP_Begin                    -      -\nmedia_play                  XF86AudioPlay               -      -\nmedia_pause                 XF86AudioPause              -      -\nmedia_play_pause            -                           -      -\nmedia_reverse               -                           -      -\nmedia_stop                  XF86AudioStop               -      -\nmedia_fast_forward          XF86AudioForward            -      -\nmedia_rewind                XF86AudioRewind             -      -\nmedia_track_next            XF86AudioNext               -      -\nmedia_track_previous        XF86AudioPrev               -      -\nmedia_record                XF86AudioRecord             -      -\nlower_volume                XF86AudioLowerVolume        -      -\nraise_volume                XF86AudioRaiseVolume        -      -\nmute_volume                 XF86AudioMute               -      -\nleft_shift                  Shift_L                     0x38   -\nleft_control                Control_L                   0x3B   -\nleft_alt                    Alt_L                       0x3A   -\nleft_super                  Super_L                     0x37   -\nleft_hyper                  Hyper_L                     -      -\nleft_meta                   Meta_L                      -      -\nright_shift                 Shift_R                     0x3C   -\nright_control               Control_R                   0x3E   -\nright_alt                   Alt_R                       0x3D   -\nright_super                 Super_R                     0x36   -\nright_hyper                 Hyper_R                     -      -\nright_meta                  Meta_R                      -      -\niso_level3_shift            ISO_Level3_Shift            -      -\niso_level5_shift            ISO_Level5_Shift            -      -\n'''  # }}}\n\nshift_map = {x[0]: x[1] for x in '`~ 1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) -_ =+ [{ ]} \\\\| ;: \\'\" ,< .> /?'.split()}\nshift_map.update({x: x.upper() for x in string.ascii_lowercase})\nfunctional_encoding_overrides = {\n    'insert': 2, 'delete': 3, 'page_up': 5, 'page_down': 6,\n    'home': 7, 'end': 8, 'tab': 9, 'f1': 11, 'f2': 12, 'f3': 13, 'enter': 13, 'f4': 14,\n    'f5': 15, 'f6': 17, 'f7': 18, 'f8': 19, 'f9': 20, 'f10': 21,\n    'f11': 23, 'f12': 24, 'escape': 27, 'backspace': 127\n}\ndifferent_trailer_functionals = {\n    'up': 'A', 'down': 'B', 'right': 'C', 'left': 'D', 'kp_begin': 'E', 'end': 'F', 'home': 'H',\n    'f1': 'P', 'f2': 'Q', 'f3': '~', 'f4': 'S', 'enter': 'u', 'tab': 'u',\n    'backspace': 'u', 'escape': 'u'\n}\n\nmacos_ansi_key_codes = {  # {{{\n    0x1D: ord('0'),\n    0x12: ord('1'),\n    0x13: ord('2'),\n    0x14: ord('3'),\n    0x15: ord('4'),\n    0x17: ord('5'),\n    0x16: ord('6'),\n    0x1A: ord('7'),\n    0x1C: ord('8'),\n    0x19: ord('9'),\n    0x00: ord('a'),\n    0x0B: ord('b'),\n    0x08: ord('c'),\n    0x02: ord('d'),\n    0x0E: ord('e'),\n    0x03: ord('f'),\n    0x05: ord('g'),\n    0x04: ord('h'),\n    0x22: ord('i'),\n    0x26: ord('j'),\n    0x28: ord('k'),\n    0x25: ord('l'),\n    0x2E: ord('m'),\n    0x2D: ord('n'),\n    0x1F: ord('o'),\n    0x23: ord('p'),\n    0x0C: ord('q'),\n    0x0F: ord('r'),\n    0x01: ord('s'),\n    0x11: ord('t'),\n    0x20: ord('u'),\n    0x09: ord('v'),\n    0x0D: ord('w'),\n    0x07: ord('x'),\n    0x10: ord('y'),\n    0x06: ord('z'),\n\n    0x27: ord('\\''),\n    0x2A: ord('\\\\'),\n    0x2B: ord(','),\n    0x18: ord('='),\n    0x32: ord('`'),\n    0x21: ord('['),\n    0x1B: ord('-'),\n    0x2F: ord('.'),\n    0x1E: ord(']'),\n    0x29: ord(';'),\n    0x2C: ord('/'),\n    0x31: ord(' '),\n}  # }}}\n\nfunctional_key_names: list[str] = []\nname_to_code: dict[str, int] = {}\nname_to_xkb: dict[str, str] = {}\nname_to_vk: dict[str, int] = {}\nname_to_macu: dict[str, str] = {}\nstart_code = 0xe000\nfor line in functional_key_defs.splitlines():\n    line = line.strip()\n    if not line or line.startswith('#'):\n        continue\n    parts = line.split()\n    name = parts[0]\n    functional_key_names.append(name)\n    name_to_code[name] = len(name_to_code) + start_code\n    if parts[1] != '-':\n        name_to_xkb[name] = parts[1]\n    if parts[2] != '-':\n        name_to_vk[name] = int(parts[2], 16)\n    if parts[3] != '-':\n        val = parts[3]\n        if not val.startswith('NS'):\n            val = f'NS{val}FunctionKey'\n        name_to_macu[name] = val\nlast_code = start_code + len(functional_key_names) - 1\nctrl_mapping = {\n    ' ': 0, '@': 0, 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7,\n    'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14, 'o': 15, 'p': 16,\n    'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21, 'v': 22, 'w': 23, 'x': 24,\n    'y': 25, 'z': 26, '[': 27, '\\\\': 28, ']': 29, '^': 30, '~': 30, '/': 31,\n    '_': 31, '?': 127, '0': 48, '1': 49, '2': 0, '3': 27, '4': 28,\n    '5': 29, '6': 30, '7': 31, '8': 127, '9': 57\n}\n\n\ndef patch_file(path: str, what: str, text: str, start_marker: str = '/* ', end_marker: str = ' */') -> None:\n    simple_start_q = f'{start_marker}start {what}{end_marker}'\n    start_q = f'{start_marker}start {what} (auto generated by gen-key-constants.py do not edit){end_marker}'\n    end_q = f'{start_marker}end {what}{end_marker}'\n\n    with open(path, 'r+') as f:\n        raw = f.read()\n        try:\n            start = raw.index(start_q)\n        except ValueError:\n            try:\n                start = raw.index(simple_start_q)\n            except ValueError:\n                raise SystemExit(f'Failed to find \"{simple_start_q}\" in {path}')\n        try:\n            end = raw.index(end_q)\n        except ValueError:\n            raise SystemExit(f'Failed to find \"{end_q}\" in {path}')\n        raw = f'{raw[:start]}{start_q}\\n{text}\\n{raw[end:]}'\n        f.seek(0)\n        f.truncate(0)\n        f.write(raw)\n    if path.endswith('.go'):\n        subprocess.check_call(['go', 'fmt', path])\n\n\ndef serialize_dict(x: dict[Any, Any]) -> str:\n    return pformat(x, indent=4).replace('{', '{\\n ', 1)\n\n\ndef serialize_go_dict(x: Union[dict[str, int], dict[int, str], dict[int, int]]) -> str:\n    ans = []\n\n    def s(x: Union[int, str]) -> str:\n        if isinstance(x, int):\n            return str(x)\n        return f'\"{x}\"'\n\n    for k, v in x.items():\n        ans.append(f'{s(k)}: {s(v)}')\n    return '{' + ', '.join(ans) + '}'\n\n\ndef generate_glfw_header() -> None:\n    lines = [\n        'typedef enum {',\n        f'  GLFW_FKEY_FIRST = 0x{start_code:x}u,',\n    ]\n    klines, pyi, names, knames = [], [], [], []\n    for name, code in name_to_code.items():\n        lines.append(f'  GLFW_FKEY_{name.upper()} = 0x{code:x}u,')\n        klines.append(f'    ADDC(GLFW_FKEY_{name.upper()});')\n        pyi.append(f'GLFW_FKEY_{name.upper()}: int')\n        names.append(f'    case GLFW_FKEY_{name.upper()}: return \"{name.upper()}\";')\n        knames.append(f'            case GLFW_FKEY_{name.upper()}: return PyUnicode_FromString(\"{name}\");')\n    lines.append(f'  GLFW_FKEY_LAST = 0x{last_code:x}u')\n    lines.append('} GLFWFunctionKey;')\n    patch_file('glfw/glfw3.h', 'functional key names', '\\n'.join(lines))\n    patch_file('kitty/glfw.c', 'glfw functional keys', '\\n'.join(klines))\n    patch_file('kitty/fast_data_types.pyi', 'glfw functional keys', '\\n'.join(pyi), start_marker='# ', end_marker='')\n    patch_file('glfw/input.c', 'functional key names', '\\n'.join(names))\n    patch_file('kitty/glfw.c', 'glfw functional key names', '\\n'.join(knames))\n\n\ndef generate_xkb_mapping() -> None:\n    lines, rlines = [], []\n    for name, xkb in name_to_xkb.items():\n        lines.append(f'        case XKB_KEY_{xkb}: return GLFW_FKEY_{name.upper()};')\n        rlines.append(f'        case GLFW_FKEY_{name.upper()}: return XKB_KEY_{xkb};')\n    patch_file('glfw/xkb_glfw.c', 'xkb to glfw', '\\n'.join(lines))\n    patch_file('glfw/xkb_glfw.c', 'glfw to xkb', '\\n'.join(rlines))\n\n\ndef generate_functional_table() -> None:\n    lines = [\n        '',\n        '.. csv-table:: Functional key codes',\n        '   :header: \"Name\", \"CSI\", \"Name\", \"CSI\"',\n        ''\n    ]\n    line_items = []\n    enc_lines = []\n    tilde_trailers = set()\n    for name, code in name_to_code.items():\n        if name in functional_encoding_overrides or name in different_trailer_functionals:\n            trailer = different_trailer_functionals.get(name, '~')\n            if trailer == '~':\n                tilde_trailers.add(code)\n            code = oc = functional_encoding_overrides.get(name, code)\n            code = code if trailer in '~u' else 1\n            enc_lines.append((' ' * 8) + f\"case GLFW_FKEY_{name.upper()}: S({code}, '{trailer}');\")\n            if code == 1 and name not in ('up', 'down', 'left', 'right'):\n                trailer += f' or {oc} ~'\n        else:\n            trailer = 'u'\n        line_items.append(name.upper())\n        line_items.append(f'``{code}\\xa0{trailer}``')\n    for li in chunks(line_items, 4):\n        lines.append('   ' + ', '.join(f'\"{x}\"' for x in li))\n    lines.append('')\n    patch_file('docs/keyboard-protocol.rst', 'functional key table', '\\n'.join(lines), start_marker='.. ', end_marker='')\n    patch_file('kitty/key_encoding.c', 'special numbers', '\\n'.join(enc_lines))\n    code_to_name = {v: k.upper() for k, v in name_to_code.items()}\n    csi_map = {v: name_to_code[k] for k, v in functional_encoding_overrides.items()}\n    letter_trailer_codes: dict[str, int] = {\n        v: functional_encoding_overrides.get(k, name_to_code.get(k, 0))\n        for k, v in different_trailer_functionals.items() if v in 'ABCDEHFPQRSZ'}\n    text = f'functional_key_number_to_name_map = {serialize_dict(code_to_name)}'\n    text += f'\\ncsi_number_to_functional_number_map = {serialize_dict(csi_map)}'\n    text += f'\\nletter_trailer_to_csi_number_map = {letter_trailer_codes!r}'\n    text += f'\\ntilde_trailers = {tilde_trailers!r}'\n    patch_file('kitty/key_encoding.py', 'csi mapping', text, start_marker='# ', end_marker='')\n    text = f'var functional_key_number_to_name_map = map[int]string{serialize_go_dict(code_to_name)}\\n'\n    text += f'\\nvar csi_number_to_functional_number_map = map[int]int{serialize_go_dict(csi_map)}\\n'\n    text += f'\\nvar letter_trailer_to_csi_number_map = map[string]int{serialize_go_dict(letter_trailer_codes)}\\n'\n    tt = ', '.join(f'{x}: true' for x in tilde_trailers)\n    text += '\\nvar tilde_trailers = map[int]bool{' + f'{tt}' + '}\\n'\n    patch_file('tools/tui/loop/key-encoding.go', 'csi mapping', text, start_marker='// ', end_marker='')\n\n\ndef generate_legacy_text_key_maps() -> None:\n    tests = []\n    tp = ' ' * 8\n    shift, alt, ctrl = 1, 2, 4\n\n    def simple(c: str) -> None:\n        shifted = shift_map.get(c, c)\n        ctrled = chr(ctrl_mapping.get(c, ord(c)))\n        call = f'enc(ord({c!r}), shifted_key=ord({shifted!r})'\n        for m in range(16):\n            if m == 0:\n                tests.append(f'{tp}ae({call}), {c!r})')\n            elif m == shift:\n                tests.append(f'{tp}ae({call}, mods=shift), {shifted!r})')\n            elif m == alt:\n                tests.append(f'{tp}ae({call}, mods=alt), \"\\\\x1b\" + {c!r})')\n            elif m == ctrl:\n                tests.append(f'{tp}ae({call}, mods=ctrl), {ctrled!r})')\n            elif m == shift | alt:\n                tests.append(f'{tp}ae({call}, mods=shift | alt), \"\\\\x1b\" + {shifted!r})')\n            elif m == ctrl | alt:\n                tests.append(f'{tp}ae({call}, mods=ctrl | alt), \"\\\\x1b\" + {ctrled!r})')\n\n    for k in shift_map:\n        simple(k)\n\n    patch_file('kitty_tests/keys.py', 'legacy letter tests', '\\n'.join(tests), start_marker='# ', end_marker='')\n\n\ndef chunks(lst: list[Any], n: int) -> Any:\n    \"\"\"Yield successive n-sized chunks from lst.\"\"\"\n    for i in range(0, len(lst), n):\n        yield lst[i:i + n]\n\n\ndef generate_ctrl_mapping() -> None:\n    lines = [\n        '.. csv-table:: Emitted bytes when :kbd:`ctrl` is held down and a key is pressed',\n        '   :header: \"Key\", \"Byte\", \"Key\", \"Byte\", \"Key\", \"Byte\"',\n        ''\n    ]\n    items = []\n    mi = []\n    for k in sorted(ctrl_mapping):\n        prefix = '\\\\' if k == '\\\\' else ('SPC' if k == ' ' else '')\n        items.append(prefix + k)\n        val = str(ctrl_mapping[k])\n        items.append(val)\n        if k in \"\\\\'\":\n            k = f'\\\\{k}'\n        mi.append(f\"        case '{k}': return {val};\")\n\n    for line_items in chunks(items, 6):\n        lines.append('   ' + ', '.join(f'\"{x}\"' for x in line_items))\n    lines.append('')\n    patch_file('docs/keyboard-protocol.rst', 'ctrl mapping', '\\n'.join(lines), start_marker='.. ', end_marker='')\n    patch_file('kitty/key_encoding.c', 'ctrl mapping', '\\n'.join(mi))\n\n\ndef generate_macos_mapping() -> None:\n    lines = []\n    for k in sorted(macos_ansi_key_codes):\n        v = macos_ansi_key_codes[k]\n        lines.append(f'        case 0x{k:x}: return 0x{v:x};')\n    patch_file('glfw/cocoa_window.m', 'vk to unicode', '\\n'.join(lines))\n    lines = []\n    for name, vk in name_to_vk.items():\n        lines.append(f'        case 0x{vk:x}: return GLFW_FKEY_{name.upper()};')\n    patch_file('glfw/cocoa_window.m', 'vk to functional', '\\n'.join(lines))\n    lines = []\n    for name, mac in name_to_macu.items():\n        lines.append(f'        case {mac}: return GLFW_FKEY_{name.upper()};')\n    patch_file('glfw/cocoa_window.m', 'macu to functional', '\\n'.join(lines))\n    lines = []\n    for name, mac in name_to_macu.items():\n        lines.append(f'        case GLFW_FKEY_{name.upper()}: return {mac};')\n    patch_file('glfw/cocoa_window.m', 'functional to macu', '\\n'.join(lines))\n\n\ndef main(args: list[str]=sys.argv) -> None:\n    generate_glfw_header()\n    generate_xkb_mapping()\n    generate_functional_table()\n    generate_legacy_text_key_maps()\n    generate_ctrl_mapping()\n    generate_macos_mapping()\n\n\nif __name__ == '__main__':\n    import runpy\n    m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))\n    m['main']([sys.executable, 'key-constants'])\n"
  },
  {
    "path": "gen/rowcolumn-diacritics.txt",
    "content": "# This file lists the diacritics used to indicate row/column numbers for\n# Unicode terminal image placeholders. It is derived from UnicodeData.txt for\n# Unicode 6.0.0 (chosen somewhat arbitrarily: it's old enough, but still\n# contains more than 255 suitable combining chars) using the following\n# command:\n#\n#     cat UnicodeData.txt | grep \"Mn;230;NSM;;\" | grep -v \"0300\\|0301\\|0302\\|0303\\|0304\\|0306\\|0307\\|0308\\|0309\\|030A\\|030B\\|030C\\|030F\\|0311\\|0313\\|0314\\|0342\\|0653\\|0654\"\n#\n# That is, we use combining chars of the same combining class 230 (above the\n# base character) that do not have decomposition mappings, and we also remove\n# some characters that may be fused with other characters during normalization,\n# like 0041 0300 -> 00C0  which is À (A with grave).\n#\n0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;;\n030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;;;;\n030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;;\n0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;;\n0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;;\n033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;;\n033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;;\n033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;;\n0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;\n034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;;\n034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;;\n034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;;\n0350;COMBINING RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;\n0351;COMBINING LEFT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;;\n0352;COMBINING FERMATA;Mn;230;NSM;;;;;N;;;;;\n0357;COMBINING RIGHT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;;\n035B;COMBINING ZIGZAG ABOVE;Mn;230;NSM;;;;;N;;;;;\n0363;COMBINING LATIN SMALL LETTER A;Mn;230;NSM;;;;;N;;;;;\n0364;COMBINING LATIN SMALL LETTER E;Mn;230;NSM;;;;;N;;;;;\n0365;COMBINING LATIN SMALL LETTER I;Mn;230;NSM;;;;;N;;;;;\n0366;COMBINING LATIN SMALL LETTER O;Mn;230;NSM;;;;;N;;;;;\n0367;COMBINING LATIN SMALL LETTER U;Mn;230;NSM;;;;;N;;;;;\n0368;COMBINING LATIN SMALL LETTER C;Mn;230;NSM;;;;;N;;;;;\n0369;COMBINING LATIN SMALL LETTER D;Mn;230;NSM;;;;;N;;;;;\n036A;COMBINING LATIN SMALL LETTER H;Mn;230;NSM;;;;;N;;;;;\n036B;COMBINING LATIN SMALL LETTER M;Mn;230;NSM;;;;;N;;;;;\n036C;COMBINING LATIN SMALL LETTER R;Mn;230;NSM;;;;;N;;;;;\n036D;COMBINING LATIN SMALL LETTER T;Mn;230;NSM;;;;;N;;;;;\n036E;COMBINING LATIN SMALL LETTER V;Mn;230;NSM;;;;;N;;;;;\n036F;COMBINING LATIN SMALL LETTER X;Mn;230;NSM;;;;;N;;;;;\n0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;;\n0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;;\n0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;;\n0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;;\n0487;COMBINING CYRILLIC POKRYTIE;Mn;230;NSM;;;;;N;;;;;\n0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;;\n0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;;\n0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;;\n0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;;\n0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;;\n0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;;;;\n0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;;\n059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;;\n059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;;\n059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;;\n059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;;\n05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;;\n05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;;\n05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;;;;\n05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;;\n05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;;\n05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;;\n05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;;\n05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;;\n0610;ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM;Mn;230;NSM;;;;;N;;;;;\n0611;ARABIC SIGN ALAYHE ASSALLAM;Mn;230;NSM;;;;;N;;;;;\n0612;ARABIC SIGN RAHMATULLAH ALAYHE;Mn;230;NSM;;;;;N;;;;;\n0613;ARABIC SIGN RADI ALLAHOU ANHU;Mn;230;NSM;;;;;N;;;;;\n0614;ARABIC SIGN TAKHALLUS;Mn;230;NSM;;;;;N;;;;;\n0615;ARABIC SMALL HIGH TAH;Mn;230;NSM;;;;;N;;;;;\n0616;ARABIC SMALL HIGH LIGATURE ALEF WITH LAM WITH YEH;Mn;230;NSM;;;;;N;;;;;\n0617;ARABIC SMALL HIGH ZAIN;Mn;230;NSM;;;;;N;;;;;\n0657;ARABIC INVERTED DAMMA;Mn;230;NSM;;;;;N;;;;;\n0658;ARABIC MARK NOON GHUNNA;Mn;230;NSM;;;;;N;;;;;\n0659;ARABIC ZWARAKAY;Mn;230;NSM;;;;;N;;;;;\n065A;ARABIC VOWEL SIGN SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;;\n065B;ARABIC VOWEL SIGN INVERTED SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;;\n065D;ARABIC REVERSED DAMMA;Mn;230;NSM;;;;;N;;;;;\n065E;ARABIC FATHA WITH TWO DOTS;Mn;230;NSM;;;;;N;;;;;\n06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;\n06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;\n06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;;\n06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;;\n06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;;\n06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;;\n06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;;\n06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;;\n06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;;\n06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;;\n06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;;\n06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;;\n06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;;\n06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;;\n06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;;\n06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;;\n0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;;\n0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;;\n0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;;\n0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;;\n0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;;\n073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;;\n073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;;\n073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;;\n0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;;\n0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;;\n0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;\n0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;\n0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;;\n0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;;\n074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;;\n07EB;NKO COMBINING SHORT HIGH TONE;Mn;230;NSM;;;;;N;;;;;\n07EC;NKO COMBINING SHORT LOW TONE;Mn;230;NSM;;;;;N;;;;;\n07ED;NKO COMBINING SHORT RISING TONE;Mn;230;NSM;;;;;N;;;;;\n07EE;NKO COMBINING LONG DESCENDING TONE;Mn;230;NSM;;;;;N;;;;;\n07EF;NKO COMBINING LONG HIGH TONE;Mn;230;NSM;;;;;N;;;;;\n07F0;NKO COMBINING LONG LOW TONE;Mn;230;NSM;;;;;N;;;;;\n07F1;NKO COMBINING LONG RISING TONE;Mn;230;NSM;;;;;N;;;;;\n07F3;NKO COMBINING DOUBLE DOT ABOVE;Mn;230;NSM;;;;;N;;;;;\n0816;SAMARITAN MARK IN;Mn;230;NSM;;;;;N;;;;;\n0817;SAMARITAN MARK IN-ALAF;Mn;230;NSM;;;;;N;;;;;\n0818;SAMARITAN MARK OCCLUSION;Mn;230;NSM;;;;;N;;;;;\n0819;SAMARITAN MARK DAGESH;Mn;230;NSM;;;;;N;;;;;\n081B;SAMARITAN MARK EPENTHETIC YUT;Mn;230;NSM;;;;;N;;;;;\n081C;SAMARITAN VOWEL SIGN LONG E;Mn;230;NSM;;;;;N;;;;;\n081D;SAMARITAN VOWEL SIGN E;Mn;230;NSM;;;;;N;;;;;\n081E;SAMARITAN VOWEL SIGN OVERLONG AA;Mn;230;NSM;;;;;N;;;;;\n081F;SAMARITAN VOWEL SIGN LONG AA;Mn;230;NSM;;;;;N;;;;;\n0820;SAMARITAN VOWEL SIGN AA;Mn;230;NSM;;;;;N;;;;;\n0821;SAMARITAN VOWEL SIGN OVERLONG A;Mn;230;NSM;;;;;N;;;;;\n0822;SAMARITAN VOWEL SIGN LONG A;Mn;230;NSM;;;;;N;;;;;\n0823;SAMARITAN VOWEL SIGN A;Mn;230;NSM;;;;;N;;;;;\n0825;SAMARITAN VOWEL SIGN SHORT A;Mn;230;NSM;;;;;N;;;;;\n0826;SAMARITAN VOWEL SIGN LONG U;Mn;230;NSM;;;;;N;;;;;\n0827;SAMARITAN VOWEL SIGN U;Mn;230;NSM;;;;;N;;;;;\n0829;SAMARITAN VOWEL SIGN LONG I;Mn;230;NSM;;;;;N;;;;;\n082A;SAMARITAN VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;;\n082B;SAMARITAN VOWEL SIGN O;Mn;230;NSM;;;;;N;;;;;\n082C;SAMARITAN VOWEL SIGN SUKUN;Mn;230;NSM;;;;;N;;;;;\n082D;SAMARITAN MARK NEQUDAA;Mn;230;NSM;;;;;N;;;;;\n0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;;\n0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;\n0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;\n0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;;;;\n0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;;;;\n0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;;;;\n0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;;;;\n135D;ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;;\n135E;ETHIOPIC COMBINING VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;;\n135F;ETHIOPIC COMBINING GEMINATION MARK;Mn;230;NSM;;;;;N;;;;;\n17DD;KHMER SIGN ATTHACAN;Mn;230;NSM;;;;;N;;;;;\n193A;LIMBU SIGN KEMPHRENG;Mn;230;NSM;;;;;N;;;;;\n1A17;BUGINESE VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;;\n1A75;TAI THAM SIGN TONE-1;Mn;230;NSM;;;;;N;;;;;\n1A76;TAI THAM SIGN TONE-2;Mn;230;NSM;;;;;N;;;;;\n1A77;TAI THAM SIGN KHUEN TONE-3;Mn;230;NSM;;;;;N;;;;;\n1A78;TAI THAM SIGN KHUEN TONE-4;Mn;230;NSM;;;;;N;;;;;\n1A79;TAI THAM SIGN KHUEN TONE-5;Mn;230;NSM;;;;;N;;;;;\n1A7A;TAI THAM SIGN RA HAAM;Mn;230;NSM;;;;;N;;;;;\n1A7B;TAI THAM SIGN MAI SAM;Mn;230;NSM;;;;;N;;;;;\n1A7C;TAI THAM SIGN KHUEN-LUE KARAN;Mn;230;NSM;;;;;N;;;;;\n1B6B;BALINESE MUSICAL SYMBOL COMBINING TEGEH;Mn;230;NSM;;;;;N;;;;;\n1B6D;BALINESE MUSICAL SYMBOL COMBINING KEMPUL;Mn;230;NSM;;;;;N;;;;;\n1B6E;BALINESE MUSICAL SYMBOL COMBINING KEMPLI;Mn;230;NSM;;;;;N;;;;;\n1B6F;BALINESE MUSICAL SYMBOL COMBINING JEGOGAN;Mn;230;NSM;;;;;N;;;;;\n1B70;BALINESE MUSICAL SYMBOL COMBINING KEMPUL WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;;\n1B71;BALINESE MUSICAL SYMBOL COMBINING KEMPLI WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;;\n1B72;BALINESE MUSICAL SYMBOL COMBINING BENDE;Mn;230;NSM;;;;;N;;;;;\n1B73;BALINESE MUSICAL SYMBOL COMBINING GONG;Mn;230;NSM;;;;;N;;;;;\n1CD0;VEDIC TONE KARSHANA;Mn;230;NSM;;;;;N;;;;;\n1CD1;VEDIC TONE SHARA;Mn;230;NSM;;;;;N;;;;;\n1CD2;VEDIC TONE PRENKHA;Mn;230;NSM;;;;;N;;;;;\n1CDA;VEDIC TONE DOUBLE SVARITA;Mn;230;NSM;;;;;N;;;;;\n1CDB;VEDIC TONE TRIPLE SVARITA;Mn;230;NSM;;;;;N;;;;;\n1CE0;VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA;Mn;230;NSM;;;;;N;;;;;\n1DC0;COMBINING DOTTED GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;\n1DC1;COMBINING DOTTED ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;\n1DC3;COMBINING SUSPENSION MARK;Mn;230;NSM;;;;;N;;;;;\n1DC4;COMBINING MACRON-ACUTE;Mn;230;NSM;;;;;N;;;;;\n1DC5;COMBINING GRAVE-MACRON;Mn;230;NSM;;;;;N;;;;;\n1DC6;COMBINING MACRON-GRAVE;Mn;230;NSM;;;;;N;;;;;\n1DC7;COMBINING ACUTE-MACRON;Mn;230;NSM;;;;;N;;;;;\n1DC8;COMBINING GRAVE-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;;\n1DC9;COMBINING ACUTE-GRAVE-ACUTE;Mn;230;NSM;;;;;N;;;;;\n1DCB;COMBINING BREVE-MACRON;Mn;230;NSM;;;;;N;;;;;\n1DCC;COMBINING MACRON-BREVE;Mn;230;NSM;;;;;N;;;;;\n1DD1;COMBINING UR ABOVE;Mn;230;NSM;;;;;N;;;;;\n1DD2;COMBINING US ABOVE;Mn;230;NSM;;;;;N;;;;;\n1DD3;COMBINING LATIN SMALL LETTER FLATTENED OPEN A ABOVE;Mn;230;NSM;;;;;N;;;;;\n1DD4;COMBINING LATIN SMALL LETTER AE;Mn;230;NSM;;;;;N;;;;;\n1DD5;COMBINING LATIN SMALL LETTER AO;Mn;230;NSM;;;;;N;;;;;\n1DD6;COMBINING LATIN SMALL LETTER AV;Mn;230;NSM;;;;;N;;;;;\n1DD7;COMBINING LATIN SMALL LETTER C CEDILLA;Mn;230;NSM;;;;;N;;;;;\n1DD8;COMBINING LATIN SMALL LETTER INSULAR D;Mn;230;NSM;;;;;N;;;;;\n1DD9;COMBINING LATIN SMALL LETTER ETH;Mn;230;NSM;;;;;N;;;;;\n1DDA;COMBINING LATIN SMALL LETTER G;Mn;230;NSM;;;;;N;;;;;\n1DDB;COMBINING LATIN LETTER SMALL CAPITAL G;Mn;230;NSM;;;;;N;;;;;\n1DDC;COMBINING LATIN SMALL LETTER K;Mn;230;NSM;;;;;N;;;;;\n1DDD;COMBINING LATIN SMALL LETTER L;Mn;230;NSM;;;;;N;;;;;\n1DDE;COMBINING LATIN LETTER SMALL CAPITAL L;Mn;230;NSM;;;;;N;;;;;\n1DDF;COMBINING LATIN LETTER SMALL CAPITAL M;Mn;230;NSM;;;;;N;;;;;\n1DE0;COMBINING LATIN SMALL LETTER N;Mn;230;NSM;;;;;N;;;;;\n1DE1;COMBINING LATIN LETTER SMALL CAPITAL N;Mn;230;NSM;;;;;N;;;;;\n1DE2;COMBINING LATIN LETTER SMALL CAPITAL R;Mn;230;NSM;;;;;N;;;;;\n1DE3;COMBINING LATIN SMALL LETTER R ROTUNDA;Mn;230;NSM;;;;;N;;;;;\n1DE4;COMBINING LATIN SMALL LETTER S;Mn;230;NSM;;;;;N;;;;;\n1DE5;COMBINING LATIN SMALL LETTER LONG S;Mn;230;NSM;;;;;N;;;;;\n1DE6;COMBINING LATIN SMALL LETTER Z;Mn;230;NSM;;;;;N;;;;;\n1DFE;COMBINING LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;\n20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;;\n20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;;\n20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;;\n20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;;\n20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;;\n20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;;\n20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;;\n20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;;\n20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;;\n20E7;COMBINING ANNUITY SYMBOL;Mn;230;NSM;;;;;N;;;;;\n20E9;COMBINING WIDE BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;\n20F0;COMBINING ASTERISK ABOVE;Mn;230;NSM;;;;;N;;;;;\n2CEF;COPTIC COMBINING NI ABOVE;Mn;230;NSM;;;;;N;;;;;\n2CF0;COPTIC COMBINING SPIRITUS ASPER;Mn;230;NSM;;;;;N;;;;;\n2CF1;COPTIC COMBINING SPIRITUS LENIS;Mn;230;NSM;;;;;N;;;;;\n2DE0;COMBINING CYRILLIC LETTER BE;Mn;230;NSM;;;;;N;;;;;\n2DE1;COMBINING CYRILLIC LETTER VE;Mn;230;NSM;;;;;N;;;;;\n2DE2;COMBINING CYRILLIC LETTER GHE;Mn;230;NSM;;;;;N;;;;;\n2DE3;COMBINING CYRILLIC LETTER DE;Mn;230;NSM;;;;;N;;;;;\n2DE4;COMBINING CYRILLIC LETTER ZHE;Mn;230;NSM;;;;;N;;;;;\n2DE5;COMBINING CYRILLIC LETTER ZE;Mn;230;NSM;;;;;N;;;;;\n2DE6;COMBINING CYRILLIC LETTER KA;Mn;230;NSM;;;;;N;;;;;\n2DE7;COMBINING CYRILLIC LETTER EL;Mn;230;NSM;;;;;N;;;;;\n2DE8;COMBINING CYRILLIC LETTER EM;Mn;230;NSM;;;;;N;;;;;\n2DE9;COMBINING CYRILLIC LETTER EN;Mn;230;NSM;;;;;N;;;;;\n2DEA;COMBINING CYRILLIC LETTER O;Mn;230;NSM;;;;;N;;;;;\n2DEB;COMBINING CYRILLIC LETTER PE;Mn;230;NSM;;;;;N;;;;;\n2DEC;COMBINING CYRILLIC LETTER ER;Mn;230;NSM;;;;;N;;;;;\n2DED;COMBINING CYRILLIC LETTER ES;Mn;230;NSM;;;;;N;;;;;\n2DEE;COMBINING CYRILLIC LETTER TE;Mn;230;NSM;;;;;N;;;;;\n2DEF;COMBINING CYRILLIC LETTER HA;Mn;230;NSM;;;;;N;;;;;\n2DF0;COMBINING CYRILLIC LETTER TSE;Mn;230;NSM;;;;;N;;;;;\n2DF1;COMBINING CYRILLIC LETTER CHE;Mn;230;NSM;;;;;N;;;;;\n2DF2;COMBINING CYRILLIC LETTER SHA;Mn;230;NSM;;;;;N;;;;;\n2DF3;COMBINING CYRILLIC LETTER SHCHA;Mn;230;NSM;;;;;N;;;;;\n2DF4;COMBINING CYRILLIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;\n2DF5;COMBINING CYRILLIC LETTER ES-TE;Mn;230;NSM;;;;;N;;;;;\n2DF6;COMBINING CYRILLIC LETTER A;Mn;230;NSM;;;;;N;;;;;\n2DF7;COMBINING CYRILLIC LETTER IE;Mn;230;NSM;;;;;N;;;;;\n2DF8;COMBINING CYRILLIC LETTER DJERV;Mn;230;NSM;;;;;N;;;;;\n2DF9;COMBINING CYRILLIC LETTER MONOGRAPH UK;Mn;230;NSM;;;;;N;;;;;\n2DFA;COMBINING CYRILLIC LETTER YAT;Mn;230;NSM;;;;;N;;;;;\n2DFB;COMBINING CYRILLIC LETTER YU;Mn;230;NSM;;;;;N;;;;;\n2DFC;COMBINING CYRILLIC LETTER IOTIFIED A;Mn;230;NSM;;;;;N;;;;;\n2DFD;COMBINING CYRILLIC LETTER LITTLE YUS;Mn;230;NSM;;;;;N;;;;;\n2DFE;COMBINING CYRILLIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;\n2DFF;COMBINING CYRILLIC LETTER IOTIFIED BIG YUS;Mn;230;NSM;;;;;N;;;;;\nA66F;COMBINING CYRILLIC VZMET;Mn;230;NSM;;;;;N;;;;;\nA67C;COMBINING CYRILLIC KAVYKA;Mn;230;NSM;;;;;N;;;;;\nA67D;COMBINING CYRILLIC PAYEROK;Mn;230;NSM;;;;;N;;;;;\nA6F0;BAMUM COMBINING MARK KOQNDON;Mn;230;NSM;;;;;N;;;;;\nA6F1;BAMUM COMBINING MARK TUKWENTIS;Mn;230;NSM;;;;;N;;;;;\nA8E0;COMBINING DEVANAGARI DIGIT ZERO;Mn;230;NSM;;;;;N;;;;;\nA8E1;COMBINING DEVANAGARI DIGIT ONE;Mn;230;NSM;;;;;N;;;;;\nA8E2;COMBINING DEVANAGARI DIGIT TWO;Mn;230;NSM;;;;;N;;;;;\nA8E3;COMBINING DEVANAGARI DIGIT THREE;Mn;230;NSM;;;;;N;;;;;\nA8E4;COMBINING DEVANAGARI DIGIT FOUR;Mn;230;NSM;;;;;N;;;;;\nA8E5;COMBINING DEVANAGARI DIGIT FIVE;Mn;230;NSM;;;;;N;;;;;\nA8E6;COMBINING DEVANAGARI DIGIT SIX;Mn;230;NSM;;;;;N;;;;;\nA8E7;COMBINING DEVANAGARI DIGIT SEVEN;Mn;230;NSM;;;;;N;;;;;\nA8E8;COMBINING DEVANAGARI DIGIT EIGHT;Mn;230;NSM;;;;;N;;;;;\nA8E9;COMBINING DEVANAGARI DIGIT NINE;Mn;230;NSM;;;;;N;;;;;\nA8EA;COMBINING DEVANAGARI LETTER A;Mn;230;NSM;;;;;N;;;;;\nA8EB;COMBINING DEVANAGARI LETTER U;Mn;230;NSM;;;;;N;;;;;\nA8EC;COMBINING DEVANAGARI LETTER KA;Mn;230;NSM;;;;;N;;;;;\nA8ED;COMBINING DEVANAGARI LETTER NA;Mn;230;NSM;;;;;N;;;;;\nA8EE;COMBINING DEVANAGARI LETTER PA;Mn;230;NSM;;;;;N;;;;;\nA8EF;COMBINING DEVANAGARI LETTER RA;Mn;230;NSM;;;;;N;;;;;\nA8F0;COMBINING DEVANAGARI LETTER VI;Mn;230;NSM;;;;;N;;;;;\nA8F1;COMBINING DEVANAGARI SIGN AVAGRAHA;Mn;230;NSM;;;;;N;;;;;\nAAB0;TAI VIET MAI KANG;Mn;230;NSM;;;;;N;;;;;\nAAB2;TAI VIET VOWEL I;Mn;230;NSM;;;;;N;;;;;\nAAB3;TAI VIET VOWEL UE;Mn;230;NSM;;;;;N;;;;;\nAAB7;TAI VIET MAI KHIT;Mn;230;NSM;;;;;N;;;;;\nAAB8;TAI VIET VOWEL IA;Mn;230;NSM;;;;;N;;;;;\nAABE;TAI VIET VOWEL AM;Mn;230;NSM;;;;;N;;;;;\nAABF;TAI VIET TONE MAI EK;Mn;230;NSM;;;;;N;;;;;\nAAC1;TAI VIET TONE MAI THO;Mn;230;NSM;;;;;N;;;;;\nFE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;;\nFE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;\nFE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;;\nFE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;\nFE24;COMBINING MACRON LEFT HALF;Mn;230;NSM;;;;;N;;;;;\nFE25;COMBINING MACRON RIGHT HALF;Mn;230;NSM;;;;;N;;;;;\nFE26;COMBINING CONJOINING MACRON;Mn;230;NSM;;;;;N;;;;;\n10A0F;KHAROSHTHI SIGN VISARGA;Mn;230;NSM;;;;;N;;;;;\n10A38;KHAROSHTHI SIGN BAR ABOVE;Mn;230;NSM;;;;;N;;;;;\n1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;;\n1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;;\n1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;;\n1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;;\n1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;;\n1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;;\n1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;;\n1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;;\n1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;;\n1D242;COMBINING GREEK MUSICAL TRISEME;Mn;230;NSM;;;;;N;;;;;\n1D243;COMBINING GREEK MUSICAL TETRASEME;Mn;230;NSM;;;;;N;;;;;\n1D244;COMBINING GREEK MUSICAL PENTASEME;Mn;230;NSM;;;;;N;;;;;\n"
  },
  {
    "path": "gen/srgb_lut.py",
    "content": "#!/usr/bin/env python\n\nimport os\nimport sys\nfrom functools import lru_cache\n\nif __name__ == '__main__' and not __package__:\n    import __main__\n    __main__.__package__ = 'gen'\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\n\ndef to_linear(a: float) -> float:\n    if a <= 0.04045:\n        return a / 12.92\n    else:\n        return float(pow((a + 0.055) / 1.055, 2.4))\n\n\n@lru_cache\ndef generate_srgb_lut(line_prefix: str = '    ') -> list[str]:\n    values: list[str] = []\n    lines: list[str] = []\n\n    for i in range(256):\n        values.append(f'{to_linear(i / 255.0):1.8f}f')\n\n    for i in range(16):\n        lines.append(line_prefix + ', '.join(values[i * 16:(i + 1) * 16]) + ',')\n\n    lines[-1] = lines[-1].rstrip(',')\n    return lines\n\n\ndef generate_srgb_gamma(declaration: str = 'static const GLfloat srgb_lut[256] = {', close: str = '};') -> str:\n    lines: list[str] = []\n    a = lines.append\n\n    a('// Generated by gen-srgb-lut.py DO NOT edit')\n    a('')\n    a(declaration)\n    lines += generate_srgb_lut()\n    a(close)\n\n    return \"\\n\".join(lines)\n\n\ndef main(args: list[str]=sys.argv) -> None:\n    c = generate_srgb_gamma()\n    with open(os.path.join('kitty', 'srgb_gamma.h'), 'w') as f:\n        f.write(f'{c}\\n')\n\n\nif __name__ == '__main__':\n    import runpy\n    m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))\n    m['main']([sys.executable, 'srgb-lut'])\n"
  },
  {
    "path": "gen/wcwidth.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\n# Imports {{{\nimport json\nimport os\nimport re\nimport subprocess\nimport sys\nfrom collections import defaultdict\nfrom collections.abc import Generator, Hashable, Iterable\nfrom contextlib import contextmanager\nfrom functools import lru_cache, partial\nfrom html.entities import html5\nfrom io import StringIO\nfrom math import ceil, log\nfrom typing import Callable, DefaultDict, Iterator, Literal, NamedTuple, Optional, Protocol, Sequence, TypedDict, TypeVar, Union\nfrom urllib.request import urlopen\n\nif __name__ == '__main__' and not __package__:\n    import __main__\n    __main__.__package__ = 'gen'\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n# }}}\n\n# Fetching data {{{\nnon_characters = frozenset(range(0xfffe, 0x10ffff, 0x10000))\nnon_characters |= frozenset(range(0xffff, 0x10ffff + 1, 0x10000))\nnon_characters |= frozenset(range(0xfdd0, 0xfdf0))\nif len(non_characters) != 66:\n    raise SystemExit('non_characters table incorrect')\nemoji_skin_tone_modifiers = frozenset(range(0x1f3fb, 0x1F3FF + 1))\n\n\ndef fetch_url(url: str) -> str:\n    bn = os.path.basename(url)\n    local = os.path.join('/tmp', bn)\n    if os.path.exists(local):\n        with open(local, 'rb') as f:\n            data = f.read()\n    else:\n        data = urlopen(url).read()\n        with open(local, 'wb') as f:\n            f.write(data)\n    return data.decode()\n\n\ndef get_data(fname: str, folder: str = 'UCD') -> Iterable[str]:\n    url = f'https://www.unicode.org/Public/{folder}/latest/{fname}'\n    for line in fetch_url(url).splitlines():\n        line = line.strip()\n        if line and not line.startswith('#'):\n            yield line\n\n\n@lru_cache(maxsize=2)\ndef unicode_version() -> tuple[int, int, int]:\n    for line in get_data(\"ReadMe.txt\"):\n        m = re.search(r'Version\\s+(\\d+)\\.(\\d+)\\.(\\d+)', line)\n        if m is not None:\n            return int(m.group(1)), int(m.group(2)), int(m.group(3))\n    raise ValueError('Could not find Unicode Version')\n# }}}\n\n# Parsing Unicode databases {{{\n# Map of class names to set of codepoints in class\nclass_maps: dict[str, set[int]] = {}\nall_symbols: set[int] = set()\nname_map: dict[int, str] = {}\nword_search_map: DefaultDict[str, set[int]] = defaultdict(set)\nsoft_hyphen = 0xad\nflag_codepoints = frozenset(range(0x1F1E6, 0x1F1E6 + 26))\n# See https://github.com/harfbuzz/harfbuzz/issues/169\nmarks = set(emoji_skin_tone_modifiers) | flag_codepoints\nnot_assigned = set(range(0, sys.maxunicode))\nproperty_maps: dict[str, set[int]] = defaultdict(set)\ngrapheme_segmentation_maps: dict[str, set[int]] = defaultdict(set)\ngrapheme_break_as_int: dict[str, int] = {}\nint_as_grapheme_break: tuple[str, ...] = ()\nincb_as_int: dict[str, int] = {}\nint_as_incb: tuple[str, ...] = ()\nincb_map: dict[str, set[int]] = defaultdict(set)\nextended_pictographic: set[int] = set()\n\n\ndef parse_prop_list() -> None:\n    global marks\n    for line in get_data('ucd/PropList.txt'):\n        if line.startswith('#'):\n            continue\n        cp_or_range, rest = line.split(';', 1)\n        chars = parse_range_spec(cp_or_range.strip())\n        name = rest.strip().split()[0]\n        property_maps[name] |= chars\n    # see https://www.unicode.org/faq/unsup_char.html#3\n    marks |= property_maps['Other_Default_Ignorable_Code_Point']\n\n\ndef parse_ucd() -> None:\n\n    def add_word(w: str, c: int) -> None:\n        if c <= 32 or c == 127 or 128 <= c <= 159:\n            return\n        if len(w) > 1:\n            word_search_map[w.lower()].add(c)\n\n    first: Optional[int] = None\n    for word, c in html5.items():\n        if len(c) == 1:\n            add_word(word.rstrip(';'), ord(c))\n    word_search_map['nnbsp'].add(0x202f)\n    for line in get_data('ucd/UnicodeData.txt'):\n        parts = [x.strip() for x in line.split(';')]\n        codepoint = int(parts[0], 16)\n        name = parts[1] or parts[10]\n        if name == '<control>':\n            name = parts[10]\n        if name:\n            name_map[codepoint] = name\n            for word in name.lower().split():\n                add_word(word, codepoint)\n        category = parts[2]\n        s = class_maps.setdefault(category, set())\n        desc = parts[1]\n        codepoints: Union[tuple[int, ...], Iterable[int]] = (codepoint,)\n        if first is None:\n            if desc.endswith(', First>'):\n                first = codepoint\n                continue\n        else:\n            codepoints = range(first, codepoint + 1)\n            first = None\n        for codepoint in codepoints:\n            s.add(codepoint)\n            not_assigned.discard(codepoint)\n            if category.startswith('M'):\n                marks.add(codepoint)\n            elif category.startswith('S'):\n                all_symbols.add(codepoint)\n            elif category == 'Cf':\n                # we add Cf to marks as it contains things like tags and zero\n                # width chars. Not sure if *all* of Cf should be treated as\n                # combining chars, might need to add individual exceptions in\n                # the future.\n                marks.add(codepoint)\n\n    gndata = fetch_url('https://raw.githubusercontent.com/ryanoasis/nerd-fonts/refs/heads/master/glyphnames.json')\n    for name, val in json.loads(gndata).items():\n        if name != 'METADATA':\n            codepoint = int(val['code'], 16)\n            category, sep, name = name.rpartition('-')\n            name = name or category\n            name = name.replace('_', ' ')\n            if name and codepoint not in name_map:\n                name_map[codepoint] = name.upper()\n                for word in name.lower().split():\n                    add_word(word, codepoint)\n\n    # Some common synonyms\n    word_search_map['bee'] |= word_search_map['honeybee']\n    word_search_map['lambda'] |= word_search_map['lamda']\n    word_search_map['lamda'] |= word_search_map['lambda']\n    word_search_map['diamond'] |= word_search_map['gem']\n\n\ndef parse_range_spec(spec: str) -> set[int]:\n    spec = spec.strip()\n    if '..' in spec:\n        chars_ = tuple(map(lambda x: int(x, 16), filter(None, spec.split('.'))))\n        chars = set(range(chars_[0], chars_[1] + 1))\n    else:\n        chars = {int(spec, 16)}\n    return chars\n\n\ndef split_two(line: str) -> tuple[set[int], str]:\n    spec, rest = line.split(';', 1)\n    spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()\n    return parse_range_spec(spec), rest\n\n\nall_emoji: set[int] = set()\nemoji_presentation_bases: set[int] = set()\nnarrow_emoji: set[int] = set()\nwide_emoji: set[int] = set()\nflags: dict[int, list[int]] = {}\n\n\ndef parse_basic_emoji(spec: str) -> None:\n    parts = list(filter(None, spec.split()))\n    has_emoji_presentation = len(parts) < 2\n    chars = parse_range_spec(parts[0])\n    all_emoji.update(chars)\n    emoji_presentation_bases.update(chars)\n    (wide_emoji if has_emoji_presentation else narrow_emoji).update(chars)\n\n\ndef parse_keycap_sequence(spec: str) -> None:\n    base, fe0f, cc = list(filter(None, spec.split()))\n    chars = parse_range_spec(base)\n    all_emoji.update(chars)\n    emoji_presentation_bases.update(chars)\n    narrow_emoji.update(chars)\n\n\ndef parse_flag_emoji_sequence(spec: str) -> None:\n    a, b = list(filter(None, spec.split()))\n    left, right = int(a, 16), int(b, 16)\n    chars = {left, right}\n    all_emoji.update(chars)\n    wide_emoji.update(chars)\n    emoji_presentation_bases.update(chars)\n    flags.setdefault(left, []).append(right)\n\n\ndef parse_emoji_tag_sequence(spec: str) -> None:\n    a = int(spec.split()[0], 16)\n    all_emoji.add(a)\n    wide_emoji.add(a)\n    emoji_presentation_bases.add(a)\n\n\ndef parse_emoji_modifier_sequence(spec: str) -> None:\n    a, b = list(filter(None, spec.split()))\n    char, mod = int(a, 16), int(b, 16)\n    mod\n    all_emoji.add(char)\n    wide_emoji.add(char)\n    emoji_presentation_bases.add(char)\n\n\ndef parse_emoji() -> None:\n    for line in get_data('emoji-sequences.txt', 'emoji'):\n        parts = [x.strip() for x in line.split(';')]\n        if len(parts) < 2:\n            continue\n        data, etype = parts[:2]\n        if etype == 'Basic_Emoji':\n            parse_basic_emoji(data)\n        elif etype == 'Emoji_Keycap_Sequence':\n            parse_keycap_sequence(data)\n        elif etype == 'RGI_Emoji_Flag_Sequence':\n            parse_flag_emoji_sequence(data)\n        elif etype == 'RGI_Emoji_Tag_Sequence':\n            parse_emoji_tag_sequence(data)\n        elif etype == 'RGI_Emoji_Modifier_Sequence':\n            parse_emoji_modifier_sequence(data)\n\n\ndoublewidth: set[int] = set()\nambiguous: set[int] = set()\n\n\ndef parse_eaw() -> None:\n    global doublewidth, ambiguous\n    seen: set[int] = set()\n    for line in get_data('ucd/EastAsianWidth.txt'):\n        chars, eaw = split_two(line)\n        if eaw == 'A':\n            ambiguous |= chars\n            seen |= chars\n        elif eaw in ('W', 'F'):\n            doublewidth |= chars\n            seen |= chars\n    doublewidth |= set(range(0x3400, 0x4DBF + 1)) - seen\n    doublewidth |= set(range(0x4E00, 0x9FFF + 1)) - seen\n    doublewidth |= set(range(0xF900, 0xFAFF + 1)) - seen\n    doublewidth |= set(range(0x20000, 0x2FFFD + 1)) - seen\n    doublewidth |= set(range(0x30000, 0x3FFFD + 1)) - seen\n\n\ndef parse_grapheme_segmentation() -> None:\n    global extended_pictographic, grapheme_break_as_int, incb_as_int, int_as_grapheme_break, int_as_incb\n    global seg_props_from_int, seg_props_as_int\n    grapheme_segmentation_maps['AtStart']  # this is used by the segmentation algorithm, no character has it\n    grapheme_segmentation_maps['None']  # this is used by the segmentation algorithm, no character has it\n    for line in get_data('ucd/auxiliary/GraphemeBreakProperty.txt'):\n        chars, category = split_two(line)\n        grapheme_segmentation_maps[category] |= chars\n    grapheme_segmentation_maps['Private_Expecting_RI']  # this is used by the segmentation algorithm, no character has it\n    grapheme_break_as_int = {x: i for i, x in enumerate(grapheme_segmentation_maps)}\n    int_as_grapheme_break = tuple(grapheme_break_as_int)\n    incb_map['None']   # used by segmentation algorithm no character has it\n    for line in get_data('ucd/DerivedCoreProperties.txt'):\n        spec, rest = line.split(';', 1)\n        category = rest.strip().split(' ', 1)[0].strip().rstrip(';')\n        chars = parse_range_spec(spec.strip())\n        if category == 'InCB':\n            # Most InCB chars also have a GBP categorization, but not all,\n            # there exist some InCB chars that do not have a GBP category\n            subcat = rest.strip().split(';')[1].strip().split()[0].strip()\n            incb_map[subcat] |= chars\n    incb_as_int = {x: i for i, x in enumerate(incb_map)}\n    int_as_incb = tuple(incb_as_int)\n    for line in get_data('ucd/emoji/emoji-data.txt'):\n        chars, category = split_two(line)\n        if 'Extended_Pictographic#' == category:\n            extended_pictographic |= chars\n    seg_props_from_int = {'grapheme_break': int_as_grapheme_break, 'indic_conjunct_break': int_as_incb}\n    seg_props_as_int = {'grapheme_break': grapheme_break_as_int, 'indic_conjunct_break': incb_as_int}\n\n\nclass GraphemeSegmentationTest(TypedDict):\n    data: tuple[str, ...]\n    comment: str\n\n\ngrapheme_segmentation_tests: list[GraphemeSegmentationTest] = []\n\n\ndef parse_test_data() -> None:\n    for line in get_data('ucd/auxiliary/GraphemeBreakTest.txt'):\n        t, comment = line.split('#')\n        t = t.lstrip('÷').strip().rstrip('÷').strip()\n        chars: list[list[str]] = [[]]\n        for x in re.split(r'([÷×])', t):\n            x = x.strip()\n            match x:\n                case '÷':\n                    chars.append([])\n                case '×':\n                    pass\n                case '':\n                    pass\n                case _:\n                    ch = chr(int(x, 16))\n                    chars[-1].append(ch)\n        c = tuple(''.join(c) for c in chars)\n        grapheme_segmentation_tests.append({'data': c, 'comment': comment.strip()})\n    grapheme_segmentation_tests.append({\n        'data': (' ', '\\xad', ' '),\n        'comment': '÷ [0.2] SPACE (Other) ÷ [0.4] SOFT HYPHEN ÷ [999.0] SPACE (Other) ÷ [0.3]'\n    })\n    grapheme_segmentation_tests.append({\n        'data': ('\\U0001f468\\u200d\\U0001f469\\u200d\\U0001f467\\u200d\\U0001f466',),\n        'comment': '÷ [0.2] MAN × [9.0] ZERO WIDTH JOINER × [11.0] WOMAN × [9.0] ZERO WIDTH JOINER × [11.0] GIRL × [9.0] ZERO WIDTH JOINER × [11.0] BOY ÷ [0.3]'\n    })\n# }}}\n\n\ndef write_case(spec: Union[tuple[int, ...], int], p: Callable[..., None], for_go: bool = False) -> None:\n    if isinstance(spec, tuple):\n        if for_go:\n            v = ', '.join(f'0x{x:x}' for x in range(spec[0], spec[1] + 1))\n            p(f'\\t\\tcase {v}:')\n        else:\n            p('\\t\\tcase 0x{:x} ... 0x{:x}:'.format(*spec))\n    else:\n        p(f'\\t\\tcase 0x{spec:x}:')\n\n\n@contextmanager\ndef create_header(path: str, include_data_types: bool = True) -> Generator[Callable[..., None], None, None]:\n    with open(path, 'w') as f:\n        p = partial(print, file=f)\n        p('// Unicode data, built from the Unicode Standard', '.'.join(map(str, unicode_version())))\n        p(f'// Code generated by {os.path.basename(__file__)}, DO NOT EDIT.', end='\\n\\n')\n        if path.endswith('.h'):\n            p('#pragma once')\n        if include_data_types:\n            p('#include \"data-types.h\"\\n')\n            p('START_ALLOW_CASE_RANGE')\n        p()\n        yield p\n        p()\n        if include_data_types:\n            p('END_ALLOW_CASE_RANGE')\n\n\ndef gen_names() -> None:\n    aliases_map: dict[int, set[str]] = {}\n    for word, codepoints in word_search_map.items():\n        for cp in codepoints:\n            aliases_map.setdefault(cp, set()).add(word)\n    if len(name_map) > 0xffff:\n        raise Exception('Too many named codepoints')\n    with open('tools/unicode_names/names.txt', 'w') as f:\n        print(len(name_map), len(word_search_map), file=f)\n        for cp in sorted(name_map):\n            name = name_map[cp]\n            words = name.lower().split()\n            aliases = aliases_map.get(cp, set()) - set(words)\n            end = '\\n'\n            if aliases:\n                end = '\\t' + ' '.join(sorted(aliases)) + end\n            print(cp, *words, end=end, file=f)\n\n\ndef gofmt(*files: str) -> None:\n    subprocess.check_call(['gofmt', '-w', '-s'] + list(files))\n\n\ndef gen_rowcolumn_diacritics() -> None:\n    # codes of all row/column diacritics\n    codes = []\n    with open(\"gen/rowcolumn-diacritics.txt\") as file:\n        for line in file.readlines():\n            if line.startswith('#'):\n                continue\n            code = int(line.split(\";\")[0], 16)\n            codes.append(code)\n\n    go_file = 'tools/utils/images/rowcolumn_diacritics.go'\n    with create_header('kitty/rowcolumn-diacritics.c') as p, create_header(go_file, include_data_types=False) as g:\n        p('int diacritic_to_num(char_type code) {')\n        p('\\tswitch (code) {')\n        g('package images')\n        g(f'var NumberToDiacritic = [{len(codes)}]rune''{')\n        g(', '.join(f'0x{x:x}' for x in codes) + ',')\n        g('}')\n\n        range_start_num = 1\n        range_start = 0\n        range_end = 0\n\n        def print_range() -> None:\n            if range_start >= range_end:\n                return\n            write_case((range_start, range_end), p)\n            p('\\t\\treturn code - ' + hex(range_start) + ' + ' +\n              str(range_start_num) + ';')\n\n        for code in codes:\n            if range_end == code:\n                range_end += 1\n            else:\n                print_range()\n                range_start_num += range_end - range_start\n                range_start = code\n                range_end = code + 1\n        print_range()\n\n        p('\\t}')\n        p('\\treturn 0;')\n        p('}')\n    gofmt(go_file)\n\n\ndef gen_test_data() -> None:\n    with open('kitty_tests/GraphemeBreakTest.json', 'wb') as f:\n        f.write(json.dumps(grapheme_segmentation_tests, indent=2, ensure_ascii=False).encode())\n\n\ndef getsize(data: Iterable[int]) -> Literal[1, 2, 4]:\n    # return smallest possible integer size for the given array\n    maxdata = max(data)\n    if maxdata < 256:\n        return 1\n    if maxdata < 65536:\n        return 2\n    return 4\n\n\ndef mask_for(bits: int) -> int:\n    return ~((~0) << bits)\n\n\nHashableType = TypeVar('HashableType', bound=Hashable)\n\ndef splitbins(t: tuple[HashableType, ...], property_size: int, use_fixed_shift: int = 0) -> tuple[list[int], list[int], list[HashableType], int]:\n    if use_fixed_shift:\n        candidates = range(use_fixed_shift, use_fixed_shift + 1)\n    else:\n        n = len(t)-1    # last valid index\n        maxshift = 0    # the most we can shift n and still have something left\n        if n > 0:\n            while n >> 1:\n                n >>= 1\n                maxshift += 1\n        candidates = range(maxshift + 1)\n    t3: list[HashableType] = []\n    tmap: dict[HashableType, int] = {}\n    seen = set()\n    for x in t:\n        if x not in seen:\n            seen.add(x)\n            tmap[x] = len(t3)\n            t3.append(x)\n    t_int = tuple(tmap[x] for x in t)\n    bytesz = sys.maxsize\n\n    def memsize() -> int:\n        ans = len(t1)*getsize(t1)\n        sz3 = len(t3)*property_size + len(t2)*getsize(t2)\n        sz2 = len(t2) * property_size\n        return ans + min(sz2, sz3)\n    for shift in candidates:\n        t1: list[int] = []\n        t2: list[int] = []\n        size = 2**shift\n        bincache: dict[tuple[int, ...], int] = {}\n        for i in range(0, len(t_int), size):\n            bin = t_int[i:i+size]\n            index = bincache.get(bin)\n            if index is None:\n                index = len(t2)\n                bincache[bin] = index\n                t2.extend(bin)\n            t1.append(index >> shift)\n        # determine memory size\n        b = memsize()\n        if b < bytesz:\n            best = t1, t2, shift\n            bytesz = b\n    t1, t2, shift = best\n    return t1, t2, t3, shift\n\n\nclass Property(Protocol):\n    @property\n    def as_c(self) -> str:\n        return ''\n\n    @property\n    def as_go(self) -> str:\n        return ''\n\n    @classmethod\n    def bitsize(cls) -> int:\n        return 0\n\n\ndef get_types(sz: int) -> tuple[str, str]:\n    sz *= 8\n    return f'uint{sz}_t', f'uint{sz}'\n\n\ndef gen_multistage_table(\n    c: Callable[..., None], g: Callable[..., None], t1: Sequence[int], t2: Sequence[int], t3: Sequence[Property], shift: int, input_max_val: int\n) -> None:\n    t1_type_sz = getsize(t1)\n    ctype_t1, gotype_t1 = get_types(t1_type_sz)\n    mask = mask_for(shift)\n    name = t3[0].__class__.__name__\n    t2_type_sz = getsize(tuple(range(len(t3))))\n    ctype_t2, gotype_t2 = get_types(t2_type_sz)\n    t3_type_sz = t3[0].bitsize() // 8\n    lname = name.lower()\n    input_type = get_types(getsize((input_max_val,)))[1]\n\n    # Output t1\n    c(f'static const char_type {name}_mask = {mask}u;')\n    c(f'static const char_type {name}_shift = {shift}u;')\n    c(f'static const {ctype_t1} {name}_t1[{len(t1)}] = ''{')\n    c(f'\\t{\", \".join(map(str, t1))}')\n    c('};')\n    g(f'const {lname}_mask = {mask}')\n    g(f'const {lname}_shift = {shift}')\n    g(f'var {lname}_t1 = [{len(t1)}]{gotype_t1}''{')\n    g(f'\\t{\", \".join(map(str, t1))},')\n    g('}')\n    bytesz = len(t1) * t1_type_sz\n\n    if t3_type_sz > t2_type_sz:  # needs 3 levels\n        bytesz += len(t2) * t2_type_sz + len(t3) * t3_type_sz\n        c(f'static const {ctype_t2} {name}_t2[{len(t2)}] = ''{')\n        c(f'\\t{\", \".join(map(str, t2))}')\n        c('};')\n        items = '\\n\\t'.join(x.as_c + f', // {i}' for i, x in enumerate(t3))\n        c(f'static const {name} {name}_t3[{len(t3)}] = ''{')\n        c(f'\\t{items}')\n        c('};')\n\n        g(f'var {lname}_t2 = [{len(t2)}]{gotype_t2}''{')\n        g(f'\\t{\", \".join(map(str, t2))},')\n        g('}')\n        items = '\\n\\t'.join(x.as_go + f', // {i}' for i, x in enumerate(t3))\n        g(f'var {lname}_t3 = [{len(t3)}]{name}''{')\n        g(f'\\t{items}')\n        g('}')\n\n        g(f'''\n        // Array accessor function that avoids bounds checking\n        func {lname}_for(x {input_type}) {name} {{\n            t1 := uintptr(*(*{gotype_t1})(unsafe.Pointer(uintptr(unsafe.Pointer(&{lname}_t1[0])) + uintptr(x>>{lname}_shift)*{t1_type_sz})))\n            t1_shifted := (t1 << {lname}_shift) + (uintptr(x) & {lname}_mask)\n            t2 := uintptr(*(*{gotype_t2})(unsafe.Pointer(uintptr(unsafe.Pointer(&{lname}_t2[0])) + t1_shifted*{t2_type_sz})))\n            return *(*{name})(unsafe.Pointer(uintptr(unsafe.Pointer(&{lname}_t3[0])) + t2*{t3_type_sz}))\n        }}\n        ''')\n    else:\n        t3 = tuple(t3[i] for i in t2)\n        bytesz += len(t3) * t3_type_sz\n        items = '\\n\\t'.join(x.as_c + ',' for x in t3)\n        c(f'static const {name} {name}_t2[{len(t3)}] = ''{')\n        c(f'\\t{items}')\n        c('};')\n        items = '\\n\\t'.join(x.as_go + ',' for x in t3)\n        g(f'var {lname}_t2 = [{len(t3)}]{name}''{')\n        g(f'\\t{items}')\n        g('}')\n        g(f'''\n        // Array accessor function that avoids bounds checking\n        func {lname}_for(x {input_type}) {name} {{\n            t1 := uintptr(*(*{gotype_t1})(unsafe.Pointer(uintptr(unsafe.Pointer(&{lname}_t1[0])) + uintptr(x>>{lname}_shift)*{t1_type_sz})))\n            t1_shifted := (t1 << {lname}_shift) + (uintptr(x) & {lname}_mask)\n            return *(*{name})(unsafe.Pointer(uintptr(unsafe.Pointer(&{lname}_t2[0])) + t1_shifted*{t3_type_sz}))\n        }}\n        ''')\n    print(f'Size of {name} table: {ceil(bytesz/1024)}KB with {shift} bit shift')\n\n\nwidth_shift = 4\n\n\ndef bitsize(maxval: int) -> int:  # number of bits needed to store maxval\n    return ceil(log(maxval, 2))\n\n\ndef clamped_bitsize(val: int) -> int:\n    if val <= 8:\n        return 8\n    if val <= 16:\n        return 16\n    if val <= 32:\n        return 32\n    if val <= 64:\n        return 64\n    raise ValueError('Too many fields')\n\n\ndef bitfield_from_int(\n    fields: dict[str, int], x: int, int_to_str: dict[str, tuple[str, ...]]\n) -> dict[str, str | bool]:\n    # first field is least significant, last field is most significant\n    args: dict[str, str | bool] = {}\n    for f, shift in fields.items():\n        mask = mask_for(shift)\n        val = x & mask\n        if shift == 1:\n            args[f] = bool(val)\n        else:\n            args[f] = int_to_str[f][val]\n        x >>= shift\n    return args\n\n\ndef bitfield_as_int(\n    fields: dict[str, int], vals: Sequence[bool | str], str_maps: dict[str, dict[str, int]]\n) -> int:\n    # first field is least significant, last field is most significant\n    ans = shift = 0\n    for i, (f, width) in enumerate(fields.items()):\n        qval = vals[i]\n        if isinstance(qval, str):\n            val = str_maps[f][qval]\n        else:\n            val = int(qval)\n        ans |= val << shift\n        shift += width\n    return ans\n\n\nseg_props_from_int: dict[str, tuple[str, ...]] = {}\nseg_props_as_int: dict[str, dict[str, int]] = {}\n\n\nclass GraphemeSegmentationProps(NamedTuple):\n\n    grapheme_break: str = ''  # set at runtime\n    indic_conjunct_break: str = ''  # set at runtime\n    is_extended_pictographic: bool = True\n\n    @classmethod\n    def used_bits(cls) -> int:\n        return sum(int(cls._field_defaults[f]) for f in cls._fields)\n\n    @classmethod\n    def bitsize(cls) -> int:\n        return clamped_bitsize(cls.used_bits())\n\n    @classmethod\n    def fields(cls) -> dict[str, int]:\n        return {f: int(cls._field_defaults[f]) for f in cls._fields}\n\n    @classmethod\n    def from_int(cls, x: int) -> 'GraphemeSegmentationProps':\n        args = bitfield_from_int(cls.fields(), x, seg_props_from_int)\n        return cls(**args)  # type: ignore\n\n    def __int__(self) -> int:\n        return bitfield_as_int(self.fields(), self, seg_props_as_int)\n\n\ncontrol_grapheme_breaks = 'CR', 'LF', 'Control'\nlinker_or_extend = 'Linker', 'Extend'\n\n\ndef bitfield_declaration_as_c(name: str, fields: dict[str, int], *alternate_fields: dict[str, int]) -> str:\n    base_size = clamped_bitsize(sum(fields.values()))\n    base_type = f'uint{base_size}_t'\n    ans = [f'// {name}Declaration: uses {sum(fields.values())} bits {{''{{', f'typedef union {name} {{']\n    def struct(fields: dict[str, int]) -> Iterator[str]:\n        if not fields:\n            return\n        empty = base_size - sum(fields.values())\n        yield '    struct __attribute__((packed)) {'\n        yield '#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__'\n        for f, width in reversed(fields.items()):\n            yield f'        uint{clamped_bitsize(width)}_t {f} : {width};'\n        if empty:\n            yield f'        uint{clamped_bitsize(empty)}_t : {empty};'\n        yield '#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__'\n        if empty:\n            yield f'        uint{clamped_bitsize(empty)}_t : {empty};'\n        for f, width in fields.items():\n            yield f'        uint{clamped_bitsize(width)}_t {f} : {width};'\n        yield '#else'\n        yield '#error \"Unsupported endianness\"'\n        yield '#endif'\n        yield '    };'\n    ans.extend(struct(fields))\n    for fields in alternate_fields:\n        ans.extend(struct(fields))\n    ans.append(f'    {base_type} val;')\n    ans.append(f'}} {name};')\n    ans.append(f'static_assert(sizeof({name}) == sizeof({base_type}), \"Fix the ordering of {name}\");')\n    ans.append(f'// End{name}Declaration }}''}}')\n    return '\\n'.join(ans)\n\n\nclass GraphemeSegmentationState(NamedTuple):\n    grapheme_break: str = ''  # set at runtime\n    # True if the last character ends a sequence of Indic_Conjunct_Break values:  consonant {extend|linker}*\n    incb_consonant_extended: bool = True\n    # True if the last character ends a sequence of Indic_Conjunct_Break values:  consonant {extend|linker}* linker\n    incb_consonant_extended_linker: bool = True\n    # True if the last character ends a sequence of Indic_Conjunct_Break values:  consonant {extend|linker}* linker {extend|linker}*\n    incb_consonant_extended_linker_extended: bool = True\n    # True if the last character ends an emoji modifier sequence \\p{Extended_Pictographic} Extend*\n    emoji_modifier_sequence: bool = True\n    # True if the last character was immediately preceded by an emoji modifier sequence   \\p{Extended_Pictographic} Extend*\n    emoji_modifier_sequence_before_last_char: bool = True\n\n    @classmethod\n    def make(cls) -> 'GraphemeSegmentationState':\n        return GraphemeSegmentationState('AtStart', False, False, False, False, False)\n\n    @classmethod\n    def fields(cls) -> dict[str, int]:\n        return {f: int(cls._field_defaults[f]) for f in cls._fields}\n\n    @classmethod\n    def from_int(cls, x: int) -> 'GraphemeSegmentationState':\n        args = bitfield_from_int(cls.fields(), x, {'grapheme_break': int_as_grapheme_break})\n        return cls(**args)  # type: ignore\n\n    def __int__(self) -> int:\n        return bitfield_as_int(self.fields(), self, seg_props_as_int)\n\n    @classmethod\n    def c_declaration(cls) -> str:\n        return bitfield_declaration_as_c(cls.__name__, cls.fields())\n\n    @classmethod\n    def used_bits(cls) -> int:\n        return sum(int(cls._field_defaults[f]) for f in cls._fields)\n\n    @classmethod\n    def bitsize(cls) -> int:\n        return clamped_bitsize(cls.used_bits())\n\n    def add_to_current_cell(self, p: GraphemeSegmentationProps) -> 'GraphemeSegmentationResult':\n        prev = self.grapheme_break\n        prop = p.grapheme_break\n        incb = p.indic_conjunct_break\n        add_to_cell = False\n        if self.grapheme_break == 'AtStart':\n            add_to_cell = True\n            if prop == 'Regional_Indicator':\n                prop = 'Private_Expecting_RI'\n        else:\n            # No break between CR and LF (GB3).\n            if prev == 'CR' and prop == 'LF':\n                add_to_cell = True\n            # Break before and after controls (GB4, GB5).\n            elif prev in control_grapheme_breaks or prop in control_grapheme_breaks:\n                pass\n            # No break between Hangul syllable sequences (GB6, GB7, GB8).\n            elif (\n                (prev == 'L' and prop in ('L', 'V', 'LV', 'LVT')) or\n                (prev in ('LV', 'V') and prop in ('V', 'T')) or\n                (prev in ('LVT', 'T') and prop == 'T')\n            ):\n                add_to_cell = True\n            # No break before: extending characters or ZWJ (GB9), SpacingMarks (GB9a), Prepend characters (GB9b).\n            elif prop in ('Extend', 'ZWJ', 'SpacingMark') or prev == 'Prepend':\n                add_to_cell = True\n            # No break within certain combinations of Indic_Conjunct_Break values\n            # Between consonant {extend|linker}* linker {extend|linker}* and consonant (GB9c).\n            elif self.incb_consonant_extended_linker_extended and incb == 'Consonant':\n                add_to_cell = True\n            # No break within emoji modifier sequences or emoji zwj sequences (GB11).\n            elif prev == 'ZWJ' and self.emoji_modifier_sequence_before_last_char and p.is_extended_pictographic:\n                add_to_cell = True\n            # No break between RI if there is an odd number of RI characters before (GB12, GB13).\n            elif prop == 'Regional_Indicator':\n                if prev == 'Private_Expecting_RI':\n                    add_to_cell = True\n                else:\n                    prop = 'Private_Expecting_RI'\n            # Break everywhere else GB999\n\n        incb_consonant_extended_linker = self.incb_consonant_extended and incb == 'Linker'\n        incb_consonant_extended_linker_extended = incb_consonant_extended_linker or (\n                self.incb_consonant_extended_linker_extended and incb in linker_or_extend)\n        incb_consonant_extended = incb == 'Consonant' or (\n            self.incb_consonant_extended and incb in linker_or_extend)\n        emoji_modifier_sequence_before_last_char = self.emoji_modifier_sequence\n        emoji_modifier_sequence = (self.emoji_modifier_sequence and prop == 'Extend') or p.is_extended_pictographic\n\n        return GraphemeSegmentationResult(GraphemeSegmentationState(\n            grapheme_break=prop, incb_consonant_extended=incb_consonant_extended,\n            incb_consonant_extended_linker=incb_consonant_extended_linker,\n            incb_consonant_extended_linker_extended=incb_consonant_extended_linker_extended,\n            emoji_modifier_sequence=emoji_modifier_sequence, emoji_modifier_sequence_before_last_char=emoji_modifier_sequence_before_last_char\n        ), add_to_cell)\n\n\ndef split_into_graphemes(props: Sequence[GraphemeSegmentationProps], text: str) -> Iterator[str]:\n    s = GraphemeSegmentationState.make()\n    pos = 0\n    for i, ch in enumerate(text):\n        p = props[ord(ch)]\n        s, add_to_cell = s.add_to_current_cell(p)\n        if not add_to_cell:\n            yield text[pos:i]\n            pos = i\n    if pos < len(text):\n        yield text[pos:]\n\n\ndef split_into_graphemes_with_table(\n    props: Sequence['GraphemeSegmentationProps'], table: Sequence['GraphemeSegmentationResult'], text: str,\n) -> Iterator[str]:\n    s = GraphemeSegmentationResult.make()\n    pos = 0\n    for i, ch in enumerate(text):\n        k = int(GraphemeSegmentationKey(s.new_state, props[ord(ch)]))\n        s = table[k]\n        if not s.add_to_current_cell:\n            yield text[pos:i]\n            pos = i\n    if pos < len(text):\n        yield text[pos:]\n\n\ndef test_grapheme_segmentation(split_into_graphemes: Callable[[str], Iterator[str]]) -> None:\n    for test in grapheme_segmentation_tests:\n        expected = test['data']\n        actual = tuple(split_into_graphemes(''.join(test['data'])))\n        if expected != actual:\n            def as_codepoints(text: str) -> str:\n                return ' '.join(hex(ord(x))[2:] for x in text)\n            qe = tuple(map(as_codepoints, expected))\n            qa = tuple(map(as_codepoints, actual))\n            raise SystemExit(f'Failed to split graphemes for: {test[\"comment\"]}\\n{expected!r} {qe} != {actual!r} {qa}')\n\n\nclass GraphemeSegmentationKey(NamedTuple):\n    state: GraphemeSegmentationState\n    char: GraphemeSegmentationProps\n\n    @classmethod\n    def from_int(cls, x: int) -> 'GraphemeSegmentationKey':\n        shift = GraphemeSegmentationProps.used_bits()\n        mask = mask_for(shift)\n        state = GraphemeSegmentationState.from_int(x >> shift)\n        char = GraphemeSegmentationProps.from_int(x & mask)\n        return GraphemeSegmentationKey(state, char)\n\n    def __int__(self) -> int:\n        shift = GraphemeSegmentationProps.used_bits()\n        return int(self.state) << shift | int(self.char)\n\n    def result(self) -> 'GraphemeSegmentationResult':\n        return self.state.add_to_current_cell(self.char)\n\n    @classmethod\n    def code_to_convert_to_int(cls, for_go: bool = False) -> str:\n        lines: list[str] = []\n        a = lines.append\n        shift = GraphemeSegmentationProps.used_bits()\n        if for_go:\n            base_type = f'uint{GraphemeSegmentationState.bitsize()}'\n            a(f'func grapheme_segmentation_key(r GraphemeSegmentationResult, ch CharProps) ({base_type}) ''{')\n            a(f'\\treturn (r.State() << {shift}) | ch.GraphemeSegmentationProperty()')\n            a('}')\n        else:\n            base_type = f'uint{GraphemeSegmentationState.bitsize()}_t'\n            a(f'static inline {base_type} {cls.__name__}(GraphemeSegmentationResult r, CharProps ch)' '{')\n            a(f'\\treturn (r.state << {shift}) | ch.grapheme_segmentation_property;')\n            a('}')\n        return '\\n'.join(lines)\n\n\nclass GraphemeSegmentationResult(NamedTuple):\n    new_state: GraphemeSegmentationState = GraphemeSegmentationState()\n    add_to_current_cell: bool = True\n\n    @classmethod\n    def used_bits(cls) -> int:\n        return sum(int(GraphemeSegmentationState._field_defaults[f]) for f in GraphemeSegmentationState._fields) + 1\n\n    @classmethod\n    def bitsize(cls) -> int:\n        return clamped_bitsize(cls.used_bits())\n\n    @classmethod\n    def make(cls) -> 'GraphemeSegmentationResult':\n        return GraphemeSegmentationResult(GraphemeSegmentationState.make(), False)\n\n    @classmethod\n    def go_fields(cls) -> Sequence[str]:\n        ans = []\n        ans.append('add_to_current_cell 1')\n        for f, width in reversed(GraphemeSegmentationState.fields().items()):\n            ans.append(f'{f} {width}')\n        return tuple(ans)\n\n    @property\n    def as_go(self) -> str:\n        shift = 0\n        parts = []\n        for f in reversed(GraphemeSegmentationResult.go_fields()):\n            f, _, w = f.partition(' ')\n            bits = int(w)\n            if f != 'add_to_current_cell':\n                x = getattr(self.new_state, f)\n                if f == 'grapheme_break':\n                    x = f'GraphemeSegmentationResult(GBP_{x})'\n                else:\n                    x = int(x)\n            else:\n                x = int(self.add_to_current_cell)\n            mask = '0b' + '1' * bits\n            parts.append(f'(({x} & {mask}) << {shift})')\n            shift += bits\n        return ' | '.join(parts)\n\n    @classmethod\n    def go_extra(cls) -> str:\n        bits = GraphemeSegmentationState.used_bits()\n        base_type = f'uint{GraphemeSegmentationState.bitsize()}'\n        return f'''\nfunc (r GraphemeSegmentationResult) State() (ans {base_type}) {{\n    return {base_type}(r) & {mask_for(bits)}\n}}\n    '''\n\n    @property\n    def as_c(self) -> str:\n        parts = []\n        for f in GraphemeSegmentationState._fields:\n            x = getattr(self.new_state, f)\n            match f:\n                case 'grapheme_break':\n                    x = f'GBP_{x}'\n                case _:\n                    x = int(x)\n            parts.append(f'.{f}={x}')\n        parts.append(f'.add_to_current_cell={int(self.add_to_current_cell)}')\n        return '{' + ', '.join(parts) + '}'\n\n    @classmethod\n    def c_declaration(cls) -> str:\n        fields = {'add_to_current_cell': 1}\n        sfields = GraphemeSegmentationState.fields()\n        fields.update(sfields)\n        bits = sum(sfields.values())\n        # dont know if the alternate state access works in big endian\n        return bitfield_declaration_as_c('GraphemeSegmentationResult', fields, {'state': bits})\n\n\nclass CharProps(NamedTuple):\n\n    width: int = 3\n    is_emoji: bool = True\n    category: str = ''  # set at runtime\n    is_emoji_presentation_base: bool = True\n\n    # derived properties for fast lookup\n    is_invalid: bool = True\n    is_non_rendered: bool = True\n    is_symbol: bool = True\n    is_combining_char: bool = True\n    is_word_char: bool = True\n    is_punctuation: bool = True\n\n    # needed for grapheme segmentation set as LSB bits for easy conversion to GraphemeSegmentationProps\n    grapheme_break: str = ''  # set at runtime\n    indic_conjunct_break: str = ''  # set at runtime\n    is_extended_pictographic: bool = True\n\n    @classmethod\n    def bitsize(cls) -> int:\n        ans = sum(int(cls._field_defaults[f]) for f in cls._fields)\n        return clamped_bitsize(ans)\n\n    @classmethod\n    def go_fields(cls) -> Sequence[str]:\n        ans = []\n        for f in cls._fields:\n            bits = int(cls._field_defaults[f])\n            if f == 'width':\n                f = 'shifted_width'\n            ans.append(f'{f} {bits}')\n        return tuple(ans)\n\n    @property\n    def as_go(self) -> str:\n        shift = 0\n        parts = []\n        for f in reversed(self.go_fields()):\n            f, _, w = f.partition(' ')\n            if f == 'shifted_width':\n                f = 'width'\n            x = getattr(self, f)\n            match f:\n                case 'width':\n                    x += width_shift\n                case 'grapheme_break':\n                    x = f'CharProps(GBP_{x})'\n                case 'indic_conjunct_break':\n                    x = f'CharProps(ICB_{x})'\n                case 'category':\n                    x = f'CharProps(UC_{x})'\n                case _:\n                    x = int(x)\n            bits = int(w)\n            mask = '0b' + '1' * bits\n            parts.append(f'(({x} & {mask}) << {shift})')\n            shift += bits\n        return ' | '.join(parts)\n\n    @classmethod\n    def go_extra(cls) -> str:\n        base_type = f'uint{GraphemeSegmentationState.bitsize()}'\n        f = GraphemeSegmentationProps.fields()\n        s = f['grapheme_break'] + f['indic_conjunct_break']\n        return f'''\nfunc (s CharProps) Width() int {{\n\treturn int(s.Shifted_width()) - {width_shift}\n}}\n\nfunc (s CharProps) GraphemeSegmentationProperty() {base_type} {{\n    return {base_type}(s.Grapheme_break() | (s.Indic_conjunct_break() << {f[\"grapheme_break\"]}) | (s.Is_extended_pictographic() << {s}))\n}}\n    '''\n\n    @property\n    def as_c(self) -> str:\n        parts = []\n        for f in self._fields:\n            x = getattr(self, f)\n            match f:\n                case 'width':\n                    x += width_shift\n                    f = 'shifted_width'\n                case 'grapheme_break':\n                    x = f'GBP_{x}'\n                case 'indic_conjunct_break':\n                    x = f'ICB_{x}'\n                case 'category':\n                    x = f'UC_{x}'\n                case _:\n                    x = int(x)\n            parts.append(f'.{f}={x}')\n        return '{' + ', '.join(parts) + '}'\n\n    @classmethod\n    def fields(cls) -> dict[str, int]:\n        return {'shifted_width' if f == 'width' else f: int(cls._field_defaults[f]) for f in cls._fields}\n\n    @classmethod\n    def c_declaration(cls) -> str:\n        # Dont know if grapheme_segmentation_property in alternate works on big endian\n        alternate = {\n            'grapheme_segmentation_property': sum(int(cls._field_defaults[f]) for f in GraphemeSegmentationProps._fields)\n        }\n        return bitfield_declaration_as_c(cls.__name__, cls.fields(), alternate)\n\n\ndef generate_enum(p: Callable[..., None], gp: Callable[..., None], name: str, *items: str, prefix: str = '') -> None:\n    p(f'typedef enum {name} {{')  # }}\n    gp(f'type {name} uint8\\n')\n    gp('const (')  # )\n    for i, x in enumerate(items):\n        x = prefix + x\n        p(f'\\t{x},')\n        if i == 0:\n            gp(f'{x} {name} = iota')\n        else:\n            gp(x)\n    p(f'}} {name};')\n    gp(')')\n    p('')\n    gp('')\n\n\ndef category_set(predicate: Callable[[str], bool]) -> set[int]:\n    ans = set()\n    for c, chs in class_maps.items():\n        if predicate(c):\n            ans |= chs\n    return ans\n\n\ndef top_level_category(q: str) -> set[int]:\n    return category_set(lambda x: x[0] in q)\n\n\ndef patch_declaration(name: str, decl: str, raw: str) -> str:\n    begin = f'// {name}Declaration'\n    end = f'// End{name}Declaration }}''}}'\n    return re.sub(rf'{begin}.+?{end}', decl.rstrip(), raw, flags=re.DOTALL)\n\n\ndef gen_char_props() -> None:\n    CharProps._field_defaults['grapheme_break'] = str(bitsize(len(grapheme_segmentation_maps)))\n    CharProps._field_defaults['indic_conjunct_break'] = str(bitsize(len(incb_map)))\n    CharProps._field_defaults['category'] = str(bitsize(len(class_maps) + 1))\n    GraphemeSegmentationProps._field_defaults['grapheme_break'] = CharProps._field_defaults['grapheme_break']\n    GraphemeSegmentationProps._field_defaults['indic_conjunct_break'] = CharProps._field_defaults['indic_conjunct_break']\n    GraphemeSegmentationState._field_defaults['grapheme_break'] = GraphemeSegmentationProps._field_defaults['grapheme_break']\n    invalid = class_maps['Cc'] | class_maps['Cs'] | non_characters\n    non_printing = invalid | class_maps['Cf']\n    non_rendered = non_printing | property_maps['Other_Default_Ignorable_Code_Point'] | set(range(0xfe00, 0xfe0f + 1))\n    is_word_char = top_level_category('LN')\n    is_punctuation = top_level_category('P')\n    width_map: dict[int, int] = {}\n    cat_map: dict[int, str] = {}\n    for cat, chs in class_maps.items():\n        for ch in chs:\n            cat_map[ch] = cat\n    def aw(s: Iterable[int], width: int) -> None:\n        nonlocal width_map\n        d = dict.fromkeys(s, width)\n        d.update(width_map)\n        width_map = d\n\n    aw(flag_codepoints, 2)\n    aw(doublewidth, 2)\n    aw(wide_emoji, 2)\n    aw(marks | {0}, 0)\n    aw(non_printing, -1)\n    aw(ambiguous, -2)\n    aw(class_maps['Co'], -3)  # Private use\n    aw(not_assigned, -4)\n\n    gs_map: dict[int, str] = {}\n    icb_map: dict[int, str] = {}\n    for name, cps in grapheme_segmentation_maps.items():\n        gs_map.update(dict.fromkeys(cps, name))\n    for name, cps in incb_map.items():\n        icb_map.update(dict.fromkeys(cps, name))\n    prop_array = tuple(\n        CharProps(\n            width=width_map.get(ch, 1), grapheme_break=gs_map.get(ch, 'None'), indic_conjunct_break=icb_map.get(ch, 'None'),\n            is_invalid=ch in invalid, is_non_rendered=ch in non_rendered, is_emoji=ch in all_emoji, is_symbol=ch in all_symbols,\n            is_extended_pictographic=ch in extended_pictographic, is_emoji_presentation_base=ch in emoji_presentation_bases,\n            is_combining_char=ch in marks, category=cat_map.get(ch, 'Cn'), is_word_char=ch in is_word_char,\n            is_punctuation=ch in is_punctuation,\n        ) for ch in range(sys.maxunicode + 1))\n    gsprops = tuple(GraphemeSegmentationProps(\n        grapheme_break=x.grapheme_break, indic_conjunct_break=x.indic_conjunct_break,\n        is_extended_pictographic=x.is_extended_pictographic) for x in prop_array)\n    test_grapheme_segmentation(partial(split_into_graphemes, gsprops))\n    gseg_results = tuple(GraphemeSegmentationKey.from_int(i).result() for i in range(1 << 16))\n\n    test_grapheme_segmentation(partial(split_into_graphemes_with_table, gsprops, gseg_results))\n    t1, t2, t3, t_shift = splitbins(prop_array, CharProps.bitsize() // 8)\n    g1, g2, g3, g_shift = splitbins(gseg_results, GraphemeSegmentationResult.bitsize() // 8)\n\n    from .bitfields import make_bitfield\n    buf = StringIO()\n    cen = partial(print, file=buf)\n    with create_header('kitty/char-props-data.h', include_data_types=False) as c, open('tools/wcswidth/char-props-data.go', 'w') as gof:\n        gp = partial(print, file=gof)\n        gp('package wcswidth')\n        gp('import \"unsafe\"')\n        gp(f'const MAX_UNICODE = {sys.maxunicode}')\n        gp(f'const UNICODE_LIMIT = {sys.maxunicode + 1}')\n        cen('// UCBDeclaration {{''{')\n        cen(f'#define MAX_UNICODE ({sys.maxunicode}u)')\n        generate_enum(cen, gp, 'GraphemeBreakProperty', *grapheme_segmentation_maps, prefix='GBP_')\n        generate_enum(c, gp, 'IndicConjunctBreak', *incb_map, prefix='ICB_')\n        generate_enum(cen, gp, 'UnicodeCategory', 'Cn', *class_maps, prefix='UC_')\n        cen('// EndUCBDeclaration }}''}')\n        gp(make_bitfield('tools/wcswidth', 'CharProps', *CharProps.go_fields(), add_package=False)[1])\n        gp(make_bitfield('tools/wcswidth', 'GraphemeSegmentationResult', *GraphemeSegmentationResult.go_fields(), add_package=False)[1])\n        gp(CharProps.go_extra())\n        gp(GraphemeSegmentationResult.go_extra())\n        gen_multistage_table(c, gp, t1, t2, t3, t_shift, len(prop_array)-1)\n        gen_multistage_table(c, gp, g1, g2, g3, g_shift, len(gseg_results)-1)\n        c(GraphemeSegmentationKey.code_to_convert_to_int())\n        c(GraphemeSegmentationState.c_declaration())\n        gp(GraphemeSegmentationKey.code_to_convert_to_int(for_go=True))\n    gofmt(gof.name)\n    with open('kitty/char-props.h', 'r+') as f:\n        raw = f.read()\n        nraw = re.sub(r'\\d+/\\*=width_shift\\*/', f'{width_shift}/*=width_shift*/', raw)\n        nraw = patch_declaration('CharProps', CharProps.c_declaration(), nraw)\n        nraw = patch_declaration('GraphemeSegmentationResult', GraphemeSegmentationResult.c_declaration(), nraw)\n        nraw = patch_declaration('UCB', buf.getvalue(), nraw)\n        if nraw != raw:\n            f.seek(0)\n            f.truncate()\n            f.write(nraw)\n\n\ndef main(args: list[str]=sys.argv) -> None:\n    parse_ucd()\n    parse_prop_list()\n    parse_emoji()\n    parse_eaw()\n    parse_grapheme_segmentation()\n    parse_test_data()\n    gen_names()\n    gen_rowcolumn_diacritics()\n    gen_test_data()\n    gen_char_props()\n\n\nif __name__ == '__main__':\n    import runpy\n    m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))\n    m['main']([sys.executable, 'wcwidth'])\n"
  },
  {
    "path": "glad/generate.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nimport shlex\nimport shutil\nimport subprocess\n\ncmdline = (\n    'glad --out-path {dest} --api gl:core=3.1 '\n    ' --extensions GL_ARB_texture_storage,GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,'\n    'GL_ARB_instanced_arrays,GL_KHR_debug,GL_ARB_framebuffer_sRGB,GL_EXT_framebuffer_sRGB '\n    'c --header-only --debug'\n)\n\n\ndef clean(x):\n    if os.path.exists(x):\n        shutil.rmtree(x)\n\n\ndef regenerate():\n    clean('out')\n\n    subprocess.check_call(\n        shlex.split(cmdline.format(dest='out'))\n    )\n\n\ndef strip_trailing_whitespace(c):\n    return re.sub(r'\\s+$', '', c, flags=re.MULTILINE) + '\\n'\n\n\ndef export():\n    with open('out/include/glad/gl.h', 'r', encoding='utf-8') as source:\n        data = source.read()\n        data = strip_trailing_whitespace(data)\n\n        with open('../kitty/gl-wrapper.h', 'w', encoding='utf-8') as dest:\n            dest.write(data)\n\n\nif __name__ == '__main__':\n    os.chdir(os.path.dirname(os.path.abspath(__file__)))\n    regenerate()\n    export()\n"
  },
  {
    "path": "glfw/__init__.py",
    "content": ""
  },
  {
    "path": "glfw/backend_utils.c",
    "content": "/*\n * backend_utils.c\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define _GNU_SOURCE\n#include \"backend_utils.h\"\n#include \"internal.h\"\n#include \"memfd.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <time.h>\n#include <stdio.h>\n\n#ifdef __NetBSD__\n#define ppoll pollts\n#endif\n\nvoid\nupdate_fds(EventLoopData *eld) {\n    for (nfds_t i = 0; i < eld->watches_count; i++) {\n        Watch *w = eld->watches + i;\n        eld->fds[i].fd = w->fd;\n        eld->fds[i].events = w->enabled ? w->events : 0;\n    }\n}\n\n\nid_type\naddWatch(EventLoopData *eld, const char* name, int fd, int events, int enabled, watch_callback_func cb, void *cb_data) {\n    if (eld->watches_count >= sizeof(eld->watches)/sizeof(eld->watches[0])) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Too many watches added\");\n        return 0;\n    }\n    static id_type watch_counter = 0;\n    Watch *w = eld->watches + eld->watches_count++;\n    w->name = name;\n    w->fd = fd; w->events = events; w->enabled = enabled;\n    w->callback = cb;\n    w->callback_data = cb_data;\n    w->free = NULL;\n    w->id = ++watch_counter;\n    update_fds(eld);\n    return w->id;\n}\n\n#define removeX(which, item_id, update_func) {\\\n    for (nfds_t i = 0; i < eld->which##_count; i++) { \\\n        if (eld->which[i].id == item_id) { \\\n            eld->which##_count--; \\\n            if (eld->which[i].callback_data && eld->which[i].free) { \\\n                eld->which[i].free(eld->which[i].id, eld->which[i].callback_data); \\\n                eld->which[i].callback_data = NULL; eld->which[i].free = NULL; \\\n            } \\\n            if (i < eld->which##_count) { \\\n                memmove(eld->which + i, eld->which + i + 1, sizeof(eld->which[0]) * (eld->which##_count - i)); \\\n            } \\\n            update_func(eld); break; \\\n}}}\n\nvoid\nremoveWatch(EventLoopData *eld, id_type watch_id) {\n    removeX(watches, watch_id, update_fds);\n}\n\nvoid\ntoggleWatch(EventLoopData *eld, id_type watch_id, int enabled) {\n    for (nfds_t i = 0; i < eld->watches_count; i++) {\n        if (eld->watches[i].id == watch_id) {\n            if (eld->watches[i].enabled != enabled) {\n                eld->watches[i].enabled = enabled;\n                update_fds(eld);\n            }\n            break;\n        }\n    }\n}\n\nstatic id_type timer_counter = 0;\n\nstatic int\ncompare_timers(const void *a_, const void *b_) {\n    const Timer *a = (const Timer*)a_, *b = (const Timer*)b_;\n    return (a->trigger_at > b->trigger_at) ? 1 : (a->trigger_at < b->trigger_at) ? -1 : 0;\n}\n\nstatic void\nupdate_timers(EventLoopData *eld) {\n    if (eld->timers_count > 1) qsort(eld->timers, eld->timers_count, sizeof(eld->timers[0]), compare_timers);\n}\n\nid_type\naddTimer(EventLoopData *eld, const char *name, monotonic_t interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free) {\n    if (eld->timers_count >= sizeof(eld->timers)/sizeof(eld->timers[0])) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Too many timers added\");\n        return 0;\n    }\n    Timer *t = eld->timers + eld->timers_count++;\n    t->interval = interval;\n    t->name = name;\n    t->trigger_at = enabled ? monotonic() + interval : MONOTONIC_T_MAX;\n    t->repeats = repeats;\n    t->callback = cb;\n    t->callback_data = cb_data;\n    t->free = free;\n    t->id = ++timer_counter;\n    update_timers(eld);\n    return timer_counter;\n}\n\nvoid\nremoveTimer(EventLoopData *eld, id_type timer_id) {\n    removeX(timers, timer_id, update_timers);\n}\n\nvoid\nremoveAllTimers(EventLoopData *eld) {\n    for (nfds_t i = 0; i < eld->timers_count; i++) {\n        if (eld->timers[i].free && eld->timers[i].callback_data) eld->timers[i].free(eld->timers[i].id, eld->timers[i].callback_data);\n    }\n    eld->timers_count = 0;\n}\n\nvoid\ntoggleTimer(EventLoopData *eld, id_type timer_id, int enabled) {\n    for (nfds_t i = 0; i < eld->timers_count; i++) {\n        if (eld->timers[i].id == timer_id) {\n            monotonic_t trigger_at = enabled ? (monotonic() + eld->timers[i].interval) : MONOTONIC_T_MAX;\n            if (trigger_at != eld->timers[i].trigger_at) {\n                eld->timers[i].trigger_at = trigger_at;\n                update_timers(eld);\n            }\n            break;\n        }\n    }\n}\n\nvoid\nchangeTimerInterval(EventLoopData *eld, id_type timer_id, monotonic_t interval) {\n    for (nfds_t i = 0; i < eld->timers_count; i++) {\n        if (eld->timers[i].id == timer_id) {\n            eld->timers[i].interval = interval;\n            break;\n        }\n    }\n}\n\n\nmonotonic_t\nprepareForPoll(EventLoopData *eld, monotonic_t timeout) {\n    for (nfds_t i = 0; i < eld->watches_count; i++) eld->fds[i].revents = 0;\n    if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return timeout;\n    monotonic_t now = monotonic(), next_repeat_at = eld->timers[0].trigger_at;\n    if (timeout < 0 || now + timeout > next_repeat_at) {\n        timeout = next_repeat_at <= now ? 0 : next_repeat_at - now;\n    }\n    return timeout;\n}\n\nstatic struct timespec\ncalc_time(monotonic_t nsec) {\n    struct timespec result;\n    result.tv_sec  = nsec / (1000LL * 1000LL * 1000LL);\n    result.tv_nsec = nsec % (1000LL * 1000LL * 1000LL);\n    return result;\n}\n\nint\npollWithTimeout(struct pollfd *fds, nfds_t nfds, monotonic_t timeout) {\n    struct timespec tv = calc_time(timeout);\n    return ppoll(fds, nfds, &tv, NULL);\n}\n\nstatic void\ndispatchEvents(EventLoopData *eld) {\n    for (nfds_t i = 0; i < eld->watches_count; i++) {\n        Watch *ww = eld->watches + i;\n        struct pollfd *pfd = eld->fds + i;\n        if (pfd->revents & ww->events) {\n            ww->ready = 1;\n            if (ww->callback) ww->callback(ww->fd, pfd->revents, ww->callback_data);\n        } else ww->ready = 0;\n    }\n}\n\nunsigned\ndispatchTimers(EventLoopData *eld) {\n    if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return 0;\n    static struct { timer_callback_func func; id_type id; void* data; bool repeats; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])];\n    unsigned num_dispatches = 0;\n    monotonic_t now = monotonic();\n    for (nfds_t i = 0; i < eld->timers_count && eld->timers[i].trigger_at <= now; i++) {\n        eld->timers[i].trigger_at = now + eld->timers[i].interval;\n        dispatches[num_dispatches].func = eld->timers[i].callback;\n        dispatches[num_dispatches].id = eld->timers[i].id;\n        dispatches[num_dispatches].data = eld->timers[i].callback_data;\n        dispatches[num_dispatches].repeats = eld->timers[i].repeats;\n        num_dispatches++;\n    }\n    // we dispatch separately so that the callbacks can modify timers\n    for (unsigned i = 0; i < num_dispatches; i++) {\n        dispatches[i].func(dispatches[i].id, dispatches[i].data);\n        if (!dispatches[i].repeats) {\n            removeTimer(eld, dispatches[i].id);\n        }\n    }\n    if (num_dispatches) update_timers(eld);\n    return num_dispatches;\n}\n\nstatic void\ndrain_wakeup_fd(int fd, EventLoopData* eld) {\n    static char drain_buf[64];\n    eld->wakeup_data_read = false;\n    while(true) {\n        ssize_t ret = read(fd, drain_buf, sizeof(drain_buf));\n        if (ret < 0) {\n            if (errno == EINTR) continue;\n            break;\n        }\n        if (ret > 0) { eld->wakeup_data_read = true; continue; }\n        break;\n    }\n}\n\nstatic void\nmark_wakep_fd_ready(int fd UNUSED, int events UNUSED, void *data) {\n    ((EventLoopData*)(data))->wakeup_fd_ready = true;\n}\n\nstatic void\nmark_key_repeat_fd_ready(int fd UNUSED, int events UNUSED, void *data) {\n    ((EventLoopData*)(data))->key_repeat_fd_ready = true;\n}\n\n\nbool\ninitPollData(EventLoopData *eld, int display_fd) {\n    if (!addWatch(eld, \"display\", display_fd, POLLIN, 1, NULL, NULL)) return false;\n#ifdef HAS_EVENT_FD\n    eld->wakeupFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);\n    if (eld->wakeupFd == -1) return false;\n    const int wakeup_fd = eld->wakeupFd;\n#else\n    if (pipe2(eld->wakeupFds, O_CLOEXEC | O_NONBLOCK) != 0) return false;\n    const int wakeup_fd = eld->wakeupFds[0];\n#endif\n    if (!addWatch(eld, \"wakeup\", wakeup_fd, POLLIN, 1, mark_wakep_fd_ready, eld)) return false;\n\n#ifdef HAS_TIMER_FD\n    eld->key_repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);\n    if (eld->key_repeat_fd < 0) return false;\n    const int key_repeat_fd = eld->key_repeat_fd;\n#else\n    if (pipe2(eld->key_repeat_fds, O_CLOEXEC | O_NONBLOCK) != 0) return false;\n    const int key_repeat_fd = eld->key_repeat_fds[0];\n#endif\n#ifdef _GLFW_WAYLAND\n    if (!addWatch(eld, \"key_repeat\", key_repeat_fd, POLLIN, 1, mark_key_repeat_fd_ready, eld)) return false;\n#else\n    (void)key_repeat_fd; (void)mark_key_repeat_fd_ready;\n#endif\n    return true;\n}\n\nvoid\ncheck_for_wakeup_events(EventLoopData *eld) {\n#ifdef HAS_EVENT_FD\n    int fd = eld->wakeupFd;\n#else\n    int fd = eld->wakeupFds[0];\n#endif\n    drain_wakeup_fd(fd, eld);\n}\n\nvoid\nwakeupEventLoop(EventLoopData *eld) {\n#ifdef HAS_EVENT_FD\n    static const uint64_t value = 1;\n    while (write(eld->wakeupFd, &value, sizeof value) < 0 && (errno == EINTR || errno == EAGAIN));\n#else\n    while (write(eld->wakeupFds[1], \"w\", 1) < 0 && (errno == EINTR || errno == EAGAIN));\n#endif\n}\n\nstatic void\ncloseFds(int *fds, size_t count) {\n    while(count--) {\n        if (*fds > 0) {\n            close(*fds);\n            *fds = -1;\n        }\n        fds++;\n    }\n}\n\nvoid\nfinalizePollData(EventLoopData *eld) {\n    (void)closeFds;\n#ifdef HAS_EVENT_FD\n    close(eld->wakeupFd); eld->wakeupFd = -1;\n#else\n    closeFds(eld->wakeupFds, arraysz(eld->wakeupFds));\n#endif\n#ifdef HAS_TIMER_FD\n    close(eld->key_repeat_fd); eld->key_repeat_fd = -1;\n#else\n    closeFds(eld->key_repeat_fds, arraysz(eld->key_repeat_fds));\n#endif\n}\n\nint\npollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func display_callback) {\n    int read_ok = 0;\n    timeout = prepareForPoll(eld, timeout);\n    EVDBG(\"pollForEvents final timeout: %.3f\", monotonic_t_to_s_double(timeout));\n    int result;\n    monotonic_t end_time = monotonic() + timeout;\n    eld->wakeup_fd_ready = false; eld->key_repeat_fd_ready = false;\n\n    while(1) {\n        if (timeout >= 0) {\n            errno = 0;\n            result = pollWithTimeout(eld->fds, eld->watches_count, timeout);\n            int saved_errno = errno;\n            if (display_callback) display_callback(result, eld->fds[0].revents && eld->watches[0].events, NULL);\n            dispatchTimers(eld);\n            if (result > 0) {\n                dispatchEvents(eld);\n                read_ok = eld->watches[0].ready;\n                break;\n            }\n            timeout = end_time - monotonic();\n            if (timeout <= 0) break;\n            if (result < 0 && (saved_errno == EINTR || saved_errno == EAGAIN)) continue;\n            break;\n        } else {\n            errno = 0;\n            result = poll(eld->fds, eld->watches_count, -1);\n            int saved_errno = errno;\n            if (display_callback) display_callback(result, eld->fds[0].revents && eld->watches[0].events, NULL);\n            dispatchTimers(eld);\n            if (result > 0) {\n                dispatchEvents(eld);\n                read_ok = eld->watches[0].ready;\n            }\n            if (result < 0 && (saved_errno == EINTR || saved_errno == EAGAIN)) continue;\n            break;\n        }\n    }\n    return read_ok;\n}\n\n// Duplicate a UTF-8 encoded string\n// but cut it so that it has at most max_length bytes plus the null byte.\n// This does not take combining characters into account.\nGLFWAPI char* utf_8_strndup(const char* source, size_t max_length) {\n    if (!source) return NULL;\n    size_t length = strnlen(source, max_length);\n    if (length >= max_length) {\n        for (length = max_length; length > 0; length--) {\n            if ((source[length] & 0xC0) != 0x80) break;\n        }\n    }\n\n    char* result = malloc(length + 1);\n    memcpy(result, source, length);\n    result[length] = 0;\n    return result;\n}\n\n/*\n * Create a new, unique, anonymous file of the given size, and\n * return the file descriptor for it. The file descriptor is set\n * CLOEXEC. The file is immediately suitable for mmap()'ing\n * the given size at offset zero.\n *\n * The file should not have a permanent backing store like a disk,\n * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.\n *\n * The file name is deleted from the file system.\n *\n * The file is suitable for buffer sharing between processes by\n * transmitting the file descriptor over Unix sockets using the\n * SCM_RIGHTS methods.\n *\n * posix_fallocate() is used to guarantee that disk space is available\n * for the file at the given size. If disk space is insufficient, errno\n * is set to ENOSPC. If posix_fallocate() is not supported, program may\n * receive SIGBUS on accessing mmap()'ed file contents instead.\n */\nint createAnonymousFile(off_t size) {\n    int ret, fd = -1, shm_anon = 0;\n#ifdef HAS_MEMFD_CREATE\n    fd = glfw_memfd_create(\"glfw-shared\", MFD_CLOEXEC | MFD_ALLOW_SEALING);\n    if (fd < 0) return -1;\n    // We can add this seal before calling posix_fallocate(), as the file\n    // is currently zero-sized anyway.\n    //\n    // There is also no need to check for the return value, we couldn’t do\n    // anything with it anyway.\n    fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);\n#elif defined(SHM_ANON)\n    fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);\n    if (fd < 0) return -1;\n    shm_anon = 1;\n#else\n    static const char template[] = \"/glfw-shared-XXXXXX\";\n    const char* path;\n    char* name;\n\n    path = getenv(\"XDG_RUNTIME_DIR\");\n    if (!path)\n    {\n        errno = ENOENT;\n        return -1;\n    }\n\n    name = calloc(strlen(path) + sizeof(template), 1);\n    strcpy(name, path);\n    strcat(name, template);\n\n    fd = createTmpfileCloexec(name);\n\n    free(name);\n\n    if (fd < 0)\n        return -1;\n#endif\n    // posix_fallocate does not work on SHM descriptors\n    ret = shm_anon ? ftruncate(fd, size) : posix_fallocate(fd, 0, size);\n    if (ret != 0)\n    {\n        close(fd);\n        errno = ret;\n        return -1;\n    }\n    return fd;\n}\n"
  },
  {
    "path": "glfw/backend_utils.h",
    "content": "//========================================================================\n// GLFW 3.4\n//------------------------------------------------------------------------\n// Copyright (c) 2014 Kovid Goyal\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#pragma once\n#include \"../kitty/monotonic.h\"\n#include <poll.h>\n#include <unistd.h>\n#include <stdbool.h>\n#include <sys/types.h>\n\n#ifdef __has_include\n#if __has_include(<sys/eventfd.h>)\n#define HAS_EVENT_FD\n#include <sys/eventfd.h>\n#endif\n#if __has_include(<sys/timerfd.h>)\n#define HAS_TIMER_FD\n#include <sys/timerfd.h>\n#endif\n#else\n#define HAS_EVENT_FD\n#include <sys/eventfd.h>\n#endif\n\ntypedef unsigned long long id_type;\ntypedef void(*watch_callback_func)(int, int, void*);\ntypedef void(*timer_callback_func)(id_type, void*);\ntypedef void (* GLFWuserdatafreefun)(id_type, void*);\n\ntypedef struct {\n    int fd, events, enabled, ready;\n    watch_callback_func callback;\n    void *callback_data;\n    GLFWuserdatafreefun free;\n    id_type id;\n    const char *name;\n} Watch;\n\ntypedef struct {\n    id_type id;\n    monotonic_t interval, trigger_at;\n    timer_callback_func callback;\n    void *callback_data;\n    GLFWuserdatafreefun free;\n    const char *name;\n    bool repeats;\n} Timer;\n\n#define MAX_NUM_OF_WATCHED_FDS 64\n\ntypedef struct {\n    struct pollfd fds[MAX_NUM_OF_WATCHED_FDS];\n#ifdef HAS_EVENT_FD\n    int wakeupFd;\n#else\n    int wakeupFds[2];\n#endif\n#ifdef HAS_TIMER_FD\n    int key_repeat_fd;\n#else\n    int key_repeat_fds[2];\n#endif\n    bool wakeup_data_read, wakeup_fd_ready, key_repeat_fd_ready;\n    nfds_t watches_count, timers_count;\n    Watch watches[MAX_NUM_OF_WATCHED_FDS];\n    Timer timers[128];\n} EventLoopData;\n\n\nvoid check_for_wakeup_events(EventLoopData *eld);\nid_type addWatch(EventLoopData *eld, const char *name, int fd, int events, int enabled, watch_callback_func cb, void *cb_data);\nvoid removeWatch(EventLoopData *eld, id_type watch_id);\nvoid toggleWatch(EventLoopData *eld, id_type watch_id, int enabled);\nid_type addTimer(EventLoopData *eld, const char *name, monotonic_t interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free);\nvoid removeTimer(EventLoopData *eld, id_type timer_id);\nvoid removeAllTimers(EventLoopData *eld);\nvoid toggleTimer(EventLoopData *eld, id_type timer_id, int enabled);\nvoid changeTimerInterval(EventLoopData *eld, id_type timer_id, monotonic_t interval);\nmonotonic_t prepareForPoll(EventLoopData *eld, monotonic_t timeout);\nint pollWithTimeout(struct pollfd *fds, nfds_t nfds, monotonic_t timeout);\nint pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func);\nunsigned dispatchTimers(EventLoopData *eld);\nvoid finalizePollData(EventLoopData *eld);\nbool initPollData(EventLoopData *eld, int display_fd);\nvoid wakeupEventLoop(EventLoopData *eld);\nchar* utf_8_strndup(const char* source, size_t max_length);\nint createAnonymousFile(off_t size);\n"
  },
  {
    "path": "glfw/cocoa_displaylink.m",
    "content": "/*\n * cocoa_displaylink.m\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n// CVDisplayLink is deprecated replace with CADisplayLink via [NSScreen displayLink] once base macOS version is 14\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n\n#include \"internal.h\"\n#include <CoreVideo/CVDisplayLink.h>\n#include <os/lock.h>\n\n#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll)\n#define MAX_NUM_OF_DISPLAYS 256\n\ntypedef struct _GLFWDisplayLinkNS\n{\n    CVDisplayLinkRef displayLink;\n    CGDirectDisplayID displayID;\n    monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at;\n    bool pending_dispatch;\n} _GLFWDisplayLinkNS;\n\nstatic struct {\n    _GLFWDisplayLinkNS entries[MAX_NUM_OF_DISPLAYS];\n    os_unfair_lock locks[MAX_NUM_OF_DISPLAYS];\n    bool locks_initialized[MAX_NUM_OF_DISPLAYS];\n    size_t count;\n} displayLinks = {0};\n\nstatic inline size_t\nindex_for_entry(_GLFWDisplayLinkNS *entry) {\n    return (size_t)(entry - displayLinks.entries);\n}\n\nstatic inline os_unfair_lock*\nlock_for_entry(_GLFWDisplayLinkNS *entry) {\n    return &displayLinks.locks[index_for_entry(entry)];\n}\n\nstatic CGDirectDisplayID\ndisplayIDForWindow(_GLFWwindow *w) {\n    NSWindow *nw = w->ns.object;\n    NSDictionary *dict = [nw.screen deviceDescription];\n    NSNumber *displayIDns = dict[@\"NSScreenNumber\"];\n    if (displayIDns) return [displayIDns unsignedIntValue];\n    return (CGDirectDisplayID)-1;\n}\n\nvoid\n_glfwClearDisplayLinks(void) {\n    for (size_t i = 0; i < displayLinks.count; i++) {\n        _GLFWDisplayLinkNS *entry = &displayLinks.entries[i];\n        os_unfair_lock *lock = &displayLinks.locks[i];\n        os_unfair_lock_lock(lock);\n        CVDisplayLinkRef link = entry->displayLink;\n        entry->displayLink = NULL;\n        entry->displayID = (CGDirectDisplayID)0;\n        entry->lastRenderFrameRequestedAt = 0;\n        entry->first_unserviced_render_frame_request_at = 0;\n        entry->pending_dispatch = false;\n        os_unfair_lock_unlock(lock);\n        if (link) {\n            CVDisplayLinkStop(link);\n            CVDisplayLinkRelease(link);\n        }\n    }\n    displayLinks.count = 0;\n}\n\nstatic void _glfwDispatchRenderFrame(void *);\n\nstatic CVReturn\ndisplayLinkCallback(\n        CVDisplayLinkRef displayLink UNUSED,\n        const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,\n        CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo) {\n    _GLFWDisplayLinkNS *entry = (_GLFWDisplayLinkNS *)userInfo;\n    if (entry) {\n        os_unfair_lock *lock = lock_for_entry(entry);\n        os_unfair_lock_lock(lock);\n        const bool should_dispatch = entry->first_unserviced_render_frame_request_at &&\n            !entry->pending_dispatch;\n        CGDirectDisplayID displayID = entry->displayID;\n        if (should_dispatch) entry->pending_dispatch = true;\n        os_unfair_lock_unlock(lock);\n        if (should_dispatch) dispatch_async_f(dispatch_get_main_queue(), (void*)(uintptr_t)displayID, _glfwDispatchRenderFrame);\n    }\n    return kCVReturnSuccess;\n}\n\nstatic void\n_glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) {\n    CVDisplayLinkCreateWithCGDisplay(entry->displayID, &entry->displayLink);\n    if (entry->displayLink) CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, entry);\n}\n\nunsigned\n_glfwCreateDisplayLink(CGDirectDisplayID displayID) {\n    for (unsigned i = 0; i < displayLinks.count; i++) {\n        os_unfair_lock *existing_lock = &displayLinks.locks[i];\n        os_unfair_lock_lock(existing_lock);\n        const bool already_created = displayLinks.entries[i].displayID == displayID;\n        os_unfair_lock_unlock(existing_lock);\n        if (already_created) return i;\n    }\n    if (displayLinks.count >= MAX_NUM_OF_DISPLAYS) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Too many monitors cannot create display link\");\n        return displayLinks.count;\n    }\n    unsigned idx = displayLinks.count;\n    _GLFWDisplayLinkNS *entry = &displayLinks.entries[idx];\n    if (!displayLinks.locks_initialized[idx]) {\n        displayLinks.locks[idx] = OS_UNFAIR_LOCK_INIT;\n        displayLinks.locks_initialized[idx] = true;\n    }\n    os_unfair_lock *lock = &displayLinks.locks[idx];\n    os_unfair_lock_lock(lock);\n    memset(entry, 0, sizeof(entry[0]));\n    entry->displayID = displayID;\n    displayLinks.count++;\n    _glfw_create_cv_display_link(entry);\n    os_unfair_lock_unlock(lock);\n    return idx;\n}\n\nstatic unsigned long long display_link_shutdown_timer = 0;\n\nstatic void\n_glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data UNUSED) {\n    display_link_shutdown_timer = 0;\n    for (size_t i = 0; i < displayLinks.count; i++) {\n        _GLFWDisplayLinkNS *dl = &displayLinks.entries[i];\n        os_unfair_lock *lock = &displayLinks.locks[i];\n        os_unfair_lock_lock(lock);\n        CVDisplayLinkRef link = dl->displayLink;\n        if (link) CVDisplayLinkRetain(link);\n        dl->lastRenderFrameRequestedAt = 0;\n        dl->first_unserviced_render_frame_request_at = 0;\n        dl->pending_dispatch = false;\n        os_unfair_lock_unlock(lock);\n        if (link) {\n            CVDisplayLinkStop(link);\n            CVDisplayLinkRelease(link);\n        }\n    }\n}\n\nvoid\n_glfwRequestRenderFrame(_GLFWwindow *w) {\n    CGDirectDisplayID displayID = displayIDForWindow(w);\n    if (display_link_shutdown_timer) {\n        _glfwPlatformUpdateTimer(display_link_shutdown_timer, DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, true);\n    } else {\n        display_link_shutdown_timer = _glfwPlatformAddTimer(DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, false, _glfwShutdownCVDisplayLink, NULL, NULL);\n    }\n    monotonic_t now = glfwGetTime();\n    bool found_display_link = false;\n    _GLFWDisplayLinkNS *dl = NULL;\n    for (size_t i = 0; i < displayLinks.count; i++) {\n        dl = &displayLinks.entries[i];\n        bool need_start = false, need_stop = false, need_recreate = false;\n        bool retain_link = false;\n        CVDisplayLinkRef link = NULL;\n        CVDisplayLinkRef new_link = NULL;\n        bool new_link_retained = false;\n        os_unfair_lock *lock = &displayLinks.locks[i];\n        os_unfair_lock_lock(lock);\n        link = dl->displayLink;\n        if (dl->displayID == displayID) {\n            found_display_link = true;\n            monotonic_t first_unserviced = dl->first_unserviced_render_frame_request_at;\n            dl->lastRenderFrameRequestedAt = now;\n            if (!first_unserviced) {\n                dl->first_unserviced_render_frame_request_at = now;\n                first_unserviced = now;\n            }\n            dl->pending_dispatch = false;\n            if (link) {\n                if (!CVDisplayLinkIsRunning(link)) {\n                    need_start = true;\n                    retain_link = true;\n                } else if (now - first_unserviced > s_to_monotonic_t(1ll)) {\n                    need_recreate = true;\n                    dl->first_unserviced_render_frame_request_at = now;\n                    dl->displayLink = NULL;\n                }\n            }\n        } else if (link && dl->lastRenderFrameRequestedAt && now - dl->lastRenderFrameRequestedAt >= DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL) {\n            need_stop = true;\n            retain_link = true;\n            dl->lastRenderFrameRequestedAt = 0;\n            dl->first_unserviced_render_frame_request_at = 0;\n            dl->pending_dispatch = false;\n        }\n        if (retain_link && link) CVDisplayLinkRetain(link);\n        os_unfair_lock_unlock(lock);\n        if (need_recreate && link) {\n            CVDisplayLinkStop(link);\n            CVDisplayLinkRelease(link);\n            os_unfair_lock_lock(lock);\n            _glfw_create_cv_display_link(dl);\n            new_link = dl->displayLink;\n            if (new_link) {\n                CVDisplayLinkRetain(new_link);\n                new_link_retained = true;\n            }\n            dl->pending_dispatch = false;\n            os_unfair_lock_unlock(lock);\n            if (new_link) {\n                if (!CVDisplayLinkIsRunning(new_link)) CVDisplayLinkStart(new_link);\n                if (new_link_retained) CVDisplayLinkRelease(new_link);\n            }\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                \"CVDisplayLink stuck possibly because of sleep/screensaver + Apple's incompetence, recreating.\");\n        } else {\n            if (need_start && link) CVDisplayLinkStart(link);\n            else if (need_stop && link) CVDisplayLinkStop(link);\n            if (retain_link && link) CVDisplayLinkRelease(link);\n        }\n    }\n    if (!found_display_link) {\n        unsigned idx = _glfwCreateDisplayLink(displayID);\n        if (idx < displayLinks.count) {\n            dl = &displayLinks.entries[idx];\n            os_unfair_lock *lock = &displayLinks.locks[idx];\n            os_unfair_lock_lock(lock);\n            dl->lastRenderFrameRequestedAt = now;\n            dl->first_unserviced_render_frame_request_at = now;\n            dl->pending_dispatch = false;\n            CVDisplayLinkRef link = dl->displayLink;\n            if (link) CVDisplayLinkRetain(link);\n            os_unfair_lock_unlock(lock);\n            if (link) {\n                if (!CVDisplayLinkIsRunning(link)) CVDisplayLinkStart(link);\n                CVDisplayLinkRelease(link);\n            }\n        }\n    }\n}\n\nstatic void\n_glfwDispatchRenderFrame(void *passed_in_data) {\n    CGDirectDisplayID displayID = (uintptr_t)passed_in_data;\n    _GLFWwindow *w = _glfw.windowListHead;\n    while (w) {\n        if (w->ns.renderFrameRequested && displayID == displayIDForWindow(w)) {\n            w->ns.renderFrameRequested = false;\n            w->ns.renderFrameCallback((GLFWwindow*)w);\n        }\n        w = w->next;\n    }\n    for (size_t i = 0; i < displayLinks.count; i++) {\n        _GLFWDisplayLinkNS *dl = &displayLinks.entries[i];\n        bool need_stop = false;\n        CVDisplayLinkRef link = NULL;\n        os_unfair_lock *lock = &displayLinks.locks[i];\n        os_unfair_lock_lock(lock);\n        if (dl->displayID == displayID) {\n            dl->first_unserviced_render_frame_request_at = 0;\n            dl->pending_dispatch = false;\n            bool any_pending_request = false;\n            _GLFWwindow *window = _glfw.windowListHead;\n            while (window) {\n                if (window->ns.renderFrameRequested && displayID == displayIDForWindow(window)) {\n                    any_pending_request = true;\n                    break;\n                }\n                window = window->next;\n            }\n            link = dl->displayLink;\n            if (!any_pending_request && link && CVDisplayLinkIsRunning(link)) {\n                need_stop = true;\n                CVDisplayLinkRetain(link);\n                dl->lastRenderFrameRequestedAt = 0;\n            }\n        }\n        os_unfair_lock_unlock(lock);\n        if (need_stop && link) {\n            CVDisplayLinkStop(link);\n            CVDisplayLinkRelease(link);\n        }\n    }\n}\n"
  },
  {
    "path": "glfw/cocoa_init.m",
    "content": "//========================================================================\n// GLFW 3.4 macOS - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n#include \"../kitty/monotonic.h\"\n#include <sys/param.h> // For MAXPATHLEN\n#include <sys/sysctl.h>\n#include <pthread.h>\n\n// Needed for _NSGetProgname\n#include <crt_externs.h>\n\n// Change to our application bundle's resources directory, if present\n//\nstatic void changeToResourcesDirectory(void)\n{\n    char resourcesPath[MAXPATHLEN];\n\n    CFBundleRef bundle = CFBundleGetMainBundle();\n    if (!bundle)\n        return;\n\n    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);\n\n    CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);\n    if (CFStringCompare(CFSTR(\"Resources\"), last, 0) != kCFCompareEqualTo)\n    {\n        CFRelease(last);\n        CFRelease(resourcesURL);\n        return;\n    }\n\n    CFRelease(last);\n\n    if (!CFURLGetFileSystemRepresentation(resourcesURL,\n                                          true,\n                                          (UInt8*) resourcesPath,\n                                          MAXPATHLEN))\n    {\n        CFRelease(resourcesURL);\n        return;\n    }\n\n    CFRelease(resourcesURL);\n\n    chdir(resourcesPath);\n}\n\n// Set up the menu bar (manually)\n// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that\n// could go away at any moment, lots of stuff that really should be\n// localize(d|able), etc.  Add a nib to save us this horror.\n//\nstatic void createMenuBar(void)\n{\n    size_t i;\n    NSString* appName = nil;\n    NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary];\n    NSString* nameKeys[] =\n    {\n        @\"CFBundleDisplayName\",\n        @\"CFBundleName\",\n        @\"CFBundleExecutable\",\n    };\n\n    // Try to figure out what the calling application is called\n\n    for (i = 0;  i < sizeof(nameKeys) / sizeof(nameKeys[0]);  i++)\n    {\n        id name = bundleInfo[nameKeys[i]];\n        if (name &&\n            [name isKindOfClass:[NSString class]] &&\n            ![name isEqualToString:@\"\"])\n        {\n            appName = name;\n            break;\n        }\n    }\n\n    if (!appName)\n    {\n        char** progname = _NSGetProgname();\n        if (progname && *progname)\n            appName = @(*progname);\n        else\n            appName = @\"GLFW Application\";\n    }\n\n    NSMenu* bar = [[NSMenu alloc] init];\n    [NSApp setMainMenu:bar];\n\n    NSMenuItem* appMenuItem =\n        [bar addItemWithTitle:@\"\" action:NULL keyEquivalent:@\"\"];\n    NSMenu* appMenu = [[NSMenu alloc] init];\n    [appMenuItem setSubmenu:appMenu];\n\n    [appMenu addItemWithTitle:[NSString stringWithFormat:@\"About %@\", appName]\n                       action:@selector(orderFrontStandardAboutPanel:)\n                keyEquivalent:@\"\"];\n    [appMenu addItem:[NSMenuItem separatorItem]];\n    NSMenu* servicesMenu = [[NSMenu alloc] init];\n    [NSApp setServicesMenu:servicesMenu];\n    [[appMenu addItemWithTitle:@\"Services\"\n                       action:NULL\n                keyEquivalent:@\"\"] setSubmenu:servicesMenu];\n    [servicesMenu release];\n    [appMenu addItem:[NSMenuItem separatorItem]];\n    [appMenu addItemWithTitle:[NSString stringWithFormat:@\"Hide %@\", appName]\n                       action:@selector(hide:)\n                keyEquivalent:@\"h\"];\n    [[appMenu addItemWithTitle:@\"Hide Others\"\n                       action:@selector(hideOtherApplications:)\n                keyEquivalent:@\"h\"]\n        setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand];\n    [appMenu addItemWithTitle:@\"Show All\"\n                       action:@selector(unhideAllApplications:)\n                keyEquivalent:@\"\"];\n    [appMenu addItem:[NSMenuItem separatorItem]];\n    [appMenu addItemWithTitle:[NSString stringWithFormat:@\"Quit %@\", appName]\n                       action:@selector(terminate:)\n                keyEquivalent:@\"q\"];\n\n    NSMenuItem* windowMenuItem =\n        [bar addItemWithTitle:@\"\" action:NULL keyEquivalent:@\"\"];\n    [bar release];\n    NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@\"Window\"];\n    [NSApp setWindowsMenu:windowMenu];\n    [windowMenuItem setSubmenu:windowMenu];\n\n    [windowMenu addItemWithTitle:@\"Minimize\"\n                          action:@selector(performMiniaturize:)\n                   keyEquivalent:@\"m\"];\n    [windowMenu addItemWithTitle:@\"Zoom\"\n                          action:@selector(performZoom:)\n                   keyEquivalent:@\"\"];\n    [windowMenu addItem:[NSMenuItem separatorItem]];\n    [windowMenu addItemWithTitle:@\"Bring All to Front\"\n                          action:@selector(arrangeInFront:)\n                   keyEquivalent:@\"\"];\n\n    // TODO: Make this appear at the bottom of the menu (for consistency)\n    [windowMenu addItem:[NSMenuItem separatorItem]];\n    [[windowMenu addItemWithTitle:@\"Enter Full Screen\"\n                           action:@selector(toggleFullScreen:)\n                    keyEquivalent:@\"f\"]\n     setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];\n\n    // Prior to Snow Leopard, we need to use this oddly-named semi-private API\n    // to get the application menu working properly.\n    SEL setAppleMenuSelector = NSSelectorFromString(@\"setAppleMenu:\");\n    [NSApp performSelector:setAppleMenuSelector withObject:appMenu];\n}\n\n// Retrieve Unicode data for the current keyboard layout\n//\nstatic bool updateUnicodeDataNS(void)\n{\n    if (_glfw.ns.inputSource)\n    {\n        CFRelease(_glfw.ns.inputSource);\n        _glfw.ns.inputSource = NULL;\n        _glfw.ns.unicodeData = nil;\n    }\n\n    for (_GLFWwindow *window = _glfw.windowListHead;  window;  window = window->next)\n        window->ns.deadKeyState = 0;\n\n    _glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();\n    if (!_glfw.ns.inputSource)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to retrieve keyboard layout input source\");\n        return false;\n    }\n\n    _glfw.ns.unicodeData =\n        TISGetInputSourceProperty(_glfw.ns.inputSource,\n                                  kTISPropertyUnicodeKeyLayoutData);\n    if (!_glfw.ns.unicodeData)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to retrieve keyboard layout Unicode data\");\n        return false;\n    }\n\n    return true;\n}\n\n// Load HIToolbox.framework and the TIS symbols we need from it\n//\nstatic bool initializeTIS(void)\n{\n    // This works only because Cocoa has already loaded it properly\n    _glfw.ns.tis.bundle =\n        CFBundleGetBundleWithIdentifier(CFSTR(\"com.apple.HIToolbox\"));\n    if (!_glfw.ns.tis.bundle)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to load HIToolbox.framework\");\n        return false;\n    }\n\n    CFStringRef* kPropertyUnicodeKeyLayoutData =\n        CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,\n                                      CFSTR(\"kTISPropertyUnicodeKeyLayoutData\"));\n    *(void **)&_glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =\n        CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,\n                                          CFSTR(\"TISCopyCurrentKeyboardLayoutInputSource\"));\n    *(void **)&_glfw.ns.tis.GetInputSourceProperty =\n        CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,\n                                          CFSTR(\"TISGetInputSourceProperty\"));\n    *(void **)&_glfw.ns.tis.GetKbdType =\n        CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,\n                                          CFSTR(\"LMGetKbdType\"));\n\n    if (!kPropertyUnicodeKeyLayoutData ||\n        !TISCopyCurrentKeyboardLayoutInputSource ||\n        !TISGetInputSourceProperty ||\n        !LMGetKbdType)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to load TIS API symbols\");\n        return false;\n    }\n\n    _glfw.ns.tis.kPropertyUnicodeKeyLayoutData =\n        *kPropertyUnicodeKeyLayoutData;\n\n    return updateUnicodeDataNS();\n}\n\nstatic void\ndisplay_reconfigured(CGDirectDisplayID display UNUSED, CGDisplayChangeSummaryFlags flags, void *userInfo UNUSED)\n{\n    if (flags & kCGDisplayBeginConfigurationFlag) {\n        return;\n    }\n    if (flags & kCGDisplaySetModeFlag) {\n        // GPU possibly changed\n    }\n}\n\nstatic NSDictionary<NSString*,NSNumber*> *global_shortcuts = nil;\n\n@interface GLFWHelper : NSObject\n@end\n\n@implementation GLFWHelper\n\n- (void)selectedKeyboardInputSourceChanged:(NSObject* )object\n{\n    (void)object;\n    updateUnicodeDataNS();\n}\n\n- (void)doNothing:(id)object\n{\n    (void)object;\n}\n\n// watch for settings change and rebuild global_shortcuts using key/value observing on NSUserDefaults\n- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context\n{\n    (void)keyPath; (void)object; (void)change; (void)context;\n    if (global_shortcuts != nil) {\n        [global_shortcuts release];\n        global_shortcuts = nil;\n    }\n}\n\n@end // GLFWHelper\n\n// Delegate for application related notifications {{{\n\n@interface GLFWApplicationDelegate : NSObject <NSApplicationDelegate>\n    - (void)handleAppearanceChange;\n@end\n\n@implementation GLFWApplicationDelegate\n\n- (void)applicationDidActivate:(NSNotification *)notification {\n    NSRunningApplication *app = notification.userInfo[NSWorkspaceApplicationKey];\n    if (app && app.processIdentifier != getpid()) {\n        _glfw.ns.previous_front_most_application = app.processIdentifier;\n        debug_rendering(\"Front most application changed to: %s pid: %d\\n\", app.bundleIdentifier.UTF8String, app.processIdentifier)\n    }\n}\n\n- (void)applicationDidBecomeActive:(NSNotification *)notification {\n    (void)notification;\n    // When the application becomes active after switching spaces (e.g., swiping\n    // back from a fullscreen app on another space), macOS may not send\n    // windowDidBecomeKey: for the already-key window. This leaves GLFW thinking\n    // no window has focus (since windowDidResignKey: was sent when leaving).\n    // Ensure GLFW's focus state is updated to match the actual key window.\n    NSWindow *keyWindow = [NSApp keyWindow];\n    if (keyWindow && !_glfw.focusedWindowId) {\n        for (_GLFWwindow *window = _glfw.windowListHead; window; window = window->next) {\n            if (window->ns.object == keyWindow) {\n                if (_glfw.focusedWindowId != window->id) _glfwInputWindowFocus(window, true);\n                break;\n            }\n        }\n    }\n}\n\n- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender\n{\n    (void)sender;\n    if (_glfw.callbacks.application_close) _glfw.callbacks.application_close(0);\n    return NSTerminateCancel;\n}\n\n- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {\n    return YES;\n}\n\nstatic GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL;\n\n- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag\n{\n    (void)sender;\n    if (!handle_reopen_callback) return YES;\n    if (handle_reopen_callback(flag)) return YES;\n    return NO;\n}\n\n- (void)applicationDidChangeScreenParameters:(NSNotification *) notification\n{\n    (void)notification;\n    _GLFWwindow* window;\n\n    for (window = _glfw.windowListHead;  window;  window = window->next)\n    {\n        if (window->context.client != GLFW_NO_API)\n            [window->context.nsgl.object update];\n    }\n\n    _glfwPollMonitorsNS();\n}\n\nstatic GLFWapplicationwillfinishlaunchingfun finish_launching_callback = NULL;\n\n- (void)applicationWillFinishLaunching:(NSNotification *)notification\n{\n    (void)notification;\n    if (_glfw.hints.init.ns.menubar)\n    {\n        // In case we are unbundled, make us a proper UI application\n        [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];\n\n        // Menu bar setup must go between sharedApplication and finishLaunching\n        // in order to properly emulate the behavior of NSApplicationMain\n\n        if ([[NSBundle mainBundle] pathForResource:@\"MainMenu\" ofType:@\"nib\"])\n        {\n            [[NSBundle mainBundle] loadNibNamed:@\"MainMenu\"\n                                          owner:NSApp\n                                topLevelObjects:&_glfw.ns.nibObjects];\n        }\n        else\n            createMenuBar();\n    }\n    if (finish_launching_callback) finish_launching_callback(false);\n}\n\n- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {\n    (void)sender;\n    if (!filename || !_glfw.ns.url_open_callback) return NO;\n    const char *url = NULL;\n    @try {\n        url = [[[NSURL fileURLWithPath:filename] absoluteString] UTF8String];\n    } @catch(NSException *exc) {\n        NSLog(@\"Converting openFile filename: %@ failed with error: %@\", filename, exc.reason);\n        return NO;\n    }\n    if (!url) return NO;\n    return _glfw.ns.url_open_callback(url);\n}\n\n- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames {\n    (void)sender;\n    if (!_glfw.ns.url_open_callback || !filenames) return;\n    for (id x in filenames) {\n        NSString *filename = x;\n        const char *url = NULL;\n        @try {\n            url = [[[NSURL fileURLWithPath:filename] absoluteString] UTF8String];\n        } @catch(NSException *exc) {\n            NSLog(@\"Converting openFiles filename: %@ failed with error: %@\", filename, exc.reason);\n        }\n        if (url) _glfw.ns.url_open_callback(url);\n    }\n}\n\n// Remove openFile and openFiles when the minimum supported macOS version is 10.13\n- (void)application:(NSApplication *)sender openURLs:(NSArray<NSURL *> *)urls\n{\n    (void)sender;\n    if (!_glfw.ns.url_open_callback || !urls) return;\n    for (id x in urls) {\n        NSURL *ns_url = x;\n        const char *url = NULL;\n        @try {\n            url = [[ns_url absoluteString] UTF8String];\n        } @catch(NSException *exc) {\n            NSLog(@\"Converting openURLs url: %@ failed with error: %@\", ns_url, exc.reason);\n        }\n        if (url) _glfw.ns.url_open_callback(url);\n    }\n}\n\nstatic void *AppearanceObservationContext = &AppearanceObservationContext;\nstatic NSDate *application_finished_launching_at = nil;\n\n- (void)applicationDidFinishLaunching:(NSNotification *)notification\n{\n    (void)notification;\n    [[NSApplication sharedApplication] addObserver:self\n        forKeyPath:@\"effectiveAppearance\" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial\n        context:AppearanceObservationContext];\n\n    if (finish_launching_callback) finish_launching_callback(true);\n    [NSApp stop:nil];\n\n    CGDisplayRegisterReconfigurationCallback(display_reconfigured, NULL);\n    _glfwCocoaPostEmptyEvent();\n    application_finished_launching_at = [NSDate date];\n}\n\nGLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {\n    (void)query_if_unintialized;\n    int theme_type = GLFW_COLOR_SCHEME_NO_PREFERENCE;\n    NSAppearance *changedAppearance = NSApp.effectiveAppearance;\n    NSAppearanceName newAppearance = [changedAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];\n    if([newAppearance isEqualToString:NSAppearanceNameDarkAqua]){\n        theme_type = GLFW_COLOR_SCHEME_DARK;\n    } else {\n        theme_type = GLFW_COLOR_SCHEME_LIGHT;\n    }\n    return theme_type;\n}\n\n- (void)observeValueForKeyPath:(NSString *)keyPath\n                      ofObject:(id)object\n                        change:(NSDictionary<NSKeyValueChangeKey, id> *)change\n                       context:(void *)context {\n                           if (context == AppearanceObservationContext) {\n        if ([keyPath isEqualToString:@\"effectiveAppearance\"]) {\n            // The initial call (from NSKeyValueObservingOptionInitial) might happen on a background thread.\n            // Dispatch to the main thread to be safe, especially if updating UI.\n            __block __typeof__(self) weakSelf = self;\n            dispatch_async(dispatch_get_main_queue(), ^{\n                [weakSelf handleAppearanceChange];\n            });\n        }\n    } else {\n        // If the context doesn't match, pass the notification to the superclass.\n        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];\n    }\n}\n\n- (void)handleAppearanceChange {\n    static GLFWColorScheme previously_reported_appearance = GLFW_COLOR_SCHEME_NO_PREFERENCE;\n    GLFWColorScheme new_appearance = glfwGetCurrentSystemColorTheme(true);\n    if (new_appearance != previously_reported_appearance) {\n        previously_reported_appearance = new_appearance;\n        _glfwInputColorScheme(new_appearance, false);\n    }\n}\n\n- (void)applicationWillTerminate:(NSNotification *)aNotification\n{\n    (void)aNotification;\n    CGDisplayRemoveReconfigurationCallback(display_reconfigured, NULL);\n    @try {\n        [[NSApplication sharedApplication] removeObserver:self\n                                               forKeyPath:@\"effectiveAppearance\"\n                                                  context:AppearanceObservationContext];\n    } @catch (NSException * __unused exception) {\n        // Ignore exceptions, which can happen if the observer was never added.\n    }\n}\n\n- (void)applicationDidHide:(NSNotification *)notification\n{\n    (void)notification;\n    int i;\n\n    for (i = 0;  i < _glfw.monitorCount;  i++)\n        _glfwRestoreVideoModeNS(_glfw.monitors[i]);\n}\n\n@end // GLFWApplicationDelegate\n// }}}\n\n\n@interface GLFWApplication : NSApplication\n- (void)tick_callback;\n@end\n\n@implementation GLFWApplication\n- (void)tick_callback\n{\n    _glfwDispatchTickCallback();\n}\n\n@end\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid* _glfwLoadLocalVulkanLoaderNS(void)\n{\n    CFBundleRef bundle = CFBundleGetMainBundle();\n    if (!bundle)\n        return NULL;\n\n    CFURLRef url =\n        CFBundleCopyAuxiliaryExecutableURL(bundle, CFSTR(\"libvulkan.1.dylib\"));\n    if (!url)\n        return NULL;\n\n    char path[PATH_MAX];\n    void* handle = NULL;\n\n    if (CFURLGetFileSystemRepresentation(url, true, (UInt8*) path, sizeof(path) - 1))\n        handle = _glfw_dlopen(path);\n\n    CFRelease(url);\n    return handle;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n/**\n * Apple Symbolic HotKeys Ids\n * To find this symbolic hot keys indices do:\n * 1. open Terminal\n * 2. restore defaults in System Preferences > Keyboard > Shortcuts\n * 3. defaults read com.apple.symbolichotkeys > current.txt\n * 4. enable/disable given symbolic hot key in System Preferences > Keyboard > Shortcuts\n * 5. defaults read com.apple.symbolichotkeys | diff -C 5 current.txt -\n * 6. restore defaults in System Preferences > Keyboard > Shortcuts\n */\ntypedef enum AppleShortcutNames {\n    // launchpad & dock\n    kSHKTurnDockHidingOnOrOff                   = 52,   // Opt, Cmd, D\n    kSHKShowLaunchpad                           = 160,  //\n\n    // display\n    kSHKDecreaseDisplayBrightness1              = 53,   // F14 (Fn)\n    kSHKDecreaseDisplayBrightness2              = 55,   // F14 (Fn, Ctrl)\n    kSHKIncreaseDisplayBrightness1              = 54,   // F15 (Fn)\n    kSHKIncreaseDisplayBrightness2              = 56,   // F15 (Fn, Ctrl)\n\n    // mission control\n    kSHKMissionControl                          = 32,   // Ctrl, Arrow Up\n    kSHKShowNotificationCenter                  = 163,  //\n    kSHKTurnDoNotDisturbOnOrOff                 = 175,  //\n    kSHKApplicationWindows                      = 33,   // Ctrl, Arrow Down\n    kSHKShowDesktop                             = 36,   // F11\n    kSHKMoveLeftASpace                          = 79,   // Ctrl, Arrow Left\n    kSHKMoveRightASpace                         = 81,   // Ctrl, Arrow Right\n    kSHKSwitchToDesktop1                        = 118,  // Ctrl, 1\n    kSHKSwitchToDesktop2                        = 119,  // Ctrl, 2\n    kSHKSwitchToDesktop3                        = 120,  // Ctrl, 3\n    kSHKSwitchToDesktop4                        = 121,  // Ctrl, 4\n    kSHKQuickNote                               = 190,  // Fn, Q\n\n    // keyboard\n    kSHKChangeTheWayTabMovesFocus               = 13,   // Ctrl, F7\n    kSHKTurnKeyboardAccessOnOrOff               = 12,   // Ctrl, F1\n    kSHKMoveFocusToTheMenuBar                   = 7,    // Ctrl, F2\n    kSHKMoveFocusToTheDock                      = 8,    // Ctrl, F3\n    kSHKMoveFocusToActiveOrNextWindow           = 9,    // Ctrl, F4\n    kSHKMoveFocusToTheWindowToolbar             = 10,   // Ctrl, F5\n    kSHKMoveFocusToTheFloatingWindow            = 11,   // Ctrl, F6\n    kSHKMoveFocusToNextWindow                   = 27,   // Cmd, `\n    kSHKMoveFocusToStatusMenus                  = 57,   // Ctrl, F8\n\n    // input sources\n    kSHKSelectThePreviousInputSource            = 60,   // Ctrl, Space bar\n    kSHKSelectNextSourceInInputMenu             = 61,   // Ctrl, Opt, Space bar\n\n    // screenshots\n    kSHKSavePictureOfScreenAsAFile              = 28,   // Shift, Cmd, 3\n    kSHKCopyPictureOfScreenToTheClipboard       = 29,   // Ctrl, Shift, Cmd, 3\n    kSHKSavePictureOfSelectedAreaAsAFile        = 30,   // Shift, Cmd, 4\n    kSHKCopyPictureOfSelectedAreaToTheClipboard = 31,   // Ctrl, Shift, Cmd, 4\n    kSHKScreenshotAndRecordingOptions           = 184,  // Shift, Cmd, 5\n\n    // spotlight\n    kSHKShowSpotlightSearch                     = 64,   // Cmd, Space bar\n    kSHKShowFinderSearchWindow                  = 65,   // Opt, Cmd, Space bar\n\n    // accessibility\n    kSHKTurnZoomOnOrOff                         = 15,   // Opt, Cmd, 8\n    kSHKTurnImageSmoothingOnOrOff               = 23,   // Opt, Cmd, Backslash \"\\\"\n    kSHKZoomOut                                 = 19,   // Opt, Cmd, -\n    kSHKZoomIn                                  = 17,   // Opt, Cmd, =\n    kSHKTurnFocusFollowingOnOrOff               = 179,  //\n\n    kSHKIncreaseContrast                        = 25,   // Ctrl, Opt, Cmd, .\n    kSHKDecreaseContrast                        = 26,   // Ctrl, Opt, Cmd, ,\n\n    kSHKInvertColors                            = 21,   // Ctrl, Opt, Cmd, 8\n    kSHKTurnVoiceOverOnOrOff                    = 59,   // Cmd, F5\n    kSHKShowAccessibilityControls               = 162,  // Opt, Cmd, F5\n\n    // app shortcuts\n    kSHKShowHelpMenu                            = 98,   // Shift, Cmd, /\n\n    // deprecated (Not shown on macOS Monterey)\n    kSHKMoveFocusToTheWindowDrawer              = 51,   // Opt, Cmd, `\n    kSHKShowDashboard                           = 62,   // F12\n    kSHKLookUpInDictionary                      = 70,   // Shift, Cmd, E\n    kSHKHideAndShowFrontRow                     = 73,   // Cmd, Esc\n    kSHKActivateSpaces                          = 75,   // F8\n\n    // unknown\n    kSHKUnknown                                 = 0,    //\n} AppleShortcutNames;\n\nstatic bool\nis_shiftable_shortcut(int scv) {\n    return scv == kSHKMoveFocusToActiveOrNextWindow || scv == kSHKMoveFocusToNextWindow;\n}\n\n#define USEFUL_MODS(x) (x & (NSEventModifierFlagShift | NSEventModifierFlagOption | NSEventModifierFlagCommand | NSEventModifierFlagControl | NSEventModifierFlagFunction))\n\nstatic void\nbuild_global_shortcuts_lookup(void) {\n    // dump these in a terminal with: defaults read com.apple.symbolichotkeys\n    NSMutableDictionary<NSString*, NSNumber*> *temp = [NSMutableDictionary dictionaryWithCapacity:128];  // will be autoreleased\n    NSMutableSet<NSNumber*> *temp_configured = [NSMutableSet setWithCapacity:128];  // will be autoreleased\n    NSMutableSet<NSNumber*> *temp_missing_value = [NSMutableSet setWithCapacity:128];  // will be autoreleased\n    NSDictionary *apple_settings = [[NSUserDefaults standardUserDefaults] persistentDomainForName:@\"com.apple.symbolichotkeys\"];\n    if (apple_settings) {\n        NSDictionary<NSString*, id> *symbolic_hotkeys = [apple_settings objectForKey:@\"AppleSymbolicHotKeys\"];\n        if (symbolic_hotkeys) {\n            for (NSString *key in symbolic_hotkeys) {\n                id obj = symbolic_hotkeys[key];\n                if (![key isKindOfClass:[NSString class]] || ![obj isKindOfClass:[NSDictionary class]]) continue;\n                NSInteger sc = [key integerValue];\n                NSDictionary *sc_value = obj;\n                id enabled = [sc_value objectForKey:@\"enabled\"];\n                if (!enabled || ![enabled isKindOfClass:[NSNumber class]]) continue;\n                [temp_configured addObject:@(sc)];\n                if (![enabled boolValue]) continue;\n                id v = [sc_value objectForKey:@\"value\"];\n                if (!v || ![v isKindOfClass:[NSDictionary class]]) {\n                    if ([enabled boolValue]) [temp_missing_value addObject:@(sc)];\n                    continue;\n                }\n                NSDictionary *value = v;\n                id t = [value objectForKey:@\"type\"];\n                if (!t || ![t isKindOfClass:[NSString class]] || ![t isEqualToString:@\"standard\"]) continue;\n                id p = [value objectForKey:@\"parameters\"];\n                if (!p || ![p isKindOfClass:[NSArray class]] || [(NSArray*)p count] < 2) continue;\n                NSArray<NSNumber*> *parameters = p;\n                NSInteger ch = [parameters[0] isKindOfClass:[NSNumber class]] ? [parameters[0] integerValue] : 0xffff;\n                NSInteger vk = [parameters[1] isKindOfClass:[NSNumber class]] ? [parameters[1] integerValue] : 0xffff;\n                NSEventModifierFlags mods = ([parameters count] > 2 && [parameters[2] isKindOfClass:[NSNumber class]]) ? [parameters[2] unsignedIntegerValue] : 0;\n                mods = USEFUL_MODS(mods);\n                static char buf[64];\n#define S(x, k) snprintf(buf, sizeof(buf) - 1, #x\":%lx:%ld\", (unsigned long)mods, (long)k)\n                if (ch == 0xffff) { if (vk == 0xffff) continue; S(v, vk); } else S(c, ch);\n                temp[@(buf)] = @(sc);\n                // the move to next window shortcuts also respond to the same shortcut + shift\n                if (is_shiftable_shortcut([key intValue]) && !(mods & NSEventModifierFlagShift)) {\n                    mods |= NSEventModifierFlagShift;\n                    if (ch == 0xffff) S(v, vk); else S(c, ch);\n                    temp[@(buf)] = @(sc);\n                }\n#undef S\n            }\n        }\n    }\n\n    // Add global shortcut definitions when the default enabled shortcut is not defined,\n    // or when the default enabled shortcut is not disabled and is missing a value.\n    // Here are the shortcuts that are enabled by default in the standard ANSI (US) layout.\n    // macOS provides separate configurations for some languages or keyboards.\n    // In general, the rules here will not take effect.\n    static char buf[64];\n#define S(i, t, m, k) if ([temp_configured member:@(i)] == nil || [temp_missing_value member:@(i)] != nil) { \\\n        snprintf(buf, sizeof(buf) - 1, #t\":%lx:%ld\", (unsigned long)m, (long)k); \\\n        temp[@(buf)] = @(i); \\\n    }\n\n    // launchpad & dock\n    S(kSHKTurnDockHidingOnOrOff, c, (NSEventModifierFlagOption | NSEventModifierFlagCommand), 'd'); // Opt, Cmd, D\n    // mission control\n    S(kSHKMissionControl, v, NSEventModifierFlagControl, 126); // Ctrl, Arrow Up\n    S(kSHKApplicationWindows, v, NSEventModifierFlagControl, 125); // Ctrl, Arrow Down\n    // keyboard\n    S(kSHKMoveFocusToTheMenuBar, v, NSEventModifierFlagControl, 120); // Ctrl, F2\n    S(kSHKMoveFocusToTheDock, v, NSEventModifierFlagControl, 99); // Ctrl, F3\n    S(kSHKMoveFocusToActiveOrNextWindow, v, NSEventModifierFlagControl, 118); // Ctrl, F4\n    S(kSHKMoveFocusToActiveOrNextWindow, v, (NSEventModifierFlagShift | NSEventModifierFlagControl), 118); // Shift, Ctrl, F4\n    S(kSHKMoveFocusToNextWindow, c, NSEventModifierFlagCommand, 96); // Cmd, `\n    S(kSHKMoveFocusToNextWindow, c, (NSEventModifierFlagShift | NSEventModifierFlagCommand), 96); // Shift, Cmd, `\n    S(kSHKMoveFocusToStatusMenus, v, NSEventModifierFlagControl, 100); // Ctrl, F8\n    // input sources\n    S(kSHKSelectThePreviousInputSource, c, NSEventModifierFlagControl, 32); // Ctrl, Space bar\n    S(kSHKSelectNextSourceInInputMenu, c, (NSEventModifierFlagControl | NSEventModifierFlagOption), 32); // Ctrl, Opt, Space bar\n    // spotlight\n    S(kSHKShowSpotlightSearch, c, NSEventModifierFlagCommand, 32); // Cmd, Space bar\n    S(kSHKShowFinderSearchWindow, c, (NSEventModifierFlagOption | NSEventModifierFlagCommand), 32); // Opt, Cmd, Space bar\n\n#undef S\n    global_shortcuts = [[NSDictionary dictionaryWithDictionary:temp] retain];\n    /* NSLog(@\"global_shortcuts: %@\", global_shortcuts); */\n}\n\nstatic int\nis_active_apple_global_shortcut(NSEvent *event) {\n    if (global_shortcuts == nil) build_global_shortcuts_lookup();\n    NSEventModifierFlags modifierFlags = USEFUL_MODS([event modifierFlags]);\n    static char lookup_key[64];\n#define LOOKUP(t, k) \\\n    snprintf(lookup_key, sizeof(lookup_key) - 1, #t\":%lx:%ld\", (unsigned long)modifierFlags, (long)k); \\\n    NSNumber *sc = global_shortcuts[@(lookup_key)]; \\\n    if (sc != nil) return [sc intValue]; \\\n\n    if ([event.charactersIgnoringModifiers length] == 1) {\n        if (modifierFlags & NSEventModifierFlagShift) {\n            const uint32_t ch_without_shift = vk_to_unicode_key_with_current_layout([event keyCode]);\n            if (ch_without_shift < GLFW_FKEY_FIRST || ch_without_shift > GLFW_FKEY_LAST) {\n                LOOKUP(c, ch_without_shift);\n            }\n        }\n        const unichar ch = [event.charactersIgnoringModifiers characterAtIndex:0];\n        LOOKUP(c, ch);\n    }\n    unsigned short vk = [event keyCode];\n    if (vk != 0xffff) {\n        LOOKUP(v, vk);\n    }\n#undef LOOKUP\n    return kSHKUnknown;\n}\n\nstatic bool\nis_useful_apple_global_shortcut(int sc) {\n    switch(sc) {\n        // launchpad & dock\n        case kSHKTurnDockHidingOnOrOff:                   // Opt, Cmd, D\n        case kSHKShowLaunchpad:                           //\n\n        // display\n        case kSHKDecreaseDisplayBrightness1:              // F14 (Fn)\n        case kSHKDecreaseDisplayBrightness2:              // F14 (Fn, Ctrl)\n        case kSHKIncreaseDisplayBrightness1:              // F15 (Fn)\n        case kSHKIncreaseDisplayBrightness2:              // F14 (Fn, Ctrl)\n\n        // mission control\n        case kSHKMissionControl:                          // Ctrl, Arrow Up\n        case kSHKShowNotificationCenter:                  //\n        case kSHKTurnDoNotDisturbOnOrOff:                 //\n        case kSHKApplicationWindows:                      // Ctrl, Arrow Down\n        case kSHKShowDesktop:                             // F11\n        case kSHKMoveLeftASpace:                          // Ctrl, Arrow Left\n        case kSHKMoveRightASpace:                         // Ctrl, Arrow Right\n        case kSHKSwitchToDesktop1:                        // Ctrl, 1\n        case kSHKSwitchToDesktop2:                        // Ctrl, 2\n        case kSHKSwitchToDesktop3:                        // Ctrl, 3\n        case kSHKSwitchToDesktop4:                        // Ctrl, 4\n        case kSHKQuickNote:                               // Fn, Q\n\n        // keyboard\n        /* case kSHKChangeTheWayTabMovesFocus:               // Ctrl, F7 */\n        /* case kSHKTurnKeyboardAccessOnOrOff:               // Ctrl, F1 */\n        case kSHKMoveFocusToTheMenuBar:                   // Ctrl, F2\n        case kSHKMoveFocusToTheDock:                      // Ctrl, F3\n        case kSHKMoveFocusToActiveOrNextWindow:           // Ctrl, F4\n        /* case kSHKMoveFocusToTheWindowToolbar:             // Ctrl, F5 */\n        /* case kSHKMoveFocusToTheFloatingWindow:            // Ctrl, F6 */\n        case kSHKMoveFocusToNextWindow:                   // Cmd, `\n        case kSHKMoveFocusToStatusMenus:                  // Ctrl, F8\n\n        // input sources\n        case kSHKSelectThePreviousInputSource:            // Ctrl, Space bar\n        case kSHKSelectNextSourceInInputMenu:             // Ctrl, Opt, Space bar\n\n        // screenshots\n        /* case kSHKSavePictureOfScreenAsAFile:              // Shift, Cmd, 3 */\n        /* case kSHKCopyPictureOfScreenToTheClipboard:       // Ctrl, Shift, Cmd, 3 */\n        /* case kSHKSavePictureOfSelectedAreaAsAFile:        // Shift, Cmd, 4 */\n        /* case kSHKCopyPictureOfSelectedAreaToTheClipboard: // Ctrl, Shift, Cmd, 4 */\n        /* case kSHKScreenshotAndRecordingOptions:           // Shift, Cmd, 5 */\n\n        // spotlight\n        case kSHKShowSpotlightSearch:                     // Cmd, Space bar\n        case kSHKShowFinderSearchWindow:                  // Opt, Cmd, Space bar\n\n        // accessibility\n        /* case kSHKTurnZoomOnOrOff:                         // Opt, Cmd, 8 */\n        /* case kSHKTurnImageSmoothingOnOrOff:               // Opt, Cmd, Backslash \"\\\" */\n        /* case kSHKZoomOut:                                 // Opt, Cmd, - */\n        /* case kSHKZoomIn:                                  // Opt, Cmd, = */\n        /* case kSHKTurnFocusFollowingOnOrOff:               // */\n        /* case kSHKIncreaseContrast:                        // Ctrl, Opt, Cmd, . */\n        /* case kSHKDecreaseContrast:                        // Ctrl, Opt, Cmd, , */\n        /* case kSHKInvertColors:                            // Ctrl, Opt, Cmd, 8 */\n        /* case kSHKTurnVoiceOverOnOrOff:                    // Cmd, F5 */\n        /* case kSHKShowAccessibilityControls:               // Opt, Cmd, F5 */\n\n        // app shortcuts\n        /* case kSHKShowHelpMenu:                            // Shift, Cmd, / */\n\n        // deprecated (Not shown on macOS Monterey)\n        /* case kSHKMoveFocusToTheWindowDrawer:              // Opt, Cmd, ` */\n        /* case kSHKShowDashboard:                           // F12 */\n        /* case kSHKLookUpInDictionary:                      // Shift, Cmd, E */\n        /* case kSHKHideAndShowFrontRow:                     // Cmd, Esc */\n        /* case kSHKActivateSpaces:                          // F8 */\n            return true;\n        default:\n            return false;\n    }\n}\n\nstatic bool\nis_apple_jis_layout_function_key(NSEvent *event) {\n    return [event keyCode] == 0x66 /* kVK_JIS_Eisu */ || [event keyCode] == 0x68 /* kVK_JIS_Kana */;\n}\n\nstatic bool\nhas_apple_fn_global_shortcut(void) {\n    NSDictionary *hitoolbox_settings = [[NSUserDefaults standardUserDefaults] persistentDomainForName:@\"com.apple.HIToolbox\"];\n    id obj = [hitoolbox_settings objectForKey:@\"AppleFnUsageType\"];\n    if (![obj isKindOfClass:[NSNumber class]]) return false;\n    // Non-zero AppleFnUsageType means macOS has reserved Fn/Globe for a\n    // system action such as input source switching, emoji picker, or dictation.\n    return [obj integerValue] != 0;\n}\n\nstatic bool\nis_apple_fn_global_shortcut(NSEvent *event) {\n    if ([event keyCode] != 0x3f /* kVK_Function */) return false;\n    NSEventModifierFlags mods = USEFUL_MODS([event modifierFlags]);\n    if (mods != 0 && mods != NSEventModifierFlagFunction) return false;\n    return has_apple_fn_global_shortcut();\n}\n\nGLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) {\n    GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback;\n    handle_reopen_callback = callback;\n    return previous;\n}\n\nGLFWAPI GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback) {\n    GLFWapplicationwillfinishlaunchingfun previous = finish_launching_callback;\n    finish_launching_callback = callback;\n    return previous;\n}\n\nint _glfwPlatformInit(bool *supports_window_occlusion)\n{\n    @autoreleasepool {\n\n    *supports_window_occlusion = true;\n    _glfw.ns.helper = [[GLFWHelper alloc] init];\n\n    [NSThread detachNewThreadSelector:@selector(doNothing:)\n                             toTarget:_glfw.ns.helper\n                           withObject:nil];\n\n    if (NSApp)\n        _glfw.ns.finishedLaunching = true;\n\n    [GLFWApplication sharedApplication];\n\n    _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init];\n    if (_glfw.ns.delegate == nil)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to create application delegate\");\n        return false;\n    }\n\n    [NSApp setDelegate:_glfw.ns.delegate];\n    [[[NSWorkspace sharedWorkspace] notificationCenter]\n        addObserver:_glfw.ns.delegate\n        selector:@selector(applicationDidActivate:)\n        name:NSWorkspaceDidActivateApplicationNotification\n        object:nil];\n    static struct {\n        unsigned short virtual_key_code;\n        NSEventModifierFlags input_source_switch_modifiers;\n        NSTimeInterval timestamp;\n    } last_keydown_shortcut_event;\n    last_keydown_shortcut_event.virtual_key_code = 0xffff;\n    last_keydown_shortcut_event.input_source_switch_modifiers = 0;\n\n    NSEvent* (^keydown_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)\n    {\n        debug_key(\"---------------- key down -------------------\\n\");\n        debug_key(\"%s\\n\", [[event description] UTF8String]);\n        if (!_glfw.ignoreOSKeyboardProcessing && !_glfw.keyboard_grabbed) {\n            // first check if there is a global menu bar shortcut\n            if ([[NSApp mainMenu] performKeyEquivalent:event]) {\n                debug_key(\"keyDown triggered global menu bar action ignoring\\n\");\n                last_keydown_shortcut_event.virtual_key_code = [event keyCode];\n                last_keydown_shortcut_event.input_source_switch_modifiers = 0;\n                last_keydown_shortcut_event.timestamp = [event timestamp];\n                return nil;\n            }\n            // now check if there is a useful apple shortcut\n            int global_shortcut = is_active_apple_global_shortcut(event);\n            if (is_useful_apple_global_shortcut(global_shortcut)) {\n                debug_key(\"keyDown triggered global macOS shortcut ignoring\\n\");\n                last_keydown_shortcut_event.virtual_key_code = [event keyCode];\n                // record the modifier keys if switching to the next input source\n                last_keydown_shortcut_event.input_source_switch_modifiers = (global_shortcut == kSHKSelectNextSourceInInputMenu) ? USEFUL_MODS([event modifierFlags]) : 0;\n                last_keydown_shortcut_event.timestamp = [event timestamp];\n                return event;\n            }\n            // check for JIS keyboard layout function keys\n            if (is_apple_jis_layout_function_key(event)) {\n                debug_key(\"keyDown triggered JIS layout function key ignoring\\n\");\n                last_keydown_shortcut_event.virtual_key_code = [event keyCode];\n                last_keydown_shortcut_event.input_source_switch_modifiers = 0;\n                last_keydown_shortcut_event.timestamp = [event timestamp];\n                return event;\n            }\n        }\n        last_keydown_shortcut_event.virtual_key_code = 0xffff;\n        NSWindow *kw = [NSApp keyWindow];\n        if (kw && kw.contentView) [kw.contentView keyDown:event];\n        else debug_key(\"keyDown ignored as no keyWindow present\\n\");\n        return nil;\n    };\n\n    NSEvent* (^keyup_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)\n    {\n        debug_key(\"----------------- key up --------------------\\n\");\n        debug_key(\"%s\\n\", [[event description] UTF8String]);\n        if (last_keydown_shortcut_event.virtual_key_code != 0xffff && last_keydown_shortcut_event.virtual_key_code == [event keyCode]) {\n            // ignore as the corresponding key down event triggered a menu bar or macOS shortcut\n            last_keydown_shortcut_event.virtual_key_code = 0xffff;\n            debug_key(\"keyUp ignored as corresponds to previous keyDown that triggered a shortcut\\n\");\n            return nil;\n        }\n        NSWindow *kw = [NSApp keyWindow];\n        if (kw && kw.contentView) [kw.contentView keyUp:event];\n        else debug_key(\"keyUp ignored as no keyWindow present\\n\");\n        return nil;\n    };\n\n    NSEvent* (^flags_changed_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)\n    {\n        debug_key(\"-------------- flags changed -----------------\\n\");\n        debug_key(\"%s\\n\", [[event description] UTF8String]);\n        last_keydown_shortcut_event.virtual_key_code = 0xffff;\n        if (!_glfw.ignoreOSKeyboardProcessing && !_glfw.keyboard_grabbed && is_apple_fn_global_shortcut(event)) {\n            debug_key(\"flagsChanged triggered global fn shortcut ignoring\\n\");\n            return event;\n        }\n        // switching to the next input source is only confirmed when all modifier keys are released\n        if (last_keydown_shortcut_event.input_source_switch_modifiers) {\n            if (!([event modifierFlags] & last_keydown_shortcut_event.input_source_switch_modifiers))\n                last_keydown_shortcut_event.input_source_switch_modifiers = 0;\n            return event;\n        }\n        NSWindow *kw = [NSApp keyWindow];\n        if (kw && kw.contentView) [kw.contentView flagsChanged:event];\n        else debug_key(\"flagsChanged ignored as no keyWindow present\\n\");\n        return nil;\n    };\n\n    _glfw.ns.keyUpMonitor =\n        [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp\n                                              handler:keyup_block];\n    _glfw.ns.keyDownMonitor =\n        [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown\n                                              handler:keydown_block];\n    _glfw.ns.flagsChangedMonitor =\n        [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskFlagsChanged\n                                              handler:flags_changed_block];\n\n    if (_glfw.hints.init.ns.chdir)\n        changeToResourcesDirectory();\n\n    [[NSUserDefaults standardUserDefaults] registerDefaults:@{\n        // Press and Hold prevents some keys from emitting repeated characters\n        @\"ApplePressAndHoldEnabled\": @NO,\n        // Dont generate openFile events from command line arguments\n        @\"NSTreatUnknownArgumentsAsOpen\": @\"NO\",\n        // This Tahoe nonsense causes slowdowns in some situations, see for example:\n        // https://issues.chromium.org/issues/452372350 it doesnt affect\n        // autofill via Edit->Autofill\n        @\"NSAutoFillHeuristicControllerEnabled\" : @NO,\n    }];\n\n    NSUserDefaults *apple_settings = [[NSUserDefaults alloc] initWithSuiteName:@\"com.apple.symbolichotkeys\"];\n    [apple_settings addObserver:_glfw.ns.helper\n                     forKeyPath:@\"AppleSymbolicHotKeys\"\n                        options:NSKeyValueObservingOptionNew\n                        context:NULL];\n    _glfw.ns.appleSettings = apple_settings;\n\n    [[NSNotificationCenter defaultCenter]\n        addObserver:_glfw.ns.helper\n           selector:@selector(selectedKeyboardInputSourceChanged:)\n               name:NSTextInputContextKeyboardSelectionDidChangeNotification\n             object:nil];\n\n    _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);\n    if (!_glfw.ns.eventSource)\n        return false;\n\n    CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0);\n\n    if (!initializeTIS())\n        return false;\n\n    _glfwPollMonitorsNS();\n    return true;\n\n    } // autoreleasepool\n}\nstatic NSDate*\nget_process_start_time(pid_t pid) {\n    struct kinfo_proc kp;\n    size_t len = sizeof(kp);\n    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };\n\n    if (sysctl(mib, 4, &kp, &len, NULL, 0) == 0 && len == sizeof(kp)) {\n        struct timeval start_tv = kp.kp_proc.p_starttime;\n        time_t start_sec = start_tv.tv_sec;\n        suseconds_t start_usec = start_tv.tv_usec;\n        NSTimeInterval t = (NSTimeInterval)start_sec + ((NSTimeInterval)start_usec / 1000000.0);\n        return [NSDate dateWithTimeIntervalSince1970:t];\n    }\n    return nil;\n}\n\nvoid _glfwPlatformTerminate(void)\n{\n    @autoreleasepool {\n\n    // Kill the AutoFill helper process that macOS Tahoe starts and fails to\n    // shutdown on application exit, see https://github.com/kovidgoyal/kitty/issues/9299\n    // Only kill helpers that were launched within a few seconds of this process to\n    // avoid killing helpers from other processes. This is obviously not robust\n    // but since Apple cant design its way out of a paper bag, it's the best we\n    // can do.\n    if (application_finished_launching_at != nil) {\n        for (NSRunningApplication *app in [[NSWorkspace sharedWorkspace] runningApplications]) {\n            if ([app.bundleIdentifier isEqualToString:@\"com.apple.SafariPlatformSupport.Helper\"] &&\n                [[app.localizedName lowercaseString] containsString:@\"autofill (kitty)\"]) {\n                NSDate *st = get_process_start_time(app.processIdentifier);\n                if (st != nil) {\n                    NSTimeInterval timeDifference = [application_finished_launching_at timeIntervalSinceDate:st];\n                    [st release];\n                    if (fabs(timeDifference) <= 5) [app forceTerminate];\n                }\n            }\n        }\n        [application_finished_launching_at release]; application_finished_launching_at = nil;\n    }\n\n    _glfwClearDisplayLinks();\n\n    if (_glfw.ns.inputSource)\n    {\n        CFRelease(_glfw.ns.inputSource);\n        _glfw.ns.inputSource = NULL;\n        _glfw.ns.unicodeData = nil;\n    }\n\n    if (_glfw.ns.eventSource)\n    {\n        CFRelease(_glfw.ns.eventSource);\n        _glfw.ns.eventSource = NULL;\n    }\n\n    if (_glfw.ns.delegate)\n    {\n        [NSApp setDelegate:nil];\n        [_glfw.ns.delegate release];\n        _glfw.ns.delegate = nil;\n    }\n\n    if (_glfw.ns.helper)\n    {\n        [[NSNotificationCenter defaultCenter]\n            removeObserver:_glfw.ns.helper\n                      name:NSTextInputContextKeyboardSelectionDidChangeNotification\n                    object:nil];\n        [[NSNotificationCenter defaultCenter]\n            removeObserver:_glfw.ns.helper];\n        if (_glfw.ns.appleSettings)\n            [_glfw.ns.appleSettings removeObserver:_glfw.ns.helper forKeyPath:@\"AppleSymbolicHotKeys\"];\n        [_glfw.ns.helper release];\n        _glfw.ns.helper = nil;\n    }\n\n    if (_glfw.ns.keyUpMonitor)\n        [NSEvent removeMonitor:_glfw.ns.keyUpMonitor];\n    if (_glfw.ns.keyDownMonitor)\n        [NSEvent removeMonitor:_glfw.ns.keyDownMonitor];\n    if (_glfw.ns.flagsChangedMonitor)\n        [NSEvent removeMonitor:_glfw.ns.flagsChangedMonitor];\n\n    if (_glfw.ns.appleSettings != nil) {\n        [_glfw.ns.appleSettings release];\n        _glfw.ns.appleSettings = nil;\n    }\n\n    _glfwTerminateNSGL();\n    if (global_shortcuts != nil) { [global_shortcuts release]; global_shortcuts = nil; }\n\n    } // autoreleasepool\n}\n\nconst char* _glfwPlatformGetVersionString(void)\n{\n    return _GLFW_VERSION_NUMBER \" Cocoa NSGL EGL OSMesa\"\n#if defined(_GLFW_BUILD_DLL)\n        \" dynamic\"\n#endif\n        ;\n}\n\nstatic GLFWtickcallback tick_callback = NULL;\nstatic void* tick_callback_data = NULL;\nstatic bool tick_callback_requested = false;\nstatic pthread_t main_thread;\nstatic NSLock *tick_lock = NULL;\n\n\nvoid _glfwDispatchTickCallback(void) {\n    if (tick_lock && tick_callback) {\n        while(true) {\n            bool do_call = false;\n            [tick_lock lock];\n            if (tick_callback_requested) { do_call = true; tick_callback_requested = false; }\n            [tick_lock unlock];\n            if (do_call) tick_callback(tick_callback_data);\n            else break;\n        }\n    }\n}\n\nstatic void\nrequest_tick_callback(void) {\n    if (!tick_callback_requested) {\n        tick_callback_requested = true;\n        [NSApp performSelectorOnMainThread:@selector(tick_callback) withObject:nil waitUntilDone:NO];\n    }\n}\n\nvoid _glfwPlatformPostEmptyEvent(void)\n{\n    if (pthread_equal(pthread_self(), main_thread)) {\n        request_tick_callback();\n    } else if (tick_lock) {\n        [tick_lock lock];\n        request_tick_callback();\n        [tick_lock unlock];\n    }\n}\n\n\nvoid _glfwPlatformStopMainLoop(void) {\n    [NSApp stop:nil];\n    _glfwCocoaPostEmptyEvent();\n}\n\nvoid _glfwPlatformRunMainLoop(GLFWtickcallback callback, void* data) {\n    main_thread = pthread_self();\n    tick_callback = callback;\n    tick_callback_data = data;\n    tick_lock = [NSLock new];\n    [NSApp run];\n    [tick_lock release];\n    tick_lock = NULL;\n    tick_callback = NULL;\n    tick_callback_data = NULL;\n}\n\n\ntypedef struct {\n    NSTimer *os_timer;\n    unsigned long long id;\n    bool repeats;\n    monotonic_t interval;\n    GLFWuserdatafun callback;\n    void *callback_data;\n    GLFWuserdatafun free_callback_data;\n} Timer;\n\nstatic Timer timers[128] = {{0}};\nstatic size_t num_timers = 0;\n\nstatic void\nremove_timer_at(size_t idx) {\n    if (idx < num_timers) {\n        Timer *t = timers + idx;\n        if (t->os_timer) { [t->os_timer invalidate]; t->os_timer = NULL; }\n        if (t->callback_data && t->free_callback_data) { t->free_callback_data(t->id, t->callback_data); t->callback_data = NULL; }\n        remove_i_from_array(timers, idx, num_timers);\n    }\n}\n\nstatic void schedule_timer(Timer *t) {\n    t->os_timer = [NSTimer scheduledTimerWithTimeInterval:monotonic_t_to_s_double(t->interval) repeats:(t->repeats ? YES: NO) block:^(NSTimer *os_timer) {\n        for (size_t i = 0; i < num_timers; i++) {\n            if (timers[i].os_timer == os_timer) {\n                timers[i].callback(timers[i].id, timers[i].callback_data);\n                if (!timers[i].repeats) remove_timer_at(i);\n                break;\n            }\n        }\n    }];\n}\n\nunsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback) {\n    static unsigned long long timer_counter = 0;\n    if (num_timers >= sizeof(timers)/sizeof(timers[0]) - 1) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Too many timers added\");\n        return 0;\n    }\n    Timer *t = timers + num_timers++;\n    t->id = ++timer_counter;\n    t->repeats = repeats;\n    t->interval = interval;\n    t->callback = callback;\n    t->callback_data = callback_data;\n    t->free_callback_data = free_callback;\n    schedule_timer(t);\n    return timer_counter;\n}\n\nvoid _glfwPlatformRemoveTimer(unsigned long long timer_id) {\n    for (size_t i = 0; i < num_timers; i++) {\n        if (timers[i].id == timer_id) {\n            remove_timer_at(i);\n            break;\n        }\n    }\n}\n\nvoid _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {\n    for (size_t i = 0; i < num_timers; i++) {\n        if (timers[i].id == timer_id) {\n            Timer *t = timers + i;\n            if (t->os_timer) { [t->os_timer invalidate]; t->os_timer = NULL; }\n            t->interval = interval;\n            if (enabled) schedule_timer(t);\n            break;\n        }\n    }\n}\n\nvoid _glfwPlatformInputColorScheme(GLFWColorScheme appearance UNUSED) { }\nbool _glfwPlatformGrabKeyboard(bool grab UNUSED) { return true; /* directly uses _glfw.keyboard_grabbed */ }\n"
  },
  {
    "path": "glfw/cocoa_joystick.h",
    "content": "//========================================================================\n// GLFW 3.4 Cocoa - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#include <IOKit/IOKitLib.h>\n#include <IOKit/IOCFPlugIn.h>\n#include <IOKit/hid/IOHIDLib.h>\n#include <IOKit/hid/IOHIDKeys.h>\n\n#define _GLFW_PLATFORM_JOYSTICK_STATE         _GLFWjoystickNS ns\n#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE\n\n#define _GLFW_PLATFORM_MAPPING_NAME \"Mac OS X\"\n\n// Cocoa-specific per-joystick data\n//\ntypedef struct _GLFWjoystickNS\n{\n    IOHIDDeviceRef      device;\n    CFMutableArrayRef   axes;\n    CFMutableArrayRef   buttons;\n    CFMutableArrayRef   hats;\n} _GLFWjoystickNS;\n\n"
  },
  {
    "path": "glfw/cocoa_joystick.m",
    "content": "//========================================================================\n// GLFW 3.4 Cocoa - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>\n// Copyright (c) 2012 Torsten Walluhn <tw@mad-cad.net>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n#include <unistd.h>\n#include <ctype.h>\n#include <string.h>\n\n#include <mach/mach.h>\n#include <mach/mach_error.h>\n\n#include <CoreFoundation/CoreFoundation.h>\n#include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>\n\n\n// Joystick element information\n//\ntypedef struct _GLFWjoyelementNS\n{\n    IOHIDElementRef native;\n    uint32_t        usage;\n    int             index;\n    long            minimum;\n    long            maximum;\n\n} _GLFWjoyelementNS;\n\n\n// Returns the value of the specified element of the specified joystick\n//\nstatic long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element)\n{\n    IOHIDValueRef valueRef;\n    long value = 0;\n\n    if (js->ns.device)\n    {\n        if (IOHIDDeviceGetValue(js->ns.device,\n                                element->native,\n                                &valueRef) == kIOReturnSuccess)\n        {\n            value = IOHIDValueGetIntegerValue(valueRef);\n        }\n    }\n\n    return value;\n}\n\n// Comparison function for matching the SDL element order\n//\nstatic CFComparisonResult compareElements(const void* fp,\n                                          const void* sp,\n                                          void* user UNUSED)\n{\n    const _GLFWjoyelementNS* fe = fp;\n    const _GLFWjoyelementNS* se = sp;\n    if (fe->usage < se->usage)\n        return kCFCompareLessThan;\n    if (fe->usage > se->usage)\n        return kCFCompareGreaterThan;\n    if (fe->index < se->index)\n        return kCFCompareLessThan;\n    if (fe->index > se->index)\n        return kCFCompareGreaterThan;\n    return kCFCompareEqualTo;\n}\n\n// Removes the specified joystick\n//\nstatic void closeJoystick(_GLFWjoystick* js)\n{\n    int i;\n\n    if (!js->present)\n        return;\n\n    for (i = 0;  i < CFArrayGetCount(js->ns.axes);  i++)\n        free((void*) CFArrayGetValueAtIndex(js->ns.axes, i));\n    CFRelease(js->ns.axes);\n\n    for (i = 0;  i < CFArrayGetCount(js->ns.buttons);  i++)\n        free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i));\n    CFRelease(js->ns.buttons);\n\n    for (i = 0;  i < CFArrayGetCount(js->ns.hats);  i++)\n        free((void*) CFArrayGetValueAtIndex(js->ns.hats, i));\n    CFRelease(js->ns.hats);\n\n    _glfwFreeJoystick(js);\n    _glfwInputJoystick(js, GLFW_DISCONNECTED);\n}\n\n// Callback for user-initiated joystick addition\n//\nstatic void matchCallback(void* context UNUSED,\n                          IOReturn result UNUSED,\n                          void* sender UNUSED,\n                          IOHIDDeviceRef device)\n{\n    int jid;\n    char name[256];\n    char guid[33];\n    CFIndex i;\n    CFTypeRef property;\n    uint32_t vendor = 0, product = 0, version = 0;\n    _GLFWjoystick* js;\n    CFMutableArrayRef axes, buttons, hats;\n\n    for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)\n    {\n        if (_glfw.joysticks[jid].ns.device == device)\n            return;\n    }\n\n    axes    = CFArrayCreateMutable(NULL, 0, NULL);\n    buttons = CFArrayCreateMutable(NULL, 0, NULL);\n    hats    = CFArrayCreateMutable(NULL, 0, NULL);\n\n    property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));\n    if (property)\n    {\n        CFStringGetCString(property,\n                           name,\n                           sizeof(name),\n                           kCFStringEncodingUTF8);\n    }\n    else\n        strncpy(name, \"Unknown\", sizeof(name));\n\n    property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));\n    if (property)\n        CFNumberGetValue(property, kCFNumberSInt32Type, &vendor);\n\n    property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));\n    if (property)\n        CFNumberGetValue(property, kCFNumberSInt32Type, &product);\n\n    property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey));\n    if (property)\n        CFNumberGetValue(property, kCFNumberSInt32Type, &version);\n\n    // Generate a joystick GUID that matches the SDL 2.0.5+ one\n    if (vendor && product)\n    {\n        snprintf(guid, sizeof(guid), \"03000000%02x%02x0000%02x%02x0000%02x%02x0000\",\n                (uint8_t) vendor, (uint8_t) (vendor >> 8),\n                (uint8_t) product, (uint8_t) (product >> 8),\n                (uint8_t) version, (uint8_t) (version >> 8));\n    }\n    else\n    {\n        snprintf(guid, sizeof(guid), \"05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00\",\n                name[0], name[1], name[2], name[3],\n                name[4], name[5], name[6], name[7],\n                name[8], name[9], name[10]);\n    }\n\n    CFArrayRef elements =\n        IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);\n\n    for (i = 0;  i < CFArrayGetCount(elements);  i++)\n    {\n        IOHIDElementRef native = (IOHIDElementRef)\n            CFArrayGetValueAtIndex(elements, i);\n        if (CFGetTypeID(native) != IOHIDElementGetTypeID())\n            continue;\n\n        const IOHIDElementType type = IOHIDElementGetType(native);\n        if ((type != kIOHIDElementTypeInput_Axis) &&\n            (type != kIOHIDElementTypeInput_Button) &&\n            (type != kIOHIDElementTypeInput_Misc))\n        {\n            continue;\n        }\n\n        CFMutableArrayRef target = NULL;\n\n        const uint32_t usage = IOHIDElementGetUsage(native);\n        const uint32_t page = IOHIDElementGetUsagePage(native);\n        if (page == kHIDPage_GenericDesktop)\n        {\n            switch (usage)\n            {\n                case kHIDUsage_GD_X:\n                case kHIDUsage_GD_Y:\n                case kHIDUsage_GD_Z:\n                case kHIDUsage_GD_Rx:\n                case kHIDUsage_GD_Ry:\n                case kHIDUsage_GD_Rz:\n                case kHIDUsage_GD_Slider:\n                case kHIDUsage_GD_Dial:\n                case kHIDUsage_GD_Wheel:\n                    target = axes;\n                    break;\n                case kHIDUsage_GD_Hatswitch:\n                    target = hats;\n                    break;\n                case kHIDUsage_GD_DPadUp:\n                case kHIDUsage_GD_DPadRight:\n                case kHIDUsage_GD_DPadDown:\n                case kHIDUsage_GD_DPadLeft:\n                case kHIDUsage_GD_SystemMainMenu:\n                case kHIDUsage_GD_Select:\n                case kHIDUsage_GD_Start:\n                    target = buttons;\n                    break;\n            }\n        }\n        else if (page == kHIDPage_Simulation)\n        {\n            switch (usage)\n            {\n                case kHIDUsage_Sim_Accelerator:\n                case kHIDUsage_Sim_Brake:\n                case kHIDUsage_Sim_Throttle:\n                case kHIDUsage_Sim_Rudder:\n                case kHIDUsage_Sim_Steering:\n                    target = axes;\n                    break;\n            }\n        }\n        else if (page == kHIDPage_Button || page == kHIDPage_Consumer)\n            target = buttons;\n\n        if (target)\n        {\n            _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS));\n            element->native  = native;\n            element->usage   = usage;\n            element->index   = (int) CFArrayGetCount(target);\n            element->minimum = IOHIDElementGetLogicalMin(native);\n            element->maximum = IOHIDElementGetLogicalMax(native);\n            CFArrayAppendValue(target, element);\n        }\n    }\n\n    CFRelease(elements);\n\n    CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)),\n                      compareElements, NULL);\n    CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)),\n                      compareElements, NULL);\n    CFArraySortValues(hats, CFRangeMake(0, CFArrayGetCount(hats)),\n                      compareElements, NULL);\n\n    js = _glfwAllocJoystick(name, guid,\n                            (int) CFArrayGetCount(axes),\n                            (int) CFArrayGetCount(buttons),\n                            (int) CFArrayGetCount(hats));\n\n    js->ns.device  = device;\n    js->ns.axes    = axes;\n    js->ns.buttons = buttons;\n    js->ns.hats    = hats;\n\n    _glfwInputJoystick(js, GLFW_CONNECTED);\n}\n\n// Callback for user-initiated joystick removal\n//\nstatic void removeCallback(void* context UNUSED,\n                           IOReturn result UNUSED,\n                           void* sender UNUSED,\n                           IOHIDDeviceRef device)\n{\n    int jid;\n\n    for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)\n    {\n        if (_glfw.joysticks[jid].ns.device == device)\n        {\n            closeJoystick(_glfw.joysticks + jid);\n            break;\n        }\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nbool _glfwPlatformInitJoysticks(void)\n{\n    CFMutableArrayRef matching;\n    const long usages[] =\n    {\n        kHIDUsage_GD_Joystick,\n        kHIDUsage_GD_GamePad,\n        kHIDUsage_GD_MultiAxisController\n    };\n\n    _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault,\n                                             kIOHIDOptionsTypeNone);\n\n    matching = CFArrayCreateMutable(kCFAllocatorDefault,\n                                    0,\n                                    &kCFTypeArrayCallBacks);\n    if (!matching)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Cocoa: Failed to create array\");\n        return false;\n    }\n\n    for (size_t i = 0;  i < sizeof(usages) / sizeof(long);  i++)\n    {\n        const long page = kHIDPage_GenericDesktop;\n\n        CFMutableDictionaryRef dict =\n            CFDictionaryCreateMutable(kCFAllocatorDefault,\n                                      0,\n                                      &kCFTypeDictionaryKeyCallBacks,\n                                      &kCFTypeDictionaryValueCallBacks);\n        if (!dict)\n            continue;\n\n        CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault,\n                                             kCFNumberLongType,\n                                             &page);\n        CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault,\n                                              kCFNumberLongType,\n                                              &usages[i]);\n        if (pageRef && usageRef)\n        {\n            CFDictionarySetValue(dict,\n                                 CFSTR(kIOHIDDeviceUsagePageKey),\n                                 pageRef);\n            CFDictionarySetValue(dict,\n                                 CFSTR(kIOHIDDeviceUsageKey),\n                                 usageRef);\n            CFArrayAppendValue(matching, dict);\n        }\n\n        if (pageRef)\n            CFRelease(pageRef);\n        if (usageRef)\n            CFRelease(usageRef);\n\n        CFRelease(dict);\n    }\n\n    IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching);\n    CFRelease(matching);\n\n    IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager,\n                                               &matchCallback, NULL);\n    IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager,\n                                              &removeCallback, NULL);\n    IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager,\n                                    CFRunLoopGetMain(),\n                                    kCFRunLoopDefaultMode);\n    IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone);\n\n    // Execute the run loop once in order to register any initially-attached\n    // joysticks\n    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);\n    return true;\n}\n\nvoid _glfwPlatformTerminateJoysticks(void)\n{\n    int jid;\n\n    for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)\n        closeJoystick(_glfw.joysticks + jid);\n\n    if (_glfw.ns.hidManager)\n    {\n        CFRelease(_glfw.ns.hidManager);\n        _glfw.ns.hidManager = NULL;\n    }\n}\n\n\nint _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)\n{\n    if (mode & _GLFW_POLL_AXES)\n    {\n        CFIndex i;\n\n        for (i = 0;  i < CFArrayGetCount(js->ns.axes);  i++)\n        {\n            _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)\n                CFArrayGetValueAtIndex(js->ns.axes, i);\n\n            const long raw = getElementValue(js, axis);\n            // Perform auto calibration\n            if (raw < axis->minimum)\n                axis->minimum = raw;\n            if (raw > axis->maximum)\n                axis->maximum = raw;\n\n            const long size = axis->maximum - axis->minimum;\n            if (size == 0)\n                _glfwInputJoystickAxis(js, (int) i, 0.f);\n            else\n            {\n                const float value = (2.f * (raw - axis->minimum) / size) - 1.f;\n                _glfwInputJoystickAxis(js, (int) i, value);\n            }\n        }\n    }\n\n    if (mode & _GLFW_POLL_BUTTONS)\n    {\n        CFIndex i;\n\n        for (i = 0;  i < CFArrayGetCount(js->ns.buttons);  i++)\n        {\n            _GLFWjoyelementNS* button = (_GLFWjoyelementNS*)\n                CFArrayGetValueAtIndex(js->ns.buttons, i);\n            const char value = getElementValue(js, button) - button->minimum;\n            const int state = (value > 0) ? GLFW_PRESS : GLFW_RELEASE;\n            _glfwInputJoystickButton(js, (int) i, state);\n        }\n\n        for (i = 0;  i < CFArrayGetCount(js->ns.hats);  i++)\n        {\n            const int states[9] =\n            {\n                GLFW_HAT_UP,\n                GLFW_HAT_RIGHT_UP,\n                GLFW_HAT_RIGHT,\n                GLFW_HAT_RIGHT_DOWN,\n                GLFW_HAT_DOWN,\n                GLFW_HAT_LEFT_DOWN,\n                GLFW_HAT_LEFT,\n                GLFW_HAT_LEFT_UP,\n                GLFW_HAT_CENTERED\n            };\n\n            _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)\n                CFArrayGetValueAtIndex(js->ns.hats, i);\n            long state = getElementValue(js, hat) - hat->minimum;\n            if (state < 0 || state > 8)\n                state = 8;\n\n            _glfwInputJoystickHat(js, (int) i, states[state]);\n        }\n    }\n\n    return js->present;\n}\n\nvoid _glfwPlatformUpdateGamepadGUID(char* guid)\n{\n    if ((strncmp(guid + 4, \"000000000000\", 12) == 0) &&\n        (strncmp(guid + 20, \"000000000000\", 12) == 0))\n    {\n        char original[33];\n        strncpy(original, guid, sizeof(original) - 1);\n        snprintf(guid, 33, \"03000000%.4s0000%.4s000000000000\",\n                original, original + 16);\n    }\n}\n"
  },
  {
    "path": "glfw/cocoa_monitor.m",
    "content": "//========================================================================\n// GLFW 3.4 macOS - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n#include <stdlib.h>\n#include <limits.h>\n#include <math.h>\n#include <assert.h>\n\n#include <IOKit/graphics/IOGraphicsLib.h>\n#include <CoreVideo/CVBase.h>\n#include <ApplicationServices/ApplicationServices.h>\n\n\n// Get the name of the specified display, or NULL\n//\nstatic char*\ngetDisplayName(CGDirectDisplayID displayID, NSScreen* screen)\n{\n    // IOKit doesn't work on Apple Silicon anymore\n    // Luckily, 10.15 introduced -[NSScreen localizedName].\n    // Use it if available, and fall back to IOKit otherwise.\n    if (screen)\n    {\n        if ([screen respondsToSelector:@selector(localizedName)])\n        {\n            NSString* name = [screen valueForKey:@\"localizedName\"];\n            if (name) {\n                return _glfw_strdup([name UTF8String]);\n            }\n        }\n    }\n    io_iterator_t it;\n    io_service_t service;\n    CFDictionaryRef info;\n\n    if (IOServiceGetMatchingServices(0,\n                                     IOServiceMatching(\"IODisplayConnect\"),\n                                     &it) != 0)\n    {\n        // This may happen if a desktop Mac is running headless\n        return NULL;\n    }\n\n    while ((service = IOIteratorNext(it)) != 0)\n    {\n        info = IODisplayCreateInfoDictionary(service,\n                                             kIODisplayOnlyPreferredName);\n\n        CFNumberRef vendorIDRef =\n            CFDictionaryGetValue(info, CFSTR(kDisplayVendorID));\n        CFNumberRef productIDRef =\n            CFDictionaryGetValue(info, CFSTR(kDisplayProductID));\n        if (!vendorIDRef || !productIDRef)\n        {\n            CFRelease(info);\n            continue;\n        }\n\n        unsigned int vendorID, productID;\n        CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID);\n        CFNumberGetValue(productIDRef, kCFNumberIntType, &productID);\n\n        if (CGDisplayVendorNumber(displayID) == vendorID &&\n            CGDisplayModelNumber(displayID) == productID)\n        {\n            // Info dictionary is used and freed below\n            break;\n        }\n\n        CFRelease(info);\n    }\n\n    IOObjectRelease(it);\n\n    if (!service)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to find service port for display\");\n        return NULL;\n    }\n\n    CFDictionaryRef names =\n        CFDictionaryGetValue(info, CFSTR(kDisplayProductName));\n\n    CFStringRef nameRef;\n\n    if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR(\"en_US\"),\n                                                 (const void**) &nameRef))\n    {\n        // This may happen if a desktop Mac is running headless\n        CFRelease(info);\n        return NULL;\n    }\n\n    const CFIndex size =\n        CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef),\n                                          kCFStringEncodingUTF8);\n    char* name = calloc(size + 1, 1);\n    CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8);\n\n    CFRelease(info);\n    return name;\n}\n\n// Check whether the display mode should be included in enumeration\n//\nstatic bool modeIsGood(CGDisplayModeRef mode)\n{\n    uint32_t flags = CGDisplayModeGetIOFlags(mode);\n\n    if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag))\n        return false;\n    if (flags & kDisplayModeInterlacedFlag)\n        return false;\n    if (flags & kDisplayModeStretchedFlag)\n        return false;\n\n#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100\n    CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);\n    if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) &&\n        CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0))\n    {\n        CFRelease(format);\n        return false;\n    }\n\n    CFRelease(format);\n#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */\n    return true;\n}\n\n// Convert Core Graphics display mode to GLFW video mode\n//\nstatic GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,\n                                            double fallbackRefreshRate)\n{\n    GLFWvidmode result;\n    result.width = (int) CGDisplayModeGetWidth(mode);\n    result.height = (int) CGDisplayModeGetHeight(mode);\n    result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode));\n\n    if (result.refreshRate == 0)\n        result.refreshRate = (int) round(fallbackRefreshRate);\n\n#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100\n    CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);\n    if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0)\n    {\n        result.redBits = 5;\n        result.greenBits = 5;\n        result.blueBits = 5;\n    }\n    else\n#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */\n    {\n        result.redBits = 8;\n        result.greenBits = 8;\n        result.blueBits = 8;\n    }\n\n#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100\n    CFRelease(format);\n#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */\n    return result;\n}\n\n// Starts reservation for display fading\n//\nstatic CGDisplayFadeReservationToken beginFadeReservation(void)\n{\n    CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken;\n\n    if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess)\n    {\n        CGDisplayFade(token, 0.3,\n                      kCGDisplayBlendNormal,\n                      kCGDisplayBlendSolidColor,\n                      0.0, 0.0, 0.0,\n                      TRUE);\n    }\n\n    return token;\n}\n\n// Ends reservation for display fading\n//\nstatic void endFadeReservation(CGDisplayFadeReservationToken token)\n{\n    if (token != kCGDisplayFadeReservationInvalidToken)\n    {\n        CGDisplayFade(token, 0.5,\n                      kCGDisplayBlendSolidColor,\n                      kCGDisplayBlendNormal,\n                      0.0, 0.0, 0.0,\n                      FALSE);\n        CGReleaseDisplayFadeReservation(token);\n    }\n}\n\n// Finds and caches the NSScreen corresponding to the specified monitor\n//\nstatic bool refreshMonitorScreen(_GLFWmonitor* monitor)\n{\n    if (monitor->ns.screen)\n        return true;\n\n    for (NSScreen* screen in [NSScreen screens])\n    {\n        NSNumber* displayID = [screen deviceDescription][@\"NSScreenNumber\"];\n\n        // HACK: Compare unit numbers instead of display IDs to work around\n        //       display replacement on machines with automatic graphics\n        //       switching\n        if (monitor->ns.unitNumber == CGDisplayUnitNumber([displayID unsignedIntValue]))\n        {\n            monitor->ns.screen = screen;\n            return true;\n        }\n    }\n\n    _glfwInputError(GLFW_PLATFORM_ERROR, \"Cocoa: Failed to find a screen for monitor\");\n    return false;\n}\n\n// Returns the display refresh rate queried from the I/O registry\n//\nstatic double getFallbackRefreshRate(CGDirectDisplayID displayID)\n{\n    double refreshRate = 60.0;\n\n    io_iterator_t it;\n    io_service_t service;\n\n    if (IOServiceGetMatchingServices(0,\n                                     IOServiceMatching(\"IOFramebuffer\"),\n                                     &it) != 0)\n    {\n        return refreshRate;\n    }\n\n    while ((service = IOIteratorNext(it)) != 0)\n    {\n        const CFNumberRef indexRef =\n            IORegistryEntryCreateCFProperty(service,\n                                            CFSTR(\"IOFramebufferOpenGLIndex\"),\n                                            kCFAllocatorDefault,\n                                            kNilOptions);\n        if (!indexRef)\n            continue;\n\n        uint32_t index = 0;\n        CFNumberGetValue(indexRef, kCFNumberIntType, &index);\n        CFRelease(indexRef);\n\n        if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID)\n            continue;\n\n        const CFNumberRef clockRef =\n            IORegistryEntryCreateCFProperty(service,\n                                            CFSTR(\"IOFBCurrentPixelClock\"),\n                                            kCFAllocatorDefault,\n                                            kNilOptions);\n        const CFNumberRef countRef =\n            IORegistryEntryCreateCFProperty(service,\n                                            CFSTR(\"IOFBCurrentPixelCount\"),\n                                            kCFAllocatorDefault,\n                                            kNilOptions);\n\n        uint32_t clock = 0, count = 0;\n\n        if (clockRef)\n        {\n            CFNumberGetValue(clockRef, kCFNumberIntType, &clock);\n            CFRelease(clockRef);\n        }\n\n        if (countRef)\n        {\n            CFNumberGetValue(countRef, kCFNumberIntType, &count);\n            CFRelease(countRef);\n        }\n\n        if (clock > 0 && count > 0)\n            refreshRate = clock / (double) count;\n\n        break;\n    }\n\n    IOObjectRelease(it);\n    return refreshRate;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Poll for changes in the set of connected monitors\nvoid _glfwPollMonitorsNS(void)\n{\n    uint32_t displayCount;\n    CGGetOnlineDisplayList(0, NULL, &displayCount);\n    CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID));\n    CGGetOnlineDisplayList(displayCount, displays, &displayCount);\n    _glfwClearDisplayLinks();\n    if (_glfw.hints.init.debugRendering) {\n        fprintf(stderr, \"Polling for monitors: %u found\\n\", displayCount);\n    }\n\n    for (int i = 0;  i < _glfw.monitorCount;  i++)\n        _glfw.monitors[i]->ns.screen = nil;\n\n    _GLFWmonitor** disconnected = NULL;\n    uint32_t disconnectedCount = _glfw.monitorCount;\n    if (disconnectedCount)\n    {\n        disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));\n        memcpy(disconnected,\n               _glfw.monitors,\n               _glfw.monitorCount * sizeof(_GLFWmonitor*));\n    }\n\n    for (uint32_t i = 0;  i < displayCount;  i++)\n    {\n        if (CGDisplayIsAsleep(displays[i])) {\n            if (_glfw.hints.init.debugRendering) fprintf(stderr, \"Ignoring sleeping display: %u\", displays[i]);\n            continue;\n        }\n\n        const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);\n        NSScreen* screen = nil;\n\n        for (screen in [NSScreen screens])\n        {\n            NSNumber* screenNumber = [screen deviceDescription][@\"NSScreenNumber\"];\n\n            // HACK: Compare unit numbers instead of display IDs to work around\n            //       display replacement on machines with automatic graphics\n            //       switching\n            if (CGDisplayUnitNumber([screenNumber unsignedIntValue]) == unitNumber)\n                break;\n        }\n\n        // HACK: Compare unit numbers instead of display IDs to work around\n        //       display replacement on machines with automatic graphics\n        //       switching\n        uint32_t j;\n        for (j = 0;  j < disconnectedCount;  j++)\n        {\n            if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)\n            {\n                disconnected[j]->ns.displayID  = displays[i];\n                disconnected[j]->ns.screen = screen;\n                _glfwCreateDisplayLink(displays[i]);\n                disconnected[j] = NULL;\n                break;\n            }\n        }\n\n        if (j < disconnectedCount)\n            continue;\n\n        const CGSize size = CGDisplayScreenSize(displays[i]);\n        char* name = getDisplayName(displays[i], screen);\n        if (!name) {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                \"Failed to get name for display, using generic name\");\n            name = _glfw_strdup(\"Display with no name\");\n        }\n\n        _GLFWmonitor* monitor = _glfwAllocMonitor(name, (int)size.width, (int)size.height);\n        monitor->ns.displayID  = displays[i];\n        monitor->ns.unitNumber = unitNumber;\n        monitor->ns.screen     = screen;\n        _glfwCreateDisplayLink(monitor->ns.displayID);\n\n        free(name);\n\n        CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);\n        if (CGDisplayModeGetRefreshRate(mode) == 0.0)\n            monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]);\n        CGDisplayModeRelease(mode);\n\n        _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);\n    }\n\n    for (uint32_t i = 0;  i < disconnectedCount;  i++)\n    {\n        if (disconnected[i])\n            _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);\n    }\n\n    free(disconnected);\n    free(displays);\n    _glfwRestartDisplayLinks();\n}\n\n// Change the current video mode\n//\nvoid _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)\n{\n    GLFWvidmode current;\n    _glfwPlatformGetVideoMode(monitor, &current);\n\n    const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);\n    if (_glfwCompareVideoModes(&current, best) == 0)\n        return;\n\n    CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);\n    const CFIndex count = CFArrayGetCount(modes);\n    CGDisplayModeRef native = NULL;\n\n    for (CFIndex i = 0;  i < count;  i++)\n    {\n        CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);\n        if (!modeIsGood(dm))\n            continue;\n\n        const GLFWvidmode mode =\n            vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);\n        if (_glfwCompareVideoModes(best, &mode) == 0)\n        {\n            native = dm;\n            break;\n        }\n    }\n\n    if (native)\n    {\n        if (monitor->ns.previousMode == NULL)\n            monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID);\n\n        CGDisplayFadeReservationToken token = beginFadeReservation();\n        CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL);\n        endFadeReservation(token);\n    }\n\n    CFRelease(modes);\n}\n\n// Restore the previously saved (original) video mode\n//\nvoid _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)\n{\n    if (monitor->ns.previousMode)\n    {\n        CGDisplayFadeReservationToken token = beginFadeReservation();\n        CGDisplaySetDisplayMode(monitor->ns.displayID,\n                                monitor->ns.previousMode, NULL);\n        endFadeReservation(token);\n\n        CGDisplayModeRelease(monitor->ns.previousMode);\n        monitor->ns.previousMode = NULL;\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid _glfwPlatformFreeMonitor(_GLFWmonitor* monitor UNUSED)\n{\n}\n\nvoid _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)\n{\n    const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);\n\n    if (xpos)\n        *xpos = (int) bounds.origin.x;\n    if (ypos)\n        *ypos = (int) bounds.origin.y;\n}\n\nvoid _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,\n                                         float* xscale, float* yscale)\n{\n    if (!refreshMonitorScreen(monitor))\n        return;\n\n    const NSRect points = [monitor->ns.screen frame];\n    const NSRect pixels = [monitor->ns.screen convertRectToBacking:points];\n\n    if (xscale)\n        *xscale = (float) (pixels.size.width / points.size.width);\n    if (yscale)\n        *yscale = (float) (pixels.size.height / points.size.height);\n}\n\nvoid _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,\n                                     int* xpos, int* ypos,\n                                     int* width, int* height)\n{\n    if (!refreshMonitorScreen(monitor))\n        return;\n\n    const NSRect frameRect = [monitor->ns.screen visibleFrame];\n\n    if (xpos)\n        *xpos = (int)frameRect.origin.x;\n    if (ypos)\n        *ypos = (int)_glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);\n    if (width)\n        *width = (int)frameRect.size.width;\n    if (height)\n        *height = (int)frameRect.size.height;\n}\n\nGLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)\n{\n    *count = 0;\n\n    CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);\n    const CFIndex found = CFArrayGetCount(modes);\n    GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode));\n\n    for (CFIndex i = 0;  i < found;  i++)\n    {\n        CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);\n        if (!modeIsGood(dm))\n            continue;\n\n        const GLFWvidmode mode =\n            vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);\n        CFIndex j;\n\n        for (j = 0;  j < *count;  j++)\n        {\n            if (_glfwCompareVideoModes(result + j, &mode) == 0)\n                break;\n        }\n\n        // Skip duplicate modes\n        if (j < *count)\n            continue;\n\n        (*count)++;\n        result[*count - 1] = mode;\n    }\n\n    CFRelease(modes);\n    return result;\n}\n\nbool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)\n{\n    CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);\n    if (!native) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Cocoa: Failed to query display mode\");\n        return false;\n    }\n    *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate);\n    CGDisplayModeRelease(native);\n    return true;\n}\n\nbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)\n{\n    uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID);\n    CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue));\n\n    CGGetDisplayTransferByTable(monitor->ns.displayID,\n                                size,\n                                values,\n                                values + size,\n                                values + size * 2,\n                                &size);\n\n    _glfwAllocGammaArrays(ramp, size);\n\n    for (uint32_t i = 0; i < size; i++)\n    {\n        ramp->red[i]   = (unsigned short) (values[i] * 65535);\n        ramp->green[i] = (unsigned short) (values[i + size] * 65535);\n        ramp->blue[i]  = (unsigned short) (values[i + size * 2] * 65535);\n    }\n\n    free(values);\n    return true;\n}\n\nvoid _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)\n{\n    CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue));\n\n    for (unsigned int i = 0;  i < ramp->size;  i++)\n    {\n        values[i]                  = ramp->red[i] / 65535.f;\n        values[i + ramp->size]     = ramp->green[i] / 65535.f;\n        values[i + ramp->size * 2] = ramp->blue[i] / 65535.f;\n    }\n\n    CGSetDisplayTransferByTable(monitor->ns.displayID,\n                                ramp->size,\n                                values,\n                                values + ramp->size,\n                                values + ramp->size * 2);\n\n    free(values);\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay);\n    return monitor->ns.displayID;\n}\n"
  },
  {
    "path": "glfw/cocoa_platform.h",
    "content": "//========================================================================\n// GLFW 3.4 macOS - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#include <stdint.h>\n#include <dlfcn.h>\n\n#include <Carbon/Carbon.h>\n#if defined(__OBJC__)\n#import <Cocoa/Cocoa.h>\n#else\ntypedef void* id;\n#endif\n\n// NOTE: Many Cocoa enum values have been renamed and we need to build across\n//       SDK versions where one is unavailable or the other deprecated\n//       We use the newer names in code and these macros to handle compatibility\n#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200\n #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat\n #define NSEventMaskAny NSAnyEventMask\n #define NSEventMaskKeyUp NSKeyUpMask\n #define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask\n #define NSEventModifierFlagCommand NSCommandKeyMask\n #define NSEventModifierFlagControl NSControlKeyMask\n #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask\n #define NSEventModifierFlagOption NSAlternateKeyMask\n #define NSEventModifierFlagShift NSShiftKeyMask\n #define NSEventTypeApplicationDefined NSApplicationDefined\n #define NSWindowStyleMaskBorderless NSBorderlessWindowMask\n #define NSWindowStyleMaskClosable NSClosableWindowMask\n #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask\n #define NSWindowStyleMaskResizable NSResizableWindowMask\n #define NSWindowStyleMaskTitled NSTitledWindowMask\n#endif\n\n#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101400)\n #define NSPasteboardTypeFileURL NSFilenamesPboardType\n #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat\n #define NSPasteboardTypeString NSStringPboardType\n #define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity\n#endif\n\n#define debug_key(...) if (_glfw.hints.init.debugKeyboard) { fprintf(stderr, __VA_ARGS__); fflush(stderr); }\n\ntypedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long);\ntypedef bool (* GLFWapplicationshouldhandlereopenfun)(int);\ntypedef bool (* GLFWhandleurlopen)(const char*);\ntypedef void (* GLFWapplicationwillfinishlaunchingfun)(bool);\ntypedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);\ntypedef void (* GLFWcocoarenderframefun)(GLFWwindow*);\n\ntypedef VkFlags VkMacOSSurfaceCreateFlagsMVK;\ntypedef VkFlags VkMetalSurfaceCreateFlagsEXT;\n\ntypedef struct VkMacOSSurfaceCreateInfoMVK\n{\n    VkStructureType                 sType;\n    const void*                     pNext;\n    VkMacOSSurfaceCreateFlagsMVK    flags;\n    const void*                     pView;\n} VkMacOSSurfaceCreateInfoMVK;\n\ntypedef struct VkMetalSurfaceCreateInfoEXT\n{\n    VkStructureType                 sType;\n    const void*                     pNext;\n    VkMetalSurfaceCreateFlagsEXT    flags;\n    const void*                     pLayer;\n} VkMetalSurfaceCreateInfoEXT;\n\ntypedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*);\ntypedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMetalSurfaceCreateInfoEXT*,const VkAllocationCallbacks*,VkSurfaceKHR*);\n\n#include \"posix_thread.h\"\n#include \"cocoa_joystick.h\"\n#include \"nsgl_context.h\"\n\n#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)\n#define _glfw_dlclose(handle) dlclose(handle)\n#define _glfw_dlsym(handle, name) dlsym(handle, name)\n\n#define _GLFW_PLATFORM_WINDOW_STATE         _GLFWwindowNS  ns\n#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns\n#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE  _GLFWtimerNS   ns\n#define _GLFW_PLATFORM_MONITOR_STATE        _GLFWmonitorNS ns\n#define _GLFW_PLATFORM_CURSOR_STATE         _GLFWcursorNS  ns\n\n// HIToolbox.framework pointer typedefs\n#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData\ntypedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void);\n#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource\ntypedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef);\n#define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty\ntypedef UInt8 (*PFN_LMGetKbdType)(void);\n#define LMGetKbdType _glfw.ns.tis.GetKbdType\n\ntypedef struct _GLFWDropData {\n    const char **mimes;          // Original MIME list; strings are owned here, never reordered\n    size_t mimes_count;\n    const char **copy_mimes;     // Working copy passed to callbacks; pointers into mimes[]\n    size_t copy_mimes_count;     // Accepted count after last callback\n    bool drag_accepted;\n    id pasteboard;\n    id data_mapping;\n    id file_promise_mapping;\n} _GLFWDropData;\n\n// Cocoa-specific per-window data\n//\ntypedef struct _GLFWwindowNS\n{\n    id              object;\n    id              delegate;\n    id              view;\n    id              layer;\n\n    bool            maximized;\n    bool            retina;\n    bool            in_traditional_fullscreen;\n    bool            in_fullscreen_transition;\n    bool            suppress_frame_constraints;\n    id              notch_cover_window;\n    unsigned int    notch_cover_color;\n    float           notch_cover_opacity;\n    bool            titlebar_hidden;\n    unsigned long   pre_full_screen_style_mask;\n    CGRect          pre_traditional_fullscreen_frame;\n\n    // Cached window properties to filter out duplicate events\n    int             width, height;\n    int             fbWidth, fbHeight;\n    float           xscale, yscale;\n    int             blur_radius;\n    bool live_resize_in_progress;\n    struct {\n        struct { CGFloat red, green, blue, alpha; bool was_set; } color; bool transparent;\n    } last_applied_titlebar_settings;\n\n    // The total sum of the distances the cursor has been warped\n    // since the last cursor motion event was processed\n    // This is kept to counteract Cocoa doing the same internally\n    double          cursorWarpDeltaX, cursorWarpDeltaY;\n\n    // The text input filter callback\n    GLFWcocoatextinputfilterfun textInputFilterCallback;\n    // The toggle fullscreen intercept callback\n    GLFWcocoatogglefullscreenfun toggleFullscreenCallback;\n    // Dead key state\n    UInt32 deadKeyState;\n\n    // Layer shell windows\n    struct {\n        bool is_active;\n        GLFWLayerShellConfig config;\n    } layer_shell;\n\n    // Whether a render frame has been requested for this window\n    bool renderFrameRequested;\n    GLFWcocoarenderframefun renderFrameCallback;\n    // update cursor after switching desktops with Mission Control\n    bool delayed_cursor_update_requested;\n    GLFWcocoarenderframefun resizeCallback;\n\n    // Cached MIME types from drag enter (for move events)\n    _GLFWDropData drop_data;\n\n} _GLFWwindowNS;\n\n// Cocoa-specific global data\n//\ntypedef struct _GLFWlibraryNS\n{\n    CGEventSourceRef    eventSource;\n    id                  delegate;\n    bool                finishedLaunching;\n    bool                cursorHidden;\n    TISInputSourceRef   inputSource;\n    IOHIDManagerRef     hidManager;\n    id                  unicodeData;\n    id                  helper;\n    id                  keyUpMonitor, keyDownMonitor, flagsChangedMonitor;\n    id                  appleSettings;\n    id                  nibObjects;\n\n    char                keyName[64];\n    char                text[512];\n    CGPoint             cascadePoint;\n    // Where to place the cursor when re-enabled\n    double              restoreCursorPosX, restoreCursorPosY;\n    // The window whose disabled cursor mode is active\n    _GLFWwindow*        disabledCursorWindow;\n    pid_t           previous_front_most_application;\n\n    struct {\n        CFBundleRef     bundle;\n        PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource;\n        PFN_TISGetInputSourceProperty GetInputSourceProperty;\n        PFN_LMGetKbdType GetKbdType;\n        CFStringRef     kPropertyUnicodeKeyLayoutData;\n    } tis;\n\n    // the callback to handle url open events\n    GLFWhandleurlopen url_open_callback;\n\n    // Active drag session (NSDraggingSession*) and view (NSView*)\n    id drag_session, drag_view, drag_image;\n} _GLFWlibraryNS;\n\n// Cocoa-specific per-monitor data\n//\ntypedef struct _GLFWmonitorNS\n{\n    CGDirectDisplayID   displayID;\n    CGDisplayModeRef    previousMode;\n    uint32_t            unitNumber;\n    id                  screen;\n    double              fallbackRefreshRate;\n\n} _GLFWmonitorNS;\n\n// Cocoa-specific per-cursor data\n//\ntypedef struct _GLFWcursorNS\n{\n    id              object;\n\n} _GLFWcursorNS;\n\n// Cocoa-specific global timer data\n//\ntypedef struct _GLFWtimerNS\n{\n    uint64_t        frequency;\n\n} _GLFWtimerNS;\n\nvoid _glfwPollMonitorsNS(void);\nvoid _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);\nvoid _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);\n\nfloat _glfwTransformYNS(float y);\n\nvoid* _glfwLoadLocalVulkanLoaderNS(void);\n\n\n// display links\nvoid _glfwClearDisplayLinks(void);\nvoid _glfwRestartDisplayLinks(void);\nunsigned _glfwCreateDisplayLink(CGDirectDisplayID);\nvoid _glfwRequestRenderFrame(_GLFWwindow *w);\n\n// event loop\nvoid _glfwDispatchTickCallback(void);\nvoid _glfwCocoaPostEmptyEvent(void);\n\nuint32_t vk_to_unicode_key_with_current_layout(uint16_t keycode);\n"
  },
  {
    "path": "glfw/cocoa_window.m",
    "content": "//========================================================================\n// GLFW 3.4 macOS - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"../kitty/monotonic.h\"\n#include \"glfw3.h\"\n#include \"internal.h\"\n\n#include <Availability.h>\n#import <CoreServices/CoreServices.h>\n#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>\n#include <errno.h>\n#include <float.h>\n#include <string.h>\n#include <assert.h>\n\n#define debug debug_rendering\n\n#define UTI_ROUNDTRIP_PREFIX @\"uti-is-typical-apple-nih.\"\n\nstatic NSString*\nmime_to_uti(const char *mime) {\n    if (strcmp(mime, \"text/plain\") == 0) return NSPasteboardTypeString;\n    UTType *t = [UTType typeWithMIMEType:@(mime)];  // auto-released\n    if (t != nil && !t.dynamic) return t.identifier;\n    size_t sz = strlen(mime);\n    NSMutableString *hex = [NSMutableString stringWithCapacity:sz * 2];\n    for (NSUInteger i = 0; i < sz; i++) [hex appendFormat:@\"%02x\", (unsigned char)mime[i]];\n    return [NSString stringWithFormat:@\"%@%@\", UTI_ROUNDTRIP_PREFIX, hex];  // auto-released\n}\n\nstatic char\nhexval(char c, bool *ok) {\n    if (c >= '0' && c <= '9') return c - '0';\n    if (c >= 'a' && c <= 'f') return c - 'a' + 10;\n    if (c >= 'A' && c <= 'F') return c - 'A' + 10;\n    *ok = false;\n    return 0;\n}\n\nstatic const char*\nuti_to_mime(NSString *uti) {\n    if ([uti isEqualToString:NSPasteboardTypeString]) return \"text/plain\";\n    if ([uti hasPrefix:UTI_ROUNDTRIP_PREFIX]) {\n        NSString *hexPart = [uti substringFromIndex:UTI_ROUNDTRIP_PREFIX.length];\n        NSUInteger hexLen = hexPart.length;\n        if (hexLen == 0 || hexLen % 2 != 0) { return \"\"; }\n        const char *hex = [hexPart UTF8String];\n        static char buf[4096];\n        size_t i, j;\n        for (i = 0, j = 0; i < hexLen && j < sizeof(buf)-1; i += 2, j++) {\n            char hi = hex[i], lo = hex[i + 1];\n            bool ok = true;\n            buf[j] = (hexval(hi, &ok) << 4) | hexval(lo, &ok);\n            if (!ok) return \"\";\n        }\n        buf[j] = 0;\n        return buf;\n    }\n    if (@available(macOS 11.0, *)) {\n        UTType *t = [UTType typeWithIdentifier:uti];  // auto-released\n        if (t.preferredMIMEType != nil) return [t.preferredMIMEType UTF8String];\n    }\n    return \"\";\n}\n\nstatic const char*\npolymorphic_string_as_utf8(id string) {\n    if (string == nil) return \"(nil)\";\n    NSString* characters;\n    if ([string isKindOfClass:[NSAttributedString class]])\n        characters = [string string];\n    else\n        characters = (NSString*) string;\n    return [characters UTF8String];\n}\n\nstatic bool\nforward_dictation_selector_to_app(SEL selector, id sender) {\n    static SEL start_dictation_selector = NULL, stop_dictation_selector = NULL;\n    if (start_dictation_selector == NULL) {\n        start_dictation_selector = NSSelectorFromString(@\"startDictation:\");\n        stop_dictation_selector = NSSelectorFromString(@\"stopDictation:\");\n    }\n    if (selector != start_dictation_selector && selector != stop_dictation_selector) return false;\n    if ([NSApp respondsToSelector:selector]) {\n        debug_key(\"Forwarding %s to NSApp\\n\", [NSStringFromSelector(selector) UTF8String]);\n        [NSApp performSelector:selector withObject:sender];\n        return true;\n    }\n    return false;\n}\n\nstatic uint32_t\nvk_code_to_functional_key_code(uint8_t key_code) {  // {{{\n    switch(key_code) {\n        /* start vk to functional (auto generated by gen-key-constants.py do not edit) */\n        case 0x35: return GLFW_FKEY_ESCAPE;\n        case 0x24: return GLFW_FKEY_ENTER;\n        case 0x30: return GLFW_FKEY_TAB;\n        case 0x33: return GLFW_FKEY_BACKSPACE;\n        case 0x72: return GLFW_FKEY_INSERT;\n        case 0x75: return GLFW_FKEY_DELETE;\n        case 0x7b: return GLFW_FKEY_LEFT;\n        case 0x7c: return GLFW_FKEY_RIGHT;\n        case 0x7e: return GLFW_FKEY_UP;\n        case 0x7d: return GLFW_FKEY_DOWN;\n        case 0x74: return GLFW_FKEY_PAGE_UP;\n        case 0x79: return GLFW_FKEY_PAGE_DOWN;\n        case 0x73: return GLFW_FKEY_HOME;\n        case 0x77: return GLFW_FKEY_END;\n        case 0x39: return GLFW_FKEY_CAPS_LOCK;\n        case 0x47: return GLFW_FKEY_NUM_LOCK;\n        case 0x6e: return GLFW_FKEY_MENU;\n        case 0x7a: return GLFW_FKEY_F1;\n        case 0x78: return GLFW_FKEY_F2;\n        case 0x63: return GLFW_FKEY_F3;\n        case 0x76: return GLFW_FKEY_F4;\n        case 0x60: return GLFW_FKEY_F5;\n        case 0x61: return GLFW_FKEY_F6;\n        case 0x62: return GLFW_FKEY_F7;\n        case 0x64: return GLFW_FKEY_F8;\n        case 0x65: return GLFW_FKEY_F9;\n        case 0x6d: return GLFW_FKEY_F10;\n        case 0x67: return GLFW_FKEY_F11;\n        case 0x6f: return GLFW_FKEY_F12;\n        case 0x69: return GLFW_FKEY_F13;\n        case 0x6b: return GLFW_FKEY_F14;\n        case 0x71: return GLFW_FKEY_F15;\n        case 0x6a: return GLFW_FKEY_F16;\n        case 0x40: return GLFW_FKEY_F17;\n        case 0x4f: return GLFW_FKEY_F18;\n        case 0x50: return GLFW_FKEY_F19;\n        case 0x5a: return GLFW_FKEY_F20;\n        case 0x52: return GLFW_FKEY_KP_0;\n        case 0x53: return GLFW_FKEY_KP_1;\n        case 0x54: return GLFW_FKEY_KP_2;\n        case 0x55: return GLFW_FKEY_KP_3;\n        case 0x56: return GLFW_FKEY_KP_4;\n        case 0x57: return GLFW_FKEY_KP_5;\n        case 0x58: return GLFW_FKEY_KP_6;\n        case 0x59: return GLFW_FKEY_KP_7;\n        case 0x5b: return GLFW_FKEY_KP_8;\n        case 0x5c: return GLFW_FKEY_KP_9;\n        case 0x41: return GLFW_FKEY_KP_DECIMAL;\n        case 0x4b: return GLFW_FKEY_KP_DIVIDE;\n        case 0x43: return GLFW_FKEY_KP_MULTIPLY;\n        case 0x4e: return GLFW_FKEY_KP_SUBTRACT;\n        case 0x45: return GLFW_FKEY_KP_ADD;\n        case 0x4c: return GLFW_FKEY_KP_ENTER;\n        case 0x51: return GLFW_FKEY_KP_EQUAL;\n        case 0x38: return GLFW_FKEY_LEFT_SHIFT;\n        case 0x3b: return GLFW_FKEY_LEFT_CONTROL;\n        case 0x3a: return GLFW_FKEY_LEFT_ALT;\n        case 0x37: return GLFW_FKEY_LEFT_SUPER;\n        case 0x3c: return GLFW_FKEY_RIGHT_SHIFT;\n        case 0x3e: return GLFW_FKEY_RIGHT_CONTROL;\n        case 0x3d: return GLFW_FKEY_RIGHT_ALT;\n        case 0x36: return GLFW_FKEY_RIGHT_SUPER;\n/* end vk to functional */\n        default:\n            return 0;\n    }\n} // }}}\n\nstatic uint32_t\nvk_code_to_unicode(uint8_t key_code) {  // {{{\n    switch(key_code) {\n        /* start vk to unicode (auto generated by gen-key-constants.py do not edit) */\n        case 0x0: return 0x61;\n        case 0x1: return 0x73;\n        case 0x2: return 0x64;\n        case 0x3: return 0x66;\n        case 0x4: return 0x68;\n        case 0x5: return 0x67;\n        case 0x6: return 0x7a;\n        case 0x7: return 0x78;\n        case 0x8: return 0x63;\n        case 0x9: return 0x76;\n        case 0xb: return 0x62;\n        case 0xc: return 0x71;\n        case 0xd: return 0x77;\n        case 0xe: return 0x65;\n        case 0xf: return 0x72;\n        case 0x10: return 0x79;\n        case 0x11: return 0x74;\n        case 0x12: return 0x31;\n        case 0x13: return 0x32;\n        case 0x14: return 0x33;\n        case 0x15: return 0x34;\n        case 0x16: return 0x36;\n        case 0x17: return 0x35;\n        case 0x18: return 0x3d;\n        case 0x19: return 0x39;\n        case 0x1a: return 0x37;\n        case 0x1b: return 0x2d;\n        case 0x1c: return 0x38;\n        case 0x1d: return 0x30;\n        case 0x1e: return 0x5d;\n        case 0x1f: return 0x6f;\n        case 0x20: return 0x75;\n        case 0x21: return 0x5b;\n        case 0x22: return 0x69;\n        case 0x23: return 0x70;\n        case 0x25: return 0x6c;\n        case 0x26: return 0x6a;\n        case 0x27: return 0x27;\n        case 0x28: return 0x6b;\n        case 0x29: return 0x3b;\n        case 0x2a: return 0x5c;\n        case 0x2b: return 0x2c;\n        case 0x2c: return 0x2f;\n        case 0x2d: return 0x6e;\n        case 0x2e: return 0x6d;\n        case 0x2f: return 0x2e;\n        case 0x31: return 0x20;\n        case 0x32: return 0x60;\n/* end vk to unicode */\n        default:\n            return 0;\n    }\n} // }}}\n\nstatic uint32_t\nmac_ucode_to_functional(uint32_t key_code) {  // {{{\n    switch(key_code) {\n        /* start macu to functional (auto generated by gen-key-constants.py do not edit) */\n        case NSCarriageReturnCharacter: return GLFW_FKEY_ENTER;\n        case NSTabCharacter: return GLFW_FKEY_TAB;\n        case NSBackspaceCharacter: return GLFW_FKEY_BACKSPACE;\n        case NSInsertFunctionKey: return GLFW_FKEY_INSERT;\n        case NSDeleteFunctionKey: return GLFW_FKEY_DELETE;\n        case NSLeftArrowFunctionKey: return GLFW_FKEY_LEFT;\n        case NSRightArrowFunctionKey: return GLFW_FKEY_RIGHT;\n        case NSUpArrowFunctionKey: return GLFW_FKEY_UP;\n        case NSDownArrowFunctionKey: return GLFW_FKEY_DOWN;\n        case NSPageUpFunctionKey: return GLFW_FKEY_PAGE_UP;\n        case NSPageDownFunctionKey: return GLFW_FKEY_PAGE_DOWN;\n        case NSHomeFunctionKey: return GLFW_FKEY_HOME;\n        case NSEndFunctionKey: return GLFW_FKEY_END;\n        case NSScrollLockFunctionKey: return GLFW_FKEY_SCROLL_LOCK;\n        case NSClearLineFunctionKey: return GLFW_FKEY_NUM_LOCK;\n        case NSPrintScreenFunctionKey: return GLFW_FKEY_PRINT_SCREEN;\n        case NSPauseFunctionKey: return GLFW_FKEY_PAUSE;\n        case NSMenuFunctionKey: return GLFW_FKEY_MENU;\n        case NSF1FunctionKey: return GLFW_FKEY_F1;\n        case NSF2FunctionKey: return GLFW_FKEY_F2;\n        case NSF3FunctionKey: return GLFW_FKEY_F3;\n        case NSF4FunctionKey: return GLFW_FKEY_F4;\n        case NSF5FunctionKey: return GLFW_FKEY_F5;\n        case NSF6FunctionKey: return GLFW_FKEY_F6;\n        case NSF7FunctionKey: return GLFW_FKEY_F7;\n        case NSF8FunctionKey: return GLFW_FKEY_F8;\n        case NSF9FunctionKey: return GLFW_FKEY_F9;\n        case NSF10FunctionKey: return GLFW_FKEY_F10;\n        case NSF11FunctionKey: return GLFW_FKEY_F11;\n        case NSF12FunctionKey: return GLFW_FKEY_F12;\n        case NSF13FunctionKey: return GLFW_FKEY_F13;\n        case NSF14FunctionKey: return GLFW_FKEY_F14;\n        case NSF15FunctionKey: return GLFW_FKEY_F15;\n        case NSF16FunctionKey: return GLFW_FKEY_F16;\n        case NSF17FunctionKey: return GLFW_FKEY_F17;\n        case NSF18FunctionKey: return GLFW_FKEY_F18;\n        case NSF19FunctionKey: return GLFW_FKEY_F19;\n        case NSF20FunctionKey: return GLFW_FKEY_F20;\n        case NSF21FunctionKey: return GLFW_FKEY_F21;\n        case NSF22FunctionKey: return GLFW_FKEY_F22;\n        case NSF23FunctionKey: return GLFW_FKEY_F23;\n        case NSF24FunctionKey: return GLFW_FKEY_F24;\n        case NSF25FunctionKey: return GLFW_FKEY_F25;\n        case NSF26FunctionKey: return GLFW_FKEY_F26;\n        case NSF27FunctionKey: return GLFW_FKEY_F27;\n        case NSF28FunctionKey: return GLFW_FKEY_F28;\n        case NSF29FunctionKey: return GLFW_FKEY_F29;\n        case NSF30FunctionKey: return GLFW_FKEY_F30;\n        case NSF31FunctionKey: return GLFW_FKEY_F31;\n        case NSF32FunctionKey: return GLFW_FKEY_F32;\n        case NSF33FunctionKey: return GLFW_FKEY_F33;\n        case NSF34FunctionKey: return GLFW_FKEY_F34;\n        case NSF35FunctionKey: return GLFW_FKEY_F35;\n        case NSEnterCharacter: return GLFW_FKEY_KP_ENTER;\n/* end macu to functional */\n        default:\n            return 0;\n    }\n} // }}}\n\nstatic bool\nis_surrogate(UniChar uc) { return (uc - 0xd800u) < 2048u; }\n\nstatic uint32_t\nget_first_codepoint(UniChar *utf16, UniCharCount num) {\n    if (!num) return 0;\n    if (!is_surrogate(*utf16)) return *utf16;\n    if (CFStringIsSurrogateHighCharacter(*utf16) && num > 1 && CFStringIsSurrogateLowCharacter(utf16[1])) return CFStringGetLongCharacterForSurrogatePair(utf16[0], utf16[1]);\n    return 0;\n}\n\nstatic bool\nis_pua_char(uint32_t ch) {\n    return (0xE000 <= ch && ch <= 0xF8FF) || (0xF0000 <= ch && ch <= 0xFFFFF) || (0x100000 <= ch && ch <= 0x10FFFF);\n}\n\nuint32_t\nvk_to_unicode_key_with_current_layout(uint16_t keycode)\n{\n    UInt32 dead_key_state = 0;\n    UniChar characters[256];\n    UniCharCount character_count = 0;\n    uint32_t ans = vk_code_to_functional_key_code(keycode);\n    if (ans) return ans;\n\n    if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes],\n                       keycode,\n                       kUCKeyActionDisplay,\n                       0,\n                       LMGetKbdType(),\n                       kUCKeyTranslateNoDeadKeysBit,\n                       &dead_key_state,\n                       arraysz(characters),\n                       &character_count,\n                       characters) == noErr) {\n        uint32_t cp = get_first_codepoint(characters, character_count);\n        if (cp) {\n            if (cp < 32 || (0xF700 <= cp && cp <= 0xF8FF)) return mac_ucode_to_functional(cp);\n            if (cp >= 32 && !is_pua_char(cp)) return cp;\n        }\n    }\n    return vk_code_to_unicode(keycode);\n}\n\n\n// Returns the style mask corresponding to the window settings\n//\nstatic NSUInteger getStyleMask(_GLFWwindow* window)\n{\n    NSUInteger styleMask = NSWindowStyleMaskMiniaturizable;\n    if (window->ns.titlebar_hidden) styleMask |= NSWindowStyleMaskFullSizeContentView;\n\n    if (window->monitor || !window->decorated) {\n        styleMask |= NSWindowStyleMaskBorderless;\n    } else {\n        styleMask |= NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;\n    }\n    if (window->resizable) styleMask |= NSWindowStyleMaskResizable;\n\n    return styleMask;\n}\n\n\nstatic void\nrequestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {\n    if (!callback) {\n        w->ns.renderFrameRequested = false;\n        w->ns.renderFrameCallback = NULL;\n        return;\n    }\n    w->ns.renderFrameCallback = callback;\n    w->ns.renderFrameRequested = true;\n    _glfwRequestRenderFrame(w);\n}\n\nvoid\n_glfwRestartDisplayLinks(void) {\n    _GLFWwindow* window;\n    for (window = _glfw.windowListHead;  window;  window = window->next) {\n        if (window->ns.renderFrameRequested && window->ns.renderFrameCallback) {\n            requestRenderFrame(window, window->ns.renderFrameCallback);\n        }\n    }\n}\n\n// Returns whether the cursor is in the content area of the specified window\n//\nstatic bool cursorInContentArea(_GLFWwindow* window)\n{\n    const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];\n    return [window->ns.view mouse:pos inRect:[window->ns.view frame]];\n}\n\n// Hides the cursor if not already hidden\n//\nstatic void hideCursor(_GLFWwindow* window UNUSED)\n{\n    if (!_glfw.ns.cursorHidden)\n    {\n        [NSCursor hide];\n        _glfw.ns.cursorHidden = true;\n    }\n}\n\n// Shows the cursor if not already shown\n//\nstatic void showCursor(_GLFWwindow* window UNUSED)\n{\n    if (_glfw.ns.cursorHidden)\n    {\n        [NSCursor unhide];\n        _glfw.ns.cursorHidden = false;\n    }\n}\n\n// Updates the cursor image according to its cursor mode\n//\nstatic void updateCursorImage(_GLFWwindow* window)\n{\n    if (window->cursorMode == GLFW_CURSOR_NORMAL)\n    {\n        showCursor(window);\n\n        if (window->cursor)\n            [(NSCursor*) window->cursor->ns.object set];\n        else\n            [[NSCursor arrowCursor] set];\n    }\n    else\n        hideCursor(window);\n}\n\n// Apply chosen cursor mode to a focused window\n//\nstatic void updateCursorMode(_GLFWwindow* window)\n{\n    if (window->cursorMode == GLFW_CURSOR_DISABLED)\n    {\n        _glfw.ns.disabledCursorWindow = window;\n        _glfwPlatformGetCursorPos(window,\n                                  &_glfw.ns.restoreCursorPosX,\n                                  &_glfw.ns.restoreCursorPosY);\n        _glfwCenterCursorInContentArea(window);\n        CGAssociateMouseAndMouseCursorPosition(false);\n    }\n    else if (_glfw.ns.disabledCursorWindow == window)\n    {\n        _glfw.ns.disabledCursorWindow = NULL;\n        CGAssociateMouseAndMouseCursorPosition(true);\n        _glfwPlatformSetCursorPos(window,\n                                  _glfw.ns.restoreCursorPosX,\n                                  _glfw.ns.restoreCursorPosY);\n    }\n\n    if (cursorInContentArea(window))\n        updateCursorImage(window);\n}\n\n// Make the specified window and its video mode active on its monitor\n//\nstatic void acquireMonitor(_GLFWwindow* window)\n{\n    _glfwSetVideoModeNS(window->monitor, &window->videoMode);\n    const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID);\n    const NSRect frame = NSMakeRect(bounds.origin.x,\n                                    _glfwTransformYNS(bounds.origin.y + bounds.size.height - 1),\n                                    bounds.size.width,\n                                    bounds.size.height);\n\n    [window->ns.object setFrame:frame display:YES];\n\n    _glfwInputMonitorWindow(window->monitor, window);\n}\n\n// Remove the window and restore the original video mode\n//\nstatic void releaseMonitor(_GLFWwindow* window)\n{\n    if (window->monitor->window != window)\n        return;\n\n    _glfwInputMonitorWindow(window->monitor, NULL);\n    _glfwRestoreVideoModeNS(window->monitor);\n}\n\n// Translates macOS key modifiers into GLFW ones\n//\nstatic int\ntranslateFlags(NSUInteger flags)\n{\n    int mods = 0;\n\n    if (flags & NSEventModifierFlagShift)\n        mods |= GLFW_MOD_SHIFT;\n    if (flags & NSEventModifierFlagControl)\n        mods |= GLFW_MOD_CONTROL;\n    if (flags & NSEventModifierFlagOption)\n        mods |= GLFW_MOD_ALT;\n    if (flags & NSEventModifierFlagCommand)\n        mods |= GLFW_MOD_SUPER;\n    if (flags & NSEventModifierFlagCapsLock)\n        mods |= GLFW_MOD_CAPS_LOCK;\n\n    return mods;\n}\n\nstatic const char*\nformat_mods(int mods) {\n    static char buf[128];\n    char *p = buf, *s;\n#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, x)\n    pr(\"mods: \");\n    s = p;\n    if (mods & GLFW_MOD_CONTROL) pr(\"ctrl+\");\n    if (mods & GLFW_MOD_ALT) pr(\"alt+\");\n    if (mods & GLFW_MOD_SHIFT) pr(\"shift+\");\n    if (mods & GLFW_MOD_SUPER) pr(\"super+\");\n    if (mods & GLFW_MOD_CAPS_LOCK) pr(\"capslock+\");\n    if (mods & GLFW_MOD_NUM_LOCK) pr(\"numlock+\");\n    if (p == s) pr(\"none\");\n    else p--;\n    pr(\" \");\n#undef pr\n    return buf;\n}\n\nstatic const char*\nformat_text(const char *src) {\n    static char buf[256];\n    char *p = buf;\n    const char *last_char = buf + sizeof(buf) - 1;\n    if (!src[0]) return \"<none>\";\n    while (*src) {\n        int num = snprintf(p, sizeof(buf) - (p - buf), \"0x%x \", (unsigned char)*(src++));\n        if (num < 0) return \"<error>\";\n        if (p + num >= last_char) break;\n        p += num;\n    }\n    if (p != buf) *(--p) = 0;\n    return buf;\n}\n\nstatic const char*\nsafe_name_for_keycode(unsigned int keycode) {\n    const char *ans = _glfwPlatformGetNativeKeyName(keycode);\n    if (!ans) return \"<noname>\";\n    if ((1 <= ans[0] && ans[0] <= 31) || ans[0] == 127) ans = \"<cc>\";\n    return ans;\n}\n\n\n// Translates a macOS keycode to a GLFW keycode\n//\nstatic uint32_t\ntranslateKey(uint16_t vk_key, bool apply_keymap)\n{\n    if (apply_keymap) return vk_to_unicode_key_with_current_layout(vk_key);\n    uint32_t ans = vk_code_to_functional_key_code(vk_key);\n    if (!ans) ans = vk_code_to_unicode(vk_key);\n    return ans;\n}\n\nstatic NSRect\nget_window_size_without_border_in_logical_pixels(_GLFWwindow *window) {\n    return [window->ns.object contentRectForFrameRect:[window->ns.object frame]];\n}\n\n// Defines a constant for empty ranges in NSTextInputClient\n//\nstatic const NSRange kEmptyRange = { NSNotFound, 0 };\n\n\n// Delegate for window related notifications {{{\n\n@interface GLFWWindowDelegate : NSObject\n{\n    _GLFWwindow* window;\n    NSArray<NSDictionary *> *_lastScreenStates;\n}\n\n- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow;\n- (void)request_delayed_cursor_update:(id)sender;\n\n@end\n\nstatic void update_titlebar_button_visibility_after_fullscreen_transition(_GLFWwindow*, bool, bool);\nstatic void _glfwUpdateNotchCover(_GLFWwindow*);\n\n@implementation GLFWWindowDelegate\n\n- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow\n{\n    self = [super init];\n    if (self != nil) {\n        window = initWindow;\n        _lastScreenStates = [self captureScreenStates];\n        window->ns.live_resize_in_progress = false;\n    }\n    return self;\n}\n\n- (NSArray<NSDictionary *> *)captureScreenStates {\n    NSMutableArray *states = [NSMutableArray array];\n    for (NSScreen *screen in [NSScreen screens]) {\n        // Use the screen's deviceDescription, which contains a stable ID.\n        [states addObject:screen.deviceDescription];\n    }\n    return [states copy];\n}\n\n- (void)cleanup {\n    [_lastScreenStates release]; _lastScreenStates = nil;\n}\n\n- (BOOL)windowShouldClose:(id)sender\n{\n    (void)sender;\n    if (window == nil) return YES;\n    _glfwInputWindowCloseRequest(window);\n    return NO;\n}\n\n- (void)windowDidResize:(NSNotification *)notification\n{\n    (void)notification;\n    NSArray<NSDictionary *> *currentScreenStates = [self captureScreenStates];\n    const bool is_screen_change = ![_lastScreenStates isEqualToArray:currentScreenStates];\n    NSWindowStyleMask sm = [window->ns.object styleMask];\n    const bool is_fullscreen = (sm & NSWindowStyleMaskFullScreen) != 0;\n    NSRect frame = [window->ns.object frame];\n    debug_rendering(\n            \"windowDidResize() called, is_screen_change: %d is_fullscreen: %d live_resize_in_progress: %d frame: %.1fx%.1f@(%.1f, %.1f)\\n\",\n            is_screen_change, is_fullscreen, window->ns.live_resize_in_progress, frame.size.width, frame.size.height, frame.origin.x, frame.origin.y);\n    if (is_screen_change) {\n        // This resize likely happened because a screen was added, removed, or changed resolution.\n        [_lastScreenStates release];\n        _lastScreenStates = [currentScreenStates retain];\n    }\n    [currentScreenStates release];\n\n    if (window->context.client != GLFW_NO_API)\n        [window->context.nsgl.object update];\n\n    if (_glfw.ns.disabledCursorWindow == window)\n        _glfwCenterCursorInContentArea(window);\n\n    const int maximized = [window->ns.object isZoomed];\n    if (window->ns.maximized != maximized)\n    {\n        window->ns.maximized = maximized;\n        _glfwInputWindowMaximize(window, maximized);\n    }\n\n    const NSRect contentRect = get_window_size_without_border_in_logical_pixels(window);\n    const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];\n\n    if (fbRect.size.width != window->ns.fbWidth ||\n        fbRect.size.height != window->ns.fbHeight)\n    {\n        window->ns.fbWidth  = (int)fbRect.size.width;\n        window->ns.fbHeight = (int)fbRect.size.height;\n        _glfwInputFramebufferSize(window, (int)fbRect.size.width, (int)fbRect.size.height);\n    }\n\n    if (contentRect.size.width != window->ns.width ||\n        contentRect.size.height != window->ns.height)\n    {\n        window->ns.width  = (int)contentRect.size.width;\n        window->ns.height = (int)contentRect.size.height;\n        _glfwInputWindowSize(window, (int)contentRect.size.width, (int)contentRect.size.height);\n    }\n    // Because of a bug in macOS Tahoe we cannot redraw the window in response\n    // to a resize event that was caused by a screen change as the OpenGL\n    // context is not ready yet. See: https://github.com/kovidgoyal/kitty/issues/8983\n    if (window->ns.resizeCallback && !is_screen_change && !is_fullscreen && window->ns.live_resize_in_progress)\n        window->ns.resizeCallback((GLFWwindow*)window);\n}\n\n- (void)windowDidMove:(NSNotification *)notification\n{\n    (void)notification;\n    if (window->context.client != GLFW_NO_API)\n        [window->context.nsgl.object update];\n\n    if (_glfw.ns.disabledCursorWindow == window)\n        _glfwCenterCursorInContentArea(window);\n\n    int x, y;\n    _glfwPlatformGetWindowPos(window, &x, &y);\n    _glfwInputWindowPos(window, x, y);\n}\n\n- (void)windowDidChangeOcclusionState:(NSNotification *)notification\n{\n    (void)notification;\n    _glfwInputWindowOcclusion(window, !([window->ns.object occlusionState] & NSWindowOcclusionStateVisible));\n}\n\n- (void)windowDidMiniaturize:(NSNotification *)notification\n{\n    (void)notification;\n    if (window->monitor)\n        releaseMonitor(window);\n\n    _glfwInputWindowIconify(window, true);\n}\n\n- (void)windowDidDeminiaturize:(NSNotification *)notification\n{\n    (void)notification;\n    if (window->monitor)\n        acquireMonitor(window);\n\n    _glfwInputWindowIconify(window, false);\n}\n\n- (void)windowDidBecomeKey:(NSNotification *)notification\n{\n    (void)notification;\n    if (_glfw.ns.disabledCursorWindow == window)\n        _glfwCenterCursorInContentArea(window);\n\n    _glfwInputWindowFocus(window, true);\n    updateCursorMode(window);\n    if (window->cursorMode == GLFW_CURSOR_HIDDEN) hideCursor(window);\n    if (_glfw.ns.disabledCursorWindow != window && cursorInContentArea(window))\n    {\n        double x = 0, y = 0;\n        _glfwPlatformGetCursorPos(window, &x, &y);\n        _glfwInputCursorPos(window, x, y);\n    }\n    // macOS will send a delayed event to update the cursor to arrow after switching desktops.\n    // So we need to delay and update the cursor once after that.\n    [self performSelector:@selector(request_delayed_cursor_update:) withObject:nil afterDelay:0.3];\n}\n\n- (void)windowDidResignKey:(NSNotification *)notification\n{\n    (void)notification;\n    if (window->monitor && window->autoIconify)\n        _glfwPlatformIconifyWindow(window);\n    showCursor(window);\n\n    _glfwInputWindowFocus(window, false);\n    // IME is cancelled when losing the focus\n    if ([window->ns.view hasMarkedText]) {\n        [[window->ns.view inputContext] discardMarkedText];\n        [window->ns.view unmarkText];\n        GLFWkeyevent dummy = {.action = GLFW_RELEASE, .ime_state = GLFW_IME_PREEDIT_CHANGED};\n        _glfwInputKeyboard(window, &dummy);\n        _glfw.ns.text[0] = 0;\n    }\n}\n\n- (void)windowDidChangeScreen:(NSNotification *)notification\n{\n    (void)notification;\n    if (window->ns.renderFrameRequested && window->ns.renderFrameCallback) {\n        // Ensure that if the window changed its monitor, CVDisplayLink\n        // is running for the new monitor\n        requestRenderFrame(window, window->ns.renderFrameCallback);\n    }\n}\n\n- (void)request_delayed_cursor_update:(id)sender\n{\n    (void)sender;\n    if (window) window->ns.delayed_cursor_update_requested = true;\n}\n\n- (void)windowWillEnterFullScreen:(NSNotification *)notification\n{\n    (void)notification;\n    if (window) window->ns.in_fullscreen_transition = true;\n}\n\n- (void)windowDidEnterFullScreen:(NSNotification *)notification\n{\n    (void)notification;\n    if (window) window->ns.in_fullscreen_transition = false;\n    [self performSelector:@selector(request_delayed_cursor_update:) withObject:nil afterDelay:0.3];\n}\n\n- (void)windowWillExitFullScreen:(NSNotification *)notification\n{\n    (void)notification;\n    if (window) window->ns.in_fullscreen_transition = true;\n}\n\n- (void)windowDidExitFullScreen:(NSNotification *)notification\n{\n    (void)notification;\n    if (window) {\n        window->ns.in_fullscreen_transition = false;\n        if (window->ns.in_traditional_fullscreen) {\n            // macOS finished its Cocoa exit (cleared NSWindowStyleMaskFullScreen).\n            // Defer restoration to the next run loop iteration because calling\n            // setStyleMask: inside a delegate callback can leave the window in\n            // an intermediate state. setStyleMask: also triggers macOS's\n            // constrainFrameRect:toScreen: and window tiling logic which can\n            // asynchronously reposition the window, so suppress frame\n            // constraints during the restoration (#9572).\n            unsigned long long wid = window->id;\n            NSWindowStyleMask savedMask = window->ns.pre_full_screen_style_mask;\n            CGRect savedFrame = window->ns.pre_traditional_fullscreen_frame;\n            window->ns.in_traditional_fullscreen = false;\n            _glfwUpdateNotchCover(window);\n            window->ns.suppress_frame_constraints = true;\n            dispatch_async(dispatch_get_main_queue(), ^{\n                _GLFWwindow *w = NULL;\n                for (_GLFWwindow *ww = _glfw.windowListHead; ww; ww = ww->next) {\n                    if (ww->id == wid) { w = ww; break; }\n                }\n                if (w) {\n                    NSWindow *nswindow = w->ns.object;\n                    [nswindow setStyleMask: savedMask];\n                    [nswindow setFrame: savedFrame display:YES];\n                    update_titlebar_button_visibility_after_fullscreen_transition(w, true, false);\n                    [nswindow makeFirstResponder:w->ns.view];\n                    NSNotification *resize = [NSNotification notificationWithName:NSWindowDidResizeNotification object:nswindow];\n                    [w->ns.delegate performSelector:@selector(windowDidResize:) withObject:resize afterDelay:0];\n                }\n                // Lift the constraint guard after a delay, even if the window\n                // was not found (destroyed), to keep the flag consistent (#9572).\n                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(500 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{\n                    _GLFWwindow *w2 = NULL;\n                    for (_GLFWwindow *ww = _glfw.windowListHead; ww; ww = ww->next) {\n                        if (ww->id == wid) { w2 = ww; break; }\n                    }\n                    if (w2) w2->ns.suppress_frame_constraints = false;\n                });\n            });\n        }\n    }\n    [self performSelector:@selector(request_delayed_cursor_update:) withObject:nil afterDelay:0.3];\n}\n\n@end // }}}\n\n// Text input context class for the GLFW content view {{{\n\n@interface GLFWTextInputContext : NSTextInputContext\n@end\n\n@implementation GLFWTextInputContext\n- (void)doCommandBySelector:(SEL)selector\n{\n    // interpretKeyEvents: May call insertText: or doCommandBySelector:.\n    // With the default macOS keybindings, pressing certain key combinations\n    // (e.g. Ctrl+/, Ctrl+Cmd+Down/Left/Right) will produce a beep sound.\n    debug_key(\"\\n\\tTextInputCtx: doCommandBySelector: (%s)\\n\", [NSStringFromSelector(selector) UTF8String]);\n    if (forward_dictation_selector_to_app(selector, nil)) return;\n}\n@end // }}}\n\n// File Promise Provider Delegate for async drag data {{{\n\n// Structure to hold async drag state\n@interface GLFWFilePromiseProviderDelegate : NSObject <NSFilePromiseProviderDelegate>\n{\n    GLFWid windowId, instanceId;\n    char* mimeType;  // MIME type for this provider\n    NSFileHandle *file_handle;\n    NSURL *file_url;\n    void (^completion_handler)(NSError*);\n}\n\n- (instancetype)initWithWindow:(_GLFWwindow*)initWindow mimeType:(const char*)mime instanceId:(GLFWid)iid;\n- (void)request_drag_data;\n- (void)end_transfer:(int)errorCode;\n- (void)end_transfer_with_error:(NSError*)err;\n- (bool)is_mimetype:(const char*)mime_type;\n@end\n\n@interface GLFWDraggingSource : NSObject <NSDraggingSource> {\n   NSPoint start_point, current_point;\n}\n@end\n\n// }}}\n\n// Content view class for the GLFW window {{{\n\n@interface GLFWContentView : NSView <NSTextInputClient>\n{\n    _GLFWwindow* window;\n    NSTrackingArea* trackingArea;\n    GLFWTextInputContext* input_context;\n    NSMutableAttributedString* markedText;\n    NSRect markedRect;\n    bool marked_text_cleared_by_insert;\n    int in_key_handler;\n    NSString *input_source_at_last_key_event;\n    GLFWDraggingSource *dragging_source;\n}\n\n- (void) removeGLFWWindow;\n- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow;\n- (GLFWDraggingSource*)draggingSource;\n@end\n\n@implementation GLFWContentView\n\n- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow\n{\n    self = [super init];\n    if (self != nil)\n    {\n        window = initWindow;\n        trackingArea = nil;\n        input_context = [[GLFWTextInputContext alloc] initWithClient:self];\n        dragging_source = [[GLFWDraggingSource alloc] init];\n        markedText = [[NSMutableAttributedString alloc] init];\n        markedRect = NSMakeRect(0.0, 0.0, 0.0, 0.0);\n        input_source_at_last_key_event = nil;\n        in_key_handler = 0;\n        self.identifier = @\"kitty-content-view\";\n\n        [self updateTrackingAreas];\n        char tab_mime[64];\n        snprintf(tab_mime, sizeof(tab_mime), \"application/net.kovidgoyal.kitty-tab-%d\", getpid());\n        NSMutableArray *types = [NSMutableArray arrayWithObjects:\n            NSPasteboardTypeFileURL, NSPasteboardTypeString, NSPasteboardTypeURL, NSPasteboardTypeColor,\n            NSPasteboardTypeFont, NSPasteboardTypeHTML, NSPasteboardTypePDF, NSPasteboardTypePNG,\n            NSPasteboardTypeRTF, NSPasteboardTypeSound, NSPasteboardTypeTIFF,\n            UTTypeData.identifier, UTTypeItem.identifier, UTTypeContent.identifier,\n            mime_to_uti(tab_mime),\n        nil];\n        // Add file promise types\n        [types addObjectsFromArray:[NSFilePromiseReceiver readableDraggedTypes]];\n        [self registerForDraggedTypes:types];\n    }\n\n    return self;\n}\n\n- (void)dealloc\n{\n    [trackingArea release];\n    [markedText release];\n    [dragging_source release];\n    if (input_source_at_last_key_event) [input_source_at_last_key_event release];\n    [input_context release];\n    [super dealloc];\n}\n\n- (void) removeGLFWWindow { window = NULL; }\n\n- (GLFWDraggingSource*)draggingSource { return dragging_source; }\n\n- (_GLFWwindow*)glfwWindow { return window; }\n\n- (BOOL)isOpaque { return window && [window->ns.object isOpaque]; }\n\n- (BOOL)canBecomeKeyView { return YES; }\n\n- (BOOL)acceptsFirstResponder { return YES; }\n\n- (void) viewWillStartLiveResize\n{\n    if (!window) return;\n    window->ns.live_resize_in_progress = true;\n    _glfwInputLiveResize(window, true);\n}\n\n- (void)viewDidEndLiveResize\n{\n    if (!window) return;\n    window->ns.live_resize_in_progress = false;\n    _glfwInputLiveResize(window, false);\n}\n\n- (BOOL)wantsUpdateLayer\n{\n    return YES;\n}\n\n- (void)updateLayer\n{\n    if (!window) return;\n    if (window->context.client != GLFW_NO_API) {\n        @try {\n            [window->context.nsgl.object update];\n        } @catch (NSException *e) {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"Failed to update NSGL Context object with error: %s (%s)\",\n                            [[e name] UTF8String], [[e reason] UTF8String]);\n        }\n    }\n\n    _glfwInputWindowDamage(window);\n}\n\n- (void)cursorUpdate:(NSEvent *)event\n{\n    (void)event;\n    if (window) updateCursorImage(window);\n}\n\n- (BOOL)acceptsFirstMouse:(NSEvent *)event\n{\n    (void)event;\n    return NO;  // changed by Kovid, to follow cocoa platform conventions\n}\n\n- (void)mouseDown:(NSEvent *)event\n{\n    if (!window) return;\n    _glfwInputMouseClick(window,\n                         GLFW_MOUSE_BUTTON_LEFT,\n                         GLFW_PRESS,\n                         translateFlags([event modifierFlags]));\n}\n\n- (void)mouseDragged:(NSEvent *)event\n{\n    [self mouseMoved:event];\n}\n\n- (void)mouseUp:(NSEvent *)event\n{\n    if (!window) return;\n    _glfwInputMouseClick(window,\n                         GLFW_MOUSE_BUTTON_LEFT,\n                         GLFW_RELEASE,\n                         translateFlags([event modifierFlags]));\n}\n\n- (void)mouseMoved:(NSEvent *)event\n{\n    if (!window) return;\n    if (window->cursorMode == GLFW_CURSOR_DISABLED)\n    {\n        const double dx = [event deltaX] - window->ns.cursorWarpDeltaX;\n        const double dy = [event deltaY] - window->ns.cursorWarpDeltaY;\n\n        _glfwInputCursorPos(window,\n                            window->virtualCursorPosX + dx,\n                            window->virtualCursorPosY + dy);\n    }\n    else\n    {\n        const NSRect contentRect = [window->ns.view frame];\n        // NOTE: The returned location uses base 0,1 not 0,0\n        const NSPoint pos = [event locationInWindow];\n\n        _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y);\n    }\n\n    window->ns.cursorWarpDeltaX = 0;\n    window->ns.cursorWarpDeltaY = 0;\n\n    if (window->ns.delayed_cursor_update_requested) {\n        window->ns.delayed_cursor_update_requested = false;\n        if (cursorInContentArea(window)) updateCursorImage(window);\n    }\n}\n\n- (void)rightMouseDown:(NSEvent *)event\n{\n    if (!window) return;\n    _glfwInputMouseClick(window,\n                         GLFW_MOUSE_BUTTON_RIGHT,\n                         GLFW_PRESS,\n                         translateFlags([event modifierFlags]));\n}\n\n- (void)rightMouseDragged:(NSEvent *)event\n{\n    [self mouseMoved:event];\n}\n\n- (void)rightMouseUp:(NSEvent *)event\n{\n    if (!window) return;\n    _glfwInputMouseClick(window,\n                         GLFW_MOUSE_BUTTON_RIGHT,\n                         GLFW_RELEASE,\n                         translateFlags([event modifierFlags]));\n}\n\n- (void)otherMouseDown:(NSEvent *)event\n{\n    if (!window) return;\n    _glfwInputMouseClick(window,\n                         (int) [event buttonNumber],\n                         GLFW_PRESS,\n                         translateFlags([event modifierFlags]));\n}\n\n- (void)otherMouseDragged:(NSEvent *)event\n{\n    [self mouseMoved:event];\n}\n\n- (void)otherMouseUp:(NSEvent *)event\n{\n    if (!window) return;\n    _glfwInputMouseClick(window,\n                         (int) [event buttonNumber],\n                         GLFW_RELEASE,\n                         translateFlags([event modifierFlags]));\n}\n\n- (void)mouseExited:(NSEvent *)event\n{\n    (void)event;\n    if (!window) return;\n    _glfwInputCursorEnter(window, false);\n    [[NSCursor arrowCursor] set];\n}\n\n- (void)mouseEntered:(NSEvent *)event\n{\n    (void)event;\n    if (!window) return;\n    _glfwInputCursorEnter(window, true);\n    updateCursorImage(window);\n}\n\n- (void)viewDidChangeBackingProperties\n{\n    if (!window) return;\n    const NSRect contentRect = get_window_size_without_border_in_logical_pixels(window);\n    const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];\n\n    if (fbRect.size.width != window->ns.fbWidth ||\n        fbRect.size.height != window->ns.fbHeight)\n    {\n        window->ns.fbWidth  = (int)fbRect.size.width;\n        window->ns.fbHeight = (int)fbRect.size.height;\n        _glfwInputFramebufferSize(window, (int)fbRect.size.width, (int)fbRect.size.height);\n    }\n\n    const float xscale = fbRect.size.width / contentRect.size.width;\n    const float yscale = fbRect.size.height / contentRect.size.height;\n\n    if (xscale != window->ns.xscale || yscale != window->ns.yscale)\n    {\n        window->ns.xscale = xscale;\n        window->ns.yscale = yscale;\n        _glfwInputWindowContentScale(window, xscale, yscale);\n\n        if (window->ns.retina && window->ns.layer)\n            [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];\n    }\n}\n\n- (void)drawRect:(NSRect)rect\n{\n    (void)rect;\n    if (!window) return;\n    _glfwInputWindowDamage(window);\n}\n\n- (void)updateTrackingAreas\n{\n    if (window && [window->ns.object areCursorRectsEnabled])\n        [window->ns.object disableCursorRects];\n    if (trackingArea != nil)\n    {\n        [self removeTrackingArea:trackingArea];\n        [trackingArea release];\n    }\n\n    const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |\n                                          NSTrackingActiveAlways |\n                                          NSTrackingEnabledDuringMouseDrag |\n                                          NSTrackingCursorUpdate |\n                                          NSTrackingInVisibleRect |\n                                          NSTrackingAssumeInside;\n\n    trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]\n                                                options:options\n                                                  owner:self\n                                               userInfo:nil];\n\n    [self addTrackingArea:trackingArea];\n    [super updateTrackingAreas];\n}\n\n- (NSTextInputContext *)inputContext\n{\n    return input_context;\n}\n\nstatic UInt32\nconvert_cocoa_to_carbon_modifiers(NSUInteger flags) {\n    UInt32 mods = 0;\n    if (flags & NSEventModifierFlagShift)\n        mods |= shiftKey;\n    if (flags & NSEventModifierFlagControl)\n        mods |= controlKey;\n    if (flags & NSEventModifierFlagOption)\n        mods |= optionKey;\n    if (flags & NSEventModifierFlagCommand)\n        mods |= cmdKey;\n    if (flags & NSEventModifierFlagCapsLock)\n        mods |= alphaLock;\n\n    return (mods >> 8) & 0xFF;\n}\n\nstatic void\nconvert_utf16_to_utf8(UniChar *src, UniCharCount src_length, char *dest, size_t dest_sz) {\n    CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,\n                                                            src,\n                                                            src_length,\n                                                            kCFAllocatorNull);\n    CFStringGetCString(string,\n                       dest,\n                       dest_sz,\n                       kCFStringEncodingUTF8);\n    CFRelease(string);\n}\n\nstatic bool\nalternate_key_is_ok(uint32_t key, uint32_t akey) {\n    return akey > 31 && akey != key && !is_pua_char(akey);\n}\n\nstatic void\nadd_alternate_keys(GLFWkeyevent *ev, NSEvent *event) {\n    ev->alternate_key = translateKey(ev->native_key, false);\n    if (!alternate_key_is_ok(ev->key, ev->alternate_key)) ev->alternate_key = 0;\n    if (ev->mods & GLFW_MOD_SHIFT) {\n        NSString *ci = [event charactersIgnoringModifiers];\n        if (ci) {\n            unsigned sz = [ci length];\n            if (sz > 0) {\n                UniChar buf[2] = {0};\n                buf[0] = [ci characterAtIndex:0];\n                if (sz > 1) buf[1] = [ci characterAtIndex:1];\n                ev->shifted_key = get_first_codepoint(buf, sz);\n            }\n        }\n        if (!alternate_key_is_ok(ev->key, ev->shifted_key)) ev->shifted_key = 0;\n    }\n}\n\nstatic bool\nis_ascii_control_char(char x) {\n    return x == 0 || (1 <= x && x <= 31) || x == 127;\n}\n\n- (void)keyDown:(NSEvent *)event\n{\n#define CLEAR_PRE_EDIT_TEXT glfw_keyevent.text = NULL; glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED; _glfwInputKeyboard(window, &glfw_keyevent);\n#define UPDATE_PRE_EDIT_TEXT glfw_keyevent.text = [[markedText string] UTF8String]; glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED; _glfwInputKeyboard(window, &glfw_keyevent);\n\n    const bool previous_has_marked_text = [self hasMarkedText];\n    if (input_context && (!input_source_at_last_key_event || ![input_source_at_last_key_event isEqualToString:input_context.selectedKeyboardInputSource])) {\n        if (input_source_at_last_key_event) {\n            debug_key(\"Input source changed, clearing pre-edit text and resetting deadkey state\\n\");\n            GLFWkeyevent dummy = {.action = GLFW_RELEASE, .ime_state = GLFW_IME_PREEDIT_CHANGED};\n            window->ns.deadKeyState = 0;\n            _glfwInputKeyboard(window, &dummy); // clear pre-edit text\n            [input_source_at_last_key_event release];\n            input_source_at_last_key_event = nil;\n        }\n        input_source_at_last_key_event = [input_context.selectedKeyboardInputSource retain];\n        [self unmarkText];\n    }\n\n    const unsigned int keycode = [event keyCode];\n    const NSUInteger flags = [event modifierFlags];\n    const int mods = translateFlags(flags);\n    const uint32_t key = translateKey(keycode, true);\n    const bool process_text = !_glfw.ignoreOSKeyboardProcessing && (!window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, keycode, flags) != 1);\n    _glfw.ns.text[0] = 0;\n    if (keycode == 0x33 /* backspace */ || keycode == 0x35 /* escape */ || (keycode == 0x04 /* h */ && mods == GLFW_MOD_CONTROL)) [self unmarkText];\n    GLFWkeyevent glfw_keyevent = {.key = key, .native_key = keycode, .native_key_id = keycode, .action = GLFW_PRESS, .mods = mods};\n    if (!_glfw.ns.unicodeData) {\n        // Using the cocoa API for key handling is disabled, as there is no\n        // reliable way to handle dead keys using it. Only use it if the\n        // keyboard unicode data is not available.\n        if (process_text) {\n            // this will call insertText with the text for this event, if any\n            [self interpretKeyEvents:@[event]];\n        }\n    } else {\n        static UniChar text[256];\n        UniCharCount char_count = 0;\n        const bool in_compose_sequence = window->ns.deadKeyState != 0;\n        if (UCKeyTranslate(\n                    [(NSData*) _glfw.ns.unicodeData bytes],\n                    keycode,\n                    kUCKeyActionDown,\n                    convert_cocoa_to_carbon_modifiers(flags),\n                    LMGetKbdType(),\n                    (process_text ? 0 : kUCKeyTranslateNoDeadKeysMask),\n                    &(window->ns.deadKeyState),\n                    sizeof(text)/sizeof(text[0]),\n                    &char_count,\n                    text\n                    ) != noErr) {\n            debug_key(\"UCKeyTranslate failed for keycode: 0x%x (%s) %s\\n\",\n                    keycode, safe_name_for_keycode(keycode), format_mods(mods));\n            window->ns.deadKeyState = 0;\n            return;\n        }\n        debug_key(\"\\x1b[31mPress:\\x1b[m native_key: 0x%x (%s) glfw_key: 0x%x %schar_count: %lu deadKeyState: %u repeat: %d \",\n                keycode, safe_name_for_keycode(keycode), key, format_mods(mods), char_count, window->ns.deadKeyState, event.ARepeat);\n        marked_text_cleared_by_insert = false;\n        if (process_text) {\n            in_key_handler = 1;\n            // this will call insertText which will fill up _glfw.ns.text\n            [self interpretKeyEvents:@[event]];\n            in_key_handler = 0;\n        } else {\n            window->ns.deadKeyState = 0;\n        }\n        if (window->ns.deadKeyState && (char_count == 0 || keycode == 0x75)) {\n            // 0x75 is the delete key which needs to be ignored during a compose sequence\n            debug_key(\"Sending pre-edit text for dead key (text: %s markedText: %s).\\n\", format_text(_glfw.ns.text), glfw_keyevent.text);\n            UPDATE_PRE_EDIT_TEXT;\n            return;\n        }\n        if (in_compose_sequence) {\n            debug_key(\"Clearing pre-edit text at end of compose sequence\\n\");\n            CLEAR_PRE_EDIT_TEXT;\n        }\n    }\n    if (is_ascii_control_char(_glfw.ns.text[0])) _glfw.ns.text[0] = 0;  // don't send text for ascii control codes\n    debug_key(\"text: %s glfw_key: %s marked_text: (%s)\\n\",\n            format_text(_glfw.ns.text), _glfwGetKeyName(key), [[markedText string] UTF8String]);\n    bool bracketed_ime = false;\n    if (!window->ns.deadKeyState) {\n        if ([self hasMarkedText]) {\n            if (!marked_text_cleared_by_insert) {\n                UPDATE_PRE_EDIT_TEXT;\n            } else bracketed_ime = true;\n        } else if (previous_has_marked_text) {\n            CLEAR_PRE_EDIT_TEXT;\n        }\n        if (([self hasMarkedText] || previous_has_marked_text) && !_glfw.ns.text[0]) {\n            // do not pass keys like BACKSPACE while there's pre-edit text, let IME handle it\n            debug_key(\"Ignoring key press as IME is active and it generated no text\\n\");\n            return;\n        }\n    }\n    if (bracketed_ime) {\n        // insertText followed by setMarkedText\n        CLEAR_PRE_EDIT_TEXT;\n    }\n    glfw_keyevent.text = _glfw.ns.text;\n    glfw_keyevent.ime_state = GLFW_IME_NONE;\n    add_alternate_keys(&glfw_keyevent, event);\n    _glfwInputKeyboard(window, &glfw_keyevent);\n    if (bracketed_ime) {\n        // insertText followed by setMarkedText\n        UPDATE_PRE_EDIT_TEXT;\n    }\n}\n\nstatic bool\nis_modifier_pressed(NSUInteger flags, NSUInteger target_mask, NSUInteger other_mask, NSUInteger either_mask) {\n    bool target_pressed = (flags & target_mask) != 0;\n    bool other_pressed = (flags & other_mask) != 0;\n    bool either_pressed = (flags & either_mask) != 0;\n    if (either_pressed != (target_pressed || other_pressed)) return either_pressed;\n    return target_pressed;\n}\n\n- (void)flagsChanged:(NSEvent *)event\n{\n    int action = GLFW_RELEASE;\n    const char old_first_char = _glfw.ns.text[0];\n    _glfw.ns.text[0] = 0;\n    const NSUInteger modifierFlags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;\n    const uint32_t key = vk_code_to_functional_key_code([event keyCode]);\n    const unsigned int keycode = [event keyCode];\n    const int mods = translateFlags(modifierFlags);\n    const bool process_text = !_glfw.ignoreOSKeyboardProcessing && (!window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, keycode, modifierFlags) != 1);\n    const char *mod_name = \"unknown\";\n\n    // Code for handling modifier key events copied form SDL_cocoakeyboard.m, with thanks. See IsModifierKeyPressedFunction()\n#define action_for(modname, target_mask, other_mask, either_mask) action = is_modifier_pressed([event modifierFlags], target_mask, other_mask, either_mask) ? GLFW_PRESS : GLFW_RELEASE; mod_name = #modname; break;\n    switch(key) {\n        case GLFW_FKEY_CAPS_LOCK:\n            mod_name = \"capslock\";\n            action = modifierFlags & NSEventModifierFlagCapsLock ? GLFW_PRESS : GLFW_RELEASE; break;\n        case GLFW_FKEY_LEFT_SUPER: action_for(super, NX_DEVICELCMDKEYMASK, NX_DEVICERCMDKEYMASK, NX_COMMANDMASK);\n        case GLFW_FKEY_RIGHT_SUPER: action_for(super, NX_DEVICERCMDKEYMASK, NX_DEVICELCMDKEYMASK, NX_COMMANDMASK);\n        case GLFW_FKEY_LEFT_CONTROL: action_for(ctrl, NX_DEVICELCTLKEYMASK, NX_DEVICERCTLKEYMASK, NX_CONTROLMASK);\n        case GLFW_FKEY_RIGHT_CONTROL: action_for(ctrl, NX_DEVICERCTLKEYMASK, NX_DEVICELCTLKEYMASK, NX_CONTROLMASK);\n        case GLFW_FKEY_LEFT_ALT: action_for(alt, NX_DEVICELALTKEYMASK, NX_DEVICERALTKEYMASK, NX_ALTERNATEMASK);\n        case GLFW_FKEY_RIGHT_ALT: action_for(alt, NX_DEVICERALTKEYMASK, NX_DEVICELALTKEYMASK, NX_ALTERNATEMASK);\n        case GLFW_FKEY_LEFT_SHIFT: action_for(shift, NX_DEVICELSHIFTKEYMASK, NX_DEVICERSHIFTKEYMASK, NX_SHIFTMASK);\n        case GLFW_FKEY_RIGHT_SHIFT: action_for(shift, NX_DEVICERSHIFTKEYMASK, NX_DEVICELSHIFTKEYMASK, NX_SHIFTMASK);\n        default:\n            return;\n    }\n#undef action_for\n    GLFWkeyevent glfw_keyevent = {.key = key, .native_key = keycode, .native_key_id = keycode, .action = action, .mods = mods};\n    debug_key(\"\\x1b[33mflagsChanged:\\x1b[m modifier: %s native_key: 0x%x (%s) glfw_key: 0x%x %s\\n\",\n            mod_name, keycode, safe_name_for_keycode(keycode), key, format_mods(mods));\n    marked_text_cleared_by_insert = false;\n    if (process_text && input_context) {\n        // this will call insertText which will fill up _glfw.ns.text\n        in_key_handler = 2;\n        [input_context handleEvent:event];\n        in_key_handler = 0;\n        if (marked_text_cleared_by_insert) {\n            debug_key(\"Clearing pre-edit text because insertText called from flagsChanged\\n\");\n            CLEAR_PRE_EDIT_TEXT;\n            if (_glfw.ns.text[0]) glfw_keyevent.text = _glfw.ns.text;\n            else _glfw.ns.text[0] = old_first_char;\n        }\n    }\n    glfw_keyevent.ime_state = GLFW_IME_NONE;\n    _glfwInputKeyboard(window, &glfw_keyevent);\n}\n\n- (void)keyUp:(NSEvent *)event\n{\n    const uint32_t keycode = [event keyCode];\n    const uint32_t key = translateKey(keycode, true);\n    const int mods = translateFlags([event modifierFlags]);\n\n    GLFWkeyevent glfw_keyevent = {.key = key, .native_key = keycode, .native_key_id = keycode, .action = GLFW_RELEASE, .mods = mods};\n    add_alternate_keys(&glfw_keyevent, event);\n    debug_key(\"\\x1b[32mRelease:\\x1b[m native_key: 0x%x (%s) glfw_key: 0x%x %s\\n\",\n            keycode, safe_name_for_keycode(keycode), key, format_mods(mods));\n    _glfwInputKeyboard(window, &glfw_keyevent);\n}\n\n#undef CLEAR_PRE_EDIT_TEXT\n#undef UPDATE_PRE_EDIT_TEXT\n\n- (void)scrollWheel:(NSEvent *)event\n{\n    GLFWScrollEvent ev = {\n        .keyboard_modifiers=translateFlags([event modifierFlags]), .unscaled.x = [event scrollingDeltaX], .unscaled.y = [event scrollingDeltaY]};\n    ev.x_offset = ev.unscaled.x;\n    ev.y_offset = ev.unscaled.y;\n    if ([event hasPreciseScrollingDeltas]) {\n        ev.offset_type = GLFW_SCROLL_OFFEST_HIGHRES;\n        float xscale = 1, yscale = 1;\n        _glfwPlatformGetWindowContentScale(window, &xscale, &yscale);\n        if (xscale > 0) ev.x_offset *= xscale;\n        if (yscale > 0) ev.y_offset *= yscale;\n    }\n\n    switch([event momentumPhase]) {\n        case NSEventPhaseBegan: ev.momentum_type = GLFW_MOMENTUM_PHASE_BEGAN; break;\n        case NSEventPhaseStationary: ev.momentum_type = GLFW_MOMENTUM_PHASE_STATIONARY; break;\n        case NSEventPhaseChanged: ev.momentum_type = GLFW_MOMENTUM_PHASE_ACTIVE; break;\n        case NSEventPhaseEnded: ev.momentum_type = GLFW_MOMENTUM_PHASE_ENDED; break;\n        case NSEventPhaseCancelled: ev.momentum_type = GLFW_MOMENTUM_PHASE_CANCELED; break;\n        case NSEventPhaseMayBegin: ev.momentum_type = GLFW_MOMENTUM_PHASE_MAY_BEGIN; break;\n        case NSEventPhaseNone: break;\n    }\n\n    _glfwInputScroll(window, &ev);\n}\n\n// Drop implementation for drag and drop {{{\n// Return YES to receive periodic dragging updates even when the mouse hasn't moved.\n// This allows the application to update acceptance status asynchronously.\n- (BOOL)wantsPeriodicDraggingUpdates\n{\n    return YES;\n}\n\nstatic void\nfree_drop_data(_GLFWwindow *window) {\n    if (window->ns.drop_data.mimes) {\n        for (size_t i = 0; i < window->ns.drop_data.mimes_count; i++) free((void*)window->ns.drop_data.mimes[i]);\n        free(window->ns.drop_data.mimes);\n    }\n    free(window->ns.drop_data.copy_mimes);  // pointer array only; strings owned by mimes[]\n    if (window->ns.drop_data.pasteboard) [window->ns.drop_data.pasteboard release];\n    if (window->ns.drop_data.data_mapping) [window->ns.drop_data.data_mapping release];\n    if (window->ns.drop_data.file_promise_mapping) {\n        NSFileManager *fileManager = [NSFileManager defaultManager];\n        NSError *error = nil;\n        for (NSString *key in window->ns.drop_data.file_promise_mapping) {\n            NSArray *pair = [window->ns.drop_data.file_promise_mapping objectForKey:key];\n            error = nil; if (pair[1] != [NSNull null]) [pair[1] closeAndReturnError:&error];\n            error = nil; [fileManager removeItemAtURL:pair[0] error:&error];\n        }\n        [window->ns.drop_data.file_promise_mapping release];\n    }\n    memset(&window->ns.drop_data, 0, sizeof(_GLFWDropData));\n}\n\nstatic void\nupdate_drop_state(_GLFWwindow *window, size_t accepted_count) {\n    _GLFWDropData *d = &window->ns.drop_data;\n    d->copy_mimes_count = accepted_count;\n    d->drag_accepted = accepted_count > 0;\n}\n\n// Reset the working copy of mimes so the next callback sees the full original\n// list.  Returns false on allocation failure.\nstatic bool\nreset_drop_copy_mimes(_GLFWDropData *d) {\n    if (d->mimes_count == 0) { d->copy_mimes_count = 0; return true; }\n    if (!d->copy_mimes) {\n        d->copy_mimes = malloc(d->mimes_count * sizeof(const char*));\n        if (!d->copy_mimes) return false;\n    }\n    memcpy(d->copy_mimes, d->mimes, d->mimes_count * sizeof(const char*));\n    d->copy_mimes_count = d->mimes_count;\n    return true;\n}\n\n- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender\n{\n    const NSRect contentRect = [window->ns.view frame];\n    const NSPoint pos = [sender draggingLocation];\n    double xpos = pos.x;\n    double ypos = contentRect.size.height - pos.y;\n    free_drop_data(window);\n\n    // Get MIME types from the dragging pasteboard\n    NSPasteboard* pasteboard = [sender draggingPasteboard];\n\n    // Count total types across all pasteboard items plus 2 for uri-list and text/plain\n    size_t max_types = 2;\n    for (NSPasteboardItem* item in pasteboard.pasteboardItems) max_types += [item.types count];\n    NSArray *classes = @[[NSFilePromiseReceiver class]];\n    NSArray *receivers = [pasteboard readObjectsForClasses:classes options:@{}];\n    for (NSFilePromiseReceiver *receiver in receivers) max_types += [receiver.fileTypes count];\n\n    // Pre-allocate C array for MIME types\n    const char** mime_array = (const char**)calloc(max_types, sizeof(const char*));\n    if (!mime_array) return NSDragOperationNone;\n\n    size_t mime_count = 0;\n\n    // Check for common types first (use _glfw_strdup since we need to own the strings)\n    NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};\n    if ([pasteboard canReadObjectForClasses:@[[NSURL class]] options:options]) {\n        mime_array[mime_count++] = _glfw_strdup(\"text/uri-list\");\n    }\n    if ([pasteboard canReadObjectForClasses:@[[NSString class]] options:nil]) {\n        mime_array[mime_count++] = _glfw_strdup(\"text/plain\");\n    }\n#define add_mime(uti) {  \\\n    const char* mime = uti_to_mime(uti); \\\n    if (mime && mime[0]) { \\\n        bool duplicate = false; \\\n        for (size_t i = 0; i < mime_count; i++) { \\\n            if (strcmp(mime_array[i], mime) == 0) { \\\n                duplicate = true; \\\n                break; \\\n            } \\\n        } \\\n        if (!duplicate) mime_array[mime_count++] = _glfw_strdup(mime); \\\n    } \\\n}\n    // Get file promise based types\n    for (NSFilePromiseReceiver *receiver in receivers) {\n        for (NSString *uti in receiver.fileTypes) {\n            add_mime(uti);\n        }\n    }\n\n    // Get additional types from pasteboard items\n    for (NSPasteboardItem* item in pasteboard.pasteboardItems) {\n        for (NSPasteboardType uti in item.types) {\n            add_mime(uti);\n        }\n    }\n\n    window->ns.drop_data.mimes = mime_array;\n    window->ns.drop_data.mimes_count = mime_count;\n    bool from_self = ([sender draggingSource] != nil);\n    _GLFWDropData *d = &window->ns.drop_data;\n    if (reset_drop_copy_mimes(d)) {\n        size_t accepted_count = _glfwInputDropEvent(window, GLFW_DROP_ENTER, xpos, ypos, d->copy_mimes, d->copy_mimes_count, from_self);\n        update_drop_state(window, accepted_count);\n    }\n    return window->ns.drop_data.drag_accepted ? NSDragOperationGeneric : NSDragOperationNone;\n}\n\n- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender\n{\n    if (!window->ns.drop_data.drag_accepted) return NSDragOperationNone;\n    const NSRect contentRect = [window->ns.view frame];\n    const NSPoint pos = [sender draggingLocation];\n    double xpos = pos.x;\n    double ypos = contentRect.size.height - pos.y;\n\n    bool from_self = ([sender draggingSource] != nil);\n    _GLFWDropData *d = &window->ns.drop_data;\n    if (reset_drop_copy_mimes(d)) {\n        size_t accepted_count = _glfwInputDropEvent(window, GLFW_DROP_MOVE, xpos, ypos, d->copy_mimes, d->copy_mimes_count, from_self);\n        update_drop_state(window, accepted_count);\n    }\n    return window->ns.drop_data.drag_accepted ? NSDragOperationGeneric : NSDragOperationNone;\n}\n\n- (void)draggingExited:(id <NSDraggingInfo>)sender\n{\n    bool from_self = ([sender draggingSource] != nil);\n    _GLFWDropData *d = &window->ns.drop_data;\n    if (reset_drop_copy_mimes(d)) {\n        size_t accepted_count = _glfwInputDropEvent(window, GLFW_DROP_LEAVE, 0, 0, d->copy_mimes, d->copy_mimes_count, from_self);\n        update_drop_state(window, accepted_count);\n    }\n    free_drop_data(window);\n}\n\n- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender\n{\n    if (!window->ns.drop_data.drag_accepted) return NO;\n    const NSRect contentRect = [window->ns.view frame];\n    const NSPoint pos = [sender draggingLocation];\n    double xpos = pos.x;\n    double ypos = contentRect.size.height - pos.y;\n    bool from_self = ([sender draggingSource] != nil);\n    _GLFWDropData *d = &window->ns.drop_data;\n    if (!reset_drop_copy_mimes(d)) return NO;\n    size_t num_accepted = _glfwInputDropEvent(window, GLFW_DROP_DROP, xpos, ypos, d->copy_mimes, d->copy_mimes_count, from_self);\n    if (d->copy_mimes) {\n        update_drop_state(window, num_accepted);\n        window->ns.drop_data.pasteboard = [[sender draggingPasteboard] retain];\n        for (size_t i = 0; i < num_accepted; i++)\n            _glfwPlatformRequestDropData(window, d->copy_mimes[i]);\n    }\n    return YES;\n}\n\nvoid\n_glfwPlatformRequestDropUpdate(_GLFWwindow* window UNUSED) {\n    // No-op since macOS is calling the drop move callback periodically anyway\n    // thanks to wantsPeriodicDraggingUpdates and we have no way to inform\n    // macOS of any changes except in the cocoa callbacks.\n}\n\nstatic void\nsend_data_available_event_on_next_event_loop_tick(GLFWid wid, const char *mime) {\n    char *mt = _glfw_strdup(mime);\n    dispatch_async(dispatch_get_main_queue(), ^{\n        _GLFWwindow *window = _glfwWindowForId(wid);\n        if (window) {\n            const char *mimes[1] = {mt};\n            _glfwInputDropEvent(window, GLFW_DROP_DATA_AVAILABLE, 0, 0, mimes, 1, false);\n        }\n        free(mt);\n    });\n}\n\nint\n_glfwPlatformRequestDropData(_GLFWwindow *window UNUSED, const char *mime) {\n    NSPasteboard* pasteboard = window->ns.drop_data.pasteboard;\n    if (!pasteboard) return EINVAL;\n    GLFWid wid = window->id;\n    if (window->ns.drop_data.data_mapping == nil) window->ns.drop_data.data_mapping = [[NSMutableDictionary alloc] init];\n    NSArray *pair;\n    if ((pair = window->ns.drop_data.data_mapping[@(mime)])) {\n        window->ns.drop_data.data_mapping[@(mime)] = @[pair[0], @0];\n        send_data_available_event_on_next_event_loop_tick(wid, mime);\n        return 0;\n    }\n    if (window->ns.drop_data.file_promise_mapping == nil) window->ns.drop_data.file_promise_mapping = [[NSMutableDictionary alloc] init];\n    if ((pair = window->ns.drop_data.file_promise_mapping[@(mime)])) {\n        if (pair[0] == [NSNull null]) return 0;  // waiting for promise\n        if (pair[1] != [NSNull null]) {\n            NSFileHandle *h = pair[1]; NSError *error = nil;\n            [h seekToOffset:0 error:&error];\n        }\n        send_data_available_event_on_next_event_loop_tick(wid, mime);\n        return 0;\n    }\n    NSData* data = nil; NSFilePromiseReceiver *file_promise = nil;\n    // Handle special MIME types\n    if (strcmp(mime, \"text/uri-list\") == 0) {\n        NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};\n        NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]] options:options];\n        if (urls && [urls count] > 0) {\n            NSMutableString *uri_list = [NSMutableString stringWithCapacity:4096];\n            for (NSURL* url in urls) {\n                if ([uri_list length] > 0) [uri_list appendString:@\"\\n\"];\n                if (url.fileURL) [uri_list appendString:url.filePathURL.absoluteString];\n                else [uri_list appendString:url.absoluteString];\n            }\n            data = [uri_list dataUsingEncoding:NSUTF8StringEncoding];\n        }\n    } else if (strcmp(mime, \"text/plain\") == 0 || strcmp(mime, \"text/plain;charset=utf-8\") == 0) {\n        NSArray* strings = [pasteboard readObjectsForClasses:@[[NSString class]] options:nil];\n        if (strings && [strings count] > 0) {\n            NSString* str = strings[0];\n            data = [str dataUsingEncoding:NSUTF8StringEncoding];\n        }\n    }\n    if (data == nil) {\n        // Try to read data for other MIME types using UTI\n        NSString* uti = mime_to_uti(mime);\n        if (uti) {\n            NSPasteboardType pbType = [pasteboard availableTypeFromArray:@[uti]];\n            if (pbType) data = [pasteboard dataForType:pbType];\n        }\n        if (data == nil) {\n            // look in the file promise providers\n            NSArray *receivers = [pasteboard readObjectsForClasses:@[[NSFilePromiseReceiver class]] options:@{}];\n            for (NSFilePromiseReceiver *receiver in receivers) {\n                for (NSString *uti in receiver.fileTypes) {\n                    const char *q = uti_to_mime(uti);\n                    if (q && strcmp(q, mime) == 0) {\n                        file_promise = receiver;\n                        break;\n                    }\n                }\n                if (file_promise) break;\n            }\n        }\n    }\n    if (!data && !file_promise) return ENOENT;\n    if (file_promise != nil) {\n        window->ns.drop_data.file_promise_mapping[@(mime)] = @[[NSNull null], [NSNull null], [NSNull null]];\n        char *mt = _glfw_strdup(mime);\n        [file_promise receivePromisedFilesAtDestination:[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]\n            options:@{} operationQueue:[NSOperationQueue mainQueue] reader:^(NSURL *fileURL, NSError *errorOrNil) {\n            _GLFWwindow *window = _glfwWindowForId(wid);\n            if (!window || !window->ns.drop_data.file_promise_mapping) return;\n            id null = [NSNull null];\n            if (errorOrNil) {\n                NSLog(@\"Error receiving file: %@: %@\", fileURL, errorOrNil);\n                window->ns.drop_data.file_promise_mapping[@(mt)] = @[fileURL, null, errorOrNil];\n            } else {\n                NSError *err = nil;\n                NSFileHandle *file_handle = [NSFileHandle fileHandleForReadingFromURL:fileURL error:&err];\n                window->ns.drop_data.file_promise_mapping[@(mt)] = err ? @[fileURL, null, err] : @[fileURL, file_handle, null];\n            }\n            const char *mimes[1] = {mt};\n            _glfwInputDropEvent(window, GLFW_DROP_DATA_AVAILABLE, 0, 0, mimes, 1, false);\n            free(mt);\n        }];\n    } else {\n        window->ns.drop_data.data_mapping[@(mime)] = @[data, @0];\n        const char *mimes[1] = {mime};\n        _glfwInputDropEvent(window, GLFW_DROP_DATA_AVAILABLE, 0, 0, mimes, 1, false);\n    }\n    return 0;\n}\n\nssize_t\n_glfwPlatformReadAvailableDropData(GLFWwindow *w, GLFWDropEvent *ev, char *buffer, size_t capacity) {\n    _GLFWwindow *window = (_GLFWwindow*)w; const char *mime = ev->mimes[0];\n    NSArray *pair;\n    if ((pair = window->ns.drop_data.data_mapping[@(mime)])) {\n        NSData *data = pair[0];\n        size_t offset = [pair[1] unsignedIntegerValue];\n        NSUInteger dataLength = [data length];\n        if (offset >= dataLength) return 0;  // EOF\n        NSUInteger remaining = dataLength - offset;\n        NSUInteger to_read = (remaining < capacity) ? remaining : capacity;\n        [data getBytes:buffer range:NSMakeRange(offset, to_read)];\n        offset += to_read;\n        window->ns.drop_data.data_mapping[@(mime)] = @[data, @(offset)];\n        if (to_read) send_data_available_event_on_next_event_loop_tick(window->id, mime);\n        return (ssize_t)to_read;\n    }\n    if ((pair = window->ns.drop_data.file_promise_mapping[@(mime)])) {\n        id null = [NSNull null];\n        if (pair[0] == null) { return -ENOENT; }\n        if (pair[2] != null) {\n            NSError *err = pair[2];\n            if ([err.domain isEqualToString:NSPOSIXErrorDomain]) return -err.code;\n            NSError *underlyingError = err.userInfo[NSUnderlyingErrorKey];\n            if (underlyingError && [underlyingError.domain isEqualToString:NSPOSIXErrorDomain]) return -underlyingError.code;\n            return -EIO;\n        }\n        NSFileHandle *h = pair[1];\n        int fd = h.fileDescriptor;\n        ssize_t bytesRead; do {\n            bytesRead = read(fd, buffer, capacity);\n        } while (bytesRead == -1 && errno == EINTR);\n        bytesRead = bytesRead < 0 ? -errno : bytesRead;\n        if (bytesRead > 0) send_data_available_event_on_next_event_loop_tick(window->id, mime);\n        return bytesRead;\n    }\n    return -ENOENT;\n}\n\nvoid\n_glfwPlatformEndDrop(GLFWwindow *w UNUSED, GLFWDragOperationType op UNUSED) {\n    free_drop_data((_GLFWwindow*)w);\n}\n// }}}\n\n- (BOOL)hasMarkedText\n{\n    return [markedText length] > 0;\n}\n\n- (NSRange)markedRange\n{\n    if ([markedText length] > 0)\n        return NSMakeRange(0, [markedText length] - 1);\n    else\n        return kEmptyRange;\n}\n\n- (NSRange)selectedRange\n{\n    // Return position 0 with no selection to indicate text can be inserted.\n    // This is required for macOS dictation to work - returning kEmptyRange\n    // (NSNotFound, 0) causes dictation to fail because the system doesn't\n    // know where to insert text. See https://github.com/kovidgoyal/kitty/issues/3732\n    return NSMakeRange(0, 0);\n}\n\n- (void)setMarkedText:(id)string\n        selectedRange:(NSRange)selectedRange\n     replacementRange:(NSRange)replacementRange\n{\n    const char *s = polymorphic_string_as_utf8(string);\n    debug_key(\"\\n\\tsetMarkedText: %s selectedRange: (%lu, %lu) replacementRange: (%lu, %lu)\\n\", s, selectedRange.location, selectedRange.length, replacementRange.location, replacementRange.length);\n    if (string == nil || !s[0]) {\n        bool had_marked_text = [self hasMarkedText];\n        [self unmarkText];\n        if (had_marked_text && (!in_key_handler || in_key_handler == 2)) {\n            debug_key(\"Clearing pre-edit because setMarkedText called from %s\\n\", in_key_handler ? \"flagsChanged\" : \"event loop\");\n            GLFWkeyevent glfw_keyevent = {.ime_state = GLFW_IME_PREEDIT_CHANGED};\n            _glfwInputKeyboard(window, &glfw_keyevent);\n            _glfw.ns.text[0] = 0;\n        }\n        return;\n    }\n    if ([string isKindOfClass:[NSAttributedString class]]) {\n        if (((NSMutableAttributedString*)string).length == 0) { [self unmarkText]; return; }\n        [markedText release];\n        markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string];\n    } else {\n        if (((NSString*)string).length == 0) { [self unmarkText]; return; }\n        [markedText release];\n        markedText = [[NSMutableAttributedString alloc] initWithString:string];\n    }\n    if (!in_key_handler || in_key_handler == 2) {\n        debug_key(\"Updating IME text in kitty from setMarkedText called from %s: %s\\n\", in_key_handler ? \"flagsChanged\" : \"event loop\", _glfw.ns.text);\n        GLFWkeyevent glfw_keyevent = {.text=[[markedText string] UTF8String], .ime_state = GLFW_IME_PREEDIT_CHANGED};\n        _glfwInputKeyboard(window, &glfw_keyevent);\n        _glfw.ns.text[0] = 0;\n    }\n}\n\n- (void)unmarkText\n{\n    [[markedText mutableString] setString:@\"\"];\n}\n\nvoid _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {\n    [w->ns.view updateIMEStateFor: ev->type focused:(bool)ev->focused];\n}\n\n- (void)updateIMEStateFor:(GLFWIMEUpdateType)which\n                  focused:(bool)focused\n{\n    if (which == GLFW_IME_UPDATE_FOCUS && !focused && [self hasMarkedText] && window) {\n        [input_context discardMarkedText];\n        [self unmarkText];\n        GLFWkeyevent glfw_keyevent = {.ime_state = GLFW_IME_PREEDIT_CHANGED};\n        _glfwInputKeyboard(window, &glfw_keyevent);\n        _glfw.ns.text[0] = 0;\n    }\n    if (which != GLFW_IME_UPDATE_CURSOR_POSITION) return;\n\n    if (_glfwPlatformWindowFocused(window)) [[window->ns.view inputContext] invalidateCharacterCoordinates];\n}\n\n- (NSArray*)validAttributesForMarkedText\n{\n    return [NSArray array];\n}\n\n- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range\n                                               actualRange:(NSRangePointer)actualRange\n{\n    (void)range; (void)actualRange;\n    return nil;\n}\n\n- (NSUInteger)characterIndexForPoint:(NSPoint)point\n{\n    (void)point;\n    return 0;\n}\n\n- (NSRect)firstRectForCharacterRange:(NSRange)range\n                         actualRange:(NSRangePointer)actualRange\n{\n    (void)range; (void)actualRange;\n    if (_glfw.callbacks.get_ime_cursor_position) {\n        GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION };\n        if (window && _glfw.callbacks.get_ime_cursor_position((GLFWwindow*)window, &ev)) {\n            const CGFloat left = (CGFloat)ev.cursor.left / window->ns.xscale;\n            const CGFloat top = (CGFloat)ev.cursor.top / window->ns.yscale;\n            const CGFloat cellWidth = (CGFloat)ev.cursor.width / window->ns.xscale;\n            const CGFloat cellHeight = (CGFloat)ev.cursor.height / window->ns.yscale;\n            debug_key(\"updateIMEPosition: left=%f, top=%f, width=%f, height=%f\\n\", left, top, cellWidth, cellHeight);\n            const NSRect frame = [window->ns.view frame];\n            const NSRect rectInView = NSMakeRect(left,\n                                                 frame.size.height - top - cellHeight,\n                                                 cellWidth, cellHeight);\n            markedRect = [window->ns.object convertRectToScreen: rectInView];\n        }\n    }\n    return markedRect;\n}\n\n- (void)insertText:(id)string replacementRange:(NSRange)replacementRange\n{\n    const char *utf8 = polymorphic_string_as_utf8(string);\n    debug_key(\"\\n\\tinsertText: %s replacementRange: (%lu, %lu)\\n\", utf8, replacementRange.location, replacementRange.length);\n    if ([self hasMarkedText] && !is_ascii_control_char(utf8[0])) {\n        [self unmarkText];\n        marked_text_cleared_by_insert = true;\n        if (!in_key_handler) {\n            debug_key(\"Clearing pre-edit because insertText called from event loop\\n\");\n            GLFWkeyevent glfw_keyevent = {.ime_state = GLFW_IME_PREEDIT_CHANGED};\n            _glfwInputKeyboard(window, &glfw_keyevent);\n            _glfw.ns.text[0] = 0;\n        }\n    }\n    // insertText can be called multiple times for a single key event\n    size_t existing_length = strnlen(_glfw.ns.text, sizeof(_glfw.ns.text));\n    size_t required_length = strlen(utf8) + 1;\n    size_t available_length = sizeof(_glfw.ns.text) - existing_length;\n    if (available_length >= required_length) {\n        memcpy(_glfw.ns.text + existing_length, utf8, required_length); // copies the null terminator from utf8 as well\n        _glfw.ns.text[sizeof(_glfw.ns.text) - 1] = 0;\n        if ((!in_key_handler || in_key_handler == 2) && _glfw.ns.text[0]) {\n            if (!is_ascii_control_char(_glfw.ns.text[0])) {\n                debug_key(\"Sending text to kitty from insertText called from %s: %s\\n\", in_key_handler ? \"flagsChanged\" : \"event loop\", _glfw.ns.text);\n                GLFWkeyevent glfw_keyevent = {.text=_glfw.ns.text, .ime_state=GLFW_IME_COMMIT_TEXT};\n                _glfwInputKeyboard(window, &glfw_keyevent);\n            }\n            _glfw.ns.text[0] = 0;\n        }\n    }\n}\n\n- (void)doCommandBySelector:(SEL)selector\n{\n    debug_key(\"\\n\\tdoCommandBySelector: (%s)\\n\", [NSStringFromSelector(selector) UTF8String]);\n    if (forward_dictation_selector_to_app(selector, self)) return;\n}\n\n- (void)startDictation:(id)sender\n{\n    forward_dictation_selector_to_app(_cmd, sender);\n}\n\n- (void)stopDictation:(id)sender\n{\n    forward_dictation_selector_to_app(_cmd, sender);\n}\n\n- (BOOL)isAccessibilityElement\n{\n    return YES;\n}\n\n- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector\n{\n    // Allow accessibility selectors needed for dictation and other accessibility features\n    // See https://github.com/kovidgoyal/kitty/issues/3732\n    if (selector == @selector(accessibilityRole) ||\n        selector == @selector(accessibilitySelectedText) ||\n        selector == @selector(accessibilitySelectedTextRange) ||\n        selector == @selector(accessibilityNumberOfCharacters) ||\n        selector == @selector(accessibilityInsertionPointLineNumber) ||\n        selector == @selector(accessibilityValue) ||\n        selector == @selector(setAccessibilityValue:)) return YES;\n\n    // Allow accessibility selectors needed for external window management tools\n    // (e.g. Easy Move+Resize) to find and manipulate the window.\n    // See https://github.com/kovidgoyal/kitty/issues/5561\n    if (selector == @selector(accessibilityWindow) ||\n        selector == @selector(accessibilityParent) ||\n        selector == @selector(accessibilityPosition) ||\n        selector == @selector(setAccessibilityPosition:) ||\n        selector == @selector(accessibilitySize) ||\n        selector == @selector(setAccessibilitySize:)) return YES;\n\n    return NO;\n}\n\n#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400)\n\n- (NSAccessibilityRole)accessibilityRole\n{\n    return NSAccessibilityTextAreaRole;\n}\n\n#endif\n\n- (NSString *)accessibilitySelectedText\n{\n    NSString *text = nil;\n    if (_glfw.callbacks.get_current_selection) {\n        char *s = _glfw.callbacks.get_current_selection();\n        if (s) {\n            text = [NSString stringWithUTF8String:s];\n            free(s);\n        }\n    }\n    return text;\n}\n\n// Accessibility methods required for dictation support\n// See https://github.com/kovidgoyal/kitty/issues/3732\n\n- (NSRange)accessibilitySelectedTextRange\n{\n    // Return position 0 with no selection for dictation support\n    return NSMakeRange(0, 0);\n}\n\n- (NSInteger)accessibilityNumberOfCharacters\n{\n    // Terminal doesn't have a fixed text buffer, return 0\n    return 0;\n}\n\n- (NSInteger)accessibilityInsertionPointLineNumber\n{\n    // Return line 0 as the insertion point\n    return 0;\n}\n\n- (NSString *)accessibilityValue\n{\n    // Terminal doesn't expose its buffer as an accessibility value\n    return @\"\";\n}\n\n- (void)setAccessibilityValue:(NSString *)value\n{\n    // When dictation or other accessibility features set text, insert it as keyboard input\n    if (value && [value length] > 0 && window) {\n        const char *utf8 = [value UTF8String];\n        debug_key(\"Inserting text via setAccessibilityValue: %s\\n\", utf8);\n        GLFWkeyevent glfw_keyevent = {.text=utf8, .ime_state=GLFW_IME_COMMIT_TEXT};\n        _glfwInputKeyboard(window, &glfw_keyevent);\n    }\n}\n\n// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/SysServices/Articles/using.html>\n\n// Support services receiving \"public.utf8-plain-text\" and \"NSStringPboardType\"\n- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType\n{\n    if (\n        (!sendType || [sendType isEqual:NSPasteboardTypeString] || [sendType isEqual:@\"NSStringPboardType\"]) &&\n        (!returnType || [returnType isEqual:NSPasteboardTypeString] || [returnType isEqual:@\"NSStringPboardType\"])\n    ) {\n        if (_glfw.callbacks.has_current_selection && _glfw.callbacks.has_current_selection()) return self;\n    }\n    return [super validRequestorForSendType:sendType returnType:returnType];\n}\n\n// Selected text as input to be sent to Services\n// For example, after selecting an absolute path, open the global menu bar kitty->Services and click `Show in Finder`.\n- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types\n{\n    if (!_glfw.callbacks.get_current_selection) return NO;\n    char *text = _glfw.callbacks.get_current_selection();\n    if (!text) return NO;\n    BOOL ans = NO;\n    if (text[0]) {\n        if ([types containsObject:NSPasteboardTypeString] == YES) {\n            [pboard declareTypes:@[NSPasteboardTypeString] owner:self];\n            ans = [pboard setString:@(text) forType:NSPasteboardTypeString];\n        } else if ([types containsObject:@\"NSStringPboardType\"] == YES) {\n            [pboard declareTypes:@[@\"NSStringPboardType\"] owner:self];\n            ans = [pboard setString:@(text) forType:@\"NSStringPboardType\"];\n        }\n        free(text);\n    }\n    return ans;\n}\n\n// Service output to be handled\n// For example, open System Settings->Keyboard->Keyboard Shortcuts->Services->Text, enable `Convert Text to Full Width`, select some text and execute the service.\n- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard\n{\n    NSString* text = nil;\n    NSArray *types = [pboard types];\n    if ([types containsObject:NSPasteboardTypeString] == YES) {\n        text = [pboard stringForType:NSPasteboardTypeString]; // public.utf8-plain-text\n    } else if ([types containsObject:@\"NSStringPboardType\"] == YES) {\n        text = [pboard stringForType:@\"NSStringPboardType\"]; // for older services (need re-encode?)\n    } else {\n        return NO;\n    }\n    if (text && [text length] > 0) {\n        // The service wants us to replace the selection, but we can't replace anything but insert text.\n        const char *utf8 = polymorphic_string_as_utf8(text);\n        debug_key(\"Sending text received in readSelectionFromPasteboard as key event\\n\");\n        GLFWkeyevent glfw_keyevent = {.text=utf8, .ime_state=GLFW_IME_COMMIT_TEXT};\n        _glfwInputKeyboard(window, &glfw_keyevent);\n        // Restore pre-edit text after inserting the received text\n        if ([self hasMarkedText]) {\n            glfw_keyevent.text = [[markedText string] UTF8String];\n            glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED;\n            _glfwInputKeyboard(window, &glfw_keyevent);\n        }\n        return YES;\n    }\n    return NO;\n}\n\n@end\n// }}}\n\n// GLFW window class {{{\n\n@interface GLFWWindow : NSWindow {\n    _GLFWwindow* glfw_window;\n}\n\n- (instancetype)initWithGlfwWindow:(NSRect)contentRect\n                         styleMask:(NSWindowStyleMask)style\n                           backing:(NSBackingStoreType)backingStoreType\n                        initWindow:(_GLFWwindow *)initWindow;\n\n- (void) removeGLFWWindow;\n@end\n\n@implementation GLFWWindow\n\n- (instancetype)initWithGlfwWindow:(NSRect)contentRect\n                         styleMask:(NSWindowStyleMask)style\n                           backing:(NSBackingStoreType)backingStoreType\n                        initWindow:(_GLFWwindow *)initWindow\n{\n    self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:NO];\n    if (self != nil) {\n        glfw_window = initWindow;\n        self.tabbingMode = NSWindowTabbingModeDisallowed;\n        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];\n        [center addObserver:self selector:@selector(screenParametersDidChange:) name:NSApplicationDidChangeScreenParametersNotification object:nil];\n    }\n    return self;\n}\n\n- (void)screenParametersDidChange:(NSNotification *)notification {\n    if (!glfw_window || !glfw_window->ns.layer_shell.is_active) return;\n    _glfwPlatformSetLayerShellConfig(glfw_window, NULL);\n}\n\n- (void) removeGLFWWindow\n{\n    glfw_window = NULL;\n    [[NSNotificationCenter defaultCenter] removeObserver:self];\n}\n\n- (BOOL)validateMenuItem:(NSMenuItem *)item {\n    if (item.action == @selector(performMiniaturize:)) return YES;\n    return [super validateMenuItem:item];\n}\n\n- (void)performMiniaturize:(id)sender\n{\n    if (glfw_window && (!glfw_window->decorated || glfw_window->ns.titlebar_hidden)) [self miniaturize:self];\n    else [super performMiniaturize:sender];\n}\n\n- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(nullable NSScreen *)screen\n{\n    if (glfw_window && glfw_window->ns.suppress_frame_constraints) return frameRect;\n    return [super constrainFrameRect:frameRect toScreen:screen];\n}\n\n- (BOOL)canBecomeKeyWindow\n{\n    if (!glfw_window) return NO;\n    if (glfw_window->ns.layer_shell.is_active) {\n        if (glfw_window->ns.layer_shell.config.type == GLFW_LAYER_SHELL_BACKGROUND) return NO;\n        switch(glfw_window->ns.layer_shell.config.focus_policy) {\n            case GLFW_FOCUS_NOT_ALLOWED: return NO;\n            case GLFW_FOCUS_EXCLUSIVE: return YES;\n            case GLFW_FOCUS_ON_DEMAND: return YES;\n        }\n    }\n    // Required for NSWindowStyleMaskBorderless windows\n    // Also miniaturized windows should not become key\n    return !_glfwPlatformWindowIconified(glfw_window);\n}\n\n- (BOOL)canBecomeMainWindow\n{\n    return !glfw_window->ns.layer_shell.is_active || glfw_window->ns.layer_shell.config.type != GLFW_LAYER_SHELL_BACKGROUND;\n}\n\nstatic void apply_titlebar_color_settings(_GLFWwindow *window);\n\nstatic void\nupdate_titlebar_button_visibility_after_fullscreen_transition(_GLFWwindow* w, bool traditional, bool made_fullscreen) {\n    // Update window button visibility\n    if (w->ns.titlebar_hidden) {\n        NSWindow *window = w->ns.object;\n        // The hidden buttons might be automatically reset to be visible after going full screen\n        // to show up in the auto-hide title bar, so they need to be set back to hidden.\n        BOOL button_hidden = YES;\n        // When title bar is configured to be hidden, it should be shown with buttons (auto-hide) after going to full screen.\n        if (!traditional) {\n            button_hidden = (BOOL) !made_fullscreen;\n        }\n        [[window standardWindowButton: NSWindowCloseButton] setHidden:button_hidden];\n        [[window standardWindowButton: NSWindowMiniaturizeButton] setHidden:button_hidden];\n        [[window standardWindowButton: NSWindowZoomButton] setHidden:button_hidden];\n    }\n    if (!made_fullscreen) apply_titlebar_color_settings(w);\n}\n\n- (void)toggleFullScreen:(nullable id)sender\n{\n    if (glfw_window) {\n        if (glfw_window->ns.in_fullscreen_transition) return;\n        // Capture the windowed frame before any fullscreen transition begins.\n        // This is more reliable than saving it inside _glfwPlatformToggleFullscreen\n        // because setStyleMask: calls between cycles can reposition the window (#9572).\n        if (!glfw_window->ns.in_traditional_fullscreen && !([self styleMask] & NSWindowStyleMaskFullScreen)) {\n            glfw_window->ns.pre_traditional_fullscreen_frame = [self frame];\n        }\n        if (glfw_window->ns.toggleFullscreenCallback && glfw_window->ns.toggleFullscreenCallback((GLFWwindow*)glfw_window) == 1) return;\n        glfw_window->ns.in_fullscreen_transition = true;\n    }\n    NSWindowStyleMask sm = [self styleMask];\n    bool is_fullscreen_already = (sm & NSWindowStyleMaskFullScreen) != 0;\n    // When resizeIncrements is set, Cocoa cannot restore the original window size after returning from fullscreen.\n    const NSSize original = [self resizeIncrements];\n    [self setResizeIncrements:NSMakeSize(1.0, 1.0)];\n    [super toggleFullScreen:sender];\n    [self setResizeIncrements:original];\n    // When the window decoration is hidden, toggling fullscreen causes the style mask to be changed,\n    // and causes the first responder to be cleared.\n    if (glfw_window && !glfw_window->decorated && glfw_window->ns.view) [self makeFirstResponder:glfw_window->ns.view];\n    update_titlebar_button_visibility_after_fullscreen_transition(glfw_window, false, !is_fullscreen_already);\n}\n\n- (void)zoom:(id)sender\n{\n    if (![self isZoomed]) {\n        const NSSize original = [self resizeIncrements];\n        [self setResizeIncrements:NSMakeSize(1.0, 1.0)];\n        [super zoom:sender];\n        [self setResizeIncrements:original];\n    } else {\n        [super zoom:sender];\n    }\n}\n\n@end\n// }}}\n\n\n// Create the Cocoa window\n//\nstatic bool createNativeWindow(_GLFWwindow* window,\n                                   const _GLFWwndconfig* wndconfig,\n                                   const _GLFWfbconfig* fbconfig)\n{\n    window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window];\n    if (window->ns.delegate == nil)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to create window delegate\");\n        return false;\n    }\n\n    NSRect contentRect;\n\n    if (window->monitor)\n    {\n        GLFWvidmode mode;\n        int xpos, ypos;\n\n        _glfwPlatformGetVideoMode(window->monitor, &mode);\n        _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);\n\n        contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height);\n    }\n    else\n        contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height);\n\n    window->ns.object = [[GLFWWindow alloc]\n        initWithGlfwWindow:contentRect\n                  styleMask:getStyleMask(window)\n                    backing:NSBackingStoreBuffered\n                 initWindow:window\n    ];\n\n    if (window->ns.object == nil)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Cocoa: Failed to create window\");\n        return false;\n    }\n\n    if (window->monitor)\n        [window->ns.object setLevel:NSMainMenuWindowLevel + 1];\n    else\n    {\n        [(NSWindow*) window->ns.object center];\n        CGRect screen_frame = [[(NSWindow*) window->ns.object screen] frame];\n        if (CGRectContainsPoint(screen_frame, _glfw.ns.cascadePoint)\n            || CGPointEqualToPoint(CGPointZero, _glfw.ns.cascadePoint)) {\n            _glfw.ns.cascadePoint =\n                NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint:\n                                  NSPointFromCGPoint(_glfw.ns.cascadePoint)]);\n        } else {\n            _glfw.ns.cascadePoint = CGPointZero;\n        }\n\n        if (wndconfig->resizable)\n        {\n            const NSWindowCollectionBehavior behavior =\n                NSWindowCollectionBehaviorFullScreenPrimary |\n                NSWindowCollectionBehaviorManaged;\n            [window->ns.object setCollectionBehavior:behavior];\n        }\n\n        if (wndconfig->floating)\n            [window->ns.object setLevel:NSFloatingWindowLevel];\n\n        if (wndconfig->maximized)\n            [window->ns.object zoom:nil];\n    }\n\n    if (strlen(wndconfig->ns.frameName))\n        [window->ns.object setFrameAutosaveName:@(wndconfig->ns.frameName)];\n\n    window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window];\n    window->ns.retina = wndconfig->ns.retina;\n\n    if (fbconfig->transparent)\n    {\n        [window->ns.object setOpaque:NO];\n        [window->ns.object setHasShadow:NO];\n        [window->ns.object setBackgroundColor:[NSColor clearColor]];\n    }\n\n    [window->ns.object setContentView:window->ns.view];\n    [window->ns.object makeFirstResponder:window->ns.view];\n    [window->ns.object setTitle:@(wndconfig->title)];\n    [window->ns.object setDelegate:window->ns.delegate];\n    [window->ns.object setAcceptsMouseMovedEvents:YES];\n    [window->ns.object setRestorable:NO];\n\n    _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height);\n    _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight);\n    if (wndconfig->blur_radius > 0) _glfwPlatformSetWindowBlur(window, wndconfig->blur_radius);\n\n    return true;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nint _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, const GLFWLayerShellConfig *lsc) {\n    window->ns.deadKeyState = 0;\n    if (lsc) {\n        window->ns.layer_shell.is_active = true;\n        window->ns.layer_shell.config = *lsc;\n    } else window->ns.layer_shell.is_active = false;\n    if (!_glfw.ns.finishedLaunching)\n    {\n        [NSApp run];\n        _glfw.ns.finishedLaunching = true;\n    }\n\n    if (!createNativeWindow(window, wndconfig, fbconfig))\n        return false;\n    switch((GlfwCocoaColorSpaces)wndconfig->ns.color_space) {\n        case SRGB_COLORSPACE: [window->ns.object setColorSpace:[NSColorSpace sRGBColorSpace]]; break;\n        case DISPLAY_P3_COLORSPACE: [window->ns.object setColorSpace:[NSColorSpace displayP3ColorSpace]]; break;\n        case DEFAULT_COLORSPACE: break;\n    }\n\n    if (ctxconfig->client != GLFW_NO_API)\n    {\n        if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)\n        {\n            if (!_glfwInitNSGL())\n                return false;\n            if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig))\n                return false;\n        }\n        else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)\n        {\n            // EGL implementation on macOS use CALayer* EGLNativeWindowType so we\n            // need to get the layer for EGL window surface creation.\n            [window->ns.view setWantsLayer:YES];\n            window->ns.layer = [window->ns.view layer];\n\n            if (!_glfwInitEGL())\n                return false;\n            if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))\n                return false;\n        }\n        else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)\n        {\n            if (!_glfwInitOSMesa())\n                return false;\n            if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))\n                return false;\n        }\n    }\n\n    if (window->monitor)\n    {\n        // Do not show the window here until after setting the window size, maximized state, and full screen\n        // _glfwPlatformShowWindow(window);\n        // _glfwPlatformFocusWindow(window);\n        acquireMonitor(window);\n    }\n\n    return true;\n}\n\nvoid _glfwPlatformDestroyWindow(_GLFWwindow* window)\n{\n    GLFWWindow *w = window->ns.object;\n    if (_glfw.ns.disabledCursorWindow == window)\n        _glfw.ns.disabledCursorWindow = NULL;\n    free_drop_data(window);\n    if (window->ns.notch_cover_window) {\n        [w removeChildWindow:window->ns.notch_cover_window];\n        [window->ns.notch_cover_window close];\n        [window->ns.notch_cover_window release];\n        window->ns.notch_cover_window = nil;\n    }\n\n    [w orderOut:nil];\n\n    if (window->monitor)\n        releaseMonitor(window);\n\n    if (window->context.destroy)\n        window->context.destroy(window);\n\n    [w setDelegate:nil];\n    [window->ns.delegate cleanup];\n    [window->ns.delegate release];\n    window->ns.delegate = nil;\n\n    [window->ns.view removeGLFWWindow];\n    [window->ns.view release];\n    window->ns.view = nil;\n\n    [w removeGLFWWindow];\n    // Workaround for macOS Tahoe where if the frame is not set to zero size\n    // even after NSWindow::close the window remains on screen as an invisible\n    // rectangle that intercepts mouse events and takes up space in mission\n    // control. Sigh.\n    NSRect frame = w.frame; frame.size.width = 0; frame.size.height = 0;\n    [w setFrame:frame display:NO];\n    [w setHasShadow:NO];\n    [w close];  // sends a release to NSWindow so we dont release it\n    window->ns.object = nil;\n}\n\nstatic NSScreen*\nscreen_for_window_center(_GLFWwindow *window) {\n    NSRect windowFrame = [window->ns.object frame];\n    NSPoint windowCenter = NSMakePoint(NSMidX(windowFrame), NSMidY(windowFrame));\n    for (NSScreen *screen in [NSScreen screens]) {\n        if (NSPointInRect(windowCenter, [screen frame])) {\n            return screen;\n        }\n    }\n    return NSScreen.mainScreen;\n}\n\nstatic NSScreen*\nactive_screen(void) {\n    NSPoint mouseLocation = [NSEvent mouseLocation];\n    NSArray<NSScreen *> *screens = [NSScreen screens];\n    for (NSScreen *screen in screens) {\n        if (NSPointInRect(mouseLocation, [screen frame])) {\n            return screen;\n        }\n    }\n    // As a fallback, return the main screen\n    return [NSScreen mainScreen];\n}\n\nstatic bool\nis_same_screen(NSScreen *screenA, NSScreen * screenB) {\n    if (screenA == screenB) return true;\n    NSDictionary<NSDeviceDescriptionKey, id> *deviceDescriptionA = [screenA deviceDescription];\n    NSDictionary<NSDeviceDescriptionKey, id> *deviceDescriptionB = [screenB deviceDescription];\n    NSNumber *screenNumberA = deviceDescriptionA[@\"NSScreenNumber\"];\n    NSNumber *screenNumberB = deviceDescriptionB[@\"NSScreenNumber\"];\n    return [screenNumberA isEqualToNumber:screenNumberB];\n}\n\nstatic void\nmove_window_to_screen(_GLFWwindow *window, NSScreen *target) {\n    NSRect screenFrame = [target visibleFrame];\n    NSRect windowFrame = [window->ns.object frame];\n    CGFloat newX = NSMidX(screenFrame) - (windowFrame.size.width / 2.0);\n    CGFloat newY = NSMidY(screenFrame) - (windowFrame.size.height / 2.0);\n    NSRect newWindowFrame = NSMakeRect(newX, newY, windowFrame.size.width, windowFrame.size.height);\n    [window->ns.object setFrame:newWindowFrame display:NO animate:NO];\n    if (window->ns.layer_shell.is_active) _glfwPlatformSetLayerShellConfig(window, NULL);\n}\n\nconst GLFWLayerShellConfig*\n_glfwPlatformGetLayerShellConfig(_GLFWwindow *window) {\n    return &window->ns.layer_shell.config;\n}\n\nstatic NSScreen*\nscreen_for_name(const char *name) {\n    int count = 0;\n    GLFWmonitor **monitors = glfwGetMonitors(&count);\n    for (int i = 0; i < count; i++) {\n        const char *q = glfwGetMonitorName(monitors[i]);\n        if (q && strcmp(q, name) == 0) return ((_GLFWmonitor*)monitors[i])->ns.screen;\n    }\n    return NULL;\n}\n\nbool\n_glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig *value) {\n#define config window->ns.layer_shell.config\n#define nswindow window->ns.object\n    window->resizable = false;\n    if (value) config = *value;\n    const bool is_transparent = _glfwPlatformFramebufferTransparent(window);\n    int background_blur = config.related.background_blur;\n    if (!is_transparent || config.related.background_opacity >= 1.f) { background_blur = 0; }\n    [nswindow setBackgroundColor:[NSColor clearColor]];\n    _glfwPlatformSetWindowBlur(window, background_blur);\n    window->ns.titlebar_hidden = true;\n    window->decorated = false;\n    [nswindow setTitlebarAppearsTransparent:false];\n    [nswindow setHasShadow:false];\n    [nswindow setTitleVisibility:NSWindowTitleHidden];\n    NSColorSpace *cs = nil;\n    switch (config.related.color_space) {\n        case SRGB_COLORSPACE: cs = [NSColorSpace sRGBColorSpace]; break;\n        case DISPLAY_P3_COLORSPACE: cs = [NSColorSpace displayP3ColorSpace]; break;\n        case DEFAULT_COLORSPACE: cs = nil; break;  // using deviceRGBColorSpace causes a hang when transitioning to fullscreen\n    }\n    [nswindow setColorSpace:cs];\n    [[nswindow standardWindowButton: NSWindowCloseButton] setHidden:true];\n    [[nswindow standardWindowButton: NSWindowMiniaturizeButton] setHidden:true];\n    [[nswindow standardWindowButton: NSWindowZoomButton] setHidden:true];\n    [nswindow setStyleMask:NSWindowStyleMaskBorderless];\n    // HACK: Changing the style mask can cause the first responder to be cleared\n    [nswindow makeFirstResponder:window->ns.view];\n    NSScreen *screen = screen_for_window_center(window);\n    if (config.output_name[0]) {\n        NSScreen *q = screen_for_name(config.output_name);\n        if (q) screen = q;\n    }\n    unsigned cell_width, cell_height; double left_edge_spacing, top_edge_spacing, right_edge_spacing, bottom_edge_spacing;\n    float xscale = (float)config.expected.xscale, yscale = (float)config.expected.yscale;\n    _glfwPlatformGetWindowContentScale(window, &xscale, &yscale);\n    config.size_callback((GLFWwindow*)window, xscale, yscale, &cell_width, &cell_height, &left_edge_spacing, &top_edge_spacing, &right_edge_spacing, &bottom_edge_spacing);\n    double spacing_x = left_edge_spacing + right_edge_spacing;\n    double spacing_y = top_edge_spacing + bottom_edge_spacing;\n    const unsigned xsz = config.x_size_in_pixels ? (unsigned)(config.x_size_in_pixels * xscale) : (cell_width * config.x_size_in_cells);\n    const unsigned ysz = config.y_size_in_pixels ? (unsigned)(config.y_size_in_pixels * yscale) : (cell_height * config.y_size_in_cells);\n    CGFloat dock_height = NSMinY(screen.visibleFrame) - NSMinY(screen.frame);\n    CGFloat menubar_height = NSHeight(screen.frame) - NSHeight(screen.visibleFrame) - dock_height;\n    CGFloat x = NSMinX(screen.visibleFrame), y = NSMinY(screen.visibleFrame) - 1, width = NSWidth(screen.visibleFrame), height = NSHeight(screen.visibleFrame) + 2;\n    if (config.type == GLFW_LAYER_SHELL_BACKGROUND || config.edge == GLFW_EDGE_CENTER) {\n        x = NSMinX(screen.frame); height = NSHeight(screen.frame) - menubar_height + 1; y = NSMinY(screen.frame); width = NSWidth(screen.frame);\n    }\n    // Screen co-ordinate system is with origin in lower left and y increasing upwards and x increasing rightwards\n    // NSLog(@\"frame: %@ visibleFrame: %@\\n\", NSStringFromRect(screen.frame), NSStringFromRect(screen.visibleFrame));\n    NSWindowLevel level = NSPopUpMenuWindowLevel - 1; // so that popup menus from globalmenubar function\n    NSWindowAnimationBehavior animation_behavior = NSWindowAnimationBehaviorUtilityWindow;\n    switch (config.type) {\n        case GLFW_LAYER_SHELL_BACKGROUND:\n            animation_behavior = NSWindowAnimationBehaviorNone;\n            // See: https://stackoverflow.com/questions/4982584/how-do-i-draw-the-desktop-on-mac-os-x/4982619#4982619\n            level = kCGDesktopWindowLevel;\n            break;\n        case GLFW_LAYER_SHELL_OVERLAY: case GLFW_LAYER_SHELL_NONE: break;\n        case GLFW_LAYER_SHELL_PANEL: level = NSNormalWindowLevel - 1; break;\n        case GLFW_LAYER_SHELL_TOP: level--; break;\n    }\n    if (config.type != GLFW_LAYER_SHELL_BACKGROUND && config.edge != GLFW_EDGE_CENTER) {\n        double panel_height = spacing_y + ysz / yscale, panel_width = spacing_x + xsz / xscale;\n        switch (config.edge) {\n            case GLFW_EDGE_BOTTOM: height = panel_height; break;\n            case GLFW_EDGE_TOP:\n                y += height - panel_height + 1.;\n                height = panel_height;\n                break;\n            case GLFW_EDGE_LEFT: width = panel_width; break;\n            case GLFW_EDGE_RIGHT:\n                x += width - panel_width + 1.;\n                width = panel_width;\n                break;\n            case GLFW_EDGE_CENTER_SIZED:\n                x += (width - panel_width) / 2;\n                y += (height - panel_height) / 2;\n                width = panel_width; height = panel_height;\n                break;\n            default:  // top left\n                y += height - panel_height + 1.;\n                height = panel_height; width = panel_width;\n                break;\n        }\n        if (width < 1.) width = NSWidth(screen.visibleFrame);\n        if (height < 1.) height = NSWidth(screen.visibleFrame);\n    }\n\n    if (config.edge != GLFW_EDGE_CENTER_SIZED) {\n        x += config.requested_left_margin; width -= config.requested_left_margin + config.requested_right_margin;\n        y += config.requested_bottom_margin; height -= config.requested_top_margin + config.requested_bottom_margin;\n    }\n\n    [nswindow setAnimationBehavior:animation_behavior];\n    [nswindow setLevel:level];\n    [nswindow setCollectionBehavior: (NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorIgnoresCycle)];\n    [nswindow setFrame:NSMakeRect(x, y, width, height) display:YES];\n    return true;\n#undef config\n#undef nswindow\n}\n\nvoid _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)\n{\n    if (!title) return;\n    NSString* string = @(title);\n    if (!string) return;  // the runtime failed to convert title to an NSString\n    [window->ns.object setTitle:string];\n    // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it\n    //       if the window lacks NSWindowStyleMaskTitled\n    [window->ns.object setMiniwindowTitle:string];\n}\n\nvoid _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED,\n                                int count UNUSED, const GLFWimage* images UNUSED)\n{\n    _glfwInputError(GLFW_FEATURE_UNAVAILABLE,\n                    \"Cocoa: Regular windows do not have icons on macOS\");\n}\n\nvoid _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)\n{\n    const NSRect contentRect = get_window_size_without_border_in_logical_pixels(window);\n\n    if (xpos)\n        *xpos = (int)contentRect.origin.x;\n    if (ypos)\n        *ypos = (int)_glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1);\n}\n\nvoid _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y)\n{\n    const NSRect contentRect = get_window_size_without_border_in_logical_pixels(window);\n    const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0);\n    const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect];\n    [window->ns.object setFrameOrigin:frameRect.origin];\n}\n\nvoid _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)\n{\n    const NSRect contentRect = get_window_size_without_border_in_logical_pixels(window);\n\n    if (width)\n        *width = (int)contentRect.size.width;\n    if (height)\n        *height = (int)contentRect.size.height;\n}\n\nvoid _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)\n{\n    if (window->ns.layer_shell.is_active) return;\n    if (window->monitor)\n    {\n        if (window->monitor->window == window)\n            acquireMonitor(window);\n    }\n    else\n    {\n        // Disable window resizing in fullscreen.\n        if ([window->ns.object styleMask] & NSWindowStyleMaskFullScreen || window->ns.in_traditional_fullscreen) return;\n        NSRect contentRect = get_window_size_without_border_in_logical_pixels(window);\n        contentRect.origin.y += contentRect.size.height - height;\n        contentRect.size = NSMakeSize(width, height);\n        [window->ns.object setFrame:[window->ns.object frameRectForContentRect:contentRect]\n                            display:YES];\n    }\n}\n\nvoid _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,\n                                      int minwidth, int minheight,\n                                      int maxwidth, int maxheight)\n{\n    if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)\n        [window->ns.object setContentMinSize:NSMakeSize(0, 0)];\n    else\n        [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)];\n\n    if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)\n        [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)];\n    else\n        [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)];\n}\n\nvoid _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)\n{\n    if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)\n        [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)];\n    else\n        [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)];\n}\n\nvoid _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr, int heightincr)\n{\n    if (widthincr != GLFW_DONT_CARE && heightincr != GLFW_DONT_CARE) {\n        float xscale = 1, yscale = 1;\n        _glfwPlatformGetWindowContentScale(window, &xscale, &yscale);\n        [window->ns.object setResizeIncrements:NSMakeSize(widthincr / xscale, heightincr / yscale)];\n    } else {\n        [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)];\n    }\n}\n\nvoid _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)\n{\n    const NSRect contentRect = get_window_size_without_border_in_logical_pixels(window);\n    const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];\n\n    if (width)\n        *width = (int) fbRect.size.width;\n    if (height)\n        *height = (int) fbRect.size.height;\n}\n\nvoid _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,\n                                     int* left, int* top,\n                                     int* right, int* bottom)\n{\n    const NSRect contentRect = get_window_size_without_border_in_logical_pixels(window);\n    const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect];\n\n    if (left)\n        *left = (int)(contentRect.origin.x - frameRect.origin.x);\n    if (top)\n        *top = (int)(frameRect.origin.y + frameRect.size.height -\n               contentRect.origin.y - contentRect.size.height);\n    if (right)\n        *right = (int)(frameRect.origin.x + frameRect.size.width -\n                 contentRect.origin.x - contentRect.size.width);\n    if (bottom)\n        *bottom = (int)(contentRect.origin.y - frameRect.origin.y);\n}\n\nvoid _glfwPlatformGetWindowContentScale(_GLFWwindow* window,\n                                        float* xscale, float* yscale)\n{\n    const NSRect points = get_window_size_without_border_in_logical_pixels(window);\n    const NSRect pixels = [window->ns.view convertRectToBacking:points];\n\n    if (xscale)\n        *xscale = (float) (pixels.size.width / points.size.width);\n    if (yscale)\n        *yscale = (float) (pixels.size.height / points.size.height);\n}\n\nmonotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)\n{\n    return s_double_to_monotonic_t([NSEvent doubleClickInterval]);\n}\n\nvoid _glfwPlatformIconifyWindow(_GLFWwindow* window)\n{\n    [window->ns.object miniaturize:nil];\n}\n\nvoid _glfwPlatformRestoreWindow(_GLFWwindow* window)\n{\n    if ([window->ns.object isMiniaturized])\n        [window->ns.object deminiaturize:nil];\n    else if ([window->ns.object isZoomed])\n        [window->ns.object zoom:nil];\n}\n\nvoid _glfwPlatformMaximizeWindow(_GLFWwindow* window)\n{\n    if (![window->ns.object isZoomed]) {\n        [window->ns.object zoom:nil];\n    }\n}\n\nvoid _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen)\n{\n    const bool is_background = window->ns.layer_shell.is_active && window->ns.layer_shell.config.type == GLFW_LAYER_SHELL_BACKGROUND;\n    NSWindow *nw = window->ns.object;\n    if (move_to_active_screen) {\n        NSScreen *current_screen = screen_for_window_center(window);\n        NSScreen *target_screen = active_screen();\n        if (!is_same_screen(current_screen, target_screen)) {\n            debug_rendering(\"Moving OS window %llu to active screen\\n\", window->id);\n            move_window_to_screen(window, target_screen);\n        }\n    }\n    if (is_background) {\n        [nw orderBack:nil];\n    } else {\n        // Cocoa has a bug where when showing a hidden window after\n        // fullscreening an application, the window does not get added\n        // to the current space even though it has NSWindowCollectionBehaviorCanJoinAllSpaces\n        // probably because it wasnt added to the temp space used for\n        // fullscreen. So to work around that, we change the collection\n        // behavior temporarily to NSWindowCollectionBehaviorMoveToActiveSpace\n        // and then change it back asynchronously.\n        // See https://github.com/kovidgoyal/kitty/issues/8740\n        NSWindowCollectionBehavior old = nw.collectionBehavior;\n        nw.collectionBehavior = (old & !NSWindowCollectionBehaviorCanJoinAllSpaces) | NSWindowCollectionBehaviorMoveToActiveSpace;\n        [nw orderFront:nil];\n        __block __typeof__(nw) weakSelf = nw;\n        dispatch_async(dispatch_get_main_queue(), ^{\n\t\t\tweakSelf.collectionBehavior = old;\n\t\t});\n    }\n}\n\nvoid _glfwPlatformHideWindow(_GLFWwindow* window)\n{\n    [window->ns.object orderOut:nil];\n    pid_t prev_app_pid = _glfw.ns.previous_front_most_application; _glfw.ns.previous_front_most_application = 0;\n    NSRunningApplication *app;\n    if (window->ns.layer_shell.is_active && prev_app_pid > 0 && (app = [NSRunningApplication runningApplicationWithProcessIdentifier:prev_app_pid])) {\n        unsigned num_visible = 0;\n        for (_GLFWwindow *w = _glfw.windowListHead;  w;  w = w->next) {\n            if (_glfwPlatformWindowVisible(w)) num_visible++;\n        }\n        if (!num_visible) {\n            // yieldActivationToApplication was introduced in macOS 14 (Sonoma)\n            SEL selector = NSSelectorFromString(@\"yieldActivationToApplication:\");\n            if ([NSApp respondsToSelector:selector]) {\n                [NSApp performSelector:selector withObject:app];\n                [app activateWithOptions:0];\n            } else {\n                #define NSApplicationActivateIgnoringOtherApps 2\n                [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];\n                #undef NSApplicationActivateIgnoringOtherApps\n            }\n        }\n    }\n}\n\nvoid _glfwPlatformRequestWindowAttention(_GLFWwindow* window UNUSED)\n{\n    [NSApp requestUserAttention:NSInformationalRequest];\n}\n\nint _glfwPlatformWindowBell(_GLFWwindow* window UNUSED)\n{\n    NSBeep();\n    return true;\n}\n\nvoid _glfwPlatformFocusWindow(_GLFWwindow* window)\n{\n    if (_glfwPlatformWindowIconified(window)) {\n        // miniaturized windows return false in canBecomeKeyWindow therefore\n        // unminiaturize first\n        [window->ns.object deminiaturize:nil];\n    }\n    if ([window->ns.object canBecomeKeyWindow]) {\n        // Make us the active application\n        [NSApp activateIgnoringOtherApps:YES];\n        [window->ns.object makeKeyAndOrderFront:nil];\n    }\n}\n\nvoid _glfwPlatformSetWindowMonitor(_GLFWwindow* window,\n                                   _GLFWmonitor* monitor,\n                                   int xpos, int ypos,\n                                   int width, int height,\n                                   int refreshRate UNUSED)\n{\n    if (window->monitor == monitor)\n    {\n        if (monitor)\n        {\n            if (monitor->window == window)\n                acquireMonitor(window);\n        }\n        else\n        {\n            const NSRect contentRect =\n                NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height);\n            const NSRect frameRect =\n                [window->ns.object frameRectForContentRect:contentRect\n                                                 styleMask:getStyleMask(window)];\n\n            [window->ns.object setFrame:frameRect display:YES];\n        }\n\n        return;\n    }\n\n    if (window->monitor)\n        releaseMonitor(window);\n\n    _glfwInputWindowMonitor(window, monitor);\n\n    const NSUInteger styleMask = getStyleMask(window);\n    [window->ns.object setStyleMask:styleMask];\n    // HACK: Changing the style mask can cause the first responder to be cleared\n    [window->ns.object makeFirstResponder:window->ns.view];\n\n    if (window->monitor)\n    {\n        [window->ns.object setLevel:NSMainMenuWindowLevel + 1];\n        [window->ns.object setHasShadow:NO];\n\n        acquireMonitor(window);\n    }\n    else\n    {\n        NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1),\n                                        width, height);\n        NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect\n                                                            styleMask:styleMask];\n        [window->ns.object setFrame:frameRect display:YES];\n\n        if (window->numer != GLFW_DONT_CARE &&\n            window->denom != GLFW_DONT_CARE)\n        {\n            [window->ns.object setContentAspectRatio:NSMakeSize(window->numer,\n                                                                window->denom)];\n        }\n\n        if (window->minwidth != GLFW_DONT_CARE &&\n            window->minheight != GLFW_DONT_CARE)\n        {\n            [window->ns.object setContentMinSize:NSMakeSize(window->minwidth,\n                                                            window->minheight)];\n        }\n\n        if (window->maxwidth != GLFW_DONT_CARE &&\n            window->maxheight != GLFW_DONT_CARE)\n        {\n            [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth,\n                                                            window->maxheight)];\n        }\n\n        if (window->floating)\n            [window->ns.object setLevel:NSFloatingWindowLevel];\n        else\n            [window->ns.object setLevel:NSNormalWindowLevel];\n\n        [window->ns.object setHasShadow:YES];\n        // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window\n        //       title property but the miniwindow title property is unaffected\n        [window->ns.object setTitle:[window->ns.object miniwindowTitle]];\n    }\n}\n\nint _glfwPlatformWindowFocused(_GLFWwindow* window)\n{\n    return [window->ns.object isKeyWindow];\n}\n\nint _glfwPlatformWindowOccluded(_GLFWwindow* window)\n{\n    return !([window->ns.object occlusionState] & NSWindowOcclusionStateVisible);\n}\n\nint _glfwPlatformWindowIconified(_GLFWwindow* window)\n{\n    return [window->ns.object isMiniaturized];\n}\n\nint _glfwPlatformWindowVisible(_GLFWwindow* window)\n{\n    return [window->ns.object isVisible];\n}\n\nint _glfwPlatformWindowMaximized(_GLFWwindow* window)\n{\n    return [window->ns.object isZoomed];\n}\n\nint _glfwPlatformWindowHovered(_GLFWwindow* window)\n{\n    const NSPoint point = [NSEvent mouseLocation];\n\n    if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] !=\n        [window->ns.object windowNumber])\n    {\n        return false;\n    }\n\n    return NSMouseInRect(point,\n        [window->ns.object convertRectToScreen:[window->ns.view frame]], NO);\n}\n\nint _glfwPlatformFramebufferTransparent(_GLFWwindow* window)\n{\n    return ![window->ns.object isOpaque] && ![window->ns.view isOpaque];\n}\n\nvoid _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled UNUSED)\n{\n    [window->ns.object setStyleMask:getStyleMask(window)];\n    [window->ns.object makeFirstResponder:window->ns.view];\n}\n\nvoid _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled UNUSED)\n{\n    [window->ns.object setStyleMask:getStyleMask(window)];\n    [window->ns.object makeFirstResponder:window->ns.view];\n}\n\nvoid _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)\n{\n    if (enabled)\n        [window->ns.object setLevel:NSFloatingWindowLevel];\n    else\n        [window->ns.object setLevel:NSNormalWindowLevel];\n}\n\nvoid _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)\n{\n    [window->ns.object setIgnoresMouseEvents:enabled];\n}\n\nfloat _glfwPlatformGetWindowOpacity(_GLFWwindow* window)\n{\n    return (float) [window->ns.object alphaValue];\n}\n\nvoid _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)\n{\n    [window->ns.object setAlphaValue:opacity];\n}\n\nvoid _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED)\n{\n    _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,\n                    \"Cocoa: Raw mouse motion not yet implemented\");\n}\n\nbool _glfwPlatformRawMouseMotionSupported(void)\n{\n    return false;\n}\n\nvoid _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)\n{\n    const NSRect contentRect = [window->ns.view frame];\n    // NOTE: The returned location uses base 0,1 not 0,0\n    const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];\n\n    if (xpos)\n        *xpos = pos.x;\n    if (ypos)\n        *ypos = contentRect.size.height - pos.y;\n}\n\nvoid _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)\n{\n    updateCursorImage(window);\n\n    const NSRect contentRect = [window->ns.view frame];\n    // NOTE: The returned location uses base 0,1 not 0,0\n    const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];\n\n    window->ns.cursorWarpDeltaX += x - pos.x;\n    window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y;\n\n    if (window->monitor)\n    {\n        CGDisplayMoveCursorToPoint(window->monitor->ns.displayID,\n                                   CGPointMake(x, y));\n    }\n    else\n    {\n        const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0);\n        const NSRect globalRect = [window->ns.object convertRectToScreen:localRect];\n        const NSPoint globalPoint = globalRect.origin;\n\n        CGWarpMouseCursorPosition(CGPointMake(globalPoint.x,\n                                              _glfwTransformYNS(globalPoint.y)));\n    }\n}\n\nvoid _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode UNUSED)\n{\n    if (_glfwPlatformWindowFocused(window))\n        updateCursorMode(window);\n}\n\nconst char* _glfwPlatformGetNativeKeyName(int keycode)\n{\n    UInt32 deadKeyState = 0;\n    UniChar characters[8];\n    UniCharCount characterCount = 0;\n\n    if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes],\n                       keycode,\n                       kUCKeyActionDisplay,\n                       0,\n                       LMGetKbdType(),\n                       kUCKeyTranslateNoDeadKeysBit,\n                       &deadKeyState,\n                       sizeof(characters) / sizeof(characters[0]),\n                       &characterCount,\n                       characters) != noErr)\n    {\n        return NULL;\n    }\n\n    if (!characterCount)\n        return NULL;\n\n    convert_utf16_to_utf8(characters, characterCount, _glfw.ns.keyName, sizeof(_glfw.ns.keyName));\n    return _glfw.ns.keyName;\n}\n\nint _glfwPlatformGetNativeKeyForKey(uint32_t glfw_key)\n{\n    if (GLFW_FKEY_FIRST <= glfw_key && glfw_key <= GLFW_FKEY_LAST) { // {{{\n        switch(glfw_key) {\n            /* start functional to macu (auto generated by gen-key-constants.py do not edit) */\n        case GLFW_FKEY_ENTER: return NSCarriageReturnCharacter;\n        case GLFW_FKEY_TAB: return NSTabCharacter;\n        case GLFW_FKEY_BACKSPACE: return NSBackspaceCharacter;\n        case GLFW_FKEY_INSERT: return NSInsertFunctionKey;\n        case GLFW_FKEY_DELETE: return NSDeleteFunctionKey;\n        case GLFW_FKEY_LEFT: return NSLeftArrowFunctionKey;\n        case GLFW_FKEY_RIGHT: return NSRightArrowFunctionKey;\n        case GLFW_FKEY_UP: return NSUpArrowFunctionKey;\n        case GLFW_FKEY_DOWN: return NSDownArrowFunctionKey;\n        case GLFW_FKEY_PAGE_UP: return NSPageUpFunctionKey;\n        case GLFW_FKEY_PAGE_DOWN: return NSPageDownFunctionKey;\n        case GLFW_FKEY_HOME: return NSHomeFunctionKey;\n        case GLFW_FKEY_END: return NSEndFunctionKey;\n        case GLFW_FKEY_SCROLL_LOCK: return NSScrollLockFunctionKey;\n        case GLFW_FKEY_NUM_LOCK: return NSClearLineFunctionKey;\n        case GLFW_FKEY_PRINT_SCREEN: return NSPrintScreenFunctionKey;\n        case GLFW_FKEY_PAUSE: return NSPauseFunctionKey;\n        case GLFW_FKEY_MENU: return NSMenuFunctionKey;\n        case GLFW_FKEY_F1: return NSF1FunctionKey;\n        case GLFW_FKEY_F2: return NSF2FunctionKey;\n        case GLFW_FKEY_F3: return NSF3FunctionKey;\n        case GLFW_FKEY_F4: return NSF4FunctionKey;\n        case GLFW_FKEY_F5: return NSF5FunctionKey;\n        case GLFW_FKEY_F6: return NSF6FunctionKey;\n        case GLFW_FKEY_F7: return NSF7FunctionKey;\n        case GLFW_FKEY_F8: return NSF8FunctionKey;\n        case GLFW_FKEY_F9: return NSF9FunctionKey;\n        case GLFW_FKEY_F10: return NSF10FunctionKey;\n        case GLFW_FKEY_F11: return NSF11FunctionKey;\n        case GLFW_FKEY_F12: return NSF12FunctionKey;\n        case GLFW_FKEY_F13: return NSF13FunctionKey;\n        case GLFW_FKEY_F14: return NSF14FunctionKey;\n        case GLFW_FKEY_F15: return NSF15FunctionKey;\n        case GLFW_FKEY_F16: return NSF16FunctionKey;\n        case GLFW_FKEY_F17: return NSF17FunctionKey;\n        case GLFW_FKEY_F18: return NSF18FunctionKey;\n        case GLFW_FKEY_F19: return NSF19FunctionKey;\n        case GLFW_FKEY_F20: return NSF20FunctionKey;\n        case GLFW_FKEY_F21: return NSF21FunctionKey;\n        case GLFW_FKEY_F22: return NSF22FunctionKey;\n        case GLFW_FKEY_F23: return NSF23FunctionKey;\n        case GLFW_FKEY_F24: return NSF24FunctionKey;\n        case GLFW_FKEY_F25: return NSF25FunctionKey;\n        case GLFW_FKEY_F26: return NSF26FunctionKey;\n        case GLFW_FKEY_F27: return NSF27FunctionKey;\n        case GLFW_FKEY_F28: return NSF28FunctionKey;\n        case GLFW_FKEY_F29: return NSF29FunctionKey;\n        case GLFW_FKEY_F30: return NSF30FunctionKey;\n        case GLFW_FKEY_F31: return NSF31FunctionKey;\n        case GLFW_FKEY_F32: return NSF32FunctionKey;\n        case GLFW_FKEY_F33: return NSF33FunctionKey;\n        case GLFW_FKEY_F34: return NSF34FunctionKey;\n        case GLFW_FKEY_F35: return NSF35FunctionKey;\n        case GLFW_FKEY_KP_ENTER: return NSEnterCharacter;\n/* end functional to macu */\n            default:\n                return 0;\n        }\n    } // }}}\n    if (!is_pua_char(glfw_key)) return glfw_key;\n    return 0;\n}\n\nint _glfwPlatformCreateCursor(_GLFWcursor* cursor,\n                              const GLFWimage* image,\n                              int xhot, int yhot, int count)\n{\n    NSImage* native;\n    NSBitmapImageRep* rep;\n\n    native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)];\n    if (native == nil)\n        return false;\n\n    for (int i = 0; i < count; i++) {\n        const GLFWimage *src = image + i;\n        rep = [[NSBitmapImageRep alloc]\n            initWithBitmapDataPlanes:NULL\n                        pixelsWide:src->width\n                        pixelsHigh:src->height\n                    bitsPerSample:8\n                    samplesPerPixel:4\n                            hasAlpha:YES\n                            isPlanar:NO\n                    colorSpaceName:NSCalibratedRGBColorSpace\n                        bitmapFormat:NSBitmapFormatAlphaNonpremultiplied\n                        bytesPerRow:src->width * 4\n                        bitsPerPixel:32];\n        if (rep == nil) {\n            [native release];\n            return false;\n        }\n\n        memcpy([rep bitmapData], src->pixels, src->width * src->height * 4);\n        [native addRepresentation:rep];\n        [rep release];\n    }\n\n    cursor->ns.object = [[NSCursor alloc] initWithImage:native\n                                                hotSpot:NSMakePoint(xhot, yhot)];\n\n    [native release];\n    if (cursor->ns.object == nil)\n        return false;\n    return true;\n}\n\nstatic NSCursor*\nload_hidden_system_cursor(NSString *name, SEL fallback) {\n    // this implementation comes from SDL_cocoamouse.m\n    NSString *cursorPath = [@\"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors\" stringByAppendingPathComponent:name];\n    NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@\"info.plist\"]];\n    /* we can't do animation atm.  :/ */\n    const int frames = (int)[[info valueForKey:@\"frames\"] integerValue];\n    NSCursor *cursor;\n    NSImage *image = [[NSImage alloc] initWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@\"cursor.pdf\"]];\n    if ((image == nil) || (image.isValid == NO)) {\n        return [NSCursor performSelector:fallback];\n    }\n\n    if (frames > 1) {\n        const NSCompositingOperation operation = NSCompositingOperationCopy;\n        const NSSize cropped_size = NSMakeSize(image.size.width, (int)(image.size.height / frames));\n        NSImage *cropped = [[NSImage alloc] initWithSize:cropped_size];\n        if (cropped == nil) {\n            return [NSCursor performSelector:fallback];\n        }\n\n        [cropped lockFocus];\n        {\n            const NSRect cropped_rect = NSMakeRect(0, 0, cropped_size.width, cropped_size.height);\n            [image drawInRect:cropped_rect fromRect:cropped_rect operation:operation fraction:1];\n        }\n        [cropped unlockFocus];\n        image = cropped;\n    }\n\n    cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint([[info valueForKey:@\"hotx\"] doubleValue], [[info valueForKey:@\"hoty\"] doubleValue])];\n    return cursor;\n}\n\nint _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape) {\n#define C(name, val) case name: cursor->ns.object = [NSCursor val]; break;\n#define U(name, val) case name: cursor->ns.object = [NSCursor performSelector:@selector(val)]; break;\n#define S(name, val, fallback) case name: cursor->ns.object = load_hidden_system_cursor(@#val, @selector(val)); break;\n    switch(shape) {\n        /* start glfw to cocoa (auto generated by gen-key-constants.py do not edit) */\n        C(GLFW_DEFAULT_CURSOR, arrowCursor);\n        C(GLFW_TEXT_CURSOR, IBeamCursor);\n        C(GLFW_POINTER_CURSOR, pointingHandCursor);\n        S(GLFW_HELP_CURSOR, help, arrowCursor);\n        S(GLFW_WAIT_CURSOR, busybutclickable, arrowCursor);\n        S(GLFW_PROGRESS_CURSOR, busybutclickable, arrowCursor);\n        C(GLFW_CROSSHAIR_CURSOR, crosshairCursor);\n        S(GLFW_CELL_CURSOR, cell, crosshairCursor);\n        C(GLFW_VERTICAL_TEXT_CURSOR, IBeamCursorForVerticalLayout);\n        S(GLFW_MOVE_CURSOR, move, openHandCursor);\n        C(GLFW_E_RESIZE_CURSOR, resizeRightCursor);\n        S(GLFW_NE_RESIZE_CURSOR, resizenortheast, _windowResizeNorthEastSouthWestCursor);\n        S(GLFW_NW_RESIZE_CURSOR, resizenorthwest, _windowResizeNorthWestSouthEastCursor);\n        C(GLFW_N_RESIZE_CURSOR, resizeUpCursor);\n        S(GLFW_SE_RESIZE_CURSOR, resizesoutheast, _windowResizeNorthWestSouthEastCursor);\n        S(GLFW_SW_RESIZE_CURSOR, resizesouthwest, _windowResizeNorthEastSouthWestCursor);\n        C(GLFW_S_RESIZE_CURSOR, resizeDownCursor);\n        C(GLFW_W_RESIZE_CURSOR, resizeLeftCursor);\n        C(GLFW_EW_RESIZE_CURSOR, resizeLeftRightCursor);\n        C(GLFW_NS_RESIZE_CURSOR, resizeUpDownCursor);\n        U(GLFW_NESW_RESIZE_CURSOR, _windowResizeNorthEastSouthWestCursor);\n        U(GLFW_NWSE_RESIZE_CURSOR, _windowResizeNorthWestSouthEastCursor);\n        S(GLFW_ZOOM_IN_CURSOR, zoomin, arrowCursor);\n        S(GLFW_ZOOM_OUT_CURSOR, zoomout, arrowCursor);\n        C(GLFW_ALIAS_CURSOR, dragLinkCursor);\n        C(GLFW_COPY_CURSOR, dragCopyCursor);\n        C(GLFW_NOT_ALLOWED_CURSOR, operationNotAllowedCursor);\n        C(GLFW_NO_DROP_CURSOR, operationNotAllowedCursor);\n        C(GLFW_GRAB_CURSOR, openHandCursor);\n        C(GLFW_GRABBING_CURSOR, closedHandCursor);\n/* end glfw to cocoa */\n        case GLFW_INVALID_CURSOR:\n            return false;\n    }\n#undef C\n#undef U\n#undef S\n    if (!cursor->ns.object)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to retrieve standard cursor\");\n        return false;\n    }\n\n    [cursor->ns.object retain];\n    return true;\n}\n\nvoid _glfwPlatformDestroyCursor(_GLFWcursor* cursor)\n{\n    if (cursor->ns.object)\n        [(NSCursor*) cursor->ns.object release];\n}\n\nvoid _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor UNUSED)\n{\n    if (cursorInContentArea(window))\n        updateCursorImage(window);\n}\n\nbool _glfwPlatformIsFullscreen(_GLFWwindow* w, unsigned int flags) {\n    NSWindow *window = w->ns.object;\n    bool traditional = !(flags & 1);\n    if (traditional) { if(@available(macOS 10.15.7, *)) return w->ns.in_traditional_fullscreen; }\n    NSWindowStyleMask sm = [window styleMask];\n    return sm & NSWindowStyleMaskFullScreen;\n}\n\nstatic void\nmake_window_fullscreen_after_show(unsigned long long timer_id, void* data) {\n    (void)timer_id;\n    unsigned long long window_id = (uintptr_t)data;\n    for (_GLFWwindow *w = _glfw.windowListHead;  w;  w = w->next) {\n        if (w->id == window_id) {\n            NSWindow *window = w->ns.object;\n            [window toggleFullScreen: nil];\n            update_titlebar_button_visibility_after_fullscreen_transition(w, false, true);\n            break;\n        }\n    }\n}\n\nstatic void\n_glfwUpdateNotchCover(_GLFWwindow* w) {\n    NSWindow *window = w->ns.object;\n    if (w->ns.notch_cover_window) {\n        [window removeChildWindow:w->ns.notch_cover_window];\n        [w->ns.notch_cover_window close];\n        [w->ns.notch_cover_window release];\n        w->ns.notch_cover_window = nil;\n    }\n    if (!w->ns.in_traditional_fullscreen) return;\n    if (@available(macOS 12.0, *)) {\n        CGFloat insetTop = window.screen.safeAreaInsets.top;\n        if (insetTop <= 0) return;\n        NSRect sf = [window.screen frame];\n        NSWindow *bg_window = [[NSWindow alloc] initWithContentRect:sf styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:NO];\n        [bg_window setBackgroundColor:[NSColor clearColor]];\n        [bg_window setHasShadow:NO];\n        [bg_window setOpaque:NO];\n        [bg_window setIgnoresMouseEvents:YES];\n        [bg_window setReleasedWhenClosed:NO];\n        [bg_window setColorSpace:[window colorSpace]];\n        // Add a colored subview only in the notch strip area\n        NSView *notchView = [[NSView alloc] initWithFrame:NSMakeRect(0, sf.size.height - insetTop, sf.size.width, insetTop)];\n        notchView.wantsLayer = YES;\n        unsigned int c = w->ns.notch_cover_color;\n        float a = w->ns.notch_cover_opacity;\n        notchView.layer.backgroundColor = [NSColor colorWithSRGBRed:((c >> 16) & 0xFF) / 255.0\n                                                              green:((c >> 8) & 0xFF) / 255.0\n                                                               blue:(c & 0xFF) / 255.0\n                                                              alpha:a].CGColor;\n        [bg_window.contentView addSubview:notchView];\n        // must be above otherwise shadow of main window is rendered over bg_window\n        [window addChildWindow:bg_window ordered:NSWindowAbove];\n        w->ns.notch_cover_window = bg_window;\n        [notchView release];\n    }\n}\n\nbool _glfwPlatformToggleFullscreen(_GLFWwindow* w, unsigned int flags) {\n    NSWindow *window = w->ns.object;\n    bool made_fullscreen = true;\n    bool traditional = !(flags & 1);\n    NSWindowStyleMask sm = [window styleMask];\n    if (traditional) {\n        if (@available(macOS 10.15.7, *)) {\n            // As of Big Turd NSWindowStyleMaskFullScreen is no longer usable\n            // Also no longer compatible after a minor release of macOS 10.15.7\n            if (!w->ns.in_traditional_fullscreen) {\n                // Apple throws NSGenericException if setStyleMask: clears\n                // NSWindowStyleMaskFullScreen outside a transition (see #9572).\n                // Split View sets this flag via the system, so fall back to\n                // Cocoa fullscreen toggle instead of the traditional path.\n                if (sm & NSWindowStyleMaskFullScreen) {\n                    [window toggleFullScreen:nil];\n                    return false;\n                }\n                w->ns.pre_full_screen_style_mask = sm;\n                w->ns.pre_traditional_fullscreen_frame = [window frame];\n                [window setStyleMask: NSWindowStyleMaskBorderless];\n                [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock];\n                NSRect screenFrame = [window.screen frame];\n                if (@available(macOS 12.0, *)) {\n                    screenFrame.size.height -= window.screen.safeAreaInsets.top;\n                }\n                [window setFrame:screenFrame display:YES];\n                w->ns.in_traditional_fullscreen = true;\n                _glfwUpdateNotchCover(w);\n            } else {\n                made_fullscreen = false;\n                if (sm & NSWindowStyleMaskFullScreen) {\n                    // Split View added NSWindowStyleMaskFullScreen on top of our\n                    // traditional fullscreen. We can't clear that flag directly\n                    // (NSGenericException), so trigger a Cocoa exit and defer the\n                    // traditional fullscreen cleanup to windowDidExitFullScreen:\n                    // which fires after macOS finishes its async transition (#9572).\n                    // Return true to prevent the caller from setting the window\n                    // frame during the Cocoa exit animation.\n                    [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationDefault];\n                    [window toggleFullScreen:nil];\n                    return true;\n                } else {\n                    [window setStyleMask: w->ns.pre_full_screen_style_mask];\n                    [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationDefault];\n                    w->ns.in_traditional_fullscreen = false;\n                    _glfwUpdateNotchCover(w);\n                }\n            }\n        } else {\n            bool in_fullscreen = sm & NSWindowStyleMaskFullScreen;\n            if (!(in_fullscreen)) {\n                sm |= NSWindowStyleMaskBorderless | NSWindowStyleMaskFullScreen;\n                [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock];\n            } else {\n                made_fullscreen = false;\n                sm &= ~(NSWindowStyleMaskBorderless | NSWindowStyleMaskFullScreen);\n                [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationDefault];\n            }\n            [window setStyleMask: sm];\n        }\n        // Changing the style mask causes the first responder to be cleared\n        [window makeFirstResponder:w->ns.view];\n        // If the dock and menubar are hidden going from maximized to fullscreen doesn't change the window size\n        // and macOS forgets to trigger windowDidResize, so call it ourselves\n        NSNotification *notification = [NSNotification notificationWithName:NSWindowDidResizeNotification object:window];\n        [w->ns.delegate performSelector:@selector(windowDidResize:) withObject:notification afterDelay:0];\n    } else {\n        bool in_fullscreen = sm & NSWindowStyleMaskFullScreen;\n        if (!in_fullscreen && !_glfwPlatformWindowVisible(w)) {\n            // Bug in Apple's fullscreen implementation causes fullscreen to\n            // not work before window is shown (at creation) if another window\n            // is already fullscreen. Le sigh. https://github.com/kovidgoyal/kitty/issues/7448\n            _glfwPlatformAddTimer(0, false, make_window_fullscreen_after_show, (void*)(uintptr_t)(w->id), NULL);\n            return made_fullscreen;\n        }\n        if (in_fullscreen) made_fullscreen = false;\n        [window toggleFullScreen: nil];\n    }\n    update_titlebar_button_visibility_after_fullscreen_transition(w, traditional, made_fullscreen);\n    return made_fullscreen;\n}\n\n// Clipboard {{{\n\nstatic void\nlist_clipboard_mimetypes(GLFWclipboardwritedatafun write_data, void *object) {\n#define w(x) { if (ok) ok = write_data(object, x, strlen(x)); }\n    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];\n    NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};\n    BOOL has_file_urls = [pasteboard canReadObjectForClasses:@[[NSURL class]] options:options];\n    BOOL has_strings = [pasteboard canReadObjectForClasses:@[[NSString class]] options:nil];\n    /* NSLog(@\"has_file_urls: %d has_strings: %d\", has_file_urls, has_strings); */\n    bool ok = true;\n    if (has_strings) w(\"text/plain\");\n    if (has_file_urls) w(\"text/local-path-list\");\n    for (NSPasteboardItem * item in pasteboard.pasteboardItems) {\n        for (NSPasteboardType type in item.types) {\n            /* NSLog(@\"%@\", type); */\n            const char *mime = uti_to_mime(type);\n            if (mime && mime[0] && ![@(mime) hasPrefix:@\"text/plain\"]) {\n                /* NSLog(@\"ut: %@ mt: %@ tags: %@\", ut, ut.preferredMIMEType, ut.tags); */\n                w(mime);\n            }\n        }\n    }\n#undef w\n}\n\nstatic void\nget_text_plain(GLFWclipboardwritedatafun write_data, void *object) {\n    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];\n    NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};\n    NSArray* objs = [pasteboard readObjectsForClasses:@[[NSURL class], [NSString class]] options:options];\n    bool found = false;\n    if (objs) {\n        const NSUInteger count = [objs count];\n        if (count) {\n            NSMutableData *path_list = [NSMutableData dataWithCapacity:4096];  // auto-released\n            NSMutableData *text_list = [NSMutableData dataWithCapacity:4096];  // auto-released\n            for (NSUInteger i = 0;  i < count;  i++) {\n                id obj = objs[i];\n                if ([obj isKindOfClass:[NSURL class]]) {\n                    NSURL *url = (NSURL*)obj;\n                    if (url.fileURL && url.fileSystemRepresentation) {\n                        if ([path_list length] > 0) [path_list appendBytes:\"\\n\" length:1];\n                        [path_list appendBytes:url.fileSystemRepresentation length:strlen(url.fileSystemRepresentation)];\n                    }\n                } else if ([obj isKindOfClass:[NSString class]]) {\n                    if ([text_list length] > 0) [text_list appendBytes:\"\\n\" length:1];\n                    [text_list appendData:[obj dataUsingEncoding:NSUTF8StringEncoding]];\n                }\n            }\n            const NSMutableData *text = nil;\n            if (path_list.length > 0) text = path_list;\n            else if (text_list.length > 0) text = text_list;\n            if (text) {\n                found = true;\n                write_data(object, text.mutableBytes, text.length);\n            }\n        }\n    }\n    if (!found) _glfwInputError(GLFW_PLATFORM_ERROR, \"Cocoa: Failed to retrieve text/plain from pasteboard\");\n}\n\nvoid\n_glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {\n    if (clipboard_type != GLFW_CLIPBOARD) return;\n    if (mime_type == NULL) {\n        list_clipboard_mimetypes(write_data, object);\n        return;\n    }\n    if (strcmp(mime_type, \"text/plain\") == 0) {\n        get_text_plain(write_data, object);\n        return;\n    }\n    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];\n    /* NSLog(@\"mime: %s uti: %@\", mime_type, mime_to_uti(mime_type)); */\n    NSPasteboardType t = [pasteboard availableTypeFromArray:@[mime_to_uti(mime_type)]];\n    /* NSLog(@\"available type: %@\", t); */\n    if (t != nil) {\n        NSData *data = [pasteboard dataForType:t];  // auto-released\n        /* NSLog(@\"data: %@\", data); */\n        if (data != nil && data.length > 0) {\n            write_data(object, data.bytes, data.length);\n        }\n    }\n}\n\nstatic NSMutableData*\nget_clipboard_data(const _GLFWClipboardData *cd, const char *mime) {\n    NSMutableData *ans = [NSMutableData dataWithCapacity:8192];\n    if (ans == nil) return nil;\n    GLFWDataChunk chunk = cd->get_data(mime, NULL, cd->ctype);\n    void *iter = chunk.iter;\n    if (!iter) return ans;\n    while (true) {\n        chunk = cd->get_data(mime, iter, cd->ctype);\n        if (!chunk.sz) break;\n        [ans appendBytes:chunk.data length:chunk.sz];\n        if (chunk.free) chunk.free((void*)chunk.free_data);\n    }\n    cd->get_data(NULL, iter, cd->ctype);\n    return ans;\n}\n\n\nvoid\n_glfwPlatformSetClipboard(GLFWClipboardType t) {\n    if (t != GLFW_CLIPBOARD) return;\n    NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];\n    NSMutableArray<NSPasteboardType> *ptypes = [NSMutableArray arrayWithCapacity:_glfw.clipboard.num_mime_types];  // auto-released\n    for (size_t i = 0; i < _glfw.clipboard.num_mime_types; i++) {\n        [ptypes addObject:mime_to_uti(_glfw.clipboard.mime_types[i])];\n    }\n    [pasteboard declareTypes:ptypes owner:nil];\n    for (size_t i = 0; i < _glfw.clipboard.num_mime_types; i++) {\n        NSMutableData *data = get_clipboard_data(&_glfw.clipboard, _glfw.clipboard.mime_types[i]);  // auto-released\n        /* NSLog(@\"putting data: %@ for: %s with UTI: %@\", data, _glfw.clipboard.mime_types[i], ptypes[i]); */\n        if (data != nil) [pasteboard setData:data forType:ptypes[i]];\n    }\n}\n// }}}\n\nEGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)\n{\n    if (_glfw.egl.ANGLE_platform_angle)\n    {\n        int type = 0;\n\n        if (_glfw.egl.ANGLE_platform_angle_opengl)\n        {\n            if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)\n                type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;\n        }\n\n        if (_glfw.egl.ANGLE_platform_angle_metal)\n        {\n            if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_METAL)\n                type = EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE;\n        }\n\n        if (type)\n        {\n            *attribs = calloc(3, sizeof(EGLint));\n            (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;\n            (*attribs)[1] = type;\n            (*attribs)[2] = EGL_NONE;\n            return EGL_PLATFORM_ANGLE_ANGLE;\n        }\n    }\n\n    return 0;\n}\n\nEGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)\n{\n    return EGL_DEFAULT_DISPLAY;\n}\n\nEGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)\n{\n    return window->ns.layer;\n}\n\nvoid _glfwPlatformGetRequiredInstanceExtensions(char** extensions)\n{\n    if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface)\n    {\n        extensions[0] = \"VK_KHR_surface\";\n        extensions[1] = \"VK_EXT_metal_surface\";\n    }\n    else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface)\n    {\n        extensions[0] = \"VK_KHR_surface\";\n        extensions[1] = \"VK_MVK_macos_surface\";\n    }\n}\n\nint _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance UNUSED,\n                                                      VkPhysicalDevice device UNUSED,\n                                                      uint32_t queuefamily UNUSED)\n{\n    return true;\n}\n\nVkResult _glfwPlatformCreateWindowSurface(VkInstance instance,\n                                          _GLFWwindow* window,\n                                          const VkAllocationCallbacks* allocator,\n                                          VkSurfaceKHR* surface)\n{\n#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100\n    // HACK: Dynamically load Core Animation to avoid adding an extra\n    //       dependency for the majority who don't use MoltenVK\n    NSBundle* bundle = [NSBundle bundleWithPath:@\"/System/Library/Frameworks/QuartzCore.framework\"];\n    if (!bundle)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to find QuartzCore.framework\");\n        return VK_ERROR_EXTENSION_NOT_PRESENT;\n    }\n\n    // NOTE: Create the layer here as makeBackingLayer should not return nil\n    window->ns.layer = [[bundle classNamed:@\"CAMetalLayer\"] layer];\n    if (!window->ns.layer)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to create layer for view\");\n        return VK_ERROR_EXTENSION_NOT_PRESENT;\n    }\n\n    if (window->ns.retina)\n        [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];\n\n    [window->ns.view setLayer:window->ns.layer];\n    [window->ns.view setWantsLayer:YES];\n\n    VkResult err;\n\n    if (_glfw.vk.EXT_metal_surface)\n    {\n        VkMetalSurfaceCreateInfoEXT sci;\n\n        PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT;\n        vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT)\n            vkGetInstanceProcAddr(instance, \"vkCreateMetalSurfaceEXT\");\n        if (!vkCreateMetalSurfaceEXT)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"Cocoa: Vulkan instance missing VK_EXT_metal_surface extension\");\n            return VK_ERROR_EXTENSION_NOT_PRESENT;\n        }\n\n        memset(&sci, 0, sizeof(sci));\n        sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;\n        sci.pLayer = window->ns.layer;\n\n        err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface);\n    }\n    else\n    {\n        VkMacOSSurfaceCreateInfoMVK sci;\n\n        PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK;\n        vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK)\n            vkGetInstanceProcAddr(instance, \"vkCreateMacOSSurfaceMVK\");\n        if (!vkCreateMacOSSurfaceMVK)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"Cocoa: Vulkan instance missing VK_MVK_macos_surface extension\");\n            return VK_ERROR_EXTENSION_NOT_PRESENT;\n        }\n\n        memset(&sci, 0, sizeof(sci));\n        sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;\n        sci.pView = window->ns.view;\n\n        err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface);\n    }\n\n    if (err)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Cocoa: Failed to create Vulkan surface: %s\",\n                        _glfwGetVulkanResultString(err));\n    }\n\n    return err;\n#else\n    return VK_ERROR_EXTENSION_NOT_PRESENT;\n#endif\n}\n\nint\n_glfwPlatformSetWindowBlur(_GLFWwindow *window, int radius) {\n    int orig = window->ns.blur_radius;\n    if (radius > -1 && radius != window->ns.blur_radius) {\n        extern OSStatus CGSSetWindowBackgroundBlurRadius(void* connection, NSInteger windowNumber, int radius);\n        extern void* CGSDefaultConnectionForThread(void);\n        CGSSetWindowBackgroundBlurRadius(CGSDefaultConnectionForThread(), [window->ns.object windowNumber], radius);\n        window->ns.blur_radius = radius;\n    }\n    return orig;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(nil);\n    return window->ns.object;\n}\n\nGLFWAPI GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow *handle, GLFWcocoatextinputfilterfun callback) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    _GLFW_REQUIRE_INIT_OR_RETURN(nil);\n    GLFWcocoatextinputfilterfun previous = window->ns.textInputFilterCallback;\n    window->ns.textInputFilterCallback = callback;\n    return previous;\n}\n\nGLFWAPI GLFWhandleurlopen glfwSetCocoaURLOpenCallback(GLFWhandleurlopen callback) {\n    _GLFW_REQUIRE_INIT_OR_RETURN(nil);\n    GLFWhandleurlopen prev = _glfw.ns.url_open_callback;\n    _glfw.ns.url_open_callback = callback;\n    return prev;\n}\n\nGLFWAPI GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWwindow *handle, GLFWcocoatogglefullscreenfun callback) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    _GLFW_REQUIRE_INIT_OR_RETURN(nil);\n    GLFWcocoatogglefullscreenfun previous = window->ns.toggleFullscreenCallback;\n    window->ns.toggleFullscreenCallback = callback;\n    return previous;\n}\n\nGLFWAPI void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback) {\n    requestRenderFrame((_GLFWwindow*)w, callback);\n}\n\nGLFWAPI GLFWcocoarenderframefun glfwCocoaSetWindowResizeCallback(GLFWwindow *w, GLFWcocoarenderframefun cb) {\n    _GLFWwindow* window = (_GLFWwindow*)w;\n    GLFWcocoarenderframefun current = window->ns.resizeCallback;\n    window->ns.resizeCallback = cb;\n    return current;\n}\n\n@implementation NSView (FindByIdentifier)\n\n- (NSArray<NSView *> *)viewsWithIdentifier:(NSUserInterfaceItemIdentifier)identifier {\n    NSMutableArray<NSView *> *result = [NSMutableArray array];\n    if ([self.identifier isEqual:identifier]) {\n        [result addObject:self];\n    }\n    for (NSView *sub in self.subviews) {\n        [result addObjectsFromArray:[sub viewsWithIdentifier:identifier]];\n    }\n    return result;\n}\n\n@end\n\nstatic\nvoid clear_title_bar_background_views(NSWindow *window) {\n#define tag @\"kitty-for-transparent-titlebar\"\n    NSView *contentView = window.contentView, *titlebarContainer = contentView ? contentView.superview : nil;\n    if (titlebarContainer) {\n        for (NSView *subview in [titlebarContainer viewsWithIdentifier:tag]) [subview removeFromSuperview];\n    }\n}\n\nstatic void\nset_title_bar_background(NSWindow *window, NSColor *backgroundColor) {\n    // add an extra view that just renders the background color under the transparent titlebar\n    NSView *contentView = window.contentView, *titlebarContainer = contentView ? contentView.superview : nil;\n    if (!titlebarContainer) return;\n    for (NSView *subview in [titlebarContainer viewsWithIdentifier:tag]) [subview removeFromSuperview];\n    if (!backgroundColor) return;\n\n    NSButton *b = [window standardWindowButton:NSWindowCloseButton];\n    if (b) {\n        NSView *titlebarView = b.superview;\n        NSView *bgView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, titlebarView.bounds.size.width, titlebarView.bounds.size.height)];\n        bgView.translatesAutoresizingMaskIntoConstraints = NO;\n        bgView.wantsLayer = YES;\n        bgView.layer.backgroundColor = backgroundColor.CGColor;\n        bgView.identifier = tag;\n        [titlebarView addSubview:bgView positioned:NSWindowBelow relativeTo:titlebarView.subviews[0]];\n        [NSLayoutConstraint activateConstraints:@[\n            // Pin to the top of the content view.\n            [bgView.topAnchor constraintEqualToAnchor:titlebarView.topAnchor],\n            // Pin to the leading edge of the content view.\n            [bgView.leadingAnchor constraintEqualToAnchor:titlebarView.leadingAnchor],\n            // Pin to the trailing edge of the content view.\n            [bgView.trailingAnchor constraintEqualToAnchor:titlebarView.trailingAnchor],\n            // Give it a fixed height\n            [bgView.bottomAnchor constraintEqualToAnchor:titlebarView.bottomAnchor]\n        ]];\n        [bgView release];\n        return;\n    }\n    NSView *bgView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, titlebarContainer.bounds.size.width, 32)];\n    bgView.translatesAutoresizingMaskIntoConstraints = NO;\n    bgView.wantsLayer = YES;\n    bgView.layer.backgroundColor = backgroundColor.CGColor;\n    bgView.identifier = tag;\n    // position the background view above the content view but below the titlebar view\n    [titlebarContainer addSubview:bgView positioned:NSWindowAbove relativeTo:contentView];\n    // for (NSView *subview in titlebarContainer.subviews) NSLog(@\"sv: %@\", subview.identifier);\n    [NSLayoutConstraint activateConstraints:@[\n        // Pin to the top of the content view.\n        [bgView.topAnchor constraintEqualToAnchor:titlebarContainer.topAnchor],\n        // Pin to the leading edge of the content view.\n        [bgView.leadingAnchor constraintEqualToAnchor:titlebarContainer.leadingAnchor],\n        // Pin to the trailing edge of the content view.\n        [bgView.trailingAnchor constraintEqualToAnchor:titlebarContainer.trailingAnchor],\n        // Give it a fixed height\n        [bgView.bottomAnchor constraintEqualToAnchor:contentView.topAnchor]\n    ]];\n    [bgView release];\n#undef tag\n}\n\nstatic void\napply_titlebar_color_settings(_GLFWwindow *window) {\n#define tc window->ns.last_applied_titlebar_settings.color\n    GLFWWindow *nsw = window->ns.object;\n    if (!window->ns.titlebar_hidden && window->decorated && tc.was_set && window->ns.last_applied_titlebar_settings.transparent) {\n        NSColor *titlebar_color = [NSColor colorWithSRGBRed:tc.red green:tc.green blue:tc.blue alpha:tc.alpha];\n        set_title_bar_background(nsw, titlebar_color);\n    } else clear_title_bar_background_views(nsw);\n#undef tc\n}\n\n\n\nGLFWAPI void glfwCocoaSetWindowChrome(GLFWwindow *w, unsigned int color, bool use_system_color, unsigned int system_color, int background_blur, unsigned int hide_window_decorations, bool show_text_in_titlebar, int color_space, float background_opacity, bool resizable) { @autoreleasepool {\n    _GLFWwindow* window = (_GLFWwindow*)w;\n    if (window->ns.layer_shell.is_active) return;\n    GLFWWindow *nsw = window->ns.object;\n    const bool is_transparent = _glfwPlatformFramebufferTransparent(window);\n    if (!is_transparent) { background_opacity = 1.0; background_blur = 0; }\n    NSColor *window_background = [NSColor windowBackgroundColor];\n    if (background_opacity < 1.0) {\n        // use a clear color (fully transparent) so that the final color is just the color from the surface.\n        // prevent blurring of shadows at window corners with desktop background by setting a low alpha background\n        window_background = background_blur > 0 ? [NSColor colorWithWhite: 0 alpha: 0.001f] : [NSColor clearColor];\n    }\n    NSAppearance *appearance = nil;\n#define tc window->ns.last_applied_titlebar_settings.color\n    tc.was_set = false;\n    window->ns.last_applied_titlebar_settings.transparent = false;\n    const NSWindowStyleMask current_style_mask = [nsw styleMask];\n    const bool in_fullscreen = ((current_style_mask & NSWindowStyleMaskFullScreen) != 0) || window->ns.in_traditional_fullscreen;\n    NSAppearance *light_appearance = is_transparent ? [NSAppearance appearanceNamed:NSAppearanceNameVibrantLight] : [NSAppearance appearanceNamed:NSAppearanceNameAqua];\n    NSAppearance *dark_appearance = is_transparent ? [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark] : [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];\n    if (use_system_color) {\n        switch (system_color) {\n            case 1:\n                appearance = light_appearance; break;\n            case 2:\n                appearance = dark_appearance; break;\n        }\n    } else {\n        tc.red = ((color >> 16) & 0xFF) / 255.0;\n        tc.green = ((color >> 8) & 0xFF) / 255.0;\n        tc.blue = (color & 0xFF) / 255.0;\n        tc.alpha = background_opacity;\n        tc.was_set = true;\n        double luma = 0.2126 * tc.red + 0.7152 * tc.green + 0.0722 * tc.blue;\n        appearance = luma < 0.5 ? dark_appearance : light_appearance;\n        window->ns.last_applied_titlebar_settings.transparent = true;\n    }\n    [nsw setBackgroundColor:window_background];\n    [nsw setAppearance:appearance];\n    _glfwPlatformSetWindowBlur(window, background_blur);\n    bool has_shadow = false;\n    const char *decorations_desc = \"full\";\n    window->ns.titlebar_hidden = false;\n    switch (hide_window_decorations) {\n        case 1:\n            decorations_desc = \"none\";\n            window->decorated = false;\n            break;\n        case 2:\n            decorations_desc = \"no-titlebar\";\n            window->decorated = true;\n            has_shadow = true;\n            window->ns.last_applied_titlebar_settings.transparent = true;\n            window->ns.titlebar_hidden = true;\n            show_text_in_titlebar = false;\n            break;\n        case 4:\n            decorations_desc = \"no-titlebar-and-no-corners\";\n            window->decorated = false;\n            has_shadow = true;\n            break;\n        default:\n            window->decorated = true;\n            has_shadow = true;\n            break;\n    }\n    // shadow causes burn-in/ghosting because cocoa doesnt invalidate it on OS window resize/minimize/restore.\n    // https://github.com/kovidgoyal/kitty/issues/6439\n    if (is_transparent) has_shadow = false;\n    bool hide_titlebar_buttons = !in_fullscreen && window->ns.titlebar_hidden;\n    [nsw setTitlebarAppearsTransparent:window->ns.last_applied_titlebar_settings.transparent];\n    [nsw setHasShadow:has_shadow];\n    [nsw setTitleVisibility:(show_text_in_titlebar) ? NSWindowTitleVisible : NSWindowTitleHidden];\n    NSColorSpace *cs = nil;\n    switch (color_space) {\n        case SRGB_COLORSPACE: cs = [NSColorSpace sRGBColorSpace]; break;\n        case DISPLAY_P3_COLORSPACE: cs = [NSColorSpace displayP3ColorSpace]; break;\n        case DEFAULT_COLORSPACE: cs = nil; break;  // using deviceRGBColorSpace causes a hang when transitioning to fullscreen\n    }\n    window->resizable = resizable;\n    debug(\n        \"Window Chrome state:\\n\\tbackground: %s\\n\\tappearance: %s color_space: %s\\n\\t\"\n        \"blur: %d has_shadow: %d resizable: %d decorations: %s (%d)\\n\\t\"\n        \"titlebar_transparent: %d titlebar_color_set: %d title_visibility: %d hidden: %d buttons_hidden: %d\"\n        \"\\n\",\n        window_background ? [window_background.description UTF8String] : \"<nil>\",\n        appearance ? [appearance.name UTF8String] : \"<nil>\",\n        cs ? (cs.localizedName ? [cs.localizedName UTF8String] : [cs.description UTF8String]) : \"<nil>\",\n        background_blur, has_shadow, resizable, decorations_desc, window->decorated,\n        window->ns.last_applied_titlebar_settings.transparent, tc.was_set,\n        show_text_in_titlebar, window->ns.titlebar_hidden, hide_titlebar_buttons\n    );\n    [nsw setColorSpace:cs];\n    [[nsw standardWindowButton: NSWindowCloseButton] setHidden:hide_titlebar_buttons];\n    [[nsw standardWindowButton: NSWindowMiniaturizeButton] setHidden:hide_titlebar_buttons];\n    [[nsw standardWindowButton: NSWindowZoomButton] setHidden:hide_titlebar_buttons];\n    // Apple throws a hissy fit if one attempts to clear the value of NSWindowStyleMaskFullScreen outside of a full screen transition\n    // event. See https://github.com/kovidgoyal/kitty/issues/7106\n    NSWindowStyleMask fsmask = current_style_mask & NSWindowStyleMaskFullScreen;\n    window->ns.pre_full_screen_style_mask = getStyleMask(window);\n    NSWindowStyleMask desired_mask;\n    if (in_fullscreen && window->ns.in_traditional_fullscreen) {\n        desired_mask = NSWindowStyleMaskBorderless;\n    } else {\n        desired_mask = window->ns.pre_full_screen_style_mask | fsmask;\n    }\n    // Only call setStyleMask: when the mask actually changes. Redundant\n    // calls can trigger macOS to reposition the window (#9572).\n    if (desired_mask != current_style_mask) {\n        [nsw setStyleMask:desired_mask];\n    }\n#undef tc\n    apply_titlebar_color_settings(window);\n\n    // HACK: Changing the style mask can cause the first responder to be cleared\n    [nsw makeFirstResponder:window->ns.view];\n    window->ns.notch_cover_color = color;\n    window->ns.notch_cover_opacity = background_opacity;\n    if (window->ns.notch_cover_window) _glfwUpdateNotchCover(window);\n}}\n\nGLFWAPI uint32_t\nglfwGetCocoaKeyEquivalent(uint32_t glfw_key, int glfw_mods, int *cocoa_mods) {\n    *cocoa_mods = 0;\n    if (glfw_mods & GLFW_MOD_SHIFT)\n        *cocoa_mods |= NSEventModifierFlagShift;\n    if (glfw_mods & GLFW_MOD_CONTROL)\n        *cocoa_mods |= NSEventModifierFlagControl;\n    if (glfw_mods & GLFW_MOD_ALT)\n        *cocoa_mods |= NSEventModifierFlagOption;\n    if (glfw_mods & GLFW_MOD_SUPER)\n        *cocoa_mods |= NSEventModifierFlagCommand;\n    if (glfw_mods & GLFW_MOD_CAPS_LOCK)\n        *cocoa_mods |= NSEventModifierFlagCapsLock;\n    return _glfwPlatformGetNativeKeyForKey(glfw_key);\n}\n\nGLFWAPI bool glfwIsLayerShellSupported(void) { return true; }\n\nGLFWAPI void\nglfwCocoaCycleThroughOSWindows(bool backwards) {\n    NSArray *allWindows = [NSApp windows];\n    if (allWindows.count < 2) return;\n    NSMutableArray<NSWindow *> *filteredWindows = [NSMutableArray array];\n    for (NSWindow *window in allWindows) {\n        NSRect windowFrame = [window frame];\n        // Exclude zero size windows which are likely zombie windows from the Tahoe bug\n        // if ([obj isMemberOfClass:[MyClass class]]) {\n        if (\n            windowFrame.size.width > 0 && windowFrame.size.height > 0 && \\\n            !window.isMiniaturized && window.isVisible && \\\n            [window isMemberOfClass:[GLFWWindow class]]\n        ) [filteredWindows addObject:window];\n    }\n    if (filteredWindows.count < 2) return;\n    NSWindow *keyWindow = [NSApp keyWindow];\n    NSUInteger index = [filteredWindows indexOfObject:keyWindow];\n    NSUInteger nextIndex = 0;\n    if (index != NSNotFound) {\n        if (backwards) {\n            nextIndex = (index == 0) ? [filteredWindows count] - 1 : index - 1;\n        } else nextIndex = (index + 1) % filteredWindows.count;\n    }\n    NSWindow *nextWindow = filteredWindows[nextIndex];\n    [nextWindow makeKeyAndOrderFront:nil];\n}\n\n\nGLFWAPI void\nglfwCocoaRegisterMIMETypes(GLFWwindow *window, const char **mimes, size_t count) {\n    _GLFWwindow *w = (_GLFWwindow*)window;\n    NSArray *currentTypes = [w->ns.view registeredDraggedTypes];\n    NSMutableArray *updatedTypes = [NSMutableArray arrayWithArray:currentTypes];\n    for (size_t i = 0; i < count; i++) {\n        NSString *uti = mime_to_uti(mimes[i]);\n        if (![updatedTypes containsObject:uti]) [updatedTypes addObject:uti];\n    }\n    [w->ns.view registerForDraggedTypes:updatedTypes];\n}\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Transforms a y-coordinate between the CG display and NS screen spaces\n//\nfloat _glfwTransformYNS(float y)\n{\n    return CGDisplayBounds(CGMainDisplayID()).size.height - y - 1;\n}\n\nvoid _glfwCocoaPostEmptyEvent(void) {\n    NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined\n                                        location:NSMakePoint(0, 0)\n                                   modifierFlags:0\n                                       timestamp:0\n                                    windowNumber:0\n                                         context:nil\n                                         subtype:0\n                                           data1:0\n                                           data2:0];\n    [NSApp postEvent:event atStart:YES];\n}\n\n// Drag source implementation {{{\n@implementation GLFWDraggingSource\n- (NSDragOperation)draggingSession:(NSDraggingSession*)session\n                   sourceOperationMaskForDraggingContext:(NSDraggingContext)context\n{\n    (void)session; (void)context;\n    // Return the operation based on the stored drag operations bitfield\n    NSDragOperation ops = NSDragOperationCopy;\n    int q = _glfw.drag.operations;\n    if (q & GLFW_DRAG_OPERATION_COPY) ops |= NSDragOperationCopy;\n    if (q & GLFW_DRAG_OPERATION_MOVE) ops |= NSDragOperationMove;\n    if (q & GLFW_DRAG_OPERATION_GENERIC) ops |= NSDragOperationGeneric;\n    return ops;\n}\n\n- (void)draggingSession:(NSDraggingSession *)session\n           willBeginAtPoint:(NSPoint)screenPoint\n{\n    (void)session;\n    start_point = screenPoint;\n}\n\n- (void)draggingSession:(NSDraggingSession *)session\n           movedToPoint:(NSPoint)screenPoint\n{\n    (void)session;\n    current_point = screenPoint;\n}\n\n\n- (void)draggingSession:(NSDraggingSession *)session\n           endedAtPoint:(NSPoint)screenPoint\n              operation:(NSDragOperation)operation\n{\n    (void)session;\n    _glfwPlatformFreeDragSourceData();\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (window) {\n        GLFWDragEvent ev = {0};\n        switch(operation) {\n            case NSDragOperationCopy: case NSDragOperationLink:   ev.action = GLFW_DRAG_OPERATION_COPY; break;\n            case NSDragOperationMove: case NSDragOperationDelete: ev.action = GLFW_DRAG_OPERATION_MOVE; break;\n            case NSDragOperationNone: break;\n            default: ev.action = GLFW_DRAG_OPERATION_GENERIC; break;\n        }\n        switch (operation) {\n            case NSDragOperationDelete: ev.type = GLFW_DRAG_CANCELLED; break;\n            case NSDragOperationNone: {\n                NSEvent *currentEvent = [NSApp currentEvent];\n                if (currentEvent && currentEvent.type == NSEventTypeKeyDown && currentEvent.keyCode == 53) {\n                    ev.type = GLFW_DRAG_CANCELLED;\n                } else ev.type = GLFW_DRAG_DROPPED;\n            } break;\n            default:\n                ev.type = GLFW_DRAG_DROPPED; break;\n        }\n        _glfwInputDragSourceRequest(window, &ev);\n        if (operation == NSDragOperationNone) _glfwFreeDragSourceData();\n    }\n}\n@end\n\nstatic NSMutableArray<GLFWFilePromiseProviderDelegate*> *file_promise_providers = nil;\n\nstatic int\nset_image_for_dragging_item(NSDraggingItem *draggingItem, const GLFWimage *thumbnail, NSWindow *window) {\n    CGFloat scaleFactor = 1.0;\n    [draggingItem setDraggingFrame:NSMakeRect(0, 0, 32, 32) contents:nil];\n    if (_glfw.ns.drag_image) [_glfw.ns.drag_image release];\n    _glfw.ns.drag_image = nil;\n    if (thumbnail && thumbnail->pixels && window) {\n        scaleFactor = [window backingScaleFactor];\n        if (scaleFactor == 0) scaleFactor = [NSScreen mainScreen].backingScaleFactor;\n        unsigned height = thumbnail->height + 20;  // add empty padding at bottom\n        NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc]\n            initWithBitmapDataPlanes:NULL\n                            pixelsWide:thumbnail->width\n                            pixelsHigh:height\n                        bitsPerSample:8\n                        samplesPerPixel:4\n                            hasAlpha:YES\n                            isPlanar:NO\n                        colorSpaceName:NSDeviceRGBColorSpace\n                            bytesPerRow:thumbnail->width * 4\n                        bitsPerPixel:32];\n        if (!imageRep) return ENOMEM;\n        memcpy([imageRep bitmapData], thumbnail->pixels, thumbnail->width * thumbnail->height * 4);\n        NSSize pointSize = NSMakeSize(thumbnail->width / scaleFactor, height / scaleFactor);\n        [imageRep setSize:pointSize];\n        _glfw.ns.drag_image = [[NSImage alloc] initWithSize:pointSize];\n        if (!_glfw.ns.drag_image) { [imageRep release]; return ENOMEM; }\n        [_glfw.ns.drag_image addRepresentation:imageRep];\n        [imageRep release];\n        [draggingItem setImageComponentsProvider:^NSArray<NSDraggingImageComponent *> * _Nonnull{\n            NSDraggingImageComponent *icon = [NSDraggingImageComponent draggingImageComponentWithKey:NSDraggingImageComponentIconKey];\n            NSImage *image = _glfw.ns.drag_image;\n            icon.contents = image;\n            icon.frame = NSMakeRect(pointSize.width, pointSize.height, pointSize.width, pointSize.height);\n            return @[icon];\n        }];\n    }\n    return 0;\n}\n\n\nint\n_glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {@autoreleasepool{\n    if (file_promise_providers) {\n        for (NSInteger i = [file_promise_providers count] - 1; i >= 0; i--) {\n            GLFWFilePromiseProviderDelegate* d = file_promise_providers[i];\n            [d end_transfer:EINVAL];\n        }\n    }\n    // Obtain the event and view early so we can position the drag image relative to the cursor\n    NSEvent* event = [NSApp currentEvent];\n    if (!event || ([event type] != NSEventTypeLeftMouseDown &&\n                    [event type] != NSEventTypeLeftMouseDragged)) {\n        // Create a synthetic left mouse down event using stored cursor position\n        // Convert window coordinates to screen coordinates\n        NSRect contentRect = [window->ns.view frame];\n        NSPoint windowPos = NSMakePoint(window->virtualCursorPosX,\n                                        contentRect.size.height - window->virtualCursorPosY);\n\n        event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown\n                                    location:windowPos\n                                modifierFlags:0\n                                    timestamp:[[NSProcessInfo processInfo] systemUptime]\n                                windowNumber:[window->ns.object windowNumber]\n                                    context:nil\n                                eventNumber:0\n                                    clickCount:1\n                                    pressure:1.0];\n    }\n\n    if (!event) return EIO;\n    GLFWContentView *v = window->ns.view;\n    NSMutableArray<NSDraggingItem*>* dragItems = [[[NSMutableArray alloc] init] autorelease];\n    for (size_t i = 0; i < _glfw.drag.item_count; i++) {\n        NSString* utiString = mime_to_uti(_glfw.drag.items[i].mime_type);\n        id w;\n        if (_glfw.drag.items[i].optional_data) {\n            NSPasteboardItem *pbItem = [[[NSPasteboardItem alloc] init] autorelease];\n            NSData *data = [NSData dataWithBytes:_glfw.drag.items[i].optional_data length:_glfw.drag.items[i].data_size];\n            [pbItem setData:data forType:utiString];\n            w = pbItem;\n        } else {\n            // Create file promise provider with our delegate\n            GLFWFilePromiseProviderDelegate* delegate = [[[GLFWFilePromiseProviderDelegate alloc]\n                initWithWindow:window mimeType:_glfw.drag.items[i].mime_type instanceId:_glfw.drag.instance_id] autorelease];\n            NSFilePromiseProvider *provider = [[[NSFilePromiseProvider alloc]\n                initWithFileType:utiString delegate:delegate] autorelease];\n            // Store the delegate in the provider's user info so it's retained\n            provider.userInfo = delegate;\n            w = provider;\n        }\n        NSDraggingItem* dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:w] autorelease];\n\n        if (i == 0 && thumbnail && thumbnail->pixels) {\n            int err = set_image_for_dragging_item(dragItem, thumbnail, window->ns.object);\n            if (err) return err;\n        } else {\n            [dragItem setDraggingFrame:NSMakeRect(0, 0, 32, 32) contents:nil];\n        }\n        [dragItems addObject:dragItem];\n    }\n\n    NSDraggingSession *s = [v beginDraggingSessionWithItems:dragItems event:event source:[v draggingSource]];\n    _glfw.ns.drag_session = [s retain];\n    _glfw.ns.drag_view = [v retain];\n    return 0;\n}}\n\n\n@implementation GLFWFilePromiseProviderDelegate\n\n- (void)end_transfer_with_error:(NSError*)err {\n    if (err && file_url) {\n        NSError *error;\n        NSFileManager *fileManager = [NSFileManager defaultManager];\n        [fileManager removeItemAtURL:file_url error:&error];\n    }\n    if (file_handle) [file_handle release];\n    file_handle = nil;\n    if (completion_handler) {\n        completion_handler(err);\n        Block_release(completion_handler);\n        completion_handler = nil;\n    }\n    [file_promise_providers removeObject:self];\n}\n\n- (void)end_transfer:(int)errorCode {\n    [self end_transfer_with_error:errorCode ? [NSError errorWithDomain:NSPOSIXErrorDomain code:errorCode userInfo:nil] : nil];\n}\n\n- (bool)is_mimetype:(const char*)q { return strcmp(q, mimeType) == 0; }\n\n- (void)request_drag_data {\n    if (instanceId != _glfw.drag.instance_id) { [self end_transfer:EINVAL]; return; }\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (!window) { [self end_transfer:EINVAL]; return; }\n    bool keep_going = true;\n    while (keep_going) {\n        GLFWDragEvent ev = {.type=GLFW_DRAG_DATA_REQUEST, .mime_type=mimeType};\n        _glfwInputDragSourceRequest(window, &ev);\n        if (ev.err_num) {\n            keep_going = false;\n            if (ev.err_num != EAGAIN) [self end_transfer:ev.err_num];\n        } else {\n            if (ev.data_sz) {\n                NSData* nsData = [NSData dataWithBytes:ev.data length:ev.data_sz];\n                NSError* error = nil;\n                if (![file_handle writeData:nsData error:&error]) {\n                    keep_going = false;\n                    [self end_transfer_with_error:error];\n                }\n            } else {\n                keep_going = false;\n                [self end_transfer_with_error:nil];\n            }\n            _glfwInputDragSourceRequest(window, &ev);\n        }\n    }\n}\n\n- (instancetype)initWithWindow:(_GLFWwindow*)initWindow mimeType:(const char*)mime instanceId:(GLFWid) instance_id {\n    self = [super init];\n    if (self) {\n        windowId = initWindow ? initWindow->id : 0;\n        mimeType = _glfw_strdup(mime);\n        instanceId = instance_id;\n        if (file_promise_providers == nil) file_promise_providers = [NSMutableArray array];\n        [file_promise_providers addObject:self];\n    }\n    return self;\n}\n\n- (void)dealloc {\n    free(mimeType); mimeType = NULL;\n    if (file_url) [file_url release];\n    file_url = nil;\n    [self end_transfer:EINVAL];\n    [super dealloc];\n}\n\n- (NSString*)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider fileNameForType:(NSString*)fileType {\n    (void)filePromiseProvider; (void)fileType;\n    // Generate a unique filename based on the MIME type\n    NSString* extension = @\"data\";\n    if (mimeType) {\n        UTType *type = [UTType typeWithMIMEType:@(mimeType)];\n        extension = type.preferredFilenameExtension;\n    }\n    return [NSString stringWithFormat:@\"kitty-drag-source-%@.%@\", [[NSUUID UUID] UUIDString], extension];\n}\n\n- (void)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider\n          writePromiseToURL:(NSURL*)url\n          completionHandler:(void (^)(NSError*))completionHandler\n{\n    (void)filePromiseProvider;\n    _GLFWwindow* window = _glfwWindowForId(windowId);\n    if (!window || instanceId != _glfw.drag.instance_id) {\n        completionHandler([NSError errorWithDomain:NSPOSIXErrorDomain code:EINVAL userInfo:nil]);\n        return;\n    }\n\n    // Create the file\n    NSError* error = nil;\n    if (![[NSFileManager defaultManager] createFileAtPath:url.path contents:nil attributes:nil]) {\n        error = [NSError errorWithDomain:NSPOSIXErrorDomain code:EIO userInfo:nil];\n        completionHandler(error);\n        return;\n    }\n\n    NSFileHandle* fileHandle = [NSFileHandle fileHandleForWritingToURL:url error:&error];\n    if (!fileHandle) {\n        completionHandler(error);\n        NSError *error;\n        NSFileManager *fileManager = [NSFileManager defaultManager];\n        [fileManager removeItemAtURL:url error:&error];\n        return;\n    }\n    file_handle = fileHandle; completion_handler = completionHandler;\n    file_url = [url retain];\n    [self request_drag_data];\n}\n\n@end\n\nvoid\n_glfwPlatformFreeDragSourceData(void) {\n    if (_glfw.ns.drag_session) [_glfw.ns.drag_session release];\n    _glfw.ns.drag_session = nil;\n    if (_glfw.ns.drag_view) [_glfw.ns.drag_view release];\n    _glfw.ns.drag_view = nil;\n    if (_glfw.ns.drag_image) [_glfw.ns.drag_image release];\n    _glfw.ns.drag_image = nil;\n}\n\nint\n_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {@autoreleasepool{\n    if (!_glfw.ns.drag_session || !_glfw.ns.drag_view) return 0;\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    [((NSDraggingSession*)_glfw.ns.drag_session)\n        enumerateDraggingItemsWithOptions:0\n        forView:(NSView*)_glfw.ns.drag_view\n        classes:@[[NSPasteboardItem class]]\n        searchOptions:@{}\n        usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) {\n            if (idx == 0) {\n                set_image_for_dragging_item(draggingItem, thumbnail, window->ns.object);\n                *stop = YES;\n            }\n        }];\n    return 0;\n}}\n\nint\n_glfwPlatformDragDataReady(const char *mime_type) {\n    if (!file_promise_providers) return 0;\n    for (GLFWFilePromiseProviderDelegate *d in file_promise_providers) {\n        if ([d is_mimetype:mime_type]) [d request_drag_data];\n    }\n    return 0;\n}\n"
  },
  {
    "path": "glfw/context.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// Please use C89 style variable declarations in this file because VS 2010\n//========================================================================\n\n#include \"internal.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdio.h>\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Checks whether the desired context attributes are valid\n//\n// This function checks things like whether the specified client API version\n// exists and whether all relevant options have supported and non-conflicting\n// values\n//\nbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)\n{\n    if (ctxconfig->share)\n    {\n        if (ctxconfig->client == GLFW_NO_API ||\n            ctxconfig->share->context.client == GLFW_NO_API)\n        {\n            _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);\n            return false;\n        }\n    }\n\n    if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API &&\n        ctxconfig->source != GLFW_EGL_CONTEXT_API &&\n        ctxconfig->source != GLFW_OSMESA_CONTEXT_API)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM,\n                        \"Invalid context creation API 0x%08X\",\n                        ctxconfig->source);\n        return false;\n    }\n\n    if (ctxconfig->client != GLFW_NO_API &&\n        ctxconfig->client != GLFW_OPENGL_API &&\n        ctxconfig->client != GLFW_OPENGL_ES_API)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM,\n                        \"Invalid client API 0x%08X\",\n                        ctxconfig->client);\n        return false;\n    }\n\n    if (ctxconfig->client == GLFW_OPENGL_API)\n    {\n        if ((ctxconfig->major < 1 || ctxconfig->minor < 0) ||\n            (ctxconfig->major == 1 && ctxconfig->minor > 5) ||\n            (ctxconfig->major == 2 && ctxconfig->minor > 1) ||\n            (ctxconfig->major == 3 && ctxconfig->minor > 3))\n        {\n            // OpenGL 1.0 is the smallest valid version\n            // OpenGL 1.x series ended with version 1.5\n            // OpenGL 2.x series ended with version 2.1\n            // OpenGL 3.x series ended with version 3.3\n            // For now, let everything else through\n\n            _glfwInputError(GLFW_INVALID_VALUE,\n                            \"Invalid OpenGL version %i.%i\",\n                            ctxconfig->major, ctxconfig->minor);\n            return false;\n        }\n\n        if (ctxconfig->profile)\n        {\n            if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE &&\n                ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE)\n            {\n                _glfwInputError(GLFW_INVALID_ENUM,\n                                \"Invalid OpenGL profile 0x%08X\",\n                                ctxconfig->profile);\n                return false;\n            }\n\n            if (ctxconfig->major <= 2 ||\n                (ctxconfig->major == 3 && ctxconfig->minor < 2))\n            {\n                // Desktop OpenGL context profiles are only defined for version 3.2\n                // and above\n\n                _glfwInputError(GLFW_INVALID_VALUE,\n                                \"Context profiles are only defined for OpenGL version 3.2 and above\");\n                return false;\n            }\n        }\n\n        if (ctxconfig->forward && ctxconfig->major <= 2)\n        {\n            // Forward-compatible contexts are only defined for OpenGL version 3.0 and above\n            _glfwInputError(GLFW_INVALID_VALUE,\n                            \"Forward-compatibility is only defined for OpenGL version 3.0 and above\");\n            return false;\n        }\n    }\n    else if (ctxconfig->client == GLFW_OPENGL_ES_API)\n    {\n        if (ctxconfig->major < 1 || ctxconfig->minor < 0 ||\n            (ctxconfig->major == 1 && ctxconfig->minor > 1) ||\n            (ctxconfig->major == 2 && ctxconfig->minor > 0))\n        {\n            // OpenGL ES 1.0 is the smallest valid version\n            // OpenGL ES 1.x series ended with version 1.1\n            // OpenGL ES 2.x series ended with version 2.0\n            // For now, let everything else through\n\n            _glfwInputError(GLFW_INVALID_VALUE,\n                            \"Invalid OpenGL ES version %i.%i\",\n                            ctxconfig->major, ctxconfig->minor);\n            return false;\n        }\n    }\n\n    if (ctxconfig->robustness)\n    {\n        if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION &&\n            ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET)\n        {\n            _glfwInputError(GLFW_INVALID_ENUM,\n                            \"Invalid context robustness mode 0x%08X\",\n                            ctxconfig->robustness);\n            return false;\n        }\n    }\n\n    if (ctxconfig->release)\n    {\n        if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE &&\n            ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH)\n        {\n            _glfwInputError(GLFW_INVALID_ENUM,\n                            \"Invalid context release behavior 0x%08X\",\n                            ctxconfig->release);\n            return false;\n        }\n    }\n\n    return true;\n}\n\n// Retrieves the attributes of the current context\n//\nbool _glfwRefreshContextAttribs(_GLFWwindow* window,\n                                    const _GLFWctxconfig* ctxconfig)\n{\n    int i;\n    _GLFWwindow* previous;\n    const char* version;\n    const char* prefixes[] =\n    {\n        \"OpenGL ES-CM \",\n        \"OpenGL ES-CL \",\n        \"OpenGL ES \",\n        NULL\n    };\n\n    window->context.source = ctxconfig->source;\n    window->context.client = GLFW_OPENGL_API;\n\n    previous = _glfwPlatformGetTls(&_glfw.contextSlot);\n    glfwMakeContextCurrent((GLFWwindow*) window);\n\n    window->context.GetIntegerv = (PFNGLGETINTEGERVPROC)\n        window->context.getProcAddress(\"glGetIntegerv\");\n    window->context.GetString = (PFNGLGETSTRINGPROC)\n        window->context.getProcAddress(\"glGetString\");\n    if (!window->context.GetIntegerv || !window->context.GetString)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Entry point retrieval is broken\");\n        glfwMakeContextCurrent((GLFWwindow*) previous);\n        return false;\n    }\n\n    version = (const char*) window->context.GetString(GL_VERSION);\n    if (!version)\n    {\n        if (ctxconfig->client == GLFW_OPENGL_API)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"OpenGL version string retrieval is broken\");\n        }\n        else\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"OpenGL ES version string retrieval is broken\");\n        }\n\n        glfwMakeContextCurrent((GLFWwindow*) previous);\n        return false;\n    }\n\n    for (i = 0;  prefixes[i];  i++)\n    {\n        const size_t length = strlen(prefixes[i]);\n\n        if (strncmp(version, prefixes[i], length) == 0)\n        {\n            version += length;\n            window->context.client = GLFW_OPENGL_ES_API;\n            break;\n        }\n    }\n\n    if (sscanf(version, \"%d.%d.%d\",\n                &window->context.major,\n                &window->context.minor,\n                &window->context.revision) < 1)\n    {\n        if (window->context.client == GLFW_OPENGL_API)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"No version found in OpenGL version string\");\n        }\n        else\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"No version found in OpenGL ES version string\");\n        }\n\n        glfwMakeContextCurrent((GLFWwindow*) previous);\n        return false;\n    }\n\n    if (window->context.major < ctxconfig->major ||\n        (window->context.major == ctxconfig->major &&\n         window->context.minor < ctxconfig->minor))\n    {\n        // The desired OpenGL version is greater than the actual version\n        // This only happens if the machine lacks {GLX|WGL}_ARB_create_context\n        // /and/ the user has requested an OpenGL version greater than 1.0\n\n        // For API consistency, we emulate the behavior of the\n        // {GLX|WGL}_ARB_create_context extension and fail here\n\n        if (window->context.client == GLFW_OPENGL_API)\n        {\n            _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                            \"Requested OpenGL version %i.%i, got version %i.%i\",\n                            ctxconfig->major, ctxconfig->minor,\n                            window->context.major, window->context.minor);\n        }\n        else\n        {\n            _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                            \"Requested OpenGL ES version %i.%i, got version %i.%i\",\n                            ctxconfig->major, ctxconfig->minor,\n                            window->context.major, window->context.minor);\n        }\n\n        glfwMakeContextCurrent((GLFWwindow*) previous);\n        return false;\n    }\n\n    if (window->context.major >= 3)\n    {\n        // OpenGL 3.0+ uses a different function for extension string retrieval\n        // We cache it here instead of in glfwExtensionSupported mostly to alert\n        // users as early as possible that their build may be broken\n\n        window->context.GetStringi = (PFNGLGETSTRINGIPROC)\n            window->context.getProcAddress(\"glGetStringi\");\n        if (!window->context.GetStringi)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"Entry point retrieval is broken\");\n            glfwMakeContextCurrent((GLFWwindow*) previous);\n            return false;\n        }\n    }\n\n    if (window->context.client == GLFW_OPENGL_API)\n    {\n        // Read back context flags (OpenGL 3.0 and above)\n        if (window->context.major >= 3)\n        {\n            GLint flags;\n            window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags);\n\n            if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)\n                window->context.forward = true;\n\n            if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)\n                window->context.debug = true;\n            else if (glfwExtensionSupported(\"GL_ARB_debug_output\") &&\n                     ctxconfig->debug)\n            {\n                // HACK: This is a workaround for older drivers (pre KHR_debug)\n                //       not setting the debug bit in the context flags for\n                //       debug contexts\n                window->context.debug = true;\n            }\n\n            if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR)\n                window->context.noerror = true;\n        }\n\n        // Read back OpenGL context profile (OpenGL 3.2 and above)\n        if (window->context.major >= 4 ||\n            (window->context.major == 3 && window->context.minor >= 2))\n        {\n            GLint mask;\n            window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);\n\n            if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)\n                window->context.profile = GLFW_OPENGL_COMPAT_PROFILE;\n            else if (mask & GL_CONTEXT_CORE_PROFILE_BIT)\n                window->context.profile = GLFW_OPENGL_CORE_PROFILE;\n            else if (glfwExtensionSupported(\"GL_ARB_compatibility\"))\n            {\n                // HACK: This is a workaround for the compatibility profile bit\n                //       not being set in the context flags if an OpenGL 3.2+\n                //       context was created without having requested a specific\n                //       version\n                window->context.profile = GLFW_OPENGL_COMPAT_PROFILE;\n            }\n        }\n\n        // Read back robustness strategy\n        if (glfwExtensionSupported(\"GL_ARB_robustness\"))\n        {\n            // NOTE: We avoid using the context flags for detection, as they are\n            //       only present from 3.0 while the extension applies from 1.1\n\n            GLint strategy;\n            window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB,\n                                        &strategy);\n\n            if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB)\n                window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET;\n            else if (strategy == GL_NO_RESET_NOTIFICATION_ARB)\n                window->context.robustness = GLFW_NO_RESET_NOTIFICATION;\n        }\n    }\n    else\n    {\n        // Read back robustness strategy\n        if (glfwExtensionSupported(\"GL_EXT_robustness\"))\n        {\n            // NOTE: The values of these constants match those of the OpenGL ARB\n            //       one, so we can reuse them here\n\n            GLint strategy;\n            window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB,\n                                        &strategy);\n\n            if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB)\n                window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET;\n            else if (strategy == GL_NO_RESET_NOTIFICATION_ARB)\n                window->context.robustness = GLFW_NO_RESET_NOTIFICATION;\n        }\n    }\n\n    if (glfwExtensionSupported(\"GL_KHR_context_flush_control\"))\n    {\n        GLint behavior;\n        window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior);\n\n        if (behavior == GL_NONE)\n            window->context.release = GLFW_RELEASE_BEHAVIOR_NONE;\n        else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH)\n            window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH;\n    }\n\n    glfwMakeContextCurrent((GLFWwindow*) previous);\n    return true;\n}\n\n// Searches an extension string for the specified extension\n//\nbool _glfwStringInExtensionString(const char* string, const char* extensions)\n{\n    const char* start = extensions;\n\n    for (;;)\n    {\n        const char* where;\n        const char* terminator;\n\n        where = strstr(start, string);\n        if (!where)\n            return false;\n\n        terminator = where + strlen(string);\n        if (where == start || *(where - 1) == ' ')\n        {\n            if (*terminator == ' ' || *terminator == '\\0')\n                break;\n        }\n\n        start = terminator;\n    }\n\n    return true;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW public API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)\n{\n    _GLFW_REQUIRE_INIT();\n\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot);\n\n    if (window && window->context.client == GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_NO_WINDOW_CONTEXT,\n                        \"Cannot make current with a window that has no OpenGL or OpenGL ES context\");\n        return;\n    }\n\n    if (previous)\n    {\n        if (!window || window->context.source != previous->context.source)\n            previous->context.makeCurrent(NULL);\n    }\n\n    if (window)\n        window->context.makeCurrent(window);\n}\n\nGLFWAPI GLFWwindow* glfwGetCurrentContext(void)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return _glfwPlatformGetTls(&_glfw.contextSlot);\n}\n\nGLFWAPI void glfwSwapBuffers(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (window->context.client == GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_NO_WINDOW_CONTEXT,\n                        \"Cannot swap buffers of a window that has no OpenGL or OpenGL ES context\");\n        return;\n    }\n\n    window->context.swapBuffers(window);\n#ifdef _GLFW_WAYLAND\n    _glfwWaylandAfterBufferSwap(window);\n#endif\n}\n\nGLFWAPI void glfwSwapInterval(int interval)\n{\n    _GLFWwindow* window;\n\n    _GLFW_REQUIRE_INIT();\n\n    window = _glfwPlatformGetTls(&_glfw.contextSlot);\n    if (!window)\n    {\n        _glfwInputError(GLFW_NO_CURRENT_CONTEXT,\n                        \"Cannot set swap interval without a current OpenGL or OpenGL ES context\");\n        return;\n    }\n\n    window->context.swapInterval(interval);\n}\n\nGLFWAPI int glfwExtensionSupported(const char* extension)\n{\n    _GLFWwindow* window;\n    assert(extension != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    window = _glfwPlatformGetTls(&_glfw.contextSlot);\n    if (!window)\n    {\n        _glfwInputError(GLFW_NO_CURRENT_CONTEXT,\n                        \"Cannot query extension without a current OpenGL or OpenGL ES context\");\n        return false;\n    }\n\n    if (*extension == '\\0')\n    {\n        _glfwInputError(GLFW_INVALID_VALUE, \"Extension name cannot be an empty string\");\n        return false;\n    }\n\n    if (window->context.major >= 3)\n    {\n        int i;\n        GLint count;\n\n        // Check if extension is in the modern OpenGL extensions string list\n\n        window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count);\n\n        for (i = 0;  i < count;  i++)\n        {\n            const char* en = (const char*)\n                window->context.GetStringi(GL_EXTENSIONS, i);\n            if (!en)\n            {\n                _glfwInputError(GLFW_PLATFORM_ERROR,\n                                \"Extension string retrieval is broken\");\n                return false;\n            }\n\n            if (strcmp(en, extension) == 0)\n                return true;\n        }\n    }\n    else\n    {\n        // Check if extension is in the old style OpenGL extensions string\n\n        const char* extensions = (const char*)\n            window->context.GetString(GL_EXTENSIONS);\n        if (!extensions)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"Extension string retrieval is broken\");\n            return false;\n        }\n\n        if (_glfwStringInExtensionString(extension, extensions))\n            return true;\n    }\n\n    // Check if extension is in the platform-specific string\n    return window->context.extensionSupported(extension);\n}\n\nGLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)\n{\n    _GLFWwindow* window;\n    assert(procname != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    window = _glfwPlatformGetTls(&_glfw.contextSlot);\n    if (!window)\n    {\n        _glfwInputError(GLFW_NO_CURRENT_CONTEXT,\n                        \"Cannot query entry point without a current OpenGL or OpenGL ES context\");\n        return NULL;\n    }\n\n    return window->context.getProcAddress(procname);\n}\n"
  },
  {
    "path": "glfw/dbus_glfw.c",
    "content": "//========================================================================\n// GLFW 3.4 XKB - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n\n#include \"internal.h\"\n#include \"dbus_glfw.h\"\n#include \"../kitty/monotonic.h\"\n#include <stdlib.h>\n#include <string.h>\n\nstatic void\nreport_error(DBusError *err, const char *fmt, ...) {\n    static char buf[4096];\n    va_list args;\n    va_start(args, fmt);\n    int n = vsnprintf(buf, sizeof(buf), fmt, args);\n    va_end(args);\n    if (n >= 0 && (size_t)n < (sizeof(buf) - 256)) snprintf(buf + n, sizeof(buf) - n, \". DBUS error: %s\", err->message ? err->message : \"(null)\");\n    _glfwInputError(GLFW_PLATFORM_ERROR, \"%s\", buf);\n    dbus_error_free(err);\n}\n\nstatic _GLFWDBUSData *dbus_data = NULL;\nstatic DBusConnection *session_bus = NULL;\n\nbool\nglfw_dbus_init(_GLFWDBUSData *dbus, EventLoopData *eld) {\n    dbus->eld = eld;\n    dbus_data = dbus;\n    return true;\n}\n\nstatic void\non_dbus_watch_ready(int fd UNUSED, int events, void *data) {\n    DBusWatch *watch = (DBusWatch*)data;\n    unsigned int flags = 0;\n    if (events & POLLERR) flags |= DBUS_WATCH_ERROR;\n    if (events & POLLHUP) flags |= DBUS_WATCH_HANGUP;\n    if (events & POLLIN) flags |= DBUS_WATCH_READABLE;\n    if (events & POLLOUT) flags |= DBUS_WATCH_WRITABLE;\n    dbus_watch_handle(watch, flags);\n}\n\nstatic int\nevents_for_watch(DBusWatch *watch) {\n    int events = 0;\n    unsigned int flags = dbus_watch_get_flags(watch);\n    if (flags & DBUS_WATCH_READABLE) events |= POLLIN;\n    if (flags & DBUS_WATCH_WRITABLE) events |= POLLOUT;\n    return events;\n}\n\nstatic dbus_bool_t\nadd_dbus_watch(DBusWatch *watch, void *data) {\n    id_type watch_id = addWatch(dbus_data->eld, data, dbus_watch_get_unix_fd(watch), events_for_watch(watch), dbus_watch_get_enabled(watch), on_dbus_watch_ready, watch);\n    if (!watch_id) return FALSE;\n    id_type *idp = malloc(sizeof(id_type));\n    if (!idp) return FALSE;\n    *idp = watch_id;\n    dbus_watch_set_data(watch, idp, free);\n    return TRUE;\n}\n\nstatic void\nremove_dbus_watch(DBusWatch *watch, void *data UNUSED) {\n    id_type *idp = dbus_watch_get_data(watch);\n    if (idp) removeWatch(dbus_data->eld, *idp);\n}\n\nstatic void\ntoggle_dbus_watch(DBusWatch *watch, void *data UNUSED) {\n    id_type *idp = dbus_watch_get_data(watch);\n    if (idp) toggleWatch(dbus_data->eld, *idp, dbus_watch_get_enabled(watch));\n}\n\nstatic void\non_dbus_timer_ready(id_type timer_id UNUSED, void *data) {\n    if (data) {\n        DBusTimeout *t = (DBusTimeout*)data;\n        dbus_timeout_handle(t);\n    }\n}\n\n\nstatic dbus_bool_t\nadd_dbus_timeout(DBusTimeout *timeout, void *data) {\n    int enabled = dbus_timeout_get_enabled(timeout) ? 1 : 0;\n    monotonic_t interval = ms_to_monotonic_t(dbus_timeout_get_interval(timeout));\n    if (interval < 0) return FALSE;\n    id_type timer_id = addTimer(dbus_data->eld, data, interval, enabled, true, on_dbus_timer_ready, timeout, NULL);\n    if (!timer_id) return FALSE;\n    id_type *idp = malloc(sizeof(id_type));\n    if (!idp) {\n        removeTimer(dbus_data->eld, timer_id);\n        return FALSE;\n    }\n    *idp = timer_id;\n    dbus_timeout_set_data(timeout, idp, free);\n    return TRUE;\n\n}\n\nstatic void\nremove_dbus_timeout(DBusTimeout *timeout, void *data UNUSED) {\n    id_type *idp = dbus_timeout_get_data(timeout);\n    if (idp) removeTimer(dbus_data->eld, *idp);\n}\n\nstatic void\ntoggle_dbus_timeout(DBusTimeout *timeout, void *data UNUSED) {\n    id_type *idp = dbus_timeout_get_data(timeout);\n    if (idp) toggleTimer(dbus_data->eld, *idp, dbus_timeout_get_enabled(timeout));\n}\n\n\nDBusConnection*\nglfw_dbus_connect_to(const char *path, const char* err_msg, const char *name, bool register_on_bus) {\n    DBusError err;\n    dbus_error_init(&err);\n    DBusConnection *ans = dbus_connection_open_private(path, &err);\n    if (!ans) {\n        report_error(&err, err_msg);\n        return NULL;\n    }\n    dbus_connection_set_exit_on_disconnect(ans, FALSE);\n    dbus_error_free(&err);\n    if (register_on_bus) {\n        if (!dbus_bus_register(ans, &err)) {\n            report_error(&err, err_msg);\n            return NULL;\n        }\n    }\n    if (!dbus_connection_set_watch_functions(ans, add_dbus_watch, remove_dbus_watch, toggle_dbus_watch, (void*)name, NULL)) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to set DBUS watches on connection to: %s\", path);\n        dbus_connection_close(ans);\n        dbus_connection_unref(ans);\n        return NULL;\n    }\n    if (!dbus_connection_set_timeout_functions(ans, add_dbus_timeout, remove_dbus_timeout, toggle_dbus_timeout, (void*)name, NULL)) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to set DBUS timeout functions on connection to: %s\", path);\n        dbus_connection_close(ans);\n        dbus_connection_unref(ans);\n        return NULL;\n    }\n    return ans;\n}\n\nvoid\nglfw_dbus_dispatch(DBusConnection *conn) {\n    while(dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);\n}\n\nvoid\nglfw_dbus_session_bus_dispatch(void) {\n    if (session_bus) glfw_dbus_dispatch(session_bus);\n}\n\nvoid\nglfw_dbus_terminate(_GLFWDBUSData *dbus UNUSED) {\n    if (dbus_data) {\n        dbus_data->eld = NULL;\n        dbus_data = NULL;\n    }\n    if (session_bus) {\n        dbus_connection_unref(session_bus);\n        session_bus = NULL;\n    }\n}\n\nvoid\nglfw_dbus_close_connection(DBusConnection *conn) {\n    dbus_connection_close(conn);\n    dbus_connection_unref(conn);\n}\n\nbool\nglfw_dbus_get_args(DBusMessage *msg, const char *failmsg, ...) {\n    DBusError err;\n    dbus_error_init(&err);\n    va_list ap;\n    va_start(ap, failmsg);\n    int firstarg = va_arg(ap, int);\n    bool ret = dbus_message_get_args_valist(msg, &err, firstarg, ap) ? true : false;\n    va_end(ap);\n    if (!ret) report_error(&err, failmsg);\n    return ret;\n}\n\ntypedef struct {\n    dbus_pending_callback callback;\n    void *user_data;\n} MethodResponse;\n\nstatic void\nmethod_reply_received(DBusPendingCall *pending, void *user_data) {\n    MethodResponse *res = (MethodResponse*)user_data;\n    RAII_MSG(msg, dbus_pending_call_steal_reply(pending));\n    if (msg) {\n        DBusError err;\n        dbus_error_init(&err);\n        if (dbus_set_error_from_message(&err, msg)) res->callback(NULL, &err, res->user_data);\n        else res->callback(msg, NULL, res->user_data);\n    }\n}\n\nbool\ncall_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_pending_callback callback, void *user_data, bool block) {\n    bool retval = false;\n#define REPORT(errs) _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to call DBUS method: node=%s path=%s interface=%s method=%s, with error: %s\", dbus_message_get_destination(msg), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), errs)\n    if (callback) {\n        DBusPendingCall *pending = NULL;\n        if (block) {\n            DBusError error; dbus_error_init(&error);\n            RAII_MSG(reply, dbus_connection_send_with_reply_and_block(session_bus, msg, timeout, &error));\n            if (dbus_error_is_set(&error)) {\n                callback(reply, &error, user_data);\n                return false;\n            } else if (reply) {\n                callback(reply, NULL, user_data);\n            } else return false;\n        } else if (dbus_connection_send_with_reply(conn, msg, &pending, timeout)) {\n            MethodResponse *res = malloc(sizeof(MethodResponse));\n            if (!res) return false;\n            res->callback = callback;\n            res->user_data = user_data;\n            dbus_pending_call_set_notify(pending, method_reply_received, res, free);\n            retval = true;\n        } else {\n            REPORT(\"out of memory\");\n        }\n    } else {\n        if (dbus_connection_send(conn, msg, NULL)) {\n            retval = true;\n        } else {\n            REPORT(\"out of memory\");\n        }\n    }\n    return retval;\n#undef REPORT\n}\n\nstatic bool\ncall_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void *user_data, bool blocking, va_list ap) {\n    if (!conn || !path) return false;\n    RAII_MSG(msg, dbus_message_new_method_call(node, path, interface, method));\n    if (!msg) return false;\n    bool retval = false;\n\n    int firstarg = va_arg(ap, int);\n    if ((firstarg == DBUS_TYPE_INVALID) || dbus_message_append_args_valist(msg, firstarg, ap)) {\n        retval = call_method_with_msg(conn, msg, timeout, callback, user_data, blocking);\n    } else {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to call DBUS method: %s on node: %s and interface: %s could not add arguments\", method, node, interface);\n    }\n\n    return retval;\n}\n\nbool\nglfw_dbus_call_method_with_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void* user_data, ...) {\n    bool retval;\n    va_list ap;\n    va_start(ap, user_data);\n    retval = call_method(conn, node, path, interface, method, timeout, callback, user_data, false, ap);\n    va_end(ap);\n    return retval;\n}\n\nbool\nglfw_dbus_call_blocking_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void* user_data, ...) {\n    bool retval;\n    va_list ap;\n    va_start(ap, user_data);\n    retval = call_method(conn, node, path, interface, method, timeout, callback, user_data, true, ap);\n    va_end(ap);\n    return retval;\n}\n\nbool\nglfw_dbus_call_method_no_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...) {\n    bool retval;\n    va_list ap;\n    va_start(ap, method);\n    retval = call_method(conn, node, path, interface, method, DBUS_TIMEOUT_USE_DEFAULT, NULL, NULL, false, ap);\n    va_end(ap);\n    return retval;\n}\n\nint\nglfw_dbus_match_signal(DBusMessage *msg, const char *interface, ...) {\n    va_list ap;\n    va_start(ap, interface);\n    int ans = -1, num = -1;\n    while(1) {\n        num++;\n        const char *name = va_arg(ap, const char*);\n        if (!name) break;\n        if (dbus_message_is_signal(msg, interface, name)) { ans = num; break; }\n    }\n    va_end(ap);\n    return ans;\n}\n\nstatic void\nglfw_dbus_connect_to_session_bus(void) {\n    DBusError error;\n    dbus_error_init(&error);\n    if (session_bus) {\n        dbus_connection_unref(session_bus);\n    }\n    session_bus = dbus_bus_get(DBUS_BUS_SESSION, &error);\n    if (dbus_error_is_set(&error)) {\n        report_error(&error, \"Failed to connect to DBUS session bus\");\n        session_bus = NULL;\n        return;\n    }\n    static const char *name = \"session-bus\";\n    if (!dbus_connection_set_watch_functions(session_bus, add_dbus_watch, remove_dbus_watch, toggle_dbus_watch, (void*)name, NULL)) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to set DBUS watches on connection to: %s\", name);\n        dbus_connection_close(session_bus);\n        dbus_connection_unref(session_bus);\n        return;\n    }\n    if (!dbus_connection_set_timeout_functions(session_bus, add_dbus_timeout, remove_dbus_timeout, toggle_dbus_timeout, (void*)name, NULL)) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to set DBUS timeout functions on connection to: %s\", name);\n        dbus_connection_close(session_bus);\n        dbus_connection_unref(session_bus);\n        return;\n    }\n\n}\n\nDBusConnection *\nglfw_dbus_session_bus(void) {\n    if (!session_bus) glfw_dbus_connect_to_session_bus();\n    return session_bus;\n}\n"
  },
  {
    "path": "glfw/dbus_glfw.h",
    "content": "//========================================================================\n// GLFW 3.4 XKB - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n\n#pragma once\n\n#include <dbus/dbus.h>\n#include \"backend_utils.h\"\n\nstatic inline void cleanup_msg(void *p) { DBusMessage *m = *(DBusMessage**)p; if (m) dbus_message_unref(m); m = NULL; }\n#define RAII_MSG(name, initializer) __attribute__((cleanup(cleanup_msg))) DBusMessage *name = initializer\n\ntypedef void(*dbus_pending_callback)(DBusMessage *msg, const DBusError *err, void* data);\n\ntypedef struct {\n    EventLoopData* eld;\n} _GLFWDBUSData;\n\n\nbool glfw_dbus_init(_GLFWDBUSData *dbus, EventLoopData *eld);\nvoid glfw_dbus_terminate(_GLFWDBUSData *dbus);\nDBusConnection* glfw_dbus_connect_to(const char *path, const char* err_msg, const char* name, bool register_on_bus);\nvoid glfw_dbus_close_connection(DBusConnection *conn);\nbool\ncall_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_pending_callback callback, void *user_data, bool block);\nbool\nglfw_dbus_call_method_no_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...);\nbool\nglfw_dbus_call_method_with_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout_ms, dbus_pending_callback callback, void *user_data, ...);\nbool\nglfw_dbus_call_blocking_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void* user_data, ...);\nvoid glfw_dbus_dispatch(DBusConnection *);\nvoid glfw_dbus_session_bus_dispatch(void);\nbool glfw_dbus_get_args(DBusMessage *msg, const char *failmsg, ...);\nint glfw_dbus_match_signal(DBusMessage *msg, const char *interface, ...);\nDBusConnection* glfw_dbus_session_bus(void);\n"
  },
  {
    "path": "glfw/egl_context.c",
    "content": "//========================================================================\n// GLFW 3.4 EGL - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// Please use C89 style variable declarations in this file because VS 2010\n//========================================================================\n\n#include \"internal.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n\n\n// Return a description of the specified EGL error\n//\nstatic const char* getEGLErrorString(EGLint error)\n{\n    switch (error)\n    {\n        case EGL_SUCCESS:\n            return \"Success\";\n        case EGL_NOT_INITIALIZED:\n            return \"EGL is not or could not be initialized\";\n        case EGL_BAD_ACCESS:\n            return \"EGL cannot access a requested resource\";\n        case EGL_BAD_ALLOC:\n            return \"EGL failed to allocate resources for the requested operation\";\n        case EGL_BAD_ATTRIBUTE:\n            return \"An unrecognized attribute or attribute value was passed in the attribute list\";\n        case EGL_BAD_CONTEXT:\n            return \"An EGLContext argument does not name a valid EGL rendering context\";\n        case EGL_BAD_CONFIG:\n            return \"An EGLConfig argument does not name a valid EGL frame buffer configuration\";\n        case EGL_BAD_CURRENT_SURFACE:\n            return \"The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid\";\n        case EGL_BAD_DISPLAY:\n            return \"An EGLDisplay argument does not name a valid EGL display connection\";\n        case EGL_BAD_SURFACE:\n            return \"An EGLSurface argument does not name a valid surface configured for GL rendering\";\n        case EGL_BAD_MATCH:\n            return \"Arguments are inconsistent\";\n        case EGL_BAD_PARAMETER:\n            return \"One or more argument values are invalid\";\n        case EGL_BAD_NATIVE_PIXMAP:\n            return \"A NativePixmapType argument does not refer to a valid native pixmap\";\n        case EGL_BAD_NATIVE_WINDOW:\n            return \"A NativeWindowType argument does not refer to a valid native window\";\n        case EGL_CONTEXT_LOST:\n            return \"The application must destroy all contexts and reinitialise\";\n        default:\n            return \"ERROR: UNKNOWN EGL ERROR\";\n    }\n}\n\n#ifdef _GLFW_X11\n// Returns the specified attribute of the specified EGLConfig\n//\nstatic int getEGLConfigAttrib(EGLConfig config, int attrib)\n{\n    int value;\n    eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value);\n    return value;\n}\n#endif\n\n// Return the EGLConfig most closely matching the specified hints\n//\nstatic bool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,\n                                const _GLFWfbconfig* desired,\n                                EGLConfig* result)\n{\n    EGLConfig configs[512];\n    int i = 0, nativeCount = 0, ans_idx = 0;\n    EGLint attributes[64];\n#define ATTR(k, v) { attributes[i++] = k; attributes[i++] = v; }\n    ATTR(EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER);\n    ATTR(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);\n    if (ctxconfig->client == GLFW_OPENGL_ES_API) {\n        if (ctxconfig->major == 1) ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT)\n        else ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT);\n    }\n    else if (ctxconfig->client == GLFW_OPENGL_API) ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT);\n    if (desired->samples > 0) ATTR(EGL_SAMPLES, desired->samples);\n    if (desired->depthBits > 0) ATTR(EGL_DEPTH_SIZE, desired->depthBits);\n    if (desired->stencilBits > 0) ATTR(EGL_STENCIL_SIZE, desired->stencilBits);\n    if (desired->redBits > 0) ATTR(EGL_RED_SIZE, desired->redBits);\n    if (desired->greenBits > 0) ATTR(EGL_GREEN_SIZE, desired->greenBits);\n    if (desired->blueBits > 0) ATTR(EGL_BLUE_SIZE, desired->blueBits);\n    if (desired->alphaBits > 0) ATTR(EGL_ALPHA_SIZE, desired->alphaBits);\n    ATTR(EGL_NONE, EGL_NONE);\n#undef ATTR\n    if (!eglChooseConfig(_glfw.egl.display, attributes, configs, sizeof(configs)/sizeof(configs[0]), &nativeCount)) {\n        _glfwInputError(GLFW_API_UNAVAILABLE, \"EGL: eglChooseConfig failed\");\n        return false;\n    }\n\n    if (!nativeCount)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE, \"EGL: No EGLConfigs returned\");\n        return false;\n    }\n\n\n    for (i = 0;  i < nativeCount;  i++)\n    {\n\n#if defined(_GLFW_X11)\n        {\n            const EGLConfig n = configs[i];\n            XVisualInfo vi = {0};\n\n            // Only consider EGLConfigs with associated Visuals\n            vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);\n            if (!vi.visualid)\n                continue;\n\n            if (desired->transparent)\n            {\n                int count;\n                XVisualInfo* vis =\n                    XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count);\n                if (vis)\n                {\n                    bool transparent = _glfwIsVisualTransparentX11(vis[0].visual);\n                    XFree(vis);\n                    if (!transparent) continue;\n                }\n            }\n        }\n#endif // _GLFW_X11\n        ans_idx = i;\n        break;\n    }\n    *result = configs[ans_idx];\n    return true;\n}\n\nstatic void makeContextCurrentEGL(_GLFWwindow* window)\n{\n    if (window)\n    {\n        if (!eglMakeCurrent(_glfw.egl.display,\n                            window->context.egl.surface,\n                            window->context.egl.surface,\n                            window->context.egl.handle))\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"EGL: Failed to make context current: %s\",\n                            getEGLErrorString(eglGetError()));\n            return;\n        }\n    }\n    else\n    {\n        if (!eglMakeCurrent(_glfw.egl.display,\n                            EGL_NO_SURFACE,\n                            EGL_NO_SURFACE,\n                            EGL_NO_CONTEXT))\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"EGL: Failed to clear current context: %s\",\n                            getEGLErrorString(eglGetError()));\n            return;\n        }\n    }\n\n    _glfwPlatformSetTls(&_glfw.contextSlot, window);\n}\n\nstatic void swapBuffersEGL(_GLFWwindow* window)\n{\n    if (window != _glfwPlatformGetTls(&_glfw.contextSlot))\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"EGL: The context must be current on the calling thread when swapping buffers\");\n        return;\n    }\n\n    eglSwapBuffers(_glfw.egl.display, window->context.egl.surface);\n}\n\nstatic void swapIntervalEGL(int interval)\n{\n    eglSwapInterval(_glfw.egl.display, interval);\n}\n\nstatic int extensionSupportedEGL(const char* extension)\n{\n    const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS);\n    if (extensions)\n    {\n        if (_glfwStringInExtensionString(extension, extensions))\n            return true;\n    }\n\n    return false;\n}\n\nstatic GLFWglproc getProcAddressEGL(const char* procname)\n{\n    _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);\n    assert(window != NULL);\n\n    if (window->context.egl.client)\n    {\n        GLFWglproc proc = NULL;\n        glfw_dlsym(proc, window->context.egl.client, procname);\n        if (proc)\n            return proc;\n    }\n\n    return eglGetProcAddress(procname);\n}\n\nstatic void destroyContextEGL(_GLFWwindow* window)\n{\n#if defined(_GLFW_X11)\n    // NOTE: Do not unload libGL.so.1 while the X11 display is still open,\n    //       as it will make XCloseDisplay segfault\n    if (window->context.client != GLFW_OPENGL_API)\n#endif // _GLFW_X11\n    {\n        if (window->context.egl.client)\n        {\n            _glfw_dlclose(window->context.egl.client);\n            window->context.egl.client = NULL;\n        }\n    }\n\n    if (window->context.egl.surface)\n    {\n        eglDestroySurface(_glfw.egl.display, window->context.egl.surface);\n        window->context.egl.surface = EGL_NO_SURFACE;\n    }\n\n    if (window->context.egl.handle)\n    {\n        eglDestroyContext(_glfw.egl.display, window->context.egl.handle);\n        window->context.egl.handle = EGL_NO_CONTEXT;\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Initialize EGL\n//\nbool _glfwInitEGL(void)\n{\n    int i;\n    EGLint* attribs = NULL;\n    const char* extensions;\n    const char* sonames[] =\n    {\n#if defined(_GLFW_EGL_LIBRARY)\n        _GLFW_EGL_LIBRARY,\n#elif defined(_GLFW_WIN32)\n        \"libEGL.dll\",\n        \"EGL.dll\",\n#elif defined(_GLFW_COCOA)\n        \"libEGL.dylib\",\n#elif defined(__CYGWIN__)\n        \"libEGL-1.so\",\n#else\n        \"libEGL.so.1\",\n#endif\n        NULL\n    };\n\n    if (_glfw.egl.handle)\n        return true;\n\n    for (i = 0;  sonames[i];  i++)\n    {\n        _glfw.egl.handle = _glfw_dlopen(sonames[i]);\n        if (_glfw.egl.handle)\n            break;\n    }\n\n    if (!_glfw.egl.handle)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE, \"EGL: Library not found\");\n        return false;\n    }\n\n    _glfw.egl.prefix = (strncmp(sonames[i], \"lib\", 3) == 0);\n\n    glfw_dlsym(_glfw.egl.GetConfigAttrib, _glfw.egl.handle, \"eglGetConfigAttrib\");\n    glfw_dlsym(_glfw.egl.GetConfigs, _glfw.egl.handle, \"eglGetConfigs\");\n    glfw_dlsym(_glfw.egl.ChooseConfig, _glfw.egl.handle, \"eglChooseConfig\");\n    glfw_dlsym(_glfw.egl.GetDisplay, _glfw.egl.handle, \"eglGetDisplay\");\n    glfw_dlsym(_glfw.egl.GetError, _glfw.egl.handle, \"eglGetError\");\n    glfw_dlsym(_glfw.egl.Initialize, _glfw.egl.handle, \"eglInitialize\");\n    glfw_dlsym(_glfw.egl.Terminate, _glfw.egl.handle, \"eglTerminate\");\n    glfw_dlsym(_glfw.egl.BindAPI, _glfw.egl.handle, \"eglBindAPI\");\n    glfw_dlsym(_glfw.egl.CreateContext, _glfw.egl.handle, \"eglCreateContext\");\n    glfw_dlsym(_glfw.egl.DestroySurface, _glfw.egl.handle, \"eglDestroySurface\");\n    glfw_dlsym(_glfw.egl.DestroyContext, _glfw.egl.handle, \"eglDestroyContext\");\n    glfw_dlsym(_glfw.egl.CreateWindowSurface, _glfw.egl.handle, \"eglCreateWindowSurface\");\n    glfw_dlsym(_glfw.egl.MakeCurrent, _glfw.egl.handle, \"eglMakeCurrent\");\n    glfw_dlsym(_glfw.egl.SwapBuffers, _glfw.egl.handle, \"eglSwapBuffers\");\n    glfw_dlsym(_glfw.egl.SwapInterval, _glfw.egl.handle, \"eglSwapInterval\");\n    glfw_dlsym(_glfw.egl.QueryString, _glfw.egl.handle, \"eglQueryString\");\n    glfw_dlsym(_glfw.egl.QuerySurface, _glfw.egl.handle, \"eglQuerySurface\");\n    glfw_dlsym(_glfw.egl.GetProcAddress, _glfw.egl.handle, \"eglGetProcAddress\");\n\n    if (!_glfw.egl.GetConfigAttrib ||\n        !_glfw.egl.GetConfigs ||\n        !_glfw.egl.ChooseConfig ||\n        !_glfw.egl.GetDisplay ||\n        !_glfw.egl.GetError ||\n        !_glfw.egl.Initialize ||\n        !_glfw.egl.Terminate ||\n        !_glfw.egl.BindAPI ||\n        !_glfw.egl.CreateContext ||\n        !_glfw.egl.DestroySurface ||\n        !_glfw.egl.DestroyContext ||\n        !_glfw.egl.CreateWindowSurface ||\n        !_glfw.egl.MakeCurrent ||\n        !_glfw.egl.SwapBuffers ||\n        !_glfw.egl.SwapInterval ||\n        !_glfw.egl.QueryString ||\n        !_glfw.egl.GetProcAddress)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"EGL: Failed to load required entry points\");\n\n        _glfwTerminateEGL();\n        return false;\n    }\n\n    extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);\n    if (extensions && eglGetError() == EGL_SUCCESS)\n        _glfw.egl.EXT_client_extensions = true;\n\n    if (_glfw.egl.EXT_client_extensions)\n    {\n        _glfw.egl.EXT_platform_base =\n            _glfwStringInExtensionString(\"EGL_EXT_platform_base\", extensions);\n        _glfw.egl.EXT_platform_x11 =\n            _glfwStringInExtensionString(\"EGL_EXT_platform_x11\", extensions);\n        _glfw.egl.EXT_platform_wayland =\n            _glfwStringInExtensionString(\"EGL_EXT_platform_wayland\", extensions);\n        _glfw.egl.ANGLE_platform_angle =\n            _glfwStringInExtensionString(\"EGL_ANGLE_platform_angle\", extensions);\n        _glfw.egl.ANGLE_platform_angle_opengl =\n            _glfwStringInExtensionString(\"EGL_ANGLE_platform_angle_opengl\", extensions);\n        _glfw.egl.ANGLE_platform_angle_d3d =\n            _glfwStringInExtensionString(\"EGL_ANGLE_platform_angle_d3d\", extensions);\n        _glfw.egl.ANGLE_platform_angle_vulkan =\n            _glfwStringInExtensionString(\"EGL_ANGLE_platform_angle_vulkan\", extensions);\n        _glfw.egl.ANGLE_platform_angle_metal =\n            _glfwStringInExtensionString(\"EGL_ANGLE_platform_angle_metal\", extensions);\n    }\n\n    if (_glfw.egl.EXT_platform_base)\n    {\n        _glfw.egl.GetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)\n            eglGetProcAddress(\"eglGetPlatformDisplayEXT\");\n        _glfw.egl.CreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)\n            eglGetProcAddress(\"eglCreatePlatformWindowSurfaceEXT\");\n    }\n\n    _glfw.egl.platform = _glfwPlatformGetEGLPlatform(&attribs);\n    _glfw.egl.display = EGL_NO_DISPLAY;\n    if (_glfw.egl.platform)\n    {\n        _glfw.egl.display =\n            eglGetPlatformDisplayEXT(_glfw.egl.platform,\n                                     _glfwPlatformGetEGLNativeDisplay(),\n                                     attribs);\n    }\n    free(attribs);\n\n    if (_glfw.egl.display == EGL_NO_DISPLAY)\n        _glfw.egl.display = eglGetDisplay(_glfwPlatformGetEGLNativeDisplay());\n\n\n    EGLint egl_err;\n    if (_glfw.egl.display == EGL_NO_DISPLAY && (egl_err = eglGetError()) != EGL_SUCCESS)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"EGL: Failed to get EGL display: %s\",\n                        getEGLErrorString(egl_err));\n\n        _glfwTerminateEGL();\n        return false;\n    }\n\n    if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor))\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"EGL: Failed to initialize EGL: %s display_present: %d egl_platform_present: %d\",\n                        getEGLErrorString(eglGetError()), _glfw.egl.display != EGL_NO_DISPLAY, _glfw.egl.platform != 0);\n\n        _glfwTerminateEGL();\n        return false;\n    }\n\n    _glfw.egl.KHR_create_context =\n        extensionSupportedEGL(\"EGL_KHR_create_context\");\n    _glfw.egl.KHR_create_context_no_error =\n        extensionSupportedEGL(\"EGL_KHR_create_context_no_error\");\n    _glfw.egl.KHR_gl_colorspace =\n        extensionSupportedEGL(\"EGL_KHR_gl_colorspace\");\n    _glfw.egl.KHR_get_all_proc_addresses =\n        extensionSupportedEGL(\"EGL_KHR_get_all_proc_addresses\");\n    _glfw.egl.KHR_context_flush_control =\n        extensionSupportedEGL(\"EGL_KHR_context_flush_control\");\n    _glfw.egl.EXT_present_opaque =\n        extensionSupportedEGL(\"EGL_EXT_present_opaque\");\n\n    return true;\n}\n\n// Terminate EGL\n//\nvoid _glfwTerminateEGL(void)\n{\n    if (_glfw.egl.display)\n    {\n        eglTerminate(_glfw.egl.display);\n        _glfw.egl.display = EGL_NO_DISPLAY;\n    }\n\n    if (_glfw.egl.handle)\n    {\n        _glfw_dlclose(_glfw.egl.handle);\n        _glfw.egl.handle = NULL;\n    }\n}\n\n#define setAttrib(a, v) \\\n{ \\\n    assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \\\n    attribs[index++] = a; \\\n    attribs[index++] = v; \\\n}\n\n// Create the OpenGL or OpenGL ES context\n//\nbool _glfwCreateContextEGL(_GLFWwindow* window,\n                               const _GLFWctxconfig* ctxconfig,\n                               const _GLFWfbconfig* fbconfig)\n{\n    EGLint attribs[40];\n    EGLConfig config;\n    EGLContext share = NULL;\n    EGLNativeWindowType native;\n    int index = 0;\n\n    if (!_glfw.egl.display)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE, \"EGL: API not available\");\n        return false;\n    }\n\n    if (ctxconfig->share)\n        share = ctxconfig->share->context.egl.handle;\n\n    if (!chooseEGLConfig(ctxconfig, fbconfig, &config))\n    {\n        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,\n                        \"EGL: Failed to find a suitable EGLConfig\");\n        return false;\n    }\n\n    if (ctxconfig->client == GLFW_OPENGL_ES_API)\n    {\n        if (!eglBindAPI(EGL_OPENGL_ES_API))\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"EGL: Failed to bind OpenGL ES: %s\",\n                            getEGLErrorString(eglGetError()));\n            return false;\n        }\n    }\n    else\n    {\n        if (!eglBindAPI(EGL_OPENGL_API))\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"EGL: Failed to bind OpenGL: %s\",\n                            getEGLErrorString(eglGetError()));\n            return false;\n        }\n    }\n\n    if (_glfw.egl.KHR_create_context)\n    {\n        int mask = 0, flags = 0;\n\n        if (ctxconfig->client == GLFW_OPENGL_API)\n        {\n            if (ctxconfig->forward)\n                flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;\n\n            if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)\n                mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;\n            else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)\n                mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;\n        }\n\n        if (ctxconfig->debug)\n            flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;\n\n        if (ctxconfig->robustness)\n        {\n            if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION)\n            {\n                setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,\n                          EGL_NO_RESET_NOTIFICATION_KHR);\n            }\n            else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET)\n            {\n                setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,\n                          EGL_LOSE_CONTEXT_ON_RESET_KHR);\n            }\n\n            flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;\n        }\n\n        if (ctxconfig->major != 1 || ctxconfig->minor != 0)\n        {\n            setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major);\n            setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor);\n        }\n\n        if (ctxconfig->noerror)\n        {\n            if (_glfw.egl.KHR_create_context_no_error)\n                setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, true);\n        }\n\n        if (mask)\n            setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask);\n\n        if (flags)\n            setAttrib(EGL_CONTEXT_FLAGS_KHR, flags);\n    }\n    else\n    {\n        if (ctxconfig->client == GLFW_OPENGL_ES_API)\n            setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major);\n    }\n\n    if (_glfw.egl.KHR_context_flush_control)\n    {\n        if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE)\n        {\n            setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR,\n                      EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR);\n        }\n        else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH)\n        {\n            setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR,\n                      EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR);\n        }\n    }\n\n    setAttrib(EGL_NONE, EGL_NONE);\n\n    window->context.egl.handle = eglCreateContext(_glfw.egl.display,\n                                                  config, share, attribs);\n\n    if (window->context.egl.handle == EGL_NO_CONTEXT)\n    {\n        _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                        \"EGL: Failed to create context: %s\",\n                        getEGLErrorString(eglGetError()));\n        return false;\n    }\n\n    // Set up attributes for surface creation\n    index = 0;\n\n    if (fbconfig->sRGB)\n    {\n        if (_glfw.egl.KHR_gl_colorspace)\n            setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);\n    }\n    // Disabled because it prevents transparency from working on NVIDIA drivers under Wayland\n    // https://github.com/kovidgoyal/kitty/issues/5479\n    // We anyway dont use the alpha bits for anything.\n    /* if (_glfw.egl.EXT_present_opaque) */\n    /*     setAttrib(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent); */\n\n    setAttrib(EGL_NONE, EGL_NONE);\n\n    native = _glfwPlatformGetEGLNativeWindow(window);\n    // HACK: ANGLE does not implement eglCreatePlatformWindowSurfaceEXT\n    //       despite reporting EGL_EXT_platform_base\n    if (_glfw.egl.platform && _glfw.egl.platform != EGL_PLATFORM_ANGLE_ANGLE)\n    {\n        window->context.egl.surface =\n            eglCreatePlatformWindowSurfaceEXT(_glfw.egl.display, config, native, attribs);\n    }\n    else\n    {\n        window->context.egl.surface =\n            eglCreateWindowSurface(_glfw.egl.display, config, native, attribs);\n    }\n\n    if (window->context.egl.surface == EGL_NO_SURFACE)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"EGL: Failed to create window surface: %s\",\n                        getEGLErrorString(eglGetError()));\n        return false;\n    }\n\n    window->context.egl.config = config;\n\n    EGLint a = EGL_MIN_SWAP_INTERVAL;\n    if (!eglGetConfigAttrib(_glfw.egl.display, config, a, &a)) {\n        _glfwInputError(GLFW_VERSION_UNAVAILABLE, \"EGL: could not check for non-blocking buffer swap with error: %s\", getEGLErrorString(eglGetError()));\n    } else {\n        if (a > 0) {\n            _glfwInputError(GLFW_VERSION_UNAVAILABLE, \"EGL: non-blocking swap buffers not available, minimum swap interval is: %d\", a);\n        }\n    }\n\n    // Load the appropriate client library\n    if (!_glfw.egl.KHR_get_all_proc_addresses)\n    {\n        int i;\n        const char** sonames;\n        const char* es1sonames[] =\n        {\n#if defined(_GLFW_GLESV1_LIBRARY)\n            _GLFW_GLESV1_LIBRARY,\n#elif defined(_GLFW_WIN32)\n            \"GLESv1_CM.dll\",\n            \"libGLES_CM.dll\",\n#elif defined(_GLFW_COCOA)\n            \"libGLESv1_CM.dylib\",\n#else\n            \"libGLESv1_CM.so.1\",\n            \"libGLES_CM.so.1\",\n#endif\n            NULL\n        };\n        const char* es2sonames[] =\n        {\n#if defined(_GLFW_GLESV2_LIBRARY)\n            _GLFW_GLESV2_LIBRARY,\n#elif defined(_GLFW_WIN32)\n            \"GLESv2.dll\",\n            \"libGLESv2.dll\",\n#elif defined(_GLFW_COCOA)\n            \"libGLESv2.dylib\",\n#elif defined(__CYGWIN__)\n            \"libGLESv2-2.so\",\n#else\n            \"libGLESv2.so.2\",\n#endif\n            NULL\n        };\n        const char* glsonames[] =\n        {\n#if defined(_GLFW_OPENGL_LIBRARY)\n            _GLFW_OPENGL_LIBRARY,\n#elif defined(_GLFW_WIN32)\n#elif defined(_GLFW_COCOA)\n#else\n            \"libGL.so.1\",\n#endif\n            NULL\n        };\n\n        if (ctxconfig->client == GLFW_OPENGL_ES_API)\n        {\n            if (ctxconfig->major == 1)\n                sonames = es1sonames;\n            else\n                sonames = es2sonames;\n        }\n        else\n            sonames = glsonames;\n\n        for (i = 0;  sonames[i];  i++)\n        {\n            // HACK: Match presence of lib prefix to increase chance of finding\n            //       a matching pair in the jungle that is Win32 EGL/GLES\n            if (_glfw.egl.prefix != (strncmp(sonames[i], \"lib\", 3) == 0))\n                continue;\n\n            window->context.egl.client = _glfw_dlopen(sonames[i]);\n            if (window->context.egl.client)\n                break;\n        }\n\n        if (!window->context.egl.client)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"EGL: Failed to load client library\");\n            return false;\n        }\n    }\n\n    window->context.makeCurrent = makeContextCurrentEGL;\n    window->context.swapBuffers = swapBuffersEGL;\n    window->context.swapInterval = swapIntervalEGL;\n    window->context.extensionSupported = extensionSupportedEGL;\n    window->context.getProcAddress = getProcAddressEGL;\n    window->context.destroy = destroyContextEGL;\n\n    return true;\n}\n\n#undef setAttrib\n\n// Returns the Visual and depth of the chosen EGLConfig\n//\n#if defined(_GLFW_X11)\nbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig UNUSED,\n                              const _GLFWctxconfig* ctxconfig,\n                              const _GLFWfbconfig* fbconfig,\n                              Visual** visual, int* depth)\n{\n    XVisualInfo* result;\n    XVisualInfo desired;\n    EGLConfig native;\n    EGLint visualID = 0, count = 0;\n    const long vimask = VisualScreenMask | VisualIDMask;\n\n    if (!chooseEGLConfig(ctxconfig, fbconfig, &native))\n    {\n        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,\n                        \"EGL: Failed to find a suitable EGLConfig\");\n        return false;\n    }\n\n    eglGetConfigAttrib(_glfw.egl.display, native,\n                       EGL_NATIVE_VISUAL_ID, &visualID);\n\n    desired.screen = _glfw.x11.screen;\n    desired.visualid = visualID;\n\n    result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count);\n    if (!result)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"EGL: Failed to retrieve Visual for EGLConfig\");\n        return false;\n    }\n\n    *visual = result->visual;\n    *depth = result->depth;\n\n    XFree(result);\n    return true;\n}\n#endif // _GLFW_X11\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI EGLDisplay glfwGetEGLDisplay(void)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY);\n    return _glfw.egl.display;\n}\n\nGLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT);\n\n    if (window->context.client == GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);\n        return EGL_NO_CONTEXT;\n    }\n\n    return window->context.egl.handle;\n}\n\nGLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE);\n\n    if (window->context.client == GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);\n        return EGL_NO_SURFACE;\n    }\n\n    return window->context.egl.surface;\n}\n"
  },
  {
    "path": "glfw/egl_context.h",
    "content": "//========================================================================\n// GLFW 3.4 EGL - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#if defined(_GLFW_WIN32)\n #define EGLAPIENTRY __stdcall\n#else\n #define EGLAPIENTRY\n#endif\n\n#define EGL_SUCCESS 0x3000\n#define EGL_NOT_INITIALIZED 0x3001\n#define EGL_BAD_ACCESS 0x3002\n#define EGL_BAD_ALLOC 0x3003\n#define EGL_BAD_ATTRIBUTE 0x3004\n#define EGL_BAD_CONFIG 0x3005\n#define EGL_BAD_CONTEXT 0x3006\n#define EGL_BAD_CURRENT_SURFACE 0x3007\n#define EGL_BAD_DISPLAY 0x3008\n#define EGL_BAD_MATCH 0x3009\n#define EGL_BAD_NATIVE_PIXMAP 0x300a\n#define EGL_BAD_NATIVE_WINDOW 0x300b\n#define EGL_BAD_PARAMETER 0x300c\n#define EGL_BAD_SURFACE 0x300d\n#define EGL_CONTEXT_LOST 0x300e\n#define EGL_COLOR_BUFFER_TYPE 0x303f\n#define EGL_RGB_BUFFER 0x308e\n#define EGL_SURFACE_TYPE 0x3033\n#define EGL_WINDOW_BIT 0x0004\n#define EGL_RENDERABLE_TYPE 0x3040\n#define EGL_OPENGL_ES_BIT 0x0001\n#define EGL_OPENGL_ES2_BIT 0x0004\n#define EGL_OPENGL_BIT 0x0008\n#define EGL_ALPHA_SIZE 0x3021\n#define EGL_BLUE_SIZE 0x3022\n#define EGL_GREEN_SIZE 0x3023\n#define EGL_RED_SIZE 0x3024\n#define EGL_DEPTH_SIZE 0x3025\n#define EGL_STENCIL_SIZE 0x3026\n#define EGL_SAMPLES 0x3031\n#define EGL_OPENGL_ES_API 0x30a0\n#define EGL_OPENGL_API 0x30a2\n#define EGL_NONE 0x3038\n#define EGL_EXTENSIONS 0x3055\n#define EGL_CONTEXT_CLIENT_VERSION 0x3098\n#define EGL_NATIVE_VISUAL_ID 0x302e\n#define EGL_NO_SURFACE ((EGLSurface) 0)\n#define EGL_NO_DISPLAY ((EGLDisplay) 0)\n#define EGL_NO_CONTEXT ((EGLContext) 0)\n#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0)\n#define EGL_MIN_SWAP_INTERVAL             0x303B\n#define EGL_MAX_SWAP_INTERVAL             0x303C\n\n#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002\n#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001\n#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002\n#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001\n#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd\n#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be\n#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf\n#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004\n#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098\n#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb\n#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd\n#define EGL_CONTEXT_FLAGS_KHR 0x30fc\n#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3\n#define EGL_GL_COLORSPACE_KHR 0x309d\n#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089\n#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097\n#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0\n#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098\n#define EGL_PLATFORM_X11_EXT 0x31d5\n#define EGL_PLATFORM_WAYLAND_EXT 0x31d8\n#define EGL_PLATFORM_ANGLE_ANGLE 0x3202\n#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203\n#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320d\n#define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320e\n#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207\n#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208\n#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450\n#define EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE 0x3489\n#define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348f\n\ntypedef int EGLint;\ntypedef unsigned int EGLBoolean;\ntypedef unsigned int EGLenum;\ntypedef void* EGLConfig;\ntypedef void* EGLContext;\ntypedef void* EGLDisplay;\ntypedef void* EGLSurface;\n\ntypedef void* EGLNativeDisplayType;\ntypedef void* EGLNativeWindowType;\n\n// EGL function pointer typedefs\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglChooseConfig)(EGLDisplay,EGLint const*,EGLConfig*,EGLint,EGLint*);\ntypedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType);\ntypedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum);\ntypedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext);\ntypedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface);\ntypedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint);\ntypedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint);\ntypedef const char* (EGLAPIENTRY * PFN_eglQuerySurface)(EGLDisplay,EGLSurface,EGLint,EGLint*);\ntypedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*);\n#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib\n#define eglGetConfigs _glfw.egl.GetConfigs\n#define eglChooseConfig _glfw.egl.ChooseConfig\n#define eglGetDisplay _glfw.egl.GetDisplay\n#define eglGetError _glfw.egl.GetError\n#define eglInitialize _glfw.egl.Initialize\n#define eglTerminate _glfw.egl.Terminate\n#define eglBindAPI _glfw.egl.BindAPI\n#define eglCreateContext _glfw.egl.CreateContext\n#define eglDestroySurface _glfw.egl.DestroySurface\n#define eglDestroyContext _glfw.egl.DestroyContext\n#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface\n#define eglMakeCurrent _glfw.egl.MakeCurrent\n#define eglSwapBuffers _glfw.egl.SwapBuffers\n#define eglSwapInterval _glfw.egl.SwapInterval\n#define eglQueryString _glfw.egl.QueryString\n#define eglQuerySurface _glfw.egl.QuerySurface\n#define eglGetProcAddress _glfw.egl.GetProcAddress\n\ntypedef EGLDisplay (EGLAPIENTRY * PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum,void*,const EGLint*);\ntypedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay,EGLConfig,void*,const EGLint*);\n#define eglGetPlatformDisplayEXT _glfw.egl.GetPlatformDisplayEXT\n#define eglCreatePlatformWindowSurfaceEXT _glfw.egl.CreatePlatformWindowSurfaceEXT\n\n// EGL-specific per-context data\n//\ntypedef struct _GLFWcontextEGL\n{\n    EGLConfig        config;\n    EGLContext       handle;\n    EGLSurface       surface;\n\n    void*            client;\n\n} _GLFWcontextEGL;\n\n// EGL-specific global data\n//\ntypedef struct _GLFWlibraryEGL\n{\n    EGLenum         platform;\n    EGLDisplay      display;\n    EGLint          major, minor;\n    bool            prefix;\n\n    bool            KHR_create_context;\n    bool            KHR_create_context_no_error;\n    bool            KHR_gl_colorspace;\n    bool            KHR_get_all_proc_addresses;\n    bool            KHR_context_flush_control;\n    bool            EXT_client_extensions;\n    bool            EXT_platform_base;\n    bool            EXT_platform_x11;\n    bool            EXT_platform_wayland;\n    bool            EXT_present_opaque;\n    bool            ANGLE_platform_angle;\n    bool            ANGLE_platform_angle_opengl;\n    bool            ANGLE_platform_angle_d3d;\n    bool            ANGLE_platform_angle_vulkan;\n    bool            ANGLE_platform_angle_metal;\n\n    void*           handle;\n\n    PFN_eglGetConfigAttrib      GetConfigAttrib;\n    PFN_eglGetConfigs           GetConfigs;\n    PFN_eglChooseConfig         ChooseConfig;\n    PFN_eglGetDisplay           GetDisplay;\n    PFN_eglGetError             GetError;\n    PFN_eglInitialize           Initialize;\n    PFN_eglTerminate            Terminate;\n    PFN_eglBindAPI              BindAPI;\n    PFN_eglCreateContext        CreateContext;\n    PFN_eglDestroySurface       DestroySurface;\n    PFN_eglDestroyContext       DestroyContext;\n    PFN_eglCreateWindowSurface  CreateWindowSurface;\n    PFN_eglMakeCurrent          MakeCurrent;\n    PFN_eglSwapBuffers          SwapBuffers;\n    PFN_eglSwapInterval         SwapInterval;\n    PFN_eglQueryString          QueryString;\n    PFN_eglQuerySurface         QuerySurface;\n    PFN_eglGetProcAddress       GetProcAddress;\n\n    PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT;\n    PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC CreatePlatformWindowSurfaceEXT;\n\n} _GLFWlibraryEGL;\n\n\nbool _glfwInitEGL(void);\nvoid _glfwTerminateEGL(void);\nbool _glfwCreateContextEGL(_GLFWwindow* window,\n                               const _GLFWctxconfig* ctxconfig,\n                               const _GLFWfbconfig* fbconfig);\n#if defined(_GLFW_X11)\nbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,\n                              const _GLFWctxconfig* ctxconfig,\n                              const _GLFWfbconfig* fbconfig,\n                              Visual** visual, int* depth);\n#endif /*_GLFW_X11*/\n"
  },
  {
    "path": "glfw/glfw.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport os\nimport re\nimport subprocess\nimport sys\nfrom enum import Enum\nfrom typing import Any, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple\n\n_plat = sys.platform.lower()\nis_linux = 'linux' in _plat\nis_openbsd = 'openbsd' in _plat\nbase = os.path.dirname(os.path.abspath(__file__))\n\n\ndef null_func() -> None:\n    return None\n\n\nclass CompileKey(NamedTuple):\n    src: str\n    dest: str\n\n\nclass Command(NamedTuple):\n    desc: str\n    cmd: Sequence[str]\n    is_newer_func: Callable[[], bool]\n    on_success: Callable[[], None] = null_func\n    key: Optional[CompileKey] = None\n    keyfile: Optional[str] = None\n\n\nclass ISA(Enum):\n    X86 = 0x03\n    AMD64 = 0x3e\n    ARM64 = 0xb7\n    Other = 0x0\n\n\nclass BinaryArch(NamedTuple):\n    bits: int = 64\n    isa: ISA = ISA.AMD64\n\n\nclass CompilerType(Enum):\n    gcc = 'gcc'\n    clang = 'clang'\n    unknown = 'unknown'\n\n\nclass Env:\n\n    cc: List[str] = []\n    cppflags: List[str] = []\n    cflags: List[str] = []\n    ldflags: List[str] = []\n    library_paths: Dict[str, List[str]] = {}\n    ldpaths: List[str] = []\n    ccver: Tuple[int, int]\n    vcs_rev: str = ''\n    binary_arch: BinaryArch = BinaryArch()\n    native_optimizations: bool = False\n    primary_version: int = 0\n    secondary_version: int = 0\n    xt_version: str = ''\n    has_copy_file_range: bool = False\n\n    # glfw stuff\n    all_headers: List[str] = []\n    sources: List[str] = []\n    wayland_packagedir: str = ''\n    wayland_scanner: str = ''\n    wayland_scanner_code: str = ''\n    wayland_protocols: Tuple[str, ...] = ()\n\n    def __init__(\n        self, cc: List[str] = [], cppflags: List[str] = [], cflags: List[str] = [], ldflags: List[str] = [],\n        library_paths: Dict[str, List[str]] = {}, ldpaths: Optional[List[str]] = None, ccver: Tuple[int, int] = (0, 0),\n        vcs_rev: str = '', binary_arch: BinaryArch = BinaryArch(),\n        native_optimizations: bool = False,\n    ):\n        self.cc, self.cppflags, self.cflags, self.ldflags, self.library_paths = cc, cppflags, cflags, ldflags, library_paths\n        self.ldpaths = ldpaths or []\n        self.ccver = ccver\n        self.vcs_rev = vcs_rev\n        self.binary_arch = binary_arch\n        self.native_optimizations = native_optimizations\n        self._cc_version_string = ''\n        self._compiler_type: Optional[CompilerType] = None\n\n    @property\n    def cc_version_string(self) -> str:\n        if not self._cc_version_string:\n            self._cc_version_string = subprocess.check_output(self.cc + ['--version']).decode()\n        return self._cc_version_string\n\n    @property\n    def compiler_type(self) -> CompilerType:\n        if self._compiler_type is None:\n            raw = self.cc_version_string\n            if 'Free Software Foundation' in raw:\n                self._compiler_type = CompilerType.gcc\n            elif 'clang' in raw.lower().split():\n                self._compiler_type = CompilerType.clang\n            else:\n                self._compiler_type = CompilerType.unknown\n        return self._compiler_type\n\n    def copy(self) -> 'Env':\n        ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), dict(self.library_paths), list(self.ldpaths), self.ccver)\n        ans.all_headers = list(self.all_headers)\n        ans._cc_version_string = self._cc_version_string\n        ans.sources = list(self.sources)\n        ans.wayland_packagedir = self.wayland_packagedir\n        ans.wayland_scanner = self.wayland_scanner\n        ans.wayland_scanner_code = self.wayland_scanner_code\n        ans.wayland_protocols = self.wayland_protocols\n        ans.vcs_rev = self.vcs_rev\n        ans.binary_arch = self.binary_arch\n        ans.native_optimizations = self.native_optimizations\n        ans.primary_version = self.primary_version\n        ans.secondary_version = self.secondary_version\n        ans.xt_version = self.xt_version\n        ans.has_copy_file_range = self.has_copy_file_range\n        return ans\n\n\ndef wayland_protocol_file_name(base: str, ext: str = 'c') -> str:\n    base = os.path.basename(base).rpartition('.')[0]\n    return f'wayland-{base}-client-protocol.{ext}'\n\n\ndef init_env(\n    env: Env,\n    pkg_config: Callable[..., List[str]],\n    pkg_version: Callable[[str], Tuple[int, int]],\n    at_least_version: Callable[..., None],\n    test_compile: Callable[..., Any],\n    module: str = 'x11'\n) -> Env:\n    ans = env.copy()\n    ans.cflags.append('-fPIC')\n    ans.cppflags.append(f'-D_GLFW_{module.upper()}')\n    ans.cppflags.append('-D_GLFW_BUILD_DLL')\n\n    with open(os.path.join(base, 'source-info.json')) as f:\n        sinfo = json.load(f)\n    module_sources = list(sinfo[module]['sources'])\n    if module in ('x11', 'wayland'):\n        remove = 'null_joystick.c' if is_linux else 'linux_joystick.c'\n        module_sources.remove(remove)\n\n    ans.sources = sinfo['common']['sources'] + module_sources\n    ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')]\n\n    if module in ('x11', 'wayland'):\n        ans.cflags.append('-pthread')\n        ans.ldpaths.extend('-pthread -lm'.split())\n        if not is_openbsd:\n            ans.ldpaths.extend('-lrt -ldl'.split())\n        major, minor = pkg_version('xkbcommon')\n        if (major, minor) < (0, 5):\n            raise SystemExit('libxkbcommon >= 0.5 required')\n        if major < 1:\n            ans.cflags.append('-DXKB_HAS_NO_UTF32')\n\n    if module == 'x11':\n        for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11 x11-xcb dbus-1'.split():\n            ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))\n            ans.ldpaths.extend(pkg_config(dep, '--libs'))\n\n    elif module == 'cocoa':\n        ans.cppflags.append('-DGL_SILENCE_DEPRECATION')\n        for f_ in 'Cocoa IOKit CoreFoundation CoreVideo UniformTypeIdentifiers'.split():\n            ans.ldpaths.extend(('-framework', f_))\n\n    elif module == 'wayland':\n        at_least_version('wayland-protocols', *sinfo['wayland_protocols'])\n        ans.wayland_packagedir = os.path.abspath(pkg_config('wayland-protocols', '--variable=pkgdatadir')[0])\n        ans.wayland_scanner = os.path.abspath(pkg_config('wayland-scanner', '--variable=wayland_scanner')[0])\n        scanner_version = tuple(map(int, pkg_config('wayland-scanner', '--modversion')[0].strip().split('.')))\n        ans.wayland_scanner_code = 'private-code' if scanner_version >= (1, 14, 91) else 'code'\n        ans.wayland_protocols = tuple(sinfo[module]['protocols'])\n        for p in ans.wayland_protocols:\n            ans.sources.append(wayland_protocol_file_name(p))\n            ans.all_headers.append(wayland_protocol_file_name(p, 'h'))\n        for dep in 'wayland-client wayland-cursor xkbcommon dbus-1'.split():\n            ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))\n            ans.ldpaths.extend(pkg_config(dep, '--libs'))\n        has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE\n    #include <unistd.h>\n    #include <sys/syscall.h>\n    int main(void) {\n        return syscall(__NR_memfd_create, \"test\", 0);\n    }''')\n        if has_memfd_create:\n            ans.cppflags.append('-DHAS_MEMFD_CREATE')\n\n    return ans\n\n\ndef build_wayland_protocols(\n    env: Env,\n    parallel_run: Callable[[List[Command]], None],\n    emphasis: Callable[[str], str],\n    newer: Callable[..., bool],\n    dest_dir: str\n) -> None:\n    items = []\n    for protocol in env.wayland_protocols:\n        if '/' in protocol:\n            src = os.path.join(env.wayland_packagedir, protocol)\n            if not os.path.exists(src):\n                raise SystemExit(f'The wayland-protocols package on your system is missing the {protocol} protocol definition file')\n        else:\n            src = os.path.join(os.path.dirname(os.path.abspath(__file__)), protocol)\n            if not os.path.exists(src):\n                raise SystemExit(f'The local Wayland protocol {protocol} is missing from kitty sources')\n        for ext in 'hc':\n            dest = wayland_protocol_file_name(src, ext)\n            dest = os.path.join(dest_dir, dest)\n            if newer(dest, src):\n                q = 'client-header' if ext == 'h' else env.wayland_scanner_code\n                items.append(Command(\n                    f'Generating {emphasis(os.path.basename(dest))} ...',\n                    [env.wayland_scanner, q, src, dest], lambda: True))\n    if items:\n        parallel_run(items)\n\n\nclass Arg:\n\n    def __init__(self, decl: str):\n        self.type, self.name = decl.rsplit(' ', 1)\n        self.type = self.type.strip()\n        self.name = self.name.strip()\n        while self.name.startswith('*'):\n            self.name = self.name[1:]\n            self.type = self.type + '*'\n        if '[' in self.name:\n            self.type += '[' + self.name.partition('[')[-1]\n\n    def __repr__(self) -> str:\n        return f'Arg({self.type}, {self.name})'\n\n\nclass Function:\n\n    def __init__(self, declaration: str, check_fail: bool = True):\n        self.check_fail = check_fail\n        m = re.match(\n            r'(.+?)\\s+(glfw[A-Z][a-zA-Z0-9]+)[(](.+)[)]$', declaration\n        )\n        if m is None:\n            raise SystemExit('Failed to parse ' + repr(declaration))\n        self.restype = m.group(1).strip()\n        self.name = m.group(2)\n        args = m.group(3).strip().split(',')\n        args = [x.strip() for x in args]\n        self.args = []\n        for a in args:\n            if a == 'void':\n                continue\n            self.args.append(Arg(a))\n        if not self.args:\n            self.args = [Arg('void v')]\n\n    def declaration(self) -> str:\n        return 'typedef {restype} (*{name}_func)({args});\\nGFW_EXTERN {name}_func {name}_impl;\\n#define {name} {name}_impl'.format(\n            restype=self.restype,\n            name=self.name,\n            args=', '.join(a.type for a in self.args)\n        )\n\n    def load(self) -> str:\n        ans = f'*(void **) (&{self.name}_impl) = dlsym(handle, \"{self.name}\");'\n        ans += f'\\n    if ({self.name}_impl == NULL) '\n        if self.check_fail:\n            ans += f'fail(\"Failed to load glfw function {self.name} with error: %s\", dlerror());'\n        else:\n            ans += 'dlerror(); // clear error indicator'\n        return ans\n\n\ndef generate_wrappers(glfw_header: str) -> None:\n    with open(glfw_header) as f:\n        src = f.read()\n    functions = []\n    first = None\n    for m in re.finditer(r'^GLFWAPI\\s+(.+[)]);\\s*$', src, flags=re.MULTILINE):\n        if first is None:\n            first = m.start()\n        decl = m.group(1)\n        if 'VkInstance' in decl:\n            continue\n        functions.append(Function(decl))\n    for line in '''\\\n    void* glfwGetCocoaWindow(GLFWwindow* window)\n    void* glfwGetNSGLContext(GLFWwindow *window)\n    uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor)\n    GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback)\n    GLFWhandleurlopen glfwSetCocoaURLOpenCallback(GLFWhandleurlopen callback)\n    GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWwindow *window, GLFWcocoatogglefullscreenfun callback)\n    GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback)\n    GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback)\n    uint32_t glfwGetCocoaKeyEquivalent(uint32_t glfw_key, int glfw_mods, int* cocoa_mods)\n    void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback)\n    bool glfwCocoaRecreateGLDrawable(GLFWwindow *w)\n    GLFWcocoarenderframefun glfwCocoaSetWindowResizeCallback(GLFWwindow *w, GLFWcocoarenderframefun callback)\n    void* glfwGetX11Display(void)\n    unsigned long glfwGetX11Window(GLFWwindow* window)\n    void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)\n    void glfwCocoaCycleThroughOSWindows(bool backwards)\n    void glfwCocoaSetWindowChrome(GLFWwindow* window, unsigned int color, bool use_system_color, unsigned int system_color,\\\n    int background_blur, unsigned int hide_window_decorations, bool show_text_in_titlebar, int color_space, float background_opacity, bool resizable)\n    void glfwCocoaRegisterMIMETypes(GLFWwindow *window, const char **mimes, size_t count)\n    const char* glfwGetPrimarySelectionString(GLFWwindow* window, void)\n    int glfwGetNativeKeyForName(const char* key_name, int case_sensitive)\n    void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback)\n    void glfwWaylandActivateWindow(GLFWwindow *handle, const char *activation_token)\n    const char* glfwWaylandMissingCapabilities(void)\n    void glfwWaylandRunWithActivationToken(GLFWwindow *handle, GLFWactivationcallback cb, void *cb_data)\n    bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color)\n    void glfwWaylandSetTitlebarHidden(GLFWwindow *handle, bool hidden)\n    void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle)\n    bool glfwWaylandIsWindowFullyCreated(GLFWwindow *handle)\n    bool glfwWaylandBeep(GLFWwindow *handle)\n    pid_t glfwWaylandCompositorPID(void)\n    void glfwConfigureMomentumScroller(double friction, double min_velocity, double max_velocity, unsigned timer_interval)\n    unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data)\n    void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler)\n    int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)\n'''.splitlines():\n        if line:\n            functions.append(Function(line.strip(), check_fail=False))\n\n    declarations = [f.declaration() for f in functions]\n    p = src.find(' * GLFW API tokens')\n    p = src.find('*/', p)\n    preamble = src[p + 2:first]\n    header = '''\\\n//\n// THIS FILE IS GENERATED BY glfw.py\n//\n// SAVE YOURSELF SOME TIME, DO NOT MANUALLY EDIT\n//\n\n#pragma once\n#include <stddef.h>\n#include <stdint.h>\n#include \"monotonic.h\"\n\n#ifndef GFW_EXTERN\n#define GFW_EXTERN extern\n#endif\n{}\n\ntypedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);\ntypedef bool (* GLFWapplicationshouldhandlereopenfun)(int);\ntypedef bool (* GLFWhandleurlopen)(const char*);\ntypedef void (* GLFWapplicationwillfinishlaunchingfun)(bool);\ntypedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);\ntypedef void (* GLFWcocoarenderframefun)(GLFWwindow*);\ntypedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);\ntypedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);\ntypedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, int, const char*);\n{}\n\nconst char* load_glfw(const char* path);\n'''.format(preamble, '\\n\\n'.join(declarations))\n    with open('../kitty/glfw-wrapper.h', 'w') as f:\n        f.write(header)\n\n    code = '''\n// generated by glfw.py DO NOT edit\n\n#define GFW_EXTERN\n#include \"data-types.h\"\n#include \"glfw-wrapper.h\"\n#include <dlfcn.h>\n\nstatic void* handle = NULL;\n\n#define fail(msg, ...) { snprintf(buf, sizeof(buf), msg, __VA_ARGS__); return buf; }\n\nconst char*\nload_glfw(const char* path) {\n    static char buf[2048];\n    handle = dlopen(path, RTLD_LAZY);\n    if (handle == NULL) fail(\"Failed to dlopen %s with error: %s\", path, dlerror());\n    dlerror();\n\n    LOAD\n\n    return NULL;\n}\n\nvoid\nunload_glfw(void) {\n    if (handle) { dlclose(handle); handle = NULL; }\n}\n'''.replace('LOAD', '\\n\\n    '.join(f.load() for f in functions))\n    with open('../kitty/glfw-wrapper.c', 'w') as f:\n        f.write(code)\n\n\ndef main() -> None:\n    os.chdir(os.path.dirname(os.path.abspath(__file__)))\n    generate_wrappers('glfw3.h')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "glfw/glfw3.h",
    "content": "/*************************************************************************\n * GLFW 3.4 - www.glfw.org\n * A library for OpenGL, window and input\n *------------------------------------------------------------------------\n * Copyright (c) 2002-2006 Marcus Geelnard\n * Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n *\n * This software is provided 'as-is', without any express or implied\n * warranty. In no event will the authors be held liable for any damages\n * arising from the use of this software.\n *\n * Permission is granted to anyone to use this software for any purpose,\n * including commercial applications, and to alter it and redistribute it\n * freely, subject to the following restrictions:\n *\n * 1. The origin of this software must not be misrepresented; you must not\n *    claim that you wrote the original software. If you use this software\n *    in a product, an acknowledgment in the product documentation would\n *    be appreciated but is not required.\n *\n * 2. Altered source versions must be plainly marked as such, and must not\n *    be misrepresented as being the original software.\n *\n * 3. This notice may not be removed or altered from any source\n *    distribution.\n *\n *************************************************************************/\n\n#ifndef _glfw3_h_\n#define _glfw3_h_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\n/*************************************************************************\n * Doxygen documentation\n *************************************************************************/\n\n/*! @file glfw3.h\n *  @brief The header of the GLFW 3 API.\n *\n *  This is the header file of the GLFW 3 API.  It defines all its types and\n *  declares all its functions.\n *\n *  For more information about how to use this file, see @ref build_include.\n */\n/*! @defgroup context Context reference\n *  @brief Functions and types related to OpenGL and OpenGL ES contexts.\n *\n *  This is the reference documentation for OpenGL and OpenGL ES context related\n *  functions.  For more task-oriented information, see the @ref context_guide.\n */\n/*! @defgroup vulkan Vulkan reference\n *  @brief Functions and types related to Vulkan.\n *\n *  This is the reference documentation for Vulkan related functions and types.\n *  For more task-oriented information, see the @ref vulkan_guide.\n */\n/*! @defgroup init Initialization, version and error reference\n *  @brief Functions and types related to initialization and error handling.\n *\n *  This is the reference documentation for initialization and termination of\n *  the library, version management and error handling.  For more task-oriented\n *  information, see the @ref intro_guide.\n */\n/*! @defgroup input Input reference\n *  @brief Functions and types related to input handling.\n *\n *  This is the reference documentation for input related functions and types.\n *  For more task-oriented information, see the @ref input_guide.\n */\n/*! @defgroup monitor Monitor reference\n *  @brief Functions and types related to monitors.\n *\n *  This is the reference documentation for monitor related functions and types.\n *  For more task-oriented information, see the @ref monitor_guide.\n */\n/*! @defgroup window Window reference\n *  @brief Functions and types related to windows.\n *\n *  This is the reference documentation for window related functions and types,\n *  including creation, deletion and event polling.  For more task-oriented\n *  information, see the @ref window_guide.\n */\n\n\n/*************************************************************************\n * Compiler- and platform-specific preprocessor work\n *************************************************************************/\n\n/* If we are we on Windows, we want a single define for it.\n */\n#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__))\n #define _WIN32\n#endif /* _WIN32 */\n\n/* Include because most Windows GLU headers need wchar_t and\n * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h.\n * Include it unconditionally to avoid surprising side-effects.\n */\n#include <stddef.h>\n\n/* Include because it is needed by Vulkan and related functions.\n * Include it unconditionally to avoid surprising side-effects.\n */\n#include <stdint.h>\n\n#include <stdbool.h>\n\n/* Include for ssize_t on POSIX systems. On Windows we define it if needed. */\n#if !defined(_WIN32)\n  #include <sys/types.h>\n#else\n  #if !defined(SSIZE_T_DEFINED)\n    typedef intptr_t ssize_t;\n    #define SSIZE_T_DEFINED\n  #endif\n#endif\n\n#if defined(GLFW_INCLUDE_VULKAN)\n  #include <vulkan/vulkan.h>\n#endif /* Vulkan header */\n\n/* The Vulkan header may have indirectly included windows.h (because of\n * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it.\n */\n\n/* It is customary to use APIENTRY for OpenGL function pointer declarations on\n * all platforms.  Additionally, the Windows OpenGL header needs APIENTRY.\n */\n#if !defined(APIENTRY)\n #if defined(_WIN32)\n  #define APIENTRY __stdcall\n #else\n  #define APIENTRY\n #endif\n #define GLFW_APIENTRY_DEFINED\n#endif /* APIENTRY */\n\n/* Some Windows OpenGL headers need this.\n */\n#if !defined(WINGDIAPI) && defined(_WIN32)\n #define WINGDIAPI __declspec(dllimport)\n #define GLFW_WINGDIAPI_DEFINED\n#endif /* WINGDIAPI */\n\n/* Some Windows GLU headers need this.\n */\n#if !defined(CALLBACK) && defined(_WIN32)\n #define CALLBACK __stdcall\n #define GLFW_CALLBACK_DEFINED\n#endif /* CALLBACK */\n\n/* Include the chosen OpenGL or OpenGL ES headers.\n */\n#if defined(GLFW_INCLUDE_ES1)\n\n #include <GLES/gl.h>\n #if defined(GLFW_INCLUDE_GLEXT)\n  #include <GLES/glext.h>\n #endif\n\n#elif defined(GLFW_INCLUDE_ES2)\n\n #include <GLES2/gl2.h>\n #if defined(GLFW_INCLUDE_GLEXT)\n  #include <GLES2/gl2ext.h>\n #endif\n\n#elif defined(GLFW_INCLUDE_ES3)\n\n #include <GLES3/gl3.h>\n #if defined(GLFW_INCLUDE_GLEXT)\n  #include <GLES2/gl2ext.h>\n #endif\n\n#elif defined(GLFW_INCLUDE_ES31)\n\n #include <GLES3/gl31.h>\n #if defined(GLFW_INCLUDE_GLEXT)\n  #include <GLES2/gl2ext.h>\n #endif\n\n#elif defined(GLFW_INCLUDE_ES32)\n\n #include <GLES3/gl32.h>\n #if defined(GLFW_INCLUDE_GLEXT)\n  #include <GLES2/gl2ext.h>\n #endif\n\n#elif defined(GLFW_INCLUDE_GLCOREARB)\n\n #if defined(__APPLE__)\n\n  #include <OpenGL/gl3.h>\n  #if defined(GLFW_INCLUDE_GLEXT)\n   #include <OpenGL/gl3ext.h>\n  #endif /*GLFW_INCLUDE_GLEXT*/\n\n #else /*__APPLE__*/\n\n  #include <GL/glcorearb.h>\n\n #endif /*__APPLE__*/\n\n#elif defined(GLFW_INCLUDE_GLU)\n\n #if defined(__APPLE__)\n\n  #if defined(GLFW_INCLUDE_GLU)\n   #include <OpenGL/glu.h>\n  #endif\n\n #else /*__APPLE__*/\n\n  #if defined(GLFW_INCLUDE_GLU)\n   #include <GL/glu.h>\n  #endif\n\n #endif /*__APPLE__*/\n\n#elif !defined(GLFW_INCLUDE_NONE) && \\\n      !defined(__gl_h_) && \\\n      !defined(__gles1_gl_h_) && \\\n      !defined(__gles2_gl2_h_) && \\\n      !defined(__gles2_gl3_h_) && \\\n      !defined(__gles2_gl31_h_) && \\\n      !defined(__gles2_gl32_h_) && \\\n      !defined(__gl_glcorearb_h_) && \\\n      !defined(__gl2_h_) /*legacy*/ && \\\n      !defined(__gl3_h_) /*legacy*/ && \\\n      !defined(__gl31_h_) /*legacy*/ && \\\n      !defined(__gl32_h_) /*legacy*/ && \\\n      !defined(__glcorearb_h_) /*legacy*/ && \\\n      !defined(__GL_H__) /*non-standard*/ && \\\n      !defined(__gltypes_h_) /*non-standard*/ && \\\n      !defined(__glee_h_) /*non-standard*/\n\n #if defined(__APPLE__)\n\n  #if !defined(GLFW_INCLUDE_GLEXT)\n   #define GL_GLEXT_LEGACY\n  #endif\n  #include <OpenGL/gl.h>\n\n #else /*__APPLE__*/\n\n  #include <GL/gl.h>\n  #if defined(GLFW_INCLUDE_GLEXT)\n   #include <GL/glext.h>\n  #endif\n\n #endif /*__APPLE__*/\n\n#endif /* OpenGL and OpenGL ES headers */\n\n#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL)\n /* GLFW_DLL must be defined by applications that are linking against the DLL\n  * version of the GLFW library.  _GLFW_BUILD_DLL is defined by the GLFW\n  * configuration header when compiling the DLL version of the library.\n  */\n #error \"You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined\"\n#endif\n\n/* GLFWAPI is used to declare public API functions for export\n * from the DLL / shared library / dynamic library.\n */\n#if defined(_WIN32) && defined(_GLFW_BUILD_DLL)\n /* We are building GLFW as a Win32 DLL */\n #define GLFWAPI __declspec(dllexport)\n#elif defined(_WIN32) && defined(GLFW_DLL)\n /* We are calling GLFW as a Win32 DLL */\n #define GLFWAPI __declspec(dllimport)\n#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)\n /* We are building GLFW as a shared / dynamic library */\n #define GLFWAPI __attribute__((visibility(\"default\")))\n#else\n /* We are building or calling GLFW as a static library */\n #define GLFWAPI\n#endif\n\n\n/*************************************************************************\n * GLFW API tokens\n *************************************************************************/\n\n/*! @name GLFW version macros\n *  @{ */\n/*! @brief The major version number of the GLFW library.\n *\n *  This is incremented when the API is changed in non-compatible ways.\n *  @ingroup init\n */\n#define GLFW_VERSION_MAJOR          3\n/*! @brief The minor version number of the GLFW library.\n *\n *  This is incremented when features are added to the API but it remains\n *  backward-compatible.\n *  @ingroup init\n */\n#define GLFW_VERSION_MINOR          4\n/*! @brief The revision number of the GLFW library.\n *\n *  This is incremented when a bug fix release is made that does not contain any\n *  API changes.\n *  @ingroup init\n */\n#define GLFW_VERSION_REVISION       0\n/*! @} */\n\n/*! @defgroup hat_state Joystick hat states\n *  @brief Joystick hat states.\n *\n *  See [joystick hat input](@ref joystick_hat) for how these are used.\n *\n *  @ingroup input\n *  @{ */\n#define GLFW_HAT_CENTERED           0\n#define GLFW_HAT_UP                 1\n#define GLFW_HAT_RIGHT              2\n#define GLFW_HAT_DOWN               4\n#define GLFW_HAT_LEFT               8\n#define GLFW_HAT_RIGHT_UP           (GLFW_HAT_RIGHT | GLFW_HAT_UP)\n#define GLFW_HAT_RIGHT_DOWN         (GLFW_HAT_RIGHT | GLFW_HAT_DOWN)\n#define GLFW_HAT_LEFT_UP            (GLFW_HAT_LEFT  | GLFW_HAT_UP)\n#define GLFW_HAT_LEFT_DOWN          (GLFW_HAT_LEFT  | GLFW_HAT_DOWN)\n/*! @} */\n\n/*! @defgroup keys Keyboard keys\n *  @brief Keyboard key IDs.\n *\n *  See [key input](@ref input_key) for how these are used.\n *\n *  These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60),\n *  but re-arranged to map to 7-bit ASCII for printable keys (function keys are\n *  put in the 256+ range).\n *\n *  The naming of the key codes follow these rules:\n *   - The US keyboard layout is used\n *   - Names of printable alphanumeric characters are used (e.g. \"A\", \"R\",\n *     \"3\", etc.)\n *   - For non-alphanumeric characters, Unicode:ish names are used (e.g.\n *     \"COMMA\", \"LEFT_SQUARE_BRACKET\", etc.). Note that some names do not\n *     correspond to the Unicode standard (usually for brevity)\n *   - Keys that lack a clear US mapping are named \"WORLD_x\"\n *   - For non-printable keys, custom names are used (e.g. \"F4\",\n *     \"BACKSPACE\", etc.)\n *\n *  @ingroup input\n *  @{\n */\n\n/* start functional key names (auto generated by gen-key-constants.py do not edit) */\ntypedef enum {\n  GLFW_FKEY_FIRST = 0xe000u,\n  GLFW_FKEY_ESCAPE = 0xe000u,\n  GLFW_FKEY_ENTER = 0xe001u,\n  GLFW_FKEY_TAB = 0xe002u,\n  GLFW_FKEY_BACKSPACE = 0xe003u,\n  GLFW_FKEY_INSERT = 0xe004u,\n  GLFW_FKEY_DELETE = 0xe005u,\n  GLFW_FKEY_LEFT = 0xe006u,\n  GLFW_FKEY_RIGHT = 0xe007u,\n  GLFW_FKEY_UP = 0xe008u,\n  GLFW_FKEY_DOWN = 0xe009u,\n  GLFW_FKEY_PAGE_UP = 0xe00au,\n  GLFW_FKEY_PAGE_DOWN = 0xe00bu,\n  GLFW_FKEY_HOME = 0xe00cu,\n  GLFW_FKEY_END = 0xe00du,\n  GLFW_FKEY_CAPS_LOCK = 0xe00eu,\n  GLFW_FKEY_SCROLL_LOCK = 0xe00fu,\n  GLFW_FKEY_NUM_LOCK = 0xe010u,\n  GLFW_FKEY_PRINT_SCREEN = 0xe011u,\n  GLFW_FKEY_PAUSE = 0xe012u,\n  GLFW_FKEY_MENU = 0xe013u,\n  GLFW_FKEY_F1 = 0xe014u,\n  GLFW_FKEY_F2 = 0xe015u,\n  GLFW_FKEY_F3 = 0xe016u,\n  GLFW_FKEY_F4 = 0xe017u,\n  GLFW_FKEY_F5 = 0xe018u,\n  GLFW_FKEY_F6 = 0xe019u,\n  GLFW_FKEY_F7 = 0xe01au,\n  GLFW_FKEY_F8 = 0xe01bu,\n  GLFW_FKEY_F9 = 0xe01cu,\n  GLFW_FKEY_F10 = 0xe01du,\n  GLFW_FKEY_F11 = 0xe01eu,\n  GLFW_FKEY_F12 = 0xe01fu,\n  GLFW_FKEY_F13 = 0xe020u,\n  GLFW_FKEY_F14 = 0xe021u,\n  GLFW_FKEY_F15 = 0xe022u,\n  GLFW_FKEY_F16 = 0xe023u,\n  GLFW_FKEY_F17 = 0xe024u,\n  GLFW_FKEY_F18 = 0xe025u,\n  GLFW_FKEY_F19 = 0xe026u,\n  GLFW_FKEY_F20 = 0xe027u,\n  GLFW_FKEY_F21 = 0xe028u,\n  GLFW_FKEY_F22 = 0xe029u,\n  GLFW_FKEY_F23 = 0xe02au,\n  GLFW_FKEY_F24 = 0xe02bu,\n  GLFW_FKEY_F25 = 0xe02cu,\n  GLFW_FKEY_F26 = 0xe02du,\n  GLFW_FKEY_F27 = 0xe02eu,\n  GLFW_FKEY_F28 = 0xe02fu,\n  GLFW_FKEY_F29 = 0xe030u,\n  GLFW_FKEY_F30 = 0xe031u,\n  GLFW_FKEY_F31 = 0xe032u,\n  GLFW_FKEY_F32 = 0xe033u,\n  GLFW_FKEY_F33 = 0xe034u,\n  GLFW_FKEY_F34 = 0xe035u,\n  GLFW_FKEY_F35 = 0xe036u,\n  GLFW_FKEY_KP_0 = 0xe037u,\n  GLFW_FKEY_KP_1 = 0xe038u,\n  GLFW_FKEY_KP_2 = 0xe039u,\n  GLFW_FKEY_KP_3 = 0xe03au,\n  GLFW_FKEY_KP_4 = 0xe03bu,\n  GLFW_FKEY_KP_5 = 0xe03cu,\n  GLFW_FKEY_KP_6 = 0xe03du,\n  GLFW_FKEY_KP_7 = 0xe03eu,\n  GLFW_FKEY_KP_8 = 0xe03fu,\n  GLFW_FKEY_KP_9 = 0xe040u,\n  GLFW_FKEY_KP_DECIMAL = 0xe041u,\n  GLFW_FKEY_KP_DIVIDE = 0xe042u,\n  GLFW_FKEY_KP_MULTIPLY = 0xe043u,\n  GLFW_FKEY_KP_SUBTRACT = 0xe044u,\n  GLFW_FKEY_KP_ADD = 0xe045u,\n  GLFW_FKEY_KP_ENTER = 0xe046u,\n  GLFW_FKEY_KP_EQUAL = 0xe047u,\n  GLFW_FKEY_KP_SEPARATOR = 0xe048u,\n  GLFW_FKEY_KP_LEFT = 0xe049u,\n  GLFW_FKEY_KP_RIGHT = 0xe04au,\n  GLFW_FKEY_KP_UP = 0xe04bu,\n  GLFW_FKEY_KP_DOWN = 0xe04cu,\n  GLFW_FKEY_KP_PAGE_UP = 0xe04du,\n  GLFW_FKEY_KP_PAGE_DOWN = 0xe04eu,\n  GLFW_FKEY_KP_HOME = 0xe04fu,\n  GLFW_FKEY_KP_END = 0xe050u,\n  GLFW_FKEY_KP_INSERT = 0xe051u,\n  GLFW_FKEY_KP_DELETE = 0xe052u,\n  GLFW_FKEY_KP_BEGIN = 0xe053u,\n  GLFW_FKEY_MEDIA_PLAY = 0xe054u,\n  GLFW_FKEY_MEDIA_PAUSE = 0xe055u,\n  GLFW_FKEY_MEDIA_PLAY_PAUSE = 0xe056u,\n  GLFW_FKEY_MEDIA_REVERSE = 0xe057u,\n  GLFW_FKEY_MEDIA_STOP = 0xe058u,\n  GLFW_FKEY_MEDIA_FAST_FORWARD = 0xe059u,\n  GLFW_FKEY_MEDIA_REWIND = 0xe05au,\n  GLFW_FKEY_MEDIA_TRACK_NEXT = 0xe05bu,\n  GLFW_FKEY_MEDIA_TRACK_PREVIOUS = 0xe05cu,\n  GLFW_FKEY_MEDIA_RECORD = 0xe05du,\n  GLFW_FKEY_LOWER_VOLUME = 0xe05eu,\n  GLFW_FKEY_RAISE_VOLUME = 0xe05fu,\n  GLFW_FKEY_MUTE_VOLUME = 0xe060u,\n  GLFW_FKEY_LEFT_SHIFT = 0xe061u,\n  GLFW_FKEY_LEFT_CONTROL = 0xe062u,\n  GLFW_FKEY_LEFT_ALT = 0xe063u,\n  GLFW_FKEY_LEFT_SUPER = 0xe064u,\n  GLFW_FKEY_LEFT_HYPER = 0xe065u,\n  GLFW_FKEY_LEFT_META = 0xe066u,\n  GLFW_FKEY_RIGHT_SHIFT = 0xe067u,\n  GLFW_FKEY_RIGHT_CONTROL = 0xe068u,\n  GLFW_FKEY_RIGHT_ALT = 0xe069u,\n  GLFW_FKEY_RIGHT_SUPER = 0xe06au,\n  GLFW_FKEY_RIGHT_HYPER = 0xe06bu,\n  GLFW_FKEY_RIGHT_META = 0xe06cu,\n  GLFW_FKEY_ISO_LEVEL3_SHIFT = 0xe06du,\n  GLFW_FKEY_ISO_LEVEL5_SHIFT = 0xe06eu,\n  GLFW_FKEY_LAST = 0xe06eu\n} GLFWFunctionKey;\n/* end functional key names */\n\n/*! @} */\n\n/*! @defgroup mods Modifier key flags\n *  @brief Modifier key flags.\n *\n *  See [key input](@ref input_key) for how these are used.\n *\n *  @ingroup input\n *  @{ */\n\n/*! @brief If this bit is set one or more Shift keys were held down.\n *\n *  If this bit is set one or more Shift keys were held down.\n */\n#define GLFW_MOD_SHIFT           0x0001\n/*! @brief If this bit is set one or more Alt keys were held down.\n *\n *  If this bit is set one or more Alt keys were held down.\n */\n#define GLFW_MOD_ALT             0x0002\n/*! @brief If this bit is set one or more Alt keys were held down.\n *\n *  If this bit is set one or more Alt keys were held down.\n */\n#define GLFW_MOD_CONTROL         0x0004\n/*! @brief If this bit is set one or more Super keys were held down.\n *\n *  If this bit is set one or more Super keys were held down.\n */\n#define GLFW_MOD_SUPER           0x0008\n/*! @brief If this bit is set one or more Hyper keys were held down.\n *\n *  If this bit is set one or more Hyper keys were held down.\n */\n#define GLFW_MOD_HYPER           0x0010\n/*! @brief If this bit is set one or more Meta keys were held down.\n *\n *  If this bit is set one or more Meta keys were held down.\n */\n#define GLFW_MOD_META            0x0020\n/*! @brief If this bit is set the Caps Lock key is enabled.\n *\n *  If this bit is set the Caps Lock key is enabled and the @ref\n *  GLFW_LOCK_KEY_MODS input mode is set.\n */\n#define GLFW_MOD_CAPS_LOCK       0x0040\n/*! @brief If this bit is set the Num Lock key is enabled.\n *\n *  If this bit is set the Num Lock key is enabled and the @ref\n *  GLFW_LOCK_KEY_MODS input mode is set.\n */\n#define GLFW_MOD_NUM_LOCK        0x0080\n#define GLFW_MOD_LAST            GLFW_MOD_NUM_LOCK\n#define GLFW_LOCK_MASK           (GLFW_MOD_NUM_LOCK | GLFW_MOD_CAPS_LOCK)\n\n/*! @} */\n\n/*! @defgroup buttons Mouse buttons\n *  @brief Mouse button IDs.\n *\n *  See [mouse button input](@ref input_mouse_button) for how these are used.\n *\n *  @ingroup input\n *  @{ */\ntypedef enum GLFWMouseButton {\n    GLFW_MOUSE_BUTTON_1 = 0,\n    GLFW_MOUSE_BUTTON_LEFT = 0,\n    GLFW_MOUSE_BUTTON_2 = 1,\n    GLFW_MOUSE_BUTTON_RIGHT = 1,\n    GLFW_MOUSE_BUTTON_3 = 2,\n    GLFW_MOUSE_BUTTON_MIDDLE = 2,\n    GLFW_MOUSE_BUTTON_4 = 3,\n    GLFW_MOUSE_BUTTON_5 = 4,\n    GLFW_MOUSE_BUTTON_6 = 5,\n    GLFW_MOUSE_BUTTON_7 = 6,\n    GLFW_MOUSE_BUTTON_8 = 7,\n    GLFW_MOUSE_BUTTON_LAST = 7\n} GLFWMouseButton;\n/*! @} */\n\ntypedef enum GLFWColorScheme {\n    GLFW_COLOR_SCHEME_NO_PREFERENCE = 0,\n    GLFW_COLOR_SCHEME_DARK = 1,\n    GLFW_COLOR_SCHEME_LIGHT = 2\n} GLFWColorScheme;\n\ntypedef enum GLFWMomentumType {\n    GLFW_NO_MOMENTUM_DATA = 0,\n    GLFW_MOMENTUM_PHASE_BEGAN = 1,\n    GLFW_MOMENTUM_PHASE_STATIONARY = 2,\n    GLFW_MOMENTUM_PHASE_ACTIVE = 3,\n    GLFW_MOMENTUM_PHASE_ENDED = 4,\n    GLFW_MOMENTUM_PHASE_CANCELED = 5,\n    GLFW_MOMENTUM_PHASE_MAY_BEGIN = 6,\n} GLFWMomentumType;\n\ntypedef enum GLFWOffsetType {\n    GLFW_SCROLL_OFFSET_LINES = 0,\n    GLFW_SCROLL_OFFEST_V120 = 1,\n    GLFW_SCROLL_OFFEST_HIGHRES = 2,\n} GLFWOffsetType;\n\ntypedef struct GLFWScrollEvent {\n    double x_offset, y_offset;  // offsets are scaled by the window scale for HIGHRES\n    struct { double x, y; } unscaled;  // unscaled offsets, aka logical pixels\n    GLFWMomentumType momentum_type;\n    GLFWOffsetType offset_type;\n    int keyboard_modifiers;\n} GLFWScrollEvent;\n\n/*! @defgroup joysticks Joysticks\n *  @brief Joystick IDs.\n *\n *  See [joystick input](@ref joystick) for how these are used.\n *\n *  @ingroup input\n *  @{ */\n#define GLFW_JOYSTICK_1             0\n#define GLFW_JOYSTICK_2             1\n#define GLFW_JOYSTICK_3             2\n#define GLFW_JOYSTICK_4             3\n#define GLFW_JOYSTICK_5             4\n#define GLFW_JOYSTICK_6             5\n#define GLFW_JOYSTICK_7             6\n#define GLFW_JOYSTICK_8             7\n#define GLFW_JOYSTICK_9             8\n#define GLFW_JOYSTICK_10            9\n#define GLFW_JOYSTICK_11            10\n#define GLFW_JOYSTICK_12            11\n#define GLFW_JOYSTICK_13            12\n#define GLFW_JOYSTICK_14            13\n#define GLFW_JOYSTICK_15            14\n#define GLFW_JOYSTICK_16            15\n#define GLFW_JOYSTICK_LAST          GLFW_JOYSTICK_16\n/*! @} */\n\n/*! @defgroup gamepad_buttons Gamepad buttons\n *  @brief Gamepad buttons.\n *\n *  See @ref gamepad for how these are used.\n *\n *  @ingroup input\n *  @{ */\n#define GLFW_GAMEPAD_BUTTON_A               0\n#define GLFW_GAMEPAD_BUTTON_B               1\n#define GLFW_GAMEPAD_BUTTON_X               2\n#define GLFW_GAMEPAD_BUTTON_Y               3\n#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER     4\n#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER    5\n#define GLFW_GAMEPAD_BUTTON_BACK            6\n#define GLFW_GAMEPAD_BUTTON_START           7\n#define GLFW_GAMEPAD_BUTTON_GUIDE           8\n#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB      9\n#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB     10\n#define GLFW_GAMEPAD_BUTTON_DPAD_UP         11\n#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT      12\n#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN       13\n#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT       14\n#define GLFW_GAMEPAD_BUTTON_LAST            GLFW_GAMEPAD_BUTTON_DPAD_LEFT\n\n#define GLFW_GAMEPAD_BUTTON_CROSS       GLFW_GAMEPAD_BUTTON_A\n#define GLFW_GAMEPAD_BUTTON_CIRCLE      GLFW_GAMEPAD_BUTTON_B\n#define GLFW_GAMEPAD_BUTTON_SQUARE      GLFW_GAMEPAD_BUTTON_X\n#define GLFW_GAMEPAD_BUTTON_TRIANGLE    GLFW_GAMEPAD_BUTTON_Y\n/*! @} */\n\n/*! @defgroup gamepad_axes Gamepad axes\n *  @brief Gamepad axes.\n *\n *  See @ref gamepad for how these are used.\n *\n *  @ingroup input\n *  @{ */\n#define GLFW_GAMEPAD_AXIS_LEFT_X        0\n#define GLFW_GAMEPAD_AXIS_LEFT_Y        1\n#define GLFW_GAMEPAD_AXIS_RIGHT_X       2\n#define GLFW_GAMEPAD_AXIS_RIGHT_Y       3\n#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER  4\n#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5\n#define GLFW_GAMEPAD_AXIS_LAST          GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER\n/*! @} */\n\n/*! @defgroup errors Error codes\n *  @brief Error codes.\n *\n *  See [error handling](@ref error_handling) for how these are used.\n *\n *  @ingroup init\n *  @{ */\n/*! @brief No error has occurred.\n *\n *  No error has occurred.\n *\n *  @analysis Yay.\n */\n#define GLFW_NO_ERROR               0\n/*! @brief GLFW has not been initialized.\n *\n *  This occurs if a GLFW function was called that must not be called unless the\n *  library is [initialized](@ref intro_init).\n *\n *  @analysis Application programmer error.  Initialize GLFW before calling any\n *  function that requires initialization.\n */\n#define GLFW_NOT_INITIALIZED        0x00010001\n/*! @brief No context is current for this thread.\n *\n *  This occurs if a GLFW function was called that needs and operates on the\n *  current OpenGL or OpenGL ES context but no context is current on the calling\n *  thread.  One such function is @ref glfwSwapInterval.\n *\n *  @analysis Application programmer error.  Ensure a context is current before\n *  calling functions that require a current context.\n */\n#define GLFW_NO_CURRENT_CONTEXT     0x00010002\n/*! @brief One of the arguments to the function was an invalid enum value.\n *\n *  One of the arguments to the function was an invalid enum value, for example\n *  requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib.\n *\n *  @analysis Application programmer error.  Fix the offending call.\n */\n#define GLFW_INVALID_ENUM           0x00010003\n/*! @brief One of the arguments to the function was an invalid value.\n *\n *  One of the arguments to the function was an invalid value, for example\n *  requesting a non-existent OpenGL or OpenGL ES version like 2.7.\n *\n *  Requesting a valid but unavailable OpenGL or OpenGL ES version will instead\n *  result in a @ref GLFW_VERSION_UNAVAILABLE error.\n *\n *  @analysis Application programmer error.  Fix the offending call.\n */\n#define GLFW_INVALID_VALUE          0x00010004\n/*! @brief A memory allocation failed.\n *\n *  A memory allocation failed.\n *\n *  @analysis A bug in GLFW or the underlying operating system.  Report the bug\n *  to our [issue tracker](https://github.com/glfw/glfw/issues).\n */\n#define GLFW_OUT_OF_MEMORY          0x00010005\n/*! @brief GLFW could not find support for the requested API on the system.\n *\n *  GLFW could not find support for the requested API on the system.\n *\n *  @analysis The installed graphics driver does not support the requested\n *  API, or does not support it via the chosen context creation backend.\n *  Below are a few examples.\n *\n *  @par\n *  Some pre-installed Windows graphics drivers do not support OpenGL.  AMD only\n *  supports OpenGL ES via EGL, while Nvidia and Intel only support it via\n *  a WGL or GLX extension.  macOS does not provide OpenGL ES at all.  The Mesa\n *  EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary\n *  driver.  Older graphics drivers do not support Vulkan.\n */\n#define GLFW_API_UNAVAILABLE        0x00010006\n/*! @brief The requested OpenGL or OpenGL ES version is not available.\n *\n *  The requested OpenGL or OpenGL ES version (including any requested context\n *  or framebuffer hints) is not available on this machine.\n *\n *  @analysis The machine does not support your requirements.  If your\n *  application is sufficiently flexible, downgrade your requirements and try\n *  again.  Otherwise, inform the user that their machine does not match your\n *  requirements.\n *\n *  @par\n *  Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0\n *  comes out before the 4.x series gets that far, also fail with this error and\n *  not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions\n *  will exist.\n */\n#define GLFW_VERSION_UNAVAILABLE    0x00010007\n/*! @brief A platform-specific error occurred that does not match any of the\n *  more specific categories.\n *\n *  A platform-specific error occurred that does not match any of the more\n *  specific categories.\n *\n *  @analysis A bug or configuration error in GLFW, the underlying operating\n *  system or its drivers, or a lack of required resources.  Report the issue to\n *  our [issue tracker](https://github.com/glfw/glfw/issues).\n */\n#define GLFW_PLATFORM_ERROR         0x00010008\n/*! @brief The requested format is not supported or available.\n *\n *  If emitted during window creation, the requested pixel format is not\n *  supported.\n *\n *  If emitted when querying the clipboard, the contents of the clipboard could\n *  not be converted to the requested format.\n *\n *  @analysis If emitted during window creation, one or more\n *  [hard constraints](@ref window_hints_hard) did not match any of the\n *  available pixel formats.  If your application is sufficiently flexible,\n *  downgrade your requirements and try again.  Otherwise, inform the user that\n *  their machine does not match your requirements.\n *\n *  @par\n *  If emitted when querying the clipboard, ignore the error or report it to\n *  the user, as appropriate.\n */\n#define GLFW_FORMAT_UNAVAILABLE     0x00010009\n/*! @brief The specified window does not have an OpenGL or OpenGL ES context.\n *\n *  A window that does not have an OpenGL or OpenGL ES context was passed to\n *  a function that requires it to have one.\n *\n *  @analysis Application programmer error.  Fix the offending call.\n */\n#define GLFW_NO_WINDOW_CONTEXT      0x0001000A\n/*! @brief The requested feature is not provided by the platform.\n *\n *  The requested feature is not provided by the platform, so GLFW is unable to\n *  implement it.  The documentation for each function notes if it could emit\n *  this error.\n *\n *  @analysis Platform or platform version limitation.  The error can be ignored\n *  unless the feature is critical to the application.\n *\n *  @par\n *  A function call that emits this error has no effect other than the error and\n *  updating any existing out parameters.\n */\n#define GLFW_FEATURE_UNAVAILABLE    0x0001000C\n/*! @brief The requested feature is not implemented for the platform.\n *\n *  The requested feature has not yet been implemented in GLFW for this platform.\n *\n *  @analysis An incomplete implementation of GLFW for this platform, hopefully\n *  fixed in a future release.  The error can be ignored unless the feature is\n *  critical to the application.\n *\n *  @par\n *  A function call that emits this error has no effect other than the error and\n *  updating any existing out parameters.\n */\n#define GLFW_FEATURE_UNIMPLEMENTED  0x0001000D\n/*! @} */\n\n/*! @addtogroup window\n *  @{ */\n/*! @brief Input focus window hint and attribute\n *\n *  Input focus [window hint](@ref GLFW_FOCUSED_hint) or\n *  [window attribute](@ref GLFW_FOCUSED_attrib).\n */\n#define GLFW_FOCUSED                0x00020001\n/*! @brief Window iconification window attribute\n *\n *  Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib).\n */\n#define GLFW_ICONIFIED              0x00020002\n/*! @brief Window resize-ability window hint and attribute\n *\n *  Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and\n *  [window attribute](@ref GLFW_RESIZABLE_attrib).\n */\n#define GLFW_RESIZABLE              0x00020003\n/*! @brief Window visibility window hint and attribute\n *\n *  Window visibility [window hint](@ref GLFW_VISIBLE_hint) and\n *  [window attribute](@ref GLFW_VISIBLE_attrib).\n */\n#define GLFW_VISIBLE                0x00020004\n/*! @brief Window decoration window hint and attribute\n *\n *  Window decoration [window hint](@ref GLFW_DECORATED_hint) and\n *  [window attribute](@ref GLFW_DECORATED_attrib).\n */\n#define GLFW_DECORATED              0x00020005\n/*! @brief Window auto-iconification window hint and attribute\n *\n *  Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and\n *  [window attribute](@ref GLFW_AUTO_ICONIFY_attrib).\n */\n#define GLFW_AUTO_ICONIFY           0x00020006\n/*! @brief Window decoration window hint and attribute\n *\n *  Window decoration [window hint](@ref GLFW_FLOATING_hint) and\n *  [window attribute](@ref GLFW_FLOATING_attrib).\n */\n#define GLFW_FLOATING               0x00020007\n/*! @brief Window maximization window hint and attribute\n *\n *  Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and\n *  [window attribute](@ref GLFW_MAXIMIZED_attrib).\n */\n#define GLFW_MAXIMIZED              0x00020008\n/*! @brief Cursor centering window hint\n *\n *  Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint).\n */\n#define GLFW_CENTER_CURSOR          0x00020009\n/*! @brief Window framebuffer transparency hint and attribute\n *\n *  Window framebuffer transparency\n *  [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and\n *  [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib).\n */\n#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A\n/*! @brief Mouse cursor hover window attribute.\n *\n *  Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib).\n */\n#define GLFW_HOVERED                0x0002000B\n/*! @brief Input focus on calling show window hint and attribute\n *\n *  Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or\n *  [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib).\n */\n#define GLFW_FOCUS_ON_SHOW          0x0002000C\n\n/*! @brief Mouse input transparency window hint and attribute\n *\n *  Mouse input transparency [window hint](@ref GLFW_MOUSE_PASSTHROUGH_hint) or\n *  [window attribute](@ref GLFW_MOUSE_PASSTHROUGH_attrib).\n */\n#define GLFW_MOUSE_PASSTHROUGH      0x0002000D\n\n/*! @brief Occlusion window attribute\n *\n *  Occlusion [window attribute](@ref GLFW_OCCLUDED_attrib).\n */\n#define GLFW_OCCLUDED               0x0002000E\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_RED_BITS).\n */\n#define GLFW_RED_BITS               0x00021001\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS).\n */\n#define GLFW_GREEN_BITS             0x00021002\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS).\n */\n#define GLFW_BLUE_BITS              0x00021003\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS).\n */\n#define GLFW_ALPHA_BITS             0x00021004\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS).\n */\n#define GLFW_DEPTH_BITS             0x00021005\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS).\n */\n#define GLFW_STENCIL_BITS           0x00021006\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS).\n */\n#define GLFW_ACCUM_RED_BITS         0x00021007\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS).\n */\n#define GLFW_ACCUM_GREEN_BITS       0x00021008\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS).\n */\n#define GLFW_ACCUM_BLUE_BITS        0x00021009\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS).\n */\n#define GLFW_ACCUM_ALPHA_BITS       0x0002100A\n/*! @brief Framebuffer auxiliary buffer hint.\n *\n *  Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS).\n */\n#define GLFW_AUX_BUFFERS            0x0002100B\n/*! @brief OpenGL stereoscopic rendering hint.\n *\n *  OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO).\n */\n#define GLFW_STEREO                 0x0002100C\n/*! @brief Framebuffer MSAA samples hint.\n *\n *  Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES).\n */\n#define GLFW_SAMPLES                0x0002100D\n/*! @brief Framebuffer sRGB hint.\n *\n *  Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE).\n */\n#define GLFW_SRGB_CAPABLE           0x0002100E\n/*! @brief Monitor refresh rate hint.\n *\n *  Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE).\n */\n#define GLFW_REFRESH_RATE           0x0002100F\n/*! @brief Framebuffer double buffering hint.\n *\n *  Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER).\n */\n#define GLFW_DOUBLEBUFFER           0x00021010\n\n/*! @brief Context client API hint and attribute.\n *\n *  Context client API [hint](@ref GLFW_CLIENT_API_hint) and\n *  [attribute](@ref GLFW_CLIENT_API_attrib).\n */\n#define GLFW_CLIENT_API             0x00022001\n/*! @brief Context client API major version hint and attribute.\n *\n *  Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint)\n *  and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib).\n */\n#define GLFW_CONTEXT_VERSION_MAJOR  0x00022002\n/*! @brief Context client API minor version hint and attribute.\n *\n *  Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint)\n *  and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib).\n */\n#define GLFW_CONTEXT_VERSION_MINOR  0x00022003\n/*! @brief Context client API revision number hint and attribute.\n *\n *  Context client API revision number\n *  [attribute](@ref GLFW_CONTEXT_REVISION_attrib).\n */\n#define GLFW_CONTEXT_REVISION       0x00022004\n/*! @brief Context robustness hint and attribute.\n *\n *  Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint)\n *  and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib).\n */\n#define GLFW_CONTEXT_ROBUSTNESS     0x00022005\n/*! @brief OpenGL forward-compatibility hint and attribute.\n *\n *  OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint)\n *  and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib).\n */\n#define GLFW_OPENGL_FORWARD_COMPAT  0x00022006\n/*! @brief Debug mode context hint and attribute.\n *\n *  Debug mode context [hint](@ref GLFW_CONTEXT_DEBUG_hint) and\n *  [attribute](@ref GLFW_CONTEXT_DEBUG_attrib).\n */\n#define GLFW_CONTEXT_DEBUG          0x00022007\n/*! @brief Legacy name for compatibility.\n *\n *  This is an alias for compatibility with earlier versions.\n */\n#define GLFW_OPENGL_DEBUG_CONTEXT   GLFW_CONTEXT_DEBUG\n/*! @brief OpenGL profile hint and attribute.\n *\n *  OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and\n *  [attribute](@ref GLFW_OPENGL_PROFILE_attrib).\n */\n#define GLFW_OPENGL_PROFILE         0x00022008\n/*! @brief Context flush-on-release hint and attribute.\n *\n *  Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and\n *  [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib).\n */\n#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009\n/*! @brief Context error suppression hint and attribute.\n *\n *  Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and\n *  [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib).\n */\n#define GLFW_CONTEXT_NO_ERROR       0x0002200A\n/*! @brief Context creation API hint and attribute.\n *\n *  Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and\n *  [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib).\n */\n#define GLFW_CONTEXT_CREATION_API   0x0002200B\n/*! @brief Window content area scaling window\n *  [window hint](@ref GLFW_SCALE_TO_MONITOR).\n */\n#define GLFW_SCALE_TO_MONITOR       0x0002200C\n/*! @brief macOS specific\n *  [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint).\n */\n#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001\n/*! @brief macOS specific\n *  [window hint](@ref GLFW_COCOA_FRAME_NAME_hint).\n */\n#define GLFW_COCOA_FRAME_NAME         0x00023002\n/*! @brief macOS specific\n *  [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint).\n */\n#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003\n/*! @brief macOS specific\n *  [window hint](@ref GLFW_COCOA_COLOR_SPACE_hint).\n */\n#define GLFW_COCOA_COLOR_SPACE 0x00023004\ntypedef enum {\n    DEFAULT_COLORSPACE = 0,\n    SRGB_COLORSPACE = 1,\n    DISPLAY_P3_COLORSPACE = 2,\n} GlfwCocoaColorSpaces;\n/*! @brief Blur Radius. On macOS the actual radius is used. On Linux it is treated as a bool.\n *  [window hint](@ref GLFW_BLUR_RADIUS).\n */\n#define GLFW_BLUR_RADIUS       0x0002305\n\n/*! @brief X11 specific\n *  [window hint](@ref GLFW_X11_CLASS_NAME_hint).\n */\n#define GLFW_X11_CLASS_NAME         0x00024001\n/*! @brief X11 specific\n *  [window hint](@ref GLFW_X11_CLASS_NAME_hint).\n */\n#define GLFW_X11_INSTANCE_NAME      0x00024002\n\n#define GLFW_WAYLAND_APP_ID         0x00025001\n#define GLFW_WAYLAND_BGCOLOR        0x00025002\n#define GLFW_WAYLAND_WINDOW_TAG     0x00025003\n/*! @} */\n\n#define GLFW_NO_API                          0\n#define GLFW_OPENGL_API             0x00030001\n#define GLFW_OPENGL_ES_API          0x00030002\n\n#define GLFW_NO_ROBUSTNESS                   0\n#define GLFW_NO_RESET_NOTIFICATION  0x00031001\n#define GLFW_LOSE_CONTEXT_ON_RESET  0x00031002\n\n#define GLFW_OPENGL_ANY_PROFILE              0\n#define GLFW_OPENGL_CORE_PROFILE    0x00032001\n#define GLFW_OPENGL_COMPAT_PROFILE  0x00032002\n\n#define GLFW_CURSOR                 0x00033001\n#define GLFW_STICKY_KEYS            0x00033002\n#define GLFW_STICKY_MOUSE_BUTTONS   0x00033003\n#define GLFW_LOCK_KEY_MODS          0x00033004\n#define GLFW_RAW_MOUSE_MOTION       0x00033005\n\n#define GLFW_CURSOR_NORMAL          0x00034001\n#define GLFW_CURSOR_HIDDEN          0x00034002\n#define GLFW_CURSOR_DISABLED        0x00034003\n\n#define GLFW_ANY_RELEASE_BEHAVIOR            0\n#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001\n#define GLFW_RELEASE_BEHAVIOR_NONE  0x00035002\n\n#define GLFW_NATIVE_CONTEXT_API     0x00036001\n#define GLFW_EGL_CONTEXT_API        0x00036002\n#define GLFW_OSMESA_CONTEXT_API     0x00036003\n\n#define GLFW_ANGLE_PLATFORM_TYPE_NONE    0x00037001\n#define GLFW_ANGLE_PLATFORM_TYPE_OPENGL  0x00037002\n#define GLFW_ANGLE_PLATFORM_TYPE_OPENGLES 0x00037003\n#define GLFW_ANGLE_PLATFORM_TYPE_D3D9    0x00037004\n#define GLFW_ANGLE_PLATFORM_TYPE_D3D11   0x00037005\n#define GLFW_ANGLE_PLATFORM_TYPE_VULKAN  0x00037007\n#define GLFW_ANGLE_PLATFORM_TYPE_METAL   0x00037008\n\n/*! @defgroup shapes Standard cursor shapes\n *  @brief Standard system cursor shapes.\n *\n *  See [standard cursor creation](@ref cursor_standard) for how these are used.\n *\n *  @ingroup input\n *  @{ */\n\ntypedef enum {\n/* start mouse cursor shapes (auto generated by gen-key-constants.py do not edit) */\n    GLFW_DEFAULT_CURSOR,\n    GLFW_TEXT_CURSOR,\n    GLFW_POINTER_CURSOR,\n    GLFW_HELP_CURSOR,\n    GLFW_WAIT_CURSOR,\n    GLFW_PROGRESS_CURSOR,\n    GLFW_CROSSHAIR_CURSOR,\n    GLFW_CELL_CURSOR,\n    GLFW_VERTICAL_TEXT_CURSOR,\n    GLFW_MOVE_CURSOR,\n    GLFW_E_RESIZE_CURSOR,\n    GLFW_NE_RESIZE_CURSOR,\n    GLFW_NW_RESIZE_CURSOR,\n    GLFW_N_RESIZE_CURSOR,\n    GLFW_SE_RESIZE_CURSOR,\n    GLFW_SW_RESIZE_CURSOR,\n    GLFW_S_RESIZE_CURSOR,\n    GLFW_W_RESIZE_CURSOR,\n    GLFW_EW_RESIZE_CURSOR,\n    GLFW_NS_RESIZE_CURSOR,\n    GLFW_NESW_RESIZE_CURSOR,\n    GLFW_NWSE_RESIZE_CURSOR,\n    GLFW_ZOOM_IN_CURSOR,\n    GLFW_ZOOM_OUT_CURSOR,\n    GLFW_ALIAS_CURSOR,\n    GLFW_COPY_CURSOR,\n    GLFW_NOT_ALLOWED_CURSOR,\n    GLFW_NO_DROP_CURSOR,\n    GLFW_GRAB_CURSOR,\n    GLFW_GRABBING_CURSOR,\n    GLFW_INVALID_CURSOR,\n/* end mouse cursor shapes */\n} GLFWCursorShape;\n/*! @} */\n\n#define GLFW_CONNECTED              0x00040001\n#define GLFW_DISCONNECTED           0x00040002\n\n/*! @addtogroup init\n *  @{ */\n/*! @brief Joystick hat buttons init hint.\n *\n *  Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS).\n */\n#define GLFW_JOYSTICK_HAT_BUTTONS   0x00050001\n/*! @brief ANGLE rendering backend init hint.\n *\n *  ANGLE rendering backend [init hint](@ref GLFW_ANGLE_PLATFORM_TYPE_hint).\n */\n#define GLFW_ANGLE_PLATFORM_TYPE    0x00050002\n#define GLFW_DEBUG_KEYBOARD         0x00050003\n#define GLFW_DEBUG_RENDERING        0x00050004\n/*! @brief macOS specific init hint.\n *\n *  macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint).\n */\n#define GLFW_COCOA_CHDIR_RESOURCES  0x00051001\n/*! @brief macOS specific init hint.\n *\n *  macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint).\n */\n#define GLFW_COCOA_MENUBAR          0x00051002\n#define GLFW_WAYLAND_IME            0x00051003\n/*! @} */\n\n#define GLFW_DONT_CARE              -1\n\n\n/*************************************************************************\n * GLFW API types\n *************************************************************************/\n\n/*! @brief Client API function pointer type.\n *\n *  Generic function pointer used for returning client API function pointers\n *  without forcing a cast from a regular pointer.\n *\n *  @sa @ref context_glext\n *  @sa @ref glfwGetProcAddress\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup context\n */\ntypedef void (*GLFWglproc)(void);\n\n/*! @brief Vulkan API function pointer type.\n *\n *  Generic function pointer used for returning Vulkan API function pointers\n *  without forcing a cast from a regular pointer.\n *\n *  @sa @ref vulkan_proc\n *  @sa @ref glfwGetInstanceProcAddress\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup vulkan\n */\ntypedef void (*GLFWvkproc)(void);\n\n/*! @brief Opaque monitor object.\n *\n *  Opaque monitor object.\n *\n *  @see @ref monitor_object\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\ntypedef struct GLFWmonitor GLFWmonitor;\n\n/*! @brief Opaque window object.\n *\n *  Opaque window object.\n *\n *  @see @ref window_object\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef struct GLFWwindow GLFWwindow;\n\n/*! @brief Opaque cursor object.\n *\n *  Opaque cursor object.\n *\n *  @see @ref cursor_object\n *\n *  @since Added in version 3.1.\n *\n *  @ingroup input\n */\ntypedef struct GLFWcursor GLFWcursor;\n\n/*! @brief Opaque drop data object.\n *\n *  Opaque drop data object representing data from a drag and drop operation.\n *  This object is passed to the drop callback and can be used to query\n *  available MIME types and read the dropped data in chunks.\n *\n *  @see @ref path_drop\n *  @see @ref glfwGetDropMimeTypes\n *  @see @ref glfwReadDropData\n *\n *  @since Added in version 4.0.\n *\n *  @ingroup input\n */\ntypedef enum {\n    GLFW_RELEASE = 0,\n    GLFW_PRESS = 1,\n    GLFW_REPEAT = 2\n} GLFWKeyAction;\n\ntypedef enum {\n    GLFW_IME_NONE,\n    GLFW_IME_PREEDIT_CHANGED,\n    GLFW_IME_COMMIT_TEXT,\n    GLFW_IME_WAYLAND_DONE_EVENT,\n} GLFWIMEState;\n\ntypedef enum {\n    GLFW_IME_UPDATE_FOCUS = 1,\n    GLFW_IME_UPDATE_CURSOR_POSITION = 2\n} GLFWIMEUpdateType;\n\ntypedef struct GLFWIMEUpdateEvent {\n    GLFWIMEUpdateType type;\n    const char *before_text, *at_text, *after_text;\n    bool focused;\n    struct {\n        int left, top, width, height;\n    } cursor;\n} GLFWIMEUpdateEvent;\n\n\ntypedef struct GLFWkeyevent\n{\n    // The [keyboard key](@ref keys) that was pressed or released.\n    uint32_t key, shifted_key, alternate_key;\n\n    // The platform-specific identifier of the key.\n    int native_key;\n\n    // The event action. Either `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.\n    GLFWKeyAction action;\n\n    // Bit field describing which [modifier keys](@ref mods) were held down.\n    int mods;\n\n    // UTF-8 encoded text generated by this key event or empty string or NULL\n    const char *text;\n\n    // Used for Input Method events. Zero for normal key events.\n    //   A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed.\n    //   A value of GLFW_IME_COMMIT_TEXT means the text should be committed.\n    GLFWIMEState ime_state;\n\n    // For internal use only. On Linux it is the actual keycode reported by the windowing system, in contrast\n    // to native_key which can be the result of a compose operation. On macOS it is the same as native_key.\n    uint32_t native_key_id;\n\n    // True if this is a synthesized event on focus change\n    bool fake_event_on_focus_change;\n} GLFWkeyevent;\n\ntypedef enum { GLFW_LAYER_SHELL_NONE, GLFW_LAYER_SHELL_BACKGROUND, GLFW_LAYER_SHELL_PANEL, GLFW_LAYER_SHELL_TOP, GLFW_LAYER_SHELL_OVERLAY } GLFWLayerShellType;\n\ntypedef enum { GLFW_EDGE_TOP, GLFW_EDGE_BOTTOM, GLFW_EDGE_LEFT, GLFW_EDGE_RIGHT, GLFW_EDGE_CENTER, GLFW_EDGE_NONE, GLFW_EDGE_CENTER_SIZED } GLFWEdge;\n\ntypedef enum { GLFW_FOCUS_NOT_ALLOWED, GLFW_FOCUS_EXCLUSIVE, GLFW_FOCUS_ON_DEMAND} GLFWFocusPolicy;\n\ntypedef struct GLFWLayerShellConfig {\n    GLFWLayerShellType type;\n    GLFWEdge edge;\n    struct {\n        GLFWEdge edge;\n        int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;\n    } previous;\n    bool was_toggled_to_fullscreen;\n    char output_name[128];\n    GLFWFocusPolicy focus_policy;\n    unsigned x_size_in_cells, x_size_in_pixels;\n    unsigned y_size_in_cells, y_size_in_pixels;\n    int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;\n    int requested_exclusive_zone, hide_on_focus_loss;\n    unsigned override_exclusive_zone;\n    void (*size_callback)(GLFWwindow *window, float xscale, float yscale, unsigned *cell_width, unsigned *cell_height, double *left_edge_spacing, double *top_edge_spacing, double *right_edge_spacing, double *bottom_edge_spacing);\n    struct { float xscale, yscale; } expected;\n    struct {\n        float background_opacity; int background_blur, color_space;\n    } related;\n} GLFWLayerShellConfig;\n\ntypedef struct GLFWDBUSNotificationData {\n    const char *app_name, *icon, *summary, *body, *category, **actions; size_t num_actions;\n    int32_t timeout; uint8_t urgency; uint32_t replaces; int muted;\n} GLFWDBUSNotificationData;\n\ntypedef enum { GLFW_DROP_ENTER, GLFW_DROP_MOVE, GLFW_DROP_LEAVE, GLFW_DROP_DROP, GLFW_DROP_STATUS_UPDATE, GLFW_DROP_DATA_AVAILABLE } GLFWDropEventType;\n\n/*! @brief Drag operation types.\n *\n *  These constants specify the type of drag operation (copy, move, or generic).\n *\n *  @ingroup input\n */\ntypedef enum {\n    GLFW_DRAG_OPERATION_NONE = 0,  // no operation, drop was not accepted\n    /*! Move the dragged data to the destination. */\n    GLFW_DRAG_OPERATION_MOVE = 1,\n    /*! Copy the dragged data to the destination. */\n    GLFW_DRAG_OPERATION_COPY = 2,\n    /*! Generic drag operation (platform decides semantics). */\n    GLFW_DRAG_OPERATION_GENERIC = 4\n} GLFWDragOperationType;\n\n\ntypedef struct GLFWDropEvent {\n    GLFWDropEventType type;\n    const char **mimes; size_t num_mimes;\n    // Positions are only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE.\n    // They are in window co-ordinates same as for mouse events\n    double xpos, ypos;\n    bool from_self;  // Only valid upto GLFW_DROP_DROP\n    ssize_t (*read_data)(GLFWwindow *w, struct GLFWDropEvent* ev, char *buffer, size_t sz);  // Only valid for GLFW_DROP_DATA_AVAILABLE\n    void (*finish_drop)(GLFWwindow *w, GLFWDragOperationType op); // Only valid for GLFW_DROP_DROP and GLFW_DROP_DATA_AVAILABLE\n} GLFWDropEvent;\ntypedef void (* GLFWdropeventfun)(GLFWwindow*, GLFWDropEvent *event);\n\n\n/*! @brief The function pointer type for error callbacks.\n *\n *  This is the function pointer type for error callbacks.  An error callback\n *  function has the following signature:\n *  @code\n *  void callback_name(int error_code, const char* description)\n *  @endcode\n *\n *  @param[in] error_code An [error code](@ref errors).  Future releases may add\n *  more error codes.\n *  @param[in] description A UTF-8 encoded string describing the error.\n *\n *  @pointer_lifetime The error description string is valid until the callback\n *  function returns.\n *\n *  @sa @ref error_handling\n *  @sa @ref glfwSetErrorCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup init\n */\ntypedef void (* GLFWerrorfun)(int,const char*);\n\n/*! @brief The function pointer type for window position callbacks.\n *\n *  This is the function pointer type for window position callbacks.  A window\n *  position callback function has the following signature:\n *  @code\n *  void callback_name(GLFWwindow* window, int xpos, int ypos)\n *  @endcode\n *\n *  @param[in] window The window that was moved.\n *  @param[in] xpos The new x-coordinate, in screen coordinates, of the\n *  upper-left corner of the content area of the window.\n *  @param[in] ypos The new y-coordinate, in screen coordinates, of the\n *  upper-left corner of the content area of the window.\n *\n *  @sa @ref window_pos\n *  @sa @ref glfwSetWindowPosCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowposfun)(GLFWwindow*,int,int);\n\n/*! @brief The function pointer type for window size callbacks.\n *\n *  This is the function pointer type for window size callbacks.  A window size\n *  callback function has the following signature:\n *  @code\n *  void callback_name(GLFWwindow* window, int width, int height)\n *  @endcode\n *\n *  @param[in] window The window that was resized.\n *  @param[in] width The new width, in screen coordinates, of the window.\n *  @param[in] height The new height, in screen coordinates, of the window.\n *\n *  @sa @ref window_size\n *  @sa @ref glfwSetWindowSizeCallback\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int);\n\n/*! @brief The function pointer type for window close callbacks.\n *\n *  This is the function pointer type for window close callbacks.  A window\n *  close callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window)\n *  @endcode\n *\n *  @param[in] window The window that the user attempted to close.\n *\n *  @sa @ref window_close\n *  @sa @ref glfwSetWindowCloseCallback\n *\n *  @since Added in version 2.5.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowclosefun)(GLFWwindow*);\n\n/*! @brief The function pointer type for application close callbacks.\n *\n *  This is the function pointer type for application close callbacks.  A application\n *  close callback function has the following signature:\n *  @code\n *  void function_name(int flags)\n *  @endcode\n *\n *  @param[in] flags 0 for a user requested application quit, 1 if a fatal error occurred and application should quit ASAP\n *\n *  @sa @ref glfwSetApplicationCloseCallback\n *\n *  @ingroup window\n */\ntypedef void (* GLFWapplicationclosefun)(int);\n\n\n/*! @brief The function pointer type for system color theme change callbacks.\n *\n *  This is the function pointer type for system color theme changes.\n *  @code\n *  void function_name(GLFWColorScheme theme_type, bool is_initial_value)\n *  @endcode\n *\n *  @param[in] theme_type 0 for unknown, 1 for dark and 2 for light\n *  @param[in] is_initial_value true if this is the initial read of the color theme on systems where it is asynchronous such as Linux\n *\n *  @sa @ref glfwSetSystemColorThemeChangeCallback\n *\n *  @ingroup window\n */\ntypedef void (* GLFWsystemcolorthemechangefun)(GLFWColorScheme, bool);\n\n\n/*! @brief The function pointer type for window content refresh callbacks.\n *\n *  This is the function pointer type for window content refresh callbacks.\n *  A window content refresh callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window);\n *  @endcode\n *\n *  @param[in] window The window whose content needs to be refreshed.\n *\n *  @sa @ref window_refresh\n *  @sa @ref glfwSetWindowRefreshCallback\n *\n *  @since Added in version 2.5.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowrefreshfun)(GLFWwindow*);\n\n/*! @brief The function pointer type for window focus callbacks.\n *\n *  This is the function pointer type for window focus callbacks.  A window\n *  focus callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int focused)\n *  @endcode\n *\n *  @param[in] window The window that gained or lost input focus.\n *  @param[in] focused `true` if the window was given input focus, or\n *  `false` if it lost it.\n *\n *  @sa @ref window_focus\n *  @sa @ref glfwSetWindowFocusCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowfocusfun)(GLFWwindow*,int);\n\n/*! @brief The function signature for window occlusion callbacks.\n *\n *  This is the function signature for window occlusion callback functions.\n *\n *  @param[in] window The window whose occlusion state changed.\n *  @param[in] occluded `true` if the window was occluded, or `false`\n *  if the window is no longer occluded.\n *\n *  @sa @ref window_occlusion\n *  @sa @ref glfwSetWindowOcclusionCallback\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowocclusionfun)(GLFWwindow*, bool);\n\n\n/*! @brief The function pointer type for window iconify callbacks.\n *\n *  This is the function pointer type for window iconify callbacks.  A window\n *  iconify callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int iconified)\n *  @endcode\n *\n *  @param[in] window The window that was iconified or restored.\n *  @param[in] iconified `true` if the window was iconified, or\n *  `false` if it was restored.\n *\n *  @sa @ref window_iconify\n *  @sa @ref glfwSetWindowIconifyCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int);\n\n/*! @brief The function pointer type for window maximize callbacks.\n *\n *  This is the function pointer type for window maximize callbacks.  A window\n *  maximize callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int maximized)\n *  @endcode\n *\n *  @param[in] window The window that was maximized or restored.\n *  @param[in] maximized `true` if the window was maximized, or\n *  `false` if it was restored.\n *\n *  @sa @ref window_maximize\n *  @sa glfwSetWindowMaximizeCallback\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int);\n\n/*! @brief The function pointer type for framebuffer size callbacks.\n *\n *  This is the function pointer type for framebuffer size callbacks.\n *  A framebuffer size callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int width, int height)\n *  @endcode\n *\n *  @param[in] window The window whose framebuffer was resized.\n *  @param[in] width The new width, in pixels, of the framebuffer.\n *  @param[in] height The new height, in pixels, of the framebuffer.\n *\n *  @sa @ref window_fbsize\n *  @sa @ref glfwSetFramebufferSizeCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int);\n\n/*! @brief The function pointer type for window content scale callbacks.\n *\n *  This is the function pointer type for window content scale callbacks.\n *  A window content scale callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, float xscale, float yscale)\n *  @endcode\n *\n *  @param[in] window The window whose content scale changed.\n *  @param[in] xscale The new x-axis content scale of the window.\n *  @param[in] yscale The new y-axis content scale of the window.\n *\n *  @sa @ref window_scale\n *  @sa @ref glfwSetWindowContentScaleCallback\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float);\n\n/*! @brief The function pointer type for mouse button callbacks.\n *\n *  This is the function pointer type for mouse button callback functions.\n *  A mouse button callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int button, int action, int mods)\n *  @endcode\n *\n *  @param[in] window The window that received the event.\n *  @param[in] button The [mouse button](@ref buttons) that was pressed or\n *  released.\n *  @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`.  Future releases\n *  may add more actions.\n *  @param[in] mods Bit field describing which [modifier keys](@ref mods) were\n *  held down.\n *\n *  @sa @ref input_mouse_button\n *  @sa @ref glfwSetMouseButtonCallback\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle and modifier mask parameters.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int);\n\n/*! @brief The function pointer type for cursor position callbacks.\n *\n *  This is the function pointer type for cursor position callbacks.  A cursor\n *  position callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, double xpos, double ypos);\n *  @endcode\n *\n *  @param[in] window The window that received the event.\n *  @param[in] xpos The new cursor x-coordinate, relative to the left edge of\n *  the content area.\n *  @param[in] ypos The new cursor y-coordinate, relative to the top edge of the\n *  content area.\n *\n *  @sa @ref cursor_pos\n *  @sa @ref glfwSetCursorPosCallback\n *\n *  @since Added in version 3.0.  Replaces `GLFWmouseposfun`.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWcursorposfun)(GLFWwindow*,double,double);\n\n/*! @brief The function pointer type for cursor enter/leave callbacks.\n *\n *  This is the function pointer type for cursor enter/leave callbacks.\n *  A cursor enter/leave callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int entered)\n *  @endcode\n *\n *  @param[in] window The window that received the event.\n *  @param[in] entered `true` if the cursor entered the window's content\n *  area, or `false` if it left it.\n *\n *  @sa @ref cursor_enter\n *  @sa @ref glfwSetCursorEnterCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWcursorenterfun)(GLFWwindow*,int);\n\n/*! @brief The function pointer type for scroll callbacks.\n *\n *  This is the function pointer type for scroll callbacks.  A scroll callback\n *  function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, double xoffset, double yoffset)\n *  @endcode\n *\n *  @param[in] window The window that received the event.\n *  @param[in] xoffset The scroll offset along the x-axis.\n *  @param[in] yoffset The scroll offset along the y-axis.\n *  @param[in] flags A bit-mask providing extra data about the event.\n *  flags & 1 will be true if and only if the offset values are \"high-precision\",\n *  typically pixel values. Otherwise the offset values are number of lines.\n *  (flags >> 1) & 7 will have value 1 for the start of momentum scrolling,\n *  value 2 for stationary momentum scrolling, value 3 for momentum scrolling\n *  in progress, value 4 for momentum scrolling ended, value 5 for momentum\n *  scrolling cancelled and value 6 if scrolling may begin soon.\n *  @param[int] mods The keyboard modifiers\n *\n *  @sa @ref scrolling\n *  @sa @ref glfwSetScrollCallback\n *\n *  @since Added in version 3.0.  Replaces `GLFWmousewheelfun`.\n *  @since Changed in version 4.0.  Added `flags` parameter.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWscrollfun)(GLFWwindow*,const GLFWScrollEvent*);\n\n/*! @brief The function pointer type for key callbacks.\n *\n *  This is the function pointer type for key callbacks.  A keyboard\n *  key callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, uint32_t key, int native_key, int action, int mods)\n *  @endcode\n *  The semantics of this function are that the key that is interacted with on the\n *  keyboard is reported, and the text, if any generated by the key is reported.\n *  So, for example, if on a US-ASCII keyboard the user presses Shift+= GLFW\n *  will report the text \"+\" and the key as GLFW_KEY_EQUAL. The reported key takes into\n *  account any current keyboard maps defined in the OS. So with a dvorak mapping, pressing\n *  the \"s\" key will generate text \"o\" and GLFW_KEY_O.\n *\n *  @param[in] window The window that received the event.\n *  @param[in] ev The key event, see GLFWkeyevent. The data in this event is only valid for\n *  the lifetime of the callback.\n *\n *  @note On X11/Wayland if a modifier other than the modifiers GLFW reports\n *  (ctrl/shift/alt/super) is used, GLFW will report the shifted key rather\n *  than the unshifted key. So for example, if ISO_Shift_Level_5 is used to\n *  convert the key A into UP GLFW will report the key as UP with no modifiers.\n *\n *  @sa @ref input_key\n *  @sa @ref glfwSetKeyboardCallback\n *\n *  @since Added in version 4.0.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);\n\ntypedef enum {\n    GLFW_DRAG_DATA_REQUEST,  // request data for specified mime type\n    GLFW_DRAG_CANCELLED,\n    GLFW_DRAG_FINSHED,\n    GLFW_DRAG_ACCEPTED,  // mimetype was accepted or NULL if drag was accepted but no mime type specified\n    GLFW_DRAG_ACTION_CHANGED,  // action was changed 0 or GLFWDragOperationType\n    GLFW_DRAG_DROPPED,  // drop was performed but no data transferred yet\n} GLFWDragEventType;\n\ntypedef struct GLFWDragSourceItem {\n    const char *mime_type;\n    // Can be on null to provide data when the drag is started should be used only when the data is relatively small\n    const char *optional_data;\n    size_t data_size;\n} GLFWDragSourceItem;\n\ntypedef struct GLFWDragEvent {\n    GLFWDragEventType type;\n    // When the drag event callback is called with a mimetype and no data, the\n    // application should set the data ans data_sz and err_num fields.\n    // Once glfw is done reading the data the drag event callback will be\n    // called with the data pointer unchanged. The application is now free\n    // to delete the data, as needed.\n    const char *mime_type;\n    const char *data; size_t data_sz;\n    int err_num;  // POSIX error code indicating failure fetching data\n    GLFWDragOperationType action;  // can be 0 indicating no action\n} GLFWDragEvent;\n\ntypedef void (* GLFWdragsourcefun)(GLFWwindow* window, GLFWDragEvent *ev);\n\n/*! @brief The function pointer type for drag event callbacks.\n *\n *  This is the function pointer type for drag event callbacks. A drag event\n *  callback function has the following signature:\n *  @code\n *  int function_name(GLFWwindow* window, int event, double xpos, double ypos, const char** mime_types, int* mime_count)\n *  @endcode\n *\n *  @param[in] window The window that received the drag event.\n *  @param[in] event The drag event type: @ref GLFW_DRAG_ENTER, @ref GLFW_DRAG_MOVE,\n *  or @ref GLFW_DRAG_LEAVE.\n *  @param[in] xpos The x-coordinate of the drag position in window coordinates.\n *  @param[in] ypos The y-coordinate of the drag position in window coordinates.\n *  @param[in,out] mime_types A writable array of MIME type strings available from the drag source.\n *  For @ref GLFW_DRAG_ENTER and @ref GLFW_DRAG_MOVE events this is non-NULL and contains all\n *  available MIME types. The callback is responsible for sorting this list by priority and\n *  keeping only the MIME types it wants to accept. The first MIME type in the sorted list\n *  will be used for the drop operation. The strings are only valid for the duration of the\n *  callback; if you need to store them, make copies. For @ref GLFW_DRAG_LEAVE events this\n *  is `NULL`.\n *  @param[in,out] mime_count Pointer to the number of MIME types in the array. The callback\n *  should update this to reflect the new count after sorting and filtering. For\n *  @ref GLFW_DRAG_LEAVE events this is `NULL`.\n *  @return For @ref GLFW_DRAG_ENTER and @ref GLFW_DRAG_MOVE events, return non-zero\n *  to accept the drag or zero to reject it. This allows the application to\n *  dynamically accept or reject the drag based on the current position.\n *  Return value is ignored for @ref GLFW_DRAG_LEAVE events.\n *\n *  @sa @ref drag_events\n *  @sa @ref glfwSetDragCallback\n *  @sa @ref glfwUpdateDragState\n *\n *  @since Added in version 4.0.\n *\n *  @ingroup input\n */\ntypedef int (* GLFWdragfun)(GLFWwindow*, GLFWDragEventType event, double xpos, double ypos, const char** mime_types, int* mime_count);\n\ntypedef void (* GLFWliveresizefun)(GLFWwindow*, bool);\n\n/*! @brief The function pointer type for monitor configuration callbacks.\n *\n *  This is the function pointer type for monitor configuration callbacks.\n *  A monitor callback function has the following signature:\n *  @code\n *  void function_name(GLFWmonitor* monitor, int event)\n *  @endcode\n *\n *  @param[in] monitor The monitor that was connected or disconnected.\n *  @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.  Future\n *  releases may add more events.\n *\n *  @sa @ref monitor_event\n *  @sa @ref glfwSetMonitorCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\ntypedef void (* GLFWmonitorfun)(GLFWmonitor*,int);\n\n/*! @brief The function pointer type for joystick configuration callbacks.\n *\n *  This is the function pointer type for joystick configuration callbacks.\n *  A joystick configuration callback function has the following signature:\n *  @code\n *  void function_name(int jid, int event)\n *  @endcode\n *\n *  @param[in] jid The joystick that was connected or disconnected.\n *  @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.  Future\n *  releases may add more events.\n *\n *  @sa @ref joystick_event\n *  @sa @ref glfwSetJoystickCallback\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWjoystickfun)(int,int);\n\ntypedef void (* GLFWuserdatafun)(unsigned long long, void*);\ntypedef void (* GLFWtickcallback)(void*);\ntypedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data);\ntypedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool is_single_glyph);\ntypedef char* (* GLFWcurrentselectionfun)(void);\ntypedef bool (* GLFWhascurrentselectionfun)(void);\ntypedef void (* GLFWclipboarddatafreefun)(void* data);\ntypedef struct GLFWDataChunk {\n    const char *data;\n    size_t sz;\n    GLFWclipboarddatafreefun free;\n    void *iter, *free_data;\n} GLFWDataChunk;\ntypedef enum {\n    GLFW_CLIPBOARD, GLFW_PRIMARY_SELECTION\n} GLFWClipboardType;\ntypedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);\ntypedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);\ntypedef bool (* GLFWimecursorpositionfun)(GLFWwindow *window, GLFWIMEUpdateEvent *ev);\ntypedef void (* GLFWclipboardlostfun )(GLFWClipboardType);\n\n/*! @brief Video mode type.\n *\n *  This describes a single video mode.\n *\n *  @sa @ref monitor_modes\n *  @sa @ref glfwGetVideoMode\n *  @sa @ref glfwGetVideoModes\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added refresh rate member.\n *\n *  @ingroup monitor\n */\ntypedef struct GLFWvidmode\n{\n    /*! The width, in screen coordinates, of the video mode.\n     */\n    int width;\n    /*! The height, in screen coordinates, of the video mode.\n     */\n    int height;\n    /*! The bit depth of the red channel of the video mode.\n     */\n    int redBits;\n    /*! The bit depth of the green channel of the video mode.\n     */\n    int greenBits;\n    /*! The bit depth of the blue channel of the video mode.\n     */\n    int blueBits;\n    /*! The refresh rate, in Hz, of the video mode.\n     */\n    int refreshRate;\n} GLFWvidmode;\n\n/*! @brief Gamma ramp.\n *\n *  This describes the gamma ramp for a monitor.\n *\n *  @sa @ref monitor_gamma\n *  @sa @ref glfwGetGammaRamp\n *  @sa @ref glfwSetGammaRamp\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\ntypedef struct GLFWgammaramp\n{\n    /*! An array of value describing the response of the red channel.\n     */\n    unsigned short* red;\n    /*! An array of value describing the response of the green channel.\n     */\n    unsigned short* green;\n    /*! An array of value describing the response of the blue channel.\n     */\n    unsigned short* blue;\n    /*! The number of elements in each array.\n     */\n    unsigned int size;\n} GLFWgammaramp;\n\n/*! @brief Image data.\n *\n *  This describes a single 2D image.  See the documentation for each related\n *  function what the expected pixel format is.\n *\n *  @sa @ref cursor_custom\n *  @sa @ref window_icon\n *\n *  @since Added in version 2.1.\n *  @glfw3 Removed format and bytes-per-pixel members.\n *\n *  @ingroup window\n */\ntypedef struct GLFWimage\n{\n    /*! The width, in pixels, of this image.\n     */\n    int width;\n    /*! The height, in pixels, of this image.\n     */\n    int height;\n    /*! The pixel data of this image, arranged left-to-right, top-to-bottom.\n     */\n    const unsigned char* pixels;\n} GLFWimage;\n\n/*! @brief Gamepad input state\n *\n *  This describes the input state of a gamepad.\n *\n *  @sa @ref gamepad\n *  @sa @ref glfwGetGamepadState\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\ntypedef struct GLFWgamepadstate\n{\n    /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS`\n     *  or `GLFW_RELEASE`.\n     */\n    unsigned char buttons[15];\n    /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0\n     *  to 1.0 inclusive.\n     */\n    float axes[6];\n} GLFWgamepadstate;\n\n\n/*************************************************************************\n * GLFW API functions\n *************************************************************************/\n\n/*! @brief Initializes the GLFW library.\n *\n *  This function initializes the GLFW library.  Before most GLFW functions can\n *  be used, GLFW must be initialized, and before an application terminates GLFW\n *  should be terminated in order to free any resources allocated during or\n *  after initialization.\n *\n *  If this function fails, it calls @ref glfwTerminate before returning.  If it\n *  succeeds, you should call @ref glfwTerminate before the application exits.\n *\n *  Additional calls to this function after successful initialization but before\n *  termination will return `true` immediately.\n *\n *  @return `true` if successful, or `false` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark @macos This function will change the current directory of the\n *  application to the `Contents/Resources` subdirectory of the application's\n *  bundle, if present.  This can be disabled with the @ref\n *  GLFW_COCOA_CHDIR_RESOURCES init hint.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref intro_init\n *  @sa @ref glfwTerminate\n *\n *  @since Added in version 1.0.\n *\n *  @ingroup init\n */\nGLFWAPI int glfwInit(monotonic_t start_time, bool *supports_window_occlusion);\nGLFWAPI void glfwRunMainLoop(GLFWtickcallback callback, void *callback_data);\nGLFWAPI void glfwStopMainLoop(void);\nGLFWAPI unsigned long long glfwAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void * callback_data, GLFWuserdatafun free_callback);\nGLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled);\nGLFWAPI void glfwRemoveTimer(unsigned long long);\nGLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function);\nGLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback);\nGLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback);\nGLFWAPI GLFWimecursorpositionfun glfwSetIMECursorPositionCallback(GLFWimecursorpositionfun callback);\nGLFWAPI bool glfwIsLayerShellSupported(void);\n\n/*! @brief Terminates the GLFW library.\n *\n *  This function destroys all remaining windows and cursors, restores any\n *  modified gamma ramps and frees any other allocated resources.  Once this\n *  function is called, you must again call @ref glfwInit successfully before\n *  you will be able to use most GLFW functions.\n *\n *  If GLFW has been successfully initialized, this function should be called\n *  before the application exits.  If initialization fails, there is no need to\n *  call this function, as it is called by @ref glfwInit before it returns\n *  failure.\n *\n *  This function has no effect if GLFW is not initialized.\n *\n *  @errors Possible errors include @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark This function may be called before @ref glfwInit.\n *\n *  @warning The contexts of any remaining windows must not be current on any\n *  other thread when this function is called.\n *\n *  @reentrancy This function must not be called from a callback.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref intro_init\n *  @sa @ref glfwInit\n *\n *  @since Added in version 1.0.\n *\n *  @ingroup init\n */\nGLFWAPI void glfwTerminate(void);\n\n/*! @brief Sets the specified init hint to the desired value.\n *\n *  This function sets hints for the next initialization of GLFW.\n *\n *  The values you set hints to are never reset by GLFW, but they only take\n *  effect during initialization.  Once GLFW has been initialized, any values\n *  you set will be ignored until the library is terminated and initialized\n *  again.\n *\n *  Some hints are platform specific.  These may be set on any platform but they\n *  will only affect their specific platform.  Other platforms will ignore them.\n *  Setting these hints requires no platform specific headers or functions.\n *\n *  @param[in] hint The [init hint](@ref init_hints) to set.\n *  @param[in] value The new value of the init hint.\n *\n *  @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref\n *  GLFW_INVALID_VALUE.\n *\n *  @remarks This function may be called before @ref glfwInit.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa init_hints\n *  @sa glfwInit\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup init\n */\nGLFWAPI void glfwInitHint(int hint, int value);\n\n/*! @brief Retrieves the version of the GLFW library.\n *\n *  This function retrieves the major, minor and revision numbers of the GLFW\n *  library.  It is intended for when you are using GLFW as a shared library and\n *  want to ensure that you are using the minimum required version.\n *\n *  Any or all of the version arguments may be `NULL`.\n *\n *  @param[out] major Where to store the major version number, or `NULL`.\n *  @param[out] minor Where to store the minor version number, or `NULL`.\n *  @param[out] rev Where to store the revision number, or `NULL`.\n *\n *  @errors None.\n *\n *  @remark This function may be called before @ref glfwInit.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref intro_version\n *  @sa @ref glfwGetVersionString\n *\n *  @since Added in version 1.0.\n *\n *  @ingroup init\n */\nGLFWAPI void glfwGetVersion(int* major, int* minor, int* rev);\n\n/*! @brief Returns a string describing the compile-time configuration.\n *\n *  This function returns the compile-time generated\n *  [version string](@ref intro_version_string) of the GLFW library binary.  It\n *  describes the version, platform, compiler and any platform-specific\n *  compile-time options.  It should not be confused with the OpenGL or OpenGL\n *  ES version string, queried with `glGetString`.\n *\n *  __Do not use the version string__ to parse the GLFW library version.  The\n *  @ref glfwGetVersion function provides the version of the running library\n *  binary in numerical format.\n *\n *  @return The ASCII encoded GLFW version string.\n *\n *  @errors None.\n *\n *  @remark This function may be called before @ref glfwInit.\n *\n *  @pointer_lifetime The returned string is static and compile-time generated.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref intro_version\n *  @sa @ref glfwGetVersion\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup init\n */\nGLFWAPI const char* glfwGetVersionString(void);\n\n/*! @brief Returns and clears the last error for the calling thread.\n *\n *  This function returns and clears the [error code](@ref errors) of the last\n *  error that occurred on the calling thread, and optionally a UTF-8 encoded\n *  human-readable description of it.  If no error has occurred since the last\n *  call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is\n *  set to `NULL`.\n *\n *  @param[in] description Where to store the error description pointer, or `NULL`.\n *  @return The last error code for the calling thread, or @ref GLFW_NO_ERROR\n *  (zero).\n *\n *  @errors None.\n *\n *  @pointer_lifetime The returned string is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is guaranteed to be valid only until the\n *  next error occurs or the library is terminated.\n *\n *  @remark This function may be called before @ref glfwInit.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref error_handling\n *  @sa @ref glfwSetErrorCallback\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup init\n */\nGLFWAPI int glfwGetError(const char** description);\n\n/*! @brief Sets the error callback.\n *\n *  This function sets the error callback, which is called with an error code\n *  and a human-readable description each time a GLFW error occurs.\n *\n *  The error code is set before the callback is called.  Calling @ref\n *  glfwGetError from the error callback will return the same value as the error\n *  code argument.\n *\n *  The error callback is called on the thread where the error occurred.  If you\n *  are using GLFW from multiple threads, your error callback needs to be\n *  written accordingly.\n *\n *  Because the description string may have been generated specifically for that\n *  error, it is not guaranteed to be valid after the callback has returned.  If\n *  you wish to use it after the callback returns, you need to make a copy.\n *\n *  Once set, the error callback remains set even after the library has been\n *  terminated.\n *\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set.\n *\n *  @callback_signature\n *  @code\n *  void callback_name(int error_code, const char* description)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [callback pointer type](@ref GLFWerrorfun).\n *\n *  @errors None.\n *\n *  @remark This function may be called before @ref glfwInit.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref error_handling\n *  @sa @ref glfwGetError\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup init\n */\nGLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback);\n\n/*! @brief Returns the currently connected monitors.\n *\n *  This function returns an array of handles for all currently connected\n *  monitors.  The primary monitor is always first in the returned array.  If no\n *  monitors were found, this function returns `NULL`.\n *\n *  @param[out] count Where to store the number of monitors in the returned\n *  array.  This is set to zero if an error occurred.\n *  @return An array of monitor handles, or `NULL` if no monitors were found or\n *  if an [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @pointer_lifetime The returned array is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is guaranteed to be valid only until the\n *  monitor configuration changes or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_monitors\n *  @sa @ref monitor_event\n *  @sa @ref glfwGetPrimaryMonitor\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI GLFWmonitor** glfwGetMonitors(int* count);\n\n/*! @brief Returns the primary monitor.\n *\n *  This function returns the primary monitor.  This is usually the monitor\n *  where elements like the task bar or global menu bar are located.\n *\n *  @return The primary monitor, or `NULL` if no monitors were found or if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @remark The primary monitor is always first in the array returned by @ref\n *  glfwGetMonitors.\n *\n *  @sa @ref monitor_monitors\n *  @sa @ref glfwGetMonitors\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void);\n\n/*! @brief Returns the position of the monitor's viewport on the virtual screen.\n *\n *  This function returns the position, in screen coordinates, of the upper-left\n *  corner of the specified monitor.\n *\n *  Any or all of the position arguments may be `NULL`.  If an error occurs, all\n *  non-`NULL` position arguments will be set to zero.\n *\n *  @param[in] monitor The monitor to query.\n *  @param[out] xpos Where to store the monitor x-coordinate, or `NULL`.\n *  @param[out] ypos Where to store the monitor y-coordinate, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_properties\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos);\n\n/*! @brief Retrieves the work area of the monitor.\n *\n *  This function returns the position, in screen coordinates, of the upper-left\n *  corner of the work area of the specified monitor along with the work area\n *  size in screen coordinates. The work area is defined as the area of the\n *  monitor not occluded by the operating system task bar where present. If no\n *  task bar exists then the work area is the monitor resolution in screen\n *  coordinates.\n *\n *  Any or all of the position and size arguments may be `NULL`.  If an error\n *  occurs, all non-`NULL` position and size arguments will be set to zero.\n *\n *  @param[in] monitor The monitor to query.\n *  @param[out] xpos Where to store the monitor x-coordinate, or `NULL`.\n *  @param[out] ypos Where to store the monitor y-coordinate, or `NULL`.\n *  @param[out] width Where to store the monitor width, or `NULL`.\n *  @param[out] height Where to store the monitor height, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_workarea\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup monitor\n */\nGLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height);\n\n/*! @brief Returns the physical size of the monitor.\n *\n *  This function returns the size, in millimetres, of the display area of the\n *  specified monitor.\n *\n *  Some systems do not provide accurate monitor size information, either\n *  because the monitor\n *  [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data)\n *  data is incorrect or because the driver does not report it accurately.\n *\n *  Any or all of the size arguments may be `NULL`.  If an error occurs, all\n *  non-`NULL` size arguments will be set to zero.\n *\n *  @param[in] monitor The monitor to query.\n *  @param[out] widthMM Where to store the width, in millimetres, of the\n *  monitor's display area, or `NULL`.\n *  @param[out] heightMM Where to store the height, in millimetres, of the\n *  monitor's display area, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @remark @win32 calculates the returned physical size from the\n *  current resolution and system DPI instead of querying the monitor EDID data.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_properties\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM);\n\n/*! @brief Retrieves the content scale for the specified monitor.\n *\n *  This function retrieves the content scale for the specified monitor.  The\n *  content scale is the ratio between the current DPI and the platform's\n *  default DPI.  This is especially important for text and any UI elements.  If\n *  the pixel dimensions of your UI scaled by this look appropriate on your\n *  machine then it should appear at a reasonable size on other machines\n *  regardless of their DPI and scaling settings.  This relies on the system DPI\n *  and scaling settings being somewhat correct.\n *\n *  The content scale may depend on both the monitor resolution and pixel\n *  density and on user settings.  It may be very different from the raw DPI\n *  calculated from the physical size and current resolution.\n *\n *  @param[in] monitor The monitor to query.\n *  @param[out] xscale Where to store the x-axis content scale, or `NULL`.\n *  @param[out] yscale Where to store the y-axis content scale, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_scale\n *  @sa @ref glfwGetWindowContentScale\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup monitor\n */\nGLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale);\n\n/*! @brief Returns the name of the specified monitor.\n *\n *  This function returns a human-readable name, encoded as UTF-8, of the\n *  specified monitor.  The name typically reflects the make and model of the\n *  monitor and is not guaranteed to be unique among the connected monitors.\n *\n *  @param[in] monitor The monitor to query.\n *  @return The UTF-8 encoded name of the monitor, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @pointer_lifetime The returned string is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified monitor is\n *  disconnected or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_properties\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor);\nGLFWAPI const char* glfwGetMonitorDescription(GLFWmonitor* monitor);\n\n/*! @brief Sets the user pointer of the specified monitor.\n *\n *  This function sets the user-defined pointer of the specified monitor.  The\n *  current value is retained until the monitor is disconnected.  The initial\n *  value is `NULL`.\n *\n *  This function may be called from the monitor callback, even for a monitor\n *  that is being disconnected.\n *\n *  @param[in] monitor The monitor whose pointer to set.\n *  @param[in] pointer The new value.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Access is not\n *  synchronized.\n *\n *  @sa @ref monitor_userptr\n *  @sa @ref glfwGetMonitorUserPointer\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup monitor\n */\nGLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer);\n\n/*! @brief Returns the user pointer of the specified monitor.\n *\n *  This function returns the current value of the user-defined pointer of the\n *  specified monitor.  The initial value is `NULL`.\n *\n *  This function may be called from the monitor callback, even for a monitor\n *  that is being disconnected.\n *\n *  @param[in] monitor The monitor whose pointer to return.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Access is not\n *  synchronized.\n *\n *  @sa @ref monitor_userptr\n *  @sa @ref glfwSetMonitorUserPointer\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup monitor\n */\nGLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor);\n\n/*! @brief Sets the monitor configuration callback.\n *\n *  This function sets the monitor configuration callback, or removes the\n *  currently set callback.  This is called when a monitor is connected to or\n *  disconnected from the system.\n *\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWmonitor* monitor, int event)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWmonitorfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_event\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback);\n\n/*! @brief Returns the available video modes for the specified monitor.\n *\n *  This function returns an array of all video modes supported by the specified\n *  monitor.  The returned array is sorted in ascending order, first by color\n *  bit depth (the sum of all channel depths) and then by resolution area (the\n *  product of width and height).\n *\n *  @param[in] monitor The monitor to query.\n *  @param[out] count Where to store the number of video modes in the returned\n *  array.  This is set to zero if an error occurred.\n *  @return An array of video modes, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The returned array is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified monitor is\n *  disconnected, this function is called again for that monitor or the library\n *  is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_modes\n *  @sa @ref glfwGetVideoMode\n *\n *  @since Added in version 1.0.\n *  @glfw3 Changed to return an array of modes for a specific monitor.\n *\n *  @ingroup monitor\n */\nGLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count);\n\n/*! @brief Returns the current mode of the specified monitor.\n *\n *  This function returns the current video mode of the specified monitor.  If\n *  you have created a full screen window for that monitor, the return value\n *  will depend on whether that window is iconified.\n *\n *  @param[in] monitor The monitor to query.\n *  @return The current mode of the monitor, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The returned array is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified monitor is\n *  disconnected or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_modes\n *  @sa @ref glfwGetVideoModes\n *\n *  @since Added in version 3.0.  Replaces `glfwGetDesktopMode`.\n *\n *  @ingroup monitor\n */\nGLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor);\n\n/*! @brief Generates a gamma ramp and sets it for the specified monitor.\n *\n *  This function generates an appropriately sized gamma ramp from the specified\n *  exponent and then calls @ref glfwSetGammaRamp with it.  The value must be\n *  a finite number greater than zero.\n *\n *  The software controlled gamma ramp is applied _in addition_ to the hardware\n *  gamma correction, which today is usually an approximation of sRGB gamma.\n *  This means that setting a perfectly linear ramp, or gamma 1.0, will produce\n *  the default (usually sRGB-like) behavior.\n *\n *  For gamma correct rendering with OpenGL or OpenGL ES, see the @ref\n *  GLFW_SRGB_CAPABLE hint.\n *\n *  @param[in] monitor The monitor whose gamma ramp to set.\n *  @param[in] gamma The desired exponent.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark @wayland Gamma handling is a privileged protocol, this function\n *  will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_gamma\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma);\n\n/*! @brief Returns the current gamma ramp for the specified monitor.\n *\n *  This function returns the current gamma ramp of the specified monitor.\n *\n *  @param[in] monitor The monitor to query.\n *  @return The current gamma ramp, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark @wayland Gamma handling is a privileged protocol, this function\n *  will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while\n *  returning `NULL`.\n *\n *  @pointer_lifetime The returned structure and its arrays are allocated and\n *  freed by GLFW.  You should not free them yourself.  They are valid until the\n *  specified monitor is disconnected, this function is called again for that\n *  monitor or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_gamma\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor);\n\n/*! @brief Sets the current gamma ramp for the specified monitor.\n *\n *  This function sets the current gamma ramp for the specified monitor.  The\n *  original gamma ramp for that monitor is saved by GLFW the first time this\n *  function is called and is restored by @ref glfwTerminate.\n *\n *  The software controlled gamma ramp is applied _in addition_ to the hardware\n *  gamma correction, which today is usually an approximation of sRGB gamma.\n *  This means that setting a perfectly linear ramp, or gamma 1.0, will produce\n *  the default (usually sRGB-like) behavior.\n *\n *  For gamma correct rendering with OpenGL or OpenGL ES, see the @ref\n *  GLFW_SRGB_CAPABLE hint.\n *\n *  @param[in] monitor The monitor whose gamma ramp to set.\n *  @param[in] ramp The gamma ramp to use.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark The size of the specified gamma ramp should match the size of the\n *  current ramp for that monitor.\n *\n *  @remark @win32 The gamma ramp size must be 256.\n *\n *  @remark @wayland Gamma handling is a privileged protocol, this function\n *  will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The specified gamma ramp is copied before this function\n *  returns.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref monitor_gamma\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\nGLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp);\n\n/*! @brief Resets all window hints to their default values.\n *\n *  This function resets all window hints to their\n *  [default values](@ref window_hints_values).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_hints\n *  @sa @ref glfwWindowHint\n *  @sa @ref glfwWindowHintString\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwDefaultWindowHints(void);\n\n/*! @brief Sets the specified window hint to the desired value.\n *\n *  This function sets hints for the next call to @ref glfwCreateWindow.  The\n *  hints, once set, retain their values until changed by a call to this\n *  function or @ref glfwDefaultWindowHints, or until the library is terminated.\n *\n *  Only integer value hints can be set with this function.  String value hints\n *  are set with @ref glfwWindowHintString.\n *\n *  This function does not check whether the specified hint values are valid.\n *  If you set hints to invalid values this will instead be reported by the next\n *  call to @ref glfwCreateWindow.\n *\n *  Some hints are platform specific.  These may be set on any platform but they\n *  will only affect their specific platform.  Other platforms will ignore them.\n *  Setting these hints requires no platform specific headers or functions.\n *\n *  @param[in] hint The [window hint](@ref window_hints) to set.\n *  @param[in] value The new value of the window hint.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_INVALID_ENUM.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_hints\n *  @sa @ref glfwWindowHintString\n *  @sa @ref glfwDefaultWindowHints\n *\n *  @since Added in version 3.0.  Replaces `glfwOpenWindowHint`.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwWindowHint(int hint, int value);\n\n/*! @brief Sets the specified window hint to the desired value.\n *\n *  This function sets hints for the next call to @ref glfwCreateWindow.  The\n *  hints, once set, retain their values until changed by a call to this\n *  function or @ref glfwDefaultWindowHints, or until the library is terminated.\n *\n *  Only string type hints can be set with this function.  Integer value hints\n *  are set with @ref glfwWindowHint.\n *\n *  This function does not check whether the specified hint values are valid.\n *  If you set hints to invalid values this will instead be reported by the next\n *  call to @ref glfwCreateWindow.\n *\n *  Some hints are platform specific.  These may be set on any platform but they\n *  will only affect their specific platform.  Other platforms will ignore them.\n *  Setting these hints requires no platform specific headers or functions.\n *\n *  @param[in] hint The [window hint](@ref window_hints) to set.\n *  @param[in] value The new value of the window hint.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_INVALID_ENUM.\n *\n *  @pointer_lifetime The specified string is copied before this function\n *  returns.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_hints\n *  @sa @ref glfwWindowHint\n *  @sa @ref glfwDefaultWindowHints\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwWindowHintString(int hint, const char* value);\n\n/*! @brief Creates a window and its associated context.\n *\n *  This function creates a window and its associated OpenGL or OpenGL ES\n *  context.  Most of the options controlling how the window and its context\n *  should be created are specified with [window hints](@ref window_hints).\n *\n *  Successful creation does not change which context is current.  Before you\n *  can use the newly created context, you need to\n *  [make it current](@ref context_current).  For information about the `share`\n *  parameter, see @ref context_sharing.\n *\n *  The created window, framebuffer and context may differ from what you\n *  requested, as not all parameters and hints are\n *  [hard constraints](@ref window_hints_hard).  This includes the size of the\n *  window, especially for full screen windows.  To query the actual attributes\n *  of the created window, framebuffer and context, see @ref\n *  glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize.\n *\n *  To create a full screen window, you need to specify the monitor the window\n *  will cover.  If no monitor is specified, the window will be windowed mode.\n *  Unless you have a way for the user to choose a specific monitor, it is\n *  recommended that you pick the primary monitor.  For more information on how\n *  to query connected monitors, see @ref monitor_monitors.\n *\n *  For full screen windows, the specified size becomes the resolution of the\n *  window's _desired video mode_.  As long as a full screen window is not\n *  iconified, the supported video mode most closely matching the desired video\n *  mode is set for the specified monitor.  For more information about full\n *  screen windows, including the creation of so called _windowed full screen_\n *  or _borderless full screen_ windows, see @ref window_windowed_full_screen.\n *\n *  Once you have created the window, you can switch it between windowed and\n *  full screen mode with @ref glfwSetWindowMonitor.  This will not affect its\n *  OpenGL or OpenGL ES context.\n *\n *  By default, newly created windows use the placement recommended by the\n *  window system.  To create the window at a specific position, make it\n *  initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window\n *  hint, set its [position](@ref window_pos) and then [show](@ref window_hide)\n *  it.\n *\n *  As long as at least one full screen window is not iconified, the screensaver\n *  is prohibited from starting.\n *\n *  Window systems put limits on window sizes.  Very large or very small window\n *  dimensions may be overridden by the window system on creation.  Check the\n *  actual [size](@ref window_size) after creation.\n *\n *  The [swap interval](@ref buffer_swap) is not set during window creation and\n *  the initial value may vary depending on driver settings and defaults.\n *\n *  @param[in] width The desired width, in screen coordinates, of the window.\n *  This must be greater than zero.\n *  @param[in] height The desired height, in screen coordinates, of the window.\n *  This must be greater than zero.\n *  @param[in] title The initial, UTF-8 encoded window title.\n *  @param[in] monitor The monitor to use for full screen mode, or `NULL` for\n *  windowed mode.\n *  @param[in] share The window whose context to share resources with, or `NULL`\n *  to not share resources.\n *  @return The handle of the created window, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref\n *  GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark @win32 Window creation will fail if the Microsoft GDI software\n *  OpenGL implementation is the only one available.\n *\n *  @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it\n *  will be set as the initial icon for the window.  If no such icon is present,\n *  the `IDI_APPLICATION` icon will be used instead.  To set a different icon,\n *  see @ref glfwSetWindowIcon.\n *\n *  @remark @win32 The context to share resources with must not be current on\n *  any other thread.\n *\n *  @remark @macos The OS only supports forward-compatible core profile contexts\n *  for OpenGL versions 3.2 and later.  Before creating an OpenGL context of\n *  version 3.2 or later you must set the\n *  [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and\n *  [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly.\n *  OpenGL 3.0 and 3.1 contexts are not supported at all on macOS.\n *\n *  @remark @macos The GLFW window has no icon, as it is not a document\n *  window, but the dock icon will be the same as the application bundle's icon.\n *  For more information on bundles, see the\n *  [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)\n *  in the Mac Developer Library.\n *\n *  @remark @macos The first time a window is created the menu bar is created.\n *  If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu\n *  bar.  Otherwise a minimal menu bar is created manually with common commands\n *  like Hide, Quit and About.  The About entry opens a minimal about dialog\n *  with information from the application's bundle.  Menu bar creation can be\n *  disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint.\n *\n *  @remark @macos On OS X 10.10 and later the window frame will not be rendered\n *  at full resolution on Retina displays unless the\n *  [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint)\n *  hint is `true` and the `NSHighResolutionCapable` key is enabled in the\n *  application bundle's `Info.plist`.  For more information, see\n *  [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html)\n *  in the Mac Developer Library.  The GLFW test and example programs use\n *  a custom `Info.plist` template for this, which can be found as\n *  `CMake/MacOSXBundleInfo.plist.in` in the source tree.\n *\n *  @remark @macos When activating frame autosaving with\n *  [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified\n *  window size and position may be overridden by previously saved values.\n *\n *  @remark @x11 Some window managers will not respect the placement of\n *  initially hidden windows.\n *\n *  @remark @x11 Due to the asynchronous nature of X11, it may take a moment for\n *  a window to reach its requested state.  This means you may not be able to\n *  query the final size, position or other attributes directly after window\n *  creation.\n *\n *  @remark @x11 The class part of the `WM_CLASS` window property will by\n *  default be set to the window title passed to this function.  The instance\n *  part will use the contents of the `RESOURCE_NAME` environment variable, if\n *  present and not empty, or fall back to the window title.  Set the\n *  [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and\n *  [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to\n *  override this.\n *\n *  @remark @wayland Compositors should implement the xdg-decoration protocol\n *  for GLFW to decorate the window properly.  If this protocol isn't\n *  supported, or if the compositor prefers client-side decorations, a very\n *  simple fallback frame will be drawn using the wp_viewporter protocol.  A\n *  compositor can still emit close, maximize or fullscreen events, using for\n *  instance a keybind mechanism.  If neither of these protocols is supported,\n *  the window won't be decorated.\n *\n *  @remark @wayland A full screen window will not attempt to change the mode,\n *  no matter what the requested size or refresh rate.\n *\n *  @remark @wayland Screensaver inhibition requires the idle-inhibit protocol\n *  to be implemented in the user's compositor.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_creation\n *  @sa @ref glfwDestroyWindow\n *\n *  @since Added in version 3.0.  Replaces `glfwOpenWindow`.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share, const GLFWLayerShellConfig *lsc);\nGLFWAPI bool glfwToggleFullscreen(GLFWwindow *window, unsigned int flags);\nGLFWAPI bool glfwIsFullscreen(GLFWwindow *window, unsigned int flags);\nGLFWAPI bool glfwAreSwapsAllowed(const GLFWwindow* window);\nGLFWAPI const GLFWLayerShellConfig* glfwGetLayerShellConfig(GLFWwindow* handle);\nGLFWAPI bool glfwSetLayerShellConfig(GLFWwindow* handle, const GLFWLayerShellConfig *value);\n\n/*! @brief Destroys the specified window and its context.\n *\n *  This function destroys the specified window and its context.  On calling\n *  this function, no further callbacks will be called for that window.\n *\n *  If the context of the specified window is current on the main thread, it is\n *  detached before being destroyed.\n *\n *  @param[in] window The window to destroy.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @note The context of the specified window must not be current on any other\n *  thread when this function is called.\n *\n *  @reentrancy This function must not be called from a callback.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_creation\n *  @sa @ref glfwCreateWindow\n *\n *  @since Added in version 3.0.  Replaces `glfwCloseWindow`.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwDestroyWindow(GLFWwindow* window);\n\n/*! @brief Checks the close flag of the specified window.\n *\n *  This function returns the value of the close flag of the specified window.\n *\n *  @param[in] window The window to query.\n *  @return The value of the close flag.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Access is not\n *  synchronized.\n *\n *  @sa @ref window_close\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI int glfwWindowShouldClose(GLFWwindow* window);\n\n/*! @brief Sets the close flag of the specified window.\n *\n *  This function sets the value of the close flag of the specified window.\n *  This can be used to override the user's attempt to close the window, or\n *  to signal that it should be closed.\n *\n *  @param[in] window The window whose flag to change.\n *  @param[in] value The new value.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Access is not\n *  synchronized.\n *\n *  @sa @ref window_close\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value);\n\n/*! @brief Sets the title of the specified window.\n *\n *  This function sets the window title, encoded as UTF-8, of the specified\n *  window.\n *\n *  @param[in] window The window whose title to change.\n *  @param[in] title The UTF-8 encoded window title.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark @macos The window title will not be updated until the next time you\n *  process events.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_title\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);\n\n/*! @brief Sets the icon for the specified window.\n *\n *  This function sets the icon of the specified window.  If passed an array of\n *  candidate images, those of or closest to the sizes desired by the system are\n *  selected.  If no images are specified, the window reverts to its default\n *  icon.\n *\n *  The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight\n *  bits per channel with the red channel first.  They are arranged canonically\n *  as packed sequential rows, starting from the top-left corner.\n *\n *  The desired image sizes varies depending on platform and system settings.\n *  The selected images will be rescaled as needed.  Good sizes include 16x16,\n *  32x32 and 48x48.\n *\n *  @param[in] window The window whose icon to set.\n *  @param[in] count The number of images in the specified array, or zero to\n *  revert to the default window icon.\n *  @param[in] images The images to create the icon from.  This is ignored if\n *  count is zero.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).\n *\n *  @pointer_lifetime The specified image data is copied before this function\n *  returns.\n *\n *  @remark @macos Regular windows do not have icons on macOS.  This function\n *  will emit @ref GLFW_FEATURE_UNAVAILABLE.  The dock icon will be the same as\n *  the application bundle's icon.  For more information on bundles, see the\n *  [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)\n *  in the Mac Developer Library.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_icon\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images);\n\n/*! @brief Retrieves the position of the content area of the specified window.\n *\n *  This function retrieves the position, in screen coordinates, of the\n *  upper-left corner of the content area of the specified window.\n *\n *  Any or all of the position arguments may be `NULL`.  If an error occurs, all\n *  non-`NULL` position arguments will be set to zero.\n *\n *  @param[in] window The window to query.\n *  @param[out] xpos Where to store the x-coordinate of the upper-left corner of\n *  the content area, or `NULL`.\n *  @param[out] ypos Where to store the y-coordinate of the upper-left corner of\n *  the content area, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).\n *\n *  @remark @wayland There is no way for an application to retrieve the global\n *  position of its windows.  This function will emit @ref\n *  GLFW_FEATURE_UNAVAILABLE.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_pos\n *  @sa @ref glfwSetWindowPos\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos);\n\n/*! @brief Sets the position of the content area of the specified window.\n *\n *  This function sets the position, in screen coordinates, of the upper-left\n *  corner of the content area of the specified windowed mode window.  If the\n *  window is a full screen window, this function does nothing.\n *\n *  __Do not use this function__ to move an already visible window unless you\n *  have very good reasons for doing so, as it will confuse and annoy the user.\n *\n *  The window manager may put limits on what positions are allowed.  GLFW\n *  cannot and should not override these limits.\n *\n *  @param[in] window The window to query.\n *  @param[in] xpos The x-coordinate of the upper-left corner of the content area.\n *  @param[in] ypos The y-coordinate of the upper-left corner of the content area.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).\n *\n *  @remark @wayland There is no way for an application to set the global\n *  position of its windows.  This function will emit @ref\n *  GLFW_FEATURE_UNAVAILABLE.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_pos\n *  @sa @ref glfwGetWindowPos\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos);\n\n/*! @brief Retrieves the size of the content area of the specified window.\n *\n *  This function retrieves the size, in screen coordinates, of the content area\n *  of the specified window.  If you wish to retrieve the size of the\n *  framebuffer of the window in pixels, see @ref glfwGetFramebufferSize.\n *\n *  Any or all of the size arguments may be `NULL`.  If an error occurs, all\n *  non-`NULL` size arguments will be set to zero.\n *\n *  @param[in] window The window whose size to retrieve.\n *  @param[out] width Where to store the width, in screen coordinates, of the\n *  content area, or `NULL`.\n *  @param[out] height Where to store the height, in screen coordinates, of the\n *  content area, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_size\n *  @sa @ref glfwSetWindowSize\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height);\n\n/*! @brief Sets the size limits of the specified window.\n *\n *  This function sets the size limits of the content area of the specified\n *  window.  If the window is full screen, the size limits only take effect\n *  once it is made windowed.  If the window is not resizable, this function\n *  does nothing.\n *\n *  The size limits are applied immediately to a windowed mode window and may\n *  cause it to be resized.\n *\n *  The maximum dimensions must be greater than or equal to the minimum\n *  dimensions and all must be greater than or equal to zero.\n *\n *  @param[in] window The window to set limits for.\n *  @param[in] minwidth The minimum width, in screen coordinates, of the content\n *  area, or `GLFW_DONT_CARE`.\n *  @param[in] minheight The minimum height, in screen coordinates, of the\n *  content area, or `GLFW_DONT_CARE`.\n *  @param[in] maxwidth The maximum width, in screen coordinates, of the content\n *  area, or `GLFW_DONT_CARE`.\n *  @param[in] maxheight The maximum height, in screen coordinates, of the\n *  content area, or `GLFW_DONT_CARE`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark If you set size limits and an aspect ratio that conflict, the\n *  results are undefined.\n *\n *  @remark @wayland The size limits will not be applied until the window is\n *  actually resized, either by the user or by the compositor.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_sizelimits\n *  @sa @ref glfwSetWindowAspectRatio\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight);\n\n/*! @brief Sets the aspect ratio of the specified window.\n *\n *  This function sets the required aspect ratio of the content area of the\n *  specified window.  If the window is full screen, the aspect ratio only takes\n *  effect once it is made windowed.  If the window is not resizable, this\n *  function does nothing.\n *\n *  The aspect ratio is specified as a numerator and a denominator and both\n *  values must be greater than zero.  For example, the common 16:9 aspect ratio\n *  is specified as 16 and 9, respectively.\n *\n *  If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect\n *  ratio limit is disabled.\n *\n *  The aspect ratio is applied immediately to a windowed mode window and may\n *  cause it to be resized.\n *\n *  @param[in] window The window to set limits for.\n *  @param[in] numer The numerator of the desired aspect ratio, or\n *  `GLFW_DONT_CARE`.\n *  @param[in] denom The denominator of the desired aspect ratio, or\n *  `GLFW_DONT_CARE`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark If you set size limits and an aspect ratio that conflict, the\n *  results are undefined.\n *\n *  @remark @wayland The aspect ratio will not be applied until the window is\n *  actually resized, either by the user or by the compositor.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_sizelimits\n *  @sa @ref glfwSetWindowSizeLimits\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup window\n */\n\nGLFWAPI void glfwSetWindowSizeIncrements(GLFWwindow* window, int widthincr, int heightincr);\n\n/*! @brief Sets the size increments of the specified window.\n *\n *  This function sets the size increments of the content area of the specified\n *  window.  If the window is full screen, the size limits only take effect\n *  once it is made windowed.  If the window is not resizable, this function\n *  does nothing.\n *\n *  The size increments are applied immediately to a windowed mode window and\n *  may cause it to be resized.\n *\n *  The dimension increments must be greater than zero.\n *\n *  @param[in] window The window to set limits for.\n *  @param[in] widthincr The width increments, in screen coordinates, of the\n *  content area, or `GLFW_DONT_CARE`.\n *  @param[in] heightincr The height increments, in screen coordinates, of the\n *  content area, or `GLFW_DONT_CARE`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark If you set size limits and an aspect ratio that conflict, the\n *  results are undefined.\n *\n *  @remark @wayland The size limits will not be applied until the window is\n *  actually resized, either by the user or by the compositor.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_sizelimits\n *  @sa @ref glfwSetWindowSizeLimits\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup window\n */\n\nGLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom);\n\n/*! @brief Sets the size of the content area of the specified window.\n *\n *  This function sets the size, in screen coordinates, of the content area of\n *  the specified window.\n *\n *  For full screen windows, this function updates the resolution of its desired\n *  video mode and switches to the video mode closest to it, without affecting\n *  the window's context.  As the context is unaffected, the bit depths of the\n *  framebuffer remain unchanged.\n *\n *  If you wish to update the refresh rate of the desired video mode in addition\n *  to its resolution, see @ref glfwSetWindowMonitor.\n *\n *  The window manager may put limits on what sizes are allowed.  GLFW cannot\n *  and should not override these limits.\n *\n *  @param[in] window The window to resize.\n *  @param[in] width The desired width, in screen coordinates, of the window\n *  content area.\n *  @param[in] height The desired height, in screen coordinates, of the window\n *  content area.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark @wayland A full screen window will not attempt to change the mode,\n *  no matter what the requested size.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_size\n *  @sa @ref glfwGetWindowSize\n *  @sa @ref glfwSetWindowMonitor\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height);\n\n/*! @brief Retrieves the size of the framebuffer of the specified window.\n *\n *  This function retrieves the size, in pixels, of the framebuffer of the\n *  specified window.  If you wish to retrieve the size of the window in screen\n *  coordinates, see @ref glfwGetWindowSize.\n *\n *  Any or all of the size arguments may be `NULL`.  If an error occurs, all\n *  non-`NULL` size arguments will be set to zero.\n *\n *  @param[in] window The window whose framebuffer to query.\n *  @param[out] width Where to store the width, in pixels, of the framebuffer,\n *  or `NULL`.\n *  @param[out] height Where to store the height, in pixels, of the framebuffer,\n *  or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_fbsize\n *  @sa @ref glfwSetFramebufferSizeCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height);\n\n/*! @brief Retrieves the size of the frame of the window.\n *\n *  This function retrieves the size, in screen coordinates, of each edge of the\n *  frame of the specified window.  This size includes the title bar, if the\n *  window has one.  The size of the frame may vary depending on the\n *  [window-related hints](@ref window_hints_wnd) used to create it.\n *\n *  Because this function retrieves the size of each window frame edge and not\n *  the offset along a particular coordinate axis, the retrieved values will\n *  always be zero or positive.\n *\n *  Any or all of the size arguments may be `NULL`.  If an error occurs, all\n *  non-`NULL` size arguments will be set to zero.\n *\n *  @param[in] window The window whose frame size to query.\n *  @param[out] left Where to store the size, in screen coordinates, of the left\n *  edge of the window frame, or `NULL`.\n *  @param[out] top Where to store the size, in screen coordinates, of the top\n *  edge of the window frame, or `NULL`.\n *  @param[out] right Where to store the size, in screen coordinates, of the\n *  right edge of the window frame, or `NULL`.\n *  @param[out] bottom Where to store the size, in screen coordinates, of the\n *  bottom edge of the window frame, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_size\n *\n *  @since Added in version 3.1.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom);\n\n/*! @brief Retrieves the content scale for the specified window.\n *\n *  This function retrieves the content scale for the specified window.  The\n *  content scale is the ratio between the current DPI and the platform's\n *  default DPI.  This is especially important for text and any UI elements.  If\n *  the pixel dimensions of your UI scaled by this look appropriate on your\n *  machine then it should appear at a reasonable size on other machines\n *  regardless of their DPI and scaling settings.  This relies on the system DPI\n *  and scaling settings being somewhat correct.\n *\n *  On systems where each monitors can have its own content scale, the window\n *  content scale will depend on which monitor the system considers the window\n *  to be on.\n *\n *  @param[in] window The window to query.\n *  @param[out] xscale Where to store the x-axis content scale, or `NULL`.\n *  @param[out] yscale Where to store the y-axis content scale, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_scale\n *  @sa @ref glfwSetWindowContentScaleCallback\n *  @sa @ref glfwGetMonitorContentScale\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale);\n\n/*! @brief Returns the double click time interval.\n *\n *  This function returns the maximum time between clicks to count as a\n *  double click.\n *\n *  The double click interval is a positive finite number greater than zero,\n *  where zero means that no click is ever recognized as a double click. If the\n *  system does not support a double click interval, this function always returns one half.\n *\n *  @return The double click interval.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref double_click\n *  @sa @ref click_interval\n *  @sa @ref double_click_interval\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI monotonic_t glfwGetDoubleClickInterval(GLFWwindow* window);\n\n/*! @brief Returns the opacity of the whole window.\n *\n *  This function returns the opacity of the window, including any decorations.\n *\n *  The opacity (or alpha) value is a positive finite number between zero and\n *  one, where zero is fully transparent and one is fully opaque.  If the system\n *  does not support whole window transparency, this function always returns one.\n *\n *  The initial opacity value for newly created windows is one.\n *\n *  @param[in] window The window to query.\n *  @return The opacity value of the specified window.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_transparency\n *  @sa @ref glfwSetWindowOpacity\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI float glfwGetWindowOpacity(GLFWwindow* window);\n\n/*! @brief Sets the opacity of the whole window.\n *\n *  This function sets the opacity of the window, including any decorations.\n *\n *  The opacity (or alpha) value is a positive finite number between zero and\n *  one, where zero is fully transparent and one is fully opaque.\n *\n *  The initial opacity value for newly created windows is one.\n *\n *  A window created with framebuffer transparency may not use whole window\n *  transparency.  The results of doing this are undefined.\n *\n *  @param[in] window The window to set the opacity for.\n *  @param[in] opacity The desired opacity of the specified window.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).\n *\n *  @remark @wayland There is no way to set an opacity factor for a window.\n *  This function will emit @ref GLFW_FEATURE_UNAVAILABLE.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_transparency\n *  @sa @ref glfwGetWindowOpacity\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity);\n\n/*! @brief Iconifies the specified window.\n *\n *  This function iconifies (minimizes) the specified window if it was\n *  previously restored.  If the window is already iconified, this function does\n *  nothing.\n *\n *  If the specified window is a full screen window, the original monitor\n *  resolution is restored until the window is restored.\n *\n *  @param[in] window The window to iconify.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark @wayland Once a window is iconified, @ref glfwRestoreWindow won’t\n *  be able to restore it.  This is a design decision of the xdg-shell\n *  protocol.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_iconify\n *  @sa @ref glfwRestoreWindow\n *  @sa @ref glfwMaximizeWindow\n *\n *  @since Added in version 2.1.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwIconifyWindow(GLFWwindow* window);\n\n/*! @brief Restores the specified window.\n *\n *  This function restores the specified window if it was previously iconified\n *  (minimized) or maximized.  If the window is already restored, this function\n *  does nothing.\n *\n *  If the specified window is a full screen window, the resolution chosen for\n *  the window is restored on the selected monitor.\n *\n *  @param[in] window The window to restore.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_iconify\n *  @sa @ref glfwIconifyWindow\n *  @sa @ref glfwMaximizeWindow\n *\n *  @since Added in version 2.1.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwRestoreWindow(GLFWwindow* window);\n\n/*! @brief Maximizes the specified window.\n *\n *  This function maximizes the specified window if it was previously not\n *  maximized.  If the window is already maximized, this function does nothing.\n *\n *  If the specified window is a full screen window, this function does nothing.\n *\n *  @param[in] window The window to maximize.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @par Thread Safety\n *  This function may only be called from the main thread.\n *\n *  @sa @ref window_iconify\n *  @sa @ref glfwIconifyWindow\n *  @sa @ref glfwRestoreWindow\n *\n *  @since Added in GLFW 3.2.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwMaximizeWindow(GLFWwindow* window);\n\n/*! @brief Makes the specified window visible.\n *\n *  This function makes the specified window visible if it was previously\n *  hidden.  If the window is already visible or is in full screen mode, this\n *  function does nothing.\n *\n *  By default, windowed mode windows are focused when shown\n *  Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint\n *  to change this behavior for all newly created windows, or change the\n *  behavior for an existing window with @ref glfwSetWindowAttrib.\n *\n *  @param[in] window The window to make visible.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_hide\n *  @sa @ref glfwHideWindow\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwShowWindow(GLFWwindow* window, bool move_to_active_screen);\n\n/*! @brief Hides the specified window.\n *\n *  This function hides the specified window if it was previously visible.  If\n *  the window is already hidden or is in full screen mode, this function does\n *  nothing.\n *\n *  @param[in] window The window to hide.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_hide\n *  @sa @ref glfwShowWindow\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwHideWindow(GLFWwindow* window);\n\n/*! @brief Brings the specified window to front and sets input focus.\n *\n *  This function brings the specified window to front and sets input focus.\n *  The window should already be visible and not iconified.\n *\n *  By default, both windowed and full screen mode windows are focused when\n *  initially created.  Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to\n *  disable this behavior.\n *\n *  Also by default, windowed mode windows are focused when shown\n *  with @ref glfwShowWindow. Set the\n *  [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior.\n *\n *  __Do not use this function__ to steal focus from other applications unless\n *  you are certain that is what the user wants.  Focus stealing can be\n *  extremely disruptive.\n *\n *  For a less disruptive way of getting the user's attention, see\n *  [attention requests](@ref window_attention).\n *\n *  @param[in] window The window to give input focus.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).\n *\n *  @remark @wayland It is not possible for an application to set the input\n *  focus.  This function will emit @ref GLFW_FEATURE_UNAVAILABLE.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_focus\n *  @sa @ref window_attention\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwFocusWindow(GLFWwindow* window);\n\n/*! @brief Requests user attention to the specified window.\n *\n *  This function requests user attention to the specified window.  On\n *  platforms where this is not supported, attention is requested to the\n *  application as a whole.\n *\n *  Once the user has given attention, usually by focusing the window or\n *  application, the system will end the request automatically.\n *\n *  @param[in] window The window to request attention to.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark @macos Attention is requested to the application as a whole, not the\n *  specific window.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_attention\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);\n\n/*! @brief Sounds an audible bell associated with the window\n *\n *  This function sounds an audible bell, on platforms where it is\n *  supported. Currently (macOS, Windows, X11 and Wayland).\n *\n *  @param[in] window The window with which the bell is associated.\n *  @return true if the bell succeeded otherwise false\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark @macos Bell is associated to the application as a whole, not the\n *  specific window.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI int glfwWindowBell(GLFWwindow* window);\n\n\n/*! @brief Returns the monitor that the window uses for full screen mode.\n *\n *  This function returns the handle of the monitor that the specified window is\n *  in full screen on.\n *\n *  @param[in] window The window to query.\n *  @return The monitor, or `NULL` if the window is in windowed mode or an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_monitor\n *  @sa @ref glfwSetWindowMonitor\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window);\n\n/*! @brief Sets the mode, monitor, video mode and placement of a window.\n *\n *  This function sets the monitor that the window uses for full screen mode or,\n *  if the monitor is `NULL`, makes it windowed mode.\n *\n *  When setting a monitor, this function updates the width, height and refresh\n *  rate of the desired video mode and switches to the video mode closest to it.\n *  The window position is ignored when setting a monitor.\n *\n *  When the monitor is `NULL`, the position, width and height are used to\n *  place the window content area.  The refresh rate is ignored when no monitor\n *  is specified.\n *\n *  If you only wish to update the resolution of a full screen window or the\n *  size of a windowed mode window, see @ref glfwSetWindowSize.\n *\n *  When a window transitions from full screen to windowed mode, this function\n *  restores any previous window settings such as whether it is decorated,\n *  floating, resizable, has size or aspect ratio limits, etc.\n *\n *  @param[in] window The window whose monitor, size or video mode to set.\n *  @param[in] monitor The desired monitor, or `NULL` to set windowed mode.\n *  @param[in] xpos The desired x-coordinate of the upper-left corner of the\n *  content area.\n *  @param[in] ypos The desired y-coordinate of the upper-left corner of the\n *  content area.\n *  @param[in] width The desired with, in screen coordinates, of the content\n *  area or video mode.\n *  @param[in] height The desired height, in screen coordinates, of the content\n *  area or video mode.\n *  @param[in] refreshRate The desired refresh rate, in Hz, of the video mode,\n *  or `GLFW_DONT_CARE`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise\n *  affected by any resizing or mode switching, although you may need to update\n *  your viewport if the framebuffer size has changed.\n *\n *  @remark @wayland The desired window position is ignored, as there is no way\n *  for an application to set this property.\n *\n *  @remark @wayland Setting the window to full screen will not attempt to\n *  change the mode, no matter what the requested size or refresh rate.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_monitor\n *  @sa @ref window_full_screen\n *  @sa @ref glfwGetWindowMonitor\n *  @sa @ref glfwSetWindowSize\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);\n\n/*! @brief Returns an attribute of the specified window.\n *\n *  This function returns the value of an attribute of the specified window or\n *  its OpenGL or OpenGL ES context.\n *\n *  @param[in] window The window to query.\n *  @param[in] attrib The [window attribute](@ref window_attribs) whose value to\n *  return.\n *  @return The value of the attribute, or zero if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark Framebuffer related hints are not window attributes.  See @ref\n *  window_attribs_fb for more information.\n *\n *  @remark Zero is a valid value for many window and context related\n *  attributes so you cannot use a return value of zero as an indication of\n *  errors.  However, this function should not fail as long as it is passed\n *  valid arguments and the library has been [initialized](@ref intro_init).\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_attribs\n *  @sa @ref glfwSetWindowAttrib\n *\n *  @since Added in version 3.0.  Replaces `glfwGetWindowParam` and\n *  `glfwGetGLVersion`.\n *\n *  @ingroup window\n */\nGLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);\n\n/*! @brief Sets an attribute of the specified window.\n *\n *  This function sets the value of an attribute of the specified window.\n *\n *  The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),\n *  [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib),\n *  [GLFW_FLOATING](@ref GLFW_FLOATING_attrib),\n *  [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and\n *  [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib).\n *  [GLFW_MOUSE_PASSTHROUGH](@ref GLFW_MOUSE_PASSTHROUGH_attrib)\n *\n *  Some of these attributes are ignored for full screen windows.  The new\n *  value will take effect if the window is later made windowed.\n *\n *  Some of these attributes are ignored for windowed mode windows.  The new\n *  value will take effect if the window is later made full screen.\n *\n *  @param[in] window The window to set the attribute for.\n *  @param[in] attrib A supported window attribute.\n *  @param[in] value `true` or `false`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark Calling @ref glfwGetWindowAttrib will always return the latest\n *  value, even if that value is ignored by the current mode of the window.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_attribs\n *  @sa @ref glfwGetWindowAttrib\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value);\nGLFWAPI int glfwSetWindowBlur(GLFWwindow* window, int value);\n\n/*! @brief Sets the user pointer of the specified window.\n *\n *  This function sets the user-defined pointer of the specified window.  The\n *  current value is retained until the window is destroyed.  The initial value\n *  is `NULL`.\n *\n *  @param[in] window The window whose pointer to set.\n *  @param[in] pointer The new value.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Access is not\n *  synchronized.\n *\n *  @sa @ref window_userptr\n *  @sa @ref glfwGetWindowUserPointer\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer);\n\n/*! @brief Returns the user pointer of the specified window.\n *\n *  This function returns the current value of the user-defined pointer of the\n *  specified window.  The initial value is `NULL`.\n *\n *  @param[in] window The window whose pointer to return.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Access is not\n *  synchronized.\n *\n *  @sa @ref window_userptr\n *  @sa @ref glfwSetWindowUserPointer\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window);\n\n/*! @brief Sets the position callback for the specified window.\n *\n *  This function sets the position callback of the specified window, which is\n *  called when the window is moved.  The callback is provided with the\n *  position, in screen coordinates, of the upper-left corner of the content\n *  area of the window.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int xpos, int ypos)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowposfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @remark @wayland This callback will never be called, as there is no way for\n *  an application to know its global position.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_pos\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback);\n\n/*! @brief Sets the size callback for the specified window.\n *\n *  This function sets the size callback of the specified window, which is\n *  called when the window is resized.  The callback is provided with the size,\n *  in screen coordinates, of the content area of the window.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int width, int height)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowsizefun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_size\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter and return value.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback);\n\n/*! @brief Sets the close callback for the specified window.\n *\n *  This function sets the close callback of the specified window, which is\n *  called when the user attempts to close the window, for example by clicking\n *  the close widget in the title bar.\n *\n *  The close flag is set before this callback is called, but you can modify it\n *  at any time with @ref glfwSetWindowShouldClose.\n *\n *  The close callback is not triggered by @ref glfwDestroyWindow.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowclosefun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @remark @macos Selecting Quit from the application menu will trigger the\n *  close callback for all windows.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_close\n *\n *  @since Added in version 2.5.\n *  @glfw3 Added window handle parameter and return value.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback);\nGLFWAPI GLFWapplicationclosefun glfwSetApplicationCloseCallback(GLFWapplicationclosefun callback);\nGLFWAPI GLFWsystemcolorthemechangefun glfwSetSystemColorThemeChangeCallback(GLFWsystemcolorthemechangefun callback);\nGLFWAPI GLFWclipboardlostfun glfwSetClipboardLostCallback(GLFWclipboardlostfun callback);\nGLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized);\n\n/*! @brief Sets the refresh callback for the specified window.\n *\n *  This function sets the refresh callback of the specified window, which is\n *  called when the content area of the window needs to be redrawn, for example\n *  if the window has been exposed after having been covered by another window.\n *\n *  On compositing window systems such as Aero, Compiz, Aqua or Wayland, where\n *  the window contents are saved off-screen, this callback may be called only\n *  very infrequently or never at all.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window);\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowrefreshfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_refresh\n *\n *  @since Added in version 2.5.\n *  @glfw3 Added window handle parameter and return value.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback);\n\n/*! @brief Sets the focus callback for the specified window.\n *\n *  This function sets the focus callback of the specified window, which is\n *  called when the window gains or loses input focus.\n *\n *  After the focus callback is called for a window that lost input focus,\n *  synthetic key and mouse button release events will be generated for all such\n *  that had been pressed.  For more information, see @ref glfwSetKeyCallback\n *  and @ref glfwSetMouseButtonCallback.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int focused)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowfocusfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_focus\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback);\n\n/*! @brief Sets the occlusion callback for the specified window.\n *\n *  This function sets the occlusion callback of the specified window, which is\n *  called when the window becomes (fully) occluded by other windows or when (a\n *  part of) the window becomes visible again because an overlapping window is\n *  moved away.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int iconified)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowiconifyfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_occlusion\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowocclusionfun glfwSetWindowOcclusionCallback(GLFWwindow* window, GLFWwindowocclusionfun callback);\n\n/*! @brief Sets the iconify callback for the specified window.\n *\n *  This function sets the iconification callback of the specified window, which\n *  is called when the window is iconified or restored.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int iconified)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowiconifyfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_iconify\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback);\n\n/*! @brief Sets the maximize callback for the specified window.\n *\n *  This function sets the maximization callback of the specified window, which\n *  is called when the window is maximized or restored.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int maximized)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowmaximizefun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_maximize\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback);\n\n/*! @brief Sets the framebuffer resize callback for the specified window.\n *\n *  This function sets the framebuffer resize callback of the specified window,\n *  which is called when the framebuffer of the specified window is resized.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int width, int height)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWframebuffersizefun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_fbsize\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback);\n\n/*! @brief Sets the window content scale callback for the specified window.\n *\n *  This function sets the window content scale callback of the specified window,\n *  which is called when the content scale of the specified window changes.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, float xscale, float yscale)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWwindowcontentscalefun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref window_scale\n *  @sa @ref glfwGetWindowContentScale\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\nGLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback);\n\n/*! @brief Posts an empty event to the event queue.\n *\n *  This function posts an empty event from the current thread to the event\n *  queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref events\n *  @sa @ref glfwWaitEvents\n *  @sa @ref glfwWaitEventsTimeout\n *\n *  @since Added in version 3.1.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwPostEmptyEvent(void);\n\nGLFWAPI bool glfwGetIgnoreOSKeyboardProcessing(void);\nGLFWAPI void glfwSetIgnoreOSKeyboardProcessing(bool enabled);\nGLFWAPI bool glfwGrabKeyboard(int grab);\n\n/*! @brief Returns the value of an input option for the specified window.\n *\n *  This function returns the value of an input option for the specified window.\n *  The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,\n *  @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or\n *  @ref GLFW_RAW_MOUSE_MOTION.\n *\n *  @param[in] window The window to query.\n *  @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,\n *  `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or\n *  `GLFW_RAW_MOUSE_MOTION`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_INVALID_ENUM.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref glfwSetInputMode\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup input\n */\nGLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);\n\n/*! @brief Sets an input option for the specified window.\n *\n *  This function sets an input mode option for the specified window.  The mode\n *  must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,\n *  @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or\n *  @ref GLFW_RAW_MOUSE_MOTION.\n *\n *  If the mode is `GLFW_CURSOR`, the value must be one of the following cursor\n *  modes:\n *  - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally.\n *  - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the\n *    content area of the window but does not restrict the cursor from leaving.\n *  - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual\n *    and unlimited cursor movement.  This is useful for implementing for\n *    example 3D camera controls.\n *\n *  If the mode is `GLFW_STICKY_KEYS`, the value must be either `true` to\n *  enable sticky keys, or `false` to disable it.  If sticky keys are\n *  enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS`\n *  the next time it is called even if the key had been released before the\n *  call.  This is useful when you are only interested in whether keys have been\n *  pressed but not when or in which order.\n *\n *  If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either\n *  `true` to enable sticky mouse buttons, or `false` to disable it.\n *  If sticky mouse buttons are enabled, a mouse button press will ensure that\n *  @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even\n *  if the mouse button had been released before the call.  This is useful when\n *  you are only interested in whether mouse buttons have been pressed but not\n *  when or in which order.\n *\n *  If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `true` to\n *  enable lock key modifier bits, or `false` to disable them.  If enabled,\n *  callbacks that receive modifier bits will also have the @ref\n *  GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on,\n *  and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on.\n *\n *  If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `true`\n *  to enable raw (unscaled and unaccelerated) mouse motion when the cursor is\n *  disabled, or `false` to disable it.  If raw motion is not supported,\n *  attempting to set this will emit @ref GLFW_FEATURE_UNAVAILABLE.  Call @ref\n *  glfwRawMouseMotionSupported to check for support.\n *\n *  @param[in] window The window whose input mode to set.\n *  @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,\n *  `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or\n *  `GLFW_RAW_MOUSE_MOTION`.\n *  @param[in] value The new value of the specified input mode.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM, @ref GLFW_PLATFORM_ERROR and @ref\n *  GLFW_FEATURE_UNAVAILABLE (see above).\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref glfwGetInputMode\n *\n *  @since Added in version 3.0.  Replaces `glfwEnable` and `glfwDisable`.\n *\n *  @ingroup input\n */\nGLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value);\n\n/*! @brief Returns whether raw mouse motion is supported.\n *\n *  This function returns whether raw mouse motion is supported on the current\n *  system.  This status does not change after GLFW has been initialized so you\n *  only need to check this once.  If you attempt to enable raw motion on\n *  a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted.\n *\n *  Raw mouse motion is closer to the actual motion of the mouse across\n *  a surface.  It is not affected by the scaling and acceleration applied to\n *  the motion of the desktop cursor.  That processing is suitable for a cursor\n *  while raw motion is better for controlling for example a 3D camera.  Because\n *  of this, raw mouse motion is only provided when the cursor is disabled.\n *\n *  @return `true` if raw mouse motion is supported on the current machine,\n *  or `false` otherwise.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref raw_mouse_motion\n *  @sa @ref glfwSetInputMode\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI int glfwRawMouseMotionSupported(void);\n\n/*! @brief Returns the layout-specific name of the specified printable key.\n *\n *  This function returns the name of the specified printable key, encoded as\n *  UTF-8.  This is typically the character that key would produce without any\n *  modifier keys, intended for displaying key bindings to the user.  For dead\n *  keys, it is typically the diacritic it would add to a character.\n *\n *  __Do not use this function__ for [text input](@ref input_char).  You will\n *  break text input for many languages even if it happens to work for yours.\n *\n *  If the key is `0`, the keycode is used to identify the key,\n *  otherwise the keycode is ignored.  If you specify a non-printable key, or\n *  `0` and a keycode that maps to a non-printable key, this\n *  function returns `NULL` but does not emit an error.\n *\n *  This behavior allows you to always pass in the arguments in the\n *  [key callback](@ref input_key) without modification.\n *\n *  Names for printable keys depend on keyboard layout, while names for\n *  non-printable keys are the same across layouts but depend on the application\n *  language and should be localized along with other user interface text.\n *\n *  @param[in] key The key to query, or `0`.\n *  @param[in] native_key The platform-specifc identifier of the key to query.\n *  @return The UTF-8 encoded, layout-specific name of the key, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark The contents of the returned string may change when a keyboard\n *  layout change event is received.\n *\n *  @pointer_lifetime The returned string is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref input_key_name\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup input\n */\nGLFWAPI const char* glfwGetKeyName(uint32_t key, int native_key);\n\n/*! @brief Returns the platform-specific identifier of the specified key.\n *\n *  This function returns the platform-specific identifier of the specified key.\n *\n *  If the key is `0` or does not exist on the keyboard this\n *  method will return `-1`.\n *\n *  @param[in] key Any [named key](@ref keys).\n *  @return The platform-specific identifier for the key, or `-1` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref input_key\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI int glfwGetNativeKeyForKey(uint32_t key);\n\n/*! @brief Returns the last reported state of a keyboard key for the specified\n *  window.\n *\n *  This function returns the last state reported for the specified key to the\n *  specified window.  The returned state is one of `GLFW_PRESS` or\n *  `GLFW_RELEASE`.  The higher-level action `GLFW_REPEAT` is only reported to\n *  the key callback.\n *\n *  If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns\n *  `GLFW_PRESS` the first time you call it for a key that was pressed, even if\n *  that key has already been released.\n *\n *  The key functions deal with physical keys, with [key tokens](@ref keys)\n *  named after their use on the standard US keyboard layout.  If you want to\n *  input text, use the Unicode character callback instead.\n *\n *  The [modifier key bit masks](@ref mods) are not key tokens and cannot be\n *  used with this function.\n *\n *  __Do not use this function__ to implement [text input](@ref input_char).\n *\n *  @param[in] window The desired window.\n *  @param[in] key The desired [keyboard key](@ref keys).  `0` is\n *  not a valid key for this function.\n *  @return One of `GLFW_PRESS` or `GLFW_RELEASE`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_INVALID_ENUM.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref input_key\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWKeyAction glfwGetKey(GLFWwindow* window, uint32_t key);\n\n/*! @brief Returns the last reported state of a mouse button for the specified\n *  window.\n *\n *  This function returns the last state reported for the specified mouse button\n *  to the specified window.  The returned state is one of `GLFW_PRESS` or\n *  `GLFW_RELEASE`.\n *\n *  If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function\n *  returns `GLFW_PRESS` the first time you call it for a mouse button that was\n *  pressed, even if that mouse button has already been released.\n *\n *  @param[in] window The desired window.\n *  @param[in] button The desired [mouse button](@ref buttons).\n *  @return One of `GLFW_PRESS` or `GLFW_RELEASE`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_INVALID_ENUM.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref input_mouse_button\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup input\n */\nGLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button);\n\n/*! @brief Retrieves the position of the cursor relative to the content area of\n *  the window.\n *\n *  This function returns the position of the cursor, in screen coordinates,\n *  relative to the upper-left corner of the content area of the specified\n *  window.\n *\n *  If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor\n *  position is unbounded and limited only by the minimum and maximum values of\n *  a `double`.\n *\n *  The coordinate can be converted to their integer equivalents with the\n *  `floor` function.  Casting directly to an integer type works for positive\n *  coordinates, but fails for negative ones.\n *\n *  Any or all of the position arguments may be `NULL`.  If an error occurs, all\n *  non-`NULL` position arguments will be set to zero.\n *\n *  @param[in] window The desired window.\n *  @param[out] xpos Where to store the cursor x-coordinate, relative to the\n *  left edge of the content area, or `NULL`.\n *  @param[out] ypos Where to store the cursor y-coordinate, relative to the to\n *  top edge of the content area, or `NULL`.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref cursor_pos\n *  @sa @ref glfwSetCursorPos\n *\n *  @since Added in version 3.0.  Replaces `glfwGetMousePos`.\n *\n *  @ingroup input\n */\nGLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos);\n\n/*! @brief Sets the position of the cursor, relative to the content area of the\n *  window.\n *\n *  This function sets the position, in screen coordinates, of the cursor\n *  relative to the upper-left corner of the content area of the specified\n *  window.  The window must have input focus.  If the window does not have\n *  input focus when this function is called, it fails silently.\n *\n *  __Do not use this function__ to implement things like camera controls.  GLFW\n *  already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the\n *  cursor, transparently re-centers it and provides unconstrained cursor\n *  motion.  See @ref glfwSetInputMode for more information.\n *\n *  If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is\n *  unconstrained and limited only by the minimum and maximum values of\n *  a `double`.\n *\n *  @param[in] window The desired window.\n *  @param[in] xpos The desired x-coordinate, relative to the left edge of the\n *  content area.\n *  @param[in] ypos The desired y-coordinate, relative to the top edge of the\n *  content area.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @remark @wayland This function will only work when the cursor mode is\n *  `GLFW_CURSOR_DISABLED`, otherwise it will do nothing.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref cursor_pos\n *  @sa @ref glfwGetCursorPos\n *\n *  @since Added in version 3.0.  Replaces `glfwSetMousePos`.\n *\n *  @ingroup input\n */\nGLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos);\n\n/*! @brief Creates a custom cursor.\n *\n *  Creates a new custom cursor image that can be set for a window with @ref\n *  glfwSetCursor.  The cursor can be destroyed with @ref glfwDestroyCursor.\n *  Any remaining cursors are destroyed by @ref glfwTerminate.\n *\n *  The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight\n *  bits per channel with the red channel first.  They are arranged canonically\n *  as packed sequential rows, starting from the top-left corner.\n *\n *  The cursor hotspot is specified in pixels, relative to the upper-left corner\n *  of the cursor image.  Like all other coordinate systems in GLFW, the X-axis\n *  points to the right and the Y-axis points down.\n *\n *  @param[in] image The desired cursor image.\n *  @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot.\n *  @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot.\n *  @param[in] count The number of images. Used on Cocoa for retina cursors. The first image should be the 1:1 scale image.\n *  @return The handle of the created cursor, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The specified image data is copied before this function\n *  returns.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref cursor_object\n *  @sa @ref glfwDestroyCursor\n *  @sa @ref glfwCreateStandardCursor\n *\n *  @since Added in version 3.1. Changed in 4.0 to add the count parameter.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot, int count);\n\n/*! @brief Creates a cursor with a standard shape.\n *\n *  Returns a cursor with a [standard shape](@ref shapes), that can be set for\n *  a window with @ref glfwSetCursor.\n *\n *  @param[in] shape One of the [standard shapes](@ref shapes).\n *  @return A new cursor ready to use or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref cursor_object\n *  @sa @ref glfwCreateCursor\n *\n *  @since Added in version 3.1.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWcursor* glfwCreateStandardCursor(GLFWCursorShape shape);\n\n/*! @brief Destroys a cursor.\n *\n *  This function destroys a cursor previously created with @ref\n *  glfwCreateCursor.  Any remaining cursors will be destroyed by @ref\n *  glfwTerminate.\n *\n *  If the specified cursor is current for any window, that window will be\n *  reverted to the default cursor.  This does not affect the cursor mode.\n *\n *  @param[in] cursor The cursor object to destroy.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @reentrancy This function must not be called from a callback.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref cursor_object\n *  @sa @ref glfwCreateCursor\n *\n *  @since Added in version 3.1.\n *\n *  @ingroup input\n */\nGLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);\n\n/*! @brief Sets the cursor for the window.\n *\n *  This function sets the cursor image to be used when the cursor is over the\n *  content area of the specified window.  The set cursor will only be visible\n *  when the [cursor mode](@ref cursor_mode) of the window is\n *  `GLFW_CURSOR_NORMAL`.\n *\n *  On some platforms, the set cursor may not be visible unless the window also\n *  has input focus.\n *\n *  @param[in] window The window to set the cursor for.\n *  @param[in] cursor The cursor to set, or `NULL` to switch back to the default\n *  arrow cursor.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref cursor_object\n *\n *  @since Added in version 3.1.\n *\n *  @ingroup input\n */\nGLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);\n\n/*! @brief Sets the callback for handling keyboard events.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* window, GLFWkeyboardfun callback);\n\n/*! @brief Notifies the OS Input Method Event system of changes to application input state\n *\n * Used to notify the IME system of changes in state such as focus gained/lost\n * and text cursor position.\n *\n * @param ev: What data to notify.\n *\n *  @ingroup input\n *  @since Added in version 4.0\n */\nGLFWAPI void glfwUpdateIMEState(GLFWwindow* window, const GLFWIMEUpdateEvent *ev);\n\n\n/*! @brief Sets the mouse button callback.\n *\n *  This function sets the mouse button callback of the specified window, which\n *  is called when a mouse button is pressed or released.\n *\n *  When a window loses input focus, it will generate synthetic mouse button\n *  release events for all pressed mouse buttons.  You can tell these events\n *  from user-generated events by the fact that the synthetic ones are generated\n *  after the focus loss event has been processed, i.e. after the\n *  [window focus callback](@ref glfwSetWindowFocusCallback) has been called.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int button, int action, int mods)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWmousebuttonfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref input_mouse_button\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter and return value.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback);\n\n/*! @brief Sets the cursor position callback.\n *\n *  This function sets the cursor position callback of the specified window,\n *  which is called when the cursor is moved.  The callback is provided with the\n *  position, in screen coordinates, relative to the upper-left corner of the\n *  content area of the window.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, double xpos, double ypos);\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWcursorposfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref cursor_pos\n *\n *  @since Added in version 3.0.  Replaces `glfwSetMousePosCallback`.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback);\n\n/*! @brief Sets the cursor enter/leave callback.\n *\n *  This function sets the cursor boundary crossing callback of the specified\n *  window, which is called when the cursor enters or leaves the content area of\n *  the window.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, int entered)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWcursorenterfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref cursor_enter\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback);\n\n/*! @brief Sets the scroll callback.\n *\n *  This function sets the scroll callback of the specified window, which is\n *  called when a scrolling device is used, such as a mouse wheel or scrolling\n *  area of a touchpad.\n *\n *  The scroll callback receives all scrolling input, like that from a mouse\n *  wheel or a touchpad scrolling area.\n *\n *  @param[in] window The window whose callback to set.\n *  @param[in] callback The new scroll callback, or `NULL` to remove the\n *  currently set callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(GLFWwindow* window, double xoffset, double yoffset)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWscrollfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref scrolling\n *\n *  @since Added in version 3.0.  Replaces `glfwSetMouseWheelCallback`.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback);\n\nGLFWAPI GLFWliveresizefun glfwSetLiveResizeCallback(GLFWwindow* window, GLFWliveresizefun callback);\n\nGLFWAPI GLFWdropeventfun glfwSetDropEventCallback(GLFWwindow *window, GLFWdropeventfun callback);\nGLFWAPI void glfwRequestDropUpdate(GLFWwindow *window);  // ask for update before GLFW_DROP_DROP happens\nGLFWAPI int glfwRequestDropData(GLFWwindow *window, const char *mime);\nGLFWAPI void glfwEndDrop(GLFWwindow *window, GLFWDragOperationType op);\nGLFWAPI GLFWdragsourcefun glfwSetDragSourceCallback(GLFWwindow* window, GLFWdragsourcefun callback);\n\n// Start a drag. If called with operations == -1 indicates that previously\n// requested data via GLFW_DRAG_DATA_REQUEST is ready. operations == -2 means\n// that the drag image is changed.\nGLFWAPI int glfwStartDrag(GLFWwindow* window, const GLFWDragSourceItem *items, size_t mime_count, const GLFWimage* thumbnail, int operations, bool needs_toplevel_on_wayland);\n\n/*! @brief Returns whether the specified joystick is present.\n *\n *  This function returns whether the specified joystick is present.\n *\n *  There is no need to call this function before other functions that accept\n *  a joystick ID, as they all check for presence before performing any other\n *  work.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @return `true` if the joystick is present, or `false` otherwise.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref joystick\n *\n *  @since Added in version 3.0.  Replaces `glfwGetJoystickParam`.\n *\n *  @ingroup input\n */\nGLFWAPI int glfwJoystickPresent(int jid);\n\n/*! @brief Returns the values of all axes of the specified joystick.\n *\n *  This function returns the values of all axes of the specified joystick.\n *  Each element in the array is a value between -1.0 and 1.0.\n *\n *  If the specified joystick is not present this function will return `NULL`\n *  but will not generate an error.  This can be used instead of first calling\n *  @ref glfwJoystickPresent.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @param[out] count Where to store the number of axis values in the returned\n *  array.  This is set to zero if the joystick is not present or an error\n *  occurred.\n *  @return An array of axis values, or `NULL` if the joystick is not present or\n *  an [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The returned array is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified joystick is\n *  disconnected or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref joystick_axis\n *\n *  @since Added in version 3.0.  Replaces `glfwGetJoystickPos`.\n *\n *  @ingroup input\n */\nGLFWAPI const float* glfwGetJoystickAxes(int jid, int* count);\n\n/*! @brief Returns the state of all buttons of the specified joystick.\n *\n *  This function returns the state of all buttons of the specified joystick.\n *  Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`.\n *\n *  For backward compatibility with earlier versions that did not have @ref\n *  glfwGetJoystickHats, the button array also includes all hats, each\n *  represented as four buttons.  The hats are in the same order as returned by\n *  __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and\n *  _left_.  To disable these extra buttons, set the @ref\n *  GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization.\n *\n *  If the specified joystick is not present this function will return `NULL`\n *  but will not generate an error.  This can be used instead of first calling\n *  @ref glfwJoystickPresent.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @param[out] count Where to store the number of button states in the returned\n *  array.  This is set to zero if the joystick is not present or an error\n *  occurred.\n *  @return An array of button states, or `NULL` if the joystick is not present\n *  or an [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The returned array is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified joystick is\n *  disconnected or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref joystick_button\n *\n *  @since Added in version 2.2.\n *  @glfw3 Changed to return a dynamic array.\n *\n *  @ingroup input\n */\nGLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count);\n\n/*! @brief Returns the state of all hats of the specified joystick.\n *\n *  This function returns the state of all hats of the specified joystick.\n *  Each element in the array is one of the following values:\n *\n *  Name                  | Value\n *  ----                  | -----\n *  `GLFW_HAT_CENTERED`   | 0\n *  `GLFW_HAT_UP`         | 1\n *  `GLFW_HAT_RIGHT`      | 2\n *  `GLFW_HAT_DOWN`       | 4\n *  `GLFW_HAT_LEFT`       | 8\n *  `GLFW_HAT_RIGHT_UP`   | `GLFW_HAT_RIGHT` \\| `GLFW_HAT_UP`\n *  `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \\| `GLFW_HAT_DOWN`\n *  `GLFW_HAT_LEFT_UP`    | `GLFW_HAT_LEFT` \\| `GLFW_HAT_UP`\n *  `GLFW_HAT_LEFT_DOWN`  | `GLFW_HAT_LEFT` \\| `GLFW_HAT_DOWN`\n *\n *  The diagonal directions are bitwise combinations of the primary (up, right,\n *  down and left) directions and you can test for these individually by ANDing\n *  it with the corresponding direction.\n *\n *  @code\n *  if (hats[2] & GLFW_HAT_RIGHT)\n *  {\n *      // State of hat 2 could be right-up, right or right-down\n *  }\n *  @endcode\n *\n *  If the specified joystick is not present this function will return `NULL`\n *  but will not generate an error.  This can be used instead of first calling\n *  @ref glfwJoystickPresent.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @param[out] count Where to store the number of hat states in the returned\n *  array.  This is set to zero if the joystick is not present or an error\n *  occurred.\n *  @return An array of hat states, or `NULL` if the joystick is not present\n *  or an [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The returned array is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified joystick is\n *  disconnected, this function is called again for that joystick or the library\n *  is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref joystick_hat\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count);\n\n/*! @brief Returns the name of the specified joystick.\n *\n *  This function returns the name, encoded as UTF-8, of the specified joystick.\n *  The returned string is allocated and freed by GLFW.  You should not free it\n *  yourself.\n *\n *  If the specified joystick is not present this function will return `NULL`\n *  but will not generate an error.  This can be used instead of first calling\n *  @ref glfwJoystickPresent.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick\n *  is not present or an [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The returned string is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified joystick is\n *  disconnected or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref joystick_name\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup input\n */\nGLFWAPI const char* glfwGetJoystickName(int jid);\n\n/*! @brief Returns the SDL compatible GUID of the specified joystick.\n *\n *  This function returns the SDL compatible GUID, as a UTF-8 encoded\n *  hexadecimal string, of the specified joystick.  The returned string is\n *  allocated and freed by GLFW.  You should not free it yourself.\n *\n *  The GUID is what connects a joystick to a gamepad mapping.  A connected\n *  joystick will always have a GUID even if there is no gamepad mapping\n *  assigned to it.\n *\n *  If the specified joystick is not present this function will return `NULL`\n *  but will not generate an error.  This can be used instead of first calling\n *  @ref glfwJoystickPresent.\n *\n *  The GUID uses the format introduced in SDL 2.0.5.  This GUID tries to\n *  uniquely identify the make and model of a joystick but does not identify\n *  a specific unit, e.g. all wired Xbox 360 controllers will have the same\n *  GUID on that platform.  The GUID for a unit may vary between platforms\n *  depending on what hardware information the platform specific APIs provide.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick\n *  is not present or an [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.\n *\n *  @pointer_lifetime The returned string is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified joystick is\n *  disconnected or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref gamepad\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI const char* glfwGetJoystickGUID(int jid);\n\n/*! @brief Sets the user pointer of the specified joystick.\n *\n *  This function sets the user-defined pointer of the specified joystick.  The\n *  current value is retained until the joystick is disconnected.  The initial\n *  value is `NULL`.\n *\n *  This function may be called from the joystick callback, even for a joystick\n *  that is being disconnected.\n *\n *  @param[in] jid The joystick whose pointer to set.\n *  @param[in] pointer The new value.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Access is not\n *  synchronized.\n *\n *  @sa @ref joystick_userptr\n *  @sa @ref glfwGetJoystickUserPointer\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer);\n\n/*! @brief Returns the user pointer of the specified joystick.\n *\n *  This function returns the current value of the user-defined pointer of the\n *  specified joystick.  The initial value is `NULL`.\n *\n *  This function may be called from the joystick callback, even for a joystick\n *  that is being disconnected.\n *\n *  @param[in] jid The joystick whose pointer to return.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Access is not\n *  synchronized.\n *\n *  @sa @ref joystick_userptr\n *  @sa @ref glfwSetJoystickUserPointer\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI void* glfwGetJoystickUserPointer(int jid);\n\n/*! @brief Returns whether the specified joystick has a gamepad mapping.\n *\n *  This function returns whether the specified joystick is both present and has\n *  a gamepad mapping.\n *\n *  If the specified joystick is present but does not have a gamepad mapping\n *  this function will return `false` but will not generate an error.  Call\n *  @ref glfwJoystickPresent to check if a joystick is present regardless of\n *  whether it has a mapping.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @return `true` if a joystick is both present and has a gamepad mapping,\n *  or `false` otherwise.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_INVALID_ENUM.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref gamepad\n *  @sa @ref glfwGetGamepadState\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI int glfwJoystickIsGamepad(int jid);\n\n/*! @brief Sets the joystick configuration callback.\n *\n *  This function sets the joystick configuration callback, or removes the\n *  currently set callback.  This is called when a joystick is connected to or\n *  disconnected from the system.\n *\n *  For joystick connection and disconnection events to be delivered on all\n *  platforms, you need to call one of the [event processing](@ref events)\n *  functions.  Joystick disconnection may also be detected and the callback\n *  called by joystick functions.  The function will then return whatever it\n *  returns if the joystick is not present.\n *\n *  @param[in] callback The new callback, or `NULL` to remove the currently set\n *  callback.\n *  @return The previously set callback, or `NULL` if no callback was set or the\n *  library had not been [initialized](@ref intro_init).\n *\n *  @callback_signature\n *  @code\n *  void function_name(int jid, int event)\n *  @endcode\n *  For more information about the callback parameters, see the\n *  [function pointer type](@ref GLFWjoystickfun).\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref joystick_event\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup input\n */\nGLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback);\n\n/*! @brief Adds the specified SDL_GameControllerDB gamepad mappings.\n *\n *  This function parses the specified ASCII encoded string and updates the\n *  internal list with any gamepad mappings it finds.  This string may\n *  contain either a single gamepad mapping or many mappings separated by\n *  newlines.  The parser supports the full format of the `gamecontrollerdb.txt`\n *  source file including empty lines and comments.\n *\n *  See @ref gamepad_mapping for a description of the format.\n *\n *  If there is already a gamepad mapping for a given GUID in the internal list,\n *  it will be replaced by the one passed to this function.  If the library is\n *  terminated and re-initialized the internal list will revert to the built-in\n *  default.\n *\n *  @param[in] string The string containing the gamepad mappings.\n *  @return `true` if successful, or `false` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_INVALID_VALUE.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref gamepad\n *  @sa @ref glfwJoystickIsGamepad\n *  @sa @ref glfwGetGamepadName\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI int glfwUpdateGamepadMappings(const char* string);\n\n/*! @brief Returns the human-readable gamepad name for the specified joystick.\n *\n *  This function returns the human-readable name of the gamepad from the\n *  gamepad mapping assigned to the specified joystick.\n *\n *  If the specified joystick is not present or does not have a gamepad mapping\n *  this function will return `NULL` but will not generate an error.  Call\n *  @ref glfwJoystickPresent to check whether it is present regardless of\n *  whether it has a mapping.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @return The UTF-8 encoded name of the gamepad, or `NULL` if the\n *  joystick is not present, does not have a mapping or an\n *  [error](@ref error_handling) occurred.\n *\n *  @pointer_lifetime The returned string is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is valid until the specified joystick is\n *  disconnected, the gamepad mappings are updated or the library is terminated.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref gamepad\n *  @sa @ref glfwJoystickIsGamepad\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI const char* glfwGetGamepadName(int jid);\n\n/*! @brief Retrieves the state of the specified joystick remapped as a gamepad.\n *\n *  This function retrieves the state of the specified joystick remapped to\n *  an Xbox-like gamepad.\n *\n *  If the specified joystick is not present or does not have a gamepad mapping\n *  this function will return `false` but will not generate an error.  Call\n *  @ref glfwJoystickPresent to check whether it is present regardless of\n *  whether it has a mapping.\n *\n *  The Guide button may not be available for input as it is often hooked by the\n *  system or the Steam client.\n *\n *  Not all devices have all the buttons or axes provided by @ref\n *  GLFWgamepadstate.  Unavailable buttons and axes will always report\n *  `GLFW_RELEASE` and 0.0 respectively.\n *\n *  @param[in] jid The [joystick](@ref joysticks) to query.\n *  @param[out] state The gamepad input state of the joystick.\n *  @return `true` if successful, or `false` if no joystick is\n *  connected, it has no gamepad mapping or an [error](@ref error_handling)\n *  occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_INVALID_ENUM.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref gamepad\n *  @sa @ref glfwUpdateGamepadMappings\n *  @sa @ref glfwJoystickIsGamepad\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\nGLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state);\n\nGLFWAPI void glfwSetClipboardDataTypes(GLFWClipboardType clipboard_type, const char* const *mime_types, size_t num_mime_types, GLFWclipboarditerfun get_iter);\nGLFWAPI void glfwGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object);\n\n/*! @brief Returns the GLFW time.\n *\n *  This function returns the current GLFW time, in seconds.  Unless the time\n *  has been set using @ref glfwSetTime it measures time elapsed since GLFW was\n *  initialized.\n *\n *  The resolution of the timer is system dependent, but is usually on the order\n *  of a few micro- or nanoseconds.  It uses the highest-resolution monotonic\n *  time source on each supported platform.\n *\n *  @return The current time, in seconds, or zero if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.  Reading and\n *  writing of the internal base time is not atomic, so it needs to be\n *  externally synchronized with calls to @ref glfwSetTime.\n *\n *  @sa @ref time\n *\n *  @since Added in version 1.0.\n *\n *  @ingroup input\n */\nGLFWAPI monotonic_t glfwGetTime(void);\n\n/*! @brief Makes the context of the specified window current for the calling\n *  thread.\n *\n *  This function makes the OpenGL or OpenGL ES context of the specified window\n *  current on the calling thread.  A context must only be made current on\n *  a single thread at a time and each thread can have only a single current\n *  context at a time.\n *\n *  When moving a context between threads, you must make it non-current on the\n *  old thread before making it current on the new one.\n *\n *  By default, making a context non-current implicitly forces a pipeline flush.\n *  On machines that support `GL_KHR_context_flush_control`, you can control\n *  whether a context performs this flush by setting the\n *  [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint)\n *  hint.\n *\n *  The specified window must have an OpenGL or OpenGL ES context.  Specifying\n *  a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT\n *  error.\n *\n *  @param[in] window The window whose context to make current, or `NULL` to\n *  detach the current context.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref context_current\n *  @sa @ref glfwGetCurrentContext\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup context\n */\nGLFWAPI void glfwMakeContextCurrent(GLFWwindow* window);\n\n/*! @brief Returns the window whose context is current on the calling thread.\n *\n *  This function returns the window whose OpenGL or OpenGL ES context is\n *  current on the calling thread.\n *\n *  @return The window whose context is current, or `NULL` if no window's\n *  context is current.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref context_current\n *  @sa @ref glfwMakeContextCurrent\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup context\n */\nGLFWAPI GLFWwindow* glfwGetCurrentContext(void);\n\n/*! @brief Swaps the front and back buffers of the specified window.\n *\n *  This function swaps the front and back buffers of the specified window when\n *  rendering with OpenGL or OpenGL ES.  If the swap interval is greater than\n *  zero, the GPU driver waits the specified number of screen updates before\n *  swapping the buffers.\n *\n *  The specified window must have an OpenGL or OpenGL ES context.  Specifying\n *  a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT\n *  error.\n *\n *  This function does not apply to Vulkan.  If you are rendering with Vulkan,\n *  see `vkQueuePresentKHR` instead.\n *\n *  @param[in] window The window whose buffers to swap.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark __EGL:__ The context of the specified window must be current on the\n *  calling thread.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref buffer_swap\n *  @sa @ref glfwSwapInterval\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\nGLFWAPI void glfwSwapBuffers(GLFWwindow* window);\n\n/*! @brief Sets the swap interval for the current context.\n *\n *  This function sets the swap interval for the current OpenGL or OpenGL ES\n *  context, i.e. the number of screen updates to wait from the time @ref\n *  glfwSwapBuffers was called before swapping the buffers and returning.  This\n *  is sometimes called _vertical synchronization_, _vertical retrace\n *  synchronization_ or just _vsync_.\n *\n *  A context that supports either of the `WGL_EXT_swap_control_tear` and\n *  `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap\n *  intervals, which allows the driver to swap immediately even if a frame\n *  arrives a little bit late.  You can check for these extensions with @ref\n *  glfwExtensionSupported.\n *\n *  A context must be current on the calling thread.  Calling this function\n *  without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.\n *\n *  This function does not apply to Vulkan.  If you are rendering with Vulkan,\n *  see the present mode of your swapchain instead.\n *\n *  @param[in] interval The minimum number of screen updates to wait for\n *  until the buffers are swapped by @ref glfwSwapBuffers.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark This function is not called during context creation, leaving the\n *  swap interval set to whatever is the default on that platform.  This is done\n *  because some swap interval extensions used by GLFW do not allow the swap\n *  interval to be reset to zero once it has been set to a non-zero value.\n *\n *  @remark Some GPU drivers do not honor the requested swap interval, either\n *  because of a user setting that overrides the application's request or due to\n *  bugs in the driver.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref buffer_swap\n *  @sa @ref glfwSwapBuffers\n *\n *  @since Added in version 1.0.\n *\n *  @ingroup context\n */\nGLFWAPI void glfwSwapInterval(int interval);\n\n/*! @brief Returns whether the specified extension is available.\n *\n *  This function returns whether the specified\n *  [API extension](@ref context_glext) is supported by the current OpenGL or\n *  OpenGL ES context.  It searches both for client API extension and context\n *  creation API extensions.\n *\n *  A context must be current on the calling thread.  Calling this function\n *  without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.\n *\n *  As this functions retrieves and searches one or more extension strings each\n *  call, it is recommended that you cache its results if it is going to be used\n *  frequently.  The extension strings will not change during the lifetime of\n *  a context, so there is no danger in doing this.\n *\n *  This function does not apply to Vulkan.  If you are using Vulkan, see @ref\n *  glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties`\n *  and `vkEnumerateDeviceExtensionProperties` instead.\n *\n *  @param[in] extension The ASCII encoded name of the extension.\n *  @return `true` if the extension is available, or `false`\n *  otherwise.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref\n *  GLFW_PLATFORM_ERROR.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref context_glext\n *  @sa @ref glfwGetProcAddress\n *\n *  @since Added in version 1.0.\n *\n *  @ingroup context\n */\nGLFWAPI int glfwExtensionSupported(const char* extension);\n\n/*! @brief Returns the address of the specified function for the current\n *  context.\n *\n *  This function returns the address of the specified OpenGL or OpenGL ES\n *  [core or extension function](@ref context_glext), if it is supported\n *  by the current context.\n *\n *  A context must be current on the calling thread.  Calling this function\n *  without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.\n *\n *  This function does not apply to Vulkan.  If you are rendering with Vulkan,\n *  see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and\n *  `vkGetDeviceProcAddr` instead.\n *\n *  @param[in] procname The ASCII encoded name of the function.\n *  @return The address of the function, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark The address of a given function is not guaranteed to be the same\n *  between contexts.\n *\n *  @remark This function may return a non-`NULL` address despite the\n *  associated version or extension not being available.  Always check the\n *  context version or extension string first.\n *\n *  @pointer_lifetime The returned function pointer is valid until the context\n *  is destroyed or the library is terminated.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref context_glext\n *  @sa @ref glfwExtensionSupported\n *\n *  @since Added in version 1.0.\n *\n *  @ingroup context\n */\nGLFWAPI GLFWglproc glfwGetProcAddress(const char* procname);\n\n/*! @brief Returns whether the Vulkan loader and an ICD have been found.\n *\n *  This function returns whether the Vulkan loader and any minimally functional\n *  ICD have been found.\n *\n *  The availability of a Vulkan loader and even an ICD does not by itself\n *  guarantee that surface creation or even instance creation is possible.\n *  For example, on Fermi systems Nvidia will install an ICD that provides no\n *  actual Vulkan support.  Call @ref glfwGetRequiredInstanceExtensions to check\n *  whether the extensions necessary for Vulkan surface creation are available\n *  and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue\n *  family of a physical device supports image presentation.\n *\n *  @return `true` if Vulkan is minimally available, or `false`\n *  otherwise.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref vulkan_support\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup vulkan\n */\nGLFWAPI int glfwVulkanSupported(void);\n\n/*! @brief Returns the Vulkan instance extensions required by GLFW.\n *\n *  This function returns an array of names of Vulkan instance extensions required\n *  by GLFW for creating Vulkan surfaces for GLFW windows.  If successful, the\n *  list will always contain `VK_KHR_surface`, so if you don't require any\n *  additional extensions you can pass this list directly to the\n *  `VkInstanceCreateInfo` struct.\n *\n *  If Vulkan is not available on the machine, this function returns `NULL` and\n *  generates a @ref GLFW_API_UNAVAILABLE error.  Call @ref glfwVulkanSupported\n *  to check whether Vulkan is at least minimally available.\n *\n *  If Vulkan is available but no set of extensions allowing window surface\n *  creation was found, this function returns `NULL`.  You may still use Vulkan\n *  for off-screen rendering and compute work.\n *\n *  @param[out] count Where to store the number of extensions in the returned\n *  array.  This is set to zero if an error occurred.\n *  @return An array of ASCII encoded extension names, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_API_UNAVAILABLE.\n *\n *  @remark Additional extensions may be required by future versions of GLFW.\n *  You should check if any extensions you wish to enable are already in the\n *  returned array, as it is an error to specify an extension more than once in\n *  the `VkInstanceCreateInfo` struct.\n *\n *  @remark @macos This function currently supports either the\n *  `VK_MVK_macos_surface` extension from MoltenVK or `VK_EXT_metal_surface`\n *  extension.\n *\n *  @pointer_lifetime The returned array is allocated and freed by GLFW.  You\n *  should not free it yourself.  It is guaranteed to be valid only until the\n *  library is terminated.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref vulkan_ext\n *  @sa @ref glfwCreateWindowSurface\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup vulkan\n */\nGLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count);\n\n#if defined(VK_VERSION_1_0)\n\n/*! @brief Returns the address of the specified Vulkan instance function.\n *\n *  This function returns the address of the specified Vulkan core or extension\n *  function for the specified instance.  If instance is set to `NULL` it can\n *  return any function exported from the Vulkan loader, including at least the\n *  following functions:\n *\n *  - `vkEnumerateInstanceExtensionProperties`\n *  - `vkEnumerateInstanceLayerProperties`\n *  - `vkCreateInstance`\n *  - `vkGetInstanceProcAddr`\n *\n *  If Vulkan is not available on the machine, this function returns `NULL` and\n *  generates a @ref GLFW_API_UNAVAILABLE error.  Call @ref glfwVulkanSupported\n *  to check whether Vulkan is at least minimally available.\n *\n *  This function is equivalent to calling `vkGetInstanceProcAddr` with\n *  a platform-specific query of the Vulkan loader as a fallback.\n *\n *  @param[in] instance The Vulkan instance to query, or `NULL` to retrieve\n *  functions related to instance creation.\n *  @param[in] procname The ASCII encoded name of the function.\n *  @return The address of the function, or `NULL` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref\n *  GLFW_API_UNAVAILABLE.\n *\n *  @pointer_lifetime The returned function pointer is valid until the library\n *  is terminated.\n *\n *  @thread_safety This function may be called from any thread.\n *\n *  @sa @ref vulkan_proc\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup vulkan\n */\nGLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname);\n\n/*! @brief Returns whether the specified queue family can present images.\n *\n *  This function returns whether the specified queue family of the specified\n *  physical device supports presentation to the platform GLFW was built for.\n *\n *  If Vulkan or the required window surface creation instance extensions are\n *  not available on the machine, or if the specified instance was not created\n *  with the required extensions, this function returns `false` and\n *  generates a @ref GLFW_API_UNAVAILABLE error.  Call @ref glfwVulkanSupported\n *  to check whether Vulkan is at least minimally available and @ref\n *  glfwGetRequiredInstanceExtensions to check what instance extensions are\n *  required.\n *\n *  @param[in] instance The instance that the physical device belongs to.\n *  @param[in] device The physical device that the queue family belongs to.\n *  @param[in] queuefamily The index of the queue family to query.\n *  @return `true` if the queue family supports presentation, or\n *  `false` otherwise.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark @macos This function currently always returns `true`, as the\n *  `VK_MVK_macos_surface` extension does not provide\n *  a `vkGetPhysicalDevice*PresentationSupport` type function.\n *\n *  @thread_safety This function may be called from any thread.  For\n *  synchronization details of Vulkan objects, see the Vulkan specification.\n *\n *  @sa @ref vulkan_present\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup vulkan\n */\nGLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily);\n\n/*! @brief Creates a Vulkan surface for the specified window.\n *\n *  This function creates a Vulkan surface for the specified window.\n *\n *  If the Vulkan loader or at least one minimally functional ICD were not found,\n *  this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref\n *  GLFW_API_UNAVAILABLE error.  Call @ref glfwVulkanSupported to check whether\n *  Vulkan is at least minimally available.\n *\n *  If the required window surface creation instance extensions are not\n *  available or if the specified instance was not created with these extensions\n *  enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and\n *  generates a @ref GLFW_API_UNAVAILABLE error.  Call @ref\n *  glfwGetRequiredInstanceExtensions to check what instance extensions are\n *  required.\n *\n *  The window surface cannot be shared with another API so the window must\n *  have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib)\n *  set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error\n *  and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`.\n *\n *  The window surface must be destroyed before the specified Vulkan instance.\n *  It is the responsibility of the caller to destroy the window surface.  GLFW\n *  does not destroy it for you.  Call `vkDestroySurfaceKHR` to destroy the\n *  surface.\n *\n *  @param[in] instance The Vulkan instance to create the surface in.\n *  @param[in] window The window to create the surface for.\n *  @param[in] allocator The allocator to use, or `NULL` to use the default\n *  allocator.\n *  @param[out] surface Where to store the handle of the surface.  This is set\n *  to `VK_NULL_HANDLE` if an error occurred.\n *  @return `VK_SUCCESS` if successful, or a Vulkan error code if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref\n *  GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE\n *\n *  @remark If an error occurs before the creation call is made, GLFW returns\n *  the Vulkan error code most appropriate for the error.  Appropriate use of\n *  @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should\n *  eliminate almost all occurrences of these errors.\n *\n *  @remark @macos This function currently only supports the\n *  `VK_MVK_macos_surface` extension from MoltenVK.\n *\n *  @remark @macos This function creates and sets a `CAMetalLayer` instance for\n *  the window content view, which is required for MoltenVK to function.\n *\n *  @thread_safety This function may be called from any thread.  For\n *  synchronization details of Vulkan objects, see the Vulkan specification.\n *\n *  @sa @ref vulkan_surface\n *  @sa @ref glfwGetRequiredInstanceExtensions\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup vulkan\n */\nGLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);\n\n#endif /*VK_VERSION_1_0*/\n\n\n/*************************************************************************\n * Global definition cleanup\n *************************************************************************/\n\n/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */\n\n#ifdef GLFW_WINGDIAPI_DEFINED\n #undef WINGDIAPI\n #undef GLFW_WINGDIAPI_DEFINED\n#endif\n\n#ifdef GLFW_CALLBACK_DEFINED\n #undef CALLBACK\n #undef GLFW_CALLBACK_DEFINED\n#endif\n\n/* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally\n * defined by some gl.h variants (OpenBSD) so define it after if needed.\n */\n#ifndef GLAPIENTRY\n #define GLAPIENTRY APIENTRY\n#endif\n\n/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _glfw3_h_ */\n"
  },
  {
    "path": "glfw/glx_context.c",
    "content": "//========================================================================\n// GLFW 3.4 GLX - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#ifndef GLXBadProfileARB\n #define GLXBadProfileARB 13\n#endif\n\n\n// Returns the specified attribute of the specified GLXFBConfig\n//\nstatic int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib)\n{\n    int value;\n    glXGetFBConfigAttrib(_glfw.x11.display, fbconfig, attrib, &value);\n    return value;\n}\n\nstatic GLXFBConfig*\nchoose_fb_config(const _GLFWfbconfig* desired, bool trust_window_bit, int *nelements, bool use_best_color_depth) {\n    int attrib_list[64];\n    int pos = 0;\n#define ATTR(x, y) { attrib_list[pos++] = x; attrib_list[pos++] = y; }\n\n    ATTR(GLX_DOUBLEBUFFER, desired->doublebuffer ? True : False);\n    if (desired->stereo > 0) ATTR(GLX_STEREO, desired->stereo ? True : False);\n    if (desired->auxBuffers > 0) ATTR(GLX_AUX_BUFFERS, desired->auxBuffers);\n    if (_glfw.glx.ARB_multisample && desired->samples > 0) ATTR(GLX_SAMPLES, desired->samples);\n    if (desired->depthBits != GLFW_DONT_CARE) ATTR(GLX_DEPTH_SIZE, desired->depthBits);\n    if (desired->stencilBits != GLFW_DONT_CARE) ATTR(GLX_STENCIL_SIZE, desired->stencilBits);\n    if (use_best_color_depth) {\n        // we just ask for the highest available R+G+B+A color depth. This hopefully\n        // works with 10bit (r=10, g=10, b=19, a=2) visuals\n        ATTR(GLX_RED_SIZE, 1); ATTR(GLX_GREEN_SIZE, 1); ATTR(GLX_BLUE_SIZE, 1); ATTR(GLX_ALPHA_SIZE, 1);\n    } else {\n        if (desired->redBits != GLFW_DONT_CARE) ATTR(GLX_RED_SIZE, desired->redBits);\n        if (desired->greenBits != GLFW_DONT_CARE) ATTR(GLX_GREEN_SIZE, desired->greenBits);\n        if (desired->blueBits != GLFW_DONT_CARE) ATTR(GLX_BLUE_SIZE, desired->blueBits);\n        if (desired->alphaBits != GLFW_DONT_CARE) ATTR(GLX_ALPHA_SIZE, desired->alphaBits);\n    }\n    if (desired->accumRedBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_RED_SIZE, desired->accumRedBits);\n    if (desired->accumGreenBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_GREEN_SIZE, desired->accumGreenBits);\n    if (desired->accumBlueBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_BLUE_SIZE, desired->accumBlueBits);\n    if (desired->accumAlphaBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_ALPHA_SIZE, desired->accumAlphaBits);\n    if (!trust_window_bit) ATTR(GLX_DRAWABLE_TYPE, 0);\n    ATTR(None, None);\n    return glXChooseFBConfig(_glfw.x11.display, _glfw.x11.screen, attrib_list, nelements);\n#undef ATTR\n}\n\n\n// Return the GLXFBConfig most closely matching the specified hints\n//\nstatic bool chooseGLXFBConfig(const _GLFWfbconfig* desired,\n                                  GLXFBConfig* result)\n{\n    GLXFBConfig* nativeConfigs;\n    int i, nativeCount, ans_idx = 0;\n    const char* vendor;\n    bool trustWindowBit = true;\n    static _GLFWfbconfig prev_desired  = {0};\n    static GLXFBConfig prev_result = 0;\n    if (prev_result != 0 && memcmp(&prev_desired, desired, sizeof(_GLFWfbconfig)) == 0) {\n        *result = prev_result;\n        return true;\n    }\n    prev_desired = *desired;\n\n    // HACK: This is a (hopefully temporary) workaround for Chromium\n    //       (VirtualBox GL) not setting the window bit on any GLXFBConfigs\n    vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR);\n    if (vendor && strcmp(vendor, \"Chromium\") == 0)\n        trustWindowBit = false;\n    nativeConfigs = choose_fb_config(desired, trustWindowBit, &nativeCount, false);\n    if (!nativeConfigs || !nativeCount)\n    {\n        nativeConfigs = choose_fb_config(desired, trustWindowBit, &nativeCount, true);\n\n        if (!nativeConfigs || !nativeCount) {\n            _glfwInputError(GLFW_API_UNAVAILABLE, \"GLX: No GLXFBConfigs returned\");\n            return false;\n        }\n    }\n    for (i = 0;  i < nativeCount;  i++)\n    {\n        const GLXFBConfig n = nativeConfigs[i];\n        bool transparency_matches = true, srgb_matches = true;\n        if (desired->transparent) {\n            transparency_matches = false;\n            XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n);\n            if (vi && _glfwIsVisualTransparentX11(vi->visual)) transparency_matches = true;\n        }\n\n        if (desired->sRGB && (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB)) {\n            srgb_matches = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB) ? true : false;\n        }\n\n        if (transparency_matches && srgb_matches) {\n            ans_idx = i; break;\n        }\n\n    }\n\n    *result = nativeConfigs[ans_idx];\n    prev_result = nativeConfigs[ans_idx];\n\n    XFree(nativeConfigs);\n\n    return true;\n}\n\n// Create the OpenGL context using legacy API\n//\nstatic GLXContext createLegacyContextGLX(_GLFWwindow* window UNUSED,\n                                         GLXFBConfig fbconfig,\n                                         GLXContext share)\n{\n    return glXCreateNewContext(_glfw.x11.display,\n                               fbconfig,\n                               GLX_RGBA_TYPE,\n                               share,\n                               True);\n}\n\nstatic void makeContextCurrentGLX(_GLFWwindow* window)\n{\n    if (window)\n    {\n        if (!glXMakeCurrent(_glfw.x11.display,\n                            window->context.glx.window,\n                            window->context.glx.handle))\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"GLX: Failed to make context current\");\n            return;\n        }\n    }\n    else\n    {\n        if (!glXMakeCurrent(_glfw.x11.display, None, NULL))\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"GLX: Failed to clear current context\");\n            return;\n        }\n    }\n\n    _glfwPlatformSetTls(&_glfw.contextSlot, window);\n}\n\nstatic void swapBuffersGLX(_GLFWwindow* window)\n{\n    glXSwapBuffers(_glfw.x11.display, window->context.glx.window);\n}\n\nstatic void swapIntervalGLX(int interval)\n{\n    _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);\n\n    if (_glfw.glx.EXT_swap_control)\n    {\n        _glfw.glx.SwapIntervalEXT(_glfw.x11.display,\n                                  window->context.glx.window,\n                                  interval);\n    }\n    else if (_glfw.glx.MESA_swap_control)\n        _glfw.glx.SwapIntervalMESA(interval);\n    else if (_glfw.glx.SGI_swap_control)\n    {\n        if (interval > 0)\n            _glfw.glx.SwapIntervalSGI(interval);\n    }\n}\n\nstatic int extensionSupportedGLX(const char* extension)\n{\n    const char* extensions =\n        glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen);\n    if (extensions)\n    {\n        if (_glfwStringInExtensionString(extension, extensions))\n            return true;\n    }\n\n    return false;\n}\n\nstatic GLFWglproc getProcAddressGLX(const char* procname)\n{\n    if (_glfw.glx.GetProcAddress)\n        return _glfw.glx.GetProcAddress((const GLubyte*) procname);\n    else if (_glfw.glx.GetProcAddressARB)\n        return _glfw.glx.GetProcAddressARB((const GLubyte*) procname);\n    else {\n        GLFWglproc ans = NULL;\n        glfw_dlsym(ans, _glfw.glx.handle, procname);\n        return ans;\n    }\n}\n\nstatic void destroyContextGLX(_GLFWwindow* window)\n{\n    if (window->context.glx.window)\n    {\n        glXDestroyWindow(_glfw.x11.display, window->context.glx.window);\n        window->context.glx.window = None;\n    }\n\n    if (window->context.glx.handle)\n    {\n        glXDestroyContext(_glfw.x11.display, window->context.glx.handle);\n        window->context.glx.handle = NULL;\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Initialize GLX\n//\nbool _glfwInitGLX(void)\n{\n    int i;\n    const char* sonames[] =\n    {\n#if defined(_GLFW_GLX_LIBRARY)\n        _GLFW_GLX_LIBRARY,\n#elif defined(__CYGWIN__)\n        \"libGL-1.so\",\n#else\n        \"libGL.so.1\",\n        \"libGL.so\",\n#endif\n        NULL\n    };\n\n    if (_glfw.glx.handle)\n        return true;\n\n    for (i = 0;  sonames[i];  i++)\n    {\n        _glfw.glx.handle = _glfw_dlopen(sonames[i]);\n        if (_glfw.glx.handle)\n            break;\n    }\n\n    if (!_glfw.glx.handle)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE, \"GLX: Failed to load GLX\");\n        return false;\n    }\n\n    glfw_dlsym(_glfw.glx.GetFBConfigs, _glfw.glx.handle, \"glXGetFBConfigs\");\n    glfw_dlsym(_glfw.glx.GetFBConfigAttrib, _glfw.glx.handle, \"glXGetFBConfigAttrib\");\n    glfw_dlsym(_glfw.glx.ChooseFBConfig, _glfw.glx.handle, \"glXChooseFBConfig\");\n    glfw_dlsym(_glfw.glx.GetClientString, _glfw.glx.handle, \"glXGetClientString\");\n    glfw_dlsym(_glfw.glx.QueryExtension, _glfw.glx.handle, \"glXQueryExtension\");\n    glfw_dlsym(_glfw.glx.QueryVersion, _glfw.glx.handle, \"glXQueryVersion\");\n    glfw_dlsym(_glfw.glx.DestroyContext, _glfw.glx.handle, \"glXDestroyContext\");\n    glfw_dlsym(_glfw.glx.MakeCurrent, _glfw.glx.handle, \"glXMakeCurrent\");\n    glfw_dlsym(_glfw.glx.SwapBuffers, _glfw.glx.handle, \"glXSwapBuffers\");\n    glfw_dlsym(_glfw.glx.QueryExtensionsString, _glfw.glx.handle, \"glXQueryExtensionsString\");\n    glfw_dlsym(_glfw.glx.CreateNewContext, _glfw.glx.handle, \"glXCreateNewContext\");\n    glfw_dlsym(_glfw.glx.CreateWindow, _glfw.glx.handle, \"glXCreateWindow\");\n    glfw_dlsym(_glfw.glx.DestroyWindow, _glfw.glx.handle, \"glXDestroyWindow\");\n    glfw_dlsym(_glfw.glx.GetProcAddress, _glfw.glx.handle, \"glXGetProcAddress\");\n    glfw_dlsym(_glfw.glx.GetProcAddressARB, _glfw.glx.handle, \"glXGetProcAddressARB\");\n    glfw_dlsym(_glfw.glx.GetVisualFromFBConfig, _glfw.glx.handle, \"glXGetVisualFromFBConfig\");\n\n    if (!_glfw.glx.GetFBConfigs ||\n        !_glfw.glx.GetFBConfigAttrib ||\n        !_glfw.glx.GetClientString ||\n        !_glfw.glx.QueryExtension ||\n        !_glfw.glx.QueryVersion ||\n        !_glfw.glx.DestroyContext ||\n        !_glfw.glx.MakeCurrent ||\n        !_glfw.glx.SwapBuffers ||\n        !_glfw.glx.QueryExtensionsString ||\n        !_glfw.glx.CreateNewContext ||\n        !_glfw.glx.CreateWindow ||\n        !_glfw.glx.DestroyWindow ||\n        !_glfw.glx.GetProcAddress ||\n        !_glfw.glx.GetProcAddressARB ||\n        !_glfw.glx.GetVisualFromFBConfig)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"GLX: Failed to load required entry points\");\n        return false;\n    }\n\n    if (!glXQueryExtension(_glfw.x11.display,\n                           &_glfw.glx.errorBase,\n                           &_glfw.glx.eventBase))\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE, \"GLX: GLX extension not found\");\n        return false;\n    }\n\n    if (!glXQueryVersion(_glfw.x11.display, &_glfw.glx.major, &_glfw.glx.minor))\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"GLX: Failed to query GLX version\");\n        return false;\n    }\n\n    if (_glfw.glx.major == 1 && _glfw.glx.minor < 3)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"GLX: GLX version 1.3 is required\");\n        return false;\n    }\n\n    if (extensionSupportedGLX(\"GLX_EXT_swap_control\"))\n    {\n        _glfw.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)\n            getProcAddressGLX(\"glXSwapIntervalEXT\");\n\n        if (_glfw.glx.SwapIntervalEXT)\n            _glfw.glx.EXT_swap_control = true;\n    }\n\n    if (extensionSupportedGLX(\"GLX_SGI_swap_control\"))\n    {\n        _glfw.glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)\n            getProcAddressGLX(\"glXSwapIntervalSGI\");\n\n        if (_glfw.glx.SwapIntervalSGI)\n            _glfw.glx.SGI_swap_control = true;\n    }\n\n    if (extensionSupportedGLX(\"GLX_MESA_swap_control\"))\n    {\n        _glfw.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC)\n            getProcAddressGLX(\"glXSwapIntervalMESA\");\n\n        if (_glfw.glx.SwapIntervalMESA)\n            _glfw.glx.MESA_swap_control = true;\n    }\n\n    if (extensionSupportedGLX(\"GLX_ARB_multisample\"))\n        _glfw.glx.ARB_multisample = true;\n\n    if (extensionSupportedGLX(\"GLX_ARB_framebuffer_sRGB\"))\n        _glfw.glx.ARB_framebuffer_sRGB = true;\n\n    if (extensionSupportedGLX(\"GLX_EXT_framebuffer_sRGB\"))\n        _glfw.glx.EXT_framebuffer_sRGB = true;\n\n    if (extensionSupportedGLX(\"GLX_ARB_create_context\"))\n    {\n        _glfw.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)\n            getProcAddressGLX(\"glXCreateContextAttribsARB\");\n\n        if (_glfw.glx.CreateContextAttribsARB)\n            _glfw.glx.ARB_create_context = true;\n    }\n\n    if (extensionSupportedGLX(\"GLX_ARB_create_context_robustness\"))\n        _glfw.glx.ARB_create_context_robustness = true;\n\n    if (extensionSupportedGLX(\"GLX_ARB_create_context_profile\"))\n        _glfw.glx.ARB_create_context_profile = true;\n\n    if (extensionSupportedGLX(\"GLX_EXT_create_context_es2_profile\"))\n        _glfw.glx.EXT_create_context_es2_profile = true;\n\n    if (extensionSupportedGLX(\"GLX_ARB_create_context_no_error\"))\n        _glfw.glx.ARB_create_context_no_error = true;\n\n    if (extensionSupportedGLX(\"GLX_ARB_context_flush_control\"))\n        _glfw.glx.ARB_context_flush_control = true;\n\n    return true;\n}\n\n// Terminate GLX\n//\nvoid _glfwTerminateGLX(void)\n{\n    // NOTE: This function must not call any X11 functions, as it is called\n    //       after XCloseDisplay (see _glfwPlatformTerminate for details)\n\n    if (_glfw.glx.handle)\n    {\n        _glfw_dlclose(_glfw.glx.handle);\n        _glfw.glx.handle = NULL;\n    }\n}\n\n#define setAttrib(a, v) \\\n{ \\\n    assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \\\n    attribs[index++] = a; \\\n    attribs[index++] = v; \\\n}\n\n// Create the OpenGL or OpenGL ES context\n//\nbool _glfwCreateContextGLX(_GLFWwindow* window,\n                               const _GLFWctxconfig* ctxconfig,\n                               const _GLFWfbconfig* fbconfig)\n{\n    int attribs[40];\n    GLXFBConfig native = NULL;\n    GLXContext share = NULL;\n\n    if (ctxconfig->share)\n        share = ctxconfig->share->context.glx.handle;\n\n    if (!chooseGLXFBConfig(fbconfig, &native))\n    {\n        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,\n                        \"GLX: Failed to find a suitable GLXFBConfig\");\n        return false;\n    }\n\n    if (ctxconfig->client == GLFW_OPENGL_ES_API)\n    {\n        if (!_glfw.glx.ARB_create_context ||\n            !_glfw.glx.ARB_create_context_profile ||\n            !_glfw.glx.EXT_create_context_es2_profile)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable\");\n            return false;\n        }\n    }\n\n    if (ctxconfig->forward)\n    {\n        if (!_glfw.glx.ARB_create_context)\n        {\n            _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                            \"GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable\");\n            return false;\n        }\n    }\n\n    if (ctxconfig->profile)\n    {\n        if (!_glfw.glx.ARB_create_context ||\n            !_glfw.glx.ARB_create_context_profile)\n        {\n            _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                            \"GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable\");\n            return false;\n        }\n    }\n\n    _glfwGrabErrorHandlerX11();\n\n    if (_glfw.glx.ARB_create_context)\n    {\n        int index = 0, mask = 0, flags = 0;\n\n        if (ctxconfig->client == GLFW_OPENGL_API)\n        {\n            if (ctxconfig->forward)\n                flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;\n\n            if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)\n                mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB;\n            else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)\n                mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;\n        }\n        else\n            mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT;\n\n        if (ctxconfig->debug)\n            flags |= GLX_CONTEXT_DEBUG_BIT_ARB;\n\n        if (ctxconfig->robustness)\n        {\n            if (_glfw.glx.ARB_create_context_robustness)\n            {\n                if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION)\n                {\n                    setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,\n                              GLX_NO_RESET_NOTIFICATION_ARB);\n                }\n                else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET)\n                {\n                    setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,\n                              GLX_LOSE_CONTEXT_ON_RESET_ARB);\n                }\n\n                flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;\n            }\n        }\n\n        if (ctxconfig->release)\n        {\n            if (_glfw.glx.ARB_context_flush_control)\n            {\n                if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE)\n                {\n                    setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,\n                              GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);\n                }\n                else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH)\n                {\n                    setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,\n                              GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);\n                }\n            }\n        }\n\n        if (ctxconfig->noerror)\n        {\n            if (_glfw.glx.ARB_create_context_no_error)\n                setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, true);\n        }\n\n        // NOTE: Only request an explicitly versioned context when necessary, as\n        //       explicitly requesting version 1.0 does not always return the\n        //       highest version supported by the driver\n        if (ctxconfig->major != 1 || ctxconfig->minor != 0)\n        {\n            setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major);\n            setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor);\n        }\n\n        if (mask)\n            setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask);\n\n        if (flags)\n            setAttrib(GLX_CONTEXT_FLAGS_ARB, flags);\n\n        setAttrib(None, None);\n\n        window->context.glx.handle =\n            _glfw.glx.CreateContextAttribsARB(_glfw.x11.display,\n                                              native,\n                                              share,\n                                              True,\n                                              attribs);\n\n        // HACK: This is a fallback for broken versions of the Mesa\n        //       implementation of GLX_ARB_create_context_profile that fail\n        //       default 1.0 context creation with a GLXBadProfileARB error in\n        //       violation of the extension spec\n        if (!window->context.glx.handle)\n        {\n            if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB &&\n                ctxconfig->client == GLFW_OPENGL_API &&\n                ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE &&\n                ctxconfig->forward == false)\n            {\n                window->context.glx.handle =\n                    createLegacyContextGLX(window, native, share);\n            }\n        }\n    }\n    else\n    {\n        window->context.glx.handle =\n            createLegacyContextGLX(window, native, share);\n    }\n\n    _glfwReleaseErrorHandlerX11();\n\n    if (!window->context.glx.handle)\n    {\n        _glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, \"GLX: Failed to create context\");\n        return false;\n    }\n\n    window->context.glx.window =\n        glXCreateWindow(_glfw.x11.display, native, window->x11.handle, NULL);\n    if (!window->context.glx.window)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"GLX: Failed to create window\");\n        return false;\n    }\n\n    window->context.makeCurrent = makeContextCurrentGLX;\n    window->context.swapBuffers = swapBuffersGLX;\n    window->context.swapInterval = swapIntervalGLX;\n    window->context.extensionSupported = extensionSupportedGLX;\n    window->context.getProcAddress = getProcAddressGLX;\n    window->context.destroy = destroyContextGLX;\n\n    return true;\n}\n\n#undef setAttrib\n\n// Returns the Visual and depth of the chosen GLXFBConfig\n//\nbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig UNUSED,\n                              const _GLFWctxconfig* ctxconfig UNUSED,\n                              const _GLFWfbconfig* fbconfig,\n                              Visual** visual, int* depth)\n{\n    GLXFBConfig native;\n    XVisualInfo* result;\n\n    if (!chooseGLXFBConfig(fbconfig, &native))\n    {\n        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,\n                        \"GLX: Failed to find a suitable GLXFBConfig\");\n        return false;\n    }\n\n    result = glXGetVisualFromFBConfig(_glfw.x11.display, native);\n    if (!result)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"GLX: Failed to retrieve Visual for GLXFBConfig\");\n        return false;\n    }\n\n    *visual = result->visual;\n    *depth  = result->depth;\n\n    XFree(result);\n    return true;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (window->context.client == GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);\n        return NULL;\n    }\n\n    return window->context.glx.handle;\n}\n\nGLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(None);\n\n    if (window->context.client == GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);\n        return None;\n    }\n\n    return window->context.glx.window;\n}\n"
  },
  {
    "path": "glfw/glx_context.h",
    "content": "//========================================================================\n// GLFW 3.4 GLX - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#define GLX_VENDOR 1\n#define GLX_RGBA_BIT 0x00000001\n#define GLX_WINDOW_BIT 0x00000001\n#define GLX_DRAWABLE_TYPE 0x8010\n#define GLX_RENDER_TYPE 0x8011\n#define GLX_RGBA_TYPE 0x8014\n#define GLX_DOUBLEBUFFER 5\n#define GLX_STEREO 6\n#define GLX_AUX_BUFFERS 7\n#define GLX_RED_SIZE 8\n#define GLX_GREEN_SIZE 9\n#define GLX_BLUE_SIZE 10\n#define GLX_ALPHA_SIZE 11\n#define GLX_DEPTH_SIZE 12\n#define GLX_STENCIL_SIZE 13\n#define GLX_ACCUM_RED_SIZE 14\n#define GLX_ACCUM_GREEN_SIZE 15\n#define GLX_ACCUM_BLUE_SIZE 16\n#define GLX_ACCUM_ALPHA_SIZE 17\n#define GLX_SAMPLES 0x186a1\n#define GLX_VISUAL_ID 0x800b\n\n#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2\n#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001\n#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002\n#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001\n#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126\n#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002\n#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091\n#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092\n#define GLX_CONTEXT_FLAGS_ARB 0x2094\n#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004\n#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004\n#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252\n#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256\n#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261\n#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097\n#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0\n#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098\n#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3\n\ntypedef XID GLXWindow;\ntypedef XID GLXDrawable;\ntypedef struct __GLXFBConfig* GLXFBConfig;\ntypedef struct __GLXcontext* GLXContext;\ntypedef void (*__GLXextproc)(void);\n\ntypedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*);\ntypedef GLXFBConfig* (*PFNGLXCHOOSEFBCONFIGPROC)(Display*,int,const int*,int*);\ntypedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int);\ntypedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*);\ntypedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*);\ntypedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext);\ntypedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext);\ntypedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable);\ntypedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int);\ntypedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*);\ntypedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool);\ntypedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName);\ntypedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int);\ntypedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig);\ntypedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*);\ntypedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow);\n\ntypedef int (*PFNGLXSWAPINTERVALMESAPROC)(int);\ntypedef int (*PFNGLXSWAPINTERVALSGIPROC)(int);\ntypedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*);\n\n// libGL.so function pointer typedefs\n#define glXGetFBConfigs _glfw.glx.GetFBConfigs\n#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib\n#define glXChooseFBConfig _glfw.glx.ChooseFBConfig\n#define glXGetClientString _glfw.glx.GetClientString\n#define glXQueryExtension _glfw.glx.QueryExtension\n#define glXQueryVersion _glfw.glx.QueryVersion\n#define glXDestroyContext _glfw.glx.DestroyContext\n#define glXMakeCurrent _glfw.glx.MakeCurrent\n#define glXSwapBuffers _glfw.glx.SwapBuffers\n#define glXQueryExtensionsString _glfw.glx.QueryExtensionsString\n#define glXCreateNewContext _glfw.glx.CreateNewContext\n#define glXGetVisualFromFBConfig _glfw.glx.GetVisualFromFBConfig\n#define glXCreateWindow _glfw.glx.CreateWindow\n#define glXDestroyWindow _glfw.glx.DestroyWindow\n\n#define _GLFW_PLATFORM_CONTEXT_STATE            _GLFWcontextGLX glx;\n#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE    _GLFWlibraryGLX glx;\n\n\n// GLX-specific per-context data\n//\ntypedef struct _GLFWcontextGLX\n{\n    GLXContext      handle;\n    GLXWindow       window;\n\n} _GLFWcontextGLX;\n\n// GLX-specific global data\n//\ntypedef struct _GLFWlibraryGLX\n{\n    int             major, minor;\n    int             eventBase;\n    int             errorBase;\n\n    // dlopen handle for libGL.so.1\n    void*           handle;\n\n    // GLX 1.3 functions\n    PFNGLXGETFBCONFIGSPROC              GetFBConfigs;\n    PFNGLXGETFBCONFIGATTRIBPROC         GetFBConfigAttrib;\n    PFNGLXCHOOSEFBCONFIGPROC            ChooseFBConfig;\n    PFNGLXGETCLIENTSTRINGPROC           GetClientString;\n    PFNGLXQUERYEXTENSIONPROC            QueryExtension;\n    PFNGLXQUERYVERSIONPROC              QueryVersion;\n    PFNGLXDESTROYCONTEXTPROC            DestroyContext;\n    PFNGLXMAKECURRENTPROC               MakeCurrent;\n    PFNGLXSWAPBUFFERSPROC               SwapBuffers;\n    PFNGLXQUERYEXTENSIONSSTRINGPROC     QueryExtensionsString;\n    PFNGLXCREATENEWCONTEXTPROC          CreateNewContext;\n    PFNGLXGETVISUALFROMFBCONFIGPROC     GetVisualFromFBConfig;\n    PFNGLXCREATEWINDOWPROC              CreateWindow;\n    PFNGLXDESTROYWINDOWPROC             DestroyWindow;\n\n    // GLX 1.4 and extension functions\n    PFNGLXGETPROCADDRESSPROC            GetProcAddress;\n    PFNGLXGETPROCADDRESSPROC            GetProcAddressARB;\n    PFNGLXSWAPINTERVALSGIPROC           SwapIntervalSGI;\n    PFNGLXSWAPINTERVALEXTPROC           SwapIntervalEXT;\n    PFNGLXSWAPINTERVALMESAPROC          SwapIntervalMESA;\n    PFNGLXCREATECONTEXTATTRIBSARBPROC   CreateContextAttribsARB;\n    bool            SGI_swap_control;\n    bool            EXT_swap_control;\n    bool            MESA_swap_control;\n    bool            ARB_multisample;\n    bool            ARB_framebuffer_sRGB;\n    bool            EXT_framebuffer_sRGB;\n    bool            ARB_create_context;\n    bool            ARB_create_context_profile;\n    bool            ARB_create_context_robustness;\n    bool            EXT_create_context_es2_profile;\n    bool            ARB_create_context_no_error;\n    bool            ARB_context_flush_control;\n\n} _GLFWlibraryGLX;\n\nbool _glfwInitGLX(void);\nvoid _glfwTerminateGLX(void);\nbool _glfwCreateContextGLX(_GLFWwindow* window,\n                               const _GLFWctxconfig* ctxconfig,\n                               const _GLFWfbconfig* fbconfig);\nvoid _glfwDestroyContextGLX(_GLFWwindow* window);\nbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,\n                              const _GLFWctxconfig* ctxconfig,\n                              const _GLFWfbconfig* fbconfig,\n                              Visual** visual, int* depth);\n"
  },
  {
    "path": "glfw/ibus_glfw.c",
    "content": "//========================================================================\n// GLFW 3.4 XKB - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n/* To test under X11 start IBUS as:\n * ibus-daemon -drxR\n * Setup the input sources you want with:\n * ibus-setup\n * Switch to the input source you want to test with:\n * ibus engine name\n * You can list available engines with:\n * ibus list-engine\n * Then run kitty as:\n * GLFW_IM_MODULE=ibus kitty\n */\n#define _GNU_SOURCE\n#include <stdio.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <limits.h>\n\n#include \"internal.h\"\n#include \"ibus_glfw.h\"\n\n#define debug debug_input\nstatic const char IBUS_SERVICE[]         = \"org.freedesktop.IBus\";\nstatic const char IBUS_PATH[]            = \"/org/freedesktop/IBus\";\nstatic const char IBUS_INTERFACE[]       = \"org.freedesktop.IBus\";\nstatic const char IBUS_INPUT_INTERFACE[] = \"org.freedesktop.IBus.InputContext\";\nenum Capabilities {\n    IBUS_CAP_PREEDIT_TEXT       = 1 << 0,\n    IBUS_CAP_AUXILIARY_TEXT     = 1 << 1,\n    IBUS_CAP_LOOKUP_TABLE       = 1 << 2,\n    IBUS_CAP_FOCUS              = 1 << 3,\n    IBUS_CAP_PROPERTY           = 1 << 4,\n    IBUS_CAP_SURROUNDING_TEXT   = 1 << 5\n};\n\ntypedef enum\n{\n    IBUS_SHIFT_MASK    = 1 << 0,\n    IBUS_LOCK_MASK     = 1 << 1,\n    IBUS_CONTROL_MASK  = 1 << 2,\n    IBUS_MOD1_MASK     = 1 << 3,\n    IBUS_MOD2_MASK     = 1 << 4,\n    IBUS_MOD3_MASK     = 1 << 5,\n    IBUS_MOD4_MASK     = 1 << 6,\n    IBUS_MOD5_MASK     = 1 << 7,\n    IBUS_BUTTON1_MASK  = 1 << 8,\n    IBUS_BUTTON2_MASK  = 1 << 9,\n    IBUS_BUTTON3_MASK  = 1 << 10,\n    IBUS_BUTTON4_MASK  = 1 << 11,\n    IBUS_BUTTON5_MASK  = 1 << 12,\n\n    /* The next few modifiers are used by XKB, so we skip to the end.\n     * Bits 15 - 23 are currently unused. Bit 29 is used internally.\n     */\n\n    /* ibus mask */\n    IBUS_HANDLED_MASK  = 1 << 24,\n    IBUS_FORWARD_MASK  = 1 << 25,\n    IBUS_IGNORED_MASK  = IBUS_FORWARD_MASK,\n\n    IBUS_SUPER_MASK    = 1 << 26,\n    IBUS_HYPER_MASK    = 1 << 27,\n    IBUS_META_MASK     = 1 << 28,\n\n    IBUS_RELEASE_MASK  = 1 << 30,\n\n    IBUS_MODIFIER_MASK = 0x5f001fff\n} IBusModifierType;\n\n\nstatic uint32_t\nibus_key_state_from_glfw(unsigned int glfw_modifiers, int action) {\n    uint32_t ans = action == GLFW_RELEASE ? IBUS_RELEASE_MASK : 0;\n#define M(g, i) if(glfw_modifiers & GLFW_MOD_##g) ans |= i\n    M(SHIFT, IBUS_SHIFT_MASK);\n    M(CAPS_LOCK, IBUS_LOCK_MASK);\n    M(CONTROL, IBUS_CONTROL_MASK);\n    M(ALT, IBUS_MOD1_MASK);\n    M(NUM_LOCK, IBUS_MOD2_MASK);\n    M(SUPER, IBUS_MOD4_MASK);\n    /* To do: figure out how to get super/hyper/meta */\n#undef M\n    return ans;\n}\n\nstatic unsigned int\nglfw_modifiers_from_ibus_state(uint32_t ibus_key_state) {\n    unsigned int ans = 0;\n#define M(g, i) if(ibus_key_state & i) ans |= GLFW_MOD_##g\n    M(SHIFT, IBUS_SHIFT_MASK);\n    M(CAPS_LOCK, IBUS_LOCK_MASK);\n    M(CONTROL, IBUS_CONTROL_MASK);\n    M(ALT, IBUS_MOD1_MASK);\n    M(NUM_LOCK, IBUS_MOD2_MASK);\n    M(SUPER, IBUS_MOD4_MASK);\n    /* To do: figure out how to get super/hyper/meta */\n#undef M\n    return ans;\n}\n\nstatic bool\ntest_env_var(const char *name, const char *val) {\n    const char *q = getenv(name);\n    return (q && strcmp(q, val) == 0) ? true : false;\n}\n\nstatic size_t\nGLFW_MIN(size_t a, size_t b) {\n    return a < b ? a : b;\n}\n\nstatic const char*\nget_ibus_text_from_message(DBusMessage *msg) {\n    /* The message structure is (from dbus-monitor)\n       variant       struct {\n         string \"IBusText\"\n         array [\n         ]\n         string \"ash \"\n         variant             struct {\n               string \"IBusAttrList\"\n               array [\n               ]\n               array [\n               ]\n            }\n      }\n    */\n    const char *text = NULL;\n    const char *struct_id = NULL;\n    DBusMessageIter iter, sub1, sub2;\n    dbus_message_iter_init(msg, &iter);\n\n    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return NULL;\n\n    dbus_message_iter_recurse(&iter, &sub1);\n\n    if (dbus_message_iter_get_arg_type(&sub1) != DBUS_TYPE_STRUCT) return NULL;\n\n    dbus_message_iter_recurse(&sub1, &sub2);\n\n    if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) return NULL;\n\n    dbus_message_iter_get_basic(&sub2, &struct_id);\n    if (!struct_id || strncmp(struct_id, \"IBusText\", sizeof(\"IBusText\")) != 0) return NULL;\n\n    dbus_message_iter_next(&sub2);\n    dbus_message_iter_next(&sub2);\n\n    if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) return NULL;\n\n    dbus_message_iter_get_basic(&sub2, &text);\n\n    return text;\n}\n\nstatic void\nhandle_ibus_forward_key_event(DBusMessage *msg) {\n    uint32_t keysym, keycode, state;\n    DBusMessageIter iter;\n    dbus_message_iter_init(msg, &iter);\n\n    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;\n    dbus_message_iter_get_basic(&iter, &keysym);\n    dbus_message_iter_next(&iter);\n\n    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;\n    dbus_message_iter_get_basic(&iter, &keycode);\n    dbus_message_iter_next(&iter);\n\n    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;\n    dbus_message_iter_get_basic(&iter, &state);\n\n    int mods = glfw_modifiers_from_ibus_state(state);\n\n    debug(\"IBUS: ForwardKeyEvent: keysym=%x, keycode=%x, state=%x, glfw_mods=%x\\n\", keysym, keycode, state, mods);\n    glfw_xkb_forwarded_key_from_ime(keysym, mods);\n}\n\nstatic void\nsend_text(const char *text, GLFWIMEState ime_state) {\n    _GLFWwindow *w = _glfwFocusedWindow();\n    if (w && w->callbacks.keyboard) {\n        GLFWkeyevent fake_ev = {.action = GLFW_PRESS};\n        fake_ev.text = text;\n        fake_ev.ime_state = ime_state;\n        w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);\n    }\n}\n\n// Connection handling {{{\n\nstatic DBusHandlerResult\nmessage_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data) {\n    // To monitor signals from IBUS, use\n    //  dbus-monitor --address `ibus address` \"type='signal',interface='org.freedesktop.IBus.InputContext'\"\n    _GLFWIBUSData *ibus = (_GLFWIBUSData*)user_data;\n    (void)ibus;\n    const char *text;\n    switch(glfw_dbus_match_signal(msg, IBUS_INPUT_INTERFACE, \"CommitText\", \"UpdatePreeditText\", \"HidePreeditText\", \"ShowPreeditText\", \"ForwardKeyEvent\", NULL)) {\n        case 0:\n            text = get_ibus_text_from_message(msg);\n            debug(\"IBUS: CommitText: '%s'\\n\", text ? text : \"(nil)\");\n            send_text(text, GLFW_IME_COMMIT_TEXT);\n            break;\n        case 1:\n            text = get_ibus_text_from_message(msg);\n            debug(\"IBUS: UpdatePreeditText: '%s'\\n\", text ? text : \"(nil)\");\n            send_text(text, GLFW_IME_PREEDIT_CHANGED);\n            break;\n        case 2:\n            debug(\"IBUS: HidePreeditText\\n\");\n            send_text(\"\", GLFW_IME_PREEDIT_CHANGED);\n            break;\n        case 3:\n            debug(\"IBUS: ShowPreeditText\\n\");\n            break;\n        case 4:\n            handle_ibus_forward_key_event(msg);\n            break;\n    }\n    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n}\n\nstatic DBusHandlerResult\nibus_on_owner_change(DBusConnection* conn UNUSED, DBusMessage* msg, void* user_data) {\n    if (dbus_message_is_signal(msg, \"org.freedesktop.DBus\", \"NameOwnerChanged\")) {\n        const char* name;\n        const char* old_owner;\n        const char* new_owner;\n\n        if (!dbus_message_get_args(msg, NULL,\n            DBUS_TYPE_STRING, &name,\n            DBUS_TYPE_STRING, &old_owner,\n            DBUS_TYPE_STRING, &new_owner,\n            DBUS_TYPE_INVALID\n        )) {\n            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n        }\n\n        if (strcmp(name, \"org.freedesktop.IBus\") != 0) {\n            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n        }\n\n        _GLFWIBUSData* ibus = (_GLFWIBUSData*) user_data;\n        ibus->name_owner_changed = true;\n\n        return DBUS_HANDLER_RESULT_HANDLED;\n\n    }\n\n    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n}\n\nstatic const char*\nget_ibus_address_file_name(void) {\n    const char *addr;\n    static char ans[PATH_MAX];\n    static char display[64] = {0};\n    addr = getenv(\"IBUS_ADDRESS\");\n    int offset = 0;\n    if (addr && addr[0]) {\n        memcpy(ans, addr, GLFW_MIN(strlen(addr), sizeof(ans)));\n        return ans;\n    }\n    const char* disp_num = NULL;\n    const char *host = \"unix\";\n    // See https://github.com/ibus/ibus/commit/8ce25208c3f4adfd290a032c6aa739d2b7580eb1 for why we need this dance.\n    const char *de = getenv(\"WAYLAND_DISPLAY\");\n    if (de) {\n        disp_num = de;\n    } else {\n        const char *de = getenv(\"DISPLAY\");\n        if (!de || !de[0]) de = \":0.0\";\n        strncpy(display, de, sizeof(display) - 1);\n        char *dnum = strrchr(display, ':');\n        if (!dnum) {\n            _glfwInputError(GLFW_PLATFORM_ERROR, \"Could not get IBUS address file name as DISPLAY env var has no colon\");\n            return NULL;\n        }\n        char *screen_num = strrchr(display, '.');\n        *dnum = 0;\n        dnum++;\n        if (screen_num) *screen_num = 0;\n        if (*display) host = display;\n        disp_num = dnum;\n    }\n\n    memset(ans, 0, sizeof(ans));\n    const char *conf_env = getenv(\"XDG_CONFIG_HOME\");\n    if (conf_env && conf_env[0]) {\n        offset = snprintf(ans, sizeof(ans), \"%s\", conf_env);\n    } else {\n        conf_env = getenv(\"HOME\");\n        if (!conf_env || !conf_env[0]) {\n            _glfwInputError(GLFW_PLATFORM_ERROR, \"Could not get IBUS address file name as no HOME env var is set\");\n            return NULL;\n        }\n        offset = snprintf(ans, sizeof(ans), \"%s/.config\", conf_env);\n    }\n    DBusError err;\n    char *key = dbus_try_get_local_machine_id(&err);\n    if (!key) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Cannot connect to IBUS as could not get DBUS local machine id with error %s: %s\", err.name ? err.name : \"\", err.message ? err.message : \"\");\n        return NULL;\n    }\n    snprintf(ans + offset, sizeof(ans) - offset, \"/ibus/bus/%s-%s-%s\", key, host, disp_num);\n    dbus_free(key);\n    return ans;\n}\n\n\nstatic bool\nread_ibus_address(_GLFWIBUSData *ibus) {\n    static char buf[1024];\n    struct stat s;\n    FILE *addr_file = fopen(ibus->address_file_name, \"r\");\n    if (!addr_file) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to open IBUS address file: %s with error: %s\", ibus->address_file_name, strerror(errno));\n        return false;\n    }\n    int stat_result = fstat(fileno(addr_file), &s);\n    bool found = false;\n    while (fgets(buf, sizeof(buf), addr_file)) {\n        if (strncmp(buf, \"IBUS_ADDRESS=\", sizeof(\"IBUS_ADDRESS=\")-1) == 0) {\n            size_t sz = strlen(buf);\n            if (buf[sz-1] == '\\n') buf[sz-1] = 0;\n            if (buf[sz-2] == '\\r') buf[sz-2] = 0;\n            found = true;\n            break;\n        }\n    }\n    fclose(addr_file); addr_file = NULL;\n    if (stat_result != 0) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to stat IBUS address file: %s with error: %s\", ibus->address_file_name, strerror(errno));\n        return false;\n    }\n    ibus->address_file_mtime = s.st_mtime;\n    if (found) {\n        free((void*)ibus->address);\n        ibus->address = _glfw_strdup(buf + sizeof(\"IBUS_ADDRESS=\") - 1);\n        return true;\n    }\n    _glfwInputError(GLFW_PLATFORM_ERROR, \"Could not find IBUS_ADDRESS in %s\", ibus->address_file_name);\n    return false;\n}\n\nvoid\ninput_context_created(DBusMessage *msg, const DBusError *err, void *data) {\n    if (err) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"IBUS: Failed to create input context with error: %s: %s\", err->name, err->message);\n        return;\n    }\n    const char *path = NULL;\n    if (!glfw_dbus_get_args(msg, \"Failed to get IBUS context path from reply\", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return;\n    _GLFWIBUSData *ibus = (_GLFWIBUSData*)data;\n    free((void*)ibus->input_ctx_path);\n    ibus->input_ctx_path = _glfw_strdup(path);\n    if (!ibus->input_ctx_path) return;\n    dbus_bus_add_match(ibus->conn, \"type='signal',interface='org.freedesktop.DBus', member='NameOwnerChanged'\", NULL);\n    dbus_connection_add_filter(ibus->conn, ibus_on_owner_change, ibus, free);\n    dbus_bus_add_match(ibus->conn, \"type='signal',interface='org.freedesktop.IBus.InputContext'\", NULL);\n    DBusObjectPathVTable ibus_vtable = {.message_function = message_handler};\n    dbus_connection_try_register_object_path(ibus->conn, ibus->input_ctx_path, &ibus_vtable, ibus, NULL);\n    enum Capabilities caps = IBUS_CAP_FOCUS | IBUS_CAP_PREEDIT_TEXT;\n    if (!glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, \"SetCapabilities\", DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID)) return;\n    ibus->ok = true;\n    glfw_ibus_set_focused(ibus, _glfwFocusedWindow() != NULL);\n    glfw_ibus_set_cursor_geometry(ibus, 0, 0, 0, 0);\n    debug(\"Connected to IBUS daemon for IME input management\\n\");\n}\n\nstatic bool\nsetup_connection(_GLFWIBUSData *ibus) {\n    const char *client_name = \"GLFW_Application\";\n    const char *address_file_name = get_ibus_address_file_name();\n    ibus->ok = false;\n    if (!address_file_name) return false;\n    free((void*)ibus->address_file_name);\n    ibus->address_file_name = _glfw_strdup(address_file_name);\n    if (!read_ibus_address(ibus)) return false;\n    if (ibus->conn) {\n        glfw_dbus_close_connection(ibus->conn);\n        ibus->conn = NULL;\n    }\n    debug(\"Connecting to IBUS daemon @ %s for IME input management\\n\", ibus->address);\n    ibus->conn = glfw_dbus_connect_to(ibus->address, \"Failed to connect to the IBUS daemon, with error\", \"ibus\", true);\n    if (!ibus->conn) return false;\n    free((void*)ibus->input_ctx_path); ibus->input_ctx_path = NULL;\n    if (!glfw_dbus_call_method_with_reply(\n            ibus->conn, IBUS_SERVICE, IBUS_PATH, IBUS_INTERFACE, \"CreateInputContext\", DBUS_TIMEOUT_USE_DEFAULT, input_context_created, ibus,\n            DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID)) {\n        return false;\n    }\n    return true;\n}\n\n\nvoid\nglfw_connect_to_ibus(_GLFWIBUSData *ibus) {\n    if (ibus->inited) return;\n    if (!test_env_var(\"GLFW_IM_MODULE\", \"ibus\")) return;\n    ibus->inited = true;\n    ibus->name_owner_changed = false;\n    setup_connection(ibus);\n}\n\nvoid\nglfw_ibus_terminate(_GLFWIBUSData *ibus) {\n    if (ibus->conn) {\n        glfw_dbus_close_connection(ibus->conn);\n        ibus->conn = NULL;\n    }\n#define F(x) if (ibus->x) { free((void*)ibus->x); ibus->x = NULL; }\n    F(input_ctx_path);\n    F(address);\n    F(address_file_name);\n#undef F\n\n    ibus->ok = false;\n}\n\nstatic bool\ncheck_connection(_GLFWIBUSData *ibus) {\n    if (!ibus->inited) return false;\n    if (ibus->conn && dbus_connection_get_is_connected(ibus->conn) && !ibus->name_owner_changed) return ibus->ok;\n    struct stat s;\n    ibus->name_owner_changed = false;\n    if (stat(ibus->address_file_name, &s) != 0 || s.st_mtime != ibus->address_file_mtime) {\n        if (!read_ibus_address(ibus)) return false;\n        return setup_connection(ibus);\n    }\n    return false;\n}\n\n\nvoid\nglfw_ibus_dispatch(_GLFWIBUSData *ibus) {\n    if (ibus->conn) glfw_dbus_dispatch(ibus->conn);\n}\n// }}}\n\nstatic void\nsimple_message(_GLFWIBUSData *ibus, const char *method) {\n    if (check_connection(ibus)) {\n        glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, method, DBUS_TYPE_INVALID);\n    }\n}\n\nvoid\nglfw_ibus_set_focused(_GLFWIBUSData *ibus, bool focused) {\n    simple_message(ibus, focused ? \"FocusIn\" : \"FocusOut\");\n}\n\nvoid\nglfw_ibus_set_cursor_geometry(_GLFWIBUSData *ibus, int x, int y, int w, int h) {\n    if (check_connection(ibus)) {\n        glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, \"SetCursorLocation\",\n                DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &w, DBUS_TYPE_INT32, &h, DBUS_TYPE_INVALID);\n    }\n}\n\nvoid\nkey_event_processed(DBusMessage *msg, const DBusError *err, void *data) {\n    uint32_t handled = 0;\n    _GLFWIBUSKeyEvent *ev = (_GLFWIBUSKeyEvent*)data;\n    // Restore key's text from the text embedded in the structure.\n    ev->glfw_ev.text = ev->__embedded_text;\n    bool is_release = ev->glfw_ev.action == GLFW_RELEASE;\n    bool failed = false;\n    if (err) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"IBUS: Failed to process key with error: %s: %s\", err->name, err->message);\n        failed = true;\n    } else {\n        glfw_dbus_get_args(msg, \"Failed to get IBUS handled key from reply\", DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID);\n        debug(\"IBUS processed native_key: 0x%x release: %d handled: %u\\n\", ev->glfw_ev.native_key, is_release, handled);\n    }\n    glfw_xkb_key_from_ime(ev, handled ? true : false, failed);\n    free(ev);\n}\n\nbool\nibus_process_key(const _GLFWIBUSKeyEvent *ev_, _GLFWIBUSData *ibus) {\n    if (!check_connection(ibus)) return false;\n    _GLFWIBUSKeyEvent *ev = calloc(1, sizeof(_GLFWIBUSKeyEvent));\n    if (!ev) return false;\n    memcpy(ev, ev_, sizeof(_GLFWIBUSKeyEvent));\n    // Put the key's text in a field IN the structure, for proper serialization.\n    if (ev->glfw_ev.text) strncpy(ev->__embedded_text, ev->glfw_ev.text, sizeof(ev->__embedded_text) - 1);\n    ev->glfw_ev.text = NULL;\n    uint32_t state = ibus_key_state_from_glfw(ev->glfw_ev.mods, ev->glfw_ev.action);\n    if (!glfw_dbus_call_method_with_reply(\n            ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, \"ProcessKeyEvent\",\n            3000, key_event_processed, ev,\n            DBUS_TYPE_UINT32, &ev->ibus_keysym, DBUS_TYPE_UINT32, &ev->ibus_keycode, DBUS_TYPE_UINT32,\n            &state, DBUS_TYPE_INVALID)) {\n        free(ev);\n        return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "glfw/ibus_glfw.h",
    "content": "//========================================================================\n// GLFW 3.4 XKB - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n\n#pragma once\n\n#include \"internal.h\"\n#include \"dbus_glfw.h\"\n#include <xkbcommon/xkbcommon.h>\n\ntypedef struct {\n    bool ok, inited, name_owner_changed;\n    time_t address_file_mtime;\n    DBusConnection *conn;\n    const char *input_ctx_path, *address_file_name, *address;\n} _GLFWIBUSData;\n\ntypedef struct {\n    xkb_keycode_t ibus_keycode;\n    xkb_keysym_t ibus_keysym;\n    GLFWid window_id;\n    GLFWkeyevent glfw_ev;\n    char __embedded_text[64];\n} _GLFWIBUSKeyEvent;\n\nvoid glfw_connect_to_ibus(_GLFWIBUSData *ibus);\nvoid glfw_ibus_terminate(_GLFWIBUSData *ibus);\nvoid glfw_ibus_set_focused(_GLFWIBUSData *ibus, bool focused);\nvoid glfw_ibus_dispatch(_GLFWIBUSData *ibus);\nbool ibus_process_key(const _GLFWIBUSKeyEvent *ev_, _GLFWIBUSData *ibus);\nvoid glfw_ibus_set_cursor_geometry(_GLFWIBUSData *ibus, int x, int y, int w, int h);\n"
  },
  {
    "path": "glfw/init.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// Please use C89 style variable declarations in this file because VS 2010\n//========================================================================\n\n#include \"internal.h\"\n#include \"mappings.h\"\n\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdarg.h>\n\n\n// The global variables below comprise all mutable global data in GLFW\n//\n// Any other global variable is a bug\n\n// Global state shared between compilation units of GLFW\n//\n_GLFWlibrary _glfw = { false };\n\n// These are outside of _glfw so they can be used before initialization and\n// after termination\n//\nstatic _GLFWerror _glfwMainThreadError;\nstatic GLFWerrorfun _glfwErrorCallback;\nstatic _GLFWinitconfig _glfwInitHints = {\n    .hatButtons = true,\n    .angleType = GLFW_ANGLE_PLATFORM_TYPE_NONE,\n    .debugKeyboard = false,\n    .debugRendering = false,\n    .ns = {\n        .menubar = true,  // macOS menu bar\n        .chdir = true   // macOS bundle chdir\n    },\n    .wl = {\n        .ime = true,  // Wayland IME support\n    },\n};\n\n// Terminate the library\n//\nstatic void terminate(void)\n{\n    int i;\n\n    memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks));\n    _glfw_free_clipboard_data(&_glfw.clipboard);\n    _glfw_free_clipboard_data(&_glfw.primary);\n\n    while (_glfw.windowListHead)\n        glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead);\n\n    while (_glfw.cursorListHead)\n        glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead);\n\n    for (i = 0;  i < _glfw.monitorCount;  i++)\n    {\n        _GLFWmonitor* monitor = _glfw.monitors[i];\n        if (monitor->originalRamp.size)\n            _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp);\n        _glfwFreeMonitor(monitor);\n    }\n\n    free(_glfw.monitors);\n    _glfw.monitors = NULL;\n    _glfw.monitorCount = 0;\n\n    free(_glfw.mappings);\n    _glfw.mappings = NULL;\n    _glfw.mappingCount = 0;\n\n    _glfwTerminateVulkan();\n    _glfwPlatformTerminateJoysticks();\n    _glfwPlatformTerminate();\n\n    _glfw.initialized = false;\n\n    while (_glfw.errorListHead)\n    {\n        _GLFWerror* error = _glfw.errorListHead;\n        _glfw.errorListHead = error->next;\n        free(error);\n    }\n\n    _glfwPlatformDestroyTls(&_glfw.contextSlot);\n    _glfwPlatformDestroyTls(&_glfw.errorSlot);\n    _glfwPlatformDestroyMutex(&_glfw.errorLock);\n\n    memset(&_glfw, 0, sizeof(_glfw));\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nchar* _glfw_strdup(const char* source)\n{\n    const size_t length = strlen(source);\n    char* result = malloc(length + 1);\n    memcpy(result, source, length);\n    result[length] = 0;\n    return result;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                         GLFW event API                       //////\n//////////////////////////////////////////////////////////////////////////\n\n// Notifies shared code of an error\n//\nvoid _glfwInputError(int code, const char* format, ...)\n{\n    _GLFWerror* error;\n    char description[_GLFW_MESSAGE_SIZE];\n\n    if (format)\n    {\n        va_list vl;\n\n        va_start(vl, format);\n        vsnprintf(description, sizeof(description), format, vl);\n        va_end(vl);\n\n        description[sizeof(description) - 1] = '\\0';\n    }\n    else\n    {\n        if (code == GLFW_NOT_INITIALIZED)\n            strcpy(description, \"The GLFW library is not initialized\");\n        else if (code == GLFW_NO_CURRENT_CONTEXT)\n            strcpy(description, \"There is no current context\");\n        else if (code == GLFW_INVALID_ENUM)\n            strcpy(description, \"Invalid argument for enum parameter\");\n        else if (code == GLFW_INVALID_VALUE)\n            strcpy(description, \"Invalid value for parameter\");\n        else if (code == GLFW_OUT_OF_MEMORY)\n            strcpy(description, \"Out of memory\");\n        else if (code == GLFW_API_UNAVAILABLE)\n            strcpy(description, \"The requested API is unavailable\");\n        else if (code == GLFW_VERSION_UNAVAILABLE)\n            strcpy(description, \"The requested API version is unavailable\");\n        else if (code == GLFW_PLATFORM_ERROR)\n            strcpy(description, \"A platform-specific error occurred\");\n        else if (code == GLFW_FORMAT_UNAVAILABLE)\n            strcpy(description, \"The requested format is unavailable\");\n        else if (code == GLFW_NO_WINDOW_CONTEXT)\n            strcpy(description, \"The specified window has no context\");\n        else if (code == GLFW_FEATURE_UNAVAILABLE)\n            strcpy(description, \"The requested feature cannot be implemented for this platform\");\n        else if (code == GLFW_FEATURE_UNIMPLEMENTED)\n            strcpy(description, \"The requested feature has not yet been implemented for this platform\");\n        else\n            strcpy(description, \"ERROR: UNKNOWN GLFW ERROR\");\n    }\n\n    if (_glfw.initialized)\n    {\n        error = _glfwPlatformGetTls(&_glfw.errorSlot);\n        if (!error)\n        {\n            error = calloc(1, sizeof(_GLFWerror));\n            _glfwPlatformSetTls(&_glfw.errorSlot, error);\n            _glfwPlatformLockMutex(&_glfw.errorLock);\n            error->next = _glfw.errorListHead;\n            _glfw.errorListHead = error;\n            _glfwPlatformUnlockMutex(&_glfw.errorLock);\n        }\n    }\n    else\n        error = &_glfwMainThreadError;\n\n    error->code = code;\n    strcpy(error->description, description);\n\n    if (_glfwErrorCallback)\n        _glfwErrorCallback(code, description);\n}\n\nvoid\n_glfwDebug(const char *format, ...) {\n    if (format)\n    {\n        va_list vl;\n\n        fprintf(stderr, \"[%.3f] \", monotonic_t_to_s_double(monotonic()));\n        va_start(vl, format);\n        vfprintf(stderr, format, vl);\n        va_end(vl);\n        fprintf(stderr, \"\\n\");\n    }\n\n}\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW public API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI int glfwInit(monotonic_t start_time, bool *supports_window_occlusion)\n{\n    *supports_window_occlusion = false;\n    if (_glfw.initialized)\n        return true;\n    monotonic_start_time = start_time;\n\n    memset(&_glfw, 0, sizeof(_glfw));\n    _glfw.hints.init = _glfwInitHints;\n    _glfw.ignoreOSKeyboardProcessing = false;\n\n    if (!_glfwPlatformInit(supports_window_occlusion))\n    {\n        terminate();\n        return false;\n    }\n\n    if (!_glfwPlatformCreateMutex(&_glfw.errorLock) ||\n        !_glfwPlatformCreateTls(&_glfw.errorSlot) ||\n        !_glfwPlatformCreateTls(&_glfw.contextSlot))\n    {\n        terminate();\n        return false;\n    }\n\n    _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError);\n\n    _glfw.initialized = true;\n\n    glfwDefaultWindowHints();\n\n    {\n        int i;\n\n        for (i = 0;  _glfwDefaultMappings[i];  i++)\n        {\n            if (!glfwUpdateGamepadMappings(_glfwDefaultMappings[i]))\n            {\n                terminate();\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\nGLFWAPI void glfwTerminate(void)\n{\n    if (!_glfw.initialized)\n        return;\n\n    terminate();\n}\n\nGLFWAPI void glfwInitHint(int hint, int value)\n{\n    switch (hint)\n    {\n        case GLFW_JOYSTICK_HAT_BUTTONS:\n            _glfwInitHints.hatButtons = value;\n            return;\n        case GLFW_ANGLE_PLATFORM_TYPE:\n            _glfwInitHints.angleType = value;\n            return;\n        case GLFW_DEBUG_KEYBOARD:\n            _glfwInitHints.debugKeyboard = value;\n            return;\n        case GLFW_DEBUG_RENDERING:\n            _glfwInitHints.debugRendering = value;\n            return;\n        case GLFW_COCOA_CHDIR_RESOURCES:\n            _glfwInitHints.ns.chdir = value;\n            return;\n        case GLFW_COCOA_MENUBAR:\n            _glfwInitHints.ns.menubar = value;\n            return;\n        case GLFW_WAYLAND_IME:\n            _glfwInitHints.wl.ime = value;\n            return;\n    }\n\n    _glfwInputError(GLFW_INVALID_ENUM,\n                    \"Invalid init hint 0x%08X\", hint);\n}\n\nGLFWAPI void glfwGetVersion(int* major, int* minor, int* rev)\n{\n    if (major != NULL)\n        *major = GLFW_VERSION_MAJOR;\n    if (minor != NULL)\n        *minor = GLFW_VERSION_MINOR;\n    if (rev != NULL)\n        *rev = GLFW_VERSION_REVISION;\n}\n\nGLFWAPI const char* glfwGetVersionString(void)\n{\n    return _glfwPlatformGetVersionString();\n}\n\nGLFWAPI int glfwGetError(const char** description)\n{\n    _GLFWerror* error;\n    int code = GLFW_NO_ERROR;\n\n    if (description)\n        *description = NULL;\n\n    if (_glfw.initialized)\n        error = _glfwPlatformGetTls(&_glfw.errorSlot);\n    else\n        error = &_glfwMainThreadError;\n\n    if (error)\n    {\n        code = error->code;\n        error->code = GLFW_NO_ERROR;\n        if (description && code)\n            *description = error->description;\n    }\n\n    return code;\n}\n\nGLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun)\n{\n    _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun);\n    return cbfun;\n}\n\n\nGLFWAPI void glfwRunMainLoop(GLFWtickcallback callback, void *data)\n{\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformRunMainLoop(callback, data);\n}\n\nGLFWAPI void glfwStopMainLoop(void) {\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformStopMainLoop();\n}\n\nGLFWAPI unsigned long long glfwAddTimer(\n        monotonic_t interval, bool repeats, GLFWuserdatafun callback,\n        void *callback_data, GLFWuserdatafun free_callback)\n{\n    return _glfwPlatformAddTimer(interval, repeats, callback, callback_data, free_callback);\n}\n\nGLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {\n    _glfwPlatformUpdateTimer(timer_id, interval, enabled);\n}\n\nGLFWAPI void glfwRemoveTimer(unsigned long long timer_id) {\n    _glfwPlatformRemoveTimer(timer_id);\n}\n\nGLFWAPI GLFWapplicationclosefun glfwSetApplicationCloseCallback(GLFWapplicationclosefun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.application_close, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWsystemcolorthemechangefun glfwSetSystemColorThemeChangeCallback(GLFWsystemcolorthemechangefun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.system_color_theme_change, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWclipboardlostfun glfwSetClipboardLostCallback(GLFWclipboardlostfun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.clipboard_lost, cbfun);\n    return cbfun;\n}\n\n\nGLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.draw_text, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.get_current_selection, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWimecursorpositionfun glfwSetIMECursorPositionCallback(GLFWimecursorpositionfun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.get_ime_cursor_position, cbfun);\n    return cbfun;\n}\n"
  },
  {
    "path": "glfw/input.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// Please use C89 style variable declarations in this file because VS 2010\n//========================================================================\n\n#include \"internal.h\"\n#include \"../kitty/monotonic.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <float.h>\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n\n// Internal key state used for sticky keys\n#define _GLFW_STICK 3\n\n// Internal constants for gamepad mapping source types\n#define _GLFW_JOYSTICK_AXIS     1\n#define _GLFW_JOYSTICK_BUTTON   2\n#define _GLFW_JOYSTICK_HATBIT   3\n\n// Initializes the platform joystick API if it has not been already\n//\nstatic bool initJoysticks(void)\n{\n    if (!_glfw.joysticksInitialized)\n    {\n        if (!_glfwPlatformInitJoysticks())\n        {\n            _glfwPlatformTerminateJoysticks();\n            return false;\n        }\n    }\n\n    return _glfw.joysticksInitialized = true;\n}\n\n// Finds a mapping based on joystick GUID\n//\nstatic _GLFWmapping* findMapping(const char* guid)\n{\n    int i;\n\n    for (i = 0;  i < _glfw.mappingCount;  i++)\n    {\n        if (strcmp(_glfw.mappings[i].guid, guid) == 0)\n            return _glfw.mappings + i;\n    }\n\n    return NULL;\n}\n\n// Checks whether a gamepad mapping element is present in the hardware\n//\nstatic bool isValidElementForJoystick(const _GLFWmapelement* e,\n                                          const _GLFWjoystick* js)\n{\n    if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount)\n        return false;\n    else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount)\n        return false;\n    else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount)\n        return false;\n\n    return true;\n}\n\n// Finds a mapping based on joystick GUID and verifies element indices\n//\nstatic _GLFWmapping* findValidMapping(const _GLFWjoystick* js)\n{\n    _GLFWmapping* mapping = findMapping(js->guid);\n    if (mapping)\n    {\n        int i;\n\n        for (i = 0;  i <= GLFW_GAMEPAD_BUTTON_LAST;  i++)\n        {\n            if (!isValidElementForJoystick(mapping->buttons + i, js))\n            {\n                _glfwInputError(GLFW_INVALID_VALUE,\n                                \"Invalid button in gamepad mapping %s (%s)\",\n                                mapping->guid,\n                                mapping->name);\n                return NULL;\n            }\n        }\n\n        for (i = 0;  i <= GLFW_GAMEPAD_AXIS_LAST;  i++)\n        {\n            if (!isValidElementForJoystick(mapping->axes + i, js))\n            {\n                _glfwInputError(GLFW_INVALID_VALUE,\n                                \"Invalid axis in gamepad mapping %s (%s)\",\n                                mapping->guid,\n                                mapping->name);\n                return NULL;\n            }\n        }\n    }\n\n    return mapping;\n}\n\n// Parses an SDL_GameControllerDB line and adds it to the mapping list\n//\nstatic bool parseMapping(_GLFWmapping* mapping, const char* string)\n{\n    const char* c = string;\n    size_t i, length;\n    struct\n    {\n        const char* name;\n        _GLFWmapelement* element;\n    } fields[] =\n    {\n        { \"platform\",      NULL },\n        { \"a\",             mapping->buttons + GLFW_GAMEPAD_BUTTON_A },\n        { \"b\",             mapping->buttons + GLFW_GAMEPAD_BUTTON_B },\n        { \"x\",             mapping->buttons + GLFW_GAMEPAD_BUTTON_X },\n        { \"y\",             mapping->buttons + GLFW_GAMEPAD_BUTTON_Y },\n        { \"back\",          mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK },\n        { \"start\",         mapping->buttons + GLFW_GAMEPAD_BUTTON_START },\n        { \"guide\",         mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE },\n        { \"leftshoulder\",  mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER },\n        { \"rightshoulder\", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER },\n        { \"leftstick\",     mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB },\n        { \"rightstick\",    mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB },\n        { \"dpup\",          mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP },\n        { \"dpright\",       mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT },\n        { \"dpdown\",        mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN },\n        { \"dpleft\",        mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT },\n        { \"lefttrigger\",   mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER },\n        { \"righttrigger\",  mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER },\n        { \"leftx\",         mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X },\n        { \"lefty\",         mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y },\n        { \"rightx\",        mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X },\n        { \"righty\",        mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y }\n    };\n\n    length = strcspn(c, \",\");\n    if (length != 32 || c[length] != ',')\n    {\n        _glfwInputError(GLFW_INVALID_VALUE, NULL);\n        return false;\n    }\n\n    memcpy(mapping->guid, c, length);\n    c += length + 1;\n\n    length = strcspn(c, \",\");\n    if (length >= sizeof(mapping->name) || c[length] != ',')\n    {\n        _glfwInputError(GLFW_INVALID_VALUE, NULL);\n        return false;\n    }\n\n    memcpy(mapping->name, c, length);\n    c += length + 1;\n\n    while (*c)\n    {\n        // TODO: Implement output modifiers\n        if (*c == '+' || *c == '-')\n            return false;\n\n        for (i = 0;  i < sizeof(fields) / sizeof(fields[0]);  i++)\n        {\n            length = strlen(fields[i].name);\n            if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':')\n                continue;\n\n            c += length + 1;\n\n            if (fields[i].element)\n            {\n                _GLFWmapelement* e = fields[i].element;\n                int8_t minimum = -1;\n                int8_t maximum = 1;\n\n                if (*c == '+')\n                {\n                    minimum = 0;\n                    c += 1;\n                }\n                else if (*c == '-')\n                {\n                    maximum = 0;\n                    c += 1;\n                }\n\n                if (*c == 'a')\n                    e->type = _GLFW_JOYSTICK_AXIS;\n                else if (*c == 'b')\n                    e->type = _GLFW_JOYSTICK_BUTTON;\n                else if (*c == 'h')\n                    e->type = _GLFW_JOYSTICK_HATBIT;\n                else\n                    break;\n\n                if (e->type == _GLFW_JOYSTICK_HATBIT)\n                {\n                    const unsigned long hat = strtoul(c + 1, (char**) &c, 10);\n                    const unsigned long bit = strtoul(c + 1, (char**) &c, 10);\n                    e->index = (uint8_t) ((hat << 4) | bit);\n                }\n                else\n                    e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10);\n\n                if (e->type == _GLFW_JOYSTICK_AXIS)\n                {\n                    e->axisScale = 2 / (maximum - minimum);\n                    e->axisOffset = -(maximum + minimum);\n\n                    if (*c == '~')\n                    {\n                        e->axisScale = -e->axisScale;\n                        e->axisOffset = -e->axisOffset;\n                    }\n                }\n            }\n            else\n            {\n                length = strlen(_GLFW_PLATFORM_MAPPING_NAME);\n                if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0)\n                    return false;\n            }\n\n            break;\n        }\n\n        c += strcspn(c, \",\");\n        c += strspn(c, \",\");\n    }\n\n    for (i = 0;  i < 32;  i++)\n    {\n        if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F')\n            mapping->guid[i] += 'a' - 'A';\n    }\n\n    _glfwPlatformUpdateGamepadGUID(mapping->guid);\n    return true;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                         GLFW event API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nstatic void\nset_key_action(_GLFWwindow *window, const GLFWkeyevent *ev, int action, int idx) {\n    const unsigned sz = arraysz(window->activated_keys);\n    if (idx < 0) {\n        for (unsigned i = 0; i < sz; i++) {\n            if (window->activated_keys[i].native_key_id == 0) {\n                idx = i;\n                break;\n            }\n        }\n        if (idx < 0) {\n            idx = sz - 1;\n            memmove(window->activated_keys, window->activated_keys + 1, sizeof(window->activated_keys[0]) * (sz - 1));\n            window->activated_keys[sz - 1].native_key_id = 0;\n        }\n    }\n    if (action == GLFW_RELEASE) {\n        memset(window->activated_keys + idx, 0, sizeof(window->activated_keys[0]));\n        if (idx < (int)sz - 1) {\n            memmove(window->activated_keys + idx, window->activated_keys + idx + 1, sizeof(window->activated_keys[0]) * (sz - 1 - idx));\n            memset(window->activated_keys + sz - 1, 0, sizeof(window->activated_keys[0]));\n        }\n    } else {\n        window->activated_keys[idx] = *ev;\n        window->activated_keys[idx].text = NULL;\n    }\n}\n\n// Notifies shared code of a physical key event\n//\nvoid _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)\n{\n    if (ev->native_key_id > 0)\n    {\n        bool repeated = false;\n        int idx = -1;\n        int current_action = GLFW_RELEASE;\n        const unsigned sz = arraysz(window->activated_keys);\n        for (unsigned i = 0; i < sz; i++) {\n            if (window->activated_keys[i].native_key_id == ev->native_key_id) {\n                idx = i;\n                current_action = window->activated_keys[i].action;\n                break;\n            }\n        }\n\n        if (ev->action == GLFW_RELEASE) {\n            if (current_action == GLFW_RELEASE) return;\n            if (idx > -1) {\n                const GLFWkeyevent *press_event = window->activated_keys + idx;\n                if (press_event->action == GLFW_PRESS || press_event->action == GLFW_REPEAT) {\n                    // Compose sequences under X11 give a different key value for press and release events\n                    // but we want the same key value so override it.\n                    ev->native_key = press_event->native_key;\n                    ev->key = press_event->key;\n                    ev->shifted_key = press_event->shifted_key;\n                    ev->alternate_key = press_event->alternate_key;\n                }\n            }\n        }\n\n        if (ev->action == GLFW_PRESS && current_action == GLFW_PRESS)\n            repeated = true;\n\n        set_key_action(window, ev, (ev->action == GLFW_RELEASE && window->stickyKeys) ? _GLFW_STICK : ev->action, idx);\n\n        if (repeated)\n            ev->action = GLFW_REPEAT;\n    }\n\n\n    // FIXME: will need to update ev->virtual_mods here too?\n    if (window->callbacks.keyboard) {\n        if (!window->lockKeyMods) ev->mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);\n        window->callbacks.keyboard((GLFWwindow*) window, ev);\n    }\n}\n\n// Notifies shared code of a scroll event\n//\nvoid _glfwInputScroll(_GLFWwindow* window, const GLFWScrollEvent *ev)\n{\n    if (window->callbacks.scroll)\n        window->callbacks.scroll((GLFWwindow*) window, ev);\n}\n\n// Notifies shared code of a mouse button click event\n//\nvoid _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods)\n{\n    if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST)\n        return;\n\n    if (!window->lockKeyMods)\n        mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);\n\n    if (action == GLFW_RELEASE && window->stickyMouseButtons)\n        window->mouseButtons[button] = _GLFW_STICK;\n    else\n        window->mouseButtons[button] = (char) action;\n\n    if (window->callbacks.mouseButton)\n        window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods);\n}\n\n// Notifies shared code of a cursor motion event\n// The position is specified in content area relative screen coordinates\n//\nvoid _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos)\n{\n    if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos)\n        return;\n\n    window->virtualCursorPosX = xpos;\n    window->virtualCursorPosY = ypos;\n\n    if (window->callbacks.cursorPos)\n        window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos);\n}\n\n// Notifies shared code of a cursor enter/leave event\n//\nvoid _glfwInputCursorEnter(_GLFWwindow* window, bool entered)\n{\n    if (window->callbacks.cursorEnter)\n        window->callbacks.cursorEnter((GLFWwindow*) window, entered);\n}\n\n// Notifies shared code of a drop event.\n// The caller is responsible for passing a mutable working-copy of the mimes\n// array (reset to the full original list before each call) so that the\n// callback can sort/filter in-place without touching the backend's canonical\n// storage.  The return value is ev.num_mimes after the callback returns,\n// i.e. the number of accepted (possibly reordered) mimes starting at\n// mimes[0].\nsize_t _glfwInputDropEvent(_GLFWwindow *window, GLFWDropEventType type, double xpos, double ypos, const char** mimes, size_t num_mimes, bool from_self) {\n    if (!window->callbacks.drop_event) return 0;\n    GLFWDropEvent ev = {\n        .mimes=mimes, .type=type, .xpos=xpos, .ypos=ypos, .num_mimes=num_mimes, .from_self=from_self,\n        .read_data=type == GLFW_DROP_DATA_AVAILABLE ? _glfwPlatformReadAvailableDropData : NULL,\n        .finish_drop=type == GLFW_DROP_DATA_AVAILABLE || type == GLFW_DROP_DROP ? _glfwPlatformEndDrop : NULL,\n    };\n    window->callbacks.drop_event((GLFWwindow*)window, &ev);\n    return ev.num_mimes;\n}\n\n// Notifies shared code that the OS wants data for a MIME type from the drag source\n//\nvoid _glfwInputDragSourceRequest(_GLFWwindow* window, GLFWDragEvent *ev) {\n    if (window->callbacks.drag_source)\n        window->callbacks.drag_source((GLFWwindow*) window, ev);\n}\n\n// Notifies shared code of a joystick connection or disconnection\n//\nvoid _glfwInputJoystick(_GLFWjoystick* js, int event)\n{\n    const int jid = (int) (js - _glfw.joysticks);\n\n    if (_glfw.callbacks.joystick)\n        _glfw.callbacks.joystick(jid, event);\n}\n\n// Notifies shared code of the new value of a joystick axis\n//\nvoid _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value)\n{\n    js->axes[axis] = value;\n}\n\n// Notifies shared code of the new value of a joystick button\n//\nvoid _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value)\n{\n    js->buttons[button] = value;\n}\n\n// Notifies shared code of the new value of a joystick hat\n//\nvoid _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value)\n{\n    const int base = js->buttonCount + hat * 4;\n\n    js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE;\n    js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE;\n    js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE;\n    js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE;\n\n    js->hats[hat] = value;\n}\n\nvoid _glfwInputColorScheme(GLFWColorScheme value, bool is_initial_value) {\n    _glfwPlatformInputColorScheme(value);\n    if (_glfw.callbacks.system_color_theme_change) _glfw.callbacks.system_color_theme_change(value, is_initial_value);\n}\n\nvoid _glfwInputClipboardLost(GLFWClipboardType which) {\n    if (_glfw.callbacks.clipboard_lost) _glfw.callbacks.clipboard_lost(which);\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Returns an available joystick object with arrays and name allocated\n//\n_GLFWjoystick* _glfwAllocJoystick(const char* name,\n                                  const char* guid,\n                                  int axisCount,\n                                  int buttonCount,\n                                  int hatCount)\n{\n    int jid;\n    _GLFWjoystick* js;\n\n    for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)\n    {\n        if (!_glfw.joysticks[jid].present)\n            break;\n    }\n\n    if (jid > GLFW_JOYSTICK_LAST)\n        return NULL;\n\n    js = _glfw.joysticks + jid;\n    js->present     = true;\n    js->name        = _glfw_strdup(name);\n    js->axes        = calloc(axisCount, sizeof(float));\n    js->buttons     = calloc(buttonCount + (size_t) hatCount * 4, 1);\n    js->hats        = calloc(hatCount, 1);\n    js->axisCount   = axisCount;\n    js->buttonCount = buttonCount;\n    js->hatCount    = hatCount;\n\n    strncpy(js->guid, guid, sizeof(js->guid) - 1);\n    js->mapping = findValidMapping(js);\n\n    return js;\n}\n\n// Frees arrays and name and flags the joystick object as unused\n//\nvoid _glfwFreeJoystick(_GLFWjoystick* js)\n{\n    free(js->name);\n    free(js->axes);\n    free(js->buttons);\n    free(js->hats);\n    memset(js, 0, sizeof(_GLFWjoystick));\n}\n\nunsigned int\nencode_utf8(uint32_t ch, char* dest) {\n    if (ch < 0x80) {\n        dest[0] = (char)ch;\n        return 1;\n    }\n    if (ch < 0x800) {\n        dest[0] = (ch>>6) | 0xC0;\n        dest[1] = (ch & 0x3F) | 0x80;\n        return 2;\n    }\n    if (ch < 0x10000) {\n        dest[0] = (ch>>12) | 0xE0;\n        dest[1] = ((ch>>6) & 0x3F) | 0x80;\n        dest[2] = (ch & 0x3F) | 0x80;\n        return 3;\n    }\n    if (ch < 0x110000) {\n        dest[0] = (ch>>18) | 0xF0;\n        dest[1] = ((ch>>12) & 0x3F) | 0x80;\n        dest[2] = ((ch>>6) & 0x3F) | 0x80;\n        dest[3] = (ch & 0x3F) | 0x80;\n        return 4;\n    }\n    return 0;\n}\n\nconst char*\n_glfwGetKeyName(int key)\n{\n    switch (key)\n    {\n        /* start functional key names (auto generated by gen-key-constants.py do not edit) */\n    case GLFW_FKEY_ESCAPE: return \"ESCAPE\";\n    case GLFW_FKEY_ENTER: return \"ENTER\";\n    case GLFW_FKEY_TAB: return \"TAB\";\n    case GLFW_FKEY_BACKSPACE: return \"BACKSPACE\";\n    case GLFW_FKEY_INSERT: return \"INSERT\";\n    case GLFW_FKEY_DELETE: return \"DELETE\";\n    case GLFW_FKEY_LEFT: return \"LEFT\";\n    case GLFW_FKEY_RIGHT: return \"RIGHT\";\n    case GLFW_FKEY_UP: return \"UP\";\n    case GLFW_FKEY_DOWN: return \"DOWN\";\n    case GLFW_FKEY_PAGE_UP: return \"PAGE_UP\";\n    case GLFW_FKEY_PAGE_DOWN: return \"PAGE_DOWN\";\n    case GLFW_FKEY_HOME: return \"HOME\";\n    case GLFW_FKEY_END: return \"END\";\n    case GLFW_FKEY_CAPS_LOCK: return \"CAPS_LOCK\";\n    case GLFW_FKEY_SCROLL_LOCK: return \"SCROLL_LOCK\";\n    case GLFW_FKEY_NUM_LOCK: return \"NUM_LOCK\";\n    case GLFW_FKEY_PRINT_SCREEN: return \"PRINT_SCREEN\";\n    case GLFW_FKEY_PAUSE: return \"PAUSE\";\n    case GLFW_FKEY_MENU: return \"MENU\";\n    case GLFW_FKEY_F1: return \"F1\";\n    case GLFW_FKEY_F2: return \"F2\";\n    case GLFW_FKEY_F3: return \"F3\";\n    case GLFW_FKEY_F4: return \"F4\";\n    case GLFW_FKEY_F5: return \"F5\";\n    case GLFW_FKEY_F6: return \"F6\";\n    case GLFW_FKEY_F7: return \"F7\";\n    case GLFW_FKEY_F8: return \"F8\";\n    case GLFW_FKEY_F9: return \"F9\";\n    case GLFW_FKEY_F10: return \"F10\";\n    case GLFW_FKEY_F11: return \"F11\";\n    case GLFW_FKEY_F12: return \"F12\";\n    case GLFW_FKEY_F13: return \"F13\";\n    case GLFW_FKEY_F14: return \"F14\";\n    case GLFW_FKEY_F15: return \"F15\";\n    case GLFW_FKEY_F16: return \"F16\";\n    case GLFW_FKEY_F17: return \"F17\";\n    case GLFW_FKEY_F18: return \"F18\";\n    case GLFW_FKEY_F19: return \"F19\";\n    case GLFW_FKEY_F20: return \"F20\";\n    case GLFW_FKEY_F21: return \"F21\";\n    case GLFW_FKEY_F22: return \"F22\";\n    case GLFW_FKEY_F23: return \"F23\";\n    case GLFW_FKEY_F24: return \"F24\";\n    case GLFW_FKEY_F25: return \"F25\";\n    case GLFW_FKEY_F26: return \"F26\";\n    case GLFW_FKEY_F27: return \"F27\";\n    case GLFW_FKEY_F28: return \"F28\";\n    case GLFW_FKEY_F29: return \"F29\";\n    case GLFW_FKEY_F30: return \"F30\";\n    case GLFW_FKEY_F31: return \"F31\";\n    case GLFW_FKEY_F32: return \"F32\";\n    case GLFW_FKEY_F33: return \"F33\";\n    case GLFW_FKEY_F34: return \"F34\";\n    case GLFW_FKEY_F35: return \"F35\";\n    case GLFW_FKEY_KP_0: return \"KP_0\";\n    case GLFW_FKEY_KP_1: return \"KP_1\";\n    case GLFW_FKEY_KP_2: return \"KP_2\";\n    case GLFW_FKEY_KP_3: return \"KP_3\";\n    case GLFW_FKEY_KP_4: return \"KP_4\";\n    case GLFW_FKEY_KP_5: return \"KP_5\";\n    case GLFW_FKEY_KP_6: return \"KP_6\";\n    case GLFW_FKEY_KP_7: return \"KP_7\";\n    case GLFW_FKEY_KP_8: return \"KP_8\";\n    case GLFW_FKEY_KP_9: return \"KP_9\";\n    case GLFW_FKEY_KP_DECIMAL: return \"KP_DECIMAL\";\n    case GLFW_FKEY_KP_DIVIDE: return \"KP_DIVIDE\";\n    case GLFW_FKEY_KP_MULTIPLY: return \"KP_MULTIPLY\";\n    case GLFW_FKEY_KP_SUBTRACT: return \"KP_SUBTRACT\";\n    case GLFW_FKEY_KP_ADD: return \"KP_ADD\";\n    case GLFW_FKEY_KP_ENTER: return \"KP_ENTER\";\n    case GLFW_FKEY_KP_EQUAL: return \"KP_EQUAL\";\n    case GLFW_FKEY_KP_SEPARATOR: return \"KP_SEPARATOR\";\n    case GLFW_FKEY_KP_LEFT: return \"KP_LEFT\";\n    case GLFW_FKEY_KP_RIGHT: return \"KP_RIGHT\";\n    case GLFW_FKEY_KP_UP: return \"KP_UP\";\n    case GLFW_FKEY_KP_DOWN: return \"KP_DOWN\";\n    case GLFW_FKEY_KP_PAGE_UP: return \"KP_PAGE_UP\";\n    case GLFW_FKEY_KP_PAGE_DOWN: return \"KP_PAGE_DOWN\";\n    case GLFW_FKEY_KP_HOME: return \"KP_HOME\";\n    case GLFW_FKEY_KP_END: return \"KP_END\";\n    case GLFW_FKEY_KP_INSERT: return \"KP_INSERT\";\n    case GLFW_FKEY_KP_DELETE: return \"KP_DELETE\";\n    case GLFW_FKEY_KP_BEGIN: return \"KP_BEGIN\";\n    case GLFW_FKEY_MEDIA_PLAY: return \"MEDIA_PLAY\";\n    case GLFW_FKEY_MEDIA_PAUSE: return \"MEDIA_PAUSE\";\n    case GLFW_FKEY_MEDIA_PLAY_PAUSE: return \"MEDIA_PLAY_PAUSE\";\n    case GLFW_FKEY_MEDIA_REVERSE: return \"MEDIA_REVERSE\";\n    case GLFW_FKEY_MEDIA_STOP: return \"MEDIA_STOP\";\n    case GLFW_FKEY_MEDIA_FAST_FORWARD: return \"MEDIA_FAST_FORWARD\";\n    case GLFW_FKEY_MEDIA_REWIND: return \"MEDIA_REWIND\";\n    case GLFW_FKEY_MEDIA_TRACK_NEXT: return \"MEDIA_TRACK_NEXT\";\n    case GLFW_FKEY_MEDIA_TRACK_PREVIOUS: return \"MEDIA_TRACK_PREVIOUS\";\n    case GLFW_FKEY_MEDIA_RECORD: return \"MEDIA_RECORD\";\n    case GLFW_FKEY_LOWER_VOLUME: return \"LOWER_VOLUME\";\n    case GLFW_FKEY_RAISE_VOLUME: return \"RAISE_VOLUME\";\n    case GLFW_FKEY_MUTE_VOLUME: return \"MUTE_VOLUME\";\n    case GLFW_FKEY_LEFT_SHIFT: return \"LEFT_SHIFT\";\n    case GLFW_FKEY_LEFT_CONTROL: return \"LEFT_CONTROL\";\n    case GLFW_FKEY_LEFT_ALT: return \"LEFT_ALT\";\n    case GLFW_FKEY_LEFT_SUPER: return \"LEFT_SUPER\";\n    case GLFW_FKEY_LEFT_HYPER: return \"LEFT_HYPER\";\n    case GLFW_FKEY_LEFT_META: return \"LEFT_META\";\n    case GLFW_FKEY_RIGHT_SHIFT: return \"RIGHT_SHIFT\";\n    case GLFW_FKEY_RIGHT_CONTROL: return \"RIGHT_CONTROL\";\n    case GLFW_FKEY_RIGHT_ALT: return \"RIGHT_ALT\";\n    case GLFW_FKEY_RIGHT_SUPER: return \"RIGHT_SUPER\";\n    case GLFW_FKEY_RIGHT_HYPER: return \"RIGHT_HYPER\";\n    case GLFW_FKEY_RIGHT_META: return \"RIGHT_META\";\n    case GLFW_FKEY_ISO_LEVEL3_SHIFT: return \"ISO_LEVEL3_SHIFT\";\n    case GLFW_FKEY_ISO_LEVEL5_SHIFT: return \"ISO_LEVEL5_SHIFT\";\n/* end functional key names */\n        case 0:                          return \"UNKNOWN\";\n    }\n    static char buf[16];\n    encode_utf8(key, buf);\n    return buf;\n}\n\n// Center the cursor in the content area of the specified window\n//\nvoid _glfwCenterCursorInContentArea(_GLFWwindow* window)\n{\n    int width, height;\n\n    _glfwPlatformGetWindowSize(window, &width, &height);\n    _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW public API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI int glfwRequestDropData(GLFWwindow *window, const char *mime) {\n    return _glfwPlatformRequestDropData((_GLFWwindow*)window, mime);\n}\n\nGLFWAPI void glfwEndDrop(GLFWwindow *window, GLFWDragOperationType op) {\n    _glfwPlatformEndDrop(window, op);\n}\n\nGLFWAPI void glfwRequestDropUpdate(GLFWwindow *window) {\n    _glfwPlatformRequestDropUpdate((_GLFWwindow*)window);\n}\n\nGLFWAPI bool glfwGetIgnoreOSKeyboardProcessing(void) {\n    return _glfw.ignoreOSKeyboardProcessing;\n}\n\nGLFWAPI void glfwSetIgnoreOSKeyboardProcessing(bool enabled) {\n    _glfw.ignoreOSKeyboardProcessing = enabled;\n}\n\nGLFWAPI bool glfwGrabKeyboard(int grab) {\n    if (grab == 0 || grab == 1) {\n        if (_glfwPlatformGrabKeyboard(grab)) _glfw.keyboard_grabbed = grab;\n    }\n    return _glfw.keyboard_grabbed;\n}\n\nGLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(0);\n\n    switch (mode)\n    {\n        case GLFW_CURSOR:\n            return window->cursorMode;\n        case GLFW_STICKY_KEYS:\n            return window->stickyKeys;\n        case GLFW_STICKY_MOUSE_BUTTONS:\n            return window->stickyMouseButtons;\n        case GLFW_LOCK_KEY_MODS:\n            return window->lockKeyMods;\n        case GLFW_RAW_MOUSE_MOTION:\n            return window->rawMouseMotion;\n    }\n\n    _glfwInputError(GLFW_INVALID_ENUM, \"Invalid input mode 0x%08X\", mode);\n    return 0;\n}\n\nGLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (mode == GLFW_CURSOR)\n    {\n        if (value != GLFW_CURSOR_NORMAL &&\n            value != GLFW_CURSOR_HIDDEN &&\n            value != GLFW_CURSOR_DISABLED)\n        {\n            _glfwInputError(GLFW_INVALID_ENUM,\n                            \"Invalid cursor mode 0x%08X\",\n                            value);\n            return;\n        }\n\n        if (window->cursorMode == value)\n            return;\n\n        window->cursorMode = value;\n\n        _glfwPlatformGetCursorPos(window, &window->virtualCursorPosX, &window->virtualCursorPosY);\n        _glfwPlatformSetCursorMode(window, value);\n    }\n    else if (mode == GLFW_STICKY_KEYS)\n    {\n        value = value ? true : false;\n        if (window->stickyKeys == value)\n            return;\n\n        if (!value)\n        {\n            // Release all sticky keys\n            for (unsigned i = arraysz(window->activated_keys) - 1;  i-- > 0;)\n            {\n                if (window->activated_keys[i].action == _GLFW_STICK) {\n                    if (i < arraysz(window->activated_keys) - 1) {\n                        memmove(window->activated_keys + i, window->activated_keys + i + 1, sizeof(window->activated_keys[0]) * (arraysz(window->activated_keys) - 1 - i));\n                    }\n                    memset(window->activated_keys + arraysz(window->activated_keys) - 1, 0, sizeof(window->activated_keys[0]));\n                }\n            }\n        }\n\n        window->stickyKeys = value;\n    }\n    else if (mode == GLFW_STICKY_MOUSE_BUTTONS)\n    {\n        value = value ? true : false;\n        if (window->stickyMouseButtons == value)\n            return;\n\n        if (!value)\n        {\n            int i;\n\n            // Release all sticky mouse buttons\n            for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)\n            {\n                if (window->mouseButtons[i] == _GLFW_STICK)\n                    window->mouseButtons[i] = GLFW_RELEASE;\n            }\n        }\n\n        window->stickyMouseButtons = value;\n    }\n    else if (mode == GLFW_LOCK_KEY_MODS)\n    {\n        window->lockKeyMods = value ? true : false;\n    }\n    else if (mode == GLFW_RAW_MOUSE_MOTION)\n    {\n        if (!_glfwPlatformRawMouseMotionSupported())\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"Raw mouse motion is not supported on this system\");\n            return;\n        }\n\n        value = value ? true : false;\n        if (window->rawMouseMotion == value)\n            return;\n\n        window->rawMouseMotion = value;\n        _glfwPlatformSetRawMouseMotion(window, value);\n    }\n    else\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid input mode 0x%08X\", mode);\n}\n\nGLFWAPI int glfwRawMouseMotionSupported(void)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n    return _glfwPlatformRawMouseMotionSupported();\n}\n\nGLFWAPI const char* glfwGetKeyName(uint32_t key, int native_key)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    if (key) return _glfwGetKeyName(key);\n\n    native_key = _glfwPlatformGetNativeKeyForKey(key);\n    return _glfwPlatformGetNativeKeyName(native_key);\n}\n\nGLFWAPI int glfwGetNativeKeyForKey(uint32_t key)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(-1);\n\n    return _glfwPlatformGetNativeKeyForKey(key);\n}\n\nGLFWAPI GLFWKeyAction glfwGetKey(GLFWwindow* handle, uint32_t key)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE);\n    if (!key) return GLFW_RELEASE;\n\n    int current_action = GLFW_RELEASE;\n    const unsigned sz = arraysz(window->activated_keys);\n    int idx = -1;\n    for (unsigned i = 0; i < sz; i++) {\n        if (window->activated_keys[i].key == key) {\n            idx = i;\n            current_action = window->activated_keys[i].action;\n            break;\n        }\n    }\n\n\n    if (current_action == _GLFW_STICK)\n    {\n        // Sticky mode: release key now\n        GLFWkeyevent ev = {0};\n        set_key_action(window, &ev, GLFW_RELEASE, idx);\n        current_action = GLFW_PRESS;\n    }\n\n    return current_action;\n}\n\nGLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE);\n\n    if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid mouse button %i\", button);\n        return GLFW_RELEASE;\n    }\n\n    if (window->mouseButtons[button] == _GLFW_STICK)\n    {\n        // Sticky mode: release mouse button now\n        window->mouseButtons[button] = GLFW_RELEASE;\n        return GLFW_PRESS;\n    }\n\n    return (int) window->mouseButtons[button];\n}\n\nGLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    if (xpos)\n        *xpos = 0;\n    if (ypos)\n        *ypos = 0;\n\n    _GLFW_REQUIRE_INIT();\n\n    if (window->cursorMode == GLFW_CURSOR_DISABLED)\n    {\n        if (xpos)\n            *xpos = window->virtualCursorPosX;\n        if (ypos)\n            *ypos = window->virtualCursorPosY;\n    }\n    else\n        _glfwPlatformGetCursorPos(window, xpos, ypos);\n}\n\nGLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (xpos != xpos || xpos < -DBL_MAX || xpos > DBL_MAX ||\n        ypos != ypos || ypos < -DBL_MAX || ypos > DBL_MAX)\n    {\n        _glfwInputError(GLFW_INVALID_VALUE,\n                        \"Invalid cursor position %f %f\",\n                        xpos, ypos);\n        return;\n    }\n\n    if (!_glfwPlatformWindowFocused(window))\n        return;\n\n    if (window->cursorMode == GLFW_CURSOR_DISABLED)\n    {\n        // Only update the accumulated position if the cursor is disabled\n        window->virtualCursorPosX = xpos;\n        window->virtualCursorPosY = ypos;\n    }\n    else\n    {\n        // Update system cursor position\n        _glfwPlatformSetCursorPos(window, xpos, ypos);\n    }\n}\n\nGLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot, int count)\n{\n    _GLFWcursor* cursor;\n\n    assert(image != NULL);\n    assert(count > 0);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    cursor = calloc(1, sizeof(_GLFWcursor));\n    cursor->next = _glfw.cursorListHead;\n    _glfw.cursorListHead = cursor;\n\n    if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot, count))\n    {\n        glfwDestroyCursor((GLFWcursor*) cursor);\n        return NULL;\n    }\n\n    return (GLFWcursor*) cursor;\n}\n\nGLFWAPI GLFWcursor* glfwCreateStandardCursor(GLFWCursorShape shape)\n{\n    _GLFWcursor* cursor;\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (shape >= GLFW_INVALID_CURSOR)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid standard cursor: %d\", shape);\n        return NULL;\n    }\n\n    cursor = calloc(1, sizeof(_GLFWcursor));\n    cursor->next = _glfw.cursorListHead;\n    _glfw.cursorListHead = cursor;\n\n    if (!_glfwPlatformCreateStandardCursor(cursor, shape))\n    {\n        glfwDestroyCursor((GLFWcursor*) cursor);\n        return NULL;\n    }\n\n    return (GLFWcursor*) cursor;\n}\n\nGLFWAPI void glfwDestroyCursor(GLFWcursor* handle)\n{\n    _GLFWcursor* cursor = (_GLFWcursor*) handle;\n\n    _GLFW_REQUIRE_INIT();\n\n    if (cursor == NULL)\n        return;\n\n    // Make sure the cursor is not being used by any window\n    {\n        _GLFWwindow* window;\n\n        for (window = _glfw.windowListHead;  window;  window = window->next)\n        {\n            if (window->cursor == cursor)\n                glfwSetCursor((GLFWwindow*) window, NULL);\n        }\n    }\n\n    _glfwPlatformDestroyCursor(cursor);\n\n    // Unlink cursor from global linked list\n    {\n        _GLFWcursor** prev = &_glfw.cursorListHead;\n\n        while (*prev != cursor)\n            prev = &((*prev)->next);\n\n        *prev = cursor->next;\n    }\n\n    free(cursor);\n}\n\nGLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) windowHandle;\n    _GLFWcursor* cursor = (_GLFWcursor*) cursorHandle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    window->cursor = cursor;\n\n    _glfwPlatformSetCursor(window, cursor);\n}\n\nGLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* handle, GLFWkeyboardfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.keyboard, cbfun);\n    return cbfun;\n}\n\nGLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, const GLFWIMEUpdateEvent *ev) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(_GLFW_COCOA)\n    _glfwPlatformUpdateIMEState(window, ev);\n#else\n    (void)window; (void)which; (void)a; (void)b; (void)c; (void)d;\n#endif\n}\n\nGLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle,\n                                                      GLFWmousebuttonfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle,\n                                                  GLFWcursorposfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle,\n                                                      GLFWcursorenterfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle,\n                                            GLFWscrollfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWdropeventfun glfwSetDropEventCallback(GLFWwindow* handle, GLFWdropeventfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.drop_event, cbfun);\n    return cbfun;\n}\n\n\nGLFWAPI GLFWdragsourcefun glfwSetDragSourceCallback(GLFWwindow* handle, GLFWdragsourcefun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.drag_source, cbfun);\n    return cbfun;\n}\n\nvoid\n_glfwFreeDragSourceData(void) {\n    _glfwPlatformFreeDragSourceData();\n    if (_glfw.drag.items) {\n        for (size_t i = 0; i < _glfw.drag.item_count; i++) {\n            free((void*)_glfw.drag.items[i].mime_type);\n            free((void*)_glfw.drag.items[i].optional_data);\n        }\n        free(_glfw.drag.items);\n    }\n    GLFWid iid = _glfw.drag.instance_id;\n    memset(&_glfw.drag, 0, sizeof(_glfw.drag));\n    _glfw.drag.instance_id = iid;\n}\n\nGLFWAPI int\nglfwStartDrag(GLFWwindow* handle, const GLFWDragSourceItem *items, size_t item_count, const GLFWimage* thumbnail, int operations, bool needs_toplevel_on_wayland) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n    _GLFW_REQUIRE_INIT_OR_RETURN(EINVAL);\n    if (operations == -1) return _glfwPlatformDragDataReady(items[0].mime_type);\n    if (operations == -2) return _glfwPlatformChangeDragImage(thumbnail);\n    _glfwFreeDragSourceData();\n    _glfw.drag.instance_id++;\n    if (!items || !item_count) return 0;\n    _glfw.drag.items = calloc(item_count, sizeof(_glfw.drag.items[0]));\n    if (!_glfw.drag.items) return ENOMEM;\n    _glfw.drag.item_count = item_count;\n    for (size_t i = 0; i < item_count; i++) {\n        if (!items[i].mime_type || !items[i].mime_type[0]) {\n            _glfwFreeDragSourceData(); return EINVAL;\n        }\n        _glfw.drag.items[i].mime_type = _glfw_strdup(items[i].mime_type);\n        if (!_glfw.drag.items[i].mime_type) { _glfwFreeDragSourceData(); return ENOMEM; }\n        if (items[i].optional_data) {\n            _glfw.drag.items[i].optional_data = malloc(items[i].data_size);\n            if (!_glfw.drag.items[i].optional_data) { _glfwFreeDragSourceData(); return ENOMEM; }\n            memcpy((void*)_glfw.drag.items[i].optional_data, items[i].optional_data, items[i].data_size);\n        }\n        _glfw.drag.items[i].data_size = items[i].data_size;\n    }\n    _glfw.drag.window_id = window->id;\n    _glfw.drag.operations = operations;\n    _glfw.drag.needs_toplevel_on_wayland = needs_toplevel_on_wayland;\n    int ans = _glfwPlatformStartDrag(window, thumbnail);\n    if (ans != 0) _glfwFreeDragSourceData();\n    return ans;\n}\n\nGLFWAPI int glfwJoystickPresent(int jid)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return false;\n    }\n\n    if (!initJoysticks())\n        return false;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return false;\n\n    return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE);\n}\n\nGLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n    assert(count != NULL);\n\n    *count = 0;\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return NULL;\n    }\n\n    if (!initJoysticks())\n        return NULL;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return NULL;\n\n    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES))\n        return NULL;\n\n    *count = js->axisCount;\n    return js->axes;\n}\n\nGLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n    assert(count != NULL);\n\n    *count = 0;\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return NULL;\n    }\n\n    if (!initJoysticks())\n        return NULL;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return NULL;\n\n    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS))\n        return NULL;\n\n    if (_glfw.hints.init.hatButtons)\n        *count = js->buttonCount + js->hatCount * 4;\n    else\n        *count = js->buttonCount;\n\n    return js->buttons;\n}\n\nGLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n    assert(count != NULL);\n\n    *count = 0;\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return NULL;\n    }\n\n    if (!initJoysticks())\n        return NULL;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return NULL;\n\n    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS))\n        return NULL;\n\n    *count = js->hatCount;\n    return js->hats;\n}\n\nGLFWAPI const char* glfwGetJoystickName(int jid)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return NULL;\n    }\n\n    if (!initJoysticks())\n        return NULL;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return NULL;\n\n    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))\n        return NULL;\n\n    return js->name;\n}\n\nGLFWAPI const char* glfwGetJoystickGUID(int jid)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return NULL;\n    }\n\n    if (!initJoysticks())\n        return NULL;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return NULL;\n\n    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))\n        return NULL;\n\n    return js->guid;\n}\n\nGLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n\n    _GLFW_REQUIRE_INIT();\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return;\n\n    js->userPointer = pointer;\n}\n\nGLFWAPI void* glfwGetJoystickUserPointer(int jid)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return NULL;\n\n    return js->userPointer;\n}\n\nGLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (!initJoysticks())\n        return NULL;\n\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun);\n    return cbfun;\n}\n\nGLFWAPI int glfwUpdateGamepadMappings(const char* string)\n{\n    int jid;\n    const char* c = string;\n\n    assert(string != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    while (*c)\n    {\n        if ((*c >= '0' && *c <= '9') ||\n            (*c >= 'a' && *c <= 'f') ||\n            (*c >= 'A' && *c <= 'F'))\n        {\n            char line[1024];\n\n            const size_t length = strcspn(c, \"\\r\\n\");\n            if (length < sizeof(line))\n            {\n                _GLFWmapping mapping = {{0}};\n\n                memcpy(line, c, length);\n                line[length] = '\\0';\n\n                if (parseMapping(&mapping, line))\n                {\n                    _GLFWmapping* previous = findMapping(mapping.guid);\n                    if (previous)\n                        *previous = mapping;\n                    else\n                    {\n                        _glfw.mappingCount++;\n                        _glfw.mappings =\n                            realloc(_glfw.mappings,\n                                    sizeof(_GLFWmapping) * _glfw.mappingCount);\n                        _glfw.mappings[_glfw.mappingCount - 1] = mapping;\n                    }\n                }\n            }\n\n            c += length;\n        }\n        else\n        {\n            c += strcspn(c, \"\\r\\n\");\n            c += strspn(c, \"\\r\\n\");\n        }\n    }\n\n    for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)\n    {\n        _GLFWjoystick* js = _glfw.joysticks + jid;\n        if (js->present)\n            js->mapping = findValidMapping(js);\n    }\n\n    return true;\n}\n\nGLFWAPI int glfwJoystickIsGamepad(int jid)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return false;\n    }\n\n    if (!initJoysticks())\n        return false;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return false;\n\n    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))\n        return false;\n\n    return js->mapping != NULL;\n}\n\nGLFWAPI const char* glfwGetGamepadName(int jid)\n{\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return NULL;\n    }\n\n    if (!initJoysticks())\n        return NULL;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return NULL;\n\n    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))\n        return NULL;\n\n    if (!js->mapping)\n        return NULL;\n\n    return js->mapping->name;\n}\n\nGLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)\n{\n    int i;\n    _GLFWjoystick* js;\n\n    assert(jid >= GLFW_JOYSTICK_1);\n    assert(jid <= GLFW_JOYSTICK_LAST);\n    assert(state != NULL);\n\n    memset(state, 0, sizeof(GLFWgamepadstate));\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    if (jid < 0 || jid > GLFW_JOYSTICK_LAST)\n    {\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid joystick ID %i\", jid);\n        return false;\n    }\n\n    if (!initJoysticks())\n        return false;\n\n    js = _glfw.joysticks + jid;\n    if (!js->present)\n        return false;\n\n    if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL))\n        return false;\n\n    if (!js->mapping)\n        return false;\n\n    for (i = 0;  i <= GLFW_GAMEPAD_BUTTON_LAST;  i++)\n    {\n        const _GLFWmapelement* e = js->mapping->buttons + i;\n        if (e->type == _GLFW_JOYSTICK_AXIS)\n        {\n            const float value = js->axes[e->index] * e->axisScale + e->axisOffset;\n            // HACK: This should be baked into the value transform\n            // TODO: Bake into transform when implementing output modifiers\n            if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0))\n            {\n                if (value >= 0.f)\n                    state->buttons[i] = GLFW_PRESS;\n            }\n            else\n            {\n                if (value <= 0.f)\n                    state->buttons[i] = GLFW_PRESS;\n            }\n        }\n        else if (e->type == _GLFW_JOYSTICK_HATBIT)\n        {\n            const unsigned int hat = e->index >> 4;\n            const unsigned int bit = e->index & 0xf;\n            if (js->hats[hat] & bit)\n                state->buttons[i] = GLFW_PRESS;\n        }\n        else if (e->type == _GLFW_JOYSTICK_BUTTON)\n            state->buttons[i] = js->buttons[e->index];\n    }\n\n    for (i = 0;  i <= GLFW_GAMEPAD_AXIS_LAST;  i++)\n    {\n        const _GLFWmapelement* e = js->mapping->axes + i;\n        if (e->type == _GLFW_JOYSTICK_AXIS)\n        {\n            const float value = js->axes[e->index] * e->axisScale + e->axisOffset;\n            state->axes[i] = fminf(fmaxf(value, -1.f), 1.f);\n        }\n        else if (e->type == _GLFW_JOYSTICK_HATBIT)\n        {\n            const unsigned int hat = e->index >> 4;\n            const unsigned int bit = e->index & 0xf;\n            if (js->hats[hat] & bit)\n                state->axes[i] = 1.f;\n            else\n                state->axes[i] = -1.f;\n        }\n        else if (e->type == _GLFW_JOYSTICK_BUTTON)\n            state->axes[i] = js->buttons[e->index] * 2.f - 1.f;\n    }\n\n    return true;\n}\n\nvoid _glfw_free_clipboard_data(_GLFWClipboardData *cd) {\n    if (cd->mime_types) {\n        for (size_t i = 0; i < cd->num_mime_types; i++) free((void*)cd->mime_types[i]);\n        free((void*)cd->mime_types);\n    }\n    memset(cd, 0, sizeof(cd[0]));\n}\n\nGLFWAPI void glfwGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformGetClipboard(clipboard_type, mime_type, write_data, object);\n}\n\nGLFWAPI void glfwSetClipboardDataTypes(GLFWClipboardType clipboard_type, const char* const *mime_types, size_t num_mime_types, GLFWclipboarditerfun get_data) {\n    assert(mime_types != NULL);\n    assert(get_data != NULL);\n    _GLFW_REQUIRE_INIT();\n    _GLFWClipboardData *cd = NULL;\n    switch(clipboard_type) {\n        case GLFW_CLIPBOARD: cd = &_glfw.clipboard; break;\n        case GLFW_PRIMARY_SELECTION: cd = &_glfw.primary; break;\n    }\n    _glfw_free_clipboard_data(cd);\n    cd->get_data = get_data;\n    cd->mime_types = calloc(num_mime_types, sizeof(char*));\n    cd->num_mime_types = 0;\n    cd->ctype = clipboard_type;\n    for (size_t i = 0; i < num_mime_types; i++) {\n        if (mime_types[i]) {\n            cd->mime_types[cd->num_mime_types++] = _glfw_strdup(mime_types[i]);\n        }\n    }\n    _glfwPlatformSetClipboard(clipboard_type);\n}\n\nGLFWAPI monotonic_t glfwGetTime(void)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(0);\n    return monotonic();\n}\n"
  },
  {
    "path": "glfw/internal.h",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#pragma once\n\n#include \"../kitty/monotonic.h\"\n\n#if defined(_GLFW_USE_CONFIG_H)\n #include \"glfw_config.h\"\n#endif\n\n#define arraysz(x) (sizeof(x)/sizeof(x[0]))\n#define MAX(x, y) __extension__ ({ \\\n    __typeof__ (x) a = (x); __typeof__ (y) b = (y); \\\n        a > b ? a : b;})\n\n#if defined(GLFW_INCLUDE_GLCOREARB) || \\\n    defined(GLFW_INCLUDE_ES1)       || \\\n    defined(GLFW_INCLUDE_ES2)       || \\\n    defined(GLFW_INCLUDE_ES3)       || \\\n    defined(GLFW_INCLUDE_ES31)      || \\\n    defined(GLFW_INCLUDE_ES32)      || \\\n    defined(GLFW_INCLUDE_NONE)      || \\\n    defined(GLFW_INCLUDE_GLEXT)     || \\\n    defined(GLFW_INCLUDE_GLU)       || \\\n    defined(GLFW_INCLUDE_VULKAN)    || \\\n    defined(GLFW_DLL)\n #error \"You must not define any header option macros when compiling GLFW\"\n#endif\n\n#define GLFW_INCLUDE_NONE\n#include \"glfw3.h\"\n\n#define EGL_PRESENT_OPAQUE_EXT 0x31df\n\n#define _GLFW_INSERT_FIRST      0\n#define _GLFW_INSERT_LAST       1\n\n#define _GLFW_POLL_PRESENCE     0\n#define _GLFW_POLL_AXES         1\n#define _GLFW_POLL_BUTTONS      2\n#define _GLFW_POLL_ALL          (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS)\n\n#define _GLFW_MESSAGE_SIZE      1024\n\ntypedef unsigned long long GLFWid;\n\ntypedef struct _GLFWerror       _GLFWerror;\ntypedef struct _GLFWinitconfig  _GLFWinitconfig;\ntypedef struct _GLFWwndconfig   _GLFWwndconfig;\ntypedef struct _GLFWctxconfig   _GLFWctxconfig;\ntypedef struct _GLFWfbconfig    _GLFWfbconfig;\ntypedef struct _GLFWcontext     _GLFWcontext;\ntypedef struct _GLFWwindow      _GLFWwindow;\ntypedef struct _GLFWlibrary     _GLFWlibrary;\ntypedef struct _GLFWmonitor     _GLFWmonitor;\ntypedef struct _GLFWcursor      _GLFWcursor;\ntypedef struct _GLFWmapelement  _GLFWmapelement;\ntypedef struct _GLFWmapping     _GLFWmapping;\ntypedef struct _GLFWjoystick    _GLFWjoystick;\ntypedef struct _GLFWtls         _GLFWtls;\ntypedef struct _GLFWmutex       _GLFWmutex;\n\ntypedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*);\ntypedef void (* _GLFWswapbuffersfun)(_GLFWwindow*);\ntypedef void (* _GLFWswapintervalfun)(int);\ntypedef int (* _GLFWextensionsupportedfun)(const char*);\ntypedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*);\ntypedef void (* _GLFWdestroycontextfun)(_GLFWwindow*);\n\n#define GL_VERSION 0x1F02\n#define GL_NONE 0\n#define GL_COLOR_BUFFER_BIT 0x00004000\n#define GL_UNSIGNED_BYTE 0x1401\n#define GL_EXTENSIONS 0x1F03\n#define GL_NUM_EXTENSIONS 0x821d\n#define GL_CONTEXT_FLAGS 0x821e\n#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001\n#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002\n#define GL_CONTEXT_PROFILE_MASK 0x9126\n#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002\n#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001\n#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256\n#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252\n#define GL_NO_RESET_NOTIFICATION_ARB 0x8261\n#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82fb\n#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82fc\n#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008\n\n#define MAX(x, y) __extension__ ({ \\\n    __typeof__ (x) a = (x); __typeof__ (y) b = (y); \\\n        a > b ? a : b;})\n#define MIN(x, y) __extension__ ({ \\\n    __typeof__ (x) a = (x); __typeof__ (y) b = (y); \\\n        a < b ? a : b;})\n\ntypedef int GLint;\ntypedef unsigned int GLuint;\ntypedef unsigned int GLenum;\ntypedef unsigned int GLbitfield;\ntypedef unsigned char GLubyte;\n\ntypedef void (APIENTRY * PFNGLCLEARPROC)(GLbitfield);\ntypedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum);\ntypedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*);\ntypedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint);\n\n#define VK_NULL_HANDLE 0\n\ntypedef void* VkInstance;\ntypedef void* VkPhysicalDevice;\ntypedef uint64_t VkSurfaceKHR;\ntypedef uint32_t VkFlags;\ntypedef uint32_t VkBool32;\n\ntypedef enum VkStructureType\n{\n    VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000,\n    VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000,\n    VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,\n    VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,\n    VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,\n    VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000,\n    VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF\n} VkStructureType;\n\ntypedef enum VkResult\n{\n    VK_SUCCESS = 0,\n    VK_NOT_READY = 1,\n    VK_TIMEOUT = 2,\n    VK_EVENT_SET = 3,\n    VK_EVENT_RESET = 4,\n    VK_INCOMPLETE = 5,\n    VK_ERROR_OUT_OF_HOST_MEMORY = -1,\n    VK_ERROR_OUT_OF_DEVICE_MEMORY = -2,\n    VK_ERROR_INITIALIZATION_FAILED = -3,\n    VK_ERROR_DEVICE_LOST = -4,\n    VK_ERROR_MEMORY_MAP_FAILED = -5,\n    VK_ERROR_LAYER_NOT_PRESENT = -6,\n    VK_ERROR_EXTENSION_NOT_PRESENT = -7,\n    VK_ERROR_FEATURE_NOT_PRESENT = -8,\n    VK_ERROR_INCOMPATIBLE_DRIVER = -9,\n    VK_ERROR_TOO_MANY_OBJECTS = -10,\n    VK_ERROR_FORMAT_NOT_SUPPORTED = -11,\n    VK_ERROR_SURFACE_LOST_KHR = -1000000000,\n    VK_SUBOPTIMAL_KHR = 1000001003,\n    VK_ERROR_OUT_OF_DATE_KHR = -1000001004,\n    VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001,\n    VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001,\n    VK_ERROR_VALIDATION_FAILED_EXT = -1000011001,\n    VK_RESULT_MAX_ENUM = 0x7FFFFFFF\n} VkResult;\n\ntypedef struct VkAllocationCallbacks VkAllocationCallbacks;\n\ntypedef struct VkExtensionProperties\n{\n    char            extensionName[256];\n    uint32_t        specVersion;\n} VkExtensionProperties;\n\ntypedef void (APIENTRY * PFN_vkVoidFunction)(void);\n\n#if defined(_GLFW_VULKAN_STATIC)\n  PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance,const char*);\n  VkResult vkEnumerateInstanceExtensionProperties(const char*,uint32_t*,VkExtensionProperties*);\n#else\n  typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*);\n  typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*);\n  #define vkEnumerateInstanceExtensionProperties _glfw.vk.EnumerateInstanceExtensionProperties\n  #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr\n#endif\n\n#if defined(_GLFW_COCOA)\n #include \"cocoa_platform.h\"\n#elif defined(_GLFW_WIN32)\n #include \"win32_platform.h\"\n#elif defined(_GLFW_X11)\n #include \"x11_platform.h\"\n#elif defined(_GLFW_WAYLAND)\n #include \"wl_platform.h\"\n#elif defined(_GLFW_OSMESA)\n #include \"null_platform.h\"\n#else\n #error \"No supported window creation API selected\"\n#endif\n\n#include \"egl_context.h\"\n#include \"osmesa_context.h\"\n\n#define remove_i_from_array(array, i, count) { \\\n    (count)--; \\\n    if ((i) < (count)) { \\\n        memmove((array) + (i), (array) + (i) + 1, sizeof((array)[0]) * ((count) - (i))); /* NOLINT(bugprone-sizeof-expression) */ \\\n    }}\n\n\n// Constructs a version number string from the public header macros\n#define _GLFW_CONCAT_VERSION(m, n, r) #m \".\" #n \".\" #r\n#define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r)\n#define _GLFW_VERSION_NUMBER _GLFW_MAKE_VERSION(GLFW_VERSION_MAJOR, \\\n                                                GLFW_VERSION_MINOR, \\\n                                                GLFW_VERSION_REVISION)\n\n// Checks for whether the library has been initialized\n#define _GLFW_REQUIRE_INIT()                         \\\n    if (!_glfw.initialized)                          \\\n    {                                                \\\n        _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \\\n        return;                                      \\\n    }\n#define _GLFW_REQUIRE_INIT_OR_RETURN(x)              \\\n    if (!_glfw.initialized)                          \\\n    {                                                \\\n        _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \\\n        return x;                                    \\\n    }\n\n// Swaps the provided pointers\n#define _GLFW_SWAP_POINTERS(x, y) \\\n    do{                           \\\n        __typeof__(x) t;          \\\n        t = x;                    \\\n        x = y;                    \\\n        y = t;                    \\\n    }while(0)\n\n\n// Suppress some pedantic warnings\n#ifdef __clang__\n#define START_ALLOW_CASE_RANGE _Pragma(\"clang diagnostic push\") _Pragma(\"clang diagnostic ignored \\\"-Wpedantic\\\"\")\n#define END_ALLOW_CASE_RANGE _Pragma(\"clang diagnostic pop\")\n#define ALLOW_UNUSED_RESULT _Pragma(\"clang diagnostic push\") _Pragma(\"clang diagnostic ignored \\\"-Wunused-result\\\"\")\n#define END_ALLOW_UNUSED_RESULT _Pragma(\"clang diagnostic pop\")\n#else\n#define START_ALLOW_CASE_RANGE _Pragma(\"GCC diagnostic ignored \\\"-Wpedantic\\\"\")\n#define END_ALLOW_CASE_RANGE _Pragma(\"GCC diagnostic pop\")\n#define ALLOW_UNUSED_RESULT _Pragma(\"GCC diagnostic ignored \\\"-Wunused-result\\\"\")\n#define END_ALLOW_UNUSED_RESULT _Pragma(\"GCC diagnostic pop\")\n#endif\n\n// dlsym that works with -Wpedantic\n#define glfw_dlsym(dest, handle, name) do {*(void **)&(dest) = _glfw_dlsym(handle, name);}while (0)\n\n// Mark function arguments as unused\n#define UNUSED __attribute__ ((unused))\n\n// Per-thread error structure\n//\nstruct _GLFWerror\n{\n    _GLFWerror*     next;\n    int             code;\n    char            description[_GLFW_MESSAGE_SIZE];\n};\n\n// Initialization configuration\n//\n// Parameters relating to the initialization of the library\n//\nstruct _GLFWinitconfig\n{\n    bool          hatButtons;\n    int           angleType;\n    bool          debugKeyboard;\n    bool          debugRendering;\n    struct {\n        bool      menubar;\n        bool      chdir;\n    } ns;\n    struct {\n        bool ime;\n    } wl;\n};\n\n// Window configuration\n//\n// Parameters relating to the creation of the window but not directly related\n// to the framebuffer.  This is used to pass window creation parameters from\n// shared code to the platform API.\n//\nstruct _GLFWwndconfig\n{\n    int           width;\n    int           height;\n    const char*   title;\n    bool          resizable;\n    bool          visible;\n    bool          decorated;\n    bool          focused;\n    bool          autoIconify;\n    bool          floating;\n    bool          maximized;\n    bool          centerCursor;\n    bool          focusOnShow;\n    bool          mousePassthrough;\n    bool          scaleToMonitor;\n    int           blur_radius;\n    struct {\n        bool      retina;\n        int       color_space;\n        char      frameName[256];\n    } ns;\n    struct {\n        char      className[256];\n        char      instanceName[256];\n    } x11;\n    struct {\n        char      appId[256], windowTag[256];\n        uint32_t  bgcolor;\n    } wl;\n};\n\n// Context configuration\n//\n// Parameters relating to the creation of the context but not directly related\n// to the framebuffer.  This is used to pass context creation parameters from\n// shared code to the platform API.\n//\nstruct _GLFWctxconfig\n{\n    int           client;\n    int           source;\n    int           major;\n    int           minor;\n    bool          forward;\n    bool          debug;\n    bool          noerror;\n    int           profile;\n    int           robustness;\n    int           release;\n    _GLFWwindow*  share;\n    struct {\n        bool      offline;\n    } nsgl;\n};\n\n// Framebuffer configuration\n//\n// This describes buffers and their sizes.  It also contains\n// a platform-specific ID used to map back to the backend API object.\n//\n// It is used to pass framebuffer parameters from shared code to the platform\n// API and also to enumerate and select available framebuffer configs.\n//\nstruct _GLFWfbconfig\n{\n    int         redBits;\n    int         greenBits;\n    int         blueBits;\n    int         alphaBits;\n    int         depthBits;\n    int         stencilBits;\n    int         accumRedBits;\n    int         accumGreenBits;\n    int         accumBlueBits;\n    int         accumAlphaBits;\n    int         auxBuffers;\n    bool        stereo;\n    int         samples;\n    bool        sRGB;\n    bool        doublebuffer;\n    bool        transparent;\n    uintptr_t   handle;\n};\n\n// Context structure\n//\nstruct _GLFWcontext\n{\n    int                 client;\n    int                 source;\n    int                 major, minor, revision;\n    bool                forward, debug, noerror;\n    int                 profile;\n    int                 robustness;\n    int                 release;\n\n    PFNGLGETSTRINGIPROC GetStringi;\n    PFNGLGETINTEGERVPROC GetIntegerv;\n    PFNGLGETSTRINGPROC  GetString;\n\n    _GLFWmakecontextcurrentfun  makeCurrent;\n    _GLFWswapbuffersfun         swapBuffers;\n    _GLFWswapintervalfun        swapInterval;\n    _GLFWextensionsupportedfun  extensionSupported;\n    _GLFWgetprocaddressfun      getProcAddress;\n    _GLFWdestroycontextfun      destroy;\n\n    // This is defined in the context API's context.h\n    _GLFW_PLATFORM_CONTEXT_STATE\n    // This is defined in egl_context.h\n    _GLFWcontextEGL egl;\n    // This is defined in osmesa_context.h\n    _GLFWcontextOSMesa osmesa;\n};\n\n// Window and context structure\n//\nstruct _GLFWwindow\n{\n    struct _GLFWwindow* next;\n\n    // Window settings and state\n    bool                resizable;\n    bool                decorated;\n    bool                autoIconify;\n    bool                floating;\n    bool                focusOnShow;\n    bool                mousePassthrough;\n    bool                shouldClose;\n    void*               userPointer;\n    GLFWid              id;\n    GLFWvidmode         videoMode;\n    _GLFWmonitor*       monitor;\n    _GLFWcursor*        cursor;\n\n    int                 minwidth, minheight;\n    int                 maxwidth, maxheight;\n    int                 numer, denom;\n    int                 widthincr, heightincr;\n\n    bool                stickyKeys;\n    bool                stickyMouseButtons;\n    bool                lockKeyMods;\n    int                 cursorMode;\n    char                mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];\n    GLFWkeyevent        activated_keys[16];\n    // Virtual cursor position when cursor is disabled\n    double              virtualCursorPosX, virtualCursorPosY;\n    bool                rawMouseMotion;\n\n    _GLFWcontext        context;\n#ifdef _GLFW_WAYLAND\n    bool                swaps_disallowed;\n#else\n    const bool                swaps_disallowed;\n#endif\n\n    struct {\n        GLFWwindowposfun        pos;\n        GLFWwindowsizefun       size;\n        GLFWwindowclosefun      close;\n        GLFWwindowrefreshfun    refresh;\n        GLFWwindowfocusfun      focus;\n        GLFWwindowocclusionfun  occlusion;\n        GLFWwindowiconifyfun    iconify;\n        GLFWwindowmaximizefun   maximize;\n        GLFWframebuffersizefun  fbsize;\n        GLFWwindowcontentscalefun scale;\n        GLFWmousebuttonfun      mouseButton;\n        GLFWcursorposfun        cursorPos;\n        GLFWcursorenterfun      cursorEnter;\n        GLFWscrollfun           scroll;\n        GLFWkeyboardfun         keyboard;\n        GLFWliveresizefun       liveResize;\n\n        GLFWdragsourcefun       drag_source;\n        GLFWdropeventfun drop_event;\n    } callbacks;\n\n    // This is defined in the window API's platform.h\n    _GLFW_PLATFORM_WINDOW_STATE;\n};\n\n// Monitor structure\n//\nstruct _GLFWmonitor\n{\n    const char *name, *description;\n    void*           userPointer;\n\n    // Physical dimensions in millimeters.\n    int             widthMM, heightMM;\n\n    // The window whose video mode is current on this monitor\n    _GLFWwindow*    window;\n\n    GLFWvidmode*    modes;\n    int             modeCount;\n    GLFWvidmode     currentMode;\n\n    GLFWgammaramp   originalRamp;\n    GLFWgammaramp   currentRamp;\n\n    // This is defined in the window API's platform.h\n    _GLFW_PLATFORM_MONITOR_STATE;\n};\n\n// Cursor structure\n//\nstruct _GLFWcursor\n{\n    _GLFWcursor*    next;\n\n    // This is defined in the window API's platform.h\n    _GLFW_PLATFORM_CURSOR_STATE;\n};\n\n// Gamepad mapping element structure\n//\nstruct _GLFWmapelement\n{\n    uint8_t         type;\n    uint8_t         index;\n    int8_t          axisScale;\n    int8_t          axisOffset;\n};\n\n// Gamepad mapping structure\n//\nstruct _GLFWmapping\n{\n    char            name[128];\n    char            guid[33];\n    _GLFWmapelement buttons[15];\n    _GLFWmapelement axes[6];\n};\n\n// Joystick structure\n//\nstruct _GLFWjoystick\n{\n    bool            present;\n    float*          axes;\n    int             axisCount;\n    unsigned char*  buttons;\n    int             buttonCount;\n    unsigned char*  hats;\n    int             hatCount;\n    char*           name;\n    void*           userPointer;\n    char            guid[33];\n    _GLFWmapping*   mapping;\n\n    // This is defined in the joystick API's joystick.h\n    _GLFW_PLATFORM_JOYSTICK_STATE;\n};\n\n// Thread local storage structure\n//\nstruct _GLFWtls\n{\n    // This is defined in the platform's thread.h\n    _GLFW_PLATFORM_TLS_STATE;\n};\n\n// Mutex structure\n//\nstruct _GLFWmutex\n{\n    // This is defined in the platform's thread.h\n    _GLFW_PLATFORM_MUTEX_STATE;\n};\n\ntypedef struct _GLFWClipboardData {\n    const char** mime_types;\n    size_t num_mime_types;\n    GLFWclipboarditerfun get_data;\n    GLFWClipboardType ctype;\n} _GLFWClipboardData;\n\n// Library global data\n//\nstruct _GLFWlibrary\n{\n    bool                initialized;\n\n    struct {\n        _GLFWinitconfig init;\n        _GLFWfbconfig   framebuffer;\n        _GLFWwndconfig  window;\n        _GLFWctxconfig  context;\n        int             refreshRate;\n    } hints;\n\n    _GLFWClipboardData primary, clipboard;\n\n    _GLFWerror*         errorListHead;\n    _GLFWcursor*        cursorListHead;\n    _GLFWwindow*        windowListHead;\n    GLFWid              focusedWindowId;\n\n    _GLFWmonitor**      monitors;\n    int                 monitorCount;\n\n    bool                joysticksInitialized;\n    _GLFWjoystick       joysticks[GLFW_JOYSTICK_LAST + 1];\n    _GLFWmapping*       mappings;\n    int                 mappingCount;\n\n    _GLFWtls            errorSlot;\n    _GLFWtls            contextSlot;\n    _GLFWmutex          errorLock;\n\n    bool                ignoreOSKeyboardProcessing, keyboard_grabbed;\n\n    struct {\n        bool            available;\n        void*           handle;\n        char*           extensions[2];\n#if !defined(_GLFW_VULKAN_STATIC)\n        PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties;\n        PFN_vkGetInstanceProcAddr GetInstanceProcAddr;\n#endif\n        bool            KHR_surface;\n#if defined(_GLFW_WIN32)\n        bool            KHR_win32_surface;\n#elif defined(_GLFW_COCOA)\n        bool            MVK_macos_surface;\n        bool            EXT_metal_surface;\n#elif defined(_GLFW_X11)\n        bool            KHR_xlib_surface;\n        bool            KHR_xcb_surface;\n#elif defined(_GLFW_WAYLAND)\n        bool            KHR_wayland_surface;\n#endif\n    } vk;\n\n    struct {\n        GLFWmonitorfun  monitor;\n        GLFWjoystickfun joystick;\n        GLFWapplicationclosefun application_close;\n        GLFWclipboardlostfun clipboard_lost;\n        GLFWsystemcolorthemechangefun system_color_theme_change;\n        GLFWdrawtextfun draw_text;\n        GLFWcurrentselectionfun get_current_selection;\n        GLFWhascurrentselectionfun has_current_selection;\n        GLFWimecursorpositionfun get_ime_cursor_position;\n    } callbacks;\n\n    // This is defined in the window API's platform.h\n    _GLFW_PLATFORM_LIBRARY_WINDOW_STATE;\n    // This is defined in the context API's context.h\n    _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE\n    // This is defined in the platform's joystick.h\n    _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE\n    // This is defined in egl_context.h\n    _GLFWlibraryEGL egl;\n    // This is defined in osmesa_context.h\n    _GLFWlibraryOSMesa osmesa;\n\n    struct {\n        GLFWDragSourceItem *items; size_t item_count;\n        GLFWid window_id, instance_id; int operations;\n        bool needs_toplevel_on_wayland;\n    } drag;\n};\n\n// Global state shared between compilation units of GLFW\n//\nextern _GLFWlibrary _glfw;\n\ntypedef struct GeometryRect { int x, y, width, height; } GeometryRect;\ntypedef struct MonitorGeometry {\n    GeometryRect full, workarea;\n} MonitorGeometry;\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nint _glfwPlatformInit(bool*);\nvoid _glfwPlatformTerminate(void);\nconst char* _glfwPlatformGetVersionString(void);\n\nvoid _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos);\nvoid _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos);\nvoid _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode);\nvoid _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled);\nbool _glfwPlatformRawMouseMotionSupported(void);\nint _glfwPlatformCreateCursor(_GLFWcursor* cursor,\n                              const GLFWimage* image, int xhot, int yhot, int count);\nint _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape);\nvoid _glfwPlatformDestroyCursor(_GLFWcursor* cursor);\nvoid _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);\n\nconst char* _glfwPlatformGetNativeKeyName(int native_key);\nint _glfwPlatformGetNativeKeyForKey(uint32_t key);\n\nvoid _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);\nvoid _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);\nvoid _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,\n                                         float* xscale, float* yscale);\nvoid _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height);\nGLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count);\nbool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);\nbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);\nvoid _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);\n\nvoid _glfwPlatformSetClipboard(GLFWClipboardType t);\nvoid _glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object);\n\nbool _glfwPlatformInitJoysticks(void);\nvoid _glfwPlatformTerminateJoysticks(void);\nint _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);\nvoid _glfwPlatformUpdateGamepadGUID(char* guid);\n\nint _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, const GLFWLayerShellConfig *lsc);\nvoid _glfwPlatformDestroyWindow(_GLFWwindow* window);\nvoid _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title);\nvoid _glfwPlatformSetWindowIcon(_GLFWwindow* window,\n                                int count, const GLFWimage* images);\nbool _glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig *value);\nconst GLFWLayerShellConfig* _glfwPlatformGetLayerShellConfig(_GLFWwindow* window);\nvoid _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos);\nvoid _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos);\nvoid _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height);\nvoid _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height);\nvoid _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,\n                                      int minwidth, int minheight,\n                                      int maxwidth, int maxheight);\nvoid _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom);\nvoid _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr, int heightincr);\nvoid _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height);\nvoid _glfwInputLiveResize(_GLFWwindow* window, bool started);\nvoid _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,\n                                     int* left, int* top,\n                                     int* right, int* bottom);\nvoid _glfwPlatformGetWindowContentScale(_GLFWwindow* window,\n                                        float* xscale, float* yscale);\nmonotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window);\nvoid _glfwPlatformIconifyWindow(_GLFWwindow* window);\nvoid _glfwPlatformRestoreWindow(_GLFWwindow* window);\nvoid _glfwPlatformMaximizeWindow(_GLFWwindow* window);\nvoid _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen);\nvoid _glfwPlatformHideWindow(_GLFWwindow* window);\nvoid _glfwPlatformRequestWindowAttention(_GLFWwindow* window);\nint _glfwPlatformWindowBell(_GLFWwindow* window);\nvoid _glfwPlatformFocusWindow(_GLFWwindow* window);\nvoid _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,\n                                   int xpos, int ypos, int width, int height,\n                                   int refreshRate);\nbool _glfwPlatformToggleFullscreen(_GLFWwindow *w, unsigned int flags);\nbool _glfwPlatformIsFullscreen(_GLFWwindow *w, unsigned int flags);\nint _glfwPlatformWindowFocused(_GLFWwindow* window);\nint _glfwPlatformWindowOccluded(_GLFWwindow* window);\nint _glfwPlatformWindowIconified(_GLFWwindow* window);\nint _glfwPlatformWindowVisible(_GLFWwindow* window);\nint _glfwPlatformWindowMaximized(_GLFWwindow* window);\nint _glfwPlatformWindowHovered(_GLFWwindow* window);\nint _glfwPlatformFramebufferTransparent(_GLFWwindow* window);\nfloat _glfwPlatformGetWindowOpacity(_GLFWwindow* window);\nvoid _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled);\nvoid _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled);\nvoid _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled);\nvoid _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled);\nvoid _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);\nvoid _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev);\nvoid _glfwPlatformChangeCursorTheme(void);\n\nvoid _glfwPlatformPollEvents(void);\nvoid _glfwPlatformWaitEvents(void);\nvoid _glfwPlatformWaitEventsTimeout(monotonic_t timeout);\nvoid _glfwPlatformPostEmptyEvent(void);\n\nEGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs);\nEGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void);\nEGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window);\n\nvoid _glfwPlatformGetRequiredInstanceExtensions(char** extensions);\nint _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,\n                                                      VkPhysicalDevice device,\n                                                      uint32_t queuefamily);\nVkResult _glfwPlatformCreateWindowSurface(VkInstance instance,\n                                          _GLFWwindow* window,\n                                          const VkAllocationCallbacks* allocator,\n                                          VkSurfaceKHR* surface);\n\nbool _glfwPlatformCreateTls(_GLFWtls* tls);\nvoid _glfwPlatformDestroyTls(_GLFWtls* tls);\nvoid* _glfwPlatformGetTls(_GLFWtls* tls);\nvoid _glfwPlatformSetTls(_GLFWtls* tls, void* value);\n\nbool _glfwPlatformCreateMutex(_GLFWmutex* mutex);\nvoid _glfwPlatformDestroyMutex(_GLFWmutex* mutex);\nvoid _glfwPlatformLockMutex(_GLFWmutex* mutex);\nvoid _glfwPlatformUnlockMutex(_GLFWmutex* mutex);\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                         GLFW event API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid _glfwInputWindowFocus(_GLFWwindow* window, bool focused);\nvoid _glfwInputWindowOcclusion(_GLFWwindow* window, bool occluded);\nvoid _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos);\nvoid _glfwInputWindowSize(_GLFWwindow* window, int width, int height);\nvoid _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height);\nvoid _glfwInputWindowContentScale(_GLFWwindow* window,\n                                  float xscale, float yscale);\nvoid _glfwInputWindowIconify(_GLFWwindow* window, bool iconified);\nvoid _glfwInputWindowMaximize(_GLFWwindow* window, bool maximized);\nvoid _glfwInputWindowDamage(_GLFWwindow* window);\nvoid _glfwInputWindowCloseRequest(_GLFWwindow* window);\nvoid _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);\n\nvoid _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev);\nvoid _glfwInputClipboardLost(GLFWClipboardType which);\nvoid _glfwInputScroll(_GLFWwindow* window, const GLFWScrollEvent *ev);\nvoid _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);\nvoid _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);\nvoid _glfwInputCursorEnter(_GLFWwindow* window, bool entered);\n// Platform functions for drop data reading\nvoid _glfwPlatformRequestDropUpdate(_GLFWwindow* window);\nsize_t _glfwInputDropEvent(_GLFWwindow *window, GLFWDropEventType type, double xpos, double ypos, const char** mimes, size_t num_mimes, bool from_self);\nssize_t _glfwPlatformReadAvailableDropData(GLFWwindow *w, GLFWDropEvent *ev, char *buffer, size_t sz);\nvoid _glfwPlatformEndDrop(GLFWwindow *w, GLFWDragOperationType op);\nint _glfwPlatformRequestDropData(_GLFWwindow *window, const char *mime);\n// Platform functions for drag source\nint _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail);\nvoid _glfwFreeDragSourceData(void);\nvoid _glfwPlatformFreeDragSourceData(void);\nvoid _glfwInputDragSourceRequest(_GLFWwindow* window, GLFWDragEvent *ev);\nint _glfwPlatformDragDataReady(const char *mime_type);\nint _glfwPlatformChangeDragImage(const GLFWimage *thumbnail);\n\n\nvoid _glfwInputColorScheme(GLFWColorScheme, bool);\nvoid _glfwPlatformInputColorScheme(GLFWColorScheme);\nvoid _glfwInputJoystick(_GLFWjoystick* js, int event);\nvoid _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value);\nvoid _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value);\nvoid _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value);\n\nvoid _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement);\nvoid _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window);\n\n#if defined(__GNUC__) || defined(__clang__)\nvoid _glfwInputError(int code, const char* format, ...)\n    __attribute__((format(printf, 2, 3)));\nvoid _glfwDebug(const char* format, ...)\n    __attribute__((format(printf, 1, 2)));\n#else\nvoid _glfwInputError(int code, const char* format, ...);\nvoid _glfwDebug(const char* format, ...);\n#endif\n\n#ifdef DEBUG_EVENT_LOOP\n#define EVDBG(...) _glfwDebug(__VA_ARGS__)\n#else\n#define EVDBG(...)\n#endif\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nbool _glfwStringInExtensionString(const char* string, const char* extensions);\nbool _glfwRefreshContextAttribs(_GLFWwindow* window,\n                                    const _GLFWctxconfig* ctxconfig);\nbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig);\n\nconst GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,\n                                        const GLFWvidmode* desired);\nint _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second);\n_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM);\nvoid _glfwFreeMonitor(_GLFWmonitor* monitor);\nvoid _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size);\nvoid _glfwFreeGammaArrays(GLFWgammaramp* ramp);\nvoid _glfwSplitBPP(int bpp, int* red, int* green, int* blue);\n\n_GLFWjoystick* _glfwAllocJoystick(const char* name,\n                                  const char* guid,\n                                  int axisCount,\n                                  int buttonCount,\n                                  int hatCount);\nvoid _glfwFreeJoystick(_GLFWjoystick* js);\nconst char* _glfwGetKeyName(int key);\nvoid _glfwCenterCursorInContentArea(_GLFWwindow* window);\n\nbool _glfwInitVulkan(int mode);\nvoid _glfwTerminateVulkan(void);\nconst char* _glfwGetVulkanResultString(VkResult result);\n_GLFWwindow* _glfwFocusedWindow(void);\n_GLFWwindow* _glfwWindowForId(GLFWid id);\nvoid _glfwPlatformRunMainLoop(GLFWtickcallback, void*);\nvoid _glfwPlatformStopMainLoop(void);\nunsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback);\nvoid _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled);\nvoid _glfwPlatformRemoveTimer(unsigned long long timer_id);\nint _glfwPlatformSetWindowBlur(_GLFWwindow* handle, int value);\nMonitorGeometry _glfwPlatformGetMonitorGeometry(_GLFWmonitor* monitor);\nbool _glfwPlatformGrabKeyboard(bool grab);\nvoid glfw_handle_scroll_event_for_momentum(_GLFWwindow *w, const GLFWScrollEvent *ev, bool stopped, bool is_finger_based);\n#define glfw_cancel_momentum_scroll() glfw_handle_scroll_event_for_momentum(NULL, NULL, false, false)\n#ifdef _GLFW_X11\n#define momentum_scroll_gesture_detection_timeout_ms 50\n#else\n#define momentum_scroll_gesture_detection_timeout_ms 0\n#endif\n\nchar* _glfw_strdup(const char* source);\n\nvoid _glfw_free_clipboard_data(_GLFWClipboardData *cd);\n\n#define debug_rendering(...) if (_glfw.hints.init.debugRendering) { timed_debug_print(__VA_ARGS__); }\n#define debug_input(...) if (_glfw.hints.init.debugKeyboard) { timed_debug_print(__VA_ARGS__); }\n#define safe_close(fd) do { errno = 0; close(fd); } while(errno == EINTR)\n"
  },
  {
    "path": "glfw/kwin-blur-v1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<protocol name=\"blur\">\n  <copyright><![CDATA[\n    SPDX-FileCopyrightText: 2015 Martin Gräßlin\n    SPDX-FileCopyrightText: 2015 Marco Martin\n\n    SPDX-License-Identifier: LGPL-2.1-or-later\n  ]]></copyright>\n  <interface name=\"org_kde_kwin_blur_manager\" version=\"1\">\n      <request name=\"create\">\n          <arg name=\"id\" type=\"new_id\" interface=\"org_kde_kwin_blur\"/>\n          <arg name=\"surface\" type=\"object\" interface=\"wl_surface\"/>\n      </request>\n      <request name=\"unset\">\n          <arg name=\"surface\" type=\"object\" interface=\"wl_surface\"/>\n      </request>\n  </interface>\n  <interface name=\"org_kde_kwin_blur\" version=\"1\">\n      <request name=\"commit\">\n      </request>\n      <request name=\"set_region\">\n        <arg name=\"region\" type=\"object\" interface=\"wl_region\" allow-null=\"true\"/>\n      </request>\n      <request name=\"release\" type=\"destructor\">\n        <description summary=\"release the blur object\"/>\n      </request>\n  </interface>\n</protocol>\n"
  },
  {
    "path": "glfw/linux_desktop_settings.c",
    "content": "/*\n * linux_cursor_settings.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"linux_desktop_settings.h\"\n#include <stdlib.h>\n#include <strings.h>\n#include <string.h>\n\n#define DESKTOP_SERVICE \"org.freedesktop.portal.Desktop\"\n#define DESKTOP_PATH \"/org/freedesktop/portal/desktop\"\n#define DESKTOP_INTERFACE \"org.freedesktop.portal.Settings\"\n#define GNOME_DESKTOP_NAMESPACE \"org.gnome.desktop.interface\"\n#define FDO_DESKTOP_NAMESPACE \"org.freedesktop.appearance\"\nstatic const char* supported_namespaces[2] = {FDO_DESKTOP_NAMESPACE, GNOME_DESKTOP_NAMESPACE};\n#define FDO_APPEARANCE_KEY \"color-scheme\"\n\n\nstatic char theme_name[128] = {0};\nstatic int theme_size = -1;\nstatic GLFWColorScheme appearance = GLFW_COLOR_SCHEME_NO_PREFERENCE;\nstatic bool cursor_theme_changed = false, appearance_initialized = false;\n\n#define HANDLER(name_) static void name_(DBusMessage *msg, const DBusError* err, void *data) { \\\n    (void)data; \\\n    if (err) { \\\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"%s: failed with error: %s: %s\", #name_, err->name, err->message); \\\n        return; \\\n    }\n\nHANDLER(get_color_scheme_legacy)\n    DBusMessageIter iter, variant_iter, variant_iter2;\n    if (!dbus_message_iter_init(msg, &iter)) return;\n    dbus_message_iter_recurse(&iter, &variant_iter);\n    int type = dbus_message_iter_get_arg_type(&variant_iter);\n    if (type != DBUS_TYPE_VARIANT) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Read for color-scheme did not return a variant\"); return;\n    }\n    dbus_message_iter_recurse(&variant_iter, &variant_iter2);\n    if (type != DBUS_TYPE_VARIANT) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Read for color-scheme did not return a nested variant\"); return;\n    }\n    uint32_t val;\n    dbus_message_iter_get_basic(&variant_iter2, &val);\n    if (val < 3) appearance = val;\n}\n\nstatic void\nget_color_scheme(DBusMessage *msg, const DBusError* err, void *data) {\n    (void) data;\n    if (err) {\n        if (strcmp(\"org.freedesktop.DBus.Error.UnknownMethod\", err->name) == 0) {\n            DBusConnection *session_bus = glfw_dbus_session_bus();\n            if (session_bus) {\n                const char *namespace = FDO_DESKTOP_NAMESPACE, *key = FDO_APPEARANCE_KEY;\n                glfw_dbus_call_blocking_method(session_bus, DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, \"Read\", DBUS_TIMEOUT_USE_DEFAULT,\n                    get_color_scheme_legacy, NULL, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID);\n            }\n            return;\n        } else {\n            _glfwInputError(GLFW_PLATFORM_ERROR, \"%s: failed with error: %s: %s\", \"get_color_scheme\", err->name, err->message);\n            return;\n        }\n    }\n    uint32_t val;\n    DBusMessageIter iter, variant_iter;\n    if (!dbus_message_iter_init(msg, &iter)) return;\n    dbus_message_iter_recurse(&iter, &variant_iter);\n    int type = dbus_message_iter_get_arg_type(&variant_iter);\n    if (type != DBUS_TYPE_UINT32) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"ReadOne for color-scheme did not return a uint32\"); return;\n    }\n    dbus_message_iter_get_basic(&variant_iter, &val);\n    if (val < 3) appearance = val;\n}\n\nGLFWColorScheme\nglfw_current_system_color_theme(bool query_if_unintialized) {\n    if (!appearance_initialized && query_if_unintialized) {\n        appearance_initialized = true;\n        DBusConnection *session_bus = glfw_dbus_session_bus();\n        if (session_bus) {\n            const char *namespace = FDO_DESKTOP_NAMESPACE, *key = FDO_APPEARANCE_KEY;\n            glfw_dbus_call_blocking_method(session_bus, DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, \"ReadOne\", DBUS_TIMEOUT_USE_DEFAULT,\n                get_color_scheme, NULL, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID);\n        }\n    }\n    return appearance;\n}\n\nstatic void\nprocess_fdo_setting(const char *key, DBusMessageIter *value) {\n    if (strcmp(key, FDO_APPEARANCE_KEY) == 0) {\n        if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_UINT32) {\n            uint32_t val;\n            dbus_message_iter_get_basic(value, &val);\n            if (val > 2) val = 0;\n            if (!appearance_initialized) {\n                appearance_initialized = true;\n                if (val != appearance) {\n                    appearance = val;\n                    _glfwInputColorScheme(appearance, true);\n                }\n            }\n        }\n    }\n}\n\nstatic void\nprocess_gnome_setting(const char *key, DBusMessageIter *value) {\n    if (strcmp(key, \"cursor-size\") == 0) {\n        if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_INT32) {\n            int32_t sz;\n            dbus_message_iter_get_basic(value, &sz);\n            if (sz > 0 && sz != theme_size) {\n                theme_size = sz;\n                cursor_theme_changed = true;\n            }\n        }\n    } else if (strcmp(key, \"cursor-theme\") == 0) {\n        if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_STRING) {\n            const char *name;\n            dbus_message_iter_get_basic(value, &name);\n            if (name) {\n                strncpy(theme_name, name, sizeof(theme_name) - 1);\n                cursor_theme_changed = true;\n            }\n        }\n    }\n}\n\nstatic void\nprocess_settings_dict(DBusMessageIter *array_iter, void(process_setting)(const char *, DBusMessageIter*)) {\n    DBusMessageIter item_iter, value_iter;\n    while (dbus_message_iter_get_arg_type(array_iter) == DBUS_TYPE_DICT_ENTRY) {\n        dbus_message_iter_recurse(array_iter, &item_iter);\n        if (dbus_message_iter_get_arg_type(&item_iter) == DBUS_TYPE_STRING) {\n            const char *key;\n            dbus_message_iter_get_basic(&item_iter, &key);\n            if (dbus_message_iter_next(&item_iter) && dbus_message_iter_get_arg_type(&item_iter) == DBUS_TYPE_VARIANT) {\n                dbus_message_iter_recurse(&item_iter, &value_iter);\n                process_setting(key, &value_iter);\n            }\n        }\n        if (!dbus_message_iter_next(array_iter)) break;\n    }\n}\n\nHANDLER(process_desktop_settings)\n    cursor_theme_changed = false;\n    DBusMessageIter root, array, item, settings;\n    dbus_message_iter_init(msg, &root);\n#define die(...) { _glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); return; }\n    if (dbus_message_iter_get_arg_type(&root) != DBUS_TYPE_ARRAY) die(\"Reply to request for desktop settings is not an array\");\n    dbus_message_iter_recurse(&root, &array);\n    while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {\n        dbus_message_iter_recurse(&array, &item);\n        if (dbus_message_iter_get_arg_type(&item) == DBUS_TYPE_STRING) {\n            const char *namespace;\n            dbus_message_iter_get_basic(&item, &namespace);\n            if (dbus_message_iter_next(&item) && dbus_message_iter_get_arg_type(&item) == DBUS_TYPE_ARRAY) {\n                dbus_message_iter_recurse(&item, &settings);\n                if (strcmp(namespace, FDO_DESKTOP_NAMESPACE) == 0) {\n                    process_settings_dict(&settings, process_fdo_setting);\n                } else if (strcmp(namespace, GNOME_DESKTOP_NAMESPACE) == 0) {\n                    process_settings_dict(&settings, process_gnome_setting);\n                }\n            }\n        }\n        if (!dbus_message_iter_next(&array)) break;\n    }\n#undef die\n#ifndef _GLFW_X11\n    if (cursor_theme_changed) _glfwPlatformChangeCursorTheme();\n#endif\n}\n\n#undef HANDLER\n\nstatic bool\nread_desktop_settings(DBusConnection *session_bus) {\n    RAII_MSG(msg, dbus_message_new_method_call(DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, \"ReadAll\"));\n    if (!msg) return false;\n    DBusMessageIter iter, array_iter;\n    dbus_message_iter_init_append(msg, &iter);\n    if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, \"s\", &array_iter)) { return false; }\n    for (unsigned i = 0; i < arraysz(supported_namespaces); ++i) {\n        if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &supported_namespaces[i])) return false;\n    }\n    if (!dbus_message_iter_close_container(&iter, &array_iter)) { return false; }\n    return call_method_with_msg(session_bus, msg, DBUS_TIMEOUT_USE_DEFAULT, process_desktop_settings, NULL, false);\n}\n\nvoid\nglfw_current_cursor_theme(const char **theme, int *size) {\n    *theme = theme_name[0] ? theme_name : NULL;\n    *size =  (theme_size > 0 && theme_size < 2048) ? theme_size : 32;\n}\n\nstatic void\nget_cursor_theme_from_env(void) {\n    const char *q = getenv(\"XCURSOR_THEME\");\n    if (q) strncpy(theme_name, q, sizeof(theme_name)-1);\n    const char *env = getenv(\"XCURSOR_SIZE\");\n    theme_size = 32;\n    if (env) {\n        const int retval = atoi(env);\n        if (retval > 0 && retval < 2048) theme_size = retval;\n    }\n}\n\nstatic void\non_color_scheme_change(DBusMessage *message) {\n    DBusMessageIter iter[2];\n    dbus_message_iter_init (message, &iter[0]);\n    int current_type;\n    while ((current_type = dbus_message_iter_get_arg_type (&iter[0])) != DBUS_TYPE_INVALID) {\n        if (current_type == DBUS_TYPE_VARIANT) {\n            dbus_message_iter_recurse(&iter[0], &iter[1]);\n            if (dbus_message_iter_get_arg_type(&iter[1]) == DBUS_TYPE_UINT32) {\n                uint32_t val = 0;\n                dbus_message_iter_get_basic(&iter[1], &val);\n                if (val > 2) val = 0;\n                if (val != appearance) {\n                    appearance = val;\n                    appearance_initialized = true;\n                    _glfwInputColorScheme(appearance, false);\n                }\n            }\n            break;\n        }\n        dbus_message_iter_next(&iter[0]);\n    }\n}\n\nstatic DBusHandlerResult\nsetting_changed(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data UNUSED) {\n    /* printf(\"session_bus settings_changed invoked interface: %s member: %s\\n\", dbus_message_get_interface(msg), dbus_message_get_member(msg)); */\n    if (dbus_message_is_signal(msg, DESKTOP_INTERFACE, \"SettingChanged\")) {\n        const char *namespace = NULL, *key = NULL;\n        if (glfw_dbus_get_args(msg, \"Failed to get namespace and key from SettingChanged notification signal\", DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {\n            if (strcmp(namespace, FDO_DESKTOP_NAMESPACE) == 0) {\n                if (strcmp(key, FDO_APPEARANCE_KEY) == 0) {\n                    on_color_scheme_change(msg);\n                }\n            }\n        }\n\n    }\n    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n}\n\n\nvoid\nglfw_initialize_desktop_settings(void) {\n    get_cursor_theme_from_env();\n    DBusConnection *session_bus = glfw_dbus_session_bus();\n    if (session_bus) {\n        if (!read_desktop_settings(session_bus)) _glfwInputError(GLFW_PLATFORM_ERROR, \"WARNING: Failed to read desktop settings, using defaults, make sure you have the desktop portal running.\");\n        dbus_bus_add_match(session_bus, \"type='signal',interface='\" DESKTOP_INTERFACE \"',member='SettingChanged'\", NULL);\n        dbus_connection_add_filter(session_bus, setting_changed, NULL, NULL);\n    }\n}\n"
  },
  {
    "path": "glfw/linux_desktop_settings.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"dbus_glfw.h\"\n#include \"internal.h\"\n\n\nvoid glfw_initialize_desktop_settings(void);\nvoid glfw_current_cursor_theme(const char **theme, int *size);\nGLFWColorScheme glfw_current_system_color_theme(bool);\n"
  },
  {
    "path": "glfw/linux_joystick.c",
    "content": "//========================================================================\n// GLFW 3.4 Linux - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#define _POSIX_C_SOURCE 200809L\n\n#include \"internal.h\"\n\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/inotify.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n\n#ifndef SYN_DROPPED // < v2.6.39 kernel headers\n// Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32\n#define SYN_DROPPED 3\n#endif\n\n// Apply an EV_KEY event to the specified joystick\n//\nstatic void handleKeyEvent(_GLFWjoystick* js, int code, int value)\n{\n    _glfwInputJoystickButton(js,\n                             js->linjs.keyMap[code - BTN_MISC],\n                             value ? GLFW_PRESS : GLFW_RELEASE);\n}\n\n// Apply an EV_ABS event to the specified joystick\n//\nstatic void handleAbsEvent(_GLFWjoystick* js, int code, int value)\n{\n    const int index = js->linjs.absMap[code];\n\n    if (code >= ABS_HAT0X && code <= ABS_HAT3Y)\n    {\n        static const char stateMap[3][3] =\n        {\n            { GLFW_HAT_CENTERED, GLFW_HAT_UP,       GLFW_HAT_DOWN },\n            { GLFW_HAT_LEFT,     GLFW_HAT_LEFT_UP,  GLFW_HAT_LEFT_DOWN },\n            { GLFW_HAT_RIGHT,    GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN },\n        };\n\n        const int hat = (code - ABS_HAT0X) / 2;\n        const int axis = (code - ABS_HAT0X) % 2;\n        int* state = js->linjs.hats[hat];\n\n        // NOTE: Looking at several input drivers, it seems all hat events use\n        //       -1 for left / up, 0 for centered and 1 for right / down\n        if (value == 0)\n            state[axis] = 0;\n        else if (value < 0)\n            state[axis] = 1;\n        else if (value > 0)\n            state[axis] = 2;\n\n        _glfwInputJoystickHat(js, index, stateMap[state[0]][state[1]]);\n    }\n    else\n    {\n        const struct input_absinfo* info = &js->linjs.absInfo[code];\n        float normalized = value;\n\n        const int range = info->maximum - info->minimum;\n        if (range)\n        {\n            // Normalize to 0.0 -> 1.0\n            normalized = (normalized - info->minimum) / range;\n            // Normalize to -1.0 -> 1.0\n            normalized = normalized * 2.0f - 1.0f;\n        }\n\n        _glfwInputJoystickAxis(js, index, normalized);\n    }\n}\n\n// Poll state of absolute axes\n//\nstatic void pollAbsState(_GLFWjoystick* js)\n{\n    for (int code = 0;  code < ABS_CNT;  code++)\n    {\n        if (js->linjs.absMap[code] < 0)\n            continue;\n\n        struct input_absinfo* info = &js->linjs.absInfo[code];\n\n        if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0)\n            continue;\n\n        handleAbsEvent(js, code, info->value);\n    }\n}\n\n#define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8)))\n\n// Attempt to open the specified joystick device\n//\nstatic bool openJoystickDevice(const char* path)\n{\n    for (int jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)\n    {\n        if (!_glfw.joysticks[jid].present)\n            continue;\n        if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)\n            return false;\n    }\n\n    _GLFWjoystickLinux linjs = {0};\n    linjs.fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);\n    if (linjs.fd == -1)\n        return false;\n\n    char evBits[(EV_CNT + 7) / 8] = {0};\n    char keyBits[(KEY_CNT + 7) / 8] = {0};\n    char absBits[(ABS_CNT + 7) / 8] = {0};\n    struct input_id id;\n\n    if (ioctl(linjs.fd, (int32_t)EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 ||\n        ioctl(linjs.fd, (int32_t)EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||\n        ioctl(linjs.fd, (int32_t)EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 ||\n        ioctl(linjs.fd, (int32_t)EVIOCGID, &id) < 0)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Linux: Failed to query input device: %s\",\n                        strerror(errno));\n        close(linjs.fd);\n        return false;\n    }\n\n    // Ensure this device supports the events expected of a joystick\n    if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits))\n    {\n        close(linjs.fd);\n        return false;\n    }\n\n    char name[256] = \"\";\n\n    if (ioctl(linjs.fd, (int32_t)EVIOCGNAME(sizeof(name)), name) < 0)\n        strncpy(name, \"Unknown\", sizeof(name));\n\n    char guid[33] = \"\";\n\n    // Generate a joystick GUID that matches the SDL 2.0.5+ one\n    if (id.vendor && id.product && id.version)\n    {\n        sprintf(guid, \"%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000\",\n                id.bustype & 0xff, id.bustype >> 8,\n                id.vendor & 0xff,  id.vendor >> 8,\n                id.product & 0xff, id.product >> 8,\n                id.version & 0xff, id.version >> 8);\n    }\n    else\n    {\n        sprintf(guid, \"%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00\",\n                id.bustype & 0xff, id.bustype >> 8,\n                name[0], name[1], name[2], name[3],\n                name[4], name[5], name[6], name[7],\n                name[8], name[9], name[10]);\n    }\n\n    int axisCount = 0, buttonCount = 0, hatCount = 0;\n\n    for (int code = BTN_MISC;  code < KEY_CNT;  code++)\n    {\n        if (!isBitSet(code, keyBits))\n            continue;\n\n        linjs.keyMap[code - BTN_MISC] = buttonCount;\n        buttonCount++;\n    }\n\n    for (int code = 0;  code < ABS_CNT;  code++)\n    {\n        linjs.absMap[code] = -1;\n        if (!isBitSet(code, absBits))\n            continue;\n\n        if (code >= ABS_HAT0X && code <= ABS_HAT3Y)\n        {\n            linjs.absMap[code] = hatCount;\n            hatCount++;\n            // Skip the Y axis\n            code++;\n        }\n        else\n        {\n            if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0)\n                continue;\n\n            linjs.absMap[code] = axisCount;\n            axisCount++;\n        }\n    }\n\n    _GLFWjoystick* js =\n        _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);\n    if (!js)\n    {\n        close(linjs.fd);\n        return false;\n    }\n\n    strncpy(linjs.path, path, sizeof(linjs.path) - 1);\n    memcpy(&js->linjs, &linjs, sizeof(linjs));\n\n    pollAbsState(js);\n\n    _glfwInputJoystick(js, GLFW_CONNECTED);\n    return true;\n}\n\n#undef isBitSet\n\n// Frees all resources associated with the specified joystick\n//\nstatic void closeJoystick(_GLFWjoystick* js)\n{\n    close(js->linjs.fd);\n    _glfwFreeJoystick(js);\n    _glfwInputJoystick(js, GLFW_DISCONNECTED);\n}\n\n// Lexically compare joysticks by name; used by qsort\n//\nstatic int compareJoysticks(const void* fp, const void* sp)\n{\n    const _GLFWjoystick* fj = fp;\n    const _GLFWjoystick* sj = sp;\n    return strcmp(fj->linjs.path, sj->linjs.path);\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid _glfwDetectJoystickConnectionLinux(void)\n{\n    if (_glfw.linjs.inotify <= 0)\n        return;\n\n    ssize_t offset = 0;\n    char buffer[16384];\n    const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));\n\n    while (size > offset)\n    {\n        regmatch_t match;\n        const struct inotify_event* e = (struct inotify_event*) (buffer + offset);\n\n        offset += sizeof(struct inotify_event) + e->len;\n\n        if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0)\n            continue;\n\n        char path[PATH_MAX];\n        snprintf(path, sizeof(path), \"/dev/input/%s\", e->name);\n\n        if (e->mask & (IN_CREATE | IN_ATTRIB))\n            openJoystickDevice(path);\n        else if (e->mask & IN_DELETE)\n        {\n            for (int jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)\n            {\n                if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)\n                {\n                    closeJoystick(_glfw.joysticks + jid);\n                    break;\n                }\n            }\n        }\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nbool _glfwPlatformInitJoysticks(void)\n{\n    const char* dirname = \"/dev/input\";\n\n    _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);\n    if (_glfw.linjs.inotify > 0)\n    {\n        // HACK: Register for IN_ATTRIB to get notified when udev is done\n        //       This works well in practice but the true way is libudev\n\n        _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify,\n                                              dirname,\n                                              IN_CREATE | IN_ATTRIB | IN_DELETE);\n    }\n\n    // Continue without device connection notifications if inotify fails\n\n    if (regcomp(&_glfw.linjs.regex, \"^event[0-9]\\\\+$\", 0) != 0)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Linux: Failed to compile regex\");\n        return false;\n    }\n\n    int count = 0;\n\n    DIR* dir = opendir(dirname);\n    if (dir)\n    {\n        struct dirent* entry;\n\n        while ((entry = readdir(dir)))\n        {\n            regmatch_t match;\n\n            if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0)\n                continue;\n\n            char path[PATH_MAX];\n\n            snprintf(path, sizeof(path), \"%s/%s\", dirname, entry->d_name);\n\n            if (openJoystickDevice(path))\n                count++;\n        }\n\n        closedir(dir);\n    }\n\n    // Continue with no joysticks if enumeration fails\n\n    qsort(_glfw.joysticks, count, sizeof(_glfw.joysticks[0]), compareJoysticks);\n    return true;\n}\n\nvoid _glfwPlatformTerminateJoysticks(void)\n{\n    int jid;\n\n    for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)\n    {\n        _GLFWjoystick* js = _glfw.joysticks + jid;\n        if (js->present)\n            closeJoystick(js);\n    }\n\n    if (_glfw.linjs.inotify > 0)\n    {\n        if (_glfw.linjs.watch > 0)\n            inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch);\n\n        close(_glfw.linjs.inotify);\n        regfree(&_glfw.linjs.regex);\n    }\n}\n\nint _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode UNUSED)\n{\n    // Read all queued events (non-blocking)\n    for (;;)\n    {\n        struct input_event e;\n\n        errno = 0;\n        if (read(js->linjs.fd, &e, sizeof(e)) < 0)\n        {\n            // Reset the joystick slot if the device was disconnected\n            if (errno == ENODEV)\n                closeJoystick(js);\n\n            break;\n        }\n\n        if (e.type == EV_SYN)\n        {\n            if (e.code == SYN_DROPPED)\n                _glfw.linjs.dropped = true;\n            else if (e.code == SYN_REPORT)\n            {\n                _glfw.linjs.dropped = false;\n                pollAbsState(js);\n            }\n        }\n\n        if (_glfw.linjs.dropped)\n            continue;\n\n        if (e.type == EV_KEY)\n            handleKeyEvent(js, e.code, e.value);\n        else if (e.type == EV_ABS)\n            handleAbsEvent(js, e.code, e.value);\n    }\n\n    return js->present;\n}\n\nvoid _glfwPlatformUpdateGamepadGUID(char* guid UNUSED)\n{\n}\n"
  },
  {
    "path": "glfw/linux_joystick.h",
    "content": "//========================================================================\n// GLFW 3.4 Linux - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#include <linux/input.h>\n#include <linux/limits.h>\n#include <regex.h>\n\n#define _GLFW_PLATFORM_JOYSTICK_STATE         _GLFWjoystickLinux linjs\n#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux  linjs;\n\n#define _GLFW_PLATFORM_MAPPING_NAME \"Linux\"\n\n// Linux-specific joystick data\n//\ntypedef struct _GLFWjoystickLinux\n{\n    int                     fd;\n    char                    path[PATH_MAX];\n    int                     keyMap[KEY_CNT - BTN_MISC];\n    int                     absMap[ABS_CNT];\n    struct input_absinfo    absInfo[ABS_CNT];\n    int                     hats[4][2];\n} _GLFWjoystickLinux;\n\n// Linux-specific joystick API data\n//\ntypedef struct _GLFWlibraryLinux\n{\n    int                     inotify;\n    int                     watch;\n    regex_t                 regex;\n    bool                    dropped;\n} _GLFWlibraryLinux;\n\nvoid _glfwDetectJoystickConnectionLinux(void);\n"
  },
  {
    "path": "glfw/linux_notify.c",
    "content": "/*\n * linux_notify.c\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define _POSIX_C_SOURCE 200809L\n#include \"internal.h\"\n#include \"linux_notify.h\"\n#include <stdlib.h>\n#include <string.h>\n\n#define NOTIFICATIONS_SERVICE  \"org.freedesktop.Notifications\"\n#define NOTIFICATIONS_PATH \"/org/freedesktop/Notifications\"\n#define NOTIFICATIONS_IFACE \"org.freedesktop.Notifications\"\n\nstatic inline void cleanup_free(void *p) { free(*(void**)p); }\n#define RAII_ALLOC(type, name, initializer) __attribute__((cleanup(cleanup_free))) type *name = initializer\n\ntypedef struct {\n    notification_id_type next_id;\n    GLFWDBusnotificationcreatedfun callback;\n    void *data;\n} NotificationCreatedData;\n\nstatic GLFWDBusnotificationactivatedfun activated_handler = NULL;\n\nvoid\nglfw_dbus_set_user_notification_activated_handler(GLFWDBusnotificationactivatedfun handler) {\n    activated_handler = handler;\n}\n\nvoid\nnotification_created(DBusMessage *msg, const DBusError* err, void *data) {\n    if (err) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Notify: Failed to create notification error: %s: %s\", err->name, err->message);\n        if (data) free(data);\n        return;\n    }\n    uint32_t id;\n    if (!glfw_dbus_get_args(msg, \"Failed to get Notification uid\", DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID)) return;\n    NotificationCreatedData *ncd = (NotificationCreatedData*)data;\n    if (ncd) {\n        if (ncd->callback) ncd->callback(ncd->next_id, id, ncd->data);\n        free(ncd);\n    }\n}\n\nstatic DBusHandlerResult\nmessage_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data UNUSED) {\n    /* printf(\"session_bus message_handler invoked interface: %s member: %s\\n\", dbus_message_get_interface(msg), dbus_message_get_member(msg)); */\n    if (dbus_message_is_signal(msg, NOTIFICATIONS_IFACE, \"ActionInvoked\")) {\n        uint32_t id;\n        const char *action = NULL;\n        if (glfw_dbus_get_args(msg, \"Failed to get args from ActionInvoked notification signal\",\n                    DBUS_TYPE_UINT32, &id, DBUS_TYPE_STRING, &action, DBUS_TYPE_INVALID)) {\n            if (activated_handler) {\n                activated_handler(id, 2, action);\n                return DBUS_HANDLER_RESULT_HANDLED;\n            }\n        }\n\n    }\n\n    if (dbus_message_is_signal(msg, NOTIFICATIONS_IFACE, \"ActivationToken\")) {\n        uint32_t id;\n        const char *token = NULL;\n        if (glfw_dbus_get_args(msg, \"Failed to get args from ActivationToken notification signal\",\n                    DBUS_TYPE_UINT32, &id, DBUS_TYPE_STRING, &token, DBUS_TYPE_INVALID)) {\n            if (activated_handler) {\n                activated_handler(id, 1, token);\n                return DBUS_HANDLER_RESULT_HANDLED;\n            }\n        }\n\n    }\n\n    if (dbus_message_is_signal(msg, NOTIFICATIONS_IFACE, \"NotificationClosed\")) {\n        uint32_t id;\n        if (glfw_dbus_get_args(msg, \"Failed to get args from NotificationClosed notification signal\",\n                    DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID)) {\n            if (activated_handler) {\n                activated_handler(id, 0, \"\");\n                return DBUS_HANDLER_RESULT_HANDLED;\n            }\n        }\n    }\n    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n}\n\nstatic bool\ncancel_user_notification(DBusConnection *session_bus, uint32_t *id) {\n    return glfw_dbus_call_method_no_reply(session_bus, NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, \"CloseNotification\", DBUS_TYPE_UINT32, id, DBUS_TYPE_INVALID);\n}\n\nstatic void\ngot_capabilities(DBusMessage *msg, const DBusError* err, void* data UNUSED) {\n    if (err) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Notify: Failed to get server capabilities error: %s: %s\", err->name, err->message);\n        return;\n    }\n#define check_call(func, err, ...) if (!func(__VA_ARGS__)) { _glfwInputError(GLFW_PLATFORM_ERROR, \"Notify: GetCapabilities: %s\", err); return;  }\n    DBusMessageIter iter, array_iter;\n    check_call(dbus_message_iter_init, \"message has no parameters\", msg, &iter);\n    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Notify: GetCapabilities: %s\", \"reply is not an array of strings\");\n        return;\n    }\n    dbus_message_iter_recurse(&iter, &array_iter);\n    char buf[2048] = {0}, *p = buf, *end = buf + sizeof(buf);\n    while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRING) {\n        const char *str;\n        dbus_message_iter_get_basic(&array_iter, &str);\n        size_t len = strlen(str);\n        if (len && p + len + 2 < end) { p = stpcpy(p, str); *(p++) = '\\n'; }\n        dbus_message_iter_next(&array_iter);\n    }\n    if (activated_handler) activated_handler(0, -1, buf);\n#undef check_call\n\n}\n\nstatic bool\nget_capabilities(DBusConnection *session_bus) {\n    return glfw_dbus_call_method_with_reply(session_bus, NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, \"GetCapabilities\", 60, got_capabilities, NULL, DBUS_TYPE_INVALID);\n}\n\nnotification_id_type\nglfw_dbus_send_user_notification(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *user_data) {\n    DBusConnection *session_bus = glfw_dbus_session_bus();\n    if (!session_bus) return 0;\n    if (n->timeout == -9999 && n->urgency == 255) return cancel_user_notification(session_bus, user_data) ? 1 : 0;\n    if (n->timeout == -99999 && n->urgency == 255) return get_capabilities(session_bus) ? 1 : 0;\n    static DBusConnection *added_signal_match = NULL;\n    if (added_signal_match != session_bus) {\n        dbus_bus_add_match(session_bus, \"type='signal',interface='\" NOTIFICATIONS_IFACE \"',member='ActionInvoked'\", NULL);\n        dbus_bus_add_match(session_bus, \"type='signal',interface='\" NOTIFICATIONS_IFACE \"',member='NotificationClosed'\", NULL);\n        dbus_bus_add_match(session_bus, \"type='signal',interface='\" NOTIFICATIONS_IFACE \"',member='ActivationToken'\", NULL);\n        dbus_connection_add_filter(session_bus, message_handler, NULL, NULL);\n        added_signal_match = session_bus;\n    }\n    RAII_ALLOC(NotificationCreatedData, data, malloc(sizeof(NotificationCreatedData)));\n    if (!data) return 0;\n    static notification_id_type notification_id = 0;\n    data->next_id = ++notification_id;\n    data->callback = callback; data->data = user_data;\n    if (!data->next_id) data->next_id = ++notification_id;\n\n    RAII_MSG(msg, dbus_message_new_method_call(NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, \"Notify\"));\n    if (!msg) { return 0; }\n    DBusMessageIter args, array, variant, dict;\n    dbus_message_iter_init_append(msg, &args);\n#define check_call(func, ...) if (!func(__VA_ARGS__)) { _glfwInputError(GLFW_PLATFORM_ERROR, \"%s\", \"Out of memory allocating DBUS message for notification\\n\"); return 0; }\n#define APPEND(to, type, val) check_call(dbus_message_iter_append_basic, &to, type, &val);\n    APPEND(args, DBUS_TYPE_STRING, n->app_name)\n    APPEND(args, DBUS_TYPE_UINT32, n->replaces)\n    APPEND(args, DBUS_TYPE_STRING, n->icon)\n    APPEND(args, DBUS_TYPE_STRING, n->summary)\n    APPEND(args, DBUS_TYPE_STRING, n->body)\n    check_call(dbus_message_iter_open_container, &args, DBUS_TYPE_ARRAY, \"s\", &array);\n    if (n->actions) {\n        for (size_t i = 0; i < n->num_actions; i++) {\n            APPEND(array, DBUS_TYPE_STRING, n->actions[i]);\n        }\n    }\n    check_call(dbus_message_iter_close_container, &args, &array);\n    check_call(dbus_message_iter_open_container, &args, DBUS_TYPE_ARRAY, \"{sv}\", &array);\n\n#define append_sv_dictionary_entry(k, val_type, val) { \\\n    check_call(dbus_message_iter_open_container, &array, DBUS_TYPE_DICT_ENTRY, NULL, &dict); \\\n    static const char *key = k; \\\n    APPEND(dict, DBUS_TYPE_STRING, key); \\\n    check_call(dbus_message_iter_open_container, &dict, DBUS_TYPE_VARIANT, val_type##_AS_STRING, &variant); \\\n    APPEND(variant, val_type, val); \\\n    check_call(dbus_message_iter_close_container, &dict, &variant); \\\n    check_call(dbus_message_iter_close_container, &array, &dict); \\\n}\n    append_sv_dictionary_entry(\"urgency\", DBUS_TYPE_BYTE, n->urgency);\n    if (n->category && n->category[0]) append_sv_dictionary_entry(\"category\", DBUS_TYPE_STRING, n->category);\n    if (n->muted) append_sv_dictionary_entry(\"suppress-sound\", DBUS_TYPE_BOOLEAN, n->muted);\n\n    check_call(dbus_message_iter_close_container, &args, &array);\n    APPEND(args, DBUS_TYPE_INT32, n->timeout)\n#undef check_call\n#undef APPEND\n    if (!call_method_with_msg(session_bus, msg, 5000, notification_created, data, false)) return 0;\n    notification_id_type ans = data->next_id;\n    data = NULL;\n    return ans;\n}\n"
  },
  {
    "path": "glfw/linux_notify.h",
    "content": "/*\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n\n#include \"dbus_glfw.h\"\n#include \"internal.h\"\n\ntypedef unsigned long long notification_id_type;\ntypedef void (*GLFWDBusnotificationcreatedfun)(notification_id_type, uint32_t, void*);\ntypedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, int, const char*);\nnotification_id_type\nglfw_dbus_send_user_notification(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun, void*);\nvoid\nglfw_dbus_set_user_notification_activated_handler(GLFWDBusnotificationactivatedfun handler);\n"
  },
  {
    "path": "glfw/main_loop.h",
    "content": "/*\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"internal.h\"\n#include \"../kitty/monotonic.h\"\n\n#ifndef GLFW_LOOP_BACKEND\n#define GLFW_LOOP_BACKEND x11\n#endif\n\nstatic bool keep_going = false;\n\n\nvoid _glfwPlatformStopMainLoop(void) {\n    if (keep_going) {\n        keep_going = false;\n        _glfwPlatformPostEmptyEvent();\n    }\n}\n\nvoid _glfwPlatformRunMainLoop(GLFWtickcallback tick_callback, void* data) {\n    keep_going = 1;\n    EventLoopData *eld = &_glfw.GLFW_LOOP_BACKEND.eventLoopData;\n    while(keep_going) {\n        _glfwPlatformWaitEvents();\n        EVDBG(\"--------- loop tick, wakeups_happened: %d ----------\", eld->wakeup_data_read);\n        if (eld->wakeup_data_read) {\n            eld->wakeup_data_read = false;\n            tick_callback(data);\n        }\n    }\n    EVDBG(\"main loop exiting\");\n}\n\nunsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback) {\n    return addTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, \"user timer\", interval, 1, repeats, callback, callback_data, free_callback);\n}\n\nvoid _glfwPlatformRemoveTimer(unsigned long long timer_id) {\n    removeTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id);\n}\n\nvoid _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {\n    changeTimerInterval(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id, interval);\n    toggleTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id, enabled);\n}\n"
  },
  {
    "path": "glfw/mappings.h",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// As mappings.h.in, this file is used by CMake to produce the mappings.h\n// header file.  If you are adding a GLFW specific gamepad mapping, this is\n// where to put it.\n//========================================================================\n// As mappings.h, this provides all pre-defined gamepad mappings, including\n// all available in SDL_GameControllerDB.  Do not edit this file.  Any gamepad\n// mappings not specific to GLFW should be submitted to SDL_GameControllerDB.\n// This file can be re-generated from mappings.h.in and the upstream\n// gamecontrollerdb.txt with the GenerateMappings.cmake script.\n//========================================================================\n\n// All gamepad mappings not labeled GLFW are copied from the\n// SDL_GameControllerDB project under the following license:\n//\n// Simple DirectMedia Layer\n// Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>\n//\n// This software is provided 'as-is', without any express or implied warranty.\n// In no event will the authors be held liable for any damages arising from the\n// use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source distribution.\n\nconst char* _glfwDefaultMappings[] =\n{\n\"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,\",\n\"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,\",\n\"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,\",\n\"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,\",\n\"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,\",\n\"030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,\",\n\"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,\",\n\"030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\",\n\"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,\",\n\"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\",\n\"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\",\n\"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,\",\n\"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\",\n\"030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\",\n\"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,\",\n\"03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,\",\n\"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\",\n\"03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\",\n\"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,\",\n\"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,\",\n\"03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\",\n\"030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,\",\n\"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,\",\n\"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\",\n\"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\",\n\"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,\",\n\"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,\",\n\"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,\",\n\"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,\",\n\"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\",\n\"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows,\",\n\"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,\",\n\"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,\",\n\"03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,\",\n\"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,\",\n\"03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,\",\n\"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,\",\n\"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,\",\n\"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,\",\n\"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,\",\n\"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\",\n\"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,\",\n\"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,\",\n\"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,\",\n\"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\",\n\"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,platform:Windows,\",\n\"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\",\n\"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,\",\n\"03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\",\n\"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,\",\n\"030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,\",\n\"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,\",\n\"030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,\",\n\"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,\",\n\"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,\",\n\"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,\",\n\"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,\",\n\"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,\",\n\"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,\",\n\"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,\",\n\"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,\",\n\"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,\",\n\"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,\",\n\"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,\",\n\"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,\",\n\"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X,\",\n\"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,\",\n\"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,\",\n\"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,\",\n\"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,\",\n\"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,\",\n\"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,\",\n\"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,\",\n\"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,\",\n\"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,\",\n\"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,\",\n\"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,\",\n\"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,\",\n\"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,\",\n\"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,\",\n\"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,\",\n\"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,\",\n\"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"030000005e0400008e02000001000000,Steam Virtual GamePad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,\",\n\"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,\",\n\"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,\",\n\"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,\",\n\"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,\",\n\"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,\",\n\"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,\",\n\"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,\",\n\"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,\",\n\"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,\",\n\"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,\",\n\"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,\",\n\"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,\",\n\"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,\",\n\"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\",\n\"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\",\n\"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\",\n\"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,\",\n\"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,\",\n\"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,\",\n\"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,\",\n\"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,\",\n\"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,\",\n\"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,\",\n\"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,\",\n\"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\",\n\"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,\",\n\"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,\",\n\"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,\",\n\"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\",\n\"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,\",\n\"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,\",\n\"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,\",\n\"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\",\n\"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,\",\n\"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\",\n\"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,\",\n\"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,\",\n\"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,\",\n\"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,\",\n\"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,\",\n\"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,\",\n\"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,\",\n\"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,\",\n\"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,platform:Linux,\",\n\"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,\",\n\"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\",\n\"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,\",\n\"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,\",\n\"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,\",\n\"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,\",\n\"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,\",\n\"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\",\n\"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,\",\n\"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\",\n\"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,\",\n\"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,\",\n\"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,\",\n\"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,\",\n\"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\",\n\"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,\",\n\"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\",\n\"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\",\n\"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\",\n\"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\",\n\"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\",\n\"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\",\n\"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\",\n\"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,\",\n\"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\",\n\"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\",\n\"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\",\n\"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\",\n\"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,\",\n\"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,\",\n\"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,\",\n\"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,\",\n\"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,\",\n\"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,\",\n\"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,\",\n\"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,\",\n\"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,\",\n\"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,\",\n\"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,\",\n\"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,\",\n\"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,\",\n\"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,\",\n\"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,\",\n\"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,\",\n\"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,\",\n\"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,\",\n\"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,\",\n\"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,\",\n\"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,\",\n\"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,\",\n\"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,\",\n\"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,\",\n\"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,\",\n\"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,\",\n\"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,\",\n\"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,\",\n\"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,\",\n\"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,\",\n\"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,\",\n\"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,\",\n\"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,\",\n\"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\",\n\"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\",\n\"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,\",\n\"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,\",\n\"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,\",\n\"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,\",\n\"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,\",\n\"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,\",\n\"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,\",\n\"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,\",\n\n\"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\",\n\"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\",\n\"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\",\n\"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\",\n\"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\",\n\"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\",\n\"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\",\nNULL\n};\n\n"
  },
  {
    "path": "glfw/memfd.h",
    "content": "/*\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#ifdef HAS_MEMFD_CREATE\n\n#include <unistd.h>\n#include <sys/syscall.h>\nstatic inline int glfw_memfd_create(const char *name, unsigned int flags) {\n    return (int)syscall(__NR_memfd_create, name, flags);\n}\n\n#ifndef F_LINUX_SPECIFIC_BASE\n#define F_LINUX_SPECIFIC_BASE 1024\n#endif\n\n#ifndef F_ADD_SEALS\n#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)\n#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)\n\n#define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */\n#define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */\n#define F_SEAL_GROW     0x0004  /* prevent file from growing */\n#define F_SEAL_WRITE    0x0008  /* prevent writes */\n#endif\n\n#ifndef MFD_CLOEXEC\n#define MFD_CLOEXEC     0x0001U\n#define MFD_ALLOW_SEALING 0x0002U\n#endif\n\n#else\n\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nstatic inline int createTmpfileCloexec(char* tmpname)\n{\n    int fd;\n\n    fd = mkostemp(tmpname, O_CLOEXEC);\n    if (fd >= 0)\n        unlink(tmpname);\n\n    return fd;\n}\n\n#endif\n"
  },
  {
    "path": "glfw/momentum-scroll.c",
    "content": "/*\n * momentum-scroll.c\n * Copyright (C) 2026 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"internal.h\"\n#include <math.h>\n\ntypedef struct ScrollSample {\n    double dx, dy;\n    monotonic_t timestamp;\n} ScrollSample;\n\n#define DEQUE_DATA_TYPE ScrollSample\n#define DEQUE_NAME ScrollSamples\n#include \"../kitty/fixed_size_deque.h\"\n\ntypedef enum ScrollerState { NONE, PHYSICAL_EVENT_IN_PROGRESS, MOMENTUM_IN_PROGRESS } ScrollerState;\n\ntypedef struct MomentumScroller {\n    double friction,  // Deceleration inverse factor (0-1, higher = longer coast)\n           min_velocity, // Minimum velocity before stopping\n           max_velocity, // Maximum velocity to prevent runaway scrolling\n           velocity_scale; // Scale factor for initial velocity\n    monotonic_t timer_interval;  // animation speed\n\n    GLFWid timer_id, window_id;\n    ScrollSamples samples;\n    ScrollerState state;\n    double scale;\n    struct { double x, y; } velocity;\n    int keyboard_modifiers;\n    struct {\n        monotonic_t start, duration;\n        struct { double x, y; } displacement;\n    } physical_event;\n} MomentumScroller;\n\n#define DEFAULTS { .friction = 0.96, .min_velocity = 0.5, .max_velocity = 100, .timer_interval = 10, }\nstatic const MomentumScroller defaults = DEFAULTS;\nstatic MomentumScroller s = DEFAULTS;\n#undef DEFAULTS\n\nGLFWAPI void\nglfwConfigureMomentumScroller(double friction, double min_velocity, double max_velocity, unsigned timer_interval_ms) {\n    s.timer_interval = timer_interval_ms ? ms_to_monotonic_t(timer_interval_ms) : defaults.timer_interval;\n    s.friction = friction < 0 ? defaults.friction : MAX(0, MIN(friction, 1));\n#define S(w) s.w = w >= 0 ? w : defaults.w\n    S(min_velocity); S(max_velocity);\n#undef S\n}\n\nstatic void\ncancel_existing_scroll(bool reset_velocity) {\n    if (s.timer_id) {\n        glfwRemoveTimer(s.timer_id);\n        s.timer_id = 0;\n    }\n    if (s.state == MOMENTUM_IN_PROGRESS) {\n        _GLFWwindow *w = _glfwWindowForId(s.window_id);\n        if (w) _glfwInputScroll(\n            w, &(GLFWScrollEvent){.momentum_type=GLFW_MOMENTUM_PHASE_CANCELED, .keyboard_modifiers=s.keyboard_modifiers});\n    }\n    s.window_id = 0;\n    s.keyboard_modifiers = 0;\n    deque_clear(&s.samples);\n    s.state = NONE;\n    if (reset_velocity) { s.velocity.x = 0; s.velocity.y = 0; }\n}\n\nstatic void\nadd_sample(double dx, double dy, monotonic_t now) {\n    deque_push_back(&s.samples, (ScrollSample){dx, dy, now}, NULL);\n}\n\nstatic void\nlast_sample_delta(double *dx, double *dy) {\n    const ScrollSample *ss;\n    if ((ss = deque_peek_back(&s.samples))) { *dx = ss->dx; *dy = ss->dy; }\n    else { *dx = 0; *dy = 0; }\n}\n\nstatic void\ntrim_old_samples(monotonic_t now) {\n    const ScrollSample *ss;\n    while ((ss = deque_peek_front(&s.samples)) && (now - ss->timestamp) > ms_to_monotonic_t(150))\n        deque_pop_front(&s.samples, NULL);\n}\n\nstatic void\nadd_velocity(double x, double y) {\n    if (x == 0 || x * s.velocity.x >= 0) s.velocity.x += x;\n    else s.velocity.x = x;\n    if (y == 0 || y * s.velocity.y >= 0) s.velocity.y += y;\n    else s.velocity.y = y;\n    s.velocity.x = MAX(-s.max_velocity, MIN(s.velocity.x, s.max_velocity));\n    s.velocity.y = MAX(-s.max_velocity, MIN(s.velocity.y, s.max_velocity));\n}\n\nstatic void\nset_velocity_from_samples(monotonic_t now) {\n    s.timer_interval = ms_to_monotonic_t(8);\n    trim_old_samples(now);\n    ScrollSample ss;\n    switch (deque_size(&s.samples)) {\n        case 0:\n            return;\n        case 1:\n            deque_pop_front(&s.samples, &ss);\n            add_velocity(ss.dx, ss.dy);\n            return;\n    }\n\n    // Use weighted average - more recent samples have higher weight\n    double total_dx = 0.0, total_dy = 0.0, total_weight = 0.0;\n    monotonic_t first_time = deque_peek_front(&s.samples)->timestamp;\n    monotonic_t last_time = deque_peek_back(&s.samples)->timestamp;\n    double time_span = MAX(1, last_time - first_time);\n    for (size_t i = 0; i < deque_size(&s.samples); i++) {\n        const ScrollSample *ss = deque_at(&s.samples, i);\n        double weight = 1.0 + (ss->timestamp - first_time) / time_span;\n        total_dx += ss->dx * weight; total_dy += ss->dy * weight;\n        total_weight += weight;\n    }\n    deque_clear(&s.samples);\n    if (total_weight <= 0) return;\n    double dy = total_dy / total_weight, dx = total_dx / total_weight;\n    add_velocity(dx, dy);\n    if (false) timed_debug_print(\"momentum scroll: event velocity: %.1f final velocity: %.1f\\n\", dy, s.velocity.y);\n}\n\nstatic void\nsend_momentum_event(bool is_start) {\n    _GLFWwindow *w = _glfwWindowForId(s.window_id);\n    if (!w || w != _glfwFocusedWindow()) {\n        cancel_existing_scroll(true);\n        return;\n    }\n    s.velocity.x *= s.friction; s.velocity.y *= s.friction;\n    if (fabs(s.velocity.x) < s.min_velocity) s.velocity.x = 0;\n    if (fabs(s.velocity.y) < s.min_velocity) s.velocity.y = 0;\n\n    GLFWMomentumType m = is_start ? GLFW_MOMENTUM_PHASE_BEGAN : GLFW_MOMENTUM_PHASE_ACTIVE;\n    if (s.velocity.x == 0 && s.velocity.y == 0 && !is_start) {\n        m = GLFW_MOMENTUM_PHASE_ENDED;\n        if (s.timer_id) glfwRemoveTimer(s.timer_id);\n        s.timer_id = 0;\n        s.state = NONE;\n    }\n    GLFWScrollEvent e = {\n        .offset_type=GLFW_SCROLL_OFFEST_HIGHRES, .momentum_type=m, .unscaled.x=s.velocity.x, .unscaled.y=s.velocity.y,\n        .x_offset=s.scale * s.velocity.x, .y_offset=s.scale * s.velocity.y, .keyboard_modifiers=s.keyboard_modifiers\n    };\n    _glfwInputScroll(w, &e);\n}\n\nstatic void\nmomentum_timer_fired(unsigned long long timer_id UNUSED, void *data UNUSED) {\n    send_momentum_event(false);\n}\n\nstatic void\nstart_momentum_scroll(monotonic_t now) {\n    set_velocity_from_samples(now);\n    send_momentum_event(true);\n    s.timer_id = glfwAddTimer(s.timer_interval, true, momentum_timer_fired, NULL, NULL);\n}\n\nstatic bool\nis_suitable_for_momentum(void) {\n    return (\n        MAX(fabs(s.physical_event.displacement.x), fabs(s.physical_event.displacement.y)) > 10 &&\n        s.physical_event.duration > ms_to_monotonic_t(2)\n    );\n}\n\nvoid\nglfw_handle_scroll_event_for_momentum(\n    _GLFWwindow *w, const GLFWScrollEvent *ev, bool stopped, bool is_finger_based\n) {\n    const bool is_synthetic_momentum_start_event = stopped && momentum_scroll_gesture_detection_timeout_ms;\n    if (!w) { cancel_existing_scroll(true); return; }\n    if (!is_finger_based || ev->offset_type != GLFW_SCROLL_OFFEST_HIGHRES || s.friction < 0 || s.friction >= 1) {\n        if (ev->x_offset != 0 || ev->y_offset != 0) _glfwInputScroll(w, ev);\n        return;\n    }\n    monotonic_t now = monotonic();\n    if (is_synthetic_momentum_start_event) now -= ms_to_monotonic_t(momentum_scroll_gesture_detection_timeout_ms);\n    if (s.state == PHYSICAL_EVENT_IN_PROGRESS) {\n        s.physical_event.displacement.x += ev->unscaled.x;\n        s.physical_event.displacement.y += ev->unscaled.y;\n        if (stopped) {\n            s.physical_event.duration = now - s.physical_event.start;\n            s.physical_event.start = 0;\n        }\n    } else {\n        memset(&s.physical_event, 0, sizeof(s.physical_event));\n        s.physical_event.start = now;\n    }\n    if (ev->unscaled.y > 0) s.scale = ev->y_offset / ev->unscaled.y;\n    else if (ev->unscaled.x > 0) s.scale = ev->x_offset / ev->unscaled.x;\n    if (s.window_id && s.window_id != w->id) cancel_existing_scroll(true);\n    if (s.state != PHYSICAL_EVENT_IN_PROGRESS) cancel_existing_scroll(false);\n    if (!is_synthetic_momentum_start_event) {\n        // Check for change in direction\n        double ldx, ldy; last_sample_delta(&ldx, &ldy);\n        if (ldx * ev->x_offset < 0 || ldy * ev->y_offset < 0) cancel_existing_scroll(true);\n    }\n    s.window_id = w->id;\n    s.keyboard_modifiers = ev->keyboard_modifiers;\n    if (ev->offset_type == GLFW_SCROLL_OFFEST_HIGHRES) {\n        if (!is_synthetic_momentum_start_event) add_sample(ev->unscaled.x, ev->unscaled.y, now);\n        if (stopped) s.state = is_suitable_for_momentum() ? MOMENTUM_IN_PROGRESS : NONE;\n        else s.state = PHYSICAL_EVENT_IN_PROGRESS;\n    } else {\n        s.state = stopped ? NONE : PHYSICAL_EVENT_IN_PROGRESS;\n    }\n    if (s.state == MOMENTUM_IN_PROGRESS) start_momentum_scroll(now);\n    else _glfwInputScroll(w, ev);\n}\n"
  },
  {
    "path": "glfw/monitor.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// Please use C89 style variable declarations in this file because VS 2010\n//========================================================================\n\n#include \"internal.h\"\n\n#include <assert.h>\n#include <math.h>\n#include <float.h>\n#include <string.h>\n#include <stdlib.h>\n#include <limits.h>\n\n\n// Lexically compare video modes, used by qsort\n//\nstatic int compareVideoModes(const void* fp, const void* sp)\n{\n    const GLFWvidmode* fm = fp;\n    const GLFWvidmode* sm = sp;\n    const int fbpp = fm->redBits + fm->greenBits + fm->blueBits;\n    const int sbpp = sm->redBits + sm->greenBits + sm->blueBits;\n    const int farea = fm->width * fm->height;\n    const int sarea = sm->width * sm->height;\n\n    // First sort on color bits per pixel\n    if (fbpp != sbpp)\n        return fbpp - sbpp;\n\n    // Then sort on screen area\n    if (farea != sarea)\n        return farea - sarea;\n\n    // Then sort on width\n    if (fm->width != sm->width)\n        return fm->width - sm->width;\n\n    // Lastly sort on refresh rate\n    return fm->refreshRate - sm->refreshRate;\n}\n\n// Retrieves the available modes for the specified monitor\n//\nstatic bool refreshVideoModes(_GLFWmonitor* monitor)\n{\n    int modeCount;\n    GLFWvidmode* modes;\n\n    if (monitor->modes)\n        return true;\n\n    modes = _glfwPlatformGetVideoModes(monitor, &modeCount);\n    if (!modes)\n        return false;\n\n    qsort(modes, modeCount, sizeof(modes[0]), compareVideoModes);\n\n    free(monitor->modes);\n    monitor->modes = modes;\n    monitor->modeCount = modeCount;\n\n    return true;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                         GLFW event API                       //////\n//////////////////////////////////////////////////////////////////////////\n\n// Notifies shared code of a monitor connection or disconnection\n//\nvoid _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)\n{\n    if (action == GLFW_CONNECTED)\n    {\n        _glfw.monitorCount++;\n        _glfw.monitors =\n            realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount);\n\n        if (placement == _GLFW_INSERT_FIRST)\n        {\n            memmove(_glfw.monitors + 1,\n                    _glfw.monitors,\n                    ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*));\n            _glfw.monitors[0] = monitor;\n        }\n        else\n            _glfw.monitors[_glfw.monitorCount - 1] = monitor;\n    }\n    else if (action == GLFW_DISCONNECTED)\n    {\n        int i;\n        _GLFWwindow* window;\n\n        for (window = _glfw.windowListHead;  window;  window = window->next)\n        {\n            if (window->monitor == monitor)\n            {\n                int width, height, xoff, yoff;\n                _glfwPlatformGetWindowSize(window, &width, &height);\n                _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0);\n                _glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL);\n                _glfwPlatformSetWindowPos(window, xoff, yoff);\n            }\n        }\n\n        for (i = 0;  i < _glfw.monitorCount;  i++)\n        {\n            if (_glfw.monitors[i] == monitor)\n            {\n                remove_i_from_array(_glfw.monitors, i, _glfw.monitorCount);\n                break;\n            }\n        }\n    }\n\n    if (_glfw.callbacks.monitor)\n        _glfw.callbacks.monitor((GLFWmonitor*) monitor, action);\n\n    if (action == GLFW_DISCONNECTED)\n        _glfwFreeMonitor(monitor);\n}\n\n// Notifies shared code that a full screen window has acquired or released\n// a monitor\n//\nvoid _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window)\n{\n    monitor->window = window;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Allocates and returns a monitor object with the specified name and dimensions\n//\n_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM)\n{\n    _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor));\n    monitor->widthMM = widthMM;\n    monitor->heightMM = heightMM;\n\n    if (name)\n        monitor->name = _glfw_strdup(name);\n\n    return monitor;\n}\n\n// Frees a monitor object and any data associated with it\n//\nvoid _glfwFreeMonitor(_GLFWmonitor* monitor)\n{\n    if (monitor == NULL)\n        return;\n\n    _glfwPlatformFreeMonitor(monitor);\n\n    _glfwFreeGammaArrays(&monitor->originalRamp);\n    _glfwFreeGammaArrays(&monitor->currentRamp);\n\n    free(monitor->modes);\n    free((void*)monitor->name);\n    free((void*)monitor->description);\n    free(monitor);\n}\n\n// Allocates red, green and blue value arrays of the specified size\n//\nvoid _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size)\n{\n    ramp->red = calloc(size, sizeof(unsigned short));\n    ramp->green = calloc(size, sizeof(unsigned short));\n    ramp->blue = calloc(size, sizeof(unsigned short));\n    ramp->size = size;\n}\n\n// Frees the red, green and blue value arrays and clears the struct\n//\nvoid _glfwFreeGammaArrays(GLFWgammaramp* ramp)\n{\n    free(ramp->red);\n    free(ramp->green);\n    free(ramp->blue);\n\n    memset(ramp, 0, sizeof(GLFWgammaramp));\n}\n\n// Chooses the video mode most closely matching the desired one\n//\nconst GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,\n                                        const GLFWvidmode* desired)\n{\n    int i;\n    unsigned int sizeDiff, leastSizeDiff = UINT_MAX;\n    unsigned int rateDiff, leastRateDiff = UINT_MAX;\n    unsigned int colorDiff, leastColorDiff = UINT_MAX;\n    const GLFWvidmode* current;\n    const GLFWvidmode* closest = NULL;\n\n    if (!refreshVideoModes(monitor))\n        return NULL;\n\n    for (i = 0;  i < monitor->modeCount;  i++)\n    {\n        current = monitor->modes + i;\n\n        colorDiff = 0;\n\n        if (desired->redBits != GLFW_DONT_CARE)\n            colorDiff += abs(current->redBits - desired->redBits);\n        if (desired->greenBits != GLFW_DONT_CARE)\n            colorDiff += abs(current->greenBits - desired->greenBits);\n        if (desired->blueBits != GLFW_DONT_CARE)\n            colorDiff += abs(current->blueBits - desired->blueBits);\n\n        sizeDiff = abs((current->width - desired->width) *\n                       (current->width - desired->width) +\n                       (current->height - desired->height) *\n                       (current->height - desired->height));\n\n        if (desired->refreshRate != GLFW_DONT_CARE)\n            rateDiff = abs(current->refreshRate - desired->refreshRate);\n        else\n            rateDiff = UINT_MAX - current->refreshRate;\n\n        if ((colorDiff < leastColorDiff) ||\n            (colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) ||\n            (colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff))\n        {\n            closest = current;\n            leastSizeDiff = sizeDiff;\n            leastRateDiff = rateDiff;\n            leastColorDiff = colorDiff;\n        }\n    }\n\n    return closest;\n}\n\n// Performs lexical comparison between two @ref GLFWvidmode structures\n//\nint _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm)\n{\n    return compareVideoModes(fm, sm);\n}\n\n// Splits a color depth into red, green and blue bit depths\n//\nvoid _glfwSplitBPP(int bpp, int* red, int* green, int* blue)\n{\n    int delta;\n\n    // We assume that by 32 the user really meant 24\n    if (bpp == 32)\n        bpp = 24;\n\n    // Convert \"bits per pixel\" to red, green & blue sizes\n\n    *red = *green = *blue = bpp / 3;\n    delta = bpp - (*red * 3);\n    if (delta >= 1)\n        *green = *green + 1;\n\n    if (delta == 2)\n        *red = *red + 1;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW public API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI GLFWmonitor** glfwGetMonitors(int* count)\n{\n    assert(count != NULL);\n\n    *count = 0;\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    *count = _glfw.monitorCount;\n    return (GLFWmonitor**) _glfw.monitors;\n}\n\nGLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (!_glfw.monitorCount)\n        return NULL;\n\n    return (GLFWmonitor*) _glfw.monitors[0];\n}\n\nGLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    if (xpos)\n        *xpos = 0;\n    if (ypos)\n        *ypos = 0;\n\n    _GLFW_REQUIRE_INIT();\n\n    _glfwPlatformGetMonitorPos(monitor, xpos, ypos);\n}\n\nGLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle,\n                                    int* xpos, int* ypos,\n                                    int* width, int* height)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    if (xpos)\n        *xpos = 0;\n    if (ypos)\n        *ypos = 0;\n    if (width)\n        *width = 0;\n    if (height)\n        *height = 0;\n\n    _GLFW_REQUIRE_INIT();\n\n    _glfwPlatformGetMonitorWorkarea(monitor, xpos, ypos, width, height);\n}\n\nGLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    if (widthMM)\n        *widthMM = 0;\n    if (heightMM)\n        *heightMM = 0;\n\n    _GLFW_REQUIRE_INIT();\n\n    if (widthMM)\n        *widthMM = monitor->widthMM;\n    if (heightMM)\n        *heightMM = monitor->heightMM;\n}\n\nGLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle,\n                                        float* xscale, float* yscale)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    if (xscale)\n        *xscale = 0.f;\n    if (yscale)\n        *yscale = 0.f;\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale);\n}\n\nGLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return monitor->name ? monitor->name : \"\";\n}\n\nGLFWAPI const char* glfwGetMonitorDescription(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return monitor->description ? monitor->description : \"\";\n}\n\n\nGLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT();\n    monitor->userPointer = pointer;\n}\n\nGLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return monitor->userPointer;\n}\n\nGLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(_glfw.callbacks.monitor, cbfun);\n    return cbfun;\n}\n\nGLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n    assert(count != NULL);\n\n    *count = 0;\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (!refreshVideoModes(monitor))\n        return NULL;\n\n    *count = monitor->modeCount;\n    return monitor->modes;\n}\n\nGLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (!_glfwPlatformGetVideoMode(monitor, &monitor->currentMode)) return NULL;\n    return &monitor->currentMode;\n}\n\nGLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)\n{\n    unsigned int i;\n    unsigned short* values;\n    GLFWgammaramp ramp;\n    const GLFWgammaramp* original;\n    assert(handle != NULL);\n    assert(gamma > 0.f);\n    assert(gamma <= FLT_MAX);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX)\n    {\n        _glfwInputError(GLFW_INVALID_VALUE, \"Invalid gamma value %f\", gamma);\n        return;\n    }\n\n    original = glfwGetGammaRamp(handle);\n    if (!original)\n        return;\n\n    values = calloc(original->size, sizeof(unsigned short));\n\n    for (i = 0;  i < original->size;  i++)\n    {\n        float value;\n\n        // Calculate intensity\n        value = i / (float) (original->size - 1);\n        // Apply gamma curve\n        value = powf(value, 1.f / gamma) * 65535.f + 0.5f;\n        // Clamp to value range\n        value = fminf(value, 65535.f);\n\n        values[i] = (unsigned short) value;\n    }\n\n    ramp.red = values;\n    ramp.green = values;\n    ramp.blue = values;\n    ramp.size = original->size;\n\n    glfwSetGammaRamp(handle, &ramp);\n    free(values);\n}\n\nGLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    _glfwFreeGammaArrays(&monitor->currentRamp);\n    if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp))\n        return NULL;\n\n    return &monitor->currentRamp;\n}\n\nGLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n    assert(ramp != NULL);\n    assert(ramp->size > 0);\n    assert(ramp->red != NULL);\n    assert(ramp->green != NULL);\n    assert(ramp->blue != NULL);\n\n    if (ramp->size <= 0)\n    {\n        _glfwInputError(GLFW_INVALID_VALUE,\n                        \"Invalid gamma ramp size %i\",\n                        ramp->size);\n        return;\n    }\n\n    _GLFW_REQUIRE_INIT();\n\n    if (!monitor->originalRamp.size)\n    {\n        if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp))\n            return;\n    }\n\n    _glfwPlatformSetGammaRamp(monitor, ramp);\n}\n"
  },
  {
    "path": "glfw/monotonic.c",
    "content": "/*\n * monotonic.c\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define _POSIX_C_SOURCE 200809L\n#define MONOTONIC_IMPLEMENTATION\n#include \"../kitty/monotonic.h\"\n"
  },
  {
    "path": "glfw/nsgl_context.h",
    "content": "//========================================================================\n// GLFW 3.4 macOS - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#define _GLFW_PLATFORM_CONTEXT_STATE            _GLFWcontextNSGL nsgl;\n#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE    _GLFWlibraryNSGL nsgl;\n\n\n// NSGL-specific per-context data\n//\ntypedef struct _GLFWcontextNSGL\n{\n    id                pixelFormat;\n    id                object;\n\n} _GLFWcontextNSGL;\n\n// NSGL-specific global data\n//\ntypedef struct _GLFWlibraryNSGL\n{\n    // dlopen handle for OpenGL.framework (for glfwGetProcAddress)\n    CFBundleRef     framework;\n\n} _GLFWlibraryNSGL;\n\n\nbool _glfwInitNSGL(void);\nvoid _glfwTerminateNSGL(void);\nbool _glfwCreateContextNSGL(_GLFWwindow* window,\n                                const _GLFWctxconfig* ctxconfig,\n                                const _GLFWfbconfig* fbconfig);\nvoid _glfwDestroyContextNSGL(_GLFWwindow* window);\n"
  },
  {
    "path": "glfw/nsgl_context.m",
    "content": "//========================================================================\n// GLFW 3.4 macOS - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n#include <assert.h>\n\n\nstatic void makeContextCurrentNSGL(_GLFWwindow* window)\n{\n    if (window)\n        [window->context.nsgl.object makeCurrentContext];\n    else\n        [NSOpenGLContext clearCurrentContext];\n\n    _glfwPlatformSetTls(&_glfw.contextSlot, window);\n}\n\nstatic void swapBuffersNSGL(_GLFWwindow* window)\n{\n    // ARP appears to be unnecessary, but this is future-proof\n    [window->context.nsgl.object flushBuffer];\n}\n\nstatic void swapIntervalNSGL(int interval UNUSED)\n{\n    // As of Mojave this does not work so we use CVDisplayLink instead\n    _glfwInputError(GLFW_API_UNAVAILABLE,\n                    \"NSGL: Swap intervals do not work on macOS\");\n}\n\nstatic int extensionSupportedNSGL(const char* extension UNUSED)\n{\n    // There are no NSGL extensions\n    return false;\n}\n\nstatic GLFWglproc getProcAddressNSGL(const char* procname)\n{\n    CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault,\n                                                       procname,\n                                                       kCFStringEncodingASCII);\n\n    GLFWglproc symbol;\n    *(void **) &symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework,\n                                                           symbolName);\n\n    CFRelease(symbolName);\n\n    return symbol;\n}\n\nstatic void destroyContextNSGL(_GLFWwindow* window)\n{\n    [window->context.nsgl.pixelFormat release];\n    window->context.nsgl.pixelFormat = nil;\n\n    [window->context.nsgl.object release];\n    window->context.nsgl.object = nil;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Initialize OpenGL support\n//\nbool _glfwInitNSGL(void)\n{\n    if (_glfw.nsgl.framework)\n        return true;\n\n    _glfw.nsgl.framework =\n        CFBundleGetBundleWithIdentifier(CFSTR(\"com.apple.opengl\"));\n    if (_glfw.nsgl.framework == NULL)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"NSGL: Failed to locate OpenGL framework\");\n        return false;\n    }\n\n    return true;\n}\n\n// We use a globally shared context across all OS Windows so that\n// if all OS Windows are closed the context does not have to be recreated\n// with all associated data lost. On Apple kitty can remain running with no\n// OS Windows.\nstatic NSOpenGLContext* globally_shared_context = nil;\n\n// Terminate OpenGL support\n//\nvoid _glfwTerminateNSGL(void) {\n    if (globally_shared_context != nil) {\n        [globally_shared_context release];\n        globally_shared_context = nil;\n    }\n}\n\n\n// Create the OpenGL context\n//\nbool _glfwCreateContextNSGL(_GLFWwindow* window,\n                                const _GLFWctxconfig* ctxconfig,\n                                const _GLFWfbconfig* fbconfig)\n{\n    if (ctxconfig->client == GLFW_OPENGL_ES_API)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"NSGL: OpenGL ES is not available on macOS\");\n        return false;\n    }\n\n    if (ctxconfig->major > 2)\n    {\n        if (ctxconfig->major == 3 && ctxconfig->minor < 2)\n        {\n            _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                            \"NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above\");\n            return false;\n        }\n    }\n\n    // Context robustness modes (GL_KHR_robustness) are not yet supported by\n    // macOS but are not a hard constraint, so ignore and continue\n\n    // Context release behaviors (GL_KHR_context_flush_control) are not yet\n    // supported by macOS but are not a hard constraint, so ignore and continue\n\n    // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not\n    // a hard constraint, so ignore and continue\n\n    // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but\n    // are not a hard constraint, so ignore and continue\n\n#define addAttrib(a) \\\n{ \\\n    assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \\\n    attribs[index++] = a; \\\n}\n#define setAttrib(a, v) { addAttrib(a); addAttrib(v); }\n\n    NSOpenGLPixelFormatAttribute attribs[40];\n    int index = 0;\n\n    addAttrib(NSOpenGLPFAAccelerated);\n    addAttrib(NSOpenGLPFAClosestPolicy);\n\n    if (ctxconfig->nsgl.offline)\n    {\n        addAttrib(NSOpenGLPFAAllowOfflineRenderers);\n        // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in\n        //       Info.plist for unbundled applications\n        // HACK: This assumes that NSOpenGLPixelFormat will remain\n        //       a straightforward wrapper of its CGL counterpart\n        addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);\n    }\n\n#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000\n    if (ctxconfig->major >= 4)\n    {\n        setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core);\n    }\n    else\n#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/\n    if (ctxconfig->major >= 3)\n    {\n        setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);\n    }\n\n    if (ctxconfig->major <= 2)\n    {\n        if (fbconfig->auxBuffers != GLFW_DONT_CARE)\n            setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers);\n\n        if (fbconfig->accumRedBits != GLFW_DONT_CARE &&\n            fbconfig->accumGreenBits != GLFW_DONT_CARE &&\n            fbconfig->accumBlueBits != GLFW_DONT_CARE &&\n            fbconfig->accumAlphaBits != GLFW_DONT_CARE)\n        {\n            const int accumBits = fbconfig->accumRedBits +\n                                  fbconfig->accumGreenBits +\n                                  fbconfig->accumBlueBits +\n                                  fbconfig->accumAlphaBits;\n\n            setAttrib(NSOpenGLPFAAccumSize, accumBits);\n        }\n    }\n\n    if (fbconfig->redBits != GLFW_DONT_CARE &&\n        fbconfig->greenBits != GLFW_DONT_CARE &&\n        fbconfig->blueBits != GLFW_DONT_CARE)\n    {\n        int colorBits = fbconfig->redBits +\n                        fbconfig->greenBits +\n                        fbconfig->blueBits;\n\n        // macOS needs non-zero color size, so set reasonable values\n        if (colorBits == 0)\n            colorBits = 24;\n        else if (colorBits < 15)\n            colorBits = 15;\n\n        setAttrib(NSOpenGLPFAColorSize, colorBits);\n    }\n\n    if (fbconfig->alphaBits != GLFW_DONT_CARE)\n        setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);\n\n    if (fbconfig->depthBits != GLFW_DONT_CARE)\n        setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits);\n\n    if (fbconfig->stencilBits != GLFW_DONT_CARE)\n        setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits);\n\n    if (fbconfig->stereo)\n    {\n#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200\n        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,\n                        \"NSGL: Stereo rendering is deprecated\");\n        return false;\n#else\n        addAttrib(NSOpenGLPFAStereo);\n#endif\n    }\n\n    if (fbconfig->doublebuffer)\n        addAttrib(NSOpenGLPFADoubleBuffer);\n\n    if (fbconfig->samples != GLFW_DONT_CARE)\n    {\n        if (fbconfig->samples == 0)\n        {\n            setAttrib(NSOpenGLPFASampleBuffers, 0);\n        }\n        else\n        {\n            setAttrib(NSOpenGLPFASampleBuffers, 1);\n            setAttrib(NSOpenGLPFASamples, fbconfig->samples);\n        }\n    }\n\n    // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB\n    //       framebuffer, so there's no need (and no way) to request it\n\n    addAttrib(0);\n\n#undef addAttrib\n#undef setAttrib\n\n    window->context.nsgl.pixelFormat =\n        [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];\n    if (window->context.nsgl.pixelFormat == nil)\n    {\n        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,\n                        \"NSGL: Failed to find a suitable pixel format\");\n        return false;\n    }\n\n    NSOpenGLContext* share = globally_shared_context;\n\n    if (ctxconfig->share)\n        share = ctxconfig->share->context.nsgl.object;\n\n    window->context.nsgl.object =\n        [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat\n                                   shareContext:share];\n    if (window->context.nsgl.object == nil)\n    {\n        _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                        \"NSGL: Failed to create OpenGL context\");\n        return false;\n    }\n    if (globally_shared_context == nil) {\n        globally_shared_context = [window->context.nsgl.object retain];\n    }\n\n    if (fbconfig->transparent)\n    {\n        GLint opaque = 0;\n        [window->context.nsgl.object setValues:&opaque\n                                  forParameter:NSOpenGLContextParameterSurfaceOpacity];\n    }\n\n    [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina];\n\n    GLint interval = 0;\n    [window->context.nsgl.object setValues:&interval\n                              forParameter:NSOpenGLContextParameterSwapInterval];\n\n    [window->context.nsgl.object setView:window->ns.view];\n\n    window->context.makeCurrent = makeContextCurrentNSGL;\n    window->context.swapBuffers = swapBuffersNSGL;\n    window->context.swapInterval = swapIntervalNSGL;\n    window->context.extensionSupported = extensionSupportedNSGL;\n    window->context.getProcAddress = getProcAddressNSGL;\n    window->context.destroy = destroyContextNSGL;\n\n    return true;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI id glfwGetNSGLContext(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(nil);\n\n    if (window->context.client == GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);\n        return nil;\n    }\n\n    return window->context.nsgl.object;\n}\n\nGLFWAPI bool\nglfwCocoaRecreateGLDrawable(GLFWwindow *w) {\n    _GLFWwindow* window = (_GLFWwindow*)w;\n    if (window->context.client == GLFW_NO_API) return false;\n    @try {\n        // Save current state\n        NSOpenGLPixelFormat *pixelFormat = window->context.nsgl.pixelFormat;\n        NSOpenGLContext *oldContext = window->context.nsgl.object;\n\n        // Create a new context sharing resources with the old one\n        NSOpenGLContext *newContext = [[NSOpenGLContext alloc]\n            initWithFormat:pixelFormat\n              shareContext:oldContext];\n        if (newContext == nil) return false;\n\n        // Copy settings from old context\n        GLint opacity = 0;\n        [oldContext getValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];\n        [newContext setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];\n        GLint interval = 0;\n        [newContext setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];\n\n        // Detach old context\n        [NSOpenGLContext clearCurrentContext];\n        [oldContext clearDrawable];\n\n        // Attach new context to the view\n        [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina];\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n        [newContext setView:window->ns.view];\n#pragma clang diagnostic pop\n        [newContext makeCurrentContext];\n        [newContext update];\n\n        // Replace context\n        window->context.nsgl.object = newContext;\n        [oldContext release];\n    } @catch (NSException *e) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to recreate NSGL context: %s (%s)\", [[e name] UTF8String], [[e reason] UTF8String]);\n        return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "glfw/null_init.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2016 Google Inc.\n// Copyright (c) 2016-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n#include <stdlib.h>\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nint _glfwPlatformInit(void)\n{\n    _glfwPollMonitorsNull();\n\n    return true;\n}\n\nvoid _glfwPlatformTerminate(void)\n{\n    free(_glfw.null.clipboardString);\n    _glfwTerminateOSMesa();\n}\n\nconst char* _glfwPlatformGetVersionString(void)\n{\n    return _GLFW_VERSION_NUMBER \" null OSMesa\";\n}\n"
  },
  {
    "path": "glfw/null_joystick.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2016-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nbool _glfwPlatformInitJoysticks(void)\n{\n    return true;\n}\n\nvoid _glfwPlatformTerminateJoysticks(void)\n{\n}\n\nint _glfwPlatformPollJoystick(_GLFWjoystick* js UNUSED, int mode UNUSED)\n{\n    return false;\n}\n\nvoid _glfwPlatformUpdateGamepadGUID(char* guid UNUSED)\n{\n}\n\n"
  },
  {
    "path": "glfw/null_joystick.h",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#define _GLFW_PLATFORM_JOYSTICK_STATE         int nulljs\n#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int nulljs;\n\n#define _GLFW_PLATFORM_MAPPING_NAME \"\"\n"
  },
  {
    "path": "glfw/null_monitor.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2016 Google Inc.\n// Copyright (c) 2016-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n\n// The sole (fake) video mode of our (sole) fake monitor\n//\nstatic GLFWvidmode getVideoMode(void)\n{\n    GLFWvidmode mode;\n    mode.width = 1920;\n    mode.height = 1080;\n    mode.redBits = 8;\n    mode.greenBits = 8;\n    mode.blueBits = 8;\n    mode.refreshRate = 60;\n    return mode;\n}\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid _glfwPollMonitorsNull(void)\n{\n    const float dpi = 141.f;\n    const GLFWvidmode mode = getVideoMode();\n    _GLFWmonitor* monitor = _glfwAllocMonitor(\"Null SuperNoop 0\",\n                                              (int) (mode.width * 25.4f / dpi),\n                                              (int) (mode.height * 25.4f / dpi));\n    _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_FIRST);\n}\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)\n{\n    _glfwFreeGammaArrays(&monitor->null.ramp);\n}\n\nvoid _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor UNUSED, int* xpos, int* ypos)\n{\n    if (xpos)\n        *xpos = 0;\n    if (ypos)\n        *ypos = 0;\n}\n\nvoid _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor UNUSED,\n                                         float* xscale, float* yscale)\n{\n    if (xscale)\n        *xscale = 1.f;\n    if (yscale)\n        *yscale = 1.f;\n}\n\nvoid _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor UNUSED,\n                                     int* xpos, int* ypos,\n                                     int* width, int* height)\n{\n    const GLFWvidmode mode = getVideoMode();\n\n    if (xpos)\n        *xpos = 0;\n    if (ypos)\n        *ypos = 10;\n    if (width)\n        *width = mode.width;\n    if (height)\n        *height = mode.height - 10;\n}\n\nGLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor UNUSED, int* found)\n{\n    GLFWvidmode* mode = calloc(1, sizeof(GLFWvidmode));\n    *mode = getVideoMode();\n    *found = 1;\n    return mode;\n}\n\nvoid _glfwPlatformGetVideoMode(_GLFWmonitor* monitor UNUSED, GLFWvidmode* mode)\n{\n    *mode = getVideoMode();\n}\n\nbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)\n{\n    if (!monitor->null.ramp.size)\n    {\n        _glfwAllocGammaArrays(&monitor->null.ramp, 256);\n\n        for (unsigned int i = 0;  i < monitor->null.ramp.size;  i++)\n        {\n            const float gamma = 2.2f;\n            float value;\n            value = i / (float) (monitor->null.ramp.size - 1);\n            value = powf(value, 1.f / gamma) * 65535.f + 0.5f;\n            value = _glfw_fminf(value, 65535.f);\n\n            monitor->null.ramp.red[i]   = (unsigned short) value;\n            monitor->null.ramp.green[i] = (unsigned short) value;\n            monitor->null.ramp.blue[i]  = (unsigned short) value;\n        }\n    }\n\n    _glfwAllocGammaArrays(ramp, monitor->null.ramp.size);\n    memcpy(ramp->red,   monitor->null.ramp.red,   sizeof(short) * ramp->size);\n    memcpy(ramp->green, monitor->null.ramp.green, sizeof(short) * ramp->size);\n    memcpy(ramp->blue,  monitor->null.ramp.blue,  sizeof(short) * ramp->size);\n    return true;\n}\n\nvoid _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)\n{\n    if (monitor->null.ramp.size != ramp->size)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Null: Gamma ramp size must match current ramp size\");\n        return;\n    }\n\n    memcpy(monitor->null.ramp.red,   ramp->red,   sizeof(short) * ramp->size);\n    memcpy(monitor->null.ramp.green, ramp->green, sizeof(short) * ramp->size);\n    memcpy(monitor->null.ramp.blue,  ramp->blue,  sizeof(short) * ramp->size);\n}\n\n"
  },
  {
    "path": "glfw/null_platform.h",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2016 Google Inc.\n// Copyright (c) 2016-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#include <dlfcn.h>\n\n#define _GLFW_PLATFORM_WINDOW_STATE         _GLFWwindowNull null\n#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNull null\n#define _GLFW_PLATFORM_MONITOR_STATE        _GLFWmonitorNull null\n\n#define _GLFW_PLATFORM_CONTEXT_STATE\n#define _GLFW_PLATFORM_CURSOR_STATE\n#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE\n\n#include \"posix_time.h\"\n#include \"posix_thread.h\"\n#include \"null_joystick.h\"\n\n#if defined(_GLFW_WIN32)\n #define _glfw_dlopen(name) LoadLibraryA(name)\n #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle)\n #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name)\n#else\n #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)\n #define _glfw_dlclose(handle) dlclose(handle)\n #define _glfw_dlsym(handle, name) dlsym(handle, name)\n#endif\n\n// Null-specific per-window data\n//\ntypedef struct _GLFWwindowNull\n{\n    int             xpos;\n    int             ypos;\n    int             width;\n    int             height;\n    char*           title;\n    bool            visible;\n    bool            iconified;\n    bool            maximized;\n    bool            resizable;\n    bool            decorated;\n    bool            floating;\n    bool            transparent;\n    float           opacity;\n} _GLFWwindowNull;\n\n// Null-specific per-monitor data\n//\ntypedef struct _GLFWmonitorNull\n{\n    GLFWgammaramp   ramp;\n} _GLFWmonitorNull;\n\n// Null-specific global data\n//\ntypedef struct _GLFWlibraryNull\n{\n    int             xcursor;\n    int             ycursor;\n    char*           clipboardString;\n    _GLFWwindow*    focusedWindow;\n} _GLFWlibraryNull;\n\nvoid _glfwPollMonitorsNull(void);\n"
  },
  {
    "path": "glfw/null_window.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2016 Google Inc.\n// Copyright (c) 2016-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n#include \"../kitty/monotonic.h\"\n\n#include <errno.h>\n#include <stdlib.h>\n\nstatic void applySizeLimits(_GLFWwindow* window, int* width, int* height)\n{\n    if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)\n    {\n        const float ratio = (float) window->numer / (float) window->denom;\n        *height = (int) (*width / ratio);\n    }\n\n    if (window->minwidth != GLFW_DONT_CARE && *width < window->minwidth)\n        *width = window->minwidth;\n    else if (window->maxwidth != GLFW_DONT_CARE && *width > window->maxwidth)\n        *width = window->maxwidth;\n\n    if (window->minheight != GLFW_DONT_CARE && *height < window->minheight)\n        *height = window->minheight;\n    else if (window->maxheight != GLFW_DONT_CARE && *height > window->maxheight)\n        *height = window->maxheight;\n}\n\nstatic void fitToMonitor(_GLFWwindow* window)\n{\n    GLFWvidmode mode;\n    _glfwPlatformGetVideoMode(window->monitor, &mode);\n    _glfwPlatformGetMonitorPos(window->monitor,\n                               &window->null.xpos,\n                               &window->null.ypos);\n    window->null.width = mode.width;\n    window->null.height = mode.height;\n}\n\nstatic void acquireMonitor(_GLFWwindow* window)\n{\n    _glfwInputMonitorWindow(window->monitor, window);\n}\n\nstatic void releaseMonitor(_GLFWwindow* window)\n{\n    if (window->monitor->window != window)\n        return;\n\n    _glfwInputMonitorWindow(window->monitor, NULL);\n}\n\nstatic int createNativeWindow(_GLFWwindow* window,\n                              const _GLFWwndconfig* wndconfig,\n                              const _GLFWfbconfig* fbconfig)\n{\n    if (window->monitor)\n        fitToMonitor(window);\n    else\n    {\n        window->null.xpos = 17;\n        window->null.ypos = 17;\n        window->null.width = wndconfig->width;\n        window->null.height = wndconfig->height;\n    }\n\n    window->null.visible = wndconfig->visible;\n    window->null.decorated = wndconfig->decorated;\n    window->null.maximized = wndconfig->maximized;\n    window->null.floating = wndconfig->floating;\n    window->null.transparent = fbconfig->transparent;\n    window->null.opacity = 1.f;\n\n    return true;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nint _glfwPlatformCreateWindow(_GLFWwindow* window,\n                              const _GLFWwndconfig* wndconfig,\n                              const _GLFWctxconfig* ctxconfig,\n                              const _GLFWfbconfig* fbconfig)\n{\n    if (!createNativeWindow(window, wndconfig, fbconfig))\n        return false;\n\n    if (ctxconfig->client != GLFW_NO_API)\n    {\n        if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API ||\n            ctxconfig->source == GLFW_OSMESA_CONTEXT_API)\n        {\n            if (!_glfwInitOSMesa())\n                return false;\n            if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))\n                return false;\n        }\n        else\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE, \"Null: EGL not available\");\n            return false;\n        }\n    }\n\n    if (window->monitor)\n    {\n        _glfwPlatformShowWindow(window);\n        _glfwPlatformFocusWindow(window);\n        acquireMonitor(window);\n    }\n\n    return true;\n}\n\nvoid _glfwPlatformDestroyWindow(_GLFWwindow* window)\n{\n    if (window->monitor)\n        releaseMonitor(window);\n\n    if (_glfw.null.focusedWindow == window)\n        _glfw.null.focusedWindow = NULL;\n\n    if (window->context.destroy)\n        window->context.destroy(window);\n}\n\nvoid _glfwPlatformSetWindowTitle(_GLFWwindow* window UNUSED, const char* title UNUSED)\n{\n}\n\nvoid _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED, int count UNUSED,\n                                const GLFWimage* images UNUSED)\n{\n}\n\nvoid _glfwPlatformSetWindowMonitor(_GLFWwindow* window,\n                                   _GLFWmonitor* monitor,\n                                   int xpos, int ypos,\n                                   int width, int height,\n                                   int refreshRate UNUSED)\n{\n    if (window->monitor == monitor)\n    {\n        if (!monitor)\n        {\n            _glfwPlatformSetWindowPos(window, xpos, ypos);\n            _glfwPlatformSetWindowSize(window, width, height);\n        }\n\n        return;\n    }\n\n    if (window->monitor)\n        releaseMonitor(window);\n\n    _glfwInputWindowMonitor(window, monitor);\n\n    if (window->monitor)\n    {\n        window->null.visible = true;\n        acquireMonitor(window);\n        fitToMonitor(window);\n    }\n    else\n    {\n        _glfwPlatformSetWindowPos(window, xpos, ypos);\n        _glfwPlatformSetWindowSize(window, width, height);\n    }\n}\n\nvoid _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)\n{\n    if (xpos)\n        *xpos = window->null.xpos;\n    if (ypos)\n        *ypos = window->null.ypos;\n}\n\nvoid _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)\n{\n    if (window->monitor)\n        return;\n\n    if (window->null.xpos != xpos || window->null.ypos != ypos)\n    {\n        window->null.xpos = xpos;\n        window->null.ypos = ypos;\n        _glfwInputWindowPos(window, xpos, ypos);\n    }\n}\n\nvoid _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)\n{\n    if (width)\n        *width = window->null.width;\n    if (height)\n        *height = window->null.height;\n}\n\nvoid _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)\n{\n    if (window->monitor)\n        return;\n\n    if (window->null.width != width || window->null.height != height)\n    {\n        window->null.width = width;\n        window->null.height = height;\n        _glfwInputWindowSize(window, width, height);\n        _glfwInputFramebufferSize(window, width, height);\n    }\n}\n\nvoid _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,\n                                      int minwidth UNUSED, int minheight UNUSED,\n                                      int maxwidth UNUSED, int maxheight UNUSED)\n{\n    int width = window->null.width;\n    int height = window->null.height;\n    applySizeLimits(window, &width, &height);\n    _glfwPlatformSetWindowSize(window, width, height);\n}\n\nvoid _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n UNUSED, int d UNUSED)\n{\n    int width = window->null.width;\n    int height = window->null.height;\n    applySizeLimits(window, &width, &height);\n    _glfwPlatformSetWindowSize(window, width, height);\n}\n\nvoid _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window UNUSED, int widthincr UNUSED, int heightincr UNUSED)\n{\n}\n\nvoid _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)\n{\n    if (width)\n        *width = window->null.width;\n    if (height)\n        *height = window->null.height;\n}\n\nvoid _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,\n                                     int* left, int* top,\n                                     int* right, int* bottom)\n{\n    if (window->null.decorated && !window->monitor)\n    {\n        if (left)\n            *left = 1;\n        if (top)\n            *top = 10;\n        if (right)\n            *right = 1;\n        if (bottom)\n            *bottom = 1;\n    }\n    else\n    {\n        if (left)\n            *left = 0;\n        if (top)\n            *top = 0;\n        if (right)\n            *right = 0;\n        if (bottom)\n            *bottom = 0;\n    }\n}\n\nvoid _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,\n                                        float* xscale, float* yscale)\n{\n    if (xscale)\n        *xscale = 1.f;\n    if (yscale)\n        *yscale = 1.f;\n}\n\nmonotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)\n{\n    return ms_to_monotonic_t(500ll);\n}\n\nvoid _glfwPlatformIconifyWindow(_GLFWwindow* window)\n{\n    if (_glfw.null.focusedWindow == window)\n    {\n        _glfw.null.focusedWindow = NULL;\n        _glfwInputWindowFocus(window, false);\n    }\n\n    if (!window->null.iconified)\n    {\n        window->null.iconified = true;\n        _glfwInputWindowIconify(window, true);\n\n        if (window->monitor)\n            releaseMonitor(window);\n    }\n}\n\nvoid _glfwPlatformRestoreWindow(_GLFWwindow* window)\n{\n    if (window->null.iconified)\n    {\n        window->null.iconified = false;\n        _glfwInputWindowIconify(window, false);\n\n        if (window->monitor)\n            acquireMonitor(window);\n    }\n    else if (window->null.maximized)\n    {\n        window->null.maximized = false;\n        _glfwInputWindowMaximize(window, false);\n    }\n}\n\nvoid _glfwPlatformMaximizeWindow(_GLFWwindow* window)\n{\n    if (!window->null.maximized)\n    {\n        window->null.maximized = true;\n        _glfwInputWindowMaximize(window, true);\n    }\n}\n\nint _glfwPlatformWindowMaximized(_GLFWwindow* window)\n{\n    return window->null.maximized;\n}\n\nint _glfwPlatformWindowHovered(_GLFWwindow* window)\n{\n    return _glfw.null.xcursor >= window->null.xpos &&\n           _glfw.null.ycursor >= window->null.ypos &&\n           _glfw.null.xcursor <= window->null.xpos + window->null.width - 1 &&\n           _glfw.null.ycursor <= window->null.ypos + window->null.height - 1;\n}\n\nint _glfwPlatformFramebufferTransparent(_GLFWwindow* window)\n{\n    return window->null.transparent;\n}\n\nvoid _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled)\n{\n    window->null.resizable = enabled;\n}\n\nvoid _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)\n{\n    window->null.decorated = enabled;\n}\n\nvoid _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)\n{\n    window->null.floating = enabled;\n}\n\nvoid _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window UNUSED, bool enabled UNUSED)\n{\n}\n\nfloat _glfwPlatformGetWindowOpacity(_GLFWwindow* window)\n{\n    return window->null.opacity;\n}\n\nvoid _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)\n{\n    window->null.opacity = opacity;\n}\n\nvoid _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED)\n{\n}\n\nbool _glfwPlatformRawMouseMotionSupported(void)\n{\n    return true;\n}\n\nvoid _glfwPlatformShowWindow(_GLFWwindow* window)\n{\n    window->null.visible = true;\n}\n\nvoid _glfwPlatformRequestWindowAttention(_GLFWwindow* window UNUSED)\n{\n}\n\nint _glfwPlatformWindowBell(_GLFWwindow* window UNUSED)\n{\n    return false;\n}\n\nvoid _glfwPlatformHideWindow(_GLFWwindow* window)\n{\n    if (_glfw.null.focusedWindow == window)\n    {\n        _glfw.null.focusedWindow = NULL;\n        _glfwInputWindowFocus(window, false);\n    }\n\n    window->null.visible = false;\n}\n\nvoid _glfwPlatformFocusWindow(_GLFWwindow* window)\n{\n    if (_glfw.null.focusedWindow == window)\n        return;\n\n    if (!window->null.visible)\n        return;\n\n    _GLFWwindow* previous = _glfw.null.focusedWindow;\n    _glfw.null.focusedWindow = window;\n\n    if (previous)\n    {\n        _glfwInputWindowFocus(previous, false);\n        if (previous->monitor && previous->autoIconify)\n            _glfwPlatformIconifyWindow(previous);\n    }\n\n    _glfwInputWindowFocus(window, true);\n}\n\nint _glfwPlatformWindowFocused(_GLFWwindow* window)\n{\n    return _glfw.null.focusedWindow == window;\n}\n\nint _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)\n{\n    return false;\n}\n\nint _glfwPlatformWindowIconified(_GLFWwindow* window)\n{\n    return window->null.iconified;\n}\n\nint _glfwPlatformWindowVisible(_GLFWwindow* window)\n{\n    return window->null.visible;\n}\n\nvoid _glfwPlatformPollEvents(void)\n{\n}\n\nvoid _glfwPlatformWaitEvents(void)\n{\n}\n\nvoid _glfwPlatformWaitEventsTimeout(monotonic_t timeout UNUSED)\n{\n}\n\nvoid _glfwPlatformPostEmptyEvent(void)\n{\n}\n\nvoid _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)\n{\n    if (xpos)\n        *xpos = _glfw.null.xcursor - window->null.xpos;\n    if (ypos)\n        *ypos = _glfw.null.ycursor - window->null.ypos;\n}\n\nvoid _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)\n{\n    _glfw.null.xcursor = window->null.xpos + (int) x;\n    _glfw.null.ycursor = window->null.ypos + (int) y;\n}\n\nvoid _glfwPlatformSetCursorMode(_GLFWwindow* window UNUSED, int mode UNUSED)\n{\n}\n\nint _glfwPlatformCreateCursor(_GLFWcursor* cursor UNUSED,\n                              const GLFWimage* image UNUSED,\n                              int xhot UNUSED, int yhot UNUSED, int count UNUSED)\n{\n    return true;\n}\n\nint _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor UNUSED, int shape UNUSED)\n{\n    return true;\n}\n\nvoid _glfwPlatformDestroyCursor(_GLFWcursor* cursor UNUSED)\n{\n}\n\nvoid _glfwPlatformSetCursor(_GLFWwindow* window UNUSED, _GLFWcursor* cursor UNUSED)\n{\n}\n\nvoid _glfwPlatformCancelDrag(_GLFWwindow* window UNUSED)\n{\n    // No-op for null platform\n}\nint\n_glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {\n    (void)window; (void)thumbnail;\n    return ENOTSUP;\n}\nvoid _glfwPlatformFreeDragSourceData(void) {}\nint _glfwPlatformDragDataReady(const char *mime_type) { (void) mime_type; return 0; }\nint _glfwPlatformChangeDragImage(const GLFWimage *thumbnail) { (void)thumbnail; return 0; }\n\nconst char** _glfwPlatformGetDropMimeTypes(GLFWDropData* drop UNUSED, int* count)\n{\n    if (count) *count = 0;\n    return NULL;\n}\n\nssize_t _glfwPlatformReadDropData(GLFWDropData* drop UNUSED, const char* mime UNUSED,\n                                   void* buffer UNUSED, size_t capacity UNUSED,\n                                   monotonic_t timeout UNUSED)\n{\n    return -ENOENT;\n}\n\nvoid _glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation UNUSED, bool success UNUSED)\n{\n    if (!drop) return;\n    // Free the heap-allocated drop data structure\n    free(drop);\n}\n\nvoid _glfwPlatformSetClipboardString(const char* string)\n{\n    char* copy = _glfw_strdup(string);\n    free(_glfw.null.clipboardString);\n    _glfw.null.clipboardString = copy;\n}\n\nconst char* _glfwPlatformGetClipboardString(void)\n{\n    return _glfw.null.clipboardString;\n}\n\nconst char* _glfwPlatformGetNativeKeyName(int native_key)\n{\n    switch (scancode)\n    {\n        case GLFW_KEY_APOSTROPHE:\n            return \"'\";\n        case GLFW_KEY_COMMA:\n            return \",\";\n        case GLFW_KEY_MINUS:\n        case GLFW_KEY_KP_SUBTRACT:\n            return \"-\";\n        case GLFW_KEY_PERIOD:\n        case GLFW_KEY_KP_DECIMAL:\n            return \".\";\n        case GLFW_KEY_SLASH:\n        case GLFW_KEY_KP_DIVIDE:\n            return \"/\";\n        case GLFW_KEY_SEMICOLON:\n            return \";\";\n        case GLFW_KEY_EQUAL:\n        case GLFW_KEY_KP_EQUAL:\n            return \"=\";\n        case GLFW_KEY_LEFT_BRACKET:\n            return \"[\";\n        case GLFW_KEY_RIGHT_BRACKET:\n            return \"]\";\n        case GLFW_KEY_KP_MULTIPLY:\n            return \"*\";\n        case GLFW_KEY_KP_ADD:\n            return \"+\";\n        case GLFW_KEY_BACKSLASH:\n        case GLFW_KEY_WORLD_1:\n        case GLFW_KEY_WORLD_2:\n            return \"\\\\\";\n        case GLFW_KEY_0:\n        case GLFW_KEY_KP_0:\n            return \"0\";\n        case GLFW_KEY_1:\n        case GLFW_KEY_KP_1:\n            return \"1\";\n        case GLFW_KEY_2:\n        case GLFW_KEY_KP_2:\n            return \"2\";\n        case GLFW_KEY_3:\n        case GLFW_KEY_KP_3:\n            return \"3\";\n        case GLFW_KEY_4:\n        case GLFW_KEY_KP_4:\n            return \"4\";\n        case GLFW_KEY_5:\n        case GLFW_KEY_KP_5:\n            return \"5\";\n        case GLFW_KEY_6:\n        case GLFW_KEY_KP_6:\n            return \"6\";\n        case GLFW_KEY_7:\n        case GLFW_KEY_KP_7:\n            return \"7\";\n        case GLFW_KEY_8:\n        case GLFW_KEY_KP_8:\n            return \"8\";\n        case GLFW_KEY_9:\n        case GLFW_KEY_KP_9:\n            return \"9\";\n        case GLFW_KEY_A:\n            return \"a\";\n        case GLFW_KEY_B:\n            return \"b\";\n        case GLFW_KEY_C:\n            return \"c\";\n        case GLFW_KEY_D:\n            return \"d\";\n        case GLFW_KEY_E:\n            return \"e\";\n        case GLFW_KEY_F:\n            return \"f\";\n        case GLFW_KEY_G:\n            return \"g\";\n        case GLFW_KEY_H:\n            return \"h\";\n        case GLFW_KEY_I:\n            return \"i\";\n        case GLFW_KEY_J:\n            return \"j\";\n        case GLFW_KEY_K:\n            return \"k\";\n        case GLFW_KEY_L:\n            return \"l\";\n        case GLFW_KEY_M:\n            return \"m\";\n        case GLFW_KEY_N:\n            return \"n\";\n        case GLFW_KEY_O:\n            return \"o\";\n        case GLFW_KEY_P:\n            return \"p\";\n        case GLFW_KEY_Q:\n            return \"q\";\n        case GLFW_KEY_R:\n            return \"r\";\n        case GLFW_KEY_S:\n            return \"s\";\n        case GLFW_KEY_T:\n            return \"t\";\n        case GLFW_KEY_U:\n            return \"u\";\n        case GLFW_KEY_V:\n            return \"v\";\n        case GLFW_KEY_W:\n            return \"w\";\n        case GLFW_KEY_X:\n            return \"x\";\n        case GLFW_KEY_Y:\n            return \"y\";\n        case GLFW_KEY_Z:\n            return \"z\";\n    }\n\n    return NULL;\n}\n\nint _glfwPlatformGetNativeKeyForKey(int key)\n{\n    return key;\n}\n\nvoid _glfwPlatformGetRequiredInstanceExtensions(char** extensions UNUSED)\n{\n}\n\nint _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance UNUSED,\n                                                      VkPhysicalDevice device UNUSED,\n                                                      uint32_t queuefamily UNUSED)\n{\n    return false;\n}\n\nVkResult _glfwPlatformCreateWindowSurface(VkInstance instance UNUSED,\n                                          _GLFWwindow* window UNUSED,\n                                          const VkAllocationCallbacks* allocator UNUSED,\n                                          VkSurfaceKHR* surface UNUSED)\n{\n    // This seems like the most appropriate error to return here\n    return VK_ERROR_EXTENSION_NOT_PRESENT;\n}\n\n"
  },
  {
    "path": "glfw/osmesa_context.c",
    "content": "//========================================================================\n// GLFW 3.4 OSMesa - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2016 Google Inc.\n// Copyright (c) 2016-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// Please use C89 style variable declarations in this file because VS 2010\n//========================================================================\n\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n#include \"internal.h\"\n\n\nstatic void makeContextCurrentOSMesa(_GLFWwindow* window)\n{\n    if (window)\n    {\n        int width, height;\n        _glfwPlatformGetFramebufferSize(window, &width, &height);\n\n        // Check to see if we need to allocate a new buffer\n        if ((window->context.osmesa.buffer == NULL) ||\n            (width != window->context.osmesa.width) ||\n            (height != window->context.osmesa.height))\n        {\n            free(window->context.osmesa.buffer);\n\n            // Allocate the new buffer (width * height * 8-bit RGBA)\n            window->context.osmesa.buffer = calloc(4, (size_t) width * height);\n            window->context.osmesa.width  = width;\n            window->context.osmesa.height = height;\n        }\n\n        if (!OSMesaMakeCurrent(window->context.osmesa.handle,\n                               window->context.osmesa.buffer,\n                               GL_UNSIGNED_BYTE,\n                               width, height))\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"OSMesa: Failed to make context current\");\n            return;\n        }\n    }\n\n    _glfwPlatformSetTls(&_glfw.contextSlot, window);\n}\n\nstatic GLFWglproc getProcAddressOSMesa(const char* procname)\n{\n    return (GLFWglproc) OSMesaGetProcAddress(procname);\n}\n\nstatic void destroyContextOSMesa(_GLFWwindow* window)\n{\n    if (window->context.osmesa.handle)\n    {\n        OSMesaDestroyContext(window->context.osmesa.handle);\n        window->context.osmesa.handle = NULL;\n    }\n\n    if (window->context.osmesa.buffer)\n    {\n        free(window->context.osmesa.buffer);\n        window->context.osmesa.width = 0;\n        window->context.osmesa.height = 0;\n    }\n}\n\nstatic void swapBuffersOSMesa(_GLFWwindow* window UNUSED)\n{\n    // No double buffering on OSMesa\n}\n\nstatic void swapIntervalOSMesa(int interval UNUSED)\n{\n    // No swap interval on OSMesa\n}\n\nstatic int extensionSupportedOSMesa(const char* extension UNUSED)\n{\n    // OSMesa does not have extensions\n    return false;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nbool _glfwInitOSMesa(void)\n{\n    int i;\n    const char* sonames[] =\n    {\n#if defined(_GLFW_OSMESA_LIBRARY)\n        _GLFW_OSMESA_LIBRARY,\n#elif defined(_WIN32)\n        \"libOSMesa.dll\",\n        \"OSMesa.dll\",\n#elif defined(__APPLE__)\n        \"libOSMesa.8.dylib\",\n#elif defined(__CYGWIN__)\n        \"libOSMesa-8.so\",\n#else\n        \"libOSMesa.so.8\",\n        \"libOSMesa.so.6\",\n#endif\n        NULL\n    };\n\n    if (_glfw.osmesa.handle)\n        return true;\n\n    for (i = 0;  sonames[i];  i++)\n    {\n        _glfw.osmesa.handle = _glfw_dlopen(sonames[i]);\n        if (_glfw.osmesa.handle)\n            break;\n    }\n\n    if (!_glfw.osmesa.handle)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE, \"OSMesa: Library not found\");\n        return false;\n    }\n\n    glfw_dlsym(_glfw.osmesa.CreateContextExt, _glfw.osmesa.handle, \"OSMesaCreateContextExt\");\n    glfw_dlsym(_glfw.osmesa.CreateContextAttribs, _glfw.osmesa.handle, \"OSMesaCreateContextAttribs\");\n    glfw_dlsym(_glfw.osmesa.DestroyContext, _glfw.osmesa.handle, \"OSMesaDestroyContext\");\n    glfw_dlsym(_glfw.osmesa.MakeCurrent, _glfw.osmesa.handle, \"OSMesaMakeCurrent\");\n    glfw_dlsym(_glfw.osmesa.GetColorBuffer, _glfw.osmesa.handle, \"OSMesaGetColorBuffer\");\n    glfw_dlsym(_glfw.osmesa.GetDepthBuffer, _glfw.osmesa.handle, \"OSMesaGetDepthBuffer\");\n    glfw_dlsym(_glfw.osmesa.GetProcAddress, _glfw.osmesa.handle, \"OSMesaGetProcAddress\");\n\n    if (!_glfw.osmesa.CreateContextExt ||\n        !_glfw.osmesa.DestroyContext ||\n        !_glfw.osmesa.MakeCurrent ||\n        !_glfw.osmesa.GetColorBuffer ||\n        !_glfw.osmesa.GetDepthBuffer ||\n        !_glfw.osmesa.GetProcAddress)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"OSMesa: Failed to load required entry points\");\n\n        _glfwTerminateOSMesa();\n        return false;\n    }\n\n    return true;\n}\n\nvoid _glfwTerminateOSMesa(void)\n{\n    if (_glfw.osmesa.handle)\n    {\n        _glfw_dlclose(_glfw.osmesa.handle);\n        _glfw.osmesa.handle = NULL;\n    }\n}\n\n#define setAttrib(a, v) \\\n{ \\\n    assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \\\n    attribs[index++] = a; \\\n    attribs[index++] = v; \\\n}\n\nbool _glfwCreateContextOSMesa(_GLFWwindow* window,\n                                  const _GLFWctxconfig* ctxconfig,\n                                  const _GLFWfbconfig* fbconfig)\n{\n    OSMesaContext share = NULL;\n    const int accumBits = fbconfig->accumRedBits +\n                          fbconfig->accumGreenBits +\n                          fbconfig->accumBlueBits +\n                          fbconfig->accumAlphaBits;\n\n    if (ctxconfig->client == GLFW_OPENGL_ES_API)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"OSMesa: OpenGL ES is not available on OSMesa\");\n        return false;\n    }\n\n    if (ctxconfig->share)\n        share = ctxconfig->share->context.osmesa.handle;\n\n    if (OSMesaCreateContextAttribs)\n    {\n        int index = 0, attribs[40];\n\n        setAttrib(OSMESA_FORMAT, OSMESA_RGBA);\n        setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits);\n        setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits);\n        setAttrib(OSMESA_ACCUM_BITS, accumBits);\n\n        if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)\n        {\n            setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE);\n        }\n        else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)\n        {\n            setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE);\n        }\n\n        if (ctxconfig->major != 1 || ctxconfig->minor != 0)\n        {\n            setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major);\n            setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor);\n        }\n\n        if (ctxconfig->forward)\n        {\n            _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                            \"OSMesa: Forward-compatible contexts not supported\");\n            return false;\n        }\n\n        setAttrib(0, 0);\n\n        window->context.osmesa.handle =\n            OSMesaCreateContextAttribs(attribs, share);\n    }\n    else\n    {\n        if (ctxconfig->profile)\n        {\n            _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                            \"OSMesa: OpenGL profiles unavailable\");\n            return false;\n        }\n\n        window->context.osmesa.handle =\n            OSMesaCreateContextExt(OSMESA_RGBA,\n                                   fbconfig->depthBits,\n                                   fbconfig->stencilBits,\n                                   accumBits,\n                                   share);\n    }\n\n    if (window->context.osmesa.handle == NULL)\n    {\n        _glfwInputError(GLFW_VERSION_UNAVAILABLE,\n                        \"OSMesa: Failed to create context\");\n        return false;\n    }\n\n    window->context.makeCurrent = makeContextCurrentOSMesa;\n    window->context.swapBuffers = swapBuffersOSMesa;\n    window->context.swapInterval = swapIntervalOSMesa;\n    window->context.extensionSupported = extensionSupportedOSMesa;\n    window->context.getProcAddress = getProcAddressOSMesa;\n    window->context.destroy = destroyContextOSMesa;\n\n    return true;\n}\n\n#undef setAttrib\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width,\n                                     int* height, int* format, void** buffer)\n{\n    void* mesaBuffer;\n    GLint mesaWidth, mesaHeight, mesaFormat;\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    if (!OSMesaGetColorBuffer(window->context.osmesa.handle,\n                              &mesaWidth, &mesaHeight,\n                              &mesaFormat, &mesaBuffer))\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"OSMesa: Failed to retrieve color buffer\");\n        return false;\n    }\n\n    if (width)\n        *width = mesaWidth;\n    if (height)\n        *height = mesaHeight;\n    if (format)\n        *format = mesaFormat;\n    if (buffer)\n        *buffer = mesaBuffer;\n\n    return true;\n}\n\nGLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle,\n                                     int* width, int* height,\n                                     int* bytesPerValue,\n                                     void** buffer)\n{\n    void* mesaBuffer;\n    GLint mesaWidth, mesaHeight, mesaBytes;\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    if (!OSMesaGetDepthBuffer(window->context.osmesa.handle,\n                              &mesaWidth, &mesaHeight,\n                              &mesaBytes, &mesaBuffer))\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"OSMesa: Failed to retrieve depth buffer\");\n        return false;\n    }\n\n    if (width)\n        *width = mesaWidth;\n    if (height)\n        *height = mesaHeight;\n    if (bytesPerValue)\n        *bytesPerValue = mesaBytes;\n    if (buffer)\n        *buffer = mesaBuffer;\n\n    return true;\n}\n\nGLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (window->context.client == GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);\n        return NULL;\n    }\n\n    return window->context.osmesa.handle;\n}\n"
  },
  {
    "path": "glfw/osmesa_context.h",
    "content": "//========================================================================\n// GLFW 3.4 OSMesa - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2016 Google Inc.\n// Copyright (c) 2016-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#define OSMESA_RGBA 0x1908\n#define OSMESA_FORMAT 0x22\n#define OSMESA_DEPTH_BITS 0x30\n#define OSMESA_STENCIL_BITS 0x31\n#define OSMESA_ACCUM_BITS 0x32\n#define OSMESA_PROFILE 0x33\n#define OSMESA_CORE_PROFILE 0x34\n#define OSMESA_COMPAT_PROFILE 0x35\n#define OSMESA_CONTEXT_MAJOR_VERSION 0x36\n#define OSMESA_CONTEXT_MINOR_VERSION 0x37\n\ntypedef void* OSMesaContext;\ntypedef void (*OSMESAproc)(void);\n\ntypedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext);\ntypedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext);\ntypedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext);\ntypedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int);\ntypedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**);\ntypedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**);\ntypedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*);\n#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt\n#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs\n#define OSMesaDestroyContext _glfw.osmesa.DestroyContext\n#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent\n#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer\n#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer\n#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress\n\n// OSMesa-specific per-context data\n//\ntypedef struct _GLFWcontextOSMesa\n{\n    OSMesaContext       handle;\n    int                 width;\n    int                 height;\n    void*               buffer;\n\n} _GLFWcontextOSMesa;\n\n// OSMesa-specific global data\n//\ntypedef struct _GLFWlibraryOSMesa\n{\n    void*           handle;\n\n    PFN_OSMesaCreateContextExt      CreateContextExt;\n    PFN_OSMesaCreateContextAttribs  CreateContextAttribs;\n    PFN_OSMesaDestroyContext        DestroyContext;\n    PFN_OSMesaMakeCurrent           MakeCurrent;\n    PFN_OSMesaGetColorBuffer        GetColorBuffer;\n    PFN_OSMesaGetDepthBuffer        GetDepthBuffer;\n    PFN_OSMesaGetProcAddress        GetProcAddress;\n\n} _GLFWlibraryOSMesa;\n\n\nbool _glfwInitOSMesa(void);\nvoid _glfwTerminateOSMesa(void);\nbool _glfwCreateContextOSMesa(_GLFWwindow* window,\n                                  const _GLFWctxconfig* ctxconfig,\n                                  const _GLFWfbconfig* fbconfig);\n\n"
  },
  {
    "path": "glfw/posix_thread.c",
    "content": "//========================================================================\n// GLFW 3.4 POSIX - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n#include <assert.h>\n#include <string.h>\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nbool _glfwPlatformCreateTls(_GLFWtls* tls)\n{\n    assert(tls->posix.allocated == false);\n\n    if (pthread_key_create(&tls->posix.key, NULL) != 0)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"POSIX: Failed to create context TLS\");\n        return false;\n    }\n\n    tls->posix.allocated = true;\n    return true;\n}\n\nvoid _glfwPlatformDestroyTls(_GLFWtls* tls)\n{\n    if (tls->posix.allocated)\n        pthread_key_delete(tls->posix.key);\n    memset(tls, 0, sizeof(_GLFWtls));\n}\n\nvoid* _glfwPlatformGetTls(_GLFWtls* tls)\n{\n    assert(tls->posix.allocated == true);\n    return pthread_getspecific(tls->posix.key);\n}\n\nvoid _glfwPlatformSetTls(_GLFWtls* tls, void* value)\n{\n    assert(tls->posix.allocated == true);\n    pthread_setspecific(tls->posix.key, value);\n}\n\nbool _glfwPlatformCreateMutex(_GLFWmutex* mutex)\n{\n    assert(mutex->posix.allocated == false);\n\n    if (pthread_mutex_init(&mutex->posix.handle, NULL) != 0)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"POSIX: Failed to create mutex\");\n        return false;\n    }\n\n    return mutex->posix.allocated = true;\n}\n\nvoid _glfwPlatformDestroyMutex(_GLFWmutex* mutex)\n{\n    if (mutex->posix.allocated)\n        pthread_mutex_destroy(&mutex->posix.handle);\n    memset(mutex, 0, sizeof(_GLFWmutex));\n}\n\nvoid _glfwPlatformLockMutex(_GLFWmutex* mutex)\n{\n    assert(mutex->posix.allocated == true);\n    pthread_mutex_lock(&mutex->posix.handle);\n}\n\nvoid _glfwPlatformUnlockMutex(_GLFWmutex* mutex)\n{\n    assert(mutex->posix.allocated == true);\n    pthread_mutex_unlock(&mutex->posix.handle);\n}\n\n"
  },
  {
    "path": "glfw/posix_thread.h",
    "content": "//========================================================================\n// GLFW 3.4 POSIX - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#include <pthread.h>\n\n#define _GLFW_PLATFORM_TLS_STATE    _GLFWtlsPOSIX   posix\n#define _GLFW_PLATFORM_MUTEX_STATE  _GLFWmutexPOSIX posix\n\n\n// POSIX-specific thread local storage data\n//\ntypedef struct _GLFWtlsPOSIX\n{\n    bool            allocated;\n    pthread_key_t   key;\n\n} _GLFWtlsPOSIX;\n\n// POSIX-specific mutex data\n//\ntypedef struct _GLFWmutexPOSIX\n{\n    bool            allocated;\n    pthread_mutex_t handle;\n\n} _GLFWmutexPOSIX;\n\n"
  },
  {
    "path": "glfw/source-info.json",
    "content": "{\n  \"cocoa\": {\n    \"headers\": [\n      \"cocoa_platform.h\",\n      \"cocoa_joystick.h\",\n      \"posix_thread.h\",\n      \"nsgl_context.h\",\n      \"egl_context.h\",\n      \"osmesa_context.h\"\n    ],\n    \"sources\": [\n      \"cocoa_init.m\",\n      \"cocoa_joystick.m\",\n      \"cocoa_monitor.m\",\n      \"cocoa_window.m\",\n      \"cocoa_displaylink.m\",\n      \"posix_thread.c\",\n      \"nsgl_context.m\",\n      \"egl_context.c\",\n      \"osmesa_context.c\"\n    ]\n  },\n  \"common\": {\n    \"headers\": [\n      \"internal.h\",\n      \"mappings.h\"\n    ],\n    \"sources\": [\n      \"context.c\",\n      \"init.c\",\n      \"input.c\",\n      \"monitor.c\",\n      \"vulkan.c\",\n      \"monotonic.c\",\n      \"window.c\"\n    ]\n  },\n  \"osmesa\": {\n    \"headers\": [\n      \"null_platform.h\",\n      \"null_joystick.h\",\n      \"posix_thread.h\",\n      \"osmesa_context.h\"\n    ],\n    \"sources\": [\n      \"null_init.c\",\n      \"null_monitor.c\",\n      \"null_window.c\",\n      \"null_joystick.c\",\n      \"posix_thread.c\",\n      \"osmesa_context.c\"\n    ]\n  },\n  \"wayland\": {\n    \"headers\": [\n      \"wl_platform.h\",\n      \"posix_thread.h\",\n      \"wl_cursors.h\",\n      \"wl_text_input.h\",\n      \"xkb_glfw.h\",\n      \"dbus_glfw.h\",\n      \"ibus_glfw.h\",\n      \"backend_utils.h\",\n      \"egl_context.h\",\n      \"osmesa_context.h\",\n      \"linux_joystick.h\",\n      \"null_joystick.h\",\n      \"linux_notify.h\",\n      \"linux_desktop_settings.h\",\n      \"wl_client_side_decorations.h\",\n      \"main_loop.h\"\n    ],\n    \"protocols\": [\n      \"stable/xdg-shell/xdg-shell.xml\",\n      \"stable/viewporter/viewporter.xml\",\n      \"unstable/relative-pointer/relative-pointer-unstable-v1.xml\",\n      \"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml\",\n      \"unstable/xdg-decoration/xdg-decoration-unstable-v1.xml\",\n      \"unstable/primary-selection/primary-selection-unstable-v1.xml\",\n      \"unstable/text-input/text-input-unstable-v3.xml\",\n      \"staging/xdg-activation/xdg-activation-v1.xml\",\n      \"unstable/tablet/tablet-unstable-v2.xml\",\n      \"staging/cursor-shape/cursor-shape-v1.xml\",\n      \"staging/fractional-scale/fractional-scale-v1.xml\",\n      \"staging/single-pixel-buffer/single-pixel-buffer-v1.xml\",\n      \"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml\",\n      \"unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml\",\n      \"staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml\",\n      \"staging/xdg-system-bell/xdg-system-bell-v1.xml\",\n      \"staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml\",\n      \"staging/ext-background-effect/ext-background-effect-v1.xml\",\n      \"staging/xdg-toplevel-drag/xdg-toplevel-drag-v1.xml\",\n\n      \"kwin-blur-v1.xml\",\n      \"wlr-layer-shell-unstable-v1.xml\"\n    ],\n    \"sources\": [\n      \"wl_init.c\",\n      \"wl_monitor.c\",\n      \"wl_window.c\",\n      \"wl_cursors.c\",\n      \"wl_text_input.c\",\n      \"wl_client_side_decorations.c\",\n      \"posix_thread.c\",\n      \"xkb_glfw.c\",\n      \"dbus_glfw.c\",\n      \"ibus_glfw.c\",\n      \"egl_context.c\",\n      \"osmesa_context.c\",\n      \"backend_utils.c\",\n      \"linux_joystick.c\",\n      \"linux_desktop_settings.c\",\n      \"null_joystick.c\",\n      \"momentum-scroll.c\",\n      \"linux_notify.c\"\n    ]\n  },\n  \"wayland_protocols\": [\n    1,\n    17\n  ],\n  \"win32\": {\n    \"headers\": [\n      \"win32_platform.h\",\n      \"win32_joystick.h\",\n      \"wgl_context.h\",\n      \"egl_context.h\",\n      \"osmesa_context.h\"\n    ],\n    \"sources\": [\n      \"win32_init.c\",\n      \"win32_joystick.c\",\n      \"win32_monitor.c\",\n      \"win32_time.c\",\n      \"win32_thread.c\",\n      \"win32_window.c\",\n      \"wgl_context.c\",\n      \"egl_context.c\",\n      \"osmesa_context.c\"\n    ]\n  },\n  \"x11\": {\n    \"headers\": [\n      \"x11_platform.h\",\n      \"xkb_glfw.h\",\n      \"dbus_glfw.h\",\n      \"ibus_glfw.h\",\n      \"backend_utils.h\",\n      \"posix_thread.h\",\n      \"glx_context.h\",\n      \"egl_context.h\",\n      \"osmesa_context.h\",\n      \"linux_joystick.h\",\n      \"null_joystick.h\",\n      \"linux_notify.h\",\n      \"linux_desktop_settings.h\",\n      \"main_loop.h\"\n    ],\n    \"sources\": [\n      \"x11_init.c\",\n      \"x11_monitor.c\",\n      \"x11_window.c\",\n      \"xkb_glfw.c\",\n      \"dbus_glfw.c\",\n      \"ibus_glfw.c\",\n      \"posix_thread.c\",\n      \"glx_context.c\",\n      \"egl_context.c\",\n      \"osmesa_context.c\",\n      \"backend_utils.c\",\n      \"linux_joystick.c\",\n      \"null_joystick.c\",\n      \"linux_desktop_settings.c\",\n      \"momentum-scroll.c\",\n      \"linux_notify.c\"\n    ]\n  }\n}\n"
  },
  {
    "path": "glfw/vulkan.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// Please use C89 style variable declarations in this file because VS 2010\n//========================================================================\n\n#include \"internal.h\"\n\n#include <assert.h>\n#include <string.h>\n#include <stdlib.h>\n\n#define _GLFW_FIND_LOADER    1\n#define _GLFW_REQUIRE_LOADER 2\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nbool _glfwInitVulkan(int mode)\n{\n    VkResult err;\n    VkExtensionProperties* ep;\n    uint32_t i, count;\n\n    if (_glfw.vk.available)\n        return true;\n\n#if !defined(_GLFW_VULKAN_STATIC)\n#if defined(_GLFW_VULKAN_LIBRARY)\n    _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY);\n#elif defined(_GLFW_WIN32)\n    _glfw.vk.handle = _glfw_dlopen(\"vulkan-1.dll\");\n#elif defined(_GLFW_COCOA)\n    _glfw.vk.handle = _glfw_dlopen(\"libvulkan.1.dylib\");\n    if (!_glfw.vk.handle)\n        _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS();\n#else\n    _glfw.vk.handle = _glfw_dlopen(\"libvulkan.so.1\");\n#endif\n    if (!_glfw.vk.handle)\n    {\n        if (mode == _GLFW_REQUIRE_LOADER)\n            _glfwInputError(GLFW_API_UNAVAILABLE, \"Vulkan: Loader not found\");\n\n        return false;\n    }\n\n    *(void **) &_glfw.vk.GetInstanceProcAddr =\n        _glfw_dlsym(_glfw.vk.handle, \"vkGetInstanceProcAddr\");\n    if (!_glfw.vk.GetInstanceProcAddr)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"Vulkan: Loader does not export vkGetInstanceProcAddr\");\n\n        _glfwTerminateVulkan();\n        return false;\n    }\n\n    _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties)\n        vkGetInstanceProcAddr(NULL, \"vkEnumerateInstanceExtensionProperties\");\n    if (!_glfw.vk.EnumerateInstanceExtensionProperties)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties\");\n\n        _glfwTerminateVulkan();\n        return false;\n    }\n#endif // _GLFW_VULKAN_STATIC\n\n    err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL);\n    if (err)\n    {\n        // NOTE: This happens on systems with a loader but without any Vulkan ICD\n        if (mode == _GLFW_REQUIRE_LOADER)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"Vulkan: Failed to query instance extension count: %s\",\n                            _glfwGetVulkanResultString(err));\n        }\n\n        _glfwTerminateVulkan();\n        return false;\n    }\n\n    ep = calloc(count, sizeof(VkExtensionProperties));\n\n    err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep);\n    if (err)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"Vulkan: Failed to query instance extensions: %s\",\n                        _glfwGetVulkanResultString(err));\n\n        free(ep);\n        _glfwTerminateVulkan();\n        return false;\n    }\n\n    for (i = 0;  i < count;  i++)\n    {\n        if (strcmp(ep[i].extensionName, \"VK_KHR_surface\") == 0)\n            _glfw.vk.KHR_surface = true;\n#if defined(_GLFW_WIN32)\n        else if (strcmp(ep[i].extensionName, \"VK_KHR_win32_surface\") == 0)\n            _glfw.vk.KHR_win32_surface = true;\n#elif defined(_GLFW_COCOA)\n        else if (strcmp(ep[i].extensionName, \"VK_MVK_macos_surface\") == 0)\n            _glfw.vk.MVK_macos_surface = true;\n        else if (strcmp(ep[i].extensionName, \"VK_EXT_metal_surface\") == 0)\n            _glfw.vk.EXT_metal_surface = true;\n#elif defined(_GLFW_X11)\n        else if (strcmp(ep[i].extensionName, \"VK_KHR_xlib_surface\") == 0)\n            _glfw.vk.KHR_xlib_surface = true;\n        else if (strcmp(ep[i].extensionName, \"VK_KHR_xcb_surface\") == 0)\n            _glfw.vk.KHR_xcb_surface = true;\n#elif defined(_GLFW_WAYLAND)\n        else if (strcmp(ep[i].extensionName, \"VK_KHR_wayland_surface\") == 0)\n            _glfw.vk.KHR_wayland_surface = true;\n#endif\n    }\n\n    free(ep);\n\n    _glfw.vk.available = true;\n\n    _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions);\n\n    return true;\n}\n\nvoid _glfwTerminateVulkan(void)\n{\n#if !defined(_GLFW_VULKAN_STATIC)\n    if (_glfw.vk.handle)\n        _glfw_dlclose(_glfw.vk.handle);\n#endif\n}\n\nconst char* _glfwGetVulkanResultString(VkResult result)\n{\n    switch (result)\n    {\n        case VK_SUCCESS:\n            return \"Success\";\n        case VK_NOT_READY:\n            return \"A fence or query has not yet completed\";\n        case VK_TIMEOUT:\n            return \"A wait operation has not completed in the specified time\";\n        case VK_EVENT_SET:\n            return \"An event is signaled\";\n        case VK_EVENT_RESET:\n            return \"An event is unsignaled\";\n        case VK_INCOMPLETE:\n            return \"A return array was too small for the result\";\n        case VK_ERROR_OUT_OF_HOST_MEMORY:\n            return \"A host memory allocation has failed\";\n        case VK_ERROR_OUT_OF_DEVICE_MEMORY:\n            return \"A device memory allocation has failed\";\n        case VK_ERROR_INITIALIZATION_FAILED:\n            return \"Initialization of an object could not be completed for implementation-specific reasons\";\n        case VK_ERROR_DEVICE_LOST:\n            return \"The logical or physical device has been lost\";\n        case VK_ERROR_MEMORY_MAP_FAILED:\n            return \"Mapping of a memory object has failed\";\n        case VK_ERROR_LAYER_NOT_PRESENT:\n            return \"A requested layer is not present or could not be loaded\";\n        case VK_ERROR_EXTENSION_NOT_PRESENT:\n            return \"A requested extension is not supported\";\n        case VK_ERROR_FEATURE_NOT_PRESENT:\n            return \"A requested feature is not supported\";\n        case VK_ERROR_INCOMPATIBLE_DRIVER:\n            return \"The requested version of Vulkan is not supported by the driver or is otherwise incompatible\";\n        case VK_ERROR_TOO_MANY_OBJECTS:\n            return \"Too many objects of the type have already been created\";\n        case VK_ERROR_FORMAT_NOT_SUPPORTED:\n            return \"A requested format is not supported on this device\";\n        case VK_ERROR_SURFACE_LOST_KHR:\n            return \"A surface is no longer available\";\n        case VK_SUBOPTIMAL_KHR:\n            return \"A swapchain no longer matches the surface properties exactly, but can still be used\";\n        case VK_ERROR_OUT_OF_DATE_KHR:\n            return \"A surface has changed in such a way that it is no longer compatible with the swapchain\";\n        case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:\n            return \"The display used by a swapchain does not use the same presentable image layout\";\n        case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:\n            return \"The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API\";\n        case VK_ERROR_VALIDATION_FAILED_EXT:\n            return \"A validation layer found an error\";\n        default:\n            return \"ERROR: UNKNOWN VULKAN ERROR\";\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW public API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI int glfwVulkanSupported(void)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n    return _glfwInitVulkan(_GLFW_FIND_LOADER);\n}\n\nGLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count)\n{\n    assert(count != NULL);\n\n    *count = 0;\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER))\n        return NULL;\n\n    if (!_glfw.vk.extensions[0])\n        return NULL;\n\n    *count = 2;\n    return (const char**) _glfw.vk.extensions;\n}\n\nGLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance,\n                                              const char* procname)\n{\n    GLFWvkproc proc;\n    assert(procname != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER))\n        return NULL;\n\n    proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname);\n#if defined(_GLFW_VULKAN_STATIC)\n    if (!proc)\n    {\n        if (strcmp(procname, \"vkGetInstanceProcAddr\") == 0)\n            return (GLFWvkproc) vkGetInstanceProcAddr;\n    }\n#else\n    if (!proc)\n        *(void **) &proc = _glfw_dlsym(_glfw.vk.handle, procname);\n#endif\n\n    return proc;\n}\n\nGLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance,\n                                                     VkPhysicalDevice device,\n                                                     uint32_t queuefamily)\n{\n    assert(instance != VK_NULL_HANDLE);\n    assert(device != VK_NULL_HANDLE);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER))\n        return false;\n\n    if (!_glfw.vk.extensions[0])\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"Vulkan: Window surface creation extensions not found\");\n        return false;\n    }\n\n    return _glfwPlatformGetPhysicalDevicePresentationSupport(instance,\n                                                             device,\n                                                             queuefamily);\n}\n\nGLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance,\n                                         GLFWwindow* handle,\n                                         const VkAllocationCallbacks* allocator,\n                                         VkSurfaceKHR* surface)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(instance != VK_NULL_HANDLE);\n    assert(window != NULL);\n    assert(surface != NULL);\n\n    *surface = VK_NULL_HANDLE;\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED);\n\n    if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER))\n        return VK_ERROR_INITIALIZATION_FAILED;\n\n    if (!_glfw.vk.extensions[0])\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"Vulkan: Window surface creation extensions not found\");\n        return VK_ERROR_EXTENSION_NOT_PRESENT;\n    }\n\n    if (window->context.client != GLFW_NO_API)\n    {\n        _glfwInputError(GLFW_INVALID_VALUE,\n                        \"Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API\");\n        return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;\n    }\n\n    return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface);\n}\n"
  },
  {
    "path": "glfw/window.c",
    "content": "//========================================================================\n// GLFW 3.4 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n// Copyright (c) 2012 Torsten Walluhn <tw@mad-cad.net>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// Please use C89 style variable declarations in this file because VS 2010\n//========================================================================\n\n#include \"internal.h\"\n#include \"../kitty/monotonic.h\"\n\n#include <assert.h>\n#include <string.h>\n#include <stdlib.h>\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                         GLFW event API                       //////\n//////////////////////////////////////////////////////////////////////////\n\n// Notifies shared code that a window has lost or received input focus\n//\nvoid _glfwInputWindowFocus(_GLFWwindow* window, bool focused)\n{\n    if (window->callbacks.focus)\n        window->callbacks.focus((GLFWwindow*) window, focused);\n\n    if (!focused)\n    {\n        _glfw.focusedWindowId = 0;\n\n        for (unsigned i = 0; i < arraysz(window->activated_keys); i++)\n        {\n            if (window->activated_keys[i].key > 0 && window->activated_keys[i].action == GLFW_PRESS)\n            {\n                const int native_key = _glfwPlatformGetNativeKeyForKey(window->activated_keys[i].key);\n                GLFWkeyevent ev = {.key = window->activated_keys[i].key, .native_key = native_key, .action = GLFW_RELEASE, .fake_event_on_focus_change = true};\n                _glfwInputKeyboard(window, &ev);\n            }\n        }\n\n        for (int button = 0;  button <= GLFW_MOUSE_BUTTON_LAST;  button++)\n        {\n            if (window->mouseButtons[button] == GLFW_PRESS)\n                _glfwInputMouseClick(window, button, GLFW_RELEASE, 0);\n        }\n    } else\n        _glfw.focusedWindowId = window->id;\n}\n\n_GLFWwindow* _glfwFocusedWindow(void) {\n    if (_glfw.focusedWindowId) {\n        _GLFWwindow *w = _glfw.windowListHead;\n        while (w) {\n            if (w->id == _glfw.focusedWindowId) return w;\n            w = w->next;\n        }\n    }\n    return NULL;\n}\n\n_GLFWwindow* _glfwWindowForId(GLFWid id) {\n    _GLFWwindow *w = _glfw.windowListHead;\n    while (w) {\n        if (w->id == id) return w;\n        w = w->next;\n    }\n    return NULL;\n}\n\n// Notifies shared code that a window's occlusion state has changed\n//\nvoid _glfwInputWindowOcclusion(_GLFWwindow* window, bool occluded)\n{\n    if (window->callbacks.occlusion)\n        window->callbacks.occlusion((GLFWwindow*) window, occluded);\n}\n\n// Notifies shared code that a window has moved\n// The position is specified in content area relative screen coordinates\n//\nvoid _glfwInputWindowPos(_GLFWwindow* window, int x, int y)\n{\n    if (window->callbacks.pos)\n        window->callbacks.pos((GLFWwindow*) window, x, y);\n}\n\n// Notifies shared code that a window has been resized\n// The size is specified in screen coordinates\n//\nvoid _glfwInputWindowSize(_GLFWwindow* window, int width, int height)\n{\n    if (window->callbacks.size)\n        window->callbacks.size((GLFWwindow*) window, width, height);\n}\n\n// Notifies shared code that a window has been iconified or restored\n//\nvoid _glfwInputWindowIconify(_GLFWwindow* window, bool iconified)\n{\n    if (window->callbacks.iconify)\n        window->callbacks.iconify((GLFWwindow*) window, iconified);\n}\n\n// Notifies shared code that a window has been maximized or restored\n//\nvoid _glfwInputWindowMaximize(_GLFWwindow* window, bool maximized)\n{\n    if (window->callbacks.maximize)\n        window->callbacks.maximize((GLFWwindow*) window, maximized);\n}\n\n// Notifies shared code that a window framebuffer has been resized\n// The size is specified in pixels\n//\nvoid _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height)\n{\n    if (window->callbacks.fbsize)\n        window->callbacks.fbsize((GLFWwindow*) window, width, height);\n}\n\n// Notifies shared code that a window live resize is in progress\n//\nvoid _glfwInputLiveResize(_GLFWwindow* window, bool started)\n{\n    if (window && window->callbacks.liveResize)\n        window->callbacks.liveResize((GLFWwindow*) window, started);\n}\n\n// Notifies shared code that a window content scale has changed\n// The scale is specified as the ratio between the current and default DPI\n//\nvoid _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale)\n{\n    if (window->callbacks.scale)\n        window->callbacks.scale((GLFWwindow*) window, xscale, yscale);\n}\n\n// Notifies shared code that the window contents needs updating\n//\nvoid _glfwInputWindowDamage(_GLFWwindow* window)\n{\n    if (window->callbacks.refresh)\n        window->callbacks.refresh((GLFWwindow*) window);\n}\n\n// Notifies shared code that the user wishes to close a window\n//\nvoid _glfwInputWindowCloseRequest(_GLFWwindow* window)\n{\n    window->shouldClose = true;\n\n    if (window->callbacks.close)\n        window->callbacks.close((GLFWwindow*) window);\n}\n\n// Notifies shared code that a window has changed its desired monitor\n//\nvoid _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor)\n{\n    window->monitor = monitor;\n}\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW public API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share, const GLFWLayerShellConfig *lsc) {\n    _GLFWfbconfig fbconfig;\n    _GLFWctxconfig ctxconfig;\n    _GLFWwndconfig wndconfig;\n    _GLFWwindow* window;\n\n    assert(title != NULL);\n    assert(width >= 0);\n    assert(height >= 0);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n\n    if (width <= 0 || height <= 0)\n    {\n        _glfwInputError(GLFW_INVALID_VALUE,\n                        \"Invalid window size %ix%i\",\n                        width, height);\n\n        return NULL;\n    }\n\n    fbconfig  = _glfw.hints.framebuffer;\n    ctxconfig = _glfw.hints.context;\n    wndconfig = _glfw.hints.window;\n\n    wndconfig.width   = width;\n    wndconfig.height  = height;\n    wndconfig.title   = title;\n    ctxconfig.share   = (_GLFWwindow*) share;\n\n    if (!_glfwIsValidContextConfig(&ctxconfig))\n        return NULL;\n\n    static GLFWid windowIdCounter = 0;\n    window = calloc(1, sizeof(_GLFWwindow));\n    window->next = _glfw.windowListHead;\n    window->id = ++windowIdCounter;\n    _glfw.windowListHead = window;\n\n    window->videoMode.width       = width;\n    window->videoMode.height      = height;\n    window->videoMode.redBits     = fbconfig.redBits;\n    window->videoMode.greenBits   = fbconfig.greenBits;\n    window->videoMode.blueBits    = fbconfig.blueBits;\n    window->videoMode.refreshRate = _glfw.hints.refreshRate;\n\n    window->monitor          = (_GLFWmonitor*) monitor;\n    window->resizable        = wndconfig.resizable;\n    window->decorated        = wndconfig.decorated;\n    window->autoIconify      = wndconfig.autoIconify;\n    window->floating         = wndconfig.floating;\n    window->focusOnShow      = wndconfig.focusOnShow;\n    window->mousePassthrough = wndconfig.mousePassthrough;\n    window->cursorMode       = GLFW_CURSOR_NORMAL;\n\n    window->minwidth    = GLFW_DONT_CARE;\n    window->minheight   = GLFW_DONT_CARE;\n    window->maxwidth    = GLFW_DONT_CARE;\n    window->maxheight   = GLFW_DONT_CARE;\n    window->numer       = GLFW_DONT_CARE;\n    window->denom       = GLFW_DONT_CARE;\n    window->widthincr   = GLFW_DONT_CARE;\n    window->heightincr  = GLFW_DONT_CARE;\n\n    // Open the actual window and create its context\n    if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig, lsc))\n    {\n        glfwDestroyWindow((GLFWwindow*) window);\n        return NULL;\n    }\n\n    if (ctxconfig.client != GLFW_NO_API)\n    {\n        if (!_glfwRefreshContextAttribs(window, &ctxconfig))\n        {\n            glfwDestroyWindow((GLFWwindow*) window);\n            return NULL;\n        }\n    }\n\n    if (wndconfig.mousePassthrough)\n        _glfwPlatformSetWindowMousePassthrough(window, true);\n\n    if (window->monitor)\n    {\n        if (wndconfig.centerCursor)\n            _glfwCenterCursorInContentArea(window);\n    }\n    else\n    {\n        if (wndconfig.visible)\n        {\n            _glfwPlatformShowWindow(window, false);\n#ifndef _GLFW_WAYLAND\n            if (wndconfig.focused)\n                _glfwPlatformFocusWindow(window);\n#endif\n        }\n    }\n\n    return (GLFWwindow*) window;\n}\n\nvoid glfwDefaultWindowHints(void)\n{\n    _GLFW_REQUIRE_INIT();\n\n    // The default is OpenGL with minimum version 1.0\n    memset(&_glfw.hints.context, 0, sizeof(_glfw.hints.context));\n    _glfw.hints.context.client = GLFW_OPENGL_API;\n    _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API;\n    _glfw.hints.context.major  = 1;\n    _glfw.hints.context.minor  = 0;\n\n    // The default is a focused, visible, resizable window with decorations\n    memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window));\n    _glfw.hints.window.resizable    = true;\n    _glfw.hints.window.visible      = true;\n    _glfw.hints.window.decorated    = true;\n    _glfw.hints.window.focused      = true;\n    _glfw.hints.window.autoIconify  = true;\n    _glfw.hints.window.centerCursor = true;\n    _glfw.hints.window.focusOnShow  = true;\n    _glfw.hints.window.blur_radius  = 0;\n\n    // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil,\n    // double buffered\n    memset(&_glfw.hints.framebuffer, 0, sizeof(_glfw.hints.framebuffer));\n    _glfw.hints.framebuffer.redBits      = 8;\n    _glfw.hints.framebuffer.greenBits    = 8;\n    _glfw.hints.framebuffer.blueBits     = 8;\n    _glfw.hints.framebuffer.alphaBits    = 8;\n    _glfw.hints.framebuffer.depthBits    = 24;\n    _glfw.hints.framebuffer.stencilBits  = 8;\n    _glfw.hints.framebuffer.doublebuffer = true;\n\n    // The default is to select the highest available refresh rate\n    _glfw.hints.refreshRate = GLFW_DONT_CARE;\n\n    // The default is to use full Retina resolution framebuffers\n    _glfw.hints.window.ns.retina = true;\n    // use the default colorspace assigned by the system\n    _glfw.hints.window.ns.color_space = 0;\n}\n\nGLFWAPI void glfwWindowHint(int hint, int value)\n{\n    _GLFW_REQUIRE_INIT();\n\n    switch (hint)\n    {\n        case GLFW_RED_BITS:\n            _glfw.hints.framebuffer.redBits = value;\n            return;\n        case GLFW_GREEN_BITS:\n            _glfw.hints.framebuffer.greenBits = value;\n            return;\n        case GLFW_BLUE_BITS:\n            _glfw.hints.framebuffer.blueBits = value;\n            return;\n        case GLFW_ALPHA_BITS:\n            _glfw.hints.framebuffer.alphaBits = value;\n            return;\n        case GLFW_DEPTH_BITS:\n            _glfw.hints.framebuffer.depthBits = value;\n            return;\n        case GLFW_STENCIL_BITS:\n            _glfw.hints.framebuffer.stencilBits = value;\n            return;\n        case GLFW_ACCUM_RED_BITS:\n            _glfw.hints.framebuffer.accumRedBits = value;\n            return;\n        case GLFW_ACCUM_GREEN_BITS:\n            _glfw.hints.framebuffer.accumGreenBits = value;\n            return;\n        case GLFW_ACCUM_BLUE_BITS:\n            _glfw.hints.framebuffer.accumBlueBits = value;\n            return;\n        case GLFW_ACCUM_ALPHA_BITS:\n            _glfw.hints.framebuffer.accumAlphaBits = value;\n            return;\n        case GLFW_AUX_BUFFERS:\n            _glfw.hints.framebuffer.auxBuffers = value;\n            return;\n        case GLFW_STEREO:\n            _glfw.hints.framebuffer.stereo = value ? true : false;\n            return;\n        case GLFW_DOUBLEBUFFER:\n            _glfw.hints.framebuffer.doublebuffer = value ? true : false;\n            return;\n        case GLFW_TRANSPARENT_FRAMEBUFFER:\n            _glfw.hints.framebuffer.transparent = value ? true : false;\n            return;\n        case GLFW_SAMPLES:\n            _glfw.hints.framebuffer.samples = value;\n            return;\n        case GLFW_SRGB_CAPABLE:\n            _glfw.hints.framebuffer.sRGB = value ? true : false;\n            return;\n        case GLFW_RESIZABLE:\n            _glfw.hints.window.resizable = value ? true : false;\n            return;\n        case GLFW_DECORATED:\n            _glfw.hints.window.decorated = value ? true : false;\n            return;\n        case GLFW_FOCUSED:\n            _glfw.hints.window.focused = value ? true : false;\n            return;\n        case GLFW_AUTO_ICONIFY:\n            _glfw.hints.window.autoIconify = value ? true : false;\n            return;\n        case GLFW_FLOATING:\n            _glfw.hints.window.floating = value ? true : false;\n            return;\n        case GLFW_MAXIMIZED:\n            _glfw.hints.window.maximized = value ? true : false;\n            return;\n        case GLFW_VISIBLE:\n            _glfw.hints.window.visible = value ? true : false;\n            return;\n        case GLFW_COCOA_RETINA_FRAMEBUFFER:\n            _glfw.hints.window.ns.retina = value ? true : false;\n            return;\n        case GLFW_COCOA_COLOR_SPACE:\n            _glfw.hints.window.ns.color_space = value;\n            return;\n        case GLFW_BLUR_RADIUS:\n            _glfw.hints.window.blur_radius = value;\n            return;\n        case GLFW_COCOA_GRAPHICS_SWITCHING:\n            _glfw.hints.context.nsgl.offline = value ? true : false;\n            return;\n        case GLFW_SCALE_TO_MONITOR:\n            _glfw.hints.window.scaleToMonitor = value ? true : false;\n            return;\n        case GLFW_CENTER_CURSOR:\n            _glfw.hints.window.centerCursor = value ? true : false;\n            return;\n        case GLFW_FOCUS_ON_SHOW:\n            _glfw.hints.window.focusOnShow = value ? true : false;\n            return;\n        case GLFW_MOUSE_PASSTHROUGH:\n            _glfw.hints.window.mousePassthrough = value ? true : false;\n            return;\n        case GLFW_CLIENT_API:\n            _glfw.hints.context.client = value;\n            return;\n        case GLFW_CONTEXT_CREATION_API:\n            _glfw.hints.context.source = value;\n            return;\n        case GLFW_CONTEXT_VERSION_MAJOR:\n            _glfw.hints.context.major = value;\n            return;\n        case GLFW_CONTEXT_VERSION_MINOR:\n            _glfw.hints.context.minor = value;\n            return;\n        case GLFW_CONTEXT_ROBUSTNESS:\n            _glfw.hints.context.robustness = value;\n            return;\n        case GLFW_OPENGL_FORWARD_COMPAT:\n            _glfw.hints.context.forward = value ? true : false;\n            return;\n        case GLFW_CONTEXT_DEBUG:\n            _glfw.hints.context.debug = value ? true : false;\n            return;\n        case GLFW_CONTEXT_NO_ERROR:\n            _glfw.hints.context.noerror = value ? true : false;\n            return;\n        case GLFW_OPENGL_PROFILE:\n            _glfw.hints.context.profile = value;\n            return;\n        case GLFW_CONTEXT_RELEASE_BEHAVIOR:\n            _glfw.hints.context.release = value;\n            return;\n        case GLFW_REFRESH_RATE:\n            _glfw.hints.refreshRate = value;\n            return;\n        case GLFW_WAYLAND_BGCOLOR:\n            _glfw.hints.window.wl.bgcolor = value;\n            return;\n    }\n    _glfwInputError(GLFW_INVALID_ENUM, \"Invalid window hint 0x%08X\", hint);\n}\n\nGLFWAPI void glfwWindowHintString(int hint, const char* value)\n{\n    assert(value != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    switch (hint)\n    {\n        case GLFW_COCOA_FRAME_NAME:\n            strncpy(_glfw.hints.window.ns.frameName, value,\n                    sizeof(_glfw.hints.window.ns.frameName) - 1);\n            return;\n        case GLFW_X11_CLASS_NAME:\n            strncpy(_glfw.hints.window.x11.className, value,\n                    sizeof(_glfw.hints.window.x11.className) - 1);\n            return;\n        case GLFW_X11_INSTANCE_NAME:\n            strncpy(_glfw.hints.window.x11.instanceName, value,\n                    sizeof(_glfw.hints.window.x11.instanceName) - 1);\n            return;\n        case GLFW_WAYLAND_APP_ID:\n            strncpy(_glfw.hints.window.wl.appId, value,\n                    sizeof(_glfw.hints.window.wl.appId) - 1);\n            return;\n        case GLFW_WAYLAND_WINDOW_TAG:\n            strncpy(_glfw.hints.window.wl.windowTag, value,\n                    sizeof(_glfw.hints.window.wl.windowTag) - 1);\n            return;\n    }\n\n    _glfwInputError(GLFW_INVALID_ENUM, \"Invalid window hint string 0x%08X\", hint);\n}\n\nGLFWAPI void glfwDestroyWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n\n    _GLFW_REQUIRE_INIT();\n\n    // Allow closing of NULL (to match the behavior of free)\n    if (window == NULL)\n        return;\n\n    // Clear all callbacks to avoid exposing a half torn-down window object\n    memset(&window->callbacks, 0, sizeof(window->callbacks));\n\n    // The window's context must not be current on another thread when the\n    // window is destroyed\n    if (window == _glfwPlatformGetTls(&_glfw.contextSlot))\n        glfwMakeContextCurrent(NULL);\n\n    _glfwPlatformDestroyWindow(window);\n\n    // Unlink window from global linked list\n    {\n        _GLFWwindow** prev = &_glfw.windowListHead;\n\n        while (*prev != window)\n            prev = &((*prev)->next);\n\n        *prev = window->next;\n    }\n\n    free(window);\n}\n\nGLFWAPI int glfwWindowShouldClose(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(0);\n    return window->shouldClose;\n}\n\nGLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n    window->shouldClose = value;\n}\n\nGLFWAPI const GLFWLayerShellConfig* glfwGetLayerShellConfig(GLFWwindow* handle) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n    return _glfwPlatformGetLayerShellConfig(window);\n}\n\n\nGLFWAPI bool glfwSetLayerShellConfig(GLFWwindow* handle, const GLFWLayerShellConfig *value) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n    return _glfwPlatformSetLayerShellConfig(window, value);\n}\n\nGLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n    assert(title != NULL);\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformSetWindowTitle(window, title);\n}\n\nGLFWAPI void glfwSetWindowIcon(GLFWwindow* handle,\n                               int count, const GLFWimage* images)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n    assert(count >= 0);\n    assert(count == 0 || images != NULL);\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformSetWindowIcon(window, count, images);\n}\n\nGLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    if (xpos)\n        *xpos = 0;\n    if (ypos)\n        *ypos = 0;\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformGetWindowPos(window, xpos, ypos);\n}\n\nGLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (window->monitor)\n        return;\n\n    _glfwPlatformSetWindowPos(window, xpos, ypos);\n}\n\nGLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    if (width)\n        *width = 0;\n    if (height)\n        *height = 0;\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformGetWindowSize(window, width, height);\n}\n\nGLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n    assert(width >= 0);\n    assert(height >= 0);\n\n    _GLFW_REQUIRE_INIT();\n\n    window->videoMode.width  = width;\n    window->videoMode.height = height;\n\n    _glfwPlatformSetWindowSize(window, width, height);\n}\n\nGLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle,\n                                     int minwidth, int minheight,\n                                     int maxwidth, int maxheight)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (minwidth != GLFW_DONT_CARE && minheight != GLFW_DONT_CARE)\n    {\n        if (minwidth < 0 || minheight < 0)\n        {\n            _glfwInputError(GLFW_INVALID_VALUE,\n                            \"Invalid window minimum size %ix%i\",\n                            minwidth, minheight);\n            return;\n        }\n    }\n\n    if (maxwidth != GLFW_DONT_CARE && maxheight != GLFW_DONT_CARE)\n    {\n        if (maxwidth < 0 || maxheight < 0 ||\n            maxwidth < minwidth || maxheight < minheight)\n        {\n            _glfwInputError(GLFW_INVALID_VALUE,\n                            \"Invalid window maximum size %ix%i\",\n                            maxwidth, maxheight);\n            return;\n        }\n    }\n\n    window->minwidth  = minwidth;\n    window->minheight = minheight;\n    window->maxwidth  = maxwidth;\n    window->maxheight = maxheight;\n\n    if (window->monitor || !window->resizable)\n        return;\n\n    _glfwPlatformSetWindowSizeLimits(window,\n                                     minwidth, minheight,\n                                     maxwidth, maxheight);\n}\n\nGLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n    assert(numer != 0);\n    assert(denom != 0);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE)\n    {\n        if (numer <= 0 || denom <= 0)\n        {\n            _glfwInputError(GLFW_INVALID_VALUE,\n                            \"Invalid window aspect ratio %i:%i\",\n                            numer, denom);\n            return;\n        }\n    }\n\n    window->numer = numer;\n    window->denom = denom;\n\n    if (window->monitor || !window->resizable)\n        return;\n\n    _glfwPlatformSetWindowAspectRatio(window, numer, denom);\n}\n\nGLFWAPI void glfwSetWindowSizeIncrements(GLFWwindow* handle, int widthincr, int heightincr)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n    assert(widthincr >= 0 || widthincr == GLFW_DONT_CARE);\n    assert(heightincr >= 0 || heightincr == GLFW_DONT_CARE);\n\n    _GLFW_REQUIRE_INIT();\n\n    window->widthincr  = widthincr;\n    window->heightincr = heightincr;\n    _glfwPlatformSetWindowSizeIncrements(window, window->widthincr, window->heightincr);\n}\n\nGLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    if (width)\n        *width = 0;\n    if (height)\n        *height = 0;\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformGetFramebufferSize(window, width, height);\n}\n\nGLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle,\n                                    int* left, int* top,\n                                    int* right, int* bottom)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    if (left)\n        *left = 0;\n    if (top)\n        *top = 0;\n    if (right)\n        *right = 0;\n    if (bottom)\n        *bottom = 0;\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom);\n}\n\nGLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle,\n                                       float* xscale, float* yscale)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    if (xscale)\n        *xscale = 0.f;\n    if (yscale)\n        *yscale = 0.f;\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformGetWindowContentScale(window, xscale, yscale);\n}\n\nGLFWAPI monotonic_t glfwGetDoubleClickInterval(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(ms_to_monotonic_t(500ll));\n    return _glfwPlatformGetDoubleClickInterval(window);\n}\n\nGLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(1.f);\n    return _glfwPlatformGetWindowOpacity(window);\n}\n\nGLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n    assert(opacity == opacity);\n    assert(opacity >= 0.f);\n    assert(opacity <= 1.f);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (opacity != opacity || opacity < 0.f || opacity > 1.f)\n    {\n        _glfwInputError(GLFW_INVALID_VALUE, \"Invalid window opacity %f\", opacity);\n        return;\n    }\n\n    _glfwPlatformSetWindowOpacity(window, opacity);\n}\n\nGLFWAPI void glfwIconifyWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformIconifyWindow(window);\n}\n\nGLFWAPI void glfwRestoreWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformRestoreWindow(window);\n}\n\nGLFWAPI void glfwMaximizeWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (window->monitor)\n        return;\n\n    _glfwPlatformMaximizeWindow(window);\n}\n\nGLFWAPI void glfwShowWindow(GLFWwindow* handle, bool move_to_active_screen)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (window->monitor)\n        return;\n\n    _glfwPlatformShowWindow(window, move_to_active_screen);\n\n#ifndef _GLFW_WAYLAND\n    if (window->focusOnShow)\n        _glfwPlatformFocusWindow(window);\n#endif\n}\n\nGLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    _glfwPlatformRequestWindowAttention(window);\n}\n\nGLFWAPI int glfwWindowBell(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(false);\n\n    return _glfwPlatformWindowBell(window);\n}\n\nGLFWAPI void glfwHideWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (window->monitor)\n        return;\n\n    _glfwPlatformHideWindow(window);\n}\n\nGLFWAPI void glfwFocusWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    _glfwPlatformFocusWindow(window);\n}\n\nGLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(0);\n\n    switch (attrib)\n    {\n        case GLFW_FOCUSED:\n            return _glfwPlatformWindowFocused(window);\n        case GLFW_ICONIFIED:\n            return _glfwPlatformWindowIconified(window);\n        case GLFW_VISIBLE:\n            return _glfwPlatformWindowVisible(window);\n        case GLFW_MAXIMIZED:\n            return _glfwPlatformWindowMaximized(window);\n        case GLFW_HOVERED:\n            return _glfwPlatformWindowHovered(window);\n        case GLFW_FOCUS_ON_SHOW:\n            return window->focusOnShow;\n        case GLFW_MOUSE_PASSTHROUGH:\n            return window->mousePassthrough;\n        case GLFW_TRANSPARENT_FRAMEBUFFER:\n            return _glfwPlatformFramebufferTransparent(window);\n        case GLFW_OCCLUDED:\n            return _glfwPlatformWindowOccluded(window);\n        case GLFW_RESIZABLE:\n            return window->resizable;\n        case GLFW_DECORATED:\n            return window->decorated;\n        case GLFW_FLOATING:\n            return window->floating;\n        case GLFW_AUTO_ICONIFY:\n            return window->autoIconify;\n        case GLFW_CLIENT_API:\n            return window->context.client;\n        case GLFW_CONTEXT_CREATION_API:\n            return window->context.source;\n        case GLFW_CONTEXT_VERSION_MAJOR:\n            return window->context.major;\n        case GLFW_CONTEXT_VERSION_MINOR:\n            return window->context.minor;\n        case GLFW_CONTEXT_REVISION:\n            return window->context.revision;\n        case GLFW_CONTEXT_ROBUSTNESS:\n            return window->context.robustness;\n        case GLFW_OPENGL_FORWARD_COMPAT:\n            return window->context.forward;\n        case GLFW_CONTEXT_DEBUG:\n            return window->context.debug;\n        case GLFW_OPENGL_PROFILE:\n            return window->context.profile;\n        case GLFW_CONTEXT_RELEASE_BEHAVIOR:\n            return window->context.release;\n        case GLFW_CONTEXT_NO_ERROR:\n            return window->context.noerror;\n    }\n\n    _glfwInputError(GLFW_INVALID_ENUM, \"Invalid window attribute 0x%08X\", attrib);\n    return 0;\n}\n\nGLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n\n    value = value ? true : false;\n\n    if (attrib == GLFW_AUTO_ICONIFY)\n        window->autoIconify = value;\n    else if (attrib == GLFW_RESIZABLE)\n    {\n        if (window->resizable == value)\n            return;\n\n        window->resizable = value;\n        if (!window->monitor)\n            _glfwPlatformSetWindowResizable(window, value);\n    }\n    else if (attrib == GLFW_DECORATED)\n    {\n        if (window->decorated == value)\n            return;\n\n        window->decorated = value;\n        if (!window->monitor)\n            _glfwPlatformSetWindowDecorated(window, value);\n    }\n    else if (attrib == GLFW_FLOATING)\n    {\n        if (window->floating == value)\n            return;\n\n        window->floating = value;\n        if (!window->monitor)\n            _glfwPlatformSetWindowFloating(window, value);\n    }\n    else if (attrib == GLFW_FOCUS_ON_SHOW)\n        window->focusOnShow = value;\n    else if (attrib == GLFW_MOUSE_PASSTHROUGH)\n    {\n        if (window->mousePassthrough == value)\n            return;\n\n        window->mousePassthrough = value;\n        _glfwPlatformSetWindowMousePassthrough(window, value);\n    }\n    else\n        _glfwInputError(GLFW_INVALID_ENUM, \"Invalid window attribute 0x%08X\", attrib);\n}\n\nGLFWAPI int glfwSetWindowBlur(GLFWwindow* handle, int value)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(0);\n    return _glfwPlatformSetWindowBlur(window, value);\n}\n\n\nGLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return (GLFWmonitor*) window->monitor;\n}\n\nGLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh,\n                                  GLFWmonitor* mh,\n                                  int xpos, int ypos,\n                                  int width, int height,\n                                  int refreshRate)\n{\n    _GLFWwindow* window = (_GLFWwindow*) wh;\n    _GLFWmonitor* monitor = (_GLFWmonitor*) mh;\n    assert(window != NULL);\n    assert(width >= 0);\n    assert(height >= 0);\n\n    _GLFW_REQUIRE_INIT();\n\n    if (width <= 0 || height <= 0)\n    {\n        _glfwInputError(GLFW_INVALID_VALUE,\n                        \"Invalid window size %ix%i\",\n                        width, height);\n        return;\n    }\n\n    if (refreshRate < 0 && refreshRate != GLFW_DONT_CARE)\n    {\n        _glfwInputError(GLFW_INVALID_VALUE,\n                        \"Invalid refresh rate %i\",\n                        refreshRate);\n        return;\n    }\n\n    window->videoMode.width       = width;\n    window->videoMode.height      = height;\n    window->videoMode.refreshRate = refreshRate;\n\n    _glfwPlatformSetWindowMonitor(window, monitor,\n                                  xpos, ypos, width, height,\n                                  refreshRate);\n}\n\nGLFWAPI bool glfwToggleFullscreen(GLFWwindow* wh, unsigned int flags) {\n    _GLFWwindow* window = (_GLFWwindow*) wh;\n    if (window) return _glfwPlatformToggleFullscreen(window, flags);\n    return false;\n}\n\nGLFWAPI bool glfwIsFullscreen(GLFWwindow* wh, unsigned int flags) {\n    return _glfwPlatformIsFullscreen((_GLFWwindow*)wh, flags);\n}\n\nGLFWAPI bool glfwAreSwapsAllowed(const GLFWwindow* wh) {\n    return !(((_GLFWwindow*)wh)->swaps_disallowed);\n}\n\nGLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT();\n    window->userPointer = pointer;\n}\n\nGLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return window->userPointer;\n}\n\nGLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle,\n                                                  GLFWwindowposfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.pos, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle,\n                                                    GLFWwindowsizefun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.size, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* handle,\n                                                      GLFWwindowclosefun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.close, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* handle,\n                                                          GLFWwindowrefreshfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.refresh, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* handle,\n                                                      GLFWwindowfocusfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.focus, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWwindowocclusionfun glfwSetWindowOcclusionCallback(GLFWwindow* handle,\n                                                              GLFWwindowocclusionfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.occlusion, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle,\n                                                          GLFWwindowiconifyfun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.iconify, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle,\n                                                            GLFWwindowmaximizefun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle,\n                                                              GLFWframebuffersizefun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.fbsize, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWliveresizefun glfwSetLiveResizeCallback(GLFWwindow* handle,\n                                                              GLFWliveresizefun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.liveResize, cbfun);\n    return cbfun;\n}\n\nGLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* handle,\n                                                                    GLFWwindowcontentscalefun cbfun)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    _GLFW_SWAP_POINTERS(window->callbacks.scale, cbfun);\n    return cbfun;\n}\n\nGLFWAPI void glfwPostEmptyEvent(void)\n{\n    _GLFW_REQUIRE_INIT();\n    _glfwPlatformPostEmptyEvent();\n}\n"
  },
  {
    "path": "glfw/wl_client_side_decorations.c",
    "content": "/*\n * wl_client_side_decorations.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"wl_client_side_decorations.h\"\n\n#include \"backend_utils.h\"\n#include <sys/mman.h>\n#include <errno.h>\n#include <string.h>\n#include <stdlib.h>\n\n#define decs window->wl.decorations\n#define debug debug_rendering\n\n#define ARGB(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))\n#define A(x) (((x) >> 24) & 0xff)\n#define R(x) (((x) >> 16) & 0xff)\n#define G(x) (((x) >> 8) & 0xff)\n#define B(x) ((x) & 0xff)\n#define SWAP(x, y) do { __typeof__(x) SWAP = x; x = y; y = SWAP; } while (0)\n\n// shadow tile  {{{\ntypedef float kernel_type;\n\nstatic void\nbuild_blur_kernel(kernel_type *blur_kernel, const size_t size, kernel_type sigma) {\n    // 1D Normalized Gaussian\n    const kernel_type half = size / (kernel_type)2;\n    kernel_type sum = 0;\n    for (size_t i = 0; i < size; i++) {\n        kernel_type f = (i - half);\n        blur_kernel[i] = (kernel_type)exp(- f * f / sigma);\n        sum += blur_kernel[i];\n    }\n    for (size_t i = 0; i < size; i++) blur_kernel[i] /= sum;\n}\n\nstatic void\nblur_mask(kernel_type *image_data, ssize_t width, ssize_t height, ssize_t kernel_size, kernel_type sigma, kernel_type *scratch, kernel_type *blur_kernel, ssize_t margin) {\n    (void)margin;\n    build_blur_kernel(blur_kernel, kernel_size, sigma);\n    const size_t half = kernel_size / 2;\n\n    for (ssize_t y = 0; y < height; y++) {\n        kernel_type *s = image_data + y * width, *d = scratch + y * width;\n        for (ssize_t x = 0; x < width; x++) {\n            kernel_type a = 0;\n            for (ssize_t k = 0; k < kernel_size; k++) {\n                const ssize_t px = x + k - half;\n                if (0 <= px && px < width) a += s[px] * blur_kernel[k];\n            }\n            d[x] = a;\n        }\n    }\n\n    for (ssize_t y = 0; y < height; y++) {\n        kernel_type *d = image_data + y * width;\n        for (ssize_t x = 0; x < width; x++) {\n            kernel_type a = 0;\n            for (ssize_t k = 0; k < kernel_size; k++) {\n                const ssize_t py = y + k - half;\n                if (0 <= py && py < height) {\n                    kernel_type *s = scratch + py * width;\n                    a += s[x] * blur_kernel[k];\n                }\n            }\n            d[x] = a;\n        }\n    }\n}\n\nstatic kernel_type*\ncreate_shadow_mask(size_t width, size_t height, size_t margin, size_t kernel_size, kernel_type base_alpha, kernel_type sigma) {\n    kernel_type *mask = calloc(2 * width * height + kernel_size, sizeof(kernel_type));\n    if (!mask) return NULL;\n    for (size_t y = margin; y < height - margin; y++) {\n        kernel_type *row = mask + y * width;\n        for (size_t x = margin; x < width - margin; x++) row[x] = base_alpha;\n    }\n    blur_mask(mask, width, height, kernel_size, sigma, mask + width * height, (kernel_type*)(mask + 2 * width * height), margin);\n    return mask;\n}\n\n#define st decs.shadow_tile\n\nstatic size_t\ncreate_shadow_tile(_GLFWwindow *window) {\n    const size_t margin = (size_t)round(decs.metrics.width * decs.for_window_state.fscale);\n    if (st.data && st.for_decoration_size == margin) return margin;\n    st.for_decoration_size = margin;\n    st.segments = 7;\n    st.stride = st.segments * margin;\n    st.corner_size = margin * (st.segments - 1) / 2;\n    kernel_type* mask = create_shadow_mask(st.stride, st.stride, margin, 2 * margin + 1, (kernel_type)0.7, 32 * margin);\n    free(st.data);\n    st.data = malloc(sizeof(st.data[0]) * st.stride * st.stride);\n    if (st.data) for (size_t i = 0; i < st.stride * st.stride; i++) st.data[i] = ((uint8_t)(mask[i] * 255)) << 24;\n    free(mask);\n    return margin;\n}\n\n\n// }}}\n\nstatic bool window_needs_shadows(_GLFWwindow *w) { return !(w->wl.current.toplevel_states & TOPLEVEL_STATE_DOCKED); }\n\nstatic void\nswap_buffers(_GLFWWaylandBufferPair *pair) {\n    SWAP(pair->front, pair->back);\n    SWAP(pair->data.front, pair->data.back);\n}\n\nstatic size_t\ninit_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, double scale) {\n    memset(pair, 0, sizeof(_GLFWWaylandBufferPair));\n    pair->width = (int)round(width * scale);\n    pair->height = (int)round(height * scale);\n    pair->viewport_width = width; pair->viewport_height = height;\n    pair->stride = 4 * pair->width;\n    pair->size_in_bytes = pair->stride * pair->height;\n    return 2 * pair->size_in_bytes;\n}\n\n#define all_shadow_surfaces(Q) Q(shadow_left); Q(shadow_top); Q(shadow_right); Q(shadow_bottom); \\\n    Q(shadow_upper_left); Q(shadow_upper_right); Q(shadow_lower_left); Q(shadow_lower_right);\n#define all_surfaces(Q) Q(titlebar); all_shadow_surfaces(Q);\n\nstatic bool\nwindow_has_buffer(_GLFWwindow *window, struct wl_buffer *q) {\n#define Q(which) if (decs.which.buffer.a == q) { decs.which.buffer.a_needs_to_be_destroyed = false; return true; } if (decs.which.buffer.b == q) { decs.which.buffer.b_needs_to_be_destroyed = false; return true; }\n    all_surfaces(Q);\n#undef Q\n    return false;\n}\n\nstatic void\nbuffer_release_event(void *data, struct wl_buffer *buffer) {\n    wl_buffer_destroy(buffer);\n    _GLFWwindow *window = _glfwWindowForId((uintptr_t)data);\n    if (window && window_has_buffer(window, buffer)) decs.buffer_destroyed = true;\n}\n\nstatic struct wl_buffer_listener handle_buffer_events = {.release = buffer_release_event};\n\nstatic void\nalloc_buffer_pair(uintptr_t window_id, _GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) {\n    pair->data.a = data + *offset;\n    pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);\n    pair->a_needs_to_be_destroyed = true;\n    wl_buffer_add_listener(pair->a, &handle_buffer_events, (void*)window_id);\n    *offset += pair->size_in_bytes;\n    pair->data.b = data + *offset;\n    pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);\n    pair->b_needs_to_be_destroyed = true;\n    wl_buffer_add_listener(pair->b, &handle_buffer_events, (void*)window_id);\n    *offset += pair->size_in_bytes;\n    pair->front = pair->a; pair->back = pair->b;\n    pair->data.front = pair->data.a; pair->data.back = pair->data.b;\n}\n\nvoid\ncsd_initialize_metrics(_GLFWwindow *window) {\n    decs.metrics.width = 12;\n    decs.metrics.top = 36;\n    decs.metrics.visible_titlebar_height = decs.metrics.top - decs.metrics.width;\n    decs.metrics.horizontal = 2 * decs.metrics.width;\n    decs.metrics.vertical = decs.metrics.width + decs.metrics.top;\n}\n\nstatic void\npatch_titlebar_with_alpha_mask(uint32_t *dest, uint8_t *src, unsigned height, unsigned dest_stride, unsigned src_width, unsigned dest_left, uint32_t bg, uint32_t fg) {\n    for (unsigned y = 0; y < height; y++, src += src_width, dest += dest_stride) {\n        uint32_t *d = dest + dest_left;\n        for (unsigned i = 0; i < src_width; i++) {\n            const uint8_t alpha = src[i], calpha = 255 - alpha;\n            // Blend the red and blue components\n            uint32_t ans = ((bg & 0xff00ff) * calpha + (fg & 0xff00ff) * alpha) & 0xff00ff00;\n            // Blend the green component\n            ans += ((bg & 0xff00) * calpha + (fg & 0xff00) * alpha) & 0xff0000;\n            ans >>= 8;\n            d[i] = ans | 0xff000000;\n        }\n    }\n}\n\nstatic void\nrender_hline(uint8_t *out, unsigned width, unsigned thickness, unsigned bottom, unsigned left, unsigned right) {\n    for (unsigned y = bottom - thickness; y < bottom; y++) {\n        uint8_t *dest = out + width * y;\n        for (unsigned x = left; x < right; x++) dest[x] = 255;\n    }\n}\n\nstatic void\nrender_vline(uint8_t *out, unsigned width, unsigned thickness, unsigned left, unsigned top, unsigned bottom) {\n    for (unsigned y = top; y < bottom; y++) {\n        uint8_t *dest = out + width * y;\n        for (unsigned x = left; x < left + thickness; x++) dest[x] = 255;\n    }\n}\n\nstatic int\nscale(unsigned thickness, float factor) {\n    return (unsigned)(roundf(thickness * factor));\n}\n\nstatic void\nrender_minimize(uint8_t *out, unsigned width, unsigned height) {\n    memset(out, 0, (size_t)width * height);\n    unsigned thickness = height / 12;\n    unsigned baseline = height - thickness * 2;\n    unsigned side_margin = scale(thickness, 3.8f);\n    if (!thickness || width <= side_margin || height < baseline + 2 * thickness) return;\n    render_hline(out, width, thickness, baseline, side_margin, width - side_margin);\n}\n\nstatic void\nrender_maximize(uint8_t *out, unsigned width, unsigned height) {\n    memset(out, 0, (size_t)width * height);\n    unsigned thickness = height / 12, half_thickness = thickness / 2;\n    unsigned baseline = height - thickness * 2;\n    unsigned side_margin = scale(thickness, 3.0f);\n    unsigned top = 4 * thickness;\n    if (!half_thickness || width <= side_margin || height < baseline + 2 * thickness || top >= baseline) return;\n    render_hline(out, width, half_thickness, baseline, side_margin, width - side_margin);\n    render_hline(out, width, thickness, top + thickness, side_margin, width - side_margin);\n    render_vline(out, width, half_thickness, side_margin, top, baseline);\n    render_vline(out, width, half_thickness, width - side_margin, top, baseline);\n}\n\nstatic void\nrender_restore(uint8_t *out, unsigned width, unsigned height) {\n    memset(out, 0, (size_t)width * height);\n    unsigned thickness = height / 12, half_thickness = thickness / 2;\n    unsigned baseline = height - thickness * 2;\n    unsigned side_margin = scale(thickness, 3.0f);\n    unsigned top = 4 * thickness;\n    if (!half_thickness || width <= side_margin || height < baseline + 2 * thickness || top >= baseline) return;\n    unsigned box_height = ((baseline - top) * 3) / 4;\n    if (box_height < 2*thickness) return;\n    unsigned box_width = ((width - 2 * side_margin) * 3) / 4;\n    // bottom box\n    unsigned box_top = baseline - box_height, left = side_margin, right = side_margin + box_width, bottom = baseline;\n    render_hline(out, width, thickness, box_top + thickness, left, right);\n    render_hline(out, width, half_thickness, bottom, left, right);\n    render_vline(out, width, half_thickness, left, box_top, bottom);\n    render_vline(out, width, half_thickness, side_margin + box_width, baseline - box_height, baseline);\n    // top box\n    unsigned box_x_shift = 2 * thickness, box_y_shift = 2 * thickness;\n    box_x_shift = MIN(width - right, box_x_shift);\n    box_y_shift = MIN(box_top, box_y_shift);\n    unsigned left2 = left + box_x_shift, right2 = right + box_x_shift, top2 = box_top - box_y_shift, bottom2 = bottom - box_y_shift;\n    render_hline(out, width, thickness, top2 + thickness, left2, right2);\n    render_vline(out, width, half_thickness, right2, top2, bottom2);\n    render_hline(out, width, half_thickness, bottom2, right, right2);\n    render_vline(out, width, half_thickness, left2, top2, box_top);\n}\n\nstatic void\nrender_line(uint8_t *buf, unsigned width, unsigned height, unsigned thickness, int x1, int y1, int x2, int y2) {\n    float m = (y2 - y1) / (float)(x2 - x1);\n    float c = y1 - m * x1;\n    unsigned delta = thickness / 2, extra = thickness % 2;\n    for (int x = MAX(0, MIN(x1, x2)); x < MIN((int)width, MAX(x1, x2) + 1); x++) {\n        float ly = m * x + c;\n        for (int y = MAX(0, (int)(ly - delta)); y < MIN((int)height, (int)(ly + delta + extra + 1)); y++) buf[x + y * width] = 255;\n    }\n    for (int y = MAX(0, MIN(y1, y2)); y < MIN((int)height, MAX(y1, y2) + 1); y++) {\n        float lx = (y - c) / m;\n        for (int x = MAX(0, (int)(lx - delta)); x < MIN((int)width, (int)(lx + delta + extra + 1)); x++) buf[x + y * width] = 255;\n    }\n}\n\nstatic void\nrender_close(uint8_t *out, unsigned width, unsigned height) {\n    memset(out, 0, (size_t)width * height);\n    unsigned thickness = height / 12;\n    unsigned baseline = height - thickness * 2;\n    unsigned side_margin = scale(thickness, 3.3f);\n    int top = baseline - (width - 2 * side_margin);\n    if (top <= 0) return;\n    unsigned line_thickness = scale(thickness, 1.5f);\n    render_line(out, width, height, line_thickness, side_margin, top, width - side_margin, baseline);\n    render_line(out, width, height, line_thickness, side_margin, baseline, width - side_margin, top);\n}\n\nstatic uint32_t\naverage_intensity_in_src(uint8_t *src, unsigned src_width, unsigned src_x, unsigned src_y, unsigned factor) {\n    uint32_t ans = 0;\n    for (unsigned y = src_y; y < src_y + factor; y++) {\n        uint8_t *s = src + src_width * y;\n        for (unsigned x = src_x; x < src_x + factor; x++) ans += s[x];\n    }\n    return ans / (factor * factor);\n}\n\nstatic void\ndownsample(uint8_t *dest, uint8_t *src, unsigned dest_width, unsigned dest_height, unsigned factor) {\n    unsigned src_width = factor * dest_width;\n    for (unsigned y = 0; y < dest_height; y++) {\n        uint8_t *d = dest + dest_width * y;\n        for (unsigned x = 0; x < dest_width; x++) {\n            d[x] = MIN(255u, (uint32_t)d[x] + average_intensity_in_src(src, src_width, x * factor, y * factor, factor));\n        }\n    }\n}\n\nstatic void\nrender_button(void(*which)(uint8_t *, unsigned, unsigned), bool antialias, uint32_t *dest, uint8_t *src, unsigned height, unsigned dest_stride, unsigned src_width, unsigned dest_left, uint32_t bg, uint32_t fg) {\n    if (antialias) {\n        static const unsigned factor = 4;\n        uint8_t *big_src = malloc((size_t)factor * factor * height * src_width);\n        if (big_src) {\n            which(big_src, src_width * factor, height * factor);\n            memset(src, 0, (size_t)src_width * height);\n            downsample(src, big_src, src_width, height, factor);\n            free(big_src);\n        } else which(src, src_width, height);\n    } else which(src, src_width, height);\n    patch_titlebar_with_alpha_mask(dest, src, height, dest_stride, src_width, dest_left, bg, fg);\n}\n\nstatic void\nrender_title_bar(_GLFWwindow *window, bool to_front_buffer) {\n    const bool is_focused = window->id == _glfw.focusedWindowId;\n    const bool is_maximized = window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED;\n    const uint32_t light_fg = is_focused ? 0xff444444 : 0xff888888, light_bg = is_focused ? 0xffdddad6 : 0xffeeeeee;\n    const uint32_t dark_fg = is_focused ? 0xffffffff : 0xffcccccc, dark_bg = is_focused ? 0xff303030 : 0xff242424;\n    static const uint32_t hover_dark_bg = 0xff444444, hover_light_bg = 0xffbbbbbb;\n    uint32_t bg_color = light_bg, fg_color = light_fg, hover_bg = hover_light_bg;\n    GLFWColorScheme appearance = glfwGetCurrentSystemColorTheme(false);\n    bool is_dark = false;\n    if (decs.use_custom_titlebar_color || appearance == GLFW_COLOR_SCHEME_NO_PREFERENCE) {\n        bg_color = 0xff000000 | (decs.titlebar_color & 0xffffff);\n        double red = ((bg_color >> 16) & 0xFF) / 255.0;\n        double green = ((bg_color >> 8) & 0xFF) / 255.0;\n        double blue = (bg_color & 0xFF) / 255.0;\n        double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n        if (luma < 0.5) { fg_color = dark_fg; hover_bg = hover_dark_bg; is_dark = true; }\n        if (!decs.use_custom_titlebar_color) bg_color = luma < 0.5 ? dark_bg : light_bg;\n    } else if (appearance == GLFW_COLOR_SCHEME_DARK) { bg_color = dark_bg; fg_color = dark_fg; hover_bg = hover_dark_bg; is_dark = true; }\n    uint8_t *output = to_front_buffer ? decs.titlebar.buffer.data.front : decs.titlebar.buffer.data.back;\n\n    // render text part\n    size_t button_size = decs.titlebar.buffer.height;\n    unsigned num_buttons = 1;\n    if (window->wl.wm_capabilities.maximize) num_buttons++;\n    if (window->wl.wm_capabilities.minimize) num_buttons++;\n    if (window->wl.title && window->wl.title[0] && _glfw.callbacks.draw_text) {\n        if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.titlebar.buffer.width, decs.titlebar.buffer.height, 0, 0, num_buttons * button_size, false)) goto render_buttons;\n    }\n    // rendering of text failed, blank the buffer\n    for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.titlebar.buffer.size_in_bytes); px < end; px++) *px = bg_color;\n\nrender_buttons:\n    decs.maximize.width = 0; decs.minimize.width = 0; decs.close.width = 0;\n    if (!button_size) return;\n\n    uint8_t *alpha_mask = malloc(button_size * button_size);\n    int left = decs.titlebar.buffer.width - num_buttons * button_size;\n    if (!alpha_mask || left <= 0) return;\n#define drawb(which, antialias, func, hover_bg) { \\\n    render_button(func, antialias, (uint32_t*)output, alpha_mask, button_size, decs.titlebar.buffer.width, button_size, left, decs.which.hovered ? hover_bg : bg_color, fg_color); decs.which.left = left; decs.which.width = button_size; left += button_size; }\n\n    if (window->wl.wm_capabilities.minimize) drawb(minimize, false, render_minimize, hover_bg);\n    if (window->wl.wm_capabilities.maximize) {\n        if (is_maximized) { drawb(maximize, false, render_restore, hover_bg); } else { drawb(maximize, false, render_maximize, hover_bg); }\n    }\n    drawb(close, true, render_close, is_dark ? 0xff880000: 0xffc80000);\n    free(alpha_mask);\n#undef drawb\n}\n\nstatic void\nupdate_title_bar(_GLFWwindow *window) {\n    render_title_bar(window, false);\n    swap_buffers(&decs.titlebar.buffer);\n}\n\nstatic void\nrender_horizontal_shadow(_GLFWwindow *window, ssize_t scaled_shadow_size, ssize_t src_y_offset, ssize_t y, _GLFWWaylandBufferPair *buf) {\n    // left region\n    ssize_t src_y = src_y_offset + y;\n    const ssize_t src_leftover_corner = st.corner_size - scaled_shadow_size;\n    uint32_t *src = st.data + st.stride * src_y + scaled_shadow_size;\n    uint32_t *d_start = (uint32_t*)(buf->data.front + y * buf->stride);\n    uint32_t *d_end = (uint32_t*)(buf->data.front + (y+1) * buf->stride);\n    uint32_t *left_region_end = d_start + MIN(d_end - d_start, src_leftover_corner);\n    memcpy(d_start, src, sizeof(uint32_t) * (left_region_end - d_start));\n    // right region\n    uint32_t *right_region_start = MAX(d_start, d_end - src_leftover_corner);\n    src = st.data + st.stride * (src_y+1) - st.corner_size;\n    memcpy(right_region_start, src, sizeof(uint32_t) * MIN(src_leftover_corner, d_end - right_region_start));\n    src = st.data + st.stride * src_y + st.corner_size;\n    // middle region\n    for (uint32_t *d = left_region_end; d < right_region_start; d += scaled_shadow_size)\n        memcpy(d, src, sizeof(uint32_t) * MIN(right_region_start - d, scaled_shadow_size));\n}\n\nstatic void\ncopy_vertical_region(\n    _GLFWwindow *window, ssize_t src_y_start, ssize_t src_y_limit,\n    ssize_t y_start, ssize_t y_limit, ssize_t src_x_offset, _GLFWWaylandBufferPair *buf\n) {\n    for (ssize_t dy = y_start, sy = src_y_start; dy < y_limit && sy < src_y_limit; dy++, sy++)\n        memcpy(buf->data.front + dy * buf->stride, st.data + sy * st.stride + src_x_offset, sizeof(uint32_t) * buf->width);\n}\n\nstatic void\nrender_shadows(_GLFWwindow *window) {\n    if (!window_needs_shadows(window)) return;\n    const ssize_t scaled_shadow_size = create_shadow_tile(window);\n    if (!st.data || !scaled_shadow_size) return;  // out of memory\n    // upper and lower shadows\n    for (ssize_t y = 0; y < scaled_shadow_size; y++) {\n        _GLFWWaylandBufferPair *buf = &decs.shadow_upper_left.buffer;\n        uint32_t *src = st.data + st.stride * y;\n        uint32_t *d = (uint32_t*)(buf->data.front + y * buf->stride);\n        memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);\n\n        buf = &decs.shadow_upper_right.buffer;\n        src += st.stride - scaled_shadow_size;\n        d = (uint32_t*)(buf->data.front + y * buf->stride);\n        memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);\n\n        const size_t tile_bottom_start = st.stride - scaled_shadow_size;\n        buf = &decs.shadow_lower_left.buffer;\n        src = st.data + (tile_bottom_start + y) * st.stride;\n        d = (uint32_t*)(buf->data.front + y * buf->stride);\n        memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);\n\n        buf = &decs.shadow_lower_right.buffer;\n        src += st.stride - scaled_shadow_size;\n        d = (uint32_t*)(buf->data.front + y * buf->stride);\n        memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);\n\n        render_horizontal_shadow(window, scaled_shadow_size, 0, y, &decs.shadow_top.buffer);\n        render_horizontal_shadow(window, scaled_shadow_size, st.stride - scaled_shadow_size, y, &decs.shadow_bottom.buffer);\n    }\n    // side shadows\n    // top region\n    const ssize_t src_leftover_corner = st.corner_size - scaled_shadow_size;\n    ssize_t y_start = 0, y_end = decs.shadow_left.buffer.height, top_end = MIN(y_end, src_leftover_corner);\n    ssize_t right_src_start = st.stride - scaled_shadow_size;\n#define c(src_y_start, src_y_limit, dest_y_start, dest_y_limit) { \\\n    copy_vertical_region(window, src_y_start, src_y_limit, dest_y_start, dest_y_limit, 0, &decs.shadow_left.buffer); \\\n    copy_vertical_region(window, src_y_start, src_y_limit, dest_y_start, dest_y_limit, right_src_start, &decs.shadow_right.buffer); \\\n}\n    c(scaled_shadow_size, st.corner_size, y_start, top_end);\n    // bottom region\n    ssize_t bottom_start = MAX(0, y_end - src_leftover_corner);\n    c(st.stride - st.corner_size, st.stride - scaled_shadow_size, bottom_start, y_end);\n    // middle region\n    for (ssize_t dest_y = top_end; dest_y < bottom_start; dest_y += scaled_shadow_size)\n        c(st.corner_size, st.corner_size + scaled_shadow_size, dest_y, MIN(dest_y + scaled_shadow_size, bottom_start));\n#undef c\n\n#define copy(which) for (uint32_t *src = (uint32_t*)decs.which.buffer.data.front, *dest = (uint32_t*)decs.which.buffer.data.back; src < (uint32_t*)(decs.which.buffer.data.front + decs.which.buffer.size_in_bytes); src++, dest++) *dest = (A(*src) / 2 ) << 24;\n    all_shadow_surfaces(copy);\n#undef copy\n\n}\n#undef st\n\nstatic bool\ncreate_shm_buffers(_GLFWwindow* window) {\n    decs.mapping.size = 0;\n    const bool has_titlebar = !decs.titlebar_hidden;\n    const int side_height = window->wl.height + (has_titlebar ? decs.metrics.visible_titlebar_height : 0);\n#define bp(which, width, height) decs.mapping.size += init_buffer_pair(&decs.which.buffer, width, height, decs.for_window_state.fscale);\n    if (has_titlebar) bp(titlebar, window->wl.width, decs.metrics.visible_titlebar_height);\n    bp(shadow_top, window->wl.width, decs.metrics.width);\n    bp(shadow_bottom, window->wl.width, decs.metrics.width);\n    bp(shadow_left, decs.metrics.width, side_height);\n    bp(shadow_right, decs.metrics.width, side_height);\n    bp(shadow_upper_left, decs.metrics.width, decs.metrics.width);\n    bp(shadow_upper_right, decs.metrics.width, decs.metrics.width);\n    bp(shadow_lower_left, decs.metrics.width, decs.metrics.width);\n    bp(shadow_lower_right, decs.metrics.width, decs.metrics.width);\n#undef bp\n\n    int fd = createAnonymousFile(decs.mapping.size);\n    if (fd < 0) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Creating a buffer file for %zu B failed: %s\",\n                        decs.mapping.size, strerror(errno));\n        return false;\n    }\n    decs.mapping.data = mmap(NULL, decs.mapping.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);\n    if (decs.mapping.data == MAP_FAILED) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: mmap failed: %s\", strerror(errno));\n        close(fd);\n        return false;\n    }\n    struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size);\n    close(fd);\n    size_t offset = 0;\n#define Q(which) alloc_buffer_pair(window->id, &decs.which.buffer, pool, decs.mapping.data, &offset)\n    if (has_titlebar) Q(titlebar);\n    all_shadow_surfaces(Q);\n#undef Q\n    wl_shm_pool_destroy(pool);\n    if (has_titlebar) render_title_bar(window, true);\n    render_shadows(window);\n    debug(\"Created decoration buffers at scale: %f\\n\", decs.for_window_state.fscale);\n    return true;\n}\n\nstatic void\nfree_csd_surface(_GLFWWaylandCSDSurface *s) {\n    if (s->subsurface) wl_subsurface_destroy(s->subsurface);\n    s->subsurface = NULL;\n    if (s->wp_viewport) wp_viewport_destroy(s->wp_viewport);\n    s->wp_viewport = NULL;\n    if (s->surface) wl_surface_destroy(s->surface);\n    s->surface = NULL;\n}\n\nstatic void\nfree_csd_surfaces(_GLFWwindow *window) {\n#define Q(which) free_csd_surface(&decs.which)\n    all_surfaces(Q);\n#undef Q\n}\n\nstatic void\nfree_csd_buffers(_GLFWwindow *window) {\n#define Q(which) { \\\n    if (decs.which.buffer.a_needs_to_be_destroyed && decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \\\n    if (decs.which.buffer.b_needs_to_be_destroyed && decs.which.buffer.b) wl_buffer_destroy(decs.which.buffer.b); \\\n    memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \\\n}\n    all_surfaces(Q);\n#undef Q\n    if (decs.mapping.data) munmap(decs.mapping.data, decs.mapping.size);\n    decs.mapping.data = NULL; decs.mapping.size = 0;\n}\n\nstatic void\nposition_csd_surface(_GLFWWaylandCSDSurface *s, int x, int y) {\n    if (s->surface) {\n        wl_surface_set_buffer_scale(s->surface, 1);\n        s->x = x; s->y = y;\n        wl_subsurface_set_position(s->subsurface, s->x, s->y);\n    }\n}\n\nstatic void\ncreate_csd_surfaces(_GLFWwindow *window, _GLFWWaylandCSDSurface *s) {\n    if (s->surface) wl_surface_destroy(s->surface);\n    s->surface = wl_compositor_create_surface(_glfw.wl.compositor);\n    wl_surface_set_user_data(s->surface, window);\n    if (s->subsurface) wl_subsurface_destroy(s->subsurface);\n    s->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, s->surface, window->wl.surface);\n    if (_glfw.wl.wp_viewporter) {\n        if (s->wp_viewport) wp_viewport_destroy(s->wp_viewport);\n        s->wp_viewport = wp_viewporter_get_viewport(_glfw.wl.wp_viewporter, s->surface);\n    }\n}\n\n#define damage_csd(which, xbuffer) if (decs.which.surface) { \\\n    wl_surface_attach(decs.which.surface, (xbuffer), 0, 0); \\\n    if (decs.which.wp_viewport) wp_viewport_set_destination(decs.which.wp_viewport, decs.which.buffer.viewport_width, decs.which.buffer.viewport_height); \\\n    wl_surface_damage(decs.which.surface, 0, 0, decs.which.buffer.width, decs.which.buffer.height); \\\n    wl_surface_commit(decs.which.surface); \\\n    if (decs.which.buffer.a == (xbuffer)) { decs.which.buffer.a_needs_to_be_destroyed = false; } else { decs.which.buffer.b_needs_to_be_destroyed = false; }}\n\nstatic bool\nwindow_is_csd_capable(_GLFWwindow *window) {\n    return window->decorated && !decs.serverSide && window->wl.xdg.toplevel;\n}\n\nbool\ncsd_should_window_be_decorated(_GLFWwindow *window) {\n    return window_is_csd_capable(window) && window->monitor == NULL && (window->wl.current.toplevel_states & TOPLEVEL_STATE_FULLSCREEN) == 0;\n}\n\nstatic bool\nensure_csd_resources(_GLFWwindow *window) {\n    if (!window_is_csd_capable(window)) return false;\n    const bool has_titlebar = !decs.titlebar_hidden;\n    const bool is_focused = window->id == _glfw.focusedWindowId;\n    const bool focus_changed = is_focused != decs.for_window_state.focused;\n    const double current_scale = _glfwWaylandWindowScale(window);\n    const bool size_changed = (\n        decs.for_window_state.width != window->wl.width ||\n        decs.for_window_state.height != window->wl.height ||\n        decs.for_window_state.fscale != current_scale ||\n        !decs.mapping.data\n    );\n    const bool state_changed = decs.for_window_state.toplevel_states != window->wl.current.toplevel_states;\n    const bool titlebar_state_changed = (has_titlebar && !decs.titlebar.surface) || (!has_titlebar && decs.titlebar.surface);\n    const bool needs_update = focus_changed || size_changed || titlebar_state_changed || decs.buffer_destroyed || state_changed;\n    debug(\"CSD: old.size: %dx%d new.size: %dx%d needs_update: %d size_changed: %d state_changed: %d buffer_destroyed: %d\\n\",\n            decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update,\n            size_changed, state_changed, decs.buffer_destroyed);\n    if (!needs_update) return false;\n    decs.for_window_state.fscale = current_scale;  // used in create_shm_buffers\n    if (size_changed || decs.buffer_destroyed || titlebar_state_changed) {\n        free_csd_buffers(window);\n        if (!create_shm_buffers(window)) return false;\n        decs.buffer_destroyed = false;\n    }\n\n    const int top_y = has_titlebar ? -(int)decs.metrics.visible_titlebar_height : 0;\n\n#define setup_surface(which, x, y) \\\n    if (!decs.which.surface) create_csd_surfaces(window, &decs.which); \\\n        position_csd_surface(&decs.which, x, y);\n\n    if (has_titlebar) {\n        setup_surface(titlebar, 0, -decs.metrics.visible_titlebar_height);\n    } else {\n        free_csd_surface(&decs.titlebar);\n        if (decs.focus == CSD_titlebar) {\n            decs.focus = CENTRAL_WINDOW;\n            decs.dragging = false;\n        }\n    }\n    setup_surface(shadow_top, 0, top_y - decs.metrics.width);\n    setup_surface(shadow_bottom, 0, window->wl.height);\n    setup_surface(shadow_left, -decs.metrics.width, top_y);\n    setup_surface(shadow_right, window->wl.width, decs.shadow_left.y);\n    setup_surface(shadow_upper_left, decs.shadow_left.x, decs.shadow_top.y);\n    setup_surface(shadow_upper_right, decs.shadow_right.x, decs.shadow_top.y);\n    setup_surface(shadow_lower_left, decs.shadow_left.x, decs.shadow_bottom.y);\n    setup_surface(shadow_lower_right, decs.shadow_right.x, decs.shadow_bottom.y);\n\n    if (has_titlebar) {\n        if (focus_changed || state_changed) update_title_bar(window);\n        damage_csd(titlebar, decs.titlebar.buffer.front);\n    }\n#define d(which) damage_csd(which, is_focused ? decs.which.buffer.front : decs.which.buffer.back);\n    d(shadow_left); d(shadow_right); d(shadow_top); d(shadow_bottom);\n    d(shadow_upper_left); d(shadow_upper_right); d(shadow_lower_left); d(shadow_lower_right);\n#undef d\n\n    decs.for_window_state.width = window->wl.width;\n    decs.for_window_state.height = window->wl.height;\n    decs.for_window_state.focused = is_focused;\n    decs.for_window_state.toplevel_states = window->wl.current.toplevel_states;\n    return true;\n}\n\nvoid\ncsd_set_visible(_GLFWwindow *window, bool visible) {\n    // When setting to visible will only take effect if window currently has\n    // CSD and will also ensure CSD is of correct size and type for current window.\n    // When hiding CSD simply destroys all CSD surfaces.\n    if (visible) ensure_csd_resources(window); else free_csd_surfaces(window);\n}\n\nvoid\ncsd_free_all_resources(_GLFWwindow *window) {\n    free_csd_surfaces(window);\n    free_csd_buffers(window);\n    if (decs.shadow_tile.data) free(decs.shadow_tile.data);\n    decs.shadow_tile.data = NULL;\n}\n\nbool\ncsd_change_title(_GLFWwindow *window) {\n    if (!window_is_csd_capable(window)) return false;\n    if (ensure_csd_resources(window)) return true;  // CSD were re-rendered for other reasons\n    if (decs.titlebar.surface) {\n        update_title_bar(window);\n        damage_csd(titlebar, decs.titlebar.buffer.front);\n        return true;\n    }\n    return false;\n}\n\nvoid\ncsd_set_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height) {\n    const bool include_space_for_csd = csd_should_window_be_decorated(window);\n    const bool has_titlebar = include_space_for_csd && !decs.titlebar_hidden;\n    bool size_specified_by_compositor = *width > 0 && *height > 0;\n    if (!size_specified_by_compositor) {\n        *width = window->wl.user_requested_content_size.width;\n        *height = window->wl.user_requested_content_size.height;\n        if (window->wl.xdg.top_level_bounds.width > 0) *width = MIN(*width, window->wl.xdg.top_level_bounds.width);\n        if (window->wl.xdg.top_level_bounds.height > 0) *height = MIN(*height, window->wl.xdg.top_level_bounds.height);\n        if (has_titlebar) *height += decs.metrics.visible_titlebar_height;\n    }\n    decs.geometry.x = 0; decs.geometry.y = 0;\n    decs.geometry.width = *width; decs.geometry.height = *height;\n    if (has_titlebar) {\n        decs.geometry.y = -decs.metrics.visible_titlebar_height;\n        *height -= decs.metrics.visible_titlebar_height;\n    }\n}\n\nbool\ncsd_set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color) {\n    bool use_custom_color = !use_system_color;\n    decs.use_custom_titlebar_color = use_custom_color;\n    decs.titlebar_color = color;\n    return csd_change_title(window);\n}\n\n#define x window->wl.allCursorPosX\n#define y window->wl.allCursorPosY\n\nstatic void\nset_cursor(GLFWCursorShape shape, _GLFWwindow* window)\n{\n    if (_glfw.wl.wp_cursor_shape_device_v1) {\n        wayland_cursor_shape s = glfw_cursor_shape_to_wayland_cursor_shape(shape);\n        if (s.which > -1) {\n            debug(\"Changing cursor shape to: %s with serial: %u\\n\", s.name, _glfw.wl.pointer_enter_serial);\n            wp_cursor_shape_device_v1_set_shape(_glfw.wl.wp_cursor_shape_device_v1, _glfw.wl.pointer_enter_serial, (uint32_t)s.which);\n            return;\n        }\n    }\n\n    struct wl_buffer* buffer;\n    struct wl_cursor* cursor;\n    struct wl_cursor_image* image;\n    struct wl_surface* surface = _glfw.wl.cursorSurface;\n    const int scale = _glfwWaylandIntegerWindowScale(window);\n\n    struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale);\n    if (!theme) return;\n    cursor = _glfwLoadCursor(shape, theme);\n    if (!cursor || !cursor->images) return;\n    image = cursor->images[0];\n    if (!image) return;\n    if (image->width % scale || image->height % scale) {\n        static uint32_t warned_width = 0, warned_height = 0;\n        if (warned_width != image->width || warned_height != image->height) {\n            _glfwInputError(GLFW_PLATFORM_ERROR, \"WARNING: Cursor image size: %dx%d is not a multiple of window scale: %d. This will\"\n                    \" cause some compositors such as GNOME to crash. See https://github.com/kovidgoyal/kitty/issues/4878\", image->width, image->height, scale);\n            warned_width = image->width; warned_height = image->height;\n        }\n    }\n\n    buffer = wl_cursor_image_get_buffer(image);\n    if (!buffer) return;\n    debug(\"Calling wl_pointer_set_cursor in set_cursor with surface: %p\\n\", (void*)surface);\n    wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,\n                          surface,\n                          image->hotspot_x / scale,\n                          image->hotspot_y / scale);\n    wl_surface_set_buffer_scale(surface, scale);\n    wl_surface_attach(surface, buffer, 0, 0);\n    wl_surface_damage(surface, 0, 0,\n                      image->width, image->height);\n    wl_surface_commit(surface);\n    _glfw.wl.cursorPreviousShape = shape;\n}\n\n\nstatic bool\nupdate_hovered_button(_GLFWwindow *window) {\n    bool has_hovered_button = false;\n    int scaled_x = (int)round(decs.for_window_state.fscale * x);\n#define c(which) \\\n    if (decs.which.left <= scaled_x && scaled_x < decs.which.left + decs.which.width) { \\\n        has_hovered_button = true; \\\n        if (!decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = true; } \\\n    } else if (decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = false; }\n\n    c(minimize); c(maximize); c(close);\n#undef c\n    update_title_bar(window);\n    return has_hovered_button;\n}\n\nstatic bool\nhas_hovered_button(_GLFWwindow *window) {\n    return decs.minimize.hovered || decs.maximize.hovered || decs.close.hovered;\n}\n\nstatic void\nhandle_pointer_leave(_GLFWwindow *window, struct wl_surface *surface) {\n#define c(which) if (decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = false; }\n    if (surface == decs.titlebar.surface) {\n        c(minimize); c(maximize); c(close);\n    }\n#undef c\n    decs.focus = CENTRAL_WINDOW;\n    decs.dragging = false;\n}\n\n\nstatic void\nhandle_pointer_move(_GLFWwindow *window) {\n    GLFWCursorShape cursorShape = GLFW_DEFAULT_CURSOR;\n    switch (decs.focus)\n    {\n        case CENTRAL_WINDOW: break;\n        case CSD_titlebar: {\n            if (decs.dragging) {\n                if (window->wl.xdg.toplevel) xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial);\n            } else if (update_hovered_button(window)) cursorShape = GLFW_POINTER_CURSOR;\n        } break;\n        case CSD_shadow_top: cursorShape = GLFW_N_RESIZE_CURSOR; break;\n        case CSD_shadow_bottom: cursorShape = GLFW_S_RESIZE_CURSOR; break;\n        case CSD_shadow_left: cursorShape = GLFW_W_RESIZE_CURSOR; break;\n        case CSD_shadow_right: cursorShape = GLFW_E_RESIZE_CURSOR; break;\n        case CSD_shadow_upper_left: cursorShape = GLFW_NW_RESIZE_CURSOR; break;\n        case CSD_shadow_upper_right: cursorShape = GLFW_NE_RESIZE_CURSOR; break;\n        case CSD_shadow_lower_left: cursorShape = GLFW_SW_RESIZE_CURSOR; break;\n        case CSD_shadow_lower_right: cursorShape = GLFW_SE_RESIZE_CURSOR; break;\n    }\n    if (_glfw.wl.cursorPreviousShape != cursorShape) set_cursor(cursorShape, window);\n}\n\nstatic void\nhandle_pointer_enter(_GLFWwindow *window, struct wl_surface *surface) {\n#define Q(which) if (decs.which.surface == surface) { \\\n    decs.focus = CSD_##which; handle_pointer_move(window); return; } // enter is also a move\n\n    all_surfaces(Q)\n#undef Q\n    decs.focus = CENTRAL_WINDOW;\n    decs.dragging = false;\n}\n\nstatic void\nhandle_pointer_button(_GLFWwindow *window, uint32_t button, uint32_t state) {\n    uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;\n    if (button == BTN_LEFT) {\n        switch (decs.focus) {\n            case CENTRAL_WINDOW: break;\n            case CSD_titlebar:\n                if (state == WL_POINTER_BUTTON_STATE_PRESSED) {\n                    monotonic_t last_click_at = decs.last_click_on_top_decoration_at;\n                    decs.last_click_on_top_decoration_at = monotonic();\n                    if (decs.last_click_on_top_decoration_at - last_click_at <= _glfwPlatformGetDoubleClickInterval(window)) {\n                        decs.last_click_on_top_decoration_at = 0;\n                        if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED) _glfwPlatformRestoreWindow(window);\n                        else _glfwPlatformMaximizeWindow(window);\n                        return;\n                    }\n                } else {\n                    if (decs.minimize.hovered) _glfwPlatformIconifyWindow(window);\n                    else if (decs.maximize.hovered) {\n                        if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED) _glfwPlatformRestoreWindow(window);\n                        else _glfwPlatformMaximizeWindow(window);\n                        // hack otherwise on GNOME maximize button remains hovered sometimes\n                        decs.maximize.hovered = false; decs.titlebar_needs_update = true;\n                    } else if (decs.close.hovered) _glfwInputWindowCloseRequest(window);\n                }\n                decs.dragging = !has_hovered_button(window);\n                break;\n            case CSD_shadow_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; break;\n            case CSD_shadow_upper_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; break;\n            case CSD_shadow_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; break;\n            case CSD_shadow_upper_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; break;\n            case CSD_shadow_top: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; break;\n            case CSD_shadow_lower_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; break;\n            case CSD_shadow_bottom: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; break;\n            case CSD_shadow_lower_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; break;\n        }\n        if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial, edges);\n    }\n    else if (button == BTN_RIGHT) {\n        if (decs.focus == CSD_titlebar && window->wl.xdg.toplevel)\n        {\n            if (window->wl.wm_capabilities.window_menu) xdg_toplevel_show_window_menu(\n                    window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial, (int32_t)x, (int32_t)y - decs.metrics.top);\n            else\n                _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland compositor does not support showing wndow menu\");\n            return;\n        }\n    }\n}\n\n\nvoid\ncsd_handle_pointer_event(_GLFWwindow *window, int button, int state, struct wl_surface *surface) {\n    if (!window_is_csd_capable(window)) return;\n    decs.titlebar_needs_update = false;\n    switch (button) {\n        case -1: handle_pointer_move(window); break;\n        case -2: handle_pointer_enter(window, surface); break;\n        case -3: handle_pointer_leave(window, surface); break;\n        default: handle_pointer_button(window, button, state); break;\n    }\n    if (decs.titlebar_needs_update) {\n        csd_change_title(window);\n        if (!window->wl.waiting_for_swap_to_commit) wl_surface_commit(window->wl.surface);\n    }\n}\n#undef x\n#undef y\n"
  },
  {
    "path": "glfw/wl_client_side_decorations.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"internal.h\"\n\nvoid csd_initialize_metrics(_GLFWwindow *window);\nvoid csd_free_all_resources(_GLFWwindow *window);\nbool csd_change_title(_GLFWwindow *window);\nvoid csd_set_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height);\nbool csd_set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color);\nbool csd_should_window_be_decorated(_GLFWwindow *window);\nvoid csd_set_visible(_GLFWwindow *window, bool visible);\nvoid csd_handle_pointer_event(_GLFWwindow *window, int button, int state, struct wl_surface* surface);\n"
  },
  {
    "path": "glfw/wl_cursors.c",
    "content": "// Future devs supporting whatever Wayland protocol stabilizes for cursor selection: see _themeAdd.\n\n#include \"internal.h\"\n#include \"linux_desktop_settings.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nstatic GLFWWLCursorThemes cursor_themes;\n\nstatic int\npixels_from_scale(int scale) {\n    int factor;\n    const char* name;\n    glfw_current_cursor_theme(&name, &factor);\n    return factor * scale;\n}\n\n\nstruct wl_cursor_theme*\nglfw_wlc_theme_for_scale(int scale) {\n    for (size_t i = 0; i < cursor_themes.count; i++) {\n        if (cursor_themes.themes[i].scale == scale) return cursor_themes.themes[i].theme;\n    }\n\n    if (cursor_themes.count >= cursor_themes.capacity) {\n        cursor_themes.themes = realloc(cursor_themes.themes, sizeof(GLFWWLCursorTheme) * (cursor_themes.count + 16));\n        if (!cursor_themes.themes) {\n            _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Out of memory allocating space for cursor themes\");\n            return NULL;\n        }\n        cursor_themes.capacity = cursor_themes.count + 16;\n    }\n    int factor;\n    const char* name;\n    glfw_current_cursor_theme(&name, &factor);\n    struct wl_cursor_theme *ans = wl_cursor_theme_load(name, pixels_from_scale(scale), _glfw.wl.shm);\n    if (!ans) {\n        _glfwInputError(\n            GLFW_PLATFORM_ERROR, \"Wayland: wl_cursor_theme_load failed at scale: %d pixels: %d\",\n            scale, pixels_from_scale(scale)\n        );\n        return NULL;\n    }\n    GLFWWLCursorTheme *theme = cursor_themes.themes + cursor_themes.count++;\n    theme->scale = scale;\n    theme->theme = ans;\n    return ans;\n}\n\nvoid\nglfw_wlc_destroy(void) {\n    for (size_t i = 0; i < cursor_themes.count; i++) {\n        wl_cursor_theme_destroy(cursor_themes.themes[i].theme);\n    }\n    free(cursor_themes.themes);\n    cursor_themes.themes = NULL; cursor_themes.capacity = 0; cursor_themes.count = 0;\n}\n"
  },
  {
    "path": "glfw/wl_cursors.h",
    "content": "// Declarations for a HiDPI-aware cursor theme manager.\n\n#include <wayland-cursor.h>\n\ntypedef struct {\n    struct wl_cursor_theme *theme;\n    int scale;\n} GLFWWLCursorTheme;\n\n\ntypedef struct {\n    GLFWWLCursorTheme *themes;\n    size_t count, capacity;\n} GLFWWLCursorThemes;\n\n\nstruct wl_cursor_theme* glfw_wlc_theme_for_scale(int scale);\nvoid glfw_wlc_destroy(void);\n"
  },
  {
    "path": "glfw/wl_init.c",
    "content": "//========================================================================\n// GLFW 3.4 Wayland - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#define _GNU_SOURCE\n#include \"internal.h\"\n#include \"backend_utils.h\"\n#include \"wl_client_side_decorations.h\"\n#include \"linux_desktop_settings.h\"\n#include \"../kitty/monotonic.h\"\n#include \"wl_text_input.h\"\n#include \"wayland-text-input-unstable-v3-client-protocol.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/socket.h>\n#include <wayland-client.h>\n#include <stdio.h>\n// errno.h needed for BSD code paths\n#include <errno.h>\n// Needed for the BTN_* defines\n#ifdef __has_include\n#if __has_include(<linux/input.h>)\n#include <linux/input.h>\n#elif __has_include(<dev/evdev/input.h>)\n#include <dev/evdev/input.h>\n#endif\n#else\n#include <linux/input.h>\n#endif\n\n#define debug debug_rendering\n\n#define x window->wl.allCursorPosX\n#define y window->wl.allCursorPosY\n\nstatic _GLFWwindow*\nget_window_from_surface(struct wl_surface* surface) {\n    if (!surface) return NULL;\n    _GLFWwindow *ans = wl_surface_get_user_data(surface);\n    if (ans) {\n        const _GLFWwindow *w = _glfw.windowListHead;\n        while (w) {\n            if (w == ans) return ans;\n            w = w->next;\n        }\n    }\n    return NULL;\n}\n\nstatic void\npointerHandleEnter(\n        void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface,\n        wl_fixed_t sx, wl_fixed_t sy\n) {\n    _GLFWwindow* window = get_window_from_surface(surface);\n    if (!window) return;\n    _glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.pointer_serial = serial; _glfw.wl.pointer_enter_serial = serial;\n    _glfw.wl.pointerFocus = window;\n    window->wl.allCursorPosX = wl_fixed_to_double(sx);\n    window->wl.allCursorPosY = wl_fixed_to_double(sy);\n    if (surface != window->wl.surface) {\n        csd_handle_pointer_event(window, -2, -2, surface);\n    } else {\n        window->wl.decorations.focus = CENTRAL_WINDOW;\n        window->wl.hovered = true;\n        window->wl.cursorPosX = x;\n        window->wl.cursorPosY = y;\n        _glfwPlatformSetCursor(window, window->wl.currentCursor);\n        _glfwInputCursorEnter(window, true);\n    }\n}\n\nstatic void\npointerHandleLeave(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface) {\n    _GLFWwindow* window = _glfw.wl.pointerFocus;\n    if (!window) return;\n    _glfw.wl.serial = serial;\n    _glfw.wl.pointerFocus = NULL;\n    if (window->wl.surface == surface) {\n        window->wl.hovered = false;\n        _glfwInputCursorEnter(window, false);\n        _glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;\n    } else csd_handle_pointer_event(window, -3, -3, surface);\n}\n\nstatic void\npointerHandleMotion(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t time UNUSED, wl_fixed_t sx, wl_fixed_t sy) {\n    _GLFWwindow* window = _glfw.wl.pointerFocus;\n    if (!window || window->cursorMode == GLFW_CURSOR_DISABLED) return;\n    window->wl.allCursorPosX = wl_fixed_to_double(sx);\n    window->wl.allCursorPosY = wl_fixed_to_double(sy);\n    if (window->wl.decorations.focus != CENTRAL_WINDOW) {\n        csd_handle_pointer_event(window, -1, -1, NULL);\n    } else {\n        window->wl.cursorPosX = x;\n        window->wl.cursorPosY = y;\n        _glfwInputCursorPos(window, x, y);\n        _glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;\n    }\n}\n\nstatic void pointerHandleButton(void* data UNUSED,\n                                struct wl_pointer* pointer UNUSED,\n                                uint32_t serial,\n                                uint32_t time UNUSED,\n                                uint32_t button,\n                                uint32_t state)\n{\n    glfw_cancel_momentum_scroll();\n    _glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.pointer_serial = serial;\n\n    _GLFWwindow* window = _glfw.wl.pointerFocus;\n    if (!window) return;\n    if (window->wl.decorations.focus != CENTRAL_WINDOW) {\n        csd_handle_pointer_event(window, button, state, NULL);\n        return;\n    }\n    /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev\n     * codes. */\n    int glfwButton = button - BTN_LEFT;\n    _glfwInputMouseClick(\n            window, glfwButton, state == WL_POINTER_BUTTON_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE, _glfw.wl.xkb.states.modifiers);\n}\n#undef x\n#undef y\n\n#define info (window->wl.pointer_curr_axis_info)\n\nstatic void\npointer_handle_axis_common(enum _GLFWWaylandAxisEvent type, uint32_t axis, wl_fixed_t value) {\n    _GLFWwindow* window = _glfw.wl.pointerFocus;\n    if (!window || window->wl.decorations.focus != CENTRAL_WINDOW) return;\n    float fval = (float) wl_fixed_to_double(value);\n\n#define CASE(type, type_const, axis, fval) \\\n    case type_const: \\\n        if (info.type.axis##_axis_type == AXIS_EVENT_UNKNOWN) { \\\n        info.type.axis##_axis_type = type_const; info.type.axis = 0.f; } \\\n        info.type.axis += fval; break;\n\n    if (window) {\n        switch ((enum wl_pointer_axis)axis) {\n        case WL_POINTER_AXIS_VERTICAL_SCROLL:\n            switch (type) {\n                case AXIS_EVENT_UNKNOWN: break;\n                CASE(discrete, AXIS_EVENT_DISCRETE, y, -fval); // wheel event\n                CASE(discrete, AXIS_EVENT_VALUE120, y, -fval);  // wheel event higher res than plain discrete\n                CASE(continuous, AXIS_EVENT_CONTINUOUS, y, -fval);  // touchpad, etc. high res\n            }\n            break;\n        case WL_POINTER_AXIS_HORIZONTAL_SCROLL:\n            switch (type) {\n                case AXIS_EVENT_UNKNOWN: break;\n                CASE(discrete, AXIS_EVENT_DISCRETE, x, fval); // wheel event\n                CASE(discrete, AXIS_EVENT_VALUE120, x, fval);  // wheel event higher res than plain discrete\n                CASE(continuous, AXIS_EVENT_CONTINUOUS, x, fval);  // touchpad, etc. high res\n            }\n            break;\n        }\n    }\n#undef CASE\n}\n\nstatic void\npointer_handle_axis(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t time, uint32_t axis, wl_fixed_t value) {\n    _GLFWwindow* window = _glfw.wl.pointerFocus;\n    if (!window) return;\nswitch (axis) {\n        case WL_POINTER_AXIS_VERTICAL_SCROLL: if (!info.y_start_time) info.y_start_time = ms_to_monotonic_t(time); break;\n        case WL_POINTER_AXIS_HORIZONTAL_SCROLL: if (!info.x_start_time) info.x_start_time = ms_to_monotonic_t(time); break;\n    }\n    pointer_handle_axis_common(AXIS_EVENT_CONTINUOUS, axis, value);\n}\n\nstatic void\npointer_handle_frame(void *data UNUSED, struct wl_pointer *pointer UNUSED) {\n    _GLFWwindow* window = _glfw.wl.pointerFocus;\n    if (!window) return;\n    GLFWScrollEvent ev = {.keyboard_modifiers=_glfw.wl.xkb.states.modifiers};\n    bool found = false;\n\n    if (info.discrete.y_axis_type != AXIS_EVENT_UNKNOWN) {\n        ev.unscaled.y = info.discrete.y;\n        if (info.discrete.y_axis_type == AXIS_EVENT_VALUE120) ev.offset_type = GLFW_SCROLL_OFFEST_V120;\n        found = true;\n    } else if (info.continuous.y_axis_type != AXIS_EVENT_UNKNOWN) {\n        ev.offset_type = GLFW_SCROLL_OFFEST_HIGHRES;\n        ev.unscaled.y = info.continuous.y;\n        found = true;\n    }\n\n    if (info.discrete.x_axis_type != AXIS_EVENT_UNKNOWN) {\n        ev.unscaled.x = info.discrete.x;\n        if (info.discrete.x_axis_type == AXIS_EVENT_VALUE120) ev.offset_type = GLFW_SCROLL_OFFEST_V120;\n        found = true;\n    } else if (info.continuous.x_axis_type != AXIS_EVENT_UNKNOWN) {\n        ev.offset_type = GLFW_SCROLL_OFFEST_HIGHRES;\n        ev.unscaled.x = info.continuous.x;\n        found = true;\n    }\n    bool stopped = info.y_stop_received || info.x_stop_received;\n    if (!found && stopped) ev.offset_type = window->wl.prev_frame_offset_type;\n    ev.unscaled.x *= -1;\n    const double scale = ev.offset_type == GLFW_SCROLL_OFFEST_HIGHRES ? _glfwWaylandWindowScale(window) : 1;\n    ev.x_offset = scale * ev.unscaled.x; ev.y_offset = scale * ev.unscaled.y;\n    glfw_handle_scroll_event_for_momentum(\n        window, &ev, stopped, info.source_type == WL_POINTER_AXIS_SOURCE_FINGER);\n    window->wl.prev_frame_offset_type = ev.offset_type;\n    /* clear pointer_curr_axis_info for next frame */\n    memset(&info, 0, sizeof(info));\n}\n\nstatic void\npointer_handle_axis_source(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t source) {\n    _GLFWwindow* window = _glfw.wl.pointerFocus;\n    if (!window) return;\n    info.source_type = source;\n}\n\nstatic void\npointer_handle_axis_stop(void *data UNUSED, struct wl_pointer *wl_pointer UNUSED, uint32_t time UNUSED, uint32_t axis) {\n    _GLFWwindow* window = _glfw.wl.pointerFocus;\n    if (!window) return;\n    switch (axis) {\n        case WL_POINTER_AXIS_VERTICAL_SCROLL:\n            info.y_stop_received = true;\n            info.y_stop_time = ms_to_monotonic_t(time);\n            break;\n        case WL_POINTER_AXIS_HORIZONTAL_SCROLL:\n            info.x_stop_received = true;\n            info.x_stop_time = ms_to_monotonic_t(time);\n            break;\n    }\n}\n\n\nstatic void\npointer_handle_axis_discrete(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis, int32_t discrete) {\n    pointer_handle_axis_common(AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete));\n}\n\nstatic void\npointer_handle_axis_value120(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis, int32_t value120) {\n    pointer_handle_axis_common(AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120));\n}\n\nstatic void\npointer_handle_axis_relative_direction(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis UNUSED, uint32_t axis_relative_direction UNUSED) { }\n\n#undef info\nstatic const struct wl_pointer_listener pointerListener = {\n    .enter = pointerHandleEnter,\n    .leave = pointerHandleLeave,\n    .motion = pointerHandleMotion,\n    .button = pointerHandleButton,\n\n    .axis = pointer_handle_axis,\n    .frame = pointer_handle_frame,\n    .axis_source = pointer_handle_axis_source,\n    .axis_stop = pointer_handle_axis_stop,\n    .axis_discrete = pointer_handle_axis_discrete,\n#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION\n    .axis_value120 = pointer_handle_axis_value120,\n#endif\n#ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION\n    .axis_relative_direction = pointer_handle_axis_relative_direction,\n#endif\n};\n\nstatic void keyboardHandleKeymap(void* data UNUSED,\n                                 struct wl_keyboard* keyboard UNUSED,\n                                 uint32_t format,\n                                 int fd,\n                                 uint32_t size)\n{\n    char* mapStr;\n\n    if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Unknown keymap format: %u\", format);\n        close(fd);\n        return;\n    }\n\n    mapStr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);\n    if (mapStr == MAP_FAILED) {\n        close(fd);\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Mapping of keymap file descriptor failed: %u\", format);\n        return;\n    }\n    glfw_xkb_compile_keymap(&_glfw.wl.xkb, mapStr);\n    munmap(mapStr, size);\n    close(fd);\n\n}\n\nstatic bool\nneeds_synthetic_key_repeat(void) { return _glfw.wl.keyboardRepeatRate > 0 && !_glfw.wl.has_key_repeat_events; }\n\nstatic void\nstart_key_repeat_timer(bool initial) {\n#ifdef HAS_TIMER_FD\n    (void)initial;\n    struct itimerspec new_value = {.it_value={.tv_nsec = _glfw.wl.keyboardRepeatDelay}, .it_interval={.tv_nsec = (s_to_monotonic_t(1ll) / (monotonic_t)_glfw.wl.keyboardRepeatRate)}};\n    if (_glfw.wl.eventLoopData.key_repeat_fd > -1) timerfd_settime(\n            _glfw.wl.eventLoopData.key_repeat_fd, 0, &new_value, NULL);\n#else\n    monotonic_t interval = _glfw.wl.keyboardRepeatDelay;\n    if (!initial) interval = (s_to_monotonic_t(1ll) / (monotonic_t)_glfw.wl.keyboardRepeatRate);\n    changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, interval);\n    toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);\n#endif\n}\n\nstatic void\nstop_key_repeat_timer(void) {\n#ifdef HAS_TIMER_FD\n    struct itimerspec new_value = {0};\n    if (_glfw.wl.eventLoopData.key_repeat_fd > -1) timerfd_settime(_glfw.wl.eventLoopData.key_repeat_fd, 0, &new_value, NULL);\n#else\n    toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);\n#endif\n}\n\n#ifndef HAS_TIMER_FD\nstatic void\nsend_key_repeat_timer_event(id_type timer_id UNUSED, void *data UNUSED) {\n    char b = 1;\n    b += write(_glfw.wl.eventLoopData.key_repeat_fds[1], &b, 1);\n    if (needs_synthetic_key_repeat()) start_key_repeat_timer(false);\n}\n#endif\n\nstatic void keyboardHandleEnter(void* data UNUSED,\n                                struct wl_keyboard* keyboard UNUSED,\n                                uint32_t serial,\n                                struct wl_surface* surface,\n                                struct wl_array* keys)\n{\n    _GLFWwindow* window = get_window_from_surface(surface);\n    if (!window) return;\n\n    _glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.keyboard_enter_serial = serial;\n    _glfw.wl.keyboardFocusId = window->id;\n    _glfwInputWindowFocus(window, true);\n    uint32_t* key;\n    if (keys && _glfw.wl.keyRepeatInfo.key) {\n        wl_array_for_each(key, keys) {\n            if (*key == _glfw.wl.keyRepeatInfo.key) {\n                if (needs_synthetic_key_repeat()) start_key_repeat_timer(true);\n                break;\n            }\n        }\n    }\n}\n\nstatic void keyboardHandleLeave(void* data UNUSED,\n                                struct wl_keyboard* keyboard UNUSED,\n                                uint32_t serial,\n                                struct wl_surface* surface UNUSED)\n{\n    _GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);\n\n    if (!window)\n        return;\n\n    _glfw.wl.serial = serial;\n    _glfw.wl.keyboardFocusId = 0;\n    _glfwInputWindowFocus(window, false);\n    stop_key_repeat_timer();\n}\n\nstatic void keyboardHandleKey(void* data UNUSED,\n                              struct wl_keyboard* keyboard UNUSED,\n                              uint32_t serial,\n                              uint32_t time UNUSED,\n                              uint32_t key,\n                              uint32_t state)\n{\n    glfw_cancel_momentum_scroll();\n    _GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);\n    if (!window)\n        return;\n    int action = GLFW_PRESS;\n    switch (state) {\n        case WL_KEYBOARD_KEY_STATE_PRESSED: action = GLFW_PRESS; break;\n        case WL_KEYBOARD_KEY_STATE_RELEASED: action = GLFW_RELEASE; break;\n#ifdef WL_KEYBOARD_KEY_STATE_REPEATED_SINCE_VERSION\n        case WL_KEYBOARD_KEY_STATE_REPEATED: action = GLFW_REPEAT; break;\n#endif\n    }\n\n    _glfw.wl.serial = serial; _glfw.wl.input_serial = serial;\n    glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, key, action);\n    if (action == GLFW_PRESS && needs_synthetic_key_repeat() && glfw_xkb_should_repeat(&_glfw.wl.xkb, key))\n    {\n        _glfw.wl.keyRepeatInfo.key = key;\n        _glfw.wl.keyRepeatInfo.keyboardFocusId = window->id;\n        start_key_repeat_timer(true);\n    } else if (action == GLFW_RELEASE && key == _glfw.wl.keyRepeatInfo.key) {\n        _glfw.wl.keyRepeatInfo.key = 0;\n        stop_key_repeat_timer();\n    }\n}\n\nstatic void keyboardHandleModifiers(void* data UNUSED,\n                                    struct wl_keyboard* keyboard UNUSED,\n                                    uint32_t serial,\n                                    uint32_t modsDepressed,\n                                    uint32_t modsLatched,\n                                    uint32_t modsLocked,\n                                    uint32_t group)\n{\n    _glfw.wl.serial = serial; _glfw.wl.input_serial = serial;\n    glfw_xkb_update_modifiers(&_glfw.wl.xkb, modsDepressed, modsLatched, modsLocked, 0, 0, group);\n}\n\nstatic void keyboardHandleRepeatInfo(void* data UNUSED,\n                                     struct wl_keyboard* keyboard,\n                                     int32_t rate,\n                                     int32_t delay)\n{\n    if (keyboard != _glfw.wl.keyboard) return;\n    _glfw.wl.keyboardRepeatRate = rate;\n    _glfw.wl.keyboardRepeatDelay = ms_to_monotonic_t(delay);\n}\n\nstatic const struct wl_keyboard_listener keyboardListener = {\n    keyboardHandleKeymap,\n    keyboardHandleEnter,\n    keyboardHandleLeave,\n    keyboardHandleKey,\n    keyboardHandleModifiers,\n    keyboardHandleRepeatInfo,\n};\n\nstatic void seatHandleCapabilities(void* data UNUSED,\n                                   struct wl_seat* seat,\n                                   enum wl_seat_capability caps)\n{\n    if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer)\n    {\n        _glfw.wl.pointer = wl_seat_get_pointer(seat);\n        wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL);\n        if (_glfw.wl.wp_cursor_shape_manager_v1) {\n            if (_glfw.wl.wp_cursor_shape_device_v1) wp_cursor_shape_device_v1_destroy(_glfw.wl.wp_cursor_shape_device_v1);\n            _glfw.wl.wp_cursor_shape_device_v1 = NULL;\n            _glfw.wl.wp_cursor_shape_device_v1 = wp_cursor_shape_manager_v1_get_pointer(_glfw.wl.wp_cursor_shape_manager_v1, _glfw.wl.pointer);\n        }\n    }\n    else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)\n    {\n        if (_glfw.wl.wp_cursor_shape_device_v1) wp_cursor_shape_device_v1_destroy(_glfw.wl.wp_cursor_shape_device_v1);\n        _glfw.wl.wp_cursor_shape_device_v1 = NULL;\n        wl_pointer_destroy(_glfw.wl.pointer);\n        _glfw.wl.pointer = NULL;\n        if (_glfw.wl.cursorAnimationTimer) toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);\n    }\n\n    if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)\n    {\n        _glfw.wl.keyboard = wl_seat_get_keyboard(seat);\n        wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL);\n    }\n    else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)\n    {\n        wl_keyboard_destroy(_glfw.wl.keyboard);\n        _glfw.wl.keyboard = NULL;\n        _glfw.wl.keyboardFocusId = 0;\n        stop_key_repeat_timer();\n    }\n}\n\nstatic void seatHandleName(void* data UNUSED,\n                           struct wl_seat* seat UNUSED,\n                           const char* name UNUSED)\n{\n}\n\nstatic const struct wl_seat_listener seatListener = {\n    seatHandleCapabilities,\n    seatHandleName,\n};\nstatic void wmBaseHandlePing(void* data UNUSED,\n                             struct xdg_wm_base* wmBase,\n                             uint32_t serial)\n{\n    xdg_wm_base_pong(wmBase, serial);\n}\n\nstatic const struct xdg_wm_base_listener wmBaseListener = {\n    wmBaseHandlePing\n};\n\nstatic void extBackgroundEffectHandleCapabilities(void* data UNUSED,\n                                                  struct ext_background_effect_manager_v1* manager UNUSED,\n                                                  uint32_t flags)\n{\n    _glfw.wl.ext_background_effect_capabilities = flags;\n}\n\nstatic const struct ext_background_effect_manager_v1_listener extBackgroundEffectManagerListener = {\n    extBackgroundEffectHandleCapabilities\n};\n\nstatic void registryHandleGlobal(void* data UNUSED,\n                                 struct wl_registry* registry,\n                                 uint32_t name,\n                                 const char* interface,\n                                 uint32_t version)\n{\n#define is(x) strcmp(interface, x##_interface.name) == 0\n    if (is(wl_compositor))\n    {\n#ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION\n        _glfw.wl.compositorVersion = MIN(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION, (int)version);\n        _glfw.wl.has_preferred_buffer_scale = _glfw.wl.compositorVersion >= WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION;\n#else\n        _glfw.wl.compositorVersion = MIN(3, (int)version);\n#endif\n        _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, _glfw.wl.compositorVersion);\n    }\n    else if (is(wl_subcompositor))\n    {\n        _glfw.wl.subcompositor =\n            wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);\n    }\n    else if (is(wl_shm))\n    {\n        _glfw.wl.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);\n    }\n    else if (is(wl_output))\n    {\n        _glfwAddOutputWayland(name, version);\n    }\n    else if (is(wl_seat))\n    {\n        if (!_glfw.wl.seat)\n        {\n            _glfw.wl.has_key_repeat_events = false;\n#if defined(WL_KEYBOARD_KEY_STATE_REPEATED_SINCE_VERSION)\n            _glfw.wl.seatVersion = MIN(WL_KEYBOARD_KEY_STATE_REPEATED_SINCE_VERSION, (int)version);\n            _glfw.wl.has_key_repeat_events = _glfw.wl.seatVersion >= WL_KEYBOARD_KEY_STATE_REPEATED_SINCE_VERSION;\n#elif defined(WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION)\n            _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION, (int)version);\n#elif defined(WL_POINTER_AXIS_VALUE120_SINCE_VERSION)\n            _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_VALUE120_SINCE_VERSION, (int)version);\n#else\n            _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_DISCRETE_SINCE_VERSION, version);\n#endif\n            _glfw.wl.seat =\n                wl_registry_bind(registry, name, &wl_seat_interface,\n                                 _glfw.wl.seatVersion);\n            wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);\n        }\n        if (_glfw.wl.seat) {\n            if (_glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) _glfwSetupWaylandDataDevice();\n            if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {\n                _glfwSetupWaylandPrimarySelectionDevice();\n            }\n        }\n    }\n    else if (is(xdg_wm_base))\n    {\n        _glfw.wl.xdg_wm_base_version = 1;\n#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION\n        _glfw.wl.xdg_wm_base_version = MIN(XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION, (int)version);\n#elif defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)\n        _glfw.wl.xdg_wm_base_version = MIN(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION, (int)version);\n#endif\n        _glfw.wl.wmBase = wl_registry_bind(registry, name, &xdg_wm_base_interface, _glfw.wl.xdg_wm_base_version);\n        xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);\n    }\n    else if (is(zxdg_decoration_manager_v1))\n    {\n        _glfw.wl.decorationManager =\n        wl_registry_bind(registry, name,\n            &zxdg_decoration_manager_v1_interface, 1);\n    }\n    else if (is(zwp_relative_pointer_manager_v1))\n    {\n        _glfw.wl.relativePointerManager =\n            wl_registry_bind(registry, name,\n                             &zwp_relative_pointer_manager_v1_interface,\n                             1);\n    }\n    else if (is(zwp_pointer_constraints_v1))\n    {\n        _glfw.wl.pointerConstraints =\n            wl_registry_bind(registry, name,\n                             &zwp_pointer_constraints_v1_interface,\n                             1);\n    }\n    else if (is(zwp_text_input_manager_v3))\n    {\n        _glfwWaylandBindTextInput(registry, name);\n    }\n    else if (is(wl_data_device_manager))\n    {\n        _glfw.wl.dataDeviceManager =\n            wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3);\n        if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {\n            _glfwSetupWaylandDataDevice();\n        }\n    }\n    else if (is(zwp_primary_selection_device_manager_v1))\n    {\n        _glfw.wl.primarySelectionDeviceManager =\n            wl_registry_bind(registry, name,\n                             &zwp_primary_selection_device_manager_v1_interface,\n                             1);\n        if (_glfw.wl.seat && _glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {\n            _glfwSetupWaylandPrimarySelectionDevice();\n        }\n    }\n    else if (is(wp_single_pixel_buffer_manager_v1)) {\n        _glfw.wl.wp_single_pixel_buffer_manager_v1 = wl_registry_bind(registry, name, &wp_single_pixel_buffer_manager_v1_interface, 1);\n    }\n    else if (is(xdg_activation_v1)) {\n        _glfw.wl.xdg_activation_v1 = wl_registry_bind(registry, name, &xdg_activation_v1_interface, 1);\n    }\n    else if (is(wp_cursor_shape_manager_v1)) {\n        _glfw.wl.wp_cursor_shape_manager_v1 = wl_registry_bind(registry, name, &wp_cursor_shape_manager_v1_interface, 1);\n    }\n    else if (is(wp_fractional_scale_manager_v1)) {\n        _glfw.wl.wp_fractional_scale_manager_v1 = wl_registry_bind(registry, name, &wp_fractional_scale_manager_v1_interface, 1);\n    }\n    else if (is(wp_viewporter)) {\n        _glfw.wl.wp_viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1);\n    }\n    else if (is(org_kde_kwin_blur_manager)) {\n        _glfw.wl.org_kde_kwin_blur_manager = wl_registry_bind(registry, name, &org_kde_kwin_blur_manager_interface, 1);\n    }\n    else if (is(ext_background_effect_manager_v1)) {\n        _glfw.wl.ext_background_effect_manager_v1 = wl_registry_bind(registry, name, &ext_background_effect_manager_v1_interface, 1);\n        ext_background_effect_manager_v1_add_listener(_glfw.wl.ext_background_effect_manager_v1, &extBackgroundEffectManagerListener, NULL);\n    }\n    else if (is(zwlr_layer_shell_v1)) {\n        if (version >= 4) {\n            _glfw.wl.zwlr_layer_shell_v1_version = version;\n            _glfw.wl.zwlr_layer_shell_v1 = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version);\n        }\n    }\n    else if (is(zwp_idle_inhibit_manager_v1)) {\n        _glfw.wl.idle_inhibit_manager = wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);\n    }\n    else if (is(zwp_keyboard_shortcuts_inhibit_manager_v1)) {\n        _glfw.wl.keyboard_shortcuts_inhibit_manager = wl_registry_bind(registry, name, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);\n    }\n    else if (is(xdg_toplevel_icon_manager_v1)) {\n        _glfw.wl.xdg_toplevel_icon_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_icon_manager_v1_interface, 1);\n    }\n    else if (is(xdg_system_bell_v1)) {\n        _glfw.wl.xdg_system_bell_v1 = wl_registry_bind(registry, name, &xdg_system_bell_v1_interface, 1);\n    } else if (is(xdg_toplevel_tag_manager_v1)) {\n        _glfw.wl.xdg_toplevel_tag_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_tag_manager_v1_interface, 1);\n    } else if (is(xdg_toplevel_drag_manager_v1)) {\n        _glfw.wl.xdg_toplevel_drag_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_drag_manager_v1_interface, 1);\n    }\n#undef is\n}\n\nstatic void registryHandleGlobalRemove(void *data UNUSED,\n                                       struct wl_registry *registry UNUSED,\n                                       uint32_t name)\n{\n    _GLFWmonitor* monitor;\n\n    for (int i = 0; i < _glfw.monitorCount; ++i)\n    {\n        monitor = _glfw.monitors[i];\n        if (monitor->wl.name == name)\n        {\n            for (_GLFWwindow *window = _glfw.windowListHead;  window;  window = window->next) {\n                for (int m = window->wl.monitorsCount - 1; m >= 0; m--) {\n                    if (window->wl.monitors[m] == monitor) {\n                        remove_i_from_array(window->wl.monitors, m, window->wl.monitorsCount);\n                    }\n                }\n            }\n            _glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0);\n            return;\n        }\n    }\n}\n\n\nstatic const struct wl_registry_listener registryListener = {\n    registryHandleGlobal,\n    registryHandleGlobalRemove\n};\n\nGLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {\n    return glfw_current_system_color_theme(query_if_unintialized);\n}\n\nstatic pid_t\nget_socket_peer_pid(int fd) {\n    (void)fd;\n#ifdef __linux__\n    struct ucred ucred;\n    socklen_t len = sizeof(struct ucred);\n    return (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) ? -1 : ucred.pid;\n#elif defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)\n    struct xucred peercred;\n\tsocklen_t peercredlen = sizeof(peercred);\n    return (getsockopt(c->fd, LOCAL_PEERCRED, 1, (void *)&peercred, &peercredlen) == 0 && peercred.cr_version == XUCRED_VERSION) ? peercred.cr_pid : -1;\n#elif defined(LOCAL_PEERPID)\n    pid_t pid;\n    socklen_t pid_size = sizeof(pid);\n    return getsockopt(client, SOL_LOCAL, LOCAL_PEERPID, &pid, &pid_size) == -1 ? -1 : pid;\n#else\n    errno = ENOSYS;\n    return -1;\n#endif\n}\n\nGLFWAPI pid_t glfwWaylandCompositorPID(void) {\n    if (!_glfw.wl.display) return -1;\n    int fd = wl_display_get_fd(_glfw.wl.display);\n    if (fd < 0) return -1;\n    return get_socket_peer_pid(fd);\n}\n\nconst char*\n_glfwWaylandCompositorName(void) {\n    static bool probed = false;\n    if (!probed) {\n        probed = true;\n        static const size_t sz = 1024;\n        _glfw.wl.compositor_name = malloc(sz);\n        if (!_glfw.wl.compositor_name) return \"\";\n        char *ans = _glfw.wl.compositor_name; ans[0] = 0;\n        pid_t cpid = glfwWaylandCompositorPID();\n        if (cpid < 0) return ans;\n        snprintf(ans, sz, \"/proc/%d/cmdline\", cpid);\n        int fd = open(ans, O_RDONLY | O_CLOEXEC);\n        if (fd < 0) {\n            ans[0] = 0;\n        } else {\n            ssize_t n;\n            while (true) {\n                n = read(fd, ans, sz-1);\n                if (n < 0 && errno == EINTR) continue;\n                close(fd); break;\n            }\n            ans[n < 0 ? 0 : n] = 0;\n        }\n    }\n    return _glfw.wl.compositor_name ? _glfw.wl.compositor_name : \"\";\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nstatic const char*\nget_compositor_missing_capabilities(void) {\n#define C(title, x) if (!_glfw.wl.x) p += snprintf(p, sizeof(buf) - (p - buf), \"%s \", #title);\n    static char buf[512];\n    char *p = buf;\n    *p = 0;\n    C(viewporter, wp_viewporter); C(fractional_scale, wp_fractional_scale_manager_v1);\n    if (!_glfw.wl.org_kde_kwin_blur_manager && !_glfw.wl.ext_background_effect_manager_v1) p += snprintf(p, sizeof(buf) - (p - buf), \"%s \", \"blur\");\n    C(server_side_decorations, decorationManager);\n    C(cursor_shape, wp_cursor_shape_manager_v1); C(layer_shell, zwlr_layer_shell_v1);\n    C(single_pixel_buffer, wp_single_pixel_buffer_manager_v1); C(preferred_scale, has_preferred_buffer_scale);\n    C(idle_inhibit, idle_inhibit_manager); C(icon, xdg_toplevel_icon_manager_v1); C(bell, xdg_system_bell_v1);\n    C(window-tag, xdg_toplevel_tag_manager_v1); C(keyboard_shortcuts_inhibit, keyboard_shortcuts_inhibit_manager);\n    C(key-repeat, has_key_repeat_events); C(top_level_drag, xdg_toplevel_drag_manager_v1);\n#define P(x) p += snprintf(p, sizeof(buf) - (p - buf), \"%s \", x);\n    if (_glfw.wl.xdg_wm_base_version < 6) P(\"window-state-suspended\");\n    if (_glfw.wl.xdg_wm_base_version < 5) P(\"window-capabilities\");\n#undef P\n#undef C\n    while (p > buf && (p - 1)[0] == ' ') { p--; *p = 0; }\n    return buf;\n}\n\nGLFWAPI const char* glfwWaylandMissingCapabilities(void) { return get_compositor_missing_capabilities(); }\n\nint _glfwPlatformInit(bool *supports_window_occlusion)\n{\n    int i;\n    _GLFWmonitor* monitor;\n\n    _glfw.wl.cursor.handle = _glfw_dlopen(\"libwayland-cursor.so.0\");\n    if (!_glfw.wl.cursor.handle)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Failed to open libwayland-cursor\");\n        return false;\n    }\n\n    glfw_dlsym(_glfw.wl.cursor.theme_load, _glfw.wl.cursor.handle, \"wl_cursor_theme_load\");\n    glfw_dlsym(_glfw.wl.cursor.theme_destroy, _glfw.wl.cursor.handle, \"wl_cursor_theme_destroy\");\n    glfw_dlsym(_glfw.wl.cursor.theme_get_cursor, _glfw.wl.cursor.handle, \"wl_cursor_theme_get_cursor\");\n    glfw_dlsym(_glfw.wl.cursor.image_get_buffer, _glfw.wl.cursor.handle, \"wl_cursor_image_get_buffer\");\n\n    _glfw.wl.egl.handle = _glfw_dlopen(\"libwayland-egl.so.1\");\n    if (!_glfw.wl.egl.handle)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Failed to open libwayland-egl\");\n        return false;\n    }\n\n    glfw_dlsym(_glfw.wl.egl.window_create, _glfw.wl.egl.handle, \"wl_egl_window_create\");\n    glfw_dlsym(_glfw.wl.egl.window_destroy, _glfw.wl.egl.handle, \"wl_egl_window_destroy\");\n    glfw_dlsym(_glfw.wl.egl.window_resize, _glfw.wl.egl.handle, \"wl_egl_window_resize\");\n\n    _glfw.wl.display = wl_display_connect(NULL);\n    if (!_glfw.wl.display)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Failed to connect to display\");\n        return false;\n    }\n    if (!initPollData(&_glfw.wl.eventLoopData, wl_display_get_fd(_glfw.wl.display))) {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Failed to initialize event loop data\");\n    }\n    glfw_dbus_init(&_glfw.wl.dbus, &_glfw.wl.eventLoopData);\n    glfw_initialize_desktop_settings();\n#ifndef HAS_TIMER_FD\n    _glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, \"wayland-key-repeat\", ms_to_monotonic_t(500ll), 0, true, send_key_repeat_timer_event, NULL, NULL);\n#endif\n    _glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, \"wayland-cursor-animation\", ms_to_monotonic_t(500ll), 0, true, animateCursorImage, NULL, NULL);\n\n    _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);\n    wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL);\n\n    if (!glfw_xkb_create_context(&_glfw.wl.xkb)) return false;\n\n    // Sync so we got all registry objects\n    wl_display_roundtrip(_glfw.wl.display);\n    _glfwWaylandInitTextInput();\n\n    // Sync so we got all initial output events\n    wl_display_roundtrip(_glfw.wl.display);\n\n    for (i = 0; i < _glfw.monitorCount; ++i)\n    {\n        monitor = _glfw.monitors[i];\n        if (monitor->widthMM <= 0 || monitor->heightMM <= 0)\n        {\n            // If Wayland does not provide a physical size, assume the default 96 DPI\n            monitor->widthMM  = (int) (monitor->modes[monitor->wl.currentMode].width * 25.4f / 96.f);\n            monitor->heightMM = (int) (monitor->modes[monitor->wl.currentMode].height * 25.4f / 96.f);\n        }\n    }\n\n    if (!_glfw.wl.wmBase)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Failed to find xdg-shell in your compositor\");\n        return false;\n    }\n\n    if (_glfw.wl.shm)\n    {\n        _glfw.wl.cursorSurface =\n            wl_compositor_create_surface(_glfw.wl.compositor);\n    }\n    else\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Failed to find Wayland SHM\");\n        return false;\n    }\n    if (_glfw.hints.init.debugRendering) {\n        const char *mc = get_compositor_missing_capabilities();\n        if (mc && mc[0]) debug(\"Compositor missing capabilities: %s\\n\", mc);\n    }\n    *supports_window_occlusion = _glfw.wl.xdg_wm_base_version > 5;\n\n    return true;\n}\n\nvoid _glfwPlatformTerminate(void)\n{\n    if (_glfw.wl.activation_requests.array) {\n        for (size_t i=0; i < _glfw.wl.activation_requests.sz; i++) {\n            glfw_wl_xdg_activation_request *r = _glfw.wl.activation_requests.array + i;\n            if (r->callback) r->callback(NULL, NULL, r->callback_data);\n            xdg_activation_token_v1_destroy(r->token);\n        }\n        free(_glfw.wl.activation_requests.array);\n    }\n    _glfwTerminateEGL();\n    if (_glfw.wl.egl.handle)\n    {\n        _glfw_dlclose(_glfw.wl.egl.handle);\n        _glfw.wl.egl.handle = NULL;\n    }\n\n    glfw_xkb_release(&_glfw.wl.xkb);\n    glfw_dbus_terminate(&_glfw.wl.dbus);\n\n    glfw_wlc_destroy();\n    if (_glfw.wl.cursor.handle)\n    {\n        _glfw_dlclose(_glfw.wl.cursor.handle);\n        _glfw.wl.cursor.handle = NULL;\n    }\n\n    if (_glfw.wl.cursorSurface)\n        wl_surface_destroy(_glfw.wl.cursorSurface);\n    if (_glfw.wl.subcompositor)\n        wl_subcompositor_destroy(_glfw.wl.subcompositor);\n    if (_glfw.wl.compositor)\n        wl_compositor_destroy(_glfw.wl.compositor);\n    if (_glfw.wl.shm)\n        wl_shm_destroy(_glfw.wl.shm);\n    if (_glfw.wl.decorationManager)\n        zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager);\n    if (_glfw.wl.wmBase)\n        xdg_wm_base_destroy(_glfw.wl.wmBase);\n    if (_glfw.wl.pointer)\n        wl_pointer_destroy(_glfw.wl.pointer);\n    if (_glfw.wl.keyboard)\n        wl_keyboard_destroy(_glfw.wl.keyboard);\n    if (_glfw.wl.seat)\n        wl_seat_destroy(_glfw.wl.seat);\n    if (_glfw.wl.relativePointerManager)\n        zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager);\n    if (_glfw.wl.pointerConstraints)\n        zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);\n    _glfwWaylandDestroyTextInput();\n    if (_glfw.wl.dataSourceForClipboard)\n        wl_data_source_destroy(_glfw.wl.dataSourceForClipboard);\n    if (_glfw.wl.dataSourceForPrimarySelection)\n        zwp_primary_selection_source_v1_destroy(_glfw.wl.dataSourceForPrimarySelection);\n    for (size_t doi=0; doi < arraysz(_glfw.wl.untyped_data_offers); doi++) {\n        if (_glfw.wl.untyped_data_offers[doi].id) {\n            destroy_data_offer(&_glfw.wl.untyped_data_offers[doi]);\n        }\n    }\n    if (_glfw.wl.primary_data_offer.id) destroy_data_offer(&_glfw.wl.primary_data_offer);\n    if (_glfw.wl.clipboard_data_offer.id) destroy_data_offer(&_glfw.wl.clipboard_data_offer);\n    if (_glfw.wl.drop_data_offer.id) destroy_data_offer(&_glfw.wl.drop_data_offer);\n    if (_glfw.wl.dataDevice)\n        wl_data_device_destroy(_glfw.wl.dataDevice);\n    if (_glfw.wl.dataDeviceManager)\n        wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager);\n    if (_glfw.wl.primarySelectionDevice)\n        zwp_primary_selection_device_v1_destroy(_glfw.wl.primarySelectionDevice);\n    if (_glfw.wl.primarySelectionDeviceManager)\n        zwp_primary_selection_device_manager_v1_destroy(_glfw.wl.primarySelectionDeviceManager);\n    if (_glfw.wl.xdg_activation_v1)\n        xdg_activation_v1_destroy(_glfw.wl.xdg_activation_v1);\n    if (_glfw.wl.xdg_toplevel_icon_manager_v1)\n        xdg_toplevel_icon_manager_v1_destroy(_glfw.wl.xdg_toplevel_icon_manager_v1);\n    if (_glfw.wl.xdg_system_bell_v1)\n        xdg_system_bell_v1_destroy(_glfw.wl.xdg_system_bell_v1);\n    if (_glfw.wl.xdg_toplevel_tag_manager_v1)\n        xdg_toplevel_tag_manager_v1_destroy(_glfw.wl.xdg_toplevel_tag_manager_v1);\n    if (_glfw.wl.xdg_toplevel_drag_manager_v1)\n        xdg_toplevel_drag_manager_v1_destroy(_glfw.wl.xdg_toplevel_drag_manager_v1);\n    if (_glfw.wl.wp_single_pixel_buffer_manager_v1)\n        wp_single_pixel_buffer_manager_v1_destroy(_glfw.wl.wp_single_pixel_buffer_manager_v1);\n    if (_glfw.wl.wp_cursor_shape_manager_v1)\n        wp_cursor_shape_manager_v1_destroy(_glfw.wl.wp_cursor_shape_manager_v1);\n    if (_glfw.wl.wp_viewporter)\n        wp_viewporter_destroy(_glfw.wl.wp_viewporter);\n    if (_glfw.wl.wp_fractional_scale_manager_v1)\n        wp_fractional_scale_manager_v1_destroy(_glfw.wl.wp_fractional_scale_manager_v1);\n    if (_glfw.wl.org_kde_kwin_blur_manager)\n        org_kde_kwin_blur_manager_destroy(_glfw.wl.org_kde_kwin_blur_manager);\n    if (_glfw.wl.ext_background_effect_manager_v1)\n        ext_background_effect_manager_v1_destroy(_glfw.wl.ext_background_effect_manager_v1);\n    if (_glfw.wl.zwlr_layer_shell_v1)\n        zwlr_layer_shell_v1_destroy(_glfw.wl.zwlr_layer_shell_v1);\n    if (_glfw.wl.idle_inhibit_manager)\n        zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idle_inhibit_manager);\n    if (_glfw.wl.keyboard_shortcuts_inhibit_manager)\n        zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(_glfw.wl.keyboard_shortcuts_inhibit_manager);\n\n    if (_glfw.wl.registry)\n        wl_registry_destroy(_glfw.wl.registry);\n    if (_glfw.wl.display)\n    {\n        wl_display_flush(_glfw.wl.display);\n        wl_display_disconnect(_glfw.wl.display);\n        _glfw.wl.display = NULL;\n    }\n    finalizePollData(&_glfw.wl.eventLoopData);\n    if (_glfw.wl.compositor_name) {\n        free(_glfw.wl.compositor_name);\n        _glfw.wl.compositor_name = NULL;\n    }\n}\n\n#define GLFW_LOOP_BACKEND wl\n#include \"main_loop.h\"\n\nconst char* _glfwPlatformGetVersionString(void)\n{\n    (void)keep_going;\n    return _GLFW_VERSION_NUMBER \" Wayland EGL OSMesa\"\n#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)\n        \" clock_gettime\"\n#else\n        \" gettimeofday\"\n#endif\n        \" evdev\"\n#if defined(_GLFW_BUILD_DLL)\n        \" shared\"\n#endif\n        ;\n}\n"
  },
  {
    "path": "glfw/wl_monitor.c",
    "content": "//========================================================================\n// GLFW 3.4 Wayland - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n#include <assert.h>\n\n\nstatic void outputHandleGeometry(void* data,\n                                 struct wl_output* output UNUSED,\n                                 int32_t x,\n                                 int32_t y,\n                                 int32_t physicalWidth,\n                                 int32_t physicalHeight,\n                                 int32_t subpixel UNUSED,\n                                 const char* make UNUSED,\n                                 const char* model UNUSED,\n                                 int32_t transform UNUSED)\n{\n    struct _GLFWmonitor *monitor = data;\n    monitor->wl.x = x;\n    monitor->wl.y = y;\n    monitor->widthMM = physicalWidth;\n    monitor->heightMM = physicalHeight;\n}\n\nstatic void outputHandleMode(void* data,\n                             struct wl_output* output UNUSED,\n                             uint32_t flags,\n                             int32_t width,\n                             int32_t height,\n                             int32_t refresh)\n{\n    struct _GLFWmonitor *monitor = data;\n    GLFWvidmode mode;\n\n    mode.width = width;\n    mode.height = height;\n    mode.redBits = 8;\n    mode.greenBits = 8;\n    mode.blueBits = 8;\n    mode.refreshRate = (int) round(refresh / 1000.0);\n\n    monitor->modeCount++;\n    monitor->modes =\n        realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode));\n    monitor->modes[monitor->modeCount - 1] = mode;\n\n    if (flags & WL_OUTPUT_MODE_CURRENT)\n        monitor->wl.currentMode = monitor->modeCount - 1;\n}\n\nstatic void outputHandleDone(void* data, struct wl_output* output UNUSED)\n{\n    struct _GLFWmonitor *monitor = data;\n    for (int i = 0; i < _glfw.monitorCount; i++) {\n        if (_glfw.monitors[i] == monitor) return;\n    }\n    _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);\n}\n\nstatic void outputHandleScale(void* data,\n                              struct wl_output* output UNUSED,\n                              int32_t factor)\n{\n    struct _GLFWmonitor *monitor = data;\n    if (factor > 0 && factor < 24)\n        monitor->wl.scale = factor;\n}\n\nstatic void outputHandleName(void* data,\n                              struct wl_output* output UNUSED,\n                              const char* name) {\n    struct _GLFWmonitor *monitor = data;\n    if (name) {\n        if (monitor->name) free((void*)monitor->name);\n        monitor->name = _glfw_strdup(name);\n    }\n}\n\nstatic void outputHandleDescription(void* data,\n                              struct wl_output* output UNUSED,\n                              const char* description) {\n    struct _GLFWmonitor *monitor = data;\n    if (description) {\n        if (monitor->description) free((void*)monitor->description);\n        monitor->description = _glfw_strdup(description);\n    }\n}\n\nstatic const struct wl_output_listener outputListener = {\n    outputHandleGeometry,\n    outputHandleMode,\n    outputHandleDone,\n    outputHandleScale,\n    outputHandleName,\n    outputHandleDescription,\n};\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid _glfwAddOutputWayland(uint32_t name, uint32_t version)\n{\n    _GLFWmonitor *monitor;\n    struct wl_output *output;\n\n    if (version < 2)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Unsupported output interface version\");\n        return;\n    }\n\n    // The actual name of this output will be set in the handlers.\n    monitor = _glfwAllocMonitor(\"unnamed\", 0, 0);\n\n    output = wl_registry_bind(_glfw.wl.registry,\n                              name,\n                              &wl_output_interface,\n                              MIN(version, (unsigned)WL_OUTPUT_NAME_SINCE_VERSION));\n    if (!output)\n    {\n        _glfwFreeMonitor(monitor);\n        return;\n    }\n\n    monitor->wl.scale = 1;\n    monitor->wl.output = output;\n    monitor->wl.name = name;\n\n    wl_output_add_listener(output, &outputListener, monitor);\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)\n{\n    if (monitor->wl.output)\n        wl_output_destroy(monitor->wl.output);\n}\n\nvoid _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)\n{\n    if (xpos)\n        *xpos = monitor->wl.x;\n    if (ypos)\n        *ypos = monitor->wl.y;\n}\n\nvoid _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,\n                                         float* xscale, float* yscale)\n{\n    if (xscale)\n        *xscale = (float) monitor->wl.scale;\n    if (yscale)\n        *yscale = (float) monitor->wl.scale;\n}\n\nvoid _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,\n                                     int* xpos, int* ypos,\n                                     int* width, int* height)\n{\n    if (xpos)\n        *xpos = monitor->wl.x;\n    if (ypos)\n        *ypos = monitor->wl.y;\n    if (width)\n        *width = monitor->modes[monitor->wl.currentMode].width;\n    if (height)\n        *height = monitor->modes[monitor->wl.currentMode].height;\n}\n\nGLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)\n{\n    *found = monitor->modeCount;\n    return monitor->modes;\n}\n\nbool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)\n{\n    if (monitor->modeCount > monitor->wl.currentMode) {\n        *mode = monitor->modes[monitor->wl.currentMode];\n        return true;\n    }\n    return false;\n}\n\nbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp UNUSED)\n{\n    _glfwInputError(GLFW_FEATURE_UNAVAILABLE,\n                    \"Wayland: Gamma ramp access is not available\");\n    return false;\n}\n\nvoid _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor UNUSED,\n                               const GLFWgammaramp* ramp UNUSED)\n{\n    _glfwInputError(GLFW_FEATURE_UNAVAILABLE,\n                    \"Wayland: Gamma ramp access is not available\");\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return monitor->wl.output;\n}\n"
  },
  {
    "path": "glfw/wl_platform.h",
    "content": "//========================================================================\n// GLFW 3.4 Wayland - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#include <wayland-client.h>\n#include <dlfcn.h>\n#include <poll.h>\n\ntypedef VkFlags VkWaylandSurfaceCreateFlagsKHR;\n\ntypedef struct VkWaylandSurfaceCreateInfoKHR\n{\n    VkStructureType                 sType;\n    const void*                     pNext;\n    VkWaylandSurfaceCreateFlagsKHR  flags;\n    struct wl_display*              display;\n    struct wl_surface*              surface;\n} VkWaylandSurfaceCreateInfoKHR;\n\ntypedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*);\ntypedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*);\n\n#include \"posix_thread.h\"\n#ifdef __linux__\n#include \"linux_joystick.h\"\n#else\n#include \"null_joystick.h\"\n#endif\n#include \"backend_utils.h\"\n#include \"xkb_glfw.h\"\n#include \"wl_cursors.h\"\n\n#include \"wayland-xdg-shell-client-protocol.h\"\n#include \"wayland-xdg-decoration-unstable-v1-client-protocol.h\"\n#include \"wayland-relative-pointer-unstable-v1-client-protocol.h\"\n#include \"wayland-pointer-constraints-unstable-v1-client-protocol.h\"\n#include \"wayland-primary-selection-unstable-v1-client-protocol.h\"\n#include \"wayland-primary-selection-unstable-v1-client-protocol.h\"\n#include \"wayland-xdg-activation-v1-client-protocol.h\"\n#include \"wayland-cursor-shape-v1-client-protocol.h\"\n#include \"wayland-fractional-scale-v1-client-protocol.h\"\n#include \"wayland-viewporter-client-protocol.h\"\n#include \"wayland-kwin-blur-v1-client-protocol.h\"\n#include \"wayland-ext-background-effect-v1-client-protocol.h\"\n#include \"wayland-wlr-layer-shell-unstable-v1-client-protocol.h\"\n#include \"wayland-single-pixel-buffer-v1-client-protocol.h\"\n#include \"wayland-idle-inhibit-unstable-v1-client-protocol.h\"\n#include \"wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h\"\n#include \"wayland-xdg-toplevel-icon-v1-client-protocol.h\"\n#include \"wayland-xdg-system-bell-v1-client-protocol.h\"\n#include \"wayland-xdg-toplevel-tag-v1-client-protocol.h\"\n#include \"wayland-xdg-toplevel-drag-v1-client-protocol.h\"\n\n#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)\n#define _glfw_dlclose(handle) dlclose(handle)\n#define _glfw_dlsym(handle, name) dlsym(handle, name)\n\n#define _GLFW_PLATFORM_WINDOW_STATE         _GLFWwindowWayland  wl\n#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl\n#define _GLFW_PLATFORM_MONITOR_STATE        _GLFWmonitorWayland wl\n#define _GLFW_PLATFORM_CURSOR_STATE         _GLFWcursorWayland  wl\n\n#define _GLFW_PLATFORM_CONTEXT_STATE\n#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE\n\ntypedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*);\ntypedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*);\ntypedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*);\ntypedef struct wl_buffer* (* PFN_wl_cursor_image_get_buffer)(struct wl_cursor_image*);\n#define wl_cursor_theme_load _glfw.wl.cursor.theme_load\n#define wl_cursor_theme_destroy _glfw.wl.cursor.theme_destroy\n#define wl_cursor_theme_get_cursor _glfw.wl.cursor.theme_get_cursor\n#define wl_cursor_image_get_buffer _glfw.wl.cursor.image_get_buffer\n\ntypedef struct wl_egl_window* (* PFN_wl_egl_window_create)(struct wl_surface*, int, int);\ntypedef void (* PFN_wl_egl_window_destroy)(struct wl_egl_window*);\ntypedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int, int);\n#define wl_egl_window_create _glfw.wl.egl.window_create\n#define wl_egl_window_destroy _glfw.wl.egl.window_destroy\n#define wl_egl_window_resize _glfw.wl.egl.window_resize\n\ntypedef enum _GLFWCSDSurface\n{\n    CENTRAL_WINDOW, CSD_titlebar, CSD_shadow_top, CSD_shadow_left, CSD_shadow_bottom, CSD_shadow_right,\n    CSD_shadow_upper_left, CSD_shadow_upper_right, CSD_shadow_lower_left, CSD_shadow_lower_right,\n} _GLFWCSDSurface;\n\ntypedef struct _GLFWWaylandBufferPair {\n    struct wl_buffer *a, *b, *front, *back;\n    struct { uint8_t *a, *b, *front, *back; } data;\n    bool has_pending_update;\n    size_t size_in_bytes, width, height, viewport_width, viewport_height, stride;\n    bool a_needs_to_be_destroyed, b_needs_to_be_destroyed;\n} _GLFWWaylandBufferPair;\n\ntypedef struct _GLFWWaylandCSDSurface {\n    struct wl_surface *surface;\n    struct wl_subsurface *subsurface;\n    struct wp_viewport *wp_viewport;\n    _GLFWWaylandBufferPair buffer;\n    int x, y;\n} _GLFWWaylandCSDSurface;\n\ntypedef enum WaylandWindowState {\n\n    TOPLEVEL_STATE_NONE = 0,\n    TOPLEVEL_STATE_MAXIMIZED = 1,\n    TOPLEVEL_STATE_FULLSCREEN = 2,\n    TOPLEVEL_STATE_RESIZING = 4,\n    TOPLEVEL_STATE_ACTIVATED = 8,\n    TOPLEVEL_STATE_TILED_LEFT = 16,\n    TOPLEVEL_STATE_TILED_RIGHT = 32,\n    TOPLEVEL_STATE_TILED_TOP = 64,\n    TOPLEVEL_STATE_TILED_BOTTOM = 128,\n    TOPLEVEL_STATE_SUSPENDED = 256,\n    TOPLEVEL_STATE_CONSTRAINED_LEFT = 512,\n    TOPLEVEL_STATE_CONSTRAINED_RIGHT = 1024,\n    TOPLEVEL_STATE_CONSTRAINED_TOP = 2048,\n    TOPLEVEL_STATE_CONSTRAINED_BOTTOM = 4096,\n} WaylandWindowState;\n\ntypedef struct glfw_wl_xdg_activation_request {\n    GLFWid window_id;\n    GLFWactivationcallback callback;\n    void *callback_data;\n    uintptr_t request_id;\n    void *token;\n} glfw_wl_xdg_activation_request;\n\n\nstatic const WaylandWindowState TOPLEVEL_STATE_DOCKED = TOPLEVEL_STATE_MAXIMIZED | TOPLEVEL_STATE_FULLSCREEN | TOPLEVEL_STATE_TILED_TOP | TOPLEVEL_STATE_TILED_LEFT | TOPLEVEL_STATE_TILED_RIGHT | TOPLEVEL_STATE_TILED_BOTTOM;\n\nenum WaylandWindowPendingState {\n    PENDING_STATE_TOPLEVEL = 1,\n    PENDING_STATE_DECORATION = 2\n};\n\nenum _GLFWWaylandAxisEvent {\n    AXIS_EVENT_UNKNOWN = 0,\n    AXIS_EVENT_CONTINUOUS = 1,\n    AXIS_EVENT_DISCRETE = 2,\n    AXIS_EVENT_VALUE120 = 3\n};\n\n// Wayland-specific per-window data\n//\ntypedef struct _GLFWwindowWayland\n{\n    int                         width, height;\n    bool                        visible, created;\n    bool                        hovered;\n    bool                        transparent;\n    struct wl_surface*          surface;\n    bool                        waiting_for_swap_to_commit;\n    struct wl_egl_window*       native;\n    struct wl_callback*         callback;\n\n    struct {\n        struct xdg_surface*     surface;\n        struct xdg_toplevel*    toplevel;\n        struct zxdg_toplevel_decoration_v1* decoration;\n        struct { int width, height; } top_level_bounds;\n    } xdg;\n    struct wp_fractional_scale_v1 *wp_fractional_scale_v1;\n    struct wp_viewport *wp_viewport;\n    struct org_kde_kwin_blur *org_kde_kwin_blur;\n    struct ext_background_effect_surface_v1 *ext_background_effect_surface_v1;\n    bool has_blur, expect_scale_from_compositor, window_fully_created;\n    struct {\n        bool surface_configured, preferred_scale_received, fractional_scale_received;\n    } once;\n    struct wl_buffer *temp_buffer_used_during_window_creation;\n    struct {\n        GLFWLayerShellConfig config;\n        struct zwlr_layer_surface_v1* zwlr_layer_surface_v1;\n    } layer_shell;\n\n    /* information about axis events on current frame */\n    struct\n    {\n        struct {\n            enum _GLFWWaylandAxisEvent x_axis_type;\n            float x;\n            enum _GLFWWaylandAxisEvent y_axis_type;\n            float y;\n        } discrete, continuous;\n\n        /* Event timestamp in nanoseconds */\n        bool x_stop_received, y_stop_received;\n        uint32_t source_type;\n        monotonic_t x_start_time, x_stop_time, y_stop_time, y_start_time;\n    } pointer_curr_axis_info;\n    GLFWOffsetType prev_frame_offset_type;\n\n    _GLFWcursor*                currentCursor;\n    double                      cursorPosX, cursorPosY, allCursorPosX, allCursorPosY;\n\n    char*                       title;\n    char                        appId[256], windowTag[256];\n\n    // We need to track the monitors the window spans on to calculate the\n    // optimal scaling factor.\n    struct { uint32_t deduced, preferred; } integer_scale;\n    uint32_t                    fractional_scale;\n    bool                        initial_scale_notified;\n    _GLFWmonitor**              monitors;\n    int                         monitorsCount;\n    int                         monitorsSize;\n\n    struct {\n        struct zwp_relative_pointer_v1*    relativePointer;\n        struct zwp_locked_pointer_v1*      lockedPointer;\n    } pointerLock;\n\n    struct {\n        bool serverSide, buffer_destroyed, titlebar_needs_update, dragging, titlebar_hidden;\n        _GLFWCSDSurface focus;\n\n        _GLFWWaylandCSDSurface titlebar, shadow_left, shadow_right, shadow_top, shadow_bottom, shadow_upper_left, shadow_upper_right, shadow_lower_left, shadow_lower_right;\n\n        struct {\n            uint8_t *data;\n            size_t size;\n        } mapping;\n\n        struct {\n            int width, height;\n            bool focused;\n            double fscale;\n            WaylandWindowState toplevel_states;\n        } for_window_state;\n\n        struct {\n            unsigned int width, top, horizontal, vertical, visible_titlebar_height;\n        } metrics;\n\n        struct {\n            int32_t x, y, width, height;\n        } geometry;\n\n        struct {\n            bool hovered;\n            int width, left;\n        } minimize, maximize, close;\n\n        struct {\n            uint32_t *data;\n            size_t for_decoration_size, stride, segments, corner_size;\n        } shadow_tile;\n        monotonic_t last_click_on_top_decoration_at;\n\n        uint32_t titlebar_color;\n        bool use_custom_titlebar_color;\n    } decorations;\n\n    struct {\n        unsigned long long id;\n        void(*callback)(unsigned long long id);\n        struct wl_callback *current_wl_callback;\n    } frameCallbackData;\n\n    struct {\n        int32_t width, height;\n    } user_requested_content_size;\n\n    struct {\n        bool minimize, maximize, fullscreen, window_menu;\n    } wm_capabilities;\n\n\n    bool maximize_on_first_show;\n    uint32_t pending_state;\n    struct {\n        int width, height;\n        WaylandWindowState toplevel_states;\n        uint32_t decoration_mode;\n    } current, pending;\n    struct zwp_keyboard_shortcuts_inhibitor_v1 *keyboard_shortcuts_inhibitor;\n} _GLFWwindowWayland;\n\ntypedef struct _GLFWWaylandDataOffer\n{\n    void *id;\n    bool is_self_offer;\n    bool is_primary;\n    const char *mime_for_drop;\n    uint32_t source_actions;\n    uint32_t dnd_action;\n    struct wl_surface *surface;\n    const char **mimes;\n    size_t mimes_capacity, mimes_count;\n    const char **copy_mimes;   // Working copy passed to callbacks; pointers into mimes[]\n    size_t copy_mimes_count;   // Count of entries in copy_mimes (accepted count after callback)\n    bool drag_accepted, dropped;\n    uint32_t serial;\n    struct {\n        id_type watch_id;\n        int fd;\n        char *mime;\n    } *requested_drop_data;\n    size_t dd_capacity, dd_count;\n} _GLFWWaylandDataOffer;\n\n// Wayland-specific global data\n//\ntypedef struct _GLFWlibraryWayland\n{\n    struct wl_display*          display;\n    struct wl_registry*         registry;\n    struct wl_compositor*       compositor;\n    struct wl_subcompositor*    subcompositor;\n    struct wl_shm*              shm;\n    struct wl_seat*             seat;\n    struct wl_pointer*          pointer;\n    struct wl_keyboard*         keyboard;\n    struct wl_data_device_manager*          dataDeviceManager;\n    struct wl_data_device*      dataDevice;\n    struct xdg_wm_base*         wmBase;\n    int xdg_wm_base_version;\n    struct zxdg_decoration_manager_v1*      decorationManager;\n    struct zwp_relative_pointer_manager_v1* relativePointerManager;\n    struct zwp_pointer_constraints_v1*      pointerConstraints;\n    struct wl_data_source*                  dataSourceForClipboard;\n    struct zwp_primary_selection_device_manager_v1* primarySelectionDeviceManager;\n    struct zwp_primary_selection_device_v1*    primarySelectionDevice;\n    struct zwp_primary_selection_source_v1*    dataSourceForPrimarySelection;\n    struct xdg_activation_v1* xdg_activation_v1;\n    struct xdg_toplevel_icon_manager_v1* xdg_toplevel_icon_manager_v1;\n    struct xdg_system_bell_v1* xdg_system_bell_v1;\n    struct xdg_toplevel_tag_manager_v1* xdg_toplevel_tag_manager_v1;\n    struct xdg_toplevel_drag_manager_v1* xdg_toplevel_drag_manager_v1;\n    struct wp_cursor_shape_manager_v1* wp_cursor_shape_manager_v1;\n    struct wp_cursor_shape_device_v1* wp_cursor_shape_device_v1;\n    struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1;\n    struct wp_viewporter *wp_viewporter;\n    struct org_kde_kwin_blur_manager *org_kde_kwin_blur_manager;\n    struct ext_background_effect_manager_v1 *ext_background_effect_manager_v1;\n    uint32_t ext_background_effect_capabilities;\n    struct zwlr_layer_shell_v1* zwlr_layer_shell_v1; uint32_t zwlr_layer_shell_v1_version;\n    struct wp_single_pixel_buffer_manager_v1 *wp_single_pixel_buffer_manager_v1;\n    struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager;\n    struct zwp_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit_manager;\n\n    int                         compositorVersion;\n    int                         seatVersion;\n    bool has_key_repeat_events;\n\n    struct wl_surface*          cursorSurface;\n    GLFWCursorShape             cursorPreviousShape;\n    uint32_t                    serial, input_serial, pointer_serial, pointer_enter_serial, keyboard_enter_serial;\n\n    int32_t                     keyboardRepeatRate;\n    monotonic_t                 keyboardRepeatDelay;\n\n    struct {\n        uint32_t                key;\n        id_type                 keyRepeatTimer;\n        GLFWid                  keyboardFocusId;\n    } keyRepeatInfo;\n    id_type                     cursorAnimationTimer;\n    _GLFWXKBData                xkb;\n    _GLFWDBUSData               dbus;\n\n    _GLFWwindow*                pointerFocus;\n    GLFWid                      keyboardFocusId;\n\n    struct {\n        void*                   handle;\n\n        PFN_wl_cursor_theme_load theme_load;\n        PFN_wl_cursor_theme_destroy theme_destroy;\n        PFN_wl_cursor_theme_get_cursor theme_get_cursor;\n        PFN_wl_cursor_image_get_buffer image_get_buffer;\n    } cursor;\n\n    struct {\n        void*                   handle;\n\n        PFN_wl_egl_window_create window_create;\n        PFN_wl_egl_window_destroy window_destroy;\n        PFN_wl_egl_window_resize window_resize;\n    } egl;\n\n    struct {\n        glfw_wl_xdg_activation_request *array;\n        size_t capacity, sz;\n    } activation_requests;\n\n    EventLoopData eventLoopData;\n    _GLFWWaylandDataOffer untyped_data_offers[8];\n    _GLFWWaylandDataOffer clipboard_data_offer, primary_data_offer, drop_data_offer;\n\n    bool has_preferred_buffer_scale;\n    char *compositor_name;\n\n    // Drag source state\n    struct {\n        struct wl_data_source* source;\n        struct wl_surface *drag_icon;\n        struct wp_viewport *drag_viewport;\n        struct xdg_toplevel_drag_v1 *toplevel_drag;\n        struct xdg_surface *toplevel_xdg_surface;\n        struct xdg_toplevel *toplevel_xdg_toplevel;\n        struct wl_buffer *toplevel_buffer;\n        struct {\n            const char *mime_type;\n            int fd;\n            GLFWid watch_id;\n            char *pending_data;\n            size_t sz, offset;\n        } *data_requests;\n        size_t count, capacity;\n        GLFWDragOperationType action;\n    } drag;\n} _GLFWlibraryWayland;\n\n// Wayland-specific per-monitor data\n//\ntypedef struct _GLFWmonitorWayland\n{\n    struct wl_output*           output;\n    uint32_t                    name;\n    int                         currentMode;\n\n    int                         x;\n    int                         y;\n    int                         scale;\n\n} _GLFWmonitorWayland;\n\n// Wayland-specific per-cursor data\n//\ntypedef struct _GLFWcursorWayland\n{\n    struct wl_cursor*           cursor;\n    struct wl_buffer*           buffer;\n    int                         width, height;\n    int                         xhot, yhot;\n    unsigned int                currentImage;\n    /** The scale of the cursor, or 0 if the cursor should be loaded late, or -1 if the cursor variable itself is unused. */\n    int                         scale;\n    /** Cursor shape stored to allow late cursor loading in setCursorImage. */\n    GLFWCursorShape             shape;\n} _GLFWcursorWayland;\n\n\nvoid _glfwAddOutputWayland(uint32_t name, uint32_t version);\nvoid _glfwWaylandBeforeBufferSwap(_GLFWwindow *window);\nvoid _glfwWaylandAfterBufferSwap(_GLFWwindow *window);\nvoid _glfwSetupWaylandDataDevice(void);\nvoid _glfwSetupWaylandPrimarySelectionDevice(void);\ndouble _glfwWaylandWindowScale(_GLFWwindow*);\nint _glfwWaylandIntegerWindowScale(_GLFWwindow*);\nvoid animateCursorImage(id_type timer_id, void *data);\nstruct wl_cursor* _glfwLoadCursor(GLFWCursorShape, struct wl_cursor_theme*);\nvoid destroy_data_offer(_GLFWWaylandDataOffer*);\nconst char* _glfwWaylandCompositorName(void);\n\ntypedef struct wayland_cursor_shape {\n    int which; const char *name;\n} wayland_cursor_shape;\n\nwayland_cursor_shape\nglfw_cursor_shape_to_wayland_cursor_shape(GLFWCursorShape g);\n"
  },
  {
    "path": "glfw/wl_text_input.c",
    "content": "/*\n * wl_text_input.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"wl_text_input.h\"\n#include \"internal.h\"\n#include \"wayland-text-input-unstable-v3-client-protocol.h\"\n#include <stdlib.h>\n#include <string.h>\n#define debug debug_input\n\nstatic struct zwp_text_input_v3*                  text_input;\nstatic struct zwp_text_input_manager_v3*          text_input_manager;\nstatic char *pending_pre_edit = NULL;\nstatic char *current_pre_edit = NULL;\nstatic char *pending_commit   = NULL;\nstatic bool ime_focused = false;\nstatic int last_cursor_left = 0, last_cursor_top = 0, last_cursor_width = 0, last_cursor_height = 0;\nuint32_t commit_serial = 0;\n\nstatic void commit(void) {\n    if (text_input) {\n        zwp_text_input_v3_commit (text_input);\n        commit_serial++;\n    }\n}\n\nstatic void\ntext_input_enter(void *data UNUSED, struct zwp_text_input_v3 *txt_input, struct wl_surface *surface UNUSED) {\n    debug(\"text-input: enter event\\n\");\n    if (txt_input) {\n        ime_focused = true;\n        zwp_text_input_v3_enable(txt_input);\n        zwp_text_input_v3_set_content_type(txt_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);\n        commit();\n    }\n}\n\nstatic void\ntext_input_leave(void *data UNUSED, struct zwp_text_input_v3 *txt_input, struct wl_surface *surface UNUSED) {\n    debug(\"text-input: leave event\\n\");\n    if (txt_input) {\n        ime_focused = false;\n        zwp_text_input_v3_disable(txt_input);\n        commit();\n    }\n}\n\nstatic void\nsend_text(const char *text, GLFWIMEState ime_state) {\n    _GLFWwindow *w = _glfwFocusedWindow();\n    if (w && w->callbacks.keyboard) {\n        GLFWkeyevent fake_ev = {.action = text ? GLFW_PRESS : GLFW_RELEASE};\n        fake_ev.text = text;\n        fake_ev.ime_state = ime_state;\n        w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);\n    }\n}\n\nstatic void\ntext_input_preedit_string(\n        void                     *data UNUSED,\n        struct zwp_text_input_v3 *txt_input UNUSED,\n        const char               *text,\n        int32_t                  cursor_begin,\n        int32_t                  cursor_end\n) {\n    debug(\"text-input: preedit_string event: text: %s cursor_begin: %d cursor_end: %d\\n\", text, cursor_begin, cursor_end);\n    free(pending_pre_edit);\n    pending_pre_edit = text ? _glfw_strdup(text) : NULL;\n}\n\nstatic void\ntext_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, const char *text) {\n    debug(\"text-input: commit_string event: text: %s\\n\", text);\n    free(pending_commit);\n    pending_commit = text ? _glfw_strdup(text) : NULL;\n}\n\nstatic void\ntext_input_delete_surrounding_text(\n        void *data UNUSED,\n        struct zwp_text_input_v3 *txt_input UNUSED,\n        uint32_t before_length,\n        uint32_t after_length) {\n    debug(\"text-input: delete_surrounding_text event: before_length: %u after_length: %u\\n\", before_length, after_length);\n}\n\nstatic void\ntext_input_done(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, uint32_t serial) {\n    debug(\"text-input: done event: serial: %u current_commit_serial: %u\\n\", serial, commit_serial);\n    const bool bad_event = serial != commit_serial;\n    // See https://wayland.app/protocols/text-input-unstable-v3#zwp_text_input_v3:event:done\n    // for handling of bad events. As best as I can tell spec says we perform all client side actions as usual\n    // but send nothing back to the compositor, aka no cursor position update.\n    // See https://github.com/kovidgoyal/kitty/pull/7283 for discussion\n    if ((pending_pre_edit == NULL && current_pre_edit == NULL) ||\n        (pending_pre_edit && current_pre_edit && strcmp(pending_pre_edit, current_pre_edit) == 0)) {\n        free(pending_pre_edit); pending_pre_edit = NULL;\n    } else {\n        free(current_pre_edit);\n        current_pre_edit = pending_pre_edit;\n        pending_pre_edit = NULL;\n        if (current_pre_edit) {\n            send_text(current_pre_edit, bad_event ? GLFW_IME_WAYLAND_DONE_EVENT : GLFW_IME_PREEDIT_CHANGED);\n        } else {\n            // Clear pre-edit text\n            send_text(NULL, GLFW_IME_WAYLAND_DONE_EVENT);\n        }\n    }\n    if (pending_commit) {\n        send_text(pending_commit, GLFW_IME_COMMIT_TEXT);\n        free(pending_commit); pending_commit = NULL;\n    }\n}\n\nvoid\n_glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name) {\n    if (!text_input_manager && _glfw.hints.init.wl.ime) text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1);\n}\n\nvoid\n_glfwWaylandInitTextInput(void) {\n    static const struct zwp_text_input_v3_listener text_input_listener = {\n        .enter = text_input_enter,\n        .leave = text_input_leave,\n        .preedit_string = text_input_preedit_string,\n        .commit_string = text_input_commit_string,\n        .delete_surrounding_text = text_input_delete_surrounding_text,\n        .done = text_input_done,\n    };\n    if (_glfw.hints.init.wl.ime && !text_input && text_input_manager && _glfw.wl.seat) {\n        text_input = zwp_text_input_manager_v3_get_text_input(text_input_manager, _glfw.wl.seat);\n        if (text_input) zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);\n    }\n}\n\nvoid\n_glfwWaylandDestroyTextInput(void) {\n    if (text_input) zwp_text_input_v3_destroy(text_input);\n    if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager);\n    text_input = NULL; text_input_manager = NULL;\n    free(pending_pre_edit); pending_pre_edit = NULL;\n    free(current_pre_edit); current_pre_edit = NULL;\n    free(pending_commit); pending_commit = NULL;\n}\n\nvoid\n_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {\n    if (!text_input) return;\n    switch(ev->type) {\n        case GLFW_IME_UPDATE_FOCUS:\n            debug(\"\\ntext-input: updating IME focus state, ime_focused: %d ev->focused: %d\\n\", ime_focused, ev->focused);\n            if (ime_focused) {\n                zwp_text_input_v3_enable(text_input);\n                zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);\n            } else {\n                free(pending_pre_edit); pending_pre_edit = NULL;\n                if (current_pre_edit) {\n                    // Clear pre-edit text\n                    send_text(NULL, GLFW_IME_PREEDIT_CHANGED);\n                    free(current_pre_edit); current_pre_edit = NULL;\n                }\n                if (pending_commit) {\n                    free(pending_commit); pending_commit = NULL;\n                }\n                zwp_text_input_v3_disable(text_input);\n            }\n            commit();\n            break;\n        case GLFW_IME_UPDATE_CURSOR_POSITION: {\n            const double scale = _glfwWaylandWindowScale(w);\n#define s(x) (int)round((x) / scale)\n            const int left = s(ev->cursor.left), top = s(ev->cursor.top), width = s(ev->cursor.width), height = s(ev->cursor.height);\n#undef s\n            if (left != last_cursor_left || top != last_cursor_top || width != last_cursor_width || height != last_cursor_height) {\n                last_cursor_left = left;\n                last_cursor_top = top;\n                last_cursor_width = width;\n                last_cursor_height = height;\n                debug(\"\\ntext-input: updating cursor position: left=%d top=%d width=%d height=%d\\n\", left, top, width, height);\n                zwp_text_input_v3_set_cursor_rectangle(text_input, left, top, width, height);\n                commit();\n            }\n        }\n            break;\n    }\n}\n"
  },
  {
    "path": "glfw/wl_text_input.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include <wayland-client.h>\n\nvoid _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name);\nvoid _glfwWaylandInitTextInput(void);\nvoid _glfwWaylandDestroyTextInput(void);\n"
  },
  {
    "path": "glfw/wl_window.c",
    "content": "//========================================================================\n// GLFW 3.4 Wayland - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#define _GNU_SOURCE\n\n#include \"internal.h\"\n#include \"backend_utils.h\"\n#include \"linux_notify.h\"\n#include \"wl_client_side_decorations.h\"\n#include \"../kitty/monotonic.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <string.h>\n#include <fcntl.h>\n#include <poll.h>\n#include <sys/mman.h>\n#include <assert.h>\n#include <unistd.h>\n\n#define debug debug_rendering\n\nstatic bool\nis_layer_shell(_GLFWwindow *window) { return window->wl.layer_shell.config.type != GLFW_LAYER_SHELL_NONE; }\n\nstatic void\ninhibit_shortcuts_for(_GLFWwindow *window, bool inhibit) {\n    if (inhibit) {\n        if (window->wl.keyboard_shortcuts_inhibitor) return;\n        window->wl.keyboard_shortcuts_inhibitor = zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(_glfw.wl.keyboard_shortcuts_inhibit_manager, window->wl.surface, _glfw.wl.seat);\n    } else {\n        if (!window->wl.keyboard_shortcuts_inhibitor) return;\n        zwp_keyboard_shortcuts_inhibitor_v1_destroy(window->wl.keyboard_shortcuts_inhibitor);\n        window->wl.keyboard_shortcuts_inhibitor = NULL;\n    }\n}\n\nstatic void\nactivation_token_done(void *data, struct xdg_activation_token_v1 *xdg_token, const char *token) {\n    for (size_t i = 0; i < _glfw.wl.activation_requests.sz; i++) {\n        glfw_wl_xdg_activation_request *r = _glfw.wl.activation_requests.array + i;\n        if (r->request_id == (uintptr_t)data) {\n            _GLFWwindow *window = _glfwWindowForId(r->window_id);\n            if (r->callback) r->callback((GLFWwindow*)window, token, r->callback_data);\n            remove_i_from_array(_glfw.wl.activation_requests.array, i, _glfw.wl.activation_requests.sz);\n            break;\n        }\n    }\n    xdg_activation_token_v1_destroy(xdg_token);\n}\n\n\nstatic const struct\nxdg_activation_token_v1_listener activation_token_listener = {\n    .done = &activation_token_done,\n};\n\n\nstatic bool\nget_activation_token(\n    _GLFWwindow *window, uint32_t serial, GLFWactivationcallback cb, void *cb_data\n) {\n#define fail(msg) { _glfwInputError(GLFW_PLATFORM_ERROR, msg); if (cb) cb((GLFWwindow*)window, NULL, cb_data); return false; }\n    if (_glfw.wl.xdg_activation_v1 == NULL) fail(\"Wayland: activation requests not supported by this Wayland compositor\");\n    struct xdg_activation_token_v1 *token = xdg_activation_v1_get_activation_token(_glfw.wl.xdg_activation_v1);\n    if (token == NULL) fail(\"Wayland: failed to create activation request token\");\n    if (_glfw.wl.activation_requests.capacity < _glfw.wl.activation_requests.sz + 1) {\n        _glfw.wl.activation_requests.capacity = MAX(64u, _glfw.wl.activation_requests.capacity * 2);\n        _glfw.wl.activation_requests.array = realloc(_glfw.wl.activation_requests.array, _glfw.wl.activation_requests.capacity * sizeof(_glfw.wl.activation_requests.array[0]));\n        if (!_glfw.wl.activation_requests.array) {\n            _glfw.wl.activation_requests.capacity = 0;\n            fail(\"Wayland: Out of memory while allocation activation request\");\n        }\n    }\n    glfw_wl_xdg_activation_request *r = _glfw.wl.activation_requests.array + _glfw.wl.activation_requests.sz++;\n    memset(r, 0, sizeof(*r));\n    static uintptr_t rq = 0;\n    r->window_id = window->id;\n    r->callback = cb; r->callback_data = cb_data;\n    r->request_id = ++rq; r->token = token;\n    if (serial != 0)\n        xdg_activation_token_v1_set_serial(token, serial, _glfw.wl.seat);\n\n    xdg_activation_token_v1_set_surface(token, window->wl.surface);\n    xdg_activation_token_v1_add_listener(token, &activation_token_listener, (void*)r->request_id);\n    xdg_activation_token_v1_commit(token);\n    return true;\n#undef fail\n}\n\nstatic void\nconvert_glfw_image_to_wayland_image(const GLFWimage* image, unsigned char *target) {\n    // convert RGBA non-premultiplied to ARGB pre-multiplied\n    unsigned char* source = (unsigned char*) image->pixels;\n    for (int i = 0;  i < image->width * image->height;  i++, source += 4) {\n        unsigned int alpha = source[3];\n        *target++ = (unsigned char) ((source[2] * alpha) / 255);\n        *target++ = (unsigned char) ((source[1] * alpha) / 255);\n        *target++ = (unsigned char) ((source[0] * alpha) / 255);\n        *target++ = (unsigned char) alpha;\n    }\n}\n\nstatic struct wl_buffer* createShmBuffer(const GLFWimage* image, bool is_opaque, bool init_data)\n{\n    struct wl_shm_pool* pool;\n    struct wl_buffer* buffer;\n    int stride = image->width * 4;\n    int length = image->width * image->height * 4;\n    void* data;\n    int fd;\n\n    fd = createAnonymousFile(length);\n    if (fd < 0)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Creating a buffer file for %d B failed: %s\",\n                        length, strerror(errno));\n        return NULL;\n    }\n\n    data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);\n    if (data == MAP_FAILED)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: mmap failed: %s\", strerror(errno));\n        close(fd);\n        return NULL;\n    }\n\n    pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);\n\n    close(fd);\n    if (init_data) convert_glfw_image_to_wayland_image(image, data);\n\n    buffer =\n        wl_shm_pool_create_buffer(pool, 0,\n                                  image->width,\n                                  image->height,\n                                  stride, is_opaque ? WL_SHM_FORMAT_XRGB8888 : WL_SHM_FORMAT_ARGB8888);\n    munmap(data, length);\n    wl_shm_pool_destroy(pool);\n\n    return buffer;\n}\n\nwayland_cursor_shape\nglfw_cursor_shape_to_wayland_cursor_shape(GLFWCursorShape g) {\n    wayland_cursor_shape ans = {-1, \"\"};\n#define C(g, w) case g: ans.which = w; ans.name = #w; return ans;\n    switch(g) {\n        /* start glfw to wayland mapping (auto generated by gen-key-constants.py do not edit) */\n        C(GLFW_DEFAULT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);\n        C(GLFW_TEXT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT);\n        C(GLFW_POINTER_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);\n        C(GLFW_HELP_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP);\n        C(GLFW_WAIT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT);\n        C(GLFW_PROGRESS_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS);\n        C(GLFW_CROSSHAIR_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR);\n        C(GLFW_CELL_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CELL);\n        C(GLFW_VERTICAL_TEXT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_VERTICAL_TEXT);\n        C(GLFW_MOVE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE);\n        C(GLFW_E_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE);\n        C(GLFW_NE_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE);\n        C(GLFW_NW_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE);\n        C(GLFW_N_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE);\n        C(GLFW_SE_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE);\n        C(GLFW_SW_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE);\n        C(GLFW_S_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE);\n        C(GLFW_W_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE);\n        C(GLFW_EW_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE);\n        C(GLFW_NS_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE);\n        C(GLFW_NESW_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE);\n        C(GLFW_NWSE_RESIZE_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE);\n        C(GLFW_ZOOM_IN_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_IN);\n        C(GLFW_ZOOM_OUT_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT);\n        C(GLFW_ALIAS_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALIAS);\n        C(GLFW_COPY_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY);\n        C(GLFW_NOT_ALLOWED_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED);\n        C(GLFW_NO_DROP_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP);\n        C(GLFW_GRAB_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB);\n        C(GLFW_GRABBING_CURSOR, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING);\n/* end glfw to wayland mapping */\n        default: return ans;\n    }\n#undef C\n}\n\nstatic void\ncommit_window_surface(_GLFWwindow *window) {\n    // debug(\"Window %llu surface committed\\n\", window->id); dont log as every frame request causes a surface commit\n    wl_surface_commit(window->wl.surface);\n}\n\nstatic void\ncommit_window_surface_if_safe(_GLFWwindow *window) {\n    // we only commit if the buffer attached to the surface is the correct size,\n    // which means that at least one frame is drawn after resizeFramebuffer()\n    if (!window->wl.waiting_for_swap_to_commit) commit_window_surface(window);\n}\n\nstatic void\nset_cursor_surface(struct wl_surface *surface, int hotspot_x, int hotspot_y, const char *from_where) {\n    debug(\"Calling wl_pointer_set_cursor in %s with surface: %p and serial: %u\\n\", from_where, (void*)surface, _glfw.wl.pointer_enter_serial);\n    wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointer_enter_serial, surface, hotspot_x, hotspot_y);\n}\n\nstatic void\nsetCursorImage(_GLFWwindow* window, bool on_theme_change) {\n    _GLFWcursorWayland defaultCursor = {.shape = GLFW_DEFAULT_CURSOR};\n    _GLFWcursorWayland* cursorWayland = window->cursor ? &window->cursor->wl : &defaultCursor;\n    if (_glfw.wl.wp_cursor_shape_device_v1) {\n        wayland_cursor_shape s = glfw_cursor_shape_to_wayland_cursor_shape(cursorWayland->shape);\n        if (s.which > -1) {\n            debug(\"Changing cursor shape to: %s with serial: %u\\n\", s.name, _glfw.wl.pointer_enter_serial);\n            wp_cursor_shape_device_v1_set_shape(_glfw.wl.wp_cursor_shape_device_v1, _glfw.wl.pointer_enter_serial, (uint32_t)s.which);\n            return;\n        }\n    }\n    struct wl_cursor_image* image = NULL;\n    struct wl_buffer* buffer = NULL;\n    struct wl_surface* surface = _glfw.wl.cursorSurface;\n    const int scale = _glfwWaylandIntegerWindowScale(window);\n    if (!_glfw.wl.pointer) return;\n\n    if (cursorWayland->scale < 0) {\n        buffer = cursorWayland->buffer;\n        toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);\n    } else {\n        if (on_theme_change || cursorWayland->scale != scale) {\n            struct wl_cursor *newCursor = NULL;\n            struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale);\n            if (theme) newCursor = _glfwLoadCursor(cursorWayland->shape, theme);\n            if (newCursor != NULL) {\n                cursorWayland->cursor = newCursor;\n                cursorWayland->scale = scale;\n                cursorWayland->currentImage = 0;\n            } else {\n                _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: late cursor load failed; proceeding with existing cursor\");\n            }\n        }\n        if (!cursorWayland->cursor || !cursorWayland->cursor->image_count || !cursorWayland->cursor->images) return;\n        if (cursorWayland->currentImage >= cursorWayland->cursor->image_count) cursorWayland->currentImage = 0;\n        image = cursorWayland->cursor->images[cursorWayland->currentImage];\n        if (!image) image = cursorWayland->cursor->images[0];\n        if (!image) return;\n        buffer = wl_cursor_image_get_buffer(image);\n        if (image->delay && window->cursor) {\n            changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, ms_to_monotonic_t(image->delay));\n            toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 1);\n        } else {\n            toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);\n        }\n\n        if (!buffer)\n            return;\n\n        cursorWayland->width = image->width;\n        cursorWayland->height = image->height;\n        cursorWayland->xhot = image->hotspot_x;\n        cursorWayland->yhot = image->hotspot_y;\n    }\n\n    set_cursor_surface(surface, cursorWayland->xhot / scale, cursorWayland->yhot / scale, \"setCursorImage\");\n    wl_surface_set_buffer_scale(surface, scale);\n    wl_surface_attach(surface, buffer, 0, 0);\n    wl_surface_damage(surface, 0, 0,\n                      cursorWayland->width, cursorWayland->height);\n    wl_surface_commit(surface);\n}\n\n\nstatic bool\ncheckScaleChange(_GLFWwindow* window) {\n    if (window->wl.expect_scale_from_compositor) return false;\n    unsigned int scale = 1, monitorScale;\n    int i;\n\n    // Check if we will be able to set the buffer scale or not.\n    if (_glfw.wl.compositorVersion < 3)\n        return false;\n\n    // Get the scale factor from the highest scale monitor that this window is on\n    for (i = 0; i < window->wl.monitorsCount; ++i)\n    {\n        monitorScale = window->wl.monitors[i]->wl.scale;\n        if (scale < monitorScale)\n            scale = monitorScale;\n    }\n    if (window->wl.monitorsCount < 1 && _glfw.monitorCount > 0) {\n        // The window has not yet been assigned to any monitors, use the primary monitor\n        _GLFWmonitor *m = _glfw.monitors[0];\n        if (m && m->wl.scale > (int)scale) scale = m->wl.scale;\n    }\n\n    // Only change the framebuffer size if the scale changed.\n    if (scale != window->wl.integer_scale.deduced && !window->wl.fractional_scale)\n    {\n        window->wl.integer_scale.deduced = scale;\n        setCursorImage(window, false);\n        return true;\n    }\n    if (window->wl.monitorsCount > 0 && !window->wl.initial_scale_notified) {\n        window->wl.initial_scale_notified = true;\n        return true;\n    }\n    return false;\n}\n\nstatic void\nupdate_regions(_GLFWwindow* window) {\n    if (!window->wl.transparent) {\n        struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);\n        if (!region) return;\n        wl_region_add(region, 0, 0, window->wl.width, window->wl.height);\n        // Makes the surface considered as XRGB instead of ARGB.\n        wl_surface_set_opaque_region(window->wl.surface, region);\n        wl_region_destroy(region);\n    }\n    // Set blur region\n    if (_glfw.wl.ext_background_effect_manager_v1) {\n        if (window->wl.has_blur && (_glfw.wl.ext_background_effect_capabilities & EXT_BACKGROUND_EFFECT_MANAGER_V1_CAPABILITY_BLUR)) {\n            if (!window->wl.ext_background_effect_surface_v1)\n                window->wl.ext_background_effect_surface_v1 = ext_background_effect_manager_v1_get_background_effect(\n                    _glfw.wl.ext_background_effect_manager_v1, window->wl.surface);\n            if (window->wl.ext_background_effect_surface_v1) {\n                struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);\n                if (region) {\n                    wl_region_add(region, 0, 0, window->wl.width, window->wl.height);\n                    ext_background_effect_surface_v1_set_blur_region(window->wl.ext_background_effect_surface_v1, region);\n                    wl_region_destroy(region);\n                }\n            }\n        } else {\n            if (window->wl.ext_background_effect_surface_v1) {\n                ext_background_effect_surface_v1_set_blur_region(window->wl.ext_background_effect_surface_v1, NULL);\n            }\n        }\n    } else if (_glfw.wl.org_kde_kwin_blur_manager) {\n        if (window->wl.has_blur) {\n            if (!window->wl.org_kde_kwin_blur)\n                window->wl.org_kde_kwin_blur = org_kde_kwin_blur_manager_create(_glfw.wl.org_kde_kwin_blur_manager, window->wl.surface);\n            if (window->wl.org_kde_kwin_blur) {\n                // NULL means entire window\n                org_kde_kwin_blur_set_region(window->wl.org_kde_kwin_blur, NULL);\n                org_kde_kwin_blur_commit(window->wl.org_kde_kwin_blur);\n            }\n        } else {\n            org_kde_kwin_blur_manager_unset(_glfw.wl.org_kde_kwin_blur_manager, window->wl.surface);\n            if (window->wl.org_kde_kwin_blur) { org_kde_kwin_blur_release(window->wl.org_kde_kwin_blur); window->wl.org_kde_kwin_blur = NULL; }\n        }\n    }\n\n}\n\nint\n_glfwWaylandIntegerWindowScale(_GLFWwindow *window) {\n    int ans = (window->wl.integer_scale.preferred) ? window->wl.integer_scale.preferred : window->wl.integer_scale.deduced;\n    if (ans < 1) ans = 1;\n    return ans;\n}\n\ndouble\n_glfwWaylandWindowScale(_GLFWwindow *window) {\n    double ans = _glfwWaylandIntegerWindowScale(window);\n    if (window->wl.fractional_scale) ans = window->wl.fractional_scale / 120.;\n    return ans;\n}\n\nstatic void\nwait_for_swap_to_commit(_GLFWwindow *window) {\n    window->wl.waiting_for_swap_to_commit = true;\n    debug(\"Waiting for swap to commit Wayland surface for window: %llu\\n\", window->id);\n}\n\nstatic void\nresizeFramebuffer(_GLFWwindow* window) {\n    GLFWwindow *ctx = glfwGetCurrentContext();\n    bool ctx_changed = false;\n    if (ctx != (GLFWwindow*)window && window->context.client != GLFW_NO_API) { ctx_changed = true;  glfwMakeContextCurrent((GLFWwindow*)window); }\n    double scale = _glfwWaylandWindowScale(window);\n    int scaled_width = (int)round(window->wl.width * scale);\n    int scaled_height = (int)round(window->wl.height * scale);\n    debug(\"Resizing framebuffer of window: %llu to: %dx%d window size: %dx%d at scale: %.3f\\n\",\n            window->id, scaled_width, scaled_height, window->wl.width, window->wl.height, scale);\n    wl_egl_window_resize(window->wl.native, scaled_width, scaled_height, 0, 0);\n    update_regions(window);\n    wait_for_swap_to_commit(window);\n    if (ctx_changed) glfwMakeContextCurrent(ctx);\n    _glfwInputFramebufferSize(window, scaled_width, scaled_height);\n}\n\nvoid\n_glfwWaylandAfterBufferSwap(_GLFWwindow* window) {\n    if (window->wl.temp_buffer_used_during_window_creation) {\n        wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);\n        window->wl.temp_buffer_used_during_window_creation = NULL;\n    }\n    if (window->wl.waiting_for_swap_to_commit) {\n        debug(\"Window %llu swapped committing surface\\n\", window->id);\n        window->wl.waiting_for_swap_to_commit = false;\n        // this is not really needed, since I think eglSwapBuffers() calls wl_surface_commit()\n        // but lets be safe. See https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/egl/drivers/dri2/platform_wayland.c#L1510\n        commit_window_surface(window);\n    }\n}\n\nstatic const char*\nclipboard_mime(void) {\n    static char buf[128] = {0};\n    if (buf[0] == 0) {\n        snprintf(buf, sizeof(buf), \"application/glfw+clipboard-%d\", getpid());\n    }\n    return buf;\n}\n\nstatic void\napply_scale_changes(_GLFWwindow *window, bool resize_framebuffer, bool update_csd) {\n    double scale = _glfwWaylandWindowScale(window);\n    if (resize_framebuffer) resizeFramebuffer(window);\n    _glfwInputWindowContentScale(window, (float)scale, (float)scale);\n    if (update_csd) csd_set_visible(window, csd_should_window_be_decorated(window));  // resize the csd iff the window currently has CSD\n    int buffer_scale = window->wl.fractional_scale ? 1 : (int)scale;\n    wl_surface_set_buffer_scale(window->wl.surface, buffer_scale);\n}\n\nstatic bool\ndispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) {\n    bool size_changed = width != window->wl.width || height != window->wl.height;\n    bool scale_changed = checkScaleChange(window);\n\n    if (size_changed) {\n        _glfwInputWindowSize(window, width, height);\n        window->wl.width = width; window->wl.height = height;\n        resizeFramebuffer(window);\n    }\n\n    if (scale_changed) {\n        debug(\"Scale changed to %.3f in dispatchChangesAfterConfigure for window: %llu\\n\", _glfwWaylandWindowScale(window), window->id);\n        apply_scale_changes(window, !size_changed, false);\n    }\n\n    _glfwInputWindowDamage(window);\n\n    return size_changed || scale_changed;\n}\n\nstatic void\ninform_compositor_of_window_geometry(_GLFWwindow *window, const char *event) {\n#define geometry window->wl.decorations.geometry\n    debug(\"Setting window %llu \\\"visible area\\\" geometry in %s event: x=%d y=%d %dx%d viewport: %dx%d\\n\",\n            window->id, event, geometry.x, geometry.y, geometry.width, geometry.height, window->wl.width, window->wl.height);\n    xdg_surface_set_window_geometry(window->wl.xdg.surface, geometry.x, geometry.y, geometry.width, geometry.height);\n    if (window->wl.wp_viewport) wp_viewport_set_destination(window->wl.wp_viewport, window->wl.width, window->wl.height);\n#undef geometry\n}\n\n\nstatic void\nxdgDecorationHandleConfigure(void* data,\n                                         struct zxdg_toplevel_decoration_v1* decoration UNUSED,\n                                         uint32_t mode)\n{\n    _GLFWwindow* window = data;\n    window->wl.pending.decoration_mode = mode;\n    window->wl.pending_state |= PENDING_STATE_DECORATION;\n    debug(\"XDG decoration configure event received for window %llu: has_server_side_decorations: %d\\n\", window->id, (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE));\n}\n\nstatic const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = {\n    xdgDecorationHandleConfigure,\n};\n\nstatic void surfaceHandleEnter(void *data,\n                               struct wl_surface *surface UNUSED,\n                               struct wl_output *output)\n{\n    _GLFWwindow* window = data;\n    _GLFWmonitor* monitor = wl_output_get_user_data(output);\n\n    if (window->wl.monitorsCount + 1 > window->wl.monitorsSize)\n    {\n        ++window->wl.monitorsSize;\n        window->wl.monitors =\n            realloc(window->wl.monitors,\n                    window->wl.monitorsSize * sizeof(_GLFWmonitor*));\n    }\n\n    window->wl.monitors[window->wl.monitorsCount++] = monitor;\n\n    if (checkScaleChange(window)) {\n        debug(\"Scale changed to %.3f for window %llu in surfaceHandleEnter\\n\", _glfwWaylandWindowScale(window), window->id);\n        apply_scale_changes(window, true, true);\n    }\n}\n\nstatic void surfaceHandleLeave(void *data,\n                               struct wl_surface *surface UNUSED,\n                               struct wl_output *output)\n{\n    _GLFWwindow* window = data;\n    _GLFWmonitor* monitor = wl_output_get_user_data(output);\n    bool found;\n    int i;\n\n    for (i = 0, found = false; i < window->wl.monitorsCount - 1; ++i)\n    {\n        if (monitor == window->wl.monitors[i])\n            found = true;\n        if (found)\n            window->wl.monitors[i] = window->wl.monitors[i + 1];\n    }\n    window->wl.monitors[--window->wl.monitorsCount] = NULL;\n\n    if (checkScaleChange(window)) {\n        debug(\"Scale changed to %.3f for window %llu in surfaceHandleLeave\\n\", _glfwWaylandWindowScale(window), window->id);\n        apply_scale_changes(window, true, true);\n    }\n}\n\n#ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION\nstatic void\nsurface_preferred_buffer_scale(void *data, struct wl_surface *surface UNUSED, int32_t scale) {\n    _GLFWwindow* window = data;\n    window->wl.once.preferred_scale_received = true;\n    if ((int)window->wl.integer_scale.preferred == scale && window->wl.window_fully_created) return;\n    debug(\"Preferred integer buffer scale changed to: %d for window %llu\\n\", scale, window->id);\n    window->wl.integer_scale.preferred = scale;\n    window->wl.window_fully_created = window->wl.once.surface_configured;\n    if (!window->wl.fractional_scale) apply_scale_changes(window, true, true);\n}\n\nstatic void\nsurface_preferred_buffer_transform(void *data, struct wl_surface *surface, uint32_t transform) {\n    (void)data; (void)surface; (void)transform;\n}\n#endif\n\n\nstatic const struct wl_surface_listener surfaceListener = {\n    .enter = surfaceHandleEnter,\n    .leave = surfaceHandleLeave,\n#ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION\n    .preferred_buffer_scale = &surface_preferred_buffer_scale,\n    .preferred_buffer_transform = &surface_preferred_buffer_transform,\n#endif\n};\n\nstatic void\nfractional_scale_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1 UNUSED, uint32_t scale) {\n    _GLFWwindow *window = data;\n    window->wl.once.fractional_scale_received = true;\n    if (scale == window->wl.fractional_scale && window->wl.window_fully_created) return;\n    debug(\"Fractional scale requested: %u/120 = %.2f for window %llu\\n\", scale, scale / 120., window->id);\n    window->wl.fractional_scale = scale;\n    // niri and up-to-date mutter and up-to-date kwin all send the fractional\n    // scale before configure (as of Jan 2025). sway as of 1.10 and Hyprland send it after configure.\n    // https://github.com/hyprwm/Hyprland/issues/9126\n    // labwc doesnt support preferred buffer scale and seems to send only a\n    // single fraction scale event before configure https://github.com/kovidgoyal/kitty/issues/7540\n    window->wl.window_fully_created = window->wl.once.surface_configured;\n    apply_scale_changes(window, true, true);\n}\n\nstatic const struct wp_fractional_scale_v1_listener fractional_scale_listener = {\n    .preferred_scale = &fractional_scale_preferred_scale,\n};\n\nstatic bool\ncreate_surface(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) {\n    window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);\n    if (!window->wl.surface) return false;\n    wl_surface_add_listener(window->wl.surface, &surfaceListener, window);\n    wl_surface_set_user_data(window->wl.surface, window);\n\n    // If we already have been notified of the primary monitor scale, assume\n    // the window will be created on it and so avoid a rescale roundtrip in the common\n    // case of the window being shown on the primary monitor or all monitors having the same scale.\n    // If you change this also change get_window_content_scale() in the kitty code.\n    GLFWmonitor* monitor = glfwGetPrimaryMonitor();\n    float xscale = 1.0, yscale = 1.0;\n    int scale = 1;\n    if (monitor) {\n        glfwGetMonitorContentScale(monitor, &xscale, &yscale);\n        // see wl_monitor.c xscale is always == yscale\n        if (xscale <= 0.0001 || xscale != xscale || xscale >= 24) xscale = 1.0;\n        if (xscale > 1) scale = (int)xscale;\n    }\n    window->wl.expect_scale_from_compositor = _glfw.wl.has_preferred_buffer_scale;\n    if (_glfw.wl.wp_fractional_scale_manager_v1 && _glfw.wl.wp_viewporter) {\n        window->wl.wp_fractional_scale_v1 = wp_fractional_scale_manager_v1_get_fractional_scale(_glfw.wl.wp_fractional_scale_manager_v1, window->wl.surface);\n        if (window->wl.wp_fractional_scale_v1) {\n            window->wl.wp_viewport = wp_viewporter_get_viewport(_glfw.wl.wp_viewporter, window->wl.surface);\n            if (window->wl.wp_viewport) {\n                wp_fractional_scale_v1_add_listener(window->wl.wp_fractional_scale_v1, &fractional_scale_listener, window);\n                window->wl.expect_scale_from_compositor = true;\n            }\n        }\n    }\n    window->wl.window_fully_created = !window->wl.expect_scale_from_compositor;\n    if ((_glfw.wl.ext_background_effect_manager_v1 || _glfw.wl.org_kde_kwin_blur_manager) && wndconfig->blur_radius > 0) _glfwPlatformSetWindowBlur(window, wndconfig->blur_radius);\n\n    window->wl.integer_scale.deduced = scale;\n    if (_glfw.wl.has_preferred_buffer_scale) { scale = 1; window->wl.integer_scale.preferred = 1; }\n\n    debug(\"Creating window %llu at size: %dx%d and scale %d\\n\", window->id, wndconfig->width, wndconfig->height, scale);\n    window->wl.native = wl_egl_window_create(window->wl.surface, wndconfig->width * scale, wndconfig->height * scale);\n    if (!window->wl.native)\n        return false;\n\n    window->wl.width = wndconfig->width;\n    window->wl.height = wndconfig->height;\n    window->wl.user_requested_content_size.width = wndconfig->width;\n    window->wl.user_requested_content_size.height = wndconfig->height;\n\n\n    update_regions(window);\n\n    wl_surface_set_buffer_scale(window->wl.surface, scale);\n    if (_glfw.keyboard_grabbed) inhibit_shortcuts_for(window, true);\n    return true;\n}\n\nstatic void\nsetFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on) {\n    if (!window->wl.xdg.toplevel) return;\n    if (!window->wl.wm_capabilities.fullscreen) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland compositor does not support fullscreen\");\n        return;\n    }\n    if (on) xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, monitor ? monitor->wl.output : NULL);\n    else xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);\n}\n\n\nbool\n_glfwPlatformIsFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {\n    return window->wl.current.toplevel_states & TOPLEVEL_STATE_FULLSCREEN;\n}\n\nbool\n_glfwPlatformToggleFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {\n    bool already_fullscreen = _glfwPlatformIsFullscreen(window, flags);\n    setFullscreen(window, NULL, !already_fullscreen);\n    return !already_fullscreen;\n}\n\nstatic void\nreport_live_resize(_GLFWwindow *w, bool started) {\n    // disabled as mutter, for instance, does not send a configure event when the user stops resizing (aka releases the mouse button)\n    if (false) _glfwInputLiveResize(w, started);\n}\n\nstatic void\nxdgToplevelHandleConfigure(void* data,\n                                       struct xdg_toplevel* toplevel UNUSED,\n                                       int32_t width,\n                                       int32_t height,\n                                       struct wl_array* states)\n{\n    _GLFWwindow* window = data;\n    float aspectRatio;\n    float targetRatio;\n    enum xdg_toplevel_state* state;\n    uint32_t new_states = 0;\n    debug(\"XDG top-level configure event for window %llu: size: %dx%d states: \", window->id, width, height);\n\n    wl_array_for_each(state, states) {\n        switch (*state) {\n#define C(x) case XDG_##x: new_states |= x; debug(\"%s \", #x); break\n            C(TOPLEVEL_STATE_RESIZING);\n            C(TOPLEVEL_STATE_MAXIMIZED);\n            C(TOPLEVEL_STATE_FULLSCREEN);\n            C(TOPLEVEL_STATE_ACTIVATED);\n            C(TOPLEVEL_STATE_TILED_LEFT);\n            C(TOPLEVEL_STATE_TILED_RIGHT);\n            C(TOPLEVEL_STATE_TILED_TOP);\n            C(TOPLEVEL_STATE_TILED_BOTTOM);\n#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION\n            C(TOPLEVEL_STATE_SUSPENDED);\n#endif\n#ifdef XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION\n            C(TOPLEVEL_STATE_CONSTRAINED_LEFT);\n            C(TOPLEVEL_STATE_CONSTRAINED_RIGHT);\n            C(TOPLEVEL_STATE_CONSTRAINED_TOP);\n            C(TOPLEVEL_STATE_CONSTRAINED_BOTTOM);\n#endif\n#undef C\n        }\n    }\n    debug(\"\\n\");\n    if (new_states & TOPLEVEL_STATE_RESIZING) {\n        if (width) window->wl.user_requested_content_size.width = width;\n        if (height) window->wl.user_requested_content_size.height = height;\n        if (!(window->wl.current.toplevel_states & TOPLEVEL_STATE_RESIZING)) report_live_resize(window, true);\n    }\n    if (width != 0 && height != 0)\n    {\n        if (!(new_states & TOPLEVEL_STATE_DOCKED))\n        {\n            if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)\n            {\n                aspectRatio = (float)width / (float)height;\n                targetRatio = (float)window->numer / (float)window->denom;\n                if (aspectRatio < targetRatio)\n                    height = (int32_t)((float)width / targetRatio);\n                else if (aspectRatio > targetRatio)\n                    width = (int32_t)((float)height * targetRatio);\n            }\n        }\n    }\n\n    window->wl.pending.toplevel_states = new_states;\n    window->wl.pending.width = width;\n    window->wl.pending.height = height;\n    window->wl.pending_state |= PENDING_STATE_TOPLEVEL;\n}\n\nstatic void xdgToplevelHandleClose(void* data,\n                                   struct xdg_toplevel* toplevel UNUSED)\n{\n    _GLFWwindow* window = data;\n    window->wl.window_fully_created = true;\n    _glfwInputWindowCloseRequest(window);\n}\n\n#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)\nstatic void\nxdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel UNUSED, struct wl_array *caps) {\n    _GLFWwindow *window = data;\n#define c (window->wl.wm_capabilities)\n    memset(&c, 0, sizeof(c));\n\n    enum xdg_toplevel_wm_capabilities *cap;\n    wl_array_for_each(cap, caps) {\n        switch (*cap) {\n        case XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE: c.maximize = true; break;\n        case XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE: c.minimize = true; break;\n        case XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU: c.window_menu = true; break;\n        case XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN: c.fullscreen = true; break;\n        }\n    }\n    debug(\"Compositor top-level capabilities: maximize=%d minimize=%d window_menu=%d fullscreen=%d\\n\",\n            c.maximize, c.minimize, c.window_menu, c.fullscreen);\n#undef c\n}\n#endif\n\nstatic void\nxdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel UNUSED, int32_t width, int32_t height) {\n    _GLFWwindow *window = data;\n    window->wl.xdg.top_level_bounds.width = width;\n    window->wl.xdg.top_level_bounds.height = height;\n    debug(\"Compositor set top-level bounds of: %dx%d for window %llu\\n\", width, height, window->id);\n}\n\nstatic const struct xdg_toplevel_listener xdgToplevelListener = {\n    .configure = xdgToplevelHandleConfigure,\n    .close = xdgToplevelHandleClose,\n#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION\n    .configure_bounds = xdg_toplevel_configure_bounds,\n    .wm_capabilities = xdg_toplevel_wm_capabilities,\n#endif\n};\n\nstatic void\nupdate_fully_created_on_configure(_GLFWwindow *window) {\n    // See fractional_scale_preferred_scale() for logic\n    if (!window->wl.window_fully_created) {\n        window->wl.window_fully_created = window->wl.once.fractional_scale_received;\n        if (window->wl.window_fully_created) debug(\"Marked window as fully created in configure event\\n\");\n    }\n}\n\nstatic void\napply_xdg_configure_changes(_GLFWwindow *window) {\n    bool suspended_changed = false;\n    if (window->wl.pending_state & PENDING_STATE_TOPLEVEL) {\n        uint32_t new_states = window->wl.pending.toplevel_states;\n        int width = window->wl.pending.width;\n        int height = window->wl.pending.height;\n        if (!window->wl.once.surface_configured) {\n            window->swaps_disallowed = false;\n            wait_for_swap_to_commit(window);\n            window->wl.once.surface_configured = true;\n            update_fully_created_on_configure(window);\n        }\n\n#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION\n        suspended_changed = ((new_states & TOPLEVEL_STATE_SUSPENDED) != (window->wl.current.toplevel_states & TOPLEVEL_STATE_SUSPENDED));\n#endif\n\n        if (new_states != window->wl.current.toplevel_states ||\n                width != window->wl.current.width ||\n                height != window->wl.current.height) {\n\n            bool live_resize_done = !(new_states & TOPLEVEL_STATE_RESIZING) && (window->wl.current.toplevel_states & TOPLEVEL_STATE_RESIZING);\n            window->wl.current.toplevel_states = new_states;\n            window->wl.current.width = width;\n            window->wl.current.height = height;\n            if (live_resize_done) report_live_resize(window, false);\n        }\n    }\n\n    if (window->wl.pending_state & PENDING_STATE_DECORATION) {\n        uint32_t mode = window->wl.pending.decoration_mode;\n        bool has_server_side_decorations = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);\n        // Force CSD when decorations are hidden or titlebar is hidden\n        if (!window->decorated || window->wl.decorations.titlebar_hidden) has_server_side_decorations = false;\n        window->wl.decorations.serverSide = has_server_side_decorations;\n        window->wl.current.decoration_mode = mode;\n    }\n\n    if (window->wl.pending_state) {\n        int width = window->wl.pending.width, height = window->wl.pending.height;\n        csd_set_window_geometry(window, &width, &height);\n        bool resized = dispatchChangesAfterConfigure(window, width, height);\n        csd_set_visible(window, csd_should_window_be_decorated(window));\n        debug(\"Final window %llu content size: %dx%d resized: %d\\n\", window->id, width, height, resized);\n    }\n\n    inform_compositor_of_window_geometry(window, \"configure\");\n    commit_window_surface_if_safe(window);\n    window->wl.pending_state = 0;\n#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION\n    if (suspended_changed) {\n        _glfwInputWindowOcclusion(window, window->wl.current.toplevel_states & TOPLEVEL_STATE_SUSPENDED);\n    }\n#endif\n}\n\ntypedef union pixel {\n    struct {\n        uint8_t blue, green, red, alpha;\n    };\n    uint32_t value;\n} pixel;\n\nstatic struct wl_buffer*\ncreate_single_color_buffer(int width, int height, pixel color) {\n    // convert to pre-multiplied alpha as that's what wayland wants\n    if (width == 1 && height == 1 && _glfw.wl.wp_single_pixel_buffer_manager_v1) {\n#define C(x) (uint32_t)(((double)((uint64_t)color.alpha * color.x * UINT32_MAX)) / (255 * 255))\n        struct wl_buffer *ans = wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(\n            _glfw.wl.wp_single_pixel_buffer_manager_v1, C(red), C(green), C(blue), (uint32_t)((color.alpha / 255.) * UINT32_MAX));\n#undef C\n        if (!ans) _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: failed to create single pixel buffer\");\n        return ans;\n    }\n    float alpha = color.alpha / 255.f;\n    color.red = (uint8_t)(alpha * color.red); color.green = (uint8_t)(alpha * color.green); color.blue = (uint8_t)(alpha * color.blue);\n    int shm_format = color.alpha == 0xff ? WL_SHM_FORMAT_XRGB8888 : WL_SHM_FORMAT_ARGB8888;\n    const size_t size = (size_t)4 * width * height;\n    int fd = createAnonymousFile(size);\n    if (fd < 0) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: failed to create anonymous file\");\n        return NULL;\n    }\n    uint32_t *shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);\n    if (color.value) for (size_t i = 0; i < size/4; i++) shm_data[i] = color.value;\n    else memset(shm_data, 0, size);\n    if (!shm_data) {\n        close(fd);\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: failed to mmap anonymous file\");\n        return NULL;\n    }\n    struct wl_shm_pool *pool = wl_shm_create_pool(_glfw.wl.shm, fd, size);\n    if (!pool) {\n        close(fd); munmap(shm_data, size);\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: failed to create wl_shm_pool of size: %zu\", size);\n        return NULL;\n    }\n    struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, shm_format);\n    wl_shm_pool_destroy(pool); munmap(shm_data, size); close(fd);\n    if (!buffer) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: failed to create wl_buffer of size: %zu\", size);\n        return NULL;\n    }\n    return buffer;\n}\n\nstatic bool\nattach_temp_buffer_during_window_creation(_GLFWwindow *window) {\n    pixel color;\n    color.value = _glfw.hints.window.wl.bgcolor;\n    if (!window->wl.transparent) color.alpha = 0xff;\n    else if (color.alpha == 0) color.value = 0;  // fully transparent blends best with black and we can use memset\n\n    if (window->wl.temp_buffer_used_during_window_creation) {\n        wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);\n        window->wl.temp_buffer_used_during_window_creation = NULL;\n    }\n    int width, height;\n    _glfwPlatformGetFramebufferSize(window, &width, &height);\n\n    if (window->wl.wp_viewport) {\n        window->wl.temp_buffer_used_during_window_creation = create_single_color_buffer(1, 1, color);\n        wl_surface_set_buffer_scale(window->wl.surface, 1);\n        wp_viewport_set_destination(window->wl.wp_viewport, window->wl.width, window->wl.height);\n    } else {\n        window->wl.temp_buffer_used_during_window_creation = create_single_color_buffer(width, height, color);\n        wl_surface_set_buffer_scale(window->wl.surface, window->wl.fractional_scale ? 1: _glfwWaylandIntegerWindowScale(window));\n    }\n    if (!window->wl.temp_buffer_used_during_window_creation) return false;\n    wl_surface_attach(window->wl.surface, window->wl.temp_buffer_used_during_window_creation, 0, 0);\n    debug(\"Attached temp buffer during window %llu creation of size: %dx%d and rgba(%u, %u, %u, %u)\\n\", window->id, width, height, color.red, color.green, color.blue, color.alpha);\n    commit_window_surface(window);\n    return true;\n}\n\nstatic void\nloop_till_window_fully_created(_GLFWwindow *window) {\n    if (!window->wl.window_fully_created) {\n        GLFWwindow *ctx = glfwGetCurrentContext();\n        debug(\"Waiting for compositor to send fractional scale for window %llu\\n\", window->id);\n        monotonic_t start = monotonic();\n        while (!window->wl.window_fully_created && monotonic() - start < ms_to_monotonic_t(300)) {\n            if (wl_display_roundtrip(_glfw.wl.display) == -1) {\n                window->wl.window_fully_created = true;\n            }\n        }\n        window->wl.window_fully_created = true;\n        // If other OS windows were resized when this window is shown, the ctx might have been changed by\n        // user code, restore it to whatever it was at the start.\n        if (glfwGetCurrentContext() != ctx) glfwMakeContextCurrent(ctx);\n    }\n}\n\nstatic void\nxdgSurfaceHandleConfigure(void* data, struct xdg_surface* surface, uint32_t serial) {\n    // The poorly documented pattern Wayland requires is:\n    // 1) ack the configure,\n    // 2) set the window geometry\n    // 3) attach a new buffer of the correct size to the surface\n    // 4) only then commit the surface.\n    // buffer is attached only by eglSwapBuffers,\n    // so we set a flag to not commit the surface till the next swapbuffers. Note that\n    // wl_egl_window_resize() does not actually resize the buffer until the next draw call\n    // or buffer state query.\n    _GLFWwindow* window = data;\n    xdg_surface_ack_configure(surface, serial);\n    debug(\"XDG surface configure event received and acknowledged for window %llu\\n\", window->id);\n    apply_xdg_configure_changes(window);\n    if (!window->wl.window_fully_created) {\n        if (!attach_temp_buffer_during_window_creation(window)) window->wl.window_fully_created = true;\n    }\n}\n\nstatic const struct xdg_surface_listener xdgSurfaceListener = {\n    xdgSurfaceHandleConfigure\n};\n\nstatic void\nsetXdgDecorations(_GLFWwindow* window)\n{\n    if (window->wl.xdg.decoration) {\n        if (window->wl.decorations.titlebar_hidden) {\n            window->wl.decorations.serverSide = false;\n            zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);\n            csd_set_visible(window, csd_should_window_be_decorated(window));\n        } else {\n            window->wl.decorations.serverSide = true;\n            zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, window->decorated ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);\n        }\n    } else {\n        window->wl.decorations.serverSide = false;\n        csd_set_visible(window, csd_should_window_be_decorated(window));\n    }\n}\n\nvoid _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled UNUSED) {\n    setXdgDecorations(window);\n    inform_compositor_of_window_geometry(window, \"SetWindowDecorated\");\n    commit_window_surface_if_safe(window);\n}\n\n\nstatic struct wl_output*\nfind_output_by_name(const char* name) {\n    if (!name || !name[0]) return NULL;\n    for (int i = 0; i < _glfw.monitorCount; i++) {\n        _GLFWmonitor *m = _glfw.monitors[i];\n        if (strcmp(m->name, name) == 0) return m->wl.output;\n    }\n    return NULL;\n}\n\nstatic enum zwlr_layer_shell_v1_layer\nget_layer_shell_layer(const _GLFWwindow *window) {\n    enum zwlr_layer_shell_v1_layer which_layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; // Default to background\n    switch (window->wl.layer_shell.config.type) {\n        case GLFW_LAYER_SHELL_BACKGROUND: case GLFW_LAYER_SHELL_NONE: break;\n        case GLFW_LAYER_SHELL_PANEL: which_layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; break;\n        case GLFW_LAYER_SHELL_TOP: which_layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; break;\n        case GLFW_LAYER_SHELL_OVERLAY: which_layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; break;\n    }\n    return which_layer;\n}\n\nstatic void\nlayer_set_properties(const _GLFWwindow *window, bool during_creation, uint32_t width, uint32_t height) {\n#define config window->wl.layer_shell.config\n#define surface window->wl.layer_shell.zwlr_layer_surface_v1\n    if (!surface) return;  // cannot set properties till a surface is created\n    enum zwlr_layer_surface_v1_anchor which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;\n    int exclusive_zone = config.requested_exclusive_zone;\n    enum zwlr_layer_surface_v1_keyboard_interactivity focus_policy = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;\n    switch(config.focus_policy) {\n        case GLFW_FOCUS_NOT_ALLOWED: focus_policy = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; break;\n        case GLFW_FOCUS_EXCLUSIVE: focus_policy = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE; break;\n        case GLFW_FOCUS_ON_DEMAND: focus_policy = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND; break;\n    }\n    int panel_width = 0, panel_height = 0;\n    switch (config.type) {\n        case GLFW_LAYER_SHELL_NONE: break;\n        case GLFW_LAYER_SHELL_BACKGROUND: exclusive_zone = -1; break;\n        case GLFW_LAYER_SHELL_TOP:\n        case GLFW_LAYER_SHELL_OVERLAY:\n        case GLFW_LAYER_SHELL_PANEL:\n            switch (config.edge) {\n                case GLFW_EDGE_TOP:\n                    which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;\n                    panel_height = height;\n                    if (!config.override_exclusive_zone) exclusive_zone = height;\n                    break;\n                case GLFW_EDGE_BOTTOM:\n                    which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;\n                    panel_height = height;\n                    if (!config.override_exclusive_zone) exclusive_zone = height;\n                    break;\n                case GLFW_EDGE_LEFT:\n                    which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;\n                    panel_width = width;\n                    if (!config.override_exclusive_zone) exclusive_zone = width;\n                    break;\n                case GLFW_EDGE_RIGHT:\n                    which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;\n                    panel_width = width;\n                    if (!config.override_exclusive_zone) exclusive_zone = width;\n                    break;\n                case GLFW_EDGE_CENTER:\n                    break;\n                case GLFW_EDGE_CENTER_SIZED:\n                    which_anchor = 0;\n                    panel_width = width; panel_height = height;\n                    break;\n                case GLFW_EDGE_NONE:\n                    which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;\n                    panel_width = width; panel_height = height;\n                    break;\n            }\n    }\n    zwlr_layer_surface_v1_set_size(surface, panel_width, panel_height);\n    debug(\"Compositor will be informed that layer size: %dx%d viewport: %dx%d at next surface commit\\n\", panel_width, panel_height, width, height);\n    zwlr_layer_surface_v1_set_anchor(surface, which_anchor);\n    zwlr_layer_surface_v1_set_exclusive_zone(surface, exclusive_zone);\n    zwlr_layer_surface_v1_set_margin(surface, config.requested_top_margin, config.requested_right_margin, config.requested_bottom_margin, config.requested_left_margin);\n    if (!during_creation) zwlr_layer_surface_v1_set_layer(surface, get_layer_shell_layer(window));\n    zwlr_layer_surface_v1_set_keyboard_interactivity(surface, focus_policy);\n#undef surface\n#undef config\n}\n\nstatic void\ncalculate_layer_size(_GLFWwindow *window, uint32_t *width, uint32_t *height) {\n    const GLFWLayerShellConfig *config = &window->wl.layer_shell.config;\n    GLFWvidmode m = {0};\n    if (window->wl.monitorsCount) _glfwPlatformGetVideoMode(window->wl.monitors[0], &m);\n    int monitor_width = m.width, monitor_height = m.height;\n    const int y_margin = config->requested_bottom_margin + config->requested_top_margin, x_margin = config->requested_left_margin + config->requested_right_margin;\n    monitor_width = monitor_width > x_margin ? monitor_width - x_margin : 0;\n    monitor_height = monitor_height > y_margin ? monitor_height - y_margin : 0;\n    float xscale = (float)config->expected.xscale, yscale = (float)config->expected.yscale;\n    if (window->wl.window_fully_created) _glfwPlatformGetWindowContentScale(window, &xscale, &yscale);\n    unsigned cell_width, cell_height; double left_edge_spacing, top_edge_spacing, right_edge_spacing, bottom_edge_spacing;\n    config->size_callback((GLFWwindow*)window, xscale, yscale, &cell_width, &cell_height, &left_edge_spacing, &top_edge_spacing, &right_edge_spacing, &bottom_edge_spacing);\n    double spacing_x = left_edge_spacing + right_edge_spacing;\n    double spacing_y = top_edge_spacing + bottom_edge_spacing;\n    if (config->type == GLFW_LAYER_SHELL_BACKGROUND) {\n        if (!*width) *width = monitor_width;\n        if (!*height) *height = monitor_height;\n        return;\n    }\n    const unsigned xsz = config->x_size_in_pixels ? (unsigned)(config->x_size_in_pixels * xscale) : (cell_width * config->x_size_in_cells);\n    const unsigned ysz = config->y_size_in_pixels ? (unsigned)(config->y_size_in_pixels * yscale) : (cell_height * config->y_size_in_cells);\n    debug(\"Calculating layer shell window size at scale: %f cell_size: %u %u sz: %u %u\\n\", xscale, cell_width, cell_height, xsz, ysz);\n    if (config->edge == GLFW_EDGE_LEFT || config->edge == GLFW_EDGE_RIGHT) {\n        if (!*height) *height = monitor_height;\n        double spacing = spacing_x;\n        spacing += xsz / xscale;\n        *width = (uint32_t)(1. + spacing);\n    } else if (config->edge == GLFW_EDGE_TOP || config->edge == GLFW_EDGE_BOTTOM) {\n        if (!*width) *width = monitor_width;\n        double spacing = spacing_y;\n        spacing += ysz / yscale;\n        *height = (uint32_t)(1. + spacing);\n    } else if (config->edge == GLFW_EDGE_CENTER) {\n        if (!*width) *width = monitor_width;\n        if (!*height) *height = monitor_height;\n    } else {\n        spacing_x += xsz / xscale;\n        spacing_y += ysz / yscale;\n        *width = (uint32_t)(1. + spacing_x);\n        *height = (uint32_t)(1. + spacing_y);\n    }\n\n}\n\nstatic void\nlayer_surface_handle_configure(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial, uint32_t width, uint32_t height) {\n    debug(\"Layer shell configure event: width: %u height: %u\\n\", width, height);\n    _GLFWwindow* window = data;\n    if (!window->wl.once.surface_configured) {\n        window->swaps_disallowed = false;\n        wait_for_swap_to_commit(window);\n        window->wl.once.surface_configured = true;\n        update_fully_created_on_configure(window);\n    }\n    calculate_layer_size(window, &width, &height);\n    zwlr_layer_surface_v1_ack_configure(surface, serial);\n    if ((int)width != window->wl.width || (int)height != window->wl.height) {\n        debug(\"Layer shell size changed to %ux%u in layer_surface_handle_configure\\n\", width, height);\n        _glfwInputWindowSize(window, width, height);\n        window->wl.width = width; window->wl.height = height;\n        resizeFramebuffer(window);\n        _glfwInputWindowDamage(window);\n        layer_set_properties(window, false, window->wl.width, window->wl.height);\n        if (window->wl.wp_viewport) wp_viewport_set_destination(window->wl.wp_viewport, window->wl.width, window->wl.height);\n    }\n    commit_window_surface_if_safe(window);\n    if (!window->wl.window_fully_created) {\n        if (!attach_temp_buffer_during_window_creation(window)) window->wl.window_fully_created = true;\n    }\n}\n\nstatic void\nlayer_surface_handle_close_requested(void* data, struct zwlr_layer_surface_v1* surface UNUSED) {\n    _GLFWwindow* window = data;\n    window->wl.window_fully_created = true;\n    _glfwInputWindowCloseRequest(window);\n}\n\nstatic const struct zwlr_layer_surface_v1_listener zwlr_layer_surface_v1_listener = {\n    .configure=layer_surface_handle_configure,\n    .closed=layer_surface_handle_close_requested,\n};\n\nstatic bool\ncreate_layer_shell_surface(_GLFWwindow *window) {\n    if (!_glfw.wl.zwlr_layer_shell_v1) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: wlr-layer-shell protocol unsupported by compositor\");\n        return false;\n    }\n    window->decorated = false;  // shell windows must not have decorations\n    struct wl_output *wl_output = find_output_by_name(window->wl.layer_shell.config.output_name);\n#define ls window->wl.layer_shell.zwlr_layer_surface_v1\n    ls = zwlr_layer_shell_v1_get_layer_surface(\n            _glfw.wl.zwlr_layer_shell_v1, window->wl.surface, wl_output, get_layer_shell_layer(window), window->wl.appId[0] ? window->wl.appId : \"kitty\");\n    if (!ls) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: layer-surface creation failed\");\n        return false;\n    }\n    zwlr_layer_surface_v1_add_listener(ls, &zwlr_layer_surface_v1_listener, window);\n    layer_set_properties(window, true, window->wl.width, window->wl.height);\n    if (window->wl.wp_viewport) wp_viewport_set_destination(window->wl.wp_viewport, window->wl.width, window->wl.height);\n    commit_window_surface(window);\n    wl_display_roundtrip(_glfw.wl.display);\n    window->wl.created = true;\n#undef ls\n    return true;\n}\n\nstatic bool\ncreate_window_desktop_surface(_GLFWwindow* window)\n{\n    if (is_layer_shell(window)) return create_layer_shell_surface(window);\n\n    window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase,\n                                                         window->wl.surface);\n    if (!window->wl.xdg.surface)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: xdg-surface creation failed\");\n        return false;\n    }\n\n    xdg_surface_add_listener(window->wl.xdg.surface,\n                             &xdgSurfaceListener,\n                             window);\n\n    window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface);\n    if (!window->wl.xdg.toplevel)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: xdg-toplevel creation failed\");\n        return false;\n    }\n\n#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION\n    if (_glfw.wl.xdg_wm_base_version < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) {\n        window->wl.wm_capabilities.maximize = true; window->wl.wm_capabilities.minimize = true; window->wl.wm_capabilities.fullscreen = true;\n        window->wl.wm_capabilities.window_menu = true;\n    }\n#endif\n    xdg_toplevel_add_listener(window->wl.xdg.toplevel, &xdgToplevelListener, window);\n    if (_glfw.wl.decorationManager) {\n        window->wl.xdg.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(\n                _glfw.wl.decorationManager, window->wl.xdg.toplevel);\n        zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, &xdgDecorationListener, window);\n    }\n\n    if (window->wl.appId[0])\n        xdg_toplevel_set_app_id(window->wl.xdg.toplevel, window->wl.appId);\n    if (window->wl.windowTag[0] && _glfw.wl.xdg_toplevel_tag_manager_v1)\n        xdg_toplevel_tag_manager_v1_set_toplevel_tag(_glfw.wl.xdg_toplevel_tag_manager_v1, window->wl.xdg.toplevel, window->wl.windowTag);\n\n    if (window->wl.title)\n        xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);\n\n    if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE)\n        xdg_toplevel_set_min_size(window->wl.xdg.toplevel,\n                                  window->minwidth, window->minheight);\n    if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE)\n        xdg_toplevel_set_max_size(window->wl.xdg.toplevel,\n                                  window->maxwidth, window->maxheight);\n\n    if (window->monitor) {\n        if (window->wl.wm_capabilities.fullscreen)\n            xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, window->monitor->wl.output);\n        else\n            _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland compositor does not support fullscreen\");\n    } else {\n        if (window->wl.maximize_on_first_show) {\n            window->wl.maximize_on_first_show = false;\n            xdg_toplevel_set_maximized(window->wl.xdg.toplevel);\n        }\n        setXdgDecorations(window);\n    }\n\n    commit_window_surface(window);\n    wl_display_roundtrip(_glfw.wl.display);\n    window->wl.created = true;\n\n    return true;\n}\n\nstatic void incrementCursorImage(_GLFWwindow* window)\n{\n    if (window && window->wl.decorations.focus == CENTRAL_WINDOW && window->cursorMode != GLFW_CURSOR_HIDDEN) {\n        _GLFWcursor* cursor = window->wl.currentCursor;\n        if (cursor && cursor->wl.cursor && cursor->wl.cursor->image_count)\n        {\n            cursor->wl.currentImage += 1;\n            cursor->wl.currentImage %= cursor->wl.cursor->image_count;\n            setCursorImage(window, false);\n            toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, cursor->wl.cursor->image_count > 1);\n            return;\n        }\n    }\n    toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 1);\n}\n\nvoid\nanimateCursorImage(id_type timer_id UNUSED, void *data UNUSED) {\n    incrementCursorImage(_glfw.wl.pointerFocus);\n}\n\nstatic void\nabortOnFatalError(int last_error) {\n    static bool abort_called = false;\n    if (!abort_called) {\n        abort_called = true;\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: fatal display error: %s\", strerror(last_error));\n        if (_glfw.callbacks.application_close) _glfw.callbacks.application_close(1);\n        else {\n            _GLFWwindow* window = _glfw.windowListHead;\n            while (window)\n            {\n                _glfwInputWindowCloseRequest(window);\n                window = window->next;\n            }\n        }\n    }\n    // ensure the tick callback is called\n    _glfw.wl.eventLoopData.wakeup_data_read = true;\n}\n\nstatic void\nwayland_read_events(int poll_result, int events, void *data UNUSED) {\n    EVDBG(\"wayland_read_events poll_result: %d events: %d\", poll_result, events);\n    if (poll_result > 0 && events) wl_display_read_events(_glfw.wl.display);\n    else wl_display_cancel_read(_glfw.wl.display);\n}\n\nstatic void\nhandle_key_repeat_events(void) {\n    uint64_t num_events = 0;\n#ifdef HAS_TIMER_FD\n    if (read(_glfw.wl.eventLoopData.key_repeat_fd, &num_events, sizeof(num_events)) < (ssize_t)sizeof(num_events)) return;\n#else\n    char buf[16];\n    while(1) {\n        ssize_t num = read(_glfw.wl.eventLoopData.key_repeat_fds[0], buf, sizeof(buf));\n        if (num > 0) num_events += num;\n        else if (num == 0 || errno != EINTR) break;\n    }\n#endif\n    if (_glfw.wl.keyRepeatInfo.keyboardFocusId != _glfw.wl.keyboardFocusId || _glfw.wl.keyboardRepeatRate == 0) return;\n    _GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);\n    if (!window || !_glfw.wl.keyRepeatInfo.key) return;\n    while (num_events--) glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);\n}\n\nstatic void handleEvents(monotonic_t timeout)\n{\n    struct wl_display* display = _glfw.wl.display;\n    errno = 0;\n    EVDBG(\"starting handleEvents(%.2f)\", monotonic_t_to_s_double(timeout));\n\n    while (wl_display_prepare_read(display) != 0) {\n        if (wl_display_dispatch_pending(display) == -1) {\n            abortOnFatalError(errno);\n            return;\n        }\n    }\n\n    // If an error different from EAGAIN happens, we have likely been\n    // disconnected from the Wayland session, try to handle that the best we\n    // can.\n    errno = 0;\n    if (wl_display_flush(display) < 0 && errno != EAGAIN)\n    {\n        wl_display_cancel_read(display);\n        abortOnFatalError(errno);\n        return;\n    }\n\n    // we pass in wayland_read_events to ensure that the above wl_display_prepare_read call\n    // is followed by either wl_display_cancel_read or wl_display_read_events\n    // before any events/timers are dispatched. This allows other wayland functions\n    // to be called in the event/timer handlers without causing a deadlock\n    bool display_read_ok = pollForEvents(&_glfw.wl.eventLoopData, timeout, wayland_read_events);\n    EVDBG(\"display_read_ok: %d\", display_read_ok);\n    if (display_read_ok) {\n        int num = wl_display_dispatch_pending(display);\n        (void)num;\n        EVDBG(\"dispatched %d Wayland events\", num);\n    }\n    glfw_ibus_dispatch(&_glfw.wl.xkb.ibus);\n    glfw_dbus_session_bus_dispatch();\n    EVDBG(\"other dispatch done\");\n    if (_glfw.wl.eventLoopData.wakeup_fd_ready) check_for_wakeup_events(&_glfw.wl.eventLoopData);\n    if (_glfw.wl.eventLoopData.key_repeat_fd_ready) handle_key_repeat_events();\n}\n\nstatic struct wl_cursor*\ntry_cursor_names(struct wl_cursor_theme* theme, int arg_count, ...) {\n    struct wl_cursor* ans = NULL;\n    va_list ap;\n    va_start(ap, arg_count);\n    for (int i = 0; i < arg_count && !ans; i++) {\n        const char *name = va_arg(ap, const char *);\n        ans = wl_cursor_theme_get_cursor(theme, name);\n    }\n    va_end(ap);\n    return ans;\n}\n\nstruct wl_cursor* _glfwLoadCursor(GLFWCursorShape shape, struct wl_cursor_theme* theme)\n{\n    static bool warnings[GLFW_INVALID_CURSOR] = {0};\n    if (!theme) return NULL;\n#define NUMARGS(...)  (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*))\n#define C(name, ...) case name: { \\\n    ans = try_cursor_names(theme, NUMARGS(__VA_ARGS__), __VA_ARGS__); \\\n    if (!ans && !warnings[name]) {\\\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Could not find standard cursor: %s\", #name); \\\n        warnings[name] = true; \\\n    } \\\n    break; }\n\n    struct wl_cursor* ans = NULL;\n    switch (shape)\n    {\n        /* start glfw to xc mapping (auto generated by gen-key-constants.py do not edit) */\n        C(GLFW_DEFAULT_CURSOR, \"default\", \"left_ptr\");\n        C(GLFW_TEXT_CURSOR, \"text\", \"xterm\", \"ibeam\");\n        C(GLFW_POINTER_CURSOR, \"pointing_hand\", \"pointer\", \"hand2\", \"hand\");\n        C(GLFW_HELP_CURSOR, \"help\", \"question_arrow\", \"whats_this\");\n        C(GLFW_WAIT_CURSOR, \"wait\", \"clock\", \"watch\");\n        C(GLFW_PROGRESS_CURSOR, \"progress\", \"half-busy\", \"left_ptr_watch\");\n        C(GLFW_CROSSHAIR_CURSOR, \"crosshair\", \"tcross\");\n        C(GLFW_CELL_CURSOR, \"cell\", \"plus\", \"cross\");\n        C(GLFW_VERTICAL_TEXT_CURSOR, \"vertical-text\");\n        C(GLFW_MOVE_CURSOR, \"move\", \"fleur\", \"pointer-move\");\n        C(GLFW_E_RESIZE_CURSOR, \"e-resize\", \"right_side\");\n        C(GLFW_NE_RESIZE_CURSOR, \"ne-resize\", \"top_right_corner\");\n        C(GLFW_NW_RESIZE_CURSOR, \"nw-resize\", \"top_left_corner\");\n        C(GLFW_N_RESIZE_CURSOR, \"n-resize\", \"top_side\");\n        C(GLFW_SE_RESIZE_CURSOR, \"se-resize\", \"bottom_right_corner\");\n        C(GLFW_SW_RESIZE_CURSOR, \"sw-resize\", \"bottom_left_corner\");\n        C(GLFW_S_RESIZE_CURSOR, \"s-resize\", \"bottom_side\");\n        C(GLFW_W_RESIZE_CURSOR, \"w-resize\", \"left_side\");\n        C(GLFW_EW_RESIZE_CURSOR, \"ew-resize\", \"sb_h_double_arrow\", \"split_h\");\n        C(GLFW_NS_RESIZE_CURSOR, \"ns-resize\", \"sb_v_double_arrow\", \"split_v\");\n        C(GLFW_NESW_RESIZE_CURSOR, \"nesw-resize\", \"size_bdiag\", \"size-bdiag\");\n        C(GLFW_NWSE_RESIZE_CURSOR, \"nwse-resize\", \"size_fdiag\", \"size-fdiag\");\n        C(GLFW_ZOOM_IN_CURSOR, \"zoom-in\", \"zoom_in\");\n        C(GLFW_ZOOM_OUT_CURSOR, \"zoom-out\", \"zoom_out\");\n        C(GLFW_ALIAS_CURSOR, \"dnd-link\");\n        C(GLFW_COPY_CURSOR, \"dnd-copy\");\n        C(GLFW_NOT_ALLOWED_CURSOR, \"not-allowed\", \"forbidden\", \"crossed_circle\");\n        C(GLFW_NO_DROP_CURSOR, \"no-drop\", \"dnd-no-drop\");\n        C(GLFW_GRAB_CURSOR, \"grab\", \"openhand\", \"hand1\");\n        C(GLFW_GRABBING_CURSOR, \"grabbing\", \"closedhand\", \"dnd-none\");\n/* end glfw to xc mapping */\n        case GLFW_INVALID_CURSOR:\n            break;\n    }\n    return ans;\n#undef NUMARGS\n#undef C\n}\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nstatic bool\nattach_opengl_context_to_window(_GLFWwindow *window, const _GLFWctxconfig *ctxconfig, const _GLFWfbconfig *fbconfig) {\n    if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||\n        ctxconfig->source == GLFW_NATIVE_CONTEXT_API)\n    {\n        if (!_glfwInitEGL())\n            return false;\n        if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))\n            return false;\n    }\n    else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)\n    {\n        if (!_glfwInitOSMesa())\n            return false;\n        if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))\n            return false;\n    }\n    return true;\n}\n\nint _glfwPlatformCreateWindow(\n    _GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig,\n    const GLFWLayerShellConfig *lsc\n) {\n    window->wl.layer_shell.config = lsc ? *lsc : (GLFWLayerShellConfig){0};\n    csd_initialize_metrics(window);\n    window->wl.transparent = fbconfig->transparent;\n    strncpy(window->wl.appId, wndconfig->wl.appId, sizeof(window->wl.appId));\n    window->swaps_disallowed = true;\n\n    if (!create_surface(window, wndconfig)) return false;\n    if (wndconfig->title) window->wl.title = _glfw_strdup(wndconfig->title);\n    if (wndconfig->maximized) window->wl.maximize_on_first_show = true;\n    if (wndconfig->visible) {\n        if (!create_window_desktop_surface(window)) return false;\n        window->wl.visible = true;\n    } else {\n        window->wl.visible = false;\n        window->wl.xdg.surface = NULL;\n        window->wl.xdg.toplevel = NULL;\n        window->wl.layer_shell.zwlr_layer_surface_v1 = NULL;\n    }\n\n\n    window->wl.currentCursor = NULL;\n    // Don't set window->wl.cursorTheme to NULL here.\n\n    window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));\n    window->wl.monitorsCount = 0;\n    window->wl.monitorsSize = 1;\n    // looping till window fully created attaches a single pixel buffer to the window,\n    // this cannot be done once a OpenGL context is created for the window. So first loop\n    // and only then create the OpenGL context.\n    if (window->wl.visible) loop_till_window_fully_created(window);\n    debug(\"Creating OpenGL context and attaching it to window\\n\");\n    if (ctxconfig->client != GLFW_NO_API) attach_opengl_context_to_window(window, ctxconfig, fbconfig);\n    return true;\n}\n\nvoid _glfwPlatformDestroyWindow(_GLFWwindow* window)\n{\n    if (window == _glfw.wl.pointerFocus)\n    {\n        _glfw.wl.pointerFocus = NULL;\n        _glfwInputCursorEnter(window, false);\n    }\n    if (window->id == _glfw.wl.keyboardFocusId)\n    {\n        _glfw.wl.keyboardFocusId = 0;\n        _glfwInputWindowFocus(window, false);\n    }\n    if (window->id == _glfw.wl.keyRepeatInfo.keyboardFocusId) {\n        _glfw.wl.keyRepeatInfo.keyboardFocusId = 0;\n    }\n    if (window->wl.keyboard_shortcuts_inhibitor)\n        zwp_keyboard_shortcuts_inhibitor_v1_destroy(window->wl.keyboard_shortcuts_inhibitor);\n\n    if (window->wl.temp_buffer_used_during_window_creation)\n        wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);\n\n    if (window->wl.wp_fractional_scale_v1)\n        wp_fractional_scale_v1_destroy(window->wl.wp_fractional_scale_v1);\n    if (window->wl.wp_viewport)\n        wp_viewport_destroy(window->wl.wp_viewport);\n    if (window->wl.org_kde_kwin_blur)\n        org_kde_kwin_blur_release(window->wl.org_kde_kwin_blur);\n    if (window->wl.ext_background_effect_surface_v1)\n        ext_background_effect_surface_v1_destroy(window->wl.ext_background_effect_surface_v1);\n\n    if (window->context.destroy)\n        window->context.destroy(window);\n\n    csd_free_all_resources(window);\n    if (window->wl.xdg.decoration)\n        zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);\n\n    if (window->wl.native)\n        wl_egl_window_destroy(window->wl.native);\n\n    if (window->wl.xdg.toplevel)\n        xdg_toplevel_destroy(window->wl.xdg.toplevel);\n\n    if (window->wl.xdg.surface)\n        xdg_surface_destroy(window->wl.xdg.surface);\n\n    if (window->wl.layer_shell.zwlr_layer_surface_v1)\n        zwlr_layer_surface_v1_destroy(window->wl.layer_shell.zwlr_layer_surface_v1);\n\n    if (window->wl.surface)\n        wl_surface_destroy(window->wl.surface);\n\n    free(window->wl.title);\n    free(window->wl.monitors);\n    if (window->wl.frameCallbackData.current_wl_callback)\n        wl_callback_destroy(window->wl.frameCallbackData.current_wl_callback);\n}\n\nvoid _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)\n{\n    if (window->wl.title) {\n        if (title && strcmp(title, window->wl.title) == 0) return;\n        free(window->wl.title);\n    } else if (!title) return;\n    // Wayland cannot handle requests larger than ~8200 bytes. Sending\n    // one causes an abort(). Since titles this large are meaningless anyway\n    // ensure they do not happen.\n    window->wl.title = utf_8_strndup(title, 2048);\n    if (window->wl.xdg.toplevel) {\n        xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);\n        csd_change_title(window);\n        commit_window_surface_if_safe(window);\n    }\n}\n\nvoid\n_glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images) {\n    if (is_layer_shell(window)) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Cannot set window icon on layer shell surfaces\");\n        return;\n    }\n    if (!window->wl.xdg.toplevel) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Ignoring attempt to set window icon on window without a toplevel\");\n        return;\n    }\n    if (!_glfw.wl.xdg_toplevel_icon_manager_v1) {\n        static bool warned_once = false;\n        if (!warned_once) {\n            _glfwInputError(GLFW_FEATURE_UNAVAILABLE, \"Wayland: The compositor does not support changing window icons\");\n            warned_once = true;\n        }\n        return;\n    }\n    if (!count) {\n        xdg_toplevel_icon_manager_v1_set_icon(_glfw.wl.xdg_toplevel_icon_manager_v1, window->wl.xdg.toplevel, NULL);\n        return;\n    }\n    struct wl_buffer* *buffers = malloc(sizeof(struct wl_buffer*) * count);\n    if (!buffers) return;\n    size_t total_data_size = 0;\n    for (int i = 0; i < count; i++) total_data_size += (size_t)images[i].width * images[i].height * 4;\n    int fd = createAnonymousFile(total_data_size);\n    if (fd < 0) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Creating a buffer file for %ld B failed: %s\", (long)total_data_size, strerror(errno));\n        free(buffers);\n        return;\n    }\n    unsigned char *data = mmap(NULL, total_data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);\n    if (data == MAP_FAILED) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: mmap failed: %s\", strerror(errno));\n        free(buffers);\n        close(fd);\n        return;\n    }\n    struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, total_data_size);\n    struct xdg_toplevel_icon_v1 *icon = xdg_toplevel_icon_manager_v1_create_icon(_glfw.wl.xdg_toplevel_icon_manager_v1);\n    size_t pos = 0;\n    for (int i = 0; i < count; i++) {\n        const size_t sz = (size_t)images[i].width * images[i].height * 4;\n        convert_glfw_image_to_wayland_image(images + i, data + pos);\n        buffers[i] = wl_shm_pool_create_buffer(\n                pool, pos, images[i].width, images[i].height, images[i].width * 4, WL_SHM_FORMAT_ARGB8888);\n        xdg_toplevel_icon_v1_add_buffer(icon, buffers[i], 1);\n        pos += sz;\n    }\n    xdg_toplevel_icon_manager_v1_set_icon(_glfw.wl.xdg_toplevel_icon_manager_v1, window->wl.xdg.toplevel, icon);\n    xdg_toplevel_icon_v1_destroy(icon);\n    for (int i = 0; i < count; i++) wl_buffer_destroy(buffers[i]);\n    free(buffers);\n    wl_shm_pool_destroy(pool);\n    munmap(data, total_data_size);\n    close(fd);\n}\n\nvoid _glfwPlatformGetWindowPos(_GLFWwindow* window UNUSED, int* xpos UNUSED, int* ypos UNUSED)\n{\n    // A Wayland client is not aware of its position, so just warn and leave it\n    // as (0, 0)\n    static bool warned_once = false;\n    if (!warned_once) {\n        _glfwInputError(GLFW_FEATURE_UNAVAILABLE,\n                        \"Wayland: The platform does not provide the window position\");\n        warned_once = true;\n    }\n}\n\nvoid _glfwPlatformSetWindowPos(_GLFWwindow* window UNUSED, int xpos UNUSED, int ypos UNUSED)\n{\n    // A Wayland client can not set its position, so just warn\n\n    _glfwInputError(GLFW_FEATURE_UNAVAILABLE,\n                    \"Wayland: The platform does not support setting the window position\");\n}\n\nvoid _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)\n{\n    if (width)\n        *width = window->wl.width;\n    if (height)\n        *height = window->wl.height;\n}\n\nvoid _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)\n{\n    if (is_layer_shell(window)) {\n        _glfwInputError(GLFW_FEATURE_UNAVAILABLE,\n                        \"Wayland: Resizing of layer shell surfaces is not supported\");\n        return;\n    }\n    if (width != window->wl.width || height != window->wl.height) {\n        window->wl.user_requested_content_size.width = width;\n        window->wl.user_requested_content_size.height = height;\n        int32_t w = 0, h = 0;\n        csd_set_window_geometry(window, &w, &h);\n        window->wl.width = w; window->wl.height = h;\n        resizeFramebuffer(window);\n        csd_set_visible(window, csd_should_window_be_decorated(window));  // resizes the csd iff the window currently has csd\n        commit_window_surface_if_safe(window);\n        inform_compositor_of_window_geometry(window, \"SetWindowSize\");\n    }\n}\n\nvoid _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,\n                                      int minwidth, int minheight,\n                                      int maxwidth, int maxheight)\n{\n    if (window->wl.xdg.toplevel)\n    {\n        if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)\n            minwidth = minheight = 0;\n        if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)\n            maxwidth = maxheight = 0;\n        xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight);\n        xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight);\n        commit_window_surface_if_safe(window);\n    }\n}\n\nvoid _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window UNUSED,\n                                       int numer UNUSED, int denom UNUSED)\n{\n    // TODO: find out how to trigger a resize.\n    // The actual limits are checked in the xdg_toplevel::configure handler.\n    _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,\n                    \"Wayland: Window aspect ratio not yet implemented\");\n}\n\nvoid _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window UNUSED,\n                                          int widthincr UNUSED, int heightincr UNUSED)\n{\n    // TODO: find out how to trigger a resize.\n    // The actual limits are checked in the xdg_toplevel::configure handler.\n}\n\nvoid _glfwPlatformGetFramebufferSize(_GLFWwindow* window,\n                                     int* width, int* height)\n{\n    _glfwPlatformGetWindowSize(window, width, height);\n    double fscale = _glfwWaylandWindowScale(window);\n    if (width)\n        *width = (int)round(*width * fscale);\n    if (height)\n        *height = (int)round(*height * fscale);\n}\n\nvoid _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,\n                                     int* left, int* top,\n                                     int* right, int* bottom)\n{\n    if (window->decorated && !window->monitor && !window->wl.decorations.serverSide)\n    {\n        if (top)\n            *top = window->wl.decorations.titlebar_hidden ? 0 :\n                window->wl.decorations.metrics.top - window->wl.decorations.metrics.visible_titlebar_height;\n        if (left)\n            *left = window->wl.decorations.metrics.width;\n        if (right)\n            *right = window->wl.decorations.metrics.width;\n        if (bottom)\n            *bottom = window->wl.decorations.metrics.width;\n    }\n}\n\nvoid _glfwPlatformGetWindowContentScale(_GLFWwindow* window,\n                                        float* xscale, float* yscale)\n{\n    float fscale = (float)_glfwWaylandWindowScale(window);\n    if (xscale)\n        *xscale = fscale;\n    if (yscale)\n        *yscale = fscale;\n}\n\nmonotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)\n{\n    return ms_to_monotonic_t(500ll);\n}\n\nvoid _glfwPlatformIconifyWindow(_GLFWwindow* window)\n{\n    if (window->wl.xdg.toplevel) {\n        if (window->wl.wm_capabilities.minimize) xdg_toplevel_set_minimized(window->wl.xdg.toplevel);\n        else _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland compositor does not support minimizing windows\");\n    }\n}\n\nvoid _glfwPlatformRestoreWindow(_GLFWwindow* window)\n{\n    if (window->wl.xdg.toplevel)\n    {\n        if (window->monitor)\n            xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);\n        if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED)\n            xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);\n        // There is no way to unset minimized, or even to know if we are\n        // minimized, so there is nothing to do in this case.\n    }\n    _glfwInputWindowMonitor(window, NULL);\n}\n\nvoid _glfwPlatformMaximizeWindow(_GLFWwindow* window)\n{\n    if (window->wl.xdg.toplevel) {\n        if (window->wl.wm_capabilities.maximize) xdg_toplevel_set_maximized(window->wl.xdg.toplevel);\n        else _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland compositor does not support maximizing windows\");\n    }\n}\n\nvoid _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen UNUSED)\n{\n    if (!window->wl.visible) {\n        if (!window->wl.created) {\n            create_window_desktop_surface(window);\n            window->wl.visible = true;\n        } else {\n            // workaround for kwin layer shell bug: https://bugs.kde.org/show_bug.cgi?id=503121\n            if (is_layer_shell(window)) layer_set_properties(window, false, window->wl.width, window->wl.height);\n            window->wl.visible = true;\n            commit_window_surface(window);\n        }\n        debug(\"Window %llu mapped waiting for configure event from compositor\\n\", window->id);\n    }\n}\n\nvoid _glfwPlatformHideWindow(_GLFWwindow* window)\n{\n    if (!window->wl.visible) return;\n    wl_surface_attach(window->wl.surface, NULL, 0, 0);\n    window->wl.once.surface_configured = false;\n    window->swaps_disallowed = true;\n    window->wl.visible = false;\n    commit_window_surface(window);\n    debug(\"Window %llu unmapped\\n\", window->id);\n}\n\nbool\n_glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig *value) {\n    if (!is_layer_shell(window)) return false;\n    if (value) window->wl.layer_shell.config = *value;\n    uint32_t width, height;\n    calculate_layer_size(window, &width, &height);\n    layer_set_properties(window, false, width, height);\n    commit_window_surface(window);\n    return true;\n}\n\nstatic void\nrequest_attention(GLFWwindow *window, const char *token, void *data UNUSED) {\n    if (window && token && token[0] && _glfw.wl.xdg_activation_v1) xdg_activation_v1_activate(_glfw.wl.xdg_activation_v1, token, ((_GLFWwindow*)window)->wl.surface);\n}\n\nstatic bool\nhas_activation_in_flight(_GLFWwindow* window, GLFWactivationcallback callback) {\n    for (size_t i = 0; i < _glfw.wl.activation_requests.sz; i++) {\n        glfw_wl_xdg_activation_request *r = _glfw.wl.activation_requests.array + i;\n        if (r->window_id == window->id && r->callback == callback) return true;\n    }\n    return false;\n}\n\nvoid _glfwPlatformRequestWindowAttention(_GLFWwindow* window) {\n    if (!has_activation_in_flight(window, request_attention)) get_activation_token(window, 0, request_attention, NULL);\n}\n\nint _glfwPlatformWindowBell(_GLFWwindow* window UNUSED)\n{\n    // TODO: Use an actual Wayland API to implement this when one becomes available\n    return false;\n}\n\nstatic void\nfocus_window(GLFWwindow *window, const char *token, void *data UNUSED) {\n    if (!window) return;\n    if (token && token[0] && _glfw.wl.xdg_activation_v1) xdg_activation_v1_activate(_glfw.wl.xdg_activation_v1, token, ((_GLFWwindow*)window)->wl.surface);\n    else {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Window focus request via xdg-activation protocol was denied or is unsupported by the compositor. Use a better compositor.\");\n    }\n}\n\nvoid _glfwPlatformFocusWindow(_GLFWwindow* window UNUSED)\n{\n    // Attempt to focus the window by using the activation protocol, whether it works\n    // is entirely compositor dependent and as we all know Wayland and its ecosystem is\n    // the product of morons.\n    if (_glfw.wl.input_serial && !has_activation_in_flight(window, focus_window)) get_activation_token(window, _glfw.wl.input_serial, focus_window, NULL);\n}\n\nvoid _glfwPlatformSetWindowMonitor(_GLFWwindow* window,\n                                   _GLFWmonitor* monitor,\n                                   int xpos UNUSED, int ypos UNUSED,\n                                   int width UNUSED, int height UNUSED,\n                                   int refreshRate UNUSED)\n{\n    setFullscreen(window, monitor, monitor != NULL);\n    _glfwInputWindowMonitor(window, monitor);\n}\n\nint _glfwPlatformWindowFocused(_GLFWwindow* window)\n{\n    return _glfw.wl.keyboardFocusId == (window ? window->id : 0);\n}\n\nint _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)\n{\n#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION\n    return (window->wl.current.toplevel_states & TOPLEVEL_STATE_SUSPENDED) != 0;\n#endif\n    return false;\n}\n\nint _glfwPlatformWindowIconified(_GLFWwindow* window UNUSED)\n{\n    // xdg-shell doesn’t give any way to request whether a surface is\n    // iconified.\n    return false;\n}\n\nint _glfwPlatformWindowVisible(_GLFWwindow* window)\n{\n    return window->wl.visible;\n}\n\nint _glfwPlatformWindowMaximized(_GLFWwindow* window)\n{\n    return window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED;\n}\n\nint _glfwPlatformWindowHovered(_GLFWwindow* window)\n{\n    return window->wl.hovered;\n}\n\nint _glfwPlatformFramebufferTransparent(_GLFWwindow* window)\n{\n    return window->wl.transparent;\n}\n\nvoid _glfwPlatformSetWindowResizable(_GLFWwindow* window UNUSED, bool enabled UNUSED)\n{\n    // TODO\n    _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,\n                    \"Wayland: Window attribute setting not implemented yet\");\n}\n\nvoid _glfwPlatformSetWindowFloating(_GLFWwindow* window UNUSED, bool enabled UNUSED)\n{\n    // TODO\n    _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,\n                    \"Wayland: Window attribute setting not implemented yet\");\n}\n\nvoid _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)\n{\n    if (enabled)\n    {\n        struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);\n        wl_surface_set_input_region(window->wl.surface, region);\n        wl_region_destroy(region);\n    }\n    else\n        wl_surface_set_input_region(window->wl.surface, 0);\n    commit_window_surface_if_safe(window);\n}\n\nfloat _glfwPlatformGetWindowOpacity(_GLFWwindow* window UNUSED)\n{\n    return 1.f;\n}\n\nvoid _glfwPlatformSetWindowOpacity(_GLFWwindow* window UNUSED, float opacity UNUSED)\n{\n    _glfwInputError(GLFW_FEATURE_UNAVAILABLE,\n                    \"Wayland: The platform does not support setting the window opacity\");\n}\n\nvoid _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED)\n{\n    // This is handled in relativePointerHandleRelativeMotion\n}\n\nbool _glfwPlatformRawMouseMotionSupported(void)\n{\n    return true;\n}\n\nvoid _glfwPlatformPollEvents(void)\n{\n    wl_display_dispatch_pending(_glfw.wl.display);\n    handleEvents(0);\n}\n\nvoid _glfwPlatformWaitEvents(void)\n{\n    monotonic_t timeout = wl_display_dispatch_pending(_glfw.wl.display) > 0 ? 0 : -1;\n    handleEvents(timeout);\n}\n\nvoid _glfwPlatformWaitEventsTimeout(monotonic_t timeout)\n{\n    if (wl_display_dispatch_pending(_glfw.wl.display) > 0) timeout = 0;\n    handleEvents(timeout);\n}\n\nvoid _glfwPlatformPostEmptyEvent(void)\n{\n    wakeupEventLoop(&_glfw.wl.eventLoopData);\n}\n\nvoid _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)\n{\n    if (xpos)\n        *xpos = window->wl.cursorPosX;\n    if (ypos)\n        *ypos = window->wl.cursorPosY;\n}\n\nstatic bool isPointerLocked(_GLFWwindow* window);\n\nvoid _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)\n{\n    if (isPointerLocked(window))\n    {\n        zwp_locked_pointer_v1_set_cursor_position_hint(\n            window->wl.pointerLock.lockedPointer,\n            wl_fixed_from_double(x), wl_fixed_from_double(y));\n        commit_window_surface_if_safe(window);\n    }\n}\n\nvoid _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode UNUSED)\n{\n    _glfwPlatformSetCursor(window, window->wl.currentCursor);\n}\n\nconst char* _glfwPlatformGetNativeKeyName(int native_key)\n{\n    return glfw_xkb_keysym_name(native_key);\n}\n\nint _glfwPlatformGetNativeKeyForKey(uint32_t key)\n{\n    return glfw_xkb_sym_for_key(key);\n}\n\nint _glfwPlatformCreateCursor(_GLFWcursor* cursor,\n                              const GLFWimage* image,\n                              int xhot, int yhot, int count UNUSED)\n{\n    cursor->wl.buffer = createShmBuffer(image, false, true);\n    if (!cursor->wl.buffer)\n        return false;\n\n    cursor->wl.width = image->width;\n    cursor->wl.height = image->height;\n    cursor->wl.xhot = xhot;\n    cursor->wl.yhot = yhot;\n    cursor->wl.scale = -1;\n    cursor->wl.shape = GLFW_INVALID_CURSOR;\n    return true;\n}\n\nint _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape)\n{\n    // Don't actually load the cursor at this point,\n    // because there's not enough info to be properly HiDPI aware.\n    cursor->wl.cursor = NULL;\n    cursor->wl.currentImage = 0;\n    cursor->wl.scale = 0;\n    cursor->wl.shape = shape;\n    return true;\n}\n\nvoid _glfwPlatformDestroyCursor(_GLFWcursor* cursor)\n{\n    // If it's a standard cursor we don't need to do anything here\n    if (cursor->wl.cursor)\n        return;\n\n    if (cursor->wl.buffer)\n        wl_buffer_destroy(cursor->wl.buffer);\n}\n\nstatic void relativePointerHandleRelativeMotion(void* data,\n                                                struct zwp_relative_pointer_v1* pointer UNUSED,\n                                                uint32_t timeHi UNUSED,\n                                                uint32_t timeLo UNUSED,\n                                                wl_fixed_t dx,\n                                                wl_fixed_t dy,\n                                                wl_fixed_t dxUnaccel,\n                                                wl_fixed_t dyUnaccel)\n{\n    _GLFWwindow* window = data;\n    double xpos = window->virtualCursorPosX;\n    double ypos = window->virtualCursorPosY;\n\n    if (window->cursorMode != GLFW_CURSOR_DISABLED)\n        return;\n\n    if (window->rawMouseMotion)\n    {\n        xpos += wl_fixed_to_double(dxUnaccel);\n        ypos += wl_fixed_to_double(dyUnaccel);\n    }\n    else\n    {\n        xpos += wl_fixed_to_double(dx);\n        ypos += wl_fixed_to_double(dy);\n    }\n\n    _glfwInputCursorPos(window, xpos, ypos);\n}\n\nstatic const struct zwp_relative_pointer_v1_listener relativePointerListener = {\n    relativePointerHandleRelativeMotion\n};\n\nstatic void lockedPointerHandleLocked(void* data UNUSED,\n                                      struct zwp_locked_pointer_v1* lockedPointer UNUSED)\n{\n}\n\nstatic void unlockPointer(_GLFWwindow* window)\n{\n    struct zwp_relative_pointer_v1* relativePointer =\n        window->wl.pointerLock.relativePointer;\n    struct zwp_locked_pointer_v1* lockedPointer =\n        window->wl.pointerLock.lockedPointer;\n\n    zwp_relative_pointer_v1_destroy(relativePointer);\n    zwp_locked_pointer_v1_destroy(lockedPointer);\n\n    window->wl.pointerLock.relativePointer = NULL;\n    window->wl.pointerLock.lockedPointer = NULL;\n}\n\nstatic void lockPointer(_GLFWwindow* window UNUSED);\n\nstatic void lockedPointerHandleUnlocked(void* data UNUSED,\n                                        struct zwp_locked_pointer_v1* lockedPointer UNUSED)\n{\n}\n\nstatic const struct zwp_locked_pointer_v1_listener lockedPointerListener = {\n    lockedPointerHandleLocked,\n    lockedPointerHandleUnlocked\n};\n\nstatic void lockPointer(_GLFWwindow* window)\n{\n    struct zwp_relative_pointer_v1* relativePointer;\n    struct zwp_locked_pointer_v1* lockedPointer;\n\n    if (!_glfw.wl.relativePointerManager)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: no relative pointer manager\");\n        return;\n    }\n\n    relativePointer =\n        zwp_relative_pointer_manager_v1_get_relative_pointer(\n            _glfw.wl.relativePointerManager,\n            _glfw.wl.pointer);\n    zwp_relative_pointer_v1_add_listener(relativePointer,\n                                         &relativePointerListener,\n                                         window);\n\n    lockedPointer =\n        zwp_pointer_constraints_v1_lock_pointer(\n            _glfw.wl.pointerConstraints,\n            window->wl.surface,\n            _glfw.wl.pointer,\n            NULL,\n            ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);\n    zwp_locked_pointer_v1_add_listener(lockedPointer,\n                                       &lockedPointerListener,\n                                       window);\n\n    window->wl.pointerLock.relativePointer = relativePointer;\n    window->wl.pointerLock.lockedPointer = lockedPointer;\n\n    set_cursor_surface(NULL, 0, 0, \"lockPointer\");\n}\n\nstatic bool isPointerLocked(_GLFWwindow* window)\n{\n    return window->wl.pointerLock.lockedPointer != NULL;\n}\n\nvoid _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)\n{\n    if (!_glfw.wl.pointer)\n        return;\n\n    window->wl.currentCursor = cursor;\n\n    // If we're not in the correct window just save the cursor\n    // the next time the pointer enters the window the cursor will change\n    if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != CENTRAL_WINDOW)\n        return;\n\n    // Unlock possible pointer lock if no longer disabled.\n    if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))\n        unlockPointer(window);\n\n    if (window->cursorMode == GLFW_CURSOR_NORMAL)\n    {\n        setCursorImage(window, false);\n    }\n    else if (window->cursorMode == GLFW_CURSOR_DISABLED)\n    {\n        if (!isPointerLocked(window))\n            lockPointer(window);\n    }\n    else if (window->cursorMode == GLFW_CURSOR_HIDDEN)\n    {\n        set_cursor_surface(NULL, 0, 0, \"_glfwPlatformSetCursor\");\n    }\n}\n\nstatic bool\nwrite_all(int fd, const char *data, size_t sz) {\n    monotonic_t start = glfwGetTime();\n    size_t pos = 0;\n    while (pos < sz && glfwGetTime() - start < s_to_monotonic_t(2ll)) {\n        ssize_t ret = write(fd, data + pos, sz - pos);\n        if (ret < 0) {\n            if (errno == EAGAIN || errno == EINTR) continue;\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                \"Wayland: Could not copy writing to destination fd failed with error: %s\", strerror(errno));\n            return false;\n        }\n        if (ret > 0) {\n            start = glfwGetTime();\n            pos += ret;\n        }\n    }\n    return pos >= sz;\n}\n\nstatic void\nsend_clipboard_data(const _GLFWClipboardData *cd, const char *mime, int fd) {\n    if (strcmp(mime, \"text/plain;charset=utf-8\") == 0 || strcmp(mime, \"UTF8_STRING\") == 0 || strcmp(mime, \"TEXT\") == 0 || strcmp(mime, \"STRING\") == 0) mime = \"text/plain\";\n    GLFWDataChunk chunk = cd->get_data(mime, NULL, cd->ctype);\n    void *iter = chunk.iter;\n    if (!iter) return;\n    bool keep_going = true;\n    while (keep_going) {\n        chunk = cd->get_data(mime, iter, cd->ctype);\n        if (!chunk.sz) break;\n        if (!write_all(fd, chunk.data, chunk.sz)) keep_going = false;\n        if (chunk.free) chunk.free((void*)chunk.free_data);\n    }\n    cd->get_data(NULL, iter, cd->ctype);\n}\n\nstatic void _glfwSendClipboardText(void *data UNUSED, struct wl_data_source *data_source UNUSED, const char *mime_type, int fd) {\n    send_clipboard_data(&_glfw.clipboard, mime_type, fd);\n    close(fd);\n}\n\nstatic void _glfwSendPrimarySelectionText(void *data UNUSED, struct zwp_primary_selection_source_v1 *primary_selection_source UNUSED,\n        const char *mime_type, int fd) {\n    send_clipboard_data(&_glfw.primary, mime_type, fd);\n    close(fd);\n}\n\nstatic void\nread_offer(int data_pipe, GLFWclipboardwritedatafun write_data, void *object) {\n    wl_display_flush(_glfw.wl.display);\n    struct pollfd fds;\n    fds.fd = data_pipe;\n    fds.events = POLLIN;\n    monotonic_t start = glfwGetTime();\n#define bail(...) { \\\n    _glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \\\n    close(data_pipe); \\\n    return; \\\n}\n\n    char buf[8192];\n\n    while (glfwGetTime() - start < s_to_monotonic_t(2ll)) {\n        int ret = poll(&fds, 1, 2000);\n        if (ret == -1) {\n            if (errno == EINTR) continue;\n            bail(\"Wayland: Failed to poll clipboard data from pipe with error: %s\", strerror(errno));\n        }\n        if (!ret) {\n            bail(\"Wayland: Failed to read clipboard data from pipe (timed out)\");\n        }\n        ret = read(data_pipe, buf, sizeof(buf));\n        if (ret == -1) {\n            if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;\n            bail(\"Wayland: Failed to read clipboard data from pipe with error: %s\", strerror(errno));\n        }\n        if (ret == 0) { close(data_pipe); return; }\n        if (!write_data(object, buf, ret)) bail(\"Wayland: call to write_data() failed with data from data offer\");\n        start = glfwGetTime();\n    }\n    bail(\"Wayland: Failed to read clipboard data from pipe (timed out)\");\n#undef bail\n}\n\n\nstatic void\nread_clipboard_data_offer(struct wl_data_offer *data_offer, const char *mime, GLFWclipboardwritedatafun write_data, void *object) {\n    int pipefd[2];\n    if (pipe2(pipefd, O_CLOEXEC) != 0) return;\n    wl_data_offer_receive(data_offer, mime, pipefd[1]);\n    close(pipefd[1]);\n    read_offer(pipefd[0], write_data, object);\n}\n\nstatic void\nread_primary_selection_offer(struct zwp_primary_selection_offer_v1 *primary_selection_offer, const char *mime, GLFWclipboardwritedatafun write_data, void *object) {\n    int pipefd[2];\n    if (pipe2(pipefd, O_CLOEXEC) != 0) return;\n    zwp_primary_selection_offer_v1_receive(primary_selection_offer, mime, pipefd[1]);\n    close(pipefd[1]);\n    read_offer(pipefd[0], write_data, object);\n}\n\nstatic void data_source_canceled(void *data UNUSED, struct wl_data_source *wl_data_source) {\n    if (_glfw.wl.dataSourceForClipboard == wl_data_source) {\n        _glfw.wl.dataSourceForClipboard = NULL;\n        _glfw_free_clipboard_data(&_glfw.clipboard);\n        _glfwInputClipboardLost(GLFW_CLIPBOARD);\n    }\n    wl_data_source_destroy(wl_data_source);\n}\n\nstatic void primary_selection_source_canceled(void *data UNUSED, struct zwp_primary_selection_source_v1 *primary_selection_source) {\n    if (_glfw.wl.dataSourceForPrimarySelection == primary_selection_source) {\n        _glfw.wl.dataSourceForPrimarySelection = NULL;\n        _glfw_free_clipboard_data(&_glfw.primary);\n        _glfwInputClipboardLost(GLFW_PRIMARY_SELECTION);\n    }\n    zwp_primary_selection_source_v1_destroy(primary_selection_source);\n}\n\n// KWin aborts if we don't define these even though they are not used for copy/paste\nstatic void dummy_data_source_target(void* data UNUSED, struct wl_data_source* wl_data_source UNUSED, const char* mime_type UNUSED) {\n}\n\nstatic void dummy_data_source_action(void* data UNUSED, struct wl_data_source* wl_data_source UNUSED, unsigned int dnd_action UNUSED) {\n}\n\nstatic const struct wl_data_source_listener data_source_listener = {\n    .send = _glfwSendClipboardText,\n    .cancelled = data_source_canceled,\n    .target = dummy_data_source_target,\n    .action = dummy_data_source_action,\n};\n\nstatic const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener = {\n    .send = _glfwSendPrimarySelectionText,\n    .cancelled = primary_selection_source_canceled,\n};\n\n// Getting data from clipboard and drops {{{\nstatic void\ndestroy_drop_data(_GLFWWaylandDataOffer *offer) {\n    for (size_t i = 0; i < offer->dd_count; i++) {\n        if (offer->requested_drop_data[i].mime) free(offer->requested_drop_data[i].mime);\n        if (offer->requested_drop_data[i].fd > -1) { safe_close(offer->requested_drop_data[i].fd); }\n        if (offer->requested_drop_data[i].watch_id) removeWatch(&_glfw.wl.eventLoopData, offer->requested_drop_data[i].watch_id);\n    }\n    free(offer->requested_drop_data);\n}\n\nvoid\ndestroy_data_offer(_GLFWWaylandDataOffer *offer) {\n    if (offer->id) {\n        if (offer->is_primary) zwp_primary_selection_offer_v1_destroy(offer->id);\n        else wl_data_offer_destroy(offer->id);\n    }\n    if (offer->mimes) {\n        for (size_t i = 0; i < offer->mimes_count; i++) free((char*)offer->mimes[i]);\n        free(offer->mimes);\n    }\n    free(offer->copy_mimes);  // pointer array only; strings are owned by mimes[]\n    if (offer->requested_drop_data) destroy_drop_data(offer);\n    memset(offer, 0, sizeof(offer[0]));\n}\n\n// Reset the working copy of mimes so the next callback sees the full original\n// list.  Returns false on allocation failure.\nstatic bool\nreset_copy_mimes(_GLFWWaylandDataOffer *offer) {\n    if (offer->mimes_count == 0) { offer->copy_mimes_count = 0; return true; }\n    if (!offer->copy_mimes) {\n        offer->copy_mimes = malloc(offer->mimes_count * sizeof(const char*));\n        if (!offer->copy_mimes) return false;\n    }\n    memcpy(offer->copy_mimes, offer->mimes, offer->mimes_count * sizeof(const char*));\n    offer->copy_mimes_count = offer->mimes_count;\n    return true;\n}\n\nstatic void\nmark_data_offer(_GLFWWaylandDataOffer *ans, void *id) {\n    if (ans->id) destroy_data_offer(ans);\n    for (size_t i = 0; i < arraysz(_glfw.wl.untyped_data_offers); i++) {\n        _GLFWWaylandDataOffer *offer = _glfw.wl.untyped_data_offers + i;\n        if (offer->id == id) {\n            *ans = *offer;\n            memset(offer, 0, sizeof(offer[0]));\n            break;\n        }\n    }\n}\n\nstatic void\nmark_selection_offer(void *data UNUSED, struct wl_data_device *data_device UNUSED, struct wl_data_offer *data_offer) {\n    mark_data_offer(&_glfw.wl.clipboard_data_offer, data_offer);\n}\n\nstatic void\nmark_primary_selection_offer(void *data UNUSED, struct zwp_primary_selection_device_v1* primary_selection_device UNUSED,\n        struct zwp_primary_selection_offer_v1 *primary_selection_offer) {\n    mark_data_offer(&_glfw.wl.primary_data_offer, primary_selection_offer);\n}\n\nstatic void\nadd_offer_mimetype(_GLFWWaylandDataOffer* offer, const char* mime, bool is_self_offer) {\n    if (is_self_offer) offer->is_self_offer = is_self_offer;\n    if (strcmp(mime, clipboard_mime()) == 0) {\n        offer->is_self_offer = true;\n    }\n    if (!offer->mimes || offer->mimes_count + 1 >= offer->mimes_capacity) {\n        offer->mimes = realloc(offer->mimes, sizeof(char*) * (offer->mimes_capacity + 64));\n        if (offer->mimes) offer->mimes_capacity += 64;\n        else return;\n    }\n    offer->mimes[offer->mimes_count++] = _glfw_strdup(mime);\n}\n\nstatic _GLFWWaylandDataOffer*\ndata_offer_for_id(void *id) {\n    for (size_t i = 0; i < arraysz(_glfw.wl.untyped_data_offers); i++) {\n        _GLFWWaylandDataOffer *offer = _glfw.wl.untyped_data_offers + i;\n        if (offer->id == id) return offer;\n    }\n    if (_glfw.wl.clipboard_data_offer.id == id) return &_glfw.wl.clipboard_data_offer;\n    if (_glfw.wl.primary_data_offer.id == id) return &_glfw.wl.primary_data_offer;\n    if (_glfw.wl.drop_data_offer.id == id) return &_glfw.wl.drop_data_offer;\n    return NULL;\n}\n\nstatic void\nadd_generic_offer_mimetype(void *id, const char *mime, bool is_self_offer) {\n    _GLFWWaylandDataOffer *offer = data_offer_for_id(id);\n    if (offer) add_offer_mimetype(offer, mime, is_self_offer);\n}\n\nstatic void handle_offer_mimetype(void *data UNUSED, struct wl_data_offer* id, const char *mime) {\n    add_generic_offer_mimetype(id, mime, strcmp(mime, clipboard_mime()) == 0);\n}\n\nstatic void handle_primary_selection_offer_mimetype(void *data UNUSED, struct zwp_primary_selection_offer_v1* id, const char *mime) {\n    add_generic_offer_mimetype(id, mime, strcmp(mime, clipboard_mime()) == 0);\n}\n\nstatic void data_offer_source_actions(void *data UNUSED, struct wl_data_offer* id, uint32_t actions) {\n    _GLFWWaylandDataOffer *offer = data_offer_for_id(id);\n    if (offer) offer->source_actions = actions;\n}\n\nstatic void data_offer_action(void *data UNUSED, struct wl_data_offer* id, uint32_t action) {\n    _GLFWWaylandDataOffer *offer = data_offer_for_id(id);\n    if (offer) offer->dnd_action = action;\n}\n\nstatic const struct wl_data_offer_listener data_offer_listener = {\n    .offer = handle_offer_mimetype,\n    .source_actions = data_offer_source_actions,\n    .action = data_offer_action,\n};\n\nstatic const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = {\n    .offer = handle_primary_selection_offer_mimetype,\n};\n\nstatic void\nhandle_data_offer_generic(void *id, bool is_primary) {\n    for (size_t i = 0; i < arraysz(_glfw.wl.untyped_data_offers); i++) {\n        _GLFWWaylandDataOffer *offer = _glfw.wl.untyped_data_offers + i;\n        if (offer->id == NULL) {\n            offer->id = id;\n            offer->is_primary = is_primary;\n            return;\n        }\n    }\n    if (is_primary) zwp_primary_selection_offer_v1_destroy(id);\n    else wl_data_offer_destroy(id);\n    _glfwInputError(GLFW_PLATFORM_ERROR,\n                    \"Wayland: too many untyped data offers\");\n}\n\nstatic void handle_data_offer(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, struct wl_data_offer *id) {\n    handle_data_offer_generic(id, false);\n    wl_data_offer_add_listener(id, &data_offer_listener, NULL);\n}\n\nstatic void handle_primary_selection_offer(void *data UNUSED, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1 UNUSED, struct zwp_primary_selection_offer_v1 *id) {\n    handle_data_offer_generic(id, true);\n    zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, NULL);\n}\n\n// Helper function to update drop state from callback results\nstatic void\nupdate_drop_state(_GLFWWaylandDataOffer *d, _GLFWwindow* window UNUSED, size_t accepted_count) {\n    d->copy_mimes_count = accepted_count;\n    bool accepted = accepted_count > 0;\n    bool acceptance_changed = (accepted != d->drag_accepted);\n    // The first entry in the accepted (sorted) copy is the preferred MIME.\n    const char* new_preferred_mime = (accepted && d->copy_mimes) ? d->copy_mimes[0] : NULL;\n    bool mime_changed = false;\n\n    // Check if the preferred MIME changed\n    if (d->mime_for_drop == NULL && new_preferred_mime != NULL) {\n        mime_changed = true;\n    } else if (d->mime_for_drop != NULL && new_preferred_mime == NULL) {\n        mime_changed = true;\n    } else if (d->mime_for_drop != NULL && new_preferred_mime != NULL) {\n        mime_changed = (strcmp(d->mime_for_drop, new_preferred_mime) != 0);\n    }\n\n    if (acceptance_changed || mime_changed) {\n        d->drag_accepted = accepted;\n        d->mime_for_drop = new_preferred_mime;\n        wl_data_offer_accept(d->id, d->serial, d->mime_for_drop);\n    }\n}\n\nstatic void\ndrag_enter(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) {\n    _GLFWWaylandDataOffer *offer = &_glfw.wl.drop_data_offer;\n    mark_data_offer(offer, id);\n    if (!offer->id) return;\n    offer->is_self_offer = (_glfw.drag.window_id != 0);\n    offer->surface = surface;\n    offer->serial = serial;\n    offer->drag_accepted = false;\n    offer->mime_for_drop = NULL;\n    _GLFWwindow* window = _glfw.windowListHead;\n    while (window) {\n        if (window->wl.surface == surface) {\n            double xpos = wl_fixed_to_double(x);\n            double ypos = wl_fixed_to_double(y);\n            if (reset_copy_mimes(offer)) {\n                size_t mime_count = _glfwInputDropEvent(\n                        window, GLFW_DROP_ENTER, xpos, ypos,\n                        offer->copy_mimes, offer->copy_mimes_count, offer->is_self_offer);\n                update_drop_state(offer, window, mime_count);\n            }\n            break;\n        }\n        window = window->next;\n    }\n}\n\nstatic void\ndrag_leave(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED) {\n    _GLFWWaylandDataOffer *offer = &_glfw.wl.drop_data_offer;\n    if (offer->id) {\n        _GLFWwindow* window = _glfw.windowListHead;\n        while (window) {\n            if (window->wl.surface == _glfw.wl.drop_data_offer.surface) {\n                _glfwInputDropEvent(window, GLFW_DROP_LEAVE, 0, 0, NULL, 0, offer->is_self_offer);\n                break;\n            }\n            window = window->next;\n        }\n        if (!offer->dropped) destroy_data_offer(offer);\n    }\n}\n\nvoid\n_glfwPlatformEndDrop(GLFWwindow *w UNUSED, GLFWDragOperationType op UNUSED) {\n    _GLFWWaylandDataOffer *offer = &_glfw.wl.drop_data_offer;\n    if (offer->id) destroy_data_offer(offer);\n}\n\nssize_t\n_glfwPlatformReadAvailableDropData(GLFWwindow *w, GLFWDropEvent *ev, char *buffer, size_t sz) {\n    _GLFWwindow *window = (_GLFWwindow*)w;\n    _GLFWWaylandDataOffer *offer = &_glfw.wl.drop_data_offer;\n    if (!offer->id || offer->surface != window->wl.surface) return -ENOENT;\n    int fd = (int)ev->xpos;\n    for (size_t o = 0; o < offer->dd_count; o++) {\n        if (offer->requested_drop_data[o].fd == fd) {\n            ssize_t ret;\n            do { ret = read(fd, buffer, sz); } while (ret < 0 && (errno == EINTR || errno == EAGAIN));\n            if (ret <= 0) removeWatch(&_glfw.wl.eventLoopData, offer->requested_drop_data[o].watch_id);\n            return ret < 0 ? -errno : ret;\n        }\n    }\n    return -ENOENT;\n}\n\nstatic void\ndrop_data_available(int fd, int events UNUSED, void *data UNUSED) {\n    _GLFWWaylandDataOffer *offer = &_glfw.wl.drop_data_offer;\n    if (!offer->id) return;\n    for (size_t o = 0; o < offer->dd_count; o++) {\n        if (offer->requested_drop_data[o].fd == fd) {\n            _GLFWwindow* window = _glfw.windowListHead;\n            while (window) {\n                if (window->wl.surface == offer->surface) {\n                    const char *mimes[1] = {offer->requested_drop_data[o].mime};\n                    _glfwInputDropEvent(window, GLFW_DROP_DATA_AVAILABLE, fd, 0, mimes, 1, offer->is_self_offer);\n                    return;\n                }\n                window = window->next;\n            }\n            destroy_data_offer(offer);\n        }\n    }\n}\n\nstatic int\nrequest_drop_data(_GLFWWaylandDataOffer *offer, const char *mime) {\n    int pipefd[2];\n    if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) != 0) return errno;\n    wl_data_offer_receive(offer->id, mime, pipefd[1]);\n    safe_close(pipefd[1]);\n    // Flush to ensure the compositor processes the receive request\n    wl_display_flush(_glfw.wl.display);\n    id_type watch_id = addWatch(&_glfw.wl.eventLoopData, \"drop_data\", pipefd[0], POLLIN | POLLERR | POLLHUP, true, drop_data_available, NULL);\n    if (!watch_id) {\n        safe_close(pipefd[0]);\n        return ERANGE;\n    }\n    char *mt = _glfw_strdup(mime);\n    if (!mt) {\n        safe_close(pipefd[0]);\n        removeWatch(&_glfw.wl.eventLoopData, watch_id);\n        return ENOMEM;\n    }\n    if (!offer->requested_drop_data || offer->dd_count + 1 >= offer->dd_capacity) {\n        void *p = realloc(offer->requested_drop_data, sizeof(offer->requested_drop_data[0]) * (offer->dd_capacity + 8));\n        if (!p) {\n            safe_close(pipefd[0]);\n            removeWatch(&_glfw.wl.eventLoopData, watch_id);\n            free(mt);\n            return ENOMEM;\n        }\n        offer->requested_drop_data = p;\n        offer->dd_capacity += 64;\n    }\n    offer->requested_drop_data[offer->dd_count].mime = mt;\n    offer->requested_drop_data[offer->dd_count].watch_id = watch_id;\n    offer->requested_drop_data[offer->dd_count].fd = pipefd[0];\n    offer->dd_count++;\n    return 0;\n}\n\nint\n_glfwPlatformRequestDropData(_GLFWwindow *window UNUSED, const char *mime) {\n    _GLFWWaylandDataOffer *offer = &_glfw.wl.drop_data_offer;\n    if (offer->id) return request_drop_data(offer, mime);\n    return EINVAL;\n}\n\nstatic void\ndrop(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED) {\n    _GLFWWaylandDataOffer *offer = &_glfw.wl.drop_data_offer;\n    if (!offer->id) return;\n    offer->dropped = true;\n    _GLFWwindow* window = _glfw.windowListHead;\n    while (window) {\n        if (window->wl.surface == offer->surface) {\n            if (reset_copy_mimes(offer)) {\n                size_t num_accepted = _glfwInputDropEvent(window, GLFW_DROP_DROP, 0, 0, offer->copy_mimes, offer->copy_mimes_count, offer->is_self_offer);\n                if (!offer->copy_mimes) { destroy_data_offer(offer); return; }\n                for (size_t i = 0; i < num_accepted; i++) request_drop_data(offer, offer->copy_mimes[i]);\n            }\n            break;\n        }\n        window = window->next;\n    }\n}\n\nstatic void\nmotion(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint32_t time UNUSED, wl_fixed_t x, wl_fixed_t y) {\n    _GLFWWaylandDataOffer *offer = &_glfw.wl.drop_data_offer;\n    if (!offer->id) return;\n    _GLFWwindow* window = _glfw.windowListHead;\n    while (window) {\n        if (window->wl.surface == offer->surface) {\n            double xpos = wl_fixed_to_double(x);\n            double ypos = wl_fixed_to_double(y);\n            if (reset_copy_mimes(offer)) {\n                size_t mime_count = _glfwInputDropEvent(\n                    window, GLFW_DROP_MOVE, xpos, ypos, offer->copy_mimes, offer->copy_mimes_count, offer->is_self_offer);\n                update_drop_state(offer, window, mime_count);\n            }\n            break;\n        }\n        window = window->next;\n    }\n}\n\nvoid\n_glfwPlatformRequestDropUpdate(_GLFWwindow* window) {\n    _GLFWWaylandDataOffer *d = &_glfw.wl.drop_data_offer;\n    if (d->id && reset_copy_mimes(d)) {\n        size_t mime_count = _glfwInputDropEvent(window, GLFW_DROP_STATUS_UPDATE, 0, 0, d->copy_mimes, d->copy_mimes_count, d->is_self_offer);\n        update_drop_state(d, window, mime_count);\n    }\n}\n\nstatic const struct wl_data_device_listener data_device_listener = {\n    .data_offer = handle_data_offer,\n    .selection = mark_selection_offer,\n    .enter = drag_enter,\n    .motion = motion,\n    .drop = drop,\n    .leave = drag_leave,\n};\n\nstatic const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = {\n    .data_offer = handle_primary_selection_offer,\n    .selection = mark_primary_selection_offer,\n};\n// }}}\n\nvoid _glfwSetupWaylandDataDevice(void) {\n    _glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat);\n    if (_glfw.wl.dataDevice) wl_data_device_add_listener(_glfw.wl.dataDevice, &data_device_listener, NULL);\n}\n\nvoid _glfwSetupWaylandPrimarySelectionDevice(void) {\n    _glfw.wl.primarySelectionDevice = zwp_primary_selection_device_manager_v1_get_device(_glfw.wl.primarySelectionDeviceManager, _glfw.wl.seat);\n    if (_glfw.wl.primarySelectionDevice) zwp_primary_selection_device_v1_add_listener(_glfw.wl.primarySelectionDevice, &primary_selection_device_listener, NULL);\n}\n\nstatic bool _glfwEnsureDataDevice(void) {\n    if (!_glfw.wl.dataDeviceManager)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Cannot use clipboard, data device manager is not ready\");\n        return false;\n    }\n\n    if (!_glfw.wl.dataDevice)\n    {\n        if (!_glfw.wl.seat)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"Wayland: Cannot use clipboard, seat is not ready\");\n            return false;\n        }\n        if (!_glfw.wl.dataDevice)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"Wayland: Cannot use clipboard, failed to create data device\");\n            return false;\n        }\n    }\n    return true;\n}\n\ntypedef void(*add_offer_func)(void*, const char *mime);\n\n\nvoid\n_glfwPlatformSetClipboard(GLFWClipboardType t) {\n    _GLFWClipboardData *cd = NULL;\n    void *data_source;\n    add_offer_func f;\n    if (t == GLFW_CLIPBOARD) {\n        if (!_glfwEnsureDataDevice()) return;\n        cd = &_glfw.clipboard;\n        f = (add_offer_func)wl_data_source_offer;\n        if (_glfw.wl.dataSourceForClipboard) wl_data_source_destroy(_glfw.wl.dataSourceForClipboard);\n        _glfw.wl.dataSourceForClipboard = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);\n        if (!_glfw.wl.dataSourceForClipboard)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"Wayland: Cannot copy failed to create data source\");\n            return;\n        }\n        wl_data_source_add_listener(_glfw.wl.dataSourceForClipboard, &data_source_listener, NULL);\n        data_source = _glfw.wl.dataSourceForClipboard;\n    } else {\n        if (!_glfw.wl.primarySelectionDevice) {\n            static bool warned_about_primary_selection_device = false;\n            if (!warned_about_primary_selection_device) {\n                _glfwInputError(GLFW_PLATFORM_ERROR,\n                                \"Wayland: Cannot copy no primary selection device available\");\n                warned_about_primary_selection_device = true;\n            }\n            return;\n        }\n        cd = &_glfw.primary;\n        f = (add_offer_func)zwp_primary_selection_source_v1_offer;\n        if (_glfw.wl.dataSourceForPrimarySelection) zwp_primary_selection_source_v1_destroy(_glfw.wl.dataSourceForPrimarySelection);\n        _glfw.wl.dataSourceForPrimarySelection = zwp_primary_selection_device_manager_v1_create_source(_glfw.wl.primarySelectionDeviceManager);\n        if (!_glfw.wl.dataSourceForPrimarySelection)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"Wayland: Cannot copy failed to create primary selection source\");\n            return;\n        }\n        zwp_primary_selection_source_v1_add_listener(_glfw.wl.dataSourceForPrimarySelection, &primary_selection_source_listener, NULL);\n        data_source = _glfw.wl.dataSourceForPrimarySelection;\n    }\n    f(data_source, clipboard_mime());\n    for (size_t i = 0; i < cd->num_mime_types; i++) {\n        if (strcmp(cd->mime_types[i], \"text/plain\") == 0) {\n            f(data_source, \"TEXT\");\n            f(data_source, \"STRING\");\n            f(data_source, \"UTF8_STRING\");\n            f(data_source, \"text/plain;charset=utf-8\");\n        }\n        f(data_source, cd->mime_types[i]);\n    }\n    if (t == GLFW_CLIPBOARD) {\n        // According to some interpretations of the Wayland spec only the application that has keyboard focus can set the clipboard.\n        // Hurray for the Wayland nanny state!\n        //\n        // However in wl-roots based compositors, using the serial from the keyboard enter event doesn't work. No clue what\n        // the correct serial to use here is. Given this Wayland there probably isn't one. What a joke.\n        // Bug report: https://github.com/kovidgoyal/kitty/issues/6890\n        // Ironically one of the contributors to wl_roots claims the keyboard enter serial is the correct one to use:\n        // https://emersion.fr/blog/2020/wayland-clipboard-drag-and-drop/\n        // The Wayland spec itself says \"serial number of the event that triggered this request\"\n        // https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_data_device\n        // So who the fuck knows. Just use the latest received serial and ask anybody that uses Wayland\n        // to get their head examined.\n        wl_data_device_set_selection(_glfw.wl.dataDevice, _glfw.wl.dataSourceForClipboard, _glfw.wl.serial);\n    } else {\n        // According to the Wayland spec we can only set the primary selection in response to a pointer button event\n        // Hurray for the Wayland nanny state!\n        zwp_primary_selection_device_v1_set_selection(\n                _glfw.wl.primarySelectionDevice, _glfw.wl.dataSourceForPrimarySelection, _glfw.wl.pointer_serial);\n    }\n}\n\nstatic bool\noffer_has_mime(const _GLFWWaylandDataOffer *d, const char *mime) {\n    for (unsigned i = 0; i < d->mimes_count; i++) {\n        if (strcmp(d->mimes[i], mime) == 0) return true;\n    }\n    return false;\n}\n\nstatic const char*\nplain_text_mime_for_offer(const _GLFWWaylandDataOffer *d) {\n#define A(x) if (offer_has_mime(d, x)) return x;\n    A(\"text/plain;charset=utf-8\");\n    A(\"text/plain\");\n    A(\"UTF8_STRING\");\n    A(\"STRING\");\n    A(\"TEXT\");\n#undef A\n    return NULL;\n}\n\nvoid\n_glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {\n    _GLFWWaylandDataOffer *d = clipboard_type == GLFW_PRIMARY_SELECTION ? &_glfw.wl.primary_data_offer : &_glfw.wl.clipboard_data_offer;\n    if (!d->id) return;\n    if (d->is_self_offer) {\n        write_data(object, NULL, 1);\n        return;\n    }\n    if (mime_type == NULL) {\n        bool ok = true;\n        for (size_t o = 0; o < d->mimes_count; o++) {\n            const char *q = d->mimes[o];\n            if (strchr(d->mimes[0], '/')) {\n                if (strcmp(q, clipboard_mime()) == 0) continue;\n                if (strcmp(q, \"text/plain;charset=utf-8\") == 0) q = \"text/plain\";\n            } else {\n                if (strcmp(q, \"UTF8_STRING\") == 0 || strcmp(q, \"STRING\") == 0 || strcmp(q, \"TEXT\") == 0) q = \"text/plain\";\n            }\n            if (ok) ok = write_data(object, q, strlen(q));\n        }\n        return;\n    }\n    if (strcmp(mime_type, \"text/plain\") == 0) {\n        mime_type = plain_text_mime_for_offer(d);\n        if (!mime_type) return;\n    }\n    if (d->is_primary) {\n        read_primary_selection_offer(d->id, mime_type, write_data, object);\n    } else {\n        read_clipboard_data_offer(d->id, mime_type, write_data, object);\n    }\n}\n\nEGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs UNUSED)\n{\n    if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland)\n        return EGL_PLATFORM_WAYLAND_EXT;\n    else\n        return 0;\n}\n\nEGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)\n{\n    return _glfw.wl.display;\n}\n\nEGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)\n{\n    return window->wl.native;\n}\n\nvoid _glfwPlatformGetRequiredInstanceExtensions(char** extensions)\n{\n    if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)\n        return;\n\n    extensions[0] = \"VK_KHR_surface\";\n    extensions[1] = \"VK_KHR_wayland_surface\";\n}\n\nint _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,\n                                                      VkPhysicalDevice device,\n                                                      uint32_t queuefamily)\n{\n    PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR\n        vkGetPhysicalDeviceWaylandPresentationSupportKHR =\n        (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)\n        vkGetInstanceProcAddr(instance, \"vkGetPhysicalDeviceWaylandPresentationSupportKHR\");\n    if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"Wayland: Vulkan instance missing VK_KHR_wayland_surface extension\");\n        return VK_NULL_HANDLE;\n    }\n\n    return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,\n                                                            queuefamily,\n                                                            _glfw.wl.display);\n}\n\nVkResult _glfwPlatformCreateWindowSurface(VkInstance instance,\n                                          _GLFWwindow* window,\n                                          const VkAllocationCallbacks* allocator,\n                                          VkSurfaceKHR* surface)\n{\n    VkResult err;\n    VkWaylandSurfaceCreateInfoKHR sci;\n    PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;\n\n    vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)\n        vkGetInstanceProcAddr(instance, \"vkCreateWaylandSurfaceKHR\");\n    if (!vkCreateWaylandSurfaceKHR)\n    {\n        _glfwInputError(GLFW_API_UNAVAILABLE,\n                        \"Wayland: Vulkan instance missing VK_KHR_wayland_surface extension\");\n        return VK_ERROR_EXTENSION_NOT_PRESENT;\n    }\n\n    memset(&sci, 0, sizeof(sci));\n    sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;\n    sci.display = _glfw.wl.display;\n    sci.surface = window->wl.surface;\n\n    err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);\n    if (err)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Wayland: Failed to create Vulkan surface: %s\",\n                        _glfwGetVulkanResultString(err));\n    }\n\n    return err;\n}\n\nstatic void\nframe_handle_redraw(void *data, struct wl_callback *callback, uint32_t time UNUSED) {\n    _GLFWwindow* window = (_GLFWwindow*) data;\n    if (callback == window->wl.frameCallbackData.current_wl_callback) {\n        window->wl.frameCallbackData.callback(window->wl.frameCallbackData.id);\n        window->wl.frameCallbackData.current_wl_callback = NULL;\n    }\n    wl_callback_destroy(callback);\n}\n\nvoid\n_glfwPlatformChangeCursorTheme(void) {\n    glfw_wlc_destroy();\n    _GLFWwindow *w = _glfw.windowListHead;\n    while (w) {\n        setCursorImage(w, true);\n        w = w->next;\n    }\n\n}\n\nint\n_glfwPlatformSetWindowBlur(_GLFWwindow *window, int blur_radius) {\n    if (!window->wl.transparent) return 0;\n    bool has_blur = window->wl.has_blur;\n    bool new_has_blur = blur_radius > 0;\n    if (new_has_blur != has_blur) {\n        window->wl.has_blur = new_has_blur;\n        update_regions(window);\n    }\n    return has_blur ? 1 : 0;\n}\n\nbool\n_glfwPlatformGrabKeyboard(bool grab) {\n    if (!_glfw.wl.keyboard_shortcuts_inhibit_manager) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"The Wayland compositor does not implement inhibit-keyboard-shortcuts, cannot grab keyboard\");\n        return false;\n    }\n    for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) inhibit_shortcuts_for(window, grab);\n    return true;\n}\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI struct wl_display* glfwGetWaylandDisplay(void)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return _glfw.wl.display;\n}\n\nGLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return window->wl.surface;\n}\n\nGLFWAPI void glfwWaylandActivateWindow(GLFWwindow* handle, const char *activation_token) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    _GLFW_REQUIRE_INIT();\n    if (activation_token && activation_token[0] && _glfw.wl.xdg_activation_v1) xdg_activation_v1_activate(_glfw.wl.xdg_activation_v1, activation_token, window->wl.surface);\n}\n\nGLFWAPI void glfwWaylandRunWithActivationToken(GLFWwindow *handle, GLFWactivationcallback cb, void *cb_data) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    _GLFW_REQUIRE_INIT();\n    get_activation_token(window, _glfw.wl.input_serial, cb, cb_data);\n}\n\nGLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {\n    return glfw_xkb_keysym_from_name(keyName, caseSensitive);\n}\n\nGLFWAPI void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, void(*callback)(unsigned long long id)) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    static const struct wl_callback_listener frame_listener = { .done = frame_handle_redraw };\n    if (window->wl.frameCallbackData.current_wl_callback) wl_callback_destroy(window->wl.frameCallbackData.current_wl_callback);\n    if (window->wl.waiting_for_swap_to_commit) {\n        callback(id);\n        window->wl.frameCallbackData.id = 0;\n        window->wl.frameCallbackData.callback = NULL;\n        window->wl.frameCallbackData.current_wl_callback = NULL;\n    } else {\n        window->wl.frameCallbackData.id = id;\n        window->wl.frameCallbackData.callback = callback;\n        window->wl.frameCallbackData.current_wl_callback = wl_surface_frame(window->wl.surface);\n        if (window->wl.frameCallbackData.current_wl_callback) {\n            wl_callback_add_listener(window->wl.frameCallbackData.current_wl_callback, &frame_listener, window);\n            commit_window_surface_if_safe(window);\n        }\n    }\n}\n\nGLFWAPI unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data) {\n    return glfw_dbus_send_user_notification(n, callback, data);\n}\n\nGLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {\n    glfw_dbus_set_user_notification_activated_handler(handler);\n}\n\nGLFWAPI bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    if (!window->wl.decorations.serverSide) {\n        csd_set_titlebar_color(window, color, use_system_color);\n        return true;\n    }\n    return false;\n}\n\nGLFWAPI void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    if (csd_change_title(window)) commit_window_surface_if_safe(window);\n}\n\nGLFWAPI void glfwWaylandSetTitlebarHidden(GLFWwindow *handle, bool hidden) {\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    if (window->wl.decorations.titlebar_hidden != hidden) {\n        window->wl.decorations.titlebar_hidden = hidden;\n        setXdgDecorations(window);\n        inform_compositor_of_window_geometry(window, \"SetTitlebarHidden\");\n        commit_window_surface_if_safe(window);\n    }\n}\n\nconst GLFWLayerShellConfig*\n_glfwPlatformGetLayerShellConfig(_GLFWwindow *window) {\n    return &window->wl.layer_shell.config;\n}\n\nGLFWAPI bool glfwIsLayerShellSupported(void) { return _glfw.wl.zwlr_layer_shell_v1 != NULL; }\n\nGLFWAPI bool glfwWaylandIsWindowFullyCreated(GLFWwindow *handle) { return handle != NULL && ((_GLFWwindow*)handle)->wl.window_fully_created; }\n\nvoid\n_glfwPlatformInputColorScheme(GLFWColorScheme appearance UNUSED) {\n    _GLFWwindow* window = _glfw.windowListHead;\n    while (window) {\n        glfwWaylandRedrawCSDWindowTitle((GLFWwindow*)window);\n        window = window->next;\n    }\n}\n\nGLFWAPI bool glfwWaylandBeep(GLFWwindow *handle) {\n    if (!_glfw.wl.xdg_system_bell_v1) return false;\n    _GLFWwindow *window = (_GLFWwindow*)handle;\n    xdg_system_bell_v1_ring(_glfw.wl.xdg_system_bell_v1, window ? window->wl.surface : NULL);\n    return true;\n}\n\n// Drag source {{{\n\nstatic void\ndrag_toplevel_xdg_surface_configure(void *data UNUSED, struct xdg_surface *surface, uint32_t serial) {\n    xdg_surface_ack_configure(surface, serial);\n    if (_glfw.wl.drag.toplevel_buffer) {\n        wl_surface_attach(_glfw.wl.drag.drag_icon, _glfw.wl.drag.toplevel_buffer, 0, 0);\n        wl_surface_damage(_glfw.wl.drag.drag_icon, 0, 0, INT32_MAX, INT32_MAX);\n        wl_buffer_destroy(_glfw.wl.drag.toplevel_buffer);\n        _glfw.wl.drag.toplevel_buffer = NULL;\n    }\n    if (_glfw.wl.drag.drag_icon) wl_surface_commit(_glfw.wl.drag.drag_icon);\n}\n\nstatic const struct xdg_surface_listener drag_toplevel_xdg_surface_listener = {\n    .configure = drag_toplevel_xdg_surface_configure,\n};\n\nstatic void drag_toplevel_configure(void *data UNUSED, struct xdg_toplevel *toplevel UNUSED,\n                                    int32_t width UNUSED, int32_t height UNUSED,\n                                    struct wl_array *states UNUSED) {}\nstatic void drag_toplevel_close(void *data UNUSED, struct xdg_toplevel *toplevel UNUSED) {}\n#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION\nstatic void drag_toplevel_configure_bounds(void *data UNUSED, struct xdg_toplevel *toplevel UNUSED,\n                                           int32_t width UNUSED, int32_t height UNUSED) {}\nstatic void drag_toplevel_wm_capabilities(void *data UNUSED, struct xdg_toplevel *toplevel UNUSED,\n                                          struct wl_array *caps UNUSED) {}\n#endif\nstatic const struct xdg_toplevel_listener drag_toplevel_listener = {\n    .configure = drag_toplevel_configure,\n    .close = drag_toplevel_close,\n#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION\n    .configure_bounds = drag_toplevel_configure_bounds,\n    .wm_capabilities = drag_toplevel_wm_capabilities,\n#endif\n};\n\nstatic void\ncancel_drag(GLFWDragEventType type) {\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (window) {\n        GLFWDragEvent ev = {.type=type};\n        _glfwInputDragSourceRequest(window, &ev);\n    }\n    _glfwFreeDragSourceData();\n}\n\n#define dr _glfw.wl.drag.data_requests[i]\n\nstatic void\nfinish_drag_write(size_t i) {\n    if (dr.watch_id) removeWatch(&_glfw.wl.eventLoopData, dr.watch_id);\n    dr.watch_id = 0;\n    if (dr.fd > -1) safe_close(dr.fd);\n    dr.fd = -1;\n    free(dr.pending_data); dr.pending_data = NULL; dr.sz = 0; dr.offset = 0;\n    free((void*)dr.mime_type); dr.mime_type = NULL;\n}\n\nstatic ssize_t\nwrite_as_much_as_possible(int fd, const char *data, size_t sz) {\n    size_t ans = 0;\n    while (ans < sz) {\n        ssize_t ret = write(fd, data + ans, sz - ans);\n        if (ret < 0) {\n            if (errno == EINTR) continue;\n            if (errno == EAGAIN) return ans;\n            return ret;\n        }\n        ans += ret;\n    }\n    return ans;\n}\n\nstatic void\nsend_drag_data(_GLFWwindow *window, size_t i) {\n    ssize_t ret;\n    // Find the item matching the mime type for this data request.\n    // We cannot use i directly to index _glfw.drag.items because the compositor\n    // may call drag_source_send multiple times (once per target entered), making\n    // data_requests grow independently of the items array.\n    size_t item_idx = _glfw.drag.item_count;  // sentinel: not found\n    for (size_t j = 0; j < _glfw.drag.item_count; j++) {\n        if (dr.mime_type && _glfw.drag.items[j].mime_type &&\n                strcmp(_glfw.drag.items[j].mime_type, dr.mime_type) == 0) {\n            item_idx = j; break;\n        }\n    }\n    if (item_idx == _glfw.drag.item_count) {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n            \"Wayland: compositor requested data for unrecognised MIME type: %s\",\n            dr.mime_type ? dr.mime_type : \"(null)\");\n    }\n    bool has_preset_data = item_idx < _glfw.drag.item_count && _glfw.drag.items[item_idx].data_size > 0;\n    // On write error, only close this pipe; do NOT destroy the wl_data_source\n    // since the compositor must send cancelled/dnd_finished before that is safe.\n#define on_fail _glfwInputError(\\\n        GLFW_PLATFORM_ERROR, \"Wayland: failed to write drag source data to pipe with error: %s\", strerror(errno)); \\\n        finish_drag_write(i); return;\n\n    if (dr.sz > dr.offset) {\n        ret = write_as_much_as_possible(dr.fd, dr.pending_data + dr.offset, dr.sz - dr.offset);\n        if (ret < 0) { on_fail; } else {\n            dr.offset += ret;\n            if (dr.offset >= dr.sz) {\n                free(dr.pending_data); dr.pending_data = NULL; dr.sz = 0; dr.offset = 0;\n                finish_drag_write(i);\n            }\n        }\n    } else if (has_preset_data) {\n        do { ret = write(dr.fd, _glfw.drag.items[item_idx].optional_data, _glfw.drag.items[item_idx].data_size); } while (ret < 0 && errno == EINTR);\n        if (ret < 0) {\n            on_fail;\n        } else {\n            if ((size_t)ret >= _glfw.drag.items[item_idx].data_size) {\n                finish_drag_write(i);\n            } else {\n                void *pending = malloc(_glfw.drag.items[item_idx].data_size - ret);\n                if (!pending) { errno = ENOMEM; on_fail; } else {\n                    dr.pending_data = pending; dr.sz = _glfw.drag.items[item_idx].data_size - ret; dr.offset = 0;\n                }\n            }\n        }\n    } else {\n        GLFWDragEvent ev = {.type=GLFW_DRAG_DATA_REQUEST, .mime_type=dr.mime_type};\n        _glfwInputDragSourceRequest(window, &ev);\n        if (ev.err_num) {\n            if (ev.err_num == EAGAIN) { removeWatch(&_glfw.wl.eventLoopData, dr.watch_id); dr.watch_id = 0; }\n            else { finish_drag_write(i); return; }\n        } else {\n            if (ev.data_sz) {\n                ret = write_as_much_as_possible(dr.fd, ev.data, ev.data_sz);\n                if (ret >= 0 && (size_t)ret < ev.data_sz) {\n                    void *pending = malloc(ev.data_sz - ret);\n                    if (!pending) { ret = -1; } else {\n                        dr.pending_data = pending; dr.sz = ev.data_sz - ret; dr.offset = 0;\n                        memcpy(pending, ev.data + ret, dr.sz);\n                    }\n                }\n                _glfwInputDragSourceRequest(window, &ev);\n                if (ret < 0) { on_fail; }\n                else if ((size_t)ret >= ev.data_sz) { finish_drag_write(i); }\n            } else finish_drag_write(i);\n        }\n    }\n#undef on_fail\n}\n\nstatic void\nready_for_drag_data(int fd, int events UNUSED, void *data UNUSED) {\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (!window) { _glfwFreeDragSourceData(); return; }\n    for (size_t i = 0; i < _glfw.wl.drag.count; i++) {\n        if (dr.fd == fd) {\n            send_drag_data(window, i);\n            break;\n        }\n    }\n}\n\nstatic GLFWid\nadd_drag_watch(int fd) {\n    return addWatch(\n        &_glfw.wl.eventLoopData, \"drag_source\", fd, POLLOUT | POLLERR | POLLHUP, true, ready_for_drag_data, NULL);\n}\n\nint\n_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {\n    if (!thumbnail || !thumbnail->pixels) return 0;\n    struct wl_buffer* icon_buffer = createShmBuffer(thumbnail, false, true);\n    if (!icon_buffer) return ENOMEM;\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (_glfw.wl.drag.drag_viewport) {\n        double f_scale = window ? _glfwWaylandWindowScale(window) : 1.0;\n        int logical_width = (int)(thumbnail->width / f_scale);\n        int logical_height = (int)(thumbnail->height / f_scale);\n        wp_viewport_set_destination(_glfw.wl.drag.drag_viewport, logical_width, logical_height);\n    } else {\n        int scale = window ? _glfwWaylandIntegerWindowScale(window) : 1;\n        wl_surface_set_buffer_scale(_glfw.wl.drag.drag_icon, scale);\n    }\n    wl_surface_attach(_glfw.wl.drag.drag_icon, icon_buffer, 0, 0);\n    wl_surface_damage(_glfw.wl.drag.drag_icon, 0, 0, INT32_MAX, INT32_MAX);\n    wl_surface_commit(_glfw.wl.drag.drag_icon);\n    wl_buffer_destroy(icon_buffer);\n    return 0;\n}\n\nint\n_glfwPlatformDragDataReady(const char *mime_type) {\n    for (size_t i = 0; i < _glfw.wl.drag.count; i++) {\n        if (strcmp(dr.mime_type, mime_type) == 0) {\n            if (!dr.watch_id) dr.watch_id = add_drag_watch(dr.fd);\n        }\n    }\n    return 0;\n}\n\nstatic void\ndrag_source_send(void *data UNUSED, struct wl_data_source *source UNUSED, const char *mime_type, int fd) {\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n#define abort() safe_close(fd); cancel_drag(GLFW_DRAG_CANCELLED);  return\n    if (!window) { abort(); }\n    mime_type = _glfw_strdup(mime_type);\n    if (!mime_type) { abort(); }\n    if (!_glfw.wl.drag.data_requests || _glfw.wl.drag.capacity <= _glfw.wl.drag.count + 1) {\n        _glfw.wl.drag.capacity = MAX(8u, _glfw.wl.drag.capacity * 2);\n        if (!(_glfw.wl.drag.data_requests = realloc(_glfw.wl.drag.data_requests, sizeof(_glfw.wl.drag.data_requests[0]) * _glfw.wl.drag.capacity))) { abort(); }\n    }\n    const size_t i = _glfw.wl.drag.count++;\n    memset(&dr, 0, sizeof(_glfw.wl.drag.data_requests[0]));\n    dr.mime_type = mime_type; dr.fd = fd;\n    dr.watch_id = add_drag_watch(fd);\n#undef abort\n}\n\n#undef dr\nstatic void\ndrag_source_target(void *data UNUSED, struct wl_data_source *source UNUSED, const char *mime_type) {\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (window) {\n        GLFWDragEvent ev = {.type=GLFW_DRAG_ACCEPTED, .mime_type=mime_type};\n        _glfwInputDragSourceRequest(window, &ev);\n    } else cancel_drag(GLFW_DRAG_CANCELLED);\n}\n\nstatic void\ndrag_source_action(void *data UNUSED, struct wl_data_source *source UNUSED, uint32_t dnd_action) {\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (window) {\n        GLFWDragOperationType op = GLFW_DRAG_OPERATION_GENERIC;\n        switch (dnd_action) {\n            case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: op = 0; break;\n            case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: op = GLFW_DRAG_OPERATION_COPY; break;\n            case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: op = GLFW_DRAG_OPERATION_MOVE; break;\n        }\n        _glfw.wl.drag.action = op;\n        GLFWDragEvent ev = {.type=GLFW_DRAG_ACTION_CHANGED, .action=op};\n        _glfwInputDragSourceRequest(window, &ev);\n    } else cancel_drag(GLFW_DRAG_CANCELLED);\n}\n\nstatic void\ndrag_source_dnd_drop_performed(void *data UNUSED, struct wl_data_source *source UNUSED) {\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (window) {\n        GLFWDragEvent ev = {.type=GLFW_DRAG_DROPPED};\n        _glfwInputDragSourceRequest(window, &ev);\n    } else cancel_drag(GLFW_DRAG_CANCELLED);\n}\n\nstatic void\ndrag_source_dnd_finished(void *data UNUSED, struct wl_data_source *source UNUSED) {\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (window) {\n        GLFWDragEvent ev = {.type=GLFW_DRAG_FINSHED, .action=_glfw.wl.drag.action};\n        _glfwInputDragSourceRequest(window, &ev);\n    }\n    _glfwFreeDragSourceData();\n}\n\nstatic void\ndrag_source_cancelled(void *data UNUSED, struct wl_data_source *source UNUSED) {\n    // Uber competent Wayland people contravene their own spec and make it\n    // impossible to distinguish between drag cancelled and dropped but not\n    // accepted. https://gitlab.freedesktop.org/wayland/wayland/-/issues/140\n    // so we assume this is a drop unless we are in top-level mode. Sigh.\n    cancel_drag(_glfw.wl.drag.toplevel_xdg_toplevel ? GLFW_DRAG_CANCELLED : GLFW_DRAG_DROPPED);\n}\n\n\nstatic const struct wl_data_source_listener drag_source_listener = {\n    .send = drag_source_send,\n    .cancelled = drag_source_cancelled,\n    .target = drag_source_target,\n    .action = drag_source_action,\n    .dnd_drop_performed = drag_source_dnd_drop_performed,\n    .dnd_finished = drag_source_dnd_finished,\n};\n\nvoid\n_glfwPlatformFreeDragSourceData(void) {\n    if (_glfw.wl.drag.drag_viewport) wp_viewport_destroy(_glfw.wl.drag.drag_viewport);\n    if (_glfw.wl.drag.toplevel_drag) xdg_toplevel_drag_v1_destroy(_glfw.wl.drag.toplevel_drag);\n    if (_glfw.wl.drag.toplevel_buffer) wl_buffer_destroy(_glfw.wl.drag.toplevel_buffer);\n    if (_glfw.wl.drag.toplevel_xdg_toplevel) xdg_toplevel_destroy(_glfw.wl.drag.toplevel_xdg_toplevel);\n    if (_glfw.wl.drag.toplevel_xdg_surface) xdg_surface_destroy(_glfw.wl.drag.toplevel_xdg_surface);\n    if (_glfw.wl.drag.drag_icon) wl_surface_destroy(_glfw.wl.drag.drag_icon);\n    if (_glfw.wl.drag.source) wl_data_source_destroy(_glfw.wl.drag.source);\n    if (_glfw.wl.drag.data_requests) {\n        for (size_t i = 0; i < _glfw.wl.drag.count; i++) {\n            free((void*)_glfw.wl.drag.data_requests[i].mime_type);\n            free(_glfw.wl.drag.data_requests[i].pending_data);\n            if (_glfw.wl.drag.data_requests[i].watch_id) removeWatch(&_glfw.wl.eventLoopData, _glfw.wl.drag.data_requests[i].watch_id);\n            if (_glfw.wl.drag.data_requests[i].fd > -1) { safe_close(_glfw.wl.drag.data_requests[i].fd); }\n        }\n        free(_glfw.wl.drag.data_requests);\n    }\n    memset(&_glfw.wl.drag, 0, sizeof(_glfw.wl.drag));\n}\n\nint\n_glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {\n    if (!_glfw.wl.dataDeviceManager) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Data device manager not available\");\n        return ENOTSUP;\n    }\n\n    if (!_glfw.wl.dataDevice) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Data device not available\");\n        return ENOTSUP;\n    }\n\n    // Create the data source\n    _glfw.wl.drag.source = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);\n    if (!_glfw.wl.drag.source) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Wayland: Failed to create data source for drag\");\n        return ENOMEM;\n    }\n\n    // Set the DND action based on operation type (bitfield)\n    uint32_t wl_actions = 0; int operations = _glfw.drag.operations;\n    if (operations & GLFW_DRAG_OPERATION_COPY)\n        wl_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;\n    if (operations & GLFW_DRAG_OPERATION_MOVE)\n        wl_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;\n    if (operations & GLFW_DRAG_OPERATION_GENERIC)\n        wl_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;\n    wl_data_source_set_actions(_glfw.wl.drag.source, wl_actions);\n\n    for (size_t i = 0; i < _glfw.drag.item_count; i++) wl_data_source_offer(_glfw.wl.drag.source, _glfw.drag.items[i].mime_type);\n    wl_data_source_add_listener(_glfw.wl.drag.source, &drag_source_listener, NULL);\n\n    // Set up the drag icon surface if thumbnail is provided\n    if (thumbnail && thumbnail->pixels) {\n        _glfw.wl.drag.drag_icon = wl_compositor_create_surface(_glfw.wl.compositor);\n        if (!_glfw.wl.drag.drag_icon) return ENOMEM;\n        struct wl_buffer* icon_buffer = NULL;\n        icon_buffer = createShmBuffer(thumbnail, false, true);\n        if (!icon_buffer) return ENOMEM;\n        if (_glfw.wl.wp_viewporter) {\n            double f_scale = _glfwWaylandWindowScale(window);\n            int logical_width = (int)(thumbnail->width / f_scale);\n            int logical_height = (int)(thumbnail->height / f_scale);\n            _glfw.wl.drag.drag_viewport = wp_viewporter_get_viewport(\n                    _glfw.wl.wp_viewporter, _glfw.wl.drag.drag_icon);\n            wp_viewport_set_destination(_glfw.wl.drag.drag_viewport, logical_width, logical_height);\n        } else {\n            int scale = _glfwWaylandIntegerWindowScale(window);\n            wl_surface_set_buffer_scale(_glfw.wl.drag.drag_icon, scale);\n        }\n\n        if (_glfw.drag.needs_toplevel_on_wayland && _glfw.wl.xdg_toplevel_drag_manager_v1) {\n            _glfw.wl.drag.toplevel_drag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(\n                _glfw.wl.xdg_toplevel_drag_manager_v1, _glfw.wl.drag.source);\n            if (!_glfw.wl.drag.toplevel_drag) return ENOMEM;\n            _glfw.wl.drag.toplevel_xdg_surface = xdg_wm_base_get_xdg_surface(\n                        _glfw.wl.wmBase, _glfw.wl.drag.drag_icon);\n            if (!_glfw.wl.drag.toplevel_xdg_surface) return ENOMEM;\n            xdg_surface_add_listener(_glfw.wl.drag.toplevel_xdg_surface,\n                                        &drag_toplevel_xdg_surface_listener, NULL);\n            _glfw.wl.drag.toplevel_xdg_toplevel = xdg_surface_get_toplevel(\n                    _glfw.wl.drag.toplevel_xdg_surface);\n            if (!_glfw.wl.drag.toplevel_xdg_toplevel) return ENOMEM;\n            xdg_toplevel_add_listener(_glfw.wl.drag.toplevel_xdg_toplevel, &drag_toplevel_listener, NULL);\n            _glfw.wl.drag.toplevel_buffer = icon_buffer; icon_buffer = NULL;\n            xdg_toplevel_drag_v1_attach(_glfw.wl.drag.toplevel_drag,\n                                    _glfw.wl.drag.toplevel_xdg_toplevel, 0, 0);\n        } else wl_surface_attach(_glfw.wl.drag.drag_icon, icon_buffer, 0, 0);\n        wl_surface_commit(_glfw.wl.drag.drag_icon);\n        if (icon_buffer) wl_buffer_destroy(icon_buffer);\n    }\n    // Start the drag operation\n    wl_data_device_start_drag(\n        _glfw.wl.dataDevice, _glfw.wl.drag.source, window->wl.surface,\n        _glfw.wl.xdg_toplevel_drag_manager_v1 ? NULL : _glfw.wl.drag.drag_icon,\n        _glfw.wl.pointer_serial);\n\n    return 0;\n}\n// }}}\n"
  },
  {
    "path": "glfw/wlr-layer-shell-unstable-v1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<protocol name=\"wlr_layer_shell_unstable_v1\">\n  <copyright>\n    Copyright © 2017 Drew DeVault\n\n    Permission to use, copy, modify, distribute, and sell this\n    software and its documentation for any purpose is hereby granted\n    without fee, provided that the above copyright notice appear in\n    all copies and that both that copyright notice and this permission\n    notice appear in supporting documentation, and that the name of\n    the copyright holders not be used in advertising or publicity\n    pertaining to distribution of the software without specific,\n    written prior permission.  The copyright holders make no\n    representations about the suitability of this software for any\n    purpose.  It is provided \"as is\" without express or implied\n    warranty.\n\n    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS\n    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\n    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\n    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN\n    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\n    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\n    THIS SOFTWARE.\n  </copyright>\n\n  <interface name=\"zwlr_layer_shell_v1\" version=\"5\">\n    <description summary=\"create surfaces that are layers of the desktop\">\n      Clients can use this interface to assign the surface_layer role to\n      wl_surfaces. Such surfaces are assigned to a \"layer\" of the output and\n      rendered with a defined z-depth respective to each other. They may also be\n      anchored to the edges and corners of a screen and specify input handling\n      semantics. This interface should be suitable for the implementation of\n      many desktop shell components, and a broad number of other applications\n      that interact with the desktop.\n    </description>\n\n    <request name=\"get_layer_surface\">\n      <description summary=\"create a layer_surface from a surface\">\n        Create a layer surface for an existing surface. This assigns the role of\n        layer_surface, or raises a protocol error if another role is already\n        assigned.\n\n        Creating a layer surface from a wl_surface which has a buffer attached\n        or committed is a client error, and any attempts by a client to attach\n        or manipulate a buffer prior to the first layer_surface.configure call\n        must also be treated as errors.\n\n        After creating a layer_surface object and setting it up, the client\n        must perform an initial commit without any buffer attached.\n        The compositor will reply with a layer_surface.configure event.\n        The client must acknowledge it and is then allowed to attach a buffer\n        to map the surface.\n\n        You may pass NULL for output to allow the compositor to decide which\n        output to use. Generally this will be the one that the user most\n        recently interacted with.\n\n        Clients can specify a namespace that defines the purpose of the layer\n        surface.\n      </description>\n      <arg name=\"id\" type=\"new_id\" interface=\"zwlr_layer_surface_v1\"/>\n      <arg name=\"surface\" type=\"object\" interface=\"wl_surface\"/>\n      <arg name=\"output\" type=\"object\" interface=\"wl_output\" allow-null=\"true\"/>\n      <arg name=\"layer\" type=\"uint\" enum=\"layer\" summary=\"layer to add this surface to\"/>\n      <arg name=\"namespace\" type=\"string\" summary=\"namespace for the layer surface\"/>\n    </request>\n\n    <enum name=\"error\">\n      <entry name=\"role\" value=\"0\" summary=\"wl_surface has another role\"/>\n      <entry name=\"invalid_layer\" value=\"1\" summary=\"layer value is invalid\"/>\n      <entry name=\"already_constructed\" value=\"2\" summary=\"wl_surface has a buffer attached or committed\"/>\n    </enum>\n\n    <enum name=\"layer\">\n      <description summary=\"available layers for surfaces\">\n        These values indicate which layers a surface can be rendered in. They\n        are ordered by z depth, bottom-most first. Traditional shell surfaces\n        will typically be rendered between the bottom and top layers.\n        Fullscreen shell surfaces are typically rendered at the top layer.\n        Multiple surfaces can share a single layer, and ordering within a\n        single layer is undefined.\n      </description>\n\n      <entry name=\"background\" value=\"0\"/>\n      <entry name=\"bottom\" value=\"1\"/>\n      <entry name=\"top\" value=\"2\"/>\n      <entry name=\"overlay\" value=\"3\"/>\n    </enum>\n\n    <!-- Version 3 additions -->\n\n    <request name=\"destroy\" type=\"destructor\" since=\"3\">\n      <description summary=\"destroy the layer_shell object\">\n        This request indicates that the client will not use the layer_shell\n        object any more. Objects that have been created through this instance\n        are not affected.\n      </description>\n    </request>\n  </interface>\n\n  <interface name=\"zwlr_layer_surface_v1\" version=\"5\">\n    <description summary=\"layer metadata interface\">\n      An interface that may be implemented by a wl_surface, for surfaces that\n      are designed to be rendered as a layer of a stacked desktop-like\n      environment.\n\n      Layer surface state (layer, size, anchor, exclusive zone,\n      margin, interactivity) is double-buffered, and will be applied at the\n      time wl_surface.commit of the corresponding wl_surface is called.\n\n      Attaching a null buffer to a layer surface unmaps it.\n\n      Unmapping a layer_surface means that the surface cannot be shown by the\n      compositor until it is explicitly mapped again. The layer_surface\n      returns to the state it had right after layer_shell.get_layer_surface.\n      The client can re-map the surface by performing a commit without any\n      buffer attached, waiting for a configure event and handling it as usual.\n    </description>\n\n    <request name=\"set_size\">\n      <description summary=\"sets the size of the surface\">\n        Sets the size of the surface in surface-local coordinates. The\n        compositor will display the surface centered with respect to its\n        anchors.\n\n        If you pass 0 for either value, the compositor will assign it and\n        inform you of the assignment in the configure event. You must set your\n        anchor to opposite edges in the dimensions you omit; not doing so is a\n        protocol error. Both values are 0 by default.\n\n        Size is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"width\" type=\"uint\"/>\n      <arg name=\"height\" type=\"uint\"/>\n    </request>\n\n    <request name=\"set_anchor\">\n      <description summary=\"configures the anchor point of the surface\">\n        Requests that the compositor anchor the surface to the specified edges\n        and corners. If two orthogonal edges are specified (e.g. 'top' and\n        'left'), then the anchor point will be the intersection of the edges\n        (e.g. the top left corner of the output); otherwise the anchor point\n        will be centered on that edge, or in the center if none is specified.\n\n        Anchor is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"anchor\" type=\"uint\" enum=\"anchor\"/>\n    </request>\n\n    <request name=\"set_exclusive_zone\">\n      <description summary=\"configures the exclusive geometry of this surface\">\n        Requests that the compositor avoids occluding an area with other\n        surfaces. The compositor's use of this information is\n        implementation-dependent - do not assume that this region will not\n        actually be occluded.\n\n        A positive value is only meaningful if the surface is anchored to one\n        edge or an edge and both perpendicular edges. If the surface is not\n        anchored, anchored to only two perpendicular edges (a corner), anchored\n        to only two parallel edges or anchored to all edges, a positive value\n        will be treated the same as zero.\n\n        A positive zone is the distance from the edge in surface-local\n        coordinates to consider exclusive.\n\n        Surfaces that do not wish to have an exclusive zone may instead specify\n        how they should interact with surfaces that do. If set to zero, the\n        surface indicates that it would like to be moved to avoid occluding\n        surfaces with a positive exclusive zone. If set to -1, the surface\n        indicates that it would not like to be moved to accommodate for other\n        surfaces, and the compositor should extend it all the way to the edges\n        it is anchored to.\n\n        For example, a panel might set its exclusive zone to 10, so that\n        maximized shell surfaces are not shown on top of it. A notification\n        might set its exclusive zone to 0, so that it is moved to avoid\n        occluding the panel, but shell surfaces are shown underneath it. A\n        wallpaper or lock screen might set their exclusive zone to -1, so that\n        they stretch below or over the panel.\n\n        The default value is 0.\n\n        Exclusive zone is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"zone\" type=\"int\"/>\n    </request>\n\n    <request name=\"set_margin\">\n      <description summary=\"sets a margin from the anchor point\">\n        Requests that the surface be placed some distance away from the anchor\n        point on the output, in surface-local coordinates. Setting this value\n        for edges you are not anchored to has no effect.\n\n        The exclusive zone includes the margin.\n\n        Margin is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"top\" type=\"int\"/>\n      <arg name=\"right\" type=\"int\"/>\n      <arg name=\"bottom\" type=\"int\"/>\n      <arg name=\"left\" type=\"int\"/>\n    </request>\n\n    <enum name=\"keyboard_interactivity\">\n      <description summary=\"types of keyboard interaction possible for a layer shell surface\">\n        Types of keyboard interaction possible for layer shell surfaces. The\n        rationale for this is twofold: (1) some applications are not interested\n        in keyboard events and not allowing them to be focused can improve the\n        desktop experience; (2) some applications will want to take exclusive\n        keyboard focus.\n      </description>\n\n      <entry name=\"none\" value=\"0\">\n        <description summary=\"no keyboard focus is possible\">\n          This value indicates that this surface is not interested in keyboard\n          events and the compositor should never assign it the keyboard focus.\n\n          This is the default value, set for newly created layer shell surfaces.\n\n          This is useful for e.g. desktop widgets that display information or\n          only have interaction with non-keyboard input devices.\n        </description>\n      </entry>\n      <entry name=\"exclusive\" value=\"1\">\n        <description summary=\"request exclusive keyboard focus\">\n          Request exclusive keyboard focus if this surface is above the shell surface layer.\n\n          For the top and overlay layers, the seat will always give\n          exclusive keyboard focus to the top-most layer which has keyboard\n          interactivity set to exclusive. If this layer contains multiple\n          surfaces with keyboard interactivity set to exclusive, the compositor\n          determines the one receiving keyboard events in an implementation-\n          defined manner. In this case, no guarantee is made when this surface\n          will receive keyboard focus (if ever).\n\n          For the bottom and background layers, the compositor is allowed to use\n          normal focus semantics.\n\n          This setting is mainly intended for applications that need to ensure\n          they receive all keyboard events, such as a lock screen or a password\n          prompt.\n        </description>\n      </entry>\n      <entry name=\"on_demand\" value=\"2\" since=\"4\">\n        <description summary=\"request regular keyboard focus semantics\">\n          This requests the compositor to allow this surface to be focused and\n          unfocused by the user in an implementation-defined manner. The user\n          should be able to unfocus this surface even regardless of the layer\n          it is on.\n\n          Typically, the compositor will want to use its normal mechanism to\n          manage keyboard focus between layer shell surfaces with this setting\n          and regular toplevels on the desktop layer (e.g. click to focus).\n          Nevertheless, it is possible for a compositor to require a special\n          interaction to focus or unfocus layer shell surfaces (e.g. requiring\n          a click even if focus follows the mouse normally, or providing a\n          keybinding to switch focus between layers).\n\n          This setting is mainly intended for desktop shell components (e.g.\n          panels) that allow keyboard interaction. Using this option can allow\n          implementing a desktop shell that can be fully usable without the\n          mouse.\n        </description>\n      </entry>\n    </enum>\n\n    <request name=\"set_keyboard_interactivity\">\n      <description summary=\"requests keyboard events\">\n        Set how keyboard events are delivered to this surface. By default,\n        layer shell surfaces do not receive keyboard events; this request can\n        be used to change this.\n\n        This setting is inherited by child surfaces set by the get_popup\n        request.\n\n        Layer surfaces receive pointer, touch, and tablet events normally. If\n        you do not want to receive them, set the input region on your surface\n        to an empty region.\n\n        Keyboard interactivity is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"keyboard_interactivity\" type=\"uint\" enum=\"keyboard_interactivity\"/>\n    </request>\n\n    <request name=\"get_popup\">\n      <description summary=\"assign this layer_surface as an xdg_popup parent\">\n        This assigns an xdg_popup's parent to this layer_surface.  This popup\n        should have been created via xdg_surface::get_popup with the parent set\n        to NULL, and this request must be invoked before committing the popup's\n        initial state.\n\n        See the documentation of xdg_popup for more details about what an\n        xdg_popup is and how it is used.\n      </description>\n      <arg name=\"popup\" type=\"object\" interface=\"xdg_popup\"/>\n    </request>\n\n    <request name=\"ack_configure\">\n      <description summary=\"ack a configure event\">\n        When a configure event is received, if a client commits the\n        surface in response to the configure event, then the client\n        must make an ack_configure request sometime before the commit\n        request, passing along the serial of the configure event.\n\n        If the client receives multiple configure events before it\n        can respond to one, it only has to ack the last configure event.\n\n        A client is not required to commit immediately after sending\n        an ack_configure request - it may even ack_configure several times\n        before its next surface commit.\n\n        A client may send multiple ack_configure requests before committing, but\n        only the last request sent before a commit indicates which configure\n        event the client really is responding to.\n      </description>\n      <arg name=\"serial\" type=\"uint\" summary=\"the serial from the configure event\"/>\n    </request>\n\n    <request name=\"destroy\" type=\"destructor\">\n      <description summary=\"destroy the layer_surface\">\n        This request destroys the layer surface.\n      </description>\n    </request>\n\n    <event name=\"configure\">\n      <description summary=\"suggest a surface change\">\n        The configure event asks the client to resize its surface.\n\n        Clients should arrange their surface for the new states, and then send\n        an ack_configure request with the serial sent in this configure event at\n        some point before committing the new surface.\n\n        The client is free to dismiss all but the last configure event it\n        received.\n\n        The width and height arguments specify the size of the window in\n        surface-local coordinates.\n\n        The size is a hint, in the sense that the client is free to ignore it if\n        it doesn't resize, pick a smaller size (to satisfy aspect ratio or\n        resize in steps of NxM pixels). If the client picks a smaller size and\n        is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the\n        surface will be centered on this axis.\n\n        If the width or height arguments are zero, it means the client should\n        decide its own window dimension.\n      </description>\n      <arg name=\"serial\" type=\"uint\"/>\n      <arg name=\"width\" type=\"uint\"/>\n      <arg name=\"height\" type=\"uint\"/>\n    </event>\n\n    <event name=\"closed\">\n      <description summary=\"surface should be closed\">\n        The closed event is sent by the compositor when the surface will no\n        longer be shown. The output may have been destroyed or the user may\n        have asked for it to be removed. Further changes to the surface will be\n        ignored. The client should destroy the resource after receiving this\n        event, and create a new surface if they so choose.\n      </description>\n    </event>\n\n    <enum name=\"error\">\n      <entry name=\"invalid_surface_state\" value=\"0\" summary=\"provided surface state is invalid\"/>\n      <entry name=\"invalid_size\" value=\"1\" summary=\"size is invalid\"/>\n      <entry name=\"invalid_anchor\" value=\"2\" summary=\"anchor bitfield is invalid\"/>\n      <entry name=\"invalid_keyboard_interactivity\" value=\"3\" summary=\"keyboard interactivity is invalid\"/>\n      <entry name=\"invalid_exclusive_edge\" value=\"4\" summary=\"exclusive edge is invalid given the surface anchors\"/>\n    </enum>\n\n    <enum name=\"anchor\" bitfield=\"true\">\n      <entry name=\"top\" value=\"1\" summary=\"the top edge of the anchor rectangle\"/>\n      <entry name=\"bottom\" value=\"2\" summary=\"the bottom edge of the anchor rectangle\"/>\n      <entry name=\"left\" value=\"4\" summary=\"the left edge of the anchor rectangle\"/>\n      <entry name=\"right\" value=\"8\" summary=\"the right edge of the anchor rectangle\"/>\n    </enum>\n\n    <!-- Version 2 additions -->\n\n    <request name=\"set_layer\" since=\"2\">\n      <description summary=\"change the layer of the surface\">\n        Change the layer that the surface is rendered on.\n\n        Layer is double-buffered, see wl_surface.commit.\n      </description>\n      <arg name=\"layer\" type=\"uint\" enum=\"zwlr_layer_shell_v1.layer\" summary=\"layer to move this surface to\"/>\n    </request>\n\n    <!-- Version 5 additions -->\n\n    <request name=\"set_exclusive_edge\" since=\"5\">\n      <description summary=\"set the edge the exclusive zone will be applied to\">\n        Requests an edge for the exclusive zone to apply. The exclusive\n        edge will be automatically deduced from anchor points when possible,\n        but when the surface is anchored to a corner, it will be necessary\n        to set it explicitly to disambiguate, as it is not possible to deduce\n        which one of the two corner edges should be used.\n\n        The edge must be one the surface is anchored to, otherwise the\n        invalid_exclusive_edge protocol error will be raised.\n      </description>\n      <arg name=\"edge\" type=\"uint\" enum=\"anchor\"/>\n    </request>\n  </interface>\n</protocol>\n"
  },
  {
    "path": "glfw/x11_init.c",
    "content": "//========================================================================\n// GLFW 3.4 X11 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#define _GNU_SOURCE\n#include \"internal.h\"\n#include \"backend_utils.h\"\n#include \"linux_desktop_settings.h\"\n\n#include <X11/Xresource.h>\n\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n#include <stdio.h>\n#include <locale.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n\n// Return the atom ID only if it is listed in the specified array\n//\nstatic Atom getAtomIfSupported(Atom* supportedAtoms,\n                               unsigned long atomCount,\n                               const Atom atom)\n{\n    for (unsigned long i = 0;  i < atomCount;  i++) {\n        if (supportedAtoms[i] == atom) return atom;\n    }\n    return None;\n}\n\n// Check whether the running window manager is EWMH-compliant\n//\nstatic void detectEWMH(void)\n{\n    // First we read the _NET_SUPPORTING_WM_CHECK property on the root window\n\n    Window* windowFromRoot = NULL;\n    if (!_glfwGetWindowPropertyX11(_glfw.x11.root,\n                                   _glfw.x11.NET_SUPPORTING_WM_CHECK,\n                                   XA_WINDOW,\n                                   (unsigned char**) &windowFromRoot))\n    {\n        return;\n    }\n\n    _glfwGrabErrorHandlerX11();\n\n    // If it exists, it should be the XID of a top-level window\n    // Then we look for the same property on that window\n\n    Window* windowFromChild = NULL;\n    if (!_glfwGetWindowPropertyX11(*windowFromRoot,\n                                   _glfw.x11.NET_SUPPORTING_WM_CHECK,\n                                   XA_WINDOW,\n                                   (unsigned char**) &windowFromChild))\n    {\n        XFree(windowFromRoot);\n        return;\n    }\n\n    _glfwReleaseErrorHandlerX11();\n\n    // If the property exists, it should contain the XID of the window\n\n    if (*windowFromRoot != *windowFromChild)\n    {\n        XFree(windowFromRoot);\n        XFree(windowFromChild);\n        return;\n    }\n\n    XFree(windowFromRoot);\n    XFree(windowFromChild);\n\n    // We are now fairly sure that an EWMH-compliant WM is currently running\n    // We can now start querying the WM about what features it supports by\n    // looking in the _NET_SUPPORTED property on the root window\n    // It should contain a list of supported EWMH protocol and state atoms\n\n    Atom* supportedAtoms = NULL;\n    const unsigned long atomCount =\n        _glfwGetWindowPropertyX11(_glfw.x11.root,\n                                  _glfw.x11.NET_SUPPORTED,\n                                  XA_ATOM,\n                                  (unsigned char**) &supportedAtoms);\n    if (!supportedAtoms)\n        return;\n\n    // See which of the atoms we support that are supported by the WM\n\n#define ALL_ATOMS  \\\n    S(NET_WM_STATE) S(NET_WM_STATE_ABOVE) S(NET_WM_STATE_BELOW) S(NET_WM_STATE_FULLSCREEN) \\\n    S(NET_WM_STATE_MAXIMIZED_VERT) S(NET_WM_STATE_MAXIMIZED_HORZ) S(NET_WM_STATE_DEMANDS_ATTENTION) \\\n    S(NET_WM_STATE_SKIP_TASKBAR) S(NET_WM_STATE_SKIP_PAGER) S(NET_WM_STATE_STICKY) \\\n\\\n    S(NET_WM_FULLSCREEN_MONITORS) S(NET_WM_STRUT_PARTIAL) \\\n\\\n    S(NET_WM_WINDOW_TYPE) S(NET_WM_WINDOW_TYPE_NORMAL) S(NET_WM_WINDOW_TYPE_DOCK) S(NET_WM_WINDOW_TYPE_DESKTOP) \\\n    S(NET_WM_WINDOW_TYPE_UTILITY) S(NET_WM_WINDOW_TYPE_SPLASH) S(NET_WM_WINDOW_TYPE_DIALOG) S(NET_WM_WINDOW_TYPE_MENU) \\\n    S(NET_WM_WINDOW_TYPE_NOTIFICATION) \\\n\\\n    S(NET_WORKAREA) S(NET_CURRENT_DESKTOP) S(NET_ACTIVE_WINDOW) S(NET_FRAME_EXTENTS) S(NET_REQUEST_FRAME_EXTENTS) \\\n\\\n    S(NET_WM_ALLOWED_ACTIONS) S(NET_WM_ACTION_MOVE) S(NET_WM_ACTION_RESIZE) S(NET_WM_ACTION_MINIMIZE) \\\n    S(NET_WM_ACTION_SHADE) S(NET_WM_ACTION_STICK) S(NET_WM_ACTION_MAXIMIZE_HORZ) S(NET_WM_ACTION_MAXIMIZE_VERT) \\\n    S(NET_WM_ACTION_FULLSCREEN) S(NET_WM_ACTION_CHANGE_DESKTOP) S(NET_WM_ACTION_CLOSE) S(NET_WM_ACTION_ABOVE) \\\n    S(NET_WM_ACTION_BELOW) S(NET_WM_ACTION_ABOVE_BELOW)\n\n    static const char* atom_names[40] = {\n#define S(x) \"_\" #x,\n        ALL_ATOMS\n    };\n#undef S\n    Atom atoms[arraysz(atom_names)];\n    XInternAtoms(_glfw.x11.display, (char**)atom_names, arraysz(atom_names), False, atoms);\n    unsigned i = 0;\n#define S(name) _glfw.x11.name = getAtomIfSupported(supportedAtoms, atomCount, atoms[i++]);\n    ALL_ATOMS\n#undef S\n#undef ALL_ATOMS\n    XFree(supportedAtoms);\n}\n\nvoid\nread_xi_scroll_devices(void) {\n#define xi _glfw.x11.xi\n    xi.num_scroll_devices = 0;\n    // Require XI2.1 or later for smooth scrolling\n    if (!xi.available || xi.major < 2 || (xi.major == 2 && xi.minor < 1) || !xi.LIBINPUT_SCROLL_METHOD_ENABLED) return;\n#undef xi\n    int deviceCount;\n    XIDeviceInfo* devices = XIQueryDevice(_glfw.x11.display, XIAllDevices, &deviceCount);\n    if (!devices) return;\n    for (int i = 0; i < deviceCount; i++) {\n        XIDeviceInfo* device = &devices[i];\n        if (device->use == XIMasterPointer) _glfw.x11.xi.master_pointer_id = device->deviceid;\n        if (device->use != XISlavePointer || !device->enabled) continue;\n        Atom actual_type;\n        int actual_format;\n        unsigned long nitems, bytes_after;\n        unsigned char *data = NULL;\n        bool is_highres = false;\n\n        XIGetProperty(_glfw.x11.display, device->deviceid, _glfw.x11.xi.LIBINPUT_SCROLL_METHOD_ENABLED, 0, 2, False, XA_INTEGER, &actual_type, &actual_format, &nitems, &bytes_after, &data);\n        if (data) {\n            if (nitems > 1) is_highres = data[0] || data[1];\n            XFree(data);\n        }\n\n        // Detect if this is a finger-based device (touchpad/touchscreen)\n        bool is_finger_based = false;\n\n        // Method 1: Check for libinput tapping support (touchpads only)\n        if (_glfw.x11.xi.LIBINPUT_TAPPING != None) {\n            Atom tapping_type;\n            int tapping_format;\n            unsigned long tapping_nitems, tapping_bytes;\n            unsigned char *tapping_data = NULL;\n\n            if (XIGetProperty(_glfw.x11.display, device->deviceid,\n                              _glfw.x11.xi.LIBINPUT_TAPPING, 0, 1, False, AnyPropertyType,\n                              &tapping_type, &tapping_format, &tapping_nitems,\n                              &tapping_bytes, &tapping_data) == Success) {\n                if (tapping_data) {\n                    if (tapping_nitems > 0) is_finger_based = true;\n                    XFree(tapping_data);\n                }\n            }\n        }\n\n        // Method 2: Check for touch class (touchscreens/touchpads)\n        if (!is_finger_based) {\n            for (int j = 0; j < device->num_classes; j++) {\n                if (device->classes[j]->type == XITouchClass) {\n                    is_finger_based = true;\n                    break;\n                }\n            }\n        }\n        for (int j = 0; j < device->num_classes; j++) {\n            if (device->classes[j]->type != XIScrollClass) continue;\n            XIScrollClassInfo* scroll = (XIScrollClassInfo*)device->classes[j];\n            XIScrollDevice *d = NULL;\n            for (unsigned k = 0; k < _glfw.x11.xi.num_scroll_devices; k++) {\n                XIScrollDevice *t = _glfw.x11.xi.scroll_devices + k;\n                if (t->deviceid == device->deviceid && t->sourceid == scroll->sourceid) { d = t; break; }\n            }\n            if (!d) {\n                if (_glfw.x11.xi.num_scroll_devices >= arraysz(_glfw.x11.xi.scroll_devices)) continue;\n                d = &_glfw.x11.xi.scroll_devices[_glfw.x11.xi.num_scroll_devices++];\n                *d = (XIScrollDevice){\n                    .is_finger_based=is_finger_based, .deviceid=device->deviceid, .sourceid=scroll->sourceid,\n                };\n                if (is_highres) {\n                    d->type_detected = true;\n                    d->offset_type = GLFW_SCROLL_OFFEST_HIGHRES;\n                }\n                memcpy(d->name, device->name, MIN(sizeof(d->name)-1, strlen(device->name)));\n            }\n            if (d->num_valuators >= arraysz(d->valuators)) continue;\n            XIScrollValuator *v = d->valuators + d->num_valuators++;\n            *v = (XIScrollValuator){\n                .number=scroll->number, .is_vertical=scroll->scroll_type == XIScrollTypeVertical,\n                .increment=scroll->increment,\n            };\n        }\n        for (int j = 0; j < device->num_classes; j++) {\n            if (device->classes[j]->type != XIValuatorClass) continue;\n            XIValuatorClassInfo* vi = (XIValuatorClassInfo*)device->classes[j];\n            XIScrollDevice *d = NULL;\n            for (unsigned k = 0; k < _glfw.x11.xi.num_scroll_devices; k++) {\n                XIScrollDevice *t = _glfw.x11.xi.scroll_devices + k;\n                if (t->deviceid == device->deviceid && t->sourceid == vi->sourceid) { d = t; break; }\n            }\n            if (!d) continue;\n            XIScrollValuator *v = NULL;\n            for (unsigned k = 0; k < d->num_valuators; k++) {\n                XIScrollValuator *t = d->valuators + k;\n                if (t->number == vi->number) { v = t; break; }\n            }\n            if (!v) continue;\n            v->value = vi->value; v->mode = vi->mode; v->resolution = vi->resolution;\n            v->min = vi->min; v->max = vi->max; v->initialized = true;\n        }\n    }\n    XIFreeDeviceInfo(devices);\n}\n\n// Look for and initialize supported X11 extensions\n//\nstatic bool initExtensions(void)\n{\n    _glfw.x11.vidmode.handle = _glfw_dlopen(\"libXxf86vm.so.1\");\n    if (_glfw.x11.vidmode.handle)\n    {\n        glfw_dlsym(_glfw.x11.vidmode.QueryExtension, _glfw.x11.vidmode.handle, \"XF86VidModeQueryExtension\");\n        glfw_dlsym(_glfw.x11.vidmode.GetGammaRamp, _glfw.x11.vidmode.handle, \"XF86VidModeGetGammaRamp\");\n        glfw_dlsym(_glfw.x11.vidmode.SetGammaRamp, _glfw.x11.vidmode.handle, \"XF86VidModeSetGammaRamp\");\n        glfw_dlsym(_glfw.x11.vidmode.GetGammaRampSize, _glfw.x11.vidmode.handle, \"XF86VidModeGetGammaRampSize\");\n\n        _glfw.x11.vidmode.available =\n            XF86VidModeQueryExtension(_glfw.x11.display,\n                                      &_glfw.x11.vidmode.eventBase,\n                                      &_glfw.x11.vidmode.errorBase);\n    }\n\n#if defined(__CYGWIN__)\n    _glfw.x11.xi.handle = _glfw_dlopen(\"libXi-6.so\");\n#else\n    _glfw.x11.xi.handle = _glfw_dlopen(\"libXi.so.6\");\n#endif\n    if (_glfw.x11.xi.handle)\n    {\n        glfw_dlsym(_glfw.x11.xi.QueryVersion, _glfw.x11.xi.handle, \"XIQueryVersion\");\n        glfw_dlsym(_glfw.x11.xi.SelectEvents, _glfw.x11.xi.handle, \"XISelectEvents\");\n        glfw_dlsym(_glfw.x11.xi.QueryDevice, _glfw.x11.xi.handle, \"XIQueryDevice\");\n        glfw_dlsym(_glfw.x11.xi.FreeDeviceInfo, _glfw.x11.xi.handle, \"XIFreeDeviceInfo\");\n        glfw_dlsym(_glfw.x11.xi.GetProperty, _glfw.x11.xi.handle, \"XIGetProperty\");\n\n        if (XQueryExtension(_glfw.x11.display,\n                            \"XInputExtension\",\n                            &_glfw.x11.xi.majorOpcode,\n                            &_glfw.x11.xi.eventBase,\n                            &_glfw.x11.xi.errorBase))\n        {\n            _glfw.x11.xi.major = 2;\n            _glfw.x11.xi.minor = 1;\n\n            if (XIQueryVersion(_glfw.x11.display,\n                               &_glfw.x11.xi.major,\n                               &_glfw.x11.xi.minor) == Success)\n            {\n                _glfw.x11.xi.available = true;\n            }\n        }\n    }\n\n#if defined(__CYGWIN__)\n    _glfw.x11.randr.handle = _glfw_dlopen(\"libXrandr-2.so\");\n#else\n    _glfw.x11.randr.handle = _glfw_dlopen(\"libXrandr.so.2\");\n#endif\n    if (_glfw.x11.randr.handle)\n    {\n        glfw_dlsym(_glfw.x11.randr.AllocGamma, _glfw.x11.randr.handle, \"XRRAllocGamma\");\n        glfw_dlsym(_glfw.x11.randr.FreeGamma, _glfw.x11.randr.handle, \"XRRFreeGamma\");\n        glfw_dlsym(_glfw.x11.randr.FreeCrtcInfo, _glfw.x11.randr.handle, \"XRRFreeCrtcInfo\");\n        glfw_dlsym(_glfw.x11.randr.FreeGamma, _glfw.x11.randr.handle, \"XRRFreeGamma\");\n        glfw_dlsym(_glfw.x11.randr.FreeOutputInfo, _glfw.x11.randr.handle, \"XRRFreeOutputInfo\");\n        glfw_dlsym(_glfw.x11.randr.FreeScreenResources, _glfw.x11.randr.handle, \"XRRFreeScreenResources\");\n        glfw_dlsym(_glfw.x11.randr.GetCrtcGamma, _glfw.x11.randr.handle, \"XRRGetCrtcGamma\");\n        glfw_dlsym(_glfw.x11.randr.GetCrtcGammaSize, _glfw.x11.randr.handle, \"XRRGetCrtcGammaSize\");\n        glfw_dlsym(_glfw.x11.randr.GetCrtcInfo, _glfw.x11.randr.handle, \"XRRGetCrtcInfo\");\n        glfw_dlsym(_glfw.x11.randr.GetOutputInfo, _glfw.x11.randr.handle, \"XRRGetOutputInfo\");\n        glfw_dlsym(_glfw.x11.randr.GetOutputPrimary, _glfw.x11.randr.handle, \"XRRGetOutputPrimary\");\n        glfw_dlsym(_glfw.x11.randr.GetScreenResourcesCurrent, _glfw.x11.randr.handle, \"XRRGetScreenResourcesCurrent\");\n        glfw_dlsym(_glfw.x11.randr.QueryExtension, _glfw.x11.randr.handle, \"XRRQueryExtension\");\n        glfw_dlsym(_glfw.x11.randr.QueryVersion, _glfw.x11.randr.handle, \"XRRQueryVersion\");\n        glfw_dlsym(_glfw.x11.randr.SelectInput, _glfw.x11.randr.handle, \"XRRSelectInput\");\n        glfw_dlsym(_glfw.x11.randr.SetCrtcConfig, _glfw.x11.randr.handle, \"XRRSetCrtcConfig\");\n        glfw_dlsym(_glfw.x11.randr.SetCrtcGamma, _glfw.x11.randr.handle, \"XRRSetCrtcGamma\");\n        glfw_dlsym(_glfw.x11.randr.UpdateConfiguration, _glfw.x11.randr.handle, \"XRRUpdateConfiguration\");\n\n        if (XRRQueryExtension(_glfw.x11.display,\n                              &_glfw.x11.randr.eventBase,\n                              &_glfw.x11.randr.errorBase))\n        {\n            if (XRRQueryVersion(_glfw.x11.display,\n                                &_glfw.x11.randr.major,\n                                &_glfw.x11.randr.minor))\n            {\n                // The GLFW RandR path requires at least version 1.3\n                if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3)\n                    _glfw.x11.randr.available = true;\n            }\n            else\n            {\n                _glfwInputError(GLFW_PLATFORM_ERROR,\n                                \"X11: Failed to query RandR version\");\n            }\n        }\n    }\n\n    if (_glfw.x11.randr.available)\n    {\n        XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display,\n                                                              _glfw.x11.root);\n\n        if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0]))\n        {\n            // This is likely an older Nvidia driver with broken gamma support\n            // Flag it as useless and fall back to xf86vm gamma, if available\n            _glfw.x11.randr.gammaBroken = true;\n        }\n\n        if (!sr->ncrtc)\n        {\n            // A system without CRTCs is likely a system with broken RandR\n            // Disable the RandR monitor path and fall back to core functions\n            _glfw.x11.randr.monitorBroken = true;\n        }\n\n        XRRFreeScreenResources(sr);\n    }\n\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)\n    {\n        XRRSelectInput(_glfw.x11.display, _glfw.x11.root,\n                       RROutputChangeNotifyMask);\n    }\n\n#if defined(__CYGWIN__)\n    _glfw.x11.xcursor.handle = _glfw_dlopen(\"libXcursor-1.so\");\n#else\n    _glfw.x11.xcursor.handle = _glfw_dlopen(\"libXcursor.so.1\");\n#endif\n    if (_glfw.x11.xcursor.handle)\n    {\n        glfw_dlsym(_glfw.x11.xcursor.ImageCreate, _glfw.x11.xcursor.handle, \"XcursorImageCreate\");\n        glfw_dlsym(_glfw.x11.xcursor.ImageDestroy, _glfw.x11.xcursor.handle, \"XcursorImageDestroy\");\n        glfw_dlsym(_glfw.x11.xcursor.ImageLoadCursor, _glfw.x11.xcursor.handle, \"XcursorImageLoadCursor\");\n    }\n\n#if defined(__CYGWIN__)\n    _glfw.x11.xinerama.handle = _glfw_dlopen(\"libXinerama-1.so\");\n#else\n    _glfw.x11.xinerama.handle = _glfw_dlopen(\"libXinerama.so.1\");\n#endif\n    if (_glfw.x11.xinerama.handle)\n    {\n        glfw_dlsym(_glfw.x11.xinerama.IsActive, _glfw.x11.xinerama.handle, \"XineramaIsActive\");\n        glfw_dlsym(_glfw.x11.xinerama.QueryExtension, _glfw.x11.xinerama.handle, \"XineramaQueryExtension\");\n        glfw_dlsym(_glfw.x11.xinerama.QueryScreens, _glfw.x11.xinerama.handle, \"XineramaQueryScreens\");\n\n        if (XineramaQueryExtension(_glfw.x11.display,\n                                   &_glfw.x11.xinerama.major,\n                                   &_glfw.x11.xinerama.minor))\n        {\n            if (XineramaIsActive(_glfw.x11.display))\n                _glfw.x11.xinerama.available = true;\n        }\n    }\n\n#if defined(__CYGWIN__)\n    _glfw.x11.xrender.handle = _glfw_dlopen(\"libXrender-1.so\");\n#else\n    _glfw.x11.xrender.handle = _glfw_dlopen(\"libXrender.so.1\");\n#endif\n    if (_glfw.x11.xrender.handle)\n    {\n        glfw_dlsym(_glfw.x11.xrender.QueryExtension, _glfw.x11.xrender.handle, \"XRenderQueryExtension\");\n        glfw_dlsym(_glfw.x11.xrender.QueryVersion, _glfw.x11.xrender.handle, \"XRenderQueryVersion\");\n        glfw_dlsym(_glfw.x11.xrender.FindVisualFormat, _glfw.x11.xrender.handle, \"XRenderFindVisualFormat\");\n\n        if (XRenderQueryExtension(_glfw.x11.display,\n                                  &_glfw.x11.xrender.errorBase,\n                                  &_glfw.x11.xrender.eventBase))\n        {\n            if (XRenderQueryVersion(_glfw.x11.display,\n                                    &_glfw.x11.xrender.major,\n                                    &_glfw.x11.xrender.minor))\n            {\n                _glfw.x11.xrender.available = true;\n            }\n        }\n    }\n\n#if defined(__CYGWIN__)\n    _glfw.x11.xshape.handle = _glfw_dlopen(\"libXext-6.so\");\n#else\n    _glfw.x11.xshape.handle = _glfw_dlopen(\"libXext.so.6\");\n#endif\n    if (_glfw.x11.xshape.handle)\n    {\n        glfw_dlsym(_glfw.x11.xshape.QueryExtension, _glfw.x11.xshape.handle, \"XShapeQueryExtension\");\n        glfw_dlsym(_glfw.x11.xshape.ShapeCombineRegion, _glfw.x11.xshape.handle, \"XShapeCombineRegion\");\n        glfw_dlsym(_glfw.x11.xshape.QueryVersion, _glfw.x11.xshape.handle, \"XShapeQueryVersion\");\n\n        if (XShapeQueryExtension(_glfw.x11.display,\n            &_glfw.x11.xshape.errorBase,\n            &_glfw.x11.xshape.eventBase))\n        {\n            if (XShapeQueryVersion(_glfw.x11.display,\n                &_glfw.x11.xshape.major,\n                &_glfw.x11.xshape.minor))\n            {\n                _glfw.x11.xshape.available = true;\n            }\n        }\n    }\n\n    _glfw.x11.xkb.major = 1;\n    _glfw.x11.xkb.minor = 0;\n    _glfw.x11.xkb.available = XkbQueryExtension(_glfw.x11.display,\n            &_glfw.x11.xkb.majorOpcode,\n            &_glfw.x11.xkb.eventBase,\n            &_glfw.x11.xkb.errorBase,\n            &_glfw.x11.xkb.major,\n            &_glfw.x11.xkb.minor);\n\n    if (!_glfw.x11.xkb.available)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Failed to load Xkb extension\");\n        return false;\n    }\n    Bool supported;\n    if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported))\n    {\n        if (supported)\n            _glfw.x11.xkb.detectable = true;\n    }\n\n    if (!glfw_xkb_set_x11_events_mask()) return false;\n    if (!glfw_xkb_create_context(&_glfw.x11.xkb)) return false;\n    if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return false;\n    if (!glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL)) return false;\n\n    // String format atoms\n    _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, \"NULL\", False);\n    _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, \"UTF8_STRING\", False);\n    _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, \"ATOM_PAIR\", False);\n\n    // Custom selection property atom\n    _glfw.x11.GLFW_SELECTION =\n        XInternAtom(_glfw.x11.display, \"GLFW_SELECTION\", False);\n\n    // ICCCM standard clipboard atoms\n    _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, \"TARGETS\", False);\n    _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, \"MULTIPLE\", False);\n    _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, \"PRIMARY\", False);\n    _glfw.x11.INCR = XInternAtom(_glfw.x11.display, \"INCR\", False);\n    _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, \"CLIPBOARD\", False);\n\n    // Clipboard manager atoms\n    _glfw.x11.CLIPBOARD_MANAGER =\n        XInternAtom(_glfw.x11.display, \"CLIPBOARD_MANAGER\", False);\n    _glfw.x11.SAVE_TARGETS =\n        XInternAtom(_glfw.x11.display, \"SAVE_TARGETS\", False);\n\n    // Xdnd (drag and drop) atoms\n    _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, \"XdndAware\", False);\n    _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, \"XdndEnter\", False);\n    _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, \"XdndPosition\", False);\n    _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, \"XdndStatus\", False);\n    _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, \"XdndActionCopy\", False);\n    _glfw.x11.XdndActionMove = XInternAtom(_glfw.x11.display, \"XdndActionMove\", False);\n    _glfw.x11.XdndActionLink = XInternAtom(_glfw.x11.display, \"XdndActionLink\", False);\n    _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, \"XdndDrop\", False);\n    _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, \"XdndFinished\", False);\n    _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, \"XdndSelection\", False);\n    _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, \"XdndTypeList\", False);\n    _glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, \"XdndLeave\", False);\n    _glfw.x11.XdndProxy = XInternAtom(_glfw.x11.display, \"XdndProxy\", False);\n\n    // ICCCM, EWMH and Motif window property atoms\n    // These can be set safely even without WM support\n    // The EWMH atoms that require WM support are handled in detectEWMH\n    _glfw.x11.WM_PROTOCOLS =\n        XInternAtom(_glfw.x11.display, \"WM_PROTOCOLS\", False);\n    _glfw.x11.WM_STATE =\n        XInternAtom(_glfw.x11.display, \"WM_STATE\", False);\n    _glfw.x11.WM_DELETE_WINDOW =\n        XInternAtom(_glfw.x11.display, \"WM_DELETE_WINDOW\", False);\n    _glfw.x11.NET_SUPPORTED =\n        XInternAtom(_glfw.x11.display, \"_NET_SUPPORTED\", False);\n    _glfw.x11.NET_SUPPORTING_WM_CHECK =\n        XInternAtom(_glfw.x11.display, \"_NET_SUPPORTING_WM_CHECK\", False);\n    _glfw.x11.NET_WM_ICON =\n        XInternAtom(_glfw.x11.display, \"_NET_WM_ICON\", False);\n    _glfw.x11.NET_WM_PING =\n        XInternAtom(_glfw.x11.display, \"_NET_WM_PING\", False);\n    _glfw.x11.NET_WM_PID =\n        XInternAtom(_glfw.x11.display, \"_NET_WM_PID\", False);\n    _glfw.x11.NET_WM_NAME =\n        XInternAtom(_glfw.x11.display, \"_NET_WM_NAME\", False);\n    _glfw.x11.NET_WM_ICON_NAME =\n        XInternAtom(_glfw.x11.display, \"_NET_WM_ICON_NAME\", False);\n    _glfw.x11.NET_WM_BYPASS_COMPOSITOR =\n        XInternAtom(_glfw.x11.display, \"_NET_WM_BYPASS_COMPOSITOR\", False);\n    _glfw.x11.NET_WM_WINDOW_OPACITY =\n        XInternAtom(_glfw.x11.display, \"_NET_WM_WINDOW_OPACITY\", False);\n    _glfw.x11.MOTIF_WM_HINTS =\n        XInternAtom(_glfw.x11.display, \"_MOTIF_WM_HINTS\", False);\n\n    _glfw.x11.xi.LIBINPUT_SCROLL_METHOD_ENABLED = XInternAtom(_glfw.x11.display, \"libinput Scroll Method Enabled\", False);\n    _glfw.x11.xi.LIBINPUT_TAPPING = XInternAtom(_glfw.x11.display, \"libinput Tapping Enabled\", False);\n    read_xi_scroll_devices();\n\n    // Select XI_HierarchyChanged events to detect device add/remove\n    if (_glfw.x11.xi.available) {\n        XIEventMask em;\n        unsigned char mask[XIMaskLen(XI_HierarchyChanged)] = { 0 };\n\n        em.deviceid = XIAllDevices;\n        em.mask_len = sizeof(mask);\n        em.mask = mask;\n        XISetMask(mask, XI_HierarchyChanged);\n\n        XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);\n    }\n\n    // The compositing manager selection name contains the screen number\n    {\n        char name[32];\n        snprintf(name, sizeof(name), \"_NET_WM_CM_S%u\", _glfw.x11.screen);\n        _glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name, False);\n    }\n\n    // Detect whether an EWMH-conformant window manager is running\n    detectEWMH();\n\n    return true;\n}\n\n// Retrieve system content scale via folklore heuristics\n//\nvoid _glfwGetSystemContentScaleX11(float* xscale, float* yscale, bool bypass_cache)\n{\n    // Start by assuming the default X11 DPI\n    // NOTE: Some desktop environments (KDE) may remove the Xft.dpi field when it\n    //       would be set to 96, so assume that is the case if we cannot find it\n    float xdpi = 96.f, ydpi = 96.f;\n\n    // NOTE: Basing the scale on Xft.dpi where available should provide the most\n    //       consistent user experience (matches Qt, Gtk, etc), although not\n    //       always the most accurate one\n    char* rms = NULL;\n    char* owned_rms = NULL;\n\n    if (bypass_cache)\n    {\n        _glfwGetWindowPropertyX11(_glfw.x11.root,\n                                  _glfw.x11.RESOURCE_MANAGER,\n                                  XA_STRING,\n                                  (unsigned char**) &owned_rms);\n        rms = owned_rms;\n    } else {\n        rms = XResourceManagerString(_glfw.x11.display);\n    }\n\n    if (rms)\n    {\n        XrmDatabase db = XrmGetStringDatabase(rms);\n        if (db)\n        {\n            XrmValue value;\n            char* type = NULL;\n\n            if (XrmGetResource(db, \"Xft.dpi\", \"Xft.Dpi\", &type, &value))\n            {\n                if (type && strcmp(type, \"String\") == 0)\n                    xdpi = ydpi = (float)atof(value.addr);\n            }\n\n            XrmDestroyDatabase(db);\n        }\n        XFree(owned_rms);\n    }\n\n    *xscale = xdpi / 96.f;\n    *yscale = ydpi / 96.f;\n}\n\n// Create a blank cursor for hidden and disabled cursor modes\n//\nstatic Cursor createHiddenCursor(void)\n{\n    unsigned char pixels[16 * 16 * 4] = { 0 };\n    GLFWimage image = { 16, 16, pixels };\n    return _glfwCreateCursorX11(&image, 0, 0);\n}\n\n// Create a helper window for IPC\n//\nstatic Window createHelperWindow(void)\n{\n    XSetWindowAttributes wa;\n    wa.event_mask = PropertyChangeMask;\n\n    return XCreateWindow(_glfw.x11.display, _glfw.x11.root,\n                         0, 0, 1, 1, 0, 0,\n                         InputOnly,\n                         DefaultVisual(_glfw.x11.display, _glfw.x11.screen),\n                         CWEventMask, &wa);\n}\n\n// X error handler\n//\nstatic int errorHandler(Display *display, XErrorEvent* event)\n{\n    if (_glfw.x11.display != display)\n        return 0;\n\n    _glfw.x11.errorCode = event->error_code;\n    return 0;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Sets the X error handler callback\n//\nvoid _glfwGrabErrorHandlerX11(void)\n{\n    _glfw.x11.errorCode = Success;\n    XSetErrorHandler(errorHandler);\n}\n\n// Clears the X error handler callback\n//\nvoid _glfwReleaseErrorHandlerX11(void)\n{\n    // Synchronize to make sure all commands are processed\n    XSync(_glfw.x11.display, False);\n    XSetErrorHandler(NULL);\n}\n\n// Reports the specified error, appending information about the last X error\n//\nvoid _glfwInputErrorX11(int error, const char* message)\n{\n    char buffer[_GLFW_MESSAGE_SIZE];\n    XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode,\n                  buffer, sizeof(buffer));\n\n    _glfwInputError(error, \"%s: %s\", message, buffer);\n}\n\n// Creates a native cursor object from the specified image and hotspot\n//\nCursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)\n{\n    int i;\n    Cursor cursor;\n\n    if (!_glfw.x11.xcursor.handle)\n        return None;\n\n    XcursorImage* native = XcursorImageCreate(image->width, image->height);\n    if (native == NULL)\n        return None;\n\n    native->xhot = xhot;\n    native->yhot = yhot;\n\n    unsigned char* source = (unsigned char*) image->pixels;\n    XcursorPixel* target = native->pixels;\n\n    for (i = 0;  i < image->width * image->height;  i++, target++, source += 4)\n    {\n        unsigned int alpha = source[3];\n\n        *target = (alpha << 24) |\n                  ((unsigned char) ((source[0] * alpha) / 255) << 16) |\n                  ((unsigned char) ((source[1] * alpha) / 255) <<  8) |\n                  ((unsigned char) ((source[2] * alpha) / 255) <<  0);\n    }\n\n    cursor = XcursorImageLoadCursor(_glfw.x11.display, native);\n    XcursorImageDestroy(native);\n\n    return cursor;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {\n    return glfw_current_system_color_theme(query_if_unintialized);\n}\n\nvoid _glfwPlatformInputColorScheme(GLFWColorScheme appearance UNUSED) { }\n\nint _glfwPlatformInit(bool *supports_window_occlusion)\n{\n    *supports_window_occlusion = false;\n    XInitThreads();\n    XrmInitialize();\n\n    _glfw.x11.display = XOpenDisplay(NULL);\n    if (!_glfw.x11.display)\n    {\n        const char* display = getenv(\"DISPLAY\");\n        if (display)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"X11: Failed to open display %s\", display);\n        }\n        else\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"X11: The DISPLAY environment variable is missing\");\n        }\n\n        return false;\n    }\n\n    if (!initPollData(&_glfw.x11.eventLoopData, ConnectionNumber(_glfw.x11.display))) {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"X11: Failed to initialize event loop data\");\n    }\n    glfw_dbus_init(&_glfw.x11.dbus, &_glfw.x11.eventLoopData);\n    glfw_initialize_desktop_settings();  // needed for color scheme change notification\n\n    _glfw.x11.screen = DefaultScreen(_glfw.x11.display);\n    _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);\n    _glfw.x11.context = XUniqueContext();\n    _glfw.x11.RESOURCE_MANAGER = XInternAtom(_glfw.x11.display, \"RESOURCE_MANAGER\", True);\n    _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION = None;\n    XSelectInput(_glfw.x11.display, _glfw.x11.root, PropertyChangeMask);\n\n    _glfwGetSystemContentScaleX11(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY, false);\n\n    if (!initExtensions())\n        return false;\n\n    _glfw.x11.helperWindowHandle = createHelperWindow();\n    _glfw.x11.hiddenCursorHandle = createHiddenCursor();\n\n    _glfwPollMonitorsX11();\n    return true;\n}\n\nvoid _glfwPlatformTerminate(void)\n{\n    removeAllTimers(&_glfw.x11.eventLoopData);\n    if (_glfw.x11.helperWindowHandle)\n    {\n        if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==\n            _glfw.x11.helperWindowHandle)\n        {\n            _glfwPushSelectionToManagerX11();\n        }\n\n        XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle);\n        _glfw.x11.helperWindowHandle = None;\n    }\n\n    if (_glfw.x11.hiddenCursorHandle)\n    {\n        XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle);\n        _glfw.x11.hiddenCursorHandle = (Cursor) 0;\n    }\n\n    glfw_xkb_release(&_glfw.x11.xkb);\n    glfw_dbus_terminate(&_glfw.x11.dbus);\n    if (_glfw.x11.mime_atoms.array) {\n        for (size_t i = 0; i < _glfw.x11.mime_atoms.sz; i++) {\n            free((void*)_glfw.x11.mime_atoms.array[i].mime);\n        }\n        free(_glfw.x11.mime_atoms.array);\n    }\n    if (_glfw.x11.clipboard_atoms.array) { free(_glfw.x11.clipboard_atoms.array); }\n    if (_glfw.x11.primary_atoms.array) { free(_glfw.x11.primary_atoms.array); }\n\n    free_dnd_data();\n    if (_glfw.x11.display)\n    {\n        XCloseDisplay(_glfw.x11.display);\n        _glfw.x11.display = NULL;\n        _glfw.x11.eventLoopData.fds[0].fd = -1;\n    }\n\n    if (_glfw.x11.xcursor.handle)\n    {\n        _glfw_dlclose(_glfw.x11.xcursor.handle);\n        _glfw.x11.xcursor.handle = NULL;\n    }\n\n    if (_glfw.x11.randr.handle)\n    {\n        _glfw_dlclose(_glfw.x11.randr.handle);\n        _glfw.x11.randr.handle = NULL;\n    }\n\n    if (_glfw.x11.xinerama.handle)\n    {\n        _glfw_dlclose(_glfw.x11.xinerama.handle);\n        _glfw.x11.xinerama.handle = NULL;\n    }\n\n    if (_glfw.x11.xrender.handle)\n    {\n        _glfw_dlclose(_glfw.x11.xrender.handle);\n        _glfw.x11.xrender.handle = NULL;\n    }\n\n    if (_glfw.x11.vidmode.handle)\n    {\n        _glfw_dlclose(_glfw.x11.vidmode.handle);\n        _glfw.x11.vidmode.handle = NULL;\n    }\n\n    if (_glfw.x11.xi.handle)\n    {\n        _glfw_dlclose(_glfw.x11.xi.handle);\n        _glfw.x11.xi.handle = NULL;\n    }\n\n    // NOTE: These need to be unloaded after XCloseDisplay, as they register\n    //       cleanup callbacks that get called by that function\n    _glfwTerminateEGL();\n    _glfwTerminateGLX();\n\n    finalizePollData(&_glfw.x11.eventLoopData);\n}\n\nconst char* _glfwPlatformGetVersionString(void)\n{\n    return _GLFW_VERSION_NUMBER \" X11 GLX EGL OSMesa\"\n#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)\n        \" clock_gettime\"\n#else\n        \" gettimeofday\"\n#endif\n#if defined(__linux__)\n        \" evdev\"\n#endif\n#if defined(_GLFW_BUILD_DLL)\n        \" shared\"\n#endif\n        ;\n}\n\n#include \"main_loop.h\"\n"
  },
  {
    "path": "glfw/x11_monitor.c",
    "content": "//========================================================================\n// GLFW 3.4 X11 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#include \"internal.h\"\n\n#include <limits.h>\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n#include <assert.h>\n\n\n// Check whether the display mode should be included in enumeration\n//\nstatic bool modeIsGood(const XRRModeInfo* mi)\n{\n    return (mi->modeFlags & RR_Interlace) == 0;\n}\n\n// Calculates the refresh rate, in Hz, from the specified RandR mode info\n//\nstatic int calculateRefreshRate(const XRRModeInfo* mi)\n{\n    if (mi->hTotal && mi->vTotal)\n        return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal));\n    else\n        return 0;\n}\n\n// Returns the mode info for a RandR mode XID\n//\nstatic const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id)\n{\n    for (int i = 0;  i < sr->nmode;  i++)\n    {\n        if (sr->modes[i].id == id)\n            return sr->modes + i;\n    }\n\n    return NULL;\n}\n\n// Convert RandR mode info to GLFW video mode\n//\nstatic GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi,\n                                       const XRRCrtcInfo* ci)\n{\n    GLFWvidmode mode;\n\n    if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)\n    {\n        mode.width  = mi->height;\n        mode.height = mi->width;\n    }\n    else\n    {\n        mode.width  = mi->width;\n        mode.height = mi->height;\n    }\n\n    mode.refreshRate = calculateRefreshRate(mi);\n\n    _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),\n                  &mode.redBits, &mode.greenBits, &mode.blueBits);\n\n    return mode;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Poll for changes in the set of connected monitors\n//\nvoid _glfwPollMonitorsX11(void)\n{\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)\n    {\n        int disconnectedCount, screenCount = 0;\n        _GLFWmonitor** disconnected = NULL;\n        XineramaScreenInfo* screens = NULL;\n        XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display,\n                                                              _glfw.x11.root);\n        RROutput primary = XRRGetOutputPrimary(_glfw.x11.display,\n                                               _glfw.x11.root);\n\n        if (_glfw.x11.xinerama.available)\n            screens = XineramaQueryScreens(_glfw.x11.display, &screenCount);\n\n        disconnectedCount = _glfw.monitorCount;\n        if (disconnectedCount)\n        {\n            disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));\n            memcpy(disconnected,\n                   _glfw.monitors,\n                   _glfw.monitorCount * sizeof(_GLFWmonitor*));\n        }\n\n        for (int i = 0;  i < sr->noutput;  i++)\n        {\n            int j, type, widthMM, heightMM;\n\n            XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]);\n            if (oi->connection != RR_Connected || oi->crtc == None)\n            {\n                XRRFreeOutputInfo(oi);\n                continue;\n            }\n\n            for (j = 0;  j < disconnectedCount;  j++)\n            {\n                if (disconnected[j] &&\n                    disconnected[j]->x11.output == sr->outputs[i])\n                {\n                    disconnected[j] = NULL;\n                    break;\n                }\n            }\n\n            if (j < disconnectedCount)\n            {\n                XRRFreeOutputInfo(oi);\n                continue;\n            }\n\n            XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc);\n            if (!ci)\n            {\n                XRRFreeOutputInfo(oi);\n                continue;\n            }\n            if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)\n            {\n                widthMM  = oi->mm_height;\n                heightMM = oi->mm_width;\n            }\n            else\n            {\n                widthMM  = oi->mm_width;\n                heightMM = oi->mm_height;\n            }\n\n            if (widthMM <= 0 || heightMM <= 0)\n            {\n                // HACK: If RandR does not provide a physical size, assume the\n                //       X11 default 96 DPI and calculate from the CRTC viewport\n                // NOTE: These members are affected by rotation, unlike the mode\n                //       info and output info members\n                widthMM  = (int) (ci->width * 25.4f / 96.f);\n                heightMM = (int) (ci->height * 25.4f / 96.f);\n            }\n\n            _GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM);\n            monitor->x11.output = sr->outputs[i];\n            monitor->x11.crtc   = oi->crtc;\n\n            for (j = 0;  j < screenCount;  j++)\n            {\n                if (screens[j].x_org == ci->x &&\n                    screens[j].y_org == ci->y &&\n                    screens[j].width == (short int)ci->width &&\n                    screens[j].height == (short int)ci->height)\n                {\n                    monitor->x11.index = j;\n                    break;\n                }\n            }\n\n            if (monitor->x11.output == primary)\n                type = _GLFW_INSERT_FIRST;\n            else\n                type = _GLFW_INSERT_LAST;\n\n            _glfwInputMonitor(monitor, GLFW_CONNECTED, type);\n\n            XRRFreeOutputInfo(oi);\n            XRRFreeCrtcInfo(ci);\n        }\n\n        XRRFreeScreenResources(sr);\n\n        if (screens)\n            XFree(screens);\n\n        for (int i = 0;  i < disconnectedCount;  i++)\n        {\n            if (disconnected[i])\n                _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);\n        }\n\n        free(disconnected);\n    }\n    else\n    {\n        const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen);\n        const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen);\n\n        _glfwInputMonitor(_glfwAllocMonitor(\"Display\", widthMM, heightMM),\n                          GLFW_CONNECTED,\n                          _GLFW_INSERT_FIRST);\n    }\n}\n\n// Set the current video mode for the specified monitor\n//\nvoid _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)\n{\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)\n    {\n        GLFWvidmode current;\n        RRMode native = None;\n\n        const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);\n        _glfwPlatformGetVideoMode(monitor, &current);\n        if (_glfwCompareVideoModes(&current, best) == 0)\n            return;\n\n        XRRScreenResources* sr =\n            XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);\n        XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);\n        XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);\n\n        for (int i = 0;  i < oi->nmode;  i++)\n        {\n            const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);\n            if (!modeIsGood(mi))\n                continue;\n\n            const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);\n            if (_glfwCompareVideoModes(best, &mode) == 0)\n            {\n                native = mi->id;\n                break;\n            }\n        }\n\n        if (native)\n        {\n            if (monitor->x11.oldMode == None)\n                monitor->x11.oldMode = ci->mode;\n\n            XRRSetCrtcConfig(_glfw.x11.display,\n                             sr, monitor->x11.crtc,\n                             CurrentTime,\n                             ci->x, ci->y,\n                             native,\n                             ci->rotation,\n                             ci->outputs,\n                             ci->noutput);\n        }\n\n        XRRFreeOutputInfo(oi);\n        XRRFreeCrtcInfo(ci);\n        XRRFreeScreenResources(sr);\n    }\n}\n\n// Restore the saved (original) video mode for the specified monitor\n//\nvoid _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)\n{\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)\n    {\n        if (monitor->x11.oldMode == None)\n            return;\n\n        XRRScreenResources* sr =\n            XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);\n        XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);\n\n        XRRSetCrtcConfig(_glfw.x11.display,\n                         sr, monitor->x11.crtc,\n                         CurrentTime,\n                         ci->x, ci->y,\n                         monitor->x11.oldMode,\n                         ci->rotation,\n                         ci->outputs,\n                         ci->noutput);\n\n        XRRFreeCrtcInfo(ci);\n        XRRFreeScreenResources(sr);\n\n        monitor->x11.oldMode = None;\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nvoid _glfwPlatformFreeMonitor(_GLFWmonitor* monitor UNUSED)\n{\n}\n\nvoid _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)\n{\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)\n    {\n        XRRScreenResources* sr =\n            XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);\n        XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);\n\n        if (ci)\n        {\n            if (xpos)\n                *xpos = ci->x;\n            if (ypos)\n                *ypos = ci->y;\n\n            XRRFreeCrtcInfo(ci);\n        }\n\n        XRRFreeScreenResources(sr);\n    }\n}\n\nvoid _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor UNUSED,\n                                         float* xscale, float* yscale)\n{\n    if (xscale)\n        *xscale = _glfw.x11.contentScaleX;\n    if (yscale)\n        *yscale = _glfw.x11.contentScaleY;\n}\n\nMonitorGeometry\n_glfwPlatformGetMonitorGeometry(_GLFWmonitor *monitor) {\n    MonitorGeometry ans = {0};\n    if (!monitor) return ans;\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) {\n        XRRScreenResources* sr =\n            XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);\n        XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);\n\n        ans.full.x = ci->x;\n        ans.full.y = ci->y;\n\n        const XRRModeInfo* mi = getModeInfo(sr, ci->mode);\n\n        if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) {\n            ans.full.width  = mi->height;\n            ans.full.height = mi->width;\n        } else {\n            ans.full.width  = mi->width;\n            ans.full.height = mi->height;\n        }\n\n        XRRFreeCrtcInfo(ci);\n        XRRFreeScreenResources(sr);\n    } else {\n        ans.full.width  = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);\n        ans.full.height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);\n    }\n    ans.workarea = *(&ans.full);\n\n    if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) {\n        Atom* extents = NULL;\n        Atom* desktop = NULL;\n        const unsigned long extentCount = _glfwGetWindowPropertyX11(\n            _glfw.x11.root, _glfw.x11.NET_WORKAREA, XA_CARDINAL, (unsigned char**) &extents);\n\n        if (_glfwGetWindowPropertyX11(_glfw.x11.root, _glfw.x11.NET_CURRENT_DESKTOP, XA_CARDINAL, (unsigned char**) &desktop) > 0) {\n            if (extentCount >= 4 && *desktop < extentCount / 4) {\n                const int globalX = extents[*desktop * 4 + 0];\n                const int globalY = extents[*desktop * 4 + 1];\n                const int globalWidth  = extents[*desktop * 4 + 2];\n                const int globalHeight = extents[*desktop * 4 + 3];\n\n                if (ans.workarea.x < globalX) {\n                    ans.workarea.width -= globalX - ans.workarea.x;\n                    ans.workarea.x = globalX;\n                }\n                if (ans.workarea.y < globalY) {\n                    ans.workarea.height -= globalY - ans.workarea.y;\n                    ans.workarea.y = globalY;\n                }\n                if (ans.workarea.x + ans.workarea.width > globalX + globalWidth)\n                    ans.workarea.width = globalX - ans.workarea.x + globalWidth;\n                if (ans.workarea.y + ans.workarea.height > globalY + globalHeight)\n                    ans.workarea.height = globalY - ans.workarea.y + globalHeight;\n            }\n        }\n        if (extents) XFree(extents);\n        if (desktop) XFree(desktop);\n    }\n    return ans;\n}\n\nvoid _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) {\n    MonitorGeometry ans = _glfwPlatformGetMonitorGeometry(monitor);\n    if (xpos)\n        *xpos = ans.workarea.x;\n    if (ypos)\n        *ypos = ans.workarea.y;\n    if (width)\n        *width = ans.workarea.width;\n    if (height)\n        *height = ans.workarea.height;\n}\n\nGLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)\n{\n    GLFWvidmode* result;\n\n    *count = 0;\n\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)\n    {\n        XRRScreenResources* sr =\n            XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);\n        XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);\n        XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);\n\n        result = calloc(oi->nmode, sizeof(GLFWvidmode));\n\n        for (int i = 0;  i < oi->nmode;  i++)\n        {\n            const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);\n            if (!modeIsGood(mi))\n                continue;\n\n            const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);\n            int j;\n\n            for (j = 0;  j < *count;  j++)\n            {\n                if (_glfwCompareVideoModes(result + j, &mode) == 0)\n                    break;\n            }\n\n            // Skip duplicate modes\n            if (j < *count)\n                continue;\n\n            (*count)++;\n            result[*count - 1] = mode;\n        }\n\n        XRRFreeOutputInfo(oi);\n        XRRFreeCrtcInfo(ci);\n        XRRFreeScreenResources(sr);\n    }\n    else\n    {\n        *count = 1;\n        result = calloc(1, sizeof(GLFWvidmode));\n        _glfwPlatformGetVideoMode(monitor, result);\n    }\n\n    return result;\n}\n\nbool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {\n    bool ok = false;\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)\n    {\n        XRRScreenResources* sr =\n            XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);\n        XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);\n\n        if (ci)\n        {\n            const XRRModeInfo* mi = getModeInfo(sr, ci->mode);\n            if (mi) { // mi can be NULL if the monitor has been disconnected\n                *mode = vidmodeFromModeInfo(mi, ci);\n                ok = true;\n            }\n\n            XRRFreeCrtcInfo(ci);\n        }\n\n        XRRFreeScreenResources(sr);\n    }\n    else\n    {\n        ok = true;\n        mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);\n        mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);\n        mode->refreshRate = 0;\n\n        _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),\n                      &mode->redBits, &mode->greenBits, &mode->blueBits);\n    }\n    return ok;\n}\n\nbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)\n{\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)\n    {\n        const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display,\n                                                monitor->x11.crtc);\n        XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display,\n                                              monitor->x11.crtc);\n\n        _glfwAllocGammaArrays(ramp, size);\n\n        memcpy(ramp->red,   gamma->red,   size * sizeof(unsigned short));\n        memcpy(ramp->green, gamma->green, size * sizeof(unsigned short));\n        memcpy(ramp->blue,  gamma->blue,  size * sizeof(unsigned short));\n\n        XRRFreeGamma(gamma);\n        return true;\n    }\n    else if (_glfw.x11.vidmode.available)\n    {\n        int size;\n        XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size);\n\n        _glfwAllocGammaArrays(ramp, size);\n\n        XF86VidModeGetGammaRamp(_glfw.x11.display,\n                                _glfw.x11.screen,\n                                ramp->size, ramp->red, ramp->green, ramp->blue);\n        return true;\n    }\n    else\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"X11: Gamma ramp access not supported by server\");\n        return false;\n    }\n}\n\nvoid _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)\n{\n    if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)\n    {\n        if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != (int)ramp->size)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"X11: Gamma ramp size must match current ramp size\");\n            return;\n        }\n\n        XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size);\n\n        memcpy(gamma->red,   ramp->red,   ramp->size * sizeof(unsigned short));\n        memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short));\n        memcpy(gamma->blue,  ramp->blue,  ramp->size * sizeof(unsigned short));\n\n        XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma);\n        XRRFreeGamma(gamma);\n    }\n    else if (_glfw.x11.vidmode.available)\n    {\n        XF86VidModeSetGammaRamp(_glfw.x11.display,\n                                _glfw.x11.screen,\n                                ramp->size,\n                                (unsigned short*) ramp->red,\n                                (unsigned short*) ramp->green,\n                                (unsigned short*) ramp->blue);\n    }\n    else\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"X11: Gamma ramp access not supported by server\");\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(None);\n    return monitor->x11.crtc;\n}\n\nGLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle)\n{\n    _GLFWmonitor* monitor = (_GLFWmonitor*) handle;\n    assert(monitor != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(None);\n    return monitor->x11.output;\n}\n"
  },
  {
    "path": "glfw/x11_platform.h",
    "content": "//========================================================================\n// GLFW 3.4 X11 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#include <unistd.h>\n#include <signal.h>\n#include <stdint.h>\n#include <dlfcn.h>\n#include <poll.h>\n\n#include <X11/Xlib.h>\n#include <X11/keysym.h>\n#include <X11/Xatom.h>\n#include <X11/Xcursor/Xcursor.h>\n\n// The xcb library is needed to work with libxkb\n#include <X11/Xlib-xcb.h>\n\n// The XRandR extension provides mode setting and gamma control\n#include <X11/extensions/Xrandr.h>\n\n// The Xkb extension provides improved keyboard support\n#include <X11/XKBlib.h>\n\n// The Xinerama extension provides legacy monitor indices\n#include <X11/extensions/Xinerama.h>\n\n// The XInput extension provides raw mouse motion input\n#include <X11/extensions/XInput2.h>\n\n// The Shape extension provides custom window shapes\n#include <X11/extensions/shape.h>\n\n// The libxkb library is used for improved keyboard support\n#include \"xkb_glfw.h\"\n#include \"backend_utils.h\"\n\ntypedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);\ntypedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);\ntypedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);\ntypedef void (* PFN_XRRFreeOutputInfo)(XRROutputInfo*);\ntypedef void (* PFN_XRRFreeScreenResources)(XRRScreenResources*);\ntypedef XRRCrtcGamma* (* PFN_XRRGetCrtcGamma)(Display*,RRCrtc);\ntypedef int (* PFN_XRRGetCrtcGammaSize)(Display*,RRCrtc);\ntypedef XRRCrtcInfo* (* PFN_XRRGetCrtcInfo) (Display*,XRRScreenResources*,RRCrtc);\ntypedef XRROutputInfo* (* PFN_XRRGetOutputInfo)(Display*,XRRScreenResources*,RROutput);\ntypedef RROutput (* PFN_XRRGetOutputPrimary)(Display*,Window);\ntypedef XRRScreenResources* (* PFN_XRRGetScreenResourcesCurrent)(Display*,Window);\ntypedef Bool (* PFN_XRRQueryExtension)(Display*,int*,int*);\ntypedef Status (* PFN_XRRQueryVersion)(Display*,int*,int*);\ntypedef void (* PFN_XRRSelectInput)(Display*,Window,int);\ntypedef Status (* PFN_XRRSetCrtcConfig)(Display*,XRRScreenResources*,RRCrtc,Time,int,int,RRMode,Rotation,RROutput*,int);\ntypedef void (* PFN_XRRSetCrtcGamma)(Display*,RRCrtc,XRRCrtcGamma*);\ntypedef int (* PFN_XRRUpdateConfiguration)(XEvent*);\n#define XRRAllocGamma _glfw.x11.randr.AllocGamma\n#define XRRFreeCrtcInfo _glfw.x11.randr.FreeCrtcInfo\n#define XRRFreeGamma _glfw.x11.randr.FreeGamma\n#define XRRFreeOutputInfo _glfw.x11.randr.FreeOutputInfo\n#define XRRFreeScreenResources _glfw.x11.randr.FreeScreenResources\n#define XRRGetCrtcGamma _glfw.x11.randr.GetCrtcGamma\n#define XRRGetCrtcGammaSize _glfw.x11.randr.GetCrtcGammaSize\n#define XRRGetCrtcInfo _glfw.x11.randr.GetCrtcInfo\n#define XRRGetOutputInfo _glfw.x11.randr.GetOutputInfo\n#define XRRGetOutputPrimary _glfw.x11.randr.GetOutputPrimary\n#define XRRGetScreenResourcesCurrent _glfw.x11.randr.GetScreenResourcesCurrent\n#define XRRQueryExtension _glfw.x11.randr.QueryExtension\n#define XRRQueryVersion _glfw.x11.randr.QueryVersion\n#define XRRSelectInput _glfw.x11.randr.SelectInput\n#define XRRSetCrtcConfig _glfw.x11.randr.SetCrtcConfig\n#define XRRSetCrtcGamma _glfw.x11.randr.SetCrtcGamma\n#define XRRUpdateConfiguration _glfw.x11.randr.UpdateConfiguration\n\ntypedef XcursorImage* (* PFN_XcursorImageCreate)(int,int);\ntypedef void (* PFN_XcursorImageDestroy)(XcursorImage*);\ntypedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*);\n#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate\n#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy\n#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor\n\ntypedef Bool (* PFN_XineramaIsActive)(Display*);\ntypedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*);\ntypedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*);\n#define XineramaIsActive _glfw.x11.xinerama.IsActive\n#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension\n#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens\n\ntypedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*);\ntypedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*);\ntypedef Bool (* PFN_XF86VidModeSetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*);\ntypedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*);\n#define XF86VidModeQueryExtension _glfw.x11.vidmode.QueryExtension\n#define XF86VidModeGetGammaRamp _glfw.x11.vidmode.GetGammaRamp\n#define XF86VidModeSetGammaRamp _glfw.x11.vidmode.SetGammaRamp\n#define XF86VidModeGetGammaRampSize _glfw.x11.vidmode.GetGammaRampSize\n\ntypedef Status (* PFN_XIQueryVersion)(Display*,int*,int*);\ntypedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);\ntypedef XIDeviceInfo* (* PFN_XIQueryDevice)(Display*,int,int*);\ntypedef void (* PFN_XIFreeDeviceInfo)(XIDeviceInfo*);\ntypedef Status (* PFN_XIGetProperty)(Display *dpy, int deviceid, Atom property, long offset, long length, Bool delete_property, Atom type, Atom *type_return, int *format_return, unsigned long *num_items_return, unsigned long *bytes_after_return, unsigned char **data);\n\n#define XIQueryVersion _glfw.x11.xi.QueryVersion\n#define XISelectEvents _glfw.x11.xi.SelectEvents\n#define XIQueryDevice _glfw.x11.xi.QueryDevice\n#define XIFreeDeviceInfo _glfw.x11.xi.FreeDeviceInfo\n#define XIGetProperty _glfw.x11.xi.GetProperty\n\ntypedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*);\ntypedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*);\ntypedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*);\n#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension\n#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion\n#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat\n\ntypedef Bool (* PFN_XShapeQueryExtension)(Display*,int*,int*);\ntypedef Status (* PFN_XShapeQueryVersion)(Display*dpy,int*,int*);\ntypedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);\ntypedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);\n\n#define XShapeQueryExtension _glfw.x11.xshape.QueryExtension\n#define XShapeQueryVersion _glfw.x11.xshape.QueryVersion\n#define XShapeCombineRegion _glfw.x11.xshape.ShapeCombineRegion\n#define XShapeCombineMask _glfw.x11.xshape.ShapeCombineMask\n\ntypedef VkFlags VkXlibSurfaceCreateFlagsKHR;\ntypedef VkFlags VkXcbSurfaceCreateFlagsKHR;\n\ntypedef struct VkXlibSurfaceCreateInfoKHR\n{\n    VkStructureType             sType;\n    const void*                 pNext;\n    VkXlibSurfaceCreateFlagsKHR flags;\n    Display*                    dpy;\n    Window                      window;\n} VkXlibSurfaceCreateInfoKHR;\n\ntypedef struct VkXcbSurfaceCreateInfoKHR\n{\n    VkStructureType             sType;\n    const void*                 pNext;\n    VkXcbSurfaceCreateFlagsKHR  flags;\n    xcb_connection_t*           connection;\n    xcb_window_t                window;\n} VkXcbSurfaceCreateInfoKHR;\n\ntypedef VkResult (APIENTRY *PFN_vkCreateXlibSurfaceKHR)(VkInstance,const VkXlibSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*);\ntypedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice,uint32_t,Display*,VisualID);\ntypedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*);\ntypedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t);\n\n#include \"posix_thread.h\"\n#include \"glx_context.h\"\n#if defined(__linux__)\n#include \"linux_joystick.h\"\n#else\n#include \"null_joystick.h\"\n#endif\n\n#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)\n#define _glfw_dlclose(handle) dlclose(handle)\n#define _glfw_dlsym(handle, name) dlsym(handle, name)\n\n#define _GLFW_PLATFORM_WINDOW_STATE         _GLFWwindowX11  x11\n#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11\n#define _GLFW_PLATFORM_MONITOR_STATE        _GLFWmonitorX11 x11\n#define _GLFW_PLATFORM_CURSOR_STATE         _GLFWcursorX11  x11\n\n\n// X11-specific per-window data\n//\ntypedef struct _GLFWwindowX11\n{\n    Colormap        colormap;\n    Window          handle;\n    Window          parent;\n\n    bool            iconified;\n    bool            maximized;\n\n    // Whether the visual supports framebuffer transparency\n    bool            transparent;\n\n    // Cached position and size used to filter out duplicate events\n    int             width, height;\n    int             xpos, ypos;\n\n    // The last received cursor position, regardless of source\n    int             lastCursorPosX, lastCursorPosY;\n    // The last position the cursor was warped to by GLFW\n    int             warpCursorPosX, warpCursorPosY;\n\n    // XI2 smooth scrolling - track valuator values per window\n    struct {\n        double      verticalValue;\n        double      horizontalValue;\n    } smoothScroll;\n\n    struct {\n        bool is_active;\n        GLFWLayerShellConfig config;\n    } layer_shell;\n} _GLFWwindowX11;\n\ntypedef struct MimeAtom {\n        Atom atom;\n        const char* mime;\n} MimeAtom;\n\ntypedef struct AtomArray {\n    MimeAtom *array;\n    size_t sz, capacity;\n} AtomArray;\n\ntypedef struct XIScrollValuator {\n    double increment, value, min, max; int number, resolution, mode; bool is_vertical, initialized;\n} XIScrollValuator;\n\ntypedef struct XIScrollDevice {\n    bool is_finger_based;\n    bool type_detected;\n    int deviceid, sourceid;\n    XIScrollValuator valuators[8];\n    unsigned num_valuators;\n    char name[32];\n    unsigned num_events;\n    GLFWOffsetType offset_type;\n} XIScrollDevice;\n\ntypedef struct XdndSelectionRequest {\n    char *mime;\n    bool inflight, got_data;\n    unsigned char *data;\n    size_t offset, size;\n} XdndSelectionRequest;\n\n// X11-specific global data\n//\ntypedef struct _GLFWlibraryX11\n{\n    Display*        display;\n    int             screen;\n    Window          root;\n\n    // System content scale\n    float           contentScaleX, contentScaleY;\n    // Helper window for IPC\n    Window          helperWindowHandle;\n    // Invisible cursor for hidden cursor mode\n    Cursor          hiddenCursorHandle;\n    // Context for mapping window XIDs to _GLFWwindow pointers\n    XContext        context;\n    // Most recent error code received by X error handler\n    int             errorCode;\n    // Where to place the cursor when re-enabled\n    double          restoreCursorPosX, restoreCursorPosY;\n    // The window whose disabled cursor mode is active\n    _GLFWwindow*    disabledCursorWindow;\n\n    // Window manager atoms\n    Atom            NET_SUPPORTED;\n    Atom            NET_SUPPORTING_WM_CHECK;\n    Atom            WM_PROTOCOLS;\n    Atom            WM_STATE;\n    Atom            WM_DELETE_WINDOW;\n    Atom            NET_WM_NAME;\n    Atom            NET_WM_ALLOWED_ACTIONS, NET_WM_ACTION_MOVE, NET_WM_ACTION_RESIZE, NET_WM_ACTION_MINIMIZE, NET_WM_ACTION_SHADE, NET_WM_ACTION_STICK, NET_WM_ACTION_MAXIMIZE_HORZ, NET_WM_ACTION_MAXIMIZE_VERT, NET_WM_ACTION_FULLSCREEN, NET_WM_ACTION_CHANGE_DESKTOP, NET_WM_ACTION_CLOSE, NET_WM_ACTION_ABOVE, NET_WM_ACTION_BELOW, NET_WM_ACTION_ABOVE_BELOW;\n    Atom            NET_WM_ICON_NAME;\n    Atom            NET_WM_ICON;\n    Atom            NET_WM_PID;\n    Atom            NET_WM_PING;\n    Atom            NET_WM_WINDOW_TYPE, NET_WM_WINDOW_TYPE_NORMAL, NET_WM_WINDOW_TYPE_DOCK, NET_WM_WINDOW_TYPE_DESKTOP, NET_WM_WINDOW_TYPE_UTILITY, NET_WM_WINDOW_TYPE_SPLASH, NET_WM_WINDOW_TYPE_DIALOG, NET_WM_WINDOW_TYPE_MENU, NET_WM_WINDOW_TYPE_NOTIFICATION;\n    Atom            NET_WM_STATE;\n    Atom            NET_WM_STATE_ABOVE;\n    Atom            NET_WM_STATE_BELOW;\n    Atom            NET_WM_STATE_FULLSCREEN;\n    Atom            NET_WM_STATE_MAXIMIZED_VERT;\n    Atom            NET_WM_STATE_MAXIMIZED_HORZ;\n    Atom            NET_WM_STATE_DEMANDS_ATTENTION;\n    Atom            NET_WM_STATE_SKIP_TASKBAR;\n    Atom            NET_WM_STATE_SKIP_PAGER;\n    Atom            NET_WM_STATE_STICKY;\n    Atom            NET_WM_BYPASS_COMPOSITOR;\n    Atom            NET_WM_FULLSCREEN_MONITORS;\n    Atom            NET_WM_WINDOW_OPACITY;\n    Atom            NET_WM_CM_Sx;\n    Atom            NET_WORKAREA;\n    Atom            NET_CURRENT_DESKTOP;\n    Atom            NET_ACTIVE_WINDOW;\n    Atom            NET_FRAME_EXTENTS;\n    Atom            NET_REQUEST_FRAME_EXTENTS;\n    Atom            NET_WM_STRUT_PARTIAL;\n    Atom            MOTIF_WM_HINTS;\n\n    // Xdnd (drag and drop) atoms\n    Atom            XdndAware;\n    Atom            XdndEnter;\n    Atom            XdndPosition;\n    Atom            XdndStatus;\n    Atom            XdndActionCopy;\n    Atom            XdndActionMove;\n    Atom            XdndActionLink;\n    Atom            XdndDrop;\n    Atom            XdndFinished;\n    Atom            XdndSelection;\n    Atom            XdndTypeList;\n    Atom            XdndLeave;\n    Atom            XdndProxy;\n\n    // Selection (clipboard) atoms\n    Atom            TARGETS;\n    Atom            MULTIPLE;\n    Atom            INCR;\n    Atom            CLIPBOARD;\n    Atom            PRIMARY;\n    Atom            CLIPBOARD_MANAGER;\n    Atom            SAVE_TARGETS;\n    Atom            NULL_;\n    Atom            UTF8_STRING;\n    Atom            COMPOUND_STRING;\n    Atom            ATOM_PAIR;\n    Atom            GLFW_SELECTION;\n\n    // XRM database atom\n    Atom            RESOURCE_MANAGER;\n    // KDE window blur\n    Atom _KDE_NET_WM_BLUR_BEHIND_REGION;\n    // Atoms for MIME types\n    AtomArray mime_atoms, clipboard_atoms, primary_atoms;\n\n    struct {\n        bool        available;\n        void*       handle;\n        int         eventBase;\n        int         errorBase;\n        int         major;\n        int         minor;\n        bool        gammaBroken;\n        bool        monitorBroken;\n        PFN_XRRAllocGamma AllocGamma;\n        PFN_XRRFreeCrtcInfo FreeCrtcInfo;\n        PFN_XRRFreeGamma FreeGamma;\n        PFN_XRRFreeOutputInfo FreeOutputInfo;\n        PFN_XRRFreeScreenResources FreeScreenResources;\n        PFN_XRRGetCrtcGamma GetCrtcGamma;\n        PFN_XRRGetCrtcGammaSize GetCrtcGammaSize;\n        PFN_XRRGetCrtcInfo GetCrtcInfo;\n        PFN_XRRGetOutputInfo GetOutputInfo;\n        PFN_XRRGetOutputPrimary GetOutputPrimary;\n        PFN_XRRGetScreenResourcesCurrent GetScreenResourcesCurrent;\n        PFN_XRRQueryExtension QueryExtension;\n        PFN_XRRQueryVersion QueryVersion;\n        PFN_XRRSelectInput SelectInput;\n        PFN_XRRSetCrtcConfig SetCrtcConfig;\n        PFN_XRRSetCrtcGamma SetCrtcGamma;\n        PFN_XRRUpdateConfiguration UpdateConfiguration;\n    } randr;\n\n    _GLFWXKBData xkb;\n    _GLFWDBUSData dbus;\n\n    struct {\n        int         count;\n        int         timeout;\n        int         interval;\n        int         blanking;\n        int         exposure;\n    } saver;\n\n    struct {\n        int         version;\n        Window      source;\n        char        format[256];\n        int         format_priority;\n        Window      target_window;  // For drag events: the window being dragged over\n        const char** mimes;          // Cached MIME types from drag enter (original, never reordered)\n        size_t       mimes_count;    // Count of MIME types (full original list, never reduced)\n        const char** copy_mimes;     // Working copy passed to callbacks; pointers into mimes[]\n        size_t       copy_mimes_count; // Accepted count after last callback\n        bool drag_accepted;          // Whether the callback accepted at least one MIME type\n        bool from_self, dropped;\n        Time drop_time;\n        XdndSelectionRequest *selection_requests;\n        size_t selection_requests_count, selection_requests_capacity;\n    } xdnd;\n\n    // Drag source state\n    struct {\n        Window           source_window;\n        Atom*            type_atoms;    // Atoms for each MIME type\n        size_t           type_count;\n        Atom             action_atom;   // XdndActionCopy, XdndActionMove, or XdndActionLink\n        bool             active;        // Whether a drag is currently active\n        Window           current_target;// Current drop target window under cursor\n        Window           proxy_target;  // Proxy target if current_target has XdndProxy\n        int              xdnd_version;  // Xdnd version supported by current target\n        bool             waiting_for_status; // Waiting for XdndStatus from target\n        bool             accepted;      // Whether target accepted the drag\n        Atom             accepted_action; // Action accepted by target\n        struct {\n            const char *mime_type;\n            Window     requestor;\n            Atom       property;\n            Atom       target;\n            bool       inflight;\n        } *pending_requests;\n        size_t           pending_count;\n        size_t           pending_capacity;\n        // Thumbnail window for drag icon\n        Window           thumbnail_window;\n        Pixmap           thumbnail_pixmap;\n        GC               thumbnail_gc;\n    } drag;\n\n    struct {\n        void*       handle;\n        PFN_XcursorImageCreate ImageCreate;\n        PFN_XcursorImageDestroy ImageDestroy;\n        PFN_XcursorImageLoadCursor ImageLoadCursor;\n    } xcursor;\n\n    struct {\n        bool        available;\n        void*       handle;\n        int         major;\n        int         minor;\n        PFN_XineramaIsActive IsActive;\n        PFN_XineramaQueryExtension QueryExtension;\n        PFN_XineramaQueryScreens QueryScreens;\n    } xinerama;\n\n    struct {\n        bool        available;\n        void*       handle;\n        int         eventBase;\n        int         errorBase;\n        PFN_XF86VidModeQueryExtension QueryExtension;\n        PFN_XF86VidModeGetGammaRamp GetGammaRamp;\n        PFN_XF86VidModeSetGammaRamp SetGammaRamp;\n        PFN_XF86VidModeGetGammaRampSize GetGammaRampSize;\n    } vidmode;\n\n    struct {\n        bool        available;\n        void*       handle;\n        int         majorOpcode;\n        int         eventBase;\n        int         errorBase;\n        int         major;\n        int         minor;\n        PFN_XIQueryVersion QueryVersion;\n        PFN_XISelectEvents SelectEvents;\n        PFN_XIQueryDevice QueryDevice;\n        PFN_XIFreeDeviceInfo FreeDeviceInfo;\n        PFN_XIGetProperty GetProperty;\n        XIScrollDevice scroll_devices[16];\n        unsigned num_scroll_devices;\n        int master_pointer_id;\n        Atom LIBINPUT_SCROLL_METHOD_ENABLED, LIBINPUT_TAPPING;\n    } xi;\n\n    struct {\n        bool        available;\n        void*       handle;\n        int         major;\n        int         minor;\n        int         eventBase;\n        int         errorBase;\n        PFN_XRenderQueryExtension QueryExtension;\n        PFN_XRenderQueryVersion QueryVersion;\n        PFN_XRenderFindVisualFormat FindVisualFormat;\n    } xrender;\n\n    struct {\n        bool        available;\n        void*       handle;\n        int         major;\n        int         minor;\n        int         eventBase;\n        int         errorBase;\n        PFN_XShapeQueryExtension QueryExtension;\n        PFN_XShapeCombineRegion ShapeCombineRegion;\n        PFN_XShapeQueryVersion QueryVersion;\n        PFN_XShapeCombineMask ShapeCombineMask;\n    } xshape;\n\n    EventLoopData eventLoopData;\n\n} _GLFWlibraryX11;\n\n// X11-specific per-monitor data\n//\ntypedef struct _GLFWmonitorX11\n{\n    RROutput        output;\n    RRCrtc          crtc;\n    RRMode          oldMode;\n\n    // Index of corresponding Xinerama screen,\n    // for EWMH full screen window placement\n    int             index;\n\n} _GLFWmonitorX11;\n\n// X11-specific per-cursor data\n//\ntypedef struct _GLFWcursorX11\n{\n    Cursor handle;\n\n} _GLFWcursorX11;\n\n\nvoid _glfwPollMonitorsX11(void);\nvoid _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired);\nvoid _glfwRestoreVideoModeX11(_GLFWmonitor* monitor);\n\nCursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot);\n\nunsigned long _glfwGetWindowPropertyX11(Window window,\n                                        Atom property,\n                                        Atom type,\n                                        unsigned char** value);\nbool _glfwIsVisualTransparentX11(Visual* visual);\n\nvoid _glfwGrabErrorHandlerX11(void);\nvoid _glfwReleaseErrorHandlerX11(void);\nvoid _glfwInputErrorX11(int error, const char* message);\n\nvoid _glfwGetSystemContentScaleX11(float* xscale, float* yscale, bool bypass_cache);\nvoid _glfwPushSelectionToManagerX11(void);\nvoid read_xi_scroll_devices(void);\nvoid free_dnd_data(void);\n"
  },
  {
    "path": "glfw/x11_window.c",
    "content": "//========================================================================\n// GLFW 3.4 X11 - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2002-2006 Marcus Geelnard\n// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n// It is fine to use C99 in this file because it will not be built with VS\n//========================================================================\n\n#define _GNU_SOURCE\n#include \"internal.h\"\n#include \"math.h\"\n#include \"backend_utils.h\"\n#include \"linux_notify.h\"\n#include \"../kitty/monotonic.h\"\n\n#include <X11/cursorfont.h>\n#include <X11/Xmd.h>\n\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <limits.h>\n#include <errno.h>\n#include <assert.h>\n\n// Action for EWMH client messages\n#define _NET_WM_STATE_REMOVE        0\n#define _NET_WM_STATE_ADD           1\n#define _NET_WM_STATE_TOGGLE        2\n\n// X11 momentum scrolling timer state\nstatic struct {\n    unsigned long long timer_id;\n    GLFWid window_id;\n    GLFWScrollEvent last_event;\n} x11_momentum_scroll_state = {0};\n\nstatic void\nx11_scroll_stop_timer_callback(unsigned long long timer_id UNUSED, void *data UNUSED) {\n    x11_momentum_scroll_state.timer_id = 0;\n    _GLFWwindow *w = _glfwWindowForId(x11_momentum_scroll_state.window_id);\n    if (w) {\n        x11_momentum_scroll_state.last_event.y_offset = 0; x11_momentum_scroll_state.last_event.x_offset = 0;\n        x11_momentum_scroll_state.last_event.unscaled.x = 0; x11_momentum_scroll_state.last_event.unscaled.y = 0;\n        glfw_handle_scroll_event_for_momentum(w, &x11_momentum_scroll_state.last_event, true, true);\n    } else {\n        // Window no longer exists, cancel any ongoing momentum\n        glfw_cancel_momentum_scroll();\n    }\n}\n\nstatic void\nx11_cancel_momentum_scroll_timer(void) {\n    if (x11_momentum_scroll_state.timer_id) {\n        glfwRemoveTimer(x11_momentum_scroll_state.timer_id);\n        x11_momentum_scroll_state.timer_id = 0;\n    }\n    x11_momentum_scroll_state.window_id = 0;\n}\n\n// Additional mouse button names for XButtonEvent\n#define Button6            6\n#define Button7            7\n\n// Motif WM hints flags\n#define MWM_HINTS_DECORATIONS   2\n#define MWM_DECOR_ALL           1\n\n#define _GLFW_XDND_VERSION 5\n\n// Wait for data to arrive using poll\n// This avoids blocking other threads via the per-display Xlib lock that also\n// covers GLX functions\n//\nstatic unsigned _glfwDispatchX11Events(void);\n\n// Forward declarations for drag source helper functions\nstatic void send_drag_data(const char *mime_type, const char *data, size_t data_sz,\n                          Window requestor, Atom property, Atom target);\nstatic bool add_pending_request(const char *mime_type, Window requestor, Atom property, Atom target);\nstatic void handle_drag_motion(int root_x, int root_y, Time timestamp);\nstatic void handle_drag_button_release(Time timestamp);\nstatic void handle_xdnd_status(const XClientMessageEvent *event);\nstatic void handle_xdnd_finished(const XClientMessageEvent *event);\nstatic bool create_drag_thumbnail(const GLFWimage* thumbnail, int x, int y);\nstatic bool render_drag_thumbnail_to_pixmap(const GLFWimage* thumbnail);\n\nstatic void\nhandleEvents(monotonic_t timeout) {\n    EVDBG(\"starting handleEvents(%.2f)\", monotonic_t_to_s_double(timeout));\n    int display_read_ok = pollForEvents(&_glfw.x11.eventLoopData, timeout, NULL);\n    EVDBG(\"display_read_ok: %d\", display_read_ok);\n    if (display_read_ok) {\n        unsigned dispatched = _glfwDispatchX11Events();\n        (void)dispatched;\n        EVDBG(\"dispatched %u X11 events\", dispatched);\n    }\n    glfw_ibus_dispatch(&_glfw.x11.xkb.ibus);\n    glfw_dbus_session_bus_dispatch();\n    EVDBG(\"other dispatch done\");\n    if (_glfw.x11.eventLoopData.wakeup_fd_ready) check_for_wakeup_events(&_glfw.x11.eventLoopData);\n}\n\nstatic bool\nwaitForX11Event(monotonic_t timeout) {\n    // returns true if there is X11 data waiting to be read, does not run watches and timers\n    monotonic_t end_time = glfwGetTime() + timeout;\n    while(true) {\n        if (timeout >= 0) {\n            const int result = pollWithTimeout(_glfw.x11.eventLoopData.fds, 1, timeout);\n            if (result > 0) return true;\n            timeout = end_time - glfwGetTime();\n            if (timeout <= 0) return false;\n            if (result < 0 && (errno == EINTR || errno == EAGAIN)) continue;\n            return false;\n        } else {\n            const int result = poll(_glfw.x11.eventLoopData.fds, 1, -1);\n            if (result > 0) return true;\n            if (result < 0 && (errno == EINTR || errno == EAGAIN)) continue;\n            return false;\n        }\n    }\n}\n\n// Waits until a VisibilityNotify event arrives for the specified window or the\n// timeout period elapses (ICCCM section 4.2.2)\n//\nstatic bool waitForVisibilityNotify(_GLFWwindow* window)\n{\n    XEvent dummy;\n\n    while (!XCheckTypedWindowEvent(_glfw.x11.display,\n                                   window->x11.handle,\n                                   VisibilityNotify,\n                                   &dummy))\n    {\n        if (!waitForX11Event(ms_to_monotonic_t(100ll)))\n            return false;\n    }\n\n    return true;\n}\n\n// Returns whether the window is iconified\n//\nstatic int getWindowState(_GLFWwindow* window)\n{\n    int result = WithdrawnState;\n    struct {\n        CARD32 state;\n        Window icon;\n    } *state = NULL;\n\n    if (_glfwGetWindowPropertyX11(window->x11.handle,\n                                  _glfw.x11.WM_STATE,\n                                  _glfw.x11.WM_STATE,\n                                  (unsigned char**) &state) >= 2)\n    {\n        result = state->state;\n    }\n\n    if (state)\n        XFree(state);\n\n    return result;\n}\n\n// Returns whether the event is a selection event\n//\nstatic Bool isSelectionEvent(Display* display UNUSED, XEvent* event, XPointer pointer UNUSED)\n{\n    if (event->xany.window != _glfw.x11.helperWindowHandle)\n        return False;\n\n    return event->type == SelectionRequest ||\n           event->type == SelectionNotify ||\n           event->type == SelectionClear;\n}\n\n// Returns whether it is a _NET_FRAME_EXTENTS event for the specified window\n//\nstatic Bool isFrameExtentsEvent(Display* display UNUSED, XEvent* event, XPointer pointer)\n{\n    _GLFWwindow* window = (_GLFWwindow*) pointer;\n    return event->type == PropertyNotify &&\n           event->xproperty.state == PropertyNewValue &&\n           event->xproperty.window == window->x11.handle &&\n           event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;\n}\n\n// Returns whether it is a property event for the specified selection transfer\n//\nstatic Bool isSelPropNewValueNotify(Display* display UNUSED, XEvent* event, XPointer pointer)\n{\n    XEvent* notification = (XEvent*) pointer;\n    return event->type == PropertyNotify &&\n           event->xproperty.state == PropertyNewValue &&\n           event->xproperty.window == notification->xselection.requestor &&\n           event->xproperty.atom == notification->xselection.property;\n}\n\n// Translates an X event modifier state mask\n//\nstatic int translateState(int state)\n{\n    int mods = 0;\n\n    /* Need some way to expose hyper and meta without xkbcommon-x11 */\n    if (state & ShiftMask)\n        mods |= GLFW_MOD_SHIFT;\n    if (state & ControlMask)\n        mods |= GLFW_MOD_CONTROL;\n    if (state & Mod1Mask)\n        mods |= GLFW_MOD_ALT;\n    if (state & Mod4Mask)\n        mods |= GLFW_MOD_SUPER;\n    if (state & LockMask)\n        mods |= GLFW_MOD_CAPS_LOCK;\n    if (state & Mod2Mask)\n        mods |= GLFW_MOD_NUM_LOCK;\n\n    return mods;\n}\n\n// Sends an EWMH or ICCCM event to the window manager\n//\nstatic void sendEventToWM(_GLFWwindow* window, Atom type,\n                          long a, long b, long c, long d, long e)\n{\n    XEvent event = { ClientMessage };\n    event.xclient.window = window->x11.handle;\n    event.xclient.format = 32; // Data is 32-bit longs\n    event.xclient.message_type = type;\n    event.xclient.data.l[0] = a;\n    event.xclient.data.l[1] = b;\n    event.xclient.data.l[2] = c;\n    event.xclient.data.l[3] = d;\n    event.xclient.data.l[4] = e;\n\n    XSendEvent(_glfw.x11.display, _glfw.x11.root,\n               False,\n               SubstructureNotifyMask | SubstructureRedirectMask,\n               &event);\n}\n\n// Updates the normal hints according to the window settings\n//\nstatic void\nupdateNormalHints(_GLFWwindow* window, int width, int height)\n{\n    XSizeHints* hints = XAllocSizeHints();\n\n    if (!window->monitor)\n    {\n        if (window->resizable)\n        {\n            if (window->minwidth != GLFW_DONT_CARE &&\n                window->minheight != GLFW_DONT_CARE)\n            {\n                hints->flags |= PMinSize;\n                hints->min_width = window->minwidth;\n                hints->min_height = window->minheight;\n            }\n\n            if (window->maxwidth != GLFW_DONT_CARE &&\n                window->maxheight != GLFW_DONT_CARE)\n            {\n                hints->flags |= PMaxSize;\n                hints->max_width = window->maxwidth;\n                hints->max_height = window->maxheight;\n            }\n\n            if (window->numer != GLFW_DONT_CARE &&\n                window->denom != GLFW_DONT_CARE)\n            {\n                hints->flags |= PAspect;\n                hints->min_aspect.x = hints->max_aspect.x = window->numer;\n                hints->min_aspect.y = hints->max_aspect.y = window->denom;\n            }\n\n            if (window->widthincr != GLFW_DONT_CARE &&\n                window->heightincr != GLFW_DONT_CARE && !window->x11.maximized)\n            {\n                hints->flags |= PResizeInc;\n                hints->width_inc = window->widthincr;\n                hints->height_inc = window->heightincr;\n            }\n        }\n        else\n        {\n            hints->flags |= (PMinSize | PMaxSize);\n            hints->min_width  = hints->max_width  = width;\n            hints->min_height = hints->max_height = height;\n        }\n    }\n\n    hints->flags |= PWinGravity;\n    hints->win_gravity = StaticGravity;\n\n    XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);\n    XFree(hints);\n}\n\nstatic bool\nis_window_fullscreen(_GLFWwindow* window)\n{\n    Atom* states;\n    unsigned long i;\n    bool ans = false;\n    if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_FULLSCREEN)\n        return ans;\n    const unsigned long count =\n        _glfwGetWindowPropertyX11(window->x11.handle,\n                                  _glfw.x11.NET_WM_STATE,\n                                  XA_ATOM,\n                                  (unsigned char**) &states);\n\n    for (i = 0;  i < count;  i++)\n    {\n        if (states[i] == _glfw.x11.NET_WM_STATE_FULLSCREEN)\n        {\n            ans = true;\n            break;\n        }\n    }\n\n    if (states)\n        XFree(states);\n\n    return ans;\n}\n\nstatic void\nset_fullscreen(_GLFWwindow *window, bool on) {\n    if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) {\n        sendEventToWM(window,\n                _glfw.x11.NET_WM_STATE,\n                on ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,\n                _glfw.x11.NET_WM_STATE_FULLSCREEN,\n                0, 1, 0);\n        // Enable compositor bypass\n        if (!window->x11.transparent)\n        {\n            if (on) {\n                const unsigned long value = 1;\n\n                XChangeProperty(_glfw.x11.display,  window->x11.handle,\n                                _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,\n                                PropModeReplace, (unsigned char*) &value, 1);\n            } else {\n                XDeleteProperty(_glfw.x11.display, window->x11.handle,\n                                _glfw.x11.NET_WM_BYPASS_COMPOSITOR);\n            }\n        }\n\n    } else {\n        static bool warned = false;\n        if (!warned) {\n            warned = true;\n            _glfwInputErrorX11(GLFW_PLATFORM_ERROR,\n                               \"X11: Failed to toggle fullscreen, the window manager does not support it\");\n        }\n    }\n}\n\nbool\n_glfwPlatformIsFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {\n    return is_window_fullscreen(window);\n}\n\nbool\n_glfwPlatformToggleFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {\n    bool already_fullscreen = is_window_fullscreen(window);\n    set_fullscreen(window, !already_fullscreen);\n    return !already_fullscreen;\n}\n\n// Updates the full screen status of the window\n//\nstatic void updateWindowMode(_GLFWwindow* window)\n{\n    if (window->monitor)\n    {\n        if (_glfw.x11.xinerama.available &&\n            _glfw.x11.NET_WM_FULLSCREEN_MONITORS)\n        {\n            sendEventToWM(window,\n                          _glfw.x11.NET_WM_FULLSCREEN_MONITORS,\n                          window->monitor->x11.index,\n                          window->monitor->x11.index,\n                          window->monitor->x11.index,\n                          window->monitor->x11.index,\n                          0);\n        }\n\n        set_fullscreen(window, true);\n\n    }\n    else\n    {\n        if (_glfw.x11.xinerama.available &&\n            _glfw.x11.NET_WM_FULLSCREEN_MONITORS)\n        {\n            XDeleteProperty(_glfw.x11.display, window->x11.handle,\n                            _glfw.x11.NET_WM_FULLSCREEN_MONITORS);\n        }\n\n        set_fullscreen(window, false);\n\n    }\n}\n\n\n// Encode a Unicode code point to a UTF-8 stream\n// Based on cutef8 by Jeff Bezanson (Public Domain)\n//\nstatic size_t encodeUTF8(char* s, unsigned int ch)\n{\n    size_t count = 0;\n\n    if (ch < 0x80)\n        s[count++] = (char) ch;\n    else if (ch < 0x800)\n    {\n        s[count++] = (ch >> 6) | 0xc0;\n        s[count++] = (ch & 0x3f) | 0x80;\n    }\n    else if (ch < 0x10000)\n    {\n        s[count++] = (ch >> 12) | 0xe0;\n        s[count++] = ((ch >> 6) & 0x3f) | 0x80;\n        s[count++] = (ch & 0x3f) | 0x80;\n    }\n    else if (ch < 0x110000)\n    {\n        s[count++] = (ch >> 18) | 0xf0;\n        s[count++] = ((ch >> 12) & 0x3f) | 0x80;\n        s[count++] = ((ch >> 6) & 0x3f) | 0x80;\n        s[count++] = (ch & 0x3f) | 0x80;\n    }\n\n    return count;\n}\n\n// Convert the specified Latin-1 string to UTF-8\n//\nstatic char* convertLatin1toUTF8(const char* source)\n{\n    size_t size = 1;\n    const char* sp;\n\n    if (source) {\n        for (sp = source;  *sp;  sp++)\n            size += (*sp & 0x80) ? 2 : 1;\n    }\n\n    char* target = calloc(size, 1);\n    char* tp = target;\n\n    if (source) {\n        for (sp = source;  *sp;  sp++)\n            tp += encodeUTF8(tp, *sp);\n    }\n\n    return target;\n}\n\n// Updates the cursor image according to its cursor mode\n//\nstatic void updateCursorImage(_GLFWwindow* window)\n{\n    if (window->cursorMode == GLFW_CURSOR_NORMAL)\n    {\n        if (window->cursor)\n        {\n            XDefineCursor(_glfw.x11.display, window->x11.handle,\n                          window->cursor->x11.handle);\n        }\n        else\n            XUndefineCursor(_glfw.x11.display, window->x11.handle);\n    }\n    else\n    {\n        XDefineCursor(_glfw.x11.display, window->x11.handle,\n                      _glfw.x11.hiddenCursorHandle);\n    }\n}\n\n// Enable XI2 raw mouse motion events\n//\nstatic void enableRawMouseMotion(_GLFWwindow* window UNUSED)\n{\n    XIEventMask em;\n    unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };\n\n    em.deviceid = XIAllMasterDevices;\n    em.mask_len = sizeof(mask);\n    em.mask = mask;\n    XISetMask(mask, XI_RawMotion);\n\n    XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);\n}\n\n// Disable XI2 raw mouse motion events\n//\nstatic void disableRawMouseMotion(_GLFWwindow* window UNUSED)\n{\n    XIEventMask em;\n    unsigned char mask[] = { 0 };\n\n    em.deviceid = XIAllMasterDevices;\n    em.mask_len = sizeof(mask);\n    em.mask = mask;\n\n    XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);\n}\n\n// Enable XI2 smooth scrolling events on a window\n//\nstatic void enableSmoothScrolling(_GLFWwindow* window)\n{\n    if (!_glfw.x11.xi.num_scroll_devices) return;\n    // Select XI_Motion events on the window\n    XIEventMask em;\n    unsigned char mask[XIMaskLen(XI_Motion)] = { 0 };\n\n    em.deviceid = XIAllDevices;\n    em.mask_len = sizeof(mask);\n    em.mask = mask;\n    XISetMask(mask, XI_Motion);\n\n    XISelectEvents(_glfw.x11.display, window->x11.handle, &em, 1);\n}\n\nstatic void\nresetScrollValuators(void) {\n    for (unsigned i = 0; i < _glfw.x11.xi.num_scroll_devices; i++) {\n        XIScrollDevice *d = &_glfw.x11.xi.scroll_devices[i];\n        for (unsigned k = 0; k < d->num_valuators; k++)\n            d->valuators[k].initialized = false;\n    }\n}\n\n// Apply disabled cursor mode to a focused window\n//\nstatic void disableCursor(_GLFWwindow* window)\n{\n    if (window->rawMouseMotion)\n        enableRawMouseMotion(window);\n\n    _glfw.x11.disabledCursorWindow = window;\n    _glfwPlatformGetCursorPos(window,\n                              &_glfw.x11.restoreCursorPosX,\n                              &_glfw.x11.restoreCursorPosY);\n    updateCursorImage(window);\n    _glfwCenterCursorInContentArea(window);\n    XGrabPointer(_glfw.x11.display, window->x11.handle, True,\n                 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,\n                 GrabModeAsync, GrabModeAsync,\n                 window->x11.handle,\n                 _glfw.x11.hiddenCursorHandle,\n                 CurrentTime);\n}\n\n// Exit disabled cursor mode for the specified window\n//\nstatic void enableCursor(_GLFWwindow* window)\n{\n    if (window->rawMouseMotion)\n        disableRawMouseMotion(window);\n\n    _glfw.x11.disabledCursorWindow = NULL;\n    XUngrabPointer(_glfw.x11.display, CurrentTime);\n    _glfwPlatformSetCursorPos(window,\n                              _glfw.x11.restoreCursorPosX,\n                              _glfw.x11.restoreCursorPosY);\n    updateCursorImage(window);\n}\n\ntypedef unsigned long strut_type;\n\ntypedef struct WindowGeometry {\n    int x, y, width, height;\n    bool needs_strut;\n    strut_type struts[12];\n} WindowGeometry;\n\n#define config (window->x11.layer_shell.config)\n\nstatic _GLFWmonitor*\nfind_monitor_by_name(const char* name) {\n    if (!name || !name[0]) return (_GLFWmonitor*)glfwGetPrimaryMonitor();;\n    for (int i = 0; i < _glfw.monitorCount; i++) {\n        _GLFWmonitor *m = _glfw.monitors[i];\n        if (strcmp(m->name, name) == 0) return m;\n    }\n    return (_GLFWmonitor*)glfwGetPrimaryMonitor();;\n}\n\n\nstatic WindowGeometry\ncalculate_layer_geometry(_GLFWwindow *window) {\n    _GLFWmonitor *monitor = find_monitor_by_name(config.output_name);\n    MonitorGeometry mg = _glfwPlatformGetMonitorGeometry((_GLFWmonitor*)glfwGetPrimaryMonitor());\n    WindowGeometry ans = {0};\n    debug_rendering(\"Monitor: %s full: %dx%d@%dx%d workarea: %dx%d@%dx%d\\n\", monitor->name,\n            mg.full.width, mg.full.height, mg.full.x, mg.full.y, mg.workarea.width, mg.workarea.height, mg.workarea.x, mg.workarea.y);\n    ans.width = mg.full.width; ans.height = mg.full.height;\n    ans.x = mg.full.x; ans.y = mg.full.y;\n    ans.needs_strut = config.type == GLFW_LAYER_SHELL_PANEL;\n    if (config.type == GLFW_LAYER_SHELL_BACKGROUND) {\n        ans.x += config.requested_left_margin; ans.y += config.requested_top_margin;\n        ans.width -= config.requested_left_margin + config.requested_right_margin;\n        ans.height -= config.requested_top_margin + config.requested_bottom_margin;\n        return ans;\n    }\n    float xscale = (float)config.expected.xscale, yscale = (float)config.expected.yscale;\n    _glfwPlatformGetWindowContentScale(window, &xscale, &yscale);\n    unsigned cell_width, cell_height; double left_edge_spacing, top_edge_spacing, right_edge_spacing, bottom_edge_spacing;\n    config.size_callback((GLFWwindow*)window, xscale, yscale, &cell_width, &cell_height, &left_edge_spacing, &top_edge_spacing, &right_edge_spacing, &bottom_edge_spacing);\n    double spacing_x = left_edge_spacing + right_edge_spacing;\n    double spacing_y = top_edge_spacing + bottom_edge_spacing;\n    double xsz = config.x_size_in_pixels ? (unsigned)(config.x_size_in_pixels * xscale) : (cell_width * config.x_size_in_cells);\n    double ysz = config.y_size_in_pixels ? (unsigned)(config.y_size_in_pixels * yscale) : (cell_height * config.y_size_in_cells);\n    ans.width = (int)(1. + spacing_x + xsz); ans.height = (int)(1. + spacing_y + ysz);\n    GeometryRect m = config.type == GLFW_LAYER_SHELL_TOP || config.type == GLFW_LAYER_SHELL_OVERLAY ? mg.workarea : mg.full;\n    static const struct {\n        unsigned left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x;\n    } s = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};\n\n    switch (config.edge) {\n        case GLFW_EDGE_LEFT:\n            ans.x = m.x + config.requested_left_margin;\n            ans.y = m.y + config.requested_top_margin;\n            ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin;\n            ans.struts[s.left] = ans.width; ans.struts[s.left_end_y] = ans.height;\n            break;\n        case GLFW_EDGE_RIGHT:\n            ans.x = m.x + m.width - config.requested_right_margin - ans.width;\n            ans.y = m.y + config.requested_top_margin;\n            ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin;\n            ans.struts[s.right] = ans.width; ans.struts[s.right_end_y] = ans.height;\n            break;\n        case GLFW_EDGE_TOP:\n            ans.x = m.x + config.requested_left_margin;\n            ans.y = m.y + config.requested_top_margin;\n            ans.width = m.width - config.requested_right_margin - config.requested_left_margin;\n            ans.struts[s.top] = ans.height; ans.struts[s.top_end_x] = ans.width;\n            break;\n        case GLFW_EDGE_BOTTOM:\n            ans.x = m.x + config.requested_left_margin;\n            ans.y = m.height - config.requested_bottom_margin - ans.height;\n            ans.width = m.width - config.requested_right_margin - config.requested_left_margin;\n            ans.struts[s.bottom] = ans.height; ans.struts[s.bottom_end_x] = ans.width;\n            break;\n        case GLFW_EDGE_NONE:\n            ans.needs_strut = false;\n            ans.x = m.x + config.requested_left_margin;\n            ans.y = m.y + config.requested_top_margin;\n            break;\n        case GLFW_EDGE_CENTER_SIZED:\n            ans.needs_strut = false;\n            ans.x = (m.width - ans.width) / 2;\n            ans.y = (m.height - ans.height) / 2;\n            break;\n        default:\n            ans.needs_strut = false;\n            ans.x = m.x + config.requested_left_margin;\n            ans.y = m.y + config.requested_top_margin;\n            ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin;\n            ans.width = m.width - config.requested_right_margin - config.requested_left_margin;\n            break;\n    }\n    debug_rendering(\"Calculating layer geometry at scale: %f cell size: (%u, %u) -> %dx%d@%dx%d needs_strut: %d\\n\",\n            xscale, cell_width, cell_height, ans.width, ans.height, ans.x, ans.y, ans.needs_strut)\n    return ans;\n}\n\nGLFWAPI bool glfwIsLayerShellSupported(void) { return _glfw.x11.NET_WM_WINDOW_TYPE != 0 && _glfw.x11.NET_WM_STATE != 0; }\n\n\nstatic bool\nupdate_wm_hints(_GLFWwindow *window, const WindowGeometry *wg, const _GLFWwndconfig *wndconfig) {\n    XWMHints* hints = XAllocWMHints();\n    bool is_layer_shell = window->x11.layer_shell.is_active;\n    bool ok = false;\n    if (hints) {\n        ok = true;\n        hints->flags = StateHint | InputHint;\n        hints->initial_state = NormalState;\n        hints->input = true;\n        if (is_layer_shell && config.focus_policy == GLFW_FOCUS_NOT_ALLOWED) hints->input = false;\n        XSetWMHints(_glfw.x11.display, window->x11.handle, hints);\n        XFree(hints);\n    } else _glfwInputError(GLFW_OUT_OF_MEMORY, \"X11: Failed to allocate WM hints\");\n    if (_glfw.x11.NET_WM_WINDOW_TYPE) {\n        Atom type = 0;\n        if (is_layer_shell) {\n            const char *name = NULL;\n#define S(which) type = _glfw.x11.which; name = #which\n            switch (config.type) {\n                case GLFW_LAYER_SHELL_BACKGROUND: S(NET_WM_WINDOW_TYPE_DESKTOP); break;\n                case GLFW_LAYER_SHELL_PANEL: S(NET_WM_WINDOW_TYPE_DOCK); break;\n                default: S(NET_WM_WINDOW_TYPE_NORMAL); break;\n            }\n#undef S\n            if (!type) {\n                _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Window manager does not support _%s\", name);\n                ok = false;\n            }\n        } else if (_glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;\n        if (type) XChangeProperty(\n            _glfw.x11.display,  window->x11.handle, _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char*) &type, 1);\n    } else if (is_layer_shell) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Window manager does not support _NET_WM_WINDOW_TYPE\");\n        ok = false;\n    }\n    if (is_layer_shell) {\n        if (_glfw.x11.NET_WM_STRUT_PARTIAL) {\n            XChangeProperty(\n                _glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STRUT_PARTIAL, XA_CARDINAL, 32, PropModeReplace,\n                (unsigned char*)(wg->needs_strut ? wg->struts : (strut_type[12]){0}), 12);\n        } else if (wg->needs_strut) {\n            _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Window manager does not support _NET_WM_STRUT_PARTIAL\");\n            ok = false;\n        }\n    }\n    if (ok) {\n        updateNormalHints(window, wg->width, wg->height);\n        Atom states[8]; unsigned count = 0;\n        if (is_layer_shell) {\n            _glfwPlatformSetWindowDecorated(window, false);\n            if (_glfw.x11.NET_WM_STATE_STICKY) states[count++] = _glfw.x11.NET_WM_STATE_STICKY;\n            if (_glfw.x11.NET_WM_STATE_SKIP_PAGER) states[count++] = _glfw.x11.NET_WM_STATE_SKIP_PAGER;\n            if (_glfw.x11.NET_WM_STATE_SKIP_TASKBAR) states[count++] = _glfw.x11.NET_WM_STATE_SKIP_TASKBAR;\n#define S(x) if (_glfw.x11.x) { states[count++] = _glfw.x11.x; } else { _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Window manager does not support _%s\", #x); ok = false; }\n            switch (config.type) {\n                case GLFW_LAYER_SHELL_NONE: break;\n                case GLFW_LAYER_SHELL_BACKGROUND:  S(NET_WM_STATE_BELOW); break;\n                case GLFW_LAYER_SHELL_PANEL:\n                    // i3 does not support NET_WM_STATE_BELOW but panels work without it\n                    if (_glfw.x11.NET_WM_STATE_BELOW) { S(NET_WM_STATE_BELOW); }\n                    break;\n                case GLFW_LAYER_SHELL_TOP: case GLFW_LAYER_SHELL_OVERLAY: S(NET_WM_STATE_ABOVE); break;\n            }\n#undef S\n        } else if (wndconfig) {\n            if (!wndconfig->decorated) _glfwPlatformSetWindowDecorated(window, false);\n            if (_glfw.x11.NET_WM_STATE && !window->monitor) {\n                if (wndconfig->floating) {\n                    if (_glfw.x11.NET_WM_STATE_ABOVE) states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;\n                }\n                if (wndconfig->maximized) {\n                    if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) {\n                        states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;\n                        states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;\n                        window->x11.maximized = true;\n                    }\n                }\n            }\n        }\n        if (count && _glfw.x11.NET_WM_STATE) XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE,\n                XA_ATOM, 32, PropModeReplace, (unsigned char*) states, count);\n    }\n    if (!wndconfig && ok) {\n        _glfwPlatformSetWindowPos(window, wg->x, wg->y);\n        _glfwPlatformSetWindowSize(window, wg->width, wg->height);\n    }\n    return ok;\n#undef config\n}\n\n// Create the X11 window (and its colormap)\n//\nstatic bool createNativeWindow(_GLFWwindow* window,\n                                   const _GLFWwndconfig* wndconfig,\n                                   Visual* visual, int depth)\n{\n    WindowGeometry wg = {.width=wndconfig->width, .height=wndconfig->height};\n    if (window->x11.layer_shell.is_active) {\n        wg = calculate_layer_geometry(window);\n        window->resizable = false;\n    }\n\n    // Create a colormap based on the visual used by the current context\n    window->x11.colormap = XCreateColormap(_glfw.x11.display,\n                                           _glfw.x11.root,\n                                           visual,\n                                           AllocNone);\n\n    window->x11.transparent = _glfwIsVisualTransparentX11(visual);\n\n    XSetWindowAttributes wa = { 0 };\n    wa.colormap = window->x11.colormap;\n    wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |\n                    PointerMotionMask | ButtonPressMask | ButtonReleaseMask |\n                    ExposureMask | FocusChangeMask | VisibilityChangeMask |\n                    EnterWindowMask | LeaveWindowMask | PropertyChangeMask;\n\n    _glfwGrabErrorHandlerX11();\n\n    window->x11.parent = _glfw.x11.root;\n    debug_rendering(\"Creating window with geometry: %dx%d@%dx%d\\n\", wg.width, wg.height, wg.x, wg.y);\n    window->x11.handle = XCreateWindow(_glfw.x11.display,\n                                       _glfw.x11.root,\n                                       wg.x, wg.y,   // Position\n                                       wg.width, wg.height,\n                                       0,      // Border width\n                                       depth,  // Color depth\n                                       InputOutput,\n                                       visual,\n                                       CWBorderPixel | CWColormap | CWEventMask,\n                                       &wa);\n\n    _glfwReleaseErrorHandlerX11();\n\n    if (!window->x11.handle)\n    {\n        _glfwInputErrorX11(GLFW_PLATFORM_ERROR,\n                           \"X11: Failed to create window\");\n        return false;\n    }\n\n    XSaveContext(_glfw.x11.display,\n                 window->x11.handle,\n                 _glfw.x11.context,\n                 (XPointer) window);\n\n    // Declare the WM protocols supported by GLFW\n    {\n        Atom protocols[] =\n        {\n            _glfw.x11.WM_DELETE_WINDOW,\n            _glfw.x11.NET_WM_PING\n        };\n\n        XSetWMProtocols(_glfw.x11.display, window->x11.handle,\n                        protocols, sizeof(protocols) / sizeof(Atom));\n    }\n\n    // Declare our PID\n    {\n        const long pid = getpid();\n\n        XChangeProperty(_glfw.x11.display,  window->x11.handle,\n                        _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,\n                        PropModeReplace,\n                        (unsigned char*) &pid, 1);\n    }\n\n    if (!update_wm_hints(window, &wg, wndconfig)) return false;\n    // without this floating window position is incorrect on KDE\n    if (window->x11.layer_shell.is_active) _glfwPlatformSetWindowPos(window, wg.x, wg.y);\n\n    // Set ICCCM WM_CLASS property\n    {\n        XClassHint* hint = XAllocClassHint();\n\n        if (strlen(wndconfig->x11.instanceName) &&\n            strlen(wndconfig->x11.className))\n        {\n            hint->res_name = (char*) wndconfig->x11.instanceName;\n            hint->res_class = (char*) wndconfig->x11.className;\n        }\n        else\n        {\n            const char* resourceName = getenv(\"RESOURCE_NAME\");\n            if (resourceName && strlen(resourceName))\n                hint->res_name = (char*) resourceName;\n            else if (strlen(wndconfig->title))\n                hint->res_name = (char*) wndconfig->title;\n            else\n                hint->res_name = (char*) \"glfw-application\";\n\n            if (strlen(wndconfig->title))\n                hint->res_class = (char*) wndconfig->title;\n            else\n                hint->res_class = (char*) \"GLFW-Application\";\n        }\n\n        XSetClassHint(_glfw.x11.display, window->x11.handle, hint);\n        XFree(hint);\n    }\n\n    // Announce support for Xdnd (drag and drop)\n    {\n        const Atom version = _GLFW_XDND_VERSION;\n        XChangeProperty(_glfw.x11.display, window->x11.handle,\n                        _glfw.x11.XdndAware, XA_ATOM, 32,\n                        PropModeReplace, (unsigned char*) &version, 1);\n    }\n\n    _glfwPlatformSetWindowTitle(window, wndconfig->title);\n    _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);\n    _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);\n\n    if (_glfw.hints.window.blur_radius > 0) _glfwPlatformSetWindowBlur(window, _glfw.hints.window.blur_radius);\n\n    // Enable XI2 smooth scrolling if available\n    enableSmoothScrolling(window);\n\n    return true;\n}\n\nstatic size_t\nget_clipboard_data(const _GLFWClipboardData *cd, const char *mime, char **data) {\n    *data = NULL;\n    if (cd->get_data == NULL) { return 0; }\n    GLFWDataChunk chunk = cd->get_data(mime, NULL, cd->ctype);\n    char *buf = NULL;\n    size_t sz = 0, cap = 0;\n    void *iter = chunk.iter;\n    if (!iter) return 0;\n    while (true) {\n        chunk = cd->get_data(mime, iter, cd->ctype);\n        if (!chunk.sz) break;\n        if (cap < sz + chunk.sz) {\n            cap = MAX(cap * 2, sz + 4 * chunk.sz);\n            buf = realloc(buf, cap * sizeof(buf[0]));\n        }\n        memcpy(buf + sz, chunk.data, chunk.sz);\n        sz += chunk.sz;\n        if (chunk.free) chunk.free((void*)chunk.free_data);\n    }\n    *data = buf;\n    cd->get_data(NULL, iter, cd->ctype);\n    return sz;\n}\n\nstatic void\nget_atom_names(const Atom *atoms, int count, char **atom_names) {\n    _glfwGrabErrorHandlerX11();\n    XGetAtomNames(_glfw.x11.display, (Atom*)atoms, count, atom_names);\n    _glfwReleaseErrorHandlerX11();\n    if (_glfw.x11.errorCode != Success) {\n        for (int i = 0; i < count; i++) {\n            _glfwGrabErrorHandlerX11();\n            atom_names[i] = XGetAtomName(_glfw.x11.display, atoms[i]);\n            _glfwReleaseErrorHandlerX11();\n            if (_glfw.x11.errorCode != Success) atom_names[i] = NULL;\n        }\n    }\n}\n\n\n// Set the specified property to the selection converted to the requested target\n//\nstatic Atom writeTargetToProperty(const XSelectionRequestEvent* request)\n{\n    const AtomArray *aa;\n    const _GLFWClipboardData *cd;\n\n    if (request->selection == _glfw.x11.PRIMARY) {\n        aa = &_glfw.x11.primary_atoms;\n        cd = &_glfw.primary;\n    } else {\n        aa = &_glfw.x11.clipboard_atoms;\n        cd = &_glfw.clipboard;\n    }\n\n    if (request->property == None)\n    {\n        // The requester is a legacy client (ICCCM section 2.2)\n        // We don't support legacy clients, so fail here\n        return None;\n    }\n\n    if (request->target == _glfw.x11.TARGETS)\n    {\n        // The list of supported targets was requested\n\n        Atom *targets = calloc(aa->sz + 2, sizeof(Atom));\n        targets[0] = _glfw.x11.TARGETS;\n        targets[1] = _glfw.x11.MULTIPLE;\n        for (size_t i = 0; i < aa->sz; i++) targets[i+2] = aa->array[i].atom;\n        XChangeProperty(_glfw.x11.display,\n                        request->requestor,\n                        request->property,\n                        XA_ATOM,\n                        32,\n                        PropModeReplace,\n                        (unsigned char*) targets,\n                        aa->sz + 2);\n        free(targets);\n        return request->property;\n    }\n\n    if (request->target == _glfw.x11.MULTIPLE)\n    {\n        // Multiple conversions were requested\n\n        Atom* targets;\n        size_t i, j, count;\n\n        count = _glfwGetWindowPropertyX11(request->requestor,\n                                          request->property,\n                                          _glfw.x11.ATOM_PAIR,\n                                          (unsigned char**) &targets);\n\n        for (i = 0;  i < count;  i += 2)\n        {\n            for (j = 0;  j < aa->sz;  j++)\n            {\n                if (targets[i] == aa->array[j].atom)\n                    break;\n            }\n\n            if (j < aa->sz)\n            {\n                char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[j].mime, &data);\n\n                if (data) XChangeProperty(_glfw.x11.display,\n                                request->requestor,\n                                targets[i + 1],\n                                targets[i],\n                                8,\n                                PropModeReplace,\n                                (unsigned char *) data,\n                                sz);\n                free(data);\n            }\n            else\n                targets[i + 1] = None;\n        }\n\n        XChangeProperty(_glfw.x11.display,\n                        request->requestor,\n                        request->property,\n                        _glfw.x11.ATOM_PAIR,\n                        32,\n                        PropModeReplace,\n                        (unsigned char*) targets,\n                        count);\n\n        XFree(targets);\n\n        return request->property;\n    }\n\n    if (request->target == _glfw.x11.SAVE_TARGETS)\n    {\n        // The request is a check whether we support SAVE_TARGETS\n        // It should be handled as a no-op side effect target\n\n        XChangeProperty(_glfw.x11.display,\n                        request->requestor,\n                        request->property,\n                        _glfw.x11.NULL_,\n                        32,\n                        PropModeReplace,\n                        NULL,\n                        0);\n\n        return request->property;\n    }\n\n    // Conversion to a data target was requested\n\n    for (size_t i = 0;  i < aa->sz;  i++)\n    {\n        if (request->target == aa->array[i].atom)\n        {\n            // The requested target is one we support\n\n            char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[i].mime, &data);\n            if (data) XChangeProperty(_glfw.x11.display,\n                            request->requestor,\n                            request->property,\n                            request->target,\n                            8,\n                            PropModeReplace,\n                            (unsigned char *) data,\n                            sz);\n            free(data);\n\n            return request->property;\n        }\n    }\n\n    // The requested target is not supported\n\n    return None;\n}\n\nstatic void handleSelectionClear(XEvent* event)\n{\n    if (event->xselectionclear.selection == _glfw.x11.PRIMARY)\n    {\n        _glfw_free_clipboard_data(&_glfw.primary);\n        _glfwInputClipboardLost(GLFW_PRIMARY_SELECTION);\n    }\n    else\n    {\n        _glfw_free_clipboard_data(&_glfw.clipboard);\n        _glfwInputClipboardLost(GLFW_CLIPBOARD);\n    }\n}\n\nstatic void\nhandleSelectionRequest(XEvent* event) {\n    const XSelectionRequestEvent* request = &event->xselectionrequest;\n\n    XEvent reply = { SelectionNotify };\n    reply.xselection.display = request->display;\n    reply.xselection.requestor = request->requestor;\n    reply.xselection.selection = request->selection;\n    reply.xselection.target = request->target;\n    reply.xselection.time = request->time;\n    reply.xselection.property = None;\n\n    // Handle XdndSelection (drag and drop) specially\n    if (request->selection == _glfw.x11.XdndSelection && _glfw.drag.window_id) {\n        // Handle TARGETS request for XdndSelection\n        if (request->target == _glfw.x11.TARGETS) {\n            // Return the list of supported MIME type atoms\n            Atom *targets = calloc(_glfw.x11.drag.type_count + 2, sizeof(Atom));\n            if (targets) {\n                targets[0] = _glfw.x11.TARGETS;\n                targets[1] = _glfw.x11.MULTIPLE;\n                for (size_t i = 0; i < _glfw.x11.drag.type_count; i++) targets[i + 2] = _glfw.x11.drag.type_atoms[i];\n                XChangeProperty(_glfw.x11.display,\n                                request->requestor,\n                                request->property,\n                                XA_ATOM,\n                                32,\n                                PropModeReplace,\n                                (unsigned char*)targets,\n                                _glfw.x11.drag.type_count + 2);\n                free(targets);\n                reply.xselection.property = request->property;\n            }\n        } else {\n            // Find the matching MIME type for the requested target\n            const char* mime_type = NULL;\n            for (size_t i = 0; i < _glfw.x11.drag.type_count; i++) {\n                if (_glfw.x11.drag.type_atoms[i] == request->target) {\n                    mime_type = _glfw.drag.items[i].mime_type;\n                    break;\n                }\n            }\n            if (mime_type) {\n                // Check if we have preset data\n                const char *data = NULL;\n                size_t data_sz = 0;\n                for (size_t i = 0; i < _glfw.drag.item_count; i++) {\n                    if (strcmp(_glfw.drag.items[i].mime_type, mime_type) == 0) {\n                        data = _glfw.drag.items[i].optional_data;\n                        data_sz = _glfw.drag.items[i].data_size;\n                        break;\n                    }\n                }\n\n                if (data && data_sz > 0) {\n                    // We have preset data, send it immediately\n                    send_drag_data(mime_type, data, data_sz, request->requestor,\n                                  request->property, request->target);\n                    reply.xselection.property = request->property;\n                } else {\n                    // Add to pending requests for on-demand data\n                    if (add_pending_request(mime_type, request->requestor,\n                                          request->property, request->target)) {\n                        // Request data from application\n                        _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n                        if (window) {\n                            GLFWDragEvent ev = {\n                                .type = GLFW_DRAG_DATA_REQUEST,\n                                .mime_type = mime_type,\n                                .data = NULL,\n                                .data_sz = 0,\n                                .err_num = 0\n                            };\n                            _glfwInputDragSourceRequest(window, &ev);\n\n                            if (ev.data && ev.data_sz > 0) {\n                                // Data provided synchronously\n                                send_drag_data(mime_type, ev.data, ev.data_sz,\n                                             request->requestor, request->property,\n                                             request->target);\n                                reply.xselection.property = request->property;\n                            } else {\n                                // Data will be provided asynchronously via _glfwPlatformDragDataReady\n                                // Don't send SelectionNotify yet - it will be sent when data is ready\n                                return;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    } else {\n        // Handle regular clipboard/primary selection\n        reply.xselection.property = writeTargetToProperty(request);\n    }\n    XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);\n}\n\nstatic void\ngetSelectionString(Atom selection, Atom *targets, size_t num_targets, GLFWclipboardwritedatafun write_data, void *object, bool report_not_found)\n{\n#define XFREE(x) { if (x) XFree(x); x = NULL; }\n    if (XGetSelectionOwner(_glfw.x11.display, selection) == _glfw.x11.helperWindowHandle) {\n        write_data(object, NULL, 1);\n        return;\n    }\n    bool found = false;\n    for (size_t i = 0; !found && i < num_targets; i++)\n    {\n        char* data = NULL;\n        Atom actualType = None;\n        int actualFormat = 0;\n        unsigned long itemCount = 0, bytesAfter = 0;\n        monotonic_t start = glfwGetTime();\n        XEvent notification, dummy;\n\n        XConvertSelection(_glfw.x11.display,\n                          selection,\n                          targets[i],\n                          _glfw.x11.GLFW_SELECTION,\n                          _glfw.x11.helperWindowHandle,\n                          CurrentTime);\n\n        while (!XCheckTypedWindowEvent(_glfw.x11.display,\n                                       _glfw.x11.helperWindowHandle,\n                                       SelectionNotify,\n                                       &notification))\n        {\n            monotonic_t time = glfwGetTime();\n            if (time - start > s_to_monotonic_t(2ll)) return;\n            waitForX11Event(s_to_monotonic_t(2ll) - (time - start));\n        }\n\n        if (notification.xselection.property == None)\n            continue;\n\n        XCheckIfEvent(_glfw.x11.display,\n                      &dummy,\n                      isSelPropNewValueNotify,\n                      (XPointer) &notification);\n\n        XGetWindowProperty(_glfw.x11.display,\n                           notification.xselection.requestor,\n                           notification.xselection.property,\n                           0,\n                           LONG_MAX,\n                           True,\n                           AnyPropertyType,\n                           &actualType,\n                           &actualFormat,\n                           &itemCount,\n                           &bytesAfter,\n                           (unsigned char**) &data);\n\n        if (actualType == _glfw.x11.INCR)\n        {\n            for (;;)\n            {\n                start = glfwGetTime();\n                while (!XCheckIfEvent(_glfw.x11.display,\n                                      &dummy,\n                                      isSelPropNewValueNotify,\n                                      (XPointer) &notification))\n                {\n                    monotonic_t time = glfwGetTime();\n                    if (time - start > s_to_monotonic_t(2ll)) {\n                        return;\n                    }\n                    waitForX11Event(s_to_monotonic_t(2ll) - (time - start));\n                }\n\n                XFREE(data);\n                XGetWindowProperty(_glfw.x11.display,\n                                   notification.xselection.requestor,\n                                   notification.xselection.property,\n                                   0,\n                                   LONG_MAX,\n                                   True,\n                                   AnyPropertyType,\n                                   &actualType,\n                                   &actualFormat,\n                                   &itemCount,\n                                   &bytesAfter,\n                                   (unsigned char**) &data);\n\n                if (itemCount)\n                {\n                    const char *string = data;\n                    if (targets[i] == XA_STRING) {\n                        string = convertLatin1toUTF8(data);\n                        itemCount = strlen(string);\n                    }\n                    bool ok = write_data(object, string, itemCount);\n                    if (string != data) free((void*)string);\n                    if (!ok) { XFREE(data); break; }\n                } else { found = true; break; }\n\n            }\n        }\n        else if (actualType == targets[i])\n        {\n            if (targets[i] == XA_STRING) {\n                const char *string = convertLatin1toUTF8(data);\n                write_data(object, string, strlen(string)); free((void*)string);\n            } else write_data(object, data, itemCount);\n            found = true;\n        }\n        else if (actualType == XA_ATOM && targets[i] == _glfw.x11.TARGETS) {\n            found = true;\n            write_data(object, data, sizeof(Atom) * itemCount);\n        }\n\n        XFREE(data);\n\n    }\n\n    if (!found && report_not_found)\n    {\n        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,\n                        \"X11: Failed to convert selection to data from clipboard\");\n    }\n#undef XFREE\n}\n\n// Make the specified window and its video mode active on its monitor\n//\nstatic void acquireMonitor(_GLFWwindow* window)\n{\n    if (_glfw.x11.saver.count == 0)\n    {\n        // Remember old screen saver settings\n        XGetScreenSaver(_glfw.x11.display,\n                        &_glfw.x11.saver.timeout,\n                        &_glfw.x11.saver.interval,\n                        &_glfw.x11.saver.blanking,\n                        &_glfw.x11.saver.exposure);\n\n        // Disable screen saver\n        XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,\n                        DefaultExposures);\n    }\n\n    if (!window->monitor->window)\n        _glfw.x11.saver.count++;\n\n    _glfwSetVideoModeX11(window->monitor, &window->videoMode);\n\n    _glfwInputMonitorWindow(window->monitor, window);\n}\n\n// Remove the window and restore the original video mode\n//\nstatic void releaseMonitor(_GLFWwindow* window)\n{\n    if (window->monitor->window != window)\n        return;\n\n    _glfwInputMonitorWindow(window->monitor, NULL);\n    _glfwRestoreVideoModeX11(window->monitor);\n\n    _glfw.x11.saver.count--;\n\n    if (_glfw.x11.saver.count == 0)\n    {\n        // Restore old screen saver settings\n        XSetScreenSaver(_glfw.x11.display,\n                        _glfw.x11.saver.timeout,\n                        _glfw.x11.saver.interval,\n                        _glfw.x11.saver.blanking,\n                        _glfw.x11.saver.exposure);\n    }\n}\n\nstatic void onConfigChange(void)\n{\n    float xscale, yscale;\n    _glfwGetSystemContentScaleX11(&xscale, &yscale, true);\n\n    if (xscale != _glfw.x11.contentScaleX || yscale != _glfw.x11.contentScaleY)\n    {\n        _GLFWwindow* window = _glfw.windowListHead;\n        _glfw.x11.contentScaleX = xscale;\n        _glfw.x11.contentScaleY = yscale;\n        while (window)\n        {\n            _glfwInputWindowContentScale(window, xscale, yscale);\n            window = window->next;\n        }\n    }\n}\n\nstatic void\nhandle_mouse_move_event(_GLFWwindow *window, const int x, const int y) {\n    if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY) {\n        // The cursor was moved by something other than GLFW\n        if (window->cursorMode == GLFW_CURSOR_DISABLED) {\n            if (_glfw.x11.disabledCursorWindow != window) return;\n            if (window->rawMouseMotion) return;\n            const int dx = x - window->x11.lastCursorPosX;\n            const int dy = y - window->x11.lastCursorPosY;\n            _glfwInputCursorPos(window, window->virtualCursorPosX + dx, window->virtualCursorPosY + dy);\n        } else _glfwInputCursorPos(window, x, y);\n    }\n    window->x11.lastCursorPosX = x;\n    window->x11.lastCursorPosY = y;\n}\n\nstatic bool\nnumber_has_fractional_part(double x) {\n    return fabs(x - round(x)) >= 1e-6;\n}\n\nstatic void\nhandle_xi_motion_event(_GLFWwindow *window, XIDeviceEvent *de) {\n    XIScrollDevice *d = NULL;\n    bool scroll_valuator_found = false;\n    for (unsigned i = 0; i < _glfw.x11.xi.num_scroll_devices; i++) {\n        XIScrollDevice *t = &_glfw.x11.xi.scroll_devices[i];\n        if (t->deviceid == de->deviceid && t->sourceid == de->sourceid) {\n            d = t; break;\n        }\n    }\n    if (d && de->valuators.mask_len){\n        double xOffset = 0, yOffset = 0;\n        // Process valuators to detect scroll events\n        GLFWOffsetType type = GLFW_SCROLL_OFFEST_HIGHRES;\n        unsigned vidx = 0;\n        for (int i = 0; i < de->valuators.mask_len * 8; i++) {\n            if (!XIMaskIsSet(de->valuators.mask, i)) continue;\n            const double value = de->valuators.values[vidx++];\n            XIScrollValuator *v = NULL;\n            for (unsigned k = 0; k < d->num_valuators; k++) {\n                XIScrollValuator *t = d->valuators + k;\n                if (t->number == i) { v = t; break; }\n            }\n            if (!v) continue;\n            scroll_valuator_found = true;\n            if (!v->initialized) {\n                v->initialized = true;\n                v->value = value;\n                continue;\n            }\n            double delta = value - v->value;\n            v->value = value;\n            delta *= -1;\n            double *off = v->is_vertical ? &yOffset : &xOffset;\n            *off = delta;\n            d->num_events++;\n            if (!d->type_detected) {\n                if (v->increment == 120.) {\n                    d->type_detected = true;\n                    d->offset_type = GLFW_SCROLL_OFFEST_V120;\n                } else {\n                    bool delta_is_fractional = number_has_fractional_part(delta);\n                    if (delta_is_fractional) {\n                        if (fabs(delta * 120 - round(delta * 120)) < 0.01) {\n                            d->type_detected = d->num_events > 2;\n                            d->offset_type = GLFW_SCROLL_OFFEST_V120;\n                        } else {\n                            d->type_detected = true;\n                            d->offset_type = GLFW_SCROLL_OFFEST_HIGHRES;\n                        }\n                    } else {\n                        d->type_detected = d->num_events > 2;\n                        d->offset_type = GLFW_SCROLL_OFFSET_LINES;\n                    }\n                }\n            }\n            if (d->offset_type == GLFW_SCROLL_OFFSET_LINES) {\n                if (v->increment != 0) *off /= v->increment;\n            }\n        }\n        type = d->offset_type;\n        if (xOffset != 0 || yOffset != 0) {\n            // Get keyboard modifiers\n            int mods = translateState(de->mods.effective);\n            // Scale offsets by content scale\n            GLFWScrollEvent ev = {\n                .keyboard_modifiers = mods,\n                .x_offset = xOffset * (type == GLFW_SCROLL_OFFEST_HIGHRES ? _glfw.x11.contentScaleX : 1),\n                .y_offset = yOffset * (type == GLFW_SCROLL_OFFEST_HIGHRES ? _glfw.x11.contentScaleY : 1),\n                .unscaled = {.x = xOffset, .y = yOffset},\n                .offset_type = type,\n            };\n\n            // For high-resolution, finger-based scrolling, use timer-based momentum scrolling\n            if (d->is_finger_based && type == GLFW_SCROLL_OFFEST_HIGHRES) {\n                // Reset the timer on each scroll event\n                x11_cancel_momentum_scroll_timer();\n\n                // Store the event for later use when timer fires\n                x11_momentum_scroll_state.window_id = window->id;\n                x11_momentum_scroll_state.last_event = ev;\n\n                // Start timer\n                x11_momentum_scroll_state.timer_id = glfwAddTimer(\n                    ms_to_monotonic_t(momentum_scroll_gesture_detection_timeout_ms), false,\n                    x11_scroll_stop_timer_callback, NULL, NULL);\n\n                // Send the scroll event through momentum handler\n                glfw_handle_scroll_event_for_momentum(window, &ev, false, true);\n            } else {\n                // Regular mouse wheel scrolling - no momentum\n                _glfwInputScroll(window, &ev);\n            }\n        }\n    }\n    if (!scroll_valuator_found) {\n        x11_cancel_momentum_scroll_timer();\n        glfw_cancel_momentum_scroll();\n        handle_mouse_move_event(window, (int)de->event_x, (int)de->event_y);\n    }\n}\n\n#define dnd _glfw.x11.xdnd\n// Dropping of data onto window {{{\n\nstatic void\nend_drop(_GLFWwindow *window, GLFWDragOperationType op) {\n    bool accepted = dnd.drag_accepted || dnd.dropped;\n    XEvent reply = { ClientMessage };\n    reply.xclient.window = dnd.source;\n    reply.xclient.message_type = _glfw.x11.XdndFinished;\n    reply.xclient.format = 32;\n    reply.xclient.data.l[0] = window->x11.handle;\n    reply.xclient.data.l[1] = accepted ? 1 : 0;\n    reply.xclient.data.l[2] = None;\n    if (dnd.version >= 5) {\n        switch(op) {\n            case GLFW_DRAG_OPERATION_COPY: reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; break;\n            case GLFW_DRAG_OPERATION_MOVE: reply.xclient.data.l[2] = _glfw.x11.XdndActionMove; break;\n            case GLFW_DRAG_OPERATION_GENERIC: reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; break;\n            case GLFW_DRAG_OPERATION_NONE: reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; break;\n        }\n    }\n    XSendEvent(_glfw.x11.display, dnd.source, False, NoEventMask, &reply);\n    XFlush(_glfw.x11.display);\n}\n\n\nstatic void\nupdate_drop_state(_GLFWwindow* window, size_t accepted_count) {\n    dnd.copy_mimes_count = accepted_count;\n    bool accepted = accepted_count > 0;\n    dnd.drag_accepted = accepted;\n    // The first entry in the accepted (sorted) copy is the preferred MIME.\n    const char* new_preferred_mime = (accepted && dnd.copy_mimes) ? dnd.copy_mimes[0] : NULL;\n    // Check if the preferred MIME changed\n    bool mime_changed = strncmp(new_preferred_mime ? new_preferred_mime : \"\", dnd.format, arraysz(dnd.format)) != 0;\n    if (mime_changed) {\n        if (new_preferred_mime) strncpy(dnd.format, new_preferred_mime, arraysz(dnd.format)-1);\n        else dnd.format[0] = 0;\n    }\n    if (accepted) {\n        XEvent reply = { ClientMessage };\n        reply.xclient.window = dnd.source;\n        reply.xclient.message_type = _glfw.x11.XdndStatus;\n        reply.xclient.format = 32;\n        reply.xclient.data.l[0] = window->x11.handle;\n        reply.xclient.data.l[2] = 0; // Specify an empty rectangle\n        reply.xclient.data.l[3] = 0;\n\n        if (dnd.format_priority > 0 && accepted) {\n            // Reply that we are ready to copy the dragged data\n            reply.xclient.data.l[1] = 1; // Accept with no rectangle\n            if (_glfw.x11.xdnd.version >= 2) reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;\n        }\n        XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, False, NoEventMask, &reply);\n        XFlush(_glfw.x11.display);\n    } else {\n        end_drop(window, GLFW_DRAG_OPERATION_GENERIC);\n    }\n}\n\nstatic void\nfree_dnd_mimes(void) {\n    // Free any previously cached MIME types\n    if (dnd.mimes) {\n        for (size_t j = 0; j < dnd.mimes_count; j++) {\n            if (dnd.mimes[j]) XFree((void*)dnd.mimes[j]);\n        }\n        free(dnd.mimes);\n        dnd.mimes = NULL;\n        dnd.mimes_count = 0;\n    }\n    free(dnd.copy_mimes);  // pointer array only; strings are owned by mimes[]\n    dnd.copy_mimes = NULL;\n    dnd.copy_mimes_count = 0;\n}\n\n// Reset the working copy of mimes so the next callback sees the full original\n// list.  Returns false on allocation failure.\nstatic bool\nreset_dnd_copy_mimes(void) {\n    if (dnd.mimes_count == 0) { dnd.copy_mimes_count = 0; return true; }\n    if (!dnd.copy_mimes) {\n        dnd.copy_mimes = malloc(dnd.mimes_count * sizeof(const char*));\n        if (!dnd.copy_mimes) return false;\n    }\n    memcpy(dnd.copy_mimes, dnd.mimes, dnd.mimes_count * sizeof(const char*));\n    dnd.copy_mimes_count = dnd.mimes_count;\n    return true;\n}\n\nvoid\nfree_dnd_data(void) {\n    dnd.source = None;\n    dnd.target_window = None;\n    dnd.drag_accepted = false;\n    free_dnd_mimes();\n    if (dnd.selection_requests) {\n        for (size_t i = 0; i < dnd.selection_requests_count; i++) {\n            free(dnd.selection_requests[i].mime);\n            if (dnd.selection_requests[i].data) XFree(dnd.selection_requests[i].data);\n        }\n        free(dnd.selection_requests); dnd.selection_requests = NULL;\n    }\n    dnd.selection_requests_count = 0; dnd.selection_requests_capacity = 0;\n    dnd.format[0] = 0;\n}\n\nstatic void\nupdate_dnd_mimes(XEvent *event) {\n    unsigned long i, count;\n    Atom* formats = NULL;\n    const bool list = event->xclient.data.l[1] & 1;\n    free_dnd_mimes();\n    // Get the MIME types before calling the callback\n    if (list) {\n        count = _glfwGetWindowPropertyX11(dnd.source, _glfw.x11.XdndTypeList, XA_ATOM, (unsigned char**) &formats);\n    } else {\n        count = 3;\n        formats = (Atom*) event->xclient.data.l + 2;\n    }\n\n    // Get atom names and store them in the xdnd structure\n    char **atom_names = calloc(count, sizeof(char*));\n    int valid_mime_count = 0;\n    if (atom_names) {\n        get_atom_names(formats, count, atom_names);\n        // Compact the array to only valid MIME types\n        for (i = 0; i < count; i++) {\n            if (atom_names[i]) {\n                if (valid_mime_count != (int)i) {\n                    atom_names[valid_mime_count] = atom_names[i];\n                    atom_names[i] = NULL;\n                }\n                valid_mime_count++;\n            }\n        }\n        // Store the MIME types for later use\n        dnd.mimes = (const char**)atom_names;\n        dnd.mimes_count = valid_mime_count;\n    }\n    if (list && formats) XFree(formats);\n}\n\nvoid\n_glfwPlatformEndDrop(GLFWwindow *w, GLFWDragOperationType op) {\n    end_drop((_GLFWwindow*)w, op);\n    free_dnd_data();\n}\n\n\nstatic void\ndrop_start(_GLFWwindow *window, XEvent *event) {\n    // A drag operation has entered the window\n    if (dnd.version > _GLFW_XDND_VERSION) return;\n    free_dnd_data();\n    dnd.source  = event->xclient.data.l[0];\n    dnd.version = event->xclient.data.l[1] >> 24;\n    dnd.target_window = window->x11.handle;\n    dnd.format[0] = 0;\n    dnd.format_priority  = 0;\n    update_dnd_mimes(event);\n    dnd.from_self = _glfw.x11.drag.source_window != None && dnd.source == _glfw.x11.drag.source_window;\n    // Position is not known yet at enter time, will be updated with XdndPosition\n    if (reset_dnd_copy_mimes()) {\n        size_t accepted_count = _glfwInputDropEvent(\n            window, GLFW_DROP_ENTER, 0, 0, dnd.copy_mimes, dnd.copy_mimes_count, dnd.from_self);\n        update_drop_state(window, accepted_count);\n        // Set format_priority when the callback accepted at least one MIME type.\n        // dnd.format has already been updated by update_drop_state.\n        if (accepted_count > 0) dnd.format_priority = 1;\n    }\n}\n\nstatic void\ndrop_leave(_GLFWwindow *window, XEvent *event UNUSED) {\n    // The drag operation has left the window\n    _glfwInputDropEvent(window, GLFW_DROP_LEAVE, 0, 0, NULL, 0, dnd.from_self);\n    if (!dnd.dropped) {\n        free_dnd_data();\n    }\n}\n\nstatic void\ndrop_move(_GLFWwindow *window, XEvent *event) {\n    // The drag operation has moved over the window\n    if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) return;\n\n    const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;\n    const int yabs = (event->xclient.data.l[2]) & 0xffff;\n    Window dummy;\n    int xpos = 0, ypos = 0;\n    _glfwGrabErrorHandlerX11();\n    XTranslateCoordinates(_glfw.x11.display, _glfw.x11.root, window->x11.handle, xabs, yabs, &xpos, &ypos, &dummy);\n    _glfwReleaseErrorHandlerX11();\n    if (_glfw.x11.errorCode != Success) _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Failed to get DND event position\");\n    _glfwInputCursorPos(window, xpos, ypos);\n    if (reset_dnd_copy_mimes()) {\n        size_t mimes_count = _glfwInputDropEvent(window, GLFW_DROP_MOVE, xpos, ypos, dnd.copy_mimes, dnd.copy_mimes_count, dnd.from_self);\n        update_drop_state(window, mimes_count);\n    }\n}\n\nvoid\n_glfwPlatformRequestDropUpdate(_GLFWwindow* window) {\n    // Check if there's an active drag over this window\n    if (dnd.source == None || dnd.target_window != window->x11.handle) return;\n    // Call the drag callback with STATUS_UPDATE event to get updated state\n    // Position values are not valid for this event type\n    if (reset_dnd_copy_mimes()) {\n        size_t mimes_count = _glfwInputDropEvent(window, GLFW_DROP_STATUS_UPDATE, 0, 0, dnd.copy_mimes, dnd.copy_mimes_count, dnd.from_self);\n        update_drop_state(window, mimes_count);\n    }\n}\n\n\nstatic void\ndrop(_GLFWwindow *window, XEvent *event) {\n    // The drag operation has finished by dropping on the window\n    if (dnd.version > _GLFW_XDND_VERSION || dnd.version < 2) return;\n    dnd.dropped = true;\n    dnd.drop_time = (unsigned long)event->xclient.data.l[2];\n    if (!reset_dnd_copy_mimes()) return;\n    size_t num_accepted = _glfwInputDropEvent(window, GLFW_DROP_DROP, 0, 0, dnd.copy_mimes, dnd.copy_mimes_count, dnd.from_self);\n    if (!dnd.copy_mimes) return;\n    for (size_t i = 0; i < num_accepted; i++) _glfwPlatformRequestDropData(window, dnd.copy_mimes[i]);\n}\n\nstatic void\nrequest_selection(_GLFWwindow *window, XdndSelectionRequest *r) {\n    Atom target_atom = XInternAtom(_glfw.x11.display, r->mime, False);\n    XConvertSelection(_glfw.x11.display,\n                        _glfw.x11.XdndSelection,\n                        target_atom,\n                        _glfw.x11.XdndSelection,\n                        window->x11.handle,\n                        dnd.drop_time);\n    XFlush(_glfw.x11.display);\n    r->inflight = true;\n}\n\ntypedef struct {\n    char *mime;\n    GLFWid window_id;\n} notify_data;\n\nstatic void\nfree_notify_data(unsigned long long timer_id UNUSED, void *x) {\n    notify_data *d = x;\n    free(d->mime); free(x);\n}\n\nstatic void\nnotify_drop_data_available(unsigned long long timer_id UNUSED, void *x) {\n    notify_data *d = x;\n    const char * mimes[1] = {d->mime};\n    _GLFWwindow *window = _glfwWindowForId(d->window_id);\n    if (window) _glfwInputDropEvent(window, GLFW_DROP_DATA_AVAILABLE, 0, 0, mimes, 1, dnd.from_self);\n}\n\nssize_t\n_glfwPlatformReadAvailableDropData(GLFWwindow *w, GLFWDropEvent *ev, char *buffer, size_t sz) {\n    _GLFWwindow *window = (_GLFWwindow*)w;\n    const char *mime = ev->mimes[0];\n    for (size_t i = 0; i < dnd.selection_requests_count; i++) {\n        XdndSelectionRequest *r = dnd.selection_requests + i;\n        if (strcmp(r->mime, mime) == 0) {\n            if (!r->got_data) return -EINVAL;\n            if (r->offset >= r->size) return 0;\n            size_t to_read = MIN(sz, r->size - r->offset);\n            memcpy(buffer, r->data, to_read);\n            r->offset += to_read;\n            if (to_read) {\n                notify_data *d = malloc(sizeof(notify_data));\n                if (d) {\n                    d->mime = _glfw_strdup(mime);\n                    d->window_id = window->id;\n                    _glfwPlatformAddTimer(0, false, notify_drop_data_available, d, free_notify_data);\n                }\n            }\n            return to_read;\n        }\n    }\n    return -ENOENT;\n}\n\nstatic XdndSelectionRequest*\ninflight_selection_request(void) {\n    for (size_t i = 0; i < dnd.selection_requests_count; i++) {\n        XdndSelectionRequest *r = dnd.selection_requests + i;\n        if (r->inflight) return r;\n    }\n    return NULL;\n}\n\nint\n_glfwPlatformRequestDropData(_GLFWwindow *window, const char *mime) {\n    if (!dnd.mimes) return EINVAL;\n    for (size_t i = 0; i < dnd.selection_requests_count; i++) {\n        XdndSelectionRequest *r = dnd.selection_requests + i;\n        if (strcmp(r->mime, mime) == 0) {\n            if (r->got_data) {\n                r->offset = 0;\n                const char * mimes[1] = {mime};\n                _glfwInputDropEvent(window, GLFW_DROP_DATA_AVAILABLE, 0, 0, mimes, 1, dnd.from_self);\n            }\n            return 0;\n        }\n    }\n    if (!dnd.selection_requests || dnd.selection_requests_count + 1 >= dnd.selection_requests_capacity) {\n        dnd.selection_requests_capacity = dnd.selection_requests_count + 8;\n        dnd.selection_requests = realloc(\n            dnd.selection_requests, sizeof(dnd.selection_requests[0]) * dnd.selection_requests_capacity);\n        if (!dnd.selection_requests) return ENOMEM;\n    }\n    XdndSelectionRequest *r = dnd.selection_requests + dnd.selection_requests_count++;\n    memset(r, 0, sizeof(r[0]));\n    r->mime = _glfw_strdup(mime);\n    if (!inflight_selection_request()) request_selection(window, r);\n    return 0;\n}\n\nstatic void\ndrop_selection_notify(_GLFWwindow *window, XEvent *event) {  // requested drop data is available\n    XdndSelectionRequest *r = inflight_selection_request();\n    if (!r) return;\n    r->size = _glfwGetWindowPropertyX11(\n        event->xselection.requestor, event->xselection.property, event->xselection.target, &r->data);\n    r->inflight = false; r->got_data = true;\n    const char * mimes[1] = {r->mime};\n    _glfwInputDropEvent(window, GLFW_DROP_DATA_AVAILABLE, 0, 0, mimes, 1, dnd.from_self);\n    for (size_t i = 0; i < dnd.selection_requests_count; i++) {\n        XdndSelectionRequest *r = dnd.selection_requests + i;\n        if (!r->got_data) {\n            request_selection(window, r);\n            break;\n        }\n    }\n}\n\n\n// }}}\n#undef dnd\n\n// Process the specified X event\n//\nstatic void processEvent(XEvent *event)\n{\n    static bool keymap_dirty = false;\n#define UPDATE_KEYMAP_IF_NEEDED if (keymap_dirty) { keymap_dirty = false; glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL); }\n\n    if (_glfw.x11.randr.available)\n    {\n        if (event->type == _glfw.x11.randr.eventBase + RRNotify)\n        {\n            XRRUpdateConfiguration(event);\n            _glfwPollMonitorsX11();\n            return;\n        }\n    }\n\n    if (event->type == PropertyNotify &&\n        event->xproperty.window == _glfw.x11.root &&\n        event->xproperty.atom == _glfw.x11.RESOURCE_MANAGER)\n    {\n        onConfigChange();\n        return;\n    }\n\n    if (event->type == GenericEvent)\n    {\n        if (_glfw.x11.xi.available &&\n            event->xcookie.extension == _glfw.x11.xi.majorOpcode)\n        {\n            if (XGetEventData(_glfw.x11.display, &event->xcookie))\n            {\n                // Handle XI_RawMotion for disabled cursor\n                if (event->xcookie.evtype == XI_RawMotion)\n                {\n                    _GLFWwindow* window = _glfw.x11.disabledCursorWindow;\n                    if (window && window->rawMouseMotion)\n                    {\n                        XIRawEvent* re = event->xcookie.data;\n                        if (re->valuators.mask_len)\n                        {\n                            const double* values = re->raw_values;\n                            double xpos = window->virtualCursorPosX;\n                            double ypos = window->virtualCursorPosY;\n\n                            if (XIMaskIsSet(re->valuators.mask, 0))\n                            {\n                                xpos += *values;\n                                values++;\n                            }\n\n                            if (XIMaskIsSet(re->valuators.mask, 1))\n                                ypos += *values;\n\n                            _glfwInputCursorPos(window, xpos, ypos);\n                        }\n                    }\n                }\n                // Handle XI_Motion for smooth scrolling\n                else if (event->xcookie.evtype == XI_Motion)\n                {\n                    XIDeviceEvent* de = (XIDeviceEvent*)event->xcookie.data;\n                    if (de->deviceid != _glfw.x11.xi.master_pointer_id) {\n                        // Find the window for this event\n                        _GLFWwindow* window = NULL;\n                        if (XFindContext(_glfw.x11.display, de->event, _glfw.x11.context, (XPointer*)&window) == 0)\n                            handle_xi_motion_event(window, de);\n                    }\n                }\n                // Handle XI_HierarchyChanged for device hotplug\n                else if (event->xcookie.evtype == XI_HierarchyChanged)\n                {\n                    XIHierarchyEvent* he = (XIHierarchyEvent*)event->xcookie.data;\n                    // Check if any devices were added or removed\n                    for (int i = 0; i < he->num_info; i++) {\n                        if (he->info[i].flags & (XISlaveAdded | XISlaveRemoved |\n                                                  XIMasterAdded | XIMasterRemoved)) {\n                            // Re-read scroll devices when devices are added or removed\n                            read_xi_scroll_devices();\n                            break;\n                        }\n                    }\n                }\n                XFreeEventData(_glfw.x11.display, &event->xcookie);\n            }\n        }\n\n        return;\n    }\n\n    if (event->type == SelectionClear)\n    {\n        handleSelectionClear(event);\n        return;\n    }\n    else if (event->type == SelectionRequest)\n    {\n        handleSelectionRequest(event);\n        return;\n    }\n    else if (event->type == _glfw.x11.xkb.eventBase)\n    {\n        XkbEvent *kb_event = (XkbEvent*)event;\n        if (kb_event->any.device != (unsigned int)_glfw.x11.xkb.keyboard_device_id) return;\n        switch(kb_event->any.xkb_type) {\n            case XkbNewKeyboardNotify: {\n                XkbNewKeyboardNotifyEvent *newkb_event = (XkbNewKeyboardNotifyEvent*)kb_event;\n                if (_glfw.hints.init.debugKeyboard) printf(\n                        \"Got XkbNewKeyboardNotify event with changes: key codes: %d geometry: %d device id: %d\\n\",\n                        !!(newkb_event->changed & XkbNKN_KeycodesMask), !!(newkb_event->changed & XkbNKN_GeometryMask),\n                        !!(newkb_event->changed & XkbNKN_DeviceIDMask));\n                if (newkb_event->changed & XkbNKN_DeviceIDMask) {\n                    keymap_dirty = true;\n                    if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return;\n                }\n                if (newkb_event->changed & XkbNKN_KeycodesMask) {\n                    keymap_dirty = true;\n                }\n                return;\n            }\n            case XkbMapNotify:\n            {\n                if (_glfw.hints.init.debugKeyboard) printf(\"Got XkbMapNotify event, keymaps will be reloaded\\n\");\n                keymap_dirty = true;\n                return;\n            }\n            case XkbStateNotify:\n            {\n                UPDATE_KEYMAP_IF_NEEDED;\n                XkbStateNotifyEvent *state_event = (XkbStateNotifyEvent*)kb_event;\n                glfw_xkb_update_modifiers(\n                        &_glfw.x11.xkb, state_event->base_mods, state_event->latched_mods,\n                        state_event->locked_mods, state_event->base_group, state_event->latched_group, state_event->locked_group\n                );\n                return;\n            }\n        }\n        return;\n    }\n\n    _GLFWwindow* window = NULL;\n    if (XFindContext(_glfw.x11.display,\n                     event->xany.window,\n                     _glfw.x11.context,\n                     (XPointer*) &window) != 0)\n    {\n        // This is an event for a window that has already been destroyed\n        return;\n    }\n\n    switch (event->type)\n    {\n        case ReparentNotify:\n        {\n            window->x11.parent = event->xreparent.parent;\n            return;\n        }\n\n        case KeyPress:\n        {\n            UPDATE_KEYMAP_IF_NEEDED;\n            x11_cancel_momentum_scroll_timer();\n            glfw_cancel_momentum_scroll();\n            glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_PRESS);\n            return;\n        }\n\n        case KeyRelease:\n        {\n            UPDATE_KEYMAP_IF_NEEDED;\n            x11_cancel_momentum_scroll_timer();\n            glfw_cancel_momentum_scroll();\n            if (!_glfw.x11.xkb.detectable)\n            {\n                // HACK: Key repeat events will arrive as KeyRelease/KeyPress\n                //       pairs with similar or identical time stamps\n                //       The key repeat logic in _glfwInputKey expects only key\n                //       presses to repeat, so detect and discard release events\n                if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))\n                {\n                    XEvent next;\n                    XPeekEvent(_glfw.x11.display, &next);\n\n                    if (next.type == KeyPress &&\n                        next.xkey.window == event->xkey.window &&\n                        next.xkey.keycode == event->xkey.keycode)\n                    {\n                        // HACK: The time of repeat events sometimes doesn't\n                        //       match that of the press event, so add an\n                        //       epsilon\n                        //       Toshiyuki Takahashi can press a button\n                        //       16 times per second so it's fairly safe to\n                        //       assume that no human is pressing the key 50\n                        //       times per second (value is ms)\n                        if ((next.xkey.time - event->xkey.time) < 20)\n                        {\n                            // This is very likely a server-generated key repeat\n                            // event, so ignore it\n                            return;\n                        }\n                    }\n                }\n            }\n\n            glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_RELEASE);\n            return;\n        }\n\n        case ButtonPress:\n        {\n            const int mods = translateState(event->xbutton.state);\n\n#define cancel_momentum() x11_cancel_momentum_scroll_timer(); glfw_cancel_momentum_scroll()\n\n            if (event->xbutton.button == Button1) {\n                cancel_momentum();\n                _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);\n            } else if (event->xbutton.button == Button2) {\n                cancel_momentum();\n                _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);\n            } else if (event->xbutton.button == Button3) {\n                cancel_momentum();\n                _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);\n            }\n\n            // Modern X provides scroll events as mouse button presses\n            // Only use these if smooth scrolling is not available\n            else if (event->xbutton.button == Button4)\n            {\n                if (!_glfw.x11.xi.num_scroll_devices)\n                    _glfwInputScroll(window, &(GLFWScrollEvent){.keyboard_modifiers=mods, .y_offset=1, .unscaled.y=1});\n            }\n            else if (event->xbutton.button == Button5)\n            {\n                if (!_glfw.x11.xi.num_scroll_devices)\n                    _glfwInputScroll(window, &(GLFWScrollEvent){.keyboard_modifiers=mods, .y_offset=-1, .unscaled.y=-1});\n            }\n            else if (event->xbutton.button == Button6)\n            {\n                if (!_glfw.x11.xi.num_scroll_devices)\n                    _glfwInputScroll(window, &(GLFWScrollEvent){.keyboard_modifiers=mods, .x_offset=1, .unscaled.x=1});\n            }\n            else if (event->xbutton.button == Button7)\n            {\n                if (!_glfw.x11.xi.num_scroll_devices)\n                    _glfwInputScroll(window, &(GLFWScrollEvent){.keyboard_modifiers=mods, .x_offset=-1, .unscaled.x=-1});\n            }\n\n            else\n            {\n                cancel_momentum();\n                // Additional buttons after 7 are treated as regular buttons\n                // We subtract 4 to fill the gap left by scroll input above\n                _glfwInputMouseClick(window,\n                                     event->xbutton.button - Button1 - 4,\n                                     GLFW_PRESS,\n                                     mods);\n            }\n\n            return;\n        }\n\n        case ButtonRelease:\n        {\n            const int mods = translateState(event->xbutton.state);\n\n            // Handle drag drop on button release\n            if (_glfw.x11.drag.active && event->xbutton.button == Button1) {\n                handle_drag_button_release(event->xbutton.time);\n                return;\n            }\n\n            if (event->xbutton.button == Button1)\n            {\n                cancel_momentum();\n                _glfwInputMouseClick(window,\n                                     GLFW_MOUSE_BUTTON_LEFT,\n                                     GLFW_RELEASE,\n                                     mods);\n            }\n            else if (event->xbutton.button == Button2)\n            {\n                cancel_momentum();\n                _glfwInputMouseClick(window,\n                                     GLFW_MOUSE_BUTTON_MIDDLE,\n                                     GLFW_RELEASE,\n                                     mods);\n            }\n            else if (event->xbutton.button == Button3)\n            {\n                cancel_momentum();\n                _glfwInputMouseClick(window,\n                                     GLFW_MOUSE_BUTTON_RIGHT,\n                                     GLFW_RELEASE,\n                                     mods);\n            }\n            else if (event->xbutton.button > Button7)\n            {\n                cancel_momentum();\n                // Additional buttons after 7 are treated as regular buttons\n                // We subtract 4 to fill the gap left by scroll input above\n                _glfwInputMouseClick(window,\n                                     event->xbutton.button - Button1 - 4,\n                                     GLFW_RELEASE,\n                                     mods);\n            }\n\n            return;\n#undef cancel_momentum\n        }\n\n        case EnterNotify:\n        {\n            // XEnterWindowEvent is XCrossingEvent\n            const int x = event->xcrossing.x;\n            const int y = event->xcrossing.y;\n\n            // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise\n            //       ignore the defined cursor for hidden cursor mode\n            if (window->cursorMode == GLFW_CURSOR_HIDDEN)\n                updateCursorImage(window);\n\n            _glfwInputCursorEnter(window, true);\n            _glfwInputCursorPos(window, x, y);\n\n            window->x11.lastCursorPosX = x;\n            window->x11.lastCursorPosY = y;\n            return;\n        }\n\n        case LeaveNotify:\n        {\n            resetScrollValuators();\n            _glfwInputCursorEnter(window, false);\n            return;\n        }\n\n        case MotionNotify:\n        {\n            // Handle drag motion\n            if (_glfw.x11.drag.active) {\n                int root_x, root_y;\n                Window child;\n                XTranslateCoordinates(_glfw.x11.display,\n                                     event->xmotion.window, _glfw.x11.root,\n                                     event->xmotion.x, event->xmotion.y,\n                                     &root_x, &root_y, &child);\n                handle_drag_motion(root_x, root_y, event->xmotion.time);\n            }\n\n            x11_cancel_momentum_scroll_timer();\n            glfw_cancel_momentum_scroll();\n            handle_mouse_move_event(window, event->xmotion.x, event->xmotion.y);\n            return;\n        }\n\n        case ConfigureNotify:\n        {\n            if (event->xconfigure.width != window->x11.width ||\n                event->xconfigure.height != window->x11.height)\n            {\n                debug_rendering(\"Window resized to: %d %d from: %d %d\\n\", event->xconfigure.width, event->xconfigure.height, window->x11.width, window->x11.height);\n                _glfwInputFramebufferSize(window,\n                                          event->xconfigure.width,\n                                          event->xconfigure.height);\n\n                _glfwInputWindowSize(window,\n                                     event->xconfigure.width,\n                                     event->xconfigure.height);\n\n                window->x11.width = event->xconfigure.width;\n                window->x11.height = event->xconfigure.height;\n            }\n\n            int xpos = event->xconfigure.x;\n            int ypos = event->xconfigure.y;\n\n            // NOTE: ConfigureNotify events from the server are in local\n            //       coordinates, so if we are reparented we need to translate\n            //       the position into root (screen) coordinates\n            if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)\n            {\n                Window dummy;\n                _glfwGrabErrorHandlerX11();\n                XTranslateCoordinates(_glfw.x11.display,\n                                      window->x11.parent,\n                                      _glfw.x11.root,\n                                      xpos, ypos,\n                                      &xpos, &ypos,\n                                      &dummy);\n                _glfwReleaseErrorHandlerX11();\n                if (_glfw.x11.errorCode != Success) {\n                    _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Failed to translate ConfigureNotiy co-ords for reparented window\");\n                    return;\n                }\n            }\n            if (xpos != window->x11.xpos || ypos != window->x11.ypos)\n            {\n                debug_rendering(\"Window moved to: %d %d from: %d %d\\n\", xpos, ypos, window->x11.xpos, window->x11.xpos);\n                _glfwInputWindowPos(window, xpos, ypos);\n                window->x11.xpos = xpos;\n                window->x11.ypos = ypos;\n            }\n\n            return;\n        }\n\n        case ClientMessage:\n        {\n            // Custom client message, probably from the window manager\n\n            if (event->xclient.message_type == None) return;\n            if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS) {\n                const Atom protocol = event->xclient.data.l[0];\n                if (protocol == None)\n                    return;\n\n                if (protocol == _glfw.x11.WM_DELETE_WINDOW)\n                {\n                    // The window manager was asked to close the window, for\n                    // example by the user pressing a 'close' window decoration\n                    // button\n                    _glfwInputWindowCloseRequest(window);\n                }\n                else if (protocol == _glfw.x11.NET_WM_PING)\n                {\n                    // The window manager is pinging the application to ensure\n                    // it's still responding to events\n\n                    XEvent reply = *event;\n                    reply.xclient.window = _glfw.x11.root;\n\n                    XSendEvent(_glfw.x11.display, _glfw.x11.root,\n                               False,\n                               SubstructureNotifyMask | SubstructureRedirectMask,\n                               &reply);\n                }\n            }\n            else if (event->xclient.message_type == _glfw.x11.XdndEnter) { drop_start(window, event); }\n            else if (event->xclient.message_type == _glfw.x11.XdndDrop) { drop(window, event); }\n            else if (event->xclient.message_type == _glfw.x11.XdndLeave) { drop_leave(window, event); }\n            else if (event->xclient.message_type == _glfw.x11.XdndPosition) { drop_move(window, event); }\n            else if (event->xclient.message_type == _glfw.x11.XdndStatus) { handle_xdnd_status(&event->xclient); }\n            else if (event->xclient.message_type == _glfw.x11.XdndFinished) { handle_xdnd_finished(&event->xclient); }\n            return;\n        }\n\n        case SelectionNotify:\n            if (event->type == SelectionNotify && event->xselection.selection == _glfw.x11.XdndSelection &&\n                event->xselection.property == _glfw.x11.XdndSelection) drop_selection_notify(window, event);\n            return;\n\n        case FocusIn:\n        {\n            if (event->xfocus.mode == NotifyGrab ||\n                event->xfocus.mode == NotifyUngrab)\n            {\n                // Ignore focus events from popup indicator windows, window menu\n                // key chords and window dragging\n                return;\n            }\n\n            if (window->cursorMode == GLFW_CURSOR_DISABLED)\n                disableCursor(window);\n\n            _glfwInputWindowFocus(window, true);\n            return;\n        }\n\n        case FocusOut:\n        {\n            if (event->xfocus.mode == NotifyGrab ||\n                event->xfocus.mode == NotifyUngrab)\n            {\n                // Ignore focus events from popup indicator windows, window menu\n                // key chords and window dragging\n                return;\n            }\n\n            if (window->cursorMode == GLFW_CURSOR_DISABLED)\n                enableCursor(window);\n\n            if (window->monitor && window->autoIconify)\n                _glfwPlatformIconifyWindow(window);\n\n            resetScrollValuators();\n            _glfwInputWindowFocus(window, false);\n            return;\n        }\n\n        case Expose:\n        {\n            _glfwInputWindowDamage(window);\n            return;\n        }\n\n        case PropertyNotify:\n        {\n            if (event->xproperty.state != PropertyNewValue)\n                return;\n\n            if (event->xproperty.atom == _glfw.x11.WM_STATE)\n            {\n                const int state = getWindowState(window);\n                if (state != IconicState && state != NormalState)\n                    return;\n\n                const bool iconified = (state == IconicState);\n                if (window->x11.iconified != iconified)\n                {\n                    if (window->monitor)\n                    {\n                        if (iconified)\n                            releaseMonitor(window);\n                        else\n                            acquireMonitor(window);\n                    }\n\n                    window->x11.iconified = iconified;\n                    _glfwInputWindowIconify(window, iconified);\n                }\n            }\n            else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)\n            {\n                const bool maximized = _glfwPlatformWindowMaximized(window);\n                if (window->x11.maximized != maximized)\n                {\n                    window->x11.maximized = maximized;\n                    int width, height;\n                    _glfwPlatformGetWindowSize(window, &width, &height);\n                    updateNormalHints(window, width, height);\n                    _glfwInputWindowMaximize(window, maximized);\n                }\n            }\n\n            return;\n        }\n\n        case DestroyNotify:\n            return;\n    }\n#undef UPDATE_KEYMAP_IF_NEEDED\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW internal API                      //////\n//////////////////////////////////////////////////////////////////////////\n\n// Retrieve a single window property of the specified type\n// Inspired by fghGetWindowProperty from freeglut\n//\nunsigned long _glfwGetWindowPropertyX11(Window window,\n                                        Atom property,\n                                        Atom type,\n                                        unsigned char** value)\n{\n    Atom actualType;\n    int actualFormat;\n    unsigned long itemCount, bytesAfter;\n\n    XGetWindowProperty(_glfw.x11.display,\n                       window,\n                       property,\n                       0,\n                       LONG_MAX,\n                       False,\n                       type,\n                       &actualType,\n                       &actualFormat,\n                       &itemCount,\n                       &bytesAfter,\n                       value);\n\n    return itemCount;\n}\n\nbool _glfwIsVisualTransparentX11(Visual* visual)\n{\n    if (!_glfw.x11.xrender.available)\n        return false;\n\n    XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);\n    return pf && pf->direct.alphaMask;\n}\n\n// Push contents of our selection to clipboard manager\n//\nvoid _glfwPushSelectionToManagerX11(void)\n{\n    XConvertSelection(_glfw.x11.display,\n                      _glfw.x11.CLIPBOARD_MANAGER,\n                      _glfw.x11.SAVE_TARGETS,\n                      None,\n                      _glfw.x11.helperWindowHandle,\n                      CurrentTime);\n\n    for (;;)\n    {\n        XEvent event;\n\n        while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))\n        {\n            switch (event.type)\n            {\n                case SelectionRequest:\n                    handleSelectionRequest(&event);\n                    break;\n\n                case SelectionClear:\n                    handleSelectionClear(&event);\n                    break;\n\n                case SelectionNotify:\n                {\n                    if (event.xselection.target == _glfw.x11.SAVE_TARGETS)\n                    {\n                        // This means one of two things; either the selection\n                        // was not owned, which means there is no clipboard\n                        // manager, or the transfer to the clipboard manager has\n                        // completed\n                        // In either case, it means we are done here\n                        return;\n                    }\n\n                    break;\n                }\n            }\n        }\n\n        waitForX11Event(-1);\n    }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n//////                       GLFW platform API                      //////\n//////////////////////////////////////////////////////////////////////////\n\nint _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, const GLFWLayerShellConfig *lsc)\n{\n    Visual* visual = NULL;\n    int depth;\n    if (lsc) {\n        window->x11.layer_shell.is_active = true;\n        window->x11.layer_shell.config = *lsc;\n    } else window->x11.layer_shell.is_active = false;\n\n    if (ctxconfig->client != GLFW_NO_API)\n    {\n        if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)\n        {\n            if (!_glfwInitGLX())\n                return false;\n            if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))\n                return false;\n        }\n        else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)\n        {\n            if (!_glfwInitEGL())\n                return false;\n            if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))\n                return false;\n        }\n        else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)\n        {\n            if (!_glfwInitOSMesa())\n                return false;\n        }\n    }\n\n    if (!visual)\n    {\n        visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);\n        depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);\n    }\n\n    if (!createNativeWindow(window, wndconfig, visual, depth))\n        return false;\n\n    if (ctxconfig->client != GLFW_NO_API)\n    {\n        if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)\n        {\n            if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))\n                return false;\n        }\n        else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)\n        {\n            if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))\n                return false;\n        }\n        else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)\n        {\n            if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))\n                return false;\n        }\n    }\n\n    if (window->monitor)\n    {\n        _glfwPlatformShowWindow(window, false);\n        updateWindowMode(window);\n        acquireMonitor(window);\n    }\n\n    XFlush(_glfw.x11.display);\n    return true;\n}\n\nvoid _glfwPlatformDestroyWindow(_GLFWwindow* window)\n{\n    if (_glfw.x11.disabledCursorWindow == window)\n        _glfw.x11.disabledCursorWindow = NULL;\n\n    if (window->monitor)\n        releaseMonitor(window);\n\n    if (window->context.destroy)\n        window->context.destroy(window);\n\n    if (window->x11.handle)\n    {\n        XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);\n        XUnmapWindow(_glfw.x11.display, window->x11.handle);\n        XDestroyWindow(_glfw.x11.display, window->x11.handle);\n        window->x11.handle = (Window) 0;\n    }\n\n    if (window->x11.colormap)\n    {\n        XFreeColormap(_glfw.x11.display, window->x11.colormap);\n        window->x11.colormap = (Colormap) 0;\n    }\n\n    XFlush(_glfw.x11.display);\n}\n\nconst GLFWLayerShellConfig*\n_glfwPlatformGetLayerShellConfig(_GLFWwindow *window) {\n    return &window->x11.layer_shell.config;\n}\n\nbool\n_glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig *value) {\n    if (value) window->x11.layer_shell.config = *value;\n    WindowGeometry wg = calculate_layer_geometry(window);\n    update_wm_hints(window, &wg, NULL);\n    return false;\n}\n\nvoid _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)\n{\n#if defined(X_HAVE_UTF8_STRING)\n    Xutf8SetWMProperties(_glfw.x11.display,\n                         window->x11.handle,\n                         title, title,\n                         NULL, 0,\n                         NULL, NULL, NULL);\n#else\n    // This may be a slightly better fallback than using XStoreName and\n    // XSetIconName, which always store their arguments using STRING\n    XmbSetWMProperties(_glfw.x11.display,\n                       window->x11.handle,\n                       title, title,\n                       NULL, 0,\n                       NULL, NULL, NULL);\n#endif\n\n    XChangeProperty(_glfw.x11.display,  window->x11.handle,\n                    _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,\n                    PropModeReplace,\n                    (unsigned char*) title, strlen(title));\n\n    XChangeProperty(_glfw.x11.display,  window->x11.handle,\n                    _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,\n                    PropModeReplace,\n                    (unsigned char*) title, strlen(title));\n\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformSetWindowIcon(_GLFWwindow* window,\n                                int count, const GLFWimage* images)\n{\n    if (count)\n    {\n        int i, j, longCount = 0;\n\n        for (i = 0;  i < count;  i++)\n            longCount += 2 + images[i].width * images[i].height;\n\n        unsigned long* icon = calloc(longCount, sizeof(unsigned long));\n        unsigned long* target = icon;\n\n        for (i = 0;  i < count;  i++)\n        {\n            *target++ = images[i].width;\n            *target++ = images[i].height;\n\n            for (j = 0;  j < images[i].width * images[i].height;  j++)\n            {\n                const unsigned char *p = images->pixels + j * 4;\n                const unsigned char r = *p++, g = *p++, b = *p++, a = *p++;\n                *target++ = a << 24 | (r << 16) | (g << 8) | b;\n            }\n        }\n\n        XChangeProperty(_glfw.x11.display, window->x11.handle,\n                        _glfw.x11.NET_WM_ICON,\n                        XA_CARDINAL, 32,\n                        PropModeReplace,\n                        (unsigned char*) icon,\n                        longCount);\n\n        free(icon);\n    }\n    else\n    {\n        XDeleteProperty(_glfw.x11.display, window->x11.handle,\n                        _glfw.x11.NET_WM_ICON);\n    }\n\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)\n{\n    Window dummy;\n    int x = 0, y = 0;\n\n    _glfwGrabErrorHandlerX11();\n    XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,\n                          0, 0, &x, &y, &dummy);\n    _glfwReleaseErrorHandlerX11();\n    if (_glfw.x11.errorCode != Success)\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Failed to get window position\");\n\n    if (xpos)\n        *xpos = x;\n    if (ypos)\n        *ypos = y;\n}\n\nvoid _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)\n{\n    // HACK: Explicitly setting PPosition to any value causes some WMs, notably\n    //       Compiz and Metacity, to honor the position of unmapped windows\n    if (!_glfwPlatformWindowVisible(window))\n    {\n        long supplied;\n        XSizeHints* hints = XAllocSizeHints();\n\n        if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))\n        {\n            hints->flags |= PPosition;\n            hints->x = hints->y = 0;\n\n            XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);\n        }\n\n        XFree(hints);\n    }\n\n    XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)\n{\n    XWindowAttributes attribs;\n    XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);\n\n    if (width)\n        *width = attribs.width;\n    if (height)\n        *height = attribs.height;\n}\n\nvoid _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)\n{\n    if (window->monitor)\n    {\n        if (window->monitor->window == window)\n            acquireMonitor(window);\n    }\n    else\n    {\n        if (!window->resizable)\n            updateNormalHints(window, width, height);\n\n        XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);\n    }\n\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,\n                                      int minwidth UNUSED, int minheight UNUSED,\n                                      int maxwidth UNUSED, int maxheight UNUSED)\n{\n    int width, height;\n    _glfwPlatformGetWindowSize(window, &width, &height);\n    updateNormalHints(window, width, height);\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer UNUSED, int denom UNUSED)\n{\n    int width, height;\n    _glfwPlatformGetWindowSize(window, &width, &height);\n    updateNormalHints(window, width, height);\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr UNUSED, int heightincr UNUSED)\n{\n    int width, height;\n    _glfwPlatformGetWindowSize(window, &width, &height);\n    updateNormalHints(window, width, height);\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)\n{\n    _glfwPlatformGetWindowSize(window, width, height);\n}\n\nvoid _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,\n                                     int* left, int* top,\n                                     int* right, int* bottom)\n{\n    long* extents = NULL;\n\n    if (window->monitor || !window->decorated)\n        return;\n\n    if (_glfw.x11.NET_FRAME_EXTENTS == None)\n        return;\n\n    if (!_glfwPlatformWindowVisible(window) &&\n        _glfw.x11.NET_REQUEST_FRAME_EXTENTS)\n    {\n        XEvent event;\n\n        // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to\n        // function before the window is mapped\n        sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,\n                      0, 0, 0, 0, 0);\n\n        // HACK: Use a timeout because earlier versions of some window managers\n        //       (at least Unity, Fluxbox and Xfwm) failed to send the reply\n        //       They have been fixed but broken versions are still in the wild\n        //       If you are affected by this and your window manager is NOT\n        //       listed above, PLEASE report it to their and our issue trackers\n        while (!XCheckIfEvent(_glfw.x11.display,\n                              &event,\n                              isFrameExtentsEvent,\n                              (XPointer) window))\n        {\n            if (!waitForX11Event(ms_to_monotonic_t(500ll)))\n            {\n                _glfwInputError(GLFW_PLATFORM_ERROR,\n                                \"X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue\");\n                return;\n            }\n        }\n    }\n\n    if (_glfwGetWindowPropertyX11(window->x11.handle,\n                                  _glfw.x11.NET_FRAME_EXTENTS,\n                                  XA_CARDINAL,\n                                  (unsigned char**) &extents) == 4)\n    {\n        if (left)\n            *left = extents[0];\n        if (top)\n            *top = extents[2];\n        if (right)\n            *right = extents[1];\n        if (bottom)\n            *bottom = extents[3];\n    }\n\n    if (extents)\n        XFree(extents);\n}\n\nvoid _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,\n                                        float* xscale, float* yscale)\n{\n    if (xscale)\n        *xscale = _glfw.x11.contentScaleX;\n    if (yscale)\n        *yscale = _glfw.x11.contentScaleY;\n}\n\nmonotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)\n{\n    return ms_to_monotonic_t(500ll);\n}\n\nvoid _glfwPlatformIconifyWindow(_GLFWwindow* window)\n{\n    XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformRestoreWindow(_GLFWwindow* window)\n{\n    if (_glfwPlatformWindowIconified(window))\n    {\n        XMapWindow(_glfw.x11.display, window->x11.handle);\n        waitForVisibilityNotify(window);\n    }\n    else if (_glfwPlatformWindowVisible(window))\n    {\n        if (_glfw.x11.NET_WM_STATE &&\n            _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&\n            _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)\n        {\n            sendEventToWM(window,\n                          _glfw.x11.NET_WM_STATE,\n                          _NET_WM_STATE_REMOVE,\n                          _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,\n                          _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,\n                          1, 0);\n        }\n    }\n\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformMaximizeWindow(_GLFWwindow* window)\n{\n    if (!_glfw.x11.NET_WM_STATE ||\n        !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||\n        !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)\n    {\n        return;\n    }\n\n    if (_glfwPlatformWindowVisible(window))\n    {\n        sendEventToWM(window,\n                    _glfw.x11.NET_WM_STATE,\n                    _NET_WM_STATE_ADD,\n                    _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,\n                    _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,\n                    1, 0);\n    }\n    else\n    {\n        Atom* states = NULL;\n        unsigned long count =\n            _glfwGetWindowPropertyX11(window->x11.handle,\n                                      _glfw.x11.NET_WM_STATE,\n                                      XA_ATOM,\n                                      (unsigned char**) &states);\n\n        // NOTE: We don't check for failure as this property may not exist yet\n        //       and that's fine (and we'll create it implicitly with append)\n\n        Atom missing[2] =\n        {\n            _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,\n            _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ\n        };\n        unsigned long missingCount = 2;\n\n        for (unsigned long i = 0;  i < count;  i++)\n        {\n            for (unsigned long j = 0;  j < missingCount;  j++)\n            {\n                if (states[i] == missing[j])\n                {\n                    missing[j] = missing[missingCount - 1];\n                    missingCount--;\n                }\n            }\n        }\n\n        if (states)\n            XFree(states);\n\n        if (!missingCount)\n            return;\n\n        XChangeProperty(_glfw.x11.display, window->x11.handle,\n                        _glfw.x11.NET_WM_STATE, XA_ATOM, 32,\n                        PropModeAppend,\n                        (unsigned char*) missing,\n                        missingCount);\n    }\n\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen UNUSED)\n{\n    if (_glfwPlatformWindowVisible(window))\n        return;\n\n    XMapWindow(_glfw.x11.display, window->x11.handle);\n    // without this floating window position is incorrect on KDE\n    if (window->x11.layer_shell.is_active) {\n        WindowGeometry wg = calculate_layer_geometry(window);\n        _glfwPlatformSetWindowPos(window, wg.x, wg.y);\n    }\n    waitForVisibilityNotify(window);\n}\n\nvoid _glfwPlatformHideWindow(_GLFWwindow* window)\n{\n    XUnmapWindow(_glfw.x11.display, window->x11.handle);\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformRequestWindowAttention(_GLFWwindow* window)\n{\n    if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)\n        return;\n\n    sendEventToWM(window,\n                  _glfw.x11.NET_WM_STATE,\n                  _NET_WM_STATE_ADD,\n                  _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,\n                  0, 1, 0);\n}\n\nint _glfwPlatformWindowBell(_GLFWwindow* window)\n{\n    return XkbBell(_glfw.x11.display, window->x11.handle, 100, (Atom)0) ? true : false;\n}\n\nvoid _glfwPlatformFocusWindow(_GLFWwindow* window)\n{\n    if (_glfw.x11.NET_ACTIVE_WINDOW)\n        sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);\n    else if (_glfwPlatformWindowVisible(window))\n    {\n        XRaiseWindow(_glfw.x11.display, window->x11.handle);\n        XSetInputFocus(_glfw.x11.display, window->x11.handle,\n                       RevertToParent, CurrentTime);\n    }\n\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformSetWindowMonitor(_GLFWwindow* window,\n                                   _GLFWmonitor* monitor,\n                                   int xpos, int ypos,\n                                   int width, int height,\n                                   int refreshRate UNUSED)\n{\n    if (window->monitor == monitor)\n    {\n        if (monitor)\n        {\n            if (monitor->window == window)\n                acquireMonitor(window);\n        }\n        else\n        {\n            if (!window->resizable)\n                updateNormalHints(window, width, height);\n\n            XMoveResizeWindow(_glfw.x11.display, window->x11.handle,\n                              xpos, ypos, width, height);\n        }\n\n        XFlush(_glfw.x11.display);\n        return;\n    }\n\n    if (window->monitor)\n        releaseMonitor(window);\n\n    _glfwInputWindowMonitor(window, monitor);\n    updateNormalHints(window, width, height);\n\n    if (window->monitor)\n    {\n        if (!_glfwPlatformWindowVisible(window))\n        {\n            XMapRaised(_glfw.x11.display, window->x11.handle);\n            waitForVisibilityNotify(window);\n        }\n\n        updateWindowMode(window);\n        acquireMonitor(window);\n    }\n    else\n    {\n        updateWindowMode(window);\n        XMoveResizeWindow(_glfw.x11.display, window->x11.handle,\n                          xpos, ypos, width, height);\n    }\n\n    XFlush(_glfw.x11.display);\n}\n\nint _glfwPlatformWindowFocused(_GLFWwindow* window)\n{\n    Window focused;\n    int state;\n\n    XGetInputFocus(_glfw.x11.display, &focused, &state);\n    return window->x11.handle == focused;\n}\n\nint _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)\n{\n    return false;\n}\n\nint _glfwPlatformWindowIconified(_GLFWwindow* window)\n{\n    return getWindowState(window) == IconicState;\n}\n\nint _glfwPlatformWindowVisible(_GLFWwindow* window)\n{\n    XWindowAttributes wa;\n    XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);\n    return wa.map_state == IsViewable;\n}\n\nint _glfwPlatformWindowMaximized(_GLFWwindow* window)\n{\n    Atom* states;\n    unsigned long i;\n    bool maximized = false;\n\n    if (!_glfw.x11.NET_WM_STATE ||\n        !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||\n        !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)\n    {\n        return maximized;\n    }\n\n    const unsigned long count =\n        _glfwGetWindowPropertyX11(window->x11.handle,\n                                  _glfw.x11.NET_WM_STATE,\n                                  XA_ATOM,\n                                  (unsigned char**) &states);\n\n    for (i = 0;  i < count;  i++)\n    {\n        if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||\n            states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)\n        {\n            maximized = true;\n            break;\n        }\n    }\n\n    if (states)\n        XFree(states);\n\n    return maximized;\n}\n\nint _glfwPlatformWindowHovered(_GLFWwindow* window)\n{\n    Window w = _glfw.x11.root;\n    while (w)\n    {\n        Window root;\n        int rootX, rootY, childX, childY;\n        unsigned int mask;\n\n        _glfwGrabErrorHandlerX11();\n\n        const Bool result = XQueryPointer(_glfw.x11.display, w,\n                                          &root, &w, &rootX, &rootY,\n                                          &childX, &childY, &mask);\n\n        _glfwReleaseErrorHandlerX11();\n\n        if (_glfw.x11.errorCode == BadWindow)\n            w = _glfw.x11.root;\n        else if (!result)\n            return false;\n        else if (w == window->x11.handle)\n            return true;\n    }\n\n    return false;\n}\n\nint _glfwPlatformFramebufferTransparent(_GLFWwindow* window)\n{\n    if (!window->x11.transparent)\n        return false;\n\n    return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;\n}\n\nvoid _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled UNUSED)\n{\n    int width, height;\n    _glfwPlatformGetWindowSize(window, &width, &height);\n    updateNormalHints(window, width, height);\n}\n\nvoid _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)\n{\n    struct\n    {\n        unsigned long flags;\n        unsigned long functions;\n        unsigned long decorations;\n        long input_mode;\n        unsigned long status;\n    } hints = {0};\n\n    hints.flags = MWM_HINTS_DECORATIONS;\n    hints.decorations = enabled ? MWM_DECOR_ALL : 0;\n\n    XChangeProperty(_glfw.x11.display, window->x11.handle,\n                    _glfw.x11.MOTIF_WM_HINTS,\n                    _glfw.x11.MOTIF_WM_HINTS, 32,\n                    PropModeReplace,\n                    (unsigned char*) &hints,\n                    sizeof(hints) / sizeof(long));\n}\n\nvoid _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)\n{\n    if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)\n        return;\n\n    if (_glfwPlatformWindowVisible(window))\n    {\n        const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;\n        sendEventToWM(window,\n                      _glfw.x11.NET_WM_STATE,\n                      action,\n                      _glfw.x11.NET_WM_STATE_ABOVE,\n                      0, 1, 0);\n    }\n    else\n    {\n        Atom* states = NULL;\n        unsigned long i, count;\n\n        count = _glfwGetWindowPropertyX11(window->x11.handle,\n                                          _glfw.x11.NET_WM_STATE,\n                                          XA_ATOM,\n                                          (unsigned char**) &states);\n\n        // NOTE: We don't check for failure as this property may not exist yet\n        //       and that's fine (and we'll create it implicitly with append)\n\n        if (enabled)\n        {\n            for (i = 0;  i < count;  i++)\n            {\n                if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)\n                    break;\n            }\n\n            if (i < count)\n                return;\n\n            XChangeProperty(_glfw.x11.display, window->x11.handle,\n                            _glfw.x11.NET_WM_STATE, XA_ATOM, 32,\n                            PropModeAppend,\n                            (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,\n                            1);\n        }\n        else if (states)\n        {\n            for (i = 0;  i < count;  i++)\n            {\n                if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)\n                    break;\n            }\n\n            if (i == count)\n                return;\n\n            states[i] = states[count - 1];\n            count--;\n\n            XChangeProperty(_glfw.x11.display, window->x11.handle,\n                            _glfw.x11.NET_WM_STATE, XA_ATOM, 32,\n                            PropModeReplace, (unsigned char*) states, count);\n        }\n\n        if (states)\n            XFree(states);\n    }\n\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)\n{\n    if (!_glfw.x11.xshape.available)\n        return;\n\n    if (enabled)\n    {\n        Region region = XCreateRegion();\n        XShapeCombineRegion(_glfw.x11.display, window->x11.handle,\n                            ShapeInput, 0, 0, region, ShapeSet);\n        XDestroyRegion(region);\n    }\n    else\n    {\n        XShapeCombineMask(_glfw.x11.display, window->x11.handle,\n                          ShapeInput, 0, 0, None, ShapeSet);\n    }\n}\n\nfloat _glfwPlatformGetWindowOpacity(_GLFWwindow* window)\n{\n    float opacity = 1.f;\n\n    if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))\n    {\n        CARD32* value = NULL;\n\n        if (_glfwGetWindowPropertyX11(window->x11.handle,\n                                      _glfw.x11.NET_WM_WINDOW_OPACITY,\n                                      XA_CARDINAL,\n                                      (unsigned char**) &value))\n        {\n            opacity = (float) (*value / (double) 0xffffffffu);\n        }\n\n        if (value)\n            XFree(value);\n    }\n\n    return opacity;\n}\n\nvoid _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)\n{\n    const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);\n    XChangeProperty(_glfw.x11.display, window->x11.handle,\n                    _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,\n                    PropModeReplace, (unsigned char*) &value, 1);\n}\n\nstatic unsigned\ndispatch_x11_queued_events(int num_events) {\n    unsigned dispatched = num_events > 0 ? num_events : 0;\n    while (num_events-- > 0) {\n        XEvent event;\n        XNextEvent(_glfw.x11.display, &event);\n        processEvent(&event);\n    }\n    return dispatched;\n}\n\nstatic unsigned\n_glfwDispatchX11Events(void) {\n    _GLFWwindow* window;\n    unsigned dispatched = 0;\n\n#if defined(__linux__)\n    if (_glfw.joysticksInitialized)\n        _glfwDetectJoystickConnectionLinux();\n#endif\n    dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAfterFlush));\n\n    window = _glfw.x11.disabledCursorWindow;\n    if (window)\n    {\n        int width, height;\n        _glfwPlatformGetWindowSize(window, &width, &height);\n\n        // NOTE: Re-center the cursor only if it has moved since the last call,\n        //       to avoid breaking glfwWaitEvents with MotionNotify\n        if (window->x11.lastCursorPosX != width / 2 ||\n            window->x11.lastCursorPosY != height / 2)\n        {\n            _glfwPlatformSetCursorPos(window, width / 2.f, height / 2.f);\n        }\n    }\n\n    XFlush(_glfw.x11.display);\n    // XFlush can cause events to be queued, we don't use QueuedAfterFlush here\n    // as something might have inserted events into the queue, but we want to guarantee\n    // a flush.\n    dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAlready));\n    return dispatched;\n}\n\nvoid _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled)\n{\n    if (!_glfw.x11.xi.available)\n        return;\n\n    if (_glfw.x11.disabledCursorWindow != window)\n        return;\n\n    if (enabled)\n        enableRawMouseMotion(window);\n    else\n        disableRawMouseMotion(window);\n}\n\nbool _glfwPlatformRawMouseMotionSupported(void)\n{\n    return _glfw.x11.xi.available;\n}\n\nvoid _glfwPlatformPollEvents(void)\n{\n    _glfwDispatchX11Events();\n    handleEvents(0);\n}\n\nvoid _glfwPlatformWaitEvents(void)\n{\n    monotonic_t timeout = _glfwDispatchX11Events() ? 0 : -1;\n    handleEvents(timeout);\n}\n\nvoid _glfwPlatformWaitEventsTimeout(monotonic_t timeout)\n{\n    if (_glfwDispatchX11Events()) timeout = 0;\n    handleEvents(timeout);\n}\n\nvoid _glfwPlatformPostEmptyEvent(void)\n{\n    wakeupEventLoop(&_glfw.x11.eventLoopData);\n}\n\nvoid _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)\n{\n    Window root, child;\n    int rootX, rootY, childX, childY;\n    unsigned int mask;\n\n    XQueryPointer(_glfw.x11.display, window->x11.handle,\n                  &root, &child,\n                  &rootX, &rootY, &childX, &childY,\n                  &mask);\n\n    if (xpos)\n        *xpos = childX;\n    if (ypos)\n        *ypos = childY;\n}\n\nvoid _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)\n{\n    // Store the new position so it can be recognized later\n    window->x11.warpCursorPosX = (int) x;\n    window->x11.warpCursorPosY = (int) y;\n\n    XWarpPointer(_glfw.x11.display, None, window->x11.handle,\n                 0,0,0,0, (int) x, (int) y);\n    XFlush(_glfw.x11.display);\n}\n\nvoid _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)\n{\n    if (mode == GLFW_CURSOR_DISABLED)\n    {\n        if (_glfwPlatformWindowFocused(window))\n            disableCursor(window);\n    }\n    else if (_glfw.x11.disabledCursorWindow == window)\n        enableCursor(window);\n    else\n        updateCursorImage(window);\n\n    XFlush(_glfw.x11.display);\n}\n\nconst char* _glfwPlatformGetNativeKeyName(int native_key)\n{\n\n    return glfw_xkb_keysym_name(native_key);\n}\n\nint _glfwPlatformGetNativeKeyForKey(uint32_t key)\n{\n    return glfw_xkb_sym_for_key(key);\n}\n\nint _glfwPlatformCreateCursor(_GLFWcursor* cursor,\n                              const GLFWimage* image,\n                              int xhot, int yhot, int count UNUSED)\n{\n    cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);\n    if (!cursor->x11.handle)\n        return false;\n\n    return true;\n}\n\nstatic int\nset_cursor_from_font(_GLFWcursor* cursor, int native) {\n    cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);\n    if (!cursor->x11.handle) {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"X11: Failed to create standard cursor\");\n        return false;\n    }\n    return true;\n}\n\nstatic bool\ntry_cursor_names(_GLFWcursor *cursor, int arg_count, ...) {\n    va_list ap;\n    va_start(ap, arg_count);\n    const char *first_name = \"\";\n    for (int i = 0; i < arg_count; i++) {\n        const char *name = va_arg(ap, const char *);\n        first_name = name;\n        cursor->x11.handle = XcursorLibraryLoadCursor(_glfw.x11.display, name);\n        if (cursor->x11.handle) break;\n    }\n    va_end(ap);\n    if (!cursor->x11.handle) {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"X11: Failed to load standard cursor: %s with %d aliases via Xcursor library\", first_name, arg_count);\n        return false;\n    }\n    return true;\n}\n\n\nint _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape)\n{\n    switch(shape) {\n        /* start glfw to xc mapping (auto generated by gen-key-constants.py do not edit) */\n        case GLFW_DEFAULT_CURSOR: return set_cursor_from_font(cursor, XC_left_ptr);\n        case GLFW_TEXT_CURSOR: return set_cursor_from_font(cursor, XC_xterm);\n        case GLFW_POINTER_CURSOR: return set_cursor_from_font(cursor, XC_hand2);\n        case GLFW_HELP_CURSOR: return set_cursor_from_font(cursor, XC_question_arrow);\n        case GLFW_WAIT_CURSOR: return set_cursor_from_font(cursor, XC_clock);\n        case GLFW_PROGRESS_CURSOR: return try_cursor_names(cursor, 3, \"progress\", \"half-busy\", \"left_ptr_watch\");\n        case GLFW_CROSSHAIR_CURSOR: return set_cursor_from_font(cursor, XC_tcross);\n        case GLFW_CELL_CURSOR: return set_cursor_from_font(cursor, XC_plus);\n        case GLFW_VERTICAL_TEXT_CURSOR: return try_cursor_names(cursor, 1, \"vertical-text\");\n        case GLFW_MOVE_CURSOR: return set_cursor_from_font(cursor, XC_fleur);\n        case GLFW_E_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_right_side);\n        case GLFW_NE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_right_corner);\n        case GLFW_NW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_left_corner);\n        case GLFW_N_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_side);\n        case GLFW_SE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_right_corner);\n        case GLFW_SW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_left_corner);\n        case GLFW_S_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_side);\n        case GLFW_W_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_left_side);\n        case GLFW_EW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_h_double_arrow);\n        case GLFW_NS_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_v_double_arrow);\n        case GLFW_NESW_RESIZE_CURSOR: return try_cursor_names(cursor, 3, \"nesw-resize\", \"size_bdiag\", \"size-bdiag\");\n        case GLFW_NWSE_RESIZE_CURSOR: return try_cursor_names(cursor, 3, \"nwse-resize\", \"size_fdiag\", \"size-fdiag\");\n        case GLFW_ZOOM_IN_CURSOR: return try_cursor_names(cursor, 2, \"zoom-in\", \"zoom_in\");\n        case GLFW_ZOOM_OUT_CURSOR: return try_cursor_names(cursor, 2, \"zoom-out\", \"zoom_out\");\n        case GLFW_ALIAS_CURSOR: return try_cursor_names(cursor, 1, \"dnd-link\");\n        case GLFW_COPY_CURSOR: return try_cursor_names(cursor, 1, \"dnd-copy\");\n        case GLFW_NOT_ALLOWED_CURSOR: return try_cursor_names(cursor, 3, \"not-allowed\", \"forbidden\", \"crossed_circle\");\n        case GLFW_NO_DROP_CURSOR: return try_cursor_names(cursor, 2, \"no-drop\", \"dnd-no-drop\");\n        case GLFW_GRAB_CURSOR: return set_cursor_from_font(cursor, XC_hand1);\n        case GLFW_GRABBING_CURSOR: return try_cursor_names(cursor, 3, \"grabbing\", \"closedhand\", \"dnd-none\");\n/* end glfw to xc mapping */\n        case GLFW_INVALID_CURSOR: return false;\n    }\n    return false;\n}\n\nvoid _glfwPlatformDestroyCursor(_GLFWcursor* cursor)\n{\n    if (cursor->x11.handle)\n        XFreeCursor(_glfw.x11.display, cursor->x11.handle);\n}\n\nvoid _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor UNUSED)\n{\n    if (window->cursorMode == GLFW_CURSOR_NORMAL)\n    {\n        updateCursorImage(window);\n        XFlush(_glfw.x11.display);\n    }\n}\n\nstatic MimeAtom atom_for_mime(const char *mime) {\n    for (size_t i = 0; i < _glfw.x11.mime_atoms.sz; i++) {\n        MimeAtom ma = _glfw.x11.mime_atoms.array[i];\n        if (strcmp(ma.mime, mime) == 0) {\n            return ma;\n        }\n    }\n    MimeAtom ma = {.mime=_glfw_strdup(mime), .atom=XInternAtom(_glfw.x11.display, mime, 0)};\n    if (_glfw.x11.mime_atoms.capacity < _glfw.x11.mime_atoms.sz + 1) {\n        _glfw.x11.mime_atoms.capacity += 32;\n        _glfw.x11.mime_atoms.array = realloc(_glfw.x11.mime_atoms.array, _glfw.x11.mime_atoms.capacity * sizeof(_glfw.x11.mime_atoms.array[0]));\n    }\n    _glfw.x11.mime_atoms.array[_glfw.x11.mime_atoms.sz++] = ma;\n    return ma;\n}\n\nvoid _glfwPlatformSetClipboard(GLFWClipboardType t) {\n    Atom which = None;\n    _GLFWClipboardData *cd = NULL;\n    AtomArray *aa = NULL;\n    switch (t) {\n        case GLFW_CLIPBOARD: which = _glfw.x11.CLIPBOARD; cd = &_glfw.clipboard; aa = &_glfw.x11.clipboard_atoms; break;\n        case GLFW_PRIMARY_SELECTION: which = _glfw.x11.PRIMARY; cd = &_glfw.primary; aa = &_glfw.x11.primary_atoms; break;\n    }\n    XSetSelectionOwner(_glfw.x11.display, which, _glfw.x11.helperWindowHandle, CurrentTime);\n    if (XGetSelectionOwner(_glfw.x11.display, which) != _glfw.x11.helperWindowHandle) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Failed to become owner of clipboard selection\");\n    }\n    if (aa->capacity < cd->num_mime_types + 32) {\n        aa->capacity = cd->num_mime_types + 32;\n        aa->array = reallocarray(aa->array, aa->capacity, sizeof(aa->array[0]));\n    }\n    aa->sz = 0;\n    for (size_t i = 0; i < cd->num_mime_types; i++) {\n        MimeAtom *a = aa->array + aa->sz++;\n        *a = atom_for_mime(cd->mime_types[i]);\n        if (strcmp(cd->mime_types[i], \"text/plain\") == 0) {\n            a = aa->array + aa->sz++;\n            a->atom = _glfw.x11.UTF8_STRING;\n            a->mime = \"text/plain\";\n        }\n    }\n}\n\ntypedef struct chunked_writer {\n    char *buf; size_t sz, cap;\n    bool is_self_offer;\n} chunked_writer;\n\nstatic bool\nwrite_chunk(void *object, const char *data, size_t sz) {\n    chunked_writer *cw = object;\n    if (data) {\n        if (cw->cap < cw->sz + sz) {\n            cw->cap = MAX(cw->cap * 2, cw->sz + 8*sz);\n            cw->buf = realloc(cw->buf, cw->cap * sizeof(cw->buf[0]));\n        }\n        memcpy(cw->buf + cw->sz, data, sz);\n        cw->sz += sz;\n    } else if (sz == 1) cw->is_self_offer = true;\n    return true;\n}\n\nstatic void\nget_available_mime_types(Atom which_clipboard, GLFWclipboardwritedatafun write_data, void *object) {\n    chunked_writer cw = {0};\n    getSelectionString(which_clipboard, &_glfw.x11.TARGETS, 1, write_chunk, &cw, false);\n    if (cw.is_self_offer) {\n        write_data(object, NULL, 1);\n        return;\n    }\n    size_t count = 0;\n    bool ok = true;\n    if (cw.buf) {\n        Atom *atoms = (Atom*)cw.buf;\n        count = cw.sz / sizeof(Atom);\n        char **names = calloc(count, sizeof(char*));\n        get_atom_names(atoms, count, names);\n        for (size_t i = 0; i < count; i++) {\n            if (strchr(names[i], '/')) {\n                if (ok) ok = write_data(object, names[i], strlen(names[i]));\n            } else {\n                if (atoms[i] == _glfw.x11.UTF8_STRING || atoms[i] == XA_STRING) {\n                    if (ok) ok = write_data(object, \"text/plain\", strlen(\"text/plain\"));\n                }\n            }\n            XFree(names[i]);\n        }\n        free(cw.buf);\n        free(names);\n    }\n}\n\nvoid\n_glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {\n    Atom atoms[4], which = clipboard_type == GLFW_PRIMARY_SELECTION ? _glfw.x11.PRIMARY : _glfw.x11.CLIPBOARD;\n    if (mime_type == NULL) {\n        get_available_mime_types(which, write_data, object);\n        return;\n    }\n    size_t count = 0;\n    if (strcmp(mime_type, \"text/plain\") == 0) {\n        // UTF8_STRING is what xclip uses by default, and there are people out there that expect to be able to paste from it with a single read operation. See https://github.com/kovidgoyal/kitty/issues/5842\n        // Also ancient versions of GNOME use DOS line endings even for text/plain;charset=utf-8. See https://github.com/kovidgoyal/kitty/issues/5528#issuecomment-1325348218\n        atoms[count++] = _glfw.x11.UTF8_STRING;\n        // we need to do this because GTK/GNOME is moronic they convert text/plain to DOS line endings, see\n        // https://gitlab.gnome.org/GNOME/gtk/-/issues/2307\n        atoms[count++] = atom_for_mime(\"text/plain;charset=utf-8\").atom;\n        atoms[count++] = atom_for_mime(\"text/plain\").atom;\n        atoms[count++] = XA_STRING;\n    } else {\n        atoms[count++] = atom_for_mime(mime_type).atom;\n    }\n    getSelectionString(which, atoms, count, write_data, object, true);\n}\n\nEGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)\n{\n    if (_glfw.egl.ANGLE_platform_angle)\n    {\n        int type = 0;\n\n        if (_glfw.egl.ANGLE_platform_angle_opengl)\n        {\n            if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)\n                type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;\n        }\n\n        if (_glfw.egl.ANGLE_platform_angle_vulkan)\n        {\n            if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)\n                type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;\n        }\n\n        if (type)\n        {\n            *attribs = calloc(5, sizeof(EGLint));\n            (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;\n            (*attribs)[1] = type;\n            (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;\n            (*attribs)[3] = EGL_PLATFORM_X11_EXT;\n            (*attribs)[4] = EGL_NONE;\n            return EGL_PLATFORM_ANGLE_ANGLE;\n        }\n    }\n\n    if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)\n        return EGL_PLATFORM_X11_EXT;\n\n    return 0;\n}\n\nEGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)\n{\n    return _glfw.x11.display;\n}\n\nEGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)\n{\n    if (_glfw.egl.platform)\n        return &window->x11.handle;\n    else\n        return (EGLNativeWindowType) window->x11.handle;\n}\n\nvoid _glfwPlatformGetRequiredInstanceExtensions(char** extensions)\n{\n    if (!_glfw.vk.KHR_surface)\n        return;\n\n    if (!_glfw.vk.KHR_xcb_surface)\n    {\n        if (!_glfw.vk.KHR_xlib_surface)\n            return;\n    }\n\n    extensions[0] = \"VK_KHR_surface\";\n\n    // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but\n    //       not correctly implementing VK_KHR_xlib_surface\n    if (_glfw.vk.KHR_xcb_surface)\n        extensions[1] = \"VK_KHR_xcb_surface\";\n    else\n        extensions[1] = \"VK_KHR_xlib_surface\";\n}\n\nint _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,\n                                                      VkPhysicalDevice device,\n                                                      uint32_t queuefamily)\n{\n    VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,\n                                                          _glfw.x11.screen));\n\n    if (_glfw.vk.KHR_xcb_surface)\n    {\n        PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR\n            vkGetPhysicalDeviceXcbPresentationSupportKHR =\n            (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)\n            vkGetInstanceProcAddr(instance, \"vkGetPhysicalDeviceXcbPresentationSupportKHR\");\n        if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"X11: Vulkan instance missing VK_KHR_xcb_surface extension\");\n            return false;\n        }\n\n        xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);\n        if (!connection)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"X11: Failed to retrieve XCB connection\");\n            return false;\n        }\n\n        return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,\n                                                            queuefamily,\n                                                            connection,\n                                                            visualID);\n    }\n    else\n    {\n        PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR\n            vkGetPhysicalDeviceXlibPresentationSupportKHR =\n            (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)\n            vkGetInstanceProcAddr(instance, \"vkGetPhysicalDeviceXlibPresentationSupportKHR\");\n        if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"X11: Vulkan instance missing VK_KHR_xlib_surface extension\");\n            return false;\n        }\n\n        return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,\n                                                             queuefamily,\n                                                             _glfw.x11.display,\n                                                             visualID);\n    }\n}\n\nVkResult _glfwPlatformCreateWindowSurface(VkInstance instance,\n                                          _GLFWwindow* window,\n                                          const VkAllocationCallbacks* allocator,\n                                          VkSurfaceKHR* surface)\n{\n    if (_glfw.vk.KHR_xcb_surface)\n    {\n        VkResult err;\n        VkXcbSurfaceCreateInfoKHR sci;\n        PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;\n\n        xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);\n        if (!connection)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"X11: Failed to retrieve XCB connection\");\n            return VK_ERROR_EXTENSION_NOT_PRESENT;\n        }\n\n        vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)\n            vkGetInstanceProcAddr(instance, \"vkCreateXcbSurfaceKHR\");\n        if (!vkCreateXcbSurfaceKHR)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"X11: Vulkan instance missing VK_KHR_xcb_surface extension\");\n            return VK_ERROR_EXTENSION_NOT_PRESENT;\n        }\n\n        memset(&sci, 0, sizeof(sci));\n        sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;\n        sci.connection = connection;\n        sci.window = window->x11.handle;\n\n        err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);\n        if (err)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"X11: Failed to create Vulkan XCB surface: %s\",\n                            _glfwGetVulkanResultString(err));\n        }\n\n        return err;\n    }\n    else\n    {\n        VkResult err;\n        VkXlibSurfaceCreateInfoKHR sci;\n        PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;\n\n        vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)\n            vkGetInstanceProcAddr(instance, \"vkCreateXlibSurfaceKHR\");\n        if (!vkCreateXlibSurfaceKHR)\n        {\n            _glfwInputError(GLFW_API_UNAVAILABLE,\n                            \"X11: Vulkan instance missing VK_KHR_xlib_surface extension\");\n            return VK_ERROR_EXTENSION_NOT_PRESENT;\n        }\n\n        memset(&sci, 0, sizeof(sci));\n        sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;\n        sci.dpy = _glfw.x11.display;\n        sci.window = window->x11.handle;\n\n        err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);\n        if (err)\n        {\n            _glfwInputError(GLFW_PLATFORM_ERROR,\n                            \"X11: Failed to create Vulkan X11 surface: %s\",\n                            _glfwGetVulkanResultString(err));\n        }\n\n        return err;\n    }\n}\n\nvoid\n_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {\n    glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, ev);\n}\n\nint\n_glfwPlatformSetWindowBlur(_GLFWwindow *window, int blur_radius) {\n    if (_glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION == None) {\n        _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION = XInternAtom(_glfw.x11.display, \"_KDE_NET_WM_BLUR_BEHIND_REGION\", False);\n    }\n    if (_glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION != None) {\n        uint32_t data = 0;\n        if (blur_radius > 0) {\n            XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION,\n                    XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &data, 1);\n        } else {\n            XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION);\n        }\n        return 1;\n    }\n    return 0;\n}\n\n\nbool\n_glfwPlatformGrabKeyboard(bool grab) {\n    int result;\n    if (grab) {\n        result = XGrabKeyboard(_glfw.x11.display, _glfw.x11.root, True, GrabModeAsync, GrabModeAsync, CurrentTime);\n    } else {\n        result = XUngrabKeyboard(_glfw.x11.display, CurrentTime);\n    }\n    return result == GrabSuccess;\n}\n\n//////////////////////////////////////////////////////////////////////////\n//////                        GLFW native API                       //////\n//////////////////////////////////////////////////////////////////////////\n\nGLFWAPI Display* glfwGetX11Display(void)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);\n    return _glfw.x11.display;\n}\n\nGLFWAPI unsigned long glfwGetX11Window(GLFWwindow* handle)\n{\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    assert(window != NULL);\n\n    _GLFW_REQUIRE_INIT_OR_RETURN(None);\n    return window->x11.handle;\n}\n\nGLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {\n    return glfw_xkb_keysym_from_name(keyName, caseSensitive);\n}\n\nGLFWAPI unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data) {\n    return glfw_dbus_send_user_notification(n, callback, data);\n}\n\nGLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {\n    glfw_dbus_set_user_notification_activated_handler(handler);\n}\n\nGLFWAPI int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)\n{\n    _GLFW_REQUIRE_INIT_OR_RETURN(0);\n    _GLFWwindow* window = (_GLFWwindow*) handle;\n    return XSetCommand(_glfw.x11.display, window->x11.handle, argv, argc);\n}\n\n// Drag source {{{\n\n// Helper function to check if a window supports XdndAware\nstatic bool\nwindow_supports_xdnd(Window win, int *version_out) {\n    Atom actual_type;\n    int actual_format;\n    unsigned long count, bytes_after;\n    unsigned char *data = NULL;\n    bool supported = false;\n\n    if (XGetWindowProperty(_glfw.x11.display, win, _glfw.x11.XdndAware,\n                          0, 1, False, XA_ATOM,\n                          &actual_type, &actual_format,\n                          &count, &bytes_after, &data) == Success) {\n        if (actual_type == XA_ATOM && actual_format == 32 && count > 0) {\n            supported = true;\n            if (version_out) {\n                int version = *(int*)data;\n                *version_out = (version < _GLFW_XDND_VERSION) ? version : _GLFW_XDND_VERSION;\n            }\n        }\n        if (data) XFree(data);\n    }\n    return supported;\n}\n\n// Find the XdndAware window at the given coordinates\nstatic Window\nfind_xdnd_aware_target(Window root, int root_x, int root_y, int *version_out) {\n    Window target = None;\n    Window child = root;\n    Window parent = root;\n    Window *children = NULL;\n    unsigned int nchildren = 0;\n\n    // Walk down the window tree to find the deepest window at these coordinates\n    while (child != None) {\n        target = child;\n        int x, y;\n        if (!XTranslateCoordinates(_glfw.x11.display, root, target,\n                                   root_x, root_y, &x, &y, &child)) {\n            break;\n        }\n        if (child == None) break;\n    }\n\n    // Walk up the tree to find an XdndAware window\n    while (target != None && target != root) {\n        if (window_supports_xdnd(target, version_out)) {\n            // Check for XdndProxy\n            Atom actual_type;\n            int actual_format;\n            unsigned long count, bytes_after;\n            unsigned char *data = NULL;\n\n            if (XGetWindowProperty(_glfw.x11.display, target, _glfw.x11.XdndProxy,\n                                  0, 1, False, XA_WINDOW,\n                                  &actual_type, &actual_format,\n                                  &count, &bytes_after, &data) == Success) {\n                if (actual_type == XA_WINDOW && actual_format == 32 && count > 0) {\n                    Window proxy = *(Window*)data;\n                    XFree(data);\n                    // Verify the proxy is XdndAware\n                    if (window_supports_xdnd(proxy, NULL)) {\n                        return proxy;\n                    }\n                } else if (data) {\n                    XFree(data);\n                }\n            }\n            return target;\n        }\n\n        // Move to parent\n        Window new_parent;\n        if (XQueryTree(_glfw.x11.display, target, &parent, &new_parent, &children, &nchildren)) {\n            if (children) XFree(children);\n            target = new_parent;\n        } else {\n            break;\n        }\n    }\n\n    return None;\n}\n\n// Send XdndEnter message to target window\nstatic void\nsend_xdnd_enter(Window target, int version) {\n    XEvent event;\n    memset(&event, 0, sizeof(event));\n\n    event.xclient.type = ClientMessage;\n    event.xclient.window = target;\n    event.xclient.message_type = _glfw.x11.XdndEnter;\n    event.xclient.format = 32;\n    event.xclient.data.l[0] = _glfw.x11.drag.source_window;\n    event.xclient.data.l[1] = (version << 24);\n\n    // If we have more than 3 types, set the type list flag\n    if (_glfw.x11.drag.type_count > 3) {\n        event.xclient.data.l[1] |= 1;  // More than 3 types, use property\n        // Set the XdndTypeList property on source window\n        XChangeProperty(_glfw.x11.display, _glfw.x11.drag.source_window,\n                       _glfw.x11.XdndTypeList, XA_ATOM, 32,\n                       PropModeReplace,\n                       (unsigned char*)_glfw.x11.drag.type_atoms,\n                       _glfw.x11.drag.type_count);\n    } else {\n        // Embed up to 3 types in the message\n        for (size_t i = 0; i < _glfw.x11.drag.type_count && i < 3; i++) {\n            event.xclient.data.l[2 + i] = _glfw.x11.drag.type_atoms[i];\n        }\n    }\n\n    _glfwGrabErrorHandlerX11();\n    XSendEvent(_glfw.x11.display, target, False, NoEventMask, &event);\n    XFlush(_glfw.x11.display);\n    _glfwReleaseErrorHandlerX11();\n    if (_glfw.x11.errorCode == BadWindow) _glfw.x11.drag.current_target = None;\n}\n\n// Send XdndPosition message to target window\nstatic void\nsend_xdnd_position(Window target, int root_x, int root_y, Time timestamp) {\n    XEvent event;\n    memset(&event, 0, sizeof(event));\n\n    event.xclient.type = ClientMessage;\n    event.xclient.window = target;\n    event.xclient.message_type = _glfw.x11.XdndPosition;\n    event.xclient.format = 32;\n    event.xclient.data.l[0] = _glfw.x11.drag.source_window;\n    event.xclient.data.l[1] = 0;  // Reserved\n    event.xclient.data.l[2] = (root_x << 16) | root_y;\n    event.xclient.data.l[3] = timestamp;\n    event.xclient.data.l[4] = _glfw.x11.drag.action_atom;\n\n    _glfwGrabErrorHandlerX11();\n    XSendEvent(_glfw.x11.display, target, False, NoEventMask, &event);\n    XFlush(_glfw.x11.display);\n    _glfwReleaseErrorHandlerX11();\n    if (_glfw.x11.errorCode == BadWindow) _glfw.x11.drag.current_target = None;\n    else _glfw.x11.drag.waiting_for_status = true;\n}\n\n// Send XdndLeave message to target window\nstatic void\nsend_xdnd_leave(Window target) {\n    XEvent event;\n    memset(&event, 0, sizeof(event));\n\n    event.xclient.type = ClientMessage;\n    event.xclient.window = target;\n    event.xclient.message_type = _glfw.x11.XdndLeave;\n    event.xclient.format = 32;\n    event.xclient.data.l[0] = _glfw.x11.drag.source_window;\n\n    _glfwGrabErrorHandlerX11();\n    XSendEvent(_glfw.x11.display, target, False, NoEventMask, &event);\n    XFlush(_glfw.x11.display);\n    _glfwReleaseErrorHandlerX11();\n    // BadWindow on leave is benign – the target window is already gone\n}\n\n// Send XdndDrop message to target window\nstatic void\nsend_xdnd_drop(Window target, Time timestamp) {\n    XEvent event;\n    memset(&event, 0, sizeof(event));\n\n    event.xclient.type = ClientMessage;\n    event.xclient.window = target;\n    event.xclient.message_type = _glfw.x11.XdndDrop;\n    event.xclient.format = 32;\n    event.xclient.data.l[0] = _glfw.x11.drag.source_window;\n    event.xclient.data.l[1] = 0;  // Reserved\n    event.xclient.data.l[2] = timestamp;\n\n    _glfwGrabErrorHandlerX11();\n    XSendEvent(_glfw.x11.display, target, False, NoEventMask, &event);\n    XFlush(_glfw.x11.display);\n    _glfwReleaseErrorHandlerX11();\n    if (_glfw.x11.errorCode == BadWindow) {\n        // Target window was destroyed; cancel the drag gracefully\n        _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n        if (window) {\n            GLFWDragEvent ev = {.type = GLFW_DRAG_CANCELLED};\n            _glfwInputDragSourceRequest(window, &ev);\n        }\n        _glfwFreeDragSourceData();\n    }\n}\n\n// Render thumbnail pixels into _glfw.x11.drag.thumbnail_pixmap / thumbnail_gc.\n// thumbnail_window must already exist. On success returns true.\nstatic bool\nrender_drag_thumbnail_to_pixmap(const GLFWimage* thumbnail) {\n    // Free any previous pixmap / GC\n    if (_glfw.x11.drag.thumbnail_gc != None) {\n        XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);\n        _glfw.x11.drag.thumbnail_gc = None;\n    }\n    if (_glfw.x11.drag.thumbnail_pixmap != None) {\n        XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);\n        _glfw.x11.drag.thumbnail_pixmap = None;\n    }\n\n    _glfw.x11.drag.thumbnail_pixmap = XCreatePixmap(\n        _glfw.x11.display,\n        _glfw.x11.drag.thumbnail_window,\n        thumbnail->width,\n        thumbnail->height,\n        DefaultDepth(_glfw.x11.display, _glfw.x11.screen)\n    );\n    if (!_glfw.x11.drag.thumbnail_pixmap) return false;\n\n    _glfw.x11.drag.thumbnail_gc = XCreateGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap, 0, NULL);\n    if (!_glfw.x11.drag.thumbnail_gc) {\n        XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);\n        _glfw.x11.drag.thumbnail_pixmap = None;\n        return false;\n    }\n\n    Visual* visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);\n    XImage* ximage = XCreateImage(\n        _glfw.x11.display,\n        visual,\n        DefaultDepth(_glfw.x11.display, _glfw.x11.screen),\n        ZPixmap,\n        0,\n        NULL,\n        thumbnail->width,\n        thumbnail->height,\n        32,\n        0\n    );\n    if (!ximage) {\n        XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);\n        XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);\n        _glfw.x11.drag.thumbnail_gc = None;\n        _glfw.x11.drag.thumbnail_pixmap = None;\n        return false;\n    }\n\n    int pixels_size = thumbnail->width * thumbnail->height * 4;\n    unsigned char* ximage_data = malloc(pixels_size);\n    if (!ximage_data) {\n        XDestroyImage(ximage);\n        XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);\n        XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);\n        _glfw.x11.drag.thumbnail_gc = None;\n        _glfw.x11.drag.thumbnail_pixmap = None;\n        return false;\n    }\n    ximage->data = (char*)ximage_data;\n\n    const unsigned char* src = thumbnail->pixels;\n    for (int i = 0; i < thumbnail->width * thumbnail->height; i++) {\n        unsigned char r = src[i * 4 + 0];\n        unsigned char g = src[i * 4 + 1];\n        unsigned char b = src[i * 4 + 2];\n        unsigned char a = src[i * 4 + 3];\n        if (a < 255) {\n            r = (r * a) / 255;\n            g = (g * a) / 255;\n            b = (b * a) / 255;\n        }\n        unsigned long pixel;\n        if (ximage->byte_order == LSBFirst)\n            pixel = ((unsigned long)a << 24) | ((unsigned long)r << 16) | ((unsigned long)g << 8) | b;\n        else\n            pixel = ((unsigned long)b << 24) | ((unsigned long)g << 16) | ((unsigned long)r << 8) | a;\n        XPutPixel(ximage, i % thumbnail->width, i / thumbnail->width, pixel);\n    }\n\n    XPutImage(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap, _glfw.x11.drag.thumbnail_gc,\n              ximage, 0, 0, 0, 0, thumbnail->width, thumbnail->height);\n    XDestroyImage(ximage);\n\n    XSetWindowBackgroundPixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_window,\n                               _glfw.x11.drag.thumbnail_pixmap);\n    return true;\n}\n\n// Create thumbnail window for drag operation\nstatic bool\ncreate_drag_thumbnail(const GLFWimage* thumbnail, int x, int y) {\n    if (!thumbnail || !thumbnail->pixels || thumbnail->width <= 0 || thumbnail->height <= 0) {\n        return true;  // No thumbnail is fine\n    }\n\n    // Create an override-redirect window for the drag icon\n    XSetWindowAttributes attrs;\n    attrs.override_redirect = True;\n    attrs.background_pixel = 0;\n\n    _glfw.x11.drag.thumbnail_window = XCreateWindow(\n        _glfw.x11.display,\n        _glfw.x11.root,\n        x, y,\n        thumbnail->width, thumbnail->height,\n        0,  // border width\n        CopyFromParent,  // depth\n        InputOutput,     // class\n        CopyFromParent,  // visual\n        CWOverrideRedirect | CWBackPixel,\n        &attrs\n    );\n\n    if (!_glfw.x11.drag.thumbnail_window) {\n        return false;\n    }\n\n    if (!render_drag_thumbnail_to_pixmap(thumbnail)) {\n        XDestroyWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);\n        _glfw.x11.drag.thumbnail_window = None;\n        return false;\n    }\n\n    // Map the window to make it visible\n    XMapRaised(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);\n    XFlush(_glfw.x11.display);\n\n    return true;\n}\n\n// Handle motion during drag\nstatic void\nhandle_drag_motion(int root_x, int root_y, Time timestamp) {\n    if (!_glfw.x11.drag.active) return;\n\n    // Move thumbnail window to follow cursor\n    if (_glfw.x11.drag.thumbnail_window != None) {\n        XMoveWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window, root_x + 10, root_y + 10);\n    }\n\n    int version = _GLFW_XDND_VERSION;\n    Window new_target = find_xdnd_aware_target(_glfw.x11.root, root_x, root_y, &version);\n\n    if (new_target != _glfw.x11.drag.current_target) {\n        // Send leave to old target\n        if (_glfw.x11.drag.current_target != None) {\n            send_xdnd_leave(_glfw.x11.drag.current_target);\n        }\n\n        _glfw.x11.drag.current_target = new_target;\n        _glfw.x11.drag.waiting_for_status = false;\n        _glfw.x11.drag.accepted = false;\n\n        // Send enter to new target\n        if (new_target != None) {\n            _glfw.x11.drag.xdnd_version = version;\n            send_xdnd_enter(new_target, version);\n        }\n    }\n\n    // Send position to current target\n    if (_glfw.x11.drag.current_target != None && !_glfw.x11.drag.waiting_for_status) {\n        send_xdnd_position(_glfw.x11.drag.current_target, root_x, root_y, timestamp);\n    }\n}\n\n// Handle button release during drag (drop)\nstatic void\nhandle_drag_button_release(Time timestamp) {\n    if (!_glfw.x11.drag.active) return;\n\n    if (_glfw.x11.drag.current_target != None && _glfw.x11.drag.accepted) {\n        send_xdnd_drop(_glfw.x11.drag.current_target, timestamp);\n    } else {\n        // Drag was cancelled or not accepted\n        _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n        if (window) {\n            GLFWDragEvent ev = {.type = GLFW_DRAG_CANCELLED};\n            _glfwInputDragSourceRequest(window, &ev);\n        }\n        _glfwFreeDragSourceData();\n    }\n}\n\n// Handle XdndStatus message from drop target\nstatic void\nhandle_xdnd_status(const XClientMessageEvent *event) {\n    if (!_glfw.x11.drag.active) return;\n    if (event->data.l[0] != (long)_glfw.x11.drag.current_target) return;\n\n    _glfw.x11.drag.waiting_for_status = false;\n    _glfw.x11.drag.accepted = (event->data.l[1] & 1) != 0;\n\n    if (_glfw.x11.drag.accepted && event->data.l[4] != None) {\n        _glfw.x11.drag.accepted_action = event->data.l[4];\n    }\n}\n\n// Handle XdndFinished message from drop target\nstatic void\nhandle_xdnd_finished(const XClientMessageEvent *event) {\n    if (!_glfw.x11.drag.active) return;\n    if (event->data.l[0] != (long)_glfw.x11.drag.current_target) return;\n\n    _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n    if (window) {\n        bool accepted = (event->data.l[1] & 1) != 0;\n        GLFWDragOperationType action = 0;\n\n        if (accepted && event->data.l[2] != None) {\n            Atom action_atom = event->data.l[2];\n            if (action_atom == _glfw.x11.XdndActionCopy) {\n                action = GLFW_DRAG_OPERATION_COPY;\n            } else if (action_atom == _glfw.x11.XdndActionMove) {\n                action = GLFW_DRAG_OPERATION_MOVE;\n            } else if (action_atom == _glfw.x11.XdndActionLink) {\n                action = GLFW_DRAG_OPERATION_GENERIC;\n            }\n        }\n\n        GLFWDragEvent ev = {.type = GLFW_DRAG_FINSHED, .action = action};\n        _glfwInputDragSourceRequest(window, &ev);\n    }\n\n    _glfwFreeDragSourceData();\n}\n\n// Add a pending data request\nstatic bool\nadd_pending_request(const char *mime_type, Window requestor, Atom property, Atom target) {\n    if (_glfw.x11.drag.pending_count >= _glfw.x11.drag.pending_capacity) {\n        size_t new_capacity = _glfw.x11.drag.pending_capacity == 0 ? 8 : _glfw.x11.drag.pending_capacity * 2;\n        void *new_ptr = realloc(_glfw.x11.drag.pending_requests,\n                               new_capacity * sizeof(_glfw.x11.drag.pending_requests[0]));\n        if (!new_ptr) return false;\n        _glfw.x11.drag.pending_requests = new_ptr;\n        _glfw.x11.drag.pending_capacity = new_capacity;\n    }\n\n    size_t idx = _glfw.x11.drag.pending_count++;\n    _glfw.x11.drag.pending_requests[idx].mime_type = _glfw_strdup(mime_type);\n    _glfw.x11.drag.pending_requests[idx].requestor = requestor;\n    _glfw.x11.drag.pending_requests[idx].property = property;\n    _glfw.x11.drag.pending_requests[idx].target = target;\n    _glfw.x11.drag.pending_requests[idx].inflight = true;\n\n    return _glfw.x11.drag.pending_requests[idx].mime_type != NULL;\n}\n\n// Send drag data via XChangeProperty\nstatic void\nsend_drag_data(const char *mime_type, const char *data, size_t data_sz,\n               Window requestor, Atom property, Atom target) {\n    (void)mime_type;  // Parameter kept for consistency with other platforms\n    if (data && data_sz > 0) {\n        XChangeProperty(_glfw.x11.display, requestor, property,\n                       target, 8, PropModeReplace,\n                       (unsigned char*)data, data_sz);\n    } else {\n        // Send empty property to indicate no data\n        XChangeProperty(_glfw.x11.display, requestor, property,\n                       target, 8, PropModeReplace,\n                       (unsigned char*)\"\", 0);\n    }\n}\n\nint\n_glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {\n    // If a drag is already active, cancel it first\n    if (_glfw.x11.drag.active) {\n        _glfwFreeDragSourceData();\n    }\n\n    // Convert MIME types to atoms\n    _glfw.x11.drag.type_count = _glfw.drag.item_count;\n    _glfw.x11.drag.type_atoms = calloc(_glfw.drag.item_count, sizeof(Atom));\n    if (!_glfw.x11.drag.type_atoms) {\n        return ENOMEM;\n    }\n\n    for (size_t i = 0; i < _glfw.drag.item_count; i++) {\n        _glfw.x11.drag.type_atoms[i] = XInternAtom(_glfw.x11.display,\n                                                    _glfw.drag.items[i].mime_type,\n                                                    False);\n    }\n\n    // Determine action atom based on operations\n    if (_glfw.drag.operations & GLFW_DRAG_OPERATION_COPY) {\n        _glfw.x11.drag.action_atom = _glfw.x11.XdndActionCopy;\n    } else if (_glfw.drag.operations & GLFW_DRAG_OPERATION_MOVE) {\n        _glfw.x11.drag.action_atom = _glfw.x11.XdndActionMove;\n    } else {\n        _glfw.x11.drag.action_atom = _glfw.x11.XdndActionCopy;\n    }\n\n    _glfw.x11.drag.source_window = window->x11.handle;\n    _glfw.x11.drag.active = true;\n    _glfw.x11.drag.current_target = None;\n    _glfw.x11.drag.waiting_for_status = false;\n    _glfw.x11.drag.accepted = false;\n\n    // Initialize thumbnail fields\n    _glfw.x11.drag.thumbnail_window = None;\n    _glfw.x11.drag.thumbnail_pixmap = None;\n    _glfw.x11.drag.thumbnail_gc = None;\n\n    // Get current cursor position for thumbnail placement\n    Window root_return, child_return;\n    int root_x, root_y, win_x, win_y;\n    unsigned int mask_return;\n    XQueryPointer(_glfw.x11.display, _glfw.x11.root,\n                  &root_return, &child_return,\n                  &root_x, &root_y, &win_x, &win_y, &mask_return);\n\n    // Create thumbnail window if thumbnail is provided\n    if (!create_drag_thumbnail(thumbnail, root_x + 10, root_y + 10)) {\n        // Thumbnail creation failed, but continue with drag operation\n        _glfw.x11.drag.thumbnail_window = None;\n        _glfw.x11.drag.thumbnail_pixmap = None;\n        _glfw.x11.drag.thumbnail_gc = None;\n    }\n\n    // Grab the pointer to track drag motion\n    int result = XGrabPointer(_glfw.x11.display, window->x11.handle, False,\n                             ButtonPressMask | ButtonReleaseMask | PointerMotionMask,\n                             GrabModeAsync, GrabModeAsync,\n                             None, None, CurrentTime);\n\n    if (result != GrabSuccess) {\n        free(_glfw.x11.drag.type_atoms);\n        _glfw.x11.drag.type_atoms = NULL;\n        _glfw.x11.drag.type_count = 0;\n        _glfw.x11.drag.active = false;\n        // Clean up thumbnail if it was created\n        if (_glfw.x11.drag.thumbnail_gc != None) {\n            XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);\n            _glfw.x11.drag.thumbnail_gc = None;\n        }\n        if (_glfw.x11.drag.thumbnail_pixmap != None) {\n            XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);\n            _glfw.x11.drag.thumbnail_pixmap = None;\n        }\n        if (_glfw.x11.drag.thumbnail_window != None) {\n            XDestroyWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);\n            _glfw.x11.drag.thumbnail_window = None;\n        }\n        return EIO;\n    }\n\n    // Set ourselves as the XdndSelection owner\n    XSetSelectionOwner(_glfw.x11.display, _glfw.x11.XdndSelection,\n                       window->x11.handle, CurrentTime);\n\n    return 0;\n}\n\nvoid\n_glfwPlatformFreeDragSourceData(void) {\n    if (_glfw.x11.drag.active) {\n        // Send leave to current target\n        if (_glfw.x11.drag.current_target != None) {\n            send_xdnd_leave(_glfw.x11.drag.current_target);\n        }\n\n        // Ungrab the pointer\n        XUngrabPointer(_glfw.x11.display, CurrentTime);\n\n        _glfw.x11.drag.active = false;\n        _glfw.x11.drag.current_target = None;\n    }\n\n    // Clean up thumbnail resources\n    if (_glfw.x11.drag.thumbnail_gc != None) {\n        XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);\n        _glfw.x11.drag.thumbnail_gc = None;\n    }\n    if (_glfw.x11.drag.thumbnail_pixmap != None) {\n        XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);\n        _glfw.x11.drag.thumbnail_pixmap = None;\n    }\n    if (_glfw.x11.drag.thumbnail_window != None) {\n        XDestroyWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);\n        _glfw.x11.drag.thumbnail_window = None;\n    }\n\n    // Free type atoms\n    if (_glfw.x11.drag.type_atoms) {\n        free(_glfw.x11.drag.type_atoms);\n        _glfw.x11.drag.type_atoms = NULL;\n    }\n    _glfw.x11.drag.type_count = 0;\n\n    // Free pending requests\n    if (_glfw.x11.drag.pending_requests) {\n        for (size_t i = 0; i < _glfw.x11.drag.pending_count; i++) {\n            free((void*)_glfw.x11.drag.pending_requests[i].mime_type);\n        }\n        free(_glfw.x11.drag.pending_requests);\n        _glfw.x11.drag.pending_requests = NULL;\n    }\n    _glfw.x11.drag.pending_count = 0;\n    _glfw.x11.drag.pending_capacity = 0;\n}\n\nint\n_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {\n    if (!_glfw.x11.drag.active) return 0;\n    if (!thumbnail || !thumbnail->pixels || thumbnail->width <= 0 || thumbnail->height <= 0) return 0;\n\n    if (_glfw.x11.drag.thumbnail_window == None) {\n        // No thumbnail window yet; query cursor position and create one\n        Window root_return, child_return;\n        int root_x, root_y, win_x, win_y;\n        unsigned int mask_return;\n        XQueryPointer(_glfw.x11.display, _glfw.x11.root,\n                      &root_return, &child_return,\n                      &root_x, &root_y, &win_x, &win_y, &mask_return);\n        if (!create_drag_thumbnail(thumbnail, root_x + 10, root_y + 10)) return EIO;\n        return 0;\n    }\n\n    // Resize the existing window to match the new thumbnail dimensions\n    XResizeWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window,\n                  thumbnail->width, thumbnail->height);\n\n    if (!render_drag_thumbnail_to_pixmap(thumbnail)) return EIO;\n\n    XClearWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);\n    XFlush(_glfw.x11.display);\n    return 0;\n}\n\nint\n_glfwPlatformDragDataReady(const char *mime_type) {\n    // Find the pending request for this MIME type\n    for (size_t i = 0; i < _glfw.x11.drag.pending_count; i++) {\n        if (_glfw.x11.drag.pending_requests[i].inflight &&\n            strcmp(_glfw.x11.drag.pending_requests[i].mime_type, mime_type) == 0) {\n\n            // Get the drag event with data from the application\n            _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);\n            if (!window) return ENODEV;\n\n            GLFWDragEvent ev = {\n                .type = GLFW_DRAG_DATA_REQUEST,\n                .mime_type = mime_type,\n                .data = NULL,\n                .data_sz = 0,\n                .err_num = 0\n            };\n\n            _glfwInputDragSourceRequest(window, &ev);\n\n            // Send the data\n            send_drag_data(mime_type, ev.data, ev.data_sz,\n                          _glfw.x11.drag.pending_requests[i].requestor,\n                          _glfw.x11.drag.pending_requests[i].property,\n                          _glfw.x11.drag.pending_requests[i].target);\n\n            // Send SelectionNotify\n            XEvent reply;\n            memset(&reply, 0, sizeof(reply));\n            reply.xselection.type = SelectionNotify;\n            reply.xselection.display = _glfw.x11.display;\n            reply.xselection.requestor = _glfw.x11.drag.pending_requests[i].requestor;\n            reply.xselection.selection = _glfw.x11.XdndSelection;\n            reply.xselection.target = _glfw.x11.drag.pending_requests[i].target;\n            reply.xselection.property = _glfw.x11.drag.pending_requests[i].property;\n            reply.xselection.time = CurrentTime;\n\n            XSendEvent(_glfw.x11.display,\n                      _glfw.x11.drag.pending_requests[i].requestor,\n                      False, 0, &reply);\n\n            _glfw.x11.drag.pending_requests[i].inflight = false;\n            break;\n        }\n    }\n\n    return 0;\n}\n// }}}\n"
  },
  {
    "path": "glfw/xkb-compat-shim.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <xkbcommon/xkbcommon.h>\n// needed for xkbcommon < 1.0\n\n\n/* We don't use the uint32_t types here, to save some space. */\nstruct codepair {\n    uint16_t keysym;\n    uint16_t ucs;\n};\n\nstatic const struct codepair keysymtab[] = {\n    { 0x01a1, 0x0104 }, /*                     Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */\n    { 0x01a2, 0x02d8 }, /*                       breve ˘ BREVE */\n    { 0x01a3, 0x0141 }, /*                     Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */\n    { 0x01a5, 0x013d }, /*                      Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */\n    { 0x01a6, 0x015a }, /*                      Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */\n    { 0x01a9, 0x0160 }, /*                      Scaron Š LATIN CAPITAL LETTER S WITH CARON */\n    { 0x01aa, 0x015e }, /*                    Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */\n    { 0x01ab, 0x0164 }, /*                      Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */\n    { 0x01ac, 0x0179 }, /*                      Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */\n    { 0x01ae, 0x017d }, /*                      Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */\n    { 0x01af, 0x017b }, /*                   Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */\n    { 0x01b1, 0x0105 }, /*                     aogonek ą LATIN SMALL LETTER A WITH OGONEK */\n    { 0x01b2, 0x02db }, /*                      ogonek ˛ OGONEK */\n    { 0x01b3, 0x0142 }, /*                     lstroke ł LATIN SMALL LETTER L WITH STROKE */\n    { 0x01b5, 0x013e }, /*                      lcaron ľ LATIN SMALL LETTER L WITH CARON */\n    { 0x01b6, 0x015b }, /*                      sacute ś LATIN SMALL LETTER S WITH ACUTE */\n    { 0x01b7, 0x02c7 }, /*                       caron ˇ CARON */\n    { 0x01b9, 0x0161 }, /*                      scaron š LATIN SMALL LETTER S WITH CARON */\n    { 0x01ba, 0x015f }, /*                    scedilla ş LATIN SMALL LETTER S WITH CEDILLA */\n    { 0x01bb, 0x0165 }, /*                      tcaron ť LATIN SMALL LETTER T WITH CARON */\n    { 0x01bc, 0x017a }, /*                      zacute ź LATIN SMALL LETTER Z WITH ACUTE */\n    { 0x01bd, 0x02dd }, /*                 doubleacute ˝ DOUBLE ACUTE ACCENT */\n    { 0x01be, 0x017e }, /*                      zcaron ž LATIN SMALL LETTER Z WITH CARON */\n    { 0x01bf, 0x017c }, /*                   zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */\n    { 0x01c0, 0x0154 }, /*                      Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */\n    { 0x01c3, 0x0102 }, /*                      Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */\n    { 0x01c5, 0x0139 }, /*                      Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */\n    { 0x01c6, 0x0106 }, /*                      Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */\n    { 0x01c8, 0x010c }, /*                      Ccaron Č LATIN CAPITAL LETTER C WITH CARON */\n    { 0x01ca, 0x0118 }, /*                     Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */\n    { 0x01cc, 0x011a }, /*                      Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */\n    { 0x01cf, 0x010e }, /*                      Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */\n    { 0x01d0, 0x0110 }, /*                     Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */\n    { 0x01d1, 0x0143 }, /*                      Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */\n    { 0x01d2, 0x0147 }, /*                      Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */\n    { 0x01d5, 0x0150 }, /*                Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */\n    { 0x01d8, 0x0158 }, /*                      Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */\n    { 0x01d9, 0x016e }, /*                       Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */\n    { 0x01db, 0x0170 }, /*                Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */\n    { 0x01de, 0x0162 }, /*                    Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */\n    { 0x01e0, 0x0155 }, /*                      racute ŕ LATIN SMALL LETTER R WITH ACUTE */\n    { 0x01e3, 0x0103 }, /*                      abreve ă LATIN SMALL LETTER A WITH BREVE */\n    { 0x01e5, 0x013a }, /*                      lacute ĺ LATIN SMALL LETTER L WITH ACUTE */\n    { 0x01e6, 0x0107 }, /*                      cacute ć LATIN SMALL LETTER C WITH ACUTE */\n    { 0x01e8, 0x010d }, /*                      ccaron č LATIN SMALL LETTER C WITH CARON */\n    { 0x01ea, 0x0119 }, /*                     eogonek ę LATIN SMALL LETTER E WITH OGONEK */\n    { 0x01ec, 0x011b }, /*                      ecaron ě LATIN SMALL LETTER E WITH CARON */\n    { 0x01ef, 0x010f }, /*                      dcaron ď LATIN SMALL LETTER D WITH CARON */\n    { 0x01f0, 0x0111 }, /*                     dstroke đ LATIN SMALL LETTER D WITH STROKE */\n    { 0x01f1, 0x0144 }, /*                      nacute ń LATIN SMALL LETTER N WITH ACUTE */\n    { 0x01f2, 0x0148 }, /*                      ncaron ň LATIN SMALL LETTER N WITH CARON */\n    { 0x01f5, 0x0151 }, /*                odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */\n    { 0x01f8, 0x0159 }, /*                      rcaron ř LATIN SMALL LETTER R WITH CARON */\n    { 0x01f9, 0x016f }, /*                       uring ů LATIN SMALL LETTER U WITH RING ABOVE */\n    { 0x01fb, 0x0171 }, /*                udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */\n    { 0x01fe, 0x0163 }, /*                    tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */\n    { 0x01ff, 0x02d9 }, /*                    abovedot ˙ DOT ABOVE */\n    { 0x02a1, 0x0126 }, /*                     Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */\n    { 0x02a6, 0x0124 }, /*                 Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */\n    { 0x02a9, 0x0130 }, /*                   Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */\n    { 0x02ab, 0x011e }, /*                      Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */\n    { 0x02ac, 0x0134 }, /*                 Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */\n    { 0x02b1, 0x0127 }, /*                     hstroke ħ LATIN SMALL LETTER H WITH STROKE */\n    { 0x02b6, 0x0125 }, /*                 hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */\n    { 0x02b9, 0x0131 }, /*                    idotless ı LATIN SMALL LETTER DOTLESS I */\n    { 0x02bb, 0x011f }, /*                      gbreve ğ LATIN SMALL LETTER G WITH BREVE */\n    { 0x02bc, 0x0135 }, /*                 jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */\n    { 0x02c5, 0x010a }, /*                   Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */\n    { 0x02c6, 0x0108 }, /*                 Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */\n    { 0x02d5, 0x0120 }, /*                   Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */\n    { 0x02d8, 0x011c }, /*                 Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */\n    { 0x02dd, 0x016c }, /*                      Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */\n    { 0x02de, 0x015c }, /*                 Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */\n    { 0x02e5, 0x010b }, /*                   cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */\n    { 0x02e6, 0x0109 }, /*                 ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */\n    { 0x02f5, 0x0121 }, /*                   gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */\n    { 0x02f8, 0x011d }, /*                 gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */\n    { 0x02fd, 0x016d }, /*                      ubreve ŭ LATIN SMALL LETTER U WITH BREVE */\n    { 0x02fe, 0x015d }, /*                 scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */\n    { 0x03a2, 0x0138 }, /*                         kra ĸ LATIN SMALL LETTER KRA */\n    { 0x03a3, 0x0156 }, /*                    Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */\n    { 0x03a5, 0x0128 }, /*                      Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */\n    { 0x03a6, 0x013b }, /*                    Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */\n    { 0x03aa, 0x0112 }, /*                     Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */\n    { 0x03ab, 0x0122 }, /*                    Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */\n    { 0x03ac, 0x0166 }, /*                      Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */\n    { 0x03b3, 0x0157 }, /*                    rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */\n    { 0x03b5, 0x0129 }, /*                      itilde ĩ LATIN SMALL LETTER I WITH TILDE */\n    { 0x03b6, 0x013c }, /*                    lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */\n    { 0x03ba, 0x0113 }, /*                     emacron ē LATIN SMALL LETTER E WITH MACRON */\n    { 0x03bb, 0x0123 }, /*                    gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */\n    { 0x03bc, 0x0167 }, /*                      tslash ŧ LATIN SMALL LETTER T WITH STROKE */\n    { 0x03bd, 0x014a }, /*                         ENG Ŋ LATIN CAPITAL LETTER ENG */\n    { 0x03bf, 0x014b }, /*                         eng ŋ LATIN SMALL LETTER ENG */\n    { 0x03c0, 0x0100 }, /*                     Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */\n    { 0x03c7, 0x012e }, /*                     Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */\n    { 0x03cc, 0x0116 }, /*                   Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */\n    { 0x03cf, 0x012a }, /*                     Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */\n    { 0x03d1, 0x0145 }, /*                    Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */\n    { 0x03d2, 0x014c }, /*                     Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */\n    { 0x03d3, 0x0136 }, /*                    Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */\n    { 0x03d9, 0x0172 }, /*                     Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */\n    { 0x03dd, 0x0168 }, /*                      Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */\n    { 0x03de, 0x016a }, /*                     Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */\n    { 0x03e0, 0x0101 }, /*                     amacron ā LATIN SMALL LETTER A WITH MACRON */\n    { 0x03e7, 0x012f }, /*                     iogonek į LATIN SMALL LETTER I WITH OGONEK */\n    { 0x03ec, 0x0117 }, /*                   eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */\n    { 0x03ef, 0x012b }, /*                     imacron ī LATIN SMALL LETTER I WITH MACRON */\n    { 0x03f1, 0x0146 }, /*                    ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */\n    { 0x03f2, 0x014d }, /*                     omacron ō LATIN SMALL LETTER O WITH MACRON */\n    { 0x03f3, 0x0137 }, /*                    kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */\n    { 0x03f9, 0x0173 }, /*                     uogonek ų LATIN SMALL LETTER U WITH OGONEK */\n    { 0x03fd, 0x0169 }, /*                      utilde ũ LATIN SMALL LETTER U WITH TILDE */\n    { 0x03fe, 0x016b }, /*                     umacron ū LATIN SMALL LETTER U WITH MACRON */\n    { 0x047e, 0x203e }, /*                    overline ‾ OVERLINE */\n    { 0x04a1, 0x3002 }, /*               kana_fullstop 。 IDEOGRAPHIC FULL STOP */\n    { 0x04a2, 0x300c }, /*         kana_openingbracket 「 LEFT CORNER BRACKET */\n    { 0x04a3, 0x300d }, /*         kana_closingbracket 」 RIGHT CORNER BRACKET */\n    { 0x04a4, 0x3001 }, /*                  kana_comma 、 IDEOGRAPHIC COMMA */\n    { 0x04a5, 0x30fb }, /*            kana_conjunctive ・ KATAKANA MIDDLE DOT */\n    { 0x04a6, 0x30f2 }, /*                     kana_WO ヲ KATAKANA LETTER WO */\n    { 0x04a7, 0x30a1 }, /*                      kana_a ァ KATAKANA LETTER SMALL A */\n    { 0x04a8, 0x30a3 }, /*                      kana_i ィ KATAKANA LETTER SMALL I */\n    { 0x04a9, 0x30a5 }, /*                      kana_u ゥ KATAKANA LETTER SMALL U */\n    { 0x04aa, 0x30a7 }, /*                      kana_e ェ KATAKANA LETTER SMALL E */\n    { 0x04ab, 0x30a9 }, /*                      kana_o ォ KATAKANA LETTER SMALL O */\n    { 0x04ac, 0x30e3 }, /*                     kana_ya ャ KATAKANA LETTER SMALL YA */\n    { 0x04ad, 0x30e5 }, /*                     kana_yu ュ KATAKANA LETTER SMALL YU */\n    { 0x04ae, 0x30e7 }, /*                     kana_yo ョ KATAKANA LETTER SMALL YO */\n    { 0x04af, 0x30c3 }, /*                    kana_tsu ッ KATAKANA LETTER SMALL TU */\n    { 0x04b0, 0x30fc }, /*              prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */\n    { 0x04b1, 0x30a2 }, /*                      kana_A ア KATAKANA LETTER A */\n    { 0x04b2, 0x30a4 }, /*                      kana_I イ KATAKANA LETTER I */\n    { 0x04b3, 0x30a6 }, /*                      kana_U ウ KATAKANA LETTER U */\n    { 0x04b4, 0x30a8 }, /*                      kana_E エ KATAKANA LETTER E */\n    { 0x04b5, 0x30aa }, /*                      kana_O オ KATAKANA LETTER O */\n    { 0x04b6, 0x30ab }, /*                     kana_KA カ KATAKANA LETTER KA */\n    { 0x04b7, 0x30ad }, /*                     kana_KI キ KATAKANA LETTER KI */\n    { 0x04b8, 0x30af }, /*                     kana_KU ク KATAKANA LETTER KU */\n    { 0x04b9, 0x30b1 }, /*                     kana_KE ケ KATAKANA LETTER KE */\n    { 0x04ba, 0x30b3 }, /*                     kana_KO コ KATAKANA LETTER KO */\n    { 0x04bb, 0x30b5 }, /*                     kana_SA サ KATAKANA LETTER SA */\n    { 0x04bc, 0x30b7 }, /*                    kana_SHI シ KATAKANA LETTER SI */\n    { 0x04bd, 0x30b9 }, /*                     kana_SU ス KATAKANA LETTER SU */\n    { 0x04be, 0x30bb }, /*                     kana_SE セ KATAKANA LETTER SE */\n    { 0x04bf, 0x30bd }, /*                     kana_SO ソ KATAKANA LETTER SO */\n    { 0x04c0, 0x30bf }, /*                     kana_TA タ KATAKANA LETTER TA */\n    { 0x04c1, 0x30c1 }, /*                    kana_CHI チ KATAKANA LETTER TI */\n    { 0x04c2, 0x30c4 }, /*                    kana_TSU ツ KATAKANA LETTER TU */\n    { 0x04c3, 0x30c6 }, /*                     kana_TE テ KATAKANA LETTER TE */\n    { 0x04c4, 0x30c8 }, /*                     kana_TO ト KATAKANA LETTER TO */\n    { 0x04c5, 0x30ca }, /*                     kana_NA ナ KATAKANA LETTER NA */\n    { 0x04c6, 0x30cb }, /*                     kana_NI ニ KATAKANA LETTER NI */\n    { 0x04c7, 0x30cc }, /*                     kana_NU ヌ KATAKANA LETTER NU */\n    { 0x04c8, 0x30cd }, /*                     kana_NE ネ KATAKANA LETTER NE */\n    { 0x04c9, 0x30ce }, /*                     kana_NO ノ KATAKANA LETTER NO */\n    { 0x04ca, 0x30cf }, /*                     kana_HA ハ KATAKANA LETTER HA */\n    { 0x04cb, 0x30d2 }, /*                     kana_HI ヒ KATAKANA LETTER HI */\n    { 0x04cc, 0x30d5 }, /*                     kana_FU フ KATAKANA LETTER HU */\n    { 0x04cd, 0x30d8 }, /*                     kana_HE ヘ KATAKANA LETTER HE */\n    { 0x04ce, 0x30db }, /*                     kana_HO ホ KATAKANA LETTER HO */\n    { 0x04cf, 0x30de }, /*                     kana_MA マ KATAKANA LETTER MA */\n    { 0x04d0, 0x30df }, /*                     kana_MI ミ KATAKANA LETTER MI */\n    { 0x04d1, 0x30e0 }, /*                     kana_MU ム KATAKANA LETTER MU */\n    { 0x04d2, 0x30e1 }, /*                     kana_ME メ KATAKANA LETTER ME */\n    { 0x04d3, 0x30e2 }, /*                     kana_MO モ KATAKANA LETTER MO */\n    { 0x04d4, 0x30e4 }, /*                     kana_YA ヤ KATAKANA LETTER YA */\n    { 0x04d5, 0x30e6 }, /*                     kana_YU ユ KATAKANA LETTER YU */\n    { 0x04d6, 0x30e8 }, /*                     kana_YO ヨ KATAKANA LETTER YO */\n    { 0x04d7, 0x30e9 }, /*                     kana_RA ラ KATAKANA LETTER RA */\n    { 0x04d8, 0x30ea }, /*                     kana_RI リ KATAKANA LETTER RI */\n    { 0x04d9, 0x30eb }, /*                     kana_RU ル KATAKANA LETTER RU */\n    { 0x04da, 0x30ec }, /*                     kana_RE レ KATAKANA LETTER RE */\n    { 0x04db, 0x30ed }, /*                     kana_RO ロ KATAKANA LETTER RO */\n    { 0x04dc, 0x30ef }, /*                     kana_WA ワ KATAKANA LETTER WA */\n    { 0x04dd, 0x30f3 }, /*                      kana_N ン KATAKANA LETTER N */\n    { 0x04de, 0x309b }, /*                 voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */\n    { 0x04df, 0x309c }, /*             semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */\n    { 0x05ac, 0x060c }, /*                Arabic_comma ، ARABIC COMMA */\n    { 0x05bb, 0x061b }, /*            Arabic_semicolon ؛ ARABIC SEMICOLON */\n    { 0x05bf, 0x061f }, /*        Arabic_question_mark ؟ ARABIC QUESTION MARK */\n    { 0x05c1, 0x0621 }, /*                Arabic_hamza ء ARABIC LETTER HAMZA */\n    { 0x05c2, 0x0622 }, /*          Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */\n    { 0x05c3, 0x0623 }, /*          Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */\n    { 0x05c4, 0x0624 }, /*           Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */\n    { 0x05c5, 0x0625 }, /*       Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */\n    { 0x05c6, 0x0626 }, /*           Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */\n    { 0x05c7, 0x0627 }, /*                 Arabic_alef ا ARABIC LETTER ALEF */\n    { 0x05c8, 0x0628 }, /*                  Arabic_beh ب ARABIC LETTER BEH */\n    { 0x05c9, 0x0629 }, /*           Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */\n    { 0x05ca, 0x062a }, /*                  Arabic_teh ت ARABIC LETTER TEH */\n    { 0x05cb, 0x062b }, /*                 Arabic_theh ث ARABIC LETTER THEH */\n    { 0x05cc, 0x062c }, /*                 Arabic_jeem ج ARABIC LETTER JEEM */\n    { 0x05cd, 0x062d }, /*                  Arabic_hah ح ARABIC LETTER HAH */\n    { 0x05ce, 0x062e }, /*                 Arabic_khah خ ARABIC LETTER KHAH */\n    { 0x05cf, 0x062f }, /*                  Arabic_dal د ARABIC LETTER DAL */\n    { 0x05d0, 0x0630 }, /*                 Arabic_thal ذ ARABIC LETTER THAL */\n    { 0x05d1, 0x0631 }, /*                   Arabic_ra ر ARABIC LETTER REH */\n    { 0x05d2, 0x0632 }, /*                 Arabic_zain ز ARABIC LETTER ZAIN */\n    { 0x05d3, 0x0633 }, /*                 Arabic_seen س ARABIC LETTER SEEN */\n    { 0x05d4, 0x0634 }, /*                Arabic_sheen ش ARABIC LETTER SHEEN */\n    { 0x05d5, 0x0635 }, /*                  Arabic_sad ص ARABIC LETTER SAD */\n    { 0x05d6, 0x0636 }, /*                  Arabic_dad ض ARABIC LETTER DAD */\n    { 0x05d7, 0x0637 }, /*                  Arabic_tah ط ARABIC LETTER TAH */\n    { 0x05d8, 0x0638 }, /*                  Arabic_zah ظ ARABIC LETTER ZAH */\n    { 0x05d9, 0x0639 }, /*                  Arabic_ain ع ARABIC LETTER AIN */\n    { 0x05da, 0x063a }, /*                Arabic_ghain غ ARABIC LETTER GHAIN */\n    { 0x05e0, 0x0640 }, /*              Arabic_tatweel ـ ARABIC TATWEEL */\n    { 0x05e1, 0x0641 }, /*                  Arabic_feh ف ARABIC LETTER FEH */\n    { 0x05e2, 0x0642 }, /*                  Arabic_qaf ق ARABIC LETTER QAF */\n    { 0x05e3, 0x0643 }, /*                  Arabic_kaf ك ARABIC LETTER KAF */\n    { 0x05e4, 0x0644 }, /*                  Arabic_lam ل ARABIC LETTER LAM */\n    { 0x05e5, 0x0645 }, /*                 Arabic_meem م ARABIC LETTER MEEM */\n    { 0x05e6, 0x0646 }, /*                 Arabic_noon ن ARABIC LETTER NOON */\n    { 0x05e7, 0x0647 }, /*                   Arabic_ha ه ARABIC LETTER HEH */\n    { 0x05e8, 0x0648 }, /*                  Arabic_waw و ARABIC LETTER WAW */\n    { 0x05e9, 0x0649 }, /*          Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */\n    { 0x05ea, 0x064a }, /*                  Arabic_yeh ي ARABIC LETTER YEH */\n    { 0x05eb, 0x064b }, /*             Arabic_fathatan ً ARABIC FATHATAN */\n    { 0x05ec, 0x064c }, /*             Arabic_dammatan ٌ ARABIC DAMMATAN */\n    { 0x05ed, 0x064d }, /*             Arabic_kasratan ٍ ARABIC KASRATAN */\n    { 0x05ee, 0x064e }, /*                Arabic_fatha َ ARABIC FATHA */\n    { 0x05ef, 0x064f }, /*                Arabic_damma ُ ARABIC DAMMA */\n    { 0x05f0, 0x0650 }, /*                Arabic_kasra ِ ARABIC KASRA */\n    { 0x05f1, 0x0651 }, /*               Arabic_shadda ّ ARABIC SHADDA */\n    { 0x05f2, 0x0652 }, /*                Arabic_sukun ْ ARABIC SUKUN */\n    { 0x06a1, 0x0452 }, /*                 Serbian_dje ђ CYRILLIC SMALL LETTER DJE */\n    { 0x06a2, 0x0453 }, /*               Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */\n    { 0x06a3, 0x0451 }, /*                 Cyrillic_io ё CYRILLIC SMALL LETTER IO */\n    { 0x06a4, 0x0454 }, /*                Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */\n    { 0x06a5, 0x0455 }, /*               Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */\n    { 0x06a6, 0x0456 }, /*                 Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */\n    { 0x06a7, 0x0457 }, /*                Ukrainian_yi ї CYRILLIC SMALL LETTER YI */\n    { 0x06a8, 0x0458 }, /*                 Cyrillic_je ј CYRILLIC SMALL LETTER JE */\n    { 0x06a9, 0x0459 }, /*                Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */\n    { 0x06aa, 0x045a }, /*                Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */\n    { 0x06ab, 0x045b }, /*                Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */\n    { 0x06ac, 0x045c }, /*               Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */\n    { 0x06ad, 0x0491 }, /*   Ukrainian_ghe_with_upturn ґ CYRILLIC SMALL LETTER GHE WITH UPTURN */\n    { 0x06ae, 0x045e }, /*         Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */\n    { 0x06af, 0x045f }, /*               Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */\n    { 0x06b0, 0x2116 }, /*                  numerosign № NUMERO SIGN */\n    { 0x06b1, 0x0402 }, /*                 Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */\n    { 0x06b2, 0x0403 }, /*               Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */\n    { 0x06b3, 0x0401 }, /*                 Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */\n    { 0x06b4, 0x0404 }, /*                Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */\n    { 0x06b5, 0x0405 }, /*               Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */\n    { 0x06b6, 0x0406 }, /*                 Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */\n    { 0x06b7, 0x0407 }, /*                Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */\n    { 0x06b8, 0x0408 }, /*                 Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */\n    { 0x06b9, 0x0409 }, /*                Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */\n    { 0x06ba, 0x040a }, /*                Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */\n    { 0x06bb, 0x040b }, /*                Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */\n    { 0x06bc, 0x040c }, /*               Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */\n    { 0x06bd, 0x0490 }, /*   Ukrainian_GHE_WITH_UPTURN Ґ CYRILLIC CAPITAL LETTER GHE WITH UPTURN */\n    { 0x06be, 0x040e }, /*         Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */\n    { 0x06bf, 0x040f }, /*               Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */\n    { 0x06c0, 0x044e }, /*                 Cyrillic_yu ю CYRILLIC SMALL LETTER YU */\n    { 0x06c1, 0x0430 }, /*                  Cyrillic_a а CYRILLIC SMALL LETTER A */\n    { 0x06c2, 0x0431 }, /*                 Cyrillic_be б CYRILLIC SMALL LETTER BE */\n    { 0x06c3, 0x0446 }, /*                Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */\n    { 0x06c4, 0x0434 }, /*                 Cyrillic_de д CYRILLIC SMALL LETTER DE */\n    { 0x06c5, 0x0435 }, /*                 Cyrillic_ie е CYRILLIC SMALL LETTER IE */\n    { 0x06c6, 0x0444 }, /*                 Cyrillic_ef ф CYRILLIC SMALL LETTER EF */\n    { 0x06c7, 0x0433 }, /*                Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */\n    { 0x06c8, 0x0445 }, /*                 Cyrillic_ha х CYRILLIC SMALL LETTER HA */\n    { 0x06c9, 0x0438 }, /*                  Cyrillic_i и CYRILLIC SMALL LETTER I */\n    { 0x06ca, 0x0439 }, /*             Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */\n    { 0x06cb, 0x043a }, /*                 Cyrillic_ka к CYRILLIC SMALL LETTER KA */\n    { 0x06cc, 0x043b }, /*                 Cyrillic_el л CYRILLIC SMALL LETTER EL */\n    { 0x06cd, 0x043c }, /*                 Cyrillic_em м CYRILLIC SMALL LETTER EM */\n    { 0x06ce, 0x043d }, /*                 Cyrillic_en н CYRILLIC SMALL LETTER EN */\n    { 0x06cf, 0x043e }, /*                  Cyrillic_o о CYRILLIC SMALL LETTER O */\n    { 0x06d0, 0x043f }, /*                 Cyrillic_pe п CYRILLIC SMALL LETTER PE */\n    { 0x06d1, 0x044f }, /*                 Cyrillic_ya я CYRILLIC SMALL LETTER YA */\n    { 0x06d2, 0x0440 }, /*                 Cyrillic_er р CYRILLIC SMALL LETTER ER */\n    { 0x06d3, 0x0441 }, /*                 Cyrillic_es с CYRILLIC SMALL LETTER ES */\n    { 0x06d4, 0x0442 }, /*                 Cyrillic_te т CYRILLIC SMALL LETTER TE */\n    { 0x06d5, 0x0443 }, /*                  Cyrillic_u у CYRILLIC SMALL LETTER U */\n    { 0x06d6, 0x0436 }, /*                Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */\n    { 0x06d7, 0x0432 }, /*                 Cyrillic_ve в CYRILLIC SMALL LETTER VE */\n    { 0x06d8, 0x044c }, /*           Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */\n    { 0x06d9, 0x044b }, /*               Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */\n    { 0x06da, 0x0437 }, /*                 Cyrillic_ze з CYRILLIC SMALL LETTER ZE */\n    { 0x06db, 0x0448 }, /*                Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */\n    { 0x06dc, 0x044d }, /*                  Cyrillic_e э CYRILLIC SMALL LETTER E */\n    { 0x06dd, 0x0449 }, /*              Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */\n    { 0x06de, 0x0447 }, /*                Cyrillic_che ч CYRILLIC SMALL LETTER CHE */\n    { 0x06df, 0x044a }, /*           Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */\n    { 0x06e0, 0x042e }, /*                 Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */\n    { 0x06e1, 0x0410 }, /*                  Cyrillic_A А CYRILLIC CAPITAL LETTER A */\n    { 0x06e2, 0x0411 }, /*                 Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */\n    { 0x06e3, 0x0426 }, /*                Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */\n    { 0x06e4, 0x0414 }, /*                 Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */\n    { 0x06e5, 0x0415 }, /*                 Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */\n    { 0x06e6, 0x0424 }, /*                 Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */\n    { 0x06e7, 0x0413 }, /*                Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */\n    { 0x06e8, 0x0425 }, /*                 Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */\n    { 0x06e9, 0x0418 }, /*                  Cyrillic_I И CYRILLIC CAPITAL LETTER I */\n    { 0x06ea, 0x0419 }, /*             Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */\n    { 0x06eb, 0x041a }, /*                 Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */\n    { 0x06ec, 0x041b }, /*                 Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */\n    { 0x06ed, 0x041c }, /*                 Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */\n    { 0x06ee, 0x041d }, /*                 Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */\n    { 0x06ef, 0x041e }, /*                  Cyrillic_O О CYRILLIC CAPITAL LETTER O */\n    { 0x06f0, 0x041f }, /*                 Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */\n    { 0x06f1, 0x042f }, /*                 Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */\n    { 0x06f2, 0x0420 }, /*                 Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */\n    { 0x06f3, 0x0421 }, /*                 Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */\n    { 0x06f4, 0x0422 }, /*                 Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */\n    { 0x06f5, 0x0423 }, /*                  Cyrillic_U У CYRILLIC CAPITAL LETTER U */\n    { 0x06f6, 0x0416 }, /*                Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */\n    { 0x06f7, 0x0412 }, /*                 Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */\n    { 0x06f8, 0x042c }, /*           Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */\n    { 0x06f9, 0x042b }, /*               Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */\n    { 0x06fa, 0x0417 }, /*                 Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */\n    { 0x06fb, 0x0428 }, /*                Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */\n    { 0x06fc, 0x042d }, /*                  Cyrillic_E Э CYRILLIC CAPITAL LETTER E */\n    { 0x06fd, 0x0429 }, /*              Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */\n    { 0x06fe, 0x0427 }, /*                Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */\n    { 0x06ff, 0x042a }, /*           Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */\n    { 0x07a1, 0x0386 }, /*           Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */\n    { 0x07a2, 0x0388 }, /*         Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */\n    { 0x07a3, 0x0389 }, /*             Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */\n    { 0x07a4, 0x038a }, /*            Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */\n    { 0x07a5, 0x03aa }, /*         Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */\n    { 0x07a7, 0x038c }, /*         Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */\n    { 0x07a8, 0x038e }, /*         Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */\n    { 0x07a9, 0x03ab }, /*       Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */\n    { 0x07ab, 0x038f }, /*           Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */\n    { 0x07ae, 0x0385 }, /*        Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */\n    { 0x07af, 0x2015 }, /*              Greek_horizbar ― HORIZONTAL BAR */\n    { 0x07b1, 0x03ac }, /*           Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */\n    { 0x07b2, 0x03ad }, /*         Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */\n    { 0x07b3, 0x03ae }, /*             Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */\n    { 0x07b4, 0x03af }, /*            Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */\n    { 0x07b5, 0x03ca }, /*          Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */\n    { 0x07b6, 0x0390 }, /*    Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */\n    { 0x07b7, 0x03cc }, /*         Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */\n    { 0x07b8, 0x03cd }, /*         Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */\n    { 0x07b9, 0x03cb }, /*       Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */\n    { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */\n    { 0x07bb, 0x03ce }, /*           Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */\n    { 0x07c1, 0x0391 }, /*                 Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */\n    { 0x07c2, 0x0392 }, /*                  Greek_BETA Β GREEK CAPITAL LETTER BETA */\n    { 0x07c3, 0x0393 }, /*                 Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */\n    { 0x07c4, 0x0394 }, /*                 Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */\n    { 0x07c5, 0x0395 }, /*               Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */\n    { 0x07c6, 0x0396 }, /*                  Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */\n    { 0x07c7, 0x0397 }, /*                   Greek_ETA Η GREEK CAPITAL LETTER ETA */\n    { 0x07c8, 0x0398 }, /*                 Greek_THETA Θ GREEK CAPITAL LETTER THETA */\n    { 0x07c9, 0x0399 }, /*                  Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */\n    { 0x07ca, 0x039a }, /*                 Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */\n    { 0x07cb, 0x039b }, /*                Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMBDA */\n    { 0x07cc, 0x039c }, /*                    Greek_MU Μ GREEK CAPITAL LETTER MU */\n    { 0x07cd, 0x039d }, /*                    Greek_NU Ν GREEK CAPITAL LETTER NU */\n    { 0x07ce, 0x039e }, /*                    Greek_XI Ξ GREEK CAPITAL LETTER XI */\n    { 0x07cf, 0x039f }, /*               Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */\n    { 0x07d0, 0x03a0 }, /*                    Greek_PI Π GREEK CAPITAL LETTER PI */\n    { 0x07d1, 0x03a1 }, /*                   Greek_RHO Ρ GREEK CAPITAL LETTER RHO */\n    { 0x07d2, 0x03a3 }, /*                 Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */\n    { 0x07d4, 0x03a4 }, /*                   Greek_TAU Τ GREEK CAPITAL LETTER TAU */\n    { 0x07d5, 0x03a5 }, /*               Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */\n    { 0x07d6, 0x03a6 }, /*                   Greek_PHI Φ GREEK CAPITAL LETTER PHI */\n    { 0x07d7, 0x03a7 }, /*                   Greek_CHI Χ GREEK CAPITAL LETTER CHI */\n    { 0x07d8, 0x03a8 }, /*                   Greek_PSI Ψ GREEK CAPITAL LETTER PSI */\n    { 0x07d9, 0x03a9 }, /*                 Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */\n    { 0x07e1, 0x03b1 }, /*                 Greek_alpha α GREEK SMALL LETTER ALPHA */\n    { 0x07e2, 0x03b2 }, /*                  Greek_beta β GREEK SMALL LETTER BETA */\n    { 0x07e3, 0x03b3 }, /*                 Greek_gamma γ GREEK SMALL LETTER GAMMA */\n    { 0x07e4, 0x03b4 }, /*                 Greek_delta δ GREEK SMALL LETTER DELTA */\n    { 0x07e5, 0x03b5 }, /*               Greek_epsilon ε GREEK SMALL LETTER EPSILON */\n    { 0x07e6, 0x03b6 }, /*                  Greek_zeta ζ GREEK SMALL LETTER ZETA */\n    { 0x07e7, 0x03b7 }, /*                   Greek_eta η GREEK SMALL LETTER ETA */\n    { 0x07e8, 0x03b8 }, /*                 Greek_theta θ GREEK SMALL LETTER THETA */\n    { 0x07e9, 0x03b9 }, /*                  Greek_iota ι GREEK SMALL LETTER IOTA */\n    { 0x07ea, 0x03ba }, /*                 Greek_kappa κ GREEK SMALL LETTER KAPPA */\n    { 0x07eb, 0x03bb }, /*                Greek_lambda λ GREEK SMALL LETTER LAMBDA */\n    { 0x07ec, 0x03bc }, /*                    Greek_mu μ GREEK SMALL LETTER MU */\n    { 0x07ed, 0x03bd }, /*                    Greek_nu ν GREEK SMALL LETTER NU */\n    { 0x07ee, 0x03be }, /*                    Greek_xi ξ GREEK SMALL LETTER XI */\n    { 0x07ef, 0x03bf }, /*               Greek_omicron ο GREEK SMALL LETTER OMICRON */\n    { 0x07f0, 0x03c0 }, /*                    Greek_pi π GREEK SMALL LETTER PI */\n    { 0x07f1, 0x03c1 }, /*                   Greek_rho ρ GREEK SMALL LETTER RHO */\n    { 0x07f2, 0x03c3 }, /*                 Greek_sigma σ GREEK SMALL LETTER SIGMA */\n    { 0x07f3, 0x03c2 }, /*       Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */\n    { 0x07f4, 0x03c4 }, /*                   Greek_tau τ GREEK SMALL LETTER TAU */\n    { 0x07f5, 0x03c5 }, /*               Greek_upsilon υ GREEK SMALL LETTER UPSILON */\n    { 0x07f6, 0x03c6 }, /*                   Greek_phi φ GREEK SMALL LETTER PHI */\n    { 0x07f7, 0x03c7 }, /*                   Greek_chi χ GREEK SMALL LETTER CHI */\n    { 0x07f8, 0x03c8 }, /*                   Greek_psi ψ GREEK SMALL LETTER PSI */\n    { 0x07f9, 0x03c9 }, /*                 Greek_omega ω GREEK SMALL LETTER OMEGA */\n    { 0x08a1, 0x23b7 }, /*                 leftradical ⎷ ??? */\n    { 0x08a2, 0x250c }, /*              topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */\n    { 0x08a3, 0x2500 }, /*              horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */\n    { 0x08a4, 0x2320 }, /*                 topintegral ⌠ TOP HALF INTEGRAL */\n    { 0x08a5, 0x2321 }, /*                 botintegral ⌡ BOTTOM HALF INTEGRAL */\n    { 0x08a6, 0x2502 }, /*               vertconnector │ BOX DRAWINGS LIGHT VERTICAL */\n    { 0x08a7, 0x23a1 }, /*            topleftsqbracket ⎡ ??? */\n    { 0x08a8, 0x23a3 }, /*            botleftsqbracket ⎣ ??? */\n    { 0x08a9, 0x23a4 }, /*           toprightsqbracket ⎤ ??? */\n    { 0x08aa, 0x23a6 }, /*           botrightsqbracket ⎦ ??? */\n    { 0x08ab, 0x239b }, /*               topleftparens ⎛ ??? */\n    { 0x08ac, 0x239d }, /*               botleftparens ⎝ ??? */\n    { 0x08ad, 0x239e }, /*              toprightparens ⎞ ??? */\n    { 0x08ae, 0x23a0 }, /*              botrightparens ⎠ ??? */\n    { 0x08af, 0x23a8 }, /*        leftmiddlecurlybrace ⎨ ??? */\n    { 0x08b0, 0x23ac }, /*       rightmiddlecurlybrace ⎬ ??? */\n    /*  0x08b1                        topleftsummation ? ??? */\n    /*  0x08b2                        botleftsummation ? ??? */\n    /*  0x08b3               topvertsummationconnector ? ??? */\n    /*  0x08b4               botvertsummationconnector ? ??? */\n    /*  0x08b5                       toprightsummation ? ??? */\n    /*  0x08b6                       botrightsummation ? ??? */\n    /*  0x08b7                    rightmiddlesummation ? ??? */\n    { 0x08bc, 0x2264 }, /*               lessthanequal ≤ LESS-THAN OR EQUAL TO */\n    { 0x08bd, 0x2260 }, /*                    notequal ≠ NOT EQUAL TO */\n    { 0x08be, 0x2265 }, /*            greaterthanequal ≥ GREATER-THAN OR EQUAL TO */\n    { 0x08bf, 0x222b }, /*                    integral ∫ INTEGRAL */\n    { 0x08c0, 0x2234 }, /*                   therefore ∴ THEREFORE */\n    { 0x08c1, 0x221d }, /*                   variation ∝ PROPORTIONAL TO */\n    { 0x08c2, 0x221e }, /*                    infinity ∞ INFINITY */\n    { 0x08c5, 0x2207 }, /*                       nabla ∇ NABLA */\n    { 0x08c8, 0x223c }, /*                 approximate ∼ TILDE OPERATOR */\n    { 0x08c9, 0x2243 }, /*                similarequal ≃ ASYMPTOTICALLY EQUAL TO */\n    { 0x08cd, 0x21d4 }, /*                    ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */\n    { 0x08ce, 0x21d2 }, /*                     implies ⇒ RIGHTWARDS DOUBLE ARROW */\n    { 0x08cf, 0x2261 }, /*                   identical ≡ IDENTICAL TO */\n    { 0x08d6, 0x221a }, /*                     radical √ SQUARE ROOT */\n    { 0x08da, 0x2282 }, /*                  includedin ⊂ SUBSET OF */\n    { 0x08db, 0x2283 }, /*                    includes ⊃ SUPERSET OF */\n    { 0x08dc, 0x2229 }, /*                intersection ∩ INTERSECTION */\n    { 0x08dd, 0x222a }, /*                       union ∪ UNION */\n    { 0x08de, 0x2227 }, /*                  logicaland ∧ LOGICAL AND */\n    { 0x08df, 0x2228 }, /*                   logicalor ∨ LOGICAL OR */\n    { 0x08ef, 0x2202 }, /*           partialderivative ∂ PARTIAL DIFFERENTIAL */\n    { 0x08f6, 0x0192 }, /*                    function ƒ LATIN SMALL LETTER F WITH HOOK */\n    { 0x08fb, 0x2190 }, /*                   leftarrow ← LEFTWARDS ARROW */\n    { 0x08fc, 0x2191 }, /*                     uparrow ↑ UPWARDS ARROW */\n    { 0x08fd, 0x2192 }, /*                  rightarrow → RIGHTWARDS ARROW */\n    { 0x08fe, 0x2193 }, /*                   downarrow ↓ DOWNWARDS ARROW */\n/*  0x09df                                     blank ? ??? */\n    { 0x09e0, 0x25c6 }, /*                soliddiamond ◆ BLACK DIAMOND */\n    { 0x09e1, 0x2592 }, /*                checkerboard ▒ MEDIUM SHADE */\n    { 0x09e2, 0x2409 }, /*                          ht ␉ SYMBOL FOR HORIZONTAL TABULATION */\n    { 0x09e3, 0x240c }, /*                          ff ␌ SYMBOL FOR FORM FEED */\n    { 0x09e4, 0x240d }, /*                          cr ␍ SYMBOL FOR CARRIAGE RETURN */\n    { 0x09e5, 0x240a }, /*                          lf ␊ SYMBOL FOR LINE FEED */\n    { 0x09e8, 0x2424 }, /*                          nl ␤ SYMBOL FOR NEWLINE */\n    { 0x09e9, 0x240b }, /*                          vt ␋ SYMBOL FOR VERTICAL TABULATION */\n    { 0x09ea, 0x2518 }, /*              lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */\n    { 0x09eb, 0x2510 }, /*               uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */\n    { 0x09ec, 0x250c }, /*                upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */\n    { 0x09ed, 0x2514 }, /*               lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */\n    { 0x09ee, 0x253c }, /*               crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */\n    { 0x09ef, 0x23ba }, /*              horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */\n    { 0x09f0, 0x23bb }, /*              horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */\n    { 0x09f1, 0x2500 }, /*              horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */\n    { 0x09f2, 0x23bc }, /*              horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */\n    { 0x09f3, 0x23bd }, /*              horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */\n    { 0x09f4, 0x251c }, /*                       leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */\n    { 0x09f5, 0x2524 }, /*                      rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */\n    { 0x09f6, 0x2534 }, /*                        bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */\n    { 0x09f7, 0x252c }, /*                        topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */\n    { 0x09f8, 0x2502 }, /*                     vertbar │ BOX DRAWINGS LIGHT VERTICAL */\n    { 0x0aa1, 0x2003 }, /*                     emspace   EM SPACE */\n    { 0x0aa2, 0x2002 }, /*                     enspace   EN SPACE */\n    { 0x0aa3, 0x2004 }, /*                    em3space   THREE-PER-EM SPACE */\n    { 0x0aa4, 0x2005 }, /*                    em4space   FOUR-PER-EM SPACE */\n    { 0x0aa5, 0x2007 }, /*                  digitspace   FIGURE SPACE */\n    { 0x0aa6, 0x2008 }, /*                  punctspace   PUNCTUATION SPACE */\n    { 0x0aa7, 0x2009 }, /*                   thinspace   THIN SPACE */\n    { 0x0aa8, 0x200a }, /*                   hairspace   HAIR SPACE */\n    { 0x0aa9, 0x2014 }, /*                      emdash — EM DASH */\n    { 0x0aaa, 0x2013 }, /*                      endash – EN DASH */\n    { 0x0aac, 0x2423 }, /*                 signifblank ␣ OPEN BOX */\n    { 0x0aae, 0x2026 }, /*                    ellipsis … HORIZONTAL ELLIPSIS */\n    { 0x0aaf, 0x2025 }, /*             doubbaselinedot ‥ TWO DOT LEADER */\n    { 0x0ab0, 0x2153 }, /*                    onethird ⅓ VULGAR FRACTION ONE THIRD */\n    { 0x0ab1, 0x2154 }, /*                   twothirds ⅔ VULGAR FRACTION TWO THIRDS */\n    { 0x0ab2, 0x2155 }, /*                    onefifth ⅕ VULGAR FRACTION ONE FIFTH */\n    { 0x0ab3, 0x2156 }, /*                   twofifths ⅖ VULGAR FRACTION TWO FIFTHS */\n    { 0x0ab4, 0x2157 }, /*                 threefifths ⅗ VULGAR FRACTION THREE FIFTHS */\n    { 0x0ab5, 0x2158 }, /*                  fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */\n    { 0x0ab6, 0x2159 }, /*                    onesixth ⅙ VULGAR FRACTION ONE SIXTH */\n    { 0x0ab7, 0x215a }, /*                  fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */\n    { 0x0ab8, 0x2105 }, /*                      careof ℅ CARE OF */\n    { 0x0abb, 0x2012 }, /*                     figdash ‒ FIGURE DASH */\n    { 0x0abc, 0x27e8 }, /*            leftanglebracket ⟨ MATHEMATICAL LEFT ANGLE BRACKET */\n    { 0x0abd, 0x002e }, /*                decimalpoint . FULL STOP */\n    { 0x0abe, 0x27e9 }, /*           rightanglebracket ⟩ MATHEMATICAL RIGHT ANGLE BRACKET */\n    /*  0x0abf                                  marker ? ??? */\n    { 0x0ac3, 0x215b }, /*                   oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */\n    { 0x0ac4, 0x215c }, /*                threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */\n    { 0x0ac5, 0x215d }, /*                 fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */\n    { 0x0ac6, 0x215e }, /*                seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */\n    { 0x0ac9, 0x2122 }, /*                   trademark ™ TRADE MARK SIGN */\n    { 0x0aca, 0x2613 }, /*               signaturemark ☓ SALTIRE */\n    /*  0x0acb                       trademarkincircle ? ??? */\n    { 0x0acc, 0x25c1 }, /*            leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */\n    { 0x0acd, 0x25b7 }, /*           rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */\n    { 0x0ace, 0x25cb }, /*                emopencircle ○ WHITE CIRCLE */\n    { 0x0acf, 0x25af }, /*             emopenrectangle ▯ WHITE VERTICAL RECTANGLE */\n    { 0x0ad0, 0x2018 }, /*         leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */\n    { 0x0ad1, 0x2019 }, /*        rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */\n    { 0x0ad2, 0x201c }, /*         leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */\n    { 0x0ad3, 0x201d }, /*        rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */\n    { 0x0ad4, 0x211e }, /*                prescription ℞ PRESCRIPTION TAKE */\n    { 0x0ad5, 0x2030 }, /*                    permille ‰ PER MILLE SIGN */\n    { 0x0ad6, 0x2032 }, /*                     minutes ′ PRIME */\n    { 0x0ad7, 0x2033 }, /*                     seconds ″ DOUBLE PRIME */\n    { 0x0ad9, 0x271d }, /*                  latincross ✝ LATIN CROSS */\n    /*  0x0ada                                hexagram ? ??? */\n    { 0x0adb, 0x25ac }, /*            filledrectbullet ▬ BLACK RECTANGLE */\n    { 0x0adc, 0x25c0 }, /*         filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */\n    { 0x0add, 0x25b6 }, /*        filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */\n    { 0x0ade, 0x25cf }, /*              emfilledcircle ● BLACK CIRCLE */\n    { 0x0adf, 0x25ae }, /*                emfilledrect ▮ BLACK VERTICAL RECTANGLE */\n    { 0x0ae0, 0x25e6 }, /*            enopencircbullet ◦ WHITE BULLET */\n    { 0x0ae1, 0x25ab }, /*          enopensquarebullet ▫ WHITE SMALL SQUARE */\n    { 0x0ae2, 0x25ad }, /*              openrectbullet ▭ WHITE RECTANGLE */\n    { 0x0ae3, 0x25b3 }, /*             opentribulletup △ WHITE UP-POINTING TRIANGLE */\n    { 0x0ae4, 0x25bd }, /*           opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */\n    { 0x0ae5, 0x2606 }, /*                    openstar ☆ WHITE STAR */\n    { 0x0ae6, 0x2022 }, /*          enfilledcircbullet • BULLET */\n    { 0x0ae7, 0x25aa }, /*            enfilledsqbullet ▪ BLACK SMALL SQUARE */\n    { 0x0ae8, 0x25b2 }, /*           filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */\n    { 0x0ae9, 0x25bc }, /*         filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */\n    { 0x0aea, 0x261c }, /*                 leftpointer ☜ WHITE LEFT POINTING INDEX */\n    { 0x0aeb, 0x261e }, /*                rightpointer ☞ WHITE RIGHT POINTING INDEX */\n    { 0x0aec, 0x2663 }, /*                        club ♣ BLACK CLUB SUIT */\n    { 0x0aed, 0x2666 }, /*                     diamond ♦ BLACK DIAMOND SUIT */\n    { 0x0aee, 0x2665 }, /*                       heart ♥ BLACK HEART SUIT */\n    { 0x0af0, 0x2720 }, /*                maltesecross ✠ MALTESE CROSS */\n    { 0x0af1, 0x2020 }, /*                      dagger † DAGGER */\n    { 0x0af2, 0x2021 }, /*                doubledagger ‡ DOUBLE DAGGER */\n    { 0x0af3, 0x2713 }, /*                   checkmark ✓ CHECK MARK */\n    { 0x0af4, 0x2717 }, /*                 ballotcross ✗ BALLOT X */\n    { 0x0af5, 0x266f }, /*                musicalsharp ♯ MUSIC SHARP SIGN */\n    { 0x0af6, 0x266d }, /*                 musicalflat ♭ MUSIC FLAT SIGN */\n    { 0x0af7, 0x2642 }, /*                  malesymbol ♂ MALE SIGN */\n    { 0x0af8, 0x2640 }, /*                femalesymbol ♀ FEMALE SIGN */\n    { 0x0af9, 0x260e }, /*                   telephone ☎ BLACK TELEPHONE */\n    { 0x0afa, 0x2315 }, /*           telephonerecorder ⌕ TELEPHONE RECORDER */\n    { 0x0afb, 0x2117 }, /*         phonographcopyright ℗ SOUND RECORDING COPYRIGHT */\n    { 0x0afc, 0x2038 }, /*                       caret ‸ CARET */\n    { 0x0afd, 0x201a }, /*          singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */\n    { 0x0afe, 0x201e }, /*          doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */\n    /*  0x0aff                                  cursor ? ??? */\n    { 0x0ba3, 0x003c }, /*                   leftcaret < LESS-THAN SIGN */\n    { 0x0ba6, 0x003e }, /*                  rightcaret > GREATER-THAN SIGN */\n    { 0x0ba8, 0x2228 }, /*                   downcaret ∨ LOGICAL OR */\n    { 0x0ba9, 0x2227 }, /*                     upcaret ∧ LOGICAL AND */\n    { 0x0bc0, 0x00af }, /*                     overbar ¯ MACRON */\n    { 0x0bc2, 0x22a4 }, /*                    downtack ⊤ DOWN TACK */\n    { 0x0bc3, 0x2229 }, /*                      upshoe ∩ INTERSECTION */\n    { 0x0bc4, 0x230a }, /*                   downstile ⌊ LEFT FLOOR */\n    { 0x0bc6, 0x005f }, /*                    underbar _ LOW LINE */\n    { 0x0bca, 0x2218 }, /*                         jot ∘ RING OPERATOR */\n    { 0x0bcc, 0x2395 }, /*                        quad ⎕ APL FUNCTIONAL SYMBOL QUAD (Unicode 3.0) */\n    { 0x0bce, 0x22a5 }, /*                      uptack ⊥ UP TACK */\n    { 0x0bcf, 0x25cb }, /*                      circle ○ WHITE CIRCLE */\n    { 0x0bd3, 0x2308 }, /*                     upstile ⌈ LEFT CEILING */\n    { 0x0bd6, 0x222a }, /*                    downshoe ∪ UNION */\n    { 0x0bd8, 0x2283 }, /*                   rightshoe ⊃ SUPERSET OF */\n    { 0x0bda, 0x2282 }, /*                    leftshoe ⊂ SUBSET OF */\n    { 0x0bdc, 0x22a2 }, /*                    lefttack ⊢ RIGHT TACK */\n    { 0x0bfc, 0x22a3 }, /*                   righttack ⊣ LEFT TACK */\n    { 0x0cdf, 0x2017 }, /*        hebrew_doublelowline ‗ DOUBLE LOW LINE */\n    { 0x0ce0, 0x05d0 }, /*                hebrew_aleph א HEBREW LETTER ALEF */\n    { 0x0ce1, 0x05d1 }, /*                  hebrew_bet ב HEBREW LETTER BET */\n    { 0x0ce2, 0x05d2 }, /*                hebrew_gimel ג HEBREW LETTER GIMEL */\n    { 0x0ce3, 0x05d3 }, /*                hebrew_dalet ד HEBREW LETTER DALET */\n    { 0x0ce4, 0x05d4 }, /*                   hebrew_he ה HEBREW LETTER HE */\n    { 0x0ce5, 0x05d5 }, /*                  hebrew_waw ו HEBREW LETTER VAV */\n    { 0x0ce6, 0x05d6 }, /*                 hebrew_zain ז HEBREW LETTER ZAYIN */\n    { 0x0ce7, 0x05d7 }, /*                 hebrew_chet ח HEBREW LETTER HET */\n    { 0x0ce8, 0x05d8 }, /*                  hebrew_tet ט HEBREW LETTER TET */\n    { 0x0ce9, 0x05d9 }, /*                  hebrew_yod י HEBREW LETTER YOD */\n    { 0x0cea, 0x05da }, /*            hebrew_finalkaph ך HEBREW LETTER FINAL KAF */\n    { 0x0ceb, 0x05db }, /*                 hebrew_kaph כ HEBREW LETTER KAF */\n    { 0x0cec, 0x05dc }, /*                hebrew_lamed ל HEBREW LETTER LAMED */\n    { 0x0ced, 0x05dd }, /*             hebrew_finalmem ם HEBREW LETTER FINAL MEM */\n    { 0x0cee, 0x05de }, /*                  hebrew_mem מ HEBREW LETTER MEM */\n    { 0x0cef, 0x05df }, /*             hebrew_finalnun ן HEBREW LETTER FINAL NUN */\n    { 0x0cf0, 0x05e0 }, /*                  hebrew_nun נ HEBREW LETTER NUN */\n    { 0x0cf1, 0x05e1 }, /*               hebrew_samech ס HEBREW LETTER SAMEKH */\n    { 0x0cf2, 0x05e2 }, /*                 hebrew_ayin ע HEBREW LETTER AYIN */\n    { 0x0cf3, 0x05e3 }, /*              hebrew_finalpe ף HEBREW LETTER FINAL PE */\n    { 0x0cf4, 0x05e4 }, /*                   hebrew_pe פ HEBREW LETTER PE */\n    { 0x0cf5, 0x05e5 }, /*            hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */\n    { 0x0cf6, 0x05e6 }, /*                 hebrew_zade צ HEBREW LETTER TSADI */\n    { 0x0cf7, 0x05e7 }, /*                 hebrew_qoph ק HEBREW LETTER QOF */\n    { 0x0cf8, 0x05e8 }, /*                 hebrew_resh ר HEBREW LETTER RESH */\n    { 0x0cf9, 0x05e9 }, /*                 hebrew_shin ש HEBREW LETTER SHIN */\n    { 0x0cfa, 0x05ea }, /*                  hebrew_taw ת HEBREW LETTER TAV */\n    { 0x0da1, 0x0e01 }, /*                  Thai_kokai ก THAI CHARACTER KO KAI */\n    { 0x0da2, 0x0e02 }, /*                Thai_khokhai ข THAI CHARACTER KHO KHAI */\n    { 0x0da3, 0x0e03 }, /*               Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */\n    { 0x0da4, 0x0e04 }, /*               Thai_khokhwai ค THAI CHARACTER KHO KHWAI */\n    { 0x0da5, 0x0e05 }, /*                Thai_khokhon ฅ THAI CHARACTER KHO KHON */\n    { 0x0da6, 0x0e06 }, /*             Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */\n    { 0x0da7, 0x0e07 }, /*                 Thai_ngongu ง THAI CHARACTER NGO NGU */\n    { 0x0da8, 0x0e08 }, /*                Thai_chochan จ THAI CHARACTER CHO CHAN */\n    { 0x0da9, 0x0e09 }, /*               Thai_choching ฉ THAI CHARACTER CHO CHING */\n    { 0x0daa, 0x0e0a }, /*               Thai_chochang ช THAI CHARACTER CHO CHANG */\n    { 0x0dab, 0x0e0b }, /*                   Thai_soso ซ THAI CHARACTER SO SO */\n    { 0x0dac, 0x0e0c }, /*                Thai_chochoe ฌ THAI CHARACTER CHO CHOE */\n    { 0x0dad, 0x0e0d }, /*                 Thai_yoying ญ THAI CHARACTER YO YING */\n    { 0x0dae, 0x0e0e }, /*                Thai_dochada ฎ THAI CHARACTER DO CHADA */\n    { 0x0daf, 0x0e0f }, /*                Thai_topatak ฏ THAI CHARACTER TO PATAK */\n    { 0x0db0, 0x0e10 }, /*                Thai_thothan ฐ THAI CHARACTER THO THAN */\n    { 0x0db1, 0x0e11 }, /*          Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */\n    { 0x0db2, 0x0e12 }, /*             Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */\n    { 0x0db3, 0x0e13 }, /*                  Thai_nonen ณ THAI CHARACTER NO NEN */\n    { 0x0db4, 0x0e14 }, /*                  Thai_dodek ด THAI CHARACTER DO DEK */\n    { 0x0db5, 0x0e15 }, /*                  Thai_totao ต THAI CHARACTER TO TAO */\n    { 0x0db6, 0x0e16 }, /*               Thai_thothung ถ THAI CHARACTER THO THUNG */\n    { 0x0db7, 0x0e17 }, /*              Thai_thothahan ท THAI CHARACTER THO THAHAN */\n    { 0x0db8, 0x0e18 }, /*               Thai_thothong ธ THAI CHARACTER THO THONG */\n    { 0x0db9, 0x0e19 }, /*                   Thai_nonu น THAI CHARACTER NO NU */\n    { 0x0dba, 0x0e1a }, /*               Thai_bobaimai บ THAI CHARACTER BO BAIMAI */\n    { 0x0dbb, 0x0e1b }, /*                  Thai_popla ป THAI CHARACTER PO PLA */\n    { 0x0dbc, 0x0e1c }, /*               Thai_phophung ผ THAI CHARACTER PHO PHUNG */\n    { 0x0dbd, 0x0e1d }, /*                   Thai_fofa ฝ THAI CHARACTER FO FA */\n    { 0x0dbe, 0x0e1e }, /*                Thai_phophan พ THAI CHARACTER PHO PHAN */\n    { 0x0dbf, 0x0e1f }, /*                  Thai_fofan ฟ THAI CHARACTER FO FAN */\n    { 0x0dc0, 0x0e20 }, /*             Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */\n    { 0x0dc1, 0x0e21 }, /*                   Thai_moma ม THAI CHARACTER MO MA */\n    { 0x0dc2, 0x0e22 }, /*                  Thai_yoyak ย THAI CHARACTER YO YAK */\n    { 0x0dc3, 0x0e23 }, /*                  Thai_rorua ร THAI CHARACTER RO RUA */\n    { 0x0dc4, 0x0e24 }, /*                     Thai_ru ฤ THAI CHARACTER RU */\n    { 0x0dc5, 0x0e25 }, /*                 Thai_loling ล THAI CHARACTER LO LING */\n    { 0x0dc6, 0x0e26 }, /*                     Thai_lu ฦ THAI CHARACTER LU */\n    { 0x0dc7, 0x0e27 }, /*                 Thai_wowaen ว THAI CHARACTER WO WAEN */\n    { 0x0dc8, 0x0e28 }, /*                 Thai_sosala ศ THAI CHARACTER SO SALA */\n    { 0x0dc9, 0x0e29 }, /*                 Thai_sorusi ษ THAI CHARACTER SO RUSI */\n    { 0x0dca, 0x0e2a }, /*                  Thai_sosua ส THAI CHARACTER SO SUA */\n    { 0x0dcb, 0x0e2b }, /*                  Thai_hohip ห THAI CHARACTER HO HIP */\n    { 0x0dcc, 0x0e2c }, /*                Thai_lochula ฬ THAI CHARACTER LO CHULA */\n    { 0x0dcd, 0x0e2d }, /*                   Thai_oang อ THAI CHARACTER O ANG */\n    { 0x0dce, 0x0e2e }, /*               Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */\n    { 0x0dcf, 0x0e2f }, /*              Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */\n    { 0x0dd0, 0x0e30 }, /*                  Thai_saraa ะ THAI CHARACTER SARA A */\n    { 0x0dd1, 0x0e31 }, /*             Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */\n    { 0x0dd2, 0x0e32 }, /*                 Thai_saraaa า THAI CHARACTER SARA AA */\n    { 0x0dd3, 0x0e33 }, /*                 Thai_saraam ำ THAI CHARACTER SARA AM */\n    { 0x0dd4, 0x0e34 }, /*                  Thai_sarai ิ THAI CHARACTER SARA I */\n    { 0x0dd5, 0x0e35 }, /*                 Thai_saraii ี THAI CHARACTER SARA II */\n    { 0x0dd6, 0x0e36 }, /*                 Thai_saraue ึ THAI CHARACTER SARA UE */\n    { 0x0dd7, 0x0e37 }, /*                Thai_sarauee ื THAI CHARACTER SARA UEE */\n    { 0x0dd8, 0x0e38 }, /*                  Thai_sarau ุ THAI CHARACTER SARA U */\n    { 0x0dd9, 0x0e39 }, /*                 Thai_sarauu ู THAI CHARACTER SARA UU */\n    { 0x0dda, 0x0e3a }, /*                Thai_phinthu ฺ THAI CHARACTER PHINTHU */\n    { 0x0dde, 0x0e3e }, /*      Thai_maihanakat_maitho ฾ ??? */\n    { 0x0ddf, 0x0e3f }, /*                   Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */\n    { 0x0de0, 0x0e40 }, /*                  Thai_sarae เ THAI CHARACTER SARA E */\n    { 0x0de1, 0x0e41 }, /*                 Thai_saraae แ THAI CHARACTER SARA AE */\n    { 0x0de2, 0x0e42 }, /*                  Thai_sarao โ THAI CHARACTER SARA O */\n    { 0x0de3, 0x0e43 }, /*          Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */\n    { 0x0de4, 0x0e44 }, /*         Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */\n    { 0x0de5, 0x0e45 }, /*            Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */\n    { 0x0de6, 0x0e46 }, /*               Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */\n    { 0x0de7, 0x0e47 }, /*              Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */\n    { 0x0de8, 0x0e48 }, /*                  Thai_maiek ่ THAI CHARACTER MAI EK */\n    { 0x0de9, 0x0e49 }, /*                 Thai_maitho ้ THAI CHARACTER MAI THO */\n    { 0x0dea, 0x0e4a }, /*                 Thai_maitri ๊ THAI CHARACTER MAI TRI */\n    { 0x0deb, 0x0e4b }, /*            Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */\n    { 0x0dec, 0x0e4c }, /*            Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */\n    { 0x0ded, 0x0e4d }, /*               Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */\n    { 0x0df0, 0x0e50 }, /*                 Thai_leksun ๐ THAI DIGIT ZERO */\n    { 0x0df1, 0x0e51 }, /*                Thai_leknung ๑ THAI DIGIT ONE */\n    { 0x0df2, 0x0e52 }, /*                Thai_leksong ๒ THAI DIGIT TWO */\n    { 0x0df3, 0x0e53 }, /*                 Thai_leksam ๓ THAI DIGIT THREE */\n    { 0x0df4, 0x0e54 }, /*                  Thai_leksi ๔ THAI DIGIT FOUR */\n    { 0x0df5, 0x0e55 }, /*                  Thai_lekha ๕ THAI DIGIT FIVE */\n    { 0x0df6, 0x0e56 }, /*                 Thai_lekhok ๖ THAI DIGIT SIX */\n    { 0x0df7, 0x0e57 }, /*                Thai_lekchet ๗ THAI DIGIT SEVEN */\n    { 0x0df8, 0x0e58 }, /*                Thai_lekpaet ๘ THAI DIGIT EIGHT */\n    { 0x0df9, 0x0e59 }, /*                 Thai_lekkao ๙ THAI DIGIT NINE */\n    { 0x0ea1, 0x3131 }, /*               Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */\n    { 0x0ea2, 0x3132 }, /*          Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */\n    { 0x0ea3, 0x3133 }, /*           Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */\n    { 0x0ea4, 0x3134 }, /*                Hangul_Nieun ㄴ HANGUL LETTER NIEUN */\n    { 0x0ea5, 0x3135 }, /*           Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */\n    { 0x0ea6, 0x3136 }, /*           Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */\n    { 0x0ea7, 0x3137 }, /*               Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */\n    { 0x0ea8, 0x3138 }, /*          Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */\n    { 0x0ea9, 0x3139 }, /*                Hangul_Rieul ㄹ HANGUL LETTER RIEUL */\n    { 0x0eaa, 0x313a }, /*          Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */\n    { 0x0eab, 0x313b }, /*           Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */\n    { 0x0eac, 0x313c }, /*           Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */\n    { 0x0ead, 0x313d }, /*            Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */\n    { 0x0eae, 0x313e }, /*           Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */\n    { 0x0eaf, 0x313f }, /*          Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */\n    { 0x0eb0, 0x3140 }, /*           Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */\n    { 0x0eb1, 0x3141 }, /*                Hangul_Mieum ㅁ HANGUL LETTER MIEUM */\n    { 0x0eb2, 0x3142 }, /*                Hangul_Pieub ㅂ HANGUL LETTER PIEUP */\n    { 0x0eb3, 0x3143 }, /*           Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */\n    { 0x0eb4, 0x3144 }, /*            Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */\n    { 0x0eb5, 0x3145 }, /*                 Hangul_Sios ㅅ HANGUL LETTER SIOS */\n    { 0x0eb6, 0x3146 }, /*            Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */\n    { 0x0eb7, 0x3147 }, /*                Hangul_Ieung ㅇ HANGUL LETTER IEUNG */\n    { 0x0eb8, 0x3148 }, /*                Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */\n    { 0x0eb9, 0x3149 }, /*           Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */\n    { 0x0eba, 0x314a }, /*                Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */\n    { 0x0ebb, 0x314b }, /*               Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */\n    { 0x0ebc, 0x314c }, /*                Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */\n    { 0x0ebd, 0x314d }, /*               Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */\n    { 0x0ebe, 0x314e }, /*                Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */\n    { 0x0ebf, 0x314f }, /*                    Hangul_A ㅏ HANGUL LETTER A */\n    { 0x0ec0, 0x3150 }, /*                   Hangul_AE ㅐ HANGUL LETTER AE */\n    { 0x0ec1, 0x3151 }, /*                   Hangul_YA ㅑ HANGUL LETTER YA */\n    { 0x0ec2, 0x3152 }, /*                  Hangul_YAE ㅒ HANGUL LETTER YAE */\n    { 0x0ec3, 0x3153 }, /*                   Hangul_EO ㅓ HANGUL LETTER EO */\n    { 0x0ec4, 0x3154 }, /*                    Hangul_E ㅔ HANGUL LETTER E */\n    { 0x0ec5, 0x3155 }, /*                  Hangul_YEO ㅕ HANGUL LETTER YEO */\n    { 0x0ec6, 0x3156 }, /*                   Hangul_YE ㅖ HANGUL LETTER YE */\n    { 0x0ec7, 0x3157 }, /*                    Hangul_O ㅗ HANGUL LETTER O */\n    { 0x0ec8, 0x3158 }, /*                   Hangul_WA ㅘ HANGUL LETTER WA */\n    { 0x0ec9, 0x3159 }, /*                  Hangul_WAE ㅙ HANGUL LETTER WAE */\n    { 0x0eca, 0x315a }, /*                   Hangul_OE ㅚ HANGUL LETTER OE */\n    { 0x0ecb, 0x315b }, /*                   Hangul_YO ㅛ HANGUL LETTER YO */\n    { 0x0ecc, 0x315c }, /*                    Hangul_U ㅜ HANGUL LETTER U */\n    { 0x0ecd, 0x315d }, /*                  Hangul_WEO ㅝ HANGUL LETTER WEO */\n    { 0x0ece, 0x315e }, /*                   Hangul_WE ㅞ HANGUL LETTER WE */\n    { 0x0ecf, 0x315f }, /*                   Hangul_WI ㅟ HANGUL LETTER WI */\n    { 0x0ed0, 0x3160 }, /*                   Hangul_YU ㅠ HANGUL LETTER YU */\n    { 0x0ed1, 0x3161 }, /*                   Hangul_EU ㅡ HANGUL LETTER EU */\n    { 0x0ed2, 0x3162 }, /*                   Hangul_YI ㅢ HANGUL LETTER YI */\n    { 0x0ed3, 0x3163 }, /*                    Hangul_I ㅣ HANGUL LETTER I */\n    { 0x0ed4, 0x11a8 }, /*             Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */\n    { 0x0ed5, 0x11a9 }, /*        Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */\n    { 0x0ed6, 0x11aa }, /*         Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */\n    { 0x0ed7, 0x11ab }, /*              Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */\n    { 0x0ed8, 0x11ac }, /*         Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */\n    { 0x0ed9, 0x11ad }, /*         Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */\n    { 0x0eda, 0x11ae }, /*             Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */\n    { 0x0edb, 0x11af }, /*              Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */\n    { 0x0edc, 0x11b0 }, /*        Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */\n    { 0x0edd, 0x11b1 }, /*         Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */\n    { 0x0ede, 0x11b2 }, /*         Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */\n    { 0x0edf, 0x11b3 }, /*          Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */\n    { 0x0ee0, 0x11b4 }, /*         Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */\n    { 0x0ee1, 0x11b5 }, /*        Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */\n    { 0x0ee2, 0x11b6 }, /*         Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */\n    { 0x0ee3, 0x11b7 }, /*              Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */\n    { 0x0ee4, 0x11b8 }, /*              Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */\n    { 0x0ee5, 0x11b9 }, /*          Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */\n    { 0x0ee6, 0x11ba }, /*               Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */\n    { 0x0ee7, 0x11bb }, /*          Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */\n    { 0x0ee8, 0x11bc }, /*              Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */\n    { 0x0ee9, 0x11bd }, /*              Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */\n    { 0x0eea, 0x11be }, /*              Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */\n    { 0x0eeb, 0x11bf }, /*             Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */\n    { 0x0eec, 0x11c0 }, /*              Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */\n    { 0x0eed, 0x11c1 }, /*             Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */\n    { 0x0eee, 0x11c2 }, /*              Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */\n    { 0x0eef, 0x316d }, /*     Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */\n    { 0x0ef0, 0x3171 }, /*    Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */\n    { 0x0ef1, 0x3178 }, /*    Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */\n    { 0x0ef2, 0x317f }, /*              Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */\n/*  0x0ef3                  Hangul_KkogjiDalrinIeung ? ??? */\n    { 0x0ef4, 0x3184 }, /*   Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */\n    { 0x0ef5, 0x3186 }, /*          Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */\n    { 0x0ef6, 0x318d }, /*                Hangul_AraeA ㆍ HANGUL LETTER ARAEA */\n    { 0x0ef7, 0x318e }, /*               Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */\n    { 0x0ef8, 0x11eb }, /*            Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */\n    { 0x0ef9, 0x11f0 }, /*  Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */\n    { 0x0efa, 0x11f9 }, /*        Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */\n    { 0x0eff, 0x20a9 }, /*                  Korean_Won ₩ WON SIGN */\n    { 0x13a4, 0x20ac }, /*                        Euro € EURO SIGN */\n    { 0x13bc, 0x0152 }, /*                          OE Œ LATIN CAPITAL LIGATURE OE */\n    { 0x13bd, 0x0153 }, /*                          oe œ LATIN SMALL LIGATURE OE */\n    { 0x13be, 0x0178 }, /*                  Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */\n    { 0x20a0, 0x20a0 }, /*                     EcuSign ₠ EURO-CURRENCY SIGN */\n    { 0x20a1, 0x20a1 }, /*                   ColonSign ₡ COLON SIGN */\n    { 0x20a2, 0x20a2 }, /*                CruzeiroSign ₢ CRUZEIRO SIGN */\n    { 0x20a3, 0x20a3 }, /*                  FFrancSign ₣ FRENCH FRANC SIGN */\n    { 0x20a4, 0x20a4 }, /*                    LiraSign ₤ LIRA SIGN */\n    { 0x20a5, 0x20a5 }, /*                    MillSign ₥ MILL SIGN */\n    { 0x20a6, 0x20a6 }, /*                   NairaSign ₦ NAIRA SIGN */\n    { 0x20a7, 0x20a7 }, /*                  PesetaSign ₧ PESETA SIGN */\n    { 0x20a8, 0x20a8 }, /*                   RupeeSign ₨ RUPEE SIGN */\n    { 0x20a9, 0x20a9 }, /*                     WonSign ₩ WON SIGN */\n    { 0x20aa, 0x20aa }, /*               NewSheqelSign ₪ NEW SHEQEL SIGN */\n    { 0x20ab, 0x20ab }, /*                    DongSign ₫ DONG SIGN */\n    { 0x20ac, 0x20ac }, /*                    EuroSign € EURO SIGN */\n};\n\nxkb_keysym_t\nutf32_to_keysym(uint32_t ucs)\n{\n    /* first check for Latin-1 characters (1:1 mapping) */\n    if ((ucs >= 0x0020 && ucs <= 0x007e) ||\n        (ucs >= 0x00a0 && ucs <= 0x00ff))\n        return ucs;\n\n    /* special keysyms */\n    if ((ucs >= (XKB_KEY_BackSpace & 0x7f) && ucs <= (XKB_KEY_Clear & 0x7f)) ||\n        ucs == (XKB_KEY_Return & 0x7f) || ucs == (XKB_KEY_Escape & 0x7f))\n        return ucs | 0xff00;\n    if (ucs == (XKB_KEY_Delete & 0x7f))\n        return XKB_KEY_Delete;\n\n    /* Unicode non-symbols and code points outside Unicode planes */\n    if ((ucs >= 0xfdd0 && ucs <= 0xfdef) ||\n        ucs > 0x10ffff || (ucs & 0xfffe) == 0xfffe)\n        return XKB_KEY_NoSymbol;\n\n    /* search main table */\n    for (size_t i = 0; i < sizeof(keysymtab)/sizeof(keysymtab[0]); i++)\n        if (keysymtab[i].ucs == ucs)\n            return keysymtab[i].keysym;\n\n    /* Use direct encoding if everything else fails */\n    return ucs | 0x01000000;\n}\n"
  },
  {
    "path": "glfw/xkb_glfw.c",
    "content": "//========================================================================\n// GLFW 3.4 XKB - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n\n#include <string.h>\n#include <stdlib.h>\n#include \"internal.h\"\n#include \"xkb_glfw.h\"\n#ifdef _GLFW_X11\n#include <X11/XKBlib.h>\n#endif\n\n#define debug debug_input\n\n#ifdef XKB_HAS_NO_UTF32\n#include \"xkb-compat-shim.h\"\n#else\n#define utf32_to_keysym xkb_utf32_to_keysym\n#endif\n\nstatic int\nglfw_key_for_sym(xkb_keysym_t key) {\n    switch(key) {\n/* start xkb to glfw (auto generated by gen-key-constants.py do not edit) */\n        case XKB_KEY_Escape: return GLFW_FKEY_ESCAPE;\n        case XKB_KEY_Return: return GLFW_FKEY_ENTER;\n        case XKB_KEY_Tab: return GLFW_FKEY_TAB;\n        case XKB_KEY_BackSpace: return GLFW_FKEY_BACKSPACE;\n        case XKB_KEY_Insert: return GLFW_FKEY_INSERT;\n        case XKB_KEY_Delete: return GLFW_FKEY_DELETE;\n        case XKB_KEY_Left: return GLFW_FKEY_LEFT;\n        case XKB_KEY_Right: return GLFW_FKEY_RIGHT;\n        case XKB_KEY_Up: return GLFW_FKEY_UP;\n        case XKB_KEY_Down: return GLFW_FKEY_DOWN;\n        case XKB_KEY_Page_Up: return GLFW_FKEY_PAGE_UP;\n        case XKB_KEY_Page_Down: return GLFW_FKEY_PAGE_DOWN;\n        case XKB_KEY_Home: return GLFW_FKEY_HOME;\n        case XKB_KEY_End: return GLFW_FKEY_END;\n        case XKB_KEY_Caps_Lock: return GLFW_FKEY_CAPS_LOCK;\n        case XKB_KEY_Scroll_Lock: return GLFW_FKEY_SCROLL_LOCK;\n        case XKB_KEY_Num_Lock: return GLFW_FKEY_NUM_LOCK;\n        case XKB_KEY_Print: return GLFW_FKEY_PRINT_SCREEN;\n        case XKB_KEY_Pause: return GLFW_FKEY_PAUSE;\n        case XKB_KEY_Menu: return GLFW_FKEY_MENU;\n        case XKB_KEY_F1: return GLFW_FKEY_F1;\n        case XKB_KEY_F2: return GLFW_FKEY_F2;\n        case XKB_KEY_F3: return GLFW_FKEY_F3;\n        case XKB_KEY_F4: return GLFW_FKEY_F4;\n        case XKB_KEY_F5: return GLFW_FKEY_F5;\n        case XKB_KEY_F6: return GLFW_FKEY_F6;\n        case XKB_KEY_F7: return GLFW_FKEY_F7;\n        case XKB_KEY_F8: return GLFW_FKEY_F8;\n        case XKB_KEY_F9: return GLFW_FKEY_F9;\n        case XKB_KEY_F10: return GLFW_FKEY_F10;\n        case XKB_KEY_F11: return GLFW_FKEY_F11;\n        case XKB_KEY_F12: return GLFW_FKEY_F12;\n        case XKB_KEY_F13: return GLFW_FKEY_F13;\n        case XKB_KEY_F14: return GLFW_FKEY_F14;\n        case XKB_KEY_F15: return GLFW_FKEY_F15;\n        case XKB_KEY_F16: return GLFW_FKEY_F16;\n        case XKB_KEY_F17: return GLFW_FKEY_F17;\n        case XKB_KEY_F18: return GLFW_FKEY_F18;\n        case XKB_KEY_F19: return GLFW_FKEY_F19;\n        case XKB_KEY_F20: return GLFW_FKEY_F20;\n        case XKB_KEY_F21: return GLFW_FKEY_F21;\n        case XKB_KEY_F22: return GLFW_FKEY_F22;\n        case XKB_KEY_F23: return GLFW_FKEY_F23;\n        case XKB_KEY_F24: return GLFW_FKEY_F24;\n        case XKB_KEY_F25: return GLFW_FKEY_F25;\n        case XKB_KEY_F26: return GLFW_FKEY_F26;\n        case XKB_KEY_F27: return GLFW_FKEY_F27;\n        case XKB_KEY_F28: return GLFW_FKEY_F28;\n        case XKB_KEY_F29: return GLFW_FKEY_F29;\n        case XKB_KEY_F30: return GLFW_FKEY_F30;\n        case XKB_KEY_F31: return GLFW_FKEY_F31;\n        case XKB_KEY_F32: return GLFW_FKEY_F32;\n        case XKB_KEY_F33: return GLFW_FKEY_F33;\n        case XKB_KEY_F34: return GLFW_FKEY_F34;\n        case XKB_KEY_F35: return GLFW_FKEY_F35;\n        case XKB_KEY_KP_0: return GLFW_FKEY_KP_0;\n        case XKB_KEY_KP_1: return GLFW_FKEY_KP_1;\n        case XKB_KEY_KP_2: return GLFW_FKEY_KP_2;\n        case XKB_KEY_KP_3: return GLFW_FKEY_KP_3;\n        case XKB_KEY_KP_4: return GLFW_FKEY_KP_4;\n        case XKB_KEY_KP_5: return GLFW_FKEY_KP_5;\n        case XKB_KEY_KP_6: return GLFW_FKEY_KP_6;\n        case XKB_KEY_KP_7: return GLFW_FKEY_KP_7;\n        case XKB_KEY_KP_8: return GLFW_FKEY_KP_8;\n        case XKB_KEY_KP_9: return GLFW_FKEY_KP_9;\n        case XKB_KEY_KP_Decimal: return GLFW_FKEY_KP_DECIMAL;\n        case XKB_KEY_KP_Divide: return GLFW_FKEY_KP_DIVIDE;\n        case XKB_KEY_KP_Multiply: return GLFW_FKEY_KP_MULTIPLY;\n        case XKB_KEY_KP_Subtract: return GLFW_FKEY_KP_SUBTRACT;\n        case XKB_KEY_KP_Add: return GLFW_FKEY_KP_ADD;\n        case XKB_KEY_KP_Enter: return GLFW_FKEY_KP_ENTER;\n        case XKB_KEY_KP_Equal: return GLFW_FKEY_KP_EQUAL;\n        case XKB_KEY_KP_Separator: return GLFW_FKEY_KP_SEPARATOR;\n        case XKB_KEY_KP_Left: return GLFW_FKEY_KP_LEFT;\n        case XKB_KEY_KP_Right: return GLFW_FKEY_KP_RIGHT;\n        case XKB_KEY_KP_Up: return GLFW_FKEY_KP_UP;\n        case XKB_KEY_KP_Down: return GLFW_FKEY_KP_DOWN;\n        case XKB_KEY_KP_Page_Up: return GLFW_FKEY_KP_PAGE_UP;\n        case XKB_KEY_KP_Page_Down: return GLFW_FKEY_KP_PAGE_DOWN;\n        case XKB_KEY_KP_Home: return GLFW_FKEY_KP_HOME;\n        case XKB_KEY_KP_End: return GLFW_FKEY_KP_END;\n        case XKB_KEY_KP_Insert: return GLFW_FKEY_KP_INSERT;\n        case XKB_KEY_KP_Delete: return GLFW_FKEY_KP_DELETE;\n        case XKB_KEY_KP_Begin: return GLFW_FKEY_KP_BEGIN;\n        case XKB_KEY_XF86AudioPlay: return GLFW_FKEY_MEDIA_PLAY;\n        case XKB_KEY_XF86AudioPause: return GLFW_FKEY_MEDIA_PAUSE;\n        case XKB_KEY_XF86AudioStop: return GLFW_FKEY_MEDIA_STOP;\n        case XKB_KEY_XF86AudioForward: return GLFW_FKEY_MEDIA_FAST_FORWARD;\n        case XKB_KEY_XF86AudioRewind: return GLFW_FKEY_MEDIA_REWIND;\n        case XKB_KEY_XF86AudioNext: return GLFW_FKEY_MEDIA_TRACK_NEXT;\n        case XKB_KEY_XF86AudioPrev: return GLFW_FKEY_MEDIA_TRACK_PREVIOUS;\n        case XKB_KEY_XF86AudioRecord: return GLFW_FKEY_MEDIA_RECORD;\n        case XKB_KEY_XF86AudioLowerVolume: return GLFW_FKEY_LOWER_VOLUME;\n        case XKB_KEY_XF86AudioRaiseVolume: return GLFW_FKEY_RAISE_VOLUME;\n        case XKB_KEY_XF86AudioMute: return GLFW_FKEY_MUTE_VOLUME;\n        case XKB_KEY_Shift_L: return GLFW_FKEY_LEFT_SHIFT;\n        case XKB_KEY_Control_L: return GLFW_FKEY_LEFT_CONTROL;\n        case XKB_KEY_Alt_L: return GLFW_FKEY_LEFT_ALT;\n        case XKB_KEY_Super_L: return GLFW_FKEY_LEFT_SUPER;\n        case XKB_KEY_Hyper_L: return GLFW_FKEY_LEFT_HYPER;\n        case XKB_KEY_Meta_L: return GLFW_FKEY_LEFT_META;\n        case XKB_KEY_Shift_R: return GLFW_FKEY_RIGHT_SHIFT;\n        case XKB_KEY_Control_R: return GLFW_FKEY_RIGHT_CONTROL;\n        case XKB_KEY_Alt_R: return GLFW_FKEY_RIGHT_ALT;\n        case XKB_KEY_Super_R: return GLFW_FKEY_RIGHT_SUPER;\n        case XKB_KEY_Hyper_R: return GLFW_FKEY_RIGHT_HYPER;\n        case XKB_KEY_Meta_R: return GLFW_FKEY_RIGHT_META;\n        case XKB_KEY_ISO_Level3_Shift: return GLFW_FKEY_ISO_LEVEL3_SHIFT;\n        case XKB_KEY_ISO_Level5_Shift: return GLFW_FKEY_ISO_LEVEL5_SHIFT;\n/* end xkb to glfw */\n        default:\n            return xkb_keysym_to_utf32(key);\n    }\n}\n\nxkb_keysym_t\nglfw_xkb_sym_for_key(uint32_t key) {\n    switch(key) {\n/* start glfw to xkb (auto generated by gen-key-constants.py do not edit) */\n        case GLFW_FKEY_ESCAPE: return XKB_KEY_Escape;\n        case GLFW_FKEY_ENTER: return XKB_KEY_Return;\n        case GLFW_FKEY_TAB: return XKB_KEY_Tab;\n        case GLFW_FKEY_BACKSPACE: return XKB_KEY_BackSpace;\n        case GLFW_FKEY_INSERT: return XKB_KEY_Insert;\n        case GLFW_FKEY_DELETE: return XKB_KEY_Delete;\n        case GLFW_FKEY_LEFT: return XKB_KEY_Left;\n        case GLFW_FKEY_RIGHT: return XKB_KEY_Right;\n        case GLFW_FKEY_UP: return XKB_KEY_Up;\n        case GLFW_FKEY_DOWN: return XKB_KEY_Down;\n        case GLFW_FKEY_PAGE_UP: return XKB_KEY_Page_Up;\n        case GLFW_FKEY_PAGE_DOWN: return XKB_KEY_Page_Down;\n        case GLFW_FKEY_HOME: return XKB_KEY_Home;\n        case GLFW_FKEY_END: return XKB_KEY_End;\n        case GLFW_FKEY_CAPS_LOCK: return XKB_KEY_Caps_Lock;\n        case GLFW_FKEY_SCROLL_LOCK: return XKB_KEY_Scroll_Lock;\n        case GLFW_FKEY_NUM_LOCK: return XKB_KEY_Num_Lock;\n        case GLFW_FKEY_PRINT_SCREEN: return XKB_KEY_Print;\n        case GLFW_FKEY_PAUSE: return XKB_KEY_Pause;\n        case GLFW_FKEY_MENU: return XKB_KEY_Menu;\n        case GLFW_FKEY_F1: return XKB_KEY_F1;\n        case GLFW_FKEY_F2: return XKB_KEY_F2;\n        case GLFW_FKEY_F3: return XKB_KEY_F3;\n        case GLFW_FKEY_F4: return XKB_KEY_F4;\n        case GLFW_FKEY_F5: return XKB_KEY_F5;\n        case GLFW_FKEY_F6: return XKB_KEY_F6;\n        case GLFW_FKEY_F7: return XKB_KEY_F7;\n        case GLFW_FKEY_F8: return XKB_KEY_F8;\n        case GLFW_FKEY_F9: return XKB_KEY_F9;\n        case GLFW_FKEY_F10: return XKB_KEY_F10;\n        case GLFW_FKEY_F11: return XKB_KEY_F11;\n        case GLFW_FKEY_F12: return XKB_KEY_F12;\n        case GLFW_FKEY_F13: return XKB_KEY_F13;\n        case GLFW_FKEY_F14: return XKB_KEY_F14;\n        case GLFW_FKEY_F15: return XKB_KEY_F15;\n        case GLFW_FKEY_F16: return XKB_KEY_F16;\n        case GLFW_FKEY_F17: return XKB_KEY_F17;\n        case GLFW_FKEY_F18: return XKB_KEY_F18;\n        case GLFW_FKEY_F19: return XKB_KEY_F19;\n        case GLFW_FKEY_F20: return XKB_KEY_F20;\n        case GLFW_FKEY_F21: return XKB_KEY_F21;\n        case GLFW_FKEY_F22: return XKB_KEY_F22;\n        case GLFW_FKEY_F23: return XKB_KEY_F23;\n        case GLFW_FKEY_F24: return XKB_KEY_F24;\n        case GLFW_FKEY_F25: return XKB_KEY_F25;\n        case GLFW_FKEY_F26: return XKB_KEY_F26;\n        case GLFW_FKEY_F27: return XKB_KEY_F27;\n        case GLFW_FKEY_F28: return XKB_KEY_F28;\n        case GLFW_FKEY_F29: return XKB_KEY_F29;\n        case GLFW_FKEY_F30: return XKB_KEY_F30;\n        case GLFW_FKEY_F31: return XKB_KEY_F31;\n        case GLFW_FKEY_F32: return XKB_KEY_F32;\n        case GLFW_FKEY_F33: return XKB_KEY_F33;\n        case GLFW_FKEY_F34: return XKB_KEY_F34;\n        case GLFW_FKEY_F35: return XKB_KEY_F35;\n        case GLFW_FKEY_KP_0: return XKB_KEY_KP_0;\n        case GLFW_FKEY_KP_1: return XKB_KEY_KP_1;\n        case GLFW_FKEY_KP_2: return XKB_KEY_KP_2;\n        case GLFW_FKEY_KP_3: return XKB_KEY_KP_3;\n        case GLFW_FKEY_KP_4: return XKB_KEY_KP_4;\n        case GLFW_FKEY_KP_5: return XKB_KEY_KP_5;\n        case GLFW_FKEY_KP_6: return XKB_KEY_KP_6;\n        case GLFW_FKEY_KP_7: return XKB_KEY_KP_7;\n        case GLFW_FKEY_KP_8: return XKB_KEY_KP_8;\n        case GLFW_FKEY_KP_9: return XKB_KEY_KP_9;\n        case GLFW_FKEY_KP_DECIMAL: return XKB_KEY_KP_Decimal;\n        case GLFW_FKEY_KP_DIVIDE: return XKB_KEY_KP_Divide;\n        case GLFW_FKEY_KP_MULTIPLY: return XKB_KEY_KP_Multiply;\n        case GLFW_FKEY_KP_SUBTRACT: return XKB_KEY_KP_Subtract;\n        case GLFW_FKEY_KP_ADD: return XKB_KEY_KP_Add;\n        case GLFW_FKEY_KP_ENTER: return XKB_KEY_KP_Enter;\n        case GLFW_FKEY_KP_EQUAL: return XKB_KEY_KP_Equal;\n        case GLFW_FKEY_KP_SEPARATOR: return XKB_KEY_KP_Separator;\n        case GLFW_FKEY_KP_LEFT: return XKB_KEY_KP_Left;\n        case GLFW_FKEY_KP_RIGHT: return XKB_KEY_KP_Right;\n        case GLFW_FKEY_KP_UP: return XKB_KEY_KP_Up;\n        case GLFW_FKEY_KP_DOWN: return XKB_KEY_KP_Down;\n        case GLFW_FKEY_KP_PAGE_UP: return XKB_KEY_KP_Page_Up;\n        case GLFW_FKEY_KP_PAGE_DOWN: return XKB_KEY_KP_Page_Down;\n        case GLFW_FKEY_KP_HOME: return XKB_KEY_KP_Home;\n        case GLFW_FKEY_KP_END: return XKB_KEY_KP_End;\n        case GLFW_FKEY_KP_INSERT: return XKB_KEY_KP_Insert;\n        case GLFW_FKEY_KP_DELETE: return XKB_KEY_KP_Delete;\n        case GLFW_FKEY_KP_BEGIN: return XKB_KEY_KP_Begin;\n        case GLFW_FKEY_MEDIA_PLAY: return XKB_KEY_XF86AudioPlay;\n        case GLFW_FKEY_MEDIA_PAUSE: return XKB_KEY_XF86AudioPause;\n        case GLFW_FKEY_MEDIA_STOP: return XKB_KEY_XF86AudioStop;\n        case GLFW_FKEY_MEDIA_FAST_FORWARD: return XKB_KEY_XF86AudioForward;\n        case GLFW_FKEY_MEDIA_REWIND: return XKB_KEY_XF86AudioRewind;\n        case GLFW_FKEY_MEDIA_TRACK_NEXT: return XKB_KEY_XF86AudioNext;\n        case GLFW_FKEY_MEDIA_TRACK_PREVIOUS: return XKB_KEY_XF86AudioPrev;\n        case GLFW_FKEY_MEDIA_RECORD: return XKB_KEY_XF86AudioRecord;\n        case GLFW_FKEY_LOWER_VOLUME: return XKB_KEY_XF86AudioLowerVolume;\n        case GLFW_FKEY_RAISE_VOLUME: return XKB_KEY_XF86AudioRaiseVolume;\n        case GLFW_FKEY_MUTE_VOLUME: return XKB_KEY_XF86AudioMute;\n        case GLFW_FKEY_LEFT_SHIFT: return XKB_KEY_Shift_L;\n        case GLFW_FKEY_LEFT_CONTROL: return XKB_KEY_Control_L;\n        case GLFW_FKEY_LEFT_ALT: return XKB_KEY_Alt_L;\n        case GLFW_FKEY_LEFT_SUPER: return XKB_KEY_Super_L;\n        case GLFW_FKEY_LEFT_HYPER: return XKB_KEY_Hyper_L;\n        case GLFW_FKEY_LEFT_META: return XKB_KEY_Meta_L;\n        case GLFW_FKEY_RIGHT_SHIFT: return XKB_KEY_Shift_R;\n        case GLFW_FKEY_RIGHT_CONTROL: return XKB_KEY_Control_R;\n        case GLFW_FKEY_RIGHT_ALT: return XKB_KEY_Alt_R;\n        case GLFW_FKEY_RIGHT_SUPER: return XKB_KEY_Super_R;\n        case GLFW_FKEY_RIGHT_HYPER: return XKB_KEY_Hyper_R;\n        case GLFW_FKEY_RIGHT_META: return XKB_KEY_Meta_R;\n        case GLFW_FKEY_ISO_LEVEL3_SHIFT: return XKB_KEY_ISO_Level3_Shift;\n        case GLFW_FKEY_ISO_LEVEL5_SHIFT: return XKB_KEY_ISO_Level5_Shift;\n/* end glfw to xkb */\n        default:\n            return utf32_to_keysym(key);\n    }\n}\n\n#ifdef _GLFW_X11\n\nbool\nglfw_xkb_set_x11_events_mask(void) {\n    if (!XkbSelectEvents(_glfw.x11.display, XkbUseCoreKbd, XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask, XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask)) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to set XKB events mask\");\n        return false;\n    }\n    return true;\n}\n\nbool\nglfw_xkb_update_x11_keyboard_id(_GLFWXKBData *xkb) {\n    xkb->keyboard_device_id = -1;\n    xcb_connection_t* conn = XGetXCBConnection(_glfw.x11.display);\n    if (!conn) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Failed to retrieve XCB connection\");\n        return false;\n    }\n\n    xkb->keyboard_device_id = xkb_x11_get_core_keyboard_device_id(conn);\n    if (xkb->keyboard_device_id == -1) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"X11: Failed to retrieve core keyboard device id\");\n        return false;\n    }\n    return true;\n}\n\n#define xkb_glfw_load_keymap(keymap, ...) {\\\n    xcb_connection_t* conn = XGetXCBConnection(_glfw.x11.display); \\\n    if (conn) keymap = xkb_x11_keymap_new_from_device(xkb->context, conn, xkb->keyboard_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS); \\\n}\n\n#define xkb_glfw_load_state(keymap, state) {\\\n    xcb_connection_t* conn = XGetXCBConnection(_glfw.x11.display); \\\n    if (conn) state = xkb_x11_state_new_from_device(keymap, conn, xkb->keyboard_device_id); \\\n}\n\nstatic void\nglfw_xkb_update_masks(_GLFWXKBData *xkb) {\n    // See https://github.com/kovidgoyal/kitty/pull/3430 for discussion\n    bool succeeded = false;\n    unsigned used_bits = 0; /* To avoid using the same bit twice */\n    XkbDescPtr xkb_ptr = XkbGetMap( _glfw.x11.display, XkbVirtualModsMask | XkbVirtualModMapMask, XkbUseCoreKbd );\n\n    /* shift, control, and capsLock are special; they cannot be identified reliably on X11 */\n#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx; used_bits |= xkb->a##Mask;\n    S(control, XKB_MOD_NAME_CTRL);\n    S(shift, XKB_MOD_NAME_SHIFT);\n    S(capsLock, XKB_MOD_NAME_CAPS);\n#undef S\n#define S( a ) xkb->a##Idx = XKB_MOD_INVALID; xkb->a##Mask = 0\n    S(alt); S(super); S(hyper); S(meta); S(numLock);\n#undef S\n    if (xkb_ptr) {\n        Status status = XkbGetNames(_glfw.x11.display, XkbVirtualModNamesMask, xkb_ptr);\n        if (status == Success) {\n            for (int indx = 0; indx < XkbNumVirtualMods; ++indx) {\n                Atom atom = xkb_ptr->names->vmods[indx];\n                if (atom) {\n                    unsigned mask_rtn = 0;\n                    if (XkbVirtualModsToReal( xkb_ptr, 1<<indx, &mask_rtn) ) {\n                        const char *name = XGetAtomName(_glfw.x11.display, atom);\n#define S( a, s ) if (!(used_bits & mask_rtn) && strcmp(name, #s) == 0) xkb->a##Mask = mask_rtn, used_bits |= mask_rtn\n                        /* Note that the order matters here; earlier is higher priority. */\n                        S(alt, Alt);\n                        S(super, Super);\n                        S(numLock, NumLock);\n                        S(meta, Meta);\n                        S(hyper, Hyper);\n#undef S\n                    }\n                }\n            }\n            succeeded = true;\n        }\n        XkbFreeNames(xkb_ptr, XkbVirtualModNamesMask, True);\n        XkbFreeKeyboard(xkb_ptr, 0, True);\n    }\n    if (succeeded) {\n        unsigned indx, shifted;\n        for (indx = 0, shifted = 1; used_bits; ++indx, shifted <<= 1, used_bits >>= 1) {\n#define S( a ) if ( ( xkb->a##Mask & shifted ) == shifted ) xkb->a##Idx = indx\n            S(alt); S(super); S(hyper); S(meta); S(numLock);\n#undef S\n        }\n    }\n#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx;\n    if (!succeeded) {\n        S(numLock, XKB_MOD_NAME_NUM);\n        S(alt, XKB_MOD_NAME_ALT);\n        S(super, XKB_MOD_NAME_LOGO);\n    }\n#undef S\n    debug(\"Modifier indices alt: 0x%x super: 0x%x hyper: 0x%x meta: 0x%x numlock: 0x%x shift: 0x%x capslock: 0x%x\\n\",\n            xkb->altIdx, xkb->superIdx, xkb->hyperIdx, xkb->metaIdx, xkb->numLockIdx, xkb->shiftIdx, xkb->capsLockIdx);\n}\n\n\n#else\n\n#define xkb_glfw_load_keymap(keymap, map_str) keymap = xkb_keymap_new_from_string(xkb->context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);\n#define xkb_glfw_load_state(keymap, state) state = xkb_state_new(keymap);\n\ntypedef struct {\n    struct xkb_state *state;\n    int failed;\n    xkb_mod_mask_t used_mods;\n    xkb_mod_mask_t shift, control, capsLock, numLock, alt, super, meta, hyper;\n\n    /* Combination modifiers to try */\n    int try_shift;\n    xkb_keycode_t shift_keycode;\n} modifier_mapping_algorithm_t;\n\n/* Algorithm for mapping virtual modifiers to real modifiers:\n *   1. create new state\n *   2. for each key in keymap\n *      a. send key down to state\n *      b. if it affected exactly one bit in modifier map\n *         i) get keysym\n *         ii) if keysym matches one of the known modifiers, save it for that modifier\n *         iii) if modifier is latched, send key up and key down to toggle again\n *      c. send key up to reset the state\n *   3. if shift key found in step 2, run step 2 with all shift+key for each key\n *   4. if shift, control, alt and super are not all found, declare failure\n *   5. if failure, use static mapping from xkbcommon-names.h\n *\n * Step 3 is needed because many popular keymaps map meta to alt+shift.\n *\n * We could do better by constructing a system of linear equations, but it should not be\n * needed in any sane system. We could also use this algorithm with X11, but X11\n * provides XkbVirtualModsToReal which is guaranteed to be accurate, while this\n * algorithm is only a heuristic.\n *\n * We don't touch level3 or level5 modifiers.\n */\nstatic void modifier_mapping_algorithm( struct xkb_keymap *keymap UNUSED, xkb_keycode_t key, void *data ) {\n    modifier_mapping_algorithm_t *algorithm = ( modifier_mapping_algorithm_t * )data;\n    if ( algorithm->failed )\n        return;\n\n    if ( algorithm->try_shift ) {\n        if ( key == algorithm->shift_keycode ) return;\n        xkb_state_update_key( algorithm->state, algorithm->shift_keycode, XKB_KEY_DOWN );\n    }\n\n    enum xkb_state_component changed_type = xkb_state_update_key( algorithm->state, key, XKB_KEY_DOWN );\n    if ( changed_type & ( XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED ) ) {\n        xkb_mod_mask_t mods = xkb_state_serialize_mods( algorithm->state,\n                                                        algorithm->try_shift ? XKB_STATE_MODS_EFFECTIVE : ( XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED ) );\n\n        const xkb_keysym_t *keysyms;\n        int num_keysyms = xkb_state_key_get_syms( algorithm->state, key, &keysyms );\n        /* We can handle exactly one keysym with exactly one bit set in the implementation\n         * below; with a lot more gymnastics, we could set up an 8x8 linear system and solve\n         * for each modifier in case there are some modifiers that are only present in\n         * combination with others, but it is not worth the effort. */\n        if ( num_keysyms == 1 && mods && ( mods & ( mods-1 ) ) == 0 ) {\n#define S2( k, a )                                                      \\\n            do {                                                        \\\n                if ( keysyms[0] == XKB_KEY_##k##_L || keysyms[0] == XKB_KEY_##k##_R ) { \\\n                    if ( !algorithm->a )                                \\\n                        algorithm->a = mods;                            \\\n                    else if ( algorithm->a != mods )                    \\\n                        algorithm->failed = 1;                          \\\n                }                                                       \\\n            } while ( 0 )\n#define S1( k, a ) if ( ( keysyms[0] == XKB_KEY_##k ) && !algorithm->a ) algorithm->a = mods\n            S2( Shift, shift );\n            S2( Control, control );\n            S1( Caps_Lock, capsLock );\n            S1( Shift_Lock, numLock );\n            S2( Alt, alt );\n            S2( Super, super );\n            S2( Meta, meta );\n            S2( Hyper, hyper );\n#undef S1\n#undef S2\n        }\n        if ( !algorithm->shift_keycode && ( keysyms[0] == XKB_KEY_Shift_L || keysyms[0] == XKB_KEY_Shift_R ) )\n            algorithm->shift_keycode = key;\n\n        /* If this is a lock, then up and down to remove lock state*/\n        if ( changed_type & XKB_STATE_MODS_LOCKED ) { /* What should we do for LATCHED here? */\n            xkb_state_update_key( algorithm->state, key, XKB_KEY_UP );\n            xkb_state_update_key( algorithm->state, key, XKB_KEY_DOWN );\n        }\n    }\n    xkb_state_update_key( algorithm->state, key, XKB_KEY_UP );\n\n    if ( algorithm->try_shift ) {\n        xkb_state_update_key( algorithm->state, algorithm->shift_keycode, XKB_KEY_UP );\n    }\n}\n\nstatic int local_modifier_mapping(_GLFWXKBData *xkb) {\n    modifier_mapping_algorithm_t algorithm;\n\n    algorithm.failed = 0;\n    algorithm.used_mods = 0;\n    algorithm.shift = algorithm.control = algorithm.capsLock = algorithm.numLock = algorithm.alt = algorithm.super = algorithm.meta = algorithm.hyper = 0;\n    algorithm.try_shift = 0;\n    algorithm.shift_keycode = 0;\n\n    algorithm.state = xkb_state_new( xkb->keymap );\n    if ( algorithm.state != NULL )\n    {\n        xkb_keymap_key_for_each( xkb->keymap, &modifier_mapping_algorithm, &algorithm );\n        if ( !algorithm.shift_keycode )\n            algorithm.failed = 1;\n\n        if ( !( algorithm.shift && algorithm.control && algorithm.alt && algorithm.super && algorithm.meta && algorithm.hyper )\n             && !algorithm.failed  ) {\n            algorithm.try_shift = 1;\n            xkb_keymap_key_for_each( xkb->keymap, &modifier_mapping_algorithm, &algorithm );\n        }\n        xkb_state_unref( algorithm.state );\n        if ( !algorithm.failed && !( algorithm.shift && algorithm.control && algorithm.alt && algorithm.super ) )\n            algorithm.failed = 1;     /* must have found at least those 4 modifiers */\n    }\n\n    if ( !algorithm.failed ) {\n#define S( a ) xkb->a##Idx = XKB_MOD_INVALID; xkb->a##Mask = 0\n        S(control); S(shift); S(capsLock); S(alt); S(super); S(hyper); S(meta); S(numLock);\n#undef S\n\n        unsigned indx, shifted, used_bits = 0;\n        for (indx = 0, shifted = 1; indx < 32; ++indx, shifted <<= 1) {\n#define S( a ) if ( (xkb->a##Idx == XKB_MOD_INVALID) && !( used_bits & shifted ) && algorithm.a == shifted  ) xkb->a##Idx = indx, xkb->a##Mask = shifted, used_bits |= shifted\n            S(control); S(shift); S(capsLock); S(alt); S(super); S(hyper); S(meta); S(numLock);\n#undef S\n        }\n    }\n\n    if ( algorithm.failed )\n        debug( \"Wayland modifier autodetection algorithm failed; using defaults\\n\" );\n    return !algorithm.failed;\n}\n\nstatic void\nglfw_xkb_update_masks(_GLFWXKBData *xkb) {\n    // Should find better solution under Wayland\n    // See https://github.com/kovidgoyal/kitty/pull/3943 for discussion\n\n    if ( getenv( \"KITTY_WAYLAND_DETECT_MODIFIERS\" ) == NULL || !local_modifier_mapping( xkb ) ) {\n#define S( a ) xkb->a##Idx = XKB_MOD_INVALID; xkb->a##Mask = 0\n        S(hyper); S(meta);\n#undef S\n#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx;\n        S(control, XKB_MOD_NAME_CTRL);\n        S(shift, XKB_MOD_NAME_SHIFT);\n        S(capsLock, XKB_MOD_NAME_CAPS);\n        S(numLock, XKB_MOD_NAME_NUM);\n        S(alt, XKB_MOD_NAME_ALT);\n        S(super, XKB_MOD_NAME_LOGO);\n#undef S\n    }\n    debug(\"Modifier indices alt: 0x%x super: 0x%x hyper: 0x%x meta: 0x%x numlock: 0x%x shift: 0x%x capslock: 0x%x control: 0x%x\\n\",\n          xkb->altIdx, xkb->superIdx, xkb->hyperIdx, xkb->metaIdx, xkb->numLockIdx, xkb->shiftIdx, xkb->capsLockIdx, xkb->controlIdx);\n}\n\n#endif\n\nstatic void\nrelease_keyboard_data(_GLFWXKBData *xkb) {\n#define US(group, state, unref) if (xkb->group.state) {  unref(xkb->group.state); xkb->group.state = NULL; }\n#define UK(keymap) if(xkb->keymap) { xkb_keymap_unref(xkb->keymap); xkb->keymap = NULL; }\n    US(states, composeState, xkb_compose_state_unref);\n    UK(keymap);\n    UK(default_keymap);\n    US(states, state, xkb_state_unref);\n    US(states, clean_state, xkb_state_unref);\n    US(states, default_state, xkb_state_unref);\n#undef US\n#undef UK\n\n}\n\nvoid\nglfw_xkb_release(_GLFWXKBData *xkb) {\n    release_keyboard_data(xkb);\n    if (xkb->context) {\n        xkb_context_unref(xkb->context);\n        xkb->context = NULL;\n    }\n    glfw_ibus_terminate(&xkb->ibus);\n}\n\nbool\nglfw_xkb_create_context(_GLFWXKBData *xkb) {\n    xkb->context = xkb_context_new(0);\n    if (!xkb->context)\n    {\n        _glfwInputError(GLFW_PLATFORM_ERROR,\n                        \"Failed to initialize XKB context\");\n        return false;\n    }\n#ifndef _GLFW_WAYLAND\n    glfw_connect_to_ibus(&xkb->ibus);\n#endif\n    return true;\n}\n\nstatic const char*\nload_keymaps(_GLFWXKBData *xkb, const char *map_str) {\n    (void)(map_str);  // not needed on X11\n    xkb_glfw_load_keymap(xkb->keymap, map_str);\n    if (!xkb->keymap) return \"Failed to compile XKB keymap\";\n    // The system default keymap, can be overridden by the XKB_DEFAULT_RULES\n    // env var, see\n    // https://xkbcommon.org/doc/current/structxkb__rule__names.html\n    static struct xkb_rule_names default_rule_names = {0};\n    xkb->default_keymap = xkb_keymap_new_from_names(xkb->context, &default_rule_names, XKB_KEYMAP_COMPILE_NO_FLAGS);\n    if (!xkb->default_keymap) return \"Failed to create default XKB keymap\";\n    return NULL;\n}\n\nstatic const char*\nload_states(_GLFWXKBData *xkb) {\n    xkb_glfw_load_state(xkb->keymap, xkb->states.state);\n    xkb->states.clean_state = xkb_state_new(xkb->keymap);\n    xkb->states.default_state = xkb_state_new(xkb->default_keymap);\n    if (!xkb->states.state || !xkb->states.clean_state || !xkb->states.default_state) return \"Failed to create XKB state\";\n    return NULL;\n}\n\nstatic void\nload_compose_tables(_GLFWXKBData *xkb) {\n    /* Look up the preferred locale, falling back to \"C\" as default. */\n    struct xkb_compose_table* compose_table = NULL;\n    const char *locale = getenv(\"LC_ALL\");\n    if (!locale) locale = getenv(\"LC_CTYPE\");\n    if (!locale) locale = getenv(\"LANG\");\n    if (!locale) locale = \"C\";\n\n    // See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=903373\n    if (strcmp(locale, \"en_IN\") == 0) locale = \"en_IN.UTF-8\";\n\n    compose_table = xkb_compose_table_new_from_locale(xkb->context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);\n    if (!compose_table) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to create XKB compose table for locale %s\", locale);\n        return;\n    }\n    xkb->states.composeState = xkb_compose_state_new(compose_table, XKB_COMPOSE_STATE_NO_FLAGS);\n    if (!xkb->states.composeState) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"Failed to create XKB compose state\");\n    }\n    xkb_compose_table_unref(compose_table);\n}\n\nstatic xkb_mod_mask_t\nactive_unknown_modifiers(_GLFWXKBData *xkb, struct xkb_state *state) {\n    size_t i = 0;\n    xkb_mod_mask_t ans = 0;\n    while (xkb->unknownModifiers[i] != XKB_MOD_INVALID) {\n        if (xkb_state_mod_index_is_active(state, xkb->unknownModifiers[i], XKB_STATE_MODS_EFFECTIVE)) ans |= (1 << xkb->unknownModifiers[i]);\n        i++;\n    }\n    return ans;\n}\n\nstatic unsigned int\nupdate_one_modifier(XKBStateGroup *group, xkb_mod_mask_t mask,\n                    xkb_mod_index_t idx, unsigned int mod) {\n    if ( idx == XKB_MOD_INVALID )\n        return 0;\n    /* Optimization in the case of a single real modifier */\n    if ( mask && ( ( mask & ( mask-1 ) ) == 0 ) )\n        return (xkb_state_mod_index_is_active(group->state, idx, XKB_STATE_MODS_EFFECTIVE) == 1) ? mod : 0;\n    /* Multiple real mods map to the same virtual mod */\n    for ( unsigned indx = 0; indx < 32 && mask; ++indx, mask >>= 1 )\n        if ( ( mask & 1 ) && xkb_state_mod_index_is_active(group->state, indx, XKB_STATE_MODS_EFFECTIVE) == 1)\n            return mod;\n    return 0;\n}\n\nstatic void\nupdate_modifiers(_GLFWXKBData *xkb) {\n    XKBStateGroup *group = &xkb->states;\n#define S(attr, name) group->modifiers |= update_one_modifier( group, xkb->attr##Mask, xkb->attr##Idx, GLFW_MOD_##name )\n    S(control, CONTROL); S(alt, ALT); S(shift, SHIFT); S(super, SUPER); S(hyper, HYPER); S(meta, META); S(capsLock, CAPS_LOCK); S(numLock, NUM_LOCK);\n#undef S\n    xkb->states.activeUnknownModifiers = active_unknown_modifiers(xkb, xkb->states.state);\n\n}\n\nbool\nglfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str) {\n    const char *err;\n    debug(\"Loading new XKB keymaps\\n\");\n    release_keyboard_data(xkb);\n    err = load_keymaps(xkb, map_str);\n    if (err) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"%s\", err);\n        release_keyboard_data(xkb);\n        return false;\n    }\n    err = load_states(xkb);\n    if (err) {\n        _glfwInputError(GLFW_PLATFORM_ERROR, \"%s\", err);\n        release_keyboard_data(xkb);\n        return false;\n    }\n    load_compose_tables(xkb);\n\n    glfw_xkb_update_masks(xkb);\n    size_t capacity = arraysz(xkb->unknownModifiers), j = 0;\n    for (xkb_mod_index_t i = 0; i < capacity; i++) xkb->unknownModifiers[i] = XKB_MOD_INVALID;\n    for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap) && j < capacity - 1; i++) {\n        if (i != xkb->controlIdx && i != xkb->altIdx && i != xkb->shiftIdx && i != xkb->superIdx && i != xkb->hyperIdx && i != xkb->metaIdx && i != xkb->capsLockIdx && i != xkb->numLockIdx) xkb->unknownModifiers[j++] = i;\n    }\n    xkb->states.modifiers = 0;\n    xkb->states.activeUnknownModifiers = 0;\n    update_modifiers(xkb);\n    return true;\n}\n\nvoid\nglfw_xkb_update_modifiers(_GLFWXKBData *xkb, xkb_mod_mask_t depressed, xkb_mod_mask_t latched, xkb_mod_mask_t locked, xkb_layout_index_t base_group, xkb_layout_index_t latched_group, xkb_layout_index_t locked_group) {\n    if (!xkb->keymap) return;\n    xkb->states.modifiers = 0;\n    xkb_state_update_mask(xkb->states.state, depressed, latched, locked, base_group, latched_group, locked_group);\n    // We have to update the groups in clean_state, as they change for\n    // different keyboard layouts, see https://github.com/kovidgoyal/kitty/issues/488\n    xkb_state_update_mask(xkb->states.clean_state, 0, 0, 0, base_group, latched_group, locked_group);\n    update_modifiers(xkb);\n}\n\nbool\nglfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t keycode) {\n#ifdef _GLFW_WAYLAND\n    keycode += 8;\n#endif\n    if (!xkb->keymap) return false;\n    return xkb_keymap_key_repeats(xkb->keymap, keycode);\n}\n\n\nstatic xkb_keysym_t\ncompose_symbol(struct xkb_compose_state *composeState, xkb_keysym_t sym, int *compose_completed, char *key_text, int n) {\n    *compose_completed = 0;\n    if (sym == XKB_KEY_NoSymbol || !composeState) return sym;\n    if (xkb_compose_state_feed(composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) return sym;\n    switch (xkb_compose_state_get_status(composeState)) {\n        case XKB_COMPOSE_COMPOSED:\n            xkb_compose_state_get_utf8(composeState, key_text, n);\n            *compose_completed = 1;\n            return xkb_compose_state_get_one_sym(composeState);\n        case XKB_COMPOSE_COMPOSING:\n        case XKB_COMPOSE_CANCELLED:\n            return XKB_KEY_NoSymbol;\n        case XKB_COMPOSE_NOTHING:\n        default:\n            return sym;\n    }\n}\n\n\nconst char*\nglfw_xkb_keysym_name(xkb_keysym_t sym) {\n    static char name[256];\n    name[0] = 0;\n    xkb_keysym_get_name(sym, name, sizeof(name));\n    return name;\n}\n\nint\nglfw_xkb_keysym_from_name(const char *name, bool case_sensitive) {\n    return (int)xkb_keysym_from_name(name, case_sensitive ? XKB_KEYSYM_NO_FLAGS : XKB_KEYSYM_CASE_INSENSITIVE);\n}\n\nstatic const char*\nformat_mods(unsigned int mods) {\n    static char buf[128];\n    char *p = buf, *s;\n#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, \"%s\", x)\n    pr(\"mods: \");\n    s = p;\n    if (mods & GLFW_MOD_CONTROL) pr(\"ctrl+\");\n    if (mods & GLFW_MOD_ALT) pr(\"alt+\");\n    if (mods & GLFW_MOD_SHIFT) pr(\"shift+\");\n    if (mods & GLFW_MOD_SUPER) pr(\"super+\");\n    if (mods & GLFW_MOD_META) pr(\"meta+\");\n    if (mods & GLFW_MOD_HYPER) pr(\"hyper+\");\n    if (mods & GLFW_MOD_CAPS_LOCK) pr(\"capslock+\");\n    if (mods & GLFW_MOD_NUM_LOCK) pr(\"numlock+\");\n    if (p == s) pr(\"none\");\n    else p--;\n    pr(\" \");\n#undef pr\n    return buf;\n}\n\nstatic const char*\nformat_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) {\n    static char buf[512];\n    char *p = buf, *s;\n#define pr(x) { \\\n    int num_needed = -1; \\\n    ssize_t space_left = sizeof(buf) - (p - buf) - 1; \\\n    if (space_left > 0) num_needed = snprintf(p, space_left, \"%s\", x);  \\\n    if (num_needed > 0) p += num_needed; \\\n}\n    pr(name); pr(\": \");\n    s = p;\n    for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap); i++) {\n        xkb_mod_mask_t m = 1 << i;\n        if (m & mods) { pr(xkb_keymap_mod_get_name(xkb->keymap, i)); pr(\"+\"); }\n    }\n    if (p == s) { pr(\"none\"); }\n    else p--;\n    pr(\" \");\n#undef pr\n    return buf;\n}\n\nvoid\nglfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev) {\n    if (!xkb->keymap) return;\n    int x = 0, y = 0;\n    switch(ev->type) {\n        case GLFW_IME_UPDATE_FOCUS:\n            glfw_ibus_set_focused(&xkb->ibus, ev->focused);\n            break;\n        case GLFW_IME_UPDATE_CURSOR_POSITION:\n            _glfwPlatformGetWindowPos(w, &x, &y);\n            x += ev->cursor.left; y += ev->cursor.top;\n            glfw_ibus_set_cursor_geometry(&xkb->ibus, x, y, ev->cursor.width, ev->cursor.height);\n            break;\n    }\n}\n\nvoid\nglfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) {\n    _GLFWwindow *window = _glfwWindowForId(ev->window_id);\n    if (failed && window && window->callbacks.keyboard) {\n        // notify application to remove any existing pre-edit text\n        GLFWkeyevent fake_ev = {.action = GLFW_PRESS};\n        fake_ev.ime_state = GLFW_IME_PREEDIT_CHANGED;\n        window->callbacks.keyboard((GLFWwindow*) window, &fake_ev);\n    }\n    static xkb_keycode_t last_handled_press_keycode = 0;\n    // We filter out release events that correspond to the last press event\n    // handled by the IME system. This won't fix the case of multiple key\n    // presses before a release, but is better than nothing. For that case\n    // you'd need to implement a ring buffer to store pending key presses.\n    xkb_keycode_t prev_handled_press = last_handled_press_keycode;\n    last_handled_press_keycode = 0;\n    bool is_release = ev->glfw_ev.action == GLFW_RELEASE;\n    debug(\"From IBUS: native_key: 0x%x name: %s is_release: %d handled_by_ime: %d\\n\", ev->glfw_ev.native_key, glfw_xkb_keysym_name(ev->glfw_ev.key), is_release, handled_by_ime);\n    if (window && !handled_by_ime && !(is_release && ev->glfw_ev.native_key == (int) prev_handled_press)) {\n        debug(\"↳ to application: glfw_keycode: 0x%x (%s) keysym: 0x%x (%s) action: %s %s text: %s\\n\",\n            ev->glfw_ev.native_key, _glfwGetKeyName(ev->glfw_ev.native_key), ev->glfw_ev.key, glfw_xkb_keysym_name(ev->glfw_ev.key),\n            (ev->glfw_ev.action == GLFW_RELEASE ? \"RELEASE\" : (ev->glfw_ev.action == GLFW_PRESS ? \"PRESS\" : \"REPEAT\")),\n            format_mods(ev->glfw_ev.mods), ev->glfw_ev.text\n        );\n\n        ev->glfw_ev.ime_state = GLFW_IME_NONE;\n        _glfwInputKeyboard(window, &ev->glfw_ev);\n    } else debug(\"↳ discarded\\n\");\n    if (!is_release && handled_by_ime)\n      last_handled_press_keycode = ev->glfw_ev.native_key;\n}\n\nvoid\nglfw_xkb_forwarded_key_from_ime(xkb_keysym_t keysym, unsigned int glfw_mods) {\n    _GLFWwindow *w = _glfwFocusedWindow();\n    if (w && w->callbacks.keyboard) {\n        GLFWkeyevent fake_ev = {.action = GLFW_PRESS};\n        fake_ev.native_key = keysym;\n        fake_ev.key = glfw_key_for_sym(keysym);\n        fake_ev.mods = glfw_mods;\n        fake_ev.ime_state = GLFW_IME_NONE;\n        w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);\n    }\n}\n\nstatic bool\nis_switch_layout_key(xkb_keysym_t xkb_sym) {\n    return xkb_sym == XKB_KEY_ISO_First_Group || xkb_sym == XKB_KEY_ISO_Last_Group || xkb_sym == XKB_KEY_ISO_Next_Group || xkb_sym == XKB_KEY_ISO_Prev_Group || xkb_sym == XKB_KEY_Mode_switch;\n}\n\nvoid\nglfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t xkb_keycode, int action) {\n    if (!xkb->keymap) return;\n    static char key_text[64] = {0};\n    const xkb_keysym_t *syms, *clean_syms, *default_syms;\n    xkb_keysym_t xkb_sym, shifted_xkb_sym = XKB_KEY_NoSymbol, alternate_xkb_sym = XKB_KEY_NoSymbol;\n    xkb_keycode_t code_for_sym = xkb_keycode, ibus_keycode = xkb_keycode;\n    GLFWkeyevent glfw_ev = {.action = GLFW_PRESS, .native_key_id = xkb_keycode};\n#ifdef _GLFW_WAYLAND\n    code_for_sym += 8;\n#else\n    ibus_keycode -= 8;\n#endif\n    debug(\"%s xkb_keycode: 0x%x \", action == GLFW_RELEASE ? \"\\x1b[32mRelease\\x1b[m\" : \"\\x1b[31mPress\\x1b[m\", xkb_keycode);\n    XKBStateGroup *sg = &xkb->states;\n    int num_syms = xkb_state_key_get_syms(sg->state, code_for_sym, &syms);\n    int num_clean_syms = xkb_state_key_get_syms(sg->clean_state, code_for_sym, &clean_syms);\n    key_text[0] = 0;\n    // According to the documentation of xkb_compose_state_feed it does not\n    // support multi-sym events, so we ignore them\n    if (num_syms != 1 || num_clean_syms != 1) {\n        debug(\"num_syms: %d num_clean_syms: %d ignoring event\\n\", num_syms, num_clean_syms);\n        return;\n    }\n    xkb_sym = clean_syms[0];\n    shifted_xkb_sym = syms[0];\n    debug(\"clean_sym: %s \", glfw_xkb_keysym_name(clean_syms[0]));\n    if (action == GLFW_PRESS || action == GLFW_REPEAT) {\n        const char *text_type = \"composed_text\";\n        int compose_completed;\n        xkb_sym = compose_symbol(sg->composeState, syms[0], &compose_completed, key_text, sizeof(key_text));\n        if (xkb_sym == XKB_KEY_NoSymbol && !compose_completed) {\n            debug(\"compose not complete, ignoring.\\n\");\n            return;\n        }\n        debug(\"composed_sym: %s \", glfw_xkb_keysym_name(xkb_sym));\n        if (xkb_sym == syms[0]) { // composed sym is the same as non-composed sym\n            // Only use the clean_sym if no mods other than the mods we report\n            // are active (for example if ISO_Shift_Level_* mods are active\n            // they are not reported by GLFW so the key should be the shifted\n            // key). See https://github.com/kovidgoyal/kitty/issues/171#issuecomment-377557053\n            xkb_mod_mask_t consumed_unknown_mods = xkb_state_key_get_consumed_mods(sg->state, code_for_sym) & sg->activeUnknownModifiers;\n            if (sg->activeUnknownModifiers) debug(\"%s\", format_xkb_mods(xkb, \"active_unknown_mods\", sg->activeUnknownModifiers));\n            if (consumed_unknown_mods) { debug(\"%s\", format_xkb_mods(xkb, \"consumed_unknown_mods\", consumed_unknown_mods)); }\n            else if (!is_switch_layout_key(xkb_sym)) xkb_sym = clean_syms[0];\n            // xkb returns text even if alt and/or super are pressed\n            if ( ((GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER | GLFW_MOD_HYPER | GLFW_MOD_META) & sg->modifiers) == 0) {\n              xkb_state_key_get_utf8(sg->state, code_for_sym, key_text, sizeof(key_text));\n            }\n            text_type = \"text\";\n        }\n        if ((1 <= key_text[0] && key_text[0] <= 31) || key_text[0] == 127) {\n          key_text[0] = 0;  // don't send text for ascii control codes\n        }\n        if (key_text[0]) { debug(\"%s: %s \", text_type, key_text); }\n    }\n    if (is_switch_layout_key(xkb_sym)) { debug(\" is a keyboard layout shift key, ignoring.\\n\"); return; }\n    if (sg->modifiers & GLFW_MOD_NUM_LOCK && XKB_KEY_KP_Space <= xkb_sym && xkb_sym <= XKB_KEY_KP_9) {\n        xkb_sym = xkb_state_key_get_one_sym(sg->state, code_for_sym);\n    }\n    int num_default_syms = xkb_state_key_get_syms(sg->default_state, code_for_sym, &default_syms);\n    if (num_default_syms > 0) alternate_xkb_sym = default_syms[0];\n    int glfw_sym = glfw_key_for_sym(xkb_sym);\n\n    debug(\n        \"%s%s: %d (%s) xkb_key: %d (%s)\",\n        format_mods(sg->modifiers),\n        \"glfw_key\", glfw_sym, _glfwGetKeyName(glfw_sym),\n        xkb_sym, glfw_xkb_keysym_name(xkb_sym)\n    );\n    bool has_shifted_key = shifted_xkb_sym != xkb_sym && shifted_xkb_sym != XKB_KEY_NoSymbol;\n    bool has_alternate_key = alternate_xkb_sym != xkb_sym && alternate_xkb_sym != XKB_KEY_NoSymbol;\n    if (has_shifted_key) {\n        glfw_ev.shifted_key = glfw_key_for_sym(shifted_xkb_sym);\n        if (glfw_ev.shifted_key) debug(\" shifted_key: %d (%s)\", glfw_ev.shifted_key, _glfwGetKeyName(glfw_ev.shifted_key))\n    }\n    if (has_alternate_key) {\n        glfw_ev.alternate_key = glfw_key_for_sym(alternate_xkb_sym);\n        if (glfw_ev.alternate_key) debug(\" alternate_key: %d (%s)\", glfw_ev.alternate_key, _glfwGetKeyName(glfw_ev.alternate_key))\n    }\n    debug(\"\\n\");\n\n    // NOTE: On linux, the reported native key identifier is the XKB keysym value.\n    // Do not confuse `native_key` with `xkb_keycode` (the native keycode reported for the\n    // glfw event VS the X internal code for a key).\n    //\n    // We use the XKB keysym instead of the X keycode to be able to go back-and-forth between\n    // the GLFW keysym and the XKB keysym when needed, which is not possible using the X keycode,\n    // because of the lost information when resolving the keycode to the keysym, like consumed\n    // mods.\n    glfw_ev.native_key = xkb_sym;\n\n    glfw_ev.action = action;\n    glfw_ev.key = glfw_sym;\n    glfw_ev.mods = sg->modifiers;\n    glfw_ev.text = key_text;\n    _GLFWIBUSKeyEvent ibus_ev;\n    ibus_ev.glfw_ev = glfw_ev;\n    ibus_ev.ibus_keycode = ibus_keycode;\n    ibus_ev.window_id = window->id;\n    ibus_ev.ibus_keysym = syms[0];\n    if (ibus_process_key(&ibus_ev, &xkb->ibus)) {\n        debug(\"↳ to IBUS: keycode: 0x%x keysym: 0x%x (%s) %s\\n\", ibus_ev.ibus_keycode, ibus_ev.ibus_keysym, glfw_xkb_keysym_name(ibus_ev.ibus_keysym), format_mods(ibus_ev.glfw_ev.mods));\n    } else {\n        _glfwInputKeyboard(window, &glfw_ev);\n    }\n}\n"
  },
  {
    "path": "glfw/xkb_glfw.h",
    "content": "//========================================================================\n// GLFW 3.4 XKB - www.glfw.org\n//------------------------------------------------------------------------\n// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would\n//    be appreciated but is not required.\n//\n// 2. Altered source versions must be plainly marked as such, and must not\n//    be misrepresented as being the original software.\n//\n// 3. This notice may not be removed or altered from any source\n//    distribution.\n//\n//========================================================================\n\n#pragma once\n\n#include <xkbcommon/xkbcommon.h>\n#include <xkbcommon/xkbcommon-compose.h>\n#ifdef _GLFW_X11\n#include <xkbcommon/xkbcommon-x11.h>\n#endif\n\n#include \"ibus_glfw.h\"\n\ntypedef struct {\n    struct xkb_state*       state;\n    struct xkb_state*       clean_state;\n    struct xkb_state*       default_state;\n    struct xkb_compose_state* composeState;\n    xkb_mod_mask_t          activeUnknownModifiers;\n    unsigned int            modifiers;\n} XKBStateGroup;\n\n\ntypedef struct {\n    struct xkb_context*     context;\n    struct xkb_keymap*      keymap;\n    struct xkb_keymap*      default_keymap;\n    XKBStateGroup           states;\n\n    xkb_mod_index_t         controlIdx;\n    xkb_mod_index_t         altIdx;\n    xkb_mod_index_t         shiftIdx;\n    xkb_mod_index_t         superIdx;\n    xkb_mod_index_t         hyperIdx;\n    xkb_mod_index_t         metaIdx;\n    xkb_mod_index_t         capsLockIdx;\n    xkb_mod_index_t         numLockIdx;\n    xkb_mod_mask_t          controlMask;\n    xkb_mod_mask_t          altMask;\n    xkb_mod_mask_t          shiftMask;\n    xkb_mod_mask_t          superMask;\n    xkb_mod_mask_t          hyperMask;\n    xkb_mod_mask_t          metaMask;\n    xkb_mod_mask_t          capsLockMask;\n    xkb_mod_mask_t          numLockMask;\n    xkb_mod_index_t         unknownModifiers[256];\n    _GLFWIBUSData           ibus;\n\n#ifdef _GLFW_X11\n    int32_t                 keyboard_device_id;\n    bool                    available;\n    bool                    detectable;\n    int                     majorOpcode;\n    int                     eventBase;\n    int                     errorBase;\n    int                     major;\n    int                     minor;\n#endif\n\n} _GLFWXKBData;\n\n#ifdef _GLFW_X11\nbool glfw_xkb_set_x11_events_mask(void);\nbool glfw_xkb_update_x11_keyboard_id(_GLFWXKBData *xkb);\n#endif\n\nvoid glfw_xkb_release(_GLFWXKBData *xkb);\nbool glfw_xkb_create_context(_GLFWXKBData *xkb);\nbool glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str);\nvoid glfw_xkb_update_modifiers(_GLFWXKBData *xkb, xkb_mod_mask_t depressed, xkb_mod_mask_t latched, xkb_mod_mask_t locked, xkb_layout_index_t base_group, xkb_layout_index_t latched_group, xkb_layout_index_t locked_group);\nbool glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t keycode);\nconst char* glfw_xkb_keysym_name(xkb_keysym_t sym);\nxkb_keysym_t glfw_xkb_sym_for_key(uint32_t key);\nvoid glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t keycode, int action);\nint glfw_xkb_keysym_from_name(const char *name, bool case_sensitive);\nvoid glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev);\nvoid glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed);\nvoid glfw_xkb_forwarded_key_from_ime(xkb_keysym_t keysym, unsigned int glfw_mods);\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/kovidgoyal/kitty\n\ngo 1.26.0\n\ntoolchain go1.26.1\n\nrequire (\n\tgithub.com/ALTree/bigfloat v0.2.0\n\tgithub.com/alecthomas/chroma/v2 v2.23.1\n\tgithub.com/bmatcuk/doublestar/v4 v4.10.0\n\tgithub.com/dlclark/regexp2 v1.11.5\n\tgithub.com/google/go-cmp v0.7.0\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b\n\tgithub.com/klauspost/compress v1.18.4\n\tgithub.com/kovidgoyal/dbus v0.0.0-20250519011319-e811c41c0bc1\n\tgithub.com/kovidgoyal/go-parallel v1.1.1\n\tgithub.com/kovidgoyal/go-shm v1.0.0\n\tgithub.com/kovidgoyal/imaging v1.8.20\n\tgithub.com/nwaples/rardecode/v2 v2.2.2\n\tgithub.com/seancfoley/ipaddress-go v1.7.1\n\tgithub.com/shirou/gopsutil/v4 v4.26.2\n\tgithub.com/ulikunitz/xz v0.5.15\n\tgithub.com/zeebo/xxh3 v1.1.0\n\tgolang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b\n\tgolang.org/x/image v0.36.0\n\tgolang.org/x/sys v0.42.0\n\tgolang.org/x/text v0.34.0\n\thowett.net/plist v1.0.1\n)\n\n// Uncomment the following to use a local checkout of dbus\n// replace github.com/kovidgoyal/dbus => ../dbus\n\n// Uncomment the following to use a local checkout of imaging\n// replace github.com/kovidgoyal/imaging => ../imaging\n\nrequire (\n\tgithub.com/ebitengine/purego v0.10.0 // indirect\n\tgithub.com/go-ole/go-ole v1.2.6 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.10 // indirect\n\tgithub.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect\n\tgithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect\n\tgithub.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect\n\tgithub.com/seancfoley/bintree v1.3.1 // indirect\n\tgithub.com/tklauser/go-sysconf v0.3.16 // indirect\n\tgithub.com/tklauser/numcpus v0.11.0 // indirect\n\tgithub.com/yusufpapurcu/wmi v1.2.4 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/ALTree/bigfloat v0.2.0 h1:AwNzawrpFuw55/YDVlcPw0F0cmmXrmngBHhVrvdXPvM=\ngithub.com/ALTree/bigfloat v0.2.0/go.mod h1:+NaH2gLeY6RPBPPQf4aRotPPStg+eXc8f9ZaE4vRfD4=\ngithub.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=\ngithub.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=\ngithub.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=\ngithub.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=\ngithub.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=\ngithub.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=\ngithub.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=\ngithub.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=\ngithub.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=\ngithub.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=\ngithub.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4=\ngithub.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=\ngithub.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=\ngithub.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=\ngithub.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=\ngithub.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=\ngithub.com/kovidgoyal/dbus v0.0.0-20250519011319-e811c41c0bc1 h1:rMY/hWfcVzBm6BLX6YLA+gLJEpuXBed/VP6YEkXt8R4=\ngithub.com/kovidgoyal/dbus v0.0.0-20250519011319-e811c41c0bc1/go.mod h1:RbNG3Q1g6GUy1/WzWVx+S24m7VKyvl57vV+cr2hpt50=\ngithub.com/kovidgoyal/go-parallel v1.1.1 h1:1OzpNjtrUkBPq3UaqrnvOoB2F9RttSt811uiUXyI7ok=\ngithub.com/kovidgoyal/go-parallel v1.1.1/go.mod h1:BJNIbe6+hxyFWv7n6oEDPj3PA5qSw5OCtf0hcVxWJiw=\ngithub.com/kovidgoyal/go-shm v1.0.0 h1:HJEel9D1F9YhULvClEHJLawoRSj/1u/EDV7MJbBPgQo=\ngithub.com/kovidgoyal/go-shm v1.0.0/go.mod h1:Yzb80Xf9L3kaoB2RGok9hHwMIt7Oif61kT6t3+VnZds=\ngithub.com/kovidgoyal/imaging v1.8.20 h1:74GZ7C2rIm3rqmGEjK1GvvPOOnJ0SS5iDOa6Flfo0b0=\ngithub.com/kovidgoyal/imaging v1.8.20/go.mod h1:d3phGYkTChGYkY4y++IjpHgUGhWGELDc2NEQAqxwZZg=\ngithub.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=\ngithub.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=\ngithub.com/nwaples/rardecode/v2 v2.2.2 h1:/5oL8dzYivRM/tqX9VcTSWfbpwcbwKG1QtSJr3b3KcU=\ngithub.com/nwaples/rardecode/v2 v2.2.2/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=\ngithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=\ngithub.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=\ngithub.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=\ngithub.com/seancfoley/bintree v1.3.1 h1:cqmmQK7Jm4aw8gna0bP+huu5leVOgHGSJBEpUx3EXGI=\ngithub.com/seancfoley/bintree v1.3.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=\ngithub.com/seancfoley/ipaddress-go v1.7.1 h1:fDWryS+L8iaaH5RxIKbY0xB5Z+Zxk8xoXLN4S4eAPdQ=\ngithub.com/seancfoley/ipaddress-go v1.7.1/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=\ngithub.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI=\ngithub.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=\ngithub.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=\ngithub.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=\ngithub.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=\ngithub.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=\ngithub.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=\ngithub.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=\ngithub.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngithub.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=\ngithub.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=\ngithub.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=\ngithub.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=\ngolang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=\ngolang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=\ngolang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc=\ngolang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=\ngolang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=\ngolang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=\ngolang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhowett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=\nhowett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=\n"
  },
  {
    "path": "key_encoding.json",
    "content": "{\n  \"0\": \"G\",\n  \"1\": \"H\",\n  \"2\": \"I\",\n  \"3\": \"J\",\n  \"4\": \"K\",\n  \"5\": \"L\",\n  \"6\": \"M\",\n  \"7\": \"N\",\n  \"8\": \"O\",\n  \"9\": \"P\",\n  \"A\": \"S\",\n  \"APOSTROPHE\": \"B\",\n  \"B\": \"T\",\n  \"BACKSLASH\": \"t\",\n  \"BACKSPACE\": \"1\",\n  \"C\": \"U\",\n  \"CAPS LOCK\": \":\",\n  \"COMMA\": \"C\",\n  \"D\": \"V\",\n  \"DELETE\": \"3\",\n  \"DOWN\": \"6\",\n  \"E\": \"W\",\n  \"END\": \"-\",\n  \"ENTER\": \"z\",\n  \"EQUAL\": \"R\",\n  \"ESCAPE\": \"y\",\n  \"F\": \"X\",\n  \"F1\": \"/\",\n  \"F10\": \"]\",\n  \"F11\": \"{\",\n  \"F12\": \"}\",\n  \"F13\": \"@\",\n  \"F14\": \"%\",\n  \"F15\": \"$\",\n  \"F16\": \"#\",\n  \"F17\": \"BA\",\n  \"F18\": \"BB\",\n  \"F19\": \"BC\",\n  \"F2\": \"*\",\n  \"F20\": \"BD\",\n  \"F21\": \"BE\",\n  \"F22\": \"BF\",\n  \"F23\": \"BG\",\n  \"F24\": \"BH\",\n  \"F25\": \"BI\",\n  \"F3\": \"?\",\n  \"F4\": \"&\",\n  \"F5\": \"<\",\n  \"F6\": \">\",\n  \"F7\": \"(\",\n  \"F8\": \")\",\n  \"F9\": \"[\",\n  \"G\": \"Y\",\n  \"GRAVE ACCENT\": \"v\",\n  \"H\": \"Z\",\n  \"HOME\": \".\",\n  \"I\": \"a\",\n  \"INSERT\": \"2\",\n  \"J\": \"b\",\n  \"K\": \"c\",\n  \"KP 0\": \"BJ\",\n  \"KP 1\": \"BK\",\n  \"KP 2\": \"BL\",\n  \"KP 3\": \"BM\",\n  \"KP 4\": \"BN\",\n  \"KP 5\": \"BO\",\n  \"KP 6\": \"BP\",\n  \"KP 7\": \"BQ\",\n  \"KP 8\": \"BR\",\n  \"KP 9\": \"BS\",\n  \"KP ADD\": \"BX\",\n  \"KP DECIMAL\": \"BT\",\n  \"KP DIVIDE\": \"BU\",\n  \"KP ENTER\": \"BY\",\n  \"KP EQUAL\": \"BZ\",\n  \"KP MULTIPLY\": \"BV\",\n  \"KP SUBTRACT\": \"BW\",\n  \"L\": \"d\",\n  \"LEFT\": \"5\",\n  \"LEFT ALT\": \"Bc\",\n  \"LEFT BRACKET\": \"s\",\n  \"LEFT CONTROL\": \"Bb\",\n  \"LEFT SHIFT\": \"Ba\",\n  \"LEFT SUPER\": \"Bd\",\n  \"M\": \"e\",\n  \"MINUS\": \"D\",\n  \"N\": \"f\",\n  \"NUM LOCK\": \"=\",\n  \"O\": \"g\",\n  \"P\": \"h\",\n  \"PAGE DOWN\": \"9\",\n  \"PAGE UP\": \"8\",\n  \"PAUSE\": \"!\",\n  \"PERIOD\": \"E\",\n  \"PRINT SCREEN\": \"^\",\n  \"Q\": \"i\",\n  \"R\": \"j\",\n  \"RIGHT\": \"4\",\n  \"RIGHT ALT\": \"Bg\",\n  \"RIGHT BRACKET\": \"u\",\n  \"RIGHT CONTROL\": \"Bf\",\n  \"RIGHT SHIFT\": \"Be\",\n  \"RIGHT SUPER\": \"Bh\",\n  \"S\": \"k\",\n  \"SCROLL LOCK\": \"+\",\n  \"SEMICOLON\": \"Q\",\n  \"SLASH\": \"F\",\n  \"SPACE\": \"A\",\n  \"T\": \"l\",\n  \"TAB\": \"0\",\n  \"U\": \"m\",\n  \"UP\": \"7\",\n  \"V\": \"n\",\n  \"W\": \"o\",\n  \"WORLD 1\": \"w\",\n  \"WORLD 2\": \"x\",\n  \"X\": \"p\",\n  \"Y\": \"q\",\n  \"Z\": \"r\"\n}"
  },
  {
    "path": "kittens/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/ask/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/ask/choices.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ask\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n\t\"io\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"unicode\"\n)\n\nvar _ = fmt.Print\n\ntype Choice struct {\n\ttext          string\n\tidx           int\n\tcolor, letter string\n}\n\nfunc (self Choice) prefix() string {\n\treturn string([]rune(self.text)[:self.idx])\n}\n\nfunc (self Choice) display_letter() string {\n\treturn string([]rune(self.text)[self.idx])\n}\n\nfunc (self Choice) suffix() string {\n\treturn string([]rune(self.text)[self.idx+1:])\n}\n\ntype Range struct {\n\tstart, end, y int\n}\n\nfunc (self *Range) has_point(x, y int) bool {\n\treturn y == self.y && self.start <= x && x <= self.end\n}\n\nfunc truncate_at_space(text string, width int) (string, string) {\n\ttruncated, p := wcswidth.TruncateToVisualLengthWithWidth(text, width)\n\tif len(truncated) == len(text) {\n\t\treturn text, \"\"\n\t}\n\ti := strings.LastIndexByte(truncated, ' ')\n\tif i > 0 && p-i < 12 {\n\t\tp = i + 1\n\t}\n\treturn text[:p], text[p:]\n}\n\nfunc extra_for(width, screen_width int) int {\n\treturn max(0, screen_width-width)/2 + 1\n}\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc GetChoices(o *Options) (response string, err error) {\n\tresponse = \"\"\n\tlp, err := loop.New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tlp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)\n\n\tprefix_style_pat := regexp.MustCompile(\"^(?:\\x1b\\\\[[^m]*?m)+\")\n\tchoice_order := make([]Choice, 0, len(o.Choices))\n\tclickable_ranges := make(map[string][]Range, 16)\n\tallowed := utils.NewSet[string](max(2, len(o.Choices)))\n\tresponse_on_accept := o.Default\n\tswitch o.Type {\n\tcase \"yesno\":\n\t\tallowed.AddItems(\"y\", \"n\")\n\t\tif !allowed.Has(response_on_accept) {\n\t\t\tresponse_on_accept = \"y\"\n\t\t}\n\tcase \"choices\":\n\t\tfirst_choice := \"\"\n\t\tfor i, x := range o.Choices {\n\t\t\tletter, text, _ := strings.Cut(x, \":\")\n\t\t\tcolor := \"\"\n\t\t\tif strings.Contains(letter, \";\") {\n\t\t\t\tletter, color, _ = strings.Cut(letter, \";\")\n\t\t\t}\n\t\t\tletter = strings.ToLower(letter)\n\t\t\tidx := strings.Index(strings.ToLower(text), letter)\n\t\t\tif idx < 0 {\n\t\t\t\treturn \"\", fmt.Errorf(\"The choice letter %#v is not present in the choice text: %#v\", letter, text)\n\t\t\t}\n\t\t\tidx = len([]rune(strings.ToLower(text)[:idx]))\n\t\t\tallowed.Add(letter)\n\t\t\tc := Choice{text: text, idx: idx, color: color, letter: letter}\n\t\t\tchoice_order = append(choice_order, c)\n\t\t\tif i == 0 {\n\t\t\t\tfirst_choice = letter\n\t\t\t}\n\t\t}\n\t\tif !allowed.Has(response_on_accept) {\n\t\t\tresponse_on_accept = first_choice\n\t\t}\n\t}\n\tmessage := o.Message\n\thidden_text_start_pos := -1\n\thidden_text_end_pos := -1\n\thidden_text := \"\"\n\tm := markup.New(true)\n\treplacement_text := fmt.Sprintf(\"Press %s or click to show\", m.Green(o.UnhideKey))\n\treplacement_range := Range{-1, -1, -1}\n\tif message != \"\" && o.HiddenTextPlaceholder != \"\" {\n\t\thidden_text_start_pos = strings.Index(message, o.HiddenTextPlaceholder)\n\t\tif hidden_text_start_pos > -1 {\n\t\t\traw, err := io.ReadAll(os.Stdin)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"Failed to read hidden text from STDIN: %w\", err)\n\t\t\t}\n\t\t\thidden_text = strings.TrimRightFunc(utils.UnsafeBytesToString(raw), unicode.IsSpace)\n\t\t\thidden_text_end_pos = hidden_text_start_pos + len(replacement_text)\n\t\t\tsuffix := message[hidden_text_start_pos+len(o.HiddenTextPlaceholder):]\n\t\t\tmessage = message[:hidden_text_start_pos] + replacement_text + suffix\n\t\t}\n\t}\n\n\tdraw_long_text := func(screen_width int, text string, msg_lines []string) []string {\n\t\tif screen_width < 3 {\n\t\t\treturn msg_lines\n\t\t}\n\t\tif text == \"\" {\n\t\t\tmsg_lines = append(msg_lines, \"\")\n\t\t} else {\n\t\t\twidth := screen_width - 2\n\t\t\tprefix := prefix_style_pat.FindString(text)\n\t\t\tfor text != \"\" {\n\t\t\t\tvar t string\n\t\t\t\tt, text = truncate_at_space(text, width)\n\t\t\t\tt = strings.TrimSpace(t)\n\t\t\t\tmsg_lines = append(msg_lines, strings.Repeat(\" \", extra_for(wcswidth.Stringwidth(t), width))+m.Bold(prefix+t))\n\t\t\t}\n\t\t}\n\t\treturn msg_lines\n\t}\n\n\tctx := style.Context{AllowEscapeCodes: true}\n\n\tdraw_choice_boxes := func(y, screen_width, _ int, choices ...Choice) {\n\t\tclickable_ranges = map[string][]Range{}\n\t\twidth := screen_width - 2\n\t\tcurrent_line_length := 0\n\t\ttype Item struct{ letter, text string }\n\t\ttype Line = []Item\n\t\tvar current_line Line\n\t\tlines := make([]Line, 0, 32)\n\t\tsep := \"  \"\n\t\tsep_sz := len(sep) + 2 // for the borders\n\n\t\tfor _, choice := range choices {\n\t\t\tclickable_ranges[choice.letter] = make([]Range, 0, 4)\n\t\t\ttext := \" \" + choice.prefix()\n\t\t\tcolor := choice.color\n\t\t\tif choice.color == \"\" {\n\t\t\t\tcolor = \"green\"\n\t\t\t}\n\t\t\ttext += ctx.SprintFunc(\"fg=\" + color)(choice.display_letter())\n\t\t\ttext += choice.suffix() + \" \"\n\t\t\tsz := wcswidth.Stringwidth(text)\n\t\t\tif sz+sep_sz+current_line_length > width {\n\t\t\t\tlines = append(lines, current_line)\n\t\t\t\tcurrent_line = nil\n\t\t\t\tcurrent_line_length = 0\n\t\t\t}\n\t\t\tcurrent_line = append(current_line, Item{choice.letter, text})\n\t\t\tcurrent_line_length += sz + sep_sz\n\t\t}\n\t\tif len(current_line) > 0 {\n\t\t\tlines = append(lines, current_line)\n\t\t}\n\n\t\thighlight := func(text string) string {\n\t\t\treturn m.Yellow(text)\n\t\t}\n\n\t\ttop := func(text string, highlight_frame bool) (ans string) {\n\t\t\tans = \"╭\" + strings.Repeat(\"─\", wcswidth.Stringwidth(text)) + \"╮\"\n\t\t\tif highlight_frame {\n\t\t\t\tans = highlight(ans)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tmiddle := func(text string, highlight_frame bool) (ans string) {\n\t\t\tf := \"│\"\n\t\t\tif highlight_frame {\n\t\t\t\tf = highlight(f)\n\t\t\t}\n\t\t\treturn f + text + f\n\t\t}\n\n\t\tbottom := func(text string, highlight_frame bool) (ans string) {\n\t\t\tans = \"╰\" + strings.Repeat(\"─\", wcswidth.Stringwidth(text)) + \"╯\"\n\t\t\tif highlight_frame {\n\t\t\t\tans = highlight(ans)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tprint_line := func(add_borders func(string, bool) string, is_last bool, items ...Item) {\n\t\t\ttype Position struct {\n\t\t\t\tletter  string\n\t\t\t\tx, size int\n\t\t\t}\n\t\t\ttexts := make([]string, 0, 8)\n\t\t\tpositions := make([]Position, 0, 8)\n\t\t\tx := 0\n\t\t\tfor _, item := range items {\n\t\t\t\ttext := item.text\n\t\t\t\tpositions = append(positions, Position{item.letter, x, wcswidth.Stringwidth(text) + 2})\n\t\t\t\ttext = add_borders(text, item.letter == response_on_accept)\n\t\t\t\ttext += sep\n\t\t\t\tx += wcswidth.Stringwidth(text)\n\t\t\t\ttexts = append(texts, text)\n\t\t\t}\n\t\t\tline := strings.TrimRightFunc(strings.Join(texts, \"\"), unicode.IsSpace)\n\t\t\toffset := extra_for(wcswidth.Stringwidth(line), width)\n\t\t\tfor _, pos := range positions {\n\t\t\t\tx = pos.x\n\t\t\t\tx += offset\n\t\t\t\tclickable_ranges[pos.letter] = append(clickable_ranges[pos.letter], Range{x, x + pos.size - 1, y})\n\t\t\t}\n\t\t\tend := \"\\r\\n\"\n\t\t\tif is_last {\n\t\t\t\tend = \"\"\n\t\t\t}\n\t\t\tlp.QueueWriteString(strings.Repeat(\" \", offset) + line + end)\n\t\t\ty++\n\t\t}\n\t\tlp.AllowLineWrapping(false)\n\t\tdefer func() { lp.AllowLineWrapping(true) }()\n\t\tfor i, boxed_line := range lines {\n\t\t\tprint_line(top, false, boxed_line...)\n\t\t\tprint_line(middle, false, boxed_line...)\n\t\t\tis_last := i == len(lines)-1\n\t\t\tprint_line(bottom, is_last, boxed_line...)\n\t\t}\n\t}\n\n\tdraw_yesno := func(y, screen_width, screen_height int) {\n\t\tyes := m.Green(\"Y\") + \"es\"\n\t\tno := m.BrightRed(\"N\") + \"o\"\n\t\tif y+3 <= screen_height {\n\t\t\tdraw_choice_boxes(y, screen_width, screen_height, Choice{\"Yes\", 0, \"green\", \"y\"}, Choice{\"No\", 0, \"red\", \"n\"})\n\t\t} else {\n\t\t\tsep := strings.Repeat(\" \", 3)\n\t\t\ttext := yes + sep + no\n\t\t\tw := wcswidth.Stringwidth(text)\n\t\t\tx := extra_for(w, screen_width-2)\n\t\t\tnx := x + wcswidth.Stringwidth(yes) + len(sep)\n\t\t\tclickable_ranges = map[string][]Range{\n\t\t\t\t\"y\": {{x, x + wcswidth.Stringwidth(yes) - 1, y}},\n\t\t\t\t\"n\": {{nx, nx + wcswidth.Stringwidth(no) - 1, y}},\n\t\t\t}\n\t\t\tlp.QueueWriteString(strings.Repeat(\" \", x) + text)\n\t\t}\n\t}\n\n\tdraw_choice := func(y, screen_width, screen_height int) {\n\t\tif y+3 <= screen_height {\n\t\t\tdraw_choice_boxes(y, screen_width, screen_height, choice_order...)\n\t\t\treturn\n\t\t}\n\t\tclickable_ranges = map[string][]Range{}\n\t\tcurrent_line := \"\"\n\t\tcurrent_ranges := map[string]int{}\n\t\twidth := screen_width - 2\n\n\t\tcommit_line := func(add_newline bool) {\n\t\t\tx := extra_for(wcswidth.Stringwidth(current_line), width)\n\t\t\ttext := strings.Repeat(\" \", x) + current_line\n\t\t\tif add_newline {\n\t\t\t\tlp.Println(text)\n\t\t\t} else {\n\t\t\t\tlp.QueueWriteString(text)\n\t\t\t}\n\t\t\tfor letter, sz := range current_ranges {\n\t\t\t\tclickable_ranges[letter] = []Range{{x, x + sz - 3, y}}\n\t\t\t\tx += sz\n\t\t\t}\n\t\t\tcurrent_ranges = map[string]int{}\n\t\t\ty++\n\t\t\tcurrent_line = \"\"\n\t\t}\n\t\tfor _, choice := range choice_order {\n\t\t\ttext := choice.prefix()\n\t\t\tspec := \"\"\n\t\t\tif choice.color != \"\" {\n\t\t\t\tspec = \"fg=\" + choice.color\n\t\t\t} else {\n\t\t\t\tspec = \"fg=green\"\n\t\t\t}\n\t\t\tif choice.letter == response_on_accept {\n\t\t\t\tspec += \" u=straight\"\n\t\t\t}\n\t\t\ttext += ctx.SprintFunc(spec)(choice.display_letter())\n\t\t\ttext += choice.suffix()\n\t\t\ttext += \"  \"\n\t\t\tsz := wcswidth.Stringwidth(text)\n\t\t\tif sz+wcswidth.Stringwidth(current_line) >= width {\n\t\t\t\tcommit_line(true)\n\t\t\t}\n\t\t\tcurrent_line += text\n\t\t\tcurrent_ranges[choice.letter] = sz\n\t\t}\n\t\tif current_line != \"\" {\n\t\t\tcommit_line(false)\n\t\t}\n\t}\n\n\tdraw_screen := func() error {\n\t\tlp.StartAtomicUpdate()\n\t\tdefer lp.EndAtomicUpdate()\n\t\tlp.ClearScreen()\n\t\tmsg_lines := make([]string, 0, 8)\n\t\tsz, err := lp.ScreenSize()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif message != \"\" {\n\t\t\tscanner := utils.NewLineScanner(message)\n\t\t\tfor scanner.Scan() {\n\t\t\t\tmsg_lines = draw_long_text(int(sz.WidthCells), scanner.Text(), msg_lines)\n\t\t\t}\n\t\t}\n\t\ty := int(sz.HeightCells) - len(msg_lines)\n\t\ty = max(0, (y/2)-2)\n\t\tlp.QueueWriteString(strings.Repeat(\"\\r\\n\", y))\n\t\tfor _, line := range msg_lines {\n\t\t\tif replacement_text != \"\" {\n\t\t\t\tidx := strings.Index(line, replacement_text)\n\t\t\t\tif idx > -1 {\n\t\t\t\t\tx := wcswidth.Stringwidth(line[:idx])\n\t\t\t\t\treplacement_range = Range{x, x + wcswidth.Stringwidth(replacement_text), y}\n\t\t\t\t}\n\t\t\t}\n\t\t\tlp.Println(line)\n\t\t\ty++\n\t\t}\n\t\tif sz.HeightCells > 2 {\n\t\t\tlp.Println()\n\t\t\ty++\n\t\t}\n\t\tswitch o.Type {\n\t\tcase \"yesno\":\n\t\t\tdraw_yesno(y, int(sz.WidthCells), int(sz.HeightCells))\n\t\tcase \"choices\":\n\t\t\tdraw_choice(y, int(sz.WidthCells), int(sz.HeightCells))\n\t\t}\n\t\treturn nil\n\t}\n\n\tunhide := func() {\n\t\tif hidden_text != \"\" && message != \"\" {\n\t\t\tmessage = message[:hidden_text_start_pos] + hidden_text + message[hidden_text_end_pos:]\n\t\t\thidden_text = \"\"\n\t\t\t_ = draw_screen()\n\t\t}\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.SetCursorVisible(false)\n\t\tif o.Title != \"\" {\n\t\t\tlp.SetWindowTitle(o.Title)\n\t\t}\n\t\treturn \"\", draw_screen()\n\t}\n\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorVisible(true)\n\t\treturn \"\"\n\t}\n\n\tlp.OnText = func(text string, from_key_event, in_bracketed_paste bool) error {\n\t\ttext = strings.ToLower(text)\n\t\tif allowed.Has(text) {\n\t\t\tresponse = text\n\t\t\tlp.Quit(0)\n\t\t} else if hidden_text != \"\" && text == o.UnhideKey {\n\t\t\tunhide()\n\t\t} else if o.Type == \"yesno\" {\n\t\t\tlp.Quit(1)\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnKeyEvent = func(ev *loop.KeyEvent) error {\n\t\tif ev.MatchesPressOrRepeat(\"esc\") || ev.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\t\tev.Handled = true\n\t\t\tlp.Quit(1)\n\t\t} else if ev.MatchesPressOrRepeat(\"enter\") || ev.MatchesPressOrRepeat(\"kp_enter\") {\n\t\t\tev.Handled = true\n\t\t\tresponse = response_on_accept\n\t\t\tlp.Quit(0)\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnMouseEvent = func(ev *loop.MouseEvent) error {\n\t\ton_letter := \"\"\n\t\tfor letter, ranges := range clickable_ranges {\n\t\t\tfor _, r := range ranges {\n\t\t\t\tif r.has_point(ev.Cell.X, ev.Cell.Y) {\n\t\t\t\t\ton_letter = letter\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif on_letter != \"\" {\n\t\t\tif s, has_shape := lp.CurrentPointerShape(); !has_shape && s != loop.POINTER_POINTER {\n\t\t\t\tlp.PushPointerShape(loop.POINTER_POINTER)\n\t\t\t}\n\t\t} else {\n\t\t\tif _, has_shape := lp.CurrentPointerShape(); has_shape {\n\t\t\t\tlp.PopPointerShape()\n\t\t\t}\n\t\t}\n\n\t\tif ev.Event_type == loop.MOUSE_CLICK {\n\t\t\tif on_letter != \"\" {\n\t\t\t\tresponse = on_letter\n\t\t\t\tlp.Quit(0)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif hidden_text != \"\" && replacement_range.has_point(ev.Cell.X, ev.Cell.Y) {\n\t\t\t\tunhide()\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnResize = func(old, news loop.ScreenSize) error {\n\t\treturn draw_screen()\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn \"\", fmt.Errorf(\"Filled by signal: %s\", ds)\n\t}\n\treturn response, nil\n}\n"
  },
  {
    "path": "kittens/ask/get_line.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ask\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/kittens/choose_files\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/readline\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc get_line(o *Options, complete_file_names bool) (result string, err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors)\n\tif err != nil {\n\t\treturn\n\t}\n\tcwd, _ := os.Getwd()\n\tropts := readline.RlInit{Prompt: o.Prompt}\n\tif complete_file_names {\n\t\tropts.Completer = choose_files.FilePromptCompleter(nil)\n\t}\n\tif o.Name != \"\" {\n\t\tbase := filepath.Join(utils.CacheDir(), \"ask\")\n\t\tropts.HistoryPath = filepath.Join(base, o.Name+\".history.json\")\n\t\tos.MkdirAll(base, 0o755)\n\t}\n\trl := readline.New(lp, ropts)\n\tif o.Default != \"\" {\n\t\trl.SetText(o.Default)\n\t}\n\tlp.OnInitialize = func() (string, error) {\n\t\trl.Start()\n\t\treturn \"\", nil\n\t}\n\tlp.OnFinalize = func() string { rl.End(); return \"\" }\n\n\tlp.OnResumeFromStop = func() error {\n\t\trl.Start()\n\t\treturn nil\n\t}\n\n\tlp.OnResize = rl.OnResize\n\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\t\treturn fmt.Errorf(\"Canceled by user\")\n\t\t}\n\t\terr := rl.OnKeyEvent(event)\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tlp.Quit(0)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif err == readline.ErrAcceptInput {\n\t\t\t\thi := readline.HistoryItem{Timestamp: time.Now(), Cmd: rl.AllText(), ExitCode: 0, Cwd: cwd}\n\t\t\t\trl.AddHistoryItem(hi)\n\t\t\t\tresult = rl.AllText()\n\t\t\t\tlp.Quit(0)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif event.Handled {\n\t\t\trl.Redraw()\n\t\t\treturn nil\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnText = func(text string, from_key_event, in_bracketed_paste bool) error {\n\t\terr := rl.OnText(text, from_key_event, in_bracketed_paste)\n\t\tif err == nil {\n\t\t\trl.Redraw()\n\t\t}\n\t\treturn err\n\t}\n\n\terr = lp.Run()\n\trl.Shutdown()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\treturn \"\", fmt.Errorf(\"Killed by signal: %s\", ds)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "kittens/ask/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ask\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n)\n\nvar _ = fmt.Print\n\ntype Response struct {\n\tItems    []string `json:\"items\"`\n\tResponse string   `json:\"response\"`\n}\n\nfunc show_message(msg, title string) {\n\tif title != \"\" {\n\t\tfmt.Printf(\"%s\", loop.EscapeCodeToSetWindowTitle(title))\n\t}\n\tif msg != \"\" {\n\t\tm := markup.New(true)\n\t\tfmt.Println(m.Bold(msg))\n\t}\n}\n\nfunc main(_ *cli.Command, o *Options, args []string) (rc int, err error) {\n\toutput := tui.KittenOutputSerializer()\n\tresult := &Response{Items: args}\n\tif len(o.Prompt) > 2 && o.Prompt[0] == o.Prompt[len(o.Prompt)-1] && (o.Prompt[0] == '\"' || o.Prompt[0] == '\\'') {\n\t\to.Prompt = o.Prompt[1 : len(o.Prompt)-1]\n\t}\n\tswitch o.Type {\n\tcase \"yesno\", \"choices\":\n\t\tresult.Response, err = GetChoices(o)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\tcase \"password\":\n\t\tshow_message(o.Message, o.Title)\n\t\tpw, err := tui.ReadPassword(o.Prompt, false)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, tui.Canceled) {\n\t\t\t\tpw = \"\"\n\t\t\t} else {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t}\n\t\tresult.Response = pw\n\tcase \"line\":\n\t\tshow_message(o.Message, o.Title)\n\t\tresult.Response, err = get_line(o, false)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\tcase \"file\":\n\t\tshow_message(o.Message, o.Title)\n\t\tresult.Response, err = get_line(o, true)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\tdefault:\n\t\treturn 1, fmt.Errorf(\"Unknown type: %s\", o.Type)\n\t}\n\ts, err := output(result)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\t_, err = fmt.Println(s)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/ask/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\n\nfrom kitty.typing_compat import BossType, TypedDict\n\nfrom ..tui.handler import result_handler\n\n\ndef option_text() -> str:\n    return '''\\\n--type -t\nchoices=line,yesno,choices,password,file\ndefault=line\nType of input. Defaults to asking for a line of text.\n\n\n--message -m\nThe message to display to the user. If not specified a default\nmessage is shown.\n\n\n--name -n\nThe name for this question. Used to store history of previous answers which can\nbe used for completions and via the browse history readline bindings.\n\n\n--title --window-title\nThe title for the window in which the question is displayed. Only implemented\nfor yesno and choices types.\n\n\n--choice -c\ntype=list\ndest=choices\nA choice for the choices type. Can be specified multiple times. Every choice has\nthe syntax: ``letter[;color]:text``, where :italic:`text` is the choice\ntext and :italic:`letter` is the selection key. :italic:`letter` is a single letter\nbelonging to :italic:`text`. This letter is highlighted within the choice text.\nThere can be an optional color specification after the letter\nto indicate what color it should be.\nFor example: :code:`y:Yes` and :code:`n;red:No`\n\n\n--default -d\nA default choice or text. If unspecified, it is :code:`y` for the type\n:code:`yesno`, the first choice for :code:`choices` and empty for others types.\nThe default choice is selected when the user presses the :kbd:`Enter` key.\n\n\n--prompt -p\ndefault=\"> \"\nThe prompt to use when inputting a line of text or a password.\n\n\n--unhide-key\ndefault=u\nThe key to be pressed to unhide hidden text\n\n\n--hidden-text-placeholder\nThe text in the message to be replaced by hidden text. The hidden text is read via STDIN.\n'''\n\n\nclass Response(TypedDict):\n    items: list[str]\n    response: str | None\n\ndef main(args: list[str]) -> Response:\n    raise SystemExit('This must be run as kitten ask')\n\n\n@result_handler()\ndef handle_result(args: list[str], data: Response, target_window_id: int, boss: BossType) -> None:\n    if data['response'] is not None:\n        func, *args = data['items']\n        getattr(boss, func)(data['response'], *args)\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = ''\n    cd['options'] = option_text\n    cd['help_text'] = 'Ask the user for input'\n    cd['short_desc'] = 'Ask the user for input'\n"
  },
  {
    "path": "kittens/broadcast/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/broadcast/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nfrom base64 import standard_b64encode\nfrom gettext import gettext as _\nfrom typing import Any\n\nfrom kitty.cli import parse_args\nfrom kitty.cli_stub import BroadcastCLIOptions\nfrom kitty.key_encoding import encode_key_event\nfrom kitty.rc.base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION\nfrom kitty.remote_control import create_basic_command, encode_send\nfrom kitty.short_uuid import uuid4\nfrom kitty.typing_compat import KeyEventType, ScreenSize\n\nfrom ..tui.handler import Handler\nfrom ..tui.line_edit import LineEdit\nfrom ..tui.loop import Loop\nfrom ..tui.operations import RESTORE_CURSOR, SAVE_CURSOR, styled\n\n\ndef session_command(payload: dict[str, Any], start: bool = True) -> bytes:\n    payload = payload.copy()\n    payload['data'] = 'session:' + ('start' if start else 'end')\n    send = create_basic_command('send-text', payload, no_response=True)\n    return encode_send(send)\n\n\nclass Broadcast(Handler):\n\n    def __init__(self, opts: BroadcastCLIOptions, initial_strings: list[str]) -> None:\n        self.opts = opts\n        self.hide_input = False\n        self.initial_strings = initial_strings\n        self.payload = {'exclude_active': True, 'data': '', 'match': opts.match, 'match_tab': opts.match_tab, 'session_id': uuid4()}\n        self.line_edit = LineEdit()\n        self.session_started = False\n        if not opts.match and not opts.match_tab:\n            self.payload['all'] = True\n\n    def initialize(self) -> None:\n        self.write_broadcast_session()\n        self.print('Type the text to broadcast below, press', styled(self.opts.end_session, fg='yellow'), 'to quit:')\n        for x in self.initial_strings:\n            self.write_broadcast_text(x)\n        self.write(SAVE_CURSOR)\n\n    def commit_line(self) -> None:\n        self.write(RESTORE_CURSOR + SAVE_CURSOR)\n        self.cmd.clear_to_end_of_screen()\n        self.line_edit.write(self.write, screen_cols=self.screen_size.cols)\n\n    def on_resize(self, screen_size: ScreenSize) -> None:\n        super().on_resize(screen_size)\n        self.commit_line()\n\n    def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:\n        self.write_broadcast_text(text)\n        if not self.hide_input:\n            self.line_edit.on_text(text, in_bracketed_paste)\n        self.commit_line()\n\n    def on_interrupt(self) -> None:\n        self.write_broadcast_text('\\x03')\n        self.line_edit.clear()\n        self.commit_line()\n\n    def on_eot(self) -> None:\n        self.write_broadcast_text('\\x04')\n\n    def on_key(self, key_event: KeyEventType) -> None:\n        if key_event.matches(self.opts.hide_input_toggle):\n            self.hide_input ^= True\n            self.cmd.set_cursor_visible(not self.hide_input)\n            if self.hide_input:\n                self.end_line()\n                self.print('Input hidden, press', styled(self.opts.hide_input_toggle, fg='yellow'), 'to unhide:')\n                self.end_line()\n            return\n        if key_event.matches(self.opts.end_session):\n            self.quit_loop(0)\n            return\n        if not self.hide_input and self.line_edit.on_key(key_event):\n            self.commit_line()\n        if key_event.matches('enter'):\n            self.write_broadcast_text('\\r')\n            self.end_line()\n            return\n\n        ek = encode_key_event(key_event)\n        ek = standard_b64encode(ek.encode('utf-8')).decode('ascii')\n        self.write_broadcast_data('kitty-key:' + ek)\n\n    def end_line(self) -> None:\n        self.print('')\n        self.line_edit.clear()\n        self.write(SAVE_CURSOR)\n\n    def write_broadcast_text(self, text: str) -> None:\n        self.write_broadcast_data('base64:' + standard_b64encode(text.encode('utf-8')).decode('ascii'))\n\n    def write_broadcast_data(self, data: str) -> None:\n        payload = self.payload.copy()\n        payload['data'] = data\n        send = create_basic_command('send-text', payload, no_response=True)\n        self.write(encode_send(send))\n\n    def write_broadcast_session(self, start: bool = True) -> None:\n        self.session_started = start\n        self.write(session_command(self.payload, start))\n\n\nOPTIONS = ('''\n--hide-input-toggle\ndefault=Ctrl+Alt+Esc\nKey to press that will toggle hiding of the input in the broadcast window itself.\nUseful while typing a password, prevents the password from being visible on the screen.\n\n\n--end-session\ndefault=Ctrl+Esc\nKey to press to end the broadcast session.\n\n\n''' + MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')).format\nhelp_text = 'Broadcast typed text to kitty windows. By default text is sent to all windows, unless one of the matching options is specified'\nusage = '[initial text to send ...]'\n\n\ndef parse_broadcast_args(args: list[str]) -> tuple[BroadcastCLIOptions, list[str]]:\n    return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten broadcast', result_class=BroadcastCLIOptions)\n\n\ndef main(args: list[str]) -> dict[str, Any] | None:\n    try:\n        opts, items = parse_broadcast_args(args[1:])\n    except SystemExit as e:\n        if e.code != 0:\n            print(e.args[0], file=sys.stderr)\n            input(_('Press Enter to quit'))\n        return None\n\n    sys.stdout.flush()\n    loop = Loop()\n    handler = Broadcast(opts, items)\n    try:\n        loop.loop(handler)\n    finally:\n        if handler.session_started:\n            sys.stdout.buffer.write(session_command(handler.payload, False))\n            sys.stdout.buffer.flush()\n    return None\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Broadcast typed text to kitty windows'\n"
  },
  {
    "path": "kittens/choose_files/__init__.py",
    "content": "def syntax_aliases(x: str) -> dict[str, str]:\n    ans = {}\n    for x in x.split():\n        k, _, v = x.partition(':')\n        ans[k] = v\n    return ans\n"
  },
  {
    "path": "kittens/choose_files/archive.go",
    "content": "package choose_files\n\nimport (\n\t\"archive/tar\"\n\t\"compress/bzip2\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/klauspost/compress/gzip\"\n\t\"github.com/klauspost/compress/zip\"\n\t\"github.com/klauspost/compress/zstd\"\n\t\"github.com/nwaples/rardecode/v2\"\n\t\"github.com/ulikunitz/xz\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc IsSupportedArchiveFile(abspath string) bool {\n\tname := strings.ToLower(filepath.Base(abspath))\n\text := filepath.Ext(name)\n\tswitch ext {\n\tcase \".zip\", \".tgz\", \".tbz2\", \".tzst\", \".txz\", \".rar\":\n\t\treturn true\n\tcase \".gz\", \".bz2\", \".zst\", \".xz\":\n\t\tname = name[:len(name)-len(ext)]\n\t\text = filepath.Ext(name)\n\t\treturn ext == \".tar\"\n\tdefault:\n\t\treturn false\n\t}\n}\n\ntype archive_preview struct {\n\tpath             string\n\tmetadata         fs.FileInfo\n\tWakeupMainThread func() bool\n\tch               chan *MessagePreview\n\tmp               *MessagePreview\n}\n\nfunc displayFilename(s string) string {\n\tif isPrintableUTF8(s) {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"%X\", utils.UnsafeStringToBytes(s))\n}\n\nfunc isPrintableUTF8(s string) bool {\n\tfor _, r := range s {\n\t\tif r == '\\uFFFD' { // replacement char\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (p *archive_preview) render() {\n\tname := strings.ToLower(filepath.Base(p.path))\n\text := filepath.Ext(name)\n\tnames := []string{\"\"}\n\tpopulate_tar := func(r io.Reader) {\n\t\ttr := tar.NewReader(r)\n\t\tfor len(names) < 500 {\n\t\t\thdr, err := tr.Next()\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tnames = append(names, displayFilename(hdr.Name))\n\t\t}\n\t}\n\tswitch ext {\n\tcase \".zip\":\n\t\tr, err := zip.OpenReader(p.path)\n\t\tif err == nil || errors.Is(err, zip.ErrInsecurePath) {\n\t\t\tdefer r.Close()\n\t\t\tfor _, f := range r.File {\n\t\t\t\tif f.NonUTF8 {\n\t\t\t\t\tnames = append(names, displayFilename(f.Name))\n\t\t\t\t} else {\n\t\t\t\t\tnames = append(names, f.Name)\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\tcase \".rar\":\n\t\tr, err := rardecode.OpenReader(p.path, rardecode.SkipCheck)\n\t\tif err == nil {\n\t\t\tdefer r.Close()\n\t\t\tfor len(names) < 500 {\n\t\t\t\thdr, err := r.Next()\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tnames = append(names, displayFilename(hdr.Name))\n\t\t\t}\n\t\t}\n\tcase \".gz\", \".tgz\":\n\t\tif f, err := os.Open(p.path); err == nil {\n\t\t\tdefer f.Close()\n\t\t\tif gz, err := gzip.NewReader(f); err == nil {\n\t\t\t\tdefer gz.Close()\n\t\t\t\tpopulate_tar(gz)\n\t\t\t}\n\t\t}\n\tcase \".xz\", \".txz\":\n\t\tif f, err := os.Open(p.path); err == nil {\n\t\t\tdefer f.Close()\n\t\t\tif gz, err := xz.NewReader(f); err == nil {\n\t\t\t\tpopulate_tar(gz)\n\t\t\t}\n\t\t}\n\tcase \".bz2\", \".tbz2\":\n\t\tif f, err := os.Open(p.path); err == nil {\n\t\t\tdefer f.Close()\n\t\t\tpopulate_tar(bzip2.NewReader(f))\n\t\t}\n\tcase \".zst\", \".tzst\":\n\t\tif f, err := os.Open(p.path); err == nil {\n\t\t\tdefer f.Close()\n\t\t\tif gz, err := zstd.NewReader(f); err == nil {\n\t\t\t\tdefer gz.Close()\n\t\t\t\tpopulate_tar(gz)\n\t\t\t}\n\t\t}\n\t}\n\tmp := *p.mp\n\tmp.trailers = append(mp.trailers, names...)\n\tp.ch <- &mp\n\tp.WakeupMainThread()\n}\n\nfunc (p *archive_preview) IsReady() bool                         { return true }\nfunc (p *archive_preview) Unload()                               {}\nfunc (p *archive_preview) IsValidForColorScheme(light bool) bool { return true }\nfunc (p *archive_preview) String() string                        { return fmt.Sprintf(\"ArchivePreview{%s}\", p.path) }\n\nfunc (p *archive_preview) Render(h *Handler, x, y, width, height int) {\n\tif p.ch != nil {\n\t\tselect {\n\t\tcase mp := <-p.ch:\n\t\t\tp.mp = mp\n\t\t\tclose(p.ch)\n\t\t\tp.ch = nil\n\t\tdefault:\n\t\t}\n\t}\n\tp.mp.Render(h, x, y, width, height)\n}\n\nfunc NewArchivePeview(\n\tabspath string, metadata fs.FileInfo, opts Settings, WakeupMainThread func() bool,\n) Preview {\n\tmp := NewFileMetadataPreview(abspath, metadata)\n\tans := &archive_preview{\n\t\tpath: abspath, metadata: metadata, WakeupMainThread: WakeupMainThread, ch: make(chan *MessagePreview, 1), mp: mp,\n\t}\n\tgo ans.render()\n\treturn ans\n}\n"
  },
  {
    "path": "kittens/choose_files/calibre.go",
    "content": "package choose_files\n\nimport (\n\t\"bufio\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/icons\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\nvar calibre_needs_cleanup atomic.Bool\nvar calibre_server_load_finished atomic.Bool\n\ntype calibre_server_process struct {\n\tproc            *exec.Cmd\n\tstdout          io.ReadCloser\n\tstdin           io.WriteCloser\n\tfile_extensions *utils.Set[string]\n}\n\ntype CalibreMetadata struct {\n\tTitle        string    `json:\"title\"`\n\tAuthors      []string  `json:\"authors\"`\n\tSeries       string    `json:\"series\"`\n\tSeries_index float64   `json:\"series_index\"`\n\tTags         []string  `json:\"tags\"`\n\tRating       float64   `json:\"rating\"`\n\tPublished    time.Time `json:\"pubdate\"`\n\tTimestamp    time.Time `json:\"timestamp\"`\n}\n\ntype CalibreResponse struct {\n\tPath      string          `json:\"path\"`\n\tFiletypes []string        `json:\"filetypes\"`\n\tCover     string          `json:\"cover\"`\n\tError     string          `json:\"error\"`\n\tMetadata  CalibreMetadata `json:\"metadata\"`\n}\n\nfunc ReadLineWithTimeout(r io.Reader, timeout time.Duration) (string, error) {\n\ttype result struct {\n\t\tline string\n\t\terr  error\n\t}\n\tch := make(chan result, 1)\n\n\tgo func() {\n\t\tbr := bufio.NewReader(r)\n\t\tline, err := br.ReadString('\\n')\n\t\tch <- result{strings.TrimRight(line, \"\\n\"), err}\n\t}()\n\n\tselect {\n\tcase res := <-ch:\n\t\treturn res.line, res.err\n\tcase <-time.After(timeout):\n\t\treturn \"\", os.ErrDeadlineExceeded\n\t}\n}\n\nvar calibre_server = sync.OnceValues(func() (ans *calibre_server_process, err error) {\n\tdefer func() { calibre_server_load_finished.Store(true) }()\n\tans = &calibre_server_process{}\n\tans.proc = exec.Command(\"calibre-debug\", \"-c\", \"from calibre.ebooks.metadata.cli import *; simple_metadata_server()\")\n\tans.proc.Stderr = nil\n\tif ans.stdout, err = ans.proc.StdoutPipe(); err != nil {\n\t\treturn nil, err\n\t}\n\tif ans.stdin, err = ans.proc.StdinPipe(); err != nil {\n\t\tans.stdout.Close()\n\t\treturn nil, err\n\t}\n\tans.proc.SysProcAttr = &unix.SysProcAttr{Setsid: true}\n\tif err = ans.proc.Start(); err != nil {\n\t\terr = fmt.Errorf(\"calibre-debug executable not found in PATH, you must install the calibre program to preview these file types: %w\", err)\n\t\treturn\n\t}\n\tcalibre_needs_cleanup.Store(true)\n\tpayload, _ := json.Marshal(map[string]string{\"path\": \"\"})\n\tif _, err = ans.stdin.Write(append(payload, '\\n')); err != nil {\n\t\terr = fmt.Errorf(\"error writing to calibre metadata server: %w\", err)\n\t\treturn\n\t}\n\tline, err := ReadLineWithTimeout(ans.stdout, 2*time.Second)\n\tif err != nil {\n\t\tif errors.Is(err, os.ErrDeadlineExceeded) {\n\t\t\terr = fmt.Errorf(\"timed out waiting for response from calibre metadata server, make sure you are using calibre version >= 8.16\")\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"error reading from calibre metadata server: %w\", err)\n\t\t}\n\t\treturn\n\t}\n\tvar r CalibreResponse\n\tif err = json.Unmarshal([]byte(line), &r); err != nil {\n\t\terr = fmt.Errorf(\"unexpected response from calibre metadata server: %#v\", line)\n\t\treturn\n\t}\n\tans.file_extensions = utils.NewSet[string](len(r.Filetypes))\n\tfor _, ft := range r.Filetypes {\n\t\tans.file_extensions.Add(\".\" + ft)\n\t}\n\treturn\n})\n\nfunc calibre_cleanup() {\n\tif !calibre_needs_cleanup.Load() {\n\t\treturn\n\t}\n\tcalibre_needs_cleanup.Store(false)\n\tcalibre, _ := calibre_server()\n\tif calibre.stdin != nil {\n\t\tcalibre.stdin.Close()\n\t}\n\tif calibre.stdout != nil {\n\t\tcalibre.stdout.Close()\n\t}\n\tif calibre.proc != nil {\n\t\tcalibre.proc.Wait()\n\t}\n}\n\nfunc IsSupportedByCalibre(path string) bool {\n\text := strings.ToLower(filepath.Ext(filepath.Base(path)))\n\tif len(ext) > 1 {\n\t\tif calibre_server_load_finished.Load() {\n\t\t\tif calibre, err := calibre_server(); err == nil {\n\t\t\t\treturn calibre.file_extensions.Has(ext)\n\t\t\t}\n\t\t} else {\n\t\t\t// we dont want to delay the render loop waiting for data from\n\t\t\t// calibre-server as it causes flicker because the atomic update\n\t\t\t// timeout expires, so use a default set of extensions.\n\t\t\tgo func() { _, _ = calibre_server() }()\n\t\t\treturn slices.Contains([]string{\"cb7\", \"azw3\", \"kepub\", \"zip\", \"htmlz\", \"rar\", \"lrf\", \"cbr\", \"tpz\", \"azw1\", \"mobi\", \"lit\", \"pdf\", \"updb\", \"pml\", \"pobi\", \"fbz\", \"azw\", \"fb2\", \"cbc\", \"rtf\", \"snb\", \"opf\", \"txt\", \"epub\", \"oebzip\", \"txtz\", \"imp\", \"docx\", \"odt\", \"pdb\", \"rb\", \"prc\", \"html\", \"chm\", \"pmlz\", \"cbz\", \"lrx\", \"azw4\"}, ext[1:])\n\t\t}\n\t}\n\treturn false\n}\n\nconst CALIBRE_METADATA_KEY = \"calibre-metadata.json\"\n\ntype calibre_renderer int\n\nfunc (c calibre_renderer) Unmarshall(m map[string]string) (any, error) {\n\tdata, err := os.ReadFile(m[CALIBRE_METADATA_KEY])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ans CalibreResponse\n\tif err = json.Unmarshal(data, &ans); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ans, nil\n}\n\nfunc (c calibre_renderer) Render(path string) (m map[string][]byte, mi metadata, img *images.ImageData, err error) {\n\tcpath, err := os.CreateTemp(\"\", \"\")\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\tcpath.Close()\n\t\tos.Remove(cpath.Name())\n\t}()\n\tcalibre, err := calibre_server()\n\tif err != nil {\n\t\treturn\n\t}\n\tpayload, err := json.Marshal(map[string]string{\"path\": path, \"cover\": cpath.Name()})\n\tif err != nil {\n\t\treturn\n\t}\n\tcalibre.stdin.Write(append(payload, '\\n'))\n\tline, err := ReadLineWithTimeout(calibre.stdout, 30*time.Second)\n\tif err != nil {\n\t\treturn\n\t}\n\tlb := []byte(line)\n\tvar reply CalibreResponse\n\tif err = json.Unmarshal(lb, &reply); err != nil {\n\t\treturn\n\t}\n\tif reply.Cover == cpath.Name() {\n\t\tvar ip ImagePreviewRenderer\n\t\tif m, mi, img, err = ip.Render(cpath.Name()); err != nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tm = make(map[string][]byte)\n\t}\n\tmi.custom = &reply\n\tm[CALIBRE_METADATA_KEY] = lb\n\treturn\n}\n\nfunc (c calibre_renderer) ShowMetadata(h *Handler, s ShowData) (offset int) {\n\tw := func(text string, center bool) {\n\t\tif s.height > offset {\n\t\t\toffset += h.render_wrapped_text_in_region(text, s.x, s.y+offset, s.width, s.height-offset, center)\n\t\t}\n\t}\n\text := filepath.Ext(s.abspath)\n\ttext := fmt.Sprintf(\"%s: %s\", ext, humanize.Bytes(uint64(s.metadata.Size())))\n\ticon := icons.IconForPath(s.abspath)\n\tw(icon+\"  \"+text, true)\n\tr := s.custom_metadata.custom.(*CalibreResponse)\n\tw(\"Title: \"+r.Metadata.Title, false)\n\tw(\"Authors: \"+strings.Join(r.Metadata.Authors, \" & \"), false)\n\tif r.Metadata.Series != \"\" {\n\t\tw(fmt.Sprintf(\"Series: Book %g of %s\", r.Metadata.Series_index, r.Metadata.Series), false)\n\t}\n\tif len(r.Metadata.Tags) > 0 {\n\t\tw(\"Tags: \"+strings.Join(r.Metadata.Authors, \", \"), false)\n\t}\n\treturn\n}\n\nfunc (c calibre_renderer) String() string {\n\treturn \"Calibre\"\n}\n\nfunc NewCalibrePreview(\n\tabspath string, metadata fs.FileInfo, opts Settings, WakeupMainThread func() bool,\n) Preview {\n\tc := calibre_renderer(0)\n\tif ans, err := NewImagePreview(abspath, metadata, opts, WakeupMainThread, c); err == nil {\n\t\treturn ans\n\t} else {\n\t\treturn NewErrorPreview(err)\n\t}\n}\n"
  },
  {
    "path": "kittens/choose_files/cmd_preview.go",
    "content": "package choose_files\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\n\t\"github.com/kovidgoyal/kitty/tools/icons\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nconst CMD_METADATA_KEY = \"cmd-metadata.json\"\n\ntype cmd_renderer struct {\n\tcmdline []string\n}\n\nfunc (c cmd_renderer) String() string {\n\treturn c.cmdline[0]\n}\n\ntype CmdResult struct {\n\tLines      []string `json:\"lines\"`\n\tImage      string   `json:\"image\"`\n\tTitleExtra string   `json:\"title_extra\"`\n}\n\nfunc (c cmd_renderer) Render(path string) (m map[string][]byte, mi metadata, img *images.ImageData, err error) {\n\tcmdline := append(c.cmdline, path)\n\tcmd := exec.Command(cmdline[0], cmdline[1:]...)\n\tcmd.Stdin = nil\n\tcmd.SysProcAttr = &unix.SysProcAttr{Setsid: true}\n\tvar stdout, stderr bytes.Buffer\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\tif err = cmd.Run(); err != nil {\n\t\terr = fmt.Errorf(\"failed to run %v to read metadata from %s with error: %w and stderr: %s\", c.cmdline, path, err, stderr.String())\n\t\treturn\n\t}\n\tvar md CmdResult\n\tif err = json.Unmarshal(stdout.Bytes(), &md); err != nil {\n\t\terr = fmt.Errorf(\"could not decode JSON response from %v for %s: %w\", c.cmdline, path, err)\n\t}\n\tif md.Image != \"\" {\n\t\tvar ip ImagePreviewRenderer\n\t\tif m, mi, img, err = ip.Render(md.Image); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tmi.custom = &md\n\treturn\n}\n\nfunc (c cmd_renderer) Unmarshall(m map[string]string) (any, error) {\n\tdata, err := os.ReadFile(m[CMD_METADATA_KEY])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ans CmdResult\n\tif err = json.Unmarshal(data, &ans); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ans, nil\n}\n\nfunc (c cmd_renderer) ShowMetadata(h *Handler, s ShowData) (offset int) {\n\tw := func(text string, center bool) {\n\t\tif s.height > offset {\n\t\t\toffset += h.render_wrapped_text_in_region(text, s.x, s.y+offset, s.width, s.height-offset, center)\n\t\t}\n\t}\n\text := filepath.Ext(s.abspath)\n\tr := s.custom_metadata.custom.(*CmdResult)\n\ttext := fmt.Sprintf(\"%s: %s%s\", ext, humanize.Bytes(uint64(s.metadata.Size())), r.TitleExtra)\n\ticon := icons.IconForPath(s.abspath)\n\tw(icon+\"  \"+text, true)\n\tfor _, line := range r.Lines {\n\t\tw(line, false)\n\t}\n\th.lp.QueueWriteString(\"\\x1b[m\") // reset SGR attributes\n\treturn\n}\n\nfunc NewCmdPreview(\n\tabspath string, metadata fs.FileInfo, opts Settings, WakeupMainThread func() bool, p previewer,\n) Preview {\n\tc := cmd_renderer{p.cmdline}\n\tif ans, err := NewImagePreview(abspath, metadata, opts, WakeupMainThread, c); err == nil {\n\t\treturn ans\n\t} else {\n\t\treturn NewErrorPreview(err)\n\t}\n}\n"
  },
  {
    "path": "kittens/choose_files/collection.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"slices\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype CollectionIndex struct {\n\tSlice, Pos int\n}\n\nfunc (c CollectionIndex) Compare(o CollectionIndex) int {\n\tif c.Slice == o.Slice {\n\t\treturn c.Pos - o.Pos\n\t}\n\treturn c.Slice - o.Slice\n}\n\nfunc (c CollectionIndex) Less(o CollectionIndex) bool {\n\treturn c.Slice < o.Slice || (c.Slice == o.Slice && c.Pos < o.Pos)\n}\n\nfunc (c *CollectionIndex) NextSlice() {\n\tc.Slice++\n\tc.Pos = 0\n}\n\ntype ResultCollection struct {\n\tslices     [][]ResultItem\n\tappend_idx CollectionIndex\n\tbatch_size int\n}\n\nfunc NewResultCollection(batch_size int) (ans *ResultCollection) {\n\tbatch_size = max(1, batch_size)\n\treturn &ResultCollection{\n\t\tbatch_size: batch_size,\n\t\tslices:     [][]ResultItem{make([]ResultItem, batch_size)},\n\t}\n}\n\nfunc (c *ResultCollection) Len() int {\n\treturn c.batch_size*(len(c.slices)-1) + c.append_idx.Pos\n}\n\nfunc (c *ResultCollection) NextAppendPointer() (ans *ResultItem) {\n\ts := c.slices[c.append_idx.Slice]\n\tans = &s[c.append_idx.Pos]\n\tif c.append_idx.Pos+1 < len(s) {\n\t\tc.append_idx.Pos++\n\t} else if c.append_idx.Slice+1 < len(c.slices) {\n\t\tc.append_idx.NextSlice()\n\t} else {\n\t\tc.slices = append(c.slices, make([]ResultItem, 4096))\n\t\tc.append_idx.NextSlice()\n\t}\n\treturn\n}\n\nfunc (c *ResultCollection) Batch(offset *CollectionIndex) (ans []ResultItem) {\n\tif offset.Slice == c.append_idx.Slice {\n\t\tif offset.Pos < c.append_idx.Pos {\n\t\t\tans = c.slices[offset.Slice][offset.Pos:c.append_idx.Pos]\n\t\t\toffset.Pos = c.append_idx.Pos\n\t\t}\n\t} else if offset.Slice < c.append_idx.Slice {\n\t\tans = c.slices[offset.Slice][offset.Pos:]\n\t\toffset.NextSlice()\n\t}\n\treturn\n}\n\nfunc (c *ResultCollection) NextDir(offset *CollectionIndex) (ans string, ignore_files []ignore_file_with_prefix) {\n\tfor ans == \"\" && offset.Compare(c.append_idx) < 0 {\n\t\tif c.slices[offset.Slice][offset.Pos].ftype&fs.ModeDir != 0 {\n\t\t\tans = c.slices[offset.Slice][offset.Pos].text\n\t\t\tignore_files = c.slices[offset.Slice][offset.Pos].ignore_files\n\t\t}\n\t\toffset.Pos++\n\t\tif offset.Pos >= len(c.slices[offset.Slice]) {\n\t\t\toffset.NextSlice()\n\t\t}\n\t}\n\treturn\n}\n\ntype SortedResults struct {\n\tslices [][]*ResultItem\n\tmutex  sync.Mutex\n\tlen    int\n}\n\nfunc NewSortedResults() *SortedResults { return &SortedResults{} }\nfunc (s *SortedResults) lock()         { s.mutex.Lock() }\nfunc (s *SortedResults) unlock()       { s.mutex.Unlock() }\n\nfunc (s *SortedResults) Len() int {\n\ts.lock()\n\tdefer s.unlock()\n\treturn s.len\n}\n\nfunc (s *SortedResults) Clear() {\n\ts.lock()\n\tdefer s.unlock()\n\ts.slices = nil\n\ts.len = 0\n}\n\nfunc (s *SortedResults) At(pos CollectionIndex) (ans *ResultItem) {\n\ts.lock()\n\tdefer s.unlock()\n\tif pos.Slice < len(s.slices) {\n\t\ts := s.slices[pos.Slice]\n\t\tif pos.Pos < len(s) {\n\t\t\tans = s[pos.Pos]\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *SortedResults) RenderedMatches(pos CollectionIndex, max_num int) (ans []*ResultItem) {\n\ts.lock()\n\tdefer s.unlock()\n\tif pos.Slice >= len(s.slices) {\n\t\treturn\n\t}\n\tif max_num < 0 {\n\t\tmax_num = s.len\n\t}\n\tans = make([]*ResultItem, 0, max_num)\n\tfor ; pos.Slice < len(s.slices) && max_num > 0; pos.NextSlice() {\n\t\tsl := s.slices[pos.Slice]\n\t\tif pos.Pos >= len(sl) {\n\t\t\tcontinue\n\t\t}\n\t\tsl = sl[pos.Pos:min(len(sl), pos.Pos+max_num)]\n\t\tans = append(ans, sl...)\n\t\tmax_num -= len(sl)\n\t}\n\treturn\n}\n\nfunc (s *SortedResults) merge_slice(idx int, sl []*ResultItem) {\n\tsz := len(s.slices[idx])\n\tmaxs := sl[len(sl)-1].score\n\tlimit := idx + 1\n\tfor limit < len(s.slices) {\n\t\tq := s.slices[limit]\n\t\tif q[0].score > maxs {\n\t\t\tbreak\n\t\t}\n\t\tsz += len(q)\n\t\tlimit++\n\t}\n\tans := make([]*ResultItem, 0, sz)\n\ta := 0\n\tb := CollectionIndex{Slice: idx}\n\tss := s.slices[b.Slice]\n\tfor a < len(sl) {\n\t\tif sl[a].score <= ss[b.Pos].score {\n\t\t\tans = append(ans, sl[a])\n\t\t\ta++\n\t\t} else {\n\t\t\tans = append(ans, ss[b.Pos])\n\t\t\tb.Pos++\n\t\t\tif b.Pos >= len(ss) {\n\t\t\t\tb.NextSlice()\n\t\t\t\tif b.Slice >= limit {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tss = s.slices[b.Slice]\n\t\t\t}\n\t\t}\n\t}\n\tans = append(ans, sl[a:]...)\n\tfor ; b.Slice < limit; b.NextSlice() {\n\t\tans = append(ans, s.slices[b.Slice][b.Pos:]...)\n\t}\n\ts.slices = slices.Replace(s.slices, idx, limit, ans)\n}\n\nfunc (s *SortedResults) AddSortedSlice(sl []*ResultItem) {\n\tif len(sl) == 0 {\n\t\treturn\n\t}\n\ts.lock()\n\tdefer s.unlock()\n\ts.len += len(sl)\n\tif len(s.slices) == 0 {\n\t\ts.slices = append(s.slices, sl)\n\t\treturn\n\t}\n\tsl_min, sl_max := sl[0].score, sl[len(sl)-1].score\n\tfor i, q := range s.slices {\n\t\tswitch {\n\t\tcase sl_max <= q[0].score:\n\t\t\ts.slices = slices.Insert(s.slices, i, sl)\n\t\t\treturn\n\t\tcase sl_min >= q[len(q)-1].score:\n\t\t\tcontinue\n\t\tdefault:\n\t\t\ts.merge_slice(i, sl)\n\t\t\treturn\n\t\t}\n\t}\n\ts.slices = append(s.slices, sl)\n}\n\nfunc (s *SortedResults) Apply(first, last CollectionIndex, action func(*ResultItem) (keep_going bool)) {\n\ts.lock()\n\tdefer s.unlock()\n\tif first.Slice >= len(s.slices) || first.Pos >= len(s.slices[first.Slice]) {\n\t\treturn\n\t}\n\tamt := utils.IfElse(first.Less(last), 1, -1)\n\tvar did_wrap bool\n\tfor {\n\t\tif !action(s.slices[first.Slice][first.Pos]) {\n\t\t\tbreak\n\t\t}\n\t\tif first.Compare(last) == 0 {\n\t\t\tbreak\n\t\t}\n\t\tfirst, did_wrap = s.increment_with_wrap_around(first, amt)\n\t\tif did_wrap {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc (s *SortedResults) Closest(idx CollectionIndex, matches func(*ResultItem) bool) *CollectionIndex {\n\ts.lock()\n\tdefer s.unlock()\n\tif idx.Slice >= len(s.slices) || idx.Pos >= len(s.slices[idx.Slice]) {\n\t\treturn nil\n\t}\n\n\ttype result struct {\n\t\tidx   CollectionIndex\n\t\tcount int\n\t}\n\tvar a, b result\n\titerate := func(idx CollectionIndex, amt int, result *result) {\n\t\tvar did_wrap bool\n\t\tvar count int\n\t\tresult.count = -1\n\t\tfor {\n\t\t\tidx, did_wrap = s.increment_with_wrap_around(idx, amt)\n\t\t\tif did_wrap {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcount++\n\t\t\tif matches(s.slices[idx.Slice][idx.Pos]) {\n\t\t\t\tresult.idx = idx\n\t\t\t\tresult.count = count\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tgo func() { iterate(idx, 1, &a) }()\n\tgo func() { iterate(idx, -1, &a) }()\n\tif a.count < 0 && b.count < 0 {\n\t\treturn nil\n\t}\n\treturn utils.IfElse(a.count < b.count, &b.idx, &a.idx)\n}\n\nfunc (s *SortedResults) IncrementIndexWithWrapAroundAndCheck(idx CollectionIndex, amt int) (ans CollectionIndex, did_wrap bool) {\n\ts.lock()\n\tdefer s.unlock()\n\treturn s.increment_with_wrap_around(idx, amt)\n}\n\nfunc (s *SortedResults) IncrementIndexWithWrapAround(idx CollectionIndex, amt int) CollectionIndex {\n\ts.lock()\n\tdefer s.unlock()\n\tans, _ := s.increment_with_wrap_around(idx, amt)\n\treturn ans\n}\n\nfunc (s *SortedResults) increment_with_wrap_around(idx CollectionIndex, amt int) (CollectionIndex, bool) {\n\tdid_wrap := false\n\tif amt > 0 {\n\t\tfor amt > 0 {\n\t\t\tif delta := min(amt, len(s.slices[idx.Slice])-1-idx.Pos); delta > 0 {\n\t\t\t\tidx.Pos += delta\n\t\t\t\tamt -= delta\n\t\t\t} else {\n\t\t\t\tidx.NextSlice()\n\t\t\t\tif idx.Slice >= len(s.slices) {\n\t\t\t\t\tidx = CollectionIndex{} // wraparound\n\t\t\t\t\tdid_wrap = true\n\t\t\t\t}\n\t\t\t\tamt--\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// we use separate code for negative increment instead of doing\n\t\t// increment = len - increment as it is faster in the common case of\n\t\t// increment much smaller than len\n\t\tamt *= -1\n\t\tfor amt > 0 {\n\t\t\tif idx.Pos > 0 {\n\t\t\t\tdelta := min(amt, idx.Pos)\n\t\t\t\tamt -= delta\n\t\t\t\tidx.Pos -= delta\n\t\t\t} else {\n\t\t\t\tif idx.Slice == 0 {\n\t\t\t\t\tidx = CollectionIndex{Slice: len(s.slices) - 1, Pos: len(s.slices[len(s.slices)-1]) - 1}\n\t\t\t\t\tdid_wrap = true\n\t\t\t\t} else {\n\t\t\t\t\tidx.Slice--\n\t\t\t\t\tidx.Pos = len(s.slices[idx.Slice]) - 1\n\t\t\t\t}\n\t\t\t\tamt--\n\t\t\t}\n\t\t}\n\t}\n\treturn idx, did_wrap\n}\n\n// Return a - b\nfunc (s *SortedResults) SignedDistance(a, b CollectionIndex) (ans int) {\n\ts.lock()\n\tdefer s.unlock()\n\treturn s.signed_distance(a, b)\n}\n\n// Return a - b\nfunc (s *SortedResults) signed_distance(a, b CollectionIndex) (ans int) {\n\tmult := -1\n\tif b.Less(a) {\n\t\ta, b = b, a\n\t\tmult = 1\n\t}\n\tlimit := min(b.Slice, len(s.slices))\n\tfor ; a.Slice < limit; a.NextSlice() {\n\t\tans += len(s.slices[a.Slice]) - a.Pos\n\t}\n\treturn mult * (ans + (b.Pos - a.Pos))\n}\n\n// Return |a - b|\nfunc (s *SortedResults) distance(a, b CollectionIndex) (ans int) {\n\tif b.Less(a) {\n\t\ta, b = b, a\n\t}\n\tlimit := min(b.Slice, len(s.slices))\n\tfor ; a.Slice < limit; a.NextSlice() {\n\t\tans += len(s.slices[a.Slice]) - a.Pos\n\t}\n\treturn ans + (b.Pos - a.Pos)\n}\n\nfunc (s *SortedResults) SplitIntoColumns(calc_num_cols func(int) int, num_per_column, num_before_current int, current CollectionIndex) (ans [][]*ResultItem, num_before int, first_idx CollectionIndex) {\n\ts.lock()\n\tdefer s.unlock()\n\tnum_cols := calc_num_cols(s.len)\n\ttotal := num_cols * num_per_column\n\tif total < 1 {\n\t\treturn nil, 0, CollectionIndex{}\n\t}\n\tnum_before = min(total-1, num_before_current)\n\tidx, did_wrap := s.increment_with_wrap_around(current, -num_before)\n\tlast_slice := s.slices[len(s.slices)-1]\n\tlast := CollectionIndex{Slice: len(s.slices) - 1, Pos: len(last_slice) - 1}\n\tif did_wrap {\n\t\tidx = CollectionIndex{}\n\t} else if s.distance(idx, last) < total-1 {\n\t\tif idx, did_wrap = s.increment_with_wrap_around(last, 1-total); did_wrap {\n\t\t\tidx = CollectionIndex{}\n\t\t}\n\t}\n\tfirst_idx = idx\n\tnum_before = s.distance(idx, current)\n\t// fmt.Printf(\"111111 idx: %v current: %v num_before: %d\\n\", idx, current, num_before)\n\tans = make([][]*ResultItem, num_cols)\n\tfor colidx := range len(ans) {\n\t\tcol := make([]*ResultItem, 0, num_per_column)\n\t\tfor len(col) < num_per_column && idx.Slice < len(s.slices) {\n\t\t\tss := s.slices[idx.Slice]\n\t\t\tlimit := min(len(ss), idx.Pos+num_per_column-len(col))\n\t\t\tcol = append(col, ss[idx.Pos:limit]...)\n\t\t\tidx.Pos = limit\n\t\t\tif idx.Pos >= len(ss) {\n\t\t\t\tidx.NextSlice()\n\t\t\t\tif idx.Slice >= len(s.slices) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tans[colidx] = col\n\t}\n\treturn\n}\n"
  },
  {
    "path": "kittens/choose_files/ffmpeg.go",
    "content": "package choose_files\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/hako/durafmt\"\n\t\"github.com/kovidgoyal/imaging/magick\"\n\t\"github.com/kovidgoyal/kitty/tools/icons\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nconst FFMPEG_METADATA_KEY = \"ffmpeg-metadata.json\"\n\ntype ffmpeg_renderer int\n\nvar (\n\tvideo_width            = 480\n\tvideo_fps              = 10\n\tvideo_duration         = 5.0\n\tvideo_encoding_quality = 75\n)\n\nfunc ffmpeg_thumbnail_cmd(path, outpath string) *exec.Cmd {\n\treturn exec.Command(\n\t\t\"ffmpeg\", \"-loglevel\", \"fatal\", \"-y\", \"-an\", \"-sn\", \"-dn\", \"-i\", path, \"-t\", fmt.Sprintf(\"%f\", video_duration),\n\t\t\"-vf\", fmt.Sprintf(\"fps=%d,scale=%d:-1:flags=lanczos\", video_fps, video_width),\n\t\t\"-c:v\", \"libwebp\", \"-lossless\", \"0\", \"-compression_level\", \"0\", \"-q:v\",\n\t\tfmt.Sprintf(\"%d\", video_encoding_quality), \"-loop\", \"0\", \"-f\", \"webp\", outpath,\n\t)\n}\n\nfunc ffmpeg_thumbnail(path, tempath string, wg *sync.WaitGroup) (ans *images.ImageData, err error) {\n\tdefer wg.Done()\n\tcmd := ffmpeg_thumbnail_cmd(path, tempath)\n\tcmd.Stdin = nil\n\tcmd.SysProcAttr = &unix.SysProcAttr{Setsid: true}\n\tvar stderr bytes.Buffer\n\tcmd.Stdout = nil\n\tcmd.Stderr = &stderr\n\tif err = cmd.Run(); err != nil {\n\t\treturn ans, fmt.Errorf(\"failed to use ffmpeg to render video from %s with error: %w and stderr: %s\", path, err, stderr.String())\n\t}\n\tans, err = images.OpenImageFromPath(tempath)\n\treturn\n}\n\ntype FFMpegFormat struct {\n\tStart_time string         `json:\"start_time\"`\n\tDuration   string         `json:\"duration\"`\n\tTags       map[string]any `json:\"tags\"`\n}\n\ntype FFMpegStream struct {\n\tCodec_type string `json:\"codec_type\"`\n\tWidth      int    `json:\"width\"`\n\tHeight     int    `json:\"height\"`\n}\n\ntype FFMpegMetadata struct {\n\tStreams []FFMpegStream `json:\"streams\"`\n\tFormat  FFMpegFormat   `json:\"format\"`\n}\n\nfunc ffmpeg_metadata_cmd(path string) *exec.Cmd {\n\treturn exec.Command(\n\t\t\"ffprobe\", \"-v\", \"quiet\", \"-print_format\", \"json\", \"-show_format\", \"-show_streams\", path,\n\t)\n}\n\nfunc ffmpeg_metadata(path string, wg *sync.WaitGroup) (ans FFMpegMetadata, err error) {\n\tdefer wg.Done()\n\tcmd := ffmpeg_metadata_cmd(path)\n\tcmd.Stdin = nil\n\tcmd.SysProcAttr = &unix.SysProcAttr{Setsid: true}\n\tvar stdout, stderr bytes.Buffer\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\tif err = cmd.Run(); err != nil {\n\t\treturn ans, fmt.Errorf(\"failed to use ffprobe to read metadata from %s with error: %w and stderr: %s\", path, err, stderr.String())\n\t}\n\tif err = json.Unmarshal(stdout.Bytes(), &ans); err != nil {\n\t\treturn ans, fmt.Errorf(\"could not decode JSON response from ffprobe for %s: %w\", path, err)\n\t}\n\treturn\n}\n\nfunc (c ffmpeg_renderer) Render(path string) (m map[string][]byte, mi metadata, img *images.ImageData, err error) {\n\twg := sync.WaitGroup{}\n\ttempfile, err := os.CreateTemp(magick.TempDirInRAMIfPossible(), \"kitty-choose-files-*.webp\")\n\tif err != nil {\n\t\treturn nil, mi, nil, err\n\t}\n\tdefer func() { _ = os.Remove(tempfile.Name()); tempfile.Close() }()\n\tvar metadata FFMpegMetadata\n\tvar metadata_error error\n\twg.Add(1)\n\tgo func() { metadata, metadata_error = ffmpeg_metadata(path, &wg) }()\n\twg.Add(1)\n\tgo func() { img, err = ffmpeg_thumbnail(path, tempfile.Name(), &wg) }()\n\twg.Wait()\n\tif metadata_error != nil {\n\t\treturn nil, mi, nil, metadata_error\n\t}\n\tvar ip ImagePreviewRenderer\n\tif m, mi, img, err = ip.Render(tempfile.Name()); err != nil {\n\t\treturn\n\t}\n\tmi.custom = &metadata\n\n\treturn\n}\n\nfunc (c ffmpeg_renderer) Unmarshall(m map[string]string) (any, error) {\n\tdata, err := os.ReadFile(m[FFMPEG_METADATA_KEY])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ans FFMpegMetadata\n\tif err = json.Unmarshal(data, &ans); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ans, nil\n}\n\nfunc (c ffmpeg_renderer) ShowMetadata(h *Handler, s ShowData) (offset int) {\n\tw := func(text string, center bool) {\n\t\tif s.height > offset {\n\t\t\toffset += h.render_wrapped_text_in_region(text, s.x, s.y+offset, s.width, s.height-offset, center)\n\t\t}\n\t}\n\text := filepath.Ext(s.abspath)\n\ttext := fmt.Sprintf(\"%s: %s\", ext, humanize.Bytes(uint64(s.metadata.Size())))\n\tr := s.custom_metadata.custom.(*FFMpegMetadata)\n\ticon := icons.IconForPath(s.abspath)\n\tvar width, height int\n\tfor _, s := range r.Streams {\n\t\tif s.Width > 0 && s.Height > 0 {\n\t\t\twidth, height = s.Width, s.Height\n\t\t\tbreak\n\t\t}\n\t}\n\ttext += fmt.Sprintf(\" %dx%d\", width, height)\n\tw(icon+\"  \"+text, true)\n\tst := humanize.Time(s.metadata.ModTime())\n\tif d, perr := strconv.ParseFloat(r.Format.Duration, 64); perr == nil {\n\t\tduration := time.Duration(d * float64(time.Second))\n\t\tst += fmt.Sprintf(\", %s\", durafmt.Parse(duration).LimitFirstN(1).String())\n\t}\n\tw(st, true)\n\treturn\n}\n\nfunc (c ffmpeg_renderer) String() string {\n\treturn \"FFMpeg\"\n}\n\nfunc NewFFMpegPreview(\n\tabspath string, metadata fs.FileInfo, opts Settings, WakeupMainThread func() bool,\n) Preview {\n\tc := ffmpeg_renderer(0)\n\tif ans, err := NewImagePreview(abspath, metadata, opts, WakeupMainThread, c); err == nil {\n\t\treturn ans\n\t} else {\n\t\treturn NewErrorPreview(err)\n\t}\n}\n"
  },
  {
    "path": "kittens/choose_files/filters.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype Filter struct {\n\tName, Type, Pattern string\n\tMatch               func(filename string) bool\n}\n\nfunc (f Filter) String() string {\n\treturn fmt.Sprintf(\"%s:%s:%s\", f.Type, f.Pattern, f.Name)\n}\n\nfunc (f Filter) Equal(other Filter) bool {\n\treturn f.Type == other.Type && f.Pattern == other.Pattern\n}\n\nfunc NewFilter(spec string) (*Filter, error) {\n\tparts := strings.SplitN(spec, \":\", 3)\n\tif len(parts) != 3 {\n\t\treturn nil, fmt.Errorf(\"%#v is not a valid filter specifier, must have at least two colons\", spec)\n\t}\n\tans := &Filter{Name: parts[2], Pattern: parts[1], Type: parts[0]}\n\tif _, err := filepath.Match(ans.Pattern, \"test\"); err != nil {\n\t\treturn nil, fmt.Errorf(\"%#v is not a valid glob pattern with error: %w\", ans.Pattern, err)\n\t}\n\tif ans.Pattern != \"*\" && ans.Pattern != \"\" {\n\t\tswitch ans.Type {\n\t\tcase \"glob\":\n\t\t\tans.Match = func(filename string) bool {\n\t\t\t\tm, _ := filepath.Match(ans.Pattern, filename)\n\t\t\t\treturn m\n\t\t\t}\n\t\tcase \"mime\":\n\t\t\tans.Match = func(filename string) bool {\n\t\t\t\tmime := utils.GuessMimeType(filename)\n\t\t\t\tif mime == \"\" {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tm, _ := filepath.Match(ans.Pattern, mime)\n\t\t\t\treturn m\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"%#v is not a valid filter type\", ans.Type)\n\t\t}\n\t}\n\treturn ans, nil\n}\n\nfunc CombinedFilter(filters ...Filter) Filter {\n\tif len(filters) == 0 {\n\t\treturn Filter{}\n\t}\n\tfor _, f := range filters {\n\t\tif f.Match == nil {\n\t\t\treturn f\n\t\t}\n\t}\n\tans := filters[0]\n\tmatchers := utils.Map(func(f Filter) func(filename string) bool { return f.Match }, filters)\n\tans.Match = func(filename string) bool {\n\t\tfor _, m := range matchers {\n\t\t\tif m(filename) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\treturn ans\n}\n"
  },
  {
    "path": "kittens/choose_files/footer.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nconst HOVER_STYLE = \"default fg=red\"\n\ntype single_line_region struct {\n\tx, width, y int\n\tid          string\n\tcallback    func(string) error\n}\n\nfunc (h *Handler) draw_footer() (num_lines int, err error) {\n\tlines := []string{}\n\tscreen_width := h.screen_size.width\n\tsctx := style.Context{AllowEscapeCodes: true}\n\tif h.state.screen == SAVE_FILE {\n\t\tm := h.state.filter_map\n\t\th.state.filter_map = nil\n\t\tdefer func() { h.state.filter_map = m }()\n\t}\n\n\tif len(h.state.filter_map)+len(h.state.selections) > 0 {\n\t\tbuf := strings.Builder{}\n\t\tpos := 0\n\t\tcurrent_style := sctx.SprintFunc(\"italic fg=green intense\")\n\t\tnon_current_style := sctx.SprintFunc(\"dim\")\n\t\tvar crs []single_line_region\n\t\tw := func(presep, text string, sfunc func(...any) string, id string, cb func(string)) {\n\t\t\tsz := len(presep)\n\t\t\tif sz+pos >= screen_width {\n\t\t\t\tlines = append(lines, buf.String())\n\t\t\t\tpos = 0\n\t\t\t\tbuf.Reset()\n\t\t\t} else {\n\t\t\t\tbuf.WriteString(presep)\n\t\t\t\tpos += sz\n\t\t\t}\n\t\t\tsz = wcswidth.Stringwidth(text)\n\t\t\tif sz+pos >= screen_width {\n\t\t\t\tlines = append(lines, buf.String())\n\t\t\t\tpos = 0\n\t\t\t\tbuf.Reset()\n\t\t\t}\n\t\t\tif sfunc != nil {\n\t\t\t\ttext = sfunc(text)\n\t\t\t}\n\t\t\tbuf.WriteString(text)\n\t\t\tif cb != nil {\n\t\t\t\tcrs = append(crs, single_line_region{x: pos, width: sz - 1, y: len(lines), id: id, callback: func(filter string) error {\n\t\t\t\t\tcb(filter)\n\t\t\t\t\th.state.redraw_needed = true\n\t\t\t\t\treturn nil\n\t\t\t\t}})\n\t\t\t}\n\t\t\tpos += sz\n\t\t}\n\t\tflush := func() {\n\t\t\tif s := buf.String(); s != \"\" {\n\t\t\t\tlines = append(lines, s)\n\t\t\t}\n\t\t\tpos = 0\n\t\t\tbuf.Reset()\n\t\t}\n\t\tif len(h.state.filter_map) > 0 {\n\t\t\tw(\"\", \"󰈲  Filter:\", nil, \"\", nil)\n\t\t\tfor _, name := range h.state.filter_names {\n\t\t\t\tvar cb func(string)\n\t\t\t\tif name != h.state.current_filter {\n\t\t\t\t\tcb = func(filter string) { h.set_filter(filter) }\n\t\t\t\t}\n\t\t\t\tw(\"  \", name, utils.IfElse(name == h.state.current_filter, current_style, non_current_style), name, cb)\n\t\t\t}\n\t\t\tflush()\n\t\t}\n\t\tif len(h.state.selections) > 0 {\n\t\t\tbefore := len(lines)\n\t\t\tw(\"\", \"  Selected:\", nil, \"\", nil)\n\t\t\tfor i, s := range h.state.selections {\n\t\t\t\ttext := s\n\t\t\t\tif rel, rerr := filepath.Rel(h.state.CurrentDir(), s); rerr == nil {\n\t\t\t\t\ttext = rel\n\t\t\t\t}\n\t\t\t\tw(\"  \", text, nil, s, func(abspath string) { h.state.ToggleSelection(abspath) })\n\t\t\t\tif len(lines)-before > 2 && len(h.state.selections)-i-1 > 3 {\n\t\t\t\t\tw(\"  \", fmt.Sprintf(\"and %d more…\", len(h.state.selections)-1-i), nil, \"\", nil)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tflush()\n\t\t}\n\t\toffset := h.screen_size.height - len(lines)\n\t\tfor _, cr := range crs {\n\t\t\th.state.mouse_state.AddCellRegion(cr.id, cr.x, cr.y+offset, cr.x+cr.width, cr.y+offset, cr.callback).HoverStyle = HOVER_STYLE\n\t\t}\n\t}\n\tif len(lines) > 0 {\n\t\th.lp.MoveCursorTo(1, h.screen_size.height-len(lines)+1)\n\t\tif h.state.screen == SAVE_FILE {\n\t\t\th.lp.ClearToEndOfScreen()\n\t\t}\n\t\th.lp.QueueWriteString(strings.Join(lines, \"\\r\\n\"))\n\t}\n\treturn len(lines), err\n}\n"
  },
  {
    "path": "kittens/choose_files/graphics.go",
    "content": "package choose_files\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nvar _ = fmt.Print\n\ntype placement struct {\n\tgc             *graphics.GraphicsCommand\n\tx, y, x_offset int\n}\n\nfunc (p placement) equal(o placement) bool {\n\treturn p.x == o.x && p.x_offset == o.x_offset && p.y == o.y\n}\n\ntype GraphicsHandler struct {\n\trunning_in_tmux                     bool\n\timage_id_counter, detection_file_id uint32\n\tfiles_to_delete                     []string\n\tfiles_supported                     atomic.Bool\n\tlast_rendered_image                 struct {\n\t\tp                         *ImagePreview\n\t\twidth, height             int\n\t\timage_width, image_height int\n\t}\n\timage_transmitted                             uint32\n\tcurrent_placement, last_transmitted_placement placement\n}\n\nfunc (self *GraphicsHandler) Cleanup() {\n\tfor _, f := range self.files_to_delete {\n\t\t_ = os.Remove(f)\n\t}\n}\n\nfunc (self *GraphicsHandler) new_graphics_command() *graphics.GraphicsCommand {\n\tgc := graphics.GraphicsCommand{}\n\tif self.running_in_tmux {\n\t\tgc.WrapPrefix = \"\\033Ptmux;\"\n\t\tgc.WrapSuffix = \"\\033\\\\\"\n\t\tgc.EncodeSerializedDataFunc = func(x string) string { return strings.ReplaceAll(x, \"\\033\", \"\\033\\033\") }\n\t}\n\treturn &gc\n}\n\nfunc (self *GraphicsHandler) Initialize(lp *loop.Loop) error {\n\ttmux := tui.TmuxSocketAddress()\n\tif tmux != \"\" && tui.TmuxAllowPassthrough() == nil {\n\t\tself.running_in_tmux = true\n\t}\n\tif !self.running_in_tmux {\n\t\tg := func(t graphics.GRT_t, payload string) uint32 {\n\t\t\tself.image_id_counter++\n\t\t\tg1 := self.new_graphics_command()\n\t\t\tg1.SetTransmission(t).SetAction(graphics.GRT_action_query).SetImageId(self.image_id_counter).SetDataWidth(1).SetDataHeight(1).SetFormat(\n\t\t\t\tgraphics.GRT_format_rgb).SetDataSize(uint64(len(payload)))\n\t\t\t_ = g1.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(payload))\n\t\t\treturn self.image_id_counter\n\t\t}\n\t\ttf, err := images.CreateTempInRAM()\n\t\tif err == nil {\n\t\t\tif _, err = tf.Write([]byte{1, 2, 3}); err == nil {\n\t\t\t\tself.detection_file_id = g(graphics.GRT_transmission_tempfile, tf.Name())\n\t\t\t\tself.files_to_delete = append(self.files_to_delete, tf.Name())\n\t\t\t}\n\t\t\ttf.Close()\n\t\t}\n\n\t}\n\tself.image_id_counter++\n\treturn nil\n}\n\nfunc (self *GraphicsHandler) free_image_from_terminal(lp *loop.Loop) {\n\tif self.image_transmitted > 0 {\n\t\tself.new_graphics_command().SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_free_by_id).SetImageId(self.image_transmitted).WriteWithPayloadToLoop(lp, nil)\n\t\tself.image_transmitted = 0\n\t}\n}\n\nfunc (self *GraphicsHandler) Finalize(lp *loop.Loop) {\n\tself.free_image_from_terminal(lp)\n}\n\nfunc (self *GraphicsHandler) ClearPlacements(lp *loop.Loop) {\n\tself.current_placement.gc = nil\n}\n\nfunc (self *GraphicsHandler) ApplyPlacements(lp *loop.Loop) {\n\tif self.current_placement.gc == nil {\n\t\tg := self.new_graphics_command()\n\t\tg.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_delete_by_id).SetImageId(self.image_transmitted)\n\t\t_ = g.WriteWithPayloadToLoop(lp, nil)\n\t\tself.last_transmitted_placement.gc = nil\n\t} else {\n\t\tif self.last_transmitted_placement.gc == nil || !self.current_placement.equal(self.last_transmitted_placement) {\n\t\t\tlp.MoveCursorTo(self.current_placement.x, self.current_placement.y)\n\t\t\t_ = self.current_placement.gc.WriteWithPayloadToLoop(lp, nil)\n\t\t\tself.last_transmitted_placement = self.current_placement\n\t\t}\n\t}\n}\n\nfunc (self *GraphicsHandler) HandleGraphicsCommand(gc *graphics.GraphicsCommand) error {\n\tswitch gc.ImageId() {\n\tcase self.detection_file_id:\n\t\tif gc.ResponseMessage() == \"OK\" {\n\t\t\tself.files_supported.Store(true)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (self *GraphicsHandler) cache_resized_image(cdir, cache_key string, img *images.ImageData) (m *images.SerializableImageMetadata, cached_data map[string]string, err error) {\n\ts, frames := img.Serialize()\n\tsd, err := json.Marshal(s)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tpath := filepath.Join(cdir, fmt.Sprintf(\"rsz-%s-metadata.json\", cache_key))\n\tif err = os.WriteFile(path, sd, 0o600); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to write resized frame metadata to cache: %w\", err)\n\t}\n\tcached_data = make(map[string]string, len(frames)+1)\n\tcached_data[IMAGE_METADATA_KEY] = path\n\tfor i, f := range frames {\n\t\tpath := filepath.Join(cdir, fmt.Sprintf(\"rsz-%s-%d\", cache_key, i))\n\t\tkey := IMAGE_DATA_PREFIX + strconv.Itoa(i)\n\t\tif err = os.WriteFile(path, f, 0o600); err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to write resized frame %d data to cache: %w\", i, err)\n\t\t}\n\t\tcached_data[key] = path\n\t}\n\tm = &s\n\treturn\n}\n\nfunc (self *GraphicsHandler) cached_resized_image(cdir, cache_key string) (m *images.SerializableImageMetadata, cached_data map[string]string) {\n\tpath := filepath.Join(cdir, fmt.Sprintf(\"rsz-%s-metadata.json\", cache_key))\n\tb, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar s images.SerializableImageMetadata\n\tif err = json.Unmarshal(b, &s); err != nil {\n\t\treturn\n\t}\n\tm = &s\n\tcached_data = make(map[string]string, len(s.Frames)+1)\n\tcached_data[IMAGE_METADATA_KEY] = path\n\tfor i := range len(s.Frames) {\n\t\tpath := filepath.Join(cdir, fmt.Sprintf(\"rsz-%s-%d\", cache_key, i))\n\t\tkey := IMAGE_DATA_PREFIX + strconv.Itoa(i)\n\t\tcached_data[key] = path\n\t}\n\treturn\n}\n\nfunc transmit_by_escape_code(lp *loop.Loop, frame []byte, gc *graphics.GraphicsCommand) {\n\tatomic := lp.IsAtomicUpdateActive()\n\tlp.EndAtomicUpdate()\n\tgc.SetTransmission(graphics.GRT_transmission_direct)\n\t_ = gc.WriteWithPayloadToLoop(lp, frame)\n\tif atomic {\n\t\tlp.StartAtomicUpdate()\n\t}\n}\n\nfunc transmit_by_file(lp *loop.Loop, frame_path []byte, gc *graphics.GraphicsCommand) {\n\tgc.SetTransmission(graphics.GRT_transmission_file)\n\t_ = gc.WriteWithPayloadToLoop(lp, frame_path)\n}\n\nfunc (self *GraphicsHandler) transmit(lp *loop.Loop, img *images.ImageData, m *images.SerializableImageMetadata, cached_data map[string]string) {\n\tif m == nil {\n\t\ts := img.SerializeOnlyMetadata()\n\t\tm = &s\n\t}\n\tself.image_transmitted = self.image_id_counter\n\tself.last_transmitted_placement.gc = nil\n\tself.last_rendered_image.image_width = m.Width\n\tself.last_rendered_image.image_height = m.Height\n\tis_animated := len(m.Frames) > 0\n\tframe_control_cmd := self.new_graphics_command()\n\tframe_control_cmd.SetAction(graphics.GRT_action_animate).SetImageId(self.image_transmitted)\n\tfor frame_num, frame := range m.Frames {\n\t\tgc := self.new_graphics_command()\n\t\tgc.SetImageId(self.image_transmitted)\n\t\tgc.SetDataWidth(uint64(frame.Width)).SetDataHeight(uint64(frame.Height))\n\t\tgc.SetFormat(utils.IfElse(frame.Is_opaque, graphics.GRT_format_rgb, graphics.GRT_format_rgba))\n\t\tswitch frame_num {\n\t\tcase 0:\n\t\t\tgc.SetAction(graphics.GRT_action_transmit)\n\t\t\tgc.SetCursorMovement(graphics.GRT_cursor_static)\n\t\tdefault:\n\t\t\tgc.SetAction(graphics.GRT_action_frame)\n\t\t\tgc.SetGap(int32(frame.Delay_ms))\n\t\t\tif frame.Replace {\n\t\t\t\tgc.SetCompositionMode(graphics.Overwrite)\n\t\t\t}\n\t\t\tif frame.Compose_onto > 0 {\n\t\t\t\tgc.SetOverlaidFrame(uint64(frame.Compose_onto))\n\t\t\t}\n\t\t\tgc.SetLeftEdge(uint64(frame.Left)).SetTopEdge(uint64(frame.Top))\n\t\t}\n\t\tif cached_data == nil {\n\t\t\t_, _, _, data := img.Frames[frame_num].Data()\n\t\t\ttransmit_by_escape_code(lp, data, gc)\n\t\t} else {\n\t\t\tpath := cached_data[IMAGE_DATA_PREFIX+strconv.Itoa(frame_num)]\n\t\t\ttransmit_by_file(lp, utils.UnsafeStringToBytes(path), gc)\n\t\t}\n\t\tif is_animated {\n\t\t\tswitch frame_num {\n\t\t\tcase 0:\n\t\t\t\t// set gap for the first frame and number of loops for the animation\n\t\t\t\tc := frame_control_cmd\n\t\t\t\tc.SetTargetFrame(uint64(frame.Number))\n\t\t\t\tc.SetGap(int32(frame.Delay_ms))\n\t\t\t\tc.SetNumberOfLoops(1)\n\t\t\t\t_ = c.WriteWithPayloadToLoop(lp, nil)\n\t\t\tcase 1:\n\t\t\t\tc := frame_control_cmd\n\t\t\t\tc.SetAnimationControl(2) // set animation to loading mode\n\t\t\t\t_ = c.WriteWithPayloadToLoop(lp, nil)\n\t\t\t}\n\t\t}\n\t}\n\tif is_animated {\n\t\tc := frame_control_cmd\n\t\tc.SetAnimationControl(3) // set animation to normal mode\n\t\t_ = c.WriteWithPayloadToLoop(lp, nil)\n\t}\n\n}\n\nfunc (self *GraphicsHandler) place_image(x, y, px_width, y_offset int, sz ScreenSize) {\n\tgc := self.new_graphics_command()\n\tgc.SetAction(graphics.GRT_action_display).SetImageId(self.image_transmitted).SetPlacementId(1).SetCursorMovement(graphics.GRT_cursor_static)\n\tif extra := px_width - self.last_rendered_image.image_width; extra > 1 {\n\t\textra /= 2\n\t\tx += extra / sz.cell_width\n\t\tself.current_placement.x_offset = extra % sz.cell_width\n\t\tgc.SetXOffset(uint64(self.current_placement.x_offset))\n\t}\n\tgc.SetYOffset(uint64(y_offset))\n\tself.current_placement.x, self.current_placement.y = x, y\n\tself.current_placement.gc = gc\n}\n\nfunc (self *GraphicsHandler) RenderImagePreview(h *Handler, p *ImagePreview, x, y, width, height int) {\n\tsz := h.screen_size\n\tpx_width, px_height := width*sz.cell_width, height*sz.cell_height\n\ty_offset := sz.cell_height / 2\n\tpx_height -= y_offset\n\tvar err error\n\tdefer func() {\n\t\tself.last_rendered_image.p = p\n\t\tself.last_rendered_image.width, self.last_rendered_image.height = width, height\n\t\tif err != nil {\n\t\t\tNewErrorPreview(fmt.Errorf(\"Failed to render image: %w\", err)).Render(h, x, y, width, height)\n\t\t} else if self.image_transmitted > 0 {\n\t\t\tself.place_image(x, y, px_width, y_offset, sz)\n\t\t}\n\t}()\n\tif self.last_rendered_image.p == p && self.last_rendered_image.width == width && self.last_rendered_image.height == height {\n\t\treturn\n\t}\n\tfiles_supported := self.files_supported.Load()\n\n\tif p.custom_metadata.image.Width <= px_width && p.custom_metadata.image.Height <= px_height {\n\t\tif files_supported {\n\t\t\tself.transmit(h.lp, nil, p.custom_metadata.image, p.cached_data)\n\t\t} else {\n\t\t\tif err = p.ensure_source_image(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.transmit(h.lp, p.source_img, p.custom_metadata.image, nil)\n\t\t}\n\t\treturn\n\t}\n\tcache_key := fmt.Sprintf(\"%d-%d-%p\", width, height, p)\n\timg_metadata, cached_data := self.cached_resized_image(p.disk_cache.ResultsDir(), cache_key)\n\tvar img *images.ImageData\n\tif len(cached_data) == 0 {\n\t\tif err = p.ensure_source_image(); err != nil {\n\t\t\treturn\n\t\t}\n\t\timg = p.source_img\n\t\tfinal_width, final_height := images.FitImage(img.Width, img.Height, px_width, px_height)\n\t\tif final_width != img.Width || final_height != img.Height {\n\t\t\tx_frac, y_frac := float64(final_width)/float64(img.Width), float64(final_height)/float64(img.Height)\n\t\t\timg = img.Resize(x_frac, y_frac)\n\t\t}\n\t\tif img_metadata, cached_data, err = self.cache_resized_image(p.disk_cache.ResultsDir(), cache_key, img); err != nil {\n\t\t\terr = fmt.Errorf(\"failed to cache resized image: %w\", err)\n\t\t\treturn\n\t\t}\n\t}\n\tif files_supported {\n\t\tself.transmit(h.lp, img, img_metadata, cached_data)\n\t} else {\n\t\tif img == nil {\n\t\t\tif img, err = load_image(cached_data); err != nil {\n\t\t\t\terr = fmt.Errorf(\"failed to load resized image from cache: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tself.transmit(h.lp, img, nil, nil)\n\t}\n}\n"
  },
  {
    "path": "kittens/choose_files/image_preview.go",
    "content": "package choose_files\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/hako/durafmt\"\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/disk_cache\"\n\t\"github.com/kovidgoyal/kitty/tools/icons\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nconst IMAGE_METADATA_KEY = \"image-metadata.json\"\nconst IMAGE_DATA_PREFIX = \"image-data-\"\n\nvar dc_size atomic.Int64\nvar _ = fmt.Print\n\nvar preview_cache = sync.OnceValues(func() (*disk_cache.DiskCache, error) {\n\tcdir := utils.CacheDir()\n\tcdir = filepath.Join(cdir, \"choose-files\")\n\treturn disk_cache.NewDiskCache(cdir, dc_size.Load())\n})\n\ntype ShowData struct {\n\tabspath             string\n\tmetadata            fs.FileInfo\n\tx, y, width, height int\n\tcached_data         map[string]string\n\tcustom_metadata     metadata\n}\n\ntype PreviewRenderer interface {\n\tUnmarshall(map[string]string) (any, error)\n\tRender(string) (map[string][]byte, metadata, *images.ImageData, error)\n\tShowMetadata(h *Handler, s ShowData) int\n\tString() string\n}\n\ntype render_data struct {\n\tcached_data map[string]string\n\timg         *images.ImageData\n\tmetadata    metadata\n\terr         error\n}\n\ntype metadata struct {\n\timage  *images.SerializableImageMetadata\n\tcustom any\n}\n\ntype ImagePreview struct {\n\tabspath               string\n\tmetadata              fs.FileInfo\n\tdisk_cache            *disk_cache.DiskCache\n\tcached_data           map[string]string\n\trender_err            Preview\n\trender_channel        chan render_data\n\tready                 atomic.Bool\n\tsource_img            *images.ImageData\n\tcustom_metadata       metadata\n\trenderer              PreviewRenderer\n\tfile_metadata_preview Preview\n\tWakeupMainThread      func() bool\n}\n\nfunc (p *ImagePreview) String() string {\n\treturn fmt.Sprintf(\"ImagePreview{%#v, ready: %v, renderer: %s}\", p.abspath, p.ready.Load(), p.renderer.String())\n}\nfunc (p *ImagePreview) IsValidForColorScheme(bool) bool { return true }\nfunc (p *ImagePreview) IsReady() bool                   { return p.ready.Load() || p.render_channel == nil }\n\nfunc (p *ImagePreview) Unload() {\n\tp.source_img = nil\n}\n\nfunc load_image(cached_data map[string]string) (img *images.ImageData, err error) {\n\tfp := cached_data[IMAGE_METADATA_KEY]\n\tif fp == \"\" {\n\t\treturn nil, fmt.Errorf(\"missing cached image metadata\")\n\t}\n\tb, err := os.ReadFile(fp)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read cached image metadata: %w\", err)\n\t}\n\tvar m images.SerializableImageMetadata\n\tif err = json.Unmarshal(b, &m); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode cached image metadata: %w\", err)\n\t}\n\tframes := make([][]byte, len(m.Frames))\n\tfor i := range m.Frames {\n\t\tpath := cached_data[IMAGE_DATA_PREFIX+strconv.Itoa(i)]\n\t\tif path == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"missing cached data for frame: %d\", i)\n\t\t}\n\t\td, e := os.ReadFile(path)\n\t\tif e != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to read cached image frame %d data: %w\", i, e)\n\t\t}\n\t\tm.Frames[i].Size = len(d)\n\t\tframes[i] = d\n\t}\n\treturn images.ImageFromSerialized(m, frames)\n}\n\nfunc (p *ImagePreview) ensure_source_image() (err error) {\n\tif p.source_img != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tp.render_err = NewErrorPreview(err)\n\t\t}\n\t}()\n\tp.source_img, err = load_image(p.cached_data)\n\treturn\n}\n\nfunc (p *ImagePreview) render_image(h *Handler, x, y, width, height int) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\th.err_chan <- parallel.Format_stacktrace_on_panic(r, 1)\n\t\t\tp.WakeupMainThread()\n\t\t}\n\t}()\n\n\toffset := p.renderer.ShowMetadata(h, ShowData{\n\t\tabspath: p.abspath, metadata: p.metadata, x: x, y: y, width: width, height: height, cached_data: p.cached_data,\n\t\tcustom_metadata: p.custom_metadata,\n\t})\n\tif p.custom_metadata.image != nil {\n\t\th.graphics_handler.RenderImagePreview(h, p, x, y+offset, width, height-offset)\n\t}\n}\n\nfunc (p *ImagePreview) Render(h *Handler, x, y, width, height int) {\n\tif p.render_channel == nil {\n\t\tif p.render_err == nil {\n\t\t\tp.render_image(h, x, y, width, height)\n\t\t} else {\n\t\t\tp.render_err.Render(h, x, y, width, height)\n\t\t}\n\t\treturn\n\t}\n\tselect {\n\tcase hd := <-p.render_channel:\n\t\tp.render_channel = nil\n\t\tp.cached_data = hd.cached_data\n\t\tp.source_img = hd.img\n\t\tp.custom_metadata = hd.metadata\n\t\tif hd.err != nil {\n\t\t\tp.render_err = NewFileMetadataPreviewWithError(p.abspath, p.metadata, fmt.Errorf(\"Failed to render the preview with error: %w\", hd.err))\n\t\t}\n\t\tp.Render(h, x, y, width, height)\n\t\treturn\n\tdefault:\n\t}\n\tif p.file_metadata_preview == nil {\n\t\tp.file_metadata_preview = NewFileMetadataPreview(p.abspath, p.metadata)\n\t\tm := p.file_metadata_preview.(*MessagePreview)\n\t\tm.trailers = append(m.trailers, \"\", \"Rendering image preview, please wait…\")\n\t}\n\tp.file_metadata_preview.Render(h, x, y, width, height)\n}\n\nfunc (p *ImagePreview) start_rendering() {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tp.render_channel <- render_data{err: parallel.Format_stacktrace_on_panic(r, 1)}\n\t\t}\n\t\tclose(p.render_channel)\n\t\tp.ready.Store(true)\n\t\tp.WakeupMainThread()\n\t}()\n\tkey, ans, err := p.disk_cache.GetPath(p.abspath)\n\tif err != nil {\n\t\tp.render_channel <- render_data{err: err}\n\t\treturn\n\t}\n\tif len(ans) > 0 {\n\t\tmi := metadata{}\n\t\tif mi.custom, err = p.renderer.Unmarshall(ans); err == nil {\n\t\t\tif d := ans[IMAGE_METADATA_KEY]; d != \"\" {\n\t\t\t\tif b, err := os.ReadFile(d); err == nil {\n\t\t\t\t\tvar m images.SerializableImageMetadata\n\t\t\t\t\tif err = json.Unmarshal(b, &m); err == nil {\n\t\t\t\t\t\tmi.image = &m\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif mi.custom != nil || mi.image != nil {\n\t\t\tp.render_channel <- render_data{cached_data: ans, metadata: mi}\n\t\t\treturn\n\t\t}\n\t}\n\trdata, metadata, img, err := p.renderer.Render(p.abspath)\n\tif err != nil {\n\t\tp.render_channel <- render_data{err: err}\n\t} else {\n\t\tans, err = p.disk_cache.AddPath(p.abspath, key, rdata)\n\t\tif err == nil {\n\t\t\tp.render_channel <- render_data{cached_data: ans, metadata: metadata, img: img}\n\t\t} else {\n\t\t\tp.render_channel <- render_data{err: err}\n\t\t}\n\t}\n}\n\ntype ImagePreviewRenderer uint\n\nfunc (p ImagePreviewRenderer) String() string { return \"ImagePreviewRenderer\" }\n\nfunc (p ImagePreviewRenderer) Render(abspath string) (ans map[string][]byte, m metadata, img *images.ImageData, err error) {\n\tif img, err = images.OpenImageFromPath(abspath); err != nil {\n\t\treturn nil, metadata{}, nil, err\n\t}\n\tim, data := img.Serialize()\n\tans = make(map[string][]byte, len(data)+1)\n\tsm, err := json.Marshal(im)\n\tif err != nil {\n\t\treturn nil, metadata{}, nil, err\n\t}\n\tans[IMAGE_METADATA_KEY] = sm\n\tm = metadata{image: &im}\n\tfor i, d := range data {\n\t\tkey := IMAGE_DATA_PREFIX + strconv.Itoa(i)\n\t\tans[key] = d\n\t}\n\treturn\n}\n\nfunc (p ImagePreviewRenderer) Unmarshall(map[string]string) (any, error) { return nil, nil }\n\nfunc (p ImagePreviewRenderer) ShowMetadata(h *Handler, s ShowData) int {\n\toffset := 0\n\tif m := s.custom_metadata.image; m != nil {\n\t\ttext := fmt.Sprintf(\"%s: %dx%d %s\", m.Format_uppercase, m.Width, m.Height, humanize.Bytes(uint64(s.metadata.Size())))\n\t\ticon := icons.IconForPath(\"/a.gif\")\n\t\ttext = icon + \"  \" + text\n\t\toffset += h.render_wrapped_text_in_region(text, s.x, s.y, s.width, s.height, true)\n\t}\n\tst := humanize.Time(s.metadata.ModTime())\n\tif s.custom_metadata.image != nil && len(s.custom_metadata.image.Frames) > 0 {\n\t\tvar d time.Duration\n\t\tfor _, f := range s.custom_metadata.image.Frames {\n\t\t\tif f.Delay_ms > 0 {\n\t\t\t\td += time.Duration(time.Duration(f.Delay_ms) * time.Millisecond)\n\t\t\t}\n\t\t}\n\t\tif d > 0 {\n\t\t\tst += fmt.Sprintf(\", %s\", durafmt.Parse(d).LimitFirstN(1).String())\n\t\t}\n\t}\n\toffset += h.render_wrapped_text_in_region(st, s.x, s.y+offset, s.width, s.height-offset, true)\n\treturn offset\n}\n\nfunc NewImagePreview(\n\tabspath string, metadata fs.FileInfo, opts Settings, WakeupMainThread func() bool, r PreviewRenderer,\n) (Preview, error) {\n\tdc_size.Store(opts.DiskCacheSize())\n\tans := &ImagePreview{\n\t\tabspath: abspath, metadata: metadata, render_channel: make(chan render_data, 1),\n\t\tWakeupMainThread: WakeupMainThread, renderer: r,\n\t}\n\tif dc, err := preview_cache(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\tans.disk_cache = dc\n\t}\n\tgo ans.start_rendering()\n\treturn ans, nil\n}\n"
  },
  {
    "path": "kittens/choose_files/main.go",
    "content": "package choose_files\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/ignorefiles\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/readline\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\t\"golang.org/x/text/message\"\n)\n\nvar _ = fmt.Print\nvar debugprintln = tty.DebugPrintln\n\ntype Screen int\n\nconst (\n\tNORMAL Screen = iota\n\tSAVE_FILE\n)\n\ntype Mode int\n\nconst (\n\tSELECT_SINGLE_FILE Mode = iota\n\tSELECT_MULTIPLE_FILES\n\tSELECT_SAVE_FILE\n\tSELECT_SAVE_FILES\n\tSELECT_SAVE_DIR\n\tSELECT_SINGLE_DIR\n\tSELECT_MULTIPLE_DIRS\n)\n\nfunc (m Mode) CanSelectNonExistent() bool {\n\tswitch m {\n\tcase SELECT_SAVE_FILE, SELECT_SAVE_DIR, SELECT_SAVE_FILES:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (m Mode) AllowsMultipleSelection() bool {\n\tswitch m {\n\tcase SELECT_MULTIPLE_FILES, SELECT_MULTIPLE_DIRS, SELECT_SAVE_FILES:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (m Mode) OnlyDirs() bool {\n\tswitch m {\n\tcase SELECT_SINGLE_DIR, SELECT_MULTIPLE_DIRS, SELECT_SAVE_DIR:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (m Mode) SelectFiles() bool {\n\tswitch m {\n\tcase SELECT_SINGLE_FILE, SELECT_MULTIPLE_FILES, SELECT_SAVE_FILE, SELECT_SAVE_FILES:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (m Mode) WindowTitle() string {\n\tswitch m {\n\tcase SELECT_SINGLE_FILE:\n\t\treturn \"Choose an existing file\"\n\tcase SELECT_MULTIPLE_FILES:\n\t\treturn \"Choose one or more existing files\"\n\tcase SELECT_SAVE_FILE:\n\t\treturn \"Choose a file to save\"\n\tcase SELECT_SAVE_DIR:\n\t\treturn \"Choose a directory to save\"\n\tcase SELECT_SINGLE_DIR:\n\t\treturn \"Choose an existing directory\"\n\tcase SELECT_MULTIPLE_DIRS:\n\t\treturn \"Choose one or more directories\"\n\tcase SELECT_SAVE_FILES:\n\t\treturn \"Choose files to save\"\n\t}\n\treturn \"\"\n}\n\ntype render_state struct {\n\tnum_matches, num_of_slots, num_before, num_per_column, num_columns, num_shown, preview_width int\n\tfirst_idx                                                                                    CollectionIndex\n}\n\ntype State struct {\n\tbase_dir                            string\n\tcurrent_dir                         string\n\tmultiselect                         bool\n\tsearch_text                         string\n\tmode                                Mode\n\tsuggested_save_file_name            string\n\tsuggested_save_file_path            string\n\twindow_title                        string\n\tscreen                              Screen\n\tcurrent_filter                      string\n\tfilter_map                          map[string]Filter\n\tfilter_names                        []string\n\tshow_hidden                         bool\n\tshow_preview                        bool\n\trespect_ignores                     bool\n\tsort_by_last_modified               bool\n\tglobal_ignores                      ignorefiles.IgnoreFile\n\tkeyboard_shortcuts                  []*config.KeyAction\n\tdisplay_title                       bool\n\tpygments_style, dark_pygments_style string\n\tsyntax_aliases                      map[string]string\n\tmax_disk_cache_size                 int64\n\n\tselections    []string\n\tcurrent_idx   CollectionIndex\n\tlast_render   render_state\n\tmouse_state   tui.MouseState\n\tredraw_needed bool\n}\n\nfunc (s State) DiskCacheSize() int64                  { return s.max_disk_cache_size }\nfunc (s State) HighlightStyles() (string, string)     { return s.pygments_style, s.dark_pygments_style }\nfunc (s State) SyntaxAliases() map[string]string      { return s.syntax_aliases }\nfunc (s State) DisplayTitle() bool                    { return s.display_title }\nfunc (s State) ShowHidden() bool                      { return s.show_hidden }\nfunc (s State) ShowPreview() bool                     { return s.show_preview }\nfunc (s State) RespectIgnores() bool                  { return s.respect_ignores }\nfunc (s State) SortByLastModified() bool              { return s.sort_by_last_modified }\nfunc (s State) GlobalIgnores() ignorefiles.IgnoreFile { return s.global_ignores }\nfunc (s State) BaseDir() string                       { return utils.IfElse(s.base_dir == \"\", default_cwd, s.base_dir) }\nfunc (s State) Filter() Filter                        { return s.filter_map[s.current_filter] }\nfunc (s State) Multiselect() bool                     { return s.multiselect }\nfunc (s State) String() string                        { return utils.Repr(s) }\nfunc (s State) SearchText() string                    { return s.search_text }\nfunc (s State) OnlyDirs() bool                        { return s.mode.OnlyDirs() }\nfunc (s *State) SetSearchText(val string) {\n\tif s.search_text != val {\n\t\ts.search_text = val\n\t\ts.current_idx = CollectionIndex{}\n\t}\n}\nfunc (s *State) SetCurrentDir(val string) {\n\tif q, err := filepath.Abs(val); err == nil {\n\t\tval = q\n\t}\n\tif s.CurrentDir() != val {\n\t\ts.search_text = \"\"\n\t\ts.current_idx = CollectionIndex{}\n\t\ts.current_dir = val\n\t}\n}\nfunc (s State) CurrentIndex() CollectionIndex        { return s.current_idx }\nfunc (s *State) SetCurrentIndex(val CollectionIndex) { s.current_idx = val }\nfunc (s State) CurrentDir() string {\n\treturn utils.IfElse(s.current_dir == \"\", s.BaseDir(), s.current_dir)\n}\nfunc (s State) WindowTitle() string {\n\tif s.window_title == \"\" {\n\t\treturn s.mode.WindowTitle()\n\t}\n\treturn s.window_title\n}\n\nfunc (s *State) AddSelection(abspath string) bool {\n\tif !slices.Contains(s.selections, abspath) {\n\t\ts.selections = append(s.selections, abspath)\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (s *State) ToggleSelection(abspath string) (added bool) {\n\tbefore := len(s.selections)\n\ts.selections = slices.DeleteFunc(s.selections, func(x string) bool { return x == abspath })\n\tif len(s.selections) == before {\n\t\ts.selections = append(s.selections, abspath)\n\t\tadded = true\n\t}\n\treturn\n}\n\nfunc (s *State) IsSelected(x *ResultItem) bool {\n\tif len(s.selections) == 0 {\n\t\treturn false\n\t}\n\tq := filepath.Join(s.CurrentDir(), x.text)\n\treturn slices.Contains(s.selections, q)\n}\n\ntype ScreenSize struct {\n\twidth, height, cell_width, cell_height, width_px, height_px int\n}\n\ntype Handler struct {\n\tstate                              State\n\tscreen_size                        ScreenSize\n\tresult_manager                     *ResultManager\n\tlp                                 *loop.Loop\n\trl                                 *readline.Readline\n\terr_chan                           chan error\n\tshortcut_tracker                   config.ShortcutTracker\n\tmsg_printer                        *message.Printer\n\tspinner                            *tui.Spinner\n\tpreview_manager                    *PreviewManager\n\tlast_rendered_preview              Preview\n\tlast_rendered_preview_abspath      string\n\tprev_preview_for_smooth_transition Preview\n\tgraphics_handler                   GraphicsHandler\n}\n\nfunc (self *Handler) on_escape_code(etype loop.EscapeCodeType, payload []byte) error {\n\tswitch etype {\n\tcase loop.APC:\n\t\tgc := graphics.GraphicsCommandFromAPC(payload)\n\t\tif gc != nil {\n\t\t\treturn self.graphics_handler.HandleGraphicsCommand(gc)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (h *Handler) draw_screen() (err error) {\n\th.state.redraw_needed = false\n\th.lp.StartAtomicUpdate()\n\tdefer func() {\n\t\th.state.mouse_state.UpdateHoveredIds()\n\t\th.state.mouse_state.ApplyHoverStyles(h.lp)\n\t\th.graphics_handler.ApplyPlacements(h.lp)\n\t\tif h.state.screen == NORMAL { // so that the cursor ends up in the right place\n\t\t\th.lp.MoveCursorTo(1, 1)\n\t\t\tif h.state.DisplayTitle() {\n\t\t\t\th.lp.Println(h.state.WindowTitle())\n\t\t\t\th.draw_search_bar(1)\n\t\t\t} else {\n\t\t\t\th.draw_search_bar(0)\n\t\t\t}\n\t\t}\n\t\th.lp.EndAtomicUpdate()\n\t}()\n\th.lp.ClearScreenButNotGraphics()\n\th.graphics_handler.ClearPlacements(h.lp)\n\th.state.mouse_state.ClearCellRegions()\n\tswitch h.state.screen {\n\tcase NORMAL:\n\t\tmatches, is_complete := h.get_results()\n\t\th.lp.SetWindowTitle(h.state.WindowTitle())\n\t\ty := SEARCH_BAR_HEIGHT + utils.IfElse(h.state.DisplayTitle(), 1, 0)\n\t\tfooter_height, err := h.draw_footer()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ty += h.draw_results(y, footer_height, matches, !is_complete)\n\tcase SAVE_FILE:\n\t\terr = h.draw_save_file_name_screen()\n\t}\n\treturn\n}\n\ntype previewer struct {\n\tcmdline      []string\n\tpattern      string\n\tuse_mimetype bool\n}\n\nfunc (p previewer) matches(fname, mt string) bool {\n\tq := utils.IfElse(p.use_mimetype, mt, fname)\n\tmatched, err := filepath.Match(p.pattern, q)\n\treturn matched && err == nil\n}\n\nvar previewers []previewer\n\nfunc load_previewers(cfg *Config) (err error) {\n\tfor _, spec := range cfg.Previewer {\n\t\tparts, err := shlex.Split(spec)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid previewer specification: %#v with error: %w\", spec, err)\n\t\t}\n\t\tif len(parts) < 2 {\n\t\t\treturn fmt.Errorf(\"invalid previewer specification: %#v with error: no command specified\", spec)\n\t\t}\n\t\tprefix, pattern, found := strings.Cut(parts[0], \":\")\n\t\tif !found {\n\t\t\treturn fmt.Errorf(\"invalid previewer specification: %#v with error: no prefix in pattern specification\", spec)\n\t\t}\n\t\tpreviewers = append(previewers, previewer{parts[1:], pattern, prefix == \"mime\"})\n\t}\n\treturn\n}\n\nfunc load_config(opts *Options) (ans *Config, err error) {\n\tans = NewConfig()\n\tp := config.ConfigParser{LineHandler: ans.Parse}\n\terr = p.LoadConfig(\"choose-files.conf\", opts.Config, opts.Override)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tans.KeyboardShortcuts = config.ResolveShortcuts(ans.KeyboardShortcuts)\n\tparts, err := shlex.Split(ans.Video_preview)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, x := range parts {\n\t\tk, v, found := strings.Cut(x, \"=\")\n\t\tif !found {\n\t\t\treturn nil, fmt.Errorf(\"invalid value %s in video_preview\", x)\n\t\t}\n\t\tvar i uint64\n\t\tswitch k {\n\t\tcase \"duration\":\n\t\t\tif video_duration, err = strconv.ParseFloat(v, 64); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid %s in video_preview: %s\", k, v)\n\t\t\t}\n\t\tcase \"fps\":\n\t\t\tif i, err = strconv.ParseUint(v, 10, 0); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid %s in video_preview: %s\", k, v)\n\t\t\t}\n\t\t\tvideo_fps = int(i)\n\t\tcase \"width\":\n\t\t\tif i, err = strconv.ParseUint(v, 10, 0); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid %s in video_preview: %s\", k, v)\n\t\t\t}\n\t\t\tvideo_width = int(i)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized key in video_preview: %s\", k)\n\t\t}\n\t}\n\treturn ans, nil\n}\n\nfunc (h *Handler) init_sizes(new_size loop.ScreenSize) {\n\th.screen_size.width = int(new_size.WidthCells)\n\th.screen_size.height = int(new_size.HeightCells)\n\th.screen_size.cell_width = int(new_size.CellWidth)\n\th.screen_size.cell_height = int(new_size.CellHeight)\n\th.screen_size.width_px = int(new_size.WidthPx)\n\th.screen_size.height_px = int(new_size.HeightPx)\n\th.rl.ClearCachedScreenSize()\n}\n\nfunc (h *Handler) OnInitialize() (ans string, err error) {\n\tif sz, err := h.lp.ScreenSize(); err != nil {\n\t\treturn \"\", err\n\t} else {\n\t\th.init_sizes(sz)\n\t}\n\th.lp.AllowLineWrapping(false)\n\th.lp.SetCursorShape(loop.BAR_CURSOR, true)\n\th.lp.StartBracketedPaste()\n\tif h.state.suggested_save_file_path != \"\" {\n\t\tswitch h.state.mode {\n\t\tcase SELECT_SAVE_FILE, SELECT_SAVE_DIR:\n\t\t\tif s, err := os.Stat(h.state.suggested_save_file_path); err == nil {\n\t\t\t\tif (s.IsDir() && h.state.mode != SELECT_SAVE_FILE) || (!s.IsDir() && h.state.mode == SELECT_SAVE_FILE) {\n\t\t\t\t\th.state.SetCurrentDir(filepath.Dir(h.state.suggested_save_file_path))\n\t\t\t\t\th.state.SetSearchText(filepath.Base(h.state.suggested_save_file_name))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\terr = h.graphics_handler.Initialize(h.lp)\n\th.result_manager.set_root_dir()\n\th.draw_screen()\n\th.lp.SendOverlayReady()\n\treturn\n}\n\nfunc (h *Handler) current_abspath() string {\n\tmatches, _ := h.get_results()\n\tif r := matches.At(h.state.CurrentIndex()); r != nil {\n\t\treturn filepath.Join(h.state.CurrentDir(), r.text)\n\t}\n\treturn \"\"\n\n}\n\nfunc (s *State) CanSelect(r *ResultItem) bool {\n\treturn utils.IfElse(s.OnlyDirs(), r.ftype.IsDir(), !r.ftype.IsDir())\n}\n\nfunc (h *Handler) toggle_selection_at(idx CollectionIndex) bool {\n\tmatches, _ := h.get_results()\n\tif r := matches.At(idx); r != nil && h.state.CanSelect(r) {\n\t\tm := filepath.Join(h.state.CurrentDir(), r.text)\n\t\tif added := h.state.ToggleSelection(m); added {\n\t\t\th.result_manager.last_click_anchor = &idx\n\t\t} else {\n\t\t\th.result_manager.last_click_anchor = nil\n\t\t\tif len(h.state.selections) > 0 {\n\t\t\t\tx := utils.NewSetWithItems(h.state.selections...)\n\t\t\t\tcdir := h.state.CurrentDir()\n\t\t\t\th.result_manager.last_click_anchor = matches.Closest(idx, func(q *ResultItem) bool { return x.Has(filepath.Join(cdir, q.text)) })\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (h *Handler) toggle_selection() bool {\n\treturn h.toggle_selection_at(h.state.CurrentIndex())\n}\n\nfunc (h *Handler) change_current_dir(dir string) {\n\tif dir != h.state.CurrentDir() {\n\t\th.state.SetCurrentDir(dir)\n\t\th.result_manager.set_root_dir()\n\t\th.state.last_render = render_state{}\n\t}\n}\n\nfunc (h *Handler) set_query(q string) {\n\tif q != h.state.SearchText() {\n\t\th.state.SetSearchText(q)\n\t\th.result_manager.set_query()\n\t\th.state.last_render = render_state{}\n\t}\n}\n\nfunc (h *Handler) set_filter(filter_name string) {\n\tif filter_name != h.state.current_filter {\n\t\th.state.current_filter = filter_name\n\t\th.result_manager.set_filter()\n\t\th.state.last_render = render_state{}\n\t}\n}\n\nfunc (h *Handler) change_to_current_dir_if_possible() error {\n\tif m := h.current_abspath(); m != \"\" {\n\t\tif st, err := os.Stat(m); err == nil {\n\t\t\tif !st.IsDir() {\n\t\t\t\tm = filepath.Dir(m)\n\t\t\t}\n\t\t\th.change_current_dir(m)\n\t\t\treturn h.draw_screen()\n\t\t}\n\t}\n\th.lp.Beep()\n\treturn nil\n}\n\nfunc (h *Handler) finish_selection() error {\n\tif h.state.mode.CanSelectNonExistent() && len(h.state.selections) == 0 {\n\t\treturn h.switch_to_save_file_name_mode()\n\t}\n\th.lp.Quit(0)\n\treturn nil\n}\n\nfunc (h *Handler) change_filter(delta int) bool {\n\tif len(h.state.filter_names) < 2 {\n\t\treturn false\n\t}\n\tidx := slices.Index(h.state.filter_names, h.state.current_filter)\n\tidx += delta + len(h.state.filter_names)\n\tidx %= len(h.state.filter_names)\n\th.set_filter(h.state.filter_names[idx])\n\treturn true\n}\n\nfunc (h *Handler) switch_to_save_file_name_mode() error {\n\tname := h.state.suggested_save_file_name\n\tif h.state.SearchText() != \"\" {\n\t\tname = h.state.SearchText()\n\t}\n\tif name == \"\" {\n\t\tname = h.current_abspath()\n\t\tif name != \"\" {\n\t\t\tif r, err := filepath.Rel(h.state.CurrentDir(), name); err == nil {\n\t\t\t\tname = r\n\t\t\t}\n\t\t}\n\t}\n\th.initialize_save_file_name(name)\n\treturn h.draw_screen()\n}\n\nfunc (h *Handler) switch_to_save_file_name_mode_based_on_existing() error {\n\tname := h.current_abspath()\n\tif name == \"\" {\n\t\treturn h.switch_to_save_file_name_mode()\n\t}\n\tif r, err := filepath.Rel(h.state.CurrentDir(), name); err == nil {\n\t\tname = r\n\t}\n\th.initialize_save_file_name(name)\n\treturn h.draw_screen()\n}\n\nfunc (h *Handler) accept_idx(idx CollectionIndex) (accepted bool, err error) {\n\tmatches, _ := h.get_results()\n\tif r := matches.At(idx); r != nil {\n\t\tm := filepath.Join(h.state.CurrentDir(), r.text)\n\n\t\tif h.state.mode.SelectFiles() {\n\t\t\tvar s os.FileInfo\n\t\t\tif s, err = os.Stat(m); err != nil {\n\t\t\t\treturn false, nil\n\t\t\t}\n\t\t\tif s.IsDir() {\n\t\t\t\tif h.state.mode.CanSelectNonExistent() {\n\t\t\t\t\treturn true, h.switch_to_save_file_name_mode()\n\t\t\t\t}\n\t\t\t\treturn false, nil\n\t\t\t}\n\t\t}\n\n\t\th.state.AddSelection(m)\n\t\th.result_manager.last_click_anchor = &idx\n\t\tif len(h.state.selections) > 0 {\n\t\t\treturn true, h.finish_selection()\n\t\t}\n\t\treturn true, h.draw_screen()\n\t}\n\treturn\n}\n\nfunc (h *Handler) dispatch_action(name, args string) (err error) {\n\tswitch name {\n\tcase \"quit\":\n\t\th.lp.Quit(1)\n\tcase \"next\":\n\t\tif n, nerr := strconv.Atoi(args); nerr == nil {\n\t\t\th.next_result(n)\n\t\t} else {\n\t\t\tswitch args {\n\t\t\tcase \"\":\n\t\t\t\th.next_result(1)\n\t\t\tcase \"left\":\n\t\t\t\th.move_sideways(true)\n\t\t\tcase \"right\":\n\t\t\t\th.move_sideways(false)\n\t\t\tcase \"first\":\n\t\t\t\th.state.SetCurrentIndex(CollectionIndex{})\n\t\t\t\th.state.last_render.num_before = 0\n\t\t\tcase \"last\":\n\t\t\t\tmatches, _ := h.get_results()\n\t\t\t\th.state.SetCurrentIndex(matches.IncrementIndexWithWrapAround(CollectionIndex{}, -1))\n\t\t\t\th.state.last_render.num_before = 0\n\t\t\tcase \"first_on_screen\":\n\t\t\t\th.state.SetCurrentIndex(h.state.last_render.first_idx)\n\t\t\t\th.state.last_render.num_before = 0\n\t\t\tcase \"last_on_screen\":\n\t\t\t\tmatches, _ := h.get_results()\n\t\t\t\th.state.SetCurrentIndex(matches.IncrementIndexWithWrapAround(h.state.last_render.first_idx, h.state.last_render.num_shown-1))\n\t\t\t\th.state.last_render.num_before = h.state.last_render.num_shown - 1\n\t\t\t}\n\t\t}\n\t\treturn h.draw_screen()\n\tcase \"next_filter\":\n\t\tif n, nerr := strconv.Atoi(args); nerr == nil {\n\t\t\th.change_filter(n)\n\t\t\treturn h.draw_screen()\n\t\t}\n\t\th.lp.Beep()\n\tcase \"select\":\n\t\tif !h.toggle_selection() {\n\t\t\th.lp.Beep()\n\t\t} else {\n\t\t\treturn h.draw_screen()\n\t\t}\n\tcase \"accept\":\n\t\taccepted, aerr := h.accept_idx(h.state.CurrentIndex())\n\t\tif aerr != nil {\n\t\t\treturn aerr\n\t\t}\n\t\tif !accepted {\n\t\t\th.lp.Beep()\n\t\t}\n\tcase \"typename\":\n\t\tif !h.state.mode.CanSelectNonExistent() {\n\t\t\tif h.state.mode.OnlyDirs() {\n\t\t\t\th.state.AddSelection(h.state.CurrentDir())\n\t\t\t\treturn h.finish_selection()\n\t\t\t}\n\t\t\th.lp.Beep()\n\t\t} else {\n\t\t\treturn h.switch_to_save_file_name_mode()\n\t\t}\n\tcase \"modifyname\":\n\t\tif h.state.mode.CanSelectNonExistent() {\n\t\t\treturn h.switch_to_save_file_name_mode_based_on_existing()\n\t\t} else {\n\t\t\th.lp.Beep()\n\t\t}\n\tcase \"toggle\":\n\t\tswitch args {\n\t\tcase \"preview\":\n\t\t\th.state.show_preview = !h.state.show_preview\n\t\t\treturn h.draw_screen()\n\t\tcase \"dotfiles\":\n\t\t\th.state.show_hidden = !h.state.show_hidden\n\t\t\th.result_manager.set_show_hidden()\n\t\t\treturn h.draw_screen()\n\t\tcase \"ignorefiles\":\n\t\t\th.state.respect_ignores = !h.state.respect_ignores\n\t\t\th.result_manager.set_respect_ignores()\n\t\t\treturn h.draw_screen()\n\t\tcase \"sort_by_dates\":\n\t\t\th.state.sort_by_last_modified = !h.state.sort_by_last_modified\n\t\t\th.result_manager.set_sort_by_last_modified()\n\t\t\treturn h.draw_screen()\n\t\tdefault:\n\t\t\th.lp.Beep()\n\t\t}\n\tcase \"cd\":\n\t\tswitch args {\n\t\tcase \".\":\n\t\t\treturn h.change_to_current_dir_if_possible()\n\t\tcase \"..\":\n\t\t\tcurr := h.state.CurrentDir()\n\t\t\tswitch curr {\n\t\t\tcase \"/\":\n\t\t\tcase \".\":\n\t\t\t\tif curr, err = os.Getwd(); err == nil && curr != \"/\" {\n\t\t\t\t\th.change_current_dir(filepath.Dir(curr))\n\t\t\t\t\treturn h.draw_screen()\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\th.change_current_dir(filepath.Dir(curr))\n\t\t\t\treturn h.draw_screen()\n\t\t\t}\n\t\t\th.lp.Beep()\n\t\tdefault:\n\t\t\targs = utils.Expanduser(args)\n\t\t\tif st, serr := os.Stat(args); serr != nil || !st.IsDir() {\n\t\t\t\th.lp.Beep()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif absp, err := filepath.Abs(args); err == nil {\n\t\t\t\th.change_current_dir(absp)\n\t\t\t\treturn h.draw_screen()\n\t\t\t} else {\n\t\t\t\th.lp.Beep()\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (h *Handler) OnKeyEvent(ev *loop.KeyEvent) (err error) {\n\tswitch h.state.screen {\n\tcase NORMAL:\n\t\tif h.handle_edit_keys(ev) {\n\t\t\tev.Handled = true\n\t\t\th.draw_screen()\n\t\t}\n\t\tac := h.shortcut_tracker.Match(ev, h.state.keyboard_shortcuts)\n\t\tif ac != nil {\n\t\t\tev.Handled = true\n\t\t\treturn h.dispatch_action(ac.Name, ac.Args)\n\t\t}\n\tcase SAVE_FILE:\n\t\terr = h.save_file_name_handle_key(ev)\n\t}\n\treturn\n}\n\nfunc (h *Handler) OnMouseEvent(event *loop.MouseEvent) (err error) {\n\th.state.redraw_needed = h.state.mouse_state.UpdateState(event)\n\tif err = h.state.mouse_state.DispatchEventToHoveredRegions(event); err != nil {\n\t\treturn\n\t}\n\tif h.state.redraw_needed {\n\t\terr = h.draw_screen()\n\t}\n\treturn\n}\n\nfunc (h *Handler) OnText(text string, from_key_event, in_bracketed_paste bool) (err error) {\n\tswitch h.state.screen {\n\tcase NORMAL:\n\t\th.set_query(h.state.SearchText() + text)\n\t\treturn h.draw_screen()\n\tcase SAVE_FILE:\n\t\tif err = h.rl.OnText(text, from_key_event, in_bracketed_paste); err == nil {\n\t\t\terr = h.draw_screen()\n\t\t}\n\t}\n\treturn\n}\n\ntype CachedValues struct {\n\tShow_hidden           bool `json:\"show_hidden\"`\n\tHide_preview          bool `json:\"hide_preview\"`\n\tRespect_ignores       bool `json:\"respect_ignores\"`\n\tSort_by_last_modified bool `json:\"sort_by_last_modified\"`\n}\n\nconst cache_filename = \"choose-files.json\"\n\nvar cached_values = sync.OnceValue(func() *CachedValues {\n\tans := CachedValues{Respect_ignores: true}\n\tfname := filepath.Join(utils.CacheDir(), cache_filename)\n\tif data, err := os.ReadFile(fname); err == nil {\n\t\t_ = json.Unmarshal(data, &ans)\n\t}\n\treturn &ans\n})\n\nfunc (s State) save_cached_values() {\n\tc := CachedValues{Show_hidden: s.show_hidden, Respect_ignores: s.respect_ignores, Sort_by_last_modified: s.sort_by_last_modified, Hide_preview: !s.show_preview}\n\tfname := filepath.Join(utils.CacheDir(), cache_filename)\n\tif data, err := json.Marshal(c); err == nil {\n\t\t_ = os.WriteFile(fname, data, 0600)\n\t}\n}\n\nfunc (h *Handler) set_state_from_config(conf *Config, opts *Options) (err error) {\n\th.state = State{}\n\tswitch opts.Mode {\n\tcase \"file\":\n\t\th.state.mode = SELECT_SINGLE_FILE\n\tcase \"files\":\n\t\th.state.mode = SELECT_MULTIPLE_FILES\n\tcase \"save-file\":\n\t\th.state.mode = SELECT_SAVE_FILE\n\tcase \"dir\":\n\t\th.state.mode = SELECT_SINGLE_DIR\n\tcase \"dirs\":\n\t\th.state.mode = SELECT_MULTIPLE_DIRS\n\tcase \"save-dir\":\n\t\th.state.mode = SELECT_SAVE_DIR\n\tcase \"save-files\":\n\t\th.state.mode = SELECT_SAVE_FILES\n\tdefault:\n\t\th.state.mode = SELECT_SINGLE_FILE\n\t}\n\th.state.suggested_save_file_name = opts.SuggestedSaveFileName\n\th.state.suggested_save_file_path = opts.SuggestedSaveFilePath\n\th.state.filter_map = nil\n\th.state.current_filter = \"\"\n\tif len(opts.FileFilter) > 0 {\n\t\topts.FileFilter = utils.Uniq(opts.FileFilter)\n\t\thas_all_files := false\n\t\tfmap := make(map[string][]Filter)\n\t\tseen := utils.NewSet[string](len(opts.FileFilter))\n\t\tfor _, x := range opts.FileFilter {\n\t\t\tf, ferr := NewFilter(x)\n\t\t\tif ferr != nil {\n\t\t\t\treturn ferr\n\t\t\t}\n\t\t\tif f.Match == nil {\n\t\t\t\thas_all_files = true\n\t\t\t}\n\t\t\tif h.state.current_filter == \"\" {\n\t\t\t\th.state.current_filter = f.Name\n\t\t\t}\n\t\t\tfmap[f.Name] = append(fmap[f.Name], *f)\n\t\t\tif !seen.Has(f.Name) {\n\t\t\t\tseen.Add(f.Name)\n\t\t\t\th.state.filter_names = append(h.state.filter_names, f.Name)\n\t\t\t}\n\t\t}\n\t\tif !has_all_files {\n\t\t\taf, _ := NewFilter(\"glob:*:All files\")\n\t\t\tfmap[af.Name] = append(fmap[af.Name], *af)\n\t\t\tif !seen.Has(af.Name) {\n\t\t\t\th.state.filter_names = append(h.state.filter_names, af.Name)\n\t\t\t}\n\t\t}\n\t\th.state.filter_map = make(map[string]Filter)\n\t\tfor name, filters := range fmap {\n\t\t\th.state.filter_map[name] = CombinedFilter(filters...)\n\t\t}\n\t}\n\th.state.sort_by_last_modified = false\n\th.state.respect_ignores = true\n\th.state.show_hidden = false\n\th.state.show_preview = true\n\n\tswitch conf.Show_hidden {\n\tcase Show_hidden_true, Show_hidden_y, Show_hidden_yes:\n\t\th.state.show_hidden = true\n\tcase Show_hidden_false, Show_hidden_n, Show_hidden_no:\n\t\th.state.show_hidden = false\n\tcase Show_hidden_last:\n\t\th.state.show_hidden = cached_values().Show_hidden\n\t}\n\tswitch conf.Respect_ignores {\n\tcase Respect_ignores_true, Respect_ignores_y, Respect_ignores_yes:\n\t\th.state.respect_ignores = true\n\tcase Respect_ignores_false, Respect_ignores_n, Respect_ignores_no:\n\t\th.state.respect_ignores = false\n\tcase Respect_ignores_last:\n\t\th.state.respect_ignores = cached_values().Respect_ignores\n\t}\n\tswitch conf.Sort_by_last_modified {\n\tcase Sort_by_last_modified_true, Sort_by_last_modified_y, Sort_by_last_modified_yes:\n\t\th.state.sort_by_last_modified = true\n\tcase Sort_by_last_modified_false, Sort_by_last_modified_n, Sort_by_last_modified_no:\n\t\th.state.sort_by_last_modified = false\n\tcase Sort_by_last_modified_last:\n\t\th.state.sort_by_last_modified = cached_values().Sort_by_last_modified\n\t}\n\tswitch conf.Show_preview {\n\tcase Show_preview_true, Show_preview_y, Show_preview_yes:\n\t\th.state.show_preview = true\n\tcase Show_preview_false, Show_preview_n, Show_preview_no:\n\t\th.state.show_preview = false\n\tcase Show_preview_last:\n\t\th.state.show_preview = !cached_values().Hide_preview\n\t}\n\n\th.state.global_ignores = ignorefiles.NewGitignore()\n\tif err = h.state.global_ignores.LoadLines(conf.Ignore...); err != nil {\n\t\treturn err\n\t}\n\th.state.keyboard_shortcuts = conf.KeyboardShortcuts\n\th.state.display_title = opts.DisplayTitle\n\th.state.pygments_style = conf.Pygments_style\n\th.state.dark_pygments_style = conf.Dark_pygments_style\n\th.state.syntax_aliases = conf.Syntax_aliases\n\th.state.max_disk_cache_size = int64(conf.Cache_size * (1024 * 1024 * 1024))\n\treturn\n}\n\nvar default_cwd string\nvar use_light_colors bool\n\nfunc quote_if_needed(x string) string {\n\tif s, err := shlex.Split(x); len(s) == 1 && err == nil && !strings.Contains(x, \"$\") {\n\t\treturn x\n\t}\n\treturn utils.QuoteStringForSH(x)\n}\n\nfunc for_shell_relative(x string) string {\n\tif rel, is_under, err := utils.RelativeIfUnder(default_cwd, x, false); err == nil && is_under {\n\t\tx = rel\n\t}\n\treturn quote_if_needed(x)\n}\n\nfunc main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tif opts.ClearCache {\n\t\tc, err := preview_cache()\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tif err = c.Clear(); err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t}\n\twrite_output := func(selections []string, interrupted bool, current_filter string) {\n\t\tpayload := make(map[string]any)\n\t\tif err != nil {\n\t\t\tif opts.WriteOutputTo != \"\" {\n\t\t\t\tm := fmt.Sprint(err)\n\t\t\t\tif opts.OutputFormat == \"json\" {\n\t\t\t\t\tpayload[\"error\"] = m\n\t\t\t\t\tb, _ := json.MarshalIndent(payload, \"\", \"  \")\n\t\t\t\t\tm = string(b)\n\t\t\t\t}\n\t\t\t\tos.WriteFile(opts.WriteOutputTo, []byte(m), 0600)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif interrupted {\n\t\t\tif opts.WriteOutputTo != \"\" {\n\t\t\t\tif opts.OutputFormat == \"json\" {\n\t\t\t\t\tpayload[\"interrupted\"] = true\n\t\t\t\t\tb, _ := json.MarshalIndent(payload, \"\", \"  \")\n\t\t\t\t\tos.WriteFile(opts.WriteOutputTo, b, 0600)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tpayload[\"paths\"] = selections\n\t\tif current_filter != \"\" {\n\t\t\tpayload[\"current_filter\"] = current_filter\n\t\t}\n\t\tif tui.RunningAsUI() {\n\t\t\tfmt.Println(tui.KittenOutputSerializer()(payload))\n\t\t} else {\n\t\t\tvar m string\n\t\t\tswitch opts.OutputFormat {\n\t\t\tcase \"shell\":\n\t\t\t\tm = strings.Join(utils.Map(quote_if_needed, selections), \" \")\n\t\t\tcase \"shell-relative\":\n\t\t\t\tm = strings.Join(utils.Map(for_shell_relative, selections), \" \")\n\t\t\tcase \"text\":\n\t\t\t\tm = strings.Join(selections, \"\\n\")\n\t\t\tcase \"json\":\n\t\t\t\tb, _ := json.MarshalIndent(payload, \"\", \"  \")\n\t\t\t\tm = string(b)\n\t\t\t}\n\t\t\tos.Stdout.Write(utils.UnsafeStringToBytes(m))\n\t\t\tif opts.WriteOutputTo != \"\" {\n\t\t\t\tos.WriteFile(opts.WriteOutputTo, utils.UnsafeStringToBytes(m), 0600)\n\t\t\t}\n\t\t}\n\t}\n\n\tconf, err := load_config(opts)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tif err = load_previewers(conf); err != nil {\n\t\treturn 1, err\n\t}\n\tlp, err := loop.New()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tlp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)\n\tlp.ColorSchemeChangeNotifications()\n\thandler := Handler{lp: lp, err_chan: make(chan error, 8), msg_printer: message.NewPrinter(utils.LanguageTag()), spinner: tui.NewSpinner(\"dots\")}\n\tdefer handler.graphics_handler.Cleanup()\n\tdefer calibre_cleanup()\n\tgetcwd := func() string {\n\t\tans := handler.state.CurrentDir()\n\t\tif ans == \"\" {\n\t\t\tvar err error\n\t\t\tans, err = os.Getwd()\n\t\t\tif err != nil {\n\t\t\t\tans = \".\"\n\t\t\t}\n\t\t}\n\t\treturn ans\n\t}\n\thandler.rl = readline.New(lp, readline.RlInit{\n\t\tPrompt: \"> \", ContinuationPrompt: \". \", Completer: FilePromptCompleter(getcwd),\n\t})\n\tif err = handler.set_state_from_config(conf, opts); err != nil {\n\t\treturn 1, err\n\t}\n\thandler.result_manager = NewResultManager(handler.err_chan, &handler.state, lp.WakeupMainThread)\n\thandler.preview_manager = NewPreviewManager(handler.err_chan, &handler.state, lp.WakeupMainThread)\n\tswitch len(args) {\n\tcase 0:\n\t\tif default_cwd, err = os.Getwd(); err != nil {\n\t\t\treturn\n\t\t}\n\tcase 1:\n\t\tdefault_cwd = args[0]\n\tdefault:\n\t\treturn 1, fmt.Errorf(\"Can only specify one directory to search in\")\n\t}\n\tdefault_cwd = utils.Expanduser(default_cwd)\n\tif default_cwd, err = filepath.Abs(default_cwd); err != nil {\n\t\treturn\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tif opts.WritePidTo != \"\" {\n\t\t\tif err := utils.AtomicWriteFile(opts.WritePidTo, bytes.NewReader([]byte(strconv.Itoa(os.Getpid()))), 0600); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\t\tif opts.Title != \"\" {\n\t\t\tlp.SetWindowTitle(opts.Title)\n\t\t}\n\t\tlp.RequestCurrentColorScheme()\n\t\treturn handler.OnInitialize()\n\t}\n\tlp.OnFinalize = func() string {\n\t\thandler.graphics_handler.Finalize(lp)\n\t\treturn \"\"\n\t}\n\tlp.OnResize = func(old, new_size loop.ScreenSize) (err error) {\n\t\thandler.init_sizes(new_size)\n\t\thandler.clear_cached_previews()\n\t\treturn handler.draw_screen()\n\t}\n\tlp.OnColorSchemeChange = func(p loop.ColorPreference) (err error) {\n\t\tnew_val := p == loop.LIGHT_COLOR_PREFERENCE\n\t\tif new_val != use_light_colors {\n\t\t\tuse_light_colors = new_val\n\t\t\thandler.preview_manager.invalidate_color_scheme_based_cached_items()\n\t\t\treturn handler.draw_screen()\n\t\t}\n\t\treturn\n\t}\n\tlp.OnKeyEvent = handler.OnKeyEvent\n\tlp.OnText = handler.OnText\n\tlp.OnMouseEvent = handler.OnMouseEvent\n\tlp.OnEscapeCode = handler.on_escape_code\n\tlp.OnWakeup = func() (err error) {\n\t\tselect {\n\t\tcase err = <-handler.err_chan:\n\t\tdefault:\n\t\t\terr = handler.draw_screen()\n\t\t}\n\t\treturn\n\t}\n\terr = lp.Run()\n\thandler.state.save_cached_values()\n\tif err != nil {\n\t\twrite_output(nil, false, \"\")\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\twrite_output(nil, true, \"\")\n\t\treturn 1, nil\n\t}\n\trc = lp.ExitCode()\n\tswitch rc {\n\tcase 0:\n\t\twrite_output(handler.state.selections, false, handler.state.current_filter)\n\tdefault:\n\t\twrite_output(nil, true, \"\")\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/choose_files/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nfrom typing import Any\n\nfrom kitty.conf.types import Definition\nfrom kitty.constants import appname\nfrom kitty.simple_cli_definitions import CONFIG_HELP, CompletionSpec\nfrom kitty.typing_compat import BossType\n\nfrom ..tui.handler import result_handler\n\ndefinition = Definition(\n    '!kittens.choose_files',\n)\n\nagr = definition.add_group\negr = definition.end_group\nopt = definition.add_option\nmap = definition.add_map\nmma = definition.add_mouse_map\n\nagr('scanning', 'Filesystem scanning')  # {{{\n\nopt('show_hidden', 'last', choices=('last', 'yes', 'y', 'true', 'no', 'n', 'false'), long_text='''\nWhether to show hidden files. The default value of :code:`last` means remember the last\nused value. This setting can be toggled within the program.''')\n\nopt('sort_by_last_modified', 'last', choices=('last', 'yes', 'y', 'true', 'no', 'n', 'false'), long_text='''\nWhether to sort the list of entries by last modified, instead of name. Note that sorting only applies\nbefore any query is entered. Once a query is entered entries are sorted by their matching score.\nThe default value of :code:`last` means remember the last\nused value. This setting can be toggled within the program.''')\n\nopt('respect_ignores', 'last', choices=('last', 'yes', 'y', 'true', 'no', 'n', 'false'), long_text='''\nWhether to respect .gitignore and .ignore files and the :opt:`ignore` setting.\nThe default value of :code:`last` means remember the last used value.\nThis setting can be toggled within the program.''')\n\nopt('+ignore', '', add_to_default=False, long_text='''\nAn ignore pattern to ignore matched files. Uses the same sytax as :code:`.gitignore` files (see :code:`man gitignore`).\nAnchored patterns match with respect to whatever directory is currently being displayed.\nCan be specified multiple times to use multiple patterns. Note that every pattern\nhas to be checked against every file, so use sparingly.\n''')\negr()  # }}}\n\nagr('appearance', 'Appearance')  # {{{\n\nopt('show_preview', 'last', choices=('last', 'yes', 'y', 'true', 'no', 'n', 'false'), long_text='''\nWhether to show a preview of the current file/directory. The default value of :code:`last` means remember the last\nused value. This setting can be toggled within the program.''')\n\nopt('pygments_style', 'default', long_text='''\nThe pygments color scheme to use for syntax highlighting of file previews. See :link:`pygments\nbuiltin styles <https://pygments.org/styles/>` for a list of schemes.\nThis sets the colors used for light color schemes, use :opt:`dark_pygments_style` to change the\ncolors for dark color schemes.\n''')\n\nopt('dark_pygments_style', 'github-dark', long_text='''\nThe pygments color scheme to use for syntax highlighting with dark colors. See :link:`pygments\nbuiltin styles <https://pygments.org/styles/>` for a list of schemes.\nThis sets the colors used for dark color schemes, use :opt:`pygments_style` to change the\ncolors for light color schemes.''')\n\nopt('cache_size', '0.5', option_type='positive_float', long_text='''\nThe maximum size of the disk cache, in gigabytes, used for previews. Zero or negative values\nmean no limit.\n''')\n\nopt('syntax_aliases', 'pyj:py pyi:py recipe:py', ctype='strdict_ _:', option_type='syntax_aliases',\n    long_text='''\nFile extension aliases for syntax highlight. For example, to syntax highlight\n:file:`file.xyz` as :file:`file.abc` use a setting of :code:`xyz:abc`.\nMultiple aliases must be separated by spaces.\n''')\n\nopt('video_preview', 'width=480 fps=10 duration=5', long_text='''\nControl how videos are sampled for previwing. The width controls\nthe size of the generated thumbnail from the video. Duration controls\nhow long the generated thumbnail plays for, in seconds. Note that when\nchanging these you should also use the :code:`--clear-cache` flag\notherwise it will not affect already cached previews.\n''')\n\nopt('+previewer', '', long_text='''\nSpecify an arbitrary program based preview generator. The syntax is::\n\n    pattern program arguments...\n\nHere, pattern can be used to match file names or mimetypes. For example:\n:code:`name:*.doc` matches files with the extension :code:`.doc`. Similarly,\n:code:`mime:image/*` matches all image files. :code:`program` can be any\nexecutable program in PATH. It will be run with the supplied arguments. The last argument\nwill be the path to the file for which a preview must be generated.\n\nCan be specified multiple times to setup different previewers for different types of files.\nNote that previewers specified using this option take precedence over the builtin\npreviewers.\n\nThe command must output preview data to STDOUT, as a JSON object:\n\n.. code-block:: json\n\n    {\n        \"lines\": [\"line1\", \"line2\", \"...\"],\n        \"image\": \"absolute path to generated image preview\",\n        \"title_extra\": \"some text to show on the first line\",\n    }\n\nThe lines can contain SGR formatting escape codes and will be displayed as is at the\ntop of the preview panel. The image is optional and must be in one of the JPEG, PNG, GIF, WEBP, APNG\nformats.\n''')\negr()  # }}}\n\nagr('shortcuts', 'Keyboard shortcuts')  # {{{\nmap('Quit', 'quit esc quit')\nmap('Quit', 'quit ctrl+c quit')\n\nmap('Accept current result', 'accept enter accept')\nmap('Select current result', 'select shift+enter select', long_text='''\nWhen selecting multiple files, this will add the current file to the list of selected files.\nYou can also toggle the selected status of a file by holding down the :kbd:`Ctrl` key and clicking on\nit. Similarly, the :kbd:`Alt` key can be held to click and extend the range of selected files.\n''')\nmap('Type file name', 'typename ctrl+enter typename', long_text='''\nType a file name/path rather than filtering the list of existing files.\nUseful when specifying a file or directory name for saving that does not yet exist.\nWhen choosing existing directories, will accept the directory whoose\ncontents are being currently displayed as the choice.\nDoes not work when selecting files to open rather than to save.\n''')\nmap('Modify file name', 'modifyname alt+enter modifyname', long_text='''\nModify the name of an existing file and select it for saving.\nUseful when specifying a file or directory name for saving that does not yet exist,\nbut is based on an existing file name.\nDoes not work when selecting files to open rather than to save.\n''')\n\n\nmap('Next result', 'next_result down next 1')\nmap('Previous result', 'prev_result up next -1')\nmap('Left result', 'left_result left next left')\nmap('Right result', 'right_result right next right')\nmap('First result on screen', 'first_result_on_screen home next first_on_screen')\nmap('Last result on screen', 'last_result_on_screen end next last_on_screen')\nmap('First result', 'first_result_on_screen ctrl+home next first')\nmap('Last result', 'last_result_on_screen ctrl+end next last')\n\nmap('Change to currently selected dir', 'cd_current tab cd .')\nmap('Change to parent directory', 'cd_parent shift+tab cd ..')\nmap('Change to root directory', 'cd_root ctrl+/ cd /')\nmap('Change to home directory', 'cd_home ctrl+~ cd ~')\nmap('Change to home directory', 'cd_home ctrl+` cd ~')\nmap('Change to home directory', 'cd_home ctrl+shift+` cd ~')\nmap('Change to temp directory', 'cd_tmp ctrl+t cd /tmp')\n\nmap('Next filter', 'next_filter ctrl+f 1')\nmap('Previous filter', 'prev_filter alt+f -1')\n\nmap('Toggle showing dotfiles', 'toggle_dotfiles alt+h toggle dotfiles')\nmap('Toggle showing ignored files', 'toggle_ignorefiles alt+i toggle ignorefiles')\nmap('Toggle sorting by dates', 'toggle_sort_by_dates alt+d toggle sort_by_dates')\nmap('Toggle showing preview', 'toggle_preview alt+p toggle preview')\n\negr()  # }}}\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('This must be run as kitten choose-files')\n\ndef relative_path_if_possible(path: str, base: str) -> str:\n    if not base or not path:\n        return path\n    from contextlib import suppress\n    from pathlib import Path\n    b = Path(base)\n    q = Path(path)\n    with suppress(ValueError):\n        return str(q.relative_to(b))\n    return path\n\n\n@result_handler(has_ready_notification=True)\ndef handle_result(args: list[str], data: dict[str, Any], target_window_id: int, boss: BossType) -> None:\n    import shlex\n\n    from kitty.utils import shlex_split\n    paths: list[str] = data.get('paths', [])\n    if not paths:\n        boss.ring_bell_if_allowed()\n        return\n    w = boss.window_id_map.get(target_window_id)\n    if w is None:\n        boss.ring_bell_if_allowed()\n        return\n    cwd = w.cwd_of_child\n    items = []\n    for path in paths:\n        if cwd:\n            path = relative_path_if_possible(path, cwd)\n        if w.at_prompt and len(tuple(shlex_split(path))) > 1:\n            path = shlex.quote(path)\n        items.append(path)\n    text = (' ' if w.at_prompt else '\\n').join(items)\n    w.paste_text(text)\n\n\nusage = '[directory to start choosing files in]'\n\n\nOPTIONS = '''\n--mode\ntype=choices\nchoices=file,files,save-file,dir,save-dir,dirs,save-files\ndefault=file\nThe type of object(s) to select\n\n\n--file-filter\ntype=list\nA list of filters to restrict the displayed files. Can be either mimetypes, or glob style patterns. Can be specified multiple times.\nThe syntax is :code:`type:expression:Descriptive Name`.\nFor example: :code:`mime:image/png:Images` and :code:`mime:image/gif:Images` and :code:`glob:*.[tT][xX][Tt]:Text files`.\nNote that glob patterns are case-sensitive. The mimetype specification is treated as a glob expressions as well, so you can,\nfor example, use :code:`mime:text/*` to match all text files. The first filter in the list will be applied by default. Use a filter\nsuch as :code:`glob:*:All` to match all files. Note that filtering only appies to files, not directories.\n\n\n--suggested-save-file-name\nA suggested name when picking a save file.\n\n\n--suggested-save-file-path\nPath to an existing file to use as the save file.\n\n\n--title\nWindow title to use for this chooser\n\n\n--display-title\ntype=bool-set\nShow the window title at the top, useful when this kitten is used in an\nOS window without a title bar.\n\n\n--override -o\ntype=list\nOverride individual configuration options, can be specified multiple times.\nSyntax: :italic:`name=value`.\n\n\n--config\ntype=list\ncompletion=type:file ext:conf group:\"Config files\" kwds:none,NONE\n{config_help}\n\n\n--write-output-to\nPath to a file to which the output is written in addition to STDOUT.\n\n\n--output-format\nchoices=text,json,shell,shell-relative\ndefault=text\nThe format in which to write the output. The :code:`text` format is absolute paths separated by newlines.\nThe :code:`shell` format is quoted absolute paths separated by spaces, quoting is done only if needed. The\n:code:`shell-relative` format is the same as :code:`shell` except it returns paths relative to the starting\ndirectory. Note that when invoked from a mapping, this option is ignored,\nand either text or shell format is used automatically based on whether the cursor is at a shell prompt or not.\n\n\n--write-pid-to\nPath to a file to which to write the process ID (PID) of this process to.\n\n\n--clear-cache\ntype=bool-set\nClear the caches used by this kitten.\n'''.format(config_help=CONFIG_HELP.format(conf_name='choose-files', appname=appname)).format\n\n\nhelp_text = '''\\\nSelect one or more files, quickly, using fuzzy finding, by typing just a few characters from\nthe file name. Browse matching files, using the arrow keys to navigate matches and press :kbd:`Enter`\nto select. The :kbd:`Tab` key can be used to change to a sub-folder. See the :doc:`online docs </kittens/choose-files>`\nfor full details.\n'''\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Choose files, fast'\n    cd['args_completion'] = CompletionSpec.from_string('type:directory')\nelif __name__ == '__conf__':\n    sys.options_definition = definition  # type: ignore\n"
  },
  {
    "path": "kittens/choose_files/preview.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"maps\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/highlight\"\n\t\"github.com/kovidgoyal/kitty/tools/icons\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype Preview interface {\n\tRender(h *Handler, x, y, width, height int)\n\tIsValidForColorScheme(light bool) bool\n\tUnload()\n\tIsReady() bool\n\tString() string\n}\n\ntype PreviewManager struct {\n\treport_errors    chan error\n\tsettings         Settings\n\tWakeupMainThread func() bool\n\tcache            map[string]Preview\n\tlock             sync.Mutex\n\thighlighter      highlight.Highlighter\n}\n\nfunc NewPreviewManager(err_chan chan error, settings Settings, WakeupMainThread func() bool) (ans *PreviewManager) {\n\tdefer func() { sanitize = ans.highlighter.Sanitize }()\n\treturn &PreviewManager{\n\t\treport_errors: err_chan, settings: settings, WakeupMainThread: WakeupMainThread,\n\t\tcache: make(map[string]Preview), highlighter: highlight.NewHighlighter(nil),\n\t}\n}\n\nfunc (pm *PreviewManager) cached_preview(path string) Preview {\n\tpm.lock.Lock()\n\tdefer pm.lock.Unlock()\n\treturn pm.cache[path]\n}\n\nfunc (pm *PreviewManager) set_cached_preview(path string, val Preview) {\n\tpm.lock.Lock()\n\tdefer pm.lock.Unlock()\n\tpm.cache[path] = val\n}\n\nfunc (h *Handler) render_wrapped_text_in_region(text string, x, y, width, height int, centered bool) int {\n\tlines := style.WrapTextAsLines(text, width, style.WrapOptions{})\n\tfor i, line := range lines {\n\t\textra := 0\n\t\tif centered {\n\t\t\textra = max(0, width-wcswidth.Stringwidth(line)) / 2\n\t\t}\n\t\th.lp.MoveCursorTo(x+extra, y+i)\n\t\th.lp.QueueWriteString(line)\n\t\tif i >= height {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn len(lines)\n}\n\ntype MessagePreview struct {\n\ttitle    string\n\tmsg      string\n\ttrailers []string\n}\n\nfunc (p MessagePreview) String() string                  { return fmt.Sprintf(\"MessagePreview{%#v}\", p.title) }\nfunc (p MessagePreview) IsValidForColorScheme(bool) bool { return true }\nfunc (p MessagePreview) IsReady() bool                   { return true }\n\nfunc (p MessagePreview) Unload() {}\nfunc (p MessagePreview) Render(h *Handler, x, y, width, height int) {\n\toffset := 0\n\tif p.title != \"\" {\n\t\toffset += h.render_wrapped_text_in_region(p.title, x, y, width, height, true)\n\t}\n\toffset += h.render_wrapped_text_in_region(p.msg, x, y+offset, width, height-offset, false)\n\tlimit := height - offset\n\tif limit > 1 {\n\t\tfor i, line := range p.trailers {\n\t\t\ttext := wcswidth.TruncateToVisualLength(line, width-1)\n\t\t\tif len(text) < len(line) {\n\t\t\t\ttext += \"…\"\n\t\t\t}\n\t\t\th.lp.MoveCursorTo(x, y+offset+i-1)\n\t\t\tif i >= limit {\n\t\t\t\th.lp.QueueWriteString(\"…\")\n\t\t\t\tbreak\n\t\t\t}\n\t\t\th.lp.QueueWriteString(text)\n\t\t}\n\t}\n}\n\nfunc NewErrorPreview(err error) Preview {\n\tsctx := style.Context{AllowEscapeCodes: true}\n\ttext := fmt.Sprintf(\"%s: %s\", sctx.SprintFunc(\"fg=red\")(\"Error\"), err)\n\treturn &MessagePreview{msg: text}\n}\n\nvar sanitize func(string) string\n\nfunc write_file_metadata(abspath string, metadata fs.FileInfo, entries []fs.DirEntry) (header string, trailers []string) {\n\tbuf := strings.Builder{}\n\tbuf.Grow(4096)\n\tadd := func(key, val string) { fmt.Fprintf(&buf, \"%s: %s\\n\", key, val) }\n\tftype := metadata.Mode().Type()\n\tconst file_icon = \" \"\n\tfmt.Fprintln(&buf, filepath.Base(abspath))\n\tswitch ftype {\n\tcase 0:\n\t\tadd(\"Size\", humanize.Bytes(uint64(metadata.Size())))\n\tcase fs.ModeSymlink:\n\t\tif tgt, err := os.Readlink(abspath); err == nil {\n\t\t\tadd(\"Target\", sanitize(tgt))\n\t\t} else {\n\t\t\tadd(\"Target\", err.Error())\n\t\t}\n\tcase fs.ModeDir:\n\t\tnum_files, num_dirs := 0, 0\n\t\tfor _, e := range entries {\n\t\t\tif e.IsDir() {\n\t\t\t\tnum_dirs++\n\t\t\t} else {\n\t\t\t\tnum_files++\n\t\t\t}\n\t\t}\n\t\tadd(\"Children\", fmt.Sprintf(\"%d %s  %d %s\", num_dirs, icons.IconForFileWithMode(\"dir\", fs.ModeDir, false), num_files, file_icon))\n\t}\n\tadd(\"Modified\", humanize.Time(metadata.ModTime()))\n\tadd(\"Mode\", metadata.Mode().String())\n\tif len(entries) > 0 {\n\t\ttype entry struct {\n\t\t\tlname string\n\t\t\tftype fs.FileMode\n\t\t}\n\t\ttype_map := make(map[string]entry, len(entries))\n\t\tfor _, e := range entries {\n\t\t\ttype_map[e.Name()] = entry{strings.ToLower(e.Name()), e.Type()}\n\t\t}\n\t\tnames := utils.Map(func(e fs.DirEntry) string { return e.Name() }, entries)\n\t\tslices.SortFunc(names, func(a, b string) int { return strings.Compare(type_map[a].lname, type_map[b].lname) })\n\t\tfmt.Fprintln(&buf, \"Contents:\")\n\t\tfor _, n := range names {\n\t\t\ttrailers = append(trailers, icons.IconForFileWithMode(n, type_map[n].ftype, false)+\"  \"+sanitize(n))\n\t\t}\n\t}\n\treturn buf.String(), trailers\n}\n\nfunc NewDirectoryPreview(abspath string, metadata fs.FileInfo) Preview {\n\tentries, err := os.ReadDir(abspath)\n\tif err != nil {\n\t\treturn NewErrorPreview(fmt.Errorf(\"failed to read the directory %s with error: %w\", abspath, err))\n\t}\n\ttitle := icons.IconForFileWithMode(\"dir\", fs.ModeDir, false) + \"  Directory\\n\"\n\theader, extra := write_file_metadata(abspath, metadata, entries)\n\treturn &MessagePreview{title: title, msg: header, trailers: extra}\n}\n\nfunc NewFileMetadataPreview(abspath string, metadata fs.FileInfo) *MessagePreview {\n\text := filepath.Ext(abspath)\n\tif ext == \"\" {\n\t\text = \"File\"\n\t}\n\ttitle := icons.IconForFileWithMode(filepath.Base(abspath), metadata.Mode().Type(), false) + \"  \" + ext\n\th, t := write_file_metadata(abspath, metadata, nil)\n\treturn &MessagePreview{title: title, msg: h, trailers: t}\n}\n\nfunc NewFileMetadataPreviewWithError(abspath string, metadata fs.FileInfo, err error) *MessagePreview {\n\text := filepath.Ext(abspath)\n\tif ext == \"\" {\n\t\text = \"File\"\n\t}\n\ttitle := icons.IconForFileWithMode(filepath.Base(abspath), metadata.Mode().Type(), false) + \"  \" + ext\n\th, t := write_file_metadata(abspath, metadata, nil)\n\tans := &MessagePreview{title: title, msg: h, trailers: t}\n\tlines := style.WrapTextAsLines(err.Error(), 30, style.WrapOptions{})\n\tans.trailers = append(ans.trailers, \"\")\n\tans.trailers = append(ans.trailers, lines...)\n\treturn ans\n}\n\ntype highlighed_data struct {\n\ttext  string\n\tlight bool\n\terr   error\n}\n\ntype TextFilePreview struct {\n\tplain_text, highlighted_text string\n\thighlighted_chan             chan highlighed_data\n\tready                        atomic.Bool\n\tlight                        bool\n\tpath                         string\n\tmetadata                     fs.FileInfo\n}\n\nfunc (p *TextFilePreview) String() string {\n\treturn fmt.Sprintf(\"TextFilePreview{%#v, ready: %v}\", p.path, p.ready.Load())\n}\n\nfunc (p *TextFilePreview) IsValidForColorScheme(light bool) bool { return p.light == light }\n\nfunc (p *TextFilePreview) Unload() {}\n\nfunc (p *TextFilePreview) IsReady() bool { return p.ready.Load() || p.highlighted_chan == nil }\nfunc (p *TextFilePreview) Render(h *Handler, x, y, width, height int) {\n\tif p.highlighted_chan != nil {\n\t\tselect {\n\t\tcase hd := <-p.highlighted_chan:\n\t\t\tp.highlighted_chan = nil\n\t\t\tif hd.err == nil {\n\t\t\t\tp.highlighted_text = hd.text\n\t\t\t}\n\t\tdefault:\n\t\t}\n\t}\n\ttext := p.highlighted_text\n\tif text == \"\" {\n\t\ttext = p.plain_text\n\t}\n\ts := utils.NewLineScanner(text)\n\tbuf := strings.Builder{}\n\tbuf.Grow(1024 * height)\n\ttitle := icons.IconForPath(p.path) + \"  \" + filepath.Base(p.path) + fmt.Sprintf(\" %s\", humanize.Bytes(uint64(p.metadata.Size())))\n\tfor num := 1 + h.render_wrapped_text_in_region(title, x, y, width, height, true); s.Scan() && num < height; num++ {\n\t\tline := s.Text()\n\t\ttruncated := wcswidth.TruncateToVisualLength(line, width)\n\t\tbuf.WriteString(fmt.Sprintf(loop.MoveCursorToTemplate, y+num, x))\n\t\tbuf.WriteString(truncated)\n\t\tif len(truncated) < len(line) {\n\t\t\twcswidth.KeepOnlyCSI(line[len(truncated):], &buf)\n\t\t}\n\t}\n\tbuf.WriteString(\"\\x1b[m\") // reset any highlight styles\n\th.lp.QueueWriteString(buf.String())\n}\n\nfunc NewTextFilePreview(abspath string, metadata fs.FileInfo, highlighted_chan chan highlighed_data, sanitize func(string) string) Preview {\n\tdata, err := os.ReadFile(abspath)\n\tif err != nil {\n\t\treturn NewFileMetadataPreview(abspath, metadata)\n\t}\n\ttext := utils.UnsafeBytesToString(data)\n\tif !utf8.ValidString(text) {\n\t\ttext = \"Error: not valid utf-8 text\"\n\t}\n\treturn &TextFilePreview{\n\t\tpath: abspath, plain_text: sanitize(text), highlighted_chan: highlighted_chan, light: use_light_colors, metadata: metadata}\n}\n\ntype style_resolver struct {\n\tlight                   bool\n\tlight_style, dark_style string\n\tsyntax_aliases          map[string]string\n}\n\nfunc (s style_resolver) StyleName() string {\n\treturn utils.IfElse(s.light, s.light_style, s.dark_style)\n}\nfunc (s style_resolver) UseLightColors() bool             { return s.light }\nfunc (s style_resolver) SyntaxAliases() map[string]string { return s.syntax_aliases }\nfunc (s style_resolver) TextForPath(path string) (string, error) {\n\tans, err := os.ReadFile(path)\n\tif err == nil {\n\t\treturn utils.UnsafeBytesToString(ans), nil\n\t}\n\treturn \"\", err\n}\n\nfunc (pm *PreviewManager) highlight_file_async(path string, output chan highlighed_data, ready *atomic.Bool) {\n\ts := style_resolver{light: use_light_colors, syntax_aliases: pm.settings.SyntaxAliases()}\n\ts.light_style, s.dark_style = pm.settings.HighlightStyles()\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\terr := parallel.Format_stacktrace_on_panic(r, 1)\n\t\t\t\toutput <- highlighed_data{err: err, light: s.light}\n\t\t\t}\n\t\t\tclose(output)\n\t\t\tready.Store(true)\n\t\t\tpm.WakeupMainThread()\n\t\t}()\n\t\thighlighted, err := pm.highlighter.HighlightFile(path, &s)\n\t\toutput <- highlighed_data{text: highlighted, err: err, light: s.light}\n\t}()\n}\n\nfunc (pm *PreviewManager) invalidate_color_scheme_based_cached_items() {\n\tpm.lock.Lock()\n\tdefer pm.lock.Unlock()\n\tmaps.DeleteFunc(pm.cache, func(key string, p Preview) bool { return !p.IsValidForColorScheme(use_light_colors) })\n}\n\nfunc (pm *PreviewManager) preview_for(abspath string, ftype fs.FileMode) (ans Preview) {\n\tif ans = pm.cached_preview(abspath); ans != nil {\n\t\treturn ans\n\t}\n\tdefer func() { pm.set_cached_preview(abspath, ans) }()\n\ts, err := os.Lstat(abspath)\n\tif err != nil {\n\t\treturn NewErrorPreview(err)\n\t}\n\tif s.IsDir() {\n\t\treturn NewDirectoryPreview(abspath, s)\n\t}\n\tif ftype&fs.ModeSymlink != 0 && ftype&SymlinkToDir != 0 {\n\t\ts, err = os.Stat(abspath)\n\t\tif err != nil {\n\t\t\treturn NewErrorPreview(err)\n\t\t}\n\t\treturn NewDirectoryPreview(abspath, s)\n\t}\n\tfname := filepath.Base(abspath)\n\tmt := utils.GuessMimeType(fname)\n\tfor _, q := range previewers {\n\t\tif q.matches(fname, mt) {\n\t\t\treturn NewCmdPreview(abspath, s, pm.settings, pm.WakeupMainThread, q)\n\t\t}\n\t}\n\tconst MAX_TEXT_FILE_SIZE = 16 * 1024 * 1024\n\tif s.Size() <= MAX_TEXT_FILE_SIZE && (utils.KnownTextualMimes[mt] || strings.HasPrefix(mt, \"text/\")) {\n\t\tch := make(chan highlighed_data, 2)\n\t\tans := NewTextFilePreview(abspath, s, ch, pm.highlighter.Sanitize)\n\t\tif p, ok := ans.(*TextFilePreview); ok {\n\t\t\tpm.highlight_file_async(abspath, ch, &p.ready)\n\t\t}\n\t\treturn ans\n\t}\n\tswitch {\n\tcase strings.HasPrefix(mt, \"image/\"):\n\t\tvar r ImagePreviewRenderer\n\t\tif ans, err := NewImagePreview(abspath, s, pm.settings, pm.WakeupMainThread, r); err == nil {\n\t\t\treturn ans\n\t\t} else {\n\t\t\treturn NewErrorPreview(err)\n\t\t}\n\n\tcase strings.HasPrefix(mt, \"video/\"):\n\t\treturn NewFFMpegPreview(abspath, s, pm.settings, pm.WakeupMainThread)\n\n\tcase IsSupportedArchiveFile(abspath):\n\t\treturn NewArchivePeview(abspath, s, pm.settings, pm.WakeupMainThread)\n\n\tcase IsSupportedByCalibre(abspath):\n\t\treturn NewCalibrePreview(abspath, s, pm.settings, pm.WakeupMainThread)\n\t}\n\treturn NewFileMetadataPreview(abspath, s)\n}\n\nfunc (h *Handler) clear_cached_previews() {\n\tif h.last_rendered_preview != nil {\n\t\th.last_rendered_preview.Unload()\n\t\th.last_rendered_preview = nil\n\t}\n\tif h.prev_preview_for_smooth_transition != nil {\n\t\th.prev_preview_for_smooth_transition.Unload()\n\t\th.prev_preview_for_smooth_transition = nil\n\t}\n}\n\nfunc (h *Handler) draw_preview_content(x, y, width, height int) {\n\tmatches, _ := h.get_results()\n\tr := matches.At(h.state.CurrentIndex())\n\tif r == nil {\n\t\th.render_wrapped_text_in_region(\"No preview available\", x, y, width, height, false)\n\t\treturn\n\t}\n\trender := func() {\n\t\tp := h.last_rendered_preview\n\t\tif p.IsReady() || h.prev_preview_for_smooth_transition == nil {\n\t\t\tp.Render(h, x, y, width, height)\n\t\t\tif h.prev_preview_for_smooth_transition != nil {\n\t\t\t\th.prev_preview_for_smooth_transition.Unload()\n\t\t\t\th.prev_preview_for_smooth_transition = nil\n\t\t\t}\n\t\t} else {\n\t\t\th.prev_preview_for_smooth_transition.Render(h, x, y, width, height)\n\t\t}\n\t}\n\n\tabspath := h.current_abspath()\n\tif h.last_rendered_preview != nil {\n\t\tif abspath == h.last_rendered_preview_abspath {\n\t\t\trender()\n\t\t\treturn\n\t\t}\n\t\tif h.prev_preview_for_smooth_transition != nil {\n\t\t\th.prev_preview_for_smooth_transition.Unload()\n\t\t}\n\t\th.prev_preview_for_smooth_transition = h.last_rendered_preview\n\t\th.last_rendered_preview = nil\n\t}\n\tif p := h.preview_manager.preview_for(abspath, r.ftype); p == nil {\n\t\th.render_wrapped_text_in_region(\"No preview available\", x, y, width, height, false)\n\t} else {\n\t\th.last_rendered_preview = p\n\t\th.last_rendered_preview_abspath = abspath\n\t\trender()\n\t}\n}\n"
  },
  {
    "path": "kittens/choose_files/results.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/icons\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nfunc (h *Handler) draw_results_title() {\n\ttext := filepath.Clean(h.state.CurrentDir())\n\thome := filepath.Clean(utils.Expanduser(\"~\"))\n\tif strings.HasPrefix(text, home) {\n\t\ttext = \"~\" + text[len(home):]\n\t}\n\ttext = sanitize(text)\n\tavailable_width := h.screen_size.width - 9\n\tif available_width < 2 {\n\t\treturn\n\t}\n\ttt := wcswidth.TruncateToVisualLength(text, available_width)\n\tif len(tt) < len(text) {\n\t\ttext = wcswidth.TruncateToVisualLength(text, available_width-1)\n\t}\n\ttext = fmt.Sprintf(\" %s %s \", h.lp.SprintStyled(\"fg=blue\", icons.IconForFileWithMode(text, fs.ModeDir, false)+\" \"), h.lp.SprintStyled(\"fg=intense-white bold\", text))\n\textra := available_width - wcswidth.Stringwidth(text)\n\tx := 3\n\tif extra > 1 {\n\t\tx += extra / 2\n\t}\n\th.lp.MoveCursorHorizontally(x)\n\th.lp.QueueWriteString(text)\n}\n\nfunc (h *Handler) draw_no_matches_message(in_progress bool) {\n\ttext := \"Scanning filesystem, please wait…\"\n\tif !in_progress {\n\t\ttext = utils.IfElse(h.state.SearchText() == \"\", \"No files present in this folder\", \"No matches found\")\n\t}\n\tfor _, line := range style.WrapTextAsLines(text, h.screen_size.width-2, style.WrapOptions{}) {\n\t\th.lp.QueueWriteString(\"\\r\")\n\t\th.lp.MoveCursorHorizontally(1)\n\t\th.lp.QueueWriteString(line)\n\t\th.lp.MoveCursorVertically(1)\n\t}\n\n}\n\nconst matching_position_style = \"fg=green\"\nconst selected_style = \"fg=magenta\"\nconst current_style = \"fg=intense-white bold\"\n\ntype text_chunk struct {\n\ttext      string\n\temphasize bool\n}\n\nfunc split_up_text(text string, add_ellipsis bool, positions []int) func(func(x text_chunk) bool) {\n\treturn func(yield func(x text_chunk) bool) {\n\t\tif len(positions) == 0 {\n\t\t\tif !yield(text_chunk{text, false}) {\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\tat := 0\n\t\t\trunes := []rune(text)\n\t\t\tlimit := len(runes)\n\t\t\tfor _, p := range positions {\n\t\t\t\tif max(p, at) >= limit || p < at {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif at < p && !yield(text_chunk{string(runes[at:p]), false}) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !yield(text_chunk{string(runes[p]), true}) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tat = p + 1\n\t\t\t}\n\t\t\tif at < len(runes) {\n\t\t\t\tif !yield(text_chunk{string(runes[at:]), false}) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif add_ellipsis {\n\t\t\tyield(text_chunk{\"…\", false})\n\t\t}\n\t}\n}\n\nfunc (h *Handler) render_match_with_positions(text string, add_ellipsis bool, positions []int, is_current bool) {\n\tprefix, suffix, _ := strings.Cut(h.lp.SprintStyled(matching_position_style, \" \"), \" \")\n\tif is_current {\n\t\tp, s, _ := strings.Cut(h.lp.SprintStyled(current_style, \" \"), \" \")\n\t\th.lp.QueueWriteString(p)\n\t\tdefer h.lp.QueueWriteString(s)\n\t\tsuffix += p\n\t}\n\tfor chunk := range split_up_text(text, add_ellipsis, positions) {\n\t\tif chunk.text != \"\" {\n\t\t\tif chunk.emphasize {\n\t\t\t\th.lp.QueueWriteString(prefix + chunk.text + suffix)\n\t\t\t} else {\n\t\t\t\th.lp.QueueWriteString(chunk.text)\n\t\t\t}\n\t\t}\n\t}\n}\n\nvar icon_cache map[string]string\n\nfunc icon_for(path string, x os.FileMode) string {\n\tif icon_cache == nil {\n\t\ticon_cache = make(map[string]string, 512)\n\t}\n\tif ans := icon_cache[path]; ans != \"\" {\n\t\treturn ans\n\t}\n\tvar ans string\n\tif x&fs.ModeSymlink != 0 && x&SymlinkToDir != 0 {\n\t\tans = string(icons.SYMLINK_TO_DIR)\n\t} else {\n\t\tans = icons.IconForFileWithMode(path, x, true)\n\t}\n\tif wcswidth.Stringwidth(ans) == 1 {\n\t\tans += \" \"\n\t}\n\ticon_cache[path] = ans\n\treturn ans\n}\n\nfunc (h *Handler) draw_column_of_matches(matches ResultsType, current_idx int, x, y, available_width, colnum int) {\n\troot_dir := h.state.CurrentDir()\n\tfor i, m := range matches {\n\t\th.lp.QueueWriteString(\"\\r\")\n\t\th.lp.MoveCursorHorizontally(x)\n\t\tis_selected := h.state.IsSelected(m)\n\t\tvar icon string\n\t\tif is_selected {\n\t\t\ticon = \"󰗠 \"\n\t\t} else {\n\t\t\ticon = icon_for(filepath.Join(root_dir, m.text), m.ftype)\n\t\t}\n\t\ttext := sanitize(m.text)\n\t\tadd_ellipsis := false\n\t\twidth := wcswidth.Stringwidth(text)\n\t\tif width > available_width-3 {\n\t\t\ttext = wcswidth.TruncateToVisualLength(text, available_width-4)\n\t\t\tadd_ellipsis = true\n\t\t\twidth = available_width - 3\n\t\t}\n\t\tis_current := i == current_idx\n\t\tif is_current {\n\t\t\th.lp.QueueWriteString(h.lp.SprintStyled(matching_position_style, icon+\" \"))\n\t\t} else {\n\t\t\tif is_selected {\n\t\t\t\th.lp.QueueWriteString(h.lp.SprintStyled(selected_style, icon+\" \"))\n\t\t\t} else {\n\t\t\t\th.lp.QueueWriteString(icon + \" \")\n\t\t\t}\n\t\t}\n\t\th.render_match_with_positions(text, add_ellipsis, m.sorted_positions(), is_current)\n\t\th.lp.MoveCursorVertically(1)\n\t\tcr := h.state.mouse_state.AddCellRegion(fmt.Sprintf(\"result-%d-%d\", colnum, i), x, y-1+i, x+width+2, y-1+i)\n\t\tcr.HoverStyle = HOVER_STYLE\n\t\tvar data struct {\n\t\t\tcolnum, i int\n\t\t}\n\t\tdata.colnum, data.i = colnum, i\n\t\tcr.OnClickEvent = func(id string, ev *loop.MouseEvent, cell_offset tui.Point) error {\n\t\t\tif ev.Buttons&loop.LEFT_MOUSE_BUTTON == 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tctrl_mod := utils.IfElse(runtime.GOOS == \"darwin\", loop.SUPER, loop.CTRL)\n\t\t\tmods := ev.Mods & (ctrl_mod | loop.ALT) // shift alone and ctrl+shift are used for kitty bindings\n\t\t\tmatches, _ := h.get_results()\n\t\t\tnum_before := h.state.last_render.num_of_slots*data.colnum + data.i\n\t\t\tidx, did_wrap := matches.IncrementIndexWithWrapAroundAndCheck(h.state.last_render.first_idx, num_before)\n\t\t\tif did_wrap {\n\t\t\t\th.lp.Beep()\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\td := matches.SignedDistance(idx, h.state.current_idx)\n\t\t\th.state.SetCurrentIndex(idx)\n\t\t\th.state.last_render.num_before = max(0, h.state.last_render.num_before+d)\n\t\t\tswitch mods {\n\t\t\tcase 0:\n\t\t\t\th.dispatch_action(\"accept\", \"\")\n\t\t\tcase ctrl_mod, ctrl_mod | loop.ALT:\n\t\t\t\th.dispatch_action(\"select\", \"\")\n\t\t\tcase loop.ALT:\n\t\t\t\tr := matches.At(idx)\n\t\t\t\tif (r != nil && h.state.IsSelected(r)) || h.result_manager.last_click_anchor == nil {\n\t\t\t\t\th.dispatch_action(\"select\", \"\")\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\talready_selected := utils.NewSetWithItems(h.state.selections...)\n\t\t\t\tcdir := h.state.CurrentDir()\n\t\t\t\tmatches.Apply(idx, *h.result_manager.last_click_anchor, func(r *ResultItem) bool {\n\t\t\t\t\tm := filepath.Join(cdir, r.text)\n\t\t\t\t\tif !already_selected.Has(m) && h.state.CanSelect(r) {\n\t\t\t\t\t\talready_selected.Add(m)\n\t\t\t\t\t\th.state.selections = append(h.state.selections, m)\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t\t\treturn h.draw_screen()\n\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc (h *Handler) draw_list_of_results(matches *SortedResults, y, height int) (num_cols, num_shown, preview_width int) {\n\tconst BASE_COL_WIDTH = 40\n\tavailable_width := h.screen_size.width - 2\n\tshow_preview := h.state.ShowPreview()\n\tif show_preview && available_width < BASE_COL_WIDTH+30 {\n\t\tshow_preview = false\n\t}\n\tif show_preview {\n\t\tswitch {\n\t\tcase available_width < BASE_COL_WIDTH*2:\n\t\t\tpreview_width = max(30, available_width/2)\n\t\tdefault:\n\t\t\tpreview_width = BASE_COL_WIDTH\n\t\t}\n\t\tavailable_width -= preview_width\n\t}\n\tcol_width := available_width\n\tnum_cols = 1\n\tcalc_num_cols := func(num_matches int) int {\n\t\tif num_matches == 0 || height < 2 {\n\t\t\treturn 0\n\t\t}\n\t\tif num_matches > height {\n\t\t\tcol_width = BASE_COL_WIDTH\n\t\t\tnum_cols = max(1, available_width/col_width)\n\t\t\tfor num_cols > 1 && height*(num_cols-1) >= num_matches {\n\t\t\t\tnum_cols--\n\t\t\t}\n\t\t\tcol_width = available_width / num_cols\n\t\t}\n\t\treturn num_cols\n\t}\n\tcolumns, num_before, first_idx := matches.SplitIntoColumns(calc_num_cols, height, h.state.last_render.num_before, h.state.CurrentIndex())\n\th.state.last_render.num_before = num_before\n\th.state.last_render.num_per_column = height\n\th.state.last_render.num_columns = num_cols\n\th.state.last_render.first_idx = first_idx\n\tx := 1\n\tfor i, col := range columns {\n\t\th.lp.MoveCursorTo(x, y)\n\t\th.draw_column_of_matches(col, num_before, x, y, col_width-1, i)\n\t\tnum_before -= height\n\t\tnum_shown += len(col)\n\t\tx += col_width\n\t}\n\treturn len(columns), num_shown, preview_width\n}\n\nfunc (h *Handler) draw_num_of_matches(num_shown, y int, in_progress bool) {\n\tm := \"\"\n\tswitch h.state.last_render.num_matches {\n\tcase 0:\n\t\tm = \" no matches \"\n\tdefault:\n\t\tm = fmt.Sprintf(\" %d of %s matches \", min(num_shown, h.state.last_render.num_matches), h.msg_printer.Sprint(h.state.last_render.num_matches))\n\t}\n\tw := int(math.Ceil(float64(wcswidth.Stringwidth(m)) / 2.0))\n\tspinner := \"\"\n\tspinner_width := 0\n\tif in_progress {\n\t\tspinner = h.spinner.Tick()\n\t\tspinner_width = 1 + wcswidth.Stringwidth(spinner)\n\t}\n\th.lp.MoveCursorTo(h.screen_size.width-w-spinner_width-2, y)\n\tst := loop.SizedText{Subscale_denominator: 2, Subscale_numerator: 1, Vertical_alignment: 2, Width: 1}\n\tgraphemes := wcswidth.SplitIntoGraphemes(m)\n\tfor len(graphemes) > 0 {\n\t\ts := \"\"\n\t\tfor w := 0; w < 2 && len(graphemes) > 0; {\n\t\t\tw += wcswidth.Stringwidth(graphemes[0])\n\t\t\ts += graphemes[0]\n\t\t\tgraphemes = graphemes[1:]\n\t\t}\n\t\th.lp.DrawSizedText(s, st)\n\t}\n\tif spinner != \"\" {\n\t\th.lp.QueueWriteString(spinner)\n\t}\n}\n\nfunc (h *Handler) draw_preview(y int) {\n\tx := h.screen_size.width - h.state.last_render.preview_width\n\theight := h.state.last_render.num_of_slots\n\tbuf := strings.Builder{}\n\tbuf.Grow(16 * height)\n\tbuf.WriteString(fmt.Sprintf(loop.MoveCursorToTemplate, y-1, x))\n\tbuf.WriteString(\"┬\")\n\tfor i := range height {\n\t\tbuf.WriteString(fmt.Sprintf(loop.MoveCursorToTemplate, y+i, x))\n\t\tbuf.WriteString(\"│\")\n\t}\n\tbuf.WriteString(fmt.Sprintf(loop.MoveCursorToTemplate, y+height, x))\n\tbuf.WriteString(\"┴\")\n\th.lp.QueueWriteString(buf.String())\n\th.draw_preview_content(x+1, y, h.state.last_render.preview_width-1, h.state.last_render.num_of_slots)\n}\n\nfunc (h *Handler) draw_results(y, bottom_margin int, matches *SortedResults, in_progress bool) (height int) {\n\theight = h.screen_size.height - y - bottom_margin\n\th.lp.MoveCursorTo(1, 1+y)\n\th.draw_frame(h.screen_size.width, height, in_progress)\n\th.lp.MoveCursorTo(1, 1+y)\n\th.draw_results_title()\n\ty += 2\n\th.lp.MoveCursorTo(1, y)\n\th.state.last_render.num_of_slots = height - 2\n\tnum_cols := 0\n\tnum := matches.Len()\n\tnum_shown := 0\n\th.state.last_render.preview_width = 0\n\tswitch num {\n\tcase 0:\n\t\th.draw_no_matches_message(in_progress)\n\tdefault:\n\t\tnum_cols, num_shown, h.state.last_render.preview_width = h.draw_list_of_results(matches, y, h.state.last_render.num_of_slots)\n\t}\n\th.state.last_render.num_matches = num\n\th.state.last_render.num_shown = num_shown\n\th.draw_num_of_matches(h.state.last_render.num_of_slots*num_cols, y+height-2, in_progress)\n\tif h.state.last_render.preview_width > 0 {\n\t\th.draw_preview(y)\n\t}\n\treturn\n}\n\nfunc (h *Handler) next_result(amt int) {\n\tif h.state.last_render.num_matches > 0 {\n\t\tidx := h.state.CurrentIndex()\n\t\tidx = h.result_manager.scorer.sorted_results.IncrementIndexWithWrapAround(idx, amt)\n\t\th.state.SetCurrentIndex(idx)\n\t\th.state.last_render.num_before = max(0, h.state.last_render.num_before+amt)\n\t}\n}\n\nfunc (h *Handler) move_sideways(leftwards bool) {\n\tr := h.state.last_render\n\tif r.num_matches > 0 && r.num_per_column > 0 {\n\t\tcidx := h.state.CurrentIndex()\n\t\tslots := r.num_of_slots\n\t\tif leftwards {\n\t\t\tidx := h.result_manager.scorer.sorted_results.IncrementIndexWithWrapAround(cidx, -slots)\n\t\t\tif idx.Less(cidx) {\n\t\t\t\th.state.SetCurrentIndex(idx)\n\t\t\t\tif r.num_columns > 1 && r.num_before >= r.num_per_column {\n\t\t\t\t\th.state.last_render.num_before = max(0, h.state.last_render.num_before-slots)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tidx := h.result_manager.scorer.sorted_results.IncrementIndexWithWrapAround(cidx, slots)\n\t\t\tif cidx.Less(idx) {\n\t\t\t\th.state.SetCurrentIndex(idx)\n\t\t\t\tif r.num_columns > 1 && r.num_before < (r.num_columns-1)*r.num_per_column {\n\t\t\t\t\th.state.last_render.num_before = max(0, h.state.last_render.num_before+slots)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "kittens/choose_files/results_test.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestSplitWithPositions(t *testing.T) {\n\tfor _, c := range []struct {\n\t\tsrc       string\n\t\tpositions []int\n\t\texpected  []string\n\t}{\n\t\t{\"abc\", nil, []string{\"abc\"}},\n\t\t{\"abc\", []int{0}, []string{\"a\", \"bc\"}},\n\t\t{\"abc\", []int{1}, []string{\"a\", \"b\", \"c\"}},\n\t\t{\"abc\", []int{2}, []string{\"ab\", \"c\"}},\n\t\t{\"abc\", []int{0, 1}, []string{\"a\", \"b\", \"c\"}},\n\t\t{\"abc\", []int{0, 1, 2}, []string{\"a\", \"b\", \"c\"}},\n\t\t{\"abc\", []int{0, 2}, []string{\"a\", \"b\", \"c\"}},\n\t\t// invalid positions\n\t\t{\"abc\", []int{-1}, []string{\"abc\"}},\n\t\t{\"abc\", []int{3}, []string{\"abc\"}},\n\t\t{\"abc\", []int{0, 3}, []string{\"a\", \"bc\"}},\n\t\t{\"abc\", []int{2, 0}, []string{\"ab\", \"c\"}},\n\t\t{\"abc\", []int{2, 1}, []string{\"ab\", \"c\"}},\n\t\t{\"abc\", []int{1, 0}, []string{\"a\", \"b\", \"c\"}},\n\t} {\n\t\tactual := make([]string, 0, len(c.expected))\n\t\tfor ch := range split_up_text(c.src, false, c.positions) {\n\t\t\tactual = append(actual, ch.text)\n\t\t}\n\t\tif diff := cmp.Diff(c.expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed for src: %#v positions: %v\\n%s\", c.src, c.positions, diff)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "kittens/choose_files/save-file.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc FilePromptCompleter(getcwd func() string) func(string, string) *cli.Completions {\n\tif getcwd == nil {\n\t\tgetcwd = func() string {\n\t\t\tans, err := os.Getwd()\n\t\t\tif err != nil {\n\t\t\t\tans = \".\"\n\t\t\t}\n\t\t\treturn ans\n\t\t}\n\t}\n\treturn func(before_cursor, after_cursor string) (ans *cli.Completions) {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\terr := parallel.Format_stacktrace_on_panic(r, 1)\n\t\t\t\tdebugprintln(err)\n\t\t\t}\n\t\t}()\n\t\tans = cli.NewCompletions()\n\t\tpath := before_cursor\n\t\tprefix := \"\"\n\t\tif idx := strings.Index(path, string(os.PathSeparator)); idx > -1 {\n\t\t\td := filepath.Dir(path)\n\t\t\tprefix = d + utils.IfElse(strings.HasSuffix(d, string(os.PathSeparator)), \"\", string(os.PathSeparator))\n\t\t}\n\t\tdir := \"\"\n\t\tif path == \"\" {\n\t\t\tpath = getcwd()\n\t\t\tdir = path\n\t\t} else {\n\t\t\tif !filepath.IsAbs(path) {\n\t\t\t\tpath = filepath.Join(getcwd(), path)\n\t\t\t}\n\t\t\tdir = filepath.Dir(path)\n\t\t\tif strings.HasSuffix(before_cursor, string(os.PathSeparator)) {\n\t\t\t\tdir = path\n\t\t\t}\n\t\t}\n\t\tentries, err := os.ReadDir(dir)\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tdirs := ans.AddMatchGroup(\"Directories\")\n\t\tdirs.IsFiles = true\n\t\tdirs.NoTrailingSpace = true\n\t\tfiles := ans.AddMatchGroup(\"Files\")\n\t\tfiles.IsFiles = true\n\t\tfiles.NoTrailingSpace = true\n\t\tleading, _ := filepath.Rel(dir, path)\n\t\tif leading == \".\" {\n\t\t\tleading = \"\"\n\t\t}\n\t\tfor _, e := range entries {\n\t\t\tword := e.Name()\n\t\t\tif leading == \"\" || strings.HasPrefix(word, leading) {\n\t\t\t\tcollection := utils.IfElse(e.Type().IsDir(), dirs, files)\n\t\t\t\tif prefix != \"\" {\n\t\t\t\t\tword = prefix + word\n\t\t\t\t}\n\t\t\t\tcollection.Matches = append(collection.Matches, &cli.Match{Word: word})\n\t\t\t}\n\t\t}\n\t\treturn ans\n\t}\n}\n\nfunc (h *Handler) current_save_file_path() string {\n\tans := h.rl.AllText()\n\tif ans != \"\" {\n\t\tans = utils.Expanduser(ans)\n\t\tif !filepath.IsAbs(ans) {\n\t\t\tans = filepath.Join(h.state.CurrentDir(), ans)\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc (h *Handler) save_file_name_handle_key(ev *loop.KeyEvent) (err error) {\n\tac := h.shortcut_tracker.Match(ev, h.state.keyboard_shortcuts)\n\tif ac != nil {\n\t\tswitch ac.Name {\n\t\tcase \"accept\":\n\t\t\tif p := h.current_save_file_path(); p != \"\" {\n\t\t\t\th.state.selections = append(h.state.selections, p)\n\t\t\t\th.lp.Quit(0)\n\t\t\t} else {\n\t\t\t\th.lp.Beep()\n\t\t\t}\n\t\t\tev.Handled = true\n\t\t\treturn nil\n\t\tcase \"select\":\n\t\t\tif p := h.current_save_file_path(); p != \"\" {\n\t\t\t\th.state.selections = append(h.state.selections, p)\n\t\t\t}\n\t\t\tev.Handled = true\n\t\t\th.rl.ResetText()\n\t\t\treturn h.draw_screen()\n\t\tcase \"quit\":\n\t\t\th.state.screen = NORMAL\n\t\t\tev.Handled = true\n\t\t\treturn h.draw_screen()\n\t\t}\n\t}\n\tif err = h.rl.OnKeyEvent(ev); err == nil {\n\t\terr = h.draw_screen()\n\t}\n\treturn\n}\n\nfunc (h *Handler) initialize_save_file_name(fname string) {\n\th.state.screen = SAVE_FILE\n\th.rl.ResetText()\n\tif len(h.state.selections) > 0 && fname == \"\" {\n\t\tif q, err := filepath.Abs(h.state.selections[0]); err == nil {\n\t\t\tif s, err := os.Stat(q); err == nil {\n\t\t\t\tif s.IsDir() == h.state.mode.OnlyDirs() {\n\t\t\t\t\tif fname, err = filepath.Rel(h.state.CurrentDir(), q); err != nil {\n\t\t\t\t\t\tfname = q\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\th.rl.SetText(fname)\n}\n\nfunc (h *Handler) draw_save_file_name_screen() (err error) {\n\th.lp.AllowLineWrapping(true)\n\tdesc := utils.IfElse(h.state.mode == SELECT_SAVE_FILE, \"file\", \"directory\")\n\tif h.state.DisplayTitle() {\n\t\th.lp.Println(h.state.WindowTitle())\n\t}\n\th.lp.Println(\"Enter the name of the\", desc, \"below, relative to:\")\n\th.lp.Println(h.lp.SprintStyled(\"fg=green\", h.state.CurrentDir()))\n\tif h.state.mode.AllowsMultipleSelection() {\n\t\th.lp.Println(\"Use shift+enter (or whatever you mapped the select action to) to enter multiple filenames\")\n\t}\n\th.lp.Println()\n\th.rl.RedrawNonAtomic()\n\th.lp.AllowLineWrapping(false)\n\tif len(h.state.selections) > 0 {\n\t\th.lp.SaveCursorPosition()\n\t\th.draw_footer()\n\t\th.lp.RestoreCursorPosition()\n\t}\n\treturn\n}\n"
  },
  {
    "path": "kittens/choose_files/scan.go",
    "content": "package choose_files\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/fzf\"\n\t\"github.com/kovidgoyal/kitty/tools/ignorefiles\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc (c CombinedScore) String() string {\n\treturn fmt.Sprintf(\"{score: %d length: %d index: %d}\", c.Score(), c.Length(), c.Index())\n}\n\ntype ignore_file_with_prefix struct {\n\timpl   ignorefiles.IgnoreFile\n\tprefix string\n}\n\nfunc (i *ignore_file_with_prefix) is_ignored(name string, ftype fs.FileMode) (ans bool, was_match bool) {\n\tans, linenum, _ := i.impl.IsIgnored(i.prefix+name, ftype)\n\twas_match = linenum > -1\n\treturn\n}\n\ntype ResultItem struct {\n\ttext         string\n\tftype        fs.FileMode\n\tpositions    []int // may be nil\n\tscore        CombinedScore\n\tignore_files []ignore_file_with_prefix\n}\ntype ResultsType []*ResultItem\n\nfunc (r *ResultItem) SetScoreResult(x fzf.Result) {\n\tr.positions = x.Positions\n\tr.score.Set_score(uint16(math.MaxUint16 - uint16(x.Score)))\n}\n\nfunc (r ResultItem) IsMatching() bool {\n\treturn r.score.Score() < uint16(math.MaxUint16)\n}\n\nfunc (r ResultItem) String() string {\n\treturn fmt.Sprintf(\"{text: %#v, %s, positions: %#v}\", r.text, r.score, r.positions)\n}\n\nfunc (r *ResultItem) sorted_positions() []int {\n\tif len(r.positions) > 1 {\n\t\tsort.Ints(r.positions)\n\t}\n\treturn r.positions\n}\n\ntype FileSystemScanner struct {\n\tlisteners                    []chan bool\n\tin_progress, keep_going      atomic.Bool\n\troot_dir                     string\n\tmutex                        sync.Mutex\n\tcollection                   *ResultCollection\n\tdir_reader                   func(path string) ([]fs.DirEntry, error)\n\tfile_reader                  func(path string) ([]byte, error)\n\tfilter_func                  func(filename string) bool\n\tglobal_gitignore             ignorefiles.IgnoreFile\n\tglobal_ignore                ignorefiles.IgnoreFile\n\trespect_ignores, show_hidden bool\n\tsort_by_last_modified        bool\n\n\terr error\n}\n\nfunc new_filesystem_scanner(root_dir string, notify chan bool, filter_func func(string) bool) (fss *FileSystemScanner) {\n\tans := &FileSystemScanner{root_dir: root_dir, listeners: []chan bool{notify}, collection: NewResultCollection(4096)}\n\tans.in_progress.Store(true)\n\tans.keep_going.Store(true)\n\tans.dir_reader = os.ReadDir\n\tans.file_reader = os.ReadFile\n\tans.filter_func = utils.IfElse(filter_func == nil, accept_all, filter_func)\n\tans.global_gitignore = ignorefiles.NewGitignore()\n\tans.global_ignore = ignorefiles.NewGitignore()\n\tans.respect_ignores = true\n\tans.show_hidden = false\n\treturn ans\n}\n\ntype Scanner interface {\n\tStart()\n\tCancel()\n\tAddListener(chan bool)\n\tLen() int\n\tBatch(offset *CollectionIndex) []ResultItem\n\tFinished() bool\n\tError() error\n}\n\nfunc (fss *FileSystemScanner) lock()   { fss.mutex.Lock() }\nfunc (fss *FileSystemScanner) unlock() { fss.mutex.Unlock() }\n\nfunc (fss *FileSystemScanner) Error() error {\n\tfss.lock()\n\tdefer fss.unlock()\n\treturn fss.err\n}\n\nfunc (fss *FileSystemScanner) Start() {\n\tgo fss.worker()\n}\n\nfunc (fss *FileSystemScanner) Cancel() {\n\tfss.keep_going.Store(false)\n}\n\nfunc (fss *FileSystemScanner) AddListener(x chan bool) {\n\tfss.lock()\n\tdefer fss.unlock()\n\tif !fss.in_progress.Load() {\n\t\tclose(x)\n\t} else {\n\t\tfss.listeners = append(fss.listeners, x)\n\t}\n}\n\nfunc (fss *FileSystemScanner) Len() int {\n\tfss.lock()\n\tdefer fss.unlock()\n\treturn fss.collection.Len()\n}\n\nfunc (fss *FileSystemScanner) Batch(offset *CollectionIndex) []ResultItem {\n\tfss.lock()\n\tdefer fss.unlock()\n\treturn fss.collection.Batch(offset)\n}\n\nfunc (fss *FileSystemScanner) Finished() bool {\n\treturn !fss.in_progress.Load()\n}\n\ntype sortable_dir_entry struct {\n\tname     string\n\tftype    fs.FileMode\n\tsort_key []byte\n\tbuf      [unix.NAME_MAX + 1]byte\n}\n\nconst SymlinkToDir = 1\n\n// lowercase a string into a pre-existing byte buffer with speedups for ASCII\nfunc as_lower(s string, output []byte) int {\n\tlimit := min(len(s), len(output))\n\tfound_non_ascii := false\n\tpos := 0\n\tfor i := range limit {\n\t\tc := s[i]\n\t\tif 'A' <= c && c <= 'Z' {\n\t\t\tc += 'a' - 'A'\n\t\t\tif pos < i {\n\t\t\t\tcopy(output[pos:i], s[pos:i])\n\t\t\t}\n\t\t\toutput[i] = c\n\t\t\tpos = i + 1\n\t\t} else if c >= utf8.RuneSelf {\n\t\t\tif pos < i {\n\t\t\t\tcopy(output[pos:i], s[pos:i])\n\t\t\t}\n\t\t\tfound_non_ascii = true\n\t\t\tpos = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif !found_non_ascii {\n\t\tif pos < limit {\n\t\t\tcopy(output[pos:limit], s[pos:limit])\n\t\t}\n\t\treturn limit\n\t}\n\tbuf := [4]byte{}\n\tvar n int\n\tfor _, r := range s[pos:] {\n\t\to := output[pos:]\n\t\tr = unicode.ToLower(r)\n\t\tif len(o) > 3 {\n\t\t\tn = utf8.EncodeRune(o, r)\n\t\t} else {\n\t\t\tn = utf8.EncodeRune(buf[:], r)\n\t\t\tn = copy(o, buf[:n])\n\t\t}\n\t\tpos += n\n\t\tif pos >= len(output) {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn pos\n}\n\nfunc accept_all(filename string) bool { return true }\n\nfunc (fss *FileSystemScanner) worker() {\n\tdefer func() {\n\t\tfss.lock()\n\t\tdefer fss.unlock()\n\t\tfss.in_progress.Store(false)\n\t\tif r := recover(); r != nil {\n\t\t\tqerr := parallel.Format_stacktrace_on_panic(r, 1)\n\t\t\tfss.err = qerr\n\t\t}\n\t\tfor _, l := range fss.listeners {\n\t\t\tclose(l)\n\t\t}\n\t}()\n\troot_dir, _ := filepath.Abs(fss.root_dir)\n\tif !strings.HasSuffix(root_dir, string(os.PathSeparator)) {\n\t\troot_dir += string(os.PathSeparator)\n\t}\n\tdir := root_dir\n\tvar ignore_files []ignore_file_with_prefix\n\tbase := \"\"\n\tpos := &CollectionIndex{}\n\tvar arena []sortable_dir_entry\n\tvar sortable []*sortable_dir_entry\n\tvar ignoreable []*sortable_dir_entry\n\tvar idx uint32\n\tdot_git := os.Getenv(\"GIT_DIR\")\n\tif dot_git == \"\" {\n\t\tdot_git = \".git\"\n\t}\n\t// do a breadth first traversal of the filesystem\n\tis_root := true\n\tfor dir != \"\" {\n\t\tif !fss.keep_going.Load() {\n\t\t\tbreak\n\t\t}\n\t\tentries, err := fss.dir_reader(dir)\n\t\tif err != nil {\n\t\t\tif is_root {\n\t\t\t\tfss.keep_going.Store(false)\n\t\t\t\tfss.lock()\n\t\t\t\tfss.err = err\n\t\t\t\tfss.unlock()\n\t\t\t}\n\t\t\tentries = nil\n\t\t}\n\t\tif cap(arena) < len(entries) {\n\t\t\tarena = make([]sortable_dir_entry, 0, max(1024, len(entries), 2*cap(arena)))\n\t\t\tsortable = make([]*sortable_dir_entry, 0, cap(arena))\n\t\t\tignoreable = make([]*sortable_dir_entry, 0, cap(arena))\n\t\t}\n\t\tarena = arena[:len(entries)]\n\t\tsortable = sortable[:0]\n\t\tignoreable = ignoreable[:0]\n\t\tignore_files_copied := false\n\t\tadd_ignore_file_from_impl := func(impl ignorefiles.IgnoreFile) {\n\t\t\t// we want ignore_files to be a copy as we dont want to\n\t\t\t// change the underlying array of ignore_files as it is\n\t\t\t// referenced by multiple ResultItems\n\t\t\tif !ignore_files_copied {\n\t\t\t\tignore_files_copied = true\n\t\t\t\tn := make([]ignore_file_with_prefix, len(ignore_files), len(ignore_files)+4)\n\t\t\t\tcopy(n, ignore_files)\n\t\t\t\tignore_files = n\n\t\t\t}\n\t\t\tignore_files = append(ignore_files, ignore_file_with_prefix{impl: impl})\n\t\t}\n\t\tadd_ignore_file := func(name string) {\n\t\t\tif data, rerr := fss.file_reader(dir + name); rerr == nil {\n\t\t\t\timpl := ignorefiles.NewGitignore()\n\t\t\t\tif rerr = impl.LoadString(utils.UnsafeBytesToString(data)); rerr == nil && impl.Len() > 0 {\n\t\t\t\t\tadd_ignore_file_from_impl(impl)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tentry_is_ignored := func(name string, ftype fs.FileMode) (is_ignored bool) {\n\t\t\tfor _, ignore_file := range ignore_files {\n\t\t\t\tif iig, was_match := ignore_file.is_ignored(name, ftype); was_match {\n\t\t\t\t\tis_ignored = iig\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\thas_git_ignore, has_dot_git, has_dot_ignore := false, false, false\n\t\tsort_order := 1\n\t\tfor i, e := range entries {\n\t\t\tname := e.Name()\n\t\t\tftype := e.Type()\n\t\t\tis_dir := ftype&fs.ModeDir != 0\n\t\t\tif !is_dir {\n\t\t\t\tswitch name {\n\t\t\t\tcase \".ignore\":\n\t\t\t\t\thas_dot_ignore = true\n\t\t\t\tcase \".gitignore\":\n\t\t\t\t\thas_git_ignore = true\n\t\t\t\t}\n\t\t\t\tif !fss.filter_func(name) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif name == dot_git {\n\t\t\t\t\thas_dot_git = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !fss.show_hidden && name[0] == '.' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tarena[i].name = name\n\t\t\tif ftype&fs.ModeSymlink != 0 {\n\t\t\t\tif st, serr := os.Stat(dir + arena[i].name); serr == nil && st.IsDir() {\n\t\t\t\t\tftype |= SymlinkToDir\n\t\t\t\t}\n\t\t\t}\n\t\t\tarena[i].ftype = ftype\n\t\t\tif is_dir {\n\t\t\t\tarena[i].buf[0] = '0'\n\t\t\t} else {\n\t\t\t\tarena[i].buf[0] = '1'\n\t\t\t}\n\t\t\tif fss.sort_by_last_modified {\n\t\t\t\tvar ts time.Time\n\t\t\t\tif info, err := e.Info(); err == nil {\n\t\t\t\t\tts = info.ModTime()\n\t\t\t\t}\n\t\t\t\tbinary.BigEndian.PutUint64(arena[i].buf[1:], uint64(ts.UnixNano()))\n\t\t\t\tarena[i].sort_key = arena[i].buf[:1+8]\n\t\t\t\tsort_order = -1\n\t\t\t} else {\n\t\t\t\tn := as_lower(arena[i].name, arena[i].buf[1:])\n\t\t\t\tarena[i].sort_key = arena[i].buf[:1+n]\n\t\t\t}\n\t\t\tsortable = append(sortable, &arena[i])\n\t\t}\n\t\tif fss.respect_ignores {\n\t\t\tif is_root && fss.global_ignore.Len() > 0 {\n\t\t\t\tadd_ignore_file_from_impl(fss.global_ignore)\n\t\t\t}\n\t\t\tif has_dot_git {\n\t\t\t\tif fss.global_gitignore.Len() > 0 {\n\t\t\t\t\tadd_ignore_file_from_impl(fss.global_gitignore)\n\t\t\t\t}\n\t\t\t\tadd_ignore_file(filepath.Join(dot_git, \"info\", \"exclude\"))\n\t\t\t\tif has_git_ignore {\n\t\t\t\t\tadd_ignore_file(\".gitignore\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif has_dot_ignore {\n\t\t\t\tadd_ignore_file(\".ignore\")\n\t\t\t}\n\t\t}\n\t\tfinal_entries := sortable\n\t\tif len(ignore_files) > 0 {\n\t\t\tfor _, e := range sortable {\n\t\t\t\tif !entry_is_ignored(e.name, e.ftype) {\n\t\t\t\t\tignoreable = append(ignoreable, e)\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tfinal_entries = ignoreable\n\t\t}\n\t\tslices.SortFunc(final_entries, func(a, b *sortable_dir_entry) int { return sort_order * bytes.Compare(a.sort_key, b.sort_key) })\n\t\tfss.lock()\n\t\tfor _, e := range final_entries {\n\t\t\ti := fss.collection.NextAppendPointer()\n\t\t\ti.ftype = e.ftype\n\t\t\ti.text = base + e.name\n\t\t\ti.score.Set_index(idx)\n\t\t\ti.ignore_files = ignore_files\n\t\t\tidx++\n\t\t}\n\t\tlisteners := fss.listeners\n\t\tfss.unlock()\n\t\tfor _, l := range listeners {\n\t\t\tselect {\n\t\t\tcase l <- true:\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t\tignore_files = nil\n\t\tif relpath, ignf := fss.collection.NextDir(pos); relpath != \"\" {\n\t\t\tbase = relpath + string(os.PathSeparator)\n\t\t\tdir = root_dir + base\n\t\t\tif len(ignf) != 0 {\n\t\t\t\tname := filepath.Base(relpath) + string(os.PathSeparator)\n\t\t\t\tignore_files = utils.Map(func(ignore_file ignore_file_with_prefix) ignore_file_with_prefix {\n\t\t\t\t\treturn ignore_file_with_prefix{impl: ignore_file.impl, prefix: ignore_file.prefix + name}\n\t\t\t\t}, ignf)\n\t\t\t}\n\t\t} else {\n\t\t\tdir = \"\"\n\t\t}\n\t\tis_root = false\n\t}\n}\n\ntype FileSystemScorer struct {\n\tscanner                         Scanner\n\tkeep_going, is_complete         atomic.Bool\n\troot_dir, query                 string\n\tfilter                          Filter\n\tonly_dirs                       bool\n\tmutex                           sync.Mutex\n\tsorted_results                  *SortedResults\n\ton_results                      func(error, bool)\n\tcurrent_worker_wait             *sync.WaitGroup\n\tscorer                          *fzf.FuzzyMatcher\n\tdir_reader                      func(path string) ([]fs.DirEntry, error)\n\tfile_reader                     func(path string) ([]byte, error)\n\tglobal_gitignore, global_ignore ignorefiles.IgnoreFile\n\trespect_ignores, show_hidden    bool\n\tsort_by_last_modified           bool\n}\n\nfunc NewFileSystemScorer(root_dir, query string, filter Filter, only_dirs bool, on_results func(error, bool)) (ans *FileSystemScorer) {\n\treturn &FileSystemScorer{\n\t\tquery: query, root_dir: root_dir, only_dirs: only_dirs, filter: filter, on_results: on_results,\n\t\tscorer: fzf.NewFuzzyMatcher(fzf.PATH_SCHEME), sorted_results: NewSortedResults(), respect_ignores: true,\n\t}\n}\n\nfunc (fss *FileSystemScorer) lock()   { fss.mutex.Lock() }\nfunc (fss *FileSystemScorer) unlock() { fss.mutex.Unlock() }\n\nfunc (fss *FileSystemScorer) Start() {\n\ton_results := make(chan bool)\n\tfss.is_complete.Store(false)\n\tfss.keep_going.Store(true)\n\tif fss.scanner == nil {\n\t\tsc := new_filesystem_scanner(fss.root_dir, on_results, fss.filter.Match)\n\t\tif fss.dir_reader != nil {\n\t\t\tsc.dir_reader = fss.dir_reader\n\t\t}\n\t\tif fss.file_reader != nil {\n\t\t\tsc.file_reader = fss.file_reader\n\t\t}\n\t\tif fss.global_gitignore != nil {\n\t\t\tsc.global_gitignore = fss.global_gitignore\n\t\t} else if ignore := ignorefiles.GlobalGitignore(); ignore != nil {\n\t\t\tsc.global_gitignore = ignore\n\t\t}\n\t\tif fss.global_ignore != nil {\n\t\t\tsc.global_ignore = fss.global_ignore\n\t\t}\n\t\tsc.show_hidden, sc.respect_ignores = fss.show_hidden, fss.respect_ignores\n\t\tsc.sort_by_last_modified = fss.sort_by_last_modified\n\t\tfss.scanner = sc\n\t\tfss.scanner.Start()\n\t} else {\n\t\tfss.scanner.AddListener(on_results)\n\t}\n\tfss.current_worker_wait = &sync.WaitGroup{}\n\tfss.current_worker_wait.Add(1)\n\tgo fss.worker(on_results, fss.current_worker_wait)\n}\n\nfunc (fss *FileSystemScorer) Change_query(query string) {\n\tif fss.query == query {\n\t\treturn\n\t}\n\tfss.keep_going.Store(false)\n\tif fss.current_worker_wait != nil {\n\t\tif fss.scanner != nil {\n\t\t\tfss.scanner.Cancel()\n\t\t}\n\t\tfss.current_worker_wait.Wait()\n\t}\n\tfss.lock()\n\tfss.query = query\n\tfss.sorted_results.Clear()\n\tfss.unlock()\n\tfss.Start()\n}\n\nfunc (fss *FileSystemScorer) change_scanner_setting(callback func()) {\n\tfss.keep_going.Store(false)\n\tif fss.current_worker_wait != nil {\n\t\tif fss.scanner != nil {\n\t\t\tfss.scanner.Cancel()\n\t\t}\n\t\tfss.current_worker_wait.Wait()\n\t}\n\tfss.lock()\n\tcallback()\n\tfss.sorted_results.Clear()\n\tfss.scanner = nil\n\tfss.unlock()\n\tfss.Start()\n\n}\n\nfunc (fss *FileSystemScorer) Change_filter(filter Filter) {\n\tif !fss.filter.Equal(filter) {\n\t\tfss.change_scanner_setting(func() { fss.filter = filter })\n\t}\n}\n\nfunc (fss *FileSystemScorer) Change_show_hidden(val bool) {\n\tif fss.show_hidden != val {\n\t\tfss.change_scanner_setting(func() { fss.show_hidden = val })\n\t}\n}\n\nfunc (fss *FileSystemScorer) Change_respect_ignores(val bool) {\n\tif fss.respect_ignores != val {\n\t\tfss.change_scanner_setting(func() { fss.respect_ignores = val })\n\t}\n}\n\nfunc (fss *FileSystemScorer) Change_sort_by_last_modified(val bool) {\n\tif fss.sort_by_last_modified != val {\n\t\tfss.change_scanner_setting(func() { fss.sort_by_last_modified = val })\n\t}\n}\n\nfunc (fss *FileSystemScorer) worker(on_results chan bool, worker_wait *sync.WaitGroup) {\n\tdefer func() {\n\t\tfss.is_complete.Store(true)\n\t\tdefer worker_wait.Done()\n\t\tif r := recover(); r != nil {\n\t\t\tif fss.keep_going.Load() {\n\t\t\t\tqerr := parallel.Format_stacktrace_on_panic(r, 1)\n\t\t\t\tfss.on_results(qerr, true)\n\t\t\t}\n\t\t} else {\n\t\t\tif fss.keep_going.Load() {\n\t\t\t\tfss.on_results(fss.scanner.Error(), true)\n\t\t\t}\n\t\t}\n\t}()\n\thandle_batch := func(results []ResultItem) (err error) {\n\t\tif err = fss.scanner.Error(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar rp []*ResultItem\n\t\tif fss.only_dirs {\n\t\t\trp = make([]*ResultItem, 0, len(results))\n\t\t\tfor i, r := range results {\n\t\t\t\tif r.ftype.IsDir() {\n\t\t\t\t\trp = append(rp, &results[i])\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif fss.filter.Match == nil {\n\t\t\t\trp = make([]*ResultItem, len(results))\n\t\t\t\tfor i := range len(rp) {\n\t\t\t\t\trp[i] = &results[i]\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trp = make([]*ResultItem, 0, len(results))\n\t\t\t\tfor i, r := range results {\n\t\t\t\t\tif r.ftype.IsDir() || fss.filter.Match(filepath.Base(r.text)) {\n\t\t\t\t\t\trp = append(rp, &results[i])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif len(rp) > 0 {\n\t\t\tif fss.query != \"\" {\n\t\t\t\tscores, err := fss.scorer.Score(utils.Map(func(r *ResultItem) string { return r.text }, rp), fss.query)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tfor i, r := range rp {\n\t\t\t\t\tr.SetScoreResult(scores[i])\n\t\t\t\t\tr.score.Set_length(uint16(len(r.text)))\n\t\t\t\t}\n\t\t\t\trp = utils.Filter(rp, func(r *ResultItem) bool { return r.IsMatching() })\n\t\t\t} else {\n\t\t\t\tfor _, r := range rp {\n\t\t\t\t\tr.score &= 0b11111111111111111111111111111111 // only preserve index\n\t\t\t\t\tr.positions = nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif len(rp) > 0 {\n\t\t\tslices.SortFunc(rp, func(a, b *ResultItem) int { return cmp.Compare(a.score, b.score) })\n\t\t}\n\t\tfss.sorted_results.AddSortedSlice(rp)\n\t\treturn\n\t}\n\n\toffset := &CollectionIndex{}\n\tfor range on_results {\n\t\tif !fss.keep_going.Load() {\n\t\t\tbreak\n\t\t}\n\t\tresults := fss.scanner.Batch(offset)\n\t\tif len(results) > 0 || fss.scanner.Error() != nil {\n\t\t\tfss.on_results(handle_batch(results), false)\n\t\t}\n\t}\n\tfor fss.keep_going.Load() {\n\t\tb := fss.scanner.Batch(offset)\n\t\tif len(b) == 0 {\n\t\t\tbreak\n\t\t}\n\t\tfss.on_results(handle_batch(b), false)\n\t}\n}\n\nfunc (fss *FileSystemScorer) Results() (ans *SortedResults, is_finished bool) {\n\tfss.lock()\n\tdefer fss.unlock()\n\treturn fss.sorted_results, fss.is_complete.Load()\n}\n\nfunc (fss *FileSystemScorer) Cancel() {\n\tfss.keep_going.Store(false)\n\tfss.scanner.Cancel()\n}\n\ntype Settings interface {\n\tOnlyDirs() bool\n\tCurrentDir() string\n\tSearchText() string\n\tShowHidden() bool\n\tRespectIgnores() bool\n\tSortByLastModified() bool\n\tFilter() Filter\n\tGlobalIgnores() ignorefiles.IgnoreFile\n\tHighlightStyles() (string, string)\n\tSyntaxAliases() map[string]string\n\tDiskCacheSize() int64\n}\n\ntype ResultManager struct {\n\treport_errors    chan error\n\tWakeupMainThread func() bool\n\tsettings         Settings\n\n\tscorer         *FileSystemScorer\n\tmutex          sync.Mutex\n\tlast_wakeup_at time.Time\n\n\tlast_click_anchor *CollectionIndex\n}\n\nfunc NewResultManager(err_chan chan error, settings Settings, WakeupMainThread func() bool) *ResultManager {\n\tans := &ResultManager{\n\t\treport_errors:    err_chan,\n\t\tsettings:         settings,\n\t\tWakeupMainThread: WakeupMainThread,\n\t}\n\treturn ans\n}\n\nfunc (m *ResultManager) new_scorer() {\n\troot_dir := m.current_root_dir()\n\tquery := m.settings.SearchText()\n\tm.scorer = NewFileSystemScorer(root_dir, query, m.settings.Filter(), m.settings.OnlyDirs(), m.on_results)\n\tm.scorer.respect_ignores = m.settings.RespectIgnores()\n\tm.scorer.show_hidden = m.settings.ShowHidden()\n\tm.scorer.global_ignore = m.settings.GlobalIgnores()\n\tm.scorer.sort_by_last_modified = m.settings.SortByLastModified()\n\tm.last_click_anchor = nil\n}\n\nfunc (m *ResultManager) on_results(err error, is_finished bool) {\n\tif err != nil {\n\t\tm.report_errors <- err\n\t\tm.WakeupMainThread()\n\t\treturn\n\t}\n\tm.mutex.Lock()\n\tdefer m.mutex.Unlock()\n\tif is_finished || time.Since(m.last_wakeup_at) > time.Millisecond*50 {\n\t\tm.WakeupMainThread()\n\t\tm.last_wakeup_at = time.Now()\n\t}\n}\n\nfunc (m *ResultManager) current_root_dir() string {\n\tvar err error\n\troot_dir := m.settings.CurrentDir()\n\tif root_dir == \"\" || root_dir == \".\" {\n\t\tif root_dir, err = os.Getwd(); err != nil {\n\t\t\treturn \"/\"\n\t\t}\n\t}\n\troot_dir = utils.Expanduser(root_dir)\n\tif root_dir, err = filepath.Abs(root_dir); err != nil {\n\t\treturn \"/\"\n\t}\n\treturn root_dir\n}\n\nfunc (m *ResultManager) set_root_dir() {\n\tif m.scorer != nil {\n\t\tm.scorer.Cancel()\n\t}\n\t_ = os.Chdir(m.current_root_dir()) // this is so the terminal emulator can read the wd for launch --directory=current\n\tm.new_scorer()\n\tm.mutex.Lock()\n\tm.last_wakeup_at = time.Time{}\n\tm.mutex.Unlock()\n\tm.scorer.Start()\n}\n\nfunc (m *ResultManager) set_something(callback func()) {\n\tm.mutex.Lock()\n\tm.last_wakeup_at = time.Time{}\n\tm.mutex.Unlock()\n\tif m.scorer == nil {\n\t\tm.new_scorer()\n\t\tm.scorer.Start()\n\t} else {\n\t\tm.last_click_anchor = nil\n\t\tcallback()\n\t}\n\n}\n\nfunc (m *ResultManager) set_query() {\n\tm.set_something(func() { m.scorer.Change_query(m.settings.SearchText()) })\n}\n\nfunc (m *ResultManager) set_filter() {\n\tm.set_something(func() { m.scorer.Change_filter(m.settings.Filter()) })\n}\n\nfunc (m *ResultManager) set_show_hidden() {\n\tm.set_something(func() { m.scorer.Change_show_hidden(m.settings.ShowHidden()) })\n}\n\nfunc (m *ResultManager) set_respect_ignores() {\n\tm.set_something(func() { m.scorer.Change_respect_ignores(m.settings.RespectIgnores()) })\n}\n\nfunc (m *ResultManager) set_sort_by_last_modified() {\n\tm.set_something(func() { m.scorer.Change_sort_by_last_modified(m.settings.SortByLastModified()) })\n}\n\nfunc (h *Handler) get_results() (ans *SortedResults, is_complete bool) {\n\tif h.result_manager.scorer == nil {\n\t\treturn\n\t}\n\treturn h.result_manager.scorer.Results()\n}\n"
  },
  {
    "path": "kittens/choose_files/scan_test.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"math/rand\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/kovidgoyal/kitty/tools/ignorefiles\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc TestAsLower(t *testing.T) {\n\tbuf := [512]byte{}\n\tfor _, q := range []string{\n\t\t\"abc\", \"aBc\", \"aBCCf83Dx\", \"mOoseÇa\", \"89ÇĞxxA\", \"\", \"23\", \"aIİBc\",\n\t} {\n\t\tn := as_lower(q, buf[:])\n\t\tactual := utils.UnsafeBytesToString(buf[:n])\n\t\tif diff := cmp.Diff(strings.ToLower(q), actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to lowercase: %#v\\n%s\", q, diff)\n\t\t}\n\t}\n}\n\ntype node struct {\n\tname     string\n\tchildren map[string]*node\n\tdata     string\n}\n\nfunc (n node) Name() string {\n\treturn n.name\n}\n\nfunc (n node) IsDir() bool {\n\treturn n.children != nil\n}\n\nfunc (n node) String() string {\n\treturn fmt.Sprintf(\"{name: %s num_children: %d}\", n.name, len(n.children))\n}\n\nfunc (n node) Type() fs.FileMode {\n\tif n.children == nil {\n\t\treturn 0\n\t}\n\treturn fs.ModeDir\n}\n\nfunc (n node) Info() (fs.FileInfo, error) {\n\treturn nil, fmt.Errorf(\"Info() not implemented\")\n}\n\nfunc random_name(r *rand.Rand) string {\n\tlength := 3 + r.Intn(23)\n\tbytes := make([]byte, length)\n\tfor i := range length {\n\t\tbytes[i] = byte(r.Intn(26) + 'a')\n\t}\n\treturn string(bytes)\n}\n\nfunc (n *node) generate_random_tree(depth, breadth int) {\n\tr := rand.New(rand.NewSource(111))\n\tn.children = make(map[string]*node)\n\tfor range breadth {\n\t\tc := &node{name: random_name(r)}\n\t\tn.children[c.name] = c\n\t\tif depth > 0 && r.Intn(10) < 3 {\n\t\t\tc.generate_random_tree(depth-1, breadth)\n\t\t}\n\t}\n}\n\nfunc (n node) dir_entries() []fs.DirEntry {\n\tentries := make([]fs.DirEntry, 0, len(n.children))\n\tfor _, v := range n.children {\n\t\tentries = append(entries, v)\n\t}\n\treturn entries\n}\n\nfunc (n node) ReadFile(name string) ([]byte, error) {\n\tif name == string(os.PathSeparator) {\n\t\treturn nil, fs.ErrNotExist\n\t}\n\tp := &n\n\tfor x := range strings.SplitSeq(strings.Trim(name, string(os.PathSeparator)), string(os.PathSeparator)) {\n\t\tc, found := p.children[x]\n\t\tif !found {\n\t\t\treturn nil, fs.ErrNotExist\n\t\t}\n\t\tp = c\n\t}\n\treturn []byte(p.data), nil\n}\n\nfunc (n node) ReadDir(name string) ([]fs.DirEntry, error) {\n\tif name == string(os.PathSeparator) {\n\t\treturn n.dir_entries(), nil\n\t}\n\tp := &n\n\tfor x := range strings.SplitSeq(strings.Trim(name, string(os.PathSeparator)), string(os.PathSeparator)) {\n\t\tc, found := p.children[x]\n\t\tif !found {\n\t\t\treturn nil, fs.ErrNotExist\n\t\t}\n\t\tif !c.IsDir() {\n\t\t\treturn nil, fs.ErrExist\n\t\t}\n\t\tp = c\n\t}\n\treturn p.dir_entries(), nil\n}\n\nfunc TestChooseFilesIgnore(t *testing.T) {\n\troot := node{name: string(os.PathSeparator), children: map[string]*node{\n\t\t\"a\":          {name: \"a\"},\n\t\t\"b\":          {name: \"b\"},\n\t\t\"c.png\":      {name: \"c.png\"},\n\t\t\".ignore\":    {name: \".ignore\", data: \"a\\nx/s/n\"},\n\t\t\".gitignore\": {name: \".gitignore\", data: \"b\"},\n\t\t\"x\": {name: \"x\", children: map[string]*node{\n\t\t\t\"1\": {name: \"1\"}, \"2\": {name: \"2\"}, \"3\": {name: \"3\"},\n\t\t\t\"s\": {name: \"s\", children: map[string]*node{\n\t\t\t\t\"m\": {name: \"m\"}, \"n\": {name: \"n\"},\n\t\t\t}},\n\t\t}},\n\t\t\"y\": {name: \"y\", children: map[string]*node{\n\t\t\t\"3\": {name: \"3\"}, \"4\": {name: \"4\"}, \"5\": {name: \"5\"},\n\t\t\t\"s\": {name: \"s\", children: map[string]*node{\n\t\t\t\t\"3\": {name: \"3\"}, \"4\": {name: \"4\"}, \"5\": {name: \"5\"}, \"6\": {name: \"6\"},\n\t\t\t}},\n\t\t\t\".gitignore\": {name: \".gitignore\", data: \"/s/5\"},\n\t\t\t\".git\": {name: \".git\", children: map[string]*node{\n\t\t\t\t\"info\": {name: \"info\", children: map[string]*node{\n\t\t\t\t\t\"exclude\": {name: \"exclude\", data: \"s/4\"},\n\t\t\t\t}},\n\t\t\t}},\n\t\t}},\n\t}}\n\tr := func(respect_ignores bool, expected string) {\n\t\tch := make(chan bool)\n\t\ts := new_filesystem_scanner(\"/\", ch, nil)\n\t\ts.dir_reader = root.ReadDir\n\t\ts.file_reader = root.ReadFile\n\t\ts.global_gitignore = ignorefiles.NewGitignore()\n\t\ts.global_ignore = ignorefiles.NewGitignore()\n\t\ts.respect_ignores = respect_ignores\n\t\tif err := s.global_gitignore.LoadLines(\"*.png\", \"s/3\"); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif err := s.global_ignore.LoadLines(\"x/3\"); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\ts.Start()\n\t\tfor range ch {\n\t\t}\n\t\tif s.err != nil {\n\t\t\tt.Fatal(s.err)\n\t\t}\n\t\tci := CollectionIndex{}\n\t\tactual := utils.Map(func(x ResultItem) string { return x.text }, s.Batch(&ci))\n\t\tif diff := cmp.Diff(strings.Split(expected, ` `), actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Incorrect ignoring:\\n%s\", diff)\n\t\t}\n\t}\n\tr(true, `x y b c.png x/s x/1 x/2 y/s y/3 y/4 y/5 x/s/m y/s/6`)\n\tr(false, `x y a b c.png x/s x/1 x/2 x/3 y/s y/3 y/4 y/5 x/s/m x/s/n y/s/3 y/s/4 y/s/5 y/s/6`)\n}\n\nfunc TestChooseFilesScoring(t *testing.T) {\n\troot := node{name: string(os.PathSeparator), children: map[string]*node{\n\t\t\"b\":     {name: \"b\"},\n\t\t\"a\":     {name: \"a\"},\n\t\t\"c.png\": {name: \"c.png\"},\n\t\t\"x\": {name: \"x\", children: map[string]*node{\n\t\t\t\"1\": {name: \"1\"}, \"2\": {name: \"2\"}, \"3\": {name: \"3\"},\n\t\t\t\"s\": {name: \"s\", children: map[string]*node{\n\t\t\t\t\"m\": {name: \"m\"}, \"n\": {name: \"n\"},\n\t\t\t}},\n\t\t}},\n\t\t\"y\": {name: \"y\", children: map[string]*node{\n\t\t\t\"3\": {name: \"3\"}, \"4\": {name: \"4\"}, \"5\": {name: \"5\"},\n\t\t}},\n\t}}\n\twg := sync.WaitGroup{}\n\twg.Add(1)\n\ts := NewFileSystemScorer(string(os.PathSeparator), \"\", Filter{}, false, func(err error, is_complete bool) {\n\t\tif is_complete {\n\t\t\twg.Done()\n\t\t}\n\t})\n\ts.dir_reader = root.ReadDir\n\ts.global_gitignore = ignorefiles.NewGitignore()\n\ts.Start()\n\twg.Wait()\n\tresults := func() (ans []string) {\n\t\trr, _ := s.Results()\n\t\tfor _, r := range rr.RenderedMatches(CollectionIndex{}, -1) {\n\t\t\tans = append(ans, r.text)\n\t\t}\n\t\treturn\n\t}\n\tae := func(query string, expected ...string) {\n\t\twg.Add(1)\n\t\ts.Change_query(query)\n\t\twg.Wait()\n\t\tif diff := cmp.Diff(expected, results()); diff != \"\" {\n\t\t\tt.Fatalf(\"Query less scoring failed\\n%s\", diff)\n\t\t}\n\t}\n\tae(\"a\", \"a\")\n\tae(\"3\", \"x/3\", \"y/3\")\n\tae(\"s\", \"x/s\", \"x/s/m\", \"x/s/n\")\n\tae(\"sn\", \"x/s/n\")\n\tae(\"\", \"x\", \"y\", \"a\", \"b\", \"c.png\", \"x/s\", \"x/1\", \"x/2\", \"x/3\", \"y/3\", \"y/4\", \"y/5\", \"x/s/m\", \"x/s/n\")\n\n\taf := func(filter string, expected ...string) {\n\t\tf, _ := NewFilter(filter)\n\t\twg.Add(1)\n\t\ts.Change_filter(*f)\n\t\twg.Wait()\n\t\tif diff := cmp.Diff(expected, results()); diff != \"\" {\n\t\t\tt.Fatalf(\"filter %s failed\\n%s\", filter, diff)\n\t\t}\n\t}\n\taf(\"glob:a:A\", \"x\", \"y\", \"a\", \"x/s\")\n\taf(\"glob:[ab]:A\", \"x\", \"y\", \"a\", \"b\", \"x/s\")\n\taf(\"mime:image/png:A\", \"x\", \"y\", \"c.png\", \"x/s\")\n\taf(\"mime:image/*:A\", \"x\", \"y\", \"c.png\", \"x/s\")\n\taf(\"glob:*:All\", \"x\", \"y\", \"a\", \"b\", \"c.png\", \"x/s\", \"x/1\", \"x/2\", \"x/3\", \"y/3\", \"y/4\", \"y/5\", \"x/s/m\", \"x/s/n\")\n}\n\nfunc TestSortedResults(t *testing.T) {\n\tr := NewSortedResults()\n\tidx := CollectionIndex{}\n\tm := func(items ...int) []*ResultItem {\n\t\tans := make([]*ResultItem, len(items))\n\t\tfor i, x := range items {\n\t\t\tans[i] = &ResultItem{text: strconv.Itoa(x), score: CombinedScore(x)}\n\t\t}\n\t\treturn ans\n\t}\n\tv := func(slice, pos, num int) []int {\n\t\tif num == 0 {\n\t\t\tnum = r.Len()\n\t\t}\n\t\treturn utils.Map(func(r *ResultItem) int { return int(r.score) }, r.RenderedMatches(CollectionIndex{slice, pos}, num))\n\t}\n\ttv := func(slice, pos, num int, expected ...int) {\n\t\tif diff := cmp.Diff(expected, v(slice, pos, num)); diff != \"\" {\n\t\t\tt.Fatalf(\"view failed for %v num:%d\\n%s\", CollectionIndex{slice, pos}, num, diff)\n\t\t}\n\t}\n\ttci := func(increment int, expected int) {\n\t\torig := idx\n\t\tidx = r.IncrementIndexWithWrapAround(idx, increment)\n\t\tactual := int(r.At(idx).score)\n\t\tif actual != expected {\n\t\t\tt.Fatalf(\"increment: %d on %v failed\\nexpected: %d actual: %d idx: %v\", increment, orig, expected, actual, idx)\n\t\t}\n\t}\n\tdt := func(a, b CollectionIndex, expected int) {\n\t\tactual := r.distance(a, b)\n\t\tif expected != actual {\n\t\t\tt.Fatalf(\"distance on %v and %v failed\\nexpected: %d actual: %d \", a, b, expected, actual)\n\t\t}\n\t\tif r.distance(b, a) != actual {\n\t\t\tt.Fatalf(\"distance on %v and %v not commutative %d != %d\", a, b, actual, r.distance(b, a))\n\t\t}\n\t}\n\ttc := func(num_before, expected_new_before int, ci CollectionIndex, expected ...[]int) {\n\t\tac, new_num_before, _ := r.SplitIntoColumns(func(int) int { return 2 }, 2, num_before, ci)\n\t\tactual := make([][]int, len(ac))\n\t\tfor i, x := range ac {\n\t\t\tactual[i] = utils.Map(func(r *ResultItem) int { return int(r.score) }, x)\n\t\t}\n\t\tif expected_new_before != new_num_before {\n\t\t\tt.Fatalf(\"new_num_before not as expected for num_before: %d ci: %v\\n%d != %d\", num_before, ci, expected_new_before, new_num_before)\n\t\t}\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"wrong columns for num_before: %d ci: %v\\n%s\", num_before, ci, diff)\n\t\t}\n\t}\n\tr.AddSortedSlice(m(10, 20, 30))\n\tr.AddSortedSlice(m(40, 50, 60))\n\tr.AddSortedSlice(m(70, 80, 90))\n\n\ttc(0, 0, CollectionIndex{}, []int{10, 20}, []int{30, 40})\n\ttc(1, 1, CollectionIndex{Pos: 1}, []int{10, 20}, []int{30, 40})\n\ttc(1, 1, CollectionIndex{Pos: 2}, []int{20, 30}, []int{40, 50})\n\ttc(2, 2, CollectionIndex{Pos: 2}, []int{10, 20}, []int{30, 40})\n\ttc(20, 2, CollectionIndex{Pos: 2}, []int{10, 20}, []int{30, 40})\n\tfor num_before := range 4 {\n\t\ttc(num_before, 3, CollectionIndex{2, 2}, []int{60, 70}, []int{80, 90})\n\t}\n\ttc(1, 1, CollectionIndex{1, 1}, []int{40, 50}, []int{60, 70})\n\n\tdt(CollectionIndex{Pos: 0}, CollectionIndex{Pos: 2}, 2)\n\tdt(CollectionIndex{Pos: 0}, CollectionIndex{Slice: 1}, 3)\n\tdt(CollectionIndex{Pos: 0}, CollectionIndex{Slice: 1, Pos: 1}, 4)\n\n\ttv(0, 0, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90)\n\ttv(0, 2, 3, 30, 40, 50)\n\ttv(0, 3, 3, 40, 50, 60)\n\ttv(1, 0, 4, 40, 50, 60, 70)\n\n\ttci(1, 20)\n\ttci(3, 50)\n\ttci(-1, 40)\n\ttci(-3, 10)\n\ttci(-2, 80)\n\ttci(3, 20)\n\ttci(9, 20)\n\ttci(-9, 20)\n\n\tr.AddSortedSlice(m(100, 110, 120))\n\tr.AddSortedSlice(m(41, 61, 71, 99))\n\ttv(0, 0, 0, 10, 20, 30, 40, 41, 50, 60, 61, 70, 71, 80, 90, 99, 100, 110, 120)\n\tr.AddSortedSlice(m(1, 2, 3))\n\ttv(0, 0, 0, 1, 2, 3, 10, 20, 30, 40, 41, 50, 60, 61, 70, 71, 80, 90, 99, 100, 110, 120)\n\tr.AddSortedSlice(m(1000, 2000))\n\ttv(0, 0, 0, 1, 2, 3, 10, 20, 30, 40, 41, 50, 60, 61, 70, 71, 80, 90, 99, 100, 110, 120, 1000, 2000)\n}\n\nfunc run_scoring(b *testing.B, depth, breadth int, query string) {\n\n\troot := node{name: string(os.PathSeparator)}\n\troot.generate_random_tree(depth, breadth)\n\n\tfor b.Loop() {\n\t\tb.StopTimer()\n\t\twg := sync.WaitGroup{}\n\t\twg.Add(1)\n\t\ts := NewFileSystemScorer(string(os.PathSeparator), query, Filter{}, false, func(err error, is_complete bool) {\n\t\t\tif is_complete {\n\t\t\t\twg.Done()\n\t\t\t}\n\t\t})\n\t\ts.dir_reader = root.ReadDir\n\t\ts.global_gitignore = ignorefiles.NewGitignore()\n\t\tb.StartTimer()\n\t\ts.scanner.Start()\n\t\ts.Start()\n\t\twg.Wait()\n\t}\n\tfmt.Println(\"\\nnumber of iterations: \", b.N)\n\tfmt.Println(\"time per iteration:\", b.Elapsed()/time.Duration(b.N))\n}\n\n// To run this benchmark with profiling use:\n// go test -bench=FileNameScoringWithoutQuery -benchmem -cpuprofile=/tmp/cpu.prof -memprofile=/tmp/mem.prof github.com/kovidgoyal/kitty/kittens/choose_files -o /tmp/cfexe\nfunc BenchmarkFileNameScoringWithoutQuery(b *testing.B) {\n\trun_scoring(b, 5, 20, \"\")\n}\n\n// To run this benchmark with profiling use:\n// go test -bench=FileNameScoringWithQuery -benchmem -cpuprofile=/tmp/cpu.prof -memprofile=/tmp/mem.prof github.com/kovidgoyal/kitty/kittens/choose_files -o /tmp/cfexe\nfunc BenchmarkFileNameScoringWithQuery(b *testing.B) {\n\trun_scoring(b, 5, 20, \"abc\")\n}\n"
  },
  {
    "path": "kittens/choose_files/search-bar.go",
    "content": "package choose_files\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nfunc (h *Handler) draw_frame(width, height int, in_progress bool) {\n\tlp := h.lp\n\tprefix, suffix := \"\", \"\"\n\tif in_progress {\n\t\tx := h.lp.SprintStyled(\"fg=cyan\", \" \")\n\t\tprefix, suffix, _ = strings.Cut(x, \" \")\n\t\tlp.QueueWriteString(prefix)\n\t}\n\tfor i := range height {\n\t\tlp.SaveCursorPosition()\n\t\tswitch i {\n\t\tcase 0:\n\t\t\tlp.QueueWriteString(\"╭\")\n\t\t\tlp.QueueWriteString(strings.Repeat(\"─\", width-2))\n\t\t\tlp.QueueWriteString(\"╮\")\n\t\tcase height - 1:\n\t\t\tlp.QueueWriteString(\"╰\")\n\t\t\tlp.QueueWriteString(strings.Repeat(\"─\", width-2))\n\t\t\tlp.QueueWriteString(\"╯\")\n\t\tdefault:\n\t\t\tlp.QueueWriteString(\"│\")\n\t\t\tlp.MoveCursorHorizontally(width - 2)\n\t\t\tlp.QueueWriteString(\"│\")\n\t\t}\n\t\tlp.RestoreCursorPosition()\n\t\tlp.MoveCursorVertically(1)\n\t}\n\tif suffix != \"\" {\n\t\tlp.QueueWriteString(suffix)\n\t}\n}\n\nfunc (h *Handler) draw_search_text(available_width int) {\n\ttext := h.state.SearchText()\n\tavailable_width /= 2\n\tif wcswidth.Stringwidth(text) > available_width {\n\t\tg := wcswidth.SplitIntoGraphemes(text)\n\t\tavailable_width -= 2\n\t\tg = g[len(g)-available_width:]\n\t\ttext = \"…\" + strings.Join(g, \"\")\n\t}\n\th.lp.DrawSizedText(text+\" \", loop.SizedText{Scale: 2})\n\th.lp.MoveCursorHorizontally(-2)\n}\n\nconst SEARCH_BAR_HEIGHT = 4\n\nfunc (h *Handler) draw_controls(y int) (max_width int) {\n\ttype entry struct {\n\t\ttext     string\n\t\tcallback func()\n\t\twidth    int\n\t}\n\tlines := make([]entry, 0, SEARCH_BAR_HEIGHT)\n\tadd_control := func(icon, text string, callback func()) {\n\t\tline := icon + \" \" + text\n\t\twidth := wcswidth.Stringwidth(line)\n\t\tmax_width = max(max_width, width)\n\t\tlines = append(lines, entry{line, callback, width})\n\t}\n\tadd_control(utils.IfElse(h.state.ShowHidden(), \" \", \" \"), utils.IfElse(h.state.ShowHidden(), \"hide dotfiles\", \"show dotfiles\"), func() {\n\t\th.state.show_hidden = !h.state.show_hidden\n\t\th.result_manager.set_show_hidden()\n\t})\n\tadd_control(\"󰑑 \", utils.IfElse(h.state.RespectIgnores(), \"show ignored\", \"hide ignored\"), func() {\n\t\th.state.respect_ignores = !h.state.respect_ignores\n\t\th.result_manager.set_respect_ignores()\n\t})\n\tadd_control(\" \", utils.IfElse(h.state.ShowPreview(), \"hide preview\", \"show preview\"), func() {\n\t\th.state.show_preview = !h.state.show_preview\n\t})\n\tadd_control(utils.IfElse(h.state.SortByLastModified(), \" \", \" \"), utils.IfElse(h.state.SortByLastModified(), \"sort names\", \"sort dates\"), func() {\n\t\th.state.sort_by_last_modified = !h.state.sort_by_last_modified\n\t\th.result_manager.set_sort_by_last_modified()\n\t})\n\tx := h.screen_size.width - max_width\n\tfor i, e := range lines {\n\t\th.lp.MoveCursorTo(x+1, y+i+1)\n\t\th.lp.QueueWriteString(e.text)\n\t\tcb := e.callback\n\t\th.state.mouse_state.AddCellRegion(\"rcontrol-\"+strconv.Itoa(i), x, y+i, x+e.width, y+i, func(_ string) error {\n\t\t\tcb()\n\t\t\th.state.redraw_needed = true\n\t\t\treturn nil\n\t\t}).HoverStyle = HOVER_STYLE\n\t}\n\treturn max_width + 1\n}\n\nfunc (h *Handler) draw_search_bar(y int) {\n\tleft_margin, right_margin := 0, h.draw_controls(y)\n\th.lp.MoveCursorTo(1+left_margin, 1+y)\n\tavailable_width := h.screen_size.width - left_margin - right_margin\n\th.draw_frame(available_width, SEARCH_BAR_HEIGHT, false)\n\tfor y1 := y; y1 < y+4; y1++ {\n\t\tcr := h.state.mouse_state.AddCellRegion(\"search-bar\", left_margin, y1, left_margin+available_width, y1)\n\t\tcr.PointerShape = loop.TEXT_POINTER\n\t\tcr.HoverStyle = \"none\"\n\t}\n\th.lp.MoveCursorTo(1+left_margin+1, 2+y)\n\th.draw_search_text(available_width - 2)\n}\n\nfunc (h *Handler) handle_edit_keys(ev *loop.KeyEvent) bool {\n\tswitch {\n\tcase ev.MatchesPressOrRepeat(\"backspace\"):\n\t\tif h.state.SearchText() == \"\" {\n\t\t\th.lp.Beep()\n\t\t} else {\n\t\t\tg := wcswidth.SplitIntoGraphemes(h.state.search_text)\n\t\t\th.set_query(strings.Join(g[:len(g)-1], \"\"))\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "kittens/choose_fonts/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/choose_fonts/backend.go",
    "content": "package choose_fonts\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype kitty_font_backend_type struct {\n\tfrom                    io.ReadCloser\n\tto                      io.WriteCloser\n\tjson_decoder            *json.Decoder\n\tcmd                     *exec.Cmd\n\tstderr                  strings.Builder\n\tlock                    sync.Mutex\n\tr                       io.ReadCloser\n\tw                       io.WriteCloser\n\twait_for_exit           chan error\n\tstarted, exited, failed bool\n\ttimeout                 time.Duration\n}\n\nfunc (k *kitty_font_backend_type) start() (err error) {\n\texe := utils.KittyExe()\n\tif exe == \"\" {\n\t\texe = utils.Which(\"kitty\")\n\t}\n\tif exe == \"\" {\n\t\treturn fmt.Errorf(\"Failed to find the kitty executable, this kitten requires the kitty executable to be present. You can use the environment variable KITTY_PATH_TO_KITTY_EXE to specify the path to the kitty executable\")\n\t}\n\n\tk.cmd = exec.Command(exe, \"+runpy\", \"from kittens.choose_fonts.backend import main; main()\")\n\tk.cmd.Stderr = &k.stderr\n\n\tif k.r, k.to, err = os.Pipe(); err != nil {\n\t\treturn err\n\t}\n\tk.cmd.Stdin = k.r\n\tif k.from, k.w, err = os.Pipe(); err != nil {\n\t\treturn err\n\t}\n\tk.cmd.Stdout = k.w\n\tk.json_decoder = json.NewDecoder(k.from)\n\tif err = k.cmd.Start(); err != nil {\n\t\treturn err\n\t}\n\tk.started = true\n\tk.timeout = 60 * time.Second\n\tk.wait_for_exit = make(chan error)\n\tgo func() {\n\t\tk.wait_for_exit <- k.cmd.Wait()\n\t}()\n\treturn\n}\n\nvar kitty_font_backend kitty_font_backend_type\n\nfunc (k *kitty_font_backend_type) send(v any) error {\n\tif k.to == nil {\n\t\treturn fmt.Errorf(\"Trying to send data when to pipe is nil\")\n\t}\n\tdata, err := json.Marshal(v)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could not encode message to kitty with error: %w\", err)\n\t}\n\tc := make(chan error)\n\tgo func() {\n\t\tif _, err = k.to.Write(data); err != nil {\n\t\t\tc <- fmt.Errorf(\"Failed to send message to kitty with I/O error: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tif _, err = k.to.Write([]byte{'\\n'}); err != nil {\n\t\t\tc <- fmt.Errorf(\"Failed to send message to kitty with I/O error: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tc <- nil\n\t}()\n\tselect {\n\tcase err := <-c:\n\t\treturn err\n\tcase <-time.After(k.timeout):\n\t\treturn fmt.Errorf(\"Timed out waiting to write to kitty font backend after %v\", k.timeout)\n\tcase err := <-k.wait_for_exit:\n\t\tk.exited = true\n\t\tif err == nil {\n\t\t\terr = fmt.Errorf(\"kitty font backend exited with no error while waiting for a response from it\")\n\t\t} else {\n\t\t\tk.failed = true\n\t\t}\n\t\treturn err\n\t}\n}\n\nfunc (k *kitty_font_backend_type) query(action string, cmd map[string]any, result any) error {\n\tk.lock.Lock()\n\tdefer k.lock.Unlock()\n\tif cmd == nil {\n\t\tcmd = make(map[string]any)\n\t}\n\tcmd[\"action\"] = action\n\tif err := k.send(cmd); err != nil {\n\t\treturn err\n\t}\n\tc := make(chan error)\n\tgo func() {\n\t\tif err := k.json_decoder.Decode(result); err != nil {\n\t\t\tc <- fmt.Errorf(\"Failed to decode JSON from kitty with error: %w\", err)\n\t\t}\n\t\tc <- nil\n\t}()\n\tselect {\n\tcase err := <-c:\n\t\treturn err\n\tcase <-time.After(k.timeout):\n\t\treturn fmt.Errorf(\"Timed out waiting for response from kitty font backend after %v\", k.timeout)\n\tcase err := <-k.wait_for_exit:\n\t\tk.exited = true\n\t\tif err == nil {\n\t\t\terr = fmt.Errorf(\"kitty font backed exited with no error while waiting for a response from it\")\n\t\t} else {\n\t\t\tk.failed = true\n\t\t}\n\t\treturn err\n\t}\n}\n\nfunc (k *kitty_font_backend_type) release() (err error) {\n\tif k.r != nil {\n\t\tk.r.Close()\n\t\tk.r = nil\n\t}\n\tif k.to != nil {\n\t\tk.to.Close()\n\t\tk.to = nil\n\t}\n\tif k.w != nil {\n\t\tk.w.Close()\n\t\tk.w = nil\n\t}\n\tif k.from != nil {\n\t\tk.from.Close()\n\t\tk.from = nil\n\t}\n\tif k.started && !k.exited {\n\t\ttimeout := 2 * time.Second\n\t\tselect {\n\t\tcase err = <-k.wait_for_exit:\n\t\t\tk.exited = true\n\t\t\tif err != nil {\n\t\t\t\tk.failed = true\n\t\t\t}\n\t\tcase <-time.After(timeout):\n\t\t\tk.failed = true\n\t\t\terr = fmt.Errorf(\"Timed out waiting for kitty font backend to exit for %v\", timeout)\n\t\t}\n\t}\n\tos.Stderr.WriteString(k.stderr.String())\n\treturn\n}\n"
  },
  {
    "path": "kittens/choose_fonts/backend.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport os\nimport string\nimport sys\nimport tempfile\nfrom typing import TYPE_CHECKING, Any, Literal, Optional, TypedDict\n\nfrom kitty.cli import create_default_opts\nfrom kitty.conf.utils import to_color\nfrom kitty.constants import kitten_exe\nfrom kitty.fonts import Descriptor\nfrom kitty.fonts.common import (\n    face_from_descriptor,\n    get_axis_map,\n    get_font_files,\n    get_named_style,\n    get_variable_data_for_descriptor,\n    get_variable_data_for_face,\n    is_variable,\n    spec_for_face,\n)\nfrom kitty.fonts.features import Type, known_features\nfrom kitty.fonts.list import create_family_groups\nfrom kitty.fonts.render import display_bitmap\nfrom kitty.options.types import Options\nfrom kitty.options.utils import parse_font_spec\nfrom kitty.typing_compat import NotRequired\nfrom kitty.utils import screen_size_function\n\nif TYPE_CHECKING:\n    from kitty.fast_data_types import FeatureData\n\ndef setup_debug_print() -> bool:\n    if 'KITTY_STDIO_FORWARDED' in os.environ:\n        try:\n            fd = int(os.environ['KITTY_STDIO_FORWARDED'])\n        except Exception:\n            return False\n        try:\n            sys.stdout = open(fd, 'w', closefd=False)\n            return True\n        except OSError:\n            return False\n    return False\n\n\ndef send_to_kitten(x: Any) -> None:\n    f = sys.__stdout__\n    assert f is not None\n    try:\n        f.buffer.write(json.dumps(x).encode())\n        f.buffer.write(b'\\n')\n        f.buffer.flush()\n    except BrokenPipeError:\n        raise SystemExit('Pipe to kitten was broken while sending data to it')\n\n\nclass TextStyle(TypedDict):\n    font_size: float\n    dpi_x: float\n    dpi_y: float\n    foreground: str\n    background: str\n\n\nOptNames = Literal['font_family', 'bold_font', 'italic_font', 'bold_italic_font']\nFamilyKey = tuple[OptNames, ...]\n\n\ndef opts_from_cmd(cmd: dict[str, Any]) -> tuple[Options, FamilyKey, float, float]:\n    opts = Options()\n    ts: TextStyle = cmd['text_style']\n    opts.font_size = ts['font_size']\n    opts.foreground = to_color(ts['foreground'])\n    opts.background = to_color(ts['background'])\n    family_key = []\n    def d(k: OptNames) -> None:\n        if k in cmd:\n            setattr(opts, k, parse_font_spec(cmd[k]))\n            family_key.append(k)\n    d('font_family')\n    d('bold_font')\n    d('italic_font')\n    d('bold_italic_font')\n    return opts, tuple(family_key), ts['dpi_x'], ts['dpi_y']\n\n\nBaseKey = tuple[str, int, int]\nFaceKey = tuple[str, BaseKey]\nRenderedSample = tuple[bytes, dict[str, Any]]\nRenderedSampleTransmit = dict[str, Any]\nSAMPLE_TEXT = string.ascii_lowercase + ' ' + string.digits + ' ' + string.ascii_uppercase + ' ' + string.punctuation\n\n\nclass FD(TypedDict):\n    is_index: bool\n    name: NotRequired[str]\n    tooltip: NotRequired[str]\n    sample: NotRequired[str]\n    params: NotRequired[tuple[str, ...]]\n\n\n\ndef get_features(features: dict[str, Optional['FeatureData']]) -> dict[str, FD]:\n    ans = {}\n    for tag, data in features.items():\n        kf = known_features.get(tag)\n        if kf is None or kf.type is Type.hidden:\n            continue\n        fd: FD = {'is_index': kf.type is Type.index}\n        ans[tag] = fd\n        if data is not None:\n            if n := data.get('name'):\n                fd['name'] = n\n            if n := data.get('tooltip'):\n                fd['tooltip'] = n\n            if n := data.get('sample'):\n                fd['sample'] = n\n            if p := data.get('params'):\n                fd['params'] = p\n    return ans\n\n\ndef render_face_sample(font: Descriptor, opts: Options, dpi_x: float, dpi_y: float, width: int, height: int, sample_text: str = '') -> RenderedSample:\n    face = face_from_descriptor(font, opts.font_size, dpi_x, dpi_y)\n    face.set_size(opts.font_size, dpi_x, dpi_y)\n    metadata = {\n        'variable_data': get_variable_data_for_face(face),\n        'style': font['style'],\n        'psname': face.postscript_name(),\n        'features': get_features(face.get_features()),\n        'applied_features': face.applied_features(),\n        'spec': spec_for_face(font['family'], face).as_setting,\n        'cell_width': 0, 'cell_height': 0, 'canvas_height': 0, 'canvas_width': width,\n    }\n    if is_variable(font):\n        ns = get_named_style(face)\n        if ns:\n            metadata['variable_named_style'] = ns\n        metadata['variable_axis_map'] = get_axis_map(face)\n    bitmap, cell_width, cell_height = face.render_sample_text(sample_text or SAMPLE_TEXT, width, height, opts.foreground.rgb)\n    metadata['cell_width'] = cell_width\n    metadata['cell_height'] = cell_height\n    metadata['canvas_height'] = len(bitmap) // (4 *width)\n    return bitmap, metadata\n\n\ndef render_family_sample(\n    opts: Options, family_key: FamilyKey, dpi_x: float, dpi_y: float, width: int, height: int, output_dir: str,\n    cache: dict[FaceKey, RenderedSampleTransmit]\n) -> dict[str, RenderedSampleTransmit]:\n    base_key: BaseKey = opts.font_family.created_from_string, width, height\n    ans: dict[str, RenderedSampleTransmit] = {}\n    font_files = get_font_files(opts)\n    for x in family_key:\n        key: FaceKey = x + ': ' + str(getattr(opts, x)), base_key\n        if x == 'font_family':\n            desc = font_files['medium']\n        elif x == 'bold_font':\n            desc = font_files['bold']\n        elif x == 'italic_font':\n            desc = font_files['italic']\n        elif x == 'bold_italic_font':\n            desc = font_files['bi']\n        cached = cache.get(key)\n        if cached is not None:\n            ans[x] = cached\n        else:\n            with tempfile.NamedTemporaryFile(delete=False, suffix='.rgba', dir=output_dir) as tf:\n                bitmap, metadata = render_face_sample(desc, opts, dpi_x, dpi_y, width, height)\n                tf.write(bitmap)\n            metadata['path'] = tf.name\n            cache[key] = ans[x] = metadata\n    return ans\n\n\nResolvedFace = dict[Literal['family', 'spec', 'setting'], str]\n\n\ndef spec_for_descriptor(d: Descriptor, font_size: float) -> str:\n    face = face_from_descriptor(d, font_size, 288, 288)\n    return spec_for_face(d['family'], face).as_setting\n\n\ndef resolved_faces(opts: Options) -> dict[OptNames, ResolvedFace]:\n    font_files = get_font_files(opts)\n    ans: dict[OptNames, ResolvedFace] = {}\n    def d(key: Literal['medium', 'bold', 'italic', 'bi'], opt_name: OptNames) -> None:\n        descriptor = font_files[key]\n        ans[opt_name] = {\n                'family': descriptor['family'], 'spec': spec_for_descriptor(descriptor, opts.font_size),\n                'setting': getattr(opts, opt_name).created_from_string\n        }\n    d('medium', 'font_family')\n    d('bold', 'bold_font')\n    d('italic', 'italic_font')\n    d('bi', 'bold_italic_font')\n    return ans\n\n\ndef main() -> None:\n    setup_debug_print()\n    cache: dict[FaceKey, RenderedSampleTransmit] = {}\n    for line in sys.stdin.buffer:\n        cmd = json.loads(line)\n        action = cmd.get('action', '')\n        if action == 'list_monospaced_fonts':\n            opts = create_default_opts()\n            send_to_kitten({'fonts': create_family_groups(), 'resolved_faces': resolved_faces(opts)})\n        elif action == 'read_variable_data':\n            ans = []\n            for descriptor in cmd['descriptors']:\n                ans.append(get_variable_data_for_descriptor(descriptor))\n            send_to_kitten(ans)\n        elif action == 'render_family_samples':\n            opts, family_key, dpi_x, dpi_y = opts_from_cmd(cmd)\n            send_to_kitten(render_family_sample(opts, family_key, dpi_x, dpi_y, cmd['width'], cmd['height'], cmd['output_dir'], cache))\n        else:\n            raise SystemExit(f'Unknown action: {action}')\n\n\ndef query_kitty() -> dict[str, str]:\n    import subprocess\n    ans = {}\n    for line in subprocess.check_output([kitten_exe(), 'query-terminal']).decode().splitlines():\n        k, sep, v = line.partition(':')\n        if sep == ':':\n            ans[k] = v.strip()\n    return ans\n\n\ndef showcase(family: str = 'family=\"Fira Code\"', sample_text: str = '') -> None:\n    q = query_kitty()\n    opts = Options()\n    opts.foreground = to_color(q['foreground'])\n    opts.background = to_color(q['background'])\n    opts.font_size = float(q['font_size'])\n    opts.font_family = parse_font_spec(family)\n    font_files = get_font_files(opts)\n    desc = font_files['medium']\n    ss = screen_size_function()()\n    width = ss.cell_width * ss.cols\n    height = 5 * ss.cell_height\n    bitmap, m = render_face_sample(desc, opts, float(q['dpi_x']), float(q['dpi_y']), width, height, sample_text=sample_text)\n    display_bitmap(bitmap, m['canvas_width'], m['canvas_height'])\n\n\ndef test_render(spec: str = 'family=\"Fira Code\"', width: int = 1560, height: int = 116, font_size: float = 12, dpi: float = 288) -> None:\n    opts = Options()\n    opts.font_family = parse_font_spec(spec)\n    opts.font_size = font_size\n    opts.foreground = to_color('white')\n    desc = get_font_files(opts)['medium']\n    bitmap, m = render_face_sample(desc, opts, float(dpi), float(dpi), width, height)\n    display_bitmap(bitmap, m['canvas_width'], m['canvas_height'])\n"
  },
  {
    "path": "kittens/choose_fonts/face.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"maps\"\n\t\"math\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype face_panel struct {\n\thandler *handler\n\n\tfamily, which       string\n\tsettings            faces_settings\n\tcurrent_preview     *RenderedSampleTransmit\n\tcurrent_preview_key faces_preview_key\n\tpreview_cache       map[faces_preview_key]map[string]RenderedSampleTransmit\n\tpreview_cache_mutex sync.Mutex\n}\n\n// Create a new FontSpec that keeps features and axis values and named styles\n// same as the current setting. Names are all reset apart from style name.\nfunc (self *face_panel) new_font_spec() (*FontSpec, error) {\n\tfs, err := NewFontSpec(self.get(), self.current_preview.Features)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif fs.system.val == \"auto\" {\n\t\tif fs, err = NewFontSpec(self.current_preview.Spec, self.current_preview.Features); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\t// reset these selectors as we will be using some style/axis based selector instead\n\tfs.family = settable_string{self.family, true}\n\tfs.postscript_name = settable_string{}\n\tfs.full_name = settable_string{}\n\tif len(self.current_preview.Variable_data.Axes) > 0 {\n\t\tfs.variable_name = settable_string{self.current_preview.Variable_data.Variations_postscript_name_prefix, true}\n\t} else {\n\t\tfs.variable_name = settable_string{}\n\t}\n\treturn &fs, nil\n}\n\nfunc (self *face_panel) set_variable_spec(named_style string, axis_overrides map[string]float64) error {\n\tfs, err := self.new_font_spec()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif axis_overrides != nil {\n\t\taxis_values := self.current_preview.current_axis_values()\n\t\tmaps.Copy(axis_values, axis_overrides)\n\t\tfs.axes = axis_values\n\t\tfs.style = settable_string{\"\", false}\n\t} else if named_style != \"\" {\n\t\tfs.style = settable_string{named_style, true}\n\t\tfs.axes = nil\n\t}\n\tself.set(fs.String())\n\treturn nil\n}\n\nfunc (self *face_panel) set_style(named_style string) error {\n\tfs, err := self.new_font_spec()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfs.style = settable_string{named_style, true}\n\tself.set(fs.String())\n\treturn nil\n}\n\nfunc (self *face_panel) render_lines(start_y int, lines ...string) (y int) {\n\tsz, _ := self.handler.lp.ScreenSize()\n\t_, y, str := self.handler.render_lines.InRectangle(lines, 0, start_y, int(sz.WidthCells), int(sz.HeightCells)-y, &self.handler.mouse_state, self.on_click)\n\tself.handler.lp.QueueWriteString(str)\n\treturn\n}\n\nconst current_val_style = \"fg=cyan bold\"\nconst control_name_style = \"fg=yellow bright bold\"\n\nfunc (self *face_panel) draw_axis(sz loop.ScreenSize, y int, ax VariableAxis, axis_value float64) int {\n\tlp := self.handler.lp\n\tbuf := strings.Builder{}\n\tbuf.WriteString(fmt.Sprintf(\"%s: \", lp.SprintStyled(control_name_style, utils.IfElse(ax.Strid != \"\", ax.Strid, ax.Tag))))\n\tnum_of_cells := int(sz.WidthCells) - wcswidth.Stringwidth(buf.String())\n\tif num_of_cells < 5 {\n\t\treturn y\n\t}\n\tfrac := (min(axis_value, ax.Maximum) - ax.Minimum) / (ax.Maximum - ax.Minimum)\n\tcurrent_cell := int(math.Floor(frac * float64(num_of_cells-1)))\n\tfor i := range num_of_cells {\n\t\tbuf.WriteString(utils.IfElse(i == current_cell, lp.SprintStyled(current_val_style, `⬤`),\n\t\t\ttui.InternalHyperlink(\"•\", fmt.Sprintf(\"axis:%d/%d:%s\", i, num_of_cells-1, ax.Tag))))\n\t}\n\treturn self.render_lines(y, buf.String())\n}\n\nfunc is_current_named_style(style_group_name, style_name string, vd VariableData, ns NamedStyle) bool {\n\tfor _, dax := range vd.Design_axes {\n\t\tif dax.Name == style_group_name {\n\t\t\tif val, found := ns.Axis_values[dax.Tag]; found {\n\t\t\t\tfor _, v := range dax.Values {\n\t\t\t\t\tif v.Value == val {\n\t\t\t\t\t\treturn v.Name == style_name\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *face_panel) draw_variable_fine_tune(sz loop.ScreenSize, start_y int, preview RenderedSampleTransmit) (y int, err error) {\n\ts := styles_for_variable_data(preview.Variable_data)\n\tlines := []string{}\n\tlp := self.handler.lp\n\tfor _, sg := range s.style_groups {\n\t\tif len(sg.styles) < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tformatted := make([]string, len(sg.styles))\n\t\tfor i, style_name := range sg.styles {\n\t\t\tif is_current_named_style(sg.name, style_name, preview.Variable_data, preview.Variable_named_style) {\n\t\t\t\tformatted[i] = self.handler.lp.SprintStyled(current_val_style, style_name)\n\t\t\t} else {\n\t\t\t\tformatted[i] = tui.InternalHyperlink(style_name, \"variable_style:\"+style_name)\n\t\t\t}\n\t\t}\n\t\tline := lp.SprintStyled(control_name_style, sg.name) + \": \" + strings.Join(formatted, \", \")\n\t\tlines = append(lines, line)\n\t}\n\ty = self.render_lines(start_y, lines...)\n\tsub_title := \"Fine tune the appearance by clicking in the variable axes below:\"\n\taxis_values := self.current_preview.current_axis_values()\n\tfor _, ax := range self.current_preview.Variable_data.Axes {\n\t\tif ax.Hidden {\n\t\t\tcontinue\n\t\t}\n\t\tif sub_title != \"\" {\n\t\t\ty = self.render_lines(y+1, sub_title, \"\")\n\t\t\tsub_title = ``\n\t\t}\n\t\ty = self.draw_axis(sz, y, ax, axis_values[ax.Tag])\n\t}\n\treturn y, nil\n}\n\nfunc (self *face_panel) draw_family_style_select(_ loop.ScreenSize, start_y int, preview RenderedSampleTransmit) (y int, err error) {\n\tlp := self.handler.lp\n\ts := styles_in_family(self.family, self.handler.listing.fonts[self.family])\n\tlines := []string{}\n\tfor _, sg := range s.style_groups {\n\t\tformatted := make([]string, len(sg.styles))\n\t\tfor i, style_name := range sg.styles {\n\t\t\tif style_name == preview.Style {\n\t\t\t\tformatted[i] = lp.SprintStyled(current_val_style, style_name)\n\t\t\t} else {\n\t\t\t\tformatted[i] = tui.InternalHyperlink(style_name, \"style:\"+style_name)\n\t\t\t}\n\t\t}\n\t\tline := lp.SprintStyled(control_name_style, sg.name) + \": \" + strings.Join(formatted, \", \")\n\t\tlines = append(lines, line)\n\t}\n\ty = self.render_lines(start_y, lines...)\n\treturn y, nil\n}\n\nfunc (self *face_panel) draw_font_features(_ loop.ScreenSize, start_y int, preview RenderedSampleTransmit) (y int, err error) {\n\tlp := self.handler.lp\n\ty = start_y\n\tif len(preview.Features) == 0 {\n\t\treturn\n\t}\n\tformatted := make([]string, 0, len(preview.Features))\n\tsort_keys := make(map[string]string)\n\tfor feat_tag, data := range preview.Features {\n\t\tvar text, sort_key string\n\n\t\tif preview.Applied_features[feat_tag] != \"\" {\n\t\t\ttext = preview.Applied_features[feat_tag]\n\t\t\tsort_key = text\n\t\t\tif sort_key[0] == '-' || sort_key[1] == '+' {\n\t\t\t\tsort_key = sort_key[1:]\n\t\t\t}\n\t\t\ttext = strings.Replace(text, \"+\", lp.SprintStyled(\"fg=green\", \"+\"), 1)\n\t\t\ttext = strings.Replace(text, \"-\", lp.SprintStyled(\"fg=red\", \"-\"), 1)\n\t\t\ttext = strings.Replace(text, \"=\", lp.SprintStyled(\"fg=cyan\", \"=\"), 1)\n\t\t\tif data.Name != \"\" {\n\t\t\t\ttext = data.Name + \": \" + text\n\t\t\t\tsort_key = data.Name\n\t\t\t}\n\t\t} else {\n\t\t\tif data.Name != \"\" {\n\t\t\t\ttext = data.Name\n\t\t\t\tsort_key = data.Name + \": \" + text\n\t\t\t} else {\n\t\t\t\ttext = feat_tag\n\t\t\t\tsort_key = text\n\t\t\t}\n\t\t\ttext = lp.SprintStyled(\"dim\", text)\n\t\t}\n\t\tf := tui.InternalHyperlink(text, \"feature:\"+feat_tag)\n\t\tsort_keys[f] = strings.ToLower(sort_key)\n\t\tformatted = append(formatted, f)\n\t}\n\tutils.StableSortWithKey(formatted, func(a string) string { return sort_keys[a] })\n\tline := lp.SprintStyled(control_name_style, `Features`) + \": \" + strings.Join(formatted, \", \")\n\ty = self.render_lines(start_y, ``, line)\n\treturn\n}\n\nfunc (self *handler) draw_preview_header(x int) {\n\tsz, _ := self.lp.ScreenSize()\n\twidth := int(sz.WidthCells) - x\n\tp := center_string(self.lp.SprintStyled(\"italic\", \" preview \"), width, \"─\")\n\tself.lp.QueueWriteString(self.lp.SprintStyled(\"dim\", p))\n}\n\nfunc (self *face_panel) render_preview(key faces_preview_key) {\n\tvar r map[string]RenderedSampleTransmit\n\ts := key.settings\n\tself.handler.set_worker_error(kitty_font_backend.query(\"render_family_samples\", map[string]any{\n\t\t\"text_style\": self.handler.text_style, \"font_family\": s.font_family,\n\t\t\"bold_font\": s.bold_font, \"italic_font\": s.italic_font, \"bold_italic_font\": s.bold_italic_font,\n\t\t\"width\": key.width, \"height\": key.height, \"output_dir\": self.handler.temp_dir,\n\t}, &r))\n\tself.preview_cache_mutex.Lock()\n\tdefer self.preview_cache_mutex.Unlock()\n\tself.preview_cache[key] = r\n}\n\nfunc (self *face_panel) draw_screen() (err error) {\n\tlp := self.handler.lp\n\tlp.SetCursorVisible(false)\n\tsz, _ := lp.ScreenSize()\n\tstyled := lp.SprintStyled\n\twt := \"Regular\"\n\tswitch self.which {\n\tcase \"bold_font\":\n\t\twt = \"Bold\"\n\tcase \"italic_font\":\n\t\twt = \"Italic\"\n\tcase \"bold_italic_font\":\n\t\twt = \"Bold-Italic font\"\n\t}\n\n\tlp.QueueWriteString(self.handler.format_title(fmt.Sprintf(\"%s: %s face\", self.family, wt), 0))\n\n\tlines := []string{\n\t\tfmt.Sprintf(\"Press %s to accept any changes or %s to cancel. Click on a style name below to switch to it.\", styled(\"fg=green\", \"Enter\"), styled(\"fg=red\", \"Esc\")), \"\",\n\t\tfmt.Sprintf(\"Current setting: %s\", self.get()), \"\",\n\t}\n\ty := self.render_lines(2, lines...)\n\n\tnum_lines_per_font := (int(sz.HeightCells) - y - 1) - 2\n\tnum_lines := max(1, num_lines_per_font)\n\tkey := faces_preview_key{settings: self.settings, width: int(sz.WidthCells * sz.CellWidth), height: int(sz.CellHeight) * num_lines}\n\tself.current_preview_key = key\n\tself.preview_cache_mutex.Lock()\n\tdefer self.preview_cache_mutex.Unlock()\n\tpreviews, found := self.preview_cache[key]\n\tif !found {\n\t\tself.preview_cache[key] = make(map[string]RenderedSampleTransmit)\n\t\tgo func() {\n\t\t\tself.render_preview(key)\n\t\t\tself.handler.lp.WakeupMainThread()\n\t\t}()\n\t\treturn\n\t}\n\tif len(previews) < 4 {\n\t\treturn\n\t}\n\tpreview := previews[self.which]\n\tself.current_preview = &preview\n\tif len(preview.Variable_data.Axes) > 0 {\n\t\ty, err = self.draw_variable_fine_tune(sz, y, preview)\n\t} else {\n\t\ty, err = self.draw_family_style_select(sz, y, preview)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tif y, err = self.draw_font_features(sz, y, preview); err != nil {\n\t\treturn err\n\t}\n\n\tnum_lines = int(math.Ceil(float64(preview.Canvas_height) / float64(sz.CellHeight)))\n\tif int(sz.HeightCells)-y >= num_lines+2 {\n\t\ty++\n\t\tlp.MoveCursorTo(1, y+1)\n\t\tself.handler.draw_preview_header(0)\n\t\ty++\n\t\tlp.MoveCursorTo(1, y+1)\n\t\tself.handler.graphics_manager.display_image(0, preview.Path, preview.Canvas_width, preview.Canvas_height)\n\t}\n\treturn\n}\n\nfunc (self *face_panel) initialize(h *handler) (err error) {\n\tself.handler = h\n\tself.preview_cache = make(map[faces_preview_key]map[string]RenderedSampleTransmit)\n\treturn\n}\n\nfunc (self *face_panel) on_wakeup() error {\n\treturn self.handler.draw_screen()\n}\n\nfunc (self *face_panel) get() string {\n\tswitch self.which {\n\tcase \"font_family\":\n\t\treturn self.settings.font_family\n\tcase \"bold_font\":\n\t\treturn self.settings.bold_font\n\tcase \"italic_font\":\n\t\treturn self.settings.italic_font\n\tcase \"bold_italic_font\":\n\t\treturn self.settings.bold_italic_font\n\t}\n\tpanic(fmt.Sprintf(\"Unknown self.which value: %s\", self.which))\n}\n\nfunc (self *face_panel) set(setting string) {\n\tswitch self.which {\n\tcase \"font_family\":\n\t\tself.settings.font_family = setting\n\tcase \"bold_font\":\n\t\tself.settings.bold_font = setting\n\tcase \"italic_font\":\n\t\tself.settings.italic_font = setting\n\tcase \"bold_italic_font\":\n\t\tself.settings.bold_italic_font = setting\n\t}\n}\n\nfunc (self *face_panel) update_feature_in_setting(pff ParsedFontFeature) error {\n\tfs, err := self.new_font_spec()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfound := false\n\tfor _, f := range fs.features {\n\t\tif f.tag == pff.tag {\n\t\t\tf.val = pff.val\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !found {\n\t\tfs.features = append(fs.features, &pff)\n\t}\n\tself.set(fs.String())\n\treturn nil\n}\n\nfunc (self *face_panel) remove_feature_in_setting(tag string) error {\n\tfs, err := self.new_font_spec()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(fs.features) > 0 {\n\t\tfs.features = slices.DeleteFunc(fs.features, func(x *ParsedFontFeature) bool {\n\t\t\treturn x.tag == tag\n\t\t})\n\t}\n\tself.set(fs.String())\n\treturn nil\n}\n\nfunc (self *face_panel) change_feature_value(tag string, val uint, remove bool) error {\n\tif remove {\n\t\treturn self.remove_feature_in_setting(tag)\n\t}\n\tpff := ParsedFontFeature{tag: tag, val: val}\n\treturn self.update_feature_in_setting(pff)\n}\n\nfunc (self *face_panel) handle_click_on_feature(feat_tag string) error {\n\td := self.current_preview.Features[feat_tag]\n\tif d.Is_index {\n\t\tvar current_val uint\n\t\tfor q, serialized := range self.current_preview.Applied_features {\n\t\t\tif q == feat_tag && serialized != \"\" {\n\t\t\t\tif _, num, found := strings.Cut(serialized, \"=\"); found {\n\t\t\t\t\tif v, err := strconv.ParseUint(num, 10, 0); err == nil {\n\t\t\t\t\t\tcurrent_val = uint(v)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcurrent_val = utils.IfElse(serialized[0] == '-', uint(0), uint(1))\n\t\t\t\t}\n\t\t\t\treturn self.handler.if_pane.on_enter(self.family, self.which, self.settings, feat_tag, d, current_val)\n\t\t\t}\n\t\t}\n\t\treturn self.handler.if_pane.on_enter(self.family, self.which, self.settings, feat_tag, d, current_val)\n\t} else {\n\t\tfor q, serialized := range self.current_preview.Applied_features {\n\t\t\tif q == feat_tag && serialized != \"\" {\n\t\t\t\tif serialized[0] == '-' {\n\t\t\t\t\treturn self.remove_feature_in_setting(feat_tag)\n\t\t\t\t}\n\t\t\t\treturn self.update_feature_in_setting(ParsedFontFeature{tag: feat_tag, is_bool: true, val: 0})\n\t\t\t}\n\t\t}\n\t\treturn self.update_feature_in_setting(ParsedFontFeature{tag: feat_tag, is_bool: true, val: 1})\n\t}\n}\n\nfunc (self *face_panel) on_click(id string) (err error) {\n\tscheme, val, _ := strings.Cut(id, \":\")\n\tswitch scheme {\n\tcase \"style\":\n\t\tif err = self.set_style(val); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"variable_style\":\n\t\tif err = self.set_variable_spec(val, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"feature\":\n\t\tif err = self.handle_click_on_feature(val); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"axis\":\n\t\tp, tag, _ := strings.Cut(val, \":\")\n\t\tnum, den, _ := strings.Cut(p, \"/\")\n\t\tn, _ := strconv.Atoi(num)\n\t\td, _ := strconv.Atoi(den)\n\t\tfrac := float64(n) / float64(d)\n\t\tfor _, ax := range self.current_preview.Variable_data.Axes {\n\t\t\tif ax.Tag == tag {\n\t\t\t\taxval := ax.Minimum + (ax.Maximum-ax.Minimum)*frac\n\t\t\t\tif err = self.set_variable_spec(\"\", map[string]float64{tag: axval}); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\t// Render preview synchronously to void flashing\n\tkey := self.current_preview_key\n\tkey.settings = self.settings\n\tself.preview_cache_mutex.Lock()\n\tpreviews := self.preview_cache[key]\n\tself.preview_cache_mutex.Unlock()\n\tif len(previews) < 4 {\n\t\tself.render_preview(key)\n\t}\n\treturn self.handler.draw_screen()\n}\n\nfunc (self *face_panel) on_key_event(event *loop.KeyEvent) (err error) {\n\tif event.MatchesPressOrRepeat(\"esc\") {\n\t\tevent.Handled = true\n\t\tself.handler.current_pane = &self.handler.faces\n\t\treturn self.handler.draw_screen()\n\t} else if event.MatchesPressOrRepeat(\"enter\") {\n\t\tevent.Handled = true\n\t\tself.handler.current_pane = &self.handler.faces\n\t\tself.handler.faces.settings = self.settings\n\t\treturn self.handler.draw_screen()\n\t}\n\treturn\n}\n\nfunc (self *face_panel) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) {\n\treturn\n}\n\nfunc (self *face_panel) on_enter(family, which string, settings faces_settings) error {\n\tself.family = family\n\tself.settings = settings\n\tself.which = which\n\tself.handler.current_pane = self\n\treturn self.handler.draw_screen()\n}\n"
  },
  {
    "path": "kittens/choose_fonts/faces.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype faces_settings struct {\n\tfont_family, bold_font, italic_font, bold_italic_font string\n}\n\ntype faces_preview_key struct {\n\tsettings      faces_settings\n\twidth, height int\n}\n\ntype faces struct {\n\thandler *handler\n\n\tfamily              string\n\tsettings            faces_settings\n\tpreview_cache       map[faces_preview_key]map[string]RenderedSampleTransmit\n\tpreview_cache_mutex sync.Mutex\n}\n\nconst highlight_key_style = \"fg=magenta bold\"\n\nfunc (self *faces) draw_screen() (err error) {\n\tlp := self.handler.lp\n\tlp.SetCursorVisible(false)\n\tsz, _ := lp.ScreenSize()\n\tstyled := lp.SprintStyled\n\tlp.QueueWriteString(self.handler.format_title(self.family, 0))\n\tlines := []string{\n\t\tfmt.Sprintf(\"Press %s to select this font, %s to go back to the font list or any of the %s keys below to fine-tune the appearance of the individual font styles.\", styled(\"fg=green\", \"Enter\"), styled(\"fg=red\", \"Esc\"), styled(highlight_key_style, \"highlighted\")), \"\",\n\t}\n\t_, y, str := self.handler.render_lines.InRectangle(lines, 0, 2, int(sz.WidthCells), int(sz.HeightCells), &self.handler.mouse_state, self.on_click)\n\n\tlp.QueueWriteString(str)\n\n\tnum_lines_per_font := ((int(sz.HeightCells) - y - 1) / 4) - 2\n\tnum_lines := max(1, num_lines_per_font)\n\tkey := faces_preview_key{settings: self.settings, width: int(sz.WidthCells * sz.CellWidth), height: int(sz.CellHeight) * num_lines}\n\tself.preview_cache_mutex.Lock()\n\tdefer self.preview_cache_mutex.Unlock()\n\tpreviews, found := self.preview_cache[key]\n\tif !found {\n\t\tself.preview_cache[key] = make(map[string]RenderedSampleTransmit)\n\t\tgo func() {\n\t\t\tvar r map[string]RenderedSampleTransmit\n\t\t\ts := key.settings\n\t\t\tself.handler.set_worker_error(kitty_font_backend.query(\"render_family_samples\", map[string]any{\n\t\t\t\t\"text_style\": self.handler.text_style, \"font_family\": s.font_family,\n\t\t\t\t\"bold_font\": s.bold_font, \"italic_font\": s.italic_font, \"bold_italic_font\": s.bold_italic_font,\n\t\t\t\t\"width\": key.width, \"height\": key.height, \"output_dir\": self.handler.temp_dir,\n\t\t\t}, &r))\n\t\t\tself.preview_cache_mutex.Lock()\n\t\t\tdefer self.preview_cache_mutex.Unlock()\n\t\t\tself.preview_cache[key] = r\n\t\t\tself.handler.lp.WakeupMainThread()\n\t\t}()\n\t\treturn\n\t}\n\tif len(previews) < 4 {\n\t\treturn\n\t}\n\n\tslot := 0\n\td := func(setting, title string) {\n\t\tr := previews[setting]\n\t\tnum_lines := int(math.Ceil(float64(r.Canvas_height) / float64(sz.CellHeight)))\n\t\tif int(sz.HeightCells)-y < num_lines+1 {\n\t\t\treturn\n\t\t}\n\t\tlp.MoveCursorTo(1, y+1)\n\t\t_, y, str = self.handler.render_lines.InRectangle([]string{title + \": \" + previews[setting].Psname}, 0, y, int(sz.WidthCells), int(sz.HeightCells), &self.handler.mouse_state, self.on_click)\n\t\tlp.QueueWriteString(str)\n\t\tif y+num_lines < int(sz.HeightCells) {\n\t\t\tlp.MoveCursorTo(1, y+1)\n\t\t\tself.handler.graphics_manager.display_image(slot, r.Path, r.Canvas_width, r.Canvas_height)\n\t\t\tslot++\n\t\t\ty += num_lines + 1\n\t\t}\n\t}\n\td(`font_family`, styled(highlight_key_style, \"R\")+`egular`)\n\td(`bold_font`, styled(highlight_key_style, \"B\")+`old`)\n\td(`italic_font`, styled(highlight_key_style, \"I\")+`talic`)\n\td(`bold_italic_font`, \"B\"+styled(highlight_key_style, \"o\")+`ld-Italic`)\n\n\treturn\n}\n\nfunc (self *faces) initialize(h *handler) (err error) {\n\tself.handler = h\n\tself.preview_cache = make(map[faces_preview_key]map[string]RenderedSampleTransmit)\n\treturn\n}\n\nfunc (self *faces) on_wakeup() error {\n\treturn self.handler.draw_screen()\n}\n\nfunc (self *faces) on_click(id string) (err error) {\n\treturn\n}\n\nfunc (self *faces) on_key_event(event *loop.KeyEvent) (err error) {\n\tif event.MatchesPressOrRepeat(\"esc\") {\n\t\tevent.Handled = true\n\t\tself.handler.current_pane = &self.handler.listing\n\t\treturn self.handler.draw_screen()\n\t}\n\tif event.MatchesPressOrRepeat(\"enter\") {\n\t\tevent.Handled = true\n\t\treturn self.handler.final_pane.on_enter(self.family, self.settings)\n\t}\n\treturn\n}\n\nfunc (self *faces) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) {\n\tif from_key_event {\n\t\twhich := \"\"\n\t\tswitch text {\n\t\tcase \"r\", \"R\":\n\t\t\twhich = \"font_family\"\n\t\tcase \"b\", \"B\":\n\t\t\twhich = \"bold_font\"\n\t\tcase \"i\", \"I\":\n\t\t\twhich = \"italic_font\"\n\t\tcase \"o\", \"O\":\n\t\t\twhich = \"bold_italic_font\"\n\t\t}\n\t\tif which != \"\" {\n\t\t\treturn self.handler.face_pane.on_enter(self.family, which, self.settings)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *faces) on_enter(family string) error {\n\tif family != \"\" {\n\t\tself.family = family\n\t\tr := self.handler.listing.resolved_faces_from_kitty_conf\n\t\td := func(conf ResolvedFace, setting *string, defval string) {\n\t\t\ts := utils.IfElse(conf.Setting == \"auto\", \"auto\", conf.Spec)\n\t\t\t*setting = utils.IfElse(family == conf.Family, s, defval)\n\t\t}\n\t\td(r.Font_family, &self.settings.font_family, fmt.Sprintf(`family=\"%s\"`, family))\n\t\td(r.Bold_font, &self.settings.bold_font, \"auto\")\n\t\td(r.Italic_font, &self.settings.italic_font, \"auto\")\n\t\td(r.Bold_italic_font, &self.settings.bold_italic_font, \"auto\")\n\t}\n\tself.handler.current_pane = self\n\treturn self.handler.draw_screen()\n}\n"
  },
  {
    "path": "kittens/choose_fonts/family_list.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/subseq\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype FamilyList struct {\n\tfamilies, all_families []string\n\tcurrent_search         string\n\tdisplay_strings        []string\n\twidths                 []int\n\tmax_width, current_idx int\n}\n\nfunc (self *FamilyList) Len() int {\n\treturn len(self.families)\n}\n\nfunc (self *FamilyList) Select(family string) bool {\n\tfor idx, q := range self.families {\n\t\tif q == family {\n\t\t\tself.current_idx = idx\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *FamilyList) Next(delta int, allow_wrapping bool) bool {\n\tl := func() int { return self.Len() }\n\tif l() == 0 {\n\t\treturn false\n\t}\n\tidx := self.current_idx + delta\n\tif !allow_wrapping && (idx < 0 || idx > l()) {\n\t\treturn false\n\t}\n\tfor idx < 0 {\n\t\tidx += l()\n\t}\n\tself.current_idx = idx % l()\n\treturn true\n}\n\nfunc limit_lengths(text string) string {\n\tt, _ := wcswidth.TruncateToVisualLengthWithWidth(text, 31)\n\tif len(t) >= len(text) {\n\t\treturn text\n\t}\n\treturn t + \"…\"\n}\n\nfunc match(expression string, items []string) []*subseq.Match {\n\tmatches := subseq.ScoreItems(expression, items, subseq.Options{Level1: \" \"})\n\tmatches = utils.StableSort(matches, func(a, b *subseq.Match) int {\n\t\tif b.Score < a.Score {\n\t\t\treturn -1\n\t\t}\n\t\tif b.Score > a.Score {\n\t\t\treturn 1\n\t\t}\n\t\treturn 0\n\t})\n\treturn matches\n}\n\nconst (\n\tMARK_BEFORE = \"\\033[33m\"\n\tMARK_AFTER  = \"\\033[39m\"\n)\n\nfunc apply_search(families []string, expression string, marks ...string) (matched_families []string, display_strings []string) {\n\tmark_before, mark_after := MARK_BEFORE, MARK_AFTER\n\tif len(marks) == 2 {\n\t\tmark_before, mark_after = marks[0], marks[1]\n\t}\n\tresults := utils.Filter(match(expression, families), func(x *subseq.Match) bool { return x.Score > 0 })\n\tmatched_families = make([]string, 0, len(results))\n\tdisplay_strings = make([]string, 0, len(results))\n\tfor _, m := range results {\n\t\ttext := m.Text\n\t\tpositions := m.Positions\n\t\tfor i := len(positions) - 1; i >= 0; i-- {\n\t\t\tp := positions[i]\n\t\t\ttext = text[:p] + mark_before + text[p:p+1] + mark_after + text[p+1:]\n\t\t}\n\t\tdisplay_strings = append(display_strings, text)\n\t\tmatched_families = append(matched_families, m.Text)\n\t}\n\treturn\n}\n\nfunc make_family_names_clickable(family string) string {\n\tid := wcswidth.StripEscapeCodes(family)\n\treturn tui.InternalHyperlink(family, \"family-chosen:\"+id)\n}\n\nfunc (self *FamilyList) UpdateFamilies(families []string) {\n\tself.families, self.all_families = families, families\n\tif self.current_search != \"\" {\n\t\tself.families, self.display_strings = apply_search(self.all_families, self.current_search)\n\t\tself.display_strings = utils.Map(limit_lengths, self.display_strings)\n\t} else {\n\t\tself.display_strings = utils.Map(limit_lengths, families)\n\t}\n\tself.display_strings = utils.Map(make_family_names_clickable, self.display_strings)\n\tself.widths = utils.Map(wcswidth.Stringwidth, self.display_strings)\n\tself.max_width = utils.Max(0, self.widths...)\n\tself.current_idx = 0\n}\n\nfunc (self *FamilyList) UpdateSearch(query string) bool {\n\tif query == self.current_search || len(self.all_families) == 0 {\n\t\treturn false\n\t}\n\tself.current_search = query\n\tself.UpdateFamilies(self.all_families)\n\treturn true\n}\n\ntype Line struct {\n\ttext       string\n\twidth      int\n\tis_current bool\n}\n\nfunc (self *FamilyList) Lines(num_rows int) []Line {\n\tif num_rows < 1 {\n\t\treturn nil\n\t}\n\tans := make([]Line, 0, len(self.display_strings))\n\tbefore_num := utils.Min(self.current_idx, num_rows-1)\n\tstart := self.current_idx - before_num\n\tfor i := start; i < utils.Min(start+num_rows, len(self.display_strings)); i++ {\n\t\tans = append(ans, Line{self.display_strings[i], self.widths[i], i == self.current_idx})\n\t}\n\treturn ans\n}\n\nfunc (self *FamilyList) SelectFamily(family string) bool {\n\tfor i, f := range self.families {\n\t\tif f == family {\n\t\t\tself.current_idx = i\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *FamilyList) CurrentFamily() string {\n\tif self.current_idx >= 0 && self.current_idx < len(self.families) {\n\t\treturn self.families[self.current_idx]\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "kittens/choose_fonts/final.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype final_pane struct {\n\thandler  *handler\n\tsettings faces_settings\n\tfamily   string\n\tlp       *loop.Loop\n}\n\nfunc (self *final_pane) render_lines(start_y int, lines ...string) (y int) {\n\tsz, _ := self.handler.lp.ScreenSize()\n\t_, y, str := self.handler.render_lines.InRectangle(lines, 0, start_y, int(sz.WidthCells), int(sz.HeightCells)-y, &self.handler.mouse_state, self.on_click)\n\tself.handler.lp.QueueWriteString(str)\n\treturn\n}\n\nfunc (self *final_pane) draw_screen() (err error) {\n\ts := self.lp.SprintStyled\n\th := func(x string) string { return s(highlight_key_style, x) }\n\n\tself.render_lines(0,\n\t\tfmt.Sprintf(\"You have chosen the %s family\", s(current_val_style, self.family)),\n\t\t\"\",\n\t\t\"What would you like to do?\",\n\t\t\"\",\n\t\tfmt.Sprintf(\"%s to modify %s and use the new fonts\", h(\"Enter\"), s(\"italic\", self.handler.opts.Config_file_name)),\n\t\t\"\",\n\t\tfmt.Sprintf(\"%s to abort and return to font selection\", h(\"Esc\")),\n\t\t\"\",\n\t\tfmt.Sprintf(\"%s to write the new font settings to %s\", h(\"s\"), s(\"italic\", `STDOUT`)),\n\t\t\"\",\n\t\tfmt.Sprintf(\"%s to quit\", h(\"Ctrl+c\")),\n\t)\n\treturn\n}\n\nfunc (self *final_pane) initialize(h *handler) (err error) {\n\tself.handler = h\n\tself.lp = h.lp\n\treturn\n}\n\nfunc (self *final_pane) on_wakeup() error {\n\treturn self.handler.draw_screen()\n}\n\nfunc (self *final_pane) on_click(id string) (err error) {\n\treturn\n}\n\nfunc (self faces_settings) serialized() string {\n\treturn strings.Join([]string{\n\t\t\"font_family      \" + self.font_family,\n\t\t\"bold_font        \" + self.bold_font,\n\t\t\"italic_font      \" + self.italic_font,\n\t\t\"bold_italic_font \" + self.bold_italic_font,\n\t}, \"\\n\")\n}\n\nfunc (self *final_pane) on_key_event(event *loop.KeyEvent) (err error) {\n\tif event.MatchesPressOrRepeat(\"esc\") {\n\t\tevent.Handled = true\n\t\tself.handler.current_pane = &self.handler.faces\n\t\treturn self.handler.draw_screen()\n\t}\n\tif event.MatchesPressOrRepeat(\"enter\") {\n\t\tevent.Handled = true\n\t\tpatcher := config.Patcher{Write_backup: true}\n\t\tpath := \"\"\n\t\tif filepath.IsAbs(self.handler.opts.Config_file_name) {\n\t\t\tpath = self.handler.opts.Config_file_name\n\t\t} else {\n\t\t\tpath = filepath.Join(utils.ConfigDir(), self.handler.opts.Config_file_name)\n\t\t}\n\t\tupdated, err := patcher.Patch(path, \"KITTY_FONTS\", self.settings.serialized(), \"font_family\", \"bold_font\", \"italic_font\", \"bold_italic_font\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif updated {\n\t\t\tswitch self.handler.opts.Reload_in {\n\t\t\tcase \"parent\":\n\t\t\t\tconfig.ReloadConfigInKitty(true)\n\t\t\tcase \"all\":\n\t\t\t\tconfig.ReloadConfigInKitty(false)\n\t\t\t}\n\t\t}\n\t\tself.lp.Quit(0)\n\t\treturn nil\n\n\t}\n\treturn\n}\n\nfunc (self *final_pane) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) {\n\tif from_key_event {\n\t\tswitch text {\n\t\tcase \"s\", \"S\":\n\t\t\toutput_on_exit = self.settings.serialized() + \"\\n\"\n\t\t\tself.lp.Quit(0)\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *final_pane) on_enter(family string, settings faces_settings) error {\n\tself.settings = settings\n\tself.family = family\n\tself.handler.current_pane = self\n\treturn self.handler.draw_screen()\n}\n"
  },
  {
    "path": "kittens/choose_fonts/graphics.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype image struct {\n\tid, image_number uint32\n\tcurrent_file     string\n}\n\nfunc (i image) new_graphics_command() *graphics.GraphicsCommand {\n\tgc := &graphics.GraphicsCommand{}\n\tif i.id > 0 {\n\t\tgc.SetImageId(i.id)\n\t} else {\n\t\tgc.SetImageNumber(i.image_number)\n\t}\n\treturn gc\n}\n\ntype graphics_manager struct {\n\tmain, bold, italic, bi, extra image\n\tlp                            *loop.Loop\n\timages                        [5]*image\n}\n\nfunc (g *graphics_manager) initialize(lp *loop.Loop) {\n\tg.images = [5]*image{&g.main, &g.bold, &g.italic, &g.bi, &g.extra}\n\tg.lp = lp\n\tpayload := []byte(\"123\")\n\tbuf := strings.Builder{}\n\tgc := &graphics.GraphicsCommand{}\n\tgc.SetImageNumber(7891230).SetTransmission(graphics.GRT_transmission_direct).SetDataWidth(1).SetDataHeight(1).SetFormat(\n\t\tgraphics.GRT_format_rgb).SetDataSize(uint64(len(payload)))\n\td := func() uint32 {\n\t\tim := gc.ImageNumber()\n\t\tim++\n\t\tgc.SetImageNumber(im)\n\t\t_ = gc.WriteWithPayloadTo(&buf, payload)\n\t\treturn im\n\n\t}\n\tfor _, img := range g.images {\n\t\timg.image_number = d()\n\t}\n\tlp.QueueWriteString(buf.String())\n}\n\nfunc (g *graphics_manager) clear_placements() {\n\tbuf := strings.Builder{}\n\tfor _, img := range g.images {\n\t\tif img.current_file == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tgc := img.new_graphics_command()\n\t\tgc.SetAction(graphics.GRT_action_delete)\n\t\tgc.SetDelete(utils.IfElse(img.id > 0, graphics.GRT_delete_by_id, graphics.GRT_delete_by_number))\n\t\tgc.WriteWithPayloadTo(&buf, nil)\n\t}\n\tg.lp.QueueWriteString(buf.String())\n}\n\nfunc (g *graphics_manager) display_image(slot int, path string, img_width, img_height int) {\n\timg := g.images[slot]\n\tif img.current_file != path {\n\t\tgc := img.new_graphics_command()\n\t\tgc.SetAction(graphics.GRT_action_transmit).SetDataWidth(uint64(img_width)).SetDataHeight(uint64(img_height)).SetTransmission(graphics.GRT_transmission_file)\n\t\tgc.WriteWithPayloadToLoop(g.lp, []byte(path))\n\t\timg.current_file = path\n\t}\n\tgc := img.new_graphics_command()\n\tgc.SetAction(graphics.GRT_action_display).SetCursorMovement(graphics.GRT_cursor_static)\n\tgc.WriteWithPayloadToLoop(g.lp, nil)\n}\n\nfunc (g *graphics_manager) on_response(gc *graphics.GraphicsCommand) (err error) {\n\tif gc.ResponseMessage() != \"OK\" {\n\t\treturn fmt.Errorf(\"Failed to load image with error: %s\\n\\nNote that the choose-fonts kitten does not work over SSH as it is meant to select a locally available font to use in kitty.\", gc.ResponseMessage())\n\t}\n\tfor _, img := range g.images {\n\t\tif img.image_number == gc.ImageNumber() {\n\t\t\timg.id = gc.ImageId()\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc (g *graphics_manager) finalize() {\n\tbuf := strings.Builder{}\n\tfor _, img := range g.images {\n\t\tgc := img.new_graphics_command()\n\t\tgc.SetAction(graphics.GRT_action_delete)\n\t\tgc.SetDelete(utils.IfElse(img.id > 0, graphics.GRT_free_by_id, graphics.GRT_free_by_number))\n\t\tgc.WriteWithPayloadTo(&buf, nil)\n\t}\n\tg.lp.QueueWriteString(buf.String())\n}\n"
  },
  {
    "path": "kittens/choose_fonts/index_feature.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/readline\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype if_panel struct {\n\thandler *handler\n\trl      *readline.Readline\n\n\tfamily, which, feat_tag string\n\tsettings                faces_settings\n\tfeature_data            FeatureData\n\tcurrent_val             uint\n}\n\nfunc (self *if_panel) render_lines(start_y int, lines ...string) (y int) {\n\tsz, _ := self.handler.lp.ScreenSize()\n\t_, y, str := self.handler.render_lines.InRectangle(lines, 0, start_y, int(sz.WidthCells), int(sz.HeightCells)-y, &self.handler.mouse_state, self.on_click)\n\tself.handler.lp.QueueWriteString(str)\n\treturn\n}\n\nfunc (self *if_panel) draw_screen() (err error) {\n\tlp := self.handler.lp\n\tfeat_name := utils.IfElse(self.feature_data.Name == \"\", self.feat_tag, self.feature_data.Name)\n\tlp.QueueWriteString(self.handler.format_title(\"Edit \"+feat_name, 0))\n\tlines := []string{\n\t\tfmt.Sprintf(\"Enter a value for the '%s' feature of the %s font. Values are non-negative integers. Leaving it blank will cause the feature value to be not set, i.e. take its default value.\", feat_name, self.family),\n\t}\n\tif self.feature_data.Tooltip != \"\" {\n\t\tlines = append(lines, \"\")\n\t\tlines = append(lines, self.feature_data.Tooltip)\n\t}\n\tif len(self.feature_data.Params) > 0 {\n\t\tlines = append(lines, \"\")\n\t\tlines = append(lines, \"You can also click on any of the feature names below to choose the corresponding value.\")\n\t} else {\n\t\tlines = append(lines, \"\")\n\t\tlines = append(lines, \"Consult the documentation for this font to find out what values are valid for this feature.\")\n\t}\n\tlines = append(lines, \"\")\n\tcursor_y := self.render_lines(2, lines...)\n\tif len(self.feature_data.Params) > 0 {\n\t\tlp.MoveCursorTo(1, cursor_y+3)\n\t\tnum := 1\n\t\tstrings.Join(utils.Map(func(x string) string {\n\t\t\tans := tui.InternalHyperlink(x, fmt.Sprintf(\"fval:%d\", num))\n\t\t\tnum++\n\t\t\treturn ans\n\t\t}, self.feature_data.Params), \", \")\n\t}\n\tlp.MoveCursorTo(1, cursor_y+1)\n\tlp.ClearToEndOfLine()\n\tself.rl.RedrawNonAtomic()\n\tlp.SetCursorVisible(true)\n\treturn\n}\n\nfunc (self *if_panel) initialize(h *handler) (err error) {\n\tself.handler = h\n\tself.rl = readline.New(h.lp, readline.RlInit{DontMarkPrompts: true, Prompt: \"Value: \"})\n\treturn\n}\n\nfunc (self *if_panel) on_wakeup() error {\n\treturn self.handler.draw_screen()\n}\n\nfunc (self *if_panel) on_click(id string) (err error) {\n\tscheme, val, _ := strings.Cut(id, \":\")\n\tif scheme != \"fval\" {\n\t\treturn\n\t}\n\tv, _ := strconv.ParseUint(val, 10, 0)\n\tif err = self.handler.face_pane.change_feature_value(self.feat_tag, uint(v), false); err != nil {\n\t\treturn err\n\t}\n\tself.handler.current_pane = &self.handler.face_pane\n\treturn self.handler.draw_screen()\n}\n\nfunc (self *if_panel) on_key_event(event *loop.KeyEvent) (err error) {\n\tif event.MatchesPressOrRepeat(\"esc\") {\n\t\tevent.Handled = true\n\t\tself.handler.current_pane = &self.handler.face_pane\n\t\treturn self.handler.draw_screen()\n\t}\n\tif event.MatchesPressOrRepeat(\"enter\") {\n\t\tevent.Handled = true\n\t\ttext := strings.TrimSpace(self.rl.AllText())\n\t\tremove := false\n\t\tvar val uint64\n\t\tif text == \"\" {\n\t\t\tremove = true\n\t\t} else {\n\t\t\tval, err = strconv.ParseUint(text, 10, 0)\n\t\t}\n\t\tif err != nil {\n\t\t\tself.rl.ResetText()\n\t\t\tself.handler.lp.Beep()\n\t\t} else {\n\t\t\tif err = self.handler.face_pane.change_feature_value(self.feat_tag, uint(val), remove); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tself.handler.current_pane = &self.handler.face_pane\n\t\t}\n\t\treturn self.handler.draw_screen()\n\t}\n\tif err = self.rl.OnKeyEvent(event); err != nil {\n\t\tif err == readline.ErrAcceptInput {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\treturn self.draw_screen()\n}\n\nfunc (self *if_panel) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) {\n\tif err = self.rl.OnText(text, from_key_event, in_bracketed_paste); err != nil {\n\t\treturn err\n\t}\n\treturn self.draw_screen()\n}\n\nfunc (self *if_panel) on_enter(family, which string, settings faces_settings, feat_tag string, fd FeatureData, current_val uint) error {\n\tself.family = family\n\tself.feat_tag = feat_tag\n\tself.settings = settings\n\tself.which = which\n\tself.handler.current_pane = self\n\tself.feature_data = fd\n\tself.current_val = current_val\n\tself.rl.ResetText()\n\tif self.current_val > 0 {\n\t\tself.rl.SetText(strconv.FormatUint(uint64(self.current_val), 10))\n\t}\n\treturn self.handler.draw_screen()\n}\n"
  },
  {
    "path": "kittens/choose_fonts/list.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/readline\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype preview_cache_key struct {\n\tfamily        string\n\twidth, height int\n}\n\ntype preview_cache_value struct {\n\tpath          string\n\twidth, height int\n}\n\ntype FontList struct {\n\trl                             *readline.Readline\n\tfamily_list                    FamilyList\n\tfonts                          map[string][]ListedFont\n\tfamily_list_updated            bool\n\tresolved_faces_from_kitty_conf ResolvedFaces\n\thandler                        *handler\n\tvariable_data_requested_for    *utils.Set[string]\n\tpreview_cache                  map[preview_cache_key]preview_cache_value\n\tpreview_cache_mutex            sync.Mutex\n}\n\nfunc (self *FontList) initialize(h *handler) error {\n\tself.handler = h\n\tself.preview_cache = make(map[preview_cache_key]preview_cache_value)\n\tself.rl = readline.New(h.lp, readline.RlInit{DontMarkPrompts: true, Prompt: \"Family: \"})\n\tself.variable_data_requested_for = utils.NewSet[string](256)\n\treturn nil\n}\n\nfunc (self *FontList) draw_search_bar() {\n\tlp := self.handler.lp\n\tlp.SetCursorVisible(true)\n\tlp.SetCursorShape(loop.BAR_CURSOR, true)\n\tsz, err := lp.ScreenSize()\n\tif err != nil {\n\t\treturn\n\t}\n\tlp.MoveCursorTo(1, int(sz.HeightCells))\n\tlp.ClearToEndOfLine()\n\tself.rl.RedrawNonAtomic()\n}\n\nconst SEPARATOR = \"║\"\n\nfunc center_string(x string, width int, filler ...string) string {\n\tspace := \" \"\n\tif len(filler) > 0 {\n\t\tspace = filler[0]\n\t}\n\tl := wcswidth.Stringwidth(x)\n\tspaces := int(float64(width-l) / 2)\n\tspace = strings.Repeat(space, utils.Max(0, spaces))\n\treturn space + x + space\n}\n\nfunc (self *handler) format_title(title string, start_x int) string {\n\tsz, _ := self.lp.ScreenSize()\n\treturn self.lp.SprintStyled(\"fg=green bold\", center_string(title, int(sz.WidthCells)-start_x))\n}\n\nfunc (self *FontList) draw_family_summary(start_x int, sz loop.ScreenSize) (err error) {\n\tlp := self.handler.lp\n\tfamily := self.family_list.CurrentFamily()\n\tif family == \"\" || int(sz.WidthCells) < start_x+2 {\n\t\treturn nil\n\t}\n\tlines := []string{self.handler.format_title(family, start_x), \"\"}\n\twidth := int(sz.WidthCells) - start_x - 1\n\tadd_line := func(x string) {\n\t\tlines = append(lines, style.WrapTextAsLines(x, width, style.WrapOptions{})...)\n\t}\n\tfonts := self.fonts[family]\n\tif len(fonts) == 0 {\n\t\treturn fmt.Errorf(\"The family: %s has no fonts\", family)\n\t}\n\tif has_variable_data_for_font(fonts[0]) {\n\t\ts := styles_in_family(family, fonts)\n\t\tfor _, sg := range s.style_groups {\n\t\t\tstyles := lp.SprintStyled(control_name_style, sg.name) + \": \" + strings.Join(sg.styles, \", \")\n\t\t\tadd_line(styles)\n\t\t\tadd_line(\"\")\n\t\t}\n\t\tif s.has_variable_faces {\n\t\t\tadd_line(fmt.Sprintf(\"This font is %s allowing for finer style control\", lp.SprintStyled(\"fg=magenta\", \"variable\")))\n\t\t}\n\t\tadd_line(fmt.Sprintf(\"Press the %s key to choose this family\", lp.SprintStyled(\"fg=yellow\", \"Enter\")))\n\t} else {\n\t\tlines = append(lines, \"Reading font data, please wait…\")\n\t\tkey := fonts[0].cache_key()\n\t\tif !self.variable_data_requested_for.Has(key) {\n\t\t\tself.variable_data_requested_for.Add(key)\n\t\t\tgo func() {\n\t\t\t\tself.handler.set_worker_error(ensure_variable_data_for_fonts(fonts...))\n\t\t\t\tlp.WakeupMainThread()\n\t\t\t}()\n\t\t}\n\t}\n\n\ty := 0\n\tfor _, line := range lines {\n\t\tif y >= int(sz.HeightCells)-1 {\n\t\t\tbreak\n\t\t}\n\t\tlp.MoveCursorTo(start_x+1, y+1)\n\t\tlp.QueueWriteString(line)\n\t\ty++\n\t}\n\tif self.handler.text_style.Background != \"\" {\n\t\treturn self.draw_preview(start_x, y, sz)\n\t}\n\treturn\n}\n\nfunc (self *FontList) draw_preview(x, y int, sz loop.ScreenSize) (err error) {\n\twidth_cells, height_cells := int(sz.WidthCells)-x, int(sz.HeightCells)-y\n\tif height_cells < 3 {\n\t\treturn\n\t}\n\ty++\n\tself.handler.lp.MoveCursorTo(x+1, y+1)\n\tself.handler.draw_preview_header(x)\n\ty++\n\theight_cells -= 2\n\tself.handler.lp.MoveCursorTo(x+1, y+1)\n\tkey := preview_cache_key{\n\t\tfamily: self.family_list.CurrentFamily(), width: int(sz.CellWidth) * width_cells, height: int(sz.CellHeight) * height_cells,\n\t}\n\tif key.family == \"\" {\n\t\treturn\n\t}\n\tself.preview_cache_mutex.Lock()\n\tdefer self.preview_cache_mutex.Unlock()\n\tcc := self.preview_cache[key]\n\tswitch cc.path {\n\tcase \"\":\n\t\tself.preview_cache[key] = preview_cache_value{path: \"requested\"}\n\t\tgo func() {\n\t\t\tvar r map[string]RenderedSampleTransmit\n\t\t\tself.handler.set_worker_error(kitty_font_backend.query(\"render_family_samples\", map[string]any{\n\t\t\t\t\"text_style\": self.handler.text_style, \"font_family\": key.family, \"width\": key.width, \"height\": key.height,\n\t\t\t\t\"output_dir\": self.handler.temp_dir,\n\t\t\t}, &r))\n\t\t\tself.preview_cache_mutex.Lock()\n\t\t\tdefer self.preview_cache_mutex.Unlock()\n\t\t\tself.preview_cache[key] = preview_cache_value{path: r[\"font_family\"].Path, width: r[\"font_family\"].Canvas_width, height: r[\"font_family\"].Canvas_height}\n\t\t\tself.handler.lp.WakeupMainThread()\n\t\t}()\n\t\treturn\n\tcase \"requested\":\n\t\treturn\n\t}\n\tself.handler.graphics_manager.display_image(0, cc.path, cc.width, cc.height)\n\treturn\n}\n\nfunc (self *FontList) on_wakeup() error {\n\tif !self.family_list_updated {\n\t\tself.family_list_updated = true\n\t\tself.family_list.UpdateFamilies(utils.StableSortWithKey(utils.Keys(self.fonts), strings.ToLower))\n\t\tself.family_list.SelectFamily(self.resolved_faces_from_kitty_conf.Font_family.Family)\n\t}\n\treturn self.handler.draw_screen()\n}\n\nfunc (self *FontList) draw_screen() (err error) {\n\tlp := self.handler.lp\n\tsz, err := lp.ScreenSize()\n\tif err != nil {\n\t\treturn err\n\t}\n\tnum_rows := max(0, int(sz.HeightCells)-1)\n\tmw := self.family_list.max_width + 1\n\tgreen_fg, _, _ := strings.Cut(lp.SprintStyled(\"fg=green\", \"|\"), \"|\")\n\tlines := make([]string, 0, num_rows)\n\tfor _, l := range self.family_list.Lines(num_rows) {\n\t\tline := l.text\n\t\tif l.is_current {\n\t\t\tline = strings.ReplaceAll(line, MARK_AFTER, green_fg)\n\t\t\tline = lp.SprintStyled(\"fg=green\", \">\") + lp.SprintStyled(\"fg=green bold\", line)\n\t\t} else {\n\t\t\tline = \" \" + line\n\t\t}\n\t\tlines = append(lines, line)\n\t}\n\t_, _, str := self.handler.render_lines.InRectangle(lines, 0, 0, 0, num_rows, &self.handler.mouse_state, self.on_click)\n\tlp.QueueWriteString(str)\n\tseps := strings.Repeat(SEPARATOR, num_rows)\n\tseps = strings.TrimSpace(seps)\n\t_, _, str = self.handler.render_lines.InRectangle(strings.Split(seps, \"\"), mw+1, 0, 0, num_rows, &self.handler.mouse_state)\n\tlp.QueueWriteString(str)\n\n\tif self.family_list.Len() > 0 {\n\t\tif err = self.draw_family_summary(mw+3, sz); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tself.draw_search_bar()\n\treturn\n}\n\nfunc (self *FontList) on_click(id string) error {\n\twhich, data, found := strings.Cut(id, \":\")\n\tif !found {\n\t\treturn fmt.Errorf(\"Not a valid click id: %s\", id)\n\t}\n\tswitch which {\n\tcase \"family-chosen\":\n\t\tif self.handler.state == LISTING_FAMILIES {\n\t\t\tif self.family_list.Select(data) {\n\t\t\t\tself.handler.draw_screen()\n\t\t\t} else {\n\t\t\t\tself.handler.lp.Beep()\n\t\t\t}\n\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *FontList) update_family_search() {\n\ttext := self.rl.AllText()\n\tif self.family_list.UpdateSearch(text) {\n\t\tself.handler.draw_screen()\n\t} else {\n\t\tself.draw_search_bar()\n\t}\n}\n\nfunc (self *FontList) next(delta int, allow_wrapping bool) error {\n\tif self.family_list.Next(delta, allow_wrapping) {\n\t\treturn self.handler.draw_screen()\n\t}\n\tself.handler.lp.Beep()\n\treturn nil\n}\n\nfunc (self *FontList) on_key_event(event *loop.KeyEvent) (err error) {\n\tif event.MatchesPressOrRepeat(\"enter\") {\n\t\tevent.Handled = true\n\t\tif family := self.family_list.CurrentFamily(); family != \"\" {\n\t\t\treturn self.handler.faces.on_enter(family)\n\t\t}\n\t\tself.handler.lp.Beep()\n\t\treturn\n\t}\n\tif event.MatchesPressOrRepeat(\"esc\") {\n\t\tevent.Handled = true\n\t\tif self.rl.AllText() != \"\" {\n\t\t\tself.rl.ResetText()\n\t\t\tself.update_family_search()\n\t\t\tself.handler.draw_screen()\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"canceled by user\")\n\t\t}\n\t\treturn\n\t}\n\tev := event\n\tif ev.MatchesPressOrRepeat(\"down\") {\n\t\tev.Handled = true\n\t\treturn self.next(1, true)\n\t}\n\tif ev.MatchesPressOrRepeat(\"up\") {\n\t\tev.Handled = true\n\t\treturn self.next(-1, true)\n\t}\n\tif ev.MatchesPressOrRepeat(\"page_down\") {\n\t\tev.Handled = true\n\t\tsz, err := self.handler.lp.ScreenSize()\n\t\tif err == nil {\n\t\t\terr = self.next(int(sz.HeightCells)-3, false)\n\t\t}\n\t\treturn err\n\t}\n\tif ev.MatchesPressOrRepeat(\"page_up\") {\n\t\tev.Handled = true\n\t\tsz, err := self.handler.lp.ScreenSize()\n\t\tif err == nil {\n\t\t\terr = self.next(3-int(sz.HeightCells), false)\n\t\t}\n\t\treturn err\n\t}\n\n\tif err = self.rl.OnKeyEvent(event); err != nil {\n\t\tif err == readline.ErrAcceptInput {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\tif event.Handled {\n\t\tself.update_family_search()\n\t}\n\tself.draw_search_bar()\n\treturn\n}\n\nfunc (self *FontList) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) {\n\tif err = self.rl.OnText(text, from_key_event, in_bracketed_paste); err != nil {\n\t\treturn err\n\t}\n\tself.update_family_search()\n\treturn\n}\n"
  },
  {
    "path": "kittens/choose_fonts/main.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n)\n\nvar _ = fmt.Print\nvar debugprintln = tty.DebugPrintln\nvar output_on_exit string\n\nfunc main(opts *Options) (rc int, err error) {\n\tif err = kitty_font_backend.start(); err != nil {\n\t\treturn 1, err\n\t}\n\tdefer func() {\n\t\tif werr := kitty_font_backend.release(); werr != nil {\n\t\t\tif err == nil {\n\t\t\t\terr = werr\n\t\t\t}\n\t\t\tif rc == 0 {\n\t\t\t\trc = 1\n\t\t\t}\n\t\t}\n\t}()\n\tlp, err := loop.New()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tlp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)\n\th := &handler{lp: lp, opts: opts}\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.AllowLineWrapping(false)\n\t\tlp.SetWindowTitle(`Choose a font for kitty`)\n\t\treturn \"\", h.initialize()\n\t}\n\tlp.OnWakeup = h.on_wakeup\n\tlp.OnEscapeCode = h.on_escape_code\n\tlp.OnFinalize = func() string {\n\t\th.finalize()\n\t\tlp.SetCursorVisible(true)\n\t\treturn ``\n\t}\n\tlp.OnMouseEvent = h.on_mouse_event\n\tlp.OnResize = func(_, _ loop.ScreenSize) error {\n\t\treturn h.draw_screen()\n\t}\n\tlp.OnKeyEvent = h.on_key_event\n\tlp.OnText = h.on_text\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn 1, nil\n\t}\n\tif output_on_exit != \"\" {\n\t\tos.Stdout.WriteString(output_on_exit)\n\t}\n\treturn lp.ExitCode(), nil\n}\n\ntype Options struct {\n\tReload_in        string\n\tConfig_file_name string\n}\n\nfunc EntryPoint(root *cli.Command) {\n\tans := root.AddSubCommand(&cli.Command{\n\t\tName:             \"choose-fonts\",\n\t\tShortDescription: \"Choose the fonts used in kitty\",\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\topts := Options{}\n\t\t\tif err = cmd.GetOptionValues(&opts); err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\treturn main(&opts)\n\t\t},\n\t})\n\tans.Add(cli.OptionSpec{\n\t\tName:    \"--reload-in\",\n\t\tDest:    \"Reload_in\",\n\t\tType:    \"choices\",\n\t\tChoices: \"parent, all, none\",\n\t\tDefault: \"parent\",\n\t\tHelp: `By default, this kitten will signal only the parent kitty instance it is\nrunning in to reload its config, after making changes. Use this option to\ninstead either not reload the config at all or in all running kitty instances.`,\n\t})\n\tans.Add(cli.OptionSpec{\n\t\tName:    \"--config-file-name\",\n\t\tDest:    \"Config_file_name\",\n\t\tType:    \"str\",\n\t\tDefault: \"kitty.conf\",\n\t\tHelp: `The name or path to the config file to edit. Relative paths are interpreted\nwith respect to the kitty config directory. By default the kitty config\nfile, kitty.conf is edited. This is most useful if you add include\nfonts.conf to your kitty.conf and then have the kitten operate only on\nfonts.conf, allowing kitty.conf to remain unchanged.`,\n\t})\n\n\tclone := root.AddClone(ans.Group, ans)\n\tclone.Hidden = true\n\tclone.Name = \"choose_fonts\"\n}\n"
  },
  {
    "path": "kittens/choose_fonts/main.py",
    "content": "if __name__ == '__main__':\n    import os\n    import sys\n\n    from kitty.constants import kitten_exe\n    os.execlp(kitten_exe(), 'kitten', *sys.argv)\n"
  },
  {
    "path": "kittens/choose_fonts/styles.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"slices\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\ntype style_group struct {\n\tname           string\n\tordering       int\n\tstyles         []string\n\tstyle_sort_map map[string]string\n}\n\ntype family_style_data struct {\n\tstyle_groups             []style_group\n\thas_variable_faces       bool\n\thas_style_attribute_data bool\n}\n\nfunc styles_with_attribute_data(ans *family_style_data, items ...VariableData) {\n\tgroups := make(map[string]*style_group)\n\tseen_map := make(map[string]map[string]string)\n\tget := func(key string, ordering int) *style_group {\n\t\tsg := groups[key]\n\t\tseen := seen_map[key]\n\t\tif sg == nil {\n\t\t\tans.style_groups = append(ans.style_groups, style_group{name: key, ordering: ordering, styles: make([]string, 0)})\n\t\t\tsg = &ans.style_groups[len(ans.style_groups)-1]\n\t\t\tgroups[key] = sg\n\t\t\tsg.style_sort_map = make(map[string]string)\n\t\t\tseen = make(map[string]string)\n\t\t\tseen_map[key] = seen\n\t\t}\n\t\treturn sg\n\t}\n\thas := func(n string, m map[string]string) bool {\n\t\t_, found := m[n]\n\t\treturn found\n\t}\n\tfor _, vd := range items {\n\t\tfor _, ax := range vd.Design_axes {\n\t\t\tif ax.Name == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsg := get(ax.Name, ax.Ordering)\n\t\t\tfor _, v := range ax.Values {\n\t\t\t\tif v.Name != \"\" && !has(v.Name, sg.style_sort_map) {\n\t\t\t\t\tsort_key := fmt.Sprintf(\"%09d:%s\", int(v.Value*10000), strings.ToLower(v.Name))\n\t\t\t\t\tsg.style_sort_map[v.Name] = sort_key\n\t\t\t\t\tsg.styles = append(sg.styles, v.Name)\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tfor _, ma := range vd.Multi_axis_styles {\n\t\t\tsg := get(\"Styles\", 0)\n\t\t\tif ma.Name != \"\" && !has(ma.Name, sg.style_sort_map) {\n\t\t\t\tsg.style_sort_map[ma.Name] = strings.ToLower(ma.Name)\n\t\t\t\tsg.styles = append(sg.styles, ma.Name)\n\t\t\t}\n\t\t}\n\t}\n\tans.style_groups = utils.StableSortWithKey(ans.style_groups, func(sg style_group) int { return sg.ordering })\n\tfor _, sg := range ans.style_groups {\n\t\tsg.styles = utils.StableSortWithKey(sg.styles, func(s string) string { return sg.style_sort_map[s] })\n\t}\n\n}\n\nfunc styles_for_variable_data(vd VariableData) (ans *family_style_data) {\n\tans = &family_style_data{style_groups: make([]style_group, 0)}\n\tstyles_with_attribute_data(ans, vd)\n\treturn\n}\n\nfunc styles_in_family(family string, fonts []ListedFont) (ans *family_style_data) {\n\t_ = family\n\tans = &family_style_data{style_groups: make([]style_group, 0)}\n\tvds := make([]VariableData, len(fonts))\n\tfor i, f := range fonts {\n\t\tvds[i] = variable_data_for(f)\n\t}\n\tfor _, vd := range vds {\n\t\tif len(vd.Design_axes) > 0 {\n\t\t\tans.has_style_attribute_data = true\n\t\t}\n\t\tif len(vd.Axes) > 0 {\n\t\t\tans.has_variable_faces = true\n\t\t}\n\t}\n\tif ans.has_style_attribute_data {\n\t\tstyles_with_attribute_data(ans, vds...)\n\t} else {\n\t\tans.style_groups = append(ans.style_groups, style_group{name: \"Styles\", styles: make([]string, 0)})\n\t\tsg := &ans.style_groups[0]\n\t\tseen := utils.NewSet[string]()\n\t\tfor _, f := range fonts {\n\t\t\tif f.Style != \"\" && !seen.Has(f.Style) {\n\t\t\t\tseen.Add(f.Style)\n\t\t\t\tsg.styles = append(sg.styles, f.Style)\n\t\t\t}\n\t\t}\n\t}\n\tfor _, sg := range ans.style_groups {\n\t\tslices.Sort(sg.styles)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "kittens/choose_fonts/types.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"maps\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n)\n\nvar _ = fmt.Print\n\ntype VariableAxis struct {\n\tMinimum float64 `json:\"minimum\"`\n\tMaximum float64 `json:\"maximum\"`\n\tDefault float64 `json:\"default\"`\n\tHidden  bool    `json:\"hidden\"`\n\tTag     string  `json:\"tag\"`\n\tStrid   string  `json:\"strid\"`\n}\n\ntype NamedStyle struct {\n\tAxis_values     map[string]float64 `json:\"axis_values\"`\n\tName            string             `json:\"name\"`\n\tPostscript_name string             `json:\"psname\"`\n}\n\ntype DesignAxisValue struct {\n\tFormat       int     `json:\"format\"`\n\tFlags        int     `json:\"flags\"`\n\tName         string  `json:\"name\"`\n\tValue        float64 `json:\"value\"`\n\tMinimum      float64 `json:\"minimum\"`\n\tMaximum      float64 `json:\"maximum\"`\n\tLinked_value float64 `json:\"linked_value\"`\n}\n\ntype DesignAxis struct {\n\tTag      string            `json:\"tag\"`\n\tName     string            `json:\"name\"`\n\tOrdering int               `json:\"ordering\"`\n\tValues   []DesignAxisValue `json:\"values\"`\n}\n\ntype AxisValue struct {\n\tDesign_index int     `json:\"design_index\"`\n\tValue        float64 `json:\"value\"`\n}\n\ntype MultiAxisStyle struct {\n\tFlags  int         `json:\"flags\"`\n\tName   string      `json:\"name\"`\n\tValues []AxisValue `json:\"values\"`\n}\n\ntype ListedFont struct {\n\tFamily          string         `json:\"family\"`\n\tStyle           string         `json:\"style\"`\n\tFullname        string         `json:\"full_name\"`\n\tPostscript_name string         `json:\"postscript_name\"`\n\tIs_monospace    bool           `json:\"is_monospace\"`\n\tIs_variable     bool           `json:\"is_variable\"`\n\tDescriptor      map[string]any `json:\"descriptor\"`\n}\n\ntype VariableData struct {\n\tAxes                              []VariableAxis   `json:\"axes\"`\n\tNamed_styles                      []NamedStyle     `json:\"named_styles\"`\n\tVariations_postscript_name_prefix string           `json:\"variations_postscript_name_prefix\"`\n\tElided_fallback_name              string           `json:\"elided_fallback_name\"`\n\tDesign_axes                       []DesignAxis     `json:\"design_axes\"`\n\tMulti_axis_styles                 []MultiAxisStyle `json:\"multi_axis_styles\"`\n}\n\ntype ResolvedFace struct {\n\tFamily  string `json:\"family\"`\n\tSpec    string `json:\"spec\"`\n\tSetting string `json:\"setting\"`\n}\n\ntype ResolvedFaces struct {\n\tFont_family      ResolvedFace `json:\"font_family\"`\n\tBold_font        ResolvedFace `json:\"bold_font\"`\n\tItalic_font      ResolvedFace `json:\"italic_font\"`\n\tBold_italic_font ResolvedFace `json:\"bold_italic_font\"`\n}\n\ntype ListResult struct {\n\tFonts          map[string][]ListedFont `json:\"fonts\"`\n\tResolved_faces ResolvedFaces           `json:\"resolved_faces\"`\n}\n\ntype FeatureData struct {\n\tIs_index bool     `json:\"is_index\"`\n\tName     string   `json:\"name\"`\n\tTooltip  string   `json:\"tooltip\"`\n\tSample   string   `json:\"sample\"`\n\tParams   []string `json:\"params\"`\n}\n\ntype RenderedSampleTransmit struct {\n\tPath                 string                 `json:\"path\"`\n\tVariable_data        VariableData           `json:\"variable_data\"`\n\tStyle                string                 `json:\"style\"`\n\tPsname               string                 `json:\"psname\"`\n\tSpec                 string                 `json:\"spec\"`\n\tFeatures             map[string]FeatureData `json:\"features\"`\n\tApplied_features     map[string]string      `json:\"applied_features\"`\n\tVariable_named_style NamedStyle             `json:\"variable_named_style\"`\n\tVariable_axis_map    map[string]float64     `json:\"variable_axis_map\"`\n\tCell_width           int                    `json:\"cell_width\"`\n\tCell_height          int                    `json:\"cell_height\"`\n\tCanvas_width         int                    `json:\"canvas_width\"`\n\tCanvas_height        int                    `json:\"canvas_height\"`\n}\n\nfunc (self RenderedSampleTransmit) default_axis_values() (ans map[string]float64) {\n\tans = make(map[string]float64)\n\tfor _, ax := range self.Variable_data.Axes {\n\t\tans[ax.Tag] = ax.Default\n\t}\n\treturn\n}\n\nfunc (self RenderedSampleTransmit) current_axis_values() (ans map[string]float64) {\n\tans = make(map[string]float64, len(self.Variable_data.Axes))\n\tfor _, ax := range self.Variable_data.Axes {\n\t\tans[ax.Tag] = ax.Default\n\t}\n\tif self.Variable_named_style.Name != \"\" {\n\t\tmaps.Copy(ans, self.Variable_named_style.Axis_values)\n\t} else {\n\t\tmaps.Copy(ans, self.Variable_axis_map)\n\t}\n\treturn\n}\n\nvar variable_data_cache map[string]VariableData\nvar variable_data_cache_mutex sync.Mutex\n\nfunc (f ListedFont) cache_key() string {\n\tkey := f.Postscript_name\n\tif key == \"\" {\n\t\tkey = \"path:\" + f.Descriptor[\"path\"].(string)\n\t} else {\n\t\tkey = \"psname:\" + key\n\t}\n\treturn key\n}\n\nfunc ensure_variable_data_for_fonts(fonts ...ListedFont) error {\n\tdescriptors := make([]map[string]any, 0, len(fonts))\n\tkeys := make([]string, 0, len(fonts))\n\tvariable_data_cache_mutex.Lock()\n\tfor _, f := range fonts {\n\t\tkey := f.cache_key()\n\t\tif _, found := variable_data_cache[key]; !found {\n\t\t\tdescriptors = append(descriptors, f.Descriptor)\n\t\t\tkeys = append(keys, key)\n\t\t}\n\t}\n\tvariable_data_cache_mutex.Unlock()\n\tvar data []VariableData\n\tif err := kitty_font_backend.query(\"read_variable_data\", map[string]any{\"descriptors\": descriptors}, &data); err != nil {\n\t\treturn err\n\t}\n\tvariable_data_cache_mutex.Lock()\n\tfor i, key := range keys {\n\t\tvariable_data_cache[key] = data[i]\n\t}\n\tvariable_data_cache_mutex.Unlock()\n\treturn nil\n}\n\nfunc initialize_variable_data_cache() {\n\tvariable_data_cache = make(map[string]VariableData)\n}\n\nfunc _cached_vd(key string) (ans VariableData, found bool) {\n\tvariable_data_cache_mutex.Lock()\n\tdefer variable_data_cache_mutex.Unlock()\n\tans, found = variable_data_cache[key]\n\treturn\n}\n\nfunc variable_data_for(f ListedFont) VariableData {\n\tkey := f.cache_key()\n\tans, found := _cached_vd(key)\n\tif found {\n\t\treturn ans\n\t}\n\tif err := ensure_variable_data_for_fonts(f); err != nil {\n\t\tpanic(err)\n\t}\n\tans, found = _cached_vd(key)\n\treturn ans\n}\n\nfunc has_variable_data_for_font(font ListedFont) bool {\n\t_, found := _cached_vd(font.cache_key())\n\treturn found\n}\n\ntype ParsedFontFeature struct {\n\ttag     string\n\tval     uint\n\tis_bool bool\n}\n\nfunc (self ParsedFontFeature) String() string {\n\tif self.is_bool {\n\t\treturn utils.IfElse(self.val == 0, \"-\", \"+\") + self.tag\n\t}\n\treturn fmt.Sprintf(\"%s=%d\", self.tag, self.val)\n}\n\ntype settable_string struct {\n\tval    string\n\tis_set bool\n}\n\ntype FontSpec struct {\n\tfamily, style, postscript_name, full_name, system, variable_name settable_string\n\taxes                                                             map[string]float64\n\tfeatures                                                         []*ParsedFontFeature\n}\n\nfunc (self FontSpec) String() string {\n\tif self.system.val != \"\" {\n\t\treturn self.system.val\n\t}\n\tans := strings.Builder{}\n\ta := func(k string, v settable_string) {\n\t\tif v.is_set {\n\t\t\tans.WriteString(fmt.Sprintf(\" %s=%s\", k, shlex.Quote(v.val)))\n\t\t}\n\t}\n\ta(`family`, self.family)\n\ta(`style`, self.style)\n\ta(`postscript_name`, self.postscript_name)\n\ta(`full_name`, self.full_name)\n\ta(`variable_name`, self.variable_name)\n\tfor name, val := range self.axes {\n\t\ta(name, settable_string{strconv.FormatFloat(val, 'f', -1, 64), true})\n\t}\n\tif len(self.features) > 0 {\n\t\tbuf := strings.Builder{}\n\t\tfor _, f := range self.features {\n\t\t\tbuf.WriteString(f.String())\n\t\t\tbuf.WriteString(\" \")\n\t\t}\n\t\ta(`features`, settable_string{strings.TrimSpace(buf.String()), true})\n\t}\n\treturn strings.TrimSpace(ans.String())\n}\n\nfunc NewParsedFontFeature(x string, features map[string]FeatureData) (ans ParsedFontFeature, err error) {\n\tif x != \"\" {\n\t\tif x[0] == '+' || x[0] == '-' {\n\t\t\treturn ParsedFontFeature{x[1:], utils.IfElse(x[0] == '+', uint(1), uint(0)), true}, nil\n\t\t} else {\n\t\t\ttag, val, found := strings.Cut(x, \"=\")\n\t\t\tfd, defn_found := features[tag]\n\t\t\tif defn_found && !fd.Is_index {\n\t\t\t\treturn ParsedFontFeature{tag, 1, true}, nil\n\t\t\t}\n\t\t\tpff := ParsedFontFeature{tag: tag}\n\t\t\tif found {\n\t\t\t\tv, err := strconv.ParseUint(val, 10, 0)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn ans, err\n\t\t\t\t}\n\t\t\t\tpff.val = uint(v)\n\t\t\t}\n\t\t\treturn pff, nil\n\t\t}\n\t}\n\treturn\n}\n\nfunc NewFontSpec(spec string, features map[string]FeatureData) (ans FontSpec, err error) {\n\tif spec == \"\" || spec == \"auto\" {\n\t\tans.system = settable_string{\"auto\", true}\n\t\treturn\n\t}\n\tparts, err := shlex.Split(spec)\n\tif err != nil {\n\t\treturn\n\t}\n\tif !strings.Contains(parts[0], \"=\") {\n\t\tans.system = settable_string{spec, true}\n\t\treturn\n\t}\n\tfor _, item := range parts {\n\t\tk, v, found := strings.Cut(item, \"=\")\n\t\tif !found {\n\t\t\treturn ans, fmt.Errorf(\"The font specification %s is invalid as %s does not contain an =\", spec, item)\n\t\t}\n\t\tswitch k {\n\t\tcase \"family\":\n\t\t\tans.family = settable_string{v, true}\n\t\tcase \"style\":\n\t\t\tans.style = settable_string{v, true}\n\t\tcase \"full_name\":\n\t\t\tans.full_name = settable_string{v, true}\n\t\tcase \"postscript_name\":\n\t\t\tans.postscript_name = settable_string{v, true}\n\t\tcase \"variable_name\":\n\t\t\tans.variable_name = settable_string{v, true}\n\t\tcase \"features\":\n\t\t\tfor _, x := range utils.NewSeparatorScanner(v, \" \").Split(v) {\n\t\t\t\tpff, err := NewParsedFontFeature(x, features)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn ans, err\n\t\t\t\t}\n\t\t\t\tans.features = append(ans.features, &pff)\n\t\t\t}\n\t\tdefault:\n\t\t\tif ans.axes == nil {\n\t\t\t\tans.axes = make(map[string]float64)\n\t\t\t}\n\t\t\tf, err := strconv.ParseFloat(v, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn ans, err\n\t\t\t}\n\t\t\tans.axes[k] = f\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "kittens/choose_fonts/ui.go",
    "content": "package choose_fonts\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype State int\n\nconst (\n\tSCANNING_FAMILIES State = iota\n\tLISTING_FAMILIES\n\tCHOOSING_FACES\n)\n\ntype TextStyle struct {\n\tFont_sz    float64 `json:\"font_size\"`\n\tDpi_x      float64 `json:\"dpi_x\"`\n\tDpi_y      float64 `json:\"dpi_y\"`\n\tForeground string  `json:\"foreground\"`\n\tBackground string  `json:\"background\"`\n}\n\ntype pane interface {\n\tinitialize(*handler) error\n\tdraw_screen() error\n\ton_wakeup() error\n\ton_key_event(event *loop.KeyEvent) error\n\ton_text(text string, from_key_event bool, in_bracketed_paste bool) error\n\ton_click(id string) error\n}\n\ntype handler struct {\n\topts                 *Options\n\tlp                   *loop.Loop\n\tstate                State\n\terr_mutex            sync.Mutex\n\terr_in_worker_thread error\n\tmouse_state          tui.MouseState\n\trender_count         uint\n\trender_lines         tui.RenderLines\n\ttext_style           TextStyle\n\tgraphics_manager     graphics_manager\n\ttemp_dir             string\n\n\tlisting    FontList\n\tfaces      faces\n\tface_pane  face_panel\n\tif_pane    if_panel\n\tfinal_pane final_pane\n\n\tpanes        []pane\n\tcurrent_pane pane\n}\n\nfunc (h *handler) set_worker_error(err error) {\n\th.err_mutex.Lock()\n\tdefer h.err_mutex.Unlock()\n\th.err_in_worker_thread = err\n}\n\nfunc (h *handler) get_worker_error() error {\n\th.err_mutex.Lock()\n\tdefer h.err_mutex.Unlock()\n\treturn h.err_in_worker_thread\n}\n\n// Events {{{\nfunc (h *handler) initialize() (err error) {\n\th.lp.SetCursorVisible(false)\n\th.lp.OnQueryResponse = h.on_query_response\n\th.lp.QueryTerminal(\"font_size\", \"dpi_x\", \"dpi_y\", \"foreground\", \"background\")\n\th.panes = []pane{&h.listing, &h.faces, &h.face_pane, &h.if_pane, &h.final_pane}\n\tfor _, pane := range h.panes {\n\t\tif err = pane.initialize(h); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// dont use /tmp as it may be mounted in RAM, Le Sigh\n\tif h.temp_dir, err = os.MkdirTemp(utils.CacheDir(), \"kitten-choose-fonts-*\"); err != nil {\n\t\treturn\n\t}\n\tinitialize_variable_data_cache()\n\th.graphics_manager.initialize(h.lp)\n\tgo func() {\n\t\tvar r ListResult\n\t\th.set_worker_error(kitty_font_backend.query(\"list_monospaced_fonts\", nil, &r))\n\t\th.listing.fonts = r.Fonts\n\t\th.listing.resolved_faces_from_kitty_conf = r.Resolved_faces\n\t\th.lp.WakeupMainThread()\n\t}()\n\th.draw_screen()\n\treturn\n}\n\nfunc (h *handler) finalize() {\n\tif h.temp_dir != \"\" {\n\t\tos.RemoveAll(h.temp_dir)\n\t\th.temp_dir = \"\"\n\t}\n\th.lp.SetCursorVisible(true)\n\th.lp.SetCursorShape(loop.BLOCK_CURSOR, true)\n\th.graphics_manager.finalize()\n}\n\nfunc (h *handler) on_query_response(key, val string, valid bool) error {\n\tif !valid {\n\t\treturn fmt.Errorf(\"Terminal does not support querying the: %s\", key)\n\t}\n\tset_float := func(k, v string, dest *float64) error {\n\t\tif fs, err := strconv.ParseFloat(v, 64); err == nil {\n\t\t\t*dest = fs\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"Invalid response from terminal to %s query: %#v\", k, v)\n\t\t}\n\t\treturn nil\n\t}\n\tswitch key {\n\tcase \"font_size\":\n\t\tif err := set_float(key, val, &h.text_style.Font_sz); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"dpi_x\":\n\t\tif err := set_float(key, val, &h.text_style.Dpi_x); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"dpi_y\":\n\t\tif err := set_float(key, val, &h.text_style.Dpi_y); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"foreground\":\n\t\th.text_style.Foreground = val\n\tcase \"background\":\n\t\th.text_style.Background = val\n\t\treturn h.draw_screen()\n\t}\n\treturn nil\n}\n\nfunc (h *handler) draw_screen() (err error) {\n\th.render_count++\n\th.lp.StartAtomicUpdate()\n\tdefer func() {\n\t\th.mouse_state.UpdateHoveredIds()\n\t\th.mouse_state.ApplyHoverStyles(h.lp)\n\t\th.lp.EndAtomicUpdate()\n\t}()\n\th.graphics_manager.clear_placements()\n\th.lp.ClearScreenButNotGraphics()\n\th.lp.AllowLineWrapping(false)\n\th.mouse_state.ClearCellRegions()\n\tif h.current_pane == nil {\n\t\th.lp.Println(\"Scanning system for fonts, please wait...\")\n\t} else {\n\t\treturn h.current_pane.draw_screen()\n\t}\n\treturn\n}\n\nfunc (h *handler) on_wakeup() (err error) {\n\tif err = h.get_worker_error(); err != nil {\n\t\treturn\n\t}\n\tif h.current_pane == nil {\n\t\th.current_pane = &h.listing\n\t}\n\treturn h.listing.on_wakeup()\n}\n\nfunc (h *handler) on_mouse_event(event *loop.MouseEvent) (err error) {\n\trc := h.render_count\n\tredraw_needed := false\n\tif h.mouse_state.UpdateState(event) {\n\t\tredraw_needed = true\n\t}\n\tif event.Event_type == loop.MOUSE_CLICK && event.Buttons&loop.LEFT_MOUSE_BUTTON != 0 {\n\t\tif err = h.mouse_state.ClickHoveredRegions(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif redraw_needed && rc == h.render_count {\n\t\terr = h.draw_screen()\n\t}\n\treturn\n}\n\nfunc (h *handler) on_key_event(event *loop.KeyEvent) (err error) {\n\tif event.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\tevent.Handled = true\n\t\treturn fmt.Errorf(\"canceled by user\")\n\t}\n\tif h.current_pane != nil {\n\t\terr = h.current_pane.on_key_event(event)\n\t}\n\treturn\n}\n\nfunc (h *handler) on_text(text string, from_key_event bool, in_bracketed_paste bool) (err error) {\n\tif h.current_pane != nil {\n\t\terr = h.current_pane.on_text(text, from_key_event, in_bracketed_paste)\n\t}\n\treturn\n}\n\nfunc (h *handler) on_escape_code(etype loop.EscapeCodeType, payload []byte) error {\n\tswitch etype {\n\tcase loop.APC:\n\t\tgc := graphics.GraphicsCommandFromAPC(payload)\n\t\tif gc != nil {\n\t\t\treturn h.graphics_manager.on_response(gc)\n\t\t}\n\t}\n\treturn nil\n}\n\n// }}}\n"
  },
  {
    "path": "kittens/clipboard/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/clipboard/legacy.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage clipboard\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nvar _ = fmt.Print\n\nfunc encode_read_from_clipboard(use_primary bool) string {\n\tdest := \"c\"\n\tif use_primary {\n\t\tdest = \"p\"\n\t}\n\treturn fmt.Sprintf(\"\\x1b]52;%s;?\\x1b\\\\\", dest)\n}\n\ntype base64_streaming_enc struct {\n\toutput          func(string) loop.IdType\n\tlast_written_id loop.IdType\n}\n\nfunc (self *base64_streaming_enc) Write(p []byte) (int, error) {\n\tif len(p) > 0 {\n\t\tself.last_written_id = self.output(string(p))\n\t}\n\treturn len(p), nil\n}\n\nvar ErrTooMuchPipedData = errors.New(\"Too much piped data\")\n\nfunc read_all_with_max_size(r io.Reader, max_size int) ([]byte, error) {\n\tb := make([]byte, 0, utils.Min(8192, max_size))\n\tfor {\n\t\tif len(b) == cap(b) {\n\t\t\tnew_size := utils.Min(2*cap(b), max_size)\n\t\t\tif new_size <= cap(b) {\n\t\t\t\treturn b, ErrTooMuchPipedData\n\t\t\t}\n\t\t\tb = append(make([]byte, 0, new_size), b...)\n\t\t}\n\t\tn, err := r.Read(b[len(b):cap(b)])\n\t\tb = b[:len(b)+n]\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\treturn b, err\n\t\t}\n\t}\n}\n\nfunc preread_stdin() (data_src io.Reader, tempfile *os.File, err error) {\n\t// we pre-read STDIN because otherwise if the output of a command is being piped in\n\t// and that command itself transmits on the tty we will break. For example\n\t// kitten @ ls | kitten clipboard\n\tvar stdin_data []byte\n\tstdin_data, err = read_all_with_max_size(os.Stdin, 2*1024*1024)\n\tif err == nil {\n\t\tos.Stdin.Close()\n\t} else if err != ErrTooMuchPipedData {\n\t\tos.Stdin.Close()\n\t\terr = fmt.Errorf(\"Failed to read from STDIN pipe with error: %w\", err)\n\t\treturn\n\t}\n\tif err == ErrTooMuchPipedData {\n\t\ttempfile, err = utils.CreateAnonymousTemp(\"\")\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"Failed to create a temporary from STDIN pipe with error: %w\", err)\n\t\t}\n\t\ttempfile.Write(stdin_data)\n\t\t_, err = io.Copy(tempfile, os.Stdin)\n\t\tos.Stdin.Close()\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"Failed to copy data from STDIN pipe to temp file with error: %w\", err)\n\t\t}\n\t\ttempfile.Seek(0, io.SeekStart)\n\t\tdata_src = tempfile\n\t} else if stdin_data != nil {\n\t\tdata_src = bytes.NewBuffer(stdin_data)\n\t}\n\treturn\n}\n\nfunc run_plain_text_loop(opts *Options) (err error) {\n\tstdin_is_tty := tty.IsTerminal(os.Stdin.Fd())\n\tvar data_src io.Reader\n\tvar tempfile *os.File\n\tif !stdin_is_tty && !opts.GetClipboard {\n\t\t// we dont read STDIN when getting clipboard as it makes it hard to use the kitten in contexts where\n\t\t// the user does not control STDIN such as being execed from other programs.\n\t\tdata_src, tempfile, err = preread_stdin()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif tempfile != nil {\n\t\t\tdefer tempfile.Close()\n\t\t}\n\t}\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)\n\tif err != nil {\n\t\treturn\n\t}\n\tdest := \"c\"\n\tif opts.UsePrimary {\n\t\tdest = \"p\"\n\t}\n\n\tsend_to_loop := func(data string) loop.IdType {\n\t\treturn lp.QueueWriteString(data)\n\t}\n\tenc_writer := base64_streaming_enc{output: send_to_loop}\n\tenc := base64.NewEncoder(base64.StdEncoding, &enc_writer)\n\ttransmitting := true\n\n\tafter_read_from_stdin := func() {\n\t\ttransmitting = false\n\t\tif opts.GetClipboard {\n\t\t\tlp.QueueWriteString(encode_read_from_clipboard(opts.UsePrimary))\n\t\t} else if opts.WaitForCompletion {\n\t\t\tlp.QueueWriteString(\"\\x1bP+q544e\\x1b\\\\\")\n\t\t} else {\n\t\t\tlp.Quit(0)\n\t\t}\n\t}\n\n\tbuf := make([]byte, 8192)\n\twrite_one_chunk := func() error {\n\t\torig := enc_writer.last_written_id\n\t\tfor enc_writer.last_written_id == orig {\n\t\t\tn, err := data_src.Read(buf[:cap(buf)])\n\t\t\tif n > 0 {\n\t\t\t\tenc.Write(buf[:n])\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tenc.Close()\n\t\t\t\tsend_to_loop(\"\\x1b\\\\\")\n\t\t\t\tafter_read_from_stdin()\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tsend_to_loop(\"\\x1b\\\\\")\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tif data_src != nil {\n\t\t\tsend_to_loop(fmt.Sprintf(\"\\x1b]52;%s;\", dest))\n\t\t\treturn \"\", write_one_chunk()\n\t\t}\n\t\tafter_read_from_stdin()\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnWriteComplete = func(id loop.IdType, has_pending_writes bool) error {\n\t\tif id == enc_writer.last_written_id {\n\t\t\treturn write_one_chunk()\n\t\t}\n\t\treturn nil\n\t}\n\n\tvar clipboard_contents []byte\n\n\tlp.OnEscapeCode = func(etype loop.EscapeCodeType, data []byte) (err error) {\n\t\tswitch etype {\n\t\tcase loop.DCS:\n\t\t\tif strings.HasPrefix(utils.UnsafeBytesToString(data), \"1+r\") {\n\t\t\t\tlp.Quit(0)\n\t\t\t}\n\t\tcase loop.OSC:\n\t\t\tq := utils.UnsafeBytesToString(data)\n\t\t\tif strings.HasPrefix(q, \"52;\") {\n\t\t\t\tparts := strings.SplitN(q, \";\", 3)\n\t\t\t\tif len(parts) < 3 {\n\t\t\t\t\tlp.Quit(0)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdata, err := base64.StdEncoding.DecodeString(parts[2])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"Invalid base64 encoded data from terminal with error: %w\", err)\n\t\t\t\t}\n\t\t\t\tclipboard_contents = data\n\t\t\t\tlp.Quit(0)\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tesc_count := 0\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") || event.MatchesPressOrRepeat(\"esc\") {\n\t\t\tif transmitting {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tevent.Handled = true\n\t\t\tesc_count++\n\t\t\tif esc_count < 2 {\n\t\t\t\tkey := \"Esc\"\n\t\t\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\t\t\t\tkey = \"Ctrl+C\"\n\t\t\t\t}\n\t\t\t\tlp.QueueWriteString(fmt.Sprintf(\"Waiting for response from terminal, press %s again to abort. This could cause garbage to be spewed to the screen.\\r\\n\", key))\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"Aborted by user!\")\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\tif len(clipboard_contents) > 0 {\n\t\t_, err = os.Stdout.Write(clipboard_contents)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"Failed to write to STDOUT with error: %w\", err)\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "kittens/clipboard/main.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage clipboard\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n)\n\nfunc run_mime_loop(opts *Options, args []string) (err error) {\n\tcwd, err = os.Getwd()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif opts.GetClipboard {\n\t\treturn run_get_loop(opts, args)\n\t}\n\treturn run_set_loop(opts, args)\n}\n\nfunc clipboard_main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tif opts.Password != \"\" {\n\t\tif opts.HumanName == \"\" {\n\t\t\treturn 1, fmt.Errorf(\"must specify --human-name when using a password\")\n\t\t}\n\t\tptype, val, found := strings.Cut(opts.Password, \":\")\n\t\tif !found {\n\t\t\treturn 1, fmt.Errorf(\"invalid password: %#v no password type specified\", opts.Password)\n\t\t}\n\t\tswitch ptype {\n\t\tcase \"text\":\n\t\t\topts.Password = val\n\t\tcase \"fd\":\n\t\t\tif fd, err := strconv.Atoi(val); err == nil {\n\t\t\t\tif f := os.NewFile(uintptr(fd), \"password-fd\"); f == nil {\n\t\t\t\t\treturn 1, fmt.Errorf(\"invalid file descriptor: %d\", fd)\n\t\t\t\t} else {\n\t\t\t\t\tdata, err := io.ReadAll(f)\n\t\t\t\t\tf.Close()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn 1, fmt.Errorf(\"failed to read from file descriptor: %d with error: %w\", fd, err)\n\t\t\t\t\t}\n\t\t\t\t\topts.Password = strings.TrimRightFunc(string(data), unicode.IsSpace)\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\treturn 1, fmt.Errorf(\"not a valid file descriptor number: %#v\", val)\n\t\t\t}\n\t\tcase \"file\":\n\t\t\tif data, err := os.ReadFile(val); err == nil {\n\t\t\t\topts.Password = strings.TrimRightFunc(string(data), unicode.IsSpace)\n\t\t\t} else {\n\t\t\t\treturn 1, fmt.Errorf(\"failed to read from file: %#v with error: %w\", val, err)\n\t\t\t}\n\t\t}\n\t}\n\tif len(args) > 0 {\n\t\treturn 0, run_mime_loop(opts, args)\n\t}\n\tif opts.Password != \"\" || opts.HumanName != \"\" {\n\t\treturn 1, fmt.Errorf(\"cannot use --human-name or --password in filter mode\")\n\t}\n\treturn 0, run_plain_text_loop(opts)\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, clipboard_main)\n}\n"
  },
  {
    "path": "kittens/clipboard/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\n\nOPTIONS = r'''\n--get-clipboard -g\ntype=bool-set\nOutput the current contents of the clipboard to STDOUT. Note that by default\nkitty will prompt for permission to access the clipboard. Can be controlled\nby :opt:`clipboard_control`.\n\n\n--use-primary -p\ntype=bool-set\nUse the primary selection rather than the clipboard on systems that support it,\nsuch as Linux.\n\n\n--mime -m\ntype=list\nThe mimetype of the specified file. Useful when the auto-detected mimetype is\nlikely to be incorrect or the filename has no extension and therefore no mimetype\ncan be detected. If more than one file is specified, this option should be specified multiple\ntimes, once for each specified file. When copying data from the clipboard, you can use wildcards\nto match MIME types. For example: :code:`--mime 'text/*'` will match any textual MIME type\navailable on the clipboard, usually the first matching MIME type is copied. The special MIME\ntype :code:`.` will return the list of available MIME types currently on the system clipboard.\n\n\n--alias -a\ntype=list\nSpecify aliases for MIME types. Aliased MIME types are considered equivalent.\nWhen copying to clipboard both the original and alias are made available on the\nclipboard. When copying from clipboard if the original is not found, the alias\nis used, as a fallback. Can be specified multiple times to create multiple\naliases. For example: :code:`--alias text/plain=text/x-rst` makes :code:`text/plain` an alias\nof :code:`text/rst`. Aliases are not used in filter mode. An alias for\n:code:`text/plain` is automatically created if :code:`text/plain` is not present in the input data, but some\nother :code:`text/*` MIME is present.\n\n\n--wait-for-completion\ntype=bool-set\nWait till the copy to clipboard is complete before exiting. Useful if running\nthe kitten in a dedicated, ephemeral window. Only needed in filter mode.\n\n\n--password\nA password to use when accessing the clipboard. If the user chooses to accept the password\nfuture invocations of the kitten will not have a permission prompt in this tty session. Does not\nwork in filter mode. Must be of the form: text:actual-password or fd:integer (a file descriptor\nnumber to read the password from) or file:path-to-file (a file from which to read the password).\nNote that you must also specify a human friendly name using the :option:`--human-name` flag.\n\n\n--human-name\nA human friendly name to show the user when asking for permission to access the clipboard.\n'''.format\nhelp_text = '''\\\nRead or write to the system clipboard.\n\nThis kitten operates most simply in :italic:`filter mode`.\nTo set the clipboard text, pipe in the new text on :file:`STDIN`. Use the\n:option:`--get-clipboard` option to instead output the current clipboard text content to\n:file:`STDOUT`. Note that copying from the clipboard will cause a permission\npopup, see :opt:`clipboard_control` for details.\n\nFor more control, specify filename arguments. Then, different MIME types can be copied to/from\nthe clipboard. Some examples:\n\n.. code:: sh\n\n    # Copy an image to the clipboard:\n    kitten clipboard picture.png\n\n    # Copy an image and some text to the clipboard:\n    kitten clipboard picture.jpg text.txt\n\n    # Copy text from STDIN and an image to the clipboard:\n    echo hello | kitten clipboard picture.png /dev/stdin\n\n    # Copy any raster image available on the clipboard to a PNG file:\n    kitten clipboard -g picture.png\n\n    # Copy an image to a file and text to STDOUT:\n    kitten clipboard -g picture.png /dev/stdout\n\n    # List the formats available on the system clipboard\n    kitten clipboard -g -m . /dev/stdout\n'''\n\nusage = '[files to copy to/from]'\nif __name__ == '__main__':\n    raise SystemExit('This should be run as kitten clipboard')\nelif __name__ == '__doc__':\n    from kitty.simple_cli_definitions import CompletionSpec\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Copy/paste with the system clipboard, even over SSH'\n    cd['args_completion'] = CompletionSpec.from_string('type:file mime:* group:Files')\n"
  },
  {
    "path": "kittens/clipboard/read.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage clipboard\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"image\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nvar _ = fmt.Print\nvar cwd string\n\nconst OSC_NUMBER = \"5522\"\n\ntype Output struct {\n\targ                    string\n\text                    string\n\targ_is_stream          bool\n\tmime_type              string\n\tremote_mime_type       string\n\timage_needs_conversion bool\n\tis_stream              bool\n\tdest_is_tty            bool\n\tdest                   *os.File\n\terr                    error\n\tstarted                bool\n\tall_data_received      bool\n}\n\nfunc (self *Output) cleanup() {\n\tif self.dest != nil {\n\t\tself.dest.Close()\n\t\tif !self.is_stream {\n\t\t\tos.Remove(self.dest.Name())\n\t\t}\n\t\tself.dest = nil\n\t}\n}\n\nfunc (self *Output) add_data(data []byte) {\n\tif self.err != nil {\n\t\treturn\n\t}\n\tif self.dest == nil {\n\t\tif !self.image_needs_conversion && self.arg_is_stream {\n\t\t\tself.is_stream = true\n\t\t\tself.dest = os.Stdout\n\t\t\tif self.arg == \"/dev/stderr\" {\n\t\t\t\tself.dest = os.Stderr\n\t\t\t}\n\t\t\tself.dest_is_tty = tty.IsTerminal(self.dest.Fd())\n\t\t} else {\n\t\t\td := cwd\n\t\t\tif strings.ContainsRune(self.arg, os.PathSeparator) && !self.arg_is_stream {\n\t\t\t\td = filepath.Dir(self.arg)\n\t\t\t}\n\t\t\tf, err := os.CreateTemp(d, \".\"+filepath.Base(self.arg))\n\t\t\tif err != nil {\n\t\t\t\tself.err = err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.dest = f\n\t\t}\n\t\tself.started = true\n\t}\n\tif self.dest_is_tty {\n\t\tdata = bytes.ReplaceAll(data, utils.UnsafeStringToBytes(\"\\n\"), utils.UnsafeStringToBytes(\"\\r\\n\"))\n\t}\n\t_, self.err = self.dest.Write(data)\n}\n\nfunc (self *Output) write_image(img image.Image) (err error) {\n\tvar output *os.File\n\tif self.arg_is_stream {\n\t\toutput = os.Stdout\n\t\tif self.arg == \"/dev/stderr\" {\n\t\t\toutput = os.Stderr\n\t\t}\n\t} else {\n\t\toutput, err = os.Create(self.arg)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tdefer func() {\n\t\toutput.Close()\n\t\tif err != nil && !self.arg_is_stream {\n\t\t\tos.Remove(output.Name())\n\t\t}\n\t}()\n\treturn images.Encode(output, img, self.mime_type)\n}\n\nfunc (self *Output) commit() {\n\tif self.err != nil {\n\t\treturn\n\t}\n\tif self.image_needs_conversion {\n\t\tself.dest.Seek(0, io.SeekStart)\n\t\timg, _, err := image.Decode(self.dest)\n\t\tself.dest.Close()\n\t\tos.Remove(self.dest.Name())\n\t\tif err == nil {\n\t\t\terr = self.write_image(img)\n\t\t}\n\t\tif err != nil {\n\t\t\tself.err = fmt.Errorf(\"Failed to encode image data to %s with error: %w\", self.mime_type, err)\n\t\t}\n\t} else {\n\t\tself.dest.Close()\n\t\tif !self.is_stream {\n\t\t\tf, err := os.OpenFile(self.arg, os.O_CREATE|os.O_RDONLY, 0666)\n\t\t\tif err == nil {\n\t\t\t\tfi, err := f.Stat()\n\t\t\t\tif err == nil {\n\t\t\t\t\tself.dest.Chmod(fi.Mode().Perm())\n\t\t\t\t}\n\t\t\t\tf.Close()\n\t\t\t\tos.Remove(f.Name())\n\t\t\t}\n\t\t\tself.err = os.Rename(self.dest.Name(), self.arg)\n\t\t\tif self.err != nil {\n\t\t\t\tos.Remove(self.dest.Name())\n\t\t\t\tself.err = fmt.Errorf(\"Failed to rename temporary file used for downloading to destination: %s with error: %w\", self.arg, self.err)\n\t\t\t}\n\t\t}\n\t}\n\tself.dest = nil\n}\n\nfunc (self *Output) assign_mime_type(available_mimes []string, aliases map[string][]string) (err error) {\n\tif self.mime_type == \".\" {\n\t\tself.remote_mime_type = \".\"\n\t\treturn\n\t}\n\tif slices.Contains(available_mimes, self.mime_type) {\n\t\tself.remote_mime_type = self.mime_type\n\t\treturn\n\t}\n\tif len(aliases[self.mime_type]) > 0 {\n\t\tfor _, alias := range aliases[self.mime_type] {\n\t\t\tif slices.Contains(available_mimes, alias) {\n\t\t\t\tself.remote_mime_type = alias\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\tfor _, mt := range available_mimes {\n\t\tif matched, _ := filepath.Match(self.mime_type, mt); matched {\n\t\t\tself.remote_mime_type = mt\n\t\t\treturn\n\t\t}\n\t}\n\tif images.EncodableImageTypes[self.mime_type] {\n\t\tfor _, mt := range available_mimes {\n\t\t\tif images.DecodableImageTypes[mt] {\n\t\t\t\tself.remote_mime_type = mt\n\t\t\t\tself.image_needs_conversion = true\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\tif is_textual_mime(self.mime_type) {\n\t\tfor _, mt := range available_mimes {\n\t\t\tif mt == \"text/plain\" {\n\t\t\t\tself.remote_mime_type = mt\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn fmt.Errorf(\"The MIME type %s for %s not available on the clipboard\", self.mime_type, self.arg)\n}\n\nfunc escape_metadata_value(k, x string) (ans string) {\n\tif k == \"mime\" {\n\t\tx = base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(x))\n\t}\n\treturn x\n}\n\nfunc unescape_metadata_value(k, x string) (ans string) {\n\tif k == \"mime\" {\n\t\tb, err := base64.StdEncoding.DecodeString(x)\n\t\tif err == nil {\n\t\t\tx = string(b)\n\t\t}\n\t}\n\treturn x\n}\n\nfunc Encode_bytes(metadata map[string]string, payload []byte) string {\n\tans := strings.Builder{}\n\tenc_payload := \"\"\n\tif len(payload) > 0 {\n\t\tenc_payload = base64.StdEncoding.EncodeToString(payload)\n\t}\n\tans.Grow(2048 + len(enc_payload))\n\tans.WriteString(\"\\x1b]\")\n\tans.WriteString(OSC_NUMBER)\n\tans.WriteString(\";\")\n\tfor k, v := range metadata {\n\t\tif !strings.HasSuffix(ans.String(), \";\") {\n\t\t\tans.WriteString(\":\")\n\t\t}\n\t\tans.WriteString(k)\n\t\tans.WriteString(\"=\")\n\t\tans.WriteString(escape_metadata_value(k, v))\n\t}\n\tif len(payload) > 0 {\n\t\tans.WriteString(\";\")\n\t\tans.WriteString(enc_payload)\n\t}\n\tans.WriteString(\"\\x1b\\\\\")\n\treturn ans.String()\n}\n\nfunc encode(metadata map[string]string, payload string) string {\n\treturn Encode_bytes(metadata, utils.UnsafeStringToBytes(payload))\n}\n\nfunc error_from_status(status string) error {\n\tswitch status {\n\tcase \"ENOSYS\":\n\t\treturn fmt.Errorf(\"no primary selection available on this system\")\n\tcase \"EPERM\":\n\t\treturn fmt.Errorf(\"permission denied\")\n\tcase \"EBUSY\":\n\t\treturn fmt.Errorf(\"a temporary error occurred, try again later.\")\n\tdefault:\n\t\treturn fmt.Errorf(\"%s\", status)\n\t}\n}\n\nfunc parse_escape_code(etype loop.EscapeCodeType, data []byte) (metadata map[string]string, payload []byte, err error) {\n\tif etype != loop.OSC || !bytes.HasPrefix(data, utils.UnsafeStringToBytes(OSC_NUMBER+\";\")) {\n\t\treturn\n\t}\n\tparts := bytes.SplitN(data, utils.UnsafeStringToBytes(\";\"), 3)\n\tmetadata = make(map[string]string)\n\tif len(parts) > 2 && len(parts[2]) > 0 {\n\t\tpayload, err = base64.StdEncoding.DecodeString(utils.UnsafeBytesToString(parts[2]))\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"Received OSC %s packet from terminal with invalid base64 encoded payload\", OSC_NUMBER)\n\t\t\treturn\n\t\t}\n\t}\n\tif len(parts) > 1 {\n\t\tfor record := range bytes.SplitSeq(parts[1], utils.UnsafeStringToBytes(\":\")) {\n\t\t\trp := bytes.SplitN(record, utils.UnsafeStringToBytes(\"=\"), 2)\n\t\t\tv := \"\"\n\t\t\tif len(rp) == 2 {\n\t\t\t\tv = string(rp[1])\n\t\t\t}\n\t\t\tk := string(rp[0])\n\t\t\tmetadata[k] = unescape_metadata_value(k, v)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc parse_aliases(raw []string) (map[string][]string, error) {\n\tans := make(map[string][]string, len(raw))\n\tfor _, x := range raw {\n\t\tk, v, found := strings.Cut(x, \"=\")\n\t\tif !found {\n\t\t\treturn nil, fmt.Errorf(\"%s is not valid MIME alias specification\", x)\n\t\t}\n\t\tans[k] = append(ans[k], v)\n\t\tans[v] = append(ans[v], k)\n\t}\n\treturn ans, nil\n}\n\nfunc run_get_loop(opts *Options, args []string) (err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar available_mimes []string\n\tvar wg sync.WaitGroup\n\tvar getting_data_for string\n\trequested_mimes := make(map[string]*Output)\n\treading_available_mimes := true\n\toutputs := make([]*Output, len(args))\n\taliases, merr := parse_aliases(opts.Alias)\n\tif merr != nil {\n\t\treturn merr\n\t}\n\n\tfor i, arg := range args {\n\t\toutputs[i] = &Output{arg: arg, arg_is_stream: arg == \"/dev/stdout\" || arg == \"/dev/stderr\", ext: filepath.Ext(arg)}\n\t\tif len(opts.Mime) > i {\n\t\t\toutputs[i].mime_type = opts.Mime[i]\n\t\t} else {\n\t\t\tif outputs[i].arg_is_stream {\n\t\t\t\toutputs[i].mime_type = \"text/plain\"\n\t\t\t} else {\n\t\t\t\toutputs[i].mime_type = utils.GuessMimeType(outputs[i].arg)\n\t\t\t}\n\t\t}\n\t\tif outputs[i].mime_type == \"\" {\n\t\t\treturn fmt.Errorf(\"Could not detect the MIME type for: %s use --mime to specify it manually\", arg)\n\t\t}\n\t}\n\n\tdefer func() {\n\t\tfor _, o := range outputs {\n\t\t\tif o.dest != nil {\n\t\t\t\to.cleanup()\n\t\t\t}\n\t\t}\n\t}()\n\n\tbasic_metadata := map[string]string{\"type\": \"read\"}\n\tif opts.UsePrimary {\n\t\tbasic_metadata[\"loc\"] = \"primary\"\n\t}\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.QueueWriteString(encode(basic_metadata, \".\"))\n\t\tif opts.Password != \"\" {\n\t\t\tbasic_metadata[\"pw\"] = base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(opts.Password))\n\t\t}\n\t\tif opts.HumanName != \"\" {\n\t\t\tbasic_metadata[\"name\"] = base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(opts.HumanName))\n\t\t}\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnEscapeCode = func(etype loop.EscapeCodeType, data []byte) (err error) {\n\t\tmetadata, payload, err := parse_escape_code(etype, data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif metadata == nil {\n\t\t\treturn nil\n\t\t}\n\t\tif reading_available_mimes {\n\t\t\tswitch metadata[\"status\"] {\n\t\t\tcase \"DATA\":\n\t\t\t\tavailable_mimes = utils.Map(strings.TrimSpace, strings.Split(utils.UnsafeBytesToString(payload), \" \"))\n\t\t\tcase \"OK\":\n\t\t\tcase \"DONE\":\n\t\t\t\treading_available_mimes = false\n\t\t\t\tif len(available_mimes) == 0 {\n\t\t\t\t\treturn fmt.Errorf(\"The clipboard is empty\")\n\t\t\t\t}\n\t\t\t\tfor _, o := range outputs {\n\t\t\t\t\terr = o.assign_mime_type(available_mimes, aliases)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tif o.remote_mime_type == \".\" {\n\t\t\t\t\t\to.started = true\n\t\t\t\t\t\to.add_data(utils.UnsafeStringToBytes(strings.Join(available_mimes, \"\\n\")))\n\t\t\t\t\t\to.all_data_received = true\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequested_mimes[o.remote_mime_type] = o\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif len(requested_mimes) > 0 {\n\t\t\t\t\tlp.QueueWriteString(encode(basic_metadata, strings.Join(utils.Keys(requested_mimes), \" \")))\n\t\t\t\t} else {\n\t\t\t\t\tlp.Quit(0)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"Failed to read list of available data types in the clipboard with error: %w\", error_from_status(metadata[\"status\"]))\n\t\t\t}\n\t\t} else {\n\t\t\tswitch metadata[\"status\"] {\n\t\t\tcase \"DATA\":\n\t\t\t\tcurrent_mime := metadata[\"mime\"]\n\t\t\t\to := requested_mimes[current_mime]\n\t\t\t\tif o != nil {\n\t\t\t\t\tif getting_data_for != current_mime {\n\t\t\t\t\t\tif prev := requested_mimes[getting_data_for]; prev != nil && !prev.all_data_received {\n\t\t\t\t\t\t\tprev.all_data_received = true\n\t\t\t\t\t\t\twg.Add(1)\n\t\t\t\t\t\t\tgo func() {\n\t\t\t\t\t\t\t\tprev.commit()\n\t\t\t\t\t\t\t\twg.Done()\n\t\t\t\t\t\t\t}()\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgetting_data_for = current_mime\n\t\t\t\t\t}\n\t\t\t\t\tif !o.all_data_received {\n\t\t\t\t\t\to.add_data(payload)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase \"OK\":\n\t\t\tcase \"DONE\":\n\t\t\t\tif prev := requested_mimes[getting_data_for]; getting_data_for != \"\" && prev != nil && !prev.all_data_received {\n\t\t\t\t\tprev.all_data_received = true\n\t\t\t\t\twg.Add(1)\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tprev.commit()\n\t\t\t\t\t\twg.Done()\n\t\t\t\t\t}()\n\t\t\t\t\tgetting_data_for = \"\"\n\t\t\t\t}\n\t\t\t\tlp.Quit(0)\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"Failed to read data from the clipboard with error: %w\", error_from_status(metadata[\"status\"]))\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tesc_count := 0\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") || event.MatchesPressOrRepeat(\"esc\") {\n\t\t\tevent.Handled = true\n\t\t\tesc_count++\n\t\t\tif esc_count < 2 {\n\t\t\t\tkey := \"Esc\"\n\t\t\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\t\t\t\tkey = \"Ctrl+C\"\n\t\t\t\t}\n\t\t\t\tlp.QueueWriteString(fmt.Sprintf(\"Waiting for response from terminal, press %s again to abort. This could cause garbage to be spewed to the screen.\\r\\n\", key))\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"Aborted by user!\")\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\twg.Wait()\n\tif err != nil {\n\t\treturn\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\tfor _, o := range outputs {\n\t\tif o.err != nil {\n\t\t\terr = fmt.Errorf(\"Failed to get %s with error: %w\", o.arg, o.err)\n\t\t\treturn\n\t\t}\n\t\tif !o.started {\n\t\t\terr = fmt.Errorf(\"No data for %s with MIME type: %s\", o.arg, o.mime_type)\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "kittens/clipboard/write.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage clipboard\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype Input struct {\n\tsrc              io.Reader\n\targ              string\n\text              string\n\tis_stream        bool\n\tmime_type        string\n\textra_mime_types []string\n}\n\nfunc is_textual_mime(x string) bool {\n\treturn strings.HasPrefix(x, \"text/\") || utils.KnownTextualMimes[x]\n}\n\nfunc is_text_plain_mime(x string) bool {\n\treturn x == \"text/plain\"\n}\n\nfunc (self *Input) has_mime_matching(predicate func(string) bool) bool {\n\tif predicate(self.mime_type) {\n\t\treturn true\n\t}\n\treturn slices.ContainsFunc(self.extra_mime_types, predicate)\n}\n\nfunc write_loop(inputs []*Input, opts *Options) (err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar waiting_for_write loop.IdType\n\tvar buf [4096]byte\n\taliases, aerr := parse_aliases(opts.Alias)\n\tif aerr != nil {\n\t\treturn aerr\n\t}\n\tnum_text_mimes := 0\n\thas_text_plain := false\n\tfor _, i := range inputs {\n\t\ti.extra_mime_types = aliases[i.mime_type]\n\t\tif i.has_mime_matching(is_textual_mime) {\n\t\t\tnum_text_mimes++\n\t\t\tif !has_text_plain && i.has_mime_matching(is_text_plain_mime) {\n\t\t\t\thas_text_plain = true\n\t\t\t}\n\t\t}\n\t}\n\tif num_text_mimes > 0 && !has_text_plain {\n\t\tfor _, i := range inputs {\n\t\t\tif i.has_mime_matching(is_textual_mime) {\n\t\t\t\ti.extra_mime_types = append(i.extra_mime_types, \"text/plain\")\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tmake_metadata := func(ptype, mime string) map[string]string {\n\t\tans := map[string]string{\"type\": ptype}\n\t\tif opts.UsePrimary {\n\t\t\tans[\"loc\"] = \"primary\"\n\t\t}\n\t\tif mime != \"\" {\n\t\t\tans[\"mime\"] = mime\n\t\t}\n\t\tif ptype == \"write\" {\n\t\t\tif opts.Password != \"\" {\n\t\t\t\tans[\"pw\"] = base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(opts.Password))\n\t\t\t}\n\t\t\tif opts.HumanName != \"\" {\n\t\t\t\tans[\"name\"] = base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(opts.HumanName))\n\t\t\t}\n\t\t}\n\t\treturn ans\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\twaiting_for_write = lp.QueueWriteString(encode(make_metadata(\"write\", \"\"), \"\"))\n\t\treturn \"\", nil\n\t}\n\n\twrite_chunk := func() error {\n\t\tif len(inputs) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\ti := inputs[0]\n\t\tn, err := i.src.Read(buf[:])\n\t\tif n > 0 {\n\t\t\twaiting_for_write = lp.QueueWriteString(Encode_bytes(make_metadata(\"wdata\", i.mime_type), buf[:n]))\n\t\t}\n\t\tif err != nil {\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tif len(i.extra_mime_types) > 0 {\n\t\t\t\t\tlp.QueueWriteString(encode(make_metadata(\"walias\", i.mime_type), strings.Join(i.extra_mime_types, \" \")))\n\t\t\t\t}\n\t\t\t\tinputs = inputs[1:]\n\t\t\t\tif len(inputs) == 0 {\n\t\t\t\t\tlp.QueueWriteString(encode(make_metadata(\"wdata\", \"\"), \"\"))\n\t\t\t\t\twaiting_for_write = 0\n\t\t\t\t}\n\t\t\t\treturn lp.OnWriteComplete(waiting_for_write, false)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"Failed to read from %s with error: %w\", i.arg, err)\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnWriteComplete = func(msg_id loop.IdType, has_pending_writes bool) error {\n\t\tif waiting_for_write == msg_id {\n\t\t\treturn write_chunk()\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnEscapeCode = func(etype loop.EscapeCodeType, data []byte) (err error) {\n\t\tmetadata, _, err := parse_escape_code(etype, data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif metadata != nil && metadata[\"type\"] == \"write\" {\n\t\t\tswitch metadata[\"status\"] {\n\t\t\tcase \"DONE\":\n\t\t\t\tlp.Quit(0)\n\t\t\tcase \"EIO\":\n\t\t\t\treturn fmt.Errorf(\"Could not write to clipboard an I/O error occurred while the terminal was processing the data\")\n\t\t\tcase \"EINVAL\":\n\t\t\t\treturn fmt.Errorf(\"Could not write to clipboard base64 encoding invalid\")\n\t\t\tcase \"ENOSYS\":\n\t\t\t\treturn fmt.Errorf(\"Could not write to primary selection as the system does not support it\")\n\t\t\tcase \"EPERM\":\n\t\t\t\treturn fmt.Errorf(\"Could not write to clipboard as permission was denied\")\n\t\t\tcase \"EBUSY\":\n\t\t\t\treturn fmt.Errorf(\"Could not write to clipboard, a temporary error occurred, try again later.\")\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"Could not write to clipboard unknowns status returned from terminal: %#v\", metadata[\"status\"])\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tesc_count := 0\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") || event.MatchesPressOrRepeat(\"esc\") {\n\t\t\tevent.Handled = true\n\t\t\tesc_count++\n\t\t\tif esc_count < 2 {\n\t\t\t\tkey := \"Esc\"\n\t\t\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\t\t\t\tkey = \"Ctrl+C\"\n\t\t\t\t}\n\t\t\t\tlp.QueueWriteString(fmt.Sprintf(\"Waiting for response from terminal, press %s again to abort. This could cause garbage to be spewed to the screen.\\r\\n\", key))\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"Aborted by user!\")\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc run_set_loop(opts *Options, args []string) (err error) {\n\tinputs := make([]*Input, len(args))\n\tto_process := make([]*Input, len(args))\n\tdefer func() {\n\t\tfor _, i := range inputs {\n\t\t\tif i != nil && i.src != nil {\n\t\t\t\trc, ok := i.src.(io.Closer)\n\t\t\t\tif ok {\n\t\t\t\t\trc.Close()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\tfor i, arg := range args {\n\t\tif arg == \"/dev/stdin\" {\n\t\t\tf, _, err := preread_stdin()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tinputs[i] = &Input{arg: arg, src: f, is_stream: true}\n\t\t} else {\n\t\t\tf, err := os.Open(arg)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"Failed to open %s with error: %w\", arg, err)\n\t\t\t}\n\t\t\tinputs[i] = &Input{arg: arg, src: f, ext: filepath.Ext(arg)}\n\t\t}\n\t\tif i < len(opts.Mime) {\n\t\t\tinputs[i].mime_type = opts.Mime[i]\n\t\t} else if inputs[i].is_stream {\n\t\t\tinputs[i].mime_type = \"text/plain\"\n\t\t} else if inputs[i].ext != \"\" {\n\t\t\tinputs[i].mime_type = utils.GuessMimeType(inputs[i].arg)\n\t\t}\n\t\tif inputs[i].mime_type == \"\" {\n\t\t\treturn fmt.Errorf(\"Could not guess MIME type for %s use the --mime option to specify a MIME type\", arg)\n\t\t}\n\t\tto_process[i] = inputs[i]\n\t}\n\treturn write_loop(to_process, opts)\n}\n"
  },
  {
    "path": "kittens/command_palette/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/command_palette/main.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\npackage command_palette\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/fzf\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\n// JSON data structures matching Python collect_keys_data output\ntype Binding struct {\n\tKey           string `json:\"key\"`\n\tAction        string `json:\"action\"`\n\tActionDisplay string `json:\"action_display\"`\n\tDefinition    string `json:\"definition\"`\n\tHelp          string `json:\"help\"`\n\tLongHelp      string `json:\"long_help\"`\n\tCategory      string\n\tMode          string\n\tIsMouse       bool\n}\n\ntype InputData struct {\n\tModes         map[string]map[string][]Binding `json:\"modes\"`\n\tMouse         []Binding                       `json:\"mouse\"`\n\tModeOrder     []string                        `json:\"mode_order\"`\n\tCategoryOrder map[string][]string             `json:\"category_order\"`\n}\n\n// DisplayItem wraps a binding with its per-column search texts for FZF scoring\ntype DisplayItem struct {\n\tbinding  Binding\n\tcolTexts [3]string // [0]=key, [1]=action_display, [2]=category\n}\n\n// matchInfo stores which column matched and the matched character positions\ntype matchInfo struct {\n\tcolIdx    int   // which column matched: 0=key, 1=action_display, 2=category\n\tpositions []int // rune positions in the matched column text\n}\n\ntype displayLine struct {\n\ttext      string\n\tisHeader  bool\n\tisModeHdr bool\n\titemIdx   int // index into filtered_idx, -1 for headers\n}\n\nconst maxKeyDisplayWidth = 30\n\n// unmappedLabel is shown in the key column for actions with no keyboard shortcut.\nconst unmappedLabel = \"(unmapped)\"\n\n// truncateToWidth truncates s to fit within maxWidth cells, appending \"...\" if\n// truncated and maxWidth > 3. When maxWidth <= 3, the string is simply trimmed\n// to fit without appending ellipsis (no room for it).\nfunc truncateToWidth(s string, maxWidth int) string {\n\tif wcswidth.Stringwidth(s) <= maxWidth {\n\t\treturn s\n\t}\n\trunes := []rune(s)\n\tif maxWidth <= 3 {\n\t\t// Not enough room for ellipsis; just trim to fit\n\t\tfor len(runes) > 0 && wcswidth.Stringwidth(string(runes)) > maxWidth {\n\t\t\trunes = runes[:len(runes)-1]\n\t\t}\n\t\treturn string(runes)\n\t}\n\tfor len(runes) > 0 && wcswidth.Stringwidth(string(runes))+3 > maxWidth {\n\t\trunes = runes[:len(runes)-1]\n\t}\n\treturn string(runes) + \"...\"\n}\n\n// CachedSettings holds persistent UI settings stored in command-palette.json.\ntype CachedSettings struct {\n\tShowUnmapped bool `json:\"show_unmapped\"`\n}\n\ntype Handler struct {\n\tlp              *loop.Loop\n\tscreen_size     loop.ScreenSize\n\tall_items       []DisplayItem\n\tmatcher         *fzf.FuzzyMatcher\n\tfiltered_idx    []int       // indices into all_items for current results\n\tmatch_infos     []matchInfo // parallel to filtered_idx, valid when query != \"\"\n\tquery           string\n\tselected_idx    int\n\tscroll_offset   int\n\tinput_data      InputData\n\tresult          string // action definition to execute after exit\n\tdisplay_lines   []displayLine\n\tresults_start_y int\n\tresults_height  int\n\tshow_unmapped   bool\n\tcv              *utils.CachedValues[*CachedSettings]\n}\n\nfunc (h *Handler) initialize() (string, error) {\n\tsz, err := h.lp.ScreenSize()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\th.screen_size = sz\n\th.lp.SetCursorVisible(true)\n\th.lp.SetCursorShape(loop.BAR_CURSOR, true)\n\th.lp.AllowLineWrapping(false)\n\th.lp.SetWindowTitle(\"Command Palette\")\n\n\t// Initialize with ShowUnmapped: true as the default; Load() returns this\n\t// default when no cache file exists yet.\n\th.cv = utils.NewCachedValues(\"command-palette\", &CachedSettings{ShowUnmapped: true})\n\tsettings := h.cv.Load()\n\th.show_unmapped = settings.ShowUnmapped\n\n\tif err := h.loadData(); err != nil {\n\t\treturn \"\", err\n\t}\n\n\th.matcher = fzf.NewFuzzyMatcher(fzf.DEFAULT_SCHEME)\n\th.updateFilter()\n\th.draw_screen()\n\th.lp.SendOverlayReady()\n\treturn \"\", nil\n}\n\nfunc (h *Handler) loadData() error {\n\tdata, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read stdin: %w\", err)\n\t}\n\tif len(data) == 0 {\n\t\treturn fmt.Errorf(\"no input data received on stdin; this kitten must be launched from kitty\")\n\t}\n\tif err := json.Unmarshal(data, &h.input_data); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse input data: %w\", err)\n\t}\n\n\th.flattenBindings()\n\treturn nil\n}\n\n// flattenBindings converts the hierarchical mode/category/binding data into\n// a flat list suitable for display and FZF scoring. Uses the explicit ordering\n// arrays from Python since Go maps do not preserve insertion order.\nfunc (h *Handler) flattenBindings() {\n\t// Use explicit mode ordering from Python, falling back to sorted keys\n\tmodeNames := h.input_data.ModeOrder\n\tif len(modeNames) == 0 {\n\t\tmodeNames = make([]string, 0, len(h.input_data.Modes))\n\t\tfor name := range h.input_data.Modes {\n\t\t\tmodeNames = append(modeNames, name)\n\t\t}\n\t\tsort.Slice(modeNames, func(i, j int) bool {\n\t\t\tif modeNames[i] == \"\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif modeNames[j] == \"\" {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn modeNames[i] < modeNames[j]\n\t\t})\n\t}\n\n\tfor _, modeName := range modeNames {\n\t\tcategories, ok := h.input_data.Modes[modeName]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Use explicit category ordering from Python, falling back to sorted keys\n\t\tcatNames := h.input_data.CategoryOrder[modeName]\n\t\tif len(catNames) == 0 {\n\t\t\tcatNames = make([]string, 0, len(categories))\n\t\t\tfor name := range categories {\n\t\t\t\tcatNames = append(catNames, name)\n\t\t\t}\n\t\t\tsort.Strings(catNames)\n\t\t}\n\n\t\tfor _, catName := range catNames {\n\t\t\tbindings, ok := categories[catName]\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, b := range bindings {\n\t\t\t\tb.Category = catName\n\t\t\t\tb.Mode = modeName\n\t\t\t\tb.IsMouse = false\n\t\t\t\tkeyText := b.Key\n\t\t\t\tif keyText == \"\" {\n\t\t\t\t\tkeyText = unmappedLabel\n\t\t\t\t}\n\t\t\t\th.all_items = append(h.all_items, DisplayItem{\n\t\t\t\t\tbinding:  b,\n\t\t\t\t\tcolTexts: [3]string{keyText, b.ActionDisplay, catName},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\t// Mouse bindings\n\tfor _, b := range h.input_data.Mouse {\n\t\tb.Category = \"Mouse actions\"\n\t\tb.Mode = \"\"\n\t\tb.IsMouse = true\n\t\th.all_items = append(h.all_items, DisplayItem{\n\t\t\tbinding:  b,\n\t\t\tcolTexts: [3]string{b.Key, b.ActionDisplay, \"Mouse actions\"},\n\t\t})\n\t}\n}\n\nfunc (h *Handler) updateFilter() {\n\tif h.query == \"\" {\n\t\t// Show all items in original order, respecting the show_unmapped toggle\n\t\th.filtered_idx = make([]int, 0, len(h.all_items))\n\t\tfor i, item := range h.all_items {\n\t\t\tif !h.show_unmapped && item.binding.Key == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\th.filtered_idx = append(h.filtered_idx, i)\n\t\t}\n\t\th.match_infos = nil\n\t\th.selected_idx = 0\n\t\th.scroll_offset = 0\n\t\treturn\n\t}\n\n\tnItems := len(h.all_items)\n\n\t// Build per-column text slices for batch FZF scoring\n\tcolSlices := [3][]string{\n\t\tmake([]string, nItems),\n\t\tmake([]string, nItems),\n\t\tmake([]string, nItems),\n\t}\n\tfor i, item := range h.all_items {\n\t\tcolSlices[0][i] = item.colTexts[0]\n\t\tcolSlices[1][i] = item.colTexts[1]\n\t\tcolSlices[2][i] = item.colTexts[2]\n\t}\n\n\t// Score each column independently\n\tcolResults := [3][]fzf.Result{}\n\tfor c := 0; c < 3; c++ {\n\t\tresults, err := h.matcher.Score(colSlices[c], h.query)\n\t\tif err == nil {\n\t\t\tcolResults[c] = results\n\t\t}\n\t}\n\n\ttype scored struct {\n\t\tidx       int\n\t\tscore     uint\n\t\tcolIdx    int\n\t\tpositions []int\n\t}\n\tvar matches []scored\n\tfor i := range h.all_items {\n\t\tif !h.show_unmapped && h.all_items[i].binding.Key == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tbestScore := uint(0)\n\t\tbestCol := 0\n\t\tvar bestPositions []int\n\t\tfor c := 0; c < 3; c++ {\n\t\t\tif colResults[c] != nil && i < len(colResults[c]) && colResults[c][i].Score > bestScore {\n\t\t\t\tbestScore = colResults[c][i].Score\n\t\t\t\tbestCol = c\n\t\t\t\tbestPositions = colResults[c][i].Positions\n\t\t\t}\n\t\t}\n\t\tif bestScore > 0 {\n\t\t\tmatches = append(matches, scored{idx: i, score: bestScore, colIdx: bestCol, positions: bestPositions})\n\t\t}\n\t}\n\tsort.Slice(matches, func(i, j int) bool {\n\t\treturn matches[i].score > matches[j].score\n\t})\n\th.filtered_idx = make([]int, len(matches))\n\th.match_infos = make([]matchInfo, len(matches))\n\tfor i, m := range matches {\n\t\th.filtered_idx[i] = m.idx\n\t\th.match_infos[i] = matchInfo{colIdx: m.colIdx, positions: m.positions}\n\t}\n\th.selected_idx = 0\n\th.scroll_offset = 0\n}\n\n// highlightMatchedChars returns a string with characters at the given rune\n// positions rendered using matchStyle, and the rest rendered using baseStyle\n// (or unstyled if baseStyle is empty).\nfunc (h *Handler) highlightMatchedChars(text string, positions []int, baseStyle, matchStyle string) string {\n\tif len(positions) == 0 {\n\t\tif baseStyle != \"\" {\n\t\t\treturn h.lp.SprintStyled(baseStyle, text)\n\t\t}\n\t\treturn text\n\t}\n\tposSet := make(map[int]bool, len(positions))\n\tfor _, p := range positions {\n\t\tposSet[p] = true\n\t}\n\trunes := []rune(text)\n\tvar sb strings.Builder\n\tfor i, r := range runes {\n\t\tch := string(r)\n\t\tif posSet[i] {\n\t\t\tsb.WriteString(h.lp.SprintStyled(matchStyle, ch))\n\t\t} else if baseStyle != \"\" {\n\t\t\tsb.WriteString(h.lp.SprintStyled(baseStyle, ch))\n\t\t} else {\n\t\t\tsb.WriteString(ch)\n\t\t}\n\t}\n\treturn sb.String()\n}\n\nfunc (h *Handler) selectedBinding() *Binding {\n\tif h.selected_idx < 0 || h.selected_idx >= len(h.filtered_idx) {\n\t\treturn nil\n\t}\n\tidx := h.filtered_idx[h.selected_idx]\n\tif idx < 0 || idx >= len(h.all_items) {\n\t\treturn nil\n\t}\n\treturn &h.all_items[idx].binding\n}\n\nfunc (h *Handler) draw_screen() {\n\th.lp.StartAtomicUpdate()\n\tdefer h.lp.EndAtomicUpdate()\n\th.lp.ClearScreen()\n\n\twidth := int(h.screen_size.WidthCells)\n\theight := int(h.screen_size.HeightCells)\n\tif width < 10 || height < 5 {\n\t\treturn\n\t}\n\n\t// Layout: line 1 = search bar, lines 2..height-2 = results,\n\t// line height-1 = help text, line height = key hints\n\tsearchBarY := 1\n\tresultsStartY := 2\n\thelpY := height - 1\n\thintsY := height\n\tresultsHeight := helpY - resultsStartY\n\tif resultsHeight < 1 {\n\t\tresultsHeight = 1\n\t}\n\n\th.results_start_y = resultsStartY\n\th.results_height = resultsHeight\n\n\t// Draw search bar\n\th.lp.MoveCursorTo(1, searchBarY)\n\th.lp.QueueWriteString(h.lp.SprintStyled(\"fg=bright-yellow\", \"> \"))\n\th.lp.QueueWriteString(h.query)\n\n\t// Draw results\n\tif h.query == \"\" {\n\t\th.drawGroupedResults(resultsStartY, resultsHeight, width)\n\t} else {\n\t\th.drawFlatResults(resultsStartY, resultsHeight, width)\n\t}\n\n\t// Draw help text for selected binding\n\th.lp.MoveCursorTo(1, helpY)\n\tif b := h.selectedBinding(); b != nil && b.Help != \"\" {\n\t\thelpStr := b.Help\n\t\tmaxLen := width - 2\n\t\tif maxLen < 3 {\n\t\t\tmaxLen = 3\n\t\t}\n\t\tif wcswidth.Stringwidth(helpStr) > maxLen {\n\t\t\t// Truncate by runes to avoid breaking multi-byte characters\n\t\t\trunes := []rune(helpStr)\n\t\t\tfor len(runes) > 0 && wcswidth.Stringwidth(string(runes))+3 > maxLen {\n\t\t\t\trunes = runes[:len(runes)-1]\n\t\t\t}\n\t\t\thelpStr = string(runes) + \"...\"\n\t\t}\n\t\th.lp.QueueWriteString(h.lp.SprintStyled(\"dim italic\", \" \"+helpStr))\n\t}\n\n\t// Draw key hints footer\n\th.lp.MoveCursorTo(1, hintsY)\n\tunmappedToggleLabel := \"Show\"\n\tif h.show_unmapped {\n\t\tunmappedToggleLabel = \"Hide\"\n\t}\n\tfooter := h.lp.SprintStyled(\"fg=bright-yellow\", \"[Enter]\") + \" Run  \" +\n\t\th.lp.SprintStyled(\"fg=bright-yellow\", \"[Esc]\") + \" Quit  \" +\n\t\th.lp.SprintStyled(\"fg=bright-yellow\", \"\\u2191\\u2193\") + \" Navigate  \" +\n\t\th.lp.SprintStyled(\"fg=bright-yellow\", \"[F12]\") + \" \" + unmappedToggleLabel + \" unmapped\"\n\tmatchInfo := \"\"\n\tif h.query != \"\" {\n\t\tmatchInfo = fmt.Sprintf(\"  %d/%d\", len(h.filtered_idx), len(h.all_items))\n\t}\n\th.lp.QueueWriteString(\" \" + footer + h.lp.SprintStyled(\"dim\", matchInfo))\n\n\t// Position cursor at end of search text for typing\n\th.lp.MoveCursorTo(3+wcswidth.Stringwidth(h.query), searchBarY)\n}\n\nfunc (h *Handler) drawGroupedResults(startY, maxRows, width int) {\n\tvar lines []displayLine\n\tlastMode := \"\"\n\tlastCategory := \"\"\n\n\tfor fi, idx := range h.filtered_idx {\n\t\titem := &h.all_items[idx]\n\t\tb := &item.binding\n\n\t\t// Mode header when mode changes\n\t\tif b.Mode != lastMode {\n\t\t\tlastMode = b.Mode\n\t\t\tlastCategory = \"\"\n\t\t\tif b.Mode != \"\" {\n\t\t\t\t// Non-default mode: show \"── Keyboard mode: name ──\" header (purple), no category separators\n\t\t\t\tif len(lines) > 0 {\n\t\t\t\t\tlines = append(lines, displayLine{itemIdx: -1, isHeader: true})\n\t\t\t\t}\n\t\t\t\tlabel := \"Keyboard mode: \" + b.Mode\n\t\t\t\tlabelWidth := wcswidth.Stringwidth(label)\n\t\t\t\tsepLen := max(0, width-labelWidth-6)\n\t\t\t\tsep := strings.Repeat(\"\\u2500\", sepLen)\n\t\t\t\tlines = append(lines, displayLine{\n\t\t\t\t\ttext:      fmt.Sprintf(\"  \\u2500\\u2500 %s %s\", label, sep),\n\t\t\t\t\tisModeHdr: true, isHeader: true, itemIdx: -1,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Category header when category changes - only for the default mode (\"\")\n\t\tif b.Mode == \"\" && b.Category != lastCategory {\n\t\t\tlastCategory = b.Category\n\t\t\tif len(lines) > 0 && !lines[len(lines)-1].isHeader {\n\t\t\t\tlines = append(lines, displayLine{itemIdx: -1, isHeader: true})\n\t\t\t}\n\t\t\tcatWidth := wcswidth.Stringwidth(b.Category)\n\t\t\tsepLen := max(0, width-catWidth-6)\n\t\t\tsep := strings.Repeat(\"\\u2500\", sepLen)\n\t\t\tlines = append(lines, displayLine{\n\t\t\t\ttext:     fmt.Sprintf(\"  \\u2500\\u2500 %s %s\", b.Category, sep),\n\t\t\t\tisHeader: true, itemIdx: -1,\n\t\t\t})\n\t\t}\n\n\t\t// Binding line — key column shows \"(unmapped)\" for actions with no shortcut\n\t\tkeyDisplay := b.Key\n\t\tif keyDisplay == \"\" {\n\t\t\tkeyDisplay = unmappedLabel\n\t\t}\n\t\tkeyDisplay = truncateToWidth(keyDisplay, maxKeyDisplayWidth)\n\t\tlines = append(lines, displayLine{\n\t\t\ttext:    fmt.Sprintf(\"    %-*s %s\", maxKeyDisplayWidth, keyDisplay, b.ActionDisplay),\n\t\t\titemIdx: fi,\n\t\t})\n\t}\n\n\th.display_lines = lines\n\th.drawLines(lines, startY, maxRows, width)\n}\n\nfunc (h *Handler) drawFlatResults(startY, maxRows, width int) {\n\tif len(h.filtered_idx) == 0 {\n\t\th.lp.MoveCursorTo(1, startY)\n\t\th.lp.QueueWriteString(h.lp.SprintStyled(\"italic dim\", \"  No matches found\"))\n\t\th.display_lines = []displayLine{}\n\t\treturn\n\t}\n\n\tvar lines []displayLine\n\tfor fi, idx := range h.filtered_idx {\n\t\titem := &h.all_items[idx]\n\t\tb := &item.binding\n\t\tkeyDisplay := b.Key\n\t\tif keyDisplay == \"\" {\n\t\t\tkeyDisplay = unmappedLabel\n\t\t}\n\t\tkeyDisplay = truncateToWidth(keyDisplay, maxKeyDisplayWidth)\n\t\tcatSuffix := \"\"\n\t\tif b.Mode != \"\" {\n\t\t\tcatSuffix = fmt.Sprintf(\" [%s/%s]\", b.Mode, b.Category)\n\t\t} else {\n\t\t\tcatSuffix = fmt.Sprintf(\" [%s]\", b.Category)\n\t\t}\n\t\tlines = append(lines, displayLine{\n\t\t\ttext:    fmt.Sprintf(\"    %-*s %-30s%s\", maxKeyDisplayWidth, keyDisplay, b.ActionDisplay, catSuffix),\n\t\t\titemIdx: fi,\n\t\t})\n\t}\n\n\th.display_lines = lines\n\th.drawLines(lines, startY, maxRows, width)\n}\n\nfunc (h *Handler) drawLines(lines []displayLine, startY, maxRows, width int) {\n\tif maxRows <= 0 || len(lines) == 0 {\n\t\treturn\n\t}\n\n\t// Adjust scroll to keep selected item visible\n\tselectedLineIdx := -1\n\tfor i, dl := range lines {\n\t\tif dl.itemIdx == h.selected_idx {\n\t\t\tselectedLineIdx = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif selectedLineIdx >= 0 {\n\t\tif selectedLineIdx < h.scroll_offset {\n\t\t\t// Scroll up to show selected item; also reveal any header lines above it\n\t\t\th.scroll_offset = selectedLineIdx\n\t\t\tfor h.scroll_offset > 0 && lines[h.scroll_offset-1].isHeader {\n\t\t\t\th.scroll_offset--\n\t\t\t}\n\t\t}\n\t\tif selectedLineIdx >= h.scroll_offset+maxRows {\n\t\t\th.scroll_offset = selectedLineIdx - maxRows + 1\n\t\t}\n\t}\n\th.scroll_offset = max(0, h.scroll_offset)\n\th.scroll_offset = min(h.scroll_offset, max(0, len(lines)-maxRows))\n\n\tend := min(h.scroll_offset+maxRows, len(lines))\n\tfor row, li := range lines[h.scroll_offset:end] {\n\t\th.lp.MoveCursorTo(1, startY+row)\n\t\ttext := li.text\n\t\t// Truncate at rune boundary to avoid breaking multi-byte characters\n\t\tif wcswidth.Stringwidth(text) > width {\n\t\t\trunes := []rune(text)\n\t\t\tfor len(runes) > 0 && wcswidth.Stringwidth(string(runes)) > width {\n\t\t\t\trunes = runes[:len(runes)-1]\n\t\t\t}\n\t\t\ttext = string(runes)\n\t\t}\n\n\t\tif li.isModeHdr {\n\t\t\th.lp.QueueWriteString(h.lp.SprintStyled(\"bold fg=magenta\", text))\n\t\t} else if li.isHeader {\n\t\t\th.lp.QueueWriteString(h.lp.SprintStyled(\"fg=bright-blue\", text))\n\t\t} else if li.itemIdx == h.selected_idx {\n\t\t\t// Selected item: highlight with reverse video\n\t\t\tpadded := text\n\t\t\ttextWidth := wcswidth.Stringwidth(text)\n\t\t\tif textWidth < width {\n\t\t\t\tpadded += strings.Repeat(\" \", width-textWidth)\n\t\t\t}\n\t\t\th.lp.QueueWriteString(h.lp.SprintStyled(\"fg=black bg=white\", padded))\n\t\t} else {\n\t\t\th.drawBindingLine(text, li.itemIdx, width)\n\t\t}\n\t}\n}\n\nfunc (h *Handler) drawBindingLine(text string, filteredIdx, width int) {\n\tif filteredIdx < 0 || filteredIdx >= len(h.filtered_idx) {\n\t\th.lp.QueueWriteString(text)\n\t\treturn\n\t}\n\tidx := h.filtered_idx[filteredIdx]\n\tif idx < 0 || idx >= len(h.all_items) {\n\t\th.lp.QueueWriteString(text)\n\t\treturn\n\t}\n\tb := &h.all_items[idx].binding\n\n\t// Build the key display (using unmappedLabel for items with no shortcut)\n\trawKey := b.Key\n\tif rawKey == \"\" {\n\t\trawKey = unmappedLabel\n\t}\n\tkeyDisplay := truncateToWidth(rawKey, maxKeyDisplayWidth)\n\n\t// Determine match info for highlighting (only set when a query is active)\n\tvar mi *matchInfo\n\tif h.query != \"\" && filteredIdx < len(h.match_infos) {\n\t\tmi = &h.match_infos[filteredIdx]\n\t}\n\n\tconst matchStyle = \"fg=bright-yellow\"\n\tconst keyStyle = \"fg=green\"\n\tconst unmappedStyle = \"dim fg=green\"\n\n\t// Render key column (4-space indent + key padded to maxKeyDisplayWidth + space)\n\tpaddingLen := max(0, maxKeyDisplayWidth-wcswidth.Stringwidth(keyDisplay))\n\tif mi != nil && mi.colIdx == 0 {\n\t\tks := keyStyle\n\t\tif b.Key == \"\" {\n\t\t\tks = unmappedStyle\n\t\t}\n\t\th.lp.QueueWriteString(\"    \")\n\t\th.lp.QueueWriteString(h.highlightMatchedChars(keyDisplay, mi.positions, ks, matchStyle))\n\t\th.lp.QueueWriteString(strings.Repeat(\" \", paddingLen) + \" \")\n\t} else if b.Key == \"\" {\n\t\th.lp.QueueWriteString(h.lp.SprintStyled(unmappedStyle, \"    \"+keyDisplay+strings.Repeat(\" \", paddingLen)+\" \"))\n\t} else {\n\t\th.lp.QueueWriteString(h.lp.SprintStyled(keyStyle, \"    \"+keyDisplay+strings.Repeat(\" \", paddingLen)+\" \"))\n\t}\n\n\t// Render action display column\n\tif mi != nil && mi.colIdx == 1 {\n\t\th.lp.QueueWriteString(h.highlightMatchedChars(b.ActionDisplay, mi.positions, \"\", matchStyle))\n\t} else {\n\t\th.lp.QueueWriteString(b.ActionDisplay)\n\t}\n\n\t// Render category suffix (only present in flat / search-results mode)\n\tif h.query != \"\" {\n\t\tif mi != nil && mi.colIdx == 2 {\n\t\t\tif b.Mode != \"\" {\n\t\t\t\th.lp.QueueWriteString(fmt.Sprintf(\" [%s/\", b.Mode))\n\t\t\t} else {\n\t\t\t\th.lp.QueueWriteString(\" [\")\n\t\t\t}\n\t\t\th.lp.QueueWriteString(h.highlightMatchedChars(b.Category, mi.positions, \"\", matchStyle))\n\t\t\th.lp.QueueWriteString(\"]\")\n\t\t} else {\n\t\t\tif b.Mode != \"\" {\n\t\t\t\th.lp.QueueWriteString(fmt.Sprintf(\" [%s/%s]\", b.Mode, b.Category))\n\t\t\t} else {\n\t\t\t\th.lp.QueueWriteString(fmt.Sprintf(\" [%s]\", b.Category))\n\t\t\t}\n\t\t}\n\t}\n}\n\n// rowToFilteredIdx converts a 0-indexed cell Y coordinate to a filtered item\n// index, or -1 if the cell is not over a clickable item. Internally converts\n// to 1-indexed screen rows (matching the MoveCursorTo convention) to compare\n// against results_start_y.\nfunc (h *Handler) rowToFilteredIdx(cellY int) int {\n\tscreenRow := cellY + 1 // convert 0-indexed cell to 1-indexed screen row\n\tif screenRow < h.results_start_y || screenRow >= h.results_start_y+h.results_height {\n\t\treturn -1\n\t}\n\tlineIdx := h.scroll_offset + (screenRow - h.results_start_y)\n\tif lineIdx < 0 || lineIdx >= len(h.display_lines) {\n\t\treturn -1\n\t}\n\treturn h.display_lines[lineIdx].itemIdx\n}\n\nfunc (h *Handler) onMouseEvent(ev *loop.MouseEvent) error {\n\tswitch ev.Event_type {\n\tcase loop.MOUSE_CLICK:\n\t\tif ev.Buttons&loop.LEFT_MOUSE_BUTTON != 0 {\n\t\t\tfi := h.rowToFilteredIdx(ev.Cell.Y)\n\t\t\tif fi >= 0 {\n\t\t\t\th.selected_idx = fi\n\t\t\t\th.triggerSelected()\n\t\t\t}\n\t\t}\n\tcase loop.MOUSE_MOVE:\n\t\tfi := h.rowToFilteredIdx(ev.Cell.Y)\n\t\th.lp.ClearPointerShapes()\n\t\tif fi >= 0 {\n\t\t\th.lp.PushPointerShape(loop.POINTER_POINTER)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (h *Handler) onKeyEvent(ev *loop.KeyEvent) error {\n\tif ev.MatchesPressOrRepeat(\"escape\") {\n\t\tev.Handled = true\n\t\tif h.query != \"\" {\n\t\t\th.query = \"\"\n\t\t\th.updateFilter()\n\t\t\th.draw_screen()\n\t\t} else {\n\t\t\th.lp.Quit(0)\n\t\t}\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"enter\") {\n\t\tev.Handled = true\n\t\th.triggerSelected()\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"up\") || ev.MatchesPressOrRepeat(\"ctrl+k\") || ev.MatchesPressOrRepeat(\"ctrl+p\") {\n\t\tev.Handled = true\n\t\th.moveSelection(-1)\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"down\") || ev.MatchesPressOrRepeat(\"ctrl+j\") || ev.MatchesPressOrRepeat(\"ctrl+n\") {\n\t\tev.Handled = true\n\t\th.moveSelection(1)\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"page_up\") {\n\t\tev.Handled = true\n\t\tdelta := max(1, int(h.screen_size.HeightCells)-4)\n\t\th.moveSelection(-delta)\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"page_down\") {\n\t\tev.Handled = true\n\t\tdelta := max(1, int(h.screen_size.HeightCells)-4)\n\t\th.moveSelection(delta)\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"home\") || ev.MatchesPressOrRepeat(\"ctrl+home\") {\n\t\tev.Handled = true\n\t\th.selected_idx = 0\n\t\th.draw_screen()\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"end\") || ev.MatchesPressOrRepeat(\"ctrl+end\") {\n\t\tev.Handled = true\n\t\tif len(h.filtered_idx) > 0 {\n\t\t\th.selected_idx = len(h.filtered_idx) - 1\n\t\t}\n\t\th.draw_screen()\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"backspace\") {\n\t\tev.Handled = true\n\t\tif h.query != \"\" {\n\t\t\tg := wcswidth.SplitIntoGraphemes(h.query)\n\t\t\th.query = strings.Join(g[:len(g)-1], \"\")\n\t\t\th.updateFilter()\n\t\t\th.draw_screen()\n\t\t} else {\n\t\t\th.lp.Beep()\n\t\t}\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"f12\") {\n\t\tev.Handled = true\n\t\th.show_unmapped = !h.show_unmapped\n\t\tif h.cv != nil {\n\t\t\th.cv.Opts.ShowUnmapped = h.show_unmapped\n\t\t\th.cv.Save()\n\t\t}\n\t\th.updateFilter()\n\t\th.draw_screen()\n\t\treturn nil\n\t}\n\treturn nil\n}\n\nfunc (h *Handler) onText(text string, from_key_event bool, in_bracketed_paste bool) error {\n\th.query += text\n\th.updateFilter()\n\th.draw_screen()\n\treturn nil\n}\n\nfunc (h *Handler) onResize(old, new_size loop.ScreenSize) error {\n\th.screen_size = new_size\n\th.draw_screen()\n\treturn nil\n}\n\nfunc (h *Handler) moveSelection(delta int) {\n\tif len(h.filtered_idx) == 0 {\n\t\treturn\n\t}\n\th.selected_idx += delta\n\th.selected_idx = max(0, h.selected_idx)\n\th.selected_idx = min(h.selected_idx, len(h.filtered_idx)-1)\n\th.draw_screen()\n}\n\nfunc (h *Handler) triggerSelected() {\n\tb := h.selectedBinding()\n\tif b == nil || b.IsMouse {\n\t\th.lp.Beep()\n\t\treturn\n\t}\n\th.result = b.Definition\n\th.lp.Quit(0)\n}\n\nfunc main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tif tty.IsTerminal(os.Stdin.Fd()) {\n\t\treturn 1, fmt.Errorf(\"This kitten must only be run via the command_palette action mapped to a shortcut in kitty.conf\")\n\t}\n\toutput := tui.KittenOutputSerializer()\n\tlp, err := loop.New()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\n\thandler := &Handler{lp: lp}\n\tlp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)\n\n\tlp.OnInitialize = func() (string, error) {\n\t\treturn handler.initialize()\n\t}\n\tlp.OnFinalize = func() string { return \"\" }\n\tlp.OnKeyEvent = handler.onKeyEvent\n\tlp.OnText = handler.onText\n\tlp.OnResize = handler.onResize\n\tlp.OnMouseEvent = handler.onMouseEvent\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal:\", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\trc = lp.ExitCode()\n\tif handler.result != \"\" {\n\t\ts, serr := output(map[string]string{\"action\": handler.result})\n\t\tif serr == nil {\n\t\t\tfmt.Println(s)\n\t\t}\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/command_palette/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport sys\nfrom functools import partial\nfrom typing import Any\n\nfrom kitty.fast_data_types import add_timer, get_boss\nfrom kitty.typing_compat import BossType\n\nfrom ..tui.handler import result_handler\n\n\ndef collect_keys_data(opts: Any) -> dict[str, Any]:\n    \"\"\"Collect all keybinding data from options into a JSON-serializable dict.\"\"\"\n    from kitty.actions import get_all_actions, groups\n    from kitty.options.utils import KeyDefinition\n    from kitty.types import Shortcut\n\n    # Build action->group and action->help lookups\n    action_to_group: dict[str, str] = {}\n    action_to_help: dict[str, str] = {}\n    action_to_long_help: dict[str, str] = {}\n    for group_key, actions in get_all_actions().items():\n        for action in actions:\n            action_to_group[action.name] = groups[group_key]\n            action_to_help[action.name] = action.short_help\n            action_to_long_help[action.name] = action.long_help\n\n    modes: dict[str, dict[str, list[dict[str, str]]]] = {}\n\n    def as_sc(k: 'Any', v: KeyDefinition) -> Shortcut:\n        if v.is_sequence:\n            return Shortcut((v.trigger,) + v.rest)\n        return Shortcut((k,))\n\n    for mode_name, mode in opts.keyboard_modes.items():\n        categories: dict[str, list[dict[str, str]]] = {}\n        for key, defns in mode.keymap.items():\n            # Use last non-duplicate definition\n            seen: set[tuple[Any, ...]] = set()\n            uniq: list[KeyDefinition] = []\n            for d in reversed(defns):\n                uid = d.unique_identity_within_keymap\n                if uid not in seen:\n                    seen.add(uid)\n                    uniq.append(d)\n            for d in uniq:\n                sc = as_sc(key, d)\n                key_repr = sc.human_repr(opts.kitty_mod)\n                action_repr = d.human_repr()\n                # Determine category from first word of action definition\n                action_name = d.definition.split()[0] if d.definition else 'no_op'\n                category = action_to_group.get(action_name, 'Miscellaneous')\n                help_text = action_to_help.get(action_name, '')\n                long_help = action_to_long_help.get(action_name, '')\n                categories.setdefault(category, []).append({\n                    'key': key_repr,\n                    'action': action_name,\n                    'action_display': action_repr,\n                    'definition': d.definition or action_name,\n                    'help': help_text,\n                    'long_help': long_help,\n                })\n        # Sort within categories\n        for cat in categories:\n            categories[cat].sort(key=lambda b: b['key'])\n        # Order categories by the groups order\n        ordered: dict[str, list[dict[str, str]]] = {}\n        for group_title in groups.values():\n            if group_title in categories:\n                ordered[group_title] = categories.pop(group_title)\n        # Add any remaining\n        for cat_name, binds in sorted(categories.items()):\n            ordered[cat_name] = binds\n        modes[mode_name] = ordered\n\n    # Move push_keyboard_mode <name> bindings from the default mode into the\n    # respective keyboard mode's section so they appear alongside its shortcuts.\n    if '' in modes:\n        new_default_cats: dict[str, list[dict[str, str]]] = {}\n        for cat_name, bindings in modes[''].items():\n            keep: list[dict[str, str]] = []\n            for b in bindings:\n                if b['action'] == 'push_keyboard_mode':\n                    parts = b['definition'].split()\n                    target = parts[1] if len(parts) > 1 else ''\n                    if target and target in modes:\n                        if 'Enter mode' not in modes[target]:\n                            new_target: dict[str, list[dict[str, str]]] = {'Enter mode': [b]}\n                            new_target.update(modes[target])\n                            modes[target] = new_target\n                        else:\n                            modes[target]['Enter mode'].append(b)\n                        continue\n                keep.append(b)\n            if keep:\n                new_default_cats[cat_name] = keep\n        modes[''] = new_default_cats\n\n    # Add unmapped actions (actions with no keyboard shortcut).\n    # Collect all action names that already appear in a binding.\n    mapped_actions: set[str] = set()\n    for mode_cats in modes.values():\n        for bindings in mode_cats.values():\n            for b in bindings:\n                mapped_actions.add(b['action'])\n\n    default_mode_cats = modes.setdefault('', {})\n    for group_key, actions in get_all_actions().items():\n        category = groups[group_key]\n        for action in actions:\n            if action.name not in mapped_actions:\n                default_mode_cats.setdefault(category, []).append({\n                    'key': '',\n                    'action': action.name,\n                    'action_display': action.name,\n                    'definition': action.name,\n                    'help': action.short_help,\n                    'long_help': action.long_help,\n                })\n\n    # Re-sort each category: mapped entries (non-empty key) by key first,\n    # then unmapped entries (empty key) sorted by action name.\n    for cat in default_mode_cats:\n        default_mode_cats[cat].sort(key=lambda b: (b['key'] == '', b['key'] or b['action']))\n\n    # Re-order default_mode_cats by groups ordering (adding unmapped actions may\n    # have appended new categories at the end, breaking the established order).\n    reordered: dict[str, list[dict[str, str]]] = {}\n    for group_title in groups.values():\n        if group_title in default_mode_cats:\n            reordered[group_title] = default_mode_cats[group_title]\n    for cat_name, binds in default_mode_cats.items():\n        if cat_name not in reordered:\n            reordered[cat_name] = binds\n    modes[''] = reordered\n\n    # Emit explicit mode and category ordering since JSON maps lose insertion order\n    mode_order = list(modes.keys())\n    category_order: dict[str, list[str]] = {}\n    for mode_name, cats in modes.items():\n        category_order[mode_name] = list(cats.keys())\n\n    # Mouse mappings\n    mouse: list[dict[str, str]] = []\n    for event, action in opts.mousemap.items():\n        key_repr = event.human_repr(opts.kitty_mod)\n        mouse.append({'key': key_repr, 'action': action, 'action_display': action, 'help': '', 'long_help': ''})\n    mouse.sort(key=lambda b: b['key'])\n\n    return {\n        'modes': modes,\n        'mouse': mouse,\n        'mode_order': mode_order,\n        'category_order': category_order,\n    }\n\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('This kitten must be used only from a kitty.conf mapping')\n\n\ndef callback(target_window_id: int, action: str, timer_id: int | None) -> None:\n    boss = get_boss()\n    w = boss.window_id_map.get(target_window_id)\n    boss.combine(action, w)\n\n\n@result_handler(has_ready_notification=True)\ndef handle_result(args: list[str], data: dict[str, Any], target_window_id: int, boss: BossType) -> None:\n    if data and (action := data.get('action')):\n        # run action after event loop tick so command palette overlay is closed\n        add_timer(partial(callback, target_window_id, action), 0, False)\n\nhelp_text = 'Browse and trigger keyboard shortcuts and actions'\nusage = ''\nOPTIONS = r'''\n'''.format\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = help_text\n"
  },
  {
    "path": "kittens/command_palette/main_test.go",
    "content": "package command_palette\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kovidgoyal/kitty/tools/fzf\"\n)\n\nfunc sampleInputJSON() string {\n\treturn `{\n\t\t\"modes\": {\n\t\t\t\"\": {\n\t\t\t\t\"Copy/paste\": [\n\t\t\t\t\t{\"key\": \"ctrl+shift+c\", \"action\": \"copy_to_clipboard\", \"action_display\": \"copy_to_clipboard\", \"definition\": \"copy_to_clipboard\", \"help\": \"Copy the selected text from the active window to the clipboard\", \"long_help\": \"\"},\n\t\t\t\t\t{\"key\": \"ctrl+shift+v\", \"action\": \"paste_from_clipboard\", \"action_display\": \"paste_from_clipboard\", \"definition\": \"paste_from_clipboard\", \"help\": \"Paste from the clipboard to the active window\", \"long_help\": \"\"}\n\t\t\t\t],\n\t\t\t\t\"Scrolling\": [\n\t\t\t\t\t{\"key\": \"ctrl+shift+up\", \"action\": \"scroll_line_up\", \"action_display\": \"scroll_line_up\", \"definition\": \"scroll_line_up\", \"help\": \"Scroll up one line\", \"long_help\": \"\"},\n\t\t\t\t\t{\"key\": \"ctrl+shift+down\", \"action\": \"scroll_line_down\", \"action_display\": \"scroll_line_down\", \"definition\": \"scroll_line_down\", \"help\": \"Scroll down one line\", \"long_help\": \"\"}\n\t\t\t\t],\n\t\t\t\t\"Window management\": [\n\t\t\t\t\t{\"key\": \"ctrl+shift+enter\", \"action\": \"new_window\", \"action_display\": \"new_window\", \"definition\": \"new_window\", \"help\": \"Open a new window\", \"long_help\": \"\"}\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"mw\": {\n\t\t\t\t\"Miscellaneous\": [\n\t\t\t\t\t{\"key\": \"left\", \"action\": \"neighboring_window\", \"action_display\": \"neighboring_window left\", \"definition\": \"neighboring_window left\", \"help\": \"Focus neighbor window\", \"long_help\": \"\"},\n\t\t\t\t\t{\"key\": \"esc\", \"action\": \"pop_keyboard_mode\", \"action_display\": \"pop_keyboard_mode\", \"definition\": \"pop_keyboard_mode\", \"help\": \"Pop keyboard mode\", \"long_help\": \"\"}\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"mouse\": [\n\t\t\t{\"key\": \"left press ungrabbed\", \"action\": \"mouse_selection\", \"action_display\": \"mouse_selection normal\", \"definition\": \"mouse_selection normal\", \"help\": \"\", \"long_help\": \"\"},\n\t\t\t{\"key\": \"ctrl+left press ungrabbed\", \"action\": \"mouse_selection\", \"action_display\": \"mouse_selection rectangle\", \"definition\": \"mouse_selection rectangle\", \"help\": \"\", \"long_help\": \"\"}\n\t\t],\n\t\t\"mode_order\": [\"\", \"mw\"],\n\t\t\"category_order\": {\n\t\t\t\"\": [\"Copy/paste\", \"Scrolling\", \"Window management\"],\n\t\t\t\"mw\": [\"Miscellaneous\"]\n\t\t}\n\t}`\n}\n\nfunc newTestHandler() *Handler {\n\th := &Handler{}\n\tif err := json.Unmarshal([]byte(sampleInputJSON()), &h.input_data); err != nil {\n\t\tpanic(\"test data JSON is invalid: \" + err.Error())\n\t}\n\th.flattenBindings()\n\th.matcher = fzf.NewFuzzyMatcher(fzf.DEFAULT_SCHEME)\n\treturn h\n}\n\nfunc TestFlattenAllBindings(t *testing.T) {\n\th := newTestHandler()\n\t// 5 default mode + 2 mw mode + 2 mouse = 9\n\tif len(h.all_items) != 9 {\n\t\tt.Fatalf(\"Expected 9 items, got %d\", len(h.all_items))\n\t}\n}\n\nfunc TestDefaultModeComesFirst(t *testing.T) {\n\th := newTestHandler()\n\t// First 5 items should be from default mode\n\tfor i := 0; i < 5; i++ {\n\t\tif h.all_items[i].binding.Mode != \"\" {\n\t\t\tt.Fatalf(\"Item %d should be from default mode, got mode=%q\", i, h.all_items[i].binding.Mode)\n\t\t}\n\t}\n}\n\nfunc TestCategoryOrderPreserved(t *testing.T) {\n\th := newTestHandler()\n\t// Verify categories appear in the order specified by category_order\n\tvar categories []string\n\tseen := map[string]bool{}\n\tfor _, item := range h.all_items {\n\t\tif item.binding.Mode != \"\" || item.binding.IsMouse {\n\t\t\tcontinue\n\t\t}\n\t\tcat := item.binding.Category\n\t\tif !seen[cat] {\n\t\t\tcategories = append(categories, cat)\n\t\t\tseen[cat] = true\n\t\t}\n\t}\n\texpected := []string{\"Copy/paste\", \"Scrolling\", \"Window management\"}\n\tif len(categories) != len(expected) {\n\t\tt.Fatalf(\"Expected %d categories, got %d: %v\", len(expected), len(categories), categories)\n\t}\n\tfor i, cat := range categories {\n\t\tif cat != expected[i] {\n\t\t\tt.Fatalf(\"Category %d: expected %q, got %q\", i, expected[i], cat)\n\t\t}\n\t}\n}\n\nfunc TestCustomModePresent(t *testing.T) {\n\th := newTestHandler()\n\tfound := false\n\tfor _, item := range h.all_items {\n\t\tif item.binding.Mode == \"mw\" {\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !found {\n\t\tt.Fatal(\"Expected to find items from 'mw' mode\")\n\t}\n}\n\nfunc TestMouseBindingsMarkedCorrectly(t *testing.T) {\n\th := newTestHandler()\n\tmouseCount := 0\n\tfor _, item := range h.all_items {\n\t\tif item.binding.IsMouse {\n\t\t\tmouseCount++\n\t\t\tif item.binding.Category != \"Mouse actions\" {\n\t\t\t\tt.Fatalf(\"Mouse binding should have category 'Mouse actions', got %q\", item.binding.Category)\n\t\t\t}\n\t\t}\n\t}\n\tif mouseCount != 2 {\n\t\tt.Fatalf(\"Expected 2 mouse bindings, got %d\", mouseCount)\n\t}\n}\n\nfunc TestFilterNoQueryReturnsAll(t *testing.T) {\n\th := newTestHandler()\n\th.show_unmapped = true // show all items including unmapped\n\th.query = \"\"\n\th.updateFilter()\n\tif len(h.filtered_idx) != len(h.all_items) {\n\t\tt.Fatalf(\"With no query and show_unmapped=true, expected %d items, got %d\", len(h.all_items), len(h.filtered_idx))\n\t}\n\tfor i, idx := range h.filtered_idx {\n\t\tif idx != i {\n\t\t\tt.Fatalf(\"Expected sequential order, got index %d at position %d\", idx, i)\n\t\t}\n\t}\n}\n\nfunc TestFilterMatchesSubset(t *testing.T) {\n\th := newTestHandler()\n\th.query = \"clipboard\"\n\th.updateFilter()\n\tif len(h.filtered_idx) == 0 {\n\t\tt.Fatal(\"Expected matches for 'clipboard'\")\n\t}\n\tif len(h.filtered_idx) >= len(h.all_items) {\n\t\tt.Fatal(\"Expected fewer matches than total items\")\n\t}\n\t// Verify all returned items contain relevant text in at least one column\n\tfor _, idx := range h.filtered_idx {\n\t\titem := &h.all_items[idx]\n\t\tfound := false\n\t\tfor _, col := range item.colTexts {\n\t\t\tif strings.Contains(strings.ToLower(col), \"clipboard\") {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\t_ = found // FZF does fuzzy matching, so this is a soft check\n\t}\n}\n\nfunc TestFilterNonsenseReturnsEmpty(t *testing.T) {\n\th := newTestHandler()\n\th.query = \"zzzznonexistent\"\n\th.updateFilter()\n\tif len(h.filtered_idx) != 0 {\n\t\tt.Fatalf(\"Expected no matches for nonsense, got %d\", len(h.filtered_idx))\n\t}\n}\n\nfunc TestFilterResetsSelectionAndScroll(t *testing.T) {\n\th := newTestHandler()\n\th.query = \"\"\n\th.updateFilter()\n\th.selected_idx = 3\n\th.scroll_offset = 5\n\n\th.query = \"scroll\"\n\th.updateFilter()\n\tif h.selected_idx != 0 {\n\t\tt.Fatalf(\"Expected selection reset to 0, got %d\", h.selected_idx)\n\t}\n\tif h.scroll_offset != 0 {\n\t\tt.Fatalf(\"Expected scroll offset reset to 0, got %d\", h.scroll_offset)\n\t}\n}\n\nfunc TestSelectedBindingValid(t *testing.T) {\n\th := newTestHandler()\n\th.updateFilter()\n\n\tb := h.selectedBinding()\n\tif b == nil {\n\t\tt.Fatal(\"Expected non-nil binding\")\n\t}\n\tif b.Key == \"\" || b.Action == \"\" {\n\t\tt.Fatal(\"Binding should have non-empty key and action\")\n\t}\n}\n\nfunc TestSelectedBindingNilWhenEmpty(t *testing.T) {\n\th := newTestHandler()\n\th.query = \"zzzznonexistent\"\n\th.updateFilter()\n\n\tif b := h.selectedBinding(); b != nil {\n\t\tt.Fatal(\"Expected nil binding when no matches\")\n\t}\n}\n\nfunc TestSelectedBindingNilWhenNegativeIndex(t *testing.T) {\n\th := newTestHandler()\n\th.updateFilter()\n\th.selected_idx = -1\n\n\tif b := h.selectedBinding(); b != nil {\n\t\tt.Fatal(\"Expected nil binding for negative index\")\n\t}\n}\n\nfunc TestSelectedBindingNilWhenOverflowIndex(t *testing.T) {\n\th := newTestHandler()\n\th.updateFilter()\n\th.selected_idx = len(h.filtered_idx) + 10\n\n\tif b := h.selectedBinding(); b != nil {\n\t\tt.Fatal(\"Expected nil binding for overflow index\")\n\t}\n}\n\nfunc TestSearchTextContainsKeyAndAction(t *testing.T) {\n\th := newTestHandler()\n\tfor i, item := range h.all_items {\n\t\t// colTexts[0] = key (or unmappedLabel for empty key), [1] = action_display, [2] = category\n\t\texpectedKey := item.binding.Key\n\t\tif expectedKey == \"\" {\n\t\t\texpectedKey = unmappedLabel\n\t\t}\n\t\tif !strings.Contains(item.colTexts[0], expectedKey) {\n\t\t\tt.Fatalf(\"Item %d: colTexts[0] %q should contain key %q\", i, item.colTexts[0], expectedKey)\n\t\t}\n\t\tif !strings.Contains(item.colTexts[1], item.binding.ActionDisplay) {\n\t\t\tt.Fatalf(\"Item %d: colTexts[1] %q should contain action %q\", i, item.colTexts[1], item.binding.ActionDisplay)\n\t\t}\n\t}\n}\n\nfunc TestHelpTextPreserved(t *testing.T) {\n\th := newTestHandler()\n\thelpCount := 0\n\tfor _, item := range h.all_items {\n\t\tif item.binding.Help != \"\" {\n\t\t\thelpCount++\n\t\t}\n\t}\n\tif helpCount == 0 {\n\t\tt.Fatal(\"Expected at least some bindings to have help text\")\n\t}\n\t// All keyboard bindings in our sample data have help text\n\tif helpCount < 7 {\n\t\tt.Fatalf(\"Expected at least 7 bindings with help text, got %d\", helpCount)\n\t}\n}\n\nfunc TestEmptyInputData(t *testing.T) {\n\th := &Handler{}\n\temptyJSON := `{\"modes\": {}, \"mouse\": [], \"mode_order\": [], \"category_order\": {}}`\n\tif err := json.Unmarshal([]byte(emptyJSON), &h.input_data); err != nil {\n\t\tt.Fatal(err)\n\t}\n\th.flattenBindings()\n\th.matcher = fzf.NewFuzzyMatcher(fzf.DEFAULT_SCHEME)\n\th.updateFilter()\n\n\tif len(h.all_items) != 0 {\n\t\tt.Fatalf(\"Expected 0 items for empty data, got %d\", len(h.all_items))\n\t}\n\tif len(h.filtered_idx) != 0 {\n\t\tt.Fatalf(\"Expected 0 filtered items, got %d\", len(h.filtered_idx))\n\t}\n\tif b := h.selectedBinding(); b != nil {\n\t\tt.Fatal(\"Expected nil binding for empty data\")\n\t}\n}\n\nfunc TestFallbackOrderingWithoutExplicitOrder(t *testing.T) {\n\t// Test that the kitten handles missing mode_order/category_order gracefully\n\th := &Handler{}\n\tnoOrderJSON := `{\n\t\t\"modes\": {\n\t\t\t\"\": {\n\t\t\t\t\"Scrolling\": [{\"key\": \"up\", \"action\": \"scroll\", \"action_display\": \"scroll\", \"help\": \"\", \"long_help\": \"\"}],\n\t\t\t\t\"Copy/paste\": [{\"key\": \"c\", \"action\": \"copy\", \"action_display\": \"copy\", \"help\": \"\", \"long_help\": \"\"}]\n\t\t\t}\n\t\t},\n\t\t\"mouse\": []\n\t}`\n\tif err := json.Unmarshal([]byte(noOrderJSON), &h.input_data); err != nil {\n\t\tt.Fatal(err)\n\t}\n\th.flattenBindings()\n\n\tif len(h.all_items) != 2 {\n\t\tt.Fatalf(\"Expected 2 items, got %d\", len(h.all_items))\n\t}\n\t// Without explicit order, categories should be sorted alphabetically\n\tcat0 := h.all_items[0].binding.Category\n\tcat1 := h.all_items[1].binding.Category\n\tif cat0 > cat1 {\n\t\tt.Fatalf(\"Expected alphabetical category order, got %q then %q\", cat0, cat1)\n\t}\n}\n\nfunc TestTruncateToWidth(t *testing.T) {\n\t// Short string: no truncation\n\ts := \"hello\"\n\tgot := truncateToWidth(s, 10)\n\tif got != s {\n\t\tt.Fatalf(\"Expected %q unchanged, got %q\", s, got)\n\t}\n\n\t// Exact width: no truncation\n\tgot = truncateToWidth(\"hello\", 5)\n\tif got != \"hello\" {\n\t\tt.Fatalf(\"Expected %q unchanged at exact width, got %q\", \"hello\", got)\n\t}\n\n\t// Over width: truncated with ellipsis\n\tgot = truncateToWidth(\"hello world\", 8)\n\tif !strings.HasSuffix(got, \"...\") {\n\t\tt.Fatalf(\"Expected truncated string to end with '...', got %q\", got)\n\t}\n\tif len([]rune(got)) > 8 {\n\t\tt.Fatalf(\"Expected truncated string to be at most 8 runes, got %d in %q\", len([]rune(got)), got)\n\t}\n\n\t// Long key like a mouse binding should be truncated\n\tlongKey := \"ctrl+shift+left press ungrabbed\"\n\tgot = truncateToWidth(longKey, maxKeyDisplayWidth)\n\tif len([]rune(got)) > maxKeyDisplayWidth {\n\t\tt.Fatalf(\"Key should be truncated to maxKeyDisplayWidth, got len=%d: %q\", len([]rune(got)), got)\n\t}\n\tif !strings.HasSuffix(got, \"...\") {\n\t\tt.Fatalf(\"Truncated key should end with '...', got %q\", got)\n\t}\n}\n\nfunc TestGroupedResultsModeHeaderFormat(t *testing.T) {\n\th := newTestHandler()\n\th.updateFilter()\n\n\tconst testWidth = 80 // fixed width for testing\n\n\t// Build lines as drawGroupedResults would with the new separator format\n\tvar lines []displayLine\n\tlastMode := \"\"\n\tlastCategory := \"\"\n\tfor fi, idx := range h.filtered_idx {\n\t\tb := &h.all_items[idx].binding\n\t\tif b.Mode != lastMode {\n\t\t\tlastMode = b.Mode\n\t\t\tlastCategory = \"\"\n\t\t\tif b.Mode != \"\" {\n\t\t\t\tif len(lines) > 0 {\n\t\t\t\t\tlines = append(lines, displayLine{itemIdx: -1, isHeader: true})\n\t\t\t\t}\n\t\t\t\tlabel := \"Keyboard mode: \" + b.Mode\n\t\t\t\tlabelWidth := len([]rune(label))\n\t\t\t\tsepLen := max(0, testWidth-labelWidth-6)\n\t\t\t\tsep := strings.Repeat(\"\\u2500\", sepLen)\n\t\t\t\tlines = append(lines, displayLine{\n\t\t\t\t\ttext:      fmt.Sprintf(\"  \\u2500\\u2500 %s %s\", label, sep),\n\t\t\t\t\tisModeHdr: true, isHeader: true, itemIdx: -1,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif b.Mode == \"\" && b.Category != lastCategory {\n\t\t\tlastCategory = b.Category\n\t\t\tlines = append(lines, displayLine{isHeader: true, itemIdx: -1})\n\t\t}\n\t\tlines = append(lines, displayLine{itemIdx: fi})\n\t}\n\n\t// There should be a mode header for the \"mw\" mode\n\tfound := false\n\tfor _, l := range lines {\n\t\tif l.isModeHdr && strings.Contains(l.text, \"Keyboard mode: mw\") {\n\t\t\tfound = true\n\t\t\t// Header should have ── separator characters\n\t\t\tif !strings.Contains(l.text, \"\\u2500\\u2500\") {\n\t\t\t\tt.Fatalf(\"Mode header should contain separator ── but got %q\", l.text)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\tif !found {\n\t\tt.Fatal(\"Expected to find 'Keyboard mode: mw' mode header\")\n\t}\n}\n\nfunc TestGroupedResultsNoCategoryHeadersForNonDefaultMode(t *testing.T) {\n\th := newTestHandler()\n\th.updateFilter()\n\n\t// Build lines as drawGroupedResults would, tracking whether we are currently\n\t// inside a non-default keyboard-mode section.  Category separators are only\n\t// valid for the default mode (\"\")  and for the mouse-actions block; they must\n\t// NOT appear while we are still processing items for a non-default mode (e.g.\n\t// \"mw\").  Once we transition back to Mode==\"\" (e.g. for mouse bindings) the\n\t// section is over and category headers are allowed again.\n\tvar lines []displayLine\n\tlastMode := \"\"\n\tlastCategory := \"\"\n\tfor fi, idx := range h.filtered_idx {\n\t\tb := &h.all_items[idx].binding\n\t\tif b.Mode != lastMode {\n\t\t\tlastMode = b.Mode\n\t\t\tlastCategory = \"\"\n\t\t\tif b.Mode != \"\" {\n\t\t\t\tif len(lines) > 0 {\n\t\t\t\t\tlines = append(lines, displayLine{itemIdx: -1, isHeader: true})\n\t\t\t\t}\n\t\t\t\tlines = append(lines, displayLine{\n\t\t\t\t\ttext:      fmt.Sprintf(\"  Keyboard mode: %s\", b.Mode),\n\t\t\t\t\tisModeHdr: true, isHeader: true, itemIdx: -1,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\t// Category headers are only emitted for the default-mode block.\n\t\tif b.Mode == \"\" && b.Category != lastCategory {\n\t\t\tlastCategory = b.Category\n\t\t\tlines = append(lines, displayLine{\n\t\t\t\ttext: \"category header\", isHeader: true, itemIdx: -1,\n\t\t\t})\n\t\t}\n\n\t\tlines = append(lines, displayLine{itemIdx: fi})\n\t}\n\n\t// Verify: no \"category header\" line appears while we are still inside the\n\t// non-default keyboard-mode section.\n\tnonDefaultActive := false\n\tfor _, l := range lines {\n\t\tif l.isModeHdr {\n\t\t\tnonDefaultActive = true\n\t\t\tcontinue\n\t\t}\n\t\t// A non-header item from Mode==\"\" exits the non-default section.\n\t\tif nonDefaultActive && !l.isHeader {\n\t\t\tif l.itemIdx >= 0 && l.itemIdx < len(h.filtered_idx) {\n\t\t\t\tidx := h.filtered_idx[l.itemIdx]\n\t\t\t\tif h.all_items[idx].binding.Mode == \"\" {\n\t\t\t\t\tnonDefaultActive = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestRowToFilteredIdx(t *testing.T) {\n\th := newTestHandler()\n\th.updateFilter()\n\th.results_start_y = 2\n\th.results_height = 20\n\n\t// Populate display_lines with known structure\n\th.display_lines = []displayLine{\n\t\t{isHeader: true, itemIdx: -1}, // line 0: category header\n\t\t{itemIdx: 0},                  // line 1: first item (filteredIdx=0)\n\t\t{itemIdx: 1},                  // line 2: second item (filteredIdx=1)\n\t\t{isHeader: true, itemIdx: -1}, // line 3: blank header\n\t\t{itemIdx: 2},                  // line 4: third item (filteredIdx=2)\n\t}\n\th.scroll_offset = 0\n\n\t// cellY=1 → screenRow=2 = results_start_y → lineIdx=0 = header → -1\n\tif fi := h.rowToFilteredIdx(1); fi != -1 {\n\t\tt.Fatalf(\"Expected -1 for header row, got %d\", fi)\n\t}\n\n\t// cellY=2 → screenRow=3 → lineIdx=1 = first item → filteredIdx=0\n\tif fi := h.rowToFilteredIdx(2); fi != 0 {\n\t\tt.Fatalf(\"Expected filteredIdx=0 for first item row, got %d\", fi)\n\t}\n\n\t// cellY=3 → screenRow=4 → lineIdx=2 = second item → filteredIdx=1\n\tif fi := h.rowToFilteredIdx(3); fi != 1 {\n\t\tt.Fatalf(\"Expected filteredIdx=1 for second item row, got %d\", fi)\n\t}\n\n\t// cellY=4 → screenRow=5 → lineIdx=3 = blank header → -1\n\tif fi := h.rowToFilteredIdx(4); fi != -1 {\n\t\tt.Fatalf(\"Expected -1 for blank header row, got %d\", fi)\n\t}\n\n\t// Click above results area (cellY=0 → screenRow=1 < results_start_y=2): should return -1\n\tif fi := h.rowToFilteredIdx(0); fi != -1 {\n\t\tt.Fatalf(\"Expected -1 for row above results, got %d\", fi)\n\t}\n\n\t// Click below results area (cellY=22 → screenRow=23 >= results_start_y+results_height=22): should return -1\n\tif fi := h.rowToFilteredIdx(22); fi != -1 {\n\t\tt.Fatalf(\"Expected -1 for row below results, got %d\", fi)\n\t}\n}\n\nfunc TestScrollAdjustRevealsSectionHeader(t *testing.T) {\n\t// When the selected item is scrolled into view from below,\n\t// any immediately preceding header lines should also be visible.\n\tlines := []displayLine{\n\t\t{isHeader: true, itemIdx: -1}, // line 0: category header\n\t\t{itemIdx: 0},                  // line 1: first item\n\t\t{itemIdx: 1},                  // line 2: second item\n\t\t{isHeader: true, itemIdx: -1}, // line 3: blank\n\t\t{isHeader: true, itemIdx: -1}, // line 4: category header 2\n\t\t{itemIdx: 2},                  // line 5: third item\n\t}\n\n\th := &Handler{}\n\th.filtered_idx = []int{0, 1, 2}\n\th.selected_idx = 0  // first item (at line 1)\n\th.scroll_offset = 4 // currently scrolled past the first item\n\n\t// Call the scroll adjustment logic from drawLines\n\tselectedLineIdx := -1\n\tfor i, dl := range lines {\n\t\tif dl.itemIdx == h.selected_idx {\n\t\t\tselectedLineIdx = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif selectedLineIdx != 1 {\n\t\tt.Fatalf(\"Expected selectedLineIdx=1, got %d\", selectedLineIdx)\n\t}\n\n\tmaxRows := 10\n\tif selectedLineIdx < h.scroll_offset {\n\t\th.scroll_offset = selectedLineIdx\n\t\tfor h.scroll_offset > 0 && lines[h.scroll_offset-1].isHeader {\n\t\t\th.scroll_offset--\n\t\t}\n\t}\n\tif selectedLineIdx >= h.scroll_offset+maxRows {\n\t\th.scroll_offset = selectedLineIdx - maxRows + 1\n\t}\n\th.scroll_offset = max(0, h.scroll_offset)\n\th.scroll_offset = min(h.scroll_offset, max(0, len(lines)-maxRows))\n\n\t// scroll_offset should be 0 so the category header at line 0 is visible\n\tif h.scroll_offset != 0 {\n\t\tt.Fatalf(\"Expected scroll_offset=0 to show category header, got %d\", h.scroll_offset)\n\t}\n}\n\nfunc TestColTextsPopulated(t *testing.T) {\n\th := newTestHandler()\n\tfor i, item := range h.all_items {\n\t\tif item.binding.IsMouse {\n\t\t\tcontinue\n\t\t}\n\t\texpectedKey := item.binding.Key\n\t\tif expectedKey == \"\" {\n\t\t\texpectedKey = unmappedLabel\n\t\t}\n\t\tif item.colTexts[0] != expectedKey {\n\t\t\tt.Fatalf(\"Item %d: colTexts[0]=%q expected %q\", i, item.colTexts[0], expectedKey)\n\t\t}\n\t\tif item.colTexts[1] != item.binding.ActionDisplay {\n\t\t\tt.Fatalf(\"Item %d: colTexts[1]=%q expected %q\", i, item.colTexts[1], item.binding.ActionDisplay)\n\t\t}\n\t\tif item.colTexts[2] != item.binding.Category {\n\t\t\tt.Fatalf(\"Item %d: colTexts[2]=%q expected %q\", i, item.colTexts[2], item.binding.Category)\n\t\t}\n\t}\n}\n\nfunc TestFilterSingleColumnMatch(t *testing.T) {\n\t// \"scroll\" is in action_display column only, not in key or category.\n\t// With per-column matching it should still match the action column.\n\th := newTestHandler()\n\th.query = \"scroll\"\n\th.updateFilter()\n\tif len(h.filtered_idx) == 0 {\n\t\tt.Fatal(\"Expected matches for 'scroll' against action column\")\n\t}\n\t// All matched items should have 'scroll' in exactly one column, not spread across columns\n\tfor _, idx := range h.filtered_idx {\n\t\titem := &h.all_items[idx]\n\t\tcolMatch := false\n\t\tfor _, col := range item.colTexts {\n\t\t\tif strings.Contains(strings.ToLower(col), \"scroll\") {\n\t\t\t\tcolMatch = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\t_ = colMatch // FZF does fuzzy matching; at least the characters should appear in one column\n\t}\n}\n\nfunc TestFilterMatchInfosParallelToFilteredIdx(t *testing.T) {\n\th := newTestHandler()\n\th.query = \"clipboard\"\n\th.updateFilter()\n\tif len(h.filtered_idx) == 0 {\n\t\tt.Fatal(\"Expected some matches\")\n\t}\n\tif len(h.match_infos) != len(h.filtered_idx) {\n\t\tt.Fatalf(\"match_infos length %d != filtered_idx length %d\", len(h.match_infos), len(h.filtered_idx))\n\t}\n\tfor i, mi := range h.match_infos {\n\t\tif mi.colIdx < 0 || mi.colIdx > 2 {\n\t\t\tt.Fatalf(\"match_infos[%d].colIdx=%d out of range [0,2]\", i, mi.colIdx)\n\t\t}\n\t}\n}\n\nfunc TestFilterMatchInfosNilWhenNoQuery(t *testing.T) {\n\th := newTestHandler()\n\th.query = \"\"\n\th.updateFilter()\n\tif h.match_infos != nil {\n\t\tt.Fatal(\"Expected match_infos to be nil when query is empty\")\n\t}\n}\n\nfunc TestUnmappedActionDisplayed(t *testing.T) {\n\t// Inject an item with an empty key (unmapped action) and verify display\n\th := &Handler{}\n\tunmappedJSON := `{\n\t\t\"modes\": {\n\t\t\t\"\": {\n\t\t\t\t\"Miscellaneous\": [\n\t\t\t\t\t{\"key\": \"ctrl+n\", \"action\": \"new_window\", \"action_display\": \"new_window\", \"definition\": \"new_window\", \"help\": \"Open new window\", \"long_help\": \"\"},\n\t\t\t\t\t{\"key\": \"\", \"action\": \"scroll_home\", \"action_display\": \"scroll_home\", \"definition\": \"scroll_home\", \"help\": \"Scroll to top\", \"long_help\": \"\"}\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"mouse\": [],\n\t\t\"mode_order\": [\"\"],\n\t\t\"category_order\": {\"\": [\"Miscellaneous\"]}\n\t}`\n\tif err := json.Unmarshal([]byte(unmappedJSON), &h.input_data); err != nil {\n\t\tt.Fatal(err)\n\t}\n\th.flattenBindings()\n\th.matcher = fzf.NewFuzzyMatcher(fzf.DEFAULT_SCHEME)\n\n\tif len(h.all_items) != 2 {\n\t\tt.Fatalf(\"Expected 2 items, got %d\", len(h.all_items))\n\t}\n\t// Find the unmapped item\n\tvar unmapped *DisplayItem\n\tfor i := range h.all_items {\n\t\tif h.all_items[i].binding.Key == \"\" {\n\t\t\tunmapped = &h.all_items[i]\n\t\t\tbreak\n\t\t}\n\t}\n\tif unmapped == nil {\n\t\tt.Fatal(\"Expected to find unmapped item\")\n\t}\n\t// colTexts[0] should be unmappedLabel, not empty\n\tif unmapped.colTexts[0] != unmappedLabel {\n\t\tt.Fatalf(\"Expected colTexts[0]=%q for unmapped item, got %q\", unmappedLabel, unmapped.colTexts[0])\n\t}\n\n\t// With show_unmapped=true, unmapped action should be searchable\n\th.show_unmapped = true\n\th.query = \"scroll_home\"\n\th.updateFilter()\n\tif len(h.filtered_idx) == 0 {\n\t\tt.Fatal(\"Expected unmapped action to be found by action name search when show_unmapped=true\")\n\t}\n\n\t// With show_unmapped=false, unmapped action should be hidden\n\th.show_unmapped = false\n\th.query = \"\"\n\th.updateFilter()\n\tfor _, idx := range h.filtered_idx {\n\t\tif h.all_items[idx].binding.Key == \"\" {\n\t\t\tt.Fatal(\"Expected unmapped action to be hidden when show_unmapped=false\")\n\t\t}\n\t}\n}\n\nfunc TestShowUnmappedToggle(t *testing.T) {\n\t// TestShowUnmappedToggle creates a handler with both mapped and unmapped items\n\t// and verifies that the show_unmapped flag correctly filters the display.\n\th := &Handler{}\n\tmixedJSON := `{\n\t\t\"modes\": {\n\t\t\t\"\": {\n\t\t\t\t\"Copy/paste\": [\n\t\t\t\t\t{\"key\": \"ctrl+c\", \"action\": \"copy\", \"action_display\": \"copy\", \"definition\": \"copy\", \"help\": \"Copy\", \"long_help\": \"\"},\n\t\t\t\t\t{\"key\": \"\", \"action\": \"paste_from_buffer\", \"action_display\": \"paste_from_buffer\", \"definition\": \"paste_from_buffer\", \"help\": \"Paste from buffer\", \"long_help\": \"\"}\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"mouse\": [],\n\t\t\"mode_order\": [\"\"],\n\t\t\"category_order\": {\"\": [\"Copy/paste\"]}\n\t}`\n\tif err := json.Unmarshal([]byte(mixedJSON), &h.input_data); err != nil {\n\t\tt.Fatal(err)\n\t}\n\th.flattenBindings()\n\th.matcher = fzf.NewFuzzyMatcher(fzf.DEFAULT_SCHEME)\n\n\tif len(h.all_items) != 2 {\n\t\tt.Fatalf(\"Expected 2 items in all_items, got %d\", len(h.all_items))\n\t}\n\n\t// With show_unmapped=false, only mapped items should appear\n\th.show_unmapped = false\n\th.updateFilter()\n\tif len(h.filtered_idx) != 1 {\n\t\tt.Fatalf(\"With show_unmapped=false, expected 1 item, got %d\", len(h.filtered_idx))\n\t}\n\tif h.all_items[h.filtered_idx[0]].binding.Key == \"\" {\n\t\tt.Fatal(\"Filtered item should not be unmapped when show_unmapped=false\")\n\t}\n\n\t// With show_unmapped=true, both items should appear\n\th.show_unmapped = true\n\th.updateFilter()\n\tif len(h.filtered_idx) != 2 {\n\t\tt.Fatalf(\"With show_unmapped=true, expected 2 items, got %d\", len(h.filtered_idx))\n\t}\n\n\t// Toggle back to false with a query active; unmapped should still be hidden\n\th.show_unmapped = false\n\th.query = \"paste\"\n\th.updateFilter()\n\tfor _, idx := range h.filtered_idx {\n\t\tif h.all_items[idx].binding.Key == \"\" {\n\t\t\tt.Fatal(\"Unmapped item should not appear in search results when show_unmapped=false\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "kittens/desktop_ui/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/desktop_ui/main.go",
    "content": "package desktop_ui\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/dbus\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype ServerOptions struct {\n\tConfig   []string\n\tOverride []string\n}\n\nconst server_conf_name = \"desktop-ui-portal\"\n\nfunc load_server_config(opts *ServerOptions) (ans *Config, err error) {\n\tans = NewConfig()\n\tp := config.ConfigParser{LineHandler: ans.Parse}\n\terr = p.LoadConfig(server_conf_name+\".conf\", opts.Config, opts.Override)\n\treturn\n}\n\nfunc run_server(opts *ServerOptions) (err error) {\n\tconfig, err := load_server_config(opts)\n\tif err == nil {\n\t\tportal, err := NewPortal(config, opts)\n\t\tif err == nil {\n\t\t\terr = portal.Start()\n\t\t\tif err == nil {\n\t\t\t\tdefer portal.Cleanup()\n\t\t\t}\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\tc := make(chan string)\n\t<-c\n\treturn\n}\n\nfunc specialize_command(parent *cli.Command) {\n\tparent.Run = func(cmd *cli.Command, args []string) (int, error) {\n\t\tcmd.ShowHelp()\n\t\treturn 1, nil\n\t}\n\tparent.ShortDescription = \"Implement various desktop components for use with lightweight compositors/window managers on Linux\"\n\n\trs := parent.AddSubCommand(&cli.Command{\n\t\tName:             \"run-server\",\n\t\tShortDescription: \"Start the various servers used to integrate with the Linux desktop\",\n\t\tHelpText:         \"This should be run very early in the startup sequence of your window manager, before any other programs are run.\",\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\topts := ServerOptions{}\n\t\t\terr = cmd.GetOptionValues(&opts)\n\t\t\tif err == nil {\n\t\t\t\terr = run_server(&opts)\n\t\t\t}\n\t\t\treturn utils.IfElse(err == nil, 0, 1), err\n\t\t},\n\t})\n\trs.Add(cli.OptionSpec{\n\t\tName: `--override -o`, Type: \"list\", Dest: `Override`,\n\t\tHelp: \"Override individual configuration options, can be specified multiple times. Syntax: :italic:`name=value`. For example: :italic:`-o color_scheme=dark`\",\n\t})\n\trs.Add(cli.OptionSpec{\n\t\tName: `--config -c`, Type: \"list\", Dest: `Config`,\n\t\tHelp: strings.ReplaceAll(strings.ReplaceAll(kitty.ConfigHelp, \"{appname}\", \"kitty\"), \"{conf_name}\", server_conf_name),\n\t})\n\n\tparent.AddSubCommand(&cli.Command{\n\t\tName:             \"enable-portal\",\n\t\tShortDescription: \"This will create or edit the various files needed so that the portal from this kitten is used by xdg-desktop-portal\",\n\t\tHelpText:         \"Once you run this command, add :code:`kitten desktop-ui run-server` to your window manager startup sequence and reboot your computer (or logout and restart your session) and hopefully xdg-desktop-portal should now delegate to kitty for the portals implemented here. If it doesn't try running :code:`/usr/lib/xdg-desktop-portal -r -v` it will provide a lot of logging about why it is choosing different portal backends. That combined with a careful reading of :code:`man portals.conf` should be enough to learn how to convince xdg-desktop-portal to use kitty.\\n\\nYou can change the system color-scheme dynamically by running::\\n\\n:code:`kitten desktop-ui set-color-scheme dark`\",\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\terr = enable_portal()\n\t\t\treturn utils.IfElse(err == nil, 0, 1), err\n\t\t},\n\t})\n\tparent.AddSubCommand(&cli.Command{\n\t\tName:             \"set-color-scheme\",\n\t\tShortDescription: \"Change the color scheme\",\n\t\tArgCompleter:     cli.NamesCompleter(\"Choices for color-scheme\", \"no-preference\", \"light\", \"dark\", \"toggle\"),\n\t\tUsage:            \" light|dark|no-preference|toggle\",\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\tif len(args) != 1 {\n\t\t\t\tcmd.ShowHelp()\n\t\t\t\treturn 1, fmt.Errorf(\"must specify the new color scheme value\")\n\t\t\t}\n\t\t\terr = set_color_scheme(args[0])\n\t\t\treturn utils.IfElse(err == nil, 0, 1), err\n\t\t},\n\t})\n\tparent.AddSubCommand(&cli.Command{\n\t\tName:             \"set-accent-color\",\n\t\tShortDescription: \"Change the accent color\",\n\t\tUsage:            \" color_as_hex_or_name\",\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\tif len(args) != 1 {\n\t\t\t\tcmd.ShowHelp()\n\t\t\t\treturn 1, fmt.Errorf(\"must specify the new accent color value\")\n\t\t\t}\n\t\t\tvar v dbus.Variant\n\t\t\tif v, err = to_color(args[0]); err == nil {\n\t\t\t\terr = set_variant_setting(PORTAL_APPEARANCE_NAMESPACE, PORTAL_ACCENT_COLOR_KEY, v, false)\n\t\t\t}\n\t\t\treturn utils.IfElse(err == nil, 0, 1), err\n\t\t},\n\t})\n\tparent.AddSubCommand(&cli.Command{\n\t\tName:             \"set-contrast\",\n\t\tShortDescription: \"Change the contrast. Can be high or normal.\",\n\t\tUsage:            \" high|normal\",\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\tif len(args) != 1 {\n\t\t\t\tcmd.ShowHelp()\n\t\t\t\treturn 1, fmt.Errorf(\"must specify the new contrast value\")\n\t\t\t}\n\n\t\t\tvar v dbus.Variant\n\t\t\tswitch args[0] {\n\t\t\tcase \"normal\":\n\t\t\t\tv = dbus.MakeVariant(uint32(0))\n\t\t\tcase \"high\":\n\t\t\t\tv = dbus.MakeVariant(uint32(1))\n\t\t\tdefault:\n\t\t\t\treturn 1, fmt.Errorf(\"%s is not a valid contrast value\", args[0])\n\t\t\t}\n\t\t\terr = set_variant_setting(PORTAL_APPEARANCE_NAMESPACE, PORTAL_CONTRAST_KEY, v, false)\n\t\t\treturn utils.IfElse(err == nil, 0, 1), err\n\t\t},\n\t})\n\tst := parent.AddSubCommand(&cli.Command{\n\t\tName:             \"set-setting\",\n\t\tShortDescription: \"Change an arbitrary setting\",\n\t\tUsage:            \" key [value]\",\n\t\tHelpText:         \"Set an arbitrary setting. If you want to set the color-scheme use the dedicated command for it. Use this command with care as it does no validation for the type of value. The syntax for specifying values is described at: :link:`the glib docs <https://docs.gtk.org/glib/gvariant-text-format.html>`. Leaving out the value or specifying an empty value, will delete the setting.\",\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\tval := \"\"\n\t\t\tif len(args) < 1 {\n\t\t\t\tcmd.ShowHelp()\n\t\t\t\treturn 1, fmt.Errorf(\"must specify the key\")\n\t\t\t}\n\t\t\tif len(args) > 1 {\n\t\t\t\tval = args[1]\n\t\t\t}\n\t\t\topts := SetOptions{}\n\t\t\tif err = cmd.GetOptionValues(&opts); err == nil {\n\t\t\t\terr = set_setting(args[0], val, &opts)\n\t\t\t}\n\t\t\treturn utils.IfElse(err == nil, 0, 1), err\n\t\t},\n\t})\n\tst.Add(cli.OptionSpec{\n\t\tName:    \"--namespace -n\",\n\t\tHelp:    \"The namespace in which to change the setting.\",\n\t\tDefault: PORTAL_APPEARANCE_NAMESPACE,\n\t})\n\tst.Add(cli.OptionSpec{\n\t\tName: \"--data-type\",\n\t\tHelp: \"The DBUS data type signature of the value. The default is to guess from the textual representation, see :link:`the glib docs <https://docs.gtk.org/glib/gvariant-text-format.html>` for details.\",\n\t})\n\n\tss := parent.AddSubCommand(&cli.Command{\n\t\tName:             \"show-settings\",\n\t\tShortDescription: \"Print the current values of the desktop settings\",\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\tif len(args) != 0 {\n\t\t\t\tcmd.ShowHelp()\n\t\t\t\treturn 1, fmt.Errorf(\"no arguments allowed\")\n\t\t\t}\n\t\t\topts := ShowSettingsOptions{}\n\t\t\terr = cmd.GetOptionValues(&opts)\n\t\t\tif err == nil {\n\t\t\t\terr = show_settings(&opts)\n\t\t\t}\n\t\t\treturn utils.IfElse(err == nil, 0, 1), err\n\t\t},\n\t})\n\tss.Add(cli.OptionSpec{\n\t\tName: \"--as-json\",\n\t\tHelp: \"Show the settings as JSON for machine consumption\",\n\t\tType: \"bool-set\",\n\t})\n\tss.Add(cli.OptionSpec{\n\t\tName: \"--in-namespace\",\n\t\tHelp: \"Show only settings in the specified names. Can be specified multiple times. When unspecified all namespaces are returned.\",\n\t\tType: \"list\",\n\t})\n\tss.Add(cli.OptionSpec{\n\t\tName: \"--allow-other-backends\",\n\t\tHelp: \"Normally, after printing the settings, if the settings did not come from the desktop-ui kitten the command prints an error and exits. This prevents that.\",\n\t\tType: \"bool-set\",\n\t})\n}\n\nfunc EntryPoint(root *cli.Command) {\n\tcreate_cmd(root, nil)\n}\n"
  },
  {
    "path": "kittens/desktop_ui/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport sys\n\nfrom kitty.conf.types import Definition\n\ndefinition = Definition(\n    '!kittens.choose_files',\n)\n\nagr = definition.add_group\negr = definition.end_group\nopt = definition.add_option\nmap = definition.add_map\nmma = definition.add_mouse_map\n\nagr('Appearance')\nopt('color_scheme', 'no-preference', choices=('no-preference', 'dark', 'light'), long_text='''\\\nThe color scheme for your system. This sets the initial value of the color scheme. It can be changed subsequently\nby using :code:`kitten desktop-ui color-scheme`.\n''')\nopt('accent_color', 'cyan', long_text='The RGB accent color for your system, can be specified as a color name or in hex a decimal format.')\nopt('contrast', 'normal', choices=('normal', 'high'), long_text='The preferred contrast level.')\nopt('file_chooser_size', '', long_text='''\nThe size in lines and columns of the file chooser popup window. By default it is full screen. For example:\n:code:`file_chooser_size 25 80` will cause the popup to be of size 25 lines and 80 columns. Note that if you\nuse this option, depending on the compositor you are running, the popup window may not be properly modal.\n''')\nopt('+file_chooser_kitty_conf', '',\n    long_text='Path to config file to use for kitty when drawing the file chooser window. Can be specified multiple times. By default, the'\n    ' normal kitty.conf is used. Relative paths are resolved with respect to the kitty config directory.'\n)\nopt('+file_chooser_kitty_override', '', long_text='Override individual kitty configuration options, for the file chooser window.'\n    ' Can be specified multiple times. Syntax: :italic:`name=value`. For example: :code:`font_size=20`.'\n)\n\negr()\n\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('This must be run as kitten desktop-ui')\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__conf__':\n    sys.options_definition = definition  # type: ignore\n"
  },
  {
    "path": "kittens/desktop_ui/portal.go",
    "content": "package desktop_ui\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"maps\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/dbus\"\n\t\"github.com/kovidgoyal/dbus/introspect\"\n\t\"github.com/kovidgoyal/dbus/prop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nconst PORTAL_APPEARANCE_NAMESPACE = \"org.freedesktop.appearance\"\nconst PORTAL_COLOR_SCHEME_KEY = \"color-scheme\"\nconst PORTAL_ACCENT_COLOR_KEY = \"accent-color\"\nconst PORTAL_CONTRAST_KEY = \"contrast\"\nconst PORTAL_BUS_NAME = \"org.freedesktop.impl.portal.desktop.kitty\"\nconst DESKTOP_OBJECT_PATH = \"/org/freedesktop/portal/desktop\"\nconst SETTINGS_INTERFACE = \"org.freedesktop.impl.portal.Settings\"\nconst FILE_CHOOSER_INTERFACE = \"org.freedesktop.impl.portal.FileChooser\"\nconst KITTY_OBJECT_PATH = \"/net/kovidgoyal/kitty/portal\"\nconst CHANGE_SETTINGS_INTERFACE = \"net.kovidgoyal.kitty.settings\"\nconst DESKTOP_PORTAL_NAME = \"org.freedesktop.portal.Desktop\"\nconst REQUEST_INTERFACE = \"org.freedesktop.impl.portal.Request\"\n\n// Special portal setting used to check if we are being called by xdg-desktop-portal\nconst SETTINGS_CANARY_NAMESPACE = \"net.kovidgoyal.kitty\"\nconst SETTINGS_CANARY_KEY = \"status\"\n\ntype ColorScheme uint32\n\nconst (\n\tNO_PREFERENCE ColorScheme = iota\n\tDARK\n\tLIGHT\n)\nconst (\n\tRESPONSE_SUCCESS uint32 = iota\n\tRESPONSE_CANCELED\n\tRESPONSE_ENDED\n)\n\ntype SettingsMap map[string]map[string]dbus.Variant\n\ntype Portal struct {\n\tbus                         *dbus.Conn\n\tsettings                    SettingsMap\n\tlock                        sync.Mutex\n\topts                        *Config\n\tserver_options              *ServerOptions\n\tfile_chooser_first_instance *exec.Cmd\n}\n\nfunc to_color(spec string) (v dbus.Variant, err error) {\n\tif col, err := style.ParseColor(spec); err == nil {\n\t\treturn dbus.MakeVariant([]float64{float64(col.Red) / 255., float64(col.Green) / 255., float64(col.Blue) / 255.}), nil\n\t}\n\treturn\n}\n\nfunc NewPortal(opts *Config, server_options *ServerOptions) (p *Portal, err error) {\n\tans := Portal{opts: opts, server_options: server_options}\n\tans.settings = SettingsMap{\n\t\tSETTINGS_CANARY_NAMESPACE: map[string]dbus.Variant{\n\t\t\tSETTINGS_CANARY_KEY: dbus.MakeVariant(\"running\"),\n\t\t},\n\t}\n\tans.settings[PORTAL_APPEARANCE_NAMESPACE] = map[string]dbus.Variant{}\n\tswitch opts.Color_scheme {\n\tcase Color_scheme_dark:\n\t\tans.settings[PORTAL_APPEARANCE_NAMESPACE][PORTAL_COLOR_SCHEME_KEY] = dbus.MakeVariant(uint32(DARK))\n\tcase Color_scheme_light:\n\t\tans.settings[PORTAL_APPEARANCE_NAMESPACE][PORTAL_COLOR_SCHEME_KEY] = dbus.MakeVariant(uint32(LIGHT))\n\tdefault:\n\t\tans.settings[PORTAL_APPEARANCE_NAMESPACE][PORTAL_COLOR_SCHEME_KEY] = dbus.MakeVariant(uint32(NO_PREFERENCE))\n\t}\n\tans.settings[PORTAL_APPEARANCE_NAMESPACE][PORTAL_ACCENT_COLOR_KEY], err = to_color(opts.Accent_color)\n\tvar contrast uint32\n\tif opts.Contrast == Contrast_high {\n\t\tcontrast = 1\n\t}\n\tans.settings[PORTAL_APPEARANCE_NAMESPACE][PORTAL_CONTRAST_KEY] = dbus.MakeVariant(contrast)\n\treturn &ans, nil\n}\n\ntype PropSpec map[string]*prop.Prop\ntype SignalSpec map[string][]struct {\n\tName, Type string\n}\ntype MethodSpec map[string][]struct {\n\tName, Type string\n\tOut        bool\n}\n\nfunc ExportInterface(conn *dbus.Conn, object any, interface_name, object_path string, method_spec MethodSpec, prop_spec PropSpec, signal_spec SignalSpec) (err error) {\n\top := dbus.ObjectPath(object_path)\n\tmethod_map := make(map[string]string, len(method_spec))\n\tmethods := []introspect.Method{}\n\tif len(method_spec) > 0 {\n\t\tfor method_name, args := range method_spec {\n\t\t\tmethod_map[method_name] = method_name\n\t\t\tmeth_args := make([]introspect.Arg, len(args))\n\t\t\tfor i, a := range args {\n\t\t\t\tmeth_args[i] = introspect.Arg{\n\t\t\t\t\tName:      a.Name,\n\t\t\t\t\tType:      a.Type,\n\t\t\t\t\tDirection: utils.IfElse(a.Out, \"out\", \"in\"),\n\t\t\t\t}\n\t\t\t}\n\t\t\tmethods = append(methods, introspect.Method{\n\t\t\t\tName: method_name,\n\t\t\t\tArgs: meth_args,\n\t\t\t})\n\t\t}\n\t}\n\tif err = conn.ExportWithMap(object, method_map, op, interface_name); err != nil {\n\t\treturn fmt.Errorf(\"failed to export interface: %s at object path: %s with error: %w\", interface_name, object_path, err)\n\t}\n\tvar properties []introspect.Property\n\tp := prop.Map{interface_name: prop_spec}\n\tif len(prop_spec) > 0 {\n\t\tif props, err := prop.Export(conn, op, p); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to export properties with error: %w\", err)\n\t\t} else {\n\t\t\tproperties = props.Introspection(interface_name)\n\t\t}\n\t}\n\tvar signals []introspect.Signal\n\tif len(signal_spec) > 0 {\n\t\tfor signal_name, args := range signal_spec {\n\t\t\tsig_args := make([]introspect.Arg, len(args))\n\t\t\tfor i, a := range args {\n\t\t\t\tsig_args[i] = introspect.Arg{\n\t\t\t\t\tName:      a.Name,\n\t\t\t\t\tType:      a.Type,\n\t\t\t\t\tDirection: \"out\",\n\t\t\t\t}\n\t\t\t}\n\t\t\tsignals = append(signals, introspect.Signal{\n\t\t\t\tName: signal_name,\n\t\t\t\tArgs: sig_args,\n\t\t\t})\n\t\t}\n\t}\n\n\tinterface_data := introspect.Interface{\n\t\tName:       interface_name,\n\t\tMethods:    methods,\n\t\tProperties: properties,\n\t\tSignals:    signals,\n\t}\n\tinterfaces := []introspect.Interface{\n\t\tintrospect.IntrospectData, interface_data,\n\t}\n\tif len(properties) > 0 {\n\t\tinterfaces = append(interfaces, prop.IntrospectData)\n\t}\n\tn := &introspect.Node{Name: object_path, Interfaces: interfaces}\n\tif err = conn.Export(introspect.NewIntrospectable(n), op, introspect.IntrospectData.Name); err != nil {\n\t\treturn fmt.Errorf(\"failed to export introspected methods with error: %w\", err)\n\t}\n\treturn\n}\n\nfunc (self *Portal) Start() (err error) {\n\tif self.bus, err = dbus.SessionBus(); err != nil {\n\t\treturn fmt.Errorf(\"could not connect to session D-Bus: %s\", err)\n\t}\n\treply, err := self.bus.RequestName(PORTAL_BUS_NAME, dbus.NameFlagDoNotQueue)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to register dbus name: %v\", err)\n\t}\n\tif reply != dbus.RequestNameReplyPrimaryOwner {\n\t\treturn fmt.Errorf(\"can't register D-Bus name: name already taken\")\n\t}\n\tprops := PropSpec{\n\t\t\"version\": {Value: uint32(1), Writable: false, Emit: prop.EmitFalse},\n\t}\n\tsignals := SignalSpec{\n\t\t\"SettingChanged\": {{\"namespace\", \"s\"}, {\"key\", \"s\"}, {\"value\", \"v\"}},\n\t}\n\tmethods := MethodSpec{\n\t\t\"Read\":    {{\"namespace\", \"s\", false}, {\"key\", \"s\", false}, {\"value\", \"v\", true}},\n\t\t\"ReadAll\": {{\"namespaces\", \"as\", false}, {\"value\", \"a{sa{sv}}\", true}},\n\t}\n\tif err = ExportInterface(self.bus, self, SETTINGS_INTERFACE, DESKTOP_OBJECT_PATH, methods, props, signals); err != nil {\n\t\treturn\n\t}\n\tmethods = MethodSpec{\n\t\t\"OpenFile\": {{\"handle\", \"o\", false}, {\"app_id\", \"s\", false}, {\"parent_window\", \"s\", false}, {\"title\", \"s\", false}, {\"options\", \"a{sv}\", false},\n\t\t\t{\"response\", \"u\", true}, {\"results\", \"a{sv}\", false},\n\t\t},\n\t\t\"SaveFile\": {{\"handle\", \"o\", false}, {\"app_id\", \"s\", false}, {\"parent_window\", \"s\", false}, {\"title\", \"s\", false}, {\"options\", \"a{sv}\", false},\n\t\t\t{\"response\", \"u\", true}, {\"results\", \"a{sv}\", false},\n\t\t},\n\t}\n\tif err = ExportInterface(self.bus, self, FILE_CHOOSER_INTERFACE, DESKTOP_OBJECT_PATH, methods, nil, nil); err != nil {\n\t\treturn\n\t}\n\tmethods = MethodSpec{\n\t\t\"ChangeSetting\": {{\"namespace\", \"s\", false}, {\"key\", \"s\", false}, {\"value\", \"v\", false}},\n\t\t\"RemoveSetting\": {{\"namespace\", \"s\", false}, {\"key\", \"s\", false}},\n\t}\n\tprops[\"version\"].Value = uint32(1)\n\tif err = ExportInterface(self.bus, self, CHANGE_SETTINGS_INTERFACE, KITTY_OBJECT_PATH, methods, props, nil); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ParseValueWithSignature(value, value_type_signature string) (v dbus.Variant, err error) {\n\tvar s dbus.Signature\n\tif value_type_signature != \"\" {\n\t\tif value_type_signature[0] == '@' {\n\t\t\tvalue_type_signature = value_type_signature[1:]\n\t\t}\n\t\ts, err = dbus.ParseSignature(value_type_signature)\n\t\tif err != nil {\n\t\t\treturn dbus.Variant{}, fmt.Errorf(\"%s is not a valid type signature: %w\", value_type_signature, err)\n\t\t}\n\t}\n\tv, err = dbus.ParseVariant(value, s)\n\tif err != nil {\n\t\tif value_type_signature == \"\" {\n\t\t\treturn dbus.Variant{}, fmt.Errorf(\"could not guess the data type of: %s with error: %w\", value, err)\n\t\t}\n\t\treturn dbus.Variant{}, fmt.Errorf(\"%s is not a valid value for signature: %#v with error: %w\", value, value_type_signature, err)\n\t}\n\treturn v, nil\n}\n\nfunc ParseValue(value string) (dbus.Variant, error) {\n\treturn ParseValueWithSignature(value, \"\")\n}\n\ntype ShowSettingsOptions struct {\n\tAsJson             bool\n\tAllowOtherBackends bool\n\tInNamespace        []string\n}\n\nfunc fetch_settings(conn *dbus.Conn, namespaces ...string) (ans ReadAllType, err error) {\n\tpath := \"/\" + strings.ToLower(strings.ReplaceAll(DESKTOP_PORTAL_NAME, \".\", \"/\"))\n\tobj := conn.Object(DESKTOP_PORTAL_NAME, dbus.ObjectPath(path))\n\tinterface_name := strings.ReplaceAll(DESKTOP_PORTAL_NAME, \"Desktop\", \"Settings\")\n\tif len(namespaces) == 0 {\n\t\tnamespaces = append(namespaces, \"\")\n\t}\n\tcall := obj.Call(interface_name+\".ReadAll\", dbus.FlagNoAutoStart, namespaces)\n\tif err = call.Store(&ans); err != nil {\n\t\treturn nil, fmt.Errorf(\"Failed to read response from ReadAll with error: %w\", err)\n\t}\n\treturn\n}\n\nfunc show_settings(opts *ShowSettingsOptions) (err error) {\n\tconn, err := dbus.SessionBus()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to system bus with error: %w\", err)\n\t}\n\tdefer conn.Close()\n\tvar response ReadAllType\n\tresponse, err = fetch_settings(conn, opts.InNamespace...)\n\tif opts.AsJson {\n\t\tunwrapped := make(map[string]map[string]any, len(response))\n\t\tfor ns, m := range response {\n\t\t\tw := make(map[string]any, len(m))\n\t\t\tfor k, a := range m {\n\t\t\t\tw[k] = a.Value()\n\t\t\t}\n\t\t\tunwrapped[ns] = w\n\t\t}\n\t\tj, err := json.MarshalIndent(unwrapped, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to format the response as JSON: %w\", err)\n\t\t}\n\t\tfmt.Println(string(j))\n\t} else {\n\t\tfor ns, m := range response {\n\t\t\tfmt.Println(ns + \":\")\n\t\t\tfor key, v := range m {\n\t\t\t\tfmt.Printf(\"\\t%s: %s\\n\", key, v)\n\t\t\t}\n\t\t}\n\t}\n\tif !opts.AllowOtherBackends {\n\t\tis_running_self := false\n\t\tif m, found := response[SETTINGS_CANARY_NAMESPACE]; found {\n\t\t\t_, is_running_self = m[SETTINGS_CANARY_KEY]\n\t\t}\n\t\tif !is_running_self {\n\t\t\terr = fmt.Errorf(\"the settings did not come from the desktop-ui kitten. Some other portal backend is providing the service.\")\n\t\t}\n\t}\n\treturn\n}\n\nvar DataDirs = sync.OnceValue(func() (ans []string) {\n\t// $XDG_DATA_DIRS defines the preference-ordered set of base directories\n\t// to search for data files **in addition to the $XDG_DATA_HOME** base\n\t// directory. The directories in $XDG_DATA_DIRS should be separated with\n\t// a colon ':'.\n\t// https://specifications.freedesktop.org/basedir-spec/0.8/#variables\n\n\tdata_dirs := os.Getenv(\"XDG_DATA_DIRS\")\n\tif data_dirs == \"\" {\n\t\tdata_dirs = \"/usr/local/share:/usr/share\"\n\t}\n\tdata_home := os.Getenv(\"XDG_DATA_HOME\")\n\tif data_home == \"\" {\n\t\tdata_home = utils.Expanduser(\"~/.local/share\")\n\t}\n\treturn utils.Uniq(append([]string{data_home}, strings.Split(data_dirs, \":\")...))\n})\n\nfunc IsDir(x string) bool {\n\ts, err := os.Stat(x)\n\treturn err == nil && s.IsDir()\n}\n\nvar WritableDataDirs = sync.OnceValue(func() (ans []string) {\n\tfor _, x := range DataDirs() {\n\t\tif err := os.MkdirAll(x, 0o755); err == nil && unix.Access(x, unix.W_OK) == nil {\n\t\t\tans = append(ans, x)\n\t\t}\n\t}\n\treturn\n})\n\nvar AllPortalInterfaces = sync.OnceValue(func() (ans []string) {\n\treturn []string{SETTINGS_INTERFACE, FILE_CHOOSER_INTERFACE}\n})\n\n// enable-portal {{{\nfunc patch_portals_conf(text []byte) []byte {\n\tlines := []string{}\n\tin_preferred := false\n\tpatched := false\n\tfor _, line := range utils.Splitlines(utils.UnsafeBytesToString(text)) {\n\t\tsl := strings.TrimSpace(line)\n\t\tif strings.HasPrefix(sl, \"[\") {\n\t\t\tin_preferred = sl == \"[preferred]\"\n\t\t\tlines = append(lines, line)\n\t\t\tfor _, iface := range AllPortalInterfaces() {\n\t\t\t\tlines = append(lines, iface+\"=kitty;*\")\n\t\t\t}\n\t\t\tpatched = true\n\t\t} else if in_preferred {\n\t\t\tremove := false\n\t\t\tfor _, iface := range AllPortalInterfaces() {\n\t\t\t\tif strings.HasPrefix(sl, iface) {\n\t\t\t\t\tremove = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !remove {\n\t\t\t\tlines = append(lines, line)\n\t\t\t}\n\t\t}\n\t}\n\n\tif !patched {\n\t\t// the file was empty or did not contain a section\n\t\tlines = append(lines, \"[preferred]\")\n\t\tfor _, iface := range AllPortalInterfaces() {\n\t\t\tlines = append(lines, iface+\"=kitty;*\")\n\t\t}\n\t}\n\n\treturn utils.UnsafeStringToBytes(strings.Join(lines, \"\\n\"))\n}\n\nfunc enable_portal() (err error) {\n\tif len(WritableDataDirs()) == 0 {\n\t\treturn fmt.Errorf(\"Could not find any writable data directories. Make sure XDG_DATA_DIRS is set and contains at least one directory for which you have write permission\")\n\t}\n\tportals_dir := \"\"\n\tfor _, x := range WritableDataDirs() {\n\t\t// Find-or-create the first available xdg-desktop-portals/portals directory\n\t\tq := filepath.Join(x, \"xdg-desktop-portal\", \"portals\")\n\t\tif (unix.Access(q, unix.W_OK) == nil && IsDir(q)) || (os.MkdirAll(q, 0o755) == nil) {\n\t\t\tportals_dir = q\n\t\t\tbreak\n\t\t}\n\t}\n\tif portals_dir == \"\" {\n\t\treturn fmt.Errorf(\"Could not find any writable portals directories. Make sure XDG_DATA_HOME is set and point to a directory for which you have write permission.\")\n\t}\n\tportals_defn := filepath.Join(portals_dir, \"kitty.portal\")\n\tif err = os.WriteFile(portals_defn, utils.UnsafeStringToBytes(fmt.Sprintf(\n\t\t`[portal]\nDBusName=%s\nInterfaces=%s;\n`, PORTAL_BUS_NAME, strings.Join(AllPortalInterfaces(), \";\"))), 0o644); err != nil {\n\t\treturn err\n\t}\n\tfmt.Println(\"Wrote kitty portal definition to:\", portals_defn)\n\tdbus_service_dir := \"\"\n\tfor _, x := range WritableDataDirs() {\n\t\tq := filepath.Join(x, \"dbus-1\", \"services\")\n\t\tif err := os.MkdirAll(q, 0o755); err == nil {\n\t\t\tdbus_service_dir = q\n\t\t\tbreak\n\t\t}\n\t}\n\tif dbus_service_dir == \"\" {\n\t\treturn fmt.Errorf(\"Could not find any writable portals directories. Make sure XDG_DATA_HOME is set and point to a directory for which you have write permission.\")\n\t}\n\tdbus_service_defn := filepath.Join(dbus_service_dir, PORTAL_BUS_NAME+\".service\")\n\texe_path, eerr := os.Executable()\n\tif eerr != nil {\n\t\texe_path = utils.Which(\"kitten\")\n\t}\n\tif exe_path, err = filepath.Abs(exe_path); eerr != nil {\n\t\treturn fmt.Errorf(\"failed to get path to kitten executable with error: %w\", err)\n\t}\n\tif err = os.WriteFile(dbus_service_defn, utils.UnsafeStringToBytes(fmt.Sprintf(\n\t\t`[D-BUS Service]\nName=%s\nExec=%s desktop-ui run-server\n`, PORTAL_BUS_NAME, exe_path)), 0o644); err != nil {\n\t\treturn err\n\t}\n\tfmt.Println(\"Wrote kitty DBUS activation service file to:\", dbus_service_defn)\n\n\td := os.Getenv(\"XDG_CURRENT_DESKTOP\")\n\tcf := os.Getenv(\"XDG_CONFIG_HOME\")\n\tif cf == \"\" {\n\t\tcf = utils.Expanduser(\"~/.config\")\n\t}\n\tcf = filepath.Join(cf, \"xdg-desktop-portal\")\n\tif err = os.MkdirAll(cf, 0o755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create %s to store the portals.conf file with error: %w\", cf, err)\n\t}\n\tpatched_file := \"\"\n\tdesktops := utils.Filter(strings.Split(d, \":\"), func(x string) bool { return x != \"\" })\n\tdesktops = append(desktops, \"\")\n\tfor x := range strings.SplitSeq(d, \":\") {\n\t\tq := filepath.Join(cf, utils.IfElse(x == \"\", \"portals.conf\", fmt.Sprintf(\"%s-portals.conf\", strings.ToLower(x))))\n\t\tif text, err := os.ReadFile(q); err == nil {\n\t\t\ttext := patch_portals_conf(text)\n\t\t\tif err = os.WriteFile(q, text, 0o644); err == nil {\n\t\t\t\tpatched_file = q\n\t\t\t\tfmt.Printf(\"Patched %s to use the kitty portals\\n\", patched_file)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif patched_file == \"\" {\n\t\tx := desktops[0]\n\t\tq := filepath.Join(cf, utils.IfElse(x == \"\", \"portals.conf\", fmt.Sprintf(\"%s-portals.conf\", strings.ToLower(x))))\n\t\ttext := patch_portals_conf([]byte{})\n\t\tif err = os.WriteFile(q, text, 0o644); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpatched_file = q\n\t\tfmt.Printf(\"Created %s to use the kitty portals\\n\", patched_file)\n\t}\n\treturn\n}\n\n// }}}\n\ntype SetOptions struct {\n\tNamespace, DataType string\n}\n\nfunc set_variant_setting(namespace, key string, v dbus.Variant, remove_setting bool) (err error) {\n\tconn, err := dbus.SessionBus()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to system bus with error: %w\", err)\n\t}\n\tdefer conn.Close()\n\tmethod := \"ChangeSetting\"\n\tvar vals = []any{namespace, key}\n\tif remove_setting {\n\t\tmethod = \"RemoveSetting\"\n\t} else {\n\t\tvals = append(vals, v)\n\t}\n\tobj := conn.Object(PORTAL_BUS_NAME, dbus.ObjectPath(KITTY_OBJECT_PATH))\n\tcall := obj.Call(CHANGE_SETTINGS_INTERFACE+\".\"+method, dbus.FlagNoAutoStart, vals...)\n\tif err = call.Store(); err != nil {\n\t\treturn fmt.Errorf(\"failed to call %s with error: %w\", method, err)\n\t}\n\treturn\n}\n\nfunc set_setting(key, value string, opts *SetOptions) (err error) {\n\tremove_setting := false\n\tvar v dbus.Variant\n\tif value == \"\" {\n\t\tremove_setting = true\n\t} else {\n\t\tif v, err = ParseValueWithSignature(value, opts.DataType); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn set_variant_setting(opts.Namespace, key, v, remove_setting)\n}\n\nfunc set_color_scheme(which string) (err error) {\n\tconn, err := dbus.SessionBus()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to system bus with error: %w\", err)\n\t}\n\tdefer conn.Close()\n\tval := NO_PREFERENCE\n\tvar res ReadAllType\n\tif res, err = fetch_settings(conn, PORTAL_APPEARANCE_NAMESPACE); err != nil {\n\t\treturn fmt.Errorf(\"failed to read existing color scheme setting with error: %w\", err)\n\t}\n\tif m, found := res[PORTAL_APPEARANCE_NAMESPACE]; found {\n\t\tif v, found := m[PORTAL_COLOR_SCHEME_KEY]; found {\n\t\t\tv.Store(&val)\n\t\t}\n\t}\n\tnval := val\n\tswitch which {\n\tcase \"toggle\":\n\t\tswitch val {\n\t\tcase LIGHT:\n\t\t\tnval = DARK\n\t\tcase DARK:\n\t\t\tnval = LIGHT\n\t\t}\n\tcase \"no-preference\":\n\t\tnval = NO_PREFERENCE\n\tcase \"light\":\n\t\tnval = LIGHT\n\tcase \"dark\":\n\t\tnval = DARK\n\tdefault:\n\t\treturn fmt.Errorf(\"%s is not a valid value of the color-scheme\", which)\n\t}\n\tif val == nval {\n\t\treturn\n\t}\n\tobj := conn.Object(PORTAL_BUS_NAME, dbus.ObjectPath(KITTY_OBJECT_PATH))\n\tcall := obj.Call(CHANGE_SETTINGS_INTERFACE+\".ChangeSetting\", dbus.FlagNoAutoStart, PORTAL_APPEARANCE_NAMESPACE, PORTAL_COLOR_SCHEME_KEY, dbus.MakeVariant(nval))\n\tif err = call.Store(); err != nil {\n\t\treturn fmt.Errorf(\"failed to call ChangeSetting with error: %w\", err)\n\t}\n\treturn\n}\n\nfunc (self *Portal) ChangeSetting(namespace, key string, value dbus.Variant) *dbus.Error {\n\tself.lock.Lock()\n\tdefer self.lock.Unlock()\n\tif self.settings[namespace] == nil {\n\t\tself.settings[namespace] = map[string]dbus.Variant{}\n\t}\n\tself.settings[namespace][key] = value\n\n\tif e := self.bus.Emit(\n\t\tDESKTOP_OBJECT_PATH,\n\t\tSETTINGS_INTERFACE+\".SettingChanged\",\n\t\tnamespace,\n\t\tkey,\n\t\tvalue,\n\t); e != nil {\n\t\tlog.Println(\"Couldn't emit signal:\", e)\n\t}\n\treturn nil\n}\n\nfunc (self *Portal) RemoveSetting(namespace, key string) *dbus.Error {\n\tself.lock.Lock()\n\tdefer self.lock.Unlock()\n\texisted := false\n\tif m := self.settings[namespace]; m != nil {\n\t\t_, existed = m[key]\n\t}\n\tif !existed {\n\t\treturn nil\n\t}\n\tdelete(self.settings[namespace], key)\n\treturn nil\n}\n\nfunc (self *Portal) Read(namespace, key string) (dbus.Variant, *dbus.Error) {\n\tself.lock.Lock()\n\tdefer self.lock.Unlock()\n\tif m, found := self.settings[namespace]; found {\n\t\tif v, found := m[key]; found {\n\t\t\treturn v, nil\n\t\t}\n\t}\n\treturn dbus.Variant{}, dbus.NewError(\"org.freedesktop.portal.Error.NotFound\", []any{fmt.Sprintf(\"the setting %s in the namespace %s is not supported\", key, namespace)})\n}\n\ntype ReadAllType map[string]map[string]dbus.Variant\n\nfunc (self *Portal) ReadAll(namespaces []string) (ReadAllType, *dbus.Error) {\n\tself.lock.Lock()\n\tdefer self.lock.Unlock()\n\tvar matched_namespaces = SettingsMap{}\n\tif len(namespaces) == 0 {\n\t\tmatched_namespaces = self.settings\n\t} else {\n\t\tfor _, namespace := range namespaces {\n\t\t\tif namespace == \"\" {\n\t\t\t\tmatched_namespaces = self.settings\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\tif strings.HasSuffix(namespace, \".*\") {\n\t\t\t\t\tnamespace = namespace[:len(namespace)-1]\n\t\t\t\t\tfor candidate := range self.settings {\n\t\t\t\t\t\tif strings.HasPrefix(candidate, namespace) {\n\t\t\t\t\t\t\tmatched_namespaces[candidate] = map[string]dbus.Variant{}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if _, found := self.settings[namespace]; found {\n\t\t\t\t\tmatched_namespaces[namespace] = map[string]dbus.Variant{}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tvalues := map[string]map[string]dbus.Variant{}\n\tfor namespace := range matched_namespaces {\n\t\tvalues[namespace] = make(map[string]dbus.Variant, len(self.settings[namespace]))\n\t\tmaps.Copy(values[namespace], self.settings[namespace])\n\t}\n\treturn values, nil\n}\n\nfunc (self *Portal) reload_portal_settings() {\n\tself.lock.Lock()\n\tdefer self.lock.Unlock()\n\tif config, err := load_server_config(self.server_options); err == nil {\n\t\tself.opts = config\n\t}\n}\n\ntype vmap map[string]dbus.Variant\ntype Filter_expression struct {\n\tFtype uint32\n\tVal   string\n}\ntype Filter struct {\n\tName        string\n\tExpressions []Filter_expression\n}\n\nfunc (f Filter) Equal(o Filter) bool {\n\treturn f.Name == o.Name && slices.Equal(f.Expressions, o.Expressions)\n}\n\ntype ChooseFilesData struct {\n\tTitle                                        string\n\tMode                                         string\n\tCwd                                          string\n\tSuggestedSaveFileName, SuggestedSaveFilePath string\n\tHandle                                       dbus.ObjectPath\n\tFilters                                      []Filter\n}\n\nfunc (c *ChooseFilesData) set_filters(options vmap) {\n\tif v, found := options[\"filters\"]; found {\n\t\tv.Store(&c.Filters)\n\t}\n\tif v, found := options[\"current_filter\"]; found {\n\t\tvar x Filter\n\t\tif err := v.Store(&x); err == nil {\n\t\t\tidx := slices.IndexFunc(c.Filters, func(q Filter) bool { return x.Equal(q) })\n\t\t\tif idx > -1 {\n\t\t\t\tc.Filters = slices.Delete(c.Filters, idx, idx+1)\n\t\t\t}\n\t\t\tc.Filters = slices.Insert(c.Filters, 0, x)\n\t\t}\n\t}\n}\n\nfunc get_matching_filter(name string, all_filters []Filter) (dbus.Variant, bool) {\n\tfor _, x := range all_filters {\n\t\tif x.Name == name {\n\t\t\treturn dbus.MakeVariant(x), true\n\t\t}\n\t}\n\treturn dbus.Variant{}, false\n}\n\nfunc (options vmap) get_bool(name string, defval bool) (ans bool) {\n\tif v, found := options[name]; found {\n\t\tif v.Store(&ans) == nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn defval\n}\n\nfunc (self *Portal) Cleanup() {\n\tself.lock.Lock()\n\tdefer self.lock.Unlock()\n\tif self.file_chooser_first_instance != nil {\n\t\tself.file_chooser_first_instance.Process.Signal(unix.SIGTERM)\n\t\tch := make(chan int)\n\t\tgo func() {\n\t\t\tself.file_chooser_first_instance.Wait()\n\t\t\tch <- 0\n\t\t}()\n\t\tselect {\n\t\tcase <-ch:\n\t\tcase <-time.After(time.Second):\n\t\t\tself.file_chooser_first_instance.Process.Kill()\n\t\t\tself.file_chooser_first_instance.Wait()\n\t\t}\n\t\tself.file_chooser_first_instance = nil\n\t}\n}\n\ntype ChooserResponse struct {\n\tPaths          []string `json:\"paths\"`\n\tError          string   `json:\"error\"`\n\tInterrupted    bool     `json:\"interrupted\"`\n\tCurrent_filter string   `json:\"current_filter\"`\n}\n\nfunc (self *Portal) run_file_chooser(cfd ChooseFilesData) (response uint32, result_dict vmap) {\n\tself.reload_portal_settings()\n\tresponse = RESPONSE_ENDED\n\ttdir, err := os.MkdirTemp(\"\", \"kitty-cfd\")\n\tif err != nil {\n\t\tlog.Println(\"cannot run file chooser as failed to create a temporary directory with error: \", err)\n\t\treturn\n\t}\n\tpid_path := filepath.Join(tdir, \"pid\")\n\tvar close_requested, child_killed atomic.Bool\n\tClose := func() *dbus.Error {\n\t\tclose_requested.Store(true)\n\t\tif !child_killed.Load() {\n\t\t\tif raw, err := os.ReadFile(pid_path); err == nil {\n\t\t\t\tif pid, err := strconv.Atoi(string(raw)); err == nil {\n\t\t\t\t\tchild_killed.Store(true)\n\t\t\t\t\tunix.Kill(pid, unix.SIGTERM)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tself.bus.ExportMethodTable(map[string]any{\"Close\": Close}, cfd.Handle, REQUEST_INTERFACE)\n\tdefer func() {\n\t\tself.bus.ExportMethodTable(nil, cfd.Handle, REQUEST_INTERFACE)\n\t\t_ = os.RemoveAll(tdir)\n\t}()\n\toutput_path := filepath.Join(tdir, \"output.json\")\n\n\tcmd := func() *exec.Cmd {\n\t\tself.lock.Lock()\n\t\tdefer self.lock.Unlock()\n\t\tedge, lines, columns := `center`, ``, ``\n\t\tif self.opts.File_chooser_size != \"\" {\n\t\t\tl, c, _ := strings.Cut(strings.TrimSpace(self.opts.File_chooser_size), \" \")\n\t\t\tl, c = strings.TrimSpace(l), strings.TrimSpace(c)\n\t\t\tif li, err := strconv.Atoi(l); err == nil {\n\t\t\t\tif ci, err := strconv.Atoi(c); err == nil {\n\t\t\t\t\tif li < 10 || ci < 40 {\n\t\t\t\t\t\tlog.Printf(\"file chooser size %s too small, ignoring\", self.opts.File_chooser_size)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tedge, lines, columns = `center-sized`, l, c\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tlog.Printf(\"file chooser size %s invalid with error: %s\\n\", self.opts.File_chooser_size, err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlog.Printf(\"file chooser size %s invalid with error: %s\\n\", self.opts.File_chooser_size, err)\n\t\t\t}\n\t\t}\n\t\targs := []string{\n\t\t\t\"+kitten\", \"panel\", \"--layer=overlay\", \"--edge=\" + edge, \"--focus-policy=exclusive\",\n\t\t\t\"-o\", \"background_opacity=0.85\", \"--wait-for-single-instance-window-close\",\n\t\t\t\"--grab-keyboard\", \"--single-instance\", \"--instance-group\", \"cfp-\" + strconv.Itoa(os.Getpid()),\n\t\t}\n\t\tif edge == \"center-sized\" {\n\t\t\targs = append(args, \"--lines=\"+lines, \"--columns=\"+columns)\n\t\t}\n\t\tfor _, x := range self.opts.File_chooser_kitty_conf {\n\t\t\targs = append(args, `-c`, x)\n\t\t}\n\t\tfor _, x := range self.opts.File_chooser_kitty_override {\n\t\t\targs = append(args, `-o`, x)\n\t\t}\n\t\tif self.file_chooser_first_instance == nil {\n\t\t\tfifo_path := filepath.Join(tdir, \"fifo\")\n\t\t\tif err := unix.Mkfifo(fifo_path, 0600); err != nil {\n\t\t\t\tlog.Println(\"cannot run file chooser as failed to create a fifo directory with error: \", err)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tfa := slices.Clone(args)\n\t\t\tfa = append(fa, \"--start-as-hidden\", \"sh\", \"-c\", \"echo a > '\"+fifo_path+\"'; read\")\n\t\t\tcmd := exec.Command(utils.KittyExe(), fa...)\n\t\t\tcmd.Stdout = os.Stdout\n\t\t\tcmd.Stderr = os.Stderr\n\t\t\tcmd.Start()\n\t\t\tch := make(chan int)\n\t\t\tgo func() {\n\t\t\t\tf, err := os.OpenFile(fifo_path, os.O_RDONLY, os.ModeNamedPipe)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Println(\"cannot run file chooser as failed to open fifo for read with error: \", err)\n\t\t\t\t}\n\t\t\t\tb := []byte{'a', 'b', 'c', 'd'}\n\t\t\t\tf.Read(b)\n\t\t\t\tch <- 0\n\t\t\t}()\n\t\t\tselect {\n\t\t\tcase <-ch:\n\t\t\t\tself.file_chooser_first_instance = cmd\n\t\t\tcase <-time.After(5 * time.Second):\n\t\t\t\tlog.Println(\"cannot run file chooser as panel script timed out writing to fifo\")\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\targs = append(args, \"kitten\", `choose-files`, `--mode`, cfd.Mode, `--write-output-to`, output_path, `--output-format=json`, `--display-title`)\n\t\tif cfd.SuggestedSaveFileName != \"\" {\n\t\t\targs = append(args, `--suggested-save-file-name`, cfd.SuggestedSaveFileName)\n\t\t}\n\t\tif cfd.SuggestedSaveFilePath != \"\" {\n\t\t\targs = append(args, `--suggested-save-file-path`, cfd.SuggestedSaveFilePath)\n\t\t}\n\t\tif cfd.Title != \"\" {\n\t\t\targs = append(args, \"--title\", cfd.Title)\n\t\t}\n\t\tfor _, fs := range cfd.Filters {\n\t\t\tfor _, exp := range fs.Expressions {\n\t\t\t\targs = append(args, \"--file-filter\", fmt.Sprintf(\"%s:%s:%s\", utils.IfElse(exp.Ftype == 0, \"glob\", \"mime\"), exp.Val, fs.Name))\n\t\t\t}\n\t\t}\n\t\targs = append(args, \"--write-pid-to\", pid_path)\n\t\targs = append(args, utils.IfElse(cfd.Cwd == \"\", \"~\", cfd.Cwd))\n\t\tcmd := exec.Command(utils.KittyExe(), args...)\n\t\tcmd.Stdout = os.Stdout\n\t\tcmd.Stderr = os.Stderr\n\t\treturn cmd\n\t}()\n\tif cmd == nil || close_requested.Load() {\n\t\treturn\n\t}\n\tlog.Println(\"running file chooser with args:\", cmd.Path, utils.Repr(cmd.Args))\n\tif err := cmd.Run(); err != nil {\n\t\tlog.Println(\"running file chooser failed with error: \", err)\n\t\treturn\n\t}\n\tif close_requested.Load() {\n\t\treturn\n\t}\n\traw, err := os.ReadFile(output_path)\n\tif err != nil {\n\t\tlog.Println(\"running file chooser failed, could not read from output file with error: \", err)\n\t\treturn\n\t}\n\tif close_requested.Load() {\n\t\treturn\n\t}\n\tvar result ChooserResponse\n\tif err = json.Unmarshal(raw, &result); err != nil {\n\t\tlog.Println(\"running file chooser failed, invalid JSON response with error: \", err)\n\t\treturn\n\t}\n\tif result.Error != \"\" {\n\t\tlog.Println(\"running file chooser failed, with error: \", result.Error)\n\t\treturn\n\t}\n\tif result.Interrupted {\n\t\tresponse = RESPONSE_CANCELED\n\t\tlog.Println(\"running file chooser failed, interrupted by user.\")\n\t\treturn\n\t}\n\tresponse = RESPONSE_SUCCESS\n\tprefix := \"file://\" + utils.IfElse(runtime.GOOS == \"windows\", \"/\", \"\")\n\turis := utils.Map(func(path string) string {\n\t\tpath = filepath.ToSlash(path)\n\t\tu := url.URL{Path: path}\n\t\treturn prefix + u.EscapedPath()\n\t}, result.Paths)\n\tresult_dict = vmap{\"uris\": dbus.MakeVariant(uris)}\n\tif result.Current_filter != \"\" {\n\t\tif v, found := get_matching_filter(result.Current_filter, cfd.Filters); found {\n\t\t\tresult_dict[\"current_filter\"] = v\n\t\t}\n\t}\n\treturn\n}\n\nfunc (options vmap) get_bytearray(name string) string {\n\tif v, found := options[name]; found {\n\t\tvar b []byte\n\t\tif v.Store(&b) == nil {\n\t\t\t// the FileChooser spec requires paths and filenames to be null\n\t\t\t// terminated, so remove trailing nulls.\n\t\t\treturn string(bytes.TrimRight(b, \"\\x00\"))\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc (self *Portal) OpenFile(handle dbus.ObjectPath, app_id string, parent_window string, title string, options vmap) (uint32, vmap, *dbus.Error) {\n\tcfd := ChooseFilesData{Title: title, Cwd: options.get_bytearray(\"current_folder\"), Handle: handle}\n\tcfd.set_filters(options)\n\tdir_only := options.get_bool(\"directory\", false)\n\tmultiple := options.get_bool(\"multiple\", false)\n\tif dir_only {\n\t\tcfd.Mode = utils.IfElse(multiple, \"dirs\", \"dir\")\n\t} else {\n\t\tcfd.Mode = utils.IfElse(multiple, \"files\", \"file\")\n\t}\n\tresponse, result := self.run_file_chooser(cfd)\n\treturn response, result, nil\n}\n\nfunc (self *Portal) SaveFile(handle dbus.ObjectPath, app_id string, parent_window string, title string, options vmap) (uint32, vmap, *dbus.Error) {\n\tcfd := ChooseFilesData{\n\t\tTitle: title, Cwd: options.get_bytearray(\"current_folder\"), Handle: handle,\n\t\tSuggestedSaveFileName: options.get_bytearray(\"current_name\"),\n\t\tSuggestedSaveFilePath: options.get_bytearray(\"current_file\")}\n\tmultiple := options.get_bool(\"multiple\", false)\n\tcfd.set_filters(options)\n\tcfd.Mode = utils.IfElse(multiple, \"save-files\", \"save-file\")\n\n\tresponse, result := self.run_file_chooser(cfd)\n\treturn response, result, nil\n}\n"
  },
  {
    "path": "kittens/diff/__init__.py",
    "content": "def syntax_aliases(x: str) -> dict[str, str]:\n    ans = {}\n    for x in x.split():\n        k, _, v = x.partition(':')\n        ans[k] = v\n    return ans\n"
  },
  {
    "path": "kittens/diff/collect.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"crypto/md5\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\nvar path_name_map, remote_dirs map[string]string\n\nvar mimetypes_cache, data_cache, hash_cache *utils.LRUCache[string, string]\nvar size_cache *utils.LRUCache[string, int64]\nvar lines_cache *utils.LRUCache[string, []string]\nvar light_highlighted_lines_cache *utils.LRUCache[string, []string]\nvar dark_highlighted_lines_cache *utils.LRUCache[string, []string]\nvar is_text_cache *utils.LRUCache[string, bool]\n\nfunc init_caches() {\n\tpath_name_map = make(map[string]string, 32)\n\tremote_dirs = make(map[string]string, 32)\n\tconst sz = 4096\n\tsize_cache = utils.NewLRUCache[string, int64](sz)\n\tmimetypes_cache = utils.NewLRUCache[string, string](sz)\n\tdata_cache = utils.NewLRUCache[string, string](sz)\n\tis_text_cache = utils.NewLRUCache[string, bool](sz)\n\tlines_cache = utils.NewLRUCache[string, []string](sz)\n\tlight_highlighted_lines_cache = utils.NewLRUCache[string, []string](sz)\n\tdark_highlighted_lines_cache = utils.NewLRUCache[string, []string](sz)\n\thash_cache = utils.NewLRUCache[string, string](sz)\n}\n\nfunc add_remote_dir(val string) {\n\tx := filepath.Base(val)\n\tidx := strings.LastIndex(x, \"-\")\n\tif idx > -1 {\n\t\tx = x[idx+1:]\n\t} else {\n\t\tx = \"\"\n\t}\n\tremote_dirs[val] = x\n}\n\nfunc mimetype_for_path(path string) string {\n\treturn mimetypes_cache.MustGetOrCreate(path, func(path string) string {\n\t\tmt := utils.GuessMimeTypeWithFileSystemAccess(path)\n\t\tif mt == \"\" {\n\t\t\tmt = \"application/octet-stream\"\n\t\t}\n\t\tif utils.KnownTextualMimes[mt] {\n\t\t\tif _, a, found := strings.Cut(mt, \"/\"); found {\n\t\t\t\tmt = \"text/\" + a\n\t\t\t}\n\t\t}\n\t\treturn mt\n\t})\n}\n\nfunc data_for_path(path string) (string, error) {\n\treturn data_cache.GetOrCreate(path, func(path string) (string, error) {\n\t\tans, err := os.ReadFile(path)\n\t\treturn utils.UnsafeBytesToString(ans), err\n\t})\n}\n\nfunc size_for_path(path string) (int64, error) {\n\treturn size_cache.GetOrCreate(path, func(path string) (int64, error) {\n\t\ts, err := os.Stat(path)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn s.Size(), nil\n\t})\n}\n\nfunc is_image(path string) bool {\n\treturn strings.HasPrefix(mimetype_for_path(path), \"image/\")\n}\n\nfunc is_path_text(path string) bool {\n\treturn is_text_cache.MustGetOrCreate(path, func(path string) bool {\n\t\tif is_image(path) {\n\t\t\treturn false\n\t\t}\n\t\ts1, err := os.Stat(path)\n\t\tif err == nil {\n\t\t\ts2, err := os.Stat(\"/dev/null\")\n\t\t\tif err == nil && os.SameFile(s1, s2) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\td, err := data_for_path(path)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treturn utf8.ValidString(d)\n\t})\n}\n\nfunc hash_for_path(path string) (string, error) {\n\treturn hash_cache.GetOrCreate(path, func(path string) (string, error) {\n\t\tans, err := data_for_path(path)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\thash := md5.Sum(utils.UnsafeStringToBytes(ans))\n\t\treturn utils.UnsafeBytesToString(hash[:]), err\n\t})\n\n}\n\nfunc text_to_lines(text string) []string {\n\tlines := make([]string, 0, 512)\n\tsplitlines_like_git(text, false, func(line string) { lines = append(lines, line) })\n\treturn lines\n}\n\nfunc sanitize(text string) string { return utils.ReplaceControlCodes(text, conf.Replace_tab_by, \"\\n\") }\n\nfunc lines_for_path(path string) ([]string, error) {\n\treturn lines_cache.GetOrCreate(path, func(path string) ([]string, error) {\n\t\tans, err := data_for_path(path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn text_to_lines(sanitize(ans)), nil\n\t})\n}\n\nfunc highlighted_lines_for_path(path string) ([]string, error) {\n\tplain_lines, err := lines_for_path(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ans []string\n\tvar found bool\n\tif use_light_colors {\n\t\tans, found = light_highlighted_lines_cache.Get(path)\n\t} else {\n\t\tans, found = dark_highlighted_lines_cache.Get(path)\n\t}\n\tif found && len(ans) == len(plain_lines) {\n\t\treturn ans, nil\n\t}\n\treturn plain_lines, nil\n}\n\ntype Collection struct {\n\tchanges, renames, type_map map[string]string\n\tadds, removes              *utils.Set[string]\n\tall_paths                  []string\n\tpaths_to_highlight         *utils.Set[string]\n\tadded_count, removed_count int\n}\n\nfunc (self *Collection) add_change(left, right string) {\n\tself.changes[left] = right\n\tself.all_paths = append(self.all_paths, left)\n\tself.paths_to_highlight.Add(left)\n\tself.paths_to_highlight.Add(right)\n\tself.type_map[left] = `diff`\n}\n\nfunc (self *Collection) add_rename(left, right string) {\n\tself.renames[left] = right\n\tself.all_paths = append(self.all_paths, left)\n\tself.type_map[left] = `rename`\n}\n\nfunc (self *Collection) add_add(right string) {\n\tself.adds.Add(right)\n\tself.all_paths = append(self.all_paths, right)\n\tself.paths_to_highlight.Add(right)\n\tself.type_map[right] = `add`\n\tif is_path_text(right) {\n\t\tnum, _ := lines_for_path(right)\n\t\tself.added_count += len(num)\n\t}\n}\n\nfunc (self *Collection) add_removal(left string) {\n\tself.removes.Add(left)\n\tself.all_paths = append(self.all_paths, left)\n\tself.paths_to_highlight.Add(left)\n\tself.type_map[left] = `removal`\n\tif is_path_text(left) {\n\t\tnum, _ := lines_for_path(left)\n\t\tself.removed_count += len(num)\n\t}\n}\n\nfunc (self *Collection) finalize() {\n\tutils.StableSortWithKey(self.all_paths, func(path string) string {\n\t\treturn path_name_map[path]\n\t})\n}\n\nfunc (self *Collection) Len() int { return len(self.all_paths) }\n\nfunc (self *Collection) Items() int { return len(self.all_paths) }\n\nfunc (self *Collection) Apply(f func(path, typ, changed_path string) error) error {\n\tfor _, path := range self.all_paths {\n\t\ttyp := self.type_map[path]\n\t\tchanged_path := \"\"\n\t\tswitch typ {\n\t\tcase \"diff\":\n\t\t\tchanged_path = self.changes[path]\n\t\tcase \"rename\":\n\t\t\tchanged_path = self.renames[path]\n\t\t}\n\t\tif err := f(path, typ, changed_path); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc allowed(path string, patterns ...string) bool {\n\tname := filepath.Base(path)\n\tfor _, pat := range patterns {\n\t\tif matched, err := filepath.Match(pat, name); err == nil && matched {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc remote_hostname(path string) (string, string) {\n\tfor q, val := range remote_dirs {\n\t\tif strings.HasPrefix(path, q) {\n\t\t\treturn q, val\n\t\t}\n\t}\n\treturn \"\", \"\"\n}\n\nfunc resolve_remote_name(path, defval string) string {\n\tremote_dir, rh := remote_hostname(path)\n\tif remote_dir != \"\" && rh != \"\" {\n\t\tr, err := filepath.Rel(remote_dir, path)\n\t\tif err == nil {\n\t\t\treturn rh + \":\" + r\n\t\t}\n\t}\n\treturn defval\n}\n\nfunc walk(base string, patterns []string, names *utils.Set[string], pmap, path_name_map map[string]string) error {\n\tbase, err := filepath.Abs(base)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn filepath.WalkDir(base, func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tis_allowed := allowed(path, patterns...)\n\t\tif !is_allowed {\n\t\t\tif d.IsDir() {\n\t\t\t\treturn fs.SkipDir\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tif d.IsDir() {\n\t\t\treturn nil\n\t\t}\n\t\tpath, err = filepath.Abs(path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tname, err := filepath.Rel(base, path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif name != \".\" {\n\t\t\tpath_name_map[path] = name\n\t\t\tnames.Add(name)\n\t\t\tpmap[name] = path\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc (self *Collection) collect_files(left, right string) error {\n\tleft_names, right_names := utils.NewSet[string](16), utils.NewSet[string](16)\n\tleft_path_map, right_path_map := make(map[string]string, 16), make(map[string]string, 16)\n\terr := walk(left, conf.Ignore_name, left_names, left_path_map, path_name_map)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = walk(right, conf.Ignore_name, right_names, right_path_map, path_name_map); err != nil {\n\t\treturn err\n\t}\n\tcommon_names := left_names.Intersect(right_names)\n\tchanged_names := utils.NewSet[string](common_names.Len())\n\tfor n := range common_names.Iterable() {\n\t\tld, err := data_for_path(left_path_map[n])\n\t\tvar rd string\n\t\tif err == nil {\n\t\t\trd, err = data_for_path(right_path_map[n])\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif ld != rd {\n\t\t\tchanged_names.Add(n)\n\t\t\tself.add_change(left_path_map[n], right_path_map[n])\n\t\t} else {\n\t\t\tif lstat, err := os.Stat(left_path_map[n]); err == nil {\n\t\t\t\tif rstat, err := os.Stat(right_path_map[n]); err == nil {\n\t\t\t\t\tif lstat.Mode() != rstat.Mode() {\n\t\t\t\t\t\t// identical files with only a mode change\n\t\t\t\t\t\tchanged_names.Add(n)\n\t\t\t\t\t\tself.add_change(left_path_map[n], right_path_map[n])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tremoved := left_names.Subtract(common_names)\n\tadded := right_names.Subtract(common_names)\n\tahash, rhash := make(map[string]string, added.Len()), make(map[string]string, removed.Len())\n\tfor a := range added.Iterable() {\n\t\tahash[a], err = hash_for_path(right_path_map[a])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor r := range removed.Iterable() {\n\t\trhash[r], err = hash_for_path(left_path_map[r])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor name, rh := range rhash {\n\t\tfound := false\n\t\tfor n, ah := range ahash {\n\t\t\tif ah == rh {\n\t\t\t\tld, _ := data_for_path(left_path_map[name])\n\t\t\t\trd, _ := data_for_path(right_path_map[n])\n\t\t\t\tif ld == rd {\n\t\t\t\t\tself.add_rename(left_path_map[name], right_path_map[n])\n\t\t\t\t\tadded.Discard(n)\n\t\t\t\t\tfound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tself.add_removal(left_path_map[name])\n\t\t}\n\t}\n\tfor name := range added.Iterable() {\n\t\tself.add_add(right_path_map[name])\n\t}\n\treturn nil\n}\n\nfunc create_collection(left, right string) (ans *Collection, err error) {\n\tans = &Collection{\n\t\tchanges:            make(map[string]string),\n\t\trenames:            make(map[string]string),\n\t\ttype_map:           make(map[string]string),\n\t\tadds:               utils.NewSet[string](32),\n\t\tremoves:            utils.NewSet[string](32),\n\t\tpaths_to_highlight: utils.NewSet[string](32),\n\t\tall_paths:          make([]string, 0, 32),\n\t}\n\tleft_stat, err := os.Stat(left)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif left_stat.IsDir() {\n\t\terr = ans.collect_files(left, right)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tpl, err := filepath.Abs(left)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpr, err := filepath.Abs(right)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpath_name_map[pl] = resolve_remote_name(pl, left)\n\t\tpath_name_map[pr] = resolve_remote_name(pr, right)\n\t\tans.add_change(pl, pr)\n\t}\n\tans.finalize()\n\treturn ans, err\n}\n"
  },
  {
    "path": "kittens/diff/collect_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestDiffCollectWalk(t *testing.T) {\n\ttdir := t.TempDir()\n\tj := func(x ...string) string { return filepath.Join(append([]string{tdir}, x...)...) }\n\t_ = os.MkdirAll(j(\"a\", \"b\"), 0o700)\n\t_ = os.WriteFile(j(\"a/b/c\"), nil, 0o600)\n\t_ = os.WriteFile(j(\"b\"), nil, 0o600)\n\t_ = os.WriteFile(j(\"d\"), nil, 0o600)\n\t_ = os.WriteFile(j(\"e\"), nil, 0o600)\n\t_ = os.WriteFile(j(\"#d#\"), nil, 0o600)\n\t_ = os.WriteFile(j(\"e~\"), nil, 0o600)\n\t_ = os.MkdirAll(j(\"f\"), 0o700)\n\t_ = os.WriteFile(j(\"f/g\"), nil, 0o600)\n\t_ = os.WriteFile(j(\"h space\"), nil, 0o600)\n\n\texpected_names := utils.NewSetWithItems(\"d\", \"e\", \"f/g\", \"h space\")\n\texpected_pmap := map[string]string{\n\t\t\"d\":       j(\"d\"),\n\t\t\"e\":       j(\"e\"),\n\t\t\"f/g\":     j(\"f/g\"),\n\t\t\"h space\": j(\"h space\"),\n\t}\n\tnames := utils.NewSet[string](16)\n\tpmap := make(map[string]string, 16)\n\tif err := walk(tdir, []string{\"*~\", \"#*#\", \"b\"}, names, pmap, map[string]string{}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif diff := cmp.Diff(\n\t\tutils.Sort(expected_names.AsSlice(), strings.Compare),\n\t\tutils.Sort(names.AsSlice(), strings.Compare),\n\t); diff != \"\" {\n\t\tt.Fatal(diff)\n\t}\n\tif diff := cmp.Diff(expected_pmap, pmap); diff != \"\" {\n\t\tt.Fatal(diff)\n\t}\n}\n"
  },
  {
    "path": "kittens/diff/diff.go",
    "content": "// Copied from the Go stdlib, with modifications.\n//https://github.com/golang/go/raw/master/src/internal/diff/diff.go\n\n// Copyright 2022 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage diff\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n)\n\n// A pair is a pair of values tracked for both the x and y side of a diff.\n// It is typically a pair of line indexes.\ntype pair struct{ x, y int }\n\n// Diff returns an anchored diff of the two texts old and new\n// in the “unified diff” format. If old and new are identical,\n// Diff returns a nil slice (no output).\n//\n// Unix diff implementations typically look for a diff with\n// the smallest number of lines inserted and removed,\n// which can in the worst case take time quadratic in the\n// number of lines in the texts. As a result, many implementations\n// either can be made to run for a long time or cut off the search\n// after a predetermined amount of work.\n//\n// In contrast, this implementation looks for a diff with the\n// smallest number of “unique” lines inserted and removed,\n// where unique means a line that appears just once in both old and new.\n// We call this an “anchored diff” because the unique lines anchor\n// the chosen matching regions. An anchored diff is usually clearer\n// than a standard diff, because the algorithm does not try to\n// reuse unrelated blank lines or closing braces.\n// The algorithm also guarantees to run in O(n log n) time\n// instead of the standard O(n²) time.\n//\n// Some systems call this approach a “patience diff,” named for\n// the “patience sorting” algorithm, itself named for a solitaire card game.\n// We avoid that name for two reasons. First, the name has been used\n// for a few different variants of the algorithm, so it is imprecise.\n// Second, the name is frequently interpreted as meaning that you have\n// to wait longer (to be patient) for the diff, meaning that it is a slower algorithm,\n// when in fact the algorithm is faster than the standard one.\nfunc Diff(oldName, old, newName, new string, num_of_context_lines int) []byte {\n\tif old == new {\n\t\treturn nil\n\t}\n\tx := lines(old)\n\ty := lines(new)\n\n\t// Print diff header.\n\tvar out bytes.Buffer\n\tfmt.Fprintf(&out, \"diff %s %s\\n\", oldName, newName)\n\tfmt.Fprintf(&out, \"--- %s\\n\", oldName)\n\tfmt.Fprintf(&out, \"+++ %s\\n\", newName)\n\n\t// Loop over matches to consider,\n\t// expanding each match to include surrounding lines,\n\t// and then printing diff chunks.\n\t// To avoid setup/teardown cases outside the loop,\n\t// tgs returns a leading {0,0} and trailing {len(x), len(y)} pair\n\t// in the sequence of matches.\n\tvar (\n\t\tdone  pair     // printed up to x[:done.x] and y[:done.y]\n\t\tchunk pair     // start lines of current chunk\n\t\tcount pair     // number of lines from each side in current chunk\n\t\tctext []string // lines for current chunk\n\t)\n\tfor _, m := range tgs(x, y) {\n\t\tif m.x < done.x {\n\t\t\t// Already handled scanning forward from earlier match.\n\t\t\tcontinue\n\t\t}\n\n\t\t// Expand matching lines as far possible,\n\t\t// establishing that x[start.x:end.x] == y[start.y:end.y].\n\t\t// Note that on the first (or last) iteration we may (or definitey do)\n\t\t// have an empty match: start.x==end.x and start.y==end.y.\n\t\tstart := m\n\t\tfor start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] {\n\t\t\tstart.x--\n\t\t\tstart.y--\n\t\t}\n\t\tend := m\n\t\tfor end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] {\n\t\t\tend.x++\n\t\t\tend.y++\n\t\t}\n\n\t\t// Emit the mismatched lines before start into this chunk.\n\t\t// (No effect on first sentinel iteration, when start = {0,0}.)\n\t\tfor _, s := range x[done.x:start.x] {\n\t\t\tctext = append(ctext, \"-\"+s)\n\t\t\tcount.x++\n\t\t}\n\t\tfor _, s := range y[done.y:start.y] {\n\t\t\tctext = append(ctext, \"+\"+s)\n\t\t\tcount.y++\n\t\t}\n\n\t\t// If we're not at EOF and have too few common lines,\n\t\t// the chunk includes all the common lines and continues.\n\t\tC := num_of_context_lines // number of context lines\n\t\tif (end.x < len(x) || end.y < len(y)) &&\n\t\t\t(end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) {\n\t\t\tfor _, s := range x[start.x:end.x] {\n\t\t\t\tctext = append(ctext, \" \"+s)\n\t\t\t\tcount.x++\n\t\t\t\tcount.y++\n\t\t\t}\n\t\t\tdone = end\n\t\t\tcontinue\n\t\t}\n\n\t\t// End chunk with common lines for context.\n\t\tif len(ctext) > 0 {\n\t\t\tn := min(end.x-start.x, C)\n\t\t\tfor _, s := range x[start.x : start.x+n] {\n\t\t\t\tctext = append(ctext, \" \"+s)\n\t\t\t\tcount.x++\n\t\t\t\tcount.y++\n\t\t\t}\n\t\t\tdone = pair{start.x + n, start.y + n}\n\n\t\t\t// Format and emit chunk.\n\t\t\t// Convert line numbers to 1-indexed.\n\t\t\t// Special case: empty file shows up as 0,0 not 1,0.\n\t\t\tif count.x > 0 {\n\t\t\t\tchunk.x++\n\t\t\t}\n\t\t\tif count.y > 0 {\n\t\t\t\tchunk.y++\n\t\t\t}\n\t\t\tfmt.Fprintf(&out, \"@@ -%d,%d +%d,%d @@\\n\", chunk.x, count.x, chunk.y, count.y)\n\t\t\tfor _, s := range ctext {\n\t\t\t\tout.WriteString(s)\n\t\t\t}\n\t\t\tcount.x = 0\n\t\t\tcount.y = 0\n\t\t\tctext = ctext[:0]\n\t\t}\n\n\t\t// If we reached EOF, we're done.\n\t\tif end.x >= len(x) && end.y >= len(y) {\n\t\t\tbreak\n\t\t}\n\n\t\t// Otherwise start a new chunk.\n\t\tchunk = pair{end.x - C, end.y - C}\n\t\tfor _, s := range x[chunk.x:end.x] {\n\t\t\tctext = append(ctext, \" \"+s)\n\t\t\tcount.x++\n\t\t\tcount.y++\n\t\t}\n\t\tdone = end\n\t}\n\n\treturn out.Bytes()\n}\n\n// lines returns the lines in the file x, including newlines.\n// If the file does not end in a newline, one is supplied\n// along with a warning about the missing newline.\nfunc lines(x string) []string {\n\tl := strings.SplitAfter(x, \"\\n\")\n\tif l[len(l)-1] == \"\" {\n\t\tl = l[:len(l)-1]\n\t} else {\n\t\t// Treat last line as having a message about the missing newline attached,\n\t\t// using the same text as BSD/GNU diff (including the leading backslash).\n\t\tl[len(l)-1] += \"\\n\\\\ No newline at end of file\\n\"\n\t}\n\treturn l\n}\n\n// tgs returns the pairs of indexes of the longest common subsequence\n// of unique lines in x and y, where a unique line is one that appears\n// once in x and once in y.\n//\n// The longest common subsequence algorithm is as described in\n// Thomas G. Szymanski, “A Special Case of the Maximal Common\n// Subsequence Problem,” Princeton TR #170 (January 1975),\n// available at https://research.swtch.com/tgs170.pdf.\nfunc tgs(x, y []string) []pair {\n\t// Count the number of times each string appears in a and b.\n\t// We only care about 0, 1, many, counted as 0, -1, -2\n\t// for the x side and 0, -4, -8 for the y side.\n\t// Using negative numbers now lets us distinguish positive line numbers later.\n\tm := make(map[string]int)\n\tfor _, s := range x {\n\t\tif c := m[s]; c > -2 {\n\t\t\tm[s] = c - 1\n\t\t}\n\t}\n\tfor _, s := range y {\n\t\tif c := m[s]; c > -8 {\n\t\t\tm[s] = c - 4\n\t\t}\n\t}\n\n\t// Now unique strings can be identified by m[s] = -1+-4.\n\t//\n\t// Gather the indexes of those strings in x and y, building:\n\t//\txi[i] = increasing indexes of unique strings in x.\n\t//\tyi[i] = increasing indexes of unique strings in y.\n\t//\tinv[i] = index j such that x[xi[i]] = y[yi[j]].\n\tvar xi, yi, inv []int\n\tfor i, s := range y {\n\t\tif m[s] == -1+-4 {\n\t\t\tm[s] = len(yi)\n\t\t\tyi = append(yi, i)\n\t\t}\n\t}\n\tfor i, s := range x {\n\t\tif j, ok := m[s]; ok && j >= 0 {\n\t\t\txi = append(xi, i)\n\t\t\tinv = append(inv, j)\n\t\t}\n\t}\n\n\t// Apply Algorithm A from Szymanski's paper.\n\t// In those terms, A = J = inv and B = [0, n).\n\t// We add sentinel pairs {0,0}, and {len(x),len(y)}\n\t// to the returned sequence, to help the processing loop.\n\tJ := inv\n\tn := len(xi)\n\tT := make([]int, n)\n\tL := make([]int, n)\n\tfor i := range T {\n\t\tT[i] = n + 1\n\t}\n\tfor i := range n {\n\t\tk := sort.Search(n, func(k int) bool {\n\t\t\treturn T[k] >= J[i]\n\t\t})\n\t\tT[k] = J[i]\n\t\tL[i] = k + 1\n\t}\n\tk := 0\n\tfor _, v := range L {\n\t\tif k < v {\n\t\t\tk = v\n\t\t}\n\t}\n\tseq := make([]pair, 2+k)\n\tseq[1+k] = pair{len(x), len(y)} // sentinel at end\n\tlastj := n\n\tfor i := n - 1; i >= 0; i-- {\n\t\tif L[i] == k && J[i] < lastj {\n\t\t\tseq[k] = pair{xi[i], yi[J[i]]}\n\t\t\tk--\n\t\t}\n\t}\n\tseq[0] = pair{0, 0} // sentinel at start\n\treturn seq\n}\n"
  },
  {
    "path": "kittens/diff/highlight.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/highlight\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nvar _ = fmt.Print\nvar _ = os.WriteFile\n\ntype prefer_light_colors bool\n\nfunc (s prefer_light_colors) StyleName() string {\n\treturn utils.IfElse(bool(s), conf.Pygments_style, conf.Dark_pygments_style)\n}\n\nfunc (s prefer_light_colors) UseLightColors() bool                    { return bool(s) }\nfunc (s prefer_light_colors) SyntaxAliases() map[string]string        { return conf.Syntax_aliases }\nfunc (s prefer_light_colors) TextForPath(path string) (string, error) { return data_for_path(path) }\n\nvar highlighter = sync.OnceValue(func() highlight.Highlighter {\n\treturn highlight.NewHighlighter(sanitize)\n})\n\nfunc highlight_all(paths []string, light bool) {\n\tctx := images.Context{}\n\tsrd := prefer_light_colors(light)\n\tif err := ctx.SafeParallel(0, len(paths), func(nums <-chan int) {\n\t\tfor i := range nums {\n\t\t\tpath := paths[i]\n\t\t\traw, err := highlighter().HighlightFile(path, &srd)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif light {\n\t\t\t\tlight_highlighted_lines_cache.Set(path, text_to_lines(raw))\n\t\t\t} else {\n\t\t\t\tdark_highlighted_lines_cache.Set(path, text_to_lines(raw))\n\t\t\t}\n\t\t}\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "kittens/diff/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"archive/tar\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/kittens/ssh\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc load_config(opts *Options) (ans *Config, err error) {\n\tans = NewConfig()\n\tp := config.ConfigParser{LineHandler: ans.Parse}\n\terr = p.LoadConfig(\"diff.conf\", opts.Config, opts.Override)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tans.KeyboardShortcuts = config.ResolveShortcuts(ans.KeyboardShortcuts)\n\treturn ans, nil\n}\n\nvar conf *Config\nvar opts *Options\nvar lp *loop.Loop\n\nvar temp_files []string\n\nfunc resolve_path(path string) (ans string, is_dir bool, err error) {\n\tvar s fs.FileInfo\n\tif s, err = os.Stat(path); err != nil {\n\t\treturn\n\t} else {\n\t\tif s.Mode()&fs.ModeNamedPipe != 0 {\n\t\t\tvar src, dest *os.File\n\t\t\tif src, err = os.Open(path); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer src.Close()\n\t\t\tif dest, err = os.CreateTemp(\"\", fmt.Sprintf(\"*-pipe-%s\", filepath.Base(path))); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer dest.Close()\n\t\t\ttemp_files = append(temp_files, dest.Name())\n\t\t\tif _, err = io.Copy(dest, src); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\treturn dest.Name(), false, nil\n\n\t\t} else {\n\t\t\treturn path, s.IsDir(), nil\n\t\t}\n\t}\n}\n\nfunc get_ssh_file(hostname, rpath string) (string, error) {\n\ttdir, err := os.MkdirTemp(\"\", \"*-\"+hostname)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tadd_remote_dir(tdir)\n\tis_abs := strings.HasPrefix(rpath, \"/\")\n\tfor strings.HasPrefix(rpath, \"/\") {\n\t\trpath = rpath[1:]\n\t}\n\tcmd := []string{ssh.SSHExe(), hostname, \"tar\", \"--dereference\", \"--create\", \"--file\", \"-\"}\n\tif is_abs {\n\t\tcmd = append(cmd, \"-C\", \"/\")\n\t}\n\tcmd = append(cmd, rpath)\n\tc := exec.Command(cmd[0], cmd[1:]...)\n\tc.Stdin, c.Stderr = os.Stdin, os.Stderr\n\tstdout, err := c.Output()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to ssh into remote host %s to get file %s with error: %w\", hostname, rpath, err)\n\t}\n\ttf := tar.NewReader(bytes.NewReader(stdout))\n\tcount, err := utils.ExtractAllFromTar(tf, tdir)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to untar data from remote host %s to get file %s with error: %w\", hostname, rpath, err)\n\t}\n\tans := filepath.Join(tdir, rpath)\n\tif count == 1 {\n\t\tif err = filepath.WalkDir(tdir, func(path string, d fs.DirEntry, err error) error {\n\t\t\tif !d.IsDir() {\n\t\t\t\tans = path\n\t\t\t\treturn fs.SkipAll\n\t\t\t}\n\t\t\treturn nil\n\t\t}); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn ans, nil\n}\n\nfunc get_remote_file(path string) (string, error) {\n\tif strings.HasPrefix(path, \"ssh:\") {\n\t\tparts := strings.SplitN(path, \":\", 3)\n\t\tif len(parts) == 3 {\n\t\t\treturn get_ssh_file(parts[1], parts[2])\n\t\t}\n\t}\n\treturn path, nil\n}\n\nfunc main(_ *cli.Command, opts_ *Options, args []string) (rc int, err error) {\n\topts = opts_\n\tconf, err = load_config(opts)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tif len(args) != 2 {\n\t\treturn 1, fmt.Errorf(\"You must specify exactly two files/directories to compare\")\n\t}\n\tif err = set_diff_command(conf.Diff_cmd); err != nil {\n\t\treturn 1, err\n\t}\n\tswitch conf.Color_scheme {\n\tcase Color_scheme_light:\n\t\tuse_light_colors = true\n\tcase Color_scheme_dark:\n\t\tuse_light_colors = false\n\tcase Color_scheme_auto:\n\t\tuse_light_colors = false\n\t}\n\tinit_caches()\n\tdefer func() {\n\t\tfor tdir := range remote_dirs {\n\t\t\tos.RemoveAll(tdir)\n\t\t}\n\t}()\n\tleft, err := get_remote_file(args[0])\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tright, err := get_remote_file(args[1])\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tdefer func() {\n\t\tfor _, path := range temp_files {\n\t\t\tos.Remove(path)\n\t\t}\n\t}()\n\tvar left_is_dir, right_is_dir bool\n\tif left, left_is_dir, err = resolve_path(left); err != nil {\n\t\treturn 1, err\n\t}\n\tif right, right_is_dir, err = resolve_path(right); err != nil {\n\t\treturn 1, err\n\t}\n\tif left_is_dir != right_is_dir {\n\t\treturn 1, fmt.Errorf(\"The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.'\")\n\t}\n\n\tlp, err = loop.New()\n\tloop.MouseTrackingMode(lp, loop.BUTTONS_AND_DRAG_MOUSE_TRACKING)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tlp.ColorSchemeChangeNotifications()\n\th := Handler{left: left, right: right, lp: lp}\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.SetCursorVisible(false)\n\t\tlp.SetCursorShape(loop.BAR_CURSOR, true)\n\t\tlp.AllowLineWrapping(false)\n\t\tlp.SetWindowTitle(fmt.Sprintf(\"%s vs. %s\", left, right))\n\t\tlp.QueryCapabilities()\n\t\th.initialize()\n\t\treturn \"\", nil\n\t}\n\tlp.OnCapabilitiesReceived = func(tc loop.TerminalCapabilities) error {\n\t\tif !tc.KeyboardProtocol {\n\t\t\treturn fmt.Errorf(\"This terminal does not support the kitty keyboard protocol, or you are running inside a terminal multiplexer that is blocking querying for kitty keyboard protocol support. The diff kitten cannot function without it.\")\n\t\t}\n\t\th.on_capabilities_received(tc)\n\t\treturn nil\n\t}\n\tlp.OnWakeup = h.on_wakeup\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorVisible(true)\n\t\tlp.SetCursorShape(loop.BLOCK_CURSOR, true)\n\t\th.finalize()\n\t\treturn \"\"\n\t}\n\tlp.OnResize = h.on_resize\n\tlp.OnKeyEvent = h.on_key_event\n\tlp.OnText = h.on_text\n\tlp.OnMouseEvent = h.on_mouse_event\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn 1, nil\n\t}\n\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/diff/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nfrom functools import partial\n\nfrom kitty.conf.types import Definition\nfrom kitty.constants import appname\nfrom kitty.simple_cli_definitions import CONFIG_HELP, CompletionSpec\n\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('Must be run as kitten diff')\n\ndefinition = Definition(\n    '!kittens.diff',\n)\n\nagr = definition.add_group\negr = definition.end_group\nopt = definition.add_option\nmap = definition.add_map\nmma = definition.add_mouse_map\n\n# diff {{{\nagr('diff', 'Diffing')\n\nopt('syntax_aliases', 'pyj:py pyi:py recipe:py', ctype='strdict_ _:', option_type='syntax_aliases',\n    long_text='''\nFile extension aliases for syntax highlight. For example, to syntax highlight\n:file:`file.xyz` as :file:`file.abc` use a setting of :code:`xyz:abc`.\nMultiple aliases must be separated by spaces.\n'''\n    )\n\nopt('num_context_lines', '3', option_type='positive_int',\n    long_text='The number of lines of context to show around each change.'\n    )\n\nopt('diff_cmd', 'auto',\n    long_text='''\nThe diff command to use. Must contain the placeholder :code:`_CONTEXT_` which\nwill be replaced by the number of lines of context. A few special values are allowed:\n:code:`auto` will automatically pick an available diff implementation. :code:`builtin`\nwill use the anchored diff algorithm from the Go standard library. :code:`git` will\nuse the git command to do the diffing. :code:`diff` will use the diff command to\ndo the diffing.\n'''\n    )\n\nopt('replace_tab_by', '\\\\x20\\\\x20\\\\x20\\\\x20', option_type='python_string',\n    long_text='The string to replace tabs with. Default is to use four spaces.'\n    )\n\nopt('+ignore_name', '', ctype='string',\n    add_to_default=False,\n    long_text='''\nA glob pattern that is matched against only the filename of files and directories. Matching\nfiles and directories are ignored when scanning the filesystem to look for files to diff.\nCan be specified multiple times to use multiple patterns. For example::\n\n    ignore_name .git\n    ignore_name *~\n    ignore_name *.pyc\n''',\n    )\n\nopt('mark_moved_lines', 'yes', option_type='to_bool', long_text='''\nHighlight lines that are moved, that is removed from the left and added to the right\ndifferently, using the :opt:`moved_bg` color.''')\n\nopt('word_diff_mode', 'words', choices=('words', 'central'),\n    long_text='''\nThe algorithm to use for highlighting which parts of changed lines differ.\nWhen set to :code:`words`, changed words in each changed line are highlighted.\nWhen set to :code:`central`, the central changed region of each changed line is\nhighlighted at the byte level. The :code:`words` mode is generally more useful\nas it shows exactly which words changed. Note that the :code:`words` mode\nonly applies when a changed chunk has equal numbers of added and removed lines.\n'''\n    )\n\nopt('word_regex', r'[^\\s\\p{P}]+',\n    long_text='''\nThe regular expression used to define a word when :opt:`word_diff_mode` is\n:code:`words`. The default value matches any run of non-whitespace and\nnon-punctuation characters. The expression must be a valid Go regular expression.\nIf an invalid expression is provided, diff will fail with an error.\n'''\n    )\n\negr()  # }}}\n\n# colors {{{\nagr('colors', 'Colors')\n\nopt('color_scheme', 'auto', choices=('auto', 'light', 'dark'), long_text='''\nWhether to use the light or dark colors. The default of :code:`auto` means\nto follow the parent terminal color scheme. Note that the actual colors used\nfor dark schemes are set by the :code:`dark_*` settings below and the non-prefixed\nsettings are used for light colors.\n''')\n\nopt('pygments_style', 'default', long_text='''\nThe pygments color scheme to use for syntax highlighting. See :link:`pygments\nbuiltin styles <https://pygments.org/styles/>` for a list of schemes. Note that\nthis **does not** change the colors used for diffing,\nonly the colors used for syntax highlighting. To change the general colors use the settings below.\nThis sets the colors used for light color schemes, use :opt:`dark_pygments_style` to change the\ncolors for dark color schemes.\n'''\n    )\n\nopt('dark_pygments_style', 'github-dark', long_text='''\nThe pygments color scheme to use for syntax highlighting with dark colors. See :link:`pygments\nbuiltin styles <https://pygments.org/styles/>` for a list of schemes. Note that\nthis **does not** change the colors used for diffing,\nonly the colors used for syntax highlighting. To change the general colors use the settings below.\nThis sets the colors used for dark color schemes, use :opt:`pygments_style` to change the\ncolors for light color schemes.''')\n\nopt('foreground', 'black', option_type='to_color', long_text='Basic colors')\nopt('dark_foreground', '#f8f8f2', option_type='to_color')\n\ndark_bg = '#212830'\nopt('background', 'white', option_type='to_color',)\nopt('dark_background', dark_bg, option_type='to_color',)\n\nopt('title_fg', 'black', option_type='to_color', long_text='Title colors')\nopt('dark_title_fg', 'white', option_type='to_color')\n\nopt('title_bg', 'white', option_type='to_color',)\nopt('dark_title_bg', dark_bg, option_type='to_color',)\n\nopt('margin_bg', '#fafbfc', option_type='to_color', long_text='Margin colors')\nopt('dark_margin_bg', dark_bg, option_type='to_color')\n\nopt('margin_fg', '#aaaaaa', option_type='to_color')\nopt('dark_margin_fg', '#aaaaaa', option_type='to_color')\n\nopt('removed_bg', '#ffeef0', option_type='to_color', long_text='Removed text backgrounds')\nopt('dark_removed_bg', '#352c33', option_type='to_color')\n\nopt('highlight_removed_bg', '#fdb8c0', option_type='to_color')\nopt('dark_highlight_removed_bg', '#5c3539', option_type='to_color')\n\nopt('removed_margin_bg', '#ffdce0', option_type='to_color')\nopt('dark_removed_margin_bg', '#5c3539', option_type='to_color')\n\nopt('added_bg', '#e6ffed', option_type='to_color', long_text='Added text backgrounds')\nopt('dark_added_bg', '#263834', option_type='to_color')\n\nopt('highlight_added_bg', '#acf2bd', option_type='to_color')\nopt('dark_highlight_added_bg', '#31503d', option_type='to_color')\n\nopt('added_margin_bg', '#cdffd8', option_type='to_color')\nopt('dark_added_margin_bg', '#31503d', option_type='to_color')\n\nopt('moved_bg', '#fffde7', option_type='to_color', long_text='Moved text backgrounds (same text that was removed in one place and added in another)')\nopt('dark_moved_bg', '#003333', option_type='to_color')\n\nopt('moved_margin_bg', '#fff3b0', option_type='to_color')\nopt('dark_moved_margin_bg', '#00495b', option_type='to_color')\n\nopt('filler_bg', '#fafbfc', option_type='to_color', long_text='Filler (empty) line background')\nopt('dark_filler_bg', '#262c36', option_type='to_color')\n\nopt('margin_filler_bg', 'none', option_type='to_color_or_none', long_text='Filler (empty) line background in margins, defaults to the filler background')\nopt('dark_margin_filler_bg', 'none', option_type='to_color_or_none')\n\n\nopt('hunk_margin_bg', '#dbedff', option_type='to_color', long_text='Hunk header colors')\nopt('dark_hunk_margin_bg', '#0c2d6b', option_type='to_color')\n\nopt('hunk_bg', '#f1f8ff', option_type='to_color')\nopt('dark_hunk_bg', '#253142', option_type='to_color')\n\nopt('search_bg', '#444', option_type='to_color', long_text='Highlighting')\nopt('dark_search_bg', '#2c599c', option_type='to_color')\n\nopt('search_fg', 'white', option_type='to_color')\nopt('dark_search_fg', 'white', option_type='to_color')\n\nopt('select_bg', '#b4d5fe', option_type='to_color')\nopt('dark_select_bg', '#2c599c', option_type='to_color')\n\nopt('select_fg', 'black', option_type='to_color_or_none')\nopt('dark_select_fg', 'white', option_type='to_color_or_none')\negr()  # }}}\n\n# shortcuts {{{\nagr('shortcuts', 'Keyboard shortcuts')\n\nmap('Quit',\n    'quit q quit',\n    )\nmap('Quit',\n    'quit esc quit',\n    )\n\nmap('Scroll down',\n    'scroll_down j scroll_by 1',\n    )\nmap('Scroll down',\n    'scroll_down down scroll_by 1',\n    )\n\nmap('Scroll up',\n    'scroll_up k scroll_by -1',\n    )\nmap('Scroll up',\n    'scroll_up up scroll_by -1',\n    )\n\nmap('Scroll to top',\n    'scroll_top home scroll_to start',\n    )\n\nmap('Scroll to bottom',\n    'scroll_bottom end scroll_to end',\n    )\n\nmap('Scroll to next page',\n    'scroll_page_down page_down scroll_to next-page',\n    )\nmap('Scroll to next page',\n    'scroll_page_down space scroll_to next-page',\n    )\nmap('Scroll to next page',\n    'scroll_page_down ctrl+f scroll_to next-page',\n    )\n\nmap('Scroll to previous page',\n    'scroll_page_up page_up scroll_to prev-page',\n    )\nmap('Scroll to previous page',\n    'scroll_page_up ctrl+b scroll_to prev-page',\n    )\n\nmap('Scroll down half page',\n    'scroll_half_page_down ctrl+d scroll_to next-half-page',\n    )\nmap('Scroll up half page',\n    'scroll_half_page_up ctrl+u scroll_to prev-half-page',\n    )\n\nmap('Scroll to next change',\n    'next_change n scroll_to next-change',\n    )\n\nmap('Scroll to previous change',\n    'prev_change p scroll_to prev-change',\n    )\n\nmap('Scroll to next file',\n    'next_file shift+j scroll_to next-file',\n    )\n\nmap('Scroll to previous file',\n    'prev_file shift+k scroll_to prev-file',\n    )\n\nmap('Show all context',\n    'all_context a change_context all',\n    )\n\nmap('Show default context',\n    'default_context = change_context default',\n    )\n\nmap('Increase context',\n    'increase_context + change_context 5',\n    )\n\nmap('Decrease context',\n    'decrease_context - change_context -5',\n    )\n\nmap('Search forward',\n    'search_forward / start_search regex forward',\n    )\n\nmap('Search backward',\n    'search_backward ? start_search regex backward',\n    )\n\nmap('Scroll to next search match',\n    'next_match . scroll_to next-match',\n    )\nmap('Scroll to next search match',\n    'next_match > scroll_to next-match',\n    )\n\nmap('Scroll to previous search match',\n    'prev_match , scroll_to prev-match',\n    )\nmap('Scroll to previous search match',\n    'prev_match < scroll_to prev-match',\n    )\n\nmap('Search forward (no regex)',\n    'search_forward_simple f start_search substring forward',\n    )\n\nmap('Search backward (no regex)',\n    'search_backward_simple b start_search substring backward',\n    )\n\nmap('Copy selection to clipboard', 'copy_to_clipboard y copy_to_clipboard')\nmap('Copy selection to clipboard or exit if no selection is present', 'copy_to_clipboard_or_exit ctrl+c copy_to_clipboard_or_exit')\n\negr()  # }}}\n\nOPTIONS = partial('''\\\n--context\ntype=int\ndefault=-1\nNumber of lines of context to show between changes. Negative values use the\nnumber set in :file:`diff.conf`.\n\n\n--config\ntype=list\ncompletion=type:file ext:conf group:\"Config files\" kwds:none,NONE\n{config_help}\n\n\n--override -o\ntype=list\nOverride individual configuration options, can be specified multiple times.\nSyntax: :italic:`name=value`. For example: :italic:`-o background=gray`\n\n'''.format, config_help=CONFIG_HELP.format(conf_name='diff', appname=appname))\nhelp_text = 'Show a side-by-side diff of the specified files/directories. You can also use :italic:`ssh:hostname:remote-file-path` to diff remote files.'\nusage = 'file_or_directory_left file_or_directory_right'\n\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    from kitty.guess_mime_type import text_mimes\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Pretty, side-by-side diffing of files and images'\n    mimes = ' '.join(f'mime:{x}' for x in ('text/*', 'image/*') + tuple(text_mimes))\n    cd['args_completion'] = CompletionSpec.from_string(f'type:file {mimes} group:\"Text and image files\"')\nelif __name__ == '__conf__':\n    sys.options_definition = definition  # type: ignore\n"
  },
  {
    "path": "kittens/diff/mouse.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype KittyOpts struct {\n\tWheel_scroll_multiplier float64\n\tCopy_on_select          bool\n}\n\nfunc read_relevant_kitty_opts() KittyOpts {\n\tans := KittyOpts{Wheel_scroll_multiplier: kitty.KittyConfigDefaults.Wheel_scroll_multiplier}\n\thandle_line := func(key, val string) error {\n\t\tswitch key {\n\t\tcase \"wheel_scroll_multiplier\":\n\t\t\tv, err := strconv.ParseFloat(val, 64)\n\t\t\tif err == nil {\n\t\t\t\tans.Wheel_scroll_multiplier = v\n\t\t\t}\n\t\tcase \"copy_on_select\":\n\t\t\tans.Copy_on_select = strings.ToLower(val) == \"clipboard\"\n\t\t}\n\t\treturn nil\n\t}\n\tconfig.ReadKittyConfig(handle_line)\n\treturn ans\n}\n\nvar RelevantKittyOpts = sync.OnceValue(func() KittyOpts {\n\treturn read_relevant_kitty_opts()\n})\n\nfunc (self *Handler) handle_wheel_event(up bool) {\n\tamt := int(math.Round(RelevantKittyOpts().Wheel_scroll_multiplier))\n\tif amt == 0 {\n\t\tamt = 1\n\t}\n\tif up {\n\t\tamt *= -1\n\t}\n\t_ = self.dispatch_action(`scroll_by`, strconv.Itoa(amt))\n}\n\ntype line_pos struct {\n\tmin_x, max_x int\n\ty            ScrollPos\n}\n\nfunc (self *line_pos) MinX() int { return self.min_x }\nfunc (self *line_pos) MaxX() int { return self.max_x }\nfunc (self *line_pos) Equal(other tui.LinePos) bool {\n\tif o, ok := other.(*line_pos); ok {\n\t\treturn self.y == o.y\n\t}\n\treturn false\n}\n\nfunc (self *line_pos) LessThan(other tui.LinePos) bool {\n\tif o, ok := other.(*line_pos); ok {\n\t\treturn self.y.Less(o.y)\n\t}\n\treturn false\n}\n\nfunc (self *Handler) line_pos_from_pos(x int, pos ScrollPos) *line_pos {\n\tans := line_pos{min_x: self.logical_lines.margin_size, y: pos}\n\tavailable_cols := self.logical_lines.columns / 2\n\tif x >= available_cols {\n\t\tans.min_x += available_cols\n\t\tans.max_x = utils.Max(ans.min_x, ans.min_x+self.logical_lines.ScreenLineAt(pos).right.wcswidth()-1)\n\t} else {\n\t\tans.max_x = utils.Max(ans.min_x, ans.min_x+self.logical_lines.ScreenLineAt(pos).left.wcswidth()-1)\n\t}\n\treturn &ans\n}\n\nfunc (self *Handler) start_mouse_selection(ev *loop.MouseEvent) {\n\tavailable_cols := self.logical_lines.columns / 2\n\tif ev.Cell.Y >= self.screen_size.num_lines || ev.Cell.X < self.logical_lines.margin_size || (ev.Cell.X >= available_cols && ev.Cell.X < available_cols+self.logical_lines.margin_size) {\n\t\treturn\n\t}\n\tpos := self.scroll_pos\n\tself.logical_lines.IncrementScrollPosBy(&pos, ev.Cell.Y)\n\tll := self.logical_lines.At(pos.logical_line)\n\tif ll.line_type == EMPTY_LINE || ll.line_type == IMAGE_LINE {\n\t\treturn\n\t}\n\tself.mouse_selection.StartNewSelection(ev, self.line_pos_from_pos(ev.Cell.X, pos), 0, self.screen_size.num_lines-1, self.screen_size.cell_width, self.screen_size.cell_height)\n}\n\nfunc (self *Handler) drag_scroll_tick(timer_id loop.IdType) error {\n\treturn self.mouse_selection.DragScrollTick(timer_id, self.lp, self.drag_scroll_tick, func(amt int, ev *loop.MouseEvent) error {\n\t\tif self.scroll_lines(amt) != 0 {\n\t\t\tself.do_update_mouse_selection(ev)\n\t\t\tself.draw_screen()\n\t\t}\n\t\treturn nil\n\t})\n}\n\nvar debugprintln = tty.DebugPrintln\n\nfunc (self *Handler) update_mouse_selection(ev *loop.MouseEvent) {\n\tif !self.mouse_selection.IsActive() {\n\t\treturn\n\t}\n\tif self.mouse_selection.OutOfVerticalBounds(ev) {\n\t\tself.mouse_selection.DragScroll(ev, self.lp, self.drag_scroll_tick)\n\t\treturn\n\t}\n\tself.do_update_mouse_selection(ev)\n}\n\nfunc (self *Handler) do_update_mouse_selection(ev *loop.MouseEvent) {\n\tpos := self.scroll_pos\n\ty := ev.Cell.Y\n\ty = utils.Max(0, utils.Min(y, self.screen_size.num_lines-1))\n\tself.logical_lines.IncrementScrollPosBy(&pos, y)\n\tx := self.mouse_selection.StartLine().MinX()\n\tself.mouse_selection.Update(ev, self.line_pos_from_pos(x, pos))\n\tself.draw_screen()\n}\n\nfunc (self *Handler) clear_mouse_selection() {\n\tself.mouse_selection.Clear()\n}\n\nfunc (self *Handler) text_for_current_mouse_selection() string {\n\tif self.mouse_selection.IsEmpty() {\n\t\treturn \"\"\n\t}\n\ttext := make([]byte, 0, 2048)\n\tstart_pos, end_pos := *self.mouse_selection.StartLine().(*line_pos), *self.mouse_selection.EndLine().(*line_pos)\n\n\t// if start is after end, swap them\n\tif end_pos.y.Less(start_pos.y) {\n\t\tstart_pos, end_pos = end_pos, start_pos\n\t}\n\n\tstart, end := start_pos.y, end_pos.y\n\tis_left := start_pos.min_x == self.logical_lines.margin_size\n\n\tline_for_pos := func(pos ScrollPos) string {\n\t\tif is_left {\n\t\t\treturn self.logical_lines.ScreenLineAt(pos).left.marked_up_text\n\t\t}\n\t\treturn self.logical_lines.ScreenLineAt(pos).right.marked_up_text\n\t}\n\n\tfor pos, prev_ll_idx := start, start.logical_line; pos.Less(end) || pos == end; {\n\t\tll := self.logical_lines.At(pos.logical_line)\n\t\tvar line string\n\t\tswitch ll.line_type {\n\t\tcase EMPTY_LINE:\n\t\tcase IMAGE_LINE:\n\t\t\tif pos.screen_line < ll.image_lines_offset {\n\t\t\t\tline = line_for_pos(pos)\n\t\t\t}\n\t\tdefault:\n\t\t\tline = line_for_pos(pos)\n\t\t}\n\t\tline = wcswidth.StripEscapeCodes(line)\n\t\ts, e := self.mouse_selection.LineBounds(self.line_pos_from_pos(start_pos.min_x, pos))\n\t\ts -= start_pos.min_x\n\t\te -= start_pos.min_x\n\t\tline = wcswidth.TruncateToVisualLength(line, e+1)\n\t\tif s > 0 {\n\t\t\tprefix := wcswidth.TruncateToVisualLength(line, s)\n\t\t\tline = line[len(prefix):]\n\t\t}\n\t\t// TODO: look at the original line from the source and handle leading tabs as per it\n\t\tif pos.logical_line > prev_ll_idx {\n\t\t\tline = \"\\n\" + line\n\t\t}\n\t\tprev_ll_idx = pos.logical_line\n\t\tif line != \"\" {\n\t\t\ttext = append(text, line...)\n\t\t}\n\t\tif self.logical_lines.IncrementScrollPosBy(&pos, 1) == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn utils.UnsafeBytesToString(text)\n}\n\nfunc (self *Handler) finish_mouse_selection(ev *loop.MouseEvent) {\n\tif !self.mouse_selection.IsActive() {\n\t\treturn\n\t}\n\tself.update_mouse_selection(ev)\n\tself.mouse_selection.Finish()\n\ttext := self.text_for_current_mouse_selection()\n\tif text != \"\" {\n\t\tif RelevantKittyOpts().Copy_on_select {\n\t\t\tself.lp.CopyTextToClipboard(text)\n\t\t} else {\n\t\t\tself.lp.CopyTextToPrimarySelection(text)\n\t\t}\n\t}\n}\n\nfunc (self *Handler) add_mouse_selection_to_line(line_pos ScrollPos, y int) string {\n\tif self.mouse_selection.IsEmpty() {\n\t\treturn \"\"\n\t}\n\tselection_sgr := format_as_sgr.selection\n\tx := self.mouse_selection.StartLine().MinX()\n\treturn self.mouse_selection.LineFormatSuffix(self.line_pos_from_pos(x, line_pos), selection_sgr[2:len(selection_sgr)-1], y)\n}\n"
  },
  {
    "path": "kittens/diff/patch.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\tparallel \"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/simdstring\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n)\n\nvar _ = fmt.Print\n\nconst GIT_DIFF = `git diff --no-color --no-ext-diff --exit-code -U_CONTEXT_ --no-index --`\nconst DIFF_DIFF = `diff -p -U _CONTEXT_ --`\n\nvar diff_cmd []string\n\nvar GitExe = sync.OnceValue(func() string {\n\treturn utils.FindExe(\"git\")\n})\n\nvar DiffExe = sync.OnceValue(func() string {\n\treturn utils.FindExe(\"diff\")\n})\n\nfunc find_differ() {\n\tif GitExe() != \"git\" && exec.Command(GitExe(), \"--help\").Run() == nil {\n\t\tdiff_cmd, _ = shlex.Split(GIT_DIFF)\n\t} else if DiffExe() != \"diff\" && exec.Command(DiffExe(), \"--help\").Run() == nil {\n\t\tdiff_cmd, _ = shlex.Split(DIFF_DIFF)\n\t} else {\n\t\tdiff_cmd = []string{}\n\t}\n}\n\nfunc set_diff_command(q string) error {\n\tswitch q {\n\tcase \"auto\":\n\t\tfind_differ()\n\tcase \"builtin\", \"\":\n\t\tdiff_cmd = []string{}\n\tcase \"diff\":\n\t\tdiff_cmd, _ = shlex.Split(DIFF_DIFF)\n\tcase \"git\":\n\t\tdiff_cmd, _ = shlex.Split(GIT_DIFF)\n\tdefault:\n\t\tc, err := shlex.Split(q)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdiff_cmd = c\n\t}\n\treturn nil\n}\n\n// Region represents a highlighted byte range within a line.\ntype Region struct{ offset, size int }\n\n// Center holds the highlighted regions for the left (removed) and right (added) sides of a changed line pair.\ntype Center struct {\n\tleft_regions  []Region\n\tright_regions []Region\n}\n\ntype Chunk struct {\n\tis_context              bool\n\tleft_start, right_start int\n\tleft_count, right_count int\n\tcenters                 []Center\n}\n\nfunc (self *Chunk) add_line() {\n\tself.right_count++\n}\n\nfunc (self *Chunk) remove_line() {\n\tself.left_count++\n}\n\nfunc (self *Chunk) context_line() {\n\tself.left_count++\n\tself.right_count++\n}\n\n// changed_center computes the central changed region of left/right at the byte level.\nfunc changed_center(left, right string) (ans Center) {\n\tif len(left) > 0 && len(right) > 0 {\n\t\tll, rl := len(left), len(right)\n\t\tml := utils.Min(ll, rl)\n\t\toffset := 0\n\t\tfor ; offset < ml && left[offset] == right[offset]; offset++ {\n\t\t}\n\t\tsuffix_count := 0\n\t\tfor ; suffix_count < ml && left[ll-1-suffix_count] == right[rl-1-suffix_count]; suffix_count++ {\n\t\t}\n\t\tleft_size := ll - suffix_count - offset\n\t\tright_size := rl - suffix_count - offset\n\t\tif left_size > 0 {\n\t\t\tans.left_regions = []Region{{offset: offset, size: left_size}}\n\t\t}\n\t\tif right_size > 0 {\n\t\t\tans.right_regions = []Region{{offset: offset, size: right_size}}\n\t\t}\n\t}\n\treturn\n}\n\nvar word_regexp = sync.OnceValues(func() (*regexp.Regexp, error) {\n\tpattern := `\\S+`\n\tif conf != nil && conf.Word_regex != \"\" {\n\t\tpattern = conf.Word_regex\n\t}\n\treturn regexp.Compile(pattern)\n})\n\n// word_diff_center computes highlighted regions for changed words between left and right.\nfunc word_diff_center(left, right string, re *regexp.Regexp) Center {\n\tleft_matches := re.FindAllStringIndex(left, -1)\n\tright_matches := re.FindAllStringIndex(right, -1)\n\n\ttype word struct {\n\t\ttext   string\n\t\toffset int\n\t\tsize   int\n\t}\n\tleft_words := make([]word, len(left_matches))\n\tright_words := make([]word, len(right_matches))\n\tfor i, m := range left_matches {\n\t\tleft_words[i] = word{text: left[m[0]:m[1]], offset: m[0], size: m[1] - m[0]}\n\t}\n\tfor i, m := range right_matches {\n\t\tright_words[i] = word{text: right[m[0]:m[1]], offset: m[0], size: m[1] - m[0]}\n\t}\n\n\t// Strip common prefix and suffix words so LCS only runs on the differing middle.\n\tprefix := 0\n\tfor prefix < len(left_words) && prefix < len(right_words) && left_words[prefix].text == right_words[prefix].text {\n\t\tprefix++\n\t}\n\tsuffix := 0\n\tfor suffix < len(left_words)-prefix && suffix < len(right_words)-prefix &&\n\t\tleft_words[len(left_words)-1-suffix].text == right_words[len(right_words)-1-suffix].text {\n\t\tsuffix++\n\t}\n\tlw := left_words[prefix : len(left_words)-suffix]\n\trw := right_words[prefix : len(right_words)-suffix]\n\n\tm, n := len(lw), len(rw)\n\t// LCS dynamic programming table\n\tdp := make([][]int, m+1)\n\tfor i := range dp {\n\t\tdp[i] = make([]int, n+1)\n\t}\n\tfor i := 1; i <= m; i++ {\n\t\tfor j := 1; j <= n; j++ {\n\t\t\tif lw[i-1].text == rw[j-1].text {\n\t\t\t\tdp[i][j] = dp[i-1][j-1] + 1\n\t\t\t} else if dp[i-1][j] > dp[i][j-1] {\n\t\t\t\tdp[i][j] = dp[i-1][j]\n\t\t\t} else {\n\t\t\t\tdp[i][j] = dp[i][j-1]\n\t\t\t}\n\t\t}\n\t}\n\n\t// Backtrack to find changed words within the middle slice.\n\tleft_changed := make([]bool, m)\n\tright_changed := make([]bool, n)\n\ti, j := m, n\n\tfor i > 0 && j > 0 {\n\t\tif lw[i-1].text == rw[j-1].text {\n\t\t\ti--\n\t\t\tj--\n\t\t} else if dp[i-1][j] > dp[i][j-1] {\n\t\t\tleft_changed[i-1] = true\n\t\t\ti--\n\t\t} else {\n\t\t\tright_changed[j-1] = true\n\t\t\tj--\n\t\t}\n\t}\n\tfor i > 0 {\n\t\tleft_changed[i-1] = true\n\t\ti--\n\t}\n\tfor j > 0 {\n\t\tright_changed[j-1] = true\n\t\tj--\n\t}\n\n\t// Remap changed flags to absolute indices within the full words arrays.\n\tleft_changed_abs := make([]bool, len(left_words))\n\tright_changed_abs := make([]bool, len(right_words))\n\tfor k, c := range left_changed {\n\t\tleft_changed_abs[prefix+k] = c\n\t}\n\tfor k, c := range right_changed {\n\t\tright_changed_abs[prefix+k] = c\n\t}\n\n\t// Verify that every changed word at index i has a corresponding changed\n\t// word at the same index i on the other side.  If any position is changed\n\t// on exactly one side, the lines differ in word count and it makes more\n\t// sense to highlight the single changed central region of the whole line.\n\tmax_idx := max(len(left_words), len(right_words))\n\tfor idx := 0; idx < max_idx; idx++ {\n\t\tlc := idx < len(left_words) && left_changed_abs[idx]\n\t\trc := idx < len(right_words) && right_changed_abs[idx]\n\t\tif lc != rc {\n\t\t\treturn changed_center(left, right)\n\t\t}\n\t}\n\n\t// All changed words are positionally paired.  Apply character-level\n\t// prefix/suffix trimming to each pair so only the differing central bytes\n\t// are highlighted.\n\tvar ans Center\n\tfor idx := range left_words {\n\t\tif !left_changed_abs[idx] {\n\t\t\tcontinue\n\t\t}\n\t\tlword := left_words[idx]\n\t\trword := right_words[idx]\n\t\tlt := left[lword.offset : lword.offset+lword.size]\n\t\trt := right[rword.offset : rword.offset+rword.size]\n\t\tll, rl := len(lt), len(rt)\n\t\tml := min(ll, rl)\n\t\tcpfx := 0\n\t\tfor cpfx < ml && lt[cpfx] == rt[cpfx] {\n\t\t\tcpfx++\n\t\t}\n\t\tcsfx := 0\n\t\tfor csfx < ml-cpfx && lt[ll-1-csfx] == rt[rl-1-csfx] {\n\t\t\tcsfx++\n\t\t}\n\t\tlsize := ll - cpfx - csfx\n\t\trsize := rl - cpfx - csfx\n\t\tif lsize > 0 {\n\t\t\tans.left_regions = append(ans.left_regions, Region{offset: lword.offset + cpfx, size: lsize})\n\t\t}\n\t\tif rsize > 0 {\n\t\t\tans.right_regions = append(ans.right_regions, Region{offset: rword.offset + cpfx, size: rsize})\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc (self *Chunk) finalize(_ []string, _ []string) {\n\t// Center computation is performed in parallel by Patch.compute_centers\n}\n\ntype Hunk struct {\n\tleft_start, left_count     int\n\tright_start, right_count   int\n\ttitle                      string\n\tadded_count, removed_count int\n\tchunks                     []*Chunk\n\tcurrent_chunk              *Chunk\n\tlargest_line_number        int\n}\n\nfunc (self *Hunk) new_chunk(is_context bool) *Chunk {\n\tleft_start, right_start := self.left_start, self.right_start\n\tif len(self.chunks) > 0 {\n\t\tc := self.chunks[len(self.chunks)-1]\n\t\tleft_start = c.left_start + c.left_count\n\t\tright_start = c.right_start + c.right_count\n\t}\n\treturn &Chunk{is_context: is_context, left_start: left_start, right_start: right_start}\n}\n\nfunc (self *Hunk) ensure_diff_chunk() {\n\tif self.current_chunk == nil || self.current_chunk.is_context {\n\t\tif self.current_chunk != nil {\n\t\t\tself.chunks = append(self.chunks, self.current_chunk)\n\t\t}\n\t\tself.current_chunk = self.new_chunk(false)\n\t}\n}\n\nfunc (self *Hunk) ensure_context_chunk() {\n\tif self.current_chunk == nil || !self.current_chunk.is_context {\n\t\tif self.current_chunk != nil {\n\t\t\tself.chunks = append(self.chunks, self.current_chunk)\n\t\t}\n\t\tself.current_chunk = self.new_chunk(true)\n\t}\n}\n\nfunc (self *Hunk) add_line() {\n\tself.ensure_diff_chunk()\n\tself.current_chunk.add_line()\n\tself.added_count++\n}\n\nfunc (self *Hunk) remove_line() {\n\tself.ensure_diff_chunk()\n\tself.current_chunk.remove_line()\n\tself.removed_count++\n}\n\nfunc (self *Hunk) context_line() {\n\tself.ensure_context_chunk()\n\tself.current_chunk.context_line()\n}\n\nfunc (self *Hunk) finalize(left_lines, right_lines []string) error {\n\tif self.current_chunk != nil {\n\t\tself.chunks = append(self.chunks, self.current_chunk)\n\t}\n\t// Sanity check\n\tc := self.chunks[len(self.chunks)-1]\n\tif c.left_start+c.left_count != self.left_start+self.left_count {\n\t\treturn fmt.Errorf(\"Left side line mismatch %d != %d\", c.left_start+c.left_count, self.left_start+self.left_count)\n\t}\n\tif c.right_start+c.right_count != self.right_start+self.right_count {\n\t\treturn fmt.Errorf(\"Right side line mismatch %d != %d\", c.right_start+c.right_count, self.right_start+self.right_count)\n\t}\n\tfor _, c := range self.chunks {\n\t\tc.finalize(left_lines, right_lines)\n\t}\n\treturn nil\n}\n\ntype Patch struct {\n\tall_hunks                                       []*Hunk\n\tlargest_line_number, added_count, removed_count int\n\tleft_moved_lines, right_moved_lines             *utils.Set[int]\n}\n\nfunc (self *Patch) Len() int { return len(self.all_hunks) }\n\nfunc splitlines_like_git(raw string, strip_trailing_lines bool, process_line func(string)) {\n\tsz := len(raw)\n\tif strip_trailing_lines {\n\t\tfor sz > 0 && (raw[sz-1] == '\\n' || raw[sz-1] == '\\r') {\n\t\t\tsz--\n\t\t}\n\t}\n\tstart := 0\n\tfor i := 0; i < sz; i++ {\n\t\tswitch raw[i] {\n\t\tcase '\\n':\n\t\t\tprocess_line(raw[start:i])\n\t\t\tstart = i + 1\n\t\tcase '\\r':\n\t\t\tprocess_line(raw[start:i])\n\t\t\tstart = i + 1\n\t\t\tif start < sz && raw[start] == '\\n' {\n\t\t\t\ti++\n\t\t\t\tstart++\n\t\t\t}\n\t\t}\n\t}\n\tif start < sz {\n\t\tprocess_line(raw[start:sz])\n\t}\n}\n\nfunc parse_range(x string) (start, count int) {\n\ts, c, found := strings.Cut(x, \",\")\n\tstart, _ = strconv.Atoi(s)\n\tif start < 0 {\n\t\tstart = -start\n\t}\n\tcount = 1\n\tif found {\n\t\tcount, _ = strconv.Atoi(c)\n\t}\n\treturn\n}\n\nfunc parse_hunk_header(line string) *Hunk {\n\tparts := strings.SplitN(line, \"@@\", 3)\n\tlinespec := strings.TrimSpace(parts[1])\n\ttitle := \"\"\n\tif len(parts) == 3 {\n\t\ttitle = strings.TrimSpace(parts[2])\n\t}\n\tleft, right, _ := strings.Cut(linespec, \" \")\n\tls, lc := parse_range(left)\n\trs, rc := parse_range(right)\n\treturn &Hunk{\n\t\ttitle: title, left_start: ls - 1, left_count: lc, right_start: rs - 1, right_count: rc,\n\t\tlargest_line_number: utils.Max(ls-1+lc, rs-1+rc),\n\t}\n}\n\nfunc (self *Patch) compute_centers(left_lines, right_lines []string) error {\n\tword_mode := conf != nil && conf.Word_diff_mode == Word_diff_mode_words\n\tvar re *regexp.Regexp\n\tif word_mode {\n\t\tvar err error\n\t\tre, err = word_regexp()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to compile word_regex %q: %w\", conf.Word_regex, err)\n\t\t}\n\t}\n\n\ttype pair struct {\n\t\tchunk *Chunk\n\t\tidx   int\n\t}\n\tvar pairs []pair\n\tfor _, hunk := range self.all_hunks {\n\t\tfor _, chunk := range hunk.chunks {\n\t\t\tif !chunk.is_context && chunk.left_count == chunk.right_count {\n\t\t\t\tfor i := 0; i < chunk.left_count; i++ {\n\t\t\t\t\tpairs = append(pairs, pair{chunk, i})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif len(pairs) == 0 {\n\t\treturn nil\n\t}\n\tcenters := make([]Center, len(pairs))\n\tif err := parallel.Run_in_parallel_over_range(0, func(start, end int) {\n\t\tfor i := start; i < end; i++ {\n\t\t\tp := pairs[i]\n\t\t\tleft := left_lines[p.chunk.left_start+p.idx]\n\t\t\tright := right_lines[p.chunk.right_start+p.idx]\n\t\t\tif word_mode {\n\t\t\t\tcenters[i] = word_diff_center(left, right, re)\n\t\t\t} else {\n\t\t\t\tcenters[i] = changed_center(left, right)\n\t\t\t}\n\t\t}\n\t}, 0, len(pairs)); err != nil {\n\t\treturn err\n\t}\n\tci := 0\n\tfor _, hunk := range self.all_hunks {\n\t\tfor _, chunk := range hunk.chunks {\n\t\t\tif !chunk.is_context && chunk.left_count == chunk.right_count {\n\t\t\t\tchunk.centers = centers[ci : ci+chunk.left_count]\n\t\t\t\tci += chunk.left_count\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// Use SIMD to efficiently find non-blank lines: a line is non-blank if it\n// contains at least one character that is not a space or tab.\nfunc is_non_blank(text string) bool {\n\treturn simdstring.NotIndexByte2String(text, ' ', '\\t') >= 0\n}\n\nfunc (self *Patch) detect_moved_lines(left_lines, right_lines []string) {\n\t// Build maps from line text to lists of line numbers for removed and added lines.\n\tremoved := make(map[string][]int, len(left_lines)) // text -> left line numbers\n\tadded := make(map[string][]int, len(right_lines))  // text -> right line numbers\n\tfor _, hunk := range self.all_hunks {\n\t\tfor _, chunk := range hunk.chunks {\n\t\t\tif !chunk.is_context {\n\t\t\t\tfor i := 0; i < chunk.left_count; i++ {\n\t\t\t\t\tlnum := chunk.left_start + i\n\t\t\t\t\ttext := left_lines[lnum]\n\t\t\t\t\tif is_non_blank(text) {\n\t\t\t\t\t\tremoved[text] = append(removed[text], lnum)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor i := 0; i < chunk.right_count; i++ {\n\t\t\t\t\trnum := chunk.right_start + i\n\t\t\t\t\ttext := right_lines[rnum]\n\t\t\t\t\tif is_non_blank(text) {\n\t\t\t\t\t\tadded[text] = append(added[text], rnum)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Lines that appear in both removed and added sets are moved lines. When a\n\t// line appears multiple times on each side, only min(left_count,\n\t// right_count) occurrences are marked as moved.\n\tself.left_moved_lines = utils.NewSet[int]()\n\tself.right_moved_lines = utils.NewSet[int]()\n\tfor text, lnums := range removed {\n\t\tif rnums, ok := added[text]; ok {\n\t\t\tcount := min(len(lnums), len(rnums))\n\t\t\tfor _, lnum := range lnums[:count] {\n\t\t\t\tself.left_moved_lines.Add(lnum)\n\t\t\t}\n\t\t\tfor _, rnum := range rnums[:count] {\n\t\t\t\tself.right_moved_lines.Add(rnum)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc parse_patch(raw string, left_lines, right_lines []string) (ans *Patch, err error) {\n\tans = &Patch{all_hunks: make([]*Hunk, 0, 32)}\n\tvar current_hunk *Hunk\n\tsplitlines_like_git(raw, true, func(line string) {\n\t\tif strings.HasPrefix(line, \"@@ \") {\n\t\t\tcurrent_hunk = parse_hunk_header(line)\n\t\t\tans.all_hunks = append(ans.all_hunks, current_hunk)\n\t\t} else if current_hunk != nil {\n\t\t\tvar ch byte\n\t\t\tif len(line) > 0 {\n\t\t\t\tch = line[0]\n\t\t\t}\n\t\t\tswitch ch {\n\t\t\tcase '+':\n\t\t\t\tcurrent_hunk.add_line()\n\t\t\tcase '-':\n\t\t\t\tcurrent_hunk.remove_line()\n\t\t\tcase '\\\\':\n\t\t\tdefault:\n\t\t\t\tcurrent_hunk.context_line()\n\t\t\t}\n\t\t}\n\t})\n\tfor _, h := range ans.all_hunks {\n\t\terr = h.finalize(left_lines, right_lines)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tans.added_count += h.added_count\n\t\tans.removed_count += h.removed_count\n\t}\n\tif len(ans.all_hunks) > 0 {\n\t\tans.largest_line_number = ans.all_hunks[len(ans.all_hunks)-1].largest_line_number\n\t}\n\terr = ans.compute_centers(left_lines, right_lines)\n\tif err == nil && conf.Mark_moved_lines {\n\t\tans.detect_moved_lines(left_lines, right_lines)\n\t}\n\treturn\n}\n\nfunc run_diff(file1, file2 string, num_of_context_lines int) (ok, is_different bool, patch string, err error) {\n\t// we resolve symlinks because git diff does not follow symlinks, while diff\n\t// does. We want consistent behavior, also for integration with git difftool\n\t// we always want symlinks to be followed.\n\tpath1, err := filepath.EvalSymlinks(file1)\n\tif err != nil {\n\t\treturn\n\t}\n\tpath2, err := filepath.EvalSymlinks(file2)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(diff_cmd) == 0 {\n\t\tdata1, err := data_for_path(path1)\n\t\tif err != nil {\n\t\t\treturn false, false, \"\", err\n\t\t}\n\t\tdata2, err := data_for_path(path2)\n\t\tif err != nil {\n\t\t\treturn false, false, \"\", err\n\t\t}\n\t\tpatchb := Diff(path1, data1, path2, data2, num_of_context_lines)\n\t\tif patchb == nil {\n\t\t\treturn true, false, \"\", nil\n\t\t}\n\t\treturn true, len(patchb) > 0, utils.UnsafeBytesToString(patchb), nil\n\t} else {\n\t\tcontext := strconv.Itoa(num_of_context_lines)\n\t\tcmd := utils.Map(func(x string) string {\n\t\t\treturn strings.ReplaceAll(x, \"_CONTEXT_\", context)\n\t\t}, diff_cmd)\n\n\t\tcmd = append(cmd, path1, path2)\n\t\tc := exec.Command(cmd[0], cmd[1:]...)\n\t\tstdout, stderr := bytes.Buffer{}, bytes.Buffer{}\n\t\tc.Stdout, c.Stderr = &stdout, &stderr\n\t\terr = c.Run()\n\t\tif err != nil {\n\t\t\tvar e *exec.ExitError\n\t\t\tif errors.As(err, &e) && e.ExitCode() == 1 {\n\t\t\t\treturn true, true, stdout.String(), nil\n\t\t\t}\n\t\t\treturn false, false, stderr.String(), err\n\t\t}\n\t\treturn true, false, stdout.String(), nil\n\t}\n}\n\nfunc do_diff(file1, file2 string, context_count int) (ans *Patch, err error) {\n\tok, _, raw, err := run_diff(file1, file2, context_count)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"Failed to diff %s vs. %s with errors:\\n%s\", file1, file2, raw)\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\tleft_lines, err := lines_for_path(file1)\n\tif err != nil {\n\t\treturn\n\t}\n\tright_lines, err := lines_for_path(file2)\n\tif err != nil {\n\t\treturn\n\t}\n\tans, err = parse_patch(raw, left_lines, right_lines)\n\treturn\n}\n\ntype diff_job struct{ file1, file2 string }\n\nfunc diff(jobs []diff_job, context_count int) (ans map[string]*Patch, err error) {\n\tans = make(map[string]*Patch)\n\tctx := images.Context{}\n\ttype result struct {\n\t\tfile1, file2 string\n\t\terr          error\n\t\tpatch        *Patch\n\t}\n\tresults := make(chan result, len(jobs))\n\tif err := ctx.SafeParallel(0, len(jobs), func(nums <-chan int) {\n\t\tfor i := range nums {\n\t\t\tjob := jobs[i]\n\t\t\tr := result{file1: job.file1, file2: job.file2}\n\t\t\tr.patch, r.err = do_diff(job.file1, job.file2, context_count)\n\t\t\tresults <- r\n\t\t}\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\tclose(results)\n\tfor r := range results {\n\t\tif r.err != nil {\n\t\t\treturn nil, r.err\n\t\t}\n\t\tans[r.file1] = r.patch\n\t}\n\treturn ans, nil\n}\n"
  },
  {
    "path": "kittens/diff/patch_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"regexp\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n)\n\nvar region_eq = cmpopts.EquateComparable(Region{})\n\nfunc TestWordDiffCenter(t *testing.T) {\n\tre := regexp.MustCompile(`\\S+`)\n\ttype tc struct {\n\t\tleft, right   string\n\t\tleft_regions  []Region\n\t\tright_regions []Region\n\t}\n\ttests := []tc{\n\t\t{\n\t\t\t// word count equal, single substitution at index 1 → positional pair\n\t\t\t// \"quick\" vs \"slow\": no common chars → full words\n\t\t\tleft: \"the quick brown fox\", right: \"the slow brown fox\",\n\t\t\tleft_regions:  []Region{{4, 5}},\n\t\t\tright_regions: []Region{{4, 4}},\n\t\t},\n\t\t{\n\t\t\tleft: \"hello world\", right: \"hello world\",\n\t\t\tleft_regions:  nil,\n\t\t\tright_regions: nil,\n\t\t},\n\t\t{\n\t\t\t// word count equal, single substitution at index 1 → positional pair\n\t\t\t// \"bar\" vs \"qux\": no common chars → full words\n\t\t\tleft: \"foo bar baz\", right: \"foo qux baz\",\n\t\t\tleft_regions:  []Region{{4, 3}},\n\t\t\tright_regions: []Region{{4, 3}},\n\t\t},\n\t\t{\n\t\t\t// left has 3 words, right has 4 → word counts differ with unmatched\n\t\t\t// changed words → fall back to changed_center\n\t\t\t// changed_center gives: offset=4 (common \"aaa \"), suffix=\"ccc\"(4)\n\t\t\t// left_size=3 (\"bbb\"), right_size=7 (\"xxx yyy\")\n\t\t\tleft: \"aaa bbb ccc\", right: \"aaa xxx yyy ccc\",\n\t\t\tleft_regions:  []Region{{4, 3}},\n\t\t\tright_regions: []Region{{4, 7}},\n\t\t},\n\t\t{\n\t\t\t// word on left deleted: unmatched changed word → fall back to changed_center\n\t\t\t// changed_center: prefix=\"aaa \"(4), suffix=\" ccc ddd\"(8)\n\t\t\t// left_size=3 (\"bbb\"), right_size=-1 → nil\n\t\t\tleft: \"aaa bbb ccc ddd\", right: \"aaa ccc ddd\",\n\t\t\tleft_regions:  []Region{{4, 3}},\n\t\t\tright_regions: nil,\n\t\t},\n\t\t{\n\t\t\t// word counts equal, single substitution at index 3 → positional pair\n\t\t\t// \"fox\" vs \"cat\": no common chars → full words\n\t\t\tleft: \"the quick brown fox over the lazy dog\", right: \"the quick brown cat over the lazy dog\",\n\t\t\tleft_regions:  []Region{{16, 3}},\n\t\t\tright_regions: []Region{{16, 3}},\n\t\t},\n\t\t{\n\t\t\t// single word, positional pair with common char prefix \"version\" (7)\n\t\t\tleft:          \"version1\",\n\t\t\tright:         \"version2\",\n\t\t\tleft_regions:  []Region{{7, 1}},\n\t\t\tright_regions: []Region{{7, 1}},\n\t\t},\n\t\t{\n\t\t\t// positional pair at index 1, char prefix=\"prefix\"(6), suffix=\"suffix\"(6)\n\t\t\t// word at offset 7 → highlight offset 13, size 2\n\t\t\tleft:          \"update prefixABsuffix done\",\n\t\t\tright:         \"update prefixCDsuffix done\",\n\t\t\tleft_regions:  []Region{{13, 2}},\n\t\t\tright_regions: []Region{{13, 2}},\n\t\t},\n\t\t{\n\t\t\t// equal word count, multiple positional pairs (all words changed)\n\t\t\t// each pair has no common chars → full-word regions\n\t\t\tleft: \"aaa bbb ccc\", right: \"xxx yyy zzz\",\n\t\t\tleft_regions:  []Region{{0, 3}, {4, 3}, {8, 3}},\n\t\t\tright_regions: []Region{{0, 3}, {4, 3}, {8, 3}},\n\t\t},\n\t\t{\n\t\t\t// pure insertion on right side → unmatched changed word → fall back to changed_center\n\t\t\t// changed_center finds common prefix \"aaa bbb ccc\" (11 bytes) and suffix \"\";\n\t\t\t// left_size=0 (nil), right_size=4 (\" ddd\" inserted at the end)\n\t\t\tleft: \"aaa bbb ccc\", right: \"aaa bbb ccc ddd\",\n\t\t\tleft_regions:  nil,\n\t\t\tright_regions: []Region{{11, 4}},\n\t\t},\n\t}\n\tfor _, tc := range tests {\n\t\tc := word_diff_center(tc.left, tc.right, re)\n\t\tif diff := cmp.Diff(tc.left_regions, c.left_regions, region_eq); diff != \"\" {\n\t\t\tt.Errorf(\"word_diff_center(%q, %q) left_regions mismatch: %s\", tc.left, tc.right, diff)\n\t\t}\n\t\tif diff := cmp.Diff(tc.right_regions, c.right_regions, region_eq); diff != \"\" {\n\t\t\tt.Errorf(\"word_diff_center(%q, %q) right_regions mismatch: %s\", tc.left, tc.right, diff)\n\t\t}\n\t}\n}\n\nfunc TestChangedCenter(t *testing.T) {\n\ttype tc struct {\n\t\tleft, right   string\n\t\tleft_regions  []Region\n\t\tright_regions []Region\n\t}\n\ttests := []tc{\n\t\t{\n\t\t\tleft: \"the quick brown fox\", right: \"the slow brown fox\",\n\t\t\tleft_regions:  []Region{{4, 5}},\n\t\t\tright_regions: []Region{{4, 4}},\n\t\t},\n\t\t{\n\t\t\tleft: \"hello world\", right: \"hello world\",\n\t\t\tleft_regions:  nil,\n\t\t\tright_regions: nil,\n\t\t},\n\t}\n\tfor _, tc := range tests {\n\t\tc := changed_center(tc.left, tc.right)\n\t\tif diff := cmp.Diff(tc.left_regions, c.left_regions, region_eq); diff != \"\" {\n\t\t\tt.Errorf(\"changed_center(%q, %q) left_regions mismatch: %s\", tc.left, tc.right, diff)\n\t\t}\n\t\tif diff := cmp.Diff(tc.right_regions, c.right_regions, region_eq); diff != \"\" {\n\t\t\tt.Errorf(\"changed_center(%q, %q) right_regions mismatch: %s\", tc.left, tc.right, diff)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "kittens/diff/render.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/sgr\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype LineType int\n\nconst (\n\tTITLE_LINE LineType = iota\n\tCHANGE_LINE\n\tCONTEXT_LINE\n\tHUNK_TITLE_LINE\n\tIMAGE_LINE\n\tEMPTY_LINE\n)\n\ntype Reference struct {\n\tpath    string\n\tlinenum int // 1 based\n}\n\ntype HalfScreenLine struct {\n\tmarked_up_margin_text string\n\tmarked_up_text        string\n\tis_filler             bool\n\tis_moved              bool\n\tcached_wcswidth       int\n}\n\nfunc (self *HalfScreenLine) wcswidth() int {\n\tif self.cached_wcswidth == 0 && self.marked_up_text != \"\" {\n\t\tself.cached_wcswidth = wcswidth.Stringwidth(self.marked_up_text)\n\t}\n\treturn self.cached_wcswidth\n}\n\ntype ScreenLine struct {\n\tleft, right HalfScreenLine\n}\n\ntype LogicalLine struct {\n\tline_type                       LineType\n\tscreen_lines                    []*ScreenLine\n\tis_full_width                   bool\n\tis_change_start                 bool\n\tleft_reference, right_reference Reference\n\tleft_image, right_image         struct {\n\t\tkey   string\n\t\tcount int\n\t}\n\timage_lines_offset int\n}\n\nfunc (self *LogicalLine) render_screen_line(n int, lp *loop.Loop, margin_size, columns int) {\n\tif n >= len(self.screen_lines) || n < 0 {\n\t\treturn\n\t}\n\tsl := self.screen_lines[n]\n\tavailable_cols := columns/2 - margin_size\n\tif self.is_full_width {\n\t\tavailable_cols = columns - margin_size\n\t}\n\tleft_margin := place_in(sl.left.marked_up_margin_text, margin_size)\n\tleft_text := place_in(sl.left.marked_up_text, available_cols)\n\tif sl.left.is_filler {\n\t\tleft_margin = format_as_sgr.margin_filler + left_margin\n\t\tleft_text = format_as_sgr.filler + left_text\n\t} else if sl.left.is_moved {\n\t\tleft_margin = format_as_sgr.moved_margin + left_margin\n\t\tleft_text = format_as_sgr.moved + left_text\n\t} else {\n\t\tswitch self.line_type {\n\t\tcase CHANGE_LINE, IMAGE_LINE:\n\t\t\tleft_margin = format_as_sgr.removed_margin + left_margin\n\t\t\tleft_text = format_as_sgr.removed + left_text\n\t\tcase HUNK_TITLE_LINE:\n\t\t\tleft_margin = format_as_sgr.hunk_margin + left_margin\n\t\t\tleft_text = format_as_sgr.hunk + left_text\n\t\tcase TITLE_LINE:\n\t\tdefault:\n\t\t\tleft_margin = format_as_sgr.margin + left_margin\n\t\t}\n\t}\n\tlp.QueueWriteString(left_margin + \"\\x1b[m\")\n\tlp.QueueWriteString(left_text)\n\tif self.is_full_width {\n\t\treturn\n\t}\n\tright_margin := place_in(sl.right.marked_up_margin_text, margin_size)\n\tright_text := place_in(sl.right.marked_up_text, available_cols)\n\tif sl.right.is_filler {\n\t\tright_margin = format_as_sgr.margin_filler + right_margin\n\t\tright_text = format_as_sgr.filler + right_text\n\t} else if sl.right.is_moved {\n\t\tright_margin = format_as_sgr.moved_margin + right_margin\n\t\tright_text = format_as_sgr.moved + right_text\n\t} else {\n\t\tswitch self.line_type {\n\t\tcase CHANGE_LINE, IMAGE_LINE:\n\t\t\tright_margin = format_as_sgr.added_margin + right_margin\n\t\t\tright_text = format_as_sgr.added + right_text\n\t\tcase HUNK_TITLE_LINE:\n\t\t\tright_margin = format_as_sgr.hunk_margin + right_margin\n\t\t\tright_text = format_as_sgr.hunk + right_text\n\t\tcase TITLE_LINE:\n\t\tdefault:\n\t\t\tright_margin = format_as_sgr.margin + right_margin\n\t\t}\n\t}\n\tlp.QueueWriteString(\"\\x1b[m\\r\")\n\tlp.MoveCursorHorizontally(available_cols + margin_size)\n\tlp.QueueWriteString(right_margin + \"\\x1b[m\")\n\tlp.QueueWriteString(right_text)\n}\n\nfunc (self *LogicalLine) IncrementScrollPosBy(pos *ScrollPos, amt int) (delta int) {\n\tif len(self.screen_lines) > 0 {\n\t\tnpos := utils.Max(0, utils.Min(pos.screen_line+amt, len(self.screen_lines)-1))\n\t\tdelta = npos - pos.screen_line\n\t\tpos.screen_line = npos\n\t}\n\treturn\n}\n\nfunc fit_in(text string, count int) string {\n\ttruncated := wcswidth.TruncateToVisualLength(text, count)\n\tif len(truncated) >= len(text) {\n\t\treturn text\n\t}\n\tif count > 1 {\n\t\ttruncated = wcswidth.TruncateToVisualLength(text, count-1)\n\t}\n\treturn truncated + `…`\n}\n\nfunc fill_in(text string, sz int) string {\n\tw := wcswidth.Stringwidth(text)\n\tif w < sz {\n\t\ttext += strings.Repeat(` `, (sz - w))\n\t}\n\treturn text\n}\n\nfunc place_in(text string, sz int) string {\n\treturn fill_in(fit_in(text, sz), sz)\n}\n\nvar format_as_sgr struct {\n\ttitle, margin, added, removed, added_margin, removed_margin, filler, margin_filler, hunk_margin, hunk, selection, search, moved, moved_margin string\n}\n\nvar statusline_format, added_count_format, removed_count_format, message_format func(...any) string\nvar use_light_colors bool = false\n\ntype ResolvedColors struct {\n\tAdded_bg             style.RGBA\n\tAdded_margin_bg      style.RGBA\n\tBackground           style.RGBA\n\tFiller_bg            style.RGBA\n\tForeground           style.RGBA\n\tHighlight_added_bg   style.RGBA\n\tHighlight_removed_bg style.RGBA\n\tHunk_bg              style.RGBA\n\tHunk_margin_bg       style.RGBA\n\tMargin_bg            style.RGBA\n\tMargin_fg            style.RGBA\n\tMargin_filler_bg     style.NullableColor\n\tMoved_bg             style.RGBA\n\tMoved_margin_bg      style.RGBA\n\tRemoved_bg           style.RGBA\n\tRemoved_margin_bg    style.RGBA\n\tSearch_bg            style.RGBA\n\tSearch_fg            style.RGBA\n\tSelect_bg            style.RGBA\n\tSelect_fg            style.NullableColor\n\tTitle_bg             style.RGBA\n\tTitle_fg             style.RGBA\n}\n\nvar resolved_colors ResolvedColors\n\nfunc create_formatters() {\n\trc := &resolved_colors\n\tif !use_light_colors {\n\t\trc.Added_bg = conf.Dark_added_bg\n\t\trc.Added_margin_bg = conf.Dark_added_margin_bg\n\t\trc.Background = conf.Dark_background\n\t\trc.Filler_bg = conf.Dark_filler_bg\n\t\trc.Foreground = conf.Dark_foreground\n\t\trc.Highlight_added_bg = conf.Dark_highlight_added_bg\n\t\trc.Highlight_removed_bg = conf.Dark_highlight_removed_bg\n\t\trc.Hunk_bg = conf.Dark_hunk_bg\n\t\trc.Hunk_margin_bg = conf.Dark_hunk_margin_bg\n\t\trc.Margin_bg = conf.Dark_margin_bg\n\t\trc.Margin_fg = conf.Dark_margin_fg\n\t\trc.Margin_filler_bg = conf.Dark_margin_filler_bg\n\t\trc.Moved_bg = conf.Dark_moved_bg\n\t\trc.Moved_margin_bg = conf.Dark_moved_margin_bg\n\t\trc.Removed_bg = conf.Dark_removed_bg\n\t\trc.Removed_margin_bg = conf.Dark_removed_margin_bg\n\t\trc.Search_bg = conf.Dark_search_bg\n\t\trc.Search_fg = conf.Dark_search_fg\n\t\trc.Select_bg = conf.Dark_select_bg\n\t\trc.Select_fg = conf.Dark_select_fg\n\t\trc.Title_bg = conf.Dark_title_bg\n\t\trc.Title_fg = conf.Dark_title_fg\n\t} else {\n\t\trc.Added_bg = conf.Added_bg\n\t\trc.Added_margin_bg = conf.Added_margin_bg\n\t\trc.Background = conf.Background\n\t\trc.Filler_bg = conf.Filler_bg\n\t\trc.Foreground = conf.Foreground\n\t\trc.Highlight_added_bg = conf.Highlight_added_bg\n\t\trc.Highlight_removed_bg = conf.Highlight_removed_bg\n\t\trc.Hunk_bg = conf.Hunk_bg\n\t\trc.Hunk_margin_bg = conf.Hunk_margin_bg\n\t\trc.Margin_bg = conf.Margin_bg\n\t\trc.Margin_fg = conf.Margin_fg\n\t\trc.Margin_filler_bg = conf.Margin_filler_bg\n\t\trc.Moved_bg = conf.Moved_bg\n\t\trc.Moved_margin_bg = conf.Moved_margin_bg\n\t\trc.Removed_bg = conf.Removed_bg\n\t\trc.Removed_margin_bg = conf.Removed_margin_bg\n\t\trc.Search_bg = conf.Search_bg\n\t\trc.Search_fg = conf.Search_fg\n\t\trc.Select_bg = conf.Select_bg\n\t\trc.Select_fg = conf.Select_fg\n\t\trc.Title_bg = conf.Title_bg\n\t\trc.Title_fg = conf.Title_fg\n\t}\n\tctx := style.Context{AllowEscapeCodes: true}\n\tonly_open := func(x string) string {\n\t\tans := ctx.SprintFunc(x)(\"|\")\n\t\tans, _, _ = strings.Cut(ans, \"|\")\n\t\treturn ans\n\t}\n\tformat_as_sgr.filler = only_open(\"bg=\" + rc.Filler_bg.AsRGBSharp())\n\tif rc.Margin_filler_bg.IsSet {\n\t\tformat_as_sgr.margin_filler = only_open(\"bg=\" + rc.Margin_filler_bg.Color.AsRGBSharp())\n\t} else {\n\t\tformat_as_sgr.margin_filler = only_open(\"bg=\" + rc.Filler_bg.AsRGBSharp())\n\t}\n\tformat_as_sgr.added = only_open(\"bg=\" + rc.Added_bg.AsRGBSharp())\n\tformat_as_sgr.added_margin = only_open(fmt.Sprintf(\"fg=%s bg=%s\", rc.Margin_fg.AsRGBSharp(), rc.Added_margin_bg.AsRGBSharp()))\n\tformat_as_sgr.removed = only_open(\"bg=\" + rc.Removed_bg.AsRGBSharp())\n\tformat_as_sgr.removed_margin = only_open(fmt.Sprintf(\"fg=%s bg=%s\", rc.Margin_fg.AsRGBSharp(), rc.Removed_margin_bg.AsRGBSharp()))\n\tformat_as_sgr.moved = only_open(\"bg=\" + rc.Moved_bg.AsRGBSharp())\n\tformat_as_sgr.moved_margin = only_open(fmt.Sprintf(\"fg=%s bg=%s\", rc.Margin_fg.AsRGBSharp(), rc.Moved_margin_bg.AsRGBSharp()))\n\tformat_as_sgr.title = only_open(fmt.Sprintf(\"fg=%s bg=%s bold\", rc.Title_fg.AsRGBSharp(), rc.Title_bg.AsRGBSharp()))\n\tformat_as_sgr.margin = only_open(fmt.Sprintf(\"fg=%s bg=%s\", rc.Margin_fg.AsRGBSharp(), rc.Margin_bg.AsRGBSharp()))\n\tformat_as_sgr.hunk = only_open(fmt.Sprintf(\"fg=%s bg=%s\", rc.Margin_fg.AsRGBSharp(), rc.Hunk_bg.AsRGBSharp()))\n\tformat_as_sgr.hunk_margin = only_open(fmt.Sprintf(\"fg=%s bg=%s\", rc.Margin_fg.AsRGBSharp(), rc.Hunk_margin_bg.AsRGBSharp()))\n\tformat_as_sgr.search = only_open(fmt.Sprintf(\"fg=%s bg=%s\", rc.Search_fg.AsRGBSharp(), rc.Search_bg.AsRGBSharp()))\n\tstatusline_format = ctx.SprintFunc(fmt.Sprintf(\"fg=%s\", rc.Margin_fg.AsRGBSharp()))\n\tadded_count_format = ctx.SprintFunc(fmt.Sprintf(\"fg=%s\", rc.Highlight_added_bg.AsRGBSharp()))\n\tremoved_count_format = ctx.SprintFunc(fmt.Sprintf(\"fg=%s\", rc.Highlight_removed_bg.AsRGBSharp()))\n\tmessage_format = ctx.SprintFunc(\"bold\")\n\tif rc.Select_fg.IsSet {\n\t\tformat_as_sgr.selection = only_open(fmt.Sprintf(\"fg=%s bg=%s\", rc.Select_fg.Color.AsRGBSharp(), rc.Select_bg.AsRGBSharp()))\n\t} else {\n\t\tformat_as_sgr.selection = only_open(\"bg=\" + rc.Select_bg.AsRGBSharp())\n\t}\n}\n\nfunc center_span(ltype string, offset, size int) *sgr.Span {\n\tans := sgr.NewSpan(offset, size)\n\tswitch ltype {\n\tcase \"add\":\n\t\tans.SetBackground(resolved_colors.Highlight_added_bg).SetClosingBackground(resolved_colors.Added_bg)\n\tcase \"remove\":\n\t\tans.SetBackground(resolved_colors.Highlight_removed_bg).SetClosingBackground(resolved_colors.Removed_bg)\n\t}\n\treturn ans\n}\n\nfunc title_lines(left_path, right_path string, columns, margin_size int, ans []*LogicalLine) []*LogicalLine {\n\tleft_name, right_name := path_name_map[left_path], path_name_map[right_path]\n\tavailable_cols := columns/2 - margin_size\n\tll := LogicalLine{\n\t\tline_type:      TITLE_LINE,\n\t\tleft_reference: Reference{path: left_path}, right_reference: Reference{path: right_path},\n\t}\n\tsl := ScreenLine{}\n\tif right_name != \"\" && right_name != left_name {\n\t\tsl.left.marked_up_text = format_as_sgr.title + fit_in(sanitize(left_name), available_cols)\n\t\tsl.right.marked_up_text = format_as_sgr.title + fit_in(sanitize(right_name), available_cols)\n\t} else {\n\t\tsl.left.marked_up_text = format_as_sgr.title + fit_in(sanitize(left_name), columns-margin_size)\n\t\tll.is_full_width = true\n\t}\n\tl2 := ll\n\tl2.line_type = EMPTY_LINE\n\tll.screen_lines = append(ll.screen_lines, &sl)\n\tsl2 := ScreenLine{}\n\tsl2.left.marked_up_margin_text = \"\\x1b[m\" + strings.Repeat(\"━\", margin_size)\n\tsl2.left.marked_up_text = strings.Repeat(\"━\", columns-margin_size)\n\tl2.is_full_width = true\n\tl2.screen_lines = append(l2.screen_lines, &sl2)\n\treturn append(ans, &ll, &l2)\n}\n\ntype LogicalLines struct {\n\tlines                []*LogicalLine\n\tmargin_size, columns int\n}\n\nfunc (self *LogicalLines) At(i int) *LogicalLine { return self.lines[i] }\n\nfunc (self *LogicalLines) ScreenLineAt(pos ScrollPos) *ScreenLine {\n\tif pos.logical_line < len(self.lines) && pos.logical_line >= 0 {\n\t\tline := self.lines[pos.logical_line]\n\t\tif pos.screen_line < len(line.screen_lines) && pos.screen_line >= 0 {\n\t\t\treturn self.lines[pos.logical_line].screen_lines[pos.screen_line]\n\t\t}\n\t}\n\treturn nil\n}\nfunc (self *LogicalLines) Len() int { return len(self.lines) }\n\nfunc (self *LogicalLines) NumScreenLinesTo(a ScrollPos) (ans int) {\n\treturn self.Minus(a, ScrollPos{})\n}\n\n// a - b in terms of number of screen lines between the positions\nfunc (self *LogicalLines) Minus(a, b ScrollPos) (delta int) {\n\tif a.logical_line == b.logical_line {\n\t\treturn a.screen_line - b.screen_line\n\t}\n\tamt := 1\n\tif a.Less(b) {\n\t\tamt = -1\n\t} else {\n\t\ta, b = b, a\n\t}\n\tfor i := a.logical_line; i < utils.Min(len(self.lines), b.logical_line+1); i++ {\n\t\tline := self.lines[i]\n\t\tswitch i {\n\t\tcase a.logical_line:\n\t\t\tdelta += utils.Max(0, len(line.screen_lines)-a.screen_line)\n\t\tcase b.logical_line:\n\t\t\tdelta += b.screen_line\n\t\tdefault:\n\t\t\tdelta += len(line.screen_lines)\n\t\t}\n\t}\n\treturn delta * amt\n}\n\nfunc (self *LogicalLines) IncrementScrollPosBy(pos *ScrollPos, amt int) (delta int) {\n\tif pos.logical_line < 0 || pos.logical_line >= len(self.lines) || amt == 0 {\n\t\treturn\n\t}\n\tone := 1\n\tif amt < 0 {\n\t\tone = -1\n\t}\n\tfor amt != 0 {\n\t\tline := self.lines[pos.logical_line]\n\t\td := line.IncrementScrollPosBy(pos, amt)\n\t\tif d == 0 {\n\t\t\tnlp := pos.logical_line + one\n\t\t\tif nlp < 0 || nlp >= len(self.lines) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tpos.logical_line = nlp\n\t\t\tif one > 0 {\n\t\t\t\tpos.screen_line = 0\n\t\t\t} else {\n\t\t\t\tpos.screen_line = len(self.lines[nlp].screen_lines) - 1\n\t\t\t}\n\t\t\tdelta += one\n\t\t\tamt -= one\n\t\t} else {\n\t\t\tamt -= d\n\t\t\tdelta += d\n\t\t}\n\t}\n\treturn\n}\n\nfunc human_readable(size int64) string {\n\tdivisor, suffix := 1, \"B\"\n\tfor i, candidate := range []string{\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\"} {\n\t\tif size < (1 << ((i + 1) * 10)) {\n\t\t\tdivisor, suffix = (1 << (i * 10)), candidate\n\t\t\tbreak\n\t\t}\n\t}\n\tfs := float64(size) / float64(divisor)\n\ts := strconv.FormatFloat(fs, 'f', 2, 64)\n\tif idx := strings.Index(s, \".\"); idx > -1 {\n\t\ts = s[:idx+2]\n\t}\n\tif strings.HasSuffix(s, \".0\") || strings.HasSuffix(s, \".00\") {\n\t\tidx := strings.IndexByte(s, '.')\n\t\ts = s[:idx]\n\t}\n\treturn s + \" \" + suffix\n}\n\nfunc image_lines(left_path, right_path string, screen_size screen_size, margin_size int, image_size graphics.Size, ans []*LogicalLine) ([]*LogicalLine, error) {\n\tcolumns := screen_size.columns\n\tavailable_cols := columns/2 - margin_size\n\tll, err := first_binary_line(left_path, right_path, columns, margin_size, func(path string) (string, error) {\n\t\tsz, err := size_for_path(path)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\ttext := fmt.Sprintf(\"Size: %s\", human_readable(sz))\n\t\tres := image_collection.ResolutionOf(path)\n\t\tif res.Width > -1 {\n\t\t\ttext = fmt.Sprintf(\"Dimensions: %dx%d %s\", res.Width, res.Height, text)\n\t\t}\n\t\treturn text, nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tll.image_lines_offset = len(ll.screen_lines)\n\n\tdo_side := func(path string) []string {\n\t\tif path == \"\" {\n\t\t\treturn nil\n\t\t}\n\t\tsz, err := image_collection.GetSizeIfAvailable(path, image_size)\n\t\tif err == nil {\n\t\t\tcount := int(math.Ceil(float64(sz.Height) / float64(screen_size.cell_height)))\n\t\t\treturn utils.Repeat(\"\", count)\n\t\t}\n\t\tif errors.Is(err, graphics.ErrNotFound) {\n\t\t\treturn splitlines(\"Loading image...\", available_cols)\n\t\t}\n\t\treturn splitlines(fmt.Sprintf(\"%s\", err), available_cols)\n\t}\n\tleft_lines := do_side(left_path)\n\tif ll.left_image.count = len(left_lines); ll.left_image.count > 0 {\n\t\tll.left_image.key = left_path\n\t}\n\tright_lines := do_side(right_path)\n\tif ll.right_image.count = len(right_lines); ll.right_image.count > 0 {\n\t\tll.right_image.key = right_path\n\t}\n\tfor i := 0; i < utils.Max(len(left_lines), len(right_lines)); i++ {\n\t\tsl := ScreenLine{}\n\t\tif i < len(left_lines) {\n\t\t\tsl.left.marked_up_text = left_lines[i]\n\t\t} else {\n\t\t\tsl.left.is_filler = true\n\t\t}\n\t\tif i < len(right_lines) {\n\t\t\tsl.right.marked_up_text = right_lines[i]\n\t\t} else {\n\t\t\tsl.right.is_filler = true\n\t\t}\n\t\tll.screen_lines = append(ll.screen_lines, &sl)\n\t}\n\tll.line_type = IMAGE_LINE\n\treturn append(ans, ll), nil\n}\n\nfunc first_binary_line(left_path, right_path string, columns, margin_size int, renderer func(path string) (string, error)) (*LogicalLine, error) {\n\tavailable_cols := columns/2 - margin_size\n\tll := LogicalLine{\n\t\tis_change_start: true, line_type: CHANGE_LINE,\n\t\tleft_reference: Reference{path: left_path}, right_reference: Reference{path: right_path},\n\t}\n\tif left_path == \"\" {\n\t\tline, err := renderer(right_path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, x := range splitlines(line, available_cols) {\n\t\t\tsl := ScreenLine{}\n\t\t\tsl.right.marked_up_text = x\n\t\t\tsl.left.is_filler = true\n\t\t\tll.screen_lines = append(ll.screen_lines, &sl)\n\t\t}\n\t} else if right_path == \"\" {\n\t\tline, err := renderer(left_path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, x := range splitlines(line, available_cols) {\n\t\t\tsl := ScreenLine{}\n\t\t\tsl.right.is_filler = true\n\t\t\tsl.left.marked_up_text = x\n\t\t\tll.screen_lines = append(ll.screen_lines, &sl)\n\t\t}\n\t} else {\n\t\tl, err := renderer(left_path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tr, err := renderer(right_path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tleft_lines, right_lines := splitlines(l, available_cols), splitlines(r, available_cols)\n\t\tfor i := 0; i < utils.Max(len(left_lines), len(right_lines)); i++ {\n\t\t\tsl := ScreenLine{}\n\t\t\tif i < len(left_lines) {\n\t\t\t\tsl.left.marked_up_text = left_lines[i]\n\t\t\t}\n\t\t\tif i < len(right_lines) {\n\t\t\t\tsl.right.marked_up_text = right_lines[i]\n\t\t\t}\n\t\t\tll.screen_lines = append(ll.screen_lines, &sl)\n\t\t}\n\t}\n\treturn &ll, nil\n}\n\nfunc binary_lines(left_path, right_path string, columns, margin_size int, ans []*LogicalLine) (ans2 []*LogicalLine, err error) {\n\tll, err := first_binary_line(left_path, right_path, columns, margin_size, func(path string) (string, error) {\n\t\tsz, err := size_for_path(path)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn fmt.Sprintf(\"Binary file: %s\", human_readable(sz)), nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn append(ans, ll), nil\n}\n\ntype DiffData struct {\n\tleft_path, right_path       string\n\tavailable_cols, margin_size int\n\n\tleft_lines, right_lines []string\n\tleft_moved_lines        *utils.Set[int]\n\tright_moved_lines       *utils.Set[int]\n}\n\nfunc hunk_title(hunk *Hunk) string {\n\treturn fmt.Sprintf(\"@@ -%d,%d +%d,%d @@ %s\", hunk.left_start+1, hunk.left_count, hunk.right_start+1, hunk.right_count, hunk.title)\n}\n\nfunc lines_for_context_chunk(data *DiffData, _ int, chunk *Chunk, _ int, ans []*LogicalLine) []*LogicalLine {\n\tfor i := 0; i < chunk.left_count; i++ {\n\t\tleft_line_number := chunk.left_start + i\n\t\tright_line_number := chunk.right_start + i\n\t\tll := LogicalLine{line_type: CONTEXT_LINE,\n\t\t\tleft_reference:  Reference{path: data.left_path, linenum: left_line_number + 1},\n\t\t\tright_reference: Reference{path: data.right_path, linenum: right_line_number + 1},\n\t\t}\n\t\tleft_line_number_s := strconv.Itoa(left_line_number + 1)\n\t\tright_line_number_s := strconv.Itoa(right_line_number + 1)\n\t\tfor _, text := range splitlines(data.left_lines[left_line_number], data.available_cols) {\n\t\t\tleft_line := HalfScreenLine{marked_up_margin_text: left_line_number_s, marked_up_text: text}\n\t\t\tright_line := left_line\n\t\t\tif right_line_number_s != left_line_number_s {\n\t\t\t\tright_line = HalfScreenLine{marked_up_margin_text: right_line_number_s, marked_up_text: text}\n\t\t\t}\n\t\t\tll.screen_lines = append(ll.screen_lines, &ScreenLine{left_line, right_line})\n\t\t\tleft_line_number_s, right_line_number_s = \"\", \"\"\n\t\t}\n\t\tans = append(ans, &ll)\n\t}\n\treturn ans\n}\n\nfunc splitlines(text string, width int) []string {\n\treturn style.WrapTextAsLines(text, width, style.WrapOptions{})\n}\n\nfunc render_half_line(line_number int, line, ltype string, available_cols int, center Center, is_moved bool, ans []HalfScreenLine) []HalfScreenLine {\n\tvar regions []Region\n\tif ltype == \"remove\" {\n\t\tregions = center.left_regions\n\t} else {\n\t\tregions = center.right_regions\n\t}\n\tif len(regions) > 0 {\n\t\tspans := make([]*sgr.Span, len(regions))\n\t\tfor i, r := range regions {\n\t\t\tspans[i] = center_span(ltype, r.offset, r.size)\n\t\t}\n\t\tline = sgr.InsertFormatting(line, spans...)\n\t}\n\tlnum := strconv.Itoa(line_number + 1)\n\tfor _, sc := range splitlines(line, available_cols) {\n\t\tans = append(ans, HalfScreenLine{marked_up_margin_text: lnum, marked_up_text: sc, is_moved: is_moved})\n\t\tlnum = \"\"\n\t}\n\treturn ans\n}\n\nfunc lines_for_diff_chunk(data *DiffData, _ int, chunk *Chunk, _ int, ans []*LogicalLine) []*LogicalLine {\n\tcommon := utils.Min(chunk.left_count, chunk.right_count)\n\tll, rl := make([]HalfScreenLine, 0, 32), make([]HalfScreenLine, 0, 32)\n\tfor i := 0; i < utils.Max(chunk.left_count, chunk.right_count); i++ {\n\t\tll, rl = ll[:0], rl[:0]\n\t\tvar center Center\n\t\tleft_lnum, right_lnum := 0, 0\n\t\tif i < len(chunk.centers) {\n\t\t\tcenter = chunk.centers[i]\n\t\t}\n\t\tif i < chunk.left_count {\n\t\t\tleft_lnum = chunk.left_start + i\n\t\t\tleft_is_moved := data.left_moved_lines != nil && data.left_moved_lines.Has(left_lnum)\n\t\t\tll = render_half_line(left_lnum, data.left_lines[left_lnum], \"remove\", data.available_cols, center, left_is_moved, ll)\n\t\t\tleft_lnum++\n\t\t}\n\n\t\tif i < chunk.right_count {\n\t\t\tright_lnum = chunk.right_start + i\n\t\t\tright_is_moved := data.right_moved_lines != nil && data.right_moved_lines.Has(right_lnum)\n\t\t\trl = render_half_line(right_lnum, data.right_lines[right_lnum], \"add\", data.available_cols, center, right_is_moved, rl)\n\t\t\tright_lnum++\n\t\t}\n\n\t\tif i < common {\n\t\t\textra := len(ll) - len(rl)\n\t\t\tif extra < 0 {\n\t\t\t\tll = append(ll, utils.Repeat(HalfScreenLine{}, -extra)...)\n\t\t\t} else if extra > 0 {\n\t\t\t\trl = append(rl, utils.Repeat(HalfScreenLine{}, extra)...)\n\t\t\t}\n\t\t} else {\n\t\t\tif len(ll) > 0 {\n\t\t\t\trl = append(rl, utils.Repeat(HalfScreenLine{is_filler: true}, len(ll))...)\n\t\t\t} else if len(rl) > 0 {\n\t\t\t\tll = append(ll, utils.Repeat(HalfScreenLine{is_filler: true}, len(rl))...)\n\t\t\t}\n\t\t}\n\t\tlogline := LogicalLine{\n\t\t\tline_type: CHANGE_LINE, is_change_start: i == 0,\n\t\t\tleft_reference:  Reference{path: data.left_path, linenum: left_lnum},\n\t\t\tright_reference: Reference{path: data.left_path, linenum: right_lnum},\n\t\t}\n\t\tfor l := 0; l < len(ll); l++ {\n\t\t\tlogline.screen_lines = append(logline.screen_lines, &ScreenLine{left: ll[l], right: rl[l]})\n\t\t}\n\t\tans = append(ans, &logline)\n\t}\n\treturn ans\n}\n\nfunc lines_for_diff(left_path string, right_path string, patch *Patch, columns, margin_size int, ans []*LogicalLine) (result []*LogicalLine, err error) {\n\tht := LogicalLine{\n\t\tline_type:      HUNK_TITLE_LINE,\n\t\tleft_reference: Reference{path: left_path}, right_reference: Reference{path: right_path},\n\t\tis_full_width: true,\n\t}\n\tif patch.Len() == 0 {\n\t\ttxt := \"The files are identical\"\n\t\tif lstat, err := os.Stat(left_path); err == nil {\n\t\t\tif rstat, err := os.Stat(right_path); err == nil {\n\t\t\t\tif lstat.Mode() != rstat.Mode() {\n\t\t\t\t\ttxt = fmt.Sprintf(\"Mode changed: %s to %s\", lstat.Mode(), rstat.Mode())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor _, line := range splitlines(txt, columns-margin_size) {\n\t\t\tsl := ScreenLine{}\n\t\t\tsl.left.marked_up_text = line\n\t\t\tht.screen_lines = append(ht.screen_lines, &sl)\n\t\t}\n\t\tht.line_type = EMPTY_LINE\n\t\tht.is_full_width = true\n\t\treturn append(ans, &ht), nil\n\t}\n\tavailable_cols := columns/2 - margin_size\n\tdata := DiffData{\n\t\tleft_path: left_path, right_path: right_path, available_cols: available_cols, margin_size: margin_size,\n\t\tleft_moved_lines: patch.left_moved_lines, right_moved_lines: patch.right_moved_lines,\n\t}\n\tif left_path != \"\" {\n\t\tdata.left_lines, err = highlighted_lines_for_path(left_path)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif right_path != \"\" {\n\t\tdata.right_lines, err = highlighted_lines_for_path(right_path)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tfor hunk_num, hunk := range patch.all_hunks {\n\t\thtl := ht\n\t\thtl.left_reference.linenum = hunk.left_start + 1\n\t\thtl.right_reference.linenum = hunk.right_start + 1\n\t\tfor _, line := range splitlines(hunk_title(hunk), columns-margin_size) {\n\t\t\tsl := ScreenLine{}\n\t\t\tsl.left.marked_up_text = line\n\t\t\thtl.screen_lines = append(htl.screen_lines, &sl)\n\t\t}\n\t\tans = append(ans, &htl)\n\t\tfor cnum, chunk := range hunk.chunks {\n\t\t\tif chunk.is_context {\n\t\t\t\tans = lines_for_context_chunk(&data, hunk_num, chunk, cnum, ans)\n\t\t\t} else {\n\t\t\t\tans = lines_for_diff_chunk(&data, hunk_num, chunk, cnum, ans)\n\t\t\t}\n\t\t}\n\t}\n\treturn ans, nil\n}\n\nfunc all_lines(path string, columns, margin_size int, is_add bool, ans []*LogicalLine) ([]*LogicalLine, error) {\n\tavailable_cols := columns/2 - margin_size\n\tltype := `add`\n\tll := LogicalLine{line_type: CHANGE_LINE}\n\tif !is_add {\n\t\tltype = `remove`\n\t\tll.left_reference.path = path\n\t} else {\n\t\tll.right_reference.path = path\n\t}\n\tlines, err := highlighted_lines_for_path(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar msg_lines []string\n\tif is_add {\n\t\tmsg_lines = splitlines(`This file was added`, available_cols)\n\t} else {\n\t\tmsg_lines = splitlines(`This file was removed`, available_cols)\n\t}\n\tfor line_number, line := range lines {\n\t\thlines := make([]HalfScreenLine, 0, 8)\n\t\thlines = render_half_line(line_number, line, ltype, available_cols, Center{}, false, hlines)\n\t\tl := ll\n\t\tif is_add {\n\t\t\tl.right_reference.linenum = line_number + 1\n\t\t} else {\n\t\t\tl.left_reference.linenum = line_number + 1\n\t\t}\n\t\tl.is_change_start = line_number == 0\n\t\tfor i, hl := range hlines {\n\t\t\tsl := ScreenLine{}\n\t\t\tif is_add {\n\t\t\t\tsl.right = hl\n\t\t\t\tif len(msg_lines) > 0 {\n\t\t\t\t\tsl.left.marked_up_text = msg_lines[i]\n\t\t\t\t\tsl.left.is_filler = true\n\t\t\t\t\tmsg_lines = msg_lines[1:]\n\t\t\t\t} else {\n\t\t\t\t\tsl.left.is_filler = true\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsl.left = hl\n\t\t\t\tif len(msg_lines) > 0 {\n\t\t\t\t\tsl.right.marked_up_text = msg_lines[i]\n\t\t\t\t\tsl.right.is_filler = true\n\t\t\t\t\tmsg_lines = msg_lines[1:]\n\t\t\t\t} else {\n\t\t\t\t\tsl.right.is_filler = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tl.screen_lines = append(l.screen_lines, &sl)\n\t\t}\n\t\tans = append(ans, &l)\n\t}\n\treturn ans, nil\n}\n\nfunc rename_lines(path, other_path string, columns, margin_size int, ans []*LogicalLine) ([]*LogicalLine, error) {\n\tll := LogicalLine{\n\t\tleft_reference: Reference{path: path}, right_reference: Reference{path: other_path},\n\t\tline_type: CHANGE_LINE, is_change_start: true, is_full_width: true}\n\tfor _, line := range splitlines(fmt.Sprintf(`The file %s was renamed to %s`, sanitize(path_name_map[path]), sanitize(path_name_map[other_path])), columns-margin_size) {\n\t\tsl := ScreenLine{}\n\t\tsl.right.marked_up_text = line\n\t\tll.screen_lines = append(ll.screen_lines, &sl)\n\t}\n\treturn append(ans, &ll), nil\n}\n\nfunc render(collection *Collection, diff_map map[string]*Patch, screen_size screen_size, largest_line_number int, image_size graphics.Size) (result *LogicalLines, err error) {\n\tmargin_size := utils.Max(3, len(strconv.Itoa(largest_line_number))+1)\n\tans := make([]*LogicalLine, 0, 1024)\n\tcolumns := screen_size.columns\n\terr = collection.Apply(func(path, item_type, changed_path string) error {\n\t\tans = title_lines(path, changed_path, columns, margin_size, ans)\n\t\tdefer func() {\n\t\t\tans = append(ans, &LogicalLine{line_type: EMPTY_LINE, screen_lines: []*ScreenLine{{}}})\n\t\t}()\n\n\t\tis_binary := !is_path_text(path)\n\t\tif !is_binary && item_type == `diff` && !is_path_text(changed_path) {\n\t\t\tis_binary = true\n\t\t}\n\t\tis_img := is_binary && is_image(path) || (item_type == `diff` && is_image(changed_path))\n\t\t_ = is_img\n\t\tswitch item_type {\n\t\tcase \"diff\":\n\t\t\tif is_binary {\n\t\t\t\tif is_img {\n\t\t\t\t\tans, err = image_lines(path, changed_path, screen_size, margin_size, image_size, ans)\n\t\t\t\t} else {\n\t\t\t\t\tans, err = binary_lines(path, changed_path, columns, margin_size, ans)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tans, err = lines_for_diff(path, changed_path, diff_map[path], columns, margin_size, ans)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase \"add\":\n\t\t\tif is_binary {\n\t\t\t\tif is_img {\n\t\t\t\t\tans, err = image_lines(\"\", path, screen_size, margin_size, image_size, ans)\n\t\t\t\t} else {\n\t\t\t\t\tans, err = binary_lines(\"\", path, columns, margin_size, ans)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tans, err = all_lines(path, columns, margin_size, true, ans)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase \"removal\":\n\t\t\tif is_binary {\n\t\t\t\tif is_img {\n\t\t\t\t\tans, err = image_lines(path, \"\", screen_size, margin_size, image_size, ans)\n\t\t\t\t} else {\n\t\t\t\t\tans, err = binary_lines(path, \"\", columns, margin_size, ans)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tans, err = all_lines(path, columns, margin_size, false, ans)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase \"rename\":\n\t\t\tans, err = rename_lines(path, changed_path, columns, margin_size, ans)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"Unknown change type: %#v\", item_type)\n\t\t}\n\t\treturn nil\n\t})\n\tvar ll []*LogicalLine\n\tif len(ans) > 1 {\n\t\tll = ans[:len(ans)-1]\n\t} else {\n\t\t// Having am empty list of lines causes panics later on\n\t\tll = []*LogicalLine{{line_type: EMPTY_LINE, screen_lines: []*ScreenLine{{}}}}\n\t}\n\treturn &LogicalLines{lines: ll, margin_size: margin_size, columns: columns}, err\n}\n"
  },
  {
    "path": "kittens/diff/search.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype Search struct {\n\tpat     *regexp.Regexp\n\tmatches map[ScrollPos][]Span\n}\n\nfunc (self *Search) Len() int { return len(self.matches) }\n\nfunc (self *Search) find_matches_in_lines(clean_lines []string, origin int, send_result func(screen_line, offset, size int)) {\n\tlengths := utils.Map(func(x string) int { return len(x) }, clean_lines)\n\toffsets := make([]int, len(clean_lines))\n\tcell_lengths := utils.Map(wcswidth.Stringwidth, clean_lines)\n\tcell_offsets := make([]int, len(clean_lines))\n\tfor i := range clean_lines {\n\t\tif i > 0 {\n\t\t\toffsets[i] = offsets[i-1] + lengths[i-1]\n\t\t\tcell_offsets[i] = cell_offsets[i-1] + cell_lengths[i-1]\n\t\t}\n\t}\n\tjoined_text := strings.Join(clean_lines, \"\")\n\tmatches := self.pat.FindAllStringIndex(joined_text, -1)\n\tpos := 0\n\n\tfind_pos := func(start int) int {\n\t\tfor i := pos; i < len(clean_lines); i++ {\n\t\t\tif start < offsets[i]+lengths[i] {\n\t\t\t\tpos = i\n\t\t\t\treturn pos\n\t\t\t}\n\n\t\t}\n\t\treturn -1\n\t}\n\tfor _, m := range matches {\n\t\tstart, end := m[0], m[1]\n\t\ttotal_size := end - start\n\t\tif total_size < 1 {\n\t\t\tcontinue\n\t\t}\n\t\tstart_line := find_pos(start)\n\t\tif start_line > -1 {\n\t\t\tend_line := find_pos(end)\n\t\t\tif end_line > -1 {\n\t\t\t\tfor i := start_line; i <= end_line; i++ {\n\t\t\t\t\tcell_start := 0\n\t\t\t\t\tif i == start_line {\n\t\t\t\t\t\tbyte_offset := start - offsets[i]\n\t\t\t\t\t\tcell_start = wcswidth.Stringwidth(clean_lines[i][:byte_offset])\n\t\t\t\t\t}\n\t\t\t\t\tcell_end := cell_lengths[i]\n\t\t\t\t\tif i == end_line {\n\t\t\t\t\t\tbyte_offset := end - offsets[i]\n\t\t\t\t\t\tcell_end = wcswidth.Stringwidth(clean_lines[i][:byte_offset])\n\t\t\t\t\t}\n\t\t\t\t\tsend_result(i, origin+cell_start, cell_end-cell_start)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n\nfunc (self *Search) find_matches_in_line(line *LogicalLine, margin_size, cols int, send_result func(screen_line, offset, size int)) {\n\thalf_width := cols / 2\n\tright_offset := half_width + margin_size\n\tleft_clean_lines, right_clean_lines := make([]string, len(line.screen_lines)), make([]string, len(line.screen_lines))\n\tfor i, sl := range line.screen_lines {\n\t\tif line.is_full_width {\n\t\t\tleft_clean_lines[i] = wcswidth.StripEscapeCodes(sl.left.marked_up_text)\n\t\t} else {\n\t\t\tleft_clean_lines[i] = wcswidth.StripEscapeCodes(sl.left.marked_up_text)\n\t\t\tright_clean_lines[i] = wcswidth.StripEscapeCodes(sl.right.marked_up_text)\n\t\t}\n\t}\n\tself.find_matches_in_lines(left_clean_lines, margin_size, send_result)\n\tself.find_matches_in_lines(right_clean_lines, right_offset, send_result)\n}\n\nfunc (self *Search) Has(pos ScrollPos) bool {\n\treturn len(self.matches[pos]) > 0\n}\n\ntype Span struct{ start, end int }\n\nfunc (self *Search) search(logical_lines *LogicalLines) {\n\tmargin_size := logical_lines.margin_size\n\tcols := logical_lines.columns\n\tself.matches = make(map[ScrollPos][]Span)\n\tctx := images.Context{}\n\tmutex := sync.Mutex{}\n\tif err := ctx.SafeParallel(0, logical_lines.Len(), func(nums <-chan int) {\n\t\tfor i := range nums {\n\t\t\tline := logical_lines.At(i)\n\t\t\tif line.line_type == EMPTY_LINE || line.line_type == IMAGE_LINE {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tself.find_matches_in_line(line, margin_size, cols, func(screen_line, offset, size int) {\n\t\t\t\tif size > 0 {\n\t\t\t\t\tmutex.Lock()\n\t\t\t\t\tdefer mutex.Unlock()\n\t\t\t\t\tpos := ScrollPos{i, screen_line}\n\t\t\t\t\tself.matches[pos] = append(self.matches[pos], Span{offset, offset + size - 1})\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\tfor _, spans := range self.matches {\n\t\tslices.SortFunc(spans, func(a, b Span) int { return a.start - b.start })\n\t}\n}\n\nfunc (self *Search) markup_line(pos ScrollPos, y int) string {\n\tspans := self.matches[pos]\n\tif spans == nil {\n\t\treturn \"\"\n\t}\n\tsgr := format_as_sgr.search[2:]\n\tsgr = sgr[:len(sgr)-1]\n\tans := make([]byte, 0, 32)\n\tfor _, span := range spans {\n\t\tans = append(ans, tui.FormatPartOfLine(sgr, span.start, span.end, y)...)\n\t}\n\treturn utils.UnsafeBytesToString(ans)\n}\n\nfunc do_search(pat *regexp.Regexp, logical_lines *LogicalLines) *Search {\n\tans := &Search{pat: pat, matches: make(map[ScrollPos][]Span)}\n\tans.search(logical_lines)\n\treturn ans\n}\n"
  },
  {
    "path": "kittens/diff/ui.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage diff\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/readline\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype ResultType int\n\nconst (\n\tCOLLECTION ResultType = iota\n\tDIFF\n\tHIGHLIGHT\n\tIMAGE_LOAD\n\tIMAGE_RESIZE\n)\n\ntype ScrollPos struct {\n\tlogical_line, screen_line int\n}\n\nfunc (self ScrollPos) Less(other ScrollPos) bool {\n\treturn self.logical_line < other.logical_line || (self.logical_line == other.logical_line && self.screen_line < other.screen_line)\n}\n\nfunc (self ScrollPos) Add(other ScrollPos) ScrollPos {\n\treturn ScrollPos{self.logical_line + other.logical_line, self.screen_line + other.screen_line}\n}\n\ntype AsyncResult struct {\n\terr        error\n\trtype      ResultType\n\tcollection *Collection\n\tdiff_map   map[string]*Patch\n\tpage_size  graphics.Size\n}\n\nvar image_collection *graphics.ImageCollection\n\ntype screen_size struct{ rows, columns, num_lines, cell_width, cell_height int }\ntype Handler struct {\n\tasync_results                                       chan AsyncResult\n\tmouse_selection                                     tui.MouseSelection\n\timage_count                                         int\n\tshortcut_tracker                                    config.ShortcutTracker\n\tleft, right                                         string\n\tcollection                                          *Collection\n\tdiff_map                                            map[string]*Patch\n\tlogical_lines                                       *LogicalLines\n\tterminal_capabilities_received                      bool\n\tlp                                                  *loop.Loop\n\tcurrent_context_count, original_context_count       int\n\tadded_count, removed_count                          int\n\tscreen_size                                         screen_size\n\tscroll_pos, max_scroll_pos                          ScrollPos\n\trestore_position                                    *ScrollPos\n\tinputting_command                                   bool\n\tstatusline_message                                  string\n\trl                                                  *readline.Readline\n\tcurrent_search                                      *Search\n\tcurrent_search_is_regex, current_search_is_backward bool\n\tlargest_line_number                                 int\n\timages_resized_to                                   graphics.Size\n}\n\nfunc (self *Handler) calculate_statistics() {\n\tself.added_count, self.removed_count = self.collection.added_count, self.collection.removed_count\n\tself.largest_line_number = 0\n\tfor _, patch := range self.diff_map {\n\t\tself.added_count += patch.added_count\n\t\tself.removed_count += patch.removed_count\n\t\tself.largest_line_number = utils.Max(patch.largest_line_number, self.largest_line_number)\n\t}\n}\n\nfunc (self *Handler) update_screen_size(sz loop.ScreenSize) {\n\tself.screen_size.rows = int(sz.HeightCells)\n\tself.screen_size.columns = int(sz.WidthCells)\n\tself.screen_size.num_lines = self.screen_size.rows - 1\n\tself.screen_size.cell_height = int(sz.CellHeight)\n\tself.screen_size.cell_width = int(sz.CellWidth)\n}\n\nfunc (self *Handler) on_escape_code(etype loop.EscapeCodeType, payload []byte) error {\n\tswitch etype {\n\tcase loop.APC:\n\t\tgc := graphics.GraphicsCommandFromAPC(payload)\n\t\tif gc != nil {\n\t\t\tif !image_collection.HandleGraphicsCommand(gc) {\n\t\t\t\tself.draw_screen()\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *Handler) finalize() {\n\timage_collection.Finalize(self.lp)\n}\n\nfunc set_terminal_colors(lp *loop.Loop) {\n\tcreate_formatters()\n\tlp.SetDefaultColor(loop.FOREGROUND, resolved_colors.Foreground)\n\tlp.SetDefaultColor(loop.CURSOR, resolved_colors.Foreground)\n\tlp.SetDefaultColor(loop.BACKGROUND, resolved_colors.Background)\n\tlp.SetDefaultColor(loop.SELECTION_BG, resolved_colors.Select_bg)\n\tif resolved_colors.Select_fg.IsSet {\n\t\tlp.SetDefaultColor(loop.SELECTION_FG, resolved_colors.Select_fg.Color)\n\t}\n}\n\nfunc (self *Handler) on_capabilities_received(tc loop.TerminalCapabilities) {\n\tvar use_dark_colors bool\n\tprev := use_light_colors\n\tswitch conf.Color_scheme {\n\tcase Color_scheme_auto:\n\t\tuse_dark_colors = tc.ColorPreference != loop.LIGHT_COLOR_PREFERENCE\n\tcase Color_scheme_light:\n\t\tuse_dark_colors = false\n\tcase Color_scheme_dark:\n\t\tuse_dark_colors = true\n\t}\n\tuse_light_colors = !use_dark_colors\n\tif use_light_colors != prev && (light_highlight_started || dark_highlight_started) {\n\t\tself.highlight_all()\n\t}\n\tset_terminal_colors(self.lp)\n\tself.terminal_capabilities_received = true\n\tself.draw_screen()\n}\n\nfunc (self *Handler) on_color_scheme_change(cp loop.ColorPreference) error {\n\tif conf.Color_scheme != Color_scheme_auto {\n\t\treturn nil\n\t}\n\tlight := cp == loop.LIGHT_COLOR_PREFERENCE\n\tif use_light_colors != light {\n\t\tuse_light_colors = light\n\t\tset_terminal_colors(self.lp)\n\t\tself.highlight_all()\n\t\tself.draw_screen()\n\t}\n\treturn nil\n}\n\nfunc (self *Handler) initialize() {\n\tself.rl = readline.New(self.lp, readline.RlInit{DontMarkPrompts: true, Prompt: \"/\"})\n\tself.lp.OnEscapeCode = self.on_escape_code\n\tself.lp.OnColorSchemeChange = self.on_color_scheme_change\n\timage_collection = graphics.NewImageCollection()\n\tself.current_context_count = opts.Context\n\tif self.current_context_count < 0 {\n\t\tself.current_context_count = int(conf.Num_context_lines)\n\t}\n\tsz, _ := self.lp.ScreenSize()\n\tself.update_screen_size(sz)\n\tself.original_context_count = self.current_context_count\n\tself.async_results = make(chan AsyncResult, 32)\n\tgo func() {\n\t\tself.lp.RecoverFromPanicInGoRoutine()\n\t\tr := AsyncResult{}\n\t\tr.collection, r.err = create_collection(self.left, self.right)\n\t\tself.async_results <- r\n\t\tself.lp.WakeupMainThread()\n\t}()\n\tself.draw_screen()\n}\n\nfunc (self *Handler) generate_diff() {\n\tself.diff_map = nil\n\tjobs := make([]diff_job, 0, 32)\n\t_ = self.collection.Apply(func(path, typ, changed_path string) error {\n\t\tif typ == \"diff\" {\n\t\t\tif is_path_text(path) && is_path_text(changed_path) {\n\t\t\t\tjobs = append(jobs, diff_job{path, changed_path})\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tgo func() {\n\t\tself.lp.RecoverFromPanicInGoRoutine()\n\t\tr := AsyncResult{rtype: DIFF}\n\t\tr.diff_map, r.err = diff(jobs, self.current_context_count)\n\t\tself.async_results <- r\n\t\tself.lp.WakeupMainThread()\n\t}()\n}\n\nfunc (self *Handler) on_wakeup() error {\n\tvar r AsyncResult\n\tfor {\n\t\tselect {\n\t\tcase r = <-self.async_results:\n\t\t\tif r.err != nil {\n\t\t\t\treturn r.err\n\t\t\t}\n\t\t\tr.err = self.handle_async_result(r)\n\t\t\tif r.err != nil {\n\t\t\t\treturn r.err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nvar dark_highlight_started bool\nvar light_highlight_started bool\n\nfunc (self *Handler) highlight_all() {\n\tif (use_light_colors && light_highlight_started) || (!use_light_colors && dark_highlight_started) {\n\t\treturn\n\t}\n\tif use_light_colors {\n\t\tlight_highlight_started = true\n\t} else {\n\t\tdark_highlight_started = true\n\t}\n\ttext_files := utils.Filter(self.collection.paths_to_highlight.AsSlice(), is_path_text)\n\tgo func() {\n\t\tself.lp.RecoverFromPanicInGoRoutine()\n\t\tr := AsyncResult{rtype: HIGHLIGHT}\n\t\thighlight_all(text_files, use_light_colors)\n\t\tself.async_results <- r\n\t\tself.lp.WakeupMainThread()\n\t}()\n}\n\nfunc (self *Handler) load_all_images() {\n\t_ = self.collection.Apply(func(path, item_type, changed_path string) error {\n\t\tif path != \"\" && is_image(path) {\n\t\t\timage_collection.AddPaths(path)\n\t\t\tself.image_count++\n\t\t}\n\t\tif changed_path != \"\" && is_image(changed_path) {\n\t\t\timage_collection.AddPaths(changed_path)\n\t\t\tself.image_count++\n\t\t}\n\t\treturn nil\n\t})\n\tif self.image_count > 0 {\n\t\timage_collection.Initialize(self.lp)\n\t\tgo func() {\n\t\t\tself.lp.RecoverFromPanicInGoRoutine()\n\t\t\tr := AsyncResult{rtype: IMAGE_LOAD}\n\t\t\timage_collection.LoadAll()\n\t\t\tself.async_results <- r\n\t\t\tself.lp.WakeupMainThread()\n\t\t}()\n\t}\n}\n\nfunc (self *Handler) resize_all_images_if_needed() {\n\tif self.logical_lines == nil {\n\t\treturn\n\t}\n\tmargin_size := self.logical_lines.margin_size\n\tcolumns := self.logical_lines.columns\n\tavailable_cols := columns/2 - margin_size\n\tsz := graphics.Size{\n\t\tWidth:  available_cols * self.screen_size.cell_width,\n\t\tHeight: self.screen_size.num_lines * 2 * self.screen_size.cell_height,\n\t}\n\tif sz != self.images_resized_to && self.image_count > 0 {\n\t\tgo func() {\n\t\t\tself.lp.RecoverFromPanicInGoRoutine()\n\t\t\timage_collection.ResizeForPageSize(sz.Width, sz.Height)\n\t\t\tr := AsyncResult{rtype: IMAGE_RESIZE, page_size: sz}\n\t\t\tself.async_results <- r\n\t\t\tself.lp.WakeupMainThread()\n\t\t}()\n\t}\n}\n\nfunc (self *Handler) rerender_diff() error {\n\tif self.diff_map != nil && self.collection != nil && self.terminal_capabilities_received {\n\t\terr := self.render_diff()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tself.draw_screen()\n\t}\n\treturn nil\n}\n\nfunc (self *Handler) handle_async_result(r AsyncResult) error {\n\tswitch r.rtype {\n\tcase COLLECTION:\n\t\tself.collection = r.collection\n\t\tself.generate_diff()\n\t\tself.highlight_all()\n\t\tself.load_all_images()\n\tcase DIFF:\n\t\tif !self.terminal_capabilities_received {\n\t\t\tgo func() {\n\t\t\t\tself.async_results <- r\n\t\t\t\tself.lp.WakeupMainThread()\n\t\t\t}()\n\t\t\treturn nil\n\t\t}\n\t\tself.diff_map = r.diff_map\n\t\tself.calculate_statistics()\n\t\tself.clear_mouse_selection()\n\t\terr := self.render_diff()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tself.scroll_pos = ScrollPos{}\n\t\tif self.restore_position != nil {\n\t\t\tself.scroll_pos = *self.restore_position\n\t\t\tif self.max_scroll_pos.Less(self.scroll_pos) {\n\t\t\t\tself.scroll_pos = self.max_scroll_pos\n\t\t\t}\n\t\t\tself.restore_position = nil\n\t\t}\n\t\tself.draw_screen()\n\tcase IMAGE_RESIZE:\n\t\tself.images_resized_to = r.page_size\n\t\treturn self.rerender_diff()\n\tcase IMAGE_LOAD, HIGHLIGHT:\n\t\treturn self.rerender_diff()\n\t}\n\treturn nil\n}\n\nfunc (self *Handler) on_resize(old_size, new_size loop.ScreenSize) error {\n\tself.clear_mouse_selection()\n\tself.update_screen_size(new_size)\n\tif self.diff_map != nil && self.collection != nil && self.terminal_capabilities_received {\n\t\terr := self.render_diff()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif self.max_scroll_pos.Less(self.scroll_pos) {\n\t\t\tself.scroll_pos = self.max_scroll_pos\n\t\t}\n\t}\n\tself.draw_screen()\n\treturn nil\n}\n\nfunc (self *Handler) render_diff() (err error) {\n\tif self.screen_size.columns < 8 {\n\t\treturn fmt.Errorf(\"Screen too narrow, need at least 8 columns\")\n\t}\n\tif self.screen_size.rows < 2 {\n\t\treturn fmt.Errorf(\"Screen too short, need at least 2 rows\")\n\t}\n\tself.logical_lines, err = render(self.collection, self.diff_map, self.screen_size, self.largest_line_number, self.images_resized_to)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlast := self.logical_lines.Len() - 1\n\tself.max_scroll_pos.logical_line = last\n\tif last > -1 {\n\t\tself.max_scroll_pos.screen_line = len(self.logical_lines.At(last).screen_lines) - 1\n\t} else {\n\t\tself.max_scroll_pos.screen_line = 0\n\t}\n\tself.logical_lines.IncrementScrollPosBy(&self.max_scroll_pos, -self.screen_size.num_lines+1)\n\tif self.current_search != nil {\n\t\tself.current_search.search(self.logical_lines)\n\t}\n\treturn nil\n}\n\nfunc (self *Handler) draw_image(key string, _, starting_row int) {\n\timage_collection.PlaceImageSubRect(self.lp, key, self.images_resized_to, 0, self.screen_size.cell_height*starting_row, -1, -1)\n}\n\nfunc (self *Handler) draw_image_pair(ll *LogicalLine, starting_row int) {\n\tif ll.left_image.key == \"\" && ll.right_image.key == \"\" {\n\t\treturn\n\t}\n\tdefer self.lp.QueueWriteString(\"\\r\")\n\tif ll.left_image.key != \"\" {\n\t\tself.lp.QueueWriteString(\"\\r\")\n\t\tself.lp.MoveCursorHorizontally(self.logical_lines.margin_size)\n\t\tself.draw_image(ll.left_image.key, ll.left_image.count, starting_row)\n\t}\n\tif ll.right_image.key != \"\" {\n\t\tself.lp.QueueWriteString(\"\\r\")\n\t\tself.lp.MoveCursorHorizontally(self.logical_lines.margin_size + self.logical_lines.columns/2)\n\t\tself.draw_image(ll.right_image.key, ll.right_image.count, starting_row)\n\t}\n}\n\nfunc (self *Handler) draw_screen() {\n\tself.lp.StartAtomicUpdate()\n\tdefer self.lp.EndAtomicUpdate()\n\tif self.image_count > 0 {\n\t\tself.resize_all_images_if_needed()\n\t\timage_collection.DeleteAllVisiblePlacements(self.lp)\n\t}\n\tlp.MoveCursorTo(1, 1)\n\tlp.ClearToEndOfScreen()\n\tif self.logical_lines == nil || self.diff_map == nil || self.collection == nil || !self.terminal_capabilities_received {\n\t\tlp.Println(`Calculating diff, please wait...`)\n\t\treturn\n\t}\n\tpos := self.scroll_pos\n\tseen_images := utils.NewSet[int]()\n\tfor num_written := 0; num_written < self.screen_size.num_lines; num_written++ {\n\t\tll := self.logical_lines.At(pos.logical_line)\n\t\tif ll == nil || self.logical_lines.ScreenLineAt(pos) == nil {\n\t\t\tnum_written--\n\t\t} else {\n\t\t\tis_image := ll.line_type == IMAGE_LINE\n\t\t\tll.render_screen_line(pos.screen_line, lp, self.logical_lines.margin_size, self.logical_lines.columns)\n\t\t\tif is_image && !seen_images.Has(pos.logical_line) && pos.screen_line >= ll.image_lines_offset {\n\t\t\t\tseen_images.Add(pos.logical_line)\n\t\t\t\tself.draw_image_pair(ll, pos.screen_line-ll.image_lines_offset)\n\t\t\t}\n\t\t\tif self.current_search != nil {\n\t\t\t\tif mkp := self.current_search.markup_line(pos, num_written); mkp != \"\" {\n\t\t\t\t\tlp.QueueWriteString(mkp)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif mkp := self.add_mouse_selection_to_line(pos, num_written); mkp != \"\" {\n\t\t\t\tlp.QueueWriteString(mkp)\n\t\t\t}\n\t\t\tlp.MoveCursorVertically(1)\n\t\t\tlp.QueueWriteString(\"\\x1b[m\\r\")\n\t\t}\n\t\tif self.logical_lines.IncrementScrollPosBy(&pos, 1) == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\tself.draw_status_line()\n}\n\nfunc (self *Handler) draw_status_line() {\n\tif self.logical_lines == nil || self.diff_map == nil {\n\t\treturn\n\t}\n\tself.lp.MoveCursorTo(1, self.screen_size.rows)\n\tself.lp.ClearToEndOfLine()\n\tself.lp.SetCursorVisible(self.inputting_command)\n\tif self.inputting_command {\n\t\tself.rl.RedrawNonAtomic()\n\t} else if self.statusline_message != \"\" {\n\t\tself.lp.QueueWriteString(message_format(wcswidth.TruncateToVisualLength(sanitize(self.statusline_message), self.screen_size.columns)))\n\t} else {\n\t\tnum := self.logical_lines.NumScreenLinesTo(self.scroll_pos)\n\t\tden := self.logical_lines.NumScreenLinesTo(self.max_scroll_pos)\n\t\tvar frac int\n\t\tif den > 0 {\n\t\t\tfrac = int((float64(num) * 100.0) / float64(den))\n\t\t}\n\t\tsp := statusline_format(fmt.Sprintf(\"%d%%\", frac))\n\t\tvar counts string\n\t\tif self.current_search == nil {\n\t\t\tcounts = added_count_format(strconv.Itoa(self.added_count)) + statusline_format(`,`) + removed_count_format(strconv.Itoa(self.removed_count))\n\t\t} else {\n\t\t\tcounts = statusline_format(fmt.Sprintf(\"%d matches\", self.current_search.Len()))\n\t\t}\n\t\tsuffix := counts + \"  \" + sp\n\t\tprefix := statusline_format(\":\")\n\t\tfiller := strings.Repeat(\" \", utils.Max(0, self.screen_size.columns-wcswidth.Stringwidth(prefix)-wcswidth.Stringwidth(suffix)))\n\t\tself.lp.QueueWriteString(prefix + filler + suffix)\n\t}\n}\n\nfunc (self *Handler) on_text(text string, a, b bool) error {\n\tif self.inputting_command {\n\t\tdefer self.draw_status_line()\n\t\treturn self.rl.OnText(text, a, b)\n\t}\n\tif self.statusline_message != \"\" {\n\t\tself.statusline_message = \"\"\n\t\tself.draw_status_line()\n\t\treturn nil\n\t}\n\treturn nil\n}\n\nfunc (self *Handler) do_search(query string) {\n\tself.current_search = nil\n\tif len(query) < 2 {\n\t\treturn\n\t}\n\tif !self.current_search_is_regex {\n\t\tquery = regexp.QuoteMeta(query)\n\t}\n\tpat, err := regexp.Compile(`(?i)` + query)\n\tif err != nil {\n\t\tself.statusline_message = fmt.Sprintf(\"Bad regex: %s\", err)\n\t\tself.lp.Beep()\n\t\treturn\n\t}\n\tself.current_search = do_search(pat, self.logical_lines)\n\tif self.current_search.Len() == 0 {\n\t\tself.current_search = nil\n\t\tself.statusline_message = fmt.Sprintf(\"No matches for: %#v\", query)\n\t\tself.lp.Beep()\n\t} else {\n\t\tif self.scroll_to_next_match(false, true) {\n\t\t\tself.draw_screen()\n\t\t} else {\n\t\t\tself.lp.Beep()\n\t\t}\n\t}\n}\n\nfunc (self *Handler) on_key_event(ev *loop.KeyEvent) error {\n\tif self.inputting_command {\n\t\tdefer self.draw_status_line()\n\t\tif ev.MatchesPressOrRepeat(\"esc\") {\n\t\t\tself.inputting_command = false\n\t\t\tev.Handled = true\n\t\t\treturn nil\n\t\t}\n\t\tif ev.MatchesPressOrRepeat(\"enter\") {\n\t\t\tself.inputting_command = false\n\t\t\tev.Handled = true\n\t\t\tself.do_search(self.rl.AllText())\n\t\t\tself.draw_screen()\n\t\t\treturn nil\n\t\t}\n\t\treturn self.rl.OnKeyEvent(ev)\n\t}\n\tif self.statusline_message != \"\" {\n\t\tif ev.Type != loop.RELEASE {\n\t\t\tev.Handled = true\n\t\t\tself.statusline_message = \"\"\n\t\t\tself.draw_status_line()\n\t\t}\n\t\treturn nil\n\t}\n\tif self.current_search != nil && ev.MatchesPressOrRepeat(\"esc\") {\n\t\tself.current_search = nil\n\t\tself.draw_screen()\n\t\treturn nil\n\t}\n\tac := self.shortcut_tracker.Match(ev, conf.KeyboardShortcuts)\n\tif ac != nil {\n\t\tev.Handled = true\n\t\treturn self.dispatch_action(ac.Name, ac.Args)\n\t}\n\treturn nil\n}\n\nfunc (self *Handler) scroll_lines(amt int) (delta int) {\n\tbefore := self.scroll_pos\n\tdelta = self.logical_lines.IncrementScrollPosBy(&self.scroll_pos, amt)\n\tif delta > 0 && self.max_scroll_pos.Less(self.scroll_pos) {\n\t\tself.scroll_pos = self.max_scroll_pos\n\t\tdelta = self.logical_lines.Minus(self.scroll_pos, before)\n\t}\n\treturn\n}\n\nfunc (self *Handler) scroll_to_next_change(backwards bool) bool {\n\tif backwards {\n\t\tfor i := self.scroll_pos.logical_line - 1; i >= 0; i-- {\n\t\t\tline := self.logical_lines.At(i)\n\t\t\tif line.is_change_start {\n\t\t\t\tself.scroll_pos = ScrollPos{i, 0}\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor i := self.scroll_pos.logical_line + 1; i < self.logical_lines.Len(); i++ {\n\t\t\tline := self.logical_lines.At(i)\n\t\t\tif line.is_change_start {\n\t\t\t\tself.scroll_pos = ScrollPos{i, 0}\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *Handler) scroll_to_next_file(backwards bool) bool {\n\tif backwards {\n\t\tfor i := self.scroll_pos.logical_line - 1; i >= 0; i-- {\n\t\t\tline := self.logical_lines.At(i)\n\t\t\tif line.line_type == TITLE_LINE {\n\t\t\t\tself.scroll_pos = ScrollPos{i, 0}\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor i := self.scroll_pos.logical_line + 1; i < self.logical_lines.Len(); i++ {\n\t\t\tline := self.logical_lines.At(i)\n\t\t\tif line.line_type == TITLE_LINE {\n\t\t\t\tself.scroll_pos = ScrollPos{i, 0}\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *Handler) scroll_to_next_match(backwards, include_current_match bool) bool {\n\tif self.current_search == nil {\n\t\treturn false\n\t}\n\tif self.current_search_is_backward {\n\t\tbackwards = !backwards\n\t}\n\toffset, delta := 1, 1\n\tif include_current_match {\n\t\toffset = 0\n\t}\n\tif backwards {\n\t\toffset *= -1\n\t\tdelta *= -1\n\t}\n\tpos := self.scroll_pos\n\tif offset != 0 && self.logical_lines.IncrementScrollPosBy(&pos, offset) == 0 {\n\t\treturn false\n\t}\n\tfor {\n\t\tif self.current_search.Has(pos) {\n\t\t\tself.scroll_pos = pos\n\t\t\tself.draw_screen()\n\t\t\treturn true\n\t\t}\n\t\tif self.logical_lines.IncrementScrollPosBy(&pos, delta) == 0 || self.max_scroll_pos.Less(pos) {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *Handler) change_context_count(val int) bool {\n\tval = utils.Max(0, val)\n\tif val == self.current_context_count {\n\t\treturn false\n\t}\n\tself.current_context_count = val\n\tp := self.scroll_pos\n\tself.restore_position = &p\n\tself.clear_mouse_selection()\n\tself.generate_diff()\n\tself.draw_screen()\n\treturn true\n}\n\nfunc (self *Handler) start_search(is_regex, is_backward bool) {\n\tif self.inputting_command {\n\t\tself.lp.Beep()\n\t\treturn\n\t}\n\tself.inputting_command = true\n\tself.current_search_is_regex = is_regex\n\tself.current_search_is_backward = is_backward\n\tself.rl.SetText(``)\n\tself.draw_status_line()\n}\n\nfunc (self *Handler) dispatch_action(name, args string) error {\n\tswitch name {\n\tcase `quit`:\n\t\tself.lp.Quit(0)\n\tcase `copy_to_clipboard`:\n\t\ttext := self.text_for_current_mouse_selection()\n\t\tif text == \"\" {\n\t\t\tself.lp.Beep()\n\t\t} else {\n\t\t\tself.lp.CopyTextToClipboard(text)\n\t\t}\n\tcase `copy_to_clipboard_or_exit`:\n\t\ttext := self.text_for_current_mouse_selection()\n\t\tif text == \"\" {\n\t\t\tself.lp.Quit(0)\n\t\t} else {\n\t\t\tself.lp.CopyTextToClipboard(text)\n\t\t}\n\tcase `scroll_by`:\n\t\tif args == \"\" {\n\t\t\targs = \"1\"\n\t\t}\n\t\tamt, err := strconv.Atoi(args)\n\t\tif err == nil {\n\t\t\tif self.scroll_lines(amt) == 0 {\n\t\t\t\tself.lp.Beep()\n\t\t\t} else {\n\t\t\t\tself.draw_screen()\n\t\t\t}\n\t\t} else {\n\t\t\tself.lp.Beep()\n\t\t}\n\tcase `scroll_to`:\n\t\tdone := false\n\t\tswitch {\n\t\tcase strings.Contains(args, \"file\"):\n\t\t\tdone = self.scroll_to_next_file(strings.Contains(args, `prev`))\n\t\tcase strings.Contains(args, `change`):\n\t\t\tdone = self.scroll_to_next_change(strings.Contains(args, `prev`))\n\t\tcase strings.Contains(args, `match`):\n\t\t\tdone = self.scroll_to_next_match(strings.Contains(args, `prev`), false)\n\t\tcase strings.Contains(args, `page`):\n\t\t\tamt := self.screen_size.num_lines\n\t\t\tif strings.Contains(args, `half`) {\n\t\t\t\tamt = amt / 2\n\t\t\t}\n\t\t\tif strings.Contains(args, `prev`) {\n\t\t\t\tamt *= -1\n\t\t\t}\n\t\t\tdone = self.scroll_lines(amt) != 0\n\t\tdefault:\n\t\t\tnpos := ScrollPos{}\n\t\t\tif strings.Contains(args, `end`) {\n\t\t\t\tnpos = self.max_scroll_pos\n\t\t\t}\n\t\t\tdone = npos != self.scroll_pos\n\t\t\tself.scroll_pos = npos\n\t\t}\n\t\tif done {\n\t\t\tself.draw_screen()\n\t\t} else {\n\t\t\tself.lp.Beep()\n\t\t}\n\tcase `change_context`:\n\t\tnew_ctx := self.current_context_count\n\t\tswitch args {\n\t\tcase `all`:\n\t\t\tnew_ctx = 100000\n\t\tcase `default`:\n\t\t\tnew_ctx = self.original_context_count\n\t\tdefault:\n\t\t\tdelta, _ := strconv.Atoi(args)\n\t\t\tnew_ctx += delta\n\t\t}\n\t\tif !self.change_context_count(new_ctx) {\n\t\t\tself.lp.Beep()\n\t\t}\n\tcase `start_search`:\n\t\tif self.diff_map != nil && self.logical_lines != nil {\n\t\t\ta, b, _ := strings.Cut(args, \" \")\n\t\t\tself.start_search(config.StringToBool(a), config.StringToBool(b))\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *Handler) on_mouse_event(ev *loop.MouseEvent) error {\n\tif self.logical_lines == nil {\n\t\treturn nil\n\t}\n\tif ev.Event_type == loop.MOUSE_PRESS && ev.Buttons&(loop.MOUSE_WHEEL_UP|loop.MOUSE_WHEEL_DOWN) != 0 {\n\t\tself.handle_wheel_event(ev.Buttons&(loop.MOUSE_WHEEL_UP) != 0)\n\t\treturn nil\n\t}\n\tif ev.Event_type == loop.MOUSE_PRESS && ev.Buttons&loop.LEFT_MOUSE_BUTTON != 0 {\n\t\tself.start_mouse_selection(ev)\n\t\treturn nil\n\t}\n\tif ev.Event_type == loop.MOUSE_MOVE {\n\t\tself.update_mouse_selection(ev)\n\t\treturn nil\n\t}\n\tif ev.Event_type == loop.MOUSE_RELEASE && ev.Buttons&loop.LEFT_MOUSE_BUTTON != 0 {\n\t\tself.finish_mouse_selection(ev)\n\t\treturn nil\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "kittens/hints/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/hints/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage hints\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nfunc convert_text(text string, cols int) string {\n\tlines := make([]string, 0, 64)\n\tempty_line := strings.Repeat(\"\\x00\", cols) + \"\\n\"\n\ts1 := utils.NewLineScanner(text)\n\tfor s1.Scan() {\n\t\tfull_line := s1.Text()\n\t\tif full_line == \"\" {\n\t\t\tlines = append(lines, empty_line)\n\t\t\tcontinue\n\t\t}\n\t\tif strings.TrimRight(full_line, \"\\r\") == \"\" {\n\t\t\tfor range len(full_line) {\n\t\t\t\tlines = append(lines, empty_line)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tappended := false\n\t\ts2 := utils.NewSeparatorScanner(full_line, \"\\r\")\n\t\tfor s2.Scan() {\n\t\t\tline := s2.Text()\n\t\t\tif line != \"\" {\n\t\t\t\tline_sz := wcswidth.Stringwidth(line)\n\t\t\t\textra := cols - line_sz\n\t\t\t\tif extra > 0 {\n\t\t\t\t\tline += strings.Repeat(\"\\x00\", extra)\n\t\t\t\t}\n\t\t\t\tlines = append(lines, line)\n\t\t\t\tlines = append(lines, \"\\r\")\n\t\t\t\tappended = true\n\t\t\t}\n\t\t}\n\t\tif appended {\n\t\t\tlines[len(lines)-1] = \"\\n\"\n\t\t}\n\t}\n\tans := strings.Join(lines, \"\")\n\treturn strings.TrimRight(ans, \"\\r\\n\")\n}\n\nfunc parse_input(text string) string {\n\tcols, err := strconv.Atoi(os.Getenv(\"OVERLAID_WINDOW_COLS\"))\n\tif err == nil {\n\t\treturn convert_text(text, cols)\n\t}\n\tterm, err := tty.OpenControllingTerm()\n\tif err == nil {\n\t\tsz, err := term.GetSize()\n\t\tterm.Close()\n\t\tif err == nil {\n\t\t\treturn convert_text(text, int(sz.Col))\n\t\t}\n\t}\n\treturn convert_text(text, 80)\n}\n\ntype Result struct {\n\tMatch                []string         `json:\"match\"`\n\tPrograms             []string         `json:\"programs\"`\n\tMultiple_joiner      string           `json:\"multiple_joiner\"`\n\tCustomize_processing string           `json:\"customize_processing\"`\n\tType                 string           `json:\"type\"`\n\tGroupdicts           []map[string]any `json:\"groupdicts\"`\n\tExtra_cli_args       []string         `json:\"extra_cli_args\"`\n\tLinenum_action       string           `json:\"linenum_action\"`\n\tCwd                  string           `json:\"cwd\"`\n}\n\nfunc encode_hint(num int, alphabet string) (res string) {\n\trunes := []rune(alphabet)\n\td := len(runes)\n\tfor res == \"\" || num > 0 {\n\t\tres = string(runes[num%d]) + res\n\t\tnum /= d\n\t}\n\treturn\n}\n\nfunc decode_hint(x string, alphabet string) (ans int) {\n\tbase := len(alphabet)\n\tindex_map := make(map[rune]int, len(alphabet))\n\tfor i, c := range alphabet {\n\t\tindex_map[c] = i\n\t}\n\tfor _, char := range x {\n\t\tans = ans*base + index_map[char]\n\t}\n\treturn\n}\n\nfunc as_rgb(c uint32) [3]float32 {\n\treturn [3]float32{float32((c>>16)&255) / 255.0, float32((c>>8)&255) / 255.0, float32(c&255) / 255.0}\n}\n\nfunc hints_text_color(confval string) (ans string) {\n\tans = confval\n\tif ans == \"auto\" {\n\t\tans = \"bright-gray\"\n\t\tif bc, err := tui.ReadBasicColors(); err == nil {\n\t\t\tbg := as_rgb(bc.Background)\n\t\t\tc15 := as_rgb(bc.Color15)\n\t\t\tc8 := as_rgb(bc.Color8)\n\t\t\tif utils.RGBContrast(bg[0], bg[1], bg[2], c8[0], c8[1], c8[2]) > utils.RGBContrast(bg[0], bg[1], bg[2], c15[0], c15[1], c15[2]) {\n\t\t\t\tans = \"bright-black\"\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc main(_ *cli.Command, o *Options, args []string) (rc int, err error) {\n\to.HintsTextColor = hints_text_color(o.HintsTextColor)\n\toutput := tui.KittenOutputSerializer()\n\tif tty.IsTerminal(os.Stdin.Fd()) {\n\t\treturn 1, fmt.Errorf(\"You must pass the text to be hinted on STDIN\")\n\t}\n\tstdin, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\treturn 1, fmt.Errorf(\"Failed to read from STDIN with error: %w\", err)\n\t}\n\tif len(args) > 0 && o.CustomizeProcessing == \"\" && o.Type != \"linenum\" {\n\t\treturn 1, fmt.Errorf(\"Extra command line arguments present: %s\", strings.Join(args, \" \"))\n\t}\n\tinput_text := parse_input(utils.UnsafeBytesToString(stdin))\n\ttext, all_marks, index_map, err := find_marks(input_text, o, os.Args[2:]...)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\n\tresult := Result{\n\t\tPrograms: o.Program, Multiple_joiner: o.MultipleJoiner, Customize_processing: o.CustomizeProcessing, Type: o.Type,\n\t\tExtra_cli_args: args, Linenum_action: o.LinenumAction,\n\t}\n\tresult.Cwd, _ = os.Getwd()\n\talphabet := o.Alphabet\n\tif alphabet == \"\" {\n\t\talphabet = DEFAULT_HINT_ALPHABET\n\t}\n\tignore_mark_indices := utils.NewSet[int](8)\n\twindow_title := o.WindowTitle\n\tif window_title == \"\" {\n\t\tswitch o.Type {\n\t\tcase \"url\":\n\t\t\twindow_title = \"Choose URL\"\n\t\tdefault:\n\t\t\twindow_title = \"Choose text\"\n\t\t}\n\t}\n\tcurrent_text := \"\"\n\tcurrent_input := \"\"\n\tmatch_suffix := \"\"\n\tswitch o.AddTrailingSpace {\n\tcase \"always\":\n\t\tmatch_suffix = \" \"\n\tcase \"never\":\n\tdefault:\n\t\tif o.Multiple {\n\t\t\tmatch_suffix = \" \"\n\t\t}\n\t}\n\tchosen := []*Mark{}\n\tlp, err := loop.New(loop.NoAlternateScreen) // no alternate screen reduces flicker on exit\n\tif err != nil {\n\t\treturn\n\t}\n\tfctx := style.Context{AllowEscapeCodes: true}\n\tfaint := fctx.SprintFunc(\"dim\")\n\thint_style := fctx.SprintFunc(fmt.Sprintf(\"fg=%s bg=%s bold\", o.HintsForegroundColor, o.HintsBackgroundColor))\n\ttext_style := fctx.SprintFunc(fmt.Sprintf(\"fg=%s bold\", o.HintsTextColor))\n\n\thighlight_mark := func(m *Mark, mark_text string) string {\n\t\thint := encode_hint(m.Index, alphabet)\n\t\tif current_input != \"\" && !strings.HasPrefix(hint, current_input) {\n\t\t\treturn faint(mark_text)\n\t\t}\n\t\thint = hint[len(current_input):]\n\t\tif hint == \"\" {\n\t\t\thint = \" \"\n\t\t}\n\t\tif len(mark_text) <= len(hint) {\n\t\t\tmark_text = \"\"\n\t\t} else {\n\t\t\treplaced_text := mark_text[:len(hint)]\n\t\t\treplaced_text = strings.ReplaceAll(replaced_text, \"\\r\", \"\\n\")\n\t\t\tif strings.Contains(replaced_text, \"\\n\") {\n\t\t\t\tbuf := strings.Builder{}\n\t\t\t\tbuf.Grow(2 * len(hint))\n\t\t\t\th := hint\n\t\t\t\tparts := strings.Split(replaced_text, \"\\n\")\n\t\t\t\tfor i, x := range parts {\n\t\t\t\t\tif x != \"\" {\n\t\t\t\t\t\tbuf.WriteString(h[:len(x)])\n\t\t\t\t\t\th = h[len(x):]\n\t\t\t\t\t}\n\t\t\t\t\tif i != len(parts)-1 {\n\t\t\t\t\t\tbuf.WriteString(\"\\n\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif h != \"\" {\n\t\t\t\t\tbuf.WriteString(h)\n\t\t\t\t}\n\t\t\t\thint = buf.String()\n\t\t\t}\n\t\t\tmark_text = mark_text[len(hint):]\n\t\t}\n\t\tans := hint_style(hint) + text_style(mark_text)\n\t\treturn fmt.Sprintf(\"\\x1b]8;;mark:%d\\a%s\\x1b]8;;\\a\", m.Index, ans)\n\t}\n\n\trender := func() string {\n\t\tans := text\n\t\tfor i := len(all_marks) - 1; i >= 0; i-- {\n\t\t\tmark := &all_marks[i]\n\t\t\tif ignore_mark_indices.Has(mark.Index) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmtext := highlight_mark(mark, ans[mark.Start:mark.End])\n\t\t\tans = ans[:mark.Start] + mtext + ans[mark.End:]\n\t\t}\n\t\tans = strings.ReplaceAll(ans, \"\\x00\", \"\")\n\t\treturn strings.TrimRightFunc(strings.NewReplacer(\"\\r\", \"\\r\\n\", \"\\n\", \"\\r\\n\").Replace(ans), unicode.IsSpace)\n\t}\n\n\tdraw_screen := func() {\n\t\tlp.StartAtomicUpdate()\n\t\tdefer lp.EndAtomicUpdate()\n\t\tif current_text == \"\" {\n\t\t\tcurrent_text = render()\n\t\t}\n\t\tlp.ClearScreen()\n\t\tlp.QueueWriteString(current_text)\n\t}\n\treset := func() {\n\t\tcurrent_input = \"\"\n\t\tcurrent_text = \"\"\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.SetCursorVisible(false)\n\t\tlp.SetWindowTitle(window_title)\n\t\tlp.AllowLineWrapping(false)\n\t\tdraw_screen()\n\t\tlp.SendOverlayReady()\n\t\treturn \"\", nil\n\t}\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorVisible(true)\n\t\treturn \"\"\n\t}\n\tlp.OnResize = func(old_size, new_size loop.ScreenSize) error {\n\t\tdraw_screen()\n\t\treturn nil\n\t}\n\tlp.OnRCResponse = func(data []byte) error {\n\t\tvar r struct {\n\t\t\tType string\n\t\t\tMark int\n\t\t}\n\t\tif err := json.Unmarshal(data, &r); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif r.Type == \"mark_activated\" {\n\t\t\tif m, ok := index_map[r.Mark]; ok {\n\t\t\t\tchosen = append(chosen, m)\n\t\t\t\tif o.Multiple {\n\t\t\t\t\tignore_mark_indices.Add(m.Index)\n\t\t\t\t\treset()\n\t\t\t\t} else {\n\t\t\t\t\tlp.Quit(0)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnText = func(text string, _, _ bool) error {\n\t\tchanged := false\n\t\tfor _, ch := range text {\n\t\t\tif strings.ContainsRune(alphabet, ch) {\n\t\t\t\tcurrent_input += string(ch)\n\t\t\t\tchanged = true\n\t\t\t}\n\t\t}\n\t\tif changed {\n\t\t\tmatches := []*Mark{}\n\t\t\tfor idx, m := range index_map {\n\t\t\t\tif eh := encode_hint(idx, alphabet); strings.HasPrefix(eh, current_input) {\n\t\t\t\t\tmatches = append(matches, m)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(matches) == 1 {\n\t\t\t\tchosen = append(chosen, matches[0])\n\t\t\t\tif o.Multiple {\n\t\t\t\t\tignore_mark_indices.Add(matches[0].Index)\n\t\t\t\t\treset()\n\t\t\t\t} else {\n\t\t\t\t\tlp.Quit(0)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrent_text = \"\"\n\t\t\tdraw_screen()\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnKeyEvent = func(ev *loop.KeyEvent) error {\n\t\tif ev.MatchesPressOrRepeat(\"backspace\") {\n\t\t\tev.Handled = true\n\t\t\tr := []rune(current_input)\n\t\t\tif len(r) > 0 {\n\t\t\t\tr = r[:len(r)-1]\n\t\t\t\tcurrent_input = string(r)\n\t\t\t\tcurrent_text = \"\"\n\t\t\t}\n\t\t\tdraw_screen()\n\t\t} else if ev.MatchesPressOrRepeat(\"enter\") || ev.MatchesPressOrRepeat(\"space\") {\n\t\t\tev.Handled = true\n\t\t\tif current_input != \"\" {\n\t\t\t\tidx := decode_hint(current_input, alphabet)\n\t\t\t\tif m := index_map[idx]; m != nil {\n\t\t\t\t\tchosen = append(chosen, m)\n\t\t\t\t\tignore_mark_indices.Add(idx)\n\t\t\t\t\tif o.Multiple {\n\t\t\t\t\t\treset()\n\t\t\t\t\t\tdraw_screen()\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlp.Quit(0)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcurrent_input = \"\"\n\t\t\t\t\tcurrent_text = \"\"\n\t\t\t\t\tdraw_screen()\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ev.MatchesPressOrRepeat(\"esc\") {\n\t\t\tif o.Multiple {\n\t\t\t\tlp.Quit(0)\n\t\t\t} else {\n\t\t\t\tlp.Quit(1)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn 1, nil\n\t}\n\tif lp.ExitCode() != 0 {\n\t\treturn lp.ExitCode(), nil\n\t}\n\tresult.Match = make([]string, len(chosen))\n\tresult.Groupdicts = make([]map[string]any, len(chosen))\n\tfor i, m := range chosen {\n\t\tresult.Match[i] = m.Text + match_suffix\n\t\tresult.Groupdicts[i] = m.Groupdict\n\t}\n\tfmt.Println(output(result))\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/hints/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nfrom collections.abc import Sequence\nfrom functools import lru_cache\nfrom typing import Any\n\nfrom kitty.cli_stub import HintsCLIOptions\nfrom kitty.clipboard import set_clipboard_string, set_primary_selection\nfrom kitty.constants import website_url\nfrom kitty.fast_data_types import get_options\nfrom kitty.typing_compat import BossType, WindowType\nfrom kitty.utils import get_editor, resolve_custom_file\n\nfrom ..tui.handler import result_handler\n\nDEFAULT_REGEX = r'(?m)^\\s*(.+?)\\s*$'\n\ndef load_custom_processor(customize_processing: str) -> Any:\n    if customize_processing.startswith('::import::'):\n        import importlib\n        m = importlib.import_module(customize_processing[len('::import::'):])\n        return {k: getattr(m, k) for k in dir(m)}\n    if customize_processing == '::linenum::':\n        return {'handle_result': linenum_handle_result}\n    custom_path = resolve_custom_file(customize_processing)\n    import runpy\n    return runpy.run_path(custom_path, run_name='__main__')\n\nclass Mark:\n\n    __slots__ = ('index', 'start', 'end', 'text', 'is_hyperlink', 'group_id', 'groupdict')\n\n    def __init__(\n            self,\n            index: int, start: int, end: int,\n            text: str,\n            groupdict: Any,\n            is_hyperlink: bool = False,\n            group_id: str | None = None\n    ):\n        self.index, self.start, self.end = index, start, end\n        self.text = text\n        self.groupdict = groupdict\n        self.is_hyperlink = is_hyperlink\n        self.group_id = group_id\n\n    def as_dict(self) -> dict[str, Any]:\n        return {\n            'index': self.index, 'start': self.start, 'end': self.end,\n            'text': self.text, 'groupdict': {str(k):v for k, v in (self.groupdict or {}).items()},\n            'group_id': self.group_id or '', 'is_hyperlink': self.is_hyperlink\n        }\n\n\ndef parse_hints_args(args: list[str]) -> tuple[HintsCLIOptions, list[str]]:\n    from kitty.cli import parse_args\n    return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten hints', result_class=HintsCLIOptions)\n\n\ndef custom_marking() -> None:\n    import json\n    text = sys.stdin.read()\n    sys.stdin.close()\n    opts, extra_cli_args = parse_hints_args(sys.argv[1:])\n    m = load_custom_processor(opts.customize_processing or '::impossible::')\n    if 'mark' not in m:\n        raise SystemExit(2)\n    all_marks = tuple(x.as_dict() for x in m['mark'](text, opts, Mark, extra_cli_args))\n    sys.stdout.write(json.dumps(all_marks))\n    raise SystemExit(0)\n\n\nOPTIONS = r'''\n--program\ntype=list\nWhat program to use to open matched text. Defaults to the default open program\nfor the operating system. Various special values are supported:\n\n:code:`-`\n    paste the match into the terminal window.\n\n:code:`@`\n    copy the match to the clipboard\n\n:code:`*`\n    copy the match to the primary selection (on systems that support primary selections)\n\n:code:`@NAME`\n    copy the match to the specified buffer, e.g. :code:`@a`\n\n:code:`default`\n    run the default open program. Note that when using the hyperlink :code:`--type`\n    the default is to use the kitty :doc:`hyperlink handling </open_actions>` facilities.\n\n:code:`launch`\n    run :doc:`/launch` to open the program in a new kitty tab, window, overlay, etc.\n    For example::\n\n        --program \"launch --type=tab vim\"\n\nCan be specified multiple times to run multiple programs.\n\n\n--type\ndefault=url\nchoices=url,regex,path,line,hash,word,linenum,hyperlink,ip\nThe type of text to search for. A value of :code:`linenum` is special, it looks\nfor error messages using the pattern specified with :option:`--regex`, which\nmust have the named groups: :code:`path` and :code:`line`. If not specified,\nwill look for :code:`path:line`. The :option:`--linenum-action` option\ncontrols where to display the selected error message, other options are ignored.\n\n\n--regex\ndefault={default_regex}\nThe regular expression to use when option :option:`--type` is set to\n:code:`regex`, in Perl 5 syntax. If you specify a numbered group in the regular\nexpression, only the group will be matched. This allows you to match text\nignoring a prefix/suffix, as needed. The default expression matches lines. To\nmatch text over multiple lines, things get a little tricky, as line endings\nare a sequence of zero or more null bytes followed by either a carriage return\nor a newline character. To have a pattern match over line endings you will need to\nmatch the character set ``[\\0\\r\\n]``. The newlines and null bytes are automatically\nstripped from the returned text. If you specify named groups and a\n:option:`--program`, then the program will be passed arguments corresponding\nto each named group of the form :code:`key=value`.\n\n\n--linenum-action\ndefault=self\ntype=choice\nchoices=self,window,tab,os_window,background,remote-control\nWhere to perform the action on matched errors. :code:`self` means the current\nwindow, :code:`window` a new kitty window, :code:`tab` a new tab,\n:code:`os_window` a new OS window and :code:`background` run in the background.\n:code:`remote-control` is like background but the program can use kitty remote control\nwithout needing to turn on remote control globally.\nThe actual action is whatever arguments are provided to the kitten, for\nexample:\n:code:`kitten hints --type=linenum --linenum-action=tab vim +{line} {path}`\nwill open the matched path at the matched line number in vim in\na new kitty tab. Note that in order to use :option:`--program` to copy or paste\nthe provided arguments, you need to use the special value :code:`self`.\n\n\n--url-prefixes\ndefault=default\nComma separated list of recognized URL prefixes. Defaults to the list of\nprefixes defined by the :opt:`url_prefixes` option in :file:`kitty.conf`.\n\n\n--url-excluded-characters\ndefault=default\nCharacters to exclude when matching URLs. Defaults to the list of characters\ndefined by the :opt:`url_excluded_characters` option in :file:`kitty.conf`.\nThe syntax for this option is the same as for :opt:`url_excluded_characters`.\n\n\n--word-characters\nCharacters to consider as part of a word. In addition, all characters marked as\nalphanumeric in the Unicode database will be considered as word characters.\nDefaults to the :opt:`select_by_word_characters` option from :file:`kitty.conf`.\n\n\n--minimum-match-length\ndefault=3\ntype=int\nThe minimum number of characters to consider a match.\n\n\n--multiple\ntype=bool-set\nSelect multiple matches and perform the action on all of them together at the\nend. In this mode, press :kbd:`Esc` to finish selecting.\n\n\n--multiple-joiner\ndefault=auto\nString for joining multiple selections when copying to the clipboard or\ninserting into the terminal. The special values are: :code:`space` - a space\ncharacter, :code:`newline` - a newline, :code:`empty` - an empty joiner,\n:code:`json` - a JSON serialized list, :code:`auto` - an automatic choice, based\non the type of text being selected. In addition, integers are interpreted as\nzero-based indices into the list of selections. You can use :code:`0` for the\nfirst selection and :code:`-1` for the last.\n\n\n--add-trailing-space\ndefault=auto\nchoices=auto,always,never\nAdd trailing space after matched text. Defaults to :code:`auto`, which adds the\nspace when used together with :option:`--multiple`.\n\n\n--hints-offset\ndefault=1\ntype=int\nThe offset (from zero) at which to start hint numbering. Note that only numbers\ngreater than or equal to zero are respected.\n\n\n--alphabet\nThe list of characters to use for hints. The default is to use numbers and\nlowercase English alphabets. Specify your preference as a string of characters.\nNote that you need to specify the :option:`--hints-offset` as zero to use the\nfirst character to highlight the first match, otherwise it will start with the\nsecond character by default.\n\n\n--ascending\ntype=bool-set\nMake the hints increase from top to bottom, instead of decreasing from top to\nbottom.\n\n\n--hints-foreground-color\ndefault=black\ntype=str\nThe foreground color for hints. You can use color names or hex values. For the eight basic\nnamed terminal colors you can also use the :code:`bright-` prefix to get the bright variant of the\ncolor.\n\n\n--hints-background-color\ndefault=green\ntype=str\nThe background color for hints. You can use color names or hex values. For the eight basic\nnamed terminal colors you can also use the :code:`bright-` prefix to get the bright variant of the\ncolor.\n\n\n--hints-text-color\ndefault=auto\ntype=str\nThe foreground color for text pointed to by the hints. You can use color names or hex values. For the eight basic\nnamed terminal colors you can also use the :code:`bright-` prefix to get the bright variant of the\ncolor. The default is to pick a suitable color automatically.\n\n\n--customize-processing\nName of a python file in the kitty config directory which will be imported to\nprovide custom implementations for pattern finding and performing actions\non selected matches. You can also specify absolute paths to load the script from\nelsewhere. See {hints_url} for details.\n\n\n--window-title\nThe title for the hints window, default title is based on the type of text being\nhinted.\n'''.format(\n    default_regex=DEFAULT_REGEX,\n    line='{{line}}', path='{{path}}',\n    hints_url=website_url('kittens/hints'),\n).format\nhelp_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'\nusage = ''\n\n\ndef main(args: list[str]) -> dict[str, Any] | None:\n    raise SystemExit('Should be run as kitten hints')\n\n\ndef linenum_process_result(data: dict[str, Any]) -> tuple[str, int]:\n    for match, g in zip(data['match'], data['groupdicts']):\n        path, line = g['path'], g['line']\n        if path and line:\n            return path, int(line)\n    return '', -1\n\n\ndef linenum_handle_result(args: list[str], data: dict[str, Any], target_window_id: int, boss: BossType, extra_cli_args: Sequence[str], *a: Any) -> None:\n    path, line = linenum_process_result(data)\n    if not path:\n        return\n\n    if extra_cli_args:\n        cmd = [x.format(path=path, line=line) for x in extra_cli_args]\n    else:\n        cmd = get_editor(path_to_edit=path, line_number=line)\n    w = boss.window_id_map.get(target_window_id)\n    action = data['linenum_action']\n\n    if action == 'self':\n        if w is not None:\n            def is_copy_action(s: str) -> bool:\n                return s in ('-', '@', '*') or s.startswith('@')\n\n            programs = list(filter(is_copy_action, data['programs'] or ()))\n            # keep for backward compatibility, previously option `--program` does not need to be specified to perform copy actions\n            if is_copy_action(cmd[0]):\n                programs.append(cmd.pop(0))\n            if programs:\n                text = ' '.join(cmd)\n                for program in programs:\n                    if program == '-':\n                        w.paste_bytes(text)\n                    elif program == '@':\n                        set_clipboard_string(text)\n                        boss.handle_clipboard_loss('clipboard')\n                    elif program == '*':\n                        set_primary_selection(text)\n                        boss.handle_clipboard_loss('primary')\n                    elif program.startswith('@'):\n                        boss.set_clipboard_buffer(program[1:], text)\n            else:\n                import shlex\n                text = shlex.join(cmd)\n                w.paste_bytes(f'{text}\\r')\n    elif action == 'background':\n        boss.run_background_process(cmd, cwd=data['cwd'], allow_remote_control=False)\n    elif action == 'remote-control':\n        boss.run_background_process(cmd, cwd=data['cwd'], allow_remote_control=True)\n    else:\n        getattr(boss, {\n            'window': 'new_window_with_cwd', 'tab': 'new_tab_with_cwd', 'os_window': 'new_os_window_with_cwd'\n            }[action])(*cmd)\n\n\ndef on_mark_clicked(boss: BossType, window: WindowType, url: str, hyperlink_id: int, cwd: str) -> bool:\n    if url.startswith('mark:'):\n        window.send_cmd_response({'Type': 'mark_activated', 'Mark': int(url[5:])})\n        return True\n    return False\n\n\n@result_handler(type_of_input='screen-ansi', has_ready_notification=True, open_url_handler=on_mark_clicked)\ndef handle_result(args: list[str], data: dict[str, Any], target_window_id: int, boss: BossType) -> None:\n    cp = data['customize_processing']\n    if data['type'] == 'linenum':\n        cp = '::linenum::'\n    if cp:\n        m = load_custom_processor(cp)\n        if 'handle_result' in m:\n            m['handle_result'](args, data, target_window_id, boss, data['extra_cli_args'])\n            return None\n\n    programs = data['programs'] or ('default',)\n    matches: list[str] = []\n    groupdicts = []\n    for m, g in zip(data['match'], data['groupdicts']):\n        if m:\n            matches.append(m)\n            groupdicts.append(g)\n    joiner = data['multiple_joiner']\n    try:\n        is_int: int | None = int(joiner)\n    except Exception:\n        is_int = None\n    text_type = data['type']\n\n    @lru_cache\n    def joined_text() -> str:\n        if is_int is not None:\n            try:\n                return matches[is_int]\n            except IndexError:\n                return matches[-1]\n        if joiner == 'json':\n            import json\n            return json.dumps(matches, ensure_ascii=False, indent='\\t')\n        if joiner == 'auto':\n            q = '\\n\\r' if text_type in ('line', 'url') else ' '\n        else:\n            q = {'newline': '\\n\\r', 'space': ' '}.get(joiner, '')\n        return q.join(matches)\n\n    for program in programs:\n        if program == '-':\n            w = boss.window_id_map.get(target_window_id)\n            if w is not None:\n                w.paste_text(joined_text())\n        elif program == '*':\n            set_primary_selection(joined_text())\n        elif program.startswith('@'):\n            if program == '@':\n                set_clipboard_string(joined_text())\n            else:\n                boss.set_clipboard_buffer(program[1:], joined_text())\n        else:\n            from kitty.conf.utils import to_cmdline\n            cwd = data['cwd']\n            is_default_program = program == 'default'\n            program = get_options().open_url_with if is_default_program else program\n            if text_type == 'hyperlink' and is_default_program:\n                w = boss.window_id_map.get(target_window_id)\n                for m in matches:\n                    if w is not None:\n                        w.open_url(m, hyperlink_id=1, cwd=cwd)\n            else:\n                launch_args = []\n                if isinstance(program, str) and program.startswith('launch '):\n                    launch_args = to_cmdline(program)\n                    launch_args.insert(1, '--cwd=' + cwd)\n                for m, groupdict in zip(matches, groupdicts):\n                    if groupdict:\n                        m = []\n                        for k, v in groupdict.items():\n                            m.append('{}={}'.format(k, v or ''))\n                    if launch_args:\n                        w = boss.window_id_map.get(target_window_id)\n                        boss.call_remote_control(self_window=w, args=tuple(launch_args + ([m] if isinstance(m, str) else m)))\n                    else:\n                        boss.open_url(m, program, cwd=cwd)\n\n\nif __name__ == '__main__':\n    # Run with kitty +kitten hints\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['short_desc'] = 'Select text from screen with keyboard'\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n# }}}\n"
  },
  {
    "path": "kittens/hints/marks.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage hints\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/dlclark/regexp2\"\n\t\"github.com/seancfoley/ipaddress-go/ipaddr\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nconst (\n\tDEFAULT_HINT_ALPHABET = \"0123456789abcdefghijklmnopqrstuvwxyz\"\n\tFILE_EXTENSION        = `\\.(?:[a-zA-Z0-9]{2,7}|[ahcmo])(?:\\b|[^.])`\n)\n\nfunc path_regex() string {\n\treturn fmt.Sprintf(`(?:\\S*?/[\\r\\S]+)|(?:\\S[\\r\\S]*%s)\\b`, FILE_EXTENSION)\n}\n\nfunc default_linenum_regex() string {\n\treturn fmt.Sprintf(`(?P<path>%s):(?P<line>\\d+)`, path_regex())\n}\n\ntype Mark struct {\n\tIndex        int            `json:\"index\"`\n\tStart        int            `json:\"start\"`\n\tEnd          int            `json:\"end\"`\n\tText         string         `json:\"text\"`\n\tGroup_id     string         `json:\"group_id\"`\n\tIs_hyperlink bool           `json:\"is_hyperlink\"`\n\tGroupdict    map[string]any `json:\"groupdict\"`\n}\n\nfunc process_escape_codes(text string) (ans string, hyperlinks []Mark) {\n\tremoved_size, idx := 0, 0\n\tactive_hyperlink_url := \"\"\n\tactive_hyperlink_id := \"\"\n\tactive_hyperlink_start_offset := 0\n\n\tadd_hyperlink := func(end int) {\n\t\thyperlinks = append(hyperlinks, Mark{\n\t\t\tIndex: idx, Start: active_hyperlink_start_offset, End: end, Text: active_hyperlink_url, Is_hyperlink: true, Group_id: active_hyperlink_id})\n\t\tactive_hyperlink_url, active_hyperlink_id = \"\", \"\"\n\t\tactive_hyperlink_start_offset = 0\n\t\tidx++\n\t}\n\n\tans = utils.ReplaceAll(utils.MustCompile(\"\\x1b(?:\\\\[[0-9;:]*?m|\\\\].*?\\x1b\\\\\\\\)\"), text, func(raw string, groupdict map[string]utils.SubMatch) string {\n\t\tif !strings.HasPrefix(raw, \"\\x1b]8\") {\n\t\t\tremoved_size += len(raw)\n\t\t\treturn \"\"\n\t\t}\n\t\tstart := groupdict[\"\"].Start - removed_size\n\t\tremoved_size += len(raw)\n\t\tif active_hyperlink_url != \"\" {\n\t\t\tadd_hyperlink(start)\n\t\t}\n\t\traw = raw[4 : len(raw)-2]\n\t\tif metadata, url, found := strings.Cut(raw, \";\"); found && url != \"\" {\n\t\t\tactive_hyperlink_url = url\n\t\t\tactive_hyperlink_start_offset = start\n\t\t\tif metadata != \"\" {\n\t\t\t\tfor entry := range strings.SplitSeq(metadata, \":\") {\n\t\t\t\t\tif strings.HasPrefix(entry, \"id=\") && len(entry) > 3 {\n\t\t\t\t\t\tactive_hyperlink_id = entry[3:]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn \"\"\n\t})\n\tif active_hyperlink_url != \"\" {\n\t\tadd_hyperlink(len(ans))\n\t}\n\treturn\n}\n\ntype PostProcessorFunc = func(string, int, int) (int, int)\ntype GroupProcessorFunc = func(map[string]string)\n\nfunc is_punctuation(b string) bool {\n\tswitch b {\n\tcase \",\", \".\", \"?\", \"!\":\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc closing_bracket_for(ch string) string {\n\tswitch ch {\n\tcase \"(\":\n\t\treturn \")\"\n\tcase \"[\":\n\t\treturn \"]\"\n\tcase \"{\":\n\t\treturn \"}\"\n\tcase \"<\":\n\t\treturn \">\"\n\tcase \"*\":\n\t\treturn \"*\"\n\tcase `\"`:\n\t\treturn `\"`\n\tcase \"'\":\n\t\treturn \"'\"\n\tcase \"“\":\n\t\treturn \"”\"\n\tcase \"‘\":\n\t\treturn \"’\"\n\t}\n\treturn \"\"\n}\n\nfunc char_at(s string, i int) string {\n\tans, _ := utf8.DecodeRuneInString(s[i:])\n\tif ans == utf8.RuneError {\n\t\treturn \"\"\n\t}\n\treturn string(ans)\n}\n\nfunc matching_remover(openers ...string) PostProcessorFunc {\n\treturn func(text string, s, e int) (int, int) {\n\t\tif s < e && e <= len(text) {\n\t\t\tbefore := char_at(text, s)\n\t\t\tif slices.Index(openers, before) > -1 {\n\t\t\t\tq := closing_bracket_for(before)\n\t\t\t\tif e > 0 && char_at(text, e-1) == q {\n\t\t\t\t\ts++\n\t\t\t\t\te--\n\t\t\t\t} else if char_at(text, e) == q {\n\t\t\t\t\ts++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn s, e\n\t}\n}\n\nfunc linenum_group_processor(gd map[string]string) {\n\tpat := utils.MustCompile(`:\\d+$`)\n\tgd[`path`] = pat.ReplaceAllStringFunc(gd[\"path\"], func(m string) string {\n\t\tgd[\"line\"] = m[1:]\n\t\treturn ``\n\t})\n\tgd[`path`] = utils.Expanduser(gd[`path`])\n}\n\nvar PostProcessorMap = sync.OnceValue(func() map[string]PostProcessorFunc {\n\treturn map[string]PostProcessorFunc{\n\t\t\"url\": func(text string, s, e int) (int, int) {\n\t\t\tif s > 4 && text[s-5:s] == \"link:\" { // asciidoc URLs\n\t\t\t\turl := text[s:e]\n\t\t\t\tidx := strings.LastIndex(url, \"[\")\n\t\t\t\tif idx > -1 {\n\t\t\t\t\te -= len(url) - idx\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor e > 1 && is_punctuation(char_at(text, e)) { // remove trailing punctuation\n\t\t\t\te--\n\t\t\t}\n\t\t\t// truncate url at closing bracket/quote\n\t\t\tif s > 0 && e <= len(text) && closing_bracket_for(char_at(text, s-1)) != \"\" {\n\t\t\t\tq := closing_bracket_for(char_at(text, s-1))\n\t\t\t\tidx := strings.Index(text[s:], q)\n\t\t\t\tif idx > 0 {\n\t\t\t\t\te = s + idx\n\t\t\t\t}\n\t\t\t}\n\t\t\t// reStructuredText URLs\n\t\t\tif e > 3 && text[e-2:e] == \"`_\" {\n\t\t\t\te -= 2\n\t\t\t}\n\t\t\treturn s, e\n\t\t},\n\n\t\t\"brackets\": matching_remover(\"(\", \"{\", \"[\", \"<\"),\n\t\t\"quotes\":   matching_remover(\"'\", `\"`, \"“\", \"‘\"),\n\t\t\"ip\": func(text string, s, e int) (int, int) {\n\t\t\taddr := ipaddr.NewHostName(text[s:e])\n\t\t\tif !addr.IsAddress() {\n\t\t\t\treturn -1, -1\n\t\t\t}\n\t\t\treturn s, e\n\t\t},\n\t}\n})\n\ntype KittyOpts struct {\n\tUrl_prefixes              *utils.Set[string]\n\tUrl_excluded_characters   string\n\tSelect_by_word_characters string\n}\n\nfunc read_relevant_kitty_opts() KittyOpts {\n\tans := KittyOpts{\n\t\tSelect_by_word_characters: kitty.KittyConfigDefaults.Select_by_word_characters,\n\t\tUrl_excluded_characters:   kitty.KittyConfigDefaults.Url_excluded_characters}\n\thandle_line := func(key, val string) error {\n\t\tswitch key {\n\t\tcase \"url_prefixes\":\n\t\t\tans.Url_prefixes = utils.NewSetWithItems(strings.Split(val, \" \")...)\n\t\tcase \"select_by_word_characters\":\n\t\t\tans.Select_by_word_characters = strings.TrimSpace(val)\n\t\tcase \"url_excluded_characters\":\n\t\t\tif s, err := config.StringLiteral(val); err == nil {\n\t\t\t\tans.Url_excluded_characters = s\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tconfig.ReadKittyConfig(handle_line)\n\tif ans.Url_prefixes == nil {\n\t\tans.Url_prefixes = utils.NewSetWithItems(kitty.KittyConfigDefaults.Url_prefixes...)\n\t}\n\treturn ans\n}\n\nvar RelevantKittyOpts = sync.OnceValue(func() KittyOpts {\n\treturn read_relevant_kitty_opts()\n})\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc url_excluded_characters_as_ranges_for_regex(extra_excluded string) string {\n\t// See https://url.spec.whatwg.org/#url-code-points\n\tans := strings.Builder{}\n\tans.Grow(4096)\n\ttype cr struct{ start, end rune }\n\tranges := []cr{}\n\tr := func(start rune, end ...rune) {\n\t\tif len(end) == 0 {\n\t\t\tranges = append(ranges, cr{start, start})\n\t\t} else {\n\t\t\tranges = append(ranges, cr{start, end[0]})\n\t\t}\n\t}\n\tif !strings.Contains(extra_excluded, \"\\n\") {\n\t\tr('\\n')\n\t}\n\tif !strings.Contains(extra_excluded, \"\\r\") {\n\t\tr('\\r')\n\t}\n\tr('!')\n\tr('$')\n\tr('&')\n\tr('#')\n\tr('\\'')\n\tr('/')\n\tr(':')\n\tr(';')\n\tr('@')\n\tr('_')\n\tr('~')\n\tr('(')\n\tr(')')\n\tr('*')\n\tr('+')\n\tr(',')\n\tr('-')\n\tr('.')\n\tr('=')\n\tr('?')\n\tr('%')\n\tr('a', 'z')\n\tr('A', 'Z')\n\tr('0', '9')\n\tslices.SortFunc(ranges, func(a, b cr) int { return int(a.start - b.start) })\n\tvar prev rune = -1\n\tfor _, cr := range ranges {\n\t\tif cr.start-1 > prev+1 {\n\t\t\tans.WriteString(regexp.QuoteMeta(string(prev + 1)))\n\t\t\tans.WriteRune('-')\n\t\t\tans.WriteString(regexp.QuoteMeta(string(cr.start - 1)))\n\t\t}\n\t\tprev = cr.end\n\t}\n\tans.WriteString(regexp.QuoteMeta(string(ranges[len(ranges)-1].end + 1)))\n\tans.WriteRune('-')\n\tans.WriteRune(0x9f)\n\tans.WriteString(`\\x{d800}-\\x{dfff}`)\n\tans.WriteString(`\\x{fdd0}-\\x{fdef}`)\n\tw := func(x rune) { ans.WriteRune(x) }\n\n\tw(0xFFFE)\n\tw(0xFFFF)\n\tw(0x1FFFE)\n\tw(0x1FFFF)\n\tw(0x2FFFE)\n\tw(0x2FFFF)\n\tw(0x3FFFE)\n\tw(0x3FFFF)\n\tw(0x4FFFE)\n\tw(0x4FFFF)\n\tw(0x5FFFE)\n\tw(0x5FFFF)\n\tw(0x6FFFE)\n\tw(0x6FFFF)\n\tw(0x7FFFE)\n\tw(0x7FFFF)\n\tw(0x8FFFE)\n\tw(0x8FFFF)\n\tw(0x9FFFE)\n\tw(0x9FFFF)\n\tw(0xAFFFE)\n\tw(0xAFFFF)\n\tw(0xBFFFE)\n\tw(0xBFFFF)\n\tw(0xCFFFE)\n\tw(0xCFFFF)\n\tw(0xDFFFE)\n\tw(0xDFFFF)\n\tw(0xEFFFE)\n\tw(0xEFFFF)\n\tw(0xFFFFE)\n\tw(0xFFFFF)\n\n\tif strings.Contains(extra_excluded, \"-\") {\n\t\textra_excluded = strings.ReplaceAll(extra_excluded, \"-\", \"\")\n\t\textra_excluded = regexp.QuoteMeta(extra_excluded) + \"-\"\n\t} else {\n\t\textra_excluded = regexp.QuoteMeta(extra_excluded)\n\t}\n\tans.WriteString(extra_excluded)\n\treturn ans.String()\n\n}\n\nfunc functions_for(opts *Options) (pattern string, post_processors []PostProcessorFunc, group_processors []GroupProcessorFunc, err error) {\n\tswitch opts.Type {\n\tcase \"url\":\n\t\tvar url_prefixes *utils.Set[string]\n\t\tif opts.UrlPrefixes == \"default\" {\n\t\t\turl_prefixes = RelevantKittyOpts().Url_prefixes\n\t\t} else {\n\t\t\turl_prefixes = utils.NewSetWithItems(strings.Split(opts.UrlPrefixes, \",\")...)\n\t\t}\n\t\turl_excluded_characters := RelevantKittyOpts().Url_excluded_characters\n\t\tif opts.UrlExcludedCharacters != \"default\" {\n\t\t\tif url_excluded_characters, err = config.StringLiteral(opts.UrlExcludedCharacters); err != nil {\n\t\t\t\terr = fmt.Errorf(\"Failed to parse --url-excluded-characters value: %#v with error: %w\", opts.UrlExcludedCharacters, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tpattern = fmt.Sprintf(`(?:%s)://[^%s]{3,}`, strings.Join(url_prefixes.AsSlice(), \"|\"), url_excluded_characters_as_ranges_for_regex(url_excluded_characters))\n\t\tpost_processors = append(post_processors, PostProcessorMap()[\"url\"])\n\tcase \"path\":\n\t\tpattern = path_regex()\n\t\tpost_processors = append(post_processors, PostProcessorMap()[\"brackets\"], PostProcessorMap()[\"quotes\"])\n\tcase \"line\":\n\t\tpattern = \"(?m)^\\\\s*(.+)[\\\\s\\x00]*$\"\n\tcase \"hash\":\n\t\tpattern = \"[0-9a-f][0-9a-f\\r]{6,127}\"\n\tcase \"ip\":\n\t\tpattern = (\n\t\t// IPv4 with no validation\n\t\t`((?:\\d{1,3}\\.){3}\\d{1,3}` + \"|\" +\n\t\t\t// IPv6 with no validation\n\t\t\t`(?:[a-fA-F0-9]{0,4}:){2,7}[a-fA-F0-9]{0,4})`)\n\t\tpost_processors = append(post_processors, PostProcessorMap()[\"ip\"])\n\tdefault:\n\t\tpattern = opts.Regex\n\t\tif opts.Type == \"linenum\" {\n\t\t\tif pattern == kitty.HintsDefaultRegex {\n\t\t\t\tpattern = default_linenum_regex()\n\t\t\t}\n\t\t\tpost_processors = append(post_processors, PostProcessorMap()[\"brackets\"], PostProcessorMap()[\"quotes\"])\n\t\t\tgroup_processors = append(group_processors, linenum_group_processor)\n\t\t}\n\t}\n\treturn\n}\n\ntype Capture struct {\n\tText          string\n\tText_as_runes []rune\n\tByte_Offsets  struct {\n\t\tStart, End int\n\t}\n\tRune_Offsets struct {\n\t\tStart, End int\n\t}\n}\n\nfunc (self Capture) String() string {\n\treturn fmt.Sprintf(\"Capture(start=%d, end=%d, %#v)\", self.Byte_Offsets.Start, self.Byte_Offsets.End, self.Text)\n}\n\ntype Group struct {\n\tName     string\n\tIsNamed  bool\n\tCaptures []Capture\n}\n\nfunc (self Group) LastCapture() Capture {\n\tif len(self.Captures) == 0 {\n\t\treturn Capture{}\n\t}\n\treturn self.Captures[len(self.Captures)-1]\n}\n\nfunc (self Group) String() string {\n\treturn fmt.Sprintf(\"Group(name=%#v, captures=%v)\", self.Name, self.Captures)\n}\n\ntype Match struct {\n\tGroups []Group\n}\n\nfunc (self Match) HasNamedGroups() bool {\n\tfor _, g := range self.Groups {\n\t\tif g.IsNamed {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc find_all_matches(re *regexp2.Regexp, text string) (ans []Match, err error) {\n\tm, err := re.FindStringMatch(text)\n\tif err != nil {\n\t\treturn\n\t}\n\trune_to_bytes := utils.RuneOffsetsToByteOffsets(text)\n\tget_byte_offset_map := func(groups []regexp2.Group) (ans map[int]int, err error) {\n\t\tans = make(map[int]int, len(groups)*2)\n\t\trune_offsets := make([]int, 0, len(groups)*2)\n\t\tfor _, g := range groups {\n\t\t\tfor _, c := range g.Captures {\n\t\t\t\tif _, found := ans[c.Index]; !found {\n\t\t\t\t\trune_offsets = append(rune_offsets, c.Index)\n\t\t\t\t\tans[c.Index] = -1\n\t\t\t\t}\n\t\t\t\tend := c.Index + c.Length\n\t\t\t\tif _, found := ans[end]; !found {\n\t\t\t\t\trune_offsets = append(rune_offsets, end)\n\t\t\t\t\tans[end] = -1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tslices.Sort(rune_offsets)\n\t\tfor _, pos := range rune_offsets {\n\t\t\tif ans[pos] = rune_to_bytes(pos); ans[pos] < 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"Matches are not monotonic cannot map rune offsets to byte offsets\")\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tfor m != nil {\n\t\tgroups := m.Groups()\n\t\tbom, err := get_byte_offset_map(groups)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tmatch := Match{Groups: make([]Group, len(groups))}\n\t\tfor i, g := range m.Groups() {\n\t\t\tmatch.Groups[i].Name = g.Name\n\t\t\tmatch.Groups[i].IsNamed = g.Name != \"\" && g.Name != strconv.Itoa(i)\n\t\t\tfor _, c := range g.Captures {\n\t\t\t\tcn := Capture{Text: c.String(), Text_as_runes: c.Runes()}\n\t\t\t\tcn.Rune_Offsets.End = c.Index + c.Length\n\t\t\t\tcn.Rune_Offsets.Start = c.Index\n\t\t\t\tcn.Byte_Offsets.Start, cn.Byte_Offsets.End = bom[c.Index], bom[cn.Rune_Offsets.End]\n\t\t\t\tmatch.Groups[i].Captures = append(match.Groups[i].Captures, cn)\n\t\t\t}\n\t\t}\n\t\tans = append(ans, match)\n\t\tm, _ = re.FindNextMatch(m)\n\t}\n\treturn\n}\n\nfunc mark(r *regexp2.Regexp, post_processors []PostProcessorFunc, group_processors []GroupProcessorFunc, text string, opts *Options) (ans []Mark) {\n\tsanitize_pat := regexp.MustCompile(\"[\\r\\n\\x00]\")\n\tall_matches, _ := find_all_matches(r, text)\n\tfor i, m := range all_matches {\n\t\tfull_capture := m.Groups[0].LastCapture()\n\t\tmatch_start, match_end := full_capture.Byte_Offsets.Start, full_capture.Byte_Offsets.End\n\t\tfor match_end > match_start+1 && text[match_end-1] == 0 {\n\t\t\tmatch_end--\n\t\t}\n\t\tfull_match := text[match_start:match_end]\n\t\tif len([]rune(full_match)) < opts.MinimumMatchLength {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, f := range post_processors {\n\t\t\tmatch_start, match_end = f(text, match_start, match_end)\n\t\t\tif match_start < 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif match_start < 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfull_match = sanitize_pat.ReplaceAllLiteralString(text[match_start:match_end], \"\")\n\t\tgd := make(map[string]string, len(m.Groups))\n\t\tfor idx, g := range m.Groups {\n\t\t\tif idx > 0 && g.IsNamed {\n\t\t\t\tc := g.LastCapture()\n\t\t\t\tif s, e := c.Byte_Offsets.Start, c.Byte_Offsets.End; s > -1 && e > -1 {\n\t\t\t\t\ts = max(s, match_start)\n\t\t\t\t\te = min(e, match_end)\n\t\t\t\t\tgd[g.Name] = sanitize_pat.ReplaceAllLiteralString(text[s:e], \"\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor _, f := range group_processors {\n\t\t\tf(gd)\n\t\t}\n\t\tgd2 := make(map[string]any, len(gd))\n\t\tfor k, v := range gd {\n\t\t\tgd2[k] = v\n\t\t}\n\t\tif opts.Type == \"regex\" && len(m.Groups) > 1 && !m.HasNamedGroups() {\n\t\t\tcp := m.Groups[1].LastCapture()\n\t\t\tms, me := cp.Byte_Offsets.Start, cp.Byte_Offsets.End\n\t\t\tmatch_start = max(match_start, ms)\n\t\t\tmatch_end = min(match_end, me)\n\t\t\tfull_match = sanitize_pat.ReplaceAllLiteralString(text[match_start:match_end], \"\")\n\t\t}\n\t\tif full_match != \"\" {\n\t\t\tans = append(ans, Mark{\n\t\t\t\tIndex: i, Start: match_start, End: match_end, Text: full_match, Groupdict: gd2,\n\t\t\t})\n\t\t}\n\t}\n\treturn\n}\n\ntype ErrNoMatches struct{ Type, Pattern string }\n\nfunc is_word_char(ch rune, current_chars []rune) bool {\n\treturn unicode.IsLetter(ch) || unicode.IsNumber(ch) || (unicode.IsMark(ch) && len(current_chars) > 0 && unicode.IsLetter(current_chars[len(current_chars)-1]))\n}\n\nfunc mark_words(text string, opts *Options) (ans []Mark) {\n\tleft := text\n\tvar current_run struct {\n\t\tchars       []rune\n\t\tstart, size int\n\t}\n\tchars := opts.WordCharacters\n\tif chars == \"\" {\n\t\tchars = RelevantKittyOpts().Select_by_word_characters\n\t}\n\tallowed_chars := make(map[rune]bool, len(chars))\n\tfor _, ch := range chars {\n\t\tallowed_chars[ch] = true\n\t}\n\tpos := 0\n\tpost_processors := []PostProcessorFunc{PostProcessorMap()[\"brackets\"], PostProcessorMap()[\"quotes\"]}\n\n\tcommit_run := func() {\n\t\tif len(current_run.chars) >= opts.MinimumMatchLength {\n\t\t\tmatch_start, match_end := current_run.start, current_run.start+current_run.size\n\t\t\tfor _, f := range post_processors {\n\t\t\t\tmatch_start, match_end = f(text, match_start, match_end)\n\t\t\t\tif match_start < 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif match_start > -1 && match_end > match_start {\n\t\t\t\tfull_match := text[match_start:match_end]\n\t\t\t\tif len([]rune(full_match)) >= opts.MinimumMatchLength {\n\t\t\t\t\tans = append(ans, Mark{\n\t\t\t\t\t\tIndex: len(ans), Start: match_start, End: match_end, Text: full_match,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcurrent_run.chars = nil\n\t\tcurrent_run.start = 0\n\t\tcurrent_run.size = 0\n\t}\n\n\tfor {\n\t\tch, size := utf8.DecodeRuneInString(left)\n\t\tif ch == utf8.RuneError {\n\t\t\tbreak\n\t\t}\n\t\tif allowed_chars[ch] || is_word_char(ch, current_run.chars) {\n\t\t\tif len(current_run.chars) == 0 {\n\t\t\t\tcurrent_run.start = pos\n\t\t\t}\n\t\t\tcurrent_run.chars = append(current_run.chars, ch)\n\t\t\tcurrent_run.size += size\n\t\t} else {\n\t\t\tcommit_run()\n\t\t}\n\t\tleft = left[size:]\n\t\tpos += size\n\t}\n\tcommit_run()\n\treturn\n}\n\nfunc adjust_python_offsets(text string, marks []Mark) error {\n\t// python returns rune based offsets (unicode chars not utf-8 bytes)\n\tadjust := utils.RuneOffsetsToByteOffsets(text)\n\tfor i := range marks {\n\t\tmark := &marks[i]\n\t\tif mark.End < mark.Start {\n\t\t\treturn fmt.Errorf(\"The end of a mark must not be before its start\")\n\t\t}\n\t\ts, e := adjust(mark.Start), adjust(mark.End)\n\t\tif s < 0 || e < 0 {\n\t\t\treturn fmt.Errorf(\"Overlapping marks are not supported\")\n\t\t}\n\t\tmark.Start, mark.End = s, e\n\t}\n\treturn nil\n}\n\nfunc (self *ErrNoMatches) Error() string {\n\tnone_of := \"matches\"\n\tswitch self.Type {\n\tcase \"urls\":\n\t\tnone_of = \"URLs\"\n\tcase \"hyperlinks\":\n\t\tnone_of = \"hyperlinks\"\n\t}\n\tif self.Pattern != \"\" {\n\t\treturn fmt.Sprintf(\"No %s found with pattern: %s\", none_of, self.Pattern)\n\t}\n\treturn fmt.Sprintf(\"No %s found\", none_of)\n}\n\nfunc find_marks(text string, opts *Options, cli_args ...string) (sanitized_text string, ans []Mark, index_map map[int]*Mark, err error) {\n\tsanitized_text, hyperlinks := process_escape_codes(text)\n\tused_pattern := \"\"\n\n\trun_basic_matching := func() error {\n\t\tpattern, post_processors, group_processors, err := functions_for(opts)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tr, err := regexp2.Compile(pattern, regexp2.RE2)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to compile the regex pattern: %#v with error: %w\", pattern, err)\n\t\t}\n\t\tans = mark(r, post_processors, group_processors, sanitized_text, opts)\n\t\tused_pattern = pattern\n\t\treturn nil\n\t}\n\n\tif opts.CustomizeProcessing != \"\" {\n\t\tcmd := exec.Command(utils.KittyExe(), append([]string{\"+runpy\", \"from kittens.hints.main import custom_marking; custom_marking()\"}, cli_args...)...)\n\t\tcmd.Stdin = strings.NewReader(sanitized_text)\n\t\tstdout, stderr := bytes.Buffer{}, bytes.Buffer{}\n\t\tcmd.Stdout, cmd.Stderr = &stdout, &stderr\n\t\terr = cmd.Run()\n\t\tif err != nil {\n\t\t\tvar e *exec.ExitError\n\t\t\tif errors.As(err, &e) && e.ExitCode() == 2 {\n\t\t\t\terr = run_basic_matching()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tgoto process_answer\n\t\t\t} else {\n\t\t\t\treturn \"\", nil, nil, fmt.Errorf(\"Failed to run custom processor %#v with error: %w\\n%s\", opts.CustomizeProcessing, err, stderr.String())\n\t\t\t}\n\t\t}\n\t\tans = make([]Mark, 0, 32)\n\t\terr = json.Unmarshal(stdout.Bytes(), &ans)\n\t\tif err != nil {\n\t\t\treturn \"\", nil, nil, fmt.Errorf(\"Failed to load output from custom processor %#v with error: %w\", opts.CustomizeProcessing, err)\n\t\t}\n\t\terr = adjust_python_offsets(sanitized_text, ans)\n\t\tif err != nil {\n\t\t\treturn \"\", nil, nil, fmt.Errorf(\"Custom processor %#v produced invalid mark output with error: %w\", opts.CustomizeProcessing, err)\n\t\t}\n\t} else if opts.Type == \"hyperlink\" {\n\t\tans = hyperlinks\n\t} else if opts.Type == \"word\" {\n\t\tans = mark_words(sanitized_text, opts)\n\t} else {\n\t\terr = run_basic_matching()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\nprocess_answer:\n\tif len(ans) == 0 {\n\t\treturn \"\", nil, nil, &ErrNoMatches{Type: opts.Type, Pattern: used_pattern}\n\t}\n\tlargest_index := ans[len(ans)-1].Index\n\toffset := max(0, opts.HintsOffset)\n\tindex_map = make(map[int]*Mark, len(ans))\n\tfor i := range ans {\n\t\tm := &ans[i]\n\t\tif opts.Ascending {\n\t\t\tm.Index += offset\n\t\t} else {\n\t\t\tm.Index = largest_index - m.Index + offset\n\t\t}\n\t\tindex_map[m.Index] = m\n\t}\n\treturn\n}\n"
  },
  {
    "path": "kittens/hints/marks_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage hints\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestHintMarking(t *testing.T) {\n\n\tvar opts *Options\n\tcols := 20\n\tcli_args := []string{}\n\n\treset := func() {\n\t\topts = &Options{Type: \"url\", UrlPrefixes: \"default\", Regex: kitty.HintsDefaultRegex}\n\t\tcols = 20\n\t\tcli_args = []string{}\n\t}\n\n\tr := func(text string, url ...string) (marks []Mark) {\n\t\tptext := convert_text(text, cols)\n\t\tptext, marks, _, err := find_marks(ptext, opts, cli_args...)\n\t\tif err != nil {\n\t\t\tvar e *ErrNoMatches\n\t\t\tif len(url) != 0 || !errors.As(err, &e) {\n\t\t\t\tt.Fatalf(\"%#v failed with error: %s\", text, err)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tactual := utils.Map(func(m Mark) string { return m.Text }, marks)\n\t\tif diff := cmp.Diff(url, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"%#v failed:\\n%s\", text, diff)\n\t\t}\n\t\tfor _, m := range marks {\n\t\t\tq := strings.NewReplacer(\"\\n\", \"\", \"\\r\", \"\", \"\\x00\", \"\").Replace(ptext[m.Start:m.End])\n\t\t\tif diff := cmp.Diff(m.Text, q); diff != \"\" {\n\t\t\t\tt.Fatalf(\"Mark start (%d) and end (%d) dont point to correct offset in text for %#v\\n%s\", m.Start, m.End, text, diff)\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\treset()\n\tu := `http://test.me/`\n\tr(u, u)\n\tr(u+\"#fragme\", u+\"#fragme\")\n\tr(`\"`+u+`\"`, u)\n\tr(\"(\"+u+\")\", u)\n\tcols = len(u)\n\tr(u+\"\\nxxx\", u+\"xxx\")\n\tcols = 20\n\tr(\"link:\"+u+\"[xxx]\", u)\n\tr(\"`xyz <\"+u+\">`_.\", u)\n\tr(`<a href=\"`+u+`\">moo`, u)\n\tr(\"\\x1b[mhttp://test.me/1234\\n\\x1b[mx\", \"http://test.me/1234\")\n\tr(\"\\x1b[mhttp://test.me/12345\\r\\x1b[m6\\n\\x1b[mx\", \"http://test.me/123456\")\n\n\topts.Type = \"linenum\"\n\tm := func(text, path string, line int) {\n\t\tptext := convert_text(text, cols)\n\t\t_, marks, _, err := find_marks(ptext, opts, cli_args...)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%#v failed with error: %s\", text, err)\n\t\t}\n\t\tgd := map[string]any{\"path\": path, \"line\": strconv.Itoa(line)}\n\t\tif diff := cmp.Diff(marks[0].Groupdict, gd); diff != \"\" {\n\t\t\tt.Fatalf(\"%#v failed:\\n%s\", text, diff)\n\t\t}\n\t}\n\tm(\"file.c:23\", \"file.c\", 23)\n\tm(\"file.c:23:32\", \"file.c\", 23)\n\tm(\"file.cpp:23:1\", \"file.cpp\", 23)\n\tm(\"a/file.c:23\", \"a/file.c\", 23)\n\tm(\"a/file.c:23:32\", \"a/file.c\", 23)\n\tm(\"~/file.c:23:32\", utils.Expanduser(\"~/file.c\"), 23)\n\n\treset()\n\topts.Type = \"path\"\n\tr(\"file.c\", \"file.c\")\n\tr(\"file.c.\", \"file.c\")\n\tr(\"file.epub.\", \"file.epub\")\n\tr(\"(file.epub)\", \"file.epub\")\n\tr(\"some/path\", \"some/path\")\n\n\treset()\n\tcols = 60\n\topts.Type = \"ip\"\n\tr(`100.64.0.0`, `100.64.0.0`)\n\tr(`2001:0db8:0000:0000:0000:ff00:0042:8329`, `2001:0db8:0000:0000:0000:ff00:0042:8329`)\n\tr(`2001:db8:0:0:0:ff00:42:8329`, `2001:db8:0:0:0:ff00:42:8329`)\n\tr(`2001:db8::ff00:42:8329`, `2001:db8::ff00:42:8329`)\n\tr(`2001:DB8::FF00:42:8329`, `2001:DB8::FF00:42:8329`)\n\tr(`0000:0000:0000:0000:0000:0000:0000:0001`, `0000:0000:0000:0000:0000:0000:0000:0001`)\n\tr(`2600:1901:0:b2bd::`, `2600:1901:0:b2bd::`) // ifconfig.me\n\tr(`::1`, `::1`)\n\tr(`255.255.255.256`)\n\tr(`:1`)\n\n\treset()\n\topts.Type = \"regex\"\n\topts.Regex = `(?ms)^[*]?\\s(\\S+)`\n\tr(`* 2b687c2 - test1`, `2b687c2`)\n\topts.Regex = `(?<=got:    )sha256.{4}`\n\tr(`got:    sha256-L8=`, `sha256-L8=`)\n\n\treset()\n\topts.Type = \"word\"\n\tr(`#one (two) 😍 a-1b `, `#one`, `two`, `a-1b`)\n\tr(\"fōtiz час a\\u0310b \", `fōtiz`, `час`, \"a\\u0310b\")\n\n\treset()\n\ttdir := t.TempDir()\n\tsimple := filepath.Join(tdir, \"simple.py\")\n\tcli_args = []string{\"--customize-processing\", simple, \"extra1\"}\n\tos.WriteFile(simple, []byte(`\ndef mark(text, args, Mark, extra_cli_args, *a):\n    import re\n    for idx, m in enumerate(re.finditer(r'\\w+', text)):\n        start, end = m.span()\n        mark_text = text[start:end].replace('\\n', '').replace('\\0', '')\n        yield Mark(idx, start, end, mark_text, {\"idx\": idx, \"args\": extra_cli_args})\n`), 0o600)\n\topts.Type = \"regex\"\n\topts.CustomizeProcessing = simple\n\tmarks := r(\"漢字 b\", `漢字`, `b`)\n\tif diff := cmp.Diff(marks[0].Groupdict, map[string]any{\"idx\": float64(0), \"args\": []any{\"extra1\"}}); diff != \"\" {\n\t\tt.Fatalf(\"Did not get expected groupdict from custom processor:\\n%s\", diff)\n\t}\n\topts.Regex = \"b\"\n\tos.WriteFile(simple, []byte(\"\"), 0o600)\n\tr(\"a b\", `b`)\n}\n"
  },
  {
    "path": "kittens/hyperlinked_grep/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/hyperlinked_grep/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage hyperlinked_grep\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\t\"unicode\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nvar RgExe = sync.OnceValue(func() string {\n\treturn utils.FindExe(\"rg\")\n})\n\nfunc get_options_for_rg() (expecting_args map[string]bool, alias_map map[string]string, err error) {\n\tvar raw []byte\n\traw, err = exec.Command(RgExe(), \"--help\").Output()\n\tif err != nil {\n\t\terr = fmt.Errorf(\"Failed to execute rg: %w\", err)\n\t\treturn\n\t}\n\tscanner := utils.NewLineScanner(utils.UnsafeBytesToString(raw))\n\toptions_started := false\n\texpecting_args = make(map[string]bool, 64)\n\talias_map = make(map[string]string, 52)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tif options_started {\n\t\t\ts := strings.TrimLeft(line, \" \")\n\t\t\tindent := len(line) - len(s)\n\t\t\tif indent < 8 && indent > 0 {\n\t\t\t\texpecting_arg := strings.Contains(s, \"=\")\n\t\t\t\tsingle_letter_aliases := make([]string, 0, 1)\n\t\t\t\tlong_option_names := make([]string, 0, 1)\n\t\t\t\tfor x := range strings.SplitSeq(s, \",\") {\n\t\t\t\t\tx = strings.TrimSpace(x)\n\t\t\t\t\tif strings.HasPrefix(x, \"--\") {\n\t\t\t\t\t\tlon, _, _ := strings.Cut(x[2:], \"=\")\n\t\t\t\t\t\tlong_option_names = append(long_option_names, lon)\n\t\t\t\t\t} else if strings.HasPrefix(x, \"-\") {\n\t\t\t\t\t\tson, _, _ := strings.Cut(x[1:], \" \")\n\t\t\t\t\t\tsingle_letter_aliases = append(single_letter_aliases, son)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif len(long_option_names) == 0 {\n\t\t\t\t\terr = fmt.Errorf(\"Failed to parse rg help output line: %s\", line)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tfor _, x := range single_letter_aliases {\n\t\t\t\t\talias_map[x] = long_option_names[0]\n\t\t\t\t}\n\t\t\t\tfor _, x := range long_option_names[1:] {\n\t\t\t\t\talias_map[x] = long_option_names[0]\n\t\t\t\t}\n\t\t\t\texpecting_args[long_option_names[0]] = expecting_arg\n\t\t\t}\n\t\t} else {\n\t\t\tif strings.HasSuffix(line, \"OPTIONS:\") {\n\t\t\t\toptions_started = true\n\t\t\t}\n\t\t}\n\t}\n\tif len(expecting_args) == 0 || len(alias_map) == 0 {\n\t\terr = fmt.Errorf(\"Failed to parse rg help output, could not find any options\")\n\t\treturn\n\t}\n\treturn\n}\n\ntype kitten_options struct {\n\tmatching_lines, context_lines, file_headers    bool\n\twith_filename, heading, line_number            bool\n\tstats, count, count_matches                    bool\n\tfiles, files_with_matches, files_without_match bool\n\tvimgrep                                        bool\n}\n\nfunc default_kitten_opts() *kitten_options {\n\treturn &kitten_options{\n\t\tmatching_lines: true, context_lines: true, file_headers: true,\n\t\twith_filename: true, heading: true, line_number: true,\n\t}\n\n}\n\nfunc parse_args(args ...string) (delegate_to_rg bool, sanitized_args []string, kitten_opts *kitten_options, err error) {\n\toptions_that_expect_args, alias_map, err := get_options_for_rg()\n\tif err != nil {\n\t\treturn\n\t}\n\toptions_that_expect_args[\"kitten\"] = true\n\tkitten_opts = default_kitten_opts()\n\tsanitized_args = make([]string, 0, len(args))\n\texpecting_option_arg := \"\"\n\n\tcontext_separator := \"--\"\n\tfield_context_separator := \"-\"\n\tfield_match_separator := \"-\"\n\n\thandle_option_arg := func(key, val string, with_equals bool) error {\n\t\tif key != \"kitten\" {\n\t\t\tif with_equals {\n\t\t\t\tsanitized_args = append(sanitized_args, \"--\"+key+\"=\"+val)\n\t\t\t} else {\n\t\t\t\tsanitized_args = append(sanitized_args, \"--\"+key, val)\n\t\t\t}\n\t\t}\n\t\tswitch key {\n\t\tcase \"path-separator\":\n\t\t\tif val != string(os.PathSeparator) {\n\t\t\t\tdelegate_to_rg = true\n\t\t\t}\n\t\tcase \"context-separator\":\n\t\t\tcontext_separator = val\n\t\tcase \"field-context-separator\":\n\t\t\tfield_context_separator = val\n\t\tcase \"field-match-separator\":\n\t\t\tfield_match_separator = val\n\t\tcase \"kitten\":\n\t\t\tk, v, found := strings.Cut(val, \"=\")\n\t\t\tif !found || k != \"hyperlink\" {\n\t\t\t\treturn fmt.Errorf(\"Unknown --kitten option: %s\", val)\n\t\t\t}\n\t\t\tfor x := range strings.SplitSeq(v, \",\") {\n\t\t\t\tswitch x {\n\t\t\t\tcase \"none\":\n\t\t\t\t\tkitten_opts.context_lines = false\n\t\t\t\t\tkitten_opts.file_headers = false\n\t\t\t\t\tkitten_opts.matching_lines = false\n\t\t\t\tcase \"all\":\n\t\t\t\t\tkitten_opts.context_lines = true\n\t\t\t\t\tkitten_opts.file_headers = true\n\t\t\t\t\tkitten_opts.matching_lines = true\n\t\t\t\tcase \"matching_lines\":\n\t\t\t\t\tkitten_opts.matching_lines = true\n\t\t\t\tcase \"file_headers\":\n\t\t\t\t\tkitten_opts.file_headers = true\n\t\t\t\tcase \"context_lines\":\n\t\t\t\t\tkitten_opts.context_lines = true\n\t\t\t\tdefault:\n\t\t\t\t\treturn fmt.Errorf(\"hyperlink option invalid: %s\", x)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\thandle_bool_option := func(key string) {\n\t\tswitch key {\n\t\tcase \"no-context-separator\":\n\t\t\tcontext_separator = \"\"\n\t\tcase \"no-filename\":\n\t\t\tkitten_opts.with_filename = false\n\t\tcase \"with-filename\":\n\t\t\tkitten_opts.with_filename = true\n\t\tcase \"heading\":\n\t\t\tkitten_opts.heading = true\n\t\tcase \"no-heading\":\n\t\t\tkitten_opts.heading = false\n\t\tcase \"line-number\":\n\t\t\tkitten_opts.line_number = true\n\t\tcase \"no-line-number\":\n\t\t\tkitten_opts.line_number = false\n\t\tcase \"pretty\":\n\t\t\tkitten_opts.line_number = true\n\t\t\tkitten_opts.heading = true\n\t\tcase \"stats\":\n\t\t\tkitten_opts.stats = true\n\t\tcase \"count\":\n\t\t\tkitten_opts.count = true\n\t\tcase \"count-matches\":\n\t\t\tkitten_opts.count_matches = true\n\t\tcase \"files\":\n\t\t\tkitten_opts.files = true\n\t\tcase \"files-with-matches\":\n\t\t\tkitten_opts.files_with_matches = true\n\t\tcase \"files-without-match\":\n\t\t\tkitten_opts.files_without_match = true\n\t\tcase \"vimgrep\":\n\t\t\tkitten_opts.vimgrep = true\n\t\tcase \"null\", \"null-data\", \"type-list\", \"version\", \"help\":\n\t\t\tdelegate_to_rg = true\n\t\t}\n\t}\n\n\tfor i, x := range args {\n\t\tif expecting_option_arg != \"\" {\n\t\t\tif err = handle_option_arg(expecting_option_arg, x, false); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpecting_option_arg = \"\"\n\t\t} else {\n\t\t\tif x == \"--\" {\n\t\t\t\tsanitized_args = append(sanitized_args, args[i:]...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif strings.HasPrefix(x, \"--\") {\n\t\t\t\ta, b, found := strings.Cut(x, \"=\")\n\t\t\t\ta = a[2:]\n\t\t\t\tq := alias_map[a]\n\t\t\t\tif q != \"\" {\n\t\t\t\t\ta = q\n\t\t\t\t}\n\t\t\t\tif found {\n\t\t\t\t\tif _, is_known_option := options_that_expect_args[a]; is_known_option {\n\t\t\t\t\t\tif err = handle_option_arg(a, b, true); err != nil {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsanitized_args = append(sanitized_args, x)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif options_that_expect_args[a] {\n\t\t\t\t\t\texpecting_option_arg = a\n\t\t\t\t\t} else {\n\t\t\t\t\t\thandle_bool_option(a)\n\t\t\t\t\t\tsanitized_args = append(sanitized_args, x)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if strings.HasPrefix(x, \"-\") {\n\t\t\t\tok := true\n\t\t\t\tchars := make([]string, len(x)-1)\n\t\t\t\tfor i, ch := range x[1:] {\n\t\t\t\t\tchars[i] = string(ch)\n\t\t\t\t\t_, ok = alias_map[string(ch)]\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tsanitized_args = append(sanitized_args, x)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ok {\n\t\t\t\t\tfor _, ch := range chars {\n\t\t\t\t\t\ttarget := alias_map[ch]\n\t\t\t\t\t\tif options_that_expect_args[target] {\n\t\t\t\t\t\t\texpecting_option_arg = target\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\thandle_bool_option(target)\n\t\t\t\t\t\t\tsanitized_args = append(sanitized_args, \"-\"+ch)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsanitized_args = append(sanitized_args, x)\n\t\t\t}\n\t\t}\n\t}\n\tif !kitten_opts.with_filename || context_separator != \"--\" || field_context_separator != \"-\" || field_match_separator != \"-\" {\n\t\tdelegate_to_rg = true\n\t}\n\treturn\n}\n\ntype stdout_filter struct {\n\tprefix       []byte\n\tprocess_line func(string)\n}\n\nfunc (self *stdout_filter) Write(p []byte) (n int, err error) {\n\tn = len(p)\n\tfor len(p) > 0 {\n\t\tidx := bytes.IndexByte(p, '\\n')\n\t\tif idx < 0 {\n\t\t\tself.prefix = append(self.prefix, p...)\n\t\t\tbreak\n\t\t}\n\t\tline := p[:idx]\n\t\tif len(self.prefix) > 0 {\n\t\t\tself.prefix = append(self.prefix, line...)\n\t\t\tline = self.prefix\n\t\t}\n\t\tp = p[idx+1:]\n\t\tself.process_line(utils.UnsafeBytesToString(line))\n\t\tself.prefix = self.prefix[:0]\n\t}\n\treturn\n}\n\nfunc main(_ *cli.Command, _ *Options, args []string) (rc int, err error) {\n\tdelegate_to_rg, sanitized_args, kitten_opts, err := parse_args(args...)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tif delegate_to_rg {\n\t\tsanitized_args = append([]string{\"rg\"}, sanitized_args...)\n\t\terr = unix.Exec(RgExe(), sanitized_args, os.Environ())\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"Failed to execute rg: %w\", err)\n\t\t\trc = 1\n\t\t}\n\t\treturn\n\t}\n\tcmdline := append([]string{\"--pretty\", \"--with-filename\"}, sanitized_args...)\n\tcmd := exec.Command(RgExe(), cmdline...)\n\tcmd.Stdin = os.Stdin\n\tcmd.Stderr = os.Stderr\n\tbuf := stdout_filter{prefix: make([]byte, 0, 8*1024)}\n\tcmd.Stdout = &buf\n\tsgr_pat := regexp.MustCompile(\"\\x1b\\\\[.*?m\")\n\tosc_pat := regexp.MustCompile(\"\\x1b\\\\].*?\\x1b\\\\\\\\\")\n\tnum_pat := regexp.MustCompile(`^(\\d+)([:-])`)\n\tpath_with_count_pat := regexp.MustCompile(`^(.*?)(:\\d+)`)\n\tpath_with_linenum_pat := regexp.MustCompile(`^(.*?):(\\d+):`)\n\tstats_pat := regexp.MustCompile(`^\\d+ matches$`)\n\tvimgrep_pat := regexp.MustCompile(`^(.*?):(\\d+):(\\d+):`)\n\n\tin_stats := false\n\tin_result := \"\"\n\thostname := utils.Hostname()\n\n\tget_quoted_url := func(file_path string) string {\n\t\tq, err := filepath.Abs(file_path)\n\t\tif err == nil {\n\t\t\tfile_path = q\n\t\t}\n\t\tfile_path = filepath.ToSlash(file_path)\n\t\tfile_path = strings.Join(utils.Map(url.PathEscape, strings.Split(file_path, \"/\")), \"/\")\n\t\treturn \"file://\" + hostname + file_path\n\t}\n\n\twrite := func(items ...string) {\n\t\tfor _, x := range items {\n\t\t\tos.Stdout.WriteString(x)\n\t\t}\n\t}\n\n\twrite_hyperlink := func(url, line, frag string) {\n\t\twrite(\"\\033]8;;\", url)\n\t\tif frag != \"\" {\n\t\t\twrite(\"#\", frag)\n\t\t}\n\t\twrite(\"\\033\\\\\", line, \"\\n\\033]8;;\\033\\\\\")\n\t}\n\n\tbuf.process_line = func(line string) {\n\t\tline = osc_pat.ReplaceAllLiteralString(line, \"\") // remove existing hyperlinks\n\t\tclean_line := strings.TrimRightFunc(line, unicode.IsSpace)\n\t\tclean_line = sgr_pat.ReplaceAllLiteralString(clean_line, \"\") // remove SGR formatting\n\t\tif clean_line == \"\" {\n\t\t\tin_result = \"\"\n\t\t\twrite(\"\\n\")\n\t\t} else if in_stats {\n\t\t\twrite(line, \"\\n\")\n\t\t} else if in_result != \"\" {\n\t\t\tif kitten_opts.line_number {\n\t\t\t\tm := num_pat.FindStringSubmatch(clean_line)\n\t\t\t\tif len(m) > 0 {\n\t\t\t\t\tis_match_line := len(m) > 1 && m[2] == \":\"\n\t\t\t\t\tif (is_match_line && kitten_opts.matching_lines) || (!is_match_line && kitten_opts.context_lines) {\n\t\t\t\t\t\twrite_hyperlink(in_result, line, m[1])\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\twrite(line, \"\\n\")\n\t\t} else {\n\t\t\tif strings.TrimSpace(line) != \"\" {\n\t\t\t\t// The option priority should be consistent with ripgrep here.\n\t\t\t\tif kitten_opts.stats && !in_stats && stats_pat.MatchString(clean_line) {\n\t\t\t\t\tin_stats = true\n\t\t\t\t} else if kitten_opts.count || kitten_opts.count_matches {\n\t\t\t\t\tif m := path_with_count_pat.FindStringSubmatch(clean_line); len(m) > 0 && kitten_opts.file_headers {\n\t\t\t\t\t\twrite_hyperlink(get_quoted_url(m[1]), line, \"\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t} else if kitten_opts.files || kitten_opts.files_with_matches || kitten_opts.files_without_match {\n\t\t\t\t\tif kitten_opts.file_headers {\n\t\t\t\t\t\twrite_hyperlink(get_quoted_url(clean_line), line, \"\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t} else if kitten_opts.vimgrep || !kitten_opts.heading {\n\t\t\t\t\tvar m []string\n\t\t\t\t\t// When the vimgrep option is present, it will take precedence.\n\t\t\t\t\tif kitten_opts.vimgrep {\n\t\t\t\t\t\tm = vimgrep_pat.FindStringSubmatch(clean_line)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tm = path_with_linenum_pat.FindStringSubmatch(clean_line)\n\t\t\t\t\t}\n\t\t\t\t\tif len(m) > 0 && (kitten_opts.file_headers || kitten_opts.matching_lines) {\n\t\t\t\t\t\twrite_hyperlink(get_quoted_url(m[1]), line, m[2])\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tin_result = get_quoted_url(clean_line)\n\t\t\t\t\tif kitten_opts.file_headers {\n\t\t\t\t\t\twrite_hyperlink(in_result, line, \"\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\twrite(line, \"\\n\")\n\t\t}\n\t}\n\n\terr = cmd.Run()\n\tvar ee *exec.ExitError\n\tif err != nil {\n\t\tif errors.As(err, &ee) {\n\t\t\treturn ee.ExitCode(), nil\n\t\t}\n\t\treturn 1, fmt.Errorf(\"Failed to execute rg: %w\", err)\n\t}\n\n\treturn\n}\n\nfunc specialize_command(hg *cli.Command) {\n\thg.Usage = \"arguments for the rg command\"\n\thg.ShortDescription = \"Add hyperlinks to the output of ripgrep\"\n\thg.HelpText = \"The hyperlinked_grep kitten is a thin wrapper around the rg command. It automatically adds hyperlinks to the output of rg allowing the user to click on search results to have them open directly in their editor. For details on its usage, see :doc:`/kittens/hyperlinked_grep`.\"\n\thg.IgnoreAllArgs = true\n\thg.OnlyArgsAllowed = true\n\thg.ArgCompleter = cli.CompletionForWrapper(\"rg\")\n}\n\ntype Options struct {\n}\n\nfunc create_cmd(root *cli.Command, run_func func(*cli.Command, *Options, []string) (int, error)) {\n\tans := root.AddSubCommand(&cli.Command{\n\t\tName: \"hyperlinked_grep\",\n\t\tRun: func(cmd *cli.Command, args []string) (int, error) {\n\t\t\topts := Options{}\n\t\t\terr := cmd.GetOptionValues(&opts)\n\t\t\tif err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\treturn run_func(cmd, &opts, args)\n\t\t},\n\t\tHidden: true,\n\t})\n\tspecialize_command(ans)\n\tclone := root.AddClone(ans.Group, ans)\n\tclone.Hidden = false\n\tclone.Name = \"hyperlinked-grep\"\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/hyperlinked_grep/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\n\nif __name__ == '__main__':\n    raise SystemExit('This should be run as kitten hyperlinked_grep')\nelif __name__ == '__wrapper_of__':\n    cd = sys.cli_docs  # type: ignore\n    cd['wrapper_of'] = 'rg'\n"
  },
  {
    "path": "kittens/hyperlinked_grep/main_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage hyperlinked_grep\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestRgArgParsing(t *testing.T) {\n\tif RgExe() == \"rg\" {\n\t\tt.Skip(\"Skipping as rg not found in PATH\")\n\t}\n\n\tcheck_failure := func(args ...string) {\n\t\t_, _, _, err := parse_args(args...)\n\t\tif err == nil {\n\t\t\tt.Fatalf(\"No error when parsing: %#v\", args)\n\t\t}\n\t}\n\tcheck_failure(\"--kitten\", \"xyz\")\n\tcheck_failure(\"--kitten\", \"xyz=1\")\n\n\tcheck_kitten_opts := func(matching, context, headers bool, args ...string) {\n\t\t_, _, kitten_opts, err := parse_args(args...)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error when parsing: %#v: %s\", args, err)\n\t\t}\n\t\tif matching != kitten_opts.matching_lines {\n\t\t\tt.Fatalf(\"Matching lines not correct for: %#v\", args)\n\t\t}\n\t\tif context != kitten_opts.context_lines {\n\t\t\tt.Fatalf(\"Context lines not correct for: %#v\", args)\n\t\t}\n\t\tif headers != kitten_opts.file_headers {\n\t\t\tt.Fatalf(\"File headers not correct for: %#v\", args)\n\t\t}\n\t}\n\tcheck_kitten_opts(true, true, true)\n\tcheck_kitten_opts(false, false, false, \"--kitten\", \"hyperlink=none\")\n\tcheck_kitten_opts(false, false, true, \"--kitten\", \"hyperlink=none\", \"--count\", \"--kitten=hyperlink=file_headers\")\n\tcheck_kitten_opts(false, false, true, \"--kitten\", \"hyperlink=none,file_headers\")\n\n\tcheck_kitten_opts = func(with_filename, heading, line_number bool, args ...string) {\n\t\t_, _, kitten_opts, err := parse_args(args...)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error when parsing: %#v: %s\", args, err)\n\t\t}\n\t\tif with_filename != kitten_opts.with_filename {\n\t\t\tt.Fatalf(\"with_filename not correct for: %#v\", args)\n\t\t}\n\t\tif heading != kitten_opts.heading {\n\t\t\tt.Fatalf(\"heading not correct for: %#v\", args)\n\t\t}\n\t\tif line_number != kitten_opts.line_number {\n\t\t\tt.Fatalf(\"line_number not correct for: %#v\", args)\n\t\t}\n\t}\n\n\tcheck_kitten_opts(true, true, true)\n\tcheck_kitten_opts(true, false, true, \"--no-heading\")\n\tcheck_kitten_opts(true, true, true, \"--no-heading\", \"--pretty\")\n\tcheck_kitten_opts(true, true, true, \"--no-heading\", \"--heading\")\n\n\tcheck_args := func(args, expected string) {\n\t\ta, err := shlex.Split(args)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\t_, actual, _, err := parse_args(a...)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error when parsing: %#v: %s\", args, err)\n\t\t}\n\t\tex, err := shlex.Split(expected)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif diff := cmp.Diff(ex, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"args not correct for %s\\n%s\", args, diff)\n\t\t}\n\t}\n\tcheck_args(\"--count --max-depth 10 --XxX yyy abcd\", \"--count --max-depth 10 --XxX yyy abcd\")\n\tcheck_args(\"--max-depth=10 --kitten hyperlink=none abcd\", \"--max-depth=10 abcd\")\n\tcheck_args(\"-m 10 abcd\", \"--max-count 10 abcd\")\n\tcheck_args(\"-nm 10 abcd\", \"-n --max-count 10 abcd\")\n\tcheck_args(\"-mn 10 abcd\", \"-n --max-count 10 abcd\")\n\n}\n"
  },
  {
    "path": "kittens/icat/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/icat/detect.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage icat\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nvar _ = fmt.Print\n\nfunc DetectSupport(timeout time.Duration) (memory, files, direct bool, err error) {\n\ttemp_files_to_delete := make([]string, 0, 8)\n\tshm_files_to_delete := make([]shm.MMap, 0, 8)\n\tvar direct_query_id, file_query_id, memory_query_id uint32\n\tlp, e := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)\n\tif e != nil {\n\t\terr = e\n\t\treturn\n\t}\n\tprint_error := func(format string, args ...any) {\n\t\tlp.Println(fmt.Sprintf(format, args...))\n\t}\n\n\tdefer func() {\n\t\tif len(temp_files_to_delete) > 0 && transfer_by_file != supported {\n\t\t\tfor _, name := range temp_files_to_delete {\n\t\t\t\tos.Remove(name)\n\t\t\t}\n\t\t}\n\t\tif len(shm_files_to_delete) > 0 && transfer_by_memory != supported {\n\t\t\tfor _, name := range shm_files_to_delete {\n\t\t\t\t_ = name.Unlink()\n\t\t\t}\n\t\t}\n\t}()\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tvar iid uint32\n\t\t_, _ = lp.AddTimer(timeout, false, func(loop.IdType) error {\n\t\t\treturn fmt.Errorf(\"Timed out waiting for a response from the terminal: %w\", os.ErrDeadlineExceeded)\n\t\t})\n\n\t\tg := func(t graphics.GRT_t, payload string) uint32 {\n\t\t\tiid += 1\n\t\t\tg1 := &graphics.GraphicsCommand{}\n\t\t\tg1.SetTransmission(t).SetAction(graphics.GRT_action_query).SetImageId(iid).SetDataWidth(1).SetDataHeight(1).SetFormat(\n\t\t\t\tgraphics.GRT_format_rgb).SetDataSize(uint64(len(payload)))\n\t\t\t_ = g1.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(payload))\n\t\t\treturn iid\n\t\t}\n\n\t\tdirect_query_id = g(graphics.GRT_transmission_direct, \"123\")\n\t\ttf, err := images.CreateTempInRAM()\n\t\tif err == nil {\n\t\t\tfile_query_id = g(graphics.GRT_transmission_tempfile, tf.Name())\n\t\t\ttemp_files_to_delete = append(temp_files_to_delete, tf.Name())\n\t\t\tif _, err = tf.Write([]byte{1, 2, 3}); err != nil {\n\t\t\t\tprint_error(\"Failed to write to temporary file for data transfer, file based transfer is disabled. Error: %v\", err)\n\t\t\t}\n\t\t\ttf.Close()\n\t\t} else {\n\t\t\tprint_error(\"Failed to create temporary file for data transfer, file based transfer is disabled. Error: %v\", err)\n\t\t}\n\t\tsf, err := shm.CreateTemp(\"icat-\", 3)\n\t\tif err == nil {\n\t\t\tmemory_query_id = g(graphics.GRT_transmission_sharedmem, sf.Name())\n\t\t\tshm_files_to_delete = append(shm_files_to_delete, sf)\n\t\t\tcopy(sf.Slice(), []byte{1, 2, 3})\n\t\t\tsf.Close()\n\t\t} else {\n\t\t\tvar ens *shm.ErrNotSupported\n\t\t\tif !errors.As(err, &ens) {\n\t\t\t\tprint_error(\"Failed to create SHM for data transfer, memory based transfer is disabled. Error: %v\", err)\n\t\t\t}\n\t\t}\n\t\tlp.QueueWriteString(\"\\x1b[c\")\n\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnEscapeCode = func(etype loop.EscapeCodeType, payload []byte) (err error) {\n\t\tswitch etype {\n\t\tcase loop.CSI:\n\t\t\tif len(payload) > 3 && payload[0] == '?' && payload[len(payload)-1] == 'c' {\n\t\t\t\tlp.Quit(0)\n\t\t\t\treturn nil\n\t\t\t}\n\t\tcase loop.APC:\n\t\t\tg := graphics.GraphicsCommandFromAPC(payload)\n\t\t\tif g != nil {\n\t\t\t\tif g.ResponseMessage() == \"OK\" {\n\t\t\t\t\tswitch g.ImageId() {\n\t\t\t\t\tcase direct_query_id:\n\t\t\t\t\t\tdirect = true\n\t\t\t\t\tcase file_query_id:\n\t\t\t\t\t\tfiles = true\n\t\t\t\t\tcase memory_query_id:\n\t\t\t\t\t\tmemory = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\t\tevent.Handled = true\n\t\t\tprint_error(\"Waiting for response from terminal, aborting now could lead to corruption\")\n\t\t}\n\t\tif event.MatchesPressOrRepeat(\"ctrl+z\") {\n\t\t\tevent.Handled = true\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "kittens/icat/main.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage icat\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"runtime\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/imaging\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\ntype Place struct {\n\twidth, height, left, top int\n}\n\nvar opts *Options\nvar place *Place\nvar z_index int32\nvar remove_alpha *imaging.NRGBColor\nvar flip, flop bool\n\ntype transfer_mode int\n\nconst (\n\tunknown transfer_mode = iota\n\tunsupported\n\tsupported\n)\n\ntype fit_t int\n\nconst (\n\tfit_none fit_t = iota\n\tfit_width\n\tfit_height\n\tfit_both\n)\n\nvar transfer_by_file, transfer_by_memory transfer_mode\n\nvar files_channel chan input_arg\nvar output_channel chan *image_data\nvar num_of_items int\nvar keep_going *atomic.Bool\nvar screen_size *unix.Winsize\nvar fit_mode fit_t\n\nfunc send_output(imgd *image_data) {\n\toutput_channel <- imgd\n}\n\nfunc parse_mirror() (err error) {\n\tflip = opts.Mirror == \"both\" || opts.Mirror == \"vertical\"\n\tflop = opts.Mirror == \"both\" || opts.Mirror == \"horizontal\"\n\treturn\n}\n\nfunc parse_background() (err error) {\n\tif opts.Background == \"\" || opts.Background == \"none\" {\n\t\treturn nil\n\t}\n\tcol, err := style.ParseColor(opts.Background)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Invalid value for --background: %w\", err)\n\t}\n\tremove_alpha = &imaging.NRGBColor{R: col.Red, G: col.Green, B: col.Blue}\n\treturn\n}\n\nfunc parse_z_index() (err error) {\n\tval := opts.ZIndex\n\tvar origin int32\n\tif strings.HasPrefix(val, \"--\") {\n\t\torigin = -1073741824\n\t\tval = val[1:]\n\t}\n\ti, err := strconv.ParseInt(val, 10, 32)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Invalid value for --z-index with error: %w\", err)\n\t}\n\tz_index = int32(i) + origin\n\treturn\n}\n\nfunc parse_fit() (err error) {\n\tswitch strings.ToLower(opts.Fit) {\n\tcase \"width\":\n\t\tfit_mode = fit_width\n\tcase \"height\":\n\t\tfit_mode = fit_height\n\tcase \"none\", \"neither\":\n\t\tfit_mode = fit_none\n\tcase \"both\":\n\t\tfit_mode = fit_both\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown fit specification: %#v\", opts.Fit)\n\t}\n\treturn nil\n}\n\nfunc parse_place() (err error) {\n\tif opts.Place == \"\" {\n\t\treturn nil\n\t}\n\tarea, pos, found := strings.Cut(opts.Place, \"@\")\n\tif !found {\n\t\treturn fmt.Errorf(\"Invalid --place specification: %s\", opts.Place)\n\t}\n\tw, h, found := strings.Cut(area, \"x\")\n\tif !found {\n\t\treturn fmt.Errorf(\"Invalid --place specification: %s\", opts.Place)\n\t}\n\tl, t, found := strings.Cut(pos, \"x\")\n\tif !found {\n\t\treturn fmt.Errorf(\"Invalid --place specification: %s\", opts.Place)\n\t}\n\tplace = &Place{}\n\tplace.width, err = strconv.Atoi(w)\n\tif err != nil {\n\t\treturn err\n\t}\n\tplace.height, err = strconv.Atoi(h)\n\tif err != nil {\n\t\treturn err\n\t}\n\tplace.left, err = strconv.Atoi(l)\n\tif err != nil {\n\t\treturn err\n\t}\n\tplace.top, err = strconv.Atoi(t)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc print_error(format string, args ...any) {\n\tfmt.Fprintf(os.Stderr, format, args...)\n\tfmt.Fprintln(os.Stderr)\n}\n\nfunc main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {\n\topts = o\n\tif err = parse_place(); err != nil {\n\t\treturn 1, err\n\t}\n\tif err = parse_fit(); err != nil {\n\t\treturn 1, err\n\t}\n\terr = parse_z_index()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\terr = parse_background()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\terr = parse_mirror()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tif opts.UseWindowSize == \"\" {\n\t\tif tty.IsTerminal(os.Stdout.Fd()) {\n\t\t\tscreen_size, err = tty.GetSize(int(os.Stdout.Fd()))\n\t\t} else {\n\t\t\tt, oerr := tty.OpenControllingTerm()\n\t\t\tif oerr != nil {\n\t\t\t\treturn 1, fmt.Errorf(\"Failed to open controlling terminal with error: %w\", oerr)\n\t\t\t}\n\t\t\tscreen_size, err = t.GetSize()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn 1, fmt.Errorf(\"Failed to query terminal using TIOCGWINSZ with error: %w\", err)\n\t\t}\n\t} else {\n\t\tparts := strings.SplitN(opts.UseWindowSize, \",\", 4)\n\t\tif len(parts) != 4 {\n\t\t\treturn 1, fmt.Errorf(\"Invalid size specification: %s\", opts.UseWindowSize)\n\t\t}\n\t\tscreen_size = &unix.Winsize{}\n\t\tvar t uint64\n\t\tif t, err = strconv.ParseUint(parts[0], 10, 16); err != nil || t < 1 {\n\t\t\treturn 1, fmt.Errorf(\"Invalid size specification: %s with error: %w\", opts.UseWindowSize, err)\n\t\t}\n\t\tscreen_size.Col = uint16(t)\n\t\tif t, err = strconv.ParseUint(parts[1], 10, 16); err != nil || t < 1 {\n\t\t\treturn 1, fmt.Errorf(\"Invalid size specification: %s with error: %w\", opts.UseWindowSize, err)\n\t\t}\n\t\tscreen_size.Row = uint16(t)\n\t\tif t, err = strconv.ParseUint(parts[2], 10, 16); err != nil || t < 1 {\n\t\t\treturn 1, fmt.Errorf(\"Invalid size specification: %s with error: %w\", opts.UseWindowSize, err)\n\t\t}\n\t\tscreen_size.Xpixel = uint16(t)\n\t\tif t, err = strconv.ParseUint(parts[3], 10, 16); err != nil || t < 1 {\n\t\t\treturn 1, fmt.Errorf(\"Invalid size specification: %s with error: %w\", opts.UseWindowSize, err)\n\t\t}\n\t\tscreen_size.Ypixel = uint16(t)\n\t\tif screen_size.Xpixel < screen_size.Col {\n\t\t\treturn 1, fmt.Errorf(\"Invalid size specification: %s with error: The pixel width is smaller than the number of columns\", opts.UseWindowSize)\n\t\t}\n\t\tif screen_size.Ypixel < screen_size.Row {\n\t\t\treturn 1, fmt.Errorf(\"Invalid size specification: %s with error: The pixel height is smaller than the number of rows\", opts.UseWindowSize)\n\t\t}\n\t}\n\n\tif opts.PrintWindowSize {\n\t\tfmt.Printf(\"%dx%d\", screen_size.Xpixel, screen_size.Ypixel)\n\t\treturn 0, nil\n\t}\n\tif opts.Clear {\n\t\tcc := &graphics.GraphicsCommand{}\n\t\tcc.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_free_visible)\n\t\tif err = cc.WriteWithPayloadTo(os.Stdout, nil); err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t}\n\tswitch {\n\tcase opts.ClearAll:\n\t\tcc := &graphics.GraphicsCommand{}\n\t\tcc.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_free_by_range).SetLeftEdge(0).SetTopEdge(math.MaxUint32)\n\t\tif err = cc.WriteWithPayloadTo(os.Stdout, nil); err != nil {\n\t\t\treturn 1, err\n\t\t}\n\tcase opts.Clear:\n\t\tcc := &graphics.GraphicsCommand{}\n\t\tcc.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_free_visible)\n\t\tif err = cc.WriteWithPayloadTo(os.Stdout, nil); err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t}\n\tif screen_size.Xpixel == 0 || screen_size.Ypixel == 0 {\n\t\treturn 1, fmt.Errorf(\"Terminal does not support reporting screen sizes in pixels, use a terminal such as kitty, WezTerm, Konsole, etc. that does.\")\n\t}\n\n\titems, err := process_dirs(args...)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tif opts.Place != \"\" && len(items) > 1 {\n\t\treturn 1, fmt.Errorf(\"The --place option can only be used with a single image, not %d\", len(items))\n\t}\n\tfiles_channel = make(chan input_arg, len(items))\n\tfor i, ia := range items {\n\t\tia.index = i\n\t\tfiles_channel <- ia\n\t}\n\tnum_of_items = len(items)\n\toutput_channel = make(chan *image_data, 1)\n\tkeep_going = &atomic.Bool{}\n\tkeep_going.Store(true)\n\tif !opts.DetectSupport && num_of_items > 0 {\n\t\tnum_workers := utils.Max(1, utils.Min(num_of_items, runtime.NumCPU()))\n\t\tfor range num_workers {\n\t\t\tgo run_worker()\n\t\t}\n\t}\n\n\tpassthrough_mode := no_passthrough\n\tswitch opts.Passthrough {\n\tcase \"tmux\":\n\t\tpassthrough_mode = tmux_passthrough\n\tcase \"detect\":\n\t\tif tui.TmuxSocketAddress() != \"\" {\n\t\t\tpassthrough_mode = tmux_passthrough\n\t\t}\n\t}\n\n\tif passthrough_mode == no_passthrough && (opts.TransferMode == \"detect\" || opts.DetectSupport) {\n\t\tmemory, files, direct, err := DetectSupport(time.Duration(opts.DetectionTimeout * float64(time.Second)))\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tif !direct {\n\t\t\tkeep_going.Store(false)\n\t\t\treturn 1, fmt.Errorf(\"This terminal does not support the graphics protocol use a terminal such as kitty, WezTerm or Konsole that does. If you are running inside a terminal multiplexer such as tmux or screen that might be interfering as well.\")\n\t\t}\n\t\tif memory {\n\t\t\ttransfer_by_memory = supported\n\t\t} else {\n\t\t\ttransfer_by_memory = unsupported\n\t\t}\n\t\tif files {\n\t\t\ttransfer_by_file = supported\n\t\t} else {\n\t\t\ttransfer_by_file = unsupported\n\t\t}\n\t}\n\tif passthrough_mode != no_passthrough {\n\t\t// tmux doesn't allow responses from the terminal so we can't detect if memory or file based transferring is supported\n\t\ttransfer_by_memory = unsupported\n\t\ttransfer_by_file = unsupported\n\t}\n\tif opts.DetectSupport {\n\t\tif transfer_by_memory == supported {\n\t\t\tprint_error(\"memory\")\n\t\t} else if transfer_by_file == supported {\n\t\t\tprint_error(\"files\")\n\t\t} else {\n\t\t\tprint_error(\"stream\")\n\t\t}\n\t\treturn 0, nil\n\t}\n\tuse_unicode_placeholder := opts.UnicodePlaceholder\n\tif passthrough_mode != no_passthrough {\n\t\tuse_unicode_placeholder = true\n\t}\n\tbase_id := uint32(opts.ImageId)\n\texpecting_input_sequence_number := 0\n\tpending := make([]*image_data, 0, num_of_items)\n\n\tdo_one := func(imgd *image_data) {\n\t\texpecting_input_sequence_number++\n\t\tif base_id != 0 {\n\t\t\timgd.image_id = base_id\n\t\t\tbase_id++\n\t\t\tif base_id == 0 {\n\t\t\t\tbase_id++\n\t\t\t}\n\t\t}\n\t\timgd.use_unicode_placeholder = use_unicode_placeholder\n\t\timgd.passthrough_mode = passthrough_mode\n\t\tif imgd.err != nil {\n\t\t\tprint_error(\"Failed to process \\x1b[31m%s\\x1b[39m: %s\\r\\n\", imgd.source_name, imgd.err)\n\t\t} else {\n\t\t\ttransmit_image(imgd, opts.NoTrailingNewline)\n\t\t\tif imgd.err != nil {\n\t\t\t\tprint_error(\"Failed to transmit \\x1b[31m%s\\x1b[39m: %s\\r\\n\", imgd.source_name, imgd.err)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor num_of_items > 0 {\n\t\timgd := <-output_channel\n\t\tnum_of_items--\n\t\tif imgd.input_sequence_number == expecting_input_sequence_number {\n\t\t\tdo_one(imgd)\n\t\t} else {\n\t\t\tindex, _ := slices.BinarySearchFunc(pending, imgd.input_sequence_number, func(x *image_data, n int) int {\n\t\t\t\treturn x.input_sequence_number - n\n\t\t\t})\n\t\t\tpending = slices.Insert(pending, index, imgd)\n\t\t}\n\t\tfor len(pending) > 0 && pending[0].input_sequence_number == expecting_input_sequence_number {\n\t\t\tdo_one(pending[0])\n\t\t\tpending = pending[1:]\n\t\t}\n\t}\n\tfor _, x := range pending {\n\t\tdo_one(x)\n\t}\n\tkeep_going.Store(false)\n\tif opts.Hold {\n\t\tfmt.Print(\"\\r\")\n\t\tif opts.Place != \"\" {\n\t\t\tfmt.Println()\n\t\t}\n\t\ttui.HoldTillEnter(false)\n\t}\n\treturn 0, nil\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/icat/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nOPTIONS = '''\\\n--align\ntype=choices\nchoices=center,left,right\ndefault=center\nHorizontal alignment for the displayed image.\n\n\n--place\nChoose where on the screen to display the image. The image will be scaled to fit\ninto the specified rectangle. The syntax for specifying rectangles is\n<:italic:`width`>x<:italic:`height`>@<:italic:`left`>x<:italic:`top`>.\nAll measurements are in cells (i.e. cursor positions) with the origin\n:italic:`(0, 0)` at the top-left corner of the screen. Note that the :option:`--align`\noption will horizontally align the image within this rectangle. By default, the image\nis horizontally centered within the rectangle. Using place will cause the cursor to\nbe positioned at the top left corner of the image, instead of on the line after the image.\n\n\n--scale-up\ntype=bool-set\nCause images that are smaller than the specified area to be scaled up to use as much\nof the specified area as possible. The specified area depends on either the :option:`--place`\nor the :option:`--fit` options.\n\n\n--fit\nchoices=width,height,both,none\ndefault=width\nWhen not using :option:`--place`, control how the image is scaled relative to the screen.\nYou can have it fit in the screen width or height or both or neither.\n\n\n--background\ndefault=none\nSpecify a background color, this will cause transparent images to be composited\non top of the specified color.\n\n\n--mirror\ndefault=none\ntype=choices\nchoices=none,horizontal,vertical,both\nMirror the image about a horizontal or vertical axis or both.\n\n\n--clear\ntype=bool-set\nRemove all images currently displayed on the screen. Note that\nthis cannot work with terminal multiplexers such as tmux since\nonly the multiplexer can know the position of the screen.\n\n\n--clear-all\ntype=bool-set\nRemove all images from screen and scrollback. Note that with terminal\nmultiplexers like tmux, this will move images from all panes.\n\n\n--transfer-mode\ntype=choices\nchoices=detect,file,stream,memory\ndefault=detect\nWhich mechanism to use to transfer images to the terminal. The default is to\nauto-detect. :italic:`file` means to use a temporary file, :italic:`memory` means\nto use shared memory, :italic:`stream` means to send the data via terminal\nescape codes. Note that if you use the :italic:`file` or :italic:`memory` transfer\nmodes and you are connecting over a remote session then image display will not\nwork.\n\n\n--detect-support\ntype=bool-set\nDetect support for image display in the terminal. If not supported, will exit\nwith exit code 1, otherwise will exit with code 0 and print the supported\ntransfer mode to stderr, which can be used with the :option:`--transfer-mode`\noption.\n\n\n--detection-timeout\ntype=float\ndefault=10\nThe amount of time (in seconds) to wait for a response from the terminal, when\ndetecting image display support.\n\n\n--use-window-size\nInstead of querying the terminal for the window size, use the specified size, which must\nbe of the format: width_in_cells,height_in_cells,width_in_pixels,height_in_pixels\n\n\n--print-window-size\ntype=bool-set\nPrint out the window size as <:italic:`width`>x<:italic:`height`> (in pixels) and quit. This is a\nconvenience method to query the window size if using :code:`kitten icat`\nfrom a scripting language that cannot make termios calls.\n\n\n--stdin\ntype=choices\nchoices=detect,yes,no\ndefault=detect\nRead image data from STDIN. The default is to do it automatically, when STDIN is\nnot a terminal, but you can turn it off or on explicitly, if needed.\n\n\n--silent\ntype=bool-set\nNot used, present for legacy compatibility.\n\n\n--engine\ntype=choices\nchoices=auto,builtin,magick\ndefault=auto\nThe engine used for decoding and processing of images. The default is to use\nthe most appropriate engine.  The :code:`builtin` engine uses Go's native\nimaging libraries. The :code:`magick` engine uses ImageMagick which requires\nit to be installed on the system.\n\n\n--z-index -z\ndefault=0\nZ-index of the image. When negative, text will be displayed on top of the image.\nUse a double minus for values under the threshold for drawing images under cell\nbackground colors. For example, :code:`--1` evaluates as -1,073,741,825.\n\n\n--loop -l\ndefault=-1\ntype=int\nNumber of times to loop animations. Negative values loop forever. Zero means\nonly the first frame of the animation is displayed. Otherwise, the animation\nis looped the specified number of times.\n\n\n--hold\ntype=bool-set\nWait for a key press before exiting after displaying the images.\n\n\n--unicode-placeholder\ntype=bool-set\nUse the Unicode placeholder method to display the images. Useful to display\nimages from within full screen terminal programs that do not understand the\nkitty graphics protocol such as multiplexers or editors. See\n:ref:`graphics_unicode_placeholders` for details. Note that when using this\nmethod, images placed (with :option:`--place`) that do not fit on the screen,\nwill get wrapped at the screen edge instead of getting truncated. This\nwrapping is per line and therefore the image will look like it is interleaved\nwith blank lines.\n\n\n--passthrough\ntype=choices\nchoices=detect,tmux,none\ndefault=detect\nWhether to surround graphics commands with escape sequences that allow them to passthrough\nprograms like tmux. The default is to detect when running inside tmux and automatically use\nthe tmux passthrough escape codes. Note that when this option is enabled it implies\n:option:`--unicode-placeholder` as well.\n\n\n--image-id\ntype=int\ndefault=0\nThe graphics protocol id to use for the created image. Normally, a random id is created if needed.\nThis option allows control of the id. When multiple images are sent, sequential ids starting from the specified id\nare used. Valid ids are from 1 to 4294967295. Numbers outside this range are automatically wrapped.\n\n\n--no-trailing-newline -n\ntype=bool-set\nBy default, the cursor is moved to the next line after displaying an image. This option, prevents that. Should not be used\nwhen catting multiple images. Also has no effect when the :option:`--place` option is used.\n'''\n\nhelp_text = (\n        'A cat like utility to display images in the terminal.'\n        ' You can specify multiple image files and/or directories.'\n        ' Directories are scanned recursively for image files. If STDIN'\n        ' is not a terminal, image data will be read from it as well.'\n        ' You can also specify HTTP(S) or FTP URLs which will be'\n        ' automatically downloaded and displayed.'\n)\nusage = 'image-file-or-url-or-directory ...'\n\n\nif __name__ == '__main__':\n    raise SystemExit('This should be run as kitten icat')\nelif __name__ == '__doc__':\n    import sys\n\n    from kitty.simple_cli_definitions import CompletionSpec\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = lambda: OPTIONS.format()\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Display images in the terminal'\n    cd['args_completion'] = CompletionSpec.from_string('type:file mime:image/* group:Images')\n"
  },
  {
    "path": "kittens/icat/process_images.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage icat\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"image\"\n\t\"io\"\n\t\"io/fs\"\n\t\"math\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/imaging\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nvar _ = fmt.Print\n\ntype input_arg struct {\n\targ         string\n\tvalue       string\n\tis_http_url bool\n\tindex       int\n}\n\nfunc is_http_url(arg string) bool {\n\treturn strings.HasPrefix(arg, \"https://\") || strings.HasPrefix(arg, \"http://\")\n}\n\nfunc process_dirs(args ...string) (results []input_arg, err error) {\n\tresults = make([]input_arg, 0, 64)\n\tif opts.Stdin != \"no\" && (opts.Stdin == \"yes\" || !tty.IsTerminal(os.Stdin.Fd())) {\n\t\tresults = append(results, input_arg{arg: \"/dev/stdin\"})\n\t}\n\tfor _, arg := range args {\n\t\tif arg != \"\" {\n\t\t\tif is_http_url(arg) {\n\t\t\t\tresults = append(results, input_arg{arg: arg, value: arg, is_http_url: true})\n\t\t\t} else {\n\t\t\t\tif strings.HasPrefix(arg, \"file://\") {\n\t\t\t\t\tu, err := url.Parse(arg)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, &fs.PathError{Op: \"Parse\", Path: arg, Err: err}\n\t\t\t\t\t}\n\t\t\t\t\targ = u.Path\n\t\t\t\t}\n\t\t\t\ts, err := os.Stat(arg)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, &fs.PathError{Op: \"Stat\", Path: arg, Err: err}\n\t\t\t\t}\n\t\t\t\tif s.IsDir() {\n\t\t\t\t\tif err = filepath.WalkDir(arg, func(path string, d fs.DirEntry, walk_err error) error {\n\t\t\t\t\t\tif walk_err != nil {\n\t\t\t\t\t\t\tif d == nil {\n\t\t\t\t\t\t\t\terr = &fs.PathError{Op: \"Stat\", Path: arg, Err: walk_err}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn walk_err\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !d.IsDir() {\n\t\t\t\t\t\t\tmt := utils.GuessMimeType(path)\n\t\t\t\t\t\t\tif strings.HasPrefix(mt, \"image/\") {\n\t\t\t\t\t\t\t\tresults = append(results, input_arg{arg: arg, value: path})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}); err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresults = append(results, input_arg{arg: arg, value: arg})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn results, nil\n}\n\ntype opened_input struct {\n\tfile  io.Reader\n\tbytes []byte\n\tpath  string\n}\n\ntype image_frame struct {\n\tfilename                 string\n\tin_memory_bytes          []byte\n\twidth, height, left, top int\n\ttransmission_format      graphics.GRT_f\n\tcompose_onto             int\n\treplace                  bool\n\tnumber                   int\n\tdelay_ms                 int\n}\n\ntype image_data struct {\n\tcanvas_width, canvas_height       int\n\tformat_uppercase                  string\n\tavailable_width, available_height int\n\tneeds_scaling                     bool\n\tframes                            []*image_frame\n\timage_number                      uint32\n\timage_id                          uint32\n\tcell_x_offset                     int\n\tmove_x_by                         int\n\tmove_to                           struct{ x, y int }\n\twidth_cells, height_cells         int\n\tuse_unicode_placeholder           bool\n\tpassthrough_mode                  passthrough_type\n\tinput_sequence_number             int\n\n\t// for error reporting\n\terr         error\n\tsource_name string\n}\n\nconst inf = math.MaxInt\n\nfunc set_basic_metadata(imgd *image_data) {\n\tif imgd.frames == nil {\n\t\timgd.frames = make([]*image_frame, 0, 32)\n\t}\n\tif place != nil {\n\t\timgd.available_width = place.width * int(screen_size.Xpixel) / int(screen_size.Col)\n\t\timgd.available_height = place.height * int(screen_size.Ypixel) / int(screen_size.Row)\n\t} else {\n\t\tswitch fit_mode {\n\t\tcase fit_none:\n\t\t\timgd.available_width, imgd.available_height = inf, inf\n\t\tcase fit_both:\n\t\t\timgd.available_width = int(screen_size.Xpixel)\n\t\t\timgd.available_height = int(screen_size.Ypixel)\n\t\tcase fit_width:\n\t\t\timgd.available_width = int(screen_size.Xpixel)\n\t\t\timgd.available_height = inf\n\t\tcase fit_height:\n\t\t\timgd.available_width = inf\n\t\t\timgd.available_height = int(screen_size.Ypixel)\n\t\t}\n\t}\n\timgd.needs_scaling = imgd.canvas_width > imgd.available_width || imgd.canvas_height > imgd.available_height || opts.ScaleUp\n}\n\nfunc report_error(source_name, msg string, err error) {\n\timgd := image_data{source_name: source_name, err: fmt.Errorf(\"%s: %w\", msg, err)}\n\tsend_output(&imgd)\n}\n\nfunc make_output_from_input(imgd *image_data, f *opened_input) {\n\tframe := image_frame{}\n\timgd.frames = append(imgd.frames, &frame)\n\tframe.width = imgd.canvas_width\n\tframe.height = imgd.canvas_height\n\tif imgd.format_uppercase != \"PNG\" {\n\t\tpanic(fmt.Sprintf(\"Unknown transmission format: %s\", imgd.format_uppercase))\n\t}\n\tframe.transmission_format = graphics.GRT_format_png\n\tif f.bytes != nil {\n\t\tframe.in_memory_bytes = f.bytes\n\t} else if f.path != \"\" {\n\t\tframe.filename = f.path\n\t} else {\n\t\tvar err error\n\t\tif frame.in_memory_bytes, err = io.ReadAll(f.file); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n}\n\nfunc scale_up(width, height, maxWidth, maxHeight int) (newWidth, newHeight int) {\n\tif width == 0 || height == 0 {\n\t\treturn 0, 0\n\t}\n\t// Calculate the ratio to scale the width and the ratio to scale the height.\n\t// We use floating-point division for precision.\n\twidthRatio := float64(maxWidth) / float64(width)\n\theightRatio := float64(maxHeight) / float64(height)\n\n\t// To preserve the aspect ratio and fit within the limits, we must use the\n\t// smaller of the two scaling ratios.\n\tvar ratio float64\n\tif widthRatio < heightRatio {\n\t\tratio = widthRatio\n\t} else {\n\t\tratio = heightRatio\n\t}\n\n\t// Calculate the new dimensions and convert them back to uints.\n\tnewWidth = int(float64(width) * ratio)\n\tnewHeight = int(float64(height) * ratio)\n\n\treturn newWidth, newHeight\n}\n\nfunc scale_image(imgd *image_data) bool {\n\tif imgd.needs_scaling {\n\t\twidth, height := imgd.canvas_width, imgd.canvas_height\n\t\tif opts.ScaleUp && (imgd.canvas_width < imgd.available_width || imgd.canvas_height < imgd.available_height) && (imgd.available_height != inf || imgd.available_width != inf) {\n\t\t\timgd.canvas_width, imgd.canvas_height = scale_up(imgd.canvas_width, imgd.canvas_height, imgd.available_width, imgd.available_height)\n\t\t}\n\t\tneww, newh := images.FitImage(imgd.canvas_width, imgd.canvas_height, imgd.available_width, imgd.available_height)\n\t\timgd.needs_scaling = false\n\t\tx := float64(neww) / float64(width)\n\t\ty := float64(newh) / float64(height)\n\t\timgd.canvas_width = int(x * float64(width))\n\t\timgd.canvas_height = int(y * float64(height))\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc add_frame(imgd *image_data, img image.Image, left, top int) *image_frame {\n\tconst shm_template = \"kitty-icat-*\"\n\tnum_channels := 4\n\tvar pix []byte\n\tif imaging.IsOpaque(img) {\n\t\tnum_channels, pix = 3, imaging.AsRGBData8(img)\n\t} else {\n\t\tpix = imaging.AsRGBAData8(img)\n\t}\n\tb := img.Bounds()\n\tf := image_frame{width: b.Dx(), height: b.Dy(), number: len(imgd.frames) + 1, left: left, top: top}\n\tf.transmission_format = utils.IfElse(num_channels == 3, graphics.GRT_format_rgb, graphics.GRT_format_rgba)\n\tf.in_memory_bytes = pix\n\timgd.frames = append(imgd.frames, &f)\n\treturn &f\n}\n\nfunc process_arg(arg input_arg) {\n\tvar f opened_input\n\tif arg.is_http_url {\n\t\tresp, err := http.Get(arg.value)\n\t\tif err != nil {\n\t\t\treport_error(arg.value, \"Could not get\", err)\n\t\t\treturn\n\t\t}\n\t\tdefer resp.Body.Close()\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\treport_error(arg.value, \"Could not get\", fmt.Errorf(\"bad status: %v\", resp.Status))\n\t\t\treturn\n\t\t}\n\t\tdest := bytes.Buffer{}\n\t\tdest.Grow(64 * 1024)\n\t\t_, err = io.Copy(&dest, resp.Body)\n\t\tif err != nil {\n\t\t\treport_error(arg.value, \"Could not download\", err)\n\t\t\treturn\n\t\t}\n\t\tf.bytes = dest.Bytes()\n\t\tf.file = bytes.NewReader(f.bytes)\n\t} else if arg.value == \"\" {\n\t\tstdin, err := io.ReadAll(os.Stdin)\n\t\tif err != nil {\n\t\t\treport_error(\"<stdin>\", \"Could not read from\", err)\n\t\t\treturn\n\t\t}\n\t\tf.bytes = stdin\n\t\tf.file = bytes.NewReader(f.bytes)\n\t} else {\n\t\tq, err := os.Open(arg.value)\n\t\tif err != nil {\n\t\t\treport_error(arg.value, \"Could not open\", err)\n\t\t\treturn\n\t\t}\n\t\tf.file = q\n\t\tf.path = q.Name()\n\t\tdefer q.Close()\n\t}\n\n\tvar img *images.ImageData\n\tvar dopts []imaging.DecodeOption\n\tneeds_conversion := false\n\tif flip {\n\t\tdopts = append(dopts, imaging.Transform(imaging.FlipVTransform))\n\t\tneeds_conversion = true\n\t}\n\tif flop {\n\t\tdopts = append(dopts, imaging.Transform(imaging.FlipHTransform))\n\t\tneeds_conversion = true\n\t}\n\tif remove_alpha != nil {\n\t\tdopts = append(dopts, imaging.Background(*remove_alpha))\n\t\tneeds_conversion = true\n\t}\n\tswitch opts.Engine {\n\tcase \"native\", \"builtin\":\n\t\tdopts = append(dopts, imaging.Backends(imaging.GO_IMAGE))\n\tcase \"magick\":\n\t\tdopts = append(dopts, imaging.Backends(imaging.MAGICK_IMAGE))\n\t}\n\timgd := image_data{source_name: arg.value, input_sequence_number: arg.index}\n\tdopts = append(dopts, imaging.ResizeCallback(func(w, h int) (int, int) {\n\t\timgd.canvas_width, imgd.canvas_height = w, h\n\t\tset_basic_metadata(&imgd)\n\t\tif scale_image(&imgd) {\n\t\t\tneeds_conversion = true\n\t\t\tw, h = imgd.canvas_width, imgd.canvas_height\n\t\t}\n\t\treturn w, h\n\t}))\n\tvar err error\n\tif f.path != \"\" {\n\t\timg, err = images.OpenImageFromPath(f.path, dopts...)\n\t} else {\n\t\timg, f.file, err = images.OpenImageFromReader(f.file, dopts...)\n\t}\n\tif err != nil {\n\t\treport_error(arg.value, \"Could not render image to RGB\", err)\n\t\treturn\n\t}\n\tif !keep_going.Load() {\n\t\treturn\n\t}\n\timgd.format_uppercase = img.Format_uppercase\n\timgd.canvas_width, imgd.canvas_height = img.Width, img.Height\n\tif !needs_conversion && imgd.format_uppercase == \"PNG\" && len(img.Frames) == 1 {\n\t\tmake_output_from_input(&imgd, &f)\n\t} else {\n\t\tfor _, f := range img.Frames {\n\t\t\tframe := add_frame(&imgd, f.Img, f.Left, f.Top)\n\t\t\tframe.number, frame.compose_onto = int(f.Number), int(f.Compose_onto)\n\t\t\tframe.replace = f.Replace\n\t\t\tframe.delay_ms = int(f.Delay_ms)\n\t\t}\n\t}\n\tif !keep_going.Load() {\n\t\treturn\n\t}\n\tsend_output(&imgd)\n}\n\nfunc run_worker() {\n\tfor {\n\t\tselect {\n\t\tcase arg := <-files_channel:\n\t\t\tif !keep_going.Load() {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tprocess_arg(arg)\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "kittens/icat/scaling_test.go",
    "content": "package icat\n\nimport (\n\t\"fmt\"\n\t\"image\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestScaling(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tw, h, pw, ph, ew, eh int\n\t}{\n\t\t{1000, 50, 800, 600, 800, 40},\n\t\t{1000, 50, 800000, 600, 12000, 600},\n\t\t{100, 50, 800, 600, 800, 400},\n\t\t{1920, 1080, 800, 600, 800, 450},\n\t\t{300, 900, 800, 600, 200, 600},\n\t\t{400, 300, 800, 600, 800, 600},\n\t} {\n\t\taw, ah := scale_up(tc.w, tc.h, tc.pw, tc.ph)\n\t\tactual := image.Pt(aw, ah)\n\t\texpected := image.Pt(tc.ew, tc.eh)\n\t\tif actual != expected {\n\t\t\tt.Fatalf(\"want: %v got: %v\", expected, actual)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "kittens/icat/transmit.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage icat\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"io\"\n\t\"math\"\n\tnot_rand \"math/rand/v2\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nvar _ = fmt.Print\n\ntype passthrough_type int\n\nconst (\n\tno_passthrough passthrough_type = iota\n\ttmux_passthrough\n)\n\nfunc new_graphics_command(imgd *image_data) *graphics.GraphicsCommand {\n\tgc := graphics.GraphicsCommand{}\n\tswitch imgd.passthrough_mode {\n\tcase tmux_passthrough:\n\t\tgc.WrapPrefix = \"\\033Ptmux;\"\n\t\tgc.WrapSuffix = \"\\033\\\\\"\n\t\tgc.EncodeSerializedDataFunc = func(x string) string { return strings.ReplaceAll(x, \"\\033\", \"\\033\\033\") }\n\t}\n\treturn &gc\n}\n\nfunc gc_for_image(imgd *image_data, frame_num int, frame *image_frame) *graphics.GraphicsCommand {\n\tgc := new_graphics_command(imgd)\n\tgc.SetDataWidth(uint64(frame.width)).SetDataHeight(uint64(frame.height))\n\tgc.SetQuiet(graphics.GRT_quiet_silent)\n\tgc.SetFormat(frame.transmission_format)\n\tif imgd.image_number != 0 {\n\t\tgc.SetImageNumber(imgd.image_number)\n\t}\n\tif imgd.image_id != 0 {\n\t\tgc.SetImageId(imgd.image_id)\n\t}\n\tif frame_num == 0 {\n\t\tgc.SetAction(graphics.GRT_action_transmit_and_display)\n\t\tif imgd.use_unicode_placeholder {\n\t\t\tgc.SetUnicodePlaceholder(graphics.GRT_create_unicode_placeholder)\n\t\t\tgc.SetColumns(uint64(imgd.width_cells))\n\t\t\tgc.SetRows(uint64(imgd.height_cells))\n\t\t}\n\t\tif imgd.cell_x_offset > 0 {\n\t\t\tgc.SetXOffset(uint64(imgd.cell_x_offset))\n\t\t}\n\t\tif z_index != 0 {\n\t\t\tgc.SetZIndex(z_index)\n\t\t}\n\t\tif place != nil {\n\t\t\tgc.SetCursorMovement(graphics.GRT_cursor_static)\n\t\t}\n\t} else {\n\t\tgc.SetAction(graphics.GRT_action_frame)\n\t\tgc.SetGap(int32(frame.delay_ms))\n\t\tgc.SetCompositionMode(utils.IfElse(frame.replace, graphics.Overwrite, graphics.AlphaBlend))\n\t\tif frame.compose_onto > 0 {\n\t\t\tgc.SetOverlaidFrame(uint64(frame.compose_onto))\n\t\t}\n\t\tgc.SetLeftEdge(uint64(frame.left)).SetTopEdge(uint64(frame.top))\n\t}\n\treturn gc\n}\n\nfunc transmit_shm(imgd *image_data, frame_num int, frame *image_frame) (err error) {\n\tvar mmap shm.MMap\n\tvar data_size int64\n\tif frame.in_memory_bytes == nil {\n\t\tf, err := os.Open(frame.filename)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to open image data output file: %s with error: %w\", frame.filename, err)\n\t\t}\n\t\tdefer f.Close()\n\t\tif data_size, err = f.Seek(0, io.SeekEnd); err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to seek in image data output file: %s with error: %w\", frame.filename, err)\n\t\t}\n\t\tif _, err = f.Seek(0, io.SeekStart); err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to seek in image data output file: %s with error: %w\", frame.filename, err)\n\t\t}\n\t\tif mmap, err = shm.CreateTemp(\"icat-*\", uint64(data_size)); err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to create a SHM file for transmission: %w\", err)\n\t\t}\n\t\tif _, err = io.ReadFull(f, mmap.Slice()); err != nil {\n\t\t\tmmap.Close()\n\t\t\tmmap.Unlink()\n\t\t\treturn fmt.Errorf(\"Failed to read data from image output data file: %w\", err)\n\t\t}\n\t} else {\n\t\tdata_size = int64(len(frame.in_memory_bytes))\n\t\tif mmap, err = shm.CreateTemp(\"icat-*\", uint64(data_size)); err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to create a SHM file for transmission: %w\", err)\n\t\t}\n\t\tcopy(mmap.Slice(), frame.in_memory_bytes)\n\t}\n\tdefer mmap.Close() // terminal is responsible for unlink\n\tgc := gc_for_image(imgd, frame_num, frame)\n\tgc.SetTransmission(graphics.GRT_transmission_sharedmem)\n\tgc.SetDataSize(uint64(data_size))\n\terr = gc.WriteWithPayloadTo(os.Stdout, utils.UnsafeStringToBytes(mmap.Name()))\n\treturn\n}\n\nfunc transmit_file(imgd *image_data, frame_num int, frame *image_frame) (err error) {\n\tis_temp := false\n\tfname := \"\"\n\tvar data_size int\n\tif frame.in_memory_bytes == nil {\n\t\tfname, err = filepath.Abs(frame.filename)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to convert image data output file: %s to absolute path with error: %w\", frame.filename, err)\n\t\t}\n\t\tframe.filename = \"\" // so it isn't deleted in cleanup\n\t} else {\n\t\tis_temp = true\n\t\tf, err := images.CreateTempInRAM()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to create a temp file for image data transmission: %w\", err)\n\t\t}\n\t\tdata_size = len(frame.in_memory_bytes)\n\t\t_, err = bytes.NewBuffer(frame.in_memory_bytes).WriteTo(f)\n\t\tf.Close()\n\t\tif err != nil {\n\t\t\tos.Remove(f.Name())\n\t\t\treturn fmt.Errorf(\"Failed to write image data to temp file for transmission: %w\", err)\n\t\t}\n\t\tfname = f.Name()\n\t}\n\tgc := gc_for_image(imgd, frame_num, frame)\n\tgc.SetTransmission(utils.IfElse(is_temp, graphics.GRT_transmission_tempfile, graphics.GRT_transmission_file))\n\tif data_size > 0 {\n\t\tgc.SetDataSize(uint64(data_size))\n\t}\n\treturn gc.WriteWithPayloadTo(os.Stdout, utils.UnsafeStringToBytes(fname))\n}\n\nfunc transmit_stream(imgd *image_data, frame_num int, frame *image_frame) (err error) {\n\tdata := frame.in_memory_bytes\n\tif data == nil {\n\t\tdata, err = os.ReadFile(frame.filename)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to read image data output file: %s with error: %w\", frame.filename, err)\n\t\t}\n\t}\n\tgc := gc_for_image(imgd, frame_num, frame)\n\treturn gc.WriteWithPayloadTo(os.Stdout, data)\n}\n\nfunc calculate_in_cell_x_offset(width, cell_width int) int {\n\textra_pixels := width % cell_width\n\tif extra_pixels == 0 {\n\t\treturn 0\n\t}\n\tswitch opts.Align {\n\tcase \"left\":\n\t\treturn 0\n\tcase \"right\":\n\t\treturn cell_width - extra_pixels\n\tdefault:\n\t\treturn (cell_width - extra_pixels) / 2\n\t}\n}\n\nfunc place_cursor(imgd *image_data) {\n\tcw := max(int(screen_size.Xpixel)/int(screen_size.Col), 1)\n\tch := max(int(screen_size.Ypixel)/int(screen_size.Row), 1)\n\timgd.cell_x_offset = calculate_in_cell_x_offset(imgd.canvas_width, cw)\n\timgd.width_cells = int(math.Ceil(float64(imgd.canvas_width) / float64(cw)))\n\timgd.height_cells = int(math.Ceil(float64(imgd.canvas_height) / float64(ch)))\n\tif place == nil {\n\t\tswitch opts.Align {\n\t\tcase \"center\":\n\t\t\timgd.move_x_by = (int(screen_size.Col) - imgd.width_cells) / 2\n\t\tcase \"right\":\n\t\t\timgd.move_x_by = (int(screen_size.Col) - imgd.width_cells)\n\t\t}\n\t} else {\n\t\timgd.move_to.x = place.left + 1\n\t\timgd.move_to.y = place.top + 1\n\t\tswitch opts.Align {\n\t\tcase \"center\":\n\t\t\timgd.move_to.x += (place.width - imgd.width_cells) / 2\n\t\tcase \"right\":\n\t\t\timgd.move_to.x += (place.width - imgd.width_cells)\n\t\t}\n\t}\n}\n\nfunc next_random() (ans uint32) {\n\tfor ans == 0 {\n\t\tb := make([]byte, 4)\n\t\t_, err := rand.Read(b)\n\t\tif err == nil {\n\t\t\tans = binary.LittleEndian.Uint32(b[:])\n\t\t} else {\n\t\t\tans = not_rand.Uint32()\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc write_unicode_placeholder(imgd *image_data) {\n\tprefix := \"\"\n\tforeground := fmt.Sprintf(\"\\033[38:2:%d:%d:%dm\", (imgd.image_id>>16)&255, (imgd.image_id>>8)&255, imgd.image_id&255)\n\tos.Stdout.WriteString(foreground)\n\trestore := \"\\033[39m\"\n\tif imgd.move_to.y > 0 {\n\t\tos.Stdout.WriteString(loop.SAVE_CURSOR)\n\t\trestore += loop.RESTORE_CURSOR\n\t} else if imgd.move_x_by > 0 {\n\t\tprefix = strings.Repeat(\" \", imgd.move_x_by)\n\t}\n\tdefer func() { os.Stdout.WriteString(restore) }()\n\tif imgd.move_to.y > 0 {\n\t\tfmt.Printf(loop.MoveCursorToTemplate, imgd.move_to.y, 0)\n\t}\n\tid_char := string(images.NumberToDiacritic[(imgd.image_id>>24)&255])\n\tfor r := 0; r < imgd.height_cells; r++ {\n\t\tif imgd.move_to.x > 0 {\n\t\t\tfmt.Printf(\"\\x1b[%dC\", imgd.move_to.x-1)\n\t\t} else {\n\t\t\tos.Stdout.WriteString(prefix)\n\t\t}\n\t\tfor c := 0; c < imgd.width_cells; c++ {\n\t\t\tos.Stdout.WriteString(string(kitty.ImagePlaceholderChar) + string(images.NumberToDiacritic[r]) + string(images.NumberToDiacritic[c]) + id_char)\n\t\t}\n\t\tif r < imgd.height_cells-1 {\n\t\t\tos.Stdout.WriteString(\"\\n\\r\")\n\t\t}\n\t}\n}\n\nvar seen_image_ids *utils.Set[uint32]\n\nfunc transmit_image(imgd *image_data, no_trailing_newline bool) {\n\tif seen_image_ids == nil {\n\t\tseen_image_ids = utils.NewSet[uint32](32)\n\t}\n\tvar f func(*image_data, int, *image_frame) error\n\tif opts.TransferMode != \"detect\" {\n\t\tswitch opts.TransferMode {\n\t\tcase \"file\":\n\t\t\tf = transmit_file\n\t\tcase \"memory\":\n\t\t\tf = transmit_shm\n\t\tcase \"stream\":\n\t\t\tf = transmit_stream\n\t\t}\n\t}\n\tif f == nil && transfer_by_memory == supported && imgd.frames[0].in_memory_bytes != nil {\n\t\tf = transmit_shm\n\t}\n\tif f == nil && transfer_by_file == supported {\n\t\tf = transmit_file\n\t}\n\tif f == nil {\n\t\tf = transmit_stream\n\t}\n\tif imgd.image_id == 0 {\n\t\tif imgd.use_unicode_placeholder {\n\t\t\tfor imgd.image_id&0xFF000000 == 0 || imgd.image_id&0x00FFFF00 == 0 || seen_image_ids.Has(imgd.image_id) {\n\t\t\t\t// Generate a 32-bit image id using rejection sampling such that the most\n\t\t\t\t// significant byte and the two bytes in the middle are non-zero to avoid\n\t\t\t\t// collisions with applications that cannot represent non-zero most\n\t\t\t\t// significant bytes (which is represented by the third combining character)\n\t\t\t\t// or two non-zero bytes in the middle (which requires 24-bit color mode).\n\t\t\t\timgd.image_id = next_random()\n\t\t\t}\n\t\t\tseen_image_ids.Add(imgd.image_id)\n\t\t} else {\n\t\t\tif len(imgd.frames) > 1 {\n\t\t\t\tfor imgd.image_number == 0 {\n\t\t\t\t\timgd.image_number = next_random()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tplace_cursor(imgd)\n\tif imgd.use_unicode_placeholder && utils.Max(imgd.width_cells, imgd.height_cells) >= len(images.NumberToDiacritic) {\n\t\timgd.err = fmt.Errorf(\"Image too large to be displayed using Unicode placeholders. Maximum size is %dx%d cells\", len(images.NumberToDiacritic), len(images.NumberToDiacritic))\n\t\treturn\n\t}\n\tswitch imgd.passthrough_mode {\n\tcase tmux_passthrough:\n\t\timgd.err = tui.TmuxAllowPassthrough()\n\t\tif imgd.err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tfmt.Print(\"\\r\")\n\tif !imgd.use_unicode_placeholder {\n\t\tif imgd.move_x_by > 0 {\n\t\t\tfmt.Printf(\"\\x1b[%dC\", imgd.move_x_by)\n\t\t}\n\t\tif imgd.move_to.x > 0 {\n\t\t\tfmt.Printf(loop.MoveCursorToTemplate, imgd.move_to.y, imgd.move_to.x)\n\t\t}\n\t}\n\tframe_control_cmd := new_graphics_command(imgd)\n\tframe_control_cmd.SetAction(graphics.GRT_action_animate)\n\tif imgd.image_id != 0 {\n\t\tframe_control_cmd.SetImageId(imgd.image_id)\n\t} else {\n\t\tframe_control_cmd.SetImageNumber(imgd.image_number)\n\t}\n\tis_animated := len(imgd.frames) > 1\n\n\tfor frame_num, frame := range imgd.frames {\n\t\terr := f(imgd, frame_num, frame)\n\t\tif err != nil {\n\t\t\timgd.err = err\n\t\t\treturn\n\t\t}\n\t\tif is_animated {\n\t\t\tswitch frame_num {\n\t\t\tcase 0:\n\t\t\t\t// set gap for the first frame and number of loops for the animation\n\t\t\t\tc := frame_control_cmd\n\t\t\t\tc.SetTargetFrame(uint64(frame.number))\n\t\t\t\tc.SetGap(int32(frame.delay_ms))\n\t\t\t\tswitch {\n\t\t\t\tcase opts.Loop < 0:\n\t\t\t\t\tc.SetNumberOfLoops(1)\n\t\t\t\tcase opts.Loop > 0:\n\t\t\t\t\tc.SetNumberOfLoops(uint64(opts.Loop) + 1)\n\t\t\t\t}\n\t\t\t\tif imgd.err = c.WriteWithPayloadTo(os.Stdout, nil); imgd.err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase 1:\n\t\t\t\tc := frame_control_cmd\n\t\t\t\tc.SetAnimationControl(2) // set animation to loading mode\n\t\t\t\tif imgd.err = c.WriteWithPayloadTo(os.Stdout, nil); imgd.err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif imgd.use_unicode_placeholder {\n\t\twrite_unicode_placeholder(imgd)\n\t}\n\tif is_animated {\n\t\tc := frame_control_cmd\n\t\tc.SetAnimationControl(3) // set animation to normal mode\n\t\tif imgd.err = c.WriteWithPayloadTo(os.Stdout, nil); imgd.err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif imgd.move_to.x == 0 && !no_trailing_newline {\n\t\tfmt.Println() // ensure cursor is on new line\n\t}\n}\n"
  },
  {
    "path": "kittens/notify/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/notify/main.go",
    "content": "package notify\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"image\"\n\t\"io\"\n\t\"os\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nconst ESC_CODE_PREFIX = \"\\x1b]99;\"\nconst ESC_CODE_SUFFIX = \"\\x1b\\\\\"\nconst CHUNK_SIZE = 4096\n\nfunc b64encode(x string) string {\n\treturn base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(x))\n}\n\nfunc check_id_valid(x string) bool {\n\tpat := utils.MustCompile(`[^a-zA-Z0-9_+.-]`)\n\treturn pat.ReplaceAllString(x, \"\") == x\n}\n\ntype parsed_data struct {\n\topts                    *Options\n\twait_till_closed        bool\n\texpire_time             time.Duration\n\ttitle, body, identifier string\n\timage_data              []byte\n\tinitial_msg             string\n}\n\nfunc (p *parsed_data) create_metadata() string {\n\tans := []string{}\n\tif p.opts.AppName != \"\" {\n\t\tans = append(ans, \"f=\"+b64encode(p.opts.AppName))\n\t}\n\tswitch p.opts.Urgency {\n\tcase \"low\":\n\t\tans = append(ans, \"u=0\")\n\tcase \"critical\":\n\t\tans = append(ans, \"u=2\")\n\t}\n\tif p.expire_time >= 0 {\n\t\tans = append(ans, \"w=\"+strconv.FormatInt(p.expire_time.Milliseconds(), 10))\n\t}\n\tif p.opts.Type != \"\" {\n\t\tans = append(ans, \"t=\"+b64encode(p.opts.Type))\n\t}\n\tif p.wait_till_closed {\n\t\tans = append(ans, \"c=1:a=report\")\n\t}\n\tfor _, x := range p.opts.Icon {\n\t\tans = append(ans, \"n=\"+b64encode(x))\n\t}\n\tif p.opts.IconCacheId != \"\" {\n\t\tans = append(ans, \"g=\"+p.opts.IconCacheId)\n\t}\n\tif p.opts.SoundName != \"system\" {\n\t\tans = append(ans, \"s=\"+b64encode(p.opts.SoundName))\n\t}\n\tm := strings.Join(ans, \":\")\n\tif m != \"\" {\n\t\tm = \":\" + m\n\t}\n\treturn m\n}\n\nvar debugprintln = tty.DebugPrintln\n\nfunc (p *parsed_data) generate_chunks(callback func(string)) {\n\tprefix := ESC_CODE_PREFIX + \"i=\" + p.identifier\n\twrite_chunk := func(middle string) {\n\t\tcallback(prefix + middle + ESC_CODE_SUFFIX)\n\t}\n\n\tadd_payload := func(payload_type, payload string) {\n\t\tif payload == \"\" {\n\t\t\treturn\n\t\t}\n\t\tp := utils.IfElse(payload_type == \"title\", \"\", \":p=\"+payload_type)\n\t\tpayload = b64encode(payload)\n\t\tfor len(payload) > 0 {\n\t\t\tchunk := payload[:min(CHUNK_SIZE, len(payload))]\n\t\t\tpayload = utils.IfElse(len(payload) > len(chunk), payload[len(chunk):], \"\")\n\t\t\twrite_chunk(\":d=0:e=1\" + p + \";\" + chunk)\n\t\t}\n\t}\n\tmetadata := p.create_metadata()\n\twrite_chunk(\":d=0\" + metadata + \";\")\n\tadd_payload(\"title\", p.title)\n\tadd_payload(\"body\", p.body)\n\tif len(p.image_data) > 0 {\n\t\tadd_payload(\"icon\", utils.UnsafeBytesToString(p.image_data))\n\t}\n\tif len(p.opts.Button) > 0 {\n\t\tadd_payload(\"buttons\", strings.Join(p.opts.Button, \"\\u2028\"))\n\t}\n\twrite_chunk(\";\")\n}\n\nfunc (p *parsed_data) run_loop() (err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)\n\tif err != nil {\n\t\treturn err\n\t}\n\tactivated := -1\n\tprefix := ESC_CODE_PREFIX + \"i=\" + p.identifier\n\n\tpoll_for_close := func() {\n\t\tlp.AddTimer(time.Millisecond*50, false, func(_ loop.IdType) error {\n\t\t\tlp.QueueWriteString(prefix + \":p=alive;\" + ESC_CODE_SUFFIX)\n\t\t\treturn nil\n\t\t})\n\t}\n\tlp.OnInitialize = func() (string, error) {\n\t\tif p.initial_msg != \"\" {\n\t\t\treturn p.initial_msg, nil\n\t\t}\n\t\tp.generate_chunks(func(x string) { lp.QueueWriteString(x) })\n\t\treturn \"\", nil\n\t}\n\tlp.OnEscapeCode = func(ect loop.EscapeCodeType, data []byte) error {\n\t\tif ect == loop.OSC && bytes.HasPrefix(data, []byte(ESC_CODE_PREFIX[2:])) {\n\t\t\traw := utils.UnsafeBytesToString(data[len(ESC_CODE_PREFIX[2:]):])\n\t\t\tmetadata, payload, _ := strings.Cut(raw, \";\")\n\t\t\tsent_identifier, payload_type := \"\", \"\"\n\t\t\tfor x := range strings.SplitSeq(metadata, \":\") {\n\t\t\t\tkey, val, _ := strings.Cut(x, \"=\")\n\t\t\t\tswitch key {\n\t\t\t\tcase \"i\":\n\t\t\t\t\tsent_identifier = val\n\t\t\t\tcase \"p\":\n\t\t\t\t\tpayload_type = val\n\t\t\t\t}\n\t\t\t}\n\t\t\tif sent_identifier == p.identifier {\n\t\t\t\tswitch payload_type {\n\t\t\t\tcase \"close\":\n\t\t\t\t\tif payload == \"untracked\" {\n\t\t\t\t\t\tpoll_for_close()\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlp.Quit(0)\n\t\t\t\t\t}\n\t\t\t\tcase \"alive\":\n\t\t\t\t\tlive_ids := strings.Split(payload, \",\")\n\t\t\t\t\tif slices.Contains(live_ids, p.identifier) {\n\t\t\t\t\t\tpoll_for_close()\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlp.Quit(0)\n\t\t\t\t\t}\n\t\t\t\tcase \"\":\n\t\t\t\t\tif activated, err = strconv.Atoi(utils.IfElse(payload == \"\", \"0\", payload)); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"Got invalid activation response from terminal: %#v\", payload)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tclose_requested := 0\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") || event.MatchesPressOrRepeat(\"esc\") {\n\t\t\tevent.Handled = true\n\t\t\tswitch close_requested {\n\t\t\tcase 0:\n\t\t\t\tlp.QueueWriteString(prefix + \":p=close;\" + ESC_CODE_SUFFIX)\n\t\t\t\tlp.Println(\"Closing notification, please wait...\")\n\t\t\t\tclose_requested++\n\t\t\tcase 1:\n\t\t\t\tkey := \"Esc\"\n\t\t\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\t\t\t\tkey = \"Ctrl+C\"\n\t\t\t\t}\n\t\t\t\tlp.Println(fmt.Sprintf(\"Waiting for response from terminal, press the %s key again to abort. Note that this might result in garbage being printed to the terminal.\", key))\n\t\t\t\tclose_requested++\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"Aborted by user!\")\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\tif activated > -1 && err == nil {\n\t\tfmt.Println(activated)\n\t}\n\treturn\n}\n\nfunc random_ident() (string, error) {\n\treturn utils.HumanUUID4()\n}\n\nfunc parse_duration(x string) (ans time.Duration, err error) {\n\tswitch x {\n\tcase \"never\":\n\t\treturn 0, nil\n\tcase \"\":\n\t\treturn -1, nil\n\t}\n\ttrailer := x[len(x)-1]\n\tmultipler := time.Second\n\tswitch trailer {\n\tcase 's':\n\t\tx = x[:len(x)-1]\n\tcase 'm':\n\t\tx = x[:len(x)-1]\n\t\tmultipler = time.Minute\n\tcase 'h':\n\t\tx = x[:len(x)-1]\n\t\tmultipler = time.Hour\n\tcase 'd':\n\t\tx = x[:len(x)-1]\n\t\tmultipler = time.Hour * 24\n\t}\n\tval, err := strconv.ParseFloat(x, 64)\n\tif err != nil {\n\t\treturn ans, err\n\t}\n\tans = time.Duration(float64(multipler) * val)\n\treturn\n}\n\nfunc (p *parsed_data) load_image_data() (err error) {\n\tif p.opts.IconPath == \"\" {\n\t\treturn nil\n\t}\n\tf, err := os.Open(p.opts.IconPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\t_, imgfmt, err := image.DecodeConfig(f)\n\tif _, err = f.Seek(0, io.SeekStart); err != nil {\n\t\treturn err\n\t}\n\tif err == nil && imgfmt != \"\" && strings.Contains(\"jpeg jpg gif png\", strings.ToLower(imgfmt)) {\n\t\tp.image_data, err = io.ReadAll(f)\n\t\treturn\n\t}\n\treturn fmt.Errorf(\"The icon must be in PNG, JPEG or GIF formats\")\n}\n\nfunc main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tif len(args) == 0 {\n\t\treturn 1, fmt.Errorf(\"Must specify a TITLE for the notification\")\n\t}\n\tvar p parsed_data\n\tp.opts = opts\n\tp.title = args[0]\n\tif len(args) > 1 {\n\t\tp.body = strings.Join(args[1:], \" \")\n\t}\n\tident := opts.Identifier\n\tif ident == \"\" {\n\t\tif ident, err = random_ident(); err != nil {\n\t\t\treturn 1, fmt.Errorf(\"Failed to generate a random identifier with error: %w\", err)\n\t\t}\n\t}\n\tbad_ident := func(which string) error {\n\t\treturn fmt.Errorf(\"Invalid identifier: %s must be only English letters, numbers, hyphens and underscores.\", which)\n\t}\n\tif !check_id_valid(ident) {\n\t\treturn 1, bad_ident(ident)\n\t}\n\tp.identifier = ident\n\tif !check_id_valid(opts.IconCacheId) {\n\t\treturn 1, bad_ident(opts.IconCacheId)\n\t}\n\tif len(p.title) == 0 {\n\t\tif ident == \"\" {\n\t\t\treturn 1, fmt.Errorf(\"Must specify a non-empty TITLE for the notification or specify an identifier to close a notification.\")\n\t\t}\n\t\tmsg := ESC_CODE_PREFIX + \"i=\" + ident + \":p=close;\" + ESC_CODE_SUFFIX\n\t\tif opts.OnlyPrintEscapeCode {\n\t\t\t_, err = os.Stdout.WriteString(msg)\n\t\t} else if p.wait_till_closed {\n\t\t\tp.initial_msg = msg\n\t\t\terr = p.run_loop()\n\t\t} else {\n\t\t\tvar term *tty.Term\n\t\t\tif term, err = tty.OpenControllingTerm(); err != nil {\n\t\t\t\treturn 1, fmt.Errorf(\"Failed to open controlling terminal with error: %w\", err)\n\t\t\t}\n\t\t\tif _, err = term.WriteString(msg); err != nil {\n\t\t\t\tterm.RestoreAndClose()\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\tterm.RestoreAndClose()\n\t\t}\n\t}\n\tif p.expire_time, err = parse_duration(opts.ExpireAfter); err != nil {\n\t\treturn 1, fmt.Errorf(\"Invalid expire time: %s with error: %w\", opts.ExpireAfter, err)\n\t}\n\tp.wait_till_closed = opts.WaitTillClosed\n\tif err = p.load_image_data(); err != nil {\n\t\treturn 1, fmt.Errorf(\"Failed to load image data from %s with error %w\", opts.IconPath, err)\n\t}\n\tif opts.OnlyPrintEscapeCode {\n\t\tp.generate_chunks(func(x string) {\n\t\t\tif err == nil {\n\t\t\t\t_, err = os.Stdout.WriteString(x)\n\t\t\t}\n\t\t})\n\t} else {\n\t\tif opts.PrintIdentifier {\n\t\t\tfmt.Println(ident)\n\t\t}\n\t\tif p.wait_till_closed {\n\t\t\terr = p.run_loop()\n\t\t} else {\n\t\t\tvar term *tty.Term\n\t\t\tif term, err = tty.OpenControllingTerm(); err != nil {\n\t\t\t\treturn 1, fmt.Errorf(\"Failed to open controlling terminal with error: %w\", err)\n\t\t\t}\n\t\t\tp.generate_chunks(func(x string) {\n\t\t\t\tif err == nil {\n\t\t\t\t\t_, err = term.WriteString(x)\n\t\t\t\t}\n\t\t\t})\n\t\t\tterm.RestoreAndClose()\n\t\t}\n\n\t}\n\tif err != nil {\n\t\trc = 1\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/notify/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\n\n\ndef OPTIONS() -> str:\n    from kitty.constants import standard_icon_names\n    return  f'''\n--icon -n\ntype=list\nThe name of the icon to use for the notification. An icon with this name\nwill be searched for on the computer running the terminal emulator. Can\nbe specified multiple times, the first name that is found will be used.\nStandard names: {', '.join(sorted(standard_icon_names))}\n\n\n--icon-path -p\nPath to an image file in PNG/JPEG/GIF formats to use as the icon. If both\nname and path are specified then first the name will be looked for and if not found\nthen the path will be used.\n\n\n--app-name -a\ndefault=kitten-notify\nThe application name for the notification.\n\n\n--button -b\ntype=list\nAdd a button with the specified text to the notification. Can be specified multiple times for multiple buttons.\nIf --wait-till-closed is used then the kitten will print the button number to STDOUT if the user clicks a button.\n1 for the first button, 2 for the second button and so on.\n\n\n--urgency -u\ndefault=normal\nchoices=normal,low,critical\nThe urgency of the notification.\n\n\n--expire-after -e\nThe duration, for the notification to appear on screen. The default is to\nuse the policy of the OS notification service. A value of :code:`never` means the notification should\nnever expire, however, this may or may not work depending on the policies of the OS notification\nservice. Time is specified in the form NUMBER[SUFFIX] where SUFFIX can be :code:`s` for seconds, :code:`m` for minutes,\n:code:`h` for hours or :code:`d` for days. Non-integer numbers are allowed.\nIf not specified, seconds is assumed. The notification is guaranteed to be closed automatically\nafter the specified time has elapsed. The notification could be closed before by user\naction or OS policy.\n\n\n--sound-name -s\ndefault=system\nThe name of the sound to play with the notification. :code:`system` means let the\nnotification system use whatever sound it wants. :code:`silent` means prevent\nany sound from being played. Any other value is passed to the desktop's notification system\nwhich may or may not honor it.\n\n\n--type -t\nThe notification type. Can be any string, it is used by users to create filter rules\nfor notifications, so choose something descriptive of the notification's purpose.\n\n\n--identifier -i\nThe identifier of this notification. If a notification with the same identifier\nis already displayed, it is replaced/updated.\n\n\n--print-identifier -P\ntype=bool-set\nPrint the identifier for the notification to STDOUT. Useful when not specifying\nyour own identifier via the --identifier option.\n\n\n--wait-till-closed --wait-for-completion -w\ntype=bool-set\nWait until the notification is closed. If the user activates the notification,\n\"0\" is printed to STDOUT before quitting. If a button on the notification is pressed the\nnumber corresponding to the button is printed to STDOUT. Press the Esc or Ctrl+C keys\nto close the notification manually.\n\n\n--only-print-escape-code\ntype=bool-set\nOnly print the escape code to STDOUT. Useful if using this kitten as part\nof a larger application. If this is specified, the --wait-till-closed option\nwill be used for escape code generation, but no actual waiting will be done.\n\n\n--icon-cache-id -g\nIdentifier to use when caching icons in the terminal emulator. Using an identifier means\nthat icon data needs to be transmitted only once using --icon-path. Subsequent invocations\nwill use the cached icon data, at least until the terminal instance is restarted. This is useful\nif this kitten is being used inside a larger application, with --only-print-escape-code.\n'''\n\nhelp_text = '''\\\nSend notifications to the user that are displayed to them via the\ndesktop environment's notifications service. Works over SSH as well.\n\nTo update an existing notification, specify the identifier of the notification\nwith the --identifier option. The value should be the same as the identifier specified for\nthe notification you wish to update.\n\nIf no title is specified and an identifier is specified using the --identifier\noption, then instead of creating a new notification, an existing notification\nwith the specified identifier is closed.\n'''\n\nusage = 'TITLE [BODY ...]'\nif __name__ == '__main__':\n    raise SystemExit('This should be run as `kitten notify ...`')\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Send notifications to the user'\n"
  },
  {
    "path": "kittens/pager/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/pager/file_input.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage pager\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty/tools/simdstring\"\n)\n\nvar _ = fmt.Print\n\nfunc wait_for_file_to_grow(file_name string, limit int64) (err error) {\n\t// TODO: Use the fsnotify package to avoid this poll\n\tfor {\n\t\ttime.Sleep(time.Second)\n\t\ts, err := os.Stat(file_name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif s.Size() > limit {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc read_input(input_file *os.File, input_file_name string, input_channel chan<- input_line_struct, follow_file bool, count_carriage_returns bool) {\n\tconst buf_capacity = 8192\n\tbuf := make([]byte, buf_capacity)\n\toutput_buf := strings.Builder{}\n\toutput_buf.Grow(buf_capacity)\n\tvar err error\n\tvar n int\n\tvar total_read int64\n\tvar num_carriage_returns int\n\n\tdefer func() {\n\t\t_ = input_file.Close()\n\t\tlast := input_line_struct{line: output_buf.String(), err: err, num_carriage_returns: num_carriage_returns}\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tlast.err = nil\n\t\t}\n\t\tif len(last.line) > 0 || last.err != nil {\n\t\t\tinput_channel <- last\n\t\t}\n\t\tclose(input_channel)\n\t}()\n\n\tvar process_chunk func([]byte)\n\n\tif count_carriage_returns {\n\t\tprocess_chunk = func(chunk []byte) {\n\t\t\tfor len(chunk) > 0 {\n\t\t\t\tidx := simdstring.IndexByte2(chunk, '\\n', '\\r')\n\t\t\t\tif idx == -1 {\n\t\t\t\t\t_, _ = output_buf.Write(chunk)\n\t\t\t\t\tchunk = nil\n\t\t\t\t}\n\t\t\t\tswitch chunk[idx] {\n\t\t\t\tcase '\\r':\n\t\t\t\t\tnum_carriage_returns += 1\n\t\t\t\tdefault:\n\t\t\t\t\tinput_channel <- input_line_struct{line: output_buf.String(), num_carriage_returns: num_carriage_returns, is_a_complete_line: true}\n\t\t\t\t\tnum_carriage_returns = 0\n\t\t\t\t\toutput_buf.Reset()\n\t\t\t\t\toutput_buf.Grow(buf_capacity)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tprocess_chunk = func(chunk []byte) {\n\t\t\tfor len(chunk) > 0 {\n\t\t\t\tidx := bytes.IndexByte(chunk, '\\n')\n\t\t\t\tswitch idx {\n\t\t\t\tcase -1:\n\t\t\t\t\t_, _ = output_buf.Write(chunk)\n\t\t\t\t\tchunk = nil\n\t\t\t\tdefault:\n\t\t\t\t\t_, _ = output_buf.Write(chunk[idx:])\n\t\t\t\t\tchunk = chunk[idx+1:]\n\t\t\t\t\tinput_channel <- input_line_struct{line: output_buf.String(), is_a_complete_line: true}\n\t\t\t\t\toutput_buf.Reset()\n\t\t\t\t\toutput_buf.Grow(buf_capacity)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor {\n\t\tfor err != nil {\n\t\t\tn, err = input_file.Read(buf)\n\t\t\tif n > 0 {\n\t\t\t\ttotal_read += int64(n)\n\t\t\t\tprocess_chunk(buf)\n\t\t\t}\n\t\t\tif err == unix.EAGAIN || err == unix.EINTR {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t}\n\t\tif !follow_file {\n\t\t\tbreak\n\t\t}\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tinput_file.Close()\n\t\t\tif err = wait_for_file_to_grow(input_file_name, total_read); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif input_file, err = os.Open(input_file_name); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvar off int64\n\t\t\tif off, err = input_file.Seek(total_read, io.SeekStart); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif off != total_read {\n\t\t\t\terr = fmt.Errorf(\"Failed to seek in %s to: %d\", input_file_name, off)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "kittens/pager/main.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage pager\n\n// TODO:\n// Scroll to line when starting\n// Visual mode elect with copy/paste and copy-on-select\n// Mouse based wheel scroll, drag to select, drag scroll, double click to select\n// Hyperlinks: Clicking should delegate to terminal and also allow user to specify action\n// Keyboard hints mode for clicking hyperlinks\n// Display images when used as scrollback pager\n// automatic follow when input is a pipe/tty and on last line like tail -f\n// syntax highlighting using chroma\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n)\n\nvar _ = fmt.Print\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\ntype input_line_struct struct {\n\tline                 string\n\tnum_carriage_returns int\n\tis_a_complete_line   bool\n\terr                  error\n}\n\ntype global_state_struct struct {\n\tinput_file_name string\n\topts            *Options\n}\n\nvar global_state global_state_struct\n\nfunc main(_ *cli.Command, opts_ *Options, args []string) (rc int, err error) {\n\tglobal_state.opts = opts_\n\tinput_channel := make(chan input_line_struct, 4096)\n\tvar input_file *os.File\n\tif len(args) > 1 {\n\t\treturn 1, fmt.Errorf(\"Only a single file can be viewed at a time\")\n\t}\n\tif len(args) == 0 {\n\t\tif tty.IsTerminal(os.Stdin.Fd()) {\n\t\t\treturn 1, fmt.Errorf(\"STDIN is a terminal and no filename specified. See --help\")\n\t\t}\n\t\tinput_file = os.Stdin\n\t\tglobal_state.input_file_name = \"/dev/stdin\"\n\t} else {\n\t\tinput_file, err = os.Open(args[0])\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tif tty.IsTerminal(input_file.Fd()) {\n\t\t\treturn 1, fmt.Errorf(\"%s is a terminal not paging it\", args[0])\n\t\t}\n\t\tglobal_state.input_file_name = args[0]\n\t}\n\tfollow := global_state.opts.Follow\n\tif follow && global_state.input_file_name == \"/dev/stdin\" {\n\t\tfollow = false\n\t}\n\tgo read_input(input_file, global_state.input_file_name, input_channel, follow, global_state.opts.Role == \"scrollback\")\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/pager/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport sys\n\nfrom kitty.simple_cli_definitions import CompletionSpec\n\nOPTIONS = '''\n--role\ndefault=pager\nchoices=pager,scrollback\nThe role the pager is used for. The default is a standard less like pager.\n\n\n--follow\ntype=bool-set\nFollow changes in the specified file, automatically scrolling if currently on the last line.\n'''.format\n\nhelp_text = '''\\\nDisplay text in a pager with various features such as searching, copy/paste, etc.\nText can some from the specified file or from STDIN. If no filename is specified\nand STDIN is not a TTY, it is used.\n'''\nusage = '[filename]'\n\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('Must be run as kitten pager')\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Pretty, side-by-side diffing of files and images'\n    cd['args_completion'] = CompletionSpec.from_string('type:file mime:text/* group:\"Text files\"')\n"
  },
  {
    "path": "kittens/panel/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/panel/main.go",
    "content": "package panel\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc complete_kitty_listen_on(completions *cli.Completions, word string, arg_num int) {\n\tif !strings.Contains(word, \":\") {\n\t\tmg := completions.AddMatchGroup(\"Address family\")\n\t\tmg.NoTrailingSpace = true\n\t\tfor _, q := range []string{\"unix:\", \"tcp:\"} {\n\t\t\tif strings.HasPrefix(q, word) {\n\t\t\t\tmg.AddMatch(q)\n\t\t\t}\n\t\t}\n\t} else if strings.HasPrefix(word, \"unix:\") && !strings.HasPrefix(word, \"unix:@\") {\n\t\tcli.FnmatchCompleter(\"UNIX sockets\", cli.CWD, \"*\")(completions, word[len(\"unix:\"):], arg_num)\n\t\tcompletions.AddPrefixToAllMatches(\"unix:\")\n\t}\n}\n\nvar CompleteKittyListenOn = complete_kitty_listen_on\n\nfunc GetQuickAccessKittyExe() (kitty_exe string, err error) {\n\tif kitty_exe, err = filepath.EvalSymlinks(utils.KittyExe()); err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to find path to the kitty executable, this kitten requires the kitty executable to function. The kitty executable or a symlink to it must be placed in the same directory as the kitten executable. Error: %w\", err)\n\t}\n\tif runtime.GOOS == \"darwin\" {\n\t\tq := filepath.Join(filepath.Dir(filepath.Dir(kitty_exe)), \"kitty-quick-access.app\", \"Contents\", \"MacOS\", \"kitty-quick-access\")\n\t\tif err := unix.Access(q, unix.X_OK); err == nil {\n\t\t\tkitty_exe = q\n\t\t}\n\t}\n\treturn kitty_exe, nil\n\n}\n\nfunc main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {\n\tkitty_exe, err := GetQuickAccessKittyExe()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\targv := []string{kitty_exe, \"+kitten\", \"panel\"}\n\targv = append(argv, o.AsCommandLine()...)\n\terr = unix.Exec(kitty_exe, append(argv, args...), os.Environ())\n\trc = 1\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/panel/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport sys\nfrom contextlib import suppress\nfrom functools import partial\nfrom types import MappingProxyType\nfrom typing import Any, Iterable, Mapping, Sequence\n\nfrom kitty.cli import parse_args\nfrom kitty.cli_stub import PanelCLIOptions\nfrom kitty.constants import is_macos, kitten_exe\nfrom kitty.fast_data_types import (\n    GLFW_EDGE_BOTTOM,\n    GLFW_EDGE_CENTER,\n    GLFW_EDGE_CENTER_SIZED,\n    GLFW_EDGE_LEFT,\n    GLFW_EDGE_NONE,\n    GLFW_EDGE_RIGHT,\n    GLFW_EDGE_TOP,\n    GLFW_FOCUS_EXCLUSIVE,\n    GLFW_FOCUS_NOT_ALLOWED,\n    GLFW_FOCUS_ON_DEMAND,\n    GLFW_LAYER_SHELL_BACKGROUND,\n    GLFW_LAYER_SHELL_OVERLAY,\n    GLFW_LAYER_SHELL_PANEL,\n    GLFW_LAYER_SHELL_TOP,\n    layer_shell_config_for_os_window,\n    set_layer_shell_config,\n    toggle_os_window_visibility,\n)\nfrom kitty.simple_cli_definitions import panel_options_spec\nfrom kitty.types import LayerShellConfig, run_once\nfrom kitty.typing_compat import BossType\nfrom kitty.utils import log_error\n\nargs = PanelCLIOptions()\nhelp_text = 'Use a command line program to draw a GPU accelerated panel on your desktop'\nusage = '[cmdline-to-run ...]'\n\n\ndef panel_kitten_options_spec() -> str:\n    if not hasattr(panel_kitten_options_spec, 'ans'):\n           setattr(panel_kitten_options_spec, 'ans', panel_options_spec())\n    ans: str = getattr(panel_kitten_options_spec, 'ans')\n    return ans\n\n\ndef parse_panel_args(args: list[str], track_seen_options: dict[str, Any] | None = None) -> tuple[PanelCLIOptions, list[str]]:\n    return parse_args(\n        args, panel_kitten_options_spec, usage, help_text, 'kitty +kitten panel',\n        result_class=PanelCLIOptions, track_seen_options=track_seen_options)\n\n\ndef dual_distance(spec: str, min_cell_value_if_no_pixels: int = 0) -> tuple[int, int]:\n    with suppress(Exception):\n        return int(spec), 0\n    if spec.endswith('px'):\n        return min_cell_value_if_no_pixels, int(spec[:-2])\n    if spec.endswith('c'):\n        return int(spec[:-1]), 0\n    return min_cell_value_if_no_pixels, 0\n\n\ndef layer_shell_config(opts: PanelCLIOptions) -> LayerShellConfig:\n    ltype = {\n        'background': GLFW_LAYER_SHELL_BACKGROUND,\n        'bottom': GLFW_LAYER_SHELL_PANEL,\n        'top': GLFW_LAYER_SHELL_TOP,\n        'overlay': GLFW_LAYER_SHELL_OVERLAY\n    }.get(opts.layer, GLFW_LAYER_SHELL_PANEL)\n    ltype = GLFW_LAYER_SHELL_BACKGROUND if opts.edge == 'background' else ltype\n    edge = {\n        'top': GLFW_EDGE_TOP, 'bottom': GLFW_EDGE_BOTTOM, 'left': GLFW_EDGE_LEFT, 'right': GLFW_EDGE_RIGHT,\n        'center': GLFW_EDGE_CENTER, 'none': GLFW_EDGE_NONE, 'center-sized': GLFW_EDGE_CENTER_SIZED,\n    }.get(opts.edge, GLFW_EDGE_TOP)\n    focus_policy = {\n        'not-allowed': GLFW_FOCUS_NOT_ALLOWED, 'exclusive': GLFW_FOCUS_EXCLUSIVE, 'on-demand': GLFW_FOCUS_ON_DEMAND\n    }.get(opts.focus_policy, GLFW_FOCUS_NOT_ALLOWED)\n    if opts.hide_on_focus_loss:\n        focus_policy = GLFW_FOCUS_ON_DEMAND\n    x, y = dual_distance(opts.columns, min_cell_value_if_no_pixels=1), dual_distance(opts.lines, min_cell_value_if_no_pixels=1)\n    return LayerShellConfig(type=ltype,\n                            edge=edge,\n                            x_size_in_cells=x[0], x_size_in_pixels=x[1],\n                            y_size_in_cells=y[0], y_size_in_pixels=y[1],\n                            requested_top_margin=max(0, opts.margin_top),\n                            requested_left_margin=max(0, opts.margin_left),\n                            requested_bottom_margin=max(0, opts.margin_bottom),\n                            requested_right_margin=max(0, opts.margin_right),\n                            focus_policy=focus_policy,\n                            requested_exclusive_zone=opts.exclusive_zone,\n                            override_exclusive_zone=opts.override_exclusive_zone,\n                            hide_on_focus_loss=opts.hide_on_focus_loss,\n                            output_name=opts.output_name or '')\n\n\n@run_once\ndef cli_option_to_lsc_configs_map() -> MappingProxyType[str, tuple[str, ...]]:\n    return MappingProxyType({\n        'lines': ('y_size_in_cells', 'y_size_in_pixels'),\n        'columns': ('x_size_in_cells', 'x_size_in_pixels'),\n        'margin_top': ('requested_top_margin',),\n        'margin_left': ('requested_left_margin',),\n        'margin_bottom': ('requested_bottom_margin',),\n        'margin_right': ('requested_right_margin',),\n        'edge': ('edge',),\n        'layer': ('type',),\n        'output_name': ('output_name',),\n        'focus_policy': ('focus_policy',),\n        'exclusive_zone': ('requested_exclusive_zone',),\n        'override_exclusive_zone': ('override_exclusive_zone',),\n        'hide_on_focus_loss': ('hide_on_focus_loss',)\n    })\n\n\ndef incrementally_update_layer_shell_config(existing: dict[str, Any], cli_options: Iterable[str]) -> LayerShellConfig:\n    seen_options: dict[str, Any] = {}\n    cli_options = [('' if x.startswith('--') else '--') + x for x in cli_options]\n    try:\n        try:\n            opts, _ = parse_panel_args(cli_options, track_seen_options=seen_options)\n        except SystemExit as e:\n            raise ValueError(str(e))\n        lsc = layer_shell_config(opts)\n    except Exception as e:\n        raise ValueError(f'Invalid panel options specified: {e}')\n    lsc_cli_map = cli_option_to_lsc_configs_map()\n    for option in seen_options:\n        for config in lsc_cli_map.get(option, ()):\n            existing[config] = getattr(lsc, config)\n    if seen_options.get('edge') == 'background':\n        existing['type'] = GLFW_LAYER_SHELL_BACKGROUND\n    if existing['hide_on_focus_loss']:\n        existing['focus_policy'] = GLFW_FOCUS_ON_DEMAND\n    return LayerShellConfig(**existing)\n\n\nmtime_map: dict[str, float] = {}\n\n\ndef have_config_files_been_updated(config_files: Iterable[str]) -> bool:\n    ans = False\n    for cf in config_files:\n        try:\n            mtime = os.path.getmtime(cf)\n        except OSError:\n            mtime = 0\n        if mtime_map.get(cf, 0) != mtime:\n            ans = True\n        mtime_map[cf] = mtime\n    return ans\n\n\ndef handle_single_instance_command(boss: BossType, sys_args: Sequence[str], environ: Mapping[str, str], notify_on_os_window_death: str | None = '') -> None:\n    global args\n    from kitty.cli import parse_override\n    from kitty.tabs import SpecialWindow\n    try:\n        new_args, items = parse_panel_args(list(sys_args[1:]))\n    except BaseException as e:\n        log_error(f'Invalid arguments received over single instance socket: {sys_args} with error: {e}')\n        return\n    lsc = layer_shell_config(new_args)\n    config_changed = have_config_files_been_updated(new_args.config) or args.config != new_args.config or args.override != new_args.override\n    args = new_args\n    if config_changed:\n        boss.load_config_file(*args.config, overrides=tuple(map(parse_override, new_args.override)))\n    if args.toggle_visibility and boss.os_window_map:\n        for os_window_id in boss.os_window_map:\n            existing = layer_shell_config_for_os_window(os_window_id)\n            layer_shell_config_changed = not existing or any(f for f in lsc._fields if getattr(lsc, f) != existing.get(f))\n            toggle_os_window_visibility(os_window_id, move_to_active_screen=args.move_to_active_monitor)\n            if layer_shell_config_changed:\n                set_layer_shell_config(os_window_id, lsc)\n        return\n    items = items or [kitten_exe(), 'run-shell']\n    os_window_id = boss.add_os_panel(lsc, args.cls, args.name)\n    if notify_on_os_window_death:\n        boss.os_window_death_actions[os_window_id] = partial(boss.notify_on_os_window_death, notify_on_os_window_death)\n    tm = boss.os_window_map[os_window_id]\n    tm.new_tab(SpecialWindow(cmd=items, env=dict(environ)))\n\n\ndef main(sys_args: list[str]) -> None:\n    # run_kitten runs using runpy.run_module which does not import into\n    # sys.modules, which means the module will be re-imported later, causing\n    # global variables to be duplicated, so do it now.\n    from kittens.panel.main import actual_main\n    actual_main(sys_args)\n    return\n\n\ndef actual_main(sys_args: list[str]) -> None:\n    global args\n    args, items = parse_panel_args(sys_args[1:])\n    have_config_files_been_updated(args.config)\n    sys.argv = ['kitty']\n    if args.debug_rendering:\n        sys.argv.append('--debug-rendering')\n    if args.debug_input:\n        sys.argv.append('--debug-input')\n    for config in args.config:\n        sys.argv.extend(('--config', config))\n    if not is_macos:\n        sys.argv.extend(('--class', args.cls))\n    if args.name:\n        sys.argv.extend(('--name', args.name))\n    if args.start_as_hidden:\n        sys.argv.append('--start-as=hidden')\n    if args.grab_keyboard:\n        sys.argv.append('--grab-keyboard')\n    for override in args.override:\n        sys.argv.extend(('--override', override))\n    sys.argv.append('--override=linux_display_server=auto')\n    sys.argv.append('--override=macos_quit_when_last_window_closed=yes')\n    sys.argv.append('--override=macos_hide_from_tasks=yes')\n    sys.argv.append('--override=macos_window_resizable=no')\n    if args.single_instance:\n        sys.argv.append('--single-instance')\n    if args.instance_group:\n        sys.argv.append(f'--instance-group={args.instance_group}')\n    if args.listen_on:\n        sys.argv.append(f'--listen-on={args.listen_on}')\n\n    sys.argv.extend(items)\n    from kitty.main import main as real_main\n    from kitty.main import run_app\n    run_app.cached_values_name = 'panel'\n    run_app.layer_shell_config = layer_shell_config(args)\n    real_main(called_from_panel=True)\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd: dict = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = panel_kitten_options_spec\n    cd['help_text'] = help_text\n    cd['short_desc'] = help_text\n"
  },
  {
    "path": "kittens/query_terminal/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/query_terminal/main.go",
    "content": "package query_terminal\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"os\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n)\n\nvar _ = fmt.Print\n\nfunc main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tqueries := kitty.QueryNames\n\tif len(args) > 0 && !slices.Contains(args, \"all\") {\n\t\tqueries = make([]string, len(args))\n\t\tfor i, x := range args {\n\t\t\tif !slices.Contains(kitty.QueryNames, x) {\n\t\t\t\treturn 1, fmt.Errorf(\"Unknown query: %s\", x)\n\t\t\t}\n\t\t\tqueries[i] = x\n\t\t}\n\t}\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoKeyboardStateChange, loop.NoMouseTracking, loop.NoRestoreColors, loop.NoInBandResizeNotifications)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\ttimed_out := false\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.QueryTerminal(queries...)\n\t\tlp.QueueWriteString(\"\\x1b[c\")\n\t\t_, err := lp.AddTimer(time.Duration(opts.WaitFor*float64(time.Second)), false, func(timer_id loop.IdType) error {\n\t\t\ttimed_out = true\n\t\t\tlp.Quit(1)\n\t\t\treturn nil\n\t\t})\n\n\t\treturn \"\", err\n\t}\n\tbuf := strings.Builder{}\n\n\tlp.OnQueryResponse = func(key, val string, found bool) error {\n\t\tif found {\n\t\t\tfmt.Fprintf(&buf, \"%s: %s\\n\", key, val)\n\t\t} else {\n\t\t\tfmt.Fprintf(&buf, \"%s:\\n\", key)\n\t\t}\n\t\treturn nil\n\t}\n\tlp.OnEscapeCode = func(typ loop.EscapeCodeType, data []byte) error {\n\t\tif typ == loop.CSI && bytes.HasSuffix(data, []byte{'c'}) {\n\t\t\tlp.Quit(0)\n\t\t}\n\t\treturn nil\n\t}\n\terr = lp.Run()\n\trc = lp.ExitCode()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\tos.Stdout.WriteString(buf.String())\n\n\tif timed_out {\n\t\treturn 1, fmt.Errorf(\"timed out waiting for response from terminal\")\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/query_terminal/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport re\nimport sys\nfrom binascii import hexlify, unhexlify\nfrom contextlib import suppress\nfrom typing import get_args\n\nfrom kitty.conf.utils import OSNames, os_name\nfrom kitty.constants import appname, str_version\nfrom kitty.options.types import Options\nfrom kitty.terminfo import names\n\n\nclass Query:\n    name: str = ''\n    ans: str = ''\n    help_text: str = ''\n    override_query_name: str = ''\n\n    @property\n    def query_name(self) -> str:\n        return self.override_query_name or f'kitty-query-{self.name}'\n\n    def __init__(self) -> None:\n        self.encoded_query_name = hexlify(self.query_name.encode('utf-8')).decode('ascii')\n        self.pat = re.compile(f'\\x1bP([01])\\\\+r{self.encoded_query_name}(.*?)\\x1b\\\\\\\\'.encode('ascii'))\n\n    def query_code(self) -> str:\n        return f\"\\x1bP+q{self.encoded_query_name}\\x1b\\\\\"\n\n    def decode_response(self, res: bytes | memoryview) -> str:\n        return unhexlify(res).decode('utf-8')\n\n    def more_needed(self, buffer: bytes) -> bool:\n        m = self.pat.search(buffer)\n        if m is None:\n            return True\n        if m.group(1) == b'1':\n            q = m.group(2)\n            if q.startswith(b'='):\n                with suppress(Exception):\n                    self.ans = self.decode_response(memoryview(q)[1:])\n        return False\n\n    def output_line(self) -> str:\n        return self.ans\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        raise NotImplementedError()\n\n\nall_queries: dict[str, type[Query]] = {}\n\n\ndef query(cls: type[Query]) -> type[Query]:\n    all_queries[cls.name] = cls\n    return cls\n\n\n@query\nclass TerminalName(Query):\n    name: str = 'name'\n    override_query_name: str = 'name'\n    help_text: str = f'Terminal name (e.g. :code:`{names[0]}`)'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        return appname\n\n\n@query\nclass TerminalVersion(Query):\n    name: str = 'version'\n    help_text: str = f'Terminal version (e.g. :code:`{str_version}`)'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        return str_version\n\n\n@query\nclass AllowHyperlinks(Query):\n    name: str = 'allow_hyperlinks'\n    help_text: str = 'The config option :opt:`allow_hyperlinks` in :file:`kitty.conf` for allowing hyperlinks can be :code:`yes`, :code:`no` or :code:`ask`'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        return 'ask' if opts.allow_hyperlinks == 0b11 else ('yes' if opts.allow_hyperlinks else 'no')\n\n\n@query\nclass FontFamily(Query):\n    name: str = 'font_family'\n    help_text: str = 'The current font\\'s PostScript name'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import current_fonts\n        cf = current_fonts(os_window_id)\n        return cf['medium'].postscript_name()\n\n\n@query\nclass BoldFont(Query):\n    name: str = 'bold_font'\n    help_text: str = 'The current bold font\\'s PostScript name'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import current_fonts\n        cf = current_fonts(os_window_id)\n        return cf['bold'].postscript_name()\n\n\n@query\nclass ItalicFont(Query):\n    name: str = 'italic_font'\n    help_text: str = 'The current italic font\\'s PostScript name'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import current_fonts\n        cf = current_fonts(os_window_id)\n        return cf['italic'].postscript_name()\n\n\n@query\nclass BiFont(Query):\n    name: str = 'bold_italic_font'\n    help_text: str = 'The current bold-italic font\\'s PostScript name'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import current_fonts\n        cf = current_fonts(os_window_id)\n        return cf['bi'].postscript_name()\n\n\n@query\nclass FontSize(Query):\n    name: str = 'font_size'\n    help_text: str = 'The current font size in pts'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import current_fonts\n        cf = current_fonts(os_window_id)\n        return f'{cf[\"font_sz_in_pts\"]:g}'\n\n@query\nclass DpiX(Query):\n    name: str = 'dpi_x'\n    help_text: str = 'The current DPI on the x-axis'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import current_fonts\n        cf = current_fonts(os_window_id)\n        return f'{cf[\"logical_dpi_x\"]:g}'\n\n@query\nclass DpiY(Query):\n    name: str = 'dpi_y'\n    help_text: str = 'The current DPI on the y-axis'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import current_fonts\n        cf = current_fonts(os_window_id)\n        return f'{cf[\"logical_dpi_y\"]:g}'\n\n\n@query\nclass Foreground(Query):\n    name: str = 'foreground'\n    help_text: str = 'The current foreground color as a 24-bit # color code'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import get_boss, get_options\n        boss = get_boss()\n        w = boss.window_id_map.get(window_id)\n        if w is None:\n            return opts.foreground.as_sharp\n        return (w.screen.color_profile.default_fg or get_options().foreground).as_sharp\n\n\n@query\nclass Background(Query):\n    name: str = 'background'\n    help_text: str = 'The current background color as a 24-bit # color code'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import get_boss, get_options\n        boss = get_boss()\n        w = boss.window_id_map.get(window_id)\n        if w is None:\n            return opts.background.as_sharp\n        return (w.screen.color_profile.default_bg or get_options().background).as_sharp\n\n\n@query\nclass BackgroundOpacity(Query):\n    name: str = 'background_opacity'\n    help_text: str = 'The current background opacity as a number between 0 and 1'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        from kitty.fast_data_types import background_opacity_of\n        ans = background_opacity_of(os_window_id)\n        if ans is None:\n            ans = 1.0\n        return f'{ans:g}'\n\n\n@query\nclass ClipboardControl(Query):\n    name: str = 'clipboard_control'\n    help_text: str = 'The config option :opt:`clipboard_control` in :file:`kitty.conf` for allowing reads/writes to/from the clipboard'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> str:\n        return ' '.join(opts.clipboard_control)\n\n\n@query\nclass OSName(Query):\n    name: str = 'os_name'\n    help_text: str = f'The name of the OS the terminal is running on. kitty returns values: {\", \".join(sorted(get_args(OSNames)))}'\n\n    @staticmethod\n    def get_result(opts: Options, window_id: int, os_window_id: int) -> OSNames:\n        return os_name()\n\n\ndef get_result(name: str, window_id: int, os_window_id: int) -> str | None:\n    from kitty.fast_data_types import get_options\n    q = all_queries.get(name)\n    if q is None:\n        return None\n    return q.get_result(get_options(), window_id, os_window_id)\n\n\ndef options_spec() -> str:\n    return '''\\\n--wait-for\ntype=float\ndefault=10\nThe amount of time (in seconds) to wait for a response from the terminal, after\nquerying it.\n'''\n\n\nhelp_text = '''\\\nQuery the terminal this kitten is run in for various capabilities. This sends\nescape codes to the terminal and based on its response prints out data about\nsupported capabilities. Note that this is a blocking operation, since it has to\nwait for a response from the terminal. You can control the maximum wait time via\nthe :code:`--wait-for` option.\n\nThe output is lines of the form::\n\n    query: data\n\nIf a particular :italic:`query` is unsupported by the running kitty version, the\n:italic:`data` will be blank.\n\nNote that when calling this from another program, be very careful not to perform\nany I/O on the terminal device until this kitten exits.\n\nAvailable queries are:\n\n{}\n\n'''.format('\\n'.join(\n    f':code:`{name}`:\\n  {c.help_text}\\n' for name, c in all_queries.items()))\nusage = '[query1 query2 ...]'\n\n\nif __name__ == '__main__':\n    raise SystemExit('Should be run as kitten hints')\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = options_spec\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Query the terminal for various capabilities'\n"
  },
  {
    "path": "kittens/quick_access_terminal/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/quick_access_terminal/main.go",
    "content": "package quick_access_terminal\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\n\t\"github.com/kovidgoyal/kitty/kittens/panel\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nvar complete_kitty_listen_on = panel.CompleteKittyListenOn\n\nfunc load_config(opts *Options) (ans *Config, err error) {\n\tans = NewConfig()\n\tp := config.ConfigParser{LineHandler: ans.Parse}\n\terr = p.LoadConfig(\"quick-access-terminal.conf\", opts.Config, opts.Override)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn ans, nil\n}\n\nfunc main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tconf, err := load_config(opts)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tkitty_exe, err := panel.GetQuickAccessKittyExe()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\targv := []string{kitty_exe, \"+kitten\", \"panel\", \"--toggle-visibility\", \"--exclusive-zone=0\", \"--override-exclusive-zone\", \"--layer=overlay\", \"--single-instance\", \"--move-to-active-monitor\"}\n\targv = append(argv, fmt.Sprintf(\"--lines=%s\", conf.Lines))\n\targv = append(argv, fmt.Sprintf(\"--columns=%s\", conf.Columns))\n\targv = append(argv, fmt.Sprintf(\"--edge=%s\", conf.Edge))\n\tif conf.Margin_top != 0 {\n\t\targv = append(argv, fmt.Sprintf(\"--margin-top=%d\", conf.Margin_top))\n\t}\n\tif conf.Margin_bottom != 0 {\n\t\targv = append(argv, fmt.Sprintf(\"--margin-bottom=%d\", conf.Margin_bottom))\n\t}\n\tif conf.Margin_left != 0 {\n\t\targv = append(argv, fmt.Sprintf(\"--margin-left=%d\", conf.Margin_left))\n\t}\n\tif conf.Margin_right != 0 {\n\t\targv = append(argv, fmt.Sprintf(\"--margin-right=%d\", conf.Margin_right))\n\t}\n\tif len(conf.Kitty_conf) > 0 {\n\t\tcdir := utils.ConfigDir()\n\t\tfor _, c := range conf.Kitty_conf {\n\t\t\tif !filepath.IsAbs(c) {\n\t\t\t\tc = filepath.Join(cdir, c)\n\t\t\t}\n\t\t\targv = append(argv, fmt.Sprintf(\"--config=%s\", c))\n\t\t}\n\t}\n\tif len(conf.Kitty_override) > 0 {\n\t\tfor _, c := range conf.Kitty_override {\n\t\t\targv = append(argv, fmt.Sprintf(\"--override=%s\", c))\n\t\t}\n\t}\n\n\targv = append(argv, fmt.Sprintf(\"--override=background_opacity=%f\", conf.Background_opacity))\n\tif runtime.GOOS != \"darwin\" {\n\t\targv = append(argv, fmt.Sprintf(\"--app-id=%s\", conf.App_id))\n\t}\n\tif conf.Output_name != \"\" {\n\t\targv = append(argv, fmt.Sprintf(\"--output-name=%s\", conf.Output_name))\n\t}\n\targv = append(argv, fmt.Sprintf(\"--focus-policy=%s\", conf.Focus_policy))\n\tif conf.Start_as_hidden {\n\t\targv = append(argv, `--start-as-hidden`)\n\t}\n\tif conf.Grab_keyboard {\n\t\targv = append(argv, `--grab-keyboard`)\n\t}\n\tif conf.Hide_on_focus_loss {\n\t\targv = append(argv, `--hide-on-focus-loss`)\n\t}\n\tif opts.DebugRendering {\n\t\targv = append(argv, `--debug-rendering`)\n\t}\n\tif opts.DebugInput {\n\t\targv = append(argv, `--debug-input`)\n\t}\n\tif opts.Detach {\n\t\targv = append(argv, `--detach`)\n\t}\n\tif opts.DetachedLog != \"\" {\n\t\tif dl, err := filepath.Abs(opts.DetachedLog); err != nil {\n\t\t\treturn 1, err\n\t\t} else {\n\t\t\targv = append(argv, dl)\n\t\t}\n\t}\n\tif opts.InstanceGroup != \"\" {\n\t\targv = append(argv, fmt.Sprintf(\"--instance-group=%s\", opts.InstanceGroup))\n\t}\n\n\targv = append(argv, args...)\n\terr = unix.Exec(kitty_exe, argv, os.Environ())\n\trc = 1\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/quick_access_terminal/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport re\nimport sys\n\nfrom kitty.conf.types import Definition\nfrom kitty.constants import appname\nfrom kitty.simple_cli_definitions import CONFIG_HELP, get_option_maps, grab_keyboard_docs, panel_options_spec, parse_option_spec\n\nhelp_text = 'A quick access terminal window that you can bring up instantly with a keypress or a command.'\n\ndefinition = Definition(\n    '!kittens.quick_access_terminal',\n)\n\nagr = definition.add_group\negr = definition.end_group\nopt = definition.add_option\n\npanel_opts = get_option_maps(parse_option_spec(panel_options_spec())[0])[0]\n\ndef migrate_help(x: str) -> str:\n    def sub(m: re.Match[str]) -> str:\n        return f':opt:`{m.group(1)}`'\n\n    ans = re.sub(r':option:`--(\\S+?)`', sub, x)\n    return ans.replace('Use the special value :code:`list`', 'Run :code:`kitten panel --output-name list`')\n\n\ndef help_of(x: str) -> str:\n    return migrate_help(panel_opts[x].help)\n\n\nagr('qat', 'Window appearance')\n\nopt('lines', '25', long_text=panel_opts['lines'].help)\n\nopt('columns', '80', long_text=panel_opts['columns'].help)\n\nopt('edge', 'top', choices=panel_opts['edge'].choices, long_text=help_of('edge'))\n\nopt('background_opacity', '0.85', option_type='unit_float', long_text='''\nThe background opacity of the window. This works the same as the kitty\noption of the same name, it is present here as it has a different\ndefault value for the quick access terminal.\n''')\n\nopt('hide_on_focus_loss', 'no', option_type='to_bool', long_text='''\nHide the window when it loses keyboard focus automatically. Using this option\nwill force :opt:`focus_policy` to :code:`on-demand`.\n''')\n\nopt('grab_keyboard', 'no', option_type='to_bool', long_text=grab_keyboard_docs)\n\nopt('margin_left', '0', option_type='int', long_text=help_of('margin_left'))\n\nopt('margin_right', '0', option_type='int', long_text=help_of('margin_right'))\n\nopt('margin_top', '0', option_type='int', long_text=help_of('margin_top'))\n\nopt('margin_bottom', '0', option_type='int', long_text=help_of('margin_bottom'))\n\nopt('+kitty_conf', '',\n    long_text='Path to config file to use for kitty when drawing the window. Can be specified multiple times. By default, the'\n    ' normal kitty.conf is used. Relative paths are resolved with respect to the kitty config directory.'\n)\n\nopt('+kitty_override', '', long_text='Override individual kitty configuration options, can be specified multiple times.'\n    ' Syntax: :italic:`kitty_override name=value`. For example: :code:`kitty_override font_size=20`.'\n)\n\nopt('app_id', f'{appname}-quick-access',\n    long_text='On Wayland set the :italic:`namespace` of the layer shell surface.'\n    ' On X11 set the WM_CLASS assigned to the quick access window. (Linux only)')\n\n\nopt('output_name', '', long_text=help_of('output_name'))\n\nopt('start_as_hidden', 'no', option_type='to_bool',\n    long_text='Whether to start the quick access terminal hidden. Useful if you are starting it as part of system startup.')\n\nopt('focus_policy', 'exclusive', choices=panel_opts['focus_policy'].choices, long_text=help_of('focus_policy'))\n\n\n\ndef options_spec() -> str:\n    return f'''\n--config -c\ntype=list\ncompletion=type:file ext:conf group:\"Config files\" kwds:none,NONE\n{CONFIG_HELP.format(conf_name='quick-access-terminal', appname=appname)}\n\n\n--override -o\ntype=list\nOverride individual configuration options, can be specified multiple times.\nSyntax: :italic:`name=value`. For example: :italic:`-o lines=12`\n\n\n--detach\ntype=bool-set\nDetach from the controlling terminal, if any, running in an independent child process,\nthe parent process exits immediately.\n\n\n--detached-log\nPath to a log file to store STDOUT/STDERR when using :option:`--detach`\n\n\n--instance-group\ndefault=quick-access\nThe unique name of this quick access terminal Use a different name if you want multiple such terminals.\n\n\n--debug-rendering\ntype=bool-set\nFor debugging interactions with the compositor/window manager.\n\n\n--debug-input\ntype=bool-set\nFor debugging interactions with the compositor/window manager.\n'''\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('This kitten should be run as: kitten quick-access-terminal')\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd: dict = sys.cli_docs  # type: ignore\n    cd['usage'] = '[cmdline-to-run ...]'\n    cd['options'] = options_spec\n    cd['help_text'] = help_text\n    cd['short_desc'] = help_text\nelif __name__ == '__conf__':\n    sys.options_definition = definition  # type: ignore\n"
  },
  {
    "path": "kittens/remote_file/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/remote_file/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport json\nimport os\nimport shlex\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nimport time\nfrom typing import Any, Optional\n\nfrom kitty.cli import parse_args\nfrom kitty.cli_stub import RemoteFileCLIOptions\nfrom kitty.constants import cache_dir\nfrom kitty.typing_compat import BossType\nfrom kitty.utils import SSHConnectionData, command_for_open, get_editor, open_cmd\n\nfrom ..tui.handler import result_handler\nfrom ..tui.operations import faint, raw_mode, reset_terminal, styled\nfrom ..tui.utils import get_key_press\n\nis_ssh_kitten_sentinel = '!#*&$#($ssh-kitten)(##$'\n\n\ndef key(x: str) -> str:\n    return styled(x, bold=True, fg='green')\n\n\ndef option_text() -> str:\n    return '''\\\n--mode -m\nchoices=ask,edit\ndefault=ask\nWhich mode to operate in.\n\n\n--path -p\nPath to the remote file.\n\n\n--hostname -h\nHostname of the remote host.\n\n\n--ssh-connection-data\nThe data used to connect over ssh.\n'''\n\n\ndef show_error(msg: str) -> None:\n    print(styled(msg, fg='red'), file=sys.stderr)\n    print()\n    print('Press any key to quit', flush=True)\n    with raw_mode():\n        while True:\n            try:\n                q = sys.stdin.buffer.read(1)\n                if q:\n                    break\n            except (KeyboardInterrupt, EOFError):\n                break\n\n\ndef ask_action(opts: RemoteFileCLIOptions) -> str:\n    print('What would you like to do with the remote file on {}:'.format(styled(opts.hostname or 'unknown', bold=True, fg='magenta')))\n    print(styled(opts.path or '', fg='yellow', fg_intense=True))\n    print()\n\n    def help_text(x: str) -> str:\n        return faint(x)\n\n    print('{}dit the file'.format(key('E')))\n    print(help_text('The file will be downloaded and opened in an editor. Any changes you save will'\n                    ' be automatically sent back to the remote machine'))\n    print()\n\n    print('{}pen the file'.format(key('O')))\n    print(help_text('The file will be downloaded and opened by the default open program'))\n    print()\n\n    print('{}ave the file'.format(key('S')))\n    print(help_text('The file will be downloaded to a destination you select'))\n    print()\n\n    print('{}ancel'.format(key('C')))\n    print()\n\n    sys.stdout.flush()\n    response = get_key_press('ceos', 'c')\n    return {'e': 'edit', 'o': 'open', 's': 'save'}.get(response, 'cancel')\n\n\ndef hostname_matches(from_hyperlink: str, actual: str) -> bool:\n    if from_hyperlink == actual:\n        return True\n    if from_hyperlink.partition('.')[0] == actual.partition('.')[0]:\n        return True\n    return False\n\n\nclass ControlMaster:\n\n    def __init__(self, conn_data: SSHConnectionData, remote_path: str, cli_opts: RemoteFileCLIOptions, dest: str = ''):\n        self.conn_data = conn_data\n        self.cli_opts = cli_opts\n        self.remote_path = remote_path\n        self.dest = dest\n        self.tdir = ''\n        self.last_error_log = ''\n        self.cmd_prefix = cmd = [\n            conn_data.binary, '-o', f'ControlPath=~/.ssh/kitty-rf-{os.getpid()}-%C',\n            '-o', 'TCPKeepAlive=yes', '-o', 'ControlPersist=yes'\n        ]\n        self.is_ssh_kitten = conn_data.binary is is_ssh_kitten_sentinel\n        if self.is_ssh_kitten:\n            del cmd[:]\n            self.batch_cmd_prefix = cmd\n            sk_cmdline = json.loads(conn_data.identity_file)\n            while '-t' in sk_cmdline:\n                sk_cmdline.remove('-t')\n            cmd.extend(sk_cmdline[:-2])\n        else:\n            if conn_data.port:\n                cmd.extend(['-p', str(conn_data.port)])\n            if conn_data.identity_file:\n                cmd.extend(['-i', conn_data.identity_file])\n            self.batch_cmd_prefix = cmd + ['-o', 'BatchMode=yes']\n\n    def check_call(self, cmd: list[str]) -> None:\n        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL)\n        stdout = p.communicate()[0]\n        if p.wait() != 0:\n            out = stdout.decode('utf-8', 'replace')\n            raise Exception(f'The ssh command: {shlex.join(cmd)} failed with exit code {p.returncode} and output: {out}')\n\n    def __enter__(self) -> 'ControlMaster':\n        if not self.is_ssh_kitten:\n            self.check_call(\n                self.cmd_prefix + ['-o', 'ControlMaster=auto', '-fN', self.conn_data.hostname])\n            self.check_call(\n                self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname])\n        if not self.dest:\n            self.tdir = tempfile.mkdtemp()\n            self.dest = os.path.join(self.tdir, os.path.basename(self.remote_path))\n        return self\n\n    def __exit__(self, *a: Any) -> None:\n        if not self.is_ssh_kitten:\n            subprocess.Popen(\n                self.batch_cmd_prefix + ['-O', 'exit', self.conn_data.hostname],\n                stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL\n            ).wait()\n        if self.tdir:\n            shutil.rmtree(self.tdir)\n\n    @property\n    def is_alive(self) -> bool:\n        if self.is_ssh_kitten:\n            return True\n        return subprocess.Popen(\n            self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname],\n            stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL\n        ).wait() == 0\n\n    def check_hostname_matches(self) -> bool:\n        if self.is_ssh_kitten:\n            return True\n        cp = subprocess.run(self.batch_cmd_prefix + [self.conn_data.hostname, 'hostname', '-f'], stdout=subprocess.PIPE,\n                            stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL)\n        if cp.returncode == 0:\n            q = tuple(filter(None, cp.stdout.decode('utf-8').strip().splitlines()))[-1]\n            if not hostname_matches(self.cli_opts.hostname or '', q):\n                print(reset_terminal(), end='')\n                print(f'The remote hostname {styled(q, fg=\"green\")} does not match the')\n                print(f'hostname in the hyperlink {styled(self.cli_opts.hostname or \"\", fg=\"red\")}')\n                print('This indicates that kitty has not connected to the correct remote machine.')\n                print('This can happen, for example, when using nested SSH sessions.')\n                print(f'The hostname kitty used to connect was: {styled(self.conn_data.hostname, fg=\"yellow\")}', end='')\n                if self.conn_data.port is not None:\n                    print(f' with port: {self.conn_data.port}')\n                print()\n                print()\n                print('Do you want to continue anyway?')\n                print(\n                    f'{styled(\"Y\", fg=\"green\")}es',\n                    f'{styled(\"N\", fg=\"red\")}o', sep='\\t'\n                )\n                sys.stdout.flush()\n                response = get_key_press('yn', 'n')\n                print(reset_terminal(), end='')\n                return response == 'y'\n        return True\n\n    def show_error(self, msg: str) -> None:\n        if self.last_error_log:\n            print(self.last_error_log, file=sys.stderr)\n            self.last_error_log = ''\n        show_error(msg)\n\n    def download(self) -> bool:\n        cmdline = self.batch_cmd_prefix + [self.conn_data.hostname, 'cat', shlex.quote(self.remote_path)]\n        with open(self.dest, 'wb') as f:\n            cp = subprocess.run(cmdline, stdout=f, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL)\n            if cp.returncode != 0:\n                self.last_error_log = f'The command: {shlex.join(cmdline)} failed\\n' + cp.stderr.decode()\n                return False\n        return True\n\n    def upload(self, suppress_output: bool = True) -> bool:\n        cmd_prefix = self.cmd_prefix if suppress_output else self.batch_cmd_prefix\n        cmd = cmd_prefix + [self.conn_data.hostname, 'cat', '>', shlex.quote(self.remote_path)]\n        if not suppress_output:\n            print(shlex.join(cmd))\n        with open(self.dest, 'rb') as f:\n            if suppress_output:\n                cp = subprocess.run(cmd, stdin=f, capture_output=True)\n                if cp.returncode == 0:\n                    return True\n                self.last_error_log = f'The command: {shlex.join(cmd)} failed\\n' + cp.stdout.decode()\n            else:\n                return subprocess.run(cmd, stdin=f).returncode == 0\n        return False\n\n\nResult = Optional[str]\n\n\ndef main(args: list[str]) -> Result:\n    msg = 'Ask the user what to do with the remote file. For internal use by kitty, do not run it directly.'\n    try:\n        cli_opts, items = parse_args(args[1:], option_text, '', msg, 'kitty +kitten remote_file', result_class=RemoteFileCLIOptions)\n    except SystemExit as e:\n        if e.code != 0:\n            print(e.args[0])\n            input('Press Enter to quit')\n        raise SystemExit(e.code)\n\n    try:\n        action = ask_action(cli_opts)\n    finally:\n        print(reset_terminal(), end='', flush=True)\n    try:\n        return handle_action(action, cli_opts)\n    except Exception:\n        print(reset_terminal(), end='', flush=True)\n        import traceback\n        traceback.print_exc()\n        show_error('Failed with unhandled exception')\n    return None\n\n\ndef save_as(conn_data: SSHConnectionData, remote_path: str, cli_opts: RemoteFileCLIOptions) -> None:\n    ddir = cache_dir()\n    os.makedirs(ddir, exist_ok=True)\n    last_used_store_path = os.path.join(ddir, 'remote-file-last-used.txt')\n    try:\n        with open(last_used_store_path) as f:\n            last_used_path = f.read()\n    except FileNotFoundError:\n        last_used_path = tempfile.gettempdir()\n    last_used_file = os.path.join(last_used_path, os.path.basename(remote_path))\n    print(\n        'Where do you want to save the file? Leaving it blank will save it as:',\n        styled(last_used_file, fg='yellow')\n    )\n    print('Relative paths will be resolved from:', styled(os.getcwd(), fg_intense=True, bold=True))\n    print()\n    from ..tui.path_completer import get_path\n    try:\n        dest = get_path()\n    except (KeyboardInterrupt, EOFError):\n        return\n    if dest:\n        dest = os.path.expandvars(os.path.expanduser(dest))\n        if os.path.isdir(dest):\n            dest = os.path.join(dest, os.path.basename(remote_path))\n        with open(last_used_store_path, 'w') as f:\n            f.write(os.path.dirname(os.path.abspath(dest)))\n    else:\n        dest = last_used_file\n    if os.path.exists(dest):\n        print(reset_terminal(), end='')\n        print(f'The file {styled(dest, fg=\"yellow\")} already exists. What would you like to do?')\n        print(f'{key(\"O\")}verwrite  {key(\"A\")}bort  Auto {key(\"R\")}ename {key(\"N\")}ew name')\n        response = get_key_press('anor', 'a')\n        if response == 'a':\n            return\n        if response == 'n':\n            print(reset_terminal(), end='')\n            return save_as(conn_data, remote_path, cli_opts)\n\n        if response == 'r':\n            q = dest\n            c = 0\n            while os.path.exists(q):\n                c += 1\n                b, ext = os.path.splitext(dest)\n                q = f'{b}-{c}{ext}'\n            dest = q\n    if os.path.dirname(dest):\n        os.makedirs(os.path.dirname(dest), exist_ok=True)\n    with ControlMaster(conn_data, remote_path, cli_opts, dest=dest) as master:\n        if master.check_hostname_matches():\n            if not master.download():\n                master.show_error('Failed to copy file from remote machine')\n\n\ndef handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:\n    cli_data = json.loads(cli_opts.ssh_connection_data or '')\n    if cli_data and cli_data[0] == is_ssh_kitten_sentinel:\n        conn_data = SSHConnectionData(is_ssh_kitten_sentinel, cli_data[-1], -1, identity_file=json.dumps(cli_data[1:]))\n    else:\n        conn_data = SSHConnectionData(*cli_data)\n    remote_path = cli_opts.path or ''\n    if action == 'open':\n        print('Opening', cli_opts.path, 'from', cli_opts.hostname)\n        dest = os.path.join(tempfile.mkdtemp(), os.path.basename(remote_path))\n        with ControlMaster(conn_data, remote_path, cli_opts, dest=dest) as master:\n            if master.check_hostname_matches():\n                if master.download():\n                    return dest\n                master.show_error('Failed to copy file from remote machine')\n    elif action == 'edit':\n        print('Editing', cli_opts.path, 'from', cli_opts.hostname)\n        editor = get_editor()\n        with ControlMaster(conn_data, remote_path, cli_opts) as master:\n            if not master.check_hostname_matches():\n                return None\n            if not master.download():\n                master.show_error(f'Failed to download {remote_path}')\n                return None\n            mtime = os.path.getmtime(master.dest)\n            print(reset_terminal(), end='', flush=True)\n            editor_process = subprocess.Popen(editor + [master.dest])\n            while editor_process.poll() is None:\n                time.sleep(0.1)\n                newmtime = os.path.getmtime(master.dest)\n                if newmtime > mtime:\n                    mtime = newmtime\n                    if master.is_alive:\n                        master.upload()\n            print(reset_terminal(), end='', flush=True)\n            if master.is_alive:\n                if not master.upload(suppress_output=False):\n                    master.show_error(f'Failed to upload {remote_path}')\n            else:\n                master.show_error(f'Failed to upload {remote_path}, SSH master process died')\n    elif action == 'save':\n        print('Saving', cli_opts.path, 'from', cli_opts.hostname)\n        save_as(conn_data, remote_path, cli_opts)\n    return None\n\n\n@result_handler()\ndef handle_result(args: list[str], data: Result, target_window_id: int, boss: BossType) -> None:\n    if data:\n        from kitty.fast_data_types import get_options\n        cmd = command_for_open(get_options().open_url_with)\n        open_cmd(cmd, data)\n\n\nif __name__ == '__main__':\n    main(sys.argv)\n"
  },
  {
    "path": "kittens/resize_window/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/resize_window/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport sys\nfrom typing import Any\n\nfrom kitty.cli import parse_args\nfrom kitty.cli_stub import RCOptions, ResizeCLIOptions\nfrom kitty.constants import version\nfrom kitty.key_encoding import CTRL, EventType, KeyEvent\nfrom kitty.rc.base import command_for_name, parse_subcommand_cli\nfrom kitty.remote_control import encode_send, parse_rc_args\nfrom kitty.utils import ScreenSize\n\nfrom ..tui.handler import Handler\nfrom ..tui.loop import Loop\nfrom ..tui.operations import styled\n\nglobal_opts = RCOptions()\n\n\nclass Resize(Handler):\n\n    print_on_fail: str | None = None\n\n    def __init__(self, opts: ResizeCLIOptions):\n        self.opts = opts\n\n    def initialize(self) -> None:\n        global global_opts\n        global_opts = parse_rc_args(['kitty', '@resize-window'])[0]\n        self.original_size = self.screen_size\n        self.cmd.set_cursor_visible(False)\n        self.cmd.set_line_wrapping(False)\n        self.draw_screen()\n\n    def do_window_resize(self, is_decrease: bool = False, is_horizontal: bool = True, reset: bool = False, multiplier: int = 1) -> None:\n        resize_window = command_for_name('resize_window')\n        increment = self.opts.horizontal_increment if is_horizontal else self.opts.vertical_increment\n        increment *= multiplier\n        if is_decrease:\n            increment *= -1\n        axis = 'reset' if reset else ('horizontal' if is_horizontal else 'vertical')\n        cmdline = [resize_window.name, '--self', f'--increment={increment}', '--axis=' + axis]\n        opts, items = parse_subcommand_cli(resize_window, cmdline)\n        payload = resize_window.message_to_kitty(global_opts, opts, items)\n        send = {'cmd': resize_window.name, 'version': version, 'payload': payload, 'no_response': False}\n        self.write(encode_send(send))\n\n    def on_kitty_cmd_response(self, response: dict[str, Any]) -> None:\n        if not response.get('ok'):\n            err = response['error']\n            if response.get('tb'):\n                err += '\\n' + response['tb']\n            self.print_on_fail = err\n            self.quit_loop(1)\n            return\n        res = response.get('data')\n        if res:\n            self.cmd.bell()\n\n    def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:\n        text = text.upper()\n        if text in 'WNTSR':\n            self.do_window_resize(is_decrease=text in 'NS', is_horizontal=text in 'WN', reset=text == 'R')\n        elif text == 'Q':\n            self.quit_loop(0)\n\n    def on_key(self, key_event: KeyEvent) -> None:\n        if key_event.type is EventType.RELEASE:\n            return\n        if key_event.matches('esc'):\n            self.quit_loop(0)\n            return\n        if key_event.key in ('w', 'n', 't', 's') and key_event.mods_without_locks == CTRL:\n            self.do_window_resize(is_decrease=key_event.key in 'ns', is_horizontal=key_event.key in 'wn', multiplier=2)\n\n    def on_resize(self, new_size: ScreenSize) -> None:\n        self.draw_screen()\n\n    def draw_screen(self) -> None:\n        self.cmd.clear_screen()\n        print = self.print\n        print(styled('Resize this window', bold=True, fg='gray', fg_intense=True))\n        print()\n        print('Press one of the following keys:')\n        print('  {}ider'.format(styled('W', fg='green')))\n        print('  {}arrower'.format(styled('N', fg='green')))\n        print('  {}aller'.format(styled('T', fg='green')))\n        print('  {}horter'.format(styled('S', fg='green')))\n        print('  {}eset'.format(styled('R', fg='red')))\n        print()\n        print('Press {} to quit resize mode'.format(styled('Esc', italic=True)))\n        print('Hold down {} to double step size'.format(styled('Ctrl', italic=True)))\n        print()\n        print(styled('Sizes', bold=True, fg='white', fg_intense=True))\n        print(f'Original: {self.original_size.rows} rows {self.original_size.cols} cols')\n        print('Current:  {} rows {} cols'.format(\n            styled(str(self.screen_size.rows), fg='magenta'), styled(str(self.screen_size.cols), fg='magenta')))\n\n\nOPTIONS = r'''\n--horizontal-increment\ndefault=2\ntype=int\nThe base horizontal increment.\n\n\n--vertical-increment\ndefault=2\ntype=int\nThe base vertical increment.\n'''.format\n\n\ndef main(args: list[str]) -> None:\n    msg = 'Resize the current window'\n    try:\n        cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'resize_window', result_class=ResizeCLIOptions)\n    except SystemExit as e:\n        if e.code != 0:\n            print(e.args[0], file=sys.stderr)\n            input('Press Enter to quit')\n        return\n\n    loop = Loop()\n    handler = Resize(cli_opts)\n    loop.loop(handler)\n    if handler.print_on_fail:\n        print(handler.print_on_fail, file=sys.stderr)\n        input('Press Enter to quit')\n    raise SystemExit(loop.return_code)\n"
  },
  {
    "path": "kittens/runner.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport importlib\nimport os\nimport sys\nfrom collections.abc import Callable, Generator\nfrom contextlib import contextmanager\nfrom functools import partial\nfrom typing import TYPE_CHECKING, Any, NamedTuple, cast\n\nfrom kitty.constants import list_kitty_resources\nfrom kitty.types import run_once\nfrom kitty.typing_compat import BossType, WindowType\nfrom kitty.utils import resolve_abs_or_config_path\n\naliases = {'url_hints': 'hints'}\nif TYPE_CHECKING:\n    from kitty.conf.types import Definition\nelse:\n    Definition = object\n\n\ndef resolved_kitten(k: str) -> str:\n    ans = aliases.get(k, k)\n    head, tail = os.path.split(ans)\n    tail = tail.replace('-', '_')\n    return os.path.join(head, tail)\n\n\ndef path_to_custom_kitten(config_dir: str, kitten: str) -> str:\n    path = resolve_abs_or_config_path(kitten, conf_dir=config_dir)\n    return os.path.abspath(path)\n\n\n@contextmanager\ndef preserve_sys_path() -> Generator[None, None, None]:\n    orig = sys.path[:]\n    try:\n        yield\n    finally:\n        if sys.path != orig:\n            del sys.path[:]\n            sys.path.extend(orig)\n\n\nclass CLIOnlyKitten(TypeError):\n    def __init__(self, kitten: str):\n        super().__init__(f'The {kitten} kitten must be run only at the commandline, as: kitten {kitten}')\n\n\ndef import_kitten_main_module(config_dir: str, kitten: str) -> dict[str, Any]:\n    if kitten.endswith('.py'):\n        with preserve_sys_path():\n            path = path_to_custom_kitten(config_dir, kitten)\n            if os.path.dirname(path):\n                sys.path.insert(0, os.path.dirname(path))\n            with open(path) as f:\n                src = f.read()\n            code = compile(src, path, 'exec')\n            g = {'__name__': 'kitten'}\n            exec(code, g)\n            hr = g.get('handle_result', lambda *a, **kw: None)\n        return {'start': g['main'], 'end': hr}\n\n    kitten = resolved_kitten(kitten)\n    m = importlib.import_module(f'kittens.{kitten}.main')\n    if not hasattr(m, 'main'):\n        raise CLIOnlyKitten(kitten)\n    return {\n        'start': getattr(m, 'main'),\n        'end': getattr(m, 'handle_result', lambda *a, **k: None),\n    }\n\n\nclass KittenMetadata(NamedTuple):\n    handle_result: Callable[[Any, int, BossType], None] = lambda *a: None\n\n    type_of_input: str | None = None\n    no_ui: bool = False\n    has_ready_notification: bool = False\n    open_url_handler: Callable[[BossType, WindowType, str, int, str], bool] | None = None\n    allow_remote_control: bool = False\n    remote_control_password: str | bool = False\n\n\ndef create_kitten_handler(kitten: str, orig_args: list[str]) -> KittenMetadata:\n    from kitty.constants import config_dir\n    m = import_kitten_main_module(config_dir, kitten)\n    kitten = resolved_kitten(kitten)\n    main = m['start']\n    handle_result = m['end']\n    return KittenMetadata(\n        handle_result=partial(handle_result, [kitten] + orig_args),\n        type_of_input=getattr(handle_result, 'type_of_input', None),\n        no_ui=getattr(handle_result, 'no_ui', False),\n        allow_remote_control=getattr(main, 'allow_remote_control', False),\n        remote_control_password=getattr(main, 'remote_control_password', True),\n        has_ready_notification=getattr(handle_result, 'has_ready_notification', False),\n        open_url_handler=getattr(handle_result, 'open_url_handler', None))\n\n\ndef set_debug(kitten: str) -> None:\n    import builtins\n\n    from kittens.tui.loop import debug\n    setattr(builtins, 'debug', debug)\n\n\ndef launch(args: list[str]) -> None:\n    config_dir, kitten = args[:2]\n    original_kitten_name = kitten\n    kitten = resolved_kitten(kitten)\n    del args[:2]\n    args = [kitten] + args\n    os.environ['KITTY_CONFIG_DIRECTORY'] = config_dir\n    set_debug(kitten)\n    m = import_kitten_main_module(config_dir, original_kitten_name)\n    try:\n        result = m['start'](args)\n    finally:\n        sys.stdin = sys.__stdin__\n    if result is not None:\n        import base64\n        import json\n        data = base64.b85encode(json.dumps(result).encode('utf-8'))\n        sys.stdout.buffer.write(b'\\x1bP@kitty-kitten-result|')\n        sys.stdout.buffer.write(data)\n        sys.stdout.buffer.write(b'\\x1b\\\\')\n    sys.stderr.flush()\n    sys.stdout.flush()\n\n\ndef run_kitten(kitten: str, run_name: str = '__main__') -> None:\n    import runpy\n    original_kitten_name = kitten\n    kitten = resolved_kitten(kitten)\n    set_debug(kitten)\n    if kitten in all_kitten_names():\n        runpy.run_module(f'kittens.{kitten}.main', run_name=run_name)\n        return\n    kitten = original_kitten_name\n    # Look for a custom kitten\n    if not kitten.endswith('.py'):\n        kitten += '.py'\n    from kitty.constants import config_dir\n    path = path_to_custom_kitten(config_dir, kitten)\n    if not os.path.exists(path):\n        path = path_to_custom_kitten(config_dir, resolved_kitten(kitten))\n    if not os.path.exists(path):\n        print('Available builtin kittens:', file=sys.stderr)\n        for kitten in all_kitten_names():\n            print(kitten, file=sys.stderr)\n        raise SystemExit(f'No kitten named {original_kitten_name}')\n    m = runpy.run_path(path, init_globals={'sys': sys, 'os': os}, run_name='__run_kitten__')\n    from kitty.fast_data_types import set_options\n    try:\n        m['main'](sys.argv)\n    finally:\n        set_options(None)\n\n\n@run_once\ndef all_kitten_names() -> frozenset[str]:\n    ans = []\n    for name in list_kitty_resources('kittens'):\n        if '__' not in name and '.' not in name and name != 'tui':\n            ans.append(name)\n    return frozenset(ans)\n\n\ndef list_kittens() -> None:\n    print('You must specify the name of a kitten to run')\n    print('Choose from:')\n    print()\n    for kitten in all_kitten_names():\n        print(kitten)\n\n\ndef get_kitten_cli_docs(kitten: str) -> Any:\n    setattr(sys, 'cli_docs', {})\n    run_kitten(kitten, run_name='__doc__')\n    ans = getattr(sys, 'cli_docs')\n    delattr(sys, 'cli_docs')\n    if 'help_text' in ans and 'usage' in ans and 'options' in ans:\n        return ans\n\n\ndef get_kitten_wrapper_of(kitten: str) -> str:\n    setattr(sys, 'cli_docs', {})\n    run_kitten(kitten, run_name='__wrapper_of__')\n    ans = getattr(sys, 'cli_docs')\n    delattr(sys, 'cli_docs')\n    return ans.get('wrapper_of') or ''\n\n\ndef get_kitten_completer(kitten: str) -> Any:\n    run_kitten(kitten, run_name='__completer__')\n    ans = getattr(sys, 'kitten_completer', None)\n    if ans is not None:\n        delattr(sys, 'kitten_completer')\n    return ans\n\n\ndef get_kitten_conf_docs(kitten: str) -> Definition | None:\n    setattr(sys, 'options_definition', None)\n    run_kitten(kitten, run_name='__conf__')\n    ans = getattr(sys, 'options_definition')\n    delattr(sys, 'options_definition')\n    return cast(Definition, ans)\n\n\ndef get_kitten_extra_cli_parsers(kitten: str) -> dict[str,str]:\n    setattr(sys, 'extra_cli_parsers', {})\n    run_kitten(kitten, run_name='__extra_cli_parsers__')\n    ans = getattr(sys, 'extra_cli_parsers')\n    delattr(sys, 'extra_cli_parsers')\n    return cast(dict[str, str], ans)\n\n\ndef main() -> None:\n    try:\n        args = sys.argv[1:]\n        launch(args)\n    except Exception:\n        print('Unhandled exception running kitten:')\n        import traceback\n        traceback.print_exc()\n        input('Press Enter to quit')\n"
  },
  {
    "path": "kittens/show_key/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/show_key/kitty.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage show_key\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n)\n\nvar _ = fmt.Print\n\nfunc csi(csi string) string {\n\treturn \"CSI \" + strings.NewReplacer(\":\", \" : \", \";\", \" ; \").Replace(csi[:len(csi)-1]) + \" \" + csi[len(csi)-1:]\n}\n\nfunc run_kitty_loop(_ *Options) (err error) {\n\tlp, err := loop.New(loop.FullKeyboardProtocol)\n\tif err != nil {\n\t\treturn err\n\t}\n\tctx := markup.New(true)\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.SetCursorVisible(false)\n\t\tlp.SetWindowTitle(\"kitty extended keyboard protocol demo\")\n\t\tlp.Println(\"Press any keys - Ctrl+C or Ctrl+D will terminate\")\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnKeyEvent = func(e *loop.KeyEvent) (err error) {\n\t\te.Handled = true\n\t\tif e.MatchesPressOrRepeat(\"ctrl+c\") || e.MatchesPressOrRepeat(\"ctrl+d\") {\n\t\t\tlp.Quit(0)\n\t\t\treturn\n\t\t}\n\t\tmods := e.Mods.String()\n\t\tif mods != \"\" {\n\t\t\tmods += \"+\"\n\t\t}\n\t\tetype := e.Type.String()\n\t\tkey := e.Key\n\t\tif key == \" \" {\n\t\t\tkey = \"space\"\n\t\t}\n\t\tkey = mods + key\n\t\tlp.Printf(\"%s %s %s\\r\\n\", ctx.Green(key), ctx.Yellow(etype), e.Text)\n\t\tlp.Println(ctx.Cyan(csi(e.CSI)))\n\t\tif e.AlternateKey != \"\" || e.ShiftedKey != \"\" {\n\t\t\tif e.ShiftedKey != \"\" {\n\t\t\t\tlp.QueueWriteString(ctx.Dim(\"Shifted key: \"))\n\t\t\t\tlp.QueueWriteString(e.ShiftedKey + \" \")\n\t\t\t}\n\t\t\tif e.AlternateKey != \"\" {\n\t\t\t\tlp.QueueWriteString(ctx.Dim(\"Alternate key: \"))\n\t\t\t\tlp.QueueWriteString(e.AlternateKey + \" \")\n\t\t\t}\n\t\t\tlp.Println()\n\t\t}\n\t\tlp.Println()\n\t\treturn\n\t}\n\tlp.OnText = func(text string, from_key_event bool, in_bracketed_paste bool) error {\n\t\tif from_key_event {\n\t\t\treturn nil\n\t\t}\n\t\tlp.Printf(\"%s: %s\\n\\n\", ctx.Green(\"Text\"), text)\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "kittens/show_key/legacy.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage show_key\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"io\"\n\t\"os\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc print_key(buf []byte, ctx *markup.Context) {\n\tconst ctrl_keys = \"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_\"\n\tunix := \"\"\n\tsend_text := \"\"\n\tfor _, ch := range buf {\n\t\tswitch {\n\t\tcase int(ch) < len(ctrl_keys):\n\t\t\tunix += \"^\" + ctrl_keys[ch:ch+1]\n\t\tcase ch == 127:\n\t\t\tunix += \"^?\"\n\t\tdefault:\n\t\t\tunix += string(rune(ch))\n\t\t}\n\t}\n\tfor _, ch := range string(buf) {\n\t\tq := fmt.Sprintf(\"%#v\", string(ch))\n\t\tsend_text += q[1 : len(q)-1]\n\t}\n\tos.Stdout.WriteString(unix + \"\\t\\t\")\n\tos.Stdout.WriteString(ctx.Yellow(send_text) + \"\\r\\n\")\n}\n\nfunc run_legacy_loop(opts *Options) (err error) {\n\tterm, err := tty.OpenControllingTerm(tty.SetRaw)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tterm.RestoreAndClose()\n\t}()\n\tif opts.KeyMode != \"unchanged\" {\n\t\tos.Stdout.WriteString(\"\\x1b[?1\")\n\t\tswitch opts.KeyMode {\n\t\tcase \"normal\":\n\t\t\tos.Stdout.WriteString(\"l\")\n\t\tdefault:\n\t\t\tos.Stdout.WriteString(\"h\")\n\t\t}\n\t\tdefer func() {\n\t\t\tos.Stdout.WriteString(\"\\x1b[?1l\")\n\t\t}()\n\t}\n\tfmt.Print(\"Press any keys - Ctrl+D will terminate this program\\r\\n\")\n\tctx := markup.New(true)\n\tfmt.Print(ctx.Green(\"UNIX\\t\\tsend_text\\r\\n\"))\n\tbuf := make([]byte, 64)\n\tfor {\n\t\tn, err := term.Read(buf)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif !(errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EBUSY)) {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif n > 0 {\n\t\t\tprint_key(buf[:n], ctx)\n\t\t\tif n == 1 && buf[0] == 4 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "kittens/show_key/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage show_key\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n)\n\nvar _ = fmt.Print\n\nfunc main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tif opts.KeyMode == \"kitty\" {\n\t\terr = run_kitty_loop(opts)\n\t} else {\n\t\terr = run_legacy_loop(opts)\n\t}\n\tif err != nil {\n\t\trc = 1\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/show_key/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport sys\n\nOPTIONS = r'''\n--key-mode -m\ndefault=normal\ntype=choices\nchoices=normal,application,kitty,unchanged\nThe keyboard mode to use when showing keys. :code:`normal` mode is with DECCKM\nreset and :code:`application` mode is with DECCKM set. :code:`kitty` is the full\nkitty extended keyboard protocol.\n'''.format\nhelp_text = 'Show the codes generated by the terminal for key presses in various keyboard modes'\nusage = ''\n\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('This should be run as kitten show_key')\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = help_text\n"
  },
  {
    "path": "kittens/ssh/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/ssh/askpass.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ssh\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/sha1\"\n\t\"encoding/base32\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n)\n\nvar _ = fmt.Print\n\nfunc fatal(err error) int {\n\tcli.ShowError(err)\n\treturn 1\n}\n\nfunc trigger_ask(name string) int {\n\tterm, err := tty.OpenControllingTerm()\n\tif err != nil {\n\t\treturn fatal(err)\n\t}\n\tdefer term.Close()\n\t_, err = term.WriteString(\"\\x1bP@kitty-ask|\" + name + \"\\x1b\\\\\")\n\tif err != nil {\n\t\treturn fatal(err)\n\t}\n\treturn 0\n}\n\nfunc isPasswordPrompt(msg string) bool {\n\tq := strings.ToLower(msg)\n\tif strings.Contains(q, \"passphrase\") {\n\t\treturn false\n\t}\n\treturn strings.Contains(q, \"password\")\n}\n\nfunc isOTPPrompt(msg string) bool {\n\tq := strings.ToLower(msg)\n\tif strings.Contains(q, \"passphrase\") {\n\t\treturn false\n\t}\n\tif strings.Contains(q, \"verification code\") || strings.Contains(q, \"one-time password\") || strings.Contains(q, \"one time password\") || strings.Contains(q, \"authenticator code\") || strings.Contains(q, \"authentication code\") || strings.Contains(q, \"two-factor\") || strings.Contains(q, \"2fa\") || strings.Contains(q, \"otp\") || strings.Contains(q, \"passcode\") {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc generateTOTP(secret string, digits, period int64, t time.Time) (string, error) {\n\ts := strings.ToUpper(strings.TrimSpace(secret))\n\ts = strings.ReplaceAll(s, \" \", \"\")\n\tkey, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(s)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid TOTP secret: %w\", err)\n\t}\n\tcounter := uint64(t.Unix() / period)\n\tvar buf [8]byte\n\tbinary.BigEndian.PutUint64(buf[:], counter)\n\tmac := hmac.New(sha1.New, key)\n\t_, _ = mac.Write(buf[:])\n\tsum := mac.Sum(nil)\n\toff := sum[len(sum)-1] & 0x0f\n\tcode := (uint32(sum[off])&0x7f)<<24 | (uint32(sum[off+1])&0xff)<<16 | (uint32(sum[off+2])&0xff)<<8 | (uint32(sum[off+3]) & 0xff)\n\tmod := uint32(1)\n\tfor range digits {\n\t\tmod *= 10\n\t}\n\tval := code % mod\n\tfmtstr := fmt.Sprintf(\"%%0%dd\", digits)\n\treturn fmt.Sprintf(fmtstr, val), nil\n}\n\nfunc RunSSHAskpass() int {\n\tmsg := os.Args[len(os.Args)-1]\n\tprompt := os.Getenv(\"SSH_ASKPASS_PROMPT\")\n\tis_confirm := prompt == \"confirm\"\n\tq_type := \"get_line\"\n\tif is_confirm {\n\t\tq_type = \"confirm\"\n\t}\n\tis_fingerprint_check := strings.Contains(msg, \"(yes/no/[fingerprint])\")\n\n\t// Auto-fill from ssh.conf if configured\n\tif !is_confirm && !is_fingerprint_check {\n\t\thost := os.Getenv(\"KITTY_SSH_ASKPASS_HOST\")\n\t\tuser := os.Getenv(\"KITTY_SSH_ASKPASS_USER\")\n\t\tif host != \"\" {\n\t\t\tvar overrides []string\n\t\t\t_ = json.Unmarshal([]byte(os.Getenv(\"KITTY_SSH_ASKPASS_OVERRIDES\")), &overrides)\n\t\t\tif cfg, _, err := load_config(host, user, overrides); err == nil && cfg != nil {\n\t\t\t\tif err = resolve_secrets(cfg, false); err != nil {\n\t\t\t\t\treturn fatal(err)\n\t\t\t\t}\n\t\t\t\t// Password autofill\n\t\t\t\tif isPasswordPrompt(msg) && cfg.Password != \"\" {\n\t\t\t\t\tfmt.Println(cfg.Password)\n\t\t\t\t\treturn 0\n\t\t\t\t}\n\t\t\t\t// OTP autofill\n\t\t\t\tif isOTPPrompt(msg) && cfg.Totp_secret != \"\" {\n\t\t\t\t\tcode, err := generateTOTP(cfg.Totp_secret, int64(cfg.Totp_digits), int64(cfg.Totp_period), time.Now())\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tfmt.Println(code)\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tq := map[string]any{\n\t\t\"message\":     msg,\n\t\t\"type\":        q_type,\n\t\t\"is_password\": !is_fingerprint_check,\n\t}\n\tdata, err := json.Marshal(q)\n\tif err != nil {\n\t\treturn fatal(err)\n\t}\n\tdata_shm, err := shm.CreateTemp(\"askpass-*\", uint64(len(data)+32))\n\tif err != nil {\n\t\treturn fatal(fmt.Errorf(\"Failed to create SHM file with error: %w\", err))\n\t}\n\tdefer data_shm.Close()\n\tdefer func() { _ = data_shm.Unlink() }()\n\n\tdata_shm.Slice()[0] = 0\n\tif err = shm.WriteWithSize(data_shm, data, 1); err != nil {\n\t\treturn fatal(fmt.Errorf(\"Failed to write to SHM file with error: %w\", err))\n\t}\n\tif err = data_shm.Flush(); err != nil {\n\t\treturn fatal(fmt.Errorf(\"Failed to flush SHM file with error: %w\", err))\n\t}\n\ttrigger_ask(data_shm.Name())\n\tfor {\n\t\ttime.Sleep(50 * time.Millisecond)\n\t\tif data_shm.Slice()[0] == 1 {\n\t\t\tbreak\n\t\t}\n\t}\n\tdata, err = shm.ReadWithSize(data_shm, 1)\n\tif err != nil {\n\t\treturn fatal(fmt.Errorf(\"Failed to read from SHM file with error: %w\", err))\n\t}\n\tresponse := \"\"\n\tif is_confirm {\n\t\tvar ok bool\n\t\terr = json.Unmarshal(data, &ok)\n\t\tif err != nil {\n\t\t\treturn fatal(fmt.Errorf(\"Failed to parse response data: %#v with error: %w\", string(data), err))\n\t\t}\n\t\tresponse = \"no\"\n\t\tif ok {\n\t\t\tresponse = \"yes\"\n\t\t}\n\t} else {\n\t\terr = json.Unmarshal(data, &response)\n\t\tif err != nil {\n\t\t\treturn fatal(fmt.Errorf(\"Failed to parse response data: %#v with error: %w\", string(data), err))\n\t\t}\n\t\tif is_fingerprint_check {\n\t\t\tresponse = strings.ToLower(response)\n\t\t\tswitch response {\n\t\t\tcase \"y\":\n\t\t\t\tresponse = \"yes\"\n\t\t\tcase \"n\":\n\t\t\t\tresponse = \"no\"\n\t\t\t}\n\t\t}\n\t}\n\tif response != \"\" {\n\t\tfmt.Println(response)\n\t}\n\treturn 0\n}\n"
  },
  {
    "path": "kittens/ssh/config.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ssh\n\nimport (\n\t\"archive/tar\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/paths\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\n\t\"github.com/bmatcuk/doublestar/v4\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc resolve_secret(key, val string) (string, error) {\n\tv := strings.TrimSpace(val)\n\tif v == \"\" {\n\t\treturn \"\", nil\n\t}\n\tif b, s, ok := strings.Cut(v, \":\"); ok {\n\t\tb = strings.ToLower(strings.TrimSpace(b))\n\t\ts = strings.TrimSpace(s)\n\t\tswitch b {\n\t\tcase \"text\":\n\t\t\treturn s, nil\n\t\tdefault:\n\t\t\treturn \"\", fmt.Errorf(\"Unsupported secret backend %s for %s. Supported backends: text\", b, key)\n\t\t}\n\t}\n\treturn \"\", fmt.Errorf(\"No secret backend specified for: %s\", key)\n}\n\nfunc resolve_secrets(c *Config, only_syntax bool) error {\n\t_ = only_syntax // this will be useful when using backends that require user interaction\n\tif r, err := resolve_secret(\"password\", c.Password); err != nil {\n\t\treturn err\n\t} else {\n\t\tc.Password = r\n\t}\n\tif r, err := resolve_secret(\"totp_secret\", c.Totp_secret); err != nil {\n\t\treturn err\n\t} else {\n\t\tc.Totp_secret = r\n\t}\n\treturn nil\n}\n\ntype EnvInstruction struct {\n\tkey, val                                         string\n\tdelete_on_remote, copy_from_local, literal_quote bool\n}\n\nfunc quote_for_sh(val string, literal_quote bool) string {\n\tif literal_quote {\n\t\treturn utils.QuoteStringForSH(val)\n\t}\n\t// See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html\n\tb := strings.Builder{}\n\tb.Grow(len(val) + 16)\n\tb.WriteRune('\"')\n\trunes := []rune(val)\n\tfor i, ch := range runes {\n\t\tif ch == '\\\\' || ch == '`' || ch == '\"' || (ch == '$' && i+1 < len(runes) && runes[i+1] == '(') {\n\t\t\t// special chars are escaped\n\t\t\t// $( is escaped to prevent execution\n\t\t\tb.WriteRune('\\\\')\n\t\t}\n\t\tb.WriteRune(ch)\n\t}\n\tb.WriteRune('\"')\n\treturn b.String()\n}\n\nfunc (self *EnvInstruction) Serialize(for_python bool, get_local_env func(string) (string, bool)) string {\n\tvar unset func() string\n\tvar export func(string) string\n\tif for_python {\n\t\tdumps := func(x ...any) string {\n\t\t\tans, _ := json.Marshal(x)\n\t\t\treturn utils.UnsafeBytesToString(ans)\n\t\t}\n\t\texport = func(val string) string {\n\t\t\tif val == \"\" {\n\t\t\t\treturn fmt.Sprintf(\"export %s\", dumps(self.key))\n\t\t\t}\n\t\t\treturn fmt.Sprintf(\"export %s\", dumps(self.key, val, self.literal_quote))\n\t\t}\n\t\tunset = func() string {\n\t\t\treturn fmt.Sprintf(\"unset %s\", dumps(self.key))\n\t\t}\n\t} else {\n\t\tkq := utils.QuoteStringForSH(self.key)\n\t\tunset = func() string {\n\t\t\treturn fmt.Sprintf(\"unset %s\", kq)\n\t\t}\n\t\texport = func(val string) string {\n\t\t\treturn fmt.Sprintf(\"export %s=%s\", kq, quote_for_sh(val, self.literal_quote))\n\t\t}\n\t}\n\tif self.delete_on_remote {\n\t\treturn unset()\n\t}\n\tif self.copy_from_local {\n\t\tval, found := get_local_env(self.key)\n\t\tif !found {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn export(val)\n\t}\n\treturn export(self.val)\n}\n\nfunc final_env_instructions(for_python bool, get_local_env func(string) (string, bool), env ...*EnvInstruction) string {\n\tseen := make(map[string]int, len(env))\n\tans := make([]string, 0, len(env))\n\tfor _, ei := range env {\n\t\tq := ei.Serialize(for_python, get_local_env)\n\t\tif q != \"\" {\n\t\t\tif pos, found := seen[ei.key]; found {\n\t\t\t\tans[pos] = q\n\t\t\t} else {\n\t\t\t\tseen[ei.key] = len(ans)\n\t\t\t\tans = append(ans, q)\n\t\t\t}\n\t\t}\n\t}\n\treturn strings.Join(ans, \"\\n\")\n}\n\ntype CopyInstruction struct {\n\tlocal_path, arcname string\n\texclude_patterns    []string\n}\n\nfunc ParseEnvInstruction(spec string) (ans []*EnvInstruction, err error) {\n\tconst COPY_FROM_LOCAL string = \"_kitty_copy_env_var_\"\n\tei := &EnvInstruction{}\n\tfound := false\n\tei.key, ei.val, found = strings.Cut(spec, \"=\")\n\tei.key = strings.TrimSpace(ei.key)\n\tif found {\n\t\tei.val = strings.TrimSpace(ei.val)\n\t\tif ei.val == COPY_FROM_LOCAL {\n\t\t\tei.val = \"\"\n\t\t\tei.copy_from_local = true\n\t\t}\n\t} else {\n\t\tei.delete_on_remote = true\n\t}\n\tif ei.key == \"\" {\n\t\terr = fmt.Errorf(\"The env directive must not be empty\")\n\t}\n\tans = []*EnvInstruction{ei}\n\treturn\n}\n\nvar paths_ctx *paths.Ctx\n\nfunc resolve_file_spec(spec string, is_glob bool) ([]string, error) {\n\tif paths_ctx == nil {\n\t\tpaths_ctx = &paths.Ctx{}\n\t}\n\tans := os.ExpandEnv(paths_ctx.ExpandHome(spec))\n\tif !filepath.IsAbs(ans) {\n\t\tans = paths_ctx.AbspathFromHome(ans)\n\t}\n\tif is_glob {\n\t\tfiles, err := doublestar.FilepathGlob(ans)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s is not a valid glob pattern with error: %w\", spec, err)\n\t\t}\n\t\tif len(files) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"%s matches no files\", spec)\n\t\t}\n\t\treturn files, nil\n\t}\n\terr := unix.Access(ans, unix.R_OK)\n\tif err != nil {\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\treturn nil, fmt.Errorf(\"%s does not exist\", spec)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"Cannot read from: %s with error: %w\", spec, err)\n\t}\n\treturn []string{ans}, nil\n}\n\nfunc get_arcname(loc, dest, home string) (arcname string) {\n\tif dest != \"\" {\n\t\tarcname = dest\n\t} else {\n\t\tarcname = filepath.Clean(loc)\n\t\tif strings.HasPrefix(arcname, home) {\n\t\t\tra, err := filepath.Rel(home, arcname)\n\t\t\tif err == nil {\n\t\t\t\tarcname = ra\n\t\t\t}\n\t\t}\n\t}\n\tprefix := \"home/\"\n\tif strings.HasPrefix(arcname, \"/\") {\n\t\tprefix = \"root\"\n\t}\n\treturn prefix + arcname\n}\n\nfunc ParseCopyInstruction(spec string) (ans []*CopyInstruction, err error) {\n\targs, err := shlex.Split(\"copy \" + spec)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\topts, args, err := parse_copy_args(args)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlocations := make([]string, 0, len(args))\n\tfor _, arg := range args {\n\t\tlocs, err := resolve_file_spec(arg, opts.Glob)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlocations = append(locations, locs...)\n\t}\n\tif len(locations) == 0 {\n\t\treturn nil, fmt.Errorf(\"No files to copy specified\")\n\t}\n\tif len(locations) > 1 && opts.Dest != \"\" {\n\t\treturn nil, fmt.Errorf(\"Specifying a remote location with more than one file is not supported\")\n\t}\n\thome := paths_ctx.HomePath()\n\tans = make([]*CopyInstruction, 0, len(locations))\n\tfor _, loc := range locations {\n\t\tci := CopyInstruction{local_path: loc, exclude_patterns: opts.Exclude}\n\t\tif opts.SymlinkStrategy != \"preserve\" {\n\t\t\tci.local_path, err = filepath.EvalSymlinks(loc)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Failed to resolve symlinks in %#v with error: %w\", loc, err)\n\t\t\t}\n\t\t}\n\t\tif opts.SymlinkStrategy == \"resolve\" {\n\t\t\tci.arcname = get_arcname(ci.local_path, opts.Dest, home)\n\t\t} else {\n\t\t\tci.arcname = get_arcname(loc, opts.Dest, home)\n\t\t}\n\t\tans = append(ans, &ci)\n\t}\n\treturn\n}\n\ntype file_unique_id struct {\n\tdev, inode uint64\n}\n\nfunc excluded(pattern, path string) bool {\n\tif !strings.ContainsRune(pattern, '/') {\n\t\tpath = filepath.Base(path)\n\t}\n\tif matched, err := doublestar.PathMatch(pattern, path); matched && err == nil {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc get_file_data(callback func(h *tar.Header, data []byte) error, seen map[file_unique_id]string, local_path, arcname string, exclude_patterns []string) error {\n\tvar s unix.Stat_t\n\tif err := unix.Lstat(local_path, &s); err != nil {\n\t\treturn err\n\t}\n\tcb := func(h *tar.Header, data []byte, arcname string) error {\n\t\th.Name = arcname\n\t\tif h.Typeflag == tar.TypeDir {\n\t\t\th.Name = strings.TrimRight(h.Name, \"/\") + \"/\"\n\t\t}\n\t\th.Size = int64(len(data))\n\t\th.Mode = int64(s.Mode & 0777) // discard the setuid, setgid and sticky bits\n\t\th.ModTime = time.Unix(s.Mtim.Unix())\n\t\th.AccessTime = time.Unix(s.Atim.Unix())\n\t\th.ChangeTime = time.Unix(s.Ctim.Unix())\n\t\th.Format = tar.FormatPAX\n\t\treturn callback(h, data)\n\t}\n\t// we only copy regular files, directories and symlinks\n\tswitch s.Mode & unix.S_IFMT {\n\tcase unix.S_IFBLK, unix.S_IFIFO, unix.S_IFCHR, unix.S_IFSOCK: // ignored\n\tcase unix.S_IFLNK: // symlink\n\t\ttarget, err := os.Readlink(local_path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = cb(&tar.Header{\n\t\t\tTypeflag: tar.TypeSymlink,\n\t\t\tLinkname: target,\n\t\t}, nil, arcname)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase unix.S_IFDIR: // directory\n\t\tlocal_path = filepath.Clean(local_path)\n\t\ttype entry struct {\n\t\t\tpath, arcname string\n\t\t}\n\t\tstack := []entry{{local_path, arcname}}\n\t\tfor len(stack) > 0 {\n\t\t\tx := stack[0]\n\t\t\tstack = stack[1:]\n\t\t\tentries, err := os.ReadDir(x.path)\n\t\t\tif err != nil {\n\t\t\t\tif x.path == local_path {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\terr = cb(&tar.Header{Typeflag: tar.TypeDir}, nil, x.arcname)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, e := range entries {\n\t\t\t\tentry_path := filepath.Join(x.path, e.Name())\n\t\t\t\taname := path.Join(x.arcname, e.Name())\n\t\t\t\tok := true\n\t\t\t\tfor _, pat := range exclude_patterns {\n\t\t\t\t\tif excluded(pat, entry_path) {\n\t\t\t\t\t\tok = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif e.IsDir() {\n\t\t\t\t\tstack = append(stack, entry{entry_path, aname})\n\t\t\t\t} else {\n\t\t\t\t\terr = get_file_data(callback, seen, entry_path, aname, exclude_patterns)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tcase unix.S_IFREG: // Regular file\n\t\tfid := file_unique_id{dev: uint64(s.Dev), inode: uint64(s.Ino)}\n\t\tif prev, ok := seen[fid]; ok { // Hard link\n\t\t\treturn cb(&tar.Header{Typeflag: tar.TypeLink, Linkname: prev}, nil, arcname)\n\t\t}\n\t\tseen[fid] = arcname\n\t\tdata, err := os.ReadFile(local_path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = cb(&tar.Header{Typeflag: tar.TypeReg}, data, arcname)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (ci *CopyInstruction) get_file_data(callback func(h *tar.Header, data []byte) error, seen map[file_unique_id]string) (err error) {\n\tep := ci.exclude_patterns\n\tfor _, folder_name := range []string{\"__pycache__\", \".DS_Store\"} {\n\t\tep = append(ep, \"**/\"+folder_name, \"**/\"+folder_name+\"/**\")\n\t}\n\treturn get_file_data(callback, seen, ci.local_path, ci.arcname, ep)\n}\n\ntype ConfigSet struct {\n\tall_configs []*Config\n}\n\nfunc config_for_hostname(hostname_to_match, username_to_match string, cs *ConfigSet) *Config {\n\tmatcher := func(q *Config) bool {\n\t\tfor pat := range strings.SplitSeq(q.Hostname, \" \") {\n\t\t\tupat := \"*\"\n\t\t\tif strings.Contains(pat, \"@\") {\n\t\t\t\tupat, pat, _ = strings.Cut(pat, \"@\")\n\t\t\t}\n\t\t\tvar host_matched, user_matched bool\n\t\t\tif matched, err := filepath.Match(pat, hostname_to_match); matched && err == nil {\n\t\t\t\thost_matched = true\n\t\t\t}\n\t\t\tif matched, err := filepath.Match(upat, username_to_match); matched && err == nil {\n\t\t\t\tuser_matched = true\n\t\t\t}\n\t\t\tif host_matched && user_matched {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\tfor _, c := range utils.Reversed(cs.all_configs) {\n\t\tif matcher(c) {\n\t\t\treturn c\n\t\t}\n\t}\n\treturn cs.all_configs[0]\n}\n\nfunc (self *ConfigSet) line_handler(key, val string) error {\n\tc := self.all_configs[len(self.all_configs)-1]\n\tif key == \"hostname\" {\n\t\tc = NewConfig()\n\t\tself.all_configs = append(self.all_configs, c)\n\t}\n\treturn c.Parse(key, val)\n}\n\nfunc load_config(hostname_to_match string, username_to_match string, overrides []string, paths ...string) (*Config, []config.ConfigLine, error) {\n\tans := &ConfigSet{all_configs: []*Config{NewConfig()}}\n\tp := config.ConfigParser{LineHandler: ans.line_handler}\n\terr := p.LoadConfig(\"ssh.conf\", paths, nil)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tfinal_conf := config_for_hostname(hostname_to_match, username_to_match, ans)\n\tbad_lines := p.BadLines()\n\tif len(overrides) > 0 {\n\t\th := final_conf.Hostname\n\t\toverride_parser := config.ConfigParser{LineHandler: final_conf.Parse}\n\t\tif err = override_parser.ParseOverrides(overrides...); err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tbad_lines = append(bad_lines, override_parser.BadLines()...)\n\t\tfinal_conf.Hostname = h\n\t}\n\treturn final_conf, bad_lines, nil\n}\n"
  },
  {
    "path": "kittens/ssh/config_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ssh\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\ntype Pair struct {\n\tInput, Uname, Host string\n}\n\nfunc TestSSHConfigParsing(t *testing.T) {\n\ttdir := t.TempDir()\n\thostname := \"unmatched\"\n\tusername := \"\"\n\tconf := \"\"\n\toverrides := []string{}\n\tfor_python := false\n\tcf := filepath.Join(tdir, \"ssh.conf\")\n\trt := func(expected_env ...string) {\n\t\tif err := os.WriteFile(cf, []byte(conf), 0o600); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tc, bad_lines, err := load_config(hostname, username, overrides, cf)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif len(bad_lines) != 0 {\n\t\t\tt.Fatalf(\"Bad config line: %s with error: %s\", bad_lines[0].Line, bad_lines[0].Err)\n\t\t}\n\t\tactual := final_env_instructions(for_python, func(key string) (string, bool) {\n\t\t\tif key == \"LOCAL_ENV\" {\n\t\t\t\treturn \"LOCAL_VAL\", true\n\t\t\t}\n\t\t\treturn \"\", false\n\t\t}, c.Env...)\n\t\tif expected_env == nil {\n\t\t\texpected_env = []string{}\n\t\t}\n\t\tdiff := cmp.Diff(expected_env, utils.Splitlines(actual))\n\t\tif diff != \"\" {\n\t\t\tt.Fatalf(\"Unexpected env for\\nhostname: %#v\\nusername: %#v\\nconf: %s\\n%s\", hostname, username, conf, diff)\n\t\t}\n\t}\n\trt()\n\tconf = \"env a=b\"\n\trt(`export 'a'=\"b\"`)\n\tconf = \"env a=b\"\n\toverrides = []string{\"env=c=d\"}\n\trt(`export 'a'=\"b\"`, `export 'c'=\"d\"`)\n\toverrides = nil\n\n\tconf = \"env a=\\\\\"\n\trt(`export 'a'=\"\\\\\"`)\n\tconf = `env\n\t\t\\ a=\n\t\t\\\\`\n\tconf = \"env\\n \\t \\\\ a=\\n\\\\\\\\\"\n\trt(`export 'a'=\"\\\\\"`)\n\tconf = `\n\t\te\n\t\t\\n\n\t\t\\v\n\t\t\\ a\n\t\t\\=\n\t\t\\\\\n\t\t\\`\n\trt(`export 'a'=\"\\\\\"`)\n\n\tconf = \"env a=b\\nhostname 2\\nenv a=c\\nenv b=b\"\n\trt(`export 'a'=\"b\"`)\n\thostname = \"2\"\n\trt(`export 'a'=\"c\"`, `export 'b'=\"b\"`)\n\tconf = \"env a=\"\n\trt(`export 'a'=\"\"`)\n\tconf = \"env a\"\n\trt(`unset 'a'`)\n\tconf = \"env a=b\\nhostname test@2\\nenv a=c\\nenv b=b\"\n\thostname = \"unmatched\"\n\trt(`export 'a'=\"b\"`)\n\thostname = \"2\"\n\trt(`export 'a'=\"b\"`)\n\tusername = \"test\"\n\trt(`export 'a'=\"c\"`, `export 'b'=\"b\"`)\n\tconf = \"env a=b\\nhostname 1 2\\nenv a=c\\nenv b=b\"\n\tusername = \"\"\n\thostname = \"unmatched\"\n\trt(`export 'a'=\"b\"`)\n\thostname = \"1\"\n\trt(`export 'a'=\"c\"`, `export 'b'=\"b\"`)\n\thostname = \"2\"\n\trt(`export 'a'=\"c\"`, `export 'b'=\"b\"`)\n\tfor_python = true\n\trt(`export [\"a\",\"c\",false]`, `export [\"b\",\"b\",false]`)\n\tconf = \"env a=\"\n\trt(`export [\"a\"]`)\n\tconf = \"env a\"\n\trt(`unset [\"a\"]`)\n\tconf = \"env LOCAL_ENV=_kitty_copy_env_var_\"\n\trt(`export [\"LOCAL_ENV\",\"LOCAL_VAL\",false]`)\n\tconf = \"env a=b\\nhostname 2\\ncolor_scheme xyz\"\n\thostname = \"2\"\n\trt()\n\n\tci, err := ParseCopyInstruction(\"--exclude moose --exclude second --dest=target \" + cf)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdiff := cmp.Diff(\"home/target\", ci[0].arcname)\n\tif diff != \"\" {\n\t\tt.Fatalf(\"Incorrect arcname:\\n%s\", diff)\n\t}\n\tdiff = cmp.Diff(cf, ci[0].local_path)\n\tif diff != \"\" {\n\t\tt.Fatalf(\"Incorrect local_path:\\n%s\", diff)\n\t}\n\tdiff = cmp.Diff([]string{\"moose\", \"second\"}, ci[0].exclude_patterns)\n\tif diff != \"\" {\n\t\tt.Fatalf(\"Incorrect excludes:\\n%s\", diff)\n\t}\n\tci, err = ParseCopyInstruction(\"--glob \" + filepath.Join(filepath.Dir(cf), \"*.conf\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdiff = cmp.Diff(cf, ci[0].local_path)\n\tif diff != \"\" {\n\t\tt.Fatalf(\"Incorrect local_path:\\n%s\", diff)\n\t}\n\tif len(ci) != 1 {\n\t\tt.Fatal(ci)\n\t}\n\n\tu, _ := user.Current()\n\tun := u.Username\n\tfor _, x := range []Pair{\n\t\t{\"localhost:12\", un, \"localhost:12\"},\n\t\t{\"@localhost\", un, \"@localhost\"},\n\t\t{\"ssh://@localhost:33\", un, \"localhost\"},\n\t\t{\"me@localhost\", \"me\", \"localhost\"},\n\t\t{\"ssh://me@localhost:12/something?else=1\", \"me\", \"localhost\"},\n\t} {\n\t\tue, uh := get_destination(x.Input)\n\t\tq := Pair{x.Input, ue, uh}\n\t\tif diff := cmp.Diff(x, q); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed: %s\", diff)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "kittens/ssh/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ssh\n\nimport (\n\t\"archive/tar\"\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"io\"\n\t\"io/fs\"\n\t\"maps\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/signal\"\n\t\"os/user\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/themes\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/shell_integration\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/secrets\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc get_destination(hostname string) (username, hostname_for_match string) {\n\tu, err := user.Current()\n\tif err == nil {\n\t\tusername = u.Username\n\t}\n\thostname_for_match = hostname\n\tparsed := false\n\tif strings.HasPrefix(hostname, \"ssh://\") {\n\t\tp, err := url.Parse(hostname)\n\t\tif err == nil {\n\t\t\thostname_for_match = p.Hostname()\n\t\t\tparsed = true\n\t\t\tif p.User.Username() != \"\" {\n\t\t\t\tusername = p.User.Username()\n\t\t\t}\n\t\t}\n\t} else if strings.Contains(hostname, \"@\") && hostname[0] != '@' {\n\t\tusername, hostname_for_match, _ = strings.Cut(hostname, \"@\")\n\t\tparsed = true\n\t}\n\tif !parsed && strings.Contains(hostname, \"@\") && hostname[0] != '@' {\n\t\t_, hostname_for_match, _ = strings.Cut(hostname, \"@\")\n\t}\n\treturn\n}\n\nfunc read_data_from_shared_memory(shm_name string) ([]byte, error) {\n\tdata, err := shm.ReadWithSizeAndUnlink(shm_name, func(s fs.FileInfo) error {\n\t\tif stat, ok := s.Sys().(syscall.Stat_t); ok {\n\t\t\tif os.Getuid() != int(stat.Uid) || os.Getgid() != int(stat.Gid) {\n\t\t\t\treturn fmt.Errorf(\"Incorrect owner on SHM file\")\n\t\t\t}\n\t\t}\n\t\tif s.Mode().Perm() != 0o600 {\n\t\t\treturn fmt.Errorf(\"Incorrect permissions on SHM file\")\n\t\t}\n\t\treturn nil\n\t})\n\treturn data, err\n}\n\nfunc add_cloned_env(val string) (ans map[string]string, err error) {\n\tdata, err := read_data_from_shared_memory(val)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = json.Unmarshal(data, &ans)\n\treturn ans, err\n}\n\nfunc parse_kitten_args(found_extra_args []string, username, hostname_for_match string) (overrides []string, literal_env map[string]string, ferr error) {\n\tliteral_env = make(map[string]string)\n\toverrides = make([]string, 0, 4)\n\tfor i, a := range found_extra_args {\n\t\tif i%2 == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif key, val, found := strings.Cut(a, \"=\"); found {\n\t\t\tif key == \"clone_env\" {\n\t\t\t\tle, err := add_cloned_env(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif !errors.Is(err, fs.ErrNotExist) {\n\t\t\t\t\t\treturn nil, nil, ferr\n\t\t\t\t\t}\n\t\t\t\t} else if le != nil {\n\t\t\t\t\tliteral_env = le\n\t\t\t\t}\n\t\t\t} else if key != \"hostname\" {\n\t\t\t\toverrides = append(overrides, key+\"=\"+val)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc connection_sharing_args(kitty_pid int) ([]string, error) {\n\trd := utils.RuntimeDir()\n\t// Bloody OpenSSH generates a 40 char hash and in creating the socket\n\t// appends a 27 char temp suffix to it. Socket max path length is approx\n\t// ~104 chars. And on idiotic Apple the path length to the runtime dir\n\t// (technically the cache dir since Apple has no runtime dir and thinks it's\n\t// a great idea to delete files in /tmp) is ~48 chars.\n\tif len(rd) > 35 {\n\t\tidiotic_design := fmt.Sprintf(\"/tmp/kssh-rdir-%d\", os.Geteuid())\n\t\tif err := utils.AtomicCreateSymlink(rd, idiotic_design); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\trd = idiotic_design\n\t}\n\tcp := strings.Replace(kitty.SSHControlMasterTemplate, \"{kitty_pid}\", strconv.Itoa(kitty_pid), 1)\n\tcp = strings.Replace(cp, \"{ssh_placeholder}\", \"%C\", 1)\n\treturn []string{\n\t\t\"-o\", \"ControlMaster=auto\",\n\t\t\"-o\", \"ControlPath=\" + filepath.Join(rd, cp),\n\t\t\"-o\", \"ControlPersist=yes\",\n\t\t\"-o\", \"ServerAliveInterval=60\",\n\t\t\"-o\", \"ServerAliveCountMax=5\",\n\t\t\"-o\", \"TCPKeepAlive=no\",\n\t}, nil\n}\n\nfunc set_askpass(hostname_for_match, uname string, overrides []string) (need_to_request_data bool) {\n\tneed_to_request_data = true\n\tsentinel := filepath.Join(utils.CacheDir(), \"openssh-is-new-enough-for-askpass\")\n\t_, err := os.Stat(sentinel)\n\tsentinel_exists := err == nil\n\tif sentinel_exists || GetSSHVersion().SupportsAskpassRequire() {\n\t\tif !sentinel_exists {\n\t\t\t_ = os.WriteFile(sentinel, []byte{0}, 0o644)\n\t\t}\n\t\tneed_to_request_data = false\n\t}\n\texe, err := os.Executable()\n\tif err == nil {\n\t\tos.Setenv(\"SSH_ASKPASS\", exe)\n\t\tos.Setenv(\"KITTY_KITTEN_RUN_MODULE\", \"ssh_askpass\")\n\t\t// Provide data to askpass so it can lookup auth settings in ssh.conf\n\t\tos.Setenv(\"KITTY_SSH_ASKPASS_HOST\", hostname_for_match)\n\t\tos.Setenv(\"KITTY_SSH_ASKPASS_USER\", uname)\n\t\tov, _ := json.Marshal(overrides)\n\t\tos.Setenv(\"KITTY_SSH_ASKPASS_OVERRIDES\", string(ov))\n\t\tif !need_to_request_data {\n\t\t\tos.Setenv(\"SSH_ASKPASS_REQUIRE\", \"force\")\n\t\t}\n\t} else {\n\t\tneed_to_request_data = true\n\t}\n\treturn\n}\n\ntype connection_data struct {\n\tremote_args        []string\n\thost_opts          *Config\n\thostname_for_match string\n\tusername           string\n\techo_on            bool\n\trequest_data       bool\n\tliteral_env        map[string]string\n\tlisten_on          string\n\ttest_script        string\n\tdont_create_shm    bool\n\n\tshm_name         string\n\tscript_type      string\n\trcmd             []string\n\treplacements     map[string]string\n\trequest_id       string\n\tbootstrap_script string\n}\n\nfunc get_effective_ksi_env_var(x string) string {\n\tparts := strings.Split(strings.TrimSpace(strings.ToLower(x)), \" \")\n\tcurrent := utils.NewSetWithItems(parts...)\n\tif current.Has(\"disabled\") {\n\t\treturn \"\"\n\t}\n\tallowed := utils.NewSetWithItems(kitty.AllowedShellIntegrationValues...)\n\tif !current.IsSubsetOf(allowed) {\n\t\treturn RelevantKittyOpts().Shell_integration\n\t}\n\treturn x\n}\n\nfunc serialize_env(cd *connection_data, get_local_env func(string) (string, bool)) (string, string) {\n\tksi := \"\"\n\tif cd.host_opts.Shell_integration == \"inherited\" {\n\t\tksi = get_effective_ksi_env_var(RelevantKittyOpts().Shell_integration)\n\t} else {\n\t\tksi = get_effective_ksi_env_var(cd.host_opts.Shell_integration)\n\t}\n\tenv := make([]*EnvInstruction, 0, 8)\n\tadd_env := func(key, val string, fallback ...string) *EnvInstruction {\n\t\tif val == \"\" && len(fallback) > 0 {\n\t\t\tval = fallback[0]\n\t\t}\n\t\tif val != \"\" {\n\t\t\tenv = append(env, &EnvInstruction{key: key, val: val, literal_quote: true})\n\t\t\treturn env[len(env)-1]\n\t\t}\n\t\treturn nil\n\t}\n\tadd_non_literal_env := func(key, val string, fallback ...string) *EnvInstruction {\n\t\tans := add_env(key, val, fallback...)\n\t\tif ans != nil {\n\t\t\tans.literal_quote = false\n\t\t}\n\t\treturn ans\n\t}\n\tfor k, v := range cd.literal_env {\n\t\tadd_env(k, v)\n\t}\n\tadd_env(\"TERM\", os.Getenv(\"TERM\"), RelevantKittyOpts().Term)\n\tadd_env(\"COLORTERM\", \"truecolor\")\n\tenv = append(env, cd.host_opts.Env...)\n\tadd_env(\"KITTY_WINDOW_ID\", os.Getenv(\"KITTY_WINDOW_ID\"))\n\tadd_env(\"WINDOWID\", os.Getenv(\"WINDOWID\"))\n\tif ksi != \"\" {\n\t\tadd_env(\"KITTY_SHELL_INTEGRATION\", ksi)\n\t} else {\n\t\tenv = append(env, &EnvInstruction{key: \"KITTY_SHELL_INTEGRATION\", delete_on_remote: true})\n\t}\n\tadd_non_literal_env(\"KITTY_SSH_KITTEN_DATA_DIR\", cd.host_opts.Remote_dir)\n\tadd_non_literal_env(\"KITTY_LOGIN_SHELL\", cd.host_opts.Login_shell)\n\tadd_non_literal_env(\"KITTY_LOGIN_CWD\", cd.host_opts.Cwd)\n\tif cd.host_opts.Remote_kitty != Remote_kitty_no {\n\t\tadd_env(\"KITTY_REMOTE\", cd.host_opts.Remote_kitty.String())\n\t}\n\tadd_env(\"KITTY_PUBLIC_KEY\", os.Getenv(\"KITTY_PUBLIC_KEY\"))\n\tif cd.listen_on != \"\" {\n\t\tadd_env(\"KITTY_LISTEN_ON\", cd.listen_on)\n\t}\n\treturn final_env_instructions(cd.script_type == \"py\", get_local_env, env...), ksi\n}\n\nfunc make_tarfile(cd *connection_data, get_local_env func(string) (string, bool)) ([]byte, error) {\n\tenv_script, ksi := serialize_env(cd, get_local_env)\n\tw := bytes.Buffer{}\n\tw.Grow(64 * 1024)\n\tgw, err := gzip.NewWriterLevel(&w, gzip.BestCompression)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttw := tar.NewWriter(gw)\n\trd := strings.TrimRight(cd.host_opts.Remote_dir, \"/\")\n\tseen := make(map[file_unique_id]string, 32)\n\tadd := func(h *tar.Header, data []byte) (err error) {\n\t\t// some distro's like nix mess with installed file permissions so ensure\n\t\t// files are at least readable and writable by owning user\n\t\th.Mode |= 0o600\n\t\terr = tw.WriteHeader(h)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif data != nil {\n\t\t\t_, err := tw.Write(data)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\tfor _, ci := range cd.host_opts.Copy {\n\t\terr = ci.get_file_data(add, seen)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\ttype fe struct {\n\t\tarcname string\n\t\tdata    []byte\n\t}\n\tnow := time.Now()\n\tadd_data := func(items ...fe) error {\n\t\tfor _, item := range items {\n\t\t\terr := add(\n\t\t\t\t&tar.Header{\n\t\t\t\t\tTypeflag: tar.TypeReg, Name: item.arcname, Format: tar.FormatPAX, Size: int64(len(item.data)),\n\t\t\t\t\tMode: 0o644, ModTime: now, ChangeTime: now, AccessTime: now,\n\t\t\t\t}, item.data)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tadd_entries := func(prefix string, items ...shell_integration.Entry) error {\n\t\tfor _, item := range items {\n\t\t\terr := add(\n\t\t\t\t&tar.Header{\n\t\t\t\t\tTypeflag: item.Metadata.Typeflag, Name: path.Join(prefix, path.Base(item.Metadata.Name)), Format: tar.FormatPAX,\n\t\t\t\t\tSize: int64(len(item.Data)), Mode: item.Metadata.Mode, ModTime: item.Metadata.ModTime,\n\t\t\t\t\tAccessTime: item.Metadata.AccessTime, ChangeTime: item.Metadata.ChangeTime,\n\t\t\t\t}, item.Data)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\n\t}\n\tif err = add_data(fe{\"data.sh\", utils.UnsafeStringToBytes(env_script)}); err != nil {\n\t\treturn nil, err\n\t}\n\tif cd.script_type == \"sh\" {\n\t\tif err = add_data(fe{\"bootstrap-utils.sh\", shell_integration.Data()[path.Join(\"shell-integration/ssh/bootstrap-utils.sh\")].Data}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif ksi != \"\" {\n\t\tfor _, fname := range shell_integration.Data().FilesMatching(\n\t\t\t\"shell-integration/\",\n\t\t\t\"shell-integration/ssh/.+\",        // bootstrap files are sent as command line args\n\t\t\t\"shell-integration/zsh/kitty.zsh\", // backward compat file not needed by ssh kitten\n\t\t) {\n\t\t\tarcname := path.Join(\"home/\", rd, \"/\", path.Dir(fname))\n\t\t\terr = add_entries(arcname, shell_integration.Data()[fname])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\tif cd.host_opts.Remote_kitty != Remote_kitty_no {\n\t\tarcname := path.Join(\"home/\", rd, \"/kitty\")\n\t\terr = add_data(fe{arcname + \"/version\", utils.UnsafeStringToBytes(kitty.VersionString)})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, x := range []string{\"kitty\", \"kitten\"} {\n\t\t\terr = add_entries(path.Join(arcname, \"bin\"), shell_integration.Data()[path.Join(\"shell-integration\", \"ssh\", x)])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\terr = add_entries(path.Join(\"home\", \".terminfo\"), shell_integration.Data()[\"terminfo/kitty.terminfo\"])\n\tif err == nil {\n\t\terr = add_entries(path.Join(\"home\", \".terminfo\", \"x\"), shell_integration.Data()[\"terminfo/x/\"+kitty.DefaultTermName])\n\t}\n\tif err == nil {\n\t\terr = tw.Close()\n\t\tif err == nil {\n\t\t\terr = gw.Close()\n\t\t}\n\t}\n\treturn w.Bytes(), err\n}\n\nfunc prepare_home_command(cd *connection_data) string {\n\tis_python := cd.script_type == \"py\"\n\thomevar := \"\"\n\tfor _, ei := range cd.host_opts.Env {\n\t\tif ei.key == \"HOME\" && !ei.delete_on_remote {\n\t\t\tif ei.copy_from_local {\n\t\t\t\thomevar = os.Getenv(\"HOME\")\n\t\t\t} else {\n\t\t\t\thomevar = ei.val\n\t\t\t}\n\t\t}\n\t}\n\texport_home_cmd := \"\"\n\tif homevar != \"\" {\n\t\tif is_python {\n\t\t\texport_home_cmd = base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(homevar))\n\t\t} else {\n\t\t\texport_home_cmd = fmt.Sprintf(\"export HOME=%s; cd \\\"$HOME\\\"\", utils.QuoteStringForSH(homevar))\n\t\t}\n\t}\n\treturn export_home_cmd\n}\n\nfunc prepare_exec_cmd(cd *connection_data) string {\n\t// ssh simply concatenates multiple commands using a space see\n\t// line 1129 of ssh.c and on the remote side sshd.c runs the\n\t// concatenated command as shell -c cmd\n\tif cd.script_type == \"py\" {\n\t\treturn base64.RawStdEncoding.EncodeToString(utils.UnsafeStringToBytes(strings.Join(cd.remote_args, \" \")))\n\t}\n\targs := make([]string, len(cd.remote_args))\n\tfor i, arg := range cd.remote_args {\n\t\targs[i] = strings.ReplaceAll(arg, \"'\", \"'\\\"'\\\"'\")\n\t}\n\treturn \"unset KITTY_SHELL_INTEGRATION; exec \\\"$login_shell\\\" -c '\" + strings.Join(args, \" \") + \"'\"\n}\n\nvar data_shm shm.MMap\n\nfunc prepare_script(script string, replacements map[string]string) string {\n\tif _, found := replacements[\"EXEC_CMD\"]; !found {\n\t\treplacements[\"EXEC_CMD\"] = \"\"\n\t}\n\tif _, found := replacements[\"EXPORT_HOME_CMD\"]; !found {\n\t\treplacements[\"EXPORT_HOME_CMD\"] = \"\"\n\t}\n\tkeys := utils.Keys(replacements)\n\tfor i, key := range keys {\n\t\tkeys[i] = \"\\\\b\" + key + \"\\\\b\"\n\t}\n\tpat := regexp.MustCompile(strings.Join(keys, \"|\"))\n\treturn pat.ReplaceAllStringFunc(script, func(key string) string { return replacements[key] })\n}\n\nfunc bootstrap_script(cd *connection_data) (err error) {\n\tif cd.request_id == \"\" {\n\t\tcd.request_id = os.Getenv(\"KITTY_PID\") + \"-\" + os.Getenv(\"KITTY_WINDOW_ID\")\n\t}\n\texport_home_cmd := prepare_home_command(cd)\n\texec_cmd := \"\"\n\tif len(cd.remote_args) > 0 {\n\t\texec_cmd = prepare_exec_cmd(cd)\n\t}\n\tpw, err := secrets.TokenHex()\n\tif err != nil {\n\t\treturn err\n\t}\n\ttfd, err := make_tarfile(cd, os.LookupEnv)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdata := map[string]string{\n\t\t\"tarfile\":  base64.StdEncoding.EncodeToString(tfd),\n\t\t\"pw\":       pw,\n\t\t\"hostname\": cd.hostname_for_match, \"username\": cd.username,\n\t}\n\tencoded_data, err := json.Marshal(data)\n\tif err == nil && !cd.dont_create_shm {\n\t\tdata_shm, err = shm.CreateTemp(fmt.Sprintf(\"kssh-%d-\", os.Getpid()), uint64(len(encoded_data)+8))\n\t\tif err == nil {\n\t\t\terr = shm.WriteWithSize(data_shm, encoded_data, 0)\n\t\t\tif err == nil {\n\t\t\t\terr = data_shm.Flush()\n\t\t\t}\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !cd.dont_create_shm {\n\t\tcd.shm_name = data_shm.Name()\n\t}\n\tsensitive_data := map[string]string{\"REQUEST_ID\": cd.request_id, \"DATA_PASSWORD\": pw, \"PASSWORD_FILENAME\": cd.shm_name}\n\treplacements := map[string]string{\n\t\t\"EXPORT_HOME_CMD\": export_home_cmd,\n\t\t\"EXEC_CMD\":        exec_cmd,\n\t\t\"TEST_SCRIPT\":     cd.test_script,\n\t}\n\tadd_bool := func(ok bool, key string) {\n\t\tif ok {\n\t\t\treplacements[key] = \"1\"\n\t\t} else {\n\t\t\treplacements[key] = \"0\"\n\t\t}\n\t}\n\tadd_bool(cd.request_data, \"REQUEST_DATA\")\n\tadd_bool(cd.echo_on, \"ECHO_ON\")\n\tsd := maps.Clone(replacements)\n\tif cd.request_data {\n\t\tmaps.Copy(sd, sensitive_data)\n\t}\n\tmaps.Copy(replacements, sensitive_data)\n\tcd.replacements = replacements\n\tcd.bootstrap_script = utils.UnsafeBytesToString(shell_integration.Data()[\"shell-integration/ssh/bootstrap.\"+cd.script_type].Data)\n\tcd.bootstrap_script = prepare_script(cd.bootstrap_script, sd)\n\treturn err\n}\n\nfunc wrap_bootstrap_script(cd *connection_data) {\n\t// sshd will execute the command we pass it by join all command line\n\t// arguments with a space and passing it as a single argument to the users\n\t// login shell with -c. If the user has a non POSIX login shell it might\n\t// have different escaping semantics and syntax, so the command it should\n\t// execute has to be as simple as possible, basically of the form\n\t// interpreter -c unwrap_script escaped_bootstrap_script\n\t// The unwrap_script is responsible for unescaping the bootstrap script and\n\t// executing it.\n\tencoded_script := \"\"\n\tunwrap_script := \"\"\n\tif cd.script_type == \"py\" {\n\t\tencoded_script = base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(cd.bootstrap_script))\n\t\tunwrap_script = `\"import base64, sys; eval(compile(base64.standard_b64decode(sys.argv[-1]), 'bootstrap.py', 'exec'))\"`\n\t} else {\n\t\t// We can't rely on base64 being available on the remote system, so instead\n\t\t// we quote the bootstrap script by replacing ' and \\ with \\v and \\f\n\t\t// also replacing \\n and ! with \\r and \\b for tcsh\n\t\t// finally surrounding with '\n\t\tencoded_script = \"'\" + strings.NewReplacer(\"'\", \"\\v\", \"\\\\\", \"\\f\", \"\\n\", \"\\r\", \"!\", \"\\b\").Replace(cd.bootstrap_script) + \"'\"\n\t\tunwrap_script = `'eval \"$(echo \"$0\" | tr \\\\\\v\\\\\\f\\\\\\r\\\\\\b \\\\\\047\\\\\\134\\\\\\n\\\\\\041)\"' `\n\t}\n\tcd.rcmd = []string{\"exec\", cd.host_opts.Interpreter, \"-c\", unwrap_script, encoded_script}\n}\n\nfunc get_remote_command(cd *connection_data) error {\n\tinterpreter := cd.host_opts.Interpreter\n\tq := strings.ToLower(path.Base(interpreter))\n\tis_python := strings.Contains(q, \"python\")\n\tcd.script_type = \"sh\"\n\tif is_python {\n\t\tcd.script_type = \"py\"\n\t}\n\terr := bootstrap_script(cd)\n\tif err != nil {\n\t\treturn err\n\t}\n\twrap_bootstrap_script(cd)\n\treturn nil\n}\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc drain_potential_tty_garbage(term *tty.Term) {\n\terr := term.ApplyOperations(tty.TCSANOW, tty.SetRaw)\n\tif err != nil {\n\t\treturn\n\t}\n\tcanary, err := secrets.TokenHex()\n\tif err != nil {\n\t\treturn\n\t}\n\tdcs, err := tui.DCSToKitty(\"echo\", canary)\n\tq := utils.UnsafeStringToBytes(canary)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = term.WriteAllString(dcs)\n\tif err != nil {\n\t\treturn\n\t}\n\tdata := make([]byte, 0)\n\tgive_up_at := time.Now().Add(2 * time.Second)\n\tbuf := make([]byte, 0, 8192)\n\tfor !bytes.Contains(data, q) {\n\t\tbuf = buf[:cap(buf)]\n\t\ttimeout := time.Until(give_up_at)\n\t\tif timeout < 0 {\n\t\t\tbreak\n\t\t}\n\t\tn, err := term.ReadWithTimeout(buf, timeout)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tdata = append(data, buf[:n]...)\n\t}\n}\n\nfunc change_colors(color_scheme string) (ans string, err error) {\n\tif color_scheme == \"\" {\n\t\treturn\n\t}\n\tvar theme *themes.Theme\n\tif !strings.HasSuffix(color_scheme, \".conf\") {\n\t\tcs := os.ExpandEnv(color_scheme)\n\t\ttc, closer, err := themes.LoadThemes(-1)\n\t\tif err != nil && errors.Is(err, themes.ErrNoCacheFound) {\n\t\t\ttc, closer, err = themes.LoadThemes(time.Hour * 24)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tdefer closer.Close()\n\t\ttheme = tc.ThemeByName(cs)\n\t\tif theme == nil {\n\t\t\treturn \"\", fmt.Errorf(\"No theme named %#v found\", cs)\n\t\t}\n\t} else {\n\t\ttheme, err = themes.ThemeFromFile(utils.ResolveConfPath(color_scheme))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tans, err = theme.AsEscapeCodes()\n\tif err == nil {\n\t\tans = \"\\033[#P\" + ans\n\t}\n\treturn\n}\n\nfunc run_ssh(ssh_args, server_args, found_extra_args []string) (rc int, err error) {\n\tgo shell_integration.Data()\n\tgo RelevantKittyOpts()\n\tdefer func() {\n\t\tif data_shm != nil {\n\t\t\tdata_shm.Close()\n\t\t\t_ = data_shm.Unlink()\n\t\t}\n\t}()\n\tcmd := append([]string{SSHExe()}, ssh_args...)\n\tcd := connection_data{remote_args: server_args[1:]}\n\thostname := server_args[0]\n\tif len(cd.remote_args) == 0 {\n\t\tcmd = append(cmd, \"-t\")\n\t}\n\tinsertion_point := len(cmd)\n\tcmd = append(cmd, \"--\", hostname)\n\tuname, hostname_for_match := get_destination(hostname)\n\toverrides, literal_env, err := parse_kitten_args(found_extra_args, uname, hostname_for_match)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\thost_opts, bad_lines, err := load_config(hostname_for_match, uname, overrides)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\t// check the secrets syntax here as askpass has no good way to report errors to\n\t// the user\n\tif err = resolve_secrets(host_opts, true); err != nil {\n\t\treturn 1, err\n\t}\n\tif len(bad_lines) > 0 {\n\t\tfor _, x := range bad_lines {\n\t\t\tfmt.Fprintf(os.Stderr, \"Ignoring bad config line: %s:%d with error: %s\", filepath.Base(x.Src_file), x.Line_number, x.Err)\n\t\t}\n\t}\n\tif host_opts.Delegate != \"\" {\n\t\tdelegate_cmd, err := shlex.Split(host_opts.Delegate)\n\t\tif err != nil {\n\t\t\treturn 1, fmt.Errorf(\"Could not parse delegate command: %#v with error: %w\", host_opts.Delegate, err)\n\t\t}\n\t\treturn 1, unix.Exec(utils.FindExe(delegate_cmd[0]), utils.Concat(delegate_cmd, ssh_args, server_args), os.Environ())\n\t}\n\tmaster_is_alive, master_checked := false, false\n\tvar control_master_args []string\n\tif host_opts.Share_connections {\n\t\tkpid, err := strconv.Atoi(os.Getenv(\"KITTY_PID\"))\n\t\tif err != nil {\n\t\t\treturn 1, fmt.Errorf(\"Invalid KITTY_PID env var not an integer: %#v\", os.Getenv(\"KITTY_PID\"))\n\t\t}\n\t\tcontrol_master_args, err = connection_sharing_args(kpid)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tcmd = slices.Insert(cmd, insertion_point, control_master_args...)\n\t}\n\tuse_kitty_askpass := host_opts.Askpass == Askpass_native || (host_opts.Askpass == Askpass_unless_set && os.Getenv(\"SSH_ASKPASS\") == \"\")\n\tneed_to_request_data := true\n\tif use_kitty_askpass {\n\t\tneed_to_request_data = set_askpass(hostname_for_match, uname, overrides)\n\t}\n\tmaster_is_functional := func() bool {\n\t\tif master_checked {\n\t\t\treturn master_is_alive\n\t\t}\n\t\tmaster_checked = true\n\t\tcheck_cmd := slices.Insert(cmd, 1, \"-O\", \"check\")\n\t\tmaster_is_alive = exec.Command(check_cmd[0], check_cmd[1:]...).Run() == nil\n\t\treturn master_is_alive\n\t}\n\n\tif need_to_request_data && host_opts.Share_connections && master_is_functional() {\n\t\tneed_to_request_data = false\n\t}\n\trun_control_master := func() error {\n\t\tcmcmd := slices.Clone(cmd[:insertion_point])\n\t\tcmcmd = append(cmcmd, control_master_args...)\n\t\tcmcmd = append(cmcmd, \"-N\", \"-f\")\n\t\tcmcmd = append(cmcmd, \"--\", hostname)\n\t\tc := exec.Command(cmcmd[0], cmcmd[1:]...)\n\t\tc.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr\n\t\terr := c.Run()\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"Failed to start SSH ControlMaster with cmdline: %s and error: %w\", strings.Join(cmcmd, \" \"), err)\n\t\t}\n\t\tmaster_checked = false\n\t\tmaster_is_alive = false\n\t\treturn err\n\t}\n\tif host_opts.Forward_remote_control && os.Getenv(\"KITTY_LISTEN_ON\") != \"\" {\n\t\tif !host_opts.Share_connections {\n\t\t\treturn 1, fmt.Errorf(\"Cannot use forward_remote_control=yes without share_connections=yes as it relies on SSH Controlmasters\")\n\t\t}\n\t\tif !master_is_functional() {\n\t\t\tif err = run_control_master(); err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\tif !master_is_functional() {\n\t\t\t\treturn 1, fmt.Errorf(\"SSH ControlMaster not functional after being started explicitly\")\n\t\t\t}\n\t\t}\n\t\tprotocol, listen_on, found := strings.Cut(os.Getenv(\"KITTY_LISTEN_ON\"), \":\")\n\t\tif !found {\n\t\t\treturn 1, fmt.Errorf(\"Invalid KITTY_LISTEN_ON: %#v\", os.Getenv(\"KITTY_LISTEN_ON\"))\n\t\t}\n\t\tif protocol == \"unix\" && strings.HasPrefix(listen_on, \"@\") {\n\t\t\treturn 1, fmt.Errorf(\"Cannot forward kitty remote control socket when an abstract UNIX socket (%s) is used, due to limitations in OpenSSH. Use either a path based one or a TCP socket\", listen_on)\n\t\t}\n\t\tcmcmd := slices.Clone(cmd[:insertion_point])\n\t\tcmcmd = append(cmcmd, control_master_args...)\n\t\tcmcmd = append(cmcmd, \"-R\", \"0:\"+listen_on, \"-O\", \"forward\")\n\t\tcmcmd = append(cmcmd, \"--\", hostname)\n\t\tc := exec.Command(cmcmd[0], cmcmd[1:]...)\n\t\tb := bytes.Buffer{}\n\t\tc.Stdout = &b\n\t\tc.Stderr = os.Stderr\n\t\tif err := c.Run(); err != nil {\n\t\t\treturn 1, fmt.Errorf(\"%s\\nSetup of port forward in SSH ControlMaster failed with error: %w\", b.String(), err)\n\t\t}\n\t\tport, err := strconv.Atoi(strings.TrimSpace(b.String()))\n\t\tif err != nil {\n\t\t\tos.Stderr.Write(b.Bytes())\n\t\t\treturn 1, fmt.Errorf(\"Setup of port forward in SSH ControlMaster failed with error: invalid resolved port returned: %s\", b.String())\n\t\t}\n\t\tcd.listen_on = \"tcp:localhost:\" + strconv.Itoa(port)\n\t}\n\tterm, err := tty.OpenControllingTerm(tty.SetNoEcho)\n\tif err != nil {\n\t\treturn 1, fmt.Errorf(\"Failed to open controlling terminal with error: %w\", err)\n\t}\n\tcd.echo_on = term.WasEchoOnOriginally()\n\tcd.host_opts, cd.literal_env = host_opts, literal_env\n\tcd.request_data = need_to_request_data\n\tcd.hostname_for_match, cd.username = hostname_for_match, uname\n\tescape_codes_to_set_colors, err := change_colors(cd.host_opts.Color_scheme)\n\tif err == nil {\n\t\terr = term.WriteAllString(escape_codes_to_set_colors + loop.SAVE_PRIVATE_MODE_VALUES + loop.PUSH_KEY_FLAGS + loop.HANDLE_TERMIOS_SIGNALS.EscapeCodeToSet())\n\t}\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\trestore_escape_codes := loop.RESTORE_PRIVATE_MODE_VALUES + loop.POP_KEY_FLAGS + loop.HANDLE_TERMIOS_SIGNALS.EscapeCodeToReset()\n\tif escape_codes_to_set_colors != \"\" {\n\t\trestore_escape_codes += \"\\x1b[#Q\"\n\t}\n\tsigs := make(chan os.Signal, 8)\n\tsignal.Notify(sigs, unix.SIGINT, unix.SIGTERM)\n\tcleaned_up := false\n\tcleanup := func() {\n\t\tif !cleaned_up {\n\t\t\t_ = term.WriteAllString(restore_escape_codes)\n\t\t\tterm.RestoreAndClose()\n\t\t\tsignal.Reset()\n\t\t\tcleaned_up = true\n\t\t}\n\t}\n\tdefer cleanup()\n\terr = get_remote_command(&cd)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tcmd = append(cmd, cd.rcmd...)\n\tc := exec.Command(cmd[0], cmd[1:]...)\n\tc.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr\n\terr = c.Start()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\n\tif !cd.request_data {\n\t\trq := fmt.Sprintf(\"id=%s:pwfile=%s:pw=%s\", cd.replacements[\"REQUEST_ID\"], cd.replacements[\"PASSWORD_FILENAME\"], cd.replacements[\"DATA_PASSWORD\"])\n\t\terr := term.ApplyOperations(tty.TCSANOW, tty.SetNoEcho)\n\t\tif err == nil {\n\t\t\tvar dcs string\n\t\t\tdcs, err = tui.DCSToKitty(\"ssh\", rq)\n\t\t\tif err == nil {\n\t\t\t\terr = term.WriteAllString(dcs)\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\t_ = c.Process.Kill()\n\t\t\t_ = c.Wait()\n\t\t\treturn 1, err\n\t\t}\n\t}\n\tgo func() {\n\t\t<-sigs\n\t\t// ignore any interrupt and terminate signals as they will usually be sent to the ssh child process as well\n\t\t// and we are waiting on that.\n\t}()\n\terr = c.Wait()\n\tdrain_potential_tty_garbage(term)\n\tif err != nil {\n\t\tvar exit_err *exec.ExitError\n\t\tif errors.As(err, &exit_err) {\n\t\t\tif state := exit_err.ProcessState.String(); state == \"signal: interrupt\" {\n\t\t\t\tcleanup()\n\t\t\t\t_ = unix.Kill(os.Getpid(), unix.SIGINT)\n\t\t\t\t// Give the signal time to be delivered\n\t\t\t\ttime.Sleep(20 * time.Millisecond)\n\t\t\t}\n\t\t\treturn exit_err.ExitCode(), nil\n\t\t}\n\t\treturn 1, err\n\t}\n\treturn 0, nil\n}\n\nfunc main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {\n\tif len(args) > 0 {\n\t\tswitch args[0] {\n\t\tcase \"use-python\":\n\t\t\targs = args[1:] // backwards compat from when we had a python implementation\n\t\tcase \"-h\", \"--help\":\n\t\t\tcmd.ShowHelp()\n\t\t\treturn\n\t\t}\n\t}\n\tssh_args, server_args, passthrough, found_extra_args, err := ParseSSHArgs(args, \"--kitten\")\n\tif err != nil {\n\t\tvar invargs *ErrInvalidSSHArgs\n\t\tswitch {\n\t\tcase errors.As(err, &invargs):\n\t\t\tif invargs.Msg != \"\" {\n\t\t\t\tfmt.Fprintln(os.Stderr, invargs.Msg)\n\t\t\t}\n\t\t\treturn 1, unix.Exec(SSHExe(), []string{\"ssh\"}, os.Environ())\n\t\t}\n\t\treturn 1, err\n\t}\n\tif passthrough {\n\t\treturn 1, unix.Exec(SSHExe(), utils.Concat([]string{\"ssh\"}, ssh_args, server_args), os.Environ())\n\t}\n\tif os.Getenv(\"KITTY_WINDOW_ID\") == \"\" || os.Getenv(\"KITTY_PID\") == \"\" {\n\t\treturn 1, fmt.Errorf(\"The SSH kitten is meant to run inside a kitty window\")\n\t}\n\tif !tty.IsTerminal(os.Stdin.Fd()) {\n\t\treturn 1, fmt.Errorf(\"The SSH kitten is meant for interactive use only, STDIN must be a terminal\")\n\t}\n\treturn run_ssh(ssh_args, server_args, found_extra_args)\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n\nfunc specialize_command(ssh *cli.Command) {\n\tssh.Usage = \"arguments for the ssh command\"\n\tssh.ShortDescription = \"Truly convenient SSH\"\n\tssh.HelpText = \"The ssh kitten is a thin wrapper around the ssh command. It automatically enables shell integration on the remote host, re-uses existing connections to reduce latency, makes the kitty terminfo database available, etc. Its invocation is identical to the ssh command. For details on its usage, see :doc:`/kittens/ssh`.\"\n\tssh.IgnoreAllArgs = true\n\tssh.OnlyArgsAllowed = true\n\tssh.ArgCompleter = cli.CompletionForWrapper(\"ssh\")\n}\n\nfunc test_integration_with_python(args []string) (rc int, err error) {\n\tf, err := os.CreateTemp(\"\", \"*.conf\")\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tdefer func() {\n\t\tf.Close()\n\t\tos.Remove(f.Name())\n\t}()\n\t_, err = io.Copy(f, os.Stdin)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tcd := &connection_data{\n\t\trequest_id: \"testing\", remote_args: []string{},\n\t\tusername: \"testuser\", hostname_for_match: \"host.test\", request_data: true,\n\t\ttest_script: args[0], echo_on: true,\n\t}\n\topts, bad_lines, err := load_config(cd.hostname_for_match, cd.username, nil, f.Name())\n\tif err == nil {\n\t\tif len(bad_lines) > 0 {\n\t\t\treturn 1, fmt.Errorf(\"Bad config lines: %s with error: %s\", bad_lines[0].Line, bad_lines[0].Err)\n\t\t}\n\t\tcd.host_opts = opts\n\t\terr = get_remote_command(cd)\n\t}\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tdata, err := json.Marshal(map[string]any{\"cmd\": cd.rcmd, \"shm_name\": cd.shm_name})\n\tif err == nil {\n\t\t_, err = os.Stdout.Write(data)\n\t\tos.Stdout.Close()\n\t}\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\n\treturn\n}\n\nfunc TestEntryPoint(root *cli.Command) {\n\troot.AddSubCommand(&cli.Command{\n\t\tName:            \"ssh\",\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\treturn test_integration_with_python(args)\n\t\t},\n\t})\n\n}\n"
  },
  {
    "path": "kittens/ssh/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\n\nfrom kitty.conf.types import Definition\nfrom kitty.types import run_once\n\ncopy_message = '''\\\nCopy files and directories from local to remote hosts. The specified files are\nassumed to be relative to the HOME directory and copied to the HOME on the\nremote. Directories are copied recursively. If absolute paths are used, they are\ncopied as is.'''\n\n\n@run_once\ndef option_text() -> str:\n    return '''\n--glob\ntype=bool-set\nInterpret file arguments as glob patterns. Globbing is based on\nstandard wildcards with the addition that ``/**/`` matches any number of directories.\nSee the :link:`detailed syntax <https://github.com/bmatcuk/doublestar#patterns>`.\n\n\n--dest\nThe destination on the remote host to copy to. Relative paths are resolved\nrelative to HOME on the remote host. When this option is not specified, the\nlocal file path is used as the remote destination (with the HOME directory\ngetting automatically replaced by the remote HOME). Note that environment\nvariables and ~ are not expanded.\n\n\n--exclude\ntype=list\nA glob pattern. Files with names matching this pattern are excluded from being\ntransferred. Only used when copying directories. Can\nbe specified multiple times, if any of the patterns match the file will be\nexcluded. If the pattern includes a :code:`/` then it will match against the full\npath, not just the filename. In such patterns you can use :code:`/**/` to match zero\nor more directories. For example, to exclude a directory and everything under it use\n:code:`**/directory_name`.\nSee the :link:`detailed syntax <https://github.com/bmatcuk/doublestar#patterns>` for\nhow wildcards match.\n\n\n--symlink-strategy\ndefault=preserve\nchoices=preserve,resolve,keep-path\nControl what happens if the specified path is a symlink. The default is to preserve\nthe symlink, re-creating it on the remote machine. Setting this to :code:`resolve`\nwill cause the symlink to be followed and its target used as the file/directory to copy.\nThe value of :code:`keep-path` is the same as :code:`resolve` except that the remote\nfile path is derived from the symlink's path instead of the path of the symlink's target.\nNote that this option does not apply to symlinks encountered while recursively copying directories,\nthose are always preserved.\n'''\n\n\n\ndefinition = Definition(\n    '!kittens.ssh',\n)\n\nagr = definition.add_group\negr = definition.end_group\nopt = definition.add_option\n\nagr('bootstrap', 'Host bootstrap configuration')  # {{{\n\nopt('hostname', '*', long_text='''\nThe hostname that the following options apply to. A glob pattern to match\nmultiple hosts can be used. Multiple hostnames can also be specified, separated\nby spaces. The hostname can include an optional username in the form\n:code:`user@host`. When not specified options apply to all hosts, until the\nfirst hostname specification is found. Note that matching of hostname is done\nagainst the name you specify on the command line to connect to the remote host.\nIf you wish to include the same basic configuration for many different hosts,\nyou can do so with the :ref:`include <include>` directive. In version 0.28.0\nthe behavior of this option was changed slightly, now, when a hostname is encountered\nall its config values are set to defaults instead of being inherited from a previous\nmatching hostname block. In particular it means hostnames dont inherit configurations,\nthereby avoiding hard to understand action-at-a-distance.\n''')\n\nopt('interpreter', 'sh', long_text='''\nThe interpreter to use on the remote host. Must be either a POSIX complaint\nshell or a :program:`python` executable. If the default :program:`sh` is not\navailable or broken, using an alternate interpreter can be useful.\n''')\n\nopt('remote_dir', '.local/share/kitty-ssh-kitten', long_text='''\nThe location on the remote host where the files needed for this kitten are\ninstalled. Relative paths are resolved with respect to :code:`$HOME`. Absolute\npaths have their leading / removed and so are also resolved with respect to $HOME.\n''')\n\nopt('+copy', '', add_to_default=False, ctype='CopyInstruction', long_text=f'''\n{copy_message} For example::\n\n    copy .vimrc .zshrc .config/some-dir\n\nUse :code:`--dest` to copy a file to some other destination on the remote host::\n\n    copy --dest some-other-name some-file\n\nGlob patterns can be specified to copy multiple files, with :code:`--glob`::\n\n    copy --glob images/*.png\n\nFiles can be excluded when copying with :code:`--exclude`::\n\n    copy --glob --exclude *.jpg --exclude *.bmp images/*\n\nFiles whose remote name matches the exclude pattern will not be copied.\nFor more details, see :ref:`ssh_copy_command`.\n''')\negr()  # }}}\n\nagr('shell', 'Login shell environment')  # {{{\n\nopt('shell_integration', 'inherited', long_text='''\nControl the shell integration on the remote host. See :ref:`shell_integration`\nfor details on how this setting works. The special value :code:`inherited` means\nuse the setting from :file:`kitty.conf`. This setting is useful for overriding\nintegration on a per-host basis.\n''')\n\nopt('login_shell', '', long_text='''\nThe login shell to execute on the remote host. By default, the remote user\naccount's login shell is used.\n''')\n\nopt('+env', '', add_to_default=False, ctype='EnvInstruction', long_text='''\nSpecify the environment variables to be set on the remote host. Using the\nname with an equal sign (e.g. :code:`env VAR=`) will set it to the empty string.\nSpecifying only the name (e.g. :code:`env VAR`) will remove the variable from\nthe remote shell environment. The special value :code:`_kitty_copy_env_var_`\nwill cause the value of the variable to be copied from the local environment.\nThe definitions are processed alphabetically. Note that environment variables\nare expanded recursively, for example::\n\n    env VAR1=a\n    env VAR2=${HOME}/${VAR1}/b\n\nThe value of :code:`VAR2` will be :code:`<path to home directory>/a/b`.\n''')\n\nopt('cwd', '', long_text='''\nThe working directory on the remote host to change to. Environment variables in\nthis value are expanded. The default is empty so no changing is done, which\nusually means the HOME directory is used.\n''')\n\nopt('color_scheme', '', long_text='''\nSpecify a color scheme to use when connecting to the remote host. If this option\nends with :code:`.conf`, it is assumed to be the name of a config file to load\nfrom the kitty config directory, otherwise it is assumed to be the name of a\ncolor theme to load via the :doc:`themes kitten </kittens/themes>`. Note that\nonly colors applying to the text/background are changed, other config settings\nin the .conf files/themes are ignored.\n''')\n\nopt('remote_kitty', 'if-needed', choices=('if-needed', 'no', 'yes'), long_text='''\nMake :program:`kitten` available on the remote host. Useful to run kittens such\nas the :doc:`icat kitten </kittens/icat>` to display images or the\n:doc:`transfer file kitten </kittens/transfer>` to transfer files. Only works if\nthe remote host has an architecture for which :link:`pre-compiled kitten binaries\n<https://github.com/kovidgoyal/kitty/releases>` are available. Note that kitten\nis not actually copied to the remote host, instead a small bootstrap script is\ncopied which will download and run kitten when kitten is first executed on the\nremote host. A value of :code:`if-needed` means kitten is installed only if not\nalready present in the system-wide PATH. A value of :code:`yes` means that kitten\nis installed even if already present, and the installed kitten takes precedence.\nFinally, :code:`no` means no kitten is installed on the remote host. The\ninstalled kitten can be updated by running: :code:`kitten update-self` on the\nremote host.\n''')\negr()  # }}}\n\nagr('ssh', 'SSH configuration')  # {{{\n\nopt('share_connections', 'yes', option_type='to_bool', long_text='''\nWithin a single kitty instance, all connections to a particular server can be\nshared. This reduces startup latency for subsequent connections and means that\nyou have to enter the password only once. Under the hood, it uses SSH\nControlMasters and these are automatically cleaned up by kitty when it quits.\nYou can map a shortcut to :ac:`close_shared_ssh_connections` to disconnect all\nactive shared connections.\n''')\n\nopt('askpass', 'unless-set', choices=('unless-set', 'ssh', 'native'), long_text='''\nControl the program SSH uses to ask for passwords or confirmation of host keys\netc. The default is to use kitty's native :program:`askpass`, unless the\n:envvar:`SSH_ASKPASS` environment variable is set. Set this option to\n:code:`ssh` to not interfere with the normal ssh askpass mechanism at all, which\ntypically means that ssh will prompt at the terminal. Set it to :code:`native`\nto always use kitty's native, built-in askpass implementation. Note that not\nusing the kitty askpass implementation means that SSH might need to use the\nterminal before the connection is established, so the kitten cannot use the\nterminal to send data without an extra roundtrip, adding to initial connection\nlatency.\n''')\n\nopt('delegate', '', long_text='''\nDo not use the SSH kitten for this host. Instead run the command specified as the delegate.\nFor example using :code:`delegate ssh` will run the ssh command with all arguments passed\nto the kitten, except kitten specific ones. This is useful if some hosts are not capable\nof supporting the ssh kitten.\n''')\n\nopt('forward_remote_control', 'no', option_type='to_bool', long_text='''\nForward the kitty remote control socket to the remote host. This allows using the kitty\nremote control facilities from the remote host. WARNING: This allows any software\non the remote host full access to the local computer, so only do it for trusted remote hosts.\nNote that this does not work with abstract UNIX sockets such as :file:`@mykitty` because of SSH limitations.\nThis option uses SSH socket forwarding to forward the socket pointed to by the :envvar:`KITTY_LISTEN_ON`\nenvironment variable.\n''')\n\negr()  # }}}\n\nagr('askpass', 'Askpass automation')  # {{{\n\nopt('password', '', long_text='''\nSpecify a password to use when SSH prompts for a password. The value format is\n\"backend:secret\". Currently, only the \"text\" backend is supported, which stores\nthe secret in plain text in the config file. For example:\n\n    password text:my_password\n\nIf the backend prefix is omitted, it is treated as \"text:\" for backward\ncompatibility. Beware that storing passwords in plain text is insecure.\n''')\n\nopt('totp_secret', '', long_text='''\nSpecify a TOTP shared secret to auto-fill one-time codes when SSH asks for them.\nThe value format is \"backend:secret\". Currently, only the \"text\" backend is\nsupported. For example:\n\n    totp_secret text:JBSWY3DPEHPK3PXP\n\nIf the backend prefix is omitted, it is treated as \"text:\" for backward\ncompatibility.\n''')\n\nopt('totp_digits', '6', option_type='int', long_text='''\nNumber of digits for the generated TOTP codes. Default is 6.\n''')\n\nopt('totp_period', '30', option_type='int', long_text='''\nTime period in seconds for the TOTP code validity. Default is 30.\n''')\n\negr()  # }}}\n\n\ndef main(args: list[str]) -> str | None:\n    raise SystemExit('This should be run as kitten ssh')\n\nif __name__ == '__main__':\n    main([])\nelif __name__ == '__wrapper_of__':\n    cd = getattr(sys, 'cli_docs')\n    cd['wrapper_of'] = 'ssh'\nelif __name__ == '__conf__':\n    setattr(sys, 'options_definition', definition)\nelif __name__ == '__extra_cli_parsers__':\n    setattr(sys, 'extra_cli_parsers', {'copy': option_text()})\n"
  },
  {
    "path": "kittens/ssh/main_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ssh\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc TestCloneEnv(t *testing.T) {\n\tenv := map[string]string{\"a\": \"1\", \"b\": \"2\"}\n\tdata, err := json.Marshal(env)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tmmap, err := shm.CreateTemp(\"\", 128)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer mmap.Unlink()\n\tcopy(mmap.Slice()[4:], data)\n\tbinary.BigEndian.PutUint32(mmap.Slice(), uint32(len(data)))\n\tmmap.Close()\n\tx, err := add_cloned_env(mmap.Name())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdiff := cmp.Diff(env, x)\n\tif diff != \"\" {\n\t\tt.Fatalf(\"Failed to deserialize env\\n%s\", diff)\n\t}\n}\n\nfunc basic_connection_data(overrides ...string) *connection_data {\n\tans := &connection_data{\n\t\tscript_type: \"sh\", request_id: \"123-123\", remote_args: []string{},\n\t\tusername: \"testuser\", hostname_for_match: \"host.test\",\n\t\tdont_create_shm: true,\n\t}\n\topts, bad_lines, err := load_config(ans.hostname_for_match, ans.username, overrides)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tif len(bad_lines) != 0 {\n\t\tpanic(fmt.Sprintf(\"Bad config lines: %s with error: %s\", bad_lines[0].Line, bad_lines[0].Err))\n\t}\n\tans.host_opts = opts\n\treturn ans\n}\n\nfunc TestSSHBootstrapScriptLimit(t *testing.T) {\n\tcd := basic_connection_data()\n\terr := get_remote_command(cd)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttotal := 0\n\tfor _, x := range cd.rcmd {\n\t\ttotal += len(x)\n\t}\n\tif total > 9000 {\n\t\tt.Fatalf(\"Bootstrap script too large: %d bytes\", total)\n\t}\n}\n\nfunc TestSSHTarfile(t *testing.T) {\n\ttdir := t.TempDir()\n\tcd := basic_connection_data()\n\tdata, err := make_tarfile(cd, func(key string) (val string, found bool) { return })\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tcmd := exec.Command(\"tar\", \"xpzf\", \"-\", \"-C\", tdir)\n\tcmd.Stderr = os.Stderr\n\tinp, err := cmd.StdinPipe()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\terr = cmd.Start()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_, err = inp.Write(data)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tinp.Close()\n\terr = cmd.Wait()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tseen := map[string]bool{}\n\terr = filepath.WalkDir(tdir, func(name string, d fs.DirEntry, werr error) error {\n\t\tif werr != nil {\n\t\t\treturn werr\n\t\t}\n\t\trname, werr := filepath.Rel(tdir, name)\n\t\tif werr != nil {\n\t\t\treturn werr\n\t\t}\n\t\trname = strings.ReplaceAll(rname, \"\\\\\", \"/\")\n\t\tif rname == \".\" {\n\t\t\treturn nil\n\t\t}\n\t\tfi, werr := d.Info()\n\t\tif werr != nil {\n\t\t\treturn werr\n\t\t}\n\t\tif fi.Mode().Perm()&0o600 == 0 {\n\t\t\treturn fmt.Errorf(\"%s is not rw for its owner. Actual permissions: %s\", rname, fi.Mode().String())\n\t\t}\n\t\tseen[rname] = true\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !seen[\"data.sh\"] {\n\t\tt.Fatalf(\"data.sh missing\")\n\t}\n\tfor _, x := range []string{\".terminfo/kitty.terminfo\", \".terminfo/x/\" + kitty.DefaultTermName} {\n\t\tif !seen[\"home/\"+x] {\n\t\t\tt.Fatalf(\"%s missing\", x)\n\t\t}\n\t}\n\tfor _, x := range []string{\"shell-integration/bash/kitty.bash\", \"shell-integration/fish/vendor_completions.d/kitty.fish\"} {\n\t\tif !seen[path.Join(\"home\", cd.host_opts.Remote_dir, x)] {\n\t\t\tt.Fatalf(\"%s missing\", x)\n\t\t}\n\t}\n\tfor _, x := range []string{\"kitty\", \"kitten\"} {\n\t\tp := filepath.Join(tdir, \"home\", cd.host_opts.Remote_dir, \"kitty\", \"bin\", x)\n\t\tif err = unix.Access(p, unix.X_OK); err != nil {\n\t\t\tt.Fatalf(\"Cannot execute %s with error: %s\", x, err)\n\t\t}\n\t}\n\tif seen[path.Join(\"home\", cd.host_opts.Remote_dir, \"shell-integration\", \"ssh\", \"kitten\")] {\n\t\tt.Fatalf(\"Contents of shell-integration/ssh not excluded\")\n\t}\n}\n"
  },
  {
    "path": "kittens/ssh/utils.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ssh\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nvar SSHExe = sync.OnceValue(func() string {\n\treturn utils.FindExe(\"ssh\")\n})\n\nvar SSHOptions = sync.OnceValue(func() (ssh_options map[string]string) {\n\tdefer func() {\n\t\tif ssh_options == nil {\n\t\t\tssh_options = map[string]string{\n\t\t\t\t\"4\": \"\", \"6\": \"\", \"A\": \"\", \"a\": \"\", \"C\": \"\", \"f\": \"\", \"G\": \"\", \"g\": \"\", \"K\": \"\", \"k\": \"\",\n\t\t\t\t\"M\": \"\", \"N\": \"\", \"n\": \"\", \"q\": \"\", \"s\": \"\", \"T\": \"\", \"t\": \"\", \"V\": \"\", \"v\": \"\", \"X\": \"\",\n\t\t\t\t\"x\": \"\", \"Y\": \"\", \"y\": \"\", \"B\": \"bind_interface\", \"b\": \"bind_address\", \"c\": \"cipher_spec\",\n\t\t\t\t\"D\": \"[bind_address:]port\", \"E\": \"log_file\", \"e\": \"escape_char\", \"F\": \"configfile\", \"I\": \"pkcs11\",\n\t\t\t\t\"i\": \"identity_file\", \"J\": \"[user@]host[:port]\", \"L\": \"address\", \"l\": \"login_name\", \"m\": \"mac_spec\",\n\t\t\t\t\"O\": \"ctl_cmd\", \"o\": \"option\", \"p\": \"port\", \"Q\": \"query_option\", \"R\": \"address\",\n\t\t\t\t\"S\": \"ctl_path\", \"W\": \"host:port\", \"w\": \"local_tun[:remote_tun]\",\n\t\t\t}\n\t\t}\n\t}()\n\tcmd := exec.Command(SSHExe())\n\tvar stdout, stderr bytes.Buffer\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\t_ = cmd.Run()\n\n\ttext := stderr.String()\n\tif text == \"\" || strings.Contains(text, \"OpenSSL version mismatch.\") {\n\t\t// https://bugzilla.mindrot.org/show_bug.cgi?id=3548\n\t\treturn\n\t}\n\tssh_options = make(map[string]string, 32)\n\tfor {\n\t\tpos := strings.IndexByte(text, '[')\n\t\tif pos < 0 {\n\t\t\tbreak\n\t\t}\n\t\tnum := 1\n\t\tepos := pos\n\t\tfor num > 0 {\n\t\t\tepos++\n\t\t\tswitch text[epos] {\n\t\t\tcase '[':\n\t\t\t\tnum += 1\n\t\t\tcase ']':\n\t\t\t\tnum -= 1\n\t\t\t}\n\t\t}\n\t\tq := text[pos+1 : epos]\n\t\ttext = text[epos:]\n\t\tif len(q) < 2 || !strings.HasPrefix(q, \"-\") {\n\t\t\tcontinue\n\t\t}\n\t\topt, desc, found := strings.Cut(q, \" \")\n\t\tif found {\n\t\t\tssh_options[opt[1:]] = desc\n\t\t} else {\n\t\t\tfor _, ch := range opt[1:] {\n\t\t\t\tssh_options[string(ch)] = \"\"\n\t\t\t}\n\t\t}\n\t}\n\treturn\n})\n\nfunc GetSSHCLI() (boolean_ssh_args *utils.Set[string], other_ssh_args *utils.Set[string]) {\n\tother_ssh_args, boolean_ssh_args = utils.NewSet[string](32), utils.NewSet[string](32)\n\tfor k, v := range SSHOptions() {\n\t\tk = \"-\" + k\n\t\tif v == \"\" {\n\t\t\tboolean_ssh_args.Add(k)\n\t\t} else {\n\t\t\tother_ssh_args.Add(k)\n\t\t}\n\t}\n\treturn\n}\n\nfunc is_extra_arg(arg string, extra_args []string) string {\n\tfor _, x := range extra_args {\n\t\tif arg == x || strings.HasPrefix(arg, x+\"=\") {\n\t\t\treturn x\n\t\t}\n\t}\n\treturn \"\"\n}\n\ntype ErrInvalidSSHArgs struct {\n\tMsg string\n}\n\nfunc (self *ErrInvalidSSHArgs) Error() string {\n\treturn self.Msg\n}\n\nfunc PassthroughArgs() map[string]bool {\n\treturn map[string]bool{\"-N\": true, \"-n\": true, \"-f\": true, \"-G\": true, \"-T\": true, \"-V\": true}\n}\n\nfunc ParseSSHArgs(args []string, extra_args ...string) (ssh_args []string, server_args []string, passthrough bool, found_extra_args []string, err error) {\n\tif extra_args == nil {\n\t\textra_args = []string{}\n\t}\n\tif len(args) == 0 {\n\t\tpassthrough = true\n\t\treturn\n\t}\n\tpassthrough_args := PassthroughArgs()\n\tboolean_ssh_args, other_ssh_args := GetSSHCLI()\n\tssh_args, server_args, found_extra_args = make([]string, 0, 16), make([]string, 0, 16), make([]string, 0, 16)\n\texpecting_option_val := false\n\tstop_option_processing := false\n\texpecting_extra_val := \"\"\n\tfor _, argument := range args {\n\t\tif len(server_args) > 1 || stop_option_processing {\n\t\t\tserver_args = append(server_args, argument)\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(argument, \"-\") && !expecting_option_val {\n\t\t\tif argument == \"--\" {\n\t\t\t\tstop_option_processing = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(extra_args) > 0 {\n\t\t\t\tmatching_ex := is_extra_arg(argument, extra_args)\n\t\t\t\tif matching_ex != \"\" {\n\t\t\t\t\t_, exval, found := strings.Cut(argument, \"=\")\n\t\t\t\t\tif found {\n\t\t\t\t\t\tfound_extra_args = append(found_extra_args, matching_ex, exval)\n\t\t\t\t\t} else {\n\t\t\t\t\t\texpecting_extra_val = matching_ex\n\t\t\t\t\t\texpecting_option_val = true\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\t// could be a multi-character option\n\t\t\tall_args := []rune(argument[1:])\n\t\t\tfor i, ch := range all_args {\n\t\t\t\targ := \"-\" + string(ch)\n\t\t\t\tif passthrough_args[arg] {\n\t\t\t\t\tpassthrough = true\n\t\t\t\t}\n\t\t\t\tif boolean_ssh_args.Has(arg) {\n\t\t\t\t\tssh_args = append(ssh_args, arg)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif other_ssh_args.Has(arg) {\n\t\t\t\t\tssh_args = append(ssh_args, arg)\n\t\t\t\t\tif i+1 < len(all_args) {\n\t\t\t\t\t\tssh_args = append(ssh_args, string(all_args[i+1:]))\n\t\t\t\t\t} else {\n\t\t\t\t\t\texpecting_option_val = true\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\terr = &ErrInvalidSSHArgs{Msg: \"unknown option -- \" + arg[1:]}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif expecting_option_val {\n\t\t\tif expecting_extra_val != \"\" {\n\t\t\t\tfound_extra_args = append(found_extra_args, expecting_extra_val, argument)\n\t\t\t} else {\n\t\t\t\tssh_args = append(ssh_args, argument)\n\t\t\t}\n\t\t\texpecting_option_val = false\n\t\t\tcontinue\n\t\t}\n\t\tserver_args = append(server_args, argument)\n\t}\n\tif len(server_args) == 0 && !passthrough {\n\t\terr = &ErrInvalidSSHArgs{Msg: \"\"}\n\t}\n\treturn\n}\n\ntype SSHVersion struct{ Major, Minor int }\n\nfunc (self SSHVersion) SupportsAskpassRequire() bool {\n\treturn self.Major > 8 || (self.Major == 8 && self.Minor >= 4)\n}\n\nvar GetSSHVersion = sync.OnceValue(func() SSHVersion {\n\tb, err := exec.Command(SSHExe(), \"-V\").CombinedOutput()\n\tif err != nil {\n\t\treturn SSHVersion{}\n\t}\n\tm := regexp.MustCompile(`OpenSSH_(\\d+).(\\d+)`).FindSubmatch(b)\n\tif len(m) == 3 {\n\t\tmaj, _ := strconv.Atoi(utils.UnsafeBytesToString(m[1]))\n\t\tmin, _ := strconv.Atoi(utils.UnsafeBytesToString(m[2]))\n\t\treturn SSHVersion{Major: maj, Minor: min}\n\t}\n\treturn SSHVersion{}\n})\n\ntype KittyOpts struct {\n\tTerm, Shell_integration string\n}\n\nfunc read_relevant_kitty_opts(override_conf_path ...string) KittyOpts {\n\tans := KittyOpts{Term: kitty.KittyConfigDefaults.Term, Shell_integration: kitty.KittyConfigDefaults.Shell_integration}\n\thandle_line := func(key, val string) error {\n\t\tswitch key {\n\t\tcase \"term\":\n\t\t\tans.Term = strings.TrimSpace(val)\n\t\tcase \"shell_integration\":\n\t\t\tans.Shell_integration = strings.TrimSpace(val)\n\t\t}\n\t\treturn nil\n\t}\n\tconfig.ReadKittyConfig(handle_line, override_conf_path...)\n\treturn ans\n}\n\nvar RelevantKittyOpts = sync.OnceValue(func() KittyOpts {\n\treturn read_relevant_kitty_opts()\n})\n"
  },
  {
    "path": "kittens/ssh/utils.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport subprocess\nimport traceback\nfrom collections.abc import Iterator, Sequence\nfrom contextlib import suppress\nfrom typing import Any\n\nfrom kitty.types import run_once\nfrom kitty.utils import SSHConnectionData\n\n\n@run_once\ndef ssh_options() -> dict[str, str]:\n    try:\n        p = subprocess.run(['ssh'], stderr=subprocess.PIPE, encoding='utf-8')\n        raw = p.stderr or ''\n    except FileNotFoundError:\n        return {\n            '4': '', '6': '', 'A': '', 'a': '', 'C': '', 'f': '', 'G': '', 'g': '', 'K': '', 'k': '',\n            'M': '', 'N': '', 'n': '', 'q': '', 's': '', 'T': '', 't': '', 'V': '', 'v': '', 'X': '',\n            'x': '', 'Y': '', 'y': '', 'B': 'bind_interface', 'b': 'bind_address', 'c': 'cipher_spec',\n            'D': '[bind_address:]port', 'E': 'log_file', 'e': 'escape_char', 'F': 'configfile', 'I': 'pkcs11',\n            'i': 'identity_file', 'J': '[user@]host[:port]', 'L': 'address', 'l': 'login_name', 'm': 'mac_spec',\n            'O': 'ctl_cmd', 'o': 'option', 'p': 'port', 'Q': 'query_option', 'R': 'address',\n            'S': 'ctl_path', 'W': 'host:port', 'w': 'local_tun[:remote_tun]'\n        }\n\n    ans: dict[str, str] = {}\n    pos = 0\n    while True:\n        pos = raw.find('[', pos)  # ]\n        if pos < 0:\n            break\n        num = 1\n        epos = pos\n        while num > 0:\n            epos += 1\n            if raw[epos] not in '[]':\n                continue\n            num += 1 if raw[epos] == '[' else -1  # ]\n        q = raw[pos+1:epos]\n        pos = epos\n        if len(q) < 2 or q[0] != '-':\n            continue\n        if ' ' in q:\n            opt, desc = q.split(' ', 1)\n            ans[opt[1:]] = desc\n        else:\n            ans.update(dict.fromkeys(q[1:], ''))\n    return ans\n\n\ndef is_kitten_cmdline(q: Sequence[str]) -> bool:\n    if not q:\n        return False\n    exe_name = os.path.basename(q[0]).lower()\n    if exe_name == 'kitten' and q[1:2] == ['ssh']:\n        return True\n    if len(q) < 4:\n        return False\n    if exe_name != 'kitty':\n        return False\n    if q[1:3] == ['+kitten', 'ssh'] or q[1:4] == ['+', 'kitten', 'ssh']:\n        return True\n    return q[1:3] == ['+runpy', 'from kittens.runner import main; main()'] and len(q) >= 6 and q[5] == 'ssh'\n\n\ndef patch_cmdline(key: str, val: str, argv: list[str]) -> None:\n    for i, arg in enumerate(tuple(argv)):\n        if arg.startswith(f'--kitten={key}='):\n            argv[i] = f'--kitten={key}={val}'\n            return\n        elif i > 0 and argv[i-1] == '--kitten' and (arg.startswith(f'{key}=') or arg.startswith(f'{key} ')):\n            argv[i] = f'{key}={val}'\n            return\n    idx = argv.index('ssh')\n    argv.insert(idx + 1, f'--kitten={key}={val}')\n\n\ndef remove_env_var_from_cmdline(key: str, argv: list[str]) -> None:\n    while True:\n        for i, arg in enumerate(tuple(argv)):\n            if arg.startswith(f'--kitten=env={key}='):\n                del argv[i]\n                break\n            elif i > 0 and argv[i-1] == '--kitten' and (arg.startswith(f'env={key}=') or arg.startswith(f'env {key}=')):\n                del argv[i-1:i+1]\n                break\n        else:\n            break\n\n\ndef set_single_env_var_in_cmdline(key: str, val: str, argv: list[str]) -> None:\n    remove_env_var_from_cmdline(key, argv)\n    idx = argv.index('ssh')\n    argv.insert(idx+1, f'--kitten=env={key}={val}')\n\n\ndef set_cwd_in_cmdline(cwd: str, argv: list[str]) -> None:\n    patch_cmdline('cwd', cwd, argv)\n\n\ndef create_shared_memory(data: Any, prefix: str) -> str:\n    import atexit\n    import json\n\n    from kitty.fast_data_types import get_boss\n    from kitty.shm import SharedMemory\n    db = json.dumps(data).encode('utf-8')\n    with SharedMemory(size=len(db) + SharedMemory.num_bytes_for_size, prefix=prefix) as shm:\n        shm.write_data_with_size(db)\n        shm.flush()\n        atexit.register(shm.close)  # keeps shm alive till exit\n        get_boss().atexit.shm_unlink(shm.name)\n    return shm.name\n\n\ndef read_data_from_shared_memory(shm_name: str) -> Any:\n    import json\n    import stat\n\n    from kitty.shm import SharedMemory\n    with SharedMemory(shm_name, readonly=True) as shm:\n        shm.unlink()\n        if shm.stats.st_uid != os.geteuid() or shm.stats.st_gid != os.getegid():\n            raise ValueError(f'Incorrect owner on pwfile: uid={shm.stats.st_uid} gid={shm.stats.st_gid}')\n        mode = stat.S_IMODE(shm.stats.st_mode)\n        if mode != stat.S_IREAD | stat.S_IWRITE:\n            raise ValueError(f'Incorrect permissions on pwfile: 0o{mode:03o}')\n        return json.loads(shm.read_data_with_size())\n\n\ndef get_ssh_data(msgb: memoryview, request_id: str) -> Iterator[bytes|memoryview]:\n    from base64 import standard_b64decode\n    yield b'\\nKITTY_DATA_START\\n'  # to discard leading data\n    try:\n        msg = standard_b64decode(msgb).decode('utf-8')\n        md = dict(x.split('=', 1) for x in msg.split(':'))\n        pw = md['pw']\n        pwfilename = md['pwfile']\n        rq_id = md['id']\n    except Exception:\n        traceback.print_exc()\n        yield b'invalid ssh data request message\\n'\n    else:\n        try:\n            env_data = read_data_from_shared_memory(pwfilename)\n            if pw != env_data['pw']:\n                raise ValueError('Incorrect password')\n            if rq_id != request_id:\n                raise ValueError(f'Incorrect request id: {rq_id!r} expecting the KITTY_PID-KITTY_WINDOW_ID for the current kitty window')\n        except Exception as e:\n            traceback.print_exc()\n            yield f'{e}\\n'.encode()\n        else:\n            yield b'OK\\n'\n            encoded_data = memoryview(env_data['tarfile'].encode('ascii'))\n            # macOS has a 255 byte limit on its input queue as per man stty.\n            # Not clear if that applies to canonical mode input as well, but\n            # better to be safe.\n            line_sz = 254\n            while encoded_data:\n                yield encoded_data[:line_sz]\n                yield b'\\n'\n                encoded_data = encoded_data[line_sz:]\n            yield b'KITTY_DATA_END\\n'\n\n\ndef set_env_in_cmdline(env: dict[str, str], argv: list[str], clone: bool = True) -> None:\n    from kitty.options.utils import DELETE_ENV_VAR\n    if clone:\n        patch_cmdline('clone_env', create_shared_memory(env, 'ksse-'), argv)\n        return\n    idx = argv.index('ssh') - 1\n    for i in range(idx, len(argv)):\n        if argv[i] == '--kitten':\n            idx = i + 1\n        elif argv[i].startswith('--kitten='):\n            idx = i\n    env_dirs = []\n    for k, v in env.items():\n        if v is DELETE_ENV_VAR:\n            x = f'--kitten=env={k}'\n        else:\n            x = f'--kitten=env={k}={v}'\n        env_dirs.append(x)\n    argv[idx+1:idx+1] = env_dirs\n\n\ndef get_ssh_cli() -> tuple[set[str], set[str]]:\n    other_ssh_args: set[str] = set()\n    boolean_ssh_args: set[str] = set()\n    for k, v in ssh_options().items():\n        k = f'-{k}'\n        if v:\n            other_ssh_args.add(k)\n        else:\n            boolean_ssh_args.add(k)\n    return boolean_ssh_args, other_ssh_args\n\n\ndef is_extra_arg(arg: str, extra_args: tuple[str, ...]) -> str:\n    for x in extra_args:\n        if arg == x or arg.startswith(f'{x}='):\n            return x\n    return ''\n\n\npassthrough_args = {f'-{x}' for x in 'NnfGT'}\n\n\ndef set_server_args_in_cmdline(\n    server_args: list[str], argv: list[str],\n    extra_args: tuple[str, ...] = ('--kitten',),\n    allocate_tty: bool = False\n) -> None:\n    boolean_ssh_args, other_ssh_args = get_ssh_cli()\n    ssh_args = []\n    expecting_option_val = False\n    found_extra_args: list[str] = []\n    expecting_extra_val = ''\n    ans = list(argv)\n    found_ssh = False\n    for i, argument in enumerate(argv):\n        if not found_ssh:\n            found_ssh = argument == 'ssh'\n            continue\n        if argument.startswith('-') and not expecting_option_val:\n            if argument == '--':\n                del ans[i+2:]\n                if allocate_tty and ans[i-1] != '-t':\n                    ans.insert(i, '-t')\n                break\n            if extra_args:\n                matching_ex = is_extra_arg(argument, extra_args)\n                if matching_ex:\n                    if '=' in argument:\n                        exval = argument.partition('=')[-1]\n                        found_extra_args.extend((matching_ex, exval))\n                    else:\n                        expecting_extra_val = matching_ex\n                        expecting_option_val = True\n                    continue\n            # could be a multi-character option\n            all_args = argument[1:]\n            for i, arg in enumerate(all_args):\n                arg = f'-{arg}'\n                if arg in boolean_ssh_args:\n                    ssh_args.append(arg)\n                    continue\n                if arg in other_ssh_args:\n                    ssh_args.append(arg)\n                    rest = all_args[i+1:]\n                    if rest:\n                        ssh_args.append(rest)\n                    else:\n                        expecting_option_val = True\n                    break\n                raise KeyError(f'unknown option -- {arg[1:]}')\n            continue\n        if expecting_option_val:\n            if expecting_extra_val:\n                found_extra_args.extend((expecting_extra_val, argument))\n                expecting_extra_val = ''\n            else:\n                ssh_args.append(argument)\n            expecting_option_val = False\n            continue\n        del ans[i+1:]\n        if allocate_tty and ans[i] != '-t':\n            ans.insert(i, '-t')\n        break\n    argv[:] = ans + server_args\n\n\ndef get_connection_data(args: list[str], cwd: str = '', extra_args: tuple[str, ...] = ()) -> SSHConnectionData | None:\n    boolean_ssh_args, other_ssh_args = get_ssh_cli()\n    port: int | None = None\n    expecting_port = expecting_identity = False\n    expecting_option_val = False\n    expecting_hostname = False\n    expecting_extra_val = ''\n    host_name = identity_file = found_ssh = ''\n    found_extra_args: list[tuple[str, str]] = []\n\n    for i, arg in enumerate(args):\n        if not found_ssh:\n            if os.path.basename(arg).lower() in ('ssh', 'ssh.exe'):\n                found_ssh = arg\n            continue\n        if expecting_hostname:\n            host_name = arg\n            continue\n        if arg.startswith('-') and not expecting_option_val:\n            if arg in boolean_ssh_args:\n                continue\n            if arg == '--':\n                expecting_hostname = True\n            if arg.startswith('-p'):\n                if arg[2:].isdigit():\n                    with suppress(Exception):\n                        port = int(arg[2:])\n                    continue\n                elif arg == '-p':\n                    expecting_port = True\n            elif arg.startswith('-i'):\n                if arg == '-i':\n                    expecting_identity = True\n                else:\n                    identity_file = arg[2:]\n                    continue\n            if arg.startswith('--') and extra_args:\n                matching_ex = is_extra_arg(arg, extra_args)\n                if matching_ex:\n                    if '=' in arg:\n                        exval = arg.partition('=')[-1]\n                        found_extra_args.append((matching_ex, exval))\n                        continue\n                    expecting_extra_val = matching_ex\n\n            expecting_option_val = True\n            continue\n\n        if expecting_option_val:\n            if expecting_port:\n                with suppress(Exception):\n                    port = int(arg)\n                expecting_port = False\n            elif expecting_identity:\n                identity_file = arg\n            elif expecting_extra_val:\n                found_extra_args.append((expecting_extra_val, arg))\n                expecting_extra_val = ''\n            expecting_option_val = False\n            continue\n\n        if not host_name:\n            host_name = arg\n    if not host_name:\n        return None\n    if host_name.startswith('ssh://'):\n        from urllib.parse import urlparse\n        purl = urlparse(host_name)\n        if purl.hostname:\n            host_name = purl.hostname\n        if purl.username:\n            host_name = f'{purl.username}@{host_name}'\n        if port is None and purl.port:\n            port = purl.port\n    if identity_file:\n        if not os.path.isabs(identity_file):\n            identity_file = os.path.expanduser(identity_file)\n        if not os.path.isabs(identity_file):\n            identity_file = os.path.normpath(os.path.join(cwd or os.getcwd(), identity_file))\n\n    return SSHConnectionData(found_ssh, host_name, port, identity_file, tuple(found_extra_args))\n"
  },
  {
    "path": "kittens/ssh/utils_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage ssh\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestGetSSHOptions(t *testing.T) {\n\tm := SSHOptions()\n\tif m[\"w\"] != \"local_tun[:remote_tun]\" {\n\n\t\tcmd := exec.Command(SSHExe())\n\t\tcmd.Stdout = os.Stdout\n\t\tcmd.Stderr = os.Stderr\n\t\tcmd.Run()\n\t\tt.Fatalf(\"Unexpected set of SSH options: %#v\", m)\n\t}\n}\n\nfunc TestParseSSHArgs(t *testing.T) {\n\tsplit := func(x string) []string {\n\t\tans, err := shlex.Split(x)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif len(ans) == 0 {\n\t\t\tans = []string{}\n\t\t}\n\t\treturn ans\n\t}\n\n\tp := func(args, expected_ssh_args, expected_server_args, expected_extra_args string, expected_passthrough bool) {\n\t\tssh_args, server_args, passthrough, extra_args, err := ParseSSHArgs(split(args), \"--kitten\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tcheck := func(a, b any) {\n\t\t\tdiff := cmp.Diff(a, b)\n\t\t\tif diff != \"\" {\n\t\t\t\tt.Fatalf(\"Unexpected value for args: %#v\\n%s\", args, diff)\n\t\t\t}\n\t\t}\n\t\tcheck(split(expected_ssh_args), ssh_args)\n\t\tcheck(split(expected_server_args), server_args)\n\t\tcheck(split(expected_extra_args), extra_args)\n\t\tcheck(expected_passthrough, passthrough)\n\t}\n\tp(`localhost`, ``, `localhost`, ``, false)\n\tp(`-- localhost`, ``, `localhost`, ``, false)\n\tp(`-46p23 localhost sh -c \"a b\"`, `-4 -6 -p 23`, `localhost sh -c \"a b\"`, ``, false)\n\tp(`-46p23 -S/moose -W x:6 -- localhost sh -c \"a b\"`, `-4 -6 -p 23 -S /moose -W x:6`, `localhost sh -c \"a b\"`, ``, false)\n\tp(`--kitten=abc -np23 --kitten xyz host`, `-n -p 23`, `host`, `--kitten abc --kitten xyz`, true)\n}\n\nfunc TestRelevantKittyOpts(t *testing.T) {\n\ttdir := t.TempDir()\n\tpath := filepath.Join(tdir, \"kitty.conf\")\n\tos.WriteFile(path, []byte(\"term XXX\\nshell_integration changed\\nterm abcd\"), 0o600)\n\trko := read_relevant_kitty_opts(path)\n\tif rko.Term != \"abcd\" {\n\t\tt.Fatalf(\"Unexpected TERM: %s\", RelevantKittyOpts().Term)\n\t}\n\tif rko.Shell_integration != \"changed\" {\n\t\tt.Fatalf(\"Unexpected shell_integration: %s\", RelevantKittyOpts().Shell_integration)\n\t}\n}\n"
  },
  {
    "path": "kittens/themes/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/themes/list.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage themes\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kovidgoyal/kitty/tools/themes\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype ThemesList struct {\n\tthemes, all_themes     *themes.Themes\n\tcurrent_search         string\n\tdisplay_strings        []string\n\twidths                 []int\n\tmax_width, current_idx int\n}\n\nfunc (self *ThemesList) Len() int {\n\tif self.themes == nil {\n\t\treturn 0\n\t}\n\treturn self.themes.Len()\n}\n\nfunc (self *ThemesList) Next(delta int, allow_wrapping bool) bool {\n\tif len(self.display_strings) == 0 {\n\t\treturn false\n\t}\n\tidx := self.current_idx + delta\n\tif !allow_wrapping && (idx < 0 || idx > self.Len()) {\n\t\treturn false\n\t}\n\tfor idx < 0 {\n\t\tidx += self.Len()\n\t}\n\tself.current_idx = idx % self.Len()\n\treturn true\n}\n\nfunc limit_lengths(text string) string {\n\tt, _ := wcswidth.TruncateToVisualLengthWithWidth(text, 31)\n\tif len(t) >= len(text) {\n\t\treturn text\n\t}\n\treturn t + \"…\"\n}\n\nfunc (self *ThemesList) UpdateThemes(themes *themes.Themes) {\n\tself.themes, self.all_themes = themes, themes\n\tif self.current_search != \"\" {\n\t\tself.themes = self.all_themes.Copy()\n\t\tself.display_strings = utils.Map(limit_lengths, self.themes.ApplySearch(self.current_search))\n\t} else {\n\t\tself.display_strings = utils.Map(limit_lengths, self.themes.Names())\n\t}\n\tself.widths = utils.Map(wcswidth.Stringwidth, self.display_strings)\n\tself.max_width = utils.Max(0, self.widths...)\n\tself.current_idx = 0\n}\n\nfunc (self *ThemesList) UpdateSearch(query string) bool {\n\tif query == self.current_search || self.all_themes == nil {\n\t\treturn false\n\t}\n\tself.current_search = query\n\tself.UpdateThemes(self.all_themes)\n\treturn true\n}\n\ntype Line struct {\n\ttext       string\n\twidth      int\n\tis_current bool\n}\n\nfunc (self *ThemesList) Lines(num_rows int) []Line {\n\tif num_rows < 1 {\n\t\treturn nil\n\t}\n\tans := make([]Line, 0, len(self.display_strings))\n\tbefore_num := utils.Min(self.current_idx, num_rows-1)\n\tstart := self.current_idx - before_num\n\tfor i := start; i < utils.Min(start+num_rows, len(self.display_strings)); i++ {\n\t\tans = append(ans, Line{self.display_strings[i], self.widths[i], i == self.current_idx})\n\t}\n\treturn ans\n}\n\nfunc (self *ThemesList) CurrentTheme() *themes.Theme {\n\tif self.themes == nil {\n\t\treturn nil\n\t}\n\treturn self.themes.At(self.current_idx)\n}\n"
  },
  {
    "path": "kittens/themes/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage themes\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/themes\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc complete_themes(completions *cli.Completions, word string, arg_num int) {\n\tthemes.CompleteThemes(completions, word, arg_num)\n}\n\nfunc non_interactive(opts *Options, theme_name string) (rc int, err error) {\n\tthemes, closer, err := themes.LoadThemes(time.Duration(opts.CacheAge * float64(time.Hour*24)))\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tdefer closer.Close()\n\ttheme := themes.ThemeByName(theme_name)\n\tif theme == nil {\n\t\ttheme_name = strings.ReplaceAll(theme_name, `\\`, ``)\n\t\ttheme = themes.ThemeByName(theme_name)\n\t\tif theme == nil {\n\t\t\treturn 1, fmt.Errorf(\"No theme named: %s\", theme_name)\n\t\t}\n\t}\n\tif opts.DumpTheme {\n\t\tcode, err := theme.Code()\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tfmt.Println(code)\n\t} else {\n\t\terr = theme.SaveInConf(utils.ConfigDir(), opts.ReloadIn, opts.ConfigFileName)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t}\n\treturn\n}\n\nfunc main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tif len(args) > 1 {\n\t\targs = []string{strings.Join(args, ` `)}\n\t}\n\tif len(args) == 1 {\n\t\treturn non_interactive(opts, args[0])\n\t}\n\tlp, err := loop.New()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tcv := utils.NewCachedValues(\"unicode-input\", &CachedData{Category: \"All\"})\n\th := &handler{lp: lp, opts: opts, cached_data: cv.Load()}\n\tdefer cv.Save()\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.AllowLineWrapping(false)\n\t\tlp.SetWindowTitle(`Choose a theme for kitty`)\n\t\th.initialize()\n\t\treturn \"\", nil\n\t}\n\tlp.OnWakeup = h.on_wakeup\n\tlp.OnFinalize = func() string {\n\t\th.finalize()\n\t\tlp.SetCursorVisible(true)\n\t\treturn ``\n\t}\n\tlp.OnResize = func(_, _ loop.ScreenSize) error {\n\t\th.draw_screen()\n\t\treturn nil\n\t}\n\tlp.OnKeyEvent = h.on_key_event\n\tlp.OnText = h.on_text\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn 1, nil\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n\nfunc parse_theme_metadata() error {\n\traw, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpaths := utils.Splitlines(utils.UnsafeBytesToString(raw))\n\tans := make([]*themes.ThemeMetadata, 0, len(paths))\n\tfor _, path := range paths {\n\t\tif path != \"\" {\n\t\t\tmetadata, _, err := themes.ParseThemeMetadata(path)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif metadata.Name == \"\" {\n\t\t\t\tmetadata.Name = themes.ThemeNameFromFileName(filepath.Base(path))\n\t\t\t}\n\t\t\tans = append(ans, metadata)\n\t\t}\n\t}\n\traw, err = json.Marshal(ans)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = os.Stdout.Write(raw)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc ParseEntryPoint(parent *cli.Command) {\n\tparent.AddSubCommand(&cli.Command{\n\t\tName:   \"__parse_theme_metadata__\",\n\t\tHidden: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\terr = parse_theme_metadata()\n\t\t\tif err != nil {\n\t\t\t\trc = 1\n\t\t\t}\n\t\t\treturn\n\t\t},\n\t})\n\n}\n"
  },
  {
    "path": "kittens/themes/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\n\nfrom kitty.simple_cli_definitions import CompletionSpec\n\nhelp_text = (\n    'Change the kitty theme. If no theme name is supplied, run interactively, otherwise'\n    ' change the current theme to the specified theme name.'\n)\nusage = '[theme name to switch to]'\nOPTIONS = '''\n--cache-age\ntype=float\ndefault=1\nCheck for new themes only after the specified number of days. A value of\nzero will always check for new themes. A negative value will never check\nfor new themes, instead raising an error if a local copy of the themes\nis not available.\n\n\n--reload-in\ndefault=parent\nchoices=none,parent,all\nBy default, this kitten will signal only the parent kitty instance it is\nrunning in to reload its config, after making changes. Use this option\nto instead either not reload the config at all or in all running\nkitty instances.\n\n\n--dump-theme\ntype=bool-set\ndefault=false\nWhen running non-interactively, dump the specified theme to STDOUT\ninstead of changing kitty.conf.\n\n\n--config-file-name\ndefault=kitty.conf\nThe name or path to the config file to edit. Relative paths are interpreted\nwith respect to the kitty config directory. By default the kitty config file,\nkitty.conf is edited. This is most useful if you add :code:`include themes.conf`\nto your kitty.conf and then have the kitten operate only on :file:`themes.conf`,\nallowing :code:`kitty.conf` to remain unchanged.\n'''.format\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('This must be run as kitten themes')\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Manage kitty color schemes easily'\n    cd['args_completion'] = CompletionSpec.from_string('type:special group:complete_themes')\n"
  },
  {
    "path": "kittens/themes/ui.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage themes\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"io\"\n\t\"maps\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/themes\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/readline\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype State int\n\nconst (\n\tFETCHING State = iota\n\tBROWSING\n\tSEARCHING\n\tACCEPTING\n)\nconst SEPARATOR = \"║\"\n\ntype CachedData struct {\n\tRecent   []string `json:\"recent\"`\n\tCategory string   `json:\"category\"`\n}\n\ntype fetch_data struct {\n\tthemes *themes.Themes\n\terr    error\n\tcloser io.Closer\n}\n\nvar category_filters = map[string]func(*themes.Theme) bool{\n\t\"all\":   func(*themes.Theme) bool { return true },\n\t\"dark\":  func(t *themes.Theme) bool { return t.IsDark() },\n\t\"light\": func(t *themes.Theme) bool { return !t.IsDark() },\n\t\"user\":  func(t *themes.Theme) bool { return t.IsUserDefined() },\n}\n\nfunc recent_filter(items []string) func(*themes.Theme) bool {\n\tallowed := utils.NewSetWithItems(items...)\n\treturn func(t *themes.Theme) bool {\n\t\treturn allowed.Has(t.Name())\n\t}\n}\n\ntype handler struct {\n\tlp          *loop.Loop\n\topts        *Options\n\tcached_data *CachedData\n\n\tstate            State\n\tfetch_result     chan fetch_data\n\tall_themes       *themes.Themes\n\tthemes_closer    io.Closer\n\tthemes_list      *ThemesList\n\tcategory_filters map[string]func(*themes.Theme) bool\n\tcolors_set_once  bool\n\ttabs             []string\n\trl               *readline.Readline\n}\n\n// fetching {{{\nfunc (self *handler) fetch_themes() {\n\tr := fetch_data{}\n\tr.themes, r.closer, r.err = themes.LoadThemes(time.Duration(self.opts.CacheAge * float64(time.Hour*24)))\n\tself.lp.WakeupMainThread()\n\tself.fetch_result <- r\n}\n\nfunc (self *handler) on_fetching_key_event(ev *loop.KeyEvent) error {\n\tif ev.MatchesPressOrRepeat(\"esc\") {\n\t\tself.lp.Quit(0)\n\t\tev.Handled = true\n\t}\n\treturn nil\n}\n\nfunc (self *handler) on_wakeup() error {\n\tr := <-self.fetch_result\n\tif r.err != nil {\n\t\treturn r.err\n\t}\n\tself.state = BROWSING\n\tself.all_themes = r.themes\n\tself.themes_closer = r.closer\n\tself.redraw_after_category_change()\n\treturn nil\n}\n\nfunc (self *handler) draw_fetching_screen() {\n\tself.lp.Println(\"Downloading themes from repository, please wait...\")\n}\n\n// }}}\n\nfunc (self *handler) finalize() {\n\tt := self.themes_closer\n\tif t != nil {\n\t\tt.Close()\n\t\tself.themes_closer = nil\n\t}\n}\n\nfunc (self *handler) initialize() {\n\tself.tabs = strings.Split(\"all dark light recent user\", \" \")\n\tself.rl = readline.New(self.lp, readline.RlInit{DontMarkPrompts: true, Prompt: \"/\"})\n\tself.themes_list = &ThemesList{}\n\tself.fetch_result = make(chan fetch_data)\n\tself.category_filters = make(map[string]func(*themes.Theme) bool, len(category_filters)+1)\n\tmaps.Copy(self.category_filters, category_filters)\n\tself.category_filters[\"recent\"] = recent_filter(self.cached_data.Recent)\n\tgo self.fetch_themes()\n\tself.draw_screen()\n}\n\nfunc (self *handler) enforce_cursor_state() {\n\tself.lp.SetCursorVisible(self.state == FETCHING)\n}\n\nfunc (self *handler) draw_screen() {\n\tself.lp.StartAtomicUpdate()\n\tdefer self.lp.EndAtomicUpdate()\n\tself.lp.ClearScreen()\n\tself.enforce_cursor_state()\n\tswitch self.state {\n\tcase FETCHING:\n\t\tself.draw_fetching_screen()\n\tcase BROWSING, SEARCHING:\n\t\tself.draw_browsing_screen()\n\tcase ACCEPTING:\n\t\tself.draw_accepting_screen()\n\t}\n}\n\nfunc (self *handler) current_category() string {\n\tans := self.cached_data.Category\n\tif self.category_filters[ans] == nil {\n\t\tans = \"all\"\n\t}\n\treturn ans\n}\n\nfunc (self *handler) set_current_category(category string) {\n\tif self.category_filters[category] == nil {\n\t\tcategory = \"all\"\n\t}\n\tself.cached_data.Category = category\n}\n\nfunc ReadKittyColorSettings() map[string]string {\n\tsettings := make(map[string]string, 512)\n\thandle_line := func(key, val string) error {\n\t\tif themes.AllColorSettingNames[key] {\n\t\t\tsettings[key] = val\n\t\t}\n\t\treturn nil\n\t}\n\tconfig.ReadKittyConfig(handle_line)\n\treturn settings\n}\n\nfunc (self *handler) set_colors_to_current_theme() bool {\n\tif self.themes_list == nil && self.colors_set_once {\n\t\treturn false\n\t}\n\tself.colors_set_once = true\n\tif self.themes_list != nil {\n\t\tt := self.themes_list.CurrentTheme()\n\t\tif t != nil {\n\t\t\traw, err := t.AsEscapeCodes()\n\t\t\tif err == nil {\n\t\t\t\tself.lp.QueueWriteString(raw)\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\tself.lp.QueueWriteString(themes.ColorSettingsAsEscapeCodes(ReadKittyColorSettings()))\n\treturn true\n}\n\nfunc (self *handler) redraw_after_category_change() {\n\tself.themes_list.UpdateThemes(self.all_themes.Filtered(self.category_filters[self.current_category()]))\n\tself.set_colors_to_current_theme()\n\tself.draw_screen()\n}\n\nfunc (self *handler) on_key_event(ev *loop.KeyEvent) error {\n\tswitch self.state {\n\tcase FETCHING:\n\t\treturn self.on_fetching_key_event(ev)\n\tcase BROWSING:\n\t\treturn self.on_browsing_key_event(ev)\n\tcase SEARCHING:\n\t\treturn self.on_searching_key_event(ev)\n\tcase ACCEPTING:\n\t\treturn self.on_accepting_key_event(ev)\n\t}\n\treturn nil\n}\n\n// browsing ... {{{\n\nfunc (self *handler) next_category(delta int) {\n\tidx := slices.Index(self.tabs, self.current_category()) + delta + len(self.tabs)\n\tself.set_current_category(self.tabs[idx%len(self.tabs)])\n\tself.redraw_after_category_change()\n}\n\nfunc (self *handler) next(delta int, allow_wrapping bool) {\n\tif self.themes_list.Next(delta, allow_wrapping) {\n\t\tself.set_colors_to_current_theme()\n\t\tself.draw_screen()\n\t} else {\n\t\tself.lp.Beep()\n\t}\n}\n\nfunc (self *handler) on_browsing_key_event(ev *loop.KeyEvent) error {\n\tif ev.MatchesPressOrRepeat(\"esc\") || ev.MatchesCaseInsensitiveTextOrKey(\"q\") {\n\t\tself.lp.Quit(0)\n\t\tev.Handled = true\n\t\treturn nil\n\t}\n\tfor _, cat := range self.tabs {\n\t\tif ev.MatchesPressOrRepeat(cat[0:1]) || ev.MatchesPressOrRepeat(\"alt+\"+cat[0:1]) || ev.MatchesCaseInsensitiveTextOrKey(cat[0:1]) {\n\t\t\tev.Handled = true\n\t\t\tif cat != self.current_category() {\n\t\t\t\tself.set_current_category(cat)\n\t\t\t\tself.redraw_after_category_change()\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tif ev.MatchesPressOrRepeat(\"left\") || ev.MatchesPressOrRepeat(\"shift+tab\") {\n\t\tself.next_category(-1)\n\t\tev.Handled = true\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"right\") || ev.MatchesPressOrRepeat(\"tab\") {\n\t\tself.next_category(1)\n\t\tev.Handled = true\n\t\treturn nil\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"j\") || ev.MatchesPressOrRepeat(\"down\") {\n\t\tself.next(1, true)\n\t\tev.Handled = true\n\t\treturn nil\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"k\") || ev.MatchesPressOrRepeat(\"up\") {\n\t\tself.next(-1, true)\n\t\tev.Handled = true\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"page_down\") {\n\t\tev.Handled = true\n\t\tsz, err := self.lp.ScreenSize()\n\t\tif err == nil {\n\t\t\tself.next(int(sz.HeightCells)-3, false)\n\t\t}\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"page_up\") {\n\t\tev.Handled = true\n\t\tsz, err := self.lp.ScreenSize()\n\t\tif err == nil {\n\t\t\tself.next(3-int(sz.HeightCells), false)\n\t\t}\n\t\treturn nil\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"s\") || ev.MatchesCaseInsensitiveTextOrKey(\"/\") {\n\t\tev.Handled = true\n\t\tself.start_search()\n\t\treturn nil\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"c\") || ev.MatchesPressOrRepeat(\"enter\") {\n\t\tev.Handled = true\n\t\tif self.themes_list == nil || self.themes_list.Len() == 0 {\n\t\t\tself.lp.Beep()\n\t\t} else {\n\t\t\tself.state = ACCEPTING\n\t\t\tself.draw_screen()\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *handler) start_search() {\n\tself.state = SEARCHING\n\tself.rl.SetText(self.themes_list.current_search)\n\tself.draw_screen()\n}\n\nfunc (self *handler) draw_browsing_screen() {\n\tself.draw_tab_bar()\n\tsz, err := self.lp.ScreenSize()\n\tif err != nil {\n\t\treturn\n\t}\n\tnum_rows := int(sz.HeightCells) - 2\n\tmw := self.themes_list.max_width + 1\n\tgreen_fg, _, _ := strings.Cut(self.lp.SprintStyled(\"fg=green\", \"|\"), \"|\")\n\tfor _, l := range self.themes_list.Lines(num_rows) {\n\t\tline := l.text\n\t\tif l.is_current {\n\t\t\tline = strings.ReplaceAll(line, themes.MARK_AFTER, green_fg)\n\t\t\tself.lp.PrintStyled(\"fg=green\", \">\")\n\t\t\tself.lp.PrintStyled(\"fg=green bold\", line)\n\t\t} else {\n\t\t\tself.lp.PrintStyled(\"fg=green\", \" \")\n\t\t\tself.lp.QueueWriteString(line)\n\t\t}\n\t\tself.lp.MoveCursorHorizontally(mw - l.width)\n\t\tself.lp.Println(SEPARATOR)\n\t\tnum_rows--\n\t}\n\tfor ; num_rows > 0; num_rows-- {\n\t\tself.lp.MoveCursorHorizontally(mw + 1)\n\t\tself.lp.Println(SEPARATOR)\n\t}\n\tif self.themes_list != nil && self.themes_list.Len() > 0 {\n\t\tself.draw_theme_demo()\n\t}\n\tif self.state == BROWSING {\n\t\tself.draw_bottom_bar()\n\t} else {\n\t\tself.draw_search_bar()\n\t}\n}\n\nfunc (self *handler) draw_bottom_bar() {\n\tsz, err := self.lp.ScreenSize()\n\tif err != nil {\n\t\treturn\n\t}\n\tself.lp.MoveCursorTo(1, int(sz.HeightCells))\n\tself.lp.PrintStyled(\"reverse\", strings.Repeat(\" \", int(sz.WidthCells)))\n\tself.lp.QueueWriteString(\"\\r\")\n\n\tdraw_tab := func(t, sc string) {\n\t\ttext := self.mark_shortcut(utils.Capitalize(t), sc)\n\t\tself.lp.PrintStyled(\"reverse\", \" \"+text+\" \")\n\t}\n\tdraw_tab(\"search (/)\", \"s\")\n\tdraw_tab(\"accept (⏎)\", \"c\")\n\tself.lp.QueueWriteString(\"\\x1b[m\")\n}\n\nfunc (self *handler) draw_search_bar() {\n\tsz, err := self.lp.ScreenSize()\n\tif err != nil {\n\t\treturn\n\t}\n\tself.lp.MoveCursorTo(1, int(sz.HeightCells))\n\tself.lp.ClearToEndOfLine()\n\tself.rl.RedrawNonAtomic()\n}\n\nfunc (self *handler) mark_shortcut(text, acc string) string {\n\tacc_idx := strings.Index(strings.ToLower(text), strings.ToLower(acc))\n\treturn text[:acc_idx] + self.lp.SprintStyled(\"underline bold\", text[acc_idx:acc_idx+1]) + text[acc_idx+1:]\n}\n\nfunc (self *handler) draw_tab_bar() {\n\tsz, err := self.lp.ScreenSize()\n\tif err != nil {\n\t\treturn\n\t}\n\tself.lp.PrintStyled(\"reverse\", strings.Repeat(` `, int(sz.WidthCells)))\n\tself.lp.QueueWriteString(\"\\r\")\n\tcc := self.current_category()\n\tdraw_tab := func(text, name, acc string) {\n\t\tis_active := name == cc\n\t\tif is_active {\n\t\t\ttext := self.lp.SprintStyled(\"italic\", fmt.Sprintf(\"%s #%d\", text, self.themes_list.Len()))\n\t\t\tself.lp.Printf(\" %s \", text)\n\t\t} else {\n\t\t\ttext = self.mark_shortcut(text, acc)\n\t\t\tself.lp.PrintStyled(\"reverse\", \" \"+text+\" \")\n\t\t}\n\t}\n\tfor _, title := range self.tabs {\n\t\tdraw_tab(utils.Capitalize(title), title, string([]rune(title)[0]))\n\t}\n\tself.lp.Println(\"\\x1b[m\")\n}\n\nfunc center_string(x string, width int) string {\n\tl := wcswidth.Stringwidth(x)\n\tspaces := int(float64(width-l) / 2)\n\treturn strings.Repeat(\" \", utils.Max(0, spaces)) + x + strings.Repeat(\" \", utils.Max(0, width-(spaces+l)))\n}\n\nfunc (self *handler) draw_theme_demo() {\n\tssz, err := self.lp.ScreenSize()\n\tif err != nil {\n\t\treturn\n\t}\n\ttheme := self.themes_list.CurrentTheme()\n\tif theme == nil {\n\t\treturn\n\t}\n\txstart := self.themes_list.max_width + 3\n\tsz := int(ssz.WidthCells) - xstart\n\tif sz < 20 {\n\t\treturn\n\t}\n\tsz--\n\ty := 0\n\tcolors := strings.Split(`black red green yellow blue magenta cyan white`, ` `)\n\ttrunc := sz/8 - 1\n\tpat := regexp.MustCompile(`\\s+`)\n\n\tnext_line := func() {\n\t\tself.lp.QueueWriteString(\"\\r\")\n\t\ty++\n\t\tself.lp.MoveCursorTo(xstart, y+1)\n\t\tself.lp.QueueWriteString(SEPARATOR + \" \")\n\t}\n\n\twrite_para := func(text string) {\n\t\ttext = pat.ReplaceAllLiteralString(text, \" \")\n\t\tfor text != \"\" {\n\t\t\tt, sp := wcswidth.TruncateToVisualLengthWithWidth(text, sz)\n\t\t\tself.lp.QueueWriteString(t)\n\t\t\tnext_line()\n\t\t\ttext = text[sp:]\n\t\t}\n\t}\n\n\twrite_colors := func(bg string) {\n\t\tfor _, intense := range []bool{false, true} {\n\t\t\tbuf := strings.Builder{}\n\t\t\tbuf.Grow(1024)\n\t\t\tfor _, c := range colors {\n\t\t\t\ts := c\n\t\t\t\tif intense {\n\t\t\t\t\ts = \"bright-\" + s\n\t\t\t\t}\n\t\t\t\tsTrunc := s\n\t\t\t\tif len(sTrunc) > trunc {\n\t\t\t\t\tsTrunc = sTrunc[:trunc]\n\t\t\t\t}\n\t\t\t\tbuf.WriteString(self.lp.SprintStyled(\"fg=\"+s, sTrunc))\n\t\t\t\tbuf.WriteString(\" \")\n\t\t\t}\n\t\t\ttext := strings.TrimSpace(buf.String())\n\t\t\tif bg == \"\" {\n\t\t\t\tself.lp.QueueWriteString(text)\n\t\t\t} else {\n\t\t\t\ts := bg\n\t\t\t\tif intense {\n\t\t\t\t\ts = \"bright-\" + s\n\t\t\t\t}\n\t\t\t\tself.lp.PrintStyled(\"bg=\"+s, text)\n\t\t\t}\n\t\t\tnext_line()\n\t\t}\n\t\tnext_line()\n\t}\n\tself.lp.MoveCursorTo(1, 1)\n\tnext_line()\n\tself.lp.PrintStyled(\"fg=green bold\", center_string(theme.Name(), sz))\n\tnext_line()\n\tif theme.Author() != \"\" {\n\t\tself.lp.PrintStyled(\"italic\", center_string(theme.Author(), sz))\n\t\tnext_line()\n\t}\n\tif theme.Blurb() != \"\" {\n\t\tnext_line()\n\t\twrite_para(theme.Blurb())\n\t\tnext_line()\n\t}\n\twrite_colors(\"\")\n\tfor _, bg := range colors {\n\t\twrite_colors(bg)\n\t}\n}\n\n// }}}\n\n// accepting {{{\n\nfunc (self *handler) on_accepting_key_event(ev *loop.KeyEvent) error {\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"q\") || ev.MatchesPressOrRepeat(\"esc\") || ev.MatchesPressOrRepeat(\"shift+q\") {\n\t\tev.Handled = true\n\t\tself.lp.Quit(0)\n\t\treturn nil\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"a\") || ev.MatchesPressOrRepeat(\"shift+a\") {\n\t\tev.Handled = true\n\t\tself.state = BROWSING\n\t\tself.draw_screen()\n\t\treturn nil\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"p\") || ev.MatchesPressOrRepeat(\"shift+p\") {\n\t\tev.Handled = true\n\t\tself.themes_list.CurrentTheme().SaveInDir(utils.ConfigDir())\n\t\tself.update_recent()\n\t\tself.lp.Quit(0)\n\t\treturn nil\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"m\") || ev.MatchesPressOrRepeat(\"shift+m\") {\n\t\tev.Handled = true\n\t\tself.themes_list.CurrentTheme().SaveInConf(utils.ConfigDir(), self.opts.ReloadIn, self.opts.ConfigFileName)\n\t\tself.update_recent()\n\t\tself.lp.Quit(0)\n\t\treturn nil\n\t}\n\n\tscheme := func(name string) error {\n\t\tev.Handled = true\n\t\tself.themes_list.CurrentTheme().SaveInFile(utils.ConfigDir(), name)\n\t\tself.update_recent()\n\t\tself.lp.Quit(0)\n\t\treturn nil\n\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"d\") || ev.MatchesPressOrRepeat(\"shift+d\") {\n\t\treturn scheme(kitty.DarkThemeFileName)\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"l\") || ev.MatchesPressOrRepeat(\"shift+l\") {\n\t\treturn scheme(kitty.LightThemeFileName)\n\t}\n\tif ev.MatchesCaseInsensitiveTextOrKey(\"n\") || ev.MatchesPressOrRepeat(\"shift+n\") {\n\t\treturn scheme(kitty.NoPreferenceThemeFileName)\n\t}\n\treturn nil\n}\n\nfunc (self *handler) update_recent() {\n\tif self.themes_list != nil {\n\t\trecent := slices.Clone(self.cached_data.Recent)\n\t\tname := self.themes_list.CurrentTheme().Name()\n\t\trecent = utils.Remove(recent, name)\n\t\trecent = append([]string{name}, recent...)\n\t\tif len(recent) > 20 {\n\t\t\trecent = recent[:20]\n\t\t}\n\t\tself.cached_data.Recent = recent\n\t}\n}\n\nfunc (self *handler) draw_accepting_screen() {\n\tname := self.themes_list.CurrentTheme().Name()\n\tname = self.lp.SprintStyled(\"fg=green bold\", name)\n\tkc := self.lp.SprintStyled(\"italic\", self.opts.ConfigFileName)\n\n\tac := func(x string) string {\n\t\treturn self.lp.SprintStyled(\"fg=red underline=true\", x)\n\t}\n\tself.lp.AllowLineWrapping(true)\n\tdefer self.lp.AllowLineWrapping(false)\n\tself.lp.Printf(`You have chosen the %s theme`, name)\n\tself.lp.Println()\n\tself.lp.Println()\n\tself.lp.Println(`What would you like to do?`)\n\tself.lp.Println()\n\tself.lp.Printf(` %sodify %s to load %s`, ac(\"M\"), kc, name)\n\tself.lp.Println()\n\tself.lp.Println()\n\tself.lp.Printf(` %slace the theme file in %s but do not modify %s`, ac(\"P\"), utils.ConfigDir(), kc)\n\tself.lp.Println()\n\tself.lp.Println()\n\tself.lp.Printf(` Save as colors to use when the OS switches to:`)\n\tself.lp.Println()\n\tself.lp.Printf(`   %sark mode`, ac(\"D\"))\n\tself.lp.Println()\n\tself.lp.Printf(`   %sight mode`, ac(\"L\"))\n\tself.lp.Println()\n\tself.lp.Printf(`   %so preference mode`, ac(\"N\"))\n\tself.lp.Println()\n\tself.lp.Println()\n\tself.lp.Printf(` %sbort and return to list of themes`, ac(\"A\"))\n\tself.lp.Println()\n\tself.lp.Println()\n\tself.lp.Printf(` %suit`, ac(\"Q\"))\n\tself.lp.Println()\n}\n\n// }}}\n\n// searching {{{\n\nfunc (self *handler) update_search() {\n\ttext := self.rl.AllText()\n\tif self.themes_list.UpdateSearch(text) {\n\t\tself.set_colors_to_current_theme()\n\t\tself.draw_screen()\n\t} else {\n\t\tself.draw_search_bar()\n\t}\n}\n\nfunc (self *handler) on_text(text string, a, b bool) error {\n\tif self.state == SEARCHING {\n\t\terr := self.rl.OnText(text, a, b)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tself.update_search()\n\t}\n\treturn nil\n}\n\nfunc (self *handler) on_searching_key_event(ev *loop.KeyEvent) error {\n\tif ev.MatchesPressOrRepeat(\"enter\") {\n\t\tev.Handled = true\n\t\tself.state = BROWSING\n\t\tself.draw_bottom_bar()\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"esc\") {\n\t\tev.Handled = true\n\t\tself.state = BROWSING\n\t\tself.themes_list.UpdateSearch(\"\")\n\t\tself.set_colors_to_current_theme()\n\t\tself.draw_screen()\n\t\treturn nil\n\t}\n\terr := self.rl.OnKeyEvent(ev)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ev.Handled {\n\t\tself.update_search()\n\t}\n\treturn nil\n}\n\n// }}}\n"
  },
  {
    "path": "kittens/transfer/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/transfer/algorithm.c",
    "content": "//go:build exclude_me\n/*\n * algorithm.c\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"binary.h\"\n#include <math.h>\n#include <xxhash.h>\n\nstatic PyObject *RsyncError = NULL;\nstatic const size_t default_block_size = 6 * 1024;\nstatic const size_t signature_block_size = 20;\nvoid log_error(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); }\n\n// hashers {{{\ntypedef void*(*new_hash_t)(void);\ntypedef void(*delete_hash_t)(void*);\ntypedef bool(*reset_hash_t)(void*);\ntypedef bool(*update_hash_t)(void*, const void *input, size_t length);\ntypedef void(*digest_hash_t)(const void*, void *output);\ntypedef uint64_t(*digest_hash64_t)(const void*);\ntypedef uint64_t(*oneshot_hash64_t)(const void*, size_t);\n\ntypedef struct hasher_t {\n    size_t hash_size, block_size;\n    void *state;\n    new_hash_t new;\n    delete_hash_t delete;\n    reset_hash_t reset;\n    update_hash_t update;\n    digest_hash_t digest;\n    digest_hash64_t digest64;\n    oneshot_hash64_t oneshot64;\n} hasher_t;\n\nstatic void xxh64_delete(void* s) { XXH3_freeState(s); }\nstatic bool xxh64_reset(void* s) { return XXH3_64bits_reset(s) == XXH_OK; }\nstatic void* xxh64_create(void) { void *ans = XXH3_createState(); if (ans != NULL) xxh64_reset(ans); return ans; }\nstatic bool xxh64_update(void* s, const void *input, size_t length) { return XXH3_64bits_update(s, input, length) == XXH_OK; }\nstatic uint64_t xxh64_digest64(const void* s) { return XXH3_64bits_digest(s); }\nstatic uint64_t xxh64_oneshot64(const void* s, size_t len) { return XXH3_64bits(s, len); }\nstatic void xxh64_digest(const void* s, void *output) {\n    XXH64_hash_t ans = XXH3_64bits_digest(s);\n    XXH64_canonical_t c;\n    XXH64_canonicalFromHash(&c, ans);\n    memcpy(output, c.digest, sizeof(c.digest));\n}\n\nstatic hasher_t\nxxh64_hasher(void) {\n    hasher_t ans = {\n        .hash_size=sizeof(XXH64_hash_t), .block_size = 64,\n        .new=xxh64_create, .delete=xxh64_delete, .reset=xxh64_reset, .update=xxh64_update, .digest=xxh64_digest,\n        .digest64=xxh64_digest64, .oneshot64=xxh64_oneshot64\n    };\n    return ans;\n}\n\nstatic bool xxh128_reset(void* s) { return XXH3_128bits_reset(s) == XXH_OK; }\nstatic void* xxh128_create(void) { void *ans = XXH3_createState(); if (ans != NULL) xxh128_reset(ans); return ans; }\nstatic bool xxh128_update(void* s, const void *input, size_t length) { return XXH3_128bits_update(s, input, length) == XXH_OK; }\nstatic void xxh128_digest(const void* s, void *output) {\n    XXH128_hash_t ans = XXH3_128bits_digest(s);\n    XXH128_canonical_t c;\n    XXH128_canonicalFromHash(&c, ans);\n    memcpy(output, c.digest, sizeof(c.digest));\n}\n\nstatic hasher_t\nxxh128_hasher(void) {\n    hasher_t ans = {\n        .hash_size=sizeof(XXH128_hash_t), .block_size = 64,\n        .new=xxh128_create, .delete=xxh64_delete, .reset=xxh128_reset, .update=xxh128_update, .digest=xxh128_digest,\n    };\n    return ans;\n}\n\n\ntypedef hasher_t(*hasher_constructor_t)(void);\n// }}}\n\ntypedef struct Rsync {\n    size_t block_size;\n\n    hasher_constructor_t hasher_constructor, checksummer_constructor;\n    hasher_t hasher, checksummer;\n\n    size_t buffer_cap, buffer_sz;\n} Rsync;\n\nstatic void\nfree_rsync(Rsync* r) {\n    if (r->hasher.state) { r->hasher.delete(r->hasher.state); r->hasher.state = NULL; }\n    if (r->checksummer.state) { r->checksummer.delete(r->checksummer.state); r->checksummer.state = NULL; }\n}\n\nstatic const char*\ninit_rsync(Rsync *ans, size_t block_size, int strong_hash_type, int checksum_type) {\n    memset(ans, 0, sizeof(*ans));\n    ans->block_size = block_size;\n    if (strong_hash_type == 0) ans->hasher_constructor = xxh64_hasher;\n    if (checksum_type == 0) ans->checksummer_constructor = xxh128_hasher;\n    if (ans->hasher_constructor == NULL) { free_rsync(ans); return \"Unknown strong hash type\"; }\n    if (ans->checksummer_constructor == NULL) { free_rsync(ans); return \"Unknown checksum type\"; }\n    ans->hasher = ans->hasher_constructor();\n    ans->checksummer = ans->checksummer_constructor();\n    ans->hasher.state = ans->hasher.new();\n    if (ans->hasher.state == NULL) { free_rsync(ans); return \"Out of memory\"; }\n    ans->checksummer.state = ans->checksummer.new();\n    if (ans->checksummer.state == NULL) { free_rsync(ans); return \"Out of memory\"; }\n    return NULL;\n}\n\ntypedef struct rolling_checksum {\n    uint32_t alpha, beta, val, l, first_byte_of_previous_window;\n} rolling_checksum;\n\nstatic const uint32_t _M = (1 << 16);\n\nstatic uint32_t\nrolling_checksum_full(rolling_checksum *self, uint8_t *data, uint32_t len) {\n    uint32_t alpha = 0, beta = 0;\n    self->l = len;\n    for (uint32_t i = 0; i < len; i++) {\n\t\talpha += data[i];\n\t\tbeta += (self->l - i) * data[i];\n    }\n\tself->first_byte_of_previous_window = data[0];\n\tself->alpha = alpha % _M;\n\tself->beta = beta % _M;\n\tself->val = self->alpha + _M*self->beta;\n\treturn self->val;\n}\n\ninline static void\nrolling_checksum_add_one_byte(rolling_checksum *self, uint8_t first_byte, uint8_t last_byte) {\n\tself->alpha = (self->alpha - self->first_byte_of_previous_window + last_byte) % _M;\n\tself->beta = (self->beta - (self->l)*self->first_byte_of_previous_window + self->alpha) % _M;\n\tself->val = self->alpha + _M*self->beta;\n\tself->first_byte_of_previous_window = first_byte;\n}\n\n// Python interface {{{\n\ntypedef struct buffer {\n    uint8_t *data;\n    size_t len, cap;\n} buffer;\n\nstatic bool\nensure_space(buffer *b, size_t amt) {\n    const size_t len = b->len;\n    if (amt > 0 && b->cap < len + amt) {\n        size_t newcap = MAX(b->cap * 2, len + (amt * 2));\n        b->data = realloc(b->data, newcap);\n        if (b->data == NULL) { PyErr_NoMemory(); return false; }\n        b->cap = newcap;\n    }\n    return true;\n}\n\nstatic bool\nwrite_to_buffer(buffer *b, void *data, size_t len) {\n    if (!ensure_space(b, len)) return false;\n    memcpy(b->data + b->len, data, len);\n    b->len += len;\n    return true;\n}\n\nstatic void\nshift_left(buffer *b, size_t amt) {\n    if (amt > b->len) amt = b->len;\n    if (amt > 0) {\n        b->len -= amt;\n        memmove(b->data, b->data + amt, b->len);\n    }\n}\n\n// Patcher {{{\ntypedef struct {\n    PyObject_HEAD\n    rolling_checksum rc;\n    uint64_t signature_idx;\n    size_t total_data_in_delta;\n    Rsync rsync;\n    buffer buf, block_buf;\n    PyObject *block_buf_view;\n    bool checksum_done;\n} Patcher;\n\nstatic int\nPatcher_init(PyObject *s, PyObject *args, PyObject *kwds) {\n    Patcher *self = (Patcher*)s;\n    static char *kwlist[] = {\"expected_input_size\", NULL};\n    unsigned long long expected_input_size = 0;\n    if (!PyArg_ParseTupleAndKeywords(args, kwds, \"|K\", kwlist, &expected_input_size)) return -1;\n    self->rsync.block_size = default_block_size;\n    if (expected_input_size > 0) {\n        self->rsync.block_size = (size_t)round(sqrt((double)expected_input_size));\n    }\n    const char *err = init_rsync(&self->rsync, self->rsync.block_size, 0, 0);\n    if (err != NULL) { PyErr_SetString(RsyncError, err); return -1; }\n    self->block_buf.cap = self->rsync.block_size;\n    self->block_buf.data = malloc(self->rsync.block_size);\n    if (self->block_buf.data == NULL) { PyErr_NoMemory(); return -1; }\n    if (!(self->block_buf_view = PyMemoryView_FromMemory((char*)self->block_buf.data, self->rsync.block_size, PyBUF_WRITE))) return -1;\n    return 0;\n}\n\nstatic void\nPatcher_dealloc(PyObject *self) {\n    Patcher *p = (Patcher*)self;\n    if (p->buf.data) free(p->buf.data);\n    Py_CLEAR(p->block_buf_view);\n    if (p->block_buf.data) free(p->block_buf.data);\n    free_rsync(&p->rsync);\n    Py_TYPE(self)->tp_free(self);\n}\n\nstatic PyObject*\nsignature_header(Patcher *self, PyObject *a2) {\n    RAII_PY_BUFFER(dest);\n    if (PyObject_GetBuffer(a2, &dest, PyBUF_WRITEABLE) == -1) return NULL;\n    static const ssize_t header_size = 12;\n    if (dest.len < header_size) {\n        PyErr_SetString(RsyncError, \"Output buffer is too small\");\n    }\n    uint8_t *o = dest.buf;\n    le16enc(o, 0); // version\n    le16enc(o + 2, 0);  // checksum type\n    le16enc(o + 4, 0);  // strong hash type\n    le16enc(o + 6, 0);  // weak hash type\n    le32enc(o + 8, self->rsync.block_size);  // block size\n    return PyLong_FromSsize_t(header_size);\n}\n\nstatic PyObject*\nsign_block(Patcher *self, PyObject *args) {\n    PyObject *a1, *a2;\n    if (!PyArg_ParseTuple(args, \"OO\", &a1, &a2)) return NULL;\n    RAII_PY_BUFFER(src); RAII_PY_BUFFER(dest);\n    if (PyObject_GetBuffer(a1, &src, PyBUF_SIMPLE) == -1) return NULL;\n    if (PyObject_GetBuffer(a2, &dest, PyBUF_WRITEABLE) == -1) return NULL;\n    if (dest.len < (ssize_t)signature_block_size) {\n        PyErr_SetString(RsyncError, \"Output buffer is too small\");\n    }\n    self->rsync.hasher.reset(self->rsync.hasher.state);\n    if (!self->rsync.hasher.update(self->rsync.hasher.state, src.buf, src.len)) { PyErr_SetString(PyExc_ValueError, \"String hashing failed\"); return NULL; }\n    uint64_t strong_hash = self->rsync.hasher.oneshot64(src.buf, src.len);\n    uint32_t weak_hash = rolling_checksum_full(&self->rc, src.buf, src.len);\n    uint8_t *o = dest.buf;\n    le64enc(o, self->signature_idx++);\n    le32enc(o + 8, weak_hash);\n    le64enc(o + 12, strong_hash);\n    return PyLong_FromSize_t(signature_block_size);\n}\n\ntypedef enum { OpBlock, OpData, OpHash, OpBlockRange } OpType;\n\ntypedef struct Operation {\n    OpType type;\n    uint64_t block_index, block_index_end;\n    struct { uint8_t *buf; size_t len; } data;\n} Operation;\n\nstatic size_t\nunserialize_op(uint8_t *data, size_t len, Operation *op) {\n    size_t consumed = 0;\n    switch ((OpType)(data[0])) {\n        case OpBlock:\n            consumed = 9;\n            if (len < consumed) return 0;\n            op->block_index = le64dec(data + 1);\n            break;\n        case OpBlockRange:\n            consumed = 13;\n            if (len < consumed) return 0;\n            op->block_index = le64dec(data + 1);\n            op->block_index_end = op->block_index + le32dec(data + 9);\n            break;\n        case OpHash:\n            consumed = 3;\n            if (len < consumed) return 0;\n            op->data.len = le16dec(data + 1);\n            if (len < consumed + op->data.len) return 0;\n            op->data.buf = data + 3;\n            consumed += op->data.len;\n            break;\n        case OpData:\n            consumed = 5;\n            if (len < consumed) return 0;\n            op->data.len = le32dec(data + 1);\n            if (len < consumed + op->data.len) return 0;\n            op->data.buf = data + 5;\n            consumed += op->data.len;\n            break;\n    }\n    if (consumed) op->type = data[0];\n    return consumed;\n}\n\nstatic bool\nwrite_block(Patcher *self, uint64_t block_index, PyObject *read, PyObject *write) {\n    RAII_PyObject(pos, PyLong_FromUnsignedLongLong((unsigned long long)(self->rsync.block_size * block_index)));\n    if (!pos) return false;\n    RAII_PyObject(ret, PyObject_CallFunctionObjArgs(read, pos, self->block_buf_view, NULL));\n    if (ret == NULL) return false;\n    if (!PyLong_Check(ret)) { PyErr_SetString(PyExc_TypeError, \"read callback function did not return an integer\"); return false; }\n    size_t n = PyLong_AsSize_t(ret);\n    self->rsync.checksummer.update(self->rsync.checksummer.state, self->block_buf.data, n);\n    RAII_PyObject(view, PyMemoryView_FromMemory((char*)self->block_buf.data, n, PyBUF_READ));\n    if (!view) return false;\n    RAII_PyObject(wret, PyObject_CallFunctionObjArgs(write, view, NULL));\n    if (wret == NULL) return false;\n    return true;\n}\n\nstatic void\nbytes_as_hex(const uint8_t *bytes, const size_t len, char *ans) {\n    static const char * hex = \"0123456789abcdef\";\n    char *pout = ans; const uint8_t *pin = bytes;\n    for (; pin < bytes + len; pin++) {\n        *pout++ = hex[(*pin>>4) & 0xF];\n        *pout++ = hex[ *pin     & 0xF];\n    }\n    *pout++ = 0;\n}\n\nstatic bool\napply_op(Patcher *self, Operation op, PyObject *read, PyObject *write) {\n    switch (op.type) {\n        case OpBlock:\n            return write_block(self, op.block_index, read, write);\n        case OpBlockRange:\n            for (size_t i = op.block_index; i <= op.block_index_end; i++) {\n                if (!write_block(self, i, read, write)) return false;\n            }\n            return true;\n        case OpData: {\n            self->total_data_in_delta += op.data.len;\n            self->rsync.checksummer.update(self->rsync.checksummer.state, op.data.buf, op.data.len);\n            RAII_PyObject(view, PyMemoryView_FromMemory((char*)op.data.buf, op.data.len, PyBUF_READ));\n            if (!view) return false;\n            RAII_PyObject(wret, PyObject_CallFunctionObjArgs(write, view, NULL));\n            if (!wret) return false;\n        } return true;\n        case OpHash: {\n            uint8_t actual[64];\n            if (op.data.len != self->rsync.checksummer.hash_size) { PyErr_SetString(RsyncError, \"checksum digest not the correct size\"); return false; }\n            self->rsync.checksummer.digest(self->rsync.checksummer.state, actual);\n            if (memcmp(actual, op.data.buf, self->rsync.checksummer.hash_size) != 0) {\n                char hexdigest[129];\n                bytes_as_hex(actual, self->rsync.checksummer.hash_size, hexdigest);\n                RAII_PyObject(h1, PyUnicode_FromStringAndSize(hexdigest, 2*self->rsync.checksummer.hash_size));\n                bytes_as_hex(op.data.buf, op.data.len, hexdigest);\n                RAII_PyObject(h2, PyUnicode_FromStringAndSize(hexdigest, 2*self->rsync.checksummer.hash_size));\n                PyErr_Format(RsyncError, \"Failed to verify overall file checksum actual: %S != expected: %S, this usually happens because one of the involved files was altered while the operation was in progress.\", h1, h2);\n                return false;\n            }\n            self->checksum_done = true;\n        } return true;\n    }\n    PyErr_SetString(RsyncError, \"Unknown operation type\");\n    return false;\n}\n\nstatic PyObject*\napply_delta_data(Patcher *self, PyObject *args) {\n    PyObject *read, *write;\n    RAII_PY_BUFFER(data);\n    if (!PyArg_ParseTuple(args, \"y*OO\", &data, &read, &write)) return NULL;\n    if (!write_to_buffer(&self->buf, data.buf, data.len)) return NULL;\n    size_t pos = 0;\n    Operation op = {0};\n    while (pos < self->buf.len) {\n        size_t consumed = unserialize_op(self->buf.data + pos, self->buf.len - pos, &op);\n        if (!consumed) { break; }\n        pos += consumed;\n        if (!apply_op(self, op, read, write)) break;\n    }\n    shift_left(&self->buf, pos);\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nfinish_delta_data(Patcher *self, PyObject *args UNUSED) {\n    if (self->buf.len > 0) { PyErr_Format(RsyncError, \"%zu bytes of unused delta data\", self->buf.len); return NULL; }\n    if (!self->checksum_done) { PyErr_SetString(RsyncError, \"The checksum was not received at the end of the delta data\"); return NULL; }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nPatcher_block_size(Patcher* self, void* closure UNUSED) { return PyLong_FromSize_t(self->rsync.block_size); }\nstatic PyObject*\nPatcher_total_data_in_delta(Patcher* self, void* closure UNUSED) { return PyLong_FromSize_t(self->total_data_in_delta); }\n\nPyGetSetDef Patcher_getsets[] = {\n    {\"block_size\", (getter)Patcher_block_size, NULL, NULL, NULL},\n    {\"total_data_in_delta\", (getter)Patcher_total_data_in_delta, NULL, NULL, NULL},\n    {NULL}\n};\n\n\nstatic PyMethodDef Patcher_methods[] = {\n    METHODB(sign_block, METH_VARARGS),\n    METHODB(signature_header, METH_O),\n    METHODB(apply_delta_data, METH_VARARGS),\n    METHODB(finish_delta_data, METH_NOARGS),\n    {NULL}  /* Sentinel */\n};\n\n\nPyTypeObject Patcher_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"rsync.Patcher\",\n    .tp_basicsize = sizeof(Patcher),\n    .tp_dealloc = Patcher_dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Patcher\",\n    .tp_methods = Patcher_methods,\n    .tp_new = PyType_GenericNew,\n    .tp_init = Patcher_init,\n    .tp_getset = Patcher_getsets,\n};\n// }}} Patcher\n\n// Differ {{{\ntypedef struct Signature { uint64_t index, strong_hash; } Signature;\n\ntypedef struct SignatureVal {\n    Signature sig, *weak_hash_collisions;\n    size_t len, cap;\n} SignatureVal;\n#define NAME SignatureMap\n#define KEY_TY int\n#define VAL_TY SignatureVal\nstatic void free_signature_val(SignatureVal x) { free(x.weak_hash_collisions); }\n#define VAL_DTOR_FN free_signature_val\n#include \"kitty-verstable.h\"\n\ntypedef struct Differ {\n    PyObject_HEAD\n    rolling_checksum rc;\n    uint64_t signature_idx;\n    Rsync rsync;\n    bool signature_header_parsed;\n    buffer buf;\n    SignatureMap signature_map;\n\n    PyObject *read, *write;\n    bool written, finished;\n    struct { size_t pos, sz; } window, data;\n    Operation pending_op; bool has_pending;\n    uint8_t checksum[32];\n} Differ;\n\nstatic int\nDiffer_init(PyObject *s, PyObject *args, PyObject *kwds) {\n    Differ *self = (Differ*)s;\n    static char *kwlist[] = {NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kwds, \"\", kwlist)) return -1;\n    const char *err = init_rsync(&self->rsync, default_block_size, 0, 0);\n    if (err != NULL) { PyErr_SetString(RsyncError, err); return -1; }\n    vt_init(&self->signature_map);\n    return 0;\n}\n\nstatic void\nDiffer_dealloc(PyObject *self) {\n    Differ *p = (Differ*)self;\n    if (p->buf.data) free(p->buf.data);\n    free_rsync(&p->rsync);\n    vt_cleanup(&p->signature_map);\n    Py_TYPE(self)->tp_free(self);\n}\n\nstatic void\nparse_signature_header(Differ *self) {\n    if (self->buf.len < 12) return;\n    uint8_t *p = self->buf.data;\n    uint32_t x;\n    if ((x = le16dec(p)) != 0) {\n        PyErr_Format(RsyncError, \"Invalid version in signature header: %u\", x); return;\n    } p += 2;\n    if ((x = le16dec(p)) != 0) {\n        PyErr_Format(RsyncError, \"Invalid checksum type in signature header: %u\", x); return;\n    } p += 2;\n    if ((x = le16dec(p)) != 0) {\n        PyErr_Format(RsyncError, \"Invalid strong hash type in signature header: %u\", x); return;\n    } p += 2;\n    if ((x = le16dec(p)) != 0) {\n        PyErr_Format(RsyncError, \"Invalid weak hash type in signature header: %u\", x); return;\n    } p += 2;\n    const char *err = init_rsync(&self->rsync, le32dec(p), 0, 0);\n    if (err != NULL) { PyErr_SetString(RsyncError, err); return; }\n    p += 4;\n    shift_left(&self->buf, p - self->buf.data);\n    self->signature_header_parsed = true;\n}\n\nstatic bool\nadd_collision(SignatureVal *sm, Signature s) {\n    if (sm->cap < sm->len + 1) {\n        size_t new_cap = MAX(sm->cap * 2, 8u);\n        sm->weak_hash_collisions = realloc(sm->weak_hash_collisions, new_cap * sizeof(sm->weak_hash_collisions[0]));\n        if (!sm->weak_hash_collisions) { PyErr_NoMemory(); return false; }\n        sm->cap = new_cap;\n    }\n    sm->weak_hash_collisions[sm->len++] = s;\n    return true;\n}\n\nstatic size_t\nparse_signature_block(Differ *self, uint8_t *data, size_t len) {\n    if (len < 20) return 0;\n    int weak_hash = le32dec(data + 8);\n    SignatureMap_itr i = vt_get(&self->signature_map, weak_hash);\n    if (vt_is_end(i)) {\n        SignatureVal s = {0};\n        s.sig.index = le64dec(data);\n        s.sig.strong_hash = le64dec(data+12);\n        vt_insert(&self->signature_map, weak_hash, s);\n    } else {\n        if (!add_collision(&i.data->val, (Signature){.index=le64dec(data), .strong_hash=le64dec(data+12)})) return 0;\n    }\n    return 20;\n}\n\nstatic PyObject*\nadd_signature_data(Differ *self, PyObject *args) {\n    RAII_PY_BUFFER(data);\n    if (!PyArg_ParseTuple(args, \"y*\", &data)) return NULL;\n    if (!write_to_buffer(&self->buf, data.buf, data.len)) return NULL;\n    if (!self->signature_header_parsed) {\n        parse_signature_header(self);\n        if (PyErr_Occurred()) return NULL;\n        if (!self->signature_header_parsed) { Py_RETURN_NONE; }\n    }\n    size_t pos = 0;\n    while (pos < self->buf.len) {\n        size_t consumed = parse_signature_block(self, self->buf.data + pos, self->buf.len - pos);\n        if (!consumed) { break; }\n        pos += consumed;\n    }\n    shift_left(&self->buf, pos);\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nfinish_signature_data(Differ *self, PyObject *args UNUSED) {\n    if (self->buf.len > 0) { PyErr_Format(RsyncError, \"%zu bytes of unused signature data\", self->buf.len); return NULL; }\n    self->buf.len = 0;\n    self->buf.cap = 8 * self->rsync.block_size;\n    self->buf.data = realloc(self->buf.data, self->buf.cap);\n    if (!self->buf.data) return PyErr_NoMemory();\n    Py_RETURN_NONE;\n}\n\nstatic bool\nsend_op(Differ *self, Operation *op) {\n    uint8_t metadata[32];\n    size_t len = 0;\n    metadata[0] = op->type;\n    switch (op->type) {\n        case OpBlock:\n            le64enc(metadata + 1, op->block_index);\n            len = 9;\n            break;\n        case OpBlockRange:\n            le64enc(metadata + 1, op->block_index);\n            le32enc(metadata + 9, op->block_index_end - op->block_index);\n            len = 13;\n            break;\n        case OpHash:\n            le16enc(metadata + 1, op->data.len);\n            memcpy(metadata + 3, op->data.buf, op->data.len);\n            len = 3 + op->data.len;\n            break;\n        case OpData:\n            le32enc(metadata + 1, op->data.len);\n            len = 5;\n            break;\n    }\n    RAII_PyObject(mv, PyMemoryView_FromMemory((char*)metadata, len, PyBUF_READ));\n    RAII_PyObject(ret, PyObject_CallFunctionObjArgs(self->write, mv, NULL));\n    if (ret == NULL) return false;\n    if (op->type == OpData) {\n        RAII_PyObject(mv, PyMemoryView_FromMemory((char*)op->data.buf, op->data.len, PyBUF_READ));\n        RAII_PyObject(ret, PyObject_CallFunctionObjArgs(self->write, mv, NULL));\n        if (ret == NULL) return false;\n    }\n    self->written = true;\n    return true;\n}\n\nstatic bool\nsend_pending(Differ *self) {\n    bool ret = true;\n    if (self->has_pending) {\n        ret = send_op(self, &self->pending_op);\n        self->has_pending = false;\n    }\n    return ret;\n}\n\nstatic bool\nsend_data(Differ *self) {\n    if (self->data.sz > 0) {\n        if (!send_pending(self)) return false;\n        Operation op = {.type=OpData};\n        op.data.buf = self->buf.data + self->data.pos;\n        op.data.len = self->data.sz;\n        self->data.pos += self->data.sz;\n        self->data.sz = 0;\n        return send_op(self, &op);\n    }\n    return true;\n}\n\nstatic bool\nensure_idx_valid(Differ *self, size_t idx) {\n    if (idx < self->buf.len) return true;\n    if (idx >= self->buf.cap) {\n\t\t// need to wrap the buffer, so send off any data present behind the window\n        if (!send_data(self)) return false;\n\t\t// copy the window and any data present after it to the start of the buffer\n\t\tsize_t distance_from_window_pos = idx - self->window.pos;\n\t\tsize_t amt_to_copy = self->buf.len - self->window.pos;\n        memmove(self->buf.data, self->buf.data + self->window.pos, amt_to_copy);\n        self->buf.len = amt_to_copy;\n\t\tself->window.pos = 0;\n\t\tself->data.pos = 0;\n\t\treturn ensure_idx_valid(self, distance_from_window_pos);\n    }\n    RAII_PyObject(mv, PyMemoryView_FromMemory((char*)self->buf.data + self->buf.len, self->buf.cap - self->buf.len, PyBUF_WRITE));\n    if (!mv) return false;\n    RAII_PyObject(ret, PyObject_CallFunctionObjArgs(self->read, mv, NULL));\n    if (!ret) return false;\n    if (!PyLong_Check(ret)) { PyErr_SetString(PyExc_TypeError, \"read callback did not return an integer\"); return false; }\n    size_t n = PyLong_AsSize_t(ret);\n    self->rsync.checksummer.update(self->rsync.checksummer.state, self->buf.data + self->buf.len, n);\n    self->buf.len += n;\n    return self->buf.len > idx;\n}\n\nstatic bool\nfind_strong_hash(const SignatureVal *sm, uint64_t q, uint64_t *block_index) {\n    if (sm->sig.strong_hash == q) { *block_index = sm->sig.index; return true; }\n    for (size_t i = 0; i < sm->len; i++) {\n        if (sm->weak_hash_collisions[i].strong_hash == q) { *block_index = sm->weak_hash_collisions[i].index; return true; }\n    }\n    return false;\n}\n\nstatic bool\nenqueue(Differ *self, Operation op) {\n    switch (op.type) {\n        case OpBlock:\n            if (self->has_pending) {\n                switch (self->pending_op.type) {\n                    case OpBlock:\n                        if (self->pending_op.block_index+1 == op.block_index) {\n                            self->pending_op.type = OpBlockRange;\n                            self->pending_op.block_index_end = op.block_index;\n                            return true;\n                        }\n                        break;\n                    case OpBlockRange:\n                        if (self->pending_op.block_index_end+1 == op.block_index) {\n                            self->pending_op.block_index_end = op.block_index;\n                            return true;\n                        }\n                    case OpHash: case OpData: break;\n                }\n                if (!send_pending(self)) return false;\n            }\n            self->pending_op = op;\n            self->has_pending = true;\n            return true;\n        case OpHash:\n            if (!send_pending(self)) return false;\n            return send_op(self, &op);\n        case OpBlockRange: case OpData:\n            PyErr_SetString(RsyncError, \"enqueue() must never be called with anything other than OpHash and OpBlock\");\n            return false;\n    }\n    return false;\n}\n\nstatic bool\nfinish_up(Differ *self) {\n    if (!send_data(self)) return false;\n    self->data.pos = self->window.pos;\n\tself->data.sz = self->buf.len - self->window.pos;\n    if (!send_data(self)) return false;\n    self->rsync.checksummer.digest(self->rsync.checksummer.state, self->checksum);\n    Operation op = {.type=OpHash};\n    op.data.buf = self->checksum; op.data.len = self->rsync.checksummer.hash_size;\n    if (!enqueue(self, op)) return false;\n    self->finished = true;\n    return true;\n}\n\nstatic bool\nread_next(Differ *self) {\n    if (self->window.sz > 0) {\n        if (!ensure_idx_valid(self, self->window.pos + self->window.sz)) {\n            if (PyErr_Occurred()) return false;\n            return finish_up(self);\n        }\n\t\tself->window.pos++;\n\t\tself->data.sz++;\n        rolling_checksum_add_one_byte(&self->rc, self->buf.data[self->window.pos], self->buf.data[self->window.pos + self->window.sz - 1]);\n    } else {\n        if (!ensure_idx_valid(self, self->window.pos + self->rsync.block_size - 1)) {\n            if (PyErr_Occurred()) return false;\n            return finish_up(self);\n        }\n\t\tself->window.sz = self->rsync.block_size;\n        rolling_checksum_full(&self->rc, self->buf.data + self->window.pos, self->window.sz);\n    }\n    int weak_hash = self->rc.val;\n    uint64_t block_index = 0;\n    SignatureMap_itr i = vt_get(&self->signature_map, weak_hash);\n    if (!vt_is_end(i) && find_strong_hash(&i.data->val, self->rsync.hasher.oneshot64(self->buf.data + self->window.pos, self->window.sz), &block_index)) {\n        if (!send_data(self)) return false;\n        if (!enqueue(self, (Operation){.type=OpBlock, .block_index=block_index})) return false;\n\t\tself->window.pos += self->window.sz;\n\t\tself->data.pos = self->window.pos;\n\t\tself->window.sz = 0;\n    }\n    return true;\n}\n\nstatic PyObject*\nnext_op(Differ *self, PyObject *args) {\n    if (!PyArg_ParseTuple(args, \"OO\", &self->read, &self->write)) return NULL;\n    self->written = false;\n    while (!self->written && !self->finished) {\n        if (!read_next(self)) break;\n    }\n    if (self->finished && !PyErr_Occurred()) {\n        send_pending(self);\n    }\n    self->read = NULL; self->write = NULL;\n    if (PyErr_Occurred()) return NULL;\n    if (self->finished) { Py_RETURN_FALSE; }\n    Py_RETURN_TRUE;\n}\n\nstatic PyMethodDef Differ_methods[] = {\n    METHODB(add_signature_data, METH_VARARGS),\n    METHODB(finish_signature_data, METH_NOARGS),\n    METHODB(next_op, METH_VARARGS),\n    {NULL}  /* Sentinel */\n};\n\n\nPyTypeObject Differ_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"rsync.Differ\",\n    .tp_basicsize = sizeof(Differ),\n    .tp_dealloc = Differ_dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Differ\",\n    .tp_methods = Differ_methods,\n    .tp_new = PyType_GenericNew,\n    .tp_init = Differ_init,\n};\n// }}} Differ\n\n// Hasher {{{\ntypedef struct {\n    PyObject_HEAD\n    hasher_t h;\n    const char *name;\n} Hasher;\n\nstatic int\nHasher_init(PyObject *s, PyObject *args, PyObject *kwds) {\n    Hasher *self = (Hasher*)s;\n    static char *kwlist[] = {\"which\", \"data\", NULL};\n    const char *which = \"xxh3-64\";\n    RAII_PY_BUFFER(data);\n    if (!PyArg_ParseTupleAndKeywords(args, kwds, \"|sy*\", kwlist, &which, &data)) return -1;\n    if (strcmp(which, \"xxh3-64\") == 0) {\n        self->h = xxh64_hasher();\n        self->name = \"xxh3-64\";\n    } else if (strcmp(which, \"xxh3-128\") == 0) {\n        self->h = xxh128_hasher();\n        self->name = \"xxh3-128\";\n    } else {\n        PyErr_Format(PyExc_KeyError, \"Unknown hash type: %s\", which);\n        return -1;\n    }\n    self->h.state = self->h.new();\n    if (self->h.state == NULL) { PyErr_NoMemory(); return -1; }\n    if (data.buf && data.len > 0) {\n        self->h.update(self->h.state, data.buf, data.len);\n    }\n    return 0;\n}\n\nstatic void\nHasher_dealloc(PyObject *self) {\n    Hasher *h = (Hasher*)self;\n    if (h->h.state) { h->h.delete(h->h.state); h->h.state = NULL; }\n    Py_TYPE(self)->tp_free(self);\n}\n\nstatic PyObject*\nreset(Hasher *self, PyObject *args UNUSED) {\n    if (!self->h.reset(self->h.state)) return PyErr_NoMemory();\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nupdate(Hasher *self, PyObject *o) {\n    RAII_PY_BUFFER(data);\n    if (PyObject_GetBuffer(o, &data, PyBUF_SIMPLE) == -1) return NULL;\n    if (data.buf && data.len > 0) {\n        self->h.update(self->h.state, data.buf, data.len);\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\ndigest(Hasher *self, PyObject *args UNUSED) {\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, self->h.hash_size);\n    if (ans) self->h.digest(self->h.state, PyBytes_AS_STRING(ans));\n    return ans;\n}\n\nstatic PyObject*\ndigest64(Hasher *self, PyObject *args UNUSED) {\n    if (self->h.digest64 == NULL) { PyErr_SetString(PyExc_TypeError, \"Does not support 64-bit digests\"); return NULL; }\n    unsigned long long a = self->h.digest64(self->h.state);\n    return PyLong_FromUnsignedLongLong(a);\n}\n\nstatic PyObject*\nhexdigest(Hasher *self, PyObject *args UNUSED) {\n    uint8_t digest[64]; char hexdigest[128];\n    self->h.digest(self->h.state, digest);\n    bytes_as_hex(digest, self->h.hash_size, hexdigest);\n    return PyUnicode_FromStringAndSize(hexdigest, self->h.hash_size * 2);\n}\n\n\nstatic PyObject*\nHasher_digest_size(Hasher* self, void* closure UNUSED) { return PyLong_FromSize_t(self->h.hash_size); }\nstatic PyObject*\nHasher_block_size(Hasher* self, void* closure UNUSED) { return PyLong_FromSize_t(self->h.block_size); }\nstatic PyObject*\nHasher_name(Hasher* self, void* closure UNUSED) { return PyUnicode_FromString(self->name); }\n\nstatic PyMethodDef Hasher_methods[] = {\n    METHODB(update, METH_O),\n    METHODB(digest, METH_NOARGS),\n    METHODB(digest64, METH_NOARGS),\n    METHODB(hexdigest, METH_NOARGS),\n    METHODB(reset, METH_NOARGS),\n    {NULL}  /* Sentinel */\n};\n\nPyGetSetDef Hasher_getsets[] = {\n    {\"digest_size\", (getter)Hasher_digest_size, NULL, NULL, NULL},\n    {\"block_size\", (getter)Hasher_block_size, NULL, NULL, NULL},\n    {\"name\", (getter)Hasher_name, NULL, NULL, NULL},\n    {NULL}\n};\n\n\nPyTypeObject Hasher_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"rsync.Hasher\",\n    .tp_basicsize = sizeof(Hasher),\n    .tp_dealloc = Hasher_dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Hasher\",\n    .tp_methods = Hasher_methods,\n    .tp_new = PyType_GenericNew,\n    .tp_init = Hasher_init,\n    .tp_getset = Hasher_getsets,\n};\n// }}} end Hasher\n\nstatic bool\ncall_ftc_callback(PyObject *callback, char *src, Py_ssize_t key_start, Py_ssize_t key_length, Py_ssize_t val_start, Py_ssize_t val_length) {\n    while(src[key_start] == ';' && key_length > 0 ) { key_start++; key_length--; }\n    RAII_PyObject(k, PyMemoryView_FromMemory(src + key_start, key_length, PyBUF_READ));\n    if (!k) return false;\n    RAII_PyObject(v, PyMemoryView_FromMemory(src + val_start, val_length, PyBUF_READ));\n    if (!v) return false;\n    RAII_PyObject(ret, PyObject_CallFunctionObjArgs(callback, k, v, NULL));\n    return ret != NULL;\n}\n\nstatic PyObject*\nparse_ftc(PyObject *self UNUSED, PyObject *args) {\n    RAII_PY_BUFFER(buf);\n    PyObject *callback;\n    size_t i = 0, key_start = 0, key_length = 0, val_start = 0, val_length = 0;\n    if (!PyArg_ParseTuple(args, \"s*O\", &buf, &callback)) return NULL;\n    char *src = buf.buf;\n    size_t sz = buf.len;\n    if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, \"callback must be callable\"); return NULL; }\n    for (i = 0; i < sz; i++) {\n        char ch = src[i];\n        if (key_length == 0) {\n            if (ch == '=') {\n                key_length = i - key_start;\n                val_start = i + 1;\n            }\n        } else {\n            if (ch == ';') {\n                val_length = i - val_start;\n                if (!call_ftc_callback(callback, src, key_start, key_length, val_start, val_length)) return NULL;\n                key_length = 0; key_start = i + 1; val_start = 0;\n            }\n        }\n    }\n    if (key_length && val_start) {\n        val_length = sz - val_start;\n        if (!call_ftc_callback(callback, src, key_start, key_length, val_start, val_length)) return NULL;\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npyxxh128_hash(PyObject *self UNUSED, PyObject *b) {\n    RAII_PY_BUFFER(data);\n    if (PyObject_GetBuffer(b, &data, PyBUF_SIMPLE) == -1) return NULL;\n    XXH128_canonical_t c;\n    XXH128_canonicalFromHash(&c, XXH3_128bits(data.buf, data.len));\n    return PyBytes_FromStringAndSize((char*)c.digest, sizeof(c.digest));\n}\n\nstatic PyObject*\npyxxh128_hash_with_seed(PyObject *self UNUSED, PyObject *args) {\n    RAII_PY_BUFFER(data);\n    unsigned long long seed;\n    if (!PyArg_ParseTuple(args, \"y*K\", &data, &seed)) return NULL;\n    XXH128_canonical_t c;\n    XXH128_canonicalFromHash(&c, XXH3_128bits_withSeed(data.buf, data.len, seed));\n    return PyBytes_FromStringAndSize((char*)c.digest, sizeof(c.digest));\n}\n\n\nstatic PyMethodDef module_methods[] = {\n    {\"parse_ftc\", parse_ftc, METH_VARARGS, \"\"},\n    {\"xxh128_hash\", pyxxh128_hash, METH_O, \"\"},\n    {\"xxh128_hash_with_seed\", pyxxh128_hash_with_seed, METH_VARARGS, \"\"},\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nstatic int\nexec_module(PyObject *m) {\n    RsyncError = PyErr_NewException(\"rsync.RsyncError\", NULL, NULL);\n    if (RsyncError == NULL) return -1;\n    PyModule_AddObject(m, \"RsyncError\", RsyncError);\n#define T(which) if (PyType_Ready(& which##_Type) < 0) return -1; Py_INCREF(&which##_Type);\\\n    if (PyModule_AddObject(m, #which, (PyObject *) &which##_Type) < 0) return -1;\n    T(Hasher); T(Patcher); T(Differ);\n#undef T\n    return 0;\n}\n\nIGNORE_PEDANTIC_WARNINGS\nstatic PyModuleDef_Slot slots[] = { {Py_mod_exec, (void*)exec_module}, {0, NULL} };\nEND_IGNORE_PEDANTIC_WARNINGS\n\nstatic struct PyModuleDef module = {\n    .m_base = PyModuleDef_HEAD_INIT,\n    .m_name = \"rsync\",   /* name of module */\n    .m_doc = NULL,\n    .m_slots = slots,\n    .m_methods = module_methods\n};\n\nEXPORTED PyMODINIT_FUNC\nPyInit_rsync(void) {\n    return PyModuleDef_Init(&module);\n}\n// }}}\n"
  },
  {
    "path": "kittens/transfer/ftc.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage transfer\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype Serializable interface {\n\tString() string\n\tMarshalJSON() ([]byte, error)\n}\n\ntype Unserializable interface {\n\tSetString(string) error\n}\n\ntype Action int // enum\n\nvar _ Serializable = Action_cancel\nvar _ Unserializable = (*Action)(nil)\n\nconst (\n\tAction_invalid Action = iota\n\tAction_file\n\tAction_data\n\tAction_end_data\n\tAction_receive\n\tAction_send\n\tAction_cancel\n\tAction_status\n\tAction_finish\n)\n\ntype Compression int // enum\n\nvar _ Serializable = Compression_none\nvar _ Unserializable = (*Compression)(nil)\n\nconst (\n\tCompression_none Compression = iota\n\tCompression_zlib\n)\n\ntype FileType int // enum\n\nvar _ Serializable = FileType_regular\nvar _ Unserializable = (*FileType)(nil)\n\nconst (\n\tFileType_regular FileType = iota\n\tFileType_symlink\n\tFileType_directory\n\tFileType_link\n)\n\nfunc (self FileType) ShortText() string {\n\tswitch self {\n\tcase FileType_regular:\n\t\treturn \"fil\"\n\tcase FileType_directory:\n\t\treturn \"dir\"\n\tcase FileType_symlink:\n\t\treturn \"sym\"\n\tcase FileType_link:\n\t\treturn \"lnk\"\n\t}\n\treturn \"und\"\n}\n\nfunc (self FileType) Color() string {\n\tswitch self {\n\tcase FileType_regular:\n\t\treturn \"yellow\"\n\tcase FileType_directory:\n\t\treturn \"magenta\"\n\tcase FileType_symlink:\n\t\treturn \"blue\"\n\tcase FileType_link:\n\t\treturn \"green\"\n\t}\n\treturn \"\"\n}\n\ntype TransmissionType int // enum\n\nvar _ Serializable = TransmissionType_simple\nvar _ Unserializable = (*TransmissionType)(nil)\n\nconst (\n\tTransmissionType_simple TransmissionType = iota\n\tTransmissionType_rsync\n)\n\ntype QuietLevel int // enum\n\nvar _ Serializable = Quiet_none\nvar _ Unserializable = (*QuietLevel)(nil)\n\nconst (\n\tQuiet_none             QuietLevel = iota // 0\n\tQuiet_acknowledgements                   // 1\n\tQuiet_errors                             // 2\n)\n\ntype FileTransmissionCommand struct {\n\tAction      Action           `json:\"ac,omitempty\"`\n\tCompression Compression      `json:\"zip,omitempty\"`\n\tFtype       FileType         `json:\"ft,omitempty\"`\n\tTtype       TransmissionType `json:\"tt,omitempty\"`\n\tQuiet       QuietLevel       `json:\"q,omitempty\"`\n\n\tId          string        `json:\"id,omitempty\"`\n\tFile_id     string        `json:\"fid,omitempty\"`\n\tBypass      string        `json:\"pw,omitempty\" encoding:\"base64\"`\n\tName        string        `json:\"n,omitempty\" encoding:\"base64\"`\n\tStatus      string        `json:\"st,omitempty\" encoding:\"base64\"`\n\tParent      string        `json:\"pr,omitempty\"`\n\tMtime       time.Duration `json:\"mod,omitempty\"`\n\tPermissions fs.FileMode   `json:\"prm,omitempty\"`\n\tSize        int64         `json:\"sz,omitempty\" default:\"-1\"`\n\n\tData []byte `json:\"d,omitempty\"`\n}\n\nvar ftc_field_map = sync.OnceValue(func() map[string]reflect.StructField {\n\tans := make(map[string]reflect.StructField)\n\tself := FileTransmissionCommand{}\n\tv := reflect.ValueOf(self)\n\ttyp := v.Type()\n\tfields := reflect.VisibleFields(typ)\n\tfor _, field := range fields {\n\t\tif name := field.Tag.Get(\"json\"); name != \"\" && field.IsExported() {\n\t\t\tname, _, _ = strings.Cut(name, \",\")\n\t\t\tans[name] = field\n\t\t}\n\t}\n\treturn ans\n})\n\nvar safe_string_pat = sync.OnceValue(func() *regexp.Regexp {\n\treturn regexp.MustCompile(`[^0-9a-zA-Z_:./@-]`)\n})\n\nfunc safe_string(x string) string {\n\treturn safe_string_pat().ReplaceAllLiteralString(x, ``)\n}\n\nfunc (self FileTransmissionCommand) Serialize(prefix_with_osc_code ...bool) string {\n\tans := strings.Builder{}\n\tv := reflect.ValueOf(self)\n\tfound := false\n\tif len(prefix_with_osc_code) > 0 && prefix_with_osc_code[0] {\n\t\tans.WriteString(strconv.Itoa(kitty.FileTransferCode))\n\t\tfound = true\n\t}\n\tfor name, field := range ftc_field_map() {\n\t\tval := v.FieldByIndex(field.Index)\n\t\tencoded_val := \"\"\n\t\tswitch val.Kind() {\n\t\tcase reflect.String:\n\t\t\tif sval := val.String(); sval != \"\" {\n\t\t\t\tenc := field.Tag.Get(\"encoding\")\n\t\t\t\tswitch enc {\n\t\t\t\tcase \"base64\":\n\t\t\t\t\tencoded_val = base64.RawStdEncoding.EncodeToString(utils.UnsafeStringToBytes(sval))\n\t\t\t\tdefault:\n\t\t\t\t\tencoded_val = safe_string(sval)\n\t\t\t\t}\n\t\t\t}\n\t\tcase reflect.Slice:\n\t\t\tswitch val.Type().Elem().Kind() {\n\t\t\tcase reflect.Uint8:\n\t\t\t\tif bval := val.Bytes(); len(bval) > 0 {\n\t\t\t\t\tencoded_val = base64.RawStdEncoding.EncodeToString(bval)\n\t\t\t\t}\n\t\t\t}\n\t\tcase reflect.Int64:\n\t\t\tif ival := val.Int(); ival != 0 && (ival > 0 || name != \"sz\") {\n\t\t\t\tencoded_val = strconv.FormatInt(ival, 10)\n\t\t\t}\n\t\tdefault:\n\t\t\tif val.CanInterface() {\n\t\t\t\tswitch field := val.Interface().(type) {\n\t\t\t\tcase fs.FileMode:\n\t\t\t\t\tif field = field.Perm(); field != 0 {\n\t\t\t\t\t\tencoded_val = strconv.FormatInt(int64(field), 10)\n\t\t\t\t\t}\n\t\t\t\tcase Serializable:\n\t\t\t\t\tif !val.Equal(reflect.Zero(val.Type())) {\n\t\t\t\t\t\tencoded_val = field.String()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif encoded_val != \"\" {\n\t\t\tif found {\n\t\t\t\tans.WriteString(\";\")\n\t\t\t} else {\n\t\t\t\tfound = true\n\t\t\t}\n\t\t\tans.WriteString(name)\n\t\t\tans.WriteString(\"=\")\n\t\t\tans.WriteString(encoded_val)\n\t\t}\n\t}\n\treturn ans.String()\n}\n\nfunc (self FileTransmissionCommand) String() string {\n\ts := self\n\ts.Data = nil\n\tans, _ := json.Marshal(s)\n\treturn utils.UnsafeBytesToString(ans)\n}\n\nfunc NewFileTransmissionCommand(serialized string) (ans *FileTransmissionCommand, err error) {\n\tans = &FileTransmissionCommand{}\n\tv := reflect.Indirect(reflect.ValueOf(ans))\n\tif err = utils.SetStructDefaults(v); err != nil {\n\t\treturn\n\t}\n\tfield_map := ftc_field_map()\n\tkey_length, key_start, val_start := 0, 0, 0\n\n\thandle_value := func(key, serialized_val string) error {\n\t\tkey = strings.TrimLeft(key, `;`)\n\t\tif field, ok := field_map[key]; ok {\n\t\t\tval := v.FieldByIndex(field.Index)\n\t\t\tswitch val.Kind() {\n\t\t\tcase reflect.String:\n\t\t\t\tswitch field.Tag.Get(\"encoding\") {\n\t\t\t\tcase \"base64\":\n\t\t\t\t\tb, err := base64.RawStdEncoding.DecodeString(serialized_val)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"The field %#v has invalid base64 encoded value with error: %w\", key, err)\n\t\t\t\t\t}\n\t\t\t\t\tval.SetString(utils.UnsafeBytesToString(b))\n\t\t\t\tdefault:\n\t\t\t\t\tval.SetString(safe_string(serialized_val))\n\t\t\t\t}\n\t\t\tcase reflect.Slice:\n\t\t\t\tswitch val.Type().Elem().Kind() {\n\t\t\t\tcase reflect.Uint8:\n\t\t\t\t\tb, err := base64.RawStdEncoding.DecodeString(serialized_val)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"The field %#v has invalid base64 encoded value with error: %w\", key, err)\n\t\t\t\t\t}\n\t\t\t\t\tval.SetBytes(b)\n\t\t\t\t}\n\t\t\tcase reflect.Int64:\n\t\t\t\tb, err := strconv.ParseInt(serialized_val, 10, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"The field %#v has invalid integer value with error: %w\", key, err)\n\t\t\t\t}\n\t\t\t\tval.SetInt(b)\n\t\t\tdefault:\n\t\t\t\tif val.CanAddr() {\n\t\t\t\t\tswitch field := val.Addr().Interface().(type) {\n\t\t\t\t\tcase Unserializable:\n\t\t\t\t\t\terr = field.SetString(serialized_val)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn fmt.Errorf(\"The field %#v has invalid enum value with error: %w\", key, err)\n\t\t\t\t\t\t}\n\t\t\t\t\tcase *fs.FileMode:\n\t\t\t\t\t\tb, err := strconv.ParseUint(serialized_val, 10, 32)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn fmt.Errorf(\"The field %#v has invalid file mode value with error: %w\", key, err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\t*field = fs.FileMode(b).Perm()\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"The field name %#v is not known\", key)\n\t\t}\n\t}\n\n\tfor i := 0; i < len(serialized); i++ {\n\t\tch := serialized[i]\n\t\tif key_length == 0 {\n\t\t\tif ch == '=' {\n\t\t\t\tkey_length = i - key_start\n\t\t\t\tval_start = i + 1\n\t\t\t}\n\t\t} else {\n\t\t\tif ch == ';' {\n\t\t\t\tval_length := i - val_start\n\t\t\t\tif key_length > 0 && val_start > 0 {\n\t\t\t\t\terr = handle_value(serialized[key_start:key_start+key_length], serialized[val_start:val_start+val_length])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tkey_length = 0\n\t\t\t\tkey_start = i + 1\n\t\t\t\tval_start = 0\n\t\t\t}\n\t\t}\n\t}\n\tif key_length > 0 && val_start > 0 {\n\t\terr = handle_value(serialized[key_start:key_start+key_length], serialized[val_start:])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn\n}\n\nfunc split_for_transfer(data []byte, file_id string, mark_last bool, callback func(*FileTransmissionCommand)) {\n\tconst chunk_size = 4096\n\tfor len(data) > 0 {\n\t\tchunk := data\n\t\tif len(chunk) > chunk_size {\n\t\t\tchunk = data[:chunk_size]\n\t\t}\n\t\tdata = data[len(chunk):]\n\t\tcallback(&FileTransmissionCommand{\n\t\t\tAction:  utils.IfElse(mark_last && len(data) == 0, Action_end_data, Action_data),\n\t\t\tFile_id: file_id, Data: chunk})\n\t}\n}\n"
  },
  {
    "path": "kittens/transfer/ftc_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage transfer\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestFTCSerialization(t *testing.T) {\n\tftc := FileTransmissionCommand{}\n\tq := func(expected string) {\n\t\tactual := ftc.Serialize()\n\t\tad := make(map[string]bool)\n\t\tfor x := range strings.SplitSeq(actual, \";\") {\n\t\t\tad[x] = true\n\t\t}\n\t\ted := make(map[string]bool)\n\t\tfor x := range strings.SplitSeq(expected, \";\") {\n\t\t\ted[x] = true\n\t\t}\n\t\tif diff := cmp.Diff(ed, ad); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to Serialize:\\n%s\", diff)\n\t\t}\n\t}\n\tq(\"\")\n\tftc.Action = Action_send\n\tq(\"ac=send\")\n\tftc.File_id = \"fid\"\n\tftc.Name = \"moose\"\n\tftc.Mtime = time.Second\n\tftc.Permissions = 0o600\n\tftc.Data = []byte(\"moose\")\n\tq(\"ac=send;fid=fid;n=bW9vc2U;mod=1000000000;prm=384;d=bW9vc2U\")\n\tn, err := NewFileTransmissionCommand(ftc.Serialize())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tq(n.Serialize())\n\n\tunsafe := \"moo\\x1b;;[?*.-se1\"\n\tif safe_string(unsafe) != \"moo.-se1\" {\n\t\tt.Fatalf(\"safe_string() failed for %#v yielding: %#v\", unsafe, safe_string(unsafe))\n\t}\n}\n"
  },
  {
    "path": "kittens/transfer/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage transfer\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc read_bypass(loc string) (string, error) {\n\tif loc == \"\" {\n\t\treturn \"\", nil\n\t}\n\tfdnum, err := strconv.Atoi(loc)\n\tif err == nil && fdnum >= 0 && fdnum < 256 && loc[0] >= '0' && loc[0] <= '9' {\n\t\tfile := os.NewFile(uintptr(fdnum), loc)\n\t\tdefer file.Close()\n\t\traw, err := io.ReadAll(file)\n\t\treturn utils.UnsafeBytesToString(raw), err\n\t}\n\tif loc == \"-\" {\n\t\traw, err := io.ReadAll(os.Stdin)\n\t\tdefer os.Stdin.Close()\n\t\treturn utils.UnsafeBytesToString(raw), err\n\t}\n\tswitch loc[0] {\n\tcase '.', '~', '/':\n\t\tif loc[0] == '~' {\n\t\t\tloc = utils.Expanduser(loc)\n\t\t}\n\t\traw, err := os.ReadFile(loc)\n\t\treturn utils.UnsafeBytesToString(raw), err\n\tdefault:\n\t\treturn loc, nil\n\t}\n}\n\nfunc main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {\n\tif opts.PermissionsBypass != \"\" {\n\t\tval, err := read_bypass(opts.PermissionsBypass)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\topts.PermissionsBypass = strings.TrimSpace(val)\n\t}\n\tif len(args) == 0 {\n\t\treturn 1, fmt.Errorf(\"Must specify at least one file to transfer\")\n\t}\n\tswitch opts.Direction {\n\tcase \"send\", \"download\":\n\t\terr, rc = send_main(opts, args)\n\tdefault:\n\t\terr, rc = receive_main(opts, args)\n\t}\n\tif err != nil {\n\t\trc = 1\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/transfer/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport sys\n\nusage = 'source_files_or_directories destination_path'\nhelp_text = '''\\\nTransfer files over the TTY device. Can be used to send files between any two\ncomputers provided there is a TTY connection between them, such as over SSH.\nSupports copying files, directories (recursively), symlinks and hardlinks.  Can\neven use an rsync like protocol to copy only changes between files.  When\ncopying multiple files, use the --confirm-paths option to see what exactly will\nbe copied. The easiest way to use this kitten is to first ssh into the remote\ncomputer with the ssh kitten:\n\n.. code::\n\n    $ kitten ssh my-remote-computer\n\nThen, on the remote computer run the transfer kitten to do your copying.\nTo copy a file from the remote computer to the local computer, run:\n\n.. code::\n\n    $ kitten transfer remote-file /path/to/local-file\n\nThis will copy :file:`remote-file` from the remote computer to :file:`/path/to/local-file`\non the local computer.\n\nSimilarly, to copy a file from the local computer to the remote one, run:\n\n.. code::\n\n    $ kitten transfer --direction=upload /path/to/local-file remote-file\n\nThis will copy :file:`/path/to/local-file` from the local computer\nto :file:`remote-file` on the remote computer.\n\nMultiple files can be copied:\n\n.. code::\n\n    $ kitten transfer file1 file2 /path/to/dir/\n\nThis will put :code:`file1` and :code:`file2` into the directory\n:file:`/path/to/dir/` on the local computer.\n\nDirectories can also be copied, recursively:\n\n.. code::\n\n    $ kitten transfer dir1 /path/to/dir/\n\nThis will put :file:`dir1` and all its contents into\n:file:`/path/to/dir/` on the local computer.\n\nNote that when copying multiple files or directories, the destination\nmust be an existing directory on the receiving computer. Relative file\npaths are resolved with respect to the current directory on the computer\nrunning the kitten and the home directory on the other computer. It is\na good idea to use the :option:`--confirm-paths` command line flag to verify\nthe kitten will copy the files you expect it to.\n'''\n\n\ndef option_text() -> str:\n    return '''\\\n--direction -d\ndefault=download\nchoices=upload,download,send,receive\nWhether to send or receive files. :code:`send` or :code:`download` copy files from the computer\non which the kitten is running (usually the remote computer) to the local computer. :code:`receive`\nor :code:`upload` copy files from the local computer to the remote computer.\n\n\n--mode -m\ndefault=normal\nchoices=normal,mirror\nHow to interpret command line arguments. In :code:`mirror` mode all arguments\nare assumed to be files/dirs on the sending computer and they are mirrored onto the\nreceiving computer. Files under the HOME directory are copied to the HOME directory\non the receiving computer even if the HOME directory is different.\nIn :code:`normal` mode the last argument is assumed to be a destination path on the\nreceiving computer. The last argument must be an existing directory unless copying a\nsingle file. When it is a directory it should end with a trailing slash.\n\n\n--compress\ndefault=auto\nchoices=auto,never,always\nWhether to compress data being sent. By default compression is enabled based on the\ntype of file being sent. For files recognized as being already compressed, compression\nis turned off as it just wastes CPU cycles.\n\n\n--permissions-bypass -p\nThe password to use to skip the transfer confirmation popup in kitty. Must match\nthe password set for the :opt:`file_transfer_confirmation_bypass` option in\n:file:`kitty.conf`. Note that leading and trailing whitespace is removed from\nthe password. A password starting with :code:`.`, :code:`/` or :code:`~`\ncharacters is assumed to be a file name to read the password from. A value of\n:code:`-` means read the password from STDIN. A password that is purely a number\nless than 256 is assumed to be the number of a file descriptor from which to\nread the actual password.\n\n\n--confirm-paths -c\ntype=bool-set\nBefore actually transferring files, show a mapping of local file names to remote\nfile names and ask for confirmation.\n\n\n--transmit-deltas -x\ntype=bool-set\nIf a file on the receiving side already exists, use the rsync algorithm to\nupdate it to match the file on the sending side, potentially saving lots of\nbandwidth and also automatically resuming partial transfers. Note that this will\nactually degrade performance on fast links or with small files, so use with care.\n'''\n\n\ndef main(args: list[str]) -> None:\n    raise SystemExit('This should be run as kitten transfer')\n\n\nif __name__ == '__main__':\n    main(sys.argv)\nelif __name__ == '__doc__':\n    from kitty.simple_cli_definitions import CompletionSpec\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = option_text\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Transfer files easily over the TTY device'\n    cd['args_completion'] = CompletionSpec.from_string('type:file group:\"Files\"')\n"
  },
  {
    "path": "kittens/transfer/receive.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage transfer\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/kittens/unicode_input\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/rsync\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\ntype state int\n\nconst (\n\tstate_waiting_for_permission state = iota\n\tstate_waiting_for_file_metadata\n\tstate_transferring\n\tstate_canceled\n)\n\ntype output_file interface {\n\twrite([]byte) (int, error)\n\tclose() error\n\ttell() (int64, error)\n}\n\ntype filesystem_file struct {\n\tf *os.File\n}\n\nfunc (ff *filesystem_file) tell() (int64, error) {\n\treturn ff.f.Seek(0, io.SeekCurrent)\n}\n\nfunc (ff *filesystem_file) close() error {\n\treturn ff.f.Close()\n}\n\nfunc (ff *filesystem_file) write(data []byte) (int, error) {\n\tn, err := ff.f.Write(data)\n\tif err == nil && n < len(data) {\n\t\terr = io.ErrShortWrite\n\t}\n\treturn n, err\n}\n\ntype patch_file struct {\n\tpath      string\n\tsrc, temp *os.File\n\tp         *rsync.Patcher\n}\n\nfunc (pf *patch_file) tell() (int64, error) {\n\tif pf.temp == nil {\n\t\ts, err := os.Stat(pf.path)\n\t\treturn s.Size(), err\n\t}\n\treturn pf.temp.Seek(0, io.SeekCurrent)\n}\n\nfunc (pf *patch_file) close() (err error) {\n\tif pf.p == nil {\n\t\treturn\n\t}\n\terr = pf.p.FinishDelta()\n\tpf.src.Close()\n\tpf.temp.Close()\n\tif err == nil {\n\t\terr = os.Rename(pf.temp.Name(), pf.src.Name())\n\t}\n\tpf.src = nil\n\tpf.temp = nil\n\tpf.p = nil\n\treturn\n}\n\nfunc (pf *patch_file) write(data []byte) (int, error) {\n\tif err := pf.p.UpdateDelta(data); err == nil {\n\t\treturn len(data), nil\n\t} else {\n\t\treturn 0, err\n\t}\n}\n\nfunc new_patch_file(path string, p *rsync.Patcher) (ans *patch_file, err error) {\n\tans = &patch_file{p: p, path: path}\n\tvar f *os.File\n\tif f, err = os.Open(path); err != nil {\n\t\treturn\n\t} else {\n\t\tans.src = f\n\t}\n\tif f, err = os.CreateTemp(filepath.Dir(path), \"\"); err != nil {\n\t\tans.src.Close()\n\t\treturn\n\t} else {\n\t\tans.temp = f\n\t}\n\tans.p.StartDelta(ans.temp, ans.src)\n\treturn\n}\n\ntype remote_file struct {\n\texpected_size                int64\n\texpect_diff                  bool\n\tpatcher                      *rsync.Patcher\n\ttransmit_started_at, done_at time.Time\n\twritten_bytes                int64\n\treceived_bytes               int64\n\tsent_bytes                   int64\n\tftype                        FileType\n\tmtime                        time.Duration\n\tspec_id                      int\n\tpermissions                  fs.FileMode\n\tremote_path                  string\n\tdisplay_name                 string\n\tremote_id, remote_target     string\n\tparent                       string\n\texpanded_local_path          string\n\tfile_id                      string\n\tdecompressor                 utils.StreamDecompressor\n\tcompression_type             Compression\n\tremote_symlink_value         string\n\tactual_file                  output_file\n}\n\nfunc (self *remote_file) close() (err error) {\n\tif self.decompressor != nil {\n\t\terr = self.decompressor(nil, true)\n\t\tself.decompressor = nil\n\t}\n\n\tif self.actual_file != nil {\n\t\taf := self.actual_file\n\t\tself.actual_file = nil\n\t\tcerr := af.close()\n\t\tif err == nil {\n\t\t\terr = cerr\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *remote_file) Write(data []byte) (n int, err error) {\n\tswitch self.ftype {\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"Cannot write data to files of type: %s\", self.ftype)\n\tcase FileType_symlink:\n\t\tself.remote_symlink_value += string(data)\n\t\treturn len(data), nil\n\tcase FileType_regular:\n\t\tif self.actual_file == nil {\n\t\t\tparent := filepath.Dir(self.expanded_local_path)\n\t\t\tif parent != \"\" {\n\t\t\t\tif err = os.MkdirAll(parent, 0o755); err != nil {\n\t\t\t\t\treturn 0, err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif self.expect_diff {\n\t\t\t\tif pf, err := new_patch_file(self.expanded_local_path, self.patcher); err != nil {\n\t\t\t\t\treturn 0, err\n\t\t\t\t} else {\n\t\t\t\t\tself.actual_file = pf\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ff, err := os.Create(self.expanded_local_path); err != nil {\n\t\t\t\t\treturn 0, err\n\t\t\t\t} else {\n\t\t\t\t\tf := filesystem_file{f: ff}\n\t\t\t\t\tself.actual_file = &f\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn self.actual_file.write(data)\n\t}\n}\n\nfunc (self *remote_file) write_data(data []byte, is_last bool) (amt_written int64, err error) {\n\tself.received_bytes += int64(len(data))\n\tvar base, pos int64\n\tdefer func() {\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"Failed writing to %s with error: %w\", self.expanded_local_path, err)\n\t\t}\n\t}()\n\tif self.actual_file != nil {\n\t\tbase, err = self.actual_file.tell()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\terr = self.decompressor(data, is_last)\n\tif is_last {\n\t\tself.decompressor = nil\n\t}\n\tif self.actual_file != nil && err == nil {\n\t\tpos, err = self.actual_file.tell()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t} else {\n\t\tpos = base\n\t}\n\tamt_written = pos - base\n\tif is_last && self.actual_file != nil {\n\t\tcerr := self.actual_file.close()\n\t\tif err == nil {\n\t\t\terr = cerr\n\t\t}\n\t\tself.actual_file = nil\n\t}\n\treturn\n}\n\nfunc syscall_mode(i os.FileMode) (o uint32) {\n\to |= uint32(i.Perm())\n\tif i&os.ModeSetuid != 0 {\n\t\to |= unix.S_ISUID\n\t}\n\tif i&os.ModeSetgid != 0 {\n\t\to |= unix.S_ISGID\n\t}\n\tif i&os.ModeSticky != 0 {\n\t\to |= unix.S_ISVTX\n\t}\n\t// No mapping for Go's ModeTemporary (plan9 only).\n\treturn\n}\n\nfunc (self *remote_file) apply_metadata() {\n\tt := unix.NsecToTimespec(int64(self.mtime))\n\tfor {\n\t\tif err := unix.UtimesNanoAt(unix.AT_FDCWD, self.expanded_local_path, []unix.Timespec{t, t}, unix.AT_SYMLINK_NOFOLLOW); err == nil || !(errors.Is(err, unix.EINTR) || errors.Is(err, unix.EAGAIN)) {\n\t\t\tbreak\n\t\t}\n\t}\n\tif self.ftype == FileType_symlink {\n\t\tfor {\n\t\t\tif err := unix.Fchmodat(unix.AT_FDCWD, self.expanded_local_path, syscall_mode(self.permissions), unix.AT_SYMLINK_NOFOLLOW); err == nil || !(errors.Is(err, unix.EINTR) || errors.Is(err, unix.EAGAIN)) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t} else {\n\t\t_ = os.Chmod(self.expanded_local_path, self.permissions)\n\t}\n}\n\nfunc new_remote_file(opts *Options, ftc *FileTransmissionCommand, file_id uint64) (*remote_file, error) {\n\tspec_id, err := strconv.Atoi(ftc.File_id)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tans := &remote_file{\n\t\texpected_size: ftc.Size, ftype: ftc.Ftype, mtime: ftc.Mtime, spec_id: spec_id, file_id: strconv.FormatUint(file_id, 10),\n\t\tpermissions: ftc.Permissions, remote_path: ftc.Name, display_name: wcswidth.StripEscapeCodes(ftc.Name),\n\t\tremote_id: ftc.Status, remote_target: string(ftc.Data), parent: ftc.Parent,\n\t}\n\tcompression_capable := ftc.Ftype == FileType_regular && ftc.Size > 4096 && should_be_compressed(ftc.Name, opts.Compress)\n\tif compression_capable {\n\t\tans.decompressor = utils.NewStreamDecompressor(zlib.NewReader, ans)\n\t\tans.compression_type = Compression_zlib\n\t} else {\n\t\tans.decompressor = utils.NewStreamDecompressor(nil, ans)\n\t\tans.compression_type = Compression_none\n\t}\n\treturn ans, nil\n}\n\ntype receive_progress_tracker struct {\n\ttotal_size_of_all_files   int64\n\ttotal_bytes_to_transfer   int64\n\ttotal_transferred         int64\n\ttransfered_stats_amt      int64\n\ttransfered_stats_interval time.Duration\n\tstarted_at                time.Time\n\ttransfers                 []Transfer\n\tactive_file               *remote_file\n\tdone_files                []*remote_file\n}\n\nfunc (self *receive_progress_tracker) change_active_file(nf *remote_file) {\n\tnow := time.Now()\n\tself.active_file = nf\n\tnf.transmit_started_at = now\n}\n\nfunc (self *receive_progress_tracker) start_transfer() {\n\tself.started_at = time.Now()\n\tself.transfers = append(self.transfers, Transfer{at: time.Now()})\n}\n\nfunc (self *receive_progress_tracker) file_written(af *remote_file, amt int64, is_done bool) {\n\tif self.active_file != af {\n\t\tself.change_active_file(af)\n\t}\n\taf.written_bytes += amt\n\tself.total_transferred += amt\n\tnow := time.Now()\n\tself.transfers = append(self.transfers, Transfer{amt: amt, at: now})\n\tfor len(self.transfers) > 2 && self.transfers[0].is_too_old(now) {\n\t\tutils.ShiftLeft(self.transfers, 1)\n\t}\n\tself.transfered_stats_interval = now.Sub(self.transfers[0].at)\n\tself.transfered_stats_amt = 0\n\tfor _, t := range self.transfers {\n\t\tself.transfered_stats_amt += t.amt\n\t}\n\tif is_done {\n\t\taf.done_at = now\n\t\tself.done_files = append(self.done_files, af)\n\t}\n\n}\n\ntype manager struct {\n\trequest_id              string\n\tfile_id_counter         uint64\n\tcli_opts                *Options\n\tspec                    []string\n\tdest                    string\n\tbypass                  string\n\tuse_rsync               bool\n\tfailed_specs            map[int]string\n\tspec_counts             map[int]int\n\tremote_home             string\n\tprefix, suffix          string\n\ttransfer_done           bool\n\tfiles                   []*remote_file\n\tfiles_to_be_transferred map[string]*remote_file\n\tstate                   state\n\tprogress_tracker        receive_progress_tracker\n}\n\ntype transmit_iterator = func(queue_write func(string) loop.IdType) (loop.IdType, error)\n\ntype sigwriter struct {\n\twid                     loop.IdType\n\tfile_id, prefix, suffix string\n\tq                       func(string) loop.IdType\n\tamt                     int64\n\tb                       bytes.Buffer\n}\n\nfunc (self *sigwriter) Write(b []byte) (int, error) {\n\tself.b.Write(b)\n\tif self.b.Len() > 4000 {\n\t\tself.flush()\n\t}\n\treturn len(b), nil\n}\n\nfunc (self *sigwriter) flush() {\n\tframe := len(self.prefix) + len(self.suffix)\n\tsplit_for_transfer(self.b.Bytes(), self.file_id, false, func(ftc *FileTransmissionCommand) {\n\t\tself.q(self.prefix)\n\t\tdata := ftc.Serialize(false)\n\t\tself.q(data)\n\t\tself.wid = self.q(self.suffix)\n\t\tself.amt += int64(frame + len(data))\n\t})\n\tself.b.Reset()\n}\n\nvar files_done error = errors.New(\"files done\")\n\nfunc (self *manager) request_files() transmit_iterator {\n\tpos := 0\n\treturn func(queue_write func(string) loop.IdType) (last_write_id loop.IdType, err error) {\n\t\tvar f *remote_file\n\t\tfor pos < len(self.files) {\n\t\t\tf = self.files[pos]\n\t\t\tpos++\n\t\t\tif f.ftype == FileType_directory || (f.ftype == FileType_link && f.remote_target != \"\") {\n\t\t\t\tf = nil\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif f == nil {\n\t\t\treturn 0, files_done\n\t\t}\n\t\tread_signature := self.use_rsync && f.ftype == FileType_regular\n\t\tif read_signature {\n\t\t\tif s, err := os.Lstat(f.expanded_local_path); err == nil {\n\t\t\t\tread_signature = s.Size() > 4096\n\t\t\t} else {\n\t\t\t\tread_signature = false\n\t\t\t}\n\t\t}\n\t\tlast_write_id = self.send(FileTransmissionCommand{\n\t\t\tAction: Action_file, Name: f.remote_path, File_id: f.file_id, Ttype: utils.IfElse(\n\t\t\t\tread_signature, TransmissionType_rsync, TransmissionType_simple), Compression: f.compression_type,\n\t\t}, queue_write)\n\t\tif read_signature {\n\t\t\tfsf, err := os.Open(f.expanded_local_path)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\tdefer fsf.Close()\n\t\t\tf.expect_diff = true\n\t\t\tf.patcher = rsync.NewPatcher(f.expected_size)\n\t\t\toutput := sigwriter{q: queue_write, file_id: f.file_id, prefix: self.prefix, suffix: self.suffix}\n\t\t\ts_it := f.patcher.CreateSignatureIterator(fsf, &output)\n\t\t\tfor {\n\t\t\t\terr = s_it()\n\t\t\t\tif err == io.EOF {\n\t\t\t\t\tbreak\n\t\t\t\t} else if err != nil {\n\t\t\t\t\treturn 0, err\n\t\t\t\t}\n\t\t\t}\n\t\t\toutput.flush()\n\t\t\tf.sent_bytes += output.amt\n\t\t\tlast_write_id = self.send(FileTransmissionCommand{Action: Action_end_data, File_id: f.file_id}, queue_write)\n\t\t}\n\t\treturn\n\t}\n}\n\ntype handler struct {\n\tlp                    *loop.Loop\n\tprogress_update_timer loop.IdType\n\tspinner               *tui.Spinner\n\tcli_opts              *Options\n\tctx                   *markup.Context\n\tmanager               manager\n\tquit_after_write_code int\n\tcheck_paths_printed   bool\n\ttransmit_started      bool\n\tprogress_drawn        bool\n\tmax_name_length       int\n\ttransmit_iterator     transmit_iterator\n\tlast_data_write_id    loop.IdType\n}\n\nfunc (self *manager) send(c FileTransmissionCommand, send func(string) loop.IdType) loop.IdType {\n\tsend(self.prefix)\n\tsend(c.Serialize(false))\n\treturn send(self.suffix)\n}\n\nfunc (self *manager) start_transfer(send func(string) loop.IdType) {\n\tself.send(FileTransmissionCommand{Action: Action_receive, Bypass: self.bypass, Size: int64(len(self.spec))}, send)\n\tfor i, x := range self.spec {\n\t\tself.send(FileTransmissionCommand{Action: Action_file, File_id: strconv.Itoa(i), Name: x}, send)\n\t}\n\tself.progress_tracker.start_transfer()\n}\n\nfunc (self *handler) print_err(err error) {\n\tself.lp.Println(self.ctx.BrightRed(err.Error()))\n}\n\nfunc (self *handler) abort_with_error(err error, delay ...time.Duration) {\n\tif err != nil {\n\t\tself.print_err(err)\n\t}\n\tvar d time.Duration = 5 * time.Second\n\tif len(delay) > 0 {\n\t\td = delay[0]\n\t}\n\tself.lp.Println(`Waiting to ensure terminal cancels transfer, will quit in no more than`, d)\n\tself.progress_drawn = false\n\tself.manager.send(FileTransmissionCommand{Action: Action_cancel}, self.lp.QueueWriteString)\n\tself.manager.state = state_canceled\n\t_, _ = self.lp.AddTimer(d, false, self.do_error_quit)\n}\n\nfunc (self *handler) do_error_quit(loop.IdType) error {\n\tself.lp.Quit(1)\n\treturn nil\n}\n\nfunc (self *manager) finalize_transfer() (err error) {\n\tself.transfer_done = true\n\trid_map := make(map[string]*remote_file)\n\tfor _, f := range self.files {\n\t\trid_map[f.remote_id] = f\n\t}\n\tfor _, f := range self.files {\n\t\tswitch f.ftype {\n\t\tcase FileType_directory:\n\t\t\tif err = os.MkdirAll(f.expanded_local_path, 0o755); err != nil {\n\t\t\t\treturn fmt.Errorf(\"Failed to create directory with error: %w\", err)\n\t\t\t}\n\t\tcase FileType_link:\n\t\t\ttgt, found := rid_map[f.remote_target]\n\t\t\tif !found {\n\t\t\t\treturn fmt.Errorf(`Hard link with remote id: {%s} not found`, f.remote_target)\n\t\t\t}\n\t\t\tif err = os.MkdirAll(filepath.Dir(f.expanded_local_path), 0o755); err == nil {\n\t\t\t\tos.Remove(f.expanded_local_path)\n\t\t\t\terr = os.Link(tgt.expanded_local_path, f.expanded_local_path)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(`Failed to create link with error: %w`, err)\n\t\t\t}\n\t\tcase FileType_symlink:\n\t\t\tlt := f.remote_symlink_value\n\t\t\tif f.remote_target != \"\" {\n\t\t\t\ttgt, found := rid_map[f.remote_target]\n\t\t\t\tif !found {\n\t\t\t\t\treturn fmt.Errorf(`Symbolic link with remote id: {%s} not found`, f.remote_target)\n\t\t\t\t}\n\t\t\t\tlt = tgt.expanded_local_path\n\t\t\t\tif !strings.HasPrefix(f.remote_symlink_value, \"/\") {\n\t\t\t\t\tif lt, err = filepath.Rel(filepath.Dir(f.expanded_local_path), lt); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(`Could not make symlink relative with error: %w`, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif lt == \"\" {\n\t\t\t\treturn fmt.Errorf(\"Symlink %s sent without target\", f.expanded_local_path)\n\t\t\t}\n\t\t\tos.Remove(f.expanded_local_path)\n\t\t\tif err = os.MkdirAll(filepath.Dir(f.expanded_local_path), 0o755); err != nil {\n\t\t\t\treturn fmt.Errorf(\"Failed to create directory with error: %w\", err)\n\t\t\t}\n\t\t\tif err = os.Symlink(lt, f.expanded_local_path); err != nil {\n\t\t\t\treturn fmt.Errorf(`Failed to create symlink with error: %w`, err)\n\t\t\t}\n\t\t}\n\t\tf.apply_metadata()\n\t}\n\treturn\n}\n\nfunc (self *manager) on_file_transfer_response(ftc *FileTransmissionCommand) (err error) {\n\tswitch self.state {\n\tcase state_waiting_for_permission:\n\t\tif ftc.Action == Action_status {\n\t\t\tif ftc.Status == `OK` {\n\t\t\t\tself.state = state_waiting_for_file_metadata\n\t\t\t} else {\n\t\t\t\treturn unicode_input.ErrCanceledByUser\n\t\t\t}\n\t\t} else {\n\t\t\treturn fmt.Errorf(`Unexpected response from terminal: %s`, ftc.String())\n\t\t}\n\tcase state_waiting_for_file_metadata:\n\t\tswitch ftc.Action {\n\t\tcase Action_status:\n\t\t\tif ftc.File_id != \"\" {\n\t\t\t\tfid, err := strconv.Atoi(ftc.File_id)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(`Unexpected response from terminal (non-integer file_id): %s`, ftc.String())\n\t\t\t\t}\n\t\t\t\tif fid < 0 || fid >= len(self.spec) {\n\t\t\t\t\treturn fmt.Errorf(`Unexpected response from terminal (out-of-range file_id): %s`, ftc.String())\n\t\t\t\t}\n\t\t\t\tself.failed_specs[fid] = ftc.Status\n\t\t\t} else {\n\t\t\t\tif ftc.Status == `OK` {\n\t\t\t\t\tself.state = state_transferring\n\t\t\t\t\tself.remote_home = ftc.Name\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"%s\", ftc.Status)\n\t\t\t}\n\t\tcase Action_file:\n\t\t\tfid, err := strconv.Atoi(ftc.File_id)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(`Unexpected response from terminal (non-integer file_id): %s`, ftc.String())\n\t\t\t}\n\t\t\tif fid < 0 || fid >= len(self.spec) {\n\t\t\t\treturn fmt.Errorf(`Unexpected response from terminal (out-of-range file_id): %s`, ftc.String())\n\t\t\t}\n\t\t\tself.spec_counts[fid] += 1\n\t\t\tself.file_id_counter++\n\t\t\tif rf, err := new_remote_file(self.cli_opts, ftc, self.file_id_counter); err == nil {\n\t\t\t\tself.files = append(self.files, rf)\n\t\t\t} else {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn fmt.Errorf(`Unexpected response from terminal (invalid action): %s`, ftc.String())\n\t\t}\n\tcase state_transferring:\n\t\tif ftc.Action == Action_data || ftc.Action == Action_end_data {\n\t\t\tf, found := self.files_to_be_transferred[ftc.File_id]\n\t\t\tif !found {\n\t\t\t\treturn fmt.Errorf(`Got data for unknown file id: %s`, ftc.File_id)\n\t\t\t}\n\t\t\tis_last := ftc.Action == Action_end_data\n\t\t\tif amt_written, err := f.write_data(ftc.Data, is_last); err != nil {\n\t\t\t\treturn err\n\t\t\t} else {\n\t\t\t\tself.progress_tracker.file_written(f, amt_written, is_last)\n\t\t\t}\n\t\t\tif is_last {\n\t\t\t\tdelete(self.files_to_be_transferred, ftc.File_id)\n\t\t\t\tif len(self.files_to_be_transferred) == 0 {\n\t\t\t\t\treturn self.finalize_transfer()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\treturn\n}\n\ntype tree_node struct {\n\tentry       *remote_file\n\tadded_files map[string]*tree_node\n}\n\nfunc (self *tree_node) add_child(f *remote_file) *tree_node {\n\tif x, found := self.added_files[f.remote_id]; found {\n\t\treturn x\n\t}\n\tc := tree_node{entry: f, added_files: make(map[string]*tree_node)}\n\tf.expanded_local_path = filepath.Join(self.entry.expanded_local_path, filepath.Base(f.remote_path))\n\tself.added_files[f.remote_id] = &c\n\treturn &c\n}\n\nfunc walk_tree(root *tree_node, cb func(*tree_node) error) error {\n\tfor _, c := range root.added_files {\n\t\tif err := cb(c); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := walk_tree(c, cb); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc ensure_parent(f *remote_file, node_map map[string]*tree_node, fid_map map[string]*remote_file) *tree_node {\n\tif ans := node_map[f.parent]; ans != nil {\n\t\treturn ans\n\t}\n\tparent := fid_map[f.parent]\n\tgp := ensure_parent(parent, node_map, fid_map)\n\tnode := gp.add_child(parent)\n\tnode_map[parent.remote_id] = node\n\treturn node\n}\n\nfunc make_tree(all_files []*remote_file, local_base string) (root_node *tree_node) {\n\tfid_map := make(map[string]*remote_file, len(all_files))\n\tnode_map := make(map[string]*tree_node, len(all_files))\n\tfor _, f := range all_files {\n\t\tif f.remote_id != \"\" {\n\t\t\tfid_map[f.remote_id] = f\n\t\t}\n\t}\n\troot_node = &tree_node{entry: &remote_file{expanded_local_path: local_base}, added_files: make(map[string]*tree_node)}\n\tnode_map[\"\"] = root_node\n\n\tfor _, f := range all_files {\n\t\tif f.remote_id != \"\" {\n\t\t\tp := ensure_parent(f, node_map, fid_map)\n\t\t\tp.add_child(f)\n\t\t}\n\t}\n\treturn\n}\n\nfunc isdir(path string) bool {\n\tif s, err := os.Stat(path); err == nil {\n\t\treturn s.IsDir()\n\t}\n\treturn false\n}\n\nfunc files_for_receive(opts *Options, dest string, files []*remote_file, remote_home string, specs []string) (ans []*remote_file, err error) {\n\tspec_map := make(map[int][]*remote_file)\n\tfor _, f := range files {\n\t\tspec_map[f.spec_id] = append(spec_map[f.spec_id], f)\n\t}\n\tspec_paths := make([]string, len(specs))\n\tfor i := range specs {\n\t\t// use the shortest path as the path for the spec\n\t\tslices.SortStableFunc(spec_map[i], func(a, b *remote_file) int { return len(a.remote_path) - len(b.remote_path) })\n\t\tspec_paths[i] = spec_map[i][0].remote_path\n\t}\n\tif opts.Mode == \"mirror\" {\n\t\tcommon_path := utils.Commonpath(spec_paths...)\n\t\thome := strings.TrimRight(remote_home, \"/\")\n\t\tif strings.HasPrefix(common_path, home+\"/\") {\n\t\t\tfor i, x := range spec_paths {\n\t\t\t\tb, err := filepath.Rel(home, x)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tspec_paths[i] = filepath.Join(\"~\", b)\n\t\t\t}\n\t\t}\n\t\tfor spec_id, files_for_spec := range spec_map {\n\t\t\tspec := spec_paths[spec_id]\n\t\t\ttree := make_tree(files_for_spec, filepath.Dir(expand_home(spec)))\n\t\t\tif err = walk_tree(tree, func(x *tree_node) error {\n\t\t\t\tans = append(ans, x.entry)\n\t\t\t\treturn nil\n\t\t\t}); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t} else {\n\t\tnumber_of_source_files := 0\n\t\tfor _, x := range spec_map {\n\t\t\tnumber_of_source_files += len(x)\n\t\t}\n\t\tdest_is_dir := strings.HasSuffix(dest, \"/\") || number_of_source_files > 1 || isdir(dest)\n\t\tfor _, files_for_spec := range spec_map {\n\t\t\tif dest_is_dir {\n\t\t\t\tdest_path := filepath.Join(dest, filepath.Base(files_for_spec[0].remote_path))\n\t\t\t\ttree := make_tree(files_for_spec, filepath.Dir(expand_home(dest_path)))\n\t\t\t\tif err = walk_tree(tree, func(x *tree_node) error {\n\t\t\t\t\tans = append(ans, x.entry)\n\t\t\t\t\treturn nil\n\t\t\t\t}); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tf := files_for_spec[0]\n\t\t\t\tf.expanded_local_path = expand_home(dest)\n\t\t\t\tans = append(ans, f)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *manager) collect_files() (err error) {\n\tif self.files, err = files_for_receive(self.cli_opts, self.dest, self.files, self.remote_home, self.spec); err != nil {\n\t\treturn err\n\t}\n\tself.progress_tracker.total_size_of_all_files = 0\n\tfor _, f := range self.files {\n\t\tif f.ftype != FileType_directory && f.ftype != FileType_link {\n\t\t\tself.files_to_be_transferred[f.file_id] = f\n\t\t\tself.progress_tracker.total_size_of_all_files += utils.Max(0, f.expected_size)\n\t\t}\n\t}\n\tself.progress_tracker.total_bytes_to_transfer = self.progress_tracker.total_size_of_all_files\n\treturn nil\n}\n\nfunc (self *handler) print_continue_msg() {\n\tself.lp.Println(`Press`, self.ctx.Green(`y`), `to continue or`, self.ctx.BrightRed(`n`), `to abort`)\n}\n\nfunc lexists(path string) bool {\n\t_, err := os.Lstat(path)\n\treturn err == nil\n}\n\nfunc (self *handler) print_check_paths() {\n\tif self.check_paths_printed {\n\t\treturn\n\t}\n\tself.check_paths_printed = true\n\tself.lp.Println(`The following file transfers will be performed. A red destination means an existing file will be overwritten.`)\n\tfor _, df := range self.manager.files {\n\t\tself.lp.QueueWriteString(self.ctx.Prettify(fmt.Sprintf(\":%s:`%s` \", df.ftype.Color(), df.ftype.ShortText())))\n\t\tself.lp.QueueWriteString(\" \")\n\t\tlpath := df.expanded_local_path\n\t\tif lexists(lpath) {\n\t\t\tlpath = self.ctx.Prettify(self.ctx.BrightRed(lpath) + \" \")\n\t\t}\n\t\tself.lp.Println(df.display_name, \"→\", lpath)\n\t}\n\tself.lp.Println(fmt.Sprintf(`Transferring %d file(s) of total size: %s`, len(self.manager.files), humanize.Size(self.manager.progress_tracker.total_size_of_all_files)))\n\tself.print_continue_msg()\n}\n\nfunc (self *handler) confirm_paths() {\n\tself.print_check_paths()\n}\n\nfunc (self *handler) transmit_one() {\n\tif self.transmit_iterator == nil {\n\t\treturn\n\t}\n\twid, err := self.transmit_iterator(self.lp.QueueWriteString)\n\tif err != nil {\n\t\tif err == files_done {\n\t\t\tself.transmit_iterator = nil\n\t\t} else {\n\t\t\tself.abort_with_error(err)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tself.last_data_write_id = wid\n\t}\n}\n\nfunc (self *handler) start_transfer() {\n\tself.transmit_started = true\n\tn := len(self.manager.files)\n\tmsg := `Transmitting signature of`\n\tif self.manager.use_rsync {\n\t\tmsg = `Queueing transfer of`\n\t}\n\tmsg += ` `\n\tif n == 1 {\n\t\tmsg += `one file`\n\t} else {\n\t\tmsg += fmt.Sprintf(`%d files`, n)\n\t}\n\tself.lp.Println(msg)\n\tself.max_name_length = 0\n\tfor _, f := range self.manager.files {\n\t\tself.max_name_length = utils.Max(6, self.max_name_length, wcswidth.Stringwidth(f.display_name))\n\t}\n\tself.transmit_iterator = self.manager.request_files()\n\tself.transmit_one()\n}\n\nfunc (self *handler) on_file_transfer_response(ftc *FileTransmissionCommand) (err error) {\n\tif ftc.Id != self.manager.request_id {\n\t\treturn\n\t}\n\tif ftc.Action == Action_status && ftc.Status == \"CANCELED\" {\n\t\tself.lp.Quit(1)\n\t\treturn\n\t}\n\tif self.quit_after_write_code > -1 || self.manager.state == state_canceled {\n\t\treturn\n\t}\n\ttransfer_started := self.manager.state == state_transferring\n\tif merr := self.manager.on_file_transfer_response(ftc); merr != nil {\n\t\tif merr == unicode_input.ErrCanceledByUser {\n\t\t\t// terminal will not respond to cancel request\n\t\t\treturn fmt.Errorf(\"Permission denied by user\")\n\t\t}\n\t\tself.abort_with_error(merr)\n\t\treturn\n\t}\n\tif !transfer_started && self.manager.state == state_transferring {\n\t\tif len(self.manager.failed_specs) > 0 {\n\t\t\tself.print_err(fmt.Errorf(`Failed to process some sources:`))\n\t\t\tfor spec_id, msg := range self.manager.failed_specs {\n\t\t\t\tspec := self.manager.spec[spec_id]\n\t\t\t\tif strings.HasPrefix(msg, `ENOENT:`) {\n\t\t\t\t\tmsg = `File not found`\n\t\t\t\t}\n\t\t\t\tself.lp.Println(fmt.Sprintf(`  %s: %s`, spec, msg))\n\t\t\t}\n\t\t\tself.abort_with_error(nil)\n\t\t\treturn\n\t\t}\n\t\tzero_specs := make([]string, 0, len(self.manager.spec_counts))\n\t\tfor k, v := range self.manager.spec_counts {\n\t\t\tif v == 0 {\n\t\t\t\tzero_specs = append(zero_specs, self.manager.spec[k])\n\t\t\t}\n\t\t}\n\t\tif len(zero_specs) > 0 {\n\t\t\tself.abort_with_error(fmt.Errorf(`No matches found for: %s`, strings.Join(zero_specs, \", \")))\n\t\t\treturn\n\t\t}\n\t\tif merr := self.manager.collect_files(); merr != nil {\n\t\t\tself.abort_with_error(merr)\n\t\t\treturn\n\t\t}\n\t\tif self.cli_opts.ConfirmPaths {\n\t\t\tself.confirm_paths()\n\t\t} else {\n\t\t\tself.start_transfer()\n\t\t}\n\t}\n\tif self.manager.transfer_done {\n\t\tself.manager.send(FileTransmissionCommand{Action: Action_finish}, self.lp.QueueWriteString)\n\t\tself.quit_after_write_code = 0\n\t\tif err = self.refresh_progress(0); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if self.transmit_started {\n\t\tif err = self.refresh_progress(0); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *handler) on_writing_finished(msg_id loop.IdType, has_pending_writes bool) (err error) {\n\tif self.quit_after_write_code > -1 {\n\t\tself.lp.Quit(self.quit_after_write_code)\n\t} else if msg_id == self.last_data_write_id {\n\t\tself.transmit_one()\n\t}\n\treturn nil\n}\n\nfunc (self *handler) on_interrupt() (handled bool, err error) {\n\thandled = true\n\tif self.quit_after_write_code > -1 {\n\t\treturn\n\t}\n\tif self.manager.state == state_canceled {\n\t\tself.lp.Println(`Waiting for canceled acknowledgement from terminal, will abort in a few seconds if no response received`)\n\t\treturn\n\t}\n\tself.abort_with_error(fmt.Errorf(`Interrupt requested, cancelling transfer, transferred files are in undefined state.`))\n\treturn\n}\n\nfunc (self *handler) on_sigterm() (handled bool, err error) {\n\thandled = true\n\tif self.quit_after_write_code > -1 {\n\t\treturn\n\t}\n\tself.abort_with_error(fmt.Errorf(`Terminate requested, cancelling transfer, transferred files are in undefined state.`), 2*time.Second)\n\treturn\n}\n\nfunc (self *handler) erase_progress() {\n\tif self.progress_drawn {\n\t\tself.lp.MoveCursorVertically(-2)\n\t\tself.lp.QueueWriteString(\"\\r\")\n\t\tself.lp.ClearToEndOfScreen()\n\t\tself.progress_drawn = false\n\t}\n}\n\nfunc (self *handler) render_progress(name string, p Progress) {\n\tif p.is_complete {\n\t\tp.bytes_so_far = p.total_bytes\n\t}\n\tss, _ := self.lp.ScreenSize()\n\tself.lp.QueueWriteString(render_progress_in_width(name, p, int(ss.WidthCells), self.ctx))\n}\n\nfunc (self *handler) draw_progress_for_current_file(af *remote_file, spinner_char string, is_complete bool) {\n\tp := &self.manager.progress_tracker\n\tnow := time.Now()\n\tsecs := utils.IfElse(af.done_at.IsZero(), now, af.done_at)\n\tself.render_progress(af.display_name, Progress{\n\t\tspinner_char: spinner_char, is_complete: is_complete,\n\t\tbytes_so_far: af.written_bytes, total_bytes: af.expected_size,\n\t\tsecs_so_far:   secs.Sub(af.transmit_started_at).Seconds(),\n\t\tbytes_per_sec: safe_divide(p.transfered_stats_amt, p.transfered_stats_interval),\n\t})\n}\n\nfunc (self *handler) draw_files() {\n\ttick := self.ctx.Green(`✔`)\n\tvar sc string\n\tfor _, df := range self.manager.progress_tracker.done_files {\n\t\tsc = tick\n\t\tif df.ftype == FileType_regular {\n\t\t\tself.draw_progress_for_current_file(df, sc, true)\n\t\t} else {\n\t\t\tself.lp.QueueWriteString(fmt.Sprintf(\"%s %s %s\", sc, df.display_name, self.ctx.Italic(self.ctx.Dim(df.ftype.String()))))\n\t\t}\n\t\tself.lp.Println()\n\t\tself.manager.progress_tracker.done_files = nil\n\t}\n\tis_complete := self.quit_after_write_code > -1\n\tif is_complete {\n\t\tsc = utils.IfElse(self.quit_after_write_code == 0, tick, self.ctx.Red(`✘`))\n\t} else {\n\t\tsc = self.spinner.Tick()\n\t}\n\tp := &self.manager.progress_tracker\n\tss, _ := self.lp.ScreenSize()\n\tif is_complete {\n\t\ttui.RepeatChar(`─`, int(ss.WidthCells))\n\t} else {\n\t\taf := p.active_file\n\t\tif af != nil {\n\t\t\tself.draw_progress_for_current_file(af, sc, false)\n\t\t}\n\t}\n\tself.lp.Println()\n\tif p.total_transferred > 0 {\n\t\tself.render_progress(`Total`, Progress{\n\t\t\tspinner_char: sc, bytes_so_far: p.total_transferred, total_bytes: p.total_bytes_to_transfer,\n\t\t\tsecs_so_far: time.Since(p.started_at).Seconds(), is_complete: is_complete,\n\t\t\tbytes_per_sec: safe_divide(p.transfered_stats_amt, p.transfered_stats_interval.Abs().Seconds()),\n\t\t})\n\t\tself.lp.Println()\n\t} else {\n\t\tself.lp.Println(`File data transfer has not yet started`)\n\t}\n}\n\nfunc (self *handler) schedule_progress_update(delay time.Duration) {\n\tif self.progress_update_timer != 0 {\n\t\tself.lp.RemoveTimer(self.progress_update_timer)\n\t\tself.progress_update_timer = 0\n\t}\n\ttimer_id, err := self.lp.AddTimer(delay, false, self.refresh_progress)\n\tif err == nil {\n\t\tself.progress_update_timer = timer_id\n\t}\n}\n\nfunc (self *handler) draw_progress() {\n\tif self.manager.state == state_canceled {\n\t\treturn\n\t}\n\tself.lp.AllowLineWrapping(false)\n\tdefer self.lp.AllowLineWrapping(true)\n\tself.draw_files()\n\tself.schedule_progress_update(self.spinner.Interval())\n\tself.progress_drawn = true\n}\n\nfunc (self *handler) refresh_progress(loop.IdType) error {\n\tself.lp.StartAtomicUpdate()\n\tdefer self.lp.EndAtomicUpdate()\n\tself.erase_progress()\n\tself.draw_progress()\n\treturn nil\n}\n\nfunc (self *handler) on_text(text string, from_key_event, in_bracketed_paste bool) error {\n\tif self.quit_after_write_code > -1 {\n\t\treturn nil\n\t}\n\tif self.check_paths_printed && !self.transmit_started {\n\t\tswitch strings.ToLower(text) {\n\t\tcase \"y\":\n\t\t\tself.start_transfer()\n\t\t\treturn nil\n\t\tcase \"n\":\n\t\t\tself.abort_with_error(fmt.Errorf(`Canceled by user`))\n\t\t\treturn nil\n\t\t}\n\t\tself.print_continue_msg()\n\t}\n\treturn nil\n}\nfunc (self *handler) on_key_event(ev *loop.KeyEvent) error {\n\tif self.quit_after_write_code > -1 {\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"esc\") {\n\t\tev.Handled = true\n\t\tif self.check_paths_printed && !self.transmit_started {\n\t\t\tself.abort_with_error(fmt.Errorf(`Canceled by user`))\n\t\t} else {\n\t\t\tif _, err := self.on_interrupt(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t} else if ev.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\tev.Handled = true\n\t\tif _, err := self.on_interrupt(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nvar debugprintln = tty.DebugPrintln\n\nfunc receive_loop(opts *Options, spec []string, dest string) (err error, rc int) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors)\n\tif err != nil {\n\t\treturn err, 1\n\t}\n\n\thandler := handler{\n\t\tlp: lp, quit_after_write_code: -1, cli_opts: opts, spinner: tui.NewSpinner(\"dots\"),\n\t\tctx: markup.New(true),\n\t\tmanager: manager{\n\t\t\trequest_id: random_id(), spec: spec, dest: dest, bypass: opts.PermissionsBypass, use_rsync: opts.TransmitDeltas,\n\t\t\tfailed_specs: make(map[int]string, len(spec)), spec_counts: make(map[int]int, len(spec)),\n\t\t\tsuffix: \"\\x1b\\\\\", cli_opts: opts, files_to_be_transferred: make(map[string]*remote_file),\n\t\t},\n\t}\n\tfor i := range spec {\n\t\thandler.manager.spec_counts[i] = 0\n\t}\n\thandler.manager.prefix = fmt.Sprintf(\"\\x1b]%d;id=%s;\", kitty.FileTransferCode, handler.manager.request_id)\n\tif handler.manager.bypass != `` {\n\t\tif handler.manager.bypass, err = encode_bypass(handler.manager.request_id, handler.manager.bypass); err != nil {\n\t\t\treturn err, 1\n\t\t}\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.SetCursorVisible(false)\n\t\tlp.Println(\"Scanning files…\")\n\t\thandler.manager.start_transfer(lp.QueueWriteString)\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorVisible(true)\n\t\treturn \"\"\n\t}\n\n\tlp.OnSIGINT = handler.on_interrupt\n\tlp.OnSIGTERM = handler.on_sigterm\n\tlp.OnWriteComplete = handler.on_writing_finished\n\tlp.OnText = handler.on_text\n\tlp.OnKeyEvent = handler.on_key_event\n\tlp.OnResize = func(old_sz, new_sz loop.ScreenSize) error {\n\t\tif handler.progress_drawn {\n\t\t\treturn handler.refresh_progress(0)\n\t\t}\n\t\treturn nil\n\t}\n\n\tftc_code := strconv.Itoa(kitty.FileTransferCode)\n\tlp.OnEscapeCode = func(et loop.EscapeCodeType, payload []byte) error {\n\t\tif et == loop.OSC {\n\t\t\tif idx := bytes.IndexByte(payload, ';'); idx > 0 {\n\t\t\t\tif utils.UnsafeBytesToString(payload[:idx]) == ftc_code {\n\t\t\t\t\tftc, err := NewFileTransmissionCommand(utils.UnsafeBytesToString(payload[idx+1:]))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"Received invalid FileTransmissionCommand from terminal with error: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\treturn handler.on_file_transfer_response(ftc)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tdefer func() {\n\t\tfor _, f := range handler.manager.files {\n\t\t\tf.close()\n\t\t}\n\t}()\n\n\tif err != nil {\n\t\treturn err, 1\n\t}\n\tif lp.DeathSignalName() != \"\" {\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\n\tif lp.ExitCode() != 0 {\n\t\trc = lp.ExitCode()\n\t}\n\tvar tsf, dsz, ssz int64\n\tfor _, f := range handler.manager.files {\n\t\tif rc == 0 { // no error has yet occurred report errors closing files\n\t\t\tif cerr := f.close(); cerr != nil {\n\t\t\t\treturn cerr, 1\n\t\t\t}\n\t\t}\n\t\tif f.expect_diff {\n\t\t\ttsf += f.expected_size\n\t\t\tdsz += f.received_bytes\n\t\t\tssz += f.sent_bytes\n\t\t}\n\t}\n\tif tsf > 0 && dsz+ssz > 0 && rc == 0 {\n\t\tprint_rsync_stats(tsf, dsz, ssz)\n\t}\n\treturn\n}\n\nfunc receive_main(opts *Options, args []string) (err error, rc int) {\n\tspec := args\n\tvar dest string\n\tswitch opts.Mode {\n\tcase \"mirror\":\n\t\tif len(args) < 1 {\n\t\t\treturn fmt.Errorf(\"Must specify at least one file to transfer\"), 1\n\t\t}\n\tcase \"normal\":\n\t\tif len(args) < 2 {\n\t\t\treturn fmt.Errorf(\"Must specify at least one source and a destination file to transfer\"), 1\n\t\t}\n\t\tdest = args[len(args)-1]\n\t\tspec = args[:len(args)-1]\n\t}\n\treturn receive_loop(opts, spec, dest)\n}\n"
  },
  {
    "path": "kittens/transfer/rsync.pyi",
    "content": "from typing import Callable, Union\n\nfrom kitty.typing_compat import ReadableBuffer, WriteableBuffer\n\nclass RsyncError(Exception):\n    pass\n\nclass Hasher:\n    def __init__(self, which: str, data: ReadableBuffer = b''): ...\n    def update(self, data: ReadableBuffer) -> None: ...\n    def reset(self) -> None: ...\n    def digest(self) -> bytes: ...\n    def hexdigest(self) -> str: ...\n\n    @property\n    def digest_size(self) -> int: ...\n    @property\n    def block_size(self) -> int: ...\n    @property\n    def name(self) -> str: ...\n\ndef xxh128_hash(data: ReadableBuffer) -> bytes: ...\ndef xxh128_hash_with_seed(data: ReadableBuffer, seed: int) -> bytes: ...\n\n\nclass Patcher:\n\n    def __init__(self, expected_input_size: int = 0): ...\n    def signature_header(self, output: WriteableBuffer) -> int: ...\n    def sign_block(self, block: ReadableBuffer, output: WriteableBuffer) -> int: ...\n    def apply_delta_data(self, data: ReadableBuffer, read: Callable[[int, WriteableBuffer], int], write: Callable[[ReadableBuffer], None]) -> None: ...\n    def finish_delta_data(self) -> None: ...\n\n    @property\n    def block_size(self) -> int: ...\n    @property\n    def total_data_in_delta(self) -> int: ...\n\n\nclass Differ:\n\n    def add_signature_data(self, data: ReadableBuffer) -> None: ...\n    def finish_signature_data(self) -> None: ...\n    def next_op(self, read: Callable[[WriteableBuffer], int], write: Callable[[ReadableBuffer], None]) -> bool: ...\n\n\ndef parse_ftc(x: Union[str, ReadableBuffer], callback: Callable[[memoryview, memoryview], None]) -> None: ...\n"
  },
  {
    "path": "kittens/transfer/send.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage transfer\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"golang.org/x/exp/constraints\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/rsync\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype FileState int\n\nconst (\n\tWAITING_FOR_START FileState = iota\n\tWAITING_FOR_DATA\n\tTRANSMITTING\n\tFINISHED\n\tACKNOWLEDGED\n)\n\ntype FileHash struct{ dev, inode uint64 }\n\ntype Compressor interface {\n\tCompress(data []byte) []byte\n\tFlush() []byte\n}\n\ntype IdentityCompressor struct{}\n\nfunc (self *IdentityCompressor) Compress(data []byte) []byte { return data }\nfunc (self *IdentityCompressor) Flush() []byte               { return nil }\n\ntype ZlibCompressor struct {\n\tb bytes.Buffer\n\tw zlib.Writer\n}\n\nfunc NewZlibCompressor() *ZlibCompressor {\n\tans := ZlibCompressor{}\n\tans.b.Grow(4096)\n\tans.w = *zlib.NewWriter(&ans.b)\n\treturn &ans\n}\n\nfunc (self *ZlibCompressor) Compress(data []byte) []byte {\n\t_, err := self.w.Write(data)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer self.b.Reset()\n\treturn utils.UnsafeStringToBytes(self.b.String())\n}\n\nfunc (self *ZlibCompressor) Flush() []byte {\n\tself.w.Close()\n\treturn self.b.Bytes()\n}\n\ntype File struct {\n\tfile_hash                                             FileHash\n\tttype                                                 TransmissionType\n\tcompression                                           Compression\n\tcompressor                                            Compressor\n\tfile_type                                             FileType\n\tfile_id, hard_link_target                             string\n\tlocal_path, symbolic_link_target, expanded_local_path string\n\tstat_result                                           fs.FileInfo\n\tstate                                                 FileState\n\tdisplay_name                                          string\n\tmtime                                                 time.Time\n\tfile_size, bytes_to_transmit                          int64\n\tpermissions                                           fs.FileMode\n\tremote_path                                           string\n\trsync_capable, compression_capable                    bool\n\tremote_final_path                                     string\n\tremote_initial_size                                   int64\n\terr_msg                                               string\n\tactual_file                                           *os.File\n\ttransmitted_bytes, reported_progress                  int64\n\ttransmit_started_at, transmit_ended_at, done_at       time.Time\n\tdiffer                                                *rsync.Differ\n\tdelta_loader                                          func() error\n\tdeltabuf                                              *bytes.Buffer\n}\n\nfunc get_remote_path(local_path string, remote_base string) string {\n\tif remote_base == \"\" {\n\t\treturn filepath.ToSlash(local_path)\n\t}\n\tif strings.HasSuffix(remote_base, \"/\") {\n\t\treturn filepath.Join(remote_base, filepath.Base(local_path))\n\t}\n\treturn remote_base\n}\n\nfunc NewFile(opts *Options, local_path, expanded_local_path string, file_id int, stat_result fs.FileInfo, remote_base string, file_type FileType) *File {\n\tstat, ok := stat_result.Sys().(*syscall.Stat_t)\n\tif !ok {\n\t\tpanic(\"This platform does not support getting file identities from stat results\")\n\t}\n\tans := File{\n\t\tlocal_path: local_path, expanded_local_path: expanded_local_path, file_id: fmt.Sprintf(\"%x\", file_id),\n\t\tstat_result: stat_result, file_type: file_type, display_name: wcswidth.StripEscapeCodes(local_path),\n\t\tfile_hash: FileHash{uint64(stat.Dev), stat.Ino}, mtime: stat_result.ModTime(),\n\t\tfile_size: stat_result.Size(), bytes_to_transmit: stat_result.Size(),\n\t\tpermissions: stat_result.Mode().Perm(), remote_path: filepath.ToSlash(get_remote_path(local_path, remote_base)),\n\t\trsync_capable:       file_type == FileType_regular && stat_result.Size() > 4096,\n\t\tcompression_capable: file_type == FileType_regular && stat_result.Size() > 4096 && should_be_compressed(expanded_local_path, opts.Compress),\n\t\tremote_initial_size: -1,\n\t}\n\treturn &ans\n}\n\nfunc process(opts *Options, paths []string, remote_base string, counter *int) (ans []*File, err error) {\n\tfor _, x := range paths {\n\t\texpanded := expand_home(x)\n\t\ts, err := os.Lstat(expanded)\n\t\tif err != nil {\n\t\t\treturn ans, fmt.Errorf(\"Failed to stat %s with error: %w\", x, err)\n\t\t}\n\t\tif s.IsDir() {\n\t\t\t*counter += 1\n\t\t\tans = append(ans, NewFile(opts, x, expanded, *counter, s, remote_base, FileType_directory))\n\t\t\tnew_remote_base := remote_base\n\t\t\tif new_remote_base != \"\" {\n\t\t\t\tnew_remote_base = strings.TrimRight(new_remote_base, \"/\") + \"/\" + filepath.Base(x) + \"/\"\n\t\t\t} else {\n\t\t\t\tnew_remote_base = strings.TrimRight(filepath.ToSlash(x), \"/\") + \"/\"\n\t\t\t}\n\t\t\tcontents, err := os.ReadDir(expanded)\n\t\t\tif err != nil {\n\t\t\t\treturn ans, fmt.Errorf(\"Failed to read the directory %s with error: %w\", x, err)\n\t\t\t}\n\t\t\tnew_paths := make([]string, len(contents))\n\t\t\tfor i, y := range contents {\n\t\t\t\tnew_paths[i] = filepath.Join(x, y.Name())\n\t\t\t}\n\t\t\tnew_ans, err := process(opts, new_paths, new_remote_base, counter)\n\t\t\tif err != nil {\n\t\t\t\treturn ans, err\n\t\t\t}\n\t\t\tans = append(ans, new_ans...)\n\t\t} else if s.Mode()&fs.ModeSymlink == fs.ModeSymlink {\n\t\t\t*counter += 1\n\t\t\tans = append(ans, NewFile(opts, x, expanded, *counter, s, remote_base, FileType_symlink))\n\t\t} else if s.Mode().IsRegular() {\n\t\t\t*counter += 1\n\t\t\tans = append(ans, NewFile(opts, x, expanded, *counter, s, remote_base, FileType_regular))\n\t\t}\n\t}\n\treturn\n}\n\nfunc process_mirrored_files(opts *Options, args []string) (ans []*File, err error) {\n\tpaths := utils.Map(func(x string) string { return abspath(expand_home(x)) }, args)\n\thome := strings.TrimRight(home_path(), string(filepath.Separator)) + string(filepath.Separator)\n\tpaths = utils.Map(func(path string) string {\n\t\tif strings.HasPrefix(path, home) {\n\t\t\tr, _ := filepath.Rel(home, path)\n\t\t\treturn filepath.Join(\"~\", r)\n\t\t}\n\t\treturn path\n\t}, paths)\n\tcounter := 0\n\treturn process(opts, paths, \"\", &counter)\n}\n\nfunc process_normal_files(opts *Options, args []string) (ans []*File, err error) {\n\tif len(args) < 2 {\n\t\treturn ans, fmt.Errorf(\"Must specify at least one local path and one remote path\")\n\t}\n\targs = slices.Clone(args)\n\tremote_base := filepath.ToSlash(args[len(args)-1])\n\targs = args[:len(args)-1]\n\tif len(args) > 1 && !strings.HasSuffix(remote_base, \"/\") {\n\t\tremote_base += \"/\"\n\t}\n\tpaths := utils.Map(func(x string) string { return abspath(expand_home(x)) }, args)\n\tcounter := 0\n\treturn process(opts, paths, remote_base, &counter)\n}\n\nfunc files_for_send(opts *Options, args []string) (files []*File, err error) {\n\tif opts.Mode == \"mirror\" {\n\t\tfiles, err = process_mirrored_files(opts, args)\n\t} else {\n\t\tfiles, err = process_normal_files(opts, args)\n\t}\n\tif err != nil {\n\t\treturn files, err\n\t}\n\tgroups := make(map[FileHash][]*File, len(files))\n\n\t// detect hard links\n\tfor _, f := range files {\n\t\tgroups[f.file_hash] = append(groups[f.file_hash], f)\n\t}\n\tfor _, group := range groups {\n\t\tif len(group) > 1 {\n\t\t\tfor _, lf := range group[1:] {\n\t\t\t\tlf.file_type = FileType_link\n\t\t\t\tlf.hard_link_target = \"fid:\" + group[0].file_id\n\t\t\t}\n\t\t}\n\t}\n\n\tremove := make([]int, 0, len(files))\n\t// detect symlinks to other transferred files\n\tfor i, f := range files {\n\t\tif f.file_type == FileType_symlink {\n\t\t\tlink_dest, err := os.Readlink(f.expanded_local_path)\n\t\t\tif err != nil {\n\t\t\t\tremove = append(remove, i)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tf.symbolic_link_target = \"path:\" + link_dest\n\t\t\tis_abs := filepath.IsAbs(link_dest)\n\t\t\tq := link_dest\n\t\t\tif !is_abs {\n\t\t\t\tq = filepath.Join(filepath.Dir(f.expanded_local_path), link_dest)\n\t\t\t}\n\t\t\tst, err := os.Stat(q)\n\t\t\tif err == nil {\n\t\t\t\tstat, ok := st.Sys().(*syscall.Stat_t)\n\t\t\t\tif ok {\n\t\t\t\t\tfh := FileHash{uint64(stat.Dev), stat.Ino}\n\t\t\t\t\tgr, found := groups[fh]\n\t\t\t\t\tif found {\n\t\t\t\t\t\tg := utils.Filter(gr, func(x *File) bool {\n\t\t\t\t\t\t\treturn os.SameFile(x.stat_result, st)\n\t\t\t\t\t\t})\n\t\t\t\t\t\tif len(g) > 0 {\n\t\t\t\t\t\t\tf.symbolic_link_target = \"fid\"\n\t\t\t\t\t\t\tif is_abs {\n\t\t\t\t\t\t\t\tf.symbolic_link_target = \"fid_abs\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tf.symbolic_link_target += \":\" + g[0].file_id\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif len(remove) > 0 {\n\t\tfor _, idx := range utils.Reverse(remove) {\n\t\t\tfiles[idx] = nil\n\t\t\tfiles = slices.Delete(files, idx, idx+1)\n\t\t}\n\t}\n\treturn files, nil\n}\n\ntype SendState int\n\nconst (\n\tSEND_WAITING_FOR_PERMISSION SendState = iota\n\tSEND_PERMISSION_GRANTED\n\tSEND_PERMISSION_DENIED\n\tSEND_CANCELED\n)\n\ntype Transfer struct {\n\tamt int64\n\tat  time.Time\n}\n\nfunc (self *Transfer) is_too_old(now time.Time) bool {\n\treturn now.Sub(self.at) > 30*time.Second\n}\n\ntype ProgressTracker struct {\n\ttotal_size_of_all_files, total_bytes_to_transfer int64\n\tactive_file                                      *File\n\ttotal_transferred                                int64\n\ttransfers                                        []*Transfer\n\ttransfered_stats_amt                             int64\n\ttransfered_stats_interval                        time.Duration\n\tstarted_at                                       time.Time\n\tsignature_bytes                                  int\n\ttotal_reported_progress                          int64\n}\n\nfunc (self *ProgressTracker) change_active_file(nf *File) {\n\tnow := time.Now()\n\tself.active_file = nf\n\tnf.transmit_started_at = now\n}\n\nfunc (self *ProgressTracker) start_transfer() {\n\tt := Transfer{at: time.Now()}\n\tself.transfers = append(self.transfers, &t)\n\tself.started_at = t.at\n}\n\nfunc (self *ProgressTracker) on_transmit(amt int64, active_file *File) {\n\tactive_file.transmitted_bytes += amt\n\tself.total_transferred += amt\n\tnow := time.Now()\n\tself.transfers = append(self.transfers, &Transfer{amt: amt, at: now})\n\tfor len(self.transfers) > 2 && self.transfers[0].is_too_old(now) {\n\t\tself.transfers = self.transfers[1:]\n\t}\n\tself.transfered_stats_interval = now.Sub(self.transfers[0].at)\n\tself.transfered_stats_amt = 0\n\tfor _, t := range self.transfers {\n\t\tself.transfered_stats_amt += t.amt\n\t}\n}\n\nfunc (self *ProgressTracker) on_file_progress(af *File, delta int64) {\n\tif delta > 0 {\n\t\tself.total_reported_progress += delta\n\t}\n}\n\nfunc (self *ProgressTracker) on_file_done(af *File) {\n\taf.done_at = time.Now()\n}\n\ntype SendManager struct {\n\trequest_id                                                 string\n\tstate                                                      SendState\n\tfiles                                                      []*File\n\tbypass                                                     string\n\tuse_rsync                                                  bool\n\tfile_progress                                              func(*File, int)\n\tfile_done                                                  func(*File) error\n\tfid_map                                                    map[string]*File\n\tall_acknowledged, all_started, has_transmitting, has_rsync bool\n\tactive_idx                                                 int\n\tprefix, suffix                                             string\n\tlast_progress_file                                         *File\n\tprogress_tracker                                           ProgressTracker\n\tcurrent_chunk_uncompressed_sz                              int64\n\tcurrent_chunk_write_id                                     loop.IdType\n\tcurrent_chunk_for_file_id                                  string\n}\n\nfunc (self *SendManager) start_transfer() string {\n\treturn FileTransmissionCommand{Action: Action_send, Bypass: self.bypass}.Serialize()\n}\n\nfunc (self *SendManager) initialize() {\n\tif self.bypass != \"\" {\n\t\tq, err := encode_bypass(self.request_id, self.bypass)\n\t\tif err == nil {\n\t\t\tself.bypass = q\n\t\t} else {\n\t\t\tfmt.Fprintln(os.Stderr, \"Ignoring password because of error:\", err)\n\t\t}\n\n\t}\n\tself.fid_map = make(map[string]*File, len(self.files))\n\tfor _, f := range self.files {\n\t\tself.fid_map[f.file_id] = f\n\t}\n\tself.active_idx = -1\n\tself.current_chunk_uncompressed_sz = -1\n\tself.current_chunk_for_file_id = \"\"\n\tself.prefix = fmt.Sprintf(\"\\x1b]%d;id=%s;\", kitty.FileTransferCode, self.request_id)\n\tself.suffix = \"\\x1b\\\\\"\n\tfor _, f := range self.files {\n\t\tif f.file_size > 0 {\n\t\t\tself.progress_tracker.total_size_of_all_files += f.file_size\n\t\t}\n\t}\n\tself.progress_tracker.total_bytes_to_transfer = self.progress_tracker.total_size_of_all_files\n}\n\ntype SendHandler struct {\n\tmanager                              *SendManager\n\topts                                 *Options\n\tfiles                                []*File\n\tlp                                   *loop.Loop\n\tctx                                  *markup.Context\n\ttransmit_started, file_metadata_sent bool\n\tquit_after_write_code                int\n\tfinish_cmd_write_id                  loop.IdType\n\tcheck_paths_printed                  bool\n\ttransfer_finish_sent                 bool\n\tmax_name_length                      int\n\tprogress_drawn                       bool\n\tfailed_files, done_files             []*File\n\tdone_file_ids                        *utils.Set[string]\n\ttransmit_ok_checked                  bool\n\tprogress_update_timer                loop.IdType\n\tspinner                              *tui.Spinner\n}\n\nfunc safe_divide[A constraints.Integer | constraints.Float, B constraints.Integer | constraints.Float](a A, b B) float64 {\n\tif b == 0 {\n\t\treturn 0\n\t}\n\treturn float64(a) / float64(b)\n}\n\ntype Progress struct {\n\tspinner_char    string\n\tbytes_so_far    int64\n\ttotal_bytes     int64\n\tsecs_so_far     float64\n\tbytes_per_sec   float64\n\tis_complete     bool\n\tmax_path_length int\n}\n\nfunc reduce_to_single_grapheme(text string) string {\n\tlimit := utf8.RuneCountInString(text)\n\tif limit < 2 {\n\t\treturn text\n\t}\n\tfor x := 1; x < limit; x++ {\n\t\ttt, w := wcswidth.TruncateToVisualLengthWithWidth(text, x)\n\t\tif w <= x {\n\t\t\treturn tt\n\t\t}\n\t}\n\treturn text\n}\n\nfunc render_path_in_width(path string, width int) string {\n\tpath = filepath.ToSlash(path)\n\tif wcswidth.Stringwidth(path) <= width {\n\t\treturn path\n\t}\n\tparts := strings.Split(path, string(filepath.Separator))\n\treduced := strings.Join(utils.Map(reduce_to_single_grapheme, parts[:len(parts)-1]), string(filepath.Separator))\n\tpath = filepath.Join(reduced, parts[len(parts)-1])\n\tif wcswidth.Stringwidth(path) <= width {\n\t\treturn path\n\t}\n\treturn wcswidth.TruncateToVisualLength(path, width-1) + `…`\n}\n\nfunc ljust(text string, width int) string {\n\tif w := wcswidth.Stringwidth(text); w < width {\n\t\ttext += strings.Repeat(` `, (width - w))\n\t}\n\treturn text\n}\n\nfunc rjust(text string, width int) string {\n\tif w := wcswidth.Stringwidth(text); w < width {\n\t\ttext = strings.Repeat(` `, (width-w)) + text\n\t}\n\treturn text\n}\n\nfunc render_progress_in_width(path string, p Progress, width int, ctx *markup.Context) string {\n\tunit_style := ctx.Dim(`|`)\n\tsep, trail, _ := strings.Cut(unit_style, \"|\")\n\tvar ratio, rate, eta string\n\tif p.is_complete || p.bytes_so_far >= p.total_bytes {\n\t\tratio = humanize.Size(uint64(p.total_bytes), humanize.SizeOptions{Separator: sep})\n\t\trate = humanize.Size(uint64(safe_divide(float64(p.total_bytes), p.secs_so_far)), humanize.SizeOptions{Separator: sep}) + `/s`\n\t\teta = ctx.Green(humanize.ShortDuration(time.Duration(float64(time.Second) * p.secs_so_far)))\n\t} else {\n\t\ttb := humanize.Size(p.total_bytes)\n\t\tsval, _, _ := strings.Cut(tb, \" \")\n\t\tval, _ := strconv.ParseFloat(sval, 64)\n\t\tratio = humanize.FormatNumber(val*safe_divide(p.bytes_so_far, p.total_bytes)) + `/` + strings.ReplaceAll(tb, ` `, sep)\n\t\trate = humanize.Size(p.bytes_per_sec, humanize.SizeOptions{Separator: sep}) + `/s`\n\t\tbytes_left := p.total_bytes - p.bytes_so_far\n\t\teta_seconds := safe_divide(bytes_left, p.bytes_per_sec)\n\t\teta = humanize.ShortDuration(time.Duration(float64(time.Second) * eta_seconds))\n\t}\n\tlft := p.spinner_char + ` `\n\tmax_space_for_path := width/2 - wcswidth.Stringwidth(lft)\n\tmax_path_length := 80\n\tw := utils.Min(max_path_length, max_space_for_path)\n\tprefix := lft + render_path_in_width(path, w)\n\tw += wcswidth.Stringwidth(lft)\n\tprefix = ljust(prefix, w)\n\tq := ratio + trail + ctx.Yellow(\" @ \") + rate + trail\n\tq = rjust(q, 25) + ` `\n\teta = ` ` + eta\n\tif extra := width - w - wcswidth.Stringwidth(q) - wcswidth.Stringwidth(eta); extra > 4 {\n\t\tq += tui.RenderProgressBar(safe_divide(p.bytes_so_far, p.total_bytes), extra) + eta\n\t} else {\n\t\tq += strings.TrimSpace(eta)\n\t}\n\treturn prefix + q\n}\n\nfunc (self *SendHandler) render_progress(name string, p Progress) {\n\tif p.spinner_char == \"\" {\n\t\tp.spinner_char = \" \"\n\t}\n\tif p.is_complete {\n\t\tp.bytes_so_far = p.total_bytes\n\t}\n\tp.max_path_length = self.max_name_length\n\tsz, _ := self.lp.ScreenSize()\n\tself.lp.QueueWriteString(render_progress_in_width(name, p, int(sz.WidthCells), self.ctx))\n}\n\nfunc (self *SendHandler) draw_progress() {\n\tself.lp.AllowLineWrapping(false)\n\tdefer self.lp.AllowLineWrapping(true)\n\tvar sc string\n\tfor _, df := range self.done_files {\n\t\tsc = self.ctx.Green(`✔`)\n\t\tif df.err_msg != \"\" {\n\t\t\tsc = self.ctx.Err(`✘`)\n\t\t}\n\t\tif df.file_type == FileType_regular {\n\t\t\tself.draw_progress_for_current_file(df, sc, true)\n\t\t} else {\n\t\t\tself.lp.QueueWriteString(sc + ` ` + df.display_name + ` ` + self.ctx.Dim(self.ctx.Italic(df.file_type.String())))\n\t\t}\n\t\tself.lp.Println()\n\t\tself.done_file_ids.Add(df.file_id)\n\t}\n\tself.done_files = nil\n\tis_complete := self.quit_after_write_code > -1\n\tif is_complete {\n\t\tsc = self.ctx.Green(`✔`)\n\t\tif self.quit_after_write_code != 0 {\n\t\t\tsc = self.ctx.Err(`✘`)\n\t\t}\n\t} else {\n\t\tsc = self.spinner.Tick()\n\t}\n\tnow := time.Now()\n\tif is_complete {\n\t\tsz, _ := self.lp.ScreenSize()\n\t\tself.lp.QueueWriteString(tui.RepeatChar(`─`, int(sz.WidthCells)))\n\t} else {\n\t\taf := self.manager.last_progress_file\n\t\tif af == nil || self.done_file_ids.Has(af.file_id) {\n\t\t\tif !self.manager.has_transmitting && self.done_file_ids.Len() == 0 {\n\t\t\t\tif self.manager.has_rsync {\n\t\t\t\t\tself.lp.QueueWriteString(sc + ` Transferring rsync signatures...`)\n\t\t\t\t} else {\n\t\t\t\t\tself.lp.QueueWriteString(sc + ` Transferring metadata...`)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tself.draw_progress_for_current_file(af, sc, false)\n\t\t}\n\t}\n\tself.lp.Println()\n\tif p := self.manager.progress_tracker; p.total_reported_progress > 0 {\n\t\tself.render_progress(`Total`, Progress{\n\t\t\tspinner_char: sc, bytes_so_far: p.total_reported_progress, total_bytes: p.total_bytes_to_transfer,\n\t\t\tsecs_so_far: now.Sub(p.started_at).Seconds(), is_complete: is_complete,\n\t\t\tbytes_per_sec: safe_divide(p.transfered_stats_amt, p.transfered_stats_interval.Abs().Seconds()),\n\t\t})\n\t} else {\n\t\tself.lp.QueueWriteString(`File data transfer has not yet started`)\n\t}\n\tself.lp.Println()\n\tself.schedule_progress_update(self.spinner.Interval())\n\tself.progress_drawn = true\n}\n\nfunc (self *SendHandler) draw_progress_for_current_file(af *File, spinner_char string, is_complete bool) {\n\tp := self.manager.progress_tracker\n\tvar secs_so_far time.Duration\n\tempty := File{}\n\tif af.done_at == empty.done_at {\n\t\tsecs_so_far = time.Since(af.transmit_started_at)\n\t} else {\n\t\tsecs_so_far = af.done_at.Sub(af.transmit_started_at)\n\t}\n\tself.render_progress(af.display_name, Progress{\n\t\tspinner_char: spinner_char, is_complete: is_complete,\n\t\tbytes_so_far: af.reported_progress, total_bytes: af.bytes_to_transmit,\n\t\tsecs_so_far: secs_so_far.Seconds(), bytes_per_sec: safe_divide(p.transfered_stats_amt, p.transfered_stats_interval.Abs().Seconds()),\n\t})\n}\n\nfunc (self *SendHandler) erase_progress() {\n\tif self.progress_drawn {\n\t\tself.progress_drawn = false\n\t\tself.lp.MoveCursorVertically(-2)\n\t\tself.lp.QueueWriteString(\"\\r\")\n\t\tself.lp.ClearToEndOfScreen()\n\t}\n}\n\nfunc (self *SendHandler) refresh_progress(timer_id loop.IdType) (err error) {\n\tif !self.transmit_started || self.manager.state == SEND_CANCELED {\n\t\treturn nil\n\t}\n\tif timer_id == self.progress_update_timer {\n\t\tself.progress_update_timer = 0\n\t}\n\tif self.manager.active_file() == nil && !self.manager.all_acknowledged && self.done_file_ids.Len() != 0 && self.done_file_ids.Len() < len(self.manager.files) {\n\t\tif err = self.transmit_next_chunk(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tself.lp.StartAtomicUpdate()\n\tdefer self.lp.EndAtomicUpdate()\n\tself.erase_progress()\n\tself.draw_progress()\n\treturn nil\n}\n\nfunc (self *SendHandler) schedule_progress_update(delay time.Duration) {\n\tif self.progress_update_timer == 0 {\n\t\ttimer_id, err := self.lp.AddTimer(delay, false, self.refresh_progress)\n\t\tif err == nil {\n\t\t\tself.progress_update_timer = timer_id\n\t\t}\n\t}\n}\n\nfunc (self *SendHandler) on_file_progress(f *File, change int) {\n\tself.schedule_progress_update(100 * time.Millisecond)\n}\n\nfunc (self *SendHandler) on_file_done(f *File) error {\n\tself.done_files = append(self.done_files, f)\n\tif f.err_msg != \"\" {\n\t\tself.failed_files = append(self.failed_files, f)\n\t}\n\treturn self.refresh_progress(0)\n}\n\nfunc (self *SendHandler) send_payload(payload string) loop.IdType {\n\tself.lp.QueueWriteString(self.manager.prefix)\n\tself.lp.QueueWriteString(payload)\n\treturn self.lp.QueueWriteString(self.manager.suffix)\n}\n\nfunc (self *File) metadata_command(use_rsync bool) *FileTransmissionCommand {\n\tif use_rsync && self.rsync_capable {\n\t\tself.ttype = TransmissionType_rsync\n\t}\n\tif self.compression_capable {\n\t\tself.compression = Compression_zlib\n\t\tself.compressor = NewZlibCompressor()\n\t} else {\n\t\tself.compressor = &IdentityCompressor{}\n\t}\n\treturn &FileTransmissionCommand{\n\t\tAction: Action_file, Compression: self.compression, Ftype: self.file_type,\n\t\tName: self.remote_path, Permissions: self.permissions, Mtime: time.Duration(self.mtime.UnixNano()),\n\t\tFile_id: self.file_id, Ttype: self.ttype,\n\t}\n}\n\nfunc (self *SendManager) send_file_metadata(send func(string) loop.IdType) {\n\tfor _, f := range self.files {\n\t\tftc := f.metadata_command(self.use_rsync)\n\t\tsend(ftc.Serialize())\n\t}\n}\n\nfunc (self *SendHandler) send_file_metadata() {\n\tif !self.file_metadata_sent {\n\t\tself.file_metadata_sent = true\n\t\tself.manager.send_file_metadata(self.send_payload)\n\t}\n}\n\nfunc (self *SendManager) update_collective_statuses() {\n\tvar found_not_started, found_not_done, has_rsync, has_transmitting bool\n\tfor _, f := range self.files {\n\t\tif f.state != ACKNOWLEDGED {\n\t\t\tfound_not_done = true\n\t\t}\n\t\tif f.state == WAITING_FOR_START {\n\t\t\tfound_not_started = true\n\t\t} else if f.state == TRANSMITTING {\n\t\t\thas_transmitting = true\n\t\t}\n\t\tif f.ttype == TransmissionType_rsync {\n\t\t\thas_rsync = true\n\t\t}\n\t}\n\tself.all_acknowledged = !found_not_done\n\tself.all_started = !found_not_started\n\tself.has_rsync = has_rsync\n\tself.has_transmitting = has_transmitting\n}\n\nfunc (self *SendManager) on_file_status_update(ftc *FileTransmissionCommand) error {\n\tfile := self.fid_map[ftc.File_id]\n\tif file == nil {\n\t\treturn nil\n\t}\n\tswitch ftc.Status {\n\tcase `STARTED`:\n\t\tfile.remote_final_path = ftc.Name\n\t\tfile.remote_initial_size = int64(ftc.Size)\n\t\tif file.file_type == FileType_directory {\n\t\t\tfile.state = FINISHED\n\t\t} else {\n\t\t\tif ftc.Ttype == TransmissionType_rsync {\n\t\t\t\tfile.state = WAITING_FOR_DATA\n\t\t\t} else {\n\t\t\t\tfile.state = TRANSMITTING\n\t\t\t}\n\t\t\tif file.state == WAITING_FOR_DATA {\n\t\t\t\tfile.differ = rsync.NewDiffer()\n\t\t\t}\n\t\t\tself.update_collective_statuses()\n\t\t}\n\tcase `PROGRESS`:\n\t\tself.last_progress_file = file\n\t\tchange := int64(ftc.Size) - file.reported_progress\n\t\tfile.reported_progress = int64(ftc.Size)\n\t\tself.progress_tracker.on_file_progress(file, change)\n\t\tself.file_progress(file, int(change))\n\tdefault:\n\t\tif ftc.Name != \"\" && file.remote_final_path == \"\" {\n\t\t\tfile.remote_final_path = ftc.Name\n\t\t}\n\t\tfile.state = ACKNOWLEDGED\n\t\tif ftc.Status == `OK` {\n\t\t\tif ftc.Size > 0 {\n\t\t\t\tchange := int64(ftc.Size) - file.reported_progress\n\t\t\t\tfile.reported_progress = int64(ftc.Size)\n\t\t\t\tself.progress_tracker.on_file_progress(file, change)\n\t\t\t\tself.file_progress(file, int(change))\n\t\t\t}\n\t\t} else {\n\t\t\tfile.err_msg = ftc.Status\n\t\t}\n\t\tself.progress_tracker.on_file_done(file)\n\t\tif err := self.file_done(file); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif self.active_idx > -1 && file == self.files[self.active_idx] {\n\t\t\tself.active_idx = -1\n\t\t}\n\t\tself.update_collective_statuses()\n\t}\n\treturn nil\n}\n\nfunc (self *File) start_delta_calculation() (err error) {\n\tself.state = TRANSMITTING\n\tif self.actual_file == nil {\n\t\tself.actual_file, err = os.Open(self.expanded_local_path)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tself.deltabuf = bytes.NewBuffer(make([]byte, 0, 32+rsync.DataSizeMultiple*self.differ.BlockSize()))\n\tself.delta_loader = self.differ.CreateDelta(self.actual_file, self.deltabuf)\n\treturn nil\n}\n\nfunc (self *SendManager) on_signature_data_received(ftc *FileTransmissionCommand) error {\n\tfile := self.fid_map[ftc.File_id]\n\tif file == nil || file.state != WAITING_FOR_DATA {\n\t\treturn nil\n\t}\n\tif file.differ == nil {\n\t\tfile.differ = rsync.NewDiffer()\n\t}\n\tif err := file.differ.AddSignatureData(ftc.Data); err != nil {\n\t\treturn err\n\t}\n\tself.progress_tracker.signature_bytes += len(ftc.Data)\n\tif ftc.Action == Action_end_data {\n\t\tif err := file.differ.FinishSignatureData(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn file.start_delta_calculation()\n\t}\n\treturn nil\n}\n\nfunc (self *SendManager) on_file_transfer_response(ftc *FileTransmissionCommand) error {\n\tswitch ftc.Action {\n\tcase Action_status:\n\t\tif ftc.File_id != \"\" {\n\t\t\treturn self.on_file_status_update(ftc)\n\t\t}\n\t\tif ftc.Status == \"OK\" {\n\t\t\tself.state = SEND_PERMISSION_GRANTED\n\t\t} else {\n\t\t\tself.state = SEND_PERMISSION_DENIED\n\t\t}\n\tcase Action_data, Action_end_data:\n\t\tif ftc.File_id != \"\" {\n\t\t\treturn self.on_signature_data_received(ftc)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *SendHandler) on_file_transfer_response(ftc *FileTransmissionCommand) error {\n\tif ftc.Id != self.manager.request_id {\n\t\treturn nil\n\t}\n\tif ftc.Action == Action_status && ftc.Status == \"CANCELED\" {\n\t\tself.lp.Quit(1)\n\t\treturn nil\n\t}\n\tif self.quit_after_write_code > -1 || self.manager.state == SEND_CANCELED {\n\t\treturn nil\n\t}\n\tbefore := self.manager.state\n\terr := self.manager.on_file_transfer_response(ftc)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif before == SEND_WAITING_FOR_PERMISSION {\n\t\tswitch self.manager.state {\n\t\tcase SEND_PERMISSION_DENIED:\n\t\t\tself.lp.Println(self.ctx.Err(\"Permission denied for this transfer\"))\n\t\t\tself.lp.Quit(1)\n\t\t\treturn nil\n\t\tcase SEND_PERMISSION_GRANTED:\n\t\t\tself.lp.Println(self.ctx.Green(\"Permission granted for this transfer\"))\n\t\t\tself.send_file_metadata()\n\t\t}\n\t}\n\tif !self.transmit_started {\n\t\treturn self.check_for_transmit_ok()\n\t}\n\tif self.manager.all_acknowledged {\n\t\tself.transfer_finished()\n\t} else if ftc.Action == Action_end_data && ftc.File_id != \"\" {\n\t\treturn self.transmit_next_chunk()\n\t}\n\treturn nil\n}\n\nfunc (self *SendHandler) check_for_transmit_ok() (err error) {\n\tif self.transmit_ok_checked {\n\t\treturn self.start_transfer()\n\t}\n\tif self.manager.state != SEND_PERMISSION_GRANTED {\n\t\treturn\n\t}\n\tif self.opts.ConfirmPaths {\n\t\tif self.manager.all_started {\n\t\t\tself.print_check_paths()\n\t\t}\n\t\treturn\n\t}\n\tself.transmit_ok_checked = true\n\treturn self.start_transfer()\n}\n\nfunc (self *SendHandler) print_check_paths() {\n\tif self.check_paths_printed {\n\t\treturn\n\t}\n\tself.check_paths_printed = true\n\tself.lp.Println(`The following file transfers will be performed. A red destination means an existing file will be overwritten.`)\n\tfor _, df := range self.manager.files {\n\t\tfn := df.remote_final_path\n\t\tif df.remote_initial_size > -1 {\n\t\t\tfn = self.ctx.Red(fn)\n\t\t}\n\t\tself.lp.Println(\n\t\t\tself.ctx.Prettify(fmt.Sprintf(\":%s:`%s` \", df.file_type.Color(), df.file_type.ShortText())),\n\t\t\tdf.display_name, ` → `, fn)\n\t}\n\thsize := humanize.Size(self.manager.progress_tracker.total_bytes_to_transfer)\n\tif n := len(self.manager.files); n == 1 {\n\t\tself.lp.Println(fmt.Sprintf(`Transferring %d file of total size: %s`, n, hsize))\n\t} else {\n\t\tself.lp.Println(fmt.Sprintf(`Transferring %d files of total size: %s`, n, hsize))\n\t}\n\tself.print_continue_msg()\n}\n\nfunc (self *SendManager) activate_next_ready_file() *File {\n\tif self.active_idx > -1 && self.active_idx < len(self.files) {\n\t\tself.files[self.active_idx].transmit_ended_at = time.Now()\n\t}\n\tfor i, f := range self.files {\n\t\tif f.state == TRANSMITTING {\n\t\t\tself.active_idx = i\n\t\t\tself.update_collective_statuses()\n\t\t\tself.progress_tracker.change_active_file(f)\n\t\t\treturn f\n\t\t}\n\t}\n\tself.active_idx = -1\n\tself.update_collective_statuses()\n\treturn nil\n}\n\nfunc (self *SendManager) active_file() *File {\n\tif self.active_idx > -1 && self.active_idx < len(self.files) {\n\t\treturn self.files[self.active_idx]\n\t}\n\treturn nil\n}\n\nfunc (self *File) next_chunk() (ans string, asz int, err error) {\n\tconst sz = 1024 * 1024\n\tswitch self.file_type {\n\tcase FileType_symlink:\n\t\tself.state = FINISHED\n\t\tans, asz = self.symbolic_link_target, len(self.symbolic_link_target)\n\t\treturn\n\tcase FileType_link:\n\t\tself.state = FINISHED\n\t\tans, asz = self.hard_link_target, len(self.hard_link_target)\n\t\treturn\n\t}\n\tis_last := false\n\tvar chunk []byte\n\tif self.delta_loader != nil {\n\t\tfor !is_last && self.deltabuf.Len() < sz {\n\t\t\tif err = self.delta_loader(); err != nil {\n\t\t\t\tif err == io.EOF {\n\t\t\t\t\tis_last = true\n\t\t\t\t} else {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tchunk = slices.Clone(self.deltabuf.Bytes())\n\t\tself.deltabuf.Reset()\n\t} else {\n\t\tif self.actual_file == nil {\n\t\t\tself.actual_file, err = os.Open(self.expanded_local_path)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tchunk = make([]byte, sz)\n\t\tvar n int\n\t\tn, err = self.actual_file.Read(chunk)\n\t\tif err != nil && !errors.Is(err, io.EOF) {\n\t\t\treturn\n\t\t}\n\t\tif n <= 0 {\n\t\t\tis_last = true\n\t\t} else if pos, _ := self.actual_file.Seek(0, io.SeekCurrent); pos >= self.file_size {\n\t\t\tis_last = true\n\t\t}\n\t\tchunk = chunk[:n]\n\t}\n\tuncompressed_sz := len(chunk)\n\tcchunk := self.compressor.Compress(chunk)\n\tif is_last {\n\t\ttrail := self.compressor.Flush()\n\t\tif len(trail) >= 0 {\n\t\t\tcchunk = append(cchunk, trail...)\n\t\t}\n\t\tself.state = FINISHED\n\t\tif self.actual_file != nil {\n\t\t\terr = self.actual_file.Close()\n\t\t\tself.actual_file = nil\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tself.delta_loader = nil\n\t\tself.deltabuf = nil\n\t}\n\tans, asz = utils.UnsafeBytesToString(cchunk), uncompressed_sz\n\treturn\n}\n\nfunc (self *SendManager) next_chunks(callback func(string) loop.IdType) error {\n\tif self.active_file() == nil {\n\t\tself.activate_next_ready_file()\n\t}\n\taf := self.active_file()\n\tif af == nil {\n\t\treturn nil\n\t}\n\tchunk := \"\"\n\tself.current_chunk_uncompressed_sz = 0\n\tfor af.state != FINISHED && len(chunk) == 0 {\n\t\tc, usz, err := af.next_chunk()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tself.current_chunk_uncompressed_sz += int64(usz)\n\t\tself.current_chunk_for_file_id = af.file_id\n\t\tchunk = c\n\t}\n\tis_last := af.state == FINISHED\n\tif len(chunk) > 0 {\n\t\tsplit_for_transfer(utils.UnsafeStringToBytes(chunk), af.file_id, is_last, func(ftc *FileTransmissionCommand) {\n\t\t\tself.current_chunk_write_id = callback(ftc.Serialize())\n\t\t})\n\t} else if is_last {\n\t\tself.current_chunk_write_id = callback(FileTransmissionCommand{Action: Action_end_data, File_id: af.file_id}.Serialize())\n\t}\n\tif is_last {\n\t\tself.activate_next_ready_file()\n\t\tif self.active_file() == nil {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *SendHandler) transmit_next_chunk() (err error) {\n\tfound_chunk := false\n\tfor !found_chunk {\n\t\tif err = self.manager.next_chunks(func(chunk string) loop.IdType {\n\t\t\tfound_chunk = true\n\t\t\treturn self.send_payload(chunk)\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !found_chunk {\n\t\t\tif self.manager.all_acknowledged {\n\t\t\t\tself.transfer_finished()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.manager.update_collective_statuses()\n\t\t\tif !self.manager.has_transmitting {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *SendHandler) start_transfer() (err error) {\n\tif self.manager.active_file() == nil {\n\t\tself.manager.activate_next_ready_file()\n\t}\n\tif self.manager.active_file() != nil {\n\t\tself.transmit_started = true\n\t\tself.manager.progress_tracker.start_transfer()\n\t\tif err = self.transmit_next_chunk(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tself.draw_progress()\n\t}\n\treturn\n}\n\nfunc (self *SendHandler) initialize() error {\n\tself.manager.initialize()\n\tself.spinner = tui.NewSpinner(\"dots\")\n\tself.ctx = markup.New(true)\n\tself.send_payload(self.manager.start_transfer())\n\tif self.opts.PermissionsBypass != \"\" {\n\t\t// dont wait for permission, not needed with a bypass and avoids a roundtrip\n\t\tself.send_file_metadata()\n\t}\n\treturn nil\n}\n\nfunc (self *SendHandler) transfer_finished() {\n\tif self.transfer_finish_sent {\n\t\treturn\n\t}\n\tself.transfer_finish_sent = true\n\tself.finish_cmd_write_id = self.send_payload(FileTransmissionCommand{Action: Action_finish}.Serialize())\n}\n\nfunc (self *SendHandler) on_text(text string, from_key_event, in_bracketed_paste bool) error {\n\tif self.quit_after_write_code > -1 {\n\t\treturn nil\n\t}\n\tif self.check_paths_printed && !self.transmit_started {\n\t\tswitch strings.ToLower(text) {\n\t\tcase \"y\":\n\t\t\terr := self.start_transfer()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif self.manager.all_acknowledged {\n\t\t\t\tif err = self.refresh_progress(0); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tself.transfer_finished()\n\t\t\t}\n\t\t\treturn nil\n\t\tcase \"n\":\n\t\t\tself.failed_files = nil\n\t\t\tself.abort_transfer()\n\t\t\tself.lp.Println(`Sending cancel request to terminal`)\n\t\t\treturn nil\n\t\t}\n\t\tself.print_continue_msg()\n\t}\n\treturn nil\n}\n\nfunc (self *SendHandler) print_continue_msg() {\n\tself.lp.Println(\n\t\t`Press`, self.ctx.Green(`y`), `to continue or`, self.ctx.BrightRed(`n`), `to abort`)\n}\n\nfunc (self *SendHandler) abort_transfer(delay ...time.Duration) {\n\td := 5 * time.Second\n\tif len(delay) > 0 {\n\t\td = delay[0]\n\t}\n\tself.send_payload(FileTransmissionCommand{Action: Action_cancel}.Serialize())\n\tself.manager.state = SEND_CANCELED\n\t_, _ = self.lp.AddTimer(d, false, func(loop.IdType) error {\n\t\tself.lp.Quit(1)\n\t\treturn nil\n\t})\n}\n\nfunc (self *SendHandler) on_resize(old_size, new_size loop.ScreenSize) error {\n\tif self.progress_drawn {\n\t\treturn self.refresh_progress(0)\n\t}\n\treturn nil\n}\n\nfunc (self *SendHandler) on_key_event(ev *loop.KeyEvent) error {\n\tif self.quit_after_write_code > -1 {\n\t\treturn nil\n\t}\n\tif ev.MatchesPressOrRepeat(\"esc\") {\n\t\tev.Handled = true\n\t\tif self.check_paths_printed && !self.transmit_started {\n\t\t\tself.failed_files = nil\n\t\t\tself.abort_transfer()\n\t\t\tself.lp.Println(`Sending cancel request to terminal`)\n\t\t\treturn nil\n\t\t} else {\n\t\t\tself.on_interrupt()\n\t\t}\n\t} else if ev.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\tself.on_interrupt()\n\t\tev.Handled = true\n\t}\n\treturn nil\n}\n\nfunc (self *SendHandler) on_writing_finished(msg_id loop.IdType, has_pending_writes bool) (err error) {\n\tchunk_transmitted := self.manager.current_chunk_uncompressed_sz >= 0 && msg_id == self.manager.current_chunk_write_id\n\tif chunk_transmitted {\n\t\tself.manager.progress_tracker.on_transmit(self.manager.current_chunk_uncompressed_sz, self.manager.fid_map[self.manager.current_chunk_for_file_id])\n\t\tself.manager.current_chunk_uncompressed_sz = -1\n\t\tself.manager.current_chunk_write_id = 0\n\t\tself.manager.current_chunk_for_file_id = \"\"\n\t}\n\tif self.finish_cmd_write_id > 0 && msg_id == self.finish_cmd_write_id {\n\t\tif len(self.failed_files) > 0 {\n\t\t\tself.quit_after_write_code = 1\n\t\t} else {\n\t\t\tself.quit_after_write_code = 0\n\t\t}\n\t\tif err = self.refresh_progress(0); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif self.quit_after_write_code > -1 && !has_pending_writes {\n\t\tself.lp.Quit(self.quit_after_write_code)\n\t\treturn\n\t}\n\tif self.manager.state == SEND_PERMISSION_GRANTED && !self.transmit_started {\n\t\treturn self.check_for_transmit_ok()\n\t}\n\tif chunk_transmitted {\n\t\tif err = self.refresh_progress(0); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn self.transmit_next_chunk()\n\t}\n\treturn\n}\n\nfunc (self *SendHandler) on_interrupt() {\n\tif self.quit_after_write_code > -1 {\n\t\treturn\n\t}\n\tif self.manager.state == SEND_CANCELED {\n\t\tself.lp.Println(`Waiting for canceled acknowledgement from terminal, will abort in a few seconds if no response received`)\n\t\treturn\n\t}\n\tself.lp.Println(self.ctx.BrightRed(`Interrupt requested, cancelling transfer, transferred files are in undefined state`))\n\tself.abort_transfer()\n}\n\nfunc send_loop(opts *Options, files []*File) (err error, rc int) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors)\n\tif err != nil {\n\t\treturn err, 1\n\t}\n\n\thandler := &SendHandler{\n\t\topts: opts, files: files, lp: lp, quit_after_write_code: -1,\n\t\tmax_name_length: utils.Max(0, utils.Map(func(f *File) int { return wcswidth.Stringwidth(f.display_name) }, files)...),\n\t\tprogress_drawn:  true, done_file_ids: utils.NewSet[string](),\n\t\tmanager: &SendManager{\n\t\t\trequest_id: random_id(), files: files, bypass: opts.PermissionsBypass, use_rsync: opts.TransmitDeltas,\n\t\t},\n\t}\n\thandler.manager.file_progress = handler.on_file_progress\n\thandler.manager.file_done = handler.on_file_done\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.SetCursorVisible(false)\n\t\treturn \"\", handler.initialize()\n\t}\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorVisible(true)\n\t\treturn \"\"\n\t}\n\tftc_code := strconv.Itoa(kitty.FileTransferCode)\n\tlp.OnEscapeCode = func(et loop.EscapeCodeType, payload []byte) error {\n\t\tif et == loop.OSC {\n\t\t\tif idx := bytes.IndexByte(payload, ';'); idx > 0 {\n\t\t\t\tif utils.UnsafeBytesToString(payload[:idx]) == ftc_code {\n\t\t\t\t\tftc, err := NewFileTransmissionCommand(utils.UnsafeBytesToString(payload[idx+1:]))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"Received invalid FileTransmissionCommand from terminal with error: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\treturn handler.on_file_transfer_response(ftc)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tlp.OnText = handler.on_text\n\tlp.OnKeyEvent = handler.on_key_event\n\tlp.OnResize = handler.on_resize\n\tlp.OnWriteComplete = handler.on_writing_finished\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn err, 1\n\t}\n\tif lp.DeathSignalName() != \"\" {\n\t\tlp.KillIfSignalled()\n\t\treturn\n\t}\n\tp := handler.manager.progress_tracker\n\tif handler.manager.has_rsync && p.total_transferred+int64(p.signature_bytes) > 0 && lp.ExitCode() == 0 {\n\t\tvar tsf int64\n\t\tfor _, f := range files {\n\t\t\tif f.ttype == TransmissionType_rsync {\n\t\t\t\ttsf += f.file_size\n\t\t\t}\n\t\t}\n\t\tif tsf > 0 {\n\t\t\tprint_rsync_stats(tsf, p.total_transferred, int64(p.signature_bytes))\n\t\t}\n\t}\n\tif len(handler.failed_files) > 0 {\n\t\tfmt.Fprintf(os.Stderr, \"Transfer of %d out of %d files failed\\n\", len(handler.failed_files), len(handler.manager.files))\n\t\tfor _, f := range handler.failed_files {\n\t\t\tfmt.Println(handler.ctx.BrightRed(f.display_name))\n\t\t\tfmt.Println(` `, f.err_msg)\n\t\t}\n\t\trc = 1\n\t}\n\tif lp.ExitCode() != 0 {\n\t\trc = lp.ExitCode()\n\t}\n\treturn\n}\n\nfunc send_main(opts *Options, args []string) (err error, rc int) {\n\tfmt.Println(\"Scanning files…\")\n\tfiles, err := files_for_send(opts, args)\n\tif err != nil {\n\t\treturn err, 1\n\t}\n\tfmt.Printf(\"Found %d files and directories, requesting transfer permission…\", len(files))\n\tfmt.Println()\n\terr, rc = send_loop(opts, files)\n\n\treturn\n}\n"
  },
  {
    "path": "kittens/transfer/send_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage transfer\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestPathMappingSend(t *testing.T) {\n\topts := &Options{}\n\ttdir := t.TempDir()\n\tb := filepath.Join(tdir, \"b\")\n\tos.Mkdir(b, 0o700)\n\tos.WriteFile(filepath.Join(b, \"r\"), nil, 0600)\n\tos.Mkdir(filepath.Join(b, \"d\"), 0o700)\n\tos.WriteFile(filepath.Join(b, \"d\", \"r\"), nil, 0600)\n\n\tgm := func(args ...string) ([]*File, error) {\n\t\treturn files_for_send(opts, args)\n\t}\n\n\tmp := func(path string, is_remote bool) string {\n\t\tpath = strings.TrimSpace(path)\n\t\tif strings.HasPrefix(path, \"~\") || filepath.IsAbs(path) {\n\t\t\treturn path\n\t\t}\n\t\treturn filepath.Join(tdir, path)\n\t}\n\n\ttf := func(expected string, args ...string) {\n\t\tfiles, err := gm(args...)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed with mode: %s cwd: %s home: %s and args: %#v\\n%s\", opts.Mode, cwd_path(), home_path(), args, err)\n\t\t}\n\t\tactual := make(map[string]string)\n\t\tfor _, f := range files {\n\t\t\tactual[f.expanded_local_path] = f.remote_path\n\t\t}\n\t\te := make(map[string]string, len(actual))\n\t\tfor rec := range strings.SplitSeq(expected, \" \") {\n\t\t\tk, v, _ := strings.Cut(rec, \":\")\n\t\t\te[mp(k, false)] = mp(v, true)\n\t\t}\n\t\tif diff := cmp.Diff(e, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed with mode: %s cwd: %s home: %s and args: %#v\\n%s\", opts.Mode, cwd_path(), home_path(), args, diff)\n\t\t}\n\t}\n\n\topts.Mode = \"mirror\"\n\trun_with_paths(b, \"/foo/bar\", func() {\n\t\ttf(\"b/r:b/r b/d:b/d b/d/r:b/d/r\", \"r\", \"d\")\n\t\ttf(\"b/r:b/r b/d/r:b/d/r\", \"r\", \"d/r\")\n\t})\n\trun_with_paths(b, tdir, func() {\n\t\ttf(\"b/r:~/b/r b/d:~/b/d b/d/r:~/b/d/r\", \"r\", \"d\")\n\t})\n\topts.Mode = \"normal\"\n\trun_with_paths(\"/some/else\", \"/foo/bar\", func() {\n\t\ttf(\"b/r:/dest/r b/d:/dest/d b/d/r:/dest/d/r\", filepath.Join(b, \"r\"), filepath.Join(b, \"d\"), \"/dest\")\n\t\ttf(\"b/r:~/dest/r b/d:~/dest/d b/d/r:~/dest/d/r\", filepath.Join(b, \"r\"), filepath.Join(b, \"d\"), \"~/dest\")\n\t})\n\trun_with_paths(b, \"/foo/bar\", func() {\n\t\ttf(\"b/r:/dest/r b/d:/dest/d b/d/r:/dest/d/r\", \"r\", \"d\", \"/dest\")\n\t})\n\tos.Symlink(\"/foo/b\", filepath.Join(b, \"e\"))\n\tos.Symlink(\"r\", filepath.Join(b, \"s\"))\n\tos.Link(filepath.Join(b, \"r\"), filepath.Join(b, \"h\"))\n\n\tfile_idx := 0\n\tfirst_file := func(args ...string) *File {\n\t\tfiles, err := gm(args...)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\treturn files[file_idx]\n\t}\n\tae := func(a any, b any) {\n\t\tif diff := cmp.Diff(a, b); diff != \"\" {\n\t\t\tt.Fatalf(\"%s\", diff)\n\t\t}\n\t}\n\trun_with_paths(\"/some/else\", \"/foo/bar\", func() {\n\t\tf := first_file(filepath.Join(b, \"e\"), \"dest\")\n\t\tae(f.symbolic_link_target, \"path:/foo/b\")\n\t\tf = first_file(filepath.Join(b, \"s\"), filepath.Join(b, \"r\"), \"dest\")\n\t\tae(f.symbolic_link_target, \"fid:2\")\n\t\tf = first_file(filepath.Join(b, \"h\"), \"dest\")\n\t\tae(f.file_type, FileType_regular)\n\t\tfile_idx = 1\n\t\tf = first_file(filepath.Join(b, \"h\"), filepath.Join(b, \"r\"), \"dest\")\n\t\tae(f.hard_link_target, \"fid:1\")\n\t\tae(f.file_type, FileType_link)\n\t})\n}\n"
  },
  {
    "path": "kittens/transfer/utils.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage transfer\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/crypto\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n)\n\nvar _ = fmt.Print\n\nvar global_cwd, global_home string\n\nfunc cwd_path() string {\n\tif global_cwd == \"\" {\n\t\tans, _ := os.Getwd()\n\t\treturn ans\n\t}\n\treturn global_cwd\n}\n\nfunc home_path() string {\n\tif global_home == \"\" {\n\t\treturn utils.Expanduser(\"~\")\n\t}\n\treturn global_home\n}\n\nfunc encode_bypass(request_id string, bypass string) (string, error) {\n\tq := request_id + \";\" + bypass\n\tif pkey_encoded := os.Getenv(\"KITTY_PUBLIC_KEY\"); pkey_encoded != \"\" {\n\t\tencryption_protocol, pubkey, err := crypto.DecodePublicKey(pkey_encoded)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tencrypted, err := crypto.Encrypt_data(utils.UnsafeStringToBytes(q), pubkey, encryption_protocol)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn fmt.Sprintf(\"kitty-1:%s\", utils.UnsafeBytesToString(encrypted)), nil\n\t}\n\treturn \"\", fmt.Errorf(\"KITTY_PUBLIC_KEY env var not set, cannot transmit password securely\")\n}\n\nfunc abspath(path string, use_home ...bool) string {\n\tif filepath.IsAbs(path) {\n\t\treturn path\n\t}\n\tvar base string\n\tif len(use_home) > 0 && use_home[0] {\n\t\tbase = home_path()\n\t} else {\n\t\tbase = cwd_path()\n\t}\n\treturn filepath.Join(base, path)\n}\n\nfunc expand_home(path string) string {\n\tif strings.HasPrefix(path, \"~\"+string(os.PathSeparator)) {\n\t\tpath = strings.TrimLeft(path[2:], string(os.PathSeparator))\n\t\tpath = filepath.Join(home_path(), path)\n\t} else if path == \"~\" {\n\t\tpath = home_path()\n\t}\n\treturn path\n}\n\nfunc random_id() string {\n\tbytes := []byte{0, 0}\n\trand.Read(bytes)\n\treturn fmt.Sprintf(\"%x%s\", os.Getpid(), hex.EncodeToString(bytes))\n}\n\nfunc run_with_paths(cwd, home string, f func()) {\n\tglobal_cwd, global_home = cwd, home\n\tdefer func() { global_cwd, global_home = \"\", \"\" }()\n\tf()\n}\n\nfunc should_be_compressed(path, strategy string) bool {\n\tif strategy == \"always\" {\n\t\treturn true\n\t}\n\tif strategy == \"never\" {\n\t\treturn false\n\t}\n\text := strings.ToLower(filepath.Ext(path))\n\tif ext != \"\" {\n\t\tswitch ext[1:] {\n\t\tcase \"zip\", \"odt\", \"odp\", \"pptx\", \"docx\", \"gz\", \"bz2\", \"xz\", \"svgz\":\n\t\t\treturn false\n\t\t}\n\t}\n\tmt := utils.GuessMimeType(path)\n\tif strings.HasSuffix(mt, \"+zip\") || (strings.HasPrefix(mt, \"image/\") && mt != \"image/svg+xml\") || strings.HasPrefix(mt, \"video/\") {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc print_rsync_stats(total_bytes, delta_bytes, signature_bytes int64) {\n\tfmt.Println(\"Rsync stats:\")\n\tfmt.Printf(\"  Delta size: %s Signature size: %s\\n\", humanize.Size(delta_bytes), humanize.Size(signature_bytes))\n\tfrac := float64(delta_bytes+signature_bytes) / float64(utils.Max(1, total_bytes))\n\tfmt.Printf(\"  Transmitted: %s of a total of %s (%.1f%%)\\n\", humanize.Size(delta_bytes+signature_bytes), humanize.Size(total_bytes), frac*100)\n}\n"
  },
  {
    "path": "kittens/transfer/utils.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nfrom collections.abc import Generator\nfrom contextlib import contextmanager\n\n_cwd = _home = ''\n\n\ndef abspath(path: str, use_home: bool = False) -> str:\n    base = home_path() if use_home else (_cwd or os.getcwd())\n    return os.path.normpath(os.path.join(base, path))\n\n\ndef home_path() -> str:\n    return _home or os.path.expanduser('~')\n\n\ndef cwd_path() -> str:\n    return _cwd or os.getcwd()\n\n\ndef expand_home(path: str) -> str:\n    if path.startswith('~' + os.sep) or (os.altsep and path.startswith('~' + os.altsep)):\n        return os.path.join(home_path(), path[2:].lstrip(os.sep + (os.altsep or '')))\n    return path\n\n\n@contextmanager\ndef set_paths(cwd: str = '', home: str = '') -> Generator[None, None, None]:\n    global _cwd, _home\n    orig = _cwd, _home\n    try:\n        _cwd, _home = cwd, home\n        yield\n    finally:\n        _cwd, _home = orig\n\n\nclass IdentityCompressor:\n\n    def compress(self, data: bytes | memoryview) -> bytes:\n        return bytes(data)\n\n    def flush(self) -> bytes:\n        return b''\n\n\nclass ZlibCompressor:\n\n    def __init__(self) -> None:\n        import zlib\n        self.c = zlib.compressobj()\n\n    def compress(self, data: bytes | memoryview) -> bytes:\n        return self.c.compress(data)\n\n    def flush(self) -> bytes:\n        return self.c.flush()\n"
  },
  {
    "path": "kittens/tui/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/tui/dircolors.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport stat\nfrom collections.abc import Generator\nfrom contextlib import suppress\n\nDEFAULT_DIRCOLORS = r\"\"\"# {{{\n# Configuration file for dircolors, a utility to help you set the\n# LS_COLORS environment variable used by GNU ls with the --color option.\n# Copyright (C) 1996-2019 Free Software Foundation, Inc.\n# Copying and distribution of this file, with or without modification,\n# are permitted provided the copyright notice and this notice are preserved.\n# The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the\n# slackware version of dircolors) are recognized but ignored.\n# Below are TERM entries, which can be a glob patterns, to match\n# against the TERM environment variable to determine if it is colorizable.\nTERM Eterm\nTERM ansi\nTERM *color*\nTERM con[0-9]*x[0-9]*\nTERM cons25\nTERM console\nTERM cygwin\nTERM dtterm\nTERM gnome\nTERM hurd\nTERM jfbterm\nTERM konsole\nTERM kterm\nTERM linux\nTERM linux-c\nTERM mlterm\nTERM putty\nTERM rxvt*\nTERM screen*\nTERM st\nTERM terminator\nTERM tmux*\nTERM vt100\nTERM xterm*\n# Below are the color init strings for the basic file types.\n# One can use codes for 256 or more colors supported by modern terminals.\n# The default color codes use the capabilities of an 8 color terminal\n# with some additional attributes as per the following codes:\n# Attribute codes:\n# 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed\n# Text color codes:\n# 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white\n# Background color codes:\n# 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white\n#NORMAL 00 # no color code at all\n#FILE 00 # regular file: use no color at all\nRESET 0 # reset to \"normal\" color\nDIR 01;34 # directory\nLINK 01;36 # symbolic link. (If you set this to 'target' instead of a\n # numerical value, the color is as for the file pointed to.)\nMULTIHARDLINK 00 # regular file with more than one link\nFIFO 40;33 # pipe\nSOCK 01;35 # socket\nDOOR 01;35 # door\nBLK 40;33;01 # block device driver\nCHR 40;33;01 # character device driver\nORPHAN 40;31;01 # symlink to nonexistent file, or non-stat'able file ...\nMISSING 00 # ... and the files they point to\nSETUID 37;41 # file that is setuid (u+s)\nSETGID 30;43 # file that is setgid (g+s)\nCAPABILITY 30;41 # file with capability\nSTICKY_OTHER_WRITABLE 30;42 # dir that is sticky and other-writable (+t,o+w)\nOTHER_WRITABLE 34;42 # dir that is other-writable (o+w) and not sticky\nSTICKY 37;44 # dir with the sticky bit set (+t) and not other-writable\n# This is for files with execute permission:\nEXEC 01;32\n# List any file extensions like '.gz' or '.tar' that you would like ls\n# to colorize below. Put the extension, a space, and the color init string.\n# (and any comments you want to add after a '#')\n# If you use DOS-style suffixes, you may want to uncomment the following:\n#.cmd 01;32 # executables (bright green)\n#.exe 01;32\n#.com 01;32\n#.btm 01;32\n#.bat 01;32\n# Or if you want to colorize scripts even if they do not have the\n# executable bit actually set.\n#.sh 01;32\n#.csh 01;32\n # archives or compressed (bright red)\n.tar 01;31\n.tgz 01;31\n.arc 01;31\n.arj 01;31\n.taz 01;31\n.lha 01;31\n.lz4 01;31\n.lzh 01;31\n.lzma 01;31\n.tlz 01;31\n.txz 01;31\n.tzo 01;31\n.t7z 01;31\n.zip 01;31\n.z 01;31\n.dz 01;31\n.gz 01;31\n.lrz 01;31\n.lz 01;31\n.lzo 01;31\n.xz 01;31\n.zst 01;31\n.tzst 01;31\n.bz2 01;31\n.bz 01;31\n.tbz 01;31\n.tbz2 01;31\n.tz 01;31\n.deb 01;31\n.rpm 01;31\n.jar 01;31\n.war 01;31\n.ear 01;31\n.sar 01;31\n.rar 01;31\n.alz 01;31\n.ace 01;31\n.zoo 01;31\n.cpio 01;31\n.7z 01;31\n.rz 01;31\n.cab 01;31\n.wim 01;31\n.swm 01;31\n.dwm 01;31\n.esd 01;31\n# image formats\n.jpg 01;35\n.jpeg 01;35\n.mjpg 01;35\n.mjpeg 01;35\n.gif 01;35\n.bmp 01;35\n.pbm 01;35\n.pgm 01;35\n.ppm 01;35\n.tga 01;35\n.xbm 01;35\n.xpm 01;35\n.tif 01;35\n.tiff 01;35\n.png 01;35\n.svg 01;35\n.svgz 01;35\n.mng 01;35\n.pcx 01;35\n.mov 01;35\n.mpg 01;35\n.mpeg 01;35\n.m2v 01;35\n.mkv 01;35\n.webm 01;35\n.ogm 01;35\n.mp4 01;35\n.m4v 01;35\n.mp4v 01;35\n.vob 01;35\n.qt 01;35\n.nuv 01;35\n.wmv 01;35\n.asf 01;35\n.rm 01;35\n.rmvb 01;35\n.flc 01;35\n.avi 01;35\n.fli 01;35\n.flv 01;35\n.gl 01;35\n.dl 01;35\n.xcf 01;35\n.xwd 01;35\n.yuv 01;35\n.cgm 01;35\n.emf 01;35\n# https://wiki.xiph.org/MIME_Types_and_File_Extensions\n.ogv 01;35\n.ogx 01;35\n# audio formats\n.aac 00;36\n.au 00;36\n.flac 00;36\n.m4a 00;36\n.mid 00;36\n.midi 00;36\n.mka 00;36\n.mp3 00;36\n.mpc 00;36\n.ogg 00;36\n.ra 00;36\n.wav 00;36\n# https://wiki.xiph.org/MIME_Types_and_File_Extensions\n.oga 00;36\n.opus 00;36\n.spx 00;36\n.xspf 00;36\n\"\"\"  # }}}\n\n# special file?\nspecial_types = (\n    (stat.S_IFLNK,  'ln'),  # symlink\n    (stat.S_IFIFO,  'pi'),  # pipe (FIFO)\n    (stat.S_IFSOCK, 'so'),  # socket\n    (stat.S_IFBLK,  'bd'),  # block device\n    (stat.S_IFCHR,  'cd'),  # character device\n    (stat.S_ISUID,  'su'),  # setuid\n    (stat.S_ISGID,  'sg'),  # setgid\n)\n\nCODE_MAP = {\n    'RESET': 'rs',\n    'DIR': 'di',\n    'LINK': 'ln',\n    'MULTIHARDLINK': 'mh',\n    'FIFO': 'pi',\n    'SOCK': 'so',\n    'DOOR': 'do',\n    'BLK': 'bd',\n    'CHR': 'cd',\n    'ORPHAN': 'or',\n    'MISSING': 'mi',\n    'SETUID': 'su',\n    'SETGID': 'sg',\n    'CAPABILITY': 'ca',\n    'STICKY_OTHER_WRITABLE': 'tw',\n    'OTHER_WRITABLE': 'ow',\n    'STICKY': 'st',\n    'EXEC': 'ex',\n}\n\n\ndef stat_at(file: str, cwd: int | str | None = None, follow_symlinks: bool = False) -> os.stat_result:\n    dirfd: int | None = None\n    need_to_close = False\n    if isinstance(cwd, str):\n        dirfd = os.open(cwd, os.O_RDONLY | getattr(os, 'O_CLOEXEC', 0))\n        need_to_close = True\n    elif isinstance(cwd, int):\n        dirfd = cwd\n\n    try:\n        return os.stat(file, dir_fd=dirfd, follow_symlinks=follow_symlinks)\n    finally:\n        if need_to_close and dirfd is not None:\n            os.close(dirfd)\n\n\nclass Dircolors:\n\n    def __init__(self) -> None:\n        self.codes: dict[str, str] = {}\n        self.extensions: dict[str, str] = {}\n        if not self.load_from_environ() and not self.load_from_file():\n            self.load_defaults()\n\n    def clear(self) -> None:\n        self.codes.clear()\n        self.extensions.clear()\n\n    def load_from_file(self) -> bool:\n        for candidate in (os.path.expanduser('~/.dir_colors'), '/etc/DIR_COLORS'):\n            with suppress(Exception):\n                with open(candidate) as f:\n                    return self.load_from_dircolors(f.read())\n        return False\n\n    def load_from_lscolors(self, lscolors: str) -> bool:\n        self.clear()\n        if not lscolors:\n            return False\n\n        for item in lscolors.split(':'):\n            try:\n                code, color = item.split('=', 1)\n            except ValueError:\n                continue\n            if code.startswith('*.'):\n                self.extensions[code[1:]] = color\n            else:\n                self.codes[code] = color\n\n        return bool(self.codes or self.extensions)\n\n    def load_from_environ(self, envvar: str = 'LS_COLORS') -> bool:\n        return self.load_from_lscolors(os.environ.get(envvar) or '')\n\n    def load_from_dircolors(self, database: str, strict: bool = False) -> bool:\n        self.clear()\n\n        for line in database.splitlines():\n            line = line.split('#')[0].strip()\n            if not line:\n                continue\n\n            split = line.split()\n            if len(split) != 2:\n                if strict:\n                    raise ValueError(f'Warning: unable to parse dircolors line \"{line}\"')\n                continue\n\n            key, val = split\n            if key == 'TERM':\n                continue\n            if key in CODE_MAP:\n                self.codes[CODE_MAP[key]] = val\n            elif key.startswith('.'):\n                self.extensions[key] = val\n            elif strict:\n                raise ValueError(f'Warning: unable to parse dircolors line \"{line}\"')\n\n        return bool(self.codes or self.extensions)\n\n    def load_defaults(self) -> bool:\n        self.clear()\n        return self.load_from_dircolors(DEFAULT_DIRCOLORS, True)\n\n    def generate_lscolors(self) -> str:\n        \"\"\" Output the database in the format used by the LS_COLORS environment variable. \"\"\"\n\n        def gen_pairs() -> Generator[tuple[str, str], None, None]:\n            for pair in self.codes.items():\n                yield pair\n            for pair in self.extensions.items():\n                # change .xyz to *.xyz\n                yield '*' + pair[0], pair[1]\n\n        return ':'.join('{}={}'.format(*pair) for pair in gen_pairs())\n\n    def _format_code(self, text: str, code: str) -> str:\n        val = self.codes.get(code)\n        return '\\033[{}m{}\\033[{}m'.format(val, text, self.codes.get('rs', '0')) if val else text\n\n    def _format_ext(self, text: str, ext: str) -> str:\n        val = self.extensions.get(ext, '0')\n        return '\\033[{}m{}\\033[{}m'.format(val, text, self.codes.get('rs', '0')) if val else text\n\n    def format_mode(self, text: str, sr: os.stat_result) -> str:\n        mode = sr.st_mode\n        if stat.S_ISDIR(mode):\n            if (mode & (stat.S_ISVTX | stat.S_IWOTH)) == (stat.S_ISVTX | stat.S_IWOTH):\n                # sticky and world-writable\n                return self._format_code(text, 'tw')\n            if mode & stat.S_ISVTX:\n                # sticky but not world-writable\n                return self._format_code(text, 'st')\n            if mode & stat.S_IWOTH:\n                # world-writable but not sticky\n                return self._format_code(text, 'ow')\n            # normal directory\n            return self._format_code(text, 'di')\n\n        for mask, code in special_types:\n            if (mode & mask) == mask:\n                return self._format_code(text, code)\n\n        # executable file?\n        if mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):\n            return self._format_code(text, 'ex')\n\n        # regular file, format according to its extension\n        ext = os.path.splitext(text)[1]\n        if ext:\n            return self._format_ext(text, ext)\n        return text\n\n    def __call__(self, path: str, text: str, cwd: int | str | None = None) -> str:\n        follow_symlinks = self.codes.get('ln') == 'target'\n        try:\n            sr = stat_at(path, cwd, follow_symlinks)\n        except OSError:\n            return text\n        return self.format_mode(text, sr)\n\n\ndef develop() -> None:\n    import sys\n    print(Dircolors()(sys.argv[-1], sys.argv[-1]))\n"
  },
  {
    "path": "kittens/tui/handler.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nfrom collections import deque\nfrom collections.abc import Callable, Sequence\nfrom contextlib import suppress\nfrom types import TracebackType\nfrom typing import TYPE_CHECKING, Any, ContextManager, Deque, NamedTuple, Optional, cast\n\nfrom kitty.constants import kitten_exe, running_in_kitty\nfrom kitty.fast_data_types import monotonic, safe_pipe\nfrom kitty.types import DecoratedFunc, ParsedShortcut\nfrom kitty.typing_compat import (\n    AbstractEventLoop,\n    BossType,\n    Debug,\n    ImageManagerType,\n    KeyActionType,\n    KeyEventType,\n    LoopType,\n    MouseButton,\n    MouseEvent,\n    ScreenSize,\n    TermManagerType,\n    WindowType,\n)\n\nfrom .operations import MouseTracking, pending_update\n\nif TYPE_CHECKING:\n    from kitty.file_transmission import FileTransmissionCommand\n\n\nOpenUrlHandler = Optional[Callable[[BossType, WindowType, str, int, str], bool]]\n\n\nclass ButtonEvent(NamedTuple):\n    mouse_event: MouseEvent\n    timestamp: float\n\n\ndef is_click(a: ButtonEvent, b: ButtonEvent) -> bool:\n    from .loop import EventType\n    if a.mouse_event.type is not EventType.PRESS or b.mouse_event.type is not EventType.RELEASE:\n        return False\n    x = a.mouse_event.cell_x - b.mouse_event.cell_x\n    y = a.mouse_event.cell_y - b.mouse_event.cell_y\n    return x*x + y*y <= 4\n\n\nclass KittenUI:\n    allow_remote_control: bool = False\n    remote_control_password: bool | str = False\n\n    def __init__(self, func: Callable[[list[str]], str], allow_remote_control: bool, remote_control_password: bool | str):\n        self.func = func\n        self.allow_remote_control = allow_remote_control\n        self.remote_control_password = remote_control_password\n        self.password = self.to = ''\n        self.rc_fd = -1\n        self.initialized = False\n\n    def initialize(self) -> None:\n        if self.initialized:\n            return\n        self.initialized = True\n        if running_in_kitty():\n            return\n        if self.allow_remote_control:\n            self.to = os.environ.get('KITTY_LISTEN_ON', '')\n            if not self.to:\n                raise ValueError('Remote control not enabled, this kitten should be run via a map in kitty.conf, not from the command line')\n            self.rc_fd = int(self.to.partition(':')[-1])\n            os.set_inheritable(self.rc_fd, False)\n        if (self.remote_control_password or self.remote_control_password == '') and not self.password:\n            import socket\n            with socket.fromfd(self.rc_fd, socket.AF_UNIX, socket.SOCK_STREAM) as s:\n                data = s.recv(256)\n            if not data.endswith(b'\\n'):\n                raise Exception(f'The remote control password was invalid: {data!r}')\n            self.password = data.strip().decode()\n\n    def __call__(self, args: list[str]) -> str:\n        self.initialize()\n        return self.func(args)\n\n    def allow_indiscriminate_remote_control(self, enable: bool = True) -> None:\n        if self.rc_fd > -1:\n            if enable:\n                os.set_inheritable(self.rc_fd, True)\n                if self.password:\n                    os.environ['KITTY_RC_PASSWORD'] = self.password\n            else:\n                os.set_inheritable(self.rc_fd, False)\n                if self.password:\n                    os.environ.pop('KITTY_RC_PASSWORD', None)\n\n    def remote_control(self, cmd: str | Sequence[str], **kw: Any) -> Any:\n        if not self.allow_remote_control:\n            raise ValueError('Remote control is not enabled, remember to use allow_remote_control=True')\n        prefix = [kitten_exe(), '@']\n        r = -1\n        pass_fds = list(kw.get('pass_fds') or ())\n        try:\n            if self.rc_fd > -1:\n                pass_fds.append(self.rc_fd)\n            if self.password and self.rc_fd > -1:\n                r, w = safe_pipe(False)\n                os.write(w, self.password.encode())\n                os.close(w)\n                prefix += ['--password-file', f'fd:{r}', '--use-password', 'always']\n                pass_fds.append(r)\n            if pass_fds:\n                kw['pass_fds'] = tuple(pass_fds)\n            if isinstance(cmd, str):\n                cmd = ' '.join(prefix)\n            else:\n                cmd = prefix + list(cmd)\n            import subprocess\n            if self.rc_fd > -1:\n                is_inheritable = os.get_inheritable(self.rc_fd)\n                if not is_inheritable:\n                    os.set_inheritable(self.rc_fd, True)\n            try:\n                return subprocess.run(cmd, **kw)\n            finally:\n                if self.rc_fd > -1 and not is_inheritable:\n                    os.set_inheritable(self.rc_fd, False)\n        finally:\n            if r > -1:\n                os.close(r)\n\n\ndef kitten_ui(\n    allow_remote_control: bool = KittenUI.allow_remote_control,\n    remote_control_password: bool | str = KittenUI.allow_remote_control,\n) -> Callable[[Callable[[list[str]], str]], KittenUI]:\n\n    def wrapper(impl: Callable[..., Any]) -> KittenUI:\n        return KittenUI(impl, allow_remote_control, remote_control_password)\n\n    return wrapper\n\n\nclass Handler:\n\n    image_manager_class: type[ImageManagerType] | None = None\n    use_alternate_screen = True\n    mouse_tracking = MouseTracking.none\n    terminal_io_ended = False\n    overlay_ready_report_needed = False\n\n    def _initialize(\n        self,\n        screen_size: ScreenSize,\n        term_manager: TermManagerType,\n        schedule_write: Callable[[bytes], None],\n        tui_loop: LoopType,\n        debug: Debug,\n        image_manager: ImageManagerType | None = None\n    ) -> None:\n        from .operations import commander\n        self.screen_size = screen_size\n        self._term_manager = term_manager\n        self._tui_loop = tui_loop\n        self._schedule_write = schedule_write\n        self.debug = debug\n        self.cmd = commander(self)\n        self._image_manager = image_manager\n        self._button_events: dict[MouseButton, Deque[ButtonEvent]] = {}\n\n    @property\n    def image_manager(self) -> ImageManagerType:\n        assert self._image_manager is not None\n        return self._image_manager\n\n    @property\n    def asyncio_loop(self) -> AbstractEventLoop:\n        return self._tui_loop.asyncio_loop\n\n    def add_shortcut(self, action: KeyActionType, spec: str | ParsedShortcut) -> None:\n        if not hasattr(self, '_key_shortcuts'):\n            self._key_shortcuts: dict[ParsedShortcut, KeyActionType] = {}\n        if isinstance(spec, str):\n            from kitty.key_encoding import parse_shortcut\n            spec = parse_shortcut(spec)\n        self._key_shortcuts[spec] = action\n\n    def shortcut_action(self, key_event: KeyEventType) -> KeyActionType | None:\n        for sc, action in self._key_shortcuts.items():\n            if key_event.matches(sc):\n                return action\n        return None\n\n    def __enter__(self) -> None:\n        if self._image_manager is not None:\n            self._image_manager.__enter__()\n        self.debug.fobj = self\n        self.initialize()\n\n    def __exit__(self, etype: type, value: Exception, tb: TracebackType) -> None:\n        del self.debug.fobj\n        with suppress(Exception):\n            self.finalize()\n            if self._image_manager is not None:\n                self._image_manager.__exit__(etype, value, tb)\n\n    def initialize(self) -> None:\n        pass\n\n    def finalize(self) -> None:\n        pass\n\n    def on_resize(self, screen_size: ScreenSize) -> None:\n        self.screen_size = screen_size\n\n    def quit_loop(self, return_code: int | None = None) -> None:\n        self._tui_loop.quit(return_code)\n\n    def on_term(self) -> None:\n        self._tui_loop.quit(1)\n\n    def on_hup(self) -> None:\n        self.terminal_io_ended = True\n        self._tui_loop.quit(1)\n\n    def on_key_event(self, key_event: KeyEventType, in_bracketed_paste: bool = False) -> None:\n        ' Override this method and perform_default_key_action() to handle all key events '\n        if key_event.text:\n            self.on_text(key_event.text, in_bracketed_paste)\n        else:\n            self.on_key(key_event)\n\n    def perform_default_key_action(self, key_event: KeyEventType) -> bool:\n        ' Override in sub-class if you want to handle these key events yourself '\n        if key_event.matches('ctrl+c'):\n            self.on_interrupt()\n            return True\n        if key_event.matches('ctrl+d'):\n            self.on_eot()\n            return True\n        return False\n\n    def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:\n        pass\n\n    def on_key(self, key_event: KeyEventType) -> None:\n        pass\n\n    def on_mouse_event(self, mouse_event: MouseEvent) -> None:\n        from .loop import EventType\n        if mouse_event.type is EventType.MOVE:\n            self.on_mouse_move(mouse_event)\n        elif mouse_event.type is EventType.PRESS:\n            q = self._button_events.setdefault(mouse_event.buttons, deque())\n            q.append(ButtonEvent(mouse_event, monotonic()))\n            if len(q) > 5:\n                q.popleft()\n        elif mouse_event.type is EventType.RELEASE:\n            q = self._button_events.setdefault(mouse_event.buttons, deque())\n            q.append(ButtonEvent(mouse_event, monotonic()))\n            if len(q) > 5:\n                q.popleft()\n            if len(q) > 1 and is_click(q[-2], q[-1]):\n                self.on_click(mouse_event)\n\n    def on_mouse_move(self, mouse_event: MouseEvent) -> None:\n        pass\n\n    def on_click(self, mouse_event: MouseEvent) -> None:\n        pass\n\n    def on_interrupt(self) -> None:\n        pass\n\n    def on_eot(self) -> None:\n        pass\n\n    def on_writing_finished(self) -> None:\n        pass\n\n    def on_kitty_cmd_response(self, response: dict[str, Any]) -> None:\n        pass\n\n    def on_clipboard_response(self, text: str, from_primary: bool = False) -> None:\n        pass\n\n    def on_file_transfer_response(self, ftc: 'FileTransmissionCommand') -> None:\n        pass\n\n    def on_capability_response(self, name: str, val: str) -> None:\n        pass\n\n    def write(self, data: bytes | str) -> None:\n        if isinstance(data, str):\n            data = data.encode('utf-8')\n        self._schedule_write(data)\n\n    def flush(self) -> None:\n        pass\n\n    def print(self, *args: object, sep: str = ' ', end: str = '\\r\\n') -> None:\n        data = sep.join(map(str, args)) + end\n        self.write(data)\n\n    def suspend(self) -> ContextManager[TermManagerType]:\n        return self._term_manager.suspend()\n\n    @classmethod\n    def atomic_update(cls, func: DecoratedFunc) -> DecoratedFunc:\n        from functools import wraps\n\n        @wraps(func)\n        def f(*a: Any, **kw: Any) -> Any:\n            with pending_update(a[0].write):\n                return func(*a, **kw)\n        return cast(DecoratedFunc, f)\n\n\nclass HandleResult:\n\n    type_of_input: str | None = None\n    no_ui: bool = False\n\n    def __init__(self, impl: Callable[..., Any], type_of_input: str | None, no_ui: bool, has_ready_notification: bool, open_url_handler: OpenUrlHandler):\n        self.impl = impl\n        self.no_ui = no_ui\n        self.type_of_input = type_of_input\n        self.has_ready_notification = has_ready_notification\n        self.open_url_handler = open_url_handler\n\n    def __call__(self, args: Sequence[str], data: Any, target_window_id: int, boss: BossType) -> Any:\n        return self.impl(args, data, target_window_id, boss)\n\n\n\ndef result_handler(\n    type_of_input: str | None = None,\n    no_ui: bool = False,\n    has_ready_notification: bool = Handler.overlay_ready_report_needed,\n    open_url_handler: OpenUrlHandler = None,\n) -> Callable[[Callable[..., Any]], HandleResult]:\n\n    def wrapper(impl: Callable[..., Any]) -> HandleResult:\n        return HandleResult(impl, type_of_input, no_ui, has_ready_notification, open_url_handler)\n\n    return wrapper\n"
  },
  {
    "path": "kittens/tui/images.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport codecs\nimport os\nimport sys\nfrom base64 import standard_b64encode\nfrom collections import defaultdict, deque\nfrom collections.abc import Callable, Iterator, Sequence\nfrom contextlib import suppress\nfrom enum import IntEnum\nfrom itertools import count\nfrom typing import Any, ClassVar, DefaultDict, Deque, Generic, Optional, TypeVar, Union, cast\n\nfrom kitty.conf.utils import positive_float, positive_int\nfrom kitty.fast_data_types import create_canvas\nfrom kitty.typing_compat import CompletedProcess, GRT_f, GRT_o, HandlerType\nfrom kitty.utils import ScreenSize, fit_image, which\n\nfrom .operations import cursor\n\ntry:\n    fsenc = sys.getfilesystemencoding() or 'utf-8'\n    codecs.lookup(fsenc)\nexcept Exception:\n    fsenc = 'utf-8'\n\n\nclass Dispose(IntEnum):\n    undefined = 0\n    none = 1\n    background = 2\n    previous = 3\n\n\nclass Frame:\n    gap: int  # milliseconds\n    canvas_width: int\n    canvas_height: int\n    width: int\n    height: int\n    index: int\n    xdpi: float\n    ydpi: float\n    canvas_x: int\n    canvas_y: int\n    mode: str\n    needs_blend: bool\n    dimensions_swapped: bool\n    dispose: Dispose\n    path: str = ''\n\n    def __init__(self, identify_data: Union['Frame', dict[str, str]]):\n        if isinstance(identify_data, Frame):\n            for k in Frame.__annotations__:\n                setattr(self, k, getattr(identify_data, k))\n        else:\n            self.gap = max(0, int(identify_data['gap']) * 10)\n            sz, pos = identify_data['canvas'].split('+', 1)\n            self.canvas_width, self.canvas_height = map(positive_int, sz.split('x', 1))\n            self.canvas_x, self.canvas_y = map(int, pos.split('+', 1))\n            self.width, self.height = map(positive_int, identify_data['size'].split('x', 1))\n            self.xdpi, self.ydpi = map(positive_float, identify_data['dpi'].split('x', 1))\n            self.index = positive_int(identify_data['index'])\n            q = identify_data['transparency'].lower()\n            self.mode = 'rgba' if q in ('blend', 'true') else 'rgb'\n            self.needs_blend = q == 'blend'\n            self.dispose = getattr(Dispose, identify_data['dispose'].lower())\n            self.dimensions_swapped = identify_data.get('orientation') in ('5', '6', '7', '8')\n            if self.dimensions_swapped:\n                self.canvas_width, self.canvas_height = self.canvas_height, self.canvas_width\n                self.width, self.height = self.height, self.width\n\n    def __repr__(self) -> str:\n        canvas = f'{self.canvas_width}x{self.canvas_height}:{self.canvas_x}+{self.canvas_y}'\n        geom = f'{self.width}x{self.height}'\n        return f'Frame(index={self.index}, gap={self.gap}, geom={geom}, canvas={canvas}, dispose={self.dispose.name})'\n\n\nclass ImageData:\n\n    def __init__(self, fmt: str, width: int, height: int, mode: str, frames: list[Frame]):\n        self.width, self.height, self.fmt, self.mode = width, height, fmt, mode\n        self.transmit_fmt: GRT_f = (24 if self.mode == 'rgb' else 32)\n        self.frames = frames\n\n    def __len__(self) -> int:\n        return len(self.frames)\n\n    def __iter__(self) -> Iterator[Frame]:\n        yield from self.frames\n\n    def __repr__(self) -> str:\n        frames = '\\n  '.join(map(repr, self.frames))\n        return f'Image(fmt={self.fmt}, mode={self.mode},\\n  {frames}\\n)'\n\n\nclass OpenFailed(ValueError):\n\n    def __init__(self, path: str, message: str):\n        ValueError.__init__(\n            self, f'Failed to open image: {path} with error: {message}'\n        )\n        self.path = path\n\n\nclass ConvertFailed(ValueError):\n\n    def __init__(self, path: str, message: str):\n        ValueError.__init__(\n            self, f'Failed to convert image: {path} with error: {message}'\n        )\n        self.path = path\n\n\nclass NoImageMagick(Exception):\n    pass\n\n\nclass OutdatedImageMagick(ValueError):\n\n    def __init__(self, detailed_error: str):\n        super().__init__('ImageMagick on this system is too old ImageMagick 7+ required which was first released in 2016')\n        self.detailed_error = detailed_error\n\n\nlast_imagemagick_cmd: Sequence[str] = ()\n\n\ndef run_imagemagick(path: str, cmd: Sequence[str], keep_stdout: bool = True) -> 'CompletedProcess[bytes]':\n    global last_imagemagick_cmd\n    import subprocess\n    last_imagemagick_cmd = cmd\n    try:\n        p = subprocess.run(cmd, stdout=subprocess.PIPE if keep_stdout else subprocess.DEVNULL, stderr=subprocess.PIPE)\n    except FileNotFoundError:\n        raise NoImageMagick('ImageMagick is required to process images')\n    if p.returncode != 0:\n        raise OpenFailed(path, p.stderr.decode('utf-8'))\n    return p\n\n\ndef identify(path: str) -> ImageData:\n    import json\n    q = (\n        '{\"fmt\":\"%m\",\"canvas\":\"%g\",\"transparency\":\"%A\",\"gap\":\"%T\",\"index\":\"%p\",\"size\":\"%wx%h\",'\n        '\"dpi\":\"%xx%y\",\"dispose\":\"%D\",\"orientation\":\"%[EXIF:Orientation]\"},'\n    )\n    exe = which('magick')\n    if exe:\n        cmd = [exe, 'identify']\n    else:\n        cmd = ['identify']\n    p = run_imagemagick(path, cmd + ['-format', q, '--', path])\n    raw = p.stdout.rstrip(b',')\n    data = json.loads(b'[' + raw + b']')\n    first = data[0]\n    frames = list(map(Frame, data))\n    image_fmt = first['fmt'].lower()\n    if image_fmt == 'gif' and not any(f.gap > 0 for f in frames):\n        # Some broken GIF images have all zero gaps, browsers with their usual\n        # idiot ideas render these with a default 100ms gap https://bugzilla.mozilla.org/show_bug.cgi?id=125137\n        # Browsers actually force a 100ms gap at any zero gap frame, but that\n        # just means it is impossible to deliberately use zero gap frames for\n        # sophisticated blending, so we dont do that.\n        for f in frames:\n            f.gap = 100\n    mode = 'rgb'\n    for f in frames:\n        if f.mode == 'rgba':\n            mode = 'rgba'\n            break\n    return ImageData(image_fmt, frames[0].canvas_width, frames[0].canvas_height, mode, frames)\n\n\nclass RenderedImage(ImageData):\n\n    def __init__(self, fmt: str, width: int, height: int, mode: str):\n        super().__init__(fmt, width, height, mode, [])\n\n\ndef render_image(\n    path: str, output_prefix: str,\n    m: ImageData,\n    available_width: int, available_height: int,\n    scale_up: bool,\n    only_first_frame: bool = False,\n    remove_alpha: str = '',\n    flip: bool = False, flop: bool = False,\n) -> RenderedImage:\n    import tempfile\n    has_multiple_frames = len(m) > 1\n    get_multiple_frames = has_multiple_frames and not only_first_frame\n    exe = which('magick')\n    if exe:\n        cmd = [exe, 'convert']\n    else:\n        exe = which('convert')\n        if exe is None:\n            raise OSError('Failed to find the ImageMagick convert executable, make sure it is present in PATH')\n        cmd = [exe]\n    if remove_alpha:\n        cmd += ['-background', remove_alpha, '-alpha', 'remove']\n    else:\n        cmd += ['-background', 'none']\n    if flip:\n        cmd.append('-flip')\n    if flop:\n        cmd.append('-flop')\n    cmd += ['--', path]\n    if only_first_frame and has_multiple_frames:\n        cmd[-1] += '[0]'\n    cmd.append('-auto-orient')\n    scaled = False\n    width, height = m.width, m.height\n    if scale_up:\n        if width < available_width:\n            r = available_width / width\n            width, height = available_width, int(height * r)\n            scaled = True\n    if scaled or width > available_width or height > available_height:\n        width, height = fit_image(width, height, available_width, available_height)\n        resize_cmd = ['-resize', f'{width}x{height}!']\n        if get_multiple_frames:\n            # we have to coalesce, resize and de-coalesce all frames\n            resize_cmd = ['-coalesce'] + resize_cmd + ['-deconstruct']\n        cmd += resize_cmd\n    cmd += ['-depth', '8', '-set', 'filename:f', '%w-%h-%g-%p']\n    ans = RenderedImage(m.fmt, width, height, m.mode)\n    if only_first_frame:\n        ans.frames = [Frame(m.frames[0])]\n    else:\n        ans.frames = list(map(Frame, m.frames))\n    bytes_per_pixel = 3 if m.mode == 'rgb' else 4\n\n    def check_resize(frame: Frame) -> None:\n        # ImageMagick sometimes generates RGBA images smaller than the specified\n        # size. See https://github.com/kovidgoyal/kitty/issues/276 for examples\n        sz = os.path.getsize(frame.path)\n        expected_size = bytes_per_pixel * frame.width * frame.height\n        if sz < expected_size:\n            missing = expected_size - sz\n            if missing % (bytes_per_pixel * width) != 0:\n                raise ConvertFailed(\n                    path, 'ImageMagick failed to convert {} correctly,'\n                    ' it generated {} < {} of data (w={}, h={}, bpp={})'.format(\n                        path, sz, expected_size, frame.width, frame.height, bytes_per_pixel))\n            frame.height -= missing // (bytes_per_pixel * frame.width)\n            if frame.index == 0:\n                ans.height = frame.height\n                ans.width = frame.width\n\n    with tempfile.TemporaryDirectory(dir=os.path.dirname(output_prefix)) as tdir:\n        output_template = os.path.join(tdir, f'im-%[filename:f].{m.mode}')\n        if get_multiple_frames:\n            cmd.append('+adjoin')\n        run_imagemagick(path, cmd + [output_template])\n        unseen = {x.index for x in m}\n        for x in os.listdir(tdir):\n            try:\n                parts = x.split('.', 1)[0].split('-')\n                index = int(parts[-1])\n                unseen.discard(index)\n                f = ans.frames[index]\n                f.width, f.height = map(positive_int, parts[1:3])\n                sz, pos = parts[3].split('+', 1)\n                f.canvas_width, f.canvas_height = map(positive_int, sz.split('x', 1))\n                f.canvas_x, f.canvas_y = map(int, pos.split('+', 1))\n            except Exception:\n                raise OutdatedImageMagick(f'Unexpected output filename: {x!r} produced by ImageMagick command: {last_imagemagick_cmd}')\n            f.path = output_prefix + f'-{index}.{m.mode}'\n            os.rename(os.path.join(tdir, x), f.path)\n            check_resize(f)\n    f = ans.frames[0]\n    if f.width != ans.width or f.height != ans.height:\n        with open(f.path, 'r+b') as ff:\n            data = ff.read()\n            ff.seek(0)\n            ff.truncate()\n            cd = create_canvas(data, f.width, f.canvas_x, f.canvas_y, ans.width, ans.height, 3 if ans.mode == 'rgb' else 4)\n            ff.write(cd)\n    if get_multiple_frames:\n        if unseen:\n            raise ConvertFailed(path, f'Failed to render {len(unseen)} out of {len(m)} frames of animation')\n    elif not ans.frames[0].path:\n        raise ConvertFailed(path, 'Failed to render image')\n\n    return ans\n\n\ndef render_as_single_image(\n    path: str, m: ImageData,\n    available_width: int, available_height: int,\n    scale_up: bool,\n    tdir: str | None = None,\n    remove_alpha: str = '', flip: bool = False, flop: bool = False,\n) -> tuple[str, int, int]:\n    import tempfile\n    fd, output = tempfile.mkstemp(prefix='tty-graphics-protocol-', suffix=f'.{m.mode}', dir=tdir)\n    os.close(fd)\n    result = render_image(\n        path, output, m, available_width, available_height, scale_up,\n        only_first_frame=True, remove_alpha=remove_alpha, flip=flip, flop=flop)\n    os.rename(result.frames[0].path, output)\n    return output, result.width, result.height\n\n\ndef can_display_images() -> bool:\n    ans: bool | None = getattr(can_display_images, 'ans', None)\n    if ans is None:\n        ans = which('convert') is not None\n        setattr(can_display_images, 'ans', ans)\n    return ans\n\n\nImageKey = tuple[str, int, int]\nSentImageKey = tuple[int, int, int]\nT = TypeVar('T')\n\n\nclass Alias(Generic[T]):\n\n    currently_processing: ClassVar[str] = ''\n\n    def __init__(self, defval: T) -> None:\n        self.name = ''\n        self.defval = defval\n\n    def __get__(self, instance: Optional['GraphicsCommand'], cls: type['GraphicsCommand'] | None = None) -> T:\n        if instance is None:\n            return self.defval\n        return cast(T, instance._actual_values.get(self.name, self.defval))\n\n    def __set__(self, instance: 'GraphicsCommand', val: T) -> None:\n        if val == self.defval:\n            instance._actual_values.pop(self.name, None)\n        else:\n            instance._actual_values[self.name] = val\n\n    def __set_name__(self, owner: type['GraphicsCommand'], name: str) -> None:\n        if len(name) == 1:\n            Alias.currently_processing = name\n        self.name = Alias.currently_processing\n\n\nclass GraphicsCommand:\n    a = action = Alias('t')\n    q = quiet = Alias(0)\n    f = format = Alias(32)\n    t = transmission_type = Alias('d')\n    s = data_width = animation_state = Alias(0)\n    v = data_height = loop_count = Alias(0)\n    S = data_size = Alias(0)\n    O = data_offset = Alias(0)  # noqa\n    i = image_id = Alias(0)\n    I = image_number = Alias(0)  # noqa\n    p = placement_id = Alias(0)\n    o = compression = Alias(cast(Optional[GRT_o], None))\n    m = more = Alias(0)\n    x = left_edge = Alias(0)\n    y = top_edge = Alias(0)\n    w = width = Alias(0)\n    h = height = Alias(0)\n    X = cell_x_offset = blend_mode = Alias(0)\n    Y = cell_y_offset = bgcolor = Alias(0)\n    c = columns = other_frame_number = dest_frame = Alias(0)\n    r = rows = frame_number = source_frame = Alias(0)\n    z = z_index = gap = Alias(0)\n    C = cursor_movement = compose_mode = Alias(0)\n    d = delete_action = Alias('a')\n\n    def __init__(self) -> None:\n        self._actual_values: dict[str, Any] = {}\n\n    def __repr__(self) -> str:\n        return self.serialize().decode('ascii').replace('\\033', '^]')\n\n    def clone(self) -> 'GraphicsCommand':\n        ans = GraphicsCommand()\n        ans._actual_values = self._actual_values.copy()\n        return ans\n\n    def serialize(self, payload: bytes | memoryview | str = b'') -> bytes:\n        items = []\n        for k, val in self._actual_values.items():\n            items.append(f'{k}={val}')\n\n        ans: list[bytes|memoryview] = []\n        w = ans.append\n        w(b'\\033_G')\n        w(','.join(items).encode('ascii'))\n        if payload:\n            w(b';')\n            if isinstance(payload, str):\n                payload = standard_b64encode(payload.encode('utf-8'))\n            w(payload)\n        w(b'\\033\\\\')\n        return b''.join(ans)\n\n    def clear(self) -> None:\n        self._actual_values = {}\n\n    def iter_transmission_chunks(self, data: bytes | None = None, level: int = -1, compression_threshold: int = 1024) -> Iterator[bytes]:\n        if data is None:\n            yield self.serialize()\n            return\n        gc = self.clone()\n        gc.S = len(data)\n        if level and len(data) >= compression_threshold:\n            import zlib\n            compressed = zlib.compress(data, level)\n            if len(compressed) < len(data):\n                gc.o = 'z'\n                data = compressed\n                gc.S = len(data)\n        data = standard_b64encode(data)\n        while data:\n            chunk, data = data[:4096], data[4096:]\n            gc.m = 1 if data else 0\n            yield gc.serialize(chunk)\n            gc.clear()\n\n\nclass Placement:\n    cmd: GraphicsCommand\n    x: int = 0\n    y: int = 0\n\n    def __init__(self, cmd: GraphicsCommand, x: int = 0, y: int = 0):\n        self.cmd = cmd\n        self.x = x\n        self.y = y\n\n\nclass ImageManager:\n\n    def __init__(self, handler: HandlerType):\n        self.image_id_counter = count()\n        self.handler = handler\n        self.filesystem_ok: bool | None = None\n        self.image_data: dict[str, ImageData] = {}\n        self.failed_images: dict[str, Exception] = {}\n        self.converted_images: dict[ImageKey, ImageKey] = {}\n        self.sent_images: dict[ImageKey, int] = {}\n        self.image_id_to_image_data: dict[int, ImageData] = {}\n        self.image_id_to_converted_data: dict[int, ImageKey] = {}\n        self.transmission_status: dict[int, str | int] = {}\n        self.placements_in_flight: DefaultDict[int, Deque[Placement]] = defaultdict(deque)\n        self.update_image_placement_for_resend: Callable[[int, Placement], bool] | None\n\n    @property\n    def next_image_id(self) -> int:\n        return next(self.image_id_counter) + 2\n\n    @property\n    def screen_size(self) -> ScreenSize:\n        return self.handler.screen_size\n\n    def __enter__(self) -> None:\n        import tempfile\n        self.tdir = tempfile.mkdtemp(prefix='kitten-images-')\n        with tempfile.NamedTemporaryFile(dir=self.tdir, delete=False) as f:\n            f.write(b'abcd')\n        gc = GraphicsCommand()\n        gc.a = 'q'\n        gc.s = gc.v = gc.i = 1\n        gc.t = 'f'\n        self.handler.cmd.gr_command(gc, standard_b64encode(f.name.encode(fsenc)))\n\n    def __exit__(self, *a: Any) -> None:\n        import shutil\n        shutil.rmtree(self.tdir, ignore_errors=True)\n        self.handler.cmd.clear_images_on_screen(delete_data=True)\n        self.delete_all_sent_images()\n        del self.handler\n\n    def delete_all_sent_images(self) -> None:\n        gc = GraphicsCommand()\n        gc.a = 'd'\n        for img_id in self.transmission_status:\n            gc.i = img_id\n            self.handler.cmd.gr_command(gc)\n        self.transmission_status.clear()\n\n    def handle_response(self, apc: str) -> None:\n        cdata, payload = apc[1:].partition(';')[::2]\n        control = {}\n        for x in cdata.split(','):\n            k, v = x.partition('=')[::2]\n            control[k] = v\n        try:\n            image_id = int(control.get('i', '0'))\n        except Exception:\n            image_id = 0\n        if image_id == 1:\n            self.filesystem_ok = payload == 'OK'\n            return\n        if not image_id:\n            return\n        if not self.transmission_status.get(image_id):\n            self.transmission_status[image_id] = payload\n        else:\n            in_flight = self.placements_in_flight[image_id]\n            if in_flight:\n                pl = in_flight.popleft()\n                if payload.startswith('ENOENT:'):\n                    with suppress(Exception):\n                        self.resend_image(image_id, pl)\n                if not in_flight:\n                    self.placements_in_flight.pop(image_id, None)\n\n    def resend_image(self, image_id: int, pl: Placement) -> None:\n        if self.update_image_placement_for_resend is not None and not self.update_image_placement_for_resend(image_id, pl):\n            return\n        image_data = self.image_id_to_image_data[image_id]\n        skey = self.image_id_to_converted_data[image_id]\n        self.transmit_image(image_data, image_id, *skey)\n        with cursor(self.handler.write):\n            self.handler.cmd.set_cursor_position(pl.x, pl.y)\n            self.handler.cmd.gr_command(pl.cmd)\n\n    def send_image(self, path: str, max_cols: int | None = None, max_rows: int | None = None, scale_up: bool = False) -> SentImageKey:\n        path = os.path.abspath(path)\n        if path in self.failed_images:\n            raise self.failed_images[path]\n        if path not in self.image_data:\n            try:\n                self.image_data[path] = identify(path)\n            except Exception as e:\n                self.failed_images[path] = e\n                raise\n        m = self.image_data[path]\n        ss = self.screen_size\n        if max_cols is None:\n            max_cols = ss.cols\n        if max_rows is None:\n            max_rows = ss.rows\n        available_width = max_cols * ss.cell_width\n        available_height = max_rows * ss.cell_height\n        key = path, available_width, available_height\n        skey = self.converted_images.get(key)\n        if skey is None:\n            try:\n                self.converted_images[key] = skey = self.convert_image(path, available_width, available_height, m, scale_up)\n            except Exception as e:\n                self.failed_images[path] = e\n                raise\n        final_width, final_height = skey[1:]\n        if final_width == 0:\n            return 0, 0, 0\n        image_id = self.sent_images.get(skey)\n        if image_id is None:\n            image_id = self.next_image_id\n            self.transmit_image(m, image_id, *skey)\n            self.sent_images[skey] = image_id\n        self.image_id_to_converted_data[image_id] = skey\n        self.image_id_to_image_data[image_id] = m\n        return image_id, skey[1], skey[2]\n\n    def hide_image(self, image_id: int) -> None:\n        gc = GraphicsCommand()\n        gc.a = 'd'\n        gc.i = image_id\n        self.handler.cmd.gr_command(gc)\n\n    def show_image(self, image_id: int, x: int, y: int, src_rect: tuple[int, int, int, int] | None = None) -> None:\n        gc = GraphicsCommand()\n        gc.a = 'p'\n        gc.i = image_id\n        if src_rect is not None:\n            gc.x, gc.y, gc.w, gc.h = map(int, src_rect)\n        self.placements_in_flight[image_id].append(Placement(gc, x, y))\n        with cursor(self.handler.write):\n            self.handler.cmd.set_cursor_position(x, y)\n            self.handler.cmd.gr_command(gc)\n\n    def convert_image(self, path: str, available_width: int, available_height: int, image_data: ImageData, scale_up: bool = False) -> ImageKey:\n        rgba_path, width, height = render_as_single_image(path, image_data, available_width, available_height, scale_up, tdir=self.tdir)\n        return rgba_path, width, height\n\n    def transmit_image(self, image_data: ImageData, image_id: int, rgba_path: str, width: int, height: int) -> int:\n        self.transmission_status[image_id] = 0\n        gc = GraphicsCommand()\n        gc.a = 't'\n        gc.f = image_data.transmit_fmt\n        gc.s = width\n        gc.v = height\n        gc.i = image_id\n        if self.filesystem_ok:\n            gc.t = 'f'\n            self.handler.cmd.gr_command(\n                gc, standard_b64encode(rgba_path.encode(fsenc)))\n        else:\n            import zlib\n            with open(rgba_path, 'rb') as f:\n                data = f.read()\n            gc.S = len(data)\n            data = zlib.compress(data)\n            gc.o = 'z'\n            data = standard_b64encode(data)\n            while data:\n                chunk, data = data[:4096], data[4096:]\n                gc.m = 1 if data else 0\n                self.handler.cmd.gr_command(gc, chunk)\n                gc.clear()\n        return image_id\n"
  },
  {
    "path": "kittens/tui/line_edit.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Callable\n\nfrom kitty.fast_data_types import truncate_point_for_length, wcswidth\nfrom kitty.key_encoding import EventType, KeyEvent\n\nfrom .operations import RESTORE_CURSOR, SAVE_CURSOR, move_cursor_by, set_cursor_shape\n\n\nclass LineEdit:\n\n    def __init__(self, is_password: bool = False) -> None:\n        self.clear()\n        self.is_password = is_password\n\n    def clear(self) -> None:\n        self.current_input = ''\n        self.cursor_pos = 0\n        self.pending_bell = False\n\n    def split_at_cursor(self, delta: int = 0) -> tuple[str, str]:\n        pos = max(0, self.cursor_pos + delta)\n        x = truncate_point_for_length(self.current_input, pos) if pos else 0\n        before, after = self.current_input[:x], self.current_input[x:]\n        return before, after\n\n    def write(self, write: Callable[[str], None], prompt: str = '', screen_cols: int = 0) -> None:\n        if self.pending_bell:\n            write('\\a')\n            self.pending_bell = False\n        ci = self.current_input\n        if self.is_password:\n            ci = '*' * wcswidth(ci)\n        text = prompt + ci\n        cursor_pos = self.cursor_pos + wcswidth(prompt)\n        if screen_cols:\n            write(SAVE_CURSOR + text + RESTORE_CURSOR)\n            used_lines, last_line_cursor_pos = divmod(cursor_pos, screen_cols)\n            if used_lines == 0:\n                if last_line_cursor_pos:\n                    write(move_cursor_by(last_line_cursor_pos, 'right'))\n            else:\n                if used_lines:\n                    write(move_cursor_by(used_lines, 'down'))\n                if last_line_cursor_pos:\n                    write(move_cursor_by(last_line_cursor_pos, 'right'))\n        else:\n            write(text)\n            write('\\r')\n            if cursor_pos:\n                write(move_cursor_by(cursor_pos, 'right'))\n            write(set_cursor_shape('beam'))\n\n    def add_text(self, text: str) -> None:\n        if self.current_input:\n            x = truncate_point_for_length(self.current_input, self.cursor_pos) if self.cursor_pos else 0\n            self.current_input = self.current_input[:x] + text + self.current_input[x:]\n        else:\n            self.current_input = text\n        self.cursor_pos += wcswidth(text)\n\n    def on_text(self, text: str, in_bracketed_paste: bool) -> None:\n        self.add_text(text)\n\n    def backspace(self, num: int = 1) -> bool:\n        before, after = self.split_at_cursor()\n        nbefore = before[:-num]\n        if nbefore != before:\n            self.current_input = nbefore + after\n            self.cursor_pos = wcswidth(nbefore)\n            return True\n        self.pending_bell = True\n        return False\n\n    def delete(self, num: int = 1) -> bool:\n        before, after = self.split_at_cursor()\n        nafter = after[num:]\n        if nafter != after:\n            self.current_input = before + nafter\n            self.cursor_pos = wcswidth(before)\n            return True\n        self.pending_bell = True\n        return False\n\n    def _left(self) -> None:\n        if not self.current_input:\n            self.cursor_pos = 0\n            return\n        if self.cursor_pos:\n            before, after = self.split_at_cursor(-1)\n            self.cursor_pos = wcswidth(before)\n\n    def _right(self) -> None:\n        if not self.current_input:\n            self.cursor_pos = 0\n            return\n        max_pos = wcswidth(self.current_input)\n        if self.cursor_pos >= max_pos:\n            self.cursor_pos = max_pos\n            return\n        before, after = self.split_at_cursor(1)\n        self.cursor_pos += 1 + int(wcswidth(before) == self.cursor_pos)\n\n    def _move_loop(self, func: Callable[[], None], num: int) -> bool:\n        before = self.cursor_pos\n        changed = False\n        while num > 0:\n            func()\n            changed = self.cursor_pos != before\n            if not changed:\n                break\n            num -= 1\n        if not changed:\n            self.pending_bell = True\n        return changed\n\n    def left(self, num: int = 1) -> bool:\n        return self._move_loop(self._left, num)\n\n    def right(self, num: int = 1) -> bool:\n        return self._move_loop(self._right, num)\n\n    def home(self) -> bool:\n        if self.cursor_pos:\n            self.cursor_pos = 0\n            return True\n        return False\n\n    def end(self) -> bool:\n        orig = self.cursor_pos\n        self.cursor_pos = wcswidth(self.current_input)\n        return self.cursor_pos != orig\n\n    def on_key(self, key_event: KeyEvent) -> bool:\n        if key_event.type is EventType.RELEASE:\n            return False\n        if key_event.matches('home') or key_event.matches('ctrl+a'):\n            return self.home()\n        if key_event.matches('end') or key_event.matches('ctrl+e'):\n            return self.end()\n        if key_event.matches('backspace'):\n            self.backspace()\n            return True\n        if key_event.matches('delete') or key_event.matches('ctrl+d'):\n            self.delete()\n            return True\n        if key_event.matches('left') or key_event.matches('ctrl+b'):\n            self.left()\n            return True\n        if key_event.matches('right') or key_event.matches('ctrl+f'):\n            self.right()\n            return True\n        return False\n"
  },
  {
    "path": "kittens/tui/loop.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport asyncio\nimport codecs\nimport io\nimport os\nimport re\nimport selectors\nimport signal\nimport sys\nimport termios\nfrom collections.abc import Callable, Generator\nfrom contextlib import contextmanager, suppress\nfrom enum import Enum, IntFlag, auto\nfrom functools import partial\nfrom typing import Any, NamedTuple\n\nfrom kitty.constants import is_macos\nfrom kitty.fast_data_types import FILE_TRANSFER_CODE, close_tty, normal_tty, open_tty, parse_input_from_terminal, raw_tty\nfrom kitty.key_encoding import ALT, CTRL, SHIFT, backspace_key, decode_key_event, enter_key\nfrom kitty.typing_compat import ImageManagerType, KeyEventType, Protocol\nfrom kitty.utils import ScreenSize, ScreenSizeGetter, screen_size_function, write_all\n\nfrom .handler import Handler\nfrom .operations import MouseTracking, init_state, reset_state\n\n\nclass BinaryWrite(Protocol):\n\n    def write(self, data: bytes) -> None:\n        pass\n\n    def flush(self) -> None:\n        pass\n\n\ndef debug_write(*a: Any, **kw: Any) -> None:\n    from base64 import standard_b64encode\n    fobj = kw.pop('file', sys.stderr.buffer)\n    buf = io.StringIO()\n    kw['file'] = buf\n    print(*a, **kw)\n    stext = buf.getvalue()\n    for i in range(0, len(stext), 256):\n        chunk = stext[i:i + 256]\n        text = b'\\x1bP@kitty-print|' + standard_b64encode(chunk.encode('utf-8')) + b'\\x1b\\\\'\n        fobj.write(text)\n    fobj.flush()\n\n\nclass Debug:\n\n    fobj: BinaryWrite | None = None\n\n    def __call__(self, *a: Any, **kw: Any) -> None:\n        kw['file'] = self.fobj or sys.stdout.buffer\n        debug_write(*a, **kw)\n\n\ndebug = Debug()\nftc_code = str(FILE_TRANSFER_CODE)\n\n\nclass TermManager:\n\n    def __init__(\n        self, optional_actions: int = termios.TCSANOW, use_alternate_screen: bool = True,\n        mouse_tracking: MouseTracking = MouseTracking.none\n    ) -> None:\n        self.extra_finalize: str | None = None\n        self.optional_actions = optional_actions\n        self.use_alternate_screen = use_alternate_screen\n        self.mouse_tracking = mouse_tracking\n\n    def set_state_for_loop(self, set_raw: bool = True) -> None:\n        if set_raw:\n            raw_tty(self.tty_fd, self.original_termios)\n        write_all(self.tty_fd, init_state(self.use_alternate_screen, self.mouse_tracking))\n\n    def reset_state_to_original(self) -> None:\n        normal_tty(self.tty_fd, self.original_termios)\n        if self.extra_finalize:\n            write_all(self.tty_fd, self.extra_finalize)\n        write_all(self.tty_fd, reset_state(self.use_alternate_screen))\n\n    @contextmanager\n    def suspend(self) -> Generator['TermManager', None, None]:\n        self.reset_state_to_original()\n        yield self\n        self.set_state_for_loop()\n\n    def __enter__(self) -> 'TermManager':\n        self.tty_fd, self.original_termios = open_tty(False, self.optional_actions)\n        self.set_state_for_loop(set_raw=False)\n        return self\n\n    def __exit__(self, *a: object) -> None:\n        with suppress(Exception):\n            self.reset_state_to_original()\n            close_tty(self.tty_fd, self.original_termios)\n            del self.tty_fd, self.original_termios\n\n\nclass MouseButton(IntFlag):\n    NONE, LEFT, MIDDLE, RIGHT, FOURTH, FIFTH, SIXTH, SEVENTH = 0, 1, 2, 4, 8, 16, 32, 64\n    WHEEL_UP, WHEEL_DOWN, WHEEL_LEFT, WHEEL_RIGHT = -1, -2, -4, -8\n\n\nbmap = MouseButton.LEFT, MouseButton.MIDDLE, MouseButton.RIGHT\nebmap = MouseButton.FOURTH, MouseButton.FIFTH, MouseButton.SIXTH, MouseButton.SEVENTH\nwbmap = MouseButton.WHEEL_UP, MouseButton.WHEEL_DOWN, MouseButton.WHEEL_LEFT, MouseButton.WHEEL_RIGHT\nSHIFT_INDICATOR = 1 << 2\nALT_INDICATOR = 1 << 3\nCTRL_INDICATOR = 1 << 4\nMOTION_INDICATOR = 1 << 5\n\n\nclass EventType(Enum):\n    PRESS = auto()\n    RELEASE = auto()\n    MOVE = auto()\n\n\nclass MouseEvent(NamedTuple):\n    cell_x: int\n    cell_y: int\n    pixel_x: int\n    pixel_y: int\n    type: EventType\n    buttons: MouseButton\n    mods: int\n\n\ndef pixel_to_cell(px: int, length: int, cell_length: int) -> int:\n    px = max(0, min(px, length - 1))\n    return px // cell_length\n\n\ndef decode_sgr_mouse(text: str, screen_size: ScreenSize) -> MouseEvent:\n    cb_, x_, y_ = text.split(';')\n    m, y_ = y_[-1], y_[:-1]\n    cb, x, y = map(int, (cb_, x_, y_))\n    typ = EventType.RELEASE if m == 'm' else (EventType.MOVE if cb & MOTION_INDICATOR else EventType.PRESS)\n    buttons: MouseButton = MouseButton.NONE\n    cb3 = cb & 3\n    if cb >= 128:\n        buttons |= ebmap[cb3]\n    elif cb >= 64:\n        buttons |= wbmap[cb3]\n    elif cb3 < 3:\n        buttons |= bmap[cb3]\n    mods = 0\n    if cb & SHIFT_INDICATOR:\n        mods |= SHIFT\n    if cb & ALT_INDICATOR:\n        mods |= ALT\n    if cb & CTRL_INDICATOR:\n        mods |= CTRL\n    return MouseEvent(\n        pixel_to_cell(x, screen_size.width, screen_size.cell_width), pixel_to_cell(y, screen_size.height, screen_size.cell_height),\n        x, y, typ, buttons, mods\n    )\n\n\nclass UnhandledException(Handler):\n\n    def __init__(self, tb: str) -> None:\n        self.tb = tb\n\n    def initialize(self) -> None:\n        self.cmd.clear_screen()\n        self.cmd.set_scrolling_region()\n        self.cmd.set_cursor_visible(True)\n        self.cmd.set_default_colors()\n        self.write(self.tb.replace('\\n', '\\r\\n'))\n        self.write('\\r\\n')\n        self.write('Press Enter to quit')\n\n    def on_key(self, key_event: KeyEventType) -> None:\n        if key_event.key == 'ENTER':\n            self.quit_loop(1)\n\n    def on_interrupt(self) -> None:\n        self.quit_loop(1)\n    on_eot = on_term = on_interrupt\n\n\nclass SignalManager:\n\n    def __init__(\n        self,\n        loop: asyncio.AbstractEventLoop,\n        on_winch: Callable[[], None],\n        on_interrupt: Callable[[], None],\n        on_term: Callable[[], None],\n        on_hup: Callable[[], None],\n    ) -> None:\n        self.asyncio_loop = loop\n        self.on_winch, self.on_interrupt, self.on_term = on_winch, on_interrupt, on_term\n        self.on_hup = on_hup\n\n    def __enter__(self) -> None:\n        self.asyncio_loop.add_signal_handler(signal.SIGWINCH, self.on_winch)\n        self.asyncio_loop.add_signal_handler(signal.SIGINT, self.on_interrupt)\n        self.asyncio_loop.add_signal_handler(signal.SIGTERM, self.on_term)\n        self.asyncio_loop.add_signal_handler(signal.SIGHUP, self.on_hup)\n\n    def __exit__(self, *a: Any) -> None:\n        tuple(map(self.asyncio_loop.remove_signal_handler, (\n            signal.SIGWINCH, signal.SIGINT, signal.SIGTERM, signal.SIGHUP)))\n\n\nsanitize_bracketed_paste: str = '[\\x03\\x04\\x0e\\x0f\\r\\x07\\x7f\\x8d\\x8e\\x8f\\x90\\x9b\\x9d\\x9e\\x9f]'\n\n\nclass Loop:\n\n    def __init__(\n        self,\n        sanitize_bracketed_paste: str = sanitize_bracketed_paste,\n        optional_actions: int = termios.TCSADRAIN\n    ):\n        if is_macos:\n            # On macOS PTY devices are not supported by the KqueueSelector and\n            # the PollSelector is broken, causes 100% CPU usage\n            self.asyncio_loop: asyncio.AbstractEventLoop = asyncio.SelectorEventLoop(selectors.SelectSelector())\n            asyncio.set_event_loop(self.asyncio_loop)\n        else:\n            try:\n                self.asyncio_loop = asyncio.get_event_loop()\n            except RuntimeError:\n                self.asyncio_loop = asyncio.new_event_loop()\n        self.return_code = 0\n        self.overlay_ready_reported = False\n        self.optional_actions = optional_actions\n        self.read_buf = ''\n        self.decoder = codecs.getincrementaldecoder('utf-8')('ignore')\n        try:\n            self.iov_limit = max(os.sysconf('SC_IOV_MAX') - 1, 255)\n        except Exception:\n            self.iov_limit = 255\n        self.parse_input_from_terminal = partial(parse_input_from_terminal, self._on_text, self._on_dcs, self._on_csi, self._on_osc, self._on_pm, self._on_apc)\n        self.ebs_pat = re.compile('([\\177\\r\\x03\\x04])')\n        self.in_bracketed_paste = False\n        self.sanitize_bracketed_paste = bool(sanitize_bracketed_paste)\n        if self.sanitize_bracketed_paste:\n            self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste)\n\n    def _read_ready(self, handler: Handler, fd: int) -> None:\n        try:\n            bdata = os.read(fd, io.DEFAULT_BUFFER_SIZE)\n        except BlockingIOError:\n            return\n        if not bdata:\n            handler.terminal_io_ended = True\n            self.quit(1)\n            return\n        data = self.decoder.decode(bdata)\n        if self.read_buf:\n            data = self.read_buf + data\n        self.read_buf = data\n        self.handler = handler\n        try:\n            self.read_buf = self.parse_input_from_terminal(self.read_buf, self.in_bracketed_paste)\n        except Exception:\n            self.read_buf = ''\n            raise\n        finally:\n            del self.handler\n\n    # terminal input callbacks {{{\n    def _on_text(self, text: str) -> None:\n        if self.in_bracketed_paste and self.sanitize_bracketed_paste:\n            text = self.sanitize_ibp_pat.sub('', text)\n\n        for chunk in self.ebs_pat.split(text):\n            if len(chunk) == 1:\n                if chunk == '\\r':\n                    self.handler.on_key(enter_key)\n                elif chunk == '\\177':\n                    self.handler.on_key(backspace_key)\n                elif chunk == '\\x03':\n                    self.handler.on_interrupt()\n                elif chunk == '\\x04':\n                    self.handler.on_eot()\n                else:\n                    self.handler.on_text(chunk, self.in_bracketed_paste)\n            elif chunk:\n                self.handler.on_text(chunk, self.in_bracketed_paste)\n\n    def _on_dcs(self, dcs: str) -> None:\n        if dcs.startswith('@kitty-cmd'):\n            import json\n            self.handler.on_kitty_cmd_response(json.loads(dcs[len('@kitty-cmd'):]))\n        elif dcs.startswith('1+r'):\n            from binascii import unhexlify\n            vals = dcs[3:].split(';')\n            for q in vals:\n                parts = q.split('=', 1)\n                try:\n                    name, val = parts[0], unhexlify(parts[1]).decode('utf-8', 'replace')\n                except Exception:\n                    continue\n                self.handler.on_capability_response(name, val)\n\n    def _on_csi(self, csi: str) -> None:\n        q = csi[-1]\n        if q in 'mM':\n            if csi.startswith('<'):\n                # SGR mouse event\n                try:\n                    ev = decode_sgr_mouse(csi[1:], self.handler.screen_size)\n                except Exception:\n                    pass\n                else:\n                    self.handler.on_mouse_event(ev)\n        elif q in 'u~ABCDEHFPQRS':\n            if csi == '200~':\n                self.in_bracketed_paste = True\n                return\n            elif csi == '201~':\n                self.in_bracketed_paste = False\n                return\n            try:\n                k = decode_key_event(csi[:-1], q)\n            except Exception:\n                pass\n            else:\n                if not self.handler.perform_default_key_action(k):\n                    self.handler.on_key_event(k)\n\n    def _on_pm(self, pm: str) -> None:\n        pass\n\n    def _on_osc(self, osc: str) -> None:\n        idx = osc.find(';')\n        if idx <= 0:\n            return\n        q = osc[:idx]\n        if q == '52':\n            widx = osc.find(';', idx + 1)\n            if widx < idx:\n                from_primary = osc.find('p', idx + 1) > -1\n                payload = ''\n            else:\n                from base64 import standard_b64decode\n                from_primary = osc.find('p', idx+1, widx) > -1\n                data = memoryview(osc.encode('ascii'))\n                payload = standard_b64decode(data[widx+1:]).decode('utf-8')\n            self.handler.on_clipboard_response(payload, from_primary)\n        elif q == ftc_code:\n            from kitty.file_transmission import FileTransmissionCommand\n            data = memoryview(osc.encode('ascii'))\n            self.handler.on_file_transfer_response(FileTransmissionCommand.deserialize(data[idx+1:]))\n\n    def _on_apc(self, apc: str) -> None:\n        if apc.startswith('G'):\n            if self.handler.image_manager is not None:\n                self.handler.image_manager.handle_response(apc)\n    # }}}\n\n    @property\n    def total_pending_bytes_to_write(self) -> int:\n        return sum(map(len, self.write_buf))\n\n    def _write_ready(self, handler: Handler, fd: int) -> None:\n        if len(self.write_buf) > self.iov_limit:\n            self.write_buf[self.iov_limit - 1] = b''.join(self.write_buf[self.iov_limit - 1:])\n            del self.write_buf[self.iov_limit:]\n        total_size = self.total_pending_bytes_to_write\n        if total_size:\n            try:\n                written = os.writev(fd, self.write_buf)\n            except BlockingIOError:\n                return\n            if not written:\n                handler.terminal_io_ended = True\n                self.quit(1)\n                return\n        else:\n            written = 0\n        if written >= total_size:\n            self.write_buf: list[bytes] = []\n            self.asyncio_loop.remove_writer(fd)\n            self.waiting_for_writes = False\n            handler.on_writing_finished()\n        else:\n            consumed = 0\n            for i, buf in enumerate(self.write_buf):\n                if not written:\n                    break\n                if len(buf) <= written:\n                    written -= len(buf)\n                    consumed += 1\n                    continue\n                self.write_buf[i] = buf[written:]\n                break\n            del self.write_buf[:consumed]\n\n    def quit(self, return_code: int | None = None) -> None:\n        if return_code is not None:\n            self.return_code = return_code\n        self.asyncio_loop.stop()\n\n    def loop_impl(self, handler: Handler, term_manager: TermManager, image_manager: ImageManagerType | None = None) -> str | None:\n        self.write_buf = []\n        tty_fd = term_manager.tty_fd\n        tb = None\n        self.waiting_for_writes = True\n\n        def schedule_write(data: bytes) -> None:\n            self.write_buf.append(data)\n            if not self.waiting_for_writes:\n                self.asyncio_loop.add_writer(tty_fd, self._write_ready, handler, tty_fd)\n                self.waiting_for_writes = True\n\n        def handle_exception(loop: asyncio.AbstractEventLoop, context: dict[str, Any]) -> None:\n            nonlocal tb\n            loop.stop()\n            tb = context['message']\n            exc = context.get('exception')\n            if exc is not None:\n                import traceback\n                tb += '\\n' + ''.join(traceback.format_exception(exc.__class__, exc, exc.__traceback__))\n\n        self.asyncio_loop.set_exception_handler(handle_exception)\n        handler._initialize(self._get_screen_size(), term_manager, schedule_write, self, debug, image_manager)\n        with handler:\n            if handler.overlay_ready_report_needed:\n                handler.cmd.overlay_ready()\n            self.asyncio_loop.add_reader(\n                    tty_fd, self._read_ready, handler, tty_fd)\n            self.asyncio_loop.add_writer(\n                    tty_fd, self._write_ready, handler, tty_fd)\n            self.asyncio_loop.run_forever()\n            self.asyncio_loop.remove_reader(tty_fd)\n            if self.waiting_for_writes:\n                self.asyncio_loop.remove_writer(tty_fd)\n        return tb\n\n    def loop(self, handler: Handler) -> None:\n        tb: str | None = None\n\n        def _on_sigwinch() -> None:\n            self._get_screen_size.changed = True\n            handler.screen_size = self._get_screen_size()\n            handler.on_resize(handler.screen_size)\n\n        signal_manager = SignalManager(self.asyncio_loop, _on_sigwinch, handler.on_interrupt, handler.on_term, handler.on_hup)\n        with TermManager(self.optional_actions, handler.use_alternate_screen, handler.mouse_tracking) as term_manager, signal_manager:\n            self._get_screen_size: ScreenSizeGetter = screen_size_function(term_manager.tty_fd)\n            image_manager = None\n            if handler.image_manager_class is not None:\n                image_manager = handler.image_manager_class(handler)\n            try:\n                tb = self.loop_impl(handler, term_manager, image_manager)\n            except Exception:\n                import traceback\n                tb = traceback.format_exc()\n\n            term_manager.extra_finalize = b''.join(self.write_buf).decode('utf-8')\n            if tb is not None:\n                report_overlay_ready = handler.overlay_ready_report_needed and not self.overlay_ready_reported\n                self.return_code = 1\n                if not handler.terminal_io_ended:\n                    self._report_error_loop(tb, term_manager, report_overlay_ready)\n\n    def _report_error_loop(self, tb: str, term_manager: TermManager, overlay_ready_report_needed: bool) -> None:\n        handler = UnhandledException(tb)\n        handler.overlay_ready_report_needed = overlay_ready_report_needed\n        self.loop_impl(handler, term_manager)\n"
  },
  {
    "path": "kittens/tui/operations.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport sys\nfrom collections.abc import Callable, Generator\nfrom contextlib import contextmanager\nfrom enum import Enum, auto\nfrom functools import wraps\nfrom typing import Any, Optional, TypeVar, Union\n\nfrom kitty.fast_data_types import Color\nfrom kitty.rgb import color_as_sharp, to_color\nfrom kitty.typing_compat import GraphicsCommandType, HandlerType, ScreenSize, UnderlineLiteral\n\nfrom .operations_stub import CMD\n\nGraphicsCommandType, ScreenSize  # needed for stub generation\nSAVE_CURSOR = '\\0337'\nRESTORE_CURSOR = '\\0338'\nSAVE_PRIVATE_MODE_VALUES = '\\033[?s'\nRESTORE_PRIVATE_MODE_VALUES = '\\033[?r'\nSAVE_COLORS = '\\033[#P'\nRESTORE_COLORS = '\\033[#Q'\nF = TypeVar('F')\nall_cmds: dict[str, Callable[..., Any]] = {}\n\n\nclass Mode(Enum):\n    LNM = 20, ''\n    IRM = 4, ''\n    DECKM = 1, '?'\n    DECSCNM = 5, '?'\n    DECOM = 6, '?'\n    DECAWM = 7, '?'\n    DECARM = 8, '?'\n    DECTCEM = 25, '?'\n    MOUSE_BUTTON_TRACKING = 1000, '?'\n    MOUSE_MOTION_TRACKING = 1002, '?'\n    MOUSE_MOVE_TRACKING = 1003, '?'\n    FOCUS_TRACKING = 1004, '?'\n    MOUSE_UTF8_MODE = 1005, '?'\n    MOUSE_SGR_MODE = 1006, '?'\n    MOUSE_URXVT_MODE = 1015, '?'\n    MOUSE_SGR_PIXEL_MODE = 1016, '?'\n    ALTERNATE_SCREEN = 1049, '?'\n    BRACKETED_PASTE = 2004, '?'\n    PENDING_UPDATE = 2026, '?'\n    HANDLE_TERMIOS_SIGNALS = 19997, '?'\n\n\ndef cmd(f: F) -> F:\n    all_cmds[f.__name__] = f  # type: ignore\n    return f\n\n\n@cmd\ndef set_mode(which: Mode) -> str:\n    num, private = which.value\n    return f'\\033[{private}{num}h'\n\n\n@cmd\ndef reset_mode(which: Mode) -> str:\n    num, private = which.value\n    return f'\\033[{private}{num}l'\n\n\n@cmd\ndef clear_screen() -> str:\n    return '\\033[H\\033[2J'\n\n\n@cmd\ndef clear_to_end_of_screen() -> str:\n    return '\\033[J'\n\n\n@cmd\ndef clear_to_eol() -> str:\n    return '\\033[K'\n\n\n@cmd\ndef reset_terminal() -> str:\n    return '\\033]\\033\\\\\\033c'\n\n\n@cmd\ndef bell() -> str:\n    return '\\a'\n\n\n@cmd\ndef beep() -> str:\n    return '\\a'\n\n\n@cmd\ndef set_window_title(value: str) -> str:\n    return '\\033]2;' + value.replace('\\033', '').replace('\\x9c', '') + '\\033\\\\'\n\n\n@cmd\ndef set_line_wrapping(yes_or_no: bool) -> str:\n    return set_mode(Mode.DECAWM) if yes_or_no else reset_mode(Mode.DECAWM)\n\n\n@contextmanager\ndef without_line_wrap(write: Callable[[str], None]) -> Generator[None, None, None]:\n    write(set_line_wrapping(False))\n    try:\n        yield\n    finally:\n        write(set_line_wrapping(True))\n\n\n@cmd\ndef repeat(char: str, count: int) -> str:\n    if count > 5:\n        return f'{char}\\x1b[{count-1}b'\n    return char * count\n\n\n@cmd\ndef set_cursor_visible(yes_or_no: bool) -> str:\n    return set_mode(Mode.DECTCEM) if yes_or_no else reset_mode(Mode.DECTCEM)\n\n\n@cmd\ndef set_cursor_position(x: int = 0, y: int = 0) -> str:  # (0, 0) is top left\n    return f'\\033[{y + 1};{x + 1}H'\n\n\n@cmd\ndef move_cursor_by(amt: int, direction: str) -> str:\n    suffix = {'up': 'A', 'down': 'B', 'right': 'C', 'left': 'D'}[direction]\n    return f'\\033[{amt}{suffix}'\n\n\n@cmd\ndef set_cursor_shape(shape: str = 'block', blink: bool = True) -> str:\n    val = {'block': 1, 'underline': 3, 'beam': 5}.get(shape, 1)\n    if not blink:\n        val += 1\n    return f'\\033[{val} q'\n\n\n@cmd\ndef set_scrolling_region(screen_size: Optional['ScreenSize'] = None, top: int | None = None, bottom: int | None = None) -> str:\n    if screen_size is None:\n        return '\\033[r'\n    if top is None:\n        top = 0\n    if bottom is None:\n        bottom = screen_size.rows - 1\n    if bottom < 0:\n        bottom = screen_size.rows - 1 + bottom\n    else:\n        bottom += 1\n    return f'\\033[{top + 1};{bottom + 1}r'\n\n\n@cmd\ndef scroll_screen(amt: int = 1) -> str:\n    return f'\\033[{abs(amt)}{\"T\" if amt < 0 else \"S\"}'\n\n\nSTANDARD_COLORS = {'black': 0, 'red': 1, 'green': 2, 'yellow': 3, 'blue': 4, 'magenta': 5, 'cyan': 6, 'gray': 7, 'white': 7}\nUNDERLINE_STYLES = {'straight': 1, 'double': 2, 'curly': 3, 'dotted': 4, 'dashed': 5}\n\n\nColorSpec = Union[int, str, Color]\n\n\ndef color_code(color: ColorSpec, intense: bool = False, base: int = 30) -> str:\n    if isinstance(color, str):\n        e = str((base + 60 if intense else base) + STANDARD_COLORS[color])\n    elif isinstance(color, int):\n        e = f'{base + 8}:5:{max(0, min(color, 255))}'\n    else:\n        e = f'{base + 8}{color.as_sgr}'\n    return e\n\n\n@cmd\ndef sgr(*parts: str) -> str:\n    return '\\033[{}m'.format(';'.join(parts))\n\n\n@cmd\ndef colored(\n    text: str,\n    color: ColorSpec,\n    intense: bool = False,\n    reset_to: ColorSpec | None = None,\n    reset_to_intense: bool = False\n) -> str:\n    e = color_code(color, intense)\n    return f'\\033[{e}m{text}\\033[{39 if reset_to is None else color_code(reset_to, reset_to_intense)}m'\n\n\n@cmd\ndef faint(text: str) -> str:\n    return colored(text, 'black', True)\n\n\n@cmd\ndef styled(\n    text: str,\n    fg: ColorSpec | None = None,\n    bg: ColorSpec | None = None,\n    fg_intense: bool = False,\n    bg_intense: bool = False,\n    italic: bool | None = None,\n    bold: bool | None = None,\n    underline: UnderlineLiteral | None = None,\n    underline_color: ColorSpec | None = None,\n    reverse: bool | None = None,\n    dim: bool | None = None,\n) -> str:\n    start, end = [], []\n    if fg is not None:\n        start.append(color_code(fg, fg_intense))\n        end.append('39')\n    if bg is not None:\n        start.append(color_code(bg, bg_intense, 40))\n        end.append('49')\n    if underline_color is not None:\n        if isinstance(underline_color, str):\n            underline_color = STANDARD_COLORS[underline_color]\n        start.append(color_code(underline_color, base=50))\n        end.append('59')\n    if underline is not None:\n        start.append(f'4:{UNDERLINE_STYLES[underline]}')\n        end.append('4:0')\n    if italic is not None:\n        s, e = (start, end) if italic else (end, start)\n        s.append('3')\n        e.append('23')\n    if bold is not None:\n        s, e = (start, end) if bold else (end, start)\n        s.append('1')\n        e.append('22')\n    if dim is not None:\n        s, e = (start, end) if dim else (end, start)\n        s.append('2')\n        e.append('22')\n    if reverse is not None:\n        s, e = (start, end) if reverse else (end, start)\n        s.append('7')\n        e.append('27')\n    if not start:\n        return text\n    return '\\033[{}m{}\\033[{}m'.format(';'.join(start), text, ';'.join(end))\n\n\ndef serialize_gr_command(cmd: dict[str, int | str], payload: bytes | None = None) -> bytes:\n    from .images import GraphicsCommand\n    gc = GraphicsCommand()\n    for k, v in cmd.items():\n        setattr(gc, k, v)\n    return gc.serialize(payload or b'')\n\n\n@cmd\ndef gr_command(cmd: Union[dict[str, int | str], 'GraphicsCommandType'], payload: bytes | None = None) -> str:\n    if isinstance(cmd, dict):\n        raw = serialize_gr_command(cmd, payload)\n    else:\n        raw = cmd.serialize(payload or b'')\n    return raw.decode('ascii')\n\n\n@cmd\ndef clear_images_on_screen(delete_data: bool = False) -> str:\n    from .images import GraphicsCommand\n    gc = GraphicsCommand()\n    gc.a = 'd'\n    gc.d = 'A' if delete_data else 'a'\n    return gc.serialize().decode('ascii')\n\n\nclass MouseTracking(Enum):\n    none = auto()\n    buttons_only = auto()\n    buttons_and_drag = auto()\n    full = auto()\n\n\ndef init_state(alternate_screen: bool = True, mouse_tracking: MouseTracking = MouseTracking.none, kitty_keyboard_mode: bool = True) -> str:\n    sc = SAVE_CURSOR if alternate_screen else ''\n    ans = (\n        sc + SAVE_PRIVATE_MODE_VALUES + reset_mode(Mode.LNM) +\n        reset_mode(Mode.IRM) + reset_mode(Mode.DECKM) + reset_mode(Mode.DECSCNM) +\n        set_mode(Mode.DECARM) + set_mode(Mode.DECAWM) +\n        set_mode(Mode.DECTCEM) + reset_mode(Mode.MOUSE_BUTTON_TRACKING) +\n        reset_mode(Mode.MOUSE_MOTION_TRACKING) + reset_mode(Mode.MOUSE_MOVE_TRACKING) +\n        reset_mode(Mode.FOCUS_TRACKING) + reset_mode(Mode.MOUSE_UTF8_MODE) +\n        reset_mode(Mode.MOUSE_SGR_MODE) + set_mode(Mode.BRACKETED_PASTE) + SAVE_COLORS +\n        '\\033[*x'  # reset DECSACE to default region select\n    )\n    if alternate_screen:\n        ans += set_mode(Mode.ALTERNATE_SCREEN) + reset_mode(Mode.DECOM)\n        ans += clear_screen()\n    if mouse_tracking is not MouseTracking.none:\n        ans += set_mode(Mode.MOUSE_SGR_PIXEL_MODE)\n        if mouse_tracking is MouseTracking.buttons_only:\n            ans += set_mode(Mode.MOUSE_BUTTON_TRACKING)\n        elif mouse_tracking is MouseTracking.buttons_and_drag:\n            ans += set_mode(Mode.MOUSE_MOTION_TRACKING)\n        elif mouse_tracking is MouseTracking.full:\n            ans += set_mode(Mode.MOUSE_MOVE_TRACKING)\n    if kitty_keyboard_mode:\n        ans += '\\033[>31u'  # extended keyboard mode\n    else:\n        ans += '\\033[>u'  # legacy keyboard mode\n    return ans\n\n\ndef reset_state(normal_screen: bool = True) -> str:\n    ans = '\\033[<u'  # restore keyboard mode\n    if normal_screen:\n        ans += reset_mode(Mode.ALTERNATE_SCREEN)\n    else:\n        ans += SAVE_CURSOR\n    ans += RESTORE_PRIVATE_MODE_VALUES\n    ans += RESTORE_CURSOR\n    ans += RESTORE_COLORS\n    return ans\n\n\n@contextmanager\ndef pending_update(write: Callable[[str], None]) -> Generator[None, None, None]:\n    write(set_mode(Mode.PENDING_UPDATE))\n    try:\n        yield\n    finally:\n        write(reset_mode(Mode.PENDING_UPDATE))\n\n\n@contextmanager\ndef cursor(write: Callable[[str], None]) -> Generator[None, None, None]:\n    write(SAVE_CURSOR)\n    try:\n        yield\n    finally:\n        write(RESTORE_CURSOR)\n\n\n@contextmanager\ndef alternate_screen() -> Generator[None, None, None]:\n    with open(os.ctermid(), 'w') as f:\n        print(set_mode(Mode.ALTERNATE_SCREEN), end='', file=f, flush=True)\n        try:\n            yield\n        finally:\n            print(reset_mode(Mode.ALTERNATE_SCREEN), end='', file=f, flush=True)\n\n\n@contextmanager\ndef raw_mode(fd: int | None = None) -> Generator[None, None, None]:\n    import termios\n    import tty\n    if fd is None:\n        fd = sys.stdin.fileno()\n    old = termios.tcgetattr(fd)\n    try:\n        tty.setraw(fd)\n        yield\n    finally:\n        termios.tcsetattr(fd, termios.TCSADRAIN, old)\n\n\n@cmd\ndef set_default_colors(\n    fg: Color | str | None = None,\n    bg: Color | str | None = None,\n    cursor: Color | str | None = None,\n    select_bg: Color | str | None = None,\n    select_fg: Color | str | None = None\n) -> str:\n    ans = ''\n\n    def item(which: Color | str | None, num: int) -> None:\n        nonlocal ans\n        if which is None:\n            ans += f'\\x1b]1{num}\\x1b\\\\'\n        else:\n            if isinstance(which, Color):\n                q = color_as_sharp(which)\n            else:\n                x = to_color(which)\n                assert x is not None\n                q = color_as_sharp(x)\n            ans += f'\\x1b]{num};{q}\\x1b\\\\'\n\n    item(fg, 10)\n    item(bg, 11)\n    item(cursor, 12)\n    item(select_bg, 17)\n    item(select_fg, 19)\n    return ans\n\n\n@cmd\ndef save_colors() -> str:\n    return '\\x1b[#P'\n\n\n@cmd\ndef restore_colors() -> str:\n    return '\\x1b[#Q'\n\n\n@cmd\ndef overlay_ready() -> str:\n    return '\\x1bP@kitty-overlay-ready|\\x1b\\\\'\n\n\n@cmd\ndef write_to_clipboard(data: str | bytes, use_primary: bool = False) -> str:\n    from base64 import standard_b64encode\n    fmt = 'p' if use_primary else 'c'\n    if isinstance(data, str):\n        data = data.encode('utf-8')\n    payload = standard_b64encode(data).decode('ascii')\n    return f'\\x1b]52;{fmt};{payload}\\a'\n\n\n@cmd\ndef request_from_clipboard(use_primary: bool = False) -> str:\n    return '\\x1b]52;{};?\\a'.format('p' if use_primary else 'c')\n\n\n# Boilerplate to make operations available via Handler.cmd  {{{\n\n\ndef writer(handler: HandlerType, func: Callable[..., bytes | str]) -> Callable[..., None]:\n    @wraps(func)\n    def f(*a: Any, **kw: Any) -> None:\n        handler.write(func(*a, **kw))\n    return f\n\n\ndef commander(handler: HandlerType) -> CMD:\n    ans = CMD()\n    for name, func in all_cmds.items():\n        setattr(ans, name, writer(handler, func))\n    return ans\n\n\ndef func_sig(func: Callable[..., Any]) -> Generator[str, None, None]:\n    import inspect\n    import re\n    s = inspect.signature(func)\n    for val in s.parameters.values():\n        yield re.sub(r'ForwardRef\\([\\'\"](\\w+?)[\\'\"]\\)', r'\\1', str(val).replace('NoneType', 'None'))\n\n\ndef as_type_stub() -> str:\n    ans = [\n        'from typing import *  # noqa',\n        'from kitty.typing_compat import GraphicsCommandType, ScreenSize',\n        'from kitty.fast_data_types import Color',\n        'import kitty.rgb',\n        'import kittens.tui.operations',\n    ]\n    methods = []\n    for name, func in all_cmds.items():\n        args = ', '.join(func_sig(func))\n        if args:\n            args = f', {args}'\n        methods.append(f'    def {name}(self{args}) -> str: pass')\n    ans += ['', '', 'class CMD:'] + methods\n\n    return '\\n'.join(ans) + '\\n\\n\\n'\n# }}}\n"
  },
  {
    "path": "kittens/tui/operations_stub.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nclass CMD:\n    pass\n\n\ndef generate_stub() -> None:\n    from kittens.tui.operations import as_type_stub\n    from kitty.conf.utils import save_type_stub\n    text = as_type_stub()\n    save_type_stub(text, __file__)\n"
  },
  {
    "path": "kittens/tui/path_completer.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nfrom collections.abc import Callable, Generator, Sequence\nfrom typing import Any\n\nfrom kitty.fast_data_types import wcswidth\nfrom kitty.utils import ScreenSize, screen_size_function\n\nfrom .operations import styled\n\n\ndef directory_completions(path: str, qpath: str, prefix: str = '') -> Generator[str, None, None]:\n    try:\n        entries = os.scandir(qpath)\n    except OSError:\n        return\n    for x in entries:\n        try:\n            is_dir = x.is_dir()\n        except OSError:\n            is_dir = False\n        name = x.name + (os.sep if is_dir else '')\n        if not prefix or name.startswith(prefix):\n            if path:\n                yield os.path.join(path, name)\n            else:\n                yield name\n\n\ndef expand_path(path: str) -> str:\n    return os.path.abspath(os.path.expandvars(os.path.expanduser(path)))\n\n\ndef find_completions(path: str) -> Generator[str, None, None]:\n    if path and path[0] == '~':\n        if path == '~':\n            yield '~' + os.sep\n            return\n        if os.sep not in path:\n            qpath = os.path.expanduser(path)\n            if qpath != path:\n                yield path + os.sep\n                return\n    qpath = expand_path(path)\n    if not path or path.endswith(os.sep):\n        yield from directory_completions(path, qpath)\n    else:\n        yield from directory_completions(os.path.dirname(path), os.path.dirname(qpath), os.path.basename(qpath))\n\n\ndef print_table(items: Sequence[str], screen_size: ScreenSize, dir_colors: Callable[[str, str], str]) -> None:\n    max_width = 0\n    item_widths = {}\n    for item in items:\n        item_widths[item] = w = wcswidth(item)\n        max_width = max(w, max_width)\n    col_width = max_width + 2\n    num_of_cols = max(1, screen_size.cols // col_width)\n    cr = 0\n    at_start = False\n    for item in items:\n        w = item_widths[item]\n        left = col_width - w\n        print(dir_colors(expand_path(item), item), ' ' * left, sep='', end='')\n        at_start = False\n        cr = (cr + 1) % num_of_cols\n        if not cr:\n            print()\n            at_start = True\n    if not at_start:\n        print()\n\n\nclass PathCompleter:\n\n    def __init__(self, prompt: str = '> '):\n        self.prompt = prompt\n        self.prompt_len = wcswidth(self.prompt)\n\n    def __enter__(self) -> 'PathCompleter':\n        import readline\n\n        from .dircolors import Dircolors\n        if 'libedit' in readline.__doc__:\n            readline.parse_and_bind(\"bind -e\")\n            readline.parse_and_bind(\"bind '\\t' rl_complete\")\n        else:\n            readline.parse_and_bind('tab: complete')\n            readline.parse_and_bind('set colored-stats on')\n            readline.set_completer_delims(' \\t\\n`!@#$%^&*()-=+[{]}\\\\|;:\\'\",<>?')\n        readline.set_completion_display_matches_hook(self.format_completions)\n        self.original_completer = readline.get_completer()\n        readline.set_completer(self)\n        self.cache: dict[str, tuple[str, ...]] = {}\n        self.dircolors = Dircolors()\n        return self\n\n    def format_completions(self, substitution: str, matches: Sequence[str], longest_match_length: int) -> None:\n        import readline\n        print()\n        files, dirs = [], []\n        for m in matches:\n            if m.endswith('/'):\n                if len(m) > 1:\n                    m = m[:-1]\n                dirs.append(m)\n            else:\n                files.append(m)\n\n        ss = screen_size_function()()\n        if dirs:\n            print(styled('Directories', bold=True, fg_intense=True))\n            print_table(dirs, ss, self.dircolors)\n        if files:\n            print(styled('Files', bold=True, fg_intense=True))\n            print_table(files, ss, self.dircolors)\n\n        buf = readline.get_line_buffer()\n        x = readline.get_endidx()\n        buflen = wcswidth(buf)\n        print(self.prompt, buf, sep='', end='')\n        if x < buflen:\n            pos = x + self.prompt_len\n            print(f\"\\r\\033[{pos}C\", end='')\n        print(sep='', end='', flush=True)\n\n    def __call__(self, text: str, state: int) -> str | None:\n        options = self.cache.get(text)\n        if options is None:\n            options = self.cache[text] = tuple(find_completions(text))\n        if options and state < len(options):\n            return options[state]\n        return None\n\n    def __exit__(self, *a: Any) -> bool:\n        import readline\n        del self.cache\n        readline.set_completer(self.original_completer)\n        readline.set_completion_display_matches_hook()\n        return True\n\n    def input(self) -> str:\n        with self:\n            return input(self.prompt)\n        return ''\n\n\ndef get_path(prompt: str = '> ') -> str:\n    return PathCompleter(prompt).input()\n\n\ndef develop() -> None:\n    PathCompleter().input()\n"
  },
  {
    "path": "kittens/tui/progress.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom .operations import repeat, styled\n\n\ndef render_progress_bar(frac: float, width: int = 80) -> str:\n    if frac >= 1:\n        return styled('🬋' * width, fg='green')\n    if frac <= 0:\n        return styled('🬋' * width, dim=True)\n    w = frac * width\n    fl = int(w)\n    overhang = w - fl\n    filled = repeat('🬋', fl)\n    if overhang < 0.2:\n        needs_break = True\n    elif overhang < 0.8:\n        filled += '🬃'\n        fl += 1\n        needs_break = False\n    else:\n        if fl < width - 1:\n            filled += '🬋'\n            fl += 1\n            needs_break = True\n        else:\n            filled += '🬃'\n            fl += 1\n            needs_break = False\n    ans = styled(filled, fg='blue')\n    unfilled = '🬇' if width > fl and needs_break else ''\n    filler = width - fl - len(unfilled)\n    if filler > 0:\n        unfilled += repeat('🬋', filler)\n    if unfilled:\n        ans += styled(unfilled, dim=True)\n    return ans\n"
  },
  {
    "path": "kittens/tui/spinners.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Sequence\n\nfrom kitty.fast_data_types import monotonic\nfrom kitty.typing_compat import TypedDict\n\n\nclass SpinnerDef(TypedDict):\n    interval: int\n    frames: Sequence[str]\n\n\n# Spinner definitions are from\n# https://raw.githubusercontent.com/sindresorhus/cli-spinners/main/spinners.json\nspinners: dict[str, SpinnerDef] = {  # {{{\n    \"dots\": {\n        \"interval\": 80,\n        \"frames\": [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n    },\n    \"dots2\": {\n        \"interval\": 80,\n        \"frames\": [\"⣾\", \"⣽\", \"⣻\", \"⢿\", \"⡿\", \"⣟\", \"⣯\", \"⣷\"]\n    },\n    \"dots3\": {\n        \"interval\": 80,\n        \"frames\": [\"⠋\", \"⠙\", \"⠚\", \"⠞\", \"⠖\", \"⠦\", \"⠴\", \"⠲\", \"⠳\", \"⠓\"]\n    },\n    \"dots4\": {\n        \"interval\":\n        80,\n        \"frames\":\n        [\"⠄\", \"⠆\", \"⠇\", \"⠋\", \"⠙\", \"⠸\", \"⠰\", \"⠠\", \"⠰\", \"⠸\", \"⠙\", \"⠋\", \"⠇\", \"⠆\"]\n    },\n    \"dots5\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"⠋\", \"⠙\", \"⠚\", \"⠒\", \"⠂\", \"⠂\", \"⠒\", \"⠲\", \"⠴\", \"⠦\", \"⠖\", \"⠒\", \"⠐\",\n            \"⠐\", \"⠒\", \"⠓\", \"⠋\"\n        ]\n    },\n    \"dots6\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"⠁\", \"⠉\", \"⠙\", \"⠚\", \"⠒\", \"⠂\", \"⠂\", \"⠒\", \"⠲\", \"⠴\", \"⠤\", \"⠄\", \"⠄\",\n            \"⠤\", \"⠴\", \"⠲\", \"⠒\", \"⠂\", \"⠂\", \"⠒\", \"⠚\", \"⠙\", \"⠉\", \"⠁\"\n        ]\n    },\n    \"dots7\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"⠈\", \"⠉\", \"⠋\", \"⠓\", \"⠒\", \"⠐\", \"⠐\", \"⠒\", \"⠖\", \"⠦\", \"⠤\", \"⠠\", \"⠠\",\n            \"⠤\", \"⠦\", \"⠖\", \"⠒\", \"⠐\", \"⠐\", \"⠒\", \"⠓\", \"⠋\", \"⠉\", \"⠈\"\n        ]\n    },\n    \"dots8\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"⠁\", \"⠁\", \"⠉\", \"⠙\", \"⠚\", \"⠒\", \"⠂\", \"⠂\", \"⠒\", \"⠲\", \"⠴\", \"⠤\", \"⠄\",\n            \"⠄\", \"⠤\", \"⠠\", \"⠠\", \"⠤\", \"⠦\", \"⠖\", \"⠒\", \"⠐\", \"⠐\", \"⠒\", \"⠓\", \"⠋\",\n            \"⠉\", \"⠈\", \"⠈\"\n        ]\n    },\n    \"dots9\": {\n        \"interval\": 80,\n        \"frames\": [\"⢹\", \"⢺\", \"⢼\", \"⣸\", \"⣇\", \"⡧\", \"⡗\", \"⡏\"]\n    },\n    \"dots10\": {\n        \"interval\": 80,\n        \"frames\": [\"⢄\", \"⢂\", \"⢁\", \"⡁\", \"⡈\", \"⡐\", \"⡠\"]\n    },\n    \"dots11\": {\n        \"interval\": 100,\n        \"frames\": [\"⠁\", \"⠂\", \"⠄\", \"⡀\", \"⢀\", \"⠠\", \"⠐\", \"⠈\"]\n    },\n    \"dots12\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"⢀⠀\", \"⡀⠀\", \"⠄⠀\", \"⢂⠀\", \"⡂⠀\", \"⠅⠀\", \"⢃⠀\", \"⡃⠀\", \"⠍⠀\", \"⢋⠀\", \"⡋⠀\",\n            \"⠍⠁\", \"⢋⠁\", \"⡋⠁\", \"⠍⠉\", \"⠋⠉\", \"⠋⠉\", \"⠉⠙\", \"⠉⠙\", \"⠉⠩\", \"⠈⢙\", \"⠈⡙\",\n            \"⢈⠩\", \"⡀⢙\", \"⠄⡙\", \"⢂⠩\", \"⡂⢘\", \"⠅⡘\", \"⢃⠨\", \"⡃⢐\", \"⠍⡐\", \"⢋⠠\", \"⡋⢀\",\n            \"⠍⡁\", \"⢋⠁\", \"⡋⠁\", \"⠍⠉\", \"⠋⠉\", \"⠋⠉\", \"⠉⠙\", \"⠉⠙\", \"⠉⠩\", \"⠈⢙\", \"⠈⡙\",\n            \"⠈⠩\", \"⠀⢙\", \"⠀⡙\", \"⠀⠩\", \"⠀⢘\", \"⠀⡘\", \"⠀⠨\", \"⠀⢐\", \"⠀⡐\", \"⠀⠠\", \"⠀⢀\",\n            \"⠀⡀\"\n        ]\n    },\n    \"dots8Bit\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"⠀\", \"⠁\", \"⠂\", \"⠃\", \"⠄\", \"⠅\", \"⠆\", \"⠇\", \"⡀\", \"⡁\", \"⡂\", \"⡃\", \"⡄\",\n            \"⡅\", \"⡆\", \"⡇\", \"⠈\", \"⠉\", \"⠊\", \"⠋\", \"⠌\", \"⠍\", \"⠎\", \"⠏\", \"⡈\", \"⡉\",\n            \"⡊\", \"⡋\", \"⡌\", \"⡍\", \"⡎\", \"⡏\", \"⠐\", \"⠑\", \"⠒\", \"⠓\", \"⠔\", \"⠕\", \"⠖\",\n            \"⠗\", \"⡐\", \"⡑\", \"⡒\", \"⡓\", \"⡔\", \"⡕\", \"⡖\", \"⡗\", \"⠘\", \"⠙\", \"⠚\", \"⠛\",\n            \"⠜\", \"⠝\", \"⠞\", \"⠟\", \"⡘\", \"⡙\", \"⡚\", \"⡛\", \"⡜\", \"⡝\", \"⡞\", \"⡟\", \"⠠\",\n            \"⠡\", \"⠢\", \"⠣\", \"⠤\", \"⠥\", \"⠦\", \"⠧\", \"⡠\", \"⡡\", \"⡢\", \"⡣\", \"⡤\", \"⡥\",\n            \"⡦\", \"⡧\", \"⠨\", \"⠩\", \"⠪\", \"⠫\", \"⠬\", \"⠭\", \"⠮\", \"⠯\", \"⡨\", \"⡩\", \"⡪\",\n            \"⡫\", \"⡬\", \"⡭\", \"⡮\", \"⡯\", \"⠰\", \"⠱\", \"⠲\", \"⠳\", \"⠴\", \"⠵\", \"⠶\", \"⠷\",\n            \"⡰\", \"⡱\", \"⡲\", \"⡳\", \"⡴\", \"⡵\", \"⡶\", \"⡷\", \"⠸\", \"⠹\", \"⠺\", \"⠻\", \"⠼\",\n            \"⠽\", \"⠾\", \"⠿\", \"⡸\", \"⡹\", \"⡺\", \"⡻\", \"⡼\", \"⡽\", \"⡾\", \"⡿\", \"⢀\", \"⢁\",\n            \"⢂\", \"⢃\", \"⢄\", \"⢅\", \"⢆\", \"⢇\", \"⣀\", \"⣁\", \"⣂\", \"⣃\", \"⣄\", \"⣅\", \"⣆\",\n            \"⣇\", \"⢈\", \"⢉\", \"⢊\", \"⢋\", \"⢌\", \"⢍\", \"⢎\", \"⢏\", \"⣈\", \"⣉\", \"⣊\", \"⣋\",\n            \"⣌\", \"⣍\", \"⣎\", \"⣏\", \"⢐\", \"⢑\", \"⢒\", \"⢓\", \"⢔\", \"⢕\", \"⢖\", \"⢗\", \"⣐\",\n            \"⣑\", \"⣒\", \"⣓\", \"⣔\", \"⣕\", \"⣖\", \"⣗\", \"⢘\", \"⢙\", \"⢚\", \"⢛\", \"⢜\", \"⢝\",\n            \"⢞\", \"⢟\", \"⣘\", \"⣙\", \"⣚\", \"⣛\", \"⣜\", \"⣝\", \"⣞\", \"⣟\", \"⢠\", \"⢡\", \"⢢\",\n            \"⢣\", \"⢤\", \"⢥\", \"⢦\", \"⢧\", \"⣠\", \"⣡\", \"⣢\", \"⣣\", \"⣤\", \"⣥\", \"⣦\", \"⣧\",\n            \"⢨\", \"⢩\", \"⢪\", \"⢫\", \"⢬\", \"⢭\", \"⢮\", \"⢯\", \"⣨\", \"⣩\", \"⣪\", \"⣫\", \"⣬\",\n            \"⣭\", \"⣮\", \"⣯\", \"⢰\", \"⢱\", \"⢲\", \"⢳\", \"⢴\", \"⢵\", \"⢶\", \"⢷\", \"⣰\", \"⣱\",\n            \"⣲\", \"⣳\", \"⣴\", \"⣵\", \"⣶\", \"⣷\", \"⢸\", \"⢹\", \"⢺\", \"⢻\", \"⢼\", \"⢽\", \"⢾\",\n            \"⢿\", \"⣸\", \"⣹\", \"⣺\", \"⣻\", \"⣼\", \"⣽\", \"⣾\", \"⣿\"\n        ]\n    },\n    \"line\": {\n        \"interval\": 130,\n        \"frames\": [\"-\", \"\\\\\", \"|\", \"/\"]\n    },\n    \"line2\": {\n        \"interval\": 100,\n        \"frames\": [\"⠂\", \"-\", \"–\", \"—\", \"–\", \"-\"]\n    },\n    \"pipe\": {\n        \"interval\": 100,\n        \"frames\": [\"┤\", \"┘\", \"┴\", \"└\", \"├\", \"┌\", \"┬\", \"┐\"]\n    },\n    \"simpleDots\": {\n        \"interval\": 400,\n        \"frames\": [\".  \", \".. \", \"...\", \"   \"]\n    },\n    \"simpleDotsScrolling\": {\n        \"interval\": 200,\n        \"frames\": [\".  \", \".. \", \"...\", \" ..\", \"  .\", \"   \"]\n    },\n    \"star\": {\n        \"interval\": 70,\n        \"frames\": [\"✶\", \"✸\", \"✹\", \"✺\", \"✹\", \"✷\"]\n    },\n    \"star2\": {\n        \"interval\": 80,\n        \"frames\": [\"+\", \"x\", \"*\"]\n    },\n    \"flip\": {\n        \"interval\": 70,\n        \"frames\": [\"_\", \"_\", \"_\", \"-\", \"`\", \"`\", \"'\", \"´\", \"-\", \"_\", \"_\", \"_\"]\n    },\n    \"hamburger\": {\n        \"interval\": 100,\n        \"frames\": [\"☱\", \"☲\", \"☴\"]\n    },\n    \"growVertical\": {\n        \"interval\": 120,\n        \"frames\": [\"▁\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"▆\", \"▅\", \"▄\", \"▃\"]\n    },\n    \"growHorizontal\": {\n        \"interval\": 120,\n        \"frames\": [\"▏\", \"▎\", \"▍\", \"▌\", \"▋\", \"▊\", \"▉\", \"▊\", \"▋\", \"▌\", \"▍\", \"▎\"]\n    },\n    \"balloon\": {\n        \"interval\": 140,\n        \"frames\": [\" \", \".\", \"o\", \"O\", \"@\", \"*\", \" \"]\n    },\n    \"balloon2\": {\n        \"interval\": 120,\n        \"frames\": [\".\", \"o\", \"O\", \"°\", \"O\", \"o\", \".\"]\n    },\n    \"noise\": {\n        \"interval\": 100,\n        \"frames\": [\"▓\", \"▒\", \"░\"]\n    },\n    \"bounce\": {\n        \"interval\": 120,\n        \"frames\": [\"⠁\", \"⠂\", \"⠄\", \"⠂\"]\n    },\n    \"boxBounce\": {\n        \"interval\": 120,\n        \"frames\": [\"▖\", \"▘\", \"▝\", \"▗\"]\n    },\n    \"boxBounce2\": {\n        \"interval\": 100,\n        \"frames\": [\"▌\", \"▀\", \"▐\", \"▄\"]\n    },\n    \"triangle\": {\n        \"interval\": 50,\n        \"frames\": [\"◢\", \"◣\", \"◤\", \"◥\"]\n    },\n    \"arc\": {\n        \"interval\": 100,\n        \"frames\": [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"]\n    },\n    \"circle\": {\n        \"interval\": 120,\n        \"frames\": [\"◡\", \"⊙\", \"◠\"]\n    },\n    \"squareCorners\": {\n        \"interval\": 180,\n        \"frames\": [\"◰\", \"◳\", \"◲\", \"◱\"]\n    },\n    \"circleQuarters\": {\n        \"interval\": 120,\n        \"frames\": [\"◴\", \"◷\", \"◶\", \"◵\"]\n    },\n    \"circleHalves\": {\n        \"interval\": 50,\n        \"frames\": [\"◐\", \"◓\", \"◑\", \"◒\"]\n    },\n    \"squish\": {\n        \"interval\": 100,\n        \"frames\": [\"╫\", \"╪\"]\n    },\n    \"toggle\": {\n        \"interval\": 250,\n        \"frames\": [\"⊶\", \"⊷\"]\n    },\n    \"toggle2\": {\n        \"interval\": 80,\n        \"frames\": [\"▫\", \"▪\"]\n    },\n    \"toggle3\": {\n        \"interval\": 120,\n        \"frames\": [\"□\", \"■\"]\n    },\n    \"toggle4\": {\n        \"interval\": 100,\n        \"frames\": [\"■\", \"□\", \"▪\", \"▫\"]\n    },\n    \"toggle5\": {\n        \"interval\": 100,\n        \"frames\": [\"▮\", \"▯\"]\n    },\n    \"toggle6\": {\n        \"interval\": 300,\n        \"frames\": [\"ဝ\", \"၀\"]\n    },\n    \"toggle7\": {\n        \"interval\": 80,\n        \"frames\": [\"⦾\", \"⦿\"]\n    },\n    \"toggle8\": {\n        \"interval\": 100,\n        \"frames\": [\"◍\", \"◌\"]\n    },\n    \"toggle9\": {\n        \"interval\": 100,\n        \"frames\": [\"◉\", \"◎\"]\n    },\n    \"toggle10\": {\n        \"interval\": 100,\n        \"frames\": [\"㊂\", \"㊀\", \"㊁\"]\n    },\n    \"toggle11\": {\n        \"interval\": 50,\n        \"frames\": [\"⧇\", \"⧆\"]\n    },\n    \"toggle12\": {\n        \"interval\": 120,\n        \"frames\": [\"☗\", \"☖\"]\n    },\n    \"toggle13\": {\n        \"interval\": 80,\n        \"frames\": [\"=\", \"*\", \"-\"]\n    },\n    \"arrow\": {\n        \"interval\": 100,\n        \"frames\": [\"←\", \"↖\", \"↑\", \"↗\", \"→\", \"↘\", \"↓\", \"↙\"]\n    },\n    \"arrow2\": {\n        \"interval\": 80,\n        \"frames\": [\"⬆️ \", \"↗️ \", \"➡️ \", \"↘️ \", \"⬇️ \", \"↙️ \", \"⬅️ \", \"↖️ \"]\n    },\n    \"arrow3\": {\n        \"interval\": 120,\n        \"frames\": [\"▹▹▹▹▹\", \"▸▹▹▹▹\", \"▹▸▹▹▹\", \"▹▹▸▹▹\", \"▹▹▹▸▹\", \"▹▹▹▹▸\"]\n    },\n    \"bouncingBar\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"[    ]\", \"[=   ]\", \"[==  ]\", \"[=== ]\", \"[ ===]\", \"[  ==]\",\n            \"[   =]\", \"[    ]\", \"[   =]\", \"[  ==]\", \"[ ===]\", \"[====]\",\n            \"[=== ]\", \"[==  ]\", \"[=   ]\"\n        ]\n    },\n    \"bouncingBall\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"( ●    )\", \"(  ●   )\", \"(   ●  )\", \"(    ● )\", \"(     ●)\",\n            \"(    ● )\", \"(   ●  )\", \"(  ●   )\", \"( ●    )\", \"(●     )\"\n        ]\n    },\n    \"smiley\": {\n        \"interval\": 200,\n        \"frames\": [\"😄 \", \"😝 \"]\n    },\n    \"monkey\": {\n        \"interval\": 300,\n        \"frames\": [\"🙈 \", \"🙈 \", \"🙉 \", \"🙊 \"]\n    },\n    \"hearts\": {\n        \"interval\": 100,\n        \"frames\": [\"💛 \", \"💙 \", \"💜 \", \"💚 \", \"❤️ \"]\n    },\n    \"clock\": {\n        \"interval\":\n        100,\n        \"frames\": [\n            \"🕛 \", \"🕐 \", \"🕑 \", \"🕒 \", \"🕓 \", \"🕔 \", \"🕕 \", \"🕖 \", \"🕗 \", \"🕘 \", \"🕙 \",\n            \"🕚 \"\n        ]\n    },\n    \"earth\": {\n        \"interval\": 180,\n        \"frames\": [\"🌍 \", \"🌎 \", \"🌏 \"]\n    },\n    \"material\": {\n        \"interval\":\n        17,\n        \"frames\": [\n            \"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\", \"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n            \"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\", \"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n            \"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\", \"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n            \"███████▁▁▁▁▁▁▁▁▁▁▁▁▁\", \"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\n            \"█████████▁▁▁▁▁▁▁▁▁▁▁\", \"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n            \"██████████▁▁▁▁▁▁▁▁▁▁\", \"███████████▁▁▁▁▁▁▁▁▁\",\n            \"█████████████▁▁▁▁▁▁▁\", \"██████████████▁▁▁▁▁▁\",\n            \"██████████████▁▁▁▁▁▁\", \"▁██████████████▁▁▁▁▁\",\n            \"▁██████████████▁▁▁▁▁\", \"▁██████████████▁▁▁▁▁\",\n            \"▁▁██████████████▁▁▁▁\", \"▁▁▁██████████████▁▁▁\",\n            \"▁▁▁▁█████████████▁▁▁\", \"▁▁▁▁██████████████▁▁\",\n            \"▁▁▁▁██████████████▁▁\", \"▁▁▁▁▁██████████████▁\",\n            \"▁▁▁▁▁██████████████▁\", \"▁▁▁▁▁██████████████▁\",\n            \"▁▁▁▁▁▁██████████████\", \"▁▁▁▁▁▁██████████████\",\n            \"▁▁▁▁▁▁▁█████████████\", \"▁▁▁▁▁▁▁█████████████\",\n            \"▁▁▁▁▁▁▁▁████████████\", \"▁▁▁▁▁▁▁▁████████████\",\n            \"▁▁▁▁▁▁▁▁▁███████████\", \"▁▁▁▁▁▁▁▁▁███████████\",\n            \"▁▁▁▁▁▁▁▁▁▁██████████\", \"▁▁▁▁▁▁▁▁▁▁██████████\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁████████\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\", \"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\n            \"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\", \"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\n            \"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\", \"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\n            \"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\", \"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n            \"██████▁▁▁▁▁▁▁▁▁▁▁▁▁█\", \"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\n            \"█████████▁▁▁▁▁▁▁▁▁▁▁\", \"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n            \"█████████▁▁▁▁▁▁▁▁▁▁▁\", \"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n            \"███████████▁▁▁▁▁▁▁▁▁\", \"████████████▁▁▁▁▁▁▁▁\",\n            \"████████████▁▁▁▁▁▁▁▁\", \"██████████████▁▁▁▁▁▁\",\n            \"██████████████▁▁▁▁▁▁\", \"▁██████████████▁▁▁▁▁\",\n            \"▁██████████████▁▁▁▁▁\", \"▁▁▁█████████████▁▁▁▁\",\n            \"▁▁▁▁▁████████████▁▁▁\", \"▁▁▁▁▁████████████▁▁▁\",\n            \"▁▁▁▁▁▁███████████▁▁▁\", \"▁▁▁▁▁▁▁▁█████████▁▁▁\",\n            \"▁▁▁▁▁▁▁▁█████████▁▁▁\", \"▁▁▁▁▁▁▁▁▁█████████▁▁\",\n            \"▁▁▁▁▁▁▁▁▁█████████▁▁\", \"▁▁▁▁▁▁▁▁▁▁█████████▁\",\n            \"▁▁▁▁▁▁▁▁▁▁▁████████▁\", \"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\", \"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n            \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\", \"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\"\n        ]\n    },\n    \"moon\": {\n        \"interval\": 80,\n        \"frames\": [\"🌑 \", \"🌒 \", \"🌓 \", \"🌔 \", \"🌕 \", \"🌖 \", \"🌗 \", \"🌘 \"]\n    },\n    \"runner\": {\n        \"interval\": 140,\n        \"frames\": [\"🚶 \", \"🏃 \"]\n    },\n    \"pong\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"▐⠂       ▌\", \"▐⠈       ▌\", \"▐ ⠂      ▌\", \"▐ ⠠      ▌\",\n            \"▐  ⡀     ▌\", \"▐  ⠠     ▌\", \"▐   ⠂    ▌\", \"▐   ⠈    ▌\",\n            \"▐    ⠂   ▌\", \"▐    ⠠   ▌\", \"▐     ⡀  ▌\", \"▐     ⠠  ▌\",\n            \"▐      ⠂ ▌\", \"▐      ⠈ ▌\", \"▐       ⠂▌\", \"▐       ⠠▌\",\n            \"▐       ⡀▌\", \"▐      ⠠ ▌\", \"▐      ⠂ ▌\", \"▐     ⠈  ▌\",\n            \"▐     ⠂  ▌\", \"▐    ⠠   ▌\", \"▐    ⡀   ▌\", \"▐   ⠠    ▌\",\n            \"▐   ⠂    ▌\", \"▐  ⠈     ▌\", \"▐  ⠂     ▌\", \"▐ ⠠      ▌\",\n            \"▐ ⡀      ▌\", \"▐⠠       ▌\"\n        ]\n    },\n    \"shark\": {\n        \"interval\":\n        120,\n        \"frames\": [\n            \"▐|\\\\____________▌\", \"▐_|\\\\___________▌\", \"▐__|\\\\__________▌\",\n            \"▐___|\\\\_________▌\", \"▐____|\\\\________▌\", \"▐_____|\\\\_______▌\",\n            \"▐______|\\\\______▌\", \"▐_______|\\\\_____▌\", \"▐________|\\\\____▌\",\n            \"▐_________|\\\\___▌\", \"▐__________|\\\\__▌\", \"▐___________|\\\\_▌\",\n            \"▐____________|\\\\▌\", \"▐____________/|▌\", \"▐___________/|_▌\",\n            \"▐__________/|__▌\", \"▐_________/|___▌\", \"▐________/|____▌\",\n            \"▐_______/|_____▌\", \"▐______/|______▌\", \"▐_____/|_______▌\",\n            \"▐____/|________▌\", \"▐___/|_________▌\", \"▐__/|__________▌\",\n            \"▐_/|___________▌\", \"▐/|____________▌\"\n        ]\n    },\n    \"dqpb\": {\n        \"interval\": 100,\n        \"frames\": [\"d\", \"q\", \"p\", \"b\"]\n    },\n    \"weather\": {\n        \"interval\":\n        100,\n        \"frames\": [\n            \"☀️ \", \"☀️ \", \"☀️ \", \"🌤 \", \"⛅️ \", \"🌥 \", \"☁️ \", \"🌧 \", \"🌨 \", \"🌧 \",\n            \"🌨 \", \"🌧 \", \"🌨 \", \"⛈ \", \"🌨 \", \"🌧 \", \"🌨 \", \"☁️ \", \"🌥 \", \"⛅️ \", \"🌤 \",\n            \"☀️ \", \"☀️ \"\n        ]\n    },\n    \"christmas\": {\n        \"interval\": 400,\n        \"frames\": [\"🌲\", \"🎄\"]\n    },\n    \"grenade\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"،  \", \"′  \", \" ´ \", \" ‾ \", \"  ⸌\", \"  ⸊\", \"  |\", \"  ⁎\", \"  ⁕\",\n            \" ෴ \", \"  ⁓\", \"   \", \"   \", \"   \"\n        ]\n    },\n    \"point\": {\n        \"interval\": 125,\n        \"frames\": [\"∙∙∙\", \"●∙∙\", \"∙●∙\", \"∙∙●\", \"∙∙∙\"]\n    },\n    \"layer\": {\n        \"interval\": 150,\n        \"frames\": [\"-\", \"=\", \"≡\"]\n    },\n    \"betaWave\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"ρββββββ\", \"βρβββββ\", \"ββρββββ\", \"βββρβββ\", \"ββββρββ\", \"βββββρβ\",\n            \"ββββββρ\"\n        ]\n    },\n    \"fingerDance\": {\n        \"interval\": 160,\n        \"frames\": [\"🤘 \", \"🤟 \", \"🖖 \", \"✋ \", \"🤚 \", \"👆 \"]\n    },\n    \"fistBump\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"🤜\\u3000\\u3000\\u3000\\u3000🤛 \", \"🤜\\u3000\\u3000\\u3000\\u3000🤛 \",\n            \"🤜\\u3000\\u3000\\u3000\\u3000🤛 \", \"\\u3000🤜\\u3000\\u3000🤛\\u3000 \",\n            \"\\u3000\\u3000🤜🤛\\u3000\\u3000 \", \"\\u3000🤜✨🤛\\u3000\\u3000 \",\n            \"🤜\\u3000✨\\u3000🤛\\u3000 \"\n        ]\n    },\n    \"soccerHeader\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \" 🧑⚽️       🧑 \", \"🧑  ⚽️      🧑 \", \"🧑   ⚽️     🧑 \", \"🧑    ⚽️    🧑 \",\n            \"🧑     ⚽️   🧑 \", \"🧑      ⚽️  🧑 \", \"🧑       ⚽️🧑  \", \"🧑      ⚽️  🧑 \",\n            \"🧑     ⚽️   🧑 \", \"🧑    ⚽️    🧑 \", \"🧑   ⚽️     🧑 \", \"🧑  ⚽️      🧑 \"\n        ]\n    },\n    \"mindblown\": {\n        \"interval\":\n        160,\n        \"frames\": [\n            \"😐 \", \"😐 \", \"😮 \", \"😮 \", \"😦 \", \"😦 \", \"😧 \", \"😧 \", \"🤯 \", \"💥 \", \"✨ \",\n            \"\\u3000 \", \"\\u3000 \", \"\\u3000 \"\n        ]\n    },\n    \"speaker\": {\n        \"interval\": 160,\n        \"frames\": [\"🔈 \", \"🔉 \", \"🔊 \", \"🔉 \"]\n    },\n    \"orangePulse\": {\n        \"interval\": 100,\n        \"frames\": [\"🔸 \", \"🔶 \", \"🟠 \", \"🟠 \", \"🔶 \"]\n    },\n    \"bluePulse\": {\n        \"interval\": 100,\n        \"frames\": [\"🔹 \", \"🔷 \", \"🔵 \", \"🔵 \", \"🔷 \"]\n    },\n    \"orangeBluePulse\": {\n        \"interval\": 100,\n        \"frames\": [\"🔸 \", \"🔶 \", \"🟠 \", \"🟠 \", \"🔶 \", \"🔹 \", \"🔷 \", \"🔵 \", \"🔵 \", \"🔷 \"]\n    },\n    \"timeTravel\": {\n        \"interval\":\n        100,\n        \"frames\": [\n            \"🕛 \", \"🕚 \", \"🕙 \", \"🕘 \", \"🕗 \", \"🕖 \", \"🕕 \", \"🕔 \", \"🕓 \", \"🕒 \", \"🕑 \",\n            \"🕐 \"\n        ]\n    },\n    \"aesthetic\": {\n        \"interval\":\n        80,\n        \"frames\": [\n            \"▰▱▱▱▱▱▱\", \"▰▰▱▱▱▱▱\", \"▰▰▰▱▱▱▱\", \"▰▰▰▰▱▱▱\", \"▰▰▰▰▰▱▱\", \"▰▰▰▰▰▰▱\",\n            \"▰▰▰▰▰▰▰\", \"▰▱▱▱▱▱▱\"\n        ]\n    }\n}  # }}}\n\n\nclass Spinner:\n\n    def __init__(self, name: str = 'dots'):\n        definition = spinners[name]\n        self.interval = definition['interval'] / 1000\n        self.frames = definition['frames']\n        self.current_frame = -1\n        self.last_change_at = -self.interval\n\n    def __call__(self) -> str:\n        now = monotonic()\n        if now - self.last_change_at >= self.interval:\n            self.last_change_at = now\n            self.current_frame = (self.current_frame + 1) % len(self.frames)\n        return self.frames[self.current_frame]\n"
  },
  {
    "path": "kittens/tui/utils.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport sys\nfrom collections.abc import Sequence\nfrom contextlib import suppress\nfrom typing import TYPE_CHECKING, Optional, cast\n\nfrom kitty.types import run_once\n\nfrom .operations import raw_mode, set_cursor_visible\n\nif TYPE_CHECKING:\n    from kitty.options.types import Options\n\n\ndef get_key_press(allowed: str, default: str) -> str:\n    response = default\n    with raw_mode():\n        print(set_cursor_visible(False), end='', flush=True)\n        try:\n            while True:\n                q = sys.stdin.buffer.read(1)\n                if q:\n                    if q in b'\\x1b\\x03':\n                        break\n                    with suppress(Exception):\n                        response = q.decode('utf-8').lower()\n                        if response in allowed:\n                            break\n        except (KeyboardInterrupt, EOFError):\n            pass\n        finally:\n            print(set_cursor_visible(True), end='', flush=True)\n    return response\n\n\ndef format_number(val: float, max_num_of_decimals: int = 2) -> str:\n    ans = str(val)\n    pos = ans.find('.')\n    if pos > -1:\n        ans = ans[:pos + max_num_of_decimals + 1]\n    return ans.rstrip('0').rstrip('.')\n\n\ndef human_size(\n    size: int, sep: str = ' ',\n    max_num_of_decimals: int = 2,\n    unit_list: tuple[str, ...] = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB')\n) -> str:\n    \"\"\" Convert a size in bytes into a human readable form \"\"\"\n    if size < 2:\n        return f'{size}{sep}{unit_list[0]}'\n    from math import log\n    exponent = min(int(log(size, 1024)), len(unit_list) - 1)\n    return format_number(size / 1024**exponent, max_num_of_decimals) + sep + unit_list[exponent]\n\n\ndef kitty_opts() -> 'Options':\n    from kitty.fast_data_types import get_options, set_options\n    try:\n        ans = cast(Optional['Options'], get_options())\n    except RuntimeError:\n        ans = None\n    if ans is None:\n        from kitty.cli import create_default_opts\n        from kitty.utils import suppress_error_logging\n        with suppress_error_logging():\n            ans = create_default_opts()\n            set_options(ans)\n    return ans\n\n\ndef set_kitty_opts(paths: Sequence[str], overrides: Sequence[str] = ()) -> 'Options':\n    from kitty.config import load_config\n    from kitty.fast_data_types import set_options\n    from kitty.utils import suppress_error_logging\n    with suppress_error_logging():\n        opts = load_config(*paths, overrides=overrides or None)\n        set_options(opts)\n        return opts\n\n\ndef report_error(msg: str = '', return_code: int = 1, print_exc: bool = False) -> None:\n    ' Report an error also sending the overlay ready message to ensure kitten is visible '\n    from .operations import overlay_ready\n    print(end=overlay_ready())\n    if msg:\n        print(msg, file=sys.stderr)\n    if print_exc:\n        cls, e, tb = sys.exc_info()\n        if e and not isinstance(e, (SystemExit, KeyboardInterrupt)):\n            import traceback\n            traceback.print_exc()\n    with suppress(KeyboardInterrupt, EOFError):\n        input('Press Enter to quit')\n    raise SystemExit(return_code)\n\n\ndef report_unhandled_error(msg: str = '') -> None:\n    ' Report an unhandled exception with the overlay ready message '\n    return report_error(msg, print_exc=True)\n\n\n@run_once\ndef running_in_tmux() -> str:\n    socket = os.environ.get('TMUX')\n    if not socket:\n        return ''\n    parts = socket.split(',')\n    if len(parts) < 2:\n        return ''\n    try:\n        if not os.access(parts[0], os.R_OK | os.W_OK):\n            return ''\n    except OSError:\n        return ''\n    from kitty.child import cmdline_of_pid\n    c = cmdline_of_pid(int(parts[1]))\n    if not c:\n        return ''\n    exe = os.path.basename(c[0])\n    if exe.lower() == 'tmux':\n        return exe\n    return ''\n"
  },
  {
    "path": "kittens/unicode_input/__init__.py",
    "content": ""
  },
  {
    "path": "kittens/unicode_input/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage unicode_input\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/readline\"\n\t\"github.com/kovidgoyal/kitty/tools/unicode_names\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nconst INDEX_CHAR string = \".\"\nconst INDEX_BASE = 36\nconst InvalidChar rune = unicode.MaxRune + 1\nconst default_set_of_symbols string = `\n‘’“”‹›«»‚„ 😀😛😇😈😉😍😎😮👍👎 —–§¶†‡©®™ →⇒•·°±−×÷¼½½¾\n…µ¢£€¿¡¨´¸ˆ˜ ÀÁÂÃÄÅÆÇÈÉÊË ÌÍÎÏÐÑÒÓÔÕÖØ ŒŠÙÚÛÜÝŸÞßàá âãäåæçèéêëìí\nîïðñòóôõöøœš ùúûüýÿþªºαΩ∞\n`\n\nvar DEFAULT_SET []rune\nvar EMOTICONS_SET []rune\n\nconst DEFAULT_MODE string = \"HEX\"\n\nfunc build_sets() {\n\tDEFAULT_SET = make([]rune, 0, len(default_set_of_symbols))\n\tfor _, ch := range default_set_of_symbols {\n\t\tif !unicode.IsSpace(ch) {\n\t\t\tDEFAULT_SET = append(DEFAULT_SET, ch)\n\t\t}\n\t}\n\tEMOTICONS_SET = make([]rune, 0, 0x1f64f-0x1f600+1)\n\tfor i := 0x1f600; i <= 0x1f64f; i++ {\n\t\tEMOTICONS_SET = append(EMOTICONS_SET, rune(i))\n\t}\n}\n\nfunc codepoint_ok(code rune) bool {\n\treturn !(code <= 32 || code == 127 || (128 <= code && code <= 159) || (0xd800 <= code && code <= 0xdbff) || (0xDC00 <= code && code <= 0xDFFF) || code > unicode.MaxRune)\n}\n\nfunc parse_favorites(raw string) (ans []rune) {\n\tans = make([]rune, 0, 128)\n\tfor _, line := range utils.Splitlines(raw) {\n\t\tline = strings.TrimSpace(line)\n\t\tif len(line) == 0 || strings.HasPrefix(line, \"#\") {\n\t\t\tcontinue\n\t\t}\n\t\tidx := strings.Index(line, \"#\")\n\t\tif idx > -1 {\n\t\t\tline = line[:idx]\n\t\t}\n\t\tcode_text, _, _ := strings.Cut(line, \" \")\n\t\tcode, err := strconv.ParseInt(code_text, 16, 32)\n\t\tif err == nil && code <= utf8.MaxRune && codepoint_ok(rune(code)) {\n\t\t\tans = append(ans, rune(code))\n\t\t}\n\t}\n\treturn\n}\n\nfunc serialize_favorites(favs []rune) string {\n\tb := strings.Builder{}\n\tb.Grow(8192)\n\tb.WriteString(`# Favorite characters for unicode input\n# Enter the hex code for each favorite character on a new line. Blank lines are\n# ignored and anything after a # is considered a comment.\n\n`)\n\tfor _, ch := range favs {\n\t\tb.WriteString(fmt.Sprintf(\"%x # %s %s\\n\", ch, string(ch), unicode_names.NameForCodePoint(ch)))\n\t}\n\n\treturn b.String()\n}\n\nvar loaded_favorites []rune\nvar favorites_loaded_from_user_config bool\n\nfunc favorites_path() string {\n\treturn filepath.Join(utils.ConfigDir(), \"unicode-input-favorites.conf\")\n}\n\nfunc load_favorites(refresh bool) []rune {\n\tif refresh || loaded_favorites == nil {\n\t\traw, err := os.ReadFile(favorites_path())\n\t\tif err == nil {\n\t\t\tloaded_favorites = parse_favorites(utils.UnsafeBytesToString(raw))\n\t\t\tfavorites_loaded_from_user_config = true\n\t\t} else {\n\t\t\tloaded_favorites = DEFAULT_SET\n\t\t\tfavorites_loaded_from_user_config = false\n\t\t}\n\t}\n\treturn loaded_favorites\n}\n\ntype CachedData struct {\n\tRecent []rune `json:\"recent,omitempty\"`\n\tMode   string `json:\"mode,omitempty\"`\n}\n\nvar cached_data *CachedData\n\ntype Mode int\n\nconst (\n\tHEX Mode = iota\n\tNAME\n\tEMOTICONS\n\tFAVORITES\n)\n\ntype ModeData struct {\n\tmode  Mode\n\tkey   string\n\ttitle string\n}\n\nvar all_modes [4]ModeData\n\ntype checkpoints_key struct {\n\tmode       Mode\n\ttext       string\n\tcodepoints []rune\n\tindex_word int\n}\n\nfunc (self *checkpoints_key) clear() {\n\t*self = checkpoints_key{}\n}\n\nfunc (self *checkpoints_key) is_equal(other checkpoints_key) bool {\n\treturn self.mode == other.mode && self.text == other.text && slices.Equal(self.codepoints, other.codepoints) && self.index_word == other.index_word\n}\n\ntype handler struct {\n\tmode            Mode\n\trecent          []rune\n\tcurrent_char    rune\n\terr             error\n\tlp              *loop.Loop\n\tctx             style.Context\n\trl              *readline.Readline\n\tchoice_line     string\n\temoji_variation string\n\tcheckpoints_key checkpoints_key\n\ttable           table\n\n\tcurrent_tab_formatter, tab_bar_formatter, chosen_formatter, chosen_name_formatter, dim_formatter func(...any) string\n}\n\nfunc (self *handler) initialize() {\n\tself.ctx.AllowEscapeCodes = true\n\tself.checkpoints_key.index_word = -1\n\tself.table.initialize(self.emoji_variation, self.ctx)\n\tself.lp.SetWindowTitle(\"Unicode input\")\n\tself.current_char = InvalidChar\n\tself.current_tab_formatter = self.ctx.SprintFunc(\"reverse=false bold=true\")\n\tself.tab_bar_formatter = self.ctx.SprintFunc(\"reverse=true\")\n\tself.chosen_formatter = self.ctx.SprintFunc(\"fg=green\")\n\tself.chosen_name_formatter = self.ctx.SprintFunc(\"italic=true dim=true\")\n\tself.dim_formatter = self.ctx.SprintFunc(\"dim=true\")\n\tself.rl = readline.New(self.lp, readline.RlInit{Prompt: \"> \", DontMarkPrompts: true})\n\tself.rl.Start()\n\tself.refresh()\n}\n\nfunc (self *handler) finalize() string {\n\tself.rl.End()\n\tself.rl.Shutdown()\n\treturn \"\"\n}\n\nfunc (self *handler) resolved_char() string {\n\tif self.current_char == InvalidChar {\n\t\treturn \"\"\n\t}\n\treturn resolved_char(self.current_char, self.emoji_variation)\n}\n\nfunc is_index(word string) bool {\n\tif !strings.HasPrefix(word, INDEX_CHAR) {\n\t\treturn false\n\t}\n\tword = strings.TrimLeft(word, INDEX_CHAR)\n\t_, err := strconv.ParseUint(word, INDEX_BASE, 32)\n\treturn err == nil\n}\n\nfunc (self *handler) update_codepoints() {\n\tvar q checkpoints_key\n\tq.mode = self.mode\n\tq.index_word = -1\n\tswitch self.mode {\n\tcase HEX:\n\t\tq.codepoints = self.recent\n\t\tif len(q.codepoints) == 0 {\n\t\t\tq.codepoints = DEFAULT_SET\n\t\t}\n\tcase EMOTICONS:\n\t\tq.codepoints = EMOTICONS_SET\n\tcase FAVORITES:\n\t\tq.codepoints = load_favorites(false)\n\tcase NAME:\n\t\tq.text = self.rl.AllText()\n\t\tif !q.is_equal(self.checkpoints_key) {\n\t\t\twords := strings.Split(q.text, \" \")\n\t\t\twords = utils.RemoveAll(words, INDEX_CHAR)\n\t\t\tif len(words) > 1 {\n\t\t\t\tfor i, w := range words {\n\t\t\t\t\tif i > 0 && is_index(w) {\n\t\t\t\t\t\tiw := words[i]\n\t\t\t\t\t\twords = words[:i]\n\t\t\t\t\t\tif index_word, perr := strconv.ParseInt(strings.TrimLeft(iw, INDEX_CHAR), INDEX_BASE, 0); perr == nil {\n\t\t\t\t\t\t\tq.index_word = int(index_word)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tquery := strings.Join(words, \" \")\n\t\t\tif len(query) > 1 {\n\t\t\t\twords = words[1:]\n\t\t\t\tq.codepoints = unicode_names.CodePointsForQuery(query)\n\t\t\t}\n\t\t}\n\t}\n\tif !q.is_equal(self.checkpoints_key) {\n\t\tself.checkpoints_key = q\n\t\tself.table.set_codepoints(q.codepoints, self.mode, q.index_word)\n\t}\n}\n\nvar debugprintln = tty.DebugPrintln\n\nfunc (self *handler) update_current_char() {\n\tself.update_codepoints()\n\tself.current_char = InvalidChar\n\ttext := self.rl.AllText()\n\tswitch self.mode {\n\tcase HEX:\n\t\tif strings.HasPrefix(text, INDEX_CHAR) {\n\t\t\tif len(text) > 1 {\n\t\t\t\tself.current_char = self.table.codepoint_at_hint(text[1:])\n\t\t\t}\n\t\t} else if len(text) > 0 {\n\t\t\tcode, err := strconv.ParseUint(text, 16, 32)\n\t\t\tif err == nil && code <= unicode.MaxRune {\n\t\t\t\tself.current_char = rune(code)\n\t\t\t}\n\t\t}\n\tcase NAME:\n\t\tcc := self.table.current_codepoint()\n\t\tif cc > 0 && cc <= unicode.MaxRune {\n\t\t\tself.current_char = rune(cc)\n\t\t}\n\tdefault:\n\t\tif len(text) > 0 {\n\t\t\tself.current_char = self.table.codepoint_at_hint(strings.TrimLeft(text, INDEX_CHAR))\n\t\t}\n\t}\n\tif !codepoint_ok(self.current_char) {\n\t\tself.current_char = InvalidChar\n\t}\n}\n\nfunc (self *handler) update_prompt() {\n\tself.update_current_char()\n\tch := \"??\"\n\tcolor := \"red\"\n\tself.choice_line = \"\"\n\tif self.current_char != InvalidChar {\n\t\tch, color = self.resolved_char(), \"green\"\n\t\tself.choice_line = fmt.Sprintf(\n\t\t\t\"Chosen: %s U+%x %s\", self.chosen_formatter(ch), self.current_char,\n\t\t\tself.chosen_name_formatter(title(unicode_names.NameForCodePoint(self.current_char))))\n\t}\n\tprompt := fmt.Sprintf(\"%s> \", self.ctx.SprintFunc(\"fg=\"+color)(ch))\n\tself.rl.SetPrompt(prompt)\n}\n\nfunc (self *handler) draw_title_bar() {\n\tself.lp.AllowLineWrapping(false)\n\tentries := make([]string, 0, len(all_modes))\n\tfor _, md := range all_modes {\n\t\tentry := fmt.Sprintf(\" %s (%s) \", md.title, md.key)\n\t\tif md.mode == self.mode {\n\t\t\tentry = self.current_tab_formatter(entry)\n\t\t}\n\t\tentries = append(entries, entry)\n\t}\n\tsz, _ := self.lp.ScreenSize()\n\ttext := fmt.Sprintf(\"Search by:%s\", strings.Join(entries, \"\"))\n\textra := int(sz.WidthCells) - wcswidth.Stringwidth(text)\n\tif extra > 0 {\n\t\ttext += strings.Repeat(\" \", extra)\n\t}\n\tself.lp.Println(self.tab_bar_formatter(text))\n}\n\nfunc (self *handler) draw_screen() {\n\tself.lp.StartAtomicUpdate()\n\tdefer self.lp.EndAtomicUpdate()\n\tself.lp.ClearScreen()\n\tself.draw_title_bar()\n\n\ty := 1\n\twriteln := func(text ...any) {\n\t\tself.lp.Println(text...)\n\t\ty += 1\n\t}\n\tswitch self.mode {\n\tcase NAME:\n\t\twriteln(\"Enter words from the name of the character\")\n\tcase HEX:\n\t\twriteln(\"Enter the hex code for the character\")\n\tdefault:\n\t\twriteln(\"Enter the index for the character you want from the list below\")\n\t}\n\tself.rl.RedrawNonAtomic()\n\tself.lp.AllowLineWrapping(false)\n\tself.lp.SaveCursorPosition()\n\tdefer self.lp.RestoreCursorPosition()\n\twriteln()\n\twriteln(self.choice_line)\n\tsz, _ := self.lp.ScreenSize()\n\n\twrite_help := func(x string) {\n\t\tlines := style.WrapTextAsLines(x, int(sz.WidthCells)-1, style.WrapOptions{})\n\t\tfor _, line := range lines {\n\t\t\tif line != \"\" {\n\t\t\t\twriteln(self.dim_formatter(line))\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch self.mode {\n\tcase HEX:\n\t\twrite_help(fmt.Sprintf(\"Type %s followed by the index for the recent entries below\", INDEX_CHAR))\n\tcase NAME:\n\t\twrite_help(fmt.Sprintf(\"Use Tab or arrow keys to choose a character. Type space and %s to select by index\", INDEX_CHAR))\n\tcase FAVORITES:\n\t\twrite_help(\"Press F12 to edit the list of favorites\")\n\t}\n\tq := self.table.layout(int(sz.HeightCells)-y, int(sz.WidthCells))\n\tif q != \"\" {\n\t\tself.lp.QueueWriteString(q)\n\t}\n}\n\nfunc (self *handler) on_text(text string, from_key_event, in_bracketed_paste bool) error {\n\terr := self.rl.OnText(text, from_key_event, in_bracketed_paste)\n\tif err != nil {\n\t\treturn err\n\t}\n\tself.refresh()\n\treturn nil\n}\n\nfunc (self *handler) switch_mode(mode Mode) {\n\tif self.mode != mode {\n\t\tself.mode = mode\n\t\tself.rl.ResetText()\n\t\tself.current_char = InvalidChar\n\t\tself.choice_line = \"\"\n\t}\n}\n\nfunc (self *handler) handle_hex_key_event(event *loop.KeyEvent) {\n\ttext := self.rl.AllText()\n\tuval, err := strconv.ParseUint(text, 16, 32)\n\tnew_val := -1\n\tif err != nil || uval > math.MaxInt {\n\t\treturn\n\t}\n\tval := int(uval)\n\tif event.MatchesPressOrRepeat(\"tab\") {\n\t\tnew_val = val + 10\n\t} else if event.MatchesPressOrRepeat(\"up\") {\n\t\tnew_val = val + 1\n\t} else if event.MatchesPressOrRepeat(\"down\") {\n\t\tnew_val = max(32, val-1)\n\t}\n\tif new_val > -1 {\n\t\tevent.Handled = true\n\t\tself.rl.SetText(fmt.Sprintf(\"%x\", new_val))\n\t}\n}\n\nfunc (self *handler) handle_name_key_event(event *loop.KeyEvent) {\n\tif event.MatchesPressOrRepeat(\"shift+tab\") || event.MatchesPressOrRepeat(\"left\") {\n\t\tevent.Handled = true\n\t\tself.table.move_current(0, -1)\n\t} else if event.MatchesPressOrRepeat(\"tab\") || event.MatchesPressOrRepeat(\"right\") {\n\t\tevent.Handled = true\n\t\tself.table.move_current(0, 1)\n\t} else if event.MatchesPressOrRepeat(\"up\") {\n\t\tevent.Handled = true\n\t\tself.table.move_current(-1, 0)\n\t} else if event.MatchesPressOrRepeat(\"down\") {\n\t\tevent.Handled = true\n\t\tself.table.move_current(1, 0)\n\t}\n}\n\nfunc (self *handler) handle_emoticons_key_event(event *loop.KeyEvent) {\n}\n\nfunc (self *handler) handle_favorites_key_event(event *loop.KeyEvent) {\n\tif event.MatchesPressOrRepeat(\"f12\") {\n\t\tevent.Handled = true\n\t\texe, err := os.Executable()\n\t\tif err != nil {\n\t\t\tself.err = err\n\t\t\tself.lp.Quit(1)\n\t\t\treturn\n\t\t}\n\t\tfp := favorites_path()\n\t\tif len(load_favorites(false)) == 0 || !favorites_loaded_from_user_config {\n\t\t\traw := serialize_favorites(load_favorites(false))\n\t\t\terr = os.MkdirAll(filepath.Dir(fp), 0o755)\n\t\t\tif err != nil {\n\t\t\t\tself.err = fmt.Errorf(\"Failed to create config directory to store favorites in: %w\", err)\n\t\t\t\tself.lp.Quit(1)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = utils.AtomicUpdateFile(fp, bytes.NewReader(utils.UnsafeStringToBytes(raw)), 0o600)\n\t\t\tif err != nil {\n\t\t\t\tself.err = fmt.Errorf(\"Failed to write to favorites file %s with error: %w\", fp, err)\n\t\t\t\tself.lp.Quit(1)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\terr = self.lp.SuspendAndRun(func() error {\n\t\t\tcmd := exec.Command(exe, \"edit-in-kitty\", \"--type=overlay\", fp)\n\t\t\tcmd.Stdin = os.Stdin\n\t\t\tcmd.Stdout = os.Stdout\n\t\t\tcmd.Stderr = os.Stderr\n\t\t\terr = cmd.Run()\n\t\t\tif err == nil {\n\t\t\t\tload_favorites(true)\n\t\t\t} else {\n\t\t\t\tfmt.Fprintln(os.Stderr, err)\n\t\t\t\tfmt.Fprintln(os.Stderr, \"Failed to run edit-in-kitty, favorites have not been changed. Press Enter to continue.\")\n\t\t\t\tvar ln string\n\t\t\t\tfmt.Scanln(&ln)\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tif err != nil {\n\t\t\tself.err = err\n\t\t\tself.lp.Quit(1)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (self *handler) next_mode(delta int) {\n\tfor num, md := range all_modes {\n\t\tif md.mode == self.mode {\n\t\t\tidx := (num + delta + len(all_modes)) % len(all_modes)\n\t\t\tmd = all_modes[idx]\n\t\t\tself.switch_mode(md.mode)\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nvar ErrCanceledByUser = errors.New(\"Canceled by user\")\n\nfunc (self *handler) on_key_event(event *loop.KeyEvent) (err error) {\n\tif event.MatchesPressOrRepeat(\"esc\") || event.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\treturn ErrCanceledByUser\n\t}\n\tif event.MatchesPressOrRepeat(\"f1\") || event.MatchesPressOrRepeat(\"ctrl+1\") {\n\t\tevent.Handled = true\n\t\tself.switch_mode(HEX)\n\t} else if event.MatchesPressOrRepeat(\"f2\") || event.MatchesPressOrRepeat(\"ctrl+2\") {\n\t\tevent.Handled = true\n\t\tself.switch_mode(NAME)\n\t} else if event.MatchesPressOrRepeat(\"f3\") || event.MatchesPressOrRepeat(\"ctrl+3\") {\n\t\tevent.Handled = true\n\t\tself.switch_mode(EMOTICONS)\n\t} else if event.MatchesPressOrRepeat(\"f4\") || event.MatchesPressOrRepeat(\"ctrl+4\") {\n\t\tevent.Handled = true\n\t\tself.switch_mode(FAVORITES)\n\t} else if event.MatchesPressOrRepeat(\"ctrl+tab\") || event.MatchesPressOrRepeat(\"ctrl+]\") {\n\t\tevent.Handled = true\n\t\tself.next_mode(1)\n\t} else if event.MatchesPressOrRepeat(\"ctrl+shift+tab\") || event.MatchesPressOrRepeat(\"ctrl+[\") {\n\t\tevent.Handled = true\n\t\tself.next_mode(-1)\n\t}\n\tif !event.Handled {\n\t\tswitch self.mode {\n\t\tcase HEX:\n\t\t\tself.handle_hex_key_event(event)\n\t\tcase NAME:\n\t\t\tself.handle_name_key_event(event)\n\t\tcase EMOTICONS:\n\t\t\tself.handle_emoticons_key_event(event)\n\t\tcase FAVORITES:\n\t\t\tself.handle_favorites_key_event(event)\n\t\t}\n\t}\n\tif !event.Handled {\n\t\terr = self.rl.OnKeyEvent(event)\n\t\tif err != nil {\n\t\t\tif err == readline.ErrAcceptInput {\n\t\t\t\tself.refresh()\n\t\t\t\tself.lp.Quit(0)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\tif event.Handled {\n\t\tself.refresh()\n\t}\n\treturn\n}\n\nfunc (self *handler) refresh() {\n\tself.update_prompt()\n\tself.draw_screen()\n}\n\nfunc run_loop(opts *Options) (lp *loop.Loop, err error) {\n\toutput := tui.KittenOutputSerializer()\n\tlp, err = loop.New()\n\tif err != nil {\n\t\treturn\n\t}\n\tcv := utils.NewCachedValues(\"unicode-input\", &CachedData{Recent: DEFAULT_SET, Mode: DEFAULT_MODE})\n\tcached_data = cv.Load()\n\tdefer cv.Save()\n\n\th := handler{recent: cached_data.Recent, lp: lp, emoji_variation: opts.EmojiVariation}\n\tswitch opts.Tab {\n\tcase \"previous\":\n\t\tswitch cached_data.Mode {\n\t\tcase \"HEX\":\n\t\t\th.mode = HEX\n\t\tcase \"NAME\":\n\t\t\th.mode = NAME\n\t\tcase \"EMOTICONS\":\n\t\t\th.mode = EMOTICONS\n\t\tcase \"FAVORITES\":\n\t\t\th.mode = FAVORITES\n\t\t}\n\tcase \"code\":\n\t\th.mode = HEX\n\tcase \"name\":\n\t\th.mode = NAME\n\tcase \"emoticons\":\n\t\th.mode = EMOTICONS\n\tcase \"favorites\":\n\t\th.mode = FAVORITES\n\t}\n\tall_modes[0] = ModeData{mode: HEX, title: \"Code\", key: \"F1\"}\n\tall_modes[1] = ModeData{mode: NAME, title: \"Name\", key: \"F2\"}\n\tall_modes[2] = ModeData{mode: EMOTICONS, title: \"Emoticons\", key: \"F3\"}\n\tall_modes[3] = ModeData{mode: FAVORITES, title: \"Favorites\", key: \"F4\"}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\th.initialize()\n\t\tlp.SendOverlayReady()\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnResize = func(old_size, new_size loop.ScreenSize) error {\n\t\th.refresh()\n\t\treturn nil\n\t}\n\n\tlp.OnResumeFromStop = func() error {\n\t\th.refresh()\n\t\treturn nil\n\t}\n\n\tlp.OnText = h.on_text\n\tlp.OnFinalize = h.finalize\n\tlp.OnKeyEvent = h.on_key_event\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn\n\t}\n\tif h.err == nil {\n\t\tswitch h.mode {\n\t\tcase HEX:\n\t\t\tcached_data.Mode = \"HEX\"\n\t\tcase NAME:\n\t\t\tcached_data.Mode = \"NAME\"\n\t\tcase EMOTICONS:\n\t\t\tcached_data.Mode = \"EMOTICONS\"\n\t\tcase FAVORITES:\n\t\t\tcached_data.Mode = \"FAVORITES\"\n\t\t}\n\t\tif h.current_char != InvalidChar {\n\t\t\tcached_data.Recent = h.recent\n\t\t\tidx := slices.Index(cached_data.Recent, h.current_char)\n\t\t\tif idx > -1 {\n\t\t\t\tcached_data.Recent = slices.Delete(cached_data.Recent, idx, idx+1)\n\t\t\t}\n\t\t\tcached_data.Recent = slices.Insert(cached_data.Recent, 0, h.current_char)\n\t\t\tif len(cached_data.Recent) > len(DEFAULT_SET) {\n\t\t\t\tcached_data.Recent = cached_data.Recent[:len(DEFAULT_SET)]\n\t\t\t}\n\t\t\tans := h.resolved_char()\n\t\t\to, err := output(ans)\n\t\t\tif err != nil {\n\t\t\t\treturn lp, err\n\t\t\t}\n\t\t\tfmt.Println(o)\n\t\t}\n\t}\n\terr = h.err\n\treturn\n}\n\nfunc main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {\n\tgo unicode_names.Initialize() // start parsing name data in the background\n\tbuild_sets()\n\tlp, err := run_loop(o)\n\tif err != nil {\n\t\tif err == ErrCanceledByUser {\n\t\t\terr = nil\n\t\t}\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Println(\"Killed by signal: \", ds)\n\t\tlp.KillIfSignalled()\n\t\treturn 1, nil\n\t}\n\treturn\n}\n\nfunc EntryPoint(parent *cli.Command) {\n\tcreate_cmd(parent, main)\n}\n"
  },
  {
    "path": "kittens/unicode_input/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom kitty.typing_compat import BossType\n\nfrom ..tui.handler import result_handler\n\nhelp_text = 'Input a Unicode character'\nusage = ''\nOPTIONS = '''\n--emoji-variation\ntype=choices\ndefault=none\nchoices=none,graphic,text\nWhether to use the textual or the graphical form for emoji. By default the\ndefault form specified in the Unicode standard for the symbol is used.\n\n\n--tab\ntype=choices\ndefault=previous\nchoices=previous,code,name,emoticons,favorites\nThe initial tab to display. Defaults to using the tab from the previous kitten invocation.\n\n\n'''.format\n\n\n@result_handler(has_ready_notification=True)\ndef handle_result(args: list[str], current_char: str, target_window_id: int, boss: BossType) -> None:\n    w = boss.window_id_map.get(target_window_id)\n    if w is not None:\n        w.paste_text(current_char)\n\ndef main(args: list[str]) -> str | None:\n    raise SystemExit('This should be run as kitten unicode_input')\n\nif __name__ == '__main__':\n    main([])\nelif __name__ == '__doc__':\n    import sys\n    cd = sys.cli_docs  # type: ignore\n    cd['usage'] = usage\n    cd['options'] = OPTIONS\n    cd['help_text'] = help_text\n    cd['short_desc'] = 'Browse and select unicode characters by name'\n"
  },
  {
    "path": "kittens/unicode_input/table.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage unicode_input\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/unicode_names\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nfunc resolved_char(ch rune, emoji_variation string) string {\n\tans := string(ch)\n\tif wcswidth.IsEmojiPresentationBase(ch) {\n\t\tswitch emoji_variation {\n\t\tcase \"text\":\n\t\t\tans += \"\\ufe0e\"\n\t\tcase \"graphic\":\n\t\t\tans += \"\\ufe0f\"\n\t\t}\n\t}\n\treturn ans\n\n}\n\nfunc decode_hint(text string) int {\n\tx, err := strconv.ParseInt(text, INDEX_BASE, 0)\n\tif err != nil || x < 0 {\n\t\treturn -1\n\t}\n\treturn int(x)\n}\n\nfunc encode_hint(num int) string {\n\treturn strconv.FormatUint(uint64(num), INDEX_BASE)\n}\n\nfunc ljust(s string, sz int) string {\n\tx := wcswidth.Stringwidth(s)\n\tif x < sz {\n\t\ts += strings.Repeat(\" \", sz-x)\n\t}\n\treturn s\n}\n\ntype scroll_data struct {\n\tnum_items_per_page int\n\tscroll_rows        int\n}\n\ntype table struct {\n\temoji_variation      string\n\tlayout_dirty         bool\n\tlast_rows, last_cols int\n\tcodepoints           []rune\n\tcurrent_idx          int\n\tscroll_data          scroll_data\n\ttext                 string\n\tnum_cols, num_rows   int\n\tmode                 Mode\n\n\tgreen, reversed, intense_gray func(...any) string\n}\n\nfunc (self *table) initialize(emoji_variation string, ctx style.Context) {\n\tself.emoji_variation = emoji_variation\n\tself.layout_dirty = true\n\tself.last_cols, self.last_rows = -1, -1\n\tself.green = ctx.SprintFunc(\"fg=green\")\n\tself.reversed = ctx.SprintFunc(\"reverse=true\")\n\tself.intense_gray = ctx.SprintFunc(\"fg=intense-gray\")\n}\n\nfunc (self *table) current_codepoint() rune {\n\tif len(self.codepoints) > 0 {\n\t\treturn self.codepoints[self.current_idx]\n\t}\n\treturn InvalidChar\n}\n\nfunc (self *table) set_codepoints(codepoints []rune, mode Mode, current_idx int) {\n\tdelta := len(codepoints) - len(self.codepoints)\n\tself.codepoints = codepoints\n\tif self.codepoints != nil && mode != FAVORITES && mode != HEX {\n\t\tslices.Sort(self.codepoints)\n\t}\n\tself.mode = mode\n\tself.layout_dirty = true\n\tif current_idx > -1 && current_idx < len(self.codepoints) {\n\t\tself.current_idx = current_idx\n\t}\n\tif self.current_idx >= len(self.codepoints) {\n\t\tself.current_idx = 0\n\t}\n\tif delta != 0 {\n\t\tself.scroll_data = scroll_data{}\n\t}\n}\n\nfunc (self *table) codepoint_at_hint(hint string) rune {\n\tidx := decode_hint(hint)\n\tif idx >= 0 && idx < len(self.codepoints) {\n\t\treturn self.codepoints[idx]\n\t}\n\treturn InvalidChar\n}\n\ntype cell_data struct {\n\tidx, ch, desc string\n}\n\nfunc title(x string) string {\n\tif len(x) > 1 {\n\t\tx = strings.ToUpper(x[:1]) + x[1:]\n\t}\n\treturn x\n}\n\nfunc (self *table) layout(rows, cols int) string {\n\tif !self.layout_dirty && self.last_cols == cols && self.last_rows == rows {\n\t\treturn self.text\n\t}\n\tself.last_cols, self.last_rows = cols, rows\n\tself.layout_dirty = false\n\tvar as_parts func(int, rune) cell_data\n\tvar cell func(int, cell_data)\n\tvar idx_size, space_for_desc int\n\toutput := strings.Builder{}\n\toutput.Grow(4096)\n\tswitch self.mode {\n\tcase NAME:\n\t\tas_parts = func(i int, codepoint rune) cell_data {\n\t\t\treturn cell_data{idx: ljust(encode_hint(i), idx_size), ch: resolved_char(codepoint, self.emoji_variation), desc: title(unicode_names.NameForCodePoint(codepoint))}\n\t\t}\n\n\t\tcell = func(i int, cd cell_data) {\n\t\t\tis_current := i == self.current_idx\n\t\t\ttext := self.green(cd.idx) + \" \" + cd.ch + \" \"\n\t\t\tw := wcswidth.Stringwidth(cd.ch)\n\t\t\tif w < 2 {\n\t\t\t\ttext += strings.Repeat(\" \", (2 - w))\n\t\t\t}\n\t\t\tdesc_width := wcswidth.Stringwidth(cd.desc)\n\t\t\tif desc_width > space_for_desc {\n\t\t\t\ttext += cd.desc[:space_for_desc-1] + \"…\"\n\t\t\t} else {\n\t\t\t\ttext += cd.desc\n\t\t\t\textra := space_for_desc - desc_width\n\t\t\t\tif extra > 0 {\n\t\t\t\t\ttext += strings.Repeat(\" \", extra)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif is_current {\n\t\t\t\ttext = self.reversed(text)\n\t\t\t}\n\t\t\toutput.WriteString(text)\n\t\t}\n\tdefault:\n\t\tas_parts = func(i int, codepoint rune) cell_data {\n\t\t\treturn cell_data{idx: ljust(encode_hint(i), idx_size), ch: resolved_char(codepoint, self.emoji_variation)}\n\t\t}\n\n\t\tcell = func(i int, cd cell_data) {\n\t\t\toutput.WriteString(self.green(cd.idx))\n\t\t\toutput.WriteString(\" \")\n\t\t\toutput.WriteString(self.intense_gray(cd.ch))\n\t\t\tw := wcswidth.Stringwidth(cd.ch)\n\t\t\tif w < 2 {\n\t\t\t\toutput.WriteString(strings.Repeat(\" \", (2 - w)))\n\t\t\t}\n\t\t}\n\t}\n\n\tnum := len(self.codepoints)\n\tif num < 1 {\n\t\tself.text = \"\"\n\t\tself.num_cols = 0\n\t\tself.num_rows = 0\n\t\treturn self.text\n\t}\n\tidx_size = len(encode_hint(num - 1))\n\n\tparts := make([]cell_data, len(self.codepoints))\n\tfor i, ch := range self.codepoints {\n\t\tparts[i] = as_parts(i, ch)\n\t}\n\tlongest := 0\n\tswitch self.mode {\n\tcase NAME:\n\t\tfor _, p := range parts {\n\t\t\tlongest = utils.Max(longest, idx_size+2+len(p.desc)+2)\n\t\t}\n\tdefault:\n\t\tlongest = idx_size + 3\n\t}\n\tcol_width := longest + 2\n\tcol_width = utils.Min(col_width, 40)\n\tself.num_cols = utils.Max(cols/col_width, 1)\n\tif self.num_cols == 1 {\n\t\tcol_width = cols\n\t}\n\tspace_for_desc = col_width - 2 - idx_size - 4\n\tself.num_rows = rows\n\trows_left := rows\n\tif self.scroll_data.num_items_per_page != self.num_cols*self.num_rows {\n\t\tself.update_scroll_data()\n\t}\n\tskip_scroll := self.scroll_data.scroll_rows * self.num_cols\n\n\tfor i, cd := range parts {\n\t\tif skip_scroll > 0 {\n\t\t\tskip_scroll -= 1\n\t\t\tcontinue\n\t\t}\n\t\tcell(i, cd)\n\t\toutput.WriteString(\"  \")\n\t\tif self.num_cols == 1 || (i > 0 && (i+1)%self.num_cols == 0) {\n\t\t\trows_left -= 1\n\t\t\tif rows_left == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\toutput.WriteString(\"\\r\\n\")\n\t\t}\n\t}\n\n\tself.text = output.String()\n\treturn self.text\n}\n\nfunc (self *table) update_scroll_data() {\n\tself.scroll_data.num_items_per_page = self.num_rows * self.num_cols\n\tpage_num := self.current_idx / self.scroll_data.num_items_per_page\n\tself.scroll_data.scroll_rows = self.num_rows * page_num\n}\n\nfunc (self *table) move_current(rows, cols int) {\n\tif len(self.codepoints) == 0 {\n\t\treturn\n\t}\n\tif cols != 0 {\n\t\tself.current_idx = (self.current_idx + len(self.codepoints) + cols) % len(self.codepoints)\n\t\tself.layout_dirty = true\n\t}\n\tif rows != 0 {\n\t\tamt := rows * self.num_cols\n\t\tself.current_idx += amt\n\t\tself.current_idx = utils.Max(0, utils.Min(self.current_idx, len(self.codepoints)-1))\n\t\tself.layout_dirty = true\n\t}\n\tself.update_scroll_data()\n}\n"
  },
  {
    "path": "kitty/__init__.py",
    "content": ""
  },
  {
    "path": "kitty/actions.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport inspect\nfrom typing import NamedTuple, cast\n\nfrom .boss import Boss\nfrom .tabs import Tab\nfrom .types import ActionGroup, ActionSpec, run_once\nfrom .window import Window\n\n\nclass Action(NamedTuple):\n    name: str\n    group: ActionGroup\n    short_help: str\n    long_help: str\n\n\ngroups: dict[ActionGroup, str] = {\n    'cp': 'Copy/paste',\n    'sc': 'Scrolling',\n    'win': 'Window management',\n    'tab': 'Tab management',\n    'fs': 'Font sizes',\n    'mouse': 'Mouse actions',\n    'mk': 'Marks',\n    'lay': 'Layouts',\n    'misc': 'Miscellaneous',\n    'debug': 'Debugging',\n    'session': 'Sessions',\n}\ngroup_title = groups.__getitem__\n\n\n@run_once\ndef get_all_actions() -> dict[ActionGroup, list[Action]]:\n    ' test docstring '\n\n    ans: dict[ActionGroup, list[Action]] = {}\n\n    def is_action(x: object) -> bool:\n        return isinstance(getattr(x, 'action_spec', None), ActionSpec)\n\n    def as_action(x: object) -> Action:\n        spec: ActionSpec = getattr(x, 'action_spec')\n        doc = inspect.cleandoc(spec.doc)\n        lines = doc.splitlines()\n        first = lines.pop(0)\n        short_help = first\n        long_help = '\\n'.join(lines).strip()\n        assert spec.group in groups\n        return Action(getattr(x, '__name__'), cast(ActionGroup, spec.group), short_help, long_help)\n\n    seen = set()\n    for cls in (Window, Tab, Boss):\n        for (name, func) in inspect.getmembers(cls, is_action):\n            ac = as_action(func)\n            if ac.name not in seen:\n                ans.setdefault(ac.group, []).append(ac)\n                seen.add(ac.name)\n\n    ans['misc'].append(Action('no_op', 'misc', 'Unbind a shortcut',\n                              'Mapping a shortcut to no_op causes kitty to not intercept the key stroke anymore,'\n                              ' instead passing it to the program running inside it.'))\n    return ans\n\n\ndef dump() -> None:\n    from pprint import pprint\n    pprint(get_all_actions())\n\n\ndef as_rst() -> str:\n    from .conf.types import Mapping\n    from .options.definition import definition\n    allg = get_all_actions()\n    lines: list[str] = []\n    a = lines.append\n    maps: dict[str, list[Mapping]] = {}\n    for m in definition.iter_all_maps():\n        if m.documented:\n            func = m.action_def.split()[0]\n            maps.setdefault(func, []).append(m)\n\n    def key(x: ActionGroup) -> str:\n        return group_title(x).lower()\n\n    def kitten_link(text: str) -> str:\n        x = text.split()\n        return f':doc:`kittens/{x[2]}`' if len(x) > 2 else ''\n\n    for group in sorted(allg, key=key):\n        title = group_title(group)\n        a('')\n        a(f'.. _action-group-{group}:')\n        a('')\n        a(title)\n        a('-' * len(title))\n        a('')\n\n        for action in allg[group]:\n            a('')\n            a(f'.. action:: {action.name}')\n            a('')\n            a(action.short_help)\n            a('')\n            if action.long_help:\n                a(action.long_help)\n            if action.name in maps:\n                a('')\n                a('Default shortcuts using this action:')\n                if action.name == 'kitten':\n                    a('')\n                    scs = {(kitten_link(m.parseable_text), m.short_text, f':sc:`kitty.{m.name}`') for m in maps[action.name]}\n                    for s in sorted(scs):\n                        a(f'- {s[0]} - {s[2]} {s[1]}')\n                else:\n                    sscs = {f':sc:`kitty.{m.name}`' for m in maps[action.name]}\n                    a(', '.join(sorted(sscs)))\n    return '\\n'.join(lines)\n"
  },
  {
    "path": "kitty/alpha_blend.glsl",
    "content": "vec4 alpha_blend(vec4 over, vec4 under) {\n    // Alpha blend two colors returning the resulting color pre-multiplied by its alpha\n    // and its alpha.\n    // See https://en.wikipedia.org/wiki/Alpha_compositing\n    float alpha = mix(under.a, 1.0f, over.a);\n    vec3 combined_color = mix(under.rgb * under.a, over.rgb, over.a);\n    return vec4(combined_color, alpha);\n}\n\nvec4 alpha_blend_premul(vec4 over, vec4 under) {\n    // Same as alpha_blend() except that it assumes over and under are both premultiplied.\n    float inv_over_alpha = 1.0f - over.a;\n    float alpha = over.a + under.a * inv_over_alpha;\n    return vec4(over.rgb + under.rgb * inv_over_alpha, alpha);\n}\n\nvec4 alpha_blend_premul(vec4 over, vec3 under) {\n    // same as alpha_blend_premul with under_alpha = 1 outputs a blended color\n    // with alpha 1 which is effectively pre-multiplied since alpha is 1\n    float inv_over_alpha = 1.0f - over.a;\n    return vec4(over.rgb + under.rgb * inv_over_alpha, 1.0);\n}\n"
  },
  {
    "path": "kitty/animation.c",
    "content": "/*\n * animation.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#define ANIMATION_INTERNAL_API\n\ntypedef struct LinearParameters {\n    size_t count;\n    double buf[];\n} LinearParameters;\n\ntypedef struct StepsParameters {\n    size_t num_of_buckets;\n    double jump_size, start_value;\n} StepsParameters;\n\nstatic const double bezier_epsilon = 1e-7;\nstatic const unsigned max_newton_iterations = 4;\nstatic const unsigned max_bisection_iterations = 16;\n\ntypedef struct BezierParameters {\n    double ax, bx, cx, ay, by, cy, start_gradient, end_gradient, spline_samples[11];\n} BezierParameters;\n\ntypedef double(*easing_curve)(void*, double, monotonic_t);\n\ntypedef struct animation_function {\n    void *params;\n    easing_curve curve;\n    double y_at_start, y_size;\n} animation_function;\n\n\ntypedef struct Animation {\n    animation_function *functions;\n    size_t count, capacity;\n} Animation;\n\n\n#include \"animation.h\"\n#include \"state.h\"\n\nAnimation*\nalloc_animation(void) {\n    return calloc(1, sizeof(Animation));\n}\n\nbool\nanimation_is_valid(const Animation* a) { return a != NULL && a->count > 0; }\n\nAnimation*\nfree_animation(Animation *a) {\n    if (a) {\n        for (size_t i = 0; i < a->count; i++) free(a->functions[i].params);\n        free(a->functions);\n        free(a);\n    }\n    return NULL;\n}\n\n\nstatic double\nunit_value(double x) { return MAX(0., MIN(x, 1.)); }\n\nstatic double\nlinear_easing_curve(void *p_, double val, monotonic_t duration UNUSED) {\n    LinearParameters *p = p_;\n    double start_pos = 0, stop_pos = 1, start_val = 0, stop_val = 1;\n    double *x = p->buf, *y = p->buf + p->count;\n    for (size_t i = 0; i < p->count; i++) {\n        if (x[i] >= val) {\n            stop_pos = x[i];\n            stop_val = y[i];\n            if (i > 0) {\n                start_val = y[i-1];\n                start_pos = x[i-1];\n            }\n            break;\n        }\n    }\n    if (stop_pos > start_pos) {\n        double frac = (val - start_pos) / (stop_pos - start_pos);\n        return start_val + frac * (stop_val - start_val);\n    }\n    return stop_val;\n}\n\n// Cubic Bezier {{{\nstatic double\nsample_curve_x(const BezierParameters *p, double t) {\n    // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.\n    return ((p->ax * t + p->bx) * t + p->cx) * t;\n}\n\nstatic double\nsample_curve_y(const BezierParameters *p, double t) {\n    return ((p->ay * t + p->by) * t + p->cy) * t;\n}\n\nstatic double\nsample_derivative_x(const BezierParameters *p, double t) {\n    return (3.0 * p->ax * t + 2.0 * p->bx) * t + p->cx;\n}\n\nstatic double\nsolve_curve_x(const BezierParameters *p, double x, double epsilon) {\n    // Given an x value, find a parametric value it came from.\n    double t0 = 0.0, t1 = 0.0, t2 = x, x2 = 0.0, d2 = 0.0;\n\n    // Linear interpolation of spline curve for initial guess.\n    static const size_t num_samples = arraysz(p->spline_samples);\n    double delta = 1.0 / (num_samples - 1);\n    for (size_t i = 1; i < num_samples; i++) {\n        if (x <= p->spline_samples[i]) {\n            t1 = delta * i;\n            t0 = t1 - delta;\n            t2 = t0 + (t1 - t0) * (x - p->spline_samples[i - 1]) / (p->spline_samples[i] - p->spline_samples[i - 1]);\n            break;\n        }\n    }\n\n    // Perform a few iterations of Newton's method -- normally very fast.\n    // See https://en.wikipedia.org/wiki/Newton%27s_method.\n    double newton_epsilon = MIN(bezier_epsilon, epsilon);\n    for (unsigned i = 0; i < max_newton_iterations; i++) {\n        x2 = sample_curve_x(p, t2) - x;\n        if (fabs(x2) < newton_epsilon) return t2;\n        d2 = sample_derivative_x(p, t2);\n        if (fabs(d2) < bezier_epsilon) break;\n        t2 = t2 - x2 / d2;\n    }\n    if (fabs(x2) < epsilon) return t2;\n\n    t0 = 0.0, t1 = 0.0, t2 = x, x2 = 0.0;\n    // Fall back to the bisection method for reliability.\n    unsigned iteration = 0;\n    while (t0 < t1 && iteration++ < max_bisection_iterations) {\n        x2 = sample_curve_x(p, t2);\n        if (fabs(x2 - x) < epsilon) return t2;\n        if (x > x2) t0 = t2;\n        else t1 = t2;\n        t2 = (t1 + t0) * .5;\n    }\n\n    // Failure.\n    return t2;\n}\n\nstatic double\nsolve_unit_bezier(const BezierParameters *p, double x, double epsilon) {\n    if (x < 0.0) return 0.0 + p->start_gradient * x;\n    if (x > 1.0) return 1.0 + p->end_gradient * (x - 1.0);\n    return sample_curve_y(p, solve_curve_x(p, x, epsilon));\n}\n\nstatic double\ncubic_bezier_easing_curve(void *p_, double t, monotonic_t duration) {\n    BezierParameters *p = p_;\n    // The longer the animation, the more precision we need\n    double epsilon = 1.0 / monotonic_t_to_ms(duration);\n    return fabs(solve_unit_bezier(p, t, epsilon));\n}\n// }}}\n\nstatic double\nstep_easing_curve(void *p_, double t, monotonic_t duration UNUSED) {\n    StepsParameters *p = p_;\n    size_t val_bucket = (size_t)(t * p->num_of_buckets);\n    return p->start_value + val_bucket * p->jump_size;\n}\n\nstatic double\nidentity_easing_curve(void *p_ UNUSED, double t, monotonic_t duration UNUSED) { return t; }\n\ndouble\napply_easing_curve(const Animation *a, double val, monotonic_t duration) {\n    val = unit_value(val);\n    if (!a->count) return val;\n    size_t idx = MIN((size_t)(val * a->count), a->count - 1);\n    animation_function *f = a->functions + idx;\n    double interval_size = 1. / a->count, interval_start = idx * interval_size;\n    double scaled_val = (val - interval_start) / interval_size;\n    double ans = f->curve(f->params, scaled_val, duration);\n    return f->y_at_start + unit_value(ans) * f->y_size;\n}\n\nstatic animation_function*\ninit_function(Animation *a, double y_at_start, double y_at_end, easing_curve curve) {\n    ensure_space_for(a, functions, animation_function, a->count + 1, capacity, 4, false);\n    animation_function *f = a->functions + a->count++;\n    zero_at_ptr(f);\n    f->y_at_start = y_at_start; f->y_size = y_at_end - y_at_start; f->curve = curve;\n    return f;\n}\n\nstatic bool\nis_bezier_linear(double p1x, double p1y, double p2x, double p2y) {\n    // Is linear if all four points are on the same line. P0 and P4 are fixed at (0, 0) and (1, 1) for us.\n    return p1x == p1y && p2x == p2y;\n}\n\nvoid\nadd_cubic_bezier_animation(Animation *a, double y_at_start, double y_at_end, double p1x, double p1y, double p2x, double p2y) {\n    p1x = unit_value(p1x); p2x = unit_value(p2x);\n    if (is_bezier_linear(p1x, p1y, p2x, p2y)) {\n        init_function(a, y_at_start, y_at_end, identity_easing_curve);\n        return;\n    }\n    BezierParameters *p = calloc(1, sizeof(BezierParameters));\n    if (!p) fatal(\"Out of memory\");\n    // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).\n    p->cx = 3.0 * p1x;\n    p->bx = 3.0 * (p2x - p1x) - p->cx;\n    p->ax = 1.0 - p->cx - p->bx;\n\n    p->cy = 3.0 * p1y;\n    p->by = 3.0 * (p2y - p1y) - p->cy;\n    p->ay = 1.0 - p->cy - p->by;\n\n    // Calculate gradients used for values outside the unit interval\n    if (p1x > 0) p->start_gradient = p1y / p1x;\n    else if (p1y == 0 && p2x > 0) p->start_gradient = p2y / p2x;\n    else if (p1y == 0 && p2y == 0) p->start_gradient = 1;\n    else p->start_gradient = 0;\n\n    if (p2x < 1) p->end_gradient = (p2y - 1) / (p2x - 1);\n    else if (p2y == 1 && p1x < 1) p->end_gradient = (p1y - 1) / (p1x - 1);\n    else if (p2y == 1 && p1y == 1) p->end_gradient = 1;\n    else p->end_gradient = 0;\n\n    size_t num_samples = arraysz(p->spline_samples);\n    double delta = 1. / num_samples;\n    for (size_t i = 0; i < num_samples; i++) p->spline_samples[i] = sample_curve_x(p, i * delta);\n    animation_function *f = init_function(a, y_at_start, y_at_end, cubic_bezier_easing_curve);\n    f->params = p;\n}\n\nvoid\nadd_linear_animation(Animation *a, double y_at_start, double y_at_end, size_t count, const double *x, const double *y) {\n    const size_t sz = count * sizeof(double);\n    LinearParameters *p = calloc(1, sizeof(LinearParameters) + 2 * sz);\n    if (!p) fatal(\"Out of memory\");\n    p->count = count;\n    double *px = p->buf, *py = px + count;\n    memcpy(px, x, sz); memcpy(py, y, sz);\n    animation_function *f = init_function(a, y_at_start, y_at_end, linear_easing_curve);\n    f->params = p;\n}\n\nvoid\nadd_steps_animation(Animation *a, double y_at_start, double y_at_end, size_t count, EasingStep step) {\n    double jump_size = 1. / count, start_value = 0.;\n    size_t num_of_buckets = count;\n    switch (step) {\n        case EASING_STEP_START: start_value = jump_size; break;\n        case EASING_STEP_END: break;\n        case EASING_STEP_NONE:\n            jump_size = 1. / (num_of_buckets - 1);\n            break;\n        case EASING_STEP_BOTH:\n            num_of_buckets++;\n            jump_size = 1. / num_of_buckets;\n            start_value = jump_size;\n            break;\n    }\n    StepsParameters *p = malloc(sizeof(StepsParameters));\n    if (!p) fatal(\"Out of memory\");\n    p->num_of_buckets = num_of_buckets; p->jump_size = jump_size; p->start_value = start_value;\n    animation_function *f = init_function(a, y_at_start, y_at_end, step_easing_curve);\n    f->params = p;\n}\n\nstatic PyObject*\ntest_cursor_blink_easing_function(PyObject *self UNUSED, PyObject *args) {\n    Animation *a = OPT(animation.cursor);\n    if (!animation_is_valid(a)) {\n        PyErr_SetString(PyExc_RuntimeError, \"must set a cursor blink animation on the global options object first\");\n        return NULL;\n    }\n    double t, duration_s = 0.5; int only_single = 1;\n    if (!PyArg_ParseTuple(args, \"d|pd\", &t, &only_single, &duration_s)) return NULL;\n    monotonic_t duration = s_double_to_monotonic_t(duration_s);\n    if (only_single) {\n        animation_function f = a->functions[0];\n        return PyFloat_FromDouble(f.curve(f.params, t, duration));\n    }\n    return PyFloat_FromDouble(apply_easing_curve(a, t, duration));\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(test_cursor_blink_easing_function, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\n\nbool init_animations(PyObject *module) {\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    return true;\n}\n"
  },
  {
    "path": "kitty/animation.h",
    "content": "/*\n * animation.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stddef.h>\n#include <stdbool.h>\n#include \"monotonic.h\"\n\ntypedef enum { EASING_STEP_START, EASING_STEP_END, EASING_STEP_NONE, EASING_STEP_BOTH } EasingStep;\n#ifndef ANIMATION_INTERNAL_API\ntypedef struct {int x;} *Animation;\n#endif\n#define EASE_IN_OUT 0.42, 0, 0.58, 1\n#define ANIMATION_SAMPLE_WAIT (50 * MONOTONIC_T_1e6)\nAnimation* alloc_animation(void);\ndouble apply_easing_curve(const Animation *a, double t /* must be between 0 and 1*/, monotonic_t duration);\nbool animation_is_valid(const Animation *a);\nvoid add_cubic_bezier_animation(Animation *a, double y_at_start, double y_at_end, double p1_x, double p1_y, double p2_x, double p2_y);\nvoid add_linear_animation(Animation *a, double y_at_start, double y_at_end, size_t count, const double *x, const double *y);\nvoid add_steps_animation(Animation *a, double y_at_start, double y_at_end, size_t count, EasingStep step);\nAnimation* free_animation(Animation *a);\n"
  },
  {
    "path": "kitty/arches.h",
    "content": "/*\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n\n#ifdef __aarch64__\n#define KITTY_TARGET_CPU_IS_ARM64\n#define KITTY_128BIT_ALLOWED\n#define KITTY_256BIT_ALLOWED\n#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)\n#define KITTY_TARGET_CPU_IS_X86\n#define KITTY_128BIT_ALLOWED\n#elif defined(__amd64__)\n#define KITTY_TARGET_CPU_IS_AMD64\n#define KITTY_128BIT_ALLOWED\n#define KITTY_256BIT_ALLOWED\n#endif\n\n#if defined(__clang__) && defined(KITTY_128BIT_ALLOWED)\n#define KITTY_START_128BIT_CODE\n#elif defined(KITTY_128BIT_ALLOWED)\n#define KITTY_START_128BIT_CODE\n#else\n#define KITTY_START_128BIT_CODE\n#endif\n"
  },
  {
    "path": "kitty/arena.h",
    "content": "/*\n * arena.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n\n#ifndef MA_NAME\n#error \"Must define MA_NAME\"\n#endif\n\n#ifndef MA_BLOCK_SIZE\n#define MA_BLOCK_SIZE 1u\n#endif\n\n#define MA_CAT_( a, b ) a##b\n#define MA_CAT( a, b ) MA_CAT_( a, b )\n\n#ifndef MA_ARENA_NUM_BLOCKS\n#define MA_ARENA_NUM_BLOCKS 4096u\n#endif\n\n#define MA_TYPE_NAME MA_CAT(MA_NAME, MonotonicArena)\n#define MA_BLOCK_TYPE_NAME MA_CAT(MA_NAME, MonotonicArenaBlock)\n\ntypedef struct MA_BLOCK_TYPE_NAME {\n    void *buf; size_t used, capacity;\n} MA_BLOCK_TYPE_NAME;\n\ntypedef struct MA_TYPE_NAME {\n    MA_BLOCK_TYPE_NAME *blocks;\n    size_t count, capacity;\n} MA_TYPE_NAME;\n\nstatic inline void\nMA_CAT(MA_NAME, _free_all)(MA_TYPE_NAME *self) {\n    for (size_t i = 0; i < self->count; i++) free(self->blocks[i].buf);\n    free(self->blocks);\n    zero_at_ptr(self);\n}\n\nstatic inline void*\nMA_CAT(MA_NAME, _get)(MA_TYPE_NAME *self, size_t sz) {\n    size_t required_size = (sz / MA_BLOCK_SIZE) * MA_BLOCK_SIZE;\n    if (required_size < sz) required_size += MA_BLOCK_SIZE;\n    if (!self->count || (self->blocks[self->count-1].capacity - self->blocks[self->count-1].used) < required_size) {\n        size_t count = self->count + 1;\n        size_t block_sz = MAX(required_size, MA_ARENA_NUM_BLOCKS * MA_BLOCK_SIZE);\n        void *chunk = NULL;\n        if (MA_BLOCK_SIZE >= sizeof(void*) && MA_BLOCK_SIZE % sizeof(void*) == 0) {\n            if (posix_memalign(&chunk, MA_BLOCK_SIZE, block_sz) != 0) chunk = NULL;\n            memset(chunk, 0, block_sz);\n        } else chunk = calloc(1, block_sz);\n        if (!chunk) { return NULL; }\n        if (count > self->capacity) {\n            size_t capacity = MAX(8u, 2 * self->capacity);\n            MA_BLOCK_TYPE_NAME *blocks = realloc(self->blocks, capacity * sizeof(MA_BLOCK_TYPE_NAME));\n            if (!blocks) { free(chunk); return NULL; }\n            self->capacity = capacity; self->blocks = blocks;\n        }\n        self->blocks[count - 1] = (MA_BLOCK_TYPE_NAME){.capacity=block_sz, .buf=chunk};\n        self->count = count;\n    }\n    char *ans = (char*)self->blocks[self->count-1].buf + self->blocks[self->count-1].used;\n    self->blocks[self->count-1].used += required_size;\n    return ans;\n}\n\n#undef MA_NAME\n#undef MA_BLOCK_SIZE\n#undef MA_ARENA_NUM_BLOCKS\n#undef MA_TYPE_NAME\n#undef MA_BLOCK_TYPE_NAME\n#undef MA_CAT\n#undef MA_CAT_\n"
  },
  {
    "path": "kitty/backtrace.h",
    "content": "/*\n * Copyright (C) 2022 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stdio.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#if __has_include(<execinfo.h>)\n#include <execinfo.h>\n\nstatic inline void\nprint_stack_trace(void) {\n    void *array[256];\n    size_t size;\n\n    // get void*'s for all entries on the stack\n    size = backtrace(array, 256);\n\n    // print out all the frames to stderr\n    backtrace_symbols_fd(array, size, STDERR_FILENO);\n}\n#else\nstatic inline void\nprint_stack_trace(void) {\n    fprintf(stderr, \"Stack trace functionality not available.\\n\");\n}\n#endif\n"
  },
  {
    "path": "kitty/banned.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n\n#define BANNED(func) sorry_##func##_is_a_banned_function\n\n#undef strcpy\n#define strcpy(x,y) BANNED(strcpy)\n#undef strcat\n#define strcat(x,y) BANNED(strcat)\n#undef strncpy\n#define strncpy(x,y,n) BANNED(strncpy)\n#undef strncat\n#define strncat(x,y,n) BANNED(strncat)\n\n#undef sprintf\n#undef vsprintf\n#ifdef HAVE_VARIADIC_MACROS\n#define sprintf(...) BANNED(sprintf)\n#define vsprintf(...) BANNED(vsprintf)\n#else\n#define sprintf(buf,fmt,arg) BANNED(sprintf)\n#define vsprintf(buf,fmt,arg) BANNED(vsprintf)\n#endif\n\n#undef gmtime\n#define gmtime(t) BANNED(gmtime)\n#undef localtime\n#define localtime(t) BANNED(localtime)\n#undef ctime\n#define ctime(t) BANNED(ctime)\n#undef ctime_r\n#define ctime_r(t, buf) BANNED(ctime_r)\n#undef asctime\n#define asctime(t) BANNED(asctime)\n#undef asctime_r\n#define asctime_r(t, buf) BANNED(asctime_r)\n"
  },
  {
    "path": "kitty/base64.h",
    "content": "/*\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include <stdint.h>\n#include <stddef.h>\n#include <stdbool.h>\n#include \"../3rdparty/base64/include/libbase64.h\"\n\nstatic inline size_t required_buffer_size_for_base64_decode(size_t src_sz) { return (src_sz / 4 * 3 + 2); }\nstatic inline size_t required_buffer_size_for_base64_encode(size_t src_sz) { return ((src_sz + 2) / 3 * 4); }\n\n\nstatic inline bool\nbase64_decode8(const uint8_t *src, size_t src_sz, uint8_t *dest, size_t *dest_sz) {\n    if (*dest_sz < required_buffer_size_for_base64_decode(src_sz)) return false;\n    // we ignore the return value of base64_decode as it returns non-zero when it is\n    // waiting for padding bytes\n    base64_decode((const char*)src, src_sz, (char*)dest, dest_sz, 0);\n    return true;\n}\n\nstatic inline bool\nbase64_encode8(const unsigned char *src, size_t src_len, unsigned char *out, size_t *out_len, bool add_padding) {\n    if (*out_len < required_buffer_size_for_base64_encode(src_len)) return false;\n    base64_encode((const char*)src, src_len, (char*)out, out_len, 0);\n    if (!add_padding) {\n        while(*out_len && out[*out_len - 1] == '=') *out_len -= 1;\n    }\n    return true;\n}\n"
  },
  {
    "path": "kitty/bash.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom .utils import shlex_split\n\n\ndef decode_ansi_c_quoted_string(text: str) -> str:\n    return next(shlex_split(text, True))\n\n\ndef decode_double_quoted_string(text: str, pos: int) -> tuple[str, int]:\n    escapes = r'\"\\$`'\n    buf: list[str] = []\n    a = buf.append\n    while pos < len(text):\n        ch = text[pos]\n        pos += 1\n        if ch == '\\\\':\n            if text[pos] in escapes:\n                a(text[pos])\n                pos += 1\n                continue\n            a(ch)\n        elif ch == '\"':\n            break\n        else:\n            a(ch)\n    return ''.join(buf), pos\n\n\ndef parse_modern_bash_env(text: str) -> dict[str, str]:\n    ans = {}\n    for line in text.splitlines():\n        idx = line.find('=')\n        if idx < 0:\n            break\n        key = line[:idx].rpartition(' ')[2]\n        val = line[idx+1:]\n        if val.startswith('\"'):\n            val = decode_double_quoted_string(val, 1)[0]\n        else:\n            val = decode_ansi_c_quoted_string(val)\n        ans[key] = val\n    return ans\n\n\ndef parse_bash_env(text: str, bash_version: str) -> dict[str, str]:\n    # See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html\n    parts = bash_version.split('.')\n    bv = tuple(map(int, parts[:2]))\n    if bv >= (5, 2):\n        return parse_modern_bash_env(text)\n    ans = {}\n    pos = 0\n    while pos < len(text):\n        idx = text.find('=\"', pos)\n        if idx < 0:\n            break\n        i = text.rfind(' ', 0, idx)\n        if i < 0:\n            break\n        key = text[i+1:idx]\n        pos = idx + 2\n        ans[key], pos = decode_double_quoted_string(text, pos)\n    return ans\n"
  },
  {
    "path": "kitty/bgimage_fragment.glsl",
    "content": "#pragma kitty_include_shader <alpha_blend.glsl>\n\nuniform sampler2D image;\nuniform vec4 background;\nin vec2 texcoord;\nout vec4 premult_color;\n\nvoid main() {\n    vec4 color = texture(image, texcoord);\n    premult_color = alpha_blend(color, background);\n}\n"
  },
  {
    "path": "kitty/bgimage_vertex.glsl",
    "content": "#define left  0\n#define top  1\n#define right  2\n#define bottom  3\n#define tex_left 0\n#define tex_top 0\n#define tex_right 1\n#define tex_bottom 1\n\nuniform float tiled;\nuniform vec4 sizes;  // [ window_width, window_height, image_width, image_height ]\nuniform vec4 positions;  // [ left, top, right, bottom ]\n\nout vec2 texcoord;\n\nconst vec2 tex_map[] = vec2[4](\n    vec2(tex_left, tex_top),\n    vec2(tex_left, tex_bottom),\n    vec2(tex_right, tex_bottom),\n    vec2(tex_right, tex_top)\n);\n\nfloat scale_factor(float window_size, float image_size) {\n    return window_size / image_size;\n}\n\nfloat tiling_factor(int i) {\n#define window i\n#define image i + 2\n    return tiled * scale_factor(sizes[window], sizes[image]) + (1 - tiled);\n}\n\nvoid main() {\n    vec2 pos_map[] = vec2[4](\n        vec2(positions[left], positions[top]),\n        vec2(positions[left], positions[bottom]),\n        vec2(positions[right], positions[bottom]),\n        vec2(positions[right], positions[top])\n    );\n    vec2 tex_coords = tex_map[gl_VertexID];\n#define x_axis 0\n#define y_axis 1\n    texcoord = vec2(\n        tex_coords[x_axis] * tiling_factor(x_axis),\n        tex_coords[y_axis] * tiling_factor(y_axis)\n    );\n    gl_Position = vec4(pos_map[gl_VertexID], 0, 1);\n}\n"
  },
  {
    "path": "kitty/binary.h",
    "content": "/*\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stdint.h>\n\nstatic inline uint16_t\nbe16dec(const void *pp) {\n    uint8_t const *p = (uint8_t const *)pp;\n    return (((unsigned)p[0] << 8) | p[1]);\n}\n\nstatic inline uint32_t\nbe32dec(const void *pp) {\n    uint8_t const *p = (uint8_t const *)pp;\n    return (((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) |\n            ((uint32_t)p[2] << 8) | p[3]);\n}\n\nstatic inline uint64_t\nbe64dec(const void *pp) {\n    uint8_t const *p = (uint8_t const *)pp;\n    return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4));\n}\n\nstatic inline uint16_t\nle16dec(const void *pp) {\n    uint8_t const *p = (uint8_t const *)pp;\n    return (((unsigned)p[1] << 8) | p[0]);\n}\n\nstatic inline uint32_t\nle32dec(const void *pp) {\n    uint8_t const *p = (uint8_t const *)pp;\n    return (((uint32_t)p[3] << 24) | ((uint32_t)p[2] << 16) |\n            ((uint32_t)p[1] << 8) | p[0]);\n}\n\nstatic inline uint64_t\nle64dec(const void *pp) {\n    uint8_t const *p = (uint8_t const *)pp;\n    return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p));\n}\n\nstatic inline void\nbe16enc(void *pp, uint16_t u) {\n    uint8_t *p = (uint8_t *)pp;\n    p[0] = (u >> 8) & 0xff;\n    p[1] = u & 0xff;\n}\n\nstatic inline void\nbe32enc(void *pp, uint32_t u) {\n    uint8_t *p = (uint8_t *)pp;\n    p[0] = (u >> 24) & 0xff;\n    p[1] = (u >> 16) & 0xff;\n    p[2] = (u >> 8) & 0xff;\n    p[3] = u & 0xff;\n}\n\nstatic inline void\nbe64enc(void *pp, uint64_t u) {\n    uint8_t *p = (uint8_t *)pp;\n    be32enc(p, (uint32_t)(u >> 32));\n    be32enc(p + 4, (uint32_t)(u & 0xffffffffU));\n}\n\nstatic inline void\nle16enc(void *pp, uint16_t u) {\n    uint8_t *p = (uint8_t *)pp;\n    p[0] = u & 0xff;\n    p[1] = (u >> 8) & 0xff;\n}\n\nstatic inline void\nle32enc(void *pp, uint32_t u) {\n    uint8_t *p = (uint8_t *)pp;\n    p[0] = u & 0xff;\n    p[1] = (u >> 8) & 0xff;\n    p[2] = (u >> 16) & 0xff;\n    p[3] = (u >> 24) & 0xff;\n}\n\nstatic inline void\nle64enc(void *pp, uint64_t u) {\n    uint8_t *p = (uint8_t *)pp;\n    le32enc(p, (uint32_t)(u & 0xffffffffU));\n    le32enc(p + 4, (uint32_t)(u >> 32));\n}\n"
  },
  {
    "path": "kitty/blit_common.glsl",
    "content": "out vec2 texcoord;\n\n#define left 0\n#define top 1\n#define right 2\n#define bottom 3\n\nconst ivec2 vertex_pos_map[4] = ivec2[4](\n    ivec2(right, top),\n    ivec2(right, bottom),\n    ivec2(left, bottom),\n    ivec2(left, top)\n);\n\nvoid main() {\n    ivec2 pos = vertex_pos_map[gl_VertexID];\n    texcoord = vec2(src_rect[pos.x], src_rect[pos.y]);\n    gl_Position = vec4(dest_rect[pos.x], dest_rect[pos.y], 0, 1);\n}\n"
  },
  {
    "path": "kitty/blit_fragment.glsl",
    "content": "#pragma kitty_include_shader <alpha_blend.glsl>\n#pragma kitty_include_shader <utils.glsl>\n#pragma kitty_include_shader <linear2srgb.glsl>\n\nuniform sampler2D image;\n\nin vec2 texcoord;\nout vec4 output_color;\n\nvoid main() {\n    vec4 color_premul = texture(image, texcoord);\n    output_color = vec4_premul(linear2srgb(color_premul.rgb / color_premul.a), color_premul.a);\n}\n"
  },
  {
    "path": "kitty/blit_vertex.glsl",
    "content": "uniform vec4 src_rect, dest_rect;\n#pragma kitty_include_shader <blit_common.glsl>\n"
  },
  {
    "path": "kitty/border_fragment.glsl",
    "content": "in vec4 color_premul;\nout vec4 output_premul;\n\nvoid main() {\n    output_premul = color_premul;\n}\n"
  },
  {
    "path": "kitty/border_vertex.glsl",
    "content": "#pragma kitty_include_shader <utils.glsl>\n\n#define DEFAULT_BG 0\n#define ACTIVE_BORDER_COLOR 1\n#define INACTIVE_BORDER_COLOR 2\n#define WINDOW_BACKGROUND_PLACEHOLDER 3\n#define BELL_BORDER_COLOR 4\n#define TAB_BAR_BG_COLOR 5\n#define TAB_BAR_MARGIN_COLOR 6\n#define TAB_BAR_EDGE_LEFT_COLOR 7\n#define TAB_BAR_EDGE_RIGHT_COLOR 8\nuniform uint colors[9];\nuniform float background_opacity;\nuniform float gamma_lut[256];\n\nin vec4 rect;  // left, top, right, bottom\nin uint rect_color;\nout vec4 color_premul;\n\n// indices into the rect vector\nconst int LEFT = 0;\nconst int TOP = 1;\nconst int RIGHT = 2;\nconst int BOTTOM = 3;\nconst uint FF = uint(0xff);\n\nconst uvec2 pos_map[] = uvec2[4](\n    uvec2(RIGHT, TOP),\n    uvec2(RIGHT, BOTTOM),\n    uvec2(LEFT, BOTTOM),\n    uvec2(LEFT, TOP)\n);\n\nfloat to_color(uint c) {\n    return gamma_lut[c & FF];\n}\n\nfloat is_integer_value(uint c, int x) {\n    return 1. - step(0.5, abs(float(c) - float(x)));\n}\n\nvec3 as_color_vector(uint c, int shift) {\n    return vec3(to_color(c >> shift), to_color(c >> (shift - 8)), to_color(c >> (shift - 16)));\n}\n\nvoid main() {\n    uvec2 pos = pos_map[gl_VertexID];\n    gl_Position = vec4(rect[pos.x], rect[pos.y], 0, 1);\n    vec3 window_bg = as_color_vector(rect_color, 24);\n    uint rc = rect_color & FF;\n    vec3 color3 = as_color_vector(colors[rc], 16);\n    float is_window_bg = is_integer_value(rc, WINDOW_BACKGROUND_PLACEHOLDER); // used by window padding areas\n    float is_default_bg = is_integer_value(rc, DEFAULT_BG);\n    color3 = if_one_then(is_window_bg, window_bg, color3);\n    // Actual border quads must be always drawn opaque\n    float is_not_a_border = zero_or_one(abs(\n        (float(rc) - ACTIVE_BORDER_COLOR) * (float(rc) - INACTIVE_BORDER_COLOR) * (float(rc) - BELL_BORDER_COLOR)\n    ));\n    float final_opacity = if_one_then(is_not_a_border, background_opacity, 1.);\n    color_premul = vec4_premul(color3, final_opacity);\n}\n"
  },
  {
    "path": "kitty/borders.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Iterable\nfrom enum import IntFlag\nfrom functools import partial\nfrom typing import NamedTuple\n\nfrom .fast_data_types import BORDERS_PROGRAM, current_focused_os_window_id, get_options, init_borders_program, set_borders_rects\nfrom .shaders import program_for\nfrom .typing_compat import LayoutType\nfrom .utils import color_as_int\nfrom .window_list import WindowGroup, WindowList\n\n\nclass BorderColor(IntFlag):\n    # These are indices into the array of colors in the border vertex shader\n    default_bg, active, inactive, window_bg, bell, tab_bar_bg, tab_bar_margin_color, tab_bar_left_edge_color, tab_bar_right_edge_color = range(9)\n\n\nclass Border(NamedTuple):\n    left: int\n    top: int\n    right: int\n    bottom: int\n    color: BorderColor\n    border_type: int = 0\n    horizontal: bool = False\n\n\ndef vertical_edge(rects: list[Border], color: BorderColor, width: int, top: int, bottom: int, left: int, border_type: int) -> None:\n    if width > 0:\n        rects.append(Border(left, top, left + width, bottom, color, border_type, False))\n\n\ndef horizontal_edge(rects: list[Border], color: BorderColor, height: int, left: int, right: int, top: int, border_type: int) -> None:\n    if height > 0:\n        rects.append(Border(left, top, right, top + height, color, border_type, True))\n\n\ndef add_borders(rects: list[Border], color: BorderColor, wg: WindowGroup) -> None:\n    geometry = wg.geometry\n    if geometry is None:\n        return\n    pl, pt = wg.effective_padding('left'), wg.effective_padding('top')\n    pr, pb = wg.effective_padding('right'), wg.effective_padding('bottom')\n    left = geometry.left - pl\n    top = geometry.top - pt\n    lr = geometry.right\n    right = lr + pr\n    bt = geometry.bottom\n    bottom = bt + pb\n    h = partial(horizontal_edge, rects, color)\n    v = partial(vertical_edge, rects, color)\n    width = wg.effective_border()\n    bt = bottom\n    lr = right\n    left -= width\n    top -= width\n    right += width\n    bottom += width\n    pl = pr = pb = pt = width\n    wid = wg.active_window_id\n    h(pt, left, right, top, -wid)\n    h(pb, left, right, bt, wid)\n    v(pl, top, bottom, left, -wid)\n    v(pr, top, bottom, lr, wid)\n\n\ndef load_borders_program() -> None:\n    program_for('border').compile(BORDERS_PROGRAM)\n    init_borders_program()\n\n\nclass Borders:\n\n    def __init__(self, os_window_id: int, tab_id: int):\n        self.os_window_id = os_window_id\n        self.tab_id = tab_id\n\n    def __call__(\n        self,\n        all_windows: WindowList,\n        current_layout: LayoutType,\n        tab_bar_rects: Iterable[Border],\n        draw_window_borders: bool = True,\n    ) -> None:\n        opts = get_options()\n        draw_active_borders = opts.active_border_color is not None\n        rects: list[Border] = []\n        for br in current_layout.blank_rects:\n            rects.append(Border(*br, BorderColor.default_bg))\n        rects.extend(tab_bar_rects)\n        bw = 0\n        groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))\n        if groups:\n            bw = groups[0].effective_border()\n        draw_borders = bw > 0 and draw_window_borders\n        active_group = all_windows.active_group\n\n        # Count visible windows\n        num_visible_groups = len(groups)\n\n        # When draw_window_borders_for_single_window is set and there's only 1 window,\n        # behave like draw_minimal_borders is False (draw full borders around the window)\n        if opts.draw_window_borders_for_single_window and num_visible_groups == 1:\n            draw_minimal_borders = False\n        else:\n            draw_minimal_borders = opts.draw_minimal_borders and max(opts.window_margin_width) < 1\n\n        # For single window with the option enabled, check OS window focus state\n        # When unfocused, the border should appear inactive\n        os_window_focused = True\n        if opts.draw_window_borders_for_single_window and num_visible_groups == 1:\n            os_window_focused = current_focused_os_window_id() == self.os_window_id\n\n        if draw_borders and not draw_minimal_borders:\n            for i, wg in enumerate(groups):\n                window_bg = color_as_int(wg.default_bg)\n                window_bg = (window_bg << 8) | BorderColor.window_bg\n                # Draw the border rectangles\n                if wg is active_group and draw_active_borders and os_window_focused:\n                    color = BorderColor.active\n                else:\n                    color = BorderColor.bell if wg.needs_attention else BorderColor.inactive\n                add_borders(rects, color, wg)\n\n        if draw_minimal_borders:\n            for border_line in current_layout.get_minimal_borders(all_windows):\n                rects.append(Border(*border_line.edges, border_line.color, border_line.window_id, border_line.horizontal))\n        set_borders_rects(self.os_window_id, self.tab_id, rects)\n"
  },
  {
    "path": "kitty/boss.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\n# Imports {{{\nimport base64\nimport json\nimport os\nimport re\nimport socket\nimport subprocess\nimport sys\nfrom collections.abc import Callable, Container, Generator, Iterable, Iterator, Sequence\nfrom contextlib import contextmanager, suppress\nfrom functools import partial\nfrom gettext import gettext as _\nfrom gettext import ngettext\nfrom math import floor\nfrom time import sleep\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Literal,\n    Optional,\n    Union,\n)\nfrom weakref import WeakValueDictionary\n\nfrom kitty.types import WindowResizeDrag\n\nfrom .child import cached_process_data, default_env, set_default_env\nfrom .cli import create_opts, green, parse_args\nfrom .cli_stub import CLIOptions, SaveAsSessionOptions\nfrom .clipboard import (\n    Clipboard,\n    ClipboardType,\n    get_clipboard_string,\n    get_primary_selection,\n    set_clipboard_string,\n    set_primary_selection,\n)\nfrom .colors import ColorSchemes, theme_colors\nfrom .conf.utils import BadLine, KeyAction, to_cmdline\nfrom .config import common_opts_as_dict, prepare_config_file_for_editing, store_effective_config\nfrom .constants import (\n    RC_ENCRYPTION_PROTOCOL_VERSION,\n    appname,\n    cache_dir,\n    clear_handled_signals,\n    config_dir,\n    handled_signals,\n    is_macos,\n    is_wayland,\n    kitten_exe,\n    kitty_exe,\n    logo_png_file,\n    supports_primary_selection,\n    website_url,\n)\nfrom .fast_data_types import (\n    BOTTOM_EDGE,\n    CLOSE_BEING_CONFIRMED,\n    GLFW_FKEY_ESCAPE,\n    GLFW_MOD_ALT,\n    GLFW_MOD_CONTROL,\n    GLFW_MOD_SHIFT,\n    GLFW_MOD_SUPER,\n    GLFW_MOUSE_BUTTON_LEFT,\n    GLFW_PRESS,\n    IMPERATIVE_CLOSE_REQUESTED,\n    LEFT_EDGE,\n    NO_CLOSE_REQUESTED,\n    RIGHT_EDGE,\n    TOP_EDGE,\n    ChildMonitor,\n    Color,\n    EllipticCurveKey,\n    KeyEvent,\n    SingleKey,\n    add_timer,\n    apply_options_update,\n    background_opacity_of,\n    change_background_opacity,\n    change_drag_thumbnail,\n    cocoa_hide_app,\n    cocoa_hide_other_apps,\n    cocoa_minimize_os_window,\n    cocoa_set_menubar_title,\n    create_os_window,\n    current_application_quit_request,\n    current_focused_os_window_id,\n    current_os_window,\n    destroy_global_data,\n    focus_os_window,\n    get_boss,\n    get_options,\n    get_os_window_size,\n    get_tab_being_dragged,\n    glfw_get_monitor_workarea,\n    global_font_size,\n    grab_keyboard,\n    is_layer_shell_supported,\n    last_focused_os_window_id,\n    load_png_data,\n    macos_cycle_through_os_windows,\n    mark_os_window_for_close,\n    monitor_pid,\n    monotonic,\n    os_window_focus_counters,\n    os_window_font_size,\n    redirect_mouse_handling,\n    ring_bell,\n    run_with_activation_token,\n    safe_pipe,\n    send_data_to_peer,\n    set_application_quit_request,\n    set_background_image,\n    set_boss,\n    set_options,\n    set_os_window_chrome,\n    set_os_window_size,\n    set_os_window_title,\n    set_tab_being_dragged,\n    start_drag_with_data,\n    thread_write,\n    toggle_fullscreen,\n    toggle_maximized,\n    toggle_os_window_visibility,\n    toggle_secure_input,\n    viewport_for_window,\n    wrapped_kitten_names,\n)\nfrom .key_encoding import get_name_to_functional_number_map\nfrom .keys import Mappings\nfrom .layout.base import set_layout_options\nfrom .notifications import NotificationManager\nfrom .options.types import Options, nullable_colors\nfrom .options.utils import MINIMUM_FONT_SIZE, KeyboardMode, KeyDefinition\nfrom .os_window_size import initial_window_size_func\nfrom .session import (\n    Session,\n    close_session_with_confirm,\n    create_sessions,\n    default_save_as_session_opts,\n    get_os_window_sizing_data,\n    goto_session,\n    most_recent_session,\n    save_as_session,\n)\nfrom .shaders import load_shader_programs\nfrom .simple_cli_definitions import grab_keyboard_docs\nfrom .tabs import SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager\nfrom .types import _T, AsyncResponse, LayerShellConfig, SingleInstanceData, WindowSystemMouseEvent, ac\nfrom .typing_compat import PopenType, TypedDict\nfrom .utils import (\n    cleanup_ssh_control_masters,\n    func_name,\n    get_editor,\n    get_new_os_window_size,\n    is_ok_to_read_image_file,\n    is_path_in_temp_dir,\n    less_version,\n    log_error,\n    macos_version,\n    open_url,\n    parse_address_spec,\n    parse_os_window_state,\n    platform_window_id,\n    resolve_custom_file,\n    safe_print,\n    sanitize_url_for_display_to_user,\n    shlex_split,\n    startup_notification_handler,\n    timed_debug_print,\n    which,\n)\nfrom .window import CommandOutput, CwdRequest, Window, global_watchers\n\nif TYPE_CHECKING:\n\n    from .fast_data_types import OSWindowSize\n    from .rc.base import ResponseType\n# }}}\n\nRCResponse = Union[dict[str, Any], None, AsyncResponse]\n\n\nclass OSWindowDict(TypedDict):\n    id: int\n    platform_window_id: int | None\n    is_focused: bool\n    is_active: bool\n    last_focused: bool\n    tabs: list[TabDict]\n    active_tab_history: tuple[int, ...]\n    wm_class: str\n    wm_name: str\n    background_opacity: float\n\n\nclass Atexit:\n\n    def __init__(self) -> None:\n        self.worker: subprocess.Popen[bytes] | None = None\n\n    def _write_line(self, line: str) -> None:\n        if '\\n' in line:\n            raise ValueError('Newlines not allowed in atexit arguments: {path!r}')\n        w = self.worker\n        if w is None:\n            w = self.worker = subprocess.Popen([kitten_exe(), '__atexit__'], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, close_fds=True)\n            assert w.stdin is not None\n            os.set_inheritable(w.stdin.fileno(), False)\n        assert w.stdin is not None\n        w.stdin.write((line + '\\n').encode())\n        w.stdin.flush()\n\n    def unlink(self, path: str) -> None:\n        self._write_line(f'unlink {path}')\n\n    def shm_unlink(self, path: str) -> None:\n        self._write_line(f'shm_unlink {path}')\n\n    def rmtree(self, path: str) -> None:\n        self._write_line(f'rmtree {path}')\n\n\ndef listen_on(spec: str, robust_atexit: Atexit) -> tuple[int, str]:\n    import socket\n    family, address, socket_path = parse_address_spec(spec)\n    s = socket.socket(family)\n    s.bind(address)\n    if family == socket.AF_UNIX and socket_path:\n        robust_atexit.unlink(socket_path)\n    s.listen()\n    if isinstance(address, tuple):  # tcp socket\n        h, resolved_port = s.getsockname()[:2]\n        spec = spec.rpartition(':')[0] + f':{resolved_port}'\n    import atexit\n    atexit.register(s.close)  # prevents s from being garbage collected\n    return s.fileno(), spec\n\n\ndef data_for_at(w: Window | None, arg: str, add_wrap_markers: bool = False) -> str | None:\n    if not w:\n        return None\n\n    def as_text(**kw: bool) -> str:\n        kw['add_wrap_markers'] = add_wrap_markers\n        return w.as_text(**kw) if w else ''\n\n    if arg == '@selection':\n        return w.text_for_selection()\n    if arg in ('@ansi', '@ansi_screen_scrollback'):\n        return as_text(as_ansi=True, add_history=True)\n    if arg in ('@text', '@screen_scrollback'):\n        return as_text(add_history=True)\n    if arg == '@screen':\n        return as_text()\n    if arg == '@ansi_screen':\n        return as_text(as_ansi=True)\n    if arg == '@alternate':\n        return as_text(alternate_screen=True)\n    if arg == '@alternate_scrollback':\n        return as_text(alternate_screen=True, add_history=True)\n    if arg == '@ansi_alternate':\n        return as_text(as_ansi=True, alternate_screen=True)\n    if arg == '@ansi_alternate_scrollback':\n        return as_text(as_ansi=True, alternate_screen=True, add_history=True)\n    if arg == '@first_cmd_output_on_screen':\n        return w.cmd_output(CommandOutput.first_on_screen, add_wrap_markers=add_wrap_markers)\n    if arg == '@ansi_first_cmd_output_on_screen':\n        return w.cmd_output(CommandOutput.first_on_screen, as_ansi=True, add_wrap_markers=add_wrap_markers)\n    if arg == '@last_cmd_output':\n        return w.cmd_output(CommandOutput.last_run, add_wrap_markers=add_wrap_markers)\n    if arg == '@ansi_last_cmd_output':\n        return w.cmd_output(CommandOutput.last_run, as_ansi=True, add_wrap_markers=add_wrap_markers)\n    if arg == '@last_visited_cmd_output':\n        return w.cmd_output(CommandOutput.last_visited, add_wrap_markers=add_wrap_markers)\n    if arg == '@ansi_last_visited_cmd_output':\n        return w.cmd_output(CommandOutput.last_visited, as_ansi=True, add_wrap_markers=add_wrap_markers)\n    return None\n\n\nclass DumpCommands:  # {{{\n\n    def __init__(self, args: CLIOptions):\n        self.draw_dump_buf: list[str] = []\n        self.dump_commands = args.dump_commands\n        if args.dump_bytes:\n            self.dump_bytes_to = open(args.dump_bytes, 'wb')\n\n    def __call__(self, window_id: int, what: str, *a: Any) -> None:\n        if what == 'draw':\n            if self.dump_commands:\n                self.draw_dump_buf.append(a[0])\n        elif what == 'bytes':\n            self.dump_bytes_to.write(a[0])\n            self.dump_bytes_to.flush()\n        elif what == 'error':\n            log_error(*a)\n        elif self.dump_commands:\n            if self.draw_dump_buf:\n                safe_print('draw', ''.join(self.draw_dump_buf))\n                self.draw_dump_buf = []\n            def fmt(x: Any) -> Any:\n                if isinstance(x, (bytes, memoryview)):\n                    return str(x, 'utf-8', 'replace')\n                if isinstance(x, dict):\n                    return json.dumps(x)\n                return x\n            safe_print(what, *map(fmt, a), flush=True)\n# }}}\n\n\nclass VisualSelect:\n\n    def __init__(\n        self,\n        tab_id: int,\n        os_window_id: int,\n        prev_tab_id: int | None,\n        prev_os_window_id: int | None,\n        title: str,\n        callback: Callable[[Tab | None, Window | None], None],\n        reactivate_prev_tab: bool\n    ) -> None:\n        self.tab_id = tab_id\n        self.os_window_id = os_window_id\n        self.prev_tab_id = prev_tab_id\n        self.prev_os_window_id = prev_os_window_id\n        self.callback = callback\n        self.window_ids: list[int] = []\n        self.window_used_for_selection_id = 0\n        self.reactivate_prev_tab = reactivate_prev_tab\n        set_os_window_title(self.os_window_id, title)\n\n    def cancel(self) -> None:\n        self.clear_global_state()\n        self.activate_prev_tab()\n        self.callback(None, None)\n\n    def trigger(self, window_id: int) -> None:\n        boss = self.clear_global_state()\n        self.activate_prev_tab()\n        w = boss.window_id_map.get(window_id)\n        if w is None:\n            self.callback(None, None)\n        else:\n            tab = w.tabref()\n            if tab is None:\n                self.callback(None, None)\n            else:\n                self.callback(tab, w)\n\n    def clear_global_state(self) -> 'Boss':\n        set_os_window_title(self.os_window_id, '')\n        boss = get_boss()\n        redirect_mouse_handling(False)\n        for wid in self.window_ids:\n            w = boss.window_id_map.get(wid)\n            if w is not None:\n                w.screen.set_window_char()\n        if self.window_used_for_selection_id:\n            w = boss.window_id_map.get(self.window_used_for_selection_id)\n            if w is not None:\n                boss.mark_window_for_close(w)\n        return boss\n\n    def activate_prev_tab(self) -> None:\n        if not self.reactivate_prev_tab or self.prev_tab_id is None:\n            return None\n        boss = get_boss()\n        tm = boss.os_window_map.get(self.os_window_id)\n        if tm is not None:\n            t = tm.tab_for_id(self.prev_tab_id)\n            if t is not tm.active_tab and t is not None:\n                tm.set_active_tab(t)\n        if current_focused_os_window_id() != self.prev_os_window_id and self.prev_os_window_id is not None:\n            focus_os_window(self.prev_os_window_id, True)\n\n\nclass Boss:\n\n    def __init__(\n        self,\n        opts: Options,\n        args: CLIOptions,\n        cached_values: dict[str, Any],\n        global_shortcuts: dict[str, SingleKey],\n        talk_fd: int = -1,\n    ):\n        self.drag_resize_of_window = WindowResizeDrag()\n        self.atexit = Atexit()\n        set_layout_options(opts)\n        self.clipboard = Clipboard()\n        self.window_for_dispatch: Window | None = None\n        self.primary_selection = Clipboard(ClipboardType.primary_selection)\n        self.update_check_started = False\n        self.peer_data_map: dict[int, dict[str, Sequence[str]] | None] = {}\n        self.background_process_death_notify_map: dict[int, Callable[[int, Exception | None], None]] = {}\n        self.encryption_key = EllipticCurveKey()\n        self.encryption_public_key = f'{RC_ENCRYPTION_PROTOCOL_VERSION}:{base64.b85encode(self.encryption_key.public).decode(\"ascii\")}'\n        self.clipboard_buffers: dict[str, str] = {}\n        self.update_check_process: Optional['PopenType[bytes]'] = None\n        self.window_id_map: WeakValueDictionary[int, Window] = WeakValueDictionary()\n        self.color_settings_at_startup: dict[str, Color | None] = {\n                k: opts[k] for k in opts if isinstance(opts[k], Color) or k in nullable_colors}\n        self.current_visual_select: VisualSelect | None = None\n        # A list of events received so far that are potentially part of a sequence keybinding.\n        self.cached_values = cached_values\n        self.os_window_map: dict[int, TabManager] = {}\n        self.os_window_death_actions: dict[int, Callable[[], None]] = {}\n        self.cursor_blinking = True\n        self.shutting_down = False\n        self.misc_config_errors: list[str] = []\n        # we dont allow reloading the config file to change\n        # allow_remote_control\n        self.allow_remote_control = opts.allow_remote_control\n        if self.allow_remote_control in ('y', 'yes', 'true'):\n            self.allow_remote_control = 'y'\n        elif self.allow_remote_control in ('n', 'no', 'false'):\n            self.allow_remote_control = 'n'\n        self.listening_on: str = ''\n        listen_fd = -1\n        if args.listen_on and self.allow_remote_control in ('y', 'socket', 'socket-only', 'password'):\n            try:\n                listen_fd, self.listening_on = listen_on(args.listen_on, self.atexit)\n            except Exception:\n                self.misc_config_errors.append(f'Invalid listen_on={args.listen_on}, ignoring')\n                log_error(self.misc_config_errors[-1])\n        self.child_monitor: ChildMonitor = ChildMonitor(\n            self.on_child_death,\n            DumpCommands(args) if args.dump_commands or args.dump_bytes else None,\n            talk_fd, listen_fd, self.listening_on.startswith('unix:')\n        )\n        self.args: CLIOptions = args\n        self.mouse_handler: Callable[[WindowSystemMouseEvent], None] | None = None\n        set_boss(self)\n        self.mappings: Mappings = Mappings(global_shortcuts, self.refresh_active_tab_bar)\n        self.notification_manager: NotificationManager = NotificationManager(debug=self.args.debug_keyboard or self.args.debug_rendering)\n        self.atexit.unlink(store_effective_config())\n\n    def startup_first_child(self, os_window_id: int | None, startup_sessions: Iterable[Session] = ()) -> None:\n        si = startup_sessions or create_sessions(get_options(), self.args, default_session=get_options().startup_session)\n        focused_os_window = wid = 0\n        token = os.environ.pop('XDG_ACTIVATION_TOKEN', '')\n        with Window.set_ignore_focus_changes_for_new_windows():\n            for startup_session in si:\n                # The window state from the CLI options will override and apply to every single OS window in startup session\n                wstate = self.args.start_as if self.args.start_as and self.args.start_as != 'normal' else None\n                wid = self.add_os_window(startup_session, window_state=wstate, os_window_id=os_window_id)\n                if startup_session.focus_os_window:\n                    focused_os_window = wid\n                os_window_id = None\n            if focused_os_window > 0:\n                focus_os_window(focused_os_window, True, token)\n            elif token and is_wayland() and wid:\n                focus_os_window(wid, True, token)\n        for w in self.all_windows:\n            w.ignore_focus_changes = False\n\n    def add_os_window(\n        self,\n        startup_session: Session | None = None,\n        os_window_id: int | None = None,\n        wclass: str | None = None,\n        wname: str | None = None,\n        window_state: str | None = None,\n        opts_for_size: Options | None = None,\n        startup_id: str | None = None,\n        override_title: str | None = None,\n    ) -> int:\n        if os_window_id is None:\n            size_data = get_os_window_sizing_data(opts_for_size or get_options(), startup_session)\n            wclass = wclass or getattr(startup_session, 'os_window_class', None) or self.args.cls or appname\n            wname = wname or getattr(startup_session, 'os_window_name', None) or self.args.name or wclass\n            wtitle = override_title or getattr(startup_session, 'os_window_title', None) or self.args.title\n            window_state = window_state or getattr(startup_session, 'os_window_state', None)\n            wstate = parse_os_window_state(window_state) if window_state is not None else None\n            with startup_notification_handler(do_notify=startup_id is not None, startup_id=startup_id) as pre_show_callback:\n                os_window_id = create_os_window(\n                        initial_window_size_func(size_data, self.cached_values),\n                        pre_show_callback,\n                        wtitle or appname, wname, wclass, wstate, disallow_override_title=bool(wtitle))\n        else:\n            wname = self.args.name or self.args.cls or appname\n            wclass = self.args.cls or appname\n        tm = TabManager(os_window_id, self.args, wclass, wname, startup_session)\n        self.os_window_map[os_window_id] = tm\n        return os_window_id\n\n    def add_os_panel(self, cfg: LayerShellConfig, wclass: str | None = appname, wname: str | None = appname) -> int:\n        if not is_layer_shell_supported():\n            raise RuntimeError('Creating desktop panels is not supported on this platform')\n        wclass = wclass or appname\n        wname = wname or appname\n        size_data = get_os_window_sizing_data(get_options(), None)\n        os_window_id = create_os_window(\n            initial_window_size_func(size_data, {}), lambda *a: None, appname, wname, wclass, None, layer_shell_config=cfg)\n        tm = TabManager(os_window_id, self.args, wclass, wname, None)\n        self.os_window_map[os_window_id] = tm\n        return os_window_id\n\n    def list_os_windows(\n        self, self_window: Window | None = None,\n        tab_filter: Callable[[Tab], bool] | None = None,\n        window_filter: Callable[[Window], bool] | None = None\n    ) -> Iterator[OSWindowDict]:\n        with cached_process_data():\n            active_tab_manager = self.active_tab_manager\n            focused_wid = current_focused_os_window_id()\n            last_focused = last_focused_os_window_id()\n            for os_window_id, tm in self.os_window_map.items():\n                tabs = list(tm.list_tabs(self_window, tab_filter, window_filter))\n                if tabs:\n                    bo = background_opacity_of(os_window_id)\n                    if bo is None:\n                        bo = 1\n                    yield {\n                        'id': os_window_id,\n                        'platform_window_id': platform_window_id(os_window_id),\n                        'is_active': tm is active_tab_manager,\n                        'is_focused': focused_wid == os_window_id,\n                        'last_focused': os_window_id == last_focused,\n                        'tabs': tabs,\n                        'active_tab_history': tuple(tm.active_tab_history),\n                        'wm_class': tm.wm_class,\n                        'wm_name': tm.wm_name,\n                        'background_opacity': bo,\n                    }\n\n    def serialize_state_as_session(self, session_path: str = '', ser_opts: SaveAsSessionOptions | None = None) -> Iterator[str]:\n        if ser_opts is None:\n            ser_opts = default_save_as_session_opts()\n        matched_windows = frozenset(self.match_windows(ser_opts.match)) if ser_opts.match else None\n        s = {current_focused_os_window_id(): 2, last_focused_os_window_id(): 1}\n        for i, os_window_id in enumerate(sorted(self.os_window_map, key=lambda wid: s.get(wid, 0))):\n            tm = self.os_window_map[os_window_id]\n            yield from tm.serialize_state_as_session(session_path, matched_windows, is_first=i==0, ser_opts=ser_opts)\n\n    @property\n    def all_tab_managers(self) -> Iterator[TabManager]:\n        yield from self.os_window_map.values()\n\n    @property\n    def all_tabs(self) -> Iterator[Tab]:\n        for tm in self.all_tab_managers:\n            yield from tm\n\n    @property\n    def all_windows(self) -> Iterator[Window]:\n        for tab in self.all_tabs:\n            yield from tab\n\n    def match_windows(self, match: str, self_window: Optional['Window'] = None, all_windows: Iterable[Window] | None = None) -> Iterator[Window]:\n        all_windows = self.all_windows if all_windows is None else all_windows\n        if match == 'all':\n            yield from all_windows\n            return\n        from .search_query_parser import search\n        tab = self.active_tab\n        if current_focused_os_window_id() <= 0:\n            tm = self.os_window_map.get(last_focused_os_window_id())\n            if tm is not None:\n                tab = tm.active_tab\n        wids = {w.id for w in all_windows}\n        window_id_limit = max(wids, default=-1) + 1\n        active_session = self.active_session\n        prev_active_session = most_recent_session()\n\n        def get_matches(location: str, query: str, candidates: set[int]) -> set[int]:\n            if location == 'id' and query.startswith('-'):\n                try:\n                    q = int(query)\n                except Exception:\n                    return set()\n                if q < 0:\n                    query = str(window_id_limit + q)\n            return {wid for wid in candidates if self.window_id_map[wid].matches_query(location, query, tab, self_window, active_session, prev_active_session)}\n\n        for wid in search(match, (\n            'id', 'title', 'pid', 'cwd', 'cmdline', 'num', 'env', 'var', 'recent', 'state', 'neighbor', 'session',\n        ), wids, get_matches):\n            yield self.window_id_map[wid]\n\n    def match_tabs(self, match: str, all_tabs: Iterable[Tab] | None = None) -> Iterator[Tab]:\n        all_tabs = self.all_tabs if all_tabs is None else all_tabs\n        if match == 'all':\n            yield from all_tabs\n            return\n        from .search_query_parser import search\n        tm = self.active_tab_manager\n        if current_focused_os_window_id() <= 0:\n            tm = self.os_window_map.get(last_focused_os_window_id()) or tm\n        tim = {t.id: t for t in all_tabs}\n        tab_id_limit = max(tim, default=-1) + 1\n        window_id_limit = max(self.window_id_map, default=-1) + 1\n        active_session = self.active_session\n        prev_active_session = most_recent_session()\n\n        def get_matches(location: str, query: str, candidates: set[int]) -> set[int]:\n            if location in ('id', 'window_id') and query.startswith('-'):\n                try:\n                    q = int(query)\n                except Exception:\n                    return set()\n                if q < 0:\n                    limit = tab_id_limit if location == 'id' else window_id_limit\n                    query = str(limit + q)\n            return {wid for wid in candidates if tim[wid].matches_query(location, query, tm, active_session, prev_active_session)}\n\n        found = False\n        for tid in search(match, (\n            'id', 'index', 'title', 'window_id', 'window_title', 'pid', 'cwd', 'env', 'var',\n            'cmdline', 'recent', 'state', 'session',\n        ), set(tim), get_matches):\n            found = True\n            yield tim[tid]\n\n        if not found:\n            tabs = {w.tabref() for w in self.match_windows(match)}\n            for q in tabs:\n                if q:\n                    yield q\n\n    def focus_os_window(self, os_window_id: int, if_needed_only: bool = True) -> bool:\n        if if_needed_only and current_focused_os_window_id() == os_window_id:\n            return False\n\n        def doit(token: str = '') -> None:\n            focus_os_window(os_window_id, True, token)\n\n        if is_wayland():\n            if not run_with_activation_token(doit):\n                doit()\n        else:\n            doit()\n        return True\n\n    def set_active_window(\n        self, window: Window, switch_os_window_if_needed: bool = False, for_keep_focus: bool = False, activation_token: str = ''\n    ) -> int | None:\n        for os_window_id, tm in self.os_window_map.items():\n            for tab in tm:\n                for w in tab:\n                    if w.id == window.id:\n                        if tab is not self.active_tab:\n                            tm.set_active_tab(tab, for_keep_focus=window.tabref() if for_keep_focus else None)\n                        tab.set_active_window(w, for_keep_focus=window if for_keep_focus else None)\n                        if switch_os_window_if_needed and current_focused_os_window_id() != os_window_id:\n                            if activation_token or not is_wayland():\n                                focus_os_window(os_window_id, True, activation_token)\n                            else:\n                                def doit(token: str = '') -> None:\n                                    focus_os_window(os_window_id, True, token)\n                                if not run_with_activation_token(doit):\n                                    doit()\n                        return os_window_id\n        return None\n\n    def _new_os_window(self, args: SpecialWindowInstance | Iterable[str], cwd_from: CwdRequest | None = None) -> int:\n        if isinstance(args, SpecialWindowInstance):\n            sw: SpecialWindowInstance | None = args\n        else:\n            sw = self.args_to_special_window(args, cwd_from) if args else None\n        startup_session = next(create_sessions(get_options(), special_window=sw, cwd_from=cwd_from))\n        startup_session.session_name = ''\n        ans = self.add_os_window(startup_session)\n        if cwd_from is not None and (sow := cwd_from.window) and (tm := self.os_window_map.get(ans)):\n            session_name = sow.created_in_session_name\n            if not session_name and (sow_tab := sow.tabref()):\n                session_name = sow_tab.created_in_session_name\n            if session_name:\n                for tab in tm:\n                    tab.created_in_session_name = session_name\n                    for w in tab:\n                        w.created_in_session_name = session_name\n        return ans\n\n    @ac('win', 'New OS Window')\n    def new_os_window(self, *args: str) -> None:\n        self._new_os_window(args)\n\n    @property\n    def active_window_for_cwd(self) -> Window | None:\n        t = self.active_tab\n        if t is not None:\n            return t.active_window_for_cwd\n        return None\n\n    @ac('win', '''\n        New OS Window with the same working directory as the currently active window.\n        The new OS Window is added to the currently active :ref:`session <sessions>`, if any.\n    ''')\n    def new_os_window_with_cwd(self, *args: str) -> None:\n        w = self.window_for_dispatch or self.active_window_for_cwd\n        self._new_os_window(args, CwdRequest(w))\n\n    def new_os_window_with_wd(self, wd: str | list[str], str_is_multiple_paths: bool = False) -> None:\n        if isinstance(wd, str):\n            wd = wd.split(os.pathsep) if str_is_multiple_paths else [wd]\n        for path in wd:\n            special_window = SpecialWindow(None, cwd=path)\n            self._new_os_window(special_window)\n\n    def add_child(self, window: Window) -> None:\n        assert window.child.pid is not None and window.child.child_fd is not None\n        self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)\n        self.window_id_map[window.id] = window\n\n    def _handle_remote_command(self, cmd: memoryview, window: Window | None = None, peer_id: int = 0) -> RCResponse:\n        from .remote_control import is_cmd_allowed, parse_cmd, remote_control_allowed\n        response = None\n        window = window or None\n        from_socket = peer_id > 0\n        is_fd_peer = from_socket and peer_id in self.peer_data_map\n        window_has_remote_control = bool(window and window.allow_remote_control)\n        if not window_has_remote_control and not is_fd_peer:\n            if self.allow_remote_control == 'n':\n                return {'ok': False, 'error': 'Remote control is disabled'}\n            if self.allow_remote_control == 'socket-only' and not from_socket:\n                return {'ok': False, 'error': 'Remote control is allowed over a socket only'}\n        try:\n            pcmd = parse_cmd(cmd, self.encryption_key)\n        except Exception as e:\n            log_error(f'Failed to parse remote command with error: {e}')\n            return response\n        if not pcmd:\n            return response\n        self_window: Window | None = None\n        if window is not None:\n            self_window = window\n        else:\n            try:\n                swid = int(pcmd.get('kitty_window_id', 0))\n            except Exception:\n                pass\n            else:\n                if swid > 0:\n                    self_window = self.window_id_map.get(swid)\n\n        extra_data: dict[str, Any] = {}\n        try:\n            allowed_unconditionally = (\n                self.allow_remote_control == 'y' or\n                (from_socket and not is_fd_peer and self.allow_remote_control in ('socket-only', 'socket')) or\n                (window and window.remote_control_allowed(pcmd, extra_data)) or\n                (is_fd_peer and remote_control_allowed(pcmd, self.peer_data_map.get(peer_id), None, extra_data))\n            )\n        except PermissionError:\n            return {'ok': False, 'error': 'Remote control disallowed by window specific password'}\n        if allowed_unconditionally:\n            return self._execute_remote_command(pcmd, window, peer_id, self_window)\n        q = is_cmd_allowed(pcmd, window, from_socket, extra_data)\n        if q is True:\n            return self._execute_remote_command(pcmd, window, peer_id, self_window)\n        if q is None:\n            if self.ask_if_remote_cmd_is_allowed(pcmd, window, peer_id, self_window):\n                return AsyncResponse()\n        response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control to your kitty.conf'}\n        if q is False and pcmd.get('password'):\n            response['error'] = 'The user rejected this password or it is disallowed by remote_control_password in kitty.conf'\n        no_response = pcmd.get('no_response') or False\n        if no_response:\n            return None\n        return response\n\n    def ask_if_remote_cmd_is_allowed(\n        self, pcmd: dict[str, Any], window: Window | None = None, peer_id: int = 0, self_window: Window | None = None\n    ) -> bool:\n        from kittens.tui.operations import styled\n        in_flight = 0\n        for w in self.window_id_map.values():\n            if w.window_custom_type == 'remote_command_permission_dialog':\n                in_flight += 1\n                if in_flight > 4:\n                    log_error('Denying remote command permission as there are too many existing permission requests')\n                    return False\n        wid = 0 if window is None else window.id\n        hidden_text = styled(pcmd['password'], fg='yellow')\n        overlay_window = self.choose(\n            _('A program wishes to control kitty.\\n'\n              'Action: {1}\\n' 'Password: {0}\\n\\n' '{2}'\n              ).format(\n                  hidden_text, styled(pcmd['cmd'], fg='magenta'),\n                  '\\x1b[m' + styled(_(\n                      'Note that allowing the password will allow all future actions using the same password, in this kitty instance.'\n                  ), dim=True, italic=True)),\n            partial(self.remote_cmd_permission_received, pcmd, wid, peer_id, self_window),\n            'a;green:Allow request', 'p;yellow:Allow password', 'r;magenta:Deny request', 'd;red:Deny password',\n            window=window, default='a', hidden_text=hidden_text, title=_('Allow remote control?'),\n        )\n        if overlay_window is None:\n            return False\n        overlay_window.window_custom_type = 'remote_command_permission_dialog'\n        return True\n\n    def remote_cmd_permission_received(self, pcmd: dict[str, Any], window_id: int, peer_id: int, self_window: Window | None, choice: str) -> None:\n        from .remote_control import encode_response_for_peer, set_user_password_allowed\n        response: RCResponse = None\n        window = self.window_id_map.get(window_id)\n        choice = choice or 'r'\n        if choice in ('r', 'd'):\n            if choice == 'd':\n                set_user_password_allowed(pcmd['password'], False)\n            no_response = pcmd.get('no_response') or False\n            if not no_response:\n                response = {'ok': False, 'error': 'The user rejected this ' + ('request' if choice == 'r' else 'password')}\n        elif choice in ('a', 'p'):\n            if choice == 'p':\n                set_user_password_allowed(pcmd['password'], True)\n            response = self._execute_remote_command(pcmd, window, peer_id, self_window)\n        if window is not None and response is not None and not isinstance(response, AsyncResponse):\n            window.send_cmd_response(response)\n        if peer_id > 0:\n            if response is None:\n                send_data_to_peer(peer_id, b'')\n            elif isinstance(response, AsyncResponse):\n                send_data_to_peer(peer_id, b'', True)\n            else:\n                send_data_to_peer(peer_id, encode_response_for_peer(response))\n\n    def _execute_remote_command(\n        self, pcmd: dict[str, Any], window: Window | None = None, peer_id: int = 0, self_window: Window | None = None\n    ) -> RCResponse:\n        from .remote_control import handle_cmd\n        try:\n            response = handle_cmd(self, window, pcmd, peer_id, self_window)\n        except Exception as err:\n            import traceback\n            response = {'ok': False, 'error': str(err)}\n            if not getattr(err, 'hide_traceback', False):\n                response['tb'] = traceback.format_exc()\n        return response\n\n    @ac('misc', '''\n        Run a remote control command without needing to allow remote control\n\n        For example::\n\n            map f1 remote_control set-spacing margin=30\n\n        See :ref:`rc_mapping` for details.\n        ''')\n    def remote_control(self, *args: str) -> None:\n        try:\n            self.call_remote_control(self.window_for_dispatch or self.active_window, args)\n        except (Exception, SystemExit) as e:\n            import shlex\n            self.show_error(_('remote_control mapping failed'), shlex.join(args) + '\\n' + str(e))\n\n    @ac('misc', '''\n        Run a remote control script without needing to allow remote control\n\n        For example::\n\n            map f1 remote_control_script /path/to/script arg1 arg2 ...\n\n        See :ref:`rc_mapping` for details. Relative paths are resolved with respect\n        to the kitty config directory.\n        ''')\n    def remote_control_script(self, path: str, *args: str) -> None:\n        path = resolve_custom_file(path)\n        path = which(path) or path\n        if not os.access(path, os.X_OK):\n            self.show_error('Remote control script not executable', f'The script {path} is not executable check its permissions')\n            return\n        self.run_background_process([path] + list(args), allow_remote_control=True)\n\n    def call_remote_control(self, self_window: Window | None, args: tuple[str, ...]) -> 'ResponseType':\n        from .rc.base import PayloadGetter, command_for_name, parse_subcommand_cli\n        from .remote_control import parse_rc_args\n        aa = list(args)\n        silent = False\n        if aa and aa[0].startswith('!'):\n            aa[0] = aa[0][1:]\n            silent = True\n        try:\n            global_opts, items = parse_rc_args(['@'] + aa)\n            if not items:\n                return None\n            cmd = items[0]\n            c = command_for_name(cmd)\n            opts, items = parse_subcommand_cli(c, items)\n            payload = c.message_to_kitty(global_opts, opts, items)\n        except SystemExit as e:\n            raise Exception(str(e)) from e\n        import types\n        try:\n            if isinstance(payload, types.GeneratorType):\n                for x in payload:\n                    c.response_from_kitty(self, self_window, PayloadGetter(c, x if isinstance(x, dict) else {}))\n                return None\n            return c.response_from_kitty(self, self_window, PayloadGetter(c, payload if isinstance(payload, dict) else {}))\n        except Exception as e:\n            if silent:\n                log_error(f'Failed to run remote_control mapping: {aa} with error: {e}')\n                return None\n            raise\n\n    def peer_message_received(self, msg_bytes: bytes, peer_id: int, is_remote_control: bool) -> bytes | bool | None:\n        if peer_id > 0 and msg_bytes == b'peer_death':\n            self.peer_data_map.pop(peer_id, None)\n            return False\n        if is_remote_control:\n            cmd_prefix = b'\\x1bP@kitty-cmd'\n            terminator = b'\\x1b\\\\'\n            if msg_bytes.startswith(cmd_prefix) and msg_bytes.endswith(terminator):\n                cmd = memoryview(msg_bytes)[len(cmd_prefix):-len(terminator)]\n                response = self._handle_remote_command(cmd, peer_id=peer_id)\n                if response is None:\n                    return None\n                if isinstance(response, AsyncResponse):\n                    return True\n                from kitty.remote_control import encode_response_for_peer\n                return encode_response_for_peer(response)\n            log_error('Malformatted remote control message received from peer, ignoring')\n            return None\n\n        try:\n            data:SingleInstanceData = json.loads(msg_bytes.decode('utf-8'))\n        except Exception:\n            log_error('Malformed command received over single instance socket, ignoring')\n            return None\n        if isinstance(data, dict) and data.get('cmd') == 'new_instance':\n            if data['args'][0] == 'panel':\n                from kittens.panel.main import handle_single_instance_command\n                handle_single_instance_command(self, data['args'], data['environ'], data.get('notify_on_os_window_death', ''))\n                return None\n            from .cli_stub import CLIOptions\n            startup_id = data['environ'].get('DESKTOP_STARTUP_ID', '')\n            activation_token = data['environ'].get('XDG_ACTIVATION_TOKEN', '')\n            try:\n                args, rest = parse_args(list(data['args'][1:]), result_class=CLIOptions)\n            except BaseException as e:\n                self.show_error(_('Invalid single instance command received'), _('The command: {0} is invalid with error: {1}').format(\n                    data['args'], e))\n                return None\n            cmdline_args_for_open = data.get('cmdline_args_for_open')\n            if cmdline_args_for_open:\n                self.launch_urls(*cmdline_args_for_open, no_replace_window=True)\n                return None\n            args.args = rest\n            opts = create_opts(args)\n            if data['session_data']:\n                if data['session_data'] == 'none':\n                    args.session = 'none'\n                else:\n                    from .session import PreReadSession\n                    args.session = PreReadSession(data['session_data'], data['environ'], data['session_arg'], data['session_path'])\n            else:\n                args.session = ''\n            if not os.path.isabs(args.directory):\n                args.directory = os.path.join(data['cwd'], args.directory)\n            from .child import process_env\n            clean_env = process_env(data['environ'])\n            focused_os_window = os_window_id = 0\n            for session in create_sessions(opts, args, respect_cwd=True, env_when_no_session=clean_env):\n                if not session.has_non_background_processes:\n                    # background only do not create an OS Window\n                    from .launch import LaunchSpec, launch\n                    for tab in session.tabs:\n                        for window in tab.windows:\n                            if window.is_background_process:\n                                assert isinstance(window.launch_spec, LaunchSpec)\n                                launch(get_boss(), window.launch_spec.opts, window.launch_spec.args)\n                    continue\n                wstate = args.start_as if args.start_as and args.start_as != 'normal' else None\n                os_window_id = self.add_os_window(\n                    session, wclass=args.cls, wname=args.name, opts_for_size=opts, startup_id=startup_id,\n                    override_title=args.title or None, window_state=wstate)\n                if session.focus_os_window:\n                    focused_os_window = os_window_id\n                if opts.background_opacity != get_options().background_opacity:\n                    self._set_os_window_background_opacity(os_window_id, opts.background_opacity)\n                if n := data.get('notify_on_os_window_death'):\n                    self.os_window_death_actions[os_window_id] = partial(self.notify_on_os_window_death, n)\n            if focused_os_window > 0:\n                focus_os_window(focused_os_window, True, activation_token)\n            elif activation_token and is_wayland() and os_window_id:\n                focus_os_window(os_window_id, True, activation_token)\n        else:\n            log_error('Unknown message received over single instance socket, ignoring')\n        return None\n\n    def quick_access_terminal_invoked(self) -> None:\n        for os_window_id in self.os_window_map:\n            toggle_os_window_visibility(os_window_id, move_to_active_screen=True)\n\n    def handle_remote_cmd(self, cmd: memoryview, window: Window | None = None) -> None:\n        response = self._handle_remote_command(cmd, window)\n        if response is not None and not isinstance(response, AsyncResponse) and window is not None:\n            window.send_cmd_response(response)\n\n    def mark_os_window_for_close(self, os_window_id: int, request_type: int = IMPERATIVE_CLOSE_REQUESTED) -> None:\n        if self.current_visual_select is not None and self.current_visual_select.os_window_id == os_window_id:\n            self.cancel_current_visual_select()\n        mark_os_window_for_close(os_window_id, request_type)\n\n    def _cleanup_tab_after_window_removal(self, src_tab: Tab) -> None:\n        if len(src_tab) < 1:\n            tm = src_tab.tab_manager_ref()\n            if tm is not None:\n                tm.remove(src_tab)\n                src_tab.destroy()\n                if len(tm) == 0:\n                    if not self.shutting_down:\n                        self.mark_os_window_for_close(src_tab.os_window_id)\n\n    @contextmanager\n    def suppress_focus_change_events(self) -> Generator[None, None, None]:\n        changes = {}\n        for w in self.window_id_map.values():\n            changes[w] = w.ignore_focus_changes\n            w.ignore_focus_changes = True\n        try:\n            yield\n        finally:\n            for w, val in changes.items():\n                w.ignore_focus_changes = val\n\n    def on_child_death(self, window_id: int) -> None:\n        prev_active_window = self.active_window\n        window = self.window_id_map.pop(window_id, None)\n        if window is None:\n            return\n        with self.suppress_focus_change_events():\n            for close_action in window.actions_on_close:\n                try:\n                    close_action(window)\n                except Exception:\n                    import traceback\n                    traceback.print_exc()\n            os_window_id = window.os_window_id\n            window.destroy()\n            tm = self.os_window_map.get(os_window_id)\n            tab = None\n            if tm is not None:\n                for q in tm:\n                    if window in q:\n                        tab = q\n                        break\n            if tab is not None:\n                tab.remove_window(window)\n                self._cleanup_tab_after_window_removal(tab)\n            for removal_action in window.actions_on_removal:\n                try:\n                    removal_action(window)\n                except Exception:\n                    import traceback\n                    traceback.print_exc()\n            del window.actions_on_close[:], window.actions_on_removal[:]\n\n        window = self.active_window\n        if window is not prev_active_window:\n            if prev_active_window is not None:\n                prev_active_window.focus_changed(False)\n            if window is not None:\n                window.focus_changed(True)\n\n    def mark_window_for_close(self, q: Window | None | int = None) -> None:\n        if isinstance(q, int):\n            window = self.window_id_map.get(q)\n            if window is None:\n                return\n        else:\n            window = q or self.active_window\n        if window:\n            self.child_monitor.mark_for_close(window.id)\n\n    @ac('win', 'Close the currently active window')\n    def close_window(self) -> None:\n        self.mark_window_for_close(self.window_for_dispatch)\n\n    def close_windows_with_confirmation_msg(self, windows: Iterable[Window], active_window: Window | None = None) -> tuple[str, int]:\n        num_running_programs = 0\n        num_background_programs = 0\n        count_background = get_options().confirm_os_window_close[1]\n        running_program = background_program = ''\n        windows = sorted(windows, key=lambda w: 0 if w is active_window else 1)\n        with cached_process_data():\n            for window in windows:\n                if window.has_running_program:\n                    num_running_programs += 1\n                    running_program = running_program or (window.child.foreground_cmdline or [''])[0]\n                elif count_background and (bp := window.child.background_processes):\n                    num_background_programs += len(bp)\n                    for q in bp:\n                        background_program = background_program or (q['cmdline'] or [''])[0]\n        if num := num_running_programs + num_background_programs:\n            if num_running_programs:\n                return ngettext(_('It is running: {0}.'), _('It is running: {0} and {1} other programs.'), num_running_programs).format(\n                        green(running_program), num_running_programs - 1), num\n            if num_background_programs:\n                return ngettext(_('It is running: {0} in the background.'), _(\n                    'It is running: {0} in the background and {1} other programs.'),\n                    num_background_programs).format(green(background_program), num_background_programs - 1) + ' ' + _(\n                            '\\n\\nBackground programs should be run with the disown command'\n                            ' to allow them to continue running when the terminal is closed.'), num\n        return '', 0\n\n    @ac('win', '''\n    Close window with confirmation\n\n    Asks for confirmation before closing the window. If you don't want the\n    confirmation when the window is sitting at a shell prompt\n    (requires :ref:`shell_integration`), use::\n\n        map f1 close_window_with_confirmation ignore-shell\n    ''')\n    def close_window_with_confirmation(self, ignore_shell: bool = False) -> None:\n        window = self.window_for_dispatch or self.active_window\n        if window is None:\n            return\n        msg = self.close_windows_with_confirmation_msg((window,), window)[0]\n        if not msg and not ignore_shell:\n            msg = _('It is running a shell.')\n        if msg:\n            msg = _('Are you sure you want to close this window?') + ' ' + msg\n            self.confirm(msg, self.handle_close_window_confirmation, window.id, window=window, title=_('Close window?'))\n        else:\n            self.mark_window_for_close(window)\n\n    def handle_close_window_confirmation(self, allowed: bool, window_id: int) -> None:\n        if allowed:\n            self.mark_window_for_close(window_id)\n\n    @ac('tab', 'Close the current tab')\n    def close_tab(self, tab: Tab | None = None) -> None:\n        if tab is None and self.window_for_dispatch:\n            tab = self.window_for_dispatch.tabref()\n        tab = tab or self.active_tab\n        if tab:\n            self.confirm_tab_close(tab)\n\n    @property\n    def active_tab_manager_with_dispatch(self) -> TabManager | None:\n        if self.window_for_dispatch:\n            td = self.window_for_dispatch.tabref()\n            tm = td.tab_manager_ref() if td else None\n        else:\n            tm = self.active_tab_manager\n        return tm\n\n    @ac('tab', 'Close all the tabs in the current OS window other than the currently active tab')\n    def close_other_tabs_in_os_window(self) -> None:\n        tm = self.active_tab_manager_with_dispatch\n        if tm is not None and len(tm.tabs) > 1:\n            active_tab = self.active_tab\n            for tab in tm:\n                if tab is not active_tab:\n                    self.close_tab(tab)\n\n    @ac('win', 'Close all other OS Windows other than the OS Window containing the currently active window')\n    def close_other_os_windows(self) -> None:\n        active = self.active_tab_manager_with_dispatch\n        if active is not None:\n            for x in self.os_window_map.values():\n                if x is not active:\n                    self.mark_os_window_for_close(x.os_window_id)\n\n    def confirm(\n        self, msg: str,  # can contain newlines and ANSI formatting\n        callback: Callable[..., None],  # called with True or False and *args\n        *args: Any,  # passed to the callback function\n        window: Window | None = None,  # the window associated with the confirmation\n        confirm_on_cancel: bool = False,  # on closing window\n        confirm_on_accept: bool = True,  # on pressing enter\n        title: str = ''  # window title\n    ) -> Window:\n        result: bool = False\n\n        def callback_(res: dict[str, Any], x: int, boss: Boss) -> None:\n            nonlocal result\n            result = res.get('response') == 'y'\n\n        def on_popup_overlay_removal(wid: int, boss: Boss) -> None:\n            callback(result, *args)\n\n        cmd = ['--type=yesno', '--message', msg, '--default', 'y' if confirm_on_accept else 'n']\n        if title:\n            cmd += ['--title', title]\n        w = self.run_kitten_with_metadata(\n            'ask', cmd, window=window, custom_callback=callback_, action_on_removal=on_popup_overlay_removal,\n            default_data={'response': 'y' if confirm_on_cancel else 'n'})\n        assert isinstance(w, Window)\n        return w\n\n    def choose(\n        self, msg: str,  # can contain newlines and ANSI formatting\n        callback: Callable[..., None],  # called with the choice or empty string when aborted\n        *choices: str,   # The choices, see the help for the ask kitten for format of a choice\n        window: Window | None = None,  # the window associated with the confirmation\n        default: str = '',  # the default choice when the user presses Enter\n        hidden_text: str = '',  # text to hide in the message\n        hidden_text_placeholder: str = 'HIDDEN_TEXT_PLACEHOLDER',  # placeholder text to insert in to message\n        unhide_key: str = 'u',  # key to press to unhide hidden text\n        title: str = '' # window title\n    ) -> Window | None:\n        result: str = ''\n\n        def callback_(res: dict[str, Any], x: int, boss: Boss) -> None:\n            nonlocal result\n            result = res.get('response') or ''\n\n        if hidden_text:\n            msg = msg.replace(hidden_text, hidden_text_placeholder)\n        cmd = ['--type=choices', '--message', msg]\n        if default:\n            cmd += ['-d', default]\n        for c in choices:\n            cmd += ['-c', c]\n        if hidden_text:\n            cmd += ['--hidden-text-placeholder', hidden_text_placeholder, '--unhide-key', unhide_key]\n            input_data = hidden_text\n        else:\n            input_data = None\n        if title:\n            cmd += ['--title', title]\n\n        def on_popup_overlay_removal(wid: int, boss: Boss) -> None:\n            callback(result)\n\n        ans = self.run_kitten_with_metadata(\n            'ask', cmd, window=window, custom_callback=callback_, input_data=input_data, default_data={'response': ''},\n            action_on_removal=on_popup_overlay_removal\n        )\n        if isinstance(ans, Window):\n            return ans\n        return None\n\n    def get_line(\n        self, msg: str,  # can contain newlines and ANSI formatting\n        callback: Callable[..., None],  # called with the answer or empty string when aborted\n        window: Window | None = None,  # the window associated with the confirmation\n        prompt: str = '> ',\n        is_password: bool = False,\n        initial_value: str = '',\n        window_title: str = '',\n    ) -> Window | None:\n        result: str = ''\n\n        def callback_(res: dict[str, Any], x: int, boss: Boss) -> None:\n            nonlocal result\n            result = res.get('response') or ''\n\n        def on_popup_overlay_removal(wid: int, boss: Boss) -> None:\n            callback(result)\n\n        cmd = ['--type', 'password' if is_password else 'line', '--message', msg, '--prompt', prompt]\n        if initial_value:\n            cmd.append('--default=' + initial_value)\n        if window_title:\n            cmd.append(f'--title={window_title}')\n        w = self.run_kitten_with_metadata(\n            'ask', cmd, window=window, custom_callback=callback_, default_data={'response': ''}, action_on_removal=on_popup_overlay_removal\n        )\n        return w if isinstance(w, Window) else None\n\n    def get_save_filepath(\n        self, msg: str,  # can contain newlines and ANSI formatting\n        callback: Callable[..., None],  # called with the answer or empty string when aborted\n        window: Window | None = None,  # the window associated with the confirmation\n        prompt: str = '> ',\n        initial_value: str = ''\n    ) -> None:\n        result: str = ''\n\n        def callback_(res: dict[str, Any], x: int, boss: Boss) -> None:\n            nonlocal result\n            result = res.get('response') or ''\n\n        def on_popup_overlay_removal(wid: int, boss: Boss) -> None:\n            callback(result)\n\n        cmd = ['--type', 'file', '--message', msg, '--prompt', prompt]\n        if initial_value:\n            cmd.append('--default=' + initial_value)\n        self.run_kitten_with_metadata(\n            'ask', cmd, window=window, custom_callback=callback_, default_data={'response': ''}, action_on_removal=on_popup_overlay_removal\n        )\n\n    def confirm_tab_close(self, tab: Tab) -> None:\n        msg, num_active_windows = self.close_windows_with_confirmation_msg(tab, tab.active_window)\n        x = get_options().confirm_os_window_close[0]\n        num = num_active_windows if x < 0 else len(tab)\n        needs_confirmation = x != 0 and num >= abs(x)\n        if not needs_confirmation:\n            self.close_tab_no_confirm(tab)\n            return\n        msg = msg or _('It has {} windows?').format(num)\n        if tab is not self.active_tab:\n            tm = tab.tab_manager_ref()\n            if tm is not None:\n                tm.set_active_tab(tab)\n        if tab.confirm_close_window_id and tab.confirm_close_window_id in self.window_id_map:\n            w = self.window_id_map[tab.confirm_close_window_id]\n            if w in tab:\n                tab.set_active_window(w)\n                return\n        msg = _('Are you sure you want to close this tab?') + ' ' + msg\n        w = self.confirm(msg, self.handle_close_tab_confirmation, tab.id, window=tab.active_window, title=_('Close tab?'))\n        tab.confirm_close_window_id = w.id\n\n    def handle_close_tab_confirmation(self, confirmed: bool, tab_id: int) -> None:\n        for tab in self.all_tabs:\n            if tab.id == tab_id:\n                tab.confirm_close_window_id = 0\n                break\n        else:\n            return\n        if not confirmed:\n            return\n        self.close_tab_no_confirm(tab)\n\n    def close_tab_no_confirm(self, tab: Tab) -> None:\n        if self.current_visual_select is not None and self.current_visual_select.tab_id == tab.id:\n            self.cancel_current_visual_select()\n        for window in tab:\n            self.mark_window_for_close(window)\n\n    def close_windows_no_confirm(self, windows: Sequence[Window]) -> None:\n        if self.current_visual_select is not None:\n            self.cancel_current_visual_select()\n        for window in windows:\n            self.mark_window_for_close(window)\n\n    @ac('win', 'Toggle the fullscreen status of the active OS Window')\n    def toggle_fullscreen(self, os_window_id: int = 0) -> None:\n        if os_window_id == 0:\n            tm = self.active_tab_manager_with_dispatch\n            if tm:\n                os_window_id = tm.os_window_id\n        toggle_fullscreen(os_window_id)\n\n    @ac('win', 'Toggle the maximized status of the active OS Window')\n    def toggle_maximized(self, os_window_id: int = 0) -> None:\n        if os_window_id == 0:\n            tm = self.active_tab_manager_with_dispatch\n            if tm:\n                os_window_id = tm.os_window_id\n        toggle_maximized(os_window_id)\n\n    @ac('misc', 'Toggle macOS secure keyboard entry')\n    def toggle_macos_secure_keyboard_entry(self) -> None:\n        toggle_secure_input()\n\n    @ac('misc', 'Cycle through OS windows on macOS')\n    def macos_cycle_through_os_windows(self) -> None:\n        macos_cycle_through_os_windows(False)\n\n    @ac('misc', 'Cycle through OS windows backwards on macOS')\n    def macos_cycle_through_os_windows_backwards(self) -> None:\n        macos_cycle_through_os_windows(True)\n\n    @ac('misc', 'Hide macOS kitty application')\n    def hide_macos_app(self) -> None:\n        cocoa_hide_app()\n\n    @ac('misc', 'Hide macOS other applications')\n    def hide_macos_other_apps(self) -> None:\n        cocoa_hide_other_apps()\n\n    @ac('misc', 'Minimize macOS window')\n    def minimize_macos_window(self) -> None:\n        osw_id = None\n        if self.window_for_dispatch:\n            tm = self.active_tab_manager_with_dispatch\n            if tm:\n                osw_id = tm.os_window_id\n        else:\n            osw_id = current_os_window()\n        if osw_id is not None:\n            cocoa_minimize_os_window(osw_id)\n\n    def start(self, first_os_window_id: int, startup_sessions: Iterable[Session]) -> None:\n        if not getattr(self, 'io_thread_started', False):\n            self.child_monitor.start()\n            self.io_thread_started = True\n            for signum in self.child_monitor.handled_signals():\n                handled_signals.add(signum)\n            urls: list[str] = getattr(sys, 'cmdline_args_for_open', [])\n            if urls:\n                delattr(sys, 'cmdline_args_for_open')\n                sess = create_sessions(get_options(), self.args, special_window=SpecialWindow([kitty_exe(), '+runpy', 'input()']))\n                self.startup_first_child(first_os_window_id, startup_sessions=tuple(sess))\n                self.launch_urls(*urls)\n            else:\n                self.startup_first_child(first_os_window_id, startup_sessions=startup_sessions)\n\n        if get_options().update_check_interval > 0 and not self.update_check_started and getattr(sys, 'frozen', False):\n            from .update_check import run_update_check\n            run_update_check(get_options().update_check_interval * 60 * 60)\n            self.update_check_started = True\n\n    def handle_window_title_bar_mouse(self, os_window_id: int, window_id: int, button: int, modifiers: int, action: int) -> None:\n        if tm := self.os_window_map.get(os_window_id):\n            tm.handle_window_title_bar_mouse(window_id, button, modifiers, action)\n\n    def handle_tab_bar_mouse(self, os_window_id: int, x: float, y: float, button: int, modifiers: int, action: int) -> None:\n        if tm := self.os_window_map.get(os_window_id):\n            tm.handle_tab_bar_mouse(x, y, button, modifiers, action)\n\n    def start_tab_drag(self, os_window_id: int, window_id: int, pixels: bytes, width: int, height: int) -> None:\n        if tm := self.os_window_map.get(os_window_id):\n            tm.start_tab_drag(pixels, width, height)\n\n    def on_window_resize(self, os_window_id: int, w: int, h: int, dpi_changed: bool) -> None:\n        if dpi_changed:\n            self.on_dpi_change(os_window_id)\n        else:\n            tm = self.os_window_map.get(os_window_id)\n            if tm is not None:\n                tm.resize()\n\n    @ac('misc', '''\n        Clear the terminal\n\n        See :sc:`reset_terminal <reset_terminal>` for details. For example::\n\n            # Reset the terminal\n            map f1 clear_terminal reset active\n            # Clear the terminal screen by erasing all contents\n            map f1 clear_terminal clear active\n            # Clear the terminal scrollback by erasing it\n            map f1 clear_terminal scrollback active\n            # Scroll the contents of the screen into the scrollback\n            map f1 clear_terminal scroll active\n            # Clear everything on screen up to the line with the cursor or the start of the current prompt (needs shell integration)\n            # Useful for clearing the screen up to the shell prompt and moving the shell prompt to the top of the screen.\n            map f1 clear_terminal to_cursor active\n            # Same as above except cleared lines are moved into scrollback\n            map f1 clear_terminal to_cursor_scroll active\n            # Erase the last command and its output (needs shell integration to work)\n            map f1 clear_terminal last_command active\n        ''')\n    def clear_terminal(self, action: str, only_active: bool) -> None:\n        if only_active:\n            windows = []\n            w = self.window_for_dispatch or self.active_window\n            if w is not None:\n                windows.append(w)\n        else:\n            windows = list(self.all_windows)\n        if action == 'reset':\n            for w in windows:\n                w.clear_screen(reset=True, scrollback=True)\n        elif action == 'scrollback':\n            for w in windows:\n                w.screen.clear_scrollback()\n        elif action == 'clear':\n            for w in windows:\n                w.clear_screen()\n        elif action == 'scroll':\n            for w in windows:\n                w.scroll_prompt_to_top()\n        elif action == 'to_cursor':\n            for w in windows:\n                w.scroll_prompt_to_top(clear_scrollback=True)\n        elif action == 'to_cursor_scroll':\n            for w in windows:\n                w.scroll_prompt_to_top(clear_scrollback=False)\n        elif action == 'last_command':\n            for w in windows:\n                w.screen.erase_last_command()\n        else:\n            self.show_error(_('Unknown clear type'), _('The clear type: {} is unknown').format(action))\n\n    def increase_font_size(self) -> None:  # legacy\n        cfs = global_font_size()\n        self.set_font_size(min(get_options().font_size * 5, cfs + 2.0))\n\n    def decrease_font_size(self) -> None:  # legacy\n        cfs = global_font_size()\n        self.set_font_size(max(MINIMUM_FONT_SIZE, cfs - 2.0))\n\n    def restore_font_size(self) -> None:  # legacy\n        self.set_font_size(get_options().font_size)\n\n    def set_font_size(self, new_size: float) -> None:  # legacy\n        self.change_font_size(True, None, new_size)\n\n    @ac('fs', '''\n        Change the font size for the current or all OS Windows\n\n        See :ref:`conf-kitty-shortcuts.fonts` for details.\n        ''')\n    def change_font_size(self, all_windows: bool, increment_operation: str | None, amt: float) -> None:\n        def calc_new_size(old_size: float) -> float:\n            new_size = old_size\n            if amt == 0:\n                new_size = get_options().font_size\n            else:\n                if increment_operation:\n                    match increment_operation:\n                        case '+':\n                            new_size += amt\n                        case '-':\n                            new_size -= amt\n                        case '*':\n                            new_size *= amt\n                        case '/':\n                            new_size /= amt\n                        case _:\n                            pass  # no-op\n                else:\n                    new_size = amt\n                new_size = max(MINIMUM_FONT_SIZE, min(new_size, get_options().font_size * 10))\n            return new_size\n\n        if all_windows:\n            current_global_size = global_font_size()\n            new_size = calc_new_size(current_global_size)\n            if new_size != current_global_size:\n                global_font_size(new_size)\n            os_windows = list(self.os_window_map.keys())\n        else:\n            os_windows = []\n            w = self.window_for_dispatch or self.active_window\n            if w is not None:\n                os_windows.append(w.os_window_id)\n        if os_windows:\n            final_windows = {}\n            for wid in os_windows:\n                current_size = os_window_font_size(wid)\n                if current_size:\n                    new_size = calc_new_size(current_size)\n                    if new_size != current_size:\n                        final_windows[wid] = new_size\n            if final_windows:\n                self._change_font_size(final_windows)\n\n    def _change_font_size(self, sz_map: dict[int, float]) -> None:\n        for os_window_id, sz in sz_map.items():\n            tm = self.os_window_map.get(os_window_id)\n            if tm is not None:\n                os_window_font_size(os_window_id, sz)\n                tm.resize()\n\n    def on_dpi_change(self, os_window_id: int) -> None:\n        tm = self.os_window_map.get(os_window_id)\n        if tm is not None:\n            sz = os_window_font_size(os_window_id)\n            if sz:\n                os_window_font_size(os_window_id, sz, True)\n                for tab in tm:\n                    for window in tab:\n                        window.on_dpi_change(sz)\n                tm.resize()\n\n    def _set_os_window_background_opacity(self, os_window_id: int, opacity: float) -> None:\n        change_background_opacity(os_window_id, max(0.0, min(opacity, 1.0)))\n\n    @ac('win', '''\n        Set the background opacity for the active OS Window\n\n        For example::\n\n            map f1 set_background_opacity +0.1\n            map f2 set_background_opacity -0.1\n            map f3 set_background_opacity 0.5\n        ''')\n    def set_background_opacity(self, opacity: str) -> None:\n        window = self.window_for_dispatch or self.active_window\n        if window is None or not opacity:\n            return\n        if not get_options().dynamic_background_opacity:\n            self.show_error(\n                    _('Cannot change background opacity'),\n                    _('You must set the dynamic_background_opacity option in kitty.conf to be able to change background opacity'))\n            return\n        os_window_id = window.os_window_id\n        if opacity[0] in '+-':\n            old_opacity = background_opacity_of(os_window_id)\n            if old_opacity is None:\n                return\n            fin_opacity = old_opacity + float(opacity)\n        elif opacity == 'default':\n            fin_opacity = get_options().background_opacity\n        else:\n            fin_opacity = float(opacity)\n        self._set_os_window_background_opacity(os_window_id, fin_opacity)\n\n    @property\n    def active_tab_manager(self) -> TabManager | None:\n        os_window_id = current_focused_os_window_id()\n        if os_window_id <= 0:\n            os_window_id = last_focused_os_window_id()\n        if os_window_id <= 0:\n            q = current_os_window()\n            if q is not None:\n                os_window_id = q\n        return self.os_window_map.get(os_window_id)\n\n    @property\n    def active_tab(self) -> Tab | None:\n        tm = self.active_tab_manager\n        return None if tm is None else tm.active_tab\n\n    @property\n    def active_window(self) -> Window | None:\n        t = self.active_tab\n        return None if t is None else t.active_window\n\n    @property\n    def active_session(self) -> str:\n        if t := self.active_tab:\n            if w := t.active_window:\n                return w.created_in_session_name or t.created_in_session_name\n            return t.created_in_session_name\n        return ''\n\n    @property\n    def all_loaded_session_names(self) -> Iterator[str]:\n        seen = set()\n        for w in self.all_windows:\n            if w.created_in_session_name and w.created_in_session_name not in seen:\n                seen.add(w.created_in_session_name)\n                yield w.created_in_session_name\n\n    def refresh_active_tab_bar(self) -> bool:\n        tm = self.active_tab_manager\n        if tm:\n            tm.update_tab_bar_data()\n            tm.mark_tab_bar_dirty()\n            return True\n        return False\n\n    @ac('misc', '''\n    End the current keyboard mode switching to the previous mode.\n    ''')\n    def pop_keyboard_mode(self) -> bool:\n        return self.mappings.pop_keyboard_mode()\n\n    @ac('misc', '''\n    Switch to the specified keyboard mode, pushing it onto the stack of keyboard modes.\n    ''')\n    def push_keyboard_mode(self, new_mode: str) -> None:\n        self.mappings.push_keyboard_mode(new_mode)\n\n    def dispatch_possible_special_key(self, ev: KeyEvent) -> bool:\n        return self.mappings.dispatch_possible_special_key(ev)\n\n    def cancel_current_visual_select(self) -> None:\n        if self.current_visual_select:\n            self.current_visual_select.cancel()\n            self.current_visual_select = None\n            self.mappings.pop_keyboard_mode_if_is('__visual_select__')\n\n    def visual_window_select_action(\n        self, tab: Tab,\n        callback: Callable[[Tab | None, Window | None], None],\n        choose_msg: str,\n        only_window_ids: Container[int] = (),\n        reactivate_prev_tab: bool = False\n    ) -> None:\n        import string\n        self.cancel_current_visual_select()\n        initial_tab_id: int | None = None\n        initial_os_window_id = current_os_window()\n        tm = tab.tab_manager_ref()\n        if tm is not None:\n            if tm.active_tab is not None:\n                initial_tab_id = tm.active_tab.id\n            tm.set_active_tab(tab)\n        if initial_os_window_id != tab.os_window_id:\n            self.focus_os_window(tab.os_window_id, False)\n        self.current_visual_select = VisualSelect(tab.id, tab.os_window_id, initial_tab_id, initial_os_window_id, choose_msg, callback, reactivate_prev_tab)\n        if tab.current_layout.only_active_window_visible:\n            self.select_window_in_tab_using_overlay(tab, choose_msg, only_window_ids)\n            return\n        km = KeyboardMode('__visual_select__')\n        km.on_action = 'end'\n        km.keymap[SingleKey(key=GLFW_FKEY_ESCAPE)].append(KeyDefinition(definition='visual_window_select_action_trigger 0'))\n        fmap = get_name_to_functional_number_map()\n        alphanumerics = get_options().visual_window_select_characters\n        for idx, window in tab.windows.iter_windows_with_number(only_visible=True):\n            if only_window_ids and window.id not in only_window_ids:\n                continue\n            ac = KeyDefinition(definition=f'visual_window_select_action_trigger {window.id}')\n            if idx >= len(alphanumerics):\n                break\n            ch = alphanumerics[idx]\n            window.screen.set_window_char(ch)\n            self.current_visual_select.window_ids.append(window.id)\n            for mods in (0, GLFW_MOD_CONTROL, GLFW_MOD_CONTROL | GLFW_MOD_SHIFT, GLFW_MOD_SUPER, GLFW_MOD_ALT, GLFW_MOD_SHIFT):\n                km.keymap[SingleKey(mods=mods, key=ord(ch.lower()))].append(ac)\n                if ch in string.digits:\n                    km.keymap[SingleKey(mods=mods, key=fmap[f'KP_{ch}'])].append(ac)\n        if len(self.current_visual_select.window_ids) > 1:\n            self.mappings._push_keyboard_mode(km)\n            redirect_mouse_handling(True)\n            self.mouse_handler = self.visual_window_select_mouse_handler\n        else:\n            self.visual_window_select_action_trigger(self.current_visual_select.window_ids[0] if self.current_visual_select.window_ids else 0)\n            self.ring_bell_if_allowed(tab.os_window_id)\n\n    def ring_bell_if_allowed(self, os_window_id: int = 0) -> bool:\n        if get_options().enable_audio_bell:\n            ring_bell(os_window_id or getattr(self.active_tab_manager, 'os_window_id', 0))\n            return True\n        return False\n\n    def visual_window_select_action_trigger(self, window_id: int = 0) -> None:\n        if self.current_visual_select:\n            self.current_visual_select.trigger(int(window_id))\n        self.current_visual_select = None\n\n    def visual_window_select_mouse_handler(self, ev: WindowSystemMouseEvent) -> None:\n        tab = self.active_tab\n        def trigger(window_id: int = 0) -> None:\n            self.visual_window_select_action_trigger(window_id)\n            self.mappings.pop_keyboard_mode_if_is('__visual_select__')\n\n        if ev.button == GLFW_MOUSE_BUTTON_LEFT and ev.action == GLFW_PRESS and ev.window_id:\n            w = self.window_id_map.get(ev.window_id)\n            if w is not None and tab is not None and w in tab:\n                if self.current_visual_select and self.current_visual_select.tab_id == tab.id:\n                    trigger(w.id)\n                else:\n                    trigger()\n                return\n        if ev.button > -1 and tab is not None:\n            trigger()\n\n    def mouse_event(\n        self, in_tab_bar: bool, window_id: int, action: int, modifiers: int, button: int,\n        currently_pressed_button: int, x: float, y: float\n    ) -> None:\n        if self.mouse_handler is not None:\n            ev = WindowSystemMouseEvent(in_tab_bar, window_id, action, modifiers, button, currently_pressed_button, x, y)\n            self.mouse_handler(ev)\n\n    def select_window_in_tab_using_overlay(self, tab: Tab, msg: str, only_window_ids: Container[int] = ()) -> Window | None:\n        windows: list[tuple[int | None, str]] = []\n        selectable_windows: list[tuple[int, str]] = []\n        for i, w in tab.windows.iter_windows_with_number(only_visible=False):\n            if only_window_ids and w.id not in only_window_ids:\n                windows.append((None, f'Current window: {w.title}' if w is self.active_window else w.title))\n            else:\n                windows.append((w.id, w.title))\n                selectable_windows.append((w.id, w.title))\n        if len(selectable_windows) < 2:\n            self.visual_window_select_action_trigger(selectable_windows[0][0] if selectable_windows else 0)\n            self.ring_bell_if_allowed(tab.os_window_id)\n            return None\n        cvs = self.current_visual_select\n\n        def chosen(ans: None | int | str) -> None:\n            q = self.current_visual_select\n            self.current_visual_select = None\n            if cvs and q is cvs:\n                q.trigger(ans if isinstance(ans, int) else 0)\n\n        return self.choose_entry(msg, windows, chosen, hints_args=('--hints-offset=0', '--alphabet', get_options().visual_window_select_characters.lower()))\n\n    @ac('win', '''\n        Resize the active window interactively\n\n        See :ref:`window_resizing` for details.\n        ''')\n    def start_resizing_window(self) -> None:\n        w = self.window_for_dispatch or self.active_window\n        if w is None:\n            return\n        overlay_window = self.run_kitten_with_metadata('resize_window', args=[\n            f'--horizontal-increment={get_options().window_resize_step_cells}',\n            f'--vertical-increment={get_options().window_resize_step_lines}'\n        ])\n        if overlay_window is not None:\n            overlay_window.allow_remote_control = True\n\n    def resize_layout_window(self, window: Window, increment: float, is_horizontal: bool, reset: bool = False) -> bool | None | str:\n        tab = window.tabref()\n        if tab is None or not increment:\n            return False\n        if reset:\n            tab.reset_window_sizes()\n            return None\n        return tab.resize_window_by(window.id, increment, is_horizontal)\n\n    def resize_os_window(self, os_window_id: int, width: int, height: int, unit: str, incremental: bool = False, metrics: 'None | OSWindowSize' = None) -> None:\n        if not incremental and (width < 0 or height < 0):\n            return\n        metrics = get_os_window_size(os_window_id) if metrics is None else metrics\n        if metrics is None:\n            return\n        if metrics['is_layer_shell']:\n            raise TypeError(f'The OS Window {os_window_id} is a panel and cannot be resized')\n        has_window_scaling = is_macos or is_wayland()\n        w, h = get_new_os_window_size(metrics, width, height, unit, incremental, has_window_scaling)\n        set_os_window_size(os_window_id, w, h)\n\n    def tab_for_id(self, tab_id: int) -> Tab | None:\n        for tm in self.os_window_map.values():\n            tab = tm.tab_for_id(tab_id)\n            if tab is not None:\n                return tab\n        return None\n\n    def default_bg_changed_for(self, window_id: int, via_escape_code: bool = False) -> None:\n        w = self.window_id_map.get(window_id)\n        if w is not None:\n            w.on_color_scheme_preference_change(via_escape_code=via_escape_code)\n            tm = self.os_window_map.get(w.os_window_id)\n            if tm is not None:\n                tm.update_tab_bar_data()\n                tm.mark_tab_bar_dirty()\n                t = tm.tab_for_id(w.tab_id)\n                if t is not None:\n                    t.relayout_borders()\n                set_os_window_chrome(w.os_window_id)\n\n    def dispatch_action(\n        self,\n        key_action: KeyAction,\n        window_for_dispatch: Window | None = None,\n        dispatch_type: str = 'KeyPress'\n    ) -> bool:\n\n        def report_match(f: Callable[..., Any]) -> None:\n            if self.args.debug_keyboard:\n                prefix = '\\n' if dispatch_type == 'KeyPress' else ''\n                end = ', ' if dispatch_type == 'KeyPress' else '\\n'\n                timed_debug_print(f'{prefix}\\x1b[35m{dispatch_type}\\x1b[m matched action:', func_name(f), end=end)\n\n        if key_action is not None:\n            f = getattr(self, key_action.func, None)\n            if f is not None:\n                orig, self.window_for_dispatch = self.window_for_dispatch, window_for_dispatch\n                try:\n                    report_match(f)\n                    passthrough = f(*key_action.args)\n                    if passthrough is not True:\n                        return True\n                finally:\n                    self.window_for_dispatch = orig\n        if window_for_dispatch is None:\n            tab = self.active_tab\n            window = self.active_window\n        else:\n            window = window_for_dispatch\n            tab = window.tabref()\n        if tab is None or window is None:\n            return False\n        if key_action is not None:\n            f = getattr(tab, key_action.func, getattr(window, key_action.func, None))\n            if f is not None:\n                passthrough = f(*key_action.args)\n                report_match(f)\n                if passthrough is not True:\n                    return True\n        return False\n\n    def user_menu_action(self, defn: str) -> None:\n        ' Callback from user actions in the macOS global menu bar or other menus '\n        self.combine(defn)\n\n    @ac('misc', '''\n        Combine multiple actions and map to a single keypress\n\n        The syntax is::\n\n            map key combine <separator> action1 <separator> action2 <separator> action3 ...\n\n        For example::\n\n            map kitty_mod+e combine : new_window : next_layout\n            map kitty_mod+e combine | new_tab | goto_tab -1\n        ''')\n    def combine(self, action_definition: str, window_for_dispatch: Window | None = None, dispatch_type: str = 'KeyPress', raise_error: bool = False) -> bool:\n        consumed = False\n        if action_definition:\n            try:\n                actions = get_options().alias_map.resolve_aliases(action_definition, 'map' if dispatch_type == 'KeyPress' else 'mouse_map')\n            except Exception as e:\n                self.show_error('Failed to parse action', f'{action_definition}\\n{e}')\n                return True\n            if actions:\n                window_for_dispatch = window_for_dispatch or self.window_for_dispatch\n                try:\n                    if self.dispatch_action(actions[0], window_for_dispatch, dispatch_type):\n                        consumed = True\n                        if len(actions) > 1:\n                            self.drain_actions(list(actions[1:]), window_for_dispatch, dispatch_type)\n                except Exception as e:\n                    if raise_error:\n                        raise\n                    self.show_error('Key action failed', f'{actions[0].pretty()}\\n{e}')\n                    consumed = True\n        return consumed\n\n    def on_focus(self, os_window_id: int, focused: bool) -> None:\n        tm = self.os_window_map.get(os_window_id)\n        if tm is not None:\n            w = tm.active_window\n            if w is not None:\n                w.focus_changed(focused)\n                if is_macos and focused:\n                    cocoa_set_menubar_title(w.title or '')\n            tm.mark_tab_bar_dirty()\n            # Redraw borders when focus changes if draw_window_borders_for_single_window is enabled\n            # and there's only a single window (to show inactive border when OS window loses focus)\n            opts = get_options()\n            if opts.draw_window_borders_for_single_window and (tab := tm.active_tab) is not None and not tab.windows.has_more_than_one_visible_group:\n                tab.relayout_borders()\n\n    def on_activity_since_last_focus(self, window: Window) -> None:\n        os_window_id = window.os_window_id\n        tm = self.os_window_map.get(os_window_id)\n        if tm is not None:\n            tm.mark_tab_bar_dirty()\n\n    def update_tab_bar_data(self, os_window_id: int) -> None:\n        tm = self.os_window_map.get(os_window_id)\n        if tm is not None:\n            tm.update_tab_bar_data()\n\n    def on_drop_move(self, os_window_id: int, x: int, y: int, from_self: bool, is_leave: bool) -> None:\n        if (tm := self.os_window_map.get(os_window_id)) is None:\n            return\n        if from_self:\n            tab_id, drag_started = get_tab_being_dragged()[:2]\n            if tab_id and drag_started and (tab := self.tab_for_id(tab_id)):\n                central, tab_bar = viewport_for_window(os_window_id)[:2]\n                in_tab_bar = tab_bar.left <= x < tab_bar.right and tab_bar.top <= y < tab_bar.bottom\n                detach = not in_tab_bar or tab.os_window_id != tm.os_window_id or is_leave\n                change_drag_thumbnail(tab.os_window_id, 1 if detach else 0)\n                for q in self.all_tab_managers:\n                    is_dest = q is tm and (in_tab_bar or os_window_id != tab.os_window_id) and not is_leave\n                    q.on_tab_drop_move(tab_id, is_dest, x, y)\n\n    def on_drop(self, os_window_id: int, drop: dict[str, bytes] | int, from_self: bool, x: int, y: int) -> None:\n        if isinstance(drop, int):\n            import errno\n            code = errno.errorcode.get(drop, str(drop))\n            msg = 'Unknown error'\n            with suppress(ValueError):\n                msg = os.strerror(drop)\n            self.show_error(_('Drop failed'), f'[{code}] {msg}')\n            return\n        if (tm := self.os_window_map.get(os_window_id)) is None:\n            return\n        if (tidb := drop.get(f'application/net.kovidgoyal.kitty-tab-{os.getpid()}')) and (tab := self.tab_for_id(int(tidb))):\n            central, tab_bar = viewport_for_window(os_window_id)[:2]\n            in_tab_bar = tab_bar.left <= x < tab_bar.right and tab_bar.top <= y < tab_bar.bottom\n            if in_tab_bar or tab.os_window_id != tm.os_window_id:\n                tm.on_tab_drop(x, y)\n            else:\n                self._move_tab_to(tab)\n            set_tab_being_dragged()\n            for tm in self.all_tab_managers:\n                tm.on_tab_drop_move()\n                tm.layout_tab_bar()  # ensure tab bar is fully updated\n            return\n        central, tab_bar = viewport_for_window(os_window_id)[:2]\n        if central.left <= x < central.right and central.top <= y < central.bottom:\n            x -= central.left\n            y -= central.top\n            if tab := tm.active_tab:\n                for window in tab:\n                    if window.is_visible_in_layout:\n                        g = window.geometry\n                        if g.left <= x < g.right and g.top <= y < g.bottom:\n                            window.on_drop(drop)\n                            break\n        elif tab_bar.left <= x < tab_bar.right and tab_bar.top <= y < tab_bar.bottom:\n            if (tab_id := tm.tab_bar.tab_id_at(x)) and (tab := self.tab_for_id(tab_id)) and (w := tab.active_window):\n                w.on_drop(drop)\n\n    def on_drag_source_finished(\n        self, was_dropped: bool, was_canceled: bool, accepted_mime_type: str, action: int, data: dict[str, bytes] | None,\n        needs_toplevel_on_wayland: bool\n    ) -> None:\n        if (tab_id := int((data or {}).get(f'application/net.kovidgoyal.kitty-tab-{os.getpid()}', b'0').decode())\n        ) and get_tab_being_dragged()[0] == tab_id and (tab := self.tab_for_id(tab_id)):\n            if needs_toplevel_on_wayland:\n                for tm in self.all_tab_managers:\n                    if tm.tab_being_dropped:\n                        tm.on_tab_drop(0, 0, bypass_move=True)\n                        return\n            set_tab_being_dragged()\n            for tm in self.all_tab_managers:\n                tm.on_tab_drop_move()\n            if was_dropped:  # detach tab into new OS Window\n                self._move_tab_to(tab)\n\n    @ac('win', '''\n        Focus the nth OS window if positive or the previously active OS windows if negative. When the number is larger\n        than the number of OS windows focus the last OS window. A value of zero will refocus the currently focused OS window,\n        this is useful if focus is not on any kitty OS window at all, however, it will only work if the window manager\n        allows applications to grab focus. For example::\n\n            # focus the previously active kitty OS window\n            map ctrl+p nth_os_window -1\n            # focus the current kitty OS window (grab focus)\n            map ctrl+0 nth_os_window 0\n            # focus the first kitty OS window\n            map ctrl+1 nth_os_window 1\n            # focus the last kitty OS window\n            map ctrl+1 nth_os_window 999\n    ''')\n    def nth_os_window(self, num: int = 1) -> None:\n        if not self.os_window_map:\n            return\n        if num == 0:\n            os_window_id = current_focused_os_window_id() or last_focused_os_window_id()\n            self.focus_os_window(os_window_id)\n        elif num > 0:\n            ids = tuple(self.os_window_map.keys())\n            os_window_id = ids[min(num, len(ids)) - 1]\n            self.focus_os_window(os_window_id)\n        elif num < 0:\n            fc_map = os_window_focus_counters()\n            s = sorted(fc_map.keys(), key=fc_map.__getitem__)\n            if not s:\n                return\n            try:\n                os_window_id = s[num-1]\n            except IndexError:\n                os_window_id = s[0]\n            self.focus_os_window(os_window_id)\n\n    @ac('win', 'Close the currently active OS Window')\n    def close_os_window(self) -> None:\n        tm = self.active_tab_manager_with_dispatch\n        if tm is not None:\n            self.confirm_os_window_close(tm.os_window_id)\n\n    def confirm_os_window_close(self, os_window_id: int) -> None:\n        tm = self.os_window_map.get(os_window_id)\n        if tm is None:\n            self.mark_os_window_for_close(os_window_id)\n            return\n        if self.current_visual_select is not None and self.current_visual_select.os_window_id == os_window_id:\n            self.cancel_current_visual_select()\n        active_window = tm.active_window\n        windows = []\n        for tab in tm:\n            windows += list(tab)\n        msg, num_active_windows = self.close_windows_with_confirmation_msg(windows, active_window)\n        q = get_options().confirm_os_window_close[0]\n        num = num_active_windows if q < 0 else len(windows)\n        needs_confirmation = tm is not None and q != 0 and num >= abs(q)\n        if not needs_confirmation:\n            self.mark_os_window_for_close(os_window_id)\n            return\n        current_confirmation_window: Window | None = None\n        if tm.confirm_close_window_id:\n            for tab in tm:\n                for w in tab:\n                    if w.id == tm.confirm_close_window_id:\n                        current_confirmation_window = w\n                        break\n                if current_confirmation_window is not None:\n                    break\n        if current_confirmation_window:\n            self.set_active_window(current_confirmation_window, switch_os_window_if_needed=True)\n            return\n        msg = msg or _('It has {} windows?').format(num)\n        msg = _('Are you sure you want to close this OS Window?') + ' ' + msg\n        w = self.confirm(msg, self.handle_close_os_window_confirmation, os_window_id, window=tm.active_window, title=_('Close OS window'))\n        tm.confirm_close_window_id = w.id\n\n    def handle_close_os_window_confirmation(self, confirmed: bool, os_window_id: int) -> None:\n        tm = self.os_window_map.get(os_window_id)\n        if tm is not None:\n            tm.confirm_close_window_id = 0\n        if confirmed:\n            self.mark_os_window_for_close(os_window_id)\n        else:\n            self.mark_os_window_for_close(os_window_id, NO_CLOSE_REQUESTED)\n\n    def on_os_window_closed(self, os_window_id: int, x: int, y: int, viewport_width: int, viewport_height: int, is_layer_shell: bool) -> None:\n        tm = self.os_window_map.pop(os_window_id, None)\n        opts = get_options()\n        if not is_layer_shell:\n            if opts.remember_window_position and not is_wayland() and not self.os_window_map:\n                self.cached_values['window-pos'] = x, y\n                self.cached_values['monitor-workarea'] = glfw_get_monitor_workarea()\n            self.cached_values['window-size'] = viewport_width, viewport_height\n        if tm is not None:\n            tm.destroy()\n        for window_id in tuple(w.id for w in self.window_id_map.values() if getattr(w, 'os_window_id', None) == os_window_id):\n            self.window_id_map.pop(window_id, None)\n        if not self.os_window_map and is_macos:\n            cocoa_set_menubar_title('')\n        action = self.os_window_death_actions.pop(os_window_id, None)\n        if action is not None:\n            action()\n\n    quit_confirmation_window_id: int = 0\n\n    def _call_on_quit_watchers(self, data: dict[str, Any]) -> bool:\n        w = self.active_window\n        if w is None:\n            for window in self.window_id_map.values():\n                w = window\n                break\n        if w is None:\n            return True\n        data['aborted'] = False\n        for watcher in global_watchers().on_quit:\n            try:\n                watcher(self, w, data)\n            except Exception:\n                import traceback\n                traceback.print_exc()\n            if data.get('aborted'):\n                return False\n        return True\n\n    @ac('win', 'Quit, closing all windows')\n    def quit(self, *args: Any) -> None:\n        windows = []\n        for q in self.os_window_map.values():\n            for qt in q:\n                windows += list(qt)\n        active_window = self.active_window\n        msg, num_active_windows = self.close_windows_with_confirmation_msg(windows, active_window)\n        x = get_options().confirm_os_window_close[0]\n        num = num_active_windows if x < 0 else len(windows)\n        needs_confirmation = x != 0 and num >= abs(x)\n        if not needs_confirmation:\n            if not self._call_on_quit_watchers({'confirmed': True}):\n                return\n            set_application_quit_request(IMPERATIVE_CLOSE_REQUESTED)\n            return\n        if current_application_quit_request() == CLOSE_BEING_CONFIRMED:\n            if self.quit_confirmation_window_id and self.quit_confirmation_window_id in self.window_id_map:\n                w = self.window_id_map[self.quit_confirmation_window_id]\n                tab = w.tabref()\n                if tab is not None:\n                    ctm = tab.tab_manager_ref()\n                    if ctm is not None and tab in ctm and w in tab:\n                        self.focus_os_window(ctm.os_window_id)\n                        ctm.set_active_tab(tab)\n                        tab.set_active_window(w)\n                        return\n            return\n        if not self._call_on_quit_watchers({'confirmed': False}):\n            return\n        msg = msg or _('It has {} windows.').format(num)\n        w = self.confirm(_('Are you sure you want to quit kitty?') + ' '  + msg, self.handle_quit_confirmation, window=active_window, title=_('Quit kitty?'))\n        self.quit_confirmation_window_id = w.id\n        set_application_quit_request(CLOSE_BEING_CONFIRMED)\n\n    def handle_quit_confirmation(self, confirmed: bool) -> None:\n        self.quit_confirmation_window_id = 0\n        if confirmed:\n            if not self._call_on_quit_watchers({'confirmed': True}):\n                set_application_quit_request(NO_CLOSE_REQUESTED)\n                return\n        set_application_quit_request(IMPERATIVE_CLOSE_REQUESTED if confirmed else NO_CLOSE_REQUESTED)\n\n    def notify_on_os_window_death(self, address: str) -> None:\n        import socket\n        s = socket.socket(family=socket.AF_UNIX)\n        with suppress(Exception):\n            s.connect(address)\n            s.sendall(b'c')\n            with suppress(OSError):\n                s.shutdown(socket.SHUT_RDWR)\n            s.close()\n\n    def display_scrollback(self, window: Window, data: bytes | str, input_line_number: int = 0, title: str = '', report_cursor: bool = True) -> Window | None:\n\n        def prepare_arg(x: str) -> str:\n            x = x.replace('INPUT_LINE_NUMBER', str(input_line_number))\n            x = x.replace('CURSOR_LINE', str(window.screen.cursor.y + 1) if report_cursor else '0')\n            x = x.replace('CURSOR_COLUMN', str(window.screen.cursor.x + 1) if report_cursor else '0')\n            return x\n\n        cmd = list(map(prepare_arg, get_options().scrollback_pager))\n        if not os.path.isabs(cmd[0]):\n            resolved_exe = which(cmd[0])\n            if not resolved_exe:\n                log_error(f'The scrollback_pager {cmd[0]} was not found in PATH, falling back to less')\n                resolved_exe = which('less') or 'less'\n            cmd[0] = resolved_exe\n\n        if os.path.basename(cmd[0]) == 'less':\n            cmd.append('-+F')  # reset --quit-if-one-screen\n        tab = self.active_tab\n        if tab is not None:\n            bdata = data.encode('utf-8') if isinstance(data, str) else data\n            if is_macos and cmd[0] == '/usr/bin/less' and macos_version()[:2] < (12, 3):\n                # the system less before macOS 12.3 barfs up OSC codes, so sanitize them ourselves\n                sentinel = os.path.join(cache_dir(), 'less-is-new-enough')\n                if not os.path.exists(sentinel):\n                    if less_version(cmd[0]) >= 581:\n                        open(sentinel, 'w').close()\n                    else:\n                        bdata = re.sub(br'\\x1b\\].*?\\x1b\\\\', b'', bdata)\n\n            return tab.new_special_window(\n                SpecialWindow(cmd, bdata, title or _('History'), overlay_for=window.id, cwd=window.cwd_of_child),\n                copy_colors_from=self.active_window\n                )\n        return None\n\n    @ac('misc', 'Edit the kitty.conf config file in your favorite text editor')\n    def edit_config_file(self, *a: Any) -> None:\n        confpath = prepare_config_file_for_editing()\n        self.edit_file(confpath)\n\n    def edit_file(self, path: str) -> None:\n        editor_cmd = get_editor(get_options())\n        exe = editor_cmd[0]\n        if not os.path.isabs(exe):\n            exe = which(exe) or ''\n            if not exe or not os.access(exe, os.X_OK):\n                self.show_error(_('Cannot find editor'), _(\n                    'Could not edit the file {0} because the editor {1} was not found.').format(editor_cmd[0]))\n                return\n            editor_cmd[0] = exe\n        path = os.path.abspath(os.path.expanduser(path))\n        self.new_os_window(*editor_cmd, path)\n\n    def run_kitten_with_metadata(\n        self,\n        kitten: str,\n        args: Iterable[str] = (),\n        input_data: bytes | str | None = None,\n        window: Window | None = None,\n        custom_callback: Callable[[dict[str, Any], int, 'Boss'], None] | None = None,\n        action_on_removal: Callable[[int, 'Boss'], None] | None = None,\n        default_data: dict[str, Any] | None = None\n    ) -> Any:\n        from kittens.runner import CLIOnlyKitten, KittenMetadata, create_kitten_handler\n        is_wrapped = kitten in wrapped_kitten_names()\n        if window is None:\n            w = self.active_window\n            tab = self.active_tab\n        else:\n            w = window\n            tab = w.tabref() if w else None\n        args = list(args)\n        if w is not None and '@selection' in args and (sel := self.data_for_at(which='@selection', window=w)):\n            args = [sel if xa == '@selection' else xa for xa in args]\n        try:\n            end_kitten = create_kitten_handler(kitten, args)\n        except CLIOnlyKitten:\n            is_wrapped = True\n            end_kitten = KittenMetadata()\n\n        if end_kitten.no_ui:\n            return end_kitten.handle_result(None, w.id if w else 0, self)\n\n        if w is not None and tab is not None:\n            if not is_wrapped:\n                args[0:0] = [config_dir, kitten]\n            if input_data is None:\n                type_of_input = end_kitten.type_of_input\n                q = type_of_input.split('-') if type_of_input else []\n                if not q:\n                    data: bytes | None = None\n                elif q[0] in ('text', 'history', 'ansi', 'screen'):\n                    data = w.as_text(as_ansi='ansi' in q, add_history='history' in q, add_wrap_markers='screen' in q).encode('utf-8')\n                elif type_of_input == 'selection':\n                    sel = self.data_for_at(which='@selection', window=w)\n                    data = sel.encode('utf-8') if sel else None\n                elif q[0] in ('output', 'first_output', 'last_visited_output'):\n                    which = {\n                        'output': CommandOutput.last_run, 'first_output': CommandOutput.first_on_screen,\n                        'last_visited_output': CommandOutput.last_visited}[q[0]]\n                    data = w.cmd_output(which, as_ansi='ansi' in q, add_wrap_markers='screen' in q).encode('utf-8')\n                else:\n                    raise ValueError(f'Unknown type_of_input: {type_of_input}')\n            else:\n                data = input_data if isinstance(input_data, bytes) else input_data.encode('utf-8')\n            copts = common_opts_as_dict(get_options())\n            env = {\n                'KITTY_COMMON_OPTS': json.dumps(copts),\n                'KITTY_CHILD_PID': str(w.child.pid),\n                'OVERLAID_WINDOW_LINES': str(w.screen.lines),\n                'OVERLAID_WINDOW_COLS': str(w.screen.columns),\n            }\n            if is_wrapped:\n                cmd = [kitten_exe(), kitten]\n                env['KITTEN_RUNNING_AS_UI'] = '1'\n                env['KITTY_CONFIG_DIRECTORY'] = config_dir\n                if w is not None:\n                    env['KITTY_BASIC_COLORS'] = json.dumps(w.screen.color_profile.basic_colors())\n            else:\n                cmd = [kitty_exe(), '+runpy', 'from kittens.runner import main; main()']\n                env['PYTHONWARNINGS'] = 'ignore'\n            remote_control_fd = -1\n            if end_kitten.allow_remote_control:\n                remote_control_passwords: dict[str, Sequence[str]] | None = None\n                initial_data = b''\n                if end_kitten.remote_control_password:\n                    from secrets import token_hex\n                    p = token_hex(16)\n                    remote_control_passwords = {p: end_kitten.remote_control_password if isinstance(end_kitten.remote_control_password, str) else ''}\n                    initial_data = p.encode() + b'\\n'\n                remote = self.add_fd_based_remote_control(remote_control_passwords, initial_data)\n                remote_control_fd = remote.fileno()\n            try:\n                overlay_window = tab.new_special_window(\n                    SpecialWindow(\n                        cmd + args,\n                        stdin=data,\n                        env=env,\n                        cwd=w.cwd_of_child,\n                        overlay_for=w.id,\n                        overlay_behind=end_kitten.has_ready_notification,\n                    ),\n                    copy_colors_from=w, remote_control_fd=remote_control_fd,\n                )\n            finally:\n                if end_kitten.allow_remote_control:\n                    remote.close()\n            wid = w.id\n            overlay_window.actions_on_close.append(partial(self.on_kitten_finish, wid, custom_callback or end_kitten.handle_result, default_data=default_data))\n            overlay_window.open_url_handler = end_kitten.open_url_handler\n            if action_on_removal is not None:\n\n                def callback_wrapper(*a: Any) -> None:\n                    if action_on_removal is not None:\n                        action_on_removal(wid, self)\n                overlay_window.actions_on_removal.append(callback_wrapper)\n            return overlay_window\n    _run_kitten = run_kitten_with_metadata\n\n    @ac('misc', 'Run the specified kitten. See :doc:`/kittens/custom` for details')\n    def kitten(self, kitten: str, *kargs: str) -> None:\n        self.run_kitten_with_metadata(kitten, kargs, window=self.window_for_dispatch)\n\n    def run_kitten(self, kitten: str, *args: str) -> None:\n        self.run_kitten_with_metadata(kitten, args)\n\n    def on_kitten_finish(\n        self, target_window_id: int, end_kitten: Callable[[dict[str, Any], int, 'Boss'], None],\n        source_window: Window,\n        default_data: dict[str, Any] | None = None\n    ) -> None:\n        data, source_window.kitten_result = source_window.kitten_result, None\n        if data is None:\n            data = default_data\n        if data is not None:\n            end_kitten(data, target_window_id, self)\n\n    @ac('misc', 'Input an arbitrary unicode character. See :doc:`/kittens/unicode_input` for details.')\n    def input_unicode_character(self) -> None:\n        self.run_kitten_with_metadata('unicode_input', window=self.window_for_dispatch)\n\n    @ac('misc', '''\n        Browse and trigger keyboard shortcuts and actions in a searchable overlay.\n        ''')\n    def command_palette(self) -> None:\n        from kittens.command_palette.main import collect_keys_data\n        data = collect_keys_data(get_options())\n        self.run_kitten_with_metadata('command-palette', input_data=json.dumps(data), window=self.window_for_dispatch)\n\n    @ac(\n        'tab', '''\n        Change the title of the active tab interactively, by typing in the new title.\n        If you specify an argument to this action then that is used as the title instead of asking for it.\n        Use the empty string (\"\") to reset the title to default. Use a space (\" \") to indicate that the\n        prompt should not be pre-filled. For example::\n\n            # interactive usage\n            map f1 set_tab_title\n            # set a specific title\n            map f2 set_tab_title some title\n            # reset to default\n            map f3 set_tab_title \"\"\n            # interactive usage without prefilled prompt\n            map f3 set_tab_title \" \"\n        '''\n    )\n    def set_tab_title(self, title: str | None = None) -> None:\n        tab = self.window_for_dispatch.tabref() if self.window_for_dispatch else self.active_tab\n        if tab:\n            if title is not None and title not in ('\" \"', \"' '\"):\n                if title in ('\"\"', \"''\"):\n                    title = ''\n                tab.set_title(title)\n                return\n            if (w := self.window_id_map.get(tab.renaming_in_window)) is not None and w in tab:\n                tab.set_active_window(w)\n                return\n            prefilled = (tab.name or tab.title).strip()\n            tab_id = tab.id\n\n            def on_rename_done(new_title: str) -> None:\n                if (tab := self.tab_for_id(tab_id)) is not None:\n                    tab.renaming_in_window = 0\n                    tab.set_title(new_title)\n\n            overlay_window = self.get_line(\n                _('Enter the new title for this tab below. An empty title will cause the default title to be used.'),\n                on_rename_done, window=tab.active_window, initial_value=prefilled, window_title=_('Rename tab'))\n            if overlay_window is not None:\n                tab.renaming_in_window = overlay_window.id\n\n    def create_special_window_for_show_error(self, title: str, msg: str, overlay_for: int | None = None) -> SpecialWindowInstance:\n        ec = sys.exc_info()\n        tb = ''\n        if ec != (None, None, None):\n            import traceback\n            tb = traceback.format_exc()\n        cmd = [kitten_exe(), '__show_error__', '--title', title]\n        env = {}\n        env['KITTEN_RUNNING_AS_UI'] = '1'\n        env['KITTY_CONFIG_DIRECTORY'] = config_dir\n        return SpecialWindow(\n            cmd, override_title=title,\n            stdin=json.dumps({'msg': msg, 'tb': tb}).encode(),\n            env=env,\n            overlay_for=overlay_for,\n        )\n\n    @ac('misc', 'Show an error message with the specified title and text')\n    def show_error(self, title: str, msg: str) -> None:\n        w = self.window_for_dispatch or self.active_window\n        if w:\n            tab = w.tabref()\n            if w is not None and tab is not None:\n                tab.new_special_window(self.create_special_window_for_show_error(title, msg, w.id), copy_colors_from=w)\n\n    @ac('mk', 'Create a new marker')\n    def create_marker(self) -> None:\n        w = self.window_for_dispatch or self.active_window\n        if w:\n            spec = None\n\n            def done(data: dict[str, Any], target_window_id: int, self: Boss) -> None:\n                nonlocal spec\n                spec = data['response']\n\n            def done2(target_window_id: int, self: Boss) -> None:\n                w = self.window_id_map.get(target_window_id)\n                if w is not None and spec:\n                    try:\n                        w.set_marker(spec)\n                    except Exception as err:\n                        self.show_error(_('Invalid marker specification'), str(err))\n\n            self.run_kitten_with_metadata('ask', [\n                '--name=create-marker', '--message',\n                _('Create marker, for example:\\ntext 1 ERROR\\nSee {}\\n').format(website_url('marks'))\n                ],\n                custom_callback=done, action_on_removal=done2)\n\n    @ac('misc', 'Run the kitty shell to control kitty with commands')\n    def kitty_shell(self, window_type: str = 'window') -> None:\n        kw: dict[str, Any] = {}\n        cmd = [kitty_exe(), '@']\n        aw = self.window_for_dispatch or self.active_window\n        if aw is not None:\n            env = {'KITTY_SHELL_ACTIVE_WINDOW_ID': str(aw.id)}\n            at = self.active_tab\n            if at is not None:\n                env['KITTY_SHELL_ACTIVE_TAB_ID'] = str(at.id)\n            kw['env'] = env\n        if window_type == 'tab':\n            tab = self._new_tab(SpecialWindow(cmd, **kw))\n            if tab is not None:\n                for w in tab:\n                    window = w\n        elif window_type == 'os_window':\n            os_window_id = self._new_os_window(SpecialWindow(cmd, **kw))\n            for tab in self.os_window_map[os_window_id]:\n                for w in tab:\n                    window = w\n        elif window_type == 'overlay':\n            tab = self.active_tab\n            if aw is not None and tab is not None:\n                kw['overlay_for'] = aw.id\n                window = tab.new_special_window(SpecialWindow(cmd, **kw))\n        else:\n            tab = self.active_tab\n            if tab is not None:\n                window = tab.new_special_window(SpecialWindow(cmd, **kw))\n\n        path, ext = os.path.splitext(logo_png_file)\n        window.set_logo(f'{path}-128{ext}', position='bottom-right', alpha=0.25)\n        window.allow_remote_control = True\n\n    def switch_focus_to_in_active_tab(self, window_id: int) -> None:\n        tab = self.active_tab\n        if tab:\n            tab.set_active_window(window_id)\n\n    def drag_resize_start(\n        self, edges: int, x: float, y: float, window_id: int, cell_width: int, cell_height: int,\n    ) -> bool:\n        if (w := self.window_id_map.get(window_id)) and (tab := w.tabref()):\n            data = tab.current_layout.drag_resize_target_windows(w, x, y, edges, tab.windows)\n            if not edges & (LEFT_EDGE | RIGHT_EDGE):\n                data = data._replace(horizontal_id=None)\n            if not edges & (TOP_EDGE | BOTTOM_EDGE):\n                data = data._replace(vertical_id=None)\n            self.drag_resize_of_window = WindowResizeDrag(\n                is_active=True, tab_id=tab.id, data=data,\n                cell_width=cell_width, cell_height=cell_height, initial_x=x, initial_y=y,\n            )\n            for cw in tab:\n                cw.pause_resize_notifications_to_child()\n            return True\n        return False\n\n    def drag_resize_update(self, x: float, y: float) -> None:\n        if not (r := self.drag_resize_of_window) or not (tab := self.tab_for_id(r.tab_id)):\n            return\n        if (h := r.data.horizontal_id) is not None:\n            mult = 1 if r.data.width_increases_rightwards else -1\n            step_x = floor((x - r.initial_x) / r.cell_width) * mult\n            dx = step_x - r.last_step_x\n            if dx != 0:\n                if tab.drag_resize_window(h, float(dx), True):\n                    self.drag_resize_of_window = r._replace(last_step_x=step_x)\n        if (v := r.data.vertical_id) is not None:\n            mult = 1 if r.data.height_increases_downwards else -1\n            step_y = floor((y - r.initial_y) / r.cell_height) * mult\n            dy = step_y - r.last_step_y\n            if dy != 0:\n                if tab.drag_resize_window(v, float(dy), False):\n                    self.drag_resize_of_window = r._replace(last_step_y=step_y)\n\n    def drag_resize_end(self) -> None:\n        if tab := self.tab_for_id(self.drag_resize_of_window.tab_id):\n            for cw in tab:\n                cw.pause_resize_notifications_to_child(pause=False)\n        self.drag_resize_of_window = WindowResizeDrag()\n\n    def open_kitty_website(self) -> None:\n        self.open_url(website_url())\n\n    @ac('misc', 'Open the specified URL')\n    def open_url(self, url: str, program: str | list[str] | None = None, cwd: str | None = None) -> None:\n        if not url:\n            return\n        if isinstance(program, str):\n            program = to_cmdline(program)\n        found_action = False\n        if program is None:\n            from .open_actions import actions_for_url\n            actions = list(actions_for_url(url))\n            if actions:\n                found_action = True\n                self.dispatch_action(actions.pop(0))\n                if actions:\n                    self.drain_actions(actions)\n        if not found_action:\n            extra_env = {}\n            if self.listening_on:\n                extra_env['KITTY_LISTEN_ON'] = self.listening_on\n\n            def doit(activation_token: str = '') -> None:\n                if activation_token:\n                    extra_env['XDG_ACTIVATION_TOKEN'] = activation_token\n                open_url(url, program or get_options().open_url_with, cwd=cwd, extra_env=extra_env)\n\n            if is_wayland():\n                run_with_activation_token(doit)\n            else:\n                doit()\n\n    @ac('misc', 'Sleep for the specified time period. Suffix can be s for seconds, m, for minutes, h for hours and d for days. The time can be fractional.')\n    def sleep(self, sleep_time: float = 1.0) -> None:\n        sleep(sleep_time)\n\n    @ac('misc', 'Click a URL using the keyboard')\n    def open_url_with_hints(self) -> None:\n        self.run_kitten_with_metadata('hints', window=self.window_for_dispatch)\n\n    def drain_actions(self, actions: list[KeyAction], window_for_dispatch: Window | None = None, dispatch_type: str = 'KeyPress') -> None:\n\n        def callback(timer_id: int | None) -> None:\n            self.dispatch_action(actions.pop(0), window_for_dispatch, dispatch_type)\n            if actions:\n                self.drain_actions(actions)\n        add_timer(callback, 0, False)\n\n    def destroy(self) -> None:\n        self.shutting_down = True\n        self.child_monitor.shutdown_monitor()\n        self.set_update_check_process()\n        self.update_check_process = None\n        del self.child_monitor\n        for tm in self.os_window_map.values():\n            tm.destroy()\n        self.os_window_map = {}\n        destroy_global_data()\n\n    def paste_to_active_window(self, text: str) -> None:\n        if text:\n            w = self.active_window\n            if w is not None:\n                w.paste_with_actions(text)\n\n    @ac('cp', 'Paste from the clipboard to the active window')\n    def paste_from_clipboard(self) -> None:\n        w = self.window_for_dispatch or self.active_window\n        if w is not None:\n            if w.send_paste_event():\n                return\n            text = get_clipboard_string()\n            if text:\n                w.paste_with_actions(text)\n\n    def current_primary_selection(self) -> str:\n        return get_primary_selection() if supports_primary_selection else ''\n\n    def current_primary_selection_or_clipboard(self) -> str:\n        return get_primary_selection() if supports_primary_selection else get_clipboard_string()\n\n    @ac('cp', 'Paste from the primary selection, if present, otherwise the clipboard to the active window')\n    def paste_from_selection(self) -> None:\n        w = self.window_for_dispatch or self.active_window\n        if w is not None:\n            if w.send_paste_event(is_primary_selection=True):\n                return\n            text = self.current_primary_selection_or_clipboard()\n            if text:\n                w.paste_with_actions(text)\n\n    def set_primary_selection(self) -> None:\n        w = self.active_window\n        if w is not None and not w.destroyed:\n            text = w.text_for_selection()\n            if text:\n                set_primary_selection(text)\n                self.handle_clipboard_loss('primary', w.id)\n                if get_options().copy_on_select:\n                    self.copy_to_buffer(get_options().copy_on_select)\n\n    def get_active_selection(self) -> str | None:\n        w = self.active_window\n        if w is not None and not w.destroyed:\n            return w.text_for_selection()\n        return None\n\n    def has_active_selection(self) -> bool:\n        w = self.active_window\n        if w is not None and not w.destroyed:\n            return w.has_selection()\n        return False\n\n    def set_clipboard_buffer(self, buffer_name: str, text: str | None = None) -> None:\n        if buffer_name:\n            if text is not None:\n                self.clipboard_buffers[buffer_name] = text\n            elif buffer_name in self.clipboard_buffers:\n                del self.clipboard_buffers[buffer_name]\n\n    def get_clipboard_buffer(self, buffer_name: str) -> str | None:\n        return self.clipboard_buffers.get(buffer_name)\n\n    @ac('cp', '''\n        Copy the selection from the active window to the specified buffer\n\n        See :ref:`cpbuf` for details.\n        ''')\n    def copy_to_buffer(self, buffer_name: str) -> None:\n        w = self.window_for_dispatch or self.active_window\n        if w is not None and not w.destroyed:\n            text = w.text_for_selection()\n            if text:\n                if buffer_name == 'clipboard':\n                    set_clipboard_string(text)\n                    self.handle_clipboard_loss('clipboard', w.id)\n                elif buffer_name == 'primary':\n                    set_primary_selection(text)\n                    self.handle_clipboard_loss('primary', w.id)\n                else:\n                    self.set_clipboard_buffer(buffer_name, text)\n\n    @ac('cp', '''\n        Paste from the specified buffer to the active window\n\n        See :ref:`cpbuf` for details.\n        ''')\n    def paste_from_buffer(self, buffer_name: str) -> None:\n        if buffer_name == 'clipboard':\n            text: str | None = get_clipboard_string()\n        elif buffer_name == 'primary':\n            text = get_primary_selection()\n        else:\n            text = self.get_clipboard_buffer(buffer_name)\n        if text:\n            w = self.window_for_dispatch or self.active_window\n            if w:\n                w.paste_with_actions(text)\n\n    @ac('tab', '''\n        Go to the specified tab, by number, starting with 1\n\n        Zero and negative numbers go to previously active tabs.\n        Use the :ac:`select_tab` action to interactively select a tab\n        to go to.\n        ''')\n    def goto_tab(self, tab_num: int) -> None:\n        tm = self.active_tab_manager_with_dispatch\n        if tm is not None:\n            tm.goto_tab(tab_num - 1)\n\n    def set_active_tab(self, tab: Tab) -> bool:\n        tm = self.active_tab_manager\n        if tm is not None:\n            return tm.set_active_tab(tab)\n        return False\n\n    @ac('tab', 'Make the next tab active')\n    def next_tab(self) -> None:\n        tm = self.active_tab_manager_with_dispatch\n        if tm is not None:\n            tm.next_tab()\n\n    @ac('tab', 'Make the previous tab active')\n    def previous_tab(self) -> None:\n        tm = self.active_tab_manager_with_dispatch\n        if tm is not None:\n            tm.next_tab(-1)\n\n    prev_tab = previous_tab\n\n    def process_stdin_source(\n        self, window: Window | None = None,\n        stdin: str | None = None, copy_pipe_data: dict[str, Any] | None = None\n    ) -> tuple[dict[str, str] | None, bytes | None]:\n        w = window or self.active_window\n        if not w:\n            return None, None\n        env = None\n        input_data = None\n        if stdin:\n            add_wrap_markers = stdin.endswith('_wrap')\n            if add_wrap_markers:\n                stdin = stdin[:-len('_wrap')]\n            stdin = data_for_at(w, stdin, add_wrap_markers=add_wrap_markers)\n            if stdin is not None:\n                pipe_data = w.pipe_data(stdin, has_wrap_markers=add_wrap_markers) if w else None\n                if pipe_data:\n                    if copy_pipe_data is not None:\n                        copy_pipe_data.update(pipe_data)\n                    env = {\n                        'KITTY_PIPE_DATA':\n                        '{scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}'.format(**pipe_data)\n                    }\n                input_data = stdin.encode('utf-8')\n        return env, input_data\n\n    def data_for_at(self, which: str, window: Window | None = None, add_wrap_markers: bool = False) -> str | None:\n        window = window or self.active_window\n        if not window:\n            return None\n        return data_for_at(window, which, add_wrap_markers=add_wrap_markers)\n\n    def special_window_for_cmd(\n        self, cmd: list[str],\n        window: Window | None = None,\n        stdin: str | None = None,\n        cwd_from: CwdRequest | None = None,\n        as_overlay: bool = False\n    ) -> SpecialWindowInstance:\n        w = window or self.active_window\n        env, input_data = self.process_stdin_source(w, stdin)\n        cmdline = []\n        for arg in cmd:\n            if arg == '@selection' and w:\n                q = data_for_at(w, arg)\n                if not q:\n                    continue\n                arg = q\n            cmdline.append(arg)\n        overlay_for = w.id if w and as_overlay else None\n        return SpecialWindow(cmd, input_data, cwd_from=cwd_from, overlay_for=overlay_for, env=env)\n\n    def add_fd_based_remote_control(self, remote_control_passwords: dict[str, Sequence[str]] | None = None, initial_data: bytes = b'') -> socket.socket:\n        local, remote = socket.socketpair()\n        os.set_inheritable(remote.fileno(), True)\n        if initial_data:\n            local.send(initial_data)\n        lfd = os.dup(local.fileno())\n        local.close()\n        try:\n            peer_id = self.child_monitor.inject_peer(lfd)\n        except Exception:\n            os.close(lfd)\n            remote.close()\n            raise\n        self.peer_data_map[peer_id] = remote_control_passwords\n        return remote\n\n    def run_background_process(\n        self,\n        cmd: list[str],\n        cwd: str | None = None,\n        env: dict[str, str] | None = None,\n        stdin: bytes | None = None,\n        cwd_from: CwdRequest | None = None,\n        allow_remote_control: bool = False,\n        remote_control_passwords: dict[str, Sequence[str]] | None = None,\n        notify_on_death: Callable[[int, Exception | None], None] | None = None,  # guaranteed to be called only after event loop tick\n        stdout: int | None = None, stderr: int | None = None,\n    ) -> None:\n        env = env or None\n        if env:\n            env_ = default_env().copy()\n            env_.update(env)\n            env = env_\n        if cwd_from:\n            with suppress(Exception):\n                cwd = cwd_from.cwd_of_child\n\n        def add_env(key: str, val: str) -> None:\n            nonlocal env\n            if env is None:\n                env = default_env().copy()\n            env[key] = val\n\n        def doit(activation_token: str = '') -> None:\n            nonlocal env\n            pass_fds: list[int] = []\n            fds_to_close_on_launch_failure: list[int] = []\n            if allow_remote_control:\n                remote = self.add_fd_based_remote_control(remote_control_passwords)\n                pass_fds.append(remote.fileno())\n                add_env('KITTY_LISTEN_ON', f'fd:{remote.fileno()}')\n            if activation_token:\n                add_env('XDG_ACTIVATION_TOKEN', activation_token)\n            fds_to_close_on_launch_failure = list(pass_fds)\n            if stdout is not None and stdout > -1:\n                pass_fds.append(stdout)\n            if stderr is not None and stderr > -1 and stderr not in pass_fds:\n                pass_fds.append(stderr)\n\n            def run(stdin: int | None, stdout: int | None, stderr: int | None) -> None:\n                try:\n                    p = subprocess.Popen(\n                        cmd, env=env, cwd=cwd, preexec_fn=clear_handled_signals, pass_fds=pass_fds, stdin=stdin, stdout=stdout, stderr=stderr)\n                    if notify_on_death:\n                        self.background_process_death_notify_map[p.pid] = notify_on_death\n                        monitor_pid(p.pid)\n                except Exception as err:\n                    for fd in fds_to_close_on_launch_failure:\n                        with suppress(OSError):\n                            os.close(fd)\n                    if notify_on_death:\n                        def callback(err: Exception, timer_id: int | None) -> None:\n                            notify_on_death(-1, err)\n                        add_timer(partial(callback, err), 0, False)\n                    else:\n                        self.show_error(_('Failed to run background process'), _('Failed to run background process with error: {}').format(err))\n\n            r = subprocess.DEVNULL\n            if stdin:\n                r, w = safe_pipe(False)\n                fds_to_close_on_launch_failure.append(w)\n                pass_fds.append(r)\n            try:\n                run(r, stdout, stderr)\n                if stdin:\n                    thread_write(w, stdin)\n            finally:\n                if stdin:\n                    os.close(r)\n                if allow_remote_control:\n                    remote.close()\n\n        if is_wayland():\n            if not run_with_activation_token(doit):\n                doit()\n        else:\n            doit()\n\n    def pipe(self, source: str, dest: str, exe: str, *args: str) -> Window | None:\n        cmd = [exe] + list(args)\n        window = self.active_window\n        cwd_from = CwdRequest(window) if window else None\n\n        def create_window() -> SpecialWindowInstance:\n            return self.special_window_for_cmd(\n                cmd, stdin=source, as_overlay=dest == 'overlay', cwd_from=cwd_from)\n\n        if dest == 'overlay' or dest == 'window':\n            tab = self.active_tab\n            if tab is not None:\n                return tab.new_special_window(create_window())\n        elif dest == 'tab':\n            tm = self.active_tab_manager\n            if tm is not None:\n                tm.new_tab(special_window=create_window(), cwd_from=cwd_from)\n        elif dest == 'os_window':\n            self._new_os_window(create_window(), cwd_from=cwd_from)\n        elif dest in ('clipboard', 'primary'):\n            env, stdin = self.process_stdin_source(stdin=source, window=window)\n            if stdin:\n                if dest == 'clipboard':\n                    set_clipboard_string(stdin)\n                    self.handle_clipboard_loss('clipboard')\n                else:\n                    set_primary_selection(stdin)\n                    self.handle_clipboard_loss('primary')\n        else:\n            env, stdin = self.process_stdin_source(stdin=source, window=window)\n            self.run_background_process(cmd, cwd_from=cwd_from, stdin=stdin, env=env)\n        return None\n\n    def args_to_special_window(self, args: Iterable[str], cwd_from: CwdRequest | None = None) -> SpecialWindowInstance:\n        args = list(args)\n        stdin = None\n        w = self.active_window\n\n        if args[0].startswith('@') and args[0] != '@':\n            q = data_for_at(w, args[0]) or None\n            if q is not None:\n                stdin = q.encode('utf-8')\n            del args[0]\n\n        cmd = []\n        for arg in args:\n            if arg == '@selection':\n                q = data_for_at(w, arg)\n                if not q:\n                    continue\n                arg = q\n            cmd.append(arg)\n        return SpecialWindow(cmd, stdin, cwd_from=cwd_from)\n\n    def _new_tab(self, args: SpecialWindowInstance | Iterable[str], cwd_from: CwdRequest | None = None, as_neighbor: bool = False) -> Tab | None:\n        special_window = None\n        if args:\n            if isinstance(args, SpecialWindowInstance):\n                special_window = args\n            else:\n                special_window = self.args_to_special_window(args, cwd_from=cwd_from)\n        if not self.os_window_map:\n            self.add_os_window()\n        tm = self.active_tab_manager\n        if tm is None and not self.os_window_map:\n            os_window_id = self.add_os_window()\n            tm = self.os_window_map.get(os_window_id)\n        if tm is not None:\n            return tm.new_tab(special_window=special_window, cwd_from=cwd_from, as_neighbor=as_neighbor)\n        return None\n\n    def _create_tab(self, args: list[str], cwd_from: CwdRequest | None = None) -> None:\n        as_neighbor = False\n        if args and args[0].startswith('!'):\n            as_neighbor = 'neighbor' in args[0][1:].split(',')\n            args = args[1:]\n        self._new_tab(args, as_neighbor=as_neighbor, cwd_from=cwd_from)\n\n    @ac('tab', 'Create a new tab')\n    def new_tab(self, *args: str) -> None:\n        self._create_tab(list(args))\n\n    @ac('tab', '''\n        Create a new tab with working directory for the window in it set to the same as the active window.\n        The tab is added to the currently active :ref:`session <sessions>`, if any.\n    ''')\n    def new_tab_with_cwd(self, *args: str) -> None:\n        self._create_tab(list(args), cwd_from=CwdRequest(self.window_for_dispatch or self.active_window_for_cwd))\n\n    def new_tab_with_wd(self, wd: str | list[str], str_is_multiple_paths: bool = False) -> None:\n        if isinstance(wd, str):\n            wd = wd.split(os.pathsep) if str_is_multiple_paths else [wd]\n        for path in wd:\n            special_window = SpecialWindow(None, cwd=path)\n            self._new_tab(special_window)\n\n    def _new_window(self, args: list[str], cwd_from: CwdRequest | None = None) -> Window | None:\n        if not self.os_window_map:\n            os_window_id = self.add_os_window()\n            tm = self.os_window_map.get(os_window_id)\n            if tm is not None and not tm.active_tab:\n                tm.new_tab(empty_tab=True)\n        tab = self.active_tab\n        if tab is None:\n            return None\n        allow_remote_control = False\n        location = None\n        if args and args[0].startswith('!'):\n            location = args[0][1:].lower()\n            args = args[1:]\n        if args and args[0] == '@':\n            args = args[1:]\n            allow_remote_control = True\n        if args:\n            w = tab.new_special_window(\n                self.args_to_special_window(args, cwd_from=cwd_from),\n                location=location, allow_remote_control=allow_remote_control)\n        else:\n            w = tab.new_window(cwd_from=cwd_from, location=location, allow_remote_control=allow_remote_control)\n        if cwd_from is not None and (sw := cwd_from.window):\n            session_name = sw.created_in_session_name\n            if not session_name and (sw_tab := sw.tabref()):\n                session_name = sw_tab.created_in_session_name\n            w.created_in_session_name = session_name\n        return w\n\n    @ac('win', 'Create a new window')\n    def new_window(self, *args: str) -> None:\n        self._new_window(list(args))\n\n    @ac('win', '''\n        Create a new window with working directory same as that of the active window.\n        The new window will belong to the active :ref:`session <sessions>` if any.''')\n    def new_window_with_cwd(self, *args: str) -> None:\n        w = self.window_for_dispatch or self.active_window_for_cwd\n        if w is None:\n            return self.new_window(*args)\n        self._new_window(list(args), cwd_from=CwdRequest(w))\n\n    @ac('misc', '''\n        Launch the specified program in a new window/tab/etc.\n\n        See :doc:`launch` for details\n        ''')\n    def launch(self, *args: str) -> None:\n        from kitty.launch import launch, parse_launch_args\n        opts, args_ = parse_launch_args(args)\n        if args_ and ' ' in args_[0]:\n            # this can happen for example with map f1 launch $EDITOR when $EDITOR is not a single command\n            q = which(args_[0])\n            if not q or (q is args_[0] and not os.access(q, os.X_OK)):\n                args_[:1] = shlex_split(args_[0])\n        if self.window_for_dispatch:\n            opts.source_window = opts.source_window or f'id:{self.window_for_dispatch.id}'\n            opts.next_to = opts.next_to or f'id:{self.window_for_dispatch.id}'\n        launch(self, opts, args_)\n\n    @ac('tab', 'Move the active tab forward. You can also use drag and drop to re-arrange tabs.')\n    def move_tab_forward(self) -> None:\n        tm = self.active_tab_manager\n        if tm is not None:\n            tm.move_tab(1)\n\n    @ac('tab', 'Move the active tab backward. You can also use drag and drop to re-arrange tabs.')\n    def move_tab_backward(self) -> None:\n        tm = self.active_tab_manager_with_dispatch\n        if tm is not None:\n            tm.move_tab(-1)\n\n    @ac('misc', '''\n        Turn on/off ligatures in the specified window\n\n        See :opt:`disable_ligatures` for details\n        ''')\n    def disable_ligatures_in(self, where: str | Iterable[Window], strategy: int) -> None:\n        w = self.window_for_dispatch or self.active_window\n        if isinstance(where, str):\n            windows: list[Window] = []\n            if where == 'active':\n                if w:\n                    windows = [w]\n            elif where == 'all':\n                windows = list(self.all_windows)\n            elif where == 'tab':\n                if w:\n                    tab = w.tabref()\n                    if tab:\n                        windows = list(tab)\n        else:\n            windows = list(where)\n        for window in windows:\n            window.screen.disable_ligatures = strategy\n            window.refresh()\n\n    def apply_new_options(self, opts: Options) -> None:\n        bg_before = get_options().background\n        bg_colors_before = {w.id: w.screen.color_profile.default_bg for w in self.all_windows}\n        configured_color_scheme_changed = bg_before.is_dark != opts.background.is_dark\n        # Update options storage\n        set_options(opts, is_wayland(), self.args.debug_rendering, self.args.debug_font_fallback)\n        apply_options_update()\n        set_layout_options(opts)\n        set_default_env(opts.env.copy())\n        # Update font data\n        from .fonts.render import set_font_family\n        set_font_family(opts)\n        for os_window_id, tm in self.os_window_map.items():\n            if tm is not None:\n                os_window_font_size(os_window_id, opts.font_size, True)\n                tm.resize()\n        # Update key bindings\n        if is_macos:\n            from .fast_data_types import cocoa_clear_global_shortcuts\n            cocoa_clear_global_shortcuts()\n        self.mappings.update_keymap()\n        if is_macos:\n            from .fast_data_types import cocoa_recreate_global_menu\n            cocoa_recreate_global_menu()\n        # Update misc options\n        try:\n            set_background_image(opts.background_image, tuple(self.os_window_map), True, opts.background_image_layout)\n        except Exception as e:\n            log_error(f'Failed to set background image with error: {e}')\n        for tm in self.all_tab_managers:\n            tm.apply_options()\n        # Update colors\n        if theme_colors.has_applied_theme:\n            theme_colors.refresh()\n            if theme_colors.has_applied_theme:  # in case the theme file was deleted\n                assert theme_colors.applied_theme  # to make mypy happy\n                theme_colors.apply_theme(theme_colors.applied_theme, notify_on_bg_change=False)\n        for w in self.all_windows:\n            if w.screen.color_profile.default_bg != bg_colors_before.get(w.id):\n                self.default_bg_changed_for(w.id)\n            elif configured_color_scheme_changed:\n                # the application running in the window could have set the\n                # background color, so it wont change because of a config\n                # reload, but the application might still want to be notified\n                # that the user's color scheme preference has changed.\n                w.report_color_scheme_preference_if_wanted()\n            w.refresh(reload_all_gpu_data=True)\n        load_shader_programs.recompile_if_needed()\n\n    @ac('misc', '''\n        Reload the config file\n\n        If mapped without arguments reloads the default config file, otherwise loads\n        the specified config files, in order. Loading a config file *replaces* all\n        config options. For example::\n\n            map f5 load_config_file /path/to/some/kitty.conf\n        ''')\n    def load_config_file(self, *paths: str, apply_overrides: bool = True, overrides: Sequence[str] = ()) -> None:\n        from .cli import default_config_paths\n        from .config import load_config\n        old_opts = get_options()\n        prev_paths = old_opts.all_config_paths or default_config_paths(self.args.config)\n        paths = paths or prev_paths\n        bad_lines: list[BadLine] = []\n        final_overrides = old_opts.config_overrides if apply_overrides else ()\n        if overrides:\n            final_overrides += tuple(overrides)\n        opts = load_config(*paths, overrides=final_overrides or None, accumulate_bad_lines=bad_lines)\n        if bad_lines:\n            self.show_bad_config_lines(bad_lines)\n        self.apply_new_options(opts)\n        from .open_actions import clear_caches\n        clear_caches()\n        from .guess_mime_type import clear_mime_cache\n        clear_mime_cache()\n        store_effective_config()\n        from .tab_bar import clear_caches\n        clear_caches()\n\n    def safe_delete_temp_file(self, path: str) -> None:\n        if is_path_in_temp_dir(path):\n            with suppress(FileNotFoundError):\n                os.remove(path)\n\n    def is_ok_to_read_image_file(self, path: str, fd: int) -> bool:\n        return is_ok_to_read_image_file(path, fd)\n\n    def set_update_check_process(self, process: Optional['PopenType[bytes]'] = None) -> None:\n        if self.update_check_process is not None:\n            with suppress(Exception):\n                if self.update_check_process.poll() is None:\n                    self.update_check_process.kill()\n        self.update_check_process = process\n\n    def monitor_pid(self, pid: int, callback: Callable[[int, Exception | None], None]) -> None:\n        self.background_process_death_notify_map[pid] = callback\n        monitor_pid(pid)\n\n    def on_monitored_pid_death(self, pid: int, exit_status: int) -> None:\n        callback = self.background_process_death_notify_map.pop(pid, None)\n        if callback is not None:\n            try:\n                callback(exit_status, None)\n            except Exception:\n                import traceback\n                traceback.print_exc()\n            return\n\n        update_check_process = self.update_check_process\n        if update_check_process is not None and pid == update_check_process.pid:\n            self.update_check_process = None\n            from .update_check import process_current_release\n            try:\n                assert update_check_process.stdout is not None\n                raw = update_check_process.stdout.read().decode('utf-8')\n            except Exception as e:\n                log_error(f'Failed to read data from update check process, with error: {e}')\n            else:\n                try:\n                    process_current_release(raw)\n                except Exception as e:\n                    log_error(f'Failed to process update check data {raw!r}, with error: {e}')\n\n    def show_bad_config_lines(self, bad_lines: Iterable[BadLine], misc_errors: Iterable[str] = ()) -> None:\n\n        def format_bad_line(bad_line: BadLine) -> str:\n            return f'{bad_line.number}:{bad_line.exception} in line: {bad_line.line}\\n'\n\n        groups: dict[str, list[BadLine]] = {}\n        for bl in bad_lines:\n            groups.setdefault(bl.file, []).append(bl)\n        ans: list[str] = []\n        a = ans.append\n        for file in sorted(groups):\n            if file:\n                a(f'In file {file}:')\n            [a(format_bad_line(x)) for x in groups[file]]\n        if misc_errors:\n            a('In final effective configuration:')\n            for line in misc_errors:\n                a(line)\n        msg = '\\n'.join(ans).rstrip()\n        self.show_error(_('Errors parsing configuration'), msg)\n\n    @ac('misc', '''\n        Change colors in the specified windows\n\n        For details, see :ref:`at-set-colors`. For example::\n\n            map f5 set_colors --configured /path/to/some/config/file/colors.conf\n        ''')\n    def set_colors(self, *args: str) -> None:\n        from kitty.rc.base import PayloadGetter, command_for_name, parse_subcommand_cli\n        from kitty.remote_control import parse_rc_args\n        c = command_for_name('set_colors')\n        try:\n            opts, items = parse_subcommand_cli(c, ['set-colors'] + list(args))\n        except (Exception, SystemExit) as err:\n            self.show_error('Invalid set_colors mapping', str(err))\n            return\n        try:\n            payload = c.message_to_kitty(parse_rc_args([])[0], opts, items)\n        except (Exception, SystemExit) as err:\n            self.show_error('Failed to set colors', str(err))\n            return\n        c.response_from_kitty(self, self.window_for_dispatch or self.active_window, PayloadGetter(c, payload if isinstance(payload, dict) else {}))\n\n    def _move_window_to(\n        self,\n        window: Window | None = None,\n        target_tab_id: str | int | None = None,\n        target_os_window_id: str | int | None = None\n    ) -> None:\n        window = window or self.active_window\n        if not window:\n            return\n        src_tab = window.tabref()\n        if src_tab is None:\n            return\n        with self.suppress_focus_change_events():\n            if target_os_window_id == 'new':\n                target_os_window_id = self.add_os_window()\n                tm = self.os_window_map[target_os_window_id]\n                target_tab = tm.new_tab(empty_tab=True)\n            else:\n                target_os_window_id = target_os_window_id or current_os_window()\n                if isinstance(target_tab_id, str):\n                    if not isinstance(target_os_window_id, int):\n                        q = self.active_tab_manager\n                        assert q is not None\n                        tm = q\n                    else:\n                        tm = self.os_window_map[target_os_window_id]\n                    if target_tab_id.startswith('new'):\n                        # valid values for target_tab_id are 'new', 'new_after' and 'new_before'\n                        target_tab = tm.new_tab(empty_tab=True, location=(target_tab_id[4:] or 'last'))\n                    else:\n                        target_tab = tm.tab_at_location(target_tab_id) or tm.new_tab(empty_tab=True)\n                else:\n                    for tab in self.all_tabs:\n                        if tab.id == target_tab_id:\n                            target_tab = tab\n                            target_os_window_id = tab.os_window_id\n                            break\n                    else:\n                        return\n\n            target_tab.attach_windows(src_tab.detach_window(window))\n            self._cleanup_tab_after_window_removal(src_tab)\n            target_tab.make_active()\n\n    def _move_tab_to(self, tab: Tab | None = None, target_os_window_id: int | None = None) -> Tab | None:\n        tab = tab or self.active_tab\n        if tab is None:\n            return None\n        if target_os_window_id is None:\n            target_os_window_id = self.add_os_window()\n        tm = self.os_window_map[target_os_window_id]\n        target_tab = tm.new_tab(empty_tab=True)\n        target_tab.take_over_from(tab)\n        self._cleanup_tab_after_window_removal(tab)\n        target_tab.make_active()\n        return target_tab\n\n    def choose_entry(\n        self, title: str, entries: Iterable[tuple[_T | str | None, str]],\n        callback: Callable[[_T | str | None], None],\n        subtitle: str = '',\n        hints_args: tuple[str, ...] | None = None,\n    ) -> Window | None:\n        lines = [title, subtitle, ' '] if subtitle else [title, ' ']\n        idx_map: list[_T | str | None] = []\n        ans: str | _T | None = None\n        fmt = ': {1}'\n\n        for obj, text in entries:\n            idx_map.append(obj)\n            if obj is None:\n                lines.append(text)\n            else:\n                lines.append(fmt.format(len(idx_map), text))\n\n        def done(data: dict[str, Any], target_window_id: int, self: Boss) -> None:\n            nonlocal ans\n            ans = idx_map[int(data['groupdicts'][0]['index'])]\n\n        def done2(target_window_id: int, self: Boss) -> None:\n            callback(ans)\n\n        q = self.run_kitten_with_metadata(\n            'hints', args=(\n                '--ascending', '--customize-processing=::import::kitty.choose_entry',\n                '--window-title', title,\n                *(hints_args or ())\n            ), input_data='\\r\\n'.join(lines).encode('utf-8'), custom_callback=done, action_on_removal=done2\n        )\n        return q if isinstance(q, Window) else None\n\n    @ac('session', 'Switch to the specified session, creating it if not already present. See :ref:`goto_session`.')\n    def goto_session(self, *cmdline: str) -> None:\n        goto_session(self, cmdline)\n\n    @ac('session', 'Save the current kitty state as a session file. See :ref:`save_as_session`.')\n    def save_as_session(self, *cmdline: str) -> None:\n        save_as_session(self, cmdline)\n\n    @ac('session', '''\n        Close a session, that is, close all windows that belong to the session.\n        Examples::\n            # Ask for the session to close\n            map f1 close_session\n            # Close the currently active session\n            map f1 close_session .\n            # Close session by name\n            map f1 close_session \"my session\"\n            # Close session by path to session file\n            map f1 close_session \"/path/to/session/file.kitty-session\"\n    ''')\n    def close_session(self, *cmdline: str) -> None:\n        close_session_with_confirm(self, cmdline)\n\n    @ac('tab', 'Interactively select a tab to switch to')\n    def select_tab(self) -> None:\n\n        def chosen(ans: None | str | int) -> None:\n            if isinstance(ans, int):\n                for tab in self.all_tabs:\n                    if tab.id == ans:\n                        self.set_active_tab(tab)\n\n        def format_tab_title(tab: Tab) -> str:\n            w = 'windows' if tab.num_window_groups > 1 else 'window'\n            return f'{tab.name or tab.title} [{tab.num_window_groups} {w}]'\n\n        w = self.window_for_dispatch or self.active_window\n        ct = w.tabref() if w else None\n        self.choose_entry(\n            'Choose a tab to switch to',\n            ((None, f'Current tab: {format_tab_title(t)}') if t is ct else (t.id, format_tab_title(t)) for t in self.all_tabs),\n            chosen\n        )\n\n    @ac('win', '''\n        Detach a window, moving it to another tab or OS Window\n\n        See :ref:`detaching windows <detach_window>` for details.\n        ''')\n    def detach_window(self, *args: str) -> None:\n        if not args or args[0] == 'new':\n            return self._move_window_to(target_os_window_id='new')\n        if args[0] in ('new-tab', 'tab-prev', 'tab-left', 'tab-right', 'new-tab-left', 'new-tab-right'):\n            if args[0] == 'new-tab':\n                where = 'new'\n            elif args[0] == 'new-tab-right':\n                where = 'new_after'\n            elif args[0] == 'new-tab-left':\n                where = 'new_before'\n            else:\n                where = args[0][4:]\n            return self._move_window_to(target_tab_id=where)\n        w = self.window_for_dispatch or self.active_window\n        ct = w.tabref() if w else None\n        items: list[tuple[str | int, str]] = [(t.id, t.effective_title) for t in self.all_tabs if t is not ct]\n        items.append(('new_tab', 'New tab'))\n        items.append(('new_os_window', 'New OS Window'))\n        target_window = w\n\n        def chosen(ans: None | str | int) -> None:\n            if ans is not None:\n                if isinstance(ans, str):\n                    if ans == 'new_os_window':\n                        self._move_window_to(target_os_window_id='new')\n                    elif ans == 'new_tab':\n                        self._move_window_to(target_tab_id=ans)\n                else:\n                    self._move_window_to(target_window, target_tab_id=ans)\n\n        self.choose_entry('Choose a tab to move the window to', items, chosen)\n\n    @ac('tab', '''\n        Detach a tab, moving it to another OS Window. You can also use drag and drop to detach tabs.\n\n        See :ref:`detaching windows <detach_window>` for details.\n        ''')\n    def detach_tab(self, *args: str) -> None:\n        if not args or args[0] == 'new':\n            self._move_tab_to()\n            return\n\n        items: list[tuple[str | int, str]] = []\n        ct = self.active_tab_manager_with_dispatch\n        for osw_id, tm in self.os_window_map.items():\n            if tm is not ct and tm.active_tab:\n                items.append((osw_id, tm.active_tab.title))\n        items.append(('new', 'New OS Window'))\n        w = self.window_for_dispatch or self.active_window\n        target_tab = w.tabref() if w else None\n\n        def chosen(ans: None | int | str) -> None:\n            if ans is not None:\n                os_window_id = None if isinstance(ans, str) else ans\n                self._move_tab_to(tab=target_tab, target_os_window_id=os_window_id)\n\n        self.choose_entry('Choose an OS window to move the tab to', items, chosen)\n\n    def set_background_image(\n        self, path: str | None, os_windows: tuple[int, ...], configured: bool, layout: str | None, png_data: bytes = b'',\n        linear_interpolation: bool | None = None, tint: float | None = None, tint_gaps: float | None = None\n    ) -> None:\n        set_background_image(path, os_windows, configured, layout, png_data, linear_interpolation, tint, tint_gaps)\n\n    # Can be called with kitty -o \"map f1 send_test_notification\"\n    def send_test_notification(self) -> None:\n        self.notification_manager.send_test_notification()\n\n    @ac('debug', 'Show the environment variables that the kitty process sees')\n    def show_kitty_env_vars(self) -> None:\n        w = self.window_for_dispatch or self.active_window\n        env = os.environ.copy()\n        if is_macos and env.get('LC_CTYPE') == 'UTF-8' and not getattr(sys, 'kitty_run_data').get('lc_ctype_before_python'):\n            del env['LC_CTYPE']\n        if w:\n            output = '\\n'.join(f'{k}={v}' for k, v in env.items())\n            self.display_scrollback(w, output, title=_('Current kitty env vars'), report_cursor=False)\n\n    @ac('debug', '''\n        Close all shared SSH connections\n\n        See :opt:`share_connections <kitten-ssh.share_connections>` for details.\n        ''')\n    def close_shared_ssh_connections(self) -> None:\n        cleanup_ssh_control_masters()\n\n    @ac('debug', '''Simulate a change in OS color scheme preference''')\n    def simulate_color_scheme_preference_change(self, which: str) -> None:\n        which = which.lower().replace('-', '_')\n        match which:\n            case 'light':\n                self.on_system_color_scheme_change('light', False)\n            case 'dark':\n                self.on_system_color_scheme_change('dark', False)\n            case 'no_preference':\n                self.on_system_color_scheme_change('no_preference', False)\n            case 'toggle':\n                match theme_colors.applied_theme:\n                    case 'light':\n                        self.on_system_color_scheme_change('dark', False)\n                    case _:\n                        self.on_system_color_scheme_change('light', False)\n            case _:\n                self.show_error(_('Unknown color scheme type'), _('{} is not a valid color scheme type').format(which))\n\n    @ac('debug', ''' Start a test drag operation, for use with mouse_map ''')\n    def test_dragging(self) -> None:\n        if wid := current_os_window():\n            with open(logo_png_file, 'rb') as f:\n                rgba, width, height = load_png_data(f.read())\n            drag_data = {'text/plain': b'This is a test drag of some basic text with the kitty logo as the drag icon.'}\n            start_drag_with_data(wid, drag_data, ((rgba, width, height),))\n\n    def launch_urls(self, *urls: str, no_replace_window: bool = False) -> None:\n        from .launch import force_window_launch\n        from .open_actions import actions_for_launch\n        actions: list[KeyAction] = []\n        failures = []\n        for url in urls:\n            uactions = tuple(actions_for_launch(url))\n            if uactions:\n                actions.extend(uactions)\n            else:\n                failures.append(url)\n        tab = self.active_tab\n        if tab is not None:\n            w = tab.active_window\n        else:\n            w = None\n        needs_window_replaced = False\n        if not no_replace_window and not get_options().startup_session:\n            if w is not None and w.id == 1 and monotonic() - w.started_at < 2 and len(tuple(self.all_windows)) == 1:\n                # first window, soon after startup replace it\n                needs_window_replaced = True\n\n        def clear_initial_window() -> None:\n            if needs_window_replaced and tab is not None and w is not None:\n                tab.remove_window(w)\n\n        if failures:\n            from kittens.tui.operations import styled\n            spec = '\\n  '.join(styled(u, fg='yellow') for u in failures)\n            special_window = self.create_special_window_for_show_error('Open URL error', f\"Unknown URL type, cannot open:\\n  {spec}\")\n            if needs_window_replaced and tab is not None:\n                tab.new_special_window(special_window)\n            else:\n                self._new_os_window(special_window)\n            clear_initial_window()\n            needs_window_replaced = False\n        if actions:\n            with force_window_launch(needs_window_replaced):\n                self.dispatch_action(actions.pop(0))\n            clear_initial_window()\n            if actions:\n                self.drain_actions(actions)\n\n    @ac('debug', 'Show the effective configuration kitty is running with')\n    def debug_config(self) -> None:\n        from .debug_config import debug_config\n        w = self.window_for_dispatch or self.active_window\n        if w is not None:\n            output = debug_config(get_options(), self.mappings.global_shortcuts)\n            set_clipboard_string(re.sub(r'\\x1b.+?m', '', output))\n            self.handle_clipboard_loss('clipboard')\n            output += '\\n\\x1b[35mThis debug output has been copied to the clipboard\\x1b[m'  # ]]]\n            self.display_scrollback(w, output, title=_('Current kitty options'), report_cursor=False)\n\n    @ac('misc', 'Discard this event completely ignoring it')\n    def discard_event(self) -> None:\n        pass\n    mouse_discard_event = discard_event\n\n    def sanitize_url_for_display_to_user(self, url: str) -> str:\n        return sanitize_url_for_display_to_user(url)\n\n    def on_system_color_scheme_change(self, appearance: ColorSchemes, is_initial_value: bool) -> None:\n        theme_colors.on_system_color_scheme_change(appearance, is_initial_value)\n\n    @ac('win', '''\n        Toggle to the tab matching the specified expression\n\n        Switches to the matching tab if another tab is current, otherwise\n        switches to the last used tab. Useful to easily switch to and back from a\n        tab using a single shortcut. Note that toggling works only between\n        tabs in the same OS window. See :ref:`search_syntax` for details\n        on the match expression. For example::\n\n            map f1 toggle_tab title:mytab\n        ''')\n    def toggle_tab(self, match_expression: str) -> None:\n        tm = self.active_tab_manager_with_dispatch\n        if tm is not None:\n            tm.toggle_tab(match_expression)\n\n    def update_progress_in_dock(self) -> None:\n        if not is_macos:\n            return\n        has_indeterminate_progress = False\n        num_of_windows_with_progress = total_progress = 0\n        for tm in self.os_window_map.values():\n            if tm.num_of_windows_with_progress:\n                total_progress += tm.total_progress\n                num_of_windows_with_progress += tm.num_of_windows_with_progress\n            if tm.has_indeterminate_progress:\n                has_indeterminate_progress = True\n        from .fast_data_types import cocoa_show_progress_bar_on_dock_icon\n        if num_of_windows_with_progress:\n            cocoa_show_progress_bar_on_dock_icon(min(100, total_progress / num_of_windows_with_progress))\n        elif has_indeterminate_progress:\n            cocoa_show_progress_bar_on_dock_icon(101)\n        else:\n            cocoa_show_progress_bar_on_dock_icon()\n\n    def on_clipboard_lost(self, which: Literal['clipboard', 'primary']) -> None:\n        self.handle_clipboard_loss(which)\n\n    def handle_clipboard_loss(self, which: Literal['clipboard', 'primary'], exception: int = 0) -> None:\n        opts = get_options()\n        if opts.clear_selection_on_clipboard_loss and (which == 'primary' or opts.copy_on_select == 'clipboard'):\n            for wid, window in self.window_id_map.items():\n                if wid == exception:\n                    continue\n                window.screen.clear_selection()\n\n    @ac('misc', grab_keyboard_docs)\n    def grab_keyboard(self) -> None:\n        grab_keyboard(True)\n\n    @ac('misc', 'Ungrab the keyboard if it was previously grabbed')\n    def ungrab_keyboard(self) -> None:\n        grab_keyboard(False)\n\n    def search_scrollback_in_active(self) -> None:\n        if w := self.active_window:\n            w.search_scrollback()\n"
  },
  {
    "path": "kitty/cell_defines.glsl",
    "content": "#define DO_FG_OVERRIDE {DO_FG_OVERRIDE}\n#define FG_OVERRIDE_THRESHOLD {FG_OVERRIDE_THRESHOLD}\n#define FG_OVERRIDE_ALGO {FG_OVERRIDE_ALGO}\n#define TEXT_NEW_GAMMA {TEXT_NEW_GAMMA}\n\n#define DECORATION_SHIFT {DECORATION_SHIFT}\n#define REVERSE_SHIFT {REVERSE_SHIFT}\n#define STRIKE_SHIFT {STRIKE_SHIFT}\n#define DIM_SHIFT {DIM_SHIFT}\n#define BLINK_SHIFT {BLINK_SHIFT}\n#define MARK_SHIFT {MARK_SHIFT}\n#define MARK_MASK {MARK_MASK}\n#define USE_SELECTION_FG\n#define NUM_COLORS 256\n#define COLOR_NOT_SET {COLOR_NOT_SET}\n#define COLOR_IS_SPECIAL {COLOR_IS_SPECIAL}\n#define COLOR_IS_RGB {COLOR_IS_RGB}\n#define COLOR_IS_INDEX {COLOR_IS_INDEX}\n\n#if {ONLY_BACKGROUND} == 1\n#define ONLY_BACKGROUND\n#endif\n\n#if {ONLY_FOREGROUND} == 1\n#define ONLY_FOREGROUND\n#endif\n\n// Linear space luminance values\nconst vec3 Y = vec3(0.2126, 0.7152, 0.0722);\n"
  },
  {
    "path": "kitty/cell_fragment.glsl",
    "content": "#pragma kitty_include_shader <alpha_blend.glsl>\n#pragma kitty_include_shader <linear2srgb.glsl>\n#pragma kitty_include_shader <cell_defines.glsl>\n#pragma kitty_include_shader <utils.glsl>\n\nuniform float text_contrast;\nuniform float text_gamma_adjustment;\nuniform sampler2DArray sprites;\n\nin vec3 background;\nin vec4 effective_background_premul;\n#ifndef ONLY_BACKGROUND\nin float effective_text_alpha;\nin vec3 sprite_pos;\nin vec3 underline_pos;\nin vec3 cursor_pos;\nin vec3 strike_pos;\nflat in uint underline_exclusion_pos;\nin vec3 cell_foreground;\nin vec4 cursor_color_premult;\nin vec3 decoration_fg;\nin float colored_sprite;\n#endif\n\nout vec4 output_color;\n\n// Scaling factor for the extra text-alpha adjustment for luminance-difference.\nconst float text_gamma_scaling = 0.5;\n\nfloat clamp_to_unit_float(float x) {\n    // Clamp value to suitable output range\n    return clamp(x, 0.0f, 1.0f);\n}\n\n#ifndef ONLY_BACKGROUND\n#if TEXT_NEW_GAMMA == 1\nvec4 foreground_contrast(vec4 over, vec3 under) {\n    float under_luminance = dot(under, Y);\n    float over_lumininace = dot(over.rgb, Y);\n    // Apply additional gamma-adjustment scaled by the luminance difference, the darker the foreground the more adjustment we apply.\n    // A multiplicative contrast is also available to increase saturation.\n    over.a = clamp_to_unit_float(mix(over.a, pow(over.a, text_gamma_adjustment), (1 - over_lumininace + under_luminance) * text_gamma_scaling) * text_contrast);\n    return over;\n}\n\n#else\n\nvec4 foreground_contrast(vec4 over, vec3 under) {\n    // Simulation of gamma-incorrect blending\n    float under_luminance = dot(under, Y);\n    float over_lumininace = dot(over.rgb, Y);\n    // This is the original gamma-incorrect rendering, it is the solution of the following equation:\n    //\n    // linear2srgb(over * overA2 + under * (1 - overA2)) = linear2srgb(over) * over.a + linear2srgb(under) * (1 - over.a)\n    // ^ gamma correct blending with new alpha             ^ gamma incorrect blending with old alpha\n    over.a = clamp_to_unit_float((srgb2linear(linear2srgb(over_lumininace) * over.a + linear2srgb(under_luminance) * (1.0f - over.a)) - under_luminance) / (over_lumininace - under_luminance));\n    return over;\n}\n#endif\n\nvec4 load_text_foreground_color() {\n    // For colored sprites use the color from the sprite rather than the text foreground\n    // Return non-premultiplied foreground color\n    vec4 text_fg = texture(sprites, sprite_pos);\n    return vec4(mix(cell_foreground, text_fg.rgb, colored_sprite), text_fg.a);\n}\n\nvec4 calculate_premul_foreground_from_sprites(vec4 text_fg) {\n    // Return premul foreground color from decorations (cursor, underline, strikethrough)\n    ivec3 sz = textureSize(sprites, 0);\n    float underline_alpha = texture(sprites, underline_pos).a;\n    float underline_exclusion = texelFetch(sprites, ivec3(int(\n        sprite_pos.x * float(sz.x)), int(underline_exclusion_pos), int(sprite_pos.z)), 0).a;\n    underline_alpha *= 1.0f - underline_exclusion;\n    float strike_alpha = texture(sprites, strike_pos).a;\n    float cursor_alpha = texture(sprites, cursor_pos).a;\n    // Since strike and text are the same color, we simply add the alpha values\n    float combined_alpha = min(text_fg.a + strike_alpha, 1.0f);\n    // Underline color might be different, so alpha blend\n    vec4 ans = alpha_blend(vec4(text_fg.rgb, combined_alpha * effective_text_alpha), vec4(decoration_fg, underline_alpha * effective_text_alpha));\n    return mix(ans, cursor_color_premult, cursor_alpha * cursor_color_premult.a);\n}\n\nvec4 adjust_foreground_contrast_with_background(vec4 text_fg, vec3 bg) {\n    // When rendering on a background we can adjust the alpha channel contrast\n    // to improve legibility based on the source and destination colors\n    return foreground_contrast(text_fg, bg);\n}\n#endif  // ifndef ONLY_BACKGROUND\n\n\nvoid main() {\n#ifdef ONLY_FOREGROUND\n    vec4 ans_premul;\n#else\n    vec4 ans_premul = effective_background_premul;\n#endif\n\n#ifndef ONLY_BACKGROUND\n    // blend in the foreground color\n    vec4 text_fg = load_text_foreground_color();\n    text_fg = adjust_foreground_contrast_with_background(text_fg, background);\n    vec4 text_fg_premul = calculate_premul_foreground_from_sprites(text_fg);\n#ifdef ONLY_FOREGROUND\n    ans_premul = text_fg_premul;\n#else\n    ans_premul = alpha_blend_premul(text_fg_premul, ans_premul);\n#endif\n#endif  // ifndef ONLY_BACKGROUND\n    output_color = ans_premul;\n}\n"
  },
  {
    "path": "kitty/cell_vertex.glsl",
    "content": "#extension GL_ARB_explicit_attrib_location : require\n#pragma kitty_include_shader <cell_defines.glsl>\n#pragma kitty_include_shader <utils.glsl>\n\n\n// Inputs {{{\nlayout(std140) uniform CellRenderData {\n    float use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg;\n\n    uint default_fg, highlight_fg, highlight_bg, main_cursor_fg, main_cursor_bg, url_color, url_style, inverted, extra_cursor_fg, extra_cursor_bg;\n\n    uint columns, lines, sprites_xnum, sprites_ynum, cursor_shape, cell_width, cell_height;\n    uint cursor_x1, cursor_x2, cursor_y1, cursor_y2;\n    float cursor_opacity, inactive_text_alpha, dim_opacity, blink_opacity;\n\n    // must have unique entries with 0 being default_bg and unset being UINT32_MAX\n    uint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7;\n    float bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7;\n    uint color_table[NUM_COLORS + MARK_MASK + MARK_MASK + 2];\n};\nuniform float gamma_lut[256];\nuniform uint draw_bg_bitfield;\nuniform usampler2D sprite_decorations_map;\nuniform float row_offset;\n\n// Have to use fixed locations here as all variants of the cell program share the same VAOs\nlayout(location=0) in uvec3 colors;\nlayout(location=1) in uvec2 sprite_idx;\nlayout(location=2) in uint is_selected;\n// }}}\n\nconst int fg_index_map[] = int[3](0, 1, 0);\nconst uvec2 cell_pos_map[] = uvec2[4](\n    uvec2(1u, 0u),  // right, top\n    uvec2(1u, 1u),  // right, bottom\n    uvec2(0u, 1u),  // left, bottom\n    uvec2(0u, 0u)   // left, top\n);\nconst uint cursor_shape_map[] = uint[5](  // maps cursor shape to foreground sprite index\n   0u,  // NO_CURSOR\n   0u,  // BLOCK  (this is rendered as background)\n   2u,  // BEAM\n   3u,  // UNDERLINE\n   4u   // UNFOCUSED\n);\n\n\nout vec3 background;\nout vec4 effective_background_premul;\n#ifndef ONLY_BACKGROUND\nout float effective_text_alpha;\nout vec3 sprite_pos;\nout vec3 underline_pos;\nout vec3 cursor_pos;\nout vec3 strike_pos;\nflat out uint underline_exclusion_pos;\nout vec3 cell_foreground;\nout vec4 cursor_color_premult;\nout vec3 decoration_fg;\nout float colored_sprite;\n#endif\n\n\n// Utility functions {{{\nconst uint BYTE_MASK = uint(0xFF);\nconst uint SPRITE_INDEX_MASK = uint(0x7fffffff);\nconst uint SPRITE_COLORED_MASK = uint(0x80000000);\nconst uint SPRITE_COLORED_SHIFT = 31u;\nconst uint BIT_MASK = 1u;\nconst uint DECORATION_MASK = uint({DECORATION_MASK});\n\nvec3 color_to_vec(uint c) {\n    uint r, g, b;\n    r = (c >> 16) & BYTE_MASK;\n    g = (c >> 8) & BYTE_MASK;\n    b = c & BYTE_MASK;\n    return vec3(gamma_lut[r], gamma_lut[g], gamma_lut[b]);\n}\n\nfloat one_if_equal_zero_otherwise(float a, float b) { return (1.0f - zero_or_one(abs(float(a) - float(b)))); }\n// Wee need an integer variant to accommodate GPU driver bugs, see\n// https://github.com/kovidgoyal/kitty/issues/9072\nuint one_if_equal_zero_otherwise(int a, int b) { return (1u - uint(zero_or_one(abs(float(a) - float(b))))); }\nuint one_if_equal_zero_otherwise(uint a, uint b) { return (1u - uint(zero_or_one(abs(float(a) - float(b))))); }\n\n\nuint resolve_color(uint c, uint defval) {\n    // Convert a cell color to an actual color based on the color table\n    int t = int(c & BYTE_MASK);\n    uint is_one = one_if_equal_zero_otherwise(t, 1);\n    uint is_two = one_if_equal_zero_otherwise(t, 2);\n    uint is_neither_one_nor_two = 1u - is_one - is_two;\n    return is_one * color_table[(c >> 8) & BYTE_MASK] + is_two * (c >> 8) + is_neither_one_nor_two * defval;\n}\n\nvec3 to_color(uint c, uint defval) {\n    return color_to_vec(resolve_color(c, defval));\n}\n\nvec3 resolve_dynamic_color(uint c, vec3 special_val, vec3 defval) {\n    float type = float((c >> 24) & BYTE_MASK);\n#define q(which, val) one_if_equal_zero_otherwise(type, float(which)) * val\n    return (\n        q(COLOR_IS_RGB, color_to_vec(c)) + q(COLOR_IS_INDEX, color_to_vec(color_table[c & BYTE_MASK])) +\n        q(COLOR_IS_SPECIAL, special_val) + q(COLOR_NOT_SET, defval)\n    );\n#undef q\n}\n\nfloat contrast_ratio(float under_luminance, float over_luminance) {\n    return clamp((max(under_luminance, over_luminance) + 0.05f) / (min(under_luminance, over_luminance) + 0.05f), 1.f, 21.f);\n}\n\nfloat contrast_ratio(vec3 a, vec3 b) {\n    return contrast_ratio(dot(a, Y), dot(b, Y));\n}\n\nstruct ColorPair {\n    vec3 bg, fg;\n};\n\nfloat contrast_ratio(ColorPair a) { return contrast_ratio(a.bg, a.fg); }\n\nColorPair if_less_than_pair(float a, float b, ColorPair thenval, ColorPair elseval) {\n    return ColorPair(if_less_than(a, b, thenval.bg, elseval.bg),\n            if_less_than(a, b, thenval.fg, elseval.fg));\n}\n\nColorPair if_one_then_pair(float condition, ColorPair thenval, ColorPair elseval) {\n    return ColorPair(if_one_then(condition, thenval.bg, elseval.bg),\n            if_one_then(condition, thenval.fg, elseval.fg));\n}\n\nColorPair resolve_extra_cursor_colors_for_special_cursor(vec3 cell_bg, vec3 cell_fg) {\n    ColorPair cell = ColorPair(cell_fg, cell_bg), base = ColorPair(color_to_vec(default_fg), color_to_vec(bg_colors0));\n    float cr = contrast_ratio(cell), br = contrast_ratio(base);\n    ColorPair higher_contrast_pair = if_less_than_pair(cr, br, base, cell);\n    return if_less_than_pair(cr, 2.5, higher_contrast_pair, cell);\n}\n\nColorPair resolve_extra_cursor_colors(vec3 cell_bg, vec3 cell_fg, ColorPair main_cursor) {\n    ColorPair ans = ColorPair(\n        resolve_dynamic_color(extra_cursor_bg, main_cursor.bg, main_cursor.bg),\n        resolve_dynamic_color(extra_cursor_fg, cell_bg, main_cursor.fg)\n    );\n    ColorPair special = resolve_extra_cursor_colors_for_special_cursor(cell_bg, cell_fg);\n    return if_one_then_pair(zero_or_one(abs(float(extra_cursor_bg & BYTE_MASK) - COLOR_IS_SPECIAL)), ans, special);\n}\n\nuvec3 to_sprite_coords(uint idx) {\n    uint sprites_per_page = sprites_xnum * sprites_ynum;\n    uint z = idx / sprites_per_page;\n    uint num_on_last_page = idx - sprites_per_page * z;\n    uint y = num_on_last_page / sprites_xnum;\n    uint x = num_on_last_page - sprites_xnum * y;\n    return uvec3(x, y, z);\n}\n\nvec3 to_sprite_pos(uvec2 pos, uint idx) {\n    uvec3 c = to_sprite_coords(idx);\n    vec2 s_xpos = vec2(c.x, float(c.x) + 1.0f) * (1.0f / float(sprites_xnum));\n    vec2 s_ypos = vec2(c.y, float(c.y) + 1.0f) * (1.0f / float(sprites_ynum));\n    uint texture_height_px = (cell_height + 1u) * sprites_ynum;\n    float row_height = 1.0f / float(texture_height_px);\n    s_ypos[1] -= row_height;  // skip the decorations_exclude row\n    return vec3(s_xpos[pos.x], s_ypos[pos.y], c.z);\n}\n\nuint to_underline_exclusion_pos() {\n    uvec3 c = to_sprite_coords(sprite_idx[0]);\n    uint cell_top_px = c.y * (cell_height + 1u);\n    return cell_top_px + cell_height;\n}\n\nuint read_sprite_decorations_idx() {\n    int idx = int(sprite_idx[0] & SPRITE_INDEX_MASK);\n    ivec2 sz = textureSize(sprite_decorations_map, 0);\n    int y = idx / sz[0];\n    int x = idx - y * sz[0];\n    return texelFetch(sprite_decorations_map, ivec2(x, y), 0).r;\n}\n\nuvec2 get_decorations_indices(uint in_url /* [0, 1] */, uint text_attrs) {\n    uint decorations_idx = read_sprite_decorations_idx();\n    // decorations_idx == 0 means no decorations, for example, for a blank line\n    // when drawing fractionally scaled text\n    uint has_decorations = uint(zero_or_one(float(decorations_idx)));\n    uint strike_style = ((text_attrs >> STRIKE_SHIFT) & BIT_MASK); // 0 or 1\n    uint strike_idx = decorations_idx * strike_style;\n    uint underline_style = ((text_attrs >> DECORATION_SHIFT) & DECORATION_MASK);\n    underline_style = in_url * url_style + (1u - in_url) * underline_style; // [0, 5]\n    uint has_underline = uint(step(0.5f, float(underline_style)));  // [0, 1]\n    return has_decorations * uvec2(strike_idx, has_underline * (decorations_idx + underline_style));\n}\n\nuint is_cursor(uint x, uint y) {\n    uint clamped_x = clamp(x, cursor_x1, cursor_x2);\n    uint clamped_y = clamp(y, cursor_y1, cursor_y2);\n    return one_if_equal_zero_otherwise(x, clamped_x) * one_if_equal_zero_otherwise(y, clamped_y);\n}\n// }}}\n\nstruct CellData {\n    float has_cursor, has_block_cursor;\n    uvec2 pos;\n    uint cursor_fg_sprite_idx;\n    ColorPair cursor;\n} cell_data;\n\nCellData set_vertex_position(vec3 cell_fg, vec3 cell_bg) {\n    uint instance_id = uint(gl_InstanceID);\n    float dx = 2.0 / float(columns);\n    float dy = 2.0 / float(lines);\n    /* The current cell being rendered */\n    uint row = instance_id / columns;\n    uint column = instance_id - row * columns;\n    /* The position of this vertex, at a corner of the cell  */\n    float left = -1.0 + column * dx;\n    float top = 1.0 - (float(row) + row_offset) * dy;\n    uvec2 pos = cell_pos_map[gl_VertexID];\n    gl_Position = vec4(vec2(left, left + dx)[pos.x], vec2(top, top - dy)[pos.y], 0, 1);\n    // The character sprite being rendered\n#ifndef ONLY_BACKGROUND\n    sprite_pos = to_sprite_pos(pos, sprite_idx[0] & SPRITE_INDEX_MASK);\n    colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT);\n#endif\n    // Cursor shape and colors\n    float has_main_cursor = float(is_cursor(column, row));\n    float multicursor_shape = float((is_selected >> 2) & 3u);\n    float multicursor_uses_main_cursor_shape = float((is_selected >> 4) & BIT_MASK);\n    multicursor_shape = if_one_then(multicursor_uses_main_cursor_shape, cursor_shape, multicursor_shape);\n    float final_cursor_shape = if_one_then(has_main_cursor, cursor_shape, multicursor_shape);\n    float has_cursor = zero_or_one(final_cursor_shape);\n    float is_block_cursor = has_cursor * one_if_equal_zero_otherwise(final_cursor_shape, 1.0);\n    ColorPair main_cursor = ColorPair(color_to_vec(main_cursor_bg), color_to_vec(main_cursor_fg));\n    ColorPair extra_cursor = resolve_extra_cursor_colors(cell_bg, cell_fg, main_cursor);\n    ColorPair cursor = if_one_then_pair(has_main_cursor, main_cursor, extra_cursor);\n    return CellData(has_cursor, is_block_cursor, pos, cursor_shape_map[int(final_cursor_shape)], cursor);\n}\n\nfloat background_opacity_for(uint bg, uint colorval, float opacity_if_matched) {  // opacity_if_matched if bg == colorval else 1\n    float not_matched = step(1.f, abs(float(colorval - bg)));  // not_matched = 0 if bg == colorval else 1\n    return not_matched + opacity_if_matched * (1.f - not_matched);\n}\n\nfloat calc_background_opacity(uint bg) {\n    return (\n        background_opacity_for(bg, bg_colors0, bg_opacities0) *\n        background_opacity_for(bg, bg_colors1, bg_opacities1) *\n        background_opacity_for(bg, bg_colors2, bg_opacities2) *\n        background_opacity_for(bg, bg_colors3, bg_opacities3) *\n        background_opacity_for(bg, bg_colors4, bg_opacities4) *\n        background_opacity_for(bg, bg_colors5, bg_opacities5) *\n        background_opacity_for(bg, bg_colors6, bg_opacities6) *\n        background_opacity_for(bg, bg_colors7, bg_opacities7)\n    );\n}\n\n// Overriding of foreground colors for contrast requirements {{{\n#if DO_FG_OVERRIDE == 1 && !defined(ONLY_BACKGROUND)\n#define OVERRIDE_FG_COLORS\n#pragma kitty_include_shader <hsluv.glsl>\n#if (FG_OVERRIDE_ALGO == 1)\nvec3 fg_override(float under_luminance, float over_lumininace, vec3 under, vec3 over) {\n    // If the difference in luminance is too small,\n    // force the foreground color to be black or white.\n    float diff_luminance = abs(under_luminance - over_lumininace);\n\tfloat override_level = (1.f - colored_sprite) * step(diff_luminance, FG_OVERRIDE_THRESHOLD);\n\tfloat original_level = 1.f - override_level;\n\treturn original_level * over + override_level * vec3(step(under_luminance, 0.5f));\n}\n\n#else\n\nvec3 fg_override(float under_luminance, float over_luminance, vec3 under, vec3 over) {\n    float ratio = contrast_ratio(under_luminance, over_luminance);\n    vec3 diff = abs(under - over);\n    vec3 over_hsluv = rgbToHsluv(over);\n    const float min_contrast_ratio = FG_OVERRIDE_THRESHOLD;\n    float target_lum_a = clamp((under_luminance + 0.05f) * min_contrast_ratio - 0.05f, 0.f, 1.f);\n    float target_lum_b = clamp((under_luminance + 0.05f) / min_contrast_ratio - 0.05f, 0.f, 1.f);\n    vec3 result_a = clamp(hsluvToRgb(vec3(over_hsluv.x, over_hsluv.y, target_lum_a * 100.f)), 0.f, 1.f);\n    vec3 result_b = clamp(hsluvToRgb(vec3(over_hsluv.x, over_hsluv.y, target_lum_b * 100.f)), 0.f, 1.f);\n    float result_a_ratio = contrast_ratio(under_luminance, dot(result_a, Y));\n    float result_b_ratio = contrast_ratio(under_luminance, dot(result_b, Y));\n    vec3 result = mix(result_a, result_b, step(result_a_ratio, result_b_ratio));\n    return mix(result, over, max(step(diff.r + diff.g + diff.g, 0.001f), step(min_contrast_ratio, ratio)));\n}\n#endif\n\nvec3 override_foreground_color(vec3 over, vec3 under) {\n    float under_luminance = dot(under, Y);\n    float over_lumininace = dot(over.rgb, Y);\n    return fg_override(under_luminance, over_lumininace, under, over);\n}\n#endif\n// }}}\n\nvoid main() {\n\n\n    // set cell color indices {{{\n    uvec2 default_colors = uvec2(default_fg, bg_colors0);\n    uint text_attrs = sprite_idx[1];\n    uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & BIT_MASK);\n    uint is_inverted = is_reversed + inverted;\n    int fg_index = fg_index_map[is_inverted];\n    int bg_index = 1 - fg_index;\n    int mark = int(text_attrs >> MARK_SHIFT) & MARK_MASK;\n    uint has_mark = uint(step(1, float(mark)));\n    uint bg_as_uint = resolve_color(colors[bg_index], default_colors[bg_index]);\n    bg_as_uint = has_mark * color_table[NUM_COLORS + mark - 1] + (BIT_MASK - has_mark) * bg_as_uint;\n    float cell_has_default_bg = 1.f - step(1.f, abs(float(bg_as_uint - bg_colors0))); // 1 if has default bg else 0\n    vec3 bg = color_to_vec(bg_as_uint);\n    uint fg_as_uint = resolve_color(colors[fg_index], default_colors[fg_index]);\n    fg_as_uint = has_mark * color_table[NUM_COLORS + MARK_MASK + mark] + (1u - has_mark) * fg_as_uint;\n    vec3 foreground = color_to_vec(fg_as_uint);\n    CellData cell_data = set_vertex_position(foreground, bg);\n    // }}}\n\n    // Foreground {{{\n#ifndef ONLY_BACKGROUND // background does not depend on foreground\n    float has_dim = float((text_attrs >> DIM_SHIFT) & BIT_MASK), has_blink = float((text_attrs >> BLINK_SHIFT) & BIT_MASK);\n    effective_text_alpha = inactive_text_alpha * if_one_then(has_dim, dim_opacity, 1.0) * if_one_then(\n            has_blink, blink_opacity, 1.0);\n    float in_url = float((is_selected >> 1) & BIT_MASK);\n    decoration_fg = if_one_then(in_url, color_to_vec(url_color), to_color(colors[2], fg_as_uint));\n    // Selection\n    vec3 selection_color = if_one_then(use_cell_bg_for_selection_fg, bg, color_to_vec(highlight_fg));\n    selection_color = if_one_then(use_cell_fg_for_selection_fg, foreground, selection_color);\n    foreground = if_one_then(float(is_selected & BIT_MASK), selection_color, foreground);\n    decoration_fg = if_one_then(float(is_selected & BIT_MASK), selection_color, decoration_fg);\n    // Underline and strike through (rendered via sprites)\n    uvec2 decs = get_decorations_indices(uint(in_url), text_attrs);\n    strike_pos = to_sprite_pos(cell_data.pos, decs[0]);\n    underline_pos = to_sprite_pos(cell_data.pos, decs[1]);\n    underline_exclusion_pos = to_underline_exclusion_pos();\n\n    // Cursor\n    cursor_color_premult = vec4(cell_data.cursor.bg * cursor_opacity, cursor_opacity);\n    vec3 final_cursor_text_color = mix(foreground, cell_data.cursor.fg, cursor_opacity);\n    foreground = if_one_then(cell_data.has_block_cursor, final_cursor_text_color, foreground);\n    decoration_fg = if_one_then(cell_data.has_block_cursor, final_cursor_text_color, decoration_fg);\n    cursor_pos = to_sprite_pos(cell_data.pos, cell_data.cursor_fg_sprite_idx * uint(cell_data.has_cursor));\n#endif\n    // }}}\n\n    // Background {{{\n    float bg_alpha = calc_background_opacity(bg_as_uint);\n    // we use max so that opacity of the block cursor cell background goes from bg_alpha to 1\n    float effective_cursor_opacity = max(cursor_opacity, bg_alpha);\n    // is_special_cell is either 0 or 1\n    float is_special_cell = cell_data.has_block_cursor + float(is_selected & BIT_MASK);\n    is_special_cell += float(is_reversed);  // reverse video cells should be opaque as well\n    is_special_cell = zero_or_one(is_special_cell);\n    cell_has_default_bg = if_one_then(is_special_cell, 0., cell_has_default_bg);\n\n    // special cells must always be fully opaque, otherwise leave bg_alpha untouched\n    bg_alpha = if_one_then(is_special_cell, 1.f, bg_alpha);\n    // Selection and cursor\n    bg_alpha = if_one_then(cell_data.has_block_cursor, effective_cursor_opacity, bg_alpha);\n    bg = if_one_then(float(is_selected & BIT_MASK), if_one_then(use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(highlight_bg)), bg);\n    vec3 background_rgb = if_one_then(cell_data.has_block_cursor, mix(bg, cell_data.cursor.bg, cursor_opacity), bg);\n    background = background_rgb;\n    // }}}\n\n#if !defined(ONLY_BACKGROUND) && defined(OVERRIDE_FG_COLORS)\n    decoration_fg = override_foreground_color(decoration_fg, background_rgb);\n    foreground = override_foreground_color(foreground, background_rgb);\n#endif\n\n#if !defined(ONLY_FOREGROUND)\n    vec4 bgpremul = vec4_premul(background_rgb, bg_alpha);\n    // draw_bg_bitfield has bit 0 set to draw default bg cells and bit 1 set to draw non-default bg cells\n    float cell_has_non_default_bg = 1.f - cell_has_default_bg;\n    uint draw_bg_mask = uint(2.f * cell_has_non_default_bg + cell_has_default_bg); // 1 if has default bg else 2\n    float draw_bg = step(0.5, float(draw_bg_bitfield & draw_bg_mask));\n    bgpremul *= draw_bg;\n    effective_background_premul = bgpremul;\n#endif\n\n#ifndef ONLY_BACKGROUND\n    cell_foreground = foreground;\n#endif\n}\n"
  },
  {
    "path": "kitty/char-props-data.h",
    "content": "// Unicode data, built from the Unicode Standard 17.0.0\n// Code generated by wcwidth.py, DO NOT EDIT.\n\n#pragma once\n\ntypedef enum IndicConjunctBreak {\n\tICB_None,\n\tICB_Linker,\n\tICB_Consonant,\n\tICB_Extend,\n} IndicConjunctBreak;\n\nstatic const char_type CharProps_mask = 255u;\nstatic const char_type CharProps_shift = 8u;\nstatic const uint8_t CharProps_t1[4352] = {\n\t0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 54, 52, 52, 52, 55, 21, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 52, 72, 73, 21, 74, 75, 76, 77, 78, 79, 80, 81, 82, 21, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 21, 21, 21, 108, 109, 110, 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, 21, 21, 21, 21, 113, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 114, 21, 21, 115, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 116, 111, 111, 111, 111, 111, 111, 21, 21, 117, 118, 111, 119, 120, 121, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 122, 123, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 124, 52, 125, 126, 111, 111, 111, 111, 111, 111, 111, 111, 111, 127, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 128, 40, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 40, 40, 139, 111, 111, 111, 111, 140, 141, 142, 143, 111, 144, 145, 146, 147, 148, 149, 111, 111, 150, 151, 152, 111, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 165, 165, 166, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 167, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 168, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 169, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 170, 52, 52, 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 52, 52, 173, 172, 172, 172, 172, 174, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 175, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 176, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 174, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 178, 179, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 181, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 181\n};\nstatic const uint8_t CharProps_t2[46592] = {\n\t0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 5, 6, 7, 5, 5, 5, 8, 9, 6, 10, 5, 11, 5, 5, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 5, 5, 10, 10, 10, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 8, 5, 9, 14, 15, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 10, 9, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 17, 7, 7, 18, 7, 19, 17, 20, 21, 22, 23, 10, 24, 25, 14, 26, 27, 28, 28, 20, 16, 17, 17, 20, 28, 22, 29, 28, 28, 28, 17, 13, 13, 13, 13, 13, 13, 30, 13, 13, 13, 13, 13, 13, 13, 13, 13, 30, 13, 13, 13, 13, 13, 13, 27, 30, 13, 13, 13, 13, 13, 30, 31, 31, 31, 16, 16, 16, 16, 31, 16, 31, 31, 31, 16, 31, 31, 16, 16, 31, 16, 31, 31, 16, 16, 16, 27, 31, 31, 31, 16, 31, 16, 31, 16, 13, 31, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 31, 13, 31, 13, 16, 13, 16, 13, 16, 13, 31, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 30, 31, 13, 16, 13, 31, 13, 16, 13, 16, 13, 31, 30, 31, 13, 16, 13, 16, 31, 13, 16, 13, 16, 13, 16, 30, 31, 30, 31, 13, 31, 13, 16, 13, 31, 31, 30, 31, 13, 31, 13, 16, 13, 16, 30, 31, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 30, 31, 13, 16, 13, 31, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 13, 16, 13, 16, 13, 16, 16, 16, 13, 13, 16, 13, 16, 13, 13, 16, 13, 13, 13, 16, 16, 13, 13, 13, 13, 16, 13, 13, 16, 13, 13, 13, 16, 16, 16, 13, 13, 16, 13, 13, 16, 13, 16, 13, 16, 13, 13, 16, 13, 16, 16, 13, 16, 13, 13, 16, 13, 13, 13, 16, 13, 16, 13, 13, 16, 16, 32, 13, 16, 16, 16, 32, 32, 32, 32, 13, 33, 16, 13, 33, 16, 13, 33, 16, 13, 31, 13, 31, 13, 31, 13, 31, 13, 31, 13, 31, 13, 31, 13, 31, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 13, 33, 16, 13, 16, 13, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 16, 16, 16, 16, 13, 13, 16, 13, 13, 16, 16, 13, 16, 13, 13, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 14, 14, 20, 14, 34, 35, 34, 35, 35, 35, 34, 35, 34, 34, 35, 34, 14, 14, 14, 14, 14, 14, 20, 20, 20, 20, 14, 20, 14, 20, 34, 34, 34, 34, 34, 14, 14, 14, 14, 14, 14, 14, 34, 14, 34, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 13, 16, 13, 16, 34, 14, 13, 16, 38, 38, 34, 16, 16, 16, 5, 13, 38, 38, 38, 38, 14, 14, 13, 5, 13, 13, 13, 38, 13, 38, 13, 13, 16, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 38, 30, 30, 30, 30, 30, 30, 30, 13, 13, 16, 16, 16, 16, 16, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 16, 31, 31, 31, 31, 31, 31, 31, 16, 16, 16, 16, 16, 13, 16, 16, 13, 13, 13, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 16, 16, 13, 16, 10, 13, 16, 13, 13, 16, 16, 13, 13, 13, 13, 30, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 16, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 19, 36, 36, 36, 36, 36, 39, 39, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 34, 5, 5, 5, 5, 5, 5, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 5, 11, 38, 38, 19, 19, 7, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 11, 36, 5, 36, 36, 5, 36, 36, 5, 36, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 32, 32, 32, 32, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 40, 40, 40, 40, 40, 10, 10, 10, 5, 5, 7, 5, 5, 19, 19, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 5, 24, 5, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 5, 5, 32, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 32, 36, 36, 36, 36, 36, 36, 36, 40, 19, 36, 36, 36, 36, 36, 36, 34, 34, 36, 36, 19, 36, 36, 36, 36, 32, 32, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 19, 19, 32, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 40, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 34, 34, 19, 5, 5, 5, 34, 38, 38, 36, 7, 7, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 34, 36, 36, 36, 36, 36, 36, 36, 36, 36, 34, 36, 36, 36, 34, 36, 36, 36, 36, 36, 38, 38, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 38, 38, 5, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 14, 32, 32, 32, 32, 32, 32, 32, 40, 40, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 42, 36, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 44, 42, 42, 32, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 36, 36, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 34, 32, 32, 32, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 32, 36, 42, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 38, 38, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 38, 43, 38, 38, 38, 43, 43, 43, 43, 38, 38, 36, 32, 45, 42, 42, 36, 36, 36, 36, 38, 38, 42, 42, 38, 38, 42, 42, 44, 32, 38, 38, 38, 38, 38, 38, 38, 38, 45, 38, 38, 38, 38, 43, 43, 38, 43, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 43, 43, 7, 7, 46, 46, 46, 46, 46, 46, 19, 7, 32, 5, 36, 38, 38, 36, 36, 42, 38, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 38, 32, 32, 38, 38, 36, 38, 42, 42, 42, 36, 36, 38, 38, 38, 38, 36, 36, 38, 38, 36, 36, 36, 38, 38, 38, 36, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 38, 32, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 36, 36, 32, 32, 32, 36, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 38, 43, 43, 43, 43, 43, 38, 38, 36, 32, 42, 42, 42, 36, 36, 36, 36, 36, 38, 36, 36, 42, 38, 42, 42, 44, 38, 38, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 7, 38, 38, 38, 38, 38, 38, 38, 43, 36, 36, 36, 36, 36, 36, 38, 36, 42, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 38, 38, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 38, 43, 43, 43, 43, 43, 38, 38, 36, 32, 45, 36, 42, 36, 36, 36, 36, 38, 38, 42, 42, 38, 38, 42, 42, 44, 38, 38, 38, 38, 38, 38, 38, 36, 36, 45, 38, 38, 38, 38, 43, 43, 38, 43, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 19, 43, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 32, 38, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 38, 32, 32, 38, 32, 38, 32, 32, 38, 38, 38, 32, 32, 38, 38, 38, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 45, 42, 36, 42, 42, 38, 38, 38, 42, 42, 42, 38, 42, 42, 42, 36, 38, 38, 32, 38, 38, 38, 38, 38, 38, 45, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 19, 19, 19, 19, 19, 19, 7, 19, 38, 38, 38, 38, 38, 36, 42, 42, 42, 36, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 38, 36, 32, 36, 36, 36, 42, 42, 42, 42, 38, 36, 36, 36, 38, 36, 36, 36, 44, 38, 38, 38, 38, 38, 38, 38, 36, 36, 38, 43, 43, 43, 38, 32, 32, 38, 38, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 5, 46, 46, 46, 46, 46, 46, 46, 19, 32, 36, 42, 42, 5, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 38, 38, 36, 32, 42, 36, 45, 42, 45, 42, 42, 38, 36, 45, 45, 38, 45, 45, 36, 36, 38, 38, 38, 38, 38, 38, 38, 45, 45, 38, 38, 38, 38, 38, 32, 32, 32, 38, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 32, 32, 42, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 42, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 36, 32, 45, 42, 42, 36, 36, 36, 36, 38, 42, 42, 42, 38, 42, 42, 42, 44, 47, 19, 38, 38, 38, 38, 32, 32, 32, 45, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 32, 32, 32, 32, 32, 32, 38, 36, 42, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 36, 38, 38, 38, 38, 45, 42, 42, 36, 36, 36, 38, 36, 38, 42, 42, 42, 42, 42, 42, 42, 45, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 42, 42, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 32, 48, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 7, 32, 32, 32, 32, 32, 32, 34, 36, 36, 36, 36, 36, 36, 36, 36, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 38, 32, 38, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 32, 48, 36, 36, 36, 36, 36, 36, 36, 36, 36, 32, 38, 38, 32, 32, 32, 32, 32, 38, 34, 38, 36, 36, 36, 36, 36, 36, 36, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 19, 19, 19, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19, 5, 19, 19, 19, 36, 36, 19, 19, 19, 19, 19, 19, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 36, 19, 36, 19, 36, 8, 9, 8, 9, 42, 42, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 5, 36, 36, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 19, 19, 19, 19, 19, 19, 19, 19, 36, 19, 19, 19, 19, 19, 19, 38, 19, 19, 5, 5, 5, 5, 5, 19, 19, 19, 19, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 49, 49, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 36, 49, 44, 36, 42, 42, 36, 36, 43, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 5, 5, 5, 5, 43, 43, 43, 43, 43, 43, 42, 42, 36, 36, 43, 43, 43, 43, 36, 36, 36, 43, 49, 49, 49, 43, 43, 49, 49, 49, 49, 49, 49, 49, 43, 43, 43, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 49, 42, 36, 36, 49, 49, 49, 49, 49, 49, 36, 43, 49, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 49, 49, 49, 36, 19, 19, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 13, 38, 38, 38, 38, 38, 13, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 5, 34, 16, 16, 16, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 36, 36, 36, 5, 5, 5, 5, 5, 5, 5, 5, 5, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 16, 16, 16, 16, 16, 16, 38, 38, 11, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 4, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 8, 9, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 5, 5, 55, 55, 55, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 45, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 45, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 37, 37, 42, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 42, 42, 36, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 36, 5, 5, 5, 34, 5, 5, 5, 7, 32, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 11, 5, 5, 5, 5, 36, 36, 36, 24, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 36, 36, 36, 42, 42, 42, 42, 36, 36, 42, 42, 42, 38, 38, 38, 38, 42, 42, 36, 42, 42, 42, 42, 42, 42, 36, 36, 36, 38, 38, 38, 38, 19, 38, 38, 38, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 42, 42, 36, 38, 38, 5, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 36, 42, 36, 36, 36, 36, 36, 36, 36, 38, 44, 49, 36, 49, 49, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 34, 5, 5, 5, 5, 5, 5, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 42, 32, 32, 32, 32, 32, 32, 43, 43, 32, 32, 32, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 45, 36, 36, 36, 36, 36, 45, 36, 45, 42, 42, 42, 42, 36, 45, 56, 43, 43, 43, 43, 43, 43, 43, 43, 38, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 5, 5, 5, 5, 5, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 36, 36, 36, 36, 36, 36, 36, 36, 19, 19, 19, 19, 19, 19, 19, 19, 19, 5, 5, 5, 36, 36, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 36, 36, 36, 36, 42, 42, 36, 36, 45, 44, 36, 36, 43, 43, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 43, 43, 43, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 42, 36, 36, 42, 42, 42, 36, 42, 36, 36, 36, 45, 45, 38, 38, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 42, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 36, 36, 38, 38, 38, 5, 5, 5, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 32, 32, 32, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 5, 5, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 13, 13, 13, 5, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 5, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 36, 32, 32, 32, 32, 32, 32, 36, 32, 32, 42, 36, 36, 32, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 38, 38, 13, 13, 13, 13, 13, 13, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 38, 38, 13, 13, 13, 13, 13, 13, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 38, 13, 38, 13, 38, 13, 38, 13, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 33, 33, 33, 33, 33, 33, 33, 33, 16, 16, 16, 16, 16, 16, 16, 16, 33, 33, 33, 33, 33, 33, 33, 33, 16, 16, 16, 16, 16, 16, 16, 16, 33, 33, 33, 33, 33, 33, 33, 33, 16, 16, 16, 16, 16, 38, 16, 16, 13, 13, 13, 13, 33, 14, 16, 14, 14, 14, 16, 16, 16, 38, 16, 16, 13, 13, 13, 13, 33, 14, 14, 14, 16, 16, 16, 16, 38, 38, 16, 16, 13, 13, 13, 13, 38, 14, 14, 14, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 14, 14, 14, 38, 38, 16, 16, 16, 38, 16, 16, 13, 13, 13, 13, 33, 14, 14, 38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 57, 58, 24, 24, 59, 11, 11, 59, 59, 59, 17, 5, 60, 61, 8, 23, 60, 61, 8, 23, 17, 17, 17, 5, 17, 17, 17, 17, 62, 63, 24, 24, 24, 24, 24, 4, 17, 5, 17, 17, 5, 17, 5, 5, 5, 23, 29, 17, 64, 5, 17, 15, 15, 5, 5, 5, 10, 8, 9, 5, 5, 64, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 15, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 24, 24, 24, 24, 24, 65, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 46, 34, 38, 38, 28, 46, 46, 46, 46, 46, 10, 10, 10, 8, 9, 35, 46, 28, 28, 28, 28, 46, 46, 46, 46, 46, 10, 10, 10, 8, 9, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 38, 38, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 18, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, 39, 39, 39, 36, 39, 39, 39, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 13, 26, 19, 26, 19, 13, 19, 26, 16, 13, 13, 13, 16, 16, 13, 13, 13, 31, 19, 13, 26, 19, 10, 13, 13, 13, 13, 13, 19, 19, 19, 26, 25, 19, 13, 19, 30, 19, 13, 19, 13, 30, 13, 13, 19, 16, 13, 13, 13, 13, 16, 32, 32, 32, 32, 66, 19, 19, 16, 16, 13, 13, 10, 10, 10, 10, 10, 13, 16, 16, 16, 16, 19, 10, 19, 19, 16, 19, 46, 46, 46, 28, 28, 46, 46, 46, 46, 46, 46, 28, 28, 28, 28, 46, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 55, 55, 55, 55, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 55, 55, 55, 55, 55, 55, 55, 55, 55, 13, 16, 55, 55, 55, 55, 28, 19, 19, 38, 38, 38, 38, 27, 27, 27, 27, 68, 25, 25, 25, 25, 25, 10, 10, 19, 19, 19, 19, 10, 19, 19, 10, 19, 19, 10, 19, 19, 21, 21, 19, 19, 19, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 19, 19, 27, 19, 27, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 27, 27, 10, 10, 10, 27, 27, 10, 10, 27, 10, 10, 10, 27, 10, 27, 10, 10, 10, 27, 10, 10, 10, 10, 27, 10, 10, 27, 27, 27, 27, 10, 10, 27, 10, 27, 10, 27, 27, 27, 27, 27, 27, 10, 27, 10, 10, 10, 10, 10, 27, 27, 27, 27, 10, 10, 10, 10, 27, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 10, 10, 27, 10, 10, 10, 10, 10, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 27, 10, 10, 27, 27, 27, 27, 10, 10, 27, 27, 10, 10, 27, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 27, 10, 10, 27, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 10, 10, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 8, 9, 8, 9, 19, 19, 19, 19, 19, 19, 26, 19, 19, 19, 19, 19, 19, 19, 69, 69, 19, 19, 19, 19, 10, 10, 19, 19, 19, 19, 19, 19, 21, 70, 71, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 69, 69, 69, 69, 21, 21, 21, 69, 21, 21, 69, 19, 19, 19, 19, 21, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 46, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 19, 26, 26, 26, 26, 26, 26, 26, 21, 21, 19, 19, 19, 19, 19, 19, 26, 26, 19, 19, 25, 27, 19, 19, 19, 19, 26, 26, 19, 19, 25, 27, 19, 19, 19, 19, 26, 26, 26, 19, 19, 26, 19, 19, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 72, 72, 73, 73, 10, 21, 21, 21, 21, 21, 26, 26, 19, 19, 26, 19, 19, 19, 19, 25, 26, 19, 21, 19, 19, 69, 69, 19, 19, 21, 19, 19, 19, 26, 69, 26, 19, 21, 19, 21, 21, 19, 19, 21, 19, 19, 19, 21, 19, 19, 19, 21, 21, 74, 74, 74, 74, 74, 74, 74, 74, 21, 21, 21, 19, 19, 19, 19, 19, 25, 19, 25, 19, 19, 19, 19, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 25, 26, 19, 25, 26, 25, 21, 26, 25, 26, 26, 19, 26, 26, 19, 27, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 21, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 74, 74, 74, 74, 74, 74, 19, 19, 21, 69, 21, 21, 21, 21, 19, 21, 19, 21, 21, 19, 26, 26, 21, 69, 19, 19, 19, 19, 19, 21, 19, 19, 69, 69, 19, 19, 19, 19, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 69, 26, 19, 19, 19, 19, 69, 69, 26, 26, 25, 26, 26, 26, 26, 26, 69, 25, 26, 25, 26, 25, 69, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 26, 19, 19, 19, 19, 26, 25, 69, 26, 26, 26, 26, 26, 25, 25, 69, 69, 25, 69, 26, 25, 25, 69, 69, 26, 26, 69, 26, 26, 19, 19, 21, 19, 19, 69, 19, 19, 21, 21, 69, 69, 69, 69, 19, 21, 19, 19, 21, 19, 21, 19, 21, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 26, 19, 19, 19, 19, 19, 19, 21, 19, 19, 21, 19, 19, 19, 19, 69, 19, 69, 19, 19, 19, 19, 69, 69, 69, 19, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 19, 19, 19, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 10, 10, 10, 10, 10, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 72, 72, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 9, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 21, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 10, 10, 10, 10, 10, 10, 19, 19, 19, 69, 19, 19, 19, 19, 69, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 13, 13, 13, 16, 16, 13, 16, 13, 16, 13, 16, 13, 13, 13, 13, 16, 13, 16, 16, 13, 16, 16, 16, 16, 16, 16, 34, 34, 13, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 19, 19, 19, 19, 19, 19, 13, 16, 13, 16, 36, 36, 36, 13, 16, 38, 38, 38, 38, 38, 5, 5, 5, 5, 46, 5, 5, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 16, 38, 38, 38, 38, 38, 16, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 34, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 5, 5, 23, 29, 23, 29, 5, 5, 5, 23, 29, 5, 23, 29, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 5, 5, 11, 5, 23, 29, 5, 5, 23, 29, 8, 9, 8, 9, 8, 9, 8, 9, 5, 5, 5, 5, 5, 34, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 11, 5, 5, 5, 5, 11, 5, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19, 19, 5, 5, 5, 8, 9, 8, 9, 8, 9, 8, 9, 11, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 76, 76, 76, 74, 77, 78, 79, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 74, 74, 70, 71, 70, 71, 70, 71, 70, 71, 80, 70, 71, 71, 74, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 81, 81, 81, 82, 82, 83, 77, 77, 77, 77, 77, 74, 74, 79, 79, 79, 77, 78, 84, 74, 19, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 81, 81, 85, 85, 77, 77, 78, 80, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 76, 77, 77, 77, 78, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 86, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 74, 74, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 28, 28, 28, 28, 28, 28, 28, 28, 74, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 69, 74, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 77, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 5, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 32, 36, 39, 39, 39, 5, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 5, 34, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 34, 34, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 36, 36, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 34, 34, 34, 34, 34, 34, 34, 34, 34, 14, 14, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 34, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 13, 16, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 34, 14, 14, 13, 16, 13, 16, 32, 13, 16, 13, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 13, 13, 13, 13, 16, 13, 13, 13, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 13, 13, 13, 16, 13, 16, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 34, 34, 34, 34, 13, 16, 32, 34, 34, 16, 32, 32, 32, 32, 32, 32, 32, 36, 32, 32, 32, 36, 32, 32, 32, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 36, 36, 42, 19, 19, 19, 19, 36, 38, 38, 38, 46, 46, 46, 46, 46, 46, 19, 19, 7, 19, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 42, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 32, 32, 5, 5, 5, 32, 5, 32, 32, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 45, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 5, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 38, 38, 38, 36, 36, 36, 42, 32, 32, 32, 32, 32, 43, 43, 43, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 42, 42, 36, 36, 36, 36, 42, 42, 36, 36, 42, 42, 56, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 34, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 5, 5, 43, 43, 43, 43, 43, 36, 34, 43, 43, 43, 43, 43, 43, 43, 43, 43, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 43, 43, 43, 43, 43, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 42, 42, 36, 36, 42, 42, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 36, 42, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 5, 5, 5, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 34, 43, 43, 43, 32, 32, 32, 19, 19, 19, 43, 49, 36, 49, 43, 43, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 32, 36, 36, 36, 32, 32, 36, 36, 32, 32, 32, 32, 32, 36, 36, 32, 36, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 34, 5, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 36, 36, 42, 42, 5, 5, 32, 34, 34, 42, 44, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 14, 34, 34, 34, 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 14, 14, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 36, 42, 42, 36, 42, 42, 5, 42, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 38, 38, 38, 38, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 38, 38, 38, 38, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 9, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 19, 19, 19, 19, 19, 19, 19, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 7, 19, 19, 19, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 76, 76, 76, 76, 76, 76, 76, 70, 71, 76, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 80, 80, 94, 94, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 76, 76, 70, 71, 76, 76, 76, 76, 94, 94, 94, 76, 76, 76, 38, 76, 76, 76, 76, 80, 70, 71, 70, 71, 70, 71, 76, 76, 76, 95, 80, 95, 95, 95, 38, 76, 96, 76, 76, 38, 38, 38, 38, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 24, 38, 76, 76, 76, 96, 76, 76, 76, 70, 71, 76, 95, 76, 80, 76, 76, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 76, 76, 95, 95, 95, 76, 76, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 70, 76, 71, 85, 94, 85, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 70, 95, 71, 95, 70, 71, 5, 8, 9, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 100, 100, 101, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 38, 38, 38, 96, 96, 95, 85, 74, 96, 96, 38, 19, 10, 10, 10, 10, 19, 19, 38, 65, 65, 65, 65, 65, 65, 65, 65, 65, 24, 24, 24, 19, 26, 93, 93, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 5, 5, 5, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 46, 46, 46, 46, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 46, 46, 19, 19, 19, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 55, 32, 32, 32, 32, 32, 32, 32, 32, 55, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 5, 55, 55, 55, 55, 55, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 5, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 13, 13, 38, 13, 13, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 34, 34, 34, 34, 34, 34, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 38, 38, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 5, 46, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 19, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 46, 46, 46, 38, 38, 38, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 46, 46, 32, 32, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 43, 36, 36, 36, 38, 36, 36, 38, 38, 38, 38, 38, 36, 36, 36, 36, 43, 43, 43, 43, 38, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 38, 36, 36, 36, 38, 38, 38, 38, 44, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 38, 38, 38, 38, 46, 46, 46, 46, 46, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 34, 32, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 38, 36, 36, 36, 36, 36, 11, 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 38, 10, 10, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 36, 36, 11, 38, 38, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 34, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 5, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 32, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 46, 46, 46, 46, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 42, 36, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 36, 32, 32, 36, 36, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 42, 42, 36, 36, 5, 5, 40, 5, 5, 5, 5, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 36, 44, 36, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 5, 5, 43, 42, 42, 43, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 5, 5, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 45, 32, 47, 47, 32, 5, 5, 5, 5, 36, 36, 36, 36, 5, 42, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 5, 32, 5, 5, 5, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 42, 42, 36, 45, 36, 36, 5, 5, 5, 5, 5, 5, 36, 32, 32, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 36, 36, 42, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 38, 36, 36, 32, 45, 42, 36, 42, 42, 42, 42, 38, 38, 42, 42, 38, 38, 42, 42, 45, 38, 38, 32, 38, 38, 38, 38, 38, 38, 45, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 42, 42, 38, 38, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 38, 38, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 32, 45, 42, 42, 36, 36, 36, 36, 36, 36, 38, 45, 38, 38, 45, 38, 45, 45, 45, 42, 38, 42, 42, 36, 45, 44, 47, 36, 32, 5, 5, 38, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 36, 36, 36, 42, 36, 32, 32, 32, 32, 5, 5, 5, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 38, 5, 36, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 42, 42, 36, 36, 36, 36, 36, 36, 42, 36, 42, 42, 45, 42, 36, 36, 42, 36, 36, 32, 32, 5, 32, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 42, 42, 36, 36, 36, 36, 38, 38, 42, 42, 42, 42, 36, 36, 42, 36, 36, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 32, 32, 32, 32, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 36, 42, 36, 36, 5, 5, 5, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 42, 36, 42, 42, 36, 36, 36, 36, 36, 36, 45, 36, 32, 5, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 36, 42, 36, 49, 49, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 5, 5, 5, 19, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 36, 36, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 43, 43, 43, 43, 43, 43, 43, 38, 38, 43, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 45, 42, 42, 42, 42, 42, 38, 42, 42, 38, 38, 36, 36, 45, 44, 47, 42, 47, 42, 36, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 38, 38, 36, 36, 42, 42, 42, 42, 36, 32, 5, 32, 42, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 42, 32, 36, 36, 36, 36, 5, 5, 5, 5, 5, 5, 5, 5, 44, 38, 38, 38, 38, 38, 38, 38, 38, 43, 36, 36, 36, 36, 36, 36, 42, 42, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 47, 47, 47, 47, 47, 47, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 36, 44, 5, 5, 5, 32, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 42, 36, 36, 36, 42, 36, 42, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 36, 36, 36, 36, 36, 36, 36, 38, 36, 36, 36, 36, 36, 36, 42, 36, 32, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 42, 36, 36, 36, 36, 36, 36, 36, 42, 36, 36, 42, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 38, 38, 38, 36, 38, 36, 36, 38, 36, 36, 36, 36, 36, 36, 36, 47, 36, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 42, 42, 38, 36, 36, 38, 42, 42, 36, 42, 36, 32, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 32, 32, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 42, 42, 5, 5, 38, 38, 38, 38, 38, 38, 38, 36, 36, 47, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 36, 36, 36, 36, 36, 38, 38, 38, 42, 42, 36, 45, 44, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 19, 19, 19, 19, 19, 19, 19, 7, 7, 7, 7, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 38, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 36, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 36, 36, 36, 36, 36, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 5, 5, 5, 5, 5, 19, 19, 19, 19, 34, 34, 34, 34, 5, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 46, 46, 46, 46, 46, 46, 46, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 34, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 53, 32, 32, 32, 53, 53, 53, 53, 34, 34, 5, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 5, 5, 5, 5, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 36, 32, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 77, 77, 76, 77, 81, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 82, 82, 77, 77, 79, 79, 79, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 77, 77, 77, 77, 38, 77, 77, 77, 77, 77, 77, 77, 38, 77, 77, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 38, 38, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 19, 36, 36, 5, 24, 24, 24, 24, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 19, 19, 19, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 45, 45, 36, 36, 36, 19, 19, 19, 45, 45, 45, 45, 45, 45, 24, 24, 24, 24, 24, 24, 24, 24, 36, 36, 36, 36, 36, 36, 36, 36, 19, 19, 36, 36, 36, 36, 36, 36, 36, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 36, 36, 36, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 36, 36, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 38, 13, 13, 38, 38, 13, 38, 38, 13, 13, 38, 38, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 38, 16, 38, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 38, 13, 13, 13, 13, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 13, 13, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 38, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 38, 13, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 16, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 19, 19, 19, 19, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 19, 19, 19, 19, 19, 19, 19, 19, 36, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 19, 19, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 36, 36, 36, 36, 36, 36, 36, 38, 36, 36, 38, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 34, 34, 34, 34, 34, 34, 34, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 32, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 7, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 32, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 36, 32, 32, 36, 32, 32, 32, 32, 32, 32, 32, 36, 36, 32, 32, 32, 32, 32, 36, 38, 38, 38, 38, 38, 38, 38, 38, 32, 34, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 36, 36, 36, 36, 36, 36, 36, 34, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 46, 46, 46, 7, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 38, 38, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 32, 38, 32, 38, 38, 38, 38, 38, 38, 32, 38, 38, 38, 38, 32, 38, 32, 38, 32, 38, 32, 32, 32, 38, 32, 32, 38, 32, 38, 38, 32, 38, 32, 38, 32, 38, 32, 38, 32, 38, 32, 32, 38, 32, 38, 38, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 38, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 10, 10, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 46, 46, 19, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 69, 26, 26, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 74, 69, 69, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 69, 74, 74, 69, 69, 69, 69, 69, 69, 69, 69, 69, 74, 102, 102, 102, 102, 74, 74, 74, 74, 74, 74, 74, 74, 74, 102, 102, 102, 102, 102, 102, 102, 69, 69, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 74, 74, 74, 74, 74, 74, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 19, 19, 21, 21, 21, 21, 21, 21, 21, 21, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 21, 21, 19, 21, 21, 21, 19, 19, 21, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 21, 69, 69, 69, 69, 69, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 21, 69, 21, 19, 21, 69, 69, 69, 104, 104, 104, 104, 104, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 69, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 69, 69, 69, 69, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 21, 21, 19, 19, 21, 69, 69, 21, 21, 21, 21, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 21, 21, 21, 21, 19, 19, 69, 19, 19, 19, 19, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 21, 19, 19, 21, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 19, 19, 21, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 21, 19, 19, 21, 19, 21, 19, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 21, 69, 21, 21, 21, 69, 69, 69, 19, 19, 69, 69, 69, 69, 102, 102, 102, 69, 69, 69, 69, 21, 21, 21, 21, 21, 21, 19, 19, 19, 21, 19, 69, 69, 102, 102, 102, 21, 19, 19, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 102, 69, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 10, 10, 10, 10, 10, 10, 10, 10, 10, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 69, 102, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 19, 38, 38, 38, 38, 38, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 93, 93, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 93, 93, 65, 24, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 93, 93\n};\nstatic const CharProps CharProps_t3[106] = {\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Cc, .is_emoji_presentation_base=0, .is_invalid=1, .is_non_rendered=1, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Control, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 0\n\t{.shifted_width=3, .is_emoji=0, .category=UC_Cc, .is_emoji_presentation_base=0, .is_invalid=1, .is_non_rendered=1, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Control, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 1\n\t{.shifted_width=3, .is_emoji=0, .category=UC_Cc, .is_emoji_presentation_base=0, .is_invalid=1, .is_non_rendered=1, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_LF, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 2\n\t{.shifted_width=3, .is_emoji=0, .category=UC_Cc, .is_emoji_presentation_base=0, .is_invalid=1, .is_non_rendered=1, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_CR, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 3\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Zs, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 4\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Po, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 5\n\t{.shifted_width=5, .is_emoji=1, .category=UC_Po, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 6\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Sc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 7\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Ps, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 8\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Pe, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 9\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Sm, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 10\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Pd, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 11\n\t{.shifted_width=5, .is_emoji=1, .category=UC_Nd, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 12\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lu, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 13\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Sk, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 14\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Pc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 15\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Ll, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 16\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Po, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 17\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Sc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 18\n\t{.shifted_width=5, .is_emoji=0, .category=UC_So, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 19\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Sk, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 20\n\t{.shifted_width=5, .is_emoji=1, .category=UC_So, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 21\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 22\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Pi, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 23\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Cf, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Control, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 24\n\t{.shifted_width=2, .is_emoji=1, .category=UC_So, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 25\n\t{.shifted_width=2, .is_emoji=0, .category=UC_So, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 26\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Sm, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 27\n\t{.shifted_width=2, .is_emoji=0, .category=UC_No, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 28\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Pf, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 29\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Lu, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 30\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Ll, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 31\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 32\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lt, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 33\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lm, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 34\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Lm, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 35\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Mn, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 36\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Mn, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 37\n\t{.shifted_width=0, .is_emoji=0, .category=UC_Cn, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 38\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Me, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 39\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Cf, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Prepend, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 40\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Nd, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 41\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Mc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_SpacingMark, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 42\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_Consonant, .is_extended_pictographic=0}, // 43\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Mn, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Linker, .is_extended_pictographic=0}, // 44\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Mc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 45\n\t{.shifted_width=5, .is_emoji=0, .category=UC_No, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 46\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_Prepend, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 47\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_SpacingMark, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 48\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Mc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 49\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_L, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 50\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_L, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 51\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_V, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 52\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_V, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 53\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_T, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 54\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Nl, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 55\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Mc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Linker, .is_extended_pictographic=0}, // 56\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Cf, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 57\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Cf, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_ZWJ, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 58\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Pd, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 59\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Pi, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 60\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Pf, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 61\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Zl, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Control, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 62\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Zp, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Control, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 63\n\t{.shifted_width=5, .is_emoji=1, .category=UC_Po, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 64\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Cn, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Control, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 65\n\t{.shifted_width=5, .is_emoji=1, .category=UC_Ll, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 66\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Nl, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 67\n\t{.shifted_width=2, .is_emoji=1, .category=UC_Sm, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 68\n\t{.shifted_width=6, .is_emoji=1, .category=UC_So, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 69\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Ps, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 70\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Pe, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 71\n\t{.shifted_width=5, .is_emoji=1, .category=UC_Sm, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 72\n\t{.shifted_width=6, .is_emoji=1, .category=UC_Sm, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 73\n\t{.shifted_width=6, .is_emoji=0, .category=UC_So, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 74\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Zs, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 75\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Po, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 76\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Lm, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 77\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 78\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Nl, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 79\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Pd, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 80\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Mn, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 81\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Mc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 82\n\t{.shifted_width=6, .is_emoji=1, .category=UC_Pd, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 83\n\t{.shifted_width=6, .is_emoji=1, .category=UC_Po, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 84\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Sk, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 85\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 86\n\t{.shifted_width=6, .is_emoji=0, .category=UC_No, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 87\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_LV, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 88\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_LVT, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 89\n\t{.shifted_width=3, .is_emoji=0, .category=UC_Cs, .is_emoji_presentation_base=0, .is_invalid=1, .is_non_rendered=1, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 90\n\t{.shifted_width=2, .is_emoji=0, .category=UC_Co, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 91\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Cn, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 92\n\t{.shifted_width=3, .is_emoji=0, .category=UC_Cn, .is_emoji_presentation_base=0, .is_invalid=1, .is_non_rendered=1, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 93\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Pc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=1, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 94\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Sm, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 95\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Sc, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 96\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Nd, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 97\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Lu, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 98\n\t{.shifted_width=6, .is_emoji=0, .category=UC_Ll, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 99\n\t{.shifted_width=5, .is_emoji=0, .category=UC_Lm, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 100\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Lo, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=1, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 101\n\t{.shifted_width=0, .is_emoji=0, .category=UC_Cn, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=0, .is_symbol=0, .is_combining_char=0, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_None, .indic_conjunct_break=ICB_None, .is_extended_pictographic=1}, // 102\n\t{.shifted_width=6, .is_emoji=1, .category=UC_So, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Regional_Indicator, .indic_conjunct_break=ICB_None, .is_extended_pictographic=0}, // 103\n\t{.shifted_width=6, .is_emoji=1, .category=UC_Sk, .is_emoji_presentation_base=1, .is_invalid=0, .is_non_rendered=0, .is_symbol=1, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 104\n\t{.shifted_width=4, .is_emoji=0, .category=UC_Cf, .is_emoji_presentation_base=0, .is_invalid=0, .is_non_rendered=1, .is_symbol=0, .is_combining_char=1, .is_word_char=0, .is_punctuation=0, .grapheme_break=GBP_Extend, .indic_conjunct_break=ICB_Extend, .is_extended_pictographic=0}, // 105\n};\nstatic const char_type GraphemeSegmentationResult_mask = 15u;\nstatic const char_type GraphemeSegmentationResult_shift = 4u;\nstatic const uint8_t GraphemeSegmentationResult_t1[4096] = {\n\t0, 0, 1, 0, 2, 2, 3, 2, 4, 4, 5, 4, 6, 6, 7, 6, 8, 8, 9, 8, 10, 10, 11, 10, 12, 12, 13, 12, 14, 14, 15, 14, 16, 16, 17, 16, 18, 18, 19, 18, 16, 16, 17, 16, 18, 18, 19, 18, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 20, 20, 21, 20, 22, 22, 23, 22, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 4, 4, 5, 4, 6, 6, 7, 6, 32, 32, 33, 32, 34, 34, 35, 34, 0, 36, 1, 1, 2, 37, 3, 3, 4, 38, 5, 5, 6, 39, 7, 7, 8, 40, 9, 9, 10, 41, 11, 11, 12, 42, 13, 13, 14, 43, 15, 15, 16, 44, 17, 17, 18, 45, 19, 19, 16, 44, 17, 17, 18, 45, 19, 19, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 20, 46, 21, 21, 22, 47, 23, 23, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 4, 38, 5, 5, 6, 39, 7, 7, 32, 52, 33, 33, 34, 53, 35, 35, 0, 0, 1, 0, 2, 2, 3, 2, 4, 4, 5, 4, 6, 6, 7, 6, 8, 8, 9, 8, 10, 10, 11, 10, 12, 12, 13, 12, 14, 14, 15, 14, 16, 16, 17, 16, 18, 18, 19, 18, 16, 16, 17, 16, 18, 18, 19, 18, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 20, 20, 21, 20, 22, 22, 23, 22, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 4, 4, 5, 4, 6, 6, 7, 6, 32, 32, 33, 32, 34, 34, 35, 34, 0, 36, 1, 1, 2, 37, 3, 3, 4, 38, 5, 5, 6, 39, 7, 7, 8, 40, 9, 9, 10, 41, 11, 11, 12, 42, 13, 13, 14, 43, 15, 15, 16, 44, 17, 17, 18, 45, 19, 19, 16, 44, 17, 17, 18, 45, 19, 19, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 20, 46, 21, 21, 22, 47, 23, 23, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 4, 38, 5, 5, 6, 39, 7, 7, 32, 52, 33, 33, 34, 53, 35, 35, 0, 54, 1, 54, 2, 55, 3, 55, 4, 56, 9, 56, 6, 57, 11, 57, 8, 58, 9, 58, 10, 59, 11, 59, 12, 60, 13, 60, 14, 61, 15, 61, 16, 62, 17, 62, 18, 63, 19, 63, 16, 62, 17, 62, 18, 63, 19, 63, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 20, 64, 9, 64, 22, 65, 11, 65, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 4, 56, 9, 56, 6, 57, 11, 57, 32, 70, 9, 70, 34, 71, 11, 71, 0, 36, 1, 72, 2, 37, 3, 73, 4, 38, 9, 74, 6, 39, 11, 75, 8, 40, 9, 76, 10, 41, 11, 77, 12, 42, 13, 78, 14, 43, 15, 79, 16, 44, 17, 80, 18, 45, 19, 81, 16, 44, 17, 80, 18, 45, 19, 81, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 20, 46, 9, 82, 22, 47, 11, 83, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 4, 38, 9, 74, 6, 39, 11, 75, 32, 52, 9, 88, 34, 53, 11, 89, 0, 54, 1, 54, 2, 55, 3, 55, 4, 56, 9, 56, 6, 57, 11, 57, 8, 58, 9, 58, 10, 59, 11, 59, 12, 60, 13, 60, 14, 61, 15, 61, 16, 62, 17, 62, 18, 63, 19, 63, 16, 62, 17, 62, 18, 63, 19, 63, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 20, 64, 9, 64, 22, 65, 11, 65, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 4, 56, 9, 56, 6, 57, 11, 57, 32, 70, 9, 70, 34, 71, 11, 71, 0, 36, 1, 72, 2, 37, 3, 73, 4, 38, 9, 74, 6, 39, 11, 75, 8, 40, 9, 76, 10, 41, 11, 77, 12, 42, 13, 78, 14, 43, 15, 79, 16, 44, 17, 80, 18, 45, 19, 81, 16, 44, 17, 80, 18, 45, 19, 81, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 20, 46, 9, 82, 22, 47, 11, 83, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 4, 38, 9, 74, 6, 39, 11, 75, 32, 52, 9, 88, 34, 53, 11, 89, 90, 90, 91, 90, 92, 92, 93, 92, 94, 94, 95, 94, 96, 96, 97, 96, 98, 98, 99, 98, 100, 100, 101, 100, 102, 102, 103, 102, 104, 104, 105, 104, 106, 106, 107, 106, 108, 108, 109, 108, 106, 106, 107, 106, 108, 108, 109, 108, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 110, 110, 111, 110, 112, 112, 113, 112, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 94, 94, 95, 94, 96, 96, 97, 96, 122, 122, 123, 122, 124, 124, 125, 124, 90, 126, 91, 91, 92, 127, 93, 93, 94, 128, 95, 95, 96, 129, 97, 97, 98, 130, 99, 99, 100, 131, 101, 101, 102, 132, 103, 103, 104, 133, 105, 105, 106, 134, 107, 107, 108, 135, 109, 109, 106, 134, 107, 107, 108, 135, 109, 109, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 110, 136, 111, 111, 112, 137, 113, 113, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 94, 128, 95, 95, 96, 129, 97, 97, 122, 142, 123, 123, 124, 143, 125, 125, 90, 90, 91, 90, 92, 92, 93, 92, 94, 94, 95, 94, 96, 96, 97, 96, 98, 98, 99, 98, 100, 100, 101, 100, 102, 102, 103, 102, 104, 104, 105, 104, 106, 106, 107, 106, 108, 108, 109, 108, 106, 106, 107, 106, 108, 108, 109, 108, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 110, 110, 111, 110, 112, 112, 113, 112, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 94, 94, 95, 94, 96, 96, 97, 96, 122, 122, 123, 122, 124, 124, 125, 124, 90, 126, 91, 91, 92, 127, 93, 93, 94, 128, 95, 95, 96, 129, 97, 97, 98, 130, 99, 99, 100, 131, 101, 101, 102, 132, 103, 103, 104, 133, 105, 105, 106, 134, 107, 107, 108, 135, 109, 109, 106, 134, 107, 107, 108, 135, 109, 109, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 110, 136, 111, 111, 112, 137, 113, 113, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 94, 128, 95, 95, 96, 129, 97, 97, 122, 142, 123, 123, 124, 143, 125, 125, 90, 144, 91, 144, 92, 145, 93, 145, 94, 146, 99, 146, 96, 147, 101, 147, 98, 148, 99, 148, 100, 149, 101, 149, 102, 150, 103, 150, 104, 151, 105, 151, 106, 152, 107, 152, 108, 153, 109, 153, 106, 152, 107, 152, 108, 153, 109, 153, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 110, 154, 99, 154, 112, 155, 101, 155, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 94, 146, 99, 146, 96, 147, 101, 147, 122, 160, 99, 160, 124, 161, 101, 161, 90, 126, 91, 162, 92, 127, 93, 163, 94, 128, 99, 164, 96, 129, 101, 165, 98, 130, 99, 166, 100, 131, 101, 167, 102, 132, 103, 168, 104, 133, 105, 169, 106, 134, 107, 170, 108, 135, 109, 171, 106, 134, 107, 170, 108, 135, 109, 171, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 110, 136, 99, 172, 112, 137, 101, 173, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 94, 128, 99, 164, 96, 129, 101, 165, 122, 142, 99, 178, 124, 143, 101, 179, 90, 144, 91, 144, 92, 145, 93, 145, 94, 146, 99, 146, 96, 147, 101, 147, 98, 148, 99, 148, 100, 149, 101, 149, 102, 150, 103, 150, 104, 151, 105, 151, 106, 152, 107, 152, 108, 153, 109, 153, 106, 152, 107, 152, 108, 153, 109, 153, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 110, 154, 99, 154, 112, 155, 101, 155, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 94, 146, 99, 146, 96, 147, 101, 147, 122, 160, 99, 160, 124, 161, 101, 161, 90, 126, 91, 162, 92, 127, 93, 163, 94, 128, 99, 164, 96, 129, 101, 165, 98, 130, 99, 166, 100, 131, 101, 167, 102, 132, 103, 168, 104, 133, 105, 169, 106, 134, 107, 170, 108, 135, 109, 171, 106, 134, 107, 170, 108, 135, 109, 171, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 110, 136, 99, 172, 112, 137, 101, 173, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 94, 128, 99, 164, 96, 129, 101, 165, 122, 142, 99, 178, 124, 143, 101, 179, 0, 0, 1, 0, 2, 2, 3, 2, 4, 4, 5, 4, 6, 6, 7, 6, 8, 8, 9, 8, 10, 10, 11, 10, 12, 12, 13, 12, 14, 14, 15, 14, 16, 16, 17, 16, 18, 18, 19, 18, 16, 16, 17, 16, 18, 18, 19, 18, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 20, 20, 21, 20, 22, 22, 23, 22, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 4, 4, 5, 4, 10, 10, 11, 10, 32, 32, 33, 32, 34, 34, 35, 34, 0, 36, 1, 1, 2, 37, 3, 3, 4, 38, 5, 5, 6, 39, 7, 7, 8, 40, 9, 9, 10, 41, 11, 11, 12, 42, 13, 13, 14, 43, 15, 15, 16, 44, 17, 17, 18, 45, 19, 19, 16, 44, 17, 17, 18, 45, 19, 19, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 20, 46, 21, 21, 22, 47, 23, 23, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 4, 38, 5, 5, 10, 41, 11, 11, 32, 52, 33, 33, 34, 53, 35, 35, 0, 0, 1, 0, 2, 2, 3, 2, 4, 4, 5, 4, 6, 6, 7, 6, 8, 8, 9, 8, 10, 10, 11, 10, 12, 12, 13, 12, 14, 14, 15, 14, 16, 16, 17, 16, 18, 18, 19, 18, 16, 16, 17, 16, 18, 18, 19, 18, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 20, 20, 21, 20, 22, 22, 23, 22, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 4, 4, 5, 4, 10, 10, 11, 10, 32, 32, 33, 32, 34, 34, 35, 34, 0, 36, 1, 1, 2, 37, 3, 3, 4, 38, 5, 5, 6, 39, 7, 7, 8, 40, 9, 9, 10, 41, 11, 11, 12, 42, 13, 13, 14, 43, 15, 15, 16, 44, 17, 17, 18, 45, 19, 19, 16, 44, 17, 17, 18, 45, 19, 19, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 20, 46, 21, 21, 22, 47, 23, 23, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 4, 38, 5, 5, 10, 41, 11, 11, 32, 52, 33, 33, 34, 53, 35, 35, 0, 54, 1, 54, 2, 55, 3, 55, 4, 56, 9, 56, 6, 57, 11, 57, 8, 58, 9, 58, 10, 59, 11, 59, 12, 60, 13, 60, 14, 61, 15, 61, 16, 62, 17, 62, 18, 63, 19, 63, 16, 62, 17, 62, 18, 63, 19, 63, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 20, 64, 9, 64, 22, 65, 11, 65, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 4, 56, 9, 56, 10, 59, 11, 59, 32, 70, 9, 70, 34, 71, 11, 71, 0, 36, 1, 72, 2, 37, 3, 73, 4, 38, 9, 74, 6, 39, 11, 75, 8, 40, 9, 76, 10, 41, 11, 77, 12, 42, 13, 78, 14, 43, 15, 79, 16, 44, 17, 80, 18, 45, 19, 81, 16, 44, 17, 80, 18, 45, 19, 81, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 20, 46, 9, 82, 22, 47, 11, 83, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 4, 38, 9, 74, 10, 41, 11, 77, 32, 52, 9, 88, 34, 53, 11, 89, 0, 54, 1, 54, 2, 55, 3, 55, 4, 56, 9, 56, 6, 57, 11, 57, 8, 58, 9, 58, 10, 59, 11, 59, 12, 60, 13, 60, 14, 61, 15, 61, 16, 62, 17, 62, 18, 63, 19, 63, 16, 62, 17, 62, 18, 63, 19, 63, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 20, 64, 9, 64, 22, 65, 11, 65, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 4, 56, 9, 56, 10, 59, 11, 59, 32, 70, 9, 70, 34, 71, 11, 71, 0, 36, 1, 72, 2, 37, 3, 73, 4, 38, 9, 74, 6, 39, 11, 75, 8, 40, 9, 76, 10, 41, 11, 77, 12, 42, 13, 78, 14, 43, 15, 79, 16, 44, 17, 80, 18, 45, 19, 81, 16, 44, 17, 80, 18, 45, 19, 81, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 20, 46, 9, 82, 22, 47, 11, 83, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 4, 38, 9, 74, 10, 41, 11, 77, 32, 52, 9, 88, 34, 53, 11, 89, 90, 90, 91, 90, 92, 92, 93, 92, 94, 94, 95, 94, 96, 96, 97, 96, 98, 98, 99, 98, 100, 100, 101, 100, 102, 102, 103, 102, 104, 104, 105, 104, 106, 106, 107, 106, 108, 108, 109, 108, 106, 106, 107, 106, 108, 108, 109, 108, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 110, 110, 111, 110, 112, 112, 113, 112, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 94, 94, 95, 94, 100, 100, 101, 100, 122, 122, 123, 122, 124, 124, 125, 124, 90, 126, 91, 91, 92, 127, 93, 93, 94, 128, 95, 95, 96, 129, 97, 97, 98, 130, 99, 99, 100, 131, 101, 101, 102, 132, 103, 103, 104, 133, 105, 105, 106, 134, 107, 107, 108, 135, 109, 109, 106, 134, 107, 107, 108, 135, 109, 109, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 110, 136, 111, 111, 112, 137, 113, 113, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 94, 128, 95, 95, 100, 131, 101, 101, 122, 142, 123, 123, 124, 143, 125, 125, 90, 90, 91, 90, 92, 92, 93, 92, 94, 94, 95, 94, 96, 96, 97, 96, 98, 98, 99, 98, 100, 100, 101, 100, 102, 102, 103, 102, 104, 104, 105, 104, 106, 106, 107, 106, 108, 108, 109, 108, 106, 106, 107, 106, 108, 108, 109, 108, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 110, 110, 111, 110, 112, 112, 113, 112, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 94, 94, 95, 94, 100, 100, 101, 100, 122, 122, 123, 122, 124, 124, 125, 124, 90, 126, 91, 91, 92, 127, 93, 93, 94, 128, 95, 95, 96, 129, 97, 97, 98, 130, 99, 99, 100, 131, 101, 101, 102, 132, 103, 103, 104, 133, 105, 105, 106, 134, 107, 107, 108, 135, 109, 109, 106, 134, 107, 107, 108, 135, 109, 109, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 110, 136, 111, 111, 112, 137, 113, 113, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 94, 128, 95, 95, 100, 131, 101, 101, 122, 142, 123, 123, 124, 143, 125, 125, 90, 144, 91, 144, 92, 145, 93, 145, 94, 146, 99, 146, 96, 147, 101, 147, 98, 148, 99, 148, 100, 149, 101, 149, 102, 150, 103, 150, 104, 151, 105, 151, 106, 152, 107, 152, 108, 153, 109, 153, 106, 152, 107, 152, 108, 153, 109, 153, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 110, 154, 99, 154, 112, 155, 101, 155, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 94, 146, 99, 146, 100, 149, 101, 149, 122, 160, 99, 160, 124, 161, 101, 161, 90, 126, 91, 162, 92, 127, 93, 163, 94, 128, 99, 164, 96, 129, 101, 165, 98, 130, 99, 166, 100, 131, 101, 167, 102, 132, 103, 168, 104, 133, 105, 169, 106, 134, 107, 170, 108, 135, 109, 171, 106, 134, 107, 170, 108, 135, 109, 171, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 110, 136, 99, 172, 112, 137, 101, 173, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 94, 128, 99, 164, 100, 131, 101, 167, 122, 142, 99, 178, 124, 143, 101, 179, 90, 144, 91, 144, 92, 145, 93, 145, 94, 146, 99, 146, 96, 147, 101, 147, 98, 148, 99, 148, 100, 149, 101, 149, 102, 150, 103, 150, 104, 151, 105, 151, 106, 152, 107, 152, 108, 153, 109, 153, 106, 152, 107, 152, 108, 153, 109, 153, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 110, 154, 99, 154, 112, 155, 101, 155, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 94, 146, 99, 146, 100, 149, 101, 149, 122, 160, 99, 160, 124, 161, 101, 161, 90, 126, 91, 162, 92, 127, 93, 163, 94, 128, 99, 164, 96, 129, 101, 165, 98, 130, 99, 166, 100, 131, 101, 167, 102, 132, 103, 168, 104, 133, 105, 169, 106, 134, 107, 170, 108, 135, 109, 171, 106, 134, 107, 170, 108, 135, 109, 171, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 110, 136, 99, 172, 112, 137, 101, 173, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 94, 128, 99, 164, 100, 131, 101, 167, 122, 142, 99, 178, 124, 143, 101, 179\n};\nstatic const GraphemeSegmentationResult GraphemeSegmentationResult_t2[2880] = {\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=0, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=0, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=1, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=0, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=0, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_AtStart, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_None, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Prepend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_CR, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LF, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Control, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_Extend, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Regional_Indicator, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_SpacingMark, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_L, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_V, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_T, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LV, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_LVT, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n\t{.grapheme_break=GBP_ZWJ, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=1},\n\t{.grapheme_break=GBP_Private_Expecting_RI, .incb_consonant_extended=1, .incb_consonant_extended_linker=0, .incb_consonant_extended_linker_extended=1, .emoji_modifier_sequence=1, .emoji_modifier_sequence_before_last_char=1, .add_to_current_cell=0},\n};\nstatic inline uint16_t GraphemeSegmentationKey(GraphemeSegmentationResult r, CharProps ch){\n\treturn (r.state << 7) | ch.grapheme_segmentation_property;\n}\n// GraphemeSegmentationStateDeclaration: uses 9 bits {{{\ntypedef union GraphemeSegmentationState {\n    struct __attribute__((packed)) {\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        uint8_t emoji_modifier_sequence_before_last_char : 1;\n        uint8_t emoji_modifier_sequence : 1;\n        uint8_t incb_consonant_extended_linker_extended : 1;\n        uint8_t incb_consonant_extended_linker : 1;\n        uint8_t incb_consonant_extended : 1;\n        uint8_t grapheme_break : 4;\n        uint8_t : 7;\n#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        uint8_t : 7;\n        uint8_t grapheme_break : 4;\n        uint8_t incb_consonant_extended : 1;\n        uint8_t incb_consonant_extended_linker : 1;\n        uint8_t incb_consonant_extended_linker_extended : 1;\n        uint8_t emoji_modifier_sequence : 1;\n        uint8_t emoji_modifier_sequence_before_last_char : 1;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n    uint16_t val;\n} GraphemeSegmentationState;\nstatic_assert(sizeof(GraphemeSegmentationState) == sizeof(uint16_t), \"Fix the ordering of GraphemeSegmentationState\");\n// EndGraphemeSegmentationStateDeclaration }}}\n\n"
  },
  {
    "path": "kitty/char-props.c",
    "content": "/*\n * char-props.c\n * Copyright (C) 2025 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"char-props.h\"\n#include \"char-props-data.h\"\n\nstatic char_type\nensure_char_in_range(const char_type value) {\n    // Branchless: if (value > MAX_UNICODE) value = 0\n    const int64_t diff = ((int64_t)value) - ((int64_t)(MAX_UNICODE + 1u));\n    // The right shift gives all ones for negative diff and all zeros for positive diff\n    const char_type mask = diff >> 63;\n    return value & mask;\n}\n\nCharProps\nchar_props_for(char_type ch) {\n    ch = ensure_char_in_range(ch);\n    return CharProps_t3[CharProps_t2[(CharProps_t1[ch >> CharProps_shift] << CharProps_shift) + (ch & CharProps_mask)]];\n}\n\nvoid\ngrapheme_segmentation_reset(GraphemeSegmentationResult *s) {\n    s->val = 0;\n}\n\nGraphemeSegmentationResult\ngrapheme_segmentation_step(GraphemeSegmentationResult r, CharProps ch) {\n    unsigned key = GraphemeSegmentationKey(r, ch);\n    unsigned t1 = ((unsigned)GraphemeSegmentationResult_t1[key >> GraphemeSegmentationResult_shift]) << GraphemeSegmentationResult_shift;\n    GraphemeSegmentationResult ans = GraphemeSegmentationResult_t2[t1 + (key & GraphemeSegmentationResult_mask)];\n    // printf(\"state: %u gsp: %u -> key: %u t1: %u -> add_to_cell: %u\\n\", r.state, ch.grapheme_segmentation_property, key, t1, ans.add_to_current_cell);\n    return ans;\n}\n"
  },
  {
    "path": "kitty/char-props.h",
    "content": "/*\n * char-props.h\n * Copyright (C) 2025 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\n// CharPropsDeclaration: uses 23 bits {{{\ntypedef union CharProps {\n    struct __attribute__((packed)) {\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        uint8_t is_extended_pictographic : 1;\n        uint8_t indic_conjunct_break : 2;\n        uint8_t grapheme_break : 4;\n        uint8_t is_punctuation : 1;\n        uint8_t is_word_char : 1;\n        uint8_t is_combining_char : 1;\n        uint8_t is_symbol : 1;\n        uint8_t is_non_rendered : 1;\n        uint8_t is_invalid : 1;\n        uint8_t is_emoji_presentation_base : 1;\n        uint8_t category : 5;\n        uint8_t is_emoji : 1;\n        uint8_t shifted_width : 3;\n        uint16_t : 9;\n#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        uint16_t : 9;\n        uint8_t shifted_width : 3;\n        uint8_t is_emoji : 1;\n        uint8_t category : 5;\n        uint8_t is_emoji_presentation_base : 1;\n        uint8_t is_invalid : 1;\n        uint8_t is_non_rendered : 1;\n        uint8_t is_symbol : 1;\n        uint8_t is_combining_char : 1;\n        uint8_t is_word_char : 1;\n        uint8_t is_punctuation : 1;\n        uint8_t grapheme_break : 4;\n        uint8_t indic_conjunct_break : 2;\n        uint8_t is_extended_pictographic : 1;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n    struct __attribute__((packed)) {\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        uint8_t grapheme_segmentation_property : 7;\n        uint32_t : 25;\n#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        uint32_t : 25;\n        uint8_t grapheme_segmentation_property : 7;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n    uint32_t val;\n} CharProps;\nstatic_assert(sizeof(CharProps) == sizeof(uint32_t), \"Fix the ordering of CharProps\");\n// EndCharPropsDeclaration }}}\n\n// GraphemeSegmentationResultDeclaration: uses 10 bits {{{\ntypedef union GraphemeSegmentationResult {\n    struct __attribute__((packed)) {\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        uint8_t emoji_modifier_sequence_before_last_char : 1;\n        uint8_t emoji_modifier_sequence : 1;\n        uint8_t incb_consonant_extended_linker_extended : 1;\n        uint8_t incb_consonant_extended_linker : 1;\n        uint8_t incb_consonant_extended : 1;\n        uint8_t grapheme_break : 4;\n        uint8_t add_to_current_cell : 1;\n        uint8_t : 6;\n#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        uint8_t : 6;\n        uint8_t add_to_current_cell : 1;\n        uint8_t grapheme_break : 4;\n        uint8_t incb_consonant_extended : 1;\n        uint8_t incb_consonant_extended_linker : 1;\n        uint8_t incb_consonant_extended_linker_extended : 1;\n        uint8_t emoji_modifier_sequence : 1;\n        uint8_t emoji_modifier_sequence_before_last_char : 1;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n    struct __attribute__((packed)) {\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        uint16_t state : 9;\n        uint8_t : 7;\n#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        uint8_t : 7;\n        uint16_t state : 9;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n    uint16_t val;\n} GraphemeSegmentationResult;\nstatic_assert(sizeof(GraphemeSegmentationResult) == sizeof(uint16_t), \"Fix the ordering of GraphemeSegmentationResult\");\n// EndGraphemeSegmentationResultDeclaration }}}\n\n// UCBDeclaration {{{\n#define MAX_UNICODE (1114111u)\ntypedef enum GraphemeBreakProperty {\n\tGBP_AtStart,\n\tGBP_None,\n\tGBP_Prepend,\n\tGBP_CR,\n\tGBP_LF,\n\tGBP_Control,\n\tGBP_Extend,\n\tGBP_Regional_Indicator,\n\tGBP_SpacingMark,\n\tGBP_L,\n\tGBP_V,\n\tGBP_T,\n\tGBP_LV,\n\tGBP_LVT,\n\tGBP_ZWJ,\n\tGBP_Private_Expecting_RI,\n} GraphemeBreakProperty;\n\ntypedef enum UnicodeCategory {\n\tUC_Cn,\n\tUC_Cc,\n\tUC_Zs,\n\tUC_Po,\n\tUC_Sc,\n\tUC_Ps,\n\tUC_Pe,\n\tUC_Sm,\n\tUC_Pd,\n\tUC_Nd,\n\tUC_Lu,\n\tUC_Sk,\n\tUC_Pc,\n\tUC_Ll,\n\tUC_So,\n\tUC_Lo,\n\tUC_Pi,\n\tUC_Cf,\n\tUC_No,\n\tUC_Pf,\n\tUC_Lt,\n\tUC_Lm,\n\tUC_Mn,\n\tUC_Me,\n\tUC_Mc,\n\tUC_Nl,\n\tUC_Zl,\n\tUC_Zp,\n\tUC_Cs,\n\tUC_Co,\n} UnicodeCategory;\n\n// EndUCBDeclaration }}}\n\n\nCharProps char_props_for(char_type ch);\nvoid grapheme_segmentation_reset(GraphemeSegmentationResult *s);\nGraphemeSegmentationResult grapheme_segmentation_step(GraphemeSegmentationResult r, CharProps ch);\nstatic inline int wcwidth_std(CharProps ch) { return (int)ch.shifted_width - 4/*=width_shift*/; }\nstatic inline bool is_private_use(CharProps ch) { return ch.category == UC_Co; }\nstatic inline const char* char_category(CharProps cp) {\n#define a(x) case UC_##x: return #x\n    switch((UnicodeCategory)cp.category) {\n        a(Cn); a(Cc); a(Zs); a(Po); a(Sc); a(Ps); a(Pe); a(Sm); a(Pd); a(Nd); a(Lu); a(Sk); a(Pc); a(Ll); a(So); a(Lo); a(Pi); a(Cf);\n        a(No); a(Pf); a(Lt); a(Lm); a(Mn); a(Me); a(Mc); a(Nl); a(Zl); a(Zp); a(Cs); a(Co);\n    }\n    return \"Cn\";\n#undef a\n}\n"
  },
  {
    "path": "kitty/charsets.c",
    "content": "/*\n * consolemap.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n// Taken from consolemap.c in the linux vt driver sourcecode\n\n#include \"data-types.h\"\n\n#ifndef NO_SINGLE_BYTE_CHARSETS\nstatic uint32_t charset_translations[4][256] = {\n  /* VT100 graphics mapped to Unicode */\n  {\n    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\n    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\n    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\n    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\n    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\n    0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f,\n    0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\n    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\n    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\n    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\n    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\n    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,\n    0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,\n    0x2591, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,\n    0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,\n    0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f,\n    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\n    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\n    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\n    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\n    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\n    0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\n    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\n    0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\n    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\n    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\n    0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\n    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,\n    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\n    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\n    0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\n    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff\n  },\n  /* IBM Codepage 437 mapped to Unicode */\n  {\n    0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,\n    0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,\n    0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,\n    0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,\n    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\n    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\n    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\n    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\n    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\n    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\n    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\n    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\n    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\n    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\n    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\n    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,\n    0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,\n    0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,\n    0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,\n    0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,\n    0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,\n    0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,\n    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\n    0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,\n    0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,\n    0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,\n    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,\n    0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,\n    0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,\n    0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,\n    0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,\n    0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0\n  },\n  // VAX 42 map\n  {\n    0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,\n    0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,\n    0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,\n    0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,\n    0x0020, 0x043b, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\n    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\n    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\n    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x0435,\n    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\n    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\n    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\n    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\n    0x0060, 0x0441, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\n    0x0435, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x043a,\n    0x0070, 0x0071, 0x0442, 0x0073, 0x043b, 0x0435, 0x0076, 0x0077,\n    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,\n    0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,\n    0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,\n    0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,\n    0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,\n    0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,\n    0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,\n    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\n    0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,\n    0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,\n    0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,\n    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,\n    0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,\n    0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,\n    0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,\n    0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,\n    0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0\n  },\n  /* UK mapping, same as 8-bit Latin1 except the pound sign replaces # */\n  {\n    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\n    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\n    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\n    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\n    0x0020, 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027,\n    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\n    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\n    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\n    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\n    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\n    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\n    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\n    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\n    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\n    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\n    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\n    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\n    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\n    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\n    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\n    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\n    0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\n    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\n    0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\n    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\n    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\n    0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\n    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,\n    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\n    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\n    0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\n    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff\n  },\n};\n\nuint32_t*\ntranslation_table(uint32_t which) {\n    switch(which){\n        default:\n            return NULL;\n        case '0':\n            return charset_translations[0];\n        case 'U':\n            return charset_translations[1];\n        case 'V':\n            return charset_translations[2];\n        case 'A':\n            return charset_translations[3];\n    }\n}\n#endif\n\n// UTF-8 decode taken from: https://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n\nstatic const uint8_t utf8_data[] = {\n  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f\n  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f\n  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f\n  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f\n  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f\n  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf\n  8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df\n  0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef\n  0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff\n  0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0\n  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2\n  1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4\n  1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6\n  1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8\n};\n\n#ifndef CHARSETS_STORAGE\n#define CHARSETS_STORAGE\n#endif\n\nCHARSETS_STORAGE uint32_t\ndecode_utf8(UTF8State* state, uint32_t* codep, uint8_t byte) {\n  uint32_t type = utf8_data[byte];\n\n  *codep = (*state != UTF8_ACCEPT) ?\n    (byte & 0x3fu) | (*codep << 6) :\n    (0xff >> type) & (byte);\n\n  *state = utf8_data[256 + *state*16 + type];\n  return *state;\n}\n\nCHARSETS_STORAGE size_t\ndecode_utf8_string(const char *src, size_t sz, uint32_t *dest) {\n    // dest must be a zeroed array of size at least sz\n    uint32_t codep = 0;\n    UTF8State state = 0, prev = UTF8_ACCEPT;\n    size_t i, d;\n    for (i = 0, d = 0; i < sz; i++) {\n        switch(decode_utf8(&state, &codep, src[i])) {\n            case UTF8_ACCEPT:\n                dest[d++] = codep;\n                break;\n            case UTF8_REJECT:\n                state = UTF8_ACCEPT;\n                if (prev != UTF8_ACCEPT && i > 0) i--;\n                break;\n        }\n        prev = state;\n    }\n    return d;\n}\n\nCHARSETS_STORAGE unsigned int\nencode_utf8(uint32_t ch, char* dest) {\n    if (ch < 0x80) { // only lower 7 bits can be 1\n        dest[0] = (char)ch;  // 0xxxxxxx\n        return 1;\n    }\n    if (ch < 0x800) { // only lower 11 bits can be 1\n        dest[0] = (ch>>6) | 0xC0; // 110xxxxx\n        dest[1] = (ch & 0x3F) | 0x80;  // 10xxxxxx\n        return 2;\n    }\n    if (ch < 0x10000) { // only lower 16 bits can be 1\n        dest[0] = (ch>>12) | 0xE0; // 1110xxxx\n        dest[1] = ((ch>>6) & 0x3F) | 0x80;  // 10xxxxxx\n        dest[2] = (ch & 0x3F) | 0x80;       // 10xxxxxx\n        return 3;\n    }\n    if (ch < 0x110000) { // only lower 21 bits can be 1\n        dest[0] = (ch>>18) | 0xF0; // 11110xxx\n        dest[1] = ((ch>>12) & 0x3F) | 0x80; // 10xxxxxx\n        dest[2] = ((ch>>6) & 0x3F) | 0x80;  // 10xxxxxx\n        dest[3] = (ch & 0x3F) | 0x80; // 10xxxxxx\n        return 4;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "kitty/charsets.h",
    "content": "/*\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stddef.h>\n\nuint32_t decode_utf8(uint32_t*, uint32_t*, uint8_t byte);\nsize_t decode_utf8_string(const char *src, size_t sz, uint32_t *dest);\nunsigned int encode_utf8(uint32_t ch, char* dest);\nuint32_t* translation_table(uint32_t which);\n"
  },
  {
    "path": "kitty/child-monitor.c",
    "content": "/*\n * child-monitor.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"loop-utils.h\"\n#include \"safe-wrappers.h\"\n#include \"state.h\"\n#include \"threading.h\"\n#include \"screen.h\"\n#include \"monotonic.h\"\n#include <termios.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/ioctl.h>\n#include <sys/wait.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <signal.h>\nextern PyTypeObject Screen_Type;\n\n#if defined(__APPLE__) || defined(__OpenBSD__)\n#define NO_SIGQUEUE 1\n#endif\n\n#ifdef DEBUG_EVENT_LOOP\n#define EVDBG(...) timed_debug_print(__VA_ARGS__)\n#else\n#define EVDBG(...)\n#endif\n\n#define EXTRA_FDS 2\n#ifndef MSG_NOSIGNAL\n// Apple does not implement MSG_NOSIGNAL\n#define MSG_NOSIGNAL 0\n#endif\n#define USE_RENDER_FRAMES (global_state.has_render_frames && OPT(sync_to_monitor))\n\ntypedef struct {\n    char *data;\n    size_t sz;\n    id_type peer_id;\n    bool is_remote_control_peer;\n} Message;\n\ntypedef struct {\n    PyObject_HEAD\n\n    PyObject *dump_callback, *update_screen, *death_notify;\n    unsigned int count;\n    bool shutting_down;\n    pthread_t io_thread, talk_thread;\n\n    int talk_fd, listen_fd;\n    Message *messages;\n    size_t messages_capacity, messages_count;\n    LoopData io_loop_data;\n    void (*parse_func)(void*, ParseData*, bool);\n} ChildMonitor;\n\n\ntypedef struct {\n    Screen *screen;\n    bool needs_removal;\n    int fd;\n    unsigned long id;\n    pid_t pid;\n} Child;\n\nstatic const Child EMPTY_CHILD = {0};\n#define screen_mutex(op, which) \\\n    pthread_mutex_##op(&screen->which##_buf_lock);\n#define children_mutex(op) \\\n    pthread_mutex_##op(&children_lock);\n#define talk_mutex(op) \\\n    pthread_mutex_##op(&talk_lock);\n\n\nstatic Child children[MAX_CHILDREN] = {{0}};\nstatic Child scratch[MAX_CHILDREN] = {{0}};\nstatic Child add_queue[MAX_CHILDREN] = {{0}}, remove_queue[MAX_CHILDREN] = {{0}}, remove_notify[MAX_CHILDREN] = {{0}};\nstatic size_t add_queue_count = 0, remove_queue_count = 0;\nstatic struct pollfd children_fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};\nstatic pthread_mutex_t children_lock, talk_lock;\nstatic bool kill_signal_received = false, reload_config_signal_received = false;\nstatic ChildMonitor *the_monitor = NULL;\n\ntypedef struct {\n    pid_t pid;\n    int status;\n} ReapedPID;\n\nstatic pid_t monitored_pids[256] = {0};\nstatic size_t monitored_pids_count = 0;\nstatic ReapedPID reaped_pids[arraysz(monitored_pids)] = {{0}};\nstatic size_t reaped_pids_count = 0;\n\n\n\n// Main thread functions {{{\n\n#define FREE_CHILD(x) \\\n    Py_CLEAR((x).screen); x = EMPTY_CHILD;\n\n#define XREF_CHILD(x, OP) OP(x.screen);\n#define INCREF_CHILD(x) XREF_CHILD(x, Py_INCREF)\n#define DECREF_CHILD(x) XREF_CHILD(x, Py_DECREF)\n\n// The max time to wait for events from the window system\n// before ticking over the main loop. Negative values mean wait forever.\nstatic monotonic_t maximum_wait = -1;\n\nstatic void\nset_maximum_wait(monotonic_t val) {\n    if (val >= 0 && (val < maximum_wait || maximum_wait < 0)) maximum_wait = val;\n}\n\n#define KITTY_HANDLED_SIGNALS SIGINT, SIGHUP, SIGTERM, SIGCHLD, SIGUSR1, SIGUSR2, 0\n\nstatic void\nmask_variadic_signals(int sentinel, ...) {\n    sigset_t signals;\n    sigemptyset(&signals);\n    va_list valist;\n    va_start(valist, sentinel);\n    while (true) {\n        int sig = va_arg(valist, int);\n        if (sig == sentinel) break;\n        sigaddset(&signals, sig);\n    }\n    va_end(valist);\n#ifdef HAS_SIGNAL_FD\n    sigprocmask(SIG_BLOCK, &signals, NULL);\n#else\n    struct sigaction act = {.sa_handler=SIG_IGN, .sa_flags=SA_RESTART, .sa_mask = signals};\n    va_start(valist, sentinel);\n    while (true) {\n        int sig = va_arg(valist, int);\n        if (sig == sentinel) break;\n        sigaction(sig, &act, NULL);\n    }\n    va_end(valist);\n#endif\n}\n\nstatic PyObject*\nmask_kitty_signals_process_wide(PyObject *self UNUSED, PyObject *a UNUSED) {\n    mask_variadic_signals(0, KITTY_HANDLED_SIGNALS);\n    Py_RETURN_NONE;\n}\n\nstatic int verify_peer_uid = false;\n\nstatic PyObject *\nnew_childmonitor_object(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {\n    ChildMonitor *self;\n    PyObject *dump_callback, *death_notify;\n    int talk_fd = -1, listen_fd = -1;\n    int ret;\n\n    if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, \"Can have only a single ChildMonitor instance\"); return NULL; }\n    if (!PyArg_ParseTuple(args, \"OO|iip\", &death_notify, &dump_callback, &talk_fd, &listen_fd, &verify_peer_uid)) return NULL;\n    if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {\n        PyErr_Format(PyExc_RuntimeError, \"Failed to create children_lock mutex: %s\", strerror(ret));\n        return NULL;\n    }\n    if ((ret = pthread_mutex_init(&talk_lock, NULL)) != 0) {\n        PyErr_Format(PyExc_RuntimeError, \"Failed to create talk_lock mutex: %s\", strerror(ret));\n        return NULL;\n    }\n    self = (ChildMonitor *)type->tp_alloc(type, 0);\n    if (!init_loop_data(&self->io_loop_data, KITTY_HANDLED_SIGNALS)) return PyErr_SetFromErrno(PyExc_OSError);\n    self->talk_fd = talk_fd;\n    self->listen_fd = listen_fd;\n    if (self == NULL) return PyErr_NoMemory();\n    self->death_notify = death_notify; Py_INCREF(death_notify);\n    if (dump_callback != Py_None) {\n        self->dump_callback = dump_callback; Py_INCREF(dump_callback);\n        self->parse_func = parse_worker_dump;\n    } else self->parse_func = parse_worker;\n    self->count = 0;\n    children_fds[0].fd = self->io_loop_data.wakeup_read_fd; children_fds[1].fd = self->io_loop_data.signal_read_fd;\n    children_fds[0].events = POLLIN; children_fds[1].events = POLLIN; children_fds[2].events = POLLIN;\n    the_monitor = self;\n\n    return (PyObject*) self;\n}\n\nstatic void\ndealloc(ChildMonitor* self) {\n    if (self->messages) {\n        for (size_t i = 0; i < self->messages_count; i++) free(self->messages[i].data);\n        free(self->messages); self->messages = NULL;\n        self->messages_count = 0; self->messages_capacity = 0;\n    }\n    pthread_mutex_destroy(&children_lock);\n    pthread_mutex_destroy(&talk_lock);\n    Py_CLEAR(self->dump_callback);\n    Py_CLEAR(self->death_notify);\n    while (remove_queue_count) {\n        remove_queue_count--;\n        FREE_CHILD(remove_queue[remove_queue_count]);\n    }\n    while (add_queue_count) {\n        add_queue_count--;\n        FREE_CHILD(add_queue[add_queue_count]);\n    }\n    free_loop_data(&self->io_loop_data);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic PyObject*\nhandled_signals(ChildMonitor *self, PyObject *args UNUSED) {\n    PyObject *ans = PyTuple_New(self->io_loop_data.num_handled_signals);\n    if (ans) {\n        for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(ans); i++) {\n            PyTuple_SET_ITEM(ans, i, PyLong_FromLong((long)self->io_loop_data.handled_signals[i]));\n        }\n    }\n    return ans;\n}\n\nstatic void\nwakeup_io_loop(ChildMonitor *self, bool in_signal_handler) {\n    wakeup_loop(&self->io_loop_data, in_signal_handler, \"io_loop\");\n}\n\nstatic void* io_loop(void *data);\nstatic void* talk_loop(void *data);\nstatic void send_response_to_peer(id_type peer_id, const char *msg, size_t msg_sz, bool is_async_response);\nstatic void wakeup_talk_loop(bool);\nstatic bool add_peer_to_injection_queue(int peer_fd, int pipe_fd);\nstatic bool talk_thread_started = false;\n\nstatic bool\nsimple_read_from_pipe(int fd, void *data, size_t sz) {\n    // read a small amount of data to a pipe handling only EINTR\n    while (true) {\n        ssize_t ret = read(fd, data, sz);\n        if (ret == -1 && errno == EINTR) continue;\n        return ret == (ssize_t)sz;\n    }\n}\n\n\nstatic PyObject*\ninject_peer(PyObject *s, PyObject *a) {\n#define inject_peer_doc \"inject_peer(fd) -> Start communication with a peer over the specified file descriptor\"\n    ChildMonitor *self = (ChildMonitor*)s;\n    if (!PyLong_Check(a)) { PyErr_SetString(PyExc_TypeError, \"peer fd must be an int\"); return NULL; }\n    long fd = PyLong_AsLong(a);\n    if (fd < 0) { PyErr_Format(PyExc_ValueError, \"Invalid peer fd: %ld\", fd); return NULL; }\n    if (!talk_thread_started) {\n        int ret;\n        if ((ret = pthread_create(&self->talk_thread, NULL, talk_loop, self)) != 0) {\n            return PyErr_Format(PyExc_OSError, \"Failed to start talk thread with error: %s\", strerror(ret));\n        }\n        talk_thread_started = true;\n    }\n    int fds[2] = {0};\n    if (!self_pipe(fds, false)) {\n        safe_close(fd, __FILE__, __LINE__);\n        return PyErr_SetFromErrno(PyExc_OSError);\n    }\n    if (!add_peer_to_injection_queue(fd, fds[1])) {\n        safe_close(fd, __FILE__, __LINE__);\n        safe_close(fds[0], __FILE__, __LINE__); safe_close(fds[1], __FILE__, __LINE__);\n        PyErr_SetString(PyExc_RuntimeError, \"Too many peers waiting to be injected\");\n        return NULL;\n    }\n    wakeup_talk_loop(false);\n    id_type peer_id = 0;\n    bool ok = simple_read_from_pipe(fds[0], &peer_id, sizeof(peer_id));\n    safe_close(fds[0], __FILE__, __LINE__);\n    if (!ok) { PyErr_SetString(PyExc_RuntimeError, \"Failed to read peer id from self pipe\"); return NULL; }\n    return PyLong_FromUnsignedLongLong(peer_id);\n}\n\nstatic PyObject *\nstart(PyObject *s, PyObject *a UNUSED) {\n#define start_doc \"start() -> Start the I/O thread\"\n    ChildMonitor *self = (ChildMonitor*)s;\n    int ret;\n    if (self->talk_fd > -1 || self->listen_fd > -1) {\n        if ((ret = pthread_create(&self->talk_thread, NULL, talk_loop, self)) != 0) {\n            return PyErr_Format(PyExc_OSError, \"Failed to start talk thread with error: %s\", strerror(ret));\n        }\n        talk_thread_started = true;\n    }\n    ret = pthread_create(&self->io_thread, NULL, io_loop, self);\n    if (ret != 0) return PyErr_Format(PyExc_OSError, \"Failed to start I/O thread with error: %s\", strerror(ret));\n\n    Py_RETURN_NONE;\n}\n\nstatic PyObject *\nwakeup(ChildMonitor *self, PyObject *args UNUSED) {\n#define wakeup_doc \"wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there.\"\n    wakeup_io_loop(self, false);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject *\nadd_child(ChildMonitor *self, PyObject *args) {\n#define add_child_doc \"add_child(id, pid, fd, screen) -> Add a child.\"\n    children_mutex(lock);\n    if (self->count + add_queue_count >= MAX_CHILDREN) { PyErr_SetString(PyExc_ValueError, \"Too many children\"); children_mutex(unlock); return NULL; }\n    add_queue[add_queue_count] = EMPTY_CHILD;\n#define A(attr) &add_queue[add_queue_count].attr\n    if (!PyArg_ParseTuple(args, \"kiiO\", A(id), A(pid), A(fd), A(screen))) {\n        children_mutex(unlock);\n        return NULL;\n    }\n#undef A\n    INCREF_CHILD(add_queue[add_queue_count]);\n    add_queue_count++;\n    children_mutex(unlock);\n    wakeup_io_loop(self, false);\n    Py_RETURN_NONE;\n}\n\n#define schedule_write_to_child_generic(id, num, va_start, get_next_arg, va_end) \\\n    ChildMonitor *self = the_monitor; \\\n    bool found = false; \\\n    const char *data; \\\n    size_t szval, sz = 0; \\\n    va_start(ap, num); \\\n    for (unsigned int i = 0; i < num; i++) { \\\n        get_next_arg(ap); \\\n        sz += szval; \\\n    } \\\n    va_end(ap); \\\n    children_mutex(lock); \\\n    for (size_t i = 0; i < self->count; i++) { \\\n        if (children[i].id == id) { \\\n            Screen *screen = children[i].screen; \\\n            screen_mutex(lock, write); \\\n            size_t space_left = screen->write_buf_sz - screen->write_buf_used; \\\n            if (space_left < sz) { \\\n                if (screen->write_buf_used + sz > 100 * 1024 * 1024) { \\\n                    log_error(\"Too much data being sent to child with id: %lu, ignoring it\", id); \\\n                    screen_mutex(unlock, write); \\\n                    break; \\\n                } \\\n                screen->write_buf_sz = screen->write_buf_used + sz; \\\n                screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz); \\\n                if (screen->write_buf == NULL) { fatal(\"Out of memory.\"); } \\\n            } \\\n            found = true; \\\n            va_start(ap, num); \\\n            for (unsigned int i = 0; i < num; i++) { \\\n                get_next_arg(ap); \\\n                memcpy(screen->write_buf + screen->write_buf_used, data, szval); \\\n                screen->write_buf_used += szval; \\\n            } \\\n            va_end(ap); \\\n            if (screen->write_buf_sz > BUFSIZ && screen->write_buf_used < BUFSIZ) { \\\n                screen->write_buf_sz = BUFSIZ; \\\n                screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz); \\\n                if (screen->write_buf == NULL) { fatal(\"Out of memory.\"); } \\\n            } \\\n            if (screen->write_buf_used) wakeup_io_loop(self, false); \\\n            screen_mutex(unlock, write); \\\n            break; \\\n        } \\\n    } \\\n    children_mutex(unlock); \\\n    return found;\n\nbool\nschedule_write_to_child(unsigned long id, unsigned int num, ...) {\n    va_list ap;\n#define get_next_arg(ap) data = va_arg(ap, const char*); szval = va_arg(ap, size_t);\n    schedule_write_to_child_generic(id, num, va_start, get_next_arg, va_end);\n#undef get_next_arg\n}\n\nbool\nschedule_write_to_child_python(unsigned long id, const char *prefix, PyObject *ap, const char *suffix) {\n    if (!PyTuple_Check(ap)) return false;\n    bool has_prefix = prefix && prefix[0], has_suffix = suffix && suffix[0];\n    const size_t extra = (has_prefix ? 1 : 0) + (has_suffix ? 1 : 0);\n    size_t num = PyTuple_GET_SIZE(ap) + extra;\n    Py_ssize_t pidx;\n#define py_start(ap, num) pidx = 0;\n#define py_end(ap) pidx = 0;\n#define get_next_arg(ap) { \\\n    size_t pidxf = pidx++; \\\n    if (pidxf == 0 && has_prefix) { data = prefix; szval = strlen(prefix); } \\\n    else { \\\n        if (has_prefix) pidxf--; \\\n        if (has_suffix && pidxf >= (size_t)PyTuple_GET_SIZE(ap)) { data = suffix; szval = strlen(suffix); } \\\n        else { \\\n            PyObject *t = PyTuple_GET_ITEM(ap, pidxf); \\\n            if (PyBytes_Check(t)) { data = PyBytes_AS_STRING(t); szval = PyBytes_GET_SIZE(t); } \\\n            else { \\\n                Py_ssize_t usz; \\\n                data = PyUnicode_AsUTF8AndSize(t, &usz); szval = usz; \\\n                if (!data) fatal(\"Failed to convert object to bytes in schedule_write_to_child_python\"); \\\n            } \\\n        } \\\n    } \\\n}\n    schedule_write_to_child_generic(id, num, py_start, get_next_arg, py_end);\n#undef py_start\n#undef py_end\n#undef get_next_arg\n}\n\nstatic PyObject *\nneeds_write(ChildMonitor UNUSED *self, PyObject *args) {\n#define needs_write_doc \"needs_write(id, data) -> Queue data to be written to child.\"\n    unsigned long id;\n    Py_buffer buf;\n    if (!PyArg_ParseTuple(args, \"ky*\", &id, &buf)) return NULL;\n    if (schedule_write_to_child(id, 1, buf.buf, (size_t)buf.len)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject *\nshutdown_monitor(ChildMonitor *self, PyObject *a UNUSED) {\n#define shutdown_monitor_doc \"shutdown_monitor() -> Shutdown the monitor loop.\"\n    self->shutting_down = true;\n    wakeup_talk_loop(false);\n    wakeup_io_loop(self, false);\n    int ret = pthread_join(self->io_thread, NULL);\n    if (ret != 0) return PyErr_Format(PyExc_OSError, \"Failed to join() I/O thread with error: %s\", strerror(ret));\n    if (talk_thread_started) {\n        ret = pthread_join(self->talk_thread, NULL);\n        if (ret != 0) return PyErr_Format(PyExc_OSError, \"Failed to join() talk thread with error: %s\", strerror(ret));\n    }\n    talk_thread_started = false;\n    Py_RETURN_NONE;\n}\n\nstatic bool\ndo_parse(ChildMonitor *self, Screen *screen, monotonic_t now, bool flush) {\n    ParseData pd = {.dump_callback = self->dump_callback, .now = now};\n    self->parse_func(screen, &pd, flush);\n    if (pd.input_read) {\n        if (pd.write_space_created) wakeup_io_loop(self, false);\n        if (screen->paused_rendering.expires_at) {\n            set_maximum_wait(MAX(0, screen->paused_rendering.expires_at - now));\n        } else set_maximum_wait(OPT(input_delay) - pd.time_since_new_input);\n    } else if (pd.has_pending_input) set_maximum_wait(OPT(input_delay) - pd.time_since_new_input);\n    return pd.input_read;\n}\n\nstatic bool\nparse_input(ChildMonitor *self) {\n    // Parse all available input that was read in the I/O thread.\n    size_t count = 0, remove_count = 0;\n    bool input_read = false, reload_config_called = false;\n    monotonic_t now = monotonic();\n    children_mutex(lock);\n    while (remove_queue_count) {\n        remove_queue_count--;\n        remove_notify[remove_count] = remove_queue[remove_queue_count];\n        INCREF_CHILD(remove_notify[remove_count]);\n        remove_count++;\n        FREE_CHILD(remove_queue[remove_queue_count]);\n    }\n\n    if (UNLIKELY(kill_signal_received || reload_config_signal_received)) {\n        if (kill_signal_received) {\n            global_state.quit_request = IMPERATIVE_CLOSE_REQUESTED;\n            global_state.has_pending_closes = true;\n            request_tick_callback();\n            kill_signal_received = false;\n        }\n        else if (reload_config_signal_received) {\n            reload_config_signal_received = false;\n            reload_config_called = true;\n        }\n    } else {\n        count = self->count;\n        for (size_t i = 0; i < count; i++) {\n            scratch[i] = children[i];\n            INCREF_CHILD(scratch[i]);\n        }\n    }\n    children_mutex(unlock);\n\n    Message *msgs = NULL;\n    size_t msgs_count = 0;\n    talk_mutex(lock);\n    if (UNLIKELY(self->messages_count)) {\n        msgs = malloc(sizeof(Message) * self->messages_count);\n        if (msgs) {\n            memcpy(msgs, self->messages, sizeof(Message) * self->messages_count);\n            msgs_count = self->messages_count;\n        }\n        memset(self->messages, 0, sizeof(Message) * self->messages_capacity);\n        self->messages_count = 0;\n    }\n    talk_mutex(unlock);\n\n    if (msgs_count) {\n        for (size_t i = 0; i < msgs_count; i++) {\n            Message *msg = msgs + i;\n            PyObject *resp = NULL;\n            if (msg->data) {\n                resp = PyObject_CallMethod(global_state.boss, \"peer_message_received\", \"y#KO\", msg->data, (int)msg->sz, msg->peer_id, msg->is_remote_control_peer ? Py_True : Py_False);\n                free(msg->data);\n                if (!resp) PyErr_Print();\n            }\n            if (resp) {\n                if (PyBytes_Check(resp)) send_response_to_peer(msg->peer_id, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp), false);\n                else if (resp == Py_None) send_response_to_peer(msg->peer_id, NULL, 0, false);\n                else if (resp == Py_True) send_response_to_peer(msg->peer_id, NULL, 0, true);\n                Py_CLEAR(resp);\n            } else send_response_to_peer(msg->peer_id, NULL, 0, false);\n        }\n        free(msgs); msgs = NULL;\n    }\n\n    while(remove_count) {\n        // must be done while no locks are held, since the locks are non-recursive and\n        // the python function could call into other functions in this module\n        remove_count--;\n        if (remove_notify[remove_count].screen) do_parse(self, remove_notify[remove_count].screen, now, true);\n        PyObject *t = PyObject_CallFunction(self->death_notify, \"k\", remove_notify[remove_count].id);\n        if (t == NULL) PyErr_Print();\n        else Py_DECREF(t);\n        FREE_CHILD(remove_notify[remove_count]);\n    }\n\n    for (size_t i = 0; i < count; i++) {\n        if (!scratch[i].needs_removal) {\n            if (do_parse(self, scratch[i].screen, now, false)) input_read = true;\n        }\n        DECREF_CHILD(scratch[i]);\n    }\n    if (reload_config_called) {\n        call_boss(load_config_file, NULL);\n    }\n    return input_read;\n}\n\nstatic bool\nmark_child_for_close(ChildMonitor *self, id_type window_id) {\n    bool found = false;\n    children_mutex(lock);\n    for (size_t i = 0; i < self->count; i++) {\n        if (children[i].id == window_id) {\n            children[i].needs_removal = true;\n            found = true;\n            break;\n        }\n    }\n    if (!found) {\n        for (size_t i = 0; i < add_queue_count; i++) {\n            if (add_queue[i].id == window_id) {\n                add_queue[i].needs_removal = true;\n                found = true;\n                break;\n            }\n        }\n\n    }\n    children_mutex(unlock);\n    wakeup_io_loop(self, false);\n    return found;\n}\n\n\nstatic PyObject *\nmark_for_close(ChildMonitor *self, PyObject *args) {\n#define mark_for_close_doc \"Mark a child to be removed from the child monitor\"\n    id_type window_id;\n    if (!PyArg_ParseTuple(args, \"K\", &window_id)) return NULL;\n    if (mark_child_for_close(self, window_id)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic bool\npty_resize(int fd, struct winsize *dim) {\n    while(true) {\n        if (ioctl(fd, TIOCSWINSZ, dim) == -1) {\n            if (errno == EINTR) continue;\n            if (errno != EBADF && errno != ENOTTY) {\n                log_error(\"Failed to resize tty associated with fd: %d with error: %s\", fd, strerror(errno));\n                return false;\n            }\n        }\n        break;\n    }\n    return true;\n}\n\nstatic PyObject *\nresize_pty(ChildMonitor *self, PyObject *args) {\n#define resize_pty_doc \"Resize the pty associated with the specified child\"\n    unsigned long window_id;\n    struct winsize dim;\n    int fd = -1;\n    if (!PyArg_ParseTuple(args, \"kHHHH\", &window_id, &dim.ws_row, &dim.ws_col, &dim.ws_xpixel, &dim.ws_ypixel)) return NULL;\n    children_mutex(lock);\n#define FIND(queue, count) { \\\n    for (size_t i = 0; i < count; i++) { \\\n        if (queue[i].id == window_id) { \\\n            fd = queue[i].fd; \\\n            break; \\\n        } \\\n    }}\n    FIND(children, self->count);\n    if (fd == -1) FIND(add_queue, add_queue_count);\n    if (fd != -1) {\n        if (!pty_resize(fd, &dim)) PyErr_SetFromErrno(PyExc_OSError);\n    } else log_error(\"Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %zu)\", window_id, self->count, add_queue_count);\n    children_mutex(unlock);\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nbool\nset_iutf8(int UNUSED fd, bool UNUSED on) {\n#ifdef IUTF8\n    struct termios attrs;\n    if (tcgetattr(fd, &attrs) != 0) return false;\n    if (on) attrs.c_iflag |= IUTF8;\n    else attrs.c_iflag &= ~IUTF8;\n    if (tcsetattr(fd, TCSANOW, &attrs) != 0) return false;\n#endif\n    return true;\n}\n\nstatic PyObject*\npyset_iutf8(ChildMonitor *self, PyObject *args) {\n    id_type window_id;\n    int on;\n    PyObject *found = Py_False;\n    if (!PyArg_ParseTuple(args, \"Kp\", &window_id, &on)) return NULL;\n    children_mutex(lock);\n    for (size_t i = 0; i < self->count; i++) {\n        if (children[i].id == window_id) {\n            found = Py_True;\n            if (!set_iutf8(children_fds[EXTRA_FDS + i].fd, on & 1)) PyErr_SetFromErrno(PyExc_OSError);\n            break;\n        }\n    }\n    children_mutex(unlock);\n    if (PyErr_Occurred()) return NULL;\n    Py_INCREF(found);\n    return found;\n}\n\n#undef FREE_CHILD\n#undef INCREF_CHILD\n#undef DECREF_CHILD\n\nstatic bool\ncursor_needs_render(Window *w) {\n    return memcmp(&w->render_data.screen->last_rendered.cursor, &w->render_data.screen->cursor_render_info, sizeof(CursorRenderInfo)) != 0;\n}\n\nstatic bool\ncollect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow *os_window) {\n    WindowRenderData *rd = &w->render_data;\n    const Cursor *cursor;\n    if (screen_is_overlay_active(rd->screen)) {\n        // Do not force the cursor to be visible here for the sake of some programs that prefer it hidden\n        cursor = &(rd->screen->overlay_line.original_line.cursor);\n        ans->x = rd->screen->overlay_line.cursor_x;\n        ans->y = rd->screen->overlay_line.ynum;\n    } else {\n        cursor = rd->screen->paused_rendering.expires_at ? &rd->screen->paused_rendering.cursor : rd->screen->cursor;\n        ans->x = cursor->x; ans->y = cursor->y;\n    }\n    ans->is_visible = false; ans->multicursor_count = 0; ans->cursor_opacity = 1; ans->text_blink_opacity = 1;\n    if (!rd->screen->scrolled_by) {\n        ans->multicursor_count = screen_multi_cursor_count(rd->screen);\n        ans->is_visible = screen_is_cursor_visible(rd->screen);\n    }\n    if (!ans->is_visible && ans->multicursor_count == 0 && !rd->screen->sgr_blink_was_used) return cursor_needs_render(w);\n    monotonic_t time_since_start_blink = now - os_window->cursor_blink_zero_time;\n    const bool allow_blinking = OPT(cursor_blink_interval) > 0;\n    const bool blink_has_ceased = OPT(cursor_stop_blinking_after) != 0 && time_since_start_blink > OPT(cursor_stop_blinking_after);\n    const bool cursor_blinking = !cursor->non_blinking && os_window->is_focused;\n    float blink_opacity = 1.f;\n    if (allow_blinking && !blink_has_ceased && (cursor_blinking || rd->screen->sgr_blink_was_used)) {\n        if (animation_is_valid(OPT(animation.cursor))) {\n            monotonic_t duration = OPT(cursor_blink_interval) * 2;\n            monotonic_t time_into_cycle = time_since_start_blink % duration;\n            double frac_into_cycle = (double)time_into_cycle / (double)duration;\n            blink_opacity = (float)apply_easing_curve(OPT(animation.cursor), frac_into_cycle, duration);\n            set_maximum_wait(ANIMATION_SAMPLE_WAIT);\n        } else {\n            monotonic_t n = time_since_start_blink / OPT(cursor_blink_interval);\n            blink_opacity = 1 - n % 2;\n            set_maximum_wait((n + 1) * OPT(cursor_blink_interval) - time_since_start_blink);\n        }\n    }\n    ans->text_blink_opacity = blink_opacity;\n    ans->cursor_opacity = cursor_blinking ? blink_opacity: 1.0f;\n    ans->shape = cursor->shape ? cursor->shape : OPT(cursor_shape);\n    ans->is_focused = os_window->is_focused;\n    return cursor_needs_render(w);\n}\n\nstatic void\nchange_menubar_title(PyObject *title UNUSED) {\n#ifdef __APPLE__\n    static PyObject *current_title = NULL;\n    if (title != current_title) {\n        current_title = title;\n        if (title && OPT(macos_show_window_title_in) & MENUBAR) update_menu_bar_title(title);\n    }\n#endif\n}\n\nstatic bool\nprepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg, bool scan_for_animated_images) {\n#define TD os_window->tab_bar_render_data\n    bool needs_render = os_window->needs_render;\n    os_window->needs_render = false;\n    bool was_previously_rendered_with_layers = os_window->needs_layers;\n    os_window->needs_layers = (\n        !global_state.supports_framebuffer_srgb || effective_os_window_alpha(os_window) < 1.f ||\n        os_window->live_resize.in_progress || (os_window->bgimage && os_window->bgimage->texture_id > 0)\n    );\n    if (TD.screen && os_window->num_tabs && !os_window->has_too_few_tabs) {\n        if (!os_window->tab_bar_data_updated) {\n            call_boss(update_tab_bar_data, \"K\", os_window->id);\n            os_window->tab_bar_data_updated = true;\n        }\n        // we never render a cursor in the tab bar\n        CursorRenderInfo *cri = &TD.screen->cursor_render_info;\n        zero_at_ptr(cri); cri->x = TD.screen->cursor->x; cri->y = TD.screen->cursor->y;\n        if (send_cell_data_to_gpu(TD.vao_idx, TD.screen, os_window)) needs_render = true;\n        os_window->needs_layers = os_window->needs_layers || screen_needs_rendering_in_layers(os_window, NULL, TD.screen);\n    }\n    if (OPT(mouse_hide.hide_wait) > 0 && !is_mouse_hidden(os_window)) {\n        if (now - os_window->last_mouse_activity_at >= OPT(mouse_hide.hide_wait)) hide_mouse(os_window);\n        else set_maximum_wait(OPT(mouse_hide.hide_wait) - now + os_window->last_mouse_activity_at);\n    }\n    Tab *tab = os_window->tabs + os_window->active_tab;\n    *active_window_bg = OPT(background);\n    *all_windows_have_same_bg = true;\n    *num_visible_windows = 0;\n    color_type first_window_bg = 0;\n    os_window->needs_layers = os_window->needs_layers || (OPT(cursor_trail) && tab->cursor_trail.needs_render);\n    for (unsigned int i = 0; i < tab->num_windows; i++) {\n        Window *w = tab->windows + i;\n#define WD w->render_data\n        if (w->visible && WD.screen) {\n            os_window->needs_layers = os_window->needs_layers || screen_needs_rendering_in_layers(os_window, w, WD.screen);\n            screen_check_pause_rendering(WD.screen, now);\n            *num_visible_windows += 1;\n            color_type window_bg = colorprofile_to_color(WD.screen->color_profile, WD.screen->color_profile->overridden.default_bg, WD.screen->color_profile->configured.default_bg).rgb;\n            if (*num_visible_windows == 1) first_window_bg = window_bg;\n            if (first_window_bg != window_bg) *all_windows_have_same_bg = false;\n            if (w->last_drag_scroll_at > 0) {\n                if (now - w->last_drag_scroll_at >= ms_to_monotonic_t(20ll)) {\n                    if (drag_scroll(w, os_window)) {\n                        w->last_drag_scroll_at = now;\n                        set_maximum_wait(ms_to_monotonic_t(20ll));\n                        needs_render = true;\n                    } else w->last_drag_scroll_at = 0;\n                } else set_maximum_wait(now - w->last_drag_scroll_at);\n            }\n            bool is_active_window = i == tab->active_window;\n            if (is_active_window) {\n                *active_window_id = w->id;\n                if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true;\n                WD.screen->cursor_render_info.is_focused = os_window->is_focused;\n                set_os_window_title_from_window(w, os_window);\n                *active_window_bg = window_bg;\n                if (OPT(cursor_trail)) {\n                    if (os_window->last_active_tab != os_window->active_tab && os_window->last_active_tab < os_window->num_tabs) {\n                        tab->cursor_trail = os_window->tabs[os_window->last_active_tab].cursor_trail;\n                        tab->cursor_trail.needs_render = true;\n                        tab->cursor_trail.updated_at = now;\n                        os_window->cursor_blink_zero_time = now;\n                    }\n                    if (update_cursor_trail(&tab->cursor_trail, w, now, os_window)) {\n                        needs_render = true;\n                        // A max wait of zero causes key input processing to be\n                        // slow so handle the case of OPT(repaint_delay) == 0, see https://github.com/kovidgoyal/kitty/pull/8066\n                        set_maximum_wait(MAX(OPT(repaint_delay), ms_to_monotonic_t(1ll)));\n                    } else if (OPT(cursor_trail) > now - WD.screen->cursor->position_changed_by_client_at) {\n                        // If update_cursor_trail failed due to time threshold, the trail animation\n                        // should be evaluated again shortly. Schedule next update when enough time\n                        // has passed since the cursor was last moved.\n                        set_maximum_wait(OPT(cursor_trail) - now + WD.screen->cursor->position_changed_by_client_at);\n                    }\n                }\n            } else {\n                if (WD.screen->cursor_render_info.render_even_when_unfocused) {\n                    if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true;\n                    WD.screen->cursor_render_info.is_focused = false;\n                } else {\n                    if (WD.screen->sgr_blink_was_used) {\n                        if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true;\n                        WD.screen->cursor_render_info.is_focused = false;\n                    } else {\n                        WD.screen->cursor_render_info.text_blink_opacity = 1;\n                    }\n                    WD.screen->cursor_render_info.cursor_opacity = 0;\n                }\n            }\n            if (scan_for_animated_images) {\n                monotonic_t min_gap;\n                if (scan_active_animations(WD.screen->grman, now, &min_gap, true)) needs_render = true;\n                if (min_gap < MONOTONIC_T_MAX) {\n                    global_state.check_for_active_animated_images = true;\n                    set_maximum_wait(min_gap);\n                }\n            }\n            if (send_cell_data_to_gpu(WD.vao_idx, WD.screen, os_window)) needs_render = true;\n            if (WD.screen->start_visual_bell_at != 0) needs_render = true;\n            // Prepare window title bar screen data for GPU\n            WindowRenderData *trd = &w->window_title_render_data;\n            if (trd->screen && trd->geometry.bottom > trd->geometry.top && trd->geometry.right > trd->geometry.left) {\n                trd->screen->cursor_render_info.is_visible = false;\n                if (send_cell_data_to_gpu(trd->vao_idx, trd->screen, os_window)) needs_render = true;\n            }\n        }\n    }\n    return needs_render || was_previously_rendered_with_layers != os_window->needs_layers;\n}\n\nstatic void\nthumbnail_callback(OSWindow *os_window) {\n#define tc global_state.thumbnail_callback\n    Region region = {.right=os_window->viewport_width, .bottom=os_window->viewport_height};\n    if (tc.window) {\n        Window *w = window_for_window_id(tc.window);\n        if (!w) return;\n        region.left = w->render_data.geometry.left;\n        region.top = w->render_data.geometry.top;\n        region.right = w->render_data.geometry.right;\n        region.bottom = w->render_data.geometry.bottom;\n    } else {\n        if (!tc.include_tab_bar) {\n            Region central = {0}, tab_bar = {0};\n            os_window_regions(os_window, &central, &tab_bar);\n            if (tab_bar.bottom > tab_bar.top) region = central;\n        }\n    }\n    unsigned vw = region.right - region.left, vh = region.bottom - region.top;\n    unsigned thumb_w = (unsigned)(vw * tc.scale), thumb_h = (unsigned)(vh * tc.scale);\n    if (thumb_w > tc.max_width) {\n        thumb_w = tc.max_width;\n        double scale = 300. / vw;\n        thumb_h = (unsigned)(vh * scale + 0.5f);\n    }\n    RAII_PyObject(pixels, PyBytes_FromStringAndSize(NULL, 4 * thumb_w * thumb_h));\n    if (pixels && global_state.boss) {\n        take_screenshot_of_rectangular_region(\n            os_window, region, (unsigned char*)PyBytes_AS_STRING(pixels), &thumb_w, &thumb_h);\n        _PyBytes_Resize(&pixels, 4 * thumb_w *thumb_h);\n        PyObject *r = PyObject_CallMethod(\n            global_state.boss, tc.callback, \"KKOII\", os_window->id, tc.window, pixels, thumb_w, thumb_h);\n        if (!r) PyErr_Print(); else Py_DECREF(r);\n    }\n#undef tc\n}\n\nstatic void\nrender_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) {\n    Tab *tab = os_window->tabs + os_window->active_tab;\n    setup_os_window_for_rendering(os_window, tab, NULL, true);\n    BorderRects *br = &tab->border_rects;\n    draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window);\n    br->is_dirty = false;\n    if (TD.screen && os_window->num_tabs && !os_window->has_too_few_tabs) draw_cells(&TD, os_window, true, true, false, NULL);\n    unsigned int num_of_visible_windows = 0;\n    Window *active_window = NULL;\n    for (unsigned int i = 0; i < tab->num_windows; i++) { if (tab->windows[i].visible) num_of_visible_windows++; }\n    for (unsigned int i = 0; i < tab->num_windows; i++) {\n        Window *w = tab->windows + i;\n        if (w->visible && WD.screen) {\n            bool is_active_window = i == tab->active_window;\n            if (is_active_window) active_window = w;\n            draw_cells(&WD, os_window, is_active_window, false, num_of_visible_windows == 1, w);\n            if (WD.screen->start_visual_bell_at != 0) set_maximum_wait(ANIMATION_SAMPLE_WAIT);\n            WindowRenderData *trd = &w->window_title_render_data;\n            if (trd->screen && trd->geometry.right > trd->geometry.left && trd->geometry.bottom > trd->geometry.top)\n                draw_cells(trd, os_window, i == tab->active_window, true, false, NULL);\n        }\n    }\n    setup_os_window_for_rendering(os_window, tab, active_window, false);\n    if (global_state.thumbnail_callback.os_window == os_window->id) {\n        thumbnail_callback(os_window);\n        global_state.thumbnail_callback.os_window = 0;\n    }\n    swap_window_buffers(os_window);\n    os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id;\n    os_window->focused_at_last_render = os_window->is_focused;\n    if (os_window->redraw_count) os_window->redraw_count--;\n    if (USE_RENDER_FRAMES) request_frame_render(os_window);\n#undef WD\n#undef TD\n}\n\nstatic bool\nno_render_frame_received_recently(OSWindow *w, monotonic_t now, monotonic_t max_wait) {\n    bool ans = now - w->last_render_frame_received_at > max_wait;\n    if (ans && global_state.debug_rendering) {\n        if (global_state.is_wayland) {\n            log_error(\"No render frame received in %.2f seconds\", monotonic_t_to_s_double(max_wait));\n        } else  {\n            log_error(\"No render frame received in %.2f seconds, re-requesting\", monotonic_t_to_s_double(max_wait));\n        }\n    }\n    return ans;\n}\n\nbool\nrender_os_window(OSWindow *w, monotonic_t now, bool scan_for_animated_images) {\n    if (!w->num_tabs) return false;\n    if (!should_os_window_be_rendered(w) && global_state.thumbnail_callback.os_window != w->id) {\n        update_os_window_title(w);\n        if (w->is_focused) change_menubar_title(w->window_title);\n        return false;\n    }\n    if (!w->keep_rendering_till_swap && USE_RENDER_FRAMES && w->render_state != RENDER_FRAME_READY) {\n        if (w->render_state == RENDER_FRAME_NOT_REQUESTED || no_render_frame_received_recently(w, now, ms_to_monotonic_t(250ll))) request_frame_render(w);\n        if (w->id != global_state.thumbnail_callback.os_window) {\n            // dont respect render frames soon after a resize on Wayland as they cause flicker because\n            // we want to fill the newly resized buffer ASAP, not at compositors convenience\n            if (!global_state.is_wayland || (monotonic() - w->viewport_resized_at) > s_double_to_monotonic_t(1)) {\n                return false;\n            }\n        }\n    }\n    w->render_calls++;\n    make_os_window_context_current(w);\n    bool needs_render = w->redraw_count > 0 || w->live_resize.in_progress || global_state.thumbnail_callback.os_window == w->id;\n    if (w->viewport_size_dirty) {\n        set_gpu_viewport(w->viewport_width, w->viewport_height);\n        w->viewport_size_dirty = false;\n        needs_render = true;\n    }\n    unsigned int active_window_id = 0, num_visible_windows = 0;\n    bool all_windows_have_same_bg;\n    color_type active_window_bg = 0;\n    if (!w->fonts_data) { log_error(\"No fonts data found for window id: %llu\", w->id); return false; }\n    if (prepare_to_render_os_window(w, now, &active_window_id, &active_window_bg, &num_visible_windows, &all_windows_have_same_bg, scan_for_animated_images)) needs_render = true;\n    if (w->last_active_window_id != active_window_id || w->last_active_tab != w->active_tab || w->focused_at_last_render != w->is_focused) needs_render = true;\n    if (w->render_calls < 3 && w->bgimage && w->bgimage->texture_id) needs_render = true;\n    if (needs_render) render_prepared_os_window(w, active_window_id, active_window_bg, num_visible_windows, all_windows_have_same_bg);\n    if (w->is_focused) change_menubar_title(w->window_title);\n    return needs_render;\n}\n\nstatic void\nrender(monotonic_t now, bool input_read) {\n    EVDBG(\"input_read: %d, check_for_active_animated_images: %d\\n\", input_read, global_state.check_for_active_animated_images);\n    static monotonic_t last_render_at = MONOTONIC_T_MIN;\n    monotonic_t time_since_last_render = last_render_at == MONOTONIC_T_MIN ? OPT(repaint_delay) : now - last_render_at;\n    if (!input_read && time_since_last_render < OPT(repaint_delay) && !global_state.thumbnail_callback.os_window) {\n        set_maximum_wait(OPT(repaint_delay) - time_since_last_render);\n        return;\n    }\n\n    const bool scan_for_animated_images = global_state.check_for_active_animated_images;\n    global_state.check_for_active_animated_images = false;\n\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = global_state.os_windows + i;\n#ifdef __APPLE__\n        // rendering is done in cocoa_os_window_resized()\n        if (w->live_resize.in_progress) continue;\n#endif\n        if (!render_os_window(w, now, scan_for_animated_images)) {\n            // since we didn't scan the window for animations, force a rescan on next wakeup/render frame\n            if (scan_for_animated_images) global_state.check_for_active_animated_images = true;\n        }\n        if (w->keep_rendering_till_swap) {\n            debug_rendering(\"Re-rendering window %llu on the %u attempt since swap did not happen\\n\", w->id, w->keep_rendering_till_swap);\n            set_maximum_wait(OPT(repaint_delay));\n            w->needs_render = true;\n            w->keep_rendering_till_swap--;\n        }\n\n    }\n    last_render_at = now;\n#undef TD\n}\n\n\ntypedef struct { int fd; uint8_t *buf; size_t sz; } ThreadWriteData;\n\nstatic ThreadWriteData*\nalloc_twd(size_t sz) {\n    ThreadWriteData *data = calloc(1, sizeof(ThreadWriteData));\n    if (data != NULL) {\n        data->sz = sz;\n        data->buf = malloc(sz);\n        if (data->buf == NULL) { free(data); data = NULL; }\n    }\n    return data;\n}\n\nstatic void\nfree_twd(ThreadWriteData *x) {\n    if (x != NULL) free(x->buf);\n    free(x);\n}\n\nstatic PyObject*\nsig_queue(PyObject *self UNUSED, PyObject *args) {\n    int pid, signal, value;\n    if (!PyArg_ParseTuple(args, \"iii\", &pid, &signal, &value)) return NULL;\n#ifdef NO_SIGQUEUE\n    if (kill(pid, signal) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }\n#else\n    union sigval v;\n    v.sival_int = value;\n    if (sigqueue(pid, signal, v) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }\n#endif\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nmonitor_pid(PyObject *self UNUSED, PyObject *args) {\n    int pid;\n    bool ok = true;\n    if (!PyArg_ParseTuple(args, \"i\", &pid)) return NULL;\n    children_mutex(lock);\n    if (monitored_pids_count >= arraysz(monitored_pids)) {\n        PyErr_SetString(PyExc_RuntimeError, \"Too many monitored pids\");\n        ok = false;\n    } else {\n        monitored_pids[monitored_pids_count++] = pid;\n    }\n    children_mutex(unlock);\n    if (!ok) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic void\nreport_reaped_pids(void) {\n    static ReapedPID pids[64];\n    size_t i = 0;\n    children_mutex(lock);\n    if (reaped_pids_count) {\n        for (; i < reaped_pids_count && i < arraysz(pids); i++) {\n            pids[i] = reaped_pids[i];\n        }\n        reaped_pids_count = 0;\n    }\n    children_mutex(unlock);\n    for (size_t n = 0; n < i; n++) { call_boss(on_monitored_pid_death, \"li\", (long)pids[n].pid, pids[n].status); }\n}\n\nstatic void*\nthread_write(void *x) {\n    ThreadWriteData *data = (ThreadWriteData*)x;\n    set_thread_name(\"KittyWriteStdin\");\n    int flags = fcntl(data->fd, F_GETFL, 0);\n    if (flags == -1) { free_twd(data); return 0; }\n    flags &= ~O_NONBLOCK;\n    fcntl(data->fd, F_SETFL, flags);\n    size_t pos = 0;\n    while (pos < data->sz) {\n        errno = 0;\n        ssize_t nbytes = write(data->fd, data->buf + pos, data->sz - pos);\n        if (nbytes < 0) {\n            if (errno == EAGAIN || errno == EINTR) continue;\n            break;\n        }\n        if (nbytes == 0) break;\n        pos += nbytes;\n    }\n    if (pos < data->sz) {\n        log_error(\"Failed to write all data to STDIN of child process with error: %s\", strerror(errno));\n    }\n    safe_close(data->fd, __FILE__, __LINE__);\n    free_twd(data);\n    return 0;\n}\n\nPyObject*\ncm_thread_write(PyObject UNUSED *self, PyObject *args) {\n    static pthread_t thread;\n    int fd;\n    Py_ssize_t sz;\n    const char *buf;\n    if (!PyArg_ParseTuple(args, \"is#\", &fd, &buf, &sz)) return NULL;\n    ThreadWriteData *data = alloc_twd(sz);\n    if (data == NULL) return PyErr_NoMemory();\n    data->fd = fd;\n    memcpy(data->buf, buf, data->sz);\n    int ret = pthread_create(&thread, NULL, thread_write, data);\n    if (ret != 0) { safe_close(fd, __FILE__, __LINE__); free_twd(data); return PyErr_Format(PyExc_OSError, \"Failed to start write thread with error: %s\", strerror(ret)); }\n    pthread_detach(thread);\n    Py_RETURN_NONE;\n}\n\nstatic void\npython_timer_callback(id_type timer_id, void *data) {\n    PyObject *callback = (PyObject*)data;\n    unsigned long long id = timer_id;\n    PyObject *ret = PyObject_CallFunction(callback, \"K\", id);\n    if (ret == NULL) PyErr_Print();\n    else Py_DECREF(ret);\n}\n\nstatic void\npython_timer_cleanup(id_type timer_id UNUSED, void *data) {\n    if (data) Py_DECREF((PyObject*)data);\n}\n\nstatic PyObject*\nadd_python_timer(PyObject *self UNUSED, PyObject *args) {\n    PyObject *callback;\n    double interval;\n    int repeats = 1;\n    if (!PyArg_ParseTuple(args, \"Od|p\", &callback, &interval, &repeats)) return NULL;\n    unsigned long long timer_id = add_main_loop_timer(s_double_to_monotonic_t(interval), repeats ? true: false, python_timer_callback, callback, python_timer_cleanup);\n    Py_INCREF(callback);\n    return Py_BuildValue(\"K\", timer_id);\n}\n\nstatic PyObject*\nremove_python_timer(PyObject *self UNUSED, PyObject *args) {\n    unsigned long long timer_id;\n    if (!PyArg_ParseTuple(args, \"K\", &timer_id)) return NULL;\n    remove_main_loop_timer(timer_id);\n    Py_RETURN_NONE;\n}\n\n\nstatic void\nprocess_pending_resizes(monotonic_t now) {\n    global_state.has_pending_resizes = false;\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = global_state.os_windows + i;\n        if (w->live_resize.in_progress) {\n            bool update_viewport = false;\n            if (w->live_resize.from_os_notification) {\n                if (w->live_resize.os_says_resize_complete) update_viewport = true;\n                else {\n                    // prevent a \"hang\" if the OS never sends a resize complete event\n                    // also reflow the screen when the user pauses resizing so the user can see what the resized\n                    // screen will look like.\n                    if ((now - w->live_resize.last_resize_event_at) > OPT(resize_debounce_time).on_pause) update_viewport = true;\n                    else {\n                        global_state.has_pending_resizes = true;\n                        set_maximum_wait(s_double_to_monotonic_t(0.05));\n                    }\n                }\n            } else {\n                monotonic_t debounce_time = OPT(resize_debounce_time).on_end;\n                // if more than one resize event has occurred, wait at least 0.2 secs\n                // before repainting, to avoid rapid transitions between the cells banner\n                // and the normal screen\n                if (now - w->live_resize.last_resize_event_at >= debounce_time) update_viewport = true;\n                else {\n                    global_state.has_pending_resizes = true;\n                    set_maximum_wait(debounce_time - now + w->live_resize.last_resize_event_at);\n                }\n            }\n            if (update_viewport) {\n                update_os_window_viewport(w, true);\n                change_live_resize_state(w, false);\n                zero_at_ptr(&w->live_resize);\n                // because the window size should be hidden even if update_os_window_viewport does nothing\n                // On Wayland some compositors require two redraws after a\n                // resize to actually render correctly (Run kitty -1 --wait-for-os-window-close in sway to reproduce)\n                w->redraw_count = global_state.is_wayland ? 2 : 1;\n            }\n        }\n    }\n}\n\nstatic void\nclose_os_window(ChildMonitor *self, OSWindow *os_window) {\n    int w = os_window->window_width, h = os_window->window_height;\n    if (os_window->before_fullscreen.is_set && is_os_window_fullscreen(os_window)) {\n        w = os_window->before_fullscreen.w; h = os_window->before_fullscreen.h;\n    }\n    int x = 0, y = 0;\n    if (os_window->handle && !global_state.is_wayland) glfwGetWindowPos(os_window->handle, &x, &y);\n    bool is_layer_shell = os_window->is_layer_shell;\n    destroy_os_window(os_window);\n    call_boss(on_os_window_closed, \"KiiiiO\", os_window->id, x, y, w, h, is_layer_shell ? Py_True : Py_False);\n    for (size_t t=0; t < os_window->num_tabs; t++) {\n        Tab *tab = os_window->tabs + t;\n        for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);\n    }\n    remove_os_window(os_window->id);\n}\n\nstatic bool\nprocess_pending_closes(ChildMonitor *self) {\n    if (global_state.quit_request == CONFIRMABLE_CLOSE_REQUESTED) {\n        call_boss(quit, \"\");\n    }\n    if (global_state.quit_request == IMPERATIVE_CLOSE_REQUESTED) {\n        for (size_t w = 0; w < global_state.num_os_windows; w++) global_state.os_windows[w].close_request = IMPERATIVE_CLOSE_REQUESTED;\n    }\n    bool has_open_windows = false;\n    for (size_t w = global_state.num_os_windows; w > 0; w--) {\n        OSWindow *os_window = global_state.os_windows + w - 1;\n        switch(os_window->close_request) {\n            case NO_CLOSE_REQUESTED:\n                has_open_windows = true;\n                break;\n            case CONFIRMABLE_CLOSE_REQUESTED:\n                os_window->close_request = CLOSE_BEING_CONFIRMED;\n                call_boss(confirm_os_window_close, \"K\", os_window->id);\n                if (os_window->close_request == IMPERATIVE_CLOSE_REQUESTED) {\n                    close_os_window(self, os_window);\n                } else has_open_windows = true;\n                break;\n            case CLOSE_BEING_CONFIRMED:\n                has_open_windows = true;\n                break;\n            case IMPERATIVE_CLOSE_REQUESTED:\n                close_os_window(self, os_window);\n                break;\n        }\n    }\n    global_state.has_pending_closes = false;\n#ifdef __APPLE__\n    if (!OPT(macos_quit_when_last_window_closed)) {\n        if (!has_open_windows && global_state.quit_request != IMPERATIVE_CLOSE_REQUESTED) has_open_windows = true;\n    }\n#endif\n    return !has_open_windows;\n}\n\n#ifdef __APPLE__\n// If we create new OS windows during wait_events(), using global menu actions\n// via the mouse causes a crash because of the way autorelease pools work in\n// glfw/cocoa. So we use a flag instead.\nstatic bool cocoa_pending_actions[NUM_COCOA_PENDING_ACTIONS] = {0};\nstatic bool has_cocoa_pending_actions = false;\ntypedef struct cocoa_list { char **items; size_t count, capacity; } cocoa_list;\ntypedef struct {\n    char* wd;\n    cocoa_list open_urls, untracked_notifications;\n} CocoaPendingActionsData;\nstatic CocoaPendingActionsData cocoa_pending_actions_data = {0};\n\nstatic void\ncocoa_append_to_pending_list(cocoa_list *array, const char* item) {\n    ensure_space_for(array, items, char*, array->count + 1, capacity, 8, false);\n    array->items[array->count++] = strdup(item);\n}\n\nstatic void\ncocoa_free_pending_list(cocoa_list *array) {\n    for (size_t i = 0; i < array->count; i++) free(array->items[i]);\n    free(array->items); zero_at_ptr(array);\n}\n\nstatic void\ncocoa_free_actions_data(void) {\n    if (cocoa_pending_actions_data.wd) { free(cocoa_pending_actions_data.wd); cocoa_pending_actions_data.wd = NULL; }\n    cocoa_free_pending_list(&cocoa_pending_actions_data.open_urls);\n    cocoa_free_pending_list(&cocoa_pending_actions_data.untracked_notifications);\n}\n\nvoid\nset_cocoa_pending_action(CocoaPendingAction action, const char *data) {\n    if (data) {\n        switch(action) {\n            case LAUNCH_URLS:\n                cocoa_append_to_pending_list(&cocoa_pending_actions_data.open_urls, data); break;\n            case COCOA_NOTIFICATION_UNTRACKED:\n                cocoa_append_to_pending_list(&cocoa_pending_actions_data.untracked_notifications, data); break;\n            default:\n                if (cocoa_pending_actions_data.wd) free(cocoa_pending_actions_data.wd);\n                cocoa_pending_actions_data.wd = strdup(data);\n                break;\n        }\n    }\n    cocoa_pending_actions[action] = true;\n    has_cocoa_pending_actions = true;\n    // The main loop may be blocking on the event queue, if e.g. unfocused.\n    // Unjam it so the pending action is processed right now.\n    wakeup_main_loop();\n}\n\nstatic void\nprocess_cocoa_pending_actions(void) {\n    if (cocoa_pending_actions[PREFERENCES_WINDOW]) { call_boss(edit_config_file, NULL); }\n    if (cocoa_pending_actions[NEW_OS_WINDOW]) { call_boss(new_os_window, NULL); }\n    if (cocoa_pending_actions[CLOSE_OS_WINDOW]) { call_boss(close_os_window, NULL); }\n    if (cocoa_pending_actions[CLOSE_TAB]) { call_boss(close_tab, NULL); }\n    if (cocoa_pending_actions[NEW_TAB]) { call_boss(new_tab, NULL); }\n    if (cocoa_pending_actions[NEXT_TAB]) { call_boss(next_tab, NULL); }\n    if (cocoa_pending_actions[PREVIOUS_TAB]) { call_boss(previous_tab, NULL); }\n    if (cocoa_pending_actions[DETACH_TAB]) { call_boss(detach_tab, NULL); }\n    if (cocoa_pending_actions[NEW_WINDOW]) { call_boss(new_window, NULL); }\n    if (cocoa_pending_actions[CLOSE_WINDOW]) { call_boss(close_window, NULL); }\n    if (cocoa_pending_actions[RESET_TERMINAL]) { call_boss(clear_terminal, \"sO\", \"reset\", Py_True ); }\n    if (cocoa_pending_actions[CLEAR_TERMINAL_AND_SCROLLBACK]) { call_boss(clear_terminal, \"sO\", \"to_cursor\", Py_True ); }\n    if (cocoa_pending_actions[CLEAR_SCROLLBACK]) { call_boss(clear_terminal, \"sO\", \"scrollback\", Py_True ); }\n    if (cocoa_pending_actions[CLEAR_SCREEN]) { call_boss(clear_terminal, \"sO\", \"to_cursor_scroll\", Py_True ); }\n    if (cocoa_pending_actions[CLEAR_LAST_COMMAND]) { call_boss(clear_terminal, \"sO\", \"last_command\", Py_True ); }\n    if (cocoa_pending_actions[RELOAD_CONFIG]) { call_boss(load_config_file, NULL); }\n    if (cocoa_pending_actions[TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY]) { call_boss(toggle_macos_secure_keyboard_entry, NULL); }\n    if (cocoa_pending_actions[MACOS_CYCLE_THROUGH_OS_WINDOWS]) { call_boss(macos_cycle_through_os_windows, NULL); }\n    if (cocoa_pending_actions[MACOS_CYCLE_THROUGH_OS_WINDOWS_BACKWARDS]) { call_boss(macos_cycle_through_os_windows_backwards, NULL); }\n    if (cocoa_pending_actions[SEARCH_SCROLLBACK]) { call_boss(search_scrollback_in_active, NULL); }\n    if (cocoa_pending_actions[TOGGLE_FULLSCREEN]) { call_boss(toggle_fullscreen, NULL); }\n    if (cocoa_pending_actions[OPEN_KITTY_WEBSITE]) { call_boss(open_kitty_website, NULL); }\n    if (cocoa_pending_actions[HIDE]) { call_boss(hide_macos_app, NULL); }\n    if (cocoa_pending_actions[HIDE_OTHERS]) { call_boss(hide_macos_other_apps, NULL); }\n    if (cocoa_pending_actions[MINIMIZE]) { call_boss(minimize_macos_window, NULL); }\n    if (cocoa_pending_actions[QUIT]) { call_boss(quit, NULL); }\n    if (cocoa_pending_actions_data.wd) {\n        if (cocoa_pending_actions[NEW_OS_WINDOW_WITH_WD]) { call_boss(new_os_window_with_wd, \"sO\", cocoa_pending_actions_data.wd, Py_True); }\n        if (cocoa_pending_actions[NEW_TAB_WITH_WD]) { call_boss(new_tab_with_wd, \"sO\", cocoa_pending_actions_data.wd, Py_True); }\n        if (cocoa_pending_actions[USER_MENU_ACTION]) { call_boss(user_menu_action, \"s\", cocoa_pending_actions_data.wd); }\n        free(cocoa_pending_actions_data.wd);\n        cocoa_pending_actions_data.wd = NULL;\n    }\n    for (unsigned cpa = 0; cpa < cocoa_pending_actions_data.open_urls.count; cpa++) {\n        if (cocoa_pending_actions_data.open_urls.items[cpa]) {\n            call_boss(launch_urls, \"s\", cocoa_pending_actions_data.open_urls.items[cpa]);\n            free(cocoa_pending_actions_data.open_urls.items[cpa]);\n            cocoa_pending_actions_data.open_urls.items[cpa] = NULL;\n        }\n    }\n    cocoa_pending_actions_data.open_urls.count = 0;\n\n    for (unsigned cpa = 0; cpa < cocoa_pending_actions_data.untracked_notifications.count; cpa++) {\n        if (cocoa_pending_actions_data.untracked_notifications.items[cpa]) {\n            cocoa_report_live_notifications(cocoa_pending_actions_data.untracked_notifications.items[cpa]);\n            free(cocoa_pending_actions_data.untracked_notifications.items[cpa]);\n            cocoa_pending_actions_data.untracked_notifications.items[cpa] = NULL;\n        }\n    }\n    cocoa_pending_actions_data.untracked_notifications.count = 0;\n\n\n    memset(cocoa_pending_actions, 0, sizeof(cocoa_pending_actions));\n    has_cocoa_pending_actions = false;\n\n}\n#endif\n\nstatic void process_global_state(void *data);\n\nstatic void\ndo_state_check(id_type timer_id UNUSED, void *data) {\n    EVDBG(\"State check timer fired\");\n    process_global_state(data);\n}\n\nstatic id_type state_check_timer = 0;\n\nstatic void\nprocess_global_state(void *data) {\n    EVDBG(\"Processing global state\");\n    ChildMonitor *self = data;\n    maximum_wait = -1;\n    bool state_check_timer_enabled = false;\n    bool input_read = false;\n\n    monotonic_t now = monotonic();\n    if (global_state.has_pending_resizes) {\n        process_pending_resizes(now);\n        input_read = true;\n    }\n    if (parse_input(self)) input_read = true;\n    render(now, input_read);\n#ifdef __APPLE__\n    if (has_cocoa_pending_actions) {\n        process_cocoa_pending_actions();\n        maximum_wait = 0;  // ensure loop ticks again so that the actions side effects are performed immediately\n    }\n#endif\n    report_reaped_pids();\n    bool should_quit = false;\n    if (global_state.has_pending_closes) should_quit = process_pending_closes(self);\n    if (should_quit) {\n        stop_main_loop();\n    } else {\n        if (maximum_wait >= 0) {\n            if (maximum_wait == 0) request_tick_callback();\n            else state_check_timer_enabled = true;\n        }\n    }\n    update_main_loop_timer(state_check_timer, MAX(0, maximum_wait), state_check_timer_enabled);\n}\n\nstatic PyObject*\nmain_loop(ChildMonitor *self, PyObject *a UNUSED) {\n#define main_loop_doc \"The main thread loop\"\n    state_check_timer = add_main_loop_timer(1000, true, do_state_check, self, NULL);\n    run_main_loop(process_global_state, self);\n#ifdef __APPLE__\n    cocoa_free_actions_data();\n#endif\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\n// }}}\n\n// I/O thread functions {{{\n\nstatic void\nadd_children(ChildMonitor *self) {\n    for (; add_queue_count > 0 && self->count < MAX_CHILDREN;) {\n        add_queue_count--;\n        children[self->count] = add_queue[add_queue_count];\n        add_queue[add_queue_count] = EMPTY_CHILD;\n        children_fds[EXTRA_FDS + self->count].fd = children[self->count].fd;\n        children_fds[EXTRA_FDS + self->count].events = POLLIN;\n        self->count++;\n    }\n}\n\n\nstatic void\nhangup(pid_t pid) {\n    errno = 0;\n    pid_t pgid = getpgid(pid);\n    if (errno == ESRCH) return;\n    if (errno != 0) { perror(\"Failed to get process group id for child\"); return; }\n    if (killpg(pgid, SIGHUP) != 0) {\n        if (errno != ESRCH) perror(\"Failed to kill child\");\n    }\n}\n\n\nstatic void\ncleanup_child(ssize_t i) {\n    safe_close(children[i].fd, __FILE__, __LINE__);\n    hangup(children[i].pid);\n}\n\n\nstatic void\nremove_children(ChildMonitor *self) {\n    if (self->count > 0) {\n        size_t count = 0;\n        for (ssize_t i = self->count - 1; i >= 0; i--) {\n            if (children[i].needs_removal) {\n                count++;\n                cleanup_child(i);\n                remove_queue[remove_queue_count] = children[i];\n                remove_queue_count++;\n                children[i] = EMPTY_CHILD;\n                children_fds[EXTRA_FDS + i].fd = -1;\n                size_t num_to_right = self->count - 1 - i;\n                if (num_to_right > 0) {\n                    memmove(children + i, children + i + 1, num_to_right * sizeof(Child));\n                    memmove(children_fds + EXTRA_FDS + i, children_fds + EXTRA_FDS + i + 1, num_to_right * sizeof(struct pollfd));\n                }\n            }\n        }\n        self->count -= count;\n    }\n}\n\n\nstatic bool\nread_bytes(int fd, Screen *screen) {\n    ssize_t len;\n    size_t available_buffer_space;\n\n    uint8_t *buf = vt_parser_create_write_buffer(screen->vt_parser, &available_buffer_space);\n    if (!available_buffer_space) return true;\n\n    while(true) {\n        len = read(fd, buf, available_buffer_space);\n        if (len < 0) {\n            if (errno == EINTR || errno == EAGAIN) continue;\n            if (errno != EIO) perror(\"Call to read() from child fd failed\");\n            vt_parser_commit_write(screen->vt_parser, 0);\n            return false;\n        }\n        break;\n    }\n    vt_parser_commit_write(screen->vt_parser, len);\n    return len != 0;\n}\n\n\ntypedef struct { bool kill_signal, child_died, reload_config; } SignalSet;\n\nstatic bool\nhandle_signal(const siginfo_t *siginfo, void *data) {\n    SignalSet *ss = data;\n    switch(siginfo->si_signo) {\n        case SIGINT:\n        case SIGTERM:\n        case SIGHUP:\n            ss->kill_signal = true;\n            break;\n        case SIGCHLD:\n            ss->child_died = true;\n            break;\n        case SIGUSR1:\n            ss->reload_config = true;\n            break;\n        case SIGUSR2:\n            log_error(\"Received SIGUSR2: %d\\n\", siginfo->si_value.sival_int);\n            break;\n        default:\n            break;\n    }\n    return true;\n}\n\nstatic void\nmark_child_for_removal(ChildMonitor *self, pid_t pid) {\n    children_mutex(lock);\n    for (size_t i = 0; i < self->count; i++) {\n        if (children[i].pid == pid) {\n            children[i].needs_removal = true;\n            break;\n        }\n    }\n    children_mutex(unlock);\n}\n\nstatic void\nmark_monitored_pids(pid_t pid, int status) {\n    children_mutex(lock);\n    for (ssize_t i = monitored_pids_count - 1; i >= 0; i--) {\n        if (pid == monitored_pids[i]) {\n            if (reaped_pids_count < arraysz(reaped_pids)) {\n                reaped_pids[reaped_pids_count].status = status;\n                reaped_pids[reaped_pids_count++].pid = pid;\n            }\n            remove_i_from_array(monitored_pids, (size_t)i, monitored_pids_count);\n        }\n    }\n    children_mutex(unlock);\n}\n\nstatic void\nreap_children(ChildMonitor *self, bool enable_close_on_child_death) {\n    int status;\n    pid_t pid;\n    (void)self;\n    while(true) {\n        pid = waitpid(-1, &status, WNOHANG);\n        if (pid == -1) {\n            if (errno != EINTR) break;\n        } else if (pid > 0) {\n            if (enable_close_on_child_death) mark_child_for_removal(self, pid);\n            mark_monitored_pids(pid, status);\n        } else break;\n    }\n}\n\n#ifdef KITTY_PRINT_BYTES_SENT_TO_CHILD\nstatic void\nprint_text(const unsigned char *text, ssize_t sz) {\n    for (ssize_t i = 0; i < sz; i++) {\n        unsigned char ch = text[i];\n        if (32 <= ch && ch < 127) {\n            if (ch == '\\\\') fprintf(stderr, \"%c\", ch);\n            fprintf(stderr, \"%c\", ch);\n        } else fprintf(stderr, \"\\\\x%02x\", ch);\n    }\n}\n#endif\n\n\nstatic void\nwrite_to_child(int fd, Screen *screen) {\n    size_t written = 0;\n    ssize_t ret = 0;\n    screen_mutex(lock, write);\n    while (written < screen->write_buf_used) {\n        ret = write(fd, screen->write_buf + written, screen->write_buf_used - written);\n#ifdef KITTY_PRINT_BYTES_SENT_TO_CHILD\n        fprintf(stderr, \"Wrote: %zd bytes: \", ret);\n#endif\n        if (ret > 0) {\n#ifdef KITTY_PRINT_BYTES_SENT_TO_CHILD\n            print_text(screen->write_buf + written, ret);\n#endif\n            written += ret;\n        }\n        else if (ret == 0) {\n            // could mean anything, ignore\n            break;\n        } else {\n            if (errno == EINTR) continue;\n            if (errno == EWOULDBLOCK || errno == EAGAIN) break;\n            perror(\"Call to write() to child fd failed, discarding data.\");\n            written = screen->write_buf_used;\n        }\n#ifdef KITTY_PRINT_BYTES_SENT_TO_CHILD\n        fprintf(stderr, \"\\n\");\n#endif\n    }\n    if (written) {\n        screen->write_buf_used -= written;\n        if (screen->write_buf_used) {\n            memmove(screen->write_buf, screen->write_buf + written, screen->write_buf_used);\n        }\n    }\n    screen_mutex(unlock, write);\n}\n\nstatic void*\nio_loop(void *data) {\n    // The I/O thread loop\n    size_t i;\n    int ret;\n    bool has_more, data_received, has_pending_wakeups = false;\n    monotonic_t last_main_loop_wakeup_at = -1, now = -1;\n    Screen *screen;\n    ChildMonitor *self = (ChildMonitor*)data;\n    set_thread_name(\"KittyChildMon\");\n\n    while (LIKELY(!self->shutting_down)) {\n        children_mutex(lock);\n        remove_children(self);\n        add_children(self);\n        children_mutex(unlock);\n        data_received = false;\n        for (i = 0; i < self->count + EXTRA_FDS; i++) children_fds[i].revents = 0;\n        for (i = 0; i < self->count; i++) {\n            screen = children[i].screen;\n            /* printf(\"i:%lu id:%lu fd: %d read_buf_sz: %lu write_buf_used: %lu\\n\", i, children[i].id, children[i].fd, screen->read_buf_sz, screen->write_buf_used); */\n            children_fds[EXTRA_FDS + i].events = vt_parser_has_space_for_input(screen->vt_parser) ? POLLIN : 0;\n            screen_mutex(lock, write);\n            children_fds[EXTRA_FDS + i].events |= (screen->write_buf_used ? POLLOUT  : 0);\n            screen_mutex(unlock, write);\n        }\n        if (has_pending_wakeups) {\n            now = monotonic();\n            monotonic_t time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at);\n            if (time_delta >= 0) ret = poll(children_fds, self->count + EXTRA_FDS, monotonic_t_to_ms(time_delta));\n            else ret = 0;\n        } else {\n            ret = poll(children_fds, self->count + EXTRA_FDS, -1);\n        }\n        if (ret > 0) {\n            if (children_fds[0].revents && POLLIN) drain_fd(children_fds[0].fd); // wakeup\n            if (children_fds[1].revents && POLLIN) {\n                SignalSet ss = {0};\n                data_received = true;\n                read_signals(children_fds[1].fd, handle_signal, &ss);\n                if (ss.kill_signal || ss.reload_config) {\n                    children_mutex(lock);\n                    if (ss.kill_signal) kill_signal_received = true;\n                    if (ss.reload_config) reload_config_signal_received = true;\n                    children_mutex(unlock);\n                }\n                if (ss.child_died) reap_children(self, OPT(close_on_child_death));\n            }\n            for (i = 0; i < self->count; i++) {\n                if (children_fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) {\n                    data_received = true;\n                    has_more = read_bytes(children_fds[EXTRA_FDS + i].fd, children[i].screen);\n                    if (!has_more) {\n                        // child is dead\n                        children_mutex(lock);\n                        children[i].needs_removal = true;\n                        children_mutex(unlock);\n                    }\n                }\n                if (children_fds[EXTRA_FDS + i].revents & POLLOUT) {\n                    write_to_child(children[i].fd, children[i].screen);\n                }\n                if (children_fds[EXTRA_FDS + i].revents & POLLNVAL) {\n                    // fd was closed\n                    children_mutex(lock);\n                    children[i].needs_removal = true;\n                    children_mutex(unlock);\n                    log_error(\"The child %lu had its fd unexpectedly closed\", children[i].id);\n                }\n            }\n#ifdef DEBUG_POLL_EVENTS\n            for (i = 0; i < self->count + EXTRA_FDS; i++) {\n#define P(w) if (children_fds[i].revents & w) printf(\"i:%lu %s\\n\", i, #w);\n                P(POLLIN); P(POLLPRI); P(POLLOUT); P(POLLERR); P(POLLHUP); P(POLLNVAL);\n#undef P\n            }\n#endif\n        } else if (ret < 0) {\n            if (errno != EAGAIN && errno != EINTR) {\n                perror(\"Call to poll() failed\");\n            }\n        }\n#define WAKEUP { wakeup_main_loop(); last_main_loop_wakeup_at = now; has_pending_wakeups = false; }\n        // we only wakeup the main loop after input_delay as wakeup is an expensive operation\n        // on some platforms, such as cocoa\n        if (data_received) {\n            if ((now = monotonic()) - last_main_loop_wakeup_at > OPT(input_delay)) WAKEUP\n            else has_pending_wakeups = true;\n        } else {\n            if (has_pending_wakeups && (now = monotonic()) - last_main_loop_wakeup_at > OPT(input_delay)) WAKEUP\n        }\n    }\n#undef WAKEUP\n    children_mutex(lock);\n    for (i = 0; i < self->count; i++) children[i].needs_removal = true;\n    remove_children(self);\n    children_mutex(unlock);\n    return 0;\n}\n// }}}\n\n// {{{ Talk thread functions\n\ntypedef struct {\n    id_type id;\n    size_t num_of_unresponded_messages_sent_to_main_thread, fd_array_idx;\n    bool finished_reading, waiting_for_async_response;\n    int fd;\n    struct {\n        char *data;\n        size_t capacity, used, command_end;\n        bool finished;\n    } read;\n    struct {\n        char *data;\n        size_t capacity, used;\n        bool failed;\n    } write;\n    bool is_remote_control_peer;\n} Peer;\nstatic id_type peer_id_counter = 0;\n\ntypedef struct {\n    size_t num_peers, peers_capacity;\n    Peer *peers;\n    LoopData loop_data;\n} TalkData;\nstatic TalkData talk_data = {0};\n\ntypedef struct pollfd PollFD;\n#define PEER_LIMIT 256\n#define nuke_socket(s) { shutdown(s, SHUT_RDWR); safe_close(s, __FILE__, __LINE__); }\n\nstatic id_type\nadd_peer(int peer, bool is_remote_control_peer) {\n    id_type ans = 0;\n    if (talk_data.num_peers < PEER_LIMIT) {\n        ensure_space_for(&talk_data, peers, Peer, talk_data.num_peers + 8, peers_capacity, 8, false);\n        Peer *p = talk_data.peers + talk_data.num_peers++;\n        memset(p, 0, sizeof(Peer));\n        p->fd = peer; p->id = ++peer_id_counter;\n        if (!p->id) p->id = ++peer_id_counter;\n        ans = p->id;\n        p->is_remote_control_peer = is_remote_control_peer;\n    } else {\n        log_error(\"Too many peers want to talk, ignoring one.\");\n        nuke_socket(peer);\n    }\n    return ans;\n}\n\nstatic bool\ngetpeerid(int fd, uid_t *euid, gid_t *egid) {\n#ifdef __linux__\n    struct ucred cr;\n    socklen_t sz = sizeof(cr);\n    if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &sz) != 0) return false;\n    *euid = cr.uid; *egid = cr.gid;\n#else\n    if (getpeereid(fd, euid, egid) != 0) return false;\n#endif\n    return true;\n}\n\n\nstatic bool\naccept_peer(int listen_fd, bool shutting_down, bool is_remote_control_peer) {\n    int peer = accept(listen_fd, NULL, NULL);\n    if (UNLIKELY(peer == -1)) {\n        if (errno == EINTR) return true;\n        if (!shutting_down) perror(\"accept() on talk socket failed!\");\n        return false;\n    }\n    if (verify_peer_uid) {\n        uid_t peer_uid; gid_t peer_gid;\n        if (!getpeerid(peer, &peer_uid, &peer_gid)) {\n            log_error(\"Denying access to peer because failed to get uid and gid for peer: %d with error: %s\", peer, strerror(errno));\n            shutdown(peer, SHUT_RDWR);\n            safe_close(peer, __FILE__, __LINE__);\n            return true;\n        }\n        if (peer_uid != geteuid()) {\n            log_error(\"Denying access to peer because its uid (%d) does not match our uid (%d)\", peer_uid, geteuid());\n            shutdown(peer, SHUT_RDWR);\n            safe_close(peer, __FILE__, __LINE__);\n            return true;\n        }\n    }\n    add_peer(peer, is_remote_control_peer);\n    return true;\n}\n\nstatic void\nfree_peer(Peer *peer) {\n    free(peer->read.data); peer->read.data = NULL;\n    free(peer->write.data); peer->write.data = NULL;\n    if (peer->fd > -1) { nuke_socket(peer->fd); peer->fd = -1; }\n}\n\n#define KITTY_CMD_PREFIX \"\\x1bP@kitty-cmd{\"\n\nstatic void\nqueue_peer_message(ChildMonitor *self, Peer *peer) {\n    talk_mutex(lock);\n    ensure_space_for(self, messages, Message, self->messages_count + 16, messages_capacity, 16, true);\n    Message *m = self->messages + self->messages_count++;\n    memset(m, 0, sizeof(Message));\n    if (peer->read.used) {\n        m->data = malloc(peer->read.used);\n        if (m->data) {\n            memcpy(m->data, peer->read.data, peer->read.used);\n            m->sz = peer->read.used;\n        }\n    }\n    m->peer_id = peer->id;\n    m->is_remote_control_peer = peer->is_remote_control_peer;\n    peer->num_of_unresponded_messages_sent_to_main_thread++;\n    talk_mutex(unlock);\n    wakeup_main_loop();\n}\n\nstatic void\nnotify_on_peer_removal(ChildMonitor *self, const Peer *p) {\n    ensure_space_for(self, messages, Message, self->messages_count + 16, messages_capacity, 16, true);\n    Message *m = self->messages + self->messages_count++;\n    memset(m, 0, sizeof(Message));\n    m->data = strdup(\"peer_death\");\n    if (m->data) m->sz = strlen(\"peer_death\");\n    m->peer_id = p->id;\n    m->is_remote_control_peer = p->id;\n}\n\nstatic bool\nhas_complete_peer_command(Peer *peer) {\n    peer->read.command_end = 0;\n    if (peer->read.used > sizeof(KITTY_CMD_PREFIX) && memcmp(peer->read.data, KITTY_CMD_PREFIX, sizeof(KITTY_CMD_PREFIX)-1) == 0) {\n        for (size_t i = sizeof(KITTY_CMD_PREFIX)-1; i < peer->read.used - 1; i++) {\n            if (peer->read.data[i] == 0x1b && peer->read.data[i+1] == '\\\\') {\n                peer->read.command_end = i + 2;\n                break;\n            }\n        }\n    }\n    return peer->read.command_end ? true : false;\n}\n\n\nstatic void\ndispatch_peer_command(ChildMonitor *self, Peer *peer) {\n    if (peer->read.command_end) {\n        size_t used = peer->read.used;\n        peer->read.used = peer->read.command_end;\n        queue_peer_message(self, peer);\n        peer->read.used = used;\n        if (peer->read.used > peer->read.command_end) {\n            peer->read.used -= peer->read.command_end;\n            memmove(peer->read.data, peer->read.data + peer->read.command_end, peer->read.used);\n        } else peer->read.used = 0;\n        peer->read.command_end = 0;\n    }\n}\n\nstatic void\nread_from_peer(ChildMonitor *self, Peer *peer) {\n#define failed(msg) { log_error(\"Reading from peer failed: %s\", msg); shutdown(peer->fd, SHUT_RD); peer->read.finished = true; return; }\n    if (peer->read.used >= peer->read.capacity) {\n        if (peer->read.capacity >= 64 * 1024) failed(\"Ignoring too large message from peer\");\n        peer->read.capacity = MAX(8192u, peer->read.capacity * 2);\n        peer->read.data = realloc(peer->read.data, peer->read.capacity);\n        if (!peer->read.data) failed(\"Out of memory\");\n    }\n    ssize_t n = recv(peer->fd, peer->read.data + peer->read.used, peer->read.capacity - peer->read.used, 0);\n    if (n == 0) {\n        peer->read.finished = true;\n        shutdown(peer->fd, SHUT_RD);\n        while (has_complete_peer_command(peer)) dispatch_peer_command(self, peer);\n        queue_peer_message(self, peer);\n        free(peer->read.data); peer->read.data = NULL;\n        peer->read.used = 0; peer->read.capacity = 0;\n    } else if (n < 0) {\n        if (errno != EINTR) failed(strerror(errno));\n    } else {\n        peer->read.used += n;\n        while (has_complete_peer_command(peer)) dispatch_peer_command(self, peer);\n    }\n#undef failed\n}\n\nstatic void\nwrite_to_peer(Peer *peer) {\n    talk_mutex(lock);\n    ssize_t n = send(peer->fd, peer->write.data, peer->write.used, MSG_NOSIGNAL);\n    if (n == 0) { log_error(\"send() to peer failed to send any data\"); peer->write.used = 0; peer->write.failed = true; }\n    else if (n < 0) {\n        if (errno != EINTR) { log_error(\"write() to peer socket failed with error: %s\", strerror(errno)); peer->write.used = 0; peer->write.failed = true; }\n    } else {\n        if ((size_t)n > peer->write.used) memmove(peer->write.data, peer->write.data + n, peer->write.used - n);\n        peer->write.used -= n;\n    }\n    talk_mutex(unlock);\n}\n\nstatic void\nwakeup_talk_loop(bool in_signal_handler) {\n    if (talk_thread_started) wakeup_loop(&talk_data.loop_data, in_signal_handler, \"talk_loop\");\n}\n\n\nstatic bool\nprune_peers(ChildMonitor *self) {\n    bool pruned = false;\n    for (size_t idx = talk_data.num_peers; idx-- > 0;) {\n        Peer *p = talk_data.peers + idx;\n        if (p->read.finished && !p->num_of_unresponded_messages_sent_to_main_thread && !p->write.used && !p->waiting_for_async_response) {\n            notify_on_peer_removal(self, p);\n            free_peer(p);\n            remove_i_from_array(talk_data.peers, idx, talk_data.num_peers);\n            pruned = true;\n        }\n    }\n    return pruned;\n}\n\nstatic struct {\n    size_t num;\n    struct { int peer_fd, pipe_fd; } fds[16];\n} peers_to_inject = {0};\n\nstatic bool\nadd_peer_to_injection_queue(int peer_fd, int pipe_fd) {\n    bool added = false;\n    talk_mutex(lock);\n    if (peers_to_inject.num < arraysz(peers_to_inject.fds)) {\n        peers_to_inject.fds[peers_to_inject.num].peer_fd = peer_fd;\n        peers_to_inject.fds[peers_to_inject.num].pipe_fd = pipe_fd;\n        peers_to_inject.num++;\n        added = true;\n    }\n    talk_mutex(unlock);\n    return added;\n}\n\nstatic void\nsimple_write_to_pipe(int fd, void *data, size_t sz) {\n    // write a small amount of data to a pipe handling only EINTR\n    while (true) {\n        ssize_t ret = write(fd, data, sz);\n        if (ret == -1 && errno == EINTR) continue;\n        break;\n    }\n}\n\n\nstatic void*\ntalk_loop(void *data) {\n    // The talk thread loop\n    ChildMonitor *self = (ChildMonitor*)data;\n    set_thread_name(\"KittyPeerMon\");\n    if (!init_loop_data(&talk_data.loop_data, 0)) { log_error(\"Failed to create wakeup fd for talk thread with error: %s\", strerror(errno)); }\n    PollFD fds[PEER_LIMIT + 8] = {{0}};\n    size_t num_listen_fds = 0, num_peer_fds = 0;\n#define add_listener(which) \\\n    if (self->which > -1) { \\\n        fds[num_listen_fds].fd = self->which; fds[num_listen_fds++].events = POLLIN; \\\n    }\n    add_listener(talk_fd); add_listener(listen_fd);\n#undef add_listener\n    fds[num_listen_fds].fd = talk_data.loop_data.wakeup_read_fd; fds[num_listen_fds++].events = POLLIN;\n\n    while (LIKELY(!self->shutting_down)) {\n        num_peer_fds = 0;\n        bool need_to_wakup_main_loop = false;\n        talk_mutex(lock);\n        if (peers_to_inject.num) {\n            for (size_t i = 0; i < peers_to_inject.num; i++) {\n                id_type added_peer_id = add_peer(peers_to_inject.fds[i].peer_fd, true);\n                simple_write_to_pipe(peers_to_inject.fds[i].pipe_fd, &added_peer_id, sizeof(id_type));\n                safe_close(peers_to_inject.fds[i].pipe_fd, __FILE__, __LINE__);\n            }\n            peers_to_inject.num = 0;\n        }\n        if (talk_data.num_peers > 0) {\n            if (prune_peers(self)) need_to_wakup_main_loop = true;\n            for (size_t i = 0; i < talk_data.num_peers; i++) {\n                Peer *p = talk_data.peers + i;\n                if (!p->read.finished || p->write.used) {\n                    p->fd_array_idx = num_listen_fds + num_peer_fds++;\n                    fds[p->fd_array_idx].fd = p->fd;\n                    fds[p->fd_array_idx].revents = 0;\n                    int flags = 0;\n                    if (!p->read.finished) flags |= POLLIN;\n                    if (p->write.used) flags |= POLLOUT;\n                    fds[p->fd_array_idx].events = flags;\n                } else p->fd_array_idx = 0;\n            }\n        }\n        talk_mutex(unlock);\n        if (need_to_wakup_main_loop) wakeup_main_loop();\n        for (size_t i = 0; i < num_listen_fds; i++) fds[i].revents = 0;\n        int ret = poll(fds, num_listen_fds + num_peer_fds, -1);\n        if (ret > 0) {\n            for (size_t i = 0; i < num_listen_fds - 1; i++) {\n                if (fds[i].revents & POLLIN) {\n                    if (!accept_peer(fds[i].fd, self->shutting_down, fds[i].fd == self->listen_fd)) goto end;\n                }\n            }\n            if (fds[num_listen_fds - 1].revents & POLLIN) {\n                drain_fd(fds[num_listen_fds - 1].fd);  // wakeup\n            }\n            for (size_t k = 0; k < talk_data.num_peers; k++) {\n                Peer *p = talk_data.peers + k;\n                if (p->fd_array_idx) {\n                    if (fds[p->fd_array_idx].revents & POLLIN) read_from_peer(self, p);\n                    if (fds[p->fd_array_idx].revents & POLLOUT) write_to_peer(p);\n                    if (fds[p->fd_array_idx].revents & POLLHUP) {\n                        // try to read and write nonetheless these functions will set the failed flags.\n                        if (!p->read.finished) read_from_peer(self, p);\n                        if (p->write.used) write_to_peer(p);\n                    }\n                    if (fds[p->fd_array_idx].revents & POLLNVAL) {\n                        p->read.finished = true;\n                        p->write.failed = true; p->write.used = 0;\n                    }\n                }\n            }\n        } else if (ret < 0) { if (errno != EAGAIN && errno != EINTR) perror(\"poll() on talk fds failed\"); }\n    }\nend:\n    free_loop_data(&talk_data.loop_data);\n    for (size_t i = 0; i < talk_data.num_peers; i++) free_peer(talk_data.peers + i);\n    free(talk_data.peers);\n    return 0;\n}\n\nstatic void\nsend_response_to_peer(id_type peer_id, const char *msg, size_t msg_sz, bool is_async_response) {\n    bool wakeup = false;\n    talk_mutex(lock);\n    for (size_t i = 0; i < talk_data.num_peers; i++) {\n        Peer *peer = talk_data.peers + i;\n        if (peer->id == peer_id) {\n            peer->waiting_for_async_response = is_async_response;\n            if (peer->num_of_unresponded_messages_sent_to_main_thread) peer->num_of_unresponded_messages_sent_to_main_thread--;\n            if (!peer->write.failed) {\n                if (peer->write.capacity - peer->write.used < msg_sz) {\n                    void *data = realloc(peer->write.data, peer->write.capacity + msg_sz);\n                    if (data) {\n                        peer->write.data = data;\n                        peer->write.capacity += msg_sz;\n                    } else fatal(\"Out of memory\");\n                }\n                if (msg_sz && msg) {\n                    memcpy(peer->write.data + peer->write.used, msg, msg_sz);\n                    peer->write.used += msg_sz;\n                }\n            }\n            wakeup = true;\n            break;\n        }\n    }\n    talk_mutex(unlock);\n    if (wakeup) wakeup_talk_loop(false);\n}\n\n// }}}\n\n// Boilerplate {{{\nstatic PyMethodDef methods[] = {\n    METHOD(add_child, METH_VARARGS)\n    METHOD(inject_peer, METH_O)\n    METHOD(needs_write, METH_VARARGS)\n    METHOD(start, METH_NOARGS)\n    METHOD(wakeup, METH_NOARGS)\n    METHOD(shutdown_monitor, METH_NOARGS)\n    METHOD(main_loop, METH_NOARGS)\n    METHOD(mark_for_close, METH_VARARGS)\n    METHOD(resize_pty, METH_VARARGS)\n    METHODB(handled_signals, METH_NOARGS),\n    {\"set_iutf8_winid\", (PyCFunction)pyset_iutf8, METH_VARARGS, \"\"},\n    {NULL}  /* Sentinel */\n};\n\n\nPyTypeObject ChildMonitor_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.ChildMonitor\",\n    .tp_basicsize = sizeof(ChildMonitor),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"ChildMonitor\",\n    .tp_methods = methods,\n    .tp_new = new_childmonitor_object,\n};\n\n\n\nstatic PyObject*\nsafe_pipe(PyObject *self UNUSED, PyObject *args) {\n    int nonblock = 1;\n    if (!PyArg_ParseTuple(args, \"|p\", &nonblock)) return NULL;\n    int fds[2] = {0};\n    if (!self_pipe(fds, nonblock)) return PyErr_SetFromErrno(PyExc_OSError);\n    return Py_BuildValue(\"ii\", fds[0], fds[1]);\n}\n\nstatic PyObject*\ncocoa_set_menubar_title(PyObject *self UNUSED, PyObject *args UNUSED) {\n#ifdef __APPLE__\n    PyObject *title = NULL;\n    if (!PyArg_ParseTuple(args, \"U\", &title)) return NULL;\n    change_menubar_title(title);\n#endif\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nsend_data_to_peer(PyObject *self UNUSED, PyObject *args) {\n    char * msg; Py_ssize_t sz;\n    unsigned long long peer_id;\n    int is_async_response = 0;\n    if (!PyArg_ParseTuple(args, \"Ks#|p\", &peer_id, &msg, &sz, &is_async_response)) return NULL;\n    send_response_to_peer(peer_id, msg, sz, is_async_response);\n    Py_RETURN_NONE;\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(safe_pipe, METH_VARARGS),\n    {\"add_timer\", (PyCFunction)add_python_timer, METH_VARARGS, \"\"},\n    {\"remove_timer\", (PyCFunction)remove_python_timer, METH_VARARGS, \"\"},\n    METHODB(monitor_pid, METH_VARARGS),\n    METHODB(send_data_to_peer, METH_VARARGS),\n    METHODB(cocoa_set_menubar_title, METH_VARARGS),\n    METHODB(mask_kitty_signals_process_wide, METH_NOARGS),\n    {\"sigqueue\", (PyCFunction)sig_queue, METH_VARARGS, \"\"},\n    {NULL}  /* Sentinel */\n};\n\nbool\ninit_child_monitor(PyObject *module) {\n    if (PyType_Ready(&ChildMonitor_Type) < 0) return false;\n    if (PyModule_AddObject(module, \"ChildMonitor\", (PyObject *)&ChildMonitor_Type) != 0) return false;\n    Py_INCREF(&ChildMonitor_Type);\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n#ifdef NO_SIGQUEUE\n    PyModule_AddIntConstant(module, \"has_sigqueue\", 0);\n#else\n    PyModule_AddIntConstant(module, \"has_sigqueue\", 1);\n#endif\n    return true;\n}\n\n// }}}\n"
  },
  {
    "path": "kitty/child.c",
    "content": "/*\n * child.c\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"safe-wrappers.h\"\n#include <unistd.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <string.h>\n#include <signal.h>\n#include <sys/ioctl.h>\n#include <termios.h>\n\n#define EXTRA_ENV_BUFFER_SIZE 64\n\nstatic char**\nserialize_string_tuple(PyObject *src, Py_ssize_t extra) {\n    const Py_ssize_t sz = PyTuple_GET_SIZE(src);\n    size_t required_size = sizeof(char*) * (1 + sz + extra);\n    required_size += extra * EXTRA_ENV_BUFFER_SIZE;\n    void *block = calloc(required_size, 1);\n    if (!block) { PyErr_NoMemory(); return NULL; }\n    char **ans = block;\n    for (Py_ssize_t i = 0; i < sz; i++) {\n        PyObject *x = PyTuple_GET_ITEM(src, i);\n        if (!PyUnicode_Check(x)) { free(block); PyErr_SetString(PyExc_TypeError, \"string tuple must have only strings\"); return NULL; }\n        ans[i] = (char*)PyUnicode_AsUTF8(x);\n        if (!ans[i]) { free(block); return NULL; }\n    }\n    return ans;\n}\n\nstatic void\nwrite_to_stderr(const char *text) {\n    size_t sz = strlen(text);\n    size_t written = 0;\n    while(written < sz) {\n        ssize_t amt = write(2, text + written, sz - written);\n        if (amt == 0) break;\n        if (amt < 0) {\n            if (errno == EAGAIN || errno == EINTR) continue;\n            break;\n        }\n        written += amt;\n    }\n}\n\n#define exit_on_err(m) { write_to_stderr(m); write_to_stderr(\": \"); write_to_stderr(strerror(errno)); exit(EXIT_FAILURE); }\n\nstatic void\nwait_for_terminal_ready(int fd) {\n    char data;\n    while(1) {\n        int ret = read(fd, &data, 1);\n        if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue;\n        break;\n    }\n}\n\nstatic PyObject*\nspawn(PyObject *self UNUSED, PyObject *args) {\n    PyObject *argv_p, *env_p, *handled_signals_p, *pass_fds;\n    int master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd, forward_stdio;\n    const char *kitten_exe;\n    char *cwd, *exe;\n    if (!PyArg_ParseTuple(args, \"ssO!O!iiiiiiO!spO!\", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd, &ready_read_fd, &ready_write_fd, &PyTuple_Type, &handled_signals_p, &kitten_exe, &forward_stdio, &PyTuple_Type, &pass_fds)) return NULL;\n    char name[2048] = {0};\n    if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }\n    char **argv = serialize_string_tuple(argv_p, 0);\n    if (!argv) return NULL;\n    char **env = serialize_string_tuple(env_p, 1);\n    if (!env) { free(argv); return NULL; }\n    int handled_signals[16] = {0}, num_handled_signals = MIN((int)arraysz(handled_signals), PyTuple_GET_SIZE(handled_signals_p));\n    for (Py_ssize_t i = 0; i < num_handled_signals; i++) handled_signals[i] = PyLong_AsLong(PyTuple_GET_ITEM(handled_signals_p, i));\n\n#if PY_VERSION_HEX >= 0x03070000\n    PyOS_BeforeFork();\n#endif\n    pid_t pid = fork();\n    switch(pid) {\n        case 0: {\n            // child\n#if PY_VERSION_HEX >= 0x03070000\n            PyOS_AfterFork_Child();\n#endif\n            const struct sigaction act = {.sa_handler=SIG_DFL};\n\n#define SA(which)  if (sigaction(which, &act, NULL) != 0) exit_on_err(\"sigaction() in child process failed\");\n            for (int si = 0; si < num_handled_signals; si++) { SA(handled_signals[si]); }\n            // See _Py_RestoreSignals in signalmodule.c for a list of signals python nukes\n#ifdef SIGPIPE\n            SA(SIGPIPE)\n#endif\n#ifdef SIGXFSZ\n            SA(SIGXFSZ);\n#endif\n#ifdef SIGXFZ\n            SA(SIGXFZ);\n#endif\n#undef SA\n            sigset_t signals; sigemptyset(&signals);\n            if (sigprocmask(SIG_SETMASK, &signals, NULL) != 0) exit_on_err(\"sigprocmask() in child process failed\");\n            // Use only signal-safe functions (man 7 signal-safety)\n            if (chdir(cwd) != 0) {\n                if (access(\".\", X_OK) != 0) { // existing cwd does not exist or dont have permissions for it\n                    if (chdir(\"/\") != 0) {} // ignore failure to chdir to /\n                }\n            };\n            if (setsid() == -1) exit_on_err(\"setsid() in child process failed\");\n\n            // Establish the controlling terminal (see man 7 credentials)\n            int tfd = safe_open(name, O_RDWR | O_CLOEXEC, 0);\n            if (tfd == -1) exit_on_err(\"Failed to open controlling terminal\");\n            // On BSD open() does not establish the controlling terminal\n            if (ioctl(tfd, TIOCSCTTY, 0) == -1) exit_on_err(\"Failed to set controlling terminal with TIOCSCTTY\");\n            safe_close(tfd, __FILE__, __LINE__);\n\n            fd_set passed_fds; FD_ZERO(&passed_fds); bool has_preserved_fds = false;\n            if (forward_stdio) {\n                int fd = safe_dup(STDOUT_FILENO);\n                if (fd < 0) exit_on_err(\"dup() failed for forwarded STDOUT\");\n                FD_SET(fd, &passed_fds);\n                size_t s = PyTuple_GET_SIZE(env_p);\n                env[s] = (char*)(env + (s + 2));\n                snprintf(env[s], EXTRA_ENV_BUFFER_SIZE, \"KITTY_STDIO_FORWARDED=%d\", fd);\n                fd = safe_dup(STDERR_FILENO);\n                if (fd < 0) exit_on_err(\"dup() failed for forwarded STDERR\");\n                FD_SET(fd, &passed_fds);\n                has_preserved_fds = true;\n            }\n\n            for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(pass_fds); i++) {\n                PyObject *pfd = PyTuple_GET_ITEM(pass_fds, i);\n                if (!PyLong_Check(pfd)) exit_on_err(\"pass_fds must contain only integers\");\n                int fd = PyLong_AsLong(pfd);\n                if (fd > -1 && fd < FD_SETSIZE) {\n                    FD_SET(fd, &passed_fds);\n                    has_preserved_fds = true;\n                }\n            }\n            // Redirect stdin/stdout/stderr to the pty\n            if (safe_dup2(slave, STDOUT_FILENO) == -1) exit_on_err(\"dup2() failed for fd number 1\");\n            if (safe_dup2(slave, STDERR_FILENO) == -1) exit_on_err(\"dup2() failed for fd number 2\");\n            if (stdin_read_fd > -1) {\n                if (safe_dup2(stdin_read_fd, STDIN_FILENO) == -1) exit_on_err(\"dup2() failed for fd number 0\");\n                safe_close(stdin_read_fd, __FILE__, __LINE__);\n                safe_close(stdin_write_fd, __FILE__, __LINE__);\n            } else {\n                if (safe_dup2(slave, STDIN_FILENO) == -1) exit_on_err(\"dup2() failed for fd number 0\");\n            }\n            safe_close(slave, __FILE__, __LINE__);\n            safe_close(master, __FILE__, __LINE__);\n\n            // Wait for READY_SIGNAL which indicates kitty has setup the screen object\n            safe_close(ready_write_fd, __FILE__, __LINE__);\n            wait_for_terminal_ready(ready_read_fd);\n            safe_close(ready_read_fd, __FILE__, __LINE__);\n\n            // Close any extra fds inherited from parent\n            if (has_preserved_fds) { for (int c = 3; c < 256; c++) { if (!FD_ISSET(c, &passed_fds)) safe_close(c, __FILE__, __LINE__); } }\n            else for (int c = 3; c < 256; c++) { safe_close(c, __FILE__, __LINE__); }\n\n            extern char **environ;\n            environ = env;\n            execvp(exe, argv);\n            // Report the failure and exec kitten instead, so that we are not left\n            // with a forked but not exec'ed process\n            write_to_stderr(\"Failed to launch child: \");\n            write_to_stderr(exe);\n            write_to_stderr(\"\\nWith error: \");\n            write_to_stderr(strerror(errno));\n            write_to_stderr(\"\\n\");\n            execlp(kitten_exe, \"kitten\", \"__hold_till_enter__\", NULL);\n            exit(EXIT_FAILURE);\n            break;\n        }\n        case -1: {\n#if PY_VERSION_HEX >= 0x03070000\n            int saved_errno = errno;\n            PyOS_AfterFork_Parent();\n            errno = saved_errno;\n#endif\n            PyErr_SetFromErrno(PyExc_OSError);\n            break;\n        }\n        default:\n#if PY_VERSION_HEX >= 0x03070000\n            PyOS_AfterFork_Parent();\n#endif\n            break;\n    }\n#undef exit_on_err\n    free(argv);\n    free(env);\n    if (PyErr_Occurred()) return NULL;\n    return PyLong_FromLong(pid);\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(spawn, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\n\nbool\ninit_child(PyObject *module) {\n    PyModule_AddIntMacro(module, CLD_KILLED);\n    PyModule_AddIntMacro(module, CLD_STOPPED);\n    PyModule_AddIntMacro(module, CLD_EXITED);\n    PyModule_AddIntMacro(module, CLD_CONTINUED);\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    return true;\n}\n"
  },
  {
    "path": "kitty/child.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport sys\nimport termios\nfrom collections import defaultdict\nfrom collections.abc import Generator, Sequence\nfrom contextlib import contextmanager, suppress\nfrom itertools import count\nfrom typing import TYPE_CHECKING, DefaultDict, Iterable, Mapping, Optional, TypedDict\n\nimport kitty.fast_data_types as fast_data_types\n\nfrom .constants import handled_signals, is_freebsd, is_macos, kitten_exe, kitty_base_dir, shell_path, terminfo_dir\nfrom .types import run_once\nfrom .utils import cmdline_for_hold, log_error, resolved_shell, which\n\nif TYPE_CHECKING:\n    from .window import CwdRequest\n\n\nif is_macos:\n    from kitty.fast_data_types import abspath_of_process as _abspath_of_process\n    from kitty.fast_data_types import cmdline_of_process as cmdline_\n    from kitty.fast_data_types import cwd_of_process as _cwd\n    from kitty.fast_data_types import environ_of_process as _environ_of_process\n    from kitty.fast_data_types import process_group_map as _process_group_map\n\n    def cwd_of_process(pid: int) -> str:\n        # The underlying code on macos returns a path with symlinks resolved\n        # anyway but we use realpath for extra safety.\n        return os.path.realpath(_cwd(pid), strict=True)\n\n    def abspath_of_exe(pid: int) -> str:\n        return os.path.realpath(_abspath_of_process(pid), strict=True)\n\n    def process_group_map() -> DefaultDict[int, list[int]]:\n        ans: DefaultDict[int, list[int]] = defaultdict(list)\n        for pid, pgid in _process_group_map():\n            ans[pgid].append(pid)\n        return ans\n\n    def cmdline_of_pid(pid: int) -> list[str]:\n        return cmdline_(pid)\nelse:\n\n    def cmdline_of_pid(pid: int) -> list[str]:\n        with open(f'/proc/{pid}/cmdline', 'rb') as f:\n            return list(filter(None, f.read().decode('utf-8').split('\\0')))\n\n    if is_freebsd:\n        def cwd_of_process(pid: int) -> str:\n            import subprocess\n            cp = subprocess.run(['pwdx', str(pid)], capture_output=True)\n            if cp.returncode != 0:\n                raise ValueError(f'Failed to find cwd of process with pid: {pid}')\n            ans = cp.stdout.decode('utf-8', 'replace').split()[1]\n            return os.path.realpath(ans, strict=True)\n    else:\n        def cwd_of_process(pid: int) -> str:\n            # We use realpath instead of readlink to match macOS behavior where\n            # the underlying OS API returns real paths.\n            ans = f'/proc/{pid}/cwd'\n            return os.path.realpath(ans, strict=True)\n\n    def _environ_of_process(pid: int) -> str:\n        with open(f'/proc/{pid}/environ', 'rb') as f:\n            return f.read().decode('utf-8')\n\n    def process_group_map() -> DefaultDict[int, list[int]]:\n        ans: DefaultDict[int, list[int]] = defaultdict(list)\n        for x in os.listdir('/proc'):\n            try:\n                pid = int(x)\n            except Exception:\n                continue\n            try:\n                with open(f'/proc/{x}/stat', 'rb') as f:\n                    raw = f.read().decode('utf-8')\n            except OSError:\n                continue\n            try:\n                q = int(raw.split(' ', 5)[4])\n            except Exception:\n                continue\n            ans[q].append(pid)\n        return ans\n\n    def abspath_of_exe(pid: int) -> str:\n        return os.path.realpath(f'/proc/{pid}/exe', strict=True)\n\n\n@run_once\ndef checked_terminfo_dir() -> str | None:\n    return terminfo_dir if os.path.isdir(terminfo_dir) else None\n\n\ndef processes_in_group(grp: int) -> list[int]:\n    gmap: DefaultDict[int, list[int]] | None = getattr(process_group_map, 'cached_map', None)\n    if gmap is None:\n        try:\n            gmap = process_group_map()\n        except Exception:\n            gmap = defaultdict(list)\n    return gmap.get(grp, [])\n\n\n@contextmanager\ndef cached_process_data() -> Generator[None, None, None]:\n    try:\n        cm = process_group_map()\n    except Exception:\n        cm = defaultdict(list)\n    setattr(process_group_map, 'cached_map', cm)\n    try:\n        yield\n    finally:\n        delattr(process_group_map, 'cached_map')\n\n\ndef session_id(pids: Iterable[int]) -> int:\n    for pid in pids:\n        with suppress(OSError):\n            if (sid := os.getsid(pid)) > -1:\n                return sid\n    return -1\n\ndef parse_environ_block(data: str) -> dict[str, str]:\n    \"\"\"Parse a C environ block of environment variables into a dictionary.\"\"\"\n    # The block is usually raw data from the target process.  It might contain\n    # trailing garbage and lines that do not look like assignments.\n    ret: dict[str, str] = {}\n    pos = 0\n\n    while True:\n        next_pos = data.find(\"\\0\", pos)\n        # nul byte at the beginning or double nul byte means finish\n        if next_pos <= pos:\n            break\n        # there might not be an equals sign\n        equal_pos = data.find(\"=\", pos, next_pos)\n        if equal_pos > pos:\n            key = data[pos:equal_pos]\n            value = data[equal_pos + 1:next_pos]\n            ret[key] = value\n        pos = next_pos + 1\n\n    return ret\n\n\ndef environ_of_process(pid: int) -> dict[str, str]:\n    return parse_environ_block(_environ_of_process(pid))\n\n\ndef process_env(env: Mapping[str, str] | None = None) -> dict[str, str]:\n    ans = dict(os.environ if env is None else env)\n    ssl_env_var = getattr(sys, 'kitty_ssl_env_var', None)\n    if ssl_env_var is not None:\n        ans.pop(ssl_env_var, None)\n    ans.pop('XDG_ACTIVATION_TOKEN', None)\n    ans.pop('VTE_VERSION', None)  # Used by the stupid VTE shell integration script that is installed system wide, sigh\n    ans.pop('KITTY_SI_RUN_COMMAND_AT_STARTUP', None)\n    return ans\n\n\ndef default_env() -> dict[str, str]:\n    ans: dict[str, str] | None = getattr(default_env, 'env', None)\n    if ans is None:\n        return process_env()\n    return ans\n\n\ndef set_default_env(val: dict[str, str] | None = None) -> None:\n    env = process_env().copy()\n    has_lctype = False\n    if val:\n        has_lctype = 'LC_CTYPE' in val\n        env.update(val)\n    setattr(default_env, 'env', env)\n    setattr(default_env, 'lc_ctype_set_by_user', has_lctype)\n\n\ndef set_LANG_in_default_env(val: str) -> None:\n    default_env().setdefault('LANG', val)\n\n\ndef openpty() -> tuple[int, int]:\n    master, slave = os.openpty()  # Note that master and slave are in blocking mode\n    os.set_inheritable(slave, True)\n    os.set_inheritable(master, False)\n    fast_data_types.set_iutf8_fd(master, True)\n    return master, slave\n\n\n@run_once\ndef getpid() -> str:\n    return str(os.getpid())\n\n\n@run_once\ndef base64_terminfo_data() -> str:\n    return (b'b64:' + fast_data_types.base64_encode(fast_data_types.terminfo_data(), True)).decode('ascii')\n\n\nclass ProcessDesc(TypedDict):\n    cwd: str | None\n    pid: int\n    cmdline: Sequence[str] | None\n\n\nchild_counter = count()\n\n\nclass Child:\n\n    child_fd: int | None = None\n    pid: int | None = None\n    forked = False\n\n    def __init__(\n        self,\n        argv: Sequence[str],\n        cwd: str,\n        stdin: bytes | None = None,\n        env: dict[str, str] | None = None,\n        cwd_from: Optional['CwdRequest'] = None,\n        is_clone_launch: str = '',\n        add_listen_on_env_var: bool = True,\n        hold: bool = False,\n        pass_fds: tuple[int, ...] = (),\n        remote_control_fd: int = -1,\n        hold_after_ssh: bool = False,\n        startup_command_via_shell_integration: Sequence[str] | str = (),\n    ):\n        self.is_clone_launch = is_clone_launch\n        self.id = next(child_counter)\n        self.add_listen_on_env_var = add_listen_on_env_var\n        self.argv = list(argv)\n        self.pass_fds = pass_fds\n        self.remote_control_fd = remote_control_fd\n        if cwd_from:\n            try:\n                cwd = cwd_from.modify_argv_for_launch_with_cwd(self.argv, env, hold_after_ssh=hold_after_ssh) or cwd\n            except Exception as err:\n                log_error(f'Failed to read cwd of {cwd_from} with error: {err}')\n        else:\n            cwd = os.path.expandvars(os.path.expanduser(cwd or os.getcwd()))\n        self.cwd = os.path.abspath(cwd)\n        self.stdin = stdin\n        self.env = env or {}\n        self.startup_command_via_shell_integration = startup_command_via_shell_integration\n        self.final_env:dict[str, str] = {}\n        self.is_default_shell = bool(self.argv and self.argv[0] == shell_path)\n        self.should_run_via_run_shell_kitten = is_macos and self.is_default_shell\n        self.hold = hold\n\n    def get_final_env(self) -> tuple[dict[str, str], bool]:\n        from kitty.options.utils import DELETE_ENV_VAR\n        env = default_env().copy()\n        opts = fast_data_types.get_options()\n        boss = fast_data_types.get_boss()\n        if is_macos and env.get('LC_CTYPE') == 'UTF-8' and not getattr(sys, 'kitty_run_data').get(\n                'lc_ctype_before_python') and not getattr(default_env, 'lc_ctype_set_by_user', False):\n            del env['LC_CTYPE']\n        env.update(self.env)\n        env['TERM'] = opts.term\n        env['COLORTERM'] = 'truecolor'\n        env['KITTY_PID'] = getpid()\n        env['KITTY_PUBLIC_KEY'] = boss.encryption_public_key\n        if self.remote_control_fd > -1:\n            env['KITTY_LISTEN_ON'] = f'fd:{self.remote_control_fd}'\n        elif self.add_listen_on_env_var and boss.listening_on:\n            env['KITTY_LISTEN_ON'] = boss.listening_on\n        else:\n            env.pop('KITTY_LISTEN_ON', None)\n        env.pop('KITTY_STDIO_FORWARDED', None)\n        if self.cwd:\n            # needed in case cwd is a symlink, in which case shells\n            # can use it to display the current directory name rather\n            # than the resolved path\n            env['PWD'] = self.cwd\n        if opts.terminfo_type == 'path':\n            tdir = checked_terminfo_dir()\n            if tdir:\n                env['TERMINFO'] = tdir\n        elif opts.terminfo_type == 'direct':\n            env['TERMINFO'] = base64_terminfo_data()\n        env['KITTY_INSTALLATION_DIR'] = kitty_base_dir\n        self.unmodified_argv = list(self.argv)\n        if not self.should_run_via_run_shell_kitten and 'disabled' not in opts.shell_integration:\n            from .shell_integration import modify_shell_environ\n            modify_shell_environ(opts, env, self.argv)\n        env = {k: v for k, v in env.items() if v is not DELETE_ENV_VAR}\n        if self.is_clone_launch:\n            env['KITTY_IS_CLONE_LAUNCH'] = self.is_clone_launch\n            self.is_clone_launch = '1'  # free memory\n        else:\n            env.pop('KITTY_IS_CLONE_LAUNCH', None)\n        must_run_startup_command_via_kitten = False\n        if self.startup_command_via_shell_integration:\n            if isinstance(self.startup_command_via_shell_integration, str):\n                env['KITTY_SI_RUN_COMMAND_AT_STARTUP'] = self.startup_command_via_shell_integration\n            else:\n                from .shell_integration import join\n                scmd = self.argv or resolved_shell(fast_data_types.get_options())\n                try:\n                    env['KITTY_SI_RUN_COMMAND_AT_STARTUP'] = join(scmd[0], self.startup_command_via_shell_integration)\n                except Exception:\n                    must_run_startup_command_via_kitten = True  # unknown shell\n        return env, must_run_startup_command_via_kitten\n\n    def fork(self) -> int | None:\n        if self.forked:\n            return None\n        opts = fast_data_types.get_options()\n        self.forked = True\n        master, slave = openpty()\n        stdin, self.stdin = self.stdin, None\n        ready_read_fd, ready_write_fd = os.pipe()\n        os.set_inheritable(ready_write_fd, False)\n        os.set_inheritable(ready_read_fd, True)\n        if stdin is not None:\n            stdin_read_fd, stdin_write_fd = os.pipe()\n            os.set_inheritable(stdin_write_fd, False)\n            os.set_inheritable(stdin_read_fd, True)\n        else:\n            stdin_read_fd = stdin_write_fd = -1\n        self.final_env, must_run_startup_command_via_kitten = self.get_final_env()\n        self.initial_termios_state = termios.tcgetattr(master)\n        argv = list(self.argv)\n        cwd = self.cwd\n        pass_fds = self.pass_fds\n        if self.remote_control_fd > -1:\n            pass_fds += self.remote_control_fd,\n        if self.should_run_via_run_shell_kitten or must_run_startup_command_via_kitten:\n            # bash will only source ~/.bash_profile if it detects it is a login\n            # shell (see the invocation section of the bash man page), which it\n            # does if argv[0] is prefixed by a hyphen see\n            # https://github.com/kovidgoyal/kitty/issues/247\n            # it is apparently common to use ~/.bash_profile instead of the\n            # more correct ~/.bashrc on macOS to setup env vars, so if\n            # the default shell is used prefix argv[0] by '-'\n            #\n            # it is arguable whether graphical terminals should start shells\n            # in login mode in general, there are at least a few Linux users\n            # that also make this incorrect assumption, see for example\n            # https://github.com/kovidgoyal/kitty/issues/1870\n            # xterm, urxvt, konsole and gnome-terminal do not do it in my\n            # testing.\n            import shlex\n            ksi = ' '.join(opts.shell_integration)\n            if ksi == 'invalid':\n                ksi = 'enabled'\n            argv = [kitten_exe(), 'run-shell', '--shell', shlex.join(argv), '--shell-integration', ksi]\n            if must_run_startup_command_via_kitten:\n                argv.extend(self.startup_command_via_shell_integration)\n            if is_macos and not pass_fds and not opts.forward_stdio:\n                # In addition for getlogin() to work we need to run the shell\n                # via the /usr/bin/login wrapper, sigh.\n                # And login on macOS looks for .hushlogin in CWD instead of\n                # HOME, bloody idiotic so we cant cwd when running it.\n                # https://github.com/kovidgoyal/kitty/issues/6511\n                # login closes inherited file descriptors so dont use it when\n                # forward_stdio or pass_fds are used.\n                import pwd\n                user = pwd.getpwuid(os.geteuid()).pw_name\n                if cwd:\n                    argv.append('--cwd=' + cwd)\n                    cwd = os.path.expanduser('~')\n                argv = ['/usr/bin/login', '-f', '-l', '-p', user] + argv\n        self.final_exe = final_exe = which(argv[0]) or argv[0]\n        self.final_argv0 = argv[0]\n        if self.hold:\n            argv = cmdline_for_hold(argv)\n            final_exe = argv[0]\n        env = tuple(f'{k}={v}' for k, v in self.final_env.items())\n        pid = fast_data_types.spawn(\n            final_exe, cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd,\n            ready_read_fd, ready_write_fd, tuple(handled_signals), kitten_exe(), opts.forward_stdio, pass_fds)\n        os.close(slave)\n        self.pid = pid\n        self.child_fd = master\n        if stdin is not None:\n            os.close(stdin_read_fd)\n            fast_data_types.thread_write(stdin_write_fd, stdin)\n        os.close(ready_read_fd)\n        self.terminal_ready_fd = ready_write_fd\n        if self.child_fd is not None:\n            os.set_blocking(self.child_fd, False)\n        if not is_macos:\n            ppid = getpid()\n            try:\n                fast_data_types.systemd_move_pid_into_new_scope(pid, f'kitty-{ppid}-{self.id}.scope', f'kitty child process: {pid} launched by: {ppid}')\n            except NotImplementedError:\n                pass\n            except OSError as err:\n                log_error(\"Could not move child process into a systemd scope: \" + str(err))\n        return pid\n\n    def __del__(self) -> None:\n        fd = getattr(self, 'terminal_ready_fd', -1)\n        if fd > -1:\n            os.close(fd)\n        self.terminal_ready_fd = -1\n\n    def mark_terminal_ready(self) -> None:\n        os.close(self.terminal_ready_fd)\n        self.terminal_ready_fd = -1\n\n    def cmdline_of_pid(self, pid: int) -> list[str]:\n        try:\n            ans = cmdline_of_pid(pid)\n        except Exception:\n            ans = []\n        if pid == self.pid and (not ans):\n            ans = list(self.argv)\n        return ans\n\n    def process_desc(self, pid: int) -> ProcessDesc:\n        ans: ProcessDesc = {'pid': pid, 'cmdline': None, 'cwd': None}\n        with suppress(Exception):\n            ans['cmdline'] = self.cmdline_of_pid(pid)\n        with suppress(Exception):\n            ans['cwd'] = cwd_of_process(pid) or None\n        return ans\n\n    @property\n    def foreground_processes(self) -> list[ProcessDesc]:\n        if self.child_fd is None:\n            return []\n        try:\n            pgrp = os.tcgetpgrp(self.child_fd)\n            foreground_processes = processes_in_group(pgrp) if pgrp >= 0 else []\n            return [self.process_desc(x) for x in foreground_processes]\n        except Exception:\n            return []\n\n    @property\n    def background_processes(self) -> list[ProcessDesc]:\n        if self.child_fd is None:\n            return []\n        try:\n            foreground_process_group_id = os.tcgetpgrp(self.child_fd)\n            if foreground_process_group_id < 0:\n                return []\n            gmap = process_group_map()\n\n            sid = session_id(gmap.get(foreground_process_group_id, ()))\n            if sid < 0:\n                return []\n            ans: list[ProcessDesc] = []\n            for grp_id, pids in gmap.items():\n                if grp_id != foreground_process_group_id and session_id(pids) == sid:\n                    ans.extend(map(self.process_desc, pids))\n            return ans\n        except Exception:\n            return []\n\n    @property\n    def cmdline(self) -> list[str]:\n        try:\n            assert self.pid is not None\n            return self.cmdline_of_pid(self.pid) or list(self.argv)\n        except Exception:\n            return list(self.argv)\n\n    @property\n    def foreground_cmdline(self) -> list[str]:\n        try:\n            assert self.pid_for_cwd is not None\n            return self.cmdline_of_pid(self.pid_for_cwd) or self.cmdline\n        except Exception:\n            return self.cmdline\n\n    @property\n    def environ(self) -> dict[str, str]:\n        try:\n            assert self.pid is not None\n            return environ_of_process(self.pid) or self.final_env.copy()\n        except Exception:\n            return self.final_env.copy()\n\n    @property\n    def current_cwd(self) -> str | None:\n        with suppress(Exception):\n            assert self.pid is not None\n            return cwd_of_process(self.pid)\n        return None\n\n    def get_pid_for_cwd(self, oldest: bool = False) -> int | None:\n        with suppress(Exception):\n            assert self.child_fd is not None\n            pgrp = os.tcgetpgrp(self.child_fd)\n            foreground_processes = processes_in_group(pgrp) if pgrp >= 0 else []\n            if foreground_processes:\n                # there is no easy way that I know of to know which process is the\n                # foreground process in this group from the users perspective,\n                # so we assume the one with the highest PID is as that is most\n                # likely to be the newest process. This situation can happen\n                # for example with a shell script such as:\n                # #!/bin/bash\n                # cd /tmp\n                # vim\n                # With this script , the foreground process group will contain\n                # both the bash instance running the script and vim.\n                return min(foreground_processes) if oldest else max(foreground_processes)\n        return self.pid\n\n    @property\n    def pid_for_cwd(self) -> int | None:\n        return self.get_pid_for_cwd()\n\n    def get_foreground_cwd(self, oldest: bool = False) -> str | None:\n        with suppress(Exception):\n            pid = self.get_pid_for_cwd(oldest)\n            if pid is not None:\n                return cwd_of_process(pid) or None\n        return None\n\n    def get_foreground_exe(self, oldest: bool = False) -> str | None:\n        with suppress(Exception):\n            pid = self.get_pid_for_cwd(oldest)\n            if pid is not None:\n                c = cmdline_of_pid(pid)\n                if c:\n                    return c[0]\n        return None\n\n    @property\n    def foreground_cwd(self) -> str | None:\n        return self.get_foreground_cwd()\n\n    @property\n    def foreground_environ(self) -> dict[str, str]:\n        pid = self.pid_for_cwd\n        if pid is not None:\n            with suppress(Exception):\n                return environ_of_process(pid)\n        pid = self.pid\n        if pid is not None:\n            with suppress(Exception):\n                return environ_of_process(pid)\n        return {}\n\n    def send_signal_for_key(self, key_num: bytes) -> bool:\n        import signal\n        if self.child_fd is None:\n            return False\n        t = termios.tcgetattr(self.child_fd)\n        if not t[3] & termios.ISIG:\n            return False\n        cc = t[-1]\n        if key_num == cc[termios.VINTR]:\n            s: signal.Signals = signal.SIGINT\n        elif key_num == cc[termios.VSUSP]:\n            s = signal.SIGTSTP\n        elif key_num == cc[termios.VQUIT]:\n            s = signal.SIGQUIT\n        else:\n            return False\n        pgrp = os.tcgetpgrp(self.child_fd)\n        os.killpg(pgrp, s)\n        return True\n\n    def reset_termios_state(self, when: int = termios.TCSANOW) -> None:\n        if (s := getattr(self, 'initial_termios_state', None)) and self.child_fd is not None:\n            try:\n                termios.tcsetattr(self.child_fd, when, s)\n            except OSError:\n                pass\n"
  },
  {
    "path": "kitty/choose_entry.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport re\nfrom collections.abc import Generator\nfrom typing import Any\n\nfrom .cli_stub import HintsCLIOptions\nfrom .typing_compat import MarkType\n\n\ndef mark(text: str, args: HintsCLIOptions, Mark: type[MarkType], extra_cli_args: list[str], *a: Any) -> Generator[MarkType, None, None]:\n    idx = 0\n    found_start_line = False\n    for m in re.finditer(r'(?m)^.+$', text):\n        start, end = m.span()\n        line = text[start:end].replace('\\0', '').replace('\\n', '')\n        if line == ' ':\n            found_start_line = True\n            continue\n        if line.startswith(': '):\n            yield Mark(idx, start, end, line, {'index': idx})\n            idx += 1\n        elif found_start_line:\n            # skip this line incrementing the index\n            idx += 1\n"
  },
  {
    "path": "kitty/cleanup.c",
    "content": "/*\n * cleanup.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"cleanup.h\"\n#include <unistd.h>\n\n\nkitty_cleanup_at_exit_func exit_funcs[NUM_CLEANUP_FUNCS] = {0};\n\nvoid\nregister_at_exit_cleanup_func(AtExitCleanupFunc which, kitty_cleanup_at_exit_func func) {\n    if (which < NUM_CLEANUP_FUNCS) exit_funcs[which] = func;\n}\n\nvoid\nrun_at_exit_cleanup_functions(void) {\n    for (unsigned i = 0; i < NUM_CLEANUP_FUNCS; i++) {\n        if (exit_funcs[i]) exit_funcs[i]();\n        exit_funcs[i] = NULL;\n    }\n}\n"
  },
  {
    "path": "kitty/cleanup.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\ntypedef void (*kitty_cleanup_at_exit_func)(void);\n\ntypedef enum {\n    STATE_CLEANUP_FUNC,\n    GLFW_CLEANUP_FUNC,\n    DESKTOP_CLEANUP_FUNC,\n    CORE_TEXT_CLEANUP_FUNC,\n    COCOA_CLEANUP_FUNC,\n    PNG_READER_CLEANUP_FUNC,\n    FONTCONFIG_CLEANUP_FUNC,\n    FREETYPE_CLEANUP_FUNC,\n    SYSTEMD_CLEANUP_FUNC,\n    SHADERS_CLEANUP_FUNC,\n\n    NUM_CLEANUP_FUNCS\n} AtExitCleanupFunc;\n\nvoid register_at_exit_cleanup_func(AtExitCleanupFunc which, kitty_cleanup_at_exit_func func);\nvoid run_at_exit_cleanup_functions(void);\n"
  },
  {
    "path": "kitty/cli.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nimport sys\nfrom collections.abc import Callable, Iterator, Sequence\nfrom re import Match\nfrom typing import Any, NoReturn, TypeVar, cast\n\nfrom .cli_stub import CLIOptions\nfrom .conf.utils import resolve_config\nfrom .constants import appname, clear_handled_signals, config_dir, default_pager_for_help, defconf, is_macos, str_version, website_url\nfrom .fast_data_types import parse_cli_from_spec, wcswidth\nfrom .options.types import Options as KittyOpts\nfrom .simple_cli_definitions import (\n    CompletionType,\n    OptionDefinition,\n    OptionSpecSeq,\n    defval_for_opt,\n    get_option_maps,\n    kitty_options_spec,\n    parse_option_spec,\n    serialize_as_go_string,\n)\nfrom .types import run_once\nfrom .typing_compat import BadLineType\n\nis_macos\ngo_type_map = {\n    'bool-set': 'bool', 'bool-reset': 'bool', 'bool-unset': 'bool', 'int': 'int', 'float': 'float64',\n    '': 'string', 'list': '[]string', 'choices': 'string', 'str': 'string'}\n\n\nclass GoOption:\n\n    def __init__(self, x: OptionDefinition) -> None:\n        flags = sorted(x.aliases, key=len)\n        short = ''\n        self.aliases = []\n        if len(flags) > 1 and not flags[0].startswith(\"--\"):\n            short = flags[0][1:]\n        self.short, self.long = short, x.name.replace('_', '-')\n        for f in flags:\n            q = f[2:] if f.startswith('--') else f[1:]\n            self.aliases.append(q)\n        self.type = x.type\n        if x.choices:\n            self.type = 'choices'\n        self.default = x.default\n        self.obj_defn = x\n        self.go_type = go_type_map[self.type]\n        if x.dest:\n            self.go_var_name = ''.join(x.capitalize() for x in x.dest.replace('-', '_').split('_'))\n        else:\n            self.go_var_name = ''.join(x.capitalize() for x in self.long.replace('-', '_').split('_'))\n        self.help_text = serialize_as_go_string(self.obj_defn.help.strip())\n\n    def struct_declaration(self) -> str:\n        return f'{self.go_var_name} {self.go_type}'\n\n    @property\n    def flags(self) -> list[str]:\n        return sorted(self.obj_defn.aliases)\n\n    def as_option(self, cmd_name: str = 'cmd', depth: int = 0, group: str = '') -> str:\n        add = f'AddToGroup(\"{serialize_as_go_string(group)}\", ' if group else 'Add('\n        aliases = ' '.join(self.flags)\n        ans = f'''{cmd_name}.{add}cli.OptionSpec{{\n            Name: \"{serialize_as_go_string(aliases)}\",\n            Type: \"{self.type}\",\n            Dest: \"{serialize_as_go_string(self.go_var_name)}\",\n            Help: \"{self.help_text}\",\n        '''\n        if self.type in ('choice', 'choices'):\n            c = ', '.join(self.sorted_choices)\n            cx = ', '.join(f'\"{serialize_as_go_string(x)}\"' for x in self.sorted_choices)\n            ans += f'\\nChoices: \"{serialize_as_go_string(c)}\",\\n'\n            ans += f'\\nCompleter: cli.NamesCompleter(\"Choices for {self.long}\", {cx}),'\n        elif self.obj_defn.completion.type is not CompletionType.none:\n            ans += ''.join(self.obj_defn.completion.as_go_code('Completer', ': ')) + ','\n        if depth > 0:\n            ans += f'\\n\\tDepth: {depth},\\n'\n        if self.default:\n            ans += f'\\n\\tDefault: \"{serialize_as_go_string(self.default)}\",\\n'\n        return ans + '})'\n\n    def as_string_for_commandline(self) -> Iterator[str]:\n        # }}}}}}}}}}}]]]]]]]]]]]]]]]]]\n        flag = self.flags[0]\n        val = f'opts.{self.go_var_name}'\n        if self.go_type == '[]string':\n            yield f'\\tfor _, x := range {val} {{ ans = append(ans, `{flag}=` + x) }}'\n            return\n        match self.go_type:\n            case 'bool':\n                yield f'sval = fmt.Sprintf(`%#v`, {val})'\n                godef = '`true`' if self.type != 'bool-set' else '`false`'\n            case 'int':\n                yield f'sval = fmt.Sprintf(`%d`, {val})'\n                godef = f\"`{self.default or '0'}`\"\n            case 'string':\n                yield f'sval = {val}'\n                godef = f'''\"{serialize_as_go_string(self.default or '')}\"'''\n            case 'float64':\n                yield f'sval = fmt.Sprintf(`%f`, {val})'\n                godef = f\"`{self.default or '0'}`\"\n            case _:\n                raise ValueError(f'Unknown type: {self.go_type}')\n        yield f'\\tif (sval != {godef}) {{ ans = append(ans, `{flag}=` + sval)}}'\n\n    @property\n    def sorted_choices(self) -> list[str]:\n        choices = sorted(self.obj_defn.choices)\n        choices.remove(self.default or '')\n        choices.insert(0, self.default or '')\n        return choices\n\n\ndef go_options_for_seq(seq: 'OptionSpecSeq') -> Iterator[GoOption]:\n    for x in seq:\n        if not isinstance(x, str):\n            yield GoOption(x)\n\n\n\ndef surround(x: str, start: int, end: int) -> str:\n    if sys.stdout.isatty():\n        x = f'\\033[{start}m{x}\\033[{end}m'\n    return x\n\n\nrole_map: dict[str, Callable[[str], str]] = {}\n\n\ndef role(func: Callable[[str], str]) -> Callable[[str], str]:\n    role_map[func.__name__] = func\n    return func\n\n\n@role\ndef emph(x: str) -> str:\n    return surround(x, 91, 39)\n\n\n@role\ndef cyan(x: str) -> str:\n    return surround(x, 96, 39)\n\n\n@role\ndef green(x: str) -> str:\n    return surround(x, 32, 39)\n\n\n@role\ndef blue(x: str) -> str:\n    return surround(x, 34, 39)\n\n\n@role\ndef yellow(x: str) -> str:\n    return surround(x, 93, 39)\n\n\n@role\ndef italic(x: str) -> str:\n    return surround(x, 3, 23)\n\n\n@role\ndef bold(x: str) -> str:\n    return surround(x, 1, 22)\n\n\n@role\ndef title(x: str) -> str:\n    return blue(bold(x))\n\n\n@role\ndef opt(text: str) -> str:\n    return bold(text)\n\n\n@role\ndef option(x: str) -> str:\n    idx = x.rfind('--')\n    if idx < 0:\n        idx = x.find('-')\n    if idx > -1:\n        x = x[idx:]\n    return bold(x.rstrip('>'))\n\n\n@role\ndef code(x: str) -> str:\n    return cyan(x)\n\n\ndef text_and_target(x: str) -> tuple[str, str]:\n    parts = x.split('<', 1)\n    return parts[0].strip(), parts[-1].rstrip('>')\n\n\n@role\ndef term(x: str) -> str:\n    return ref_hyperlink(x, 'term-')\n\n\n@role\ndef kbd(x: str) -> str:\n    return x\n\n\n@role\ndef env(x: str) -> str:\n    return ref_hyperlink(x, 'envvar-')\n\n\nrole_map['envvar'] = role_map['env']\n\n\n@run_once\ndef hostname() -> str:\n    from .utils import get_hostname\n    return get_hostname(fallback='localhost')\n\n\ndef hyperlink_for_url(url: str, text: str) -> str:\n    if sys.stdout.isatty():\n        return f'\\x1b]8;;{url}\\x1b\\\\\\x1b[4:3;58:5:4m{text}\\x1b[4:0;59m\\x1b]8;;\\x1b\\\\'\n    return text\n\n\ndef hyperlink_for_path(path: str, text: str) -> str:\n    path = os.path.abspath(path).replace(os.sep, \"/\")\n    if os.path.isdir(path):\n        path += path.rstrip(\"/\") + \"/\"\n    return hyperlink_for_url(f'file://{hostname()}{path}', text)\n\n\n@role\ndef file(x: str) -> str:\n    if x == 'kitty.conf':\n        x = hyperlink_for_path(os.path.join(config_dir, x), x)\n    return italic(x)\n\n\n@role\ndef doc(x: str) -> str:\n    t, q = text_and_target(x)\n    if t == q:\n        from .conf.types import ref_map\n        m = ref_map()['doc']\n        q = q.strip('/')\n        if q in m:\n            x = f'{m[q]} <{t}>'\n    return ref_hyperlink(x, 'doc-')\n\n\ndef ref_hyperlink(x: str, prefix: str = '') -> str:\n    t, q = text_and_target(x)\n    url = f'kitty+doc://{hostname()}/#ref={prefix}{q}'\n    t = re.sub(r':([a-z]+):`([^`]+)`', r'\\2', t)\n    return hyperlink_for_url(url, t)\n\n\n@role\ndef ref(x: str) -> str:\n    return ref_hyperlink(x)\n\n\n@role\ndef ac(x: str) -> str:\n    return ref_hyperlink(x, 'action-')\n\n\n@role\ndef iss(x: str) -> str:\n    return ref_hyperlink(x, 'issues-')\n\n\n@role\ndef pull(x: str) -> str:\n    return ref_hyperlink(x, 'pull-')\n\n\n@role\ndef disc(x: str) -> str:\n    return ref_hyperlink(x, 'discussions-')\n\n\ndef prettify(text: str) -> str:\n\n    def identity(x: str) -> str:\n        return x\n\n    def sub(m: 'Match[str]') -> str:\n        role, text = m.group(1, 2)\n        return role_map.get(role, identity)(text)\n\n    text = re.sub(r':([a-z]+):`([^`]+)`', sub, text)\n    return text\n\n\ndef prettify_rst(text: str) -> str:\n    return re.sub(r':([a-z]+):`([^`]+)`(=[^\\s.]+)', r':\\1:`\\2`:code:`\\3`', text)\n\n\ndef version(add_rev: bool = False) -> str:\n    rev = ''\n    from . import fast_data_types\n    if add_rev:\n        if getattr(fast_data_types, 'KITTY_VCS_REV', ''):\n            rev = f' ({fast_data_types.KITTY_VCS_REV[:10]})'\n    return '{} {}{} created by {}'.format(italic(appname), green(str_version), rev, title('Kovid Goyal'))\n\n\ndef wrap(text: str, limit: int = 80) -> Iterator[str]:\n    if not text.strip():\n        yield ''\n        return\n    in_escape = 0\n    current_line: list[str] = []\n    escapes: list[str] = []\n    current_word: list[str] = []\n    current_line_length = 0\n\n    def print_word(ch: str = '') -> Iterator[str]:\n        nonlocal current_word, current_line, escapes, current_line_length\n        cw = ''.join(current_word)\n        w = wcswidth(cw)\n        if current_line_length + w > limit:\n            yield ''.join(current_line)\n            current_line = []\n            current_line_length = 0\n            cw = cw.strip()\n            current_word = [cw]\n        if escapes:\n            current_line.append(''.join(escapes))\n            escapes = []\n        if current_word:\n            current_line.append(cw)\n            current_line_length += w\n            current_word = []\n        if ch:\n            current_word.append(ch)\n\n    for i, ch in enumerate(text):\n        if in_escape > 0:\n            if in_escape == 1 and ch in '[]':\n                in_escape = 2 if ch == '[' else 3\n            if (in_escape == 2 and ch == 'm') or (in_escape == 3 and ch == '\\\\' and text[i-1] == '\\x1b'):\n                in_escape = 0\n            escapes.append(ch)\n            continue\n        if ch == '\\x1b':\n            in_escape = 1\n            if current_word:\n                yield from print_word()\n            escapes.append(ch)\n            continue\n        if current_word and ch.isspace() and ch != '\\xa0':\n            yield from print_word(ch)\n        else:\n            current_word.append(ch)\n    yield from print_word()\n    if current_line:\n        yield ''.join(current_line)\n\n\ndef get_defaults_from_seq(seq: OptionSpecSeq) -> dict[str, Any]:\n    ans: dict[str, Any] = {}\n    for opt in seq:\n        if not isinstance(opt, str):\n            ans[opt.dest] = defval_for_opt(opt)\n    return ans\n\n\ndefault_msg = ('''\\\nRun the :italic:`{appname}` terminal emulator. You can also specify the\n:italic:`program` to run inside :italic:`{appname}` as normal arguments\nfollowing the :italic:`options`.\nFor example: {appname} --hold sh -c \"echo hello, world\"\n\nFor comprehensive documentation for kitty, please see: {url}''').format(\n    appname=appname, url=website_url())\n\n\ndef help_defval_for_bool(otype: str) -> str:\n    if otype == 'bool-set':\n        return 'no'\n    return 'yes'\n\n\nclass PrintHelpForSeq:\n\n    allow_pager = True\n\n    def __call__(self, seq: OptionSpecSeq, usage: str | None, message: str | None, appname: str) -> None:\n        from kitty.utils import screen_size_function\n        screen_size = screen_size_function()\n        try:\n            linesz = min(screen_size().cols, 76)\n        except OSError:\n            linesz = 76\n        blocks: list[str] = []\n        a = blocks.append\n\n        def wa(text: str, indent: int = 0, leading_indent: int | None = None) -> None:\n            if leading_indent is None:\n                leading_indent = indent\n            j = '\\n' + (' ' * indent)\n            lines: list[str] = []\n            for ln in text.splitlines():\n                lines.extend(wrap(ln, limit=linesz - indent))\n            a((' ' * leading_indent) + j.join(lines))\n\n        usage = '[program-to-run ...]' if usage is None else usage\n        optstring = '[options] ' if seq else ''\n        a('{}: {} {}{}'.format(title('Usage'), bold(yellow(appname)), optstring, usage))\n        a('')\n        message = message or default_msg\n        # replace rst literal code block syntax\n        message = message.replace('::\\n\\n', ':\\n\\n')\n        wa(prettify(message))\n        a('')\n        if seq:\n            a('{}:'.format(title('Options')))\n        for opt in seq:\n            if isinstance(opt, str):\n                a(f'{title(opt)}:')\n                continue\n            help_text = opt.help\n            if help_text == '!':\n                continue  # hidden option\n            a('  ' + ', '.join(map(green, sorted(opt.aliases, reverse=True))))\n            defval = opt.default\n            if (otype := opt.type).startswith('bool-'):\n                blocks[-1] += italic(f'[={help_defval_for_bool(otype)}]')\n            else:\n                dt = f'''=[{italic(defval or '\"\"')}]'''\n                blocks[-1] += dt\n            if opt.help:\n                t = help_text.replace('%default', str(defval)).strip()\n                # replace rst literal code block syntax\n                t = t.replace('::\\n\\n', ':\\n\\n')\n                t = t.replace('#placeholder_for_formatting#', '')\n                wa(prettify(t), indent=4)\n                if opt.choices:\n                    wa('Choices: {}'.format(', '.join(opt.choices)), indent=4)\n                a('')\n\n        text = '\\n'.join(blocks) + '\\n\\n' + version()\n        if print_help_for_seq.allow_pager and sys.stdout.isatty():\n            import subprocess\n            try:\n                p = subprocess.Popen(default_pager_for_help, stdin=subprocess.PIPE, preexec_fn=clear_handled_signals)\n            except FileNotFoundError:\n                print(text)\n            else:\n                try:\n                    p.communicate(text.encode('utf-8'))\n                except KeyboardInterrupt:\n                    raise SystemExit(1)\n                raise SystemExit(p.wait())\n        else:\n            print(text)\n\n\nprint_help_for_seq = PrintHelpForSeq()\n\n\ndef escape_rst(text: str) -> str:\n    text = text.replace('\\\\', '\\\\\\\\')\n    text = text.replace('*', '\\\\*')\n    text = text.replace('`', '\\\\`')\n    text = text.replace('_', '\\\\_')\n    text = text.replace('|', '\\\\|')\n    return text\n\n\ndef seq_as_rst(\n    seq: OptionSpecSeq,\n    usage: str | None,\n    message: str | None,\n    appname: str | None,\n    heading_char: str = '-'\n) -> str:\n    import textwrap\n    blocks: list[str] = []\n    a = blocks.append\n\n    usage = '[program-to-run ...]' if usage is None else usage\n    optstring = '[options] ' if seq else ''\n    a('.. highlight:: sh')\n    a('.. code-block:: sh')\n    a('')\n    a(f'  {appname} {optstring}{usage}')\n    a('')\n    message = message or default_msg\n    a(prettify_rst(message))\n    a('')\n    if seq:\n        a('Options')\n        a(heading_char * 30)\n    for opt in seq:\n        if isinstance(opt, str):\n            a(opt)\n            a('~' * (len(opt) + 10))\n            continue\n        help_text = opt.help\n        if help_text == '!':\n            continue  # hidden option\n        defn = '.. option:: '\n        if (otype := opt.type).startswith('bool-'):\n            val_name = f' [={help_defval_for_bool(otype)}]'\n        else:\n            val_name = ' <{}>'.format(opt.dest.upper())\n        a(defn + ', '.join(o + val_name for o in sorted(opt.aliases)))\n        if opt.help:\n            defval = opt.default\n            t = help_text.replace('%default', ':code:`' + escape_rst(str(defval)) + '`').strip()\n            t = t.replace('#placeholder_for_formatting#', '')\n            a('')\n            a(textwrap.indent(prettify_rst(t), ' ' * 4))\n            if defval is not None:\n                a(textwrap.indent(f'Default: :code:`{escape_rst(str(defval))}`', ' ' * 4))\n            if opt.choices:\n                a(textwrap.indent('Choices: {}'.format(', '.join(f':code:`{escape_rst(c)}`' for c in sorted(opt.choices))), ' ' * 4))\n            a('')\n\n    text = '\\n'.join(blocks)\n    return text\n\n\ndef as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str, extra_fields: Sequence[str] = ()) -> str:\n    from itertools import chain\n    ans: list[str] = [f'class {class_name}:']\n    for opt in chain(seq, disabled):\n        if isinstance(opt, str):\n            continue\n        name = opt.dest\n        otype = opt.type or 'str'\n        if otype in ('str', 'int', 'float'):\n            t = otype\n            if t == 'str' and defval_for_opt(opt) is None:\n                t = 'typing.Optional[str]'\n        elif otype == 'list':\n            t = 'typing.Sequence[str]'\n        elif otype in ('choice', 'choices'):\n            if opt.choices:\n                t = 'typing.Literal[{}]'.format(','.join(f'{x!r}' for x in opt.choices))\n            else:\n                t = 'str'\n        elif otype.startswith('bool-'):\n            t = 'bool'\n        else:\n            raise ValueError(f'Unknown CLI option type: {otype}')\n        ans.append(f'    {name}: {t}')\n    for x in extra_fields:\n        ans.append(f'    {x}')\n    return '\\n'.join(ans) + '\\n\\n\\n'\n\n\nbool_map = {'y': True, 'yes': True, 'true': True, 'n': False, 'no': False, 'false': False}\n\n\ndef to_bool(alias: str, x: str) -> bool:\n    try:\n        return bool_map[x]\n    except KeyError:\n        raise SystemExit(f'{x} is not a valid value for {alias}. Valid values are y, yes, true, n, no, false only')\n\n\nclass Options:\n\n    do_print = True\n\n    def __init__(self, seq: OptionSpecSeq, usage: str | None, message: str | None, appname: str | None):\n        self.seq = seq\n        self.usage, self.message, self.appname = usage, message, appname\n        self.names_map, self.alias_map, self.values_map = get_option_maps(seq)\n        self.help_called = self.version_called = False\n\n    def handle_help(self) -> NoReturn:\n        if self.do_print:\n            print_help_for_seq(self.seq, self.usage, self.message, self.appname or appname)\n        self.help_called = True\n        raise SystemExit(0)\n\n    def handle_version(self) -> NoReturn:\n        self.version_called = True\n        if self.do_print:\n            print(version())\n        raise SystemExit(0)\n\n\nPreparsedCLIFlags = tuple[dict[str, tuple[Any, bool]], list[str]]\n\n\ndef apply_preparsed_cli_flags(\n    preparsed_from_c: PreparsedCLIFlags, ans: Any, create_oc: Callable[[], Options],\n    track_seen_options: dict[str, Any] | None = None\n) -> list[str]:\n    for key, (val, is_seen) in preparsed_from_c[0].items():\n        if key == 'help' and is_seen and val:\n            create_oc().handle_help()\n        elif key == 'version' and is_seen and val:\n            create_oc().handle_version()\n        if is_seen and track_seen_options is not None:\n            track_seen_options[key] = val\n        setattr(ans, key, val)\n    return preparsed_from_c[1]\n\n\ndef parse_cmdline_inner(\n        args: list[str], oc: Options, disabled: OptionSpecSeq, names_map: dict[str, OptionDefinition],\n        values_map: dict[str, OptionDefinition], ans: Any, track_seen_options: dict[str, Any] | None = None\n) -> list[str]:\n    preparsed = parse_cli_from_spec(args, names_map, values_map)\n    leftover_args = apply_preparsed_cli_flags(preparsed, ans, lambda: oc, track_seen_options)\n    for opt in disabled:\n        if not isinstance(opt, str):\n            setattr(ans, opt.dest, defval_for_opt(opt))\n    return leftover_args\n\n\ndef parse_cmdline(\n    oc: Options, disabled: OptionSpecSeq, ans: Any, args: list[str] | None = None,\n    track_seen_options: dict[str, Any] | None = None\n) -> list[str]:\n    names_map = oc.names_map.copy()\n    values_map = oc.values_map.copy()\n    if 'help' not in names_map:\n        names_map['help'] = OptionDefinition(type='bool-set', aliases=('--help', '-h'))\n        values_map['help'] = False\n    if 'version' not in names_map:\n        names_map['version'] = OptionDefinition(type='bool-set', aliases=('--version', '-v'))\n        values_map['version'] = False\n    try:\n        return parse_cmdline_inner(sys.argv[1:] if args is None else args, oc, disabled, names_map, values_map, ans, track_seen_options)\n    except Exception as e:\n        raise SystemExit(str(e))\n\n\nspec_cache: dict[str, tuple[Options, OptionSpecSeq]] = {}\n\n\ndef cached_parse_cmdline(spec: str, args: list[str], ans: Any) -> list[str]:\n    if (x := spec_cache.get(spec)) is None:\n        seq, disabled = parse_option_spec(spec)\n        oc = Options(seq, '', '', '')\n        x = spec_cache[spec] = oc, disabled\n    oc, disabled = x\n    leftover_args = parse_cmdline_inner(args, oc, disabled, oc.names_map, oc.values_map, ans)\n    return leftover_args\n\n\ndef options_for_completion() -> OptionSpecSeq:\n    raw = '--help -h\\ntype=bool-set\\nShow help for {appname} command line options\\n\\n{raw}'.format(\n            appname=appname, raw=kitty_options_spec())\n    return parse_option_spec(raw)[0]\n\n\ndef option_spec_as_rst(\n    ospec: Callable[[], str] = kitty_options_spec,\n    usage: str | None = None, message: str | None = None, appname: str | None = None,\n    heading_char: str = '-'\n) -> str:\n    options = parse_option_spec(ospec())\n    seq, disabled = options\n    oc = Options(seq, usage, message, appname)\n    return seq_as_rst(oc.seq, oc.usage, oc.message, oc.appname, heading_char=heading_char)\n\n\nT = TypeVar('T')\n\n\ndef parse_args(\n    args: list[str] | None = None,\n    ospec: Callable[[], str] = kitty_options_spec,\n    usage: str | None = None,\n    message: str | None = None,\n    appname: str | None = None,\n    result_class: type[T] | None = None,\n    preparsed_from_c: PreparsedCLIFlags | None = None,\n    track_seen_options: dict[str, Any] | None = None,\n) -> tuple[T, list[str]]:\n    if result_class is not None:\n        ans = result_class()\n    else:\n        ans = cast(T, CLIOptions())\n\n    def create_oc() -> Options:\n        options = parse_option_spec(ospec())\n        seq, disabled = options\n        return Options(seq, usage, message, appname)\n\n    if preparsed_from_c:\n        return ans, apply_preparsed_cli_flags(preparsed_from_c, ans, create_oc)\n\n    options = parse_option_spec(ospec())\n    seq, disabled = options\n    oc = Options(seq, usage, message, appname)\n    return ans, parse_cmdline(oc, disabled, ans, args=args, track_seen_options=track_seen_options)\n\n\nSYSTEM_CONF = f'/etc/xdg/{appname}/{appname}.conf'\n\n\ndef default_config_paths(conf_paths: Sequence[str]) -> tuple[str, ...]:\n    return tuple(resolve_config(SYSTEM_CONF, defconf, conf_paths))\n\n\n@run_once\ndef override_pat() -> 're.Pattern[str]':\n    return re.compile(r'^([a-zA-Z0-9_]+)[ \\t]*=')\n\n\ndef parse_override(x: str) -> str:\n    # Does not cover the case where `name =` when `=` is the value.\n    return override_pat().sub(r'\\1 ', x.lstrip())\n\n\ndef create_opts(args: CLIOptions, accumulate_bad_lines: list[BadLineType] | None = None) -> KittyOpts:\n    from .config import load_config\n    config = default_config_paths(args.config)\n    overrides = map(parse_override, args.override or ())\n    opts = load_config(*config, overrides=overrides, accumulate_bad_lines=accumulate_bad_lines)\n    return opts\n\n\ndef create_default_opts() -> KittyOpts:\n    from .config import load_config\n    config = default_config_paths(())\n    opts = load_config(*config)\n    return opts\n"
  },
  {
    "path": "kitty/cli_stub.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom collections.abc import Sequence\n\n\nclass CLIOptions:\n    def __repr__(self) -> str:\n        return repr(vars(self))\n\n\nLaunchCLIOptions = AskCLIOptions = ClipboardCLIOptions = DiffCLIOptions = CLIOptions\nHintsCLIOptions = IcatCLIOptions = PanelCLIOptions = ResizeCLIOptions = CLIOptions\nErrorCLIOptions = UnicodeCLIOptions = RCOptions = RemoteFileCLIOptions = CLIOptions\nBroadcastCLIOptions = ShowKeyCLIOptions = SaveAsSessionOptions = GotoSessionOptions = CLIOptions\nThemesCLIOptions = TransferCLIOptions = LoadConfigRCOptions = ActionRCOptions = CLIOptions\n\n\ndef generate_stub() -> None:\n    from .cli import as_type_stub\n    from .conf.utils import save_type_stub\n    from .simple_cli_definitions import parse_option_spec\n    text = 'import typing\\n\\n\\n'\n\n    def do(otext: str | None = None, cls: str = 'CLIOptions', extra_fields: Sequence[str] = ()) -> None:\n        nonlocal text\n        text += as_type_stub(*parse_option_spec(otext), class_name=cls, extra_fields=extra_fields)\n\n    do(extra_fields=('args: typing.List[str]',))\n\n    from .launch import options_spec\n    do(options_spec(), 'LaunchCLIOptions')\n\n    from .remote_control import global_options_spec\n    do(global_options_spec(), 'RCOptions')\n\n    from kittens.ask.main import option_text\n    do(option_text(), 'AskCLIOptions')\n\n    from kittens.remote_file.main import option_text\n    do(option_text(), 'RemoteFileCLIOptions')\n\n    from kittens.clipboard.main import OPTIONS\n    do(OPTIONS(), 'ClipboardCLIOptions')\n\n    from kittens.show_key.main import OPTIONS\n    do(OPTIONS(), 'ShowKeyCLIOptions')\n\n    from kittens.diff.main import OPTIONS\n    do(OPTIONS(), 'DiffCLIOptions')\n\n    from kittens.hints.main import OPTIONS\n    do(OPTIONS(), 'HintsCLIOptions')\n\n    from kittens.broadcast.main import OPTIONS\n    do(OPTIONS(), 'BroadcastCLIOptions')\n\n    from kittens.icat.main import OPTIONS as OS\n    do(OS, 'IcatCLIOptions')\n\n    from kittens.panel.main import panel_kitten_options_spec\n    do(panel_kitten_options_spec(), 'PanelCLIOptions')\n\n    from kittens.resize_window.main import OPTIONS\n    do(OPTIONS(), 'ResizeCLIOptions')\n\n    from kittens.unicode_input.main import OPTIONS\n    do(OPTIONS(), 'UnicodeCLIOptions')\n\n    from kittens.themes.main import OPTIONS\n    do(OPTIONS(), 'ThemesCLIOptions')\n\n    from kittens.transfer.main import option_text\n    do(option_text(), 'TransferCLIOptions')\n\n    from kitty.session import goto_session_options, save_as_session_options\n    do(save_as_session_options(), 'SaveAsSessionOptions')\n    do(goto_session_options(), 'GotoSessionOptions')\n\n    from kitty.rc.base import all_command_names, command_for_name\n    for cmd_name in all_command_names():\n        cmd = command_for_name(cmd_name)\n        if cmd.options_spec:\n            do(cmd.options_spec, f'{cmd.__class__.__name__}RCOptions')\n\n    save_type_stub(text, __file__)\n\n\nif __name__ == '__main__':\n    import subprocess\n    subprocess.Popen([\n        'kitty', '+runpy',\n        'from kitty.cli_stub import generate_stub; generate_stub()'\n    ])\n"
  },
  {
    "path": "kitty/client.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\n# Replay the log from --dump-commands. To use first run\n# kitty --dump-commands > file.txt\n# then run\n# kitty --replay-commands file.txt\n# will replay the commands and pause at the end waiting for user to press enter\n\nimport json\nimport sys\nfrom contextlib import suppress\nfrom typing import Any\n\nfrom .fast_data_types import TEXT_SIZE_CODE\n\nCSI = '\\x1b['\nOSC = '\\x1b]'\n\n\ndef write(x: str) -> None:\n    sys.stdout.write(x)\n    sys.stdout.flush()\n\n\ndef set_title(*args: Any) -> None:\n    pass\n\n\ndef set_icon(*args: Any) -> None:\n    pass\n\n\ndef screen_bell() -> None:\n    pass\n\n\ndef screen_normal_keypad_mode() -> None:\n    write('\\x1b>')\n\n\ndef screen_alternate_keypad_mode() -> None:\n    write('\\x1b=')\n\n\ndef screen_cursor_position(y: int, x: int) -> None:\n    write(f'{CSI}{y};{x}H')\n\n\ndef screen_cursor_forward(amt: int) -> None:\n    write(f'{CSI}{amt}C')\n\n\ndef screen_save_cursor() -> None:\n    write('\\x1b7')\n\n\ndef screen_restore_cursor() -> None:\n    write('\\x1b8')\n\n\ndef screen_cursor_back1(amt: int) -> None:\n    write(f'{CSI}{amt}D')\n\n\ndef screen_save_modes() -> None:\n    write(f'{CSI}?s')\n\n\ndef screen_restore_modes() -> None:\n    write(f'{CSI}?r')\n\n\ndef screen_designate_charset(which: int, to: int) -> None:\n    w = '()'[int(which)]\n    t = chr(int(to))\n    write(f'\\x1b{w}{t}')\n\n\ndef select_graphic_rendition(payload: str) -> None:\n    write(f'{CSI}{payload}m')\n\n\ndef deccara(*a: int) -> None:\n    write(f'{CSI}{\";\".join(map(str, a))}$r')\n\n\ndef screen_cursor_to_column(c: int) -> None:\n    write(f'{CSI}{c}G')\n\n\ndef screen_cursor_to_line(ln: int) -> None:\n    write(f'{CSI}{ln}d')\n\n\ndef screen_set_mode(x: int, private: bool) -> None:\n    write(f'{CSI}{\"?\" if private else \"\"}{x}h')\n\n\ndef screen_save_mode(x: int, private: bool) -> None:\n    write(f'{CSI}{\"?\" if private else \"\"}{x}s')\n\n\ndef screen_reset_mode(x: int, private: bool) -> None:\n    write(f'{CSI}{\"?\" if private else \"\"}{x}l')\n\n\ndef screen_restore_mode(x: int, private: bool) -> None:\n    write(f'{CSI}{\"?\" if private else \"\"}{x}r')\n\n\ndef screen_set_margins(t: int, b: int) -> None:\n    write(f'{CSI}{t};{b}r')\n\n\ndef screen_indexn(n: int) -> None:\n    write(f'{CSI}{n}S')\n\n\ndef screen_delete_characters(count: int) -> None:\n    write(f'{CSI}{count}P')\n\n\ndef screen_push_colors(which: int) -> None:\n    write(f'{CSI}{which}#P')\n\n\ndef screen_pop_colors(which: int) -> None:\n    write(f'{CSI}{which}#Q')\n\n\ndef screen_report_colors() -> None:\n    write(f'{CSI}#R')\n\n\ndef screen_repeat_character(num: int) -> None:\n    write(f'{CSI}{num}b')\n\n\ndef screen_insert_characters(count: int) -> None:\n    write(f'{CSI}{count}@')\n\n\ndef screen_scroll(count: int) -> None:\n    write(f'{CSI}{count}S')\n\n\ndef screen_erase_in_display(how: int, private: bool) -> None:\n    write(f'{CSI}{\"?\" if private else \"\"}{how}J')\n\n\ndef screen_erase_in_line(how: int, private: bool) -> None:\n    write(f'{CSI}{\"?\" if private else \"\"}{how}K')\n\n\ndef screen_erase_characters(num: int) -> None:\n    write(f'{CSI}{num}X')\n\n\ndef screen_delete_lines(num: int) -> None:\n    write(f'{CSI}{num}M')\n\n\ndef screen_cursor_up2(count: int) -> None:\n    write(f'{CSI}{count}A')\n\n\ndef screen_cursor_down(count: int) -> None:\n    write(f'{CSI}{count}B')\n\n\ndef screen_cursor_down1(count: int) -> None:\n    write(f'{CSI}{count}E')\n\n\ndef screen_report_key_encoding_flags() -> None:\n    write(f'{CSI}?u')\n\n\ndef screen_set_key_encoding_flags(flags: int, how: int) -> None:\n    write(f'{CSI}={flags};{how}u')\n\n\ndef screen_push_key_encoding_flags(flags: int) -> None:\n    write(f'{CSI}>{flags}u')\n\n\ndef screen_pop_key_encoding_flags(flags: int) -> None:\n    write(f'{CSI}<{flags}u')\n\n\ndef screen_carriage_return() -> None:\n    write('\\r')\n\n\ndef screen_linefeed() -> None:\n    write('\\n')\n\n\ndef screen_tab() -> None:\n    write('\\t')\n\n\ndef screen_backspace() -> None:\n    write('\\x08')\n\n\ndef screen_set_cursor(mode: int, secondary: int) -> None:\n    write(f'{CSI}{secondary} q')\n\n\ndef screen_insert_lines(num: int) -> None:\n    write(f'{CSI}{num}L')\n\n\ndef draw(*a: str) -> None:\n    write(' '.join(a))\n\n\ndef screen_manipulate_title_stack(op: int, which: int) -> None:\n    write(f'{CSI}{op};{which}t')\n\n\ndef report_device_attributes(mode: int, char: int) -> None:\n    x = CSI\n    if char:\n        x += chr(char)\n    if mode:\n        x += str(mode)\n    write(f'{x}c')\n\n\ndef report_device_status(x: int, private: bool) -> None:\n    write(f'{CSI}{\"?\" if private else \"\"}{x}n')\n\n\ndef screen_decsace(mode: int) -> None:\n    write(f'{CSI}{mode}*x')\n\n\ndef screen_set_8bit_controls(mode: int) -> None:\n    write(f'\\x1b {\"G\" if mode else \"F\"}')\n\n\ndef write_osc(code: int, string: str = '') -> None:\n    if string:\n        write(f'{OSC}{code};{string}\\x07')\n    else:\n        write(f'{OSC}{code}\\x07')\n\n\nset_color_table_color = process_cwd_notification = write_osc\nclipboard_control_pending: str = ''\n\n\ndef set_dynamic_color(payload: str) -> None:\n    code, data = payload.partition(' ')[::2]\n    write_osc(int(code), data)\n\n\ndef shell_prompt_marking(payload: str) -> None:\n    write_osc(133, payload)\n\n\ndef clipboard_control(payload: str) -> None:\n    global clipboard_control_pending\n    code, data = payload.split(';', 1)\n    if code == '-52':\n        if clipboard_control_pending:\n            clipboard_control_pending += data.lstrip(';')\n        else:\n            clipboard_control_pending = payload\n        return\n    if clipboard_control_pending:\n        clipboard_control_pending += data.lstrip(';')\n        payload = clipboard_control_pending\n        clipboard_control_pending = ''\n    write(f'{OSC}{payload}\\x07')\n\n\ndef multicell_command(payload: str) -> None:\n    c = json.loads(payload)\n    text = c.pop('', '')\n    m = ''\n    if (w := c.pop('width', None)) is not None and w > 0:\n        m += f'w={w}:'\n    if (s := c.pop('scale', None)) is not None and s > 1:\n        m += f's={s}:'\n    if (n := c.pop('subscale_n', None)) is not None and n > 0:\n        m += f'n={n}:'\n    if (d := c.pop('subscale_d', None)) is not None and d > 0:\n        m += f'd={d}:'\n    if (v := c.pop('vertical_align', None)) is not None and v > 0:\n        m += f'v={v}:'\n    if (h := c.pop('horizontal_align', None)) is not None and h > 0:\n        m += f'h={h}:'\n    if c:\n        raise Exception('Unknown keys in multicell_command: ' + ', '.join(c))\n    write(f'{OSC}{TEXT_SIZE_CODE};{m.rstrip(\":\")};{text}\\a')\n\n\ndef screen_multi_cursor(rest: str) -> None:\n    write(f'{CSI}>{rest.strip()} q')\n\n\ndef replay(raw: str) -> None:\n    specials = frozenset({\n        'draw', 'set_title', 'set_icon', 'set_dynamic_color', 'set_color_table_color', 'select_graphic_rendition',\n        'process_cwd_notification', 'clipboard_control', 'shell_prompt_marking', 'multicell_command', 'screen_multi_cursor',\n    })\n    for line in raw.splitlines():\n        if line.strip() and not line.startswith('#'):\n            cmd, rest = line.partition(' ')[::2]\n            if cmd in specials:\n                globals()[cmd](rest)\n            else:\n                r = map(int, rest.split()) if rest else ()\n                globals()[cmd](*r)\n\n\ndef main(path: str) -> None:\n    with open(path) as f:\n        raw = f.read()\n    replay(raw)\n    with suppress(EOFError, KeyboardInterrupt):\n        input()\n"
  },
  {
    "path": "kitty/clipboard.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport io\nimport os\nfrom collections.abc import Callable, Mapping\nfrom enum import Enum, IntEnum\nfrom gettext import gettext as _\nfrom tempfile import TemporaryFile\nfrom typing import IO, NamedTuple, Union\n\nfrom .conf.utils import uniq\nfrom .constants import supports_primary_selection\nfrom .fast_data_types import (\n    ESC_OSC,\n    GLFW_CLIPBOARD,\n    GLFW_PRIMARY_SELECTION,\n    StreamingBase64Decoder,\n    find_in_memoryview,\n    get_boss,\n    get_clipboard_mime,\n    get_options,\n    set_clipboard_data_types,\n)\nfrom .typing_compat import WindowType\nfrom .utils import log_error\n\nREAD_RESPONSE_CHUNK_SIZE = 4096\n\n\nclass Tempfile:\n\n    def __init__(self, max_size: int) -> None:\n        self.file: io.BytesIO | IO[bytes] = io.BytesIO()\n        self.max_size = max_size\n\n    def rollover_if_needed(self, sz: int) -> None:\n        if isinstance(self.file, io.BytesIO) and self.file.tell() + sz > self.max_size:\n            before = self.file.getvalue()\n            self.file = TemporaryFile()\n            self.file.write(before)\n\n    def write(self, data: bytes) -> None:\n        self.rollover_if_needed(len(data))\n        self.file.write(data)\n\n    def tell(self) -> int:\n        return self.file.tell()\n\n    def seek(self, pos: int) -> None:\n        self.file.seek(pos, os.SEEK_SET)\n\n    def read(self, offset: int, size: int) -> bytes:\n        self.file.seek(offset)\n        return self.file.read(size)\n\n    def create_chunker(self, offset: int, size: int) -> Callable[[], Callable[[], bytes]]:\n        def chunk_creator() -> Callable[[], bytes]:\n            pos = offset\n            limit = offset + size\n\n            def chunker() -> bytes:\n                nonlocal pos, limit\n                if pos >= limit:\n                    return b''\n                ans = self.read(pos, min(io.DEFAULT_BUFFER_SIZE, limit - pos))\n                pos = self.file.tell()\n                return ans\n            return chunker\n        return chunk_creator\n\n\nDataType = Union[bytes, Callable[[], Callable[[], bytes]]]\nTARGETS_MIME = '.'\n\n\nclass ClipboardType(IntEnum):\n    clipboard = GLFW_CLIPBOARD\n    primary_selection = GLFW_PRIMARY_SELECTION\n    unknown = -311\n\n    @staticmethod\n    def from_osc52_where_field(where: str) -> 'ClipboardType':\n        if where in ('c', 's'):\n            return ClipboardType.clipboard\n        if where == 'p':\n            return ClipboardType.primary_selection\n        return ClipboardType.unknown\n\n\nclass Clipboard:\n\n    def __init__(self, clipboard_type: ClipboardType = ClipboardType.clipboard) -> None:\n        self.data: dict[str, DataType] = {}\n        self.clipboard_type = clipboard_type\n        self.enabled = self.clipboard_type is ClipboardType.clipboard or supports_primary_selection\n\n    def set_text(self, x: str | bytes) -> None:\n        if isinstance(x, str):\n            x = x.encode('utf-8')\n        self.set_mime({'text/plain': x})\n\n    def set_mime(self, data: Mapping[str, DataType]) -> None:\n        if self.enabled and isinstance(data, dict):\n            self.data = data\n            set_clipboard_data_types(self.clipboard_type, tuple(self.data))\n\n    def get_text(self) -> str:\n        parts: list[bytes] = []\n        self.get_mime(\"text/plain\", parts.append)\n        return b''.join(parts).decode('utf-8', 'replace')\n\n    def get_mime(self, mime: str, output: Callable[[bytes], None]) -> None:\n        if self.enabled:\n            try:\n                get_clipboard_mime(self.clipboard_type, mime, output)\n            except RuntimeError as err:\n                if str(err) != 'is_self_offer':\n                    raise\n                data = self.data.get(mime, b'')\n                if isinstance(data, bytes):\n                    output(data)\n                else:\n                    chunker = data()\n                    q = b' '\n                    while q:\n                        q = chunker()\n                        output(q)\n\n    def get_mime_data(self, mime: str) -> bytes:\n        ans: list[bytes] = []\n        self.get_mime(mime, ans.append)\n        return b''.join(ans)\n\n    def get_available_mime_types_for_paste(self) -> tuple[str, ...]:\n        if self.enabled:\n            parts: list[bytes] = []\n            try:\n                get_clipboard_mime(self.clipboard_type, None, parts.append)\n            except RuntimeError as err:\n                if str(err) != 'is_self_offer':\n                    raise\n                return tuple(self.data)\n            return tuple(x.decode('utf-8', 'replace') for x in uniq(parts))\n        return ()\n\n    def __call__(self, mime: str) -> Callable[[], bytes]:\n        data = self.data.get(mime, b'')\n        if isinstance(data, str):\n            data = data.encode('utf-8')  # type: ignore\n        if isinstance(data, bytes):\n            def chunker() -> bytes:\n                nonlocal data\n                assert isinstance(data, bytes)\n                ans = data\n                data = b''\n                return ans\n            return chunker\n\n        return data()\n\n\ndef set_clipboard_string(x: str | bytes) -> None:\n    get_boss().clipboard.set_text(x)\n\n\ndef get_clipboard_string() -> str:\n    return get_boss().clipboard.get_text()\n\n\ndef set_primary_selection(x: str | bytes) -> None:\n    get_boss().primary_selection.set_text(x)\n\n\ndef get_primary_selection() -> str:\n    return get_boss().primary_selection.get_text()\n\n\ndef develop() -> tuple[Clipboard, Clipboard]:\n    from .constants import detect_if_wayland_ok, is_macos\n    from .fast_data_types import set_boss\n    from .main import init_glfw_module\n    glfw_module = 'cocoa' if is_macos else ('wayland' if detect_if_wayland_ok() else 'x11')\n\n    class Boss:\n        clipboard = Clipboard()\n        primary_selection = Clipboard(ClipboardType.primary_selection)\n    init_glfw_module(glfw_module)\n    set_boss(Boss())  # type: ignore\n    return Boss.clipboard, Boss.primary_selection\n\n\nclass ProtocolType(Enum):\n    osc_52 = 52\n    osc_5522 = 5522\n\n\ndef encode_mime(x: str) -> str:\n    import base64\n    return base64.standard_b64encode(x.encode('utf-8')).decode('ascii')\n\n\ndef decode_metadata_value(k: str, x: str) -> str:\n    if k in ('mime', 'name', 'pw'):\n        import base64\n        x = base64.standard_b64decode(x).decode('utf-8')\n    return x\n\n\nclass ReadRequest(NamedTuple):\n    is_primary_selection: bool = False\n    mime_types: tuple[str, ...] = ('text/plain',)\n    id: str = ''\n    protocol_type: ProtocolType = ProtocolType.osc_52\n    human_name: str = ''\n    password: str = ''\n    otp_for_response: str = ''\n\n    def encode_response(self, status: str = 'DATA', mime: str = '', payload: bytes | memoryview = b'') -> bytes:\n        from base64 import standard_b64encode\n        def encode_b64(s: str) -> str:\n            return standard_b64encode(s.encode()).decode()\n        ans = f'{self.protocol_type.value};type=read:status={status}'\n        if status == 'OK' and self.is_primary_selection:\n            ans += ':loc=primary'\n        if self.id:\n            ans += f':id={self.id}'\n        if mime:\n            ans += f':mime={encode_mime(mime)}'\n        if self.otp_for_response:\n            ans += f':pw={encode_b64(self.otp_for_response)}'\n        a = ans.encode('ascii')\n        if payload:\n            a += b';' + standard_b64encode(payload)\n        return a\n\n\ndef encode_osc52(loc: str, response: str) -> str:\n    from base64 import standard_b64encode\n    return '52;{};{}'.format(\n        loc, standard_b64encode(response.encode('utf-8')).decode('ascii'))\n\n\nclass MimePos(NamedTuple):\n    start: int\n    size: int\n\n\nclass WriteRequest:\n\n    def __init__(\n        self, is_primary_selection: bool = False, protocol_type: ProtocolType = ProtocolType.osc_52, id: str = '',\n        rollover_size: int = 16 * 1024 * 1024, max_size: int = -1, human_name: str = '', password: str = '',\n    ) -> None:\n        self.decoder = StreamingBase64Decoder()\n        self.human_name = human_name\n        self.password = password\n        self.id = id\n        self.is_primary_selection = is_primary_selection\n        self.protocol_type = protocol_type\n        self.max_size_exceeded = False\n        self.tempfile = Tempfile(max_size=rollover_size)\n        self.mime_map: dict[str, MimePos] = {}\n        self.currently_writing_mime = ''\n        self.max_size = (get_options().clipboard_max_size * 1024 * 1024) if max_size < 0 else max_size\n        self.aliases: dict[str, str] = {}\n        self.committed = False\n        self.permission_pending = True\n        self.commit_pending = False\n\n    def encode_response(self, status: str = 'OK') -> bytes:\n        ans = f'{self.protocol_type.value};type=write:status={status}'\n        if self.id:\n            ans += f':id={self.id}'\n        a = ans.encode('ascii')\n        return a\n\n    def commit(self) -> None:\n        if self.committed:\n            return\n        if self.permission_pending:\n            self.commit_pending = True\n            return\n        self.committed = True\n        self.commit_pending = False\n        cp = get_boss().primary_selection if self.is_primary_selection else get_boss().clipboard\n        if cp.enabled:\n            for alias, src in self.aliases.items():\n                pos = self.mime_map.get(src)\n                if pos is not None:\n                    self.mime_map[alias] = pos\n            x = {mime: self.tempfile.create_chunker(pos.start, pos.size) for mime, pos in self.mime_map.items()}\n            cp.set_mime(x)\n\n    def add_base64_data(self, data: str | bytes | memoryview, mime: str = 'text/plain') -> None:\n        if isinstance(data, str):\n            data = data.encode('ascii')\n        if self.currently_writing_mime and self.currently_writing_mime != mime:\n            self.flush_base64_data()\n        if not self.currently_writing_mime:\n            self.mime_map[mime] = MimePos(self.tempfile.tell(), -1)\n            self.currently_writing_mime = mime\n        self.write_base64_data(data)\n\n    def flush_base64_data(self) -> None:\n        if self.currently_writing_mime:\n            if self.decoder.needs_more_data():\n                log_error('Received incomplete data for clipboard')\n            self.decoder.reset()\n            start = self.mime_map[self.currently_writing_mime][0]\n            self.mime_map[self.currently_writing_mime] = MimePos(start, self.tempfile.tell() - start)\n            self.currently_writing_mime = ''\n\n    def write_base64_data(self, b: bytes | memoryview) -> None:\n        if not self.max_size_exceeded:\n            try:\n                decoded = self.decoder.decode(b)\n            except ValueError as e:\n                log_error(f'Clipboard write request has invalid data, ignoring this chunk of data. Error: {e}')\n                self.decoder.reset()\n                decoded = b''\n            if decoded:\n                self.tempfile.write(decoded)\n                if self.max_size > 0 and self.tempfile.tell() > (self.max_size * 1024 * 1024):\n                    log_error(f'Clipboard write request has more data than allowed by clipboard_max_size ({self.max_size}), truncating')\n                    self.max_size_exceeded = True\n\n    def data_for(self, mime: str = 'text/plain', offset: int = 0, size: int = -1) -> bytes:\n        start, full_size = self.mime_map[mime]\n        if size == -1:\n            size = full_size\n        return self.tempfile.read(start+offset, size)\n\n\nclass GrantedPermission:\n\n    one_time: bool = False\n    write_ban: bool = False\n    read_ban: bool = False\n\n    def __init__(self, read: bool = False, write: bool = False, one_time: bool = False):\n        self.read, self.write = read, write\n        self.one_time = one_time\n\n\nclass ClipboardRequestManager:\n\n    def __init__(self, window_id: int) -> None:\n        self.window_id = window_id\n        self.currently_asking_permission_for: ReadRequest | None = None\n        self.in_flight_write_request: WriteRequest | None = None\n        self.osc52_in_flight_write_requests: dict[ClipboardType, WriteRequest] = {}\n        self.granted_passwords: dict[str, GrantedPermission] = {}\n\n    def parse_osc_5522(self, data: memoryview) -> None:\n        import base64\n\n        from .notifications import sanitize_id\n        idx = find_in_memoryview(data, ord(b';'))\n        if idx > -1:\n            metadata = str(data[:idx], \"utf-8\", \"replace\")\n            epayload = data[idx+1:]\n        else:\n            metadata = str(data, \"utf-8\", \"replace\")\n            epayload = data[len(data):]\n        m: dict[str, str] = {}\n        for record in metadata.split(':'):\n            try:\n                k, v = record.split('=', 1)\n            except Exception:\n                log_error('Malformed OSC 5522: metadata is not key=value pairs')\n                return\n            m[k] = decode_metadata_value(k, v)\n        typ = m.get('type', '')\n        if typ == 'read':\n            payload = base64.standard_b64decode(epayload)\n            rr = ReadRequest(\n                is_primary_selection=m.get('loc', '') == 'primary',\n                mime_types=tuple(payload.decode('utf-8').split()),\n                protocol_type=ProtocolType.osc_5522, id=sanitize_id(m.get('id', '')),\n                human_name=m.get('name', ''), password=m.get('pw', ''),\n            )\n            self.handle_read_request(rr)\n        elif typ == 'write':\n            self.in_flight_write_request = WriteRequest(\n                is_primary_selection=m.get('loc', '') == 'primary',\n                protocol_type=ProtocolType.osc_5522, id=sanitize_id(m.get('id', '')),\n                human_name=m.get('name', ''), password=m.get('pw', ''),\n            )\n            self.handle_write_request(self.in_flight_write_request)\n        elif typ == 'walias':\n            wr = self.in_flight_write_request\n            mime = m.get('mime', '')\n            if mime and wr is not None:\n                aliases = base64.standard_b64decode(epayload).decode('utf-8').split()\n                for alias in aliases:\n                    wr.aliases[alias] = mime\n        elif typ == 'wdata':\n            wr = self.in_flight_write_request\n            w = get_boss().window_id_map.get(self.window_id)\n            if wr is None:\n                return\n            mime = m.get('mime', '')\n            if mime:\n                try:\n                    wr.add_base64_data(epayload, mime)\n                except OSError:\n                    if w is not None:\n                        w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='EIO'))\n                    self.in_flight_write_request = None\n                    raise\n                except Exception:\n                    if w is not None:\n                        w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='EINVAL'))\n                    self.in_flight_write_request = None\n                    raise\n            else:\n                self.commit_write_request(wr)\n\n    def commit_write_request(self, wr: WriteRequest, needs_flush: bool = True) -> None:\n        if needs_flush:\n            wr.flush_base64_data()\n        wr.commit()\n        if wr.committed:\n            self.in_flight_write_request = None\n            w = get_boss().window_id_map.get(self.window_id)\n            if w is not None:\n                w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='DONE'))\n\n    def parse_osc_52(self, data: memoryview, is_partial: bool = False) -> None:\n        idx = find_in_memoryview(data, ord(b';'))\n        if idx > -1:\n            where = str(data[:idx], \"utf-8\", 'replace')\n            data = data[idx+1:]\n        else:\n            where = str(data, \"utf-8\", 'replace')\n            data = data[len(data):]\n        destinations = {ClipboardType.from_osc52_where_field(where) for where in where or 's0'}\n        destinations.discard(ClipboardType.unknown)\n        if len(data) == 1 and data.tobytes() == b'?':\n            for d in destinations:\n                rr = ReadRequest(is_primary_selection=d is ClipboardType.primary_selection)\n                self.handle_read_request(rr)\n        else:\n            for d in destinations:\n                wr = self.osc52_in_flight_write_requests.get(d)\n                if wr is None:\n                    wr = self.osc52_in_flight_write_requests[d] = WriteRequest(d is ClipboardType.primary_selection)\n                wr.add_base64_data(data)\n                if is_partial:\n                    return\n                self.osc52_in_flight_write_requests.pop(d, None)\n                self.handle_write_request(wr)\n\n    def handle_write_request(self, wr: WriteRequest) -> None:\n        wr.flush_base64_data()\n        q = 'write-primary' if wr.is_primary_selection else 'write-clipboard'\n        allowed = q in get_options().clipboard_control\n        self.fulfill_write_request(wr, allowed)\n\n    def fulfill_write_request(self, wr: WriteRequest, allowed: bool = True) -> None:\n        wr.permission_pending = not allowed\n        if wr.protocol_type is ProtocolType.osc_52:\n            self.fulfill_legacy_write_request(wr, allowed)\n            return\n        cp = get_boss().primary_selection if wr.is_primary_selection else get_boss().clipboard\n        w = get_boss().window_id_map.get(self.window_id)\n        if w is None:\n            self.in_flight_write_request = None\n            return\n        if not cp.enabled:\n            self.in_flight_write_request = None\n            w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='ENOSYS'))\n            return\n        if not allowed:\n            if wr.password and wr.human_name:\n                if self.password_is_allowed_already(wr.password, for_write=True):\n                    wr.permission_pending = False\n                else:\n                    wid = w.id\n                    def callback(granted: bool) -> None:\n                        if wr is not self.in_flight_write_request:\n                            return\n                        if granted:\n                            wr.permission_pending = False\n                            if wr.commit_pending:\n                                self.commit_write_request(wr, needs_flush=False)\n                        else:\n                            w = get_boss().window_id_map.get(wid)\n                            if w is not None:\n                                w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='EPERM'))\n                        self.in_flight_write_request = None\n\n                    self.request_permission(w, wr.human_name, wr.password, callback, for_write=True)\n            else:\n                self.in_flight_write_request = None\n                w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='EPERM'))\n\n    def request_permission(self, window: WindowType, human_name: str, password: str, callback: Callable[[bool], None], for_write: bool = False) -> None:\n        if (gp := self.granted_passwords.get(password)) and (gp.write_ban if for_write else gp.read_ban):\n            callback(False)\n            return\n\n        def cb(q: str) -> None:\n            p = self.granted_passwords.get(password)\n            if p is None:\n                p = self.granted_passwords[password] = GrantedPermission()\n            callback(q in ('a', 'w'))\n            match q:\n                case 'w':\n                    if for_write:\n                        p.write = True\n                    else:\n                        p.read = True\n                case 'b':\n                    if for_write:\n                        p.write = False\n                        p.write_ban = True\n                    else:\n                        p.read = False\n                        p.read_ban = True\n        if for_write:\n            msg = _('The program {0} running in this window wants to write to the system clipboard.')\n        else:\n            msg = _('The program {0} running in this window wants to read from the system clipboard.')\n        msg += '\\n\\n' + ('If you choose \"Always\" similar requests from this program will be automatically allowed for the rest of this session.')\n        msg += '\\n\\n' + ('If you choose \"Ban\" similar requests from this program will be automatically dis-allowed for the rest of this session.')\n        from kittens.tui.operations import styled\n        get_boss().choose(msg.format(styled(human_name, fg='yellow')), cb, 'a;green:Allow', 'w;yellow:Always', 'd;red:Deny', 'b;red:Ban',\n                          default='d', window=window, title=_('A program wants to access the clipboard'))\n\n    def password_is_allowed_already(self, password: str, for_write: bool = False) -> bool:\n        q = self.granted_passwords.get(password)\n        if q is not None:\n            if q.one_time:\n                self.granted_passwords.pop(password, None)\n            return q.write if for_write else q.read\n        return False\n\n    def fulfill_legacy_write_request(self, wr: WriteRequest, allowed: bool = True) -> None:\n        cp = get_boss().primary_selection if wr.is_primary_selection else get_boss().clipboard\n        w = get_boss().window_id_map.get(self.window_id)\n        if w is not None and cp.enabled and allowed:\n            wr.commit()\n\n    def handle_read_request(self, rr: ReadRequest) -> None:\n        cc = get_options().clipboard_control\n        if rr.is_primary_selection:\n            ask_for_permission = 'read-primary-ask' in cc\n            allowed = 'read-primary' in cc\n        else:\n            ask_for_permission = 'read-clipboard-ask' in cc\n            allowed = 'read-clipboard' in cc\n        if ask_for_permission:\n            self.ask_to_read_clipboard(rr)\n        else:\n            self.fulfill_read_request(rr, allowed=allowed)\n\n    def send_paste_event(self, is_primary_selection: bool) -> None:\n        from kitty.short_uuid import uuid4\n        pw = uuid4()\n        self.granted_passwords[pw] = GrantedPermission(read=True, one_time=True)\n        rr = ReadRequest(is_primary_selection=is_primary_selection, mime_types=(TARGETS_MIME,), protocol_type=ProtocolType.osc_5522)\n        rr = rr._replace(otp_for_response=pw)\n        self.fulfill_read_request(rr)\n\n    def fulfill_read_request(self, rr: ReadRequest, allowed: bool = True) -> None:\n        if rr.protocol_type is ProtocolType.osc_52:\n            return self.fulfill_legacy_read_request(rr, allowed)\n        w = get_boss().window_id_map.get(self.window_id)\n        if w is None:\n            return\n        cp = get_boss().primary_selection if rr.is_primary_selection else get_boss().clipboard\n        if not cp.enabled:\n            w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='ENOSYS'))\n            return\n        if not allowed:\n            w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='EPERM'))\n            return\n        w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='OK'))\n\n        current_mime = ''\n\n        def write_chunks(data: bytes) -> None:\n            assert w is not None\n            mv = memoryview(data)\n            while mv:\n                w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(payload=mv[:READ_RESPONSE_CHUNK_SIZE], mime=current_mime))\n                mv = mv[READ_RESPONSE_CHUNK_SIZE:]\n\n        for mime in rr.mime_types:\n            current_mime = mime\n            if mime == TARGETS_MIME:\n                payload = ' '.join(cp.get_available_mime_types_for_paste()).encode('utf-8')\n                if payload:\n                    payload += b'\\n'\n                w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(payload=payload, mime=current_mime))\n                continue\n            try:\n                cp.get_mime(mime, write_chunks)\n            except Exception as e:\n                log_error(f'Failed to read requested mime type {mime} with error: {e}')\n        w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='DONE'))\n\n    def reject_read_request(self, rr: ReadRequest) -> None:\n        if rr.protocol_type is ProtocolType.osc_52:\n            return self.fulfill_legacy_read_request(rr, False)\n        w = get_boss().window_id_map.get(self.window_id)\n        if w is not None:\n            w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='EPERM'))\n\n    def fulfill_legacy_read_request(self, rr: ReadRequest, allowed: bool = True) -> None:\n        cp = get_boss().primary_selection if rr.is_primary_selection else get_boss().clipboard\n        w = get_boss().window_id_map.get(self.window_id)\n        if w is not None:\n            text = ''\n            if cp.enabled and allowed:\n                text = cp.get_text()\n            loc = 'p' if rr.is_primary_selection else 'c'\n            w.screen.send_escape_code_to_child(ESC_OSC, encode_osc52(loc, text))\n\n    def ask_to_read_clipboard(self, rr: ReadRequest) -> None:\n        if rr.mime_types == (TARGETS_MIME,):\n            self.fulfill_read_request(rr, True)\n            return\n        if self.currently_asking_permission_for is not None:\n            self.reject_read_request(rr)\n            return\n        w = get_boss().window_id_map.get(self.window_id)\n        if w is not None:\n            self.currently_asking_permission_for = rr\n            if rr.password and rr.human_name:\n                if self.password_is_allowed_already(rr.password):\n                    self.handle_clipboard_confirmation(True)\n                    return\n                if (p := self.granted_passwords.get(rr.password)) and p.read_ban:\n                    self.handle_clipboard_confirmation(False)\n                    return\n                self.request_permission(w, rr.human_name, rr.password, self.handle_clipboard_confirmation)\n            else:\n                if rr.human_name:\n                    msg = _(\n                    'The program {} running in this window wants to read from the system clipboard.'\n                    ' Allow it to do so, once?').format(rr.human_name)\n                else:\n                    msg = _(\n                    'A program running in this window wants to read from the system clipboard.'\n                    ' Allow it to do so, once?')\n                get_boss().confirm(msg, self.handle_clipboard_confirmation, window=w)\n\n    def handle_clipboard_confirmation(self, confirmed: bool) -> None:\n        rr = self.currently_asking_permission_for\n        self.currently_asking_permission_for = None\n        if rr is not None:\n            self.fulfill_read_request(rr, confirmed)\n\n    def close(self) -> None:\n        if self.in_flight_write_request is not None:\n            self.in_flight_write_request = None\n        self.osc52_in_flight_write_requests.clear()\n"
  },
  {
    "path": "kitty/cocoa_window.h",
    "content": "/*\n * cocoa_window.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\ntypedef enum {\n    PREFERENCES_WINDOW,\n    NEW_OS_WINDOW,\n    NEW_OS_WINDOW_WITH_WD,\n    NEW_TAB_WITH_WD,\n    CLOSE_OS_WINDOW,\n    CLOSE_TAB,\n    NEW_TAB,\n    NEXT_TAB,\n    PREVIOUS_TAB,\n    DETACH_TAB,\n    LAUNCH_URLS,\n    NEW_WINDOW,\n    CLOSE_WINDOW,\n    RESET_TERMINAL,\n    CLEAR_TERMINAL_AND_SCROLLBACK,\n    CLEAR_SCROLLBACK,\n    CLEAR_SCREEN,\n    CLEAR_LAST_COMMAND,\n    RELOAD_CONFIG,\n    TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY,\n    MACOS_CYCLE_THROUGH_OS_WINDOWS,\n    MACOS_CYCLE_THROUGH_OS_WINDOWS_BACKWARDS,\n    SEARCH_SCROLLBACK,\n    TOGGLE_FULLSCREEN,\n    OPEN_KITTY_WEBSITE,\n    HIDE,\n    HIDE_OTHERS,\n    MINIMIZE,\n    QUIT,\n    USER_MENU_ACTION,\n    COCOA_NOTIFICATION_UNTRACKED,\n\n    NUM_COCOA_PENDING_ACTIONS\n} CocoaPendingAction;\n\nvoid cocoa_focus_window(void *w);\nlong cocoa_window_number(void *w);\nvoid cocoa_application_lifecycle_event(bool);\nvoid cocoa_recreate_global_menu(void);\nvoid cocoa_system_beep(const char*);\nvoid cocoa_set_activation_policy(bool);\nbool cocoa_alt_option_key_pressed(unsigned long);\nvoid cocoa_toggle_secure_keyboard_entry(void);\nvoid cocoa_hide(void);\nvoid cocoa_clear_global_shortcuts(void);\nvoid cocoa_hide_others(void);\nvoid cocoa_minimize(void *w);\nvoid cocoa_set_uncaught_exception_handler(void);\nvoid cocoa_update_menu_bar_title(PyObject*);\nsize_t cocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t array_sz);\nmonotonic_t cocoa_cursor_blink_interval(void);\nbool cocoa_render_line_of_text(const char *text, const color_type fg, const color_type bg, uint8_t *rgba_output, const size_t width, const size_t height);\nextern uint8_t* render_single_ascii_char_as_mask(const char ch, size_t *result_width, size_t *result_height);\nvoid get_cocoa_key_equivalent(uint32_t, int, char *key, size_t key_sz, int*);\nvoid set_cocoa_pending_action(CocoaPendingAction action, const char*);\nvoid cocoa_report_live_notifications(const char* ident);\nvoid cocoa_set_dock_badge(const char *label);\nvoid cocoa_clear_dock_badge_if_set(void);\n"
  },
  {
    "path": "kitty/cocoa_window.m",
    "content": "/*\n * cocoa_window.m\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n\n#include \"state.h\"\n#include \"cleanup.h\"\n#include \"cocoa_window.h\"\n#include <Availability.h>\n#include <Carbon/Carbon.h>\n#include <Cocoa/Cocoa.h>\n#include <UserNotifications/UserNotifications.h>\n#import <AudioToolbox/AudioServices.h>\n\n#include <AvailabilityMacros.h>\n// Needed for _NSGetProgname\n#include <crt_externs.h>\n#include <objc/runtime.h>\n\nstatic inline void cleanup_cfrelease(void *__p) { CFTypeRef *tp = (CFTypeRef *)__p; CFTypeRef cf = *tp; if (cf) { CFRelease(cf); } }\n#define RAII_CoreFoundation(type, name, initializer) __attribute__((cleanup(cleanup_cfrelease))) type name = initializer\n\n#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101300)\n#define NSControlStateValueOn NSOnState\n#define NSControlStateValueOff NSOffState\n#define NSControlStateValueMixed NSMixedState\n#endif\n#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101200)\n#define NSWindowStyleMaskResizable NSResizableWindowMask\n#define NSEventModifierFlagOption NSAlternateKeyMask\n#define NSEventModifierFlagCommand NSCommandKeyMask\n#define NSEventModifierFlagControl NSControlKeyMask\n#endif\n#if (MAC_OS_X_VERSION_MAX_ALLOWED < 110000)\n#define UNNotificationPresentationOptionList (1 << 3)\n#define UNNotificationPresentationOptionBanner (1 << 4)\n#endif\n\ntypedef int CGSConnectionID;\ntypedef int CGSWindowID;\ntypedef int CGSWorkspaceID;\ntypedef enum _CGSSpaceSelector {\n    kCGSSpaceCurrent = 5,\n    kCGSSpaceAll = 7\n} CGSSpaceSelector;\nextern CGSConnectionID _CGSDefaultConnection(void);\nCFArrayRef CGSCopySpacesForWindows(CGSConnectionID Connection, CGSSpaceSelector Type, CFArrayRef Windows);\n\nstatic NSMenuItem* title_menu = NULL;\nstatic bool application_has_finished_launching = false;\n\n\nstatic NSString*\nfind_app_name(void) {\n    size_t i;\n    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];\n\n    // Keys to search for as potential application names\n    NSString* name_keys[] =\n    {\n        @\"CFBundleDisplayName\",\n        @\"CFBundleName\",\n        @\"CFBundleExecutable\",\n    };\n\n    for (i = 0;  i < sizeof(name_keys) / sizeof(name_keys[0]);  i++)\n    {\n        id name = infoDictionary[name_keys[i]];\n        if (name &&\n            [name isKindOfClass:[NSString class]] &&\n            ![name isEqualToString:@\"\"])\n        {\n            return name;\n        }\n    }\n\n    char** progname = _NSGetProgname();\n    if (progname && *progname)\n        return @(*progname);\n\n    // Really shouldn't get here\n    return @\"kitty\";\n}\n\n#define debug_key(...) if (OPT(debug_keyboard)) { fprintf(stderr, __VA_ARGS__); fflush(stderr); }\n\n// SecureKeyboardEntryController {{{\n@interface SecureKeyboardEntryController : NSObject\n\n@property (nonatomic, readonly) BOOL isDesired;\n@property (nonatomic, readonly, getter=isEnabled) BOOL enabled;\n\n+ (instancetype)sharedInstance;\n\n- (void)toggle;\n- (void)update;\n\n@end\n\n@implementation SecureKeyboardEntryController {\n    int _count;\n    BOOL _desired;\n}\n\n+ (instancetype)sharedInstance {\n    static id instance;\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        instance = [[self alloc] init];\n    });\n    return instance;\n}\n\n- (instancetype)init {\n    self = [super init];\n    if (self) {\n        _desired = false;\n\n        [[NSNotificationCenter defaultCenter] addObserver:self\n                                                 selector:@selector(applicationDidResignActive:)\n                                                     name:NSApplicationDidResignActiveNotification\n                                                   object:nil];\n        [[NSNotificationCenter defaultCenter] addObserver:self\n                                                 selector:@selector(applicationDidBecomeActive:)\n                                                     name:NSApplicationDidBecomeActiveNotification\n                                                   object:nil];\n        if ([NSApp isActive]) {\n            [self update];\n        }\n    }\n    return self;\n}\n\n#pragma mark - API\n\n- (void)toggle {\n    // Set _desired to the opposite of the current state.\n    _desired = !_desired;\n    debug_key(\"SecureKeyboardEntry: toggle called. Setting desired to %d \", _desired);\n\n    // Try to set the system's state of secure input to the desired state.\n    [self update];\n}\n\n- (BOOL)isEnabled {\n    return !!IsSecureEventInputEnabled();\n}\n\n- (BOOL)isDesired {\n    return _desired;\n}\n\n#pragma mark - Notifications\n\n- (void)applicationDidResignActive:(NSNotification *)notification {\n    (void)notification;\n    if (_count > 0) {\n        debug_key(\"SecureKeyboardEntry: Application resigning active.\");\n        [self update];\n    }\n}\n\n- (void)applicationDidBecomeActive:(NSNotification *)notification {\n    (void)notification;\n    if (self.isDesired) {\n        debug_key(\"SecureKeyboardEntry: Application became active.\");\n        [self update];\n    }\n}\n\n#pragma mark - Private\n\n- (BOOL)allowed {\n    return [NSApp isActive];\n}\n\n- (void)update {\n    debug_key(\"Update secure keyboard entry. desired=%d active=%d\\n\",\n         (int)self.isDesired, (int)[NSApp isActive]);\n    const BOOL secure = self.isDesired && [self allowed];\n\n    if (secure && _count > 0) {\n        debug_key(\"Want to turn on secure input but it's already on\\n\");\n        return;\n    }\n\n    if (!secure && _count == 0) {\n        debug_key(\"Want to turn off secure input but it's already off\\n\");\n        return;\n    }\n\n    debug_key(\"Before: IsSecureEventInputEnabled returns %d \", (int)self.isEnabled);\n    if (secure) {\n        OSErr err = EnableSecureEventInput();\n        debug_key(\"EnableSecureEventInput err=%d \", (int)err);\n        if (err) {\n            debug_key(\"EnableSecureEventInput failed with error %d \", (int)err);\n        } else {\n            _count += 1;\n        }\n    } else {\n        OSErr err = DisableSecureEventInput();\n        debug_key(\"DisableSecureEventInput err=%d \", (int)err);\n        if (err) {\n            debug_key(\"DisableSecureEventInput failed with error %d \", (int)err);\n        } else {\n            _count -= 1;\n        }\n    }\n    debug_key(\"After: IsSecureEventInputEnabled returns %d\\n\", (int)self.isEnabled);\n}\n\n@end\n// }}}\n\n@interface UserMenuItem : NSMenuItem\n@property (nonatomic) size_t action_index;\n@end\n\n@implementation UserMenuItem {\n}\n@end\n\n\n\n@interface GlobalMenuTarget : NSObject\n+ (GlobalMenuTarget *) shared_instance;\n@end\n\n#define PENDING(selector, which) - (void)selector:(id)sender { (void)sender; set_cocoa_pending_action(which, NULL); }\n\n@implementation GlobalMenuTarget\n\n- (void)user_menu_action:(id)sender {\n    UserMenuItem *m = sender;\n    if (m.action_index < OPT(global_menu).count && OPT(global_menu.entries)) {\n        set_cocoa_pending_action(USER_MENU_ACTION, OPT(global_menu).entries[m.action_index].definition);\n    }\n}\n\nPENDING(edit_config_file, PREFERENCES_WINDOW)\nPENDING(new_os_window, NEW_OS_WINDOW)\nPENDING(detach_tab, DETACH_TAB)\nPENDING(close_os_window, CLOSE_OS_WINDOW)\nPENDING(close_tab, CLOSE_TAB)\nPENDING(new_tab, NEW_TAB)\nPENDING(next_tab, NEXT_TAB)\nPENDING(previous_tab, PREVIOUS_TAB)\nPENDING(new_window, NEW_WINDOW)\nPENDING(close_window, CLOSE_WINDOW)\nPENDING(reset_terminal, RESET_TERMINAL)\nPENDING(clear_terminal_and_scrollback, CLEAR_TERMINAL_AND_SCROLLBACK)\nPENDING(clear_scrollback, CLEAR_SCROLLBACK)\nPENDING(clear_screen, CLEAR_SCREEN)\nPENDING(clear_last_command, CLEAR_LAST_COMMAND)\nPENDING(reload_config, RELOAD_CONFIG)\nPENDING(toggle_macos_secure_keyboard_entry, TOGGLE_MACOS_SECURE_KEYBOARD_ENTRY)\nPENDING(macos_cycle_through_os_windows, MACOS_CYCLE_THROUGH_OS_WINDOWS)\nPENDING(macos_cycle_through_os_windows_backwards, MACOS_CYCLE_THROUGH_OS_WINDOWS_BACKWARDS)\nPENDING(search_scrollback, SEARCH_SCROLLBACK)\nPENDING(toggle_fullscreen, TOGGLE_FULLSCREEN)\nPENDING(open_kitty_website, OPEN_KITTY_WEBSITE)\nPENDING(hide_macos_app, HIDE)\nPENDING(hide_macos_other_apps, HIDE_OTHERS)\nPENDING(minimize_macos_window, MINIMIZE)\nPENDING(quit, QUIT)\n\n- (BOOL)validateMenuItem:(NSMenuItem *)item {\n    if (item.action == @selector(toggle_macos_secure_keyboard_entry:)) {\n        item.state = [SecureKeyboardEntryController sharedInstance].isDesired ? NSControlStateValueOn : NSControlStateValueOff;\n    } else if (item.action == @selector(toggle_fullscreen:)) {\n        item.title = ([NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen) ? @\"Exit Full Screen\" : @\"Enter Full Screen\";\n        if (![NSApp keyWindow]) return NO;\n    } else if (item.action == @selector(minimize_macos_window:)) {\n        NSWindow *window = [NSApp keyWindow];\n        if (!window || window.miniaturized || [NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen) return NO;\n    } else if (item.action == @selector(close_os_window:) ||\n        item.action == @selector(close_tab:) ||\n        item.action == @selector(close_window:) ||\n        item.action == @selector(reset_terminal:) ||\n        item.action == @selector(clear_terminal_and_scrollback:) ||\n        item.action == @selector(clear_last_command:) ||\n        item.action == @selector(clear_scrollback:) ||\n        item.action == @selector(clear_screen:) ||\n        item.action == @selector(previous_tab:) ||\n        item.action == @selector(next_tab:) ||\n        item.action == @selector(detach_tab:))\n    {\n        if (![NSApp keyWindow]) return NO;\n    }\n    return YES;\n}\n\n#undef PENDING\n\n+ (GlobalMenuTarget *) shared_instance\n{\n    static GlobalMenuTarget *sharedGlobalMenuTarget = nil;\n    @synchronized(self)\n    {\n        if (!sharedGlobalMenuTarget) {\n            sharedGlobalMenuTarget = [[GlobalMenuTarget alloc] init];\n            SecureKeyboardEntryController *k = [SecureKeyboardEntryController sharedInstance];\n            if (!k.isDesired && [[NSUserDefaults standardUserDefaults] boolForKey:@\"SecureKeyboardEntry\"]) [k toggle];\n        }\n        return sharedGlobalMenuTarget;\n    }\n}\n\n@end\n\ntypedef struct {\n    char key[32];\n    NSEventModifierFlags mods;\n} GlobalShortcut;\ntypedef struct {\n    GlobalShortcut new_os_window, close_os_window, close_tab, edit_config_file, reload_config;\n    GlobalShortcut previous_tab, next_tab, new_tab, new_window, close_window, reset_terminal;\n    GlobalShortcut clear_terminal_and_scrollback, clear_screen, clear_scrollback, clear_last_command;\n    GlobalShortcut toggle_macos_secure_keyboard_entry, toggle_fullscreen, open_kitty_website;\n    GlobalShortcut hide_macos_app, hide_macos_other_apps, minimize_macos_window, quit, search_scrollback;\n    GlobalShortcut macos_cycle_through_os_windows, macos_cycle_through_os_windows_backwards;\n} GlobalShortcuts;\nstatic GlobalShortcuts global_shortcuts;\n\nstatic PyObject*\ncocoa_set_global_shortcut(PyObject *self UNUSED, PyObject *args) {\n    int mods;\n    unsigned int key;\n    const char *name;\n    if (!PyArg_ParseTuple(args, \"siI\", &name, &mods, &key)) return NULL;\n    GlobalShortcut *gs = NULL;\n#define Q(x) if (strcmp(name, #x) == 0) gs = &global_shortcuts.x\n    Q(new_os_window); else Q(close_os_window); else Q(close_tab); else Q(edit_config_file);\n    else Q(new_tab); else Q(next_tab); else Q(previous_tab);\n    else Q(new_window); else Q(close_window); else Q(reset_terminal);\n    else Q(clear_terminal_and_scrollback); else Q(clear_scrollback); else Q(clear_screen); else Q(clear_last_command);\n    else Q(reload_config); else Q(toggle_macos_secure_keyboard_entry); else Q(toggle_fullscreen);\n    else Q(open_kitty_website); else Q(hide_macos_app); else Q(hide_macos_other_apps);\n    else Q(minimize_macos_window); else Q(quit); else Q(search_scrollback);\n    else Q(macos_cycle_through_os_windows); else Q(macos_cycle_through_os_windows_backwards);\n#undef Q\n    if (gs == NULL) { PyErr_SetString(PyExc_KeyError, \"Unknown shortcut name\"); return NULL; }\n    int cocoa_mods;\n    get_cocoa_key_equivalent(key, mods, gs->key, 32, &cocoa_mods);\n    gs->mods = cocoa_mods;\n    if (gs->key[0]) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\n// Implementation of applicationDockMenu: for the app delegate\nstatic NSMenu *dockMenu = nil;\nstatic NSMenu *\nget_dock_menu(id self UNUSED, SEL _cmd UNUSED, NSApplication *sender UNUSED) {\n    if (!dockMenu) {\n        GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];\n        dockMenu = [[NSMenu alloc] init];\n        [[dockMenu addItemWithTitle:@\"New OS Window\"\n                             action:@selector(new_os_window:)\n                      keyEquivalent:@\"\"]\n                          setTarget:global_menu_target];\n    }\n    return dockMenu;\n}\n\nstatic PyObject *notification_activated_callback = NULL;\n\nstatic PyObject*\nset_notification_activated_callback(PyObject *self UNUSED, PyObject *callback) {\n    Py_CLEAR(notification_activated_callback);\n    if (callback != Py_None) notification_activated_callback = Py_NewRef(callback);\n    Py_RETURN_NONE;\n}\n\nstatic void\ndo_notification_callback(const char *identifier, const char *event, const char *action_identifer) {\n    if (notification_activated_callback) {\n        PyObject *ret = PyObject_CallFunction(notification_activated_callback, \"sss\", event,\n                identifier ? identifier : \"\", action_identifer ? action_identifer : \"\");\n        if (ret) Py_DECREF(ret);\n        else PyErr_Print();\n    }\n}\n\n\n@interface NotificationDelegate : NSObject <UNUserNotificationCenterDelegate>\n@end\n\n@implementation NotificationDelegate\n    - (void)userNotificationCenter:(UNUserNotificationCenter *)center\n            willPresentNotification:(UNNotification *)notification\n            withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {\n        (void)(center); (void)notification;\n        UNNotificationPresentationOptions options = UNNotificationPresentationOptionSound;\n        if (@available(macOS 11.0, *)) options |= UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner;\n        else options |= (1 << 2); // UNNotificationPresentationOptionAlert avoid deprecated warning\n        completionHandler(options);\n    }\n\n    - (void)userNotificationCenter:(UNUserNotificationCenter *)center\n            didReceiveNotificationResponse:(UNNotificationResponse *)response\n            withCompletionHandler:(void (^)(void))completionHandler {\n        (void)(center);\n        char *identifier = strdup(response.notification.request.identifier.UTF8String);\n        char *action_identifier = strdup(response.actionIdentifier.UTF8String);\n        const char *event = \"button\";\n        if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) {\n            event = \"activated\";\n        } else if ([response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {\n            // Crapple never actually sends this event on macOS\n            event = \"closed\";\n        }\n        dispatch_async(dispatch_get_main_queue(), ^{\n            do_notification_callback(identifier, event, action_identifier);\n            free(identifier); free(action_identifier);\n        });\n        completionHandler();\n    }\n@end\n\nstatic UNUserNotificationCenter*\nget_notification_center_safely(void) {\n    NSBundle *b = [NSBundle mainBundle];\n    // when bundleIdentifier is nil currentNotificationCenter crashes instead\n    // of returning nil. Apple...purveyor of shiny TOYS\n    if (!b || !b.bundleIdentifier) return nil;\n    UNUserNotificationCenter *center = nil;\n    @try {\n        center = [UNUserNotificationCenter currentNotificationCenter];\n    } @catch (NSException *e) {\n        log_error(\"Failed to get current UNUserNotificationCenter object with error: %s (%s)\",\n                            [[e name] UTF8String], [[e reason] UTF8String]);\n    }\n    return center;\n}\n\nstatic bool\nident_in_list_of_notifications(NSString *ident, NSArray<UNNotification*> *list) {\n    for (UNNotification *n in list) {\n        if ([[[n request] identifier] isEqualToString:ident]) return true;\n    }\n    return false;\n}\n\nvoid\ncocoa_report_live_notifications(const char* ident) {\n    do_notification_callback(ident, \"live\", ident ? ident : \"\");\n}\n\nstatic bool\nremove_delivered_notification(const char *identifier) {\n    UNUserNotificationCenter *center = get_notification_center_safely();\n    if (!center) return false;\n    char *ident = strdup(identifier);\n    [center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * notifications) {\n        if (ident_in_list_of_notifications(@(ident), notifications)) {\n            [center removeDeliveredNotificationsWithIdentifiers:@[ @(ident) ]];\n        }\n        free(ident);\n    }];\n    return true;\n}\n\nstatic bool\nlive_delivered_notifications(void) {\n    UNUserNotificationCenter *center = get_notification_center_safely();\n    if (!center) return false;\n    [center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * notifications) {\n        @autoreleasepool {\n            NSMutableString *buffer = [NSMutableString stringWithCapacity:1024];  // autoreleased\n            for (UNNotification *n in notifications) [buffer appendFormat:@\"%@,\", [[n request] identifier]];\n            const char *val = [buffer UTF8String];\n            set_cocoa_pending_action(COCOA_NOTIFICATION_UNTRACKED, val ? val : \"\");\n        }\n    }];\n    return true;\n}\n\nstatic void\nschedule_notification(const char *appname, const char *identifier, const char *title, const char *body, const char *image_path, int urgency, const char *category_id, bool muted) {@autoreleasepool {\n    UNUserNotificationCenter *center = get_notification_center_safely();\n    if (!center) return;\n    // Configure the notification's payload.\n    UNMutableNotificationContent *content = [[[UNMutableNotificationContent alloc] init] autorelease];\n    if (title) content.title = @(title);\n    if (body) content.body = @(body);\n    if (appname) content.threadIdentifier = @(appname);\n    if (category_id) content.categoryIdentifier = @(category_id);\n    if (!muted) content.sound = [UNNotificationSound defaultSound];\n#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 120000\n    switch (urgency) {\n        case 0:\n            content.interruptionLevel = UNNotificationInterruptionLevelPassive;\n        case 2:\n            content.interruptionLevel = UNNotificationInterruptionLevelCritical;\n        default:\n            content.interruptionLevel = UNNotificationInterruptionLevelActive;\n    }\n#else\n    if ([content respondsToSelector:@selector(interruptionLevel)]) {\n        NSUInteger level = 1;\n        if (urgency == 0) level = 0; else if (urgency == 2) level = 3;\n        [content setValue:@(level) forKey:@\"interruptionLevel\"];\n    }\n#endif\n    if (image_path) {\n        @try {\n            NSError *error;\n            NSURL *image_url = [NSURL fileURLWithFileSystemRepresentation:image_path isDirectory:NO relativeToURL:nil];  // autoreleased\n            UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@\"image\" URL:image_url options:nil error:&error];  // autoreleased\n            if (attachment) { content.attachments = @[ attachment ]; }\n            else NSLog(@\"Error attaching image %@ to notification: %@\", @(image_path), error.localizedDescription);\n        } @catch(NSException *exc) {\n            NSLog(@\"Creating image attachment %@ for notification failed with error: %@\", @(image_path), exc.reason);\n        }\n    }\n\n    // Deliver the notification\n    static unsigned long counter = 1;\n    UNNotificationRequest* request = [\n        UNNotificationRequest requestWithIdentifier:(identifier ? @(identifier) : [NSString stringWithFormat:@\"Id_%lu\", counter++])\n        content:content trigger:nil];\n    char *duped_ident = strdup(identifier ? identifier : \"\");\n    [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {\n        if (error != nil) log_error(\"Failed to show notification: %s\", [[error localizedDescription] UTF8String]);\n        bool ok = error == nil;\n        dispatch_async(dispatch_get_main_queue(), ^{\n            do_notification_callback(duped_ident, ok ? \"created\" : \"creation_failed\", \"\");\n            free(duped_ident);\n        });\n    }];\n}}\n\n\ntypedef struct {\n    char *identifier, *title, *body, *appname, *image_path, *category_id;\n    int urgency; bool muted;\n} QueuedNotification;\n\ntypedef struct {\n    QueuedNotification *notifications;\n    size_t count, capacity;\n} NotificationQueue;\nstatic NotificationQueue notification_queue = {0};\n\nstatic void\nqueue_notification(const char *appname, const char *identifier, const char *title, const char* body, const char *image_path, int urgency, const char *category_id, bool muted) {\n    ensure_space_for((&notification_queue), notifications, QueuedNotification, notification_queue.count + 16, capacity, 16, true);\n    QueuedNotification *n = notification_queue.notifications + notification_queue.count++;\n#define d(x) n->x = (x && x[0]) ? strdup(x) : NULL;\n    d(appname); d(identifier); d(title); d(body); d(image_path); d(category_id);\n#undef d\n    n->urgency = urgency; n->muted = muted;\n}\n\nstatic void\ndrain_pending_notifications(BOOL granted) {\n    if (granted) {\n        for (size_t i = 0; i < notification_queue.count; i++) {\n            QueuedNotification *n = notification_queue.notifications + i;\n            schedule_notification(n->appname, n->identifier, n->title, n->body, n->image_path, n->urgency, n->category_id, n->muted);\n        }\n    }\n    while(notification_queue.count) {\n        QueuedNotification *n = notification_queue.notifications + --notification_queue.count;\n        if (!granted) do_notification_callback(n->identifier, \"creation_failed\", \"\");\n        free(n->identifier); free(n->title); free(n->body); free(n->appname); free(n->image_path); free(n->category_id);\n        memset(n, 0, sizeof(QueuedNotification));\n    }\n}\n\nstatic PyObject*\ncocoa_remove_delivered_notification(PyObject *self UNUSED, PyObject *x) {\n    if (!PyUnicode_Check(x)) { PyErr_SetString(PyExc_TypeError, \"identifier must be a string\"); return NULL; }\n    if (remove_delivered_notification(PyUnicode_AsUTF8(x))) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\ncocoa_live_delivered_notifications(PyObject *self UNUSED, PyObject *x UNUSED) {\n    if (live_delivered_notifications()) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic UNNotificationCategory*\ncategory_from_python(PyObject *p) {\n    RAII_PyObject(button_ids, PyObject_GetAttrString(p, \"button_ids\"));\n    RAII_PyObject(buttons, PyObject_GetAttrString(p, \"buttons\"));\n    RAII_PyObject(id, PyObject_GetAttrString(p, \"id\"));\n    NSMutableArray<UNNotificationAction *> *actions = [NSMutableArray arrayWithCapacity:PyTuple_GET_SIZE(buttons)];\n    for (int i = 0; i < PyTuple_GET_SIZE(buttons); i++) [actions addObject:\n        [UNNotificationAction actionWithIdentifier:@(PyUnicode_AsUTF8(PyTuple_GET_ITEM(button_ids, i)))\n            title:@(PyUnicode_AsUTF8(PyTuple_GET_ITEM(buttons, i))) options:UNNotificationActionOptionNone]];\n\n    return [UNNotificationCategory categoryWithIdentifier:@(PyUnicode_AsUTF8(id))\n        actions:actions intentIdentifiers:@[] options:0];\n}\n\nstatic bool\nset_notification_categories(UNUserNotificationCenter *center, PyObject *categories) {\n    NSMutableArray<UNNotificationCategory *> *ans = [NSMutableArray arrayWithCapacity:PyTuple_GET_SIZE(categories)];\n    for (int i = 0; i < PyTuple_GET_SIZE(categories); i++) {\n        UNNotificationCategory *c = category_from_python(PyTuple_GET_ITEM(categories, i));\n        if (!c) return false;\n        [ans addObject:c];\n    }\n    [center setNotificationCategories:[NSSet setWithArray:ans]];\n    return true;\n}\n\nstatic PyObject*\ncocoa_send_notification(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    const char *identifier = \"\", *title = \"\", *body = \"\", *appname = \"\", *image_path = \"\"; int urgency = 1;\n    PyObject *category, *categories; int muted = 0;\n    static const char* kwlist[] = {\"appname\", \"identifier\", \"title\", \"body\", \"category\", \"categories\", \"image_path\", \"urgency\", \"muted\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"ssssOO!|sip\", (char**)kwlist,\n        &appname, &identifier, &title, &body, &category, &PyTuple_Type, &categories, &image_path, &urgency, &muted)) return NULL;\n\n    UNUserNotificationCenter *center = get_notification_center_safely();\n    if (!center) Py_RETURN_NONE;\n    if (!center.delegate) center.delegate = [[NotificationDelegate alloc] init];\n    if (PyObject_IsTrue(categories)) if (!set_notification_categories(center, categories)) return NULL;\n    RAII_PyObject(category_id, PyObject_GetAttrString(category, \"id\"));\n    queue_notification(appname, identifier, title, body, image_path, urgency, PyUnicode_AsUTF8(category_id), muted);\n\n    // The badge permission needs to be requested as well, even though it is not used,\n    // otherwise macOS refuses to show the preference checkbox for enable/disable notification sound.\n    [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge)\n        completionHandler:^(BOOL granted, NSError * _Nullable error) {\n            if (!granted && error != nil) {\n                log_error(\"Failed to request permission for showing notification: %s\", [[error localizedDescription] UTF8String]);\n            }\n            dispatch_async(dispatch_get_main_queue(), ^{\n                drain_pending_notifications(granted);\n            });\n        }\n    ];\n    Py_RETURN_NONE;\n}\n\n@interface ServiceProvider : NSObject\n@end\n\n@implementation ServiceProvider\n\n- (BOOL)openTab:(NSPasteboard*)pasteboard\n        userData:(NSString *) UNUSED userData error:(NSError **) UNUSED error {\n    return [self openDirsFromPasteboard:pasteboard type:NEW_TAB_WITH_WD];\n}\n\n- (BOOL)openOSWindow:(NSPasteboard*)pasteboard\n        userData:(NSString *) UNUSED userData  error:(NSError **) UNUSED error {\n    return [self openDirsFromPasteboard:pasteboard type:NEW_OS_WINDOW_WITH_WD];\n}\n\n- (BOOL)openDirsFromPasteboard:(NSPasteboard *)pasteboard type:(int)type {\n    NSDictionary *options = @{ NSPasteboardURLReadingFileURLsOnlyKey: @YES };\n    NSArray *filePathArray = [pasteboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:options];\n    NSMutableArray<NSString*> *dirPathArray = [NSMutableArray arrayWithCapacity:[filePathArray count]];\n    for (NSURL *url in filePathArray) {\n        NSString *path = [url path];\n        BOOL isDirectory = NO;\n        if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) {\n            if (!isDirectory) path = [path stringByDeletingLastPathComponent];\n            if (![dirPathArray containsObject:path]) [dirPathArray addObject:path];\n        }\n    }\n    if ([dirPathArray count] > 0) {\n        // Colons are not valid in paths under macOS.\n        set_cocoa_pending_action(type, [[dirPathArray componentsJoinedByString:@\":\"] UTF8String]);\n    }\n    return YES;\n}\n\n- (BOOL)openFileURLs:(NSPasteboard*)pasteboard\n        userData:(NSString *) UNUSED userData  error:(NSError **) UNUSED error {\n    NSDictionary *options = @{ NSPasteboardURLReadingFileURLsOnlyKey: @YES };\n    NSArray *urlArray = [pasteboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:options];\n    for (NSURL *url in urlArray) {\n        NSString *path = [url path];\n        if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {\n            set_cocoa_pending_action(LAUNCH_URLS, [[[NSURL fileURLWithPath:path] absoluteString] UTF8String]);\n        }\n    }\n    return YES;\n}\n\n- (void)quickAccessTerminal:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error {\n    // we ignore event during application launch as it will cause the window to be shown and hidden\n    static bool is_first_event = true;\n    if (!is_first_event || monotonic() >= s_double_to_monotonic_t(2.0)) { call_boss(quick_access_terminal_invoked, NULL); }\n    is_first_event = false;\n}\n@end\n\n// global menu {{{\n\nstatic void\nadd_user_global_menu_entry(struct MenuItem *e, NSMenu *bar, size_t action_index) {\n    NSMenu *parent = bar;\n    UserMenuItem *final_item = nil;\n    GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];\n    for (size_t i = 0; i < e->location_count; i++) {\n        NSMenuItem *item = [parent itemWithTitle:@(e->location[i])];\n        if (!item) {\n            final_item = [[UserMenuItem alloc] initWithTitle:@(e->location[i]) action:@selector(user_menu_action:) keyEquivalent:@\"\"];\n            final_item.target = global_menu_target;\n            [parent addItem:final_item];\n            item = final_item;\n            [final_item release];\n        }\n        if (i + 1 < e->location_count) {\n            if (![item hasSubmenu]) {\n                NSMenu* sub_menu = [[NSMenu alloc] initWithTitle:item.title];\n                [item setSubmenu:sub_menu];\n                [sub_menu release];\n            }\n            parent = [item submenu];\n            if (!parent) return;\n        }\n    }\n    if (final_item != nil) {\n        final_item.action_index = action_index;\n    }\n}\n\nstatic void\ncocoa_create_global_menu(void) {\n    NSString* app_name = find_app_name();\n    NSMenu* bar = [[NSMenu alloc] init];\n    GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];\n    [NSApp setMainMenu:bar];\n\n#define MENU_ITEM(menu, title, name) { \\\n    NSMenuItem *__mi = [menu addItemWithTitle:title action:@selector(name:) keyEquivalent:@(global_shortcuts.name.key)]; \\\n    [__mi setKeyEquivalentModifierMask:global_shortcuts.name.mods]; \\\n    [__mi setTarget:global_menu_target]; \\\n}\n\n    NSMenuItem* appMenuItem =\n        [bar addItemWithTitle:@\"\"\n                       action:NULL\n                keyEquivalent:@\"\"];\n    NSMenu* appMenu = [[NSMenu alloc] init];\n    [appMenuItem setSubmenu:appMenu];\n\n    [appMenu addItemWithTitle:[NSString stringWithFormat:@\"About %@\", app_name]\n                       action:@selector(orderFrontStandardAboutPanel:)\n                keyEquivalent:@\"\"];\n    [appMenu addItem:[NSMenuItem separatorItem]];\n    MENU_ITEM(appMenu, @\"Preferences…\", edit_config_file);\n    MENU_ITEM(appMenu, @\"Reload Preferences\", reload_config);\n    [appMenu addItem:[NSMenuItem separatorItem]];\n\n    NSMenu* servicesMenu = [[NSMenu alloc] init];\n    [NSApp setServicesMenu:servicesMenu];\n    [[appMenu addItemWithTitle:@\"Services\"\n                        action:NULL\n                 keyEquivalent:@\"\"] setSubmenu:servicesMenu];\n    [servicesMenu release];\n    [appMenu addItem:[NSMenuItem separatorItem]];\n\n    MENU_ITEM(appMenu, ([NSString stringWithFormat:@\"Hide %@\", app_name]), hide_macos_app);\n    MENU_ITEM(appMenu, @\"Hide Others\", hide_macos_other_apps);\n    [appMenu addItemWithTitle:@\"Show All\"\n                       action:@selector(unhideAllApplications:)\n                keyEquivalent:@\"\"];\n    [appMenu addItem:[NSMenuItem separatorItem]];\n\n    MENU_ITEM(appMenu, @\"Secure Keyboard Entry\", toggle_macos_secure_keyboard_entry);\n    [appMenu addItem:[NSMenuItem separatorItem]];\n\n    MENU_ITEM(appMenu, ([NSString stringWithFormat:@\"Quit %@\", app_name]), quit);\n    [appMenu release];\n\n    NSMenuItem* shellMenuItem = [bar addItemWithTitle:@\"Shell\" action:NULL keyEquivalent:@\"\"];\n    NSMenu* shellMenu = [[NSMenu alloc] initWithTitle:@\"Shell\"];\n    [shellMenuItem setSubmenu:shellMenu];\n    MENU_ITEM(shellMenu, @\"New OS Window\", new_os_window);\n    MENU_ITEM(shellMenu, @\"New Tab\", new_tab);\n    MENU_ITEM(shellMenu, @\"New Window\", new_window);\n    [shellMenu addItem:[NSMenuItem separatorItem]];\n    MENU_ITEM(shellMenu, @\"Close OS Window\", close_os_window);\n    MENU_ITEM(shellMenu, @\"Close Tab\", close_tab);\n    MENU_ITEM(shellMenu, @\"Close Window\", close_window);\n    [shellMenu addItem:[NSMenuItem separatorItem]];\n    MENU_ITEM(shellMenu, @\"Reset\", reset_terminal);\n    [shellMenu release];\n    NSMenuItem* editMenuItem = [bar addItemWithTitle:@\"Edit\" action:NULL keyEquivalent:@\"\"];\n    NSMenu* editMenu = [[NSMenu alloc] initWithTitle:@\"Edit\"];\n    [editMenuItem setSubmenu:editMenu];\n    MENU_ITEM(editMenu, @\"Clear to Start\", clear_terminal_and_scrollback);\n    MENU_ITEM(editMenu, @\"Clear Scrollback\", clear_scrollback);\n    MENU_ITEM(editMenu, @\"Clear Screen\", clear_screen);\n    MENU_ITEM(editMenu, @\"Clear Last Command\", clear_last_command);\n    MENU_ITEM(editMenu, @\"Find\", search_scrollback);\n    [editMenu release];\n\n    NSMenuItem* windowMenuItem =\n        [bar addItemWithTitle:@\"Window\"\n                       action:NULL\n                keyEquivalent:@\"\"];\n    NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@\"Window\"];\n    [windowMenuItem setSubmenu:windowMenu];\n\n    MENU_ITEM(windowMenu, @\"Minimize\", minimize_macos_window);\n    [windowMenu addItemWithTitle:@\"Zoom\"\n                          action:@selector(performZoom:)\n                   keyEquivalent:@\"\"];\n    [windowMenu addItem:[NSMenuItem separatorItem]];\n    MENU_ITEM(windowMenu, @\"Cycle Through OS Windows\", macos_cycle_through_os_windows);\n    MENU_ITEM(windowMenu, @\"Cycle Through OS Windows backwards\", macos_cycle_through_os_windows_backwards);\n    [windowMenu addItem:[NSMenuItem separatorItem]];\n    [windowMenu addItemWithTitle:@\"Bring All to Front\"\n                          action:@selector(arrangeInFront:)\n                   keyEquivalent:@\"\"];\n\n    [windowMenu addItem:[NSMenuItem separatorItem]];\n    MENU_ITEM(windowMenu, @\"Show Previous Tab\", previous_tab);\n    MENU_ITEM(windowMenu, @\"Show Next Tab\", next_tab);\n    [[windowMenu addItemWithTitle:@\"Move Tab to New Window\"\n                           action:@selector(detach_tab:)\n                    keyEquivalent:@\"\"] setTarget:global_menu_target];\n\n    [windowMenu addItem:[NSMenuItem separatorItem]];\n    MENU_ITEM(windowMenu, @\"Enter Full Screen\", toggle_fullscreen);\n    [NSApp setWindowsMenu:windowMenu];\n    [windowMenu release];\n\n    NSMenuItem* helpMenuItem =\n        [bar addItemWithTitle:@\"Help\"\n                       action:NULL\n                keyEquivalent:@\"\"];\n    NSMenu* helpMenu = [[NSMenu alloc] initWithTitle:@\"Help\"];\n    [helpMenuItem setSubmenu:helpMenu];\n\n    MENU_ITEM(helpMenu, @\"Visit kitty Website\", open_kitty_website);\n    [NSApp setHelpMenu:helpMenu];\n    [helpMenu release];\n\n    if (OPT(global_menu.entries)) {\n        for (size_t i = 0; i < OPT(global_menu.count); i++) {\n            struct MenuItem *e = OPT(global_menu.entries) + i;\n            if (e->definition && e->location && e->location_count > 1) {\n                add_user_global_menu_entry(e, bar, i);\n            }\n        }\n    }\n    [bar release];\n\n\n    class_addMethod(\n        object_getClass([NSApp delegate]),\n        @selector(applicationDockMenu:),\n        (IMP)get_dock_menu,\n        \"@@:@\");\n\n\n    [NSApp setServicesProvider:[[[ServiceProvider alloc] init] autorelease]];\n\n#undef MENU_ITEM\n}\n\nvoid\ncocoa_application_lifecycle_event(bool application_launch_finished) {\n    if (application_launch_finished) {  // applicationDidFinishLaunching\n        application_has_finished_launching = true;\n    } else cocoa_create_global_menu();  // applicationWillFinishLaunching\n}\n\nvoid\ncocoa_update_menu_bar_title(PyObject *pytitle) {\n    if (!pytitle) return;\n    NSString *title = nil;\n    if (OPT(macos_menubar_title_max_length) > 0 && PyUnicode_GetLength(pytitle) > OPT(macos_menubar_title_max_length)) {\n        static char fmt[64];\n        snprintf(fmt, sizeof(fmt), \"%%%ld.%ldU%%s\", OPT(macos_menubar_title_max_length), OPT(macos_menubar_title_max_length));\n        RAII_PyObject(st, PyUnicode_FromFormat(fmt, pytitle, \"…\"));\n        if (st) title = @(PyUnicode_AsUTF8(st));\n        else PyErr_Print();\n    } else {\n        title = @(PyUnicode_AsUTF8(pytitle));\n    }\n    if (!title) return;\n    NSString *menuTitle = [NSString stringWithFormat:@\" :: %@\", title];\n    if (title_menu != NULL) {\n        [[title_menu submenu] setTitle:menuTitle];\n    } else {\n        NSMenu *bar = [NSApp mainMenu];\n        title_menu = [bar addItemWithTitle:@\"\" action:NULL keyEquivalent:@\"\"];\n        NSMenu *m = [[NSMenu alloc] initWithTitle:menuTitle];\n        [title_menu setSubmenu:m];\n        [m release];\n    }\n}\n\nvoid\ncocoa_clear_global_shortcuts(void) {\n    memset(&global_shortcuts, 0, sizeof(global_shortcuts));\n}\n\nvoid\ncocoa_recreate_global_menu(void) {\n    if (title_menu != NULL) {\n        NSMenu *bar = [NSApp mainMenu];\n        [bar removeItem:title_menu];\n    }\n    title_menu = NULL;\n    cocoa_create_global_menu();\n}\n\n\n// }}}\n\n#define NSLeftAlternateKeyMask  (0x000020 | NSEventModifierFlagOption)\n#define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)\n\nbool\ncocoa_alt_option_key_pressed(NSUInteger flags) {\n    NSUInteger q = (OPT(macos_option_as_alt) == 1) ? NSRightAlternateKeyMask : NSLeftAlternateKeyMask;\n    return (q & flags) == q;\n}\n\nvoid\ncocoa_toggle_secure_keyboard_entry(void) {\n    SecureKeyboardEntryController *k = [SecureKeyboardEntryController sharedInstance];\n    [k toggle];\n    [[NSUserDefaults standardUserDefaults] setBool:k.isDesired forKey:@\"SecureKeyboardEntry\"];\n}\n\nvoid\ncocoa_hide(void) {\n    [[NSApplication sharedApplication] performSelectorOnMainThread:@selector(hide:) withObject:nil waitUntilDone:NO];\n}\n\nvoid\ncocoa_hide_others(void) {\n    [[NSApplication sharedApplication] performSelectorOnMainThread:@selector(hideOtherApplications:) withObject:nil waitUntilDone:NO];\n}\n\nvoid\ncocoa_minimize(void *w) {\n    NSWindow *window = (NSWindow*)w;\n    if (window && !window.miniaturized) [window performSelectorOnMainThread:@selector(performMiniaturize:) withObject:nil waitUntilDone:NO];\n}\n\nvoid\ncocoa_focus_window(void *w) {\n    NSWindow *window = (NSWindow*)w;\n    [window makeKeyWindow];\n}\n\nlong\ncocoa_window_number(void *w) {\n    NSWindow *window = (NSWindow*)w;\n    return [window windowNumber];\n}\n\nsize_t\ncocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t array_sz) {\n    NSWindow *window = (NSWindow*)w;\n    if (!window) return 0;\n    NSArray *window_array = @[ @([window windowNumber]) ];\n    CFArrayRef spaces = CGSCopySpacesForWindows(_CGSDefaultConnection(), kCGSSpaceAll, (__bridge CFArrayRef)window_array);\n    CFIndex ans = CFArrayGetCount(spaces);\n    if (ans > 0) {\n        for (CFIndex i = 0; i < MIN(ans, (CFIndex)array_sz); i++) {\n            NSNumber *s = (NSNumber*)CFArrayGetValueAtIndex(spaces, i);\n            workspace_ids[i] = [s intValue];\n        }\n    } else ans = 0;\n    CFRelease(spaces);\n    return ans;\n}\n\nstatic PyObject*\ncocoa_get_lang(PyObject UNUSED *self, PyObject *args UNUSED) {\n    @autoreleasepool {\n    NSString* lang_code = [[NSLocale currentLocale] languageCode];\n    NSString* country_code = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];\n    NSString* identifier = [[NSLocale currentLocale] localeIdentifier];\n    return Py_BuildValue(\"sss\", lang_code ? [lang_code UTF8String]:\"\", country_code ? [country_code UTF8String] : \"\", identifier ? [identifier UTF8String]: \"\");\n    } // autoreleasepool\n}\n\nmonotonic_t\ncocoa_cursor_blink_interval(void) {\n    @autoreleasepool {\n\n    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];\n    double on_period_ms = [defaults doubleForKey:@\"NSTextInsertionPointBlinkPeriodOn\"];\n    double off_period_ms = [defaults doubleForKey:@\"NSTextInsertionPointBlinkPeriodOff\"];\n    double period_ms = [defaults doubleForKey:@\"NSTextInsertionPointBlinkPeriod\"];\n    double max_value = 60 * 1000.0, ans = -1.0;\n    if (on_period_ms != 0. || off_period_ms != 0.) {\n        ans = on_period_ms + off_period_ms;\n    } else if (period_ms != 0.) {\n        ans = period_ms;\n    }\n    return ans > max_value ? 0ll : ms_double_to_monotonic_t(ans);\n\n    } // autoreleasepool\n}\n\nvoid\ncocoa_set_activation_policy(bool hide_from_tasks) {\n    [NSApp setActivationPolicy:(hide_from_tasks ? NSApplicationActivationPolicyAccessory : NSApplicationActivationPolicyRegular)];\n}\n\nstatic PyObject*\ncocoa_set_url_handler(PyObject UNUSED *self, PyObject *args) {\n    @autoreleasepool {\n\n    const char *url_scheme = NULL, *bundle_id = NULL;\n    if (!PyArg_ParseTuple(args, \"s|z\", &url_scheme, &bundle_id)) return NULL;\n    if (!url_scheme || url_scheme[0] == '\\0') {\n        PyErr_SetString(PyExc_TypeError, \"Empty url scheme\");\n        return NULL;\n    }\n\n    NSString *scheme = [NSString stringWithUTF8String:url_scheme];\n    NSString *identifier = @\"\";\n    if (!bundle_id) {\n        identifier = [[NSBundle mainBundle] bundleIdentifier];\n        if (!identifier || identifier.length == 0) identifier = @\"net.kovidgoyal.kitty\";\n    } else if (bundle_id[0] != '\\0') {\n        identifier = [NSString stringWithUTF8String:bundle_id];\n    }\n    // This API has been marked as deprecated. It will need to be replaced when a new approach is available.\n    OSStatus err = LSSetDefaultHandlerForURLScheme((CFStringRef)scheme, (CFStringRef)identifier);\n    if (err == noErr) Py_RETURN_NONE;\n    PyErr_Format(PyExc_OSError, \"Failed to set default handler with error code: %d\", err);\n    return NULL;\n    } // autoreleasepool\n}\n\nstatic PyObject*\ncocoa_set_app_icon(PyObject UNUSED *self, PyObject *args) {\n    @autoreleasepool {\n\n    const char *icon_path = NULL, *app_path = NULL;\n    if (!PyArg_ParseTuple(args, \"s|z\", &icon_path, &app_path)) return NULL;\n    if (!icon_path || icon_path[0] == '\\0') {\n        PyErr_SetString(PyExc_TypeError, \"Empty icon file path\");\n        return NULL;\n    }\n    NSString *custom_icon_path = [NSString stringWithUTF8String:icon_path];\n    if (![[NSFileManager defaultManager] fileExistsAtPath:custom_icon_path]) {\n        PyErr_Format(PyExc_FileNotFoundError, \"Icon file not found: %s\", [custom_icon_path UTF8String]);\n        return NULL;\n    }\n\n    NSString *bundle_path = @\"\";\n    if (!app_path) {\n        bundle_path = [[NSBundle mainBundle] bundlePath];\n        if (!bundle_path || bundle_path.length == 0) bundle_path = @\"/Applications/kitty.app\";\n        // When compiled from source and run from the launcher folder the bundle path should be `kitty.app` in it\n        if (![bundle_path hasSuffix:@\".app\"]) {\n            NSString *launcher_app_path = [bundle_path stringByAppendingPathComponent:@\"kitty.app\"];\n            bundle_path = @\"\";\n            BOOL is_dir;\n            if ([[NSFileManager defaultManager] fileExistsAtPath:launcher_app_path isDirectory:&is_dir] && is_dir && [[NSWorkspace sharedWorkspace] isFilePackageAtPath:launcher_app_path]) {\n                bundle_path = launcher_app_path;\n            }\n        }\n    } else if (app_path[0] != '\\0') {\n        bundle_path = [NSString stringWithUTF8String:app_path];\n    }\n    if (!bundle_path || bundle_path.length == 0 || ![[NSFileManager defaultManager] fileExistsAtPath:bundle_path]) {\n        PyErr_Format(PyExc_FileNotFoundError, \"Application bundle not found: %s\", [bundle_path UTF8String]);\n        return NULL;\n    }\n\n    NSImage *icon_image = [[NSImage alloc] initWithContentsOfFile:custom_icon_path];\n    BOOL result = [[NSWorkspace sharedWorkspace] setIcon:icon_image forFile:bundle_path options:NSExcludeQuickDrawElementsIconCreationOption];\n    [icon_image release];\n    if (result) Py_RETURN_NONE;\n    PyErr_Format(PyExc_OSError, \"Failed to set custom icon %s for %s\", [custom_icon_path UTF8String], [bundle_path UTF8String]);\n    return NULL;\n\n    } // autoreleasepool\n}\n\nstatic PyObject*\ncocoa_set_dock_icon(PyObject UNUSED *self, PyObject *args) {\n    @autoreleasepool {\n\n    const char *icon_path = NULL;\n    if (!PyArg_ParseTuple(args, \"s\", &icon_path)) return NULL;\n    if (!icon_path || icon_path[0] == '\\0') {\n        PyErr_SetString(PyExc_TypeError, \"Empty icon file path\");\n        return NULL;\n    }\n    NSString *custom_icon_path = [NSString stringWithUTF8String:icon_path];\n    if ([[NSFileManager defaultManager] fileExistsAtPath:custom_icon_path]) {\n        NSImage *icon_image = [[[NSImage alloc] initWithContentsOfFile:custom_icon_path] autorelease];\n        [NSApplication sharedApplication].applicationIconImage = icon_image;\n        Py_RETURN_NONE;\n    }\n    return NULL;\n\n    } // autoreleasepool\n}\n\nstatic NSSound *beep_sound = nil;\n\nstatic void\ncleanup(void) {\n    @autoreleasepool {\n\n    if (dockMenu) [dockMenu release];\n    dockMenu = nil;\n    if (beep_sound) [beep_sound release];\n    beep_sound = nil;\n\n    drain_pending_notifications(NO);\n    free(notification_queue.notifications);\n    notification_queue.notifications = NULL;\n    notification_queue.capacity = 0;\n\n    } // autoreleasepool\n}\n\nvoid\ncocoa_system_beep(const char *path) {\n    if (!path) { NSBeep(); return; }\n    static const char *beep_path = NULL;\n    if (beep_path != path) {\n        if (beep_sound) [beep_sound release];\n        beep_sound = [[NSSound alloc] initWithContentsOfFile:@(path) byReference:YES];\n    }\n    if (beep_sound) [beep_sound play];\n    else NSBeep();\n}\n\nstatic void\nuncaughtExceptionHandler(NSException *exception) {\n    log_error(\"Unhandled exception in Cocoa: %s\", [[exception description] UTF8String]);\n    log_error(\"Stack trace:\\n%s\", [[exception.callStackSymbols description] UTF8String]);\n}\n\nvoid\ncocoa_set_uncaught_exception_handler(void) {\n    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);\n}\n\nstatic PyObject*\nconvert_imagerep_to_png(NSBitmapImageRep *rep, const char *output_path) {\n    NSData *png = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{NSImageCompressionFactor: @1.0}]; // autoreleased\n\n    if (output_path) {\n        if (![png writeToFile:@(output_path) atomically:YES]) {\n            PyErr_Format(PyExc_OSError, \"Failed to write PNG data to %s\", output_path);\n            return NULL;\n        }\n        return PyBytes_FromStringAndSize(NULL, 0);\n    }\n    return PyBytes_FromStringAndSize(png.bytes, png.length);\n}\n\nstatic PyObject*\nconvert_image_to_png(NSImage *icon, unsigned image_size, const char *output_path) {\n    NSRect r = NSMakeRect(0, 0, image_size, image_size);\n    RAII_CoreFoundation(CGColorSpaceRef, colorSpace, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));\n    RAII_CoreFoundation(CGContextRef, cgContext, CGBitmapContextCreate(NULL, image_size, image_size, 8, 4*image_size, colorSpace, kCGBitmapByteOrderDefault|kCGImageAlphaPremultipliedLast));\n    NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithCGContext:cgContext flipped:NO];  // autoreleased\n    CGImageRef cg = [icon CGImageForProposedRect:&r context:context hints:nil];\n    NSBitmapImageRep *rep = [[[NSBitmapImageRep alloc] initWithCGImage:cg] autorelease];\n    return convert_imagerep_to_png(rep, output_path);\n}\n\nstatic PyObject*\nrender_emoji(NSString *text, unsigned image_size, const char *output_path) {\n    NSFont *font = [NSFont fontWithName:@\"AppleColorEmoji\" size:12];\n    CTFontRef ctfont = (__bridge CTFontRef)(font);\n    CGFloat line_height = MAX(1, floor(CTFontGetAscent(ctfont) + CTFontGetDescent(ctfont) + MAX(0, CTFontGetLeading(ctfont)) + 0.5));\n    CGFloat pts_per_px = CTFontGetSize(ctfont) / line_height;\n    CGFloat desired_size = image_size * pts_per_px;\n    NSFont *final_font = [NSFont fontWithName:@\"AppleColorEmoji\" size:desired_size];\n    NSAttributedString *attr_string = [[[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: final_font}] autorelease];\n    NSBitmapImageRep *bmp = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil pixelsWide:image_size pixelsHigh:image_size bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:0] autorelease];\n    [NSGraphicsContext saveGraphicsState];\n    NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:bmp];\n    [NSGraphicsContext setCurrentContext:context];\n    [attr_string drawInRect:NSMakeRect(0, 0, image_size, image_size)];\n    [NSGraphicsContext restoreGraphicsState];\n    return convert_imagerep_to_png(bmp, output_path);\n}\n\n\nstatic PyObject*\nbundle_image_as_png(PyObject *self UNUSED, PyObject *args, PyObject *kw) {@autoreleasepool {\n    const char *b, *output_path = NULL; int image_type = 1; unsigned image_size = 256;\n    static const char* kwlist[] = {\"path_or_identifier\", \"output_path\", \"image_size\", \"image_type\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"s|sIi\", (char**)kwlist, &b, &output_path, &image_size, &image_type)) return NULL;\n    NSImage *icon = nil;\n    switch (image_type) {\n        case 0: case 1: {\n            NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; // autoreleased\n            if (image_type == 1) {\n                NSURL *url = [workspace URLForApplicationWithBundleIdentifier:@(b)]; // autoreleased\n                if (!url) {\n                    PyErr_Format(PyExc_KeyError, \"Failed to find bundle path for identifier: %s\", b); return NULL;\n                }\n                icon = [workspace iconForFile:@(url.fileSystemRepresentation)];\n            } else icon = [workspace iconForFile:@(b)];\n        } break;\n        case 2:\n            return render_emoji(@(b), image_size, output_path);\n        default:\n            if (@available(macOS 11.0, *)) {\n                icon = [NSImage imageWithSystemSymbolName:@(b) accessibilityDescription:@\"\"];  // autoreleased\n            } else {\n                PyErr_SetString(PyExc_ValueError, \"Your version of macOS is too old to use symbol images, need >= 11.0\"); return NULL;\n            }\n            break;\n    }\n    if (!icon) {\n        PyErr_Format(PyExc_ValueError, \"Failed to load icon for bundle: %s\", b); return NULL;\n    }\n    return convert_image_to_png(icon, image_size, output_path);\n}}\n\nstatic PyObject*\nplay_system_sound_by_id_async(PyObject *self UNUSED, PyObject *which) {\n    if (!PyLong_Check(which)) { PyErr_SetString(PyExc_TypeError, \"system sound id must be an integer\"); return NULL; }\n    AudioServicesPlaySystemSound(PyLong_AsUnsignedLong(which));\n    Py_RETURN_NONE;\n}\n\n// Dock Progress bar {{{\n@interface RoundedRectangleView : NSView {\n    unsigned intermediate_step;\n    CGFloat fill_fraction;\n    BOOL is_indeterminate;\n}\n- (void) animate;\n- (BOOL) isIndeterminate;\n- (void) setIndeterminate:(BOOL)val;\n- (void) setFraction:(CGFloat) fraction;\n@end\n\n@implementation RoundedRectangleView\n\n- (void) animate { intermediate_step++; }\n- (BOOL) isIndeterminate { return is_indeterminate; }\n- (void) setIndeterminate:(BOOL)val {\n    if (val != is_indeterminate) {\n        is_indeterminate = val;\n        intermediate_step = 0;\n        }\n    }\n- (void) setFraction:(CGFloat)fraction { fill_fraction = fraction; }\n\n\n- (void)drawRect:(NSRect)dirtyRect {\n    [super drawRect:dirtyRect];\n\n    NSRect bar = NSInsetRect(self.bounds, 4, 4);\n    CGFloat cornerRadius = self.bounds.size.height / 4.0;\n\n#define fill(bar) [[NSBezierPath bezierPathWithRoundedRect:bar xRadius:cornerRadius yRadius:cornerRadius] fill]\n    // Create the border\n    [[[NSColor whiteColor] colorWithAlphaComponent:0.8] setFill];\n    fill(bar);\n    // Create the background\n    [[[NSColor blackColor] colorWithAlphaComponent:0.8] setFill];\n    fill(NSInsetRect(bar, 0.5, 0.5));\n    // Create the progress\n    NSRect bar_progress = NSInsetRect(bar, 1, 1);\n    if (intermediate_step) {\n        unsigned num_of_steps = 80;\n        intermediate_step = intermediate_step % num_of_steps;\n        bar_progress.size.width = self.bounds.size.width / 8;\n        float frac = intermediate_step / (float)num_of_steps;\n        bar_progress.origin.x += (self.bounds.size.width - bar_progress.size.width) * frac;\n    } else bar_progress.size.width *= fill_fraction;\n    [[NSColor whiteColor] setFill];\n    fill(bar_progress);\n#undef fill\n}\n\n@end\nstatic NSView *dock_content_view = nil;\nstatic NSImageView *dock_image_view = nil;\nstatic RoundedRectangleView *dock_pbar = nil;\n\nstatic void\nanimate_dock_progress_bar(id_type timer_id UNUSED, void *data UNUSED);\n\nstatic void\ntick_dock_pbar(void) {\n    add_main_loop_timer(ms_to_monotonic_t(20), false, animate_dock_progress_bar, NULL, NULL);\n}\n\nstatic void\nanimate_dock_progress_bar(id_type timer_id UNUSED, void *data UNUSED) {\n    if (dock_pbar != nil && [dock_pbar isIndeterminate]) {\n        [dock_pbar animate];\n        NSDockTile *dockTile = [NSApp dockTile];\n        [dockTile display];\n        tick_dock_pbar();\n    }\n}\n\nstatic PyObject*\ncocoa_show_progress_bar_on_dock_icon(PyObject *self UNUSED, PyObject *args) {\n    float percent = -100;\n    if (!PyArg_ParseTuple(args, \"|f\", &percent)) return NULL;\n    NSDockTile *dockTile = [NSApp dockTile];\n    if (!dock_content_view) {\n        dock_content_view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, dockTile.size.width, dockTile.size.height)];\n        dock_image_view = [NSImageView.alloc initWithFrame:dock_content_view.frame];\n        dock_image_view.imageScaling = NSImageScaleProportionallyDown;\n        dock_image_view.image = NSApp.applicationIconImage;\n        [dock_content_view addSubview:dock_image_view];\n        dock_pbar = [[RoundedRectangleView alloc] initWithFrame:NSMakeRect(0, 0, dockTile.size.width, dockTile.size.height / 4)];\n        [dock_content_view addSubview:dock_pbar];\n    }\n    [dock_content_view setFrameSize:dockTile.size];\n    [dock_image_view setFrameSize:dockTile.size];\n    if (percent >= 0 && percent <= 100) {\n        [dock_pbar setFraction:percent/100.];\n        [dock_pbar setIndeterminate:NO];\n    } else if (percent > 100) {\n        if (![dock_pbar isIndeterminate]) {\n            [dock_pbar setIndeterminate:YES];\n            tick_dock_pbar();\n        }\n    }\n    [dock_pbar setFrameSize:NSMakeSize(dockTile.size.width - 20, 20)];\n    [dock_pbar setFrameOrigin:NSMakePoint(10, -2)];\n    [dockTile setContentView:percent < 0 ? nil : dock_content_view];\n    [dockTile display];\n    Py_RETURN_NONE;\n}\n// }}}\n\n// Dock badge {{{\n\nstatic bool dock_badge_is_set = false;\n\nvoid\ncocoa_set_dock_badge(const char *label) {\n    @autoreleasepool {\n        NSDockTile *dockTile = [NSApp dockTile];\n        [dockTile setBadgeLabel:label ? @(label) : nil];\n        [dockTile display];\n        dock_badge_is_set = (label != NULL);\n    }\n}\n\nvoid\ncocoa_clear_dock_badge_if_set(void) {\n    if (dock_badge_is_set) cocoa_set_dock_badge(NULL);\n}\n\n// }}}\n\nstatic PyMethodDef module_methods[] = {\n    {\"cocoa_play_system_sound_by_id_async\", play_system_sound_by_id_async, METH_O, \"\"},\n    {\"cocoa_get_lang\", (PyCFunction)cocoa_get_lang, METH_NOARGS, \"\"},\n    {\"cocoa_set_global_shortcut\", (PyCFunction)cocoa_set_global_shortcut, METH_VARARGS, \"\"},\n    {\"cocoa_send_notification\", (PyCFunction)(void(*)(void))cocoa_send_notification, METH_VARARGS | METH_KEYWORDS, \"\"},\n    {\"cocoa_remove_delivered_notification\", (PyCFunction)cocoa_remove_delivered_notification, METH_O, \"\"},\n    {\"cocoa_live_delivered_notifications\", (PyCFunction)cocoa_live_delivered_notifications, METH_NOARGS, \"\"},\n    {\"cocoa_set_notification_activated_callback\", (PyCFunction)set_notification_activated_callback, METH_O, \"\"},\n    {\"cocoa_set_url_handler\", (PyCFunction)cocoa_set_url_handler, METH_VARARGS, \"\"},\n    {\"cocoa_set_app_icon\", (PyCFunction)cocoa_set_app_icon, METH_VARARGS, \"\"},\n    {\"cocoa_set_dock_icon\", (PyCFunction)cocoa_set_dock_icon, METH_VARARGS, \"\"},\n    {\"cocoa_show_progress_bar_on_dock_icon\", (PyCFunction)cocoa_show_progress_bar_on_dock_icon, METH_VARARGS, \"\"},\n    {\"cocoa_bundle_image_as_png\", (PyCFunction)(void(*)(void))bundle_image_as_png, METH_VARARGS | METH_KEYWORDS, \"\"},\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nbool\ninit_cocoa(PyObject *module) {\n    cocoa_clear_global_shortcuts();\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    register_at_exit_cleanup_func(COCOA_CLEANUP_FUNC, cleanup);\n    [[NSNotificationCenter defaultCenter]\n        addObserverForName:NSApplicationDidBecomeActiveNotification\n        object:nil\n        queue:[NSOperationQueue mainQueue]\n        usingBlock:^(NSNotification *note UNUSED) {\n            cocoa_set_dock_badge(NULL);\n        }];\n    return true;\n}\n"
  },
  {
    "path": "kitty/color-names.h",
    "content": "/* ANSI-C code produced by gperf version 3.3 */\n/* Command-line: gperf -m 2000 --struct-type --includes --readonly-tables --lookup-function-name in_color_name_set --global-table --null-strings --hash-function-name color_name_hash --word-array-name color_names --pic --compare-strncmp /dev/stdin  */\n/* Computed positions: -k'1,3,5-9,12-15,$' */\n\n#if !((' ' == 32) && ('!' == 33) && ('\"' == 34) && ('#' == 35) \\\n      && ('%' == 37) && ('&' == 38) && ('\\'' == 39) && ('(' == 40) \\\n      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \\\n      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \\\n      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \\\n      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \\\n      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \\\n      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \\\n      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \\\n      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \\\n      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \\\n      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \\\n      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \\\n      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \\\n      && ('Z' == 90) && ('[' == 91) && ('\\\\' == 92) && (']' == 93) \\\n      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \\\n      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \\\n      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \\\n      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \\\n      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \\\n      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \\\n      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \\\n      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))\n/* The character set is not based on ISO-646.  */\n#error \"gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>.\"\n#endif\n\n#line 1 \"/dev/stdin\"\nstruct Keyword { int name, value; };\n#include <string.h>\n\n#define TOTAL_KEYWORDS 753\n#define MIN_WORD_LENGTH 3\n#define MAX_WORD_LENGTH 22\n#define MIN_HASH_VALUE 172\n#define MAX_HASH_VALUE 3478\n/* maximum key range = 3307, duplicates = 0 */\n\n#ifdef __GNUC__\n__inline\n#else\n#ifdef __cplusplus\ninline\n#endif\n#endif\nstatic unsigned int\ncolor_name_hash (register const char *str, register size_t len)\n{\n  static const unsigned short asso_values[] =\n    {\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479,  384, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,  689,   61,\n        60,   57,   56,  917,  884,  827,  824,  815, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479,   72,   68,  615,\n        56,   56,   92,   56,  375,  575,   56,  631,   86,  289,\n       101,   75,  202,  134,   57,   56,  191,  137,  987,  777,\n      3479,  239, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479, 3479,\n      3479, 3479, 3479, 3479, 3479, 3479\n    };\n  register unsigned int hval = len;\n\n  switch (hval)\n    {\n      default:\n        hval += asso_values[(unsigned char)str[14]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 14:\n        hval += asso_values[(unsigned char)str[13]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 13:\n        hval += asso_values[(unsigned char)str[12]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 12:\n        hval += asso_values[(unsigned char)str[11]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 11:\n      case 10:\n      case 9:\n        hval += asso_values[(unsigned char)str[8]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 8:\n        hval += asso_values[(unsigned char)str[7]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 7:\n        hval += asso_values[(unsigned char)str[6]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 6:\n        hval += asso_values[(unsigned char)str[5]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 5:\n        hval += asso_values[(unsigned char)str[4]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 4:\n      case 3:\n        hval += asso_values[(unsigned char)str[2]];\n#if (defined __cplusplus && (__cplusplus >= 201703L || (__cplusplus >= 201103L && defined __clang__ && __clang_major__ + (__clang_minor__ >= 9) > 3))) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000L && ((defined __GNUC__ && __GNUC__ >= 10) || (defined __clang__ && __clang_major__ >= 9)))\n      [[fallthrough]];\n#elif (defined __GNUC__ && __GNUC__ >= 7) || (defined __clang__ && __clang_major__ >= 10)\n      __attribute__ ((__fallthrough__));\n#endif\n      /*FALLTHROUGH*/\n      case 2:\n      case 1:\n        hval += asso_values[(unsigned char)str[0]];\n        break;\n    }\n  return hval + asso_values[(unsigned char)str[len - 1]];\n}\n\nstruct stringpool_t\n  {\n    char stringpool_str172[sizeof(\"red\")];\n    char stringpool_str173[sizeof(\"red4\")];\n    char stringpool_str174[sizeof(\"red3\")];\n    char stringpool_str177[sizeof(\"red2\")];\n    char stringpool_str178[sizeof(\"red1\")];\n    char stringpool_str202[sizeof(\"gold\")];\n    char stringpool_str229[sizeof(\"grey4\")];\n    char stringpool_str231[sizeof(\"grey3\")];\n    char stringpool_str237[sizeof(\"grey2\")];\n    char stringpool_str239[sizeof(\"grey1\")];\n    char stringpool_str245[sizeof(\"gray4\")];\n    char stringpool_str247[sizeof(\"gray3\")];\n    char stringpool_str248[sizeof(\"snow4\")];\n    char stringpool_str250[sizeof(\"snow3\")];\n    char stringpool_str253[sizeof(\"gray2\")];\n    char stringpool_str255[sizeof(\"gray1\")];\n    char stringpool_str256[sizeof(\"snow2\")];\n    char stringpool_str258[sizeof(\"snow1\")];\n    char stringpool_str259[sizeof(\"gold4\")];\n    char stringpool_str261[sizeof(\"gold3\")];\n    char stringpool_str265[sizeof(\"blue\")];\n    char stringpool_str267[sizeof(\"gold2\")];\n    char stringpool_str269[sizeof(\"gold1\")];\n    char stringpool_str286[sizeof(\"grey44\")];\n    char stringpool_str287[sizeof(\"grey34\")];\n    char stringpool_str288[sizeof(\"grey43\")];\n    char stringpool_str289[sizeof(\"grey33\")];\n    char stringpool_str290[sizeof(\"grey24\")];\n    char stringpool_str291[sizeof(\"grey14\")];\n    char stringpool_str292[sizeof(\"grey23\")];\n    char stringpool_str293[sizeof(\"grey13\")];\n    char stringpool_str294[sizeof(\"grey42\")];\n    char stringpool_str295[sizeof(\"grey32\")];\n    char stringpool_str296[sizeof(\"grey41\")];\n    char stringpool_str297[sizeof(\"grey31\")];\n    char stringpool_str298[sizeof(\"grey22\")];\n    char stringpool_str299[sizeof(\"grey12\")];\n    char stringpool_str300[sizeof(\"grey21\")];\n    char stringpool_str301[sizeof(\"grey11\")];\n    char stringpool_str302[sizeof(\"gray44\")];\n    char stringpool_str303[sizeof(\"gray34\")];\n    char stringpool_str304[sizeof(\"gray43\")];\n    char stringpool_str305[sizeof(\"gray33\")];\n    char stringpool_str306[sizeof(\"gray24\")];\n    char stringpool_str307[sizeof(\"gray14\")];\n    char stringpool_str308[sizeof(\"gray23\")];\n    char stringpool_str309[sizeof(\"gray13\")];\n    char stringpool_str310[sizeof(\"gray42\")];\n    char stringpool_str311[sizeof(\"gray32\")];\n    char stringpool_str312[sizeof(\"gray41\")];\n    char stringpool_str313[sizeof(\"gray31\")];\n    char stringpool_str314[sizeof(\"gray22\")];\n    char stringpool_str315[sizeof(\"gray12\")];\n    char stringpool_str316[sizeof(\"gray21\")];\n    char stringpool_str317[sizeof(\"gray11\")];\n    char stringpool_str319[sizeof(\"green\")];\n    char stringpool_str321[sizeof(\"orange\")];\n    char stringpool_str322[sizeof(\"blue4\")];\n    char stringpool_str324[sizeof(\"blue3\")];\n    char stringpool_str326[sizeof(\"azure\")];\n    char stringpool_str330[sizeof(\"blue2\")];\n    char stringpool_str331[sizeof(\"green4\")];\n    char stringpool_str332[sizeof(\"blue1\")];\n    char stringpool_str333[sizeof(\"green3\")];\n    char stringpool_str339[sizeof(\"green2\")];\n    char stringpool_str341[sizeof(\"green1\")];\n    char stringpool_str345[sizeof(\"darkred\")];\n    char stringpool_str350[sizeof(\"brown\")];\n    char stringpool_str352[sizeof(\"tan4\")];\n    char stringpool_str353[sizeof(\"tan3\")];\n    char stringpool_str355[sizeof(\"grey\")];\n    char stringpool_str356[sizeof(\"tan2\")];\n    char stringpool_str357[sizeof(\"tan1\")];\n    char stringpool_str362[sizeof(\"brown4\")];\n    char stringpool_str363[sizeof(\"sienna\")];\n    char stringpool_str364[sizeof(\"brown3\")];\n    char stringpool_str370[sizeof(\"brown2\")];\n    char stringpool_str371[sizeof(\"gray\")];\n    char stringpool_str372[sizeof(\"brown1\")];\n    char stringpool_str378[sizeof(\"orange4\")];\n    char stringpool_str379[sizeof(\"bisque\")];\n    char stringpool_str380[sizeof(\"orange3\")];\n    char stringpool_str383[sizeof(\"azure4\")];\n    char stringpool_str385[sizeof(\"azure3\")];\n    char stringpool_str386[sizeof(\"orange2\")];\n    char stringpool_str388[sizeof(\"orange1\")];\n    char stringpool_str391[sizeof(\"azure2\")];\n    char stringpool_str393[sizeof(\"azure1\")];\n    char stringpool_str394[sizeof(\"linen\")];\n    char stringpool_str396[sizeof(\"tan\")];\n    char stringpool_str400[sizeof(\"peru\")];\n    char stringpool_str404[sizeof(\"sienna4\")];\n    char stringpool_str406[sizeof(\"sienna3\")];\n    char stringpool_str412[sizeof(\"sienna2\")];\n    char stringpool_str414[sizeof(\"sienna1\")];\n    char stringpool_str420[sizeof(\"pink4\")];\n    char stringpool_str422[sizeof(\"pink3\")];\n    char stringpool_str425[sizeof(\"salmon\")];\n    char stringpool_str428[sizeof(\"pink2\")];\n    char stringpool_str430[sizeof(\"pink1\")];\n    char stringpool_str436[sizeof(\"bisque4\")];\n    char stringpool_str437[sizeof(\"salmon4\")];\n    char stringpool_str438[sizeof(\"bisque3\")];\n    char stringpool_str439[sizeof(\"salmon3\")];\n    char stringpool_str444[sizeof(\"bisque2\")];\n    char stringpool_str445[sizeof(\"salmon2\")];\n    char stringpool_str446[sizeof(\"bisque1\")];\n    char stringpool_str447[sizeof(\"salmon1\")];\n    char stringpool_str456[sizeof(\"plum4\")];\n    char stringpool_str458[sizeof(\"plum3\")];\n    char stringpool_str463[sizeof(\"purple\")];\n    char stringpool_str464[sizeof(\"plum2\")];\n    char stringpool_str466[sizeof(\"plum1\")];\n    char stringpool_str493[sizeof(\"orangered\")];\n    char stringpool_str494[sizeof(\"orangered4\")];\n    char stringpool_str495[sizeof(\"orangered3\")];\n    char stringpool_str498[sizeof(\"orangered2\")];\n    char stringpool_str499[sizeof(\"orangered1\")];\n    char stringpool_str507[sizeof(\"seagreen\")];\n    char stringpool_str519[sizeof(\"seagreen4\")];\n    char stringpool_str520[sizeof(\"purple4\")];\n    char stringpool_str521[sizeof(\"seagreen3\")];\n    char stringpool_str522[sizeof(\"purple3\")];\n    char stringpool_str524[sizeof(\"darkblue\")];\n    char stringpool_str527[sizeof(\"seagreen2\")];\n    char stringpool_str528[sizeof(\"purple2\")];\n    char stringpool_str529[sizeof(\"seagreen1\")];\n    char stringpool_str530[sizeof(\"purple1\")];\n    char stringpool_str531[sizeof(\"debianred\")];\n    char stringpool_str540[sizeof(\"darkorange\")];\n    char stringpool_str541[sizeof(\"darkorange4\")];\n    char stringpool_str542[sizeof(\"darkorange3\")];\n    char stringpool_str545[sizeof(\"darkorange2\")];\n    char stringpool_str546[sizeof(\"darkorange1\")];\n    char stringpool_str549[sizeof(\"darkgreen\")];\n    char stringpool_str551[sizeof(\"springgreen\")];\n    char stringpool_str552[sizeof(\"goldenrod\")];\n    char stringpool_str553[sizeof(\"goldenrod4\")];\n    char stringpool_str554[sizeof(\"goldenrod3\")];\n    char stringpool_str557[sizeof(\"goldenrod2\")];\n    char stringpool_str558[sizeof(\"goldenrod1\")];\n    char stringpool_str563[sizeof(\"springgreen4\")];\n    char stringpool_str564[sizeof(\"sea green\")];\n    char stringpool_str565[sizeof(\"springgreen3\")];\n    char stringpool_str566[sizeof(\"saddlebrown\")];\n    char stringpool_str571[sizeof(\"springgreen2\")];\n    char stringpool_str573[sizeof(\"springgreen1\")];\n    char stringpool_str582[sizeof(\"dodgerblue\")];\n    char stringpool_str583[sizeof(\"dodgerblue4\")];\n    char stringpool_str584[sizeof(\"dodgerblue3\")];\n    char stringpool_str587[sizeof(\"dodgerblue2\")];\n    char stringpool_str588[sizeof(\"dodgerblue1\")];\n    char stringpool_str596[sizeof(\"slateblue\")];\n    char stringpool_str597[sizeof(\"slateblue4\")];\n    char stringpool_str598[sizeof(\"slateblue3\")];\n    char stringpool_str601[sizeof(\"slateblue2\")];\n    char stringpool_str602[sizeof(\"slateblue1\")];\n    char stringpool_str610[sizeof(\"steelblue\")];\n    char stringpool_str611[sizeof(\"steelblue4\")];\n    char stringpool_str612[sizeof(\"steelblue3\")];\n    char stringpool_str615[sizeof(\"steelblue2\")];\n    char stringpool_str616[sizeof(\"steelblue1\")];\n    char stringpool_str624[sizeof(\"darkseagreen\")];\n    char stringpool_str629[sizeof(\"maroon\")];\n    char stringpool_str632[sizeof(\"plum\")];\n    char stringpool_str636[sizeof(\"darkseagreen4\")];\n    char stringpool_str637[sizeof(\"skyblue\")];\n    char stringpool_str638[sizeof(\"darkseagreen3\")];\n    char stringpool_str641[sizeof(\"maroon4\")];\n    char stringpool_str642[sizeof(\"darkgoldenrod\")];\n    char stringpool_str643[sizeof(\"maroon3\")];\n    char stringpool_str644[sizeof(\"darkseagreen2\")];\n    char stringpool_str646[sizeof(\"darkseagreen1\")];\n    char stringpool_str649[sizeof(\"maroon2\")];\n    char stringpool_str651[sizeof(\"maroon1\")];\n    char stringpool_str669[sizeof(\"lightgreen\")];\n    char stringpool_str674[sizeof(\"slategray4\")];\n    char stringpool_str675[sizeof(\"slategray3\")];\n    char stringpool_str677[sizeof(\"forestgreen\")];\n    char stringpool_str678[sizeof(\"slategray2\")];\n    char stringpool_str679[sizeof(\"slategray1\")];\n    char stringpool_str680[sizeof(\"palegreen4\")];\n    char stringpool_str681[sizeof(\"palegreen3\")];\n    char stringpool_str684[sizeof(\"palegreen2\")];\n    char stringpool_str685[sizeof(\"palegreen1\")];\n    char stringpool_str694[sizeof(\"skyblue4\")];\n    char stringpool_str696[sizeof(\"skyblue3\")];\n    char stringpool_str699[sizeof(\"darkgoldenrod4\")];\n    char stringpool_str701[sizeof(\"darkgoldenrod3\")];\n    char stringpool_str702[sizeof(\"skyblue2\")];\n    char stringpool_str704[sizeof(\"skyblue1\")];\n    char stringpool_str706[sizeof(\"sky blue\")];\n    char stringpool_str707[sizeof(\"darkgoldenrod2\")];\n    char stringpool_str709[sizeof(\"darkgoldenrod1\")];\n    char stringpool_str724[sizeof(\"palegreen\")];\n    char stringpool_str730[sizeof(\"dark red\")];\n    char stringpool_str745[sizeof(\"lightblue\")];\n    char stringpool_str746[sizeof(\"lightblue4\")];\n    char stringpool_str747[sizeof(\"lightblue3\")];\n    char stringpool_str750[sizeof(\"lightblue2\")];\n    char stringpool_str751[sizeof(\"lightblue1\")];\n    char stringpool_str760[sizeof(\"beige\")];\n    char stringpool_str768[sizeof(\"darkgrey\")];\n    char stringpool_str770[sizeof(\"darkmagenta\")];\n    char stringpool_str784[sizeof(\"darkgray\")];\n    char stringpool_str788[sizeof(\"magenta\")];\n    char stringpool_str792[sizeof(\"cyan\")];\n    char stringpool_str794[sizeof(\"royalblue\")];\n    char stringpool_str795[sizeof(\"royalblue4\")];\n    char stringpool_str796[sizeof(\"royalblue3\")];\n    char stringpool_str799[sizeof(\"royalblue2\")];\n    char stringpool_str800[sizeof(\"royalblue1\")];\n    char stringpool_str802[sizeof(\"darksalmon\")];\n    char stringpool_str804[sizeof(\"cyan4\")];\n    char stringpool_str806[sizeof(\"cyan3\")];\n    char stringpool_str811[sizeof(\"limegreen\")];\n    char stringpool_str812[sizeof(\"cyan2\")];\n    char stringpool_str814[sizeof(\"cyan1\")];\n    char stringpool_str817[sizeof(\"palegoldenrod\")];\n    char stringpool_str822[sizeof(\"orange red\")];\n    char stringpool_str825[sizeof(\"seashell\")];\n    char stringpool_str827[sizeof(\"tomato\")];\n    char stringpool_str829[sizeof(\"magenta4\")];\n    char stringpool_str830[sizeof(\"dodger blue\")];\n    char stringpool_str831[sizeof(\"magenta3\")];\n    char stringpool_str833[sizeof(\"dark green\")];\n    char stringpool_str836[sizeof(\"darkslateblue\")];\n    char stringpool_str837[sizeof(\"magenta2\")];\n    char stringpool_str839[sizeof(\"magenta1\")];\n    char stringpool_str840[sizeof(\"slategrey\")];\n    char stringpool_str844[sizeof(\"lightseagreen\")];\n    char stringpool_str849[sizeof(\"coral\")];\n    char stringpool_str852[sizeof(\"seashell4\")];\n    char stringpool_str854[sizeof(\"seashell3\")];\n    char stringpool_str856[sizeof(\"slategray\")];\n    char stringpool_str860[sizeof(\"seashell2\")];\n    char stringpool_str862[sizeof(\"seashell1\")];\n    char stringpool_str864[sizeof(\"lightgoldenrod\")];\n    char stringpool_str865[sizeof(\"tomato4\")];\n    char stringpool_str867[sizeof(\"tomato3\")];\n    char stringpool_str869[sizeof(\"dark orange\")];\n    char stringpool_str873[sizeof(\"tomato2\")];\n    char stringpool_str875[sizeof(\"tomato1\")];\n    char stringpool_str876[sizeof(\"coral4\")];\n    char stringpool_str878[sizeof(\"coral3\")];\n    char stringpool_str884[sizeof(\"coral2\")];\n    char stringpool_str886[sizeof(\"coral1\")];\n    char stringpool_str893[sizeof(\"mistyrose\")];\n    char stringpool_str894[sizeof(\"mistyrose4\")];\n    char stringpool_str895[sizeof(\"mistyrose3\")];\n    char stringpool_str898[sizeof(\"mistyrose2\")];\n    char stringpool_str899[sizeof(\"mistyrose1\")];\n    char stringpool_str909[sizeof(\"dark blue\")];\n    char stringpool_str912[sizeof(\"snow\")];\n    char stringpool_str921[sizeof(\"lightgoldenrod4\")];\n    char stringpool_str923[sizeof(\"lightgoldenrod3\")];\n    char stringpool_str924[sizeof(\"lightyellow4\")];\n    char stringpool_str925[sizeof(\"slate blue\")];\n    char stringpool_str926[sizeof(\"lightyellow3\")];\n    char stringpool_str929[sizeof(\"lightgoldenrod2\")];\n    char stringpool_str931[sizeof(\"lightgoldenrod1\")];\n    char stringpool_str932[sizeof(\"lightyellow2\")];\n    char stringpool_str934[sizeof(\"lightyellow1\")];\n    char stringpool_str937[sizeof(\"oldlace\")];\n    char stringpool_str938[sizeof(\"pink\")];\n    char stringpool_str939[sizeof(\"steel blue\")];\n    char stringpool_str943[sizeof(\"dimgrey\")];\n    char stringpool_str948[sizeof(\"lightsalmon\")];\n    char stringpool_str950[sizeof(\"darkturquoise\")];\n    char stringpool_str959[sizeof(\"dimgray\")];\n    char stringpool_str960[sizeof(\"lightsalmon4\")];\n    char stringpool_str962[sizeof(\"lightsalmon3\")];\n    char stringpool_str968[sizeof(\"lightsalmon2\")];\n    char stringpool_str970[sizeof(\"lightsalmon1\")];\n    char stringpool_str977[sizeof(\"saddle brown\")];\n    char stringpool_str981[sizeof(\"spring green\")];\n    char stringpool_str986[sizeof(\"slate grey\")];\n    char stringpool_str989[sizeof(\"lightgrey\")];\n    char stringpool_str998[sizeof(\"light green\")];\n    char stringpool_str1000[sizeof(\"dim grey\")];\n    char stringpool_str1002[sizeof(\"slate gray\")];\n    char stringpool_str1005[sizeof(\"lightgray\")];\n    char stringpool_str1007[sizeof(\"ivory4\")];\n    char stringpool_str1008[sizeof(\"pale green\")];\n    char stringpool_str1009[sizeof(\"ivory3\")];\n    char stringpool_str1011[sizeof(\"darkslategray4\")];\n    char stringpool_str1013[sizeof(\"darkslategray3\")];\n    char stringpool_str1015[sizeof(\"ivory2\")];\n    char stringpool_str1016[sizeof(\"dim gray\")];\n    char stringpool_str1017[sizeof(\"ivory1\")];\n    char stringpool_str1019[sizeof(\"darkslategray2\")];\n    char stringpool_str1021[sizeof(\"darkslategray1\")];\n    char stringpool_str1024[sizeof(\"old lace\")];\n    char stringpool_str1025[sizeof(\"olivedrab4\")];\n    char stringpool_str1026[sizeof(\"olivedrab3\")];\n    char stringpool_str1028[sizeof(\"dark goldenrod\")];\n    char stringpool_str1029[sizeof(\"olivedrab2\")];\n    char stringpool_str1030[sizeof(\"olivedrab1\")];\n    char stringpool_str1036[sizeof(\"olivedrab\")];\n    char stringpool_str1038[sizeof(\"indianred\")];\n    char stringpool_str1039[sizeof(\"indianred4\")];\n    char stringpool_str1040[sizeof(\"indianred3\")];\n    char stringpool_str1041[sizeof(\"lightsteelblue\")];\n    char stringpool_str1043[sizeof(\"indianred2\")];\n    char stringpool_str1044[sizeof(\"indianred1\")];\n    char stringpool_str1045[sizeof(\"grey94\")];\n    char stringpool_str1046[sizeof(\"gainsboro\")];\n    char stringpool_str1047[sizeof(\"grey93\")];\n    char stringpool_str1053[sizeof(\"grey92\")];\n    char stringpool_str1054[sizeof(\"grey84\")];\n    char stringpool_str1055[sizeof(\"grey91\")];\n    char stringpool_str1056[sizeof(\"grey83\")];\n    char stringpool_str1057[sizeof(\"grey74\")];\n    char stringpool_str1059[sizeof(\"grey73\")];\n    char stringpool_str1061[sizeof(\"gray94\")];\n    char stringpool_str1062[sizeof(\"grey82\")];\n    char stringpool_str1063[sizeof(\"gray93\")];\n    char stringpool_str1064[sizeof(\"grey81\")];\n    char stringpool_str1065[sizeof(\"grey72\")];\n    char stringpool_str1067[sizeof(\"grey71\")];\n    char stringpool_str1069[sizeof(\"gray92\")];\n    char stringpool_str1070[sizeof(\"gray84\")];\n    char stringpool_str1071[sizeof(\"gray91\")];\n    char stringpool_str1072[sizeof(\"gray83\")];\n    char stringpool_str1073[sizeof(\"gray74\")];\n    char stringpool_str1074[sizeof(\"light blue\")];\n    char stringpool_str1075[sizeof(\"gray73\")];\n    char stringpool_str1078[sizeof(\"gray82\")];\n    char stringpool_str1080[sizeof(\"gray81\")];\n    char stringpool_str1081[sizeof(\"gray72\")];\n    char stringpool_str1083[sizeof(\"gray71\")];\n    char stringpool_str1087[sizeof(\"lightslateblue\")];\n    char stringpool_str1092[sizeof(\"sandy brown\")];\n    char stringpool_str1095[sizeof(\"lime green\")];\n    char stringpool_str1098[sizeof(\"lightsteelblue4\")];\n    char stringpool_str1100[sizeof(\"lightsteelblue3\")];\n    char stringpool_str1106[sizeof(\"lightsteelblue2\")];\n    char stringpool_str1107[sizeof(\"forest green\")];\n    char stringpool_str1108[sizeof(\"lightsteelblue1\")];\n    char stringpool_str1112[sizeof(\"dark salmon\")];\n    char stringpool_str1114[sizeof(\"grey64\")];\n    char stringpool_str1115[sizeof(\"aliceblue\")];\n    char stringpool_str1116[sizeof(\"grey63\")];\n    char stringpool_str1121[sizeof(\"darkslategrey\")];\n    char stringpool_str1122[sizeof(\"grey62\")];\n    char stringpool_str1123[sizeof(\"royal blue\")];\n    char stringpool_str1124[sizeof(\"grey61\")];\n    char stringpool_str1125[sizeof(\"paleturquoise\")];\n    char stringpool_str1126[sizeof(\"dark magenta\")];\n    char stringpool_str1128[sizeof(\"mediumblue\")];\n    char stringpool_str1130[sizeof(\"gray64\")];\n    char stringpool_str1132[sizeof(\"gray63\")];\n    char stringpool_str1133[sizeof(\"ivory\")];\n    char stringpool_str1135[sizeof(\"light grey\")];\n    char stringpool_str1137[sizeof(\"darkslategray\")];\n    char stringpool_str1138[sizeof(\"gray62\")];\n    char stringpool_str1140[sizeof(\"gray61\")];\n    char stringpool_str1142[sizeof(\"wheat4\")];\n    char stringpool_str1144[sizeof(\"wheat3\")];\n    char stringpool_str1145[sizeof(\"light salmon\")];\n    char stringpool_str1147[sizeof(\"grey54\")];\n    char stringpool_str1149[sizeof(\"grey53\")];\n    char stringpool_str1150[sizeof(\"wheat2\")];\n    char stringpool_str1151[sizeof(\"light gray\")];\n    char stringpool_str1152[sizeof(\"wheat1\")];\n    char stringpool_str1153[sizeof(\"dark grey\")];\n    char stringpool_str1155[sizeof(\"grey52\")];\n    char stringpool_str1157[sizeof(\"grey51\")];\n    char stringpool_str1162[sizeof(\"thistle\")];\n    char stringpool_str1163[sizeof(\"gray54\")];\n    char stringpool_str1165[sizeof(\"gray53\")];\n    char stringpool_str1169[sizeof(\"dark gray\")];\n    char stringpool_str1171[sizeof(\"gray52\")];\n    char stringpool_str1173[sizeof(\"gray51\")];\n    char stringpool_str1182[sizeof(\"paleturquoise4\")];\n    char stringpool_str1184[sizeof(\"paleturquoise3\")];\n    char stringpool_str1190[sizeof(\"paleturquoise2\")];\n    char stringpool_str1192[sizeof(\"paleturquoise1\")];\n    char stringpool_str1203[sizeof(\"pale goldenrod\")];\n    char stringpool_str1212[sizeof(\"turquoise\")];\n    char stringpool_str1213[sizeof(\"turquoise4\")];\n    char stringpool_str1214[sizeof(\"turquoise3\")];\n    char stringpool_str1217[sizeof(\"turquoise2\")];\n    char stringpool_str1218[sizeof(\"turquoise1\")];\n    char stringpool_str1219[sizeof(\"thistle4\")];\n    char stringpool_str1220[sizeof(\"wheat\")];\n    char stringpool_str1221[sizeof(\"thistle3\")];\n    char stringpool_str1222[sizeof(\"misty rose\")];\n    char stringpool_str1227[sizeof(\"thistle2\")];\n    char stringpool_str1229[sizeof(\"thistle1\")];\n    char stringpool_str1235[sizeof(\"chocolate\")];\n    char stringpool_str1236[sizeof(\"chocolate4\")];\n    char stringpool_str1237[sizeof(\"chocolate3\")];\n    char stringpool_str1238[sizeof(\"peachpuff4\")];\n    char stringpool_str1239[sizeof(\"peachpuff3\")];\n    char stringpool_str1240[sizeof(\"chocolate2\")];\n    char stringpool_str1241[sizeof(\"chocolate1\")];\n    char stringpool_str1242[sizeof(\"peachpuff2\")];\n    char stringpool_str1243[sizeof(\"peachpuff1\")];\n    char stringpool_str1248[sizeof(\"lightcoral\")];\n    char stringpool_str1249[sizeof(\"darkcyan\")];\n    char stringpool_str1250[sizeof(\"chartreuse\")];\n    char stringpool_str1251[sizeof(\"chartreuse4\")];\n    char stringpool_str1252[sizeof(\"chartreuse3\")];\n    char stringpool_str1255[sizeof(\"chartreuse2\")];\n    char stringpool_str1256[sizeof(\"chartreuse1\")];\n    char stringpool_str1257[sizeof(\"rosybrown4\")];\n    char stringpool_str1258[sizeof(\"rosybrown3\")];\n    char stringpool_str1259[sizeof(\"deepskyblue\")];\n    char stringpool_str1261[sizeof(\"rosybrown2\")];\n    char stringpool_str1262[sizeof(\"rosybrown1\")];\n    char stringpool_str1273[sizeof(\"peachpuff\")];\n    char stringpool_str1274[sizeof(\"cadetblue\")];\n    char stringpool_str1275[sizeof(\"cadetblue4\")];\n    char stringpool_str1276[sizeof(\"cadetblue3\")];\n    char stringpool_str1279[sizeof(\"cadetblue2\")];\n    char stringpool_str1280[sizeof(\"cadetblue1\")];\n    char stringpool_str1283[sizeof(\"mediumseagreen\")];\n    char stringpool_str1287[sizeof(\"light sea green\")];\n    char stringpool_str1291[sizeof(\"mediumpurple\")];\n    char stringpool_str1294[sizeof(\"light goldenrod\")];\n    char stringpool_str1296[sizeof(\"yellow4\")];\n    char stringpool_str1298[sizeof(\"yellow3\")];\n    char stringpool_str1299[sizeof(\"lawngreen\")];\n    char stringpool_str1301[sizeof(\"rosybrown\")];\n    char stringpool_str1304[sizeof(\"yellow2\")];\n    char stringpool_str1306[sizeof(\"yellow1\")];\n    char stringpool_str1316[sizeof(\"deepskyblue4\")];\n    char stringpool_str1318[sizeof(\"deepskyblue3\")];\n    char stringpool_str1320[sizeof(\"dark slate blue\")];\n    char stringpool_str1324[sizeof(\"deepskyblue2\")];\n    char stringpool_str1326[sizeof(\"deepskyblue1\")];\n    char stringpool_str1331[sizeof(\"navy\")];\n    char stringpool_str1343[sizeof(\"lightslategrey\")];\n    char stringpool_str1348[sizeof(\"mediumpurple4\")];\n    char stringpool_str1350[sizeof(\"mediumpurple3\")];\n    char stringpool_str1353[sizeof(\"olive drab\")];\n    char stringpool_str1356[sizeof(\"mediumpurple2\")];\n    char stringpool_str1358[sizeof(\"mediumpurple1\")];\n    char stringpool_str1359[sizeof(\"lightslategray\")];\n    char stringpool_str1367[sizeof(\"indian red\")];\n    char stringpool_str1369[sizeof(\"aquamarine\")];\n    char stringpool_str1370[sizeof(\"aquamarine4\")];\n    char stringpool_str1371[sizeof(\"aquamarine3\")];\n    char stringpool_str1374[sizeof(\"aquamarine2\")];\n    char stringpool_str1375[sizeof(\"aquamarine1\")];\n    char stringpool_str1376[sizeof(\"medium blue\")];\n    char stringpool_str1383[sizeof(\"orchid\")];\n    char stringpool_str1393[sizeof(\"dark sea green\")];\n    char stringpool_str1396[sizeof(\"khaki4\")];\n    char stringpool_str1398[sizeof(\"khaki3\")];\n    char stringpool_str1403[sizeof(\"mediumslateblue\")];\n    char stringpool_str1404[sizeof(\"khaki2\")];\n    char stringpool_str1406[sizeof(\"khaki1\")];\n    char stringpool_str1407[sizeof(\"black\")];\n    char stringpool_str1408[sizeof(\"lavender\")];\n    char stringpool_str1412[sizeof(\"burlywood\")];\n    char stringpool_str1413[sizeof(\"burlywood4\")];\n    char stringpool_str1414[sizeof(\"burlywood3\")];\n    char stringpool_str1417[sizeof(\"burlywood2\")];\n    char stringpool_str1418[sizeof(\"burlywood1\")];\n    char stringpool_str1426[sizeof(\"lightcyan4\")];\n    char stringpool_str1427[sizeof(\"lightcyan3\")];\n    char stringpool_str1429[sizeof(\"mediumspringgreen\")];\n    char stringpool_str1430[sizeof(\"lightcyan2\")];\n    char stringpool_str1431[sizeof(\"lightcyan1\")];\n    char stringpool_str1440[sizeof(\"orchid4\")];\n    char stringpool_str1442[sizeof(\"orchid3\")];\n    char stringpool_str1444[sizeof(\"alice blue\")];\n    char stringpool_str1448[sizeof(\"orchid2\")];\n    char stringpool_str1449[sizeof(\"powderblue\")];\n    char stringpool_str1450[sizeof(\"orchid1\")];\n    char stringpool_str1451[sizeof(\"lightskyblue\")];\n    char stringpool_str1458[sizeof(\"yellowgreen\")];\n    char stringpool_str1468[sizeof(\"greenyellow\")];\n    char stringpool_str1469[sizeof(\"white\")];\n    char stringpool_str1470[sizeof(\"lightcyan\")];\n    char stringpool_str1484[sizeof(\"sandybrown\")];\n    char stringpool_str1495[sizeof(\"grey0\")];\n    char stringpool_str1499[sizeof(\"navyblue\")];\n    char stringpool_str1506[sizeof(\"violet\")];\n    char stringpool_str1508[sizeof(\"lightskyblue4\")];\n    char stringpool_str1510[sizeof(\"lightskyblue3\")];\n    char stringpool_str1511[sizeof(\"gray0\")];\n    char stringpool_str1516[sizeof(\"lightskyblue2\")];\n    char stringpool_str1518[sizeof(\"lightskyblue1\")];\n    char stringpool_str1543[sizeof(\"violetred\")];\n    char stringpool_str1544[sizeof(\"violetred4\")];\n    char stringpool_str1545[sizeof(\"violetred3\")];\n    char stringpool_str1548[sizeof(\"violetred2\")];\n    char stringpool_str1549[sizeof(\"violetred1\")];\n    char stringpool_str1552[sizeof(\"grey40\")];\n    char stringpool_str1553[sizeof(\"grey30\")];\n    char stringpool_str1556[sizeof(\"grey20\")];\n    char stringpool_str1557[sizeof(\"grey10\")];\n    char stringpool_str1561[sizeof(\"light coral\")];\n    char stringpool_str1564[sizeof(\"dark slate grey\")];\n    char stringpool_str1566[sizeof(\"peach puff\")];\n    char stringpool_str1568[sizeof(\"gray40\")];\n    char stringpool_str1569[sizeof(\"gray30\")];\n    char stringpool_str1572[sizeof(\"gray20\")];\n    char stringpool_str1573[sizeof(\"gray10\")];\n    char stringpool_str1580[sizeof(\"dark slate gray\")];\n    char stringpool_str1583[sizeof(\"lawn green\")];\n    char stringpool_str1585[sizeof(\"rosy brown\")];\n    char stringpool_str1588[sizeof(\"lightyellow\")];\n    char stringpool_str1603[sizeof(\"cadet blue\")];\n    char stringpool_str1609[sizeof(\"medium sea green\")];\n    char stringpool_str1616[sizeof(\"blanchedalmond\")];\n    char stringpool_str1634[sizeof(\"dark cyan\")];\n    char stringpool_str1642[sizeof(\"mediumorchid\")];\n    char stringpool_str1678[sizeof(\"light slate blue\")];\n    char stringpool_str1686[sizeof(\"dark orchid\")];\n    char stringpool_str1697[sizeof(\"powder blue\")];\n    char stringpool_str1699[sizeof(\"mediumorchid4\")];\n    char stringpool_str1701[sizeof(\"mediumorchid3\")];\n    char stringpool_str1705[sizeof(\"medium purple\")];\n    char stringpool_str1707[sizeof(\"mediumorchid2\")];\n    char stringpool_str1709[sizeof(\"mediumorchid1\")];\n    char stringpool_str1725[sizeof(\"honeydew4\")];\n    char stringpool_str1727[sizeof(\"honeydew3\")];\n    char stringpool_str1733[sizeof(\"honeydew2\")];\n    char stringpool_str1734[sizeof(\"midnightblue\")];\n    char stringpool_str1735[sizeof(\"honeydew1\")];\n    char stringpool_str1739[sizeof(\"light slate grey\")];\n    char stringpool_str1742[sizeof(\"deeppink4\")];\n    char stringpool_str1744[sizeof(\"deeppink3\")];\n    char stringpool_str1747[sizeof(\"grey9\")];\n    char stringpool_str1750[sizeof(\"deeppink2\")];\n    char stringpool_str1752[sizeof(\"deeppink1\")];\n    char stringpool_str1754[sizeof(\"light cyan\")];\n    char stringpool_str1755[sizeof(\"light slate gray\")];\n    char stringpool_str1763[sizeof(\"gray9\")];\n    char stringpool_str1765[sizeof(\"grey8\")];\n    char stringpool_str1767[sizeof(\"light steel blue\")];\n    char stringpool_str1771[sizeof(\"grey7\")];\n    char stringpool_str1773[sizeof(\"dark turquoise\")];\n    char stringpool_str1777[sizeof(\"mintcream\")];\n    char stringpool_str1781[sizeof(\"gray8\")];\n    char stringpool_str1787[sizeof(\"gray7\")];\n    char stringpool_str1804[sizeof(\"grey49\")];\n    char stringpool_str1805[sizeof(\"grey39\")];\n    char stringpool_str1808[sizeof(\"grey29\")];\n    char stringpool_str1809[sizeof(\"grey19\")];\n    char stringpool_str1817[sizeof(\"moccasin\")];\n    char stringpool_str1820[sizeof(\"gray49\")];\n    char stringpool_str1821[sizeof(\"gray39\")];\n    char stringpool_str1822[sizeof(\"grey48\")];\n    char stringpool_str1823[sizeof(\"grey38\")];\n    char stringpool_str1824[sizeof(\"gray29\")];\n    char stringpool_str1825[sizeof(\"gray19\")];\n    char stringpool_str1826[sizeof(\"grey28\")];\n    char stringpool_str1827[sizeof(\"grey18\")];\n    char stringpool_str1828[sizeof(\"grey47\")];\n    char stringpool_str1829[sizeof(\"grey37\")];\n    char stringpool_str1830[sizeof(\"lightgoldenrodyellow\")];\n    char stringpool_str1832[sizeof(\"grey27\")];\n    char stringpool_str1833[sizeof(\"grey17\")];\n    char stringpool_str1838[sizeof(\"gray48\")];\n    char stringpool_str1839[sizeof(\"gray38\")];\n    char stringpool_str1842[sizeof(\"gray28\")];\n    char stringpool_str1843[sizeof(\"gray18\")];\n    char stringpool_str1844[sizeof(\"gray47\")];\n    char stringpool_str1845[sizeof(\"gray37\")];\n    char stringpool_str1848[sizeof(\"gray27\")];\n    char stringpool_str1849[sizeof(\"gray17\")];\n    char stringpool_str1858[sizeof(\"khaki\")];\n    char stringpool_str1866[sizeof(\"antiquewhite\")];\n    char stringpool_str1872[sizeof(\"violet red\")];\n    char stringpool_str1873[sizeof(\"mint cream\")];\n    char stringpool_str1876[sizeof(\"darkorchid\")];\n    char stringpool_str1877[sizeof(\"darkorchid4\")];\n    char stringpool_str1878[sizeof(\"darkorchid3\")];\n    char stringpool_str1881[sizeof(\"darkorchid2\")];\n    char stringpool_str1882[sizeof(\"darkorchid1\")];\n    char stringpool_str1884[sizeof(\"navy blue\")];\n    char stringpool_str1885[sizeof(\"grey6\")];\n    char stringpool_str1888[sizeof(\"yellow green\")];\n    char stringpool_str1901[sizeof(\"gray6\")];\n    char stringpool_str1908[sizeof(\"lightpink4\")];\n    char stringpool_str1909[sizeof(\"lightpink3\")];\n    char stringpool_str1912[sizeof(\"lightpink2\")];\n    char stringpool_str1913[sizeof(\"lightpink1\")];\n    char stringpool_str1923[sizeof(\"antiquewhite4\")];\n    char stringpool_str1925[sizeof(\"antiquewhite3\")];\n    char stringpool_str1931[sizeof(\"antiquewhite2\")];\n    char stringpool_str1933[sizeof(\"antiquewhite1\")];\n    char stringpool_str1942[sizeof(\"grey46\")];\n    char stringpool_str1943[sizeof(\"grey36\")];\n    char stringpool_str1946[sizeof(\"grey26\")];\n    char stringpool_str1947[sizeof(\"grey16\")];\n    char stringpool_str1948[sizeof(\"pale turquoise\")];\n    char stringpool_str1951[sizeof(\"grey5\")];\n    char stringpool_str1958[sizeof(\"gray46\")];\n    char stringpool_str1959[sizeof(\"gray36\")];\n    char stringpool_str1960[sizeof(\"yellow\")];\n    char stringpool_str1962[sizeof(\"gray26\")];\n    char stringpool_str1963[sizeof(\"gray16\")];\n    char stringpool_str1964[sizeof(\"medium slate blue\")];\n    char stringpool_str1967[sizeof(\"gray5\")];\n    char stringpool_str1968[sizeof(\"lavenderblush4\")];\n    char stringpool_str1970[sizeof(\"lavenderblush3\")];\n    char stringpool_str1976[sizeof(\"lavenderblush2\")];\n    char stringpool_str1978[sizeof(\"lavenderblush1\")];\n    char stringpool_str1985[sizeof(\"floral white\")];\n    char stringpool_str1987[sizeof(\"medium orchid\")];\n    char stringpool_str1989[sizeof(\"mediumturquoise\")];\n    char stringpool_str1991[sizeof(\"mediumaquamarine\")];\n    char stringpool_str1992[sizeof(\"light sky blue\")];\n    char stringpool_str1993[sizeof(\"hotpink4\")];\n    char stringpool_str1995[sizeof(\"hotpink3\")];\n    char stringpool_str2001[sizeof(\"hotpink2\")];\n    char stringpool_str2003[sizeof(\"hotpink1\")];\n    char stringpool_str2008[sizeof(\"grey45\")];\n    char stringpool_str2009[sizeof(\"grey35\")];\n    char stringpool_str2012[sizeof(\"grey25\")];\n    char stringpool_str2013[sizeof(\"grey15\")];\n    char stringpool_str2022[sizeof(\"light goldenrod yellow\")];\n    char stringpool_str2024[sizeof(\"gray45\")];\n    char stringpool_str2025[sizeof(\"gray35\")];\n    char stringpool_str2028[sizeof(\"gray25\")];\n    char stringpool_str2029[sizeof(\"gray15\")];\n    char stringpool_str2067[sizeof(\"antique white\")];\n    char stringpool_str2068[sizeof(\"deep sky blue\")];\n    char stringpool_str2093[sizeof(\"darkviolet\")];\n    char stringpool_str2107[sizeof(\"cornflowerblue\")];\n    char stringpool_str2119[sizeof(\"floralwhite\")];\n    char stringpool_str2130[sizeof(\"medium spring green\")];\n    char stringpool_str2141[sizeof(\"cornsilk4\")];\n    char stringpool_str2143[sizeof(\"cornsilk3\")];\n    char stringpool_str2149[sizeof(\"cornsilk2\")];\n    char stringpool_str2151[sizeof(\"cornsilk1\")];\n    char stringpool_str2161[sizeof(\"firebrick4\")];\n    char stringpool_str2162[sizeof(\"firebrick3\")];\n    char stringpool_str2165[sizeof(\"firebrick2\")];\n    char stringpool_str2166[sizeof(\"firebrick1\")];\n    char stringpool_str2176[sizeof(\"cornflower blue\")];\n    char stringpool_str2185[sizeof(\"blueviolet\")];\n    char stringpool_str2188[sizeof(\"midnight blue\")];\n    char stringpool_str2218[sizeof(\"blanched almond\")];\n    char stringpool_str2220[sizeof(\"darkolivegreen\")];\n    char stringpool_str2230[sizeof(\"lavenderblush\")];\n    char stringpool_str2232[sizeof(\"darkolivegreen4\")];\n    char stringpool_str2234[sizeof(\"darkolivegreen3\")];\n    char stringpool_str2236[sizeof(\"light pink\")];\n    char stringpool_str2240[sizeof(\"darkolivegreen2\")];\n    char stringpool_str2242[sizeof(\"darkolivegreen1\")];\n    char stringpool_str2247[sizeof(\"grey100\")];\n    char stringpool_str2248[sizeof(\"palevioletred\")];\n    char stringpool_str2260[sizeof(\"deeppink\")];\n    char stringpool_str2263[sizeof(\"gray100\")];\n    char stringpool_str2279[sizeof(\"white smoke\")];\n    char stringpool_str2305[sizeof(\"palevioletred4\")];\n    char stringpool_str2306[sizeof(\"ghostwhite\")];\n    char stringpool_str2307[sizeof(\"palevioletred3\")];\n    char stringpool_str2311[sizeof(\"grey90\")];\n    char stringpool_str2313[sizeof(\"palevioletred2\")];\n    char stringpool_str2315[sizeof(\"palevioletred1\")];\n    char stringpool_str2320[sizeof(\"grey80\")];\n    char stringpool_str2323[sizeof(\"grey70\")];\n    char stringpool_str2327[sizeof(\"gray90\")];\n    char stringpool_str2336[sizeof(\"gray80\")];\n    char stringpool_str2339[sizeof(\"gray70\")];\n    char stringpool_str2347[sizeof(\"lemonchiffon\")];\n    char stringpool_str2359[sizeof(\"lemonchiffon4\")];\n    char stringpool_str2361[sizeof(\"lemonchiffon3\")];\n    char stringpool_str2367[sizeof(\"lemonchiffon2\")];\n    char stringpool_str2369[sizeof(\"lemonchiffon1\")];\n    char stringpool_str2380[sizeof(\"grey60\")];\n    char stringpool_str2389[sizeof(\"honeydew\")];\n    char stringpool_str2396[sizeof(\"gray60\")];\n    char stringpool_str2398[sizeof(\"medium turquoise\")];\n    char stringpool_str2413[sizeof(\"grey50\")];\n    char stringpool_str2422[sizeof(\"dark violet\")];\n    char stringpool_str2427[sizeof(\"medium aquamarine\")];\n    char stringpool_str2429[sizeof(\"gray50\")];\n    char stringpool_str2464[sizeof(\"papaya whip\")];\n    char stringpool_str2482[sizeof(\"lightpink\")];\n    char stringpool_str2500[sizeof(\"ghost white\")];\n    char stringpool_str2511[sizeof(\"hotpink\")];\n    char stringpool_str2514[sizeof(\"blue violet\")];\n    char stringpool_str2525[sizeof(\"whitesmoke\")];\n    char stringpool_str2544[sizeof(\"green yellow\")];\n    char stringpool_str2562[sizeof(\"dark olive green\")];\n    char stringpool_str2563[sizeof(\"grey99\")];\n    char stringpool_str2572[sizeof(\"grey89\")];\n    char stringpool_str2575[sizeof(\"grey79\")];\n    char stringpool_str2579[sizeof(\"gray99\")];\n    char stringpool_str2581[sizeof(\"grey98\")];\n    char stringpool_str2587[sizeof(\"grey97\")];\n    char stringpool_str2588[sizeof(\"gray89\")];\n    char stringpool_str2590[sizeof(\"grey88\")];\n    char stringpool_str2591[sizeof(\"gray79\")];\n    char stringpool_str2593[sizeof(\"grey78\")];\n    char stringpool_str2596[sizeof(\"grey87\")];\n    char stringpool_str2597[sizeof(\"gray98\")];\n    char stringpool_str2599[sizeof(\"grey77\")];\n    char stringpool_str2603[sizeof(\"gray97\")];\n    char stringpool_str2606[sizeof(\"gray88\")];\n    char stringpool_str2609[sizeof(\"gray78\")];\n    char stringpool_str2612[sizeof(\"gray87\")];\n    char stringpool_str2615[sizeof(\"gray77\")];\n    char stringpool_str2632[sizeof(\"grey69\")];\n    char stringpool_str2645[sizeof(\"deep pink\")];\n    char stringpool_str2648[sizeof(\"gray69\")];\n    char stringpool_str2650[sizeof(\"grey68\")];\n    char stringpool_str2654[sizeof(\"papayawhip\")];\n    char stringpool_str2656[sizeof(\"grey67\")];\n    char stringpool_str2659[sizeof(\"cornsilk\")];\n    char stringpool_str2664[sizeof(\"light yellow\")];\n    char stringpool_str2665[sizeof(\"grey59\")];\n    char stringpool_str2666[sizeof(\"gray68\")];\n    char stringpool_str2672[sizeof(\"gray67\")];\n    char stringpool_str2681[sizeof(\"gray59\")];\n    char stringpool_str2683[sizeof(\"grey58\")];\n    char stringpool_str2684[sizeof(\"lavender blush\")];\n    char stringpool_str2689[sizeof(\"grey57\")];\n    char stringpool_str2699[sizeof(\"gray58\")];\n    char stringpool_str2701[sizeof(\"grey96\")];\n    char stringpool_str2705[sizeof(\"gray57\")];\n    char stringpool_str2710[sizeof(\"grey86\")];\n    char stringpool_str2713[sizeof(\"grey76\")];\n    char stringpool_str2714[sizeof(\"hot pink\")];\n    char stringpool_str2715[sizeof(\"lemon chiffon\")];\n    char stringpool_str2717[sizeof(\"gray96\")];\n    char stringpool_str2726[sizeof(\"gray86\")];\n    char stringpool_str2729[sizeof(\"gray76\")];\n    char stringpool_str2735[sizeof(\"firebrick\")];\n    char stringpool_str2767[sizeof(\"grey95\")];\n    char stringpool_str2770[sizeof(\"grey66\")];\n    char stringpool_str2776[sizeof(\"grey85\")];\n    char stringpool_str2779[sizeof(\"grey75\")];\n    char stringpool_str2783[sizeof(\"gray95\")];\n    char stringpool_str2786[sizeof(\"gray66\")];\n    char stringpool_str2791[sizeof(\"dark khaki\")];\n    char stringpool_str2792[sizeof(\"gray85\")];\n    char stringpool_str2795[sizeof(\"gray75\")];\n    char stringpool_str2803[sizeof(\"grey56\")];\n    char stringpool_str2819[sizeof(\"gray56\")];\n    char stringpool_str2836[sizeof(\"grey65\")];\n    char stringpool_str2839[sizeof(\"mediumvioletred\")];\n    char stringpool_str2852[sizeof(\"gray65\")];\n    char stringpool_str2869[sizeof(\"grey55\")];\n    char stringpool_str2879[sizeof(\"navajo white\")];\n    char stringpool_str2885[sizeof(\"gray55\")];\n    char stringpool_str2981[sizeof(\"darkkhaki\")];\n    char stringpool_str3013[sizeof(\"navajowhite\")];\n    char stringpool_str3019[sizeof(\"pale violet red\")];\n    char stringpool_str3070[sizeof(\"navajowhite4\")];\n    char stringpool_str3072[sizeof(\"navajowhite3\")];\n    char stringpool_str3078[sizeof(\"navajowhite2\")];\n    char stringpool_str3080[sizeof(\"navajowhite1\")];\n    char stringpool_str3478[sizeof(\"medium violet red\")];\n  };\nstatic const struct stringpool_t stringpool_contents =\n  {\n    \"red\",\n    \"red4\",\n    \"red3\",\n    \"red2\",\n    \"red1\",\n    \"gold\",\n    \"grey4\",\n    \"grey3\",\n    \"grey2\",\n    \"grey1\",\n    \"gray4\",\n    \"gray3\",\n    \"snow4\",\n    \"snow3\",\n    \"gray2\",\n    \"gray1\",\n    \"snow2\",\n    \"snow1\",\n    \"gold4\",\n    \"gold3\",\n    \"blue\",\n    \"gold2\",\n    \"gold1\",\n    \"grey44\",\n    \"grey34\",\n    \"grey43\",\n    \"grey33\",\n    \"grey24\",\n    \"grey14\",\n    \"grey23\",\n    \"grey13\",\n    \"grey42\",\n    \"grey32\",\n    \"grey41\",\n    \"grey31\",\n    \"grey22\",\n    \"grey12\",\n    \"grey21\",\n    \"grey11\",\n    \"gray44\",\n    \"gray34\",\n    \"gray43\",\n    \"gray33\",\n    \"gray24\",\n    \"gray14\",\n    \"gray23\",\n    \"gray13\",\n    \"gray42\",\n    \"gray32\",\n    \"gray41\",\n    \"gray31\",\n    \"gray22\",\n    \"gray12\",\n    \"gray21\",\n    \"gray11\",\n    \"green\",\n    \"orange\",\n    \"blue4\",\n    \"blue3\",\n    \"azure\",\n    \"blue2\",\n    \"green4\",\n    \"blue1\",\n    \"green3\",\n    \"green2\",\n    \"green1\",\n    \"darkred\",\n    \"brown\",\n    \"tan4\",\n    \"tan3\",\n    \"grey\",\n    \"tan2\",\n    \"tan1\",\n    \"brown4\",\n    \"sienna\",\n    \"brown3\",\n    \"brown2\",\n    \"gray\",\n    \"brown1\",\n    \"orange4\",\n    \"bisque\",\n    \"orange3\",\n    \"azure4\",\n    \"azure3\",\n    \"orange2\",\n    \"orange1\",\n    \"azure2\",\n    \"azure1\",\n    \"linen\",\n    \"tan\",\n    \"peru\",\n    \"sienna4\",\n    \"sienna3\",\n    \"sienna2\",\n    \"sienna1\",\n    \"pink4\",\n    \"pink3\",\n    \"salmon\",\n    \"pink2\",\n    \"pink1\",\n    \"bisque4\",\n    \"salmon4\",\n    \"bisque3\",\n    \"salmon3\",\n    \"bisque2\",\n    \"salmon2\",\n    \"bisque1\",\n    \"salmon1\",\n    \"plum4\",\n    \"plum3\",\n    \"purple\",\n    \"plum2\",\n    \"plum1\",\n    \"orangered\",\n    \"orangered4\",\n    \"orangered3\",\n    \"orangered2\",\n    \"orangered1\",\n    \"seagreen\",\n    \"seagreen4\",\n    \"purple4\",\n    \"seagreen3\",\n    \"purple3\",\n    \"darkblue\",\n    \"seagreen2\",\n    \"purple2\",\n    \"seagreen1\",\n    \"purple1\",\n    \"debianred\",\n    \"darkorange\",\n    \"darkorange4\",\n    \"darkorange3\",\n    \"darkorange2\",\n    \"darkorange1\",\n    \"darkgreen\",\n    \"springgreen\",\n    \"goldenrod\",\n    \"goldenrod4\",\n    \"goldenrod3\",\n    \"goldenrod2\",\n    \"goldenrod1\",\n    \"springgreen4\",\n    \"sea green\",\n    \"springgreen3\",\n    \"saddlebrown\",\n    \"springgreen2\",\n    \"springgreen1\",\n    \"dodgerblue\",\n    \"dodgerblue4\",\n    \"dodgerblue3\",\n    \"dodgerblue2\",\n    \"dodgerblue1\",\n    \"slateblue\",\n    \"slateblue4\",\n    \"slateblue3\",\n    \"slateblue2\",\n    \"slateblue1\",\n    \"steelblue\",\n    \"steelblue4\",\n    \"steelblue3\",\n    \"steelblue2\",\n    \"steelblue1\",\n    \"darkseagreen\",\n    \"maroon\",\n    \"plum\",\n    \"darkseagreen4\",\n    \"skyblue\",\n    \"darkseagreen3\",\n    \"maroon4\",\n    \"darkgoldenrod\",\n    \"maroon3\",\n    \"darkseagreen2\",\n    \"darkseagreen1\",\n    \"maroon2\",\n    \"maroon1\",\n    \"lightgreen\",\n    \"slategray4\",\n    \"slategray3\",\n    \"forestgreen\",\n    \"slategray2\",\n    \"slategray1\",\n    \"palegreen4\",\n    \"palegreen3\",\n    \"palegreen2\",\n    \"palegreen1\",\n    \"skyblue4\",\n    \"skyblue3\",\n    \"darkgoldenrod4\",\n    \"darkgoldenrod3\",\n    \"skyblue2\",\n    \"skyblue1\",\n    \"sky blue\",\n    \"darkgoldenrod2\",\n    \"darkgoldenrod1\",\n    \"palegreen\",\n    \"dark red\",\n    \"lightblue\",\n    \"lightblue4\",\n    \"lightblue3\",\n    \"lightblue2\",\n    \"lightblue1\",\n    \"beige\",\n    \"darkgrey\",\n    \"darkmagenta\",\n    \"darkgray\",\n    \"magenta\",\n    \"cyan\",\n    \"royalblue\",\n    \"royalblue4\",\n    \"royalblue3\",\n    \"royalblue2\",\n    \"royalblue1\",\n    \"darksalmon\",\n    \"cyan4\",\n    \"cyan3\",\n    \"limegreen\",\n    \"cyan2\",\n    \"cyan1\",\n    \"palegoldenrod\",\n    \"orange red\",\n    \"seashell\",\n    \"tomato\",\n    \"magenta4\",\n    \"dodger blue\",\n    \"magenta3\",\n    \"dark green\",\n    \"darkslateblue\",\n    \"magenta2\",\n    \"magenta1\",\n    \"slategrey\",\n    \"lightseagreen\",\n    \"coral\",\n    \"seashell4\",\n    \"seashell3\",\n    \"slategray\",\n    \"seashell2\",\n    \"seashell1\",\n    \"lightgoldenrod\",\n    \"tomato4\",\n    \"tomato3\",\n    \"dark orange\",\n    \"tomato2\",\n    \"tomato1\",\n    \"coral4\",\n    \"coral3\",\n    \"coral2\",\n    \"coral1\",\n    \"mistyrose\",\n    \"mistyrose4\",\n    \"mistyrose3\",\n    \"mistyrose2\",\n    \"mistyrose1\",\n    \"dark blue\",\n    \"snow\",\n    \"lightgoldenrod4\",\n    \"lightgoldenrod3\",\n    \"lightyellow4\",\n    \"slate blue\",\n    \"lightyellow3\",\n    \"lightgoldenrod2\",\n    \"lightgoldenrod1\",\n    \"lightyellow2\",\n    \"lightyellow1\",\n    \"oldlace\",\n    \"pink\",\n    \"steel blue\",\n    \"dimgrey\",\n    \"lightsalmon\",\n    \"darkturquoise\",\n    \"dimgray\",\n    \"lightsalmon4\",\n    \"lightsalmon3\",\n    \"lightsalmon2\",\n    \"lightsalmon1\",\n    \"saddle brown\",\n    \"spring green\",\n    \"slate grey\",\n    \"lightgrey\",\n    \"light green\",\n    \"dim grey\",\n    \"slate gray\",\n    \"lightgray\",\n    \"ivory4\",\n    \"pale green\",\n    \"ivory3\",\n    \"darkslategray4\",\n    \"darkslategray3\",\n    \"ivory2\",\n    \"dim gray\",\n    \"ivory1\",\n    \"darkslategray2\",\n    \"darkslategray1\",\n    \"old lace\",\n    \"olivedrab4\",\n    \"olivedrab3\",\n    \"dark goldenrod\",\n    \"olivedrab2\",\n    \"olivedrab1\",\n    \"olivedrab\",\n    \"indianred\",\n    \"indianred4\",\n    \"indianred3\",\n    \"lightsteelblue\",\n    \"indianred2\",\n    \"indianred1\",\n    \"grey94\",\n    \"gainsboro\",\n    \"grey93\",\n    \"grey92\",\n    \"grey84\",\n    \"grey91\",\n    \"grey83\",\n    \"grey74\",\n    \"grey73\",\n    \"gray94\",\n    \"grey82\",\n    \"gray93\",\n    \"grey81\",\n    \"grey72\",\n    \"grey71\",\n    \"gray92\",\n    \"gray84\",\n    \"gray91\",\n    \"gray83\",\n    \"gray74\",\n    \"light blue\",\n    \"gray73\",\n    \"gray82\",\n    \"gray81\",\n    \"gray72\",\n    \"gray71\",\n    \"lightslateblue\",\n    \"sandy brown\",\n    \"lime green\",\n    \"lightsteelblue4\",\n    \"lightsteelblue3\",\n    \"lightsteelblue2\",\n    \"forest green\",\n    \"lightsteelblue1\",\n    \"dark salmon\",\n    \"grey64\",\n    \"aliceblue\",\n    \"grey63\",\n    \"darkslategrey\",\n    \"grey62\",\n    \"royal blue\",\n    \"grey61\",\n    \"paleturquoise\",\n    \"dark magenta\",\n    \"mediumblue\",\n    \"gray64\",\n    \"gray63\",\n    \"ivory\",\n    \"light grey\",\n    \"darkslategray\",\n    \"gray62\",\n    \"gray61\",\n    \"wheat4\",\n    \"wheat3\",\n    \"light salmon\",\n    \"grey54\",\n    \"grey53\",\n    \"wheat2\",\n    \"light gray\",\n    \"wheat1\",\n    \"dark grey\",\n    \"grey52\",\n    \"grey51\",\n    \"thistle\",\n    \"gray54\",\n    \"gray53\",\n    \"dark gray\",\n    \"gray52\",\n    \"gray51\",\n    \"paleturquoise4\",\n    \"paleturquoise3\",\n    \"paleturquoise2\",\n    \"paleturquoise1\",\n    \"pale goldenrod\",\n    \"turquoise\",\n    \"turquoise4\",\n    \"turquoise3\",\n    \"turquoise2\",\n    \"turquoise1\",\n    \"thistle4\",\n    \"wheat\",\n    \"thistle3\",\n    \"misty rose\",\n    \"thistle2\",\n    \"thistle1\",\n    \"chocolate\",\n    \"chocolate4\",\n    \"chocolate3\",\n    \"peachpuff4\",\n    \"peachpuff3\",\n    \"chocolate2\",\n    \"chocolate1\",\n    \"peachpuff2\",\n    \"peachpuff1\",\n    \"lightcoral\",\n    \"darkcyan\",\n    \"chartreuse\",\n    \"chartreuse4\",\n    \"chartreuse3\",\n    \"chartreuse2\",\n    \"chartreuse1\",\n    \"rosybrown4\",\n    \"rosybrown3\",\n    \"deepskyblue\",\n    \"rosybrown2\",\n    \"rosybrown1\",\n    \"peachpuff\",\n    \"cadetblue\",\n    \"cadetblue4\",\n    \"cadetblue3\",\n    \"cadetblue2\",\n    \"cadetblue1\",\n    \"mediumseagreen\",\n    \"light sea green\",\n    \"mediumpurple\",\n    \"light goldenrod\",\n    \"yellow4\",\n    \"yellow3\",\n    \"lawngreen\",\n    \"rosybrown\",\n    \"yellow2\",\n    \"yellow1\",\n    \"deepskyblue4\",\n    \"deepskyblue3\",\n    \"dark slate blue\",\n    \"deepskyblue2\",\n    \"deepskyblue1\",\n    \"navy\",\n    \"lightslategrey\",\n    \"mediumpurple4\",\n    \"mediumpurple3\",\n    \"olive drab\",\n    \"mediumpurple2\",\n    \"mediumpurple1\",\n    \"lightslategray\",\n    \"indian red\",\n    \"aquamarine\",\n    \"aquamarine4\",\n    \"aquamarine3\",\n    \"aquamarine2\",\n    \"aquamarine1\",\n    \"medium blue\",\n    \"orchid\",\n    \"dark sea green\",\n    \"khaki4\",\n    \"khaki3\",\n    \"mediumslateblue\",\n    \"khaki2\",\n    \"khaki1\",\n    \"black\",\n    \"lavender\",\n    \"burlywood\",\n    \"burlywood4\",\n    \"burlywood3\",\n    \"burlywood2\",\n    \"burlywood1\",\n    \"lightcyan4\",\n    \"lightcyan3\",\n    \"mediumspringgreen\",\n    \"lightcyan2\",\n    \"lightcyan1\",\n    \"orchid4\",\n    \"orchid3\",\n    \"alice blue\",\n    \"orchid2\",\n    \"powderblue\",\n    \"orchid1\",\n    \"lightskyblue\",\n    \"yellowgreen\",\n    \"greenyellow\",\n    \"white\",\n    \"lightcyan\",\n    \"sandybrown\",\n    \"grey0\",\n    \"navyblue\",\n    \"violet\",\n    \"lightskyblue4\",\n    \"lightskyblue3\",\n    \"gray0\",\n    \"lightskyblue2\",\n    \"lightskyblue1\",\n    \"violetred\",\n    \"violetred4\",\n    \"violetred3\",\n    \"violetred2\",\n    \"violetred1\",\n    \"grey40\",\n    \"grey30\",\n    \"grey20\",\n    \"grey10\",\n    \"light coral\",\n    \"dark slate grey\",\n    \"peach puff\",\n    \"gray40\",\n    \"gray30\",\n    \"gray20\",\n    \"gray10\",\n    \"dark slate gray\",\n    \"lawn green\",\n    \"rosy brown\",\n    \"lightyellow\",\n    \"cadet blue\",\n    \"medium sea green\",\n    \"blanchedalmond\",\n    \"dark cyan\",\n    \"mediumorchid\",\n    \"light slate blue\",\n    \"dark orchid\",\n    \"powder blue\",\n    \"mediumorchid4\",\n    \"mediumorchid3\",\n    \"medium purple\",\n    \"mediumorchid2\",\n    \"mediumorchid1\",\n    \"honeydew4\",\n    \"honeydew3\",\n    \"honeydew2\",\n    \"midnightblue\",\n    \"honeydew1\",\n    \"light slate grey\",\n    \"deeppink4\",\n    \"deeppink3\",\n    \"grey9\",\n    \"deeppink2\",\n    \"deeppink1\",\n    \"light cyan\",\n    \"light slate gray\",\n    \"gray9\",\n    \"grey8\",\n    \"light steel blue\",\n    \"grey7\",\n    \"dark turquoise\",\n    \"mintcream\",\n    \"gray8\",\n    \"gray7\",\n    \"grey49\",\n    \"grey39\",\n    \"grey29\",\n    \"grey19\",\n    \"moccasin\",\n    \"gray49\",\n    \"gray39\",\n    \"grey48\",\n    \"grey38\",\n    \"gray29\",\n    \"gray19\",\n    \"grey28\",\n    \"grey18\",\n    \"grey47\",\n    \"grey37\",\n    \"lightgoldenrodyellow\",\n    \"grey27\",\n    \"grey17\",\n    \"gray48\",\n    \"gray38\",\n    \"gray28\",\n    \"gray18\",\n    \"gray47\",\n    \"gray37\",\n    \"gray27\",\n    \"gray17\",\n    \"khaki\",\n    \"antiquewhite\",\n    \"violet red\",\n    \"mint cream\",\n    \"darkorchid\",\n    \"darkorchid4\",\n    \"darkorchid3\",\n    \"darkorchid2\",\n    \"darkorchid1\",\n    \"navy blue\",\n    \"grey6\",\n    \"yellow green\",\n    \"gray6\",\n    \"lightpink4\",\n    \"lightpink3\",\n    \"lightpink2\",\n    \"lightpink1\",\n    \"antiquewhite4\",\n    \"antiquewhite3\",\n    \"antiquewhite2\",\n    \"antiquewhite1\",\n    \"grey46\",\n    \"grey36\",\n    \"grey26\",\n    \"grey16\",\n    \"pale turquoise\",\n    \"grey5\",\n    \"gray46\",\n    \"gray36\",\n    \"yellow\",\n    \"gray26\",\n    \"gray16\",\n    \"medium slate blue\",\n    \"gray5\",\n    \"lavenderblush4\",\n    \"lavenderblush3\",\n    \"lavenderblush2\",\n    \"lavenderblush1\",\n    \"floral white\",\n    \"medium orchid\",\n    \"mediumturquoise\",\n    \"mediumaquamarine\",\n    \"light sky blue\",\n    \"hotpink4\",\n    \"hotpink3\",\n    \"hotpink2\",\n    \"hotpink1\",\n    \"grey45\",\n    \"grey35\",\n    \"grey25\",\n    \"grey15\",\n    \"light goldenrod yellow\",\n    \"gray45\",\n    \"gray35\",\n    \"gray25\",\n    \"gray15\",\n    \"antique white\",\n    \"deep sky blue\",\n    \"darkviolet\",\n    \"cornflowerblue\",\n    \"floralwhite\",\n    \"medium spring green\",\n    \"cornsilk4\",\n    \"cornsilk3\",\n    \"cornsilk2\",\n    \"cornsilk1\",\n    \"firebrick4\",\n    \"firebrick3\",\n    \"firebrick2\",\n    \"firebrick1\",\n    \"cornflower blue\",\n    \"blueviolet\",\n    \"midnight blue\",\n    \"blanched almond\",\n    \"darkolivegreen\",\n    \"lavenderblush\",\n    \"darkolivegreen4\",\n    \"darkolivegreen3\",\n    \"light pink\",\n    \"darkolivegreen2\",\n    \"darkolivegreen1\",\n    \"grey100\",\n    \"palevioletred\",\n    \"deeppink\",\n    \"gray100\",\n    \"white smoke\",\n    \"palevioletred4\",\n    \"ghostwhite\",\n    \"palevioletred3\",\n    \"grey90\",\n    \"palevioletred2\",\n    \"palevioletred1\",\n    \"grey80\",\n    \"grey70\",\n    \"gray90\",\n    \"gray80\",\n    \"gray70\",\n    \"lemonchiffon\",\n    \"lemonchiffon4\",\n    \"lemonchiffon3\",\n    \"lemonchiffon2\",\n    \"lemonchiffon1\",\n    \"grey60\",\n    \"honeydew\",\n    \"gray60\",\n    \"medium turquoise\",\n    \"grey50\",\n    \"dark violet\",\n    \"medium aquamarine\",\n    \"gray50\",\n    \"papaya whip\",\n    \"lightpink\",\n    \"ghost white\",\n    \"hotpink\",\n    \"blue violet\",\n    \"whitesmoke\",\n    \"green yellow\",\n    \"dark olive green\",\n    \"grey99\",\n    \"grey89\",\n    \"grey79\",\n    \"gray99\",\n    \"grey98\",\n    \"grey97\",\n    \"gray89\",\n    \"grey88\",\n    \"gray79\",\n    \"grey78\",\n    \"grey87\",\n    \"gray98\",\n    \"grey77\",\n    \"gray97\",\n    \"gray88\",\n    \"gray78\",\n    \"gray87\",\n    \"gray77\",\n    \"grey69\",\n    \"deep pink\",\n    \"gray69\",\n    \"grey68\",\n    \"papayawhip\",\n    \"grey67\",\n    \"cornsilk\",\n    \"light yellow\",\n    \"grey59\",\n    \"gray68\",\n    \"gray67\",\n    \"gray59\",\n    \"grey58\",\n    \"lavender blush\",\n    \"grey57\",\n    \"gray58\",\n    \"grey96\",\n    \"gray57\",\n    \"grey86\",\n    \"grey76\",\n    \"hot pink\",\n    \"lemon chiffon\",\n    \"gray96\",\n    \"gray86\",\n    \"gray76\",\n    \"firebrick\",\n    \"grey95\",\n    \"grey66\",\n    \"grey85\",\n    \"grey75\",\n    \"gray95\",\n    \"gray66\",\n    \"dark khaki\",\n    \"gray85\",\n    \"gray75\",\n    \"grey56\",\n    \"gray56\",\n    \"grey65\",\n    \"mediumvioletred\",\n    \"gray65\",\n    \"grey55\",\n    \"navajo white\",\n    \"gray55\",\n    \"darkkhaki\",\n    \"navajowhite\",\n    \"pale violet red\",\n    \"navajowhite4\",\n    \"navajowhite3\",\n    \"navajowhite2\",\n    \"navajowhite1\",\n    \"medium violet red\"\n  };\n#define stringpool ((const char *) &stringpool_contents)\n\n#if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 6) > 4) || (defined __clang__ && __clang_major__ >= 3)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmissing-field-initializers\"\n#endif\nstatic const struct Keyword color_names[] =\n  {\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 635 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str172, 16711680},\n#line 639 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str173, 9109504},\n#line 638 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str174, 13434880},\n    {-1}, {-1},\n#line 637 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str177, 15597568},\n#line 636 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str178, 16711680},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 177 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str202, 16766720},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 332 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str229, 657930},\n    {-1},\n#line 321 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str231, 526344},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 310 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str237, 328965},\n    {-1},\n#line 298 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str239, 197379},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 223 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str245, 657930},\n    {-1},\n#line 212 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str247, 526344},\n#line 701 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str248, 9144713},\n    {-1},\n#line 700 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str250, 13486537},\n    {-1}, {-1},\n#line 201 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str253, 328965},\n    {-1},\n#line 189 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str255, 197379},\n#line 699 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str256, 15657449},\n    {-1},\n#line 698 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str258, 16775930},\n#line 181 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str259, 9139456},\n    {-1},\n#line 180 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str261, 13479168},\n    {-1}, {-1}, {-1},\n#line 30 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str265, 255},\n    {-1},\n#line 179 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str267, 15649024},\n    {-1},\n#line 178 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str269, 16766720},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 337 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str286, 7368816},\n#line 326 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str287, 5723991},\n#line 336 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str288, 7237230},\n#line 325 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str289, 5526612},\n#line 315 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str290, 4013373},\n#line 304 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str291, 2368548},\n#line 314 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str292, 3881787},\n#line 303 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str293, 2171169},\n#line 335 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str294, 7039851},\n#line 324 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str295, 5395026},\n#line 334 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str296, 6908265},\n#line 323 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str297, 5197647},\n#line 313 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str298, 3684408},\n#line 302 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str299, 2039583},\n#line 312 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str300, 3552822},\n#line 301 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str301, 1842204},\n#line 228 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str302, 7368816},\n#line 217 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str303, 5723991},\n#line 227 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str304, 7237230},\n#line 216 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str305, 5526612},\n#line 206 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str306, 4013373},\n#line 195 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str307, 2368548},\n#line 205 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str308, 3881787},\n#line 194 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str309, 2171169},\n#line 226 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str310, 7039851},\n#line 215 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str311, 5395026},\n#line 225 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str312, 6908265},\n#line 214 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str313, 5197647},\n#line 204 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str314, 3684408},\n#line 193 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str315, 2039583},\n#line 203 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str316, 3552822},\n#line 192 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str317, 1842204},\n    {-1},\n#line 289 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str319, 65280},\n    {-1},\n#line 573 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str321, 16753920},\n#line 35 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str322, 139},\n    {-1},\n#line 34 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str324, 205},\n    {-1},\n#line 16 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str326, 15794175},\n    {-1}, {-1}, {-1},\n#line 33 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str330, 238},\n#line 294 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str331, 35584},\n#line 32 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str332, 255},\n#line 293 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str333, 52480},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 292 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str339, 60928},\n    {-1},\n#line 291 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str341, 65280},\n    {-1}, {-1}, {-1},\n#line 126 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str345, 9109504},\n    {-1}, {-1}, {-1}, {-1},\n#line 37 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str350, 10824234},\n    {-1},\n#line 718 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str352, 9132587},\n#line 717 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str353, 13468991},\n    {-1},\n#line 296 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str355, 12500670},\n#line 716 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str356, 15637065},\n#line 715 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str357, 16753999},\n    {-1}, {-1}, {-1}, {-1},\n#line 41 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str362, 9118499},\n#line 672 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str363, 10506797},\n#line 40 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str364, 13447987},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 39 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str370, 15612731},\n#line 187 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str371, 12500670},\n#line 38 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str372, 16728128},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 578 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str378, 9132544},\n#line 22 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str379, 16770244},\n#line 577 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str380, 13468928},\n    {-1}, {-1},\n#line 20 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str383, 8620939},\n    {-1},\n#line 19 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str385, 12701133},\n#line 576 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str386, 15636992},\n    {-1},\n#line 575 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str388, 16753920},\n    {-1}, {-1},\n#line 18 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str391, 14741230},\n    {-1},\n#line 17 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str393, 15794175},\n#line 508 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str394, 16445670},\n    {-1},\n#line 714 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str396, 13808780},\n    {-1}, {-1}, {-1},\n#line 617 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str400, 13468991},\n    {-1}, {-1}, {-1},\n#line 676 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str404, 9127718},\n    {-1},\n#line 675 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str406, 13461561},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 674 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str412, 15628610},\n    {-1},\n#line 673 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str414, 16745031},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 622 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str420, 9134956},\n    {-1},\n#line 621 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str422, 13472158},\n    {-1}, {-1},\n#line 654 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str425, 16416882},\n    {-1}, {-1},\n#line 620 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str428, 15641016},\n    {-1},\n#line 619 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str430, 16758213},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 26 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str436, 9141611},\n#line 658 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str437, 9129017},\n#line 25 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str438, 13481886},\n#line 657 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str439, 13463636},\n    {-1}, {-1}, {-1}, {-1},\n#line 24 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str444, 15652279},\n#line 656 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str445, 15630946},\n#line 23 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str446, 16770244},\n#line 655 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str447, 16747625},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 627 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str456, 9135755},\n    {-1},\n#line 626 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str458, 13473485},\n    {-1}, {-1}, {-1}, {-1},\n#line 630 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str463, 10494192},\n#line 625 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str464, 15642350},\n    {-1},\n#line 624 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str466, 16759807},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 579 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str493, 16729344},\n#line 583 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str494, 9118976},\n#line 582 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str495, 13448960},\n    {-1}, {-1},\n#line 581 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str498, 15613952},\n#line 580 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str499, 16729344},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 662 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str507, 3050327},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1},\n#line 666 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str519, 3050327},\n#line 634 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str520, 5577355},\n#line 665 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str521, 4443520},\n#line 633 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str522, 8201933},\n    {-1},\n#line 99 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str524, 139},\n    {-1}, {-1},\n#line 664 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str527, 5172884},\n#line 632 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str528, 9514222},\n#line 663 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str529, 5570463},\n#line 631 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str530, 10170623},\n#line 142 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str531, 14092113},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 116 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str540, 16747520},\n#line 120 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str541, 9127168},\n#line 119 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str542, 13460992},\n    {-1}, {-1},\n#line 118 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str545, 15627776},\n#line 117 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str546, 16744192},\n    {-1}, {-1},\n#line 107 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str549, 25600},\n    {-1},\n#line 703 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str551, 65407},\n#line 182 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str552, 14329120},\n#line 186 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str553, 9136404},\n#line 185 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str554, 13474589},\n    {-1}, {-1},\n#line 184 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str557, 15643682},\n#line 183 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str558, 16761125},\n    {-1}, {-1}, {-1}, {-1},\n#line 707 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str563, 35653},\n#line 661 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str564, 3050327},\n#line 706 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str565, 52582},\n#line 653 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str566, 9127187},\n    {-1}, {-1}, {-1}, {-1},\n#line 705 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str571, 61046},\n    {-1},\n#line 704 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str573, 65407},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 160 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str582, 2003199},\n#line 164 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str583, 1068683},\n#line 163 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str584, 1602765},\n    {-1}, {-1},\n#line 162 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str587, 1869550},\n#line 161 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str588, 2003199},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 686 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str596, 6970061},\n#line 690 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str597, 4668555},\n#line 689 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str598, 6904269},\n    {-1}, {-1},\n#line 688 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str601, 8021998},\n#line 687 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str602, 8613887},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 709 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str610, 4620980},\n#line 713 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str611, 3564683},\n#line 712 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str612, 5215437},\n    {-1}, {-1},\n#line 711 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str615, 6073582},\n#line 710 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str616, 6535423},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 128 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str624, 9419919},\n    {-1}, {-1}, {-1}, {-1},\n#line 514 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str629, 11546720},\n    {-1}, {-1},\n#line 623 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str632, 14524637},\n    {-1}, {-1}, {-1},\n#line 132 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str636, 6916969},\n#line 678 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str637, 8900331},\n#line 131 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str638, 10210715},\n    {-1}, {-1},\n#line 518 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str641, 9116770},\n#line 101 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str642, 12092939},\n#line 517 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str643, 13445520},\n#line 130 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str644, 11857588},\n    {-1},\n#line 129 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str646, 12713921},\n    {-1}, {-1},\n#line 516 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str649, 15610023},\n    {-1},\n#line 515 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str651, 16725171},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 475 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str669, 9498256},\n    {-1}, {-1}, {-1}, {-1},\n#line 695 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str674, 7109515},\n#line 694 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str675, 10467021},\n    {-1},\n#line 173 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str677, 2263842},\n#line 693 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str678, 12178414},\n#line 692 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str679, 13034239},\n#line 598 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str680, 5540692},\n#line 597 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str681, 8179068},\n    {-1}, {-1},\n#line 596 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str684, 9498256},\n#line 595 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str685, 10157978},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 682 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str694, 4878475},\n    {-1},\n#line 681 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str696, 7120589},\n    {-1}, {-1},\n#line 105 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str699, 9135368},\n    {-1},\n#line 104 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str701, 13473036},\n#line 680 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str702, 8306926},\n    {-1},\n#line 679 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str704, 8900351},\n    {-1},\n#line 677 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str706, 8900331},\n#line 103 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str707, 15641870},\n    {-1},\n#line 102 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str709, 16759055},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 594 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str724, 10025880},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 91 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str730, 9109504},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 457 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str745, 11393254},\n#line 461 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str746, 6849419},\n#line 460 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str747, 10141901},\n    {-1}, {-1},\n#line 459 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str750, 11722734},\n#line 458 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str751, 12578815},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 21 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str760, 16119260},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 108 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str768, 11119017},\n    {-1},\n#line 110 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str770, 9109643},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1},\n#line 106 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str784, 11119017},\n    {-1}, {-1}, {-1},\n#line 509 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str788, 16711935},\n    {-1}, {-1}, {-1},\n#line 75 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str792, 65535},\n    {-1},\n#line 647 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str794, 4286945},\n#line 651 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str795, 2572427},\n#line 650 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str796, 3825613},\n    {-1}, {-1},\n#line 649 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str799, 4419310},\n#line 648 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str800, 4749055},\n    {-1},\n#line 127 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str802, 15308410},\n    {-1},\n#line 79 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str804, 35723},\n    {-1},\n#line 78 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str806, 52685},\n    {-1}, {-1}, {-1}, {-1},\n#line 507 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str811, 3329330},\n#line 77 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str812, 61166},\n    {-1},\n#line 76 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str814, 65535},\n    {-1}, {-1},\n#line 593 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str817, 15657130},\n    {-1}, {-1}, {-1}, {-1},\n#line 574 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str822, 16729344},\n    {-1}, {-1},\n#line 667 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str825, 16774638},\n    {-1},\n#line 724 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str827, 16737095},\n    {-1},\n#line 513 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str829, 9109643},\n#line 159 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str830, 2003199},\n#line 512 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str831, 13435085},\n    {-1},\n#line 84 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str833, 25600},\n    {-1}, {-1},\n#line 133 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str836, 4734347},\n#line 511 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str837, 15597806},\n    {-1},\n#line 510 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str839, 16711935},\n#line 696 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str840, 7372944},\n    {-1}, {-1}, {-1},\n#line 487 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str844, 2142890},\n    {-1}, {-1}, {-1}, {-1},\n#line 63 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str849, 16744272},\n    {-1}, {-1},\n#line 671 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str852, 9143938},\n    {-1},\n#line 670 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str854, 13485503},\n    {-1},\n#line 691 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str856, 7372944},\n    {-1}, {-1}, {-1},\n#line 669 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str860, 15656414},\n    {-1},\n#line 668 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str862, 16774638},\n    {-1},\n#line 468 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str864, 15654274},\n#line 728 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str865, 9123366},\n    {-1},\n#line 727 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str867, 13455161},\n    {-1},\n#line 89 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str869, 16747520},\n    {-1}, {-1}, {-1},\n#line 726 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str873, 15621186},\n    {-1},\n#line 725 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str875, 16737095},\n#line 67 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str876, 9125423},\n    {-1},\n#line 66 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str878, 13458245},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 65 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str884, 15624784},\n    {-1},\n#line 64 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str886, 16740950},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 550 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str893, 16770273},\n#line 554 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str894, 9141627},\n#line 553 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str895, 13481909},\n    {-1}, {-1},\n#line 552 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str898, 15652306},\n#line 551 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str899, 16770273},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 80 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str909, 139},\n    {-1}, {-1},\n#line 697 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str912, 16775930},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 472 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str921, 9142604},\n    {-1},\n#line 471 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str923, 13483632},\n#line 505 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str924, 9145210},\n#line 683 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str925, 6970061},\n#line 504 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str926, 13487540},\n    {-1}, {-1},\n#line 470 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str929, 15654018},\n    {-1},\n#line 469 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str931, 16772235},\n#line 503 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str932, 15658705},\n    {-1},\n#line 502 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str934, 16777184},\n    {-1}, {-1},\n#line 566 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str937, 16643558},\n#line 618 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str938, 16761035},\n#line 708 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str939, 4620980},\n    {-1}, {-1}, {-1},\n#line 158 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str943, 6908265},\n    {-1}, {-1}, {-1}, {-1},\n#line 482 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str948, 16752762},\n    {-1},\n#line 140 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str950, 52945},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 157 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str959, 6908265},\n#line 486 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str960, 9131842},\n    {-1},\n#line 485 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str962, 13468002},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 484 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str968, 15635826},\n    {-1},\n#line 483 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str970, 16752762},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 652 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str977, 9127187},\n    {-1}, {-1}, {-1},\n#line 702 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str981, 65407},\n    {-1}, {-1}, {-1}, {-1},\n#line 685 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str986, 7372944},\n    {-1}, {-1},\n#line 476 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str989, 13882323},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 446 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str998, 9498256},\n    {-1},\n#line 156 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1000, 6908265},\n    {-1},\n#line 684 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1002, 7372944},\n    {-1}, {-1},\n#line 474 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1005, 13882323},\n    {-1},\n#line 419 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1007, 9145219},\n#line 590 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1008, 10025880},\n#line 418 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1009, 13487553},\n    {-1},\n#line 138 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1011, 5409675},\n    {-1},\n#line 137 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1013, 7982541},\n    {-1},\n#line 417 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1015, 15658720},\n#line 155 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1016, 6908265},\n#line 416 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1017, 16777200},\n    {-1},\n#line 136 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1019, 9301742},\n    {-1},\n#line 135 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1021, 9961471},\n    {-1}, {-1},\n#line 565 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1024, 16643558},\n#line 572 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1025, 6916898},\n#line 571 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1026, 10145074},\n    {-1},\n#line 82 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1028, 12092939},\n#line 570 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1029, 11791930},\n#line 569 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1030, 12648254},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 568 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1036, 7048739},\n    {-1},\n#line 410 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1038, 13458524},\n#line 414 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1039, 9124410},\n#line 413 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1040, 13456725},\n#line 496 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1041, 11584734},\n    {-1},\n#line 412 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1043, 15623011},\n#line 411 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1044, 16738922},\n#line 392 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1045, 15790320},\n#line 174 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1046, 14474460},\n#line 391 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1047, 15592941},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 390 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1053, 15461355},\n#line 381 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1054, 14079702},\n#line 389 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1055, 15263976},\n#line 380 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1056, 13948116},\n#line 370 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1057, 12434877},\n    {-1},\n#line 369 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1059, 12237498},\n    {-1},\n#line 283 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1061, 15790320},\n#line 379 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1062, 13750737},\n#line 282 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1063, 15592941},\n#line 378 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1064, 13619151},\n#line 368 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1065, 12105912},\n    {-1},\n#line 367 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1067, 11908533},\n    {-1},\n#line 281 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1069, 15461355},\n#line 272 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1070, 14079702},\n#line 280 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1071, 15263976},\n#line 271 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1072, 13948116},\n#line 261 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1073, 12434877},\n#line 440 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1074, 11393254},\n#line 260 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1075, 12237498},\n    {-1}, {-1},\n#line 270 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1078, 13750737},\n    {-1},\n#line 269 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1080, 13619151},\n#line 259 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1081, 12105912},\n    {-1},\n#line 258 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1083, 11908533},\n    {-1}, {-1}, {-1},\n#line 493 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1087, 8679679},\n    {-1}, {-1}, {-1}, {-1},\n#line 659 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1092, 16032864},\n    {-1}, {-1},\n#line 506 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1095, 3329330},\n    {-1}, {-1},\n#line 500 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1098, 7240587},\n    {-1},\n#line 499 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1100, 10663373},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 498 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1106, 12374766},\n#line 172 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1107, 2263842},\n#line 497 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1108, 13296127},\n    {-1}, {-1}, {-1},\n#line 92 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1112, 15308410},\n    {-1},\n#line 359 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1114, 10724259},\n#line 4 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1115, 15792383},\n#line 358 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1116, 10592673},\n    {-1}, {-1}, {-1}, {-1},\n#line 139 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1121, 3100495},\n#line 357 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1122, 10395294},\n#line 646 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1123, 4286945},\n#line 356 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1124, 10263708},\n#line 599 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1125, 11529966},\n#line 87 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1126, 9109643},\n    {-1},\n#line 529 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1128, 205},\n    {-1},\n#line 250 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1130, 10724259},\n    {-1},\n#line 249 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1132, 10592673},\n#line 415 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1133, 16777200},\n    {-1},\n#line 447 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1135, 13882323},\n    {-1},\n#line 134 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1137, 3100495},\n#line 248 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1138, 10395294},\n    {-1},\n#line 247 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1140, 10263708},\n    {-1},\n#line 745 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1142, 9141862},\n    {-1},\n#line 744 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1144, 13482646},\n#line 449 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1145, 16752762},\n    {-1},\n#line 348 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1147, 9079434},\n    {-1},\n#line 347 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1149, 8882055},\n#line 743 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1150, 15653038},\n#line 445 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1151, 13882323},\n#line 742 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1152, 16771002},\n#line 85 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1153, 11119017},\n    {-1},\n#line 346 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1155, 8750469},\n    {-1},\n#line 345 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1157, 8553090},\n    {-1}, {-1}, {-1}, {-1},\n#line 719 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1162, 14204888},\n#line 239 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1163, 9079434},\n    {-1},\n#line 238 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1165, 8882055},\n    {-1}, {-1}, {-1},\n#line 83 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1169, 11119017},\n    {-1},\n#line 237 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1171, 8750469},\n    {-1},\n#line 236 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1173, 8553090},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 603 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1182, 6720395},\n    {-1},\n#line 602 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1184, 9883085},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 601 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1190, 11464430},\n    {-1},\n#line 600 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1192, 12320767},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 589 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1203, 15657130},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 729 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1212, 4251856},\n#line 733 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1213, 34443},\n#line 732 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1214, 50637},\n    {-1}, {-1},\n#line 731 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1217, 58862},\n#line 730 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1218, 62975},\n#line 723 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1219, 9141131},\n#line 741 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1220, 16113331},\n#line 722 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1221, 13481421},\n#line 549 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1222, 16770273},\n    {-1}, {-1}, {-1}, {-1},\n#line 721 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1227, 15651566},\n    {-1},\n#line 720 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1229, 16769535},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 58 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1235, 13789470},\n#line 62 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1236, 9127187},\n#line 61 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1237, 13461021},\n#line 616 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1238, 9140069},\n#line 615 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1239, 13479829},\n#line 60 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1240, 15627809},\n#line 59 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1241, 16744228},\n#line 614 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1242, 15649709},\n#line 613 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1243, 16767673},\n    {-1}, {-1}, {-1}, {-1},\n#line 462 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1248, 15761536},\n#line 100 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1249, 35723},\n#line 53 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1250, 8388352},\n#line 57 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1251, 4557568},\n#line 56 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1252, 6737152},\n    {-1}, {-1},\n#line 55 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1255, 7794176},\n#line 54 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1256, 8388352},\n#line 645 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1257, 9136489},\n#line 644 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1258, 13474715},\n#line 150 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1259, 49151},\n    {-1},\n#line 643 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1261, 15643828},\n#line 642 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1262, 16761281},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 612 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1273, 16767673},\n#line 48 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1274, 6266528},\n#line 52 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1275, 5473931},\n#line 51 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1276, 8046029},\n    {-1}, {-1},\n#line 50 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1279, 9364974},\n#line 49 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1280, 10024447},\n    {-1}, {-1},\n#line 540 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1283, 3978097},\n    {-1}, {-1}, {-1},\n#line 450 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1287, 2142890},\n    {-1}, {-1}, {-1},\n#line 535 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1291, 9662683},\n    {-1}, {-1},\n#line 443 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1294, 15654274},\n    {-1},\n#line 754 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1296, 9145088},\n    {-1},\n#line 753 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1298, 13487360},\n#line 433 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1299, 8190976},\n    {-1},\n#line 641 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1301, 12357519},\n    {-1}, {-1},\n#line 752 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1304, 15658496},\n    {-1},\n#line 751 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1306, 16776960},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 154 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1316, 26763},\n    {-1},\n#line 153 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1318, 39629},\n    {-1},\n#line 94 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1320, 4734347},\n    {-1}, {-1}, {-1},\n#line 152 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1324, 45806},\n    {-1},\n#line 151 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1326, 49151},\n    {-1}, {-1}, {-1}, {-1},\n#line 562 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1331, 128},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1},\n#line 495 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1343, 7833753},\n    {-1}, {-1}, {-1}, {-1},\n#line 539 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1348, 6113163},\n    {-1},\n#line 538 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1350, 9005261},\n    {-1}, {-1},\n#line 567 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1353, 7048739},\n    {-1}, {-1},\n#line 537 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1356, 10451438},\n    {-1},\n#line 536 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1358, 11240191},\n#line 494 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1359, 7833753},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 409 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1367, 13458524},\n    {-1},\n#line 11 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1369, 8388564},\n#line 15 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1370, 4557684},\n#line 14 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1371, 6737322},\n    {-1}, {-1},\n#line 13 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1374, 7794374},\n#line 12 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1375, 8388564},\n#line 520 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1376, 205},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 584 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1383, 14315734},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 93 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1393, 9419919},\n    {-1}, {-1},\n#line 424 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1396, 9143886},\n    {-1},\n#line 423 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1398, 13485683},\n    {-1}, {-1}, {-1}, {-1},\n#line 541 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1403, 8087790},\n#line 422 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1404, 15656581},\n    {-1},\n#line 421 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1406, 16774799},\n#line 27 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1407, 0},\n#line 425 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1408, 15132410},\n    {-1}, {-1}, {-1},\n#line 42 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1412, 14596231},\n#line 46 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1413, 9139029},\n#line 45 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1414, 13478525},\n    {-1}, {-1},\n#line 44 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1417, 15648145},\n#line 43 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1418, 16765851},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 467 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1426, 8031115},\n#line 466 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1427, 11849165},\n    {-1},\n#line 542 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1429, 64154},\n#line 465 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1430, 13758190},\n#line 464 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1431, 14745599},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 588 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1440, 9127817},\n    {-1},\n#line 587 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1442, 13461961},\n    {-1},\n#line 3 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1444, 15792383},\n    {-1}, {-1}, {-1},\n#line 586 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1448, 15629033},\n#line 629 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1449, 11591910},\n#line 585 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1450, 16745466},\n#line 488 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1451, 8900346},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 755 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1458, 10145074},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 295 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1468, 11403055},\n#line 746 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1469, 16777215},\n#line 463 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1470, 14745599},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1},\n#line 660 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1484, 16032864},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 297 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1495, 0},\n    {-1}, {-1}, {-1},\n#line 564 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1499, 128},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 734 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1506, 15631086},\n    {-1},\n#line 492 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1508, 6323083},\n    {-1},\n#line 491 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1510, 9287373},\n#line 188 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1511, 0},\n    {-1}, {-1}, {-1}, {-1},\n#line 490 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1516, 10802158},\n    {-1},\n#line 489 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1518, 11592447},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 736 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1543, 13639824},\n#line 740 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1544, 9118290},\n#line 739 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1545, 13447800},\n    {-1}, {-1},\n#line 738 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1548, 15612556},\n#line 737 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1549, 16727702},\n    {-1}, {-1},\n#line 333 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1552, 6710886},\n#line 322 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1553, 5066061},\n    {-1}, {-1},\n#line 311 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1556, 3355443},\n#line 299 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1557, 1710618},\n    {-1}, {-1}, {-1},\n#line 441 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1561, 15761536},\n    {-1}, {-1},\n#line 96 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1564, 3100495},\n    {-1},\n#line 611 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1566, 16767673},\n    {-1},\n#line 224 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1568, 6710886},\n#line 213 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1569, 5066061},\n    {-1}, {-1},\n#line 202 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1572, 3355443},\n#line 190 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1573, 1710618},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 95 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1580, 3100495},\n    {-1}, {-1},\n#line 432 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1583, 8190976},\n    {-1},\n#line 640 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1585, 12357519},\n    {-1}, {-1},\n#line 501 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1588, 16777184},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 47 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1603, 6266528},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 523 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1609, 3978097},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 29 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1616, 16772045},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 81 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1634, 35723},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 530 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1642, 12211667},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 452 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1678, 8679679},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 90 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1686, 10040012},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 628 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1697, 11591910},\n    {-1},\n#line 534 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1699, 8009611},\n    {-1},\n#line 533 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1701, 11817677},\n    {-1}, {-1}, {-1},\n#line 522 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1705, 9662683},\n    {-1},\n#line 532 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1707, 13721582},\n    {-1},\n#line 531 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1709, 14706431},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 402 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1725, 8620931},\n    {-1},\n#line 401 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1727, 12701121},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 400 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1733, 14741216},\n#line 546 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1734, 1644912},\n#line 399 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1735, 15794160},\n    {-1}, {-1}, {-1},\n#line 454 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1739, 7833753},\n    {-1}, {-1},\n#line 149 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1742, 9112144},\n    {-1},\n#line 148 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1744, 13439094},\n    {-1}, {-1},\n#line 387 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1747, 1513239},\n    {-1}, {-1},\n#line 147 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1750, 15602313},\n    {-1},\n#line 146 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1752, 16716947},\n    {-1},\n#line 442 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1754, 14745599},\n#line 453 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1755, 7833753},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 278 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1763, 1513239},\n    {-1},\n#line 376 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1765, 1315860},\n    {-1},\n#line 455 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1767, 11584734},\n    {-1}, {-1}, {-1},\n#line 365 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1771, 1184274},\n    {-1},\n#line 97 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1773, 52945},\n    {-1}, {-1}, {-1},\n#line 548 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1777, 16121850},\n    {-1}, {-1}, {-1},\n#line 267 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1781, 1315860},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 256 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1787, 1184274},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 342 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1804, 8224125},\n#line 331 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1805, 6513507},\n    {-1}, {-1},\n#line 320 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1808, 4868682},\n#line 309 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1809, 3158064},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 555 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1817, 16770229},\n    {-1}, {-1},\n#line 233 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1820, 8224125},\n#line 222 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1821, 6513507},\n#line 341 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1822, 8026746},\n#line 330 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1823, 6381921},\n#line 211 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1824, 4868682},\n#line 200 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1825, 3158064},\n#line 319 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1826, 4671303},\n#line 308 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1827, 3026478},\n#line 340 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1828, 7895160},\n#line 329 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1829, 6184542},\n#line 473 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1830, 16448210},\n    {-1},\n#line 318 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1832, 4539717},\n#line 307 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1833, 2829099},\n    {-1}, {-1}, {-1}, {-1},\n#line 232 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1838, 8026746},\n#line 221 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1839, 6381921},\n    {-1}, {-1},\n#line 210 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1842, 4671303},\n#line 199 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1843, 3026478},\n#line 231 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1844, 7895160},\n#line 220 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1845, 6184542},\n    {-1}, {-1},\n#line 209 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1848, 4539717},\n#line 198 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1849, 2829099},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 420 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1858, 15787660},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 6 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1866, 16444375},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 735 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1872, 13639824},\n#line 547 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1873, 16121850},\n    {-1}, {-1},\n#line 121 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1876, 10040012},\n#line 125 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1877, 6824587},\n#line 124 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1878, 10105549},\n    {-1}, {-1},\n#line 123 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1881, 11680494},\n#line 122 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1882, 12533503},\n    {-1},\n#line 563 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1884, 128},\n#line 354 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1885, 986895},\n    {-1}, {-1},\n#line 750 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1888, 10145074},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1},\n#line 245 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1901, 986895},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 481 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1908, 9133925},\n#line 480 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1909, 13470869},\n    {-1}, {-1},\n#line 479 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1912, 15639213},\n#line 478 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1913, 16756409},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 10 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1923, 9143160},\n    {-1},\n#line 9 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1925, 13484208},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 8 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1931, 15654860},\n    {-1},\n#line 7 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1933, 16773083},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 339 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1942, 7697781},\n#line 328 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1943, 6052956},\n    {-1}, {-1},\n#line 317 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1946, 4342338},\n#line 306 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1947, 2697513},\n#line 591 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1948, 11529966},\n    {-1}, {-1},\n#line 343 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1951, 855309},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 230 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1958, 7697781},\n#line 219 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1959, 6052956},\n#line 749 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1960, 16776960},\n    {-1},\n#line 208 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1962, 4342338},\n#line 197 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1963, 2697513},\n#line 524 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1964, 8087790},\n    {-1}, {-1},\n#line 234 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1967, 855309},\n#line 431 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1968, 9143174},\n    {-1},\n#line 430 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1970, 13484485},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 429 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1976, 15655141},\n    {-1},\n#line 428 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1978, 16773365},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 170 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1985, 16775920},\n    {-1},\n#line 521 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1987, 12211667},\n    {-1},\n#line 543 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1989, 4772300},\n    {-1},\n#line 528 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1991, 6737322},\n#line 451 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1992, 8900346},\n#line 408 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1993, 9124450},\n    {-1},\n#line 407 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1995, 13459600},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 406 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2001, 15624871},\n    {-1},\n#line 405 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2003, 16740020},\n    {-1}, {-1}, {-1}, {-1},\n#line 338 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2008, 7566195},\n#line 327 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2009, 5855577},\n    {-1}, {-1},\n#line 316 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2012, 4210752},\n#line 305 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2013, 2500134},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 444 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2022, 16448210},\n    {-1},\n#line 229 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2024, 7566195},\n#line 218 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2025, 5855577},\n    {-1}, {-1},\n#line 207 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2028, 4210752},\n#line 196 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2029, 2500134},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 5 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2067, 16444375},\n#line 144 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2068, 49151},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 141 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2093, 9699539},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1},\n#line 69 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2107, 6591981},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1},\n#line 171 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2119, 16775920},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 525 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2130, 64154},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 74 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2141, 9144440},\n    {-1},\n#line 73 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2143, 13486257},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 72 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2149, 15657165},\n    {-1},\n#line 71 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2151, 16775388},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 169 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2161, 9116186},\n#line 168 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2162, 13444646},\n    {-1}, {-1},\n#line 167 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2165, 15608876},\n#line 166 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2166, 16724016},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 68 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2176, 6591981},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 36 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2185, 9055202},\n    {-1}, {-1},\n#line 545 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2188, 1644912},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1},\n#line 28 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2218, 16772045},\n    {-1},\n#line 111 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2220, 5597999},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 427 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2230, 16773365},\n    {-1},\n#line 115 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2232, 7244605},\n    {-1},\n#line 114 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2234, 10669402},\n    {-1},\n#line 448 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2236, 16758465},\n    {-1}, {-1}, {-1},\n#line 113 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2240, 12381800},\n    {-1},\n#line 112 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2242, 13303664},\n    {-1}, {-1}, {-1}, {-1},\n#line 300 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2247, 16777215},\n#line 604 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2248, 14381203},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1},\n#line 145 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2260, 16716947},\n    {-1}, {-1},\n#line 191 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2263, 16777215},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 747 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2279, 16119285},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 608 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2305, 9127773},\n#line 176 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2306, 16316671},\n#line 607 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2307, 13461641},\n    {-1}, {-1}, {-1},\n#line 388 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2311, 15066597},\n    {-1},\n#line 606 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2313, 15628703},\n    {-1},\n#line 605 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2315, 16745131},\n    {-1}, {-1}, {-1}, {-1},\n#line 377 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2320, 13421772},\n    {-1}, {-1},\n#line 366 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2323, 11776947},\n    {-1}, {-1}, {-1},\n#line 279 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2327, 15066597},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 268 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2336, 13421772},\n    {-1}, {-1},\n#line 257 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2339, 11776947},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 435 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2347, 16775885},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1},\n#line 439 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2359, 9144688},\n    {-1},\n#line 438 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2361, 13486501},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 437 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2367, 15657407},\n    {-1},\n#line 436 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2369, 16775885},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 355 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2380, 10066329},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 398 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2389, 15794160},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 246 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2396, 10066329},\n    {-1},\n#line 526 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2398, 4772300},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 344 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2413, 8355711},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 98 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2422, 9699539},\n    {-1}, {-1}, {-1}, {-1},\n#line 519 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2427, 6737322},\n    {-1},\n#line 235 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2429, 8355711},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 609 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2464, 16773077},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 477 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2482, 16758465},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 175 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2500, 16316671},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 404 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2511, 16738740},\n    {-1}, {-1},\n#line 31 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2514, 9055202},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 748 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2525, 16119285},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 290 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2544, 11403055},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 88 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2562, 5597999},\n#line 397 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2563, 16579836},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 386 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2572, 14935011},\n    {-1}, {-1},\n#line 375 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2575, 13224393},\n    {-1}, {-1}, {-1},\n#line 288 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2579, 16579836},\n    {-1},\n#line 396 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2581, 16448250},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 395 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2587, 16250871},\n#line 277 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2588, 14935011},\n    {-1},\n#line 385 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2590, 14737632},\n#line 266 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2591, 13224393},\n    {-1},\n#line 374 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2593, 13092807},\n    {-1}, {-1},\n#line 384 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2596, 14606046},\n#line 287 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2597, 16448250},\n    {-1},\n#line 373 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2599, 12895428},\n    {-1}, {-1}, {-1},\n#line 286 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2603, 16250871},\n    {-1}, {-1},\n#line 276 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2606, 14737632},\n    {-1}, {-1},\n#line 265 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2609, 13092807},\n    {-1}, {-1},\n#line 275 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2612, 14606046},\n    {-1}, {-1},\n#line 264 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2615, 12895428},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 364 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2632, 11579568},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1},\n#line 143 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2645, 16716947},\n    {-1}, {-1},\n#line 255 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2648, 11579568},\n    {-1},\n#line 363 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2650, 11382189},\n    {-1}, {-1}, {-1},\n#line 610 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2654, 16773077},\n    {-1},\n#line 362 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2656, 11250603},\n    {-1}, {-1},\n#line 70 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2659, 16775388},\n    {-1}, {-1}, {-1}, {-1},\n#line 456 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2664, 16777184},\n#line 353 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2665, 9868950},\n#line 254 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2666, 11382189},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 253 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2672, 11250603},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 244 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2681, 9868950},\n    {-1},\n#line 352 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2683, 9737364},\n#line 426 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2684, 16773365},\n    {-1}, {-1}, {-1}, {-1},\n#line 351 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2689, 9539985},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 243 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2699, 9737364},\n    {-1},\n#line 394 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2701, 16119285},\n    {-1}, {-1}, {-1},\n#line 242 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2705, 9539985},\n    {-1}, {-1}, {-1}, {-1},\n#line 383 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2710, 14408667},\n    {-1}, {-1},\n#line 372 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2713, 12763842},\n#line 403 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2714, 16738740},\n#line 434 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2715, 16775885},\n    {-1},\n#line 285 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2717, 16119285},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 274 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2726, 14408667},\n    {-1}, {-1},\n#line 263 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2729, 12763842},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 165 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2735, 11674146},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1},\n#line 393 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2767, 15921906},\n    {-1}, {-1},\n#line 361 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2770, 11053224},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 382 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2776, 14277081},\n    {-1}, {-1},\n#line 371 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2779, 12566463},\n    {-1}, {-1}, {-1},\n#line 284 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2783, 15921906},\n    {-1}, {-1},\n#line 252 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2786, 11053224},\n    {-1}, {-1}, {-1}, {-1},\n#line 86 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2791, 12433259},\n#line 273 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2792, 14277081},\n    {-1}, {-1},\n#line 262 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2795, 12566463},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 350 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2803, 9408399},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 241 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2819, 9408399},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 360 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2836, 10921638},\n    {-1}, {-1},\n#line 544 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2839, 13047173},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1},\n#line 251 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2852, 10921638},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 349 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2869, 9211020},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n#line 556 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2879, 16768685},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 240 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2885, 9211020},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 109 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2981, 12433259},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1},\n#line 557 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3013, 16768685},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 592 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3019, 14381203},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 561 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3070, 9140574},\n    {-1},\n#line 560 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3072, 13480843},\n    {-1}, {-1}, {-1}, {-1}, {-1},\n#line 559 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3078, 15650721},\n    {-1},\n#line 558 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3080, 16768685},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},\n    {-1},\n#line 527 \"/dev/stdin\"\n    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3478, 13047173}\n  };\n#if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 6) > 4) || (defined __clang__ && __clang_major__ >= 3)\n#pragma GCC diagnostic pop\n#endif\n\nconst struct Keyword *\nin_color_name_set (register const char *str, register size_t len)\n{\n  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n    {\n      register unsigned int key = color_name_hash (str, len);\n\n      if (key <= MAX_HASH_VALUE)\n        {\n          register int o = color_names[key].name;\n          if (o >= 0)\n            {\n              register const char *s = o + stringpool;\n\n              if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\\0')\n                return &color_names[key];\n            }\n        }\n    }\n  return (struct Keyword *) 0;\n}\n#line 756 \"/dev/stdin\"\n\n"
  },
  {
    "path": "kitty/colors.c",
    "content": "/*\n * colors.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"state.h\"\n#include <structmember.h>\n#include \"colors.h\"\n#include \"color-names.h\"\n#ifdef __APPLE__\n// Needed for strod_l\n#include <xlocale.h>\n#endif\n\n\nstatic uint32_t FG_BG_256[256] = {\n    0x000000,  // 0\n    0xcd0000,  // 1\n    0x00cd00,  // 2\n    0xcdcd00,  // 3\n    0x0000ee,  // 4\n    0xcd00cd,  // 5\n    0x00cdcd,  // 6\n    0xe5e5e5,  // 7\n    0x7f7f7f,  // 8\n    0xff0000,  // 9\n    0x00ff00,  // 10\n    0xffff00,  // 11\n    0x5c5cff,  // 12\n    0xff00ff,  // 13\n    0x00ffff,  // 14\n    0xffffff,  // 15\n};\n\nstatic void\ninit_FG_BG_table(void) {\n    if (UNLIKELY(FG_BG_256[255] == 0)) {\n        // colors 16..232: the 6x6x6 color cube\n        const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};\n        uint8_t i, j=16;\n        for(i = 0; i < 216; i++, j++) {\n            uint8_t r = valuerange[(i / 36) % 6], g = valuerange[(i / 6) % 6], b = valuerange[i % 6];\n            FG_BG_256[j] = (r << 16) | (g << 8) | b;\n        }\n        // colors 232..255: grayscale\n        for(i = 0; i < 24; i++, j++) {\n            uint8_t v = 8 + i * 10;\n            FG_BG_256[j] = (v << 16) | (v << 8) | v;\n        }\n    }\n}\n\nstatic PyObject*\ncreate_256_color_table(void) {\n    init_FG_BG_table();\n    PyObject *ans = PyTuple_New(arraysz(FG_BG_256));\n    if (ans == NULL) return PyErr_NoMemory();\n    for (size_t i=0; i < arraysz(FG_BG_256); i++) {\n        PyObject *temp = PyLong_FromUnsignedLong(FG_BG_256[i]);\n        if (temp == NULL) { Py_CLEAR(ans); return NULL; }\n        PyTuple_SET_ITEM(ans, i, temp);\n    }\n    return ans;\n}\n\nstatic void\nset_transparent_background_colors(TransparentDynamicColor *dest, PyObject *src) {\n    memset(dest, 0, sizeof(((ColorProfile*)0)->configured_transparent_colors));\n    for (Py_ssize_t i = 0; i < MIN(PyTuple_GET_SIZE(src), (Py_ssize_t)arraysz(((ColorProfile*)0)->configured_transparent_colors)); i++) {\n        PyObject *e = PyTuple_GET_ITEM(src, i);\n        dest[i].color = ((Color*)(PyTuple_GET_ITEM(e, 0)))->color.val & 0xffffff;\n        dest[i].opacity = (float)PyFloat_AsDouble(PyTuple_GET_ITEM(e, 1));\n        dest[i].is_set = true;\n    }\n}\n\nstatic bool\nset_configured_colors(ColorProfile *self, PyObject *opts) {\n#define n(which, attr) { \\\n    RAII_PyObject(t, PyObject_GetAttrString(opts, #attr)); \\\n    if (t == NULL) return false; \\\n    if (t == Py_None) { self->configured.which.rgb = 0; self->configured.which.type = COLOR_IS_SPECIAL; } \\\n    else if (PyLong_Check(t)) { \\\n        unsigned int x = PyLong_AsUnsignedLong(t); \\\n        self->configured.which.rgb = x & 0xffffff; \\\n        self->configured.which.type = COLOR_IS_RGB; \\\n    } else if (PyObject_TypeCheck(t, &Color_Type)) { \\\n        Color *c = (Color*)t; \\\n        self->configured.which.rgb = c->color.rgb; \\\n        self->configured.which.type = COLOR_IS_RGB; \\\n    } else { PyErr_SetString(PyExc_TypeError, \"colors must be integers or Color objects\"); return false; } \\\n}\n\n    n(default_fg, foreground); n(default_bg, background);\n    n(cursor_color, cursor); n(cursor_text_color, cursor_text_color);\n    n(highlight_fg, selection_foreground); n(highlight_bg, selection_background);\n    n(visual_bell_color, visual_bell_color);\n#undef n\n    RAII_PyObject(src, PyObject_GetAttrString(opts, \"transparent_background_colors\"));\n    if (!src) { PyErr_SetString(PyExc_TypeError, \"No transparent_background_colors on opts object\"); return false; }\n    set_transparent_background_colors(self->configured_transparent_colors, src);\n    return PyErr_Occurred() ? false : true;\n}\n\nstatic bool\nset_mark_colors(ColorProfile *self, PyObject *opts) {\n    char fgattr[] = \"mark?_foreground\", bgattr[] = \"mark?_background\";\n#define n(i, attr, which) { \\\n    attr[4] = '1' + i; \\\n    RAII_PyObject(t, PyObject_GetAttrString(opts, attr)); \\\n    if (t == NULL) return false; \\\n    if (!PyObject_TypeCheck(t, &Color_Type)) { PyErr_SetString(PyExc_TypeError, \"mark color is not Color object\"); return false; } \\\n    Color *c = (Color*)t; self->which[i] = c->color.rgb; \\\n}\n#define m(i) n(i, fgattr, mark_foregrounds); n(i, bgattr, mark_backgrounds);\n    m(0); m(1); m(2);\n#undef m\n#undef n\n    return true;\n}\n\nstatic bool\nset_colortable(ColorProfile *self, PyObject *opts) {\n    RAII_PyObject(ct, PyObject_GetAttrString(opts, \"color_table\"));\n    if (!ct) return false;\n    RAII_PyObject(ret, PyObject_CallMethod(ct, \"buffer_info\", NULL));\n    if (!ret) return false;\n    unsigned long *color_table = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0));\n    size_t count = PyLong_AsSize_t(PyTuple_GET_ITEM(ret, 1));\n    if (!color_table || count != arraysz(FG_BG_256)) { PyErr_SetString(PyExc_TypeError, \"color_table has incorrect length\"); return false; }\n    RAII_PyObject(r2, PyObject_GetAttrString(ct, \"itemsize\")); if (!r2) return false;\n    size_t itemsize = PyLong_AsSize_t(r2);\n    if (itemsize != sizeof(unsigned long)) { PyErr_Format(PyExc_TypeError, \"color_table has incorrect itemsize: %zu\", itemsize); return false; }\n    for (size_t i = 0; i < arraysz(FG_BG_256); i++) self->color_table[i] = color_table[i];\n    memcpy(self->orig_color_table, self->color_table, arraysz(self->color_table) * sizeof(self->color_table[0]));\n    return true;\n}\n\n\nstatic PyObject*\nnew_cp(PyTypeObject *type, PyObject *args, PyObject *kwds) {\n    PyObject *opts = global_state.options_object;\n    ColorProfile *self;\n    static const char* kw[] = {\"opts\", NULL};\n    if (args && !PyArg_ParseTupleAndKeywords(args, kwds, \"|O\", (char**)kw, &opts)) return NULL;\n    self = (ColorProfile *)type->tp_alloc(type, 0);\n    RAII_PyObject(ans, (PyObject*)self);\n    if (self != NULL) {\n        init_FG_BG_table();\n        if (opts) {\n            if (!set_configured_colors(self, opts)) return NULL;\n            if (!set_mark_colors(self, opts)) return NULL;\n            if (!set_colortable(self, opts)) return NULL;\n        } else {\n            memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256));\n            memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256));\n        }\n        self->dirty = true;\n        Py_INCREF(ans);\n    }\n    return ans;\n}\n\nstatic void\ndealloc_cp(ColorProfile* self) {\n    if (self->color_stack) free(self->color_stack);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nColorProfile*\nalloc_color_profile(void) {\n    return (ColorProfile*)new_cp(&ColorProfile_Type, NULL, NULL);\n}\n\n\nvoid\ncopy_color_profile(ColorProfile *dest, ColorProfile *src) {\n    memcpy(dest->color_table, src->color_table, sizeof(dest->color_table));\n    memcpy(dest->orig_color_table, src->orig_color_table, sizeof(dest->color_table));\n    memcpy(&dest->configured, &src->configured, sizeof(dest->configured));\n    memcpy(&dest->overridden, &src->overridden, sizeof(dest->overridden));\n    memcpy(dest->overriden_transparent_colors, src->overriden_transparent_colors, sizeof(dest->overriden_transparent_colors));\n    memcpy(dest->configured_transparent_colors, src->configured_transparent_colors, sizeof(dest->configured_transparent_colors));\n    dest->dirty = true;\n}\n\nstatic void\npatch_color_table(const char *key, PyObject *profiles, PyObject *spec, size_t which, int change_configured) {\n    PyObject *v = PyDict_GetItemString(spec, key);\n    if (v && PyLong_Check(v)) {\n        color_type color = PyLong_AsUnsignedLong(v);\n        for (Py_ssize_t j = 0; j < PyTuple_GET_SIZE(profiles); j++) {\n            ColorProfile *self = (ColorProfile*)PyTuple_GET_ITEM(profiles, j);\n            self->color_table[which] = color;\n            if (change_configured) self->orig_color_table[which] = color;\n            self->dirty = true;\n        }\n    }\n\n}\n\n#define patch_mark_color(key, profiles, spec, array, i) { \\\n    PyObject *v = PyDict_GetItemString(spec, key); \\\n    if (v && PyLong_Check(v)) { \\\n        color_type color = PyLong_AsUnsignedLong(v); \\\n        for (Py_ssize_t j = 0; j < PyTuple_GET_SIZE(profiles); j++) { \\\n            ColorProfile *self = (ColorProfile*)PyTuple_GET_ITEM(profiles, j); \\\n            self->array[i] = color; \\\n            self->dirty = true; \\\n} } }\n\n\nstatic PyObject*\npatch_color_profiles(PyObject *module UNUSED, PyObject *args) {\n    PyObject *spec, *transparent_background_colors, *profiles, *v; ColorProfile *self; int change_configured;\n    if (!PyArg_ParseTuple(args, \"O!O!O!p\", &PyDict_Type, &spec, &PyTuple_Type, &transparent_background_colors, &PyTuple_Type, &profiles, &change_configured)) return NULL;\n    char key[32] = {0};\n    for (size_t i = 0; i < arraysz(FG_BG_256); i++) {\n        snprintf(key, sizeof(key) - 1, \"color%zu\", i);\n        patch_color_table(key, profiles, spec, i, change_configured);\n    }\n    for (size_t i = 1; i <= MARK_MASK; i++) {\n#define S(which, i) snprintf(key, sizeof(key) - 1, \"mark%zu_\" #which, i); patch_mark_color(key, profiles, spec, mark_##which##s, i)\n    S(background, i); S(foreground, i);\n#undef S\n    }\n#define SI(profile_name) \\\n    DynamicColor color; \\\n    if (PyLong_Check(v)) { \\\n        color.rgb = PyLong_AsUnsignedLong(v);  color.type = COLOR_IS_RGB; \\\n    } else { color.rgb = 0; color.type = COLOR_IS_SPECIAL; }\\\n    self->overridden.profile_name = color; \\\n    if (change_configured) self->configured.profile_name = color; \\\n    self->dirty = true;\n\n#define S(config_name, profile_name) { \\\n    v = PyDict_GetItemString(spec, #config_name); \\\n    if (v) { \\\n        for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) { \\\n            self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i); \\\n            SI(profile_name); \\\n        } \\\n    } \\\n}\n        S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color);\n        S(selection_foreground, highlight_fg); S(selection_background, highlight_bg);\n        S(cursor_text_color, cursor_text_color); S(visual_bell_color, visual_bell_color);\n#undef SI\n#undef S\n    for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) {\n        self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i);\n        set_transparent_background_colors(self->overriden_transparent_colors, transparent_background_colors);\n        if (change_configured) set_transparent_background_colors(self->configured_transparent_colors, transparent_background_colors);\n    }\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nbool\ncolorprofile_to_transparent_color(const ColorProfile *self, unsigned index, color_type *color, float *opacity) {\n    *color = UINT32_MAX; *opacity = 1.0;\n    if (index < arraysz(self->configured_transparent_colors)) {\n        if (self->overriden_transparent_colors[index].is_set) {\n            *color = self->overriden_transparent_colors[index].color; *opacity = self->overriden_transparent_colors[index].opacity;\n            if (*opacity < 0) *opacity = OPT(background_opacity);\n            return true;\n        }\n        if (self->configured_transparent_colors[index].is_set) {\n            *color = self->configured_transparent_colors[index].color; *opacity = self->configured_transparent_colors[index].opacity;\n            if (*opacity < 0) *opacity = OPT(background_opacity);\n            return true;\n        }\n    }\n    return false;\n}\n\nDynamicColor\ncolorprofile_to_color(const ColorProfile *self, DynamicColor entry, DynamicColor defval) {\n    switch(entry.type) {\n        case COLOR_NOT_SET:\n            return defval;\n        case COLOR_IS_INDEX: {\n            DynamicColor ans;\n            ans.rgb = self->color_table[entry.rgb & 0xff] & 0xffffff;\n            ans.type = COLOR_IS_RGB;\n            return ans;\n        }\n        case COLOR_IS_RGB:\n        case COLOR_IS_SPECIAL:\n            return entry;\n    }\n    return entry;\n}\n\ncolor_type\ncolorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, DynamicColor defval, DynamicColor fallback, DynamicColor fallback_defval) {\n    switch(entry.type) {\n        case COLOR_NOT_SET:\n        case COLOR_IS_SPECIAL:\n            if (defval.type == COLOR_IS_SPECIAL) return colorprofile_to_color(self, fallback, fallback_defval).rgb;\n            return defval.rgb;\n        case COLOR_IS_RGB:\n            return entry.rgb;\n        case COLOR_IS_INDEX:\n            return self->color_table[entry.rgb & 0xff] & 0xffffff;\n    }\n    return entry.rgb;\n}\nstatic Color* alloc_color(unsigned char r, unsigned char g, unsigned char b, unsigned a);\n\nstatic bool\ncolortable_colors_into_dict(ColorProfile *self, unsigned start, unsigned limit, PyObject *ans) {\n    static char buf[32] = {'c', 'o', 'l', 'o', 'r', 0};\n    for (unsigned i = start; i < limit; i++) {\n        snprintf(buf + 5, sizeof(buf) - 6, \"%u\", i);\n        PyObject *val = PyLong_FromUnsignedLong(self->color_table[i]);\n        if (!val) return false;\n        int ret = PyDict_SetItemString(ans, buf, val);\n        Py_DECREF(val);\n        if (ret != 0) return false;\n    }\n    return true;\n}\n\nstatic PyObject*\nbasic_colors(ColorProfile *self, PyObject *args UNUSED) {\n#define basic_colors_doc \"Return the basic colors as a dictionary of color_name to integer or None (names are the same as used in kitty.conf)\"\n    RAII_PyObject(ans, PyDict_New()); if (ans == NULL) return NULL;\n    if (!colortable_colors_into_dict(self, 0, 16, ans)) return NULL;\n\n#define D(attr, name) { \\\n    unsigned long c = colorprofile_to_color(self, self->overridden.attr, self->configured.attr).rgb; \\\n    PyObject *val = PyLong_FromUnsignedLong(c); if (!val) return NULL; \\\n    int ret = PyDict_SetItemString(ans, #name, val); Py_DECREF(val); \\\n    if (ret != 0) return NULL; \\\n}\n\n    D(default_fg, foreground); D(default_bg, background);\n#undef D\n    return Py_NewRef(ans);\n}\n\nstatic PyObject*\nas_dict(ColorProfile *self, PyObject *args UNUSED) {\n#define as_dict_doc \"Return all colors as a dictionary of color_name to integer or None (names are the same as used in kitty.conf)\"\n    RAII_PyObject(ans, PyDict_New()); if (ans == NULL) return NULL;\n    if (!colortable_colors_into_dict(self, 0, arraysz(self->color_table), ans)) return NULL;\n#define D(attr, name) { \\\n    if (self->overridden.attr.type != COLOR_NOT_SET) { \\\n        int ret; PyObject *val; \\\n        if (self->overridden.attr.type == COLOR_IS_SPECIAL) { \\\n            val = Py_NewRef(Py_None); \\\n        } else { \\\n            unsigned long c = colorprofile_to_color(self, self->overridden.attr, self->configured.attr).rgb; \\\n            val = PyLong_FromUnsignedLong(c); \\\n        } \\\n        if (!val) { return NULL; } \\\n        ret = PyDict_SetItemString(ans, #name, val); \\\n        Py_DECREF(val); \\\n        if (ret != 0) { return NULL; } \\\n    }}\n    D(default_fg, foreground); D(default_bg, background);\n    D(cursor_color, cursor); D(cursor_text_color, cursor_text); D(highlight_fg, selection_foreground);\n    D(highlight_bg, selection_background); D(visual_bell_color, visual_bell_color);\n    RAII_PyObject(transparent_background_colors, PyList_New(0));\n    if (!transparent_background_colors) return NULL;\n    for (size_t i = 0; i < arraysz(self->overriden_transparent_colors); i++) {\n        TransparentDynamicColor *c = NULL;\n        if (self->overriden_transparent_colors[i].is_set) c = self->overriden_transparent_colors + i;\n        else if (self->configured_transparent_colors[i].is_set) c = self->configured_transparent_colors + i;\n        if (c) {\n            RAII_PyObject(t, Py_BuildValue(\"Nf\", alloc_color((c->color >> 16) & 0xff, (c->color >> 8) & 0xff, c->color & 0xff, 0), c->opacity));\n            if (!t) return NULL;\n            if (PyList_Append(transparent_background_colors, t) != 0) return NULL;\n        }\n    }\n    if (PyList_GET_SIZE(transparent_background_colors)) {\n        RAII_PyObject(t, PyList_AsTuple(transparent_background_colors));\n        if (!t) return NULL;\n        if (PyDict_SetItemString(ans, \"transparent_background_colors\", t) != 0) return NULL;\n    }\n#undef D\n    return Py_NewRef(ans);\n}\n\nstatic PyObject*\nas_color(ColorProfile *self, PyObject *val) {\n#define as_color_doc \"Convert the specified terminal color into an (r, g, b) tuple based on the current profile values\"\n    if (!PyLong_Check(val)) { PyErr_SetString(PyExc_TypeError, \"val must be an int\"); return NULL; }\n    unsigned long entry = PyLong_AsUnsignedLong(val);\n    unsigned int t = entry & 0xFF;\n    uint8_t r;\n    uint32_t col = 0;\n    switch(t) {\n        case 1:\n            r = (entry >> 8) & 0xff;\n            col = self->color_table[r];\n            break;\n        case 2:\n            col = entry >> 8;\n            break;\n        default:\n            Py_RETURN_NONE;\n    }\n    Color *ans = PyObject_New(Color, &Color_Type);\n    if (ans) {\n        ans->color.val = 0;\n        ans->color.rgb = col;\n    }\n    return (PyObject*)ans;\n}\n\nstatic PyObject*\nreset_color_table(ColorProfile *self, PyObject *a UNUSED) {\n#define reset_color_table_doc \"Reset all customized colors back to defaults\"\n    memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256));\n    self->dirty = true;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nreset_color(ColorProfile *self, PyObject *val) {\n#define reset_color_doc \"Reset the specified color\"\n    uint8_t i = PyLong_AsUnsignedLong(val) & 0xff;\n    self->color_table[i] = self->orig_color_table[i];\n    self->dirty = true;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nset_color(ColorProfile *self, PyObject *args) {\n#define set_color_doc \"Set the specified color\"\n    unsigned char i;\n    unsigned long val;\n    if (!PyArg_ParseTuple(args, \"Bk\", &i, &val)) return NULL;\n    self->color_table[i] = val;\n    self->dirty = true;\n    Py_RETURN_NONE;\n}\n\nvoid\ncopy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size_t stride) {\n    size_t i;\n    stride = MAX(1u, stride);\n    for (i = 0, buf = buf + offset; i < arraysz(self->color_table); i++, buf += stride) *buf = self->color_table[i];\n    // Copy the mark colors\n    for (i = 0; i < arraysz(self->mark_backgrounds); i++) {\n        *buf = self->mark_backgrounds[i]; buf += stride;\n    }\n    for (i = 0; i < arraysz(self->mark_foregrounds); i++) {\n        *buf = self->mark_foregrounds[i]; buf += stride;\n    }\n    self->dirty = false;\n}\n\nstatic void\npush_onto_color_stack_at(ColorProfile *self, unsigned int i) {\n    self->color_stack[i].dynamic_colors = self->overridden;\n    memcpy(self->color_stack[i].transparent_colors, self->overriden_transparent_colors, sizeof(self->overriden_transparent_colors));\n    self->color_stack[i].dynamic_colors = self->overridden;\n    memcpy(self->color_stack[i].color_table, self->color_table, sizeof(self->color_stack->color_table));\n}\n\nstatic void\ncopy_from_color_stack_at(ColorProfile *self, unsigned int i) {\n    self->overridden = self->color_stack[i].dynamic_colors;\n    memcpy(self->color_table, self->color_stack[i].color_table, sizeof(self->color_table));\n    memcpy(self->overriden_transparent_colors, self->color_stack[i].transparent_colors, sizeof(self->overriden_transparent_colors));\n}\n\nbool\ncolorprofile_push_colors(ColorProfile *self, unsigned int idx) {\n    if (idx > 10) return false;\n    size_t sz = idx ? idx : self->color_stack_idx + 1;\n    sz = MIN(10u, sz);\n    if (self->color_stack_sz < sz) {\n        self->color_stack = realloc(self->color_stack, sz * sizeof(self->color_stack[0]));\n        if (self->color_stack == NULL) fatal(\"Out of memory while ensuring space for %zu elements in color stack\", sz);\n        memset(self->color_stack + self->color_stack_sz, 0, (sz - self->color_stack_sz) * sizeof(self->color_stack[0]));\n        self->color_stack_sz = sz;\n    }\n    if (idx == 0) {\n        if (self->color_stack_idx >= self->color_stack_sz) {\n            memmove(self->color_stack, self->color_stack + 1, (self->color_stack_sz - 1) * sizeof(self->color_stack[0]));\n            idx = self->color_stack_sz - 1;\n        } else idx = self->color_stack_idx++;\n        push_onto_color_stack_at(self, idx);\n        return true;\n    }\n    idx -= 1;\n    if (idx < self->color_stack_sz) {\n        push_onto_color_stack_at(self, idx);\n        return true;\n    }\n    return false;\n}\n\nvoid\ncolorprofile_reset(ColorProfile *self) {\n    memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256));\n    self->dirty = true;\n    self->color_stack_idx = 0;\n    zero_at_ptr(&self->overridden);\n    for (unsigned i = 0; i < arraysz(self->overriden_transparent_colors); i++) {\n        zero_at_ptr(self->overriden_transparent_colors + i);\n    }\n    for (unsigned i = 0; i < self->color_stack_sz; i++) {\n        zero_at_ptr(self->color_stack + i);\n    }\n}\n\nbool\ncolorprofile_pop_colors(ColorProfile *self, unsigned int idx) {\n    if (idx == 0) {\n        if (!self->color_stack_idx) return false;\n        copy_from_color_stack_at(self, --self->color_stack_idx);\n        memset(self->color_stack + self->color_stack_idx, 0, sizeof(self->color_stack[0]));\n        return true;\n    }\n    idx -= 1;\n    if (idx < self->color_stack_sz) {\n        copy_from_color_stack_at(self, idx);\n        return true;\n    }\n    return false;\n}\n\nvoid\ncolorprofile_report_stack(ColorProfile *self, unsigned int *idx, unsigned int *count) {\n    *count = self->color_stack_idx;\n    *idx = self->color_stack_idx ? self->color_stack_idx - 1 : 0;\n}\n\nstatic PyObject*\ncolor_table_address(ColorProfile *self, PyObject *a UNUSED) {\n#define color_table_address_doc \"Pointer address to start of color table\"\n    return PyLong_FromVoidPtr((void*)self->color_table);\n}\n\nstatic PyObject*\ndefault_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {\n    return create_256_color_table();\n}\n\n// Boilerplate {{{\n\n#define CGETSET(name, nullable) \\\n    static PyObject* name##_get(ColorProfile *self, void UNUSED *closure) {  \\\n        DynamicColor ans = colorprofile_to_color(self, self->overridden.name, self->configured.name);  \\\n        if (ans.type == COLOR_IS_SPECIAL) { \\\n            if (nullable) Py_RETURN_NONE; \\\n            return (PyObject*)alloc_color(0, 0, 0, 0); \\\n        } \\\n        return (PyObject*)alloc_color((ans.rgb >> 16) & 0xff, (ans.rgb >> 8) & 0xff, ans.rgb & 0xff, 0); \\\n    } \\\n    static int name##_set(ColorProfile *self, PyObject *v, void UNUSED *closure) { \\\n        if (v == NULL) { self->overridden.name.val = 0; return 0; } \\\n        if (PyLong_Check(v)) { \\\n            unsigned long val = PyLong_AsUnsignedLong(v); \\\n            self->overridden.name.rgb = val & 0xffffff; \\\n            self->overridden.name.type = COLOR_IS_RGB; \\\n        } else if (PyObject_TypeCheck(v, &Color_Type)) { \\\n            Color *c = (Color*)v; self->overridden.name.rgb = c->color.rgb; self->overridden.name.type = COLOR_IS_RGB; \\\n        } else if (v == Py_None) { \\\n            if (!nullable) { PyErr_SetString(PyExc_TypeError, #name \" cannot be set to None\"); return -1; } \\\n            self->overridden.name.type = COLOR_IS_SPECIAL; self->overridden.name.rgb = 0; \\\n        } \\\n        self->dirty = true; return 0; \\\n    }\n\nCGETSET(default_fg, false)\nCGETSET(default_bg, false)\nCGETSET(cursor_color, true)\nCGETSET(cursor_text_color, true)\nCGETSET(highlight_fg, true)\nCGETSET(highlight_bg, true)\nCGETSET(visual_bell_color, true)\n#undef CGETSET\n\nstatic PyGetSetDef cp_getsetters[] = {\n    GETSET(default_fg)\n    GETSET(default_bg)\n    GETSET(cursor_color)\n    GETSET(cursor_text_color)\n    GETSET(highlight_fg)\n    GETSET(highlight_bg)\n    GETSET(visual_bell_color)\n    {NULL}  /* Sentinel */\n};\n\n\nstatic PyMemberDef cp_members[] = {\n    {NULL}\n};\n\nstatic PyObject*\nreload_from_opts(ColorProfile *self, PyObject *args UNUSED) {\n    PyObject *opts = global_state.options_object;\n    if (!PyArg_ParseTuple(args, \"|O\", &opts)) return NULL;\n    self->dirty = true;\n    if (!set_configured_colors(self, opts)) return NULL;\n    if (!set_mark_colors(self, opts)) return NULL;\n    if (!set_colortable(self, opts)) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nget_transparent_background_color(ColorProfile *self, PyObject *index) {\n    if (!PyLong_Check(index)) { PyErr_SetString(PyExc_TypeError, \"index must be an int\"); return NULL; }\n    unsigned long idx = PyLong_AsUnsignedLong(index);\n    if (PyErr_Occurred()) return NULL;\n    if (idx >= arraysz(self->configured_transparent_colors)) Py_RETURN_NONE;\n    TransparentDynamicColor *c = self->overriden_transparent_colors[idx].is_set ? self->overriden_transparent_colors + idx : self->configured_transparent_colors + idx;\n    if (!c->is_set) Py_RETURN_NONE;\n    float opacity = c->opacity >= 0 ? c->opacity : OPT(background_opacity);\n    return (PyObject*)alloc_color((c->color >> 16) & 0xff, (c->color >> 8) & 0xff, c->color & 0xff, (unsigned)(255.f * opacity));\n}\n\nstatic PyObject*\nset_transparent_background_color(ColorProfile *self, PyObject *const *args, Py_ssize_t nargs) {\n    if (nargs < 1) { PyErr_SetString(PyExc_TypeError, \"must specify index\"); return NULL; }\n    if (!PyLong_Check(args[0])) { PyErr_SetString(PyExc_TypeError, \"index must be an int\"); return NULL; }\n    unsigned long idx = PyLong_AsUnsignedLong(args[0]);\n    if (PyErr_Occurred()) return NULL;\n    if (idx >= arraysz(self->configured_transparent_colors)) Py_RETURN_NONE;\n    if (nargs < 2) { self->overriden_transparent_colors[idx].is_set = false; Py_RETURN_NONE; }\n    if (!PyObject_TypeCheck(args[1], &Color_Type)) { PyErr_SetString(PyExc_TypeError, \"color must be Color object\"); return NULL; }\n    Color *c = (Color*)args[1];\n    float opacity = (float)(c->color.alpha) / 255.f;\n    if (nargs > 2 && PyFloat_Check(args[2])) opacity = (float)PyFloat_AsDouble(args[2]);\n    self->overriden_transparent_colors[idx].is_set = true;\n    self->overriden_transparent_colors[idx].color = c->color.rgb;\n    self->overriden_transparent_colors[idx].opacity = MAX(-1.f, MIN(opacity, 1.f));\n    Py_RETURN_NONE;\n}\n\nstatic PyMethodDef cp_methods[] = {\n    METHOD(reset_color_table, METH_NOARGS)\n    METHOD(as_dict, METH_NOARGS)\n    METHOD(basic_colors, METH_NOARGS)\n    METHOD(color_table_address, METH_NOARGS)\n    METHOD(as_color, METH_O)\n    METHOD(reset_color, METH_O)\n    METHOD(set_color, METH_VARARGS)\n    METHODB(get_transparent_background_color, METH_O),\n    METHODB(reload_from_opts, METH_VARARGS),\n    {\"set_transparent_background_color\", (PyCFunction)(void(*)(void))set_transparent_background_color, METH_FASTCALL, \"\"},\n    {NULL}  /* Sentinel */\n};\n\n\nPyTypeObject ColorProfile_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.ColorProfile\",\n    .tp_basicsize = sizeof(ColorProfile),\n    .tp_dealloc = (destructor)dealloc_cp,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"ColorProfile\",\n    .tp_members = cp_members,\n    .tp_methods = cp_methods,\n    .tp_getset = cp_getsetters,\n    .tp_new = new_cp,\n};\n// }}}\n\nstatic Color*\nalloc_color(unsigned char r, unsigned char g, unsigned char b, unsigned a) {\n    Color *self = (Color *)(&Color_Type)->tp_alloc(&Color_Type, 0);\n    if (self != NULL) {\n        self->color.r = r; self->color.g = g; self->color.b = b; self->color.a = a;\n    }\n    return self;\n}\n\nstatic PyObject *\nnew_color(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds) {\n    static const char* kwlist[] = {\"red\", \"green\", \"blue\", \"alpha\", NULL};\n    unsigned char r = 0, g = 0, b = 0, a = 0;\n    if (!PyArg_ParseTupleAndKeywords(args, kwds, \"|BBBB\", (char**)kwlist, &r, &g, &b, &a)) return NULL;\n    return (PyObject*) alloc_color(r, g, b, a);\n}\n\nstatic PyObject *\ncolor_vectorcall(PyObject *type UNUSED, PyObject *const *args, size_t nargsf, PyObject *kwnames) {\n    const Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);\n    if (nargs > 4) {\n        PyErr_SetString(PyExc_TypeError, \"Color() takes at most 4 arguments\");\n        return NULL;\n    }\n    unsigned char rgba[4] = {0, 0, 0, 0};\n    for (Py_ssize_t i = 0; i < nargs; i++) {\n        unsigned long val = PyLong_AsUnsignedLongMask(args[i]);\n        if (val == (unsigned long)-1 && PyErr_Occurred()) return NULL;\n        rgba[i] = (unsigned char)val;\n    }\n    if (kwnames) {\n        const unsigned num = PyTuple_GET_SIZE(kwnames);\n        for (unsigned i = 0; i < num; i++) {\n            const char *name = PyUnicode_AsUTF8(PyTuple_GET_ITEM(kwnames, i));\n            if (!name) return NULL;\n            int idx;\n#define C(ch, i, expected) case ch: idx = i; if (strcmp(name, expected) != 0) { \\\n            PyErr_Format(PyExc_TypeError, \"Color() got an unexpected keyword argument '%s'\", name); return NULL; }; break;\n            switch(name[0]) {\n                C('r', 0, \"red\"); C('g', 1, \"green\"); C('b', 2, \"blue\"); C('a', 3, \"alpha\");\n                default:\n                PyErr_Format(PyExc_TypeError, \"Color() got an unexpected keyword argument '%s'\", name); return NULL;\n            }\n#undef C\n            if (idx < nargs) {\n                PyErr_Format(PyExc_TypeError, \"Color() got multiple values for argument '%s'\", name);\n                return NULL;\n            }\n            unsigned long val = PyLong_AsUnsignedLongMask(args[nargs + i]);\n            if (val == (unsigned long)-1 && PyErr_Occurred()) return NULL;\n            rgba[idx] = (unsigned char)val;\n        }\n    }\n    return (PyObject*)alloc_color(rgba[0], rgba[1], rgba[2], rgba[3]);\n}\n\nstatic PyObject*\nColor_as_int(Color *self) {\n    return PyLong_FromUnsignedLong(self->color.val);\n}\n\nstatic PyObject*\ncolor_truediv(Color *self, PyObject *divisor) {\n    RAII_PyObject(o, PyNumber_Float(divisor));\n    if (o == NULL) return NULL;\n    double r = self->color.r, g = self->color.g, b = self->color.b, a = self->color.a;\n    double d = PyFloat_AS_DOUBLE(o) * 255.;\n    return Py_BuildValue(\"dddd\", r/d, g/d, b/d, a/d);\n}\n\nstatic PyNumberMethods color_number_methods = {\n    .nb_int = (unaryfunc)Color_as_int,\n    .nb_true_divide = (binaryfunc)color_truediv,\n};\n\n#define CGETSET(name) \\\n    static PyObject* name##_get(Color *self, void UNUSED *closure) { return PyLong_FromUnsignedLong(self->color.name);  }\nCGETSET(red)\nCGETSET(green)\nCGETSET(blue)\nCGETSET(alpha)\n#undef CGETSET\n\nstatic PyObject*\nrgb_get(Color *self, void *closure UNUSED) {\n    return PyLong_FromUnsignedLong(self->color.rgb);\n}\n\nstatic PyObject*\nluminance_get(Color *self, void *closure UNUSED) {\n    return PyFloat_FromDouble(rgb_luminance(self->color) / 255.0);\n}\n\nstatic PyObject*\nis_dark_get(Color *self, void *closure UNUSED) {\n    if (rgb_luminance(self->color) / 255.0 < 0.5) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nsgr_get(Color* self, void *closure UNUSED) {\n    char buf[32];\n    int sz = snprintf(buf, sizeof(buf), \":2:%u:%u:%u\", self->color.r, self->color.g, self->color.b);\n    return PyUnicode_FromStringAndSize(buf, sz);\n}\n\nstatic PyObject*\nsharp_get(Color* self, void *closure UNUSED) {\n    char buf[32];\n    int sz;\n    if (self->color.alpha) sz = snprintf(buf, sizeof(buf), \"#%02x%02x%02x%02x\", self->color.a, self->color.r, self->color.g, self->color.b);\n    else sz = snprintf(buf, sizeof(buf), \"#%02x%02x%02x\", self->color.r, self->color.g, self->color.b);\n    return PyUnicode_FromStringAndSize(buf, sz);\n}\n\nstatic PyObject*\ncolor_cmp(PyObject *self, PyObject *other, int op) {\n    if (op != Py_EQ && op != Py_NE) return Py_NotImplemented;\n    if (!PyObject_TypeCheck(other, &Color_Type)) {\n        if (op == Py_EQ) Py_RETURN_FALSE;\n        Py_RETURN_TRUE;\n    }\n    Color *a = (Color*)self, *b = (Color*)other;\n    switch (op) {\n        case Py_EQ: { if (a->color.val == b->color.val) { Py_RETURN_TRUE; } Py_RETURN_FALSE; }\n        case Py_NE: { if (a->color.val != b->color.val) { Py_RETURN_TRUE; } Py_RETURN_FALSE; }\n        default:\n            return Py_NotImplemented;\n    }\n}\n\nstatic PyGetSetDef color_getsetters[] = {\n    {\"rgb\", (getter) rgb_get, NULL, \"rgb\", NULL},\n    {\"red\", (getter) red_get, NULL, \"red\", NULL},\n    {\"green\", (getter) green_get, NULL, \"green\", NULL},\n    {\"blue\", (getter) blue_get, NULL, \"blue\", NULL},\n    {\"alpha\", (getter) alpha_get, NULL, \"alpha\", NULL},\n    {\"r\", (getter) red_get, NULL, \"red\", NULL},\n    {\"g\", (getter) green_get, NULL, \"green\", NULL},\n    {\"b\", (getter) blue_get, NULL, \"blue\", NULL},\n    {\"a\", (getter) alpha_get, NULL, \"alpha\", NULL},\n    {\"luminance\", (getter) luminance_get, NULL, \"luminance\", NULL},\n    {\"as_sgr\", (getter) sgr_get, NULL, \"as_sgr\", NULL},\n    {\"as_sharp\", (getter) sharp_get, NULL, \"as_sharp\", NULL},\n    {\"is_dark\", (getter) is_dark_get, NULL, \"is_dark\", NULL},\n    {NULL}  /* Sentinel */\n};\n\nstatic PyObject*\ncontrast(Color* self, PyObject *o) {\n    if (!PyObject_TypeCheck(o, &Color_Type)) { PyErr_SetString(PyExc_TypeError, \"Not a Color\"); return NULL; }\n    Color *other = (Color*) o;\n    return PyFloat_FromDouble(rgb_contrast(self->color, other->color));\n}\n\nstatic int\nhexchar_to_int(char c) {\n    switch (c) {\n        START_ALLOW_CASE_RANGE\n        case '0' ... '9': return c - '0';\n        case 'a' ... 'f': return c - 'a' + 10;\n        case 'A' ... 'F': return c - 'A' + 10;\n        END_ALLOW_CASE_RANGE\n    }\n    return -1;\n}\n\nstatic bool\nparse_base16_uchar(const char *hex, unsigned char *out) {\n    const int hi = hexchar_to_int(hex[0]);\n    const int lo = hexchar_to_int(hex[1]);\n    if (hi < 0 || lo < 0) return false;\n    *out = (unsigned char)((hi << 4) | lo);\n    return true;\n}\n\nstatic bool\nparse_double(const char *src, double *out) {\n    char *endptr;\n    errno = 0;\n    *out = strtod_l(src, &endptr, get_c_locale());\n    return endptr != src && *endptr == 0 && errno == 0;\n}\n\nstatic bool\nparse_single_color(const char *c, size_t len, unsigned char *out) {\n    char buf[2];\n    if (len == 1) { buf[0] = c[0]; buf[1] = c[0]; c = buf; }\n    return parse_base16_uchar(c, out);\n}\n\nstatic PyObject*\nparse_sharp(const char *spec, size_t len) {\n    unsigned char r, g, b;\n    switch(len) {\n        case 3:\n            if (!parse_single_color(spec, 1, &r) || !parse_single_color(spec + 1, 1, &g) || !parse_single_color(spec + 2, 1, &b)) Py_RETURN_NONE;\n            break;\n        case 6: case 9: case 12:\n            if (!parse_single_color(spec, 2, &r) || !parse_single_color(spec + len/3, 2, &g) || !parse_single_color(spec + 2 * len / 3, 2, &b)) Py_RETURN_NONE;\n            break;\n        default:\n            Py_RETURN_NONE;\n    }\n    return (PyObject*)alloc_color(r, g, b, 0);\n}\n\nstatic PyObject*\nparse_rgb(const char *spec, size_t len) {\n    char buf[32];\n    if (len >= sizeof(buf)) Py_RETURN_NONE;\n    memcpy(buf, spec, len); buf[len] = 0;\n    unsigned char r, g, b; char *tok;\n#define p(buf, out) if (!(tok = strtok(buf, \"/\")) || !parse_single_color(tok, strlen(tok), &out)) Py_RETURN_NONE;\n    p(buf, r); p(NULL, g); p(NULL, b);\n#undef p\n    return (PyObject*)alloc_color(r, g, b, 0);\n}\n\nstatic unsigned char as8bit(double f) { return (unsigned char)(round(f * 255.)); }\n\nstatic bool\nparse_single_intensity(const char *s, unsigned char *out) {\n    double f; if (!parse_double(s, &f)) return false;\n    *out = as8bit(f);\n    return true;\n}\n\nstatic PyObject*\nparse_rgbi(const char *spec, size_t len) {\n    char buf[256];\n    if (len >= sizeof(buf)) Py_RETURN_NONE;\n    memcpy(buf, spec, len); buf[len] = 0;\n    unsigned char r, g, b; char *tok;\n#define p(buf, out) if (!(tok = strtok(buf, \"/\")) || !parse_single_intensity(tok, &out)) Py_RETURN_NONE;\n    p(buf, r); p(NULL, g); p(NULL, b);\n#undef p\n    return (PyObject*)alloc_color(r, g, b, 0);\n}\n\nstatic bool\nparse_double_intensity(char *s, double *out, double percentage_divider) {\n    size_t l = strlen(s);\n    if (l == 0) return false;\n    double divisor = 1;\n    if (s[l-1] == '%') { s[l-1] = 0; divisor = percentage_divider; }\n    if (!parse_double(s, out)) return false;\n    *out /= divisor;\n    return true;\n}\n\nstatic double clamp(const double f) { return MAX(0, MIN(f, 1)); }\n\nstatic double\nlinear_to_srgb(double c) { return c <= 0.0031308 ? c * 12.92 : (1.055 * pow(c, (1 / 2.4)) - 0.055); }\n\nstatic double degrees_to_radians(double degrees) { return degrees * (M_PI / 180); }\nstatic double radians_to_degrees(double radians) { return 180 * radians / M_PI; }\n\nstatic void\noklch_to_srgb(double l, double c, double h, double *r, double *g, double *b) {\n    // Convert OKLCH to OKLab\n    const double h_rad = degrees_to_radians(h);\n    const double a = c * cos(h_rad);\n    const double lb = c * sin(h_rad);\n    // Convert OKLab to Linear sRGB\n    // Using the OKLab to Linear sRGB transformation\n    const double l_ = l + 0.3963377774 * a + 0.2158037573 * lb;\n    const double m_ = l - 0.1055613458 * a - 0.0638541728 * lb;\n    const double s_ = l - 0.0894841775 * a - 1.2914855480 * lb;\n\n    const double l_lin = l_ * l_ * l_;\n    const double m_lin = m_ * m_ * m_;\n    const double s_lin = s_ * s_ * s_;\n\n    const double r_lin = +4.0767416621 * l_lin - 3.3077115913 * m_lin + 0.2309699292 * s_lin;\n    const double g_lin = -1.2684380046 * l_lin + 2.6097574011 * m_lin - 0.3413193965 * s_lin;\n    const double b_lin = -0.0041960863 * l_lin - 0.7034186147 * m_lin + 1.7076147010 * s_lin;\n\n    *r = linear_to_srgb(clamp(r_lin)); *g = linear_to_srgb(clamp(g_lin)); *b = linear_to_srgb(clamp(b_lin));\n}\n\nstatic double srgb_to_linear(double c) { return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4); }\n\n\nstatic void\nsrgb_to_oklab(double r, double g, double b, double *l, double *a, double *lb) {\n    // Convert sRGB to linear sRGB\n    const double r_lin = srgb_to_linear(r);\n    const double g_lin = srgb_to_linear(g);\n    const double b_lin = srgb_to_linear(b);\n\n    // Convert Linear sRGB to OKLab (inverse of oklch_to_srgb)\n    const double l_lin = 0.4122214708 * r_lin + 0.5363325363 * g_lin + 0.0514459929 * b_lin;\n    const double m_lin = 0.2119034982 * r_lin + 0.6806995451 * g_lin + 0.1073969566 * b_lin;\n    const double s_lin = 0.0883024619 * r_lin + 0.2817188376 * g_lin + 0.6299787005 * b_lin;\n\n    const double l_ = l_lin != 0 ? copysign(pow(fabs(l_lin), 1./3.), l_lin) : 0;\n    const double m_ = m_lin != 0 ? copysign(pow(fabs(m_lin), 1./3.), m_lin) : 0;\n    const double s_ = s_lin != 0 ? copysign(pow(fabs(s_lin), 1./3.), s_lin) : 0;\n\n    // OKLab coordinates\n    *l = 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_;\n    *a = 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_;\n    *lb = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_;\n}\n\nstatic double\ndistance(double x_l, double x_a, double x_b, double y_l, double y_a, double y_b) {\n    return sqrt((x_l - y_l)*(x_l - y_l) + (x_a - y_a)*(x_a - y_a) + (x_b - y_b)*(x_b - y_b));\n}\n\nstatic void\noklch_to_srgb_gamut_map(double l, double c, double h, double *r, double *g, double *b) {\n    // Edge cases: pure black or white don't need gamut mapping\n    if (!isfinite(l) || !isfinite(c) || !isfinite(h) || l <= 0) { *r = 0; *g = 0; *b = 0; return; }\n    if (l >= 1) { *r = 1; *g = 1; *b = 1; return; }\n    // Constants from CSS Color Module Level 4\n    static const double JND = 0.02;  // Just Noticeable Difference threshold (2% in deltaEOK)\n    static const double MIN_CONVERGENCE = 0.0001;  // Binary search precision (0.01% chroma)\n    static const double EPSILON = 0.00001;  // Small value for doubleing point comparisons\n\n    // If chroma is very small, color is essentially achromatic\n    if (c < EPSILON) { *r = linear_to_srgb(l); *g = *r; *b = *r; return; }\n    // Try the original color first\n    oklch_to_srgb(l, c, h, r, g, b);\n#define in_gamut(r,g,b) (0. <= r && r <= 1. && 0. <= g && g <= 1. && 0. <= b && b <= 1.)\n    if (in_gamut(*r,*g,*b)) return;\n    // Binary search for maximum in-gamut chroma\n    double low_chroma = 0, high_chroma = c, r_test, g_test, b_test, r_clipped, g_clipped, b_clipped;\n\n    // Convert original color to OKLab for deltaE calculations\n    while ((high_chroma - low_chroma) > MIN_CONVERGENCE) {\n        double mid_chroma = (high_chroma + low_chroma) * 0.5;\n        // Try this chroma value\n        oklch_to_srgb(l, mid_chroma, h, &r_test, &g_test, &b_test);\n        // Check if in gamut (before clipping)\n        if (in_gamut(r_test, g_test, b_test)) {\n            // In gamut - try higher chroma\n            low_chroma = mid_chroma;\n        } else {\n            // Out of gamut - clip and check deltaE\n            r_clipped = clamp(r_test); g_clipped = clamp(g_test); b_clipped = clamp(b_test);\n\n            // Convert both to OKLab for comparison\n            double l_test, a_test, lb_test, l_clipped, a_clipped, lb_clipped;\n            srgb_to_oklab(r_test, g_test, b_test, &l_test, &a_test, &lb_test);\n            srgb_to_oklab(r_clipped, g_clipped, b_clipped, &l_clipped, &a_clipped, &lb_clipped);\n\n            // Calculate perceptual difference\n            double de = distance(l_test, a_test, lb_test, l_clipped, a_clipped, lb_clipped);\n\n            if (de < JND) {\n                // Difference is imperceptible - accept this chroma\n                low_chroma = mid_chroma;\n            } else {\n                // Difference is noticeable - reduce chroma more\n                high_chroma = mid_chroma;\n            }\n        }\n    }\n    // Use the final chroma value and clip to ensure in-gamut\n    oklch_to_srgb(l, low_chroma, h, r, g, b);\n    *r = clamp(*r); *g = clamp(*g); *b = clamp(*b);\n#undef in_gamut\n}\n\nstatic double\nf_inv(double t) {\n    static const double delta = 6. / 29.;\n    return t > delta ? t*t*t : 3 * delta * delta * (t - 4. / 29.);\n}\n\n\nstatic void\nlab_to_oklch(double l, double a, double b, double *okl, double *c, double *h) {\n    const double y = (l + 16.) / 116.;\n    const double x = a / 500. + y;\n    const double z = y - b / 200.;\n    const double x_val = 0.95047 * f_inv(x);\n    const double y_val = f_inv(y);\n    const double z_val = 1.08883 * f_inv(z);\n\n    // XYZ to Linear sRGB (don't clip here to preserve out-of-gamut info)\n    const double r_lin = +3.2404542 * x_val - 1.5371385 * y_val - 0.4985314 * z_val;\n    const double g_lin = -0.9692660 * x_val + 1.8760108 * y_val + 0.0415560 * z_val;\n    const double b_lin = +0.0556434 * x_val - 0.2040259 * y_val + 1.0572252 * z_val;\n\n    // Convert linear sRGB to sRGB gamma\n    const double r_srgb = r_lin >= 0 ? linear_to_srgb(r_lin) : 0;\n    const double g_srgb = g_lin >= 0 ? linear_to_srgb(g_lin) : 0;\n    const double b_srgb = b_lin >= 0 ? linear_to_srgb(b_lin) : 0;\n\n    // Convert to OKLab\n    double a_ok, b_ok;\n    srgb_to_oklab(r_srgb, g_srgb, b_srgb, okl, &a_ok, &b_ok);\n    // Convert OKLab to OKLCH\n    *c = sqrt(a_ok * a_ok + b_ok * b_ok);\n    *h = fmod(radians_to_degrees(atan2(b_ok, a_ok)), 360.f);\n}\n\nstatic PyObject*\nparse_oklch(const char *spec, size_t len) {\n    if (len < 10 || spec[--len] != ')') Py_RETURN_NONE;\n    if (spec[0] != 'k' || spec[1] != 'l' || spec[2] != 'c' || spec[3] != 'h' || spec[4] != '(') Py_RETURN_NONE;\n    spec += 5; len -= 5;\n    char buf[256]; if (len >= sizeof(buf)) Py_RETURN_NONE;\n    memcpy(buf, spec, len); buf[len] = 0;\n    double l, c, h; char *tok;\n#define p(buf, out) if (!(tok = strtok(buf, \" ,\")) || !parse_double_intensity(tok, &out, 100)) Py_RETURN_NONE;\n    p(buf, l); p(NULL, c); p(NULL, h);\n#undef p\n    // Clamp to reasonable ranges\n    l = clamp(l);\n    c = MAX(0.f, c);  // Chroma is unbounded but we don't clamp high end\n    h = fmod(h, 360);  // Wrap hue to 0-360\n    double r, g, b;\n    oklch_to_srgb_gamut_map(l, c, h, &r, &g, &b);\n    return (PyObject*)alloc_color(as8bit(r), as8bit(g), as8bit(b), 0);\n}\n\nstatic PyObject*\nparse_lab(const char *spec, size_t len) {\n    if (len < 8 || spec[--len] != ')') Py_RETURN_NONE;\n    if (spec[0] != 'a' || spec[1] != 'b' || spec[2] != '(') Py_RETURN_NONE;\n    spec += 3; len -= 3;\n    char buf[256]; if (len >= sizeof(buf)) Py_RETURN_NONE;\n    memcpy(buf, spec, len); buf[len] = 0;\n    double l, a, b; char *tok;\n#define p(buf, out) if (!(tok = strtok(buf, \" ,\")) || !parse_double_intensity(tok, &out, 1)) Py_RETURN_NONE;\n    p(buf, l); p(NULL, a); p(NULL, b);\n#undef p\n    // Clamp to reasonable ranges\n    double okl, c, h, r, g, bb;\n    lab_to_oklch(MAX(0., MIN(l, 100.)), a, b, &okl, &c, &h);\n    oklch_to_srgb_gamut_map(okl, c, h, &r, &g, &bb);\n    return (PyObject*)alloc_color(as8bit(r), as8bit(g), as8bit(bb), 0);\n}\n\nstatic const char*\ntrim_view(const char *str, Py_ssize_t *len) {\n    if (str == NULL || *len == 0) return str;\n    const char *start = str;\n    const char *end = str + *len - 1;\n    while (start <= end && isspace((unsigned char)*start)) start++;\n    while (end > start && isspace((unsigned char)*end)) end--;\n    if (start > end) { *len = 0; } else { *len = (size_t)(end - start + 1); }\n    return start;\n}\n\nstatic PyObject*\nparse_color(PyTypeObject *type UNUSED, PyObject *pspec) {\n    if (!PyUnicode_Check(pspec)) { PyErr_SetString(PyExc_TypeError, \"spec must be a string\"); return NULL; }\n    RAII_PyObject(lower_cased, PyObject_CallMethod(pspec, \"lower\", NULL));\n    if (!lower_cased) return NULL;\n    Py_ssize_t len;\n    const char *spec = PyUnicode_AsUTF8AndSize(lower_cased, &len);\n    spec = trim_view(spec, &len);\n    if (len < 2) Py_RETURN_NONE;\n    // Remove trailing comments\n    switch (spec[0]) {\n        case '#': {\n            const char *s = strchr(spec, ' ');\n            if (s) len = s - spec;\n        } break;\n        default: {\n            const char *s = strchr(spec, '#');\n            if (s) {\n                len = s - spec;\n                spec = trim_view(spec, &len);\n            }\n        } break;\n    }\n    const struct Keyword *k = in_color_name_set(spec, len);\n    if (k) return (PyObject*)alloc_color((k->value >> 16) & 0xff, (k->value >> 8) & 0xff, k->value & 0xff, 0);\n    if (len < 4) Py_RETURN_NONE;\n    switch (spec[0]) {\n        case '#': return parse_sharp(spec + 1, len - 1);\n        case 'r':\n            if (spec[1] != 'g' || spec[2] != 'b' || len < 6) Py_RETURN_NONE;\n            switch(spec[3]) {\n                case ':': return parse_rgb(spec + 4, len - 4);\n                case 'i':\n                    if (spec[4] == 'i' && spec[5] == ':') return parse_rgbi(spec + 5, len - 5);\n            }\n            Py_RETURN_NONE;\n        case 'o': return parse_oklch(spec + 1, len - 1);\n        case 'l': return parse_lab(spec + 1, len - 1);\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyMethodDef color_methods[] = {\n    METHODB(contrast, METH_O),\n    METHODB(parse_color, METH_O | METH_CLASS),\n    {NULL}  /* Sentinel */\n};\n\n\nstatic PyObject *\nrepr(Color *self) {\n    if (self->color.alpha) return PyUnicode_FromFormat(\"Color(red=%u, green=%u, blue=%u, alpha=%u)\", self->color.r, self->color.g, self->color.b, self->color.a);\n    return PyUnicode_FromFormat(\"Color(%u, %u, %u)\", self->color.r, self->color.g, self->color.b);\n}\n\nstatic Py_hash_t\ncolor_hash(PyObject *x) {\n    return ((Color*)x)->color.val;\n}\n\nPyTypeObject Color_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"kitty.fast_data_types.Color\",\n    .tp_basicsize = sizeof(Color),\n    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_VECTORCALL,\n    .tp_doc = \"Color\",\n    .tp_new = new_color,\n    .tp_vectorcall = color_vectorcall,\n    .tp_getset = color_getsetters,\n    .tp_as_number = &color_number_methods,\n    .tp_methods = color_methods,\n    .tp_repr = (reprfunc)repr,\n    .tp_hash = color_hash,\n    .tp_richcompare = color_cmp,\n};\n\n\nstatic PyObject*\nall_color_names(PyObject *self UNUSED, PyObject *args UNUSED) {\n    RAII_PyObject(ans, PyTuple_New(TOTAL_KEYWORDS));\n    if (!ans) return NULL;\n    const struct Keyword *k;\n    Py_ssize_t n = 0;\n    for (unsigned i = 0; i <= MAX_HASH_VALUE; i++) {\n        if ((k = &color_names[i])->name > -1) {\n            const char *name = color_names[i].name + stringpool;\n            PyObject *t = Py_BuildValue(\"sN\", name, alloc_color((k->value >> 16) & 0xff, (k->value >> 8) & 0xff, k->value & 0xff, 0));\n            if (!t) return NULL;\n            PyTuple_SET_ITEM(ans, n, t); n++;\n        }\n    }\n    return Py_NewRef(ans);\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(default_color_table, METH_NOARGS),\n    METHODB(patch_color_profiles, METH_VARARGS),\n    METHODB(all_color_names, METH_NOARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nint init_ColorProfile(PyObject *module) {\\\n    if (PyType_Ready(&ColorProfile_Type) < 0) return 0;\n    if (PyModule_AddObject(module, \"ColorProfile\", (PyObject *)&ColorProfile_Type) != 0) return 0;\n    Py_INCREF(&ColorProfile_Type);\n\n    if (PyType_Ready(&Color_Type) < 0) return 0;\n    if (PyModule_AddObject(module, \"Color\", (PyObject *)&Color_Type) != 0) return 0;\n    Py_INCREF(&Color_Type);\n\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    return 1;\n}\n\n\n// }}}\n"
  },
  {
    "path": "kitty/colors.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\ntypedef union ARGB32 {\n    color_type val;\n    struct {\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        uint8_t b: 8;\n        uint8_t g: 8;\n        uint8_t r: 8;\n        uint8_t a: 8;\n#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        uint8_t a: 8;\n        uint8_t r: 8;\n        uint8_t g: 8;\n        uint8_t b: 8;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n    struct {\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        uint8_t blue: 8;\n        uint8_t green: 8;\n        uint8_t red: 8;\n        uint8_t alpha: 8;\n#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        uint8_t alpha: 8;\n        uint8_t red: 8;\n        uint8_t green: 8;\n        uint8_t blue: 8;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n    struct {\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        color_type rgb: 24;\n        uint8_t _ignore_me: 8;\n#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        uint8_t _ignore_me: 8;\n        color_type rgb: 24;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n} ARGB32;\n\ntypedef struct {\n    PyObject_HEAD\n\n    ARGB32 color;\n} Color;\n\nextern PyTypeObject ColorProfile_Type;\nextern PyTypeObject Color_Type;\n\nstatic inline double\nrgb_luminance(ARGB32 c) {\n    // From ITU BT 601 https://www.itu.int/rec/R-REC-BT.601\n    return 0.299 * c.red + 0.587 * c.green + 0.114 * c.blue;\n}\n\nstatic inline double\nrgb_contrast(ARGB32 a, ARGB32 b) {\n    double al = rgb_luminance(a), bl = rgb_luminance(b);\n    if (al < bl) SWAP(al, bl);\n    return (al + 0.05) / (bl + 0.05);\n}\n"
  },
  {
    "path": "kitty/colors.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nfrom collections.abc import Iterable, Sequence\nfrom contextlib import suppress\nfrom enum import Enum\nfrom typing import Literal, Optional, TypedDict\n\nfrom .config import parse_config\nfrom .constants import config_dir\nfrom .fast_data_types import Color, get_boss, get_options, glfw_get_system_color_theme, patch_color_profiles, patch_global_colors, set_os_window_chrome\nfrom .options.types import Options, nullable_colors, special_colors\nfrom .rgb import color_from_int\nfrom .typing_compat import WindowType\n\nColorsSpec = dict[str, Optional[int]]\nTransparentBackgroundColors = tuple[tuple[Color, float], ...]\nColorSchemes = Literal['light', 'dark', 'no_preference']\nColors = tuple[ColorsSpec, TransparentBackgroundColors]\n\n\nclass BackgroundImageOptions(TypedDict, total=False):\n    background_image: str | None\n    background_image_layout: str | None\n    background_image_linear: bool | None\n    background_tint: float | None\n    background_tint_gaps: float | None\n\n\nclass ThemeFile(Enum):\n    dark = 'dark-theme.auto.conf'\n    light = 'light-theme.auto.conf'\n    no_preference = 'no-preference-theme.auto.conf'\n\n\nclass ThemeColors:\n\n    dark_mtime: int = -1\n    light_mtime: int = -1\n    no_preference_mtime: int = -1\n    applied_theme: Literal['light', 'dark', 'no_preference', ''] = ''\n    default_colors: ColorsSpec | None = None\n    default_background_image_options: BackgroundImageOptions| None = None\n\n    def get_default_colors(self) -> ColorsSpec:\n        if self.default_colors is None:\n            from kitty.options.types import defaults, option_names\n            ans: ColorsSpec = dict.fromkeys(nullable_colors)\n\n            for name in option_names:\n                defval = getattr(defaults, name)\n                if isinstance(defval, Color):\n                    ans[name] = int(defval)\n            for name in special_colors:\n                ans[name] = getattr(defaults, name)\n            self.default_colors = ans\n            self.default_background_image_options: BackgroundImageOptions = {\n                    k: getattr(defaults, k) for k in BackgroundImageOptions.__optional_keys__}  # type: ignore\n\n        return self.default_colors\n\n    def parse_colors(self, f: Iterable[str], background_image_options: BackgroundImageOptions | None = None) -> Colors:\n        # When parsing the theme file we first apply the default theme so that\n        # all colors are reset to default values first. This is needed for themes\n        # that don't specify all colors.\n        dc_spec = self.get_default_colors()\n        if background_image_options is not None and self.default_background_image_options:\n            background_image_options.update(self.default_background_image_options)\n        spec, tbc = parse_colors((f,), background_image_options)\n        ans = dc_spec.copy()\n        ans.update(spec)\n        return ans, tbc\n\n    def refresh(self) -> bool:\n        found = False\n        with suppress(FileNotFoundError):\n            for x in os.scandir(config_dir):\n                if x.name == ThemeFile.dark.value:\n                    mtime = x.stat().st_mtime_ns\n                    if mtime > self.dark_mtime:\n                        with open(x.path) as f:\n                            d: BackgroundImageOptions = {}\n                            self.dark_spec, self.dark_tbc = self.parse_colors(f, d)\n                            self.dark_background_image_options = d\n                        self.dark_mtime = mtime\n                        found = True\n                elif x.name == ThemeFile.light.value:\n                    mtime = x.stat().st_mtime_ns\n                    if mtime > self.light_mtime:\n                        with open(x.path) as f:\n                            d = {}\n                            self.light_spec, self.light_tbc = self.parse_colors(f, d)\n                            self.light_background_image_options = d\n                        self.light_mtime = mtime\n                        found = True\n                elif x.name == ThemeFile.no_preference.value:\n                    mtime = x.stat().st_mtime_ns\n                    if mtime > self.no_preference_mtime:\n                        with open(x.path) as f:\n                            d = {}\n                            self.no_preference_spec, self.no_preference_tbc = self.parse_colors(f, d)\n                            self.no_preference_background_image_options = d\n                        self.no_preference_mtime = mtime\n                        found = True\n        return found\n\n    @property\n    def has_applied_theme(self) -> bool:\n        match self.applied_theme:\n            case '':\n                return False\n            case 'dark':\n                return self.has_dark_theme\n            case 'light':\n                return self.has_light_theme\n            case 'no_preference':\n                return self.has_no_preference_theme\n\n    @property\n    def has_dark_theme(self) -> bool:\n        return self.dark_mtime > -1\n\n    @property\n    def has_light_theme(self) -> bool:\n        return self.light_mtime > -1\n\n    @property\n    def has_no_preference_theme(self) -> bool:\n        return self.no_preference_mtime > -1\n\n    def patch_opts(self, opts: Options, debug_rendering: bool = False) -> None:\n        from .utils import log_error\n        if debug_rendering:\n            log_error('Querying system for current color scheme')\n        which = glfw_get_system_color_theme()\n        if debug_rendering:\n            log_error('Current system color scheme:', which)\n        cols: Colors | None = None\n        bgo: BackgroundImageOptions | None = None\n        if which == 'dark' and self.has_dark_theme:\n            cols = self.dark_spec, self.dark_tbc\n            bgo = self.dark_background_image_options\n        elif which == 'light' and self.has_light_theme:\n            cols = self.light_spec, self.light_tbc\n            bgo = self.light_background_image_options\n        elif which == 'no_preference' and self.has_no_preference_theme:\n            cols = self.no_preference_spec, self.no_preference_tbc\n            bgo = self.no_preference_background_image_options\n        if cols is not None:\n            patch_options_with_color_spec(opts, *cols, background_image_options=bgo)\n            patch_global_colors(cols[0], True)\n            self.applied_theme = which\n            if debug_rendering:\n                log_error(f'Applied {self.applied_theme} color theme')\n\n    def on_system_color_scheme_change(self, new_value: ColorSchemes, is_initial_value: bool = False) -> bool:\n        if is_initial_value:\n            return False\n        self.refresh()\n        return self.apply_theme(new_value)\n\n    def apply_theme(self, new_value: ColorSchemes, notify_on_bg_change: bool = True) -> bool:\n        from .utils import log_error\n        boss = get_boss()\n        if new_value == 'dark' and self.has_dark_theme:\n            patch_colors(\n                self.dark_spec, self.dark_tbc, True, notify_on_bg_change=notify_on_bg_change, background_image_options=self.dark_background_image_options)\n            self.applied_theme = new_value\n            if boss.args.debug_rendering:\n                log_error(f'Applied color theme {new_value}')\n            return True\n        if new_value == 'light' and self.has_light_theme:\n            patch_colors(\n                self.light_spec, self.light_tbc, True, notify_on_bg_change=notify_on_bg_change, background_image_options=self.light_background_image_options)\n            self.applied_theme = new_value\n            if boss.args.debug_rendering:\n                log_error(f'Applied color theme {new_value}')\n            return True\n        if new_value == 'no_preference' and self.has_no_preference_theme:\n            patch_colors(\n                self.no_preference_spec, self.no_preference_tbc, True, notify_on_bg_change=notify_on_bg_change,\n                background_image_options=self.no_preference_background_image_options)\n            self.applied_theme = new_value\n            if boss.args.debug_rendering:\n                log_error(f'Applied color theme {new_value}')\n            return True\n        return False\n\n\ntheme_colors = ThemeColors()\n\n\ndef parse_colors(args: Iterable[str | Iterable[str]], background_image_options: BackgroundImageOptions | None = None) -> Colors:\n    colors: dict[str, Color | None | int] = {}\n    nullable_color_map: dict[str, int | None] = {}\n    special_color_map: dict[str, int] = {}\n    transparent_background_colors = ()\n    for spec in args:\n        if isinstance(spec, str):\n            if '=' in spec:\n                conf = parse_config((spec.replace('=', ' '),))\n            else:\n                with open(os.path.expanduser(spec), encoding='utf-8', errors='replace') as f:\n                    conf = parse_config(f)\n        else:\n            conf = parse_config(spec)\n        transparent_background_colors = conf.pop('transparent_background_colors', ())\n        if background_image_options is not None:\n            for key in BackgroundImageOptions.__optional_keys__:\n                if key in conf:\n                    background_image_options.__setitem__(key, conf[key])\n        colors.update(conf)\n    for k in nullable_colors:\n        q = colors.pop(k, False)\n        if q is not False:\n            val = int(q) if isinstance(q, Color) else None\n            nullable_color_map[k] = val\n    for k in special_colors:\n        sq = colors.pop(k, None)\n        if isinstance(sq, int):\n            special_color_map[k] = sq\n    ans: dict[str, int | None] = {k: int(v) for k, v in colors.items() if isinstance(v, Color)}\n    ans.update(nullable_color_map)\n    ans.update(special_color_map)\n    return ans, transparent_background_colors\n\n\ndef patch_options_with_color_spec(\n    opts: Options, spec: ColorsSpec, transparent_background_colors: TransparentBackgroundColors,\n    background_image_options: BackgroundImageOptions | None = None\n) -> None:\n    for k, v in spec.items():\n        if hasattr(opts, k):\n            if v is None:\n                if k in nullable_colors:\n                    setattr(opts, k, None)\n            else:\n                if k in special_colors:\n                    setattr(opts, k, v)\n                else:\n                    setattr(opts, k, color_from_int(v))\n    opts.transparent_background_colors = transparent_background_colors\n    if background_image_options is not None:\n        for k, bv in background_image_options.items():\n            if hasattr(opts, k):\n                setattr(opts, k, bv)\n\n\ndef patch_colors(\n    spec: ColorsSpec, transparent_background_colors: TransparentBackgroundColors, configured: bool = False,\n    windows: Sequence[WindowType] | None = None, notify_on_bg_change: bool = True,\n    background_image_options: BackgroundImageOptions | None = None\n) -> None:\n    boss = get_boss()\n    if windows is None:\n        windows = tuple(boss.all_windows)\n    bg_colors_before = {w.id: w.screen.color_profile.default_bg for w in windows}\n    profiles = tuple(w.screen.color_profile for w in windows if w)\n    patch_color_profiles(spec, transparent_background_colors, profiles, configured)\n    opts = get_options()\n    if configured:\n        patch_options_with_color_spec(opts, spec, transparent_background_colors, background_image_options)\n    os_window_ids = set()\n    for tm in get_boss().all_tab_managers:\n        tm.tab_bar.patch_colors(spec)\n        tm.tab_bar.layout()\n        tm.mark_tab_bar_dirty()\n        t = tm.active_tab\n        if t is not None:\n            t.relayout_borders()\n        os_window_ids.add(tm.os_window_id)\n    patch_global_colors(spec, configured)  # changes macos_titlebar_color\n    for oswid in os_window_ids:\n        set_os_window_chrome(oswid)\n    default_bg_changed = 'background' in spec\n    notify_bg = notify_on_bg_change and default_bg_changed\n    boss = get_boss()\n    if background_image_options is not None:\n        boss.set_background_image(\n            background_image_options.get('background_image'), tuple(os_window_ids), configured,\n            layout=background_image_options.get('background_image_layout'),\n            linear_interpolation=background_image_options.get('background_image_linear'), tint=background_image_options.get('background_tint'),\n            tint_gaps=background_image_options.get('background_tint_gaps'))\n    for w in windows:\n        if w:\n            if notify_bg and w.screen.color_profile.default_bg != bg_colors_before.get(w.id):\n                boss.default_bg_changed_for(w.id)\n            w.refresh()\n"
  },
  {
    "path": "kitty/conf/__init__.py",
    "content": ""
  },
  {
    "path": "kitty/conf/generate.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport inspect\nimport os\nimport re\nimport textwrap\nfrom collections.abc import Callable, Iterator\nfrom typing import Any, get_type_hints\n\nfrom kitty.conf.types import Definition, MultiOption, Option, ParserFuncType, unset\nfrom kitty.simple_cli_definitions import serialize_as_go_string\nfrom kitty.types import _T\n\n\ndef chunks(lst: list[_T], n: int) -> Iterator[list[_T]]:\n    for i in range(0, len(lst), n):\n        yield lst[i:i + n]\n\n\ndef atoi(text: str) -> str:\n    return f'{int(text):08d}' if text.isdigit() else text\n\n\ndef natural_keys(text: str) -> tuple[str, ...]:\n    return tuple(atoi(c) for c in re.split(r'(\\d+)', text))\n\n\ndef generate_class(defn: Definition, loc: str) -> tuple[str, str]:\n    class_lines: list[str] = []\n    tc_lines: list[str] = []\n    a = class_lines.append\n    t = tc_lines.append\n    a('class Options:')\n    t('class Parser:')\n    choices = {}\n    imports: set[tuple[str, str]] = set()\n    tc_imports: set[tuple[str, str]] = set()\n    ki_imports: 're.Pattern[str]' = re.compile(r'\\b((?:kittens|kitty).+?)[,\\]]')\n\n    def option_type_as_str(x: Any) -> str:\n        needs_import = False\n        if type(x) is type:\n            ans = x.__name__\n            needs_import = True\n        else:\n            ans = repr(x)\n            ans = ans.replace('NoneType', 'None')\n        if needs_import and getattr(x, '__module__', None) and x.__module__ not in ('builtins', 'typing'):\n            imports.add((x.__module__, x.__name__))\n        return ans\n\n    def option_type_data(option: Option | MultiOption) -> tuple[Callable[[Any], Any], str]:\n        func = option.parser_func\n        if func.__module__ == 'builtins':\n            return func, func.__name__\n        th = get_type_hints(func)\n        rettype = th['return']\n        typ = option_type_as_str(rettype)\n        if isinstance(option, MultiOption):\n            typ = typ[typ.index('[') + 1:-1]\n            typ = typ.replace('tuple', 'dict', 1)\n            kq = ki_imports.search(typ)\n            if kq is not None:\n                kqi = kq.group(1)\n                kqim, kqii = kqi.rsplit('.', 1)\n                imports.add((kqim, ''))\n        return func, typ\n\n    is_mutiple_vars = {}\n    option_names = set()\n    color_table = list(map(str, range(256)))\n    choice_dedup: dict[str, str] = {}\n    choice_parser_dedup: dict[str, str] = {}\n\n    def parser_function_declaration(option_name: str) -> None:\n        t('')\n        t(f'    def {option_name}(self, val: str, ans: dict[str, typing.Any]) -> None:')\n\n    for option in sorted(defn.iter_all_options(), key=lambda a: natural_keys(a.name)):\n        option_names.add(option.name)\n        parser_function_declaration(option.name)\n        if isinstance(option, MultiOption):\n            mval: dict[str, dict[str, Any]] = {'macos': {}, 'linux': {}, '': {}}\n            func, typ = option_type_data(option)\n            for val in option:\n                if val.add_to_default:\n                    gr = mval[val.only]\n                    for k, v in func(val.defval_as_str):\n                        gr[k] = v\n            is_mutiple_vars[option.name] = typ, mval\n            sig = inspect.signature(func)\n            tc_imports.add((func.__module__, func.__name__))\n            if len(sig.parameters) == 1:\n                t(f'        for k, v in {func.__name__}(val):')\n                t(f'            ans[\"{option.name}\"][k] = v')\n            else:\n                t(f'        for k, v in {func.__name__}(val, ans[\"{option.name}\"]):')\n                t(f'            ans[\"{option.name}\"][k] = v')\n            continue\n\n        if option.choices:\n            typ = 'typing.Literal[{}]'.format(', '.join(repr(x) for x in option.choices))\n            ename = f'choices_for_{option.name}'\n            if typ in choice_dedup:\n                typ = choice_dedup[typ]\n            else:\n                choice_dedup[typ] = ename\n            choices[ename] = typ\n            typ = ename\n            func = str\n        elif defn.has_color_table and option.is_color_table_color:\n            func, typ = option_type_data(option)\n            t(f'        ans[{option.name!r}] = {func.__name__}(val)')\n            tc_imports.add((func.__module__, func.__name__))\n            cnum = int(option.name[5:])\n            color_table[cnum] = f'0x{func(option.defval_as_string).__int__():06x}'\n            continue\n        else:\n            func, typ = option_type_data(option)\n            try:\n                params = dict(inspect.signature(func).parameters)\n            except Exception:\n                params = {}\n            if 'dict_with_parse_results' in params:\n                t(f'        {func.__name__}(val, ans)')\n            else:\n                t(f'        ans[{option.name!r}] = {func.__name__}(val)')\n            if func.__module__ != 'builtins':\n                tc_imports.add((func.__module__, func.__name__))\n\n        defval_as_obj = func(option.defval_as_string)\n        if isinstance(defval_as_obj, frozenset):\n            defval = 'frozenset({' + ', '.join(repr(x) for x in sorted(defval_as_obj)) + '})'\n        else:\n            defval = repr(defval_as_obj)\n        if option.macos_defval is not unset:\n            md = repr(func(option.macos_defval))\n            defval = f'{md} if is_macos else {defval}'\n            imports.add(('kitty.constants', 'is_macos'))\n        a(f'    {option.name}: {typ} = {defval}')\n        if option.choices:\n            ecname = f'choices_for_{option.name}'\n            crepr = f'frozenset({option.choices!r})'\n            if crepr in choice_parser_dedup:\n                crepr = choice_parser_dedup[crepr]\n            else:\n                choice_parser_dedup[crepr] = ecname\n            t('        val = val.lower()')\n            t(f'        if val not in self.choices_for_{option.name}:')\n            t(f'            raise ValueError(f\"The value {{val}} is not a valid choice for {option.name}\")')\n            t(f'        ans[\"{option.name}\"] = val')\n            t('')\n            t(f'    {ecname} = {crepr}')\n\n    for option_name, (typ, mval) in is_mutiple_vars.items():\n        a(f'    {option_name}: {typ} = ' '{}')\n\n    for parser, aliases in defn.deprecations.items():\n        for alias in aliases:\n            parser_function_declaration(alias)\n            tc_imports.add((parser.__module__, parser.__name__))\n            t(f'        {parser.__name__}({alias!r}, val, ans)')\n\n    action_parsers = {}\n\n    def resolve_import(ftype: str) -> str:\n        if '.' in ftype:\n            fmod, ftype = ftype.rpartition('.')[::2]\n        else:\n            fmod = f'{loc}.options.utils'\n        imports.add((fmod, ftype))\n        return ftype\n\n    for aname, action in defn.actions.items():\n        option_names.add(aname)\n        action_parsers[aname] = func = action.parser_func\n        th = get_type_hints(func)\n        rettype = th['return']\n        typ = option_type_as_str(rettype)\n        typ = typ[typ.index('[') + 1:-1]\n        a(f'    {aname}: list[{typ}] = []')\n        for imp in action.imports:\n            resolve_import(imp)\n        for fname, ftype in action.fields.items():\n            ftype = resolve_import(ftype)\n            fval = f'{ftype}()' if ftype == 'AliasMap' else '{}'\n            a(f'    {fname}: {ftype} = {fval}')\n        parser_function_declaration(aname)\n        t(f'        for k in {func.__name__}(val):')\n        t(f'            ans[{aname!r}].append(k)')\n        tc_imports.add((func.__module__, func.__name__))\n\n    if defn.has_color_table:\n        imports.add(('array', 'array'))\n        a('    color_table: \"array[int]\" = array(\"L\", (')\n        for grp in chunks(color_table, 8):\n            a('        ' + ', '.join(grp) + ',')\n        a('    ))')\n\n    a('    config_paths: tuple[str, ...] = ()')\n    a('    all_config_paths: tuple[str, ...] = ()')\n    a('    config_overrides: tuple[str, ...] = ()')\n    a('')\n    a('    def __init__(self, options_dict: dict[str, typing.Any] | None = None) -> None:')\n    if defn.has_color_table:\n        a('        self.color_table = array(self.color_table.typecode, self.color_table)')\n    a('        if options_dict is not None:')\n    a('            null = object()')\n    a('            for key in option_names:')\n    a('                val = options_dict.get(key, null)')\n    a('                if val is not null:')\n    a('                    setattr(self, key, val)')\n\n    a('')\n    a('    @property')\n    a('    def _fields(self) -> tuple[str, ...]:')\n    a('        return option_names')\n\n    a('')\n    a('    def __iter__(self) -> typing.Iterator[str]:')\n    a('        return iter(self._fields)')\n\n    a('')\n    a('    def __len__(self) -> int:')\n    a('        return len(self._fields)')\n\n    a('')\n    a('    def _copy_of_val(self, name: str) -> typing.Any:')\n    a('        ans = getattr(self, name)')\n    a('        if isinstance(ans, dict):\\n            ans = ans.copy()')\n    a('        elif isinstance(ans, list):\\n            ans = ans[:]')\n    a('        return ans')\n\n    a('')\n    a('    def _asdict(self) -> dict[str, typing.Any]:')\n    a('        return {k: self._copy_of_val(k) for k in self}')\n\n    a('')\n    a('    def _replace(self, **kw: typing.Any) -> \"Options\":')\n    a('        ans = Options()')\n    a('        for name in self:')\n    a('            setattr(ans, name, self._copy_of_val(name))')\n    a('        for name, val in kw.items():')\n    a('            setattr(ans, name, val)')\n    a('        return ans')\n\n    a('')\n    a('    def __getitem__(self, key: int | str) -> typing.Any:')\n    a('        k = option_names[key] if isinstance(key, int) else key')\n    a('        try:')\n    a('            return getattr(self, k)')\n    a('        except AttributeError:')\n    a('            pass')\n    a('        raise KeyError(f\"No option named: {k}\")')\n\n    if defn.has_color_table:\n        a('')\n        a('    def __getattr__(self, key: str) -> typing.Any:')\n        a('        if key.startswith(\"color\"):')\n        a('            q = key[5:]')\n        a('            if q.isdigit():')\n        a('                k = int(q)')\n        a('                if 0 <= k <= 255:')\n        a('                    x = self.color_table[k]')\n        a('                    return Color((x >> 16) & 255, (x >> 8) & 255, x & 255)')\n        a('        raise AttributeError(key)')\n        a('')\n        a('    def __setattr__(self, key: str, val: typing.Any) -> typing.Any:')\n        a('        if key.startswith(\"color\"):')\n        a('            q = key[5:]')\n        a('            if q.isdigit():')\n        a('                k = int(q)')\n        a('                if 0 <= k <= 255:')\n        a('                    self.color_table[k] = int(val)')\n        a('                    return')\n        a('        object.__setattr__(self, key, val)')\n\n    a('')\n    a('')\n    a('defaults = Options()')\n    a('')\n    for option_name, (typ, mval) in is_mutiple_vars.items():\n        a(f'defaults.{option_name} = {mval[\"\"]!r}')\n        if mval['macos']:\n            imports.add(('kitty.constants', 'is_macos'))\n            a('if is_macos:')\n            a(f'    defaults.{option_name}.update({mval[\"macos\"]!r}')\n        if mval['macos']:\n            imports.add(('kitty.constants', 'is_macos'))\n            a('if not is_macos:')\n            a(f'    defaults.{option_name}.update({mval[\"linux\"]!r}')\n\n    a('')\n    for aname, func in action_parsers.items():\n        a(f'defaults.{aname} = [')\n        only: dict[str, list[tuple[str, Callable[..., Any]]]] = {}\n        for sc in defn.iter_all_maps(aname):\n            if not sc.add_to_default:\n                continue\n            text = sc.parseable_text\n            if sc.only:\n                only.setdefault(sc.only, []).append((text, func))\n            else:\n                for val in func(text):\n                    a(f'    # {sc.name}')\n                    a(f'    {val!r},')\n        a(']')\n        a('')\n        if only:\n            imports.add(('kitty.constants', 'is_macos'))\n            for cond, items in only.items():\n                cond = 'is_macos' if cond == 'macos' else 'not is_macos'\n                a(f'if {cond}:')\n                for (text, parser_func) in items:\n                    for val in parser_func(text):\n                        a(f'    defaults.{aname}.append({val!r})')\n                a('')\n\n    t('')\n    t('')\n    t('def create_result_dict() -> dict[str, typing.Any]:')\n    t('    return {')\n    for oname in is_mutiple_vars:\n        t(f'        {oname!r}: {{}},')\n    for aname in defn.actions:\n        t(f'        {aname!r}: [],')\n    t('    }')\n\n    t('')\n    t('')\n    t(f'actions: frozenset[str] = frozenset({tuple(defn.actions)!r})')\n    t('')\n    t('')\n    t('def merge_result_dicts(defaults: dict[str, typing.Any], vals: dict[str, typing.Any]) -> dict[str, typing.Any]:')\n    t('    ans = {}')\n    t('    for k, v in defaults.items():')\n    t('        if isinstance(v, dict):')\n    t('            ans[k] = merge_dicts(v, vals.get(k, {}))')\n    t('        elif k in actions:')\n    t('            ans[k] = v + vals.get(k, [])')\n    t('        else:')\n    t('            ans[k] = vals.get(k, v)')\n    t('    return ans')\n    tc_imports.add(('kitty.conf.utils', 'merge_dicts'))\n\n    t('')\n    t('')\n    t('parser = Parser()')\n    t('')\n    t('')\n    t('def parse_conf_item(key: str, val: str, ans: dict[str, typing.Any]) -> bool:')\n    t('    func = getattr(parser, key, None)')\n    t('    if func is not None:')\n    t('        func(val, ans)')\n    t('        return True')\n    t('    return False')\n\n    preamble = ['# generated by gen-config.py DO NOT edit', '']\n    a = preamble.append\n\n    def output_imports(imports: set[tuple[str, str]], add_module_imports: bool = True) -> None:\n        a('# isort: skip_file')\n        a('import typing')\n        a('import collections.abc  # noqa: F401, RUF100')\n        seen_mods = {'typing'}\n        mmap: dict[str, list[str]] = {}\n        for mod, name in imports:\n            mmap.setdefault(mod, []).append(name)\n        for mod in sorted(mmap):\n            names = list(filter(None, sorted(mmap[mod])))\n            if names:\n                lines = textwrap.wrap(', '.join(names), 100)\n                if len(lines) == 1:\n                    s = lines[0]\n                else:\n                    s = '\\n    '.join(lines)\n                    s = f'(\\n    {s}\\n)'\n                a(f'from {mod} import {s}')\n            else:\n                s = ''\n            if add_module_imports and mod not in seen_mods and mod != s:\n                a(f'import {mod}')\n                seen_mods.add(mod)\n\n    output_imports(imports)\n    a('')\n    if choices:\n        for name, cdefn in choices.items():\n            a(f'{name} = {cdefn}')\n\n    a('')\n    a('option_names = (')\n    for option_name in sorted(option_names, key=natural_keys):\n        a(f'    {option_name!r},')\n    a(')')\n    class_def = '\\n'.join(preamble + ['', ''] + class_lines)\n\n    preamble = ['# generated by gen-config.py DO NOT edit', '']\n    a = preamble.append\n    output_imports(tc_imports, False)\n\n    return class_def, '\\n'.join(preamble + ['', ''] + tc_lines)\n\n\ndef generate_c_conversion(loc: str, ctypes: list[Option | MultiOption]) -> str:\n    lines: list[str] = []\n    basic_converters = {\n        'int': 'PyLong_AsLong', 'uint': 'PyLong_AsUnsignedLong', 'bool': 'PyObject_IsTrue',\n        'float': 'PyFloat_AsFloat', 'double': 'PyFloat_AsDouble', 'percent': 'percent',\n        'time': 'parse_s_double_to_monotonic_t', 'time-ms': 'parse_ms_long_to_monotonic_t'\n    }\n\n    for opt in ctypes:\n        lines.append('')\n        lines.append(f'static void\\nconvert_from_python_{opt.name}(PyObject *val, Options *opts) ''{')\n        is_special = opt.ctype.startswith('!')\n        if is_special:\n            func = opt.ctype[1:]\n            lines.append(f'    {func}(val, opts);')\n        else:\n            func = basic_converters.get(opt.ctype, opt.ctype)\n            lines.append(f'    opts->{opt.name} = {func}(val);')\n        lines.append('}')\n        lines.append('')\n        lines.append(f'static void\\nconvert_from_opts_{opt.name}(PyObject *py_opts, Options *opts) ''{')\n        lines.append(f'    PyObject *ret = PyObject_GetAttrString(py_opts, \"{opt.name}\");')\n        lines.append('    if (ret == NULL) return;')\n        lines.append(f'    convert_from_python_{opt.name}(ret, opts);')\n        lines.append('    Py_DECREF(ret);')\n        lines.append('}')\n\n    lines.append('')\n    lines.append('static bool\\nconvert_opts_from_python_opts(PyObject *py_opts, Options *opts) ''{')\n    for opt in ctypes:\n        lines.append(f'    convert_from_opts_{opt.name}(py_opts, opts);')\n        lines.append('    if (PyErr_Occurred()) return false;')\n    lines.append('    return true;')\n    lines.append('}')\n\n    preamble = ['// generated by gen-config.py DO NOT edit', '// vim:fileencoding=utf-8', '#pragma once', '#include \"to-c.h\"']\n    return '\\n'.join(preamble + ['', ''] + lines)\n\n\ndef write_output(loc: str, defn: Definition, extra_after_type_defn: str = '') -> None:\n    cls, tc = generate_class(defn, loc)\n    ctypes = []\n    has_secret = []\n    for opt in defn.root_group.iter_all_non_groups():\n        if isinstance(opt, (Option, MultiOption)) and opt.ctype:\n            ctypes.append(opt)\n        if getattr(opt, 'has_secret', False):\n            has_secret.append(opt.name)\n    with open(os.path.join(*loc.split('.'), 'options', 'types.py'), 'w') as f:\n        f.write(f'{cls}\\n')\n        f.write(extra_after_type_defn)\n        if has_secret:\n            f.write('\\n\\nsecret_options = ' + repr(tuple(has_secret)))\n    with open(os.path.join(*loc.split('.'), 'options', 'parse.py'), 'w') as f:\n        f.write(f'{tc}\\n')\n    if ctypes:\n        c = generate_c_conversion(loc, ctypes)\n        with open(os.path.join(*loc.split('.'), 'options', 'to-c-generated.h'), 'w') as f:\n            f.write(f'{c}\\n')\n\n\ndef go_type_data(parser_func: ParserFuncType, ctype: str, is_multiple: bool = False) -> tuple[str, str]:\n    if ctype or is_multiple:\n        if ctype in ('string', ''):\n            if is_multiple:\n                return 'string', '[]string{val}, nil'\n            return 'string', 'val, nil'\n        if ctype.startswith('strdict_'):\n            _, rsep, fsep = ctype.split('_', 2)\n            return 'map[string]string', f'config.ParseStrDict(val, `{rsep}`, `{fsep}`)'\n        return f'*{ctype}', f'Parse{ctype}(val)'\n    p = parser_func.__name__\n    if p == 'int':\n        return 'int64', 'strconv.ParseInt(val, 10, 64)'\n    if p == 'str':\n        return 'string', 'val, nil'\n    if p == 'float':\n        return 'float64', 'strconv.ParseFloat(val, 10, 64)'\n    if p == 'to_bool':\n        return 'bool', 'config.StringToBool(val), nil'\n    if p == 'to_color':\n        return 'style.RGBA', 'style.ParseColor(val)'\n    if p == 'to_color_or_none':\n        return 'style.NullableColor', 'style.ParseColorOrNone(val)'\n    if p == 'positive_int':\n        return 'uint64', 'strconv.ParseUint(val, 10, 64)'\n    if p == 'positive_float':\n        return 'float64', 'config.PositiveFloat(val)'\n    if p == 'unit_float':\n        return 'float64', 'config.UnitFloat(val)'\n    if p == 'python_string':\n        return 'string', 'config.StringLiteral(val)'\n    th = get_type_hints(parser_func)\n    rettype = th['return']\n    return {int: 'int64', str: 'string', float: 'float64'}[rettype], f'{p}(val)'\n\n\nmod_map = {\n\t\t\"shift\":     \"shift\",\n\t\t\"⇧\":         \"shift\",\n\t\t\"alt\":       \"alt\",\n\t\t\"option\":    \"alt\",\n\t\t\"opt\":       \"alt\",\n\t\t\"⌥\":         \"alt\",\n\t\t\"super\":     \"super\",\n\t\t\"command\":   \"super\",\n\t\t\"cmd\":       \"super\",\n\t\t\"⌘\":         \"super\",\n\t\t\"control\":   \"ctrl\",\n\t\t\"ctrl\":      \"ctrl\",\n\t\t\"⌃\":         \"ctrl\",\n\t\t\"hyper\":     \"hyper\",\n\t\t\"meta\":      \"meta\",\n\t\t\"num_lock\":  \"num_lock\",\n\t\t\"caps_lock\": \"caps_lock\",\n}\n\ndef normalize_shortcut(spec: str) -> str:\n    if spec.endswith('+'):\n        spec = spec[:-1] + 'plus'\n    parts = spec.lower().split('+')\n    key = parts[-1]\n    if len(parts) == 1:\n        return key\n    mods = parts[:-1]\n    return '+'.join(mod_map.get(x, x) for x in mods) + '+' + key\n\n\ndef normalize_shortcuts(spec: str) -> Iterator[str]:\n    spec = spec.replace('++', '+plus')\n    spec = re.sub(r'([^+])>', '\\\\1\\0', spec)\n    for x in spec.split('\\0'):\n        yield normalize_shortcut(x)\n\n\ndef gen_go_code(defn: Definition) -> str:\n    lines = ['import \"fmt\"', 'import \"strconv\"', 'import \"github.com/kovidgoyal/kitty/tools/config\"',\n             'import \"github.com/kovidgoyal/kitty/tools/utils/style\"',\n             'var _ = fmt.Println', 'var _ = config.StringToBool', 'var _ = strconv.Atoi', 'var _ = style.ParseColor']\n    a = lines.append\n    keyboard_shortcuts = tuple(defn.iter_all_maps())\n    choices = {}\n    go_types = {}\n    go_parsers = {}\n    defaults = {}\n    multiopts = {''}\n    for option in sorted(defn.iter_all_options(), key=lambda a: natural_keys(a.name)):\n        name = option.name.capitalize()\n        if isinstance(option, MultiOption):\n            go_types[name], go_parsers[name] = go_type_data(option.parser_func, option.ctype, True)\n            multiopts.add(name)\n            defval = []\n            for x in option.items:\n                if x.add_to_default:\n                    defval.append(option.parser_func(x.defval_as_str))\n            defaults[name] = defval\n        else:\n            defaults[name] = option.parser_func(option.defval_as_string)\n            if option.choices:\n                choices[name] = option.choices\n                go_types[name] = f'{name}_Choice_Type'\n                go_parsers[name] = f'Parse_{name}(val)'\n                continue\n            go_types[name], go_parsers[name] = go_type_data(option.parser_func, option.ctype)\n\n    for oname in choices:\n        a(f'type {go_types[oname]} int')\n    a('type Config struct {')\n    for name, gotype in go_types.items():\n        if name in multiopts:\n            a(f'{name} []{gotype}')\n        else:\n            a(f'{name} {gotype}')\n    if keyboard_shortcuts:\n        a('KeyboardShortcuts []*config.KeyAction')\n    a('}')\n\n    def cval(x: str) -> str:\n        return x.replace('-', '_')\n\n    a('func NewConfig() *Config {')\n    a('return &Config{')\n    from kitty.fast_data_types import Color\n\n    def basic_defval(d: Any) -> str:\n        if isinstance(d, str):\n            dval = f'{name}_{cval(d)}' if name in choices else f'`{d}`'\n        elif isinstance(d, bool):\n            dval = repr(d).lower()\n        elif isinstance(d, dict):\n            dval = 'map[string]string{'\n            for k, v in d.items():\n                dval += f'\"{serialize_as_go_string(k)}\": \"{serialize_as_go_string(v)}\",'\n            dval += '}'\n        elif isinstance(d, list):\n            dval = '[]string{'\n            for k in d:\n                dval += f'\"{serialize_as_go_string(k)}\",'\n            dval += '}'\n        elif isinstance(d, Color):\n            dval = f'style.RGBA{{Red:{d.red}, Green: {d.green}, Blue: {d.blue}}}'\n            if 'NullableColor' in go_types[name]:\n                dval = f'style.NullableColor{{IsSet: true, Color:{dval}}}'\n        else:\n            dval = repr(d)\n        return dval\n\n    for name, pname in go_parsers.items():\n        d = defaults[name]\n        if d:\n            a(f'{name}: {basic_defval(d)},')\n    if keyboard_shortcuts:\n        a('KeyboardShortcuts: []*config.KeyAction{')\n        for sc in keyboard_shortcuts:\n            aname, aargs = map(serialize_as_go_string, sc.action_def.partition(' ')[::2])\n            a('{'f'Name: \"{aname}\", Args: \"{aargs}\", Normalized_keys: []string''{')\n            ns = normalize_shortcuts(sc.key_text)\n            a(', '.join(f'\"{serialize_as_go_string(x)}\"' for x in ns) + ',')\n            a('}''},')\n        a('},')\n\n    a('}''}')\n\n    for oname, choice_vals in choices.items():\n        a('const (')\n        for i, c in enumerate(choice_vals):\n            c = cval(c)\n            if i == 0:\n                a(f'{oname}_{c} {oname}_Choice_Type = iota')\n            else:\n                a(f'{oname}_{c}')\n        a(')')\n        a(f'func (x {oname}_Choice_Type) String() string'' {')\n        a('switch x {')\n        a('default: return \"\"')\n        for c in choice_vals:\n            a(f'case {oname}_{cval(c)}: return \"{c}\"')\n        a('}''}')\n        a(f'func {go_parsers[oname].split(\"(\")[0]}(val string) (ans {go_types[oname]}, err error) ''{')\n        a('switch val {')\n        for c in choice_vals:\n            a(f'case \"{c}\": return {oname}_{cval(c)}, nil')\n        vals = ', '.join(choice_vals)\n        a(f'default: return ans, fmt.Errorf(\"%#v is not a valid value for %s. Valid values are: %s\", val, \"{c}\", \"{vals}\")')\n        a('}''}')\n\n    has_parsers = bool(go_parsers or keyboard_shortcuts)\n    a('func (c *Config) Parse(key, val string) (err error) {')\n    if has_parsers:\n        if go_parsers:\n            a('switch key {')\n            a('default: return fmt.Errorf(\"Unknown configuration key: %#v\", key)')\n            for oname, pname in go_parsers.items():\n                ol = oname.lower()\n                is_multiple = oname in multiopts\n                a(f'case \"{ol}\":')\n                if is_multiple:\n                    a(f'var temp_val []{go_types[oname]}')\n                else:\n                    a(f'var temp_val {go_types[oname]}')\n                a(f'temp_val, err = {pname}')\n                a(f'if err != nil {{ return fmt.Errorf(\"Failed to parse {ol} = %#v with error: %w\", val, err) }}')\n                if is_multiple:\n                    a(f'c.{oname} = append(c.{oname}, temp_val...)')\n                else:\n                    a(f'c.{oname} = temp_val')\n        if keyboard_shortcuts:\n            a('case \"map\":')\n            a('tempsc, err := config.ParseMap(val)')\n            a('if err != nil { return fmt.Errorf(\"Failed to parse map = %#v with error: %w\", val, err) }')\n            a('c.KeyboardShortcuts = append(c.KeyboardShortcuts, tempsc)')\n        a('}')\n        a('return}')\n    else:\n        a('return fmt.Errorf(\"Unknown configuration key: %#v\", key)')\n        a('}')\n    return '\\n'.join(lines)\n\n\ndef main() -> None:\n    # To use run it as:\n    # kitty +runpy 'from kitty.conf.generate import main; main()' /path/to/kitten/file.py\n    import importlib\n    import sys\n\n    from kittens.runner import path_to_custom_kitten, resolved_kitten\n    from kitty.constants import config_dir\n\n    kitten = sys.argv[-1]\n    if not kitten.endswith('.py'):\n        kitten += '.py'\n    kitten = resolved_kitten(kitten)\n    path = os.path.realpath(path_to_custom_kitten(config_dir, kitten))\n    if not os.path.dirname(path):\n        raise SystemExit(f'No custom kitten named {kitten} found')\n    sys.path.insert(0, os.path.dirname(path))\n    package_name = os.path.basename(os.path.dirname(path))\n    m = importlib.import_module('kitten_options_definition')\n    defn = getattr(m, 'definition')\n    loc = package_name\n    cls, tc = generate_class(defn, loc)\n    with open(os.path.join(os.path.dirname(path), 'kitten_options_types.py'), 'w') as f:\n        f.write(f'{cls}\\n')\n    with open(os.path.join(os.path.dirname(path), 'kitten_options_parse.py'), 'w') as f:\n        f.write(f'{tc}\\n')\n"
  },
  {
    "path": "kitty/conf/types.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport builtins\nimport re\nimport textwrap\nimport typing\nfrom collections.abc import Callable, Iterable, Iterator\nfrom functools import lru_cache\nfrom importlib import import_module\nfrom re import Match\nfrom typing import Any, Optional, Sequence, Union, cast\n\nimport kitty.conf.utils as generic_parsers\nfrom kitty.constants import website_url\nfrom kitty.types import run_once\n\nif typing.TYPE_CHECKING:\n    Only = typing.Literal['macos', 'linux', '']\nelse:\n    Only = str\n\n\nclass Unset:\n    def __bool__(self) -> bool:\n        return False\n\n\nunset = Unset()\nParserFuncType = Callable[[str], Any]\n\n\ndef expand_opt_references(conf_name: str, text: str) -> str:\n    conf_name += '.'\n\n    def expand(m: 'Match[str]') -> str:\n        ref = m.group(1)\n        if '<' not in ref and '.' not in ref:\n            # full ref\n            return f':opt:`{ref} <{conf_name}{ref}>`'\n        return str(m.group())\n\n    return re.sub(r':opt:`(.+?)`', expand, text)\n\n\n@run_once\ndef ref_map() -> dict[str, dict[str, str]]:\n    import json\n\n    from ..fast_data_types import get_docs_ref_map\n    ans: dict[str, dict[str, str]] = json.loads(get_docs_ref_map())\n    return ans\n\n\ndef resolve_ref(ref: str, website_url: Callable[[str], str] = website_url) -> str:\n    m = ref_map()\n    href = m['ref'].get(ref, '')\n    prefix, rest = ref.partition('-')[::2]\n    if href:\n        pass\n    elif ref.startswith('conf-kitty-'):\n        href = f'conf#{ref}'\n    elif ref.startswith('conf-kitten-'):\n        parts = ref.split('-')\n        href = \"kittens/\" + parts[2] + f'/#{ref}'\n    elif ref.startswith('at_'):\n        base = ref.split('_', 1)[1]\n        href = \"remote-control/#at-\" + base.replace('_', '-')\n    elif ref.startswith('at-'):\n        base = ref.split('-', 1)[1]\n        href = \"remote-control/#at-\" + base.replace('_', '-')\n    elif ref.startswith('action-group-'):\n        href = f'actions/#{ref}'\n    elif prefix == 'action':\n        href = f'actions/#{rest.replace(\"_\", \"-\")}'\n    elif prefix in ('term', 'envvar'):\n        href = 'glossary/#' + ref\n    elif prefix == 'doc':\n        href = rest.lstrip('/')\n    elif prefix in ('issues', 'pull', 'discussions'):\n        t, num = ref.partition(':')[::2]\n        href = f'https://github.com/kovidgoyal/kitty/{prefix}/{rest}'\n    if not (href.startswith('https://') or href.startswith('http://')):\n        href = website_url(href)\n    return href\n\n\ndef remove_markup(text: str) -> str:\n\n    imap = {'iss': 'issues-', 'pull': 'pull-', 'disc': 'discussions-'}\n\n    def extract(m: 'Match[str]') -> tuple[str, str]:\n        parts = m.group(2).split('<')\n        t = parts[0].strip()\n        q = parts[-1].rstrip('>')\n        return t, q\n\n    def sub(m: 'Match[str]') -> str:\n        key = m.group(1)\n        if key in ('ref', 'iss', 'pull', 'disc'):\n            t, q = extract(m)\n            q = imap.get(key, '') + q\n            url = resolve_ref(q)\n            if not url:\n                raise KeyError(f'Failed to resolve :{m.group(1)}: {q}')\n            return f'{t} <{url}>'\n        if key == 'doc':\n            t, q = extract(m)\n            return f'{t} <{website_url(q)}>'\n        if key in ('term', 'option'):\n            t, _ = extract(m)\n            return t\n        if key in ('ac', 'opt'):\n            t, q = extract(m)\n            return f'{t} {q}' if q and q != t else t\n        if key == 'code':\n            return m.group(2).replace('\\\\\\\\', '\\\\')\n\n        return str(m.group(2))\n\n    return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)\n\n\ndef strip_inline_literal(text: str) -> str:\n    return re.sub(r'``([^`]+)``', r'`\\1`', text, flags=re.DOTALL)\n\n\ndef iter_blocks(lines: Iterable[str]) -> Iterator[tuple[list[str], int]]:\n    current_block: list[str] = []\n    prev_indent = 0\n    for line in lines:\n        indent_size = len(line) - len(line.lstrip())\n        if indent_size != prev_indent or not line:\n            if current_block:\n                yield current_block, prev_indent\n            current_block = []\n        prev_indent = indent_size\n        if not line:\n            yield [''], 100\n        else:\n            current_block.append(line)\n    if current_block:\n        yield current_block, indent_size\n\n\n@lru_cache(maxsize=8)\ndef block_wrapper(comment_symbol: str) -> textwrap.TextWrapper:\n    return textwrap.TextWrapper(\n            initial_indent=comment_symbol, subsequent_indent=comment_symbol, width=70, break_long_words=False\n        )\n\n\ndef wrapped_block(lines: Iterable[str], comment_symbol: str = '#: ') -> Iterator[str]:\n    wrapper = block_wrapper(comment_symbol)\n    for block, indent_size in iter_blocks(lines):\n        if indent_size > 0:\n            for line in block:\n                if not line:\n                    yield line\n                else:\n                    yield comment_symbol + line\n        else:\n            for line in wrapper.wrap('\\n'.join(block)):\n                yield line\n\n\ndef render_block(text: str, comment_symbol: str = '#: ') -> str:\n    text = remove_markup(text)\n    text = strip_inline_literal(text)\n    lines = text.splitlines()\n    return '\\n'.join(wrapped_block(lines, comment_symbol))\n\n\nclass CoalescedIteratorData:\n\n    option_groups: dict[int, list['Option']] = {}\n    action_groups: dict[str, list['Mapping']] = {}\n    coalesced: set[int] = set()\n    initialized: bool = False\n    kitty_mod: str = 'kitty_mod'\n\n    def initialize(self, root: 'Group') -> None:\n        if self.initialized:\n            return\n        self.root = root\n        option_groups = self.option_groups = {}\n        current_group: list[Option] = []\n        action_groups: dict[str, list[Mapping]] = {}\n        self.action_groups = action_groups\n        coalesced = self.coalesced = set()\n        self.kitty_mod = 'kitty_mod'\n        for item in root.iter_all_non_groups():\n            if isinstance(item, Option):\n                if item.name == 'kitty_mod':\n                    self.kitty_mod = item.defval_as_string\n                if current_group:\n                    if item.needs_coalescing:\n                        current_group.append(item)\n                        coalesced.add(id(item))\n                        continue\n                    option_groups[id(current_group[0])] = current_group[1:]\n                    current_group = [item]\n                else:\n                    current_group.append(item)\n            elif isinstance(item, Mapping):\n                if item.name in action_groups:\n                    coalesced.add(id(item))\n                    action_groups[item.name].append(item)\n                else:\n                    action_groups[item.name] = []\n        if current_group:\n            option_groups[id(current_group[0])] = current_group[1:]\n\n    def option_group_for_option(self, opt: 'Option') -> list['Option']:\n        return self.option_groups.get(id(opt), [])\n\n    def action_group_for_action(self, ac: 'Mapping') -> list['Mapping']:\n        return self.action_groups.get(ac.name, [])\n\n\nclass Option:\n\n    def __init__(\n        self, name: str, defval: str, macos_default: Unset | str, parser_func: ParserFuncType,\n        long_text: str, documented: bool, group: 'Group', choices: tuple[str, ...], ctype: str,\n        has_secret: bool = False,\n    ):\n        self.name = name\n        self.ctype = ctype\n        self.defval_as_string = defval\n        self.macos_defval = macos_default\n        self.long_text = long_text\n        self.documented = documented\n        self.group = group\n        self.parser_func = parser_func\n        self.choices = choices\n        self.has_secret = has_secret\n\n    @property\n    def needs_coalescing(self) -> bool:\n        return self.documented and not self.long_text\n\n    @property\n    def is_color_table_color(self) -> bool:\n        return self.name.startswith('color') and self.name[5:].isdigit()\n\n    def as_conf(self, commented: bool = False, level: int = 0, option_group: Sequence['Option'] = ()) -> list[str]:\n        ans: list[str] = []\n        a = ans.append\n        if not self.documented:\n            return ans\n        if option_group:\n            sz = max(len(self.name), max(len(o.name) for o in option_group))\n            a(f'{self.name.ljust(sz)} {self.defval_as_string}'.rstrip())\n            for o in option_group:\n                a(f'{o.name.ljust(sz)} {o.defval_as_string}'.rstrip())\n        else:\n            a(f'{self.name} {self.defval_as_string}'.rstrip())\n        if self.long_text:\n            a('')\n            a(render_block(self.long_text))\n            a('')\n        return ans\n\n    def as_rst(\n        self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]],\n        kitty_mod: str, level: int = 0, option_group: Sequence['Option'] = ()\n    ) -> list[str]:\n        ans: list[str] = []\n        a = ans.append\n        if not self.documented:\n            return ans\n        mopts = [self] + list(option_group)\n        a('.. opt:: ' + ', '.join(f'{conf_name}.{mo.name}' for mo in mopts))\n        if any(mo.defval_as_string for mo in mopts):\n            a('.. code-block:: conf')\n            a('')\n            sz = max(len(x.name) for x in mopts)\n            for mo in mopts:\n                a(('    {:%ds} {}' % sz).format(mo.name, mo.defval_as_string))\n        a('')\n        if self.long_text:\n            a(expand_opt_references(conf_name, self.long_text))\n            a('')\n        return ans\n\n\nclass MultiVal:\n\n    def __init__(self, val_as_str: str, add_to_default: bool, documented: bool, only: Only) -> None:\n        self.defval_as_str = val_as_str\n        self.documented = documented\n        self.only = only\n        self.add_to_default = add_to_default\n\n\nclass MultiOption:\n\n    def __init__(self, name: str, parser_func: ParserFuncType, long_text: str, group: 'Group', ctype: str, has_secret: bool = False):\n        self.name = name\n        self.ctype = ctype\n        self.parser_func = parser_func\n        self.long_text = long_text\n        self.group = group\n        self.has_secret = has_secret\n        self.items: list[MultiVal] = []\n\n    def add_value(self, val_as_str: str, add_to_default: bool, documented: bool, only: Only) -> None:\n        self.items.append(MultiVal(val_as_str, add_to_default, documented, only))\n\n    def __iter__(self) -> Iterator[MultiVal]:\n        yield from self.items\n\n    def as_conf(self, commented: bool = False, level: int = 0) -> list[str]:\n        ans: list[str] = []\n        a = ans.append\n        documented = False\n        for k in self.items:\n            if k.documented:\n                documented = True\n                if k.add_to_default:\n                    a(f'{self.name} {k.defval_as_str}'.rstrip())\n                else:\n                    # Comment out multi-options that have no default values\n                    a(f'# {self.name}'.rstrip())\n                if not k.add_to_default and k.defval_as_str:\n                    a('')\n                    a(f'#: E.g. {self.name} {k.defval_as_str}'.rstrip())\n        if self.long_text and documented:\n            a('')\n            a(render_block(self.long_text))\n            a('')\n        return ans\n\n    def as_rst(self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]], kitty_mod: str, level: int = 0) -> list[str]:\n        ans: list[str] = []\n        a = ans.append\n        a(f'.. opt:: {conf_name}.{self.name}')\n        documented = tuple(x for x in self.items if x.documented)\n        if any(k.defval_as_str for k in documented):\n            defaults = tuple(x for x in documented if x.add_to_default)\n            if defaults:\n                a('.. code-block:: conf')\n                a('')\n                for k in defaults:\n                    a(f'    {self.name:s} {k.defval_as_str}'.rstrip())\n            else:\n                a('')\n                a('Has no default values. Example values are shown below:')\n                a('')\n                a('.. code-block:: conf')\n                a('')\n                for k in self.items:\n                    a(f'    {self.name:s} {k.defval_as_str}'.rstrip())\n        a('')\n        if self.long_text:\n            a(expand_opt_references(conf_name, self.long_text))\n            a('')\n        return ans\n\n\nclass Mapping:\n    add_to_default: bool\n    short_text: str\n    long_text: str\n    documented: bool\n    setting_name: str\n    name: str\n    only: Only\n\n    @property\n    def parseable_text(self) -> str:\n        return ''\n\n    @property\n    def key_text(self) -> str:\n        return ''\n\n    def as_conf(self, commented: bool = False, level: int = 0, action_group: list['Mapping'] = []) -> list[str]:\n        ans: list[str] = []\n        if not self.documented:\n            return ans\n        a = ans.append\n        if self.short_text:\n            a(render_block(self.short_text.strip())), a('')\n        for sc in [self] + action_group:\n            if sc.documented:\n                prefix = '' if sc.add_to_default else '#::  E.g. '\n                a(f'{prefix}{sc.setting_name} {sc.parseable_text}')\n        if self.long_text:\n            a(''), a(render_block(self.long_text.strip(), '#::  '))\n        a('')\n        return ans\n\n    def as_rst(\n        self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]],\n        kitty_mod: str, level: int = 0, action_group: list['Mapping'] = []\n    ) -> list[str]:\n        ans: list[str] = []\n        a = ans.append\n        if not self.documented:\n            return ans\n        if not self.short_text:\n            raise ValueError(f'The shortcut for {self.name} has no short_text')\n        sc_text = f'{conf_name}.{self.short_text}'\n        shortcut_slugs[f'{conf_name}.{self.name}'] = (sc_text, self.key_text.replace('kitty_mod', kitty_mod))\n        a(f'.. shortcut:: {sc_text}')\n        block_started = False\n        for sc in [self] + action_group:\n            if sc.add_to_default and sc.documented:\n                if not block_started:\n                    a('.. code-block:: conf')\n                    a('')\n                    block_started = True\n                suffix = ''\n                if sc.only == 'macos':\n                    suffix = ' 🍎'\n                elif sc.only == 'linux':\n                    suffix = ' 🐧'\n                a(f'    {sc.setting_name} {sc.parseable_text.replace(\"kitty_mod\", kitty_mod)}{suffix}')\n        a('')\n        if self.long_text:\n            a('')\n            a(expand_opt_references(conf_name, self.long_text))\n            a('')\n\n        return ans\n\n\nclass ShortcutMapping(Mapping):\n    setting_name: str = 'map'\n\n    def __init__(\n        self, name: str, key: str, action_def: str, short_text: str, long_text: str, add_to_default: bool, documented: bool, group: 'Group', only: Only\n    ):\n        self.name = name\n        self.only = only\n        self.key = key\n        self.action_def = action_def\n        self.short_text = short_text\n        self.long_text = long_text\n        self.documented = documented\n        self.add_to_default = add_to_default\n        self.group = group\n\n    @property\n    def parseable_text(self) -> str:\n        return f'{self.key} {self.action_def}'\n\n    @property\n    def key_text(self) -> str:\n        return self.key\n\n\nclass MouseMapping(Mapping):\n    setting_name: str = 'mouse_map'\n\n    def __init__(\n        self, name: str, button: str, event: str, modes: str, action_def: str,\n        short_text: str, long_text: str, add_to_default: bool, documented: bool, group: 'Group', only: Only\n    ):\n        self.name = name\n        self.only = only\n        self.button = button\n        self.event = event\n        self.modes = modes\n        self.action_def = action_def\n        self.short_text = short_text\n        self.long_text = long_text\n        self.documented = documented\n        self.add_to_default = add_to_default\n        self.group = group\n\n    @property\n    def parseable_text(self) -> str:\n        return f'{self.button} {self.event} {self.modes} {self.action_def}'\n\n    @property\n    def key_text(self) -> str:\n        return self.button\n\n\nNonGroups = Union[Option, MultiOption, ShortcutMapping, MouseMapping]\nGroupItem = Union[NonGroups, 'Group']\n\n\nclass Group:\n\n    def __init__(self, name: str, title: str, coalesced_iterator_data: CoalescedIteratorData, start_text: str = '', parent: Optional['Group'] = None):\n        self.name = name\n        self.coalesced_iterator_data = coalesced_iterator_data\n        self.title = title\n        self.start_text = start_text\n        self.end_text = ''\n        self.items: list[GroupItem] = []\n        self.parent = parent\n\n    def append(self, item: GroupItem) -> None:\n        self.items.append(item)\n\n    def __iter__(self) -> Iterator[GroupItem]:\n        return iter(self.items)\n\n    def __len__(self) -> int:\n        return len(self.items)\n\n    def iter_with_coalesced_options(self) -> Iterator[GroupItem]:\n        for item in self:\n            if id(item) not in self.coalesced_iterator_data.coalesced:\n                yield item\n\n    def iter_all(self) -> Iterator[GroupItem]:\n        for x in self:\n            yield x\n            if isinstance(x, Group):\n                yield from x.iter_all()\n\n    def iter_all_non_groups(self) -> Iterator[NonGroups]:\n        for x in self:\n            if isinstance(x, Group):\n                yield from x.iter_all_non_groups()\n            else:\n                yield x\n\n    def as_rst(self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]], kitty_mod: str = 'kitty_mod', level: int = 0) -> list[str]:\n        ans: list[str] = []\n        a = ans.append\n        if level:\n            a('')\n            a(f'.. _conf-{conf_name}-{self.name}:')\n            a('')\n            a(self.title)\n            heading_level = '+' if level > 1 else '-'\n            a(heading_level * (len(self.title) + 20))\n            a('')\n            if self.start_text:\n                a(self.start_text)\n                a('')\n        else:\n            ans.extend(('.. default-domain:: conf', ''))\n\n        kitty_mod = self.coalesced_iterator_data.kitty_mod\n        for item in self.iter_with_coalesced_options():\n            if isinstance(item, Option):\n                lines = item.as_rst(conf_name, shortcut_slugs, kitty_mod, option_group=self.coalesced_iterator_data.option_group_for_option(item))\n            elif isinstance(item, Mapping):\n                lines = item.as_rst(conf_name, shortcut_slugs, kitty_mod, level + 1, action_group=self.coalesced_iterator_data.action_group_for_action(item))\n            else:\n                lines = item.as_rst(conf_name, shortcut_slugs, kitty_mod, level + 1)\n            ans.extend(lines)\n\n        if level:\n            if self.end_text:\n                a('')\n                a(self.end_text)\n        return ans\n\n    def as_conf(self, commented: bool = False, level: int = 0) -> list[str]:\n        ans: list[str] = []\n        a = ans.append\n        if level:\n            a('#: ' + self.title + ' {{''{')\n            a('')\n            if self.start_text:\n                a(render_block(self.start_text))\n                a('')\n        else:\n            ans.extend(('# vim:fileencoding=utf-8:foldmethod=marker', ''))\n\n        for item in self.iter_with_coalesced_options():\n            if isinstance(item, Option):\n                lines = item.as_conf(option_group=self.coalesced_iterator_data.option_group_for_option(item))\n            elif isinstance(item, Mapping):\n                lines = item.as_conf(commented, level + 1, action_group=self.coalesced_iterator_data.action_group_for_action(item))\n            else:\n                lines = item.as_conf(commented, level + 1)\n            ans.extend(lines)\n\n        if level:\n            if self.end_text:\n                a('')\n                a(render_block(self.end_text))\n            a('#: }}''}')\n            a('')\n        else:\n            map_groups = []\n            start: int | None = None\n            count: int | None = None\n            for i, line in enumerate(ans):\n                if line.startswith('map ') or line.startswith('mouse_map '):\n                    if start is None:\n                        start = i\n                        count = 1\n                    else:\n                        if count is not None:\n                            count += 1\n                else:\n                    if start is not None and count is not None:\n                        map_groups.append((start, count))\n                        start = count = None\n            for start, count in map_groups:\n                r = range(start, start + count)\n                sz = max(len(ans[i].split(' ', 3)[1]) for i in r)\n                for i in r:\n                    line = ans[i]\n                    parts = line.split(' ', 3)\n                    parts[1] = parts[1].ljust(sz)\n                    ans[i] = ' '.join(parts)\n\n            if commented:\n                ans = [x if x.startswith('#') or not x.strip() else (f'# {x}') for x in ans]\n            else:\n                # Comment out any invalid options that have no value\n                ans = [f'# {x}' if not x.startswith('#') and len(x.strip().split()) == 1 else x for x in ans]\n\n        return ans\n\n\ndef resolve_import(name: str, module: Any = None) -> ParserFuncType:\n    ans = None\n    if name.count('.') > 1:\n        m = import_module(name.rpartition('.')[0])\n        ans = getattr(m, name.rpartition('.')[2])\n    else:\n        ans = getattr(builtins, name, None)\n        if not callable(ans):\n            ans = getattr(generic_parsers, name, None)\n            if not callable(ans):\n                ans = getattr(module, name)\n    if not callable(ans):\n        raise TypeError(f'{name} is not a function')\n    return cast(ParserFuncType, ans)\n\n\nclass Action:\n\n    def __init__(self, name: str, option_type: str, fields: dict[str, str], imports: Iterable[str]):\n        self.name = name\n        self._parser_func = option_type\n        self.fields = fields\n        self.imports = frozenset(imports)\n\n    def resolve_imports(self, module: Any) -> 'Action':\n        self.parser_func = resolve_import(self._parser_func, module)\n        return self\n\n\nclass Definition:\n\n    def __init__(self, package: str, *actions: Action, has_color_table: bool = False) -> None:\n        if package.startswith('!'):\n            self.module_for_parsers = import_module(package[1:])\n        else:\n            self.module_for_parsers = import_module(f'{package}.options.utils')\n        self.has_color_table = has_color_table\n        self.coalesced_iterator_data = CoalescedIteratorData()\n        self.root_group = Group('', '', self.coalesced_iterator_data)\n        self.current_group = self.root_group\n        self.option_map: dict[str, Option] = {}\n        self.multi_option_map: dict[str, MultiOption] = {}\n        self.shortcut_map: dict[str, list[ShortcutMapping]] = {}\n        self.mouse_map: dict[str, list[MouseMapping]] = {}\n        self.actions = {a.name: a.resolve_imports(self.module_for_parsers) for a in actions}\n        self.deprecations: dict[ParserFuncType, tuple[str, ...]] = {}\n\n    def iter_all_non_groups(self) -> Iterator[NonGroups]:\n        yield from self.root_group.iter_all_non_groups()\n\n    def iter_all_options(self) -> Iterator[Option | MultiOption]:\n        for x in self.iter_all_non_groups():\n            if isinstance(x, (Option, MultiOption)):\n                yield x\n\n    def iter_all_maps(self, which: str = 'map') -> Iterator[ShortcutMapping | MouseMapping]:\n        for x in self.iter_all_non_groups():\n            if isinstance(x, ShortcutMapping) and which in ('map', '*'):\n                yield x\n            elif isinstance(x, MouseMapping) and which in ('mouse_map', '*'):\n                yield x\n\n    def parser_func(self, name: str) -> ParserFuncType:\n        ans = getattr(builtins, name, None)\n        if callable(ans):\n            return cast(ParserFuncType, ans)\n        ans = getattr(generic_parsers, name, None)\n        if callable(ans):\n            return cast(ParserFuncType, ans)\n        ans = getattr(self.module_for_parsers, name)\n        if not callable(ans):\n            raise TypeError(f'{name} is not a function')\n        return cast(ParserFuncType, ans)\n\n    def add_group(self, name: str, title: str = '', start_text: str = '') -> None:\n        self.current_group = Group(name, title or name, self.coalesced_iterator_data, start_text.strip(), self.current_group)\n        if self.current_group.parent is not None:\n            self.current_group.parent.append(self.current_group)\n\n    def end_group(self, end_text: str = '') -> None:\n        self.current_group.end_text = end_text.strip()\n        if self.current_group.parent is not None:\n            self.current_group = self.current_group.parent\n\n    def add_option(\n        self, name: str, defval: str | float | int | bool,\n        option_type: str = 'str', long_text: str = '',\n        documented: bool = True, add_to_default: bool = False,\n        only: Only = '', macos_default: Unset | str = unset,\n        choices: tuple[str, ...] = (),\n        ctype: str = '', has_secret: bool = False,\n    ) -> None:\n        if isinstance(defval, bool):\n            defval = 'yes' if defval else 'no'\n        else:\n            defval = str(defval)\n        is_multiple = name.startswith('+')\n        long_text = long_text.strip()\n        if is_multiple:\n            name = name[1:]\n            if macos_default is not unset:\n                raise TypeError(f'Cannot specify macos_default for is_multiple option: {name} use only instead')\n            is_new = name not in self.multi_option_map\n            if is_new:\n                self.multi_option_map[name] = MultiOption(name, self.parser_func(option_type), long_text, self.current_group, ctype, has_secret)\n            mopt = self.multi_option_map[name]\n            if is_new:\n                self.current_group.append(mopt)\n            mopt.add_value(defval, add_to_default, documented, only)\n            return\n        opt = Option(name, defval, macos_default, self.parser_func(option_type), long_text, documented, self.current_group, choices, ctype, has_secret)\n        self.current_group.append(opt)\n        self.option_map[name] = opt\n\n    def add_map(\n        self, short_text: str, defn: str, long_text: str = '', add_to_default: bool = True, documented: bool = True, only: Only = ''\n    ) -> None:\n        name, key, action_def = defn.split(maxsplit=2)\n        sc = ShortcutMapping(name, key, action_def, short_text, long_text.strip(), add_to_default, documented, self.current_group, only)\n        self.current_group.append(sc)\n        self.shortcut_map.setdefault(name, []).append(sc)\n\n    def add_mouse_map(\n        self, short_text: str, defn: str, long_text: str = '', add_to_default: bool = True, documented: bool = True, only: Only = ''\n    ) -> None:\n        name, button, event, modes, action_def = defn.split(maxsplit=4)\n        mm = MouseMapping(name, button, event, modes, action_def, short_text, long_text.strip(), add_to_default, documented, self.current_group, only)\n        self.current_group.append(mm)\n        self.mouse_map.setdefault(name, []).append(mm)\n\n    def add_deprecation(self, parser_name: str, *aliases: str) -> None:\n        self.deprecations[self.parser_func(parser_name)] = aliases\n\n    def as_conf(self, commented: bool = False) -> list[str]:\n        self.coalesced_iterator_data.initialize(self.root_group)\n        return self.root_group.as_conf(commented)\n\n    def as_rst(self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]]) -> list[str]:\n        self.coalesced_iterator_data.initialize(self.root_group)\n        return self.root_group.as_rst(conf_name, shortcut_slugs)\n"
  },
  {
    "path": "kitty/conf/utils.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nimport sys\nfrom collections.abc import Callable, Generator, Iterable, Iterator, Sequence\nfrom contextlib import contextmanager\nfrom typing import (\n    Any,\n    Generic,\n    Literal,\n    NamedTuple,\n    TypeVar,\n)\n\nfrom ..constants import _plat, is_macos\nfrom ..fast_data_types import Color\nfrom ..rgb import to_color as as_color\nfrom ..types import ConvertibleToNumbers, ParsedShortcut, run_once\nfrom ..typing_compat import Protocol\nfrom ..utils import expandvars, log_error, shlex_split\n\nkey_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\\s+(.+)$')\nnumber_unit_pat = re.compile(r'\\s*([-+]?\\d+\\.?\\d*)\\s*([^\\d\\s]*)?')\nItemParser = Callable[[str, str, dict[str, Any]], bool]\nT = TypeVar('T')\n\n\nclass OptionsProtocol(Protocol):\n\n    def _asdict(self) -> dict[str, Any]:\n        pass\n\n\nclass BadLine(NamedTuple):\n    number: int\n    line: str\n    exception: Exception\n    file: str\n\n\ndef positive_int(x: ConvertibleToNumbers) -> int:\n    return max(0, int(x))\n\n\ndef positive_float(x: ConvertibleToNumbers) -> float:\n    return max(0, float(x))\n\n\ndef percent(x: str) -> float:\n    return float(x.rstrip('%')) / 100.\n\n\ndef to_color(x: str) -> Color:\n    ans = as_color(x, validate=True)\n    if ans is None:  # this is only for type-checking\n        ans = Color(0, 0, 0)\n    return ans\n\n\ndef to_color_or_none(x: str) -> Color | None:\n    return None if x.lower() == 'none' else to_color(x)\n\n\ndef unit_float(x: ConvertibleToNumbers) -> float:\n    return max(0, min(float(x), 1))\n\n\ndef number_with_unit(x: str, default_unit: str, *extra_units: str) -> tuple[float, str]:\n    if (mat := number_unit_pat.match(x)) is not None:\n        try:\n            value = float(mat.group(1))\n        except Exception as e:\n            raise ValueError(f'Not a number: {x} with error: {e}')\n        unit = mat.group(2) or default_unit\n        if unit != default_unit and unit not in extra_units:\n            raise ValueError(f'Not a valid unit: {x}. Allowed units are: {default_unit}, {\", \".join(extra_units)}')\n        return value, unit\n    raise ValueError(f'Invalid number with unit: {x}')\n\n\ndef to_bool(x: str) -> bool:\n    return x.lower() in ('y', 'yes', 'true')\n\n\nclass ToCmdline:\n\n    def __init__(self) -> None:\n        self.override_env: dict[str, str] | None = None\n\n    def __enter__(self) -> 'ToCmdline':\n        return self\n\n    def __exit__(self, *a: Any) -> None:\n        self.override_env = None\n\n    def filter_env_vars(self, *a: str, **override: str) -> 'ToCmdline':\n        remove = frozenset(a)\n        self.override_env = {k: v for k, v in os.environ.items() if k not in remove}\n        self.override_env.update(override)\n        return self\n\n    def __call__(self, x: str, expand: bool = True) -> list[str]:\n        if expand:\n            ans = list(\n                map(\n                    lambda y: expandvars(\n                        os.path.expanduser(y),\n                        os.environ if self.override_env is None else self.override_env,\n                        fallback_to_os_env=False\n                    ),\n                    shlex_split(x)\n                )\n            )\n        else:\n            ans = list(shlex_split(x))\n        return ans\n\n\nto_cmdline_implementation = ToCmdline()\n\n\ndef to_cmdline(x: str, expand: bool = True) -> list[str]:\n    return to_cmdline_implementation(x, expand)\n\n\ndef python_string(text: str) -> str:\n    from ast import literal_eval\n    text = (text[:-1] + \"\\\\'\") if text.endswith(\"'\") else text\n    ans: str = literal_eval(\"'''\" + text.replace(\"'''\", \"'\\\\''\") + \"'''\")\n    return ans\n\n\nclass Choice:\n\n    def __init__(self, choices: Sequence[str]):\n        self.defval = choices[0]\n        self.all_choices = frozenset(choices)\n\n    def __call__(self, x: str) -> str:\n        x = x.lower()\n        if x not in self.all_choices:\n            raise ValueError(f'The value {x} is not a known choice')\n        return x\n\n\ndef choices(*choices: str) -> Choice:\n    return Choice(choices)\n\n\nclass CurrentlyParsing:\n    __slots__ = 'line', 'number', 'file'\n\n    def __init__(self, line: str = '', number: int = -1, file: str = ''):\n        self.line = line\n        self.number = number\n        self.file = file\n\n    def __copy__(self) -> 'CurrentlyParsing':\n        return CurrentlyParsing(self.line, self.number, self.file)\n\n    @contextmanager\n    def set_line(self, line: str, number: int) -> Iterator['CurrentlyParsing']:\n        orig = self.line, self.number\n        self.line = line\n        self.number = number\n        try:\n            yield self\n        finally:\n            self.line, self.number = orig\n\n    @contextmanager\n    def set_file(self, file: str) -> Iterator['CurrentlyParsing']:\n        orig = self.file\n        self.file = file\n        try:\n            yield self\n        finally:\n            self.file = orig\n\n\ncurrently_parsing = CurrentlyParsing()\nOSNames = Literal['macos', 'bsd', 'linux', 'unknown']\n\n@run_once\ndef os_name() -> OSNames:\n    if is_macos:\n        return 'macos'\n    if 'bsd' in _plat:\n        return 'bsd'\n    if 'linux' in _plat:\n        return 'linux'\n    return 'unknown'\n\n\nclass NamedLineIterator:\n\n    def __init__(self, name: str, lines: Iterator[str]):\n        self.lines = lines\n        self.name = name\n\n    def __iter__(self) -> Iterator[str]:\n        return self.lines\n\n\nclass GenincludeError(Exception): ...\n\n\ndef pygeninclude(path: str) -> list[str]:\n    import io\n    import runpy\n    before = sys.stdout\n    buf = sys.stdout = io.StringIO()\n    try:\n        runpy.run_path(path, run_name='__main__')\n    except FileNotFoundError:\n        raise\n    except Exception:\n        import traceback\n        tb = traceback.format_exc()\n        raise GenincludeError(f'Running the geninclude program: {path} failed with the error:\\n{tb}')\n    finally:\n        sys.stdout = before\n    return buf.getvalue().splitlines()\n\n\ndef geninclude(path: str) -> list[str]:\n    old = os.environ.get('KITTY_OS')\n    os.environ['KITTY_OS'] = os_name()\n    try:\n        if path.endswith('.py'):\n            return pygeninclude(path)\n        import subprocess\n        cp = subprocess.run([path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)\n        if cp.returncode != 0:\n            raise GenincludeError(f'Running the geninclude program: {path} failed with exit code: {cp.returncode} and STDERR:\\n{cp.stderr}')\n        return cp.stdout.splitlines()\n    finally:\n        if old is None:\n            os.environ.pop('KITTY_OS', None)\n        else:\n            os.environ['KITTY_OS'] = old\n\n\n\ninclude_keys = 'include', 'globinclude', 'envinclude', 'geninclude'\n\n\nclass RecursiveInclude(Exception):\n    pass\n\n\nclass Memory:\n\n    def __init__(self, accumulate_bad_lines: list[BadLine] | None) -> None:\n        self.s: set[str] = set()\n        if accumulate_bad_lines is None:\n            accumulate_bad_lines = []\n        self.accumulate_bad_lines = accumulate_bad_lines\n\n    def seen(self, path: str) -> bool:\n        key = os.path.normpath(path)\n        if key in self.s:\n            self.accumulate_bad_lines.append(BadLine(currently_parsing.number, currently_parsing.line.rstrip(), RecursiveInclude(\n                f'The file {path} has already been included, ignoring'), currently_parsing.file))\n            return True\n        self.s.add(key)\n        return False\n\n\ndef parse_line(\n    line: str,\n    parse_conf_item: ItemParser,\n    ans: dict[str, Any],\n    base_path_for_includes: str,\n    effective_config_lines: Callable[[str, str], None],\n    memory: Memory,\n    accumulate_bad_lines: list[BadLine] | None = None,\n) -> None:\n    line = line.strip()\n    if not line or line.startswith('#'):\n        return\n    m = key_pat.match(line)\n    if m is None:\n        log_error(f'Ignoring invalid config line: {line!r}')\n        return\n    key, val = m.groups()\n    if key.endswith('include') and key in include_keys:\n        val = expandvars(os.path.expanduser(val.strip()), {'KITTY_OS': os_name()})\n        if key == 'globinclude':\n            from pathlib import Path\n            vals = tuple(map(lambda x: str(os.fspath(x)), sorted(Path(base_path_for_includes).glob(val))))\n        elif key == 'envinclude':\n            from fnmatch import fnmatchcase\n            for x in os.environ:\n                if fnmatchcase(x, val):\n                    with currently_parsing.set_file(f'<env var: {x}>'):\n                        _parse(\n                            NamedLineIterator(os.path.join(base_path_for_includes, ''), iter(os.environ[x].splitlines())),\n                            parse_conf_item, ans, memory, accumulate_bad_lines, effective_config_lines\n                        )\n            return\n        elif key == 'geninclude':\n            if not os.path.isabs(val):\n                val = os.path.join(base_path_for_includes, val)\n            if not memory.seen(val):\n                try:\n                    lines = geninclude(val)\n                except FileNotFoundError as e:\n                    if e.filename == val:\n                        log_error(f'Could not find the geninclude file: {val}, ignoring')\n                    else:\n                        raise\n                else:\n                    with currently_parsing.set_file(f'<get: {val}>'):\n                        _parse(\n                            NamedLineIterator(os.path.join(base_path_for_includes, ''), iter(lines)),\n                            parse_conf_item, ans, memory, accumulate_bad_lines, effective_config_lines\n                        )\n            return\n        else:\n            if not os.path.isabs(val):\n                val = os.path.join(base_path_for_includes, val)\n            vals = (val,)\n        for val in vals:\n            if memory.seen(val):\n                continue\n            try:\n                with open(val, encoding='utf-8', errors='replace') as include:\n                    with currently_parsing.set_file(val):\n                        _parse(include, parse_conf_item, ans, memory, accumulate_bad_lines, effective_config_lines)\n            except FileNotFoundError:\n                log_error(f'Could not find included config file: {val}, ignoring')\n            except OSError:\n                log_error(\n                    'Could not read from included config file: {}, ignoring'.\n                    format(val)\n                )\n        return\n    if parse_conf_item(key, val, ans):\n        effective_config_lines(key, line)\n    else:\n        log_error(f'Ignoring unknown config key: {key}')\n\n\n\ndef _parse(\n    lines: Iterable[str],\n    parse_conf_item: ItemParser,\n    ans: dict[str, Any],\n    memory: Memory,\n    accumulate_bad_lines: list[BadLine] | None = None,\n    effective_config_lines: Callable[[str, str], None] | None = None,\n) -> None:\n    name = getattr(lines, 'name', None)\n    effective_config_lines = effective_config_lines or (lambda a, b: None)\n    if name:\n        base_path_for_includes = os.path.abspath(name) if name.endswith(os.path.sep) else os.path.dirname(os.path.abspath(name))\n    else:\n        from ..constants import config_dir\n        base_path_for_includes = config_dir\n\n    it = iter(lines)\n    line = ''\n    next_line: str = ''\n    next_line_num = 0\n\n    while True:\n        try:\n            if next_line:\n                line = next_line\n            else:\n                line = next(it).lstrip()\n                next_line_num += 1\n            line_num = next_line_num\n\n            try:\n                next_line = next(it).lstrip()\n                next_line_num += 1\n\n                while next_line.startswith('\\\\'):\n                    line = line.rstrip('\\n') + next_line[1:]\n                    try:\n                        next_line = next(it).lstrip()\n                        next_line_num += 1\n                    except StopIteration:\n                        next_line = ''\n                        break\n            except StopIteration:\n                next_line = ''\n            try:\n                with currently_parsing.set_line(line, line_num):\n                    parse_line(line, parse_conf_item, ans, base_path_for_includes, effective_config_lines, memory, accumulate_bad_lines)\n            except Exception as e:\n                if accumulate_bad_lines is None:\n                    raise\n                accumulate_bad_lines.append(BadLine(line_num, line.rstrip(), e, currently_parsing.file))\n        except StopIteration:\n            break\n\n\ndef parse_config_base(\n    lines: Iterable[str],\n    parse_conf_item: ItemParser,\n    ans: dict[str, Any],\n    accumulate_bad_lines: list[BadLine] | None = None,\n    effective_config_lines: Callable[[str, str], None] | None = None,\n) -> None:\n    _parse(lines, parse_conf_item, ans, Memory(accumulate_bad_lines), accumulate_bad_lines, effective_config_lines)\n\n\ndef merge_dicts(defaults: dict[str, Any], newvals: dict[str, Any]) -> dict[str, Any]:\n    ans = defaults.copy()\n    ans.update(newvals)\n    return ans\n\n\ndef resolve_config(SYSTEM_CONF: str, defconf: str, config_files_on_cmd_line: Sequence[str] = ()) -> Generator[str, None, None]:\n    if config_files_on_cmd_line:\n        if 'NONE' not in config_files_on_cmd_line:\n            yield SYSTEM_CONF\n            yield from config_files_on_cmd_line\n    else:\n        yield SYSTEM_CONF\n        yield defconf\n\n\ndef load_config(\n    defaults: OptionsProtocol,\n    parse_config: Callable[[Iterable[str]], dict[str, Any]],\n    merge_configs: Callable[[dict[str, Any], dict[str, Any]], dict[str, Any]],\n    *paths: str,\n    overrides: Iterable[str] | None = None,\n    initialize_defaults: Callable[[dict[str, Any]], dict[str, Any]] = lambda x: x,\n) -> tuple[dict[str, Any], tuple[str, ...]]:\n    ans = initialize_defaults(defaults._asdict())\n    found_paths = []\n    for path in paths:\n        if not path:\n            continue\n        if path == '-':\n            path = '/dev/stdin'\n            with currently_parsing.set_file(path):\n                vals = parse_config(sys.stdin)\n        else:\n            try:\n                with open(path, encoding='utf-8', errors='replace') as f:\n                    with currently_parsing.set_file(path):\n                        vals = parse_config(f)\n            except (FileNotFoundError, PermissionError):\n                continue\n        found_paths.append(path)\n        ans = merge_configs(ans, vals)\n    if overrides is not None:\n        with currently_parsing.set_file('<override>'):\n            vals = parse_config(overrides)\n        ans = merge_configs(ans, vals)\n    return ans, tuple(found_paths)\n\n\nReturnType = TypeVar('ReturnType')\nKeyFunc = Callable[[str, str], ReturnType]\n\n\nclass KeyFuncWrapper(Generic[ReturnType]):\n    def __init__(self) -> None:\n        self.args_funcs: dict[str, KeyFunc[ReturnType]] = {}\n\n    def __call__(self, *names: str) -> Callable[[KeyFunc[ReturnType]], KeyFunc[ReturnType]]:\n\n        def w(f: KeyFunc[ReturnType]) -> KeyFunc[ReturnType]:\n            for name in names:\n                if self.args_funcs.setdefault(name, f) is not f:\n                    raise ValueError(f'the args_func {name} is being redefined')\n            return f\n        return w\n\n    def get(self, name: str) -> KeyFunc[ReturnType] | None:\n        return self.args_funcs.get(name)\n\n\nclass KeyAction(NamedTuple):\n    func: str\n    args: tuple[str | float | bool | int | None, ...] = ()\n\n    def __repr__(self) -> str:\n        if self.args:\n            return f'KeyAction({self.func!r}, {self.args!r})'\n        return f'KeyAction({self.func!r})'\n\n    def pretty(self) -> str:\n        ans = self.func\n        for x in self.args:\n            ans += f' {x}'\n        return ans\n\n\ndef parse_kittens_func_args(action: str, args_funcs: dict[str, KeyFunc[tuple[str, Any]]]) -> KeyAction:\n    parts = action.strip().split(' ', 1)\n    func = parts[0]\n    if len(parts) == 1:\n        return KeyAction(func, ())\n    rest = parts[1]\n\n    try:\n        parser = args_funcs[func]\n    except KeyError as e:\n        raise KeyError(\n            f'Unknown action: {func}. Check if map action: {action} is valid'\n        ) from e\n\n    try:\n        func, args = parser(func, rest)\n    except Exception:\n        raise ValueError(f'Unknown key action: {action}')\n\n    if not isinstance(args, (list, tuple)):\n        args = (args, )\n\n    return KeyAction(func, tuple(args))\n\n\nKittensKeyDefinition = tuple[ParsedShortcut, KeyAction]\nKittensKeyMap = dict[ParsedShortcut, KeyAction]\n\n\ndef parse_kittens_key(\n    val: str, funcs_with_args: dict[str, KeyFunc[tuple[str, Any]]]\n) -> KittensKeyDefinition | None:\n    from ..key_encoding import parse_shortcut\n    sc, action = val.partition(' ')[::2]\n    if not sc or not action:\n        return None\n    ans = parse_kittens_func_args(action, funcs_with_args)\n    return parse_shortcut(sc), ans\n\n\ndef uniq(vals: Iterable[T]) -> list[T]:\n    seen: set[T] = set()\n    seen_add = seen.add\n    return [x for x in vals if x not in seen and not seen_add(x)]\n\n\ndef save_type_stub(text: str, fpath: str) -> None:\n    fpath += 'i'\n    preamble = '# Update this file by running: ./test.py mypy\\n\\n'\n    try:\n        with open(fpath) as fs:\n            existing = fs.read()\n    except FileNotFoundError:\n        existing = ''\n    current = preamble + text\n    if existing != current:\n        with open(fpath, 'w') as f:\n            f.write(current)\n"
  },
  {
    "path": "kitty/config.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport os\nfrom collections.abc import Callable, Generator, Iterable\nfrom contextlib import contextmanager, suppress\nfrom functools import partial\nfrom typing import Any\n\nfrom .conf.utils import BadLine, parse_config_base\nfrom .conf.utils import load_config as _load_config\nfrom .constants import cache_dir, defconf\nfrom .options.types import Options, defaults, option_names\nfrom .options.utils import KeyboardMode, KeyboardModeMap, KeyDefinition, MouseMap, MouseMapping, build_action_aliases\nfrom .typing_compat import TypedDict\nfrom .utils import log_error\n\n\ndef option_names_for_completion() -> tuple[str, ...]:\n    return option_names\n\n\ndef atomic_save(data: bytes, path: str) -> None:\n    import shutil\n    import tempfile\n    path = os.path.realpath(path)\n    fd, p = tempfile.mkstemp(dir=os.path.dirname(path), suffix='.tmp')\n    try:\n        with os.fdopen(fd, 'wb') as f:\n            f.write(data)\n        with suppress(FileNotFoundError):\n            shutil.copystat(path, p)\n        os.utime(p)\n        os.replace(p, path)\n    finally:\n        try:\n            os.remove(p)\n        except FileNotFoundError:\n            pass\n        except Exception as err:\n            log_error(f'Failed to delete temp file {p} for atomic save with error: {err}')\n\n\n@contextmanager\ndef cached_values_for(name: str) -> Generator[dict[str, Any], None, None]:\n    cached_path = os.path.join(cache_dir(), f'{name}.json')\n    cached_values: dict[str, Any] = {}\n    try:\n        with open(cached_path, 'rb') as f:\n            cached_values.update(json.loads(f.read().decode('utf-8')))\n    except FileNotFoundError:\n        pass\n    except Exception as err:\n        log_error(f'Failed to load cached in {name} values with error: {err}')\n\n    yield cached_values\n\n    try:\n        data = json.dumps(cached_values).encode('utf-8')\n        atomic_save(data, cached_path)\n    except Exception as err:\n        log_error(f'Failed to save cached values with error: {err}')\n\n\ndef commented_out_default_config() -> str:\n    from .options.definition import definition\n    return '\\n'.join(definition.as_conf(commented=True))\n\n\ndef prepare_config_file_for_editing() -> str:\n    if not os.path.exists(defconf):\n        d = os.path.dirname(defconf)\n        with suppress(FileExistsError):\n            os.makedirs(d)\n        with open(defconf, 'w', encoding='utf-8') as f:\n            f.write(commented_out_default_config())\n    return defconf\n\n\ndef finalize_keys(opts: Options, accumulate_bad_lines: list[BadLine] | None = None) -> None:\n    defns: list[KeyDefinition] = []\n    for d in opts.map:\n        if d is None:  # clear_all_shortcuts\n            defns = []  # type: ignore\n        else:\n            try:\n                defns.append(d.resolve_and_copy(opts.kitty_mod))\n            except Exception as err:\n                if accumulate_bad_lines is None:\n                    log_error(f'Ignoring map with invalid action: {d.definition}. Error: {err}')\n                else:\n                    accumulate_bad_lines.append(BadLine(d.definition_location.number, d.definition_location.line, err, d.definition_location.file))\n\n    modes: KeyboardModeMap = {'': KeyboardMode()}\n\n    for defn in defns:\n        if defn.options.new_mode:\n            modes[defn.options.new_mode] = nm = KeyboardMode(defn.options.new_mode)\n            nm.on_unknown = defn.options.on_unknown\n            nm.on_action = defn.options.on_action\n            nm.timeout = defn.options.timeout if defn.options.timeout is not None else opts.map_timeout\n            defn.definition = f'push_keyboard_mode {defn.options.new_mode}'\n        try:\n            m = modes[defn.options.mode]\n        except KeyError:\n            kerr = f'The keyboard mode {defn.options.mode} is unknown, ignoring the mapping'\n            if accumulate_bad_lines is None:\n                log_error(kerr)\n            else:\n                dl = defn.definition_location\n                accumulate_bad_lines.append(BadLine(dl.number, dl.line, KeyError(kerr), dl.file))\n            continue\n        items = m.keymap[defn.trigger]\n        if defn.is_sequence:\n            items = m.keymap[defn.trigger] = [kd for kd in items if defn.rest != kd.rest or defn.options.when_focus_on != kd.options.when_focus_on]\n        items.append(defn)\n    opts.keyboard_modes = modes\n\n\ndef finalize_mouse_mappings(opts: Options, accumulate_bad_lines: list[BadLine] | None = None) -> None:\n    defns: list[MouseMapping] = []\n    for d in opts.mouse_map:\n        if d is None:  # clear_all_mouse_actions\n            defns = []  # type: ignore\n        else:\n            try:\n                defns.append(d.resolve_and_copy(opts.kitty_mod))\n            except Exception as err:\n                if accumulate_bad_lines is None:\n                    log_error(f'Ignoring mouse_map with invalid action: {d.definition}. Error: {err}')\n                else:\n                    accumulate_bad_lines.append(BadLine(d.definition_location.number, d.definition_location.line, err, d.definition_location.file))\n    mousemap: MouseMap = {}\n\n    for defn in defns:\n        if defn.definition:\n            mousemap[defn.trigger] = defn.definition\n        else:\n            mousemap.pop(defn.trigger, None)\n    opts.mousemap = mousemap\n\n\ndef parse_config(\n    lines: Iterable[str], accumulate_bad_lines: list[BadLine] | None = None, effective_config_lines: Callable[[str, str], None] | None = None\n) -> dict[str, Any]:\n    from .options.parse import create_result_dict, parse_conf_item\n    ans: dict[str, Any] = create_result_dict()\n    parse_config_base(\n        lines,\n        parse_conf_item,\n        ans,\n        accumulate_bad_lines=accumulate_bad_lines,\n        effective_config_lines=effective_config_lines,\n    )\n    return ans\n\n\neffective_config_lines: list[str] = []\n\n\ndef load_config(*paths: str, overrides: Iterable[str] | None = None, accumulate_bad_lines: list[BadLine] | None = None) -> Options:\n    from .options.parse import merge_result_dicts\n    from .options.types import secret_options\n    del effective_config_lines[:]\n\n    def add_effective_config_line(key: str, line: str) -> None:\n        if key not in secret_options:\n            effective_config_lines.append(line)\n\n    overrides = tuple(overrides) if overrides is not None else ()\n    opts_dict, found_paths = _load_config(\n        defaults, partial(parse_config, accumulate_bad_lines=accumulate_bad_lines, effective_config_lines=add_effective_config_line),\n        merge_result_dicts, *paths, overrides=overrides)\n    opts = Options(opts_dict)\n\n    opts.alias_map = build_action_aliases(opts.kitten_alias, 'kitten')\n    opts.alias_map.update(build_action_aliases(opts.action_alias))\n    finalize_keys(opts, accumulate_bad_lines)\n    finalize_mouse_mappings(opts, accumulate_bad_lines)\n    # delete no longer needed definitions, replacing with empty placeholders\n    opts.kitten_alias = {}\n    opts.action_alias = {}\n    opts.mouse_map = []\n    opts.map = []\n    opts.config_paths = found_paths\n    opts.all_config_paths = paths\n    opts.config_overrides = overrides\n    return opts\n\n\ndef store_effective_config() -> str:\n    import os\n    import stat\n    import tempfile\n    dest = os.path.join(cache_dir(), 'effective-config')\n    os.makedirs(dest, exist_ok=True)\n    raw = '\\n'.join(effective_config_lines)\n    with suppress(FileNotFoundError), tempfile.NamedTemporaryFile('w', dir=dest) as tf:\n        os.chmod(tf.name, stat.S_IRUSR | stat.S_IWUSR)\n        print(raw, file=tf)\n        path = os.path.join(dest, f'{os.getpid()}')\n        os.replace(tf.name, path)\n    return path\n\n\nclass KittyCommonOpts(TypedDict):\n    select_by_word_characters: str\n    open_url_with: list[str]\n    url_prefixes: tuple[str, ...]\n\n\ndef common_opts_as_dict(opts: Options | None = None) -> KittyCommonOpts:\n    if opts is None:\n        opts = defaults\n    return {\n        'select_by_word_characters': opts.select_by_word_characters,\n        'open_url_with': opts.open_url_with,\n        'url_prefixes': opts.url_prefixes,\n    }\n"
  },
  {
    "path": "kitty/constants.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport pwd\nimport sys\nfrom collections.abc import Iterator\nfrom contextlib import suppress\nfrom typing import TYPE_CHECKING, Any, NamedTuple, Optional\n\nfrom .types import run_once\n\nif TYPE_CHECKING:\n    from .options.types import Options\n\n\nclass Version(NamedTuple):\n    major: int\n    minor: int\n    patch: int\n\n\nappname: str = 'kitty'\nkitty_face = '🐱'\nversion: Version = Version(0, 46, 1)\nstr_version: str = '.'.join(map(str, version))\n_plat = sys.platform.lower()\nis_macos: bool = 'darwin' in _plat\nis_freebsd: bool = 'freebsd' in _plat\nis_running_from_develop: bool = False\nRC_ENCRYPTION_PROTOCOL_VERSION = '1'\nwebsite_base_url = 'https://sw.kovidgoyal.net/kitty/'\ndefault_pager_for_help = ('less', '-iRXF')\nkitty_run_data: dict[str, Any] = getattr(sys, 'kitty_run_data', {})\nlaunched_by_launch_services = kitty_run_data.get('launched_by_launch_services', False)\nis_quick_access_terminal_app = kitty_run_data.get('is_quick_access_terminal_app', False)\nunserialize_launch_flag = 'kitty-unserialize-data='\n\nif getattr(sys, 'frozen', False):\n    extensions_dir: str = kitty_run_data['extensions_dir']\n\n    def get_frozen_base() -> str:\n        global is_running_from_develop\n        try:\n            from bypy_importer import running_in_develop_mode  # type: ignore\n        except ImportError:\n            pass\n        else:\n            is_running_from_develop = running_in_develop_mode()\n\n        if is_running_from_develop:\n            q = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n            try:\n                if os.path.isdir(q):\n                    return q\n            except OSError:\n                pass\n        ans = os.path.dirname(extensions_dir)\n        if is_macos:\n            ans = os.path.dirname(os.path.dirname(ans))\n        ans = os.path.join(ans, 'kitty')\n        return ans\n    kitty_base_dir = get_frozen_base()\n    del get_frozen_base\nelse:\n    kitty_base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n    extensions_dir = os.path.join(kitty_base_dir, 'kitty')\n\n\n@run_once\ndef kitty_exe() -> str:\n    rpath = kitty_run_data.get('bundle_exe_dir')\n    if not rpath:\n        items = os.environ.get('PATH', '').split(os.pathsep) + [os.path.join(kitty_base_dir, 'kitty', 'launcher')]\n        seen: set[str] = set()\n        for candidate in filter(None, items):\n            if candidate not in seen:\n                seen.add(candidate)\n                if os.access(os.path.join(candidate, 'kitty'), os.X_OK):\n                    rpath = candidate\n                    break\n        else:\n            raise RuntimeError('kitty binary not found')\n    return os.path.join(rpath, 'kitty')\n\n\n@run_once\ndef kitten_exe() -> str:\n    return os.path.join(os.path.dirname(kitty_exe()), 'kitten')\n\n\ndef _get_config_dir() -> str:\n    cdir = kitty_run_data.get('config_dir', '')\n    if cdir:\n        return str(cdir)\n    import atexit\n    import tempfile\n    ans = tempfile.mkdtemp(prefix='kitty-conf-')\n    def cleanup() -> None:\n        import shutil\n        with suppress(Exception):\n            shutil.rmtree(ans)\n    atexit.register(cleanup)\n    return ans\n\n\nconfig_dir = _get_config_dir()\ndel _get_config_dir\ndefconf = os.path.join(config_dir, 'kitty.conf')\n\n\n@run_once\ndef cache_dir() -> str:\n    if 'KITTY_CACHE_DIRECTORY' in os.environ:\n        candidate = os.path.abspath(os.environ['KITTY_CACHE_DIRECTORY'])\n    elif is_macos:\n        candidate = os.path.join(os.path.expanduser('~/Library/Caches'), appname)\n    else:\n        candidate = os.environ.get('XDG_CACHE_HOME', '~/.cache')\n        candidate = os.path.join(os.path.expanduser(candidate), appname)\n    os.makedirs(candidate, exist_ok=True)\n    return candidate\n\n\n@run_once\ndef runtime_dir() -> str:\n    if 'KITTY_RUNTIME_DIRECTORY' in os.environ:\n        candidate = os.path.abspath(os.environ['KITTY_RUNTIME_DIRECTORY'])\n    elif is_macos:\n        from .fast_data_types import user_cache_dir\n        candidate = user_cache_dir()\n    elif 'XDG_RUNTIME_DIR' in os.environ:\n        candidate = os.path.abspath(os.environ['XDG_RUNTIME_DIR'])\n    else:\n        candidate = f'/run/user/{os.geteuid()}'\n        if not os.path.isdir(candidate) or not os.access(candidate, os.X_OK | os.W_OK | os.R_OK):\n            candidate = os.path.join(cache_dir(), 'run')\n    os.makedirs(candidate, exist_ok=True)\n    import stat\n    if stat.S_IMODE(os.stat(candidate).st_mode) != 0o700:\n        os.chmod(candidate, 0o700)\n    return candidate\n\n\ndef wakeup_io_loop() -> None:\n    from .fast_data_types import get_boss\n    b = get_boss()\n    if b is not None:\n        b.child_monitor.wakeup()\n\n\nterminfo_dir = os.path.join(kitty_base_dir, 'terminfo')\nlogo_png_file = os.path.join(kitty_base_dir, 'logo', 'kitty.png')\nbeam_cursor_data_file = os.path.join(kitty_base_dir, 'logo', 'beam-cursor.png')\nshell_integration_dir = os.path.join(kitty_base_dir, 'shell-integration')\nfonts_dir = os.path.join(kitty_base_dir, 'fonts')\ntry:\n    shell_path = os.environ.get('SHELL') or pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'\nexcept KeyError:\n    with suppress(Exception):\n        print('Failed to read login shell via getpwuid() for current user, falling back to /bin/sh', file=sys.stderr)\n    shell_path = '/bin/sh'\n# Keep this short as it is limited to 103 bytes on macOS\n# https://github.com/ansible/ansible/issues/11536#issuecomment-153030743\nssh_control_master_template = 'kssh-{kitty_pid}-{ssh_placeholder}'\n\n# See https://specifications.freedesktop.org/icon-naming-spec/latest/ar01s04.html\n# Update the spec in docs/desktop-notifications.rst if you change this.\nstandard_icon_names = {\n    'error': ('dialog-error', '☠'),\n    'warning': ('dialog-warning','⚠'),\n    'warn': ('dialog-warning', '⚠'),\n    'info': ('dialog-information', 'ℹ'),\n    'question': ('dialog-question', '❔'),\n\n    'help': ('system-help', '📖'),\n    'file-manager': ('system-file-manager', '🗄'),\n    'system-monitor': ('utilities-system-monitor', '🎛'),\n    'text-editor': ('utilities-text-editor', '📄'),\n}\n\n# See https://github.com/TUNER88/iOSSystemSoundsLibrary for Apple's system\n# sound ids not all of which are available on macOS.\nstandard_sound_names = {\n    'error': ('dialog-error', 1),\n    'info': ('dialog-information', 2),\n    'warning': ('dialog-warning', 3),\n    'warn': ('dialog-warning', 3),\n    'question': ('dialog-question', 4),\n}\n\n\ndef glfw_path(module: str) -> str:\n    prefix = 'kitty.' if getattr(sys, 'frozen', False) else ''\n    return os.path.join(extensions_dir, f'{prefix}glfw-{module}.so')\n\n\ndef detect_if_wayland_ok() -> bool:\n    if 'WAYLAND_DISPLAY' not in os.environ and 'WAYLAND_SOCKET' not in os.environ:\n        return False\n    if 'KITTY_DISABLE_WAYLAND' in os.environ:\n        return False\n    wayland = glfw_path('wayland')\n    if not os.path.exists(wayland):\n        return False\n    import ctypes\n    with suppress(Exception):\n        setattr(detect_if_wayland_ok, 'keep_module_loaded', ctypes.CDLL(wayland))\n        return True\n    return False\n\n\ndef is_wayland(opts: Optional['Options'] = None) -> bool:\n    if is_macos:\n        return False\n    if opts is None:\n        return bool(getattr(is_wayland, 'ans'))\n    if opts.linux_display_server == 'auto':\n        ans = detect_if_wayland_ok()\n    else:\n        ans = opts.linux_display_server == 'wayland'\n    setattr(is_wayland, 'ans', ans)\n    return ans\n\n\nsupports_primary_selection = not is_macos\n\n\ndef running_in_kitty(set_val: bool | None = None) -> bool:\n    if set_val is not None:\n        setattr(running_in_kitty, 'ans', set_val)\n    return bool(getattr(running_in_kitty, 'ans', False))\n\n\ndef list_kitty_resources(package: str = 'kitty') -> Iterator[str]:\n    try:\n        if sys.version_info[:2] < (3, 10):\n            raise ImportError(\"importlib.resources.files() doesn't work with frozen builds on python 3.9\")\n        from importlib.resources import files\n    except ImportError:\n        from importlib.resources import contents\n        return iter(contents(package))\n    else:\n        return (path.name for path in files(package).iterdir())\n\n\ndef read_kitty_resource(name: str, package_name: str = 'kitty') -> bytes:\n    try:\n        if sys.version_info[:2] < (3, 10):\n            raise ImportError(\"importlib.resources.files() doesn't work with frozen builds on python 3.9\")\n        from importlib.resources import files\n    except ImportError:\n        from importlib.resources import read_binary\n        return read_binary(package_name, name)\n    else:\n        return (files(package_name) / name).read_bytes()\n\n\ndef website_url(doc_name: str = '', website: str = website_base_url) -> str:\n    if doc_name:\n        base, _, frag = doc_name.partition('#')\n        base = base.rstrip('/')\n        if base:\n            base += '/'\n        doc_name = base + (f'#{frag}' if frag else '')\n    return website + doc_name.lstrip('/')\n\n\nhandled_signals: set[int] = set()\n\n\ndef clear_handled_signals(*a: Any) -> None:\n    if not handled_signals:\n        return\n    import signal\n    if hasattr(signal, 'pthread_sigmask'):\n        signal.pthread_sigmask(signal.SIG_UNBLOCK, handled_signals)\n    for s in handled_signals:\n        signal.signal(s, signal.SIG_DFL)\n\n\n@run_once\ndef local_docs() -> str:\n    d = os.path.dirname\n    base = d(d(kitty_exe()))\n    from_source = kitty_run_data.get('from_source')\n    if is_macos and from_source and '/kitty.app/Contents/' in kitty_exe():\n        base = d(d(d(base)))\n    subdir = os.path.join('doc', 'kitty', 'html')\n    linux_ans = os.path.join(base, 'share', subdir)\n    if getattr(sys, 'frozen', False):\n        if is_macos:\n            return os.path.join(d(d(d(extensions_dir))), subdir)\n        return linux_ans\n    if os.path.isdir(linux_ans):\n        return linux_ans\n    if from_source:\n        sq = os.path.join(d(base), 'docs', '_build', 'html')\n        if os.path.isdir(sq):\n            return sq\n    for candidate in ('/usr', '/usr/local', '/opt/homebrew'):\n        q = os.path.join(candidate, 'share', subdir)\n        if os.path.isdir(q):\n            return q\n    return ''\n\n\n@run_once\ndef wrapped_kitten_names() -> frozenset[str]:\n    import kitty.fast_data_types as f\n    return frozenset(f.wrapped_kitten_names())\n\n\n_supports_window_occlusion = False\n\n\ndef supports_window_occlusion(set: bool | None = None) -> bool:\n    global _supports_window_occlusion\n    if set is not None:\n        _supports_window_occlusion = set\n    return _supports_window_occlusion\n"
  },
  {
    "path": "kitty/control-codes.h",
    "content": "/*\n * control_codes.h\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n// Space\n#define SP  ' '\n\n// *Null*: Does nothing.\n#define NUL 0\n\n// *Bell*: Beeps.\n#define BEL 0x07\n\n// *Backspace*: Backspace one column, but not past the beginning of the\n// line.\n#define BS 0x08\n\n// *Horizontal tab*: Move cursor to the next tab stop, or to the end\n// of the line if there is no earlier tab stop.\n#define HT 0x09\n\n// *Linefeed*: Give a line feed, and, if LNM (new\n// line mode) is set also a carriage return.\n#define LF 10\n\n// *Vertical tab*: Same as :data:`LF`.\n#define VT 0x0b\n// *Form feed*: Same as :data:`LF`.\n#define FF 0x0c\n\n// *Carriage return*: Move cursor to left margin on current line.\n#define CR 13\n\n// *Shift out*: Activate G1 character set.\n#define SO 0x0e\n\n// *Shift in*: Activate G0 character set.\n#define SI 0x0f\n\n// *Cancel*: Interrupt escape sequence. If received during an escape or\n// control sequence, cancels the sequence and displays substitution\n// character.\n#define CAN 0x18\n// *Substitute*: Same as :data:`CAN`.\n#define SUB 0x1a\n\n// *Escape*: Starts an escape sequence.\n#define ESC 0x1b\n\n// *Delete*: Is ignored.\n#define DEL 0x7f\n\n// Sharp control codes\n// -------------------\n\n// Align display\n#define DECALN '8'\n\n// Esc control codes\n// ------------------\n\n#define ESC_DCS 'P'\n#define ESC_OSC ']'\n#define ESC_CSI '['\n#define ESC_ST '\\\\'\n#define ESC_PM '^'\n#define ESC_APC '_'\n#define ESC_SOS 'X'\n\n// *Reset*.\n#define ESC_RIS 'c'\n\n// *Index*: Move cursor down one line in same column. If the cursor is\n// at the bottom margin, the screen performs a scroll-up.\n#define ESC_IND 'D'\n\n// *Next line*: Same as LF.\n#define ESC_NEL 'E'\n\n// Tabulation set: Set a horizontal tab stop at cursor position.\n#define ESC_HTS 'H'\n\n// *Reverse index*: Move cursor up one line in same column. If the\n// cursor is at the top margin, the screen performs a scroll-down.\n#define ESC_RI 'M'\n\n// Save cursor: Save cursor position, character attribute (graphic\n// rendition), character set, and origin mode selection (see\n// :data:`DECRC`).\n#define ESC_DECSC '7'\n\n// *Restore cursor*: Restore previously saved cursor position, character\n// attribute (graphic rendition), character set, and origin mode\n// selection. If none were saved, move cursor to home position.\n#define ESC_DECRC '8'\n\n// Set normal keypad mode\n#define ESC_DECKPNM '>'\n\n// Set alternate keypad mode\n#define ESC_DECKPAM  '='\n\n// ECMA-48 CSI sequences.\n// ---------------------\n\n// *Insert character*: Insert the indicated # of blank characters.\n#define ICH '@'\n\n// *Cursor up*: Move cursor up the indicated # of lines in same column.\n// Cursor stops at top margin.\n#define CUU 'A'\n\n// *Cursor down*: Move cursor down the indicated # of lines in same\n// column. Cursor stops at bottom margin.\n#define CUD 'B'\n\n// *Cursor forward*: Move cursor right the indicated # of columns.\n// Cursor stops at right margin.\n#define CUF 'C'\n\n// *Cursor back*: Move cursor left the indicated # of columns. Cursor\n// stops at left margin.\n#define CUB 'D'\n\n// *Cursor next line*: Move cursor down the indicated # of lines to\n// column 1.\n#define CNL 'E'\n\n// *Cursor previous line*: Move cursor up the indicated # of lines to\n// column 1.\n#define CPL 'F'\n\n// *Cursor horizontal align*: Move cursor to the indicated column in\n// current line.\n#define CHA 'G'\n\n// *Cursor position*: Move cursor to the indicated line, column (origin\n// at ``1, 1``).\n#define CUP 'H'\n\n// *Erase data* (default: from cursor to end of line).\n#define ED 'J'\n\n// *Erase in line* (default: from cursor to end of line).\n#define EL 'K'\n\n// *Insert line*: Insert the indicated # of blank lines, starting from\n// the current line. Lines displayed below cursor move down. Lines moved\n// past the bottom margin are lost.\n#define IL 'L'\n\n// *Delete line*: Delete the indicated # of lines, starting from the\n// current line. As lines are deleted, lines displayed below cursor\n// move up. Lines added to bottom of screen have spaces with same\n// character attributes as last line move up.\n#define DL 'M'\n\n// *Delete character*: Delete the indicated # of characters on the\n// current line. When character is deleted, all characters to the right\n// of cursor move left.\n#define DCH 'P'\n\n// Scroll up by the specified number of lines\n#define SU 'S'\n\n// Scroll down by the specified number of lines\n#define SD 'T'\n\n// *Erase character*: Erase the indicated # of characters on the\n// current line.\n#define ECH 'X'\n\n// *Horizontal position relative*: Same as :data:`CUF`.\n#define HPR 'a'\n\n// Repeat the preceding graphic character Ps times.\n#define REP 'b'\n\n// *Device Attributes*.\n#define DA 'c'\n\n// *Vertical position adjust*: Move cursor to the indicated line,\n// current column.\n#define VPA 'd'\n\n// *Vertical position relative*: Same as :data:`CUD`.\n#define VPR 'e'\n\n// *Horizontal / Vertical position*: Same as :data:`CUP`.\n#define HVP 'f'\n\n// *Tabulation clear*: Clears a horizontal tab stop at cursor position.\n#define TBC 'g'\n\n// *Set mode*.\n#define SM 'h'\n\n// *Reset mode*.\n#define RM 'l'\n\n// *Select graphics rendition*: The terminal can display the following\n// character attributes that change the character display without\n// changing the character\n#define SGR 'm'\n\n// *Device status report*.\n#define DSR 'n'\n\n// Soft reset\n#define DECSTR 'p'\n\n// *Horizontal position adjust*: Same as :data:`CHA`.\n#define HPA '`'\n\n// Back tab\n#define CBT 'Z'\n\n// Forward tab\n#define CHT 'I'\n\n// Misc sequences\n// ----------------\n\n// Change cursor shape/blink\n#define DECSCUSR 'q'\n\n// File transfer OSC number\n#define FILE_TRANSFER_CODE 5113\n// Pending mode CSI code\n#define PENDING_MODE 2026\n// Text size OSC number\n#define TEXT_SIZE_CODE 66\n"
  },
  {
    "path": "kitty/core_text.m",
    "content": "/*\n * core_text.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"state.h\"\n#include \"cleanup.h\"\n#include \"fonts.h\"\n#include \"unicode-data.h\"\n#include <structmember.h>\n#include <stdint.h>\n#include <math.h>\n#include <hb-coretext.h>\n#include <hb-ot.h>\n#import <CoreGraphics/CGBitmapContext.h>\n#import <CoreText/CTFont.h>\n#include <Foundation/Foundation.h>\n#include <CoreText/CoreText.h>\n#import <Foundation/NSString.h>\n#import <Foundation/NSDictionary.h>\n#import <AppKit/AppKit.h>\n\n#define debug debug_fonts\nstatic inline void cleanup_cfrelease(void *__p) { CFTypeRef *tp = (CFTypeRef *)__p; CFTypeRef cf = *tp; if (cf) { CFRelease(cf); } }\n#define RAII_CoreFoundation(type, name, initializer) __attribute__((cleanup(cleanup_cfrelease))) type name = initializer\n\ntypedef struct {\n    PyObject_HEAD\n\n    unsigned int units_per_em;\n    float ascent, descent, leading, underline_position, underline_thickness, point_sz, scaled_point_sz;\n    CTFontRef ct_font;\n    hb_font_t *hb_font;\n    PyObject *family_name, *full_name, *postscript_name, *path, *name_lookup_table;\n    FontFeatures font_features;\n} CTFace;\nPyTypeObject CTFace_Type;\nstatic CTFontRef system_ui_font = nil;\n\nstatic BOOL\nCTFontSupportsColorGlyphs(CTFontRef font) {\n    CFTypeRef symbolicTraits = CTFontCopyAttribute(font, kCTFontSymbolicTrait);\n    if (symbolicTraits) {\n        if ([(NSNumber *)symbolicTraits unsignedLongValue] & kCTFontColorGlyphsTrait) {\n            CFRelease(symbolicTraits);\n            return YES;\n        }\n        CFRelease(symbolicTraits);\n    }\n    return NO;\n}\n\nstatic PyObject*\nconvert_cfstring(CFStringRef src, int free_src) {\n    RAII_CoreFoundation(CFStringRef, releaseme, free_src ? src : nil);\n    (void)releaseme;\n    if (!src) return PyUnicode_FromString(\"\");\n    const char *fast = CFStringGetCStringPtr(src, kCFStringEncodingUTF8);\n    if (fast) return PyUnicode_FromString(fast);\n#define SZ 4096\n    char buf[SZ];\n    if(!CFStringGetCString(src, buf, SZ, kCFStringEncodingUTF8)) { PyErr_SetString(PyExc_ValueError, \"Failed to convert CFString\"); return NULL; }\n    return PyUnicode_FromString(buf);\n#undef SZ\n}\n\nstatic void\ninit_face(CTFace *self, CTFontRef font) {\n    if (self->hb_font) hb_font_destroy(self->hb_font);\n    self->hb_font = NULL;\n    if (self->ct_font) CFRelease(self->ct_font);\n    self->ct_font = font; CFRetain(font);\n    self->units_per_em = CTFontGetUnitsPerEm(self->ct_font);\n    self->ascent = CTFontGetAscent(self->ct_font);\n    self->descent = CTFontGetDescent(self->ct_font);\n    self->leading = CTFontGetLeading(self->ct_font);\n    self->underline_position = CTFontGetUnderlinePosition(self->ct_font);\n    self->underline_thickness = CTFontGetUnderlineThickness(self->ct_font);\n    self->scaled_point_sz = CTFontGetSize(self->ct_font);\n}\n\nstatic PyObject*\nconvert_url_to_filesystem_path(CFURLRef url) {\n    uint8_t buf[4096];\n    if (url && CFURLGetFileSystemRepresentation(url, true, buf, sizeof(buf))) return PyUnicode_FromString((const char*)buf);\n    return PyUnicode_FromString(\"\");\n}\n\nstatic PyObject*\nget_path_for_font(CTFontRef font) {\n    RAII_CoreFoundation(CFURLRef, url, CTFontCopyAttribute(font, kCTFontURLAttribute));\n    return convert_url_to_filesystem_path(url);\n}\n\nstatic PyObject*\nget_path_for_font_descriptor(CTFontDescriptorRef font) {\n    RAII_CoreFoundation(CFURLRef, url, CTFontDescriptorCopyAttribute(font, kCTFontURLAttribute));\n    return convert_url_to_filesystem_path(url);\n}\n\n\nstatic CTFace*\nct_face(CTFontRef font, PyObject *features) {\n    CTFace *self = (CTFace *)CTFace_Type.tp_alloc(&CTFace_Type, 0);\n    if (self) {\n        init_face(self, font);\n        self->family_name = convert_cfstring(CTFontCopyFamilyName(self->ct_font), true);\n        self->full_name = convert_cfstring(CTFontCopyFullName(self->ct_font), true);\n        self->postscript_name = convert_cfstring(CTFontCopyPostScriptName(self->ct_font), true);\n        self->path = get_path_for_font(self->ct_font);\n        if (self->family_name == NULL || self->full_name == NULL || self->postscript_name == NULL || self->path == NULL) { Py_CLEAR(self); }\n        else {\n            if (!create_features_for_face(postscript_name_for_face((PyObject*)self), features, &self->font_features)) { Py_CLEAR(self); }\n        }\n    }\n    return self;\n}\n\nstatic void\ndealloc(CTFace* self) {\n    if (self->hb_font) hb_font_destroy(self->hb_font);\n    if (self->ct_font) CFRelease(self->ct_font);\n    self->hb_font = NULL;\n    self->ct_font = NULL;\n    free(self->font_features.features);\n    Py_CLEAR(self->family_name); Py_CLEAR(self->full_name); Py_CLEAR(self->postscript_name); Py_CLEAR(self->path);\n    Py_CLEAR(self->name_lookup_table);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic const char*\ntag_to_string(uint32_t tag, uint8_t bytes[5]) {\n    bytes[0] = (tag >> 24) & 0xff;\n    bytes[1] = (tag >> 16) & 0xff;\n    bytes[2] = (tag >> 8) & 0xff;\n    bytes[3] = (tag) & 0xff;\n    bytes[4] = 0;\n    return (const char*)bytes;\n}\n\nstatic uint32_t\nstring_to_tag(const uint8_t *bytes) {\n    return (((uint32_t)bytes[0]) << 24) | (((uint32_t)bytes[1]) << 16) | (((uint32_t)bytes[2]) << 8) | bytes[3];\n}\n\nFontFeatures*\nfeatures_for_face(PyObject *s) { return &((CTFace*)s)->font_features; }\n\nstatic void\nadd_variation_pair(const void *key_, const void *value_, void *ctx) {\n    PyObject *ans = ctx;\n    CFNumberRef key = key_, value = value_;\n    uint32_t tag; double val;\n    if (!CFNumberGetValue(key, kCFNumberSInt32Type, &tag)) return;\n    if (!CFNumberGetValue(value, kCFNumberDoubleType, &val)) return;\n    uint8_t tag_string[5];\n    tag_to_string(tag, tag_string);\n    RAII_PyObject(pyval, PyFloat_FromDouble(val));\n    if (pyval) PyDict_SetItemString(ans, (const char*)tag_string, pyval);\n}\n\nstatic PyObject*\nvariation_to_python(CFDictionaryRef v) {\n    if (!v) { Py_RETURN_NONE; }\n    RAII_PyObject(ans, PyDict_New());\n    if (!ans) return NULL;\n    CFDictionaryApplyFunction(v, add_variation_pair, ans);\n    if (PyErr_Occurred()) return NULL;\n    Py_INCREF(ans); return ans;\n}\n\nstatic PyObject*\nfont_descriptor_to_python(CTFontDescriptorRef descriptor) {\n    RAII_PyObject(path, get_path_for_font_descriptor(descriptor));\n    RAII_PyObject(ps_name, convert_cfstring(CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute), true));\n    RAII_PyObject(family, convert_cfstring(CTFontDescriptorCopyAttribute(descriptor, kCTFontFamilyNameAttribute), true));\n    RAII_PyObject(style, convert_cfstring(CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute), true));\n    RAII_PyObject(display_name, convert_cfstring(CTFontDescriptorCopyAttribute(descriptor, kCTFontDisplayNameAttribute), true));\n    RAII_CoreFoundation(CFDictionaryRef, traits, CTFontDescriptorCopyAttribute(descriptor, kCTFontTraitsAttribute));\n    unsigned long symbolic_traits = 0; float weight = 0, width = 0, slant = 0;\n#define get_number(d, key, output, type_) { \\\n            CFNumberRef value = (CFNumberRef)CFDictionaryGetValue(d, key); \\\n            if (value) CFNumberGetValue(value, type_, &output); }\n    get_number(traits, kCTFontSymbolicTrait, symbolic_traits, kCFNumberLongType);\n    get_number(traits, kCTFontWeightTrait, weight, kCFNumberFloatType);\n    get_number(traits, kCTFontWidthTrait, width, kCFNumberFloatType);\n    get_number(traits, kCTFontSlantTrait, slant, kCFNumberFloatType);\n    RAII_CoreFoundation(CFDictionaryRef, cf_variation, CTFontDescriptorCopyAttribute(descriptor, kCTFontVariationAttribute));\n    RAII_PyObject(variation, variation_to_python(cf_variation));\n    if (!variation) return NULL;\n#undef get_number\n\n\n    PyObject *ans = Py_BuildValue(\"{ss sOsOsOsOsO sOsOsOsOsOsOsO sfsfsfsk}\",\n            \"descriptor_type\", \"core_text\",\n\n            \"path\", path, \"postscript_name\", ps_name, \"family\", family, \"style\", style, \"display_name\", display_name,\n\n            \"bold\", (symbolic_traits & kCTFontBoldTrait) != 0 ? Py_True : Py_False,\n            \"italic\", (symbolic_traits & kCTFontItalicTrait) != 0 ? Py_True : Py_False,\n            \"monospace\", (symbolic_traits & kCTFontTraitMonoSpace) != 0 ? Py_True : Py_False,\n            \"expanded\", (symbolic_traits & kCTFontExpandedTrait) != 0 ? Py_True : Py_False,\n            \"condensed\", (symbolic_traits & kCTFontCondensedTrait) != 0 ? Py_True : Py_False,\n            \"color_glyphs\", (symbolic_traits & kCTFontColorGlyphsTrait) != 0 ? Py_True : Py_False,\n            \"variation\", variation,\n\n            \"weight\", weight, \"width\", width, \"slant\", slant, \"traits\", symbolic_traits\n    );\n    return ans;\n}\n\nstatic CTFontDescriptorRef\nfont_descriptor_from_python(PyObject *src) {\n    CTFontSymbolicTraits symbolic_traits = 0;\n    RAII_CoreFoundation(CFMutableDictionaryRef, ans, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));\n    PyObject *t = PyDict_GetItemString(src, \"traits\");\n    if (t == NULL) {\n        symbolic_traits = (\n            (PyDict_GetItemString(src, \"bold\") == Py_True ? kCTFontBoldTrait : 0) |\n            (PyDict_GetItemString(src, \"italic\") == Py_True ? kCTFontItalicTrait : 0) |\n            (PyDict_GetItemString(src, \"monospace\") == Py_True ? kCTFontMonoSpaceTrait : 0));\n    } else {\n        symbolic_traits = PyLong_AsUnsignedLong(t);\n    }\n    RAII_CoreFoundation(CFNumberRef, cf_symbolic_traits, CFNumberCreate(NULL, kCFNumberSInt32Type, &symbolic_traits));\n    CFTypeRef keys[] = { kCTFontSymbolicTrait };\n    CFTypeRef values[] = { cf_symbolic_traits };\n    RAII_CoreFoundation(CFDictionaryRef, traits, CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));\n    CFDictionaryAddValue(ans, kCTFontTraitsAttribute, traits);\n\n#define SET(x, attr) if ((t = PyDict_GetItemString(src, #x))) { \\\n    RAII_CoreFoundation(CFStringRef, cs, CFStringCreateWithCString(NULL, PyUnicode_AsUTF8(t), kCFStringEncodingUTF8)); \\\n    CFDictionaryAddValue(ans, attr, cs); }\n\n    SET(family, kCTFontFamilyNameAttribute);\n    SET(style, kCTFontStyleNameAttribute);\n    SET(postscript_name, kCTFontNameAttribute);\n#undef SET\n    if ((t = PyDict_GetItemString(src, \"axis_map\"))) {\n        RAII_CoreFoundation(CFMutableDictionaryRef, axis_map, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));\n        PyObject *key, *value; Py_ssize_t pos = 0;\n        while (PyDict_Next(t, &pos, &key, &value)) {\n            double val = PyFloat_AS_DOUBLE(value);\n            uint32_t tag = string_to_tag((const uint8_t*)PyUnicode_AsUTF8(key));\n            RAII_CoreFoundation(CFNumberRef, cf_tag, CFNumberCreate(NULL, kCFNumberSInt32Type, &tag));\n            RAII_CoreFoundation(CFNumberRef, cf_val, CFNumberCreate(NULL, kCFNumberDoubleType, &val));\n            CFDictionaryAddValue(axis_map, cf_tag, cf_val);\n        }\n        CFDictionaryAddValue(ans, kCTFontVariationAttribute, axis_map);\n    }\n    return CTFontDescriptorCreateWithAttributes(ans);\n}\n\nstatic CTFontCollectionRef all_fonts_collection_data = NULL;\n\nstatic CTFontCollectionRef\nall_fonts_collection(void) {\n    if (all_fonts_collection_data == NULL) all_fonts_collection_data = CTFontCollectionCreateFromAvailableFonts(NULL);\n    return all_fonts_collection_data;\n}\n\nstatic PyObject*\ncoretext_all_fonts(PyObject UNUSED *_self, PyObject *monospaced_only_) {\n    int monospaced_only = PyObject_IsTrue(monospaced_only_);\n    RAII_CoreFoundation(CFArrayRef, matches, CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection()));\n    const CFIndex count = CFArrayGetCount(matches);\n    RAII_PyObject(ans, PyTuple_New(count));\n    if (ans == NULL) return NULL;\n    PyObject *temp;\n    Py_ssize_t num = 0;\n    for (CFIndex i = 0; i < count; i++) {\n        CTFontDescriptorRef desc = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matches, i);\n        if (monospaced_only) {\n            RAII_CoreFoundation(CFDictionaryRef, traits, CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));\n            if (traits) {\n                unsigned long symbolic_traits;\n                CFNumberRef value = (CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait);\n                if (value) {\n                    CFNumberGetValue(value, kCFNumberLongType, &symbolic_traits);\n                    if (!(symbolic_traits & kCTFontTraitMonoSpace)) continue;\n                }\n            }\n        }\n        temp = font_descriptor_to_python(desc);\n        if (temp == NULL) return NULL;\n        PyTuple_SET_ITEM(ans, num++, temp); temp = NULL;\n    }\n    if (_PyTuple_Resize(&ans, num) == -1) return NULL;\n    Py_INCREF(ans);\n    return ans;\n}\n\nstatic unsigned int\nglyph_id_for_codepoint_ctfont(CTFontRef ct_font, char_type ch) {\n    unichar chars[2] = {0};\n    CGGlyph glyphs[2] = {0};\n    int count = CFStringGetSurrogatePairForLongCharacter(ch, chars) ? 2 : 1;\n    CTFontGetGlyphsForCharacters(ct_font, chars, glyphs, count);\n    return glyphs[0];\n}\n\nstatic bool\ncf_string_equals(CFStringRef a, CFStringRef b) { return CFStringCompare(a, b, 0) == kCFCompareEqualTo; }\n\n#define LAST_RESORT_FONT_NAME \"LastResort\"\n\nstatic bool\nis_last_resort_font(CTFontRef new_font) {\n    CFStringRef name = CTFontCopyPostScriptName(new_font);\n    bool ans = cf_string_equals(name, CFSTR(LAST_RESORT_FONT_NAME));\n    CFRelease(name);\n    return ans;\n}\n\nstatic CTFontDescriptorRef _nerd_font_descriptor = NULL, builtin_nerd_font_descriptor = NULL;\n\nstatic CTFontRef nerd_font(CGFloat sz) {\n    static bool searched = false;\n    if (!searched) {\n        searched = true;\n        CFArrayRef fonts = CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection());\n        const CFIndex count = CFArrayGetCount(fonts);\n        for (CFIndex i = 0; i < count; i++) {\n            CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);\n            CFStringRef name = CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute);\n            bool is_nerd_font = cf_string_equals(name, CFSTR(\"SymbolsNFM\"));\n            CFRelease(name);\n            if (is_nerd_font) {\n                _nerd_font_descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, CTFontDescriptorCopyAttributes(descriptor));\n                break;\n            }\n        }\n        CFRelease(fonts);\n    }\n    if (_nerd_font_descriptor) return CTFontCreateWithFontDescriptor(_nerd_font_descriptor, sz, NULL);\n    if (builtin_nerd_font_descriptor) return CTFontCreateWithFontDescriptor(builtin_nerd_font_descriptor, sz, NULL);\n    return NULL;\n}\n\nstatic bool ctfont_has_codepoint(const void *ctfont, char_type cp) { return glyph_id_for_codepoint_ctfont(ctfont, cp) > 0; }\nstatic bool font_can_render_cell(CTFontRef font, const ListOfChars *lc) { return has_cell_text(ctfont_has_codepoint, font, false, lc); }\n\nstatic CTFontRef\nmanually_search_fallback_fonts(CTFontRef current_font, const ListOfChars *lc) {\n    char_type ch = lc->chars[0] ? lc->chars[0] : ' ';\n    const bool in_first_pua = 0xe000 <= ch && ch <= 0xf8ff;\n    // preferentially load from NERD fonts\n    if (in_first_pua) {\n        CTFontRef nf = nerd_font(CTFontGetSize(current_font));\n        if (nf) {\n            if (font_can_render_cell(nf, lc)) return nf;\n            CFRelease(nf);\n        }\n    }\n    CFArrayRef fonts = CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection());\n    CTFontRef ans = NULL;\n    const CFIndex count = CFArrayGetCount(fonts);\n    for (CFIndex i = 0; i < count; i++) {\n        CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);\n        CTFontRef new_font = CTFontCreateWithFontDescriptor(descriptor, CTFontGetSize(current_font), NULL);\n        if (!is_last_resort_font(new_font) && font_can_render_cell(new_font, lc)) {\n            ans = new_font;\n            break;\n        }\n        CFRelease(new_font);\n    }\n    CFRelease(fonts);\n    if (!ans) {\n        CTFontRef nf = nerd_font(CTFontGetSize(current_font));\n        if (nf) {\n            if (font_can_render_cell(nf, lc)) ans = nf;\n            else CFRelease(nf);\n        }\n    }\n    return ans;\n}\n\nstatic CTFontRef\nfind_substitute_face(CFStringRef str, CTFontRef old_font, const ListOfChars *lc) {\n    // CoreText's fallback system has various problems:\n    // 1) CTFontCreateForString returns the original font when there are combining\n    // diacritics in the text and the base character is in the original font.\n    // 2) Fallback does not work for PUA characters\n    CTFontRef new_font = CTFontCreateForString(old_font, str, CFRangeMake(0, CFStringGetLength(str)));\n    if (!new_font || is_last_resort_font(new_font) || !font_can_render_cell(new_font, lc)) {\n        if (new_font) CFRelease(new_font);\n        // CoreText's fallback font mechanism does not work for private use characters, it also fails\n        // in specific circumstances, such as:\n        return manually_search_fallback_fonts(old_font, lc);\n    }\n    return new_font;\n}\n\nstatic CTFontRef\napply_styles_to_fallback_font(CTFontRef original_fallback_font, bool bold, bool italic, const ListOfChars *lc) {\n    if (!original_fallback_font || (!bold && !italic) || is_last_resort_font(original_fallback_font)) return original_fallback_font;\n    RAII_CoreFoundation(CTFontDescriptorRef, original_descriptor, CTFontCopyFontDescriptor(original_fallback_font));\n    if (!original_descriptor) return original_fallback_font;\n    // We cannot set kCTFontTraitMonoSpace in traits as if the original\n    // fallback font is Zapf Dingbats we get .AppleSystemUIFontMonospaced as\n    // the new fallback\n    CTFontSymbolicTraits traits = 0;\n    if (bold) traits |= kCTFontTraitBold;\n    if (italic) traits |= kCTFontTraitItalic;\n    RAII_CoreFoundation(CTFontDescriptorRef, descriptor, CTFontDescriptorCreateCopyWithSymbolicTraits(original_descriptor, traits, traits));\n    if (!descriptor) return original_fallback_font;\n    CTFontRef ans = CTFontCreateWithFontDescriptor(descriptor, CTFontGetSize(original_fallback_font), NULL);\n    if (!ans) return original_fallback_font;\n    RAII_CoreFoundation(CFStringRef, new_name, CTFontCopyFamilyName(ans));\n    RAII_CoreFoundation(CFStringRef, old_name, CTFontCopyFamilyName(original_fallback_font));\n    bool same_family = cf_string_equals(new_name, old_name);\n    /* NSLog(@\"old: %@ new: %@\", old_name, new_name); */\n    if (same_family && font_can_render_cell(ans, lc)) { CFRelease(original_fallback_font); return ans; }\n    CFRelease(ans);\n    return original_fallback_font;\n}\n\nstatic bool face_has_codepoint(const void *face, char_type ch) { return glyph_id_for_codepoint(face, ch) > 0; }\nstatic struct { char *buf; size_t capacity; } ft_buffer;\n\nstatic CFStringRef\nlc_as_fallback(const ListOfChars *lc) {\n    ensure_space_for((&ft_buffer), buf, ft_buffer.buf[0], lc->count * 4 + 128, capacity, 256, false);\n    cell_as_utf8_for_fallback(lc, ft_buffer.buf, ft_buffer.capacity);\n    return CFStringCreateWithCString(NULL, ft_buffer.buf, kCFStringEncodingUTF8);\n}\n\nPyObject*\ncreate_fallback_face(PyObject *base_face, const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) {\n    CTFace *self = (CTFace*)base_face;\n    RAII_CoreFoundation(CTFontRef, new_font, NULL);\n    RAII_CoreFoundation(CFStringRef, str, lc_as_fallback(lc));\n    if (str == NULL) return PyErr_NoMemory();\n\n    if (emoji_presentation) {\n        new_font = CTFontCreateWithName((CFStringRef)@\"AppleColorEmoji\", self->scaled_point_sz, NULL);\n        if (!new_font || !glyph_id_for_codepoint_ctfont(new_font, lc->chars[0])) {\n            if (new_font) CFRelease(new_font);\n            new_font = find_substitute_face(str, self->ct_font, lc);\n        }\n    }\n    else {\n        new_font = find_substitute_face(str, self->ct_font, lc);\n        new_font = apply_styles_to_fallback_font(new_font, bold, italic, lc);\n    }\n    if (new_font == NULL) Py_RETURN_NONE;\n    RAII_PyObject(postscript_name, convert_cfstring(CTFontCopyPostScriptName(new_font), true));\n    if (!postscript_name) return NULL;\n    ssize_t idx = -1;\n    PyObject *q, *ans = NULL;\n    while ((q = iter_fallback_faces(fg, &idx))) {\n        CTFace *qf = (CTFace*)q;\n        if (PyObject_RichCompareBool(postscript_name, qf->postscript_name, Py_EQ) == 1) {\n            ans = PyLong_FromSsize_t(idx);\n            break;\n        }\n    }\n    if (!ans) {\n        ans = (PyObject*)ct_face(new_font, NULL);\n        if (ans && !has_cell_text(face_has_codepoint, ans, global_state.debug_font_fallback, lc)) {\n            Py_CLEAR(ans);\n            Py_RETURN_NONE;\n        }\n    }\n    return ans;\n}\n\nunsigned int\nglyph_id_for_codepoint(const PyObject *s, char_type ch) {\n    const CTFace *self = (CTFace*)s;\n    return glyph_id_for_codepoint_ctfont(self->ct_font, ch);\n}\n\nbool\nis_glyph_empty(PyObject *s, glyph_index g) {\n    CTFace *self = (CTFace*)s;\n    CGGlyph gg = g;\n    CGRect bounds;\n    CTFontGetBoundingRectsForGlyphs(self->ct_font, kCTFontOrientationHorizontal, &gg, &bounds, 1);\n    return bounds.size.width <= 0;\n}\n\nint\nget_glyph_width(PyObject *s, glyph_index g) {\n    CTFace *self = (CTFace*)s;\n    CGGlyph gg = g;\n    CGRect bounds;\n    CTFontGetBoundingRectsForGlyphs(self->ct_font, kCTFontOrientationHorizontal, &gg, &bounds, 1);\n    return (int)ceil(bounds.size.width);\n}\n\nstatic float\n_scaled_point_sz(double font_sz_in_pts, double dpi_x, double dpi_y) {\n    return ((dpi_x + dpi_y) / 144.0) * font_sz_in_pts;\n}\n\nstatic float\nscaled_point_sz(FONTS_DATA_HANDLE fg) {\n    return _scaled_point_sz(fg->font_sz_in_pts, fg->logical_dpi_x, fg->logical_dpi_y);\n}\n\nstatic bool\n_set_size_for_face(CTFace *self, bool force, double font_sz_in_pts, double dpi_x, double dpi_y) {\n    float sz = _scaled_point_sz(font_sz_in_pts, dpi_x, dpi_y);\n    if (!force && self->scaled_point_sz == sz) return true;\n    RAII_CoreFoundation(CTFontRef, new_font, CTFontCreateCopyWithAttributes(self->ct_font, sz, NULL, NULL));\n    if (new_font == NULL) fatal(\"Out of memory\");\n    init_face(self, new_font);\n    return true;\n}\n\nbool\nset_size_for_face(PyObject *s, unsigned int UNUSED desired_height, bool force, FONTS_DATA_HANDLE fg) {\n    CTFace *self = (CTFace*)s;\n    return _set_size_for_face(self, force, fg->font_sz_in_pts, fg->logical_dpi_x, fg->logical_dpi_y);\n}\n\nbool\nface_apply_scaling(PyObject *f, const FONTS_DATA_HANDLE fg) {\n    return set_size_for_face(f, 0, false, fg);\n}\n\nstatic PyObject*\nset_size(CTFace *self, PyObject *args) {\n    double font_sz_in_pts, dpi_x, dpi_y;\n    if (!PyArg_ParseTuple(args, \"ddd\", &font_sz_in_pts, &dpi_x, &dpi_y)) return NULL;\n    if (!_set_size_for_face(self, false, font_sz_in_pts, dpi_x, dpi_y)) return NULL;\n    Py_RETURN_NONE;\n}\n\n// CoreText delegates U+2010 to U+00AD if the font is missing U+2010. Example\n// of such a font is Fira Code. So we specialize HarfBuzz glyph lookup to take\n// this into account.\nstatic hb_bool_t\nget_nominal_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t *glyph, void *user_data) {\n    hb_font_t *parent_font = font_data; (void)user_data; (void)font;\n    hb_bool_t ans = hb_font_get_nominal_glyph(parent_font, unicode, glyph);\n    if (!ans && unicode == 0x2010) {\n        CTFontRef ct_font = hb_coretext_font_get_ct_font(parent_font);\n        unsigned int gid = glyph_id_for_codepoint_ctfont(ct_font, unicode);\n        if (gid > 0) {\n            ans = true; *glyph = gid;\n        }\n    }\n    return ans;\n}\n\nstatic hb_bool_t\nget_variation_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data) {\n    hb_font_t *parent_font = font_data; (void)user_data; (void)font;\n    hb_bool_t ans = hb_font_get_variation_glyph(parent_font, unicode, variation, glyph);\n    if (!ans && unicode == 0x2010) {\n        CTFontRef ct_font = hb_coretext_font_get_ct_font(parent_font);\n        unsigned int gid = glyph_id_for_codepoint_ctfont(ct_font, unicode);\n        if (gid > 0) {\n            ans = true; *glyph = gid;\n        }\n    }\n    return ans;\n}\n\n\nhb_font_t*\nharfbuzz_font_for_face(PyObject* s) {\n    CTFace *self = (CTFace*)s;\n    if (!self->hb_font) {\n        hb_font_t *hb = hb_coretext_font_create(self->ct_font);\n        if (!hb) fatal(\"Failed to create hb_font_t\");\n        // dunno if we need this, harfbuzz docs say it is used by CoreText\n        // for optical sizing which changes the look of glyphs at small and large sizes\n        hb_font_set_ptem(hb, self->scaled_point_sz);\n        // Setup CoreText compatible glyph lookup functions\n        self->hb_font = hb_font_create_sub_font(hb);\n        if (!self->hb_font) fatal(\"Failed to create sub hb_font_t\");\n        hb_font_funcs_t *ffunctions = hb_font_funcs_create();\n        hb_font_set_funcs(self->hb_font, ffunctions, hb, NULL);\n        hb_font_funcs_set_nominal_glyph_func(ffunctions, get_nominal_glyph, NULL, NULL);\n        hb_font_funcs_set_variation_glyph_func(ffunctions, get_variation_glyph, NULL, NULL);\n        hb_font_funcs_destroy(ffunctions); // sub font retains a reference to this\n        hb_font_destroy(hb);  // the sub font retains a reference to the parent font\n    }\n    return self->hb_font;\n}\n\nFontCellMetrics\ncell_metrics(PyObject *s) {\n    // See https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html\n    CTFace *self = (CTFace*)s;\n    FontCellMetrics fcm = {0};\n#define count (128 - 32)\n    unichar chars[count+1] = {0};\n    CGGlyph glyphs[count+1] = {0};\n    unsigned int width = 0, w, i;\n    for (i = 0; i < count; i++) chars[i] = 32 + i;\n    CTFontGetGlyphsForCharacters(self->ct_font, chars, glyphs, count);\n    for (i = 0; i < count; i++) {\n        if (glyphs[i]) {\n            w = (unsigned int)(ceilf(\n                        CTFontGetAdvancesForGlyphs(self->ct_font, kCTFontOrientationHorizontal, glyphs+i, NULL, 1)));\n            if (w > width) width = w;\n        }\n    }\n    fcm.cell_width = MAX(1u, width);\n    fcm.underline_thickness = (unsigned int)ceil(MAX(0.1, self->underline_thickness));\n    fcm.strikethrough_thickness = fcm.underline_thickness;\n    // float line_height = MAX(1, floor(self->ascent + self->descent + MAX(0, self->leading) + 0.5));\n    // Let CoreText's layout engine calculate the line height. Slower, but hopefully more accurate.\n#define W \"AQWMH_gyl \"\n    CFStringRef ts = CFSTR(W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W);\n#undef W\n    CFMutableAttributedStringRef test_string = CFAttributedStringCreateMutable(kCFAllocatorDefault, CFStringGetLength(ts));\n    CFAttributedStringReplaceString(test_string, CFRangeMake(0, 0), ts);\n    CFAttributedStringSetAttribute(test_string, CFRangeMake(0, CFStringGetLength(ts)), kCTFontAttributeName, self->ct_font);\n    CGMutablePathRef path = CGPathCreateMutable();\n    CGPathAddRect(path, NULL, CGRectMake(10, 10, 200, 8000));\n    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(test_string);\n    CFRelease(test_string);\n    CTFrameRef test_frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);\n    CGPoint origin1, origin2;\n    CTFrameGetLineOrigins(test_frame, CFRangeMake(0, 1), &origin1);\n    CTFrameGetLineOrigins(test_frame, CFRangeMake(1, 1), &origin2);\n    CGFloat line_height = origin1.y - origin2.y;\n    CFArrayRef lines = CTFrameGetLines(test_frame);\n    if (!CFArrayGetCount(lines)) fatal(\"Failed to typeset test line to calculate cell metrics\");\n    CTLineRef line = CFArrayGetValueAtIndex(lines, 0);\n    CGRect bounds = CTLineGetBoundsWithOptions(line, 0);\n    CGRect bounds_without_leading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading);\n    CGFloat typographic_ascent, typographic_descent, typographic_leading;\n    CTLineGetTypographicBounds(line, &typographic_ascent, &typographic_descent, &typographic_leading);\n    fcm.cell_height = MAX(4u, (unsigned int)ceilf(line_height));\n    CGFloat bounds_ascent = bounds_without_leading.size.height + bounds_without_leading.origin.y;\n    fcm.baseline = (unsigned int)floor(bounds_ascent + 0.5);\n    // Not sure if we should add this to bounds ascent and then round it or add\n    // it to already rounded baseline and round again.\n    fcm.underline_position = (unsigned int)floor(bounds_ascent - self->underline_position + 0.5);\n    fcm.strikethrough_position = (unsigned int)floor(fcm.baseline * 0.65);\n\n    debug(\"Cell height calculation:\\n\");\n    debug(\"\\tline height from line origins: %f\\n\", line_height);\n    debug(\"\\tline bounds: origin-y: %f height: %f\\n\", bounds.origin.y, bounds.size.height);\n    debug(\"\\tline bounds-no-leading: origin-y: %f height: %f\\n\", bounds.origin.y, bounds.size.height);\n    debug(\"\\tbounds metrics: ascent: %f\\n\", bounds_ascent);\n    debug(\"\\tline metrics: ascent: %f descent: %f leading: %f\\n\", typographic_ascent, typographic_descent, typographic_leading);\n    debug(\"\\tfont metrics: ascent: %f descent: %f leading: %f underline_position: %f\\n\", self->ascent, self->descent, self->leading, self->underline_position);\n    debug(\"\\tcell_height: %u baseline: %u underline_position: %u strikethrough_position: %u\\n\", fcm.cell_height, fcm.baseline, fcm.underline_position, fcm.strikethrough_position);\n    CFRelease(test_frame); CFRelease(path); CFRelease(framesetter);\n    return fcm;\n\n#undef count\n}\n\nPyObject*\nface_from_descriptor(PyObject *descriptor, FONTS_DATA_HANDLE fg) {\n    RAII_CoreFoundation(CTFontDescriptorRef, desc, NULL);\n    if (builtin_nerd_font_descriptor) {\n        PyObject *psname = PyDict_GetItemString(descriptor, \"postscript_name\");\n        if (psname && PyUnicode_CompareWithASCIIString(psname, \"SymbolsNFM\") == 0) {\n            RAII_PyObject(path, get_path_for_font_descriptor(builtin_nerd_font_descriptor));\n            PyObject *dpath = PyDict_GetItemString(descriptor, \"path\");\n            if (dpath && PyUnicode_Compare(path, dpath) == 0) {\n                desc = builtin_nerd_font_descriptor; CFRetain(desc);\n            }\n        }\n    }\n    if (!desc) desc = font_descriptor_from_python(descriptor);\n    if (!desc) return NULL;\n    RAII_CoreFoundation(CTFontRef, font, CTFontCreateWithFontDescriptor(desc, fg ? scaled_point_sz(fg) : 12, NULL));\n    if (!font) { PyErr_SetString(PyExc_ValueError, \"Failed to create CTFont object\"); return NULL; }\n    return (PyObject*) ct_face(font, PyDict_GetItemString(descriptor, \"features\"));\n}\n\nPyObject*\nface_from_path(const char *path, int UNUSED index, FONTS_DATA_HANDLE fg UNUSED) {\n    RAII_CoreFoundation(CFStringRef, s, CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8));\n    RAII_CoreFoundation(CFURLRef, url, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, s, kCFURLPOSIXPathStyle, false));\n    RAII_CoreFoundation(CGDataProviderRef, dp, CGDataProviderCreateWithURL(url));\n    RAII_CoreFoundation(CGFontRef, cg_font, CGFontCreateWithDataProvider(dp));\n    RAII_CoreFoundation(CTFontRef, ct_font, CTFontCreateWithGraphicsFont(cg_font, 0.0, NULL, NULL));\n    return (PyObject*) ct_face(ct_font, NULL);\n}\n\nstatic PyObject*\nnew(PyTypeObject *type UNUSED, PyObject *args, PyObject *kw) {\n    const char *path = NULL;\n    PyObject *descriptor = NULL;\n\n    static char *kwds[] = {\"descriptor\", \"path\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"|Os\", kwds, &descriptor, &path)) return NULL;\n    if (descriptor) return face_from_descriptor(descriptor, NULL);\n    if (path) return face_from_path(path, 0, NULL);\n    PyErr_SetString(PyExc_TypeError, \"Must specify either path or descriptor\");\n    return NULL;\n}\n\nPyObject*\nspecialize_font_descriptor(PyObject *base_descriptor, double font_sz_in_pts UNUSED, double dpi_x UNUSED, double dpi_y UNUSED) {\n    return PyDict_Copy(base_descriptor);\n}\n\nstruct RenderBuffers {\n    uint8_t *render_buf;\n    size_t render_buf_sz, sz;\n    CGGlyph *glyphs;\n    CGRect *boxes;\n    CGPoint *positions;\n};\nstatic struct RenderBuffers buffers = {0};\n\nstatic void\nfinalize(void) {\n    free(ft_buffer.buf); ft_buffer.buf = NULL; ft_buffer.capacity = 0;\n    free(buffers.render_buf); free(buffers.glyphs); free(buffers.boxes); free(buffers.positions);\n    memset(&buffers, 0, sizeof(struct RenderBuffers));\n    if (all_fonts_collection_data) CFRelease(all_fonts_collection_data);\n    if (system_ui_font) CFRelease(system_ui_font);\n    system_ui_font = nil;\n    if (_nerd_font_descriptor) CFRelease(_nerd_font_descriptor);\n    if (builtin_nerd_font_descriptor) CFRelease(builtin_nerd_font_descriptor);\n    _nerd_font_descriptor = NULL; builtin_nerd_font_descriptor = NULL;\n}\n\n\nstatic void\nrender_color_glyph(CTFontRef font, uint8_t *buf, int glyph_id, unsigned int width, unsigned int height, unsigned int baseline) {\n    CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();\n    if (color_space == NULL) fatal(\"Out of memory\");\n    CGContextRef ctx = CGBitmapContextCreate(buf, width, height, 8, 4 * width, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault);\n    if (ctx == NULL) fatal(\"Out of memory\");\n    CGContextSetShouldAntialias(ctx, true);\n    CGContextSetShouldSmoothFonts(ctx, true);  // sub-pixel antialias\n    CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);\n    CGAffineTransform transform = CGAffineTransformIdentity;\n    CGContextSetTextDrawingMode(ctx, kCGTextFill);\n    CGGlyph glyph = glyph_id;\n    CGContextSetTextMatrix(ctx, transform);\n    CGContextSetTextPosition(ctx, -buffers.boxes[0].origin.x, MAX(2, height - baseline));\n    CGPoint p = CGPointMake(0, 0);\n    CTFontDrawGlyphs(font, &glyph, &p, 1, ctx);\n    CGContextRelease(ctx);\n    CGColorSpaceRelease(color_space);\n    for (size_t r = 0; r < width; r++) {\n        for (size_t c = 0; c < height; c++, buf += 4) {\n            uint32_t px = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];\n            *((pixel*)buf) = px;\n        }\n    }\n}\n\nstatic void\nensure_render_space(size_t width, size_t height, size_t num_glyphs) {\n    if (buffers.render_buf_sz < width * height) {\n        free(buffers.render_buf); buffers.render_buf = NULL;\n        buffers.render_buf_sz = width * height;\n        buffers.render_buf = malloc(buffers.render_buf_sz);\n        if (buffers.render_buf == NULL) fatal(\"Out of memory\");\n    }\n    if (buffers.sz < num_glyphs) {\n        buffers.sz = MAX(128, num_glyphs * 2);\n        free(buffers.boxes); free(buffers.glyphs); free(buffers.positions);\n        buffers.boxes = calloc(sizeof(buffers.boxes[0]), buffers.sz);\n        buffers.glyphs = calloc(sizeof(buffers.glyphs[0]), buffers.sz);\n        buffers.positions = calloc(sizeof(buffers.positions[0]), buffers.sz);\n        if (!buffers.boxes || !buffers.glyphs || !buffers.positions) fatal(\"Out of memory\");\n    }\n}\n\nstatic void\nsetup_ctx_for_alpha_mask(CGContextRef render_ctx) {\n    CGContextSetShouldAntialias(render_ctx, true);\n    CGContextSetShouldSmoothFonts(render_ctx, true);\n    CGContextSetGrayFillColor(render_ctx, 1, 1); // white glyphs\n    CGContextSetGrayStrokeColor(render_ctx, 1, 1);\n    CGContextSetLineWidth(render_ctx, OPT(macos_thicken_font));\n    CGContextSetTextDrawingMode(render_ctx, kCGTextFillStroke);\n    CGContextSetTextMatrix(render_ctx, CGAffineTransformIdentity);\n}\n\nstatic void\nrender_glyphs(CTFontRef font, unsigned int width, unsigned int height, unsigned int baseline, unsigned int num_glyphs) {\n    memset(buffers.render_buf, 0, width * height);\n    CGColorSpaceRef gray_color_space = CGColorSpaceCreateDeviceGray();\n    if (gray_color_space == NULL) fatal(\"Out of memory\");\n    CGContextRef render_ctx = CGBitmapContextCreate(buffers.render_buf, width, height, 8, width, gray_color_space, (kCGBitmapAlphaInfoMask & kCGImageAlphaNone));\n    CGColorSpaceRelease(gray_color_space);\n    if (render_ctx == NULL) fatal(\"Out of memory\");\n    setup_ctx_for_alpha_mask(render_ctx);\n    CGContextSetTextPosition(render_ctx, 0, height - baseline);\n    CTFontDrawGlyphs(font, buffers.glyphs, buffers.positions, num_glyphs, render_ctx);\n    CGContextRelease(render_ctx);\n}\n\nStringCanvas\nrender_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {\n    CTFace *self = (CTFace*)s;\n    CTFontRef font = self->ct_font;\n    size_t num_chars = strnlen(text, 32);\n    unichar chars[num_chars];\n    CGSize local_advances[num_chars];\n    for (size_t i = 0; i < num_chars; i++) chars[i] = text[i];\n    ensure_render_space(0, 0, num_chars);\n    CTFontGetGlyphsForCharacters(font, chars, buffers.glyphs, num_chars);\n    CTFontGetAdvancesForGlyphs(font, kCTFontOrientationDefault, buffers.glyphs, local_advances, num_chars);\n    CGRect bounding_box = CTFontGetBoundingRectsForGlyphs(font, kCTFontOrientationDefault, buffers.glyphs, buffers.boxes, num_chars);\n    CGFloat x = 0, y = 0;\n    for (size_t i = 0; i < num_chars; i++) {\n        buffers.positions[i] = CGPointMake(x, y);\n        x += local_advances[i].width; y += local_advances[i].height;\n    }\n    StringCanvas ans = { .width = (size_t)ceil(x), .height = (size_t)(2 * bounding_box.size.height) };\n    ensure_render_space(ans.width, ans.height, num_chars);\n    render_glyphs(font, ans.width, ans.height, baseline, num_chars);\n    ans.canvas = malloc(ans.width * ans.height);\n    if (ans.canvas) memcpy(ans.canvas, buffers.render_buf, ans.width * ans.height);\n    return ans;\n}\n\nstatic void destroy_hb_buffer(hb_buffer_t **x) { if (*x) hb_buffer_destroy(*x); }\n\nstatic PyObject*\nrender_codepoint(CTFace *self, PyObject *args) {\n    unsigned long cp, fg = 0xffffff;\n    if (!PyArg_ParseTuple(args, \"k|k\", &cp, &fg)) return NULL;\n    const int num_chars = 1;\n    ensure_render_space(0, 0, num_chars);\n    buffers.glyphs[0] = glyph_id_for_codepoint_ctfont(self->ct_font, cp);\n    CGSize local_advances[num_chars];\n    CTFontGetAdvancesForGlyphs(self->ct_font, kCTFontOrientationDefault, buffers.glyphs, local_advances, num_chars);\n    CGRect bounding_box = CTFontGetBoundingRectsForGlyphs(self->ct_font, kCTFontOrientationDefault, buffers.glyphs, buffers.boxes, num_chars);\n    StringCanvas ans = { .width = (size_t)(bounding_box.size.width + 1), .height = (size_t)(1 + bounding_box.size.height) };\n    size_t baseline = ans.height;\n    ensure_render_space(ans.width, ans.height, num_chars);\n    PyObject *pbuf = PyBytes_FromStringAndSize(NULL, ans.width * ans.height * sizeof(pixel));\n    if (!pbuf) return NULL;\n    memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf));\n    const unsigned long canvas_width = ans.width, canvas_height = ans.height;\n    if (CTFontSupportsColorGlyphs(self->ct_font)) {\n        render_color_glyph(self->ct_font, (uint8_t*)PyBytes_AS_STRING(pbuf), buffers.glyphs[0], ans.width, ans.height, baseline);\n    } else {\n        render_glyphs(self->ct_font, ans.width, ans.height, baseline, num_chars);\n        uint8_t r = (fg >> 16) & 0xff, g = (fg >> 8) & 0xff, b = fg & 0xff;\n        const uint8_t *last_pixel = (uint8_t*)PyBytes_AS_STRING(pbuf) + PyBytes_GET_SIZE(pbuf) - sizeof(pixel);\n        const uint8_t *s_limit = buffers.render_buf + canvas_width * canvas_height;\n        for (\n            uint8_t *p = (uint8_t*)PyBytes_AS_STRING(pbuf), *s = buffers.render_buf;\n            p <= last_pixel && s < s_limit;\n            p += sizeof(pixel), s++\n        ) {\n            p[0] = r; p[1] = g; p[2] = b; p[3] = s[0];\n        }\n    }\n    return Py_BuildValue(\"Nkk\", pbuf, canvas_width, canvas_height);\n}\n\nstatic PyObject*\nrender_sample_text(CTFace *self, PyObject *args) {\n    unsigned long canvas_width, canvas_height;\n    unsigned long fg = 0xffffff;\n    CTFontRef font = self->ct_font;\n    PyObject *ptext;\n    if (!PyArg_ParseTuple(args, \"Ukk|k\", &ptext, &canvas_width, &canvas_height, &fg)) return NULL;\n    FontCellMetrics fcm = cell_metrics((PyObject*)self);\n    if (!fcm.cell_width || !fcm.cell_height) return Py_BuildValue(\"yII\", \"\", fcm.cell_width, fcm.cell_height);\n    size_t num_chars = PyUnicode_GET_LENGTH(ptext);\n    int num_chars_per_line = canvas_width / fcm.cell_width, num_of_lines = (int)ceil((float)num_chars / (float)num_chars_per_line);\n    canvas_height = MIN(canvas_height, num_of_lines * fcm.cell_height);\n    RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height));\n    if (!pbuf) return NULL;\n    memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf));\n\n    __attribute__((cleanup(destroy_hb_buffer))) hb_buffer_t *hb_buffer = hb_buffer_create();\n    if (!hb_buffer_pre_allocate(hb_buffer, 4*num_chars)) { PyErr_NoMemory(); return NULL; }\n    for (size_t n = 0; n < num_chars; n++) {\n        Py_UCS4 codep = PyUnicode_READ_CHAR(ptext, n);\n        hb_buffer_add_utf32(hb_buffer, &codep, 1, 0, 1);\n    }\n    hb_buffer_guess_segment_properties(hb_buffer);\n    if (!HB_DIRECTION_IS_HORIZONTAL(hb_buffer_get_direction(hb_buffer))) goto end;\n    hb_shape(harfbuzz_font_for_face((PyObject*)self), hb_buffer, self->font_features.features, self->font_features.count);\n    unsigned int len = hb_buffer_get_length(hb_buffer);\n    hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL);\n    hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(hb_buffer, NULL);\n\n    memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf));\n    if (fcm.cell_width > canvas_width) goto end;\n\n    ensure_render_space(canvas_width, canvas_height, len);\n    float pen_x = 0, pen_y = 0;\n    unsigned num_glyphs = 0;\n    CGFloat scale = CTFontGetSize(self->ct_font) / CTFontGetUnitsPerEm(self->ct_font);\n    for (unsigned int i = 0; i < len; i++) {\n        float advance = (float)positions[i].x_advance * scale;\n        if (pen_x + advance > canvas_width) {\n            pen_y += fcm.cell_height;\n            pen_x = 0;\n            if (pen_y >= canvas_height) break;\n        }\n        double x = pen_x + (double)positions[i].x_offset * scale;\n        double y = pen_y + (double)positions[i].y_offset * scale;\n        pen_x += advance;\n        buffers.positions[i] = CGPointMake(x, -y);\n        buffers.glyphs[i] = info[i].codepoint;\n        num_glyphs++;\n    }\n    render_glyphs(font, canvas_width, canvas_height, fcm.baseline, num_glyphs);\n    uint8_t r = (fg >> 16) & 0xff, g = (fg >> 8) & 0xff, b = fg & 0xff;\n    const uint8_t *last_pixel = (uint8_t*)PyBytes_AS_STRING(pbuf) + PyBytes_GET_SIZE(pbuf) - sizeof(pixel);\n    const uint8_t *s_limit = buffers.render_buf + canvas_width * canvas_height;\n    for (\n        uint8_t *p = (uint8_t*)PyBytes_AS_STRING(pbuf), *s = buffers.render_buf;\n        p <= last_pixel && s < s_limit;\n        p += sizeof(pixel), s++\n    ) {\n        p[0] = r; p[1] = g; p[2] = b; p[3] = s[0];\n    }\nend:\n    return Py_BuildValue(\"OII\", pbuf, fcm.cell_width, fcm.cell_height);\n\n}\n\nstatic bool\nensure_ui_font(size_t in_height) {\n    static size_t for_height = 0;\n    if (system_ui_font) {\n        if (for_height == in_height) return true;\n        CFRelease(system_ui_font);\n    }\n    system_ui_font = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 0.f, NULL);\n    if (!system_ui_font) return false;\n    CGFloat line_height = MAX(1, floor(CTFontGetAscent(system_ui_font) + CTFontGetDescent(system_ui_font) + MAX(0, CTFontGetLeading(system_ui_font)) + 0.5));\n    CGFloat pts_per_px = CTFontGetSize(system_ui_font) / line_height;\n    CGFloat desired_size = in_height * pts_per_px;\n    if (desired_size != CTFontGetSize(system_ui_font)) {\n        CTFontRef sized = CTFontCreateCopyWithAttributes(system_ui_font, desired_size, NULL, NULL);\n        CFRelease(system_ui_font);\n        system_ui_font = sized;\n        if (!system_ui_font) return false;\n    }\n    for_height = in_height;\n    return true;\n}\n\nbool\ncocoa_render_line_of_text(const char *text, const color_type fg, const color_type bg, uint8_t *rgba_output, const size_t width, const size_t height) {\n    CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();\n    if (color_space == NULL) return false;\n    CGContextRef ctx = CGBitmapContextCreate(rgba_output, width, height, 8, 4 * width, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault);\n    CGColorSpaceRelease(color_space);\n    if (ctx == NULL) return false;\n    if (!ensure_ui_font(height)) return false;\n\n    CGContextSetShouldAntialias(ctx, true);\n    CGContextSetShouldSmoothFonts(ctx, true);  // sub-pixel antialias\n    CGContextSetRGBFillColor(ctx, ((bg >> 16) & 0xff) / 255.f, ((bg >> 8) & 0xff) / 255.f, (bg & 0xff) / 255.f, 1.f);\n    CGContextFillRect(ctx, CGRectMake(0.0, 0.0, width, height));\n    CGContextSetTextDrawingMode(ctx, kCGTextFill);\n    CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);\n    CGContextSetRGBFillColor(ctx, ((fg >> 16) & 0xff) / 255.f, ((fg >> 8) & 0xff) / 255.f, (fg & 0xff) / 255.f, 1.f);\n    CGContextSetRGBStrokeColor(ctx, ((fg >> 16) & 0xff) / 255.f, ((fg >> 8) & 0xff) / 255.f, (fg & 0xff) / 255.f, 1.f);\n\n    NSColor *color = [NSColor colorWithCalibratedRed:((fg >> 16) & 0xff) / 255.f green:((fg >> 8) & 0xff) / 255.f blue:(fg & 0xff) / 255.f alpha:1.0];\n    NSAttributedString *str = [[NSAttributedString alloc] initWithString:@(text) attributes:@{(NSString *)kCTFontAttributeName: (__bridge id)system_ui_font, NSForegroundColorAttributeName: color}];\n    if (!str) { CGContextRelease(ctx); return false; }\n    CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)str);\n    [str release];\n    if (!line) { CGContextRelease(ctx); return false; }\n    CGFloat ascent, descent, leading;\n    CTLineGetTypographicBounds(line, &ascent, &descent, &leading);\n    CGContextSetTextPosition(ctx, 0, descent);\n    CTLineDraw(line, ctx);\n    CFRelease(line);\n    CGContextRelease(ctx);\n    return true;\n}\n\nuint8_t*\nrender_single_ascii_char_as_mask(const char ch, size_t *result_width, size_t *result_height) {\n    if (!ensure_ui_font(*result_height)) { PyErr_SetString(PyExc_RuntimeError, \"failed to create UI font\"); return NULL; }\n    unichar chars = ch;\n    CGSize local_advances[1];\n    CTFontGetGlyphsForCharacters(system_ui_font, &chars, buffers.glyphs, 1);\n    CTFontGetAdvancesForGlyphs(system_ui_font, kCTFontOrientationDefault, buffers.glyphs, local_advances, 1);\n    CGRect bounding_box = CTFontGetBoundingRectsForGlyphs(system_ui_font, kCTFontOrientationDefault, buffers.glyphs, buffers.boxes, 1);\n\n    size_t width = (size_t)ceilf(bounding_box.size.width);\n    size_t height = (size_t)ceilf(bounding_box.size.height);\n    uint8_t *canvas = calloc(width, height);\n    if (!canvas) { PyErr_NoMemory(); return NULL; }\n    CGColorSpaceRef gray_color_space = CGColorSpaceCreateDeviceGray();\n    if (gray_color_space == NULL) { PyErr_NoMemory(); free(canvas); return NULL; }\n    CGContextRef render_ctx = CGBitmapContextCreate(canvas, width, height, 8, width, gray_color_space, (kCGBitmapAlphaInfoMask & kCGImageAlphaNone));\n    CGColorSpaceRelease(gray_color_space);\n    if (render_ctx == NULL) { PyErr_NoMemory(); free(canvas); return NULL; }\n    setup_ctx_for_alpha_mask(render_ctx);\n    /* printf(\"origin.y: %f descent: %f ascent: %f height: %zu size.height: %f\\n\", bounding_box.origin.y, CTFontGetDescent(system_ui_font), CTFontGetAscent(system_ui_font), height, bounding_box.size.height); */\n    CGContextSetTextPosition(render_ctx, -bounding_box.origin.x, -bounding_box.origin.y);\n    CTFontDrawGlyphs(system_ui_font, buffers.glyphs, buffers.positions, 1, render_ctx);\n    CGContextRelease(render_ctx);\n    *result_width = width; *result_height = height;\n    return canvas;\n}\n\n\nstatic bool\ndo_render(CTFontRef ct_font, unsigned int units_per_em, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, bool allow_resize, FONTS_DATA_HANDLE fg, GlyphRenderInfo *ri) {\n    unsigned int canvas_width = cell_width * num_cells;\n    ensure_render_space(canvas_width, cell_height, num_glyphs);\n    CGRect br = CTFontGetBoundingRectsForGlyphs(ct_font, kCTFontOrientationHorizontal, buffers.glyphs, buffers.boxes, num_glyphs);\n    const bool debug_rendering = false;\n    if (allow_resize) {\n        // Resize glyphs that would bleed into neighboring cells, by scaling the font size\n        float right = 0;\n        for (unsigned i=0; i < num_glyphs; i++) right = MAX(right, buffers.boxes[i].origin.x + buffers.boxes[i].size.width);\n        if (!bold && !italic && right > canvas_width + 1) {\n            if (debug_rendering) printf(\"resizing glyphs, right: %f canvas_width: %u\\n\", right, canvas_width);\n            CGFloat sz = CTFontGetSize(ct_font);\n            sz *= canvas_width / right;\n            CTFontRef new_font = CTFontCreateCopyWithAttributes(ct_font, sz, NULL, NULL);\n            bool ret = do_render(new_font, CTFontGetUnitsPerEm(new_font), bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, false, fg, ri);\n            CFRelease(new_font);\n            return ret;\n        }\n    }\n    CGFloat x = 0, y = 0;\n    CGFloat scale = CTFontGetSize(ct_font) / units_per_em;\n    for (unsigned i=0; i < num_glyphs; i++) {\n        buffers.positions[i].x = x + hb_positions[i].x_offset * scale; buffers.positions[i].y = y + hb_positions[i].y_offset * scale;\n        if (debug_rendering) printf(\"x=%f y=%f origin=%f width=%f x_advance=%f x_offset=%f y_advance=%f y_offset=%f\\n\",\n                buffers.positions[i].x, buffers.positions[i].y, buffers.boxes[i].origin.x, buffers.boxes[i].size.width,\n                hb_positions[i].x_advance * scale, hb_positions[i].x_offset * scale,\n                hb_positions[i].y_advance * scale, hb_positions[i].y_offset * scale);\n        x += hb_positions[i].x_advance * scale; y += hb_positions[i].y_advance * scale;\n    }\n    if (*was_colored) {\n        render_color_glyph(ct_font, (uint8_t*)canvas, info[0].codepoint, cell_width * num_cells, cell_height, baseline);\n    } else {\n        render_glyphs(ct_font, canvas_width, cell_height, baseline, num_glyphs);\n        Region src = {.bottom=cell_height, .right=canvas_width}, dest = {.bottom=cell_height, .right=canvas_width};\n        render_alpha_mask(buffers.render_buf, canvas, &src, &dest, canvas_width, canvas_width, 0xffffff);\n    }\n    ri->canvas_width = canvas_width; ri->rendered_width = (unsigned)ceil(br.size.width); ri->x = 0;\n    // FiraCode ligatures result in negative origins\n    if (br.origin.x > 0) ri->x = (int)br.origin.x;\n    return true;\n}\n\nbool\nrender_glyphs_in_cells(PyObject *s, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE fg, GlyphRenderInfo *ri) {\n    CTFace *self = (CTFace*)s;\n    ensure_render_space(128, 128, num_glyphs);\n    for (unsigned i=0; i < num_glyphs; i++) buffers.glyphs[i] = info[i].codepoint;\n    return do_render(self->ct_font, self->units_per_em, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, true, fg, ri);\n}\n\n// Font tables {{{\n\nstatic bool\nensure_name_table(CTFace *self) {\n    if (self->name_lookup_table) return true;\n    RAII_CoreFoundation(CFDataRef, cftable, CTFontCopyTable(self->ct_font, kCTFontTableName, kCTFontTableOptionNoOptions));\n    const uint8_t *table = cftable ? CFDataGetBytePtr(cftable) : NULL;\n    size_t table_len = cftable ? CFDataGetLength(cftable) : 0;\n    self->name_lookup_table = read_name_font_table(table, table_len);\n    return !!self->name_lookup_table;\n}\n\nstatic PyObject*\nget_best_name(CTFace *self, PyObject *nameid) {\n    if (!ensure_name_table(self)) return NULL;\n    return get_best_name_from_name_table(self->name_lookup_table, nameid);\n}\n\nstatic PyObject*\nget_variation(CTFace *self, PyObject *args UNUSED) {\n    RAII_CoreFoundation(CFDictionaryRef, src, CTFontCopyVariation(self->ct_font));\n    return variation_to_python(src);\n}\n\nstatic PyObject*\napplied_features(CTFace *self, PyObject *a UNUSED) {\n    return font_features_as_dict(&self->font_features);\n}\n\nstatic PyObject*\nget_features(CTFace *self, PyObject *a UNUSED) {\n    if (!ensure_name_table(self)) return NULL;\n    RAII_PyObject(output, PyDict_New()); if (!output) return NULL;\n    RAII_CoreFoundation(CFDataRef, cftable, CTFontCopyTable(self->ct_font, kCTFontTableGSUB, kCTFontTableOptionNoOptions));\n    const uint8_t *table = cftable ? CFDataGetBytePtr(cftable) : NULL;\n    size_t table_len = cftable ? CFDataGetLength(cftable) : 0;\n    if (!read_features_from_font_table(table, table_len, self->name_lookup_table, output)) return NULL;\n    RAII_CoreFoundation(CFDataRef, cfpostable, CTFontCopyTable(self->ct_font, kCTFontTableGPOS, kCTFontTableOptionNoOptions));\n    table = cfpostable ? CFDataGetBytePtr(cfpostable) : NULL;\n    table_len = cfpostable ? CFDataGetLength(cfpostable) : 0;\n    if (!read_features_from_font_table(table, table_len, self->name_lookup_table, output)) return NULL;\n    Py_INCREF(output); return output;\n}\n\n\nstatic PyObject*\nget_variable_data(CTFace *self, PyObject *args UNUSED) {\n    if (!ensure_name_table(self)) return NULL;\n    RAII_PyObject(output, PyDict_New());\n    if (!output) return NULL;\n    RAII_CoreFoundation(CFDataRef, cftable, CTFontCopyTable(self->ct_font, kCTFontTableFvar, kCTFontTableOptionNoOptions));\n    const uint8_t *table = cftable ? CFDataGetBytePtr(cftable) : NULL;\n    size_t table_len = cftable ? CFDataGetLength(cftable) : 0;\n    if (!read_fvar_font_table(table, table_len, self->name_lookup_table, output)) return NULL;\n    RAII_CoreFoundation(CFDataRef, stable, CTFontCopyTable(self->ct_font, kCTFontTableSTAT, kCTFontTableOptionNoOptions));\n    table = stable ? CFDataGetBytePtr(stable) : NULL;\n    table_len = stable ? CFDataGetLength(stable) : 0;\n    if (!read_STAT_font_table(table, table_len, self->name_lookup_table, output)) return NULL;\n    Py_INCREF(output); return output;\n}\n\nstatic PyObject*\nidentify_for_debug(CTFace *self, PyObject *args UNUSED) {\n    RAII_PyObject(features, PyTuple_New(self->font_features.count)); if (!features) return NULL;\n    char buf[128];\n    for (unsigned i = 0; i < self->font_features.count; i++) {\n        hb_feature_to_string(self->font_features.features + i, buf, sizeof(buf));\n        PyObject *f = PyUnicode_FromString(buf); if (!f) return NULL;\n        PyTuple_SET_ITEM(features, i, f);\n    }\n    return PyUnicode_FromFormat(\"%V: %V\\nFeatures: %S\", self->postscript_name, \"[psname]\", self->path, \"[path]\", features);\n}\n\n// }}}\n\n\n// Boilerplate {{{\n\nstatic PyObject*\ndisplay_name(CTFace *self, PyObject *args UNUSED) {\n    CFStringRef dn = CTFontCopyDisplayName(self->ct_font);\n    return convert_cfstring(dn, true);\n}\n\nstatic PyObject*\npostscript_name(CTFace *self, PyObject *args UNUSED) {\n    return self->postscript_name ? Py_BuildValue(\"O\", self->postscript_name) : PyUnicode_FromString(\"\");\n}\n\n\nstatic PyMethodDef methods[] = {\n    METHODB(display_name, METH_NOARGS),\n    METHODB(postscript_name, METH_NOARGS),\n    METHODB(get_variable_data, METH_NOARGS),\n    METHODB(applied_features, METH_NOARGS),\n    METHODB(get_features, METH_NOARGS),\n    METHODB(get_variation, METH_NOARGS),\n    METHODB(identify_for_debug, METH_NOARGS),\n    METHODB(set_size, METH_VARARGS),\n    METHODB(render_sample_text, METH_VARARGS),\n    METHODB(render_codepoint, METH_VARARGS),\n    METHODB(get_best_name, METH_O),\n    {NULL}  /* Sentinel */\n};\n\nconst char*\npostscript_name_for_face(const PyObject *face_) {\n    const CTFace *self = (const CTFace*)face_;\n    if (self->postscript_name) return PyUnicode_AsUTF8(self->postscript_name);\n    return \"\";\n}\n\n\nstatic PyObject *\nrepr(CTFace *self) {\n    char buf[1024] = {0};\n    snprintf(buf, sizeof(buf)/sizeof(buf[0]), \"ascent=%.1f, descent=%.1f, leading=%.1f, scaled_point_sz=%.1f, underline_position=%.1f underline_thickness=%.1f\",\n        (self->ascent), (self->descent), (self->leading), (self->scaled_point_sz), (self->underline_position), (self->underline_thickness));\n    return PyUnicode_FromFormat(\n        \"Face(family=%U, full_name=%U, postscript_name=%U, path=%U, units_per_em=%u, %s)\",\n        self->family_name, self->full_name, self->postscript_name, self->path, self->units_per_em, buf\n    );\n}\n\n\nstatic PyObject*\nadd_font_file(PyObject UNUSED *_self, PyObject *args) {\n    const unsigned char *path = NULL; Py_ssize_t sz;\n    if (!PyArg_ParseTuple(args, \"s#\", &path, &sz)) return NULL;\n    RAII_CoreFoundation(CFURLRef, url, CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, path, sz, false));\n    if (CTFontManagerRegisterFontsForURL(url, kCTFontManagerScopeProcess, NULL)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nset_builtin_nerd_font(PyObject UNUSED *self, PyObject *pypath) {\n    if (!PyUnicode_Check(pypath)) { PyErr_SetString(PyExc_TypeError, \"path must be a string\"); return NULL; }\n    const char *path = NULL; Py_ssize_t sz;\n    path = PyUnicode_AsUTF8AndSize(pypath, &sz);\n    RAII_CoreFoundation(CFURLRef, url, CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const unsigned char*)path, sz, false));\n    RAII_CoreFoundation(CFArrayRef, descriptors, CTFontManagerCreateFontDescriptorsFromURL(url));\n    if (!descriptors || CFArrayGetCount(descriptors) == 0) {\n        PyErr_SetString(PyExc_OSError, \"Failed to create descriptor from nerd font path\");\n        return NULL;\n    }\n    if (builtin_nerd_font_descriptor) CFRelease(builtin_nerd_font_descriptor);\n    builtin_nerd_font_descriptor = CFArrayGetValueAtIndex(descriptors, 0);\n    CFRetain(builtin_nerd_font_descriptor);\n    return font_descriptor_to_python(builtin_nerd_font_descriptor);\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(coretext_all_fonts, METH_O),\n    METHODB(add_font_file, METH_VARARGS),\n    METHODB(set_builtin_nerd_font, METH_O),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nstatic PyMemberDef members[] = {\n#define MEM(name, type) {#name, type, offsetof(CTFace, name), READONLY, #name}\n    MEM(units_per_em, T_UINT),\n    MEM(scaled_point_sz, T_FLOAT),\n    MEM(ascent, T_FLOAT),\n    MEM(descent, T_FLOAT),\n    MEM(leading, T_FLOAT),\n    MEM(underline_position, T_FLOAT),\n    MEM(underline_thickness, T_FLOAT),\n    MEM(family_name, T_OBJECT),\n    MEM(path, T_OBJECT),\n    MEM(full_name, T_OBJECT),\n    {NULL}  /* Sentinel */\n};\n\nPyTypeObject CTFace_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.CTFace\",\n    .tp_new = new,\n    .tp_basicsize = sizeof(CTFace),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"CoreText Font face\",\n    .tp_methods = methods,\n    .tp_members = members,\n    .tp_repr = (reprfunc)repr,\n};\n\n\n\nint\ninit_CoreText(PyObject *module) {\n    if (PyType_Ready(&CTFace_Type) < 0) return 0;\n    if (PyModule_AddObject(module, \"CTFace\", (PyObject *)&CTFace_Type) != 0) return 0;\n    if (PyModule_AddFunctions(module, module_methods) != 0) return 0;\n    register_at_exit_cleanup_func(CORE_TEXT_CLEANUP_FUNC, finalize);\n    return 1;\n}\n\n// }}}\n"
  },
  {
    "path": "kitty/cross-platform-random.h",
    "content": "/*\n * Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include <stdlib.h>\n#include <stdbool.h>\n\n#if __linux__\n#include <errno.h>\n#if __has_include(<sys/random.h>)\n#include <sys/random.h>\n\nstatic inline bool\nsecure_random_bytes(void *buf, size_t nbytes) {\n    unsigned char* p = buf;\n    ssize_t left = nbytes;\n    while(1) {\n        ssize_t n = getrandom(p, left, 0);\n        if (n >= left) return true;\n        if (n < 0) {\n            if (errno != EINTR) return false;  // should never happen but if it does, we fail without any feedback\n            continue;\n        }\n        left -= n; p += n;\n    }\n}\n#else\n#include \"safe-wrappers.h\"\nstatic inline bool\nsecure_random_bytes(void *buf, size_t nbytes) {\n    int fd = safe_open(\"/dev/urandom\", O_RDONLY, 0);\n    if (fd < 0) return false;\n    size_t bytes_read = 0;\n    while (bytes_read < nbytes) {\n        ssize_t n = read(fd, (uint8_t*)buf + bytes_read, nbytes - bytes_read);\n        if (n < 0) {\n            if (errno == EINTR) continue;\n            break;\n        }\n        bytes_read += n;\n    }\n    safe_close(fd, __FILE__, __LINE__);\n    return bytes_read == nbytes;\n}\n#endif\n#else\nstatic inline bool\nsecure_random_bytes(void *buf, size_t nbytes) {\n    arc4random_buf(buf, nbytes);\n    return true;\n}\n#endif\n"
  },
  {
    "path": "kitty/crypto.c",
    "content": "/*\n * crypto.c\n * Copyright (C) 2022 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"cross-platform-random.h\"\n\n#include <openssl/evp.h>\n#include <openssl/ec.h>\n#include <openssl/err.h>\n#include <openssl/pem.h>\n#include <openssl/bio.h>\n#include <openssl/rand.h>\n#include <sys/mman.h>\n#include <structmember.h>\n\n#ifdef LIBRESSL_VERSION_NUMBER\n/* from: https://github.com/libressl/portable/blob/master/include/compat/string.h#L63 */\n#define explicit_bzero libressl_explicit_bzero\nvoid explicit_bzero(void *, size_t);\n/* from: https://github.com/libressl/portable/blob/master/crypto/compat/freezero.c */\nvoid\nfreezero(void *ptr, size_t sz) {\n    if (ptr == NULL) return;\n    explicit_bzero(ptr, sz);\n    free(ptr);\n}\n#define OPENSSL_clear_free freezero\n#endif\n\n#define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH\n\ntypedef enum HASH_ALGORITHM { SHA1_HASH, SHA224_HASH, SHA256_HASH, SHA384_HASH, SHA512_HASH } HASH_ALGORITHM;\nstatic PyObject* Crypto_Exception = NULL;\n\nstatic PyObject*\nset_error_from_openssl(const char *prefix) {\n    BIO *bio = BIO_new(BIO_s_mem());\n    ERR_print_errors(bio);\n    char *buf = NULL;\n    size_t len = BIO_get_mem_data(bio, &buf);\n    PyObject *msg = PyUnicode_FromStringAndSize(buf, len);\n    if (msg) PyErr_Format(Crypto_Exception, \"%s: %U\", prefix, msg);\n    BIO_free(bio);\n    Py_CLEAR(msg);\n    return NULL;\n}\n\n// Secret {{{\ntypedef struct {\n    PyObject_HEAD\n\n    void *secret;\n    size_t secret_len;\n} Secret;\n\nstatic PyObject *\nnew_secret(PyTypeObject *type UNUSED, PyObject *args UNUSED, PyObject *kwds UNUSED) {\n    PyErr_SetString(PyExc_TypeError, \"Cannot create Secret objects directly\"); return NULL;\n}\n\nstatic Secret* alloc_secret(size_t len);\n\nstatic void\ndealloc_secret(Secret *self) {\n    if (self->secret) OPENSSL_clear_free(self->secret, self->secret_len);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic int\n__eq__(Secret *a, Secret *b) {\n    const size_t l = a->secret_len < b->secret_len ? a->secret_len : b->secret_len;\n    return memcmp(a->secret, b->secret, l) == 0;\n}\n\nstatic Py_ssize_t\n__len__(PyObject *self) {\n    return (Py_ssize_t)(((Secret*)self)->secret_len);\n}\n\n\nstatic PySequenceMethods sequence_methods = {\n    .sq_length = __len__,\n};\n\n\nstatic PyObject *\nrichcmp(PyObject *obj1, PyObject *obj2, int op);\n\nstatic PyTypeObject Secret_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.Secret\",\n    .tp_basicsize = sizeof(Secret),\n    .tp_dealloc = (destructor)dealloc_secret,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Secure storage for secrets\",\n    .tp_new = new_secret,\n    .tp_richcompare = richcmp,\n    .tp_as_sequence = &sequence_methods,\n};\n\nRICHCMP(Secret)\n\nstatic Secret*\nalloc_secret(size_t len) {\n    Secret *self = (Secret*)Secret_Type.tp_alloc(&Secret_Type, 0);\n    if (self) {\n        self->secret_len = len;\n        if (NULL == (self->secret = OPENSSL_malloc(len))) { Py_CLEAR(self); return (Secret*)set_error_from_openssl(\"Failed to malloc\"); }\n        if (0 != mlock(self->secret, self->secret_len)) { Py_CLEAR(self); return (Secret*)PyErr_SetFromErrno(PyExc_OSError); }\n    }\n    return self;\n}\n// }}}\n\n// EllipticCurveKey {{{\ntypedef struct {\n    PyObject_HEAD\n\n    EVP_PKEY *key;\n    int algorithm, nid;\n} EllipticCurveKey;\n\n\nstatic PyObject *\nnew_ec_key(PyTypeObject *type, PyObject *args, PyObject *kwds) {\n    EllipticCurveKey *self;\n    static const char* kwlist[] = {\"algorithm\", NULL};\n    int algorithm = EVP_PKEY_X25519, nid = NID_X25519;\n    if (!PyArg_ParseTupleAndKeywords(args, kwds, \"|i\", (char**)kwlist, &algorithm)) return NULL;\n    switch(algorithm) {\n        case EVP_PKEY_X25519: break;\n        default: PyErr_SetString(PyExc_KeyError, \"Unknown algorithm\"); return NULL;\n    }\n    EVP_PKEY *key = NULL;\n    EVP_PKEY_CTX *pctx = NULL;\n#define cleanup() { if (key) EVP_PKEY_free(key); key = NULL; if (pctx) EVP_PKEY_CTX_free(pctx); pctx = NULL; }\n#define ssl_error(text) { cleanup(); return set_error_from_openssl(text); }\n\n    if (NULL == (pctx = EVP_PKEY_CTX_new_id(nid, NULL))) ssl_error(\"Failed to create context for key generation\");\n    if(1 != EVP_PKEY_keygen_init(pctx)) ssl_error(\"Failed to initialize keygen context\");\n    if (1 != EVP_PKEY_keygen(pctx, &key)) ssl_error(\"Failed to generate key\");\n\n    self = (EllipticCurveKey *)type->tp_alloc(type, 0);\n    if (self) {\n        self->key = key; key = NULL;\n        self->nid = nid; self->algorithm = algorithm;\n    }\n    cleanup();\n    return (PyObject*) self;\n#undef cleanup\n#undef ssl_error\n}\n\nstatic void\ndealloc_ec_key(EllipticCurveKey* self) {\n    if (self->key) EVP_PKEY_free(self->key);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic PyObject*\nhash_data_to_secret(const unsigned char *data, size_t len, int hash_algorithm) {\n    size_t hash_size;\n#define H(which) case which##_HASH: hash_size = which##_DIGEST_LENGTH; break;\n    switch (hash_algorithm) {\n        H(SHA1) H(SHA224) H(SHA256) H(SHA384) H(SHA512)\n        default: PyErr_Format(PyExc_KeyError, \"Unknown hash algorithm: %d\", hash_algorithm); return NULL;\n    }\n#undef H\n    Secret *ans = alloc_secret(hash_size);\n    if (!ans) return NULL;\n#define H(which) case which##_HASH: if (which(data, len, ans->secret) == NULL) { Py_CLEAR(ans); return set_error_from_openssl(\"Failed to \" #which); } break;\n    switch ((HASH_ALGORITHM)hash_algorithm) { H(SHA1) H(SHA224) H(SHA256) H(SHA384) H(SHA512) }\n#undef H\n    return (PyObject*)ans;\n}\n\nstatic PyObject*\nderive_secret(EllipticCurveKey *self, PyObject *args) {\n    const char *pubkey_raw;\n    int hash_algorithm = SHA256_HASH;\n    Py_ssize_t pubkey_len;\n    if (!PyArg_ParseTuple(args, \"y#|i\", &pubkey_raw, &pubkey_len, &hash_algorithm)) return NULL;\n\n    EVP_PKEY_CTX *ctx = NULL;\n    unsigned char *secret = NULL; size_t secret_len = 0;\n    EVP_PKEY *public_key = EVP_PKEY_new_raw_public_key(self->algorithm, NULL, (const unsigned char*)pubkey_raw, pubkey_len);\n#define cleanup() { if (public_key) EVP_PKEY_free(public_key); public_key = NULL; if (ctx) EVP_PKEY_CTX_free(ctx); ctx = NULL; if (secret) OPENSSL_clear_free(secret, secret_len); secret = NULL; }\n#define ssl_error(text) { cleanup(); return set_error_from_openssl(text); }\n    if (!public_key) ssl_error(\"Failed to create public key\");\n\n    if (NULL == (ctx = EVP_PKEY_CTX_new(self->key, NULL))) ssl_error(\"Failed to create context for shared secret derivation\");\n    if (1 != EVP_PKEY_derive_init(ctx)) ssl_error(\"Failed to initialize derivation\");\n    if (1 != EVP_PKEY_derive_set_peer(ctx, public_key)) ssl_error(\"Failed to add public key\");\n\n    if (1 != EVP_PKEY_derive(ctx, NULL, &secret_len)) ssl_error(\"Failed to get length for secret\");\n    if (NULL == (secret = OPENSSL_malloc(secret_len))) ssl_error(\"Failed to allocate secret key\");\n    if (mlock(secret, secret_len) != 0) { cleanup(); return PyErr_SetFromErrno(PyExc_OSError); }\n    if (1 != (EVP_PKEY_derive(ctx, secret, &secret_len))) ssl_error(\"Failed to derive the secret\");\n\n    PyObject *ans = hash_data_to_secret(secret, secret_len, hash_algorithm);\n    cleanup();\n    return ans;\n#undef cleanup\n#undef ssl_error\n}\n\n\nstatic PyObject*\nelliptic_curve_key_get_public(EllipticCurveKey *self, void UNUSED *closure) {\n    /* PEM_write_PUBKEY(stdout, pkey); */\n    size_t len = 0;\n    if (1 != EVP_PKEY_get_raw_public_key(self->key, NULL, &len)) return set_error_from_openssl(\"Could not get public key from EVP_PKEY\");\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, len);\n    if (!ans) return NULL;\n    if (1 != EVP_PKEY_get_raw_public_key(self->key, (unsigned char*)PyBytes_AS_STRING(ans), &len)) { Py_CLEAR(ans); return set_error_from_openssl(\"Could not get public key from EVP_PKEY\"); }\n    return ans;\n}\n\n\nstatic PyObject*\nelliptic_curve_key_get_private(EllipticCurveKey *self, void UNUSED *closure) {\n    size_t len = 0;\n    if (1 != EVP_PKEY_get_raw_private_key(self->key, NULL, &len)) return set_error_from_openssl(\"Could not get public key from EVP_PKEY\");\n    Secret *ans = alloc_secret(len);\n    if (!ans) return NULL;\n    if (mlock(PyBytes_AS_STRING(ans), len) != 0) { Py_CLEAR(ans); return PyErr_SetFromErrno(PyExc_OSError); }\n    if (1 != EVP_PKEY_get_raw_private_key(self->key, (unsigned char*)ans->secret, &len)) { Py_CLEAR(ans); return set_error_from_openssl(\"Could not get public key from EVP_PKEY\"); }\n    return (PyObject*)ans;\n}\n\n\nstatic PyGetSetDef getsetters[] = {\n    {\"public\", (getter)elliptic_curve_key_get_public, NULL, \"Get the public key as raw bytes\", NULL},\n    {\"private\", (getter)elliptic_curve_key_get_private, NULL, \"Get the private key as raw bytes\", NULL},\n    {NULL}  /* Sentinel */\n};\n\nstatic PyMethodDef methods[] = {\n    METHODB(derive_secret, METH_VARARGS),\n    {NULL}  /* Sentinel */\n};\n\n\nstatic PyTypeObject EllipticCurveKey_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.EllipticCurveKey\",\n    .tp_basicsize = sizeof(EllipticCurveKey),\n    .tp_dealloc = (destructor)dealloc_ec_key,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Keys for use with Elliptic Curve crypto\",\n    .tp_new = new_ec_key,\n    .tp_methods = methods,\n    .tp_getset = getsetters,\n};\n// }}}\n\n// AES256GCMEncrypt {{{\ntypedef struct {\n    PyObject_HEAD\n\n    EVP_CIPHER_CTX *ctx;\n    PyObject *iv, *tag;\n    int state;\n} AES256GCMEncrypt;\n\nstatic PyObject *\nnew_aes256gcmencrypt(PyTypeObject *type, PyObject *args, PyObject *kwds UNUSED) {\n    Secret *key;\n    if (!PyArg_ParseTuple(args, \"O!\", &Secret_Type, &key)) return NULL;\n    const EVP_CIPHER *cipher = EVP_get_cipherbynid(NID_aes_256_gcm);\n    if (key->secret_len != (size_t)EVP_CIPHER_key_length(cipher)) { PyErr_Format(PyExc_ValueError, \"The key for AES 256 GCM must be %d bytes long\", EVP_CIPHER_key_length(cipher)); return NULL; }\n    AES256GCMEncrypt *self = (AES256GCMEncrypt *)type->tp_alloc(type, 0);\n    if (!self) return NULL;\n    if (!(self->ctx = EVP_CIPHER_CTX_new())) { Py_CLEAR(self); return set_error_from_openssl(\"Failed to allocate encryption context\"); }\n    if (!(self->iv = PyBytes_FromStringAndSize(NULL, EVP_CIPHER_iv_length(cipher)))) { Py_CLEAR(self); return NULL; }\n    if (!secure_random_bytes((unsigned char*)PyBytes_AS_STRING(self->iv), PyBytes_GET_SIZE(self->iv))) { Py_CLEAR(self); return NULL; }\n    if (!(self->tag = PyBytes_FromStringAndSize(NULL, 0))) { Py_CLEAR(self); return NULL; }\n    if (1 != EVP_EncryptInit_ex(self->ctx, cipher, NULL, key->secret, (const unsigned char*)PyBytes_AS_STRING(self->iv))) {\n        Py_CLEAR(self); return set_error_from_openssl(\"Failed to initialize encryption context\"); }\n    return (PyObject*)self;\n}\n\nstatic void\ndealloc_aes256gcmencrypt(AES256GCMEncrypt *self) {\n    Py_CLEAR(self->iv); Py_CLEAR(self->tag);\n    if (self->ctx) EVP_CIPHER_CTX_free(self->ctx);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic PyObject*\nadd_authenticated_but_unencrypted_data(AES256GCMEncrypt *self, PyObject *args) {\n    if (self->state > 0) { PyErr_SetString(Crypto_Exception, \"Cannot add data once encryption has started\"); return NULL; }\n    const char *aad; Py_ssize_t aad_len;\n    if (!PyArg_ParseTuple(args, \"y#\", &aad, &aad_len)) return NULL;\n    int len;\n    if (aad_len > 0 && 1 != EVP_EncryptUpdate(self->ctx, NULL, &len, (const unsigned char*)aad, aad_len)) return set_error_from_openssl(\"Failed to add AAD data\");\n    Py_RETURN_NONE;\n}\n\nstatic int\ncipher_ctx_tag_length(const EVP_CIPHER_CTX *ctx) {\n#if OPENSSL_VERSION_NUMBER >= 0x30000000L\n    return EVP_CIPHER_CTX_tag_length(ctx);\n#else\n    (void)ctx;\n    return 16;\n#endif\n}\n\nstatic PyObject*\nadd_data_to_be_encrypted(AES256GCMEncrypt *self, PyObject *args) {\n    if (self->state > 1) { PyErr_SetString(Crypto_Exception, \"Encryption has been finished\"); return NULL; }\n    const char *plaintext; Py_ssize_t plaintext_len;\n    int finish_encryption = 0;\n    if (!PyArg_ParseTuple(args, \"y#|p\", &plaintext, &plaintext_len, &finish_encryption)) return NULL;\n    PyObject *ciphertext = PyBytes_FromStringAndSize(NULL, plaintext_len + 2 * EVP_CIPHER_CTX_block_size(self->ctx));\n    if (!ciphertext) return NULL;\n    self->state = 1;\n    int offset = 0;\n    if (plaintext_len) {\n        int len = PyBytes_GET_SIZE(ciphertext);\n        if (1 != EVP_EncryptUpdate(self->ctx, (unsigned char*)PyBytes_AS_STRING(ciphertext), &len, (const unsigned char*)plaintext, plaintext_len)\n            ) { Py_CLEAR(ciphertext); return set_error_from_openssl(\"Failed to encrypt\"); }\n        offset = len;\n    }\n    if (finish_encryption) {\n        int len = PyBytes_GET_SIZE(ciphertext) - offset;\n        if (1 != EVP_EncryptFinal_ex(self->ctx, (unsigned char*)PyBytes_AS_STRING(ciphertext) + offset, &len)) {\n            Py_CLEAR(ciphertext); return set_error_from_openssl(\"Failed to finish encryption\"); }\n        offset += len;\n        self->state = 2;\n\n        PyObject *tag = PyBytes_FromStringAndSize(NULL, cipher_ctx_tag_length(self->ctx));\n        if (!tag) { Py_CLEAR(ciphertext); return NULL; }\n        Py_CLEAR(self->tag); self->tag = tag;\n        if (1 != EVP_CIPHER_CTX_ctrl(self->ctx, EVP_CTRL_AEAD_GET_TAG, PyBytes_GET_SIZE(self->tag), PyBytes_AS_STRING(tag))) {\n            Py_CLEAR(ciphertext); return NULL;\n        }\n    }\n    if (offset != PyBytes_GET_SIZE(ciphertext)) { _PyBytes_Resize(&ciphertext, offset); if (!ciphertext) return NULL; }\n    return ciphertext;\n}\n\nstatic PyMethodDef aes256gcmencrypt_methods[] = {\n    METHODB(add_authenticated_but_unencrypted_data, METH_VARARGS),\n    METHODB(add_data_to_be_encrypted, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nstatic PyMemberDef aes256gcmencrypt_members[] = {\n    {\"iv\", T_OBJECT_EX, offsetof(AES256GCMEncrypt, iv), READONLY, \"IV\"},\n    {\"tag\", T_OBJECT_EX, offsetof(AES256GCMEncrypt, tag), READONLY, \"The tag for authentication\"},\n    {NULL}\n};\n\nstatic PyTypeObject AES256GCMEncrypt_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.AES256GCMEncrypt\",\n    .tp_basicsize = sizeof(AES256GCMEncrypt),\n    .tp_dealloc = (destructor)dealloc_aes256gcmencrypt,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Encrypt using AES 256 GCM with authentication\",\n    .tp_new = new_aes256gcmencrypt,\n    .tp_methods = aes256gcmencrypt_methods,\n    .tp_members = aes256gcmencrypt_members,\n};\n\n// }}}\n\n// AES256GCMDecrypt {{{\ntypedef struct {\n    PyObject_HEAD\n\n    EVP_CIPHER_CTX *ctx;\n    int state;\n} AES256GCMDecrypt;\n\nstatic PyObject *\nnew_aes256gcmdecrypt(PyTypeObject *type, PyObject *args, PyObject *kwds UNUSED) {\n    Secret *key; unsigned char *iv, *tag; Py_ssize_t iv_len, tag_len;\n    if (!PyArg_ParseTuple(args, \"O!y#y#\", &Secret_Type, &key, &iv, &iv_len, &tag, &tag_len)) return NULL;\n    const EVP_CIPHER *cipher = EVP_get_cipherbynid(NID_aes_256_gcm);\n    if (key->secret_len != (size_t)EVP_CIPHER_key_length(cipher)) { PyErr_Format(PyExc_ValueError, \"The key for AES 256 GCM must be %d bytes long\", EVP_CIPHER_key_length(cipher)); return NULL; }\n    if (iv_len < EVP_CIPHER_iv_length(cipher)) { PyErr_Format(PyExc_ValueError, \"The iv for AES 256 GCM must be at least %d bytes long\", EVP_CIPHER_iv_length(cipher)); return NULL; }\n    AES256GCMDecrypt *self = (AES256GCMDecrypt *)type->tp_alloc(type, 0);\n    if (!self) return NULL;\n    if (!(self->ctx = EVP_CIPHER_CTX_new())) { Py_CLEAR(self); return set_error_from_openssl(\"Failed to allocate decryption context\"); }\n    if (iv_len > EVP_CIPHER_iv_length(cipher)) {\n        if (!EVP_CIPHER_CTX_ctrl(self->ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL)) { Py_CLEAR(self); return set_error_from_openssl(\"Failed to set the IV length\"); }\n    }\n    if (1 != EVP_DecryptInit_ex(self->ctx, cipher, NULL, key->secret, iv)) {\n        Py_CLEAR(self); return set_error_from_openssl(\"Failed to initialize encryption context\"); }\n    // Ensure tag length is 16 because the OpenSSL verification routines will happily pass even if you set a truncated tag.\n    if (tag_len < cipher_ctx_tag_length(self->ctx)) { PyErr_Format(PyExc_ValueError, \"Tag length for AES 256 GCM must be at least %d\", cipher_ctx_tag_length(self->ctx)); return NULL; }\n    if (!EVP_CIPHER_CTX_ctrl(self->ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag)) { Py_CLEAR(self); return set_error_from_openssl(\"Failed to set the tag\"); }\n\n    return (PyObject*)self;\n}\n\nstatic void\ndealloc_aes256gcmdecrypt(AES256GCMDecrypt *self) {\n    if (self->ctx) EVP_CIPHER_CTX_free(self->ctx);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic PyObject*\nadd_data_to_be_authenticated_but_not_decrypted(AES256GCMDecrypt *self, PyObject *args) {\n    if (self->state > 0) { PyErr_SetString(Crypto_Exception, \"Cannot add data once decryption has started\"); return NULL; }\n    const char *aad; Py_ssize_t aad_len;\n    if (!PyArg_ParseTuple(args, \"y#\", &aad, &aad_len)) return NULL;\n    int len;\n    if (aad_len > 0 && 1 != EVP_DecryptUpdate(self->ctx, NULL, &len, (const unsigned char*)aad, aad_len)) return set_error_from_openssl(\"Failed to add AAD data\");\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nadd_data_to_be_decrypted(AES256GCMDecrypt *self, PyObject *args) {\n    if (self->state > 1) { PyErr_SetString(Crypto_Exception, \"Decryption has been finished\"); return NULL; }\n    const char *ciphertext; Py_ssize_t ciphertext_len;\n    int finish_decryption = 0;\n    if (!PyArg_ParseTuple(args, \"y#|p\", &ciphertext, &ciphertext_len, &finish_decryption)) return NULL;\n    PyObject *plaintext = PyBytes_FromStringAndSize(NULL, ciphertext_len + 2 * EVP_CIPHER_CTX_block_size(self->ctx));\n    if (!plaintext) return NULL;\n    self->state = 1;\n    int offset = 0;\n    if (ciphertext_len) {\n        int len = PyBytes_GET_SIZE(plaintext);\n        if (1 != EVP_DecryptUpdate(self->ctx, (unsigned char*)PyBytes_AS_STRING(plaintext), &len, (const unsigned char*)ciphertext, ciphertext_len)\n            ) { Py_CLEAR(plaintext); return set_error_from_openssl(\"Failed to decrypt\"); }\n        offset = len;\n    }\n    if (finish_decryption) {\n        int len = PyBytes_GET_SIZE(plaintext) - offset;\n        int ret = EVP_DecryptFinal_ex(self->ctx, (unsigned char*)PyBytes_AS_STRING(plaintext) + offset, &len);\n        self->state = 2;\n        if (ret <= 0) { Py_CLEAR(plaintext); PyErr_SetString(Crypto_Exception, \"Failed to finish decrypt\"); return NULL; }\n        offset += len;\n    }\n    if (offset != PyBytes_GET_SIZE(plaintext)) { _PyBytes_Resize(&plaintext, offset); if (!plaintext) return NULL; }\n    return plaintext;\n}\n\nstatic PyMethodDef aes256gcmdecrypt_methods[] = {\n    METHODB(add_data_to_be_authenticated_but_not_decrypted, METH_VARARGS),\n    METHODB(add_data_to_be_decrypted, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nstatic PyTypeObject AES256GCMDecrypt_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.AES256GCMDecrypt\",\n    .tp_basicsize = sizeof(AES256GCMDecrypt),\n    .tp_dealloc = (destructor)dealloc_aes256gcmdecrypt,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Decrypt using AES 256 GCM with authentication\",\n    .tp_new = new_aes256gcmdecrypt,\n    .tp_methods = aes256gcmdecrypt_methods,\n};\n\n// }}}\n\nstatic PyMethodDef module_methods[] = {\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nbool\ninit_crypto_library(PyObject *module) {\n    Crypto_Exception = PyErr_NewException(\"fast_data_types.CryptoError\", NULL, NULL);\n    if (Crypto_Exception == NULL) return false;\n    if (PyModule_AddObject(module, \"CryptoError\", Crypto_Exception) != 0) return false;\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    ADD_TYPE(Secret); ADD_TYPE(EllipticCurveKey); ADD_TYPE(AES256GCMEncrypt); ADD_TYPE(AES256GCMDecrypt);\n    if (PyModule_AddIntConstant(module, \"X25519\", EVP_PKEY_X25519) != 0) return false;\n#define AI(which) if (PyModule_AddIntMacro(module, which) != 0) return false;\n    AI(SHA1_HASH); AI(SHA224_HASH); AI(SHA256_HASH); AI(SHA384_HASH); AI(SHA512_HASH);\n#undef AI\n    return true;\n}\n"
  },
  {
    "path": "kitty/cursor.c",
    "content": "/*\n * cursor.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"line.h\"\n\n#include <structmember.h>\n\nstatic PyObject *\nnew_cursor_object(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {\n    Cursor *self;\n\n    self = (Cursor *)type->tp_alloc(type, 0);\n    return (PyObject*) self;\n}\n\nstatic void\ndealloc(Cursor* self) {\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\n#define EQ(x) (a->x == b->x)\nstatic int __eq__(Cursor *a, Cursor *b) {\n    return memcmp(&a->sgr, &b->sgr, sizeof(a->sgr)) == 0 && EQ(x) && EQ(y) && EQ(shape) && EQ(non_blinking);\n}\n#undef EQ\n\nstatic const char* cursor_names[NUM_OF_CURSOR_SHAPES] = { \"NO_SHAPE\", \"BLOCK\", \"BEAM\", \"UNDERLINE\", \"HOLLOW\" };\n\n#define BOOL(x) ((x) ? Py_True : Py_False)\nstatic PyObject *\nrepr(Cursor *self) {\n    return PyUnicode_FromFormat(\n        \"Cursor(x=%u, y=%u, shape=%s, blink=%R, fg=#%08x, bg=#%08x, bold=%R, italic=%R, reverse=%R, strikethrough=%R, dim=%R, decoration=%d, decoration_fg=#%08x, text_blink=%R)\",\n        self->x, self->y, (self->shape < NUM_OF_CURSOR_SHAPES ? cursor_names[self->shape] : \"INVALID\"),\n        BOOL(!self->non_blinking), self->sgr.fg, self->sgr.bg, BOOL(self->sgr.bold), BOOL(self->sgr.italic), BOOL(self->sgr.reverse), BOOL(self->sgr.strikethrough), BOOL(self->sgr.dim), self->sgr.decoration, self->sgr.decoration_fg, BOOL(self->sgr.blink)\n    );\n}\n\nvoid\ncursor_reset_display_attrs(Cursor *self) {\n    zero_at_ptr(&self->sgr);\n}\n\n\nstatic void\nparse_color(int *params, unsigned int *i, unsigned int count, uint32_t *result) {\n    unsigned int attr;\n    uint8_t r, g, b;\n    if (*i < count) {\n        attr = params[(*i)++];\n        switch(attr) {\n            case 5:\n                if (*i < count) *result = (params[(*i)++] & 0xFF) << 8 | 1;\n                break;\n            case 2: \\\n                if (*i + 2 < count) {\n                    /* Ignore the first parameter in a four parameter RGB */\n                    /* sequence (unused color space id), see https://github.com/kovidgoyal/kitty/issues/227 */\n                    if (*i +3 < count) (*i)++;\n                    r = params[(*i)++] & 0xFF;\n                    g = params[(*i)++] & 0xFF;\n                    b = params[(*i)++] & 0xFF;\n                    *result = r << 24 | g << 16 | b << 8 | 2;\n                }\n                break;\n        }\n    }\n}\n\n\nvoid\ncursor_from_sgr(Cursor *self, int *params, unsigned int count, bool is_group) {\n#define SET_COLOR(which) { parse_color(params, &i, count, &self->which); } break;\nSTART_ALLOW_CASE_RANGE\n    unsigned int i = 0, attr;\n    if (!count) { params[0] = 0; count = 1; }\n    while (i < count) {\n        attr = params[i++];\n        switch(attr) {\n            case 0:\n                cursor_reset_display_attrs(self);  break;\n            case 1:\n                self->sgr.bold = true;  break;\n            case 2:\n                self->sgr.dim = true; break;\n            case 3:\n                self->sgr.italic = true;  break;\n            case 4:\n                if (is_group && i < count) { self->sgr.decoration = MIN(5, params[i]); i++; }\n                else self->sgr.decoration = 1;\n                break;\n            case 5:\n                self->sgr.blink = true; break;\n            case 7:\n                self->sgr.reverse = true;  break;\n            case 9:\n                self->sgr.strikethrough = true;  break;\n            case 21:\n                self->sgr.decoration = 2; break;\n            case 221:\n                self->sgr.bold = false; break;\n            case 222:\n                self->sgr.dim = false; break;\n            case 22:\n                self->sgr.bold = false;  self->sgr.dim = false; break;\n            case 23:\n                self->sgr.italic = false;  break;\n            case 24:\n                self->sgr.decoration = 0;  break;\n            case 25:\n                self->sgr.blink = false; break;\n            case 27:\n                self->sgr.reverse = false;  break;\n            case 29:\n                self->sgr.strikethrough = false;  break;\n            case 30 ... 37:\n                self->sgr.fg = ((attr - 30) << 8) | 1;  break;\n            case 38:\n                SET_COLOR(sgr.fg);\n            case 39:\n                self->sgr.fg = 0;  break;\n            case 40 ... 47:\n                self->sgr.bg = ((attr - 40) << 8) | 1;  break;\n            case 48:\n                SET_COLOR(sgr.bg);\n            case 49:\n                self->sgr.bg = 0;  break;\n            case 90 ... 97:\n                self->sgr.fg = ((attr - 90 + 8) << 8) | 1;  break;\n            case 100 ... 107:\n                self->sgr.bg = ((attr - 100 + 8) << 8) | 1;  break;\n            case DECORATION_FG_CODE:\n                SET_COLOR(sgr.decoration_fg);\n            case DECORATION_FG_CODE + 1:\n                self->sgr.decoration_fg = 0; break;\n        }\n        if (is_group) break;\n    }\n#undef SET_COLOR\nEND_ALLOW_CASE_RANGE\n}\n\nvoid\napply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, int *params, unsigned int count, bool is_group) {\n#define RANGE for(unsigned c = 0; c < cell_count; c++, cell++)\n#define SET_COLOR(which) { color_type color = 0; parse_color(params, &i, count, &color); if (color) { RANGE { cell->which = color; }} } break;\n#define SIMPLE(which, val) RANGE { cell->which = (val); } break;\n#define S(which, val) RANGE { cell->attrs.which = (val); } break;\n\n    unsigned int i = 0, attr;\n    if (!count) { params[0] = 0; count = 1; }\n    while (i < count) {\n        GPUCell *cell = first_cell;\n        attr = params[i++];\n        switch(attr) {\n            case 0: {\n                const CellAttrs remove_sgr_mask = {.val=~SGR_MASK};\n                RANGE { cell->attrs.val &= remove_sgr_mask.val; cell->fg = 0; cell->bg = 0; cell->decoration_fg = 0; }\n            }\n                break;\n            case 1:\n                S(bold, true);\n            case 2:\n                S(dim, true);\n            case 3:\n                S(italic, true);\n            case 4: {\n                uint8_t val = 1;\n                if (is_group && i < count) { val = MIN(5, params[i]); i++; }\n                S(decoration, val);\n            }\n            case 5:\n                S(blink, true);\n            case 7:\n                S(reverse, true);\n            case 9:\n                S(strike, true);\n            case 21:\n                S(decoration, 2);\n            case 221:\n                S(bold, false);\n            case 222:\n                S(dim, false);\n            case 22:\n                RANGE { cell->attrs.bold = false; cell->attrs.dim = false; } break;\n            case 23:\n                S(italic, false);\n            case 24:\n                S(decoration, 0);\n            case 25:\n                S(blink, false);\n            case 27:\n                S(reverse, false);\n            case 29:\n                S(strike, false);\nSTART_ALLOW_CASE_RANGE\n            case 30 ... 37:\n                SIMPLE(fg, ((attr - 30) << 8) | 1);\n            case 38:\n                SET_COLOR(fg);\n            case 39:\n                SIMPLE(fg, 0);\n            case 40 ... 47:\n                SIMPLE(bg, ((attr - 40) << 8) | 1);\n            case 48:\n                SET_COLOR(bg);\n            case 49:\n                SIMPLE(bg, 0);\n            case 90 ... 97:\n                SIMPLE(fg, ((attr - 90 + 8) << 8) | 1);\n            case 100 ... 107:\n                SIMPLE(bg, ((attr - 100 + 8) << 8) | 1);\nEND_ALLOW_CASE_RANGE\n            case DECORATION_FG_CODE:\n                SET_COLOR(decoration_fg);\n            case DECORATION_FG_CODE + 1:\n                SIMPLE(decoration_fg, 0);\n        }\n        if (is_group) break;\n    }\n#undef SET_COLOR\n#undef RANGE\n#undef SIMPLE\n#undef S\n}\n\nconst char*\ncursor_as_sgr(const Cursor *self) {\n    GPUCell blank_cell = { 0 }, cursor_cell = {\n        .attrs = cursor_to_attrs(self),\n        .fg = self->sgr.fg & COL_MASK,\n        .bg = self->sgr.bg & COL_MASK,\n        .decoration_fg = self->sgr.decoration_fg & COL_MASK,\n    };\n    return cell_as_sgr(&cursor_cell, &blank_cell);\n}\n\nstatic PyObject *\nreset_display_attrs(Cursor *self, PyObject *a UNUSED) {\n#define reset_display_attrs_doc \"Reset all display attributes to unset\"\n    cursor_reset_display_attrs(self);\n    Py_RETURN_NONE;\n}\n\nvoid cursor_reset(Cursor *self) {\n    cursor_reset_display_attrs(self);\n    self->x = 0; self->y = 0;\n    self->shape = NO_CURSOR_SHAPE; self->non_blinking = false;\n}\n\nvoid cursor_copy_to(Cursor *src, Cursor *dest) {\n#define CCY(x) dest->x = src->x;\n    CCY(x); CCY(y); CCY(shape); CCY(non_blinking);\n#undef CCY\n    memcpy(&dest->sgr, &src->sgr, sizeof(dest->sgr));\n}\n\nstatic PyObject*\ncopy(Cursor *self, PyObject*);\n#define copy_doc \"Create a clone of this cursor\"\n\n// Boilerplate {{{\n\n#define SGR_BOOL_GETSET(x) \\\n    static PyObject* x##_get(Cursor *self, void UNUSED *closure) { PyObject *ans = self->sgr.x ? Py_True : Py_False; Py_INCREF(ans); return ans; } \\\n    static int x##_set(Cursor *self, PyObject *value, void UNUSED *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, \"Cannot delete attribute\"); return -1; } self->sgr.x = PyObject_IsTrue(value) ? true : false; return 0; }\n\nSGR_BOOL_GETSET(bold)\nSGR_BOOL_GETSET(italic)\nSGR_BOOL_GETSET(reverse)\nSGR_BOOL_GETSET(strikethrough)\nSGR_BOOL_GETSET(dim)\n#undef SGR_BOOL_GETSET\n\nstatic PyObject* blink_get(Cursor *self, void UNUSED *closure) { PyObject *ans = !self->non_blinking ? Py_True : Py_False; Py_INCREF(ans); return ans; }\n\nstatic int blink_set(Cursor *self, PyObject *value, void UNUSED *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, \"Cannot delete attribute\"); return -1; } self->non_blinking = PyObject_IsTrue(value) ? false : true; return 0; }\n\nstatic PyObject* text_blink_get(Cursor *self, void UNUSED *closure) { return Py_NewRef(self->sgr.blink ? Py_True : Py_False); }\n\nstatic int text_blink_set(Cursor *self, PyObject *value, void UNUSED *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, \"Cannot delete attribute\"); return -1; } self->sgr.blink = PyObject_IsTrue(value) ? false : true; return 0; }\n\n\n#define SGR_UINT_GETSET(x) \\\n    static PyObject* x##_get(Cursor *self, void UNUSED *closure) { return PyLong_FromUnsignedLong((unsigned long)self->sgr.x); } \\\n    static int x##_set(Cursor *self, PyObject *value, void UNUSED *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, \"Cannot delete attribute\"); return -1; } if (!PyLong_Check(value)) { PyErr_SetString(PyExc_TypeError, \"value must be an int\"); return -1; } self->sgr.x = PyLong_AsUnsignedLong(value); return 0; }\n\nSGR_UINT_GETSET(decoration)\nSGR_UINT_GETSET(fg)\nSGR_UINT_GETSET(bg)\nSGR_UINT_GETSET(decoration_fg)\n\nstatic PyMemberDef members[] = {\n    {\"x\", T_UINT, offsetof(Cursor, x), 0, \"x\"},\n    {\"y\", T_UINT, offsetof(Cursor, y), 0, \"y\"},\n    {\"shape\", T_INT, offsetof(Cursor, shape), 0, \"shape\"},\n    {NULL}  /* Sentinel */\n};\n\nstatic PyGetSetDef getseters[] = {\n    GETSET(bold)\n    GETSET(italic)\n    GETSET(reverse)\n    GETSET(strikethrough)\n    GETSET(dim)\n    GETSET(blink)\n    GETSET(text_blink)\n    GETSET(decoration)\n    GETSET(fg)\n    GETSET(bg)\n    GETSET(decoration_fg)\n    {NULL}  /* Sentinel */\n};\n\nstatic PyMethodDef methods[] = {\n    METHOD(copy, METH_NOARGS)\n    METHOD(reset_display_attrs, METH_NOARGS)\n    {NULL}  /* Sentinel */\n};\n\n\nstatic PyObject *\nrichcmp(PyObject *obj1, PyObject *obj2, int op);\n\nPyTypeObject Cursor_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.Cursor\",\n    .tp_basicsize = sizeof(Cursor),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_repr = (reprfunc)repr,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Cursors\",\n    .tp_richcompare = richcmp,\n    .tp_methods = methods,\n    .tp_members = members,\n    .tp_getset = getseters,\n    .tp_new = new_cursor_object,\n};\n\nRICHCMP(Cursor)\n\n// }}}\n\nCursor*\ncursor_copy(Cursor *self) {\n    Cursor* ans;\n    ans = alloc_cursor();\n    if (ans == NULL) { PyErr_NoMemory(); return NULL; }\n    cursor_copy_to(self, ans);\n    return ans;\n}\n\nstatic PyObject*\ncopy(Cursor *self, PyObject *a UNUSED) {\n    return (PyObject*)cursor_copy(self);\n}\n\nCursor *alloc_cursor(void) {\n    return (Cursor*)new_cursor_object(&Cursor_Type, NULL, NULL);\n}\n\nINIT_TYPE(Cursor)\n"
  },
  {
    "path": "kitty/cursor_trail.c",
    "content": "#include <float.h>\n#include \"state.h\"\n\n#define WD w->render_data\n#define EDGE(axis, index) ct->cursor_edge_##axis[index]\n\ninline static float\nnorm(float x, float y) {\n    return sqrtf(x * x + y * y);\n}\n\ntypedef struct ndc_coords { float xstart, ystart, dx, dy; } ndc_coords;\n\nstatic void\nupdate_cursor_trail_target(CursorTrail *ct, Window *w, ndc_coords g) {\n    float left = FLT_MAX, right = FLT_MAX, top = FLT_MAX, bottom = FLT_MAX;\n    switch (WD.screen->cursor_render_info.shape) {\n        case CURSOR_BLOCK:\n        case CURSOR_HOLLOW:\n        case CURSOR_BEAM:\n        case CURSOR_UNDERLINE:\n            left = g.xstart + WD.screen->cursor_render_info.x * g.dx;\n            bottom = g.ystart - (WD.screen->cursor_render_info.y + 1) * g.dy;\n        default:\n            break;\n    }\n    switch (WD.screen->cursor_render_info.shape) {\n        case CURSOR_BLOCK:\n        case CURSOR_HOLLOW:\n            right = left + g.dx;\n            top = bottom + g.dy;\n            break;\n        case CURSOR_BEAM:\n            right = left + g.dx / WD.screen->cell_size.width * OPT(cursor_beam_thickness);\n            top = bottom + g.dy;\n            break;\n        case CURSOR_UNDERLINE:\n            right = left + g.dx;\n            top = bottom + g.dy / WD.screen->cell_size.height * OPT(cursor_underline_thickness);\n            break;\n        default:\n            break;\n    }\n    if (left != FLT_MAX) {\n        EDGE(x, 0) = left;\n        EDGE(x, 1) = right;\n        EDGE(y, 0) = top;\n        EDGE(y, 1) = bottom;\n    }\n}\n\nstatic bool\nshould_skip_cursor_trail_update(CursorTrail *ct, ndc_coords g, OSWindow *os_window) {\n    if (os_window->live_resize.in_progress) {\n        return true;\n    }\n\n    if (OPT(cursor_trail_start_threshold) > 0 && !ct->needs_render) {\n        int dx = (int)round((ct->corner_x[0] - EDGE(x, 1)) / g.dx);\n        int dy = (int)round((ct->corner_y[0] - EDGE(y, 0)) / g.dy);\n        if (abs(dx) + abs(dy) <= OPT(cursor_trail_start_threshold)) {\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic void\nupdate_cursor_trail_corners(CursorTrail *ct, ndc_coords g, monotonic_t now, OSWindow *os_window) {\n    // the trail corners move towards the cursor corner at a speed proportional to their distance from the cursor corner.\n    // equivalent to exponential ease out animation.\n    static const int corner_index[2][4] = {{1, 1, 0, 0}, {0, 1, 1, 0}};\n\n    // the decay time for the trail to reach 1/1024 of its distance from the cursor corner\n    float decay_fast = OPT(cursor_trail_decay_fast);\n    float decay_slow = OPT(cursor_trail_decay_slow);\n\n    if (should_skip_cursor_trail_update(ct, g, os_window)) {\n        for (int i = 0; i < 4; ++i) {\n            ct->corner_x[i] = EDGE(x, corner_index[0][i]);\n            ct->corner_y[i] = EDGE(y, corner_index[1][i]);\n        }\n    }\n    else if (ct->updated_at < now) {\n        float cursor_center_x = (EDGE(x, 0) + EDGE(x, 1)) * 0.5f;\n        float cursor_center_y = (EDGE(y, 0) + EDGE(y, 1)) * 0.5f;\n        float cursor_diag_2 = norm(EDGE(x, 1) - EDGE(x, 0), EDGE(y, 1) - EDGE(y, 0)) * 0.5f;\n        float dt = (float)monotonic_t_to_s_double(now - ct->updated_at);\n\n        // dot product here is used to dynamically adjust the decay speed of\n        // each corner. The closer the corner is to the cursor, the faster it\n        // moves.\n        float dx[4], dy[4];\n        float dot[4];  // dot product of \"direction vector\" and \"cursor center to corner vector\"\n        for (int i = 0; i < 4; ++i) {\n            dx[i] = EDGE(x, corner_index[0][i]) - ct->corner_x[i];\n            dy[i] = EDGE(y, corner_index[1][i]) - ct->corner_y[i];\n            if (fabsf(dx[i]) < 1e-6 && fabsf(dy[i]) < 1e-6) {\n                dx[i] = dy[i] = 0.0f;\n                dot[i] = 0.0f;\n                continue;\n            }\n            dot[i] = (dx[i] * (EDGE(x, corner_index[0][i]) - cursor_center_x) +\n                      dy[i] * (EDGE(y, corner_index[1][i]) - cursor_center_y)) /\n                     cursor_diag_2 / norm(dx[i], dy[i]);\n        }\n        float min_dot = FLT_MAX, max_dot = -FLT_MAX;\n        for (int i = 0; i < 4; ++i) {\n            min_dot = fminf(min_dot, dot[i]);\n            max_dot = fmaxf(max_dot, dot[i]);\n        }\n\n        for (int i = 0; i < 4; ++i) {\n            if ((dx[i] == 0 && dy[i] == 0) || min_dot == FLT_MAX) {\n                continue;\n            }\n\n            float decay = (min_dot == max_dot)\n                ? decay_slow\n                : decay_slow + (decay_fast - decay_slow) * (dot[i] - min_dot) / (max_dot - min_dot);\n            float step = 1.0f - exp2f(-10.0f * dt / decay);\n            ct->corner_x[i] += dx[i] * step;\n            ct->corner_y[i] += dy[i] * step;\n        }\n    }\n}\n\nstatic void\nupdate_cursor_trail_opacity(CursorTrail *ct, Window *w, monotonic_t now) {\n    const bool cursor_trail_always_visible = false;\n    if (cursor_trail_always_visible) {\n        ct->opacity = 1.0f;\n    } else if (WD.screen->modes.mDECTCEM) {\n        ct->opacity += (float)monotonic_t_to_s_double(now - ct->updated_at) / OPT(cursor_trail_decay_slow);\n        ct->opacity = fminf(ct->opacity, 1.0f);\n    } else {\n        ct->opacity -= (float)monotonic_t_to_s_double(now - ct->updated_at) / OPT(cursor_trail_decay_slow);\n        ct->opacity = fmaxf(ct->opacity, 0.0f);\n    }\n}\n\nstatic void\nupdate_cursor_trail_needs_render(CursorTrail *ct, Window *w, ndc_coords g) {\n    static const int corner_index[2][4] = {{1, 1, 0, 0}, {0, 1, 1, 0}};\n    ct->needs_render = false;\n\n    // check if any corner is still far from the cursor corner, so it should be rendered\n    const float dx_threshold = g.dx / WD.screen->cell_size.width * 0.5f;\n    const float dy_threshold = g.dy / WD.screen->cell_size.height * 0.5f;\n    for (int i = 0; i < 4; ++i) {\n        float dx = fabsf(EDGE(x, corner_index[0][i]) - ct->corner_x[i]);\n        float dy = fabsf(EDGE(y, corner_index[1][i]) - ct->corner_y[i]);\n        if (dx_threshold <= dx || dy_threshold <= dy) {\n            ct->needs_render = true;\n            break;\n        }\n    }\n}\n\nbool\nupdate_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_window) {\n    ndc_coords g = {\n        .xstart = gl_pos_x(w->render_data.geometry.left, os_window->viewport_width),\n        .ystart = gl_pos_y(w->render_data.geometry.top, os_window->viewport_height),\n        .dx     = gl_size (w->render_data.screen->cell_size.width, os_window->viewport_width),\n        .dy     = gl_size (w->render_data.screen->cell_size.height, os_window->viewport_height),\n    };\n    if (!WD.screen->paused_rendering.expires_at && OPT(cursor_trail) <= now - WD.screen->cursor->position_changed_by_client_at) {\n        update_cursor_trail_target(ct, w, g);\n    }\n\n    update_cursor_trail_corners(ct, g, now, os_window);\n    update_cursor_trail_opacity(ct, w, now);\n\n    bool needs_render_prev = ct->needs_render;\n    update_cursor_trail_needs_render(ct, w, g);\n\n    ct->updated_at = now;\n\n    // returning true here will cause the cells to be drawn\n    return ct->needs_render || needs_render_prev;\n}\n\n#undef WD\n#undef EDGE\n"
  },
  {
    "path": "kitty/data-types.c",
    "content": "/*\n * data-types.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#ifdef __APPLE__\n// Needed for _CS_DARWIN_USER_CACHE_DIR\n#define _DARWIN_C_SOURCE\n#include <unistd.h>\n#undef _DARWIN_C_SOURCE\n#endif\n\n#include \"char-props.h\"\n#include \"launcher/utils.h\"\n#include \"line.h\"\n#include \"charsets.h\"\n#include \"base64.h\"\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include \"cleanup.h\"\n#include \"safe-wrappers.h\"\n#include \"control-codes.h\"\n#include \"wcswidth.h\"\n#include \"modes.h\"\n#include <stddef.h>\n#include <termios.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <locale.h>\n\n#ifdef WITH_PROFILER\n#include <gperftools/profiler.h>\n#endif\n\n#include \"monotonic.h\"\n\n#ifdef __APPLE__\n#include <libproc.h>\n#include <xlocale.h>\n\nstatic PyObject*\nuser_cache_dir(PyObject *self UNUSED, PyObject *args UNUSED) {\n    static char buf[1024];\n    if (!confstr(_CS_DARWIN_USER_CACHE_DIR, buf, sizeof(buf) - 1)) return PyErr_SetFromErrno(PyExc_OSError);\n    return PyUnicode_FromString(buf);\n}\n\nstatic PyObject*\nprocess_group_map(PyObject *self UNUSED, PyObject *args UNUSED) {\n    int num_of_processes = proc_listallpids(NULL, 0);\n    size_t bufsize = sizeof(pid_t) * (num_of_processes + 1024);\n    RAII_ALLOC(pid_t, buf, malloc(bufsize));\n    if (!buf) return PyErr_NoMemory();\n    num_of_processes = proc_listallpids(buf, (int)bufsize);\n    PyObject *ans = PyTuple_New(num_of_processes);\n    if (ans == NULL) { return PyErr_NoMemory(); }\n    for (int i = 0; i < num_of_processes; i++) {\n        long pid = buf[i], pgid = getpgid(buf[i]);\n        PyObject *t = Py_BuildValue(\"ll\", pid, pgid);\n        if (t == NULL) { Py_DECREF(ans); return NULL; }\n        PyTuple_SET_ITEM(ans, i, t);\n    }\n    return ans;\n}\n#endif\n\nstatic PyObject*\npybase64_encode(PyObject UNUSED *self, PyObject *const *args, Py_ssize_t nargs) {\n    int add_padding = 0;\n    if (nargs < 1 || nargs > 2) { PyErr_SetString(PyExc_TypeError, \"must supply one or two arguments\"); return NULL; }\n    RAII_PY_BUFFER(view);\n    if (PyUnicode_Check(args[0])) view.buf = (void*)PyUnicode_AsUTF8AndSize(args[0], &view.len);\n    else if (PyObject_GetBuffer(args[0], &view, PyBUF_SIMPLE) != 0) return NULL;\n    if (nargs == 2) add_padding = PyObject_IsTrue(args[1]);\n    size_t sz = required_buffer_size_for_base64_encode(view.len);\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, sz);\n    if (!ans) return NULL;\n    base64_encode8(view.buf, view.len, (unsigned char*)PyBytes_AS_STRING(ans), &sz, add_padding);\n    if (_PyBytes_Resize(&ans, sz) != 0) return NULL;\n    return ans;\n}\n\nstatic PyObject*\nbase64_encode_into(PyObject UNUSED *self, PyObject *args) {\n    int add_padding = 0;\n    RAII_PY_BUFFER(view); RAII_PY_BUFFER(output);\n    if (!PyArg_ParseTuple(args, \"s*w*|i\", &view, &output, &add_padding)) return NULL;\n    size_t sz = required_buffer_size_for_base64_encode(view.len);\n    if (output.len < (ssize_t)sz) { PyErr_SetString(PyExc_TypeError, \"output buffer too small\"); return NULL; }\n    base64_encode8(view.buf, view.len, output.buf, &sz, add_padding);\n    return PyLong_FromSize_t(sz);\n}\n\nstatic PyObject*\npybase64_decode(PyObject UNUSED *self, PyObject *input_data) {\n    RAII_PY_BUFFER(view);\n    if (PyUnicode_Check(input_data)) view.buf = (void*)PyUnicode_AsUTF8AndSize(input_data, &view.len);\n    else if (PyObject_GetBuffer(input_data, &view, PyBUF_SIMPLE) != 0) return NULL;\n    size_t sz = required_buffer_size_for_base64_decode(view.len);\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, sz);\n    if (!ans) return NULL;\n    if (!base64_decode8(view.buf, view.len, (unsigned char*)PyBytes_AS_STRING(ans), &sz)) {\n        Py_DECREF(ans);\n        PyErr_SetString(PyExc_ValueError, \"Invalid base64 input data\");\n        return NULL;\n    }\n    if (_PyBytes_Resize(&ans, sz) != 0) return NULL;\n    return ans;\n}\n\nstatic PyObject*\nbase64_decode_into(PyObject UNUSED *self, PyObject *args) {\n    RAII_PY_BUFFER(view); RAII_PY_BUFFER(output);\n    if (!PyArg_ParseTuple(args, \"s*w*\", &view, &output)) return NULL;\n    size_t sz = required_buffer_size_for_base64_decode(view.len);\n    if (output.len < (ssize_t)sz) { PyErr_SetString(PyExc_TypeError, \"output buffer too small\"); return NULL; }\n    if (!base64_decode8(view.buf, view.len, output.buf, &sz)) {\n        PyErr_SetString(PyExc_ValueError, \"Invalid base64 input data\");\n        return NULL;\n    }\n    return PyLong_FromSize_t(sz);\n}\n\nstatic PyObject*\nsplit_into_graphemes(PyObject UNUSED *self, PyObject *src) {\n    if (!PyUnicode_Check(src)) { PyErr_SetString(PyExc_TypeError, \"must provide a unicode string\"); return NULL; }\n    int kind = PyUnicode_KIND(src); char *data = PyUnicode_DATA(src);\n    RAII_PyObject(ans, PyList_New(0));\n    if (!ans) return NULL;\n    GraphemeSegmentationResult s; grapheme_segmentation_reset(&s);\n    Py_ssize_t pos = 0;\n    for (Py_ssize_t i = 0; i < PyUnicode_GET_LENGTH(src); i++) {\n        char_type ch = PyUnicode_READ(kind, data, i);\n        if (!(s = grapheme_segmentation_step(s, char_props_for(ch))).add_to_current_cell) {\n            RAII_PyObject(u, PyUnicode_FromKindAndData(kind, data + kind * pos, i - pos));\n            if (!u || PyList_Append(ans, u) != 0) return NULL;\n            pos = i;\n        }\n    }\n    if (pos < PyUnicode_GET_LENGTH(src)) {\n        RAII_PyObject(u, PyUnicode_FromKindAndData(kind, data + kind * pos, PyUnicode_GET_LENGTH(src) - pos));\n        if (!u || PyList_Append(ans, u) != 0) return NULL;\n    }\n    return Py_NewRef(ans);\n}\n\ntypedef struct StreamingBase64Decoder {\n    PyObject_HEAD\n    struct base64_state state;\n    bool add_trailing_bytes, needs_more_data;\n} StreamingBase64Decoder;\n\nstatic void\nStreamingBase64Decoder_reset_(StreamingBase64Decoder *self) {\n    base64_stream_decode_init(&self->state, 0);\n    self->needs_more_data = false;\n}\n\nstatic int\nStreamingBase64Decoder_init(PyObject *s, PyObject *args, PyObject *kwds UNUSED) {\n    if (PyTuple_GET_SIZE(args)) { PyErr_SetString(PyExc_TypeError, \"constructor takes no arguments\"); return -1; }\n    StreamingBase64Decoder *self = (StreamingBase64Decoder*)s;\n\tbase64_stream_decode_init(&self->state, 0);\n    return 0;\n}\n\nstatic PyObject*\nStreamingBase64Decoder_decode(StreamingBase64Decoder *self, PyObject *a) {\n    RAII_PY_BUFFER(data);\n    if (PyObject_GetBuffer(a, &data, PyBUF_SIMPLE) != 0) return NULL;\n    if (!data.buf || !data.len) return PyBytes_FromStringAndSize(NULL, 0);\n    size_t sz = required_buffer_size_for_base64_decode(data.len);\n    RAII_PyObject(ans, PyBytes_FromStringAndSize(NULL, sz));\n    if (!ans) return NULL;\n    int ret;\n    Py_BEGIN_ALLOW_THREADS\n    ret = base64_stream_decode(&self->state, data.buf, data.len, PyBytes_AS_STRING(ans), &sz);\n    Py_END_ALLOW_THREADS;\n    if (!ret) {\n        StreamingBase64Decoder_reset_(self);\n        PyErr_SetString(PyExc_ValueError, \"Invalid base64 input data\");\n        return NULL;\n    }\n    if (self->state.eof) StreamingBase64Decoder_reset_(self);\n    else self->needs_more_data = self->state.carry != 0 || self->state.bytes != 0;\n    if (_PyBytes_Resize(&ans, sz) != 0) return NULL;\n    return Py_NewRef(ans);\n}\n\nstatic PyObject*\nStreamingBase64Decoder_decode_into(StreamingBase64Decoder *self, PyObject *const *args, Py_ssize_t nargs) {\n    if (nargs != 2) { PyErr_SetString(PyExc_TypeError, \"constructor takes exactly two arguments\"); return NULL; }\n    RAII_PY_BUFFER(data);\n    if (PyObject_GetBuffer(args[0], &data, PyBUF_WRITE) != 0) return NULL;\n    if (!data.buf || !data.len) return PyLong_FromLong(0);\n    RAII_PY_BUFFER(src);\n    if (PyObject_GetBuffer(args[1], &src, PyBUF_SIMPLE) != 0) return NULL;\n    if (!src.buf || !src.len) return PyLong_FromLong(0);\n    size_t sz = required_buffer_size_for_base64_decode(src.len);\n    if ((Py_ssize_t)sz > data.len) { PyErr_SetString(PyExc_BufferError, \"output buffer too small\"); return NULL; }\n    int ret;\n    Py_BEGIN_ALLOW_THREADS\n    ret = base64_stream_decode(&self->state, src.buf, src.len, data.buf, &sz);\n    Py_END_ALLOW_THREADS\n    if (!ret) {\n        StreamingBase64Decoder_reset_(self);\n        PyErr_SetString(PyExc_ValueError, \"Invalid base64 input data\");\n        return NULL;\n    }\n    if (self->state.eof) StreamingBase64Decoder_reset_(self); else self->needs_more_data = true;\n    return PyLong_FromSize_t(sz);\n}\n\nstatic PyObject*\nStreamingBase64Decoder_reset(StreamingBase64Decoder *self, PyObject *args UNUSED) {\n    StreamingBase64Decoder_reset_(self);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nStreamingBase64Decoder_needs_more_data(StreamingBase64Decoder *self, PyObject *args UNUSED) {\n    return Py_NewRef(self->needs_more_data ? Py_True : Py_False);\n}\n\nstatic PyTypeObject StreamingBase64Decoder_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"kitty.fast_data_types.StreamingBase64Decoder\",\n    .tp_basicsize = sizeof(StreamingBase64Decoder),\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"StreamingBase64Decoder\",\n    .tp_methods = (PyMethodDef[]){\n        {\"decode\", (PyCFunction)StreamingBase64Decoder_decode, METH_O, \"\"},\n        {\"decode_into\", (PyCFunction)(void(*)(void))StreamingBase64Decoder_decode_into, METH_FASTCALL, \"\"},\n        {\"reset\", (PyCFunction)StreamingBase64Decoder_reset, METH_NOARGS, \"\"},\n        {\"needs_more_data\", (PyCFunction)StreamingBase64Decoder_needs_more_data, METH_NOARGS, \"\"},\n        {NULL, NULL, 0, NULL},\n    },\n    .tp_new = PyType_GenericNew,\n    .tp_init = StreamingBase64Decoder_init,\n};\n\nstatic int\nStreamingBase64Encoder_init(PyObject *s, PyObject *args, PyObject *kwds UNUSED) {\n    StreamingBase64Decoder *self = (StreamingBase64Decoder*)s;\n    self->add_trailing_bytes = true;\n    switch (PyTuple_GET_SIZE(args)) {\n        case 0: break;\n        case 1: self->add_trailing_bytes = PyObject_IsTrue(PyTuple_GET_ITEM(args, 0)); break;\n        default: PyErr_SetString(PyExc_TypeError, \"constructor takes no more than one argument\"); return -1;\n    }\n\tbase64_stream_encode_init(&self->state, 0);\n    return 0;\n}\n\nstatic PyObject*\nStreamingBase64Encoder_encode(StreamingBase64Decoder *self, PyObject *a) {\n    RAII_PY_BUFFER(data);\n    if (PyObject_GetBuffer(a, &data, PyBUF_SIMPLE) != 0) return NULL;\n    if (!data.buf || !data.len) return PyBytes_FromStringAndSize(NULL, 0);\n    size_t sz = required_buffer_size_for_base64_encode(data.len);\n    RAII_PyObject(ans, PyBytes_FromStringAndSize(NULL, sz));\n    if (!ans) return NULL;\n    Py_BEGIN_ALLOW_THREADS\n    base64_stream_encode(&self->state, data.buf, data.len, PyBytes_AS_STRING(ans), &sz);\n    Py_END_ALLOW_THREADS\n    if (_PyBytes_Resize(&ans, sz) != 0) return NULL;\n    return Py_NewRef(ans);\n}\n\nstatic PyObject*\nStreamingBase64Encoder_encode_into(StreamingBase64Decoder *self, PyObject *const *args, Py_ssize_t nargs) {\n    if (nargs != 2) { PyErr_SetString(PyExc_TypeError, \"constructor takes exactly two arguments\"); return NULL; }\n    RAII_PY_BUFFER(data);\n    if (PyObject_GetBuffer(args[0], &data, PyBUF_WRITE) != 0) return NULL;\n    if (!data.buf || !data.len) return PyLong_FromLong(0);\n    RAII_PY_BUFFER(src);\n    if (PyObject_GetBuffer(args[1], &src, PyBUF_SIMPLE) != 0) return NULL;\n    if (!src.buf || !src.len) return PyLong_FromLong(0);\n    size_t sz = required_buffer_size_for_base64_encode(src.len);\n    if ((Py_ssize_t)sz > data.len) { PyErr_SetString(PyExc_BufferError, \"output buffer too small\"); return NULL; }\n    Py_BEGIN_ALLOW_THREADS\n    base64_stream_encode(&self->state, src.buf, src.len, data.buf, &sz);\n    Py_END_ALLOW_THREADS\n    return PyLong_FromSize_t(sz);\n}\n\nstatic PyObject*\nStreamingBase64Encoder_reset(StreamingBase64Decoder *self, PyObject *args UNUSED) {\n    char trailer[4];\n    size_t sz;\n    base64_stream_encode_final(&self->state, trailer, &sz);\n    base64_stream_encode_init(&self->state, 0);\n    if (!self->add_trailing_bytes) { while(sz && trailer[sz-1] == '=') sz--; }\n    return PyBytes_FromStringAndSize(trailer, sz);\n}\n\nstatic PyTypeObject StreamingBase64Encoder_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"kitty.fast_data_types.StreamingBase64Encoder\",\n    .tp_basicsize = sizeof(StreamingBase64Decoder),\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"StreamingBase64Encoder\",\n    .tp_methods = (PyMethodDef[]){\n        {\"encode\", (PyCFunction)StreamingBase64Encoder_encode, METH_O, \"\"},\n        {\"encode_into\", (PyCFunction)(void(*)(void))StreamingBase64Encoder_encode_into, METH_FASTCALL, \"\"},\n        {\"reset\", (PyCFunction)StreamingBase64Encoder_reset, METH_NOARGS, \"\"},\n        {NULL, NULL, 0, NULL},\n    },\n    .tp_new = PyType_GenericNew,\n    .tp_init = StreamingBase64Encoder_init,\n};\n\n\nstatic PyObject*\npyset_iutf8(PyObject UNUSED *self, PyObject *args) {\n    int fd, on;\n    if (!PyArg_ParseTuple(args, \"ip\", &fd, &on)) return NULL;\n    if (!set_iutf8(fd, on & 1)) return PyErr_SetFromErrno(PyExc_OSError);\n    Py_RETURN_NONE;\n}\n\n#ifdef WITH_PROFILER\nstatic PyObject*\nstart_profiler(PyObject UNUSED *self, PyObject *args) {\n    char *path;\n    if (!PyArg_ParseTuple(args, \"s\", &path)) return NULL;\n    ProfilerStart(path);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nstop_profiler(PyObject UNUSED *self, PyObject *args UNUSED) {\n    ProfilerStop();\n    Py_RETURN_NONE;\n}\n#endif\n\nstatic bool\nput_tty_in_raw_mode(int fd, const struct termios* termios_p, bool read_with_timeout, int optional_actions) {\n    struct termios raw_termios = *termios_p;\n    cfmakeraw(&raw_termios);\n    if (read_with_timeout) {\n        raw_termios.c_cc[VMIN] = 0; raw_termios.c_cc[VTIME] = 1;\n    } else {\n        raw_termios.c_cc[VMIN] = 1; raw_termios.c_cc[VTIME] = 0;\n    }\n    if (tcsetattr(fd, optional_actions, &raw_termios) != 0) { PyErr_SetFromErrno(PyExc_OSError); return false; }\n    return true;\n}\n\nstatic PyObject*\nopen_tty(PyObject *self UNUSED, PyObject *args) {\n    int read_with_timeout = 0, optional_actions = TCSAFLUSH;\n    if (!PyArg_ParseTuple(args, \"|pi\", &read_with_timeout, &optional_actions)) return NULL;\n    int flags = O_RDWR | O_CLOEXEC | O_NOCTTY;\n    if (!read_with_timeout) flags |= O_NONBLOCK;\n    static char ctty[L_ctermid+1];\n    int fd = safe_open(ctermid(ctty), flags, 0);\n    if (fd == -1) { PyErr_Format(PyExc_OSError, \"Failed to open controlling terminal: %s (identified with ctermid()) with error: %s\", ctty, strerror(errno)); return NULL; }\n    struct termios *termios_p = calloc(1, sizeof(struct termios));\n    if (!termios_p) return PyErr_NoMemory();\n    if (tcgetattr(fd, termios_p) != 0) { free(termios_p); PyErr_SetFromErrno(PyExc_OSError); return NULL; }\n    if (!put_tty_in_raw_mode(fd, termios_p, read_with_timeout != 0, optional_actions)) { free(termios_p); return NULL; }\n    return Py_BuildValue(\"iN\", fd, PyLong_FromVoidPtr(termios_p));\n}\n\n#define TTY_ARGS \\\n    PyObject *tp; int fd; int optional_actions = TCSAFLUSH; \\\n    if (!PyArg_ParseTuple(args, \"iO!|i\", &fd, &PyLong_Type, &tp, &optional_actions)) return NULL; \\\n    struct termios *termios_p = PyLong_AsVoidPtr(tp);\n\nstatic PyObject*\nnormal_tty(PyObject *self UNUSED, PyObject *args) {\n    TTY_ARGS\n    if (tcsetattr(fd, optional_actions, termios_p) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nraw_tty(PyObject *self UNUSED, PyObject *args) {\n    TTY_ARGS\n    if (!put_tty_in_raw_mode(fd, termios_p, false, optional_actions)) return NULL;\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\nclose_tty(PyObject *self UNUSED, PyObject *args) {\n    TTY_ARGS\n    tcsetattr(fd, optional_actions, termios_p);  // deliberately ignore failure\n    free(termios_p);\n    safe_close(fd, __FILE__, __LINE__);\n    Py_RETURN_NONE;\n}\n\n#undef TTY_ARGS\n\nstatic PyObject*\npy_shm_open(PyObject UNUSED *self, PyObject *args) {\n    char *name;\n    int flags, mode = 0600;\n    if (!PyArg_ParseTuple(args, \"si|i\", &name, &flags, &mode)) return NULL;\n    long fd = safe_shm_open(name, flags, mode);\n    if (fd < 0) return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, PyTuple_GET_ITEM(args, 0));\n    return PyLong_FromLong(fd);\n}\n\nstatic PyObject*\npy_shm_unlink(PyObject UNUSED *self, PyObject *args) {\n    char *name;\n    if (!PyArg_ParseTuple(args, \"s\", &name)) return NULL;\n    if (shm_unlink(name) != 0) return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, PyTuple_GET_ITEM(args, 0));\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nwcwidth_wrap(PyObject UNUSED *self, PyObject *chr) {\n    return PyLong_FromLong(wcwidth_std(char_props_for(PyLong_AsLong(chr))));\n}\n\nstatic PyObject*\nlocale_is_valid(PyObject *self UNUSED, PyObject *args) {\n    char *name;\n    if (!PyArg_ParseTuple(args, \"s\", &name)) return NULL;\n    locale_t test_locale = newlocale(LC_ALL_MASK, name, NULL);\n    if (!test_locale) { Py_RETURN_FALSE; }\n    freelocale(test_locale);\n    Py_RETURN_TRUE;\n}\n\n#include \"docs_ref_map_generated.h\"\n\nstatic PyObject*\nget_docs_ref_map(PyObject *self UNUSED, PyObject *args UNUSED) {\n    return PyBytes_FromStringAndSize(docs_ref_map, sizeof(docs_ref_map));\n}\n\nstatic PyObject*\nwrapped_kittens(PyObject *self UNUSED, PyObject *args UNUSED) {\n    static const char *wrapped_kitten_names = WRAPPED_KITTENS;\n    PyObject *ans = PyUnicode_FromString(wrapped_kitten_names);\n    if (ans == NULL) return NULL;\n    PyObject *s = PyUnicode_Split(ans, NULL, -1);\n    Py_DECREF(ans);\n    return s;\n}\n\nstatic PyObject*\nexpand_ansi_c_escapes(PyObject *self UNUSED, PyObject *src) {\n    enum { NORMAL, PREV_ESC, HEX_DIGIT, OCT_DIGIT, CONTROL_CHAR } state = NORMAL;\n    if (PyUnicode_READY(src) != 0) return NULL;\n    int max_num_hex_digits = 0, hex_digit_idx = 0;\n    char hex_digits[16];\n    Py_ssize_t idx = 0, dest_idx = 0;\n    PyObject *dest = PyUnicode_New(PyUnicode_GET_LENGTH(src)*2, 1114111);\n    if (dest == NULL) return NULL;\n    const int kind = PyUnicode_KIND(src), dest_kind = PyUnicode_KIND(dest);\n    const void *data = PyUnicode_DATA(src), *dest_data = PyUnicode_DATA(dest);\n#define w(ch) { PyUnicode_WRITE(dest_kind, dest_data, dest_idx, ch); dest_idx++; }\n#define write_digits(base) { hex_digits[hex_digit_idx] = 0; if (hex_digit_idx > 0) w(strtol(hex_digits, NULL, base)); hex_digit_idx = 0; state = NORMAL; }\n#define add_digit(base) { hex_digits[hex_digit_idx++] = ch; if (idx >= PyUnicode_GET_LENGTH(src)) write_digits(base); }\n    START_ALLOW_CASE_RANGE\n    while (idx < PyUnicode_GET_LENGTH(src)) {\n        Py_UCS4 ch = PyUnicode_READ(kind, data, idx); idx++;\n        switch(state) {\n            case NORMAL: {\n                if (ch == '\\\\' && idx < PyUnicode_GET_LENGTH(src)) {\n                    state = PREV_ESC;\n                    continue;\n                }\n                w(ch);\n            } break;\n            case CONTROL_CHAR: w(ch & 0x1f); state = NORMAL; break;\n            case HEX_DIGIT: {\n                if (hex_digit_idx < max_num_hex_digits && (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F'))) add_digit(16)\n                else { write_digits(16); idx--; }\n            }; break;\n            case OCT_DIGIT: {\n                if ('0' <= ch && ch <= '7' && hex_digit_idx < max_num_hex_digits) add_digit(16)\n                else { write_digits(8); idx--; }\n            }; break;\n            case PREV_ESC: {\n                state = NORMAL;\n                switch(ch) {\n                    default: w('\\\\'); w(ch); break;\n                    case 'a': w(7); break;\n                    case 'b': w(8); break;\n                    case 'c': if (idx < PyUnicode_GET_LENGTH(src)) {state = CONTROL_CHAR;} else {w('\\\\'); w(ch);}; break;\n                    case 'e': case 'E': w(27); break;\n                    case 'f': w(12); break;\n                    case 'n': w(10); break;\n                    case 'r': w(13); break;\n                    case 't': w(9); break;\n                    case 'v': w(11); break;\n                    case 'x': max_num_hex_digits = 2; hex_digit_idx = 0; state = HEX_DIGIT; break;\n                    case 'u': max_num_hex_digits = 4; hex_digit_idx = 0; state = HEX_DIGIT; break;\n                    case 'U': max_num_hex_digits = 8; hex_digit_idx = 0; state = HEX_DIGIT; break;\n                    case '0' ... '7': max_num_hex_digits = 3; hex_digits[0] = ch; hex_digit_idx = 1; state = OCT_DIGIT; break;\n                    case '\\\\': w('\\\\'); break;\n                    case '?': w('?'); break;\n                    case '\"': w('\"'); break;\n                    case '\\'': w('\\''); break;\n                }\n            } break;\n        }\n    }\n#undef add_digit\n#undef write_digits\n#undef w\n    END_ALLOW_CASE_RANGE\n    PyObject *ans = PyUnicode_FromKindAndData(dest_kind, dest_data, dest_idx);\n    Py_DECREF(dest);\n    return ans;\n}\n\nSTART_ALLOW_CASE_RANGE\nstatic PyObject*\nc0_replace_bytes(const char *input_data, Py_ssize_t input_sz) {\n    RAII_PyObject(ans, PyBytes_FromStringAndSize(NULL, input_sz * 3));\n    if (!ans) return NULL;\n    char *output = PyBytes_AS_STRING(ans);\n    char buf[4];\n    Py_ssize_t j = 0;\n    for (Py_ssize_t i = 0; i < input_sz; i++) {\n        const char x = input_data[i];\n        switch (x) {\n            case C0_EXCEPT_NL_SPACE_TAB: {\n                const uint32_t ch = 0x2400 + x;\n                const unsigned sz = encode_utf8(ch, buf);\n                for (unsigned c = 0; c < sz; c++, j++) output[j] = buf[c];\n            } break;\n            default:\n                output[j++] = x; break;\n        }\n    }\n    if (_PyBytes_Resize(&ans, j) != 0) return NULL;\n    Py_INCREF(ans);\n    return ans;\n}\n\nstatic PyObject*\nc0_replace_unicode(PyObject *input) {\n    RAII_PyObject(ans, PyUnicode_New(PyUnicode_GET_LENGTH(input), 1114111));\n    if (!ans) return NULL;\n    void *input_data = PyUnicode_DATA(input);\n    int input_kind = PyUnicode_KIND(input);\n    void *output_data = PyUnicode_DATA(ans);\n    int output_kind = PyUnicode_KIND(ans);\n    Py_UCS4 maxchar = 0;\n    bool changed = false;\n    for (Py_ssize_t i = 0; i < PyUnicode_GET_LENGTH(input); i++) {\n        Py_UCS4 ch = PyUnicode_READ(input_kind, input_data, i);\n        switch(ch) { case C0_EXCEPT_NL_SPACE_TAB: ch += 0x2400; changed = true; }\n        if (ch > maxchar) maxchar = ch;\n        PyUnicode_WRITE(output_kind, output_data, i, ch);\n    }\n    if (!changed) { Py_INCREF(input); return input; }\n    if (maxchar > 65535) { Py_INCREF(ans); return ans; }\n    RAII_PyObject(ans2, PyUnicode_New(PyUnicode_GET_LENGTH(ans), maxchar));\n    if (!ans2) return NULL;\n    if (PyUnicode_CopyCharacters(ans2, 0, ans, 0, PyUnicode_GET_LENGTH(ans)) == -1) return NULL;\n    Py_INCREF(ans2); return ans2;\n}\nEND_ALLOW_CASE_RANGE\n\nstatic PyObject*\nreplace_c0_codes_except_nl_space_tab(PyObject *self UNUSED, PyObject *obj) {\n    if (PyUnicode_Check(obj)) {\n        return c0_replace_unicode(obj);\n    } else if (PyBytes_Check(obj)) {\n        return c0_replace_bytes(PyBytes_AS_STRING(obj), PyBytes_GET_SIZE(obj));\n    } else if (PyMemoryView_Check(obj)) {\n        Py_buffer *buf = PyMemoryView_GET_BUFFER(obj);\n        return c0_replace_bytes(buf->buf, buf->len);\n    } else if (PyByteArray_Check(obj)) {\n        return c0_replace_bytes(PyByteArray_AS_STRING(obj), PyByteArray_GET_SIZE(obj));\n    } else {\n        PyErr_SetString(PyExc_TypeError, \"Input must be bytes, memoryview, bytearray or unicode\");\n        return NULL;\n    }\n}\n\n\nstatic PyObject*\nfind_in_memoryview(PyObject *self UNUSED, PyObject *args) {\n    unsigned char q;\n    RAII_PY_BUFFER(view);\n    if (!PyArg_ParseTuple(args, \"y*b\", &view, &q)) return NULL;\n    const char *buf = view.buf, *p = memchr(buf, q, view.len);\n    Py_ssize_t ans = -1;\n    if (p) ans = p - buf;\n    return PyLong_FromSsize_t(ans);\n}\n\n#include \"terminfo.h\"\n\nstatic PyObject*\npy_terminfo_data(PyObject *self UNUSED, PyObject *args UNUSED) {\n    return PyBytes_FromStringAndSize((const char*)terminfo_data, arraysz(terminfo_data));\n}\n\nstatic PyObject*\npy_monotonic(PyObject *self UNUSED, PyObject *args UNUSED) {\n    return PyFloat_FromDouble(monotonic_t_to_s_double(monotonic()));\n}\n\nstatic PyObject*\npy_timed_debug_print(PyObject *self UNUSED, PyObject *args) {\n    const char *payload; Py_ssize_t sz;\n    if (!PyArg_ParseTuple(args, \"s#\", &payload, &sz)) return NULL;\n    const char *fmt = \"%.*s\";\n    if (sz && payload[sz-1] == '\\n') { fmt = \"%.*s\\n\"; sz--; }\n    timed_debug_print(fmt, sz, payload);\n    Py_RETURN_NONE;\n}\n\nstatic locale_t c_locale = 0;\nlocale_t get_c_locale(void) { return c_locale; }\n\nstatic PyObject*\npy_run_atexit_cleanup_functions(PyObject *self UNUSED, PyObject *args UNUSED) {\n    run_at_exit_cleanup_functions();\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npy_char_props_for(PyObject *self UNUSED, PyObject *ch) {\n    if (!PyUnicode_Check(ch) || PyUnicode_GET_LENGTH(ch) != 1) { PyErr_SetString(PyExc_TypeError, \"must supply a single character\"); return NULL; }\n    char_type c = PyUnicode_READ_CHAR(ch, 0);\n    CharProps cp = char_props_for(c);\n#define B(x) #x, cp.x ? Py_True : Py_False\n    return Py_BuildValue(\"{si sO sB sB ss sO sO}\",\n        \"width\", wcwidth_std(cp), B(is_extended_pictographic), \"grapheme_break\", cp.grapheme_break,\n        \"indic_conjunct_break\", cp.indic_conjunct_break, \"category\", char_category(cp), B(is_emoji), B(is_emoji_presentation_base)\n    );\n#undef B\n}\n\nstatic PyObject*\nexpanduser(PyObject *self UNUSED, PyObject *path) {\n    if (!PyUnicode_Check(path)) { PyErr_SetString(PyExc_TypeError, \"path must a string\"); return NULL; }\n    char buf[PATH_MAX + 1];\n    expand_tilde(PyUnicode_AsUTF8(path), buf, arraysz(buf));\n    return PyUnicode_FromString(buf);\n}\n\nstatic PyObject*\nabspath(PyObject *self UNUSED, PyObject *path) {\n    if (!PyUnicode_Check(path)) { PyErr_SetString(PyExc_TypeError, \"path must a string\"); return NULL; }\n    char buf[PATH_MAX + 1];\n    lexical_absolute_path(PyUnicode_AsUTF8(path), buf, arraysz(buf));\n    return PyUnicode_FromString(buf);\n}\n\nstatic PyObject*\nread_file(PyObject *self UNUSED, PyObject *path) {\n    if (!PyUnicode_Check(path)) { PyErr_SetString(PyExc_TypeError, \"path must a string\"); return NULL; }\n    size_t sz;\n    char *result = read_full_file(PyUnicode_AsUTF8(path), &sz);\n    if (!result) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }\n    PyObject *ans = PyBytes_FromStringAndSize(result, sz);\n    free(result);\n    return ans;\n}\n\nstatic PyObject*\npy_makedirs(PyObject *self UNUSED, PyObject *args) {\n    int mode = 0755; const char *p;\n    if (!PyArg_ParseTuple(args, \"s|i\", &p, &mode)) return NULL;\n    if (!makedirs(p, mode)) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npy_get_config_dir(PyObject *self UNUSED, PyObject *args UNUSED) {\n    char buf[PATH_MAX];\n    if (get_config_dir(buf, PATH_MAX)) return PyUnicode_FromString(buf);\n    return PyUnicode_FromString(\"\");\n}\n\n#include \"launcher/cli-parser.h\"\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(replace_c0_codes_except_nl_space_tab, METH_O),\n    METHODB(read_file, METH_O),\n    {\"parse_cli_from_spec\", parse_cli_from_python_spec, METH_VARARGS, \"\"},\n    {\"wcwidth\", (PyCFunction)wcwidth_wrap, METH_O, \"\"},\n    {\"expanduser\", (PyCFunction)expanduser, METH_O, \"\"},\n    {\"abspath\", (PyCFunction)abspath, METH_O, \"\"},\n    {\"expand_ansi_c_escapes\", (PyCFunction)expand_ansi_c_escapes, METH_O, \"\"},\n    {\"get_docs_ref_map\", (PyCFunction)get_docs_ref_map, METH_NOARGS, \"\"},\n    {\"get_config_dir\", (PyCFunction)py_get_config_dir, METH_NOARGS, \"\"},\n    {\"wcswidth\", (PyCFunction)wcswidth_std, METH_O, \"\"},\n    {\"open_tty\", open_tty, METH_VARARGS, \"\"},\n    {\"makedirs\", py_makedirs, METH_VARARGS, \"\"},\n    {\"normal_tty\", normal_tty, METH_VARARGS, \"\"},\n    {\"raw_tty\", raw_tty, METH_VARARGS, \"\"},\n    {\"close_tty\", close_tty, METH_VARARGS, \"\"},\n    {\"set_iutf8_fd\", (PyCFunction)pyset_iutf8, METH_VARARGS, \"\"},\n    {\"base64_encode\", (PyCFunction)(void (*) (void))(pybase64_encode), METH_FASTCALL, \"\"},\n    {\"base64_encode_into\", (PyCFunction)base64_encode_into, METH_VARARGS, \"\"},\n    {\"base64_decode\", (PyCFunction)(void (*) (void))(pybase64_decode), METH_O, \"\"},\n    {\"base64_decode_into\", (PyCFunction)base64_decode_into, METH_VARARGS, \"\"},\n    {\"char_props_for\", py_char_props_for, METH_O, \"\"},\n    {\"split_into_graphemes\", (PyCFunction)split_into_graphemes, METH_O, \"\"},\n    {\"thread_write\", (PyCFunction)cm_thread_write, METH_VARARGS, \"\"},\n    {\"locale_is_valid\", (PyCFunction)locale_is_valid, METH_VARARGS, \"\"},\n    {\"shm_open\", (PyCFunction)py_shm_open, METH_VARARGS, \"\"},\n    {\"shm_unlink\", (PyCFunction)py_shm_unlink, METH_VARARGS, \"\"},\n    {\"wrapped_kitten_names\", (PyCFunction)wrapped_kittens, METH_NOARGS, \"\"},\n    {\"terminfo_data\", (PyCFunction)py_terminfo_data, METH_NOARGS, \"\"},\n    {\"monotonic\", (PyCFunction)py_monotonic, METH_NOARGS, \"\"},\n    {\"timed_debug_print\", (PyCFunction)py_timed_debug_print, METH_VARARGS, \"\"},\n    {\"find_in_memoryview\", (PyCFunction)find_in_memoryview, METH_VARARGS, \"\"},\n    {\"run_at_exit_cleanup_functions\", (PyCFunction)py_run_atexit_cleanup_functions, METH_NOARGS, \"\"},\n#ifdef __APPLE__\n    METHODB(user_cache_dir, METH_NOARGS),\n    METHODB(process_group_map, METH_NOARGS),\n#endif\n#ifdef WITH_PROFILER\n    {\"start_profiler\", (PyCFunction)start_profiler, METH_VARARGS, \"\"},\n    {\"stop_profiler\", (PyCFunction)stop_profiler, METH_NOARGS, \"\"},\n#endif\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nstatic void\nfree_fast_data_types_module(void *m UNUSED) {\n    freelocale(c_locale);\n    run_at_exit_cleanup_functions();\n}\n\nstatic struct PyModuleDef module = {\n    .m_base = PyModuleDef_HEAD_INIT,\n    .m_name = \"fast_data_types\",   /* name of module */\n    .m_doc = NULL,\n    .m_size = -1,\n    .m_methods = module_methods,\n    .m_free = free_fast_data_types_module,\n};\n\n\nextern int init_LineBuf(PyObject *);\nextern int init_HistoryBuf(PyObject *);\nextern int init_Cursor(PyObject *);\nextern int init_Shlex(PyObject *);\nextern int init_Parser(PyObject *);\nextern int init_DiskCache(PyObject *);\nextern bool init_child_monitor(PyObject *);\nextern int init_Line(PyObject *);\nextern int init_ColorProfile(PyObject *);\nextern int init_Screen(PyObject *);\nextern bool init_animations(PyObject*);\nextern bool init_fontconfig_library(PyObject*);\nextern bool init_crypto_library(PyObject*);\nextern bool init_desktop(PyObject*);\nextern bool init_fonts(PyObject*);\nextern bool init_glfw(PyObject *m);\nextern bool init_child(PyObject *m);\nextern bool init_state(PyObject *module);\nextern bool init_keys(PyObject *module);\nextern bool init_graphics(PyObject *module);\nextern bool init_shaders(PyObject *module);\nextern bool init_mouse(PyObject *module);\nextern bool init_kittens(PyObject *module);\nextern bool init_logging(PyObject *module);\nextern bool init_png_reader(PyObject *module);\nextern bool init_utmp(PyObject *module);\nextern bool init_loop_utils(PyObject *module);\nextern bool init_systemd_module(PyObject *module);\n#ifdef __APPLE__\nextern int init_CoreText(PyObject *);\nextern bool init_cocoa(PyObject *module);\nextern bool init_macos_process_info(PyObject *module);\n#else\nextern bool init_freetype_library(PyObject*);\nextern bool init_freetype_render_ui_text(PyObject*);\n#endif\n\nstatic unsigned\nshift_to_first_set_bit(CellAttrs x) {\n    unsigned num_of_bits = 8 * sizeof(x.val);\n    unsigned ans = 0;\n    while (num_of_bits--) {\n        if (x.val & 1) return ans;\n        x.val >>= 1;\n        ans++;\n    }\n    return ans;\n}\n\n\nEXPORTED PyMODINIT_FUNC\nPyInit_fast_data_types(void) {\n    PyObject *m;\n    m = PyModule_Create(&module);\n    if (m == NULL) return NULL;\n    init_monotonic();\n\n    if (!init_logging(m)) return NULL;\n    if (!init_LineBuf(m)) return NULL;\n    if (!init_HistoryBuf(m)) return NULL;\n    if (!init_Line(m)) return NULL;\n    if (!init_Cursor(m)) return NULL;\n    if (!init_Shlex(m)) return NULL;\n    if (!init_Parser(m)) return NULL;\n    if (!init_DiskCache(m)) return NULL;\n    if (!init_child_monitor(m)) return NULL;\n    if (!init_ColorProfile(m)) return NULL;\n    if (!init_Screen(m)) return NULL;\n    if (!init_glfw(m)) return NULL;\n    if (!init_child(m)) return NULL;\n    if (!init_state(m)) return NULL;\n    if (!init_keys(m)) return NULL;\n    if (!init_graphics(m)) return NULL;\n    if (!init_shaders(m)) return NULL;\n    if (!init_mouse(m)) return NULL;\n    if (!init_kittens(m)) return NULL;\n    if (!init_png_reader(m)) return NULL;\n#ifdef __APPLE__\n    if (!init_macos_process_info(m)) return NULL;\n    if (!init_CoreText(m)) return NULL;\n    if (!init_cocoa(m)) return NULL;\n#else\n    if (!init_freetype_library(m)) return NULL;\n    if (!init_fontconfig_library(m)) return NULL;\n    if (!init_desktop(m)) return NULL;\n    if (!init_freetype_render_ui_text(m)) return NULL;\n#endif\n    if (!init_fonts(m)) return NULL;\n    if (!init_utmp(m)) return NULL;\n    if (!init_loop_utils(m)) return NULL;\n    if (!init_crypto_library(m)) return NULL;\n    if (!init_systemd_module(m)) return NULL;\n    if (!init_animations(m)) return NULL;\n\n    CellAttrs a;\n#define s(name, attr) { a.val = 0; a.attr = 1; PyModule_AddIntConstant(m, #name, shift_to_first_set_bit(a)); }\n    s(BOLD, bold); s(ITALIC, italic); s(REVERSE, reverse); s(MARK, mark);\n    s(STRIKETHROUGH, strike); s(DIM, dim); s(DECORATION, decoration); s(BLINK, blink);\n#undef s\n    PyModule_AddIntConstant(m, \"MARK_MASK\", MARK_MASK);\n    PyModule_AddIntConstant(m, \"DECORATION_MASK\", DECORATION_MASK);\n    PyModule_AddStringMacro(m, ERROR_PREFIX);\n#ifdef KITTY_VCS_REV\n    PyModule_AddStringMacro(m, KITTY_VCS_REV);\n#endif\n    PyModule_AddIntMacro(m, CURSOR_BLOCK);\n    PyModule_AddIntMacro(m, CURSOR_BEAM);\n    PyModule_AddIntMacro(m, CURSOR_UNDERLINE);\n    PyModule_AddIntMacro(m, CURSOR_HOLLOW);\n    PyModule_AddIntMacro(m, NO_CURSOR_SHAPE);\n    PyModule_AddIntMacro(m, DECAWM);\n    PyModule_AddIntMacro(m, DECCOLM);\n    PyModule_AddIntMacro(m, DECOM);\n    PyModule_AddIntMacro(m, IRM);\n    PyModule_AddIntMacro(m, FILE_TRANSFER_CODE);\n    PyModule_AddIntMacro(m, ESC_CSI);\n    PyModule_AddIntMacro(m, ESC_OSC);\n    PyModule_AddIntMacro(m, ESC_APC);\n    PyModule_AddIntMacro(m, ESC_DCS);\n    PyModule_AddIntMacro(m, ESC_PM);\n    PyModule_AddIntMacro(m, TEXT_SIZE_CODE);\n    PyModule_AddIntMacro(m, COLOR_NOT_SET);\n    PyModule_AddIntMacro(m, COLOR_IS_SPECIAL);\n    PyModule_AddIntMacro(m, COLOR_IS_INDEX);\n    PyModule_AddIntMacro(m, COLOR_IS_RGB);\n#ifdef __APPLE__\n    // Apple says its SHM_NAME_MAX but SHM_NAME_MAX is not actually declared in typical CrApple style.\n    // This value is based on experimentation and from qsharedmemory.cpp in Qt\n    PyModule_AddIntConstant(m, \"SHM_NAME_MAX\", 30);\n#else\n    // FreeBSD's man page says this is 1023. Linux says its PATH_MAX.\n    PyModule_AddIntConstant(m, \"SHM_NAME_MAX\", MIN(1023, PATH_MAX));\n#endif\n\n    if (PyType_Ready(&StreamingBase64Decoder_Type) < 0) return NULL;\n    if (PyModule_AddObject(m, \"StreamingBase64Decoder\", (PyObject *) &StreamingBase64Decoder_Type) < 0) return NULL;\n    if (PyType_Ready(&StreamingBase64Encoder_Type) < 0) return NULL;\n    if (PyModule_AddObject(m, \"StreamingBase64Encoder\", (PyObject *) &StreamingBase64Encoder_Type) < 0) return NULL;\n    c_locale = newlocale(LC_NUMERIC_MASK, \"C\", (locale_t)0);\n    if (!c_locale) { PyErr_NoMemory(); return NULL; }\n\n    return m;\n}\n"
  },
  {
    "path": "kitty/data-types.h",
    "content": "/*\n * data-types.h\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#ifdef _POSIX_C_SOURCE\n#error \"Must include \\\"data-types.h\\\" before any system headers\"\n#endif\n#define PY_SSIZE_T_CLEAN\n#include <Python.h>\n\n#include <assert.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <poll.h>\n#include <pthread.h>\n#include <locale.h>\n#include \"glfw-wrapper.h\"\n#include \"banned.h\"\n// Required minimum OpenGL version\n#define OPENGL_REQUIRED_VERSION_MAJOR 3\n#ifdef __APPLE__\n#include <xlocale.h>\n#define OPENGL_REQUIRED_VERSION_MINOR 3\n#else\n#define OPENGL_REQUIRED_VERSION_MINOR 1\n#endif\n#define GLSL_VERSION 140\n#define GLFW_MOD_KITTY (GLFW_MOD_LAST * 2)\n#define UNUSED __attribute__ ((unused))\n#define PYNOARG PyObject *__a1 UNUSED, PyObject *__a2 UNUSED\n#define EXPORTED __attribute__ ((visibility (\"default\")))\n#define LIKELY(x)    __builtin_expect (!!(x), 1)\n#define UNLIKELY(x)  __builtin_expect (!!(x), 0)\n#define MAX(x, y) __extension__ ({ \\\n    const __typeof__ (x) __a__ = (x); const __typeof__ (y) __b__ = (y); \\\n        __a__ > __b__ ? __a__ : __b__;})\n#define MIN(x, y) __extension__ ({ \\\n    const __typeof__ (x) __a__ = (x); const __typeof__ (y) __b__ = (y); \\\n        __a__ < __b__ ? __a__ : __b__;})\n#define SWAP(x, y) do { __typeof__(x) _sw_ = y; y = x; x = _sw_; } while(0)\n#define xstr(s) str(s)\n#define str(s) #s\n#define arraysz(x) (sizeof(x)/sizeof(x[0]))\n#define zero_at_i(array, idx) memset((array) + (idx), 0, sizeof((array)[0]))\n#define zero_at_ptr(p) memset((p), 0, sizeof((p)[0]))\n#define literal_strlen(x) (sizeof(x)-1)\n#define zero_at_ptr_count(p, count) memset((p), 0, (count) * sizeof((p)[0]))\n#define C0_EXCEPT_NL_SPACE_TAB_DEL 0x0 ... 0x8: case 0xb ... 0x1f\n#define C0_EXCEPT_NL_SPACE_TAB 0x0 ... 0x8: case 0xb ... 0x1f: case 0x7f\nvoid log_error(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));\n#define fatal(...) { log_error(__VA_ARGS__); exit(EXIT_FAILURE); }\nstatic inline void cleanup_free(void *p) { free(*(void**)p); }\n#define RAII_ALLOC(type, name, initializer) __attribute__((cleanup(cleanup_free))) type *name = initializer\nstatic inline void cleanup_decref(PyObject **p) { Py_CLEAR(*p); }\n#define RAII_PyObject(name, initializer) __attribute__((cleanup(cleanup_decref))) PyObject *name = initializer\n#define RAII_PY_BUFFER(name) __attribute__((cleanup(PyBuffer_Release))) Py_buffer name = {0}\n\ntypedef unsigned long long id_type;\ntypedef uint32_t char_type;\nstatic_assert(sizeof(Py_UCS4) == sizeof(char_type), \"PyUCS4 and char_type dont match\");\n#define MAX_CHAR_TYPE_VALUE UINT32_MAX\ntypedef uint32_t color_type;\ntypedef uint16_t hyperlink_id_type;\ntypedef int key_type;\n#define HYPERLINK_MAX_NUMBER UINT16_MAX\ntypedef uint16_t combining_type;\ntypedef uint16_t glyph_index;\ntypedef uint32_t pixel;\ntypedef unsigned int index_type;\ntypedef uint32_t sprite_index;\ntypedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE, CURSOR_HOLLOW, NUM_OF_CURSOR_SHAPES } CursorShape;\ntypedef enum { DISABLE_LIGATURES_NEVER, DISABLE_LIGATURES_CURSOR, DISABLE_LIGATURES_ALWAYS } DisableLigature;\n\n#define ERROR_PREFIX \"[PARSE ERROR]\"\ntypedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MODE } MouseTrackingMode;\ntypedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL, SGR_PIXEL_PROTOCOL} MouseTrackingProtocol;\ntypedef enum MouseShapes {\n    INVALID_POINTER,\n    /* start mouse shapes (auto generated by gen-key-constants.py do not edit) */\n    DEFAULT_POINTER,\n    TEXT_POINTER,\n    POINTER_POINTER,\n    HELP_POINTER,\n    WAIT_POINTER,\n    PROGRESS_POINTER,\n    CROSSHAIR_POINTER,\n    CELL_POINTER,\n    VERTICAL_TEXT_POINTER,\n    MOVE_POINTER,\n    E_RESIZE_POINTER,\n    NE_RESIZE_POINTER,\n    NW_RESIZE_POINTER,\n    N_RESIZE_POINTER,\n    SE_RESIZE_POINTER,\n    SW_RESIZE_POINTER,\n    S_RESIZE_POINTER,\n    W_RESIZE_POINTER,\n    EW_RESIZE_POINTER,\n    NS_RESIZE_POINTER,\n    NESW_RESIZE_POINTER,\n    NWSE_RESIZE_POINTER,\n    ZOOM_IN_POINTER,\n    ZOOM_OUT_POINTER,\n    ALIAS_POINTER,\n    COPY_POINTER,\n    NOT_ALLOWED_POINTER,\n    NO_DROP_POINTER,\n    GRAB_POINTER,\n    GRABBING_POINTER,\n/* end mouse shapes */\n} MouseShape;\ntypedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn;\ntypedef enum { SCROLLBAR_NEVER, SCROLLBAR_ON_SCROLLED, SCROLLBAR_ON_HOVERED, SCROLLBAR_ON_SCROLL_AND_HOVER, SCROLLBAR_ALWAYS } ScrollbarVisibilityPolicy;\ntypedef enum { TILING, SCALED, MIRRORED, CLAMPED, CENTER_CLAMPED, CENTER_SCALED } BackgroundImageLayout;\ntypedef struct ImageAnchorPosition {\n    float canvas_x, canvas_y, image_x, image_y;\n} ImageAnchorPosition;\n\n#define MAX_CHILDREN 512\n#define BLANK_CHAR 0\n#define COL_MASK 0xFFFFFFFF\n#define DECORATION_FG_CODE 58\n\n// PUA character used as an image placeholder.\n#define IMAGE_PLACEHOLDER_CHAR 0x10EEEE\n\n#define FG 1\n#define BG 2\n\n#define COPY_CELL(src, s, dest, d) \\\n    (dest)->cpu_cells[d] = (src)->cpu_cells[s]; (dest)->gpu_cells[d] = (src)->gpu_cells[s];\n\n#define COPY_SELF_CELL(s, d) COPY_CELL(self, s, self, d)\n\n#define METHOD(name, arg_type) {#name, (PyCFunction)name, arg_type, name##_doc},\n#define METHODB(name, arg_type) {#name, (PyCFunction)name, arg_type, \"\"}\n\n#define BOOL_GETSET(type, x) \\\n    static PyObject* x##_get(type *self, void UNUSED *closure) { PyObject *ans = self->x ? Py_True : Py_False; Py_INCREF(ans); return ans; } \\\n    static int x##_set(type *self, PyObject *value, void UNUSED *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, \"Cannot delete attribute\"); return -1; } self->x = PyObject_IsTrue(value) ? true : false; return 0; }\n\n#define GETSET(x) \\\n    {#x, (getter) x##_get, (setter) x##_set, #x, NULL},\n\n#ifndef EXTRA_INIT\n#define EXTRA_INIT\n#endif\n#define INIT_TYPE(type) \\\n    int init_##type(PyObject *module) {\\\n        if (PyType_Ready(&type##_Type) < 0) return 0; \\\n        if (PyModule_AddObject(module, #type, (PyObject *)&type##_Type) != 0) return 0; \\\n        Py_INCREF(&type##_Type); \\\n        EXTRA_INIT; \\\n        return 1; \\\n    }\n\n#define RICHCMP(type) \\\n    static PyObject * richcmp(PyObject *obj1, PyObject *obj2, int op) { \\\n        PyObject *result = NULL; \\\n        int eq; \\\n        if (op != Py_EQ && op != Py_NE) { Py_RETURN_NOTIMPLEMENTED; } \\\n        if (!PyObject_TypeCheck(obj1, &type##_Type)) { Py_RETURN_FALSE; } \\\n        if (!PyObject_TypeCheck(obj2, &type##_Type)) { Py_RETURN_FALSE; } \\\n        eq = __eq__((type*)obj1, (type*)obj2); \\\n        if (op == Py_NE) result = eq ? Py_False : Py_True; \\\n        else result = eq ? Py_True : Py_False; \\\n        Py_INCREF(result); \\\n        return result; \\\n    }\n\n#ifdef __clang__\n#define START_IGNORE_DIAGNOSTIC(diag) _Pragma(xstr(clang diagnostic push))  _Pragma(xstr(clang diagnostic ignored diag))\n#define END_IGNORE_DIAGNOSTIC _Pragma(\"clang diagnostic pop\")\n#else\n#define START_IGNORE_DIAGNOSTIC(diag) _Pragma(xstr(GCC diagnostic push))  _Pragma(xstr(GCC diagnostic ignored diag))\n#define END_IGNORE_DIAGNOSTIC _Pragma(\"GCC diagnostic pop\")\n#endif\n\n#define IGNORE_PEDANTIC_WARNINGS START_IGNORE_DIAGNOSTIC(\"-Wpedantic\")\n#define END_IGNORE_PEDANTIC_WARNINGS END_IGNORE_DIAGNOSTIC\n#define ALLOW_UNUSED_RESULT START_IGNORE_DIAGNOSTIC(\"-Wunused-result\")\n#define END_ALLOW_UNUSED_RESULT END_IGNORE_DIAGNOSTIC\n#define START_ALLOW_CASE_RANGE IGNORE_PEDANTIC_WARNINGS\n#define END_ALLOW_CASE_RANGE END_IGNORE_PEDANTIC_WARNINGS\n#define BIT_MASK(__TYPE__, __ONE_COUNT__) \\\n    (((__TYPE__) (-((__ONE_COUNT__) != 0))) \\\n    & (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__))))\n#define ADD_TYPE(which) \\\n    if (PyType_Ready(&which##_Type) < 0) return false; \\\n    if (PyModule_AddObject(module, #which, (PyObject *)&which##_Type) != 0) return false; \\\n    Py_INCREF(&which##_Type);\n\n\ntypedef enum UTF8State { UTF8_ACCEPT = 0, UTF8_REJECT = 1} UTF8State;\n\ntypedef struct {\n    // right = left + width, bottom = top + height\n    uint32_t left, top, right, bottom;\n} Region;\n\ntypedef enum { UNKNOWN_PROMPT_KIND = 0, PROMPT_START = 1, SECONDARY_PROMPT = 2, OUTPUT_START = 3 } PromptKind;\ntypedef struct {int x;} *HYPERLINK_POOL_HANDLE;\ntypedef struct {\n    Py_UCS4 *buf;\n    size_t len, capacity;\n    HYPERLINK_POOL_HANDLE hyperlink_pool;\n    hyperlink_id_type active_hyperlink_id;\n} ANSIBuf;\n\ntypedef struct {\n    PyObject_HEAD\n    monotonic_t position_changed_by_client_at;\n    unsigned int x, y;\n    bool non_blinking;\n    CursorShape shape;\n\n    struct {\n        bool bold, italic, reverse, strikethrough, dim, blink;\n        uint8_t decoration;\n        color_type fg, bg, decoration_fg;\n    } sgr;\n} Cursor;\n\ntypedef struct {\n    bool is_focused, render_even_when_unfocused, is_visible;\n    CursorShape shape;\n    unsigned x, y, multicursor_count;\n    float cursor_opacity, text_blink_opacity;\n} CursorRenderInfo;\n\ntypedef enum DynamicColorType {\n    COLOR_NOT_SET, COLOR_IS_SPECIAL, COLOR_IS_INDEX, COLOR_IS_RGB\n} DynamicColorType;\n\ntypedef union DynamicColor {\n    struct {\n        color_type rgb: 24;\n        DynamicColorType type: 8;\n    };\n    color_type val;\n} DynamicColor;\n\ntypedef struct {\n    DynamicColor default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg, visual_bell_color;\n} DynamicColors;\n\ntypedef struct TransparentDynamicColor {\n    color_type color; float opacity; bool is_set;\n} TransparentDynamicColor;\n\n\n#define MARK_MASK (3u)\n\ntypedef struct {\n    PyObject_HEAD\n\n    bool dirty;\n    uint32_t color_table[256], orig_color_table[256];\n    TransparentDynamicColor configured_transparent_colors[8], overriden_transparent_colors[8];\n    struct { DynamicColors dynamic_colors; uint32_t color_table[256]; TransparentDynamicColor transparent_colors[8]; } *color_stack;\n    unsigned int color_stack_idx, color_stack_sz;\n    DynamicColors configured, overridden;\n    color_type mark_foregrounds[MARK_MASK+1], mark_backgrounds[MARK_MASK+1];\n} ColorProfile;\n\ntypedef struct {\n    unsigned width, height;\n} CellPixelSize;\n\ntypedef struct {int x;} *SPRITE_MAP_HANDLE;\n\ntypedef struct FontCellMetrics {\n    unsigned int cell_width, cell_height, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;\n} FontCellMetrics;\n#define FONTS_DATA_HEAD SPRITE_MAP_HANDLE sprite_map; double logical_dpi_x, logical_dpi_y, font_sz_in_pts; FontCellMetrics fcm;\ntypedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE;\n\n#define clear_sprite_position(cell) (cell).sprite_idx = 0;\n\n#define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \\\n    if ((base)->capacity < num) { \\\n        size_t _newcap = MAX((size_t)initial_cap, MAX(2 * (base)->capacity, (size_t)num)); \\\n        (base)->array = realloc((base)->array, sizeof(type) * _newcap); \\\n        if ((base)->array == NULL) fatal(\"Out of memory while ensuring space for %zu elements in array of %s\", (size_t)num, #type); \\\n        if (zero_mem) memset((base)->array + (base)->capacity, 0, sizeof(type) * (_newcap - (base)->capacity)); \\\n        (base)->capacity = _newcap; \\\n    }\n\n#define remove_i_from_array(array, i, count) { \\\n    (count)--; \\\n    if ((i) < (count)) { \\\n        memmove((array) + (i), (array) + (i) + 1, sizeof((array)[0]) * ((count) - (i))); \\\n    }}\n\n// Global functions\nCursor* alloc_cursor(void);\nColorProfile* alloc_color_profile(void);\nvoid copy_color_profile(ColorProfile*, ColorProfile*);\nPyObject* parse_bytes_dump(PyObject UNUSED *, PyObject *);\nPyObject* parse_bytes(PyObject UNUSED *, PyObject *);\nvoid cursor_reset(Cursor*);\nCursor* cursor_copy(Cursor*);\nvoid cursor_copy_to(Cursor *src, Cursor *dest);\nvoid cursor_reset_display_attrs(Cursor*);\nvoid cursor_from_sgr(Cursor *self, int *params, unsigned int count, bool is_group);\nconst char* cursor_as_sgr(const Cursor *);\n\nPyObject* cm_thread_write(PyObject *self, PyObject *args);\nbool schedule_write_to_child(unsigned long id, unsigned int num, ...);\nbool schedule_write_to_child_python(unsigned long id, const char *prefix, PyObject* tuple_of_str_or_bytes, const char *suffix);\nbool set_iutf8(int, bool);\n\nDynamicColor colorprofile_to_color(const ColorProfile *self, DynamicColor entry, DynamicColor defval);\nvoid colorprofile_reset(ColorProfile *self);\nbool colorprofile_to_transparent_color(const ColorProfile *self, unsigned index, color_type *color, float *opacity);\ncolor_type\ncolorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, DynamicColor defval, DynamicColor fallback, DynamicColor falback_defval);\nvoid copy_color_table_to_buffer(ColorProfile *self, color_type *address, int offset, size_t stride);\nbool colorprofile_push_colors(ColorProfile*, unsigned int);\nbool colorprofile_pop_colors(ColorProfile*, unsigned int);\nvoid colorprofile_report_stack(ColorProfile*, unsigned int*, unsigned int*);\n\nvoid set_mouse_cursor(MouseShape);\nvoid enter_event(int modifiers);\nvoid leave_event(int modifiers);\nvoid mouse_event(const int, int, int);\nvoid focus_in_event(void);\nvoid scroll_event(const GLFWScrollEvent *ev);\nvoid on_key_input(const GLFWkeyevent *ev);\nvoid request_window_attention(id_type, bool);\nvoid free_drag_source(void);\nlocale_t get_c_locale(void);\n#ifndef __APPLE__\nvoid play_canberra_sound(const char *which_sound, const char *event_id, bool is_path, const char *role, const char *theme_name);\n#endif\nSPRITE_MAP_HANDLE alloc_sprite_map(void);\nvoid free_sprite_data(FONTS_DATA_HANDLE);\nconst char* get_hyperlink_for_id(const HYPERLINK_POOL_HANDLE, hyperlink_id_type id, bool only_url);\n\n#define memset_array(array, val, count) if ((count) > 0) { \\\n    (array)[0] = (val); \\\n    size_t __copied__ = 1; \\\n    while (__copied__ < (count)) { \\\n        const size_t __num__ = MIN(__copied__, (count) - __copied__); \\\n        memcpy((array) + __copied__, (array), __num__ * sizeof((val))); \\\n        __copied__ += __num__; \\\n    } \\\n}\n"
  },
  {
    "path": "kitty/debug_config.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nimport socket\nimport sys\nimport termios\nimport time\nfrom collections.abc import Callable, Iterator, Sequence\nfrom contextlib import suppress\nfrom functools import partial\nfrom pprint import pformat\nfrom typing import IO, TypeVar\n\nfrom kittens.tui.operations import colored, styled\n\nfrom .child import cmdline_of_pid\nfrom .cli import version\nfrom .colors import theme_colors\nfrom .constants import extensions_dir, is_macos, is_wayland, kitty_base_dir, kitty_exe, shell_path\nfrom .fast_data_types import Color, SingleKey, current_fonts, glfw_get_system_color_theme, gpu_driver_version_string, num_users, wayland_compositor_data\nfrom .options.types import Options as KittyOpts\nfrom .options.types import defaults, secret_options\nfrom .options.utils import KeyboardMode, KeyDefinition\nfrom .rgb import color_as_sharp, color_from_int\nfrom .types import MouseEvent, Shortcut, mod_to_names\nfrom .utils import shlex_split\n\nAnyEvent = TypeVar('AnyEvent', MouseEvent, Shortcut)\nPrint = Callable[..., None]\nShortcutMap = dict[Shortcut, str]\n\n\ndef green(x: str) -> str:\n    return colored(x, 'green')\n\n\ndef yellow(x: str) -> str:\n    return colored(x, 'yellow')\n\n\ndef title(x: str) -> str:\n    return colored(x, 'blue', intense=True)\n\n\ndef print_event(ev: str, defn: str, print: Print) -> None:\n    print(f'\\t{ev} →  {defn}')\n\n\ndef print_mapping_changes(defns: dict[str, str], changes: set[str], text: str, print: Print) -> None:\n    if changes:\n        print(title(text))\n        for k in sorted(changes):\n            print_event(k, defns[k], print)\n\n\ndef compare_maps(\n    final: dict[AnyEvent, str], final_kitty_mod: int, initial: dict[AnyEvent, str], initial_kitty_mod: int, print: Print, mode_name: str = ''\n) -> None:\n    ei = {k.human_repr(initial_kitty_mod): v for k, v in initial.items()}\n    ef = {k.human_repr(final_kitty_mod): v for k, v in final.items()}\n    added = set(ef) - set(ei)\n    removed = set(ei) - set(ef)\n    changed = {k for k in set(ef) & set(ei) if ef[k] != ei[k]}\n    which = 'shortcuts' if isinstance(next(iter(initial or final)), Shortcut) else 'mouse actions'\n    if mode_name and (added or removed or changed):\n        print(f'{title(\"Changes in keyboard mode: \" + mode_name)}')\n    print_mapping_changes(ef, added, f'Added {which}:', print)\n    print_mapping_changes(ei, removed, f'Removed {which}:', print)\n    print_mapping_changes(ef, changed, f'Changed {which}:', print)\n\n\n\ndef compare_opts(opts: KittyOpts, global_shortcuts: dict[str, SingleKey] | None, print: Print) -> None:\n    from .config import load_config\n    print()\n    print('Config options different from defaults:')\n    default_opts = load_config()\n    ignored = ('keymap', 'sequence_map', 'mousemap', 'map', 'mouse_map')\n    changed_opts = [\n        f for f in sorted(defaults._fields)\n        if f not in ignored and getattr(opts, f) != getattr(defaults, f)\n    ]\n    field_len = max(map(len, changed_opts)) if changed_opts else 20\n    fmt = f'{{:{field_len:d}s}}'\n    colors = []\n    for f in changed_opts:\n        if f in secret_options:\n            print(title(f'{f}:'), 'REDACTED FOR SECURITY')\n            continue\n        val = getattr(opts, f)\n        if isinstance(val, dict):\n            print(f'{f}:')\n            if f == 'symbol_map':\n                for k in sorted(val):\n                    print(f'\\tU+{k[0]:04x} - U+{k[1]:04x} → {val[k]}')\n            elif f == 'modify_font':\n                for k in sorted(val):\n                    print('   ', val[k])\n            elif f == 'exe_search_path':\n                for k in val:\n                    print('   ', k)\n            else:\n                print(pformat(val))\n        else:\n            val = getattr(opts, f)\n            if isinstance(val, Color):\n                colors.append(fmt.format(f) + ' ' + color_as_sharp(val) + ' ' + styled('  ', bg=val))\n            else:\n                if f == 'kitty_mod':\n                    print(fmt.format(f), '+'.join(mod_to_names(getattr(opts, f))))\n                elif f in ('wayland_titlebar_color', 'macos_titlebar_color'):\n                    if val == 0:\n                        cval = 'system'\n                    elif val == 1:\n                        cval = 'background'\n                    else:\n                        col = color_from_int(val >> 8)\n                        cval = color_as_sharp(col) + ' ' + styled('  ', bg=col)\n                    colors.append(fmt.format(f) + ' ' + cval)\n                else:\n                    print(fmt.format(f), str(getattr(opts, f)))\n\n    compare_maps(opts.mousemap, opts.kitty_mod, default_opts.mousemap, default_opts.kitty_mod, print)\n\n    def as_str(defns: Sequence[KeyDefinition]) -> str:\n        seen = set()\n        uniq = []\n        for d in reversed(defns):\n            key = d.unique_identity_within_keymap\n            if key not in seen:\n                seen.add(key)\n                uniq.append(d)\n        return ', '.join(d.human_repr() for d in uniq)\n\n    def build_shortcut_map(km: KeyboardMode) -> ShortcutMap:\n        result: ShortcutMap = {}\n        for defns in km.keymap.values():\n            by_seq: dict[tuple[SingleKey, ...], list[KeyDefinition]] = {}\n            for d in defns:\n                by_seq.setdefault(d.full_key_sequence_to_trigger, []).append(d)\n            for seq, seq_defns in by_seq.items():\n                result[Shortcut(seq)] = as_str(seq_defns)\n        return result\n\n    for kmn, initial_ in default_opts.keyboard_modes.items():\n        initial = build_shortcut_map(initial_)\n        final_ = opts.keyboard_modes.get(kmn, KeyboardMode(kmn))\n        final = build_shortcut_map(final_)\n        if not kmn and global_shortcuts:\n            for action, sk in global_shortcuts.items():\n                sc = Shortcut((sk,))\n                if sc not in final:\n                    final[sc] = action\n        compare_maps(final, opts.kitty_mod, initial, default_opts.kitty_mod, print, mode_name=kmn)\n    new_keyboard_modes = set(opts.keyboard_modes) - set(default_opts.keyboard_modes)\n    for kmn in new_keyboard_modes:\n        initial_ = KeyboardMode(kmn)\n        initial = build_shortcut_map(initial_)\n        final_ = opts.keyboard_modes[kmn]\n        final = build_shortcut_map(final_)\n        compare_maps(final, opts.kitty_mod, initial, default_opts.kitty_mod, print, mode_name=kmn)\n    if colors:\n        print(f'{title(\"Colors\")}:', end='\\n\\t')\n        print('\\n\\t'.join(sorted(colors)))\n\n\nclass IssueData:\n\n    def __init__(self) -> None:\n        self.uname = os.uname()\n        self.s, self.n, self.r, self.v, self.m = self.uname\n        try:\n            self.hostname = self.o = socket.gethostname()\n        except Exception:\n            self.hostname = self.o = 'localhost'\n        _time = time.localtime()\n        self.formatted_time = self.d = time.strftime('%a %b %d %Y', _time)\n        self.formatted_date = self.t = time.strftime('%H:%M:%S', _time)\n        try:\n            self.tty_name = format_tty_name(os.ctermid())\n        except OSError:\n            self.tty_name = '(none)'\n        self.l = self.tty_name\n        self.baud_rate = 0\n        if sys.stdin.isatty():\n            with suppress(OSError):\n                self.baud_rate = termios.tcgetattr(sys.stdin.fileno())[5]\n        self.b = str(self.baud_rate)\n        try:\n            self.num_users = num_users()\n        except RuntimeError:\n            self.num_users = -1\n        self.u = str(self.num_users)\n        self.U = self.u + ' user' + ('' if self.num_users == 1 else 's')\n        self.vars = {}\n        with suppress(Exception), open('/etc/os-release') as osf:\n            for line in osf:\n                k, _, v = line.strip().partition('=')\n                self.vars[k] = ' '.join(shlex_split(v))\n        if not self.vars:\n            with suppress(Exception), open('/usr/lib/os-release') as osf:\n                for line in osf:\n                    k, _, v = line.strip().partition('=')\n                    self.vars[k] = ' '.join(shlex_split(v))\n\n    def translate_issue_char(self, char: str) -> str:\n        try:\n            return str(getattr(self, char)) if len(char) == 1 else char\n        except AttributeError:\n            return char\n\n    def parse_issue_file(self, issue_file: IO[str]) -> Iterator[str]:\n        state = 'normal'\n        varname = ''\n        for ch in issue_file.read():\n            match state:\n                case 'normal':\n                    if ch == '\\\\':\n                        state = 'escape'\n                    else:\n                        yield ch\n                case 'escape':\n                    match ch:\n                        case 'S':\n                            state = 'sub_start'\n                        case '\\\\':\n                            yield '\\\\'\n                            state = 'normal'\n                        case _:\n                            yield self.translate_issue_char(ch)\n                            state = 'normal'\n                case 'sub_start':\n                    if ch == '{':  # }\n                        state = 'sub'\n                        varname = ''\n                    else:\n                        yield ch\n                        state = 'normal'\n                case 'sub':\n                    if ch == '}':\n                        if val := self.vars.get(varname):\n                            yield val\n                        state = 'normal'\n                    else:\n                        varname += ch\n\n\ndef format_tty_name(raw: str) -> str:\n    return re.sub(r'^/dev/([^/]+)/([^/]+)$', r'\\1\\2', raw)\n\n\ndef compositor_name() -> str:\n    ans = 'X11'\n    if is_wayland():\n        ans = 'Wayland'\n        with suppress(Exception):\n            pid, missing_capabilities = wayland_compositor_data()\n            if pid > -1:\n                cmdline = cmdline_of_pid(pid)\n                exe = cmdline[0]\n                with suppress(Exception):\n                    import subprocess\n                    if exe.lower() == 'hyprland':\n                        raw = subprocess.check_output(['hyprctl', 'version']).decode().strip()\n                        m = re.search(r'^Tag:\\s*(\\S+)', raw, flags=re.M)\n                        if m is not None:\n                            exe = f'{exe} {m.group(1)}'\n                        else:\n                            exe = raw.splitlines()[0]\n                    exe = subprocess.check_output([exe, '--version']).decode().strip().splitlines()[0]\n                ans += f' ({exe})'\n            if missing_capabilities:\n                ans += f' missing: {missing_capabilities}'\n    return ans\n\n\ndef issue_data() -> str:\n    with suppress(Exception):\n        idata = IssueData()\n        with open('/etc/issue', encoding='utf-8', errors='replace') as f:\n            return ''.join(idata.parse_issue_file(f)).strip()\n    return ''\n\n\ndef debug_config(opts: KittyOpts | None = None, global_shortcuts: dict[str, SingleKey] | None = None) -> str:\n    if opts is None:\n        from kitty.cli import create_default_opts\n        opts = create_default_opts()\n    from io import StringIO\n    out = StringIO()\n    p = partial(print, file=out)\n    p(version(add_rev=True))\n    p(' '.join(os.uname()))\n    if is_macos:\n        import subprocess\n        with suppress(Exception):\n            p(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip())\n    if (idata := issue_data()):\n        p(idata)\n    with suppress(Exception), open('/etc/lsb-release', encoding='utf-8', errors='replace') as f:\n        p(f.read().strip())\n    if not is_macos:\n        p('Running under:', green(compositor_name()))\n    p(green('OpenGL:'), gpu_driver_version_string())\n    p(green('Frozen:'), 'True' if getattr(sys, 'frozen', False) else 'False')\n    p(green('Fonts:'))\n    for k, font in current_fonts().items():\n        if hasattr(font, 'identify_for_debug'):\n            flines = font.identify_for_debug().splitlines()\n            p(yellow(f'{k.rjust(8)}:'), flines[0])\n            for fl in flines[1:]:\n                p(' ' * 9, fl)\n    p(green('Paths:'))\n    p(yellow('  kitty:'), os.path.realpath(kitty_exe()))\n    p(yellow('  base dir:'), kitty_base_dir)\n    p(yellow('  extensions dir:'), extensions_dir)\n    p(yellow('  system shell:'), shell_path)\n    p(f'System color scheme: {green(glfw_get_system_color_theme())}. Applied color theme type: {yellow(theme_colors.applied_theme or \"none\")}')\n    if opts.config_paths:\n        p(green('Loaded config files:'))\n        p(' ', '\\n  '.join(opts.config_paths))\n    if opts.config_overrides:\n        p(green('Loaded config overrides:'))\n        p(' ', '\\n  '.join(opts.config_overrides))\n    compare_opts(opts, global_shortcuts, p)\n    p()\n    p(green('Important environment variables seen by the kitty process:'))\n\n    def penv(k: str) -> None:\n        v = os.environ.get(k)\n        if v is not None:\n            p('\\t' + k.ljust(35), styled(v, dim=True))\n\n    for k in (\n        'PATH LANG KITTY_CONFIG_DIRECTORY KITTY_CACHE_DIRECTORY VISUAL EDITOR SHELL'\n        ' GLFW_IM_MODULE KITTY_WAYLAND_DETECT_MODIFIERS DISPLAY WAYLAND_DISPLAY USER XCURSOR_SIZE'\n    ).split():\n        penv(k)\n    for k in os.environ:\n        if k.startswith('LC_') or k.startswith('XDG_'):\n            penv(k)\n    return out.getvalue()\n"
  },
  {
    "path": "kitty/decorations.c",
    "content": "/*\n * decorations.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"decorations.h\"\n#include \"state.h\"\n\ntypedef uint32_t uint;\n\nstatic uint max(uint a, uint b) { return a > b ? a : b; }\nstatic uint min(uint a, uint b) { return a < b ? a : b; }\n\n// Decorations {{{\n#define STRAIGHT_UNDERLINE_LOOP \\\n    unsigned half = fcm.underline_thickness / 2; \\\n    DecorationGeometry ans = {.top = half > fcm.underline_position ? 0 : fcm.underline_position - half}; \\\n    for (unsigned y = ans.top; fcm.underline_thickness > 0 && y < fcm.cell_height; fcm.underline_thickness--, y++, ans.height++)\n\nDecorationGeometry\nadd_straight_underline(uint8_t *buf, FontCellMetrics fcm) {\n    STRAIGHT_UNDERLINE_LOOP {\n        memset(buf + fcm.cell_width * y, 0xff, fcm.cell_width * sizeof(buf[0]));\n    }\n    return ans;\n}\n\nDecorationGeometry\nadd_strikethrough(uint8_t *buf, FontCellMetrics fcm) {\n    unsigned half = fcm.strikethrough_thickness / 2;\n    DecorationGeometry ans = {.top = half > fcm.strikethrough_position ? 0 : fcm.strikethrough_position - half};\n    for (unsigned y = ans.top; fcm.strikethrough_thickness > 0 && y < fcm.cell_height; fcm.strikethrough_thickness--, y++, ans.height++) {\n        memset(buf + fcm.cell_width * y, 0xff, fcm.cell_width * sizeof(buf[0]));\n    }\n    return ans;\n}\n\n\nDecorationGeometry\nadd_missing_glyph(uint8_t *buf, FontCellMetrics fcm) {\n    DecorationGeometry ans = {.height=fcm.cell_height};\n    unsigned thickness = min(fcm.underline_thickness, fcm.strikethrough_thickness);\n    thickness = min(thickness, fcm.cell_width);\n    for (unsigned y = 0; y < ans.height; y++) {\n        uint8_t *line = buf + fcm.cell_width * y;\n        if (y < thickness || y >= ans.height - thickness) memset(line, 0xff, fcm.cell_width);\n        else {\n            memset(line, 0xff, thickness);\n            memset(line + fcm.cell_width - thickness, 0xff, thickness);\n        }\n    }\n    return ans;\n}\n\nDecorationGeometry\nadd_double_underline(uint8_t *buf, FontCellMetrics fcm) {\n    unsigned a = fcm.underline_position > fcm.underline_thickness ? fcm.underline_position - fcm.underline_thickness : 0;\n    a = min(a, fcm.cell_height - 1);\n    unsigned b = min(fcm.underline_position, fcm.cell_height - 1);\n    unsigned top = min(a, b), bottom = max(a, b);\n    int deficit = 2 - (bottom - top);\n    if (deficit > 0) {\n        if (bottom + deficit < fcm.cell_height) bottom += deficit;\n        else if (bottom < fcm.cell_height - 1) {\n            bottom += 1;\n            if (deficit > 1) top -= deficit - 1;\n        } else top -= deficit;\n    }\n    top = max(0u, min(top, fcm.cell_height - 1u));\n    bottom = max(0u, min(bottom, fcm.cell_height - 1u));\n    memset(buf + fcm.cell_width * top, 0xff, fcm.cell_width);\n    memset(buf + fcm.cell_width * bottom, 0xff, fcm.cell_width);\n    DecorationGeometry ans = {.top=top, .height = bottom + 1 - top};\n    return ans;\n}\n\nstatic unsigned\ndistribute_dots(unsigned available_space, unsigned num_of_dots, unsigned *summed_gaps, unsigned *gaps) {\n    unsigned dot_size = max(1u, available_space / (2u * num_of_dots));\n    unsigned extra = 2 * num_of_dots * dot_size;\n    extra = available_space > extra ? available_space - extra : 0;\n    for (unsigned i = 0; i < num_of_dots; i++) gaps[i] = dot_size;\n    if (extra > 0) {\n        unsigned idx = 0;\n        while (extra > 0) {\n            gaps[idx] += 1;\n            idx = (idx + 1) % num_of_dots;\n            extra--;\n        }\n    }\n    gaps[0] /= 2;\n    for (unsigned i = 0; i < num_of_dots; i++) {\n        summed_gaps[i] = 0;\n        for (unsigned g = 0; g <= i; g++) summed_gaps[i] += gaps[g];\n    }\n    return dot_size;\n}\n\nDecorationGeometry\nadd_dotted_underline(uint8_t *buf, FontCellMetrics fcm) {\n    unsigned num_of_dots = MAX(1u, fcm.cell_width / (2 * MAX(1u, fcm.underline_thickness)));\n    RAII_ALLOC(unsigned, spacing, malloc(num_of_dots * 2 * sizeof(unsigned)));\n    if (!spacing) fatal(\"Out of memory\");\n    unsigned size = distribute_dots(fcm.cell_width, num_of_dots, spacing, spacing + num_of_dots);\n    STRAIGHT_UNDERLINE_LOOP {\n        uint8_t *offset = buf + fcm.cell_width * y;\n        for (unsigned j = 0; j < num_of_dots; j++) {\n            unsigned s = spacing[j];\n            memset(offset + j * size + s, 0xff, size);\n        }\n    }\n    return ans;\n}\n\nDecorationGeometry\nadd_dashed_underline(uint8_t *buf, FontCellMetrics fcm) {\n    unsigned quarter_width = fcm.cell_width / 4;\n    unsigned dash_width = fcm.cell_width - 3 * quarter_width;\n    unsigned second_dash_start = 3 * quarter_width;\n    STRAIGHT_UNDERLINE_LOOP {\n        uint8_t *offset = buf + fcm.cell_width * y;\n        memset(offset, 0xff, dash_width);\n        memset(offset + second_dash_start, 0xff, dash_width);\n    }\n    return ans;\n}\n\nstatic unsigned\nadd_intensity(uint8_t *buf, unsigned x, int y, uint8_t val, unsigned max_y, unsigned position, unsigned cell_width) {\n    y += position;\n    y = min(MAX(0, y), max_y);\n    unsigned idx = cell_width * y + x;\n    buf[idx] = min(255, buf[idx] + val);\n    return y;\n}\n\nstatic uint\nminus(uint a, uint b) {  // saturating subtraction (a > b ? a - b : 0)\n    uint res = a - b;\n    res &= -(res <= a);\n    return res;\n}\n\nDecorationGeometry\nadd_curl_underline(uint8_t *buf, FontCellMetrics fcm) {\n    unsigned max_x = fcm.cell_width - 1, max_y = fcm.cell_height - 1;\n    double xfactor = ((OPT(undercurl_style) & 1) ? 4.0 : 2.0) * M_PI / max_x;\n    div_t d = div(fcm.underline_thickness, 2);\n    /*printf(\"cell_width: %u cell_height: %u underline_position: %u underline_thickness: %u\\n\",*/\n    /*        fcm.cell_width, fcm.cell_height, fcm.underline_position, fcm.underline_thickness);*/\n    unsigned position = min(fcm.underline_position, minus(fcm.cell_height, d.quot + d.rem));\n    unsigned thickness = max(1u, min(fcm.underline_thickness, minus(fcm.cell_height, position + 1)));\n    unsigned max_height = fcm.cell_height - minus(position, thickness / 2);  // descender from the font\n    unsigned half_height = max(1u, max_height / 4u);  // 4 so as to be not too large\n    if (OPT(undercurl_style) & 2) thickness = max(half_height, thickness);\n    else thickness = max(1u, thickness) - (thickness < 3u ? 1u : 2u);\n\n    position += half_height * 2;\n    if (position + half_height > max_y) position = max_y - half_height;\n    /*printf(\"position: %u half_height: %u thickness: %u\\n\", position, half_height, thickness);*/\n\n    unsigned miny = fcm.cell_height, maxy = 0;\n    // Use the Wu antialias algorithm to draw the curve\n    // cosine waves always have slope <= 1 so are never steep\n    for (unsigned x = 0; x < fcm.cell_width; x++) {\n        double y = half_height * cos(x * xfactor);\n        int y1 = (int)(floor(y - thickness)), y2 = (int)(ceil(y));\n        unsigned intensity = (unsigned)((255. * fabs(y - floor(y))));\n        unsigned i1 = 255 - intensity, i2 = intensity;\n        unsigned yc = add_intensity(buf, x, y1, i1, max_y, position, fcm.cell_width);  // upper bound\n        if (i1) { if (yc < miny) miny = yc; if (yc > maxy) maxy = yc; }\n        yc = add_intensity(buf, x, y2, i2, max_y, position, fcm.cell_width);  // lower bound\n        if (i2) { if (yc < miny) miny = yc; if (yc > maxy) maxy = yc; }\n        // fill between upper and lower bound\n        for (unsigned t = 1; t <= thickness; t++) add_intensity(buf, x, y1 + t, 255, max_y, position, fcm.cell_width);\n    }\n    DecorationGeometry ans = {.top=miny, .height=maxy-miny + 1};\n    return ans;\n}\n\nstatic void\nvert(uint8_t *ans, bool is_left_edge, double width_pt, double dpi_x, FontCellMetrics fcm) {\n    unsigned width = max(1u, min((unsigned)(round(width_pt * dpi_x / 72.0)), fcm.cell_width));\n    const unsigned left = is_left_edge ? 0 : (fcm.cell_width > width ? fcm.cell_width - width : 0);\n    for (unsigned y = 0; y < fcm.cell_height; y++) {\n        const unsigned offset = y * fcm.cell_width + left;\n        memset(ans + offset, 0xff, width);\n    }\n}\n\nstatic unsigned\nhorz(uint8_t *ans, bool is_top_edge, double height_pt, double dpi_y, FontCellMetrics fcm) {\n    unsigned height = max(1u, min((unsigned)(round(height_pt * dpi_y / 72.0)), fcm.cell_height));\n    const unsigned top = is_top_edge ? 0 : (fcm.cell_height > height ? fcm.cell_height - height : 0);\n    for (unsigned y = top; y < top + height; y++) {\n        const unsigned offset = y * fcm.cell_width;\n        memset(ans + offset, 0xff, fcm.cell_width);\n    }\n    return top;\n}\n\n\nDecorationGeometry\nadd_beam_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x) {\n    vert(buf, true, OPT(cursor_beam_thickness), dpi_x, fcm);\n    DecorationGeometry ans = {.height=fcm.cell_height};\n    return ans;\n}\n\nDecorationGeometry\nadd_underline_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_y) {\n    DecorationGeometry ans = {0};\n    ans.top = horz(buf, false, OPT(cursor_underline_thickness), dpi_y, fcm);\n    ans.height = fcm.cell_height - ans.top;\n    return ans;\n}\n\nDecorationGeometry\nadd_hollow_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x, double dpi_y) {\n    vert(buf, true, 1.0, dpi_x, fcm); vert(buf, false, 1.0, dpi_x, fcm);\n    horz(buf, true, 1.0, dpi_y, fcm); horz(buf, false, 1.0, dpi_y, fcm);\n    DecorationGeometry ans = {.height=fcm.cell_height};\n    return ans;\n}\n\n// }}}\n\ntypedef struct Range {\n    uint start, end;\n} Range;\n\ntypedef struct Limit { double upper, lower; } Limit;\ntypedef struct FloatPoint { double x, y; } FloatPoint;\n\ntypedef struct Canvas {\n    uint8_t *mask;\n    uint width, height, supersample_factor;\n    struct { double x, y; } dpi;\n    double scale;  // used to scale line thickness with font size for multicell rendering\n    Range *holes; uint holes_count, holes_capacity;\n    Limit *y_limits; uint y_limits_count, y_limits_capacity;\n} Canvas;\n\nstatic void\nfill_canvas(Canvas *self, int byte) { memset(self->mask, byte, sizeof(self->mask[0]) * self->width * self->height); }\n\nstatic void\nappend_hole(Canvas *self, Range hole) {\n    ensure_space_for(self, holes, self->holes[0], self->holes_count + 1, holes_capacity, self->width, false);\n    self->holes[self->holes_count++] = hole;\n}\n\nstatic void\nappend_limit(Canvas *self, double upper, double lower) {\n    ensure_space_for(self, y_limits, self->y_limits[0], self->y_limits_count + 1, y_limits_capacity, self->width, false);\n    self->y_limits[self->y_limits_count].upper = upper;\n    self->y_limits[self->y_limits_count++].lower = lower;\n}\n\nstatic double\nthickness_as_float(Canvas *self, uint level, bool horizontal) {\n    level = min(level, arraysz(OPT(box_drawing_scale)));\n    double pts = OPT(box_drawing_scale)[level];\n    double dpi = horizontal ? self->dpi.x : self->dpi.y;\n    return self->supersample_factor * self->scale * pts * dpi / 72.0;\n}\n\nstatic uint\nthickness(Canvas *self, uint level, bool horizontal) {\n    return (uint)ceil(thickness_as_float(self, level, horizontal));\n}\n\nstatic const uint hole_factor = 8;\n\nstatic void\nget_holes(Canvas *self, uint sz, uint hole_sz, uint num) {\n    uint all_holes_use = (num + 1) * hole_sz;\n    uint individual_block_size = max(1u, minus(sz, all_holes_use) / (num + 1));\n    uint half_hole_sz = hole_sz / 2;\n    int pos = - half_hole_sz;\n    while (pos < (int)sz) {\n        uint left = pos > 0 ? pos : 0;\n        uint right = min(sz, pos + hole_sz);\n        if (right > left) append_hole(self, (Range){left, right});\n        pos = right + individual_block_size;\n    }\n}\n\nstatic void\nadd_hholes(Canvas *self, uint level, uint num) {\n    uint line_sz = thickness(self, level, true);\n    uint hole_sz = self->width / hole_factor;\n    uint start = minus(self->height / 2, line_sz / 2);\n    get_holes(self, self->width, hole_sz, num);\n    for (uint y = 0; y < start + line_sz; y++) {\n        uint offset = y * self->width;\n        for (uint i = 0; i < self->holes_count; i++) memset(self->mask + offset + self->holes[i].start, 0, self->holes[i].end - self->holes[i].start);\n    }\n}\n\nstatic void\nadd_vholes(Canvas *self, uint level, uint num) {\n    uint line_sz = thickness(self, level, false);\n    uint hole_sz = self->height / hole_factor;\n    uint start = minus(self->width / 2, line_sz / 2);\n    get_holes(self, self->height, hole_sz, num);\n    for (uint i = 0; i < self->holes_count; i++) {\n        for (uint y = self->holes[i].start; y < self->holes[i].end; y++) {\n            uint offset = y * self->width;\n            memset(self->mask + offset + start, 0, line_sz);\n        }\n    }\n}\n\nstatic Range\nhline_limits(Canvas *self, uint y, uint level) {\n    uint sz = thickness(self, level, false);\n    Range r = {.start=minus(y, sz / 2)};\n    r.end = min(r.start + sz, self->height);\n    return r;\n}\n\nstatic void\ndraw_hline(Canvas *self, uint x1, uint x2, uint y, uint level) {\n    // Draw a horizontal line between [x1, x2) centered at y with the thickness given by level and self->supersample_factor\n    Range r = hline_limits(self, y, level);\n    for (uint y = r.start; y < r.end; y++) {\n        uint8_t *py = self->mask + y * self->width;\n        memset(py + x1, 255, minus(min(x2, self->width), x1));\n    }\n}\n\nstatic Range\nvline_limits(Canvas *self, uint x, uint level) {\n    uint sz = thickness(self, level, true);\n    Range r = {.start = minus(x, sz / 2)};\n    r.end = min(r.start + sz, self->width);\n    return r;\n}\n\nstatic void\ndraw_vline(Canvas *self, uint y1, uint y2, uint x, uint level) {\n    // Draw a vertical line between [y1, y2) centered at x with the thickness given by level and self->supersample_factor\n    Range r = vline_limits(self, x, level);\n    uint xsz = minus(r.end, r.start);\n    for (uint y = y1; y < min(y2, self->height); y++) {\n        uint8_t *py = self->mask + y * self->width;\n        memset(py + r.start, 255, xsz);\n    }\n}\n\nstatic uint\nhalf_width(Canvas *self) {  // align with non-supersampled co-ords\n    return self->supersample_factor * (self->width / 2 / self->supersample_factor);\n}\n\nstatic uint\nhalf_height(Canvas *self) { // align with non-supersampled co-ords\n    return self->supersample_factor * (self->height / 2 / self->supersample_factor);\n}\n\nstatic double\nunit_double(double x) {\n    return x < 0.0 ? 0.0 : (x > 1.0 ? 1.0 : x);\n}\n\nstatic double\nsmoothstep(double edge0, double edge1, double x) {\n    if (edge0 == edge1) return x < edge0 ? 0.0 : 1.0;\n    double t = unit_double((x - edge0) / (edge1 - edge0));\n    return t * t * (3.0 - 2.0 * t);\n}\n\nstatic void\nhalf_hline(Canvas *self, uint level, bool right_half, uint extend_by) {\n    uint x1, x2;\n    if (right_half) {\n        x1 = minus(half_width(self), extend_by); x2 = self->width;\n    } else {\n        x1 = 0; x2 = half_width(self) + extend_by;\n    }\n    draw_hline(self, x1, x2, half_height(self), level);\n}\n\ntypedef union Point {\n    struct {\n        int32_t x: 32, y: 32;\n    };\n    int64_t val;\n} Point;\n\n\nstatic Point\nhalf_dhline(Canvas *self, uint level, bool right_half, Edge which) {\n    uint x1 = 0, x2 = 0;\n    if (right_half) { x1 = self->width / 2; x2 = self->width; } else x2 = self->width / 2;\n    uint gap = thickness(self, level + 1, false);\n    Point ans = {.x=self->height / 2 - gap, .y=self->height / 2 + gap};\n    if (which & TOP_EDGE) draw_hline(self, x1, x2, ans.x, level);\n    if (which & BOTTOM_EDGE) draw_hline(self, x1, x2, ans.y, level);\n    return ans;\n}\n\nstatic Point\nhalf_dvline(Canvas *self, uint level, bool bottom_half, Edge which) {\n    uint y1 = 0, y2 = 0;\n    if (bottom_half) { y1 = self->height / 2; y2 = self->height; } else y2 = self->height / 2;\n    uint gap = thickness(self, level + 1, true);\n    Point ans = {.x=self->width / 2 - gap, .y=self->width / 2 + gap};\n    if (which & LEFT_EDGE) draw_vline(self, y1, y2, ans.x, level);\n    if (which & RIGHT_EDGE) draw_vline(self, y1, y2, ans.y, level);\n    return ans;\n}\n\nstatic Point\ndhline(Canvas *self, uint level, Edge which) {\n    half_dhline(self, level, false, which);\n    return half_dhline(self, level, true, which);\n}\n\nstatic Point\ndvline(Canvas *self, uint level, Edge which) {\n    half_dvline(self, level, false, which);\n    return half_dvline(self, level, true, which);\n}\n\n\nstatic void\nhalf_vline(Canvas *self, uint level, bool bottom_half, uint extend_by) {\n    uint y1, y2;\n    if (bottom_half) {\n        y1 = minus(half_height(self), extend_by); y2 = self->height;\n    } else {\n        y1 = 0; y2 = half_height(self) + extend_by;\n    }\n    draw_vline(self, y1, y2, half_width(self), level);\n}\n\nstatic void\nhline(Canvas *self, uint level) {\n    half_hline(self, level, false, 0);\n    half_hline(self, level, true, 0);\n}\n\nstatic void\nvline(Canvas *self, uint level) {\n    half_vline(self, level, false, 0);\n    half_vline(self, level, true, 0);\n}\n\nstatic void\nhholes(Canvas *self, uint level, uint num) {\n    hline(self, level);\n    add_hholes(self, level, num);\n}\n\nstatic void\nvholes(Canvas *self, uint level, uint num) {\n    vline(self, level);\n    add_vholes(self, level, num);\n}\n\nstatic uint8_t\nplus(uint8_t a, uint8_t b) {\n    uint8_t res = a + b;\n    res |= -(res < a);\n    return res;\n}\n\nstatic uint8_t\naverage_intensity(const Canvas *src, uint dest_x, uint dest_y) {\n    uint src_x = dest_x * src->supersample_factor, src_y = dest_y * src->supersample_factor;\n    uint total = 0;\n    for (uint y = src_y; y < src_y + src->supersample_factor; y++) {\n        uint offset = src->width * y;\n        for (uint x = src_x; x < src_x + src->supersample_factor; x++) total += src->mask[offset + x];\n    }\n    return (total / (src->supersample_factor * src->supersample_factor)) & 0xff;\n}\n\nstatic void\ndownsample(const Canvas *src, Canvas *dest) {\n    for (uint y = 0; y < dest->height; y++) {\n        uint offset = dest->width * y;\n        for (uint x = 0; x < dest->width; x++) {\n            dest->mask[offset + x] = plus(dest->mask[offset + x], average_intensity(src, x, y));\n        }\n    }\n}\n\ntypedef struct StraightLine {\n    double m, c;\n} StraightLine;\n\n\nstatic StraightLine\nline_from_points(double x1, double y1, double x2, double y2) {\n    StraightLine ans = {.m = (y2 - y1) / (x2 - x1)};\n    ans.c = y1 - ans.m * x1;\n    return ans;\n}\n\nstatic double\nline_y(StraightLine l, int x) {\n    return l.m * x + l.c;\n}\n\n#define calc_limits(self, lower_y, upper_y) { \\\n    if (!self->y_limits) { \\\n        self->y_limits_count = self->width; self->y_limits = malloc(sizeof(self->y_limits[0]) * self->y_limits_count); \\\n        if (!self->y_limits) fatal(\"Out of memory\"); \\\n    } \\\n    for (uint x = 0; x < self->width; x++) { self->y_limits[x].lower = lower_y; self->y_limits[x].upper = upper_y; } \\\n}\n\nstatic void\nfill_region(Canvas *self, bool inverted) {\n    uint8_t full = 0, empty = 0; if (inverted) empty = 255; else full = 255;\n    for (uint y = 0; y < self->height; y++) {\n        uint offset = y * self->width;\n        for (uint x = 0; x < self->width && x < self->y_limits_count; x++) {\n            self->mask[offset + x] = self->y_limits[x].lower <= y && y <= self->y_limits[x].upper ? full : empty;\n        }\n    }\n}\n\nstatic void\ntriangle(Canvas *self, bool left, bool inverted) {\n    int ay1 = 0, by1 = self->height - 1, y2 = self->height / 2, x1 = 0, x2 = 0;\n    if (left) x2 = self->width - 1; else x1 = self->width - 1;\n    StraightLine uppery = line_from_points(x1, ay1, x2, y2);\n    StraightLine lowery = line_from_points(x1, by1, x2, y2);\n    calc_limits(self, line_y(uppery, x), line_y(lowery, x));\n    fill_region(self, inverted);\n}\n\ntypedef enum Corner {\n    TOP_LEFT = LEFT_EDGE | TOP_EDGE, TOP_RIGHT = TOP_EDGE | RIGHT_EDGE,\n    BOTTOM_LEFT = BOTTOM_EDGE | LEFT_EDGE, BOTTOM_RIGHT = BOTTOM_EDGE | RIGHT_EDGE,\n} Corner;\n\nstatic void\nthick_line(Canvas *self, uint thickness_in_pixels, Point p1, Point p2) {\n    if (p1.x > p2.x) SWAP(p1, p2);\n    StraightLine l = line_from_points(p1.x, p1.y, p2.x, p2.y);\n    div_t d = div(thickness_in_pixels, 2);\n    int delta = d.quot, extra = d.rem;\n    for (int x = p1.x > 0 ? p1.x : 0; x < (int)self->width && x < p2.x + 1; x++) {\n        int y_p = (int)line_y(l, x);\n        for (int y = MAX(0, y_p - delta); y < MIN(y_p + delta + extra, (int)self->height); y++) {\n            self->mask[x + y * self->width] = 255;\n        }\n    }\n}\n\nstatic void\nframe(Canvas *self, uint level, Edge edges) {\n    uint h = thickness(self, level, true), v = thickness(self, level, false);\n#define line(x1, x2, y1, y2) { \\\n    for (uint y=y1; y < min(y2, self->height); y++) memset(self->mask + y * self->width + x1, 255, minus(min(x2, self->width), x1)); }\n#define hline(y1, y2) line(0, self->width, y1, y2)\n#define vline(x1, x2) line(x1, x2, 0, self->height)\n    if (edges & TOP_EDGE) hline(0, h + 1);\n    if (edges & BOTTOM_EDGE) hline(self->height - h - 1, self->height);\n    if (edges & LEFT_EDGE) vline(0, v + 1);\n    if (edges & RIGHT_EDGE) vline(self->width - v - 1, self->width);\n#undef hline\n#undef vline\n#undef line\n}\n\ntypedef enum Segment { LEFT, MIDDLE, RIGHT } Segment;\n\nstatic void\nprogress_bar(Canvas *self, Segment which, bool filled) {\n    const Edge edges = TOP_EDGE | BOTTOM_EDGE;\n    switch(which) {\n        case LEFT: frame(self, 1, LEFT_EDGE | edges); break;\n        case MIDDLE: frame(self, 1, edges); break;\n        case RIGHT: frame(self, 1, RIGHT_EDGE | edges); break;\n    }\n    if (!filled) return;\n    uint h = thickness(self, 1, true), v = thickness(self, 1, false);\n    static const uint gap_factor = 3;\n    uint y1 = gap_factor * h, y2 = minus(self->height, gap_factor*h), x1 = 0, x2 = 0;\n    switch(which) {\n        case LEFT: x1 = gap_factor * v; x2 = self->width; break;\n        case MIDDLE: x2 = self->width; break;\n        case RIGHT: x2 = minus(self->width, gap_factor * v); break;\n    }\n    for (uint y = y1; y < y2; y++) memset(self->mask + y * self->width + x1, 255, minus(min(x2, self->width), x1));\n}\n\nstatic void\nhalf_cross_line(Canvas *self, uint level, Corner corner) {\n    uint my = minus(self->height, 1) / 2; Point p1 = {0}, p2 = {0};\n    switch (corner) {\n        case TOP_LEFT: p2.x = minus(self->width, 1); p2.y = my; break;\n        case BOTTOM_LEFT: p1.x = minus(self->width, 1); p1.y = my; p2.y = self->height -1; break;\n        case TOP_RIGHT: p1.x = minus(self->width, 1); p2.y = my; break;\n        case BOTTOM_RIGHT: p2.x = minus(self->width, 1), p2.y = minus(self->height, 1); p1.y = my; break;\n    }\n    thick_line(self, thickness(self, level, true), p1, p2);\n}\n\nstatic void\ncross_line(Canvas *self, uint level, bool left) {\n    uint w = minus(self->width, 1), h = minus(self->height, 1);\n    Point p1 = {0}, p2 = {0};\n    if (left) p2 = (Point){.x=w, .y=h}; else { p1.x = w; p2.y = h; }\n    thick_line(self, thickness(self, level, true), p1, p2);\n}\n\ntypedef struct CubicBezier {\n    Point start, c1, c2, end;\n} CubicBezier;\n\n#define bezier_eq(which) { \\\n    const CubicBezier *cb = v; \\\n    const double u = 1. - t; \\\n    const double u_3 = u * u * u; \\\n    const double t_3 = t * t * t; \\\n    return u_3 * cb->start.which + 3 * t * u * (u * cb->c1.which + t * cb->c2.which) + t_3 * cb->end.which; \\\n}\n\n#define bezier_prime_eq(which) { \\\n    const CubicBezier *cb = v; \\\n    const double u = 1. - t; \\\n    const double u_2 = u * u; \\\n    const double t_2 = t * t; \\\n    return 3 * u_2 * (cb->c1.which - cb->start.which) + 6 * t * u * (cb->c2.which - cb->c1.which) + 3 * t_2 * (cb->end.which - cb->c2.which); \\\n}\n\nstatic double bezier_x(const void *v, double t) { bezier_eq(x); }\nstatic double bezier_y(const void *v, double t) { bezier_eq(y); }\nstatic double bezier_prime_x(const void *v, double t) { bezier_prime_eq(x); }\nstatic double bezier_prime_y(const void *v, double t) { bezier_prime_eq(y); }\n#undef bezier_eq\n#undef bezier_prime_eq\n\nstatic int\nfind_bezier_for_D(int width, int height) {\n    int cx = width - 1, last_cx = cx;\n    CubicBezier cb = {.end={.x=0, .y=height - 1}, .c2={.x=0, .y=height - 1}};\n    while (true) {\n        cb.c1.x = cx; cb.c2.x = cx;\n        if (bezier_x(&cb, 0.5) > width - 1) return last_cx;\n        last_cx = cx++;\n    }\n}\n\nstatic double\nfind_t_for_x(const CubicBezier *cb, int x, double start_t) {\n    if (fabs(bezier_x(cb, start_t) - x) < 0.1) return start_t;\n    static const double t_limit = 0.5;\n    double increment = t_limit - start_t;\n    if (increment <= 0) return start_t;\n    while (true) {\n        double q = bezier_x(cb, start_t + increment);\n        if (fabs(q - x) < 0.1) return start_t + increment;\n        if (q > x) {\n            increment /= 2.0;\n            if (increment < 1e-6) {\n                log_error(\"Failed to find cubic bezier t for x=%d\\n\", x);\n                return start_t;\n            }\n        } else {\n            start_t += increment;\n            increment = t_limit - start_t;\n            if (increment <= 0) return start_t;\n        }\n    }\n}\n\n\nstatic void\nget_bezier_limits(Canvas *self, const CubicBezier *cb) {\n    int start_x = (int)bezier_x(cb, 0), max_x = (int)bezier_x(cb, 0.5);\n    double last_t = 0.;\n    for (int x = start_x; x < max_x + 1; x++) {\n        if (x > start_x) last_t = find_t_for_x(cb, x, last_t);\n        double upper = bezier_y(cb, last_t), lower = bezier_y(cb, 1.0 - last_t);\n        if (fabs(upper - lower) <= 2.0) break;  // avoid pip on end of D\n        append_limit(self, lower, upper);\n    }\n}\n\n#define mirror_horizontally(expr) { \\\n    RAII_ALLOC(uint8_t, mbuf, calloc(self->width, self->height)); \\\n    if (!mbuf) fatal(\"Out of memory\"); \\\n    uint8_t *buf = self->mask; \\\n    self->mask = mbuf; \\\n    expr; \\\n    self->mask = buf; \\\n    for (uint y = 0; y < self->height; y++) { \\\n        uint offset = y * self->width; \\\n        for (uint src_x = 0; src_x < self->width; src_x++) { \\\n            uint dest_x = self->width - 1 - src_x; \\\n            buf[offset + dest_x] = mbuf[offset + src_x]; \\\n        } \\\n    } \\\n}\n\nstatic void\nfilled_D(Canvas *self, bool left) {\n    int c1x = find_bezier_for_D(self->width, self->height);\n    CubicBezier cb = {.end={.y=self->height-1}, .c1 = {.x=c1x}, .c2 = {.x=c1x, .y=self->height - 1}};\n    get_bezier_limits(self, &cb);\n    if (left) fill_region(self, false);\n    else mirror_horizontally(fill_region(self, false));\n}\n\ntypedef double(*curve_func)(const void *, double t);\n\n#define NAME position_set\n#define KEY_TY Point\n#define HASH_FN hash_point\n#define CMPR_FN cmpr_point\nstatic uint64_t hash_point(Point p);\nstatic bool cmpr_point(Point, Point);\n#include \"kitty-verstable.h\"\nstatic uint64_t hash_point(Point p) { return vt_hash_integer(p.val); }\nstatic bool cmpr_point(Point a, Point b) { return a.val == b.val; }\n\ntypedef struct ClipRect { uint left, top, x_end, y_end; } ClipRect;\n\nstatic void\ndraw_parametrized_curve_with_derivative_and_antialiasing(\n    Canvas *self, void *curve_data, double line_width, curve_func xfunc, curve_func yfunc,\n    curve_func x_prime, curve_func y_prime, double x_offset, double y_offset, const ClipRect *clip_to\n) {\n    line_width = fmax(1., line_width);\n    double half_thickness = line_width / 2.0;\n    uint i=0, larger_dim = MAX(self->height, self->width);\n    double t = 0;\n    double step = 1.0 / larger_dim;\n    uint cap = 2 * larger_dim;\n    const double min_step = step / 1000., max_step = step;\n    RAII_ALLOC(FloatPoint, samples, malloc(sizeof(FloatPoint) * cap));\n    if (!samples) fatal(\"Out of memory\");\n    ClipRect cr = clip_to ? *clip_to : (ClipRect){.x_end=self->width, .y_end=self->height};\n    while (true) {\n        samples[i] = (FloatPoint){xfunc(curve_data, t) + x_offset, yfunc(curve_data, t) + y_offset};\n        if (t >= 1.0) break;\n        // Dynamically adjust step size based on curve's derivative\n        double dx = x_prime(curve_data, t), dy = y_prime(curve_data, t);\n        double d = sqrt(dx * dx + dy * dy);\n        step = 1.0 / fmax(1e-6, d);\n        step = fmax(min_step, fmin(step, max_step));\n        t = fmin(t + step, 1.0);\n        i++;\n        if (i >= cap) {\n            cap *= 2;\n            samples = realloc(samples, sizeof(samples[0]) * cap);\n            if (!samples) fatal(\"Out of memory\");\n        }\n    }\n    const uint num_samples = i;\n    for (uint py = cr.top; py < cr.y_end; py++) {\n        uint ypos = self->width * py;\n        for (uint px = cr.left; px < cr.x_end; px++) {\n            // Center of the current pixel\n            double pixel_center_x = (double)px + 0.5;\n            double pixel_center_y = (double)py + 0.5;\n\n            double min_dist_sq = -1.0;\n\n            // Find the closest point on the curve to the pixel center by sampling the curve.\n            for (uint i = 0; i < num_samples; ++i) {\n                double dx = samples[i].x - pixel_center_x;\n                double dy = samples[i].y - pixel_center_y;\n                double dist_sq = dx * dx + dy * dy;\n                if (min_dist_sq < 0 || dist_sq < min_dist_sq) min_dist_sq = dist_sq;\n            }\n\n            double distance = sqrt(min_dist_sq);\n\n            // Calculate alpha based on the distance from the curve.\n            // This creates the anti-aliased edge. The distance from the center\n            // of the pixel to the edge of the stroke is used.\n            // We assume a pixel has a width of 1.0 for this calculation.\n            double alpha_unclamped = half_thickness - distance + 0.5;\n\n            uint offset = ypos + px;\n            uint8_t old_alpha = self->mask[offset];\n            double alpha = MAX(0.0, MIN(alpha_unclamped, 1.0));\n            self->mask[offset] = (uint8_t)(alpha * 255 + (1 - alpha) * old_alpha);  // alpha blend\n        }\n    }\n}\n\nstatic void\nrounded_separator(Canvas *self, uint level, bool left) {\n    uint gap = thickness(self, level, true);\n    int c1x = find_bezier_for_D(minus(self->width, gap), minus(self->height, gap));\n    uint half_gap = gap / 2;\n    CubicBezier cb = {.end={.y=minus(self->height,  1 + half_gap)}, .c1={.x=c1x},\n        .c2={.x=c1x, .y=minus(self->height, 1 + half_gap)}};\n    double line_width = thickness_as_float(self, level, true);\n#define d draw_parametrized_curve_with_derivative_and_antialiasing(\\\n        self, &cb, line_width, bezier_x, bezier_y, bezier_prime_x, bezier_prime_y, 0, half_gap, NULL)\n    if (left) { d; } else { mirror_horizontally(d); }\n#undef d\n}\n\nstatic void\ncorner_triangle(Canvas *self, const Corner corner) {\n    StraightLine diag;\n    const uint w = minus(self->width, 1), h = minus(self->height, 1);\n    bool top = corner == TOP_RIGHT || corner == TOP_LEFT;\n    if (corner == TOP_RIGHT || corner == BOTTOM_LEFT) diag = line_from_points(0, 0, w, h);\n    else diag = line_from_points(w, 0, 0, h);\n    for (uint x = 0; x < self->width; x++) {\n        if (top) append_limit(self, line_y(diag, x), 0);\n        else append_limit(self, h, line_y(diag, x));\n    }\n    fill_region(self, false);\n}\n\ntypedef struct Circle {\n    double x, y, radius;\n    double start, end, amt;\n} Circle;\n\nstatic Circle\ncircle(double x, double y, double radius, double start_at, double end_at) {\n    double conv = M_PI / 180.;\n    Circle ans = {.x=x, .y=y, .radius=radius, .start=start_at*conv, .end=end_at*conv};\n    ans.amt = ans.end - ans.start;\n    return ans;\n}\n\nstatic double circle_x(const void *v, double t) { const Circle *c=v; return c->x + c->radius * cos(c->start + c->amt * t); }\nstatic double circle_y(const void *v, double t) { const Circle *c=v; return c->y + c->radius * sin(c->start + c->amt * t); }\nstatic double circle_prime_x(const void *v, double t) { const Circle *c=v; return -c->radius * sin(c->start + c->amt * t); }\nstatic double circle_prime_y(const void *v, double t) { const Circle *c=v; return c->radius * cos(c->start + c->amt * t); }\n\nstatic void\nspinner(Canvas *self, uint level, double start_degrees, double end_degrees) {\n    double x = self->width / 2.0, y = self->height / 2.0;\n    double line_width = thickness_as_float(self, level, true);\n    double half_real_line_width = fmax(0.5, line_width / 2.0);\n    double radius = fmax(0, fmin(x, y) - half_real_line_width);\n    Circle c = circle(x, y, radius, start_degrees, end_degrees);\n    uint leftover = minus(self->height, 2*(uint)(ceil(radius) + half_real_line_width) + 1) / 2;\n    ClipRect cr = {.top=leftover, .y_end=self->height - leftover, .x_end=self->width};\n    draw_parametrized_curve_with_derivative_and_antialiasing(\n        self, &c, line_width, circle_x, circle_y, circle_prime_x, circle_prime_y, 0, 0, &cr);\n}\n\nstatic void\nfill_circle_of_radius(Canvas *self, double origin_x, double origin_y, double radius, uint8_t alpha) {\n    const double limit = radius * radius;\n    for (uint y = 0; y < self->height; y++) {\n        for (uint x = 0; x < self->width; x++) {\n            double xw = (double)x - origin_x, yh = (double)y - origin_y;\n            if (xw * xw + yh * yh <= limit) self->mask[y * self->width + x] = alpha;\n        }\n    }\n}\n\nstatic void\nfill_circle(Canvas *self, double scale, double gap, bool invert) {\n    const uint w = self->width / 2, h = self->height / 2;\n    const double radius = (int)(scale * min(w, h) - gap / 2);\n    const uint8_t fill = invert ? 0 : 255;\n    fill_circle_of_radius(self, w, h, radius, fill);\n}\n\nstatic void\ndraw_fish_eye(Canvas *self, uint level UNUSED) {\n    double x = self->width / 2., y = self->height / 2.;\n    double radius = fmin(x, y);\n    uint leftover = minus(self->height, 2*(uint)ceil(radius) + 1) / 2;\n    double central_radius = (2./3.) * radius;\n    fill_circle_of_radius(self, x, y, central_radius, 255);\n    double line_width = fmax(1. * self->supersample_factor, (radius - central_radius) / 2.5);\n    radius = fmax(0, fmin(x, y) - line_width / 2.);\n    Circle c = circle(x, y, radius, 0, 360);\n    ClipRect cr = {.top=leftover, .y_end=self->height - leftover, .x_end=self->width};\n    draw_parametrized_curve_with_derivative_and_antialiasing(\n            self, &c, line_width, circle_x, circle_y, circle_prime_x, circle_prime_y, 0, 0, &cr);\n}\n\nstatic void\ninner_corner(Canvas *self, uint level, Corner corner) {\n    uint hgap = thickness(self, level + 1, true), vgap = thickness(self, level + 1, false);\n    uint vthick = thickness(self, level, true) / 2;\n    uint x1 = 0, x2 = self->width, y1 = 0, y2 = self->height; int xd = 1, yd = 1;\n    if (corner & LEFT_EDGE) {\n        xd = -1;\n        Range vlinelimit = vline_limits(self, self->width / 2 + (xd * hgap), level);\n        x2 = vlinelimit.end;\n    } else x1 = minus(self->width / 2 + hgap, vthick);\n    if (corner & TOP_EDGE) {\n        y2 = minus(self->height / 2, vgap); yd = -1;\n    } else y1 = self->height / 2 + vgap;\n    draw_hline(self, x1, x2, self->height / 2 + (yd * vgap), level);\n    draw_vline(self, y1, y2, self->width / 2 + (xd * hgap), level);\n}\n\nstatic Range\nfourth_range(uint size, uint which) {\n    uint thickness = max(1, size / 4);\n    uint block = thickness * 4;\n    if (block == size) return (Range){.start=thickness * which, .end=thickness * (which + 1)};\n    if (block > size) {\n        uint start = min(which * thickness, minus(size, thickness));\n        return (Range){.start=start, .end=start + thickness};\n    }\n    uint extra = minus(size, block);\n    uint thicknesses[4] = {thickness, thickness, thickness, thickness};\n    uint pos = 0;\n    if (extra) {\n#define d(i) thicknesses[i]++; if (!--extra) goto done;\n        // ensures the thickness of first and last are least likely to be changed\n        d(1); d(2); d(3); d(0);\n#undef d\n    }\ndone:\n    for (uint i = 0; i < which; i++) pos += thicknesses[i];\n    return (Range){.start=pos, .end=pos + thicknesses[which]};\n}\n\n\nstatic Range\neight_range(uint size, uint which) {\n    uint thickness = max(1, size / 8);\n    uint block = thickness * 8;\n    if (block == size) return (Range){.start=thickness * which, .end=thickness * (which + 1)};\n    if (block > size) {\n        uint start = min(which * thickness, minus(size, thickness));\n        return (Range){.start=start, .end=start + thickness};\n    }\n    uint extra = minus(size, block);\n    uint thicknesses[8] = {thickness, thickness, thickness, thickness, thickness, thickness, thickness, thickness};\n    uint pos = 0;\n    if (extra) {\n#define d(i) thicknesses[i]++; if (!--extra) goto done;\n        // ensures the thickness of first and last are least likely to be changed\n        d(3); d(4); d(2); d(5); d(6); d(1); d(7); d(0);\n#undef d\n    }\ndone:\n    for (uint i = 0; i < which; i++) pos += thicknesses[i];\n    return (Range){.start=pos, .end=pos + thicknesses[which]};\n}\n\nstatic void\neight_bar(Canvas *self, uint which, bool horizontal) {\n    Range x_range, y_range;\n    if (horizontal) {\n        x_range = (Range){0, self->width};\n        y_range = eight_range(self->height, which);\n    } else {\n        y_range = (Range){0, self->height};\n        x_range = eight_range(self->width, which);\n    }\n    for (uint y = y_range.start; y < y_range.end; y++) {\n        uint offset = y * self->width;\n        memset(self->mask + offset + x_range.start, 255, minus(x_range.end, x_range.start));\n    }\n}\n\n\nstatic void\noctant_segment(Canvas *self, uint8_t which, bool left) {\n    Range x_range = left ? (Range){0, self->width / 2} : (Range){self->width/2, self->width};\n    Range y_range = fourth_range(self->height, which);\n    for (uint y = y_range.start; y < y_range.end; y++) {\n        uint offset = y * self->width;\n        memset(self->mask + offset + x_range.start, 255, minus(x_range.end, x_range.start));\n    }\n}\n\nstatic void\noctant(Canvas *self, uint8_t which) {\n    enum flags { a = 1, b = 2, c = 4, d = 8, m = 16, n = 32, o = 64, p = 128 };\n    static const enum flags mapping[232] = {\n        // 00 - 0f\n        b,     b|m,   a|b|m, n,       a|n,   a|m|n,   b|n,     a|b|n,     b|m|n, c,   a|c, c|m,   a|c|m, a|b|c, b|c|m, a|b|c|m,\n        // 10 - 1f\n        c|n,   a|c|n, c|m|n, a|c|m|n, b|c|n, a|b|c|n, b|c|m|n, a|b|c|m|n, o,     a|o, m|o, a|m|o, b|o,   a|b|o, b|m|o, a|b|m|o,\n        // 20 - 2f\n        a|n|o, m|n|o, a|m|n|o, b|n|o, a|b|n|o, b|m|n|o, a|b|m|n|o, c|o, a|c|o, c|m|o, a|c|m|o, b|c|o, a|b|c|o, b|c|m|o, a|b|c|m|o, c|n|o,\n        // 30 - 3f\n        a|c|n|o, c|m|n|o, a|c|m|n|o, b|c|n|o, a|b|c|n|o, b|c|m|n|o, a|d, d|m, a|d|m, b|d, a|b|d, b|d|m, a|b|d|m, d|n, a|d|n, d|m|n,\n        // 40 - 4f\n        a|d|m|n, b|d|n, a|b|d|n, b|d|m|n, a|b|d|m|n, a|c|d, c|d|m, a|c|d|m, b|c|d, b|c|d|m, a|b|c|d|m, c|d|n, a|c|d|n, a|c|d|m|n, b|c|d|n, a|b|c|d|n,\n        // 50 - 5f\n        b|c|d|m|n, d|o, a|d|o, d|m|o, a|d|m|o, b|d|o, a|b|d|o, b|d|m|o, a|b|d|m|o, d|n|o, a|d|n|o, d|m|n|o, a|d|m|n|o, b|d|n|o, a|b|d|n|o, b|d|m|n|o,\n        // 60 - 6f\n        ~(c|p), c|d|o, a|c|d|o, c|d|m|o, a|c|d|m|o, b|c|d|o, ~(m|n|p), b|c|d|m|o, ~(n|p), c|d|n|o, a|c|d|n|o, c|d|m|n|o, ~(b|p), b|c|d|n|o, ~(m|p), ~(a|p),\n        // 70 - 7f\n        ~p, a|p, m|p, a|m|p, b|p, a|b|p, b|m|p, a|b|m|p, n|p, a|n|p, m|n|p, a|m|n|p, b|n|p, a|b|n|p, b|m|n|p, ~(c|d|o),\n        // 80 - 8f\n        c|p, a|c|p, c|m|p, a|c|m|p, b|c|p, a|b|c|p, b|c|m|p, ~(d|n|o), c|n|p, a|c|n|p, c|m|n|p, ~(b|d|o), b|c|n|p, ~(d|m|o), ~(a|d|o), ~(d|o),\n        // 90 - 9f\n        a|o|p, m|o|p, a|m|o|p, b|o|p, b|m|o|p, a|b|m|o|p, n|o|p, a|n|o|p, a|m|n|o|p, b|n|o|p, a|b|n|o|p, b|m|n|o|p, c|o|p, a|c|o|p, c|m|o|p, a|c|m|o|p,\n        // a0 - af\n        b|c|o|p, a|b|c|o|p, b|c|m|o|p, ~(n|d), c|n|o|p, a|c|n|o|p, c|m|n|o|p, ~(b|d), b|c|n|o|p, ~(d|m), ~(a|d), ~d, a|d|p, d|m|p, a|d|m|p, b|d|p,\n        // b0 - bf\n        a|b|d|p, b|d|m|p, a|b|d|m|p, d|n|p, a|d|n|p, d|m|n|p, a|d|m|n|p, b|d|n|p, a|b|d|n|p, b|d|m|n|p, ~(c|o), c|d|p, a|c|d|p, c|d|m|p, a|c|d|m|p, b|c|d|p,\n\n        // c0 -cf\n        a|b|c|d|p, b|c|d|m|p, ~(n|o), c|d|n|p, a|c|d|n|p, c|d|m|n|p, ~(b|o), b|c|d|n|p, ~(m|o), ~(a|o), ~o, d|o|p, a|d|o|p, d|m|o|p, a|d|m|o|p, b|d|o|p,\n\n        // d0 - df\n        a|b|d|o|p, b|d|m|o|p, ~(c|n), d|n|o|p, a|d|n|o|p, d|m|n|o|p, ~(b|c), b|d|n|o|p, ~(c|m), ~(a|c), ~c, a|c|d|o|p, c|d|m|o|p, ~(b|n), b|c|d|o|p, ~(a|n),\n        // e0 - e7\n        ~n, c|d|n|o|p, ~(b|m), ~b, ~m, ~a, b|c, n|o,\n\n    };\n    which = mapping[which];\n    if (which & a) octant_segment(self, 0, true);\n    if (which & b) octant_segment(self, 1, true);\n    if (which & c) octant_segment(self, 2, true);\n    if (which & d) octant_segment(self, 3, true);\n    if (which & m) octant_segment(self, 0, false);\n    if (which & n) octant_segment(self, 1, false);\n    if (which & o) octant_segment(self, 2, false);\n    if (which & p) octant_segment(self, 3, false);\n\n}\n\nstatic void\neight_block(Canvas *self, int horizontal, ...) {\n    va_list args; va_start(args, horizontal);\n    int which;\n    while ((which = va_arg(args, int)) >= 0) eight_bar(self, which, horizontal);\n    va_end(args);\n}\n\ntypedef struct Shade {\n    bool light, invert, fill_blank;\n    Edge which_half;\n    uint xnum, ynum;\n} Shade;\n\n#define is_odd(x) ((x) & 1u)\n\nstatic void\nshade(Canvas *self, Shade s) {\n    const uint square_width = max(1, self->width / s.xnum);\n    const uint square_height = max(1, s.ynum ? (self->height / s.ynum) : square_width);\n    uint number_of_rows = self->height / square_height;\n    uint number_of_cols = self->width / square_width;\n\n    // Make sure the parity is correct\n    // (except when that would cause division by zero)\n    if (number_of_cols > 1 && is_odd(number_of_cols) != is_odd(s.xnum)) number_of_cols--;\n    if (number_of_rows > 1 && is_odd(number_of_rows) != is_odd(s.ynum)) number_of_rows--;\n\n    // Calculate how much space remains unused, and how frequently\n    // to insert an extra column/row to fill all of it\n    uint excess_cols = minus(self->width, square_width * number_of_cols);\n    double square_width_extension = (double)excess_cols / number_of_cols;\n\n    uint excess_rows = minus(self->height, square_height * number_of_rows);\n    double square_height_extension = (double)excess_rows / number_of_rows;\n\n    Range rows = {.end=number_of_rows}, cols = {.end=number_of_cols};\n    switch(s.which_half) {\n        // this is to remove gaps between half-filled characters\n        case TOP_EDGE: rows.end /= 2; square_height_extension *= 2; break;\n        case BOTTOM_EDGE: rows.start = number_of_rows / 2; square_height_extension *= 2; break;\n        case LEFT_EDGE: cols.end /= 2; square_width_extension *= 2; break;\n        case RIGHT_EDGE: cols.start = number_of_cols / 2; square_width_extension *= 2; break;\n    }\n\n    bool extra_row = false;\n    uint ey = 0, old_ey = 0, drawn_rows = 0;\n\n    for (uint r = rows.start; r < rows.end; r++) {\n        // Keep track of how much extra height has accumulated, and add an extra row at every passed integer, including 0\n        old_ey = ey;\n        ey = (uint)ceil(drawn_rows * square_height_extension);\n        extra_row = ey != old_ey;\n        drawn_rows += 1;\n        bool extra_col = false;\n        uint ex = 0, old_ex = 0, drawn_cols = 0;\n        for (uint c = cols.start; c < cols.end; c++) {\n            old_ex = ex;\n            ex = (uint)ceil(drawn_cols * square_width_extension);\n            extra_col = ex != old_ex;\n            drawn_cols += 1;\n\n            // Fill extra rows with semi-transparent pixels that match the pattern\n            if (extra_row) {\n                uint y = r * square_height + old_ey;\n                uint offset = self->width * y;\n                for (uint xc = 0; xc < square_width; xc++) {\n                    uint x = c * square_width + xc + ex;\n                    if (s.light) {\n                        if (s.invert) self->mask[offset + x] = is_odd(c) ? 255 : 70;\n                        else self->mask[offset + x] = is_odd(c) ? 0 : 70;\n                    } else self->mask[offset + x] = is_odd(c) == s.invert ? 120 : 30;\n                }\n            }\n            // Do the same for the extra columns\n            if (extra_col) {\n                uint x = c * square_width + old_ex;\n                for (uint yr = 0; yr < square_height; yr++) {\n                    uint y = r * square_height + yr + ey;\n                    uint offset = self->width * y;\n                    if (s.light) {\n                        if (s.invert) self->mask[offset + x] = is_odd(r) ? 255 : 70;\n                        else self->mask[offset + x] = is_odd(r) ? 0 : 70;\n                    } else self->mask[offset + x] = is_odd(r) == s.invert ? 120 : 30;\n                }\n            }\n            // And in case they intersect, set the corner pixel too\n            if (extra_row && extra_col) {\n                uint x = c * square_width + old_ex;\n                uint y = r * square_height + old_ey;\n                uint offset = self->width * y;\n                self->mask[offset + x] = 50;\n            }\n\n            const bool is_blank = s.invert ^ (is_odd(r) != is_odd(c) || (s.light && is_odd(r)));\n            if (!is_blank) {\n                // Fill the square\n                for (uint yr = 0; yr < square_height; yr++) {\n                    uint y = r * square_height + yr + ey;\n                    uint offset = self->width * y;\n                    for (uint xc = 0; xc < square_width; xc++) {\n                        uint x = c * square_width + xc + ex;\n                        self->mask[offset + x] = 255;\n                    }\n                }\n            }\n        }\n    }\n    if (!s.fill_blank) return;\n    cols = (Range){.end=self->width}; rows = (Range){.end=self->height};\n    switch(s.which_half) {\n        case BOTTOM_EDGE: rows.end = self->height / 2; break;\n        case TOP_EDGE: rows.start = minus(self->height / 2, 1); break;\n        case RIGHT_EDGE: cols.end = self->width / 2; break;\n        case LEFT_EDGE: cols.start = minus(self->width / 2, 1); break;\n    }\n    for (uint r = rows.start; r < rows.end; r++) memset(self->mask + r * self->width + cols.start, 255, cols.end - cols.start);\n}\n\nstatic void\napply_mask(Canvas *self, uint8_t *mask) {\n    for (uint y = 0; y < self->height; y++) {\n        uint offset = y * self->width;\n        for (uint x = 0; x < self->width; x++) {\n            uint p = offset + x;\n            self->mask[p] = (uint8_t)round((mask[p] / 255.0) * self->mask[p]);\n        }\n    }\n}\n\nstatic void\ncross_shade(Canvas *self, bool rotate) {\n    static const uint num_of_lines = 7;\n    uint line_thickness = max(self->supersample_factor, self->width / num_of_lines);\n    uint delta = 2 * line_thickness;\n    uint y1 = 0, y2 = self->height;\n    if (rotate) SWAP(y1, y2);\n    for (uint x = 0; x < self->width; x += delta) {\n        thick_line(self, line_thickness, (Point){.x=0 + x, .y=y1}, (Point){.x=self->width + x, .y=y2});\n        thick_line(self, line_thickness, (Point){.x=0 - x, .y=y1}, (Point){.x=self->width - x, .y=y2});\n    }\n}\n\nstatic void\nquad(Canvas *self, Corner which) {\n    uint x = which & LEFT_EDGE ? 0 : 1, y = which & TOP_EDGE ? 0 : 1;\n    uint num_cols = self->width / 2;\n    uint left = x * num_cols;\n    uint right = x ? self->width : num_cols;\n    uint num_rows = self->height / 2;\n    uint top = y * num_rows;\n    uint bottom = y ? self->height : num_rows;\n    for (uint r = top; r < bottom; r++) {\n        uint off = r * self->width;\n        memset(self->mask + off + left, 255, right - left);\n    }\n}\n\nstatic void\nquads(Canvas *self, ...) {\n    va_list args; va_start(args, self);\n    int which;\n    while ((which = va_arg(args, int))) quad(self, which);\n    va_end(args);\n}\n\nstatic void\nsmooth_mosaic(Canvas *self, bool lower, double ax, double ay, double bx, double by) {\n    StraightLine l = line_from_points(\n        ax * minus(self->width, 1), ay * minus(self->height, 1), bx * minus(self->width, 1), by * minus(self->height, 1));\n    for (uint y = 0; y < self->height; y++) {\n        uint offset = y * self->width;\n        for (uint x = 0; x < self->width; x++) {\n            double edge = line_y(l, x);\n            if ((lower && y >= edge) || (!lower && y <= edge)) self->mask[offset + x] = 255;\n        }\n    }\n}\n\nstatic void\nhalf_triangle(Canvas *self, Edge which, bool inverted) {\n    uint mid_x = self->width / 2, mid_y = self->height / 2;\n    StraightLine u, l;\n    append_limit(self, 0, 0); // ensure space for limits\n#define set_limits(startx, endx, a, b) for (uint x = startx; x < endx; x++) self->y_limits[x] = (Limit){.upper=b, .lower=a};\n    switch (which) {\n        case LEFT_EDGE:\n            u = line_from_points(0, 0, mid_x, mid_y);\n            l = line_from_points(0, minus(self->height, 1), mid_x, mid_y);\n            set_limits(0, self->width, line_y(u, x), line_y(l, x));\n            break;\n        case TOP_EDGE:\n            l = line_from_points(0, 0, mid_x, mid_y);\n            set_limits(0, mid_x, 0, line_y(l, x));\n            l = line_from_points(mid_x, mid_y, minus(self->width, 1), 0);\n            set_limits(mid_x, self->width, 0, line_y(l, x));\n            break;\n        case RIGHT_EDGE:\n            u = line_from_points(mid_x, mid_y, minus(self->width, 1), 0);\n            l = line_from_points(mid_x, mid_y, minus(self->width, 1), minus(self->height, 1));\n            set_limits(0, self->width, line_y(u, x), line_y(l, x));\n            break;\n        case BOTTOM_EDGE:\n            l = line_from_points(0, minus(self->height, 1), mid_x, mid_y);\n            set_limits(0, mid_x, line_y(l, x), minus(self->height, 1));\n            l = line_from_points(mid_x, mid_y, minus(self->width, 1), minus(self->height, 1));\n            set_limits(mid_x, self->width, line_y(l, x), minus(self->height, 1));\n            break;\n    }\n    self->y_limits_count = self->width;\n    fill_region(self, inverted);\n#undef set_limits\n}\n\nstatic void\nmid_lines(Canvas *self, uint level, ...) {\n    uint mid_x = self->width / 2, mid_y = self->height / 2;\n    const uint th = thickness(self, level, true);\n    const Point l = {.x=0, .y=mid_y}, t={.x=mid_x, .y=0}, r={.x=minus(self->width, 1), .y=mid_y}, b={.x=mid_x, .y=minus(self->height, 1)};\n    va_list args; va_start(args, level);\n    Corner which;\n    while ((which = va_arg(args, int)) > 0) {\n        Point p1, p2;\n        switch(which) {\n            case TOP_LEFT: p1 = l; p2 = t; break;\n            case TOP_RIGHT: p1 = r; p2 = t; break;\n            case BOTTOM_LEFT: p1 = l; p2 = b; break;\n            case BOTTOM_RIGHT: p1 = r; p2 = b; break;\n        }\n        thick_line(self, th, p1, p2);\n    }\n    va_end(args);\n}\n\nstatic Point*\nget_fading_lines(uint total_length, uint num, Edge fade) {\n    uint step = total_length / num, d1 = 0; int dir = 1;\n    if (fade == LEFT_EDGE || fade == TOP_EDGE) { dir = -1; d1 = total_length; }\n    Point *ans = malloc(num * sizeof(Point));\n    if (!ans) fatal(\"Out of memory\");\n    for (uint i = 0; i < num; i++) {\n        uint sz = step * (num - i) / (num + 1);\n        if (step > 2 && sz >= step - 1) sz = step - 2;\n        int d2 = d1 + dir * sz; if (d2 < 0) d2 = 0;\n        if (d1 <= (uint)d2) { ans[i].x = d1; ans[i].y = d2; }\n        else { ans[i].x = d2; ans[i].y = d1; }\n        d1 += step * dir;\n    }\n    return ans;\n}\n\nstatic void\nfading_hline(Canvas *self, uint level, uint num, Edge fade) {\n    uint y = self->height / 2;\n    RAII_ALLOC(Point, pts, get_fading_lines(self->width, num, fade));\n    for (uint i = 0; i < num; i++) {\n        uint x1 = pts[i].x, x2 = pts[i].y;\n        draw_hline(self, x1, x2, y, level);\n    }\n}\n\nstatic void\nfading_vline(Canvas *self, uint level, uint num, Edge fade) {\n    uint x = self->width / 2;\n    RAII_ALLOC(Point, pts, get_fading_lines(self->height, num, fade));\n    for (uint i = 0; i < num; i++) {\n        uint y1 = pts[i].x, y2 = pts[i].y;\n        draw_vline(self, y1, y2, x, level);\n    }\n}\n\nstatic void\nrounded_corner(Canvas *self, uint level, Corner which) {\n    // Render a rounded box corner.\n    const Range hori_line_range = hline_limits(self, half_height(self), level);\n    const Range vert_line_range = vline_limits(self, half_width(self), level);\n    const uint hori_line_height = hori_line_range.end - hori_line_range.start;\n    const uint vert_line_width = vert_line_range.end - vert_line_range.start;\n    const double adjusted_Hx = (double)vert_line_range.start + (double)vert_line_width / 2.0;\n    const double adjusted_Hy = (double)hori_line_range.start + (double)hori_line_height / 2.0;\n    const double stroke = (double)max(hori_line_height, vert_line_width);\n    const double corner_radius = fmin(adjusted_Hx, adjusted_Hy);\n    const double bx = adjusted_Hx - corner_radius;\n    const double by = adjusted_Hy - corner_radius;\n\n    // Anti-aliasing on corner\n    const double aa_corner = (double)self->supersample_factor * 0.5;\n    const double half_stroke = 0.5 * stroke;\n\n    const double x_shift = (which & RIGHT_EDGE) ? adjusted_Hx : -adjusted_Hx;\n    const double y_shift = (which & TOP_EDGE) ? -adjusted_Hy : adjusted_Hy;\n\n    for (uint y = 0; y < self->height; y++) {\n        const double sample_y = (double)y + y_shift + 0.5;\n        const double pos_y = sample_y - adjusted_Hy;\n        const uint row_off = y * self->width;\n\n        for (uint x = 0; x < self->width; x++) {\n            const double sample_x = (double)x + x_shift + 0.5;\n            const double pos_x = sample_x - adjusted_Hx;\n\n            const double qx = fabs(pos_x) - bx;\n            const double qy = fabs(pos_y) - by;\n            const double dx = qx > 0.0 ? qx : 0.0;\n            const double dy = qy > 0.0 ? qy : 0.0;\n            const double dist = hypot(dx, dy) + fmin(fmax(qx, qy), 0.0) - corner_radius;\n\n            const double aa = (qx > 1e-7 && qy > 1e-7) ? aa_corner : 0.0;\n            const double outer = half_stroke - dist;\n            const double inner = -half_stroke - dist;\n            const double alpha = smoothstep(-aa, aa, outer) - smoothstep(-aa, aa, inner);\n\n            if (alpha <= 0.0) continue;\n            const uint8_t value = (uint8_t)lrint(unit_double(alpha) * 255.0);\n            uint8_t *p = &self->mask[row_off + x];\n            if (value > *p) *p = value;\n        }\n    }\n}\n\nstatic void\ncommit(Canvas *self, Edge lines, bool solid) {\n    static const uint level = 1; static const double scale = 0.9;\n    uint hw = half_width(self), hh = half_height(self);\n    if (lines & RIGHT_EDGE) draw_hline(self, hw, self->width, hh, level);\n    if (lines & LEFT_EDGE) draw_hline(self, 0, hw, hh, level);\n    if (lines & TOP_EDGE) draw_vline(self, 0, hh, hw, level);\n    if (lines & BOTTOM_EDGE) draw_vline(self, hh, self->height, hw, level);\n    fill_circle(self, scale, 0, false);\n    if (!solid) fill_circle(self, scale, thickness(self, level, true), true);\n}\n\n// thin and fat line levels\n#define t 1u\n#define f 3u\n\nstatic void\ncorner(Canvas *self, uint hlevel, uint vlevel, Corner which) {\n    const uint v_thickness = thickness(self, vlevel, true);\n\n    uint v_half_tickness;\n    if (which & LEFT_EDGE && v_thickness % 2 != 0) {\n        v_half_tickness = v_thickness / 2 + 1;\n    } else {\n        v_half_tickness = v_thickness / 2;\n    }\n\n    half_hline(self, hlevel, which & RIGHT_EDGE, v_half_tickness);\n    half_vline(self, vlevel, which & BOTTOM_EDGE, 0);\n}\n\nstatic void\ncross(Canvas *self, uint which) {\n    static const uint level_map[16][4] = {\n        {t, t, t, t}, {f, t, t, t}, {t, f, t, t}, {f, f, t, t}, {t, t, f, t}, {t, t, t, f}, {t, t, f, f},\n        {f, t, f, t}, {t, f, f, t}, {f, t, t, f}, {t, f, t, f}, {f, f, f, t}, {f, f, t, f}, {f, t, f, f},\n        {t, f, f, f}, {f, f, f, f}\n    };\n    const uint *m = level_map[which];\n    half_hline(self, m[0], false, 0); half_hline(self, m[1], true, 0);\n    half_vline(self, m[2], false, 0); half_vline(self, m[3], true, 0);\n}\n\nstatic void\nvert_t(Canvas *self, uint base_char, uint variation) {\n    static const uint level_map[8][3] = {\n        {t, t, t}, {t, f, t}, {f, t, t}, {t, t, f}, {f, t, f}, {f, f, t}, {t, f, f}, {f, f, f}\n    };\n    const uint *m = level_map[variation];\n    half_vline(self, m[0], false, 0);\n    half_hline(self, m[1], base_char != L'┤', 0);\n    half_vline(self, m[2], true, 0);\n}\n\nstatic void\nhorz_t(Canvas *self, uint base_char, uint variation) {\n    static const uint level_map[8][3] = {\n        {t, t, t}, {f, t, t}, {t, f, t}, {f, f, t}, {t, t, f}, {f, t, f}, {t, f, f}, {f, f, f}\n    };\n    const uint *m = level_map[variation];\n    half_hline(self, m[0], false, 0);\n    half_hline(self, m[1], true, 0);\n    half_vline(self, m[2], base_char != L'┴', 0);\n}\n\nstatic void\ndvcorner(Canvas *self, uint level, Corner which) {\n    Point dline_position = half_dhline(self, level, which & LEFT_EDGE, TOP_EDGE | BOTTOM_EDGE);\n\n    if (which & BOTTOM_EDGE) {\n        Range bottom_limit = hline_limits(self, dline_position.y, level);\n        draw_vline(self, 0, bottom_limit.end, half_width(self), level);\n    } else {\n        Range top_limit = hline_limits(self, dline_position.x, level);\n        draw_vline(self, top_limit.start, self->height, half_width(self), level);\n    }\n}\n\nstatic void\ndhcorner(Canvas *self, uint level, Corner which) {\n    Point dline_position = half_dvline(self, level, which & TOP_EDGE, LEFT_EDGE | RIGHT_EDGE);\n\n    if (which & RIGHT_EDGE) {\n        Range right_limit = vline_limits(self, dline_position.y, level);\n        draw_hline(self, 0, right_limit.end, half_height(self), level);\n    } else {\n        Range left_limit = vline_limits(self, dline_position.x, level);\n        draw_hline(self, left_limit.start, self->width, half_height(self), level);\n    }\n}\n\nstatic void\ndcorner(Canvas *self, uint level, Corner which) {\n    uint hgap = thickness(self, level + 1, false);\n    uint vgap = thickness(self, level + 1, true);\n    uint x1 = self->width / 2, x2 = self->width / 2;\n    if (which & RIGHT_EDGE) x1 = 0; else x2 = self->width;\n    uint ypos = self->height / 2;\n    int ydelta = which & BOTTOM_EDGE ? hgap : -hgap;\n    if (which & RIGHT_EDGE) x2 += vgap; else x1 = minus(x1, vgap);\n    draw_hline(self, x1, x2, ypos + ydelta, level);\n    if (which & RIGHT_EDGE) x2 = minus(x2, 2 * vgap); else x1 += 2 * vgap;\n    draw_hline(self, x1, x2, ypos - ydelta, level);\n\n    uint xpos = self->width / 2;\n    int xdelta = (which & LEFT_EDGE) ? vgap : -vgap;\n    Range top_hline_limit = hline_limits(self, ypos + ydelta, level);\n    Range bottom_hline_limit = hline_limits(self, ypos - ydelta, level);\n    if (which & TOP_EDGE) {\n        draw_vline(self, top_hline_limit.start, self->height, xpos - xdelta, level);\n        draw_vline(self, bottom_hline_limit.start, self->height, xpos + xdelta, level);\n    } else {\n        draw_vline(self, 0, bottom_hline_limit.end, xpos + xdelta, level);\n        draw_vline(self, 0, top_hline_limit.end, xpos - xdelta, level);\n    }\n}\n\n\nstatic void\ndpip(Canvas *self, uint level, Edge which) {\n    uint x1, x2, y1, y2;\n    if (which & (LEFT_EDGE | RIGHT_EDGE)) {\n        Point p = dvline(self, level, LEFT_EDGE | RIGHT_EDGE);\n        if (which & LEFT_EDGE) { x1 = 0; x2 = p.x; } else { x1 = p.y; x2 =self->width; }\n        draw_hline(self, x1, x2, self->height / 2, level);\n    } else {\n        Point p = dhline(self, level, TOP_EDGE | BOTTOM_EDGE);\n        if (which & TOP_EDGE) { y1 = 0; y2 = p.x; } else { y1 = p.y; y2 = self->height; }\n        draw_vline(self, y1, y2, self->width / 2, level);\n    }\n}\n\nstatic void\nbraille_dot(Canvas *self, uint col, uint row) {\n    static const uint num_x_dots = 2, num_y_dots = 4;\n    unsigned x_gaps[num_x_dots * 2], y_gaps[num_y_dots * 2];\n    unsigned dot_width = distribute_dots(self->width, num_x_dots, x_gaps, x_gaps + num_x_dots);\n    unsigned dot_height = distribute_dots(self->height, num_y_dots, y_gaps, y_gaps + num_y_dots);\n    uint x_start = x_gaps[col] + col * dot_width;\n    uint y_start = y_gaps[row] + row * dot_height;\n    if (y_start < self->height && x_start < self->width) {\n        for (uint y = y_start; y < min(self->height, y_start + dot_height); y++) {\n            uint offset = y * self->width;\n            memset(self->mask + offset + x_start, 255, minus(min(self->width, x_start + dot_width), x_start));\n        }\n    }\n}\n\n\nstatic void\nbraille(Canvas *self, uint8_t which) {\n    if (!which) return;\n    for (uint8_t i = 0, mask = 1; i < 8; i++, mask <<= 1) {\n        if (which & mask) {\n            uint q = i + 1, col, row;\n            switch(q) { case 1: case 2: case 3: case 7: col = 0; break; default: col = 1; break; }\n            switch(q) { case 1: case 4: row = 0; break; case 2: case 5: row = 1; break; case 3: case 6: row = 2; break; default: row = 3; }\n            braille_dot(self, col, row);\n        }\n    }\n}\n\nstatic void\ndraw_sextant(Canvas *self, uint row, uint col) {\n    Point start = {0}, end = {.x=self->width, .y = self->height};\n    switch(row) {\n        case 0: end.y = self->height / 3; break;\n        case 1: start.y = self->height / 3; end.y = 2 * self->height / 3; break;\n        case 2: start.y = 2 * self->height / 3; break;\n    }\n    switch(col) {\n        case 0: end.x = self->width / 2; break;\n        default: start.x = self->width / 2; break;\n    }\n    for (int r = start.y; r < end.y; r++) {\n        uint off = r * self->width;\n        memset(self->mask + off + start.x, 255, end.x - start.x);\n    }\n}\n\nstatic void\nsextant(Canvas *self, uint which) {\n#define add_row(q, r) if (q & 1) { draw_sextant(self, r, 0); } if (q & 2) { draw_sextant(self, r, 1); }\n    add_row(which % 4, 0)\n    add_row(which / 4, 1)\n    add_row(which / 16, 2)\n#undef add_row\n}\n\nvoid\nrender_box_char(char_type ch, uint8_t *buf, unsigned width, unsigned height, double dpi_x, double dpi_y, double scale) {\n    Canvas canvas = {.mask=buf, .width = width, .height = height, .dpi={.x=dpi_x, .y=dpi_y}, .supersample_factor=1u, .scale=scale}, ss = canvas;\n    ss.mask = buf + width*height; ss.supersample_factor = SUPERSAMPLE_FACTOR;\n    ss.width *= SUPERSAMPLE_FACTOR; ss.height *= SUPERSAMPLE_FACTOR;\n    fill_canvas(&canvas, 0);\n    Canvas *c = &canvas;\n\n#define SB(ch, ...) case ch: fill_canvas(&ss, 0); c = &ss, __VA_ARGS__; downsample(&ss, &canvas);\n#define CC(ch, ...) case ch: __VA_ARGS__; break\n#define SS(ch, ...) SB(ch, __VA_ARGS__); break\n#define C(ch, func, ...) CC(ch, func(c, __VA_ARGS__))\n#define S(ch, func, ...) SS(ch, func(c, __VA_ARGS__))\nSTART_ALLOW_CASE_RANGE\n\n    switch(ch) {\n        default: log_error(\"Unknown box drawing character: U+%x rendered as blank\", ch); break;\n        case L'█': fill_canvas(c, 255); break;\n\n        C(L'─', hline, 1);\n        C(L'━', hline, 3);\n        C(L'│', vline, 1);\n        C(L'┃', vline, 3);\n\n        C(L'╌', hholes, 1, 1);\n        C(L'╍', hholes, 3, 1);\n        C(L'┄', hholes, 1, 2);\n        C(L'┅', hholes, 3, 2);\n        C(L'┈', hholes, 1, 3);\n        C(L'┉', hholes, 3, 3);\n\n        C(L'╎', vholes, 1, 1);\n        C(L'╏', vholes, 3, 1);\n        C(L'┆', vholes, 1, 2);\n        C(L'┇', vholes, 3, 2);\n        C(L'┊', vholes, 1, 3);\n        C(L'┋', vholes, 3, 3);\n\n        C(L'╴', half_hline, 1, false, 0);\n        C(L'╵', half_vline, 1, false, 0);\n        C(L'╶', half_hline, 1, true, 0);\n        C(L'╷', half_vline, 1, true, 0);\n        C(L'╸', half_hline, 3, false, 0);\n        C(L'╹', half_vline, 3, false, 0);\n        C(L'╺', half_hline, 3, true, 0);\n        C(L'╻', half_vline, 3, true, 0);\n        CC(L'╾', half_hline(c, 3, false, 0); half_hline(c, 1, true, 0));\n        CC(L'╼', half_hline(c, 1, false, 0); half_hline(c, 3, true, 0));\n        CC(L'╿', half_vline(c, 3, false, 0); half_vline(c, 1, true, 0));\n        CC(L'╽', half_vline(c, 1, false, 0); half_vline(c, 3, true, 0));\n\n        S(L'', triangle, true, false);\n        S(L'', triangle, true, true);\n        SS(L'', half_cross_line(c, 1, TOP_LEFT); half_cross_line(c, 1, BOTTOM_LEFT));\n        S(L'', triangle, false, false);\n        S(L'', triangle, false, true);\n        SS(L'', half_cross_line(c, 1, TOP_RIGHT); half_cross_line(c, 1, BOTTOM_RIGHT));\n\n        S(L'', filled_D, true);\n        S(L'◗', filled_D, true);\n        S(L'', filled_D, false);\n        S(L'◖', filled_D, false);\n        C(L'', rounded_separator, 1, true);\n        C(L'', rounded_separator, 1, false);\n\n        S(L'', cross_line, 1, true);\n        S(L'', cross_line, 1, true);\n        S(L'╲', cross_line, 1, true);\n        S(L'', cross_line, 1, false);\n        S(L'', cross_line, 1, false);\n        S(L'╱', cross_line, 1, false);\n        SS(L'╳', cross_line(c, 1, false); cross_line(c, 1, true));\n\n        S(L'', corner_triangle, BOTTOM_LEFT);\n        S(L'◣', corner_triangle, BOTTOM_LEFT);\n        S(L'', corner_triangle, BOTTOM_RIGHT);\n        S(L'◢', corner_triangle, BOTTOM_RIGHT);\n        S(L'', corner_triangle, TOP_LEFT);\n        S(L'◤', corner_triangle, TOP_LEFT);\n        S(L'', corner_triangle, TOP_RIGHT);\n        S(L'◥', corner_triangle, TOP_RIGHT);\n\n        C(L'', progress_bar, LEFT, false);\n        C(L'', progress_bar, MIDDLE, false);\n        C(L'', progress_bar, RIGHT, false);\n        C(L'', progress_bar, LEFT, true);\n        C(L'', progress_bar, MIDDLE, true);\n        C(L'', progress_bar, RIGHT, true);\n\n        C(L'', spinner, 1, 235, 305);\n        C(L'', spinner, 1, 270, 390);\n        C(L'', spinner, 1, 315, 470);\n        C(L'', spinner, 1, 360, 540);\n        C(L'', spinner, 1, 80, 220);\n        C(L'', spinner, 1, 170, 270);\n        C(L'○', spinner, 0, 0, 360);\n        C(L'◜', spinner, 1, 180, 270);\n        C(L'◝', spinner, 1, 270, 360);\n        C(L'◞', spinner, 1, 360, 450);\n        C(L'◟', spinner, 1, 450, 540);\n        C(L'◠', spinner, 1, 180, 360);\n        C(L'◡', spinner, 1, 0, 180);\n        S(L'●', fill_circle, 1.0, 0, false);\n        S(L'◉', draw_fish_eye, 0);\n\n        C(L'═', dhline, 1, TOP_EDGE | BOTTOM_EDGE);\n        C(L'║', dvline, 1, LEFT_EDGE | RIGHT_EDGE);\n        CC(L'╞', vline(c, 1); half_dhline(c, 1, true, TOP_EDGE | BOTTOM_EDGE));\n        CC(L'╡', vline(c, 1); half_dhline(c, 1, false, TOP_EDGE | BOTTOM_EDGE));\n        CC(L'╥', hline(c, 1); half_dvline(c, 1, true, LEFT_EDGE | RIGHT_EDGE));\n        CC(L'╨', hline(c, 1); half_dvline(c, 1, false, LEFT_EDGE | RIGHT_EDGE));\n        CC(L'╪', vline(c, 1); dhline(c, 1, TOP_EDGE | BOTTOM_EDGE));\n        CC(L'╫', hline(c, 1), dvline(c, 1, LEFT_EDGE | RIGHT_EDGE));\n        CC(L'╬', inner_corner(c, 1, TOP_LEFT); inner_corner(c, 1, TOP_RIGHT); inner_corner(c, 1, BOTTOM_LEFT); inner_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'╠', inner_corner(c, 1, TOP_RIGHT); inner_corner(c, 1, BOTTOM_RIGHT); dvline(c, 1, LEFT_EDGE));\n        CC(L'╣', inner_corner(c, 1, TOP_LEFT); inner_corner(c, 1, BOTTOM_LEFT); dvline(c, 1, RIGHT_EDGE));\n        CC(L'╦', inner_corner(c, 1, BOTTOM_LEFT); inner_corner(c, 1, BOTTOM_RIGHT); dhline(c, 1, TOP_EDGE));\n        CC(L'╩', inner_corner(c, 1, TOP_LEFT); inner_corner(c, 1, TOP_RIGHT); dhline(c, 1, BOTTOM_EDGE));\n\n#define EH(ch, ...) C(ch, eight_block, true, __VA_ARGS__, -1);\n        EH(L'▔', 0);\n        EH(L'▀', 0, 1, 2, 3);\n        EH(L'▁', 7);\n        EH(L'▂', 6, 7);\n        EH(L'▃', 5, 6, 7);\n        EH(L'▄', 4, 5, 6, 7);\n        EH(L'▅', 3, 4, 5, 6, 7);\n        EH(L'▆', 2, 3, 4, 5, 6, 7);\n        EH(L'▇', 1, 2, 3, 4, 5, 6, 7);\n#undef EH\n#define EV(ch, ...) C(ch, eight_block, false, __VA_ARGS__, -1);\n        EV(L'▉', 0, 1, 2, 3, 4, 5, 6);\n        EV(L'▊', 0, 1, 2, 3, 4, 5);\n        EV(L'▋', 0, 1, 2, 3, 4);\n        EV(L'▌', 0, 1, 2, 3);\n        EV(L'▍', 0, 1, 2);\n        EV(L'▎', 0, 1);\n        EV(L'▏', 0);\n        EV(L'▕', 7);\n        EV(L'▐', 4, 5, 6, 7);\n#undef EV\n#define SH(ch, ...) C(ch, shade, (Shade){ __VA_ARGS__ });\n        SH(L'░', .xnum=12, .light=true);\n        SH(L'▒', .xnum=12);\n        SH(L'▓', .xnum=12, .light=true, .invert=true);\n        SH(L'🮌', .xnum=12, .which_half=LEFT_EDGE);\n        SH(L'🮍', .xnum=12, .which_half=RIGHT_EDGE);\n        SH(L'🮎', .xnum=12, .which_half=TOP_EDGE);\n        SH(L'🮏', .xnum=12, .which_half=BOTTOM_EDGE);\n        SH(L'🮐', .xnum=12, .invert=true);\n        SH(L'🮑', .xnum=12, .invert=true, .fill_blank=true, .which_half=BOTTOM_EDGE);\n        SH(L'🮒', .xnum=12, .invert=true, .fill_blank=true, .which_half=TOP_EDGE);\n        SH(L'🮓', .xnum=12, .invert=true, .fill_blank=true, .which_half=RIGHT_EDGE);\n        SH(L'🮔', .xnum=12, .invert=true, .fill_blank=true, .which_half=LEFT_EDGE);\n        SH(L'🮕', .xnum=4, .ynum=4);\n        SH(L'🮖', .xnum=4, .ynum=4, .invert=true);\n        SH(L'🮗', .xnum=1, .ynum=4, .invert=true);\n#define M(ch, corner) SB(ch, corner_triangle(c, corner)); \\\n            memcpy(ss.mask, canvas.mask, sizeof(canvas.mask[0]) * canvas.width * canvas.height); \\\n            fill_canvas(&canvas, 0); shade(&canvas, (Shade){.xnum=12}); \\\n            apply_mask(&canvas, ss.mask); break;\n        M(L'🮜', TOP_LEFT);\n        M(L'🮝', TOP_RIGHT);\n        M(L'🮞', BOTTOM_RIGHT);\n        M(L'🮟', BOTTOM_LEFT);\n#undef M\n#undef SH\n        S(L'🮘', cross_shade, false);\n        S(L'🮙', cross_shade, true);\n\n        C(L'▖', quad, BOTTOM_LEFT);\n        C(L'▗', quad, BOTTOM_RIGHT);\n        C(L'▘', quad, TOP_LEFT);\n        C(L'▝', quad, TOP_RIGHT);\n        C(L'▙', quads, TOP_LEFT, BOTTOM_LEFT, BOTTOM_RIGHT, 0);\n        C(L'▚', quads, TOP_LEFT, BOTTOM_RIGHT, 0);\n        C(L'▛', quads, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, 0);\n        C(L'▜', quads, TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, 0);\n        C(L'▞', quads, TOP_RIGHT, BOTTOM_LEFT, 0);\n        C(L'▟', quads, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, 0);\n\n        S(L'🬼', smooth_mosaic, true, 0, 2. / 3, 0.5, 1);\n        S(L'🬽', smooth_mosaic, true, 0, 2. / 3, 1, 1);\n        S(L'🬾', smooth_mosaic, true, 0, 1. / 3, 0.5, 1);\n        S(L'🬿', smooth_mosaic, true, 0, 1. / 3, 1, 1);\n        S(L'🭀', smooth_mosaic, true, 0, 0, 0.5, 1);\n\n        S(L'🭁', smooth_mosaic, true, 0, 1. / 3, 0.5, 0);\n        S(L'🭂', smooth_mosaic, true, 0, 1. / 3, 1, 0);\n        S(L'🭃', smooth_mosaic, true, 0, 2. / 3, 0.5, 0);\n        S(L'🭄', smooth_mosaic, true, 0, 2. / 3, 1, 0);\n        S(L'🭅', smooth_mosaic, true, 0, 1, 0.5, 0);\n        S(L'🭆', smooth_mosaic, true, 0, 2. / 3, 1, 1. / 3);\n\n        S(L'🭇', smooth_mosaic, true, 0.5, 1, 1, 2. / 3);\n        S(L'🭈', smooth_mosaic, true, 0, 1, 1, 2. / 3);\n        S(L'🭉', smooth_mosaic, true, 0.5, 1, 1, 1. / 3);\n        S(L'🭊', smooth_mosaic, true, 0, 1, 1, 1. / 3);\n        S(L'🭋', smooth_mosaic, true, 0.5, 1, 1, 0);\n\n        S(L'🭌', smooth_mosaic, true, 0.5, 0, 1, 1. / 3);\n        S(L'🭍', smooth_mosaic, true, 0, 0, 1, 1. / 3);\n        S(L'🭎', smooth_mosaic, true, 0.5, 0, 1, 2. / 3);\n        S(L'🭏', smooth_mosaic, true, 0, 0, 1, 2. / 3);\n        S(L'🭐', smooth_mosaic, true, 0.5, 0, 1, 1);\n        S(L'🭑', smooth_mosaic, true, 0, 1. / 3, 1, 2. / 3);\n\n        S(L'🭒', smooth_mosaic, false, 0, 2. / 3, 0.5, 1);\n        S(L'🭓', smooth_mosaic, false, 0, 2. / 3, 1, 1);\n        S(L'🭔', smooth_mosaic, false, 0, 1. / 3, 0.5, 1);\n        S(L'🭕', smooth_mosaic, false, 0, 1. / 3, 1, 1);\n        S(L'🭖', smooth_mosaic, false, 0, 0, 0.5, 1);\n\n        S(L'🭗', smooth_mosaic, false, 0, 1. / 3, 0.5, 0);\n        S(L'🭘', smooth_mosaic, false, 0, 1. / 3, 1, 0);\n        S(L'🭙', smooth_mosaic, false, 0, 2. / 3, 0.5, 0);\n        S(L'🭚', smooth_mosaic, false, 0, 2. / 3, 1, 0);\n        S(L'🭛', smooth_mosaic, false, 0, 1, 0.5, 0);\n\n        S(L'🭜', smooth_mosaic, false, 0, 2. / 3, 1, 1. / 3);\n        S(L'🭝', smooth_mosaic, false, 0.5, 1, 1, 2. / 3);\n        S(L'🭞', smooth_mosaic, false, 0, 1, 1, 2. / 3);\n        S(L'🭟', smooth_mosaic, false, 0.5, 1, 1, 1. / 3);\n        S(L'🭠', smooth_mosaic, false, 0, 1, 1, 1. / 3);\n        S(L'🭡', smooth_mosaic, false, 0.5, 1, 1, 0);\n\n        S(L'🭢', smooth_mosaic, false, 0.5, 0, 1, 1. / 3);\n        S(L'🭣', smooth_mosaic, false, 0, 0, 1, 1. / 3);\n        S(L'🭤', smooth_mosaic, false, 0.5, 0, 1, 2. / 3);\n        S(L'🭥', smooth_mosaic, false, 0, 0, 1, 2. / 3);\n        S(L'🭦', smooth_mosaic, false, 0.5, 0, 1, 1);\n        S(L'🭧', smooth_mosaic, false, 0, 1. / 3, 1, 2. / 3);\n\n        S(L'🭨', half_triangle, LEFT_EDGE, true);\n        S(L'🭩', half_triangle, TOP_EDGE, true);\n        S(L'🭪', half_triangle, RIGHT_EDGE, true);\n        S(L'🭫', half_triangle, BOTTOM_EDGE, true);\n        S(L'🭬', half_triangle, LEFT_EDGE, false);\n        SS(L'🮛', half_triangle(c, LEFT_EDGE, false), half_triangle(c, RIGHT_EDGE, false));\n        S(L'🭭', half_triangle, TOP_EDGE, false);\n        S(L'🭮', half_triangle, RIGHT_EDGE, false);\n        S(L'🭯', half_triangle, BOTTOM_EDGE, false);\n        SS(L'🮚', half_triangle(c, BOTTOM_EDGE, false), half_triangle(c, TOP_EDGE, false));\n\n        CC(L'🭼', eight_bar(c, 0, false); eight_bar(c, 7, true));\n        CC(L'🭽', eight_bar(c, 0, false); eight_bar(c, 0, true));\n        CC(L'🭾', eight_bar(c, 7, false); eight_bar(c, 0, true));\n        CC(L'🭿', eight_bar(c, 7, false); eight_bar(c, 7, true));\n        CC(L'🮀', eight_bar(c, 0, true); eight_bar(c, 7, true));\n        CC(L'🮁', eight_bar(c, 0, true); eight_bar(c, 2, true); eight_bar(c, 4, true); eight_bar(c, 7, true));\n\n        C(L'🮂', eight_block, true, 0, 1, -1);\n        C(L'🮃', eight_block, true, 0, 1, 2, -1);\n        C(L'🮄', eight_block, true, 0, 1, 2, 3, 4, -1);\n        C(L'🮅', eight_block, true, 0, 1, 2, 3, 4, 5, -1);\n        C(L'🮆', eight_block, true, 0, 1, 2, 3, 4, 5, 6, -1);\n        C(L'🮇', eight_block, false, 6, 7, -1);\n        C(L'🮈', eight_block, false, 5, 6, 7, -1);\n        C(L'🮉', eight_block, false, 3, 4, 5, 6, 7, -1);\n        C(L'🮊', eight_block, false, 2, 3, 4, 5, 6, 7, -1);\n        C(L'🮋', eight_block, false, 1, 2, 3, 4, 5, 6, 7, -1);\n\n        S(L'🮠', mid_lines, 1, TOP_LEFT, 0);\n        S(L'🮡', mid_lines, 1, TOP_RIGHT, 0);\n        S(L'🮢', mid_lines, 1, BOTTOM_LEFT, 0);\n        S(L'🮣', mid_lines, 1, BOTTOM_RIGHT, 0);\n        S(L'🮤', mid_lines, 1, TOP_LEFT, BOTTOM_LEFT, 0);\n        S(L'🮥', mid_lines, 1, TOP_RIGHT, BOTTOM_RIGHT, 0);\n        S(L'🮦', mid_lines, 1, BOTTOM_RIGHT, BOTTOM_LEFT, 0);\n        S(L'🮧', mid_lines, 1, TOP_RIGHT, TOP_LEFT, 0);\n        S(L'🮨', mid_lines, 1, BOTTOM_RIGHT, TOP_LEFT, 0);\n        S(L'🮩', mid_lines, 1, BOTTOM_LEFT, TOP_RIGHT, 0);\n        S(L'🮪', mid_lines, 1, BOTTOM_LEFT, TOP_RIGHT, BOTTOM_RIGHT, 0);\n        S(L'🮫', mid_lines, 1, BOTTOM_LEFT, TOP_LEFT, BOTTOM_RIGHT, 0);\n        S(L'🮬', mid_lines, 1, TOP_RIGHT, TOP_LEFT, BOTTOM_RIGHT, 0);\n        S(L'🮭', mid_lines, 1, TOP_RIGHT, TOP_LEFT, BOTTOM_LEFT, 0);\n        S(L'🮮', mid_lines, 1, TOP_RIGHT, BOTTOM_RIGHT, TOP_LEFT, BOTTOM_LEFT, 0);\n\n        C(L'', hline, 1);\n        C(L'', vline, 1);\n        C(L'', fading_hline, 1, 4, RIGHT_EDGE);\n        C(L'', fading_hline, 1, 4, LEFT_EDGE);\n        C(L'', fading_vline, 1, 5, BOTTOM_EDGE);\n        C(L'', fading_vline, 1, 5, TOP_EDGE);\n\n        C(L'', rounded_corner, 1, TOP_LEFT);\n        C(L'', rounded_corner, 1, TOP_RIGHT);\n        C(L'', rounded_corner, 1, BOTTOM_LEFT);\n        C(L'', rounded_corner, 1, BOTTOM_RIGHT);\n\n        CC(L'', vline(c, 1); rounded_corner(c, 1, BOTTOM_LEFT));\n        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_LEFT));\n        CC(L'', rounded_corner(c, 1, BOTTOM_LEFT), rounded_corner(c, 1, TOP_LEFT));\n        CC(L'', vline(c, 1); rounded_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_RIGHT));\n        CC(L'', rounded_corner(c, 1, TOP_RIGHT), rounded_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_RIGHT));\n        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_LEFT));\n        CC(L'', rounded_corner(c, 1, TOP_LEFT), rounded_corner(c, 1, TOP_RIGHT));\n        CC(L'', hline(c, 1); rounded_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'', hline(c, 1); rounded_corner(c, 1, BOTTOM_LEFT));\n        CC(L'', rounded_corner(c, 1, BOTTOM_LEFT), rounded_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'', vline(c, 1); rounded_corner(c, 1, BOTTOM_LEFT), rounded_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_LEFT), rounded_corner(c, 1, TOP_RIGHT));\n        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_RIGHT), rounded_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'', hline(c, 1); rounded_corner(c, 1, BOTTOM_LEFT), rounded_corner(c, 1, TOP_LEFT));\n        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_LEFT), rounded_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'', vline(c, 1); rounded_corner(c, 1, TOP_RIGHT), rounded_corner(c, 1, BOTTOM_LEFT));\n        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_LEFT), rounded_corner(c, 1, BOTTOM_RIGHT));\n        CC(L'', hline(c, 1); rounded_corner(c, 1, TOP_RIGHT), rounded_corner(c, 1, BOTTOM_LEFT));\n\n#define P(ch, lines) S(ch, commit, lines, true); S(ch+1, commit, lines, false);\n        P(L'', 0);\n        P(L'', RIGHT_EDGE);\n        P(L'', LEFT_EDGE);\n        P(L'', LEFT_EDGE | RIGHT_EDGE);\n        P(L'', BOTTOM_EDGE);\n        P(L'', TOP_EDGE);\n        P(L'', BOTTOM_EDGE | TOP_EDGE);\n        P(L'', RIGHT_EDGE | BOTTOM_EDGE);\n        P(L'', LEFT_EDGE | BOTTOM_EDGE);\n        P(L'', RIGHT_EDGE | TOP_EDGE);\n        P(L'', LEFT_EDGE | TOP_EDGE);\n        P(L'', TOP_EDGE | BOTTOM_EDGE | RIGHT_EDGE);\n        P(L'', TOP_EDGE | BOTTOM_EDGE | LEFT_EDGE);\n        P(L'', LEFT_EDGE | RIGHT_EDGE | BOTTOM_EDGE);\n        P(L'', LEFT_EDGE | RIGHT_EDGE | TOP_EDGE);\n        P(L'', LEFT_EDGE | RIGHT_EDGE | TOP_EDGE | BOTTOM_EDGE);\n#undef P\n#define Q(ch, which) C(ch, corner, t, t, which); C(ch + 1, corner, f, t, which); C(ch + 2, corner, t, f, which); C(ch + 3, corner, f, f, which);\n        Q(L'┌', BOTTOM_RIGHT); Q(L'┐', BOTTOM_LEFT); Q(L'└', TOP_RIGHT); Q(L'┘', TOP_LEFT);\n#undef Q\n        C(L'╭', rounded_corner, 1, TOP_LEFT);\n        C(L'╮', rounded_corner, 1, TOP_RIGHT);\n        C(L'╰', rounded_corner, 1, BOTTOM_LEFT);\n        C(L'╯', rounded_corner, 1, BOTTOM_RIGHT);\n\n        case L'┼' ... L'┼' + 15: cross(c, ch - L'┼'); break;\n#define T(q, func) case q ... q + 7: func(c, q, ch - q); break;\n        T(L'├', vert_t); T(L'┤', vert_t);\n        T(L'┬', horz_t); T(L'┴', horz_t);\n#undef T\n        C(L'╒', dvcorner, 1, TOP_LEFT);\n        C(L'╕', dvcorner, 1, TOP_RIGHT);\n        C(L'╘', dvcorner, 1, BOTTOM_LEFT);\n        C(L'╛', dvcorner, 1, BOTTOM_RIGHT);\n        C(L'╓', dhcorner, 1, TOP_LEFT);\n        C(L'╖', dhcorner, 1, TOP_RIGHT);\n        C(L'╙', dhcorner, 1, BOTTOM_LEFT);\n        C(L'╜', dhcorner, 1, BOTTOM_RIGHT);\n        C(L'╔', dcorner, 1, TOP_LEFT);\n        C(L'╗', dcorner, 1, TOP_RIGHT);\n        C(L'╚', dcorner, 1, BOTTOM_LEFT);\n        C(L'╝', dcorner, 1, BOTTOM_RIGHT);\n        C(L'╟', dpip, 1, RIGHT_EDGE);\n        C(L'╢', dpip, 1, LEFT_EDGE);\n        C(L'╤', dpip, 1, BOTTOM_EDGE);\n        C(L'╧', dpip, 1, TOP_EDGE);\n\n        case 0x2800 ... 0x2800 + 255: braille(c, ch - 0x2800); break;\n        case 0x1fb00 ... 0x1fb00 + 19: sextant(c, ch - 0x1fb00 + 1); break;\n        case 0x1fb14 ... 0x1fb14 + 19: sextant(c, ch - 0x1fb00 + 2); break;\n        case 0x1fb28 ... 0x1fb28 + 19: sextant(c, ch - 0x1fb00 + 3); break;\n        case 0x1fb70 ... 0x1fb70 + 5: eight_bar(c, ch - 0x1fb6f, false); break;\n        case 0x1fb76 ... 0x1fb76 + 5: eight_bar(c, ch - 0x1fb75, true); break;\n        case 0x1fbe6: octant(c, 0xe6); break;\n        case 0x1fbe7: octant(c, 0xe7); break;\n        case 0x1cd00 ... 0x1cde5: octant(c, ch - 0x1cd00); break;\n    }\n    free(canvas.holes); free(canvas.y_limits);\n    free(ss.holes); free(ss.y_limits);\nEND_ALLOW_CASE_RANGE\n#undef CC\n#undef SS\n#undef C\n#undef S\n#undef SB\n#undef t\n#undef f\n}\n"
  },
  {
    "path": "kitty/decorations.h",
    "content": "/*\n * decorations.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\ntypedef struct DecorationGeometry {\n    uint32_t top, height;\n} DecorationGeometry;\n\nDecorationGeometry add_straight_underline(uint8_t *buf, FontCellMetrics fcm);\nDecorationGeometry add_double_underline(uint8_t *buf, FontCellMetrics fcm);\nDecorationGeometry add_dotted_underline(uint8_t *buf, FontCellMetrics fcm);\nDecorationGeometry add_dashed_underline(uint8_t *buf, FontCellMetrics fcm);\nDecorationGeometry add_curl_underline(uint8_t *buf, FontCellMetrics fcm);\nDecorationGeometry add_strikethrough(uint8_t *buf, FontCellMetrics fcm);\nDecorationGeometry add_missing_glyph(uint8_t *buf, FontCellMetrics fcm);\nDecorationGeometry add_beam_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x);\nDecorationGeometry add_underline_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_y);\nDecorationGeometry add_hollow_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x, double dpi_y);\nvoid render_box_char(char_type ch, uint8_t *buf, unsigned width, unsigned height, double dpi_x, double dpi_y, double scale);\n#define SUPERSAMPLE_FACTOR 4u\n"
  },
  {
    "path": "kitty/desktop.c",
    "content": "/*\n * desktop.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"state.h\"\n#include \"safe-wrappers.h\"\n#include \"cleanup.h\"\n#include \"loop-utils.h\"\n#include \"threading.h\"\n#include <dlfcn.h>\n\n#define FUNC(name, restype, ...) typedef restype (*name##_func)(__VA_ARGS__); static name##_func name = NULL\n#define LOAD_FUNC(handle, name) {\\\n    *(void **) (&name) = dlsym(handle, #name); \\\n    if (!name) { \\\n        const char* error = dlerror(); \\\n        if (error != NULL) { \\\n            PyErr_Format(PyExc_OSError, \"Failed to load the function %s with error: %s\", #name, error); dlclose(handle); handle = NULL; return NULL; \\\n        } \\\n    } \\\n}\n\nFUNC(sn_display_new, void*, void*, void*, void*);\nFUNC(sn_launchee_context_new_from_environment, void*, void*, int);\nFUNC(sn_launchee_context_new, void*, void*, int, const char*);\nFUNC(sn_display_unref, void, void*);\nFUNC(sn_launchee_context_setup_window, void, void*, int32_t);\nFUNC(sn_launchee_context_complete, void, void*);\nFUNC(sn_launchee_context_unref, void, void*);\n\nstatic void* libsn_handle = NULL;\n\nstatic PyObject*\ninit_x11_startup_notification(PyObject UNUSED *self, PyObject *args) {\n    static bool done = false;\n    if (!done) {\n        done = true;\n\n        const char* libnames[] = {\n#if defined(_KITTY_STARTUP_NOTIFICATION_LIBRARY)\n            _KITTY_STARTUP_NOTIFICATION_LIBRARY,\n#else\n            \"libstartup-notification-1.so\",\n            // some installs are missing the .so symlink, so try the full name\n            \"libstartup-notification-1.so.0\",\n            \"libstartup-notification-1.so.0.0.0\",\n#endif\n            NULL\n        };\n        for (int i = 0; libnames[i]; i++) {\n            libsn_handle = dlopen(libnames[i], RTLD_LAZY);\n            if (libsn_handle) break;\n        }\n        if (libsn_handle == NULL) {\n            PyErr_Format(PyExc_OSError, \"Failed to load %s with error: %s\", libnames[0], dlerror());\n            return NULL;\n        }\n        dlerror();    /* Clear any existing error */\n#define F(name) LOAD_FUNC(libsn_handle, name)\n        F(sn_display_new);\n        F(sn_launchee_context_new_from_environment);\n        F(sn_launchee_context_new);\n        F(sn_display_unref);\n        F(sn_launchee_context_setup_window);\n        F(sn_launchee_context_complete);\n        F(sn_launchee_context_unref);\n#undef F\n    }\n\n    int window_id;\n    PyObject *dp;\n    char *startup_id = NULL;\n    if (!PyArg_ParseTuple(args, \"O!i|z\", &PyLong_Type, &dp, &window_id, &startup_id)) return NULL;\n    void* display = PyLong_AsVoidPtr(dp);\n    void* sn_display = sn_display_new(display, NULL, NULL);\n    if (!sn_display) { PyErr_SetString(PyExc_OSError, \"Failed to create SnDisplay\"); return NULL; }\n    void *ctx = startup_id ? sn_launchee_context_new(sn_display, 0, startup_id) : sn_launchee_context_new_from_environment(sn_display, 0);\n    sn_display_unref(sn_display);\n    if (!ctx) { PyErr_SetString(PyExc_OSError, \"Failed to create startup-notification context\"); return NULL; }\n    sn_launchee_context_setup_window(ctx, window_id);\n    return PyLong_FromVoidPtr(ctx);\n}\n\nstatic PyObject*\nend_x11_startup_notification(PyObject UNUSED *self, PyObject *args) {\n    if (!libsn_handle) Py_RETURN_NONE;\n    PyObject *dp;\n    if (!PyArg_ParseTuple(args, \"O!\", &PyLong_Type, &dp)) return NULL;\n    void *ctx = PyLong_AsVoidPtr(dp);\n    sn_launchee_context_complete(ctx);\n    sn_launchee_context_unref(ctx);\n\n    Py_RETURN_NONE;\n}\n\nstatic void* libcanberra_handle = NULL;\nstatic void *canberra_ctx = NULL;\nFUNC(ca_context_create, int, void**);\nFUNC(ca_context_destroy, int, void*);\nFUNC(ca_context_play_full, int, void*, uint32_t, void*, void(*)(void), void*);\ntypedef int (*ca_context_play_func)(void*, uint32_t, ...); static ca_context_play_func ca_context_play = NULL;\ntypedef int (*ca_context_change_props_func)(void*, ...); static ca_context_change_props_func ca_context_change_props = NULL;\n\nstatic PyObject*\nload_libcanberra_functions(void) {\n    LOAD_FUNC(libcanberra_handle, ca_context_create);\n    LOAD_FUNC(libcanberra_handle, ca_context_play);\n    LOAD_FUNC(libcanberra_handle, ca_context_play_full);\n    LOAD_FUNC(libcanberra_handle, ca_context_destroy);\n    LOAD_FUNC(libcanberra_handle, ca_context_change_props);\n    return NULL;\n}\n\nstatic void\nload_libcanberra(void) {\n    static bool done = false;\n    if (done) return;\n    done = true;\n    const char* libnames[] = {\n#if defined(_KITTY_CANBERRA_LIBRARY)\n        _KITTY_CANBERRA_LIBRARY,\n#else\n        \"libcanberra.so\",\n        // some installs are missing the .so symlink, so try the full name\n        \"libcanberra.so.0\",\n        \"libcanberra.so.0.2.5\",\n#endif\n        NULL\n    };\n    for (int i = 0; libnames[i]; i++) {\n        libcanberra_handle = dlopen(libnames[i], RTLD_LAZY);\n        if (libcanberra_handle) break;\n    }\n    if (libcanberra_handle == NULL) {\n        fprintf(stderr, \"Failed to load %s, cannot play beep sound, with error: %s\\n\", libnames[0], dlerror());\n        return;\n    }\n    load_libcanberra_functions();\n    if (PyErr_Occurred()) {\n        PyErr_Print();\n        dlclose(libcanberra_handle); libcanberra_handle = NULL;\n        return;\n    }\n    if (ca_context_create(&canberra_ctx) != 0) {\n        fprintf(stderr, \"Failed to create libcanberra context, cannot play beep sound\\n\");\n        canberra_ctx = NULL;\n        dlclose(libcanberra_handle); libcanberra_handle = NULL;\n    } else {\n        if (ca_context_change_props(canberra_ctx, \"application.name\", \"kitty Terminal\", \"application.id\", \"kitty\", NULL) != 0) {\n            fprintf(stderr, \"Failed to set basic properties on libcanberra context, cannot play beep sound\\n\");\n        }\n    }\n}\n\ntypedef struct {\n    char *which_sound, *event_id, *media_role, *theme_name;\n    bool is_path;\n} CanberraEvent;\n\nstatic pthread_t canberra_thread;\nstatic int canberra_pipe_r = -1, canberra_pipe_w = -1;\nstatic pthread_mutex_t canberra_lock;\nstatic CanberraEvent current_sound = {0};\n\nstatic void\nfree_canberra_event_fields(CanberraEvent *e) {\n    free(e->which_sound); e->which_sound = NULL;\n    free(e->event_id); e->event_id = NULL;\n    free(e->media_role); e->media_role = NULL;\n    free(e->theme_name); e->theme_name = NULL;\n}\n\nstatic void\nplay_current_sound(void) {\n    CanberraEvent e;\n    pthread_mutex_lock(&canberra_lock);\n    e = current_sound;\n    current_sound = (const CanberraEvent){ 0 };\n    pthread_mutex_unlock(&canberra_lock);\n    if (e.which_sound && e.event_id && e.media_role) {\n        const char *which_type = e.is_path ? \"media.filename\" : \"event.id\";\n        ca_context_play(\n            canberra_ctx, 0,\n            which_type, e.which_sound,\n            \"event.description\", e.event_id,\n            \"media.role\", e.media_role,\n            \"canberra.xdg-theme.name\", e.theme_name,\n            NULL\n        );\n        free_canberra_event_fields(&e);\n    }\n}\n\nstatic void\nqueue_canberra_sound(const char *which_sound, const char *event_id, bool is_path, const char *media_role, const char *theme_name) {\n    pthread_mutex_lock(&canberra_lock);\n    current_sound.which_sound = strdup(which_sound);\n    current_sound.event_id = strdup(event_id);\n    current_sound.media_role = strdup(media_role);\n    current_sound.is_path = is_path;\n    current_sound.theme_name = theme_name ? strdup(theme_name) : NULL;\n    pthread_mutex_unlock(&canberra_lock);\n    while (true) {\n        ssize_t ret = write(canberra_pipe_w, \"w\", 1);\n        if (ret < 0) {\n            if (errno == EINTR) continue;\n            log_error(\"Failed to write to canberra wakeup fd with error: %s\", strerror(errno));\n        }\n        break;\n    }\n}\n\nstatic void*\ncanberra_play_loop(void *x UNUSED) {\n    // canberra hangs on misconfigured systems. We dont want kitty to hang so use a thread.\n    // For example: https://github.com/kovidgoyal/kitty/issues/5646\n    static char buf[16];\n    set_thread_name(\"LinuxAudioSucks\");\n    while (true) {\n        int ret = read(canberra_pipe_r, buf, sizeof(buf));\n        if (ret < 0) {\n            if (errno == EINTR || errno == EAGAIN) continue;\n            break;\n        }\n        play_current_sound();\n    }\n    safe_close(canberra_pipe_r, __FILE__, __LINE__);\n    return NULL;\n}\n\nvoid\nplay_canberra_sound(const char *which_sound, const char *event_id, bool is_path, const char *media_role, const char *theme_name) {\n    load_libcanberra();\n    if (libcanberra_handle == NULL || canberra_ctx == NULL) return;\n    int ret;\n    if (canberra_pipe_r == -1) {\n        int fds[2];\n        if ((ret = pthread_mutex_init(&canberra_lock, NULL)) != 0) return;\n        if (!self_pipe(fds, false)) return;\n        canberra_pipe_r = fds[0]; canberra_pipe_w = fds[1];\n        int flags = fcntl(canberra_pipe_w, F_GETFL);\n        fcntl(canberra_pipe_w, F_SETFL, flags | O_NONBLOCK);\n        if ((ret = pthread_create(&canberra_thread, NULL, canberra_play_loop, NULL)) != 0) return;\n    }\n    queue_canberra_sound(which_sound, event_id, is_path, media_role, theme_name);\n}\n\nstatic PyObject*\nplay_desktop_sound_async(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    const char *which, *event_id = \"test sound\";\n    const char *theme_name = OPT(bell_theme);\n    if (!theme_name || !theme_name[0]) theme_name = \"__custom\";\n    int is_path = 0;\n    static const char* kwlist[] = {\"sound_name\", \"event_id\", \"is_path\", \"theme_name\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"s|sps\", (char**)kwlist, &which, &event_id, &is_path, &theme_name)) return NULL;\n    play_canberra_sound(which, event_id, is_path, \"event\", theme_name);\n    Py_RETURN_NONE;\n}\n\nstatic void\nfinalize(void) {\n    if (libsn_handle) dlclose(libsn_handle);\n    libsn_handle = NULL;\n    if (canberra_pipe_w > -1) {\n        pthread_mutex_lock(&canberra_lock);\n        free_canberra_event_fields(&current_sound);\n        pthread_mutex_unlock(&canberra_lock);\n        safe_close(canberra_pipe_w, __FILE__, __LINE__);\n    }\n    if (canberra_ctx) ca_context_destroy(canberra_ctx);\n    canberra_ctx = NULL;\n    if (libcanberra_handle) dlclose(libcanberra_handle);\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(init_x11_startup_notification, METH_VARARGS),\n    METHODB(end_x11_startup_notification, METH_VARARGS),\n    {\"play_desktop_sound_async\", (PyCFunction)(void(*)(void))play_desktop_sound_async, METH_VARARGS | METH_KEYWORDS, \"\"},\n\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\n\nbool\ninit_desktop(PyObject *m) {\n    if (PyModule_AddFunctions(m, module_methods) != 0) return false;\n    register_at_exit_cleanup_func(DESKTOP_CLEANUP_FUNC, finalize);\n    return true;\n}\n"
  },
  {
    "path": "kitty/disk-cache.c",
    "content": "/*\n * disk-cache.c\n * Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define MAX_KEY_SIZE 16u\n\n#include \"disk-cache.h\"\n#include \"safe-wrappers.h\"\n#include \"simd-string.h\"\n#include \"loop-utils.h\"\n#include \"fast-file-copy.h\"\n#include \"threading.h\"\n#include \"cross-platform-random.h\"\n#include <structmember.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <time.h>\n\ntypedef struct CacheKey {\n    void *hash_key;\n    unsigned short hash_keylen;\n} CacheKey;\n\ntypedef struct {\n    uint8_t *data;\n    size_t data_sz;\n    bool written_to_disk, uses_encryption;\n    off_t pos_in_cache_file;\n    uint8_t encryption_key[64];\n} CacheValue;\n\n#define NAME cache_map\n#define KEY_TY CacheKey\n#define VAL_TY CacheValue*\nstatic uint64_t key_hash(KEY_TY k);\n#define HASH_FN key_hash\nstatic bool keys_are_equal(CacheKey a, CacheKey b) { return a.hash_keylen == b.hash_keylen && memcmp(a.hash_key, b.hash_key, a.hash_keylen) == 0; }\n#define CMPR_FN keys_are_equal\nstatic void free_cache_value(CacheValue *cv) { free(cv->data); cv->data = NULL; free(cv); }\nstatic void free_cache_key(CacheKey cv) { free(cv.hash_key); cv.hash_key = NULL; }\n#define KEY_DTOR_FN free_cache_key\n#define VAL_DTOR_FN free_cache_value\n#include \"kitty-verstable.h\"\nstatic uint64_t key_hash(CacheKey k) { return vt_hash_bytes(k.hash_key, k.hash_keylen); }\n#define cache_map_for_loop(i) vt_create_for_loop(cache_map_itr, i, &self->map)\n\ntypedef struct Hole {\n    off_t pos, size;\n} Hole;\n\n#define NAME hole_pos_map\n#define KEY_TY off_t\n#define VAL_TY off_t\n#include \"kitty-verstable.h\"\n#define hole_pos_map_for_loop(i) vt_create_for_loop(hole_pos_map_itr, i, &self->holes.pos_map)\n\ntypedef struct PosList { size_t count, capacity; off_t *positions; } PosList;\n#define NAME hole_size_map\n#define KEY_TY off_t\n#define VAL_TY PosList\nstatic void free_pos_list(PosList p) { free(p.positions); }\n#define VAL_DTOR_FN free_pos_list\n#include \"kitty-verstable.h\"\n#define hole_size_map_for_loop(i) vt_create_for_loop(hole_size_map_itr, i, &holes->size_map)\n\ntypedef struct Holes {\n    hole_pos_map pos_map, end_pos_map;\n    hole_size_map size_map;\n    off_t largest_hole_size;\n} Holes;\n\ntypedef struct {\n    PyObject_HEAD\n    char *cache_dir;\n    int cache_file_fd;\n    Py_ssize_t small_hole_threshold;\n    unsigned int defrag_factor;\n    pthread_mutex_t lock;\n    pthread_t write_thread;\n    bool thread_started, lock_inited, loop_data_inited, shutting_down, fully_initialized;\n    LoopData loop_data;\n    struct { CacheValue val; CacheKey key; } currently_writing;\n    cache_map map;\n    Holes holes;\n    unsigned long long total_size;\n    off_t end_of_data_offset;\n    bool needs_encryption;\n} DiskCache;\n\n#define mutex(op) pthread_mutex_##op(&self->lock)\n\nstatic PyObject*\nnew_diskcache_object(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {\n    DiskCache *self;\n    self = (DiskCache*)type->tp_alloc(type, 0);\n    if (self) {\n        self->cache_file_fd = -1;\n        self->small_hole_threshold = 512;\n        self->defrag_factor = 2;\n        self->needs_encryption = true;\n    }\n    return (PyObject*) self;\n}\n\nstatic int\nopen_cache_file_without_tmpfile(const char *cache_path) {\n    int fd = -1;\n    static const char template[] = \"%s/disk-cache-XXXXXXXXXXXX\";\n    const size_t sz = strlen(cache_path) + sizeof(template) + 4;\n    RAII_ALLOC(char, buf, calloc(1, sz));\n    if (!buf) { errno = ENOMEM; return -1; }\n    snprintf(buf, sz - 1, template, cache_path);\n    while (fd < 0) {\n        fd = mkostemp(buf, O_CLOEXEC);\n        if (fd > -1 || errno != EINTR) break;\n    }\n    if (fd > -1) unlink(buf);\n    return fd;\n}\n\nstatic int\nopen_cache_file(const char *cache_path, bool *opened_securely) {\n    int fd = -1;\n    *opened_securely = false;\n#ifdef O_TMPFILE\n    while (fd < 0) {\n        fd = safe_open(cache_path, O_TMPFILE | O_CLOEXEC | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);\n        if (fd > -1 || errno != EINTR) break;\n    }\n    if (fd == -1) fd = open_cache_file_without_tmpfile(cache_path);\n    else *opened_securely = true;\n#else\n    fd = open_cache_file_without_tmpfile(cache_path);\n#endif\n    return fd;\n}\n\n// Write loop {{{\n\ntypedef struct {\n    CacheKey key;\n    off_t old_offset, new_offset;\n    size_t data_sz;\n} DefragEntry;\n\nstatic CacheKey\nkeydup(CacheKey k) {\n    CacheKey ans = {.hash_key=malloc(k.hash_keylen), .hash_keylen=k.hash_keylen};\n    if (ans.hash_key) memcpy(ans.hash_key, k.hash_key, k.hash_keylen);\n    return ans;\n}\n\nstatic void\ncleanup_holes(Holes *holes) {\n    vt_cleanup(&holes->size_map);\n    vt_cleanup(&holes->pos_map);\n    vt_cleanup(&holes->end_pos_map);\n    holes->largest_hole_size = 0;\n}\n\nstatic void\ndefrag(DiskCache *self) {\n    int new_cache_file = -1;\n    RAII_ALLOC(DefragEntry, defrag_entries, NULL);\n    RAII_FreeFastFileCopyBuffer(fcb);\n    bool lock_released = false, ok = false;\n\n    off_t size_on_disk = self->end_of_data_offset;\n    if (size_on_disk <= 0) goto cleanup;\n    size_t num_entries = vt_size(&self->map);\n    if (!num_entries) goto cleanup;\n    bool opened_securely;\n    new_cache_file = open_cache_file(self->cache_dir, &opened_securely);\n    if (new_cache_file < 0) {\n        perror(\"Failed to open second file for defrag of disk cache\");\n        goto cleanup;\n    }\n    defrag_entries = calloc(num_entries, sizeof(DefragEntry));\n    if (!defrag_entries) goto cleanup;\n    size_t total_data_size = 0, num_entries_to_defrag = 0;\n    cache_map_for_loop(i) {\n        CacheValue *s = i.data->val;\n        if (s->pos_in_cache_file > -1 && s->data_sz) {\n            total_data_size += s->data_sz;\n            DefragEntry *e = defrag_entries + num_entries_to_defrag++;\n            e->old_offset = s->pos_in_cache_file;\n            e->data_sz = s->data_sz;\n            e->key = keydup(i.data->key);  // have to dup the key as we release the mutex and another thread might free the underlying key.\n            if (!e->key.hash_key) { fprintf(stderr, \"Failed to allocate space for keydup in defrag\\n\"); goto cleanup; }\n        }\n    }\n    if (ftruncate(new_cache_file, total_data_size) != 0) {\n        perror(\"Failed to allocate space for new disk cache file during defrag\");\n        goto cleanup;\n    }\n    lseek(new_cache_file, 0, SEEK_SET);\n\n    mutex(unlock); lock_released = true;\n\n    off_t current_pos = 0;\n    for (size_t i = 0; i < num_entries_to_defrag; i++) {\n        DefragEntry *e = defrag_entries + i;\n        if (!copy_between_files(self->cache_file_fd, new_cache_file, e->old_offset, e->data_sz, &fcb)) {\n            perror(\"Failed to copy data to new disk cache file during defrag\");\n            goto cleanup;\n        }\n        e->new_offset = current_pos;\n        current_pos += e->data_sz;\n    }\n    ok = true;\n\ncleanup:\n    if (lock_released) mutex(lock);\n    if (ok) {\n        cleanup_holes(&self->holes);\n        safe_close(self->cache_file_fd, __FILE__, __LINE__);\n        self->cache_file_fd = new_cache_file; new_cache_file = -1;\n        for (size_t i = 0; i < num_entries_to_defrag; i++) {\n            DefragEntry *e = defrag_entries + i;\n            cache_map_itr i = vt_get(&self->map, e->key);\n            if (!vt_is_end(i)) i.data->val->pos_in_cache_file = e->new_offset;\n            free(e->key.hash_key);\n        }\n        self->end_of_data_offset = lseek(self->cache_file_fd, 0, SEEK_CUR);\n        self->needs_encryption = !opened_securely;\n    }\n    if (new_cache_file > -1) safe_close(new_cache_file, __FILE__, __LINE__);\n}\n\nstatic void\nappend_position(PosList *p, off_t pos) {\n    ensure_space_for(p, positions, off_t, p->count + 1, capacity, 8, false);\n    p->positions[p->count++] = pos;\n}\n\nstatic void\nadd_hole_to_maps(Holes *holes, Hole h) {\n    if (vt_is_end(vt_insert(&holes->pos_map, h.pos, h.size))) fatal(\"Out of memory\");\n    if (vt_is_end(vt_insert(&holes->end_pos_map, h.pos + h.size, h.size))) fatal(\"Out of memory\");\n    hole_size_map_itr i = vt_get_or_insert(&holes->size_map, h.size, (PosList){0});\n    if (vt_is_end(i)) fatal(\"Out of memory\");\n    append_position(&(i.data->val), h.pos);\n    holes->largest_hole_size = MAX(h.size, holes->largest_hole_size);\n}\n\nstatic void\nupdate_largest_hole_size(Holes *holes) {\n    holes->largest_hole_size = 0;\n    hole_size_map_for_loop(i) {\n        if (i.data->key > holes->largest_hole_size) holes->largest_hole_size = i.data->key;\n    }\n}\n\nstatic void\nremove_hole_from_maps_itr(Holes *holes, Hole h, hole_size_map_itr i, size_t pos_in_sizes_array) {\n    vt_erase(&holes->pos_map, h.pos); vt_erase(&holes->end_pos_map, h.pos + h.size);\n    if (i.data->val.count <= 1) {\n        vt_erase_itr(&holes->size_map, i);\n        if (h.size > holes->largest_hole_size) update_largest_hole_size(holes);\n    } else remove_i_from_array(i.data->val.positions, pos_in_sizes_array, i.data->val.count);\n}\n\nstatic void\nremove_hole_from_maps(Holes *holes, Hole h) {\n    hole_size_map_itr i = vt_get(&holes->size_map, h.size);\n    for (size_t x = 0; x < i.data->val.count; x++) {\n        if (i.data->val.positions[x] == h.pos) {\n            remove_hole_from_maps_itr(holes, h, vt_get(&holes->size_map, h.size), x);\n            return;\n        }\n    }\n}\n\nstatic bool\nfind_hole_to_use(DiskCache *self, const off_t required_sz) {\n    if (self->holes.largest_hole_size < required_sz) return false;\n    hole_size_map_itr i = vt_get(&self->holes.size_map, required_sz);\n    if (vt_is_end(i)) {\n        for (i = vt_first(&self->holes.size_map); !vt_is_end(i); i = vt_next(i)) {\n            if (i.data->key >= required_sz) break;\n        }\n    }\n    if (vt_is_end(i)) return false;\n    Hole h = {.pos=i.data->val.positions[i.data->val.count-1], .size=i.data->key};\n    remove_hole_from_maps_itr(&self->holes, h, i, i.data->val.count-1);\n    self->currently_writing.val.pos_in_cache_file = h.pos;\n    if (required_sz < h.size) {\n        h.pos += required_sz; h.size -= required_sz;\n        if (h.size > self->small_hole_threshold) add_hole_to_maps(&self->holes, h);\n    }\n    return true;\n}\n\nstatic inline bool\nneeds_defrag(DiskCache *self) {\n    return self->total_size && self->end_of_data_offset > 0 && (size_t)self->end_of_data_offset > self->total_size * self->defrag_factor;\n}\n\nstatic void\nadd_hole(DiskCache *self, const off_t pos, const off_t size) {\n    if (size <= self->small_hole_threshold) return;\n    if (vt_size(&self->holes.pos_map)) {\n        // See if we can find a neighboring hole to merge this hole into\n        // First look for a hole after us\n        off_t end_pos = pos + size;\n        hole_pos_map_itr i = vt_get(&self->holes.pos_map, end_pos);\n        Hole original_hole, new_hole;\n        bool found = false;\n        if (vt_is_end(i)) {\n            // Now look for a hole before us\n            i = vt_get(&self->holes.end_pos_map, pos);\n            if (!vt_is_end(i)) {\n                original_hole.pos = i.data->key - i.data->val; original_hole.size = i.data->val;\n                new_hole.pos = original_hole.pos; new_hole.size = original_hole.size + size;\n                found = true;\n            }\n        } else {\n            original_hole.pos = i.data->key; original_hole.size = i.data->val;\n            new_hole.pos = pos; new_hole.size = original_hole.size + size;\n            found = true;\n            // there could be a hole before us as well\n            i = vt_get(&self->holes.end_pos_map, pos);\n            if (!vt_is_end(i)) {\n                self->holes.largest_hole_size = MAX(self->holes.largest_hole_size, new_hole.size);\n                remove_hole_from_maps(&self->holes, original_hole);\n                original_hole.pos = i.data->key - i.data->val; original_hole.size = i.data->val;\n                new_hole.pos = original_hole.pos; new_hole.size += original_hole.size;\n            }\n        }\n        if (found) {\n            // prevent remove_hole_from_maps updating largest hole size\n            self->holes.largest_hole_size = MAX(self->holes.largest_hole_size, new_hole.size);\n            remove_hole_from_maps(&self->holes, original_hole);\n            add_hole_to_maps(&self->holes, new_hole);\n            return;\n        }\n    }\n    Hole h = {.pos=pos, .size=size };\n    add_hole_to_maps(&self->holes, h);\n}\n\nstatic void\nremove_from_disk(DiskCache *self, CacheValue *s) {\n    if (s->written_to_disk) {\n        s->written_to_disk = false;\n        if (s->data_sz && s->pos_in_cache_file > -1) {\n            add_hole(self, s->pos_in_cache_file, s->data_sz);\n            s->pos_in_cache_file = -1;\n        }\n    }\n}\n\nstatic bool\nfind_cache_entry_to_write(DiskCache *self) {\n    if (needs_defrag(self)) defrag(self);\n    cache_map_for_loop(i) {\n        CacheValue *s = i.data->val;\n        if (!s->written_to_disk) {\n            if (s->data) {\n                if (self->currently_writing.val.data) free(self->currently_writing.val.data);\n                self->currently_writing.val.data = s->data;\n                s->data = NULL;\n                self->currently_writing.val.data_sz = s->data_sz;\n                self->currently_writing.val.pos_in_cache_file = -1;\n                s->uses_encryption = false;\n                if (self->needs_encryption && secure_random_bytes(s->encryption_key, sizeof(s->encryption_key))) {\n                    xor_data64(s->encryption_key, self->currently_writing.val.data, s->data_sz);\n                    s->uses_encryption = true;\n                }\n                self->currently_writing.key.hash_keylen = MIN(i.data->key.hash_keylen, MAX_KEY_SIZE);\n                memcpy(self->currently_writing.key.hash_key, i.data->key.hash_key, self->currently_writing.key.hash_keylen);\n                find_hole_to_use(self, self->currently_writing.val.data_sz);\n                return true;\n            }\n            s->written_to_disk = true;\n            s->pos_in_cache_file = 0;\n            s->data_sz = 0;\n        }\n    }\n    return false;\n}\n\nstatic bool\nwrite_dirty_entry(DiskCache *self) {\n    size_t left = self->currently_writing.val.data_sz;\n    uint8_t *p = self->currently_writing.val.data;\n    if (self->currently_writing.val.pos_in_cache_file < 0) {\n        self->currently_writing.val.pos_in_cache_file = self->end_of_data_offset;\n        if (self->currently_writing.val.pos_in_cache_file < 0) {\n            perror(\"Failed to seek in disk cache file\");\n            return false;\n        }\n    }\n    off_t offset = self->currently_writing.val.pos_in_cache_file;\n    while (left > 0) {\n        ssize_t n = pwrite(self->cache_file_fd, p, left, offset);\n        if (n < 0) {\n            if (errno == EINTR || errno == EAGAIN) continue;\n            perror(\"Failed to write to disk-cache file\");\n            self->currently_writing.val.pos_in_cache_file = -1;\n            return false;\n        }\n        if (n == 0) {\n            fprintf(stderr, \"Failed to write to disk-cache file with zero return\\n\");\n            self->currently_writing.val.pos_in_cache_file = -1;\n            return false;\n        }\n        left -= n;\n        p += n;\n        offset += n;\n        self->end_of_data_offset = MAX(self->end_of_data_offset, offset);\n    }\n    return true;\n}\n\nstatic void\nretire_currently_writing(DiskCache *self) {\n    cache_map_itr i = vt_get(&self->map, self->currently_writing.key);\n    if (!vt_is_end(i)) {\n        i.data->val->written_to_disk = true;\n        i.data->val->pos_in_cache_file = self->currently_writing.val.pos_in_cache_file;\n    }\n    free(self->currently_writing.val.data);\n    self->currently_writing.val.data = NULL;\n    self->currently_writing.val.data_sz = 0;\n}\n\nstatic int\nclear_disk_cache_with_lock_held(DiskCache *self) {\n    vt_cleanup(&self->map);\n    cleanup_holes(&self->holes);\n    self->total_size = 0;\n    self->end_of_data_offset = 0;\n    if (self->cache_file_fd > -1) {\n        if (ftruncate(self->cache_file_fd, 0) == -1) return errno;\n    }\n    return 0;\n}\n\nstatic void*\nwrite_loop(void *data) {\n    DiskCache *self = (DiskCache*)data;\n    set_thread_name(\"DiskCacheWrite\");\n    struct pollfd fds[1] = {0};\n    fds[0].fd = self->loop_data.wakeup_read_fd;\n    fds[0].events = POLLIN;\n    bool found_dirty_entry = false;\n\n    while (!self->shutting_down) {\n        mutex(lock);\n        found_dirty_entry = find_cache_entry_to_write(self);\n        size_t count = vt_size(&self->map);\n        mutex(unlock);\n        if (found_dirty_entry) {\n            write_dirty_entry(self);\n            mutex(lock);\n            retire_currently_writing(self);\n            mutex(unlock);\n            continue;\n        } else if (!count) {\n            mutex(lock);\n            count = vt_size(&self->map);\n            if (!count) clear_disk_cache_with_lock_held(self);  // failure to truncate is not fatal\n            mutex(unlock);\n        }\n\n        if (poll(fds, 1, -1) > 0 && fds[0].revents & POLLIN) {\n            drain_fd(fds[0].fd);  // wakeup\n        }\n    }\n    return 0;\n}\n// }}}\n\nstatic bool\nensure_state(DiskCache *self) {\n    int ret;\n    if (self->fully_initialized) return true;\n    if (!self->loop_data_inited) {\n        if (!init_loop_data(&self->loop_data, 0)) { PyErr_SetFromErrno(PyExc_OSError); return false; }\n        self->loop_data_inited = true;\n    }\n    if (!self->currently_writing.key.hash_key) {\n        self->currently_writing.key.hash_key = malloc(MAX_KEY_SIZE);\n        if (!self->currently_writing.key.hash_key) { PyErr_NoMemory(); return false; }\n    }\n\n    if (!self->lock_inited) {\n        if ((ret = pthread_mutex_init(&self->lock, NULL)) != 0) {\n            PyErr_Format(PyExc_OSError, \"Failed to create disk cache lock mutex: %s\", strerror(ret));\n            return false;\n        }\n        self->lock_inited = true;\n    }\n\n    if (!self->thread_started) {\n        if ((ret = pthread_create(&self->write_thread, NULL, write_loop, self)) != 0) {\n            PyErr_Format(PyExc_OSError, \"Failed to start disk cache write thread with error: %s\", strerror(ret));\n            return false;\n        }\n        self->thread_started = true;\n    }\n\n    if (!self->cache_dir) {\n        PyObject *kc = NULL, *cache_dir = NULL;\n        kc = PyImport_ImportModule(\"kitty.constants\");\n        if (kc) {\n            cache_dir = PyObject_CallMethod(kc, \"cache_dir\", NULL);\n            if (cache_dir) {\n                if (PyUnicode_Check(cache_dir)) {\n                    self->cache_dir = strdup(PyUnicode_AsUTF8(cache_dir));\n                    if (!self->cache_dir) PyErr_NoMemory();\n                } else PyErr_SetString(PyExc_TypeError, \"cache_dir() did not return a string\");\n            }\n        }\n        Py_CLEAR(kc); Py_CLEAR(cache_dir);\n        if (PyErr_Occurred()) return false;\n    }\n\n    if (self->cache_file_fd < 0) {\n        bool opened_securely;\n        self->cache_file_fd = open_cache_file(self->cache_dir, &opened_securely);\n        if (self->cache_file_fd < 0) {\n            PyErr_SetFromErrnoWithFilename(PyExc_OSError, self->cache_dir);\n            return false;\n        }\n        self->needs_encryption = !opened_securely;\n    }\n    vt_init(&self->map); vt_init(&self->holes.pos_map); vt_init(&self->holes.size_map); vt_init(&self->holes.end_pos_map);\n    self->fully_initialized = true;\n    return true;\n}\n\nstatic void\nwakeup_write_loop(DiskCache *self) {\n    if (self->thread_started) wakeup_loop(&self->loop_data, false, \"disk_cache_write_loop\");\n}\n\nstatic void\ndealloc(DiskCache* self) {\n    self->shutting_down = true;\n    if (self->thread_started) {\n        wakeup_write_loop(self);\n        pthread_join(self->write_thread, NULL);\n        self->thread_started = false;\n    }\n    if (self->currently_writing.key.hash_key) {\n        free(self->currently_writing.key.hash_key); self->currently_writing.key.hash_key = NULL;\n    }\n    if (self->lock_inited) {\n        pthread_mutex_destroy(&self->lock);\n        self->lock_inited = false;\n    }\n    if (self->loop_data_inited) {\n        free_loop_data(&self->loop_data);\n        self->loop_data_inited = false;\n    }\n    vt_cleanup(&self->map); cleanup_holes(&self->holes);\n    if (self->cache_file_fd > -1) {\n        safe_close(self->cache_file_fd, __FILE__, __LINE__);\n        self->cache_file_fd = -1;\n    }\n    if (self->currently_writing.val.data) free(self->currently_writing.val.data);\n    free(self->cache_dir); self->cache_dir = NULL;\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic CacheValue*\ncreate_cache_entry(void) {\n    CacheValue *s = calloc(1, sizeof(CacheValue));\n    if (!s) return (CacheValue*)PyErr_NoMemory();\n    s->pos_in_cache_file = -2;\n    return s;\n}\n\nbool\nadd_to_disk_cache(PyObject *self_, const void *key, size_t key_sz, const void *data, size_t data_sz) {\n    DiskCache *self = (DiskCache*)self_;\n    if (!ensure_state(self)) return false;\n    if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, \"cache key is too long\"); return false; }\n    RAII_ALLOC(uint8_t, copied_data, malloc(data_sz));\n    if (!copied_data) { PyErr_NoMemory(); return false; }\n    memcpy(copied_data, data, data_sz);\n    CacheKey k = {.hash_key=(void*)key, .hash_keylen=key_sz};\n\n    mutex(lock);\n    cache_map_itr i = vt_get(&self->map, k);\n    CacheValue *s;\n    if (vt_is_end(i)) {\n        k.hash_key = malloc(key_sz);\n        if (!k.hash_key) { PyErr_NoMemory(); goto end; }\n        memcpy(k.hash_key, key, key_sz);\n        if (!(s = create_cache_entry())) goto end;\n        if (vt_is_end(vt_insert(&self->map, k, s))) { PyErr_NoMemory(); goto end; }\n    } else {\n        s = i.data->val;\n        remove_from_disk(self, s);\n        self->total_size -= MIN(self->total_size, s->data_sz);\n        if (s->data) free(s->data);\n    }\n    s->data = copied_data; s->data_sz = data_sz; copied_data = NULL;\n    self->total_size += s->data_sz;\nend:\n    mutex(unlock);\n    if (PyErr_Occurred()) return false;\n    wakeup_write_loop(self);\n    return true;\n}\n\nbool\nremove_from_disk_cache(PyObject *self_, const void *key, size_t key_sz) {\n    DiskCache *self = (DiskCache*)self_;\n    if (!ensure_state(self)) return false;\n    if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, \"cache key is too long\"); return false; }\n    CacheValue *s = NULL;\n    CacheKey k = {.hash_key=(void*)key, .hash_keylen=key_sz};\n    bool removed = false;\n\n    mutex(lock);\n    cache_map_itr i = vt_get(&self->map, k);\n    if (!vt_is_end(i)) {\n        removed = true;\n        s = i.data->val;\n        remove_from_disk(self, s);\n        self->total_size = (self->total_size > s->data_sz) ? self->total_size - s->data_sz : 0;\n        vt_erase_itr(&self->map, i);\n    }\n    mutex(unlock);\n    wakeup_write_loop(self);\n    return removed;\n}\n\nstatic int\nclear_disk_cache(PyObject *self_) {\n    // This is currently only used in testing\n    DiskCache *self = (DiskCache*)self_;\n    if (!ensure_state(self)) return 0;\n    int saved_errno = 0;\n    disk_cache_wait_for_write(self_, 0);\n    mutex(lock);\n    saved_errno = clear_disk_cache_with_lock_held(self);\n    mutex(unlock);\n    wakeup_write_loop(self);\n    return saved_errno;\n}\n\nstatic void\nread_from_cache_file(const DiskCache *self, off_t pos, size_t sz, void *dest) {\n    uint8_t *p = dest;\n    while (sz) {\n        ssize_t n = pread(self->cache_file_fd, p, sz, pos);\n        if (n > 0) {\n            sz -= n;\n            p += n;\n            pos += n;\n            continue;\n        }\n        if (n < 0) {\n            if (errno == EINTR || errno == EAGAIN) continue;\n            PyErr_SetFromErrnoWithFilename(PyExc_OSError, self->cache_dir);\n            break;\n        }\n        if (n == 0) {\n            PyErr_SetString(PyExc_OSError, \"Disk cache file truncated\");\n            break;\n        }\n    }\n}\n\nstatic void\nread_from_cache_entry(const DiskCache *self, const CacheValue *s, void *dest) {\n    size_t sz = s->data_sz;\n    off_t pos = s->pos_in_cache_file;\n    if (pos < 0) {\n        PyErr_SetString(PyExc_OSError, \"Cache entry was not written, could not read from it\");\n        return;\n    }\n    read_from_cache_file(self, pos, sz, dest);\n}\n\nvoid*\nread_from_disk_cache(PyObject *self_, const void *key, size_t key_sz, void*(allocator)(void*, size_t), void* allocator_data, bool store_in_ram) {\n    DiskCache *self = (DiskCache*)self_;\n    void *data = NULL;\n    if (!ensure_state(self)) return data;\n    if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, \"cache key is too long\"); return data; }\n    CacheKey k = {.hash_key=(void*)key, .hash_keylen=key_sz};\n\n    mutex(lock);\n    cache_map_itr i = vt_get(&self->map, k);\n    if (vt_is_end(i)) { PyErr_SetString(PyExc_KeyError, \"No cached entry with specified key found\"); goto end; }\n    CacheValue *s = i.data->val;\n    data = allocator(allocator_data, s->data_sz);\n    if (!data) { PyErr_NoMemory(); goto end; }\n\n    if (s->data) { memcpy(data, s->data, s->data_sz); }\n    else if (self->currently_writing.val.data && self->currently_writing.key.hash_key && keys_are_equal(self->currently_writing.key, k)) {\n        memcpy(data, self->currently_writing.val.data, s->data_sz);\n        if (s->uses_encryption) xor_data64(s->encryption_key, data, s->data_sz);\n    }\n    else {\n        read_from_cache_entry(self, s, data);\n        if (s->uses_encryption) xor_data64(s->encryption_key, data, s->data_sz);\n    }\n    if (store_in_ram && !s->data && s->data_sz) {\n        void *copy = malloc(s->data_sz);\n        if (copy) {\n            memcpy(copy, data, s->data_sz); s->data = copy;\n        }\n    }\nend:\n    mutex(unlock);\n    return data;\n}\n\nsize_t\ndisk_cache_clear_from_ram(PyObject *self_, bool(matches)(void*, void *key, unsigned keysz), void *data) {\n    DiskCache *self = (DiskCache*)self_;\n    size_t ans = 0;\n    if (!ensure_state(self)) return ans;\n    mutex(lock);\n    cache_map_for_loop(i) {\n        CacheValue *s = i.data->val;\n        if (s->written_to_disk && s->data && matches(data, i.data->key.hash_key, i.data->key.hash_keylen)) {\n            free(s->data); s->data = NULL;\n            ans++;\n        }\n    }\n    mutex(unlock);\n    return ans;\n}\n\nbool\ndisk_cache_wait_for_write(PyObject *self_, monotonic_t timeout) {\n    DiskCache *self = (DiskCache*)self_;\n    if (!ensure_state(self)) return false;\n    monotonic_t end_at = monotonic() + timeout;\n    while (!timeout || monotonic() <= end_at) {\n        bool pending = false;\n        mutex(lock);\n        cache_map_for_loop(i) {\n            if (!i.data->val->written_to_disk) {\n                pending = true;\n                break;\n            }\n        }\n        mutex(unlock);\n        if (!pending) return true;\n        wakeup_write_loop(self);\n        struct timespec a = { .tv_nsec = 10L * MONOTONIC_T_1e6 }, b;  // 10ms sleep\n        nanosleep(&a, &b);\n    }\n    return false;\n}\n\nsize_t\ndisk_cache_total_size(PyObject *self) { return ((DiskCache*)self)->total_size; }\n\nsize_t\ndisk_cache_num_cached_in_ram(PyObject *self_) {\n    DiskCache *self = (DiskCache*)self_;\n    unsigned long ans = 0;\n    if (ensure_state(self)) {\n        mutex(lock);\n        cache_map_for_loop(i) {\n            if (i.data->val->written_to_disk && i.data->val->data) ans++;\n        }\n        mutex(unlock);\n    }\n    return ans;\n}\n\n\n// The Python interface used only for testing {{{\n#define PYWRAP(name) static PyObject* py##name(DiskCache *self, PyObject *args)\n#define PA(fmt, ...) if (!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;\nPYWRAP(ensure_state) {\n    (void)args;\n    ensure_state(self);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nwait_for_write(PyObject *self, PyObject *args) {\n    double timeout = 0;\n    PA(\"|d\", &timeout);\n    if (disk_cache_wait_for_write(self, s_double_to_monotonic_t(timeout))) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nend_of_data_offset(PyObject *self_, PyObject *args UNUSED) {\n    // Only used for testing\n    DiskCache *self = (DiskCache*)self_;\n    unsigned long long ans = 0;\n    mutex(lock);\n    if (self->cache_file_fd > -1) ans = MAX(0, self->end_of_data_offset);\n    mutex(unlock);\n    return PyLong_FromUnsignedLongLong(ans);\n}\n\nstatic PyObject*\nclear(PyObject *self, PyObject *args UNUSED) {\n    int saved_errno = clear_disk_cache(self);\n    if (saved_errno) return PyErr_SetFromErrno(PyExc_OSError);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nholes(PyObject *self_, PyObject *args UNUSED) {\n    DiskCache *self = (DiskCache*)self_;\n    mutex(lock);\n    RAII_PyObject(ans, PyFrozenSet_New(NULL));\n    if (ans) {\n        hole_pos_map_for_loop(i) {\n            RAII_PyObject(t, Py_BuildValue(\"LL\", (long long)i.data->key, (long long)i.data->val));\n            if (!t || PySet_Add(ans, t) != 0) break;\n        }\n    }\n    mutex(unlock);\n    if (PyErr_Occurred()) return NULL;\n    Py_INCREF(ans);\n    return ans;\n}\n\n\nstatic PyObject*\nadd(PyObject *self, PyObject *args) {\n    const char *key, *data;\n    Py_ssize_t keylen, datalen;\n    PA(\"y#y#\", &key, &keylen, &data, &datalen);\n    if (!add_to_disk_cache(self, key, keylen, data, datalen)) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npyremove(PyObject *self, PyObject *args) {\n    const char *key;\n    Py_ssize_t keylen;\n    PA(\"y#\", &key, &keylen);\n    bool removed = remove_from_disk_cache(self, key, keylen);\n    if (PyErr_Occurred()) return NULL;\n    if (removed) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\ntypedef struct {\n    PyObject *bytes;\n} BytesWrapper;\n\nstatic void*\nbytes_alloc(void *x, size_t sz) {\n    BytesWrapper *w = x;\n    w->bytes = PyBytes_FromStringAndSize(NULL, sz);\n    if (!w->bytes) return NULL;\n    return PyBytes_AS_STRING(w->bytes);\n}\n\nPyObject*\nread_from_disk_cache_python(PyObject *self, const void *key, size_t keysz, bool store_in_ram) {\n    BytesWrapper w = {0};\n    read_from_disk_cache(self, key, keysz, bytes_alloc, &w, store_in_ram);\n    if (PyErr_Occurred()) { Py_CLEAR(w.bytes); return NULL; }\n    return w.bytes;\n}\n\nstatic PyObject*\nget(PyObject *self, PyObject *args) {\n    const char *key;\n    Py_ssize_t keylen;\n    int store_in_ram = 0;\n    PA(\"y#|p\", &key, &keylen, &store_in_ram);\n    return read_from_disk_cache_python(self, key, keylen, store_in_ram);\n}\n\nstatic bool\npython_clear_predicate(void *data, void *key, unsigned keysz) {\n    PyObject *ret = PyObject_CallFunction(data, \"y#\", key, keysz);\n    if (ret == NULL) { PyErr_Print(); return false; }\n    bool ans = PyObject_IsTrue(ret);\n    Py_DECREF(ret);\n    return ans;\n}\n\nstatic PyObject*\nremove_from_ram(PyObject *self, PyObject *callable) {\n    if (!PyCallable_Check(callable)) { PyErr_SetString(PyExc_TypeError, \"not a callable\"); return NULL; }\n    return PyLong_FromUnsignedLong(disk_cache_clear_from_ram(self, python_clear_predicate, callable));\n}\n\nstatic PyObject*\nnum_cached_in_ram(PyObject *self, PyObject *args UNUSED) {\n    return PyLong_FromUnsignedLong(disk_cache_num_cached_in_ram(self));\n}\n\n\n#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}\nstatic PyMethodDef methods[] = {\n    MW(ensure_state, METH_NOARGS),\n    {\"add\", add, METH_VARARGS, NULL},\n    {\"remove\", pyremove, METH_VARARGS, NULL},\n    {\"remove_from_ram\", remove_from_ram, METH_O, NULL},\n    {\"num_cached_in_ram\", num_cached_in_ram, METH_NOARGS, NULL},\n    {\"get\", get, METH_VARARGS, NULL},\n    {\"wait_for_write\", wait_for_write, METH_VARARGS, NULL},\n    {\"end_of_data_offset\", end_of_data_offset, METH_NOARGS, NULL},\n    {\"clear\", clear, METH_NOARGS, NULL},\n    {\"holes\", holes, METH_NOARGS, NULL},\n\n    {NULL}  /* Sentinel */\n};\n\nstatic PyMemberDef members[] = {\n    {\"total_size\", T_ULONGLONG, offsetof(DiskCache, total_size), READONLY, \"total_size\"},\n    {\"small_hole_threshold\", T_PYSSIZET, offsetof(DiskCache, small_hole_threshold), 0, \"small_hole_threshold\"},\n    {\"defrag_factor\", T_UINT, offsetof(DiskCache, defrag_factor), 0, \"defrag_factor\"},\n    {NULL},\n};\n\n\nPyTypeObject DiskCache_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.DiskCache\",\n    .tp_basicsize = sizeof(DiskCache),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"A disk based secure cache\",\n    .tp_methods = methods,\n    .tp_members = members,\n    .tp_new = new_diskcache_object,\n};\n\nINIT_TYPE(DiskCache)\nPyObject* create_disk_cache(void) { return new_diskcache_object(&DiskCache_Type, NULL, NULL); }\n// }}}\n"
  },
  {
    "path": "kitty/disk-cache.h",
    "content": "/*\n * Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\nPyObject* create_disk_cache(void);\nbool add_to_disk_cache(PyObject *self, const void *key, size_t key_sz, const void *data, size_t data_sz);\nbool remove_from_disk_cache(PyObject *self_, const void *key, size_t key_sz);\nvoid* read_from_disk_cache(PyObject *self_, const void *key, size_t key_sz, void*(allocator)(void*, size_t), void*, bool);\nPyObject* read_from_disk_cache_python(PyObject *self_, const void *key, size_t key_sz, bool);\nbool disk_cache_wait_for_write(PyObject *self, monotonic_t timeout);\nsize_t disk_cache_total_size(PyObject *self);\nsize_t disk_cache_size_on_disk(PyObject *self);\nsize_t disk_cache_clear_from_ram(PyObject *self_, bool(matches)(void* data, void *key, unsigned keysz), void*);\nsize_t disk_cache_num_cached_in_ram(PyObject *self_);\n\nstatic inline void* disk_cache_malloc_allocator(void *x, size_t sz) {\n    *((size_t*)x) = sz;\n    return malloc(sz);\n}\n\nstatic inline bool\nread_from_disk_cache_simple(PyObject *self_, const void *key, size_t key_sz, void **data, size_t *data_sz, bool store_in_ram) {\n    *data = read_from_disk_cache(self_, key, key_sz, disk_cache_malloc_allocator, data_sz, store_in_ram);\n    if (PyErr_Occurred()) {\n        PyErr_Clear();\n        return false;\n    }\n    return *data != NULL;\n}\n"
  },
  {
    "path": "kitty/entry_points.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport sys\n\n\ndef icat(args: list[str]) -> None:\n    from kitty.constants import kitten_exe\n    os.execl(kitten_exe(), \"kitten\", *args)\n\n\ndef list_fonts(args: list[str]) -> None:\n    from kitty.fonts.list import main as list_main\n    list_main(args)\n\n\ndef runpy(args: list[str]) -> None:\n    if len(args) < 2:\n        raise SystemExit('Usage: kitty +runpy \"some python code\"')\n    sys.argv = ['kitty'] + args[2:]\n    exec(args[1])\n\n\ndef hold(args: list[str]) -> None:\n    from kitty.constants import kitten_exe\n    args = ['kitten', '__hold_till_enter__'] + args[1:]\n    os.execvp(kitten_exe(), args)\n\n\ndef complete(args: list[str]) -> None:\n    # Delegate to kitten to maintain backward compatibility\n    if len(args) < 2 or args[1] not in ('setup', 'zsh', 'fish2', 'bash'):\n        raise SystemExit(1)\n    if args[1] == 'fish2':\n        args[1:1] = ['fish', '_legacy_completion=fish2']\n    elif len(args) >= 3 and args [1:3] == ['setup', 'fish2']:\n        args[2] = 'fish'\n    from kitty.constants import kitten_exe\n    args = ['kitten', '__complete__'] + args[1:]\n    os.execvp(kitten_exe(), args)\n\n\ndef open_urls(args: list[str]) -> None:\n    setattr(sys, 'cmdline_args_for_open', True)\n    sys.argv = ['kitty'] + args[1:]\n    from kitty.main import main as kitty_main\n    kitty_main()\n\n\ndef launch(args: list[str]) -> None:\n    import runpy\n    sys.argv = args[1:]\n    try:\n        exe = args[1]\n    except IndexError:\n        raise SystemExit(\n            'usage: kitty +launch script.py [arguments to be passed to script.py ...]\\n\\n'\n            'script.py will be run with full access to kitty code. If script.py is '\n            'prefixed with a : it will be searched for in PATH. If script.py is a directory '\n            'the __main__.py file inside it is run just as with the normal Python interpreter.'\n        )\n    if exe.startswith(':'):\n        import shutil\n        q = shutil.which(exe[1:])\n        if not q:\n            raise SystemExit(f'{exe[1:]} not found in PATH')\n        exe = q\n    if not os.path.exists(exe):\n        raise SystemExit(f'{exe} does not exist')\n    runpy.run_path(exe, run_name='__main__')\n\n\ndef shebang(args: list[str]) -> None:\n    from kitty.constants import kitten_exe\n    os.execvp(kitten_exe(), ['kitten', '__shebang__', 'confirm-if-needed'] + args[1:])\n\n\ndef run_kitten(args: list[str]) -> None:\n    try:\n        kitten = args[1]\n    except IndexError:\n        from kittens.runner import list_kittens\n        list_kittens()\n        raise SystemExit(1)\n    sys.argv = args[1:]\n    from kittens.runner import run_kitten as rk\n    rk(kitten)\n\n\ndef namespaced(args: list[str]) -> None:\n    try:\n        func = namespaced_entry_points[args[1]]\n    except IndexError:\n        raise SystemExit('The kitty command line is incomplete')\n    except KeyError:\n        pass\n    else:\n        func(args[1:])\n        return\n    raise SystemExit(f'{args[1]} is not a known entry point. Choices are: ' + ', '.join(namespaced_entry_points))\n\n\nentry_points = {\n    # These two are here for backwards compat\n    'icat': icat,\n    'list-fonts': list_fonts,\n\n    '+': namespaced,\n}\nnamespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}\nnamespaced_entry_points['hold'] = hold\nnamespaced_entry_points['complete'] = complete\nnamespaced_entry_points['runpy'] = runpy\nnamespaced_entry_points['launch'] = launch\nnamespaced_entry_points['open'] = open_urls\nnamespaced_entry_points['kitten'] = run_kitten\nnamespaced_entry_points['shebang'] = shebang\n\n\ndef setup_openssl_environment(ext_dir: str) -> None:\n    # Use our bundled CA certificates instead of the system ones, since\n    # many systems come with no certificates in a usable form or have various\n    # locations for the certificates.\n    d = os.path.dirname\n    if 'darwin' in sys.platform.lower():\n        cert_file = os.path.join(d(d(d(ext_dir))), 'cacert.pem')\n    else:\n        cert_file = os.path.join(d(ext_dir), 'cacert.pem')\n    os.environ['SSL_CERT_FILE'] = cert_file\n    setattr(sys, 'kitty_ssl_env_var', 'SSL_CERT_FILE')\n\n\ndef main() -> None:\n    if getattr(sys, 'frozen', False) and (ext_dir := getattr(sys, 'kitty_run_data', {}).get('extensions_dir')):\n        setup_openssl_environment(ext_dir)\n    first_arg = '' if len(sys.argv) < 2 else sys.argv[1]\n    func = entry_points.get(first_arg)\n    if func is None:\n        if first_arg.startswith('+'):\n            namespaced(['+', first_arg[1:]] + sys.argv[2:])\n        else:\n            from kitty.main import main as kitty_main\n            kitty_main()\n    else:\n        func(sys.argv[1:])\n"
  },
  {
    "path": "kitty/fast-file-copy.c",
    "content": "/*\n * fast-file-copy.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#if __linux__\n#define _GNU_SOURCE 1\n#endif\n#include \"fast-file-copy.h\"\n#if __linux__\n#define HAS_SENDFILE\n#include <sys/sendfile.h>\n#include <unistd.h>\n#endif\n\nstatic bool\ncopy_with_buffer(int infd, int outfd, off_t in_pos, size_t len, FastFileCopyBuffer *fcb) {\n    if (!fcb->buf) {\n        fcb->sz = 32 * 1024;\n        fcb->buf = malloc(fcb->sz);\n        if (!fcb->buf) return false;\n    }\n    while (len) {\n        ssize_t amt_read = pread(infd, fcb->buf, MIN(len, fcb->sz), in_pos);\n        if (amt_read < 0) {\n            if (errno == EINTR || errno == EAGAIN) continue;\n            return false;\n        }\n        if (amt_read == 0) {\n            errno = EIO;\n            return false;\n        }\n        len -= amt_read;\n        in_pos += amt_read;\n        uint8_t *p = fcb->buf;\n        while(amt_read) {\n            ssize_t amt_written = write(outfd, p, amt_read);\n            if (amt_written < 0) {\n                if (errno == EINTR || errno == EAGAIN) continue;\n                return false;\n            }\n            if (amt_written == 0) {\n                errno = EIO;\n                return false;\n            }\n            amt_read -= amt_written;\n            p += amt_written;\n        }\n    }\n    return true;\n}\n\n#ifdef HAS_SENDFILE\nstatic bool\ncopy_with_sendfile(int infd, int outfd, off_t in_pos, size_t len, FastFileCopyBuffer *fcb) {\n    unsigned num_of_consecutive_zero_returns = 128;\n    while (len) {\n        off_t r = in_pos;\n        ssize_t n = sendfile(outfd, infd, &r, len);\n        if (n < 0) {\n            if (errno == EAGAIN) continue;\n            if (errno == ENOSYS || // No kernel support\n                errno == EPERM  ||\n                errno == EINVAL)   // ZFS for some reason\n                return copy_with_buffer(infd, outfd, in_pos, len, fcb);\n            return false;\n        }\n        if (n == 0) {\n            // happens if input file is truncated\n            if (!--num_of_consecutive_zero_returns) return false;\n            continue;\n        };\n        num_of_consecutive_zero_returns = 128;\n        in_pos += n; len -= n;\n    }\n    return true;\n}\n\nstatic bool\ncopy_with_file_range(int infd, int outfd, off_t in_pos, size_t len, FastFileCopyBuffer *fcb) {\n#ifdef HAS_COPY_FILE_RANGE\n    unsigned num_of_consecutive_zero_returns = 128;\n    while (len) {\n        int64_t r = in_pos;\n        ssize_t n = copy_file_range(infd, &r, outfd, NULL, len, 0);\n        if (n < 0) {\n            if (errno == EAGAIN) continue;\n            if (errno == ENOSYS     || // Linux < 4.5\n                errno == EPERM      || // Possibly Docker\n                errno == EINVAL     || // ZFS for some reason\n                errno == EIO        || // CIFS\n                errno == EOPNOTSUPP || // NFS\n                errno == EXDEV)        // Prior to Linux 5.3, it was not possible to copy_file_range across file systems\n                return copy_with_sendfile(infd, outfd, in_pos, len, fcb);\n            return false;\n        }\n        if (n == 0) {\n            // happens if input file is truncated\n            if (!--num_of_consecutive_zero_returns) return false;\n            continue;\n        };\n        num_of_consecutive_zero_returns = 128;\n        in_pos += n; len -= n;\n    }\n    return true;\n#else\n    return copy_with_sendfile(infd, outfd, in_pos, len, fcb);\n#endif\n}\n\n\n#endif\n\nbool\ncopy_between_files(int infd, int outfd, off_t in_pos, size_t len, FastFileCopyBuffer *fcb) {\n#ifdef HAS_SENDFILE\n    return copy_with_file_range(infd, outfd, in_pos, len, fcb);\n#else\n    return copy_with_buffer(infd, outfd, in_pos, len, fcb);\n#endif\n    return true;\n}\n"
  },
  {
    "path": "kitty/fast-file-copy.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\ntypedef struct FastFileCopyBuffer {\n    uint8_t *buf;\n    size_t sz;\n} FastFileCopyBuffer;\n\nstatic inline void\nfree_fast_file_copy_buffer(FastFileCopyBuffer *fcb) { free(fcb->buf); fcb->buf = NULL; }\n\n#define RAII_FreeFastFileCopyBuffer(name) __attribute__ ((__cleanup__(free_fast_file_copy_buffer))) FastFileCopyBuffer name = {0}\n\nbool copy_between_files(int infd, int outfd, off_t in_pos, size_t len, FastFileCopyBuffer *fcb);\n"
  },
  {
    "path": "kitty/fast_data_types.pyi",
    "content": "import termios\nfrom typing import Any, Callable, Dict, Iterator, List, Literal, NewType, Optional, Sequence, Tuple, TypedDict, Union, overload\n\nfrom kitty.borders import Border\nfrom kitty.boss import Boss\nfrom kitty.fonts import VariableData\nfrom kitty.fonts.render import FontObject\nfrom kitty.marks import MarkerFunc\nfrom kitty.notifications import MacOSNotificationCategory\nfrom kitty.options.types import Options\nfrom kitty.simple_cli_definitions import OptionDefinition\nfrom kitty.types import LayerShellConfig, SignalInfo\nfrom kitty.typing_compat import EdgeLiteral, NotRequired, ReadableBuffer, WriteableBuffer\n\n# Constants {{{\nSCALE_BITS: int\nWIDTH_BITS: int\nSUBSCALE_BITS: int\nCOLOR_IS_SPECIAL: int\nCOLOR_NOT_SET: int\nCOLOR_IS_RGB: int\nCOLOR_IS_INDEX: int\nGLFW_DRAG_OPERATION_MOVE: int\nGLFW_DRAG_OPERATION_COPY: int\nGLFW_DRAG_OPERATION_GENERIC: int\nGLFW_LAYER_SHELL_NONE: int\nGLFW_LAYER_SHELL_PANEL: int\nGLFW_LAYER_SHELL_TOP: int\nGLFW_LAYER_SHELL_OVERLAY: int\nGLFW_LAYER_SHELL_BACKGROUND: int\nGLFW_EDGE_TOP: int\nGLFW_EDGE_BOTTOM: int\nGLFW_EDGE_LEFT: int\nGLFW_EDGE_RIGHT: int\nGLFW_EDGE_CENTER: int\nGLFW_EDGE_CENTER_SIZED: int\nGLFW_EDGE_NONE: int\nGLFW_FOCUS_NOT_ALLOWED: int\nGLFW_FOCUS_EXCLUSIVE: int\nGLFW_FOCUS_ON_DEMAND: int\nIMAGE_PLACEHOLDER_CHAR: int\nGLFW_PRIMARY_SELECTION: int\nGLFW_CLIPBOARD: int\nCLD_KILLED: int\nCLD_STOPPED: int\nCLD_CONTINUED: int\nCLD_EXITED: int\nSHM_NAME_MAX: int\nMOUSE_SELECTION_LINE: int\nMOUSE_SELECTION_EXTEND: int\nMOUSE_SELECTION_NORMAL: int\nMOUSE_SELECTION_WORD: int\nMOUSE_SELECTION_RECTANGLE: int\nMOUSE_SELECTION_LINE_FROM_POINT: int\nMOUSE_SELECTION_UPTO_SURROUNDING_WHITESPACE: int\nMOUSE_SELECTION_WORD_AND_LINE_FROM_POINT: int\nMOUSE_SELECTION_MOVE_END: int\nKITTY_VCS_REV: str\nNO_CLOSE_REQUESTED: int\nIMPERATIVE_CLOSE_REQUESTED: int\nCLOSE_BEING_CONFIRMED: int\nERROR_PREFIX: str\nGLSL_VERSION: int\n# start glfw functional keys (auto generated by gen-key-constants.py do not edit)\nGLFW_FKEY_ESCAPE: int\nGLFW_FKEY_ENTER: int\nGLFW_FKEY_TAB: int\nGLFW_FKEY_BACKSPACE: int\nGLFW_FKEY_INSERT: int\nGLFW_FKEY_DELETE: int\nGLFW_FKEY_LEFT: int\nGLFW_FKEY_RIGHT: int\nGLFW_FKEY_UP: int\nGLFW_FKEY_DOWN: int\nGLFW_FKEY_PAGE_UP: int\nGLFW_FKEY_PAGE_DOWN: int\nGLFW_FKEY_HOME: int\nGLFW_FKEY_END: int\nGLFW_FKEY_CAPS_LOCK: int\nGLFW_FKEY_SCROLL_LOCK: int\nGLFW_FKEY_NUM_LOCK: int\nGLFW_FKEY_PRINT_SCREEN: int\nGLFW_FKEY_PAUSE: int\nGLFW_FKEY_MENU: int\nGLFW_FKEY_F1: int\nGLFW_FKEY_F2: int\nGLFW_FKEY_F3: int\nGLFW_FKEY_F4: int\nGLFW_FKEY_F5: int\nGLFW_FKEY_F6: int\nGLFW_FKEY_F7: int\nGLFW_FKEY_F8: int\nGLFW_FKEY_F9: int\nGLFW_FKEY_F10: int\nGLFW_FKEY_F11: int\nGLFW_FKEY_F12: int\nGLFW_FKEY_F13: int\nGLFW_FKEY_F14: int\nGLFW_FKEY_F15: int\nGLFW_FKEY_F16: int\nGLFW_FKEY_F17: int\nGLFW_FKEY_F18: int\nGLFW_FKEY_F19: int\nGLFW_FKEY_F20: int\nGLFW_FKEY_F21: int\nGLFW_FKEY_F22: int\nGLFW_FKEY_F23: int\nGLFW_FKEY_F24: int\nGLFW_FKEY_F25: int\nGLFW_FKEY_F26: int\nGLFW_FKEY_F27: int\nGLFW_FKEY_F28: int\nGLFW_FKEY_F29: int\nGLFW_FKEY_F30: int\nGLFW_FKEY_F31: int\nGLFW_FKEY_F32: int\nGLFW_FKEY_F33: int\nGLFW_FKEY_F34: int\nGLFW_FKEY_F35: int\nGLFW_FKEY_KP_0: int\nGLFW_FKEY_KP_1: int\nGLFW_FKEY_KP_2: int\nGLFW_FKEY_KP_3: int\nGLFW_FKEY_KP_4: int\nGLFW_FKEY_KP_5: int\nGLFW_FKEY_KP_6: int\nGLFW_FKEY_KP_7: int\nGLFW_FKEY_KP_8: int\nGLFW_FKEY_KP_9: int\nGLFW_FKEY_KP_DECIMAL: int\nGLFW_FKEY_KP_DIVIDE: int\nGLFW_FKEY_KP_MULTIPLY: int\nGLFW_FKEY_KP_SUBTRACT: int\nGLFW_FKEY_KP_ADD: int\nGLFW_FKEY_KP_ENTER: int\nGLFW_FKEY_KP_EQUAL: int\nGLFW_FKEY_KP_SEPARATOR: int\nGLFW_FKEY_KP_LEFT: int\nGLFW_FKEY_KP_RIGHT: int\nGLFW_FKEY_KP_UP: int\nGLFW_FKEY_KP_DOWN: int\nGLFW_FKEY_KP_PAGE_UP: int\nGLFW_FKEY_KP_PAGE_DOWN: int\nGLFW_FKEY_KP_HOME: int\nGLFW_FKEY_KP_END: int\nGLFW_FKEY_KP_INSERT: int\nGLFW_FKEY_KP_DELETE: int\nGLFW_FKEY_KP_BEGIN: int\nGLFW_FKEY_MEDIA_PLAY: int\nGLFW_FKEY_MEDIA_PAUSE: int\nGLFW_FKEY_MEDIA_PLAY_PAUSE: int\nGLFW_FKEY_MEDIA_REVERSE: int\nGLFW_FKEY_MEDIA_STOP: int\nGLFW_FKEY_MEDIA_FAST_FORWARD: int\nGLFW_FKEY_MEDIA_REWIND: int\nGLFW_FKEY_MEDIA_TRACK_NEXT: int\nGLFW_FKEY_MEDIA_TRACK_PREVIOUS: int\nGLFW_FKEY_MEDIA_RECORD: int\nGLFW_FKEY_LOWER_VOLUME: int\nGLFW_FKEY_RAISE_VOLUME: int\nGLFW_FKEY_MUTE_VOLUME: int\nGLFW_FKEY_LEFT_SHIFT: int\nGLFW_FKEY_LEFT_CONTROL: int\nGLFW_FKEY_LEFT_ALT: int\nGLFW_FKEY_LEFT_SUPER: int\nGLFW_FKEY_LEFT_HYPER: int\nGLFW_FKEY_LEFT_META: int\nGLFW_FKEY_RIGHT_SHIFT: int\nGLFW_FKEY_RIGHT_CONTROL: int\nGLFW_FKEY_RIGHT_ALT: int\nGLFW_FKEY_RIGHT_SUPER: int\nGLFW_FKEY_RIGHT_HYPER: int\nGLFW_FKEY_RIGHT_META: int\nGLFW_FKEY_ISO_LEVEL3_SHIFT: int\nGLFW_FKEY_ISO_LEVEL5_SHIFT: int\n# end glfw functional keys\nGLFW_MOD_SHIFT: int\nGLFW_MOD_CONTROL: int\nGLFW_MOD_ALT: int\nGLFW_MOD_SUPER: int\nGLFW_MOD_HYPER: int\nGLFW_MOD_META: int\nGLFW_MOD_CAPS_LOCK: int\nGLFW_MOD_NUM_LOCK: int\nGLFW_MOD_KITTY: int\nGLFW_MOUSE_BUTTON_1: int\nGLFW_MOUSE_BUTTON_2: int\nGLFW_MOUSE_BUTTON_3: int\nGLFW_MOUSE_BUTTON_4: int\nGLFW_MOUSE_BUTTON_5: int\nGLFW_MOUSE_BUTTON_6: int\nGLFW_MOUSE_BUTTON_7: int\nGLFW_MOUSE_BUTTON_8: int\nGLFW_MOUSE_BUTTON_LAST: int\nGLFW_MOUSE_BUTTON_LEFT: int\nGLFW_MOUSE_BUTTON_RIGHT: int\nGLFW_MOUSE_BUTTON_MIDDLE: int\nGLFW_JOYSTICK_1: int\nGLFW_JOYSTICK_2: int\nGLFW_JOYSTICK_3: int\nGLFW_JOYSTICK_4: int\nGLFW_JOYSTICK_5: int\nGLFW_JOYSTICK_6: int\nGLFW_JOYSTICK_7: int\nGLFW_JOYSTICK_8: int\nGLFW_JOYSTICK_9: int\nGLFW_JOYSTICK_10: int\nGLFW_JOYSTICK_11: int\nGLFW_JOYSTICK_12: int\nGLFW_JOYSTICK_13: int\nGLFW_JOYSTICK_14: int\nGLFW_JOYSTICK_15: int\nGLFW_JOYSTICK_16: int\nGLFW_JOYSTICK_LAST: int\nGLFW_NOT_INITIALIZED: int\nGLFW_NO_CURRENT_CONTEXT: int\nGLFW_INVALID_ENUM: int\nGLFW_INVALID_VALUE: int\nGLFW_OUT_OF_MEMORY: int\nGLFW_API_UNAVAILABLE: int\nGLFW_VERSION_UNAVAILABLE: int\nGLFW_PLATFORM_ERROR: int\nGLFW_FORMAT_UNAVAILABLE: int\nGLFW_FOCUSED: int\nGLFW_ICONIFIED: int\nGLFW_RESIZABLE: int\nGLFW_VISIBLE: int\nGLFW_DECORATED: int\nGLFW_AUTO_ICONIFY: int\nGLFW_FLOATING: int\nGLFW_RED_BITS: int\nGLFW_GREEN_BITS: int\nGLFW_BLUE_BITS: int\nGLFW_ALPHA_BITS: int\nGLFW_DEPTH_BITS: int\nGLFW_STENCIL_BITS: int\nGLFW_ACCUM_RED_BITS: int\nGLFW_ACCUM_GREEN_BITS: int\nGLFW_ACCUM_BLUE_BITS: int\nGLFW_ACCUM_ALPHA_BITS: int\nGLFW_AUX_BUFFERS: int\nGLFW_STEREO: int\nGLFW_SAMPLES: int\nGLFW_SRGB_CAPABLE: int\nGLFW_REFRESH_RATE: int\nGLFW_DOUBLEBUFFER: int\nGLFW_CLIENT_API: int\nGLFW_CONTEXT_VERSION_MAJOR: int\nGLFW_CONTEXT_VERSION_MINOR: int\nGLFW_CONTEXT_REVISION: int\nGLFW_CONTEXT_ROBUSTNESS: int\nGLFW_OPENGL_FORWARD_COMPAT: int\nGLFW_CONTEXT_DEBUG: int\nGLFW_OPENGL_PROFILE: int\nGLFW_OPENGL_API: int\nGLFW_OPENGL_ES_API: int\nGLFW_NO_ROBUSTNESS: int\nGLFW_NO_RESET_NOTIFICATION: int\nGLFW_LOSE_CONTEXT_ON_RESET: int\nGLFW_OPENGL_ANY_PROFILE: int\nGLFW_OPENGL_CORE_PROFILE: int\nGLFW_OPENGL_COMPAT_PROFILE: int\nGLFW_CURSOR: int\nGLFW_STICKY_KEYS: int\nGLFW_STICKY_MOUSE_BUTTONS: int\nGLFW_CURSOR_NORMAL: int\nGLFW_CURSOR_HIDDEN: int\nGLFW_CURSOR_DISABLED: int\nGLFW_CONNECTED: int\nGLFW_DISCONNECTED: int\nGLFW_PRESS: int\nGLFW_RELEASE: int\nGLFW_REPEAT: int\nCURSOR_BEAM: int\nCURSOR_BLOCK: int\nCURSOR_HOLLOW: int\nNO_CURSOR_SHAPE: int\nCURSOR_UNDERLINE: int\nDECAWM: int\nBGIMAGE_PROGRAM: int\nCELL_PROGRAM: int\nCELL_FG_PROGRAM: int\nCELL_BG_PROGRAM: int\nBLIT_PROGRAM: int\nSCREENSHOT_PROGRAM: int\nROUNDED_RECT_PROGRAM: int\nDECORATION: int\nBLINK: int\nDIM: int\nGRAPHICS_ALPHA_MASK_PROGRAM: int\nGRAPHICS_PROGRAM: int\nGRAPHICS_PREMULT_PROGRAM: int\nMARK: int\nMARK_MASK: int\nDECORATION_MASK: int\nFILE_TRANSFER_CODE: int\nESC_CSI: int\nESC_OSC: int\nESC_DCS: int\nESC_APC: int\nESC_PM: int\nREVERSE: int\nSCROLL_FULL: int\nSCROLL_LINE: int\nSCROLL_PAGE: int\nSTRIKETHROUGH: int\nTINT_PROGRAM: int\nFC_MONO: int = 100\nFC_DUAL: int\nFC_WEIGHT_REGULAR: int\nFC_WEIGHT_BOLD: int\nFC_WEIGHT_SEMIBOLD: int\nFC_WEIGHT_MEDIUM: int\nFC_WIDTH_NORMAL: int\nFC_SLANT_ROMAN: int\nFC_SLANT_ITALIC: int\nBORDERS_PROGRAM: int\nTRAIL_PROGRAM: int\nPRESS: int\nRELEASE: int\nDRAG: int\nMOVE: int\nWINDOW_NORMAL: int = 0\nWINDOW_FULLSCREEN: int\nWINDOW_MAXIMIZED: int\nWINDOW_MINIMIZED: int\nWINDOW_HIDDEN: int\nTEXT_SIZE_CODE: int\nTOP_EDGE: int\nBOTTOM_EDGE: int\nLEFT_EDGE: int\nRIGHT_EDGE: int\n# }}}\n\n\ndef encode_key_for_tty(\n    key: int = 0,\n    shifted_key: int = 0,\n    alternate_key: int = 0,\n    mods: int = 0,\n    action: int = 1,\n    key_encoding_flags: int = 0,\n    text: str = \"\",\n    cursor_key_mode: bool = False\n) -> str:\n    pass\n\n\ndef log_error_string(s: str) -> None:\n    pass\n\n\ndef glfw_get_key_name(key: int, native_key: int) -> Optional[str]:\n    pass\n\n\nStartupCtx = NewType('StartupCtx', int)\nDisplay = NewType('Display', int)\n\n\ndef init_x11_startup_notification(\n    display: Display,\n    window_id: int,\n    startup_id: Optional[str] = None\n) -> StartupCtx:\n    pass\n\n\ndef end_x11_startup_notification(ctx: StartupCtx) -> None:\n    pass\n\n\ndef x11_display() -> Optional[Display]:\n    pass\n\n\ndef user_cache_dir() -> str:\n    pass\n\n\ndef process_group_map() -> Tuple[Tuple[int, int], ...]:\n    pass\n\n\ndef environ_of_process(pid: int) -> str:\n    pass\n\n\ndef cmdline_of_process(pid: int) -> List[str]:\n    pass\n\n\ndef cwd_of_process(pid: int) -> str:\n    pass\n\n\ndef abspath_of_process(pid: int) -> str: ...\n\ndef default_color_table() -> Tuple[int, ...]:\n    pass\n\n\nclass FontConfigPattern(TypedDict):\n    descriptor_type: Literal['fontconfig']\n    path: str\n    index: int\n    family: str\n    full_name: str\n    postscript_name: str\n    style: str\n    spacing: str\n    fontfeatures: List[str]\n    weight: int\n    width: int\n    slant: int\n    hint_style: int\n    subpixel: int\n    lcdfilter: int\n    hinting: bool\n    scalable: bool\n    outline: bool\n    color: bool\n    variable: bool\n    named_instance: bool\n\n    # The following two are used by C code to get a face from the pattern\n    named_style: NotRequired[int]\n    axes: NotRequired[Tuple[float, ...]]\n    features: NotRequired[Tuple[ParsedFontFeature, ...]]\n\n\ndef fc_list(spacing: int = -1, allow_bitmapped_fonts: bool = False, only_variable: bool = False) -> Tuple[FontConfigPattern, ...]:\n    pass\n\n\ndef fc_match(\n    family: Optional[str] = None,\n    bold: bool = False,\n    italic: bool = False,\n    spacing: int = FC_MONO,\n    allow_bitmapped_fonts: bool = False,\n    size_in_pts: float = 0.,\n    dpi: float = 0.\n) -> FontConfigPattern:\n    pass\n\n\ndef fc_match_postscript_name(\n    postscript_name: str\n) -> FontConfigPattern:\n    pass\n\n\ndef add_font_file(path: str) -> bool: ...\ndef set_builtin_nerd_font(path: str) -> Union[CoreTextFont, FontConfigPattern]: ...\n\n\nclass FeatureData(TypedDict):\n    name: NotRequired[str]\n    tooltip: NotRequired[str]\n    sample: NotRequired[str]\n    params: NotRequired[Tuple[str, ...]]\n\n\nclass Face:\n    path: Optional[str]\n    def __init__(self, descriptor: Optional[FontConfigPattern] = None, path: str = '', index: int = 0): ...\n    def get_variable_data(self) -> VariableData: ...\n    def identify_for_debug(self) -> str: ...\n    def postscript_name(self) -> str: ...\n    def set_size(self, sz_in_pts: float, dpi_x: float, dpi_y: float) -> None: ...\n    def render_sample_text(\n        self, text: str, width: int, height: int, fg_color: int = 0xffffff\n    ) -> tuple[bytes, int, int]: ...\n    def render_codepoint(self, cp: int, fg_color: int = 0xffffff) -> tuple[bytes, int, int]: ...\n    def get_variation(self) -> Optional[Dict[str, float]]: ...\n    def get_features(self) -> Dict[str, Optional[FeatureData]]: ...\n    def applied_features(self) -> Dict[str, str]: ...\n\n\nclass CoreTextFont(TypedDict):\n    descriptor_type: Literal['core_text']\n    path: str\n    postscript_name: str\n    display_name: str\n    family: str\n    style: str\n    bold: bool\n    italic: bool\n    expanded: bool\n    condensed: bool\n    color_glyphs: bool\n    monospace: bool\n    variation: Optional[Dict[str, float]]\n    weight: float\n    width: float\n    slant: float\n    traits: int\n\n    # The following is used by C code to get a face from the pattern\n    axis_map: NotRequired[Dict[str, float]]\n    features: NotRequired[Tuple[ParsedFontFeature, ...]]\n\n\nclass CTFace:\n    path: Optional[str]\n    def __init__(self, descriptor: Optional[CoreTextFont] = None, path: str = ''): ...\n    def get_variable_data(self) -> VariableData: ...\n    def identify_for_debug(self) -> str: ...\n    def postscript_name(self) -> str: ...\n    def set_size(self, sz_in_pts: float, dpi_x: float, dpi_y: float) -> None: ...\n    def render_sample_text(\n        self, text: str, width: int, height: int, fg_color: int = 0xffffff,\n    ) -> tuple[bytes, int, int]: ...\n    def render_codepoint(self, cp: int, fg_color: int = 0xffffff) -> tuple[bytes, int, int]: ...\n    def get_variation(self) -> Optional[Dict[str, float]]: ...\n    def get_features(self) -> Dict[str, Optional[FeatureData]]: ...\n    def applied_features(self) -> Dict[str, str]: ...\n\n\ndef coretext_all_fonts(monospaced_only: bool) -> Tuple[CoreTextFont, ...]:\n    pass\n\n\nclass ParsedFontFeature:\n    def __init__(self, s: str): ...\n\n\ndef add_timer(\n    callback: Callable[[Optional[int]], None],\n    interval: float,\n    repeats: bool = True\n) -> int:\n    pass\n\n\ndef remove_timer(timer_id: int) -> None:\n    pass\n\n\ndef monitor_pid(pid: int) -> None:\n    pass\n\n\ndef add_window(os_window_id: int, tab_id: int, title: str) -> int:\n    pass\n\n\ndef compile_program(\n    which: int, vertex_shaders: Tuple[str, ...], fragment_shaders: Tuple[str, ...], allow_recompile: bool = False\n) -> int:\n    pass\n\n\ndef init_cell_program() -> None:\n    pass\n\n\ndef set_os_window_chrome(os_window_id: int) -> bool:\n    pass\n\n\ndef set_borders_rects(os_window_id: int, tab_id: int, rects: list[Border]) -> None: ...\n\n\ndef init_borders_program() -> None:\n    pass\n\ndef os_window_has_background_image(os_window_id: int) -> bool:\n    pass\n\n\ndef dbus_set_notification_callback(c: Optional[Callable[[str, int, Union[str, int]], None]]) -> None: ...\n\ndef dbus_send_notification(\n    app_name: str,\n    app_icon: str,\n    title: str,\n    body: str,\n    actions: dict[str, str],\n    timeout: int = -1,\n    urgency: int = 1,\n    replaces: int = 0,\n    category: str = '',\n    muted: bool = False,\n) -> int:\n    pass\n\n\ndef dbus_close_notification(dbus_notification_id: int) -> bool: ...\n\n\ndef cocoa_send_notification(\n    appname: str,\n    identifier: str,\n    title: str,\n    body: str,\n    category: MacOSNotificationCategory,\n    categories: tuple[MacOSNotificationCategory, ...],\n    image_path: str = '',\n    urgency: int = 1,\n    muted: bool = False,\n) -> None:\n    pass\n\ndef cocoa_bundle_image_as_png(path_or_identifier: str, output_path: str = '', image_size: int = 256, image_type: int = 1) -> bytes: ...\ndef cocoa_remove_delivered_notification(identifier: str) -> bool: ...\ndef cocoa_live_delivered_notifications() -> bool: ...\n\ndef create_os_window(\n    get_window_size: Callable[[int, int, int, int, float, float], Tuple[int,\n                                                                        int]],\n    pre_show_callback: Callable[[int], None],\n    title: str,\n    wm_class_name: str,\n    wm_class_class: str,\n    window_state: Optional[int] = WINDOW_NORMAL,\n    load_programs: Optional[Callable[[], None]] = None,\n    x: Optional[int] = None,\n    y: Optional[int] = None,\n    disallow_override_title: bool = False,\n    layer_shell_config: Optional[LayerShellConfig] = None,\n) -> int:\n    pass\n\n\ndef update_window_title(\n    os_window_id: int, tab_id: int, window_id: int, title: str\n) -> None:\n    pass\n\n\ndef update_window_visibility(\n    os_window_id: int, tab_id: int, window_id: int,\n    visible: bool\n) -> None:\n    pass\n\n\ndef sync_os_window_title(os_window_id: int) -> None:\n    pass\n\n\ndef set_options(\n    opts: Optional[Options],\n    is_wayland: bool = False,\n    debug_rendering: bool = False,\n    debug_font_fallback: bool = False\n) -> None:\n    pass\n\n\ndef get_options() -> Options:\n    pass\n\n\ndef glfw_primary_monitor_size() -> tuple[int, int]:\n    pass\n\ndef glfw_get_monitor_workarea() -> tuple[tuple[int, int, int, int], ...]:\n    pass\n\n\ndef set_default_window_icon(path: str) -> None:\n    pass\n\n\ndef set_os_window_icon(os_window_id: int, path: str | None | bytes = None) -> None: ...\n\n\ndef set_custom_cursor(\n    cursor_shape: str,\n    images: Tuple[Tuple[bytes, int, int], ...],\n    x: int = 0,\n    y: int = 0\n) -> None:\n    pass\n\n\ndef is_css_pointer_name_valid(name: str) -> bool: ...\ndef pointer_name_to_css_name(name: str) -> str: ...\n\n\ndef load_png_data(data: bytes) -> Tuple[bytes, int, int]:\n    pass\n\n\ndef glfw_terminate() -> None:\n    pass\n\n\ndef glfw_init(\n    path: str, edge_spacing_func: Callable[[EdgeLiteral], float], debug_keyboard: bool = False, debug_rendering: bool = False,\n    wayland_enable_ime: bool = True\n) -> tuple[bool, bool]:\n    pass\n\n\ndef free_font_data() -> None:\n    pass\n\n\ndef toggle_maximized(os_window_id: int = 0) -> bool:\n    pass\n\n\ndef toggle_fullscreen(os_window_id: int = 0) -> bool:\n    pass\n\n\ndef thread_write(fd: int, data: bytes) -> None:\n    pass\n\n\ndef set_ignore_os_keyboard_processing(yes: bool) -> None:\n    pass\n\n\ndef set_background_image(\n    path: Optional[str],\n    os_window_ids: Tuple[int, ...],\n    configured: bool = True,\n    layout_name: Optional[str] = None,\n    png_data: bytes = b'',\n    linear: bool | None = None,\n    tint: float | None = None,\n    tint_gaps: float | None = None,\n) -> None:\n    pass\n\n\ndef set_boss(boss: Boss) -> None:\n    pass\n\n\ndef get_boss() -> Boss:  # this can return None but we ignore that for convenience\n    pass\n\n\ndef safe_pipe(nonblock: bool = True) -> Tuple[int, int]:\n    pass\n\n\ndef patch_global_colors(spec: Dict[str, Optional[int]], configured: bool) -> None:\n    pass\n\n\nclass Color:\n    @classmethod\n    def parse_color(cls, spec: str) -> Color | None: ...\n\n    @property\n    def rgb(self) -> int:\n        pass\n\n    @property\n    def red(self) -> int:\n        pass\n    r = red\n\n    @property\n    def green(self) -> int:\n        pass\n    g = green\n\n    @property\n    def blue(self) -> int:\n        pass\n    b = blue\n\n    @property\n    def alpha(self) -> int:\n        pass\n    a = alpha\n\n    @property\n    def luminance(self) -> float:\n        pass\n\n    @property\n    def is_dark(self) -> bool:\n        pass\n\n    @property\n    def as_sgr(self) -> str:\n        pass\n\n    @property\n    def as_sharp(self) -> str:\n        pass\n\n    def __init__(self, red: int = 0, green: int = 0, blue: int = 0, alpha: int = 0) -> None:\n        pass\n\n    def __truediv__(self, divisor: float) -> Tuple[float, float, float, float]:  # (r, g, b, a)\n        pass\n\n    def __int__(self) -> int:\n        pass\n\n    def __hash__(self) -> int:\n        pass\n\n    def __eq__(self, other: Any) -> bool:\n        pass\n\n    def __ne__(self, other: Any) -> bool:\n        pass\n\n    def contrast(self, other: 'Color') -> float:\n        pass\n\n\nclass ColorProfile:\n\n    # The dynamic color properties return the current color value. Use delattr\n    # to reset them to configured value.\n    @property\n    def default_fg(self) -> Color: ...\n    @default_fg.setter\n    def default_fg(self, val: Union[int|Color]) -> None: ...\n\n    @property\n    def default_bg(self) -> Color: ...\n    @default_bg.setter\n    def default_bg(self, val: Union[int|Color]) -> None: ...\n\n    @property\n    def cursor_color(self) -> Optional[Color]: ...\n    @cursor_color.setter\n    def cursor_color(self, val: Union[None|int|Color]) -> None: ...\n\n    @property\n    def cursor_text_color(self) -> Optional[Color]: ...\n    @cursor_text_color.setter\n    def cursor_text_color(self, val: Union[None|int|Color]) -> None: ...\n\n    @property\n    def highlight_fg(self) -> Optional[Color]: ...\n    @highlight_fg.setter\n    def highlight_fg(self, val: Union[None|int|Color]) -> None: ...\n\n    @property\n    def highlight_bg(self) -> Optional[Color]: ...\n    @highlight_bg.setter\n    def highlight_bg(self, val: Union[None|int|Color]) -> None: ...\n\n    @property\n    def visual_bell_color(self) -> Optional[Color]: ...\n    @visual_bell_color.setter\n    def visual_bell_color(self, val: Union[None|int|Color]) -> None: ...\n\n    def __init__(self, opts: Optional[Options] = None): ...\n\n    def as_dict(self) -> Dict[str, int | None | tuple[tuple[Color, float], ...]]: ...\n    def basic_colors(self) -> Dict[str, int | None | tuple[tuple[Color, float], ...]]: ...\n\n    def as_color(self, val: int) -> Optional[Color]:\n        pass\n\n    def set_color(self, num: int, val: int) -> None:\n        pass\n\n    def reset_color_table(self) -> None:\n        pass\n\n    def reset_color(self, num: int) -> None:\n        pass\n\n    def reload_from_opts(self, opts: Optional[Options] = None) -> None: ...\n\n    def get_transparent_background_color(self, index: int) -> Color | None: ...\n    def set_transparent_background_color(self, index: int, color: Color | None = None, opacity: float | None = None) -> None: ...\n\n\ndef patch_color_profiles(\n        spec: Dict[str, Optional[int]], transparent_background_colors: tuple[tuple[Color, float], ...],\n        profiles: Tuple[ColorProfile, ...], change_configured: bool\n) -> None:\n    pass\n\n\ndef create_canvas(d: bytes, w: int, x: int, y: int, cw: int, ch: int, bpp: int) -> bytes: ...\n\n\ndef os_window_font_size(\n    os_window_id: int, new_sz: float = -1., force: bool = False\n) -> float:\n    pass\n\n\ndef cocoa_set_notification_activated_callback(identifier: Optional[Callable[[str, str, str], None]]) -> None:\n    pass\n\n\ndef cocoa_set_global_shortcut(name: str, mods: int, key: int) -> bool:\n    pass\n\n\ndef cocoa_get_lang() -> Tuple[str, str, str]:\n    pass\n\n\ndef cocoa_set_url_handler(url_scheme: str, bundle_id: Optional[str] = None) -> None:\n    pass\n\n\ndef cocoa_set_app_icon(icon_path: str, app_path: Optional[str] = None) -> None:\n    pass\n\n\ndef cocoa_set_dock_icon(icon_path: str) -> None:\n    pass\n\n\ndef cocoa_show_progress_bar_on_dock_icon(progress: float = -100) -> None:\n    pass\n\n\ndef cocoa_hide_app() -> None:\n    pass\n\n\ndef cocoa_hide_other_apps() -> None:\n    pass\n\n\ndef cocoa_minimize_os_window(os_window_id: Optional[int] = None) -> None:\n    pass\n\n\ndef locale_is_valid(name: str) -> bool:\n    pass\n\n\ndef mark_os_window_for_close(os_window_id: int, cr_type: int = 2) -> bool:\n    pass\n\n\ndef set_application_quit_request(cr_type: int = 2) -> None:\n    pass\n\n\ndef current_application_quit_request() -> int:\n    pass\n\n\ndef global_font_size(val: float = -1.) -> float:\n    pass\n\n\ndef focus_os_window(os_window_id: int, also_raise: bool = True, activation_token: Optional[str] = None) -> bool:\n    pass\n\n\ndef toggle_secure_input() -> None:\n    pass\n\n\ndef macos_cycle_through_os_windows(backwards: bool) -> None:\n    pass\n\n\ndef start_profiler(path: str) -> None:\n    pass\n\n\ndef stop_profiler() -> None:\n    pass\n\n\ndef destroy_global_data() -> None:\n    pass\n\n\ndef current_os_window() -> Optional[int]:\n    pass\n\n\ndef last_focused_os_window_id() -> int:\n    pass\n\n\ndef current_focused_os_window_id() -> int:\n    pass\n\n\ndef cocoa_set_menubar_title(title: str) -> None:\n    pass\n\n\ndef change_os_window_state(state: int, os_window_id: Optional[int] = 0) -> None:\n    pass\n\n\ndef change_background_opacity(os_window_id: int, opacity: float) -> bool:\n    pass\n\n\ndef background_opacity_of(os_window_id: int) -> Optional[float]:\n    pass\n\n\ndef read_command_response(fd: int, timeout: float, list: List[bytes]) -> None:\n    pass\n\n\ndef wcswidth(string: str) -> int:\n    pass\n\n\ndef is_emoji_presentation_base(code: int) -> bool:\n    pass\n\n\ndef x11_window_id(os_window_id: int) -> int:\n    pass\n\n\ndef cocoa_window_id(os_window_id: int) -> int:\n    pass\n\n\ndef swap_tabs(os_window_id: int, a: int, b: int) -> None: ...\ndef reorder_tabs(os_window_id: int, *tab_ids: int) -> None: ...\n\ndef set_active_tab(os_window_id: int, a: int) -> None:\n    pass\n\n\ndef set_active_window(os_window_id: int, tab_id: int, window_id: int) -> None:\n    pass\n\n\ndef ring_bell(os_window_id: int = 0) -> None: ...\ndef request_attention(os_window_id: int) -> None: ...\n\n\ndef concat_cells(cell_width: int, cell_height: int, is_32_bit: bool, cells: Tuple[bytes, ...], bgcolor: int = 0) -> bytes:\n    pass\n\n\nFontFace = Union[Face, CTFace]\n\nclass CurrentFonts(TypedDict):\n    medium: FontFace\n    bold: FontFace\n    italic: FontFace\n    bi: FontFace\n    symbol: Tuple[FontFace, ...]\n    fallback: Tuple[FontFace, ...]\n    font_sz_in_pts: float\n    logical_dpi_x: float\n    logical_dpi_y: float\n\n\ndef current_fonts(os_window_id: int = 0) -> CurrentFonts: ...\n\n\ndef remove_window(os_window_id: int, tab_id: int, window_id: int) -> None:\n    pass\n\n\ndef remove_tab(os_window_id: int, tab_id: int) -> None:\n    pass\n\n\ndef pt_to_px(pt: float, os_window_id: int = 0) -> int:\n    pass\n\n\ndef next_window_id() -> int:\n    pass\n\n\ndef mark_tab_bar_dirty(os_window_id: int, should_be_shown: bool) -> None:\n    pass\n\n\ndef is_tab_bar_visible(os_window_id: int) -> bool: ...\n\n\ndef detach_window(os_window_id: int, tab_id: int, window_id: int) -> None:\n    pass\n\n\ndef attach_window(os_window_id: int, tab_id: int, window_id: int) -> None:\n    pass\n\n\ndef add_tab(os_window_id: int) -> int:\n    pass\n\n\ndef cell_size_for_window(os_window_id: int) -> Tuple[int, int]:\n    pass\n\n\ndef wakeup_main_loop() -> None:\n    pass\n\n\nclass Region:\n    left: int\n    top: int\n    right: int\n    bottom: int\n    width: int\n    height: int\n\n    def __init__(self, x: Tuple[int, int, int, int, int, int]):\n        pass\n\n\ndef viewport_for_window(\n    os_window_id: int\n) -> Tuple[Region, Region, int, int, int, int]:\n    pass\n\n\nTermiosPtr = NewType('TermiosPtr', int)\n\n\ndef raw_tty(fd: int, termios_ptr: TermiosPtr, optional_actions: int = termios.TCSAFLUSH) -> None:\n    pass\n\n\ndef close_tty(fd: int, termios_ptr: TermiosPtr, optional_actions: int = termios.TCSAFLUSH) -> None:\n    pass\n\n\ndef normal_tty(fd: int, termios_ptr: TermiosPtr, optional_actions: int = termios.TCSAFLUSH) -> None:\n    pass\n\n\ndef open_tty(read_with_timeout: bool = False, optional_actions: int = termios.TCSAFLUSH) -> Tuple[int, TermiosPtr]:\n    pass\n\n\ndef parse_input_from_terminal(\n    text_callback: Callable[[str], None], dcs_callback: Callable[[str], None],\n    csi_callback: Callable[[str], None], osc_callback: Callable[[str], None],\n    pm_callback: Callable[[str], None], apc_callback: Callable[[str], None],\n    data: str, in_bracketed_paste: bool\n) -> str:\n    pass\n\n\nclass Line:\n\n    def sprite_at(self, cell: int) -> int: ...\n\n\ndef test_shape(line: Line,\n               path: Optional[str] = None,\n               index: int = 0) -> List[Tuple[int, int, int, Tuple[int, ...]]]:\n    pass\n\n\ndef test_render_line(line: Line) -> None:\n    pass\n\n\ndef sprite_map_set_limits(w: int, h: int) -> None:\n    pass\n\n\ndef set_send_sprite_to_gpu(\n    func: Optional[Callable[[int, int, int, bytes], None]]\n) -> None:\n    pass\n\n\ndef set_font_data(\n    descriptor_for_idx: Callable[[int], Tuple[Union[FontObject|str], bool, bool]],\n    bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,\n    symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float,\n    narrow_symbols: Tuple[Tuple[int, int, int], ...],\n) -> None:\n    pass\n\n\ndef get_fallback_font(text: str, bold: bool, italic: bool) -> Any:\n    pass\n\n\ndef create_test_font_group(sz: float, dpix: float, dpiy: float) -> tuple[int, int, int]: ...\n\n\nclass HistoryBuf:\n\n    def pagerhist_as_text(self, upto_output_start: bool = False) -> str:\n        pass\n\n    def pagerhist_as_bytes(self) -> bytes:\n        pass\n\n\nclass LineBuf:\n\n    def is_continued(self, idx: int) -> bool:\n        pass\n\n    def line(self, num: int) -> Line:\n        pass\n\n    def as_ansi(self, callback: Callable[[str], None]) -> None: ...\n\n\nclass Cursor:\n    x: int\n    y: int\n    bg: int\n    fg: int\n    bold: bool\n    italic: bool\n    blink: bool\n    text_blink: bool\n    shape: int\n\n\nclass Screen:\n\n    color_profile: ColorProfile\n    columns: int\n    lines: int\n    focus_tracking_enabled: bool\n    historybuf: HistoryBuf\n    linebuf: LineBuf\n    in_bracketed_paste_mode: bool\n    in_band_resize_notification: bool\n    paste_events: bool\n    color_preference_notification: bool\n    cursor_visible: bool\n    scrolled_by: int\n    cursor: Cursor\n    disable_ligatures: int\n    cursor_key_mode: bool\n    auto_repeat_enabled: bool\n    render_unfocused_cursor: bool\n    last_reported_cwd: Optional[bytes]\n\n    def __init__(\n            self,\n            callbacks: Any = None,\n            lines: int = 80, columns: int = 24, scrollback: int = 0,\n            cell_width: int = 10, cell_height: int = 20,\n            window_id: int = 0,\n            test_child: Any = None\n    ):\n        pass\n\n    def test_create_write_buffer(self) -> memoryview: ...\n    def test_commit_write_buffer(self, inp: memoryview, output: memoryview) -> int: ...\n    def test_parse_written_data(self, dump_callback: None = None) -> None: ...\n    def hyperlink_for_id(self, hyperlink_id: int) -> str: ...\n    def erase_last_command(self, include_prompt: bool = True) -> bool: ...\n\n    def cursor_at_prompt(self) -> bool:\n        pass\n\n    def ignore_bells_for(self, duration: float = 1) -> None:\n        pass\n\n    def set_window_char(self, ch: str = \"\") -> None:\n        pass\n\n    def current_key_encoding_flags(self) -> int:\n        pass\n\n    def line(self, num: int) -> Line:\n        pass\n\n    def visual_line(self, num: int) -> Line:\n        pass\n\n    def draw(self, text: str) -> None:\n        pass\n\n    def dump_lines_with_attrs(self, acc: Callable[[str], None], which_screen: int = -1) -> None:\n        pass\n\n    def apply_sgr(self, text: str) -> None:\n        pass\n\n    def copy_colors_from(self, other: 'Screen') -> None:\n        pass\n\n    def mark_as_dirty(self) -> None:\n        pass\n\n    def reload_all_gpu_data(self) -> None:\n        pass\n\n    def resize(self, width: int, height: int) -> None:\n        pass\n\n    def send_escape_code_to_child(self, code: int, text: Union[str, bytes, Tuple[Union[str, bytes], ...]]) -> bool:\n        pass\n\n    def reset_callbacks(self) -> None:\n        pass\n\n    def has_selection(self) -> bool:\n        pass\n\n    def text_for_selection(self, ansi: bool, strip_trailing_spaces: bool) -> Tuple[str, ...]:\n        pass\n\n    def is_rectangle_select(self) -> bool:\n        pass\n\n    def is_using_alternate_linebuf(self) -> bool:\n        pass\n\n    def is_main_linebuf(self) -> bool:\n        pass\n\n    def erase_in_line(self, mode: int = 0, private: bool = False) -> None:\n        pass\n\n    def scroll(self, amt: int, upwards: bool) -> bool:\n        pass\n\n    def fractional_scroll(self, amt: float) -> bool:\n        pass\n\n    def scroll_to_next_mark(self, mark: int = 0, backwards: bool = True) -> bool:\n        pass\n\n    def scroll_to_prompt(self, num_of_prompts: int = -1, scroll_offset: int = 0) -> bool:\n        pass\n\n    def set_last_visited_prompt(self, visual_y: int = 0) -> bool:\n        pass\n\n    def reverse_scroll(self, amt: int, fill_from_scrollback: bool = False) -> bool:\n        pass\n\n    def scroll_prompt_to_bottom(self) -> None:\n        pass\n\n    def clear_selection(self) -> None:\n        pass\n\n    def reset_mode(self, mode: int, private: bool = False) -> None:\n        pass\n\n    def refresh_sprite_positions(self) -> None:\n        pass\n\n    def set_marker(self, marker: Optional[MarkerFunc] = None) -> None:\n        pass\n\n    def paste_bytes(self, data: bytes) -> None:\n        pass\n    paste = paste_bytes\n\n    def as_text(self, callback: Callable[[str], None], as_ansi: bool, insert_wrap_markers: bool) -> None:\n        pass\n    as_text_non_visual = as_text\n    as_text_alternate = as_text\n    as_text_for_history_buf = as_text\n\n    def cmd_output(self, which: int, callback: Callable[[str], None], as_ansi: bool, insert_wrap_markers: bool) -> bool:\n        pass\n\n    def scroll_until_cursor_prompt(self, add_to_scrollback: bool = True) -> None:\n        pass\n\n    def reset(self) -> None:\n        pass\n\n    def erase_in_display(self, how: int = 0, private: bool = False) -> None:\n        pass\n\n    def clear_scrollback(self) -> None:\n        pass\n\n    def focus_changed(self, focused: bool) -> bool:\n        pass\n\n    def has_focus(self) -> bool:\n        pass\n\n    def has_activity_since_last_focus(self) -> bool:\n        pass\n\n    def insert_characters(self, num: int) -> None:\n        pass\n\n    def delete_characters(self, num: int) -> None: ...\n    def erase_characters(self, num: int) -> None: ...\n\n    def line_edge_colors(self) -> Tuple[int, int]:\n        pass\n\n    def current_pointer_shape(self) -> str: ...\n    def change_pointer_shape(self, op: str, name: str) -> None: ...\n\n    def bell(self) -> None: ...\n    def pause_rendering(self, pause: bool = True, for_how_long_in_ms: int = 100) -> bool: ...\n\ndef set_tab_bar_render_data(\n    os_window_id: int, screen: Screen, left: int, top: int, right: int, bottom: int\n) -> None:\n    pass\n\n\ndef set_window_title_bar_render_data(\n    os_window_id: int, tab_id: int, window_id: int, screen: Screen,\n    left: int, top: int, right: int, bottom: int\n) -> None:\n    pass\n\n\ndef set_window_render_data(\n    os_window_id: int, tab_id: int, window_id: int, screen: Screen,\n    left: int, top: int, right: int, bottom: int,\n    spaces_left: int, spaces_top: int, spaces_right: int, spaces_bottom: int\n) -> None:\n    pass\n\n\ndef truncate_point_for_length(\n    text: str, num_cells: int, start_pos: int = 0\n) -> int:\n    pass\n\n\nclass ChildMonitor:\n\n    def __init__(\n        self,\n        death_notify: Callable[[int], None],\n        dump_callback: Optional[Callable[[int, str, Any], None]],\n        talk_fd: int = -1,\n        listen_fd: int = -1,\n        verify_peer_uid: bool = False,\n    ):\n        pass\n\n    def wakeup(self) -> None:\n        pass\n\n    def handled_signals(self) -> Tuple[int, ...]:\n        pass\n\n    def main_loop(self) -> None:\n        pass\n\n    def resize_pty(self, window_id: int, rows: int, cols: int, x_pixels: int, y_pixels: int) -> None:\n        pass\n\n    def needs_write(self, child_id: int, data: bytes | memoryview) -> bool:\n        pass\n\n    def set_iutf8_winid(self, win_id: int, on: bool) -> bool:\n        pass\n\n    def add_child(self, id: int, pid: int, fd: int, screen: Screen) -> None:\n        pass\n\n    def mark_for_close(self, window_id: int) -> bool:\n        pass\n\n    def start(self) -> None:\n        pass\n\n    def shutdown_monitor(self) -> None:\n        pass\n\n    def inject_peer(self, fd: int) -> int: ...\n\n\nclass KeyEvent:\n\n    def __init__(\n        self, key: int, shifted_key: int = 0, alternate_key: int = 0, mods: int = 0, action: int = 1, native_key: int = 1, ime_state: int = 0, text: str = ''\n    ):\n        pass\n\n    @property\n    def key(self) -> int:\n        pass\n\n    @property\n    def shifted_key(self) -> int:\n        pass\n\n    @property\n    def alternate_key(self) -> int:\n        pass\n\n    @property\n    def mods(self) -> int:\n        pass\n\n    @property\n    def action(self) -> int:\n        pass\n\n    @property\n    def native_key(self) -> int:\n        pass\n\n    @property\n    def ime_state(self) -> int:\n        pass\n\n    @property\n    def text(self) -> str:\n        pass\n\n\ndef set_iutf8_fd(fd: int, on: bool) -> bool:\n    pass\n\n\ndef spawn(\n    exe: str,\n    cwd: str,\n    argv: Tuple[str, ...],\n    env: Tuple[str, ...],\n    master: int,\n    slave: int,\n    stdin_read_fd: int,\n    stdin_write_fd: int,\n    ready_read_fd: int,\n    ready_write_fd: int,\n    handled_signals: Tuple[int, ...],\n    kitten_exe: str,\n    forward_stdio: bool,\n    pass_fds: tuple[int, ...],\n) -> int:\n    pass\n\n\ndef set_window_padding(os_window_id: int, tab_id: int, window_id: int, left: int, top: int, right: int, bottom: int) -> None:\n    pass\n\n\ndef click_mouse_url(os_window_id: int, tab_id: int, window_id: int) -> bool:\n    pass\n\n\ndef click_mouse_cmd_output(os_window_id: int, tab_id: int, window_id: int, select_cmd_output: bool) -> bool:\n    pass\n\n\ndef move_cursor_to_mouse_if_in_prompt(os_window_id: int, tab_id: int, window_id: int) -> bool:\n    pass\n\n\ndef mouse_selection(os_window_id: int, tab_id: int, window_id: int, code: int, button: int) -> None:\n    pass\n\n\ndef send_mouse_event(\n    screen: Screen, cell_x: int, cell_y: int, button: int, action: int, mods: int,\n    pixel_x: int = 0, pixel_y: int = 0, in_left_half_of_cell: bool = False\n) -> bool: ...\n\n\ndef set_window_logo(os_window_id: int, tab_id: int, window_id: int, path: str, position: str, alpha: float, png_data: bytes = b'') -> None:\n    pass\n\n\ndef get_window_logo_settings_if_not_default(os_window_id: int, tab_id: int, window_id: int) -> None | tuple[\n        str, float, tuple[float, float, float, float]]: ...\n\ndef apply_options_update() -> None:\n    pass\n\n\ndef set_os_window_size(os_window_id: int, x: int, y: int) -> bool:\n    pass\n\n\nclass OSWindowSize(TypedDict):\n    width: int\n    height: int\n    framebuffer_width: int\n    framebuffer_height: int\n    xscale: float\n    yscale: float\n    xdpi: float\n    ydpi: float\n    cell_width: int\n    cell_height: int\n    is_layer_shell: bool\n\n\ndef mark_os_window_dirty(os_window_id: int) -> None:\n    pass\n\n\ndef get_os_window_size(os_window_id: int) -> Optional[OSWindowSize]:\n    pass\n\n\ndef get_os_window_pos(os_window_id: int) -> Tuple[int, int]:\n    pass\n\ndef set_os_window_pos(os_window_id: int, x: int, y: int) -> None:\n    pass\n\ndef get_all_processes() -> Tuple[int, ...]:\n    pass\n\ndef glfw_get_monitor_names() -> tuple[tuple[str, str], ...]: ...\n\ndef num_users() -> int:\n    pass\n\n\ndef redirect_mouse_handling(yes: bool) -> None:\n    pass\n\n\ndef get_click_interval() -> float:\n    pass\n\n\ndef send_data_to_peer(peer_id: int, data: Union[str, bytes], is_async_response: bool = False) -> None:\n    pass\n\n\ndef set_os_window_title(os_window_id: int, title: str) -> None:\n    pass\n\n\ndef get_os_window_title(os_window_id: int) -> Optional[str]:\n    pass\n\n\ndef update_ime_position_for_window(window_id: int, force: bool = False, update_focus: int = 0) -> bool:\n    pass\n\n\ndef shm_open(name: str, flags: int, mode: int = 0o600) -> int:\n    pass\n\n\ndef shm_unlink(name: str) -> None:\n    pass\n\n\ndef sigqueue(pid: int, signal: int, value: int) -> None:\n    pass\n\n\ndef read_signals(fd: int, callback: Callable[[SignalInfo], None]) -> None:\n    pass\n\n\ndef install_signal_handlers(*signals: int) -> Tuple[int, int]:\n    pass\n\n\ndef remove_signal_handlers() -> None:\n    pass\n\n\nX25519: int\nSHA1_HASH: int\nSHA224_HASH: int\nSHA256_HASH: int\nSHA384_HASH: int\nSHA512_HASH: int\n\n\nclass Secret:\n    pass\n\n\nclass EllipticCurveKey:\n\n    def __init__(\n        self, algorithm: int = 0  # X25519\n    ): pass\n\n    def derive_secret(\n        self, pubkey: bytes, hash_algorithm: int = 0  # SHA256_HASH\n    ) -> Secret: pass\n\n    @property\n    def public(self) -> bytes: ...\n\n    @property\n    def private(self) -> Secret: ...\n\n\nclass AES256GCMEncrypt:\n\n    def __init__(self, key: Secret): ...\n\n    def add_authenticated_but_unencrypted_data(self, data: bytes) -> None: ...\n\n    def add_data_to_be_encrypted(self, data: bytes, finished: bool = False) -> bytes: ...\n\n    @property\n    def iv(self) -> bytes: ...\n\n    @property\n    def tag(self) -> bytes: ...\n\n\nclass AES256GCMDecrypt:\n\n    def __init__(self, key: Secret, iv: bytes, tag: bytes): ...\n\n    def add_data_to_be_authenticated_but_not_decrypted(self, data: bytes) -> None: ...\n\n    def add_data_to_be_decrypted(self, data: bytes, finished: bool = False) -> bytes: ...\n\n\nclass Shlex:\n    def __init__(self, src: str, allow_ansi_quoted_strings: bool = False): ...\n    def next_word(self) -> Tuple[int, str]: ...\n    def __next__(self) -> str: ...\n    def __iter__(self) -> Iterator[str]: ...\n\n\nclass SingleKey:\n\n    __slots__ = ()\n\n    def __init__(self, mods: int = 0, is_native: object = False, key: int = -1): ...\n    def __hash__(self) -> int: ...\n    def __len__(self) -> int: ...\n    def __getitem__(self, x: int) -> int: ...\n    @property\n    def mods(self) -> int: ...\n    @property\n    def is_native(self) -> bool: ...\n    @property\n    def key(self) -> int: ...\n    @property\n    def defined_with_kitty_mod(self) -> bool: ...\n    def __iter__(self) -> Iterator[int]: ...\n    def _replace(self, mods: int = 0, is_native: object = False, key: int = -1) -> 'SingleKey': ...\n    def resolve_kitty_mod(self, mod: int) -> 'SingleKey': ...\n\n\ndef set_use_os_log(yes: bool) -> None: ...\ndef get_docs_ref_map() -> bytes: ...\ndef set_clipboard_data_types(ct: int, mime_types: Tuple[str, ...]) -> None: ...\ndef get_clipboard_mime(ct: int, mime: Optional[str], callback: Callable[[bytes], None]) -> None: ...\ndef run_with_activation_token(func: Callable[[str], None]) -> bool: ...\ndef toggle_os_window_visibility(os_window_id: int, visible: bool | Literal[-1] = -1, move_to_active_screen: bool = False) -> bool: ...\ndef parse_cli_from_spec(\n    args: list[str], names_map: dict[str, OptionDefinition], defval_map: dict[str, Any]\n) -> tuple[dict[str, tuple[Any, bool]], list[str]]: ...\ndef layer_shell_config_for_os_window(os_window_id: int) -> dict[str, Any] | None: ...\ndef set_layer_shell_config(os_window_id: int, cfg: LayerShellConfig) -> bool: ...\ndef wrapped_kitten_names() -> List[str]: ...\ndef expand_ansi_c_escapes(test: str) -> str: ...\ndef update_tab_bar_edge_colors(os_window_id: int) -> bool: ...\ndef mask_kitty_signals_process_wide() -> None: ...\ndef is_modifier_key(key: int) -> bool: ...\ndef base64_encode(src: Union[str, ReadableBuffer], add_padding: bool = False) -> bytes: ...\ndef base64_encode_into(src: Union[str, ReadableBuffer], output: WriteableBuffer, add_padding: bool = False) -> int: ...\ndef base64_decode(src: Union[str, ReadableBuffer]) -> bytes: ...\ndef base64_decode_into(src: Union[str, ReadableBuffer], output: WriteableBuffer) -> int: ...\ndef cocoa_recreate_global_menu() -> None: ...\ndef cocoa_clear_global_shortcuts() -> None: ...\ndef update_pointer_shape(os_window_id: int) -> None: ...\ndef is_layer_shell_supported() -> bool: ...\ndef os_window_focus_counters() -> Dict[int, int]: ...\ndef find_in_memoryview(buf: Union[bytes, memoryview, bytearray], chr: int) -> int: ...\n@overload\ndef replace_c0_codes_except_nl_space_tab(text: str) -> str:...\n@overload\ndef replace_c0_codes_except_nl_space_tab(text: Union[bytes, memoryview, bytearray]) -> bytes:...\ndef terminfo_data() -> bytes:...\ndef wayland_compositor_data() -> Tuple[int, Optional[str]]:...\ndef monotonic() -> float: ...\ndef timed_debug_print(x: str) -> None: ...\ndef gpu_driver_version_string() -> str: ...\ndef systemd_move_pid_into_new_scope(pid: int, scope_name: str, description: str) -> str: ...\ndef play_desktop_sound_async(name: str, event_id: str = 'test sound', is_path: bool = False, theme_name: str = '') -> str: ...\ndef cocoa_play_system_sound_by_id_async(sound_id: int) -> None: ...\ndef glfw_get_system_color_theme(query_if_unintialized: bool = True) -> Literal['light', 'dark', 'no_preference']: ...\ndef set_redirect_keys_to_overlay(os_window_id: int, tab_id: int, window_id: int, overlay_window_id: int) -> None: ...\ndef buffer_keys_in_window(os_window_id: int, tab_id: int, window_id: int, enabled: bool = True) -> bool: ...\ndef sprite_idx_to_pos(idx: int, xnum: int, ynum: int) -> tuple[int, int, int]: ...\ndef render_box_char(ch: int, width: int, height: int, scale: float = 1.0, dpi_x: float = 96.0, dpi_y: float = 96.0) -> bytes: ...\ndef run_at_exit_cleanup_functions() -> None: ...\ndef all_color_names() -> tuple[tuple[str, Color], ...]: ...\ndef grab_keyboard(grab: bool | None) -> bool: ...\nDecorationTypes = Literal[\n    'curl', 'dashed', 'dotted', 'double', 'straight', 'strikethrough', 'beam_cursor', 'underline_cursor', 'hollow_cursor', 'missing']\ndef render_decoration(\n    which: DecorationTypes, cell_width: int, cell_height: int, underline_position: int, underline_thickness: int, dpi: float = 96.0\n) -> bytes: ...\ndef os_window_is_invisible(os_window_id: int) -> bool: ...\n\nclass MousePosition(TypedDict):\n    cell_x: int\n    cell_y: int\n    in_left_half_of_cell: bool\n\ndef get_mouse_data_for_window(os_window_id: int, tab_id: int, window_id: int) -> Optional[MousePosition]: ...\n\n\nclass StreamingBase64Decoder:\n    # reset the state to empty to start decoding a new stream\n    def reset(self) -> None: ...\n    # decode the specified data\n    def decode(self, data: ReadableBuffer) -> bytes: ...\n    # decode the specified data, return number of bytes written dest should be as large as src (technically 3/4 src + 2)\n    def decode_into(self, dest: WriteableBuffer, src: ReadableBuffer) -> int: ...\n    # whether the data stream decoded so far is complete or not\n    def needs_more_data(self) -> bool: ...\n\n\nclass StreamingBase64Encodeer:\n    def __init__(self, add_trailing_bytes: bool = True) -> None: ...\n    # encode the specified data\n    def encode(self, data: ReadableBuffer) -> bytes: ...\n    # reset the state to empty to start encoding a new stream, return any trailing bytes from the previous encode call\n    def reset(self) -> bytes: ...\n    # encode the specified data, return number of bytes written dest should be at least 4/3 *src + 2 bytes in size\n    def encode_into(self, dest: WriteableBuffer, src: ReadableBuffer) -> int: ...\n\n\ndef start_drag_with_data(\n    os_window_id: int, data_map: dict[str, bytes], thumbnails: Sequence[tuple[bytes, int, int]],\n    operations: int = GLFW_DRAG_OPERATION_MOVE\n) -> None: ...\ndef change_drag_thumbnail(os_window_id: int, idx: int = -1) -> None: ...\ndef draw_single_line_of_text(os_window_id: int, text: str, fg: int, bg: int, width: int, padding_y: int = 2) -> bytes: ...\ndef set_tab_being_dragged(tab_id: int = 0, drag_started: bool = False, x: float = 0, y: float = 0) -> None: ...\ndef get_tab_being_dragged() -> tuple[int, bool, float, float]: ...\ndef request_callback_with_thumbnail(\n        callback: str, os_window_id: int, window_id: int = 0, include_tab_bar: bool = False,\n        scale: float = 0.25, max_width: int = 480\n) -> None: ...\ndef png_from_32bit_rgba_data(data: bytes, width: int, height: int, flip_vertically: bool = False) -> bytes: ...\n"
  },
  {
    "path": "kitty/file_transmission.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport errno\nimport inspect\nimport io\nimport json\nimport os\nimport re\nimport stat\nimport tempfile\nfrom base64 import b85decode\nfrom collections import defaultdict, deque\nfrom collections.abc import Callable, Iterable, Iterator\nfrom contextlib import suppress\nfrom dataclasses import Field, dataclass, field, fields\nfrom enum import Enum, auto\nfrom functools import partial\nfrom gettext import gettext as _\nfrom itertools import count\nfrom time import time_ns\nfrom typing import IO, Any, DefaultDict, Deque, Union\n\nfrom kittens.transfer.utils import IdentityCompressor, ZlibCompressor, abspath, expand_home, home_path\nfrom kitty.fast_data_types import ESC_OSC, FILE_TRANSFER_CODE, AES256GCMDecrypt, add_timer, base64_decode, base64_encode, get_boss, get_options, monotonic\nfrom kitty.types import run_once\nfrom kitty.typing_compat import ReadableBuffer, WriteableBuffer\n\nfrom .utils import log_error\n\nEXPIRE_TIME = 10  # minutes\nMAX_ACTIVE_RECEIVES = MAX_ACTIVE_SENDS = 10\nftc_prefix = str(FILE_TRANSFER_CODE)\n\n\n@run_once\ndef safe_string_pat() -> 're.Pattern[str]':\n    return re.compile(r'[^0-9a-zA-Z_:./@-]')\n\n\ndef safe_string(x: str) -> str:\n    return safe_string_pat().sub('', x)\n\n\ndef as_unicode(x: str | bytes) -> str:\n    if isinstance(x, bytes):\n        x = x.decode('ascii')\n    return x\n\n\ndef encode_bypass(request_id: str, bypass: str) -> str:\n    import hashlib\n    q = request_id + ';' + bypass\n    return 'sha256:' + hashlib.sha256(q.encode('utf-8', 'replace')).hexdigest()\n\n\ndef split_for_transfer(\n    data: bytes | bytearray | memoryview,\n    session_id: str = '', file_id: str = '',\n    mark_last: bool = False,\n    chunk_size: int = 4096\n) -> Iterator['FileTransmissionCommand']:\n    if isinstance(data, (bytes, bytearray)):\n        data = memoryview(data)\n    while len(data):\n        ac = Action.data\n        if mark_last and len(data) <= chunk_size:\n            ac = Action.end_data\n        yield FileTransmissionCommand(action=ac, id=session_id, file_id=file_id, data=data[:chunk_size])\n        data = data[chunk_size:]\n\n\ndef iter_file_metadata(file_specs: Iterable[tuple[str, str]]) -> Iterator[Union['FileTransmissionCommand', 'TransmissionError']]:\n    file_map: DefaultDict[tuple[int, int], list[FileTransmissionCommand]] = defaultdict(list)\n    counter = count()\n\n    def skey(sr: os.stat_result) -> tuple[int, int]:\n        return sr.st_dev, sr.st_ino\n\n    def make_ftc(path: str, spec_id: str, sr: os.stat_result | None = None, parent: str = '') -> FileTransmissionCommand:\n        if sr is None:\n            sr = os.stat(path, follow_symlinks=False)\n        if stat.S_ISLNK(sr.st_mode):\n            ftype = FileType.symlink\n        elif stat.S_ISDIR(sr.st_mode):\n            ftype = FileType.directory\n        elif stat.S_ISREG(sr.st_mode):\n            ftype = FileType.regular\n        else:\n            raise ValueError('Not an appropriate file type')\n        ans = FileTransmissionCommand(\n            action=Action.file, file_id=spec_id, mtime=sr.st_mtime_ns, permissions=stat.S_IMODE(sr.st_mode),\n            name=path, status=str(next(counter)), size=sr.st_size, ftype=ftype, parent=parent\n        )\n        file_map[skey(sr)].append(ans)\n        return ans\n\n    def add_dir(ftc: FileTransmissionCommand) -> None:\n        try:\n            lr = os.listdir(ftc.name)\n        except OSError:\n            return\n        for entry in lr:\n            try:\n                child_ftc = make_ftc(os.path.join(ftc.name, entry), spec_id, parent=ftc.status)\n            except (ValueError, OSError):\n                continue\n            if child_ftc.ftype is FileType.directory:\n                add_dir(child_ftc)\n\n    for spec_id, spec in file_specs:\n        path = spec\n        if not os.path.isabs(path):\n            path = expand_home(path)\n            if not os.path.isabs(path):\n                path = abspath(path, use_home=True)\n        try:\n            sr = os.stat(path, follow_symlinks=False)\n            read_ok = os.access(path, os.R_OK, follow_symlinks=False)\n        except OSError as err:\n            errname = errno.errorcode.get(err.errno, 'EFAIL') if err.errno is not None else 'EFAIL'\n            yield TransmissionError(file_id=spec_id, code=errname, msg='Failed to read spec')\n            continue\n        if not read_ok:\n            yield TransmissionError(file_id=spec_id, code='EPERM', msg='No permission to read spec')\n            continue\n        try:\n            ftc = make_ftc(path, spec_id, sr)\n        except ValueError:\n            yield TransmissionError(file_id=spec_id, code='EINVAL', msg='Not a valid filetype')\n            continue\n        if ftc.ftype is FileType.directory:\n            add_dir(ftc)\n\n    def resolve_symlink(ftc: FileTransmissionCommand) -> FileTransmissionCommand:\n        if ftc.ftype is FileType.symlink:\n            try:\n                dest = os.path.realpath(ftc.name)\n            except OSError:\n                pass\n            else:\n                try:\n                    s = os.stat(dest, follow_symlinks=False)\n                except OSError:\n                    pass\n                else:\n                    tgt = file_map.get(skey(s))\n                    if tgt is not None:\n                        ftc.data = tgt[0].status.encode('utf-8')\n        return ftc\n\n    for fkey, cmds in file_map.items():\n        base = cmds[0]\n        yield resolve_symlink(base)\n        if len(cmds) > 1 and base.ftype is FileType.regular:\n            for q in cmds:\n                if q is not base and q.ftype is FileType.regular:\n                    q.ftype = FileType.link\n                    q.data = base.status.encode('utf-8', 'replace')\n                    yield q\n\n\nclass NameReprEnum(Enum):\n\n    def __repr__(self) -> str:\n        return f'<{self.__class__.__name__}.{self.name}>'\n\n\nclass Action(NameReprEnum):\n    send = auto()\n    file = auto()\n    data = auto()\n    end_data = auto()\n    receive = auto()\n    invalid = auto()\n    cancel = auto()\n    status = auto()\n    finish = auto()\n\n\nclass Compression(NameReprEnum):\n    zlib = auto()\n    none = auto()\n\n\nclass FileType(NameReprEnum):\n    regular = auto()\n    directory = auto()\n    symlink = auto()\n    link = auto()\n\n    @property\n    def short_text(self) -> str:\n        return {FileType.regular: 'fil', FileType.directory: 'dir', FileType.symlink: 'sym', FileType.link: 'lnk'}[self]\n\n    @property\n    def color(self) -> str:\n        return {FileType.regular: 'yellow', FileType.directory: 'magenta', FileType.symlink: 'blue', FileType.link: 'green'}[self]\n\n\nclass TransmissionType(NameReprEnum):\n    simple = auto()\n    rsync = auto()\n\n\nErrorCode = Enum('ErrorCode', 'OK STARTED CANCELED PROGRESS EINVAL EPERM EISDIR ENOENT')\n\n\nclass TransmissionError(Exception):\n\n    def __init__(\n        self, code: ErrorCode | str = ErrorCode.EINVAL,\n        msg: str = 'Generic error',\n        transmit: bool = True,\n        file_id: str = '',\n        name: str = '',\n        size: int = -1,\n        ttype: TransmissionType = TransmissionType.simple,\n    ) -> None:\n        super().__init__(msg)\n        self.transmit = transmit\n        self.file_id = file_id\n        self.human_msg = msg\n        self.code = code\n        self.name = name\n        self.size = size\n        self.ttype = ttype\n\n    def as_ftc(self, request_id: str) -> 'FileTransmissionCommand':\n        name = self.code if isinstance(self.code, str) else self.code.name\n        if self.human_msg:\n            name += ':' + self.human_msg\n        return FileTransmissionCommand(\n            action=Action.status, id=request_id, file_id=self.file_id, status=name, name=self.name, size=self.size, ttype=self.ttype\n        )\n\n\n@run_once\ndef name_to_serialized_map() -> dict[str, str]:\n    ans: dict[str, str] = {}\n    for k in fields(FileTransmissionCommand):\n        ans[k.name] = k.metadata.get('sname', k.name)\n    return ans\n\n\n@run_once\ndef serialized_to_field_map() -> dict[bytes | memoryview, 'Field[Any]']:\n    ans: dict[bytes | memoryview, 'Field[Any]'] = {}\n    for k in fields(FileTransmissionCommand):\n        ans[k.metadata.get('sname', k.name).encode('ascii')] = k\n    return ans\n\n\n@dataclass\nclass FileTransmissionCommand:\n\n    action: Action = field(default=Action.invalid, metadata={'sname': 'ac'})\n    compression: Compression = field(default=Compression.none, metadata={'sname': 'zip'})\n    ftype: FileType = field(default=FileType.regular, metadata={'sname': 'ft'})\n    ttype: TransmissionType = field(default=TransmissionType.simple, metadata={'sname': 'tt'})\n    id: str = ''\n    file_id: str = field(default='', metadata={'sname': 'fid'})\n    bypass: str = field(default='', metadata={'base64': True, 'sname': 'pw'})\n    quiet: int = field(default=0, metadata={'sname': 'q'})\n    mtime: int = field(default=-1, metadata={'sname': 'mod'})\n    permissions: int = field(default=-1, metadata={'sname': 'prm'})\n    size: int = field(default=-1, metadata={'sname': 'sz'})\n    name: str = field(default='', metadata={'base64': True, 'sname': 'n'})\n    status: str = field(default='', metadata={'base64': True, 'sname': 'st'})\n    parent: str = field(default='', metadata={'sname': 'pr'})\n    data: bytes | memoryview = field(default=b'', repr=False, metadata={'sname': 'd'})\n\n    def __repr__(self) -> str:\n        ans = []\n        for k in fields(self):\n            if not k.repr:\n                continue\n            val = getattr(self, k.name)\n            if val != k.default:\n                ans.append(f'{k.name}={val!r}')\n        if self.data:\n            ans.append(f'data={len(self.data)} bytes')\n        return 'FTC(' + ', '.join(ans) + ')'\n\n    def asdict(self, keep_defaults: bool = False) -> dict[str, str | int | bytes]:\n        ans = {}\n        for k in fields(self):\n            val = getattr(self, k.name)\n            if not keep_defaults and val == k.default:\n                continue\n            if inspect.isclass(k.type) and issubclass(k.type, Enum):\n                val = val.name\n            ans[k.name] = val\n        return ans\n\n    def get_serialized_fields(self, prefix_with_osc_code: bool = False) -> Iterator[str | bytes]:\n        nts = name_to_serialized_map()\n        found = False\n        if prefix_with_osc_code:\n            yield ftc_prefix\n            found = True\n\n        for k in fields(self):\n            name = k.name\n            val = getattr(self, name)\n            if val == k.default:\n                continue\n            if found:\n                yield ';'\n            else:\n                found = True\n            yield nts[name]\n            yield '='\n            if inspect.isclass(k.type) and issubclass(k.type, Enum):\n                yield val.name\n            elif k.type == bytes | memoryview:\n                yield base64_encode(val)\n            elif k.type is str:\n                if k.metadata.get('base64'):\n                    yield base64_encode(val.encode('utf-8'))\n                else:\n                    yield safe_string(val)\n            elif k.type is int:\n                yield str(val)\n            else:\n                raise KeyError(f'Field of unknown type: {k.name}')\n\n    def serialize(self, prefix_with_osc_code: bool = False) -> str:\n        return ''.join(map(as_unicode, self.get_serialized_fields(prefix_with_osc_code)))\n\n    @classmethod\n    def deserialize(cls, data: str | bytes | memoryview) -> 'FileTransmissionCommand':\n        ans = FileTransmissionCommand()\n        fmap = serialized_to_field_map()\n        from kittens.transfer.rsync import parse_ftc\n\n        def handle_item(key: memoryview, val: memoryview) -> None:\n            field = fmap.get(key)\n            if field is None:\n                return\n            if inspect.isclass(field.type) and issubclass(field.type, Enum):\n                setattr(ans, field.name, field.type[str(val, \"utf-8\")])\n            elif field.type == bytes | memoryview:\n                setattr(ans, field.name, base64_decode(val))\n            elif field.type is int:\n                setattr(ans, field.name, int(val))\n            elif field.type is str:\n                if field.metadata.get('base64'):\n                    sval = base64_decode(val).decode('utf-8')\n                else:\n                    sval = safe_string(str(val, \"utf-8\"))\n                setattr(ans, field.name, sval)\n\n        parse_ftc(data, handle_item)\n        if ans.action is Action.invalid:\n            raise ValueError('No valid action specified in file transmission command')\n\n        return ans\n\n\nclass IdentityDecompressor:\n\n    def __call__(self, data: bytes | memoryview, is_last: bool = False) -> bytes:\n        return bytes(data)\n\n\nclass ZlibDecompressor:\n\n    def __init__(self) -> None:\n        import zlib\n        self.d = zlib.decompressobj(wbits=0)\n\n    def __call__(self, data: bytes | memoryview, is_last: bool = False) -> bytes:\n        ans = self.d.decompress(data)\n        if is_last:\n            ans += self.d.flush()\n        return ans\n\n\nclass PatchFile:\n\n    def __init__(self, path: str, expected_size: int):\n        from kittens.transfer.rsync import Patcher\n        self.patcher = Patcher(expected_size)\n        self.block_buffer = memoryview(bytearray(self.patcher.block_size))\n        self.path = path\n        self.signature_done = False\n        self.src_file: io.BufferedReader | None = None\n        self._dest_file: IO[bytes] | None = None\n        self.closed = False\n\n    @property\n    def dest_file(self) -> IO[bytes]:\n        if self._dest_file is None:\n            self._dest_file = tempfile.NamedTemporaryFile(mode='wb', dir=os.path.dirname(os.path.abspath(os.path.realpath(self.path))), delete=False)\n        return self._dest_file\n\n    def close(self) -> None:\n        if self.closed:\n            return\n        self.closed = True\n        p = self.patcher\n        del self.block_buffer, self.patcher\n        if self._dest_file is not None and not self._dest_file.closed:\n            self._dest_file.close()\n            p.finish_delta_data()\n            if self.src_file is not None:\n                os.replace(self.dest_file.name, self.src_file.name)\n        if self.src_file is not None and not self.src_file.closed:\n            self.src_file.close()\n\n    def tell(self) -> int:\n        df = self.dest_file\n        if df.closed:\n            return os.path.getsize(self.path)\n        return df.tell()\n\n    def read_from_src(self, pos: int, b: WriteableBuffer) -> int:\n        assert self.src_file is not None\n        self.src_file.seek(pos, os.SEEK_SET)\n        return self.src_file.readinto(b)\n\n    def write_to_dest(self, b: ReadableBuffer) -> None:\n        self.dest_file.write(b)\n\n    def write(self, b: bytes) -> None:\n        self.patcher.apply_delta_data(b, self.read_from_src, self.write_to_dest)\n\n    def next_signature_block(self, buf: memoryview) -> int:\n        if self.signature_done:\n            return 0\n        if self.src_file is None:\n            self.src_file = open(self.path, 'rb')\n            return self.patcher.signature_header(buf)\n        n = self.src_file.readinto(self.block_buffer)\n        if n > 0:\n            n = self.patcher.sign_block(self.block_buffer[:n], buf)\n        else:\n            self.src_file.seek(0, os.SEEK_SET)\n            self.signature_done = True\n        return n\n\n\nclass DestFile:\n\n    def __init__(self, ftc: FileTransmissionCommand) -> None:\n        self.name = ftc.name\n        if not os.path.isabs(self.name):\n            self.name = expand_home(self.name)\n            if not os.path.isabs(self.name):\n                self.name = abspath(self.name, use_home=True)\n        try:\n            self.existing_stat: os.stat_result | None = os.stat(self.name, follow_symlinks=False)\n        except OSError:\n            self.existing_stat = None\n        self.needs_unlink = self.existing_stat is not None and (self.existing_stat.st_nlink > 1 or stat.S_ISLNK(self.existing_stat.st_mode))\n        self.mtime = ftc.mtime\n        self.file_id = ftc.file_id\n        self.permissions = ftc.permissions\n        if self.permissions != FileTransmissionCommand.permissions:\n            self.permissions = stat.S_IMODE(self.permissions)\n        self.ftype = ftc.ftype\n        self.ttype = ftc.ttype\n        self.link_target = b''\n        self.needs_data_sent = self.ttype is not TransmissionType.simple\n        self.decompressor: ZlibDecompressor | IdentityDecompressor = ZlibDecompressor() if ftc.compression is Compression.zlib else IdentityDecompressor()\n        self.closed = self.ftype is FileType.directory\n        self.actual_file: PatchFile | IO[bytes] | None = None\n        self.failed = False\n        self.bytes_written = 0\n\n    def signature_iterator(self) -> PatchFile:\n        self.actual_file = PatchFile(self.name, self.existing_stat.st_size if self.existing_stat is not None else 0)\n        return self.actual_file\n\n    def __repr__(self) -> str:\n        return f'DestFile(name={self.name}, file_id={self.file_id}, actual_file={self.actual_file})'\n\n    def close(self) -> None:\n        if not self.closed:\n            self.closed = True\n            if self.actual_file is not None:\n                self.actual_file.close()\n                self.actual_file = None\n\n    def make_parent_dirs(self) -> str:\n        d = os.path.dirname(self.name)\n        if d:\n            os.makedirs(d, exist_ok=True)\n        return d\n\n    def apply_metadata(self, is_symlink: bool = False) -> None:\n        if self.permissions != FileTransmissionCommand.permissions:\n            if is_symlink:\n                with suppress(NotImplementedError):\n                    os.chmod(self.name, self.permissions, follow_symlinks=False)\n            else:\n                os.chmod(self.name, self.permissions)\n        if self.mtime != FileTransmissionCommand.mtime:\n            if is_symlink:\n                with suppress(NotImplementedError):\n                    os.utime(self.name, ns=(self.mtime, self.mtime), follow_symlinks=False)\n            else:\n                os.utime(self.name, ns=(self.mtime, self.mtime))\n\n    def unlink_existing_if_needed(self, force: bool = False) -> None:\n        if force or self.needs_unlink:\n            with suppress(FileNotFoundError):\n                os.unlink(self.name)\n            self.existing_stat = None\n            self.needs_unlink = False\n\n    def write_data(self, all_files: dict[str, 'DestFile'], data: bytes | memoryview, is_last: bool) -> None:\n        if self.ftype is FileType.directory:\n            raise TransmissionError(code=ErrorCode.EISDIR, file_id=self.file_id, msg='Cannot write data to a directory entry')\n        if self.closed:\n            raise TransmissionError(file_id=self.file_id, msg='Cannot write to a closed file')\n        if self.ftype in (FileType.symlink, FileType.link):\n            self.link_target += data\n            self.bytes_written += len(data)\n            if is_last:\n                lt = self.link_target.decode('utf-8', 'replace')\n                base = self.make_parent_dirs()\n                self.unlink_existing_if_needed(force=True)\n                if lt.startswith('fid:'):\n                    lt = all_files[lt[4:]].name\n                    if self.ftype is FileType.symlink:\n                        lt = os.path.relpath(lt, os.path.dirname(self.name))\n                elif lt.startswith('fid_abs:'):\n                    lt = all_files[lt[8:]].name\n                elif lt.startswith('path:'):\n                    lt = lt[5:]\n                    if not os.path.isabs(lt) and self.ftype is FileType.link:\n                        lt = os.path.join(base, lt)\n                    lt = lt.replace('/', os.sep)\n                else:\n                    raise TransmissionError(msg='Unknown link target type', file_id=self.file_id)\n                if self.ftype is FileType.symlink:\n                    os.symlink(lt, self.name)\n                else:\n                    os.link(lt, self.name)\n                self.close()\n                self.apply_metadata(is_symlink=True)\n        elif self.ftype is FileType.regular:\n            decompressed = self.decompressor(data, is_last=is_last)\n            if self.actual_file is None:\n                self.make_parent_dirs()\n                self.unlink_existing_if_needed()\n                flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC | getattr(os, 'O_CLOEXEC', 0) | getattr(os, 'O_BINARY', 0)\n                self.actual_file = open(os.open(self.name, flags, self.permissions), mode='r+b', closefd=True)\n            af = self.actual_file\n            if decompressed or is_last:\n                af.write(decompressed)\n                self.bytes_written = af.tell()\n            if is_last:\n                self.close()\n                self.apply_metadata()\n\n\ndef check_bypass(password: str, request_id: str, bypass_data: str) -> bool:\n    protocol, sep, bypass_data = bypass_data.partition(':')\n    if protocol == 'kitty-1':\n        try:\n            pcmd = json.loads(bypass_data)\n            pubkey = pcmd.get('pubkey', '')\n            if not pubkey:\n                return False\n            ekey = get_boss().encryption_key\n            d = AES256GCMDecrypt(ekey.derive_secret(b85decode(pubkey)), b85decode(pcmd['iv']), b85decode(pcmd['tag']))\n            data = d.add_data_to_be_decrypted(b85decode(pcmd['encrypted']), True)\n            timestamp, sep, payload = data.decode('utf-8').partition(':')\n            delta = time_ns() - int(timestamp)\n            if abs(delta) > 5 * 60 * 1e9:\n                return False\n            return payload == f'{request_id};{password}'\n        except Exception as err:\n            log_error(f'Invalid file transmission bypass data received: {err}')\n            return False\n    elif protocol == 'sha256':\n        return (encode_bypass(request_id, password) == bypass_data) if password else False\n    else:\n        log_error(f'Invalid file transmission bypass data received with protocol: {protocol}')\n    return False\n\n\nclass ActiveReceive:\n    id: str\n    files: dict[str, DestFile]\n    accepted: bool = False\n\n    def __init__(self, request_id: str, quiet: int, bypass: str) -> None:\n        self.id = request_id\n        self.bypass_ok: bool | None = None\n        if bypass:\n            byp = get_options().file_transfer_confirmation_bypass\n            self.bypass_ok = check_bypass(byp, request_id, bypass)\n        self.files = {}\n        self.last_activity_at = monotonic()\n        self.send_acknowledgements = quiet < 1\n        self.send_errors = quiet < 2\n        self.pending_files_to_transmit_signature_of: Deque[tuple[PatchFile, str]] = deque()\n        self.signature_pending_chunks: Deque[FileTransmissionCommand] = deque()\n\n    @property\n    def is_expired(self) -> bool:\n        return monotonic() - self.last_activity_at > (60 * EXPIRE_TIME)\n\n    def close(self) -> None:\n        for x in self.files.values():\n            x.close()\n        self.files = {}\n\n    def cancel(self) -> None:\n        self.close()\n\n    def start_file(self, ftc: FileTransmissionCommand) -> DestFile:\n        self.last_activity_at = monotonic()\n        if ftc.file_id in self.files:\n            raise TransmissionError(\n                msg=f'The file_id {ftc.file_id} already exists',\n                file_id=ftc.file_id,\n            )\n        self.files[ftc.file_id] = df = DestFile(ftc)\n        return df\n\n    def add_data(self, ftc: FileTransmissionCommand) -> DestFile:\n        self.last_activity_at = monotonic()\n        df = self.files.get(ftc.file_id)\n        if df is None:\n            raise TransmissionError(file_id=ftc.file_id, msg='Cannot write to a file without first starting it')\n        if df.failed:\n            return df\n        try:\n            df.write_data(self.files, ftc.data, ftc.action is Action.end_data)\n        except Exception:\n            df.failed = True\n            with suppress(Exception):\n                df.close()\n            raise\n        return df\n\n    def commit(self, send_os_error: Callable[[OSError, str, 'ActiveReceive', str], None]) -> None:\n        directories = sorted((df for df in self.files.values() if df.ftype is FileType.directory), key=lambda x: len(x.name), reverse=True)\n        for df in directories:\n            with suppress(OSError):\n                # we ignore failures to apply directory metadata as we have already sent an OK for the dir\n                df.apply_metadata()\n\n\nclass SourceFile:\n\n    def __init__(self, ftc: FileTransmissionCommand):\n        self.file_id = ftc.file_id\n        self.path = ftc.name\n        self.ttype = ftc.ttype\n        self.waiting_for_signature = True if self.ttype is TransmissionType.rsync else False\n        self.transmitted = False\n        self.stat = os.stat(self.path, follow_symlinks=False)\n        if stat.S_ISDIR(self.stat.st_mode):\n            raise TransmissionError(ErrorCode.EINVAL, msg='Cannot send a directory', file_id=self.file_id)\n        self.compressor: ZlibCompressor | IdentityCompressor = IdentityCompressor()\n        self.target = b''\n        self.open_file: io.BufferedReader | None = None\n        if stat.S_ISLNK(self.stat.st_mode):\n            self.target = os.readlink(self.path).encode('utf-8')\n        else:\n            self.open_file = open(self.path, 'rb')\n            if ftc.compression is Compression.zlib:\n                self.compressor = ZlibCompressor()\n        from kittens.transfer import rsync\n        self.differ = rsync.Differ() if self.waiting_for_signature else None\n        self.buf = bytearray()\n        self.write_pos = 0\n\n    def write(self, b: ReadableBuffer) -> None:\n        self.buf[self.write_pos:self.write_pos+len(b)] = b\n        self.write_pos += len(b)\n\n    @property\n    def ready_to_transmit(self) -> bool:\n        return not self.transmitted and not self.waiting_for_signature\n\n    def close(self) -> None:\n        if self.open_file is not None:\n            self.open_file.close()\n            self.open_file = None\n        self.differ = None\n\n    def next_chunk(self, sz: int = 1024 * 1024) -> tuple[bytes, int]:\n        data: bytes | memoryview = b''\n        if self.target:\n            self.transmitted = True\n            data = self.target\n        else:\n            if self.open_file is None:\n                self.transmitted = True\n                data = b''\n            else:\n                if self.differ is None:\n                    data = self.open_file.read(sz)\n                    if not data or self.open_file.tell() >= self.stat.st_size:\n                        self.transmitted = True\n                else:\n                    self.write_pos = 0\n                    has_more = self.differ.next_op(self.open_file.readinto, self.write)\n                    data = memoryview(self.buf)[:self.write_pos]\n                    if not has_more:\n                        self.transmitted = True\n        uncompressed_sz = len(data)\n        cchunk = self.compressor.compress(data)\n        if self.transmitted and not isinstance(self.compressor, IdentityCompressor):\n            cchunk += self.compressor.flush()\n        if self.transmitted:\n            self.close()\n        return cchunk, uncompressed_sz\n\n\nclass ActiveSend:\n\n    def __init__(self, request_id: str, quiet: int, bypass: str, num_of_args: int) -> None:\n        self.id = request_id\n        self.expected_num_of_args = num_of_args\n        self.bypass_ok: bool | None = None\n        if bypass:\n            byp = get_options().file_transfer_confirmation_bypass\n            self.bypass_ok = check_bypass(byp, request_id, bypass)\n        self.accepted = False\n        self.last_activity_at = monotonic()\n        self.send_acknowledgements = quiet < 1\n        self.send_errors = quiet < 2\n        self.last_activity_at = monotonic()\n        self.file_specs: list[tuple[str, str]] = []\n        self.queued_files_map: dict[str, SourceFile] = {}\n        self.active_file: SourceFile | None = None\n        self.pending_chunks: Deque[FileTransmissionCommand] = deque()\n        self.metadata_sent = False\n\n    @property\n    def spec_complete(self) -> bool:\n        return self.expected_num_of_args <= len(self.file_specs)\n\n    def add_file_spec(self, cmd: FileTransmissionCommand) -> None:\n        self.last_activity_at = monotonic()\n        if len(self.file_specs) > 8192 or self.spec_complete:\n            raise TransmissionError(ErrorCode.EINVAL, 'Too many file specs')\n        self.file_specs.append((cmd.file_id, cmd.name))\n\n    def add_send_file(self, cmd: FileTransmissionCommand) -> None:\n        self.last_activity_at = monotonic()\n        if len(self.queued_files_map) > 32768:\n            raise TransmissionError(ErrorCode.EINVAL, 'Too many queued files')\n        self.queued_files_map[cmd.file_id] = SourceFile(cmd)\n\n    def add_signature_data(self, cmd: FileTransmissionCommand) -> None:\n        self.last_activity_at = monotonic()\n        af = self.queued_files_map.get(cmd.file_id)\n        if af is None:\n            raise TransmissionError(ErrorCode.EINVAL, f'Signature data for unknown file_id: {cmd.file_id}')\n        sl = af.differ\n        if sl is None:\n            raise TransmissionError(ErrorCode.EINVAL, f'Signature data for file that is not using rsync: {cmd.file_id}')\n        sl.add_signature_data(cmd.data)\n        if cmd.action is Action.end_data:\n            sl.finish_signature_data()\n            af.waiting_for_signature = False\n\n    @property\n    def is_expired(self) -> bool:\n        return monotonic() - self.last_activity_at > (60 * EXPIRE_TIME)\n\n    def close(self) -> None:\n        if self.active_file is not None:\n            self.active_file.close()\n            self.active_file = None\n\n    def next_chunk(self) -> FileTransmissionCommand | None:\n        self.last_activity_at = monotonic()\n        if self.pending_chunks:\n            return self.pending_chunks.popleft()\n        af = self.active_file\n        if af is None:\n            for f in self.queued_files_map.values():\n                if f.ready_to_transmit:\n                    self.active_file = af = f\n                    break\n            if af is None:\n                return None\n            self.queued_files_map.pop(af.file_id, None)\n        while True:\n            chunk, uncompressed_sz = af.next_chunk()\n            if af.transmitted:\n                self.active_file = None\n                break\n            if chunk:\n                break\n        if chunk:\n            self.pending_chunks.extend(split_for_transfer(chunk, file_id=af.file_id, mark_last=af.transmitted))\n            return self.pending_chunks.popleft()\n        elif af.transmitted:\n            return FileTransmissionCommand(action=Action.end_data, file_id=af.file_id)\n        return None\n\n    def return_chunk(self, ftc: FileTransmissionCommand) -> None:\n        self.pending_chunks.insert(0, ftc)\n\n\nclass FileTransmission:\n\n    def __init__(self, window_id: int):\n        self.window_id = window_id\n        self.active_receives: dict[str, ActiveReceive] = {}\n        self.active_sends: dict[str, ActiveSend] = {}\n        self.pending_receive_responses: Deque[FileTransmissionCommand] = deque()\n        self.pending_timer: int | None = None\n\n    def callback_after(self, callback: Callable[[int | None], None], timeout: float = 0) -> int | None:\n        return add_timer(callback, timeout, False)\n\n    def start_pending_timer(self) -> None:\n        if self.pending_timer is None:\n            self.pending_timer = self.callback_after(self.try_pending, 0.2)\n\n    def try_pending(self, timer_id: int | None) -> None:\n        self.pending_timer = None\n        while self.pending_receive_responses:\n            payload = self.pending_receive_responses.popleft()\n            ar = self.active_receives.get(payload.id)\n            if ar is None:\n                continue\n            if not self.write_ftc_to_child(payload, appendleft=True):\n                break\n            ar.last_activity_at = monotonic()\n        self.prune_expired()\n\n    def __del__(self) -> None:\n        for ar in self.active_receives.values():\n            ar.close()\n        self.active_receives = {}\n        for a in self.active_sends.values():\n            a.close()\n        self.active_receives = {}\n        self.active_sends = {}\n\n    def drop_receive(self, receive_id: str) -> None:\n        ar = self.active_receives.pop(receive_id, None)\n        if ar is not None:\n            ar.close()\n\n    def drop_send(self, send_id: str) -> None:\n        a = self.active_sends.pop(send_id, None)\n        if a is not None:\n            a.close()\n\n    def prune_expired(self) -> None:\n        for k in tuple(self.active_receives):\n            if self.active_receives[k].is_expired:\n                self.drop_receive(k)\n        for a in tuple(self.active_sends):\n            if self.active_sends[a].is_expired:\n                self.drop_send(a)\n\n    def handle_serialized_command(self, data: memoryview) -> None:\n        try:\n            cmd = FileTransmissionCommand.deserialize(data)\n        except Exception as e:\n            log_error(f'Failed to parse file transmission command with error: {e}')\n            return\n        # print('from kitten:', cmd)\n        if not cmd.id:\n            log_error('File transmission command without id received, ignoring')\n            return\n        if cmd.action is Action.cancel:\n            if cmd.id in self.active_receives:\n                self.handle_receive_cmd(cmd)\n                return\n            if cmd.id in self.active_sends:\n                self.handle_send_cmd(cmd)\n                return\n        self.prune_expired()\n        if cmd.id in self.active_receives or cmd.action is Action.send:\n            self.handle_receive_cmd(cmd)\n        if cmd.id in self.active_sends or cmd.action is Action.receive:\n            self.handle_send_cmd(cmd)\n\n    def handle_send_cmd(self, cmd: FileTransmissionCommand) -> None:\n        if cmd.id in self.active_sends:\n            asd = self.active_sends[cmd.id]\n            if cmd.action is Action.receive:\n                log_error('File transmission receive received for already active id, aborting')\n                self.drop_send(cmd.id)\n                return\n            if cmd.action is Action.file:\n                try:\n                    asd.add_send_file(cmd) if asd.metadata_sent else asd.add_file_spec(cmd)\n                except OSError as err:\n                    self.send_fail_on_os_error(err, 'Failed to add send file', asd, cmd.file_id)\n                    self.drop_send(asd.id)\n                    return\n                except TransmissionError as err:\n                    self.drop_send(asd.id)\n                    if asd.send_errors:\n                        self.send_transmission_error(asd.id, err)\n                    return\n                if asd.metadata_sent:\n                    self.pump_send_chunks(asd)\n                else:\n                    if asd.spec_complete and asd.accepted:\n                        self.send_metadata_for_send_transfer(asd)\n                return\n            if cmd.action in (Action.data, Action.end_data):\n                try:\n                    asd.add_signature_data(cmd)\n                except TransmissionError as err:\n                    self.drop_send(asd.id)\n                    if asd.send_errors:\n                        self.send_transmission_error(asd.id, err)\n                else:\n                    self.pump_send_chunks(asd)\n            elif cmd.action in (Action.status, Action.finish):\n                self.drop_send(asd.id)\n                return\n            if not asd.accepted:\n                log_error(f'File transmission command {cmd.action} received for pending id: {cmd.id}, aborting')\n                self.drop_send(cmd.id)\n                return\n            asd.last_activity_at = monotonic()\n        else:\n            if cmd.action is not Action.receive:\n                log_error(f'File transmission command {cmd.action} received for unknown or rejected id: {cmd.id}, ignoring')\n                return\n            if len(self.active_sends) >= MAX_ACTIVE_SENDS:\n                log_error('New File transmission send with too many active receives, ignoring')\n                return\n            asd = self.active_sends[cmd.id] = ActiveSend(cmd.id, cmd.quiet, cmd.bypass, cmd.size)\n            self.start_send(asd.id)\n            return\n        if cmd.action is Action.cancel:\n            self.drop_send(asd.id)\n            if asd.send_acknowledgements:\n                self.send_status_response(ErrorCode.CANCELED, request_id=asd.id)\n\n    def send_metadata_for_send_transfer(self, asd: ActiveSend) -> None:\n        sent = False\n        for ftc in iter_file_metadata(asd.file_specs):\n            if isinstance(ftc, TransmissionError):\n                sent = True\n                if asd.send_errors:\n                    self.send_transmission_error(asd.id, ftc)\n            else:\n                ftc.id = asd.id\n                self.write_ftc_to_child(ftc)\n                sent = True\n        if sent:\n            self.send_status_response(code=ErrorCode.OK, request_id=asd.id, name=home_path())\n            asd.metadata_sent = True\n        else:\n            self.send_status_response(code=ErrorCode.ENOENT, request_id=asd.id, msg='No files found')\n            self.drop_send(asd.id)\n\n    def pump_send_chunks(self, asd: ActiveSend) -> None:\n        while True:\n            try:\n                ftc = asd.next_chunk()\n            except OSError as err:\n                fid = asd.active_file.file_id if asd.active_file else ''\n                self.send_fail_on_os_error(err, 'Failed to read data from file', asd, file_id=fid)\n                self.drop_send(asd.id)\n                break\n            if ftc is None:\n                break\n            ftc.id = asd.id\n            if not self.write_ftc_to_child(ftc, use_pending=False):\n                asd.return_chunk(ftc)\n                self.callback_after(self.pump_sends, 0.05)\n                break\n\n    def pump_sends(self, timer_id: int | None) -> None:\n        for asd in self.active_sends.values():\n            if asd.metadata_sent:\n                self.pump_send_chunks(asd)\n\n    def handle_receive_cmd(self, cmd: FileTransmissionCommand) -> None:\n        if cmd.id in self.active_receives:\n            ar = self.active_receives[cmd.id]\n            if cmd.action is Action.send:\n                log_error('File transmission send received for already active id, aborting')\n                self.drop_receive(cmd.id)\n                return\n            if not ar.accepted:\n                log_error(f'File transmission command {cmd.action} received for pending id: {cmd.id}, aborting')\n                self.drop_receive(cmd.id)\n                return\n            ar.last_activity_at = monotonic()\n        else:\n            if cmd.action is not Action.send:\n                log_error(f'File transmission command {cmd.action} received for unknown or rejected id: {cmd.id}, ignoring')\n                return\n            if len(self.active_receives) >= MAX_ACTIVE_RECEIVES:\n                log_error('New File transmission send with too many active receives, ignoring')\n                return\n            ar = self.active_receives[cmd.id] = ActiveReceive(cmd.id, cmd.quiet, cmd.bypass)\n            self.start_receive(ar.id)\n            return\n\n        if cmd.action is Action.cancel:\n            self.drop_receive(ar.id)\n            if ar.send_acknowledgements:\n                self.send_status_response(ErrorCode.CANCELED, request_id=ar.id)\n        elif cmd.action is Action.file:\n            try:\n                df = ar.start_file(cmd)\n            except TransmissionError as err:\n                if ar.send_errors:\n                    self.send_transmission_error(ar.id, err)\n            except Exception as err:\n                log_error(f'Transmission protocol failed to start file with error: {err}')\n                if ar.send_errors:\n                    te = TransmissionError(file_id=cmd.file_id, msg=str(err))\n                    self.send_transmission_error(ar.id, te)\n            else:\n                if df.ftype is FileType.directory:\n                    try:\n                        os.makedirs(df.name, exist_ok=True)\n                    except OSError as err:\n                        self.send_fail_on_os_error(err, 'Failed to create directory', ar, df.file_id)\n                    else:\n                        self.send_status_response(ErrorCode.OK, ar.id, df.file_id, name=df.name)\n                else:\n                    if ar.send_acknowledgements:\n                        sz = df.existing_stat.st_size if df.existing_stat is not None else -1\n                        ttype = TransmissionType.rsync \\\n                            if sz > -1 and df.ttype is TransmissionType.rsync and df.ftype is FileType.regular else TransmissionType.simple\n                        self.send_status_response(code=ErrorCode.STARTED, request_id=ar.id, file_id=df.file_id, name=df.name, size=sz, ttype=ttype)\n                        df.ttype = ttype\n                        if ttype is TransmissionType.rsync:\n                            try:\n                                fs = df.signature_iterator()\n                            except OSError as err:\n                                self.send_fail_on_os_error(err, 'Failed to open file to read signature', ar, df.file_id)\n                            else:\n                                ar.pending_files_to_transmit_signature_of.append((fs, df.file_id))\n                                self.callback_after(partial(self.transmit_rsync_signature, ar.id))\n        elif cmd.action in (Action.data, Action.end_data):\n            try:\n                before = 0\n                bf = ar.files.get(cmd.file_id)\n                if bf is not None:\n                    before = bf.bytes_written\n                df = ar.add_data(cmd)\n                if df.failed:\n                    return\n                if ar.send_acknowledgements:\n                    if df.closed:\n                        self.send_status_response(\n                            code=ErrorCode.OK, request_id=ar.id, file_id=df.file_id, name=df.name, size=df.bytes_written)\n                    elif df.bytes_written > before:\n                        self.send_status_response(\n                            code=ErrorCode.PROGRESS, request_id=ar.id, file_id=df.file_id, size=df.bytes_written)\n            except TransmissionError as err:\n                if ar.send_errors:\n                    self.send_transmission_error(ar.id, err)\n            except Exception as err:\n                import traceback\n                st = traceback.format_exc()\n                log_error(f'Transmission protocol failed to write data to file with error: {st}')\n                if ar.send_errors:\n                    te = TransmissionError(file_id=cmd.file_id, msg=str(err))\n                    self.send_transmission_error(ar.id, te)\n        elif cmd.action is Action.finish:\n            try:\n                ar.commit(self.send_fail_on_os_error)\n            except TransmissionError as err:\n                if ar.send_errors:\n                    self.send_transmission_error(ar.id, err)\n            except Exception as err:\n                log_error(f'Transmission protocol failed to commit receive with error: {err}')\n                if ar.send_errors:\n                    te = TransmissionError(msg=str(err))\n                    self.send_transmission_error(ar.id, te)\n            finally:\n                self.drop_receive(ar.id)\n        else:\n            log_error(f'Transmission receive command with unknown action: {cmd.action}, ignoring')\n\n    def transmit_rsync_signature(self, receive_id: str, timer_id: int | None = None) -> None:\n        q = self.active_receives.get(receive_id)\n        if q is None:\n            return\n        ar = q  # for mypy\n        while ar.signature_pending_chunks:\n            if self.write_ftc_to_child(ar.signature_pending_chunks[0], use_pending=False):\n                ar.signature_pending_chunks.popleft()\n            else:\n                self.callback_after(partial(self.transmit_rsync_signature, receive_id), timeout=0.1)\n                return\n        if not ar.pending_files_to_transmit_signature_of:\n            return\n        fs, file_id = ar.pending_files_to_transmit_signature_of[0]\n        pos = 0\n        buf = memoryview(bytearray(4096))\n        is_finished = False\n        while len(buf) >= pos + 32:\n            try:\n                n = fs.next_signature_block(buf[pos:])\n            except OSError as err:\n                if ar.send_errors:\n                    self.send_fail_on_os_error(err, 'Failed to read signature', ar, file_id)\n                return\n            if not n:\n                is_finished = True\n                ar.pending_files_to_transmit_signature_of.popleft()\n                break\n            pos += n\n\n        chunk = buf[:pos]\n        has_capacity = True\n\n        def write_ftc(data: FileTransmissionCommand) -> None:\n            nonlocal has_capacity\n            if has_capacity:\n                if not self.write_ftc_to_child(data, use_pending=False):\n                    has_capacity = False\n                    ar.signature_pending_chunks.append(data)\n            else:\n                ar.signature_pending_chunks.append(data)\n\n        if len(chunk):\n            for data in split_for_transfer(chunk, session_id=receive_id, file_id=file_id):\n                write_ftc(data)\n        if is_finished:\n            endftc = FileTransmissionCommand(id=receive_id, action=Action.end_data, file_id=file_id)\n            write_ftc(endftc)\n        self.callback_after(partial(self.transmit_rsync_signature, receive_id))\n\n    def send_status_response(\n        self, code: ErrorCode | str = ErrorCode.EINVAL,\n        request_id: str = '', file_id: str = '', msg: str = '',\n        name: str = '', size: int = -1,\n        ttype: TransmissionType = TransmissionType.simple,\n    ) -> bool:\n        err = TransmissionError(code=code, msg=msg, file_id=file_id, name=name, size=size, ttype=ttype)\n        return self.write_ftc_to_child(err.as_ftc(request_id))\n\n    def send_transmission_error(self, request_id: str, err: TransmissionError) -> bool:\n        if err.transmit:\n            return self.write_ftc_to_child(err.as_ftc(request_id))\n        return True\n\n    def write_ftc_to_child(self, payload: FileTransmissionCommand, appendleft: bool = False, use_pending: bool = True) -> bool:\n        boss = get_boss()\n        window = boss.window_id_map.get(self.window_id)\n        if window is not None:\n            data = tuple(payload.get_serialized_fields(prefix_with_osc_code=True))\n            queued = window.screen.send_escape_code_to_child(ESC_OSC, data)\n            if not queued:\n                if use_pending:\n                    if appendleft:\n                        self.pending_receive_responses.appendleft(payload)\n                    else:\n                        self.pending_receive_responses.append(payload)\n                    self.start_pending_timer()\n            return queued\n        return False\n\n    def start_send(self, asd_id: str) -> None:\n        asd = self.active_sends[asd_id]\n        if asd.bypass_ok is not None:\n            self.handle_receive_confirmation(asd.bypass_ok, asd_id)\n            return\n        boss = get_boss()\n        window = boss.window_id_map.get(self.window_id)\n        if window is not None:\n            boss.confirm(_(\n                'The remote machine wants to read some files from this computer. Do you want to allow the transfer?'),\n                self.handle_receive_confirmation, asd_id, window=window,\n            )\n\n    def handle_receive_confirmation(self, confirmed: bool, cmd_id: str) -> None:\n        asd = self.active_sends.get(cmd_id)\n        if asd is None:\n            return\n        if confirmed:\n            asd.accepted = True\n        else:\n            self.drop_send(asd.id)\n        if asd.accepted:\n            if asd.send_acknowledgements:\n                self.send_status_response(code=ErrorCode.OK, request_id=asd.id)\n            if asd.spec_complete:\n                self.send_metadata_for_send_transfer(asd)\n        else:\n            if asd.send_errors:\n                self.send_status_response(code=ErrorCode.EPERM, request_id=asd.id, msg='User refused the transfer')\n\n    def start_receive(self, ar_id: str) -> None:\n        ar = self.active_receives[ar_id]\n        if ar.bypass_ok is not None:\n            self.handle_send_confirmation(ar.bypass_ok, ar_id)\n            return\n        boss = get_boss()\n        window = boss.window_id_map.get(self.window_id)\n        if window is not None:\n            boss.confirm(_(\n                'The remote machine wants to send some files to this computer. Do you want to allow the transfer?'),\n                self.handle_send_confirmation, ar_id, window=window,\n            )\n\n    def handle_send_confirmation(self, confirmed: bool, cmd_id: str) -> None:\n        ar = self.active_receives.get(cmd_id)\n        if ar is None:\n            return\n        if confirmed:\n            ar.accepted = True\n        else:\n            self.drop_receive(ar.id)\n        if ar.accepted:\n            if ar.send_acknowledgements:\n                self.send_status_response(code=ErrorCode.OK, request_id=ar.id)\n        else:\n            if ar.send_errors:\n                self.send_status_response(code=ErrorCode.EPERM, request_id=ar.id, msg='User refused the transfer')\n\n    def send_fail_on_os_error(self, err: OSError, msg: str, ar: ActiveSend | ActiveReceive, file_id: str = '') -> None:\n        if not ar.send_errors:\n            return\n        errname = errno.errorcode.get(err.errno, 'EFAIL') if err.errno is not None else 'EFAIL'\n        self.send_status_response(code=errname, msg=msg, request_id=ar.id, file_id=file_id)\n\n    def active_file(self, rid: str = '', file_id: str = '') -> DestFile:\n        return self.active_receives[rid].files[file_id]\n\n\nclass TestFileTransmission(FileTransmission):\n\n    def __init__(self, allow: bool = True) -> None:\n        super().__init__(0)\n        self.test_responses: list[dict[str, str | int | bytes]] = []\n        self.allow = allow\n\n    def write_ftc_to_child(self, payload: FileTransmissionCommand, appendleft: bool = False, use_pending: bool = True) -> bool:\n        self.test_responses.append(payload.asdict())\n        return True\n\n    def start_receive(self, aid: str) -> None:\n        self.handle_send_confirmation(self.allow, aid)\n\n    def start_send(self, aid: str) -> None:\n        self.handle_receive_confirmation(self.allow, aid)\n\n    def callback_after(self, callback: Callable[[int | None], None], timeout: float = 0) -> int | None:\n        callback(None)\n        return None\n"
  },
  {
    "path": "kitty/fixed_size_deque.h",
    "content": "/*\n * fixed_size_deque.h\n * Copyright (C) 2026 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n *\n * A fixed size deque that does not allocate. To use define DEQUE_NAME, DEQUE_CAPACITY and\n * DEQUE_DATA_TYPE and include header. Use deque_push_back() to append. To\n * iterate in append order use deque_at(i) for 0 <= i < deque_size().\n */\n\n#include <stdbool.h>\n#include <string.h>\n\n#ifndef DEQUE_NAME\n#define DEQUE_NAME CircularDeque\n#endif\n\n#ifndef DEQUE_CAPACITY\n#define DEQUE_CAPACITY 50\n#endif\n\n#ifndef DEQUE_DATA_TYPE\n#define DEQUE_DATA_TYPE int\n#endif\n\ntypedef struct {\n    DEQUE_DATA_TYPE items[DEQUE_CAPACITY];\n    unsigned head;  // Index of first element\n    unsigned tail;  // Index one past last element\n    unsigned count; // Number of elements\n} DEQUE_NAME;\n\n// Check if empty\nstatic inline bool\ndeque_is_empty(const DEQUE_NAME* dq) { return dq->count == 0; }\n\n// Check if full\nstatic inline bool\ndeque_is_full(const DEQUE_NAME* dq) { return dq->count == DEQUE_CAPACITY; }\n\n// Get current size\nstatic inline unsigned\ndeque_size(const DEQUE_NAME* dq) { return dq->count; }\n\n// Push to back auto-evicts from front if full.\n// Returns true if an item was evicted, which will be copied to *evicted_item is not NULL.\nstatic inline bool\ndeque_push_back(DEQUE_NAME* dq, DEQUE_DATA_TYPE item, DEQUE_DATA_TYPE *evicted_item) {\n    bool evicted = false;\n    if (deque_is_full(dq)) {\n        // Evict front item\n        if (evicted_item) *evicted_item = dq->items[dq->head];\n        evicted = true;\n        dq->head = (dq->head + 1) % DEQUE_CAPACITY;\n        dq->count--;\n    }\n    dq->items[dq->tail] = item;\n    dq->tail = (dq->tail + 1) % DEQUE_CAPACITY;\n    dq->count++;\n    return evicted;\n}\n\n// Push to front, auto-evicts from back if full.\n// Returns true if an item was evicted, which will be copied to *evicted_item is not NULL.\nstatic inline bool\ndeque_push_front(DEQUE_NAME* dq, DEQUE_DATA_TYPE item, DEQUE_DATA_TYPE *evicted_item) {\n    bool evicted = false;\n\n    if (deque_is_full(dq)) {\n        // Evict oldest (back) item\n        dq->tail = (dq->tail - 1 + DEQUE_CAPACITY) % DEQUE_CAPACITY;\n        if (evicted_item) *evicted_item = dq->items[dq->tail];\n        evicted = true;\n        dq->count--;\n    }\n\n    dq->head = (dq->head - 1 + DEQUE_CAPACITY) % DEQUE_CAPACITY;\n    dq->items[dq->head] = item;\n    dq->count++;\n\n    return evicted;\n}\n\n// Pop from front\nstatic inline bool\ndeque_pop_front(DEQUE_NAME* dq, DEQUE_DATA_TYPE *ans) {\n    if (deque_is_empty(dq)) return false;\n    if (ans) *ans = dq->items[dq->head];\n    dq->head = (dq->head + 1) % DEQUE_CAPACITY;\n    dq->count--;\n    return true;\n}\n\n// Pop from back\nstatic inline bool\ndeque_pop_back(DEQUE_NAME* dq, DEQUE_DATA_TYPE *ans) {\n    if (deque_is_empty(dq)) return false;\n    dq->tail = (dq->tail - 1 + DEQUE_CAPACITY) % DEQUE_CAPACITY;\n    if (ans) *ans = dq->items[dq->tail];\n    dq->count--;\n    return true;\n}\n\n// Peek at front without removing\nstatic inline const DEQUE_DATA_TYPE*\ndeque_peek_front(const DEQUE_NAME* dq) {\n    if (deque_is_empty(dq)) return NULL;\n    return &dq->items[dq->head];\n}\n\n// Peek at back without removing\nstatic inline const DEQUE_DATA_TYPE*\ndeque_peek_back(const DEQUE_NAME* dq) {\n    if (deque_is_empty(dq)) return NULL;\n    int idx = (dq->tail - 1 + DEQUE_CAPACITY) % DEQUE_CAPACITY;\n    return &dq->items[idx];\n}\n\n// Access by index (0 = oldest, count-1 = newest)\nstatic inline const DEQUE_DATA_TYPE*\ndeque_at(const DEQUE_NAME* dq, unsigned index) {\n    if (index >= dq->count) return NULL;\n    return &dq->items[(dq->head + index) % DEQUE_CAPACITY];\n}\n\n// Clear all items (doesn't free items)\nstatic inline void\ndeque_clear(DEQUE_NAME* dq) {\n    dq->head = 0;\n    dq->tail = 0;\n    dq->count = 0;\n}\n\n#undef DEQUE_CAPACITY\n#undef DEQUE_DATA_TYPE\n#undef DEQUE_NAME\n"
  },
  {
    "path": "kitty/font-names.c",
    "content": "/*\n * font-names.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"fonts.h\"\n\nstatic PyObject*\ndecode_name_record(PyObject *namerec) {\n#define d(x) PyLong_AsUnsignedLong(PyTuple_GET_ITEM(namerec, x))\n    unsigned long platform_id = d(0), encoding_id = d(1), language_id = d(2);\n#undef d\n    const char *encoding = \"unicode_escape\";\n    if ((platform_id == 3 && encoding_id == 1) || platform_id == 0) encoding = \"utf-16-be\";\n    else if (platform_id == 1 && encoding_id == 0 && language_id == 0) encoding = \"mac-roman\";\n    PyObject *b = PyTuple_GET_ITEM(namerec, 3);\n    return PyUnicode_Decode(PyBytes_AS_STRING(b), PyBytes_GET_SIZE(b), encoding, \"replace\");\n}\n\n\nstatic bool\nnamerec_matches(PyObject *namerec, unsigned platform_id, unsigned encoding_id, unsigned language_id) {\n#define d(x) PyLong_AsUnsignedLong(PyTuple_GET_ITEM(namerec, x))\n    return d(0) == platform_id && d(1) == encoding_id && d(2) == language_id;\n#undef d\n}\n\nstatic PyObject*\nfind_matching_namerec(PyObject *namerecs, unsigned platform_id, unsigned encoding_id, unsigned language_id) {\n    for (Py_ssize_t i = 0; i < PyList_GET_SIZE(namerecs); i++) {\n        PyObject *namerec = PyList_GET_ITEM(namerecs, i);\n        if (namerec_matches(namerec, platform_id, encoding_id, language_id)) return decode_name_record(namerec);\n    }\n    return NULL;\n}\n\n\nbool\nadd_font_name_record(PyObject *table, uint16_t platform_id, uint16_t encoding_id, uint16_t language_id, uint16_t name_id, const char *string, uint16_t string_len) {\n    RAII_PyObject(key, PyLong_FromUnsignedLong((unsigned long)name_id));\n    if (!key) return false;\n    RAII_PyObject(list, PyDict_GetItem(table, key));\n    if (list == NULL) {\n        list = PyList_New(0);\n        if (!list) return false;\n        if (PyDict_SetItem(table, key, list) != 0) return false;\n    } else Py_INCREF(list);\n    RAII_PyObject(value, Py_BuildValue(\"(H H H y#)\", platform_id, encoding_id, language_id, string, (Py_ssize_t)string_len));\n    if (!value) return false;\n    if (PyList_Append(list, value) != 0) return false;\n    return true;\n}\n\nPyObject*\nget_best_name_from_name_table(PyObject *table, PyObject *name_id) {\n    PyObject *namerecs = PyDict_GetItem(table, name_id);\n    if (namerecs == NULL) return PyUnicode_FromString(\"\");\n    if (PyList_GET_SIZE(namerecs) == 1) return decode_name_record(PyList_GET_ITEM(namerecs, 0));\n#define d(...) { PyObject *ans = find_matching_namerec(namerecs, __VA_ARGS__); if (ans != NULL || PyErr_Occurred()) return ans; }\n    d(3, 1, 1033);  // Microsoft/Windows/US English\n    d(1, 0, 0);     // Mac/Roman/English\n    d(0, 6, 0);     // Unicode/SMP/*\n    d(0, 4, 0);     // Unicode/SMP/*\n    d(0, 3, 0);     // Unicode/BMP/*\n    d(0, 2, 0);     // Unicode/10646-BMP/*\n    d(0, 1, 0);     // Unicode/1.1/*\n#undef d\n    return PyUnicode_FromString(\"\");\n\n}\n\nstatic PyObject*\nget_best_name(PyObject *table, unsigned long name_id) {\n    RAII_PyObject(id, PyLong_FromUnsignedLong(name_id));\n    return get_best_name_from_name_table(table, id);\n}\n\n// OpenType tables are big-endian for god knows what reason so need to byteswap\nstatic uint16_t\nbyteswap(const uint16_t *p) {\n    const uint8_t *b = (const uint8_t*)p;\n    return (((uint16_t)b[0]) << 8) | b[1];\n}\n\nstatic uint32_t\nbyteswap32(const uint32_t *val) {\n    const uint8_t *p = (const uint8_t*)val;\n    return (((uint32_t)p[0]) << 24) | (((uint32_t)p[1]) << 16) | (((uint32_t)p[2]) << 8) | p[3];\n}\n\nstatic double\nload_fixed(const uint32_t *p_) {\n    uint32_t p = byteswap32(p_);\n    static const double denom = 1 << 16;\n    return ((int32_t)p) / denom;\n}\n\n#define next byteswap(p++)\n#define next32 load_fixed(p32++)\n\nPyObject*\nread_name_font_table(const uint8_t *table, size_t table_len) {\n    if (!table || table_len < 9 * sizeof(uint16_t)) return PyDict_New();\n    uint16_t *p = (uint16_t*)table; p++;\n    uint16_t num_of_name_records = next, storage_offset = next;\n    const uint8_t *storage = table + storage_offset, *slimit = table + table_len;\n    if (storage >= slimit) return PyDict_New();\n    RAII_PyObject(ans, PyDict_New());\n    for (; num_of_name_records > 0 && p + 6 <= (uint16_t*)slimit; num_of_name_records--) {\n        uint16_t platform_id = next, encoding_id = next, language_id = next, name_id = next, length = next, offset = next;\n        const uint8_t *s = storage + offset;\n        if (s + length <= slimit && !add_font_name_record(\n            ans, platform_id, encoding_id, language_id, name_id, (const char*)(s), length)) return NULL;\n    }\n    Py_INCREF(ans);\n    return ans;\n\n}\n\nstatic bool is_digit(char x) { return '0' <= x && x <= '9'; }\n\nstatic PyObject*\nread_cv_feature_table(const uint8_t *table, const uint8_t *limit, PyObject *name_lookup_table) {\n    RAII_PyObject(ans, PyDict_New()); if (!ans) return NULL;\n    if (limit - table >= 12) {\n        uint16_t *p = (uint16_t*)(table + 2);\n        uint16_t name_id = next, tooltip_id = next, sample_id = next, num_params = next, first_value_id = next;\n        if (name_id) {\n            RAII_PyObject(name, get_best_name(name_lookup_table, name_id)); if (!name) return NULL;\n            if (PyDict_SetItemString(ans, \"name\", name) != 0) return NULL;\n        }\n        if (tooltip_id) {\n            RAII_PyObject(tooltip, get_best_name(name_lookup_table, tooltip_id)); if (!tooltip) return NULL;\n            if (PyDict_SetItemString(ans, \"tooltip\", tooltip) != 0) return NULL;\n        }\n        if (sample_id) {\n            RAII_PyObject(sample, get_best_name(name_lookup_table, sample_id)); if (!sample) return NULL;\n            if (PyDict_SetItemString(ans, \"sample\", sample) != 0) return NULL;\n        }\n        if (num_params && first_value_id) {\n            RAII_PyObject(params, PyTuple_New(num_params)); if (!params) return NULL;\n            for (uint16_t i = 0; i < num_params; i++) {\n                PyObject *pval = get_best_name(name_lookup_table, first_value_id + i); if (!pval) return NULL;\n                PyTuple_SET_ITEM(params, i, pval);\n            }\n            if (PyDict_SetItemString(ans, \"params\", params) != 0) return NULL;\n        }\n    }\n    Py_INCREF(ans); return ans;\n}\n\nstatic PyObject*\nread_ss_feature_table(const uint8_t *table, const uint8_t *limit, PyObject *name_lookup_table) {\n    RAII_PyObject(ans, PyDict_New()); if (!ans) return NULL;\n    if (limit - table < 4) { Py_INCREF(ans); return ans; }\n    uint16_t *p = (uint16_t*)(table + 2);\n    uint16_t name_id = next;\n    if (name_id) {\n        RAII_PyObject(name, get_best_name(name_lookup_table, name_id)); if (!name) return NULL;\n        if (PyDict_SetItemString(ans, \"name\", name) != 0) return NULL;\n    }\n    Py_INCREF(ans); return ans;\n}\n\nbool\nread_features_from_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output) {\n    if (table_len < 20) return true;\n    const uint16_t *p = (uint16_t*)table;\n    const uint8_t *limit = table + table_len;\n    uint16_t major_version = next, minor_version = next, script_list_offset = next, feature_list_offset = next;\n    (void)major_version; (void)minor_version; (void)script_list_offset;\n    const uint8_t *feature_list_table = table + feature_list_offset;\n    char tag_buf[5] = {0};\n    if (feature_list_table + 2 >= limit) return true;\n    p = (uint16_t*)feature_list_table;\n    uint16_t feature_count = next;\n    const uint8_t *pos = (uint8_t*)p;\n    for (uint16_t i = 0; i < feature_count && pos + 6 <= limit; pos += 6, i++) {\n        memcpy(tag_buf, pos, 4);\n        RAII_PyObject(tag, PyUnicode_FromString(tag_buf));\n        if (!tag) return false;\n        if (PyDict_Contains(output, tag) == 1) continue;\n        if (PyDict_SetItem(output, tag, Py_None) != 0) return false;\n        p = (uint16_t*)(pos + 4); uint16_t offset_to_feature_table = next;\n        const uint8_t *feature_table = feature_list_table + offset_to_feature_table;\n        if (feature_table + 2 > limit) continue;\n        p = (uint16_t*)(feature_table); uint16_t offset_to_feature_params_table = next;\n        if (tag_buf[0] == 'c' && tag_buf[1] == 'v' && is_digit(tag_buf[2]) && is_digit(tag_buf[3])) {\n            if (offset_to_feature_params_table) {\n                RAII_PyObject(cv, read_cv_feature_table(feature_table + offset_to_feature_params_table, limit, name_lookup_table));\n                if (!cv) return false;\n                if (PyDict_SetItem(output, tag, cv) != 0) return false;\n            }\n        } else if (tag_buf[0] == 's' && tag_buf[1] == 's' && '0' <= tag_buf[2] && tag_buf[2] <= '2' && is_digit(tag_buf[3])) {\n            if (offset_to_feature_params_table) {\n                RAII_PyObject(ss, read_ss_feature_table(feature_table + offset_to_feature_params_table, limit, name_lookup_table));\n                if (!ss) return false;\n                if (PyDict_SetItem(output, tag, ss) != 0) return false;\n            }\n        }\n    }\n    return true;\n}\n\nbool\nread_STAT_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output) {\n    uint16_t elided_fallback_name_id = 0;\n    RAII_PyObject(design_axes, PyTuple_New(0));\n    RAII_PyObject(multi_axis_styles, PyTuple_New(0));\n    if (!design_axes || !multi_axis_styles) return false;\n    if (table_len < 20) goto ok;\n    const uint16_t *p = (uint16_t*)table;\n    uint16_t major_version = next, minor_version = next, size_of_design_axis_entry = next, count_of_design_axis_entries = next;\n    const uint32_t *p32 = (uint32_t*)p;\n    uint32_t offset_to_start_of_design_axes_entries = byteswap32(p32++);\n    p = (uint16_t*)p32;\n    uint16_t count_of_axis_value_entries = next;\n    p32 = (uint32_t*)p;\n    uint32_t offset_to_start_of_axis_value_entries = byteswap32(p32++);\n    p = (uint16_t*)p32;\n    elided_fallback_name_id = next;\n    if (major_version == 1 && minor_version < 1) elided_fallback_name_id = 0;\n    const uint8_t *table_limit = table + table_len;\n    size_t count = 0;\n    if (_PyTuple_Resize(&design_axes, count_of_design_axis_entries) == -1) return false;\n    for (\n        const uint8_t *pos = table + offset_to_start_of_design_axes_entries;\n        pos + size_of_design_axis_entry <= table_limit && count < count_of_design_axis_entries;\n        pos += size_of_design_axis_entry, count++\n    ) {\n        p = (uint16_t*)(pos + 4);\n        uint16_t name_id = next, ordering = next;\n        PyObject *rec = Py_BuildValue(\"{ss# sN sH sN}\", \"tag\", (char*)pos, 4, \"name\", get_best_name(name_lookup_table, name_id), \"ordering\", ordering, \"values\", PyList_New(0));\n        if (!rec) return false;\n        PyTuple_SET_ITEM(design_axes, count, rec);\n    }\n    if (_PyTuple_Resize(&design_axes, count) == -1) return false;\n    count = 0;\n    const uint8_t *start_of_axis_values_offsets_array = table + offset_to_start_of_axis_value_entries;\n    Py_ssize_t i = 0;\n    if (_PyTuple_Resize(&multi_axis_styles, count_of_axis_value_entries) == -1) return false;\n    for (\n        const uint8_t *pos = start_of_axis_values_offsets_array;\n        pos + 2 <= table_limit && count < count_of_axis_value_entries;\n        pos += 2, count++\n    ) {\n        p = (uint16_t*)pos;\n        uint16_t offset = next;\n        const uint8_t *start_of_axis_values_table = start_of_axis_values_offsets_array + offset;\n        if (start_of_axis_values_table + 12 > table_limit) continue;\n        p = (uint16_t*)(start_of_axis_values_table);\n        uint16_t format = next, axis_index = next, flags = next, value_name_id = next;\n        p32 = (uint32_t*)p;\n#define app(fmt, ...) { \\\n    RAII_PyObject(v, Py_BuildValue(\"{sH sH sN \" fmt \"}\", \\\n                \"format\", format, \"flags\", flags, \"name\", get_best_name(name_lookup_table, value_name_id), __VA_ARGS__)); \\\n    if (!v) return false; \\\n    PyObject *l = PyDict_GetItemString(PyTuple_GET_ITEM(design_axes, axis_index), \"values\"); \\\n    if (l && PyList_Append(l, v) != 0) return false;  \\\n}\n        switch(format) {\n            case 1: if (p32 + 1 <= (uint32_t*)table_limit && axis_index < PyTuple_GET_SIZE(design_axes)) {\n                const double value = next32; app(\"sd\", \"value\", value);\n            } break;\n            case 2: if (p32 + 3 <= (uint32_t*)table_limit && axis_index < PyTuple_GET_SIZE(design_axes)) {\n                const double value = next32, minimum = next32, maximum = next32;\n                app(\"sd sd sd\", \"value\", value, \"minimum\", minimum, \"maximum\", maximum);\n            } break;\n            case 3: if (p32 + 2 <= (uint32_t*)table_limit && axis_index < PyTuple_GET_SIZE(design_axes)) {\n                const double value = next32, linked_value = next32;\n                app(\"sd sd\", \"value\", value, \"linked_value\", linked_value);\n            } break;\n            case 4: if ((uint8_t*)(p) + 6 * axis_index <= table_limit) {\n                RAII_PyObject(values, PyTuple_New(axis_index));\n                if (!values) return false;\n                for (uint16_t n = 0; n < axis_index; n++, p += 3) {\n                    uint16_t actual_axis_index = next;\n                    p32 = (uint32_t*)p; double value = next32;\n                    PyObject *e = Py_BuildValue(\"{sH sd}\", \"design_index\", actual_axis_index, \"value\", value);\n                    if (!e) return false;\n                    PyTuple_SET_ITEM(values, n, e);\n                }\n                PyObject *e = Py_BuildValue(\"{sH sN sO}\", \"flags\", flags,\n                        \"name\", get_best_name(name_lookup_table, value_name_id), \"values\", values);\n                if (!e) return false;\n                PyTuple_SET_ITEM(multi_axis_styles, i++, e);\n            } break;\n        }\n    }\n    if (_PyTuple_Resize(&multi_axis_styles, i) == -1) return false;\nok:\n    if (PyDict_SetItemString(output, \"design_axes\", design_axes) != 0) return false;\n    if (PyDict_SetItemString(output, \"multi_axis_styles\", multi_axis_styles) != 0) return false;\n    if (PyDict_SetItemString(output, \"elided_fallback_name\", elided_fallback_name_id ? get_best_name(name_lookup_table, elided_fallback_name_id) : PyUnicode_FromString(\"\")) != 0) return false;\n    return true;\n}\n\nbool\nread_fvar_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output) {\n    RAII_PyObject(named_styles, PyTuple_New(0)); if (!named_styles) return false;\n    RAII_PyObject(axes, PyTuple_New(0)); if (!axes) return false;\n\n    if (!table || table_len < 14 * sizeof(uint16_t)) goto ok;\n\n    const uint16_t *p = (uint16_t*)table;\n    p += 2;\n    const uint16_t offset_to_start_of_axis_array = next; next;\n    const uint16_t num_of_axis_records = next, size_of_axis_record = next, num_of_name_records = next, size_of_name_record = next;\n    const uint16_t size_of_coordinates = num_of_axis_records * sizeof(int32_t);\n    if (size_of_name_record < size_of_coordinates + 4) {\n        PyErr_Format(PyExc_ValueError, \"size of name record: %u too small\", size_of_name_record); return NULL;\n    }\n    const bool has_postscript_name = size_of_name_record >= 3 * sizeof(uint16_t) + size_of_coordinates;\n    uint16_t i = 0;\n    if (size_of_axis_record < 20) { PyErr_Format(PyExc_ValueError, \"size of axis record: %u too small\", size_of_axis_record); return NULL; }\n    if (_PyTuple_Resize(&axes, num_of_axis_records) == -1) return NULL;\n    for (\n        const uint8_t *pos = table + offset_to_start_of_axis_array;\n        pos + size_of_axis_record <= table + table_len && i < num_of_axis_records;\n        i++, pos += size_of_axis_record\n    ) {\n        uint32_t *p32 = (uint32_t*)(pos + 4);\n        const double minimum = next32, def = next32, maximum = next32;\n        p = (uint16_t*)(pos + 16);\n        int32_t flags = next, strid = next;\n        PyObject *axis = Py_BuildValue(\"{sd sd sd sN sO sN}\",\n            \"minimum\", minimum, \"maximum\", maximum, \"default\", def, \"tag\", PyUnicode_FromStringAndSize((const char*)pos, 4),\n            \"hidden\", (flags & 1) ? Py_True : Py_False, \"strid\", get_best_name(name_lookup_table, strid)\n        ); if (!axis) return NULL;\n        PyTuple_SET_ITEM(axes, i, axis);\n    }\n    if (_PyTuple_Resize(&axes, i) == -1) return NULL;\n    char tag_buf[5] = {0};\n    i = 0;\n    if (_PyTuple_Resize(&named_styles, num_of_name_records) == -1) return NULL;\n    for (\n        const uint8_t *pos = table + offset_to_start_of_axis_array + num_of_axis_records * size_of_axis_record;\n        pos + size_of_name_record <= table + table_len && i < num_of_name_records;\n        i++, pos += size_of_name_record\n    ) {\n        p = (uint16_t*)pos;\n        uint16_t name_id = next, psname_id = 0xffff; next;\n        const uint32_t *p32 = (uint32_t*)p;\n        RAII_PyObject(axis_values, PyDict_New());\n        if (!axis_values) return NULL;\n        for (uint16_t i = 0; i < num_of_axis_records; i++) {\n            const uint8_t *t = table + offset_to_start_of_axis_array + i * size_of_axis_record;\n            memcpy(tag_buf, t, 4);\n            RAII_PyObject(pval, PyFloat_FromDouble(next32));\n            if (!pval || PyDict_SetItemString(axis_values, tag_buf, pval) != 0) return NULL;\n        }\n        if (has_postscript_name) { p = (uint16_t*)p32; psname_id = next; }\n        PyObject *ns = Py_BuildValue(\"{sO sN sN}\",\n            \"axis_values\", axis_values, \"name\", get_best_name(name_lookup_table, name_id),\n            \"psname\", (psname_id != 0xffff && psname_id ? get_best_name(name_lookup_table, psname_id) : PyUnicode_FromString(\"\")));\n        if (!ns) return NULL;\n        PyTuple_SET_ITEM(named_styles, i, ns);\n    }\n    if (_PyTuple_Resize(&named_styles, i) == -1) return NULL;\nok:\n    if (PyDict_SetItemString(output, \"variations_postscript_name_prefix\", get_best_name(name_lookup_table, 25)) != 0) return false;\n    if (PyDict_SetItemString(output, \"axes\", axes) != 0) return false;\n    if (PyDict_SetItemString(output, \"named_styles\", named_styles) != 0) return false;\n    return true;\n}\n#undef next32\n#undef next\n"
  },
  {
    "path": "kitty/fontconfig.c",
    "content": "/*\n * fontconfig.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"cleanup.h\"\n#include \"lineops.h\"\n#include \"fonts.h\"\n#include <fontconfig/fontconfig.h>\n#include <dlfcn.h>\n#include \"freetype_render_ui_text.h\"\n#ifndef FC_COLOR\n#define FC_COLOR \"color\"\n#endif\n\n\nstatic bool initialized = false;\nstatic void* libfontconfig_handle = NULL;\nstatic struct {PyObject *face, *descriptor;} builtin_nerd_font = {0};\n\n#define FcInit dynamically_loaded_fc_symbol.Init\n#define FcFini dynamically_loaded_fc_symbol.Fini\n#define FcCharSetAddChar dynamically_loaded_fc_symbol.CharSetAddChar\n#define FcPatternDestroy dynamically_loaded_fc_symbol.PatternDestroy\n#define FcObjectSetDestroy dynamically_loaded_fc_symbol.ObjectSetDestroy\n#define FcPatternAddDouble dynamically_loaded_fc_symbol.PatternAddDouble\n#define FcPatternAddString dynamically_loaded_fc_symbol.PatternAddString\n#define FcFontMatch dynamically_loaded_fc_symbol.FontMatch\n#define FcCharSetCreate dynamically_loaded_fc_symbol.CharSetCreate\n#define FcPatternGetString dynamically_loaded_fc_symbol.PatternGetString\n#define FcFontSetDestroy dynamically_loaded_fc_symbol.FontSetDestroy\n#define FcPatternGetInteger dynamically_loaded_fc_symbol.PatternGetInteger\n#define FcPatternAddBool dynamically_loaded_fc_symbol.PatternAddBool\n#define FcFontList dynamically_loaded_fc_symbol.FontList\n#define FcObjectSetBuild dynamically_loaded_fc_symbol.ObjectSetBuild\n#define FcCharSetDestroy dynamically_loaded_fc_symbol.CharSetDestroy\n#define FcConfigSubstitute dynamically_loaded_fc_symbol.ConfigSubstitute\n#define FcDefaultSubstitute dynamically_loaded_fc_symbol.DefaultSubstitute\n#define FcPatternAddInteger dynamically_loaded_fc_symbol.PatternAddInteger\n#define FcPatternCreate dynamically_loaded_fc_symbol.PatternCreate\n#define FcPatternGetBool dynamically_loaded_fc_symbol.PatternGetBool\n#define FcPatternAddCharSet dynamically_loaded_fc_symbol.PatternAddCharSet\n#define FcConfigAppFontAddFile dynamically_loaded_fc_symbol.ConfigAppFontAddFile\n\nstatic struct {\n    FcBool(*Init)(void);\n    void(*Fini)(void);\n    FcBool (*CharSetAddChar) (FcCharSet *fcs, FcChar32 ucs4);\n    void (*PatternDestroy) (FcPattern *p);\n    void (*ObjectSetDestroy) (FcObjectSet *os);\n    FcBool (*PatternAddDouble) (FcPattern *p, const char *object, double d);\n    FcBool (*PatternAddString) (FcPattern *p, const char *object, const FcChar8 *s);\n    FcPattern * (*FontMatch) (FcConfig\t*config, FcPattern\t*p, FcResult\t*result);\n    FcCharSet* (*CharSetCreate) (void);\n    FcResult (*PatternGetString) (const FcPattern *p, const char *object, int n, FcChar8 ** s);\n    void (*FontSetDestroy) (FcFontSet *s);\n    FcResult (*PatternGetInteger) (const FcPattern *p, const char *object, int n, int *i);\n    FcBool (*PatternAddBool) (FcPattern *p, const char *object, FcBool b);\n    FcFontSet * (*FontList) (FcConfig\t*config, FcPattern\t*p, FcObjectSet *os);\n    FcObjectSet * (*ObjectSetBuild) (const char *first, ...);\n    void (*CharSetDestroy) (FcCharSet *fcs);\n    FcBool (*ConfigSubstitute) (FcConfig\t*config, FcPattern\t*p, FcMatchKind\tkind);\n    void (*DefaultSubstitute) (FcPattern *pattern);\n    FcBool (*PatternAddInteger) (FcPattern *p, const char *object, int i);\n    FcPattern * (*PatternCreate) (void);\n    FcResult (*PatternGetBool) (const FcPattern *p, const char *object, int n, FcBool *b);\n    FcBool (*PatternAddCharSet) (FcPattern *p, const char *object, const FcCharSet *c);\n    FcBool (*ConfigAppFontAddFile) (FcConfig *config, const FcChar8 *file);\n} dynamically_loaded_fc_symbol = {0};\n#define LOAD_FUNC(name) {\\\n    *(void **) (&dynamically_loaded_fc_symbol.name) = dlsym(libfontconfig_handle, \"Fc\" #name); \\\n    if (!dynamically_loaded_fc_symbol.name) { \\\n        const char* error = dlerror(); \\\n        fatal(\"Failed to load the function Fc\" #name \" with error: %s\", error ? error : \"\"); \\\n    } \\\n}\n\n\nstatic void\nload_fontconfig_lib(void) {\n        const char* libnames[] = {\n#if defined(_KITTY_FONTCONFIG_LIBRARY)\n            _KITTY_FONTCONFIG_LIBRARY,\n#else\n            \"libfontconfig.so\",\n            // some installs are missing the .so symlink, so try the full name\n            \"libfontconfig.so.1\",\n#endif\n            NULL\n        };\n        for (int i = 0; libnames[i]; i++) {\n            libfontconfig_handle = dlopen(libnames[i], RTLD_LAZY);\n            if (libfontconfig_handle) break;\n        }\n        if (libfontconfig_handle == NULL) { fatal(\"Failed to find and load fontconfig\"); }\n        dlerror();    /* Clear any existing error */\n        LOAD_FUNC(Init);\n        LOAD_FUNC(Fini);\n        LOAD_FUNC(CharSetAddChar);\n        LOAD_FUNC(PatternDestroy);\n        LOAD_FUNC(ObjectSetDestroy);\n        LOAD_FUNC(PatternAddDouble);\n        LOAD_FUNC(PatternAddString);\n        LOAD_FUNC(FontMatch);\n        LOAD_FUNC(CharSetCreate);\n        LOAD_FUNC(PatternGetString);\n        LOAD_FUNC(FontSetDestroy);\n        LOAD_FUNC(PatternGetInteger);\n        LOAD_FUNC(PatternAddBool);\n        LOAD_FUNC(FontList);\n        LOAD_FUNC(ObjectSetBuild);\n        LOAD_FUNC(CharSetDestroy);\n        LOAD_FUNC(ConfigSubstitute);\n        LOAD_FUNC(DefaultSubstitute);\n        LOAD_FUNC(PatternAddInteger);\n        LOAD_FUNC(PatternCreate);\n        LOAD_FUNC(PatternGetBool);\n        LOAD_FUNC(PatternAddCharSet);\n        LOAD_FUNC(ConfigAppFontAddFile);\n}\n#undef LOAD_FUNC\n\nstatic void\nensure_initialized(void) {\n    if (!initialized) {\n        load_fontconfig_lib();\n        if (!FcInit()) fatal(\"Failed to initialize fontconfig library\");\n        initialized = true;\n    }\n}\n\nstatic void\nfinalize(void) {\n    if (initialized) {\n        Py_CLEAR(builtin_nerd_font.face);\n        Py_CLEAR(builtin_nerd_font.descriptor);\n        FcFini();\n        dlclose(libfontconfig_handle);\n        libfontconfig_handle = NULL;\n        initialized = false;\n    }\n}\n\nstatic PyObject*\npybool(FcBool x) { PyObject *ans = x ? Py_True: Py_False; Py_INCREF(ans); return ans; }\n\nstatic PyObject*\npyspacing(int val) {\n#define S(x) case FC_##x: return PyUnicode_FromString(#x)\n    switch(val) { S(PROPORTIONAL); S(DUAL); S(MONO); S(CHARCELL); default: return PyUnicode_FromString(\"UNKNOWN\"); }\n#undef S\n}\n\nstatic PyObject*\nincrement_and_return(PyObject *x) { if (x) Py_INCREF(x); return x; }\n\nstatic PyObject*\npattern_as_dict(FcPattern *pat) {\n    RAII_PyObject(ans, Py_BuildValue(\"{ss}\", \"descriptor_type\", \"fontconfig\"));\n    if (ans == NULL) return NULL;\n\n#define PS(x) PyUnicode_Decode((const char*)x, strlen((const char*)x), \"UTF-8\", \"replace\")\n\n#define G(type, get, which, conv, name, default) { \\\n    type out; \\\n    if (get(pat, which, 0, &out) == FcResultMatch) { \\\n        RAII_PyObject(p, conv(out)); \\\n        if (!p || PyDict_SetItemString(ans, #name, p) != 0) return NULL; \\\n    } else { RAII_PyObject(d, default); if (!d || PyDict_SetItemString(ans, #name, d) != 0) return NULL; } \\\n}\n\n#define L(type, get, which, conv, name) { \\\n    type out; int n = 0; \\\n    RAII_PyObject(list, PyList_New(0)); \\\n    if (!list) return NULL; \\\n    while (get(pat, which, n++, &out) == FcResultMatch) { \\\n        RAII_PyObject(p, conv(out));  \\\n        if (!p || PyList_Append(list, p) != 0) return NULL; \\\n    } \\\n    if (PyDict_SetItemString(ans, #name, list) != 0) return NULL; \\\n}\n#define S(which, key) G(FcChar8*, FcPatternGetString, which, PS, key, PyUnicode_FromString(\"\"))\n#define LS(which, key) L(FcChar8*, FcPatternGetString, which, PS, key)\n#define I(which, key) G(int, FcPatternGetInteger, which, PyLong_FromLong, key, PyLong_FromUnsignedLong(0))\n#define B(which, key) G(FcBool, FcPatternGetBool, which, pybool, key, increment_and_return(Py_False))\n#define E(which, key, conv) G(int, FcPatternGetInteger, which, conv, key, PyLong_FromUnsignedLong(0))\n    S(FC_FILE, path);\n    S(FC_FAMILY, family);\n    S(FC_STYLE, style);\n    S(FC_FULLNAME, full_name);\n    S(FC_POSTSCRIPT_NAME, postscript_name);\n    LS(FC_FONT_FEATURES, fontfeatures);\n    B(FC_VARIABLE, variable);\n#ifdef FC_NAMED_INSTANCE\n    B(FC_NAMED_INSTANCE, named_instance);\n#else\n    PyDict_SetItemString(ans, \"named_instance\", Py_False);\n#endif\n    I(FC_WEIGHT, weight);\n    I(FC_WIDTH, width)\n    I(FC_SLANT, slant);\n    I(FC_HINT_STYLE, hint_style);\n    I(FC_INDEX, index);\n    I(FC_RGBA, subpixel);\n    I(FC_LCD_FILTER, lcdfilter);\n    B(FC_HINTING, hinting);\n    B(FC_SCALABLE, scalable);\n    B(FC_OUTLINE, outline);\n    B(FC_COLOR, color);\n    E(FC_SPACING, spacing, pyspacing);\n\n    Py_INCREF(ans);\n    return ans;\n#undef PS\n#undef S\n#undef I\n#undef B\n#undef E\n#undef G\n#undef L\n#undef LS\n}\n\nstatic PyObject*\nfont_set(FcFontSet *fs) {\n    PyObject *ans = PyTuple_New(fs->nfont);\n    if (ans == NULL) return NULL;\n    for (int i = 0; i < fs->nfont; i++) {\n        PyObject *d = pattern_as_dict(fs->fonts[i]);\n        if (d == NULL) { Py_CLEAR(ans); break; }\n        PyTuple_SET_ITEM(ans, i, d);\n    }\n    return ans;\n}\n\n#define AP(func, which, in, desc) if (!func(pat, which, in)) { PyErr_Format(PyExc_ValueError, \"Failed to add %s to fontconfig pattern\", desc, NULL); goto end; }\n\nstatic PyObject*\nfc_list(PyObject UNUSED *self, PyObject *args, PyObject *kw) {\n    ensure_initialized();\n    int allow_bitmapped_fonts = 0, spacing = -1, only_variable = 0;\n    PyObject *ans = NULL;\n    FcObjectSet *os = NULL;\n    FcPattern *pat = NULL;\n    FcFontSet *fs = NULL;\n    static char *kwds[] = {\"spacing\", \"allow_bitmapped_fonts\", \"only_variable\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"|ipp\", kwds, &spacing, &allow_bitmapped_fonts, &only_variable)) return NULL;\n    pat = FcPatternCreate();\n    if (pat == NULL) return PyErr_NoMemory();\n    if (!allow_bitmapped_fonts) {\n        AP(FcPatternAddBool, FC_OUTLINE, FcTrue, \"outline\");\n        AP(FcPatternAddBool, FC_SCALABLE, FcTrue, \"scalable\");\n    }\n    if (spacing > -1) AP(FcPatternAddInteger, FC_SPACING, spacing, \"spacing\");\n    if (only_variable) AP(FcPatternAddBool, FC_VARIABLE, FcTrue, \"variable\");\n    os = FcObjectSetBuild(FC_FILE, FC_POSTSCRIPT_NAME, FC_FAMILY, FC_STYLE, FC_FULLNAME, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_HINT_STYLE, FC_INDEX, FC_HINTING, FC_SCALABLE, FC_OUTLINE, FC_COLOR, FC_SPACING, FC_VARIABLE,\n#ifdef FC_NAMED_INSTANCE\n    FC_NAMED_INSTANCE,\n#endif\n    NULL);\n    if (!os) { PyErr_SetString(PyExc_ValueError, \"Failed to create fontconfig object set\"); goto end; }\n    fs = FcFontList(NULL, pat, os);\n    if (!fs) { PyErr_SetString(PyExc_ValueError, \"Failed to create fontconfig font set\"); goto end; }\n    ans = font_set(fs);\nend:\n    if (pat != NULL) FcPatternDestroy(pat);\n    if (os != NULL) FcObjectSetDestroy(os);\n    if (fs != NULL) FcFontSetDestroy(fs);\n    return ans;\n}\n\nstatic PyObject*\n_fc_match(FcPattern *pat) {\n    FcPattern *match = NULL;\n    PyObject *ans = NULL;\n    FcResult result;\n    FcConfigSubstitute(NULL, pat, FcMatchPattern);\n    FcDefaultSubstitute(pat);\n    /* printf(\"fc_match = %s\\n\", FcNameUnparse(pat)); */\n    match = FcFontMatch(NULL, pat, &result);\n    if (match == NULL) { PyErr_SetString(PyExc_KeyError, \"FcFontMatch() failed\"); goto end; }\n    ans = pattern_as_dict(match);\nend:\n    if (match) FcPatternDestroy(match);\n    return ans;\n}\n\nstatic char_type char_buf[1024];\n\nstatic void\nadd_charset(FcPattern *pat, size_t num) {\n    FcCharSet *charset = NULL;\n    if (num) {\n        charset = FcCharSetCreate();\n        if (charset == NULL) { PyErr_NoMemory(); goto end; }\n        for (size_t i = 0; i < num; i++) {\n            if (!FcCharSetAddChar(charset, char_buf[i])) {\n                PyErr_SetString(PyExc_RuntimeError, \"Failed to add character to fontconfig charset\");\n                goto end;\n            }\n        }\n        AP(FcPatternAddCharSet, FC_CHARSET, charset, \"charset\");\n    }\nend:\n    if (charset != NULL) FcCharSetDestroy(charset);\n}\n\nstatic bool\n_native_fc_match(FcPattern *pat, FontConfigFace *ans) {\n    bool ok = false;\n    FcPattern *match = NULL;\n    FcResult result;\n    FcConfigSubstitute(NULL, pat, FcMatchPattern);\n    FcDefaultSubstitute(pat);\n    /* printf(\"fc_match = %s\\n\", FcNameUnparse(pat)); */\n    match = FcFontMatch(NULL, pat, &result);\n    if (match == NULL) { PyErr_SetString(PyExc_KeyError, \"FcFontMatch() failed\"); goto end; }\n    FcChar8 *out;\n#define g(func, prop, output) if (func(match, prop, 0, &output) != FcResultMatch) { PyErr_SetString(PyExc_ValueError, \"No \" #prop \" found in fontconfig match result\"); goto end; }\n    g(FcPatternGetString, FC_FILE, out);\n    g(FcPatternGetInteger, FC_INDEX, ans->index);\n    g(FcPatternGetInteger, FC_HINT_STYLE, ans->hintstyle);\n    g(FcPatternGetBool, FC_HINTING, ans->hinting);\n#undef g\n    ans->path = strdup((char*)out);\n    if (!ans->path) { PyErr_NoMemory(); goto end; }\n    ok = true;\nend:\n    if (match != NULL) FcPatternDestroy(match);\n    return ok;\n}\n\n\nbool\ninformation_for_font_family(const char *family, bool bold, bool italic, FontConfigFace *ans) {\n    ensure_initialized();\n    memset(ans, 0, sizeof(FontConfigFace));\n    FcPattern *pat = FcPatternCreate();\n    bool ok = false;\n    if (pat == NULL) { PyErr_NoMemory(); return ok; }\n    if (family && strlen(family) > 0) AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, \"family\");\n    if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, \"weight\"); }\n    if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, \"slant\"); }\n    ok = _native_fc_match(pat, ans);\nend:\n    if (pat != NULL) FcPatternDestroy(pat);\n    return ok;\n}\n\n\nstatic PyObject*\nfc_match(PyObject UNUSED *self, PyObject *args) {\n    ensure_initialized();\n    char *family = NULL;\n    int bold = 0, italic = 0, allow_bitmapped_fonts = 0, spacing = FC_MONO;\n    double size_in_pts = 0, dpi = 0;\n    FcPattern *pat = NULL;\n    PyObject *ans = NULL;\n\n    if (!PyArg_ParseTuple(args, \"|zppipdd\", &family, &bold, &italic, &spacing, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL;\n    pat = FcPatternCreate();\n    if (pat == NULL) return PyErr_NoMemory();\n\n    if (family && strlen(family) > 0) AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, \"family\");\n    if (spacing >= FC_DUAL) {\n        // pass the family,monospace as the family parameter to fc-match,\n        // which will fallback to using monospace if the family does not match.\n        AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)\"monospace\", \"family\");\n        AP(FcPatternAddInteger, FC_SPACING, spacing, \"spacing\");\n    }\n    if (!allow_bitmapped_fonts) {\n        AP(FcPatternAddBool, FC_OUTLINE, true, \"outline\");\n        AP(FcPatternAddBool, FC_SCALABLE, true, \"scalable\");\n    }\n    if (size_in_pts > 0) { AP(FcPatternAddDouble, FC_SIZE, size_in_pts, \"size\"); }\n    if (dpi > 0) { AP(FcPatternAddDouble, FC_DPI, dpi, \"dpi\"); }\n    if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, \"weight\"); }\n    if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, \"slant\"); }\n    ans = _fc_match(pat);\n\nend:\n    if (pat != NULL) FcPatternDestroy(pat);\n    return ans;\n}\n\nstatic PyObject*\nfc_match_postscript_name(PyObject UNUSED *self, PyObject *args) {\n    ensure_initialized();\n    const char *postscript_name = NULL;\n    FcPattern *pat = NULL;\n    PyObject *ans = NULL;\n\n    if (!PyArg_ParseTuple(args, \"s\", &postscript_name)) return NULL;\n    if (!postscript_name || !postscript_name[0]) { PyErr_SetString(PyExc_KeyError, \"postscript_name must not be empty\"); return NULL; }\n\n    pat = FcPatternCreate();\n    if (pat == NULL) return PyErr_NoMemory();\n\n    AP(FcPatternAddString, FC_POSTSCRIPT_NAME, (const FcChar8*)postscript_name, \"postscript_name\");\n\n    ans = _fc_match(pat);\n\nend:\n    if (pat != NULL) FcPatternDestroy(pat);\n    return ans;\n}\n\nPyObject*\nspecialize_font_descriptor(PyObject *base_descriptor, double font_sz_in_pts, double dpi_x, double dpi_y) {\n    ensure_initialized();\n    PyObject *p = PyDict_GetItemString(base_descriptor, \"path\");\n    PyObject *idx = PyDict_GetItemString(base_descriptor, \"index\");\n    if (p == NULL) { PyErr_SetString(PyExc_ValueError, \"Base descriptor has no path\"); return NULL; }\n    if (idx == NULL) { PyErr_SetString(PyExc_ValueError, \"Base descriptor has no index\"); return NULL; }\n    unsigned long face_idx = PyLong_AsUnsignedLong(idx);\n    if (PyErr_Occurred()) return NULL;\n\n    FcPattern *pat = FcPatternCreate();\n    if (pat == NULL) return PyErr_NoMemory();\n    RAII_PyObject(features, PyList_New(0));\n    if (!features) return NULL;\n    RAII_PyObject(final_features, NULL);\n    RAII_PyObject(ans, NULL);\n    AP(FcPatternAddString, FC_FILE, (const FcChar8*)PyUnicode_AsUTF8(p), \"path\");\n    AP(FcPatternAddInteger, FC_INDEX, face_idx, \"index\");\n    AP(FcPatternAddDouble, FC_SIZE, font_sz_in_pts, \"size\");\n    AP(FcPatternAddDouble, FC_DPI, (dpi_x + dpi_y) / 2.0, \"dpi\");\n    ans = _fc_match(pat);\n    FcPatternDestroy(pat); pat = NULL;\n    if (!ans) return NULL;\n    // fontconfig returns a completely random font if the base descriptor\n    // points to a font that fontconfig hasnt indexed, for example the built-in\n    // NERD font\n    PyObject *new_path = PyDict_GetItemString(ans, \"path\");\n    if (!new_path || PyObject_RichCompareBool(p, new_path, Py_EQ) != 1) { Py_CLEAR(ans); ans = PyDict_Copy(base_descriptor); if (!ans) return NULL;  }\n\n    if (face_idx > 0) {\n        // For some reason FcFontMatch sets the index to zero, so manually restore it.\n        if (PyDict_SetItemString(ans, \"index\", idx) != 0) return NULL;\n    }\n    PyObject *named_style = PyDict_GetItemString(base_descriptor, \"named_style\");\n    if (named_style) {\n        if (PyDict_SetItemString(ans, \"named_style\", named_style) != 0) return NULL;\n    }\n    PyObject *axes = PyDict_GetItemString(base_descriptor, \"axes\");\n    if (axes) {\n        if (PyDict_SetItemString(ans, \"axes\", axes) != 0) return NULL;\n    }\n    PyObject *ff = PyDict_GetItemString(ans, \"fontfeatures\");\n    if (ff && PyList_GET_SIZE(ff)) {\n        for (Py_ssize_t i = 0; i < PyList_GET_SIZE(ff); i++) {\n            RAII_PyObject(pff, (PyObject*)parse_font_feature(PyUnicode_AsUTF8(PyList_GET_ITEM(ff, i))));\n            if (pff == NULL) {\n                PyErr_Print(); fprintf(stderr, \"\\n\");\n            } else if (PyList_Append(features, pff) != 0) return NULL;\n        }\n    }\n    PyObject *base_features = PyDict_GetItemString(base_descriptor, \"features\");\n    final_features = PyTuple_New(PyList_GET_SIZE(features) + (base_features ? PyTuple_GET_SIZE(base_features) : 0));\n    if (!final_features) return NULL;\n    for (Py_ssize_t i = 0; i < PyList_GET_SIZE(features); i++) { PyTuple_SET_ITEM(final_features, i, Py_NewRef(PyList_GET_ITEM(features, i))); }\n    if (base_features) {\n        for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(base_features); i++) { PyTuple_SET_ITEM(final_features, i + PyList_GET_SIZE(features), Py_NewRef(PyTuple_GET_ITEM(base_features, i))); }\n    }\n    if (PyDict_SetItemString(ans, \"features\", final_features) != 0) return NULL;\n    Py_INCREF(ans);\n    return ans;\nend:\n    if (pat) FcPatternDestroy(pat);\n    return NULL;\n}\n\nbool\nfallback_font(char_type ch, const char *family, bool bold, bool italic, bool prefer_color, FontConfigFace *ans) {\n    ensure_initialized();\n    memset(ans, 0, sizeof(FontConfigFace));\n    bool ok = false;\n    FcPattern *pat = FcPatternCreate();\n    if (pat == NULL) { PyErr_NoMemory(); return ok; }\n    if (family) AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, \"family\");\n    if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, \"weight\"); }\n    if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, \"slant\"); }\n    if (prefer_color) { AP(FcPatternAddBool, FC_COLOR, true, \"color\"); }\n    char_buf[0] = ch;\n    add_charset(pat, 1);\n    ok = _native_fc_match(pat, ans);\nend:\n    if (pat != NULL) FcPatternDestroy(pat);\n    return ok;\n}\n\nstatic bool face_has_codepoint(const void *face, char_type cp) { return glyph_id_for_codepoint(face, cp) > 0; }\n\nPyObject*\ncreate_fallback_face(PyObject UNUSED *base_face, const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) {\n    ensure_initialized();\n    PyObject *ans = NULL;\n    RAII_PyObject(d, NULL);\n    FcPattern *pat = FcPatternCreate();\n    if (pat == NULL) return PyErr_NoMemory();\n    bool glyph_found = false;\n    AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)(emoji_presentation ? \"emoji\" : \"monospace\"), \"family\");\n    if (!emoji_presentation && bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, \"weight\"); }\n    if (!emoji_presentation && italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, \"slant\"); }\n    if (emoji_presentation) { AP(FcPatternAddBool, FC_COLOR, true, \"color\"); }\n    size_t num = cell_as_unicode_for_fallback(lc, char_buf, arraysz(char_buf));\n    add_charset(pat, num);\n    d = _fc_match(pat);\nface_from_descriptor:\n    if (d) {\n        ssize_t idx = -1;\n        PyObject *q;\n        while ((q = iter_fallback_faces(fg, &idx))) {\n            if (face_equals_descriptor(q, d)) {\n                ans = PyLong_FromSsize_t(idx);\n                if (!glyph_found) glyph_found = has_cell_text(face_has_codepoint, q, false, lc);\n                goto end;\n            }\n        }\n        ans = face_from_descriptor(d, fg);\n        if (!glyph_found && ans) glyph_found = has_cell_text(face_has_codepoint, ans, false, lc);\n    }\nend:\n    Py_CLEAR(d);\n    if (pat != NULL) { FcPatternDestroy(pat); pat = NULL; }\n    if (!glyph_found && !PyErr_Occurred()) {\n        if (builtin_nerd_font.face && has_cell_text(face_has_codepoint, builtin_nerd_font.face, false, lc)) {\n            Py_CLEAR(ans);\n            d = builtin_nerd_font.descriptor; Py_INCREF(d); glyph_found = true; goto face_from_descriptor;\n        } else {\n            if (global_state.debug_font_fallback && ans) has_cell_text(face_has_codepoint, ans, true, lc);\n            Py_CLEAR(ans); ans = Py_None; Py_INCREF(ans);\n        }\n    }\n    return ans;\n}\n\n\nstatic PyObject*\nset_builtin_nerd_font(PyObject UNUSED *self, PyObject *pypath) {\n    if (!PyUnicode_Check(pypath)) { PyErr_SetString(PyExc_TypeError, \"path must be a string\"); return NULL; }\n    ensure_initialized();\n    const char *path = PyUnicode_AsUTF8(pypath);\n    FcPattern *pat = FcPatternCreate();\n    if (pat == NULL) return PyErr_NoMemory();\n    Py_CLEAR(builtin_nerd_font.face);\n    Py_CLEAR(builtin_nerd_font.descriptor);\n\n    builtin_nerd_font.face = face_from_path(path, 0, NULL);\n    if (builtin_nerd_font.face) {\n        // Copy whatever hinting settings fontconfig returns for the nerd font postscript name\n        AP(FcPatternAddString, FC_POSTSCRIPT_NAME, (const unsigned char*)postscript_name_for_face(builtin_nerd_font.face), \"postscript_name\");\n        RAII_PyObject(d, _fc_match(pat));\n        if (!d) goto end;\n        builtin_nerd_font.descriptor = PyDict_New();\n        if (!builtin_nerd_font.descriptor) goto end;\n#define copy(key) { PyObject *t = PyDict_GetItemString(d, #key); if (t) { if (PyDict_SetItemString(builtin_nerd_font.descriptor, #key, t) != 0)goto end; } }\n        copy(hinting); copy(hint_style);\n#undef copy\n        if (PyDict_SetItemString(builtin_nerd_font.descriptor, \"path\", pypath) != 0) goto end;\n        if (PyDict_SetItemString(builtin_nerd_font.descriptor, \"index\", PyLong_FromLong(0)) != 0) goto end;\n    }\nend:\n    if (pat) FcPatternDestroy(pat);\n    if (PyErr_Occurred()) {\n        Py_CLEAR(builtin_nerd_font.face);\n        Py_CLEAR(builtin_nerd_font.descriptor);\n        return NULL;\n    }\n    Py_INCREF(builtin_nerd_font.descriptor);\n    return builtin_nerd_font.descriptor;\n}\n\n\nstatic PyObject*\nadd_font_file(PyObject UNUSED *self, PyObject *args) {\n    ensure_initialized();\n    const char *path = NULL;\n    if (!PyArg_ParseTuple(args, \"s\", &path)) return NULL;\n    if (FcConfigAppFontAddFile(NULL, (const unsigned char*)path)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\n\n#undef AP\n\nstatic PyMethodDef module_methods[] = {\n    {\"fc_list\", (PyCFunction)(void (*) (void))(fc_list), METH_VARARGS | METH_KEYWORDS, NULL},\n    METHODB(fc_match, METH_VARARGS),\n    METHODB(fc_match_postscript_name, METH_VARARGS),\n    METHODB(add_font_file, METH_VARARGS),\n    METHODB(set_builtin_nerd_font, METH_O),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nbool\ninit_fontconfig_library(PyObject *module) {\n    register_at_exit_cleanup_func(FONTCONFIG_CLEANUP_FUNC, finalize);\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    PyModule_AddIntMacro(module, FC_WEIGHT_REGULAR);\n    PyModule_AddIntMacro(module, FC_WEIGHT_MEDIUM);\n    PyModule_AddIntMacro(module, FC_WEIGHT_SEMIBOLD);\n    PyModule_AddIntMacro(module, FC_WEIGHT_BOLD);\n    PyModule_AddIntMacro(module, FC_SLANT_ITALIC);\n    PyModule_AddIntMacro(module, FC_SLANT_ROMAN);\n    PyModule_AddIntMacro(module, FC_PROPORTIONAL);\n    PyModule_AddIntMacro(module, FC_DUAL);\n    PyModule_AddIntMacro(module, FC_MONO);\n    PyModule_AddIntMacro(module, FC_CHARCELL);\n    PyModule_AddIntMacro(module, FC_WIDTH_NORMAL);\n\n    return true;\n}\n"
  },
  {
    "path": "kitty/fonts/__init__.py",
    "content": "from collections.abc import Sequence\nfrom enum import Enum, IntEnum, auto\nfrom typing import TYPE_CHECKING, Literal, NamedTuple, TypedDict, TypeVar, Union\n\nfrom kitty.fast_data_types import ParsedFontFeature\nfrom kitty.types import run_once\nfrom kitty.typing_compat import CoreTextFont, FontConfigPattern\nfrom kitty.utils import shlex_split\n\nif TYPE_CHECKING:\n    import re\n\n\nclass ListedFont(TypedDict):\n    family: str\n    style: str\n    full_name: str\n    postscript_name: str\n    is_monospace: bool\n    is_variable: bool\n    descriptor: FontConfigPattern | CoreTextFont\n\n\nclass VariableAxis(TypedDict):\n    minimum: float\n    maximum: float\n    default: float\n    hidden: bool\n    tag: str\n    strid: str  # Can be empty string when not present\n\n\nclass NamedStyle(TypedDict):\n    axis_values: dict[str, float]\n    name: str\n    psname: str  # can be empty string when not present\n\n\nclass DesignValue1(TypedDict):\n    format: Literal[1]\n    flags: int\n    name: str\n    value: float\n\n\nclass DesignValue2(TypedDict):\n    format: Literal[2]\n    flags: int\n    name: str\n    value: float\n    minimum: float\n    maximum: float\n\n\nclass DesignValue3(TypedDict):\n    format: Literal[3]\n    flags: int\n    name: str\n    value: float\n    linked_value: float\n\n\nDesignValue = Union[DesignValue1, DesignValue2, DesignValue3]\n\n\nclass DesignAxis(TypedDict):\n    name: str\n    ordering: int\n    tag: str\n    values: list[DesignValue]\n\n\nclass AxisValue(TypedDict):\n    design_index: int\n    value: float\n\n\nclass MultiAxisStyle(TypedDict):\n    flags: int\n    name: str\n    values: tuple[AxisValue, ...]\n\n\nclass VariableData(TypedDict):\n    axes: tuple[VariableAxis, ...]\n    named_styles: tuple[NamedStyle, ...]\n    variations_postscript_name_prefix: str\n    elided_fallback_name: str\n    design_axes: tuple[DesignAxis, ...]\n    multi_axis_styles: tuple[MultiAxisStyle, ...]\n\n\nclass ModificationType(Enum):\n    underline_position = auto()\n    underline_thickness = auto()\n    strikethrough_position = auto()\n    strikethrough_thickness = auto()\n    cell_width = auto()\n    cell_height = auto()\n    baseline = auto()\n    size = auto()\n\n\nclass ModificationUnit(IntEnum):\n    pt = 0\n    percent = 1\n    pixel = 2\n\n\nclass ModificationValue(NamedTuple):\n    val: float\n    unit: ModificationUnit\n\n    def __repr__(self) -> str:\n        u = '%' if self.unit is ModificationUnit.percent else ''\n        return f'{self.val:g}{u}'\n\n\nclass FontModification(NamedTuple):\n    mod_type: ModificationType\n    mod_value: ModificationValue\n    font_name: str = ''\n\n    def __repr__(self) -> str:\n        fn = f' {self.font_name}' if self.font_name else ''\n        return f'{self.mod_type.name}{fn} {self.mod_value}'\n\n\nclass FontSpec(NamedTuple):\n    family: str | None = None\n    style: str | None = None\n    postscript_name: str | None = None\n    full_name: str | None = None\n    system: str | None = None\n    axes: tuple[tuple[str, float], ...] = ()\n    variable_name: str | None = None\n    features: tuple[ParsedFontFeature, ...] = ()\n    created_from_string: str = ''\n\n    @classmethod\n    def from_setting(cls, spec: str) -> 'FontSpec':\n        if spec == 'auto':\n            return FontSpec(system='auto', created_from_string=spec)\n        items = tuple(shlex_split(spec))\n        if '=' not in items[0]:\n            return FontSpec(system=spec, created_from_string=spec)\n        axes = {}\n        defined = {}\n        features: tuple[ParsedFontFeature, ...] = ()\n        for item in items:\n            k, sep, v = item.partition('=')\n            if sep != '=':\n                raise ValueError(f'The font specification: {spec} is not valid as {item} does not contain an =')\n            if k in ('family', 'style', 'full_name', 'postscript_name', 'variable_name'):\n                defined[k] = v\n            elif k == 'features':\n                features += tuple(ParsedFontFeature(x) for x in v.split())\n            else:\n                try:\n                    axes[k] = float(v)\n                except Exception:\n                    raise ValueError(f'The font specification: {spec} is not valid as {v} is not a number')\n        return FontSpec(axes=tuple(axes.items()), created_from_string=spec, features=features, **defined)\n\n    @property\n    def is_system(self) -> bool:\n        return bool(self.system)\n\n    @property\n    def is_auto(self) -> bool:\n        return self.system == 'auto'\n\n    @property\n    def as_setting(self) -> str:\n        if self.created_from_string:\n            return self.created_from_string\n        if self.system:\n            return self.system\n        ans = []\n        from shlex import quote\n        def a(key: str, val: str) -> None:\n            ans.append(f'{key}={quote(val)}')\n\n        if self.family is not None:\n            a('family', self.family)\n        if self.postscript_name is not None:\n            a('postscript_name', self.postscript_name)\n        if self.full_name is not None:\n            a('full_name', self.full_name)\n        if self.variable_name is not None:\n            a('variable_name', self.variable_name)\n        if self.style is not None:\n            a('style', self.style)\n        if self.features:\n            a('features', ' '.join(str(f) for f in self.features))\n        if self.axes:\n            for (key, val) in self.axes:\n                a(key, f'{val:g}')\n        return ' '.join(ans)\n\n    def __str__(self) -> str:\n        return self.as_setting\n\n    # Cannot change __repr__ as it will break config generation\n\n\nDescriptor = Union[FontConfigPattern, CoreTextFont]\nDescriptorVar = TypeVar('DescriptorVar', FontConfigPattern, CoreTextFont, Descriptor)\n\n\nclass Score(NamedTuple):\n    variable_score: int\n    style_score: float\n    monospace_score: int\n    width_score: int\n\n\nclass Scorer:\n\n    def __init__(self, bold: bool = False, italic: bool = False, monospaced: bool = True, prefer_variable: bool = False) -> None:\n        self.bold = bold\n        self.italic = italic\n        self.monospaced = monospaced\n        self.prefer_variable = prefer_variable\n\n    def sorted_candidates(self, candidates: Sequence[DescriptorVar], dump: bool = False) -> list[DescriptorVar]:\n        raise NotImplementedError()\n\n    def __repr__(self) -> str:\n        return f'{self.__class__.__name__}(bold={self.bold}, italic={self.italic}, monospaced={self.monospaced}, prefer_variable={self.prefer_variable})'\n    __str__ = __repr__\n\n\n@run_once\ndef fnname_pat() -> 're.Pattern[str]':\n    import re\n    return re.compile(r'\\s+')\n\n\ndef family_name_to_key(family: str) -> str:\n    return fnname_pat().sub(' ', family).strip().lower()\n"
  },
  {
    "path": "kitty/fonts/common.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING, Any, Literal, TypedDict, Union\n\nfrom kitty.constants import is_macos\nfrom kitty.fast_data_types import ParsedFontFeature\nfrom kitty.fonts import Descriptor, DescriptorVar, DesignAxis, FontSpec, NamedStyle, Scorer, VariableAxis, VariableData, family_name_to_key\nfrom kitty.options.types import Options\n\nif TYPE_CHECKING:\n    from kitty.fast_data_types import CTFace\n    from kitty.fast_data_types import Face as FT_Face\n\n    FontCollectionMapType = Literal['family_map', 'ps_map', 'full_map', 'variable_map']\n    FontMap = dict[FontCollectionMapType, dict[str, list[Descriptor]]]\n    Face = Union[FT_Face, CTFace]\n    def all_fonts_map(monospaced: bool) -> FontMap: ...\n    def create_scorer(bold: bool = False, italic: bool = False, monospaced: bool = True, prefer_variable: bool = False) -> Scorer: ...\n    def find_best_match(\n        family: str, bold: bool = False, italic: bool = False, monospaced: bool = True, ignore_face: Descriptor | None = None,\n        prefer_variable: bool = False,\n        ) -> Descriptor: ...\n    def find_last_resort_text_font(bold: bool = False, italic: bool = False, monospaced: bool = True) -> Descriptor: ...\n    def face_from_descriptor(descriptor: Descriptor, font_sz_in_pts: float | None = None, dpi_x: float | None = None, dpi_y: float | None = None\n                             ) -> Face: ...\n    def is_monospace(descriptor: Descriptor) -> bool: ...\n    def is_variable(descriptor: Descriptor) -> bool: ...\n    def set_named_style(name: str, font: Descriptor, vd: VariableData) -> bool: ...\n    def set_axis_values(tag_map: dict[str, float], font: Descriptor, vd: VariableData) -> bool: ...\n    def get_axis_values(font: Descriptor, vd: VariableData) -> dict[str, float]: ...\nelse:\n    FontCollectionMapType = FontMap = None\n    from kitty.fast_data_types import specialize_font_descriptor\n    if is_macos:\n        from kitty.fast_data_types import CTFace as Face\n        from kitty.fonts.core_text import (\n            all_fonts_map,\n            create_scorer,\n            find_best_match,\n            find_last_resort_text_font,\n            get_axis_values,\n            is_monospace,\n            is_variable,\n            set_axis_values,\n            set_named_style,\n        )\n    else:\n        from kitty.fast_data_types import Face\n        from kitty.fonts.fontconfig import (\n            all_fonts_map,\n            create_scorer,\n            find_best_match,\n            find_last_resort_text_font,\n            get_axis_values,\n            is_monospace,\n            is_variable,\n            set_axis_values,\n            set_named_style,\n        )\n    def face_from_descriptor(descriptor, font_sz_in_pts = None, dpi_x = None, dpi_y = None):\n        if font_sz_in_pts is not None:\n            descriptor = specialize_font_descriptor(descriptor, font_sz_in_pts, dpi_x, dpi_y)\n        return Face(descriptor=descriptor)\n\n\ncache_for_variable_data_by_path: dict[str, VariableData] = {}\nattr_map = {(False, False): 'font_family', (True, False): 'bold_font', (False, True): 'italic_font', (True, True): 'bold_italic_font'}\n\n\nclass Event:\n    is_set: bool = False\n\n\nclass FamilyAxisValues:\n    regular_weight: float | None = None\n    regular_slant: float | None = None\n    regular_ital: float | None = None\n    regular_width: float | None = None\n\n    bold_weight: float | None = None\n\n    italic_slant: float | None = None\n    italic_ital: float | None = None\n\n    def get_wght(self, bold: bool, italic: bool) -> float | None:\n        return self.bold_weight if bold else self.regular_weight\n\n    def get_ital(self, bold: bool, italic: bool) -> float | None:\n        return self.italic_ital if italic else self.regular_ital\n\n    def get_slnt(self, bold: bool, italic: bool) -> float | None:\n        return self.italic_slant if italic else self.regular_slant\n\n    def get_wdth(self, bold: bool, italic: bool) -> float | None:\n        return self.regular_width\n\n    def get(self, tag: str, bold: bool, italic: bool) -> float | None:\n        f = getattr(self, f'get_{tag}', None)\n        return None if f is None else f(bold, italic)\n\n    def set_regular_values(self, axis_values: dict[str, float]) -> None:\n        self.regular_weight = axis_values.get('wght')\n        self.regular_width = axis_values.get('wdth')\n        self.regular_ital = axis_values.get('ital')\n        self.regular_slant = axis_values.get('slnt')\n\n    def set_bold_values(self, axis_values: dict[str, float]) -> None:\n        self.bold_weight = axis_values.get('wght')\n\n    def set_italic_values(self, axis_values: dict[str, float]) -> None:\n        self.italic_ital = axis_values.get('ital')\n        self.italic_slant = axis_values.get('slnt')\n\n\ndef get_variable_data_for_descriptor(d: Descriptor) -> VariableData:\n    if not d['path']:\n        return face_from_descriptor(d).get_variable_data()\n    ans = cache_for_variable_data_by_path.get(d['path'])\n    if ans is None:\n        ans = cache_for_variable_data_by_path[d['path']] = face_from_descriptor(d).get_variable_data()\n    return ans\n\n\ndef get_variable_data_for_face(d: Face) -> VariableData:\n    path = d.path\n    if not path:\n        return d.get_variable_data()\n    ans = cache_for_variable_data_by_path.get(path)\n    if ans is None:\n        ans = cache_for_variable_data_by_path[path] = d.get_variable_data()\n    return ans\n\n\ndef find_best_match_in_candidates(\n    candidates: list[DescriptorVar], scorer: Scorer, is_medium_face: bool, ignore_face: DescriptorVar | None = None\n) -> DescriptorVar | None:\n    if candidates:\n        for x in scorer.sorted_candidates(candidates):\n            if ignore_face is None or x != ignore_face:\n                return x\n    return None\n\n\ndef pprint(*a: Any, **kw: Any) -> None:\n    from pprint import pprint\n    pprint(*a, **kw)\n\n\ndef find_medium_variant(font: DescriptorVar) -> DescriptorVar:\n    font = font.copy()\n    vd = get_variable_data_for_descriptor(font)\n    for i, ns in enumerate(vd['named_styles']):\n        if ns['name'] == 'Regular':\n            set_named_style(ns['psname'] or ns['name'], font, vd)\n            return font\n    axis_values = {}\n    for i, ax in enumerate(vd['axes']):\n        tag = ax['tag']\n        for dax in vd['design_axes']:\n            if dax['tag'] == tag:\n                for x in dax['values']:\n                    if x['format'] in (1, 2):\n                        if x['name'] == 'Regular':\n                            axis_values[tag] = x['value']\n                            break\n    if axis_values:\n        set_axis_values(axis_values, font, vd)\n    return font\n\n\ndef get_bold_design_weight(dax: DesignAxis, ax: VariableAxis, regular_weight: float) -> float:\n    ans = regular_weight\n    candidates = []\n    for x in dax['values']:\n        if x['format'] in (1, 2):\n            if x['value'] > regular_weight:\n                candidates.append(x['value'])\n    if candidates:\n        ans = min(candidates)\n    return ans\n\n\ndef get_design_value_for(dax: DesignAxis, ax: VariableAxis, bold: bool, italic: bool, family_axis_values: FamilyAxisValues) -> float:\n    family_val = family_axis_values.get(ax['tag'], bold, italic)\n    if family_val is not None and ax['minimum'] <= family_val <= ax['maximum']:\n        return family_val\n    default = ax['default']\n    if dax['tag'] == 'wght':\n        keys = ('semibold', 'bold', 'heavy', 'black') if bold else ('regular', 'medium')\n    elif dax['tag'] in ('ital', 'slnt'):\n        keys = ('italic', 'oblique', 'slanted', 'slant') if italic else ('regular', 'normal', 'medium', 'upright')\n    else:\n        return default\n    priorities = {}\n    for x in dax['values']:\n        if x['format'] in (1, 2):\n            q = x['name'].lower()\n            try:\n                idx = keys.index(q)\n            except ValueError:\n                continue\n            priorities[x['value']] = idx\n    ans = default\n    if priorities:\n        ans = sorted(priorities, key=priorities.__getitem__)[0]\n    if bold and ax['tag'] == 'wght' and family_axis_values.regular_weight is not None and ans <= family_axis_values.regular_weight:\n        ans = get_bold_design_weight(dax, ax, family_axis_values.regular_weight)\n    return ans\n\n\ndef find_bold_italic_variant(medium: Descriptor, bold: bool, italic: bool, family_axis_values: FamilyAxisValues) -> Descriptor:\n    # we first pick the best font file for bold/italic if there are more than\n    # one. For example SourceCodeVF has Italic and Upright faces with variable\n    # weights in each, so we rely on the OS font matcher to give us the best\n    # font file.\n    monospaced = is_monospace(medium)\n    unsorted = all_fonts_map(monospaced)['variable_map'][family_name_to_key(medium['family'])]\n    fonts = create_scorer(bold, italic, monospaced).sorted_candidates(unsorted)\n    vd = get_variable_data_for_descriptor(fonts[0])\n    ans = fonts[0].copy()\n    # now we need to specialise all axes in ans\n    axis_values = {}\n    dax_map = {dax['tag']: dax for dax in vd['design_axes']}\n    for ax in vd['axes']:\n        tag = ax['tag']\n        dax = dax_map.get(tag)\n        if dax is not None:\n            axis_values[tag] = get_design_value_for(dax, ax, bold, italic, family_axis_values)\n    if axis_values:\n        set_axis_values(axis_values, ans, vd)\n    return ans\n\n\ndef find_best_variable_face(spec: FontSpec, bold: bool, italic: bool, monospaced: bool, candidates: list[Descriptor]) -> Descriptor:\n    if spec.variable_name is not None:\n        q = spec.variable_name.lower()\n        for font in candidates:\n            vd = get_variable_data_for_descriptor(font)\n            if vd['variations_postscript_name_prefix'].lower() == q:\n                return font\n    if spec.style:\n        q = spec.style.lower()\n        for font in candidates:\n            vd = get_variable_data_for_descriptor(font)\n            for x in vd['named_styles']:\n                if x['psname'].lower() == q:\n                    return font\n            for x in vd['named_styles']:\n                if x['name'].lower() == q:\n                    return font\n    return create_scorer(bold, italic, monospaced).sorted_candidates(candidates)[0]\n\n\ndef get_fine_grained_font(\n    spec: FontSpec, bold: bool = False, italic: bool = False, family_axis_values: FamilyAxisValues = FamilyAxisValues(),\n    resolved_medium_font: Descriptor | None = None, monospaced: bool = True, match_is_more_specific_than_family: Event = Event()\n) -> Descriptor:\n    font_map = all_fonts_map(monospaced)\n    is_medium_face = resolved_medium_font is None\n    scorer = create_scorer(bold, italic, monospaced)\n    if spec.postscript_name:\n        q = find_best_match_in_candidates(font_map['ps_map'].get(family_name_to_key(spec.postscript_name), []), scorer, is_medium_face)\n        if q:\n            match_is_more_specific_than_family.is_set = True\n            return q\n    if spec.full_name:\n        q = find_best_match_in_candidates(font_map['full_map'].get(family_name_to_key(spec.full_name), []), scorer, is_medium_face)\n        if q:\n            match_is_more_specific_than_family.is_set = True\n            return q\n    if spec.family:\n        key = family_name_to_key(spec.family)\n        # First look for a variable font\n        candidates = font_map['variable_map'].get(key, [])\n        if candidates:\n            q = candidates[0] if len(candidates) == 1 else find_best_variable_face(spec, bold, italic, monospaced, candidates)\n            q, applied = apply_variation_to_pattern(q, spec)\n            if applied:\n                match_is_more_specific_than_family.is_set = True\n                return q\n            return find_medium_variant(q) if resolved_medium_font is None else find_bold_italic_variant(resolved_medium_font, bold, italic, family_axis_values)\n        # Now look for any font\n        candidates = font_map['family_map'].get(key, [])\n        if candidates:\n            if spec.style:\n                qs = spec.style.lower()\n                candidates = [x for x in candidates if x['style'].lower() == qs]\n            q = find_best_match_in_candidates(candidates, scorer, is_medium_face)\n            if q:\n                return q\n\n    return find_last_resort_text_font(bold, italic, monospaced)\n\n\ndef apply_variation_to_pattern(pat: Descriptor, spec: FontSpec) -> tuple[Descriptor, bool]:\n    vd = face_from_descriptor(pat).get_variable_data()\n    pat = pat.copy()\n    if spec.style:\n        if set_named_style(spec.style, pat, vd):\n            return pat, True\n    tag_map, name_map = {}, {}\n    for i, ax in enumerate(vd['axes']):\n        tag_map[ax['tag']] = i\n        if ax['strid']:\n            name_map[ax['strid'].lower()] = ax['tag']\n    axis_values = {}\n    for axspec in spec.axes:\n        qname = axspec[0]\n        if qname in tag_map:\n            axis_values[qname] = axspec[1]\n            continue\n        tag = name_map.get(qname.lower())\n        if tag:\n            axis_values[tag] = axspec[1]\n    return pat, set_axis_values(axis_values, pat, vd)\n\n\ndef get_font_from_spec(\n    spec: FontSpec, bold: bool = False, italic: bool = False, family_axis_values: FamilyAxisValues = FamilyAxisValues(),\n    resolved_medium_font: Descriptor | None = None, match_is_more_specific_than_family: Event = Event()\n) -> Descriptor:\n    if not spec.is_system:\n        ans = get_fine_grained_font(spec, bold, italic, resolved_medium_font=resolved_medium_font, family_axis_values=family_axis_values,\n                                     match_is_more_specific_than_family=match_is_more_specific_than_family)\n        if spec.features:\n            ans = ans.copy()\n            ans['features'] = spec.features\n        return ans\n    family = spec.system or ''\n    if family == 'auto':\n        if bold or italic:\n            assert resolved_medium_font is not None\n            family = resolved_medium_font['family']\n            if is_variable(resolved_medium_font) or is_actually_variable_despite_fontconfigs_lies(resolved_medium_font):\n                v = find_bold_italic_variant(resolved_medium_font, bold, italic, family_axis_values=family_axis_values)\n                if v is not None:\n                    return v\n        else:\n            family = 'monospace'\n    return find_best_match(family, bold, italic, ignore_face=resolved_medium_font)\n\n\nclass FontFiles(TypedDict):\n    medium: Descriptor\n    bold: Descriptor\n    italic: Descriptor\n    bi: Descriptor\n\n\nactually_variable_cache: dict[str, bool] = {}\n\n\ndef is_actually_variable_despite_fontconfigs_lies(d: Descriptor) -> bool:\n    if d['descriptor_type'] != 'fontconfig':\n        return False\n    path = d['path']\n    ans = actually_variable_cache.get(path)\n    if ans is not None:\n        return ans\n    m = all_fonts_map(is_monospace(d))['variable_map']\n    for x in m.get(family_name_to_key(d['family']), ()):\n        if x['path'] == path:\n            actually_variable_cache[path] = True\n            return True\n    actually_variable_cache[path] = False\n    return False\n\n\ndef get_font_files(opts: Options) -> FontFiles:\n    ans: dict[str, Descriptor] = {}\n    match_is_more_specific_than_family = Event()\n    medium_font = get_font_from_spec(opts.font_family, match_is_more_specific_than_family=match_is_more_specific_than_family)\n    medium_font_is_variable = is_variable(medium_font) or is_actually_variable_despite_fontconfigs_lies(medium_font)\n    if not match_is_more_specific_than_family.is_set and medium_font_is_variable:\n        medium_font = find_medium_variant(medium_font)\n    family_axis_values = FamilyAxisValues()\n    if medium_font_is_variable:\n        family_axis_values.set_regular_values(get_axis_values(medium_font, get_variable_data_for_descriptor(medium_font)))\n    kd = {(False, False): 'medium', (True, False): 'bold', (False, True): 'italic', (True, True): 'bi'}\n    for (bold, italic), attr in attr_map.items():\n        if bold or italic:\n            spec: FontSpec = getattr(opts, attr)\n            font = get_font_from_spec(spec, bold, italic, resolved_medium_font=medium_font, family_axis_values=family_axis_values)\n            # Set family axis values based on the values in font\n            if not (bold and italic) and (is_variable(medium_font) or is_actually_variable_despite_fontconfigs_lies(medium_font)):\n                av = get_axis_values(font, get_variable_data_for_descriptor(font))\n                (family_axis_values.set_italic_values if italic else family_axis_values.set_bold_values)(av)\n            if spec.is_auto and not font.get('features') and medium_font.get('features'):\n                # Set font features based on medium face features\n                font = font.copy()\n                font['features'] = medium_font['features']\n        else:\n            font = medium_font\n        key = kd[(bold, italic)]\n        ans[key] = font\n    return {'medium': ans['medium'], 'bold': ans['bold'], 'italic': ans['italic'], 'bi': ans['bi']}\n\n\ndef axis_values_are_equal(defaults: dict[str, float], a: dict[str, float], b: dict[str, float]) -> bool:\n    ad, bd = defaults.copy(), defaults.copy()\n    ad.update(a)\n    bd.update(b)\n    return ad == bd\n\n\ndef _get_named_style(axis_map: dict[str, float], vd: VariableData) -> NamedStyle | None:\n    defaults = {ax['tag']: ax['default'] for ax in vd['axes']}\n    for ns in vd['named_styles']:\n        if axis_values_are_equal(defaults, ns['axis_values'], axis_map):\n            return ns\n    return None\n\n\ndef get_named_style(face_or_descriptor: Face | Descriptor) -> NamedStyle | None:\n    if isinstance(face_or_descriptor, dict):\n        d: Descriptor = face_or_descriptor\n        vd = get_variable_data_for_descriptor(d)\n        if d['descriptor_type'] == 'fontconfig':\n            ns = d.get('named_style', -1)\n            if ns > -1 and ns < len(vd['named_styles']):\n                return vd['named_styles'][ns]\n            axis_map = {}\n            axes = vd['axes']\n            for i, val in enumerate(d.get('axes', ())):\n                if i < len(axes):\n                    axis_map[axes[i]['tag']] = val\n        else:\n            axis_map = d.get('axis_map', {}).copy()\n    else:\n        face: Face = face_or_descriptor\n        vd = get_variable_data_for_face(face)\n        q = face.get_variation()\n        if q is None:\n            return None\n        axis_map = q\n    return _get_named_style(axis_map, vd)\n\n\ndef get_axis_map(face_or_descriptor: Face | Descriptor) -> dict[str, float]:\n    base_axis_map = {}\n    axis_map: dict[str, float] = {}\n    if isinstance(face_or_descriptor, dict):\n        d: Descriptor = face_or_descriptor\n        vd = get_variable_data_for_descriptor(d)\n        if d['descriptor_type'] == 'fontconfig':\n            ns = d.get('named_style', -1)\n            if ns > -1 and ns < len(vd['named_styles']):\n                base_axis_map = vd['named_styles'][ns]['axis_values'].copy()\n            axis_map = {}\n            axes = vd['axes']\n            for i, val in enumerate(d.get('axes', ())):\n                if i < len(axes):\n                    axis_map[axes[i]['tag']] = val\n\n        else:\n            axis_map = d.get('axis_map', {}).copy()\n    else:\n        face: Face = face_or_descriptor\n        q = face.get_variation()\n        if q is not None:\n            axis_map = q\n    base_axis_map.update(axis_map)\n    return base_axis_map\n\n\ndef spec_for_face(family: str, face: Face) -> FontSpec:\n    v = face.get_variation()\n    features = tuple(map(ParsedFontFeature, face.applied_features().values()))\n    if v is None:\n        return FontSpec(family=family, postscript_name=face.postscript_name(), features=features)\n    vd = face.get_variable_data()\n    varname = vd['variations_postscript_name_prefix']\n    ns = get_named_style(face)\n    if ns is None:\n        axes = []\n        for key, val in get_axis_map(face).items():\n            axes.append((key, val))\n        return FontSpec(family=family, variable_name=varname, axes=tuple(axes), features=features)\n    return FontSpec(family=family, variable_name=varname, style=ns['psname'] or ns['name'], features=features)\n\n\ndef develop(family: str = '') -> None:\n    import sys\n    family = family or sys.argv[-1]\n    from kitty.options.utils import parse_font_spec\n    opts = Options()\n    opts.font_family = parse_font_spec(family)\n    ff = get_font_files(opts)\n    def s(name: str, d: Descriptor) -> None:\n        f = face_from_descriptor(d)\n        print(name, str(f))\n        features = f.get_features()\n        print('  Features :', features)\n\n    s('Medium     :', ff['medium'])\n    print()\n    s('Bold       :', ff['bold'])\n    print()\n    s('Italic     :', ff['italic'])\n    print()\n    s('Bold-Italic:', ff['bi'])\n\n\ndef list_fonts(monospaced: bool = True) -> dict[str, list[dict[str, str]]]:\n    ans: dict[str, list[dict[str, str]]] = {}\n    for key, descriptors in all_fonts_map(monospaced)['family_map'].items():\n        entries = ans.setdefault(key, [])\n        for d in descriptors:\n            entries.append({'family': d['family'], 'psname': d['postscript_name'], 'path': d['path'], 'style': d['style']})\n    return ans\n\n\nif __name__ == '__main__':\n    develop()\n"
  },
  {
    "path": "kitty/fonts/core_text.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport itertools\nimport operator\nfrom collections import defaultdict\nfrom collections.abc import Generator, Iterable, Sequence\nfrom functools import lru_cache\nfrom typing import NamedTuple\n\nfrom kitty.fast_data_types import CTFace, coretext_all_fonts\nfrom kitty.typing_compat import CoreTextFont\nfrom kitty.utils import log_error\n\nfrom . import Descriptor, DescriptorVar, ListedFont, Score, Scorer, VariableData, family_name_to_key\n\nattr_map = {(False, False): 'font_family',\n            (True, False): 'bold_font',\n            (False, True): 'italic_font',\n            (True, True): 'bold_italic_font'}\n\n\nFontMap = dict[str, dict[str, list[CoreTextFont]]]\n\n\ndef create_font_map(all_fonts: Iterable[CoreTextFont]) -> FontMap:\n    ans: FontMap = {'family_map': {}, 'ps_map': {}, 'full_map': {}, 'variable_map': {}}\n    vmap: dict[str, list[CoreTextFont]] = defaultdict(list)\n    for x in all_fonts:\n        f = family_name_to_key(x['family'])\n        s = family_name_to_key(x['style'])\n        ps = family_name_to_key(x['postscript_name'])\n        ans['family_map'].setdefault(f, []).append(x)\n        ans['ps_map'].setdefault(ps, []).append(x)\n        ans['full_map'].setdefault(f'{f} {s}', []).append(x)\n        if x['variation'] is not None:\n            vmap[f].append(x)\n    # CoreText makes a separate descriptor for each named style in each\n    # variable font file. Keep only the default style descriptor, which has an\n    # empty variation dictionary. If no default exists, pick the one with the\n    # smallest variation dictionary size.\n    keyfunc = operator.itemgetter('path')\n    for k, v in vmap.items():\n        v.sort(key=keyfunc)\n        uniq_per_path = []\n        for _, g in itertools.groupby(v, keyfunc):\n            uniq_per_path.append(sorted(g, key=lambda x: len(x['variation'] or ()))[0])\n        ans['variable_map'][k] = uniq_per_path\n    return ans\n\n\n@lru_cache(maxsize=2)\ndef all_fonts_map(monospaced: bool = True) -> FontMap:\n    return create_font_map(coretext_all_fonts(monospaced))\n\n\ndef is_monospace(descriptor: CoreTextFont) -> bool:\n    return descriptor['monospace']\n\n\ndef is_variable(descriptor: CoreTextFont) -> bool:\n    return descriptor['variation'] is not None\n\n\ndef list_fonts() -> Generator[ListedFont, None, None]:\n    for fd in coretext_all_fonts(False):\n        f = fd['family']\n        if f:\n            fn = fd['display_name']\n            if not fn:\n                fn = f'{f} {fd[\"style\"]}'.strip()\n            yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': fd['monospace'],\n                   'is_variable': is_variable(fd), 'descriptor': fd, 'style': fd['style']}\n\n\nclass WeightRange(NamedTuple):\n    minimum: float = 99999\n    maximum: float = -99999\n    medium: float = -99999\n    bold: float = -99999\n\n    @property\n    def is_valid(self) -> bool:\n        return self.minimum != wr.minimum and self.maximum != wr.maximum and self.medium != wr.medium and self.bold != wr.bold\n\nwr = WeightRange()\n\n\n@lru_cache\ndef weight_range_for_family(family: str) -> WeightRange:\n    faces = all_fonts_map(True)['family_map'].get(family_name_to_key(family), ())\n    mini, maxi, medium, bold = wr.minimum, wr.maximum, wr.medium, wr.bold\n    for face in faces:\n        w = face['weight']\n        mini, maxi = min(w, mini), max(w, maxi)\n        s = face['style'].lower()\n        if not s:\n            continue\n        s = s.split()[0]\n        if s == 'semibold':\n            bold = w\n        elif s == 'bold' and bold == wr.bold:\n            bold = w\n        elif s == 'regular':\n            medium = w\n        elif s == 'medium' and medium == wr.medium:\n            medium = w\n    return WeightRange(mini, maxi, medium, bold)\n\n\nclass CTScorer(Scorer):\n    weight_range: WeightRange | None = None\n\n    def score(self, candidate: Descriptor) -> Score:\n        assert candidate['descriptor_type'] == 'core_text'\n        variable_score = 0 if self.prefer_variable and candidate['variation'] is not None else 1\n        bold_score = candidate['weight']  # -1 to 1 with 0 being normal\n        if self.weight_range is None:\n            if bold_score < 0:  # thinner than normal, reject\n                bold_score = 2.0\n            else:\n                if self.bold:\n                    # prefer semibold=0.3 to full bold = 0.4\n                    bold_score = abs(bold_score - 0.3)\n        else:\n            anchor = self.weight_range.bold if self.bold else self.weight_range.medium\n            bold_score = abs(bold_score - anchor)\n        italic_score = candidate['slant'] # -1 to 1 with 0 being upright < 0 being backward slant, abs(slant) == 1 implies 30 deg rotation\n        if self.italic:\n            if italic_score < 0:\n                italic_score = 2.0\n            else:\n                italic_score = abs(1 - italic_score)\n        monospace_match = 0 if candidate['monospace'] else 1\n        is_regular_width = not candidate['expanded'] and not candidate['condensed']\n        return Score(variable_score, bold_score + italic_score, monospace_match, 0 if is_regular_width else 1)\n\n    def sorted_candidates(self, candidates: Sequence[DescriptorVar], dump: bool = False) -> list[DescriptorVar]:\n        self.weight_range = None\n        families = {x['family'] for x in candidates}\n        if len(families) == 1:\n            wr = weight_range_for_family(next(iter(families)))\n            if wr.is_valid and wr.medium < 0:  # Operator Mono is an example of this craziness\n                self.weight_range = wr\n        candidates = sorted(candidates, key=self.score)\n        if dump:\n            print(self)\n            if self.weight_range:\n                print(self.weight_range)\n            for x in candidates:\n                assert x['descriptor_type'] == 'core_text'\n                print(CTFace(descriptor=x).postscript_name(),\n                      f'bold={x[\"bold\"]}', f'italic={x[\"italic\"]}', f'weight={x[\"weight\"]:.2f}', f'slant={x[\"slant\"]:.2f}')\n                print(' ', self.score(x))\n            print()\n        return candidates\n\n\ndef create_scorer(bold: bool = False, italic: bool = False, monospaced: bool = True, prefer_variable: bool = False) -> Scorer:\n    return CTScorer(bold, italic, monospaced, prefer_variable)\n\n\ndef find_last_resort_text_font(bold: bool = False, italic: bool = False, monospaced: bool = True) -> CoreTextFont:\n    font_map = all_fonts_map(monospaced)\n    candidates = font_map['family_map']['menlo']\n    return create_scorer(bold, italic, monospaced).sorted_candidates(candidates)[0]\n\n\ndef find_best_match(\n    family: str, bold: bool = False, italic: bool = False, monospaced: bool = True, ignore_face: CoreTextFont | None = None,\n    prefer_variable: bool = False\n) -> CoreTextFont:\n    q = family_name_to_key(family)\n    font_map = all_fonts_map(monospaced)\n    scorer = create_scorer(bold, italic, monospaced, prefer_variable=prefer_variable)\n\n    # First look for an exact match\n    for selector in ('ps_map', 'full_map'):\n        candidates = font_map[selector].get(q)\n        if candidates:\n            candidates = scorer.sorted_candidates(candidates)\n            possible = candidates[0]\n            if possible != ignore_face:\n                return possible\n\n    # See if we have a variable font\n    if not bold and not italic and font_map['variable_map'].get(q):\n        candidates = font_map['variable_map'][q]\n        candidates = scorer.sorted_candidates(candidates)\n        possible = candidates[0]\n        if possible != ignore_face:\n            from .common import find_medium_variant\n            return find_medium_variant(possible)\n\n    # Let CoreText choose the font if the family exists, otherwise\n    # fallback to Menlo\n    if q not in font_map['family_map']:\n        if family.lower() not in ('monospace', 'symbols nerd font mono'):\n            log_error(f'The font {family} was not found, falling back to Menlo')\n        q = 'menlo'\n    candidates = scorer.sorted_candidates(font_map['family_map'][q])\n    return candidates[0]\n\n\ndef font_for_family(family: str) -> tuple[CoreTextFont, bool, bool]:\n    ans = find_best_match(family, monospaced=False)\n    return ans, ans['bold'], ans['italic']\n\n\ndef descriptor(f: ListedFont) -> CoreTextFont:\n    d = f['descriptor']\n    assert d['descriptor_type'] == 'core_text'\n    return d\n\n\ndef prune_family_group(g: list[ListedFont]) -> list[ListedFont]:\n    # CoreText returns a separate font for every style in the variable font, so\n    # merge them.\n    variable_paths = {descriptor(f)['path']: False for f in g if f['is_variable']}\n    if not variable_paths:\n        return g\n    def is_ok(d: CoreTextFont) -> bool:\n        if d['path'] not in variable_paths:\n            return True\n        if not variable_paths[d['path']]:\n            variable_paths[d['path']] = True\n            return True\n        return False\n    return [x for x in g if is_ok(descriptor(x))]\n\n\ndef set_axis_values(tag_map: dict[str, float], font: CoreTextFont, vd: VariableData) -> bool:\n    known_axes = {ax['tag'] for ax in vd['axes']}\n    previous = font.get('axis_map', {})\n    new = previous.copy()\n    for tag in known_axes:\n        val = tag_map.get(tag)\n        if val is not None:\n            new[tag] = val\n    font['axis_map'] = new\n    return new != previous\n\n\ndef set_named_style(name: str, font: CoreTextFont, vd: VariableData) -> bool:\n    q = name.lower()\n    for i, ns in enumerate(vd['named_styles']):\n        if ns['psname'].lower() == q:\n            return set_axis_values(ns['axis_values'], font, vd)\n    for i, ns in enumerate(vd['named_styles']):\n        if ns['name'].lower() == q:\n            return set_axis_values(ns['axis_values'], font, vd)\n    if vd['elided_fallback_name']:\n        for i, ns in enumerate(vd['named_styles']):\n            eq = ' '.join(ns['name'].replace(vd['elided_fallback_name'], '').strip().split()).lower()\n            if q == eq:\n                return set_axis_values(ns['axis_values'], font, vd)\n    return False\n\n\ndef get_axis_values(font: CoreTextFont, vd: VariableData) -> dict[str, float]:\n    return font.get('axis_map', {})\n"
  },
  {
    "path": "kitty/fonts/features.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom enum import IntEnum\nfrom typing import NamedTuple\n\n\nclass Type(IntEnum):\n    boolean = 1\n    index = 2\n    hidden = 3\n\n\nclass FeatureDefinition(NamedTuple):\n    name: str\n    type: Type\n\n# From: https://learn.microsoft.com/en-ca/typography/opentype/spec/featurelist\nknown_features: dict[str, FeatureDefinition] = {  # {{{\n    'aalt':\tFeatureDefinition('Access All Alternates', Type.index),\n    'abvf':\tFeatureDefinition('Above-base Forms', Type.hidden),\n    'abvm':\tFeatureDefinition('Above-base Mark Positioning', Type.hidden),\n    'abvs':\tFeatureDefinition('Above-base Substitutions', Type.hidden),\n    'afrc':\tFeatureDefinition('Alternative Fractions', Type.boolean),\n    'akhn':\tFeatureDefinition('Akhand', Type.hidden),\n    'blwf':\tFeatureDefinition('Below-base Forms', Type.hidden),\n    'blwm':\tFeatureDefinition('Below-base Mark Positioning', Type.hidden),\n    'blws':\tFeatureDefinition('Below-base Substitutions', Type.hidden),\n    'calt':\tFeatureDefinition('Contextual Alternates', Type.boolean),\n    'case':\tFeatureDefinition('Case-Sensitive Forms', Type.hidden),\n    'ccmp':\tFeatureDefinition('Glyph Composition / Decomposition', Type.hidden),\n    'cfar':\tFeatureDefinition('Conjunct Form After Ro', Type.hidden),\n    'chws':\tFeatureDefinition('Contextual Half-width Spacing', Type.boolean),\n    'cjct':\tFeatureDefinition('Conjunct Forms', Type.hidden),\n    'clig':\tFeatureDefinition('Contextual Ligatures', Type.boolean),\n    'cpct':\tFeatureDefinition('Centered CJK Punctuation', Type.boolean),\n    'cpsp':\tFeatureDefinition('Capital Spacing', Type.boolean),\n    'cswh':\tFeatureDefinition('Contextual Swash', Type.boolean),\n    'curs':\tFeatureDefinition('Cursive Positioning', Type.hidden),\n    'c2pc':\tFeatureDefinition('Petite Capitals From Capitals', Type.boolean),\n    'c2sc':\tFeatureDefinition('Small Capitals From Capitals', Type.boolean),\n    'dist':\tFeatureDefinition('Distances', Type.hidden),\n    'dlig':\tFeatureDefinition('Discretionary Ligatures', Type.boolean),\n    'dnom':\tFeatureDefinition('Denominators', Type.hidden),\n    'dtls':\tFeatureDefinition('Dotless Forms', Type.hidden),\n    'expt':\tFeatureDefinition('Expert Forms', Type.boolean),\n    'falt':\tFeatureDefinition('Final Glyph on Line Alternates', Type.boolean),\n    'fin2':\tFeatureDefinition('Terminal Forms #2', Type.hidden),\n    'fin3':\tFeatureDefinition('Terminal Forms #3', Type.hidden),\n    'fina':\tFeatureDefinition('Terminal Forms', Type.hidden),\n    'flac':\tFeatureDefinition('Flattened accent forms', Type.hidden),\n    'frac':\tFeatureDefinition('Fractions', Type.boolean),\n    'fwid':\tFeatureDefinition('Full Widths', Type.boolean),\n    'half':\tFeatureDefinition('Half Forms', Type.hidden),\n    'haln':\tFeatureDefinition('Halant Forms', Type.hidden),\n    'halt':\tFeatureDefinition('Alternate Half Widths', Type.boolean),\n    'hist':\tFeatureDefinition('Historical Forms', Type.boolean),\n    'hkna':\tFeatureDefinition('Horizontal Kana Alternates', Type.boolean),\n    'hlig':\tFeatureDefinition('Historical Ligatures', Type.boolean),\n    'hngl':\tFeatureDefinition('Hangul', Type.boolean),\n    'hojo':\tFeatureDefinition('Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms)', Type.boolean),\n    'hwid':\tFeatureDefinition('Half Widths', Type.boolean),\n    'init':\tFeatureDefinition('Initial Forms', Type.hidden),\n    'isol':\tFeatureDefinition('Isolated Forms', Type.hidden),\n    'ital':\tFeatureDefinition('Italics', Type.boolean),\n    'jalt':\tFeatureDefinition('Justification Alternates', Type.boolean),\n    'jp78':\tFeatureDefinition('JIS78 Forms', Type.boolean),\n    'jp83':\tFeatureDefinition('JIS83 Forms', Type.boolean),\n    'jp90':\tFeatureDefinition('JIS90 Forms', Type.boolean),\n    'jp04':\tFeatureDefinition('JIS2004 Forms', Type.boolean),\n    'kern':\tFeatureDefinition('Kerning', Type.boolean),\n    'lfbd':\tFeatureDefinition('Left Bounds', Type.boolean),\n    'liga':\tFeatureDefinition('Standard Ligatures', Type.boolean),\n    'ljmo':\tFeatureDefinition('Leading Jamo Forms', Type.hidden),\n    'lnum':\tFeatureDefinition('Lining Figures', Type.boolean),\n    'locl':\tFeatureDefinition('Localized Forms', Type.hidden),\n    'ltra':\tFeatureDefinition('Left-to-right alternates', Type.hidden),\n    'ltrm':\tFeatureDefinition('Left-to-right mirrored forms', Type.hidden),\n    'mark':\tFeatureDefinition('Mark Positioning', Type.hidden),\n    'med2':\tFeatureDefinition('Medial Forms #2', Type.hidden),\n    'medi':\tFeatureDefinition('Medial Forms', Type.hidden),\n    'mgrk':\tFeatureDefinition('Mathematical Greek', Type.boolean),\n    'mkmk':\tFeatureDefinition('Mark to Mark Positioning', Type.hidden),\n    'mset':\tFeatureDefinition('Mark Positioning via Substitution', Type.hidden),\n    'nalt':\tFeatureDefinition('Alternate Annotation Forms', Type.index),\n    'nlck':\tFeatureDefinition('NLC Kanji Forms', Type.boolean),\n    'nukt':\tFeatureDefinition('Nukta Forms', Type.hidden),\n    'numr':\tFeatureDefinition('Numerators', Type.hidden),\n    'onum':\tFeatureDefinition('Oldstyle Figures', Type.boolean),\n    'opbd':\tFeatureDefinition('Optical Bounds', Type.boolean),\n    'ordn':\tFeatureDefinition('Ordinals', Type.boolean),\n    'ornm':\tFeatureDefinition('Ornaments', Type.index),\n    'palt':\tFeatureDefinition('Proportional Alternate Widths', Type.boolean),\n    'pcap':\tFeatureDefinition('Petite Capitals', Type.boolean),\n    'pkna':\tFeatureDefinition('Proportional Kana', Type.boolean),\n    'pnum':\tFeatureDefinition('Proportional Figures', Type.boolean),\n    'pref':\tFeatureDefinition('Pre-Base Forms', Type.hidden),\n    'pres':\tFeatureDefinition('Pre-base Substitutions', Type.hidden),\n    'pstf':\tFeatureDefinition('Post-base Forms', Type.hidden),\n    'psts':\tFeatureDefinition('Post-base Substitutions', Type.hidden),\n    'pwid':\tFeatureDefinition('Proportional Widths', Type.boolean),\n    'qwid':\tFeatureDefinition('Quarter Widths', Type.boolean),\n    'rand':\tFeatureDefinition('Randomize', Type.boolean),\n    'rclt':\tFeatureDefinition('Required Contextual Alternates', Type.hidden),\n    'rkrf':\tFeatureDefinition('Rakar Forms', Type.hidden),\n    'rlig':\tFeatureDefinition('Required Ligatures', Type.hidden),\n    'rphf':\tFeatureDefinition('Reph Forms', Type.hidden),\n    'rtbd':\tFeatureDefinition('Right Bounds', Type.boolean),\n    'rtla':\tFeatureDefinition('Right-to-left alternates', Type.hidden),\n    'rtlm':\tFeatureDefinition('Right-to-left mirrored forms', Type.hidden),\n    'ruby':\tFeatureDefinition('Ruby Notation Forms', Type.boolean),\n    'rvrn':\tFeatureDefinition('Required Variation Alternates', Type.hidden),\n    'salt':\tFeatureDefinition('Stylistic Alternates', Type.index),\n    'sinf':\tFeatureDefinition('Scientific Inferiors', Type.boolean),\n    'size':\tFeatureDefinition('Optical size', Type.hidden),\n    'smcp':\tFeatureDefinition('Small Capitals', Type.boolean),\n    'smpl':\tFeatureDefinition('Simplified Forms', Type.boolean),\n    'ssty':\tFeatureDefinition('Math script style alternates', Type.hidden),\n    'stch':\tFeatureDefinition('Stretching Glyph Decomposition', Type.hidden),\n    'subs':\tFeatureDefinition('Subscript', Type.boolean),\n    'sups':\tFeatureDefinition('Superscript', Type.boolean),\n    'swsh':\tFeatureDefinition('Swash', Type.index),\n    'titl':\tFeatureDefinition('Titling', Type.boolean),\n    'tjmo':\tFeatureDefinition('Trailing Jamo Forms', Type.hidden),\n    'tnam':\tFeatureDefinition('Traditional Name Forms', Type.boolean),\n    'tnum':\tFeatureDefinition('Tabular Figures', Type.boolean),\n    'trad':\tFeatureDefinition('Traditional Forms', Type.index),\n    'twid':\tFeatureDefinition('Third Widths', Type.boolean),\n    'unic':\tFeatureDefinition('Unicase', Type.boolean),\n    'valt':\tFeatureDefinition('Alternate Vertical Metrics', Type.boolean),\n    'vatu':\tFeatureDefinition('Vattu Variants', Type.hidden),\n    'vchw':\tFeatureDefinition('Vertical Contextual Half-width Spacing', Type.hidden),\n    'vert':\tFeatureDefinition('Vertical Writing', Type.boolean),\n    'vhal':\tFeatureDefinition('Alternate Vertical Half Metrics', Type.boolean),\n    'vjmo':\tFeatureDefinition('Vowel Jamo Forms', Type.hidden),\n    'vkna':\tFeatureDefinition('Vertical Kana Alternates', Type.boolean),\n    'vkrn':\tFeatureDefinition('Vertical Kerning', Type.boolean),\n    'vpal':\tFeatureDefinition('Proportional Alternate Vertical Metrics', Type.boolean),\n    'vrt2':\tFeatureDefinition('Vertical Alternates and Rotation', Type.boolean),\n    'vrtr':\tFeatureDefinition('Vertical Alternates for Rotation', Type.boolean),\n    'zero':\tFeatureDefinition('Slashed Zero', Type.boolean),\n}\nfor i in range(1, 100):\n    known_features[f'cv{i:02d}'] = FeatureDefinition(f'Character Variant {i}', Type.index)\nfor i in range(1, 20):\n    known_features[f'ss{i:02d}'] = FeatureDefinition(f'Stylistic Set {i}', Type.boolean)\n# }}}\n\n\n"
  },
  {
    "path": "kitty/fonts/fontconfig.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nfrom collections.abc import Generator, Sequence\nfrom functools import lru_cache\nfrom typing import Literal, NamedTuple, Optional, cast\n\nfrom kitty.fast_data_types import (\n    FC_DUAL,\n    FC_MONO,\n    FC_SLANT_ITALIC,\n    FC_SLANT_ROMAN,\n    FC_WEIGHT_REGULAR,\n    FC_WIDTH_NORMAL,\n    Face,\n    fc_list,\n)\nfrom kitty.fast_data_types import (\n    FC_WEIGHT_SEMIBOLD as FC_WEIGHT_BOLD,\n)\nfrom kitty.fast_data_types import fc_match as fc_match_impl\nfrom kitty.typing_compat import FontConfigPattern\n\nfrom . import Descriptor, DescriptorVar, ListedFont, Score, Scorer, VariableData, family_name_to_key\n\nFontCollectionMapType = Literal['family_map', 'ps_map', 'full_map', 'variable_map']\nFontMap = dict[FontCollectionMapType, dict[str, list[FontConfigPattern]]]\n\n\ndef create_font_map(all_fonts: tuple[FontConfigPattern, ...]) -> FontMap:\n    ans: FontMap = {'family_map': {}, 'ps_map': {}, 'full_map': {}, 'variable_map': {}}\n    for x in all_fonts:\n        if not x.get('path'):\n            continue\n        f = family_name_to_key(x['family'])\n        full = family_name_to_key(x['full_name'])\n        ps = family_name_to_key(x['postscript_name'])\n        ans['family_map'].setdefault(f, []).append(x)\n        ans['ps_map'].setdefault(ps, []).append(x)\n        ans['full_map'].setdefault(full, []).append(x)\n        if x['variable']:\n            ans['variable_map'].setdefault(f, []).append(x)\n    return ans\n\n\n@lru_cache(maxsize=2)\ndef all_fonts_map(monospaced: bool = True) -> FontMap:\n    if monospaced:\n        ans = fc_list(spacing=FC_DUAL) + fc_list(spacing=FC_MONO)\n    else:\n        # allow non-monospaced and bitmapped fonts as these are used for\n        # symbol_map\n        ans = fc_list(allow_bitmapped_fonts=True)\n    return create_font_map(ans)\n\n\ndef is_monospace(descriptor: FontConfigPattern) -> bool:\n    return descriptor['spacing'] in ('MONO', 'DUAL')\n\n\ndef is_variable(descriptor: FontConfigPattern) -> bool:\n    return descriptor['variable']\n\n\ndef list_fonts(only_variable: bool = False) -> Generator[ListedFont, None, None]:\n    for fd in fc_list(only_variable=only_variable):\n        f = fd.get('family')\n        if f and isinstance(f, str):\n            fn_ = fd.get('full_name')\n            if fn_:\n                fn = str(fn_)\n            else:\n                fn = f'{f} {fd.get(\"style\", \"\")}'.strip()\n            yield {\n                'family': f, 'full_name': fn, 'postscript_name': str(fd.get('postscript_name', '')),\n                'is_monospace': is_monospace(fd), 'descriptor': fd, 'is_variable': is_variable(fd),\n                'style': fd['style'],\n            }\n\n\n@lru_cache\ndef fc_match(family: str, bold: bool, italic: bool, spacing: int = FC_MONO) -> FontConfigPattern:\n    return fc_match_impl(family, bold, italic, spacing)\n\n\nclass WeightRange(NamedTuple):\n    minimum: int = sys.maxsize\n    maximum: int = -1\n    medium: int = -1\n    bold: int = -1\n\n    @property\n    def is_valid(self) -> bool:\n        return self.minimum != wr.minimum and self.maximum != wr.maximum and self.medium != wr.medium and self.bold != wr.bold\n\nwr = WeightRange()\n\n\n@lru_cache\ndef weight_range_for_family(family: str) -> WeightRange:\n    faces = all_fonts_map(True)['family_map'].get(family_name_to_key(family), ())\n    mini, maxi, medium, bold = wr.minimum, wr.maximum, wr.medium, wr.bold\n    seen_weights = set()\n    for face in faces:\n        w = face['weight']\n        mini, maxi = min(w, mini), max(w, maxi)\n        seen_weights.add(w)\n        s = face['style'].lower()\n        if not s:\n            continue\n        s = s.split()[0]\n        if s == 'semibold':\n            bold = w\n        elif s == 'bold' and bold == wr.bold:\n            bold = w\n        elif s == 'regular':\n            medium = w\n        elif s == 'medium' and medium == wr.medium:\n            medium = w\n    if len(seen_weights) < 2:\n        return wr\n    return WeightRange(mini, maxi, medium, bold)\n\n\n\nclass FCScorer(Scorer):\n\n    weight_range: WeightRange | None = None\n\n    def score(self, candidate: Descriptor) -> Score:\n        assert candidate['descriptor_type'] == 'fontconfig'\n        variable_score = 0 if self.prefer_variable and candidate['variable'] else 1\n        if self.weight_range is None:\n            bold_score = abs((FC_WEIGHT_BOLD if self.bold else FC_WEIGHT_REGULAR) - candidate['weight'])\n        else:\n            bold_score = abs((self.weight_range.bold if self.bold else self.weight_range.medium) - candidate['weight'])\n        italic_score = abs((FC_SLANT_ITALIC if self.italic else FC_SLANT_ROMAN) - candidate['slant'])\n        monospace_match = 0\n        if self.monospaced:\n            monospace_match = 0 if candidate.get('spacing') == 'MONO' else 1\n        width_score = abs(candidate['width'] - FC_WIDTH_NORMAL)\n        return Score(variable_score, bold_score / 1000 + italic_score / 110, monospace_match, width_score)\n\n    def sorted_candidates(self, candidates: Sequence[DescriptorVar], dump: bool = False) -> list[DescriptorVar]:\n        self.weight_range = None\n        families = {x['family'] for x in candidates}\n        if len(families) == 1:\n            wr = weight_range_for_family(next(iter(families)))\n            if wr.is_valid and wr.medium < 100:  # Operator Mono and Cascadia Code are examples\n                self.weight_range = wr\n        candidates = sorted(candidates, key=self.score)\n        if dump:\n            print(self)\n            if self.weight_range:\n                print(self.weight_range)\n            for x in candidates:\n                assert x['descriptor_type'] == 'fontconfig'\n                print(Face(descriptor=x).postscript_name(), f'weight={x[\"weight\"]}', f'slant={x[\"slant\"]}')\n                print(' ', self.score(x))\n            print()\n        return candidates\n\n\ndef create_scorer(bold: bool = False, italic: bool = False, monospaced: bool = True, prefer_variable: bool = False) -> Scorer:\n    return FCScorer(bold, italic, monospaced, prefer_variable)\n\n\ndef find_last_resort_text_font(bold: bool = False, italic: bool = False, monospaced: bool = True) -> FontConfigPattern:\n    # Use fc-match with a generic family\n    family = 'monospace' if monospaced else 'sans-serif'\n    return fc_match(family, bold, italic)\n\n\ndef find_best_match(\n        family: str, bold: bool = False, italic: bool = False, monospaced: bool = True,\n        ignore_face: FontConfigPattern | None = None, prefer_variable: bool = False,\n) -> FontConfigPattern:\n    from .common import find_best_match_in_candidates\n    q = family_name_to_key(family)\n    font_map = all_fonts_map(monospaced)\n    scorer = create_scorer(bold, italic, monospaced, prefer_variable=prefer_variable)\n    is_medium_face = not bold and not italic\n    # First look for an exact match\n    groups: tuple[FontCollectionMapType, ...] = ('ps_map', 'full_map', 'family_map')\n    for which in groups:\n        m = font_map[which]\n        cq = m.get(q, [])\n        if cq:\n            if which == 'full_map' and cq[0]['family'] == cq[0]['full_name']:\n                continue  # IBM Plex Mono has fullname of regular face == family_name under fontconfig\n            exact_match = find_best_match_in_candidates(cq, scorer, is_medium_face, ignore_face=ignore_face)\n            if exact_match:\n                return exact_match\n\n    # Use fc-match to see if we can find a monospaced font that matches family\n    # When aliases are defined, spacing can cause the incorrect font to be\n    # returned, so check with and without spacing and use the one that matches.\n    mono_possibility = fc_match(family, False, False, FC_MONO)\n    dual_possibility = fc_match(family, False, False, FC_DUAL)\n    any_possibility = fc_match(family, False, False, 0)\n    tries = (dual_possibility, mono_possibility) if any_possibility == dual_possibility else (mono_possibility, dual_possibility)\n    for possibility in tries:\n        for key, map_key in (('postscript_name', 'ps_map'), ('full_name', 'full_map'), ('family', 'family_map')):\n            map_key = cast(FontCollectionMapType, map_key)\n            val: str | None = cast(Optional[str], possibility.get(key))\n            if val:\n                candidates = font_map[map_key].get(family_name_to_key(val))\n                if candidates:\n                    if len(candidates) == 1:\n                        # happens if the family name is an alias, so we search with\n                        # the actual family name to see if we can find all the\n                        # fonts in the family.\n                        family_name_candidates = font_map['family_map'].get(family_name_to_key(candidates[0]['family']))\n                        if family_name_candidates and len(family_name_candidates) > 1:\n                            candidates = family_name_candidates\n                    return scorer.sorted_candidates(candidates)[0]\n    return find_last_resort_text_font(bold, italic, monospaced)\n\n\ndef font_for_family(family: str) -> tuple[FontConfigPattern, bool, bool]:\n    ans = find_best_match(family, monospaced=False)\n    return ans, ans.get('weight', 0) >= FC_WEIGHT_BOLD, ans.get('slant', FC_SLANT_ROMAN) != FC_SLANT_ROMAN\n\n\ndef descriptor(f: ListedFont) -> FontConfigPattern:\n    d = f['descriptor']\n    assert d['descriptor_type'] == 'fontconfig'\n    return d\n\n\ndef prune_family_group(g: list[ListedFont]) -> list[ListedFont]:\n    # fontconfig creates dummy entries for named styles in variable fonts, prune them\n    variable_paths = {descriptor(f)['path'] for f in g if f['is_variable']}\n    if not variable_paths:\n        return g\n    def is_ok(d: FontConfigPattern) -> bool:\n        return d['variable'] or d['path'] not in variable_paths\n\n    return [x for x in g if is_ok(descriptor(x))]\n\n\ndef set_named_style(name: str, font: FontConfigPattern, vd: VariableData) -> bool:\n    q = name.lower()\n    for i, ns in enumerate(vd['named_styles']):\n        if ns['psname'].lower() == q:\n            font['named_style'] = i\n            return True\n    for i, ns in enumerate(vd['named_styles']):\n        if ns['name'].lower() == q:\n            font['named_style'] = i\n            return True\n    if vd['elided_fallback_name']:\n        for i, ns in enumerate(vd['named_styles']):\n            eq = ' '.join(ns['name'].replace(vd['elided_fallback_name'], '').strip().split()).lower()\n            if q == eq:\n                font['named_style'] = i\n                return True\n    return False\n\n\ndef lift_axes_to_named_style_if_possible(font: FontConfigPattern, vd: VariableData) -> bool:\n    axes = font.get('axes', tuple(ax['default'] for ax in vd['axes']))\n    q = {vd['axes'][i]['tag']: val for i, val in enumerate(axes)}\n    for i, ns in enumerate(vd['named_styles']):\n        if ns['axis_values'] == q:\n            font.pop('axes', None)\n            font['named_style'] = i\n            return True\n    return False\n\n\ndef set_axis_values(tag_map: dict[str, float], font: FontConfigPattern, vd: VariableData) -> bool:\n    axes = list(font.get('axes', ())) or [ax['default'] for ax in vd['axes']]\n    changed = False\n    for i, ax in enumerate(vd['axes']):\n        val = tag_map.get(ax['tag'])\n        if val is not None:\n            changed = True\n            axes[i] = val\n    if changed:\n        font['axes'] = tuple(axes)\n        lift_axes_to_named_style_if_possible(font, vd)\n    return changed\n\n\ndef get_axis_values(font: FontConfigPattern, vd: VariableData) -> dict[str, float]:\n    ans: dict[str, float] = {}\n    ns = font.get('named_style')\n    if ns is not None:\n        if ns > -1 and ns < len(vd['named_styles']):\n            ans = vd['named_styles'][ns]['axis_values']\n\n    axis_values = font.get('axes', ())\n    for i, ax in enumerate(vd['axes']):\n        tag = ax['tag']\n        if i < len(axis_values):\n            ans[tag] = axis_values[i]\n        else:\n            if tag not in ans:\n                ans[tag] = ax['default']\n    return ans\n"
  },
  {
    "path": "kitty/fonts/list.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Sequence\n\nfrom kitty.constants import is_macos\n\nfrom . import ListedFont\nfrom .common import get_variable_data_for_descriptor\n\nif is_macos:\n    from .core_text import list_fonts, prune_family_group\nelse:\n    from .fontconfig import list_fonts, prune_family_group\n\n\ndef create_family_groups(monospaced: bool = True) -> dict[str, list[ListedFont]]:\n    g: dict[str, list[ListedFont]] = {}\n    for f in list_fonts():\n        if not monospaced or f['is_monospace']:\n            g.setdefault(f['family'], []).append(f)\n    return {k: prune_family_group(v) for k, v in g.items()}\n\n\ndef as_json(indent: int | None = None) -> str:\n    import json\n    groups = create_family_groups()\n    for v in groups.values():\n        for f in v:\n            f['variable_data'] = get_variable_data_for_descriptor(f['descriptor'])  # type: ignore\n    return json.dumps(groups, indent=indent)\n\n\ndef main(argv: Sequence[str]) -> None:\n    import os\n\n    from kitty.constants import kitten_exe, kitty_exe\n    argv = list(argv)\n    if '--psnames' in argv:\n        argv.remove('--psnames')\n    os.environ['KITTY_PATH_TO_KITTY_EXE'] = kitty_exe()\n    os.execlp(kitten_exe(), 'kitten', 'choose-fonts')\n"
  },
  {
    "path": "kitty/fonts/render.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport sys\nfrom collections.abc import Callable, Generator\nfrom typing import TYPE_CHECKING, Any, Literal, Union\n\nfrom kitty.constants import fonts_dir, is_macos\nfrom kitty.fast_data_types import (\n    Screen,\n    concat_cells,\n    create_test_font_group,\n    current_fonts,\n    get_fallback_font,\n    render_decoration,\n    set_builtin_nerd_font,\n    set_font_data,\n    set_options,\n    set_send_sprite_to_gpu,\n    sprite_idx_to_pos,\n    sprite_map_set_limits,\n    test_render_line,\n    test_shape,\n)\nfrom kitty.options.types import Options, defaults\nfrom kitty.options.utils import parse_font_spec\nfrom kitty.types import _T\nfrom kitty.typing_compat import CoreTextFont, FontConfigPattern\nfrom kitty.utils import log_error\n\nfrom . import family_name_to_key\nfrom .common import get_font_files\n\nif is_macos:\n    from .core_text import font_for_family as font_for_family_macos\nelse:\n    from .fontconfig import font_for_family as font_for_family_fontconfig\n\nif TYPE_CHECKING:\n    from kitty.fast_data_types import CTFace, DecorationTypes, Face\nelse:\n    DecorationTypes = str\n\nFontObject = Union[CoreTextFont, FontConfigPattern]\ncurrent_faces: list[tuple[FontObject, bool, bool]] = []\nbuiltin_nerd_font_descriptor: FontObject | None = None\n\n\ndef font_for_family(family: str) -> tuple[FontObject, bool, bool]:\n    if is_macos:\n        return font_for_family_macos(family)\n    return font_for_family_fontconfig(family)\n\n\ndef merge_ranges(\n    a: tuple[tuple[int, int], _T], b: tuple[tuple[int, int], _T], priority_map: dict[tuple[int, int], int]\n) -> Generator[tuple[tuple[int, int], _T], None, None]:\n    a_start, a_end = a[0]\n    b_start, b_end = b[0]\n    a_val, b_val = a[1], b[1]\n    a_prio, b_prio = priority_map[a[0]], priority_map[b[0]]\n    if b_start > a_end:\n        if b_start == a_end + 1 and a_val == b_val:\n            # ranges can be coalesced\n            r = ((a_start, b_end), a_val)\n            priority_map[r[0]] = max(a_prio, b_prio)\n            yield r\n            return\n        # disjoint ranges\n        yield a\n        yield b\n        return\n    if a_val == b_val:\n        # mergeable ranges\n        r = ((a_start, max(a_end, b_end)), a_val)\n        priority_map[r[0]] = max(a_prio, b_prio)\n        yield r\n        return\n    before_range = mid_range = after_range = None\n    before_range_prio = mid_range_prio = after_range_prio = 0\n    if b_start > a_start:\n        before_range = ((a_start, b_start - 1), a_val)\n        before_range_prio = a_prio\n    mid_end = min(a_end, b_end)\n    if mid_end >= b_start:\n        # overlap range\n        mid_range = ((b_start, mid_end), a_val if priority_map[a[0]] >= priority_map[b[0]] else b_val)\n        mid_range_prio = max(a_prio, b_prio)\n    # after range\n    if mid_end is a_end:\n        if b_end > a_end:\n            after_range = ((a_end + 1, b_end), b_val)\n            after_range_prio = b_prio\n    else:\n        if a_end > b_end:\n            after_range = ((b_end + 1, a_end), a_val)\n            after_range_prio = a_prio\n    # check if the before, mid and after ranges can be coalesced\n    ranges: list[tuple[tuple[int, int], _T]] = []\n    priorities: list[int] = []\n    for rq, prio in ((before_range, before_range_prio), (mid_range, mid_range_prio), (after_range, after_range_prio)):\n        if rq is None:\n            continue\n        r = rq\n        if ranges:\n            x = ranges[-1]\n            if x[0][1] + 1 == r[0][0] and x[1] == r[1]:\n                ranges[-1] = ((x[0][0], r[0][1]), x[1])\n                priorities[-1] = max(priorities[-1], prio)\n            else:\n                ranges.append(r)\n                priorities.append(prio)\n        else:\n            ranges.append(r)\n            priorities.append(prio)\n    for r, p in zip(ranges, priorities):\n        priority_map[r[0]] = p\n    yield from ranges\n\n\ndef coalesce_symbol_maps(maps: dict[tuple[int, int], _T]) -> dict[tuple[int, int], _T]:\n    if not maps:\n        return maps\n    priority_map = {r: i for i, r in enumerate(maps.keys())}\n    ranges = tuple((r, maps[r]) for r in sorted(maps))\n    ans = [ranges[0]]\n\n    for i in range(1, len(ranges)):\n        r = ranges[i]\n        new_ranges = merge_ranges(ans[-1], r, priority_map)\n        if ans:\n            del ans[-1]\n        if not ans:\n            ans = list(new_ranges)\n        else:\n            for r in new_ranges:\n                prev = ans[-1]\n                if prev[0][1] + 1 == r[0][0] and prev[1] == r[1]:\n                    ans[-1] = (prev[0][0], r[0][1]), prev[1]\n                else:\n                    ans.append(r)\n    return dict(ans)\n\n\ndef create_symbol_map(opts: Options) -> tuple[tuple[int, int, int], ...]:\n    val = coalesce_symbol_maps(opts.symbol_map)\n    family_map: dict[str, int] = {}\n    count = 0\n    for family in val.values():\n        if family not in family_map:\n            font, bold, italic = font_for_family(family)\n            fkey = family_name_to_key(family)\n            if fkey in ('symbolsnfm', 'symbols nerd font mono') and font['postscript_name'] != 'SymbolsNFM' and builtin_nerd_font_descriptor:\n                font = builtin_nerd_font_descriptor\n                bold = italic = False\n            family_map[family] = count\n            count += 1\n            current_faces.append((font, bold, italic))\n    sm = tuple((a, b, family_map[f]) for (a, b), f in val.items())\n    return sm\n\n\ndef create_narrow_symbols(opts: Options) -> tuple[tuple[int, int, int], ...]:\n    return tuple((a, b, v) for (a, b), v in coalesce_symbol_maps(opts.narrow_symbols).items())\n\n\ndescriptor_overrides: dict[int, tuple[str, bool, bool]] = {}\n\n\ndef descriptor_for_idx(idx: int) -> tuple[FontObject | str, bool, bool]:\n    ans = descriptor_overrides.get(idx)\n    if ans is None:\n        return current_faces[idx]\n    return ans\n\n\ndef dump_font_debug() -> None:\n    cf = current_fonts()\n    log_error('Text fonts:')\n    for key, text in {'medium': 'Normal', 'bold': 'Bold', 'italic': 'Italic', 'bi': 'Bold-Italic'}.items():\n        log_error(f'  {text}:', cf[key].identify_for_debug())  # type: ignore\n    ss = cf['symbol']\n    if ss:\n        log_error('Symbol map fonts:')\n        for s in ss:\n            log_error('  ' + s.identify_for_debug())\n\n\ndef set_font_family(opts: Options | None = None, override_font_size: float | None = None, add_builtin_nerd_font: bool = False) -> None:\n    global current_faces, builtin_nerd_font_descriptor\n    opts = opts or defaults\n    sz = override_font_size or opts.font_size\n    font_map = get_font_files(opts)\n    current_faces = [(font_map['medium'], False, False)]\n    ftypes: list[Literal['bold', 'italic', 'bi']] = ['bold', 'italic', 'bi']\n    indices = {k: 0 for k in ftypes}\n    for k in ftypes:\n        if k in font_map:\n            indices[k] = len(current_faces)\n            current_faces.append((font_map[k], 'b' in k, 'i' in k))\n    before = len(current_faces)\n    if add_builtin_nerd_font:\n        builtin_nerd_font_path = os.path.join(fonts_dir, 'SymbolsNerdFontMono-Regular.ttf')\n        if os.path.exists(builtin_nerd_font_path):\n            builtin_nerd_font_descriptor = set_builtin_nerd_font(builtin_nerd_font_path)\n        else:\n            log_error(f'No builtin NERD font found in {fonts_dir}')\n    sm = create_symbol_map(opts)\n    ns = create_narrow_symbols(opts)\n    num_symbol_fonts = len(current_faces) - before\n    set_font_data(\n        descriptor_for_idx,\n        indices['bold'], indices['italic'], indices['bi'], num_symbol_fonts,\n        sm, sz, ns\n    )\n\n\nif TYPE_CHECKING:\n    import ctypes\n    CBufType = ctypes.Array[ctypes.c_ubyte]\nelse:\n    CBufType = None\nUnderlineCallback = Callable[[CBufType, int, int, int, int], None]\n\n\nclass setup_for_testing:\n\n    xnum = 100000\n    ynum = 100\n    baseline = 0\n\n    def __init__(self, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, main_face_path: str = ''):\n        self.family, self.size, self.dpi = family, size, dpi\n        self.main_face_path = main_face_path\n\n    def __enter__(self) -> tuple[dict[tuple[int, int, int], bytes], int, int]:\n        global descriptor_overrides\n        opts = defaults._replace(font_family=parse_font_spec(self.family), font_size=self.size)\n        set_options(opts)\n        sprites = {}\n\n        def send_to_gpu(x: int, y: int, z: int, data: bytes) -> None:\n            sprites[(x, y, z)] = data\n\n        sprite_map_set_limits(self.xnum, self.ynum)\n        set_send_sprite_to_gpu(send_to_gpu)\n        self.orig_desc_overrides = descriptor_overrides\n        descriptor_overrides = {}\n        if self.main_face_path:\n            descriptor_overrides[0] = self.main_face_path, False, False\n        try:\n            set_font_family(opts)\n            cell_width, cell_height, self.baseline = create_test_font_group(self.size, self.dpi, self.dpi)\n            return sprites, cell_width, cell_height\n        except Exception:\n            set_send_sprite_to_gpu(None)\n            raise\n\n    def __exit__(self, *args: Any) -> None:\n        global descriptor_overrides\n        descriptor_overrides = self.orig_desc_overrides\n        set_send_sprite_to_gpu(None)\n\n\ndef render_string(text: str, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0) -> tuple[int, int, list[bytes]]:\n    with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height):\n        s = Screen(None, 1, len(text)*2)\n        line = s.line(0)\n        s.draw(text)\n        test_render_line(line)\n    cells = []\n    found_content = False\n    for i in reversed(range(s.columns)):\n        sp = line.sprite_at(i)\n        sp &= 0x7fffffff\n        if not sp and not found_content:\n            continue\n        found_content = True\n        cells.append(sprites[sprite_idx_to_pos(sp, setup_for_testing.xnum, setup_for_testing.ynum)])\n    return cell_width, cell_height, list(reversed(cells))\n\n\ndef shape_string(\n    text: str = \"abcd\", family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, path: str | None = None\n) -> list[tuple[int, int, int, tuple[int, ...]]]:\n    with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height):\n        s = Screen(None, 1, len(text)*2)\n        line = s.line(0)\n        s.draw(text)\n        return test_shape(line, path)\n\n\ndef show(rgba_data: bytes | memoryview, width: int, height: int, fmt: int = 32) -> None:\n    from base64 import standard_b64encode\n\n    from kittens.tui.images import GraphicsCommand\n\n    data = memoryview(standard_b64encode(rgba_data))\n    cmd = GraphicsCommand()\n    cmd.a = 'T'\n    cmd.f = fmt\n    cmd.s = width\n    cmd.v = height\n\n    sys.stdout.flush()\n    while data:\n        chunk, data = data[:4096], data[4096:]\n        cmd.m = 1 if data else 0\n        sys.stdout.buffer.write(cmd.serialize(chunk))\n        cmd.clear()\n    sys.stdout.buffer.flush()\n\n\ndef display_bitmap(rgb_data: bytes, width: int, height: int) -> None:\n    assert len(rgb_data) == 4 * width * height\n    show(rgb_data, width, height)\n\n\ndef test_render_string(\n        text: str = 'Hello, world!',\n        family: str = 'monospace',\n        size: float = 64.0,\n        dpi: float = 96.0\n) -> None:\n\n    cell_width, cell_height, cells = render_string(text, family, size, dpi)\n    rgb_data = concat_cells(cell_width, cell_height, True, tuple(cells))\n    cf = current_fonts()\n    fonts = [cf['medium'].postscript_name()]\n    fonts.extend(f.postscript_name() for f in cf['fallback'])\n    msg = 'Rendered string {} below, with fonts: {}\\n'.format(text, ', '.join(fonts))\n    try:\n        print(msg)\n    except UnicodeEncodeError:\n        sys.stdout.buffer.write(msg.encode('utf-8') + b'\\n')\n    display_bitmap(rgb_data, cell_width * len(cells), cell_height)\n    print('\\n')\n\n\ndef test_fallback_font(qtext: str | None = None, bold: bool = False, italic: bool = False) -> None:\n    with setup_for_testing():\n        if qtext:\n            trials = [qtext]\n        else:\n            trials = ['你', 'He\\u0347\\u0305', '\\U0001F929']\n        for text in trials:\n            f = get_fallback_font(text, bold, italic)\n            try:\n                print(text, f)\n            except UnicodeEncodeError:\n                sys.stdout.buffer.write(f'{text} {f}\\n'.encode())\n\n\ndef showcase() -> None:\n    f = 'monospace' if is_macos else 'Liberation Mono'\n    test_render_string('He\\u0347\\u0305llo\\u0337, w\\u0302or\\u0306l\\u0354d!', family=f)\n    test_render_string('你好,世界', family=f)\n    test_render_string('│😁│🙏│😺│', family=f)\n    test_render_string('A=>>B!=C', family='Fira Code')\n\n\ndef create_face(path: str) -> 'Union[CTFace, Face]':\n    if is_macos:\n        from kitty.fast_data_types import CTFace\n        return CTFace(path=path)\n    from kitty.fast_data_types import Face\n    return Face(path=path)\n\n\ndef test_render_codepoint(chars: str = '😺', path: str = '/t/Noto-COLRv1.ttf', font_size: float = 160.0) -> None:\n    f = create_face(path=path)\n    f.set_size(font_size, 96, 96)\n    for char in chars:\n        bitmap, w, h = f.render_codepoint(ord(char))\n        print('Rendered:', char)\n        display_bitmap(bitmap, w, h)\n        print('\\n')\n\n\ndef test_render_decoration(which: DecorationTypes, cell_width: int, cell_height: int, underline_position: int, underline_thickness: int) -> None:\n    buf = render_decoration(which, cell_width, cell_height, underline_position, underline_thickness)\n    cells = buf, buf, buf, buf, buf\n    rgb_data = concat_cells(cell_width, cell_height, False, cells)\n    display_bitmap(rgb_data, cell_width * len(cells), cell_height)\n"
  },
  {
    "path": "kitty/fonts.c",
    "content": "/*\n * vim:fileencoding=utf-8\n * fonts.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"fonts.h\"\n#include \"pyport.h\"\n#include \"charsets.h\"\n#include \"state.h\"\n#include \"char-props.h\"\n#include \"decorations.h\"\n#include \"glyph-cache.h\"\n#include \"print-graphics.h\"\n\n#define MISSING_GLYPH 1\n#define MAX_NUM_EXTRA_GLYPHS_PUA 4u\n\n#define debug debug_fonts\n\nstatic PyObject *python_send_to_gpu_impl = NULL;\nextern PyTypeObject Line_Type;\n\nenum {NO_FONT=-3, MISSING_FONT=-2, BLANK_FONT=-1, BOX_FONT=0};\ntypedef enum {\n    LIGATURE_UNKNOWN, INFINITE_LIGATURE_START, INFINITE_LIGATURE_MIDDLE, INFINITE_LIGATURE_END\n} LigatureType;\n\n\ntypedef struct {\n    unsigned x, y, z, xnum, ynum, max_y;\n} GPUSpriteTracker;\n\ntypedef struct RunFont {\n    unsigned scale, subscale_n, subscale_d, multicell_y;\n    union {\n        struct { uint8_t vertical: 4; uint8_t horizontal: 4; };\n        uint8_t val;\n    } align;\n    ssize_t font_idx;\n} RunFont;\n\nstatic hb_buffer_t *harfbuzz_buffer = NULL;\nstatic hb_feature_t hb_features[3] = {{0}};\nstatic struct { char_type *codepoints; size_t capacity; } shape_buffer = {0};\nstatic size_t max_texture_size = 1024, max_array_len = 1024;\ntypedef enum { LIGA_FEATURE, DLIG_FEATURE, CALT_FEATURE } HBFeature;\n\ntypedef struct {\n    char_type left, right;\n    size_t font_idx;\n} SymbolMap;\n\nstatic SymbolMap *symbol_maps = NULL, *narrow_symbols = NULL;\nstatic size_t num_symbol_maps = 0, num_narrow_symbols = 0;\n\ntypedef enum { SPACER_STRATEGY_UNKNOWN, SPACERS_BEFORE, SPACERS_AFTER, SPACERS_IOSEVKA } SpacerStrategy;\n\ntypedef struct {\n    PyObject *face;\n    // Map glyphs to sprite map co-ords\n    SPRITE_POSITION_MAP_HANDLE sprite_position_hash_table;\n    hb_feature_t* ffs_hb_features;\n    size_t num_ffs_hb_features;\n    GLYPH_PROPERTIES_MAP_HANDLE glyph_properties_hash_table;\n    bool bold, italic, emoji_presentation;\n    SpacerStrategy spacer_strategy;\n} Font;\n\ntypedef struct Canvas {\n    pixel *buf;\n    uint8_t *alpha_mask;\n    unsigned current_cells, alloced_cells, alloced_scale, current_scale;\n    size_t size_in_bytes, alpha_mask_sz_in_bytes;\n} Canvas;\n\n#define NAME fallback_font_map_t\n#define KEY_TY const char*\n#define VAL_TY size_t\nstatic void free_const(const void* x) { free((void*)x); }\n#define KEY_DTOR_FN free_const\n#include \"kitty-verstable.h\"\n\ntypedef struct ScaledFontData {\n    FontCellMetrics fcm;\n    double font_sz_in_pts;\n} ScaledFontData;\n\n#define NAME scaled_font_map_t\n#define KEY_TY float\n#define VAL_TY ScaledFontData\n#define HASH_FN vt_hash_float\n#define CMPR_FN vt_cmpr_float\n#include \"kitty-verstable.h\"\n\ntypedef union DecorationsKey {\n    struct { uint8_t scale : 8, subscale_n : 8, subscale_d : 8, align : 8, multicell_y : 8, u1 : 8, u2 : 8, u3 : 8; };\n    uint64_t val;\n} DecorationsKey;\nstatic_assert(sizeof(DecorationsKey) == sizeof(uint64_t), \"Fix the ordering of DecorationsKey\");\n\ntypedef struct DecorationMetadata {\n    sprite_index start_idx;\n    DecorationGeometry underline_region;\n} DecorationMetadata;\n\nstatic uint64_t hash_decorations_key(DecorationsKey k) { return vt_hash_integer(k.val); }\nstatic bool cmpr_decorations_key(DecorationsKey a, DecorationsKey b) { return a.val == b.val; }\n#define NAME decorations_index_map_t\n#define KEY_TY DecorationsKey\n#define VAL_TY DecorationMetadata\n#define HASH_FN hash_decorations_key\n#define CMPR_FN cmpr_decorations_key\n#include \"kitty-verstable.h\"\n\n\ntypedef struct {\n    FONTS_DATA_HEAD\n    id_type id;\n    size_t fonts_capacity, fonts_count, fallback_fonts_count;\n    ssize_t medium_font_idx, bold_font_idx, italic_font_idx, bi_font_idx, first_symbol_font_idx, first_fallback_font_idx;\n    Font *fonts;\n    Canvas canvas;\n    GPUSpriteTracker sprite_tracker;\n    fallback_font_map_t fallback_font_map;\n    scaled_font_map_t scaled_font_map;\n    decorations_index_map_t decorations_index_map;\n} FontGroup;\n\nstatic FontGroup* font_groups = NULL;\nstatic size_t font_groups_capacity = 0;\nstatic size_t num_font_groups = 0;\nstatic id_type font_group_id_counter = 0;\nstatic void initialize_font_group(FontGroup *fg);\n\nstatic void\ndisplay_glyph(const pixel *b, unsigned width, unsigned height) {\n    print_abgr32(b, width, height);\n}\n\nstatic void\ndump_sprite(pixel *b, unsigned width, unsigned height) {\n    for (unsigned y = 0; y < height; y++) {\n        pixel *p = b + y * width;\n        for (unsigned x = 0; x < width; x++) printf(\"%d \", p[x] != 0);\n        printf(\"\\n\");\n    }\n}\n\nstatic void\npython_send_to_gpu(FontGroup *fg, sprite_index idx, pixel *buf) {\n    if (0) dump_sprite(buf, fg->fcm.cell_width, fg->fcm.cell_height);\n    unsigned int x, y, z;\n    sprite_index_to_pos(idx, fg->sprite_tracker.xnum, fg->sprite_tracker.ynum, &x, &y, &z);\n    const size_t sprite_size = (size_t)fg->fcm.cell_width * fg->fcm.cell_height;\n    PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, \"IIIy#\", x, y, z, buf, sprite_size * sizeof(buf[0]));\n    if (ret == NULL) PyErr_Print();\n    else Py_DECREF(ret);\n}\n\nstatic void\nensure_canvas_can_fit(FontGroup *fg, unsigned cells, unsigned scale) {\n#define cs(cells, scale) (sizeof(fg->canvas.buf[0]) * 3u * cells * fg->fcm.cell_width * (fg->fcm.cell_height + 1) * scale * scale)\n    size_t size_in_bytes = cs(cells, scale);\n    if (size_in_bytes > fg->canvas.size_in_bytes) {\n        free(fg->canvas.buf);\n        fg->canvas.alloced_cells = MAX(8u, cells + 4u);\n        fg->canvas.alloced_scale = MAX(scale, 4u);\n        fg->canvas.size_in_bytes = cs(fg->canvas.alloced_cells, fg->canvas.alloced_scale);\n        fg->canvas.buf = malloc(fg->canvas.size_in_bytes);\n        if (!fg->canvas.buf) fatal(\"Out of memory allocating canvas\");\n    }\n    fg->canvas.current_cells = cells;\n    fg->canvas.current_scale = scale;\n    if (fg->canvas.buf) memset(fg->canvas.buf, 0, cs(cells, scale));\n#undef cs\n    size_in_bytes = (sizeof(fg->canvas.alpha_mask[0]) * SUPERSAMPLE_FACTOR * SUPERSAMPLE_FACTOR * 2 * fg->fcm.cell_width * fg->fcm.cell_height * scale * scale);\n    if (size_in_bytes > fg->canvas.alpha_mask_sz_in_bytes) {\n        fg->canvas.alpha_mask_sz_in_bytes = size_in_bytes;\n        fg->canvas.alpha_mask = malloc(fg->canvas.alpha_mask_sz_in_bytes);\n        if (!fg->canvas.alpha_mask) fatal(\"Out of memory allocating canvas\");\n    }\n}\n\n\nstatic void\nsave_window_font_groups(void) {\n    for (size_t o = 0; o < global_state.num_os_windows; o++) {\n        OSWindow *w = global_state.os_windows + o;\n        w->temp_font_group_id = w->fonts_data ? ((FontGroup*)(w->fonts_data))->id : 0;\n    }\n}\n\nstatic void\nrestore_window_font_groups(void) {\n    for (size_t o = 0; o < global_state.num_os_windows; o++) {\n        OSWindow *w = global_state.os_windows + o;\n        w->fonts_data = NULL;\n        for (size_t i = 0; i < num_font_groups; i++) {\n            if (font_groups[i].id == w->temp_font_group_id) {\n                w->fonts_data = (FONTS_DATA_HANDLE)(font_groups + i);\n                break;\n            }\n        }\n    }\n}\n\nstatic bool\nfont_group_is_unused(FontGroup *fg) {\n    for (size_t o = 0; o < global_state.num_os_windows; o++) {\n        OSWindow *w = global_state.os_windows + o;\n        if (w->temp_font_group_id == fg->id) return false;\n    }\n    return true;\n}\n\nvoid\nfree_maps(Font *font) {\n    free_sprite_position_hash_table(&font->sprite_position_hash_table);\n    free_glyph_properties_hash_table(&font->glyph_properties_hash_table);\n}\n\nstatic void\ndel_font(Font *f) {\n    Py_CLEAR(f->face);\n    free(f->ffs_hb_features); f->ffs_hb_features = NULL;\n    free_maps(f);\n    f->bold = false; f->italic = false;\n}\n\nstatic void\ndel_font_group(FontGroup *fg) {\n    free(fg->canvas.buf); free(fg->canvas.alpha_mask); fg->canvas = (Canvas){0};\n    free_sprite_data((FONTS_DATA_HANDLE)fg);\n    vt_cleanup(&fg->fallback_font_map);\n    vt_cleanup(&fg->scaled_font_map);\n    vt_cleanup(&fg->decorations_index_map);\n    for (size_t i = 0; i < fg->fonts_count; i++) del_font(fg->fonts + i);\n    free(fg->fonts); fg->fonts = NULL; fg->fonts_count = 0;\n}\n\nstatic void\ntrim_unused_font_groups(void) {\n    save_window_font_groups();\n    size_t i = 0;\n    while (i < num_font_groups) {\n        if (font_group_is_unused(font_groups + i)) {\n            del_font_group(font_groups + i);\n            size_t num_to_right = (--num_font_groups) - i;\n            if (!num_to_right) break;\n            memmove(font_groups + i, font_groups + 1 + i, num_to_right * sizeof(FontGroup));\n        } else i++;\n    }\n    restore_window_font_groups();\n}\n\nstatic void\nadd_font_group(void) {\n    if (num_font_groups) trim_unused_font_groups();\n    if (num_font_groups >= font_groups_capacity) {\n        save_window_font_groups();\n        font_groups_capacity += 5;\n        font_groups = realloc(font_groups, sizeof(FontGroup) * font_groups_capacity);\n        if (font_groups == NULL) fatal(\"Out of memory creating a new font group\");\n        restore_window_font_groups();\n    }\n    num_font_groups++;\n}\n\nstatic FontGroup*\nfont_group_for(double font_sz_in_pts, double logical_dpi_x, double logical_dpi_y) {\n    for (size_t i = 0; i < num_font_groups; i++) {\n        FontGroup *fg = font_groups + i;\n        if (fg->font_sz_in_pts == font_sz_in_pts && fg->logical_dpi_x == logical_dpi_x && fg->logical_dpi_y == logical_dpi_y) return fg;\n    }\n    add_font_group();\n    FontGroup *fg = font_groups + num_font_groups - 1;\n    zero_at_ptr(fg);\n    fg->font_sz_in_pts = font_sz_in_pts;\n    fg->logical_dpi_x = logical_dpi_x;\n    fg->logical_dpi_y = logical_dpi_y;\n    fg->id = ++font_group_id_counter;\n    initialize_font_group(fg);\n    return fg;\n}\n\n\n\n// Sprites {{{\n\nvoid\nsprite_tracker_set_limits(size_t max_texture_size_, size_t max_array_len_) {\n    max_texture_size = max_texture_size_;\n    max_array_len = MIN(0xfffu, max_array_len_);\n}\n\nstatic bool\ndo_increment(FontGroup *fg) {\n    fg->sprite_tracker.x++;\n    if (fg->sprite_tracker.x >= fg->sprite_tracker.xnum) {\n        fg->sprite_tracker.x = 0; fg->sprite_tracker.y++;\n        fg->sprite_tracker.ynum = MIN(MAX(fg->sprite_tracker.ynum, fg->sprite_tracker.y + 1), fg->sprite_tracker.max_y);\n        if (fg->sprite_tracker.y >= fg->sprite_tracker.max_y) {\n            fg->sprite_tracker.y = 0; fg->sprite_tracker.z++;\n            if (fg->sprite_tracker.z >= MIN((size_t)UINT16_MAX, max_array_len)) { PyErr_SetString(PyExc_RuntimeError, \"Out of texture space for sprites\"); return false; }\n        }\n    }\n    return true;\n}\n\nstatic uint32_t\ncurrent_sprite_index(const GPUSpriteTracker *sprite_tracker) {\n    return sprite_tracker->z * (sprite_tracker->xnum * sprite_tracker->ynum) + sprite_tracker->y * sprite_tracker->xnum + sprite_tracker->x;\n}\n\nstatic SpritePosition*\nsprite_position_for(FontGroup *fg, RunFont rf, glyph_index *glyphs, unsigned glyph_count, uint8_t ligature_index, unsigned cell_count) {\n    bool created;\n    Font *font = fg->fonts + rf.font_idx;\n    uint8_t subscale = ((rf.subscale_n & 0xf) << 4) | (rf.subscale_d & 0xf);\n    SpritePosition *s = find_or_create_sprite_position(\n        font->sprite_position_hash_table, glyphs, glyph_count, ligature_index, cell_count,\n        rf.scale, subscale, rf.multicell_y, rf.align.val, &created);\n    if (!s) { PyErr_NoMemory(); return NULL; }\n    return s;\n}\n\nvoid\nsprite_tracker_current_layout(FONTS_DATA_HANDLE data, unsigned int *x, unsigned int *y, unsigned int *z) {\n    FontGroup *fg = (FontGroup*)data;\n    *x = fg->sprite_tracker.xnum; *y = fg->sprite_tracker.ynum; *z = fg->sprite_tracker.z;\n}\n\n\nstatic void\nsprite_tracker_set_layout(GPUSpriteTracker *sprite_tracker, unsigned int cell_width, unsigned int cell_height) {\n    sprite_tracker->xnum = MIN(MAX(1u, max_texture_size / cell_width), (size_t)UINT16_MAX);\n    sprite_tracker->max_y = MIN(MAX(1u, max_texture_size / cell_height), (size_t)UINT16_MAX);\n    sprite_tracker->ynum = 1;\n    sprite_tracker->x = 0; sprite_tracker->y = 0; sprite_tracker->z = 0;\n}\n\nstatic void\ncalculate_underline_exclusion_zones(pixel *buf, const FontGroup *fg, DecorationGeometry dg, FontCellMetrics scaled_metrics) {\n    pixel *ans = buf + fg->fcm.cell_height * fg->fcm.cell_width;\n    const unsigned bottom = MIN(dg.top + dg.height, fg->fcm.cell_height);\n    unsigned thickness = scaled_metrics.underline_thickness;\n    switch(OPT(underline_exclusion.unit)) {\n        case 2:\n            thickness = ((long)round((OPT(underline_exclusion).thickness * (fg->logical_dpi_x / 72.0)))); break;\n        case 1:\n            thickness = (unsigned)OPT(underline_exclusion).thickness; break;\n        default:\n            thickness = (unsigned)(OPT(underline_exclusion).thickness * thickness); break;\n    }\n    thickness = MAX(1u, thickness);\n    if (0) printf(\"dg: %u %u cell_height: %u scaled_cell_height: %u\\n\", dg.top, dg.height, fg->fcm.cell_height, scaled_metrics.cell_height);\n    if (0) { display_glyph(buf, fg->fcm.cell_width, fg->fcm.cell_height); printf(\"\\n\"); }\n    unsigned max_overlap = 0;\n#define is_rendered(x, y) ((buf[(y) * fg->fcm.cell_width + (x)] & 0x000000ff) > 0)\n    for (unsigned x = 0; x < fg->fcm.cell_width; x++) {\n        for (unsigned y = dg.top; y < bottom && !ans[x]; y++) {\n            if (is_rendered(x, y)) {\n                while (y + 1 < bottom && is_rendered(x, y + 1)) y++;\n                max_overlap = MAX(max_overlap, y - dg.top + 1);\n                unsigned start_x = x > thickness ? x - thickness : 0;\n                for (unsigned dx = start_x; dx < MIN(x + thickness, fg->fcm.cell_width); dx++) ans[dx] = 0xffffffff;\n                break;\n            }\n        }\n    }\n#undef is_rendered\n    if (dg.height > 1 && max_overlap <= dg.height / 2) {\n        // ignore half thickness overlap as this is likely a false positive not an actual descender\n        memset(ans, 0, fg->fcm.cell_width * sizeof(ans[0]));\n    }\n    if (0) dump_sprite(ans, fg->fcm.cell_width, 1);\n}\n\nstatic sprite_index\ncurrent_send_sprite_to_gpu(FontGroup *fg, pixel *buf, DecorationMetadata dec, FontCellMetrics scaled_metrics) {\n    sprite_index ans = current_sprite_index(&fg->sprite_tracker);\n    if (!do_increment(fg)) return 0;\n    if (python_send_to_gpu_impl) { python_send_to_gpu(fg, ans, buf); return ans; }\n    if (dec.underline_region.height && OPT(underline_exclusion).thickness > 0) calculate_underline_exclusion_zones(\n            buf, fg, dec.underline_region, scaled_metrics);\n    send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, ans, buf, dec.start_idx);\n    if (0) { printf(\"Sprite: %u dec_idx: %u\\n\", ans, dec.start_idx); display_glyph(buf, fg->fcm.cell_width, fg->fcm.cell_height); printf(\"\\n\"); }\n    return ans;\n}\n\n\n// }}}\n\nstatic PyObject*\ndesc_to_face(PyObject *desc, FONTS_DATA_HANDLE fg) {\n    PyObject *d = specialize_font_descriptor(desc, fg->font_sz_in_pts, fg->logical_dpi_x, fg->logical_dpi_y);\n    if (d == NULL) return NULL;\n    PyObject *ans = face_from_descriptor(d, fg);\n    Py_DECREF(d);\n    return ans;\n}\n\nstatic void\nadd_feature(FontFeatures *output, const hb_feature_t *feature) {\n    for (size_t i = 0; i < output->count; i++) {\n        if (output->features[i].tag == feature->tag) {\n            output->features[i] = *feature;\n            return;\n        }\n    }\n    output->features[output->count++] = *feature;\n}\n\nstatic const char*\ntag_to_string(uint32_t tag, uint8_t bytes[5]) {\n    bytes[0] = (tag >> 24) & 0xff;\n    bytes[1] = (tag >> 16) & 0xff;\n    bytes[2] = (tag >> 8) & 0xff;\n    bytes[3] = (tag) & 0xff;\n    bytes[4] = 0;\n    return (const char*)bytes;\n}\n\nPyObject*\nfont_features_as_dict(const FontFeatures *font_features) {\n    RAII_PyObject(ans, PyDict_New());\n    if (!ans) return NULL;\n    char buf[256];\n    char tag[5] = {0};\n    for (size_t i = 0; i < font_features->count; i++) {\n        tag_to_string(font_features->features[i].tag, (unsigned char*)tag);\n        hb_feature_to_string(&font_features->features[i], buf, arraysz(buf));\n        PyObject *t = PyUnicode_FromString(buf);\n        if (!t) return NULL;\n        if (PyDict_SetItemString(ans, tag, t) != 0) return NULL;\n    }\n    Py_INCREF(ans); return ans;\n}\n\nbool\ncreate_features_for_face(const char *psname, PyObject *features, FontFeatures *output) {\n    size_t count_from_descriptor = features ? PyTuple_GET_SIZE(features): 0;\n    __typeof__(OPT(font_features).entries) from_opts = NULL;\n    if (psname) {\n        for (size_t i = 0; i < OPT(font_features).num && !from_opts; i++) {\n            __typeof__(OPT(font_features).entries) e = OPT(font_features).entries + i;\n            if (strcmp(e->psname, psname) == 0) from_opts = e;\n        }\n    }\n    size_t count_from_opts = from_opts ? from_opts->num : 0;\n    output->features = calloc(MAX(2u, count_from_opts + count_from_descriptor), sizeof(output->features[0]));\n    if (!output->features) { PyErr_NoMemory(); return false; }\n    for (size_t i = 0; i < count_from_opts; i++) {\n        add_feature(output, &from_opts->features[i]);\n    }\n    for (size_t i = 0; i < count_from_descriptor; i++) {\n        ParsedFontFeature *f = (ParsedFontFeature*)PyTuple_GET_ITEM(features, i);\n        add_feature(output, &f->feature);\n    }\n    if (!output->count) {\n        if (strstr(psname, \"NimbusMonoPS-\") == psname) {\n            add_feature(output, &hb_features[LIGA_FEATURE]);\n            add_feature(output, &hb_features[DLIG_FEATURE]);\n        }\n    }\n    return true;\n}\n\nstatic bool\ninit_hash_tables(Font *f) {\n    f->sprite_position_hash_table = create_sprite_position_hash_table();\n    if (!f->sprite_position_hash_table) { PyErr_NoMemory(); return false; }\n    f->glyph_properties_hash_table = create_glyph_properties_hash_table();\n    if (!f->glyph_properties_hash_table) { PyErr_NoMemory(); return false; }\n    return true;\n}\n\nstatic bool\ninit_font(Font *f, PyObject *face, bool bold, bool italic, bool emoji_presentation) {\n    f->face = face; Py_INCREF(f->face);\n    f->bold = bold; f->italic = italic; f->emoji_presentation = emoji_presentation;\n    if (!init_hash_tables(f)) return false;\n    const FontFeatures *features = features_for_face(face);\n    f->ffs_hb_features = calloc(1 + features->count, sizeof(hb_feature_t));\n    if (!f->ffs_hb_features) { PyErr_NoMemory(); return false; }\n    f->num_ffs_hb_features = features->count;\n    if (features->count) memcpy(f->ffs_hb_features, features->features, sizeof(hb_feature_t) * features->count);\n    memcpy(f->ffs_hb_features + f->num_ffs_hb_features++, &hb_features[CALT_FEATURE], sizeof(hb_feature_t));\n    return true;\n}\n\nstatic void\nfree_font_groups(void) {\n    if (font_groups) {\n        for (size_t i = 0; i < num_font_groups; i++) del_font_group(font_groups + i);\n        free(font_groups); font_groups = NULL;\n        font_groups_capacity = 0; num_font_groups = 0;\n    }\n}\n\nstatic void\nadjust_metric(unsigned int *metric, float adj, AdjustmentUnit unit, double dpi) {\n    if (adj == 0.f) return;\n    int a = 0;\n    switch (unit) {\n        case POINT:\n            a = ((long)round((adj * (dpi / 72.0)))); break;\n        case PERCENT:\n            *metric = (int)roundf((fabsf(adj) * (float)*metric) / 100.f); return;\n        case PIXEL:\n            a = (int)roundf(adj); break;\n    }\n    *metric = (a < 0 && -a > (int)*metric) ? 0 : *metric + a;\n}\n\nstatic unsigned int\nadjust_ypos(unsigned int pos, unsigned int cell_height, int adjustment) {\n    if (adjustment >= 0) adjustment = MIN(adjustment, (int)pos - 1);\n    else adjustment = MAX(adjustment, (int)pos - (int)cell_height + 1);\n    return pos - adjustment;\n}\n\nstatic void\ncalc_cell_metrics(FontGroup *fg, PyObject *face) {\n    fg->fcm = cell_metrics(face);\n    if (!fg->fcm.cell_width) fatal(\"Failed to calculate cell width for the specified font\");\n    unsigned int before_cell_height = fg->fcm.cell_height;\n    unsigned int cw = fg->fcm.cell_width, ch = fg->fcm.cell_height;\n    adjust_metric(&cw, OPT(cell_width).val, OPT(cell_width).unit, fg->logical_dpi_x);\n    adjust_metric(&ch, OPT(cell_height).val, OPT(cell_height).unit, fg->logical_dpi_y);\n#define MAX_DIM 1000\n#define MIN_WIDTH 2\n#define MIN_HEIGHT 4\n    if (cw >= MIN_WIDTH && cw <= MAX_DIM) fg->fcm.cell_width = cw;\n    else log_error(\"Cell width invalid after adjustment, ignoring modify_font cell_width\");\n    if (ch >= MIN_HEIGHT && ch <= MAX_DIM) fg->fcm.cell_height = ch;\n    else log_error(\"Cell height invalid after adjustment, ignoring modify_font cell_height\");\n    int line_height_adjustment = fg->fcm.cell_height - before_cell_height;\n    if (fg->fcm.cell_height < MIN_HEIGHT) fatal(\"Line height too small: %u\", fg->fcm.cell_height);\n    if (fg->fcm.cell_height > MAX_DIM) fatal(\"Line height too large: %u\", fg->fcm.cell_height);\n    if (fg->fcm.cell_width < MIN_WIDTH) fatal(\"Cell width too small: %u\", fg->fcm.cell_width);\n    if (fg->fcm.cell_width > MAX_DIM) fatal(\"Cell width too large: %u\", fg->fcm.cell_width);\n#undef MIN_WIDTH\n#undef MIN_HEIGHT\n#undef MAX_DIM\n\n    unsigned int baseline_before = fg->fcm.baseline;\n#define A(which, dpi) adjust_metric(&fg->fcm.which, OPT(which).val, OPT(which).unit, fg->logical_dpi_##dpi);\n    A(underline_thickness, y); A(underline_position, y); A(strikethrough_thickness, y); A(strikethrough_position, y); A(baseline, y);\n#undef A\n\n    if (baseline_before != fg->fcm.baseline) {\n        int adjustment = fg->fcm.baseline - baseline_before;\n        fg->fcm.baseline = adjust_ypos(baseline_before, fg->fcm.cell_height, adjustment);\n        fg->fcm.underline_position = adjust_ypos(fg->fcm.underline_position, fg->fcm.cell_height, adjustment);\n        fg->fcm.strikethrough_position = adjust_ypos(fg->fcm.strikethrough_position, fg->fcm.cell_height, adjustment);\n    }\n\n    fg->fcm.underline_position = MIN(fg->fcm.cell_height - 1, fg->fcm.underline_position);\n    // ensure there is at least a couple of pixels available to render styled underlines\n    // there should be at least one pixel on either side of the underline_position\n    if (fg->fcm.underline_position > fg->fcm.baseline + 1 && fg->fcm.underline_position > fg->fcm.cell_height - 1)\n      fg->fcm.underline_position = MAX(fg->fcm.baseline + 1, fg->fcm.cell_height - 1);\n    if (line_height_adjustment > 1) {\n        fg->fcm.baseline += MIN(fg->fcm.cell_height - 1, (unsigned)line_height_adjustment / 2);\n        fg->fcm.underline_position += MIN(fg->fcm.cell_height - 1, (unsigned)line_height_adjustment / 2);\n    }\n}\n\nstatic bool\nface_has_codepoint(const void* face, char_type cp) {\n    return glyph_id_for_codepoint(face, cp) > 0;\n}\n\nstatic bool\nhas_emoji_presentation(const CPUCell *c, const ListOfChars *lc) {\n    bool is_text_presentation;\n    CharProps cp;\n    return c->is_multicell && lc->count && (cp = char_props_for(lc->chars[0])).is_emoji && (\n        ( (is_text_presentation = wcwidth_std(cp) < 2) && lc->count > 1 && lc->chars[1] == VS16 ) ||\n        ( !is_text_presentation && (lc->count == 1 || lc->chars[1] != VS15) )\n    );\n}\n\nbool\nhas_cell_text(bool(*has_codepoint)(const void*, char_type ch), const void* face, bool do_debug, const ListOfChars *lc) {\n    RAII_ListOfChars(llc);\n    if (!has_codepoint(face, lc->chars[0])) goto not_found;\n    for (unsigned i = 1; i < lc->count; i++) {\n        if (!char_props_for(lc->chars[i]).is_non_rendered) {\n            ensure_space_for_chars(&llc, llc.count+1);\n            llc.chars[llc.count++] = lc->chars[i];\n        }\n    }\n    if (llc.count == 0) return true;\n    if (llc.count == 1) {\n        if (has_codepoint(face, llc.chars[0])) return true;\n        char_type ch = 0;\n        if (hb_unicode_compose(hb_unicode_funcs_get_default(), lc->chars[0], llc.chars[0], &ch) && face_has_codepoint(face, ch)) return true;\n        goto not_found;\n    }\n    for (unsigned i = 0; i < llc.count; i++) {\n        if (!has_codepoint(face, llc.chars[i])) goto not_found;\n    }\n    return true;\nnot_found:\n    if (do_debug) {\n        debug(\"The font chosen by the OS for the text: \");\n        debug(\"U+%x \", lc->chars[0]);\n        for (unsigned i = 1; i < lc->count; i++) {\n            if (lc->chars[i]) debug(\"U+%x \", lc->chars[i]);\n        }\n        debug(\"is \"); PyObject_Print((PyObject*)face, stderr, 0);\n        debug(\" but it does not actually contain glyphs for that text\\n\");\n    }\n    return false;\n}\n\nstatic void\noutput_cell_fallback_data(const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation, PyObject *face) {\n    debug(\"U+%x \", lc->chars[0]);\n    for (unsigned i = 1; i < lc->count; i++) debug(\"U+%x \", lc->chars[i]);\n    if (bold) debug(\"bold \");\n    if (italic) debug(\"italic \");\n    if (emoji_presentation) debug(\"emoji_presentation \");\n    if (PyLong_Check(face)) debug(\"using previous fallback font at index: \");\n    PyObject_Print(face, stderr, 0);\n    debug(\"\\n\");\n}\n\nPyObject*\niter_fallback_faces(FONTS_DATA_HANDLE fgh, ssize_t *idx) {\n    FontGroup *fg = (FontGroup*)fgh;\n    if (*idx + 1 < (ssize_t)fg->fallback_fonts_count) {\n        *idx += 1;\n        return fg->fonts[fg->first_fallback_font_idx + *idx].face;\n    }\n    return NULL;\n}\n\nstatic ssize_t\nload_fallback_font(FontGroup *fg, const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation) {\n    if (fg->fallback_fonts_count > 100) { log_error(\"Too many fallback fonts\"); return MISSING_FONT; }\n    ssize_t f;\n\n    if (bold) f = italic ? fg->bi_font_idx : fg->bold_font_idx;\n    else f = italic ? fg->italic_font_idx : fg->medium_font_idx;\n    if (f < 0) f = fg->medium_font_idx;\n\n    PyObject *face = create_fallback_face(fg->fonts[f].face, lc, bold, italic, emoji_presentation, (FONTS_DATA_HANDLE)fg);\n    if (face == NULL) { PyErr_Print(); return MISSING_FONT; }\n    if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }\n    if (global_state.debug_font_fallback) output_cell_fallback_data(lc, bold, italic, emoji_presentation, face);\n    if (PyLong_Check(face)) { ssize_t ans = fg->first_fallback_font_idx + PyLong_AsSsize_t(face); Py_DECREF(face); return ans; }\n    set_size_for_face(face, fg->fcm.cell_height, true, (FONTS_DATA_HANDLE)fg);\n\n    ensure_space_for(fg, fonts, Font, fg->fonts_count + 1, fonts_capacity, 5, true);\n    ssize_t ans = fg->first_fallback_font_idx + fg->fallback_fonts_count;\n    Font *af = &fg->fonts[ans];\n    if (!init_font(af, face, bold, italic, emoji_presentation)) fatal(\"Out of memory\");\n    Py_DECREF(face);\n    fg->fallback_fonts_count++;\n    fg->fonts_count++;\n    return ans;\n}\n\nstatic size_t\nchars_as_utf8(const ListOfChars *lc, char *buf, size_t bufsz, char_type zero_char) {\n    size_t n;\n    if (lc->count == 1) n = encode_utf8(lc->chars[0] ? lc->chars[0] : zero_char, buf);\n    else {\n        n = encode_utf8(lc->chars[0], buf);\n        if (lc->chars[0] != '\\t') for (unsigned i = 1; i < lc->count && n < bufsz - 4; i++) n += encode_utf8(lc->chars[i], buf + n);\n    }\n    buf[n] = 0;\n    return n;\n}\n\nstatic ssize_t\nfallback_font(FontGroup *fg, const CPUCell *cpu_cell, const GPUCell *gpu_cell, const ListOfChars *lc) {\n    bool bold = gpu_cell->attrs.bold;\n    bool italic = gpu_cell->attrs.italic;\n    bool emoji_presentation = has_emoji_presentation(cpu_cell, lc);\n    char style = emoji_presentation ? 'a' : 'A';\n    if (bold) style += italic ? 3 : 2; else style += italic ? 1 : 0;\n    char cell_text[4u * (MAX_NUM_CODEPOINTS_PER_CELL + 8u)] = {style};\n    const size_t cell_text_len = 1 + chars_as_utf8(lc, cell_text + 1, arraysz(cell_text) - 1, ' ');\n    fallback_font_map_t_itr fi = vt_get(&fg->fallback_font_map, cell_text);\n    if (!vt_is_end(fi)) return fi.data->val;\n    ssize_t idx = load_fallback_font(fg, lc, bold, italic, emoji_presentation);\n    const char *alloced_key = strndup(cell_text, cell_text_len);\n    if (alloced_key) vt_insert(&fg->fallback_font_map, alloced_key, idx);\n    return idx;\n}\n\nstatic ssize_t\nin_symbol_maps(FontGroup *fg, char_type ch) {\n    for (size_t i = 0; i < num_symbol_maps; i++) {\n        if (symbol_maps[i].left <= ch && ch <= symbol_maps[i].right) return fg->first_symbol_font_idx + symbol_maps[i].font_idx;\n    }\n    return NO_FONT;\n}\n\n\nstatic bool allow_use_of_box_fonts = true;\n\n// Decides which 'font' to use for a given cell.\n//\n// Possible results:\n// - NO_FONT\n// - MISSING_FONT\n// - BLANK_FONT\n// - BOX_FONT\n// - an index in the fonts list\nstatic ssize_t\nfont_for_cell(FontGroup *fg, const CPUCell *cpu_cell, const GPUCell *gpu_cell, bool *is_main_font, bool *is_emoji_presentation, TextCache *tc, ListOfChars *lc) {\n    *is_main_font = false;\n    *is_emoji_presentation = false;\n    text_in_cell(cpu_cell, tc, lc);\nSTART_ALLOW_CASE_RANGE\n    ssize_t ans;\n    switch(lc->chars[0]) {\n        case 0:\n        case '\\t':\n        case IMAGE_PLACEHOLDER_CHAR:\n            return BLANK_FONT;\n        case 0x2500 ... 0x2573:\n        case 0x2574 ... 0x259f:\n        case 0x25d6 ... 0x25d7:\n        case 0x25cb: case 0x25c9: case 0x25cf:\n        case 0x25dc ... 0x25e5:\n        case 0x2800 ... 0x28ff:\n        case 0xe0b0 ... 0xe0bf: case 0xe0d6 ... 0xe0d7:    // powerline box drawing\n        case 0xee00 ... 0xee0b:    // fira code progress bar/spinner\n        case 0x1fb00 ... 0x1fbae:  // symbols for legacy computing\n        case 0x1cd00 ... 0x1cde5: case 0x1fbe6: case 0x1fbe7:  // octants\n        case 0xf5d0 ... 0xf60d:    // branch drawing characters\n            if (allow_use_of_box_fonts) return BOX_FONT;\n            /* fallthrough */\n        default:\n            // Optimisation to avoid rendering spaces, except in the case of\n            // scaled multicells as the decorations there have to rendered\n            // scaled as well.\n            if (lc->count == 1 && (lc->chars[0] == ' ' || lc->chars[0] == 0x2002 /* en-space */) && (!cpu_cell->is_multicell || cpu_cell->scale == 1)) return BLANK_FONT;\n            *is_emoji_presentation = has_emoji_presentation(cpu_cell, lc);\n            ans = in_symbol_maps(fg, lc->chars[0]);\n            if (ans > -1) return ans;\n            switch(gpu_cell->attrs.bold | (gpu_cell->attrs.italic << 1)) {\n                case 0:\n                    ans = fg->medium_font_idx; break;\n                case 1:\n                    ans = fg->bold_font_idx ; break;\n                case 2:\n                    ans = fg->italic_font_idx; break;\n                case 3:\n                    ans = fg->bi_font_idx; break;\n            }\n            if (ans < 0) ans = fg->medium_font_idx;\n            if (!*is_emoji_presentation && has_cell_text((bool(*)(const void*, char_type))face_has_codepoint, (fg->fonts + ans)->face, false, lc)) { *is_main_font = true; return ans; }\n            return fallback_font(fg, cpu_cell, gpu_cell, lc);\n    }\nEND_ALLOW_CASE_RANGE\n}\n\n// Gives a unique (arbitrary) id to a box glyph\nstatic glyph_index\nbox_glyph_id(char_type ch) {\nSTART_ALLOW_CASE_RANGE\n    switch(ch) {\n        case 0x2500 ... 0x25ff:\n            return ch - 0x2500; // IDs from 0x00 to 0xff\n        case 0xe0b0 ... 0xee0b:\n            return 0x100 + ch - 0xe0b0;   // IDs from 0x100 to 0xe5b\n        case 0x2800 ... 0x28ff:\n            return 0xf00 + ch - 0x2800; // IDs from 0xf00 to 0xfff\n        case 0x1fb00 ... 0x1fbae:\n            return 0x1000 + ch - 0x1fb00; // IDs from 0x1000 to 0x10ae\n        case 0x1cd00 ... 0x1cde5:\n            return 0x1100 + ch - 0x1cd00; // IDs from 0x1100 to 0x11e5\n        case 0x1fbe6: case 0x1fbe7: return 0x11e6 + ch - 0x1fbe6;\n        case 0xf5d0 ... 0xf60d:\n            return 0x2000 + ch - 0xf5d0; // IDs from 0x2000 to 0x203d\n        default:\n            return 0xffff;\n    }\nEND_ALLOW_CASE_RANGE\n}\n\nstatic PyObject *descriptor_for_idx = NULL;\n\nvoid\nrender_alpha_mask(const uint8_t *alpha_mask, pixel* dest, const Region *src_rect, const Region *dest_rect, size_t src_stride, size_t dest_stride, pixel color_rgb) {\n    pixel col = (color_rgb << 8) & 0xffffff00;\n    for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {\n        pixel *d = dest + dest_stride * dr;\n        const uint8_t *s = alpha_mask + src_stride * sr;\n        for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {\n            uint8_t src_alpha = d[dc] & 0xff;\n            uint8_t alpha = s[sc];\n            d[dc] = col | MAX(alpha, src_alpha);\n        }\n    }\n}\n\ntypedef struct GlyphRenderScratch {\n    SpritePosition* *sprite_positions;\n    glyph_index *glyphs;\n    size_t sz;\n    ListOfChars *lc;\n} GlyphRenderScratch;\nstatic GlyphRenderScratch global_glyph_render_scratch = {0};\n\nstatic void\nensure_glyph_render_scratch_space(size_t sz) {\n#define a global_glyph_render_scratch\n    sz += 16;\n    if (a.sz < sz) {\n        free(a.glyphs); a.glyphs = malloc(sz * sizeof(a.glyphs[0])); if (!a.glyphs) fatal(\"Out of memory\");\n        free(a.sprite_positions); a.sprite_positions = malloc(sz * sizeof(SpritePosition*)); if (!a.sprite_positions) fatal(\"Out of memory\");\n        a.sz = sz;\n        if (!a.lc) {\n            a.lc = alloc_list_of_chars();\n            if (!a.lc) fatal(\"Out of memory\");\n        }\n    }\n#undef a\n}\n\nstatic float\neffective_scale(RunFont rf) {\n    float ans = MAX(1u, rf.scale);\n    if (rf.subscale_n && rf.subscale_d && rf.subscale_n < rf.subscale_d) {\n        ans *= ((float)rf.subscale_n) / rf.subscale_d;\n    }\n    return ans;\n}\n\nstatic float\nscaled_cell_dimensions(RunFont rf, unsigned *width, unsigned *height) {\n    float frac = MAX(effective_scale(rf), MIN(4.f, (float)*width) / *width);\n    *width = (unsigned)ceilf(frac * *width);\n    *height = (unsigned)ceilf(frac * *height);\n    return frac;\n}\n\nstatic float\napply_scale_to_font_group(FontGroup *fg, RunFont *rf) {\n    unsigned int scaled_cell_width = fg->fcm.cell_width, scaled_cell_height = fg->fcm.cell_height;\n    float scale = rf ? scaled_cell_dimensions(*rf, &scaled_cell_width, &scaled_cell_height) : 1.f;\n    scaled_font_map_t_itr i = vt_get(&fg->scaled_font_map, scale);\n    ScaledFontData sfd;\n#define apply_scaling(which_fg) if (!face_apply_scaling(medium_font->face, (FONTS_DATA_HANDLE)(which_fg))) { \\\n            if (PyErr_Occurred()) PyErr_Print(); \\\n            fatal(\"Could not apply scale of %f to font group at size: %f\", scale, (which_fg)->font_sz_in_pts); \\\n        }\n\n    if (vt_is_end(i)) {\n        Font *medium_font = &fg->fonts[fg->medium_font_idx];\n        FontGroup copy = {.fcm=fg->fcm, .logical_dpi_x=fg->logical_dpi_x, .logical_dpi_y=fg->logical_dpi_y};\n        copy.fcm.cell_width = scaled_cell_width; copy.fcm.cell_height = scaled_cell_height;\n        copy.font_sz_in_pts = scale * fg->font_sz_in_pts;\n        apply_scaling(&copy);\n        calc_cell_metrics(&copy, medium_font->face);\n        if (copy.fcm.cell_width > scaled_cell_width || copy.fcm.cell_height > scaled_cell_height) {\n            float wfrac = (float)copy.fcm.cell_width / scaled_cell_width, hfrac = (float)copy.fcm.cell_height / scaled_cell_height;\n            float frac = MIN(wfrac, hfrac);\n            copy.font_sz_in_pts *= frac;\n            while (true) {\n                apply_scaling(&copy);\n                calc_cell_metrics(&copy, medium_font->face);\n                if (copy.fcm.cell_width <= scaled_cell_width && copy.fcm.cell_height <= scaled_cell_height) break;\n                if (copy.font_sz_in_pts <= 1) break;\n                copy.font_sz_in_pts -= 0.1;\n            }\n        }\n        sfd.fcm = copy.fcm; sfd.font_sz_in_pts = copy.font_sz_in_pts;\n        sfd.fcm.cell_width = scaled_cell_width; sfd.fcm.cell_height = scaled_cell_height;\n        if (vt_is_end(vt_insert(&fg->scaled_font_map, scale, sfd))) fatal(\"Out of memory inserting scaled font data into map\");\n        apply_scaling(fg);\n    } else sfd = i.data->val;\n    fg->font_sz_in_pts = sfd.font_sz_in_pts;\n    fg->fcm = sfd.fcm;\n    return scale;\n#undef apply_scaling\n}\n\nstatic pixel*\npointer_to_space_for_last_sprite(Canvas *canvas, FontCellMetrics fcm, unsigned *sz) {\n    *sz = fcm.cell_width * (fcm.cell_height + 1);\n    return canvas->buf + (canvas->size_in_bytes / sizeof(canvas->buf[0]) - *sz);\n}\n\nstatic pixel*\nextract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells) {\n    unsigned sz;\n    pixel *ans = pointer_to_space_for_last_sprite(&fg->canvas, fg->fcm, &sz);\n    pixel *dest = ans, *src = fg->canvas.buf + (i * fg->fcm.cell_width);\n    unsigned int stride = fg->fcm.cell_width * num_cells;\n    for (unsigned int r = 0; r < fg->fcm.cell_height; r++, dest += fg->fcm.cell_width, src += stride) memcpy(dest, src, fg->fcm.cell_width * sizeof(fg->canvas.buf[0]));\n    memset(ans + sz - fg->fcm.cell_width, 0, fg->fcm.cell_width * sizeof(ans[0]));  // underline_exclusion\n    return ans;\n}\n\nstatic void\ncalculate_regions_for_line(RunFont rf, unsigned cell_height, Region *src, Region *dest) {\n    unsigned src_height = src->bottom;\n    Region src_in_full_coords = *src; unsigned full_dest_height = cell_height * rf.scale;\n    if (rf.subscale_n && rf.subscale_d) {\n        switch(rf.align.vertical) {\n            case 0: break; // top aligned no change\n            case 1: // bottom aligned\n                src_in_full_coords.top = full_dest_height - src_height;\n                src_in_full_coords.bottom = full_dest_height;\n                break;\n            case 2: // centered\n                src_in_full_coords.top = (full_dest_height - src_height) / 2;\n                src_in_full_coords.bottom = src_in_full_coords.top + src_height;\n                break;\n        }\n    }\n    Region dest_in_full_coords = {.top = rf.multicell_y * cell_height, .bottom = (rf.multicell_y + 1) * cell_height};\n    unsigned intersection_top = MAX(src_in_full_coords.top, dest_in_full_coords.top);\n    unsigned intersection_bottom = MIN(src_in_full_coords.bottom, dest_in_full_coords.bottom);\n    unsigned src_top_delta = intersection_top - src_in_full_coords.top, src_bottom_delta = src_in_full_coords.bottom - intersection_bottom;\n    src->top += src_top_delta; src->bottom = src->bottom > src_bottom_delta ? src->bottom - src_bottom_delta : 0;\n    unsigned dest_top_delta = intersection_top - dest_in_full_coords.top, dest_bottom_delta = dest_in_full_coords.bottom - intersection_bottom;\n    dest->top = dest_top_delta; dest->bottom = cell_height > dest_bottom_delta ? cell_height - dest_bottom_delta : 0;\n}\n\nstatic pixel*\nextract_cell_region(Canvas *canvas, unsigned i, Region *src, const Region *dest, unsigned src_width, FontCellMetrics unscaled_metrics) {\n    src->left = i * unscaled_metrics.cell_width; src->right = MIN(src_width, src->left + unscaled_metrics.cell_width);\n    unsigned sz;\n    pixel *ans = pointer_to_space_for_last_sprite(canvas, unscaled_metrics, &sz);\n    memset(ans, 0, sz * sizeof(ans[0]));\n    unsigned width = MIN(src->right - src->left, unscaled_metrics.cell_width);\n    for (unsigned srcy = src->top, desty = dest->top; srcy < src->bottom && desty < dest->bottom; srcy++, desty++) {\n        pixel *srcp = canvas->buf + srcy * src_width, *destp = ans + desty * unscaled_metrics.cell_width;\n        memcpy(destp, srcp + src->left, width * sizeof(destp[0]));\n    }\n    return ans;\n}\n\nstatic void\nset_cell_sprite(GPUCell *cell, const SpritePosition *sp) {\n    cell->sprite_idx = sp->idx & 0x7fffffff;\n    if (sp->colored) cell->sprite_idx |= 0x80000000;\n}\n\nstatic Region\nmap_scaled_decoration_geometry(DecorationGeometry sdg, Region src, Region dest) {\n    unsigned scaled_top = MAX(sdg.top, src.top), scaled_bottom = MIN(sdg.top + sdg.height, src.bottom);\n    unsigned unscaled_top = dest.top + (scaled_top - src.top);\n    unsigned unscaled_bottom = unscaled_top + (scaled_bottom > scaled_top ? scaled_bottom - scaled_top : 0);\n    unscaled_bottom = MIN(unscaled_bottom, dest.bottom);\n    /*printf(\"src: (%u, %u) dest: (%u, %u) sdg: (%u, %u) scaled: (%u, %u) unscaled: (%u, %u)\\n\",*/\n    /*    src.top, src.bottom, dest.top, dest.bottom, sdg.top, sdg.top + sdg.height, scaled_top, scaled_bottom, unscaled_top, unscaled_bottom);*/\n    return (Region){.top=unscaled_top, .bottom=MAX(unscaled_top, unscaled_bottom)};\n}\n\nstatic void\nrender_scaled_decoration(FontCellMetrics unscaled_metrics, FontCellMetrics scaled_metrics, uint8_t *alpha_mask, pixel *output, Region src, Region dest) {\n    memset(output, 0, sizeof(output[0]) * unscaled_metrics.cell_width * (unscaled_metrics.cell_height + 1));\n    unsigned src_limit = MIN(scaled_metrics.cell_height, src.bottom), dest_limit = MIN(unscaled_metrics.cell_height, dest.bottom);\n    unsigned cell_width = MIN(scaled_metrics.cell_width, unscaled_metrics.cell_width);\n    for (unsigned srcy = src.top, desty=dest.top; srcy < src_limit && desty < dest_limit; srcy++, desty++) {\n        uint8_t *srcp = alpha_mask + cell_width * srcy;\n        pixel *destp = output + cell_width * desty;\n        for (unsigned x = 0; x < cell_width; x++) destp[x] = 0xffffff00 | srcp[x];\n    }\n}\n\nstatic sprite_index\nrender_decorations(FontGroup *fg, Region src, Region dest, FontCellMetrics scaled_metrics, DecorationGeometry *underline_region) {\n    *underline_region = (DecorationGeometry){0};\n    if ((src.bottom == src.top) || (dest.bottom == dest.top)) return 0;   // no overlap\n    const FontCellMetrics unscaled_metrics = fg->fcm;\n    scaled_metrics.cell_width = unscaled_metrics.cell_width;\n    RAII_ALLOC(uint8_t, alpha_mask, malloc((size_t)scaled_metrics.cell_height * scaled_metrics.cell_width));\n    RAII_ALLOC(pixel, buf, malloc(sizeof(pixel) * unscaled_metrics.cell_width * (unscaled_metrics.cell_height + 1)));\n    if (!alpha_mask || !buf) fatal(\"Out of memory\");\n    sprite_index ans = 0;\n    bool is_underline = false; uint32_t underline_top = unscaled_metrics.cell_height, underline_bottom = 0;\n#define do_one(call) { \\\n    memset(alpha_mask, 0, sizeof(alpha_mask[0]) * scaled_metrics.cell_width * scaled_metrics.cell_height); \\\n    DecorationGeometry sdg = call; \\\n    render_scaled_decoration(unscaled_metrics, scaled_metrics, alpha_mask, buf, src, dest); \\\n    sprite_index q = current_send_sprite_to_gpu(fg, buf, (DecorationMetadata){0}, scaled_metrics); \\\n    if (!ans) ans = q; \\\n    if (is_underline) { \\\n        Region r = map_scaled_decoration_geometry(sdg, src, dest); \\\n        if (r.top < underline_top) underline_top = r.top; \\\n        if (r.bottom > underline_bottom) underline_bottom = r.bottom; \\\n    }; \\\n}\n\n    do_one(add_strikethrough(alpha_mask, scaled_metrics));\n    is_underline = true;\n    do_one(add_straight_underline(alpha_mask, scaled_metrics));\n    do_one(add_double_underline(alpha_mask, scaled_metrics));\n    do_one(add_curl_underline(alpha_mask, scaled_metrics));\n    do_one(add_dotted_underline(alpha_mask, scaled_metrics));\n    do_one(add_dashed_underline(alpha_mask, scaled_metrics));\n\n    underline_bottom = MIN(underline_bottom, unscaled_metrics.cell_height);\n    if (underline_top < underline_bottom) {\n        underline_region->top = underline_top;\n        underline_region->height = underline_bottom - underline_top;\n    }\n    return ans;\n#undef do_one\n}\n\nstatic DecorationMetadata\nindex_for_decorations(FontGroup *fg, RunFont rf, Region src, Region dest, FontCellMetrics scaled_metrics) {\n    const DecorationsKey key = {.scale=rf.scale, .subscale_n = rf.subscale_n, .subscale_d = rf.subscale_d, .align = rf.align.val, .multicell_y = rf.multicell_y, .u1 = 0, .u2 = 0, .u3 = 0 };\n    decorations_index_map_t_itr i = vt_get(&fg->decorations_index_map, key);\n    if (!vt_is_end(i)) return i.data->val;\n    DecorationMetadata val;\n    val.start_idx = render_decorations(fg, src, dest, scaled_metrics, &val.underline_region);\n    if (vt_is_end(vt_insert(&fg->decorations_index_map, key, val))) fatal(\"Out of memory\");\n    return val;\n}\n\nstatic void\nrender_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell, const TextCache *tc) {\n    ensure_glyph_render_scratch_space(64);\n    text_in_cell(cpu_cell, tc, global_glyph_render_scratch.lc);\n    ensure_glyph_render_scratch_space(rf.scale * global_glyph_render_scratch.lc->count);\n    unsigned num_glyphs = 0, num_cells = rf.scale;\n    for (unsigned i = 0; i < global_glyph_render_scratch.lc->count; i++) {\n        glyph_index glyph = box_glyph_id(global_glyph_render_scratch.lc->chars[i]);\n        if (glyph != 0xffff) global_glyph_render_scratch.glyphs[num_glyphs++] = glyph;\n        else global_glyph_render_scratch.lc->chars[i] = 0;\n    }\n#define failed {\\\n    if (PyErr_Occurred()) PyErr_Print(); \\\n    for (unsigned i = 0; i < num_cells; i++) gpu_cell[i].sprite_idx = 0; \\\n    return; \\\n}\n    if (!num_glyphs) failed;\n    bool all_rendered = true;\n#define sp global_glyph_render_scratch.sprite_positions\n    for (unsigned ligature_index = 0; ligature_index < num_cells; ligature_index++) {\n        sp[ligature_index] = sprite_position_for(fg, rf, global_glyph_render_scratch.glyphs, num_glyphs, ligature_index, num_cells);\n        if (sp[ligature_index] == NULL) failed;\n        sp[ligature_index]->colored = false;\n        if (!sp[ligature_index]->rendered) all_rendered = false;\n    }\n    if (all_rendered) {\n        for (unsigned i = 0; i < num_cells; i++) set_cell_sprite(gpu_cell + i, sp[i]);\n        return;\n    }\n    FontCellMetrics unscaled_metrics = fg->fcm;\n    float scale = apply_scale_to_font_group(fg, &rf);\n    ensure_canvas_can_fit(fg, num_glyphs + 1, rf.scale);\n    FontCellMetrics scaled_metrics = fg->fcm;\n    if (scale != 1) apply_scale_to_font_group(fg, NULL);\n    ensure_canvas_can_fit(fg, num_glyphs + 1, rf.scale);  // in case unscaled size is larger is than scaled size\n    unsigned mask_stride = scaled_metrics.cell_width * num_glyphs, right_shift = 0;\n    if (rf.subscale_n && rf.subscale_d && rf.align.horizontal && scaled_metrics.cell_width <= unscaled_metrics.cell_width) {\n        int delta = unscaled_metrics.cell_width * num_cells - mask_stride;\n        if (rf.align.horizontal == 2) delta /= 2;\n        if (delta > 0) {\n            right_shift = delta;\n            mask_stride += delta;\n        }\n    }\n    Region src = {.right = scaled_metrics.cell_width, .bottom = scaled_metrics.cell_height }, dest = src;\n    for (unsigned i = 0, cnum = 0; i < num_glyphs; i++) {\n        unsigned int ch = global_glyph_render_scratch.lc->chars[cnum++];\n        while (!ch) ch = global_glyph_render_scratch.lc->chars[cnum++];\n        render_box_char(ch, fg->canvas.alpha_mask, src.right, src.bottom, fg->logical_dpi_x, fg->logical_dpi_y, scale);\n        dest.left = i * scaled_metrics.cell_width + right_shift; dest.right = dest.left + scaled_metrics.cell_width;\n        render_alpha_mask(fg->canvas.alpha_mask, fg->canvas.buf, &src, &dest, src.right, mask_stride, 0xffffff);\n    }\n    src.right = mask_stride; dest = src; dest.right = unscaled_metrics.cell_width * num_cells;\n    /*printf(\"Rendered char sz: (%u, %u)\\n\", src.right, src.bottom); dump_sprite(fg->canvas.buf, src.right, src.bottom);*/\n    calculate_regions_for_line(rf, unscaled_metrics.cell_height, &src, &dest);\n    DecorationMetadata dm = index_for_decorations(fg, rf, src, dest, scaled_metrics);\n    /*printf(\"width: %u height: %u unscaled_cell_width: %u unscaled_cell_height: %u src.top: %u src.bottom: %u num_cells: %u\\n\", width, height, fg->fcm.cell_width, fg->fcm.cell_height, src.top, src.bottom, num_cells);*/\n    for (unsigned i = 0; i < num_cells; i++) {\n        if (!sp[i]->rendered) {\n            pixel *b = extract_cell_region(&fg->canvas, i, &src, &dest, mask_stride, unscaled_metrics);\n            /*printf(\"cell %u src -> dest: (%u %u) -> (%u %u)\\n\", i, src.left, src.right, dest.left, dest.right);*/\n            sp[i]->idx = current_send_sprite_to_gpu(fg, b, dm, scaled_metrics);\n            if (!sp[i]->idx) failed;\n            /*dump_sprite(b, unscaled_metrics.cell_width, unscaled_metrics.cell_height);*/\n            sp[i]->rendered = true; sp[i]->colored = false;\n        }\n        set_cell_sprite(gpu_cell + i, sp[i]);\n        /*printf(\"Sprite %u: pos: %u sz: (%u, %u)\\n\", i, sp[i]->idx, fg->fcm.cell_width, fg->fcm.cell_height); dump_sprite(b, fg->fcm.cell_width, fg->fcm.cell_height);*/\n    }\n#undef sp\n#undef failed\n}\n\nstatic void\nload_hb_buffer(CPUCell *first_cpu_cell, index_type num_cells, const TextCache *tc, ListOfChars *lc) {\n    size_t num = 0;\n    hb_buffer_clear_contents(harfbuzz_buffer);\n    // Although hb_buffer_add_codepoints is supposedly an append, we have to\n    // add all text in one call otherwise it breaks shaping, presumably because\n    // of context??\n    for (; num_cells; first_cpu_cell++, num_cells--) {\n        if (first_cpu_cell->is_multicell && first_cpu_cell->x) continue;\n        text_in_cell(first_cpu_cell, tc, lc);\n        ensure_space_for((&shape_buffer), codepoints, shape_buffer.codepoints[0], lc->count + num, capacity, 512, false);\n        memcpy(shape_buffer.codepoints + num, lc->chars, lc->count * sizeof(shape_buffer.codepoints[0]));\n        num += lc->count;\n    }\n    hb_buffer_add_codepoints(harfbuzz_buffer, shape_buffer.codepoints, num, 0, num);\n    hb_buffer_guess_segment_properties(harfbuzz_buffer);\n    if (OPT(force_ltr)) hb_buffer_set_direction(harfbuzz_buffer, HB_DIRECTION_LTR);\n}\n\n\nstatic void\nrender_filled_sprite(pixel *buf, unsigned num_glyphs, FontCellMetrics scaled_metrics, unsigned num_scaled_cells) {\n    if (num_scaled_cells > num_glyphs) {\n        memset(buf, 0xff, sizeof(buf[0]) * num_glyphs * scaled_metrics.cell_width);\n        memset(buf + num_glyphs * scaled_metrics.cell_width, 0, sizeof(buf[0]) * (num_scaled_cells - num_glyphs) * scaled_metrics.cell_width);\n        for (unsigned y = 1; y < scaled_metrics.cell_height; y++) memcpy(\n            buf + scaled_metrics.cell_width * num_scaled_cells * y, buf, sizeof(buf[0]) * scaled_metrics.cell_width * num_scaled_cells );\n    } else memset(buf, 0xff, sizeof(buf[0]) * num_glyphs * scaled_metrics.cell_height * scaled_metrics.cell_width );\n}\n\nstatic void\napply_horizontal_alignment(pixel *canvas, RunFont rf, bool center_glyph, GlyphRenderInfo ri, unsigned max_render_width, unsigned canvas_height, unsigned num_cells, unsigned num_glyphs, bool was_colored) {\n    int delta = 0;\n#ifdef __APPLE__\n    if (num_cells == 2 && was_colored) center_glyph = true;\n#else\n    (void)was_colored;\n#endif\n    if (rf.subscale_n && rf.subscale_d && rf.align.horizontal && rf.subscale_n < rf.subscale_d) {\n        delta = max_render_width - ri.rendered_width;\n        if (rf.align.horizontal == 2) delta /= 2;\n    } else if (center_glyph && num_glyphs && num_cells > 1 && ri.rendered_width < max_render_width) {\n        unsigned half = (max_render_width - ri.rendered_width) / 2;\n        if (half > 1) delta = half;\n    }\n    delta -= ri.x;\n    if (delta > 0) right_shift_canvas(canvas, ri.canvas_width, canvas_height, delta);\n}\n\n\nstatic void\nrender_group(\n    FontGroup *fg, unsigned num_cells, unsigned num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells,\n    hb_glyph_info_t *info, hb_glyph_position_t *positions, RunFont rf, glyph_index *glyphs, unsigned glyph_count,\n    bool center_glyph, const TextCache *tc, float scale, FontCellMetrics unscaled_metrics\n) {\n#define sp global_glyph_render_scratch.sprite_positions\n    const FontCellMetrics scaled_metrics = fg->fcm;\n    const Font *font = fg->fonts + rf.font_idx;\n    bool all_rendered = true;\n\n    unsigned num_scaled_cells = MAX(1u, (unsigned)ceil(num_cells / scale));\n    const unsigned canvas_width = num_cells * unscaled_metrics.cell_width;\n    const bool rendering_in_smaller_area = rf.subscale_n < rf.subscale_d;\n    if (rendering_in_smaller_area) {\n        // scw might be 1 px less than scaled_metrics.cell_width because of rounding, but it is the correct value\n        // to use to determine the num of scaled cells\n        unsigned scw = (unsigned)(unscaled_metrics.cell_width * scale);\n        num_scaled_cells = num_cells * unscaled_metrics.cell_width / scw;\n    }\n    unsigned scaled_canvas_width = num_scaled_cells * scaled_metrics.cell_width;\n\n#define failed { \\\n    if (PyErr_Occurred()) PyErr_Print(); \\\n    for (unsigned i = 0; i < num_cells; i++) gpu_cells[i].sprite_idx = 0; \\\n    return; \\\n}\n\n    // One can have infinite ligatures with repeated groups of sprites when scaled size is an exact multiple or\n    // divisor of unscaled size but I cant be bothered to implement that.\n    const bool is_infinite_ligature = num_cells == num_scaled_cells && num_cells > 9 && num_glyphs == num_cells;\n    for (unsigned i = 0, ligature_index = 0; i < num_cells; i++) {\n        bool is_repeat_sprite = is_infinite_ligature && i > 1 && i + 1 < num_glyphs && glyphs[i] == glyphs[i-1] && glyphs[i] == glyphs[i-2] && glyphs[i] == glyphs[i+1];\n        sp[i] = is_repeat_sprite ? sp[i-1] : sprite_position_for(fg, rf, glyphs, glyph_count, ligature_index++, num_cells);\n        if (!sp[i]) failed;\n        if (!sp[i]->rendered) all_rendered = false;\n    }\n    if (all_rendered) {\n        for (unsigned i = 0; i < num_cells; i++) set_cell_sprite(gpu_cells + i, sp[i]);\n        return;\n    }\n\n    ensure_canvas_can_fit(fg, MAX(num_cells, num_scaled_cells) + 1, rf.scale);\n    if (rendering_in_smaller_area) ensure_canvas_can_fit(fg, 2 * num_cells + 1, (unsigned)ceil(scale));  // scratch space\n    pixel *scratch = fg->canvas.buf + canvas_width * unscaled_metrics.cell_height;\n    text_in_cell(cpu_cells, tc, global_glyph_render_scratch.lc);\n    bool is_only_filled_boxes = false;\n    if (global_glyph_render_scratch.lc->chars[0] == 0x2588) {\n        glyph_index box_glyph_id = global_glyph_render_scratch.glyphs[0];\n        is_only_filled_boxes = true;\n        for (unsigned i = 1; i < num_glyphs && is_only_filled_boxes; i++) if (global_glyph_render_scratch.glyphs[i] != box_glyph_id) is_only_filled_boxes = false;\n    }\n    bool was_colored = !is_only_filled_boxes && has_emoji_presentation(cpu_cells, global_glyph_render_scratch.lc);\n    GlyphRenderInfo ri = {0};\n    pixel *canvas = rendering_in_smaller_area && canvas_width != scaled_canvas_width ? scratch : fg->canvas.buf;\n    if (is_only_filled_boxes) { // special case rendering of █ for tests\n        render_filled_sprite(canvas, num_glyphs, scaled_metrics, num_scaled_cells);\n        ri.canvas_width = canvas_width; ri.rendered_width = num_glyphs * scaled_metrics.cell_width;\n        // dump_sprite(canvas, scaled_metrics.cell_width * num_scaled_cells, scaled_metrics.cell_height);\n    } else {\n        render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, canvas, scaled_metrics.cell_width, scaled_metrics.cell_height, num_scaled_cells, scaled_metrics.baseline, &was_colored, (FONTS_DATA_HANDLE)fg, &ri);\n        ri.rendered_width = MIN(ri.rendered_width, ri.canvas_width);\n    }\n    // printf(\"num_cells: %u num_scaled_cells: %u num_glyphs: %u scale: %f unscaled: %ux%u scaled: %ux%u rendered_width: %d\\n\", num_cells, num_scaled_cells, num_glyphs, scale, unscaled_metrics.cell_width, unscaled_metrics.cell_height, scaled_metrics.cell_width, scaled_metrics.cell_height, ri.rendered_width);\n    if (canvas == scratch) {\n        if (canvas_width != scaled_canvas_width) {\n            unsigned stride = MIN(canvas_width, scaled_canvas_width);\n            for (unsigned y = 0; y < scaled_metrics.cell_height; y++) {\n                pixel *dest_row = fg->canvas.buf + y * canvas_width;\n                memcpy(dest_row, canvas + y * scaled_canvas_width, sizeof(pixel) * stride);\n                if (scaled_canvas_width < canvas_width) memset(\n                    dest_row + scaled_canvas_width, 0, sizeof(pixel) * (canvas_width - scaled_canvas_width));\n            }\n            ri.canvas_width = canvas_width;\n            scaled_canvas_width = canvas_width;\n        } else memcpy(fg->canvas.buf, canvas, sizeof(pixel) * canvas_width * scaled_metrics.cell_height);\n        canvas = fg->canvas.buf;\n    }\n    apply_horizontal_alignment(\n        canvas, rf, center_glyph, ri, canvas_width,\n        scaled_metrics.cell_height, num_scaled_cells, num_glyphs, was_colored);\n    if (PyErr_Occurred()) PyErr_Print();\n    // display_glyph(canvas, canvas_width, scaled_metrics.cell_height); printf(\"\\n\");\n\n    fg->fcm = unscaled_metrics;  // needed for current_send_sprite_to_gpu()\n\n    if (num_cells == num_scaled_cells && scale == 1.f && !rendering_in_smaller_area) {\n        Region src = {.bottom=unscaled_metrics.cell_height, .right=unscaled_metrics.cell_width}, dest = src;\n        DecorationMetadata dm = index_for_decorations(fg, rf, src, dest, scaled_metrics);\n        for (unsigned i = 0; i < num_cells; i++) {\n            if (!sp[i]->rendered) {\n                bool is_repeat_sprite = is_infinite_ligature && i > 0 && sp[i]->idx == sp[i-1]->idx;\n                if (!is_repeat_sprite) {\n                    pixel *b = num_cells == 1 ? canvas : extract_cell_from_canvas(fg, i, num_cells);\n                    sp[i]->idx = current_send_sprite_to_gpu(fg, b, dm, scaled_metrics);\n                    if (!sp[i]->idx) failed;\n                } else sp[i]->idx = sp[i-1]->idx;\n                sp[i]->rendered = true; sp[i]->colored = was_colored;\n            }\n            set_cell_sprite(gpu_cells + i, sp[i]);\n        }\n    } else {\n        Region src={.bottom=scaled_metrics.cell_height, .right=scaled_metrics.cell_width * num_scaled_cells}, dest={.right=unscaled_metrics.cell_width};\n        calculate_regions_for_line(rf, unscaled_metrics.cell_height, &src, &dest);\n        DecorationMetadata dm = index_for_decorations(fg, rf, src, dest, scaled_metrics);\n        if (rendering_in_smaller_area) src.right = unscaled_metrics.cell_width;\n        // printf(\"line: %u src -> dest: (%u %u) -> (%u %u)\\n\", rf.multicell_y, src.top, src.bottom, dest.top, dest.bottom);\n        for (unsigned i = 0; i < num_cells; i++) {\n            if (!sp[i]->rendered) {\n                pixel *b = extract_cell_region(\n                    &fg->canvas, i, &src, &dest, scaled_canvas_width, unscaled_metrics);\n                /*printf(\"cell %u src -> dest: (%u %u) -> (%u %u)\\n\", i, src.left, src.right, dest.left, dest.right);*/\n                sp[i]->idx = current_send_sprite_to_gpu(fg, b, dm, scaled_metrics);\n                if (!sp[i]->idx) failed;\n                /*dump_sprite(b, unscaled_metrics.cell_width, unscaled_metrics.cell_height);*/\n                sp[i]->rendered = true; sp[i]->colored = was_colored;\n            }\n            set_cell_sprite(gpu_cells + i, sp[i]);\n        }\n    }\n    fg->fcm = scaled_metrics;\n#undef sp\n#undef failed\n}\n\ntypedef struct {\n    CPUCell *cpu_cell;\n    GPUCell *gpu_cell;\n    unsigned int num_codepoints;\n    unsigned int codepoints_consumed;\n    char_type current_codepoint;\n} CellData;\n\ntypedef struct {\n    unsigned int first_glyph_idx, first_cell_idx, num_glyphs, num_cells;\n    bool has_special_glyph, started_with_infinite_ligature;\n} Group;\n\ntypedef struct {\n    uint32_t previous_cluster;\n    bool prev_was_special, prev_was_empty;\n    CellData current_cell_data;\n    Group *groups;\n    size_t groups_capacity, group_idx, glyph_idx, cell_idx, num_cells, num_glyphs;\n    CPUCell *first_cpu_cell, *last_cpu_cell;\n    GPUCell *first_gpu_cell, *last_gpu_cell;\n    hb_glyph_info_t *info;\n    hb_glyph_position_t *positions;\n} GroupState;\n\nstatic GroupState group_state = {0};\n\nstatic void\nshape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb_font_t *font, Font *fobj, bool disable_ligature, const TextCache *tc) {\n    if (group_state.groups_capacity <= 2 * num_cells) {\n        group_state.groups_capacity = MAX(128u, 2 * num_cells);  // avoid unnecessary reallocs\n        group_state.groups = realloc(group_state.groups, sizeof(Group) * group_state.groups_capacity);\n        if (!group_state.groups) fatal(\"Out of memory\");\n    }\n    RAII_ListOfChars(lc);\n    text_in_cell(first_cpu_cell, tc, &lc);\n    group_state.previous_cluster = UINT32_MAX;\n    group_state.prev_was_special = false;\n    group_state.prev_was_empty = false;\n    group_state.current_cell_data.cpu_cell = first_cpu_cell;\n    group_state.current_cell_data.gpu_cell = first_gpu_cell;\n    group_state.current_cell_data.num_codepoints = MAX(1u, lc.count);\n    group_state.current_cell_data.codepoints_consumed = 0;\n    group_state.current_cell_data.current_codepoint = lc.chars[0];\n    zero_at_ptr_count(group_state.groups, group_state.groups_capacity);\n    group_state.group_idx = 0;\n    group_state.glyph_idx = 0;\n    group_state.cell_idx = 0;\n    group_state.num_cells = num_cells;\n    group_state.first_cpu_cell = first_cpu_cell;\n    group_state.first_gpu_cell = first_gpu_cell;\n    group_state.last_cpu_cell = first_cpu_cell + (num_cells ? num_cells - 1 : 0);\n    group_state.last_gpu_cell = first_gpu_cell + (num_cells ? num_cells - 1 : 0);\n    load_hb_buffer(first_cpu_cell, num_cells, tc, &lc);\n\n    size_t num_features = fobj->num_ffs_hb_features;\n    if (num_features && !disable_ligature) num_features--;  // the last feature is always -calt\n    hb_shape(font, harfbuzz_buffer, fobj->ffs_hb_features, num_features);\n\n    unsigned int info_length, positions_length;\n    group_state.info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_length);\n    group_state.positions = hb_buffer_get_glyph_positions(harfbuzz_buffer, &positions_length);\n    if (!group_state.info || !group_state.positions) group_state.num_glyphs = 0;\n    else group_state.num_glyphs = MIN(info_length, positions_length);\n}\n\nstatic bool\nis_special_glyph(glyph_index glyph_id, Font *font, CellData* cell_data) {\n    // A glyph is special if the codepoint it corresponds to matches a\n    // different glyph in the font\n    GlyphProperties s = find_glyph_properties(font->glyph_properties_hash_table, glyph_id);\n    if (!s.special_set) {\n        bool is_special = cell_data->current_codepoint ? (\n            glyph_id != glyph_id_for_codepoint(font->face, cell_data->current_codepoint) ? true : false)\n            :\n            false;\n        s.special_set = 1; s.special_val = is_special;\n        set_glyph_properties(font->glyph_properties_hash_table, glyph_id, s);\n    }\n    return s.special_val;\n}\n\nstatic bool\nis_empty_glyph(glyph_index glyph_id, Font *font) {\n    // A glyph is empty if its metrics have a width of zero\n    GlyphProperties s = find_glyph_properties(font->glyph_properties_hash_table, glyph_id);\n    if (!s.empty_set) {\n        s.empty_val = is_glyph_empty(font->face, glyph_id) ? 1 : 0;\n        s.empty_set = 1;\n        set_glyph_properties(font->glyph_properties_hash_table, glyph_id, s);\n    }\n    return s.empty_val;\n}\n\nstatic unsigned int\ncheck_cell_consumed(CellData *cell_data, CPUCell *last_cpu_cell, const TextCache *tc, ListOfChars *lc) {\n    cell_data->codepoints_consumed++;\n    if (cell_data->codepoints_consumed >= cell_data->num_codepoints) {\n        uint16_t width = 1;\n        if (cell_data->cpu_cell->is_multicell) width = cell_data->cpu_cell->width * cell_data->cpu_cell->scale;\n        cell_data->cpu_cell += width;\n        cell_data->gpu_cell += width;\n        cell_data->codepoints_consumed = 0;\n        if (cell_data->cpu_cell <= last_cpu_cell) {\n            text_in_cell(cell_data->cpu_cell, tc, lc);\n            cell_data->num_codepoints = lc->count;\n            cell_data->current_codepoint = lc->chars[0];\n        } else cell_data->current_codepoint = 0;\n        return width;\n    }\n    text_in_cell(cell_data->cpu_cell, tc, lc);\n    char_type cc = lc->chars[cell_data->codepoints_consumed];\n    // VS15/16 cause rendering to break, as they get marked as\n    // special glyphs, so map to 0, to avoid that\n    cell_data->current_codepoint = (cc == VS15 || cc == VS16) ? 0 : cc;\n    return 0;\n}\n\nstatic LigatureType\nligature_type_from_glyph_name(const char *glyph_name, SpacerStrategy strategy) {\n    const char *p, *m, *s, *e;\n    if (strategy == SPACERS_IOSEVKA) {\n        p = strrchr(glyph_name, '.');\n        m = \".join-m\"; s = \".join-l\"; e = \".join-r\";\n    } else {\n        p = strrchr(glyph_name, '_');\n        m = \"_middle.seq\"; s = \"_start.seq\"; e = \"_end.seq\";\n    }\n    if (p) {\n        if (strcmp(p, m) == 0) return INFINITE_LIGATURE_MIDDLE;\n        if (strcmp(p, s) == 0) return INFINITE_LIGATURE_START;\n        if (strcmp(p, e) == 0) return INFINITE_LIGATURE_END;\n    }\n    return LIGATURE_UNKNOWN;\n}\n\n#define G(x) (group_state.x)\n\nstatic void\ndetect_spacer_strategy(hb_font_t *hbf, Font *font, const TextCache *tc) {\n    CPUCell cpu_cells[3] = {0};\n    for (unsigned i = 0; i < arraysz(cpu_cells); i++) cell_set_char(&cpu_cells[i], '=');\n    const CellAttrs w1 = {0};\n    GPUCell gpu_cells[3] = {{.attrs = w1}, {.attrs = w1}, {.attrs = w1}};\n    shape(cpu_cells, gpu_cells, arraysz(cpu_cells), hbf, font, false, tc);\n    font->spacer_strategy = SPACERS_BEFORE;\n    if (G(num_glyphs) > 1) {\n        glyph_index glyph_id = G(info)[G(num_glyphs) - 1].codepoint;\n        bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data));\n        bool is_empty = is_special && is_empty_glyph(glyph_id, font);\n        if (is_empty) font->spacer_strategy = SPACERS_AFTER;\n    }\n    shape(cpu_cells, gpu_cells, 2, hbf, font, false, tc);\n    if (G(num_glyphs)) {\n        char glyph_name[128]; glyph_name[arraysz(glyph_name)-1] = 0;\n        for (unsigned i = 0; i < G(num_glyphs); i++) {\n            glyph_index glyph_id = G(info)[i].codepoint;\n            hb_font_glyph_to_string(hbf, glyph_id, glyph_name, arraysz(glyph_name)-1);\n            char *dot = strrchr(glyph_name, '.');\n            if (dot && (!strcmp(dot, \".join-l\") || !strcmp(dot, \".join-r\") || !strcmp(dot, \".join-m\"))) {\n                font->spacer_strategy = SPACERS_IOSEVKA;\n                break;\n            }\n        }\n    }\n\n    // If spacer_strategy is still default, check ### glyph to confirm strategy\n    // https://github.com/kovidgoyal/kitty/issues/4721\n    if (font->spacer_strategy == SPACERS_BEFORE) {\n        for (unsigned i = 0; i < arraysz(cpu_cells); i++) cell_set_char(&cpu_cells[i], '#');\n        shape(cpu_cells, gpu_cells, arraysz(cpu_cells), hbf, font, false, tc);\n        if (G(num_glyphs) > 1) {\n            glyph_index glyph_id = G(info)[G(num_glyphs) - 1].codepoint;\n            bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data));\n            bool is_empty = is_special && is_empty_glyph(glyph_id, font);\n            if (is_empty) font->spacer_strategy = SPACERS_AFTER;\n        }\n    }\n}\n\nstatic LigatureType\nligature_type_for_glyph(hb_font_t *hbf, glyph_index glyph_id, SpacerStrategy strategy) {\n    static char glyph_name[128]; glyph_name[arraysz(glyph_name)-1] = 0;\n    hb_font_glyph_to_string(hbf, glyph_id, glyph_name, arraysz(glyph_name)-1);\n    return ligature_type_from_glyph_name(glyph_name, strategy);\n}\n\n#define L INFINITE_LIGATURE_START\n#define M INFINITE_LIGATURE_MIDDLE\n#define R INFINITE_LIGATURE_END\n#define I LIGATURE_UNKNOWN\nstatic bool\nis_iosevka_lig_starter(LigatureType before, LigatureType current, LigatureType after) {\n    return (current == R || (current == I && (after == L || after == M))) \\\n                     && \\\n           !(before == R || before == M);\n}\n\nstatic bool\nis_iosevka_lig_ender(LigatureType before, LigatureType current, LigatureType after) {\n    return (current == L || (current == I && (before == R || before == M))) \\\n                     && \\\n            !(after == L || after == M);\n}\n#undef L\n#undef M\n#undef R\n#undef I\n\nstatic LigatureType *ligature_types = NULL;\nstatic size_t ligature_types_sz = 0;\n\nstatic void\ngroup_iosevka(Font *font, hb_font_t *hbf, const TextCache *tc, ListOfChars *lc) {\n    // Group as per algorithm discussed in: https://github.com/be5invis/Iosevka/issues/1007\n    if (ligature_types_sz <= G(num_glyphs)) {\n        ligature_types_sz = G(num_glyphs) + 16;\n        ligature_types = realloc(ligature_types, ligature_types_sz * sizeof(ligature_types[0]));\n        if (!ligature_types) fatal(\"Out of memory allocating ligature types array\");\n    }\n    for (size_t i = G(glyph_idx); i < G(num_glyphs); i++) {\n        ligature_types[i] = ligature_type_for_glyph(hbf, G(info)[i].codepoint, font->spacer_strategy);\n    }\n\n    uint32_t cluster, next_cluster;\n    while (G(glyph_idx) < G(num_glyphs) && G(cell_idx) < G(num_cells)) {\n        cluster = G(info)[G(glyph_idx)].cluster;\n        uint32_t num_codepoints_used_by_glyph = 0;\n        bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1;\n        Group *current_group = G(groups) + G(group_idx);\n        if (is_last_glyph) {\n            num_codepoints_used_by_glyph = UINT32_MAX;\n            next_cluster = 0;\n        } else {\n            next_cluster = G(info)[G(glyph_idx) + 1].cluster;\n            // RTL languages like Arabic have decreasing cluster numbers\n            if (next_cluster != cluster) num_codepoints_used_by_glyph = cluster > next_cluster ? cluster - next_cluster : next_cluster - cluster;\n        }\n        const LigatureType before = G(glyph_idx) ? ligature_types[G(glyph_idx - 1)] : LIGATURE_UNKNOWN;\n        const LigatureType current = ligature_types[G(glyph_idx)];\n        const LigatureType after = is_last_glyph ? LIGATURE_UNKNOWN : ligature_types[G(glyph_idx + 1)];\n        bool end_current_group = false;\n        if (current_group->num_glyphs) {\n            if (is_iosevka_lig_ender(before, current, after)) end_current_group = true;\n            else {\n                if (!current_group->num_cells && !current_group->has_special_glyph) {\n                    if (is_iosevka_lig_starter(before, current, after)) current_group->has_special_glyph = true;\n                    else end_current_group = true;\n                }\n            }\n        }\n        if (!current_group->num_glyphs++) {\n            if (is_iosevka_lig_starter(before, current, after)) current_group->has_special_glyph = true;\n            else end_current_group = true;\n            current_group->first_glyph_idx = G(glyph_idx);\n            current_group->first_cell_idx = G(cell_idx);\n        }\n        if (is_last_glyph) {\n            // soak up all remaining cells\n            if (G(cell_idx) < G(num_cells)) {\n                unsigned int num_left = G(num_cells) - G(cell_idx);\n                current_group->num_cells += num_left;\n                G(cell_idx) += num_left;\n            }\n        } else {\n            unsigned int num_cells_consumed = 0;\n            while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) {\n                unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell), tc, lc);\n                G(cell_idx) += w;\n                num_cells_consumed += w;\n                num_codepoints_used_by_glyph--;\n            }\n            current_group->num_cells += num_cells_consumed;\n        }\n        if (end_current_group && current_group->num_cells) G(group_idx)++;\n        G(glyph_idx)++;\n    }\n}\n\nstatic void\ngroup_normal(Font *font, hb_font_t *hbf, const TextCache *tc, ListOfChars *lc) {\n    /* Now distribute the glyphs into groups of cells\n     * Considerations to keep in mind:\n     * Group sizes should be as small as possible for best performance\n     * Combining chars can result in multiple glyphs rendered into a single cell\n     * Emoji and East Asian wide chars can cause a single glyph to be rendered over multiple cells\n     * Ligature fonts, take two common approaches:\n     * 1. ABC becomes EMPTY, EMPTY, WIDE GLYPH this means we have to render N glyphs in N cells (example Fira Code)\n     * 2. ABC becomes WIDE GLYPH this means we have to render one glyph in N cells (example Operator Mono Lig)\n     * 3. ABC becomes WIDE GLYPH, EMPTY, EMPTY this means we have to render N glyphs in N cells (example Cascadia Code)\n     * 4. Variable length ligatures are identified by a glyph naming convention of _start.seq, _middle.seq and _end.seq\n     *    with EMPTY glyphs in the middle or after (both Fira Code and Cascadia Code)\n     *\n     * We rely on the cluster numbers from harfbuzz to tell us how many unicode codepoints a glyph corresponds to.\n     * Then we check if the glyph is a ligature glyph (is_special_glyph) and if it is an empty glyph.\n     * We detect if the font uses EMPTY glyphs before or after ligature glyphs (1. or 3. above) by checking what it does for === and ###.\n     * Finally we look at the glyph name. These five datapoints give us enough information to satisfy the constraint above,\n     * for a wide variety of fonts.\n     */\n    uint32_t cluster, next_cluster;\n    bool add_to_current_group;\n    bool prev_glyph_was_inifinte_ligature_end = false;\n    while (G(glyph_idx) < G(num_glyphs) && G(cell_idx) < G(num_cells)) {\n        glyph_index glyph_id = G(info)[G(glyph_idx)].codepoint;\n        LigatureType ligature_type = ligature_type_for_glyph(hbf, glyph_id, font->spacer_strategy);\n        cluster = G(info)[G(glyph_idx)].cluster;\n        bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data));\n        bool is_empty = is_special && is_empty_glyph(glyph_id, font);\n        uint32_t num_codepoints_used_by_glyph = 0;\n        bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1;\n        Group *current_group = G(groups) + G(group_idx);\n\n        if (is_last_glyph) {\n            num_codepoints_used_by_glyph = UINT32_MAX;\n            next_cluster = 0;\n        } else {\n            next_cluster = G(info)[G(glyph_idx) + 1].cluster;\n            // RTL languages like Arabic have decreasing cluster numbers\n            if (next_cluster != cluster) num_codepoints_used_by_glyph = cluster > next_cluster ? cluster - next_cluster : next_cluster - cluster;\n        }\n        if (!current_group->num_glyphs) {\n            add_to_current_group = true;\n        } else if (current_group->started_with_infinite_ligature) {\n            if (prev_glyph_was_inifinte_ligature_end) add_to_current_group = is_empty && font->spacer_strategy == SPACERS_AFTER;\n            else add_to_current_group = ligature_type == INFINITE_LIGATURE_MIDDLE || ligature_type == INFINITE_LIGATURE_END || is_empty;\n        } else {\n            if (is_special) {\n                if (!current_group->num_cells) add_to_current_group = true;\n                else if (font->spacer_strategy == SPACERS_BEFORE) add_to_current_group = G(prev_was_empty);\n                else add_to_current_group = is_empty;\n            } else {\n                add_to_current_group = !G(prev_was_special) || !current_group->num_cells;\n            }\n        }\n#if 0\n        char ch[8] = {0};\n        encode_utf8(G(current_cell_data).current_codepoint, ch);\n        printf(\"\\x1b[32m→ %s\\x1b[m glyph_idx: %zu glyph_id: %u group_idx: %zu cluster: %u -> %u is_special: %d\\n\"\n                \"  num_codepoints_used_by_glyph: %u current_group: (%u cells, %u glyphs) add_to_current_group: %d\\n\",\n                ch, G(glyph_idx), glyph_id, G(group_idx), cluster, next_cluster, is_special,\n                num_codepoints_used_by_glyph, current_group->num_cells, current_group->num_glyphs, add_to_current_group);\n#endif\n\n        if (!add_to_current_group) { current_group = G(groups) + ++G(group_idx); }\n        if (!current_group->num_glyphs++) {\n            if (ligature_type == INFINITE_LIGATURE_START || ligature_type == INFINITE_LIGATURE_MIDDLE) current_group->started_with_infinite_ligature = true;\n            current_group->first_glyph_idx = G(glyph_idx);\n            current_group->first_cell_idx = G(cell_idx);\n        }\n\n        if (is_special) current_group->has_special_glyph = true;\n        if (is_last_glyph) {\n            // soak up all remaining cells\n            if (G(cell_idx) < G(num_cells)) {\n                unsigned int num_left = G(num_cells) - G(cell_idx);\n                current_group->num_cells += num_left;\n                G(cell_idx) += num_left;\n            }\n        } else {\n            unsigned int num_cells_consumed = 0;\n            while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) {\n                unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell), tc, lc);\n                G(cell_idx) += w;\n                num_cells_consumed += w;\n                num_codepoints_used_by_glyph--;\n            }\n            if (num_cells_consumed) {\n                current_group->num_cells += num_cells_consumed;\n                if (!is_special) {  // not a ligature, end the group\n                    G(group_idx)++; current_group = G(groups) + G(group_idx);\n                }\n            }\n        }\n\n        G(prev_was_special) = is_special;\n        G(prev_was_empty) = is_empty;\n        G(previous_cluster) = cluster;\n        prev_glyph_was_inifinte_ligature_end = ligature_type == INFINITE_LIGATURE_END;\n        G(glyph_idx)++;\n    }\n}\n\n\nstatic float\nshape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font, RunFont rf, FontGroup *fg, bool disable_ligature, const TextCache *tc, ListOfChars *lc) {\n    float scale = apply_scale_to_font_group(fg, &rf);\n    if (scale != 1.f) if (!face_apply_scaling(font->face, (FONTS_DATA_HANDLE)fg) && PyErr_Occurred()) PyErr_Print();\n    hb_font_t *hbf = harfbuzz_font_for_face(font->face);\n    if (font->spacer_strategy == SPACER_STRATEGY_UNKNOWN) detect_spacer_strategy(hbf, font, tc);\n    shape(first_cpu_cell, first_gpu_cell, num_cells, hbf, font, disable_ligature, tc);\n    if (font->spacer_strategy == SPACERS_IOSEVKA) group_iosevka(font, hbf, tc, lc);\n    else group_normal(font, hbf, tc, lc);\n#if 0\n        static char dbuf[1024];\n        // You can also generate this easily using hb-shape --show-extents --cluster-level=1 --shapers=ot /path/to/font/file text\n        hb_buffer_serialize_glyphs(harfbuzz_buffer, 0, group_state.num_glyphs, dbuf, sizeof(dbuf), NULL, harfbuzz_font_for_face(font->face), HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT | HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS);\n        printf(\"\\n%s\\n\", dbuf);\n#endif\n    if (scale != 1.f) {\n        apply_scale_to_font_group(fg, NULL);\n        if (!face_apply_scaling(font->face, (FONTS_DATA_HANDLE)fg) && PyErr_Occurred()) PyErr_Print();\n    }\n    return scale;\n}\n\nstatic void\ncollapse_pua_space_ligature(index_type num_cells) {\n    Group *g = G(groups);\n    G(group_idx) = 0;\n    g->num_cells = num_cells;\n    // We dont want to render the spaces in a space ligature because\n    // there exist stupid fonts like Powerline that have no space glyph,\n    // so special case it: https://github.com/kovidgoyal/kitty/issues/1225\n    g->num_glyphs = 1;\n}\n\nstatic bool\ngroup_has_more_than_one_scaled_cell(const Group *group, float scale) {\n    return group->num_cells / scale > 1.0f;\n}\n\n\nstatic void\nsplit_run_at_offset(index_type cursor_offset, index_type *left, index_type *right, float scale) {\n    *left = 0; *right = 0;\n    for (unsigned idx = 0; idx < G(group_idx) + 1; idx++) {\n        Group *group = G(groups) + idx;\n        if (group->first_cell_idx <= cursor_offset && cursor_offset < group->first_cell_idx + group->num_cells) {\n            if (group->has_special_glyph && group_has_more_than_one_scaled_cell(group, scale)) {\n                // likely a calt ligature\n                *left = group->first_cell_idx; *right = group->first_cell_idx + group->num_cells;\n            }\n            break;\n        }\n    }\n}\n\nstatic void\nrender_groups(FontGroup *fg, RunFont rf, bool center_glyph, const TextCache *tc) {\n    unsigned idx = 0;\n    const FontCellMetrics unscaled_metrics = fg->fcm;\n    float scale = apply_scale_to_font_group(fg, &rf);\n    if (scale != 1.f) if (!face_apply_scaling(fg->fonts[rf.font_idx].face, (FONTS_DATA_HANDLE)fg) && PyErr_Occurred()) PyErr_Print();\n    while (idx <= G(group_idx)) {\n        Group *group = G(groups) + idx;\n        if (!group->num_cells) break;\n         // printf(\"Group: idx: %u num_cells: %u num_glyphs: %u first_glyph_idx: %u first_cell_idx: %u total_num_glyphs: %zu\\n\", idx, group->num_cells, group->num_glyphs, group->first_glyph_idx, group->first_cell_idx, group_state.num_glyphs);\n        if (group->num_glyphs) {\n            ensure_glyph_render_scratch_space(MAX(group->num_glyphs, group->num_cells));\n            for (unsigned i = 0; i < group->num_glyphs; i++) global_glyph_render_scratch.glyphs[i] = G(info)[group->first_glyph_idx + i].codepoint;\n            render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, rf, global_glyph_render_scratch.glyphs, group->num_glyphs, center_glyph, tc, scale, unscaled_metrics);\n        }\n        idx++;\n    }\n    if (scale != 1.f) {\n        apply_scale_to_font_group(fg, NULL);\n        if (!face_apply_scaling(fg->fonts[rf.font_idx].face, (FONTS_DATA_HANDLE)fg) && PyErr_Occurred()) PyErr_Print();\n    }\n\n}\n\nstatic PyObject*\ntest_shape(PyObject UNUSED *self, PyObject *args) {\n    Line *line;\n    char *path = NULL;\n    int index = 0;\n    if(!PyArg_ParseTuple(args, \"O!|zi\", &Line_Type, &line, &path, &index)) return NULL;\n    index_type num = 0;\n    const CPUCell *c;\n    while(num < line->xnum && cell_has_text(line->cpu_cells + num)) {\n        index_type width = 1;\n        if ((c = line->cpu_cells + num)->is_multicell) {\n            width = c->width * c->scale;\n        }\n        num += width;\n    }\n    PyObject *face = NULL;\n    Font *font;\n    if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, \"must create at least one font group first\"); return NULL; }\n    FontGroup *fg = font_groups;\n    if (path) {\n        face = face_from_path(path, index, (FONTS_DATA_HANDLE)font_groups);\n        if (face == NULL) return NULL;\n        font = calloc(1, sizeof(Font));\n        font->face = face;\n        if (!init_hash_tables(font)) return NULL;\n    } else {\n        font = fg->fonts + fg->medium_font_idx;\n    }\n    RunFont rf = {0};\n    RAII_ListOfChars(lc);\n    shape_run(line->cpu_cells, line->gpu_cells, num, font, rf, fg, false, line->text_cache, &lc);\n\n    PyObject *ans = PyList_New(0);\n    unsigned int idx = 0;\n    glyph_index first_glyph;\n    while (idx <= G(group_idx)) {\n        Group *group = G(groups) + idx;\n        if (!group->num_cells) break;\n        first_glyph = group->num_glyphs ? G(info)[group->first_glyph_idx].codepoint : 0;\n\n        PyObject *eg = PyTuple_New(group->num_glyphs);\n        for (size_t g = 0; g < group->num_glyphs; g++) PyTuple_SET_ITEM(eg, g, Py_BuildValue(\"H\", G(info)[group->first_glyph_idx + g].codepoint));\n        PyList_Append(ans, Py_BuildValue(\"IIHN\", group->num_cells, group->num_glyphs, first_glyph, eg));\n        idx++;\n    }\n    if (face) { Py_CLEAR(face); free_maps(font); free(font); }\n    return ans;\n}\n#undef G\n\nstatic void\nrender_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, RunFont rf, bool pua_space_ligature, bool center_glyph, int cursor_offset, DisableLigature disable_ligature_strategy, const TextCache *tc, ListOfChars *lc) {\n    float scale;\n    switch(rf.font_idx) {\n        default:\n            scale = shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[rf.font_idx], rf, fg, disable_ligature_strategy == DISABLE_LIGATURES_ALWAYS, tc, lc);\n            if (pua_space_ligature) collapse_pua_space_ligature(num_cells);\n            else if (cursor_offset > -1) { // false if DISABLE_LIGATURES_NEVER\n                index_type left, right;\n                split_run_at_offset(cursor_offset, &left, &right, scale);\n                if (right > left) {\n                    if (left) {\n                        shape_run(first_cpu_cell, first_gpu_cell, left, &fg->fonts[rf.font_idx], rf, fg, false, tc, lc);\n                        render_groups(fg, rf, center_glyph, tc);\n                    }\n                        shape_run(first_cpu_cell + left, first_gpu_cell + left, right - left, &fg->fonts[rf.font_idx], rf, fg, true, tc, lc);\n                        render_groups(fg, rf, center_glyph, tc);\n                    if (right < num_cells) {\n                        shape_run(first_cpu_cell + right, first_gpu_cell + right, num_cells - right, &fg->fonts[rf.font_idx], rf, fg, false, tc, lc);\n                        render_groups(fg, rf, center_glyph, tc);\n                    }\n                    break;\n                }\n            }\n            render_groups(fg, rf, center_glyph, tc);\n            break;\n        case BLANK_FONT:\n            while (num_cells--) {\n                first_gpu_cell->sprite_idx = 0; first_cpu_cell++; first_gpu_cell++;\n            }\n            break;\n        case BOX_FONT:\n            while(num_cells) {\n                render_box_cell(fg, rf, first_cpu_cell, first_gpu_cell, tc);\n                num_cells -= rf.scale;\n                first_cpu_cell += rf.scale;\n                first_gpu_cell += rf.scale;\n            }\n            break;\n        case MISSING_FONT:\n            while(num_cells--) { first_gpu_cell->sprite_idx = MISSING_GLYPH; first_cpu_cell++; first_gpu_cell++; }\n            break;\n    }\n}\n\nstatic bool\nis_non_emoji_dingbat(char_type ch, CharProps cp) {\n    switch(ch) {\n        START_ALLOW_CASE_RANGE\n        case 0x2700 ... 0x27bf:\n        case 0x1f100 ... 0x1f1ff:\n            return !cp.is_emoji;\n        END_ALLOW_CASE_RANGE\n    }\n    return false;\n}\n\nstatic unsigned int\ncell_cap_for_codepoint(const char_type cp) {\n    unsigned int ans = UINT_MAX;\n    for (size_t i = 0; i < num_narrow_symbols; i++) {\n        SymbolMap *sm = narrow_symbols + i;\n        if (sm->left <= cp && cp <= sm->right) ans = sm->font_idx;\n    }\n    return ans;\n}\n\nstatic bool\nrun_fonts_are_equal(const RunFont *a, const RunFont *b) {\n    return a->font_idx == b->font_idx && a->scale == b->scale && a->subscale_n == b->subscale_n && a->subscale_d == b->subscale_d && a->align.val == b->align.val && a->multicell_y == b->multicell_y;\n}\n\nstatic bool\nmulticell_intersects_cursor(const Line *line, index_type lnum, const Cursor *cursor) {\n    const CPUCell *c = line->cpu_cells + cursor->x;\n    if (c->is_multicell) {\n        index_type min_y = lnum > c->y ? lnum - c->y : 0;\n        index_type max_y = lnum + (c->scale - c->y - 1);\n        return min_y <= cursor->y && cursor->y <= max_y;\n    } else return lnum == cursor->y;\n}\n\nvoid\nrender_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor, DisableLigature disable_ligature_strategy, ListOfChars *lc) {\n#define RENDER if (run_font.font_idx != NO_FONT && i > first_cell_in_run) { \\\n    int cursor_offset = -1; \\\n    if (disable_ligature_at_cursor && first_cell_in_run <= cursor->x && cursor->x <= i && cursor->x < line->xnum && \\\n            multicell_intersects_cursor(line, lnum, cursor)) cursor_offset = cursor->x - first_cell_in_run; \\\n    render_run(fg, line->cpu_cells + first_cell_in_run, line->gpu_cells + first_cell_in_run, i - first_cell_in_run, run_font, false, center_glyph, cursor_offset, disable_ligature_strategy, line->text_cache, lc); \\\n}\n    FontGroup *fg = (FontGroup*)fg_;\n    RunFont basic_font = {.scale=1, .font_idx = NO_FONT}, run_font = basic_font, cell_font = basic_font;\n    bool center_glyph = false;\n    bool disable_ligature_at_cursor = cursor != NULL && disable_ligature_strategy == DISABLE_LIGATURES_CURSOR;\n    index_type first_cell_in_run, i;\n    for (i=0, first_cell_in_run=0; i < line->xnum; i++) {\n        cell_font = basic_font;\n        CPUCell *cpu_cell = line->cpu_cells + i;\n        if (cpu_cell->is_multicell) {\n            if (cpu_cell->x) {\n                if (cpu_cell->x + 1u < mcd_x_limit(cpu_cell)) i += mcd_x_limit(cpu_cell) - cpu_cell->x - 1u;\n                continue;\n            }\n            cell_font.scale = cpu_cell->scale; cell_font.subscale_n = cpu_cell->subscale_n; cell_font.subscale_d = cpu_cell->subscale_d;\n            cell_font.align.vertical = cpu_cell->valign; cell_font.align.horizontal = cpu_cell->halign;\n            cell_font.multicell_y = cpu_cell->y;\n        }\n        text_in_cell(cpu_cell, line->text_cache, lc);\n        bool is_main_font, is_emoji_presentation;\n        GPUCell *gpu_cell = line->gpu_cells + i;\n        const char_type first_ch = lc->chars[0];\n        cell_font.font_idx = font_for_cell(fg, cpu_cell, gpu_cell, &is_main_font, &is_emoji_presentation, line->text_cache, lc);\n        CharProps cp = char_props_for(first_ch);\n        if (\n                cell_font.font_idx != MISSING_FONT &&\n                ((!is_main_font && !is_emoji_presentation && cp.is_symbol) || (cell_font.font_idx != BOX_FONT && (is_private_use(cp))) || is_non_emoji_dingbat(first_ch, cp))\n        ) {\n            unsigned int desired_cells = 1;\n            if (cell_font.font_idx > 0) {\n                Font *font = (fg->fonts + cell_font.font_idx);\n                glyph_index glyph_id = glyph_id_for_codepoint(font->face, first_ch);\n\n                int width = get_glyph_width(font->face, glyph_id);\n                desired_cells = (unsigned int)ceilf((float)width / fg->fcm.cell_width);\n            }\n            desired_cells = MIN(desired_cells, cell_cap_for_codepoint(first_ch));\n\n            unsigned int num_spaces = 0;\n            while (\n                    i + num_spaces + 1 < line->xnum\n                    && (cell_is_char(line->cpu_cells + i + num_spaces + 1, ' ') || cell_is_char(line->cpu_cells + i + num_spaces + 1, 0x2002))  // space or en-space\n                    && num_spaces < MAX_NUM_EXTRA_GLYPHS_PUA\n                    && num_spaces + 1 < desired_cells\n            ) {\n                num_spaces++;\n                // We have a private use char followed by space(s), render it as a multi-cell ligature.\n                GPUCell *space_cell = line->gpu_cells + i + num_spaces;\n                // Ensure the space cell uses the foreground color from the PUA cell.\n                // This is needed because there are applications like\n                // Powerline that use PUA+space with different foreground colors\n                // for the space and the PUA. See for example: https://github.com/kovidgoyal/kitty/issues/467\n                space_cell->fg = gpu_cell->fg;\n                space_cell->decoration_fg = gpu_cell->decoration_fg;\n            }\n            if (num_spaces) {\n                center_glyph = true;\n                RENDER\n                center_glyph = false;\n                render_run(fg, line->cpu_cells + i, line->gpu_cells + i, num_spaces + 1, cell_font, true, center_glyph, -1, disable_ligature_strategy, line->text_cache, lc);\n                run_font = basic_font;\n                first_cell_in_run = i + num_spaces + 1;\n                i += num_spaces;\n                continue;\n            }\n        }\n        if (run_font.font_idx == NO_FONT) run_font = cell_font;\n        if (run_fonts_are_equal(&run_font, &cell_font)) continue;\n        RENDER\n        run_font = cell_font;\n        first_cell_in_run = i;\n    }\n    RENDER\n#undef RENDER\n}\n\nStringCanvas\nrender_simple_text(FONTS_DATA_HANDLE fg_, const char *text) {\n    FontGroup *fg = (FontGroup*)fg_;\n    if (fg->fonts_count && fg->medium_font_idx) return render_simple_text_impl(fg->fonts[fg->medium_font_idx].face, text, fg->fcm.baseline);\n    StringCanvas ans = {0};\n    return ans;\n}\n\nstatic void\nclear_symbol_maps(void) {\n    if (symbol_maps) { free(symbol_maps); symbol_maps = NULL; num_symbol_maps = 0; }\n    if (narrow_symbols) { free(narrow_symbols); narrow_symbols = NULL; num_narrow_symbols = 0; }\n}\n\ntypedef struct {\n    unsigned int main, bold, italic, bi, num_symbol_fonts;\n} DescriptorIndices;\n\nDescriptorIndices descriptor_indices = {0};\n\nstatic bool\nset_symbol_maps(SymbolMap **maps, size_t *num, const PyObject *sm) {\n    *num = PyTuple_GET_SIZE(sm);\n    *maps = calloc(*num, sizeof(SymbolMap));\n    if (*maps == NULL) { PyErr_NoMemory(); return false; }\n    for (size_t s = 0; s < *num; s++) {\n        unsigned int left, right, font_idx;\n        SymbolMap *x = *maps + s;\n        if (!PyArg_ParseTuple(PyTuple_GET_ITEM(sm, s), \"III\", &left, &right, &font_idx)) return NULL;\n        x->left = left; x->right = right; x->font_idx = font_idx;\n    }\n    return true;\n}\n\nstatic PyObject*\nset_font_data(PyObject UNUSED *m, PyObject *args) {\n    PyObject *sm, *ns;\n    Py_CLEAR(descriptor_for_idx);\n    if (!PyArg_ParseTuple(args, \"OIIIIO!dO!\",\n                &descriptor_for_idx,\n                &descriptor_indices.bold, &descriptor_indices.italic, &descriptor_indices.bi, &descriptor_indices.num_symbol_fonts,\n                &PyTuple_Type, &sm, &OPT(font_size), &PyTuple_Type, &ns)) return NULL;\n    Py_INCREF(descriptor_for_idx);\n    free_font_groups();\n    clear_symbol_maps();\n    set_symbol_maps(&symbol_maps, &num_symbol_maps, sm);\n    set_symbol_maps(&narrow_symbols, &num_narrow_symbols, ns);\n    Py_RETURN_NONE;\n}\n\nstatic void\nsend_prerendered_sprites(FontGroup *fg) {\n    // blank cell\n    ensure_canvas_can_fit(fg, 1, 1);\n    DecorationMetadata dm = {.start_idx=5};\n    current_send_sprite_to_gpu(fg, fg->canvas.buf, dm, fg->fcm);\n    const unsigned cell_area = fg->fcm.cell_height * fg->fcm.cell_width;\n    RAII_ALLOC(uint8_t, alpha_mask, malloc(cell_area));\n    if (!alpha_mask) fatal(\"Out of memory\");\n    Region r = { .right = fg->fcm.cell_width, .bottom = fg->fcm.cell_height };\n#define do_one(call) \\\n    memset(alpha_mask, 0, cell_area); \\\n    call; \\\n    ensure_canvas_can_fit(fg, 1, 1);  /* clear canvas */ \\\n    render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->fcm.cell_width, fg->fcm.cell_width, 0xffffff); \\\n    current_send_sprite_to_gpu(fg, fg->canvas.buf, dm, fg->fcm);\n\n    // If you change the mapping of these cells you will need to change\n    // BEAM_IDX in shader.c and STRIKE_SPRITE_INDEX in\n    // shaders.py and MISSING_GLYPH in font.c and dec_idx above\n    do_one(add_missing_glyph(alpha_mask, fg->fcm));\n    do_one(add_beam_cursor(alpha_mask, fg->fcm, fg->logical_dpi_x));\n    do_one(add_underline_cursor(alpha_mask, fg->fcm, fg->logical_dpi_y));\n    do_one(add_hollow_cursor(alpha_mask, fg->fcm, fg->logical_dpi_x, fg->logical_dpi_y));\n    RunFont rf = {.scale=1};\n    Region rg = {.bottom = fg->fcm.cell_height, .right = fg->fcm.cell_width};\n    sprite_index actual_dec_idx = index_for_decorations(fg, rf, rg, rg, fg->fcm).start_idx;\n    if (actual_dec_idx != dm.start_idx) fatal(\"dec_idx: %u != actual_dec_idx: %u\", dm.start_idx, actual_dec_idx);\n\n#undef do_one\n}\n\nstatic size_t\ninitialize_font(FontGroup *fg, unsigned int desc_idx, const char *ftype) {\n    PyObject *d = PyObject_CallFunction(descriptor_for_idx, \"I\", desc_idx);\n    if (d == NULL) { PyErr_Print(); fatal(\"Failed for %s font\", ftype); }\n    bool bold = PyObject_IsTrue(PyTuple_GET_ITEM(d, 1));\n    bool italic = PyObject_IsTrue(PyTuple_GET_ITEM(d, 2));\n    PyObject *x = PyTuple_GET_ITEM(d, 0);\n    PyObject *face  = PyUnicode_Check(x) ? face_from_path(PyUnicode_AsUTF8(x), 0, (FONTS_DATA_HANDLE)fg) : desc_to_face(x, (FONTS_DATA_HANDLE)fg);\n    Py_CLEAR(d);\n    if (face == NULL) { PyErr_Print(); fatal(\"Failed to convert descriptor to face for %s font\", ftype); }\n    size_t idx = fg->fonts_count++;\n    bool ok = init_font(fg->fonts + idx, face, bold, italic, false);\n    Py_CLEAR(face);\n    if (!ok) {\n        if (PyErr_Occurred()) { PyErr_Print(); }\n        fatal(\"Failed to initialize %s font: %zu\", ftype, idx);\n    }\n    return idx;\n}\n\nstatic void\ninitialize_font_group(FontGroup *fg) {\n    fg->fonts_capacity = 10 + descriptor_indices.num_symbol_fonts;\n    fg->fonts = calloc(fg->fonts_capacity, sizeof(Font));\n    if (fg->fonts == NULL) fatal(\"Out of memory allocating fonts array\");\n    fg->fonts_count = 1;  // the 0 index font is the box font\n    if (!init_hash_tables(fg->fonts)) fatal(\"Out of memory\");\n    vt_init(&fg->fallback_font_map);\n    vt_init(&fg->scaled_font_map);\n    vt_init(&fg->decorations_index_map);\n#define I(attr)  if (descriptor_indices.attr) fg->attr##_font_idx = initialize_font(fg, descriptor_indices.attr, #attr); else fg->attr##_font_idx = -1;\n    fg->medium_font_idx = initialize_font(fg, 0, \"medium\");\n    I(bold); I(italic); I(bi);\n#undef I\n    fg->first_symbol_font_idx = fg->fonts_count; fg->first_fallback_font_idx = fg->fonts_count;\n    fg->fallback_fonts_count = 0;\n    for (size_t i = 0; i < descriptor_indices.num_symbol_fonts; i++) {\n        initialize_font(fg, descriptor_indices.bi + 1 + i, \"symbol_map\");\n        fg->first_fallback_font_idx++;\n    }\n#undef I\n    calc_cell_metrics(fg, fg->fonts[fg->medium_font_idx].face);\n    ensure_canvas_can_fit(fg, 8, 1);\n    sprite_tracker_set_layout(&fg->sprite_tracker, fg->fcm.cell_width, fg->fcm.cell_height);\n    // rescale the symbol_map faces for the desired cell height, this is how fallback fonts are sized as well\n    for (size_t i = 0; i < descriptor_indices.num_symbol_fonts; i++) {\n        Font *font = fg->fonts + i + fg->first_symbol_font_idx;\n        set_size_for_face(font->face, fg->fcm.cell_height, true, (FONTS_DATA_HANDLE)fg);\n    }\n    ScaledFontData sfd = {.fcm=fg->fcm, .font_sz_in_pts=fg->font_sz_in_pts};\n    vt_insert(&fg->scaled_font_map, 1.f, sfd);\n}\n\n\nvoid\nsend_prerendered_sprites_for_window(OSWindow *w) {\n    FontGroup *fg = (FontGroup*)w->fonts_data;\n    if (!fg->sprite_map) {\n        fg->sprite_map = alloc_sprite_map();\n        send_prerendered_sprites(fg);\n    }\n}\n\nFONTS_DATA_HANDLE\nload_fonts_data(double font_sz_in_pts, double dpi_x, double dpi_y) {\n    FontGroup *fg = font_group_for(font_sz_in_pts, dpi_x, dpi_y);\n    return (FONTS_DATA_HANDLE)fg;\n}\n\nstatic void\nfinalize(void) {\n    Py_CLEAR(python_send_to_gpu_impl);\n    clear_symbol_maps();\n    Py_CLEAR(descriptor_for_idx);\n    free_font_groups();\n    free(ligature_types);\n    if (harfbuzz_buffer) { hb_buffer_destroy(harfbuzz_buffer); harfbuzz_buffer = NULL; }\n    free(group_state.groups); group_state.groups = NULL; group_state.groups_capacity = 0;\n    free(global_glyph_render_scratch.glyphs);\n    free(global_glyph_render_scratch.sprite_positions);\n    if (global_glyph_render_scratch.lc) { cleanup_list_of_chars(global_glyph_render_scratch.lc); free(global_glyph_render_scratch.lc); }\n    global_glyph_render_scratch = (GlyphRenderScratch){0};\n    free(shape_buffer.codepoints); zero_at_ptr(&shape_buffer);\n}\n\nstatic PyObject*\nsprite_map_set_layout(PyObject UNUSED *self, PyObject *args) {\n    unsigned int w, h;\n    if(!PyArg_ParseTuple(args, \"II\", &w, &h)) return NULL;\n    if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, \"must create font group first\"); return NULL; }\n    sprite_tracker_set_layout(&font_groups->sprite_tracker, w, h);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\ntest_sprite_position_increment(PyObject UNUSED *self, PyObject *args UNUSED) {\n    if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, \"must create font group first\"); return NULL; }\n    FontGroup *fg = font_groups;\n    unsigned int x, y, z;\n    sprite_index_to_pos(current_sprite_index(&fg->sprite_tracker), fg->sprite_tracker.xnum, fg->sprite_tracker.ynum, &x, &y, &z);\n    if (!do_increment(fg)) return NULL;\n    return Py_BuildValue(\"III\", x, y, z);\n}\n\nstatic PyObject*\nset_send_sprite_to_gpu(PyObject UNUSED *self, PyObject *func) {\n    Py_CLEAR(python_send_to_gpu_impl);\n    if (func != Py_None) {\n        python_send_to_gpu_impl = func;\n        Py_INCREF(python_send_to_gpu_impl);\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\ntest_render_line(PyObject UNUSED *self, PyObject *args) {\n    PyObject *line;\n    if (!PyArg_ParseTuple(args, \"O!\", &Line_Type, &line)) return NULL;\n    if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, \"must create font group first\"); return NULL; }\n    RAII_ListOfChars(lc);\n    render_line((FONTS_DATA_HANDLE)font_groups, (Line*)line, 0, NULL, DISABLE_LIGATURES_NEVER, &lc);\n    Py_RETURN_NONE;\n}\n\nstatic uint32_t\nalpha_blend(uint32_t fg, uint32_t bg) {\n    uint32_t r1 = (fg >> 16) & 0xFF, g1 = (fg >> 8) & 0xFF, b1 = fg & 0xFF, a = (fg >> 24) & 0xff;\n    uint32_t r2 = (bg >> 16) & 0xFF, g2 = (bg >> 8) & 0xFF, b2 = bg & 0xFF;\n    float alpha = a / 255.f;\n\n#define mix(x) uint32_t x = ((uint32_t)(alpha * x##1 + (1.0f - alpha) * x##2)) & 0xff;\n    mix(r); mix(g); mix(b);\n#undef mix\n    // Combine components into result color\n    return (0xff000000) | (r << 16) | (g << 8) | b;\n}\n\nstatic PyObject*\nrender_decoration(PyObject *self UNUSED, PyObject *args) {\n    const char *which;\n    FontCellMetrics fcm = {0};\n    double dpi = 96.0;\n    if (!PyArg_ParseTuple(args, \"sIIII|d\", &which, &fcm.cell_width, &fcm.cell_height, &fcm.underline_position, &fcm.underline_thickness, &dpi)) return NULL;\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, (Py_ssize_t)fcm.cell_width * fcm.cell_height);\n    if (!ans) return NULL;\n    memset(PyBytes_AS_STRING(ans), 0, PyBytes_GET_SIZE(ans));\n#define u(x) if (strcmp(which, #x) == 0) add_ ## x ## _underline((uint8_t*)PyBytes_AS_STRING(ans), fcm)\n    u(curl);\n    u(dashed);\n    u(dotted);\n    u(double);\n    u(straight);\n    else if (strcmp(which, \"strikethrough\") == 0) add_strikethrough((uint8_t*)PyBytes_AS_STRING(ans), fcm);\n    else if (strcmp(which, \"missing\") == 0) add_missing_glyph((uint8_t*)PyBytes_AS_STRING(ans), fcm);\n    else if (strcmp(which, \"beam_cursor\") == 0) add_beam_cursor((uint8_t*)PyBytes_AS_STRING(ans), fcm, dpi);\n    else if (strcmp(which, \"underline_cursor\") == 0) add_underline_cursor((uint8_t*)PyBytes_AS_STRING(ans), fcm, dpi);\n    else if (strcmp(which, \"hollow_cursor\") == 0) add_hollow_cursor((uint8_t*)PyBytes_AS_STRING(ans), fcm, dpi, dpi);\n    else { Py_CLEAR(ans); PyErr_Format(PyExc_KeyError, \"Unknown decoration type: %s\", which); }\n    return ans;\n#undef u\n}\n\nstatic PyObject*\nconcat_cells(PyObject UNUSED *self, PyObject *args) {\n    // Concatenate cells returning RGBA data\n    unsigned int cell_width, cell_height;\n    int is_32_bit;\n    PyObject *cells;\n    unsigned long bgcolor = 0;\n    if (!PyArg_ParseTuple(args, \"IIpO!|k\", &cell_width, &cell_height, &is_32_bit, &PyTuple_Type, &cells, &bgcolor)) return NULL;\n    size_t num_cells = PyTuple_GET_SIZE(cells), r, c, i;\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, (size_t)4 * cell_width * cell_height * num_cells);\n    if (ans == NULL) return PyErr_NoMemory();\n    pixel *dest = (pixel*)PyBytes_AS_STRING(ans);\n    for (r = 0; r < cell_height; r++) {\n        for (c = 0; c < num_cells; c++) {\n            void *s = ((uint8_t*)PyBytes_AS_STRING(PyTuple_GET_ITEM(cells, c)));\n            if (is_32_bit) {\n                pixel *src = (pixel*)s + cell_width * r;\n                for (i = 0; i < cell_width; i++, dest++) dest[0] = alpha_blend(src[0], bgcolor);\n            } else {\n                uint8_t *src = (uint8_t*)s + cell_width * r;\n                for (i = 0; i < cell_width; i++, dest++) dest[0] = alpha_blend(0x00ffffff | ((src[i] & 0xff) << 24), bgcolor);\n            }\n        }\n    }\n    return ans;\n}\n\nstatic PyObject*\ncurrent_fonts(PyObject *self UNUSED, PyObject *args) {\n    unsigned long long os_window_id = 0;\n    if (!PyArg_ParseTuple(args, \"|K\", &os_window_id)) return NULL;\n    if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, \"must create font group first\"); return NULL; }\n    FontGroup *fg = font_groups;\n    if (os_window_id) {\n        OSWindow *os_window = os_window_for_id(os_window_id);\n        if (!os_window) { PyErr_SetString(PyExc_KeyError, \"no oswindow with the specified id exists\"); return NULL; }\n        fg = (FontGroup*)os_window->fonts_data;\n    }\n    RAII_PyObject(ans, PyDict_New());\n    if (!ans) return NULL;\n#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fg->fonts[val].face) != 0) { return NULL; }}\n    SET(medium, fg->medium_font_idx);\n    if (fg->bold_font_idx > 0) SET(bold, fg->bold_font_idx);\n    if (fg->italic_font_idx > 0) SET(italic, fg->italic_font_idx);\n    if (fg->bi_font_idx > 0) SET(bi, fg->bi_font_idx);\n    unsigned num_symbol_fonts = fg->first_fallback_font_idx - fg->first_symbol_font_idx;\n    RAII_PyObject(ss, PyTuple_New(num_symbol_fonts));\n    if (!ss) return NULL;\n    for (size_t i = 0; i < num_symbol_fonts; i++) {\n        Py_INCREF(fg->fonts[fg->first_symbol_font_idx + i].face);\n        PyTuple_SET_ITEM(ss, i, fg->fonts[fg->first_symbol_font_idx + i].face);\n    }\n    if (PyDict_SetItemString(ans, \"symbol\", ss) != 0) return NULL;\n    RAII_PyObject(ff, PyTuple_New(fg->fallback_fonts_count));\n    if (!ff) return NULL;\n    for (size_t i = 0; i < fg->fallback_fonts_count; i++) {\n        Py_INCREF(fg->fonts[fg->first_fallback_font_idx + i].face);\n        PyTuple_SET_ITEM(ff, i, fg->fonts[fg->first_fallback_font_idx + i].face);\n    }\n    if (PyDict_SetItemString(ans, \"fallback\", ff) != 0) return NULL;\n#define p(x) { RAII_PyObject(t, PyFloat_FromDouble(fg->x)); if (!t) return NULL; if (PyDict_SetItemString(ans, #x, t) != 0) return NULL; }\n    p(font_sz_in_pts); p(logical_dpi_x); p(logical_dpi_y);\n#undef p\n    Py_INCREF(ans);\n    return ans;\n#undef SET\n}\n\nstatic PyObject*\nget_fallback_font(PyObject UNUSED *self, PyObject *args) {\n    if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, \"must create font group first\"); return NULL; }\n    PyObject *text;\n    int bold, italic;\n    if (!PyArg_ParseTuple(args, \"Upp\", &text, &bold, &italic)) return NULL;\n    GPUCell gpu_cell = {0}; CPUCell cpu_cell = {0};\n    RAII_ListOfChars(lc); lc.count = PyUnicode_GET_LENGTH(text);\n    ensure_space_for_chars(&lc, lc.count);\n    if (!PyUnicode_AsUCS4(text, lc.chars, lc.capacity, 1)) return NULL;\n    if (bold) gpu_cell.attrs.bold = true;\n    if (italic) gpu_cell.attrs.italic = true;\n    FontGroup *fg = font_groups;\n    ssize_t ans = fallback_font(fg, &cpu_cell, &gpu_cell, &lc);\n    if (ans == MISSING_FONT) { PyErr_SetString(PyExc_ValueError, \"No fallback font found\"); return NULL; }\n    if (ans < 0) { PyErr_SetString(PyExc_ValueError, \"Too many fallback fonts\"); return NULL; }\n    return fg->fonts[ans].face;\n}\n\nstatic PyObject*\ncreate_test_font_group(PyObject *self UNUSED, PyObject *args) {\n    double sz, dpix, dpiy;\n    if (!PyArg_ParseTuple(args, \"ddd\", &sz, &dpix, &dpiy)) return NULL;\n    FontGroup *fg = font_group_for(sz, dpix, dpiy);\n    if (!fg->sprite_map) send_prerendered_sprites(fg);\n    return Py_BuildValue(\"III\", fg->fcm.cell_width, fg->fcm.cell_height, fg->fcm.baseline);\n}\n\nstatic PyObject*\nfree_font_data(PyObject *self UNUSED, PyObject *args UNUSED) {\n    finalize();\n    Py_RETURN_NONE;\n}\n\nPyTypeObject ParsedFontFeature_Type;\n\nParsedFontFeature*\nparse_font_feature(const char *spec) {\n    ParsedFontFeature *self = (ParsedFontFeature*)ParsedFontFeature_Type.tp_alloc(&ParsedFontFeature_Type, 0);\n    if (self != NULL) {\n        if (!hb_feature_from_string(spec, -1, &self->feature)) {\n            PyErr_Format(PyExc_ValueError, \"%s is not a valid font feature\", self);\n            Py_CLEAR(self);\n        }\n    }\n    return self;\n}\n\nstatic PyObject *\nparsed_font_feature_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds UNUSED) {\n    const char *s;\n    if (!PyArg_ParseTuple(args, \"s\", &s)) return NULL;\n    return (PyObject*)parse_font_feature(s);\n}\n\nstatic PyObject*\nparsed_font_feature_str(PyObject *self_) {\n    char buf[128];\n    hb_feature_to_string(&((ParsedFontFeature*)self_)->feature, buf, arraysz(buf));\n    return PyUnicode_FromString(buf);\n}\n\nstatic PyObject*\nparsed_font_feature_repr(PyObject *self_) {\n    RAII_PyObject(s, parsed_font_feature_str(self_));\n    return s ? PyObject_Repr(s) : NULL;\n}\n\n\nstatic PyObject*\nparsed_font_feature_cmp(PyObject *self, PyObject *other, int op) {\n    if (op != Py_EQ && op != Py_NE) return Py_NotImplemented;\n    if (!PyObject_TypeCheck(other, &ParsedFontFeature_Type)) {\n        if (op == Py_EQ) Py_RETURN_FALSE;\n        Py_RETURN_TRUE;\n    }\n    ParsedFontFeature *a = (ParsedFontFeature*)self, *b = (ParsedFontFeature*)other;\n    PyObject *ret = Py_True;\n    if (memcmp(&a->feature, &b->feature, sizeof(hb_feature_t)) == 0) {\n        if (op == Py_NE) ret = Py_False;\n    } else {\n        if (op == Py_EQ) ret = Py_False;\n    }\n    Py_INCREF(ret); return ret;\n}\n\nstatic Py_hash_t\nparsed_font_feature_hash(PyObject *s) {\n    ParsedFontFeature *self = (ParsedFontFeature*)s;\n    if (!self->hash_computed) {\n        self->hash_computed = true;\n        self->hashval = vt_hash_bytes(&self->feature, sizeof(hb_feature_t));\n    }\n    return self->hashval;\n}\n\nstatic PyObject*\nparsed_font_feature_call(PyObject *s, PyObject *args, PyObject *kwargs UNUSED) {\n    ParsedFontFeature *self = (ParsedFontFeature*)s;\n    void *dest = PyLong_AsVoidPtr(args);\n    memcpy(dest, &self->feature, sizeof(hb_feature_t));\n    Py_RETURN_NONE;\n}\n\nPyTypeObject ParsedFontFeature_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"kitty.fast_data_types.ParsedFontFeature\",\n    .tp_basicsize = sizeof(ParsedFontFeature),\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"FontFeature\",\n    .tp_new = parsed_font_feature_new,\n    .tp_str = parsed_font_feature_str,\n    .tp_repr = parsed_font_feature_repr,\n    .tp_richcompare = parsed_font_feature_cmp,\n    .tp_hash = parsed_font_feature_hash,\n    .tp_call = parsed_font_feature_call,\n};\n\nstatic PyObject*\npyspecialize_font_descriptor(PyObject *self UNUSED, PyObject *args) {\n    PyObject *desc; double font_sz, dpi_x, dpi_y;\n    if (!PyArg_ParseTuple(args, \"Offf\", &desc, &font_sz, &dpi_x, &dpi_y)) return NULL;\n    return specialize_font_descriptor(desc, font_sz, dpi_x, dpi_y);\n}\n\nstatic PyObject*\nset_allow_use_of_box_fonts(PyObject *self UNUSED, PyObject *val) {\n    allow_use_of_box_fonts = PyObject_IsTrue(val);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nsprite_idx_to_pos(PyObject *self UNUSED, PyObject *args) {\n    unsigned x, y, z, idx, xnum, ynum;\n    if (!PyArg_ParseTuple(args, \"III\", &idx, &xnum, &ynum)) return NULL;\n    sprite_index_to_pos(idx, xnum, ynum, &x, &y, &z);\n    return Py_BuildValue(\"III\", x, y, z);\n}\n\nstatic PyObject*\npyrender_box_char(PyObject *self UNUSED, PyObject *args) {\n    unsigned int ch;\n    unsigned long width, height; double dpi_x = 96., dpi_y = 96., scale = 1.;\n    if (!PyArg_ParseTuple(args, \"Ikk|ddd\", &ch, &width, &height, &scale, &dpi_x, &dpi_y)) return NULL;\n    RAII_PyObject(ans, PyBytes_FromStringAndSize(NULL, width*16 * height*16));\n    if (!ans) return NULL;\n    render_box_char(ch, (uint8_t*)PyBytes_AS_STRING(ans), width, height, dpi_x, dpi_y, scale);\n    if (_PyBytes_Resize(&ans, width * height) != 0) return NULL;\n    return Py_NewRef(ans);\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(set_font_data, METH_VARARGS),\n    METHODB(sprite_idx_to_pos, METH_VARARGS),\n    METHODB(free_font_data, METH_NOARGS),\n    METHODB(create_test_font_group, METH_VARARGS),\n    METHODB(sprite_map_set_layout, METH_VARARGS),\n    METHODB(test_sprite_position_increment, METH_NOARGS),\n    METHODB(concat_cells, METH_VARARGS),\n    METHODB(render_decoration, METH_VARARGS),\n    METHODB(set_send_sprite_to_gpu, METH_O),\n    METHODB(set_allow_use_of_box_fonts, METH_O),\n    METHODB(test_shape, METH_VARARGS),\n    METHODB(current_fonts, METH_VARARGS),\n    METHODB(test_render_line, METH_VARARGS),\n    METHODB(get_fallback_font, METH_VARARGS),\n    {\"specialize_font_descriptor\", (PyCFunction)pyspecialize_font_descriptor, METH_VARARGS, \"\"},\n    {\"render_box_char\", (PyCFunction)pyrender_box_char, METH_VARARGS, \"\"},\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nbool\ninit_fonts(PyObject *module) {\n    harfbuzz_buffer = hb_buffer_create();\n    if (harfbuzz_buffer == NULL || !hb_buffer_allocation_successful(harfbuzz_buffer) || !hb_buffer_pre_allocate(harfbuzz_buffer, 2048)) { PyErr_NoMemory(); return false; }\n    hb_buffer_set_cluster_level(harfbuzz_buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);\n#define create_feature(feature, where) {\\\n    if (!hb_feature_from_string(feature, sizeof(feature) - 1, &hb_features[where])) { \\\n        PyErr_SetString(PyExc_RuntimeError, \"Failed to create \" feature \" harfbuzz feature\"); \\\n        return false; \\\n    }}\n    create_feature(\"-liga\", LIGA_FEATURE);\n    create_feature(\"-dlig\", DLIG_FEATURE);\n    create_feature(\"-calt\", CALT_FEATURE);\n#undef create_feature\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    if (PyType_Ready(&ParsedFontFeature_Type) < 0) return 0;\n    if (PyModule_AddObject(module, \"ParsedFontFeature\", (PyObject *)&ParsedFontFeature_Type) != 0) return 0;\n    Py_INCREF(&ParsedFontFeature_Type);\n\n    return true;\n}\n"
  },
  {
    "path": "kitty/fonts.h",
    "content": "/*\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"lineops.h\"\n#include \"state.h\"\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpedantic\"\n#include <hb.h>\n#pragma GCC diagnostic pop\n\ntypedef struct {\n    uint8_t *canvas;\n    size_t width, height;\n} StringCanvas;\n\ntypedef struct FontFeatures {\n    size_t count;\n    hb_feature_t *features;\n} FontFeatures;\n\ntypedef struct ParsedFontFeature {\n    PyObject_HEAD\n\n    hb_feature_t feature;\n    Py_hash_t hashval;\n    bool hash_computed;\n} ParsedFontFeature;\n\ntypedef struct GlyphRenderInfo {\n    unsigned canvas_width, rendered_width;\n    int x;\n} GlyphRenderInfo;\n\nParsedFontFeature* parse_font_feature(const char *spec);\n\n// API that font backends need to implement\nunsigned int glyph_id_for_codepoint(const PyObject *, char_type);\nint get_glyph_width(PyObject *, glyph_index);\nbool is_glyph_empty(PyObject *, glyph_index);\nhb_font_t* harfbuzz_font_for_face(PyObject*);\nbool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE);\nFontCellMetrics cell_metrics(PyObject*);\nbool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE, GlyphRenderInfo*);\nPyObject* create_fallback_face(PyObject *base_face, const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg);\nPyObject* specialize_font_descriptor(PyObject *base_descriptor, double, double, double);\nPyObject* face_from_path(const char *path, int index, FONTS_DATA_HANDLE);\nPyObject* face_from_descriptor(PyObject*, FONTS_DATA_HANDLE);\nPyObject* iter_fallback_faces(FONTS_DATA_HANDLE fgh, ssize_t *idx);\nbool face_equals_descriptor(PyObject *face_, PyObject *descriptor);\nconst char* postscript_name_for_face(const PyObject*);\n\nvoid sprite_tracker_current_layout(FONTS_DATA_HANDLE data, unsigned int *x, unsigned int *y, unsigned int *z);\nvoid render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, const Region *src_rect, const Region *dest_rect, size_t src_stride, size_t dest_stride, pixel color_rgb);\nvoid render_line(FONTS_DATA_HANDLE, Line *line, index_type lnum, Cursor *cursor, DisableLigature, ListOfChars*);\nvoid sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len);\ntypedef void (*free_extra_data_func)(void*);\nStringCanvas render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline);\nStringCanvas render_simple_text(FONTS_DATA_HANDLE fg_, const char *text);\nbool face_apply_scaling(PyObject*face, const FONTS_DATA_HANDLE fg);\n\nbool\nadd_font_name_record(PyObject *table, uint16_t platform_id, uint16_t encoding_id, uint16_t language_id, uint16_t name_id, const char *string, uint16_t string_len);\nPyObject*\nget_best_name_from_name_table(PyObject *table, PyObject *name_id);\nPyObject*\nread_name_font_table(const uint8_t *table, size_t table_len);\nbool\nread_fvar_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output);\nbool\nread_STAT_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output);\nbool\nread_features_from_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output);\nFontFeatures* features_for_face(PyObject *);\nbool create_features_for_face(const char* psname, PyObject *features, FontFeatures* output);\nPyObject*\nfont_features_as_dict(const FontFeatures *font_features);\nbool\nhas_cell_text(bool(*has_codepoint)(const void*, char_type ch), const void* face, bool do_debug, const ListOfChars *lc);\n\nstatic inline void\nright_shift_canvas(pixel *canvas, size_t width, size_t height, size_t amt) {\n    pixel *src;\n    size_t r;\n    for (r = 0, src = canvas; r < height; r++, src += width) {\n        memmove(src + amt, src, sizeof(pixel) * (width - amt));\n        zero_at_ptr_count(src, amt);\n    }\n}\n"
  },
  {
    "path": "kitty/freetype.c",
    "content": "/*\n * freetype.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"fonts.h\"\n#include \"colors.h\"\n#include \"cleanup.h\"\n#include \"state.h\"\n#include <math.h>\n#include <structmember.h>\n#include <ft2build.h>\n#include <hb-ft.h>\n#include <cairo-ft.h>\n\n#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 7\n#define FT_Bitmap_Init FT_Bitmap_New\n#endif\n\n#include FT_BITMAP_H\n#include FT_TRUETYPE_TABLES_H\n#include FT_MULTIPLE_MASTERS_H\n#include FT_SFNT_NAMES_H\n#include FT_TYPES_H\n\ntypedef union FaceIndex {\n    struct {\n        FT_Long ttc_index : 16;\n        FT_Long variation_index : 16;\n    };\n    FT_Long val;\n} FaceIndex;\n\ntypedef struct FaceMetrics {\n    float size_in_pts;\n    unsigned int units_per_EM;\n    // The following are in font units use font_units_to_pixels_x/y to convert to pixels\n    int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;\n} FaceMetrics;\n\ntypedef struct {\n    PyObject_HEAD\n\n    FT_Face face, face_for_cairo;\n    FaceMetrics metrics;\n    int hinting, hintstyle;\n    bool is_scalable, has_color, is_variable, has_svg;\n    FT_F26Dot6 char_width, char_height;\n    double xdpi, ydpi;\n    PyObject *path;\n    long index;\n    hb_font_t *harfbuzz_font;\n    struct {\n        cairo_font_face_t *font;\n        void *buf;\n        cairo_surface_t *surface;\n        cairo_t *cr;\n        size_t width, height, stride;\n        unsigned size_in_px;\n    } cairo;\n    hb_codepoint_t space_glyph_id;\n    void *extra_data;\n    free_extra_data_func free_extra_data;\n    PyObject *name_lookup_table;\n    FontFeatures font_features;\n    unsigned short dark_palette_index, light_palette_index, palettes_scanned;\n} Face;\nPyTypeObject Face_Type;\n\nstatic PyObject* FreeType_Exception = NULL;\n\nvoid\nset_freetype_error(const char* prefix, int err_code) {\n    int i = 0;\n#undef FTERRORS_H_\n#undef __FTERRORS_H__\n#define FT_ERRORDEF( e, v, s )  { e, s },\n#define FT_ERROR_START_LIST     {\n#define FT_ERROR_END_LIST       { 0, NULL } };\n\n    static const struct {\n        int          err_code;\n        const char*  err_msg;\n    } ft_errors[] =\n\n#ifdef FT_ERRORS_H\n#include FT_ERRORS_H\n#else\n    FT_ERROR_START_LIST FT_ERROR_END_LIST\n#endif\n\n    while(ft_errors[i].err_msg != NULL) {\n        if (ft_errors[i].err_code == err_code) {\n            PyErr_Format(FreeType_Exception, \"%s %s\", prefix, ft_errors[i].err_msg);\n            return;\n        }\n        i++;\n    }\n    PyErr_Format(FreeType_Exception, \"%s (error code: %d)\", prefix, err_code);\n}\n\nstatic FT_Library  library;\n\nFT_Library\nfreetype_library(void) { return library; }\n\nstatic int\nfont_units_to_pixels_y(Face *self, int x) {\n    return (int)ceil((double)FT_MulFix(x, self->face->size->metrics.y_scale) / 64.0);\n}\n\nstatic int\nfont_units_to_pixels_x(Face *self, int x) {\n    return (int)ceil((double)FT_MulFix(x, self->face->size->metrics.x_scale) / 64.0);\n}\n\n\nstatic int\nget_load_flags(int hinting, int hintstyle, int base) {\n    int flags = base;\n    if (hinting) {\n        if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL;\n        else if (0 < hintstyle) flags |= FT_LOAD_TARGET_LIGHT;\n    } else flags |= FT_LOAD_NO_HINTING;\n    return flags;\n}\n\n\nstatic bool\nload_glyph(Face *self, int glyph_index, int load_type) {\n    int flags = get_load_flags(self->hinting, self->hintstyle, load_type);\n    int error = FT_Load_Glyph(self->face, glyph_index, flags);\n    if (error) {\n        char buf[256];\n        snprintf(buf, sizeof(buf) - 1, \"Failed to load glyph_index=%d load_type=%d, with error:\", glyph_index, load_type);\n        set_freetype_error(buf, error); return false;\n    }\n    return true;\n}\n\nstatic unsigned int\nget_height_for_char(Face *self, char ch) {\n    unsigned int ans = 0;\n    int glyph_index = FT_Get_Char_Index(self->face, ch);\n    if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {\n        unsigned int baseline = font_units_to_pixels_y(self, self->metrics.ascender);\n        FT_GlyphSlotRec *glyph = self->face->glyph;\n        FT_Bitmap *bm = &glyph->bitmap;\n        if (glyph->bitmap_top <= 0 || (glyph->bitmap_top > 0 && (unsigned int)glyph->bitmap_top < baseline)) {\n            ans = baseline - glyph->bitmap_top + bm->rows;\n        }\n    }\n    return ans;\n}\n\nstatic unsigned int\ncalc_cell_height(Face *self, bool for_metrics) {\n    unsigned int ans = font_units_to_pixels_y(self, self->metrics.height);\n    if (for_metrics) {\n        unsigned int underscore_height = get_height_for_char(self, '_');\n        if (underscore_height > ans) {\n            if (global_state.debug_font_fallback) printf(\n                \"Increasing cell height by %u pixels to work around buggy font that renders underscore outside the bounding box\\n\", underscore_height - ans);\n            return underscore_height;\n        }\n    }\n    return ans;\n}\n\nstatic bool\nset_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, double xdpi_, double ydpi_, unsigned int desired_height, unsigned int cell_height) {\n    FT_UInt xdpi = (FT_UInt)xdpi_, ydpi = (FT_UInt)ydpi_;\n    int error = FT_Set_Char_Size(self->face, 0, char_height, xdpi, ydpi);\n    if (!error) {\n        self->char_width = char_width; self->char_height = char_height;\n        self->xdpi = xdpi_; self->ydpi = ydpi_;\n    } else {\n        if (!self->is_scalable && self->face->num_fixed_sizes > 0) {\n            int32_t min_diff = INT32_MAX;\n            if (desired_height == 0) desired_height = cell_height;\n            if (desired_height == 0) {\n                desired_height = (unsigned int)ceil(((double)char_height / 64.) * (double)ydpi / 72.);\n                desired_height += (unsigned int)ceil(0.2 * desired_height);\n            }\n            FT_Int strike_index = -1;\n            for (FT_Int i = 0; i < self->face->num_fixed_sizes; i++) {\n                int h = self->face->available_sizes[i].height;\n                int32_t diff = h < (int32_t)desired_height ? (int32_t)desired_height - h : h - (int32_t)desired_height;\n                if (diff < min_diff) {\n                    min_diff = diff;\n                    strike_index = i;\n                }\n            }\n            if (strike_index > -1) {\n                error = FT_Select_Size(self->face, strike_index);\n                if (error) { set_freetype_error(\"Failed to set char size for non-scalable font, with error:\", error); return false; }\n                self->xdpi = xdpi_; self->ydpi = ydpi_;\n                return true;\n            }\n        }\n        set_freetype_error(\"Failed to set char size, with error:\", error);\n        return false;\n    }\n    if (self->harfbuzz_font != NULL) hb_ft_font_changed(self->harfbuzz_font);\n    return !error;\n}\n\nbool\nset_size_for_face(PyObject *s, unsigned int desired_height, bool force, FONTS_DATA_HANDLE fg) {\n    Face *self = (Face*)s;\n    FT_F26Dot6 w = (FT_F26Dot6)(ceil(fg->font_sz_in_pts * 64.0));\n    FT_UInt xdpi = (FT_UInt)fg->logical_dpi_x, ydpi = (FT_UInt)fg->logical_dpi_y;\n    if (!force && (self->char_width == w && self->char_height == w && self->xdpi == xdpi && self->ydpi == ydpi)) return true;\n    self->metrics.size_in_pts = (float)fg->font_sz_in_pts;\n    return set_font_size(self, w, w, fg->logical_dpi_x, fg->logical_dpi_y, desired_height, fg->fcm.cell_height);\n}\n\nstatic PyObject*\nset_size(Face *self, PyObject *args) {\n    double font_sz_in_pts, dpi_x, dpi_y;\n    if (!PyArg_ParseTuple(args, \"ddd\", &font_sz_in_pts, &dpi_x, &dpi_y)) return NULL;\n    FT_F26Dot6 w = (FT_F26Dot6)(ceil(font_sz_in_pts * 64.0));\n    if (self->char_width == w && self->char_height == w && self->xdpi == dpi_x && self->ydpi == dpi_y) { Py_RETURN_NONE; }\n    self->metrics.size_in_pts = (float)font_sz_in_pts;\n    if (!set_font_size(self, w, w, dpi_x, dpi_y, 0, 0)) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic void\ncopy_face_metrics(Face *self) {\n#define CPY(n) self->metrics.n = self->face->n;\n    CPY(units_per_EM); CPY(ascender); CPY(descender); CPY(height); CPY(max_advance_width); CPY(max_advance_height); CPY(underline_position); CPY(underline_thickness);\n#undef CPY\n}\n\nbool\nface_apply_scaling(PyObject *f, const FONTS_DATA_HANDLE fg) {\n    Face *self = (Face*)f;\n    if (set_size_for_face(f, 0, false, fg)) {\n        if (self->harfbuzz_font) hb_ft_font_changed(self->harfbuzz_font);\n        copy_face_metrics(self);\n        return true;\n    }\n    return false;\n}\n\nstatic bool\ninit_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, long index, FONTS_DATA_HANDLE fg) {\n    copy_face_metrics(self);\n    self->index = index;\n    self->is_scalable = FT_IS_SCALABLE(self->face);\n    self->has_color = FT_HAS_COLOR(self->face);\n    self->is_variable = FT_HAS_MULTIPLE_MASTERS(self->face);\n#ifdef FT_HAS_SVG\n    self->has_svg = FT_HAS_SVG(self->face);\n#else\n    self->has_svg = false;\n#endif\n    self->hinting = hinting; self->hintstyle = hintstyle;\n    if (fg && !set_size_for_face((PyObject*)self, 0, false, fg)) return false;\n    self->harfbuzz_font = hb_ft_font_create(self->face, NULL);\n    if (self->harfbuzz_font == NULL) { PyErr_NoMemory(); return false; }\n    hb_ft_font_set_load_flags(self->harfbuzz_font, get_load_flags(self->hinting, self->hintstyle, FT_LOAD_DEFAULT));\n    FT_Reference_Face(self->face);\n\n    TT_OS2 *os2 = (TT_OS2*)FT_Get_Sfnt_Table(self->face, FT_SFNT_OS2);\n    if (os2 != NULL) {\n      self->metrics.strikethrough_position = os2->yStrikeoutPosition;\n      self->metrics.strikethrough_thickness = os2->yStrikeoutSize;\n    }\n\n    self->path = path; Py_INCREF(self->path);\n    self->space_glyph_id = glyph_id_for_codepoint((PyObject*)self, ' ');\n    return true;\n}\n\nstatic void*\nset_load_error(const char *path, int error) {\n    char buf[2048];\n    snprintf(buf, sizeof(buf), \"Failed to load face from path: %s with error:\", path);\n    set_freetype_error(buf, error);\n    return NULL;\n}\n\nbool\nface_equals_descriptor(PyObject *face_, PyObject *descriptor) {\n    Face *face = (Face*)face_;\n    PyObject *t = PyDict_GetItemString(descriptor, \"path\");\n    if (!t) return false;\n    if (PyObject_RichCompareBool(face->path, t, Py_EQ) != 1) return false;\n    t = PyDict_GetItemString(descriptor, \"index\");\n    if (t && PyLong_AsLong(t) != face->face->face_index) return false;\n    return true;\n}\n\nstatic char* get_variation_as_string(Face *self);\n\nPyObject*\nface_from_descriptor(PyObject *descriptor, FONTS_DATA_HANDLE fg) {\n#define D(key, conv, missing_ok) { \\\n    PyObject *t = PyDict_GetItemString(descriptor, #key); \\\n    if (t == NULL) { \\\n        if (!missing_ok) { PyErr_SetString(PyExc_KeyError, \"font descriptor is missing the key: \" #key); return NULL; } \\\n    } else key = conv(t); \\\n}\n    const char *path = NULL;\n    long index = 0;\n    bool hinting = false;\n    long hint_style = 0;\n    D(path, PyUnicode_AsUTF8, false);\n    D(index, PyLong_AsLong, true);\n    D(hinting, PyObject_IsTrue, true);\n    D(hint_style, PyLong_AsLong, true);\n#undef D\n    RAII_PyObject(retval, Face_Type.tp_alloc(&Face_Type, 0));\n    Face *self = (Face *)retval;\n    if (retval != NULL) {\n        int error;\n        if ((error = FT_New_Face(library, path, index, &(self->face)))) { self->face = NULL; return set_load_error(path, error); }\n        if (!init_ft_face(self, PyDict_GetItemString(descriptor, \"path\"), hinting, hint_style, index, fg)) { Py_CLEAR(retval); return NULL; }\n        PyObject *ns = PyDict_GetItemString(descriptor, \"named_style\");\n        if (ns) {\n            unsigned long index = PyLong_AsUnsignedLong(ns);\n            if (PyErr_Occurred()) return NULL;\n            if ((error = FT_Set_Named_Instance(self->face, index + 1))) return set_load_error(path, error);\n        }\n        PyObject *axes = PyDict_GetItemString(descriptor, \"axes\");\n        Py_ssize_t sz;\n        if (axes && (sz = PyTuple_GET_SIZE(axes))) {\n            RAII_ALLOC(FT_Fixed, coords, malloc(sizeof(FT_Fixed) * sz));\n            for (Py_ssize_t i = 0; i < sz; i++) {\n                PyObject *t = PyTuple_GET_ITEM(axes, i);\n                double val = PyFloat_AsDouble(t);\n                if (PyErr_Occurred()) return NULL;\n                coords[i] = (FT_Fixed)(val * 65536.0);\n            }\n            if ((error = FT_Set_Var_Design_Coordinates(self->face, sz, coords))) return set_load_error(path, error);\n        }\n        if (!create_features_for_face(postscript_name_for_face((PyObject*)self), PyDict_GetItemString(descriptor, \"features\"), &self->font_features)) return NULL;\n    }\n    Py_XINCREF(retval);\n    return retval;\n}\n\nFontFeatures*\nfeatures_for_face(PyObject *s) { return &((Face*)s)->font_features; }\n\nstatic PyObject*\nnew(PyTypeObject *type UNUSED, PyObject *args, PyObject *kw) {\n    const char *path = NULL;\n    long index = 0;\n    PyObject *descriptor = NULL;\n\n    static char *kwds[] = {\"descriptor\", \"path\", \"index\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"|Osi\", kwds, &descriptor, &path, &index)) return NULL;\n    if (descriptor) {\n        return face_from_descriptor(descriptor, NULL);\n    }\n    if (path) return face_from_path(path, index, NULL);\n    PyErr_SetString(PyExc_TypeError, \"Must specify either path or descriptor\");\n    return NULL;\n}\n\nFT_Face\nnative_face_from_path(const char *path, int index) {\n    int error;\n    FT_Face ans;\n    error = FT_New_Face(library, path, index, &ans);\n    if (error) return set_load_error(path, error);\n    return ans;\n}\n\nPyObject*\nface_from_path(const char *path, int index, FONTS_DATA_HANDLE fg) {\n    Face *ans = (Face*)Face_Type.tp_alloc(&Face_Type, 0);\n    if (ans == NULL) return NULL;\n    int error;\n    error = FT_New_Face(library, path, index, &ans->face);\n    if (error) { ans->face = NULL; return set_load_error(path, error); }\n    RAII_PyObject(pypath, PyUnicode_FromString(path));\n    if (!pypath) return NULL;\n    if (!init_ft_face(ans, pypath, true, 3, index, fg)) { Py_CLEAR(ans); return NULL; }\n    return (PyObject*)ans;\n}\n\nstatic inline void cleanup_ftmm(FT_MM_Var **p) { if (*p) FT_Done_MM_Var(library, *p); *p = NULL; }\n\nstatic const char*\ntag_to_string(uint32_t tag, uint8_t bytes[5]) {\n    bytes[0] = (tag >> 24) & 0xff;\n    bytes[1] = (tag >> 16) & 0xff;\n    bytes[2] = (tag >> 8) & 0xff;\n    bytes[3] = (tag) & 0xff;\n    bytes[4] = 0;\n    return (const char*)bytes;\n}\n\n#define RAII_FTMMVar(name) __attribute__((cleanup(cleanup_ftmm))) FT_MM_Var *name = NULL\n\nstatic void free_cairo(Face *self);\n\nstatic void\ndealloc(Face* self) {\n    if (self->harfbuzz_font) hb_font_destroy(self->harfbuzz_font);\n    FT_Done_Face(self->face);\n    free_cairo(self);\n    if (self->extra_data && self->free_extra_data) self->free_extra_data(self->extra_data);\n    free(self->font_features.features);\n    Py_CLEAR(self->path);\n    Py_CLEAR(self->name_lookup_table);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic PyObject *\nrepr(Face *self) {\n    const char *ps_name = FT_Get_Postscript_Name(self->face);\n#define B(x) ((x) ? Py_True : Py_False)\n    FaceIndex instance;\n    instance.val = self->face->face_index;\n    return PyUnicode_FromFormat(\n        \"Face(family=%s style=%s ps_name=%s path=%S ttc_index=%d variant=%S named_instance=%S scalable=%S color=%S)\",\n        self->face->family_name ? self->face->family_name : \"\", self->face->style_name ? self->face->style_name : \"\",\n        ps_name ? ps_name: \"\", self->path, instance.ttc_index,\n        B(FT_IS_VARIATION(self->face)), B(FT_IS_NAMED_INSTANCE(self->face)), B(self->is_scalable), B(self->has_color)\n    );\n#undef B\n}\n\nconst char*\npostscript_name_for_face(const PyObject *face_) {\n    const Face *self = (const Face*)face_;\n    const char *ps_name = FT_Get_Postscript_Name(self->face);\n    return ps_name ? ps_name : \"\";\n}\n\nstatic unsigned int\ncalc_cell_width(Face *self) {\n    unsigned int ans = 0;\n    for (char_type i = 32; i < 128; i++) {\n        int glyph_index = FT_Get_Char_Index(self->face, i);\n        if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {\n            ans = MAX(ans, (unsigned int)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));\n        }\n    }\n    if (!ans) ans = MAX(1u, (unsigned int)ceilf(self->face->size->metrics.max_advance / 64.f));\n    return ans;\n}\n\n\nFontCellMetrics\ncell_metrics(PyObject *s) {\n    Face *self = (Face*)s;\n    FontCellMetrics ans = {0};\n    ans.cell_width = calc_cell_width(self);\n    ans.cell_height = calc_cell_height(self, true);\n    ans.baseline = font_units_to_pixels_y(self, self->metrics.ascender);\n    ans.underline_position = MIN(ans.cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->metrics.ascender - self->metrics.underline_position)));\n    ans.underline_thickness = MAX(1, font_units_to_pixels_y(self, self->metrics.underline_thickness));\n\n    if (self->metrics.strikethrough_position != 0) {\n      ans.strikethrough_position = MIN(ans.cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->metrics.ascender - self->metrics.strikethrough_position)));\n    } else {\n      ans.strikethrough_position = (unsigned int)floor(ans.baseline * 0.65);\n    }\n    if (self->metrics.strikethrough_thickness > 0) {\n      ans.strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->metrics.strikethrough_thickness));\n    } else {\n      ans.strikethrough_thickness = ans.underline_thickness;\n    }\n    return ans;\n}\n\nunsigned int\nglyph_id_for_codepoint(const PyObject *s, char_type cp) {\n    return s ? FT_Get_Char_Index(((Face*)s)->face, cp) : 0;\n}\n\ntypedef enum { NOT_COLORED, CBDT_COLORED, COLR_V0_COLORED, COLR_V1_COLORED } GlyphColorType;\n\nstatic bool\nis_colrv0_glyph (Face *self, int glyph_id) {\n    FT_LayerIterator iterator = {0}; FT_UInt layer_glyph_index = 0, layer_color_index = 0;\n    return FT_Get_Color_Glyph_Layer(self->face, glyph_id, &layer_glyph_index, &layer_color_index, &iterator);\n}\n\nstatic bool\nis_colrv1_glyph(Face *self, int glyph_id) {\n    FT_OpaquePaint paint = {0};\n    return FT_Get_Color_Glyph_Paint(self->face, glyph_id, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &paint);\n}\n\nstatic bool\nis_colored_cbdt_glyph(Face *self, int glyph_id) {\n    FT_Error err = FT_Load_Glyph(self->face, glyph_id, get_load_flags(self->hinting, self->hintstyle, FT_LOAD_DEFAULT | FT_LOAD_COLOR));\n    if (err) return false;\n    return self->face->glyph->format == FT_GLYPH_FORMAT_BITMAP && self->face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA;\n}\n\nstatic GlyphColorType\nglyph_color_type(Face *self, int glyph_id) {\n    if (is_colrv1_glyph(self, glyph_id)) return COLR_V1_COLORED;\n    if (is_colrv0_glyph(self, glyph_id)) return COLR_V0_COLORED;\n    if (is_colored_cbdt_glyph(self, glyph_id)) return CBDT_COLORED;\n    return NOT_COLORED;\n}\n\nbool\nis_glyph_empty(PyObject *s, glyph_index g) {\n    Face *self = (Face*)s;\n    if (!load_glyph(self, g, FT_LOAD_DEFAULT)) { PyErr_Print(); return false; }\n#define M self->face->glyph->metrics\n    /* printf(\"glyph: %u horiBearingX: %ld horiBearingY: %ld width: %ld height: %ld\\n\", g, M.horiBearingX, M.horiBearingY, M.width, M.height); */\n    return M.width == 0;\n#undef M\n}\n\nint\nget_glyph_width(PyObject *s, glyph_index g) {\n    Face *self = (Face*)s;\n    if (!load_glyph(self, g, FT_LOAD_DEFAULT)) { PyErr_Print(); return 0; }\n#define M self->face->glyph->metrics\n#define B self->face->glyph->bitmap\n    // printf(\"glyph: %u bitmap.width: %d bitmap.rows: %d horiAdvance: %ld horiBearingX: %ld horiBearingY: %ld vertBearingX: %ld vertBearingY: %ld vertAdvance: %ld width: %ld height: %ld\\n\", g, B.width, B.rows, M.horiAdvance, M.horiBearingX, M.horiBearingY, M.vertBearingX, M.vertBearingY, M.vertAdvance, M.width, M.height);\n    return B.width ? (int)B.width : (int)(M.width / 64);\n#undef M\n#undef B\n}\n\nhb_font_t*\nharfbuzz_font_for_face(PyObject *self) { return ((Face*)self)->harfbuzz_font; }\n\n\ntypedef struct {\n    unsigned char* buf;\n    size_t start_x, width, stride;\n    size_t rows;\n    FT_Pixel_Mode pixel_mode;\n    bool needs_free;\n    unsigned int factor, right_edge;\n    int bitmap_left, bitmap_top;\n} ProcessedBitmap;\n\nstatic void\nfree_processed_bitmap(ProcessedBitmap *bm) {\n    if (bm->needs_free) {\n        bm->needs_free = false;\n        free(bm->buf); bm->buf = NULL;\n    }\n}\n\nstatic void\ntrim_borders(ProcessedBitmap *ans, size_t extra) {\n    bool column_has_text = false;\n\n    // Trim empty columns from the right side of the bitmap\n    for (ssize_t x = ans->width - 1; !column_has_text && x > -1 && extra > 0; x--) {\n        for (size_t y = 0; y < ans->rows && !column_has_text; y++) {\n            if (ans->buf[x + y * ans->stride] > 200) column_has_text = true;\n        }\n        if (!column_has_text) { ans->width--; extra--; }\n    }\n\n    // Remove any remaining extra columns from the left edge of the bitmap\n    ans->start_x = extra;\n    ans->width -= extra;\n}\n\nstatic void\npopulate_processed_bitmap(FT_GlyphSlotRec *slot, FT_Bitmap *bitmap, ProcessedBitmap *ans, bool copy_buf) {\n    ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;\n    ans->rows = bitmap->rows;\n    if (copy_buf) {\n        ans->buf = malloc(ans->rows * ans->stride);\n        if (!ans->buf) fatal(\"Out of memory\");\n        ans->needs_free = true;\n        memcpy(ans->buf, bitmap->buffer, ans->rows * ans->stride);\n    } else ans->buf = bitmap->buffer;\n    ans->start_x = 0; ans->width = bitmap->width;\n    ans->pixel_mode = bitmap->pixel_mode;\n    ans->bitmap_top = slot->bitmap_top; ans->bitmap_left = slot->bitmap_left;\n}\n\nbool\nfreetype_convert_mono_bitmap(FT_Bitmap *src, FT_Bitmap *dest) {\n    FT_Bitmap_Init(dest);\n    // This also sets pixel_mode to FT_PIXEL_MODE_GRAY so we don't have to\n    int error = FT_Bitmap_Convert(library, src, dest, 1);\n    if (error) { set_freetype_error(\"Failed to convert bitmap, with error:\", error); return false; }\n    // Normalize gray levels to the range [0..255]\n    dest->num_grays = 256;\n    unsigned int stride = dest->pitch < 0 ? -dest->pitch : dest->pitch;\n    for (unsigned i = 0; i < (unsigned)dest->rows; ++i) {\n        // We only have 2 levels\n        for (unsigned j = 0; j < (unsigned)dest->width; ++j) dest->buffer[i * stride + j] *= 255;\n    }\n    return true;\n}\n\nstatic bool\nrender_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, bool bold, bool italic, bool rescale, FONTS_DATA_HANDLE fg) {\n    if (!load_glyph(self, glyph_id, FT_LOAD_RENDER)) return false;\n    unsigned int max_width = cell_width * num_cells;\n\n    // Embedded bitmap glyph?\n    if (self->face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {\n        FT_Bitmap bitmap;\n        freetype_convert_mono_bitmap(&self->face->glyph->bitmap, &bitmap);\n        populate_processed_bitmap(self->face->glyph, &bitmap, ans, true);\n        FT_Bitmap_Done(library, &bitmap);\n    } else {\n        populate_processed_bitmap(self->face->glyph, &self->face->glyph->bitmap, ans, false);\n    }\n\n    if (ans->width > max_width) {\n        size_t extra = ans->width - max_width;\n        if (italic && extra < cell_width / 2) {\n            trim_borders(ans, extra);\n        } else if (extra == 2 && num_cells == 1) {\n            // there exist fonts that have bitmaps just a couple of pixels\n            // wider than their advances, rather than rescale, which looks\n            // bad, we just crop the bitmap on the right. See https://github.com/kovidgoyal/kitty/issues/352\n        } else if (rescale && self->is_scalable && extra > 1) {\n            FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;\n            float ar = (float)max_width / (float)ans->width;\n            if (set_font_size(self, (FT_F26Dot6)((float)self->char_width * ar), (FT_F26Dot6)((float)self->char_height * ar), self->xdpi, self->ydpi, 0, fg->fcm.cell_height)) {\n                free_processed_bitmap(ans);\n                if (!render_bitmap(self, glyph_id, ans, cell_width, cell_height, num_cells, bold, italic, false, fg)) return false;\n                if (!set_font_size(self, char_width, char_height, self->xdpi, self->ydpi, 0, fg->fcm.cell_height)) return false;\n            } else return false;\n        }\n    }\n    return true;\n}\n\nint\ndownsample_32bit_image(uint8_t *src, unsigned src_width, unsigned src_height, unsigned src_stride, uint8_t *dest, unsigned dest_width, unsigned dest_height) {\n    // Downsample using a simple area averaging algorithm. Could probably do\n    // better with bi-cubic or lanczos, but at these small sizes I don't think\n    // it matters\n    float ratio = MAX((float)src_width / dest_width, (float)src_height / dest_height);\n    int factor = (int)ceilf(ratio);\n    uint8_t *d = dest;\n    for (unsigned int i = 0, sr = 0; i < dest_height; i++, sr += factor) {\n        for (unsigned int j = 0, sc = 0; j < dest_width; j++, sc += factor, d += 4) {\n            // calculate area average\n            unsigned int r=0, g=0, b=0, a=0, count=0;\n            for (unsigned int y=sr; y < MIN(sr + factor, src_height); y++) {\n                uint8_t *p = src + (y * src_stride) + sc * 4;\n                for (unsigned int x=sc; x < MIN(sc + factor, src_width); x++, count++) {\n                    b += *(p++); g += *(p++); r += *(p++); a += *(p++);\n                }\n            }\n            if (count) {\n                d[0] = b / count; d[1] = g / count; d[2] = r / count; d[3] = a / count;\n            }\n        }\n    }\n    return factor;\n}\n\nstatic bool\nset_cairo_exception(const char *msg, cairo_status_t s) {\n    PyErr_Format(FreeType_Exception, \"cairo error: %s: %s\", msg, cairo_status_to_string(s));\n    return false;\n}\n\nstatic void\nfree_cairo_surface_data(Face *self) {\n    if (self->cairo.cr) cairo_destroy(self->cairo.cr);\n    if (self->cairo.surface) cairo_surface_destroy(self->cairo.surface);\n    if (self->cairo.buf) free(self->cairo.buf);\n}\n\nstatic void\nfree_cairo(Face *self) {\n    free_cairo_surface_data(self);\n    if (self->cairo.font) cairo_font_face_destroy(self->cairo.font);\n    zero_at_ptr(&self->cairo);\n}\n\nstatic void\ncairo_done_ft_face(void* x) { if (x) FT_Done_Face(x); }\n\nstatic bool\nis_color_dark(color_type c) {\n    ARGB32 bg = {.val=c};\n    return rgb_luminance(bg) / 255.0 < 0.5;\n}\n\nstatic unsigned short\nget_preferred_palette_index(Face *self) {\n    if (!self->palettes_scanned) {\n        self->palettes_scanned = 1;\n        self->dark_palette_index = CAIRO_COLOR_PALETTE_DEFAULT; self->light_palette_index = CAIRO_COLOR_PALETTE_DEFAULT;\n        FT_Palette_Data palette_data;\n        FT_Error error = FT_Palette_Data_Get(self->face, &palette_data);\n        if (error) log_error(\"Could not retrieve palette data for font from FreeType\");\n        else if (palette_data.palette_flags) {\n            for (FT_UShort i = 0; i < palette_data.num_palettes; i++) {\n                FT_UShort flags = palette_data.palette_flags[i];\n                if (flags & FT_PALETTE_FOR_DARK_BACKGROUND) self->dark_palette_index = i;\n                else if (flags & FT_PALETTE_FOR_DARK_BACKGROUND) self->light_palette_index = i;\n            }\n        }\n    }\n    return is_color_dark(OPT(background)) ? self->dark_palette_index : self->light_palette_index;\n}\n\nstatic char*\nget_variation_as_string(Face *self) {\n    RAII_FTMMVar(mm);\n    FT_Error err;\n    if ((err = FT_Get_MM_Var(self->face, &mm))) return NULL;\n    RAII_ALLOC(FT_Fixed, coords, malloc(mm->num_axis * sizeof(FT_Fixed))); if (!coords) return NULL;\n    if ((err = FT_Get_Var_Design_Coordinates(self->face, mm->num_axis, coords))) return NULL;\n    RAII_ALLOC(char, buf, NULL);\n    uint8_t tag[5];\n    size_t pos = 0, sz = 0, n, bufsz;\n    for (FT_UInt i = 0; i < mm->num_axis; i++) {\n        double val = coords[i] / 65536.0;\n        tag_to_string(mm->axis[i].tag, tag);\n        if (sz - pos < 32) {\n            sz += 4096; buf = realloc(buf, sz); if (!buf) return NULL;\n        }\n        bufsz = sz - pos - 1;\n        if ((long)val == val) n = snprintf(buf + pos, bufsz, \"%s=%ld,\", tag, (long)val);\n        else n = snprintf(buf + pos, bufsz, \"%s=%.4f,\", tag, val);\n        if (n < bufsz) pos += n;\n    }\n    char *ans = NULL;\n    if (buf) {\n        buf[pos] = 0; if (pos && buf[pos-1] == ',') buf[pos-1] = 0;\n        ans = buf; buf = NULL;\n    }\n    return ans;\n}\n\nstatic void\nset_variation_for_cairo(Face *self, cairo_font_options_t *opts) {\n    RAII_ALLOC(char, buf, get_variation_as_string(self));\n    cairo_font_options_set_variations(opts, buf ? buf : \"\");\n}\n\n\nstatic bool\nensure_cairo_resources(Face *self, size_t width, size_t height) {\n    if (!self->cairo.font) {\n        const char *path = PyUnicode_AsUTF8(self->path);\n        int error;\n        if ((error = FT_New_Face(library, path, self->index, &self->face_for_cairo))) {\n            self->face_for_cairo = NULL; return set_load_error(path, error);\n        }\n        self->cairo.font = cairo_ft_font_face_create_for_ft_face(self->face_for_cairo, 0);\n        if (!self->cairo.font) { FT_Done_Face(self->face_for_cairo); self->face_for_cairo = NULL; PyErr_NoMemory(); return false; }\n        // Sadly cairo does not use FT_Reference_Face https://lists.cairographics.org/archives/cairo/2015-March/026023.html\n        // so we have to let cairo manage lifetime of the FT_Face\n        static const cairo_user_data_key_t key;\n        cairo_status_t status = cairo_font_face_set_user_data(self->cairo.font, &key, self->face_for_cairo, cairo_done_ft_face);\n        if (status) {\n            FT_Done_Face(self->face_for_cairo); self->face_for_cairo = NULL;\n            PyErr_Format(PyExc_RuntimeError, \"Failed to set cairo font destructor with error: %s\", cairo_status_to_string(status));\n            return false;\n        }\n        self->cairo.size_in_px = 0;\n    }\n    size_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);\n    if (stride * height > self->cairo.stride * self->cairo.height) {\n        free_cairo_surface_data(self);\n        self->cairo.width = 0; self->cairo.height = 0;\n        self->cairo.stride = stride;\n        int ret = posix_memalign(&self->cairo.buf, 64, self->cairo.stride * height);\n        switch (ret) {\n            case 0: break;\n            case ENOMEM: PyErr_NoMemory(); return false;\n            case EINVAL: PyErr_SetString(FreeType_Exception, \"Invalid alignment for cairo surface buffer: 64\"); return false;\n            default: PyErr_SetString(FreeType_Exception, \"Unknown error when calling posix_memalign to create ciro surface buffer\"); return false;\n        }\n        self->cairo.surface = cairo_image_surface_create_for_data(self->cairo.buf, CAIRO_FORMAT_ARGB32, width, height, self->cairo.stride);\n        if (!self->cairo.surface) { PyErr_NoMemory(); return false; }\n        self->cairo.cr = cairo_create(self->cairo.surface);\n        if (!self->cairo.cr) { PyErr_NoMemory(); return false; }\n        cairo_set_font_face(self->cairo.cr, self->cairo.font);\n        self->cairo.width = width; self->cairo.height = height; self->cairo.size_in_px = 0;\n        cairo_font_options_t *opts = cairo_font_options_create();\n        cairo_status_t s;\n#define check(msg) \\\n        if ((s = cairo_font_options_status(opts)) != CAIRO_STATUS_SUCCESS) { \\\n            cairo_font_options_destroy(opts); \\\n            return set_cairo_exception(msg, s); \\\n        }\n        check(\"Failed to create cairo font options\");\n        cairo_hint_style_t h = CAIRO_HINT_STYLE_NONE;\n        if (self->hinting) {\n            switch(self->hintstyle) {\n                case 0: break;\n                case 1: h = CAIRO_HINT_STYLE_SLIGHT; break;\n                case 2: h = CAIRO_HINT_STYLE_MEDIUM; break;\n                case 3: h = CAIRO_HINT_STYLE_FULL; break;\n                default: h = CAIRO_HINT_STYLE_MEDIUM; break;\n            }\n        }\n        cairo_font_options_set_hint_style(opts, h); check(\"Failed to set cairo hintstyle\");\n        cairo_font_options_set_color_palette(opts, get_preferred_palette_index(self)); check(\"Failed to set cairo palette index\");\n        set_variation_for_cairo(self, opts); check(\"Failed to set cairo font variations\");\n        cairo_set_font_options(self->cairo.cr, opts);\n        cairo_font_options_destroy(opts);\n#undef check\n    }\n    return true;\n}\n\nstatic long\npt_to_px(double pt, double dpi) {\n    return ((long)round((pt * (dpi / 72.0))));\n}\n\nstatic void\nset_cairo_font_size(Face *self, double size_in_pts) {\n    unsigned sz_px = pt_to_px(size_in_pts, (self->xdpi + self->ydpi) / 2.0);\n    if (self->cairo.size_in_px == sz_px) return;\n    cairo_set_font_size(self->cairo.cr, sz_px);\n    self->cairo.size_in_px = sz_px;\n}\n\nstatic cairo_scaled_font_t*\nfit_cairo_glyph(Face *self, cairo_glyph_t *g, cairo_text_extents_t *bb, cairo_scaled_font_t *sf, unsigned width, unsigned height) {\n    while (self->cairo.size_in_px > 2 && (bb->width > width || bb->height > height)) {\n        double ratio = MIN(width / bb->width, height / bb->height);\n        unsigned sz = (unsigned)(ratio * self->cairo.size_in_px);\n        if (sz >= self->cairo.size_in_px) sz = self->cairo.size_in_px - 2;\n        cairo_set_font_size(self->cairo.cr, sz);\n        sf = cairo_get_scaled_font(self->cairo.cr);\n        cairo_scaled_font_glyph_extents(sf, g, 1, bb);\n        self->cairo.size_in_px = sz;\n    }\n    return sf;\n}\n\nstatic bool\nrender_glyph_with_cairo(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned width, unsigned height, ARGB32 fg, unsigned cell_baseline) {\n    cairo_glyph_t g = {.index=glyph_id};\n    cairo_text_extents_t bb = {0};\n    if (!ensure_cairo_resources(self, MAX(width, 256u), MAX(height, 256u))) return false;\n    set_cairo_font_size(self, self->metrics.size_in_pts);\n    cairo_scaled_font_t *sf = cairo_get_scaled_font(self->cairo.cr);\n    cairo_scaled_font_glyph_extents(sf, &g, 1, &bb);\n    cairo_font_extents_t fm;\n    if (!width || !height) {\n        cairo_scaled_font_extents(sf, &fm);\n        width = (unsigned)ceil(fm.max_x_advance); height = (unsigned)ceil(fm.height);\n        return render_glyph_with_cairo(self, glyph_id, ans, width, height, fg, cell_baseline);\n    }\n    sf = fit_cairo_glyph(self, &g, &bb, sf, width, height);\n    cairo_scaled_font_extents(sf, &fm);\n    g.y = fm.ascent;\n    memset(self->cairo.buf, 0, self->cairo.stride * self->cairo.height);\n    cairo_set_source_rgba(self->cairo.cr, fg.r / 255., fg.g / 255., fg.b / 255., fg.a / 255.);\n    cairo_text_extents_t extents;\n    cairo_glyph_extents(self->cairo.cr, &g, 1, &extents);\n    cairo_show_glyphs(self->cairo.cr, &g, 1);\n    cairo_surface_flush(self->cairo.surface);\n#if 0\n        printf(\"canvas: %ux%u glyph: %.1fx%.1f x_bearing: %.1f y_bearing: %.1f cell_baseline: %u font_baseline: %.1f\\n\",\n                width, height, bb.width, bb.height, bb.x_bearing, bb.y_bearing, cell_baseline, fm.ascent);\n        cairo_status_t s = cairo_surface_write_to_png(cairo_get_target(self->cairo.cr), \"/tmp/glyph.png\");\n        if (s) fprintf(stderr, \"Failed to write to PNG with error: %s\", cairo_status_to_string(s));\n#endif\n    ans->pixel_mode = FT_PIXEL_MODE_MAX;  // place_bitmap_in_canvas() takes this to mean ARGB\n    ans->buf = self->cairo.buf; ans->needs_free = false;\n    ans->start_x = 0;\n    ans->width = width;\n    ans->stride = self->cairo.stride;\n    ans->rows = height;\n    ans->bitmap_left = (int)bb.x_bearing;\n    ans->bitmap_top = -(int)bb.y_bearing;\n    ans->right_edge = (int)ceil(extents.x_advance);\n    return true;\n}\n\n\nstatic bool\nrender_color_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline) {\n    const unsigned int width_to_render_in = num_cells * cell_width;\n    uint8_t v = is_color_dark(OPT(background)) ? 255 : 0;\n    ARGB32 fg = {.red = v, .green = v, .blue = v, .alpha=255};\n    return render_glyph_with_cairo(self, glyph_id, ans, width_to_render_in, cell_height, fg, baseline);\n}\n\n\nstatic void\ncopy_color_bitmap_bgra(uint8_t *src, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride) {\n    for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {\n        pixel *d = dest + dest_stride * dr;\n        uint8_t *s = src + src_stride * sr;\n        for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {\n            uint8_t *bgra = s + 4 * sc;\n            const float inv_alpha = 255.f / bgra[3];\n#define C(idx, shift) ( (uint8_t)(bgra[idx] * inv_alpha) << shift)\n            d[dc] = C(2, 24) | C(1, 16) | C(0, 8) | bgra[3];\n#undef C\n        }\n    }\n}\n\nstatic void\ncopy_color_bitmap_argb(uint8_t *src, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride) {\n    for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {\n        pixel *d = dest + dest_stride * dr;\n        pixel *s = (pixel*)(src + src_stride * sr);\n        for (size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {\n            pixel argb = s[sc]; pixel alpha = (argb >> 24) & 0xff;\n            const float inv_alpha = 255.f / alpha;\n#define C(src_shift, dest_shift) ( (pixel)(((argb >> src_shift) & 0xff) * inv_alpha) << dest_shift )\n            d[dc] = C(16, 24) | C(8, 16) | C(0, 8) | alpha;\n#undef C\n        }\n    }\n}\n\n\nstatic const bool debug_placement = false;\n\nstatic void\nplace_bitmap_in_canvas(pixel *cell, ProcessedBitmap *bm, size_t cell_width, size_t cell_height, float x_offset, float y_offset, size_t baseline, unsigned int glyph_num, pixel fg_rgb, size_t x_in_canvas, size_t y_in_canvas) {\n    // We want the glyph to be positioned inside the cell based on the bearingX\n    // and bearingY values, making sure that it does not overflow the cell.\n\n    Region src = { .left = bm->start_x, .bottom = bm->rows, .right = bm->width + bm->start_x }, dest = { .bottom = cell_height, .right = cell_width };\n\n    // Calculate column bounds\n    int32_t xoff = (int32_t)(x_offset + bm->bitmap_left);\n    if (debug_placement) printf(\" bitmap_left: %d xoff: %d\", bm->bitmap_left, xoff);\n    if (xoff < 0) src.left += -xoff;\n    else dest.left = xoff;\n    // Move the dest start column back if the width overflows because of it, but only if we are not in a very long/infinite ligature\n    if (glyph_num < 4 && dest.left > 0 && dest.left + bm->width > cell_width) {\n        uint32_t extra = dest.left + bm->width - cell_width;\n        dest.left = extra > dest.left ? 0 : dest.left - extra;\n    }\n    dest.left += x_in_canvas;\n\n    // Calculate row bounds\n    int32_t yoff = (ssize_t)(y_offset + bm->bitmap_top);\n    if ((yoff > 0 && (size_t)yoff > baseline)) {\n        dest.top = 0;\n    } else {\n        dest.top = baseline - yoff;\n    }\n    dest.top += y_in_canvas;\n\n    // printf(\"x_offset: %d y_offset: %d src_start_row: %u src_start_column: %u dest_start_row: %u dest_start_column: %u bm_width: %lu bitmap_rows: %lu\\n\", xoff, yoff, src.top, src.left, dest.top, dest.left, bm->width, bm->rows);\n\n    switch (bm->pixel_mode) {\n        case FT_PIXEL_MODE_BGRA: copy_color_bitmap_bgra(bm->buf, cell, &src, &dest, bm->stride, cell_width); break;\n        case FT_PIXEL_MODE_MAX: copy_color_bitmap_argb(bm->buf, cell, &src, &dest, bm->stride, cell_width); break;\n        default: render_alpha_mask(bm->buf, cell, &src, &dest, bm->stride, cell_width, fg_rgb); break;\n    }\n}\n\nstatic const ProcessedBitmap EMPTY_PBM = {.factor = 1};\n\nbool\nrender_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE fg, GlyphRenderInfo *ri) {\n    Face *self = (Face*)f;\n    bool is_emoji = *was_colored; *was_colored = is_emoji && self->has_color;\n    float x = 0.f, y = 0.f;\n    ProcessedBitmap bm;\n    unsigned int canvas_width = cell_width * num_cells;\n    GlyphColorType colored;\n    for (unsigned int i = 0; i < num_glyphs; i++) {\n        bm = EMPTY_PBM;\n        // dont load the space glyph since loading it fails for some fonts/sizes and it is anyway to be rendered as a blank\n        if (info[i].codepoint != self->space_glyph_id) {\n            if (*was_colored && (colored = glyph_color_type(self, info[i].codepoint)) != NOT_COLORED) {\n                if (!render_color_bitmap(self, info[i].codepoint, &bm, cell_width, cell_height, num_cells, baseline)) {\n                    if (PyErr_Occurred()) PyErr_Print();\n                    if (!render_bitmap(self, info[i].codepoint, &bm, cell_width, cell_height, num_cells, bold, italic, true, fg)) {\n                        free_processed_bitmap(&bm);\n                        return false;\n                    }\n                    *was_colored = false;\n                }\n            } else {\n                if (!render_bitmap(self, info[i].codepoint, &bm, cell_width, cell_height, num_cells, bold, italic, true, fg)) {\n                    free_processed_bitmap(&bm);\n                    return false;\n                }\n            }\n        }\n        float x_offset = x + (float)positions[i].x_offset / 64.0f;\n        y = (float)positions[i].y_offset / 64.0f;\n        if (debug_placement) printf(\"%d: x=%f canvas: %u\", i, x_offset, canvas_width);\n        if ((*was_colored || self->face->glyph->metrics.width > 0) && bm.width > 0) {\n            place_bitmap_in_canvas(canvas, &bm, canvas_width, cell_height, x_offset, y, baseline, i, 0xffffff, 0, 0);\n        }\n        if (debug_placement) printf(\" adv: %f\\n\", (float)positions[i].x_advance / 64.0f);\n        // the roundf() below is needed for infinite length ligatures, for a test case\n        // use: kitty --config None -o 'font_family Fira Code' -o 'font_size 4.5' sh -c\n        // \"echo '|---|--------|-------|-------------|-------------|HH'; read\"\n        // if this causes issues with non-infinite ligatures, we could choose this behavior\n        // based on num_glyphs and/or num_cells\n        x += roundf((float)positions[i].x_advance / 64.0f);\n        free_processed_bitmap(&bm);\n    }\n\n    ri->canvas_width = canvas_width; ri->rendered_width = (unsigned)x; ri->x = 0;\n    // x_advance is wrong for colored bitmaps that have been downsampled\n    if (*was_colored) ri->rendered_width = num_glyphs == 1 ? bm.right_edge : canvas_width;\n    return true;\n}\n\nstatic PyObject*\npostscript_name(PyObject *s, PyObject *a UNUSED) {\n    Face *self = (Face*)s;\n    const char *psname = FT_Get_Postscript_Name(self->face);\n    if (psname) return Py_BuildValue(\"s\", psname);\n    Py_INCREF(self->path);\n    return self->path;\n}\n\nstatic PyObject*\nidentify_for_debug(PyObject *s, PyObject *a UNUSED) {\n    Face *self = (Face*)s;\n    FaceIndex instance;\n    instance.val = self->face->face_index;\n    RAII_PyObject(features, PyTuple_New(self->font_features.count)); if (!features) return NULL;\n    char buf[128];\n    for (unsigned i = 0; i < self->font_features.count; i++) {\n        hb_feature_to_string(self->font_features.features + i, buf, sizeof(buf));\n        PyObject *f = PyUnicode_FromString(buf); if (!f) return NULL;\n        PyTuple_SET_ITEM(features, i, f);\n    }\n    return PyUnicode_FromFormat(\"%s: %V:%d\\nFeatures: %S\", FT_Get_Postscript_Name(self->face), self->path, \"[path]\", instance.val, features);\n}\n\nstatic PyObject*\nextra_data(PyObject *self, PyObject *a UNUSED) {\n    return PyLong_FromVoidPtr(((Face*)self)->extra_data);\n}\n\n// NAME table {{{\nstatic bool\nensure_name_table(Face *self) {\n    if (self->name_lookup_table) return true;\n    RAII_PyObject(ans, PyDict_New());\n    if (!ans) return false;\n    FT_SfntName temp;\n    for (FT_UInt i = 0; i < FT_Get_Sfnt_Name_Count(self->face); i++) {\n        FT_Error err = FT_Get_Sfnt_Name(self->face, i, &temp);\n        if (err != 0) continue;\n        if (!add_font_name_record(ans, temp.platform_id, temp.encoding_id, temp.language_id, temp.name_id, (const char*)temp.string, temp.string_len)) return NULL;\n    }\n    self->name_lookup_table = ans; Py_INCREF(ans);\n    return true;\n}\n\nstatic PyObject*\nget_best_name(Face *self, PyObject *nameid) {\n    if (!ensure_name_table(self)) return NULL;\n    return get_best_name_from_name_table(self->name_lookup_table, nameid);\n}\n\nstatic PyObject*\n_get_best_name(Face *self, unsigned long nameid) {\n    RAII_PyObject(key, PyLong_FromUnsignedLong(nameid));\n    return key ? get_best_name(self, key) : NULL;\n}\n// }}}\n\nstatic PyObject*\nconvert_named_style_to_python(Face *face, const FT_Var_Named_Style *src, FT_Var_Axis *axes, unsigned num_of_axes) {\n    RAII_PyObject(axis_values, PyDict_New());\n    if (!axis_values) return NULL;\n    uint8_t tag_buf[5] = {0};\n    for (FT_UInt i = 0; i < num_of_axes; i++) {\n        double val = src->coords[i] / 65536.0;\n        RAII_PyObject(pval, PyFloat_FromDouble(val));\n        if (!pval) return NULL;\n        if (PyDict_SetItemString(axis_values, tag_to_string(axes[i].tag, tag_buf), pval) != 0) return NULL;\n    }\n    RAII_PyObject(name, _get_best_name(face, src->strid));\n    if (!name) PyErr_Clear();\n    RAII_PyObject(psname, src->psid == 0xffff ? NULL : _get_best_name(face, src->psid));\n    if (!psname) PyErr_Clear();\n    return Py_BuildValue(\"{sO sO sO}\", \"axis_values\", axis_values, \"name\", name ? name : PyUnicode_FromString(\"\"), \"psname\", psname ? psname : PyUnicode_FromString(\"\"));\n}\n\nstatic PyObject*\nconvert_axis_to_python(Face *face, const FT_Var_Axis *src, FT_UInt flags) {\n    PyObject *strid = _get_best_name(face, src->strid);\n    if (!strid) { PyErr_Clear(); strid = PyUnicode_FromString(\"\"); }\n    uint8_t tag_buf[5] = {0};\n    return Py_BuildValue(\"{sd sd sd sO ss ss sN}\",\n        \"minimum\", src->minimum / 65536.0, \"maximum\", src->maximum / 65536.0, \"default\", src->def / 65536.0,\n        \"hidden\", flags & FT_VAR_AXIS_FLAG_HIDDEN ? Py_True : Py_False, \"name\", src->name, \"tag\", tag_to_string(src->tag, tag_buf),\n        \"strid\", strid\n    );\n}\n\nstatic PyObject*\nget_variation(Face *self, PyObject *a UNUSED) {\n    RAII_FTMMVar(mm);\n    FT_Error err;\n    if ((err = FT_Get_MM_Var(self->face, &mm))) { Py_RETURN_NONE; }\n    RAII_ALLOC(FT_Fixed, coords, malloc(mm->num_axis * sizeof(FT_Fixed)));\n    if (!coords) return PyErr_NoMemory();\n    if ((err = FT_Get_Var_Design_Coordinates(self->face, mm->num_axis, coords))) {\n        set_freetype_error(\"Failed to load the variation data from font with error:\", err); return NULL;\n    }\n    RAII_PyObject(ans, PyDict_New()); if (!ans) return NULL;\n    uint8_t tag[5];\n    for (FT_UInt i = 0; i < mm->num_axis; i++) {\n        double val = coords[i] / 65536.0;\n        tag_to_string(mm->axis[i].tag, tag);\n        RAII_PyObject(pval, PyFloat_FromDouble(val));\n        if (!pval) return NULL;\n        if (PyDict_SetItemString(ans, (const char*)tag, pval) != 0) return NULL;\n    }\n    Py_INCREF(ans); return ans;\n}\n\nstatic PyObject*\napplied_features(Face *self, PyObject *a UNUSED) {\n    return font_features_as_dict(&self->font_features);\n}\n\nstatic PyObject*\nget_features(Face *self, PyObject *a UNUSED) {\n    FT_Error err;\n    FT_ULong length = 0;\n    if (!ensure_name_table(self)) return NULL;\n    RAII_PyObject(output, PyDict_New()); if (!output) return NULL;\n    if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0, NULL, &length)) == 0) {\n        RAII_ALLOC(uint8_t, table, malloc(length));\n        if (!table) return PyErr_NoMemory();\n        if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0, table, &length))) {\n            set_freetype_error(\"Failed to load the GSUB table from font with error:\", err); return NULL;\n        }\n        if (!read_features_from_font_table(table, length, self->name_lookup_table, output)) return NULL;\n    }\n    length = 0;\n    if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('G', 'P', 'O', 'S'), 0, NULL, &length)) == 0) {\n        RAII_ALLOC(uint8_t, table, malloc(length));\n        if (!table) return PyErr_NoMemory();\n        if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('G', 'P', 'O', 'S'), 0, table, &length))) {\n            set_freetype_error(\"Failed to load the GSUB table from font with error:\", err); return NULL;\n        }\n        if (!read_features_from_font_table(table, length, self->name_lookup_table, output)) return NULL;\n    }\n    Py_INCREF(output); return output;\n}\n\nstatic PyObject*\nget_variable_data(Face *self, PyObject *a UNUSED) {\n    if (!ensure_name_table(self)) return NULL;\n    RAII_PyObject(output, PyDict_New()); if (!output) return NULL;\n    RAII_PyObject(axes, PyTuple_New(0));\n    RAII_PyObject(named_styles, PyTuple_New(0));\n    if (!axes || !named_styles) return NULL;\n    FT_Error err;\n    FT_ULong length = 0;\n    if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('S', 'T', 'A', 'T'), 0, NULL, &length)) == 0) {\n        RAII_ALLOC(uint8_t, table, malloc(length));\n        if (!table) return PyErr_NoMemory();\n        if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('S', 'T', 'A', 'T'), 0, table, &length))) {\n            set_freetype_error(\"Failed to load the STAT table from font with error:\", err); return NULL;\n        }\n        if (!read_STAT_font_table(table, length, self->name_lookup_table, output)) return NULL;\n    } else if (!read_STAT_font_table(NULL, 0, self->name_lookup_table, output)) return NULL;\n    if (self->is_variable) {\n        RAII_FTMMVar(mm);\n        if ((err = FT_Get_MM_Var(self->face, &mm))) { set_freetype_error(\"Failed to get variable axis data from font with error:\", err); return NULL; }\n        if (_PyTuple_Resize(&axes, mm->num_axis) == -1) return NULL;\n        if (_PyTuple_Resize(&named_styles, mm->num_namedstyles) == -1) return NULL;\n        for (FT_UInt i = 0; i < mm->num_namedstyles; i++) {\n            PyObject *s = convert_named_style_to_python(self, mm->namedstyle + i, mm->axis, mm->num_axis);\n            if (!s) return NULL;\n            PyTuple_SET_ITEM(named_styles, i, s);\n        }\n\n        for (FT_UInt i = 0; i < mm->num_axis; i++) {\n            FT_UInt flags;\n            FT_Get_Var_Axis_Flags(mm, i, &flags);\n            PyObject *s = convert_axis_to_python(self, mm->axis + i, flags);\n\n            if (!s) return NULL;\n            PyTuple_SET_ITEM(axes, i, s);\n        }\n    }\n    if (PyDict_SetItemString(output, \"variations_postscript_name_prefix\", _get_best_name(self, 25)) != 0) return NULL;\n    if (PyDict_SetItemString(output, \"axes\", axes) != 0) return NULL;\n    if (PyDict_SetItemString(output, \"named_styles\", named_styles) != 0) return NULL;\n    Py_INCREF(output); return output;\n}\n\nStringCanvas\nrender_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {\n    Face *self = (Face*)s;\n    StringCanvas ans = {0};\n    size_t num_chars = strnlen(text, 32);\n    int max_char_width = font_units_to_pixels_x(self, self->face->max_advance_width);\n    size_t canvas_width = max_char_width * (num_chars*2);\n    size_t canvas_height = font_units_to_pixels_y(self, self->face->height) + 8;\n    pixel *canvas = calloc(canvas_width * canvas_height, sizeof(pixel));\n    if (!canvas) return ans;\n    size_t pen_x = 0;\n    ProcessedBitmap pbm;\n    for (size_t n = 0; n < num_chars; n++) {\n        FT_UInt glyph_index = FT_Get_Char_Index(self->face, text[n]);\n        int error = FT_Load_Glyph(self->face, glyph_index, FT_LOAD_DEFAULT);\n        if (error) continue;\n        error = FT_Render_Glyph(self->face->glyph, FT_RENDER_MODE_NORMAL);\n        if (error) continue;\n        FT_Bitmap *bitmap = &self->face->glyph->bitmap;\n        pbm = EMPTY_PBM;\n        populate_processed_bitmap(self->face->glyph, bitmap, &pbm, false);\n        place_bitmap_in_canvas(canvas, &pbm, canvas_width, canvas_height, 0, 0, baseline, n, 0xffffff, pen_x, 0);\n        pen_x += self->face->glyph->advance.x >> 6;\n    }\n    ans.width = pen_x; ans.height = canvas_height;\n    ans.canvas = malloc(ans.width * ans.height);\n    if (ans.canvas) {\n        for (size_t row = 0; row < ans.height; row++) {\n            unsigned char *destp = ans.canvas + (ans.width * row);\n            pixel *srcp = canvas + (canvas_width * row);\n            for (size_t i = 0; i < ans.width; i++) destp[i] = srcp[i] & 0xff;\n        }\n    }\n    free(canvas);\n    return ans;\n}\n\nstatic void destroy_hb_buffer(hb_buffer_t **x) { if (*x) hb_buffer_destroy(*x); }\n\nstatic PyObject*\nrender_codepoint(Face *self, PyObject *args) {\n    unsigned long cp, fg = 0xffffff;\n    if (!PyArg_ParseTuple(args, \"k|k\", &cp, &fg)) return NULL;\n    FT_UInt glyph_index = FT_Get_Char_Index(self->face, cp);\n    ProcessedBitmap pbm = EMPTY_PBM;\n    GlyphColorType colored;\n    if (self->has_color && (colored = glyph_color_type(self, glyph_index)) != NOT_COLORED) {\n        render_color_bitmap(self, glyph_index, &pbm, 0, 0, 0, 0);\n    } else {\n        int load_flags = get_load_flags(self->hinting, self->hintstyle, FT_LOAD_RENDER);\n        FT_Load_Glyph(self->face, glyph_index, load_flags);\n        FT_Render_Glyph(self->face->glyph, FT_RENDER_MODE_NORMAL);\n        FT_Bitmap *bitmap = &self->face->glyph->bitmap;\n        populate_processed_bitmap(self->face->glyph, bitmap, &pbm, false);\n    }\n    const unsigned long canvas_width = pbm.width, canvas_height = pbm.rows;\n    RAII_PyObject(ans, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_height * canvas_width));\n    if (!ans) return NULL;\n    pixel *canvas = (pixel*)PyBytes_AS_STRING(ans);\n    memset(canvas, 0, PyBytes_GET_SIZE(ans));\n    place_bitmap_in_canvas(canvas, &pbm, canvas_width, canvas_height, 0, 0, 0, 99999, fg, 0, 0);\n    free_processed_bitmap(&pbm);\n    for (pixel *c = canvas; c < canvas + canvas_width * canvas_height; c++) {\n        uint8_t *p = (uint8_t*)c;\n        uint8_t a = p[0], b = p[1], g = p[2], r = p[3];\n        p[0] = r; p[1] = g; p[2] = b; p[3] = a;\n    }\n    return Py_BuildValue(\"Okk\", ans, canvas_width, canvas_height);\n}\n\nstatic PyObject*\nrender_sample_text(Face *self, PyObject *args) {\n    unsigned long canvas_width, canvas_height;\n    unsigned long fg = 0xffffff;\n    PyObject *ptext;\n    if (!PyArg_ParseTuple(args, \"Ukk|k\", &ptext, &canvas_width, &canvas_height, &fg)) return NULL;\n    FontCellMetrics fcm = cell_metrics((PyObject*)self);\n    RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height));\n    if (!pbuf) return NULL;\n    memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf));\n    if (!fcm.cell_width || !fcm.cell_height) return Py_BuildValue(\"OII\", pbuf, fcm.cell_width, fcm.cell_height);\n    int num_chars_per_line = canvas_width / fcm.cell_width, num_of_lines = (int)ceil((float)PyUnicode_GET_LENGTH(ptext) / (float)num_chars_per_line);\n    canvas_height = MIN(canvas_height, num_of_lines * fcm.cell_height);\n\n    __attribute__((cleanup(destroy_hb_buffer))) hb_buffer_t *hb_buffer = hb_buffer_create();\n    if (!hb_buffer_pre_allocate(hb_buffer, 4*PyUnicode_GET_LENGTH(ptext))) { PyErr_NoMemory(); return NULL; }\n    for (ssize_t n = 0; n < PyUnicode_GET_LENGTH(ptext); n++) {\n        Py_UCS4 codep = PyUnicode_READ_CHAR(ptext, n);\n        hb_buffer_add_utf32(hb_buffer, &codep, 1, 0, 1);\n    }\n    hb_buffer_guess_segment_properties(hb_buffer);\n    if (!HB_DIRECTION_IS_HORIZONTAL(hb_buffer_get_direction(hb_buffer))) goto end;\n    hb_shape(harfbuzz_font_for_face((PyObject*)self), hb_buffer, self->font_features.features, self->font_features.count);\n    unsigned int len = hb_buffer_get_length(hb_buffer);\n    hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL);\n    hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(hb_buffer, NULL);\n\n    if (fcm.cell_width > canvas_width) goto end;\n    pixel *canvas = (pixel*)PyBytes_AS_STRING(pbuf);\n    int load_flags = get_load_flags(self->hinting, self->hintstyle, FT_LOAD_RENDER);\n    int error;\n\n    float pen_x = 0, pen_y = 0;\n    for (unsigned int i = 0; i < len; i++) {\n        float advance = (float)positions[i].x_advance / 64.0f;\n        if (pen_x + advance > canvas_width) {\n            pen_y += fcm.cell_height;\n            pen_x = 0;\n            if (pen_y >= canvas_height) break;\n        }\n        size_t x = (size_t)round(pen_x + (float)positions[i].x_offset / 64.0f);\n        size_t y = (size_t)round(pen_y + (float)positions[i].y_offset / 64.0f);\n        pen_x += advance;\n        if ((error = FT_Load_Glyph(self->face, info[i].codepoint, load_flags))) continue;\n        if ((error = FT_Render_Glyph(self->face->glyph, FT_RENDER_MODE_NORMAL))) continue;\n        FT_Bitmap *bitmap = &self->face->glyph->bitmap;\n        ProcessedBitmap pbm = EMPTY_PBM;\n        populate_processed_bitmap(self->face->glyph, bitmap, &pbm, false);\n        place_bitmap_in_canvas(canvas, &pbm, canvas_width, canvas_height, x, 0, fcm.baseline, 99999, fg, 0, y);\n        free_processed_bitmap(&pbm);\n    }\n\n    const uint8_t *last_pixel = (uint8_t*)PyBytes_AS_STRING(pbuf) + PyBytes_GET_SIZE(pbuf) - sizeof(pixel);\n    for (uint8_t *p = (uint8_t*)PyBytes_AS_STRING(pbuf); p <= last_pixel; p += sizeof(pixel)) {\n        uint8_t a = p[0], b = p[1], g = p[2], r = p[3];\n        p[0] = r; p[1] = g; p[2] = b; p[3] = a;\n    }\nend:\n    return Py_BuildValue(\"OII\", pbuf, fcm.cell_width, fcm.cell_height);\n}\n\n\n// Boilerplate {{{\n\nstatic PyMemberDef members[] = {\n#define MEM(name, type) {#name, type, offsetof(Face, name), READONLY, #name}\n#define MMEM(name, type) {#name, type, offsetof(Face, metrics) + offsetof(FaceMetrics, name), READONLY, #name}\n    MMEM(units_per_EM, T_UINT),\n    MMEM(size_in_pts, T_FLOAT),\n    MMEM(ascender, T_INT),\n    MMEM(descender, T_INT),\n    MMEM(height, T_INT),\n    MMEM(max_advance_width, T_INT),\n    MMEM(max_advance_height, T_INT),\n    MMEM(underline_position, T_INT),\n    MMEM(underline_thickness, T_INT),\n    MMEM(strikethrough_position, T_INT),\n    MMEM(strikethrough_thickness, T_INT),\n    MEM(is_scalable, T_BOOL),\n    MEM(is_variable, T_BOOL),\n    MEM(has_svg, T_BOOL),\n    MEM(has_color, T_BOOL),\n    MEM(path, T_OBJECT_EX),\n    {NULL}  /* Sentinel */\n#undef MEM\n#undef MMEM\n};\n\nstatic PyMethodDef methods[] = {\n    METHODB(postscript_name, METH_NOARGS),\n    METHODB(identify_for_debug, METH_NOARGS),\n    METHODB(extra_data, METH_NOARGS),\n    METHODB(get_variable_data, METH_NOARGS),\n    METHODB(applied_features, METH_NOARGS),\n    METHODB(get_features, METH_NOARGS),\n    METHODB(get_variation, METH_NOARGS),\n    METHODB(get_best_name, METH_O),\n    METHODB(set_size, METH_VARARGS),\n    METHODB(render_sample_text, METH_VARARGS),\n    METHODB(render_codepoint, METH_VARARGS),\n    {NULL}  /* Sentinel */\n};\n\nPyTypeObject Face_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.Face\",\n    .tp_new = new,\n    .tp_basicsize = sizeof(Face),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"FreeType Font face\",\n    .tp_methods = methods,\n    .tp_members = members,\n    .tp_repr = (reprfunc)repr,\n};\n\nstatic void\nfree_freetype(void) {\n    cairo_debug_reset_static_data();\n    FT_Done_FreeType(library);\n}\n\nbool\ninit_freetype_library(PyObject *m) {\n    if (PyType_Ready(&Face_Type) < 0) return 0;\n    if (PyModule_AddObject(m, \"Face\", (PyObject *)&Face_Type) != 0) return 0;\n    Py_INCREF(&Face_Type);\n    FreeType_Exception = PyErr_NewException(\"fast_data_types.FreeTypeError\", NULL, NULL);\n    if (FreeType_Exception == NULL) return false;\n    if (PyModule_AddObject(m, \"FreeTypeError\", FreeType_Exception) != 0) return false;\n    int error = FT_Init_FreeType(&library);\n    if (error) {\n        set_freetype_error(\"Failed to initialize FreeType library, with error:\", error);\n        return false;\n    }\n    register_at_exit_cleanup_func(FREETYPE_CLEANUP_FUNC, free_freetype);\n    return true;\n}\n\n// }}}\n"
  },
  {
    "path": "kitty/freetype_render_ui_text.c",
    "content": "/*\n * freetype_render_ui_text.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"freetype_render_ui_text.h\"\n#include <hb.h>\n#include <hb-ft.h>\n#include \"charsets.h\"\n#include \"char-props.h\"\n#include \"wcswidth.h\"\n#include FT_BITMAP_H\n#define ELLIPSIS 0x2026\n\ntypedef struct FamilyInformation {\n    char *name;\n    bool bold, italic;\n} FamilyInformation;\n\ntypedef struct Face {\n    FT_Face freetype;\n    hb_font_t *hb;\n    FT_UInt pixel_size;\n    int hinting, hintstyle;\n    struct Face **fallbacks;\n    size_t count, capacity;\n} Face;\n\ntypedef struct {\n    unsigned char* buf;\n    size_t start_x, width, stride;\n    size_t rows;\n    FT_Pixel_Mode pixel_mode;\n    unsigned int left_edge, top_edge, bottom_edge, right_edge;\n    float factor;\n    int bitmap_left, bitmap_top;\n} ProcessedBitmap;\n\ntypedef struct RenderCtx {\n    bool created;\n    Face main_face;\n    FontConfigFace main_face_information;\n    FamilyInformation main_face_family;\n    hb_buffer_t *hb_buffer;\n} RenderCtx;\n\n#define main_face ctx->main_face\n#define main_face_information ctx->main_face_information\n#define main_face_family ctx->main_face_family\n#define hb_buffer ctx->hb_buffer\n\nstatic FT_UInt\nglyph_id_for_codepoint(Face *face, char_type cp) {\n    return FT_Get_Char_Index(face->freetype, cp);\n}\n\nstatic void\nfree_face(Face *face) {\n    if (face->freetype) FT_Done_Face(face->freetype);\n    if (face->hb) hb_font_destroy(face->hb);\n    for (size_t i = 0; i < face->count; i++) { free_face(face->fallbacks[i]); free(face->fallbacks[i]); }\n    free(face->fallbacks);\n    memset(face, 0, sizeof(Face));\n}\n\nstatic void\ncleanup(RenderCtx *ctx) {\n    free_face(&main_face);\n    free(main_face_information.path); main_face_information.path = NULL;\n    free(main_face_family.name);\n    memset(&main_face_family, 0, sizeof(FamilyInformation));\n    if (hb_buffer) hb_buffer_destroy(hb_buffer);\n    hb_buffer = NULL;\n}\n\nvoid\nset_main_face_family(FreeTypeRenderCtx ctx_, const char *family, bool bold, bool italic) {\n    RenderCtx *ctx = (RenderCtx*)ctx_;\n    if (\n        (family == main_face_family.name || (main_face_family.name && strcmp(family, main_face_family.name) == 0)) &&\n        main_face_family.bold == bold && main_face_family.italic == italic\n    ) return;\n    cleanup(ctx);\n    main_face_family.name = family ? strdup(family) : NULL;\n    main_face_family.bold = bold; main_face_family.italic = italic;\n}\n\nstatic int\nget_load_flags(int hinting, int hintstyle, int base) {\n    int flags = base;\n    if (hinting) {\n        if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL;\n        else if (0 < hintstyle) flags |= FT_LOAD_TARGET_LIGHT;\n    } else flags |= FT_LOAD_NO_HINTING;\n    return flags;\n}\n\nstatic bool\nload_font(FontConfigFace *info, Face *ans) {\n    ans->freetype = native_face_from_path(info->path, info->index);\n    if (!ans->freetype || PyErr_Occurred()) return false;\n    ans->hb = hb_ft_font_create(ans->freetype, NULL);\n    if (!ans->hb) { PyErr_NoMemory(); return false; }\n    ans->hinting = info->hinting; ans->hintstyle = info->hintstyle;\n    hb_ft_font_set_load_flags(ans->hb, get_load_flags(ans->hinting, ans->hintstyle, FT_LOAD_DEFAULT));\n    return true;\n}\n\nstatic int\nfont_units_to_pixels_y(FT_Face face, int x) {\n    return (int)ceil((double)FT_MulFix(x, face->size->metrics.y_scale) / 64.0);\n}\n\n\nstatic FT_UInt\nchoose_bitmap_size(FT_Face face, FT_UInt desired_height) {\n    unsigned short best = 0, diff = USHRT_MAX;\n    const short limit = face->num_fixed_sizes;\n    for (short i = 0; i < limit; i++) {\n        unsigned short h = face->available_sizes[i].height;\n        unsigned short d = h > (unsigned short)desired_height ? h - (unsigned short)desired_height : (unsigned short)desired_height - h;\n        if (d < diff) {\n            diff = d;\n            best = i;\n        }\n    }\n    FT_Select_Size(face, best);\n    return best;\n}\n\nstatic void\nset_pixel_size(RenderCtx *ctx, Face *face, FT_UInt sz, bool get_metrics UNUSED) {\n    if (sz != face->pixel_size) {\n        if (face->freetype->num_fixed_sizes > 0 && FT_HAS_COLOR(face->freetype)) choose_bitmap_size(face->freetype, font_units_to_pixels_y(main_face.freetype, main_face.freetype->height));\n        else FT_Set_Pixel_Sizes(face->freetype, sz, sz);\n        hb_ft_font_changed(face->hb);\n        hb_ft_font_set_load_flags(face->hb, get_load_flags(face->hinting, face->hintstyle, FT_LOAD_DEFAULT));\n        face->pixel_size = sz;\n    }\n}\n\n\ntypedef struct RenderState {\n    uint32_t pending_in_buffer, fg, bg;\n    pixel *output;\n    size_t output_width, output_height, stride;\n    Face *current_face;\n    float x, y, start_pos_for_current_run;\n    int y_offset;\n    Region src, dest;\n    unsigned sz_px;\n    bool truncated;\n    bool horizontally_center;\n} RenderState;\n\nstatic void\nsetup_regions(ProcessedBitmap *bm, RenderState *rs, int baseline) {\n    rs->src = (Region){ .left = bm->start_x, .bottom = bm->rows, .right = bm->width + bm->start_x };\n    rs->dest = (Region){ .bottom = rs->output_height, .right = rs->output_width };\n    int xoff = (int)(rs->x + bm->bitmap_left);\n    if (xoff < 0) rs->src.left += -xoff;\n    else rs->dest.left = xoff;\n    if (rs->horizontally_center) {\n        int run_width = (int)(rs->output_width - rs->start_pos_for_current_run);\n        rs->dest.left = (int)rs->start_pos_for_current_run + (run_width > (int)bm->width ? (run_width - bm->width)/2 : 0);\n    }\n    int yoff = (int)(rs->y + bm->bitmap_top);\n    if ((yoff > 0 && yoff > baseline)) {\n        rs->dest.top = 0;\n    } else {\n        rs->dest.top = baseline - yoff;\n    }\n    rs->dest.top += rs->y_offset;\n}\n\n#define ARGB(a, r, g, b) ( (a & 0xff) << 24 ) | ( (r & 0xff) << 16) | ( (g & 0xff) << 8 ) | (b & 0xff)\n\nstatic pixel\npremult_pixel(pixel p, uint16_t alpha) {\n#define s(x) (x * alpha / 255)\n    uint16_t r = (p >> 16) & 0xff, g = (p >> 8) & 0xff, b = p & 0xff;\n    return ARGB(alpha, s(r), s(g), s(b));\n#undef s\n}\n\nstatic pixel\nalpha_blend_premult(pixel over, pixel under) {\n    const uint16_t over_r = (over >> 16) & 0xff, over_g = (over >> 8) & 0xff, over_b = over & 0xff;\n    const uint16_t under_r = (under >> 16) & 0xff, under_g = (under >> 8) & 0xff, under_b = under & 0xff;\n    const uint16_t factor = 255 - ((over >> 24) & 0xff);\n#define ans(x) (over_##x + (factor * under_##x) / 255)\n    return ARGB(under >> 24, ans(r), ans(g), ans(b));\n#undef ans\n}\n\nstatic void\nrender_color_bitmap(ProcessedBitmap *src, RenderState *rs) {\n    for (size_t sr = rs->src.top, dr = rs->dest.top; sr < rs->src.bottom && dr < rs->dest.bottom; sr++, dr++) {\n        pixel *dest_row = rs->output + rs->stride * dr;\n        uint8_t *src_px = src->buf + src->stride * sr + 4 * rs->src.left;\n        for (size_t sc = rs->src.left, dc = rs->dest.left; sc < rs->src.right && dc < rs->dest.right; sc++, dc++, src_px += 4) {\n            pixel fg = premult_pixel(ARGB(src_px[3], src_px[2], src_px[1], src_px[0]), src_px[3]);\n            dest_row[dc] = alpha_blend_premult(fg, dest_row[dc]);\n        }\n    }\n}\n\nstatic void\nrender_gray_bitmap(ProcessedBitmap *src, RenderState *rs) {\n    for (size_t sr = rs->src.top, dr = rs->dest.top; sr < rs->src.bottom && dr < rs->dest.bottom; sr++, dr++) {\n        pixel *dest_row = rs->output + rs->stride * dr;\n        uint8_t *src_row = src->buf + src->stride * sr;\n        for (size_t sc = rs->src.left, dc = rs->dest.left; sc < rs->src.right && dc < rs->dest.right; sc++, dc++) {\n            pixel fg = premult_pixel(rs->fg, src_row[sc]);\n            dest_row[dc] = alpha_blend_premult(fg, dest_row[dc]);\n        }\n    }\n}\n\nstatic void\npopulate_processed_bitmap(FT_GlyphSlotRec *slot, FT_Bitmap *bitmap, ProcessedBitmap *ans) {\n    ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;\n    ans->rows = bitmap->rows;\n    ans->start_x = 0; ans->width = bitmap->width;\n    ans->pixel_mode = bitmap->pixel_mode;\n    ans->bitmap_top = slot->bitmap_top; ans->bitmap_left = slot->bitmap_left;\n    ans->buf = bitmap->buffer;\n}\n\nstatic void\ndetect_edges(ProcessedBitmap *ans) {\n#define check const uint8_t *p = ans->buf + x * 4 + y * ans->stride; if (p[3] > 20)\n    ans->right_edge = 0; ans->bottom_edge = 0;\n    for (ssize_t x = ans->width - 1; !ans->right_edge && x > -1; x--) {\n        for (size_t y = 0; y < ans->rows && !ans->right_edge; y++) {\n            check ans->right_edge = x;\n        }\n    }\n    for (ssize_t y = ans->rows - 1; !ans->bottom_edge && y > -1; y--) {\n        for (size_t x = 0; x < ans->width && !ans->bottom_edge; x++) {\n            check ans->bottom_edge = y;\n        }\n    }\n    ans->left_edge = ans->width;\n    for (size_t x = 0; ans->left_edge == ans->width && x < ans->width; x++) {\n        for (size_t y = 0; y < ans->rows && ans->left_edge == ans->width; y++) {\n            check ans->left_edge = x;\n        }\n    }\n    ans->top_edge = ans->rows;\n    for (size_t y = 0; ans->top_edge == ans->rows && y < ans->rows; y++) {\n        for (size_t x = 0; x < ans->width && ans->top_edge == ans->rows; x++) {\n            check ans->top_edge = y;\n        }\n    }\n#undef check\n}\n\nstatic Face*\nfind_fallback_font_for(RenderCtx *ctx, char_type codep, char_type next_codep) {\n    if (glyph_id_for_codepoint(&main_face, codep) > 0) return &main_face;\n    for (size_t i = 0; i < main_face.count; i++) {\n        if (glyph_id_for_codepoint(main_face.fallbacks[i], codep) > 0) return main_face.fallbacks[i];\n    }\n    FontConfigFace q;\n    bool prefer_color = false;\n    char_type string[3] = {codep, next_codep, 0};\n    if (wcswidth_string(string) >= 2 && char_props_for(codep).is_emoji_presentation_base) prefer_color = true;\n    if (!fallback_font(codep, main_face_family.name, main_face_family.bold, main_face_family.italic, prefer_color, &q)) return NULL;\n    ensure_space_for(&main_face, fallbacks, Face, main_face.count + 1, capacity, 8, true);\n    Face *ans = calloc(1, sizeof(Face));\n    if (!ans) fatal(\"Out of memory\");\n    bool ok = load_font(&q, ans);\n    if (PyErr_Occurred()) PyErr_Print();\n    free(q.path);\n    if (!ok) { free(ans); return NULL; }\n    main_face.fallbacks[main_face.count] = ans;\n    main_face.count++;\n    return ans;\n}\n\n\nstatic unsigned\ncalculate_ellipsis_width(RenderCtx *ctx) {\n    Face *face = find_fallback_font_for(ctx, ELLIPSIS, 0);\n    if (!face) return 0;\n    set_pixel_size(ctx, face, main_face.pixel_size, false);\n    int glyph_index = FT_Get_Char_Index(face->freetype, ELLIPSIS);\n    if (!glyph_index) return 0;\n    int error = FT_Load_Glyph(face->freetype, glyph_index, get_load_flags(face->hinting, face->hintstyle, FT_LOAD_DEFAULT));\n    if (error) return 0;\n    return (unsigned)ceilf((float)face->freetype->glyph->metrics.horiAdvance / 64.f);\n}\n\n\nstatic bool\nrender_run(RenderCtx *ctx, RenderState *rs) {\n    hb_buffer_guess_segment_properties(hb_buffer);\n    if (!HB_DIRECTION_IS_HORIZONTAL(hb_buffer_get_direction(hb_buffer))) {\n        PyErr_SetString(PyExc_ValueError, \"Vertical text is not supported\");\n        return false;\n    }\n    FT_Face face = rs->current_face->freetype;\n    bool has_color = FT_HAS_COLOR(face);\n    FT_UInt pixel_size = rs->sz_px;\n    set_pixel_size(ctx, rs->current_face, pixel_size, false);\n    hb_shape(rs->current_face->hb, hb_buffer, NULL, 0);\n    unsigned int len = hb_buffer_get_length(hb_buffer);\n    hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL);\n    hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(hb_buffer, NULL);\n    int baseline = font_units_to_pixels_y(face, face->ascender);\n    int load_flags = get_load_flags(rs->current_face->hinting, rs->current_face->hintstyle, FT_LOAD_RENDER | (has_color ? FT_LOAD_COLOR : 0));\n    float pos = rs->x;\n    unsigned int limit = len;\n    for (unsigned int i = 0; i < len; i++) {\n        float delta = (float)positions[i].x_offset / 64.0f + (float)positions[i].x_advance / 64.0f;\n        if (pos + delta >= rs->output_width) {\n            limit = i;\n            break;\n        }\n        pos += delta;\n    }\n    if (limit < len) {\n        unsigned ellipsis_width = calculate_ellipsis_width(ctx);\n        while (pos + ellipsis_width >= rs->output_width && limit) {\n            limit--;\n            pos -= (float)positions[limit].x_offset / 64.0f + (float)positions[limit].x_advance / 64.0f;\n        }\n        rs->truncated = true;\n    }\n\n    rs->start_pos_for_current_run = rs->x;\n    for (unsigned int i = 0; i < limit; i++) {\n        rs->x += (float)positions[i].x_offset / 64.0f;\n        rs->y += (float)positions[i].y_offset / 64.0f;\n        if (rs->x > rs->output_width) break;\n        int error = FT_Load_Glyph(face, info[i].codepoint, load_flags);\n        if (error) {\n            set_freetype_error(\"Failed loading glyph\", error);\n            PyErr_Print();\n            continue;\n        };\n        ProcessedBitmap pbm = {0};\n        switch(face->glyph->bitmap.pixel_mode) {\n            case FT_PIXEL_MODE_BGRA: {\n                uint8_t *buf = NULL;\n                unsigned text_height = font_units_to_pixels_y(main_face.freetype, main_face.freetype->height);\n                populate_processed_bitmap(face->glyph, &face->glyph->bitmap, &pbm);\n                unsigned bm_width = 0, bm_height = text_height;\n                if (pbm.rows > bm_height) {\n                    double ratio = pbm.width / (double)pbm.rows;\n                    bm_width = (unsigned)(ratio * bm_height);\n                    buf = calloc((size_t)bm_height * bm_width, sizeof(pixel));\n                    if (!buf) break;\n                    downsample_32bit_image(pbm.buf, pbm.width, pbm.rows, pbm.stride, buf, bm_width, bm_height);\n                    pbm.buf = buf; pbm.stride = 4 * bm_width; pbm.width = bm_width; pbm.rows = bm_height;\n                    detect_edges(&pbm);\n                }\n                setup_regions(&pbm, rs, baseline);\n                if (bm_width) {\n                    /* printf(\"bottom_edge: %u top_edge: %u left_edge: %u right_edge: %u\\n\", */\n                    /*         pbm.bottom_edge, pbm.top_edge, pbm.left_edge, pbm.right_edge); */\n                    rs->src.top = pbm.top_edge; rs->src.bottom = pbm.bottom_edge + 1;\n                    rs->src.left = pbm.left_edge; rs->src.right = pbm.right_edge + 1;\n                    rs->dest.left = (int)(rs->x + 2);\n                    positions[i].x_advance = (pbm.right_edge - pbm.left_edge + 2) * 64;\n                    unsigned main_baseline = font_units_to_pixels_y(main_face.freetype, main_face.freetype->ascender);\n                    unsigned symbol_height = pbm.bottom_edge - pbm.top_edge;\n                    unsigned baseline_y = main_baseline + rs->y_offset, text_bottom_y = text_height + rs->y_offset;\n                    if (symbol_height <= baseline_y) {\n                        rs->dest.top = baseline_y - symbol_height + 2;\n                    } else {\n                        if (symbol_height <= text_bottom_y) rs->dest.top = text_bottom_y - symbol_height;\n                        else rs->dest.top = 0;\n                    }\n                    rs->dest.top += main_baseline > pbm.bottom_edge ? main_baseline - pbm.bottom_edge : 0;\n                    /* printf(\"symbol_height: %u baseline_y: %u\\n\", symbol_height, baseline_y); */\n                }\n                render_color_bitmap(&pbm, rs);\n                free(buf);\n            }\n                break;\n            case FT_PIXEL_MODE_MONO: {\n                FT_Bitmap bitmap;\n                freetype_convert_mono_bitmap(&face->glyph->bitmap, &bitmap);\n                populate_processed_bitmap(face->glyph, &bitmap, &pbm);\n                setup_regions(&pbm, rs, baseline);\n                render_gray_bitmap(&pbm, rs);\n                FT_Bitmap_Done(freetype_library(), &bitmap);\n            }\n                break;\n            case FT_PIXEL_MODE_GRAY:\n                populate_processed_bitmap(face->glyph, &face->glyph->bitmap, &pbm);\n                setup_regions(&pbm, rs, baseline);\n                render_gray_bitmap(&pbm, rs);\n                break;\n            default:\n                PyErr_Format(PyExc_TypeError, \"Unknown FreeType bitmap type: 0x%x\", face->glyph->bitmap.pixel_mode);\n                return false;\n                break;\n        }\n        rs->x += (float)positions[i].x_advance / 64.0f;\n    }\n    return true;\n}\n\nstatic bool\nprocess_codepoint(RenderCtx *ctx, RenderState *rs, char_type codep, char_type next_codep) {\n    bool add_to_current_buffer = false;\n    Face *fallback_font = NULL;\n    if (char_props_for(codep).is_combining_char) {\n        add_to_current_buffer = true;\n    } else if (glyph_id_for_codepoint(&main_face, codep) > 0) {\n        add_to_current_buffer = rs->current_face == &main_face;\n        if (!add_to_current_buffer) fallback_font = &main_face;\n    } else {\n        if (glyph_id_for_codepoint(rs->current_face, codep) > 0) fallback_font = rs->current_face;\n        else fallback_font = find_fallback_font_for(ctx, codep, next_codep);\n        add_to_current_buffer = !fallback_font || rs->current_face == fallback_font;\n    }\n    if (!add_to_current_buffer) {\n        if (rs->pending_in_buffer) {\n            if (!render_run(ctx, rs)) return false;\n            rs->pending_in_buffer = 0;\n            hb_buffer_clear_contents(hb_buffer);\n        }\n        if (fallback_font) rs->current_face = fallback_font;\n    }\n    hb_buffer_add_utf32(hb_buffer, &codep, 1, 0, 1);\n    rs->pending_in_buffer += 1;\n    return true;\n}\n\nbool\nrender_single_line(FreeTypeRenderCtx ctx_, const char *text, unsigned sz_px, pixel fg, pixel bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool horizontally_center_runs) {\n    RenderCtx *ctx = (RenderCtx*)ctx_;\n    if (!ctx->created) return false;\n    size_t output_width = right_margin <= width ? width - right_margin : 0;\n    bool has_text = text && text[0];\n    pixel pbg = premult_pixel(bg, ((bg >> 24) & 0xff));\n    for (size_t y = 0; y < height; y++) {\n        pixel *px = (pixel*)(output_buf + 4 * y * width);\n        for (size_t x = (size_t)x_offset; x < output_width; x++) px[x] = pbg;\n    }\n    if (!has_text) return true;\n    hb_buffer_clear_contents(hb_buffer);\n    if (!hb_buffer_pre_allocate(hb_buffer, 512)) { PyErr_NoMemory(); return false; }\n\n    size_t text_len = strlen(text);\n    char_type *unicode = calloc(text_len + 1, sizeof(char_type));\n    if (!unicode) { PyErr_NoMemory(); return false; }\n    bool ok = false;\n    text_len = decode_utf8_string(text, text_len, unicode);\n    set_pixel_size(ctx, &main_face, sz_px, true);\n    unsigned text_height = font_units_to_pixels_y(main_face.freetype, main_face.freetype->height);\n    RenderState rs = {\n        .current_face = &main_face, .fg = fg, .bg = bg, .horizontally_center = horizontally_center_runs,\n        .output_width = output_width, .output_height = height, .stride = width,\n        .output = (pixel*)output_buf, .x = x_offset, .y = y_offset, .sz_px = sz_px\n    };\n    if (text_height < height) rs.y_offset = (height - text_height) / 2;\n\n    for (size_t i = 0; i < text_len && rs.x < rs.output_width && !rs.truncated; i++) {\n        if (!process_codepoint(ctx, &rs, unicode[i], unicode[i + 1])) goto end;\n    }\n    if (rs.pending_in_buffer && rs.x < rs.output_width && !rs.truncated) {\n        if (!render_run(ctx, &rs)) goto end;\n        rs.pending_in_buffer = 0;\n        hb_buffer_clear_contents(hb_buffer);\n    }\n    if (rs.truncated) {\n        hb_buffer_clear_contents(hb_buffer);\n        rs.pending_in_buffer = 0;\n        rs.current_face = &main_face;\n        if (!process_codepoint(ctx, &rs, ELLIPSIS, 0)) goto end;\n        if (!render_run(ctx, &rs)) goto end;\n    }\n    ok = true;\nend:\n    free(unicode);\n    return ok;\n}\n\nstatic uint8_t*\nrender_single_char_bitmap(const FT_Bitmap *bm, size_t *result_width, size_t *result_height) {\n    *result_width = bm->width; *result_height = bm->rows;\n    uint8_t *rendered = malloc(*result_width * *result_height);\n    if (!rendered) { PyErr_NoMemory(); return NULL; }\n    for (size_t r = 0; r < bm->rows; r++) {\n        uint8_t *src_row = bm->buffer + bm->pitch * r;\n        uint8_t *dest_row = rendered + *result_width * r;\n        memcpy(dest_row, src_row, *result_width);\n    }\n    return rendered;\n}\n\ntypedef struct TempFontData {\n    Face *face;\n    FT_UInt orig_sz;\n} TempFontData;\n\nstatic void\ncleanup_resize(TempFontData *f) {\n    if (f->face && f->face->freetype) {\n        f->face->pixel_size = f->orig_sz;\n        FT_Set_Pixel_Sizes(f->face->freetype, f->orig_sz, f->orig_sz);\n    }\n}\n#define RAII_TempFontData(name) __attribute__((cleanup(cleanup_resize))) TempFontData name = {0}\n\nstatic void*\nreport_freetype_error_for_char(int error, char ch, const char *operation) {\n    char buf[128];\n    snprintf(buf, sizeof(buf), \"Failed to %s glyph for character: %c, with error: \", operation, ch);\n    set_freetype_error(buf, error);\n    return NULL;\n}\n\nuint8_t*\nrender_single_ascii_char_as_mask(FreeTypeRenderCtx ctx_, const char ch, size_t *result_width, size_t *result_height) {\n    RenderCtx *ctx = (RenderCtx*)ctx_;\n    if (!ctx->created) { PyErr_SetString(PyExc_RuntimeError, \"freetype render ctx not created\"); return NULL; }\n    size_t avail_height = *result_height;\n    if (avail_height < 4) { PyErr_Format(PyExc_ValueError, \"Invalid available height: %zu\", avail_height); return NULL; }\n    Face *face = &main_face;\n    RAII_TempFontData(temp);\n    temp.face = face; temp.orig_sz = face->pixel_size;\n    set_pixel_size(ctx, face, avail_height, false);\n    int glyph_index = FT_Get_Char_Index(face->freetype, ch);\n    if (!glyph_index) { PyErr_Format(PyExc_KeyError, \"character %c not found in font\", ch); return NULL; }\n    unsigned int height = font_units_to_pixels_y(face->freetype, face->freetype->height);\n    float ratio = ((float)height) / avail_height;\n    face->pixel_size = (FT_UInt)(face->pixel_size / ratio);\n    if (face->pixel_size != temp.orig_sz) FT_Set_Pixel_Sizes(face->freetype, avail_height, avail_height);\n    int error = FT_Load_Glyph(face->freetype, glyph_index, get_load_flags(face->hinting, face->hintstyle, FT_LOAD_DEFAULT));\n    if (error) return report_freetype_error_for_char(error, ch, \"load\");\n    if (face->freetype->glyph->format != FT_GLYPH_FORMAT_BITMAP) {\n        error = FT_Render_Glyph(face->freetype->glyph, FT_RENDER_MODE_NORMAL);\n        if (error) return report_freetype_error_for_char(error, ch, \"render\");\n    }\n    uint8_t *rendered = NULL;\n    switch(face->freetype->glyph->bitmap.pixel_mode) {\n        case FT_PIXEL_MODE_MONO: {\n            FT_Bitmap bitmap;\n            if (!freetype_convert_mono_bitmap(&face->freetype->glyph->bitmap, &bitmap)) return NULL;\n            rendered = render_single_char_bitmap(&bitmap, result_width, result_height);\n            FT_Bitmap_Done(freetype_library(), &bitmap);\n        }\n            break;\n        case FT_PIXEL_MODE_GRAY:\n            rendered = render_single_char_bitmap(&face->freetype->glyph->bitmap, result_width, result_height);\n            break;\n        default:\n            PyErr_Format(PyExc_TypeError, \"Unknown FreeType bitmap type: 0x%x\", face->freetype->glyph->bitmap.pixel_mode);\n            break;\n    }\n    return rendered;\n}\n\nFreeTypeRenderCtx\ncreate_freetype_render_context(const char *family, bool bold, bool italic) {\n    RenderCtx *ctx = calloc(1, sizeof(RenderCtx));\n    main_face_family.name = family ? strdup(family) : NULL;\n    main_face_family.bold = bold; main_face_family.italic = italic;\n    if (!information_for_font_family(main_face_family.name, main_face_family.bold, main_face_family.italic, &main_face_information)) return NULL;\n    if (!load_font(&main_face_information, &main_face)) return NULL;\n    hb_buffer = hb_buffer_create();\n    if (!hb_buffer) { PyErr_NoMemory(); return NULL; }\n    ctx->created = true;\n    return (FreeTypeRenderCtx)ctx;\n}\n\nvoid\nrelease_freetype_render_context(FreeTypeRenderCtx ctx) { if (ctx) { cleanup((RenderCtx*)ctx); free(ctx); } }\n\nstatic PyObject*\nrender_line(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    // use for testing as below\n    // kitty +runpy \"from kitty.fast_data_types import *; open('/tmp/test.rgba', 'wb').write(freetype_render_line())\" && convert -size 800x60 -depth 8 /tmp/test.rgba /tmp/test.png && icat /tmp/test.png\n    const char *text = \"Test 猫 H🐱🚀b rendering with ellipsis for cut off text\", *family = NULL;\n    unsigned int width = 800, height = 60, right_margin = 0;\n    int bold = 0, italic = 0;\n    unsigned long fg = 0, bg = 0xfffefefe;\n    float x_offset = 0, y_offset = 0;\n    static const char* kwlist[] = {\"text\", \"width\", \"height\", \"font_family\", \"bold\", \"italic\", \"fg\", \"bg\", \"x_offset\", \"y_offset\", \"right_margin\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"|sIIzppkkffI\", (char**)kwlist, &text, &width, &height, &family, &bold, &italic, &fg, &bg, &x_offset, &y_offset, &right_margin)) return NULL;\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, (Py_ssize_t)width * height * 4);\n    if (!ans) return NULL;\n    uint8_t *buffer = (uint8_t*) PyBytes_AS_STRING(ans);\n    RenderCtx *ctx = (RenderCtx*)create_freetype_render_context(family, bold, italic);\n    if (!ctx) return NULL;\n    if (!render_single_line((FreeTypeRenderCtx)ctx, text, 3 * height / 4, 0, 0xffffffff, buffer, width, height, x_offset, y_offset, right_margin, false)) {\n        Py_CLEAR(ans);\n        if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, \"Unknown error while rendering text\");\n        ans = NULL;\n    } else {\n        // remove pre-multiplication and convert to ABGR which is what the ImageMagick .rgba filetype wants\n        for (pixel *p = (pixel*)buffer, *end = (pixel*)(buffer + PyBytes_GET_SIZE(ans)); p < end; p++) {\n            const uint16_t a = (*p >> 24) & 0xff;\n            if (!a) continue;\n            uint16_t r = (*p >> 16) & 0xff, g = (*p >> 8) & 0xff, b = *p & 0xff;\n#define c(x) (((x * 255) / a))\n            *p = ARGB(a, c(b), c(g), c(r));\n#undef c\n        }\n    }\n    release_freetype_render_context((FreeTypeRenderCtx)ctx);\n    return ans;\n}\n\nstatic PyObject*\npath_for_font(PyObject *self UNUSED, PyObject *args) {\n    const char *family = NULL; int bold = 0, italic = 0;\n    if (!PyArg_ParseTuple(args, \"|zpp\", &family, &bold, &italic)) return NULL;\n    FontConfigFace f;\n    if (!information_for_font_family(family, bold, italic, &f)) return NULL;\n    PyObject *ret = Py_BuildValue(\"{ss si si si}\", \"path\", f.path, \"index\", f.index, \"hinting\", f.hinting, \"hintstyle\", f.hintstyle);\n    free(f.path);\n    return ret;\n}\n\nstatic PyObject*\nfallback_for_char(PyObject *self UNUSED, PyObject *args) {\n    const char *family = NULL; int bold = 0, italic = 0;\n    unsigned int ch;\n    if (!PyArg_ParseTuple(args, \"I|zpp\", &ch, &family, &bold, &italic)) return NULL;\n    FontConfigFace f;\n    if (!fallback_font(ch, family, bold, italic, false, &f)) return NULL;\n    PyObject *ret = Py_BuildValue(\"{ss si si si}\", \"path\", f.path, \"index\", f.index, \"hinting\", f.hinting, \"hintstyle\", f.hintstyle);\n    free(f.path);\n    return ret;\n}\n\nstatic PyMethodDef module_methods[] = {\n    {\"fontconfig_path_for_font\", (PyCFunction)(void (*) (void))(path_for_font), METH_VARARGS, NULL},\n    {\"fontconfig_fallback_for_char\", (PyCFunction)(void (*) (void))(fallback_for_char), METH_VARARGS, NULL},\n    {\"freetype_render_line\", (PyCFunction)(void (*) (void))(render_line), METH_VARARGS | METH_KEYWORDS, NULL},\n\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\n\nbool\ninit_freetype_render_ui_text(PyObject *module) {\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    return true;\n}\n"
  },
  {
    "path": "kitty/freetype_render_ui_text.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n#include <hb-ft.h>\n\ntypedef struct {bool created;} *FreeTypeRenderCtx;\n\nFreeTypeRenderCtx create_freetype_render_context(const char *family, bool bold, bool italic);\nvoid set_main_face_family(FreeTypeRenderCtx ctx, const char *family, bool bold, bool italic);\nbool render_single_line(FreeTypeRenderCtx ctx, const char *text, unsigned sz_px, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool horizontally_center_runs);\nuint8_t* render_single_ascii_char_as_mask(FreeTypeRenderCtx ctx_, const char ch, size_t *result_width, size_t *result_height);\nvoid release_freetype_render_context(FreeTypeRenderCtx ctx);\n\ntypedef struct FontConfigFace {\n    char *path;\n    int index;\n    int hinting;\n    int hintstyle;\n} FontConfigFace;\n\nbool information_for_font_family(const char *family, bool bold, bool italic, FontConfigFace *ans);\nFT_Face native_face_from_path(const char *path, int index);\nbool fallback_font(char_type ch, const char *family, bool bold, bool italic, bool prefer_color, FontConfigFace *ans);\nbool freetype_convert_mono_bitmap(FT_Bitmap *src, FT_Bitmap *dest);\nFT_Library freetype_library(void);\nvoid set_freetype_error(const char* prefix, int err_code);\nint downsample_32bit_image(uint8_t *src, unsigned src_width, unsigned src_height, unsigned src_stride, uint8_t *dest, unsigned dest_width, unsigned dest_height);\n"
  },
  {
    "path": "kitty/gl-wrapper.c",
    "content": "#define GLAD_GL_IMPLEMENTATION\n#include \"gl-wrapper.h\"\n"
  },
  {
    "path": "kitty/gl-wrapper.h",
    "content": "/**\n * Loader generated by glad 2.0.8 on Mon Aug 11 01:10:28 2025\n *\n * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0\n *\n * Generator: C/C++\n * Specification: gl\n * Extensions: 8\n *\n * APIs:\n *  - gl:core=3.1\n *\n * Options:\n *  - ALIAS = False\n *  - DEBUG = True\n *  - HEADER_ONLY = True\n *  - LOADER = False\n *  - MX = False\n *  - ON_DEMAND = False\n *\n * Commandline:\n *    --api='gl:core=3.1' --extensions='GL_ARB_copy_image,GL_ARB_framebuffer_sRGB,GL_ARB_instanced_arrays,GL_ARB_multisample,GL_ARB_robustness,GL_ARB_texture_storage,GL_EXT_framebuffer_sRGB,GL_KHR_debug' c --debug --header-only\n *\n * Online:\n *    http://glad.sh/#api=gl%3Acore%3D3.1&extensions=GL_ARB_copy_image%2CGL_ARB_framebuffer_sRGB%2CGL_ARB_instanced_arrays%2CGL_ARB_multisample%2CGL_ARB_robustness%2CGL_ARB_texture_storage%2CGL_EXT_framebuffer_sRGB%2CGL_KHR_debug&generator=c&options=DEBUG%2CHEADER_ONLY\n *\n */\n#ifndef GLAD_GL_H_\n#define GLAD_GL_H_\n#ifdef __clang__\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wreserved-id-macro\"\n#endif\n#ifdef __gl_h_\n  #error OpenGL (gl.h) header already included (API: gl), remove previous include!\n#endif\n#define __gl_h_ 1\n#ifdef __gl3_h_\n  #error OpenGL (gl3.h) header already included (API: gl), remove previous include!\n#endif\n#define __gl3_h_ 1\n#ifdef __glext_h_\n  #error OpenGL (glext.h) header already included (API: gl), remove previous include!\n#endif\n#define __glext_h_ 1\n#ifdef __gl3ext_h_\n  #error OpenGL (gl3ext.h) header already included (API: gl), remove previous include!\n#endif\n#define __gl3ext_h_ 1\n#ifdef __clang__\n#pragma clang diagnostic pop\n#endif\n#define GLAD_GL\n#define GLAD_OPTION_GL_DEBUG\n#define GLAD_OPTION_GL_HEADER_ONLY\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#ifndef GLAD_PLATFORM_H_\n#define GLAD_PLATFORM_H_\n#ifndef GLAD_PLATFORM_WIN32\n  #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)\n    #define GLAD_PLATFORM_WIN32 1\n  #else\n    #define GLAD_PLATFORM_WIN32 0\n  #endif\n#endif\n#ifndef GLAD_PLATFORM_APPLE\n  #ifdef __APPLE__\n    #define GLAD_PLATFORM_APPLE 1\n  #else\n    #define GLAD_PLATFORM_APPLE 0\n  #endif\n#endif\n#ifndef GLAD_PLATFORM_EMSCRIPTEN\n  #ifdef __EMSCRIPTEN__\n    #define GLAD_PLATFORM_EMSCRIPTEN 1\n  #else\n    #define GLAD_PLATFORM_EMSCRIPTEN 0\n  #endif\n#endif\n#ifndef GLAD_PLATFORM_UWP\n  #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY)\n    #ifdef __has_include\n      #if __has_include(<winapifamily.h>)\n        #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1\n      #endif\n    #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_\n      #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1\n    #endif\n  #endif\n  #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY\n    #include <winapifamily.h>\n    #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)\n      #define GLAD_PLATFORM_UWP 1\n    #endif\n  #endif\n  #ifndef GLAD_PLATFORM_UWP\n    #define GLAD_PLATFORM_UWP 0\n  #endif\n#endif\n#ifdef __GNUC__\n  #define GLAD_GNUC_EXTENSION __extension__\n#else\n  #define GLAD_GNUC_EXTENSION\n#endif\n#define GLAD_UNUSED(x) (void)(x)\n#ifndef GLAD_API_CALL\n  #if defined(GLAD_API_CALL_EXPORT)\n    #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__)\n      #if defined(GLAD_API_CALL_EXPORT_BUILD)\n        #if defined(__GNUC__)\n          #define GLAD_API_CALL __attribute__ ((dllexport)) extern\n        #else\n          #define GLAD_API_CALL __declspec(dllexport) extern\n        #endif\n      #else\n        #if defined(__GNUC__)\n          #define GLAD_API_CALL __attribute__ ((dllimport)) extern\n        #else\n          #define GLAD_API_CALL __declspec(dllimport) extern\n        #endif\n      #endif\n    #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD)\n      #define GLAD_API_CALL __attribute__ ((visibility (\"default\"))) extern\n    #else\n      #define GLAD_API_CALL extern\n    #endif\n  #else\n    #define GLAD_API_CALL extern\n  #endif\n#endif\n#ifdef APIENTRY\n  #define GLAD_API_PTR APIENTRY\n#elif GLAD_PLATFORM_WIN32\n  #define GLAD_API_PTR __stdcall\n#else\n  #define GLAD_API_PTR\n#endif\n#ifndef GLAPI\n#define GLAPI GLAD_API_CALL\n#endif\n#ifndef GLAPIENTRY\n#define GLAPIENTRY GLAD_API_PTR\n#endif\n#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor)\n#define GLAD_VERSION_MAJOR(version) (version / 10000)\n#define GLAD_VERSION_MINOR(version) (version % 10000)\n#define GLAD_GENERATOR_VERSION \"2.0.8\"\ntypedef void (*GLADapiproc)(void);\ntypedef GLADapiproc (*GLADloadfunc)(const char *name);\ntypedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name);\ntypedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...);\ntypedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...);\n#endif /* GLAD_PLATFORM_H_ */\n#define GL_2D 0x0600\n#define GL_2_BYTES 0x1407\n#define GL_3D 0x0601\n#define GL_3D_COLOR 0x0602\n#define GL_3D_COLOR_TEXTURE 0x0603\n#define GL_3_BYTES 0x1408\n#define GL_4D_COLOR_TEXTURE 0x0604\n#define GL_4_BYTES 0x1409\n#define GL_ACCUM 0x0100\n#define GL_ACCUM_ALPHA_BITS 0x0D5B\n#define GL_ACCUM_BLUE_BITS 0x0D5A\n#define GL_ACCUM_BUFFER_BIT 0x00000200\n#define GL_ACCUM_CLEAR_VALUE 0x0B80\n#define GL_ACCUM_GREEN_BITS 0x0D59\n#define GL_ACCUM_RED_BITS 0x0D58\n#define GL_ACTIVE_ATTRIBUTES 0x8B89\n#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A\n#define GL_ACTIVE_TEXTURE 0x84E0\n#define GL_ACTIVE_UNIFORMS 0x8B86\n#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36\n#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35\n#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87\n#define GL_ADD 0x0104\n#define GL_ADD_SIGNED 0x8574\n#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E\n#define GL_ALIASED_POINT_SIZE_RANGE 0x846D\n#define GL_ALL_ATTRIB_BITS 0xFFFFFFFF\n#define GL_ALPHA 0x1906\n#define GL_ALPHA12 0x803D\n#define GL_ALPHA16 0x803E\n#define GL_ALPHA4 0x803B\n#define GL_ALPHA8 0x803C\n#define GL_ALPHA_BIAS 0x0D1D\n#define GL_ALPHA_BITS 0x0D55\n#define GL_ALPHA_INTEGER 0x8D97\n#define GL_ALPHA_SCALE 0x0D1C\n#define GL_ALPHA_TEST 0x0BC0\n#define GL_ALPHA_TEST_FUNC 0x0BC1\n#define GL_ALPHA_TEST_REF 0x0BC2\n#define GL_ALWAYS 0x0207\n#define GL_AMBIENT 0x1200\n#define GL_AMBIENT_AND_DIFFUSE 0x1602\n#define GL_AND 0x1501\n#define GL_AND_INVERTED 0x1504\n#define GL_AND_REVERSE 0x1502\n#define GL_ARRAY_BUFFER 0x8892\n#define GL_ARRAY_BUFFER_BINDING 0x8894\n#define GL_ATTACHED_SHADERS 0x8B85\n#define GL_ATTRIB_STACK_DEPTH 0x0BB0\n#define GL_AUTO_NORMAL 0x0D80\n#define GL_AUX0 0x0409\n#define GL_AUX1 0x040A\n#define GL_AUX2 0x040B\n#define GL_AUX3 0x040C\n#define GL_AUX_BUFFERS 0x0C00\n#define GL_BACK 0x0405\n#define GL_BACK_LEFT 0x0402\n#define GL_BACK_RIGHT 0x0403\n#define GL_BGR 0x80E0\n#define GL_BGRA 0x80E1\n#define GL_BGRA_INTEGER 0x8D9B\n#define GL_BGR_INTEGER 0x8D9A\n#define GL_BITMAP 0x1A00\n#define GL_BITMAP_TOKEN 0x0704\n#define GL_BLEND 0x0BE2\n#define GL_BLEND_COLOR 0x8005\n#define GL_BLEND_DST 0x0BE0\n#define GL_BLEND_DST_ALPHA 0x80CA\n#define GL_BLEND_DST_RGB 0x80C8\n#define GL_BLEND_EQUATION 0x8009\n#define GL_BLEND_EQUATION_ALPHA 0x883D\n#define GL_BLEND_EQUATION_RGB 0x8009\n#define GL_BLEND_SRC 0x0BE1\n#define GL_BLEND_SRC_ALPHA 0x80CB\n#define GL_BLEND_SRC_RGB 0x80C9\n#define GL_BLUE 0x1905\n#define GL_BLUE_BIAS 0x0D1B\n#define GL_BLUE_BITS 0x0D54\n#define GL_BLUE_INTEGER 0x8D96\n#define GL_BLUE_SCALE 0x0D1A\n#define GL_BOOL 0x8B56\n#define GL_BOOL_VEC2 0x8B57\n#define GL_BOOL_VEC3 0x8B58\n#define GL_BOOL_VEC4 0x8B59\n#define GL_BUFFER 0x82E0\n#define GL_BUFFER_ACCESS 0x88BB\n#define GL_BUFFER_ACCESS_FLAGS 0x911F\n#define GL_BUFFER_MAPPED 0x88BC\n#define GL_BUFFER_MAP_LENGTH 0x9120\n#define GL_BUFFER_MAP_OFFSET 0x9121\n#define GL_BUFFER_MAP_POINTER 0x88BD\n#define GL_BUFFER_SIZE 0x8764\n#define GL_BUFFER_USAGE 0x8765\n#define GL_BYTE 0x1400\n#define GL_C3F_V3F 0x2A24\n#define GL_C4F_N3F_V3F 0x2A26\n#define GL_C4UB_V2F 0x2A22\n#define GL_C4UB_V3F 0x2A23\n#define GL_CCW 0x0901\n#define GL_CLAMP 0x2900\n#define GL_CLAMP_FRAGMENT_COLOR 0x891B\n#define GL_CLAMP_READ_COLOR 0x891C\n#define GL_CLAMP_TO_BORDER 0x812D\n#define GL_CLAMP_TO_EDGE 0x812F\n#define GL_CLAMP_VERTEX_COLOR 0x891A\n#define GL_CLEAR 0x1500\n#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1\n#define GL_CLIENT_ALL_ATTRIB_BITS 0xFFFFFFFF\n#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1\n#define GL_CLIENT_PIXEL_STORE_BIT 0x00000001\n#define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002\n#define GL_CLIP_DISTANCE0 0x3000\n#define GL_CLIP_DISTANCE1 0x3001\n#define GL_CLIP_DISTANCE2 0x3002\n#define GL_CLIP_DISTANCE3 0x3003\n#define GL_CLIP_DISTANCE4 0x3004\n#define GL_CLIP_DISTANCE5 0x3005\n#define GL_CLIP_DISTANCE6 0x3006\n#define GL_CLIP_DISTANCE7 0x3007\n#define GL_CLIP_PLANE0 0x3000\n#define GL_CLIP_PLANE1 0x3001\n#define GL_CLIP_PLANE2 0x3002\n#define GL_CLIP_PLANE3 0x3003\n#define GL_CLIP_PLANE4 0x3004\n#define GL_CLIP_PLANE5 0x3005\n#define GL_COEFF 0x0A00\n#define GL_COLOR 0x1800\n#define GL_COLOR_ARRAY 0x8076\n#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898\n#define GL_COLOR_ARRAY_POINTER 0x8090\n#define GL_COLOR_ARRAY_SIZE 0x8081\n#define GL_COLOR_ARRAY_STRIDE 0x8083\n#define GL_COLOR_ARRAY_TYPE 0x8082\n#define GL_COLOR_ATTACHMENT0 0x8CE0\n#define GL_COLOR_ATTACHMENT1 0x8CE1\n#define GL_COLOR_ATTACHMENT10 0x8CEA\n#define GL_COLOR_ATTACHMENT11 0x8CEB\n#define GL_COLOR_ATTACHMENT12 0x8CEC\n#define GL_COLOR_ATTACHMENT13 0x8CED\n#define GL_COLOR_ATTACHMENT14 0x8CEE\n#define GL_COLOR_ATTACHMENT15 0x8CEF\n#define GL_COLOR_ATTACHMENT16 0x8CF0\n#define GL_COLOR_ATTACHMENT17 0x8CF1\n#define GL_COLOR_ATTACHMENT18 0x8CF2\n#define GL_COLOR_ATTACHMENT19 0x8CF3\n#define GL_COLOR_ATTACHMENT2 0x8CE2\n#define GL_COLOR_ATTACHMENT20 0x8CF4\n#define GL_COLOR_ATTACHMENT21 0x8CF5\n#define GL_COLOR_ATTACHMENT22 0x8CF6\n#define GL_COLOR_ATTACHMENT23 0x8CF7\n#define GL_COLOR_ATTACHMENT24 0x8CF8\n#define GL_COLOR_ATTACHMENT25 0x8CF9\n#define GL_COLOR_ATTACHMENT26 0x8CFA\n#define GL_COLOR_ATTACHMENT27 0x8CFB\n#define GL_COLOR_ATTACHMENT28 0x8CFC\n#define GL_COLOR_ATTACHMENT29 0x8CFD\n#define GL_COLOR_ATTACHMENT3 0x8CE3\n#define GL_COLOR_ATTACHMENT30 0x8CFE\n#define GL_COLOR_ATTACHMENT31 0x8CFF\n#define GL_COLOR_ATTACHMENT4 0x8CE4\n#define GL_COLOR_ATTACHMENT5 0x8CE5\n#define GL_COLOR_ATTACHMENT6 0x8CE6\n#define GL_COLOR_ATTACHMENT7 0x8CE7\n#define GL_COLOR_ATTACHMENT8 0x8CE8\n#define GL_COLOR_ATTACHMENT9 0x8CE9\n#define GL_COLOR_BUFFER_BIT 0x00004000\n#define GL_COLOR_CLEAR_VALUE 0x0C22\n#define GL_COLOR_INDEX 0x1900\n#define GL_COLOR_INDEXES 0x1603\n#define GL_COLOR_LOGIC_OP 0x0BF2\n#define GL_COLOR_MATERIAL 0x0B57\n#define GL_COLOR_MATERIAL_FACE 0x0B55\n#define GL_COLOR_MATERIAL_PARAMETER 0x0B56\n#define GL_COLOR_SUM 0x8458\n#define GL_COLOR_WRITEMASK 0x0C23\n#define GL_COMBINE 0x8570\n#define GL_COMBINE_ALPHA 0x8572\n#define GL_COMBINE_RGB 0x8571\n#define GL_COMPARE_REF_TO_TEXTURE 0x884E\n#define GL_COMPARE_R_TO_TEXTURE 0x884E\n#define GL_COMPILE 0x1300\n#define GL_COMPILE_AND_EXECUTE 0x1301\n#define GL_COMPILE_STATUS 0x8B81\n#define GL_COMPRESSED_ALPHA 0x84E9\n#define GL_COMPRESSED_INTENSITY 0x84EC\n#define GL_COMPRESSED_LUMINANCE 0x84EA\n#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB\n#define GL_COMPRESSED_RED 0x8225\n#define GL_COMPRESSED_RED_RGTC1 0x8DBB\n#define GL_COMPRESSED_RG 0x8226\n#define GL_COMPRESSED_RGB 0x84ED\n#define GL_COMPRESSED_RGBA 0x84EE\n#define GL_COMPRESSED_RG_RGTC2 0x8DBD\n#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC\n#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE\n#define GL_COMPRESSED_SLUMINANCE 0x8C4A\n#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B\n#define GL_COMPRESSED_SRGB 0x8C48\n#define GL_COMPRESSED_SRGB_ALPHA 0x8C49\n#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3\n#define GL_CONSTANT 0x8576\n#define GL_CONSTANT_ALPHA 0x8003\n#define GL_CONSTANT_ATTENUATION 0x1207\n#define GL_CONSTANT_COLOR 0x8001\n#define GL_CONTEXT_FLAGS 0x821E\n#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002\n#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001\n#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004\n#define GL_COORD_REPLACE 0x8862\n#define GL_COPY 0x1503\n#define GL_COPY_INVERTED 0x150C\n#define GL_COPY_PIXEL_TOKEN 0x0706\n#define GL_COPY_READ_BUFFER 0x8F36\n#define GL_COPY_WRITE_BUFFER 0x8F37\n#define GL_CULL_FACE 0x0B44\n#define GL_CULL_FACE_MODE 0x0B45\n#define GL_CURRENT_BIT 0x00000001\n#define GL_CURRENT_COLOR 0x0B00\n#define GL_CURRENT_FOG_COORD 0x8453\n#define GL_CURRENT_FOG_COORDINATE 0x8453\n#define GL_CURRENT_INDEX 0x0B01\n#define GL_CURRENT_NORMAL 0x0B02\n#define GL_CURRENT_PROGRAM 0x8B8D\n#define GL_CURRENT_QUERY 0x8865\n#define GL_CURRENT_RASTER_COLOR 0x0B04\n#define GL_CURRENT_RASTER_DISTANCE 0x0B09\n#define GL_CURRENT_RASTER_INDEX 0x0B05\n#define GL_CURRENT_RASTER_POSITION 0x0B07\n#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08\n#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F\n#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06\n#define GL_CURRENT_SECONDARY_COLOR 0x8459\n#define GL_CURRENT_TEXTURE_COORDS 0x0B03\n#define GL_CURRENT_VERTEX_ATTRIB 0x8626\n#define GL_CW 0x0900\n#define GL_DEBUG_CALLBACK_FUNCTION 0x8244\n#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245\n#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D\n#define GL_DEBUG_LOGGED_MESSAGES 0x9145\n#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243\n#define GL_DEBUG_OUTPUT 0x92E0\n#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242\n#define GL_DEBUG_SEVERITY_HIGH 0x9146\n#define GL_DEBUG_SEVERITY_LOW 0x9148\n#define GL_DEBUG_SEVERITY_MEDIUM 0x9147\n#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B\n#define GL_DEBUG_SOURCE_API 0x8246\n#define GL_DEBUG_SOURCE_APPLICATION 0x824A\n#define GL_DEBUG_SOURCE_OTHER 0x824B\n#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248\n#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249\n#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247\n#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D\n#define GL_DEBUG_TYPE_ERROR 0x824C\n#define GL_DEBUG_TYPE_MARKER 0x8268\n#define GL_DEBUG_TYPE_OTHER 0x8251\n#define GL_DEBUG_TYPE_PERFORMANCE 0x8250\n#define GL_DEBUG_TYPE_POP_GROUP 0x826A\n#define GL_DEBUG_TYPE_PORTABILITY 0x824F\n#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269\n#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E\n#define GL_DECAL 0x2101\n#define GL_DECR 0x1E03\n#define GL_DECR_WRAP 0x8508\n#define GL_DELETE_STATUS 0x8B80\n#define GL_DEPTH 0x1801\n#define GL_DEPTH24_STENCIL8 0x88F0\n#define GL_DEPTH32F_STENCIL8 0x8CAD\n#define GL_DEPTH_ATTACHMENT 0x8D00\n#define GL_DEPTH_BIAS 0x0D1F\n#define GL_DEPTH_BITS 0x0D56\n#define GL_DEPTH_BUFFER_BIT 0x00000100\n#define GL_DEPTH_CLEAR_VALUE 0x0B73\n#define GL_DEPTH_COMPONENT 0x1902\n#define GL_DEPTH_COMPONENT16 0x81A5\n#define GL_DEPTH_COMPONENT24 0x81A6\n#define GL_DEPTH_COMPONENT32 0x81A7\n#define GL_DEPTH_COMPONENT32F 0x8CAC\n#define GL_DEPTH_FUNC 0x0B74\n#define GL_DEPTH_RANGE 0x0B70\n#define GL_DEPTH_SCALE 0x0D1E\n#define GL_DEPTH_STENCIL 0x84F9\n#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A\n#define GL_DEPTH_TEST 0x0B71\n#define GL_DEPTH_TEXTURE_MODE 0x884B\n#define GL_DEPTH_WRITEMASK 0x0B72\n#define GL_DIFFUSE 0x1201\n#define GL_DITHER 0x0BD0\n#define GL_DOMAIN 0x0A02\n#define GL_DONT_CARE 0x1100\n#define GL_DOT3_RGB 0x86AE\n#define GL_DOT3_RGBA 0x86AF\n#define GL_DOUBLE 0x140A\n#define GL_DOUBLEBUFFER 0x0C32\n#define GL_DRAW_BUFFER 0x0C01\n#define GL_DRAW_BUFFER0 0x8825\n#define GL_DRAW_BUFFER1 0x8826\n#define GL_DRAW_BUFFER10 0x882F\n#define GL_DRAW_BUFFER11 0x8830\n#define GL_DRAW_BUFFER12 0x8831\n#define GL_DRAW_BUFFER13 0x8832\n#define GL_DRAW_BUFFER14 0x8833\n#define GL_DRAW_BUFFER15 0x8834\n#define GL_DRAW_BUFFER2 0x8827\n#define GL_DRAW_BUFFER3 0x8828\n#define GL_DRAW_BUFFER4 0x8829\n#define GL_DRAW_BUFFER5 0x882A\n#define GL_DRAW_BUFFER6 0x882B\n#define GL_DRAW_BUFFER7 0x882C\n#define GL_DRAW_BUFFER8 0x882D\n#define GL_DRAW_BUFFER9 0x882E\n#define GL_DRAW_FRAMEBUFFER 0x8CA9\n#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6\n#define GL_DRAW_PIXEL_TOKEN 0x0705\n#define GL_DST_ALPHA 0x0304\n#define GL_DST_COLOR 0x0306\n#define GL_DYNAMIC_COPY 0x88EA\n#define GL_DYNAMIC_DRAW 0x88E8\n#define GL_DYNAMIC_READ 0x88E9\n#define GL_EDGE_FLAG 0x0B43\n#define GL_EDGE_FLAG_ARRAY 0x8079\n#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B\n#define GL_EDGE_FLAG_ARRAY_POINTER 0x8093\n#define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C\n#define GL_ELEMENT_ARRAY_BUFFER 0x8893\n#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895\n#define GL_EMISSION 0x1600\n#define GL_ENABLE_BIT 0x00002000\n#define GL_EQUAL 0x0202\n#define GL_EQUIV 0x1509\n#define GL_EVAL_BIT 0x00010000\n#define GL_EXP 0x0800\n#define GL_EXP2 0x0801\n#define GL_EXTENSIONS 0x1F03\n#define GL_EYE_LINEAR 0x2400\n#define GL_EYE_PLANE 0x2502\n#define GL_FALSE 0\n#define GL_FASTEST 0x1101\n#define GL_FEEDBACK 0x1C01\n#define GL_FEEDBACK_BUFFER_POINTER 0x0DF0\n#define GL_FEEDBACK_BUFFER_SIZE 0x0DF1\n#define GL_FEEDBACK_BUFFER_TYPE 0x0DF2\n#define GL_FILL 0x1B02\n#define GL_FIXED_ONLY 0x891D\n#define GL_FLAT 0x1D00\n#define GL_FLOAT 0x1406\n#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD\n#define GL_FLOAT_MAT2 0x8B5A\n#define GL_FLOAT_MAT2x3 0x8B65\n#define GL_FLOAT_MAT2x4 0x8B66\n#define GL_FLOAT_MAT3 0x8B5B\n#define GL_FLOAT_MAT3x2 0x8B67\n#define GL_FLOAT_MAT3x4 0x8B68\n#define GL_FLOAT_MAT4 0x8B5C\n#define GL_FLOAT_MAT4x2 0x8B69\n#define GL_FLOAT_MAT4x3 0x8B6A\n#define GL_FLOAT_VEC2 0x8B50\n#define GL_FLOAT_VEC3 0x8B51\n#define GL_FLOAT_VEC4 0x8B52\n#define GL_FOG 0x0B60\n#define GL_FOG_BIT 0x00000080\n#define GL_FOG_COLOR 0x0B66\n#define GL_FOG_COORD 0x8451\n#define GL_FOG_COORDINATE 0x8451\n#define GL_FOG_COORDINATE_ARRAY 0x8457\n#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D\n#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456\n#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455\n#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454\n#define GL_FOG_COORDINATE_SOURCE 0x8450\n#define GL_FOG_COORD_ARRAY 0x8457\n#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D\n#define GL_FOG_COORD_ARRAY_POINTER 0x8456\n#define GL_FOG_COORD_ARRAY_STRIDE 0x8455\n#define GL_FOG_COORD_ARRAY_TYPE 0x8454\n#define GL_FOG_COORD_SRC 0x8450\n#define GL_FOG_DENSITY 0x0B62\n#define GL_FOG_END 0x0B64\n#define GL_FOG_HINT 0x0C54\n#define GL_FOG_INDEX 0x0B61\n#define GL_FOG_MODE 0x0B65\n#define GL_FOG_START 0x0B63\n#define GL_FRAGMENT_DEPTH 0x8452\n#define GL_FRAGMENT_SHADER 0x8B30\n#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B\n#define GL_FRAMEBUFFER 0x8D40\n#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215\n#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214\n#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210\n#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211\n#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216\n#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0\n#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212\n#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2\n#define GL_FRAMEBUFFER_BINDING 0x8CA6\n#define GL_FRAMEBUFFER_COMPLETE 0x8CD5\n#define GL_FRAMEBUFFER_DEFAULT 0x8218\n#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6\n#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB\n#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7\n#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56\n#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC\n#define GL_FRAMEBUFFER_SRGB 0x8DB9\n#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA\n#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9\n#define GL_FRAMEBUFFER_UNDEFINED 0x8219\n#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD\n#define GL_FRONT 0x0404\n#define GL_FRONT_AND_BACK 0x0408\n#define GL_FRONT_FACE 0x0B46\n#define GL_FRONT_LEFT 0x0400\n#define GL_FRONT_RIGHT 0x0401\n#define GL_FUNC_ADD 0x8006\n#define GL_FUNC_REVERSE_SUBTRACT 0x800B\n#define GL_FUNC_SUBTRACT 0x800A\n#define GL_GENERATE_MIPMAP 0x8191\n#define GL_GENERATE_MIPMAP_HINT 0x8192\n#define GL_GEQUAL 0x0206\n#define GL_GREATER 0x0204\n#define GL_GREEN 0x1904\n#define GL_GREEN_BIAS 0x0D19\n#define GL_GREEN_BITS 0x0D53\n#define GL_GREEN_INTEGER 0x8D95\n#define GL_GREEN_SCALE 0x0D18\n#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253\n#define GL_HALF_FLOAT 0x140B\n#define GL_HINT_BIT 0x00008000\n#define GL_INCR 0x1E02\n#define GL_INCR_WRAP 0x8507\n#define GL_INDEX 0x8222\n#define GL_INDEX_ARRAY 0x8077\n#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899\n#define GL_INDEX_ARRAY_POINTER 0x8091\n#define GL_INDEX_ARRAY_STRIDE 0x8086\n#define GL_INDEX_ARRAY_TYPE 0x8085\n#define GL_INDEX_BITS 0x0D51\n#define GL_INDEX_CLEAR_VALUE 0x0C20\n#define GL_INDEX_LOGIC_OP 0x0BF1\n#define GL_INDEX_MODE 0x0C30\n#define GL_INDEX_OFFSET 0x0D13\n#define GL_INDEX_SHIFT 0x0D12\n#define GL_INDEX_WRITEMASK 0x0C21\n#define GL_INFO_LOG_LENGTH 0x8B84\n#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254\n#define GL_INT 0x1404\n#define GL_INTENSITY 0x8049\n#define GL_INTENSITY12 0x804C\n#define GL_INTENSITY16 0x804D\n#define GL_INTENSITY4 0x804A\n#define GL_INTENSITY8 0x804B\n#define GL_INTERLEAVED_ATTRIBS 0x8C8C\n#define GL_INTERPOLATE 0x8575\n#define GL_INT_SAMPLER_1D 0x8DC9\n#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE\n#define GL_INT_SAMPLER_2D 0x8DCA\n#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF\n#define GL_INT_SAMPLER_2D_RECT 0x8DCD\n#define GL_INT_SAMPLER_3D 0x8DCB\n#define GL_INT_SAMPLER_BUFFER 0x8DD0\n#define GL_INT_SAMPLER_CUBE 0x8DCC\n#define GL_INT_VEC2 0x8B53\n#define GL_INT_VEC3 0x8B54\n#define GL_INT_VEC4 0x8B55\n#define GL_INVALID_ENUM 0x0500\n#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506\n#define GL_INVALID_INDEX 0xFFFFFFFF\n#define GL_INVALID_OPERATION 0x0502\n#define GL_INVALID_VALUE 0x0501\n#define GL_INVERT 0x150A\n#define GL_KEEP 0x1E00\n#define GL_LEFT 0x0406\n#define GL_LEQUAL 0x0203\n#define GL_LESS 0x0201\n#define GL_LIGHT0 0x4000\n#define GL_LIGHT1 0x4001\n#define GL_LIGHT2 0x4002\n#define GL_LIGHT3 0x4003\n#define GL_LIGHT4 0x4004\n#define GL_LIGHT5 0x4005\n#define GL_LIGHT6 0x4006\n#define GL_LIGHT7 0x4007\n#define GL_LIGHTING 0x0B50\n#define GL_LIGHTING_BIT 0x00000040\n#define GL_LIGHT_MODEL_AMBIENT 0x0B53\n#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8\n#define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51\n#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52\n#define GL_LINE 0x1B01\n#define GL_LINEAR 0x2601\n#define GL_LINEAR_ATTENUATION 0x1208\n#define GL_LINEAR_MIPMAP_LINEAR 0x2703\n#define GL_LINEAR_MIPMAP_NEAREST 0x2701\n#define GL_LINES 0x0001\n#define GL_LINE_BIT 0x00000004\n#define GL_LINE_LOOP 0x0002\n#define GL_LINE_RESET_TOKEN 0x0707\n#define GL_LINE_SMOOTH 0x0B20\n#define GL_LINE_SMOOTH_HINT 0x0C52\n#define GL_LINE_STIPPLE 0x0B24\n#define GL_LINE_STIPPLE_PATTERN 0x0B25\n#define GL_LINE_STIPPLE_REPEAT 0x0B26\n#define GL_LINE_STRIP 0x0003\n#define GL_LINE_TOKEN 0x0702\n#define GL_LINE_WIDTH 0x0B21\n#define GL_LINE_WIDTH_GRANULARITY 0x0B23\n#define GL_LINE_WIDTH_RANGE 0x0B22\n#define GL_LINK_STATUS 0x8B82\n#define GL_LIST_BASE 0x0B32\n#define GL_LIST_BIT 0x00020000\n#define GL_LIST_INDEX 0x0B33\n#define GL_LIST_MODE 0x0B30\n#define GL_LOAD 0x0101\n#define GL_LOGIC_OP 0x0BF1\n#define GL_LOGIC_OP_MODE 0x0BF0\n#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252\n#define GL_LOWER_LEFT 0x8CA1\n#define GL_LUMINANCE 0x1909\n#define GL_LUMINANCE12 0x8041\n#define GL_LUMINANCE12_ALPHA12 0x8047\n#define GL_LUMINANCE12_ALPHA4 0x8046\n#define GL_LUMINANCE16 0x8042\n#define GL_LUMINANCE16_ALPHA16 0x8048\n#define GL_LUMINANCE4 0x803F\n#define GL_LUMINANCE4_ALPHA4 0x8043\n#define GL_LUMINANCE6_ALPHA2 0x8044\n#define GL_LUMINANCE8 0x8040\n#define GL_LUMINANCE8_ALPHA8 0x8045\n#define GL_LUMINANCE_ALPHA 0x190A\n#define GL_MAJOR_VERSION 0x821B\n#define GL_MAP1_COLOR_4 0x0D90\n#define GL_MAP1_GRID_DOMAIN 0x0DD0\n#define GL_MAP1_GRID_SEGMENTS 0x0DD1\n#define GL_MAP1_INDEX 0x0D91\n#define GL_MAP1_NORMAL 0x0D92\n#define GL_MAP1_TEXTURE_COORD_1 0x0D93\n#define GL_MAP1_TEXTURE_COORD_2 0x0D94\n#define GL_MAP1_TEXTURE_COORD_3 0x0D95\n#define GL_MAP1_TEXTURE_COORD_4 0x0D96\n#define GL_MAP1_VERTEX_3 0x0D97\n#define GL_MAP1_VERTEX_4 0x0D98\n#define GL_MAP2_COLOR_4 0x0DB0\n#define GL_MAP2_GRID_DOMAIN 0x0DD2\n#define GL_MAP2_GRID_SEGMENTS 0x0DD3\n#define GL_MAP2_INDEX 0x0DB1\n#define GL_MAP2_NORMAL 0x0DB2\n#define GL_MAP2_TEXTURE_COORD_1 0x0DB3\n#define GL_MAP2_TEXTURE_COORD_2 0x0DB4\n#define GL_MAP2_TEXTURE_COORD_3 0x0DB5\n#define GL_MAP2_TEXTURE_COORD_4 0x0DB6\n#define GL_MAP2_VERTEX_3 0x0DB7\n#define GL_MAP2_VERTEX_4 0x0DB8\n#define GL_MAP_COLOR 0x0D10\n#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010\n#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008\n#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004\n#define GL_MAP_READ_BIT 0x0001\n#define GL_MAP_STENCIL 0x0D11\n#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020\n#define GL_MAP_WRITE_BIT 0x0002\n#define GL_MATRIX_MODE 0x0BA0\n#define GL_MAX 0x8008\n#define GL_MAX_3D_TEXTURE_SIZE 0x8073\n#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF\n#define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35\n#define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B\n#define GL_MAX_CLIP_DISTANCES 0x0D32\n#define GL_MAX_CLIP_PLANES 0x0D32\n#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF\n#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33\n#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32\n#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D\n#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E\n#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31\n#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C\n#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C\n#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144\n#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143\n#define GL_MAX_DRAW_BUFFERS 0x8824\n#define GL_MAX_ELEMENTS_INDICES 0x80E9\n#define GL_MAX_ELEMENTS_VERTICES 0x80E8\n#define GL_MAX_EVAL_ORDER 0x0D30\n#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D\n#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49\n#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C\n#define GL_MAX_LABEL_LENGTH 0x82E8\n#define GL_MAX_LIGHTS 0x0D31\n#define GL_MAX_LIST_NESTING 0x0B31\n#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36\n#define GL_MAX_NAME_STACK_DEPTH 0x0D37\n#define GL_MAX_PIXEL_MAP_TABLE 0x0D34\n#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905\n#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38\n#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8\n#define GL_MAX_RENDERBUFFER_SIZE 0x84E8\n#define GL_MAX_SAMPLES 0x8D57\n#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B\n#define GL_MAX_TEXTURE_COORDS 0x8871\n#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872\n#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD\n#define GL_MAX_TEXTURE_SIZE 0x0D33\n#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39\n#define GL_MAX_TEXTURE_UNITS 0x84E2\n#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80\n#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30\n#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F\n#define GL_MAX_VARYING_COMPONENTS 0x8B4B\n#define GL_MAX_VARYING_FLOATS 0x8B4B\n#define GL_MAX_VERTEX_ATTRIBS 0x8869\n#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C\n#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B\n#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A\n#define GL_MAX_VIEWPORT_DIMS 0x0D3A\n#define GL_MIN 0x8007\n#define GL_MINOR_VERSION 0x821C\n#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904\n#define GL_MIRRORED_REPEAT 0x8370\n#define GL_MODELVIEW 0x1700\n#define GL_MODELVIEW_MATRIX 0x0BA6\n#define GL_MODELVIEW_STACK_DEPTH 0x0BA3\n#define GL_MODULATE 0x2100\n#define GL_MULT 0x0103\n#define GL_MULTISAMPLE 0x809D\n#define GL_MULTISAMPLE_ARB 0x809D\n#define GL_MULTISAMPLE_BIT 0x20000000\n#define GL_MULTISAMPLE_BIT_ARB 0x20000000\n#define GL_N3F_V3F 0x2A25\n#define GL_NAME_STACK_DEPTH 0x0D70\n#define GL_NAND 0x150E\n#define GL_NEAREST 0x2600\n#define GL_NEAREST_MIPMAP_LINEAR 0x2702\n#define GL_NEAREST_MIPMAP_NEAREST 0x2700\n#define GL_NEVER 0x0200\n#define GL_NICEST 0x1102\n#define GL_NONE 0\n#define GL_NOOP 0x1505\n#define GL_NOR 0x1508\n#define GL_NORMALIZE 0x0BA1\n#define GL_NORMAL_ARRAY 0x8075\n#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897\n#define GL_NORMAL_ARRAY_POINTER 0x808F\n#define GL_NORMAL_ARRAY_STRIDE 0x807F\n#define GL_NORMAL_ARRAY_TYPE 0x807E\n#define GL_NORMAL_MAP 0x8511\n#define GL_NOTEQUAL 0x0205\n#define GL_NO_ERROR 0\n#define GL_NO_RESET_NOTIFICATION_ARB 0x8261\n#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2\n#define GL_NUM_EXTENSIONS 0x821D\n#define GL_OBJECT_LINEAR 0x2401\n#define GL_OBJECT_PLANE 0x2501\n#define GL_ONE 1\n#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004\n#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002\n#define GL_ONE_MINUS_DST_ALPHA 0x0305\n#define GL_ONE_MINUS_DST_COLOR 0x0307\n#define GL_ONE_MINUS_SRC_ALPHA 0x0303\n#define GL_ONE_MINUS_SRC_COLOR 0x0301\n#define GL_OPERAND0_ALPHA 0x8598\n#define GL_OPERAND0_RGB 0x8590\n#define GL_OPERAND1_ALPHA 0x8599\n#define GL_OPERAND1_RGB 0x8591\n#define GL_OPERAND2_ALPHA 0x859A\n#define GL_OPERAND2_RGB 0x8592\n#define GL_OR 0x1507\n#define GL_ORDER 0x0A01\n#define GL_OR_INVERTED 0x150D\n#define GL_OR_REVERSE 0x150B\n#define GL_OUT_OF_MEMORY 0x0505\n#define GL_PACK_ALIGNMENT 0x0D05\n#define GL_PACK_IMAGE_HEIGHT 0x806C\n#define GL_PACK_LSB_FIRST 0x0D01\n#define GL_PACK_ROW_LENGTH 0x0D02\n#define GL_PACK_SKIP_IMAGES 0x806B\n#define GL_PACK_SKIP_PIXELS 0x0D04\n#define GL_PACK_SKIP_ROWS 0x0D03\n#define GL_PACK_SWAP_BYTES 0x0D00\n#define GL_PASS_THROUGH_TOKEN 0x0700\n#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50\n#define GL_PIXEL_MAP_A_TO_A 0x0C79\n#define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9\n#define GL_PIXEL_MAP_B_TO_B 0x0C78\n#define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8\n#define GL_PIXEL_MAP_G_TO_G 0x0C77\n#define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7\n#define GL_PIXEL_MAP_I_TO_A 0x0C75\n#define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5\n#define GL_PIXEL_MAP_I_TO_B 0x0C74\n#define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4\n#define GL_PIXEL_MAP_I_TO_G 0x0C73\n#define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3\n#define GL_PIXEL_MAP_I_TO_I 0x0C70\n#define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0\n#define GL_PIXEL_MAP_I_TO_R 0x0C72\n#define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2\n#define GL_PIXEL_MAP_R_TO_R 0x0C76\n#define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6\n#define GL_PIXEL_MAP_S_TO_S 0x0C71\n#define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1\n#define GL_PIXEL_MODE_BIT 0x00000020\n#define GL_PIXEL_PACK_BUFFER 0x88EB\n#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED\n#define GL_PIXEL_UNPACK_BUFFER 0x88EC\n#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF\n#define GL_POINT 0x1B00\n#define GL_POINTS 0x0000\n#define GL_POINT_BIT 0x00000002\n#define GL_POINT_DISTANCE_ATTENUATION 0x8129\n#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128\n#define GL_POINT_SIZE 0x0B11\n#define GL_POINT_SIZE_GRANULARITY 0x0B13\n#define GL_POINT_SIZE_MAX 0x8127\n#define GL_POINT_SIZE_MIN 0x8126\n#define GL_POINT_SIZE_RANGE 0x0B12\n#define GL_POINT_SMOOTH 0x0B10\n#define GL_POINT_SMOOTH_HINT 0x0C51\n#define GL_POINT_SPRITE 0x8861\n#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0\n#define GL_POINT_TOKEN 0x0701\n#define GL_POLYGON 0x0009\n#define GL_POLYGON_BIT 0x00000008\n#define GL_POLYGON_MODE 0x0B40\n#define GL_POLYGON_OFFSET_FACTOR 0x8038\n#define GL_POLYGON_OFFSET_FILL 0x8037\n#define GL_POLYGON_OFFSET_LINE 0x2A02\n#define GL_POLYGON_OFFSET_POINT 0x2A01\n#define GL_POLYGON_OFFSET_UNITS 0x2A00\n#define GL_POLYGON_SMOOTH 0x0B41\n#define GL_POLYGON_SMOOTH_HINT 0x0C53\n#define GL_POLYGON_STIPPLE 0x0B42\n#define GL_POLYGON_STIPPLE_BIT 0x00000010\n#define GL_POLYGON_TOKEN 0x0703\n#define GL_POSITION 0x1203\n#define GL_PREVIOUS 0x8578\n#define GL_PRIMARY_COLOR 0x8577\n#define GL_PRIMITIVES_GENERATED 0x8C87\n#define GL_PRIMITIVE_RESTART 0x8F9D\n#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E\n#define GL_PROGRAM 0x82E2\n#define GL_PROGRAM_PIPELINE 0x82E4\n#define GL_PROJECTION 0x1701\n#define GL_PROJECTION_MATRIX 0x0BA7\n#define GL_PROJECTION_STACK_DEPTH 0x0BA4\n#define GL_PROXY_TEXTURE_1D 0x8063\n#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19\n#define GL_PROXY_TEXTURE_2D 0x8064\n#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B\n#define GL_PROXY_TEXTURE_3D 0x8070\n#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B\n#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7\n#define GL_Q 0x2003\n#define GL_QUADRATIC_ATTENUATION 0x1209\n#define GL_QUADS 0x0007\n#define GL_QUAD_STRIP 0x0008\n#define GL_QUERY 0x82E3\n#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16\n#define GL_QUERY_BY_REGION_WAIT 0x8E15\n#define GL_QUERY_COUNTER_BITS 0x8864\n#define GL_QUERY_NO_WAIT 0x8E14\n#define GL_QUERY_RESULT 0x8866\n#define GL_QUERY_RESULT_AVAILABLE 0x8867\n#define GL_QUERY_WAIT 0x8E13\n#define GL_R 0x2002\n#define GL_R11F_G11F_B10F 0x8C3A\n#define GL_R16 0x822A\n#define GL_R16F 0x822D\n#define GL_R16I 0x8233\n#define GL_R16UI 0x8234\n#define GL_R16_SNORM 0x8F98\n#define GL_R32F 0x822E\n#define GL_R32I 0x8235\n#define GL_R32UI 0x8236\n#define GL_R3_G3_B2 0x2A10\n#define GL_R8 0x8229\n#define GL_R8I 0x8231\n#define GL_R8UI 0x8232\n#define GL_R8_SNORM 0x8F94\n#define GL_RASTERIZER_DISCARD 0x8C89\n#define GL_READ_BUFFER 0x0C02\n#define GL_READ_FRAMEBUFFER 0x8CA8\n#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA\n#define GL_READ_ONLY 0x88B8\n#define GL_READ_WRITE 0x88BA\n#define GL_RED 0x1903\n#define GL_RED_BIAS 0x0D15\n#define GL_RED_BITS 0x0D52\n#define GL_RED_INTEGER 0x8D94\n#define GL_RED_SCALE 0x0D14\n#define GL_REFLECTION_MAP 0x8512\n#define GL_RENDER 0x1C00\n#define GL_RENDERBUFFER 0x8D41\n#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53\n#define GL_RENDERBUFFER_BINDING 0x8CA7\n#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52\n#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54\n#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51\n#define GL_RENDERBUFFER_HEIGHT 0x8D43\n#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44\n#define GL_RENDERBUFFER_RED_SIZE 0x8D50\n#define GL_RENDERBUFFER_SAMPLES 0x8CAB\n#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55\n#define GL_RENDERBUFFER_WIDTH 0x8D42\n#define GL_RENDERER 0x1F01\n#define GL_RENDER_MODE 0x0C40\n#define GL_REPEAT 0x2901\n#define GL_REPLACE 0x1E01\n#define GL_RESCALE_NORMAL 0x803A\n#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256\n#define GL_RETURN 0x0102\n#define GL_RG 0x8227\n#define GL_RG16 0x822C\n#define GL_RG16F 0x822F\n#define GL_RG16I 0x8239\n#define GL_RG16UI 0x823A\n#define GL_RG16_SNORM 0x8F99\n#define GL_RG32F 0x8230\n#define GL_RG32I 0x823B\n#define GL_RG32UI 0x823C\n#define GL_RG8 0x822B\n#define GL_RG8I 0x8237\n#define GL_RG8UI 0x8238\n#define GL_RG8_SNORM 0x8F95\n#define GL_RGB 0x1907\n#define GL_RGB10 0x8052\n#define GL_RGB10_A2 0x8059\n#define GL_RGB12 0x8053\n#define GL_RGB16 0x8054\n#define GL_RGB16F 0x881B\n#define GL_RGB16I 0x8D89\n#define GL_RGB16UI 0x8D77\n#define GL_RGB16_SNORM 0x8F9A\n#define GL_RGB32F 0x8815\n#define GL_RGB32I 0x8D83\n#define GL_RGB32UI 0x8D71\n#define GL_RGB4 0x804F\n#define GL_RGB5 0x8050\n#define GL_RGB5_A1 0x8057\n#define GL_RGB8 0x8051\n#define GL_RGB8I 0x8D8F\n#define GL_RGB8UI 0x8D7D\n#define GL_RGB8_SNORM 0x8F96\n#define GL_RGB9_E5 0x8C3D\n#define GL_RGBA 0x1908\n#define GL_RGBA12 0x805A\n#define GL_RGBA16 0x805B\n#define GL_RGBA16F 0x881A\n#define GL_RGBA16I 0x8D88\n#define GL_RGBA16UI 0x8D76\n#define GL_RGBA16_SNORM 0x8F9B\n#define GL_RGBA2 0x8055\n#define GL_RGBA32F 0x8814\n#define GL_RGBA32I 0x8D82\n#define GL_RGBA32UI 0x8D70\n#define GL_RGBA4 0x8056\n#define GL_RGBA8 0x8058\n#define GL_RGBA8I 0x8D8E\n#define GL_RGBA8UI 0x8D7C\n#define GL_RGBA8_SNORM 0x8F97\n#define GL_RGBA_INTEGER 0x8D99\n#define GL_RGBA_MODE 0x0C31\n#define GL_RGB_INTEGER 0x8D98\n#define GL_RGB_SCALE 0x8573\n#define GL_RG_INTEGER 0x8228\n#define GL_RIGHT 0x0407\n#define GL_S 0x2000\n#define GL_SAMPLER 0x82E6\n#define GL_SAMPLER_1D 0x8B5D\n#define GL_SAMPLER_1D_ARRAY 0x8DC0\n#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3\n#define GL_SAMPLER_1D_SHADOW 0x8B61\n#define GL_SAMPLER_2D 0x8B5E\n#define GL_SAMPLER_2D_ARRAY 0x8DC1\n#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4\n#define GL_SAMPLER_2D_RECT 0x8B63\n#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64\n#define GL_SAMPLER_2D_SHADOW 0x8B62\n#define GL_SAMPLER_3D 0x8B5F\n#define GL_SAMPLER_BUFFER 0x8DC2\n#define GL_SAMPLER_CUBE 0x8B60\n#define GL_SAMPLER_CUBE_SHADOW 0x8DC5\n#define GL_SAMPLES 0x80A9\n#define GL_SAMPLES_ARB 0x80A9\n#define GL_SAMPLES_PASSED 0x8914\n#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E\n#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E\n#define GL_SAMPLE_ALPHA_TO_ONE 0x809F\n#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F\n#define GL_SAMPLE_BUFFERS 0x80A8\n#define GL_SAMPLE_BUFFERS_ARB 0x80A8\n#define GL_SAMPLE_COVERAGE 0x80A0\n#define GL_SAMPLE_COVERAGE_ARB 0x80A0\n#define GL_SAMPLE_COVERAGE_INVERT 0x80AB\n#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB\n#define GL_SAMPLE_COVERAGE_VALUE 0x80AA\n#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA\n#define GL_SCISSOR_BIT 0x00080000\n#define GL_SCISSOR_BOX 0x0C10\n#define GL_SCISSOR_TEST 0x0C11\n#define GL_SECONDARY_COLOR_ARRAY 0x845E\n#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C\n#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D\n#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A\n#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C\n#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B\n#define GL_SELECT 0x1C02\n#define GL_SELECTION_BUFFER_POINTER 0x0DF3\n#define GL_SELECTION_BUFFER_SIZE 0x0DF4\n#define GL_SEPARATE_ATTRIBS 0x8C8D\n#define GL_SEPARATE_SPECULAR_COLOR 0x81FA\n#define GL_SET 0x150F\n#define GL_SHADER 0x82E1\n#define GL_SHADER_SOURCE_LENGTH 0x8B88\n#define GL_SHADER_TYPE 0x8B4F\n#define GL_SHADE_MODEL 0x0B54\n#define GL_SHADING_LANGUAGE_VERSION 0x8B8C\n#define GL_SHININESS 0x1601\n#define GL_SHORT 0x1402\n#define GL_SIGNED_NORMALIZED 0x8F9C\n#define GL_SINGLE_COLOR 0x81F9\n#define GL_SLUMINANCE 0x8C46\n#define GL_SLUMINANCE8 0x8C47\n#define GL_SLUMINANCE8_ALPHA8 0x8C45\n#define GL_SLUMINANCE_ALPHA 0x8C44\n#define GL_SMOOTH 0x1D01\n#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23\n#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22\n#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13\n#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12\n#define GL_SOURCE0_ALPHA 0x8588\n#define GL_SOURCE0_RGB 0x8580\n#define GL_SOURCE1_ALPHA 0x8589\n#define GL_SOURCE1_RGB 0x8581\n#define GL_SOURCE2_ALPHA 0x858A\n#define GL_SOURCE2_RGB 0x8582\n#define GL_SPECULAR 0x1202\n#define GL_SPHERE_MAP 0x2402\n#define GL_SPOT_CUTOFF 0x1206\n#define GL_SPOT_DIRECTION 0x1204\n#define GL_SPOT_EXPONENT 0x1205\n#define GL_SRC0_ALPHA 0x8588\n#define GL_SRC0_RGB 0x8580\n#define GL_SRC1_ALPHA 0x8589\n#define GL_SRC1_RGB 0x8581\n#define GL_SRC2_ALPHA 0x858A\n#define GL_SRC2_RGB 0x8582\n#define GL_SRC_ALPHA 0x0302\n#define GL_SRC_ALPHA_SATURATE 0x0308\n#define GL_SRC_COLOR 0x0300\n#define GL_SRGB 0x8C40\n#define GL_SRGB8 0x8C41\n#define GL_SRGB8_ALPHA8 0x8C43\n#define GL_SRGB_ALPHA 0x8C42\n#define GL_STACK_OVERFLOW 0x0503\n#define GL_STACK_UNDERFLOW 0x0504\n#define GL_STATIC_COPY 0x88E6\n#define GL_STATIC_DRAW 0x88E4\n#define GL_STATIC_READ 0x88E5\n#define GL_STENCIL 0x1802\n#define GL_STENCIL_ATTACHMENT 0x8D20\n#define GL_STENCIL_BACK_FAIL 0x8801\n#define GL_STENCIL_BACK_FUNC 0x8800\n#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802\n#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803\n#define GL_STENCIL_BACK_REF 0x8CA3\n#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4\n#define GL_STENCIL_BACK_WRITEMASK 0x8CA5\n#define GL_STENCIL_BITS 0x0D57\n#define GL_STENCIL_BUFFER_BIT 0x00000400\n#define GL_STENCIL_CLEAR_VALUE 0x0B91\n#define GL_STENCIL_FAIL 0x0B94\n#define GL_STENCIL_FUNC 0x0B92\n#define GL_STENCIL_INDEX 0x1901\n#define GL_STENCIL_INDEX1 0x8D46\n#define GL_STENCIL_INDEX16 0x8D49\n#define GL_STENCIL_INDEX4 0x8D47\n#define GL_STENCIL_INDEX8 0x8D48\n#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95\n#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96\n#define GL_STENCIL_REF 0x0B97\n#define GL_STENCIL_TEST 0x0B90\n#define GL_STENCIL_VALUE_MASK 0x0B93\n#define GL_STENCIL_WRITEMASK 0x0B98\n#define GL_STEREO 0x0C33\n#define GL_STREAM_COPY 0x88E2\n#define GL_STREAM_DRAW 0x88E0\n#define GL_STREAM_READ 0x88E1\n#define GL_SUBPIXEL_BITS 0x0D50\n#define GL_SUBTRACT 0x84E7\n#define GL_T 0x2001\n#define GL_T2F_C3F_V3F 0x2A2A\n#define GL_T2F_C4F_N3F_V3F 0x2A2C\n#define GL_T2F_C4UB_V3F 0x2A29\n#define GL_T2F_N3F_V3F 0x2A2B\n#define GL_T2F_V3F 0x2A27\n#define GL_T4F_C4F_N3F_V4F 0x2A2D\n#define GL_T4F_V4F 0x2A28\n#define GL_TEXTURE 0x1702\n#define GL_TEXTURE0 0x84C0\n#define GL_TEXTURE1 0x84C1\n#define GL_TEXTURE10 0x84CA\n#define GL_TEXTURE11 0x84CB\n#define GL_TEXTURE12 0x84CC\n#define GL_TEXTURE13 0x84CD\n#define GL_TEXTURE14 0x84CE\n#define GL_TEXTURE15 0x84CF\n#define GL_TEXTURE16 0x84D0\n#define GL_TEXTURE17 0x84D1\n#define GL_TEXTURE18 0x84D2\n#define GL_TEXTURE19 0x84D3\n#define GL_TEXTURE2 0x84C2\n#define GL_TEXTURE20 0x84D4\n#define GL_TEXTURE21 0x84D5\n#define GL_TEXTURE22 0x84D6\n#define GL_TEXTURE23 0x84D7\n#define GL_TEXTURE24 0x84D8\n#define GL_TEXTURE25 0x84D9\n#define GL_TEXTURE26 0x84DA\n#define GL_TEXTURE27 0x84DB\n#define GL_TEXTURE28 0x84DC\n#define GL_TEXTURE29 0x84DD\n#define GL_TEXTURE3 0x84C3\n#define GL_TEXTURE30 0x84DE\n#define GL_TEXTURE31 0x84DF\n#define GL_TEXTURE4 0x84C4\n#define GL_TEXTURE5 0x84C5\n#define GL_TEXTURE6 0x84C6\n#define GL_TEXTURE7 0x84C7\n#define GL_TEXTURE8 0x84C8\n#define GL_TEXTURE9 0x84C9\n#define GL_TEXTURE_1D 0x0DE0\n#define GL_TEXTURE_1D_ARRAY 0x8C18\n#define GL_TEXTURE_2D 0x0DE1\n#define GL_TEXTURE_2D_ARRAY 0x8C1A\n#define GL_TEXTURE_3D 0x806F\n#define GL_TEXTURE_ALPHA_SIZE 0x805F\n#define GL_TEXTURE_ALPHA_TYPE 0x8C13\n#define GL_TEXTURE_BASE_LEVEL 0x813C\n#define GL_TEXTURE_BINDING_1D 0x8068\n#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C\n#define GL_TEXTURE_BINDING_2D 0x8069\n#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D\n#define GL_TEXTURE_BINDING_3D 0x806A\n#define GL_TEXTURE_BINDING_BUFFER 0x8C2C\n#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514\n#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6\n#define GL_TEXTURE_BIT 0x00040000\n#define GL_TEXTURE_BLUE_SIZE 0x805E\n#define GL_TEXTURE_BLUE_TYPE 0x8C12\n#define GL_TEXTURE_BORDER 0x1005\n#define GL_TEXTURE_BORDER_COLOR 0x1004\n#define GL_TEXTURE_BUFFER 0x8C2A\n#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D\n#define GL_TEXTURE_COMPARE_FUNC 0x884D\n#define GL_TEXTURE_COMPARE_MODE 0x884C\n#define GL_TEXTURE_COMPONENTS 0x1003\n#define GL_TEXTURE_COMPRESSED 0x86A1\n#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0\n#define GL_TEXTURE_COMPRESSION_HINT 0x84EF\n#define GL_TEXTURE_COORD_ARRAY 0x8078\n#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A\n#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092\n#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088\n#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A\n#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089\n#define GL_TEXTURE_CUBE_MAP 0x8513\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519\n#define GL_TEXTURE_DEPTH 0x8071\n#define GL_TEXTURE_DEPTH_SIZE 0x884A\n#define GL_TEXTURE_DEPTH_TYPE 0x8C16\n#define GL_TEXTURE_ENV 0x2300\n#define GL_TEXTURE_ENV_COLOR 0x2201\n#define GL_TEXTURE_ENV_MODE 0x2200\n#define GL_TEXTURE_FILTER_CONTROL 0x8500\n#define GL_TEXTURE_GEN_MODE 0x2500\n#define GL_TEXTURE_GEN_Q 0x0C63\n#define GL_TEXTURE_GEN_R 0x0C62\n#define GL_TEXTURE_GEN_S 0x0C60\n#define GL_TEXTURE_GEN_T 0x0C61\n#define GL_TEXTURE_GREEN_SIZE 0x805D\n#define GL_TEXTURE_GREEN_TYPE 0x8C11\n#define GL_TEXTURE_HEIGHT 0x1001\n#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F\n#define GL_TEXTURE_INTENSITY_SIZE 0x8061\n#define GL_TEXTURE_INTENSITY_TYPE 0x8C15\n#define GL_TEXTURE_INTERNAL_FORMAT 0x1003\n#define GL_TEXTURE_LOD_BIAS 0x8501\n#define GL_TEXTURE_LUMINANCE_SIZE 0x8060\n#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14\n#define GL_TEXTURE_MAG_FILTER 0x2800\n#define GL_TEXTURE_MATRIX 0x0BA8\n#define GL_TEXTURE_MAX_LEVEL 0x813D\n#define GL_TEXTURE_MAX_LOD 0x813B\n#define GL_TEXTURE_MIN_FILTER 0x2801\n#define GL_TEXTURE_MIN_LOD 0x813A\n#define GL_TEXTURE_PRIORITY 0x8066\n#define GL_TEXTURE_RECTANGLE 0x84F5\n#define GL_TEXTURE_RED_SIZE 0x805C\n#define GL_TEXTURE_RED_TYPE 0x8C10\n#define GL_TEXTURE_RESIDENT 0x8067\n#define GL_TEXTURE_SHARED_SIZE 0x8C3F\n#define GL_TEXTURE_STACK_DEPTH 0x0BA5\n#define GL_TEXTURE_STENCIL_SIZE 0x88F1\n#define GL_TEXTURE_WIDTH 0x1000\n#define GL_TEXTURE_WRAP_R 0x8072\n#define GL_TEXTURE_WRAP_S 0x2802\n#define GL_TEXTURE_WRAP_T 0x2803\n#define GL_TRANSFORM_BIT 0x00001000\n#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E\n#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F\n#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F\n#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85\n#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84\n#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88\n#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83\n#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76\n#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6\n#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3\n#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4\n#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5\n#define GL_TRIANGLES 0x0004\n#define GL_TRIANGLE_FAN 0x0006\n#define GL_TRIANGLE_STRIP 0x0005\n#define GL_TRUE 1\n#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C\n#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42\n#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43\n#define GL_UNIFORM_BLOCK_BINDING 0x8A3F\n#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40\n#define GL_UNIFORM_BLOCK_INDEX 0x8A3A\n#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44\n#define GL_UNIFORM_BUFFER 0x8A11\n#define GL_UNIFORM_BUFFER_BINDING 0x8A28\n#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34\n#define GL_UNIFORM_BUFFER_SIZE 0x8A2A\n#define GL_UNIFORM_BUFFER_START 0x8A29\n#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E\n#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D\n#define GL_UNIFORM_NAME_LENGTH 0x8A39\n#define GL_UNIFORM_OFFSET 0x8A3B\n#define GL_UNIFORM_SIZE 0x8A38\n#define GL_UNIFORM_TYPE 0x8A37\n#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255\n#define GL_UNPACK_ALIGNMENT 0x0CF5\n#define GL_UNPACK_IMAGE_HEIGHT 0x806E\n#define GL_UNPACK_LSB_FIRST 0x0CF1\n#define GL_UNPACK_ROW_LENGTH 0x0CF2\n#define GL_UNPACK_SKIP_IMAGES 0x806D\n#define GL_UNPACK_SKIP_PIXELS 0x0CF4\n#define GL_UNPACK_SKIP_ROWS 0x0CF3\n#define GL_UNPACK_SWAP_BYTES 0x0CF0\n#define GL_UNSIGNED_BYTE 0x1401\n#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362\n#define GL_UNSIGNED_BYTE_3_3_2 0x8032\n#define GL_UNSIGNED_INT 0x1405\n#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B\n#define GL_UNSIGNED_INT_10_10_10_2 0x8036\n#define GL_UNSIGNED_INT_24_8 0x84FA\n#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368\n#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E\n#define GL_UNSIGNED_INT_8_8_8_8 0x8035\n#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367\n#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1\n#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6\n#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2\n#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7\n#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5\n#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3\n#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8\n#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4\n#define GL_UNSIGNED_INT_VEC2 0x8DC6\n#define GL_UNSIGNED_INT_VEC3 0x8DC7\n#define GL_UNSIGNED_INT_VEC4 0x8DC8\n#define GL_UNSIGNED_NORMALIZED 0x8C17\n#define GL_UNSIGNED_SHORT 0x1403\n#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366\n#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033\n#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365\n#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034\n#define GL_UNSIGNED_SHORT_5_6_5 0x8363\n#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364\n#define GL_UPPER_LEFT 0x8CA2\n#define GL_V2F 0x2A20\n#define GL_V3F 0x2A21\n#define GL_VALIDATE_STATUS 0x8B83\n#define GL_VENDOR 0x1F00\n#define GL_VERSION 0x1F02\n#define GL_VERTEX_ARRAY 0x8074\n#define GL_VERTEX_ARRAY_BINDING 0x85B5\n#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896\n#define GL_VERTEX_ARRAY_POINTER 0x808E\n#define GL_VERTEX_ARRAY_SIZE 0x807A\n#define GL_VERTEX_ARRAY_STRIDE 0x807C\n#define GL_VERTEX_ARRAY_TYPE 0x807B\n#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F\n#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE\n#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622\n#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD\n#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A\n#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645\n#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623\n#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624\n#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625\n#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642\n#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643\n#define GL_VERTEX_SHADER 0x8B31\n#define GL_VIEWPORT 0x0BA2\n#define GL_VIEWPORT_BIT 0x00000800\n#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E\n#define GL_WRITE_ONLY 0x88B9\n#define GL_XOR 0x1506\n#define GL_ZERO 0\n#define GL_ZOOM_X 0x0D16\n#define GL_ZOOM_Y 0x0D17\n#ifndef __khrplatform_h_\n#define __khrplatform_h_\n/*\n** Copyright (c) 2008-2018 The Khronos Group Inc.\n**\n** Permission is hereby granted, free of charge, to any person obtaining a\n** copy of this software and/or associated documentation files (the\n** \"Materials\"), to deal in the Materials without restriction, including\n** without limitation the rights to use, copy, modify, merge, publish,\n** distribute, sublicense, and/or sell copies of the Materials, and to\n** permit persons to whom the Materials are furnished to do so, subject to\n** the following conditions:\n**\n** The above copyright notice and this permission notice shall be included\n** in all copies or substantial portions of the Materials.\n**\n** THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.\n*/\n/* Khronos platform-specific types and definitions.\n *\n * The master copy of khrplatform.h is maintained in the Khronos EGL\n * Registry repository at https://github.com/KhronosGroup/EGL-Registry\n * The last semantic modification to khrplatform.h was at commit ID:\n *      67a3e0864c2d75ea5287b9f3d2eb74a745936692\n *\n * Adopters may modify this file to suit their platform. Adopters are\n * encouraged to submit platform specific modifications to the Khronos\n * group so that they can be included in future versions of this file.\n * Please submit changes by filing pull requests or issues on\n * the EGL Registry repository linked above.\n *\n *\n * See the Implementer's Guidelines for information about where this file\n * should be located on your system and for more details of its use:\n *    http://www.khronos.org/registry/implementers_guide.pdf\n *\n * This file should be included as\n *        #include <KHR/khrplatform.h>\n * by Khronos client API header files that use its types and defines.\n *\n * The types in khrplatform.h should only be used to define API-specific types.\n *\n * Types defined in khrplatform.h:\n *    khronos_int8_t              signed   8  bit\n *    khronos_uint8_t             unsigned 8  bit\n *    khronos_int16_t             signed   16 bit\n *    khronos_uint16_t            unsigned 16 bit\n *    khronos_int32_t             signed   32 bit\n *    khronos_uint32_t            unsigned 32 bit\n *    khronos_int64_t             signed   64 bit\n *    khronos_uint64_t            unsigned 64 bit\n *    khronos_intptr_t            signed   same number of bits as a pointer\n *    khronos_uintptr_t           unsigned same number of bits as a pointer\n *    khronos_ssize_t             signed   size\n *    khronos_usize_t             unsigned size\n *    khronos_float_t             signed   32 bit floating point\n *    khronos_time_ns_t           unsigned 64 bit time in nanoseconds\n *    khronos_utime_nanoseconds_t unsigned time interval or absolute time in\n *                                         nanoseconds\n *    khronos_stime_nanoseconds_t signed time interval in nanoseconds\n *    khronos_boolean_enum_t      enumerated boolean type. This should\n *      only be used as a base type when a client API's boolean type is\n *      an enum. Client APIs which use an integer or other type for\n *      booleans cannot use this as the base type for their boolean.\n *\n * Tokens defined in khrplatform.h:\n *\n *    KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.\n *\n *    KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.\n *    KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.\n *\n * Calling convention macros defined in this file:\n *    KHRONOS_APICALL\n *    KHRONOS_GLAD_API_PTR\n *    KHRONOS_APIATTRIBUTES\n *\n * These may be used in function prototypes as:\n *\n *      KHRONOS_APICALL void KHRONOS_GLAD_API_PTR funcname(\n *                                  int arg1,\n *                                  int arg2) KHRONOS_APIATTRIBUTES;\n */\n#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)\n#   define KHRONOS_STATIC 1\n#endif\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APICALL\n *-------------------------------------------------------------------------\n * This precedes the return type of the function in the function prototype.\n */\n#if defined(KHRONOS_STATIC)\n    /* If the preprocessor constant KHRONOS_STATIC is defined, make the\n     * header compatible with static linking. */\n#   define KHRONOS_APICALL\n#elif defined(_WIN32)\n#   define KHRONOS_APICALL __declspec(dllimport)\n#elif defined (__SYMBIAN32__)\n#   define KHRONOS_APICALL IMPORT_C\n#elif defined(__ANDROID__)\n#   define KHRONOS_APICALL __attribute__((visibility(\"default\")))\n#else\n#   define KHRONOS_APICALL\n#endif\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_GLAD_API_PTR\n *-------------------------------------------------------------------------\n * This follows the return type of the function  and precedes the function\n * name in the function prototype.\n */\n#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)\n    /* Win32 but not WinCE */\n#   define KHRONOS_GLAD_API_PTR __stdcall\n#else\n#   define KHRONOS_GLAD_API_PTR\n#endif\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APIATTRIBUTES\n *-------------------------------------------------------------------------\n * This follows the closing parenthesis of the function prototype arguments.\n */\n#if defined (__ARMCC_2__)\n#define KHRONOS_APIATTRIBUTES __softfp\n#else\n#define KHRONOS_APIATTRIBUTES\n#endif\n/*-------------------------------------------------------------------------\n * basic type definitions\n *-----------------------------------------------------------------------*/\n#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)\n/*\n * Using <stdint.h>\n */\n#include <stdint.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n/*\n * To support platform where unsigned long cannot be used interchangeably with\n * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.\n * Ideally, we could just use (u)intptr_t everywhere, but this could result in\n * ABI breakage if khronos_uintptr_t is changed from unsigned long to\n * unsigned long long or similar (this results in different C++ name mangling).\n * To avoid changes for existing platforms, we restrict usage of intptr_t to\n * platforms where the size of a pointer is larger than the size of long.\n */\n#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)\n#if __SIZEOF_POINTER__ > __SIZEOF_LONG__\n#define KHRONOS_USE_INTPTR_T\n#endif\n#endif\n#elif defined(__VMS ) || defined(__sgi)\n/*\n * Using <inttypes.h>\n */\n#include <inttypes.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)\n/*\n * Win32\n */\ntypedef __int32                 khronos_int32_t;\ntypedef unsigned __int32        khronos_uint32_t;\ntypedef __int64                 khronos_int64_t;\ntypedef unsigned __int64        khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n#elif defined(__sun__) || defined(__digital__)\n/*\n * Sun or Digital\n */\ntypedef int                     khronos_int32_t;\ntypedef unsigned int            khronos_uint32_t;\n#if defined(__arch64__) || defined(_LP64)\ntypedef long int                khronos_int64_t;\ntypedef unsigned long int       khronos_uint64_t;\n#else\ntypedef long long int           khronos_int64_t;\ntypedef unsigned long long int  khronos_uint64_t;\n#endif /* __arch64__ */\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n#elif 0\n/*\n * Hypothetical platform with no float or int64 support\n */\ntypedef int                     khronos_int32_t;\ntypedef unsigned int            khronos_uint32_t;\n#define KHRONOS_SUPPORT_INT64   0\n#define KHRONOS_SUPPORT_FLOAT   0\n#else\n/*\n * Generic fallback\n */\n#include <stdint.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n#endif\n/*\n * Types that are (so far) the same on all platforms\n */\ntypedef signed   char          khronos_int8_t;\ntypedef unsigned char          khronos_uint8_t;\ntypedef signed   short int     khronos_int16_t;\ntypedef unsigned short int     khronos_uint16_t;\n/*\n * Types that differ between LLP64 and LP64 architectures - in LLP64,\n * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears\n * to be the only LLP64 architecture in current use.\n */\n#ifdef KHRONOS_USE_INTPTR_T\ntypedef intptr_t               khronos_intptr_t;\ntypedef uintptr_t              khronos_uintptr_t;\n#elif defined(_WIN64)\ntypedef signed   long long int khronos_intptr_t;\ntypedef unsigned long long int khronos_uintptr_t;\n#else\ntypedef signed   long  int     khronos_intptr_t;\ntypedef unsigned long  int     khronos_uintptr_t;\n#endif\n#if defined(_WIN64)\ntypedef signed   long long int khronos_ssize_t;\ntypedef unsigned long long int khronos_usize_t;\n#else\ntypedef signed   long  int     khronos_ssize_t;\ntypedef unsigned long  int     khronos_usize_t;\n#endif\n#if KHRONOS_SUPPORT_FLOAT\n/*\n * Float type\n */\ntypedef          float         khronos_float_t;\n#endif\n#if KHRONOS_SUPPORT_INT64\n/* Time types\n *\n * These types can be used to represent a time interval in nanoseconds or\n * an absolute Unadjusted System Time.  Unadjusted System Time is the number\n * of nanoseconds since some arbitrary system event (e.g. since the last\n * time the system booted).  The Unadjusted System Time is an unsigned\n * 64 bit value that wraps back to 0 every 584 years.  Time intervals\n * may be either signed or unsigned.\n */\ntypedef khronos_uint64_t       khronos_utime_nanoseconds_t;\ntypedef khronos_int64_t        khronos_stime_nanoseconds_t;\n#endif\n/*\n * Dummy value used to pad enum types to 32 bits.\n */\n#ifndef KHRONOS_MAX_ENUM\n#define KHRONOS_MAX_ENUM 0x7FFFFFFF\n#endif\n/*\n * Enumerated boolean type\n *\n * Values other than zero should be considered to be true.  Therefore\n * comparisons should not be made against KHRONOS_TRUE.\n */\ntypedef enum {\n    KHRONOS_FALSE = 0,\n    KHRONOS_TRUE  = 1,\n    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM\n} khronos_boolean_enum_t;\n#endif /* __khrplatform_h_ */\ntypedef unsigned int GLenum;\ntypedef unsigned char GLboolean;\ntypedef unsigned int GLbitfield;\ntypedef void GLvoid;\ntypedef khronos_int8_t GLbyte;\ntypedef khronos_uint8_t GLubyte;\ntypedef khronos_int16_t GLshort;\ntypedef khronos_uint16_t GLushort;\ntypedef int GLint;\ntypedef unsigned int GLuint;\ntypedef khronos_int32_t GLclampx;\ntypedef int GLsizei;\ntypedef khronos_float_t GLfloat;\ntypedef khronos_float_t GLclampf;\ntypedef double GLdouble;\ntypedef double GLclampd;\ntypedef void *GLeglClientBufferEXT;\ntypedef void *GLeglImageOES;\ntypedef char GLchar;\ntypedef char GLcharARB;\n#ifdef __APPLE__\ntypedef void *GLhandleARB;\n#else\ntypedef unsigned int GLhandleARB;\n#endif\ntypedef khronos_uint16_t GLhalf;\ntypedef khronos_uint16_t GLhalfARB;\ntypedef khronos_int32_t GLfixed;\n#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060)\ntypedef khronos_intptr_t GLintptr;\n#else\ntypedef khronos_intptr_t GLintptr;\n#endif\n#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060)\ntypedef khronos_intptr_t GLintptrARB;\n#else\ntypedef khronos_intptr_t GLintptrARB;\n#endif\n#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060)\ntypedef khronos_ssize_t GLsizeiptr;\n#else\ntypedef khronos_ssize_t GLsizeiptr;\n#endif\n#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060)\ntypedef khronos_ssize_t GLsizeiptrARB;\n#else\ntypedef khronos_ssize_t GLsizeiptrARB;\n#endif\ntypedef khronos_int64_t GLint64;\ntypedef khronos_int64_t GLint64EXT;\ntypedef khronos_uint64_t GLuint64;\ntypedef khronos_uint64_t GLuint64EXT;\ntypedef struct __GLsync *GLsync;\nstruct _cl_context;\nstruct _cl_event;\ntypedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);\ntypedef unsigned short GLhalfNV;\ntypedef GLintptr GLvdpauSurfaceNV;\ntypedef void (GLAD_API_PTR *GLVULKANPROCNV)(void);\n#define GL_VERSION_1_0 1\nGLAD_API_CALL int GLAD_GL_VERSION_1_0;\n#define GL_VERSION_1_1 1\nGLAD_API_CALL int GLAD_GL_VERSION_1_1;\n#define GL_VERSION_1_2 1\nGLAD_API_CALL int GLAD_GL_VERSION_1_2;\n#define GL_VERSION_1_3 1\nGLAD_API_CALL int GLAD_GL_VERSION_1_3;\n#define GL_VERSION_1_4 1\nGLAD_API_CALL int GLAD_GL_VERSION_1_4;\n#define GL_VERSION_1_5 1\nGLAD_API_CALL int GLAD_GL_VERSION_1_5;\n#define GL_VERSION_2_0 1\nGLAD_API_CALL int GLAD_GL_VERSION_2_0;\n#define GL_VERSION_2_1 1\nGLAD_API_CALL int GLAD_GL_VERSION_2_1;\n#define GL_VERSION_3_0 1\nGLAD_API_CALL int GLAD_GL_VERSION_3_0;\n#define GL_VERSION_3_1 1\nGLAD_API_CALL int GLAD_GL_VERSION_3_1;\n#define GL_ARB_copy_image 1\nGLAD_API_CALL int GLAD_GL_ARB_copy_image;\n#define GL_ARB_framebuffer_sRGB 1\nGLAD_API_CALL int GLAD_GL_ARB_framebuffer_sRGB;\n#define GL_ARB_instanced_arrays 1\nGLAD_API_CALL int GLAD_GL_ARB_instanced_arrays;\n#define GL_ARB_multisample 1\nGLAD_API_CALL int GLAD_GL_ARB_multisample;\n#define GL_ARB_robustness 1\nGLAD_API_CALL int GLAD_GL_ARB_robustness;\n#define GL_ARB_texture_storage 1\nGLAD_API_CALL int GLAD_GL_ARB_texture_storage;\n#define GL_EXT_framebuffer_sRGB 1\nGLAD_API_CALL int GLAD_GL_EXT_framebuffer_sRGB;\n#define GL_KHR_debug 1\nGLAD_API_CALL int GLAD_GL_KHR_debug;\ntypedef void (GLAD_API_PTR *PFNGLACCUMPROC)(GLenum op, GLfloat value);\ntypedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture);\ntypedef void (GLAD_API_PTR *PFNGLALPHAFUNCPROC)(GLenum func, GLfloat ref);\ntypedef GLboolean (GLAD_API_PTR *PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint * textures, GLboolean * residences);\ntypedef void (GLAD_API_PTR *PFNGLARRAYELEMENTPROC)(GLint i);\ntypedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader);\ntypedef void (GLAD_API_PTR *PFNGLBEGINPROC)(GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLBEGINQUERYPROC)(GLenum target, GLuint id);\ntypedef void (GLAD_API_PTR *PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode);\ntypedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name);\ntypedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);\ntypedef void (GLAD_API_PTR *PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer);\ntypedef void (GLAD_API_PTR *PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);\ntypedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar * name);\ntypedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);\ntypedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer);\ntypedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture);\ntypedef void (GLAD_API_PTR *PFNGLBINDVERTEXARRAYPROC)(GLuint array);\ntypedef void (GLAD_API_PTR *PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte * bitmap);\ntypedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\ntypedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha);\ntypedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor);\ntypedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);\ntypedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);\ntypedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage);\ntypedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data);\ntypedef void (GLAD_API_PTR *PFNGLCALLLISTPROC)(GLuint list);\ntypedef void (GLAD_API_PTR *PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void * lists);\ntypedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target);\ntypedef void (GLAD_API_PTR *PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp);\ntypedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask);\ntypedef void (GLAD_API_PTR *PFNGLCLEARACCUMPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\ntypedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);\ntypedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint * value);\ntypedef void (GLAD_API_PTR *PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint * value);\ntypedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\ntypedef void (GLAD_API_PTR *PFNGLCLEARDEPTHPROC)(GLdouble depth);\ntypedef void (GLAD_API_PTR *PFNGLCLEARINDEXPROC)(GLfloat c);\ntypedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s);\ntypedef void (GLAD_API_PTR *PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture);\ntypedef void (GLAD_API_PTR *PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble * equation);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3BVPROC)(const GLbyte * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3UBVPROC)(const GLubyte * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3UIVPROC)(const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR3USVPROC)(const GLushort * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4BVPROC)(const GLbyte * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4UBVPROC)(const GLubyte * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4UIVPROC)(const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLOR4USVPROC)(const GLushort * v);\ntypedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);\ntypedef void (GLAD_API_PTR *PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a);\ntypedef void (GLAD_API_PTR *PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader);\ntypedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data);\ntypedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data);\ntypedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data);\ntypedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data);\ntypedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data);\ntypedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data);\ntypedef void (GLAD_API_PTR *PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);\ntypedef void (GLAD_API_PTR *PFNGLCOPYIMAGESUBDATAPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);\ntypedef void (GLAD_API_PTR *PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type);\ntypedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);\ntypedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\ntypedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\ntypedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef GLuint (GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void);\ntypedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type);\ntypedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void * userParam);\ntypedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled);\ntypedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf);\ntypedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers);\ntypedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers);\ntypedef void (GLAD_API_PTR *PFNGLDELETELISTSPROC)(GLuint list, GLsizei range);\ntypedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program);\ntypedef void (GLAD_API_PTR *PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint * ids);\ntypedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers);\ntypedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader);\ntypedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures);\ntypedef void (GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint * arrays);\ntypedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func);\ntypedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag);\ntypedef void (GLAD_API_PTR *PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f);\ntypedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader);\ntypedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap);\ntypedef void (GLAD_API_PTR *PFNGLDISABLECLIENTSTATEPROC)(GLenum array);\ntypedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);\ntypedef void (GLAD_API_PTR *PFNGLDISABLEIPROC)(GLenum target, GLuint index);\ntypedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count);\ntypedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);\ntypedef void (GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf);\ntypedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs);\ntypedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices);\ntypedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount);\ntypedef void (GLAD_API_PTR *PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices);\ntypedef void (GLAD_API_PTR *PFNGLEDGEFLAGPROC)(GLboolean flag);\ntypedef void (GLAD_API_PTR *PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLEDGEFLAGVPROC)(const GLboolean * flag);\ntypedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap);\ntypedef void (GLAD_API_PTR *PFNGLENABLECLIENTSTATEPROC)(GLenum array);\ntypedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);\ntypedef void (GLAD_API_PTR *PFNGLENABLEIPROC)(GLenum target, GLuint index);\ntypedef void (GLAD_API_PTR *PFNGLENDPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLENDCONDITIONALRENDERPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLENDLISTPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLENDQUERYPROC)(GLenum target);\ntypedef void (GLAD_API_PTR *PFNGLENDTRANSFORMFEEDBACKPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLEVALCOORD1DPROC)(GLdouble u);\ntypedef void (GLAD_API_PTR *PFNGLEVALCOORD1DVPROC)(const GLdouble * u);\ntypedef void (GLAD_API_PTR *PFNGLEVALCOORD1FPROC)(GLfloat u);\ntypedef void (GLAD_API_PTR *PFNGLEVALCOORD1FVPROC)(const GLfloat * u);\ntypedef void (GLAD_API_PTR *PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v);\ntypedef void (GLAD_API_PTR *PFNGLEVALCOORD2DVPROC)(const GLdouble * u);\ntypedef void (GLAD_API_PTR *PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v);\ntypedef void (GLAD_API_PTR *PFNGLEVALCOORD2FVPROC)(const GLfloat * u);\ntypedef void (GLAD_API_PTR *PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2);\ntypedef void (GLAD_API_PTR *PFNGLEVALMESH2PROC)(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2);\ntypedef void (GLAD_API_PTR *PFNGLEVALPOINT1PROC)(GLint i);\ntypedef void (GLAD_API_PTR *PFNGLEVALPOINT2PROC)(GLint i, GLint j);\ntypedef void (GLAD_API_PTR *PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat * buffer);\ntypedef void (GLAD_API_PTR *PFNGLFINISHPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLFLUSHPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length);\ntypedef void (GLAD_API_PTR *PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLFOGCOORDDPROC)(GLdouble coord);\ntypedef void (GLAD_API_PTR *PFNGLFOGCOORDDVPROC)(const GLdouble * coord);\ntypedef void (GLAD_API_PTR *PFNGLFOGCOORDFPROC)(GLfloat coord);\ntypedef void (GLAD_API_PTR *PFNGLFOGCOORDFVPROC)(const GLfloat * coord);\ntypedef void (GLAD_API_PTR *PFNGLFOGFPROC)(GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLFOGFVPROC)(GLenum pname, const GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLFOGIPROC)(GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLFOGIVPROC)(GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\ntypedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\ntypedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\ntypedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);\ntypedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);\ntypedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLFRUSTUMPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\ntypedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers);\ntypedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers);\ntypedef GLuint (GLAD_API_PTR *PFNGLGENLISTSPROC)(GLsizei range);\ntypedef void (GLAD_API_PTR *PFNGLGENQUERIESPROC)(GLsizei n, GLuint * ids);\ntypedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers);\ntypedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures);\ntypedef void (GLAD_API_PTR *PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays);\ntypedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target);\ntypedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name);\ntypedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name);\ntypedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformBlockName);\ntypedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformName);\ntypedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint * uniformIndices, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders);\ntypedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name);\ntypedef void (GLAD_API_PTR *PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean * data);\ntypedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data);\ntypedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void ** params);\ntypedef void (GLAD_API_PTR *PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void * data);\ntypedef void (GLAD_API_PTR *PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble * equation);\ntypedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void * img);\ntypedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog);\ntypedef void (GLAD_API_PTR *PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble * data);\ntypedef GLenum (GLAD_API_PTR *PFNGLGETERRORPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data);\ntypedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar * name);\ntypedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params);\ntypedef GLenum (GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSARBPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint * data);\ntypedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data);\ntypedef void (GLAD_API_PTR *PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label);\ntypedef void (GLAD_API_PTR *PFNGLGETOBJECTPTRLABELPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label);\ntypedef void (GLAD_API_PTR *PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat * values);\ntypedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint * values);\ntypedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort * values);\ntypedef void (GLAD_API_PTR *PFNGLGETPOINTERVPROC)(GLenum pname, void ** params);\ntypedef void (GLAD_API_PTR *PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte * mask);\ntypedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog);\ntypedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog);\ntypedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source);\ntypedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params);\ntypedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name);\ntypedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGIPROC)(GLenum name, GLuint index);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name);\ntypedef GLuint (GLAD_API_PTR *PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar * uniformBlockName);\ntypedef void (GLAD_API_PTR *PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const* uniformNames, GLuint * uniformIndices);\ntypedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name);\ntypedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer);\ntypedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble * params);\ntypedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint lod, GLsizei bufSize, void * img);\ntypedef void (GLAD_API_PTR *PFNGLGETNTEXIMAGEARBPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * img);\ntypedef void (GLAD_API_PTR *PFNGLGETNUNIFORMDVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble * params);\ntypedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params);\ntypedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLINDEXMASKPROC)(GLuint mask);\ntypedef void (GLAD_API_PTR *PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLINDEXDPROC)(GLdouble c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXDVPROC)(const GLdouble * c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXFPROC)(GLfloat c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXFVPROC)(const GLfloat * c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXIPROC)(GLint c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXIVPROC)(const GLint * c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXSPROC)(GLshort c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXSVPROC)(const GLshort * c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXUBPROC)(GLubyte c);\ntypedef void (GLAD_API_PTR *PFNGLINDEXUBVPROC)(const GLubyte * c);\ntypedef void (GLAD_API_PTR *PFNGLINITNAMESPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void * pointer);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIPROC)(GLenum target, GLuint index);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISLISTPROC)(GLuint list);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISQUERYPROC)(GLuint id);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture);\ntypedef GLboolean (GLAD_API_PTR *PFNGLISVERTEXARRAYPROC)(GLuint array);\ntypedef void (GLAD_API_PTR *PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern);\ntypedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width);\ntypedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program);\ntypedef void (GLAD_API_PTR *PFNGLLISTBASEPROC)(GLuint base);\ntypedef void (GLAD_API_PTR *PFNGLLOADIDENTITYPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLLOADMATRIXDPROC)(const GLdouble * m);\ntypedef void (GLAD_API_PTR *PFNGLLOADMATRIXFPROC)(const GLfloat * m);\ntypedef void (GLAD_API_PTR *PFNGLLOADNAMEPROC)(GLuint name);\ntypedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble * m);\ntypedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat * m);\ntypedef void (GLAD_API_PTR *PFNGLLOGICOPPROC)(GLenum opcode);\ntypedef void (GLAD_API_PTR *PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points);\ntypedef void (GLAD_API_PTR *PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points);\ntypedef void (GLAD_API_PTR *PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points);\ntypedef void (GLAD_API_PTR *PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points);\ntypedef void * (GLAD_API_PTR *PFNGLMAPBUFFERPROC)(GLenum target, GLenum access);\ntypedef void * (GLAD_API_PTR *PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);\ntypedef void (GLAD_API_PTR *PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2);\ntypedef void (GLAD_API_PTR *PFNGLMAPGRID1FPROC)(GLint un, GLfloat u1, GLfloat u2);\ntypedef void (GLAD_API_PTR *PFNGLMAPGRID2DPROC)(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2);\ntypedef void (GLAD_API_PTR *PFNGLMAPGRID2FPROC)(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2);\ntypedef void (GLAD_API_PTR *PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLMATRIXMODEPROC)(GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLMULTMATRIXDPROC)(const GLdouble * m);\ntypedef void (GLAD_API_PTR *PFNGLMULTMATRIXFPROC)(const GLfloat * m);\ntypedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble * m);\ntypedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat * m);\ntypedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei drawcount);\ntypedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q);\ntypedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLNEWLISTPROC)(GLuint list, GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3BVPROC)(const GLbyte * v);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz);\ntypedef void (GLAD_API_PTR *PFNGLNORMAL3SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label);\ntypedef void (GLAD_API_PTR *PFNGLOBJECTPTRLABELPROC)(const void * ptr, GLsizei length, const GLchar * label);\ntypedef void (GLAD_API_PTR *PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\ntypedef void (GLAD_API_PTR *PFNGLPASSTHROUGHPROC)(GLfloat token);\ntypedef void (GLAD_API_PTR *PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat * values);\ntypedef void (GLAD_API_PTR *PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint * values);\ntypedef void (GLAD_API_PTR *PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort * values);\ntypedef void (GLAD_API_PTR *PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERFPROC)(GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLPIXELZOOMPROC)(GLfloat xfactor, GLfloat yfactor);\ntypedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLPOINTSIZEPROC)(GLfloat size);\ntypedef void (GLAD_API_PTR *PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units);\ntypedef void (GLAD_API_PTR *PFNGLPOLYGONSTIPPLEPROC)(const GLubyte * mask);\ntypedef void (GLAD_API_PTR *PFNGLPOPATTRIBPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLPOPCLIENTATTRIBPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLPOPDEBUGGROUPPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLPOPMATRIXPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLPOPNAMEPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index);\ntypedef void (GLAD_API_PTR *PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint * textures, const GLfloat * priorities);\ntypedef void (GLAD_API_PTR *PFNGLPUSHATTRIBPROC)(GLbitfield mask);\ntypedef void (GLAD_API_PTR *PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask);\ntypedef void (GLAD_API_PTR *PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message);\ntypedef void (GLAD_API_PTR *PFNGLPUSHMATRIXPROC)(void);\ntypedef void (GLAD_API_PTR *PFNGLPUSHNAMEPROC)(GLuint name);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS2DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS2FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS2IPROC)(GLint x, GLint y);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS2IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS2SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS3DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS3FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS3IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS3SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS4DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS4FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS4IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w);\ntypedef void (GLAD_API_PTR *PFNGLRASTERPOS4SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLREADBUFFERPROC)(GLenum src);\ntypedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLREADNPIXELSARBPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data);\ntypedef void (GLAD_API_PTR *PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2);\ntypedef void (GLAD_API_PTR *PFNGLRECTDVPROC)(const GLdouble * v1, const GLdouble * v2);\ntypedef void (GLAD_API_PTR *PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);\ntypedef void (GLAD_API_PTR *PFNGLRECTFVPROC)(const GLfloat * v1, const GLfloat * v2);\ntypedef void (GLAD_API_PTR *PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2);\ntypedef void (GLAD_API_PTR *PFNGLRECTIVPROC)(const GLint * v1, const GLint * v2);\ntypedef void (GLAD_API_PTR *PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2);\ntypedef void (GLAD_API_PTR *PFNGLRECTSVPROC)(const GLshort * v1, const GLshort * v2);\ntypedef GLint (GLAD_API_PTR *PFNGLRENDERMODEPROC)(GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (GLAD_API_PTR *PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (GLAD_API_PTR *PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);\ntypedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert);\ntypedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEARBPROC)(GLfloat value, GLboolean invert);\ntypedef void (GLAD_API_PTR *PFNGLSCALEDPROC)(GLdouble x, GLdouble y, GLdouble z);\ntypedef void (GLAD_API_PTR *PFNGLSCALEFPROC)(GLfloat x, GLfloat y, GLfloat z);\ntypedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte * v);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte * v);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3USVPROC)(const GLushort * v);\ntypedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint * buffer);\ntypedef void (GLAD_API_PTR *PFNGLSHADEMODELPROC)(GLenum mode);\ntypedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length);\ntypedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask);\ntypedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask);\ntypedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask);\ntypedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask);\ntypedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass);\ntypedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);\ntypedef void (GLAD_API_PTR *PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD1DPROC)(GLdouble s);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD1DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD1FPROC)(GLfloat s);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD1FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD1IPROC)(GLint s);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD1IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD1SPROC)(GLshort s);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD1SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD2DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD2FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD2IPROC)(GLint s, GLint t);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD2IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD2SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD3DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD3FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD3IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD3SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD4DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD4FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD4IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORD4SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param);\ntypedef void (GLAD_API_PTR *PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param);\ntypedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);\ntypedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params);\ntypedef void (GLAD_API_PTR *PFNGLTEXSTORAGE1DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);\ntypedef void (GLAD_API_PTR *PFNGLTEXSTORAGE2DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\ntypedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels);\ntypedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode);\ntypedef void (GLAD_API_PTR *PFNGLTRANSLATEDPROC)(GLdouble x, GLdouble y, GLdouble z);\ntypedef void (GLAD_API_PTR *PFNGLTRANSLATEFPROC)(GLfloat x, GLfloat y, GLfloat z);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value);\ntypedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFERPROC)(GLenum target);\ntypedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program);\ntypedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX2DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX2FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX2IPROC)(GLint x, GLint y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX2IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX2SPROC)(GLshort x, GLshort y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX2SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX3DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX3FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX3IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX3SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX4DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX4FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX4IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEX4SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORARBPROC)(GLuint index, GLuint divisor);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort * v);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer);\ntypedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IPROC)(GLint x, GLint y);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SVPROC)(const GLshort * v);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DVPROC)(const GLdouble * v);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FVPROC)(const GLfloat * v);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IVPROC)(const GLint * v);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z);\ntypedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SVPROC)(const GLshort * v);\nGLAD_API_CALL PFNGLACCUMPROC glad_glAccum;\nGLAD_API_CALL PFNGLACCUMPROC glad_debug_glAccum;\n#define glAccum glad_debug_glAccum\nGLAD_API_CALL PFNGLACTIVETEXTUREPROC glad_glActiveTexture;\nGLAD_API_CALL PFNGLACTIVETEXTUREPROC glad_debug_glActiveTexture;\n#define glActiveTexture glad_debug_glActiveTexture\nGLAD_API_CALL PFNGLALPHAFUNCPROC glad_glAlphaFunc;\nGLAD_API_CALL PFNGLALPHAFUNCPROC glad_debug_glAlphaFunc;\n#define glAlphaFunc glad_debug_glAlphaFunc\nGLAD_API_CALL PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident;\nGLAD_API_CALL PFNGLARETEXTURESRESIDENTPROC glad_debug_glAreTexturesResident;\n#define glAreTexturesResident glad_debug_glAreTexturesResident\nGLAD_API_CALL PFNGLARRAYELEMENTPROC glad_glArrayElement;\nGLAD_API_CALL PFNGLARRAYELEMENTPROC glad_debug_glArrayElement;\n#define glArrayElement glad_debug_glArrayElement\nGLAD_API_CALL PFNGLATTACHSHADERPROC glad_glAttachShader;\nGLAD_API_CALL PFNGLATTACHSHADERPROC glad_debug_glAttachShader;\n#define glAttachShader glad_debug_glAttachShader\nGLAD_API_CALL PFNGLBEGINPROC glad_glBegin;\nGLAD_API_CALL PFNGLBEGINPROC glad_debug_glBegin;\n#define glBegin glad_debug_glBegin\nGLAD_API_CALL PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;\nGLAD_API_CALL PFNGLBEGINCONDITIONALRENDERPROC glad_debug_glBeginConditionalRender;\n#define glBeginConditionalRender glad_debug_glBeginConditionalRender\nGLAD_API_CALL PFNGLBEGINQUERYPROC glad_glBeginQuery;\nGLAD_API_CALL PFNGLBEGINQUERYPROC glad_debug_glBeginQuery;\n#define glBeginQuery glad_debug_glBeginQuery\nGLAD_API_CALL PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;\nGLAD_API_CALL PFNGLBEGINTRANSFORMFEEDBACKPROC glad_debug_glBeginTransformFeedback;\n#define glBeginTransformFeedback glad_debug_glBeginTransformFeedback\nGLAD_API_CALL PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;\nGLAD_API_CALL PFNGLBINDATTRIBLOCATIONPROC glad_debug_glBindAttribLocation;\n#define glBindAttribLocation glad_debug_glBindAttribLocation\nGLAD_API_CALL PFNGLBINDBUFFERPROC glad_glBindBuffer;\nGLAD_API_CALL PFNGLBINDBUFFERPROC glad_debug_glBindBuffer;\n#define glBindBuffer glad_debug_glBindBuffer\nGLAD_API_CALL PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase;\nGLAD_API_CALL PFNGLBINDBUFFERBASEPROC glad_debug_glBindBufferBase;\n#define glBindBufferBase glad_debug_glBindBufferBase\nGLAD_API_CALL PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange;\nGLAD_API_CALL PFNGLBINDBUFFERRANGEPROC glad_debug_glBindBufferRange;\n#define glBindBufferRange glad_debug_glBindBufferRange\nGLAD_API_CALL PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation;\nGLAD_API_CALL PFNGLBINDFRAGDATALOCATIONPROC glad_debug_glBindFragDataLocation;\n#define glBindFragDataLocation glad_debug_glBindFragDataLocation\nGLAD_API_CALL PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;\nGLAD_API_CALL PFNGLBINDFRAMEBUFFERPROC glad_debug_glBindFramebuffer;\n#define glBindFramebuffer glad_debug_glBindFramebuffer\nGLAD_API_CALL PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;\nGLAD_API_CALL PFNGLBINDRENDERBUFFERPROC glad_debug_glBindRenderbuffer;\n#define glBindRenderbuffer glad_debug_glBindRenderbuffer\nGLAD_API_CALL PFNGLBINDTEXTUREPROC glad_glBindTexture;\nGLAD_API_CALL PFNGLBINDTEXTUREPROC glad_debug_glBindTexture;\n#define glBindTexture glad_debug_glBindTexture\nGLAD_API_CALL PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray;\nGLAD_API_CALL PFNGLBINDVERTEXARRAYPROC glad_debug_glBindVertexArray;\n#define glBindVertexArray glad_debug_glBindVertexArray\nGLAD_API_CALL PFNGLBITMAPPROC glad_glBitmap;\nGLAD_API_CALL PFNGLBITMAPPROC glad_debug_glBitmap;\n#define glBitmap glad_debug_glBitmap\nGLAD_API_CALL PFNGLBLENDCOLORPROC glad_glBlendColor;\nGLAD_API_CALL PFNGLBLENDCOLORPROC glad_debug_glBlendColor;\n#define glBlendColor glad_debug_glBlendColor\nGLAD_API_CALL PFNGLBLENDEQUATIONPROC glad_glBlendEquation;\nGLAD_API_CALL PFNGLBLENDEQUATIONPROC glad_debug_glBlendEquation;\n#define glBlendEquation glad_debug_glBlendEquation\nGLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;\nGLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEPROC glad_debug_glBlendEquationSeparate;\n#define glBlendEquationSeparate glad_debug_glBlendEquationSeparate\nGLAD_API_CALL PFNGLBLENDFUNCPROC glad_glBlendFunc;\nGLAD_API_CALL PFNGLBLENDFUNCPROC glad_debug_glBlendFunc;\n#define glBlendFunc glad_debug_glBlendFunc\nGLAD_API_CALL PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;\nGLAD_API_CALL PFNGLBLENDFUNCSEPARATEPROC glad_debug_glBlendFuncSeparate;\n#define glBlendFuncSeparate glad_debug_glBlendFuncSeparate\nGLAD_API_CALL PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer;\nGLAD_API_CALL PFNGLBLITFRAMEBUFFERPROC glad_debug_glBlitFramebuffer;\n#define glBlitFramebuffer glad_debug_glBlitFramebuffer\nGLAD_API_CALL PFNGLBUFFERDATAPROC glad_glBufferData;\nGLAD_API_CALL PFNGLBUFFERDATAPROC glad_debug_glBufferData;\n#define glBufferData glad_debug_glBufferData\nGLAD_API_CALL PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;\nGLAD_API_CALL PFNGLBUFFERSUBDATAPROC glad_debug_glBufferSubData;\n#define glBufferSubData glad_debug_glBufferSubData\nGLAD_API_CALL PFNGLCALLLISTPROC glad_glCallList;\nGLAD_API_CALL PFNGLCALLLISTPROC glad_debug_glCallList;\n#define glCallList glad_debug_glCallList\nGLAD_API_CALL PFNGLCALLLISTSPROC glad_glCallLists;\nGLAD_API_CALL PFNGLCALLLISTSPROC glad_debug_glCallLists;\n#define glCallLists glad_debug_glCallLists\nGLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;\nGLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_debug_glCheckFramebufferStatus;\n#define glCheckFramebufferStatus glad_debug_glCheckFramebufferStatus\nGLAD_API_CALL PFNGLCLAMPCOLORPROC glad_glClampColor;\nGLAD_API_CALL PFNGLCLAMPCOLORPROC glad_debug_glClampColor;\n#define glClampColor glad_debug_glClampColor\nGLAD_API_CALL PFNGLCLEARPROC glad_glClear;\nGLAD_API_CALL PFNGLCLEARPROC glad_debug_glClear;\n#define glClear glad_debug_glClear\nGLAD_API_CALL PFNGLCLEARACCUMPROC glad_glClearAccum;\nGLAD_API_CALL PFNGLCLEARACCUMPROC glad_debug_glClearAccum;\n#define glClearAccum glad_debug_glClearAccum\nGLAD_API_CALL PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi;\nGLAD_API_CALL PFNGLCLEARBUFFERFIPROC glad_debug_glClearBufferfi;\n#define glClearBufferfi glad_debug_glClearBufferfi\nGLAD_API_CALL PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv;\nGLAD_API_CALL PFNGLCLEARBUFFERFVPROC glad_debug_glClearBufferfv;\n#define glClearBufferfv glad_debug_glClearBufferfv\nGLAD_API_CALL PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv;\nGLAD_API_CALL PFNGLCLEARBUFFERIVPROC glad_debug_glClearBufferiv;\n#define glClearBufferiv glad_debug_glClearBufferiv\nGLAD_API_CALL PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv;\nGLAD_API_CALL PFNGLCLEARBUFFERUIVPROC glad_debug_glClearBufferuiv;\n#define glClearBufferuiv glad_debug_glClearBufferuiv\nGLAD_API_CALL PFNGLCLEARCOLORPROC glad_glClearColor;\nGLAD_API_CALL PFNGLCLEARCOLORPROC glad_debug_glClearColor;\n#define glClearColor glad_debug_glClearColor\nGLAD_API_CALL PFNGLCLEARDEPTHPROC glad_glClearDepth;\nGLAD_API_CALL PFNGLCLEARDEPTHPROC glad_debug_glClearDepth;\n#define glClearDepth glad_debug_glClearDepth\nGLAD_API_CALL PFNGLCLEARINDEXPROC glad_glClearIndex;\nGLAD_API_CALL PFNGLCLEARINDEXPROC glad_debug_glClearIndex;\n#define glClearIndex glad_debug_glClearIndex\nGLAD_API_CALL PFNGLCLEARSTENCILPROC glad_glClearStencil;\nGLAD_API_CALL PFNGLCLEARSTENCILPROC glad_debug_glClearStencil;\n#define glClearStencil glad_debug_glClearStencil\nGLAD_API_CALL PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture;\nGLAD_API_CALL PFNGLCLIENTACTIVETEXTUREPROC glad_debug_glClientActiveTexture;\n#define glClientActiveTexture glad_debug_glClientActiveTexture\nGLAD_API_CALL PFNGLCLIPPLANEPROC glad_glClipPlane;\nGLAD_API_CALL PFNGLCLIPPLANEPROC glad_debug_glClipPlane;\n#define glClipPlane glad_debug_glClipPlane\nGLAD_API_CALL PFNGLCOLOR3BPROC glad_glColor3b;\nGLAD_API_CALL PFNGLCOLOR3BPROC glad_debug_glColor3b;\n#define glColor3b glad_debug_glColor3b\nGLAD_API_CALL PFNGLCOLOR3BVPROC glad_glColor3bv;\nGLAD_API_CALL PFNGLCOLOR3BVPROC glad_debug_glColor3bv;\n#define glColor3bv glad_debug_glColor3bv\nGLAD_API_CALL PFNGLCOLOR3DPROC glad_glColor3d;\nGLAD_API_CALL PFNGLCOLOR3DPROC glad_debug_glColor3d;\n#define glColor3d glad_debug_glColor3d\nGLAD_API_CALL PFNGLCOLOR3DVPROC glad_glColor3dv;\nGLAD_API_CALL PFNGLCOLOR3DVPROC glad_debug_glColor3dv;\n#define glColor3dv glad_debug_glColor3dv\nGLAD_API_CALL PFNGLCOLOR3FPROC glad_glColor3f;\nGLAD_API_CALL PFNGLCOLOR3FPROC glad_debug_glColor3f;\n#define glColor3f glad_debug_glColor3f\nGLAD_API_CALL PFNGLCOLOR3FVPROC glad_glColor3fv;\nGLAD_API_CALL PFNGLCOLOR3FVPROC glad_debug_glColor3fv;\n#define glColor3fv glad_debug_glColor3fv\nGLAD_API_CALL PFNGLCOLOR3IPROC glad_glColor3i;\nGLAD_API_CALL PFNGLCOLOR3IPROC glad_debug_glColor3i;\n#define glColor3i glad_debug_glColor3i\nGLAD_API_CALL PFNGLCOLOR3IVPROC glad_glColor3iv;\nGLAD_API_CALL PFNGLCOLOR3IVPROC glad_debug_glColor3iv;\n#define glColor3iv glad_debug_glColor3iv\nGLAD_API_CALL PFNGLCOLOR3SPROC glad_glColor3s;\nGLAD_API_CALL PFNGLCOLOR3SPROC glad_debug_glColor3s;\n#define glColor3s glad_debug_glColor3s\nGLAD_API_CALL PFNGLCOLOR3SVPROC glad_glColor3sv;\nGLAD_API_CALL PFNGLCOLOR3SVPROC glad_debug_glColor3sv;\n#define glColor3sv glad_debug_glColor3sv\nGLAD_API_CALL PFNGLCOLOR3UBPROC glad_glColor3ub;\nGLAD_API_CALL PFNGLCOLOR3UBPROC glad_debug_glColor3ub;\n#define glColor3ub glad_debug_glColor3ub\nGLAD_API_CALL PFNGLCOLOR3UBVPROC glad_glColor3ubv;\nGLAD_API_CALL PFNGLCOLOR3UBVPROC glad_debug_glColor3ubv;\n#define glColor3ubv glad_debug_glColor3ubv\nGLAD_API_CALL PFNGLCOLOR3UIPROC glad_glColor3ui;\nGLAD_API_CALL PFNGLCOLOR3UIPROC glad_debug_glColor3ui;\n#define glColor3ui glad_debug_glColor3ui\nGLAD_API_CALL PFNGLCOLOR3UIVPROC glad_glColor3uiv;\nGLAD_API_CALL PFNGLCOLOR3UIVPROC glad_debug_glColor3uiv;\n#define glColor3uiv glad_debug_glColor3uiv\nGLAD_API_CALL PFNGLCOLOR3USPROC glad_glColor3us;\nGLAD_API_CALL PFNGLCOLOR3USPROC glad_debug_glColor3us;\n#define glColor3us glad_debug_glColor3us\nGLAD_API_CALL PFNGLCOLOR3USVPROC glad_glColor3usv;\nGLAD_API_CALL PFNGLCOLOR3USVPROC glad_debug_glColor3usv;\n#define glColor3usv glad_debug_glColor3usv\nGLAD_API_CALL PFNGLCOLOR4BPROC glad_glColor4b;\nGLAD_API_CALL PFNGLCOLOR4BPROC glad_debug_glColor4b;\n#define glColor4b glad_debug_glColor4b\nGLAD_API_CALL PFNGLCOLOR4BVPROC glad_glColor4bv;\nGLAD_API_CALL PFNGLCOLOR4BVPROC glad_debug_glColor4bv;\n#define glColor4bv glad_debug_glColor4bv\nGLAD_API_CALL PFNGLCOLOR4DPROC glad_glColor4d;\nGLAD_API_CALL PFNGLCOLOR4DPROC glad_debug_glColor4d;\n#define glColor4d glad_debug_glColor4d\nGLAD_API_CALL PFNGLCOLOR4DVPROC glad_glColor4dv;\nGLAD_API_CALL PFNGLCOLOR4DVPROC glad_debug_glColor4dv;\n#define glColor4dv glad_debug_glColor4dv\nGLAD_API_CALL PFNGLCOLOR4FPROC glad_glColor4f;\nGLAD_API_CALL PFNGLCOLOR4FPROC glad_debug_glColor4f;\n#define glColor4f glad_debug_glColor4f\nGLAD_API_CALL PFNGLCOLOR4FVPROC glad_glColor4fv;\nGLAD_API_CALL PFNGLCOLOR4FVPROC glad_debug_glColor4fv;\n#define glColor4fv glad_debug_glColor4fv\nGLAD_API_CALL PFNGLCOLOR4IPROC glad_glColor4i;\nGLAD_API_CALL PFNGLCOLOR4IPROC glad_debug_glColor4i;\n#define glColor4i glad_debug_glColor4i\nGLAD_API_CALL PFNGLCOLOR4IVPROC glad_glColor4iv;\nGLAD_API_CALL PFNGLCOLOR4IVPROC glad_debug_glColor4iv;\n#define glColor4iv glad_debug_glColor4iv\nGLAD_API_CALL PFNGLCOLOR4SPROC glad_glColor4s;\nGLAD_API_CALL PFNGLCOLOR4SPROC glad_debug_glColor4s;\n#define glColor4s glad_debug_glColor4s\nGLAD_API_CALL PFNGLCOLOR4SVPROC glad_glColor4sv;\nGLAD_API_CALL PFNGLCOLOR4SVPROC glad_debug_glColor4sv;\n#define glColor4sv glad_debug_glColor4sv\nGLAD_API_CALL PFNGLCOLOR4UBPROC glad_glColor4ub;\nGLAD_API_CALL PFNGLCOLOR4UBPROC glad_debug_glColor4ub;\n#define glColor4ub glad_debug_glColor4ub\nGLAD_API_CALL PFNGLCOLOR4UBVPROC glad_glColor4ubv;\nGLAD_API_CALL PFNGLCOLOR4UBVPROC glad_debug_glColor4ubv;\n#define glColor4ubv glad_debug_glColor4ubv\nGLAD_API_CALL PFNGLCOLOR4UIPROC glad_glColor4ui;\nGLAD_API_CALL PFNGLCOLOR4UIPROC glad_debug_glColor4ui;\n#define glColor4ui glad_debug_glColor4ui\nGLAD_API_CALL PFNGLCOLOR4UIVPROC glad_glColor4uiv;\nGLAD_API_CALL PFNGLCOLOR4UIVPROC glad_debug_glColor4uiv;\n#define glColor4uiv glad_debug_glColor4uiv\nGLAD_API_CALL PFNGLCOLOR4USPROC glad_glColor4us;\nGLAD_API_CALL PFNGLCOLOR4USPROC glad_debug_glColor4us;\n#define glColor4us glad_debug_glColor4us\nGLAD_API_CALL PFNGLCOLOR4USVPROC glad_glColor4usv;\nGLAD_API_CALL PFNGLCOLOR4USVPROC glad_debug_glColor4usv;\n#define glColor4usv glad_debug_glColor4usv\nGLAD_API_CALL PFNGLCOLORMASKPROC glad_glColorMask;\nGLAD_API_CALL PFNGLCOLORMASKPROC glad_debug_glColorMask;\n#define glColorMask glad_debug_glColorMask\nGLAD_API_CALL PFNGLCOLORMASKIPROC glad_glColorMaski;\nGLAD_API_CALL PFNGLCOLORMASKIPROC glad_debug_glColorMaski;\n#define glColorMaski glad_debug_glColorMaski\nGLAD_API_CALL PFNGLCOLORMATERIALPROC glad_glColorMaterial;\nGLAD_API_CALL PFNGLCOLORMATERIALPROC glad_debug_glColorMaterial;\n#define glColorMaterial glad_debug_glColorMaterial\nGLAD_API_CALL PFNGLCOLORPOINTERPROC glad_glColorPointer;\nGLAD_API_CALL PFNGLCOLORPOINTERPROC glad_debug_glColorPointer;\n#define glColorPointer glad_debug_glColorPointer\nGLAD_API_CALL PFNGLCOMPILESHADERPROC glad_glCompileShader;\nGLAD_API_CALL PFNGLCOMPILESHADERPROC glad_debug_glCompileShader;\n#define glCompileShader glad_debug_glCompileShader\nGLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D;\nGLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_debug_glCompressedTexImage1D;\n#define glCompressedTexImage1D glad_debug_glCompressedTexImage1D\nGLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;\nGLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_debug_glCompressedTexImage2D;\n#define glCompressedTexImage2D glad_debug_glCompressedTexImage2D\nGLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D;\nGLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_debug_glCompressedTexImage3D;\n#define glCompressedTexImage3D glad_debug_glCompressedTexImage3D\nGLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D;\nGLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_debug_glCompressedTexSubImage1D;\n#define glCompressedTexSubImage1D glad_debug_glCompressedTexSubImage1D\nGLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;\nGLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_debug_glCompressedTexSubImage2D;\n#define glCompressedTexSubImage2D glad_debug_glCompressedTexSubImage2D\nGLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D;\nGLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_debug_glCompressedTexSubImage3D;\n#define glCompressedTexSubImage3D glad_debug_glCompressedTexSubImage3D\nGLAD_API_CALL PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData;\nGLAD_API_CALL PFNGLCOPYBUFFERSUBDATAPROC glad_debug_glCopyBufferSubData;\n#define glCopyBufferSubData glad_debug_glCopyBufferSubData\nGLAD_API_CALL PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData;\nGLAD_API_CALL PFNGLCOPYIMAGESUBDATAPROC glad_debug_glCopyImageSubData;\n#define glCopyImageSubData glad_debug_glCopyImageSubData\nGLAD_API_CALL PFNGLCOPYPIXELSPROC glad_glCopyPixels;\nGLAD_API_CALL PFNGLCOPYPIXELSPROC glad_debug_glCopyPixels;\n#define glCopyPixels glad_debug_glCopyPixels\nGLAD_API_CALL PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D;\nGLAD_API_CALL PFNGLCOPYTEXIMAGE1DPROC glad_debug_glCopyTexImage1D;\n#define glCopyTexImage1D glad_debug_glCopyTexImage1D\nGLAD_API_CALL PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;\nGLAD_API_CALL PFNGLCOPYTEXIMAGE2DPROC glad_debug_glCopyTexImage2D;\n#define glCopyTexImage2D glad_debug_glCopyTexImage2D\nGLAD_API_CALL PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D;\nGLAD_API_CALL PFNGLCOPYTEXSUBIMAGE1DPROC glad_debug_glCopyTexSubImage1D;\n#define glCopyTexSubImage1D glad_debug_glCopyTexSubImage1D\nGLAD_API_CALL PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;\nGLAD_API_CALL PFNGLCOPYTEXSUBIMAGE2DPROC glad_debug_glCopyTexSubImage2D;\n#define glCopyTexSubImage2D glad_debug_glCopyTexSubImage2D\nGLAD_API_CALL PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D;\nGLAD_API_CALL PFNGLCOPYTEXSUBIMAGE3DPROC glad_debug_glCopyTexSubImage3D;\n#define glCopyTexSubImage3D glad_debug_glCopyTexSubImage3D\nGLAD_API_CALL PFNGLCREATEPROGRAMPROC glad_glCreateProgram;\nGLAD_API_CALL PFNGLCREATEPROGRAMPROC glad_debug_glCreateProgram;\n#define glCreateProgram glad_debug_glCreateProgram\nGLAD_API_CALL PFNGLCREATESHADERPROC glad_glCreateShader;\nGLAD_API_CALL PFNGLCREATESHADERPROC glad_debug_glCreateShader;\n#define glCreateShader glad_debug_glCreateShader\nGLAD_API_CALL PFNGLCULLFACEPROC glad_glCullFace;\nGLAD_API_CALL PFNGLCULLFACEPROC glad_debug_glCullFace;\n#define glCullFace glad_debug_glCullFace\nGLAD_API_CALL PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback;\nGLAD_API_CALL PFNGLDEBUGMESSAGECALLBACKPROC glad_debug_glDebugMessageCallback;\n#define glDebugMessageCallback glad_debug_glDebugMessageCallback\nGLAD_API_CALL PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl;\nGLAD_API_CALL PFNGLDEBUGMESSAGECONTROLPROC glad_debug_glDebugMessageControl;\n#define glDebugMessageControl glad_debug_glDebugMessageControl\nGLAD_API_CALL PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert;\nGLAD_API_CALL PFNGLDEBUGMESSAGEINSERTPROC glad_debug_glDebugMessageInsert;\n#define glDebugMessageInsert glad_debug_glDebugMessageInsert\nGLAD_API_CALL PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;\nGLAD_API_CALL PFNGLDELETEBUFFERSPROC glad_debug_glDeleteBuffers;\n#define glDeleteBuffers glad_debug_glDeleteBuffers\nGLAD_API_CALL PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;\nGLAD_API_CALL PFNGLDELETEFRAMEBUFFERSPROC glad_debug_glDeleteFramebuffers;\n#define glDeleteFramebuffers glad_debug_glDeleteFramebuffers\nGLAD_API_CALL PFNGLDELETELISTSPROC glad_glDeleteLists;\nGLAD_API_CALL PFNGLDELETELISTSPROC glad_debug_glDeleteLists;\n#define glDeleteLists glad_debug_glDeleteLists\nGLAD_API_CALL PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;\nGLAD_API_CALL PFNGLDELETEPROGRAMPROC glad_debug_glDeleteProgram;\n#define glDeleteProgram glad_debug_glDeleteProgram\nGLAD_API_CALL PFNGLDELETEQUERIESPROC glad_glDeleteQueries;\nGLAD_API_CALL PFNGLDELETEQUERIESPROC glad_debug_glDeleteQueries;\n#define glDeleteQueries glad_debug_glDeleteQueries\nGLAD_API_CALL PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;\nGLAD_API_CALL PFNGLDELETERENDERBUFFERSPROC glad_debug_glDeleteRenderbuffers;\n#define glDeleteRenderbuffers glad_debug_glDeleteRenderbuffers\nGLAD_API_CALL PFNGLDELETESHADERPROC glad_glDeleteShader;\nGLAD_API_CALL PFNGLDELETESHADERPROC glad_debug_glDeleteShader;\n#define glDeleteShader glad_debug_glDeleteShader\nGLAD_API_CALL PFNGLDELETETEXTURESPROC glad_glDeleteTextures;\nGLAD_API_CALL PFNGLDELETETEXTURESPROC glad_debug_glDeleteTextures;\n#define glDeleteTextures glad_debug_glDeleteTextures\nGLAD_API_CALL PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays;\nGLAD_API_CALL PFNGLDELETEVERTEXARRAYSPROC glad_debug_glDeleteVertexArrays;\n#define glDeleteVertexArrays glad_debug_glDeleteVertexArrays\nGLAD_API_CALL PFNGLDEPTHFUNCPROC glad_glDepthFunc;\nGLAD_API_CALL PFNGLDEPTHFUNCPROC glad_debug_glDepthFunc;\n#define glDepthFunc glad_debug_glDepthFunc\nGLAD_API_CALL PFNGLDEPTHMASKPROC glad_glDepthMask;\nGLAD_API_CALL PFNGLDEPTHMASKPROC glad_debug_glDepthMask;\n#define glDepthMask glad_debug_glDepthMask\nGLAD_API_CALL PFNGLDEPTHRANGEPROC glad_glDepthRange;\nGLAD_API_CALL PFNGLDEPTHRANGEPROC glad_debug_glDepthRange;\n#define glDepthRange glad_debug_glDepthRange\nGLAD_API_CALL PFNGLDETACHSHADERPROC glad_glDetachShader;\nGLAD_API_CALL PFNGLDETACHSHADERPROC glad_debug_glDetachShader;\n#define glDetachShader glad_debug_glDetachShader\nGLAD_API_CALL PFNGLDISABLEPROC glad_glDisable;\nGLAD_API_CALL PFNGLDISABLEPROC glad_debug_glDisable;\n#define glDisable glad_debug_glDisable\nGLAD_API_CALL PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState;\nGLAD_API_CALL PFNGLDISABLECLIENTSTATEPROC glad_debug_glDisableClientState;\n#define glDisableClientState glad_debug_glDisableClientState\nGLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;\nGLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_debug_glDisableVertexAttribArray;\n#define glDisableVertexAttribArray glad_debug_glDisableVertexAttribArray\nGLAD_API_CALL PFNGLDISABLEIPROC glad_glDisablei;\nGLAD_API_CALL PFNGLDISABLEIPROC glad_debug_glDisablei;\n#define glDisablei glad_debug_glDisablei\nGLAD_API_CALL PFNGLDRAWARRAYSPROC glad_glDrawArrays;\nGLAD_API_CALL PFNGLDRAWARRAYSPROC glad_debug_glDrawArrays;\n#define glDrawArrays glad_debug_glDrawArrays\nGLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced;\nGLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDPROC glad_debug_glDrawArraysInstanced;\n#define glDrawArraysInstanced glad_debug_glDrawArraysInstanced\nGLAD_API_CALL PFNGLDRAWBUFFERPROC glad_glDrawBuffer;\nGLAD_API_CALL PFNGLDRAWBUFFERPROC glad_debug_glDrawBuffer;\n#define glDrawBuffer glad_debug_glDrawBuffer\nGLAD_API_CALL PFNGLDRAWBUFFERSPROC glad_glDrawBuffers;\nGLAD_API_CALL PFNGLDRAWBUFFERSPROC glad_debug_glDrawBuffers;\n#define glDrawBuffers glad_debug_glDrawBuffers\nGLAD_API_CALL PFNGLDRAWELEMENTSPROC glad_glDrawElements;\nGLAD_API_CALL PFNGLDRAWELEMENTSPROC glad_debug_glDrawElements;\n#define glDrawElements glad_debug_glDrawElements\nGLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced;\nGLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDPROC glad_debug_glDrawElementsInstanced;\n#define glDrawElementsInstanced glad_debug_glDrawElementsInstanced\nGLAD_API_CALL PFNGLDRAWPIXELSPROC glad_glDrawPixels;\nGLAD_API_CALL PFNGLDRAWPIXELSPROC glad_debug_glDrawPixels;\n#define glDrawPixels glad_debug_glDrawPixels\nGLAD_API_CALL PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements;\nGLAD_API_CALL PFNGLDRAWRANGEELEMENTSPROC glad_debug_glDrawRangeElements;\n#define glDrawRangeElements glad_debug_glDrawRangeElements\nGLAD_API_CALL PFNGLEDGEFLAGPROC glad_glEdgeFlag;\nGLAD_API_CALL PFNGLEDGEFLAGPROC glad_debug_glEdgeFlag;\n#define glEdgeFlag glad_debug_glEdgeFlag\nGLAD_API_CALL PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer;\nGLAD_API_CALL PFNGLEDGEFLAGPOINTERPROC glad_debug_glEdgeFlagPointer;\n#define glEdgeFlagPointer glad_debug_glEdgeFlagPointer\nGLAD_API_CALL PFNGLEDGEFLAGVPROC glad_glEdgeFlagv;\nGLAD_API_CALL PFNGLEDGEFLAGVPROC glad_debug_glEdgeFlagv;\n#define glEdgeFlagv glad_debug_glEdgeFlagv\nGLAD_API_CALL PFNGLENABLEPROC glad_glEnable;\nGLAD_API_CALL PFNGLENABLEPROC glad_debug_glEnable;\n#define glEnable glad_debug_glEnable\nGLAD_API_CALL PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState;\nGLAD_API_CALL PFNGLENABLECLIENTSTATEPROC glad_debug_glEnableClientState;\n#define glEnableClientState glad_debug_glEnableClientState\nGLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;\nGLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYPROC glad_debug_glEnableVertexAttribArray;\n#define glEnableVertexAttribArray glad_debug_glEnableVertexAttribArray\nGLAD_API_CALL PFNGLENABLEIPROC glad_glEnablei;\nGLAD_API_CALL PFNGLENABLEIPROC glad_debug_glEnablei;\n#define glEnablei glad_debug_glEnablei\nGLAD_API_CALL PFNGLENDPROC glad_glEnd;\nGLAD_API_CALL PFNGLENDPROC glad_debug_glEnd;\n#define glEnd glad_debug_glEnd\nGLAD_API_CALL PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;\nGLAD_API_CALL PFNGLENDCONDITIONALRENDERPROC glad_debug_glEndConditionalRender;\n#define glEndConditionalRender glad_debug_glEndConditionalRender\nGLAD_API_CALL PFNGLENDLISTPROC glad_glEndList;\nGLAD_API_CALL PFNGLENDLISTPROC glad_debug_glEndList;\n#define glEndList glad_debug_glEndList\nGLAD_API_CALL PFNGLENDQUERYPROC glad_glEndQuery;\nGLAD_API_CALL PFNGLENDQUERYPROC glad_debug_glEndQuery;\n#define glEndQuery glad_debug_glEndQuery\nGLAD_API_CALL PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;\nGLAD_API_CALL PFNGLENDTRANSFORMFEEDBACKPROC glad_debug_glEndTransformFeedback;\n#define glEndTransformFeedback glad_debug_glEndTransformFeedback\nGLAD_API_CALL PFNGLEVALCOORD1DPROC glad_glEvalCoord1d;\nGLAD_API_CALL PFNGLEVALCOORD1DPROC glad_debug_glEvalCoord1d;\n#define glEvalCoord1d glad_debug_glEvalCoord1d\nGLAD_API_CALL PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv;\nGLAD_API_CALL PFNGLEVALCOORD1DVPROC glad_debug_glEvalCoord1dv;\n#define glEvalCoord1dv glad_debug_glEvalCoord1dv\nGLAD_API_CALL PFNGLEVALCOORD1FPROC glad_glEvalCoord1f;\nGLAD_API_CALL PFNGLEVALCOORD1FPROC glad_debug_glEvalCoord1f;\n#define glEvalCoord1f glad_debug_glEvalCoord1f\nGLAD_API_CALL PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv;\nGLAD_API_CALL PFNGLEVALCOORD1FVPROC glad_debug_glEvalCoord1fv;\n#define glEvalCoord1fv glad_debug_glEvalCoord1fv\nGLAD_API_CALL PFNGLEVALCOORD2DPROC glad_glEvalCoord2d;\nGLAD_API_CALL PFNGLEVALCOORD2DPROC glad_debug_glEvalCoord2d;\n#define glEvalCoord2d glad_debug_glEvalCoord2d\nGLAD_API_CALL PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv;\nGLAD_API_CALL PFNGLEVALCOORD2DVPROC glad_debug_glEvalCoord2dv;\n#define glEvalCoord2dv glad_debug_glEvalCoord2dv\nGLAD_API_CALL PFNGLEVALCOORD2FPROC glad_glEvalCoord2f;\nGLAD_API_CALL PFNGLEVALCOORD2FPROC glad_debug_glEvalCoord2f;\n#define glEvalCoord2f glad_debug_glEvalCoord2f\nGLAD_API_CALL PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv;\nGLAD_API_CALL PFNGLEVALCOORD2FVPROC glad_debug_glEvalCoord2fv;\n#define glEvalCoord2fv glad_debug_glEvalCoord2fv\nGLAD_API_CALL PFNGLEVALMESH1PROC glad_glEvalMesh1;\nGLAD_API_CALL PFNGLEVALMESH1PROC glad_debug_glEvalMesh1;\n#define glEvalMesh1 glad_debug_glEvalMesh1\nGLAD_API_CALL PFNGLEVALMESH2PROC glad_glEvalMesh2;\nGLAD_API_CALL PFNGLEVALMESH2PROC glad_debug_glEvalMesh2;\n#define glEvalMesh2 glad_debug_glEvalMesh2\nGLAD_API_CALL PFNGLEVALPOINT1PROC glad_glEvalPoint1;\nGLAD_API_CALL PFNGLEVALPOINT1PROC glad_debug_glEvalPoint1;\n#define glEvalPoint1 glad_debug_glEvalPoint1\nGLAD_API_CALL PFNGLEVALPOINT2PROC glad_glEvalPoint2;\nGLAD_API_CALL PFNGLEVALPOINT2PROC glad_debug_glEvalPoint2;\n#define glEvalPoint2 glad_debug_glEvalPoint2\nGLAD_API_CALL PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer;\nGLAD_API_CALL PFNGLFEEDBACKBUFFERPROC glad_debug_glFeedbackBuffer;\n#define glFeedbackBuffer glad_debug_glFeedbackBuffer\nGLAD_API_CALL PFNGLFINISHPROC glad_glFinish;\nGLAD_API_CALL PFNGLFINISHPROC glad_debug_glFinish;\n#define glFinish glad_debug_glFinish\nGLAD_API_CALL PFNGLFLUSHPROC glad_glFlush;\nGLAD_API_CALL PFNGLFLUSHPROC glad_debug_glFlush;\n#define glFlush glad_debug_glFlush\nGLAD_API_CALL PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange;\nGLAD_API_CALL PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_debug_glFlushMappedBufferRange;\n#define glFlushMappedBufferRange glad_debug_glFlushMappedBufferRange\nGLAD_API_CALL PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer;\nGLAD_API_CALL PFNGLFOGCOORDPOINTERPROC glad_debug_glFogCoordPointer;\n#define glFogCoordPointer glad_debug_glFogCoordPointer\nGLAD_API_CALL PFNGLFOGCOORDDPROC glad_glFogCoordd;\nGLAD_API_CALL PFNGLFOGCOORDDPROC glad_debug_glFogCoordd;\n#define glFogCoordd glad_debug_glFogCoordd\nGLAD_API_CALL PFNGLFOGCOORDDVPROC glad_glFogCoorddv;\nGLAD_API_CALL PFNGLFOGCOORDDVPROC glad_debug_glFogCoorddv;\n#define glFogCoorddv glad_debug_glFogCoorddv\nGLAD_API_CALL PFNGLFOGCOORDFPROC glad_glFogCoordf;\nGLAD_API_CALL PFNGLFOGCOORDFPROC glad_debug_glFogCoordf;\n#define glFogCoordf glad_debug_glFogCoordf\nGLAD_API_CALL PFNGLFOGCOORDFVPROC glad_glFogCoordfv;\nGLAD_API_CALL PFNGLFOGCOORDFVPROC glad_debug_glFogCoordfv;\n#define glFogCoordfv glad_debug_glFogCoordfv\nGLAD_API_CALL PFNGLFOGFPROC glad_glFogf;\nGLAD_API_CALL PFNGLFOGFPROC glad_debug_glFogf;\n#define glFogf glad_debug_glFogf\nGLAD_API_CALL PFNGLFOGFVPROC glad_glFogfv;\nGLAD_API_CALL PFNGLFOGFVPROC glad_debug_glFogfv;\n#define glFogfv glad_debug_glFogfv\nGLAD_API_CALL PFNGLFOGIPROC glad_glFogi;\nGLAD_API_CALL PFNGLFOGIPROC glad_debug_glFogi;\n#define glFogi glad_debug_glFogi\nGLAD_API_CALL PFNGLFOGIVPROC glad_glFogiv;\nGLAD_API_CALL PFNGLFOGIVPROC glad_debug_glFogiv;\n#define glFogiv glad_debug_glFogiv\nGLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;\nGLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_debug_glFramebufferRenderbuffer;\n#define glFramebufferRenderbuffer glad_debug_glFramebufferRenderbuffer\nGLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D;\nGLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE1DPROC glad_debug_glFramebufferTexture1D;\n#define glFramebufferTexture1D glad_debug_glFramebufferTexture1D\nGLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;\nGLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DPROC glad_debug_glFramebufferTexture2D;\n#define glFramebufferTexture2D glad_debug_glFramebufferTexture2D\nGLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D;\nGLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE3DPROC glad_debug_glFramebufferTexture3D;\n#define glFramebufferTexture3D glad_debug_glFramebufferTexture3D\nGLAD_API_CALL PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer;\nGLAD_API_CALL PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_debug_glFramebufferTextureLayer;\n#define glFramebufferTextureLayer glad_debug_glFramebufferTextureLayer\nGLAD_API_CALL PFNGLFRONTFACEPROC glad_glFrontFace;\nGLAD_API_CALL PFNGLFRONTFACEPROC glad_debug_glFrontFace;\n#define glFrontFace glad_debug_glFrontFace\nGLAD_API_CALL PFNGLFRUSTUMPROC glad_glFrustum;\nGLAD_API_CALL PFNGLFRUSTUMPROC glad_debug_glFrustum;\n#define glFrustum glad_debug_glFrustum\nGLAD_API_CALL PFNGLGENBUFFERSPROC glad_glGenBuffers;\nGLAD_API_CALL PFNGLGENBUFFERSPROC glad_debug_glGenBuffers;\n#define glGenBuffers glad_debug_glGenBuffers\nGLAD_API_CALL PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;\nGLAD_API_CALL PFNGLGENFRAMEBUFFERSPROC glad_debug_glGenFramebuffers;\n#define glGenFramebuffers glad_debug_glGenFramebuffers\nGLAD_API_CALL PFNGLGENLISTSPROC glad_glGenLists;\nGLAD_API_CALL PFNGLGENLISTSPROC glad_debug_glGenLists;\n#define glGenLists glad_debug_glGenLists\nGLAD_API_CALL PFNGLGENQUERIESPROC glad_glGenQueries;\nGLAD_API_CALL PFNGLGENQUERIESPROC glad_debug_glGenQueries;\n#define glGenQueries glad_debug_glGenQueries\nGLAD_API_CALL PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;\nGLAD_API_CALL PFNGLGENRENDERBUFFERSPROC glad_debug_glGenRenderbuffers;\n#define glGenRenderbuffers glad_debug_glGenRenderbuffers\nGLAD_API_CALL PFNGLGENTEXTURESPROC glad_glGenTextures;\nGLAD_API_CALL PFNGLGENTEXTURESPROC glad_debug_glGenTextures;\n#define glGenTextures glad_debug_glGenTextures\nGLAD_API_CALL PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays;\nGLAD_API_CALL PFNGLGENVERTEXARRAYSPROC glad_debug_glGenVertexArrays;\n#define glGenVertexArrays glad_debug_glGenVertexArrays\nGLAD_API_CALL PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;\nGLAD_API_CALL PFNGLGENERATEMIPMAPPROC glad_debug_glGenerateMipmap;\n#define glGenerateMipmap glad_debug_glGenerateMipmap\nGLAD_API_CALL PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;\nGLAD_API_CALL PFNGLGETACTIVEATTRIBPROC glad_debug_glGetActiveAttrib;\n#define glGetActiveAttrib glad_debug_glGetActiveAttrib\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMPROC glad_debug_glGetActiveUniform;\n#define glGetActiveUniform glad_debug_glGetActiveUniform\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName;\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_debug_glGetActiveUniformBlockName;\n#define glGetActiveUniformBlockName glad_debug_glGetActiveUniformBlockName\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv;\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_debug_glGetActiveUniformBlockiv;\n#define glGetActiveUniformBlockiv glad_debug_glGetActiveUniformBlockiv\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName;\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMNAMEPROC glad_debug_glGetActiveUniformName;\n#define glGetActiveUniformName glad_debug_glGetActiveUniformName\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv;\nGLAD_API_CALL PFNGLGETACTIVEUNIFORMSIVPROC glad_debug_glGetActiveUniformsiv;\n#define glGetActiveUniformsiv glad_debug_glGetActiveUniformsiv\nGLAD_API_CALL PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;\nGLAD_API_CALL PFNGLGETATTACHEDSHADERSPROC glad_debug_glGetAttachedShaders;\n#define glGetAttachedShaders glad_debug_glGetAttachedShaders\nGLAD_API_CALL PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;\nGLAD_API_CALL PFNGLGETATTRIBLOCATIONPROC glad_debug_glGetAttribLocation;\n#define glGetAttribLocation glad_debug_glGetAttribLocation\nGLAD_API_CALL PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v;\nGLAD_API_CALL PFNGLGETBOOLEANI_VPROC glad_debug_glGetBooleani_v;\n#define glGetBooleani_v glad_debug_glGetBooleani_v\nGLAD_API_CALL PFNGLGETBOOLEANVPROC glad_glGetBooleanv;\nGLAD_API_CALL PFNGLGETBOOLEANVPROC glad_debug_glGetBooleanv;\n#define glGetBooleanv glad_debug_glGetBooleanv\nGLAD_API_CALL PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;\nGLAD_API_CALL PFNGLGETBUFFERPARAMETERIVPROC glad_debug_glGetBufferParameteriv;\n#define glGetBufferParameteriv glad_debug_glGetBufferParameteriv\nGLAD_API_CALL PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv;\nGLAD_API_CALL PFNGLGETBUFFERPOINTERVPROC glad_debug_glGetBufferPointerv;\n#define glGetBufferPointerv glad_debug_glGetBufferPointerv\nGLAD_API_CALL PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData;\nGLAD_API_CALL PFNGLGETBUFFERSUBDATAPROC glad_debug_glGetBufferSubData;\n#define glGetBufferSubData glad_debug_glGetBufferSubData\nGLAD_API_CALL PFNGLGETCLIPPLANEPROC glad_glGetClipPlane;\nGLAD_API_CALL PFNGLGETCLIPPLANEPROC glad_debug_glGetClipPlane;\n#define glGetClipPlane glad_debug_glGetClipPlane\nGLAD_API_CALL PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage;\nGLAD_API_CALL PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_debug_glGetCompressedTexImage;\n#define glGetCompressedTexImage glad_debug_glGetCompressedTexImage\nGLAD_API_CALL PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog;\nGLAD_API_CALL PFNGLGETDEBUGMESSAGELOGPROC glad_debug_glGetDebugMessageLog;\n#define glGetDebugMessageLog glad_debug_glGetDebugMessageLog\nGLAD_API_CALL PFNGLGETDOUBLEVPROC glad_glGetDoublev;\nGLAD_API_CALL PFNGLGETDOUBLEVPROC glad_debug_glGetDoublev;\n#define glGetDoublev glad_debug_glGetDoublev\nGLAD_API_CALL PFNGLGETERRORPROC glad_glGetError;\nGLAD_API_CALL PFNGLGETERRORPROC glad_debug_glGetError;\n#define glGetError glad_debug_glGetError\nGLAD_API_CALL PFNGLGETFLOATVPROC glad_glGetFloatv;\nGLAD_API_CALL PFNGLGETFLOATVPROC glad_debug_glGetFloatv;\n#define glGetFloatv glad_debug_glGetFloatv\nGLAD_API_CALL PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation;\nGLAD_API_CALL PFNGLGETFRAGDATALOCATIONPROC glad_debug_glGetFragDataLocation;\n#define glGetFragDataLocation glad_debug_glGetFragDataLocation\nGLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;\nGLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_debug_glGetFramebufferAttachmentParameteriv;\n#define glGetFramebufferAttachmentParameteriv glad_debug_glGetFramebufferAttachmentParameteriv\nGLAD_API_CALL PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB;\nGLAD_API_CALL PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_debug_glGetGraphicsResetStatusARB;\n#define glGetGraphicsResetStatusARB glad_debug_glGetGraphicsResetStatusARB\nGLAD_API_CALL PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v;\nGLAD_API_CALL PFNGLGETINTEGERI_VPROC glad_debug_glGetIntegeri_v;\n#define glGetIntegeri_v glad_debug_glGetIntegeri_v\nGLAD_API_CALL PFNGLGETINTEGERVPROC glad_glGetIntegerv;\nGLAD_API_CALL PFNGLGETINTEGERVPROC glad_debug_glGetIntegerv;\n#define glGetIntegerv glad_debug_glGetIntegerv\nGLAD_API_CALL PFNGLGETLIGHTFVPROC glad_glGetLightfv;\nGLAD_API_CALL PFNGLGETLIGHTFVPROC glad_debug_glGetLightfv;\n#define glGetLightfv glad_debug_glGetLightfv\nGLAD_API_CALL PFNGLGETLIGHTIVPROC glad_glGetLightiv;\nGLAD_API_CALL PFNGLGETLIGHTIVPROC glad_debug_glGetLightiv;\n#define glGetLightiv glad_debug_glGetLightiv\nGLAD_API_CALL PFNGLGETMAPDVPROC glad_glGetMapdv;\nGLAD_API_CALL PFNGLGETMAPDVPROC glad_debug_glGetMapdv;\n#define glGetMapdv glad_debug_glGetMapdv\nGLAD_API_CALL PFNGLGETMAPFVPROC glad_glGetMapfv;\nGLAD_API_CALL PFNGLGETMAPFVPROC glad_debug_glGetMapfv;\n#define glGetMapfv glad_debug_glGetMapfv\nGLAD_API_CALL PFNGLGETMAPIVPROC glad_glGetMapiv;\nGLAD_API_CALL PFNGLGETMAPIVPROC glad_debug_glGetMapiv;\n#define glGetMapiv glad_debug_glGetMapiv\nGLAD_API_CALL PFNGLGETMATERIALFVPROC glad_glGetMaterialfv;\nGLAD_API_CALL PFNGLGETMATERIALFVPROC glad_debug_glGetMaterialfv;\n#define glGetMaterialfv glad_debug_glGetMaterialfv\nGLAD_API_CALL PFNGLGETMATERIALIVPROC glad_glGetMaterialiv;\nGLAD_API_CALL PFNGLGETMATERIALIVPROC glad_debug_glGetMaterialiv;\n#define glGetMaterialiv glad_debug_glGetMaterialiv\nGLAD_API_CALL PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel;\nGLAD_API_CALL PFNGLGETOBJECTLABELPROC glad_debug_glGetObjectLabel;\n#define glGetObjectLabel glad_debug_glGetObjectLabel\nGLAD_API_CALL PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel;\nGLAD_API_CALL PFNGLGETOBJECTPTRLABELPROC glad_debug_glGetObjectPtrLabel;\n#define glGetObjectPtrLabel glad_debug_glGetObjectPtrLabel\nGLAD_API_CALL PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv;\nGLAD_API_CALL PFNGLGETPIXELMAPFVPROC glad_debug_glGetPixelMapfv;\n#define glGetPixelMapfv glad_debug_glGetPixelMapfv\nGLAD_API_CALL PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv;\nGLAD_API_CALL PFNGLGETPIXELMAPUIVPROC glad_debug_glGetPixelMapuiv;\n#define glGetPixelMapuiv glad_debug_glGetPixelMapuiv\nGLAD_API_CALL PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv;\nGLAD_API_CALL PFNGLGETPIXELMAPUSVPROC glad_debug_glGetPixelMapusv;\n#define glGetPixelMapusv glad_debug_glGetPixelMapusv\nGLAD_API_CALL PFNGLGETPOINTERVPROC glad_glGetPointerv;\nGLAD_API_CALL PFNGLGETPOINTERVPROC glad_debug_glGetPointerv;\n#define glGetPointerv glad_debug_glGetPointerv\nGLAD_API_CALL PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple;\nGLAD_API_CALL PFNGLGETPOLYGONSTIPPLEPROC glad_debug_glGetPolygonStipple;\n#define glGetPolygonStipple glad_debug_glGetPolygonStipple\nGLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;\nGLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_debug_glGetProgramInfoLog;\n#define glGetProgramInfoLog glad_debug_glGetProgramInfoLog\nGLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;\nGLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_debug_glGetProgramiv;\n#define glGetProgramiv glad_debug_glGetProgramiv\nGLAD_API_CALL PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv;\nGLAD_API_CALL PFNGLGETQUERYOBJECTIVPROC glad_debug_glGetQueryObjectiv;\n#define glGetQueryObjectiv glad_debug_glGetQueryObjectiv\nGLAD_API_CALL PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv;\nGLAD_API_CALL PFNGLGETQUERYOBJECTUIVPROC glad_debug_glGetQueryObjectuiv;\n#define glGetQueryObjectuiv glad_debug_glGetQueryObjectuiv\nGLAD_API_CALL PFNGLGETQUERYIVPROC glad_glGetQueryiv;\nGLAD_API_CALL PFNGLGETQUERYIVPROC glad_debug_glGetQueryiv;\n#define glGetQueryiv glad_debug_glGetQueryiv\nGLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;\nGLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_debug_glGetRenderbufferParameteriv;\n#define glGetRenderbufferParameteriv glad_debug_glGetRenderbufferParameteriv\nGLAD_API_CALL PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;\nGLAD_API_CALL PFNGLGETSHADERINFOLOGPROC glad_debug_glGetShaderInfoLog;\n#define glGetShaderInfoLog glad_debug_glGetShaderInfoLog\nGLAD_API_CALL PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;\nGLAD_API_CALL PFNGLGETSHADERSOURCEPROC glad_debug_glGetShaderSource;\n#define glGetShaderSource glad_debug_glGetShaderSource\nGLAD_API_CALL PFNGLGETSHADERIVPROC glad_glGetShaderiv;\nGLAD_API_CALL PFNGLGETSHADERIVPROC glad_debug_glGetShaderiv;\n#define glGetShaderiv glad_debug_glGetShaderiv\nGLAD_API_CALL PFNGLGETSTRINGPROC glad_glGetString;\nGLAD_API_CALL PFNGLGETSTRINGPROC glad_debug_glGetString;\n#define glGetString glad_debug_glGetString\nGLAD_API_CALL PFNGLGETSTRINGIPROC glad_glGetStringi;\nGLAD_API_CALL PFNGLGETSTRINGIPROC glad_debug_glGetStringi;\n#define glGetStringi glad_debug_glGetStringi\nGLAD_API_CALL PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv;\nGLAD_API_CALL PFNGLGETTEXENVFVPROC glad_debug_glGetTexEnvfv;\n#define glGetTexEnvfv glad_debug_glGetTexEnvfv\nGLAD_API_CALL PFNGLGETTEXENVIVPROC glad_glGetTexEnviv;\nGLAD_API_CALL PFNGLGETTEXENVIVPROC glad_debug_glGetTexEnviv;\n#define glGetTexEnviv glad_debug_glGetTexEnviv\nGLAD_API_CALL PFNGLGETTEXGENDVPROC glad_glGetTexGendv;\nGLAD_API_CALL PFNGLGETTEXGENDVPROC glad_debug_glGetTexGendv;\n#define glGetTexGendv glad_debug_glGetTexGendv\nGLAD_API_CALL PFNGLGETTEXGENFVPROC glad_glGetTexGenfv;\nGLAD_API_CALL PFNGLGETTEXGENFVPROC glad_debug_glGetTexGenfv;\n#define glGetTexGenfv glad_debug_glGetTexGenfv\nGLAD_API_CALL PFNGLGETTEXGENIVPROC glad_glGetTexGeniv;\nGLAD_API_CALL PFNGLGETTEXGENIVPROC glad_debug_glGetTexGeniv;\n#define glGetTexGeniv glad_debug_glGetTexGeniv\nGLAD_API_CALL PFNGLGETTEXIMAGEPROC glad_glGetTexImage;\nGLAD_API_CALL PFNGLGETTEXIMAGEPROC glad_debug_glGetTexImage;\n#define glGetTexImage glad_debug_glGetTexImage\nGLAD_API_CALL PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv;\nGLAD_API_CALL PFNGLGETTEXLEVELPARAMETERFVPROC glad_debug_glGetTexLevelParameterfv;\n#define glGetTexLevelParameterfv glad_debug_glGetTexLevelParameterfv\nGLAD_API_CALL PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv;\nGLAD_API_CALL PFNGLGETTEXLEVELPARAMETERIVPROC glad_debug_glGetTexLevelParameteriv;\n#define glGetTexLevelParameteriv glad_debug_glGetTexLevelParameteriv\nGLAD_API_CALL PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv;\nGLAD_API_CALL PFNGLGETTEXPARAMETERIIVPROC glad_debug_glGetTexParameterIiv;\n#define glGetTexParameterIiv glad_debug_glGetTexParameterIiv\nGLAD_API_CALL PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv;\nGLAD_API_CALL PFNGLGETTEXPARAMETERIUIVPROC glad_debug_glGetTexParameterIuiv;\n#define glGetTexParameterIuiv glad_debug_glGetTexParameterIuiv\nGLAD_API_CALL PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;\nGLAD_API_CALL PFNGLGETTEXPARAMETERFVPROC glad_debug_glGetTexParameterfv;\n#define glGetTexParameterfv glad_debug_glGetTexParameterfv\nGLAD_API_CALL PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;\nGLAD_API_CALL PFNGLGETTEXPARAMETERIVPROC glad_debug_glGetTexParameteriv;\n#define glGetTexParameteriv glad_debug_glGetTexParameteriv\nGLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying;\nGLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_debug_glGetTransformFeedbackVarying;\n#define glGetTransformFeedbackVarying glad_debug_glGetTransformFeedbackVarying\nGLAD_API_CALL PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex;\nGLAD_API_CALL PFNGLGETUNIFORMBLOCKINDEXPROC glad_debug_glGetUniformBlockIndex;\n#define glGetUniformBlockIndex glad_debug_glGetUniformBlockIndex\nGLAD_API_CALL PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices;\nGLAD_API_CALL PFNGLGETUNIFORMINDICESPROC glad_debug_glGetUniformIndices;\n#define glGetUniformIndices glad_debug_glGetUniformIndices\nGLAD_API_CALL PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;\nGLAD_API_CALL PFNGLGETUNIFORMLOCATIONPROC glad_debug_glGetUniformLocation;\n#define glGetUniformLocation glad_debug_glGetUniformLocation\nGLAD_API_CALL PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;\nGLAD_API_CALL PFNGLGETUNIFORMFVPROC glad_debug_glGetUniformfv;\n#define glGetUniformfv glad_debug_glGetUniformfv\nGLAD_API_CALL PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;\nGLAD_API_CALL PFNGLGETUNIFORMIVPROC glad_debug_glGetUniformiv;\n#define glGetUniformiv glad_debug_glGetUniformiv\nGLAD_API_CALL PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv;\nGLAD_API_CALL PFNGLGETUNIFORMUIVPROC glad_debug_glGetUniformuiv;\n#define glGetUniformuiv glad_debug_glGetUniformuiv\nGLAD_API_CALL PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv;\nGLAD_API_CALL PFNGLGETVERTEXATTRIBIIVPROC glad_debug_glGetVertexAttribIiv;\n#define glGetVertexAttribIiv glad_debug_glGetVertexAttribIiv\nGLAD_API_CALL PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv;\nGLAD_API_CALL PFNGLGETVERTEXATTRIBIUIVPROC glad_debug_glGetVertexAttribIuiv;\n#define glGetVertexAttribIuiv glad_debug_glGetVertexAttribIuiv\nGLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;\nGLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVPROC glad_debug_glGetVertexAttribPointerv;\n#define glGetVertexAttribPointerv glad_debug_glGetVertexAttribPointerv\nGLAD_API_CALL PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv;\nGLAD_API_CALL PFNGLGETVERTEXATTRIBDVPROC glad_debug_glGetVertexAttribdv;\n#define glGetVertexAttribdv glad_debug_glGetVertexAttribdv\nGLAD_API_CALL PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;\nGLAD_API_CALL PFNGLGETVERTEXATTRIBFVPROC glad_debug_glGetVertexAttribfv;\n#define glGetVertexAttribfv glad_debug_glGetVertexAttribfv\nGLAD_API_CALL PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;\nGLAD_API_CALL PFNGLGETVERTEXATTRIBIVPROC glad_debug_glGetVertexAttribiv;\n#define glGetVertexAttribiv glad_debug_glGetVertexAttribiv\nGLAD_API_CALL PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB;\nGLAD_API_CALL PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_debug_glGetnCompressedTexImageARB;\n#define glGetnCompressedTexImageARB glad_debug_glGetnCompressedTexImageARB\nGLAD_API_CALL PFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB;\nGLAD_API_CALL PFNGLGETNTEXIMAGEARBPROC glad_debug_glGetnTexImageARB;\n#define glGetnTexImageARB glad_debug_glGetnTexImageARB\nGLAD_API_CALL PFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB;\nGLAD_API_CALL PFNGLGETNUNIFORMDVARBPROC glad_debug_glGetnUniformdvARB;\n#define glGetnUniformdvARB glad_debug_glGetnUniformdvARB\nGLAD_API_CALL PFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB;\nGLAD_API_CALL PFNGLGETNUNIFORMFVARBPROC glad_debug_glGetnUniformfvARB;\n#define glGetnUniformfvARB glad_debug_glGetnUniformfvARB\nGLAD_API_CALL PFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB;\nGLAD_API_CALL PFNGLGETNUNIFORMIVARBPROC glad_debug_glGetnUniformivARB;\n#define glGetnUniformivARB glad_debug_glGetnUniformivARB\nGLAD_API_CALL PFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB;\nGLAD_API_CALL PFNGLGETNUNIFORMUIVARBPROC glad_debug_glGetnUniformuivARB;\n#define glGetnUniformuivARB glad_debug_glGetnUniformuivARB\nGLAD_API_CALL PFNGLHINTPROC glad_glHint;\nGLAD_API_CALL PFNGLHINTPROC glad_debug_glHint;\n#define glHint glad_debug_glHint\nGLAD_API_CALL PFNGLINDEXMASKPROC glad_glIndexMask;\nGLAD_API_CALL PFNGLINDEXMASKPROC glad_debug_glIndexMask;\n#define glIndexMask glad_debug_glIndexMask\nGLAD_API_CALL PFNGLINDEXPOINTERPROC glad_glIndexPointer;\nGLAD_API_CALL PFNGLINDEXPOINTERPROC glad_debug_glIndexPointer;\n#define glIndexPointer glad_debug_glIndexPointer\nGLAD_API_CALL PFNGLINDEXDPROC glad_glIndexd;\nGLAD_API_CALL PFNGLINDEXDPROC glad_debug_glIndexd;\n#define glIndexd glad_debug_glIndexd\nGLAD_API_CALL PFNGLINDEXDVPROC glad_glIndexdv;\nGLAD_API_CALL PFNGLINDEXDVPROC glad_debug_glIndexdv;\n#define glIndexdv glad_debug_glIndexdv\nGLAD_API_CALL PFNGLINDEXFPROC glad_glIndexf;\nGLAD_API_CALL PFNGLINDEXFPROC glad_debug_glIndexf;\n#define glIndexf glad_debug_glIndexf\nGLAD_API_CALL PFNGLINDEXFVPROC glad_glIndexfv;\nGLAD_API_CALL PFNGLINDEXFVPROC glad_debug_glIndexfv;\n#define glIndexfv glad_debug_glIndexfv\nGLAD_API_CALL PFNGLINDEXIPROC glad_glIndexi;\nGLAD_API_CALL PFNGLINDEXIPROC glad_debug_glIndexi;\n#define glIndexi glad_debug_glIndexi\nGLAD_API_CALL PFNGLINDEXIVPROC glad_glIndexiv;\nGLAD_API_CALL PFNGLINDEXIVPROC glad_debug_glIndexiv;\n#define glIndexiv glad_debug_glIndexiv\nGLAD_API_CALL PFNGLINDEXSPROC glad_glIndexs;\nGLAD_API_CALL PFNGLINDEXSPROC glad_debug_glIndexs;\n#define glIndexs glad_debug_glIndexs\nGLAD_API_CALL PFNGLINDEXSVPROC glad_glIndexsv;\nGLAD_API_CALL PFNGLINDEXSVPROC glad_debug_glIndexsv;\n#define glIndexsv glad_debug_glIndexsv\nGLAD_API_CALL PFNGLINDEXUBPROC glad_glIndexub;\nGLAD_API_CALL PFNGLINDEXUBPROC glad_debug_glIndexub;\n#define glIndexub glad_debug_glIndexub\nGLAD_API_CALL PFNGLINDEXUBVPROC glad_glIndexubv;\nGLAD_API_CALL PFNGLINDEXUBVPROC glad_debug_glIndexubv;\n#define glIndexubv glad_debug_glIndexubv\nGLAD_API_CALL PFNGLINITNAMESPROC glad_glInitNames;\nGLAD_API_CALL PFNGLINITNAMESPROC glad_debug_glInitNames;\n#define glInitNames glad_debug_glInitNames\nGLAD_API_CALL PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays;\nGLAD_API_CALL PFNGLINTERLEAVEDARRAYSPROC glad_debug_glInterleavedArrays;\n#define glInterleavedArrays glad_debug_glInterleavedArrays\nGLAD_API_CALL PFNGLISBUFFERPROC glad_glIsBuffer;\nGLAD_API_CALL PFNGLISBUFFERPROC glad_debug_glIsBuffer;\n#define glIsBuffer glad_debug_glIsBuffer\nGLAD_API_CALL PFNGLISENABLEDPROC glad_glIsEnabled;\nGLAD_API_CALL PFNGLISENABLEDPROC glad_debug_glIsEnabled;\n#define glIsEnabled glad_debug_glIsEnabled\nGLAD_API_CALL PFNGLISENABLEDIPROC glad_glIsEnabledi;\nGLAD_API_CALL PFNGLISENABLEDIPROC glad_debug_glIsEnabledi;\n#define glIsEnabledi glad_debug_glIsEnabledi\nGLAD_API_CALL PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;\nGLAD_API_CALL PFNGLISFRAMEBUFFERPROC glad_debug_glIsFramebuffer;\n#define glIsFramebuffer glad_debug_glIsFramebuffer\nGLAD_API_CALL PFNGLISLISTPROC glad_glIsList;\nGLAD_API_CALL PFNGLISLISTPROC glad_debug_glIsList;\n#define glIsList glad_debug_glIsList\nGLAD_API_CALL PFNGLISPROGRAMPROC glad_glIsProgram;\nGLAD_API_CALL PFNGLISPROGRAMPROC glad_debug_glIsProgram;\n#define glIsProgram glad_debug_glIsProgram\nGLAD_API_CALL PFNGLISQUERYPROC glad_glIsQuery;\nGLAD_API_CALL PFNGLISQUERYPROC glad_debug_glIsQuery;\n#define glIsQuery glad_debug_glIsQuery\nGLAD_API_CALL PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;\nGLAD_API_CALL PFNGLISRENDERBUFFERPROC glad_debug_glIsRenderbuffer;\n#define glIsRenderbuffer glad_debug_glIsRenderbuffer\nGLAD_API_CALL PFNGLISSHADERPROC glad_glIsShader;\nGLAD_API_CALL PFNGLISSHADERPROC glad_debug_glIsShader;\n#define glIsShader glad_debug_glIsShader\nGLAD_API_CALL PFNGLISTEXTUREPROC glad_glIsTexture;\nGLAD_API_CALL PFNGLISTEXTUREPROC glad_debug_glIsTexture;\n#define glIsTexture glad_debug_glIsTexture\nGLAD_API_CALL PFNGLISVERTEXARRAYPROC glad_glIsVertexArray;\nGLAD_API_CALL PFNGLISVERTEXARRAYPROC glad_debug_glIsVertexArray;\n#define glIsVertexArray glad_debug_glIsVertexArray\nGLAD_API_CALL PFNGLLIGHTMODELFPROC glad_glLightModelf;\nGLAD_API_CALL PFNGLLIGHTMODELFPROC glad_debug_glLightModelf;\n#define glLightModelf glad_debug_glLightModelf\nGLAD_API_CALL PFNGLLIGHTMODELFVPROC glad_glLightModelfv;\nGLAD_API_CALL PFNGLLIGHTMODELFVPROC glad_debug_glLightModelfv;\n#define glLightModelfv glad_debug_glLightModelfv\nGLAD_API_CALL PFNGLLIGHTMODELIPROC glad_glLightModeli;\nGLAD_API_CALL PFNGLLIGHTMODELIPROC glad_debug_glLightModeli;\n#define glLightModeli glad_debug_glLightModeli\nGLAD_API_CALL PFNGLLIGHTMODELIVPROC glad_glLightModeliv;\nGLAD_API_CALL PFNGLLIGHTMODELIVPROC glad_debug_glLightModeliv;\n#define glLightModeliv glad_debug_glLightModeliv\nGLAD_API_CALL PFNGLLIGHTFPROC glad_glLightf;\nGLAD_API_CALL PFNGLLIGHTFPROC glad_debug_glLightf;\n#define glLightf glad_debug_glLightf\nGLAD_API_CALL PFNGLLIGHTFVPROC glad_glLightfv;\nGLAD_API_CALL PFNGLLIGHTFVPROC glad_debug_glLightfv;\n#define glLightfv glad_debug_glLightfv\nGLAD_API_CALL PFNGLLIGHTIPROC glad_glLighti;\nGLAD_API_CALL PFNGLLIGHTIPROC glad_debug_glLighti;\n#define glLighti glad_debug_glLighti\nGLAD_API_CALL PFNGLLIGHTIVPROC glad_glLightiv;\nGLAD_API_CALL PFNGLLIGHTIVPROC glad_debug_glLightiv;\n#define glLightiv glad_debug_glLightiv\nGLAD_API_CALL PFNGLLINESTIPPLEPROC glad_glLineStipple;\nGLAD_API_CALL PFNGLLINESTIPPLEPROC glad_debug_glLineStipple;\n#define glLineStipple glad_debug_glLineStipple\nGLAD_API_CALL PFNGLLINEWIDTHPROC glad_glLineWidth;\nGLAD_API_CALL PFNGLLINEWIDTHPROC glad_debug_glLineWidth;\n#define glLineWidth glad_debug_glLineWidth\nGLAD_API_CALL PFNGLLINKPROGRAMPROC glad_glLinkProgram;\nGLAD_API_CALL PFNGLLINKPROGRAMPROC glad_debug_glLinkProgram;\n#define glLinkProgram glad_debug_glLinkProgram\nGLAD_API_CALL PFNGLLISTBASEPROC glad_glListBase;\nGLAD_API_CALL PFNGLLISTBASEPROC glad_debug_glListBase;\n#define glListBase glad_debug_glListBase\nGLAD_API_CALL PFNGLLOADIDENTITYPROC glad_glLoadIdentity;\nGLAD_API_CALL PFNGLLOADIDENTITYPROC glad_debug_glLoadIdentity;\n#define glLoadIdentity glad_debug_glLoadIdentity\nGLAD_API_CALL PFNGLLOADMATRIXDPROC glad_glLoadMatrixd;\nGLAD_API_CALL PFNGLLOADMATRIXDPROC glad_debug_glLoadMatrixd;\n#define glLoadMatrixd glad_debug_glLoadMatrixd\nGLAD_API_CALL PFNGLLOADMATRIXFPROC glad_glLoadMatrixf;\nGLAD_API_CALL PFNGLLOADMATRIXFPROC glad_debug_glLoadMatrixf;\n#define glLoadMatrixf glad_debug_glLoadMatrixf\nGLAD_API_CALL PFNGLLOADNAMEPROC glad_glLoadName;\nGLAD_API_CALL PFNGLLOADNAMEPROC glad_debug_glLoadName;\n#define glLoadName glad_debug_glLoadName\nGLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd;\nGLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXDPROC glad_debug_glLoadTransposeMatrixd;\n#define glLoadTransposeMatrixd glad_debug_glLoadTransposeMatrixd\nGLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf;\nGLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXFPROC glad_debug_glLoadTransposeMatrixf;\n#define glLoadTransposeMatrixf glad_debug_glLoadTransposeMatrixf\nGLAD_API_CALL PFNGLLOGICOPPROC glad_glLogicOp;\nGLAD_API_CALL PFNGLLOGICOPPROC glad_debug_glLogicOp;\n#define glLogicOp glad_debug_glLogicOp\nGLAD_API_CALL PFNGLMAP1DPROC glad_glMap1d;\nGLAD_API_CALL PFNGLMAP1DPROC glad_debug_glMap1d;\n#define glMap1d glad_debug_glMap1d\nGLAD_API_CALL PFNGLMAP1FPROC glad_glMap1f;\nGLAD_API_CALL PFNGLMAP1FPROC glad_debug_glMap1f;\n#define glMap1f glad_debug_glMap1f\nGLAD_API_CALL PFNGLMAP2DPROC glad_glMap2d;\nGLAD_API_CALL PFNGLMAP2DPROC glad_debug_glMap2d;\n#define glMap2d glad_debug_glMap2d\nGLAD_API_CALL PFNGLMAP2FPROC glad_glMap2f;\nGLAD_API_CALL PFNGLMAP2FPROC glad_debug_glMap2f;\n#define glMap2f glad_debug_glMap2f\nGLAD_API_CALL PFNGLMAPBUFFERPROC glad_glMapBuffer;\nGLAD_API_CALL PFNGLMAPBUFFERPROC glad_debug_glMapBuffer;\n#define glMapBuffer glad_debug_glMapBuffer\nGLAD_API_CALL PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange;\nGLAD_API_CALL PFNGLMAPBUFFERRANGEPROC glad_debug_glMapBufferRange;\n#define glMapBufferRange glad_debug_glMapBufferRange\nGLAD_API_CALL PFNGLMAPGRID1DPROC glad_glMapGrid1d;\nGLAD_API_CALL PFNGLMAPGRID1DPROC glad_debug_glMapGrid1d;\n#define glMapGrid1d glad_debug_glMapGrid1d\nGLAD_API_CALL PFNGLMAPGRID1FPROC glad_glMapGrid1f;\nGLAD_API_CALL PFNGLMAPGRID1FPROC glad_debug_glMapGrid1f;\n#define glMapGrid1f glad_debug_glMapGrid1f\nGLAD_API_CALL PFNGLMAPGRID2DPROC glad_glMapGrid2d;\nGLAD_API_CALL PFNGLMAPGRID2DPROC glad_debug_glMapGrid2d;\n#define glMapGrid2d glad_debug_glMapGrid2d\nGLAD_API_CALL PFNGLMAPGRID2FPROC glad_glMapGrid2f;\nGLAD_API_CALL PFNGLMAPGRID2FPROC glad_debug_glMapGrid2f;\n#define glMapGrid2f glad_debug_glMapGrid2f\nGLAD_API_CALL PFNGLMATERIALFPROC glad_glMaterialf;\nGLAD_API_CALL PFNGLMATERIALFPROC glad_debug_glMaterialf;\n#define glMaterialf glad_debug_glMaterialf\nGLAD_API_CALL PFNGLMATERIALFVPROC glad_glMaterialfv;\nGLAD_API_CALL PFNGLMATERIALFVPROC glad_debug_glMaterialfv;\n#define glMaterialfv glad_debug_glMaterialfv\nGLAD_API_CALL PFNGLMATERIALIPROC glad_glMateriali;\nGLAD_API_CALL PFNGLMATERIALIPROC glad_debug_glMateriali;\n#define glMateriali glad_debug_glMateriali\nGLAD_API_CALL PFNGLMATERIALIVPROC glad_glMaterialiv;\nGLAD_API_CALL PFNGLMATERIALIVPROC glad_debug_glMaterialiv;\n#define glMaterialiv glad_debug_glMaterialiv\nGLAD_API_CALL PFNGLMATRIXMODEPROC glad_glMatrixMode;\nGLAD_API_CALL PFNGLMATRIXMODEPROC glad_debug_glMatrixMode;\n#define glMatrixMode glad_debug_glMatrixMode\nGLAD_API_CALL PFNGLMULTMATRIXDPROC glad_glMultMatrixd;\nGLAD_API_CALL PFNGLMULTMATRIXDPROC glad_debug_glMultMatrixd;\n#define glMultMatrixd glad_debug_glMultMatrixd\nGLAD_API_CALL PFNGLMULTMATRIXFPROC glad_glMultMatrixf;\nGLAD_API_CALL PFNGLMULTMATRIXFPROC glad_debug_glMultMatrixf;\n#define glMultMatrixf glad_debug_glMultMatrixf\nGLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd;\nGLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXDPROC glad_debug_glMultTransposeMatrixd;\n#define glMultTransposeMatrixd glad_debug_glMultTransposeMatrixd\nGLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf;\nGLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXFPROC glad_debug_glMultTransposeMatrixf;\n#define glMultTransposeMatrixf glad_debug_glMultTransposeMatrixf\nGLAD_API_CALL PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays;\nGLAD_API_CALL PFNGLMULTIDRAWARRAYSPROC glad_debug_glMultiDrawArrays;\n#define glMultiDrawArrays glad_debug_glMultiDrawArrays\nGLAD_API_CALL PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements;\nGLAD_API_CALL PFNGLMULTIDRAWELEMENTSPROC glad_debug_glMultiDrawElements;\n#define glMultiDrawElements glad_debug_glMultiDrawElements\nGLAD_API_CALL PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d;\nGLAD_API_CALL PFNGLMULTITEXCOORD1DPROC glad_debug_glMultiTexCoord1d;\n#define glMultiTexCoord1d glad_debug_glMultiTexCoord1d\nGLAD_API_CALL PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv;\nGLAD_API_CALL PFNGLMULTITEXCOORD1DVPROC glad_debug_glMultiTexCoord1dv;\n#define glMultiTexCoord1dv glad_debug_glMultiTexCoord1dv\nGLAD_API_CALL PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f;\nGLAD_API_CALL PFNGLMULTITEXCOORD1FPROC glad_debug_glMultiTexCoord1f;\n#define glMultiTexCoord1f glad_debug_glMultiTexCoord1f\nGLAD_API_CALL PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv;\nGLAD_API_CALL PFNGLMULTITEXCOORD1FVPROC glad_debug_glMultiTexCoord1fv;\n#define glMultiTexCoord1fv glad_debug_glMultiTexCoord1fv\nGLAD_API_CALL PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i;\nGLAD_API_CALL PFNGLMULTITEXCOORD1IPROC glad_debug_glMultiTexCoord1i;\n#define glMultiTexCoord1i glad_debug_glMultiTexCoord1i\nGLAD_API_CALL PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv;\nGLAD_API_CALL PFNGLMULTITEXCOORD1IVPROC glad_debug_glMultiTexCoord1iv;\n#define glMultiTexCoord1iv glad_debug_glMultiTexCoord1iv\nGLAD_API_CALL PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s;\nGLAD_API_CALL PFNGLMULTITEXCOORD1SPROC glad_debug_glMultiTexCoord1s;\n#define glMultiTexCoord1s glad_debug_glMultiTexCoord1s\nGLAD_API_CALL PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv;\nGLAD_API_CALL PFNGLMULTITEXCOORD1SVPROC glad_debug_glMultiTexCoord1sv;\n#define glMultiTexCoord1sv glad_debug_glMultiTexCoord1sv\nGLAD_API_CALL PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d;\nGLAD_API_CALL PFNGLMULTITEXCOORD2DPROC glad_debug_glMultiTexCoord2d;\n#define glMultiTexCoord2d glad_debug_glMultiTexCoord2d\nGLAD_API_CALL PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv;\nGLAD_API_CALL PFNGLMULTITEXCOORD2DVPROC glad_debug_glMultiTexCoord2dv;\n#define glMultiTexCoord2dv glad_debug_glMultiTexCoord2dv\nGLAD_API_CALL PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f;\nGLAD_API_CALL PFNGLMULTITEXCOORD2FPROC glad_debug_glMultiTexCoord2f;\n#define glMultiTexCoord2f glad_debug_glMultiTexCoord2f\nGLAD_API_CALL PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv;\nGLAD_API_CALL PFNGLMULTITEXCOORD2FVPROC glad_debug_glMultiTexCoord2fv;\n#define glMultiTexCoord2fv glad_debug_glMultiTexCoord2fv\nGLAD_API_CALL PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i;\nGLAD_API_CALL PFNGLMULTITEXCOORD2IPROC glad_debug_glMultiTexCoord2i;\n#define glMultiTexCoord2i glad_debug_glMultiTexCoord2i\nGLAD_API_CALL PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv;\nGLAD_API_CALL PFNGLMULTITEXCOORD2IVPROC glad_debug_glMultiTexCoord2iv;\n#define glMultiTexCoord2iv glad_debug_glMultiTexCoord2iv\nGLAD_API_CALL PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s;\nGLAD_API_CALL PFNGLMULTITEXCOORD2SPROC glad_debug_glMultiTexCoord2s;\n#define glMultiTexCoord2s glad_debug_glMultiTexCoord2s\nGLAD_API_CALL PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv;\nGLAD_API_CALL PFNGLMULTITEXCOORD2SVPROC glad_debug_glMultiTexCoord2sv;\n#define glMultiTexCoord2sv glad_debug_glMultiTexCoord2sv\nGLAD_API_CALL PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d;\nGLAD_API_CALL PFNGLMULTITEXCOORD3DPROC glad_debug_glMultiTexCoord3d;\n#define glMultiTexCoord3d glad_debug_glMultiTexCoord3d\nGLAD_API_CALL PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv;\nGLAD_API_CALL PFNGLMULTITEXCOORD3DVPROC glad_debug_glMultiTexCoord3dv;\n#define glMultiTexCoord3dv glad_debug_glMultiTexCoord3dv\nGLAD_API_CALL PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f;\nGLAD_API_CALL PFNGLMULTITEXCOORD3FPROC glad_debug_glMultiTexCoord3f;\n#define glMultiTexCoord3f glad_debug_glMultiTexCoord3f\nGLAD_API_CALL PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv;\nGLAD_API_CALL PFNGLMULTITEXCOORD3FVPROC glad_debug_glMultiTexCoord3fv;\n#define glMultiTexCoord3fv glad_debug_glMultiTexCoord3fv\nGLAD_API_CALL PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i;\nGLAD_API_CALL PFNGLMULTITEXCOORD3IPROC glad_debug_glMultiTexCoord3i;\n#define glMultiTexCoord3i glad_debug_glMultiTexCoord3i\nGLAD_API_CALL PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv;\nGLAD_API_CALL PFNGLMULTITEXCOORD3IVPROC glad_debug_glMultiTexCoord3iv;\n#define glMultiTexCoord3iv glad_debug_glMultiTexCoord3iv\nGLAD_API_CALL PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s;\nGLAD_API_CALL PFNGLMULTITEXCOORD3SPROC glad_debug_glMultiTexCoord3s;\n#define glMultiTexCoord3s glad_debug_glMultiTexCoord3s\nGLAD_API_CALL PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv;\nGLAD_API_CALL PFNGLMULTITEXCOORD3SVPROC glad_debug_glMultiTexCoord3sv;\n#define glMultiTexCoord3sv glad_debug_glMultiTexCoord3sv\nGLAD_API_CALL PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d;\nGLAD_API_CALL PFNGLMULTITEXCOORD4DPROC glad_debug_glMultiTexCoord4d;\n#define glMultiTexCoord4d glad_debug_glMultiTexCoord4d\nGLAD_API_CALL PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv;\nGLAD_API_CALL PFNGLMULTITEXCOORD4DVPROC glad_debug_glMultiTexCoord4dv;\n#define glMultiTexCoord4dv glad_debug_glMultiTexCoord4dv\nGLAD_API_CALL PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f;\nGLAD_API_CALL PFNGLMULTITEXCOORD4FPROC glad_debug_glMultiTexCoord4f;\n#define glMultiTexCoord4f glad_debug_glMultiTexCoord4f\nGLAD_API_CALL PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv;\nGLAD_API_CALL PFNGLMULTITEXCOORD4FVPROC glad_debug_glMultiTexCoord4fv;\n#define glMultiTexCoord4fv glad_debug_glMultiTexCoord4fv\nGLAD_API_CALL PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i;\nGLAD_API_CALL PFNGLMULTITEXCOORD4IPROC glad_debug_glMultiTexCoord4i;\n#define glMultiTexCoord4i glad_debug_glMultiTexCoord4i\nGLAD_API_CALL PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv;\nGLAD_API_CALL PFNGLMULTITEXCOORD4IVPROC glad_debug_glMultiTexCoord4iv;\n#define glMultiTexCoord4iv glad_debug_glMultiTexCoord4iv\nGLAD_API_CALL PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s;\nGLAD_API_CALL PFNGLMULTITEXCOORD4SPROC glad_debug_glMultiTexCoord4s;\n#define glMultiTexCoord4s glad_debug_glMultiTexCoord4s\nGLAD_API_CALL PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv;\nGLAD_API_CALL PFNGLMULTITEXCOORD4SVPROC glad_debug_glMultiTexCoord4sv;\n#define glMultiTexCoord4sv glad_debug_glMultiTexCoord4sv\nGLAD_API_CALL PFNGLNEWLISTPROC glad_glNewList;\nGLAD_API_CALL PFNGLNEWLISTPROC glad_debug_glNewList;\n#define glNewList glad_debug_glNewList\nGLAD_API_CALL PFNGLNORMAL3BPROC glad_glNormal3b;\nGLAD_API_CALL PFNGLNORMAL3BPROC glad_debug_glNormal3b;\n#define glNormal3b glad_debug_glNormal3b\nGLAD_API_CALL PFNGLNORMAL3BVPROC glad_glNormal3bv;\nGLAD_API_CALL PFNGLNORMAL3BVPROC glad_debug_glNormal3bv;\n#define glNormal3bv glad_debug_glNormal3bv\nGLAD_API_CALL PFNGLNORMAL3DPROC glad_glNormal3d;\nGLAD_API_CALL PFNGLNORMAL3DPROC glad_debug_glNormal3d;\n#define glNormal3d glad_debug_glNormal3d\nGLAD_API_CALL PFNGLNORMAL3DVPROC glad_glNormal3dv;\nGLAD_API_CALL PFNGLNORMAL3DVPROC glad_debug_glNormal3dv;\n#define glNormal3dv glad_debug_glNormal3dv\nGLAD_API_CALL PFNGLNORMAL3FPROC glad_glNormal3f;\nGLAD_API_CALL PFNGLNORMAL3FPROC glad_debug_glNormal3f;\n#define glNormal3f glad_debug_glNormal3f\nGLAD_API_CALL PFNGLNORMAL3FVPROC glad_glNormal3fv;\nGLAD_API_CALL PFNGLNORMAL3FVPROC glad_debug_glNormal3fv;\n#define glNormal3fv glad_debug_glNormal3fv\nGLAD_API_CALL PFNGLNORMAL3IPROC glad_glNormal3i;\nGLAD_API_CALL PFNGLNORMAL3IPROC glad_debug_glNormal3i;\n#define glNormal3i glad_debug_glNormal3i\nGLAD_API_CALL PFNGLNORMAL3IVPROC glad_glNormal3iv;\nGLAD_API_CALL PFNGLNORMAL3IVPROC glad_debug_glNormal3iv;\n#define glNormal3iv glad_debug_glNormal3iv\nGLAD_API_CALL PFNGLNORMAL3SPROC glad_glNormal3s;\nGLAD_API_CALL PFNGLNORMAL3SPROC glad_debug_glNormal3s;\n#define glNormal3s glad_debug_glNormal3s\nGLAD_API_CALL PFNGLNORMAL3SVPROC glad_glNormal3sv;\nGLAD_API_CALL PFNGLNORMAL3SVPROC glad_debug_glNormal3sv;\n#define glNormal3sv glad_debug_glNormal3sv\nGLAD_API_CALL PFNGLNORMALPOINTERPROC glad_glNormalPointer;\nGLAD_API_CALL PFNGLNORMALPOINTERPROC glad_debug_glNormalPointer;\n#define glNormalPointer glad_debug_glNormalPointer\nGLAD_API_CALL PFNGLOBJECTLABELPROC glad_glObjectLabel;\nGLAD_API_CALL PFNGLOBJECTLABELPROC glad_debug_glObjectLabel;\n#define glObjectLabel glad_debug_glObjectLabel\nGLAD_API_CALL PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel;\nGLAD_API_CALL PFNGLOBJECTPTRLABELPROC glad_debug_glObjectPtrLabel;\n#define glObjectPtrLabel glad_debug_glObjectPtrLabel\nGLAD_API_CALL PFNGLORTHOPROC glad_glOrtho;\nGLAD_API_CALL PFNGLORTHOPROC glad_debug_glOrtho;\n#define glOrtho glad_debug_glOrtho\nGLAD_API_CALL PFNGLPASSTHROUGHPROC glad_glPassThrough;\nGLAD_API_CALL PFNGLPASSTHROUGHPROC glad_debug_glPassThrough;\n#define glPassThrough glad_debug_glPassThrough\nGLAD_API_CALL PFNGLPIXELMAPFVPROC glad_glPixelMapfv;\nGLAD_API_CALL PFNGLPIXELMAPFVPROC glad_debug_glPixelMapfv;\n#define glPixelMapfv glad_debug_glPixelMapfv\nGLAD_API_CALL PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv;\nGLAD_API_CALL PFNGLPIXELMAPUIVPROC glad_debug_glPixelMapuiv;\n#define glPixelMapuiv glad_debug_glPixelMapuiv\nGLAD_API_CALL PFNGLPIXELMAPUSVPROC glad_glPixelMapusv;\nGLAD_API_CALL PFNGLPIXELMAPUSVPROC glad_debug_glPixelMapusv;\n#define glPixelMapusv glad_debug_glPixelMapusv\nGLAD_API_CALL PFNGLPIXELSTOREFPROC glad_glPixelStoref;\nGLAD_API_CALL PFNGLPIXELSTOREFPROC glad_debug_glPixelStoref;\n#define glPixelStoref glad_debug_glPixelStoref\nGLAD_API_CALL PFNGLPIXELSTOREIPROC glad_glPixelStorei;\nGLAD_API_CALL PFNGLPIXELSTOREIPROC glad_debug_glPixelStorei;\n#define glPixelStorei glad_debug_glPixelStorei\nGLAD_API_CALL PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf;\nGLAD_API_CALL PFNGLPIXELTRANSFERFPROC glad_debug_glPixelTransferf;\n#define glPixelTransferf glad_debug_glPixelTransferf\nGLAD_API_CALL PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi;\nGLAD_API_CALL PFNGLPIXELTRANSFERIPROC glad_debug_glPixelTransferi;\n#define glPixelTransferi glad_debug_glPixelTransferi\nGLAD_API_CALL PFNGLPIXELZOOMPROC glad_glPixelZoom;\nGLAD_API_CALL PFNGLPIXELZOOMPROC glad_debug_glPixelZoom;\n#define glPixelZoom glad_debug_glPixelZoom\nGLAD_API_CALL PFNGLPOINTPARAMETERFPROC glad_glPointParameterf;\nGLAD_API_CALL PFNGLPOINTPARAMETERFPROC glad_debug_glPointParameterf;\n#define glPointParameterf glad_debug_glPointParameterf\nGLAD_API_CALL PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv;\nGLAD_API_CALL PFNGLPOINTPARAMETERFVPROC glad_debug_glPointParameterfv;\n#define glPointParameterfv glad_debug_glPointParameterfv\nGLAD_API_CALL PFNGLPOINTPARAMETERIPROC glad_glPointParameteri;\nGLAD_API_CALL PFNGLPOINTPARAMETERIPROC glad_debug_glPointParameteri;\n#define glPointParameteri glad_debug_glPointParameteri\nGLAD_API_CALL PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv;\nGLAD_API_CALL PFNGLPOINTPARAMETERIVPROC glad_debug_glPointParameteriv;\n#define glPointParameteriv glad_debug_glPointParameteriv\nGLAD_API_CALL PFNGLPOINTSIZEPROC glad_glPointSize;\nGLAD_API_CALL PFNGLPOINTSIZEPROC glad_debug_glPointSize;\n#define glPointSize glad_debug_glPointSize\nGLAD_API_CALL PFNGLPOLYGONMODEPROC glad_glPolygonMode;\nGLAD_API_CALL PFNGLPOLYGONMODEPROC glad_debug_glPolygonMode;\n#define glPolygonMode glad_debug_glPolygonMode\nGLAD_API_CALL PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;\nGLAD_API_CALL PFNGLPOLYGONOFFSETPROC glad_debug_glPolygonOffset;\n#define glPolygonOffset glad_debug_glPolygonOffset\nGLAD_API_CALL PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple;\nGLAD_API_CALL PFNGLPOLYGONSTIPPLEPROC glad_debug_glPolygonStipple;\n#define glPolygonStipple glad_debug_glPolygonStipple\nGLAD_API_CALL PFNGLPOPATTRIBPROC glad_glPopAttrib;\nGLAD_API_CALL PFNGLPOPATTRIBPROC glad_debug_glPopAttrib;\n#define glPopAttrib glad_debug_glPopAttrib\nGLAD_API_CALL PFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib;\nGLAD_API_CALL PFNGLPOPCLIENTATTRIBPROC glad_debug_glPopClientAttrib;\n#define glPopClientAttrib glad_debug_glPopClientAttrib\nGLAD_API_CALL PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup;\nGLAD_API_CALL PFNGLPOPDEBUGGROUPPROC glad_debug_glPopDebugGroup;\n#define glPopDebugGroup glad_debug_glPopDebugGroup\nGLAD_API_CALL PFNGLPOPMATRIXPROC glad_glPopMatrix;\nGLAD_API_CALL PFNGLPOPMATRIXPROC glad_debug_glPopMatrix;\n#define glPopMatrix glad_debug_glPopMatrix\nGLAD_API_CALL PFNGLPOPNAMEPROC glad_glPopName;\nGLAD_API_CALL PFNGLPOPNAMEPROC glad_debug_glPopName;\n#define glPopName glad_debug_glPopName\nGLAD_API_CALL PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex;\nGLAD_API_CALL PFNGLPRIMITIVERESTARTINDEXPROC glad_debug_glPrimitiveRestartIndex;\n#define glPrimitiveRestartIndex glad_debug_glPrimitiveRestartIndex\nGLAD_API_CALL PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures;\nGLAD_API_CALL PFNGLPRIORITIZETEXTURESPROC glad_debug_glPrioritizeTextures;\n#define glPrioritizeTextures glad_debug_glPrioritizeTextures\nGLAD_API_CALL PFNGLPUSHATTRIBPROC glad_glPushAttrib;\nGLAD_API_CALL PFNGLPUSHATTRIBPROC glad_debug_glPushAttrib;\n#define glPushAttrib glad_debug_glPushAttrib\nGLAD_API_CALL PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib;\nGLAD_API_CALL PFNGLPUSHCLIENTATTRIBPROC glad_debug_glPushClientAttrib;\n#define glPushClientAttrib glad_debug_glPushClientAttrib\nGLAD_API_CALL PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup;\nGLAD_API_CALL PFNGLPUSHDEBUGGROUPPROC glad_debug_glPushDebugGroup;\n#define glPushDebugGroup glad_debug_glPushDebugGroup\nGLAD_API_CALL PFNGLPUSHMATRIXPROC glad_glPushMatrix;\nGLAD_API_CALL PFNGLPUSHMATRIXPROC glad_debug_glPushMatrix;\n#define glPushMatrix glad_debug_glPushMatrix\nGLAD_API_CALL PFNGLPUSHNAMEPROC glad_glPushName;\nGLAD_API_CALL PFNGLPUSHNAMEPROC glad_debug_glPushName;\n#define glPushName glad_debug_glPushName\nGLAD_API_CALL PFNGLRASTERPOS2DPROC glad_glRasterPos2d;\nGLAD_API_CALL PFNGLRASTERPOS2DPROC glad_debug_glRasterPos2d;\n#define glRasterPos2d glad_debug_glRasterPos2d\nGLAD_API_CALL PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv;\nGLAD_API_CALL PFNGLRASTERPOS2DVPROC glad_debug_glRasterPos2dv;\n#define glRasterPos2dv glad_debug_glRasterPos2dv\nGLAD_API_CALL PFNGLRASTERPOS2FPROC glad_glRasterPos2f;\nGLAD_API_CALL PFNGLRASTERPOS2FPROC glad_debug_glRasterPos2f;\n#define glRasterPos2f glad_debug_glRasterPos2f\nGLAD_API_CALL PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv;\nGLAD_API_CALL PFNGLRASTERPOS2FVPROC glad_debug_glRasterPos2fv;\n#define glRasterPos2fv glad_debug_glRasterPos2fv\nGLAD_API_CALL PFNGLRASTERPOS2IPROC glad_glRasterPos2i;\nGLAD_API_CALL PFNGLRASTERPOS2IPROC glad_debug_glRasterPos2i;\n#define glRasterPos2i glad_debug_glRasterPos2i\nGLAD_API_CALL PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv;\nGLAD_API_CALL PFNGLRASTERPOS2IVPROC glad_debug_glRasterPos2iv;\n#define glRasterPos2iv glad_debug_glRasterPos2iv\nGLAD_API_CALL PFNGLRASTERPOS2SPROC glad_glRasterPos2s;\nGLAD_API_CALL PFNGLRASTERPOS2SPROC glad_debug_glRasterPos2s;\n#define glRasterPos2s glad_debug_glRasterPos2s\nGLAD_API_CALL PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv;\nGLAD_API_CALL PFNGLRASTERPOS2SVPROC glad_debug_glRasterPos2sv;\n#define glRasterPos2sv glad_debug_glRasterPos2sv\nGLAD_API_CALL PFNGLRASTERPOS3DPROC glad_glRasterPos3d;\nGLAD_API_CALL PFNGLRASTERPOS3DPROC glad_debug_glRasterPos3d;\n#define glRasterPos3d glad_debug_glRasterPos3d\nGLAD_API_CALL PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv;\nGLAD_API_CALL PFNGLRASTERPOS3DVPROC glad_debug_glRasterPos3dv;\n#define glRasterPos3dv glad_debug_glRasterPos3dv\nGLAD_API_CALL PFNGLRASTERPOS3FPROC glad_glRasterPos3f;\nGLAD_API_CALL PFNGLRASTERPOS3FPROC glad_debug_glRasterPos3f;\n#define glRasterPos3f glad_debug_glRasterPos3f\nGLAD_API_CALL PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv;\nGLAD_API_CALL PFNGLRASTERPOS3FVPROC glad_debug_glRasterPos3fv;\n#define glRasterPos3fv glad_debug_glRasterPos3fv\nGLAD_API_CALL PFNGLRASTERPOS3IPROC glad_glRasterPos3i;\nGLAD_API_CALL PFNGLRASTERPOS3IPROC glad_debug_glRasterPos3i;\n#define glRasterPos3i glad_debug_glRasterPos3i\nGLAD_API_CALL PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv;\nGLAD_API_CALL PFNGLRASTERPOS3IVPROC glad_debug_glRasterPos3iv;\n#define glRasterPos3iv glad_debug_glRasterPos3iv\nGLAD_API_CALL PFNGLRASTERPOS3SPROC glad_glRasterPos3s;\nGLAD_API_CALL PFNGLRASTERPOS3SPROC glad_debug_glRasterPos3s;\n#define glRasterPos3s glad_debug_glRasterPos3s\nGLAD_API_CALL PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv;\nGLAD_API_CALL PFNGLRASTERPOS3SVPROC glad_debug_glRasterPos3sv;\n#define glRasterPos3sv glad_debug_glRasterPos3sv\nGLAD_API_CALL PFNGLRASTERPOS4DPROC glad_glRasterPos4d;\nGLAD_API_CALL PFNGLRASTERPOS4DPROC glad_debug_glRasterPos4d;\n#define glRasterPos4d glad_debug_glRasterPos4d\nGLAD_API_CALL PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv;\nGLAD_API_CALL PFNGLRASTERPOS4DVPROC glad_debug_glRasterPos4dv;\n#define glRasterPos4dv glad_debug_glRasterPos4dv\nGLAD_API_CALL PFNGLRASTERPOS4FPROC glad_glRasterPos4f;\nGLAD_API_CALL PFNGLRASTERPOS4FPROC glad_debug_glRasterPos4f;\n#define glRasterPos4f glad_debug_glRasterPos4f\nGLAD_API_CALL PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv;\nGLAD_API_CALL PFNGLRASTERPOS4FVPROC glad_debug_glRasterPos4fv;\n#define glRasterPos4fv glad_debug_glRasterPos4fv\nGLAD_API_CALL PFNGLRASTERPOS4IPROC glad_glRasterPos4i;\nGLAD_API_CALL PFNGLRASTERPOS4IPROC glad_debug_glRasterPos4i;\n#define glRasterPos4i glad_debug_glRasterPos4i\nGLAD_API_CALL PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv;\nGLAD_API_CALL PFNGLRASTERPOS4IVPROC glad_debug_glRasterPos4iv;\n#define glRasterPos4iv glad_debug_glRasterPos4iv\nGLAD_API_CALL PFNGLRASTERPOS4SPROC glad_glRasterPos4s;\nGLAD_API_CALL PFNGLRASTERPOS4SPROC glad_debug_glRasterPos4s;\n#define glRasterPos4s glad_debug_glRasterPos4s\nGLAD_API_CALL PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv;\nGLAD_API_CALL PFNGLRASTERPOS4SVPROC glad_debug_glRasterPos4sv;\n#define glRasterPos4sv glad_debug_glRasterPos4sv\nGLAD_API_CALL PFNGLREADBUFFERPROC glad_glReadBuffer;\nGLAD_API_CALL PFNGLREADBUFFERPROC glad_debug_glReadBuffer;\n#define glReadBuffer glad_debug_glReadBuffer\nGLAD_API_CALL PFNGLREADPIXELSPROC glad_glReadPixels;\nGLAD_API_CALL PFNGLREADPIXELSPROC glad_debug_glReadPixels;\n#define glReadPixels glad_debug_glReadPixels\nGLAD_API_CALL PFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB;\nGLAD_API_CALL PFNGLREADNPIXELSARBPROC glad_debug_glReadnPixelsARB;\n#define glReadnPixelsARB glad_debug_glReadnPixelsARB\nGLAD_API_CALL PFNGLRECTDPROC glad_glRectd;\nGLAD_API_CALL PFNGLRECTDPROC glad_debug_glRectd;\n#define glRectd glad_debug_glRectd\nGLAD_API_CALL PFNGLRECTDVPROC glad_glRectdv;\nGLAD_API_CALL PFNGLRECTDVPROC glad_debug_glRectdv;\n#define glRectdv glad_debug_glRectdv\nGLAD_API_CALL PFNGLRECTFPROC glad_glRectf;\nGLAD_API_CALL PFNGLRECTFPROC glad_debug_glRectf;\n#define glRectf glad_debug_glRectf\nGLAD_API_CALL PFNGLRECTFVPROC glad_glRectfv;\nGLAD_API_CALL PFNGLRECTFVPROC glad_debug_glRectfv;\n#define glRectfv glad_debug_glRectfv\nGLAD_API_CALL PFNGLRECTIPROC glad_glRecti;\nGLAD_API_CALL PFNGLRECTIPROC glad_debug_glRecti;\n#define glRecti glad_debug_glRecti\nGLAD_API_CALL PFNGLRECTIVPROC glad_glRectiv;\nGLAD_API_CALL PFNGLRECTIVPROC glad_debug_glRectiv;\n#define glRectiv glad_debug_glRectiv\nGLAD_API_CALL PFNGLRECTSPROC glad_glRects;\nGLAD_API_CALL PFNGLRECTSPROC glad_debug_glRects;\n#define glRects glad_debug_glRects\nGLAD_API_CALL PFNGLRECTSVPROC glad_glRectsv;\nGLAD_API_CALL PFNGLRECTSVPROC glad_debug_glRectsv;\n#define glRectsv glad_debug_glRectsv\nGLAD_API_CALL PFNGLRENDERMODEPROC glad_glRenderMode;\nGLAD_API_CALL PFNGLRENDERMODEPROC glad_debug_glRenderMode;\n#define glRenderMode glad_debug_glRenderMode\nGLAD_API_CALL PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;\nGLAD_API_CALL PFNGLRENDERBUFFERSTORAGEPROC glad_debug_glRenderbufferStorage;\n#define glRenderbufferStorage glad_debug_glRenderbufferStorage\nGLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample;\nGLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_debug_glRenderbufferStorageMultisample;\n#define glRenderbufferStorageMultisample glad_debug_glRenderbufferStorageMultisample\nGLAD_API_CALL PFNGLROTATEDPROC glad_glRotated;\nGLAD_API_CALL PFNGLROTATEDPROC glad_debug_glRotated;\n#define glRotated glad_debug_glRotated\nGLAD_API_CALL PFNGLROTATEFPROC glad_glRotatef;\nGLAD_API_CALL PFNGLROTATEFPROC glad_debug_glRotatef;\n#define glRotatef glad_debug_glRotatef\nGLAD_API_CALL PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;\nGLAD_API_CALL PFNGLSAMPLECOVERAGEPROC glad_debug_glSampleCoverage;\n#define glSampleCoverage glad_debug_glSampleCoverage\nGLAD_API_CALL PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB;\nGLAD_API_CALL PFNGLSAMPLECOVERAGEARBPROC glad_debug_glSampleCoverageARB;\n#define glSampleCoverageARB glad_debug_glSampleCoverageARB\nGLAD_API_CALL PFNGLSCALEDPROC glad_glScaled;\nGLAD_API_CALL PFNGLSCALEDPROC glad_debug_glScaled;\n#define glScaled glad_debug_glScaled\nGLAD_API_CALL PFNGLSCALEFPROC glad_glScalef;\nGLAD_API_CALL PFNGLSCALEFPROC glad_debug_glScalef;\n#define glScalef glad_debug_glScalef\nGLAD_API_CALL PFNGLSCISSORPROC glad_glScissor;\nGLAD_API_CALL PFNGLSCISSORPROC glad_debug_glScissor;\n#define glScissor glad_debug_glScissor\nGLAD_API_CALL PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3BPROC glad_debug_glSecondaryColor3b;\n#define glSecondaryColor3b glad_debug_glSecondaryColor3b\nGLAD_API_CALL PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3BVPROC glad_debug_glSecondaryColor3bv;\n#define glSecondaryColor3bv glad_debug_glSecondaryColor3bv\nGLAD_API_CALL PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3DPROC glad_debug_glSecondaryColor3d;\n#define glSecondaryColor3d glad_debug_glSecondaryColor3d\nGLAD_API_CALL PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3DVPROC glad_debug_glSecondaryColor3dv;\n#define glSecondaryColor3dv glad_debug_glSecondaryColor3dv\nGLAD_API_CALL PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3FPROC glad_debug_glSecondaryColor3f;\n#define glSecondaryColor3f glad_debug_glSecondaryColor3f\nGLAD_API_CALL PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3FVPROC glad_debug_glSecondaryColor3fv;\n#define glSecondaryColor3fv glad_debug_glSecondaryColor3fv\nGLAD_API_CALL PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3IPROC glad_debug_glSecondaryColor3i;\n#define glSecondaryColor3i glad_debug_glSecondaryColor3i\nGLAD_API_CALL PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3IVPROC glad_debug_glSecondaryColor3iv;\n#define glSecondaryColor3iv glad_debug_glSecondaryColor3iv\nGLAD_API_CALL PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3SPROC glad_debug_glSecondaryColor3s;\n#define glSecondaryColor3s glad_debug_glSecondaryColor3s\nGLAD_API_CALL PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3SVPROC glad_debug_glSecondaryColor3sv;\n#define glSecondaryColor3sv glad_debug_glSecondaryColor3sv\nGLAD_API_CALL PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3UBPROC glad_debug_glSecondaryColor3ub;\n#define glSecondaryColor3ub glad_debug_glSecondaryColor3ub\nGLAD_API_CALL PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3UBVPROC glad_debug_glSecondaryColor3ubv;\n#define glSecondaryColor3ubv glad_debug_glSecondaryColor3ubv\nGLAD_API_CALL PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3UIPROC glad_debug_glSecondaryColor3ui;\n#define glSecondaryColor3ui glad_debug_glSecondaryColor3ui\nGLAD_API_CALL PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3UIVPROC glad_debug_glSecondaryColor3uiv;\n#define glSecondaryColor3uiv glad_debug_glSecondaryColor3uiv\nGLAD_API_CALL PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3USPROC glad_debug_glSecondaryColor3us;\n#define glSecondaryColor3us glad_debug_glSecondaryColor3us\nGLAD_API_CALL PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv;\nGLAD_API_CALL PFNGLSECONDARYCOLOR3USVPROC glad_debug_glSecondaryColor3usv;\n#define glSecondaryColor3usv glad_debug_glSecondaryColor3usv\nGLAD_API_CALL PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer;\nGLAD_API_CALL PFNGLSECONDARYCOLORPOINTERPROC glad_debug_glSecondaryColorPointer;\n#define glSecondaryColorPointer glad_debug_glSecondaryColorPointer\nGLAD_API_CALL PFNGLSELECTBUFFERPROC glad_glSelectBuffer;\nGLAD_API_CALL PFNGLSELECTBUFFERPROC glad_debug_glSelectBuffer;\n#define glSelectBuffer glad_debug_glSelectBuffer\nGLAD_API_CALL PFNGLSHADEMODELPROC glad_glShadeModel;\nGLAD_API_CALL PFNGLSHADEMODELPROC glad_debug_glShadeModel;\n#define glShadeModel glad_debug_glShadeModel\nGLAD_API_CALL PFNGLSHADERSOURCEPROC glad_glShaderSource;\nGLAD_API_CALL PFNGLSHADERSOURCEPROC glad_debug_glShaderSource;\n#define glShaderSource glad_debug_glShaderSource\nGLAD_API_CALL PFNGLSTENCILFUNCPROC glad_glStencilFunc;\nGLAD_API_CALL PFNGLSTENCILFUNCPROC glad_debug_glStencilFunc;\n#define glStencilFunc glad_debug_glStencilFunc\nGLAD_API_CALL PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;\nGLAD_API_CALL PFNGLSTENCILFUNCSEPARATEPROC glad_debug_glStencilFuncSeparate;\n#define glStencilFuncSeparate glad_debug_glStencilFuncSeparate\nGLAD_API_CALL PFNGLSTENCILMASKPROC glad_glStencilMask;\nGLAD_API_CALL PFNGLSTENCILMASKPROC glad_debug_glStencilMask;\n#define glStencilMask glad_debug_glStencilMask\nGLAD_API_CALL PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;\nGLAD_API_CALL PFNGLSTENCILMASKSEPARATEPROC glad_debug_glStencilMaskSeparate;\n#define glStencilMaskSeparate glad_debug_glStencilMaskSeparate\nGLAD_API_CALL PFNGLSTENCILOPPROC glad_glStencilOp;\nGLAD_API_CALL PFNGLSTENCILOPPROC glad_debug_glStencilOp;\n#define glStencilOp glad_debug_glStencilOp\nGLAD_API_CALL PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;\nGLAD_API_CALL PFNGLSTENCILOPSEPARATEPROC glad_debug_glStencilOpSeparate;\n#define glStencilOpSeparate glad_debug_glStencilOpSeparate\nGLAD_API_CALL PFNGLTEXBUFFERPROC glad_glTexBuffer;\nGLAD_API_CALL PFNGLTEXBUFFERPROC glad_debug_glTexBuffer;\n#define glTexBuffer glad_debug_glTexBuffer\nGLAD_API_CALL PFNGLTEXCOORD1DPROC glad_glTexCoord1d;\nGLAD_API_CALL PFNGLTEXCOORD1DPROC glad_debug_glTexCoord1d;\n#define glTexCoord1d glad_debug_glTexCoord1d\nGLAD_API_CALL PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv;\nGLAD_API_CALL PFNGLTEXCOORD1DVPROC glad_debug_glTexCoord1dv;\n#define glTexCoord1dv glad_debug_glTexCoord1dv\nGLAD_API_CALL PFNGLTEXCOORD1FPROC glad_glTexCoord1f;\nGLAD_API_CALL PFNGLTEXCOORD1FPROC glad_debug_glTexCoord1f;\n#define glTexCoord1f glad_debug_glTexCoord1f\nGLAD_API_CALL PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv;\nGLAD_API_CALL PFNGLTEXCOORD1FVPROC glad_debug_glTexCoord1fv;\n#define glTexCoord1fv glad_debug_glTexCoord1fv\nGLAD_API_CALL PFNGLTEXCOORD1IPROC glad_glTexCoord1i;\nGLAD_API_CALL PFNGLTEXCOORD1IPROC glad_debug_glTexCoord1i;\n#define glTexCoord1i glad_debug_glTexCoord1i\nGLAD_API_CALL PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv;\nGLAD_API_CALL PFNGLTEXCOORD1IVPROC glad_debug_glTexCoord1iv;\n#define glTexCoord1iv glad_debug_glTexCoord1iv\nGLAD_API_CALL PFNGLTEXCOORD1SPROC glad_glTexCoord1s;\nGLAD_API_CALL PFNGLTEXCOORD1SPROC glad_debug_glTexCoord1s;\n#define glTexCoord1s glad_debug_glTexCoord1s\nGLAD_API_CALL PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv;\nGLAD_API_CALL PFNGLTEXCOORD1SVPROC glad_debug_glTexCoord1sv;\n#define glTexCoord1sv glad_debug_glTexCoord1sv\nGLAD_API_CALL PFNGLTEXCOORD2DPROC glad_glTexCoord2d;\nGLAD_API_CALL PFNGLTEXCOORD2DPROC glad_debug_glTexCoord2d;\n#define glTexCoord2d glad_debug_glTexCoord2d\nGLAD_API_CALL PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv;\nGLAD_API_CALL PFNGLTEXCOORD2DVPROC glad_debug_glTexCoord2dv;\n#define glTexCoord2dv glad_debug_glTexCoord2dv\nGLAD_API_CALL PFNGLTEXCOORD2FPROC glad_glTexCoord2f;\nGLAD_API_CALL PFNGLTEXCOORD2FPROC glad_debug_glTexCoord2f;\n#define glTexCoord2f glad_debug_glTexCoord2f\nGLAD_API_CALL PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv;\nGLAD_API_CALL PFNGLTEXCOORD2FVPROC glad_debug_glTexCoord2fv;\n#define glTexCoord2fv glad_debug_glTexCoord2fv\nGLAD_API_CALL PFNGLTEXCOORD2IPROC glad_glTexCoord2i;\nGLAD_API_CALL PFNGLTEXCOORD2IPROC glad_debug_glTexCoord2i;\n#define glTexCoord2i glad_debug_glTexCoord2i\nGLAD_API_CALL PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv;\nGLAD_API_CALL PFNGLTEXCOORD2IVPROC glad_debug_glTexCoord2iv;\n#define glTexCoord2iv glad_debug_glTexCoord2iv\nGLAD_API_CALL PFNGLTEXCOORD2SPROC glad_glTexCoord2s;\nGLAD_API_CALL PFNGLTEXCOORD2SPROC glad_debug_glTexCoord2s;\n#define glTexCoord2s glad_debug_glTexCoord2s\nGLAD_API_CALL PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv;\nGLAD_API_CALL PFNGLTEXCOORD2SVPROC glad_debug_glTexCoord2sv;\n#define glTexCoord2sv glad_debug_glTexCoord2sv\nGLAD_API_CALL PFNGLTEXCOORD3DPROC glad_glTexCoord3d;\nGLAD_API_CALL PFNGLTEXCOORD3DPROC glad_debug_glTexCoord3d;\n#define glTexCoord3d glad_debug_glTexCoord3d\nGLAD_API_CALL PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv;\nGLAD_API_CALL PFNGLTEXCOORD3DVPROC glad_debug_glTexCoord3dv;\n#define glTexCoord3dv glad_debug_glTexCoord3dv\nGLAD_API_CALL PFNGLTEXCOORD3FPROC glad_glTexCoord3f;\nGLAD_API_CALL PFNGLTEXCOORD3FPROC glad_debug_glTexCoord3f;\n#define glTexCoord3f glad_debug_glTexCoord3f\nGLAD_API_CALL PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv;\nGLAD_API_CALL PFNGLTEXCOORD3FVPROC glad_debug_glTexCoord3fv;\n#define glTexCoord3fv glad_debug_glTexCoord3fv\nGLAD_API_CALL PFNGLTEXCOORD3IPROC glad_glTexCoord3i;\nGLAD_API_CALL PFNGLTEXCOORD3IPROC glad_debug_glTexCoord3i;\n#define glTexCoord3i glad_debug_glTexCoord3i\nGLAD_API_CALL PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv;\nGLAD_API_CALL PFNGLTEXCOORD3IVPROC glad_debug_glTexCoord3iv;\n#define glTexCoord3iv glad_debug_glTexCoord3iv\nGLAD_API_CALL PFNGLTEXCOORD3SPROC glad_glTexCoord3s;\nGLAD_API_CALL PFNGLTEXCOORD3SPROC glad_debug_glTexCoord3s;\n#define glTexCoord3s glad_debug_glTexCoord3s\nGLAD_API_CALL PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv;\nGLAD_API_CALL PFNGLTEXCOORD3SVPROC glad_debug_glTexCoord3sv;\n#define glTexCoord3sv glad_debug_glTexCoord3sv\nGLAD_API_CALL PFNGLTEXCOORD4DPROC glad_glTexCoord4d;\nGLAD_API_CALL PFNGLTEXCOORD4DPROC glad_debug_glTexCoord4d;\n#define glTexCoord4d glad_debug_glTexCoord4d\nGLAD_API_CALL PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv;\nGLAD_API_CALL PFNGLTEXCOORD4DVPROC glad_debug_glTexCoord4dv;\n#define glTexCoord4dv glad_debug_glTexCoord4dv\nGLAD_API_CALL PFNGLTEXCOORD4FPROC glad_glTexCoord4f;\nGLAD_API_CALL PFNGLTEXCOORD4FPROC glad_debug_glTexCoord4f;\n#define glTexCoord4f glad_debug_glTexCoord4f\nGLAD_API_CALL PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv;\nGLAD_API_CALL PFNGLTEXCOORD4FVPROC glad_debug_glTexCoord4fv;\n#define glTexCoord4fv glad_debug_glTexCoord4fv\nGLAD_API_CALL PFNGLTEXCOORD4IPROC glad_glTexCoord4i;\nGLAD_API_CALL PFNGLTEXCOORD4IPROC glad_debug_glTexCoord4i;\n#define glTexCoord4i glad_debug_glTexCoord4i\nGLAD_API_CALL PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv;\nGLAD_API_CALL PFNGLTEXCOORD4IVPROC glad_debug_glTexCoord4iv;\n#define glTexCoord4iv glad_debug_glTexCoord4iv\nGLAD_API_CALL PFNGLTEXCOORD4SPROC glad_glTexCoord4s;\nGLAD_API_CALL PFNGLTEXCOORD4SPROC glad_debug_glTexCoord4s;\n#define glTexCoord4s glad_debug_glTexCoord4s\nGLAD_API_CALL PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv;\nGLAD_API_CALL PFNGLTEXCOORD4SVPROC glad_debug_glTexCoord4sv;\n#define glTexCoord4sv glad_debug_glTexCoord4sv\nGLAD_API_CALL PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer;\nGLAD_API_CALL PFNGLTEXCOORDPOINTERPROC glad_debug_glTexCoordPointer;\n#define glTexCoordPointer glad_debug_glTexCoordPointer\nGLAD_API_CALL PFNGLTEXENVFPROC glad_glTexEnvf;\nGLAD_API_CALL PFNGLTEXENVFPROC glad_debug_glTexEnvf;\n#define glTexEnvf glad_debug_glTexEnvf\nGLAD_API_CALL PFNGLTEXENVFVPROC glad_glTexEnvfv;\nGLAD_API_CALL PFNGLTEXENVFVPROC glad_debug_glTexEnvfv;\n#define glTexEnvfv glad_debug_glTexEnvfv\nGLAD_API_CALL PFNGLTEXENVIPROC glad_glTexEnvi;\nGLAD_API_CALL PFNGLTEXENVIPROC glad_debug_glTexEnvi;\n#define glTexEnvi glad_debug_glTexEnvi\nGLAD_API_CALL PFNGLTEXENVIVPROC glad_glTexEnviv;\nGLAD_API_CALL PFNGLTEXENVIVPROC glad_debug_glTexEnviv;\n#define glTexEnviv glad_debug_glTexEnviv\nGLAD_API_CALL PFNGLTEXGENDPROC glad_glTexGend;\nGLAD_API_CALL PFNGLTEXGENDPROC glad_debug_glTexGend;\n#define glTexGend glad_debug_glTexGend\nGLAD_API_CALL PFNGLTEXGENDVPROC glad_glTexGendv;\nGLAD_API_CALL PFNGLTEXGENDVPROC glad_debug_glTexGendv;\n#define glTexGendv glad_debug_glTexGendv\nGLAD_API_CALL PFNGLTEXGENFPROC glad_glTexGenf;\nGLAD_API_CALL PFNGLTEXGENFPROC glad_debug_glTexGenf;\n#define glTexGenf glad_debug_glTexGenf\nGLAD_API_CALL PFNGLTEXGENFVPROC glad_glTexGenfv;\nGLAD_API_CALL PFNGLTEXGENFVPROC glad_debug_glTexGenfv;\n#define glTexGenfv glad_debug_glTexGenfv\nGLAD_API_CALL PFNGLTEXGENIPROC glad_glTexGeni;\nGLAD_API_CALL PFNGLTEXGENIPROC glad_debug_glTexGeni;\n#define glTexGeni glad_debug_glTexGeni\nGLAD_API_CALL PFNGLTEXGENIVPROC glad_glTexGeniv;\nGLAD_API_CALL PFNGLTEXGENIVPROC glad_debug_glTexGeniv;\n#define glTexGeniv glad_debug_glTexGeniv\nGLAD_API_CALL PFNGLTEXIMAGE1DPROC glad_glTexImage1D;\nGLAD_API_CALL PFNGLTEXIMAGE1DPROC glad_debug_glTexImage1D;\n#define glTexImage1D glad_debug_glTexImage1D\nGLAD_API_CALL PFNGLTEXIMAGE2DPROC glad_glTexImage2D;\nGLAD_API_CALL PFNGLTEXIMAGE2DPROC glad_debug_glTexImage2D;\n#define glTexImage2D glad_debug_glTexImage2D\nGLAD_API_CALL PFNGLTEXIMAGE3DPROC glad_glTexImage3D;\nGLAD_API_CALL PFNGLTEXIMAGE3DPROC glad_debug_glTexImage3D;\n#define glTexImage3D glad_debug_glTexImage3D\nGLAD_API_CALL PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv;\nGLAD_API_CALL PFNGLTEXPARAMETERIIVPROC glad_debug_glTexParameterIiv;\n#define glTexParameterIiv glad_debug_glTexParameterIiv\nGLAD_API_CALL PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv;\nGLAD_API_CALL PFNGLTEXPARAMETERIUIVPROC glad_debug_glTexParameterIuiv;\n#define glTexParameterIuiv glad_debug_glTexParameterIuiv\nGLAD_API_CALL PFNGLTEXPARAMETERFPROC glad_glTexParameterf;\nGLAD_API_CALL PFNGLTEXPARAMETERFPROC glad_debug_glTexParameterf;\n#define glTexParameterf glad_debug_glTexParameterf\nGLAD_API_CALL PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;\nGLAD_API_CALL PFNGLTEXPARAMETERFVPROC glad_debug_glTexParameterfv;\n#define glTexParameterfv glad_debug_glTexParameterfv\nGLAD_API_CALL PFNGLTEXPARAMETERIPROC glad_glTexParameteri;\nGLAD_API_CALL PFNGLTEXPARAMETERIPROC glad_debug_glTexParameteri;\n#define glTexParameteri glad_debug_glTexParameteri\nGLAD_API_CALL PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;\nGLAD_API_CALL PFNGLTEXPARAMETERIVPROC glad_debug_glTexParameteriv;\n#define glTexParameteriv glad_debug_glTexParameteriv\nGLAD_API_CALL PFNGLTEXSTORAGE1DPROC glad_glTexStorage1D;\nGLAD_API_CALL PFNGLTEXSTORAGE1DPROC glad_debug_glTexStorage1D;\n#define glTexStorage1D glad_debug_glTexStorage1D\nGLAD_API_CALL PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D;\nGLAD_API_CALL PFNGLTEXSTORAGE2DPROC glad_debug_glTexStorage2D;\n#define glTexStorage2D glad_debug_glTexStorage2D\nGLAD_API_CALL PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D;\nGLAD_API_CALL PFNGLTEXSTORAGE3DPROC glad_debug_glTexStorage3D;\n#define glTexStorage3D glad_debug_glTexStorage3D\nGLAD_API_CALL PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D;\nGLAD_API_CALL PFNGLTEXSUBIMAGE1DPROC glad_debug_glTexSubImage1D;\n#define glTexSubImage1D glad_debug_glTexSubImage1D\nGLAD_API_CALL PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;\nGLAD_API_CALL PFNGLTEXSUBIMAGE2DPROC glad_debug_glTexSubImage2D;\n#define glTexSubImage2D glad_debug_glTexSubImage2D\nGLAD_API_CALL PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D;\nGLAD_API_CALL PFNGLTEXSUBIMAGE3DPROC glad_debug_glTexSubImage3D;\n#define glTexSubImage3D glad_debug_glTexSubImage3D\nGLAD_API_CALL PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings;\nGLAD_API_CALL PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_debug_glTransformFeedbackVaryings;\n#define glTransformFeedbackVaryings glad_debug_glTransformFeedbackVaryings\nGLAD_API_CALL PFNGLTRANSLATEDPROC glad_glTranslated;\nGLAD_API_CALL PFNGLTRANSLATEDPROC glad_debug_glTranslated;\n#define glTranslated glad_debug_glTranslated\nGLAD_API_CALL PFNGLTRANSLATEFPROC glad_glTranslatef;\nGLAD_API_CALL PFNGLTRANSLATEFPROC glad_debug_glTranslatef;\n#define glTranslatef glad_debug_glTranslatef\nGLAD_API_CALL PFNGLUNIFORM1FPROC glad_glUniform1f;\nGLAD_API_CALL PFNGLUNIFORM1FPROC glad_debug_glUniform1f;\n#define glUniform1f glad_debug_glUniform1f\nGLAD_API_CALL PFNGLUNIFORM1FVPROC glad_glUniform1fv;\nGLAD_API_CALL PFNGLUNIFORM1FVPROC glad_debug_glUniform1fv;\n#define glUniform1fv glad_debug_glUniform1fv\nGLAD_API_CALL PFNGLUNIFORM1IPROC glad_glUniform1i;\nGLAD_API_CALL PFNGLUNIFORM1IPROC glad_debug_glUniform1i;\n#define glUniform1i glad_debug_glUniform1i\nGLAD_API_CALL PFNGLUNIFORM1IVPROC glad_glUniform1iv;\nGLAD_API_CALL PFNGLUNIFORM1IVPROC glad_debug_glUniform1iv;\n#define glUniform1iv glad_debug_glUniform1iv\nGLAD_API_CALL PFNGLUNIFORM1UIPROC glad_glUniform1ui;\nGLAD_API_CALL PFNGLUNIFORM1UIPROC glad_debug_glUniform1ui;\n#define glUniform1ui glad_debug_glUniform1ui\nGLAD_API_CALL PFNGLUNIFORM1UIVPROC glad_glUniform1uiv;\nGLAD_API_CALL PFNGLUNIFORM1UIVPROC glad_debug_glUniform1uiv;\n#define glUniform1uiv glad_debug_glUniform1uiv\nGLAD_API_CALL PFNGLUNIFORM2FPROC glad_glUniform2f;\nGLAD_API_CALL PFNGLUNIFORM2FPROC glad_debug_glUniform2f;\n#define glUniform2f glad_debug_glUniform2f\nGLAD_API_CALL PFNGLUNIFORM2FVPROC glad_glUniform2fv;\nGLAD_API_CALL PFNGLUNIFORM2FVPROC glad_debug_glUniform2fv;\n#define glUniform2fv glad_debug_glUniform2fv\nGLAD_API_CALL PFNGLUNIFORM2IPROC glad_glUniform2i;\nGLAD_API_CALL PFNGLUNIFORM2IPROC glad_debug_glUniform2i;\n#define glUniform2i glad_debug_glUniform2i\nGLAD_API_CALL PFNGLUNIFORM2IVPROC glad_glUniform2iv;\nGLAD_API_CALL PFNGLUNIFORM2IVPROC glad_debug_glUniform2iv;\n#define glUniform2iv glad_debug_glUniform2iv\nGLAD_API_CALL PFNGLUNIFORM2UIPROC glad_glUniform2ui;\nGLAD_API_CALL PFNGLUNIFORM2UIPROC glad_debug_glUniform2ui;\n#define glUniform2ui glad_debug_glUniform2ui\nGLAD_API_CALL PFNGLUNIFORM2UIVPROC glad_glUniform2uiv;\nGLAD_API_CALL PFNGLUNIFORM2UIVPROC glad_debug_glUniform2uiv;\n#define glUniform2uiv glad_debug_glUniform2uiv\nGLAD_API_CALL PFNGLUNIFORM3FPROC glad_glUniform3f;\nGLAD_API_CALL PFNGLUNIFORM3FPROC glad_debug_glUniform3f;\n#define glUniform3f glad_debug_glUniform3f\nGLAD_API_CALL PFNGLUNIFORM3FVPROC glad_glUniform3fv;\nGLAD_API_CALL PFNGLUNIFORM3FVPROC glad_debug_glUniform3fv;\n#define glUniform3fv glad_debug_glUniform3fv\nGLAD_API_CALL PFNGLUNIFORM3IPROC glad_glUniform3i;\nGLAD_API_CALL PFNGLUNIFORM3IPROC glad_debug_glUniform3i;\n#define glUniform3i glad_debug_glUniform3i\nGLAD_API_CALL PFNGLUNIFORM3IVPROC glad_glUniform3iv;\nGLAD_API_CALL PFNGLUNIFORM3IVPROC glad_debug_glUniform3iv;\n#define glUniform3iv glad_debug_glUniform3iv\nGLAD_API_CALL PFNGLUNIFORM3UIPROC glad_glUniform3ui;\nGLAD_API_CALL PFNGLUNIFORM3UIPROC glad_debug_glUniform3ui;\n#define glUniform3ui glad_debug_glUniform3ui\nGLAD_API_CALL PFNGLUNIFORM3UIVPROC glad_glUniform3uiv;\nGLAD_API_CALL PFNGLUNIFORM3UIVPROC glad_debug_glUniform3uiv;\n#define glUniform3uiv glad_debug_glUniform3uiv\nGLAD_API_CALL PFNGLUNIFORM4FPROC glad_glUniform4f;\nGLAD_API_CALL PFNGLUNIFORM4FPROC glad_debug_glUniform4f;\n#define glUniform4f glad_debug_glUniform4f\nGLAD_API_CALL PFNGLUNIFORM4FVPROC glad_glUniform4fv;\nGLAD_API_CALL PFNGLUNIFORM4FVPROC glad_debug_glUniform4fv;\n#define glUniform4fv glad_debug_glUniform4fv\nGLAD_API_CALL PFNGLUNIFORM4IPROC glad_glUniform4i;\nGLAD_API_CALL PFNGLUNIFORM4IPROC glad_debug_glUniform4i;\n#define glUniform4i glad_debug_glUniform4i\nGLAD_API_CALL PFNGLUNIFORM4IVPROC glad_glUniform4iv;\nGLAD_API_CALL PFNGLUNIFORM4IVPROC glad_debug_glUniform4iv;\n#define glUniform4iv glad_debug_glUniform4iv\nGLAD_API_CALL PFNGLUNIFORM4UIPROC glad_glUniform4ui;\nGLAD_API_CALL PFNGLUNIFORM4UIPROC glad_debug_glUniform4ui;\n#define glUniform4ui glad_debug_glUniform4ui\nGLAD_API_CALL PFNGLUNIFORM4UIVPROC glad_glUniform4uiv;\nGLAD_API_CALL PFNGLUNIFORM4UIVPROC glad_debug_glUniform4uiv;\n#define glUniform4uiv glad_debug_glUniform4uiv\nGLAD_API_CALL PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding;\nGLAD_API_CALL PFNGLUNIFORMBLOCKBINDINGPROC glad_debug_glUniformBlockBinding;\n#define glUniformBlockBinding glad_debug_glUniformBlockBinding\nGLAD_API_CALL PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX2FVPROC glad_debug_glUniformMatrix2fv;\n#define glUniformMatrix2fv glad_debug_glUniformMatrix2fv\nGLAD_API_CALL PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX2X3FVPROC glad_debug_glUniformMatrix2x3fv;\n#define glUniformMatrix2x3fv glad_debug_glUniformMatrix2x3fv\nGLAD_API_CALL PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX2X4FVPROC glad_debug_glUniformMatrix2x4fv;\n#define glUniformMatrix2x4fv glad_debug_glUniformMatrix2x4fv\nGLAD_API_CALL PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX3FVPROC glad_debug_glUniformMatrix3fv;\n#define glUniformMatrix3fv glad_debug_glUniformMatrix3fv\nGLAD_API_CALL PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX3X2FVPROC glad_debug_glUniformMatrix3x2fv;\n#define glUniformMatrix3x2fv glad_debug_glUniformMatrix3x2fv\nGLAD_API_CALL PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX3X4FVPROC glad_debug_glUniformMatrix3x4fv;\n#define glUniformMatrix3x4fv glad_debug_glUniformMatrix3x4fv\nGLAD_API_CALL PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX4FVPROC glad_debug_glUniformMatrix4fv;\n#define glUniformMatrix4fv glad_debug_glUniformMatrix4fv\nGLAD_API_CALL PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX4X2FVPROC glad_debug_glUniformMatrix4x2fv;\n#define glUniformMatrix4x2fv glad_debug_glUniformMatrix4x2fv\nGLAD_API_CALL PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv;\nGLAD_API_CALL PFNGLUNIFORMMATRIX4X3FVPROC glad_debug_glUniformMatrix4x3fv;\n#define glUniformMatrix4x3fv glad_debug_glUniformMatrix4x3fv\nGLAD_API_CALL PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer;\nGLAD_API_CALL PFNGLUNMAPBUFFERPROC glad_debug_glUnmapBuffer;\n#define glUnmapBuffer glad_debug_glUnmapBuffer\nGLAD_API_CALL PFNGLUSEPROGRAMPROC glad_glUseProgram;\nGLAD_API_CALL PFNGLUSEPROGRAMPROC glad_debug_glUseProgram;\n#define glUseProgram glad_debug_glUseProgram\nGLAD_API_CALL PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;\nGLAD_API_CALL PFNGLVALIDATEPROGRAMPROC glad_debug_glValidateProgram;\n#define glValidateProgram glad_debug_glValidateProgram\nGLAD_API_CALL PFNGLVERTEX2DPROC glad_glVertex2d;\nGLAD_API_CALL PFNGLVERTEX2DPROC glad_debug_glVertex2d;\n#define glVertex2d glad_debug_glVertex2d\nGLAD_API_CALL PFNGLVERTEX2DVPROC glad_glVertex2dv;\nGLAD_API_CALL PFNGLVERTEX2DVPROC glad_debug_glVertex2dv;\n#define glVertex2dv glad_debug_glVertex2dv\nGLAD_API_CALL PFNGLVERTEX2FPROC glad_glVertex2f;\nGLAD_API_CALL PFNGLVERTEX2FPROC glad_debug_glVertex2f;\n#define glVertex2f glad_debug_glVertex2f\nGLAD_API_CALL PFNGLVERTEX2FVPROC glad_glVertex2fv;\nGLAD_API_CALL PFNGLVERTEX2FVPROC glad_debug_glVertex2fv;\n#define glVertex2fv glad_debug_glVertex2fv\nGLAD_API_CALL PFNGLVERTEX2IPROC glad_glVertex2i;\nGLAD_API_CALL PFNGLVERTEX2IPROC glad_debug_glVertex2i;\n#define glVertex2i glad_debug_glVertex2i\nGLAD_API_CALL PFNGLVERTEX2IVPROC glad_glVertex2iv;\nGLAD_API_CALL PFNGLVERTEX2IVPROC glad_debug_glVertex2iv;\n#define glVertex2iv glad_debug_glVertex2iv\nGLAD_API_CALL PFNGLVERTEX2SPROC glad_glVertex2s;\nGLAD_API_CALL PFNGLVERTEX2SPROC glad_debug_glVertex2s;\n#define glVertex2s glad_debug_glVertex2s\nGLAD_API_CALL PFNGLVERTEX2SVPROC glad_glVertex2sv;\nGLAD_API_CALL PFNGLVERTEX2SVPROC glad_debug_glVertex2sv;\n#define glVertex2sv glad_debug_glVertex2sv\nGLAD_API_CALL PFNGLVERTEX3DPROC glad_glVertex3d;\nGLAD_API_CALL PFNGLVERTEX3DPROC glad_debug_glVertex3d;\n#define glVertex3d glad_debug_glVertex3d\nGLAD_API_CALL PFNGLVERTEX3DVPROC glad_glVertex3dv;\nGLAD_API_CALL PFNGLVERTEX3DVPROC glad_debug_glVertex3dv;\n#define glVertex3dv glad_debug_glVertex3dv\nGLAD_API_CALL PFNGLVERTEX3FPROC glad_glVertex3f;\nGLAD_API_CALL PFNGLVERTEX3FPROC glad_debug_glVertex3f;\n#define glVertex3f glad_debug_glVertex3f\nGLAD_API_CALL PFNGLVERTEX3FVPROC glad_glVertex3fv;\nGLAD_API_CALL PFNGLVERTEX3FVPROC glad_debug_glVertex3fv;\n#define glVertex3fv glad_debug_glVertex3fv\nGLAD_API_CALL PFNGLVERTEX3IPROC glad_glVertex3i;\nGLAD_API_CALL PFNGLVERTEX3IPROC glad_debug_glVertex3i;\n#define glVertex3i glad_debug_glVertex3i\nGLAD_API_CALL PFNGLVERTEX3IVPROC glad_glVertex3iv;\nGLAD_API_CALL PFNGLVERTEX3IVPROC glad_debug_glVertex3iv;\n#define glVertex3iv glad_debug_glVertex3iv\nGLAD_API_CALL PFNGLVERTEX3SPROC glad_glVertex3s;\nGLAD_API_CALL PFNGLVERTEX3SPROC glad_debug_glVertex3s;\n#define glVertex3s glad_debug_glVertex3s\nGLAD_API_CALL PFNGLVERTEX3SVPROC glad_glVertex3sv;\nGLAD_API_CALL PFNGLVERTEX3SVPROC glad_debug_glVertex3sv;\n#define glVertex3sv glad_debug_glVertex3sv\nGLAD_API_CALL PFNGLVERTEX4DPROC glad_glVertex4d;\nGLAD_API_CALL PFNGLVERTEX4DPROC glad_debug_glVertex4d;\n#define glVertex4d glad_debug_glVertex4d\nGLAD_API_CALL PFNGLVERTEX4DVPROC glad_glVertex4dv;\nGLAD_API_CALL PFNGLVERTEX4DVPROC glad_debug_glVertex4dv;\n#define glVertex4dv glad_debug_glVertex4dv\nGLAD_API_CALL PFNGLVERTEX4FPROC glad_glVertex4f;\nGLAD_API_CALL PFNGLVERTEX4FPROC glad_debug_glVertex4f;\n#define glVertex4f glad_debug_glVertex4f\nGLAD_API_CALL PFNGLVERTEX4FVPROC glad_glVertex4fv;\nGLAD_API_CALL PFNGLVERTEX4FVPROC glad_debug_glVertex4fv;\n#define glVertex4fv glad_debug_glVertex4fv\nGLAD_API_CALL PFNGLVERTEX4IPROC glad_glVertex4i;\nGLAD_API_CALL PFNGLVERTEX4IPROC glad_debug_glVertex4i;\n#define glVertex4i glad_debug_glVertex4i\nGLAD_API_CALL PFNGLVERTEX4IVPROC glad_glVertex4iv;\nGLAD_API_CALL PFNGLVERTEX4IVPROC glad_debug_glVertex4iv;\n#define glVertex4iv glad_debug_glVertex4iv\nGLAD_API_CALL PFNGLVERTEX4SPROC glad_glVertex4s;\nGLAD_API_CALL PFNGLVERTEX4SPROC glad_debug_glVertex4s;\n#define glVertex4s glad_debug_glVertex4s\nGLAD_API_CALL PFNGLVERTEX4SVPROC glad_glVertex4sv;\nGLAD_API_CALL PFNGLVERTEX4SVPROC glad_debug_glVertex4sv;\n#define glVertex4sv glad_debug_glVertex4sv\nGLAD_API_CALL PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d;\nGLAD_API_CALL PFNGLVERTEXATTRIB1DPROC glad_debug_glVertexAttrib1d;\n#define glVertexAttrib1d glad_debug_glVertexAttrib1d\nGLAD_API_CALL PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv;\nGLAD_API_CALL PFNGLVERTEXATTRIB1DVPROC glad_debug_glVertexAttrib1dv;\n#define glVertexAttrib1dv glad_debug_glVertexAttrib1dv\nGLAD_API_CALL PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;\nGLAD_API_CALL PFNGLVERTEXATTRIB1FPROC glad_debug_glVertexAttrib1f;\n#define glVertexAttrib1f glad_debug_glVertexAttrib1f\nGLAD_API_CALL PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;\nGLAD_API_CALL PFNGLVERTEXATTRIB1FVPROC glad_debug_glVertexAttrib1fv;\n#define glVertexAttrib1fv glad_debug_glVertexAttrib1fv\nGLAD_API_CALL PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s;\nGLAD_API_CALL PFNGLVERTEXATTRIB1SPROC glad_debug_glVertexAttrib1s;\n#define glVertexAttrib1s glad_debug_glVertexAttrib1s\nGLAD_API_CALL PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv;\nGLAD_API_CALL PFNGLVERTEXATTRIB1SVPROC glad_debug_glVertexAttrib1sv;\n#define glVertexAttrib1sv glad_debug_glVertexAttrib1sv\nGLAD_API_CALL PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d;\nGLAD_API_CALL PFNGLVERTEXATTRIB2DPROC glad_debug_glVertexAttrib2d;\n#define glVertexAttrib2d glad_debug_glVertexAttrib2d\nGLAD_API_CALL PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv;\nGLAD_API_CALL PFNGLVERTEXATTRIB2DVPROC glad_debug_glVertexAttrib2dv;\n#define glVertexAttrib2dv glad_debug_glVertexAttrib2dv\nGLAD_API_CALL PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;\nGLAD_API_CALL PFNGLVERTEXATTRIB2FPROC glad_debug_glVertexAttrib2f;\n#define glVertexAttrib2f glad_debug_glVertexAttrib2f\nGLAD_API_CALL PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;\nGLAD_API_CALL PFNGLVERTEXATTRIB2FVPROC glad_debug_glVertexAttrib2fv;\n#define glVertexAttrib2fv glad_debug_glVertexAttrib2fv\nGLAD_API_CALL PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s;\nGLAD_API_CALL PFNGLVERTEXATTRIB2SPROC glad_debug_glVertexAttrib2s;\n#define glVertexAttrib2s glad_debug_glVertexAttrib2s\nGLAD_API_CALL PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv;\nGLAD_API_CALL PFNGLVERTEXATTRIB2SVPROC glad_debug_glVertexAttrib2sv;\n#define glVertexAttrib2sv glad_debug_glVertexAttrib2sv\nGLAD_API_CALL PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d;\nGLAD_API_CALL PFNGLVERTEXATTRIB3DPROC glad_debug_glVertexAttrib3d;\n#define glVertexAttrib3d glad_debug_glVertexAttrib3d\nGLAD_API_CALL PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv;\nGLAD_API_CALL PFNGLVERTEXATTRIB3DVPROC glad_debug_glVertexAttrib3dv;\n#define glVertexAttrib3dv glad_debug_glVertexAttrib3dv\nGLAD_API_CALL PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;\nGLAD_API_CALL PFNGLVERTEXATTRIB3FPROC glad_debug_glVertexAttrib3f;\n#define glVertexAttrib3f glad_debug_glVertexAttrib3f\nGLAD_API_CALL PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;\nGLAD_API_CALL PFNGLVERTEXATTRIB3FVPROC glad_debug_glVertexAttrib3fv;\n#define glVertexAttrib3fv glad_debug_glVertexAttrib3fv\nGLAD_API_CALL PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s;\nGLAD_API_CALL PFNGLVERTEXATTRIB3SPROC glad_debug_glVertexAttrib3s;\n#define glVertexAttrib3s glad_debug_glVertexAttrib3s\nGLAD_API_CALL PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv;\nGLAD_API_CALL PFNGLVERTEXATTRIB3SVPROC glad_debug_glVertexAttrib3sv;\n#define glVertexAttrib3sv glad_debug_glVertexAttrib3sv\nGLAD_API_CALL PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4NBVPROC glad_debug_glVertexAttrib4Nbv;\n#define glVertexAttrib4Nbv glad_debug_glVertexAttrib4Nbv\nGLAD_API_CALL PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4NIVPROC glad_debug_glVertexAttrib4Niv;\n#define glVertexAttrib4Niv glad_debug_glVertexAttrib4Niv\nGLAD_API_CALL PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4NSVPROC glad_debug_glVertexAttrib4Nsv;\n#define glVertexAttrib4Nsv glad_debug_glVertexAttrib4Nsv\nGLAD_API_CALL PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub;\nGLAD_API_CALL PFNGLVERTEXATTRIB4NUBPROC glad_debug_glVertexAttrib4Nub;\n#define glVertexAttrib4Nub glad_debug_glVertexAttrib4Nub\nGLAD_API_CALL PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4NUBVPROC glad_debug_glVertexAttrib4Nubv;\n#define glVertexAttrib4Nubv glad_debug_glVertexAttrib4Nubv\nGLAD_API_CALL PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4NUIVPROC glad_debug_glVertexAttrib4Nuiv;\n#define glVertexAttrib4Nuiv glad_debug_glVertexAttrib4Nuiv\nGLAD_API_CALL PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4NUSVPROC glad_debug_glVertexAttrib4Nusv;\n#define glVertexAttrib4Nusv glad_debug_glVertexAttrib4Nusv\nGLAD_API_CALL PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4BVPROC glad_debug_glVertexAttrib4bv;\n#define glVertexAttrib4bv glad_debug_glVertexAttrib4bv\nGLAD_API_CALL PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d;\nGLAD_API_CALL PFNGLVERTEXATTRIB4DPROC glad_debug_glVertexAttrib4d;\n#define glVertexAttrib4d glad_debug_glVertexAttrib4d\nGLAD_API_CALL PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4DVPROC glad_debug_glVertexAttrib4dv;\n#define glVertexAttrib4dv glad_debug_glVertexAttrib4dv\nGLAD_API_CALL PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;\nGLAD_API_CALL PFNGLVERTEXATTRIB4FPROC glad_debug_glVertexAttrib4f;\n#define glVertexAttrib4f glad_debug_glVertexAttrib4f\nGLAD_API_CALL PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4FVPROC glad_debug_glVertexAttrib4fv;\n#define glVertexAttrib4fv glad_debug_glVertexAttrib4fv\nGLAD_API_CALL PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4IVPROC glad_debug_glVertexAttrib4iv;\n#define glVertexAttrib4iv glad_debug_glVertexAttrib4iv\nGLAD_API_CALL PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s;\nGLAD_API_CALL PFNGLVERTEXATTRIB4SPROC glad_debug_glVertexAttrib4s;\n#define glVertexAttrib4s glad_debug_glVertexAttrib4s\nGLAD_API_CALL PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4SVPROC glad_debug_glVertexAttrib4sv;\n#define glVertexAttrib4sv glad_debug_glVertexAttrib4sv\nGLAD_API_CALL PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4UBVPROC glad_debug_glVertexAttrib4ubv;\n#define glVertexAttrib4ubv glad_debug_glVertexAttrib4ubv\nGLAD_API_CALL PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4UIVPROC glad_debug_glVertexAttrib4uiv;\n#define glVertexAttrib4uiv glad_debug_glVertexAttrib4uiv\nGLAD_API_CALL PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv;\nGLAD_API_CALL PFNGLVERTEXATTRIB4USVPROC glad_debug_glVertexAttrib4usv;\n#define glVertexAttrib4usv glad_debug_glVertexAttrib4usv\nGLAD_API_CALL PFNGLVERTEXATTRIBDIVISORARBPROC glad_glVertexAttribDivisorARB;\nGLAD_API_CALL PFNGLVERTEXATTRIBDIVISORARBPROC glad_debug_glVertexAttribDivisorARB;\n#define glVertexAttribDivisorARB glad_debug_glVertexAttribDivisorARB\nGLAD_API_CALL PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i;\nGLAD_API_CALL PFNGLVERTEXATTRIBI1IPROC glad_debug_glVertexAttribI1i;\n#define glVertexAttribI1i glad_debug_glVertexAttribI1i\nGLAD_API_CALL PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI1IVPROC glad_debug_glVertexAttribI1iv;\n#define glVertexAttribI1iv glad_debug_glVertexAttribI1iv\nGLAD_API_CALL PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui;\nGLAD_API_CALL PFNGLVERTEXATTRIBI1UIPROC glad_debug_glVertexAttribI1ui;\n#define glVertexAttribI1ui glad_debug_glVertexAttribI1ui\nGLAD_API_CALL PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI1UIVPROC glad_debug_glVertexAttribI1uiv;\n#define glVertexAttribI1uiv glad_debug_glVertexAttribI1uiv\nGLAD_API_CALL PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i;\nGLAD_API_CALL PFNGLVERTEXATTRIBI2IPROC glad_debug_glVertexAttribI2i;\n#define glVertexAttribI2i glad_debug_glVertexAttribI2i\nGLAD_API_CALL PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI2IVPROC glad_debug_glVertexAttribI2iv;\n#define glVertexAttribI2iv glad_debug_glVertexAttribI2iv\nGLAD_API_CALL PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui;\nGLAD_API_CALL PFNGLVERTEXATTRIBI2UIPROC glad_debug_glVertexAttribI2ui;\n#define glVertexAttribI2ui glad_debug_glVertexAttribI2ui\nGLAD_API_CALL PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI2UIVPROC glad_debug_glVertexAttribI2uiv;\n#define glVertexAttribI2uiv glad_debug_glVertexAttribI2uiv\nGLAD_API_CALL PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i;\nGLAD_API_CALL PFNGLVERTEXATTRIBI3IPROC glad_debug_glVertexAttribI3i;\n#define glVertexAttribI3i glad_debug_glVertexAttribI3i\nGLAD_API_CALL PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI3IVPROC glad_debug_glVertexAttribI3iv;\n#define glVertexAttribI3iv glad_debug_glVertexAttribI3iv\nGLAD_API_CALL PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui;\nGLAD_API_CALL PFNGLVERTEXATTRIBI3UIPROC glad_debug_glVertexAttribI3ui;\n#define glVertexAttribI3ui glad_debug_glVertexAttribI3ui\nGLAD_API_CALL PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI3UIVPROC glad_debug_glVertexAttribI3uiv;\n#define glVertexAttribI3uiv glad_debug_glVertexAttribI3uiv\nGLAD_API_CALL PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI4BVPROC glad_debug_glVertexAttribI4bv;\n#define glVertexAttribI4bv glad_debug_glVertexAttribI4bv\nGLAD_API_CALL PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i;\nGLAD_API_CALL PFNGLVERTEXATTRIBI4IPROC glad_debug_glVertexAttribI4i;\n#define glVertexAttribI4i glad_debug_glVertexAttribI4i\nGLAD_API_CALL PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI4IVPROC glad_debug_glVertexAttribI4iv;\n#define glVertexAttribI4iv glad_debug_glVertexAttribI4iv\nGLAD_API_CALL PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI4SVPROC glad_debug_glVertexAttribI4sv;\n#define glVertexAttribI4sv glad_debug_glVertexAttribI4sv\nGLAD_API_CALL PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI4UBVPROC glad_debug_glVertexAttribI4ubv;\n#define glVertexAttribI4ubv glad_debug_glVertexAttribI4ubv\nGLAD_API_CALL PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui;\nGLAD_API_CALL PFNGLVERTEXATTRIBI4UIPROC glad_debug_glVertexAttribI4ui;\n#define glVertexAttribI4ui glad_debug_glVertexAttribI4ui\nGLAD_API_CALL PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI4UIVPROC glad_debug_glVertexAttribI4uiv;\n#define glVertexAttribI4uiv glad_debug_glVertexAttribI4uiv\nGLAD_API_CALL PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv;\nGLAD_API_CALL PFNGLVERTEXATTRIBI4USVPROC glad_debug_glVertexAttribI4usv;\n#define glVertexAttribI4usv glad_debug_glVertexAttribI4usv\nGLAD_API_CALL PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer;\nGLAD_API_CALL PFNGLVERTEXATTRIBIPOINTERPROC glad_debug_glVertexAttribIPointer;\n#define glVertexAttribIPointer glad_debug_glVertexAttribIPointer\nGLAD_API_CALL PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;\nGLAD_API_CALL PFNGLVERTEXATTRIBPOINTERPROC glad_debug_glVertexAttribPointer;\n#define glVertexAttribPointer glad_debug_glVertexAttribPointer\nGLAD_API_CALL PFNGLVERTEXPOINTERPROC glad_glVertexPointer;\nGLAD_API_CALL PFNGLVERTEXPOINTERPROC glad_debug_glVertexPointer;\n#define glVertexPointer glad_debug_glVertexPointer\nGLAD_API_CALL PFNGLVIEWPORTPROC glad_glViewport;\nGLAD_API_CALL PFNGLVIEWPORTPROC glad_debug_glViewport;\n#define glViewport glad_debug_glViewport\nGLAD_API_CALL PFNGLWINDOWPOS2DPROC glad_glWindowPos2d;\nGLAD_API_CALL PFNGLWINDOWPOS2DPROC glad_debug_glWindowPos2d;\n#define glWindowPos2d glad_debug_glWindowPos2d\nGLAD_API_CALL PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv;\nGLAD_API_CALL PFNGLWINDOWPOS2DVPROC glad_debug_glWindowPos2dv;\n#define glWindowPos2dv glad_debug_glWindowPos2dv\nGLAD_API_CALL PFNGLWINDOWPOS2FPROC glad_glWindowPos2f;\nGLAD_API_CALL PFNGLWINDOWPOS2FPROC glad_debug_glWindowPos2f;\n#define glWindowPos2f glad_debug_glWindowPos2f\nGLAD_API_CALL PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv;\nGLAD_API_CALL PFNGLWINDOWPOS2FVPROC glad_debug_glWindowPos2fv;\n#define glWindowPos2fv glad_debug_glWindowPos2fv\nGLAD_API_CALL PFNGLWINDOWPOS2IPROC glad_glWindowPos2i;\nGLAD_API_CALL PFNGLWINDOWPOS2IPROC glad_debug_glWindowPos2i;\n#define glWindowPos2i glad_debug_glWindowPos2i\nGLAD_API_CALL PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv;\nGLAD_API_CALL PFNGLWINDOWPOS2IVPROC glad_debug_glWindowPos2iv;\n#define glWindowPos2iv glad_debug_glWindowPos2iv\nGLAD_API_CALL PFNGLWINDOWPOS2SPROC glad_glWindowPos2s;\nGLAD_API_CALL PFNGLWINDOWPOS2SPROC glad_debug_glWindowPos2s;\n#define glWindowPos2s glad_debug_glWindowPos2s\nGLAD_API_CALL PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv;\nGLAD_API_CALL PFNGLWINDOWPOS2SVPROC glad_debug_glWindowPos2sv;\n#define glWindowPos2sv glad_debug_glWindowPos2sv\nGLAD_API_CALL PFNGLWINDOWPOS3DPROC glad_glWindowPos3d;\nGLAD_API_CALL PFNGLWINDOWPOS3DPROC glad_debug_glWindowPos3d;\n#define glWindowPos3d glad_debug_glWindowPos3d\nGLAD_API_CALL PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv;\nGLAD_API_CALL PFNGLWINDOWPOS3DVPROC glad_debug_glWindowPos3dv;\n#define glWindowPos3dv glad_debug_glWindowPos3dv\nGLAD_API_CALL PFNGLWINDOWPOS3FPROC glad_glWindowPos3f;\nGLAD_API_CALL PFNGLWINDOWPOS3FPROC glad_debug_glWindowPos3f;\n#define glWindowPos3f glad_debug_glWindowPos3f\nGLAD_API_CALL PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv;\nGLAD_API_CALL PFNGLWINDOWPOS3FVPROC glad_debug_glWindowPos3fv;\n#define glWindowPos3fv glad_debug_glWindowPos3fv\nGLAD_API_CALL PFNGLWINDOWPOS3IPROC glad_glWindowPos3i;\nGLAD_API_CALL PFNGLWINDOWPOS3IPROC glad_debug_glWindowPos3i;\n#define glWindowPos3i glad_debug_glWindowPos3i\nGLAD_API_CALL PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv;\nGLAD_API_CALL PFNGLWINDOWPOS3IVPROC glad_debug_glWindowPos3iv;\n#define glWindowPos3iv glad_debug_glWindowPos3iv\nGLAD_API_CALL PFNGLWINDOWPOS3SPROC glad_glWindowPos3s;\nGLAD_API_CALL PFNGLWINDOWPOS3SPROC glad_debug_glWindowPos3s;\n#define glWindowPos3s glad_debug_glWindowPos3s\nGLAD_API_CALL PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv;\nGLAD_API_CALL PFNGLWINDOWPOS3SVPROC glad_debug_glWindowPos3sv;\n#define glWindowPos3sv glad_debug_glWindowPos3sv\nGLAD_API_CALL void gladSetGLPreCallback(GLADprecallback cb);\nGLAD_API_CALL void gladSetGLPostCallback(GLADpostcallback cb);\nGLAD_API_CALL void gladInstallGLDebug(void);\nGLAD_API_CALL void gladUninstallGLDebug(void);\nGLAD_API_CALL int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr);\nGLAD_API_CALL int gladLoadGL( GLADloadfunc load);\n#ifdef __cplusplus\n}\n#endif\n#endif\n/* Source */\n#ifdef GLAD_GL_IMPLEMENTATION\n/**\n * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef GLAD_IMPL_UTIL_C_\n#define GLAD_IMPL_UTIL_C_\n#ifdef _MSC_VER\n#define GLAD_IMPL_UTIL_SSCANF sscanf_s\n#else\n#define GLAD_IMPL_UTIL_SSCANF sscanf\n#endif\n#endif /* GLAD_IMPL_UTIL_C_ */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nint GLAD_GL_VERSION_1_0 = 0;\nint GLAD_GL_VERSION_1_1 = 0;\nint GLAD_GL_VERSION_1_2 = 0;\nint GLAD_GL_VERSION_1_3 = 0;\nint GLAD_GL_VERSION_1_4 = 0;\nint GLAD_GL_VERSION_1_5 = 0;\nint GLAD_GL_VERSION_2_0 = 0;\nint GLAD_GL_VERSION_2_1 = 0;\nint GLAD_GL_VERSION_3_0 = 0;\nint GLAD_GL_VERSION_3_1 = 0;\nint GLAD_GL_ARB_copy_image = 0;\nint GLAD_GL_ARB_framebuffer_sRGB = 0;\nint GLAD_GL_ARB_instanced_arrays = 0;\nint GLAD_GL_ARB_multisample = 0;\nint GLAD_GL_ARB_robustness = 0;\nint GLAD_GL_ARB_texture_storage = 0;\nint GLAD_GL_EXT_framebuffer_sRGB = 0;\nint GLAD_GL_KHR_debug = 0;\nstatic void _pre_call_gl_callback_default(const char *name, GLADapiproc apiproc, int len_args, ...) {\n    GLAD_UNUSED(len_args);\n    if (apiproc == NULL) {\n        fprintf(stderr, \"GLAD: ERROR %s is NULL!\\n\", name);\n        return;\n    }\n    if (glad_glGetError == NULL) {\n        fprintf(stderr, \"GLAD: ERROR glGetError is NULL!\\n\");\n        return;\n    }\n    (void) glad_glGetError();\n}\nstatic void _post_call_gl_callback_default(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...) {\n    GLenum error_code;\n    GLAD_UNUSED(ret);\n    GLAD_UNUSED(apiproc);\n    GLAD_UNUSED(len_args);\n    error_code = glad_glGetError();\n    if (error_code != GL_NO_ERROR) {\n        fprintf(stderr, \"GLAD: ERROR %d in %s!\\n\", error_code, name);\n    }\n}\nstatic GLADprecallback _pre_call_gl_callback = _pre_call_gl_callback_default;\nvoid gladSetGLPreCallback(GLADprecallback cb) {\n    _pre_call_gl_callback = cb;\n}\nstatic GLADpostcallback _post_call_gl_callback = _post_call_gl_callback_default;\nvoid gladSetGLPostCallback(GLADpostcallback cb) {\n    _post_call_gl_callback = cb;\n}\nPFNGLACCUMPROC glad_glAccum = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glAccum(GLenum op, GLfloat value) {\n    _pre_call_gl_callback(\"glAccum\", (GLADapiproc) glad_glAccum, 2, op, value);\n    glad_glAccum(op, value);\n    _post_call_gl_callback(NULL, \"glAccum\", (GLADapiproc) glad_glAccum, 2, op, value);\n}\nPFNGLACCUMPROC glad_debug_glAccum = glad_debug_impl_glAccum;\nPFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glActiveTexture(GLenum texture) {\n    _pre_call_gl_callback(\"glActiveTexture\", (GLADapiproc) glad_glActiveTexture, 1, texture);\n    glad_glActiveTexture(texture);\n    _post_call_gl_callback(NULL, \"glActiveTexture\", (GLADapiproc) glad_glActiveTexture, 1, texture);\n}\nPFNGLACTIVETEXTUREPROC glad_debug_glActiveTexture = glad_debug_impl_glActiveTexture;\nPFNGLALPHAFUNCPROC glad_glAlphaFunc = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glAlphaFunc(GLenum func, GLfloat ref) {\n    _pre_call_gl_callback(\"glAlphaFunc\", (GLADapiproc) glad_glAlphaFunc, 2, func, ref);\n    glad_glAlphaFunc(func, ref);\n    _post_call_gl_callback(NULL, \"glAlphaFunc\", (GLADapiproc) glad_glAlphaFunc, 2, func, ref);\n}\nPFNGLALPHAFUNCPROC glad_debug_glAlphaFunc = glad_debug_impl_glAlphaFunc;\nPFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glAreTexturesResident(GLsizei n, const GLuint * textures, GLboolean * residences) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glAreTexturesResident\", (GLADapiproc) glad_glAreTexturesResident, 3, n, textures, residences);\n    ret = glad_glAreTexturesResident(n, textures, residences);\n    _post_call_gl_callback((void*) &ret, \"glAreTexturesResident\", (GLADapiproc) glad_glAreTexturesResident, 3, n, textures, residences);\n    return ret;\n}\nPFNGLARETEXTURESRESIDENTPROC glad_debug_glAreTexturesResident = glad_debug_impl_glAreTexturesResident;\nPFNGLARRAYELEMENTPROC glad_glArrayElement = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glArrayElement(GLint i) {\n    _pre_call_gl_callback(\"glArrayElement\", (GLADapiproc) glad_glArrayElement, 1, i);\n    glad_glArrayElement(i);\n    _post_call_gl_callback(NULL, \"glArrayElement\", (GLADapiproc) glad_glArrayElement, 1, i);\n}\nPFNGLARRAYELEMENTPROC glad_debug_glArrayElement = glad_debug_impl_glArrayElement;\nPFNGLATTACHSHADERPROC glad_glAttachShader = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glAttachShader(GLuint program, GLuint shader) {\n    _pre_call_gl_callback(\"glAttachShader\", (GLADapiproc) glad_glAttachShader, 2, program, shader);\n    glad_glAttachShader(program, shader);\n    _post_call_gl_callback(NULL, \"glAttachShader\", (GLADapiproc) glad_glAttachShader, 2, program, shader);\n}\nPFNGLATTACHSHADERPROC glad_debug_glAttachShader = glad_debug_impl_glAttachShader;\nPFNGLBEGINPROC glad_glBegin = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBegin(GLenum mode) {\n    _pre_call_gl_callback(\"glBegin\", (GLADapiproc) glad_glBegin, 1, mode);\n    glad_glBegin(mode);\n    _post_call_gl_callback(NULL, \"glBegin\", (GLADapiproc) glad_glBegin, 1, mode);\n}\nPFNGLBEGINPROC glad_debug_glBegin = glad_debug_impl_glBegin;\nPFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBeginConditionalRender(GLuint id, GLenum mode) {\n    _pre_call_gl_callback(\"glBeginConditionalRender\", (GLADapiproc) glad_glBeginConditionalRender, 2, id, mode);\n    glad_glBeginConditionalRender(id, mode);\n    _post_call_gl_callback(NULL, \"glBeginConditionalRender\", (GLADapiproc) glad_glBeginConditionalRender, 2, id, mode);\n}\nPFNGLBEGINCONDITIONALRENDERPROC glad_debug_glBeginConditionalRender = glad_debug_impl_glBeginConditionalRender;\nPFNGLBEGINQUERYPROC glad_glBeginQuery = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBeginQuery(GLenum target, GLuint id) {\n    _pre_call_gl_callback(\"glBeginQuery\", (GLADapiproc) glad_glBeginQuery, 2, target, id);\n    glad_glBeginQuery(target, id);\n    _post_call_gl_callback(NULL, \"glBeginQuery\", (GLADapiproc) glad_glBeginQuery, 2, target, id);\n}\nPFNGLBEGINQUERYPROC glad_debug_glBeginQuery = glad_debug_impl_glBeginQuery;\nPFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBeginTransformFeedback(GLenum primitiveMode) {\n    _pre_call_gl_callback(\"glBeginTransformFeedback\", (GLADapiproc) glad_glBeginTransformFeedback, 1, primitiveMode);\n    glad_glBeginTransformFeedback(primitiveMode);\n    _post_call_gl_callback(NULL, \"glBeginTransformFeedback\", (GLADapiproc) glad_glBeginTransformFeedback, 1, primitiveMode);\n}\nPFNGLBEGINTRANSFORMFEEDBACKPROC glad_debug_glBeginTransformFeedback = glad_debug_impl_glBeginTransformFeedback;\nPFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindAttribLocation(GLuint program, GLuint index, const GLchar * name) {\n    _pre_call_gl_callback(\"glBindAttribLocation\", (GLADapiproc) glad_glBindAttribLocation, 3, program, index, name);\n    glad_glBindAttribLocation(program, index, name);\n    _post_call_gl_callback(NULL, \"glBindAttribLocation\", (GLADapiproc) glad_glBindAttribLocation, 3, program, index, name);\n}\nPFNGLBINDATTRIBLOCATIONPROC glad_debug_glBindAttribLocation = glad_debug_impl_glBindAttribLocation;\nPFNGLBINDBUFFERPROC glad_glBindBuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindBuffer(GLenum target, GLuint buffer) {\n    _pre_call_gl_callback(\"glBindBuffer\", (GLADapiproc) glad_glBindBuffer, 2, target, buffer);\n    glad_glBindBuffer(target, buffer);\n    _post_call_gl_callback(NULL, \"glBindBuffer\", (GLADapiproc) glad_glBindBuffer, 2, target, buffer);\n}\nPFNGLBINDBUFFERPROC glad_debug_glBindBuffer = glad_debug_impl_glBindBuffer;\nPFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindBufferBase(GLenum target, GLuint index, GLuint buffer) {\n    _pre_call_gl_callback(\"glBindBufferBase\", (GLADapiproc) glad_glBindBufferBase, 3, target, index, buffer);\n    glad_glBindBufferBase(target, index, buffer);\n    _post_call_gl_callback(NULL, \"glBindBufferBase\", (GLADapiproc) glad_glBindBufferBase, 3, target, index, buffer);\n}\nPFNGLBINDBUFFERBASEPROC glad_debug_glBindBufferBase = glad_debug_impl_glBindBufferBase;\nPFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) {\n    _pre_call_gl_callback(\"glBindBufferRange\", (GLADapiproc) glad_glBindBufferRange, 5, target, index, buffer, offset, size);\n    glad_glBindBufferRange(target, index, buffer, offset, size);\n    _post_call_gl_callback(NULL, \"glBindBufferRange\", (GLADapiproc) glad_glBindBufferRange, 5, target, index, buffer, offset, size);\n}\nPFNGLBINDBUFFERRANGEPROC glad_debug_glBindBufferRange = glad_debug_impl_glBindBufferRange;\nPFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindFragDataLocation(GLuint program, GLuint color, const GLchar * name) {\n    _pre_call_gl_callback(\"glBindFragDataLocation\", (GLADapiproc) glad_glBindFragDataLocation, 3, program, color, name);\n    glad_glBindFragDataLocation(program, color, name);\n    _post_call_gl_callback(NULL, \"glBindFragDataLocation\", (GLADapiproc) glad_glBindFragDataLocation, 3, program, color, name);\n}\nPFNGLBINDFRAGDATALOCATIONPROC glad_debug_glBindFragDataLocation = glad_debug_impl_glBindFragDataLocation;\nPFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindFramebuffer(GLenum target, GLuint framebuffer) {\n    _pre_call_gl_callback(\"glBindFramebuffer\", (GLADapiproc) glad_glBindFramebuffer, 2, target, framebuffer);\n    glad_glBindFramebuffer(target, framebuffer);\n    _post_call_gl_callback(NULL, \"glBindFramebuffer\", (GLADapiproc) glad_glBindFramebuffer, 2, target, framebuffer);\n}\nPFNGLBINDFRAMEBUFFERPROC glad_debug_glBindFramebuffer = glad_debug_impl_glBindFramebuffer;\nPFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindRenderbuffer(GLenum target, GLuint renderbuffer) {\n    _pre_call_gl_callback(\"glBindRenderbuffer\", (GLADapiproc) glad_glBindRenderbuffer, 2, target, renderbuffer);\n    glad_glBindRenderbuffer(target, renderbuffer);\n    _post_call_gl_callback(NULL, \"glBindRenderbuffer\", (GLADapiproc) glad_glBindRenderbuffer, 2, target, renderbuffer);\n}\nPFNGLBINDRENDERBUFFERPROC glad_debug_glBindRenderbuffer = glad_debug_impl_glBindRenderbuffer;\nPFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindTexture(GLenum target, GLuint texture) {\n    _pre_call_gl_callback(\"glBindTexture\", (GLADapiproc) glad_glBindTexture, 2, target, texture);\n    glad_glBindTexture(target, texture);\n    _post_call_gl_callback(NULL, \"glBindTexture\", (GLADapiproc) glad_glBindTexture, 2, target, texture);\n}\nPFNGLBINDTEXTUREPROC glad_debug_glBindTexture = glad_debug_impl_glBindTexture;\nPFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBindVertexArray(GLuint array) {\n    _pre_call_gl_callback(\"glBindVertexArray\", (GLADapiproc) glad_glBindVertexArray, 1, array);\n    glad_glBindVertexArray(array);\n    _post_call_gl_callback(NULL, \"glBindVertexArray\", (GLADapiproc) glad_glBindVertexArray, 1, array);\n}\nPFNGLBINDVERTEXARRAYPROC glad_debug_glBindVertexArray = glad_debug_impl_glBindVertexArray;\nPFNGLBITMAPPROC glad_glBitmap = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBitmap(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte * bitmap) {\n    _pre_call_gl_callback(\"glBitmap\", (GLADapiproc) glad_glBitmap, 7, width, height, xorig, yorig, xmove, ymove, bitmap);\n    glad_glBitmap(width, height, xorig, yorig, xmove, ymove, bitmap);\n    _post_call_gl_callback(NULL, \"glBitmap\", (GLADapiproc) glad_glBitmap, 7, width, height, xorig, yorig, xmove, ymove, bitmap);\n}\nPFNGLBITMAPPROC glad_debug_glBitmap = glad_debug_impl_glBitmap;\nPFNGLBLENDCOLORPROC glad_glBlendColor = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {\n    _pre_call_gl_callback(\"glBlendColor\", (GLADapiproc) glad_glBlendColor, 4, red, green, blue, alpha);\n    glad_glBlendColor(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glBlendColor\", (GLADapiproc) glad_glBlendColor, 4, red, green, blue, alpha);\n}\nPFNGLBLENDCOLORPROC glad_debug_glBlendColor = glad_debug_impl_glBlendColor;\nPFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBlendEquation(GLenum mode) {\n    _pre_call_gl_callback(\"glBlendEquation\", (GLADapiproc) glad_glBlendEquation, 1, mode);\n    glad_glBlendEquation(mode);\n    _post_call_gl_callback(NULL, \"glBlendEquation\", (GLADapiproc) glad_glBlendEquation, 1, mode);\n}\nPFNGLBLENDEQUATIONPROC glad_debug_glBlendEquation = glad_debug_impl_glBlendEquation;\nPFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {\n    _pre_call_gl_callback(\"glBlendEquationSeparate\", (GLADapiproc) glad_glBlendEquationSeparate, 2, modeRGB, modeAlpha);\n    glad_glBlendEquationSeparate(modeRGB, modeAlpha);\n    _post_call_gl_callback(NULL, \"glBlendEquationSeparate\", (GLADapiproc) glad_glBlendEquationSeparate, 2, modeRGB, modeAlpha);\n}\nPFNGLBLENDEQUATIONSEPARATEPROC glad_debug_glBlendEquationSeparate = glad_debug_impl_glBlendEquationSeparate;\nPFNGLBLENDFUNCPROC glad_glBlendFunc = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBlendFunc(GLenum sfactor, GLenum dfactor) {\n    _pre_call_gl_callback(\"glBlendFunc\", (GLADapiproc) glad_glBlendFunc, 2, sfactor, dfactor);\n    glad_glBlendFunc(sfactor, dfactor);\n    _post_call_gl_callback(NULL, \"glBlendFunc\", (GLADapiproc) glad_glBlendFunc, 2, sfactor, dfactor);\n}\nPFNGLBLENDFUNCPROC glad_debug_glBlendFunc = glad_debug_impl_glBlendFunc;\nPFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {\n    _pre_call_gl_callback(\"glBlendFuncSeparate\", (GLADapiproc) glad_glBlendFuncSeparate, 4, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);\n    glad_glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);\n    _post_call_gl_callback(NULL, \"glBlendFuncSeparate\", (GLADapiproc) glad_glBlendFuncSeparate, 4, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);\n}\nPFNGLBLENDFUNCSEPARATEPROC glad_debug_glBlendFuncSeparate = glad_debug_impl_glBlendFuncSeparate;\nPFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {\n    _pre_call_gl_callback(\"glBlitFramebuffer\", (GLADapiproc) glad_glBlitFramebuffer, 10, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);\n    glad_glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);\n    _post_call_gl_callback(NULL, \"glBlitFramebuffer\", (GLADapiproc) glad_glBlitFramebuffer, 10, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);\n}\nPFNGLBLITFRAMEBUFFERPROC glad_debug_glBlitFramebuffer = glad_debug_impl_glBlitFramebuffer;\nPFNGLBUFFERDATAPROC glad_glBufferData = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBufferData(GLenum target, GLsizeiptr size, const void * data, GLenum usage) {\n    _pre_call_gl_callback(\"glBufferData\", (GLADapiproc) glad_glBufferData, 4, target, size, data, usage);\n    glad_glBufferData(target, size, data, usage);\n    _post_call_gl_callback(NULL, \"glBufferData\", (GLADapiproc) glad_glBufferData, 4, target, size, data, usage);\n}\nPFNGLBUFFERDATAPROC glad_debug_glBufferData = glad_debug_impl_glBufferData;\nPFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void * data) {\n    _pre_call_gl_callback(\"glBufferSubData\", (GLADapiproc) glad_glBufferSubData, 4, target, offset, size, data);\n    glad_glBufferSubData(target, offset, size, data);\n    _post_call_gl_callback(NULL, \"glBufferSubData\", (GLADapiproc) glad_glBufferSubData, 4, target, offset, size, data);\n}\nPFNGLBUFFERSUBDATAPROC glad_debug_glBufferSubData = glad_debug_impl_glBufferSubData;\nPFNGLCALLLISTPROC glad_glCallList = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCallList(GLuint list) {\n    _pre_call_gl_callback(\"glCallList\", (GLADapiproc) glad_glCallList, 1, list);\n    glad_glCallList(list);\n    _post_call_gl_callback(NULL, \"glCallList\", (GLADapiproc) glad_glCallList, 1, list);\n}\nPFNGLCALLLISTPROC glad_debug_glCallList = glad_debug_impl_glCallList;\nPFNGLCALLLISTSPROC glad_glCallLists = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCallLists(GLsizei n, GLenum type, const void * lists) {\n    _pre_call_gl_callback(\"glCallLists\", (GLADapiproc) glad_glCallLists, 3, n, type, lists);\n    glad_glCallLists(n, type, lists);\n    _post_call_gl_callback(NULL, \"glCallLists\", (GLADapiproc) glad_glCallLists, 3, n, type, lists);\n}\nPFNGLCALLLISTSPROC glad_debug_glCallLists = glad_debug_impl_glCallLists;\nPFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL;\nstatic GLenum GLAD_API_PTR glad_debug_impl_glCheckFramebufferStatus(GLenum target) {\n    GLenum ret;\n    _pre_call_gl_callback(\"glCheckFramebufferStatus\", (GLADapiproc) glad_glCheckFramebufferStatus, 1, target);\n    ret = glad_glCheckFramebufferStatus(target);\n    _post_call_gl_callback((void*) &ret, \"glCheckFramebufferStatus\", (GLADapiproc) glad_glCheckFramebufferStatus, 1, target);\n    return ret;\n}\nPFNGLCHECKFRAMEBUFFERSTATUSPROC glad_debug_glCheckFramebufferStatus = glad_debug_impl_glCheckFramebufferStatus;\nPFNGLCLAMPCOLORPROC glad_glClampColor = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClampColor(GLenum target, GLenum clamp) {\n    _pre_call_gl_callback(\"glClampColor\", (GLADapiproc) glad_glClampColor, 2, target, clamp);\n    glad_glClampColor(target, clamp);\n    _post_call_gl_callback(NULL, \"glClampColor\", (GLADapiproc) glad_glClampColor, 2, target, clamp);\n}\nPFNGLCLAMPCOLORPROC glad_debug_glClampColor = glad_debug_impl_glClampColor;\nPFNGLCLEARPROC glad_glClear = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClear(GLbitfield mask) {\n    _pre_call_gl_callback(\"glClear\", (GLADapiproc) glad_glClear, 1, mask);\n    glad_glClear(mask);\n    _post_call_gl_callback(NULL, \"glClear\", (GLADapiproc) glad_glClear, 1, mask);\n}\nPFNGLCLEARPROC glad_debug_glClear = glad_debug_impl_glClear;\nPFNGLCLEARACCUMPROC glad_glClearAccum = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearAccum(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {\n    _pre_call_gl_callback(\"glClearAccum\", (GLADapiproc) glad_glClearAccum, 4, red, green, blue, alpha);\n    glad_glClearAccum(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glClearAccum\", (GLADapiproc) glad_glClearAccum, 4, red, green, blue, alpha);\n}\nPFNGLCLEARACCUMPROC glad_debug_glClearAccum = glad_debug_impl_glClearAccum;\nPFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {\n    _pre_call_gl_callback(\"glClearBufferfi\", (GLADapiproc) glad_glClearBufferfi, 4, buffer, drawbuffer, depth, stencil);\n    glad_glClearBufferfi(buffer, drawbuffer, depth, stencil);\n    _post_call_gl_callback(NULL, \"glClearBufferfi\", (GLADapiproc) glad_glClearBufferfi, 4, buffer, drawbuffer, depth, stencil);\n}\nPFNGLCLEARBUFFERFIPROC glad_debug_glClearBufferfi = glad_debug_impl_glClearBufferfi;\nPFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat * value) {\n    _pre_call_gl_callback(\"glClearBufferfv\", (GLADapiproc) glad_glClearBufferfv, 3, buffer, drawbuffer, value);\n    glad_glClearBufferfv(buffer, drawbuffer, value);\n    _post_call_gl_callback(NULL, \"glClearBufferfv\", (GLADapiproc) glad_glClearBufferfv, 3, buffer, drawbuffer, value);\n}\nPFNGLCLEARBUFFERFVPROC glad_debug_glClearBufferfv = glad_debug_impl_glClearBufferfv;\nPFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint * value) {\n    _pre_call_gl_callback(\"glClearBufferiv\", (GLADapiproc) glad_glClearBufferiv, 3, buffer, drawbuffer, value);\n    glad_glClearBufferiv(buffer, drawbuffer, value);\n    _post_call_gl_callback(NULL, \"glClearBufferiv\", (GLADapiproc) glad_glClearBufferiv, 3, buffer, drawbuffer, value);\n}\nPFNGLCLEARBUFFERIVPROC glad_debug_glClearBufferiv = glad_debug_impl_glClearBufferiv;\nPFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint * value) {\n    _pre_call_gl_callback(\"glClearBufferuiv\", (GLADapiproc) glad_glClearBufferuiv, 3, buffer, drawbuffer, value);\n    glad_glClearBufferuiv(buffer, drawbuffer, value);\n    _post_call_gl_callback(NULL, \"glClearBufferuiv\", (GLADapiproc) glad_glClearBufferuiv, 3, buffer, drawbuffer, value);\n}\nPFNGLCLEARBUFFERUIVPROC glad_debug_glClearBufferuiv = glad_debug_impl_glClearBufferuiv;\nPFNGLCLEARCOLORPROC glad_glClearColor = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {\n    _pre_call_gl_callback(\"glClearColor\", (GLADapiproc) glad_glClearColor, 4, red, green, blue, alpha);\n    glad_glClearColor(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glClearColor\", (GLADapiproc) glad_glClearColor, 4, red, green, blue, alpha);\n}\nPFNGLCLEARCOLORPROC glad_debug_glClearColor = glad_debug_impl_glClearColor;\nPFNGLCLEARDEPTHPROC glad_glClearDepth = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearDepth(GLdouble depth) {\n    _pre_call_gl_callback(\"glClearDepth\", (GLADapiproc) glad_glClearDepth, 1, depth);\n    glad_glClearDepth(depth);\n    _post_call_gl_callback(NULL, \"glClearDepth\", (GLADapiproc) glad_glClearDepth, 1, depth);\n}\nPFNGLCLEARDEPTHPROC glad_debug_glClearDepth = glad_debug_impl_glClearDepth;\nPFNGLCLEARINDEXPROC glad_glClearIndex = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearIndex(GLfloat c) {\n    _pre_call_gl_callback(\"glClearIndex\", (GLADapiproc) glad_glClearIndex, 1, c);\n    glad_glClearIndex(c);\n    _post_call_gl_callback(NULL, \"glClearIndex\", (GLADapiproc) glad_glClearIndex, 1, c);\n}\nPFNGLCLEARINDEXPROC glad_debug_glClearIndex = glad_debug_impl_glClearIndex;\nPFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClearStencil(GLint s) {\n    _pre_call_gl_callback(\"glClearStencil\", (GLADapiproc) glad_glClearStencil, 1, s);\n    glad_glClearStencil(s);\n    _post_call_gl_callback(NULL, \"glClearStencil\", (GLADapiproc) glad_glClearStencil, 1, s);\n}\nPFNGLCLEARSTENCILPROC glad_debug_glClearStencil = glad_debug_impl_glClearStencil;\nPFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClientActiveTexture(GLenum texture) {\n    _pre_call_gl_callback(\"glClientActiveTexture\", (GLADapiproc) glad_glClientActiveTexture, 1, texture);\n    glad_glClientActiveTexture(texture);\n    _post_call_gl_callback(NULL, \"glClientActiveTexture\", (GLADapiproc) glad_glClientActiveTexture, 1, texture);\n}\nPFNGLCLIENTACTIVETEXTUREPROC glad_debug_glClientActiveTexture = glad_debug_impl_glClientActiveTexture;\nPFNGLCLIPPLANEPROC glad_glClipPlane = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glClipPlane(GLenum plane, const GLdouble * equation) {\n    _pre_call_gl_callback(\"glClipPlane\", (GLADapiproc) glad_glClipPlane, 2, plane, equation);\n    glad_glClipPlane(plane, equation);\n    _post_call_gl_callback(NULL, \"glClipPlane\", (GLADapiproc) glad_glClipPlane, 2, plane, equation);\n}\nPFNGLCLIPPLANEPROC glad_debug_glClipPlane = glad_debug_impl_glClipPlane;\nPFNGLCOLOR3BPROC glad_glColor3b = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3b(GLbyte red, GLbyte green, GLbyte blue) {\n    _pre_call_gl_callback(\"glColor3b\", (GLADapiproc) glad_glColor3b, 3, red, green, blue);\n    glad_glColor3b(red, green, blue);\n    _post_call_gl_callback(NULL, \"glColor3b\", (GLADapiproc) glad_glColor3b, 3, red, green, blue);\n}\nPFNGLCOLOR3BPROC glad_debug_glColor3b = glad_debug_impl_glColor3b;\nPFNGLCOLOR3BVPROC glad_glColor3bv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3bv(const GLbyte * v) {\n    _pre_call_gl_callback(\"glColor3bv\", (GLADapiproc) glad_glColor3bv, 1, v);\n    glad_glColor3bv(v);\n    _post_call_gl_callback(NULL, \"glColor3bv\", (GLADapiproc) glad_glColor3bv, 1, v);\n}\nPFNGLCOLOR3BVPROC glad_debug_glColor3bv = glad_debug_impl_glColor3bv;\nPFNGLCOLOR3DPROC glad_glColor3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3d(GLdouble red, GLdouble green, GLdouble blue) {\n    _pre_call_gl_callback(\"glColor3d\", (GLADapiproc) glad_glColor3d, 3, red, green, blue);\n    glad_glColor3d(red, green, blue);\n    _post_call_gl_callback(NULL, \"glColor3d\", (GLADapiproc) glad_glColor3d, 3, red, green, blue);\n}\nPFNGLCOLOR3DPROC glad_debug_glColor3d = glad_debug_impl_glColor3d;\nPFNGLCOLOR3DVPROC glad_glColor3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glColor3dv\", (GLADapiproc) glad_glColor3dv, 1, v);\n    glad_glColor3dv(v);\n    _post_call_gl_callback(NULL, \"glColor3dv\", (GLADapiproc) glad_glColor3dv, 1, v);\n}\nPFNGLCOLOR3DVPROC glad_debug_glColor3dv = glad_debug_impl_glColor3dv;\nPFNGLCOLOR3FPROC glad_glColor3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3f(GLfloat red, GLfloat green, GLfloat blue) {\n    _pre_call_gl_callback(\"glColor3f\", (GLADapiproc) glad_glColor3f, 3, red, green, blue);\n    glad_glColor3f(red, green, blue);\n    _post_call_gl_callback(NULL, \"glColor3f\", (GLADapiproc) glad_glColor3f, 3, red, green, blue);\n}\nPFNGLCOLOR3FPROC glad_debug_glColor3f = glad_debug_impl_glColor3f;\nPFNGLCOLOR3FVPROC glad_glColor3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glColor3fv\", (GLADapiproc) glad_glColor3fv, 1, v);\n    glad_glColor3fv(v);\n    _post_call_gl_callback(NULL, \"glColor3fv\", (GLADapiproc) glad_glColor3fv, 1, v);\n}\nPFNGLCOLOR3FVPROC glad_debug_glColor3fv = glad_debug_impl_glColor3fv;\nPFNGLCOLOR3IPROC glad_glColor3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3i(GLint red, GLint green, GLint blue) {\n    _pre_call_gl_callback(\"glColor3i\", (GLADapiproc) glad_glColor3i, 3, red, green, blue);\n    glad_glColor3i(red, green, blue);\n    _post_call_gl_callback(NULL, \"glColor3i\", (GLADapiproc) glad_glColor3i, 3, red, green, blue);\n}\nPFNGLCOLOR3IPROC glad_debug_glColor3i = glad_debug_impl_glColor3i;\nPFNGLCOLOR3IVPROC glad_glColor3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3iv(const GLint * v) {\n    _pre_call_gl_callback(\"glColor3iv\", (GLADapiproc) glad_glColor3iv, 1, v);\n    glad_glColor3iv(v);\n    _post_call_gl_callback(NULL, \"glColor3iv\", (GLADapiproc) glad_glColor3iv, 1, v);\n}\nPFNGLCOLOR3IVPROC glad_debug_glColor3iv = glad_debug_impl_glColor3iv;\nPFNGLCOLOR3SPROC glad_glColor3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3s(GLshort red, GLshort green, GLshort blue) {\n    _pre_call_gl_callback(\"glColor3s\", (GLADapiproc) glad_glColor3s, 3, red, green, blue);\n    glad_glColor3s(red, green, blue);\n    _post_call_gl_callback(NULL, \"glColor3s\", (GLADapiproc) glad_glColor3s, 3, red, green, blue);\n}\nPFNGLCOLOR3SPROC glad_debug_glColor3s = glad_debug_impl_glColor3s;\nPFNGLCOLOR3SVPROC glad_glColor3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glColor3sv\", (GLADapiproc) glad_glColor3sv, 1, v);\n    glad_glColor3sv(v);\n    _post_call_gl_callback(NULL, \"glColor3sv\", (GLADapiproc) glad_glColor3sv, 1, v);\n}\nPFNGLCOLOR3SVPROC glad_debug_glColor3sv = glad_debug_impl_glColor3sv;\nPFNGLCOLOR3UBPROC glad_glColor3ub = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3ub(GLubyte red, GLubyte green, GLubyte blue) {\n    _pre_call_gl_callback(\"glColor3ub\", (GLADapiproc) glad_glColor3ub, 3, red, green, blue);\n    glad_glColor3ub(red, green, blue);\n    _post_call_gl_callback(NULL, \"glColor3ub\", (GLADapiproc) glad_glColor3ub, 3, red, green, blue);\n}\nPFNGLCOLOR3UBPROC glad_debug_glColor3ub = glad_debug_impl_glColor3ub;\nPFNGLCOLOR3UBVPROC glad_glColor3ubv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3ubv(const GLubyte * v) {\n    _pre_call_gl_callback(\"glColor3ubv\", (GLADapiproc) glad_glColor3ubv, 1, v);\n    glad_glColor3ubv(v);\n    _post_call_gl_callback(NULL, \"glColor3ubv\", (GLADapiproc) glad_glColor3ubv, 1, v);\n}\nPFNGLCOLOR3UBVPROC glad_debug_glColor3ubv = glad_debug_impl_glColor3ubv;\nPFNGLCOLOR3UIPROC glad_glColor3ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3ui(GLuint red, GLuint green, GLuint blue) {\n    _pre_call_gl_callback(\"glColor3ui\", (GLADapiproc) glad_glColor3ui, 3, red, green, blue);\n    glad_glColor3ui(red, green, blue);\n    _post_call_gl_callback(NULL, \"glColor3ui\", (GLADapiproc) glad_glColor3ui, 3, red, green, blue);\n}\nPFNGLCOLOR3UIPROC glad_debug_glColor3ui = glad_debug_impl_glColor3ui;\nPFNGLCOLOR3UIVPROC glad_glColor3uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3uiv(const GLuint * v) {\n    _pre_call_gl_callback(\"glColor3uiv\", (GLADapiproc) glad_glColor3uiv, 1, v);\n    glad_glColor3uiv(v);\n    _post_call_gl_callback(NULL, \"glColor3uiv\", (GLADapiproc) glad_glColor3uiv, 1, v);\n}\nPFNGLCOLOR3UIVPROC glad_debug_glColor3uiv = glad_debug_impl_glColor3uiv;\nPFNGLCOLOR3USPROC glad_glColor3us = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3us(GLushort red, GLushort green, GLushort blue) {\n    _pre_call_gl_callback(\"glColor3us\", (GLADapiproc) glad_glColor3us, 3, red, green, blue);\n    glad_glColor3us(red, green, blue);\n    _post_call_gl_callback(NULL, \"glColor3us\", (GLADapiproc) glad_glColor3us, 3, red, green, blue);\n}\nPFNGLCOLOR3USPROC glad_debug_glColor3us = glad_debug_impl_glColor3us;\nPFNGLCOLOR3USVPROC glad_glColor3usv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor3usv(const GLushort * v) {\n    _pre_call_gl_callback(\"glColor3usv\", (GLADapiproc) glad_glColor3usv, 1, v);\n    glad_glColor3usv(v);\n    _post_call_gl_callback(NULL, \"glColor3usv\", (GLADapiproc) glad_glColor3usv, 1, v);\n}\nPFNGLCOLOR3USVPROC glad_debug_glColor3usv = glad_debug_impl_glColor3usv;\nPFNGLCOLOR4BPROC glad_glColor4b = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4b(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha) {\n    _pre_call_gl_callback(\"glColor4b\", (GLADapiproc) glad_glColor4b, 4, red, green, blue, alpha);\n    glad_glColor4b(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColor4b\", (GLADapiproc) glad_glColor4b, 4, red, green, blue, alpha);\n}\nPFNGLCOLOR4BPROC glad_debug_glColor4b = glad_debug_impl_glColor4b;\nPFNGLCOLOR4BVPROC glad_glColor4bv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4bv(const GLbyte * v) {\n    _pre_call_gl_callback(\"glColor4bv\", (GLADapiproc) glad_glColor4bv, 1, v);\n    glad_glColor4bv(v);\n    _post_call_gl_callback(NULL, \"glColor4bv\", (GLADapiproc) glad_glColor4bv, 1, v);\n}\nPFNGLCOLOR4BVPROC glad_debug_glColor4bv = glad_debug_impl_glColor4bv;\nPFNGLCOLOR4DPROC glad_glColor4d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4d(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha) {\n    _pre_call_gl_callback(\"glColor4d\", (GLADapiproc) glad_glColor4d, 4, red, green, blue, alpha);\n    glad_glColor4d(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColor4d\", (GLADapiproc) glad_glColor4d, 4, red, green, blue, alpha);\n}\nPFNGLCOLOR4DPROC glad_debug_glColor4d = glad_debug_impl_glColor4d;\nPFNGLCOLOR4DVPROC glad_glColor4dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glColor4dv\", (GLADapiproc) glad_glColor4dv, 1, v);\n    glad_glColor4dv(v);\n    _post_call_gl_callback(NULL, \"glColor4dv\", (GLADapiproc) glad_glColor4dv, 1, v);\n}\nPFNGLCOLOR4DVPROC glad_debug_glColor4dv = glad_debug_impl_glColor4dv;\nPFNGLCOLOR4FPROC glad_glColor4f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {\n    _pre_call_gl_callback(\"glColor4f\", (GLADapiproc) glad_glColor4f, 4, red, green, blue, alpha);\n    glad_glColor4f(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColor4f\", (GLADapiproc) glad_glColor4f, 4, red, green, blue, alpha);\n}\nPFNGLCOLOR4FPROC glad_debug_glColor4f = glad_debug_impl_glColor4f;\nPFNGLCOLOR4FVPROC glad_glColor4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glColor4fv\", (GLADapiproc) glad_glColor4fv, 1, v);\n    glad_glColor4fv(v);\n    _post_call_gl_callback(NULL, \"glColor4fv\", (GLADapiproc) glad_glColor4fv, 1, v);\n}\nPFNGLCOLOR4FVPROC glad_debug_glColor4fv = glad_debug_impl_glColor4fv;\nPFNGLCOLOR4IPROC glad_glColor4i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4i(GLint red, GLint green, GLint blue, GLint alpha) {\n    _pre_call_gl_callback(\"glColor4i\", (GLADapiproc) glad_glColor4i, 4, red, green, blue, alpha);\n    glad_glColor4i(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColor4i\", (GLADapiproc) glad_glColor4i, 4, red, green, blue, alpha);\n}\nPFNGLCOLOR4IPROC glad_debug_glColor4i = glad_debug_impl_glColor4i;\nPFNGLCOLOR4IVPROC glad_glColor4iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4iv(const GLint * v) {\n    _pre_call_gl_callback(\"glColor4iv\", (GLADapiproc) glad_glColor4iv, 1, v);\n    glad_glColor4iv(v);\n    _post_call_gl_callback(NULL, \"glColor4iv\", (GLADapiproc) glad_glColor4iv, 1, v);\n}\nPFNGLCOLOR4IVPROC glad_debug_glColor4iv = glad_debug_impl_glColor4iv;\nPFNGLCOLOR4SPROC glad_glColor4s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4s(GLshort red, GLshort green, GLshort blue, GLshort alpha) {\n    _pre_call_gl_callback(\"glColor4s\", (GLADapiproc) glad_glColor4s, 4, red, green, blue, alpha);\n    glad_glColor4s(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColor4s\", (GLADapiproc) glad_glColor4s, 4, red, green, blue, alpha);\n}\nPFNGLCOLOR4SPROC glad_debug_glColor4s = glad_debug_impl_glColor4s;\nPFNGLCOLOR4SVPROC glad_glColor4sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glColor4sv\", (GLADapiproc) glad_glColor4sv, 1, v);\n    glad_glColor4sv(v);\n    _post_call_gl_callback(NULL, \"glColor4sv\", (GLADapiproc) glad_glColor4sv, 1, v);\n}\nPFNGLCOLOR4SVPROC glad_debug_glColor4sv = glad_debug_impl_glColor4sv;\nPFNGLCOLOR4UBPROC glad_glColor4ub = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) {\n    _pre_call_gl_callback(\"glColor4ub\", (GLADapiproc) glad_glColor4ub, 4, red, green, blue, alpha);\n    glad_glColor4ub(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColor4ub\", (GLADapiproc) glad_glColor4ub, 4, red, green, blue, alpha);\n}\nPFNGLCOLOR4UBPROC glad_debug_glColor4ub = glad_debug_impl_glColor4ub;\nPFNGLCOLOR4UBVPROC glad_glColor4ubv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4ubv(const GLubyte * v) {\n    _pre_call_gl_callback(\"glColor4ubv\", (GLADapiproc) glad_glColor4ubv, 1, v);\n    glad_glColor4ubv(v);\n    _post_call_gl_callback(NULL, \"glColor4ubv\", (GLADapiproc) glad_glColor4ubv, 1, v);\n}\nPFNGLCOLOR4UBVPROC glad_debug_glColor4ubv = glad_debug_impl_glColor4ubv;\nPFNGLCOLOR4UIPROC glad_glColor4ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4ui(GLuint red, GLuint green, GLuint blue, GLuint alpha) {\n    _pre_call_gl_callback(\"glColor4ui\", (GLADapiproc) glad_glColor4ui, 4, red, green, blue, alpha);\n    glad_glColor4ui(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColor4ui\", (GLADapiproc) glad_glColor4ui, 4, red, green, blue, alpha);\n}\nPFNGLCOLOR4UIPROC glad_debug_glColor4ui = glad_debug_impl_glColor4ui;\nPFNGLCOLOR4UIVPROC glad_glColor4uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4uiv(const GLuint * v) {\n    _pre_call_gl_callback(\"glColor4uiv\", (GLADapiproc) glad_glColor4uiv, 1, v);\n    glad_glColor4uiv(v);\n    _post_call_gl_callback(NULL, \"glColor4uiv\", (GLADapiproc) glad_glColor4uiv, 1, v);\n}\nPFNGLCOLOR4UIVPROC glad_debug_glColor4uiv = glad_debug_impl_glColor4uiv;\nPFNGLCOLOR4USPROC glad_glColor4us = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4us(GLushort red, GLushort green, GLushort blue, GLushort alpha) {\n    _pre_call_gl_callback(\"glColor4us\", (GLADapiproc) glad_glColor4us, 4, red, green, blue, alpha);\n    glad_glColor4us(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColor4us\", (GLADapiproc) glad_glColor4us, 4, red, green, blue, alpha);\n}\nPFNGLCOLOR4USPROC glad_debug_glColor4us = glad_debug_impl_glColor4us;\nPFNGLCOLOR4USVPROC glad_glColor4usv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColor4usv(const GLushort * v) {\n    _pre_call_gl_callback(\"glColor4usv\", (GLADapiproc) glad_glColor4usv, 1, v);\n    glad_glColor4usv(v);\n    _post_call_gl_callback(NULL, \"glColor4usv\", (GLADapiproc) glad_glColor4usv, 1, v);\n}\nPFNGLCOLOR4USVPROC glad_debug_glColor4usv = glad_debug_impl_glColor4usv;\nPFNGLCOLORMASKPROC glad_glColorMask = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {\n    _pre_call_gl_callback(\"glColorMask\", (GLADapiproc) glad_glColorMask, 4, red, green, blue, alpha);\n    glad_glColorMask(red, green, blue, alpha);\n    _post_call_gl_callback(NULL, \"glColorMask\", (GLADapiproc) glad_glColorMask, 4, red, green, blue, alpha);\n}\nPFNGLCOLORMASKPROC glad_debug_glColorMask = glad_debug_impl_glColorMask;\nPFNGLCOLORMASKIPROC glad_glColorMaski = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColorMaski(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) {\n    _pre_call_gl_callback(\"glColorMaski\", (GLADapiproc) glad_glColorMaski, 5, index, r, g, b, a);\n    glad_glColorMaski(index, r, g, b, a);\n    _post_call_gl_callback(NULL, \"glColorMaski\", (GLADapiproc) glad_glColorMaski, 5, index, r, g, b, a);\n}\nPFNGLCOLORMASKIPROC glad_debug_glColorMaski = glad_debug_impl_glColorMaski;\nPFNGLCOLORMATERIALPROC glad_glColorMaterial = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColorMaterial(GLenum face, GLenum mode) {\n    _pre_call_gl_callback(\"glColorMaterial\", (GLADapiproc) glad_glColorMaterial, 2, face, mode);\n    glad_glColorMaterial(face, mode);\n    _post_call_gl_callback(NULL, \"glColorMaterial\", (GLADapiproc) glad_glColorMaterial, 2, face, mode);\n}\nPFNGLCOLORMATERIALPROC glad_debug_glColorMaterial = glad_debug_impl_glColorMaterial;\nPFNGLCOLORPOINTERPROC glad_glColorPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glColorPointer(GLint size, GLenum type, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glColorPointer\", (GLADapiproc) glad_glColorPointer, 4, size, type, stride, pointer);\n    glad_glColorPointer(size, type, stride, pointer);\n    _post_call_gl_callback(NULL, \"glColorPointer\", (GLADapiproc) glad_glColorPointer, 4, size, type, stride, pointer);\n}\nPFNGLCOLORPOINTERPROC glad_debug_glColorPointer = glad_debug_impl_glColorPointer;\nPFNGLCOMPILESHADERPROC glad_glCompileShader = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCompileShader(GLuint shader) {\n    _pre_call_gl_callback(\"glCompileShader\", (GLADapiproc) glad_glCompileShader, 1, shader);\n    glad_glCompileShader(shader);\n    _post_call_gl_callback(NULL, \"glCompileShader\", (GLADapiproc) glad_glCompileShader, 1, shader);\n}\nPFNGLCOMPILESHADERPROC glad_debug_glCompileShader = glad_debug_impl_glCompileShader;\nPFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCompressedTexImage1D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data) {\n    _pre_call_gl_callback(\"glCompressedTexImage1D\", (GLADapiproc) glad_glCompressedTexImage1D, 7, target, level, internalformat, width, border, imageSize, data);\n    glad_glCompressedTexImage1D(target, level, internalformat, width, border, imageSize, data);\n    _post_call_gl_callback(NULL, \"glCompressedTexImage1D\", (GLADapiproc) glad_glCompressedTexImage1D, 7, target, level, internalformat, width, border, imageSize, data);\n}\nPFNGLCOMPRESSEDTEXIMAGE1DPROC glad_debug_glCompressedTexImage1D = glad_debug_impl_glCompressedTexImage1D;\nPFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data) {\n    _pre_call_gl_callback(\"glCompressedTexImage2D\", (GLADapiproc) glad_glCompressedTexImage2D, 8, target, level, internalformat, width, height, border, imageSize, data);\n    glad_glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data);\n    _post_call_gl_callback(NULL, \"glCompressedTexImage2D\", (GLADapiproc) glad_glCompressedTexImage2D, 8, target, level, internalformat, width, height, border, imageSize, data);\n}\nPFNGLCOMPRESSEDTEXIMAGE2DPROC glad_debug_glCompressedTexImage2D = glad_debug_impl_glCompressedTexImage2D;\nPFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCompressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data) {\n    _pre_call_gl_callback(\"glCompressedTexImage3D\", (GLADapiproc) glad_glCompressedTexImage3D, 9, target, level, internalformat, width, height, depth, border, imageSize, data);\n    glad_glCompressedTexImage3D(target, level, internalformat, width, height, depth, border, imageSize, data);\n    _post_call_gl_callback(NULL, \"glCompressedTexImage3D\", (GLADapiproc) glad_glCompressedTexImage3D, 9, target, level, internalformat, width, height, depth, border, imageSize, data);\n}\nPFNGLCOMPRESSEDTEXIMAGE3DPROC glad_debug_glCompressedTexImage3D = glad_debug_impl_glCompressedTexImage3D;\nPFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCompressedTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data) {\n    _pre_call_gl_callback(\"glCompressedTexSubImage1D\", (GLADapiproc) glad_glCompressedTexSubImage1D, 7, target, level, xoffset, width, format, imageSize, data);\n    glad_glCompressedTexSubImage1D(target, level, xoffset, width, format, imageSize, data);\n    _post_call_gl_callback(NULL, \"glCompressedTexSubImage1D\", (GLADapiproc) glad_glCompressedTexSubImage1D, 7, target, level, xoffset, width, format, imageSize, data);\n}\nPFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_debug_glCompressedTexSubImage1D = glad_debug_impl_glCompressedTexSubImage1D;\nPFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data) {\n    _pre_call_gl_callback(\"glCompressedTexSubImage2D\", (GLADapiproc) glad_glCompressedTexSubImage2D, 9, target, level, xoffset, yoffset, width, height, format, imageSize, data);\n    glad_glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);\n    _post_call_gl_callback(NULL, \"glCompressedTexSubImage2D\", (GLADapiproc) glad_glCompressedTexSubImage2D, 9, target, level, xoffset, yoffset, width, height, format, imageSize, data);\n}\nPFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_debug_glCompressedTexSubImage2D = glad_debug_impl_glCompressedTexSubImage2D;\nPFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data) {\n    _pre_call_gl_callback(\"glCompressedTexSubImage3D\", (GLADapiproc) glad_glCompressedTexSubImage3D, 11, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);\n    glad_glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);\n    _post_call_gl_callback(NULL, \"glCompressedTexSubImage3D\", (GLADapiproc) glad_glCompressedTexSubImage3D, 11, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);\n}\nPFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_debug_glCompressedTexSubImage3D = glad_debug_impl_glCompressedTexSubImage3D;\nPFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) {\n    _pre_call_gl_callback(\"glCopyBufferSubData\", (GLADapiproc) glad_glCopyBufferSubData, 5, readTarget, writeTarget, readOffset, writeOffset, size);\n    glad_glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);\n    _post_call_gl_callback(NULL, \"glCopyBufferSubData\", (GLADapiproc) glad_glCopyBufferSubData, 5, readTarget, writeTarget, readOffset, writeOffset, size);\n}\nPFNGLCOPYBUFFERSUBDATAPROC glad_debug_glCopyBufferSubData = glad_debug_impl_glCopyBufferSubData;\nPFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {\n    _pre_call_gl_callback(\"glCopyImageSubData\", (GLADapiproc) glad_glCopyImageSubData, 15, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth);\n    glad_glCopyImageSubData(srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth);\n    _post_call_gl_callback(NULL, \"glCopyImageSubData\", (GLADapiproc) glad_glCopyImageSubData, 15, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth);\n}\nPFNGLCOPYIMAGESUBDATAPROC glad_debug_glCopyImageSubData = glad_debug_impl_glCopyImageSubData;\nPFNGLCOPYPIXELSPROC glad_glCopyPixels = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type) {\n    _pre_call_gl_callback(\"glCopyPixels\", (GLADapiproc) glad_glCopyPixels, 5, x, y, width, height, type);\n    glad_glCopyPixels(x, y, width, height, type);\n    _post_call_gl_callback(NULL, \"glCopyPixels\", (GLADapiproc) glad_glCopyPixels, 5, x, y, width, height, type);\n}\nPFNGLCOPYPIXELSPROC glad_debug_glCopyPixels = glad_debug_impl_glCopyPixels;\nPFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCopyTexImage1D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border) {\n    _pre_call_gl_callback(\"glCopyTexImage1D\", (GLADapiproc) glad_glCopyTexImage1D, 7, target, level, internalformat, x, y, width, border);\n    glad_glCopyTexImage1D(target, level, internalformat, x, y, width, border);\n    _post_call_gl_callback(NULL, \"glCopyTexImage1D\", (GLADapiproc) glad_glCopyTexImage1D, 7, target, level, internalformat, x, y, width, border);\n}\nPFNGLCOPYTEXIMAGE1DPROC glad_debug_glCopyTexImage1D = glad_debug_impl_glCopyTexImage1D;\nPFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {\n    _pre_call_gl_callback(\"glCopyTexImage2D\", (GLADapiproc) glad_glCopyTexImage2D, 8, target, level, internalformat, x, y, width, height, border);\n    glad_glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);\n    _post_call_gl_callback(NULL, \"glCopyTexImage2D\", (GLADapiproc) glad_glCopyTexImage2D, 8, target, level, internalformat, x, y, width, height, border);\n}\nPFNGLCOPYTEXIMAGE2DPROC glad_debug_glCopyTexImage2D = glad_debug_impl_glCopyTexImage2D;\nPFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) {\n    _pre_call_gl_callback(\"glCopyTexSubImage1D\", (GLADapiproc) glad_glCopyTexSubImage1D, 6, target, level, xoffset, x, y, width);\n    glad_glCopyTexSubImage1D(target, level, xoffset, x, y, width);\n    _post_call_gl_callback(NULL, \"glCopyTexSubImage1D\", (GLADapiproc) glad_glCopyTexSubImage1D, 6, target, level, xoffset, x, y, width);\n}\nPFNGLCOPYTEXSUBIMAGE1DPROC glad_debug_glCopyTexSubImage1D = glad_debug_impl_glCopyTexSubImage1D;\nPFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {\n    _pre_call_gl_callback(\"glCopyTexSubImage2D\", (GLADapiproc) glad_glCopyTexSubImage2D, 8, target, level, xoffset, yoffset, x, y, width, height);\n    glad_glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);\n    _post_call_gl_callback(NULL, \"glCopyTexSubImage2D\", (GLADapiproc) glad_glCopyTexSubImage2D, 8, target, level, xoffset, yoffset, x, y, width, height);\n}\nPFNGLCOPYTEXSUBIMAGE2DPROC glad_debug_glCopyTexSubImage2D = glad_debug_impl_glCopyTexSubImage2D;\nPFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) {\n    _pre_call_gl_callback(\"glCopyTexSubImage3D\", (GLADapiproc) glad_glCopyTexSubImage3D, 9, target, level, xoffset, yoffset, zoffset, x, y, width, height);\n    glad_glCopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height);\n    _post_call_gl_callback(NULL, \"glCopyTexSubImage3D\", (GLADapiproc) glad_glCopyTexSubImage3D, 9, target, level, xoffset, yoffset, zoffset, x, y, width, height);\n}\nPFNGLCOPYTEXSUBIMAGE3DPROC glad_debug_glCopyTexSubImage3D = glad_debug_impl_glCopyTexSubImage3D;\nPFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL;\nstatic GLuint GLAD_API_PTR glad_debug_impl_glCreateProgram(void) {\n    GLuint ret;\n    _pre_call_gl_callback(\"glCreateProgram\", (GLADapiproc) glad_glCreateProgram, 0);\n    ret = glad_glCreateProgram();\n    _post_call_gl_callback((void*) &ret, \"glCreateProgram\", (GLADapiproc) glad_glCreateProgram, 0);\n    return ret;\n}\nPFNGLCREATEPROGRAMPROC glad_debug_glCreateProgram = glad_debug_impl_glCreateProgram;\nPFNGLCREATESHADERPROC glad_glCreateShader = NULL;\nstatic GLuint GLAD_API_PTR glad_debug_impl_glCreateShader(GLenum type) {\n    GLuint ret;\n    _pre_call_gl_callback(\"glCreateShader\", (GLADapiproc) glad_glCreateShader, 1, type);\n    ret = glad_glCreateShader(type);\n    _post_call_gl_callback((void*) &ret, \"glCreateShader\", (GLADapiproc) glad_glCreateShader, 1, type);\n    return ret;\n}\nPFNGLCREATESHADERPROC glad_debug_glCreateShader = glad_debug_impl_glCreateShader;\nPFNGLCULLFACEPROC glad_glCullFace = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glCullFace(GLenum mode) {\n    _pre_call_gl_callback(\"glCullFace\", (GLADapiproc) glad_glCullFace, 1, mode);\n    glad_glCullFace(mode);\n    _post_call_gl_callback(NULL, \"glCullFace\", (GLADapiproc) glad_glCullFace, 1, mode);\n}\nPFNGLCULLFACEPROC glad_debug_glCullFace = glad_debug_impl_glCullFace;\nPFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDebugMessageCallback(GLDEBUGPROC callback, const void * userParam) {\n    _pre_call_gl_callback(\"glDebugMessageCallback\", (GLADapiproc) glad_glDebugMessageCallback, 2, callback, userParam);\n    glad_glDebugMessageCallback(callback, userParam);\n    _post_call_gl_callback(NULL, \"glDebugMessageCallback\", (GLADapiproc) glad_glDebugMessageCallback, 2, callback, userParam);\n}\nPFNGLDEBUGMESSAGECALLBACKPROC glad_debug_glDebugMessageCallback = glad_debug_impl_glDebugMessageCallback;\nPFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDebugMessageControl(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled) {\n    _pre_call_gl_callback(\"glDebugMessageControl\", (GLADapiproc) glad_glDebugMessageControl, 6, source, type, severity, count, ids, enabled);\n    glad_glDebugMessageControl(source, type, severity, count, ids, enabled);\n    _post_call_gl_callback(NULL, \"glDebugMessageControl\", (GLADapiproc) glad_glDebugMessageControl, 6, source, type, severity, count, ids, enabled);\n}\nPFNGLDEBUGMESSAGECONTROLPROC glad_debug_glDebugMessageControl = glad_debug_impl_glDebugMessageControl;\nPFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDebugMessageInsert(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf) {\n    _pre_call_gl_callback(\"glDebugMessageInsert\", (GLADapiproc) glad_glDebugMessageInsert, 6, source, type, id, severity, length, buf);\n    glad_glDebugMessageInsert(source, type, id, severity, length, buf);\n    _post_call_gl_callback(NULL, \"glDebugMessageInsert\", (GLADapiproc) glad_glDebugMessageInsert, 6, source, type, id, severity, length, buf);\n}\nPFNGLDEBUGMESSAGEINSERTPROC glad_debug_glDebugMessageInsert = glad_debug_impl_glDebugMessageInsert;\nPFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteBuffers(GLsizei n, const GLuint * buffers) {\n    _pre_call_gl_callback(\"glDeleteBuffers\", (GLADapiproc) glad_glDeleteBuffers, 2, n, buffers);\n    glad_glDeleteBuffers(n, buffers);\n    _post_call_gl_callback(NULL, \"glDeleteBuffers\", (GLADapiproc) glad_glDeleteBuffers, 2, n, buffers);\n}\nPFNGLDELETEBUFFERSPROC glad_debug_glDeleteBuffers = glad_debug_impl_glDeleteBuffers;\nPFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteFramebuffers(GLsizei n, const GLuint * framebuffers) {\n    _pre_call_gl_callback(\"glDeleteFramebuffers\", (GLADapiproc) glad_glDeleteFramebuffers, 2, n, framebuffers);\n    glad_glDeleteFramebuffers(n, framebuffers);\n    _post_call_gl_callback(NULL, \"glDeleteFramebuffers\", (GLADapiproc) glad_glDeleteFramebuffers, 2, n, framebuffers);\n}\nPFNGLDELETEFRAMEBUFFERSPROC glad_debug_glDeleteFramebuffers = glad_debug_impl_glDeleteFramebuffers;\nPFNGLDELETELISTSPROC glad_glDeleteLists = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteLists(GLuint list, GLsizei range) {\n    _pre_call_gl_callback(\"glDeleteLists\", (GLADapiproc) glad_glDeleteLists, 2, list, range);\n    glad_glDeleteLists(list, range);\n    _post_call_gl_callback(NULL, \"glDeleteLists\", (GLADapiproc) glad_glDeleteLists, 2, list, range);\n}\nPFNGLDELETELISTSPROC glad_debug_glDeleteLists = glad_debug_impl_glDeleteLists;\nPFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteProgram(GLuint program) {\n    _pre_call_gl_callback(\"glDeleteProgram\", (GLADapiproc) glad_glDeleteProgram, 1, program);\n    glad_glDeleteProgram(program);\n    _post_call_gl_callback(NULL, \"glDeleteProgram\", (GLADapiproc) glad_glDeleteProgram, 1, program);\n}\nPFNGLDELETEPROGRAMPROC glad_debug_glDeleteProgram = glad_debug_impl_glDeleteProgram;\nPFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteQueries(GLsizei n, const GLuint * ids) {\n    _pre_call_gl_callback(\"glDeleteQueries\", (GLADapiproc) glad_glDeleteQueries, 2, n, ids);\n    glad_glDeleteQueries(n, ids);\n    _post_call_gl_callback(NULL, \"glDeleteQueries\", (GLADapiproc) glad_glDeleteQueries, 2, n, ids);\n}\nPFNGLDELETEQUERIESPROC glad_debug_glDeleteQueries = glad_debug_impl_glDeleteQueries;\nPFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteRenderbuffers(GLsizei n, const GLuint * renderbuffers) {\n    _pre_call_gl_callback(\"glDeleteRenderbuffers\", (GLADapiproc) glad_glDeleteRenderbuffers, 2, n, renderbuffers);\n    glad_glDeleteRenderbuffers(n, renderbuffers);\n    _post_call_gl_callback(NULL, \"glDeleteRenderbuffers\", (GLADapiproc) glad_glDeleteRenderbuffers, 2, n, renderbuffers);\n}\nPFNGLDELETERENDERBUFFERSPROC glad_debug_glDeleteRenderbuffers = glad_debug_impl_glDeleteRenderbuffers;\nPFNGLDELETESHADERPROC glad_glDeleteShader = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteShader(GLuint shader) {\n    _pre_call_gl_callback(\"glDeleteShader\", (GLADapiproc) glad_glDeleteShader, 1, shader);\n    glad_glDeleteShader(shader);\n    _post_call_gl_callback(NULL, \"glDeleteShader\", (GLADapiproc) glad_glDeleteShader, 1, shader);\n}\nPFNGLDELETESHADERPROC glad_debug_glDeleteShader = glad_debug_impl_glDeleteShader;\nPFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteTextures(GLsizei n, const GLuint * textures) {\n    _pre_call_gl_callback(\"glDeleteTextures\", (GLADapiproc) glad_glDeleteTextures, 2, n, textures);\n    glad_glDeleteTextures(n, textures);\n    _post_call_gl_callback(NULL, \"glDeleteTextures\", (GLADapiproc) glad_glDeleteTextures, 2, n, textures);\n}\nPFNGLDELETETEXTURESPROC glad_debug_glDeleteTextures = glad_debug_impl_glDeleteTextures;\nPFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDeleteVertexArrays(GLsizei n, const GLuint * arrays) {\n    _pre_call_gl_callback(\"glDeleteVertexArrays\", (GLADapiproc) glad_glDeleteVertexArrays, 2, n, arrays);\n    glad_glDeleteVertexArrays(n, arrays);\n    _post_call_gl_callback(NULL, \"glDeleteVertexArrays\", (GLADapiproc) glad_glDeleteVertexArrays, 2, n, arrays);\n}\nPFNGLDELETEVERTEXARRAYSPROC glad_debug_glDeleteVertexArrays = glad_debug_impl_glDeleteVertexArrays;\nPFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDepthFunc(GLenum func) {\n    _pre_call_gl_callback(\"glDepthFunc\", (GLADapiproc) glad_glDepthFunc, 1, func);\n    glad_glDepthFunc(func);\n    _post_call_gl_callback(NULL, \"glDepthFunc\", (GLADapiproc) glad_glDepthFunc, 1, func);\n}\nPFNGLDEPTHFUNCPROC glad_debug_glDepthFunc = glad_debug_impl_glDepthFunc;\nPFNGLDEPTHMASKPROC glad_glDepthMask = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDepthMask(GLboolean flag) {\n    _pre_call_gl_callback(\"glDepthMask\", (GLADapiproc) glad_glDepthMask, 1, flag);\n    glad_glDepthMask(flag);\n    _post_call_gl_callback(NULL, \"glDepthMask\", (GLADapiproc) glad_glDepthMask, 1, flag);\n}\nPFNGLDEPTHMASKPROC glad_debug_glDepthMask = glad_debug_impl_glDepthMask;\nPFNGLDEPTHRANGEPROC glad_glDepthRange = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDepthRange(GLdouble n, GLdouble f) {\n    _pre_call_gl_callback(\"glDepthRange\", (GLADapiproc) glad_glDepthRange, 2, n, f);\n    glad_glDepthRange(n, f);\n    _post_call_gl_callback(NULL, \"glDepthRange\", (GLADapiproc) glad_glDepthRange, 2, n, f);\n}\nPFNGLDEPTHRANGEPROC glad_debug_glDepthRange = glad_debug_impl_glDepthRange;\nPFNGLDETACHSHADERPROC glad_glDetachShader = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDetachShader(GLuint program, GLuint shader) {\n    _pre_call_gl_callback(\"glDetachShader\", (GLADapiproc) glad_glDetachShader, 2, program, shader);\n    glad_glDetachShader(program, shader);\n    _post_call_gl_callback(NULL, \"glDetachShader\", (GLADapiproc) glad_glDetachShader, 2, program, shader);\n}\nPFNGLDETACHSHADERPROC glad_debug_glDetachShader = glad_debug_impl_glDetachShader;\nPFNGLDISABLEPROC glad_glDisable = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDisable(GLenum cap) {\n    _pre_call_gl_callback(\"glDisable\", (GLADapiproc) glad_glDisable, 1, cap);\n    glad_glDisable(cap);\n    _post_call_gl_callback(NULL, \"glDisable\", (GLADapiproc) glad_glDisable, 1, cap);\n}\nPFNGLDISABLEPROC glad_debug_glDisable = glad_debug_impl_glDisable;\nPFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDisableClientState(GLenum array) {\n    _pre_call_gl_callback(\"glDisableClientState\", (GLADapiproc) glad_glDisableClientState, 1, array);\n    glad_glDisableClientState(array);\n    _post_call_gl_callback(NULL, \"glDisableClientState\", (GLADapiproc) glad_glDisableClientState, 1, array);\n}\nPFNGLDISABLECLIENTSTATEPROC glad_debug_glDisableClientState = glad_debug_impl_glDisableClientState;\nPFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDisableVertexAttribArray(GLuint index) {\n    _pre_call_gl_callback(\"glDisableVertexAttribArray\", (GLADapiproc) glad_glDisableVertexAttribArray, 1, index);\n    glad_glDisableVertexAttribArray(index);\n    _post_call_gl_callback(NULL, \"glDisableVertexAttribArray\", (GLADapiproc) glad_glDisableVertexAttribArray, 1, index);\n}\nPFNGLDISABLEVERTEXATTRIBARRAYPROC glad_debug_glDisableVertexAttribArray = glad_debug_impl_glDisableVertexAttribArray;\nPFNGLDISABLEIPROC glad_glDisablei = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDisablei(GLenum target, GLuint index) {\n    _pre_call_gl_callback(\"glDisablei\", (GLADapiproc) glad_glDisablei, 2, target, index);\n    glad_glDisablei(target, index);\n    _post_call_gl_callback(NULL, \"glDisablei\", (GLADapiproc) glad_glDisablei, 2, target, index);\n}\nPFNGLDISABLEIPROC glad_debug_glDisablei = glad_debug_impl_glDisablei;\nPFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDrawArrays(GLenum mode, GLint first, GLsizei count) {\n    _pre_call_gl_callback(\"glDrawArrays\", (GLADapiproc) glad_glDrawArrays, 3, mode, first, count);\n    glad_glDrawArrays(mode, first, count);\n    _post_call_gl_callback(NULL, \"glDrawArrays\", (GLADapiproc) glad_glDrawArrays, 3, mode, first, count);\n}\nPFNGLDRAWARRAYSPROC glad_debug_glDrawArrays = glad_debug_impl_glDrawArrays;\nPFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {\n    _pre_call_gl_callback(\"glDrawArraysInstanced\", (GLADapiproc) glad_glDrawArraysInstanced, 4, mode, first, count, instancecount);\n    glad_glDrawArraysInstanced(mode, first, count, instancecount);\n    _post_call_gl_callback(NULL, \"glDrawArraysInstanced\", (GLADapiproc) glad_glDrawArraysInstanced, 4, mode, first, count, instancecount);\n}\nPFNGLDRAWARRAYSINSTANCEDPROC glad_debug_glDrawArraysInstanced = glad_debug_impl_glDrawArraysInstanced;\nPFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDrawBuffer(GLenum buf) {\n    _pre_call_gl_callback(\"glDrawBuffer\", (GLADapiproc) glad_glDrawBuffer, 1, buf);\n    glad_glDrawBuffer(buf);\n    _post_call_gl_callback(NULL, \"glDrawBuffer\", (GLADapiproc) glad_glDrawBuffer, 1, buf);\n}\nPFNGLDRAWBUFFERPROC glad_debug_glDrawBuffer = glad_debug_impl_glDrawBuffer;\nPFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDrawBuffers(GLsizei n, const GLenum * bufs) {\n    _pre_call_gl_callback(\"glDrawBuffers\", (GLADapiproc) glad_glDrawBuffers, 2, n, bufs);\n    glad_glDrawBuffers(n, bufs);\n    _post_call_gl_callback(NULL, \"glDrawBuffers\", (GLADapiproc) glad_glDrawBuffers, 2, n, bufs);\n}\nPFNGLDRAWBUFFERSPROC glad_debug_glDrawBuffers = glad_debug_impl_glDrawBuffers;\nPFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDrawElements(GLenum mode, GLsizei count, GLenum type, const void * indices) {\n    _pre_call_gl_callback(\"glDrawElements\", (GLADapiproc) glad_glDrawElements, 4, mode, count, type, indices);\n    glad_glDrawElements(mode, count, type, indices);\n    _post_call_gl_callback(NULL, \"glDrawElements\", (GLADapiproc) glad_glDrawElements, 4, mode, count, type, indices);\n}\nPFNGLDRAWELEMENTSPROC glad_debug_glDrawElements = glad_debug_impl_glDrawElements;\nPFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount) {\n    _pre_call_gl_callback(\"glDrawElementsInstanced\", (GLADapiproc) glad_glDrawElementsInstanced, 5, mode, count, type, indices, instancecount);\n    glad_glDrawElementsInstanced(mode, count, type, indices, instancecount);\n    _post_call_gl_callback(NULL, \"glDrawElementsInstanced\", (GLADapiproc) glad_glDrawElementsInstanced, 5, mode, count, type, indices, instancecount);\n}\nPFNGLDRAWELEMENTSINSTANCEDPROC glad_debug_glDrawElementsInstanced = glad_debug_impl_glDrawElementsInstanced;\nPFNGLDRAWPIXELSPROC glad_glDrawPixels = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels) {\n    _pre_call_gl_callback(\"glDrawPixels\", (GLADapiproc) glad_glDrawPixels, 5, width, height, format, type, pixels);\n    glad_glDrawPixels(width, height, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glDrawPixels\", (GLADapiproc) glad_glDrawPixels, 5, width, height, format, type, pixels);\n}\nPFNGLDRAWPIXELSPROC glad_debug_glDrawPixels = glad_debug_impl_glDrawPixels;\nPFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices) {\n    _pre_call_gl_callback(\"glDrawRangeElements\", (GLADapiproc) glad_glDrawRangeElements, 6, mode, start, end, count, type, indices);\n    glad_glDrawRangeElements(mode, start, end, count, type, indices);\n    _post_call_gl_callback(NULL, \"glDrawRangeElements\", (GLADapiproc) glad_glDrawRangeElements, 6, mode, start, end, count, type, indices);\n}\nPFNGLDRAWRANGEELEMENTSPROC glad_debug_glDrawRangeElements = glad_debug_impl_glDrawRangeElements;\nPFNGLEDGEFLAGPROC glad_glEdgeFlag = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEdgeFlag(GLboolean flag) {\n    _pre_call_gl_callback(\"glEdgeFlag\", (GLADapiproc) glad_glEdgeFlag, 1, flag);\n    glad_glEdgeFlag(flag);\n    _post_call_gl_callback(NULL, \"glEdgeFlag\", (GLADapiproc) glad_glEdgeFlag, 1, flag);\n}\nPFNGLEDGEFLAGPROC glad_debug_glEdgeFlag = glad_debug_impl_glEdgeFlag;\nPFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEdgeFlagPointer(GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glEdgeFlagPointer\", (GLADapiproc) glad_glEdgeFlagPointer, 2, stride, pointer);\n    glad_glEdgeFlagPointer(stride, pointer);\n    _post_call_gl_callback(NULL, \"glEdgeFlagPointer\", (GLADapiproc) glad_glEdgeFlagPointer, 2, stride, pointer);\n}\nPFNGLEDGEFLAGPOINTERPROC glad_debug_glEdgeFlagPointer = glad_debug_impl_glEdgeFlagPointer;\nPFNGLEDGEFLAGVPROC glad_glEdgeFlagv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEdgeFlagv(const GLboolean * flag) {\n    _pre_call_gl_callback(\"glEdgeFlagv\", (GLADapiproc) glad_glEdgeFlagv, 1, flag);\n    glad_glEdgeFlagv(flag);\n    _post_call_gl_callback(NULL, \"glEdgeFlagv\", (GLADapiproc) glad_glEdgeFlagv, 1, flag);\n}\nPFNGLEDGEFLAGVPROC glad_debug_glEdgeFlagv = glad_debug_impl_glEdgeFlagv;\nPFNGLENABLEPROC glad_glEnable = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEnable(GLenum cap) {\n    _pre_call_gl_callback(\"glEnable\", (GLADapiproc) glad_glEnable, 1, cap);\n    glad_glEnable(cap);\n    _post_call_gl_callback(NULL, \"glEnable\", (GLADapiproc) glad_glEnable, 1, cap);\n}\nPFNGLENABLEPROC glad_debug_glEnable = glad_debug_impl_glEnable;\nPFNGLENABLECLIENTSTATEPROC glad_glEnableClientState = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEnableClientState(GLenum array) {\n    _pre_call_gl_callback(\"glEnableClientState\", (GLADapiproc) glad_glEnableClientState, 1, array);\n    glad_glEnableClientState(array);\n    _post_call_gl_callback(NULL, \"glEnableClientState\", (GLADapiproc) glad_glEnableClientState, 1, array);\n}\nPFNGLENABLECLIENTSTATEPROC glad_debug_glEnableClientState = glad_debug_impl_glEnableClientState;\nPFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEnableVertexAttribArray(GLuint index) {\n    _pre_call_gl_callback(\"glEnableVertexAttribArray\", (GLADapiproc) glad_glEnableVertexAttribArray, 1, index);\n    glad_glEnableVertexAttribArray(index);\n    _post_call_gl_callback(NULL, \"glEnableVertexAttribArray\", (GLADapiproc) glad_glEnableVertexAttribArray, 1, index);\n}\nPFNGLENABLEVERTEXATTRIBARRAYPROC glad_debug_glEnableVertexAttribArray = glad_debug_impl_glEnableVertexAttribArray;\nPFNGLENABLEIPROC glad_glEnablei = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEnablei(GLenum target, GLuint index) {\n    _pre_call_gl_callback(\"glEnablei\", (GLADapiproc) glad_glEnablei, 2, target, index);\n    glad_glEnablei(target, index);\n    _post_call_gl_callback(NULL, \"glEnablei\", (GLADapiproc) glad_glEnablei, 2, target, index);\n}\nPFNGLENABLEIPROC glad_debug_glEnablei = glad_debug_impl_glEnablei;\nPFNGLENDPROC glad_glEnd = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEnd(void) {\n    _pre_call_gl_callback(\"glEnd\", (GLADapiproc) glad_glEnd, 0);\n    glad_glEnd();\n    _post_call_gl_callback(NULL, \"glEnd\", (GLADapiproc) glad_glEnd, 0);\n}\nPFNGLENDPROC glad_debug_glEnd = glad_debug_impl_glEnd;\nPFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEndConditionalRender(void) {\n    _pre_call_gl_callback(\"glEndConditionalRender\", (GLADapiproc) glad_glEndConditionalRender, 0);\n    glad_glEndConditionalRender();\n    _post_call_gl_callback(NULL, \"glEndConditionalRender\", (GLADapiproc) glad_glEndConditionalRender, 0);\n}\nPFNGLENDCONDITIONALRENDERPROC glad_debug_glEndConditionalRender = glad_debug_impl_glEndConditionalRender;\nPFNGLENDLISTPROC glad_glEndList = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEndList(void) {\n    _pre_call_gl_callback(\"glEndList\", (GLADapiproc) glad_glEndList, 0);\n    glad_glEndList();\n    _post_call_gl_callback(NULL, \"glEndList\", (GLADapiproc) glad_glEndList, 0);\n}\nPFNGLENDLISTPROC glad_debug_glEndList = glad_debug_impl_glEndList;\nPFNGLENDQUERYPROC glad_glEndQuery = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEndQuery(GLenum target) {\n    _pre_call_gl_callback(\"glEndQuery\", (GLADapiproc) glad_glEndQuery, 1, target);\n    glad_glEndQuery(target);\n    _post_call_gl_callback(NULL, \"glEndQuery\", (GLADapiproc) glad_glEndQuery, 1, target);\n}\nPFNGLENDQUERYPROC glad_debug_glEndQuery = glad_debug_impl_glEndQuery;\nPFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEndTransformFeedback(void) {\n    _pre_call_gl_callback(\"glEndTransformFeedback\", (GLADapiproc) glad_glEndTransformFeedback, 0);\n    glad_glEndTransformFeedback();\n    _post_call_gl_callback(NULL, \"glEndTransformFeedback\", (GLADapiproc) glad_glEndTransformFeedback, 0);\n}\nPFNGLENDTRANSFORMFEEDBACKPROC glad_debug_glEndTransformFeedback = glad_debug_impl_glEndTransformFeedback;\nPFNGLEVALCOORD1DPROC glad_glEvalCoord1d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalCoord1d(GLdouble u) {\n    _pre_call_gl_callback(\"glEvalCoord1d\", (GLADapiproc) glad_glEvalCoord1d, 1, u);\n    glad_glEvalCoord1d(u);\n    _post_call_gl_callback(NULL, \"glEvalCoord1d\", (GLADapiproc) glad_glEvalCoord1d, 1, u);\n}\nPFNGLEVALCOORD1DPROC glad_debug_glEvalCoord1d = glad_debug_impl_glEvalCoord1d;\nPFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalCoord1dv(const GLdouble * u) {\n    _pre_call_gl_callback(\"glEvalCoord1dv\", (GLADapiproc) glad_glEvalCoord1dv, 1, u);\n    glad_glEvalCoord1dv(u);\n    _post_call_gl_callback(NULL, \"glEvalCoord1dv\", (GLADapiproc) glad_glEvalCoord1dv, 1, u);\n}\nPFNGLEVALCOORD1DVPROC glad_debug_glEvalCoord1dv = glad_debug_impl_glEvalCoord1dv;\nPFNGLEVALCOORD1FPROC glad_glEvalCoord1f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalCoord1f(GLfloat u) {\n    _pre_call_gl_callback(\"glEvalCoord1f\", (GLADapiproc) glad_glEvalCoord1f, 1, u);\n    glad_glEvalCoord1f(u);\n    _post_call_gl_callback(NULL, \"glEvalCoord1f\", (GLADapiproc) glad_glEvalCoord1f, 1, u);\n}\nPFNGLEVALCOORD1FPROC glad_debug_glEvalCoord1f = glad_debug_impl_glEvalCoord1f;\nPFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalCoord1fv(const GLfloat * u) {\n    _pre_call_gl_callback(\"glEvalCoord1fv\", (GLADapiproc) glad_glEvalCoord1fv, 1, u);\n    glad_glEvalCoord1fv(u);\n    _post_call_gl_callback(NULL, \"glEvalCoord1fv\", (GLADapiproc) glad_glEvalCoord1fv, 1, u);\n}\nPFNGLEVALCOORD1FVPROC glad_debug_glEvalCoord1fv = glad_debug_impl_glEvalCoord1fv;\nPFNGLEVALCOORD2DPROC glad_glEvalCoord2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalCoord2d(GLdouble u, GLdouble v) {\n    _pre_call_gl_callback(\"glEvalCoord2d\", (GLADapiproc) glad_glEvalCoord2d, 2, u, v);\n    glad_glEvalCoord2d(u, v);\n    _post_call_gl_callback(NULL, \"glEvalCoord2d\", (GLADapiproc) glad_glEvalCoord2d, 2, u, v);\n}\nPFNGLEVALCOORD2DPROC glad_debug_glEvalCoord2d = glad_debug_impl_glEvalCoord2d;\nPFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalCoord2dv(const GLdouble * u) {\n    _pre_call_gl_callback(\"glEvalCoord2dv\", (GLADapiproc) glad_glEvalCoord2dv, 1, u);\n    glad_glEvalCoord2dv(u);\n    _post_call_gl_callback(NULL, \"glEvalCoord2dv\", (GLADapiproc) glad_glEvalCoord2dv, 1, u);\n}\nPFNGLEVALCOORD2DVPROC glad_debug_glEvalCoord2dv = glad_debug_impl_glEvalCoord2dv;\nPFNGLEVALCOORD2FPROC glad_glEvalCoord2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalCoord2f(GLfloat u, GLfloat v) {\n    _pre_call_gl_callback(\"glEvalCoord2f\", (GLADapiproc) glad_glEvalCoord2f, 2, u, v);\n    glad_glEvalCoord2f(u, v);\n    _post_call_gl_callback(NULL, \"glEvalCoord2f\", (GLADapiproc) glad_glEvalCoord2f, 2, u, v);\n}\nPFNGLEVALCOORD2FPROC glad_debug_glEvalCoord2f = glad_debug_impl_glEvalCoord2f;\nPFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalCoord2fv(const GLfloat * u) {\n    _pre_call_gl_callback(\"glEvalCoord2fv\", (GLADapiproc) glad_glEvalCoord2fv, 1, u);\n    glad_glEvalCoord2fv(u);\n    _post_call_gl_callback(NULL, \"glEvalCoord2fv\", (GLADapiproc) glad_glEvalCoord2fv, 1, u);\n}\nPFNGLEVALCOORD2FVPROC glad_debug_glEvalCoord2fv = glad_debug_impl_glEvalCoord2fv;\nPFNGLEVALMESH1PROC glad_glEvalMesh1 = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalMesh1(GLenum mode, GLint i1, GLint i2) {\n    _pre_call_gl_callback(\"glEvalMesh1\", (GLADapiproc) glad_glEvalMesh1, 3, mode, i1, i2);\n    glad_glEvalMesh1(mode, i1, i2);\n    _post_call_gl_callback(NULL, \"glEvalMesh1\", (GLADapiproc) glad_glEvalMesh1, 3, mode, i1, i2);\n}\nPFNGLEVALMESH1PROC glad_debug_glEvalMesh1 = glad_debug_impl_glEvalMesh1;\nPFNGLEVALMESH2PROC glad_glEvalMesh2 = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2) {\n    _pre_call_gl_callback(\"glEvalMesh2\", (GLADapiproc) glad_glEvalMesh2, 5, mode, i1, i2, j1, j2);\n    glad_glEvalMesh2(mode, i1, i2, j1, j2);\n    _post_call_gl_callback(NULL, \"glEvalMesh2\", (GLADapiproc) glad_glEvalMesh2, 5, mode, i1, i2, j1, j2);\n}\nPFNGLEVALMESH2PROC glad_debug_glEvalMesh2 = glad_debug_impl_glEvalMesh2;\nPFNGLEVALPOINT1PROC glad_glEvalPoint1 = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalPoint1(GLint i) {\n    _pre_call_gl_callback(\"glEvalPoint1\", (GLADapiproc) glad_glEvalPoint1, 1, i);\n    glad_glEvalPoint1(i);\n    _post_call_gl_callback(NULL, \"glEvalPoint1\", (GLADapiproc) glad_glEvalPoint1, 1, i);\n}\nPFNGLEVALPOINT1PROC glad_debug_glEvalPoint1 = glad_debug_impl_glEvalPoint1;\nPFNGLEVALPOINT2PROC glad_glEvalPoint2 = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glEvalPoint2(GLint i, GLint j) {\n    _pre_call_gl_callback(\"glEvalPoint2\", (GLADapiproc) glad_glEvalPoint2, 2, i, j);\n    glad_glEvalPoint2(i, j);\n    _post_call_gl_callback(NULL, \"glEvalPoint2\", (GLADapiproc) glad_glEvalPoint2, 2, i, j);\n}\nPFNGLEVALPOINT2PROC glad_debug_glEvalPoint2 = glad_debug_impl_glEvalPoint2;\nPFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFeedbackBuffer(GLsizei size, GLenum type, GLfloat * buffer) {\n    _pre_call_gl_callback(\"glFeedbackBuffer\", (GLADapiproc) glad_glFeedbackBuffer, 3, size, type, buffer);\n    glad_glFeedbackBuffer(size, type, buffer);\n    _post_call_gl_callback(NULL, \"glFeedbackBuffer\", (GLADapiproc) glad_glFeedbackBuffer, 3, size, type, buffer);\n}\nPFNGLFEEDBACKBUFFERPROC glad_debug_glFeedbackBuffer = glad_debug_impl_glFeedbackBuffer;\nPFNGLFINISHPROC glad_glFinish = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFinish(void) {\n    _pre_call_gl_callback(\"glFinish\", (GLADapiproc) glad_glFinish, 0);\n    glad_glFinish();\n    _post_call_gl_callback(NULL, \"glFinish\", (GLADapiproc) glad_glFinish, 0);\n}\nPFNGLFINISHPROC glad_debug_glFinish = glad_debug_impl_glFinish;\nPFNGLFLUSHPROC glad_glFlush = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFlush(void) {\n    _pre_call_gl_callback(\"glFlush\", (GLADapiproc) glad_glFlush, 0);\n    glad_glFlush();\n    _post_call_gl_callback(NULL, \"glFlush\", (GLADapiproc) glad_glFlush, 0);\n}\nPFNGLFLUSHPROC glad_debug_glFlush = glad_debug_impl_glFlush;\nPFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) {\n    _pre_call_gl_callback(\"glFlushMappedBufferRange\", (GLADapiproc) glad_glFlushMappedBufferRange, 3, target, offset, length);\n    glad_glFlushMappedBufferRange(target, offset, length);\n    _post_call_gl_callback(NULL, \"glFlushMappedBufferRange\", (GLADapiproc) glad_glFlushMappedBufferRange, 3, target, offset, length);\n}\nPFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_debug_glFlushMappedBufferRange = glad_debug_impl_glFlushMappedBufferRange;\nPFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogCoordPointer(GLenum type, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glFogCoordPointer\", (GLADapiproc) glad_glFogCoordPointer, 3, type, stride, pointer);\n    glad_glFogCoordPointer(type, stride, pointer);\n    _post_call_gl_callback(NULL, \"glFogCoordPointer\", (GLADapiproc) glad_glFogCoordPointer, 3, type, stride, pointer);\n}\nPFNGLFOGCOORDPOINTERPROC glad_debug_glFogCoordPointer = glad_debug_impl_glFogCoordPointer;\nPFNGLFOGCOORDDPROC glad_glFogCoordd = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogCoordd(GLdouble coord) {\n    _pre_call_gl_callback(\"glFogCoordd\", (GLADapiproc) glad_glFogCoordd, 1, coord);\n    glad_glFogCoordd(coord);\n    _post_call_gl_callback(NULL, \"glFogCoordd\", (GLADapiproc) glad_glFogCoordd, 1, coord);\n}\nPFNGLFOGCOORDDPROC glad_debug_glFogCoordd = glad_debug_impl_glFogCoordd;\nPFNGLFOGCOORDDVPROC glad_glFogCoorddv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogCoorddv(const GLdouble * coord) {\n    _pre_call_gl_callback(\"glFogCoorddv\", (GLADapiproc) glad_glFogCoorddv, 1, coord);\n    glad_glFogCoorddv(coord);\n    _post_call_gl_callback(NULL, \"glFogCoorddv\", (GLADapiproc) glad_glFogCoorddv, 1, coord);\n}\nPFNGLFOGCOORDDVPROC glad_debug_glFogCoorddv = glad_debug_impl_glFogCoorddv;\nPFNGLFOGCOORDFPROC glad_glFogCoordf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogCoordf(GLfloat coord) {\n    _pre_call_gl_callback(\"glFogCoordf\", (GLADapiproc) glad_glFogCoordf, 1, coord);\n    glad_glFogCoordf(coord);\n    _post_call_gl_callback(NULL, \"glFogCoordf\", (GLADapiproc) glad_glFogCoordf, 1, coord);\n}\nPFNGLFOGCOORDFPROC glad_debug_glFogCoordf = glad_debug_impl_glFogCoordf;\nPFNGLFOGCOORDFVPROC glad_glFogCoordfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogCoordfv(const GLfloat * coord) {\n    _pre_call_gl_callback(\"glFogCoordfv\", (GLADapiproc) glad_glFogCoordfv, 1, coord);\n    glad_glFogCoordfv(coord);\n    _post_call_gl_callback(NULL, \"glFogCoordfv\", (GLADapiproc) glad_glFogCoordfv, 1, coord);\n}\nPFNGLFOGCOORDFVPROC glad_debug_glFogCoordfv = glad_debug_impl_glFogCoordfv;\nPFNGLFOGFPROC glad_glFogf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogf(GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glFogf\", (GLADapiproc) glad_glFogf, 2, pname, param);\n    glad_glFogf(pname, param);\n    _post_call_gl_callback(NULL, \"glFogf\", (GLADapiproc) glad_glFogf, 2, pname, param);\n}\nPFNGLFOGFPROC glad_debug_glFogf = glad_debug_impl_glFogf;\nPFNGLFOGFVPROC glad_glFogfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogfv(GLenum pname, const GLfloat * params) {\n    _pre_call_gl_callback(\"glFogfv\", (GLADapiproc) glad_glFogfv, 2, pname, params);\n    glad_glFogfv(pname, params);\n    _post_call_gl_callback(NULL, \"glFogfv\", (GLADapiproc) glad_glFogfv, 2, pname, params);\n}\nPFNGLFOGFVPROC glad_debug_glFogfv = glad_debug_impl_glFogfv;\nPFNGLFOGIPROC glad_glFogi = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogi(GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glFogi\", (GLADapiproc) glad_glFogi, 2, pname, param);\n    glad_glFogi(pname, param);\n    _post_call_gl_callback(NULL, \"glFogi\", (GLADapiproc) glad_glFogi, 2, pname, param);\n}\nPFNGLFOGIPROC glad_debug_glFogi = glad_debug_impl_glFogi;\nPFNGLFOGIVPROC glad_glFogiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFogiv(GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glFogiv\", (GLADapiproc) glad_glFogiv, 2, pname, params);\n    glad_glFogiv(pname, params);\n    _post_call_gl_callback(NULL, \"glFogiv\", (GLADapiproc) glad_glFogiv, 2, pname, params);\n}\nPFNGLFOGIVPROC glad_debug_glFogiv = glad_debug_impl_glFogiv;\nPFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {\n    _pre_call_gl_callback(\"glFramebufferRenderbuffer\", (GLADapiproc) glad_glFramebufferRenderbuffer, 4, target, attachment, renderbuffertarget, renderbuffer);\n    glad_glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);\n    _post_call_gl_callback(NULL, \"glFramebufferRenderbuffer\", (GLADapiproc) glad_glFramebufferRenderbuffer, 4, target, attachment, renderbuffertarget, renderbuffer);\n}\nPFNGLFRAMEBUFFERRENDERBUFFERPROC glad_debug_glFramebufferRenderbuffer = glad_debug_impl_glFramebufferRenderbuffer;\nPFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {\n    _pre_call_gl_callback(\"glFramebufferTexture1D\", (GLADapiproc) glad_glFramebufferTexture1D, 5, target, attachment, textarget, texture, level);\n    glad_glFramebufferTexture1D(target, attachment, textarget, texture, level);\n    _post_call_gl_callback(NULL, \"glFramebufferTexture1D\", (GLADapiproc) glad_glFramebufferTexture1D, 5, target, attachment, textarget, texture, level);\n}\nPFNGLFRAMEBUFFERTEXTURE1DPROC glad_debug_glFramebufferTexture1D = glad_debug_impl_glFramebufferTexture1D;\nPFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {\n    _pre_call_gl_callback(\"glFramebufferTexture2D\", (GLADapiproc) glad_glFramebufferTexture2D, 5, target, attachment, textarget, texture, level);\n    glad_glFramebufferTexture2D(target, attachment, textarget, texture, level);\n    _post_call_gl_callback(NULL, \"glFramebufferTexture2D\", (GLADapiproc) glad_glFramebufferTexture2D, 5, target, attachment, textarget, texture, level);\n}\nPFNGLFRAMEBUFFERTEXTURE2DPROC glad_debug_glFramebufferTexture2D = glad_debug_impl_glFramebufferTexture2D;\nPFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) {\n    _pre_call_gl_callback(\"glFramebufferTexture3D\", (GLADapiproc) glad_glFramebufferTexture3D, 6, target, attachment, textarget, texture, level, zoffset);\n    glad_glFramebufferTexture3D(target, attachment, textarget, texture, level, zoffset);\n    _post_call_gl_callback(NULL, \"glFramebufferTexture3D\", (GLADapiproc) glad_glFramebufferTexture3D, 6, target, attachment, textarget, texture, level, zoffset);\n}\nPFNGLFRAMEBUFFERTEXTURE3DPROC glad_debug_glFramebufferTexture3D = glad_debug_impl_glFramebufferTexture3D;\nPFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) {\n    _pre_call_gl_callback(\"glFramebufferTextureLayer\", (GLADapiproc) glad_glFramebufferTextureLayer, 5, target, attachment, texture, level, layer);\n    glad_glFramebufferTextureLayer(target, attachment, texture, level, layer);\n    _post_call_gl_callback(NULL, \"glFramebufferTextureLayer\", (GLADapiproc) glad_glFramebufferTextureLayer, 5, target, attachment, texture, level, layer);\n}\nPFNGLFRAMEBUFFERTEXTURELAYERPROC glad_debug_glFramebufferTextureLayer = glad_debug_impl_glFramebufferTextureLayer;\nPFNGLFRONTFACEPROC glad_glFrontFace = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFrontFace(GLenum mode) {\n    _pre_call_gl_callback(\"glFrontFace\", (GLADapiproc) glad_glFrontFace, 1, mode);\n    glad_glFrontFace(mode);\n    _post_call_gl_callback(NULL, \"glFrontFace\", (GLADapiproc) glad_glFrontFace, 1, mode);\n}\nPFNGLFRONTFACEPROC glad_debug_glFrontFace = glad_debug_impl_glFrontFace;\nPFNGLFRUSTUMPROC glad_glFrustum = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) {\n    _pre_call_gl_callback(\"glFrustum\", (GLADapiproc) glad_glFrustum, 6, left, right, bottom, top, zNear, zFar);\n    glad_glFrustum(left, right, bottom, top, zNear, zFar);\n    _post_call_gl_callback(NULL, \"glFrustum\", (GLADapiproc) glad_glFrustum, 6, left, right, bottom, top, zNear, zFar);\n}\nPFNGLFRUSTUMPROC glad_debug_glFrustum = glad_debug_impl_glFrustum;\nPFNGLGENBUFFERSPROC glad_glGenBuffers = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGenBuffers(GLsizei n, GLuint * buffers) {\n    _pre_call_gl_callback(\"glGenBuffers\", (GLADapiproc) glad_glGenBuffers, 2, n, buffers);\n    glad_glGenBuffers(n, buffers);\n    _post_call_gl_callback(NULL, \"glGenBuffers\", (GLADapiproc) glad_glGenBuffers, 2, n, buffers);\n}\nPFNGLGENBUFFERSPROC glad_debug_glGenBuffers = glad_debug_impl_glGenBuffers;\nPFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGenFramebuffers(GLsizei n, GLuint * framebuffers) {\n    _pre_call_gl_callback(\"glGenFramebuffers\", (GLADapiproc) glad_glGenFramebuffers, 2, n, framebuffers);\n    glad_glGenFramebuffers(n, framebuffers);\n    _post_call_gl_callback(NULL, \"glGenFramebuffers\", (GLADapiproc) glad_glGenFramebuffers, 2, n, framebuffers);\n}\nPFNGLGENFRAMEBUFFERSPROC glad_debug_glGenFramebuffers = glad_debug_impl_glGenFramebuffers;\nPFNGLGENLISTSPROC glad_glGenLists = NULL;\nstatic GLuint GLAD_API_PTR glad_debug_impl_glGenLists(GLsizei range) {\n    GLuint ret;\n    _pre_call_gl_callback(\"glGenLists\", (GLADapiproc) glad_glGenLists, 1, range);\n    ret = glad_glGenLists(range);\n    _post_call_gl_callback((void*) &ret, \"glGenLists\", (GLADapiproc) glad_glGenLists, 1, range);\n    return ret;\n}\nPFNGLGENLISTSPROC glad_debug_glGenLists = glad_debug_impl_glGenLists;\nPFNGLGENQUERIESPROC glad_glGenQueries = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGenQueries(GLsizei n, GLuint * ids) {\n    _pre_call_gl_callback(\"glGenQueries\", (GLADapiproc) glad_glGenQueries, 2, n, ids);\n    glad_glGenQueries(n, ids);\n    _post_call_gl_callback(NULL, \"glGenQueries\", (GLADapiproc) glad_glGenQueries, 2, n, ids);\n}\nPFNGLGENQUERIESPROC glad_debug_glGenQueries = glad_debug_impl_glGenQueries;\nPFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGenRenderbuffers(GLsizei n, GLuint * renderbuffers) {\n    _pre_call_gl_callback(\"glGenRenderbuffers\", (GLADapiproc) glad_glGenRenderbuffers, 2, n, renderbuffers);\n    glad_glGenRenderbuffers(n, renderbuffers);\n    _post_call_gl_callback(NULL, \"glGenRenderbuffers\", (GLADapiproc) glad_glGenRenderbuffers, 2, n, renderbuffers);\n}\nPFNGLGENRENDERBUFFERSPROC glad_debug_glGenRenderbuffers = glad_debug_impl_glGenRenderbuffers;\nPFNGLGENTEXTURESPROC glad_glGenTextures = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGenTextures(GLsizei n, GLuint * textures) {\n    _pre_call_gl_callback(\"glGenTextures\", (GLADapiproc) glad_glGenTextures, 2, n, textures);\n    glad_glGenTextures(n, textures);\n    _post_call_gl_callback(NULL, \"glGenTextures\", (GLADapiproc) glad_glGenTextures, 2, n, textures);\n}\nPFNGLGENTEXTURESPROC glad_debug_glGenTextures = glad_debug_impl_glGenTextures;\nPFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGenVertexArrays(GLsizei n, GLuint * arrays) {\n    _pre_call_gl_callback(\"glGenVertexArrays\", (GLADapiproc) glad_glGenVertexArrays, 2, n, arrays);\n    glad_glGenVertexArrays(n, arrays);\n    _post_call_gl_callback(NULL, \"glGenVertexArrays\", (GLADapiproc) glad_glGenVertexArrays, 2, n, arrays);\n}\nPFNGLGENVERTEXARRAYSPROC glad_debug_glGenVertexArrays = glad_debug_impl_glGenVertexArrays;\nPFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGenerateMipmap(GLenum target) {\n    _pre_call_gl_callback(\"glGenerateMipmap\", (GLADapiproc) glad_glGenerateMipmap, 1, target);\n    glad_glGenerateMipmap(target);\n    _post_call_gl_callback(NULL, \"glGenerateMipmap\", (GLADapiproc) glad_glGenerateMipmap, 1, target);\n}\nPFNGLGENERATEMIPMAPPROC glad_debug_glGenerateMipmap = glad_debug_impl_glGenerateMipmap;\nPFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name) {\n    _pre_call_gl_callback(\"glGetActiveAttrib\", (GLADapiproc) glad_glGetActiveAttrib, 7, program, index, bufSize, length, size, type, name);\n    glad_glGetActiveAttrib(program, index, bufSize, length, size, type, name);\n    _post_call_gl_callback(NULL, \"glGetActiveAttrib\", (GLADapiproc) glad_glGetActiveAttrib, 7, program, index, bufSize, length, size, type, name);\n}\nPFNGLGETACTIVEATTRIBPROC glad_debug_glGetActiveAttrib = glad_debug_impl_glGetActiveAttrib;\nPFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name) {\n    _pre_call_gl_callback(\"glGetActiveUniform\", (GLADapiproc) glad_glGetActiveUniform, 7, program, index, bufSize, length, size, type, name);\n    glad_glGetActiveUniform(program, index, bufSize, length, size, type, name);\n    _post_call_gl_callback(NULL, \"glGetActiveUniform\", (GLADapiproc) glad_glGetActiveUniform, 7, program, index, bufSize, length, size, type, name);\n}\nPFNGLGETACTIVEUNIFORMPROC glad_debug_glGetActiveUniform = glad_debug_impl_glGetActiveUniform;\nPFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformBlockName) {\n    _pre_call_gl_callback(\"glGetActiveUniformBlockName\", (GLADapiproc) glad_glGetActiveUniformBlockName, 5, program, uniformBlockIndex, bufSize, length, uniformBlockName);\n    glad_glGetActiveUniformBlockName(program, uniformBlockIndex, bufSize, length, uniformBlockName);\n    _post_call_gl_callback(NULL, \"glGetActiveUniformBlockName\", (GLADapiproc) glad_glGetActiveUniformBlockName, 5, program, uniformBlockIndex, bufSize, length, uniformBlockName);\n}\nPFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_debug_glGetActiveUniformBlockName = glad_debug_impl_glGetActiveUniformBlockName;\nPFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetActiveUniformBlockiv\", (GLADapiproc) glad_glGetActiveUniformBlockiv, 4, program, uniformBlockIndex, pname, params);\n    glad_glGetActiveUniformBlockiv(program, uniformBlockIndex, pname, params);\n    _post_call_gl_callback(NULL, \"glGetActiveUniformBlockiv\", (GLADapiproc) glad_glGetActiveUniformBlockiv, 4, program, uniformBlockIndex, pname, params);\n}\nPFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_debug_glGetActiveUniformBlockiv = glad_debug_impl_glGetActiveUniformBlockiv;\nPFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetActiveUniformName(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformName) {\n    _pre_call_gl_callback(\"glGetActiveUniformName\", (GLADapiproc) glad_glGetActiveUniformName, 5, program, uniformIndex, bufSize, length, uniformName);\n    glad_glGetActiveUniformName(program, uniformIndex, bufSize, length, uniformName);\n    _post_call_gl_callback(NULL, \"glGetActiveUniformName\", (GLADapiproc) glad_glGetActiveUniformName, 5, program, uniformIndex, bufSize, length, uniformName);\n}\nPFNGLGETACTIVEUNIFORMNAMEPROC glad_debug_glGetActiveUniformName = glad_debug_impl_glGetActiveUniformName;\nPFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint * uniformIndices, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetActiveUniformsiv\", (GLADapiproc) glad_glGetActiveUniformsiv, 5, program, uniformCount, uniformIndices, pname, params);\n    glad_glGetActiveUniformsiv(program, uniformCount, uniformIndices, pname, params);\n    _post_call_gl_callback(NULL, \"glGetActiveUniformsiv\", (GLADapiproc) glad_glGetActiveUniformsiv, 5, program, uniformCount, uniformIndices, pname, params);\n}\nPFNGLGETACTIVEUNIFORMSIVPROC glad_debug_glGetActiveUniformsiv = glad_debug_impl_glGetActiveUniformsiv;\nPFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders) {\n    _pre_call_gl_callback(\"glGetAttachedShaders\", (GLADapiproc) glad_glGetAttachedShaders, 4, program, maxCount, count, shaders);\n    glad_glGetAttachedShaders(program, maxCount, count, shaders);\n    _post_call_gl_callback(NULL, \"glGetAttachedShaders\", (GLADapiproc) glad_glGetAttachedShaders, 4, program, maxCount, count, shaders);\n}\nPFNGLGETATTACHEDSHADERSPROC glad_debug_glGetAttachedShaders = glad_debug_impl_glGetAttachedShaders;\nPFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL;\nstatic GLint GLAD_API_PTR glad_debug_impl_glGetAttribLocation(GLuint program, const GLchar * name) {\n    GLint ret;\n    _pre_call_gl_callback(\"glGetAttribLocation\", (GLADapiproc) glad_glGetAttribLocation, 2, program, name);\n    ret = glad_glGetAttribLocation(program, name);\n    _post_call_gl_callback((void*) &ret, \"glGetAttribLocation\", (GLADapiproc) glad_glGetAttribLocation, 2, program, name);\n    return ret;\n}\nPFNGLGETATTRIBLOCATIONPROC glad_debug_glGetAttribLocation = glad_debug_impl_glGetAttribLocation;\nPFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetBooleani_v(GLenum target, GLuint index, GLboolean * data) {\n    _pre_call_gl_callback(\"glGetBooleani_v\", (GLADapiproc) glad_glGetBooleani_v, 3, target, index, data);\n    glad_glGetBooleani_v(target, index, data);\n    _post_call_gl_callback(NULL, \"glGetBooleani_v\", (GLADapiproc) glad_glGetBooleani_v, 3, target, index, data);\n}\nPFNGLGETBOOLEANI_VPROC glad_debug_glGetBooleani_v = glad_debug_impl_glGetBooleani_v;\nPFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetBooleanv(GLenum pname, GLboolean * data) {\n    _pre_call_gl_callback(\"glGetBooleanv\", (GLADapiproc) glad_glGetBooleanv, 2, pname, data);\n    glad_glGetBooleanv(pname, data);\n    _post_call_gl_callback(NULL, \"glGetBooleanv\", (GLADapiproc) glad_glGetBooleanv, 2, pname, data);\n}\nPFNGLGETBOOLEANVPROC glad_debug_glGetBooleanv = glad_debug_impl_glGetBooleanv;\nPFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetBufferParameteriv(GLenum target, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetBufferParameteriv\", (GLADapiproc) glad_glGetBufferParameteriv, 3, target, pname, params);\n    glad_glGetBufferParameteriv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetBufferParameteriv\", (GLADapiproc) glad_glGetBufferParameteriv, 3, target, pname, params);\n}\nPFNGLGETBUFFERPARAMETERIVPROC glad_debug_glGetBufferParameteriv = glad_debug_impl_glGetBufferParameteriv;\nPFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetBufferPointerv(GLenum target, GLenum pname, void ** params) {\n    _pre_call_gl_callback(\"glGetBufferPointerv\", (GLADapiproc) glad_glGetBufferPointerv, 3, target, pname, params);\n    glad_glGetBufferPointerv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetBufferPointerv\", (GLADapiproc) glad_glGetBufferPointerv, 3, target, pname, params);\n}\nPFNGLGETBUFFERPOINTERVPROC glad_debug_glGetBufferPointerv = glad_debug_impl_glGetBufferPointerv;\nPFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void * data) {\n    _pre_call_gl_callback(\"glGetBufferSubData\", (GLADapiproc) glad_glGetBufferSubData, 4, target, offset, size, data);\n    glad_glGetBufferSubData(target, offset, size, data);\n    _post_call_gl_callback(NULL, \"glGetBufferSubData\", (GLADapiproc) glad_glGetBufferSubData, 4, target, offset, size, data);\n}\nPFNGLGETBUFFERSUBDATAPROC glad_debug_glGetBufferSubData = glad_debug_impl_glGetBufferSubData;\nPFNGLGETCLIPPLANEPROC glad_glGetClipPlane = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetClipPlane(GLenum plane, GLdouble * equation) {\n    _pre_call_gl_callback(\"glGetClipPlane\", (GLADapiproc) glad_glGetClipPlane, 2, plane, equation);\n    glad_glGetClipPlane(plane, equation);\n    _post_call_gl_callback(NULL, \"glGetClipPlane\", (GLADapiproc) glad_glGetClipPlane, 2, plane, equation);\n}\nPFNGLGETCLIPPLANEPROC glad_debug_glGetClipPlane = glad_debug_impl_glGetClipPlane;\nPFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetCompressedTexImage(GLenum target, GLint level, void * img) {\n    _pre_call_gl_callback(\"glGetCompressedTexImage\", (GLADapiproc) glad_glGetCompressedTexImage, 3, target, level, img);\n    glad_glGetCompressedTexImage(target, level, img);\n    _post_call_gl_callback(NULL, \"glGetCompressedTexImage\", (GLADapiproc) glad_glGetCompressedTexImage, 3, target, level, img);\n}\nPFNGLGETCOMPRESSEDTEXIMAGEPROC glad_debug_glGetCompressedTexImage = glad_debug_impl_glGetCompressedTexImage;\nPFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL;\nstatic GLuint GLAD_API_PTR glad_debug_impl_glGetDebugMessageLog(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog) {\n    GLuint ret;\n    _pre_call_gl_callback(\"glGetDebugMessageLog\", (GLADapiproc) glad_glGetDebugMessageLog, 8, count, bufSize, sources, types, ids, severities, lengths, messageLog);\n    ret = glad_glGetDebugMessageLog(count, bufSize, sources, types, ids, severities, lengths, messageLog);\n    _post_call_gl_callback((void*) &ret, \"glGetDebugMessageLog\", (GLADapiproc) glad_glGetDebugMessageLog, 8, count, bufSize, sources, types, ids, severities, lengths, messageLog);\n    return ret;\n}\nPFNGLGETDEBUGMESSAGELOGPROC glad_debug_glGetDebugMessageLog = glad_debug_impl_glGetDebugMessageLog;\nPFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetDoublev(GLenum pname, GLdouble * data) {\n    _pre_call_gl_callback(\"glGetDoublev\", (GLADapiproc) glad_glGetDoublev, 2, pname, data);\n    glad_glGetDoublev(pname, data);\n    _post_call_gl_callback(NULL, \"glGetDoublev\", (GLADapiproc) glad_glGetDoublev, 2, pname, data);\n}\nPFNGLGETDOUBLEVPROC glad_debug_glGetDoublev = glad_debug_impl_glGetDoublev;\nPFNGLGETERRORPROC glad_glGetError = NULL;\nstatic GLenum GLAD_API_PTR glad_debug_impl_glGetError(void) {\n    GLenum ret;\n    _pre_call_gl_callback(\"glGetError\", (GLADapiproc) glad_glGetError, 0);\n    ret = glad_glGetError();\n    _post_call_gl_callback((void*) &ret, \"glGetError\", (GLADapiproc) glad_glGetError, 0);\n    return ret;\n}\nPFNGLGETERRORPROC glad_debug_glGetError = glad_debug_impl_glGetError;\nPFNGLGETFLOATVPROC glad_glGetFloatv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetFloatv(GLenum pname, GLfloat * data) {\n    _pre_call_gl_callback(\"glGetFloatv\", (GLADapiproc) glad_glGetFloatv, 2, pname, data);\n    glad_glGetFloatv(pname, data);\n    _post_call_gl_callback(NULL, \"glGetFloatv\", (GLADapiproc) glad_glGetFloatv, 2, pname, data);\n}\nPFNGLGETFLOATVPROC glad_debug_glGetFloatv = glad_debug_impl_glGetFloatv;\nPFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL;\nstatic GLint GLAD_API_PTR glad_debug_impl_glGetFragDataLocation(GLuint program, const GLchar * name) {\n    GLint ret;\n    _pre_call_gl_callback(\"glGetFragDataLocation\", (GLADapiproc) glad_glGetFragDataLocation, 2, program, name);\n    ret = glad_glGetFragDataLocation(program, name);\n    _post_call_gl_callback((void*) &ret, \"glGetFragDataLocation\", (GLADapiproc) glad_glGetFragDataLocation, 2, program, name);\n    return ret;\n}\nPFNGLGETFRAGDATALOCATIONPROC glad_debug_glGetFragDataLocation = glad_debug_impl_glGetFragDataLocation;\nPFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetFramebufferAttachmentParameteriv\", (GLADapiproc) glad_glGetFramebufferAttachmentParameteriv, 4, target, attachment, pname, params);\n    glad_glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);\n    _post_call_gl_callback(NULL, \"glGetFramebufferAttachmentParameteriv\", (GLADapiproc) glad_glGetFramebufferAttachmentParameteriv, 4, target, attachment, pname, params);\n}\nPFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_debug_glGetFramebufferAttachmentParameteriv = glad_debug_impl_glGetFramebufferAttachmentParameteriv;\nPFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB = NULL;\nstatic GLenum GLAD_API_PTR glad_debug_impl_glGetGraphicsResetStatusARB(void) {\n    GLenum ret;\n    _pre_call_gl_callback(\"glGetGraphicsResetStatusARB\", (GLADapiproc) glad_glGetGraphicsResetStatusARB, 0);\n    ret = glad_glGetGraphicsResetStatusARB();\n    _post_call_gl_callback((void*) &ret, \"glGetGraphicsResetStatusARB\", (GLADapiproc) glad_glGetGraphicsResetStatusARB, 0);\n    return ret;\n}\nPFNGLGETGRAPHICSRESETSTATUSARBPROC glad_debug_glGetGraphicsResetStatusARB = glad_debug_impl_glGetGraphicsResetStatusARB;\nPFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetIntegeri_v(GLenum target, GLuint index, GLint * data) {\n    _pre_call_gl_callback(\"glGetIntegeri_v\", (GLADapiproc) glad_glGetIntegeri_v, 3, target, index, data);\n    glad_glGetIntegeri_v(target, index, data);\n    _post_call_gl_callback(NULL, \"glGetIntegeri_v\", (GLADapiproc) glad_glGetIntegeri_v, 3, target, index, data);\n}\nPFNGLGETINTEGERI_VPROC glad_debug_glGetIntegeri_v = glad_debug_impl_glGetIntegeri_v;\nPFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetIntegerv(GLenum pname, GLint * data) {\n    _pre_call_gl_callback(\"glGetIntegerv\", (GLADapiproc) glad_glGetIntegerv, 2, pname, data);\n    glad_glGetIntegerv(pname, data);\n    _post_call_gl_callback(NULL, \"glGetIntegerv\", (GLADapiproc) glad_glGetIntegerv, 2, pname, data);\n}\nPFNGLGETINTEGERVPROC glad_debug_glGetIntegerv = glad_debug_impl_glGetIntegerv;\nPFNGLGETLIGHTFVPROC glad_glGetLightfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetLightfv(GLenum light, GLenum pname, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetLightfv\", (GLADapiproc) glad_glGetLightfv, 3, light, pname, params);\n    glad_glGetLightfv(light, pname, params);\n    _post_call_gl_callback(NULL, \"glGetLightfv\", (GLADapiproc) glad_glGetLightfv, 3, light, pname, params);\n}\nPFNGLGETLIGHTFVPROC glad_debug_glGetLightfv = glad_debug_impl_glGetLightfv;\nPFNGLGETLIGHTIVPROC glad_glGetLightiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetLightiv(GLenum light, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetLightiv\", (GLADapiproc) glad_glGetLightiv, 3, light, pname, params);\n    glad_glGetLightiv(light, pname, params);\n    _post_call_gl_callback(NULL, \"glGetLightiv\", (GLADapiproc) glad_glGetLightiv, 3, light, pname, params);\n}\nPFNGLGETLIGHTIVPROC glad_debug_glGetLightiv = glad_debug_impl_glGetLightiv;\nPFNGLGETMAPDVPROC glad_glGetMapdv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetMapdv(GLenum target, GLenum query, GLdouble * v) {\n    _pre_call_gl_callback(\"glGetMapdv\", (GLADapiproc) glad_glGetMapdv, 3, target, query, v);\n    glad_glGetMapdv(target, query, v);\n    _post_call_gl_callback(NULL, \"glGetMapdv\", (GLADapiproc) glad_glGetMapdv, 3, target, query, v);\n}\nPFNGLGETMAPDVPROC glad_debug_glGetMapdv = glad_debug_impl_glGetMapdv;\nPFNGLGETMAPFVPROC glad_glGetMapfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetMapfv(GLenum target, GLenum query, GLfloat * v) {\n    _pre_call_gl_callback(\"glGetMapfv\", (GLADapiproc) glad_glGetMapfv, 3, target, query, v);\n    glad_glGetMapfv(target, query, v);\n    _post_call_gl_callback(NULL, \"glGetMapfv\", (GLADapiproc) glad_glGetMapfv, 3, target, query, v);\n}\nPFNGLGETMAPFVPROC glad_debug_glGetMapfv = glad_debug_impl_glGetMapfv;\nPFNGLGETMAPIVPROC glad_glGetMapiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetMapiv(GLenum target, GLenum query, GLint * v) {\n    _pre_call_gl_callback(\"glGetMapiv\", (GLADapiproc) glad_glGetMapiv, 3, target, query, v);\n    glad_glGetMapiv(target, query, v);\n    _post_call_gl_callback(NULL, \"glGetMapiv\", (GLADapiproc) glad_glGetMapiv, 3, target, query, v);\n}\nPFNGLGETMAPIVPROC glad_debug_glGetMapiv = glad_debug_impl_glGetMapiv;\nPFNGLGETMATERIALFVPROC glad_glGetMaterialfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetMaterialfv(GLenum face, GLenum pname, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetMaterialfv\", (GLADapiproc) glad_glGetMaterialfv, 3, face, pname, params);\n    glad_glGetMaterialfv(face, pname, params);\n    _post_call_gl_callback(NULL, \"glGetMaterialfv\", (GLADapiproc) glad_glGetMaterialfv, 3, face, pname, params);\n}\nPFNGLGETMATERIALFVPROC glad_debug_glGetMaterialfv = glad_debug_impl_glGetMaterialfv;\nPFNGLGETMATERIALIVPROC glad_glGetMaterialiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetMaterialiv(GLenum face, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetMaterialiv\", (GLADapiproc) glad_glGetMaterialiv, 3, face, pname, params);\n    glad_glGetMaterialiv(face, pname, params);\n    _post_call_gl_callback(NULL, \"glGetMaterialiv\", (GLADapiproc) glad_glGetMaterialiv, 3, face, pname, params);\n}\nPFNGLGETMATERIALIVPROC glad_debug_glGetMaterialiv = glad_debug_impl_glGetMaterialiv;\nPFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetObjectLabel(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label) {\n    _pre_call_gl_callback(\"glGetObjectLabel\", (GLADapiproc) glad_glGetObjectLabel, 5, identifier, name, bufSize, length, label);\n    glad_glGetObjectLabel(identifier, name, bufSize, length, label);\n    _post_call_gl_callback(NULL, \"glGetObjectLabel\", (GLADapiproc) glad_glGetObjectLabel, 5, identifier, name, bufSize, length, label);\n}\nPFNGLGETOBJECTLABELPROC glad_debug_glGetObjectLabel = glad_debug_impl_glGetObjectLabel;\nPFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetObjectPtrLabel(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label) {\n    _pre_call_gl_callback(\"glGetObjectPtrLabel\", (GLADapiproc) glad_glGetObjectPtrLabel, 4, ptr, bufSize, length, label);\n    glad_glGetObjectPtrLabel(ptr, bufSize, length, label);\n    _post_call_gl_callback(NULL, \"glGetObjectPtrLabel\", (GLADapiproc) glad_glGetObjectPtrLabel, 4, ptr, bufSize, length, label);\n}\nPFNGLGETOBJECTPTRLABELPROC glad_debug_glGetObjectPtrLabel = glad_debug_impl_glGetObjectPtrLabel;\nPFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetPixelMapfv(GLenum map, GLfloat * values) {\n    _pre_call_gl_callback(\"glGetPixelMapfv\", (GLADapiproc) glad_glGetPixelMapfv, 2, map, values);\n    glad_glGetPixelMapfv(map, values);\n    _post_call_gl_callback(NULL, \"glGetPixelMapfv\", (GLADapiproc) glad_glGetPixelMapfv, 2, map, values);\n}\nPFNGLGETPIXELMAPFVPROC glad_debug_glGetPixelMapfv = glad_debug_impl_glGetPixelMapfv;\nPFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetPixelMapuiv(GLenum map, GLuint * values) {\n    _pre_call_gl_callback(\"glGetPixelMapuiv\", (GLADapiproc) glad_glGetPixelMapuiv, 2, map, values);\n    glad_glGetPixelMapuiv(map, values);\n    _post_call_gl_callback(NULL, \"glGetPixelMapuiv\", (GLADapiproc) glad_glGetPixelMapuiv, 2, map, values);\n}\nPFNGLGETPIXELMAPUIVPROC glad_debug_glGetPixelMapuiv = glad_debug_impl_glGetPixelMapuiv;\nPFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetPixelMapusv(GLenum map, GLushort * values) {\n    _pre_call_gl_callback(\"glGetPixelMapusv\", (GLADapiproc) glad_glGetPixelMapusv, 2, map, values);\n    glad_glGetPixelMapusv(map, values);\n    _post_call_gl_callback(NULL, \"glGetPixelMapusv\", (GLADapiproc) glad_glGetPixelMapusv, 2, map, values);\n}\nPFNGLGETPIXELMAPUSVPROC glad_debug_glGetPixelMapusv = glad_debug_impl_glGetPixelMapusv;\nPFNGLGETPOINTERVPROC glad_glGetPointerv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetPointerv(GLenum pname, void ** params) {\n    _pre_call_gl_callback(\"glGetPointerv\", (GLADapiproc) glad_glGetPointerv, 2, pname, params);\n    glad_glGetPointerv(pname, params);\n    _post_call_gl_callback(NULL, \"glGetPointerv\", (GLADapiproc) glad_glGetPointerv, 2, pname, params);\n}\nPFNGLGETPOINTERVPROC glad_debug_glGetPointerv = glad_debug_impl_glGetPointerv;\nPFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetPolygonStipple(GLubyte * mask) {\n    _pre_call_gl_callback(\"glGetPolygonStipple\", (GLADapiproc) glad_glGetPolygonStipple, 1, mask);\n    glad_glGetPolygonStipple(mask);\n    _post_call_gl_callback(NULL, \"glGetPolygonStipple\", (GLADapiproc) glad_glGetPolygonStipple, 1, mask);\n}\nPFNGLGETPOLYGONSTIPPLEPROC glad_debug_glGetPolygonStipple = glad_debug_impl_glGetPolygonStipple;\nPFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog) {\n    _pre_call_gl_callback(\"glGetProgramInfoLog\", (GLADapiproc) glad_glGetProgramInfoLog, 4, program, bufSize, length, infoLog);\n    glad_glGetProgramInfoLog(program, bufSize, length, infoLog);\n    _post_call_gl_callback(NULL, \"glGetProgramInfoLog\", (GLADapiproc) glad_glGetProgramInfoLog, 4, program, bufSize, length, infoLog);\n}\nPFNGLGETPROGRAMINFOLOGPROC glad_debug_glGetProgramInfoLog = glad_debug_impl_glGetProgramInfoLog;\nPFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetProgramiv(GLuint program, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetProgramiv\", (GLADapiproc) glad_glGetProgramiv, 3, program, pname, params);\n    glad_glGetProgramiv(program, pname, params);\n    _post_call_gl_callback(NULL, \"glGetProgramiv\", (GLADapiproc) glad_glGetProgramiv, 3, program, pname, params);\n}\nPFNGLGETPROGRAMIVPROC glad_debug_glGetProgramiv = glad_debug_impl_glGetProgramiv;\nPFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetQueryObjectiv(GLuint id, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetQueryObjectiv\", (GLADapiproc) glad_glGetQueryObjectiv, 3, id, pname, params);\n    glad_glGetQueryObjectiv(id, pname, params);\n    _post_call_gl_callback(NULL, \"glGetQueryObjectiv\", (GLADapiproc) glad_glGetQueryObjectiv, 3, id, pname, params);\n}\nPFNGLGETQUERYOBJECTIVPROC glad_debug_glGetQueryObjectiv = glad_debug_impl_glGetQueryObjectiv;\nPFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint * params) {\n    _pre_call_gl_callback(\"glGetQueryObjectuiv\", (GLADapiproc) glad_glGetQueryObjectuiv, 3, id, pname, params);\n    glad_glGetQueryObjectuiv(id, pname, params);\n    _post_call_gl_callback(NULL, \"glGetQueryObjectuiv\", (GLADapiproc) glad_glGetQueryObjectuiv, 3, id, pname, params);\n}\nPFNGLGETQUERYOBJECTUIVPROC glad_debug_glGetQueryObjectuiv = glad_debug_impl_glGetQueryObjectuiv;\nPFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetQueryiv(GLenum target, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetQueryiv\", (GLADapiproc) glad_glGetQueryiv, 3, target, pname, params);\n    glad_glGetQueryiv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetQueryiv\", (GLADapiproc) glad_glGetQueryiv, 3, target, pname, params);\n}\nPFNGLGETQUERYIVPROC glad_debug_glGetQueryiv = glad_debug_impl_glGetQueryiv;\nPFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetRenderbufferParameteriv\", (GLADapiproc) glad_glGetRenderbufferParameteriv, 3, target, pname, params);\n    glad_glGetRenderbufferParameteriv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetRenderbufferParameteriv\", (GLADapiproc) glad_glGetRenderbufferParameteriv, 3, target, pname, params);\n}\nPFNGLGETRENDERBUFFERPARAMETERIVPROC glad_debug_glGetRenderbufferParameteriv = glad_debug_impl_glGetRenderbufferParameteriv;\nPFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog) {\n    _pre_call_gl_callback(\"glGetShaderInfoLog\", (GLADapiproc) glad_glGetShaderInfoLog, 4, shader, bufSize, length, infoLog);\n    glad_glGetShaderInfoLog(shader, bufSize, length, infoLog);\n    _post_call_gl_callback(NULL, \"glGetShaderInfoLog\", (GLADapiproc) glad_glGetShaderInfoLog, 4, shader, bufSize, length, infoLog);\n}\nPFNGLGETSHADERINFOLOGPROC glad_debug_glGetShaderInfoLog = glad_debug_impl_glGetShaderInfoLog;\nPFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetShaderSource(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source) {\n    _pre_call_gl_callback(\"glGetShaderSource\", (GLADapiproc) glad_glGetShaderSource, 4, shader, bufSize, length, source);\n    glad_glGetShaderSource(shader, bufSize, length, source);\n    _post_call_gl_callback(NULL, \"glGetShaderSource\", (GLADapiproc) glad_glGetShaderSource, 4, shader, bufSize, length, source);\n}\nPFNGLGETSHADERSOURCEPROC glad_debug_glGetShaderSource = glad_debug_impl_glGetShaderSource;\nPFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetShaderiv(GLuint shader, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetShaderiv\", (GLADapiproc) glad_glGetShaderiv, 3, shader, pname, params);\n    glad_glGetShaderiv(shader, pname, params);\n    _post_call_gl_callback(NULL, \"glGetShaderiv\", (GLADapiproc) glad_glGetShaderiv, 3, shader, pname, params);\n}\nPFNGLGETSHADERIVPROC glad_debug_glGetShaderiv = glad_debug_impl_glGetShaderiv;\nPFNGLGETSTRINGPROC glad_glGetString = NULL;\nstatic const GLubyte * GLAD_API_PTR glad_debug_impl_glGetString(GLenum name) {\n    const GLubyte * ret;\n    _pre_call_gl_callback(\"glGetString\", (GLADapiproc) glad_glGetString, 1, name);\n    ret = glad_glGetString(name);\n    _post_call_gl_callback((void*) &ret, \"glGetString\", (GLADapiproc) glad_glGetString, 1, name);\n    return ret;\n}\nPFNGLGETSTRINGPROC glad_debug_glGetString = glad_debug_impl_glGetString;\nPFNGLGETSTRINGIPROC glad_glGetStringi = NULL;\nstatic const GLubyte * GLAD_API_PTR glad_debug_impl_glGetStringi(GLenum name, GLuint index) {\n    const GLubyte * ret;\n    _pre_call_gl_callback(\"glGetStringi\", (GLADapiproc) glad_glGetStringi, 2, name, index);\n    ret = glad_glGetStringi(name, index);\n    _post_call_gl_callback((void*) &ret, \"glGetStringi\", (GLADapiproc) glad_glGetStringi, 2, name, index);\n    return ret;\n}\nPFNGLGETSTRINGIPROC glad_debug_glGetStringi = glad_debug_impl_glGetStringi;\nPFNGLGETTEXENVFVPROC glad_glGetTexEnvfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexEnvfv(GLenum target, GLenum pname, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetTexEnvfv\", (GLADapiproc) glad_glGetTexEnvfv, 3, target, pname, params);\n    glad_glGetTexEnvfv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexEnvfv\", (GLADapiproc) glad_glGetTexEnvfv, 3, target, pname, params);\n}\nPFNGLGETTEXENVFVPROC glad_debug_glGetTexEnvfv = glad_debug_impl_glGetTexEnvfv;\nPFNGLGETTEXENVIVPROC glad_glGetTexEnviv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexEnviv(GLenum target, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetTexEnviv\", (GLADapiproc) glad_glGetTexEnviv, 3, target, pname, params);\n    glad_glGetTexEnviv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexEnviv\", (GLADapiproc) glad_glGetTexEnviv, 3, target, pname, params);\n}\nPFNGLGETTEXENVIVPROC glad_debug_glGetTexEnviv = glad_debug_impl_glGetTexEnviv;\nPFNGLGETTEXGENDVPROC glad_glGetTexGendv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexGendv(GLenum coord, GLenum pname, GLdouble * params) {\n    _pre_call_gl_callback(\"glGetTexGendv\", (GLADapiproc) glad_glGetTexGendv, 3, coord, pname, params);\n    glad_glGetTexGendv(coord, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexGendv\", (GLADapiproc) glad_glGetTexGendv, 3, coord, pname, params);\n}\nPFNGLGETTEXGENDVPROC glad_debug_glGetTexGendv = glad_debug_impl_glGetTexGendv;\nPFNGLGETTEXGENFVPROC glad_glGetTexGenfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexGenfv(GLenum coord, GLenum pname, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetTexGenfv\", (GLADapiproc) glad_glGetTexGenfv, 3, coord, pname, params);\n    glad_glGetTexGenfv(coord, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexGenfv\", (GLADapiproc) glad_glGetTexGenfv, 3, coord, pname, params);\n}\nPFNGLGETTEXGENFVPROC glad_debug_glGetTexGenfv = glad_debug_impl_glGetTexGenfv;\nPFNGLGETTEXGENIVPROC glad_glGetTexGeniv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexGeniv(GLenum coord, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetTexGeniv\", (GLADapiproc) glad_glGetTexGeniv, 3, coord, pname, params);\n    glad_glGetTexGeniv(coord, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexGeniv\", (GLADapiproc) glad_glGetTexGeniv, 3, coord, pname, params);\n}\nPFNGLGETTEXGENIVPROC glad_debug_glGetTexGeniv = glad_debug_impl_glGetTexGeniv;\nPFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void * pixels) {\n    _pre_call_gl_callback(\"glGetTexImage\", (GLADapiproc) glad_glGetTexImage, 5, target, level, format, type, pixels);\n    glad_glGetTexImage(target, level, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glGetTexImage\", (GLADapiproc) glad_glGetTexImage, 5, target, level, format, type, pixels);\n}\nPFNGLGETTEXIMAGEPROC glad_debug_glGetTexImage = glad_debug_impl_glGetTexImage;\nPFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexLevelParameterfv(GLenum target, GLint level, GLenum pname, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetTexLevelParameterfv\", (GLADapiproc) glad_glGetTexLevelParameterfv, 4, target, level, pname, params);\n    glad_glGetTexLevelParameterfv(target, level, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexLevelParameterfv\", (GLADapiproc) glad_glGetTexLevelParameterfv, 4, target, level, pname, params);\n}\nPFNGLGETTEXLEVELPARAMETERFVPROC glad_debug_glGetTexLevelParameterfv = glad_debug_impl_glGetTexLevelParameterfv;\nPFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetTexLevelParameteriv\", (GLADapiproc) glad_glGetTexLevelParameteriv, 4, target, level, pname, params);\n    glad_glGetTexLevelParameteriv(target, level, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexLevelParameteriv\", (GLADapiproc) glad_glGetTexLevelParameteriv, 4, target, level, pname, params);\n}\nPFNGLGETTEXLEVELPARAMETERIVPROC glad_debug_glGetTexLevelParameteriv = glad_debug_impl_glGetTexLevelParameteriv;\nPFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexParameterIiv(GLenum target, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetTexParameterIiv\", (GLADapiproc) glad_glGetTexParameterIiv, 3, target, pname, params);\n    glad_glGetTexParameterIiv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexParameterIiv\", (GLADapiproc) glad_glGetTexParameterIiv, 3, target, pname, params);\n}\nPFNGLGETTEXPARAMETERIIVPROC glad_debug_glGetTexParameterIiv = glad_debug_impl_glGetTexParameterIiv;\nPFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexParameterIuiv(GLenum target, GLenum pname, GLuint * params) {\n    _pre_call_gl_callback(\"glGetTexParameterIuiv\", (GLADapiproc) glad_glGetTexParameterIuiv, 3, target, pname, params);\n    glad_glGetTexParameterIuiv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexParameterIuiv\", (GLADapiproc) glad_glGetTexParameterIuiv, 3, target, pname, params);\n}\nPFNGLGETTEXPARAMETERIUIVPROC glad_debug_glGetTexParameterIuiv = glad_debug_impl_glGetTexParameterIuiv;\nPFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexParameterfv(GLenum target, GLenum pname, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetTexParameterfv\", (GLADapiproc) glad_glGetTexParameterfv, 3, target, pname, params);\n    glad_glGetTexParameterfv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexParameterfv\", (GLADapiproc) glad_glGetTexParameterfv, 3, target, pname, params);\n}\nPFNGLGETTEXPARAMETERFVPROC glad_debug_glGetTexParameterfv = glad_debug_impl_glGetTexParameterfv;\nPFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTexParameteriv(GLenum target, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetTexParameteriv\", (GLADapiproc) glad_glGetTexParameteriv, 3, target, pname, params);\n    glad_glGetTexParameteriv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glGetTexParameteriv\", (GLADapiproc) glad_glGetTexParameteriv, 3, target, pname, params);\n}\nPFNGLGETTEXPARAMETERIVPROC glad_debug_glGetTexParameteriv = glad_debug_impl_glGetTexParameteriv;\nPFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetTransformFeedbackVarying(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name) {\n    _pre_call_gl_callback(\"glGetTransformFeedbackVarying\", (GLADapiproc) glad_glGetTransformFeedbackVarying, 7, program, index, bufSize, length, size, type, name);\n    glad_glGetTransformFeedbackVarying(program, index, bufSize, length, size, type, name);\n    _post_call_gl_callback(NULL, \"glGetTransformFeedbackVarying\", (GLADapiproc) glad_glGetTransformFeedbackVarying, 7, program, index, bufSize, length, size, type, name);\n}\nPFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_debug_glGetTransformFeedbackVarying = glad_debug_impl_glGetTransformFeedbackVarying;\nPFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL;\nstatic GLuint GLAD_API_PTR glad_debug_impl_glGetUniformBlockIndex(GLuint program, const GLchar * uniformBlockName) {\n    GLuint ret;\n    _pre_call_gl_callback(\"glGetUniformBlockIndex\", (GLADapiproc) glad_glGetUniformBlockIndex, 2, program, uniformBlockName);\n    ret = glad_glGetUniformBlockIndex(program, uniformBlockName);\n    _post_call_gl_callback((void*) &ret, \"glGetUniformBlockIndex\", (GLADapiproc) glad_glGetUniformBlockIndex, 2, program, uniformBlockName);\n    return ret;\n}\nPFNGLGETUNIFORMBLOCKINDEXPROC glad_debug_glGetUniformBlockIndex = glad_debug_impl_glGetUniformBlockIndex;\nPFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetUniformIndices(GLuint program, GLsizei uniformCount, const GLchar *const* uniformNames, GLuint * uniformIndices) {\n    _pre_call_gl_callback(\"glGetUniformIndices\", (GLADapiproc) glad_glGetUniformIndices, 4, program, uniformCount, uniformNames, uniformIndices);\n    glad_glGetUniformIndices(program, uniformCount, uniformNames, uniformIndices);\n    _post_call_gl_callback(NULL, \"glGetUniformIndices\", (GLADapiproc) glad_glGetUniformIndices, 4, program, uniformCount, uniformNames, uniformIndices);\n}\nPFNGLGETUNIFORMINDICESPROC glad_debug_glGetUniformIndices = glad_debug_impl_glGetUniformIndices;\nPFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL;\nstatic GLint GLAD_API_PTR glad_debug_impl_glGetUniformLocation(GLuint program, const GLchar * name) {\n    GLint ret;\n    _pre_call_gl_callback(\"glGetUniformLocation\", (GLADapiproc) glad_glGetUniformLocation, 2, program, name);\n    ret = glad_glGetUniformLocation(program, name);\n    _post_call_gl_callback((void*) &ret, \"glGetUniformLocation\", (GLADapiproc) glad_glGetUniformLocation, 2, program, name);\n    return ret;\n}\nPFNGLGETUNIFORMLOCATIONPROC glad_debug_glGetUniformLocation = glad_debug_impl_glGetUniformLocation;\nPFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetUniformfv(GLuint program, GLint location, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetUniformfv\", (GLADapiproc) glad_glGetUniformfv, 3, program, location, params);\n    glad_glGetUniformfv(program, location, params);\n    _post_call_gl_callback(NULL, \"glGetUniformfv\", (GLADapiproc) glad_glGetUniformfv, 3, program, location, params);\n}\nPFNGLGETUNIFORMFVPROC glad_debug_glGetUniformfv = glad_debug_impl_glGetUniformfv;\nPFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetUniformiv(GLuint program, GLint location, GLint * params) {\n    _pre_call_gl_callback(\"glGetUniformiv\", (GLADapiproc) glad_glGetUniformiv, 3, program, location, params);\n    glad_glGetUniformiv(program, location, params);\n    _post_call_gl_callback(NULL, \"glGetUniformiv\", (GLADapiproc) glad_glGetUniformiv, 3, program, location, params);\n}\nPFNGLGETUNIFORMIVPROC glad_debug_glGetUniformiv = glad_debug_impl_glGetUniformiv;\nPFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetUniformuiv(GLuint program, GLint location, GLuint * params) {\n    _pre_call_gl_callback(\"glGetUniformuiv\", (GLADapiproc) glad_glGetUniformuiv, 3, program, location, params);\n    glad_glGetUniformuiv(program, location, params);\n    _post_call_gl_callback(NULL, \"glGetUniformuiv\", (GLADapiproc) glad_glGetUniformuiv, 3, program, location, params);\n}\nPFNGLGETUNIFORMUIVPROC glad_debug_glGetUniformuiv = glad_debug_impl_glGetUniformuiv;\nPFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetVertexAttribIiv(GLuint index, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetVertexAttribIiv\", (GLADapiproc) glad_glGetVertexAttribIiv, 3, index, pname, params);\n    glad_glGetVertexAttribIiv(index, pname, params);\n    _post_call_gl_callback(NULL, \"glGetVertexAttribIiv\", (GLADapiproc) glad_glGetVertexAttribIiv, 3, index, pname, params);\n}\nPFNGLGETVERTEXATTRIBIIVPROC glad_debug_glGetVertexAttribIiv = glad_debug_impl_glGetVertexAttribIiv;\nPFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetVertexAttribIuiv(GLuint index, GLenum pname, GLuint * params) {\n    _pre_call_gl_callback(\"glGetVertexAttribIuiv\", (GLADapiproc) glad_glGetVertexAttribIuiv, 3, index, pname, params);\n    glad_glGetVertexAttribIuiv(index, pname, params);\n    _post_call_gl_callback(NULL, \"glGetVertexAttribIuiv\", (GLADapiproc) glad_glGetVertexAttribIuiv, 3, index, pname, params);\n}\nPFNGLGETVERTEXATTRIBIUIVPROC glad_debug_glGetVertexAttribIuiv = glad_debug_impl_glGetVertexAttribIuiv;\nPFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetVertexAttribPointerv(GLuint index, GLenum pname, void ** pointer) {\n    _pre_call_gl_callback(\"glGetVertexAttribPointerv\", (GLADapiproc) glad_glGetVertexAttribPointerv, 3, index, pname, pointer);\n    glad_glGetVertexAttribPointerv(index, pname, pointer);\n    _post_call_gl_callback(NULL, \"glGetVertexAttribPointerv\", (GLADapiproc) glad_glGetVertexAttribPointerv, 3, index, pname, pointer);\n}\nPFNGLGETVERTEXATTRIBPOINTERVPROC glad_debug_glGetVertexAttribPointerv = glad_debug_impl_glGetVertexAttribPointerv;\nPFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetVertexAttribdv(GLuint index, GLenum pname, GLdouble * params) {\n    _pre_call_gl_callback(\"glGetVertexAttribdv\", (GLADapiproc) glad_glGetVertexAttribdv, 3, index, pname, params);\n    glad_glGetVertexAttribdv(index, pname, params);\n    _post_call_gl_callback(NULL, \"glGetVertexAttribdv\", (GLADapiproc) glad_glGetVertexAttribdv, 3, index, pname, params);\n}\nPFNGLGETVERTEXATTRIBDVPROC glad_debug_glGetVertexAttribdv = glad_debug_impl_glGetVertexAttribdv;\nPFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetVertexAttribfv\", (GLADapiproc) glad_glGetVertexAttribfv, 3, index, pname, params);\n    glad_glGetVertexAttribfv(index, pname, params);\n    _post_call_gl_callback(NULL, \"glGetVertexAttribfv\", (GLADapiproc) glad_glGetVertexAttribfv, 3, index, pname, params);\n}\nPFNGLGETVERTEXATTRIBFVPROC glad_debug_glGetVertexAttribfv = glad_debug_impl_glGetVertexAttribfv;\nPFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetVertexAttribiv(GLuint index, GLenum pname, GLint * params) {\n    _pre_call_gl_callback(\"glGetVertexAttribiv\", (GLADapiproc) glad_glGetVertexAttribiv, 3, index, pname, params);\n    glad_glGetVertexAttribiv(index, pname, params);\n    _post_call_gl_callback(NULL, \"glGetVertexAttribiv\", (GLADapiproc) glad_glGetVertexAttribiv, 3, index, pname, params);\n}\nPFNGLGETVERTEXATTRIBIVPROC glad_debug_glGetVertexAttribiv = glad_debug_impl_glGetVertexAttribiv;\nPFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetnCompressedTexImageARB(GLenum target, GLint lod, GLsizei bufSize, void * img) {\n    _pre_call_gl_callback(\"glGetnCompressedTexImageARB\", (GLADapiproc) glad_glGetnCompressedTexImageARB, 4, target, lod, bufSize, img);\n    glad_glGetnCompressedTexImageARB(target, lod, bufSize, img);\n    _post_call_gl_callback(NULL, \"glGetnCompressedTexImageARB\", (GLADapiproc) glad_glGetnCompressedTexImageARB, 4, target, lod, bufSize, img);\n}\nPFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_debug_glGetnCompressedTexImageARB = glad_debug_impl_glGetnCompressedTexImageARB;\nPFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetnTexImageARB(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * img) {\n    _pre_call_gl_callback(\"glGetnTexImageARB\", (GLADapiproc) glad_glGetnTexImageARB, 6, target, level, format, type, bufSize, img);\n    glad_glGetnTexImageARB(target, level, format, type, bufSize, img);\n    _post_call_gl_callback(NULL, \"glGetnTexImageARB\", (GLADapiproc) glad_glGetnTexImageARB, 6, target, level, format, type, bufSize, img);\n}\nPFNGLGETNTEXIMAGEARBPROC glad_debug_glGetnTexImageARB = glad_debug_impl_glGetnTexImageARB;\nPFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetnUniformdvARB(GLuint program, GLint location, GLsizei bufSize, GLdouble * params) {\n    _pre_call_gl_callback(\"glGetnUniformdvARB\", (GLADapiproc) glad_glGetnUniformdvARB, 4, program, location, bufSize, params);\n    glad_glGetnUniformdvARB(program, location, bufSize, params);\n    _post_call_gl_callback(NULL, \"glGetnUniformdvARB\", (GLADapiproc) glad_glGetnUniformdvARB, 4, program, location, bufSize, params);\n}\nPFNGLGETNUNIFORMDVARBPROC glad_debug_glGetnUniformdvARB = glad_debug_impl_glGetnUniformdvARB;\nPFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetnUniformfvARB(GLuint program, GLint location, GLsizei bufSize, GLfloat * params) {\n    _pre_call_gl_callback(\"glGetnUniformfvARB\", (GLADapiproc) glad_glGetnUniformfvARB, 4, program, location, bufSize, params);\n    glad_glGetnUniformfvARB(program, location, bufSize, params);\n    _post_call_gl_callback(NULL, \"glGetnUniformfvARB\", (GLADapiproc) glad_glGetnUniformfvARB, 4, program, location, bufSize, params);\n}\nPFNGLGETNUNIFORMFVARBPROC glad_debug_glGetnUniformfvARB = glad_debug_impl_glGetnUniformfvARB;\nPFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetnUniformivARB(GLuint program, GLint location, GLsizei bufSize, GLint * params) {\n    _pre_call_gl_callback(\"glGetnUniformivARB\", (GLADapiproc) glad_glGetnUniformivARB, 4, program, location, bufSize, params);\n    glad_glGetnUniformivARB(program, location, bufSize, params);\n    _post_call_gl_callback(NULL, \"glGetnUniformivARB\", (GLADapiproc) glad_glGetnUniformivARB, 4, program, location, bufSize, params);\n}\nPFNGLGETNUNIFORMIVARBPROC glad_debug_glGetnUniformivARB = glad_debug_impl_glGetnUniformivARB;\nPFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glGetnUniformuivARB(GLuint program, GLint location, GLsizei bufSize, GLuint * params) {\n    _pre_call_gl_callback(\"glGetnUniformuivARB\", (GLADapiproc) glad_glGetnUniformuivARB, 4, program, location, bufSize, params);\n    glad_glGetnUniformuivARB(program, location, bufSize, params);\n    _post_call_gl_callback(NULL, \"glGetnUniformuivARB\", (GLADapiproc) glad_glGetnUniformuivARB, 4, program, location, bufSize, params);\n}\nPFNGLGETNUNIFORMUIVARBPROC glad_debug_glGetnUniformuivARB = glad_debug_impl_glGetnUniformuivARB;\nPFNGLHINTPROC glad_glHint = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glHint(GLenum target, GLenum mode) {\n    _pre_call_gl_callback(\"glHint\", (GLADapiproc) glad_glHint, 2, target, mode);\n    glad_glHint(target, mode);\n    _post_call_gl_callback(NULL, \"glHint\", (GLADapiproc) glad_glHint, 2, target, mode);\n}\nPFNGLHINTPROC glad_debug_glHint = glad_debug_impl_glHint;\nPFNGLINDEXMASKPROC glad_glIndexMask = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexMask(GLuint mask) {\n    _pre_call_gl_callback(\"glIndexMask\", (GLADapiproc) glad_glIndexMask, 1, mask);\n    glad_glIndexMask(mask);\n    _post_call_gl_callback(NULL, \"glIndexMask\", (GLADapiproc) glad_glIndexMask, 1, mask);\n}\nPFNGLINDEXMASKPROC glad_debug_glIndexMask = glad_debug_impl_glIndexMask;\nPFNGLINDEXPOINTERPROC glad_glIndexPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexPointer(GLenum type, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glIndexPointer\", (GLADapiproc) glad_glIndexPointer, 3, type, stride, pointer);\n    glad_glIndexPointer(type, stride, pointer);\n    _post_call_gl_callback(NULL, \"glIndexPointer\", (GLADapiproc) glad_glIndexPointer, 3, type, stride, pointer);\n}\nPFNGLINDEXPOINTERPROC glad_debug_glIndexPointer = glad_debug_impl_glIndexPointer;\nPFNGLINDEXDPROC glad_glIndexd = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexd(GLdouble c) {\n    _pre_call_gl_callback(\"glIndexd\", (GLADapiproc) glad_glIndexd, 1, c);\n    glad_glIndexd(c);\n    _post_call_gl_callback(NULL, \"glIndexd\", (GLADapiproc) glad_glIndexd, 1, c);\n}\nPFNGLINDEXDPROC glad_debug_glIndexd = glad_debug_impl_glIndexd;\nPFNGLINDEXDVPROC glad_glIndexdv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexdv(const GLdouble * c) {\n    _pre_call_gl_callback(\"glIndexdv\", (GLADapiproc) glad_glIndexdv, 1, c);\n    glad_glIndexdv(c);\n    _post_call_gl_callback(NULL, \"glIndexdv\", (GLADapiproc) glad_glIndexdv, 1, c);\n}\nPFNGLINDEXDVPROC glad_debug_glIndexdv = glad_debug_impl_glIndexdv;\nPFNGLINDEXFPROC glad_glIndexf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexf(GLfloat c) {\n    _pre_call_gl_callback(\"glIndexf\", (GLADapiproc) glad_glIndexf, 1, c);\n    glad_glIndexf(c);\n    _post_call_gl_callback(NULL, \"glIndexf\", (GLADapiproc) glad_glIndexf, 1, c);\n}\nPFNGLINDEXFPROC glad_debug_glIndexf = glad_debug_impl_glIndexf;\nPFNGLINDEXFVPROC glad_glIndexfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexfv(const GLfloat * c) {\n    _pre_call_gl_callback(\"glIndexfv\", (GLADapiproc) glad_glIndexfv, 1, c);\n    glad_glIndexfv(c);\n    _post_call_gl_callback(NULL, \"glIndexfv\", (GLADapiproc) glad_glIndexfv, 1, c);\n}\nPFNGLINDEXFVPROC glad_debug_glIndexfv = glad_debug_impl_glIndexfv;\nPFNGLINDEXIPROC glad_glIndexi = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexi(GLint c) {\n    _pre_call_gl_callback(\"glIndexi\", (GLADapiproc) glad_glIndexi, 1, c);\n    glad_glIndexi(c);\n    _post_call_gl_callback(NULL, \"glIndexi\", (GLADapiproc) glad_glIndexi, 1, c);\n}\nPFNGLINDEXIPROC glad_debug_glIndexi = glad_debug_impl_glIndexi;\nPFNGLINDEXIVPROC glad_glIndexiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexiv(const GLint * c) {\n    _pre_call_gl_callback(\"glIndexiv\", (GLADapiproc) glad_glIndexiv, 1, c);\n    glad_glIndexiv(c);\n    _post_call_gl_callback(NULL, \"glIndexiv\", (GLADapiproc) glad_glIndexiv, 1, c);\n}\nPFNGLINDEXIVPROC glad_debug_glIndexiv = glad_debug_impl_glIndexiv;\nPFNGLINDEXSPROC glad_glIndexs = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexs(GLshort c) {\n    _pre_call_gl_callback(\"glIndexs\", (GLADapiproc) glad_glIndexs, 1, c);\n    glad_glIndexs(c);\n    _post_call_gl_callback(NULL, \"glIndexs\", (GLADapiproc) glad_glIndexs, 1, c);\n}\nPFNGLINDEXSPROC glad_debug_glIndexs = glad_debug_impl_glIndexs;\nPFNGLINDEXSVPROC glad_glIndexsv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexsv(const GLshort * c) {\n    _pre_call_gl_callback(\"glIndexsv\", (GLADapiproc) glad_glIndexsv, 1, c);\n    glad_glIndexsv(c);\n    _post_call_gl_callback(NULL, \"glIndexsv\", (GLADapiproc) glad_glIndexsv, 1, c);\n}\nPFNGLINDEXSVPROC glad_debug_glIndexsv = glad_debug_impl_glIndexsv;\nPFNGLINDEXUBPROC glad_glIndexub = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexub(GLubyte c) {\n    _pre_call_gl_callback(\"glIndexub\", (GLADapiproc) glad_glIndexub, 1, c);\n    glad_glIndexub(c);\n    _post_call_gl_callback(NULL, \"glIndexub\", (GLADapiproc) glad_glIndexub, 1, c);\n}\nPFNGLINDEXUBPROC glad_debug_glIndexub = glad_debug_impl_glIndexub;\nPFNGLINDEXUBVPROC glad_glIndexubv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glIndexubv(const GLubyte * c) {\n    _pre_call_gl_callback(\"glIndexubv\", (GLADapiproc) glad_glIndexubv, 1, c);\n    glad_glIndexubv(c);\n    _post_call_gl_callback(NULL, \"glIndexubv\", (GLADapiproc) glad_glIndexubv, 1, c);\n}\nPFNGLINDEXUBVPROC glad_debug_glIndexubv = glad_debug_impl_glIndexubv;\nPFNGLINITNAMESPROC glad_glInitNames = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glInitNames(void) {\n    _pre_call_gl_callback(\"glInitNames\", (GLADapiproc) glad_glInitNames, 0);\n    glad_glInitNames();\n    _post_call_gl_callback(NULL, \"glInitNames\", (GLADapiproc) glad_glInitNames, 0);\n}\nPFNGLINITNAMESPROC glad_debug_glInitNames = glad_debug_impl_glInitNames;\nPFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glInterleavedArrays(GLenum format, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glInterleavedArrays\", (GLADapiproc) glad_glInterleavedArrays, 3, format, stride, pointer);\n    glad_glInterleavedArrays(format, stride, pointer);\n    _post_call_gl_callback(NULL, \"glInterleavedArrays\", (GLADapiproc) glad_glInterleavedArrays, 3, format, stride, pointer);\n}\nPFNGLINTERLEAVEDARRAYSPROC glad_debug_glInterleavedArrays = glad_debug_impl_glInterleavedArrays;\nPFNGLISBUFFERPROC glad_glIsBuffer = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsBuffer(GLuint buffer) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsBuffer\", (GLADapiproc) glad_glIsBuffer, 1, buffer);\n    ret = glad_glIsBuffer(buffer);\n    _post_call_gl_callback((void*) &ret, \"glIsBuffer\", (GLADapiproc) glad_glIsBuffer, 1, buffer);\n    return ret;\n}\nPFNGLISBUFFERPROC glad_debug_glIsBuffer = glad_debug_impl_glIsBuffer;\nPFNGLISENABLEDPROC glad_glIsEnabled = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsEnabled(GLenum cap) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsEnabled\", (GLADapiproc) glad_glIsEnabled, 1, cap);\n    ret = glad_glIsEnabled(cap);\n    _post_call_gl_callback((void*) &ret, \"glIsEnabled\", (GLADapiproc) glad_glIsEnabled, 1, cap);\n    return ret;\n}\nPFNGLISENABLEDPROC glad_debug_glIsEnabled = glad_debug_impl_glIsEnabled;\nPFNGLISENABLEDIPROC glad_glIsEnabledi = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsEnabledi(GLenum target, GLuint index) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsEnabledi\", (GLADapiproc) glad_glIsEnabledi, 2, target, index);\n    ret = glad_glIsEnabledi(target, index);\n    _post_call_gl_callback((void*) &ret, \"glIsEnabledi\", (GLADapiproc) glad_glIsEnabledi, 2, target, index);\n    return ret;\n}\nPFNGLISENABLEDIPROC glad_debug_glIsEnabledi = glad_debug_impl_glIsEnabledi;\nPFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsFramebuffer(GLuint framebuffer) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsFramebuffer\", (GLADapiproc) glad_glIsFramebuffer, 1, framebuffer);\n    ret = glad_glIsFramebuffer(framebuffer);\n    _post_call_gl_callback((void*) &ret, \"glIsFramebuffer\", (GLADapiproc) glad_glIsFramebuffer, 1, framebuffer);\n    return ret;\n}\nPFNGLISFRAMEBUFFERPROC glad_debug_glIsFramebuffer = glad_debug_impl_glIsFramebuffer;\nPFNGLISLISTPROC glad_glIsList = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsList(GLuint list) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsList\", (GLADapiproc) glad_glIsList, 1, list);\n    ret = glad_glIsList(list);\n    _post_call_gl_callback((void*) &ret, \"glIsList\", (GLADapiproc) glad_glIsList, 1, list);\n    return ret;\n}\nPFNGLISLISTPROC glad_debug_glIsList = glad_debug_impl_glIsList;\nPFNGLISPROGRAMPROC glad_glIsProgram = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsProgram(GLuint program) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsProgram\", (GLADapiproc) glad_glIsProgram, 1, program);\n    ret = glad_glIsProgram(program);\n    _post_call_gl_callback((void*) &ret, \"glIsProgram\", (GLADapiproc) glad_glIsProgram, 1, program);\n    return ret;\n}\nPFNGLISPROGRAMPROC glad_debug_glIsProgram = glad_debug_impl_glIsProgram;\nPFNGLISQUERYPROC glad_glIsQuery = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsQuery(GLuint id) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsQuery\", (GLADapiproc) glad_glIsQuery, 1, id);\n    ret = glad_glIsQuery(id);\n    _post_call_gl_callback((void*) &ret, \"glIsQuery\", (GLADapiproc) glad_glIsQuery, 1, id);\n    return ret;\n}\nPFNGLISQUERYPROC glad_debug_glIsQuery = glad_debug_impl_glIsQuery;\nPFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsRenderbuffer(GLuint renderbuffer) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsRenderbuffer\", (GLADapiproc) glad_glIsRenderbuffer, 1, renderbuffer);\n    ret = glad_glIsRenderbuffer(renderbuffer);\n    _post_call_gl_callback((void*) &ret, \"glIsRenderbuffer\", (GLADapiproc) glad_glIsRenderbuffer, 1, renderbuffer);\n    return ret;\n}\nPFNGLISRENDERBUFFERPROC glad_debug_glIsRenderbuffer = glad_debug_impl_glIsRenderbuffer;\nPFNGLISSHADERPROC glad_glIsShader = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsShader(GLuint shader) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsShader\", (GLADapiproc) glad_glIsShader, 1, shader);\n    ret = glad_glIsShader(shader);\n    _post_call_gl_callback((void*) &ret, \"glIsShader\", (GLADapiproc) glad_glIsShader, 1, shader);\n    return ret;\n}\nPFNGLISSHADERPROC glad_debug_glIsShader = glad_debug_impl_glIsShader;\nPFNGLISTEXTUREPROC glad_glIsTexture = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsTexture(GLuint texture) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsTexture\", (GLADapiproc) glad_glIsTexture, 1, texture);\n    ret = glad_glIsTexture(texture);\n    _post_call_gl_callback((void*) &ret, \"glIsTexture\", (GLADapiproc) glad_glIsTexture, 1, texture);\n    return ret;\n}\nPFNGLISTEXTUREPROC glad_debug_glIsTexture = glad_debug_impl_glIsTexture;\nPFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glIsVertexArray(GLuint array) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glIsVertexArray\", (GLADapiproc) glad_glIsVertexArray, 1, array);\n    ret = glad_glIsVertexArray(array);\n    _post_call_gl_callback((void*) &ret, \"glIsVertexArray\", (GLADapiproc) glad_glIsVertexArray, 1, array);\n    return ret;\n}\nPFNGLISVERTEXARRAYPROC glad_debug_glIsVertexArray = glad_debug_impl_glIsVertexArray;\nPFNGLLIGHTMODELFPROC glad_glLightModelf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLightModelf(GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glLightModelf\", (GLADapiproc) glad_glLightModelf, 2, pname, param);\n    glad_glLightModelf(pname, param);\n    _post_call_gl_callback(NULL, \"glLightModelf\", (GLADapiproc) glad_glLightModelf, 2, pname, param);\n}\nPFNGLLIGHTMODELFPROC glad_debug_glLightModelf = glad_debug_impl_glLightModelf;\nPFNGLLIGHTMODELFVPROC glad_glLightModelfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLightModelfv(GLenum pname, const GLfloat * params) {\n    _pre_call_gl_callback(\"glLightModelfv\", (GLADapiproc) glad_glLightModelfv, 2, pname, params);\n    glad_glLightModelfv(pname, params);\n    _post_call_gl_callback(NULL, \"glLightModelfv\", (GLADapiproc) glad_glLightModelfv, 2, pname, params);\n}\nPFNGLLIGHTMODELFVPROC glad_debug_glLightModelfv = glad_debug_impl_glLightModelfv;\nPFNGLLIGHTMODELIPROC glad_glLightModeli = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLightModeli(GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glLightModeli\", (GLADapiproc) glad_glLightModeli, 2, pname, param);\n    glad_glLightModeli(pname, param);\n    _post_call_gl_callback(NULL, \"glLightModeli\", (GLADapiproc) glad_glLightModeli, 2, pname, param);\n}\nPFNGLLIGHTMODELIPROC glad_debug_glLightModeli = glad_debug_impl_glLightModeli;\nPFNGLLIGHTMODELIVPROC glad_glLightModeliv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLightModeliv(GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glLightModeliv\", (GLADapiproc) glad_glLightModeliv, 2, pname, params);\n    glad_glLightModeliv(pname, params);\n    _post_call_gl_callback(NULL, \"glLightModeliv\", (GLADapiproc) glad_glLightModeliv, 2, pname, params);\n}\nPFNGLLIGHTMODELIVPROC glad_debug_glLightModeliv = glad_debug_impl_glLightModeliv;\nPFNGLLIGHTFPROC glad_glLightf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLightf(GLenum light, GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glLightf\", (GLADapiproc) glad_glLightf, 3, light, pname, param);\n    glad_glLightf(light, pname, param);\n    _post_call_gl_callback(NULL, \"glLightf\", (GLADapiproc) glad_glLightf, 3, light, pname, param);\n}\nPFNGLLIGHTFPROC glad_debug_glLightf = glad_debug_impl_glLightf;\nPFNGLLIGHTFVPROC glad_glLightfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLightfv(GLenum light, GLenum pname, const GLfloat * params) {\n    _pre_call_gl_callback(\"glLightfv\", (GLADapiproc) glad_glLightfv, 3, light, pname, params);\n    glad_glLightfv(light, pname, params);\n    _post_call_gl_callback(NULL, \"glLightfv\", (GLADapiproc) glad_glLightfv, 3, light, pname, params);\n}\nPFNGLLIGHTFVPROC glad_debug_glLightfv = glad_debug_impl_glLightfv;\nPFNGLLIGHTIPROC glad_glLighti = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLighti(GLenum light, GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glLighti\", (GLADapiproc) glad_glLighti, 3, light, pname, param);\n    glad_glLighti(light, pname, param);\n    _post_call_gl_callback(NULL, \"glLighti\", (GLADapiproc) glad_glLighti, 3, light, pname, param);\n}\nPFNGLLIGHTIPROC glad_debug_glLighti = glad_debug_impl_glLighti;\nPFNGLLIGHTIVPROC glad_glLightiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLightiv(GLenum light, GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glLightiv\", (GLADapiproc) glad_glLightiv, 3, light, pname, params);\n    glad_glLightiv(light, pname, params);\n    _post_call_gl_callback(NULL, \"glLightiv\", (GLADapiproc) glad_glLightiv, 3, light, pname, params);\n}\nPFNGLLIGHTIVPROC glad_debug_glLightiv = glad_debug_impl_glLightiv;\nPFNGLLINESTIPPLEPROC glad_glLineStipple = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLineStipple(GLint factor, GLushort pattern) {\n    _pre_call_gl_callback(\"glLineStipple\", (GLADapiproc) glad_glLineStipple, 2, factor, pattern);\n    glad_glLineStipple(factor, pattern);\n    _post_call_gl_callback(NULL, \"glLineStipple\", (GLADapiproc) glad_glLineStipple, 2, factor, pattern);\n}\nPFNGLLINESTIPPLEPROC glad_debug_glLineStipple = glad_debug_impl_glLineStipple;\nPFNGLLINEWIDTHPROC glad_glLineWidth = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLineWidth(GLfloat width) {\n    _pre_call_gl_callback(\"glLineWidth\", (GLADapiproc) glad_glLineWidth, 1, width);\n    glad_glLineWidth(width);\n    _post_call_gl_callback(NULL, \"glLineWidth\", (GLADapiproc) glad_glLineWidth, 1, width);\n}\nPFNGLLINEWIDTHPROC glad_debug_glLineWidth = glad_debug_impl_glLineWidth;\nPFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLinkProgram(GLuint program) {\n    _pre_call_gl_callback(\"glLinkProgram\", (GLADapiproc) glad_glLinkProgram, 1, program);\n    glad_glLinkProgram(program);\n    _post_call_gl_callback(NULL, \"glLinkProgram\", (GLADapiproc) glad_glLinkProgram, 1, program);\n}\nPFNGLLINKPROGRAMPROC glad_debug_glLinkProgram = glad_debug_impl_glLinkProgram;\nPFNGLLISTBASEPROC glad_glListBase = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glListBase(GLuint base) {\n    _pre_call_gl_callback(\"glListBase\", (GLADapiproc) glad_glListBase, 1, base);\n    glad_glListBase(base);\n    _post_call_gl_callback(NULL, \"glListBase\", (GLADapiproc) glad_glListBase, 1, base);\n}\nPFNGLLISTBASEPROC glad_debug_glListBase = glad_debug_impl_glListBase;\nPFNGLLOADIDENTITYPROC glad_glLoadIdentity = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLoadIdentity(void) {\n    _pre_call_gl_callback(\"glLoadIdentity\", (GLADapiproc) glad_glLoadIdentity, 0);\n    glad_glLoadIdentity();\n    _post_call_gl_callback(NULL, \"glLoadIdentity\", (GLADapiproc) glad_glLoadIdentity, 0);\n}\nPFNGLLOADIDENTITYPROC glad_debug_glLoadIdentity = glad_debug_impl_glLoadIdentity;\nPFNGLLOADMATRIXDPROC glad_glLoadMatrixd = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLoadMatrixd(const GLdouble * m) {\n    _pre_call_gl_callback(\"glLoadMatrixd\", (GLADapiproc) glad_glLoadMatrixd, 1, m);\n    glad_glLoadMatrixd(m);\n    _post_call_gl_callback(NULL, \"glLoadMatrixd\", (GLADapiproc) glad_glLoadMatrixd, 1, m);\n}\nPFNGLLOADMATRIXDPROC glad_debug_glLoadMatrixd = glad_debug_impl_glLoadMatrixd;\nPFNGLLOADMATRIXFPROC glad_glLoadMatrixf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLoadMatrixf(const GLfloat * m) {\n    _pre_call_gl_callback(\"glLoadMatrixf\", (GLADapiproc) glad_glLoadMatrixf, 1, m);\n    glad_glLoadMatrixf(m);\n    _post_call_gl_callback(NULL, \"glLoadMatrixf\", (GLADapiproc) glad_glLoadMatrixf, 1, m);\n}\nPFNGLLOADMATRIXFPROC glad_debug_glLoadMatrixf = glad_debug_impl_glLoadMatrixf;\nPFNGLLOADNAMEPROC glad_glLoadName = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLoadName(GLuint name) {\n    _pre_call_gl_callback(\"glLoadName\", (GLADapiproc) glad_glLoadName, 1, name);\n    glad_glLoadName(name);\n    _post_call_gl_callback(NULL, \"glLoadName\", (GLADapiproc) glad_glLoadName, 1, name);\n}\nPFNGLLOADNAMEPROC glad_debug_glLoadName = glad_debug_impl_glLoadName;\nPFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLoadTransposeMatrixd(const GLdouble * m) {\n    _pre_call_gl_callback(\"glLoadTransposeMatrixd\", (GLADapiproc) glad_glLoadTransposeMatrixd, 1, m);\n    glad_glLoadTransposeMatrixd(m);\n    _post_call_gl_callback(NULL, \"glLoadTransposeMatrixd\", (GLADapiproc) glad_glLoadTransposeMatrixd, 1, m);\n}\nPFNGLLOADTRANSPOSEMATRIXDPROC glad_debug_glLoadTransposeMatrixd = glad_debug_impl_glLoadTransposeMatrixd;\nPFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLoadTransposeMatrixf(const GLfloat * m) {\n    _pre_call_gl_callback(\"glLoadTransposeMatrixf\", (GLADapiproc) glad_glLoadTransposeMatrixf, 1, m);\n    glad_glLoadTransposeMatrixf(m);\n    _post_call_gl_callback(NULL, \"glLoadTransposeMatrixf\", (GLADapiproc) glad_glLoadTransposeMatrixf, 1, m);\n}\nPFNGLLOADTRANSPOSEMATRIXFPROC glad_debug_glLoadTransposeMatrixf = glad_debug_impl_glLoadTransposeMatrixf;\nPFNGLLOGICOPPROC glad_glLogicOp = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glLogicOp(GLenum opcode) {\n    _pre_call_gl_callback(\"glLogicOp\", (GLADapiproc) glad_glLogicOp, 1, opcode);\n    glad_glLogicOp(opcode);\n    _post_call_gl_callback(NULL, \"glLogicOp\", (GLADapiproc) glad_glLogicOp, 1, opcode);\n}\nPFNGLLOGICOPPROC glad_debug_glLogicOp = glad_debug_impl_glLogicOp;\nPFNGLMAP1DPROC glad_glMap1d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMap1d(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points) {\n    _pre_call_gl_callback(\"glMap1d\", (GLADapiproc) glad_glMap1d, 6, target, u1, u2, stride, order, points);\n    glad_glMap1d(target, u1, u2, stride, order, points);\n    _post_call_gl_callback(NULL, \"glMap1d\", (GLADapiproc) glad_glMap1d, 6, target, u1, u2, stride, order, points);\n}\nPFNGLMAP1DPROC glad_debug_glMap1d = glad_debug_impl_glMap1d;\nPFNGLMAP1FPROC glad_glMap1f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMap1f(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points) {\n    _pre_call_gl_callback(\"glMap1f\", (GLADapiproc) glad_glMap1f, 6, target, u1, u2, stride, order, points);\n    glad_glMap1f(target, u1, u2, stride, order, points);\n    _post_call_gl_callback(NULL, \"glMap1f\", (GLADapiproc) glad_glMap1f, 6, target, u1, u2, stride, order, points);\n}\nPFNGLMAP1FPROC glad_debug_glMap1f = glad_debug_impl_glMap1f;\nPFNGLMAP2DPROC glad_glMap2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMap2d(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points) {\n    _pre_call_gl_callback(\"glMap2d\", (GLADapiproc) glad_glMap2d, 10, target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points);\n    glad_glMap2d(target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points);\n    _post_call_gl_callback(NULL, \"glMap2d\", (GLADapiproc) glad_glMap2d, 10, target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points);\n}\nPFNGLMAP2DPROC glad_debug_glMap2d = glad_debug_impl_glMap2d;\nPFNGLMAP2FPROC glad_glMap2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMap2f(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points) {\n    _pre_call_gl_callback(\"glMap2f\", (GLADapiproc) glad_glMap2f, 10, target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points);\n    glad_glMap2f(target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points);\n    _post_call_gl_callback(NULL, \"glMap2f\", (GLADapiproc) glad_glMap2f, 10, target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points);\n}\nPFNGLMAP2FPROC glad_debug_glMap2f = glad_debug_impl_glMap2f;\nPFNGLMAPBUFFERPROC glad_glMapBuffer = NULL;\nstatic void * GLAD_API_PTR glad_debug_impl_glMapBuffer(GLenum target, GLenum access) {\n    void * ret;\n    _pre_call_gl_callback(\"glMapBuffer\", (GLADapiproc) glad_glMapBuffer, 2, target, access);\n    ret = glad_glMapBuffer(target, access);\n    _post_call_gl_callback((void*) &ret, \"glMapBuffer\", (GLADapiproc) glad_glMapBuffer, 2, target, access);\n    return ret;\n}\nPFNGLMAPBUFFERPROC glad_debug_glMapBuffer = glad_debug_impl_glMapBuffer;\nPFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL;\nstatic void * GLAD_API_PTR glad_debug_impl_glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {\n    void * ret;\n    _pre_call_gl_callback(\"glMapBufferRange\", (GLADapiproc) glad_glMapBufferRange, 4, target, offset, length, access);\n    ret = glad_glMapBufferRange(target, offset, length, access);\n    _post_call_gl_callback((void*) &ret, \"glMapBufferRange\", (GLADapiproc) glad_glMapBufferRange, 4, target, offset, length, access);\n    return ret;\n}\nPFNGLMAPBUFFERRANGEPROC glad_debug_glMapBufferRange = glad_debug_impl_glMapBufferRange;\nPFNGLMAPGRID1DPROC glad_glMapGrid1d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMapGrid1d(GLint un, GLdouble u1, GLdouble u2) {\n    _pre_call_gl_callback(\"glMapGrid1d\", (GLADapiproc) glad_glMapGrid1d, 3, un, u1, u2);\n    glad_glMapGrid1d(un, u1, u2);\n    _post_call_gl_callback(NULL, \"glMapGrid1d\", (GLADapiproc) glad_glMapGrid1d, 3, un, u1, u2);\n}\nPFNGLMAPGRID1DPROC glad_debug_glMapGrid1d = glad_debug_impl_glMapGrid1d;\nPFNGLMAPGRID1FPROC glad_glMapGrid1f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMapGrid1f(GLint un, GLfloat u1, GLfloat u2) {\n    _pre_call_gl_callback(\"glMapGrid1f\", (GLADapiproc) glad_glMapGrid1f, 3, un, u1, u2);\n    glad_glMapGrid1f(un, u1, u2);\n    _post_call_gl_callback(NULL, \"glMapGrid1f\", (GLADapiproc) glad_glMapGrid1f, 3, un, u1, u2);\n}\nPFNGLMAPGRID1FPROC glad_debug_glMapGrid1f = glad_debug_impl_glMapGrid1f;\nPFNGLMAPGRID2DPROC glad_glMapGrid2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMapGrid2d(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2) {\n    _pre_call_gl_callback(\"glMapGrid2d\", (GLADapiproc) glad_glMapGrid2d, 6, un, u1, u2, vn, v1, v2);\n    glad_glMapGrid2d(un, u1, u2, vn, v1, v2);\n    _post_call_gl_callback(NULL, \"glMapGrid2d\", (GLADapiproc) glad_glMapGrid2d, 6, un, u1, u2, vn, v1, v2);\n}\nPFNGLMAPGRID2DPROC glad_debug_glMapGrid2d = glad_debug_impl_glMapGrid2d;\nPFNGLMAPGRID2FPROC glad_glMapGrid2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMapGrid2f(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2) {\n    _pre_call_gl_callback(\"glMapGrid2f\", (GLADapiproc) glad_glMapGrid2f, 6, un, u1, u2, vn, v1, v2);\n    glad_glMapGrid2f(un, u1, u2, vn, v1, v2);\n    _post_call_gl_callback(NULL, \"glMapGrid2f\", (GLADapiproc) glad_glMapGrid2f, 6, un, u1, u2, vn, v1, v2);\n}\nPFNGLMAPGRID2FPROC glad_debug_glMapGrid2f = glad_debug_impl_glMapGrid2f;\nPFNGLMATERIALFPROC glad_glMaterialf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMaterialf(GLenum face, GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glMaterialf\", (GLADapiproc) glad_glMaterialf, 3, face, pname, param);\n    glad_glMaterialf(face, pname, param);\n    _post_call_gl_callback(NULL, \"glMaterialf\", (GLADapiproc) glad_glMaterialf, 3, face, pname, param);\n}\nPFNGLMATERIALFPROC glad_debug_glMaterialf = glad_debug_impl_glMaterialf;\nPFNGLMATERIALFVPROC glad_glMaterialfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMaterialfv(GLenum face, GLenum pname, const GLfloat * params) {\n    _pre_call_gl_callback(\"glMaterialfv\", (GLADapiproc) glad_glMaterialfv, 3, face, pname, params);\n    glad_glMaterialfv(face, pname, params);\n    _post_call_gl_callback(NULL, \"glMaterialfv\", (GLADapiproc) glad_glMaterialfv, 3, face, pname, params);\n}\nPFNGLMATERIALFVPROC glad_debug_glMaterialfv = glad_debug_impl_glMaterialfv;\nPFNGLMATERIALIPROC glad_glMateriali = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMateriali(GLenum face, GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glMateriali\", (GLADapiproc) glad_glMateriali, 3, face, pname, param);\n    glad_glMateriali(face, pname, param);\n    _post_call_gl_callback(NULL, \"glMateriali\", (GLADapiproc) glad_glMateriali, 3, face, pname, param);\n}\nPFNGLMATERIALIPROC glad_debug_glMateriali = glad_debug_impl_glMateriali;\nPFNGLMATERIALIVPROC glad_glMaterialiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMaterialiv(GLenum face, GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glMaterialiv\", (GLADapiproc) glad_glMaterialiv, 3, face, pname, params);\n    glad_glMaterialiv(face, pname, params);\n    _post_call_gl_callback(NULL, \"glMaterialiv\", (GLADapiproc) glad_glMaterialiv, 3, face, pname, params);\n}\nPFNGLMATERIALIVPROC glad_debug_glMaterialiv = glad_debug_impl_glMaterialiv;\nPFNGLMATRIXMODEPROC glad_glMatrixMode = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMatrixMode(GLenum mode) {\n    _pre_call_gl_callback(\"glMatrixMode\", (GLADapiproc) glad_glMatrixMode, 1, mode);\n    glad_glMatrixMode(mode);\n    _post_call_gl_callback(NULL, \"glMatrixMode\", (GLADapiproc) glad_glMatrixMode, 1, mode);\n}\nPFNGLMATRIXMODEPROC glad_debug_glMatrixMode = glad_debug_impl_glMatrixMode;\nPFNGLMULTMATRIXDPROC glad_glMultMatrixd = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultMatrixd(const GLdouble * m) {\n    _pre_call_gl_callback(\"glMultMatrixd\", (GLADapiproc) glad_glMultMatrixd, 1, m);\n    glad_glMultMatrixd(m);\n    _post_call_gl_callback(NULL, \"glMultMatrixd\", (GLADapiproc) glad_glMultMatrixd, 1, m);\n}\nPFNGLMULTMATRIXDPROC glad_debug_glMultMatrixd = glad_debug_impl_glMultMatrixd;\nPFNGLMULTMATRIXFPROC glad_glMultMatrixf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultMatrixf(const GLfloat * m) {\n    _pre_call_gl_callback(\"glMultMatrixf\", (GLADapiproc) glad_glMultMatrixf, 1, m);\n    glad_glMultMatrixf(m);\n    _post_call_gl_callback(NULL, \"glMultMatrixf\", (GLADapiproc) glad_glMultMatrixf, 1, m);\n}\nPFNGLMULTMATRIXFPROC glad_debug_glMultMatrixf = glad_debug_impl_glMultMatrixf;\nPFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultTransposeMatrixd(const GLdouble * m) {\n    _pre_call_gl_callback(\"glMultTransposeMatrixd\", (GLADapiproc) glad_glMultTransposeMatrixd, 1, m);\n    glad_glMultTransposeMatrixd(m);\n    _post_call_gl_callback(NULL, \"glMultTransposeMatrixd\", (GLADapiproc) glad_glMultTransposeMatrixd, 1, m);\n}\nPFNGLMULTTRANSPOSEMATRIXDPROC glad_debug_glMultTransposeMatrixd = glad_debug_impl_glMultTransposeMatrixd;\nPFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultTransposeMatrixf(const GLfloat * m) {\n    _pre_call_gl_callback(\"glMultTransposeMatrixf\", (GLADapiproc) glad_glMultTransposeMatrixf, 1, m);\n    glad_glMultTransposeMatrixf(m);\n    _post_call_gl_callback(NULL, \"glMultTransposeMatrixf\", (GLADapiproc) glad_glMultTransposeMatrixf, 1, m);\n}\nPFNGLMULTTRANSPOSEMATRIXFPROC glad_debug_glMultTransposeMatrixf = glad_debug_impl_glMultTransposeMatrixf;\nPFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiDrawArrays(GLenum mode, const GLint * first, const GLsizei * count, GLsizei drawcount) {\n    _pre_call_gl_callback(\"glMultiDrawArrays\", (GLADapiproc) glad_glMultiDrawArrays, 4, mode, first, count, drawcount);\n    glad_glMultiDrawArrays(mode, first, count, drawcount);\n    _post_call_gl_callback(NULL, \"glMultiDrawArrays\", (GLADapiproc) glad_glMultiDrawArrays, 4, mode, first, count, drawcount);\n}\nPFNGLMULTIDRAWARRAYSPROC glad_debug_glMultiDrawArrays = glad_debug_impl_glMultiDrawArrays;\nPFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiDrawElements(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount) {\n    _pre_call_gl_callback(\"glMultiDrawElements\", (GLADapiproc) glad_glMultiDrawElements, 5, mode, count, type, indices, drawcount);\n    glad_glMultiDrawElements(mode, count, type, indices, drawcount);\n    _post_call_gl_callback(NULL, \"glMultiDrawElements\", (GLADapiproc) glad_glMultiDrawElements, 5, mode, count, type, indices, drawcount);\n}\nPFNGLMULTIDRAWELEMENTSPROC glad_debug_glMultiDrawElements = glad_debug_impl_glMultiDrawElements;\nPFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord1d(GLenum target, GLdouble s) {\n    _pre_call_gl_callback(\"glMultiTexCoord1d\", (GLADapiproc) glad_glMultiTexCoord1d, 2, target, s);\n    glad_glMultiTexCoord1d(target, s);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord1d\", (GLADapiproc) glad_glMultiTexCoord1d, 2, target, s);\n}\nPFNGLMULTITEXCOORD1DPROC glad_debug_glMultiTexCoord1d = glad_debug_impl_glMultiTexCoord1d;\nPFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord1dv(GLenum target, const GLdouble * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord1dv\", (GLADapiproc) glad_glMultiTexCoord1dv, 2, target, v);\n    glad_glMultiTexCoord1dv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord1dv\", (GLADapiproc) glad_glMultiTexCoord1dv, 2, target, v);\n}\nPFNGLMULTITEXCOORD1DVPROC glad_debug_glMultiTexCoord1dv = glad_debug_impl_glMultiTexCoord1dv;\nPFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord1f(GLenum target, GLfloat s) {\n    _pre_call_gl_callback(\"glMultiTexCoord1f\", (GLADapiproc) glad_glMultiTexCoord1f, 2, target, s);\n    glad_glMultiTexCoord1f(target, s);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord1f\", (GLADapiproc) glad_glMultiTexCoord1f, 2, target, s);\n}\nPFNGLMULTITEXCOORD1FPROC glad_debug_glMultiTexCoord1f = glad_debug_impl_glMultiTexCoord1f;\nPFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord1fv(GLenum target, const GLfloat * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord1fv\", (GLADapiproc) glad_glMultiTexCoord1fv, 2, target, v);\n    glad_glMultiTexCoord1fv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord1fv\", (GLADapiproc) glad_glMultiTexCoord1fv, 2, target, v);\n}\nPFNGLMULTITEXCOORD1FVPROC glad_debug_glMultiTexCoord1fv = glad_debug_impl_glMultiTexCoord1fv;\nPFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord1i(GLenum target, GLint s) {\n    _pre_call_gl_callback(\"glMultiTexCoord1i\", (GLADapiproc) glad_glMultiTexCoord1i, 2, target, s);\n    glad_glMultiTexCoord1i(target, s);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord1i\", (GLADapiproc) glad_glMultiTexCoord1i, 2, target, s);\n}\nPFNGLMULTITEXCOORD1IPROC glad_debug_glMultiTexCoord1i = glad_debug_impl_glMultiTexCoord1i;\nPFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord1iv(GLenum target, const GLint * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord1iv\", (GLADapiproc) glad_glMultiTexCoord1iv, 2, target, v);\n    glad_glMultiTexCoord1iv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord1iv\", (GLADapiproc) glad_glMultiTexCoord1iv, 2, target, v);\n}\nPFNGLMULTITEXCOORD1IVPROC glad_debug_glMultiTexCoord1iv = glad_debug_impl_glMultiTexCoord1iv;\nPFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord1s(GLenum target, GLshort s) {\n    _pre_call_gl_callback(\"glMultiTexCoord1s\", (GLADapiproc) glad_glMultiTexCoord1s, 2, target, s);\n    glad_glMultiTexCoord1s(target, s);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord1s\", (GLADapiproc) glad_glMultiTexCoord1s, 2, target, s);\n}\nPFNGLMULTITEXCOORD1SPROC glad_debug_glMultiTexCoord1s = glad_debug_impl_glMultiTexCoord1s;\nPFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord1sv(GLenum target, const GLshort * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord1sv\", (GLADapiproc) glad_glMultiTexCoord1sv, 2, target, v);\n    glad_glMultiTexCoord1sv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord1sv\", (GLADapiproc) glad_glMultiTexCoord1sv, 2, target, v);\n}\nPFNGLMULTITEXCOORD1SVPROC glad_debug_glMultiTexCoord1sv = glad_debug_impl_glMultiTexCoord1sv;\nPFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord2d(GLenum target, GLdouble s, GLdouble t) {\n    _pre_call_gl_callback(\"glMultiTexCoord2d\", (GLADapiproc) glad_glMultiTexCoord2d, 3, target, s, t);\n    glad_glMultiTexCoord2d(target, s, t);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord2d\", (GLADapiproc) glad_glMultiTexCoord2d, 3, target, s, t);\n}\nPFNGLMULTITEXCOORD2DPROC glad_debug_glMultiTexCoord2d = glad_debug_impl_glMultiTexCoord2d;\nPFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord2dv(GLenum target, const GLdouble * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord2dv\", (GLADapiproc) glad_glMultiTexCoord2dv, 2, target, v);\n    glad_glMultiTexCoord2dv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord2dv\", (GLADapiproc) glad_glMultiTexCoord2dv, 2, target, v);\n}\nPFNGLMULTITEXCOORD2DVPROC glad_debug_glMultiTexCoord2dv = glad_debug_impl_glMultiTexCoord2dv;\nPFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord2f(GLenum target, GLfloat s, GLfloat t) {\n    _pre_call_gl_callback(\"glMultiTexCoord2f\", (GLADapiproc) glad_glMultiTexCoord2f, 3, target, s, t);\n    glad_glMultiTexCoord2f(target, s, t);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord2f\", (GLADapiproc) glad_glMultiTexCoord2f, 3, target, s, t);\n}\nPFNGLMULTITEXCOORD2FPROC glad_debug_glMultiTexCoord2f = glad_debug_impl_glMultiTexCoord2f;\nPFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord2fv(GLenum target, const GLfloat * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord2fv\", (GLADapiproc) glad_glMultiTexCoord2fv, 2, target, v);\n    glad_glMultiTexCoord2fv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord2fv\", (GLADapiproc) glad_glMultiTexCoord2fv, 2, target, v);\n}\nPFNGLMULTITEXCOORD2FVPROC glad_debug_glMultiTexCoord2fv = glad_debug_impl_glMultiTexCoord2fv;\nPFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord2i(GLenum target, GLint s, GLint t) {\n    _pre_call_gl_callback(\"glMultiTexCoord2i\", (GLADapiproc) glad_glMultiTexCoord2i, 3, target, s, t);\n    glad_glMultiTexCoord2i(target, s, t);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord2i\", (GLADapiproc) glad_glMultiTexCoord2i, 3, target, s, t);\n}\nPFNGLMULTITEXCOORD2IPROC glad_debug_glMultiTexCoord2i = glad_debug_impl_glMultiTexCoord2i;\nPFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord2iv(GLenum target, const GLint * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord2iv\", (GLADapiproc) glad_glMultiTexCoord2iv, 2, target, v);\n    glad_glMultiTexCoord2iv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord2iv\", (GLADapiproc) glad_glMultiTexCoord2iv, 2, target, v);\n}\nPFNGLMULTITEXCOORD2IVPROC glad_debug_glMultiTexCoord2iv = glad_debug_impl_glMultiTexCoord2iv;\nPFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord2s(GLenum target, GLshort s, GLshort t) {\n    _pre_call_gl_callback(\"glMultiTexCoord2s\", (GLADapiproc) glad_glMultiTexCoord2s, 3, target, s, t);\n    glad_glMultiTexCoord2s(target, s, t);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord2s\", (GLADapiproc) glad_glMultiTexCoord2s, 3, target, s, t);\n}\nPFNGLMULTITEXCOORD2SPROC glad_debug_glMultiTexCoord2s = glad_debug_impl_glMultiTexCoord2s;\nPFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord2sv(GLenum target, const GLshort * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord2sv\", (GLADapiproc) glad_glMultiTexCoord2sv, 2, target, v);\n    glad_glMultiTexCoord2sv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord2sv\", (GLADapiproc) glad_glMultiTexCoord2sv, 2, target, v);\n}\nPFNGLMULTITEXCOORD2SVPROC glad_debug_glMultiTexCoord2sv = glad_debug_impl_glMultiTexCoord2sv;\nPFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord3d(GLenum target, GLdouble s, GLdouble t, GLdouble r) {\n    _pre_call_gl_callback(\"glMultiTexCoord3d\", (GLADapiproc) glad_glMultiTexCoord3d, 4, target, s, t, r);\n    glad_glMultiTexCoord3d(target, s, t, r);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord3d\", (GLADapiproc) glad_glMultiTexCoord3d, 4, target, s, t, r);\n}\nPFNGLMULTITEXCOORD3DPROC glad_debug_glMultiTexCoord3d = glad_debug_impl_glMultiTexCoord3d;\nPFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord3dv(GLenum target, const GLdouble * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord3dv\", (GLADapiproc) glad_glMultiTexCoord3dv, 2, target, v);\n    glad_glMultiTexCoord3dv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord3dv\", (GLADapiproc) glad_glMultiTexCoord3dv, 2, target, v);\n}\nPFNGLMULTITEXCOORD3DVPROC glad_debug_glMultiTexCoord3dv = glad_debug_impl_glMultiTexCoord3dv;\nPFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord3f(GLenum target, GLfloat s, GLfloat t, GLfloat r) {\n    _pre_call_gl_callback(\"glMultiTexCoord3f\", (GLADapiproc) glad_glMultiTexCoord3f, 4, target, s, t, r);\n    glad_glMultiTexCoord3f(target, s, t, r);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord3f\", (GLADapiproc) glad_glMultiTexCoord3f, 4, target, s, t, r);\n}\nPFNGLMULTITEXCOORD3FPROC glad_debug_glMultiTexCoord3f = glad_debug_impl_glMultiTexCoord3f;\nPFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord3fv(GLenum target, const GLfloat * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord3fv\", (GLADapiproc) glad_glMultiTexCoord3fv, 2, target, v);\n    glad_glMultiTexCoord3fv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord3fv\", (GLADapiproc) glad_glMultiTexCoord3fv, 2, target, v);\n}\nPFNGLMULTITEXCOORD3FVPROC glad_debug_glMultiTexCoord3fv = glad_debug_impl_glMultiTexCoord3fv;\nPFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord3i(GLenum target, GLint s, GLint t, GLint r) {\n    _pre_call_gl_callback(\"glMultiTexCoord3i\", (GLADapiproc) glad_glMultiTexCoord3i, 4, target, s, t, r);\n    glad_glMultiTexCoord3i(target, s, t, r);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord3i\", (GLADapiproc) glad_glMultiTexCoord3i, 4, target, s, t, r);\n}\nPFNGLMULTITEXCOORD3IPROC glad_debug_glMultiTexCoord3i = glad_debug_impl_glMultiTexCoord3i;\nPFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord3iv(GLenum target, const GLint * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord3iv\", (GLADapiproc) glad_glMultiTexCoord3iv, 2, target, v);\n    glad_glMultiTexCoord3iv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord3iv\", (GLADapiproc) glad_glMultiTexCoord3iv, 2, target, v);\n}\nPFNGLMULTITEXCOORD3IVPROC glad_debug_glMultiTexCoord3iv = glad_debug_impl_glMultiTexCoord3iv;\nPFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord3s(GLenum target, GLshort s, GLshort t, GLshort r) {\n    _pre_call_gl_callback(\"glMultiTexCoord3s\", (GLADapiproc) glad_glMultiTexCoord3s, 4, target, s, t, r);\n    glad_glMultiTexCoord3s(target, s, t, r);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord3s\", (GLADapiproc) glad_glMultiTexCoord3s, 4, target, s, t, r);\n}\nPFNGLMULTITEXCOORD3SPROC glad_debug_glMultiTexCoord3s = glad_debug_impl_glMultiTexCoord3s;\nPFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord3sv(GLenum target, const GLshort * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord3sv\", (GLADapiproc) glad_glMultiTexCoord3sv, 2, target, v);\n    glad_glMultiTexCoord3sv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord3sv\", (GLADapiproc) glad_glMultiTexCoord3sv, 2, target, v);\n}\nPFNGLMULTITEXCOORD3SVPROC glad_debug_glMultiTexCoord3sv = glad_debug_impl_glMultiTexCoord3sv;\nPFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord4d(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q) {\n    _pre_call_gl_callback(\"glMultiTexCoord4d\", (GLADapiproc) glad_glMultiTexCoord4d, 5, target, s, t, r, q);\n    glad_glMultiTexCoord4d(target, s, t, r, q);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord4d\", (GLADapiproc) glad_glMultiTexCoord4d, 5, target, s, t, r, q);\n}\nPFNGLMULTITEXCOORD4DPROC glad_debug_glMultiTexCoord4d = glad_debug_impl_glMultiTexCoord4d;\nPFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord4dv(GLenum target, const GLdouble * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord4dv\", (GLADapiproc) glad_glMultiTexCoord4dv, 2, target, v);\n    glad_glMultiTexCoord4dv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord4dv\", (GLADapiproc) glad_glMultiTexCoord4dv, 2, target, v);\n}\nPFNGLMULTITEXCOORD4DVPROC glad_debug_glMultiTexCoord4dv = glad_debug_impl_glMultiTexCoord4dv;\nPFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) {\n    _pre_call_gl_callback(\"glMultiTexCoord4f\", (GLADapiproc) glad_glMultiTexCoord4f, 5, target, s, t, r, q);\n    glad_glMultiTexCoord4f(target, s, t, r, q);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord4f\", (GLADapiproc) glad_glMultiTexCoord4f, 5, target, s, t, r, q);\n}\nPFNGLMULTITEXCOORD4FPROC glad_debug_glMultiTexCoord4f = glad_debug_impl_glMultiTexCoord4f;\nPFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord4fv(GLenum target, const GLfloat * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord4fv\", (GLADapiproc) glad_glMultiTexCoord4fv, 2, target, v);\n    glad_glMultiTexCoord4fv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord4fv\", (GLADapiproc) glad_glMultiTexCoord4fv, 2, target, v);\n}\nPFNGLMULTITEXCOORD4FVPROC glad_debug_glMultiTexCoord4fv = glad_debug_impl_glMultiTexCoord4fv;\nPFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord4i(GLenum target, GLint s, GLint t, GLint r, GLint q) {\n    _pre_call_gl_callback(\"glMultiTexCoord4i\", (GLADapiproc) glad_glMultiTexCoord4i, 5, target, s, t, r, q);\n    glad_glMultiTexCoord4i(target, s, t, r, q);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord4i\", (GLADapiproc) glad_glMultiTexCoord4i, 5, target, s, t, r, q);\n}\nPFNGLMULTITEXCOORD4IPROC glad_debug_glMultiTexCoord4i = glad_debug_impl_glMultiTexCoord4i;\nPFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord4iv(GLenum target, const GLint * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord4iv\", (GLADapiproc) glad_glMultiTexCoord4iv, 2, target, v);\n    glad_glMultiTexCoord4iv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord4iv\", (GLADapiproc) glad_glMultiTexCoord4iv, 2, target, v);\n}\nPFNGLMULTITEXCOORD4IVPROC glad_debug_glMultiTexCoord4iv = glad_debug_impl_glMultiTexCoord4iv;\nPFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord4s(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q) {\n    _pre_call_gl_callback(\"glMultiTexCoord4s\", (GLADapiproc) glad_glMultiTexCoord4s, 5, target, s, t, r, q);\n    glad_glMultiTexCoord4s(target, s, t, r, q);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord4s\", (GLADapiproc) glad_glMultiTexCoord4s, 5, target, s, t, r, q);\n}\nPFNGLMULTITEXCOORD4SPROC glad_debug_glMultiTexCoord4s = glad_debug_impl_glMultiTexCoord4s;\nPFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glMultiTexCoord4sv(GLenum target, const GLshort * v) {\n    _pre_call_gl_callback(\"glMultiTexCoord4sv\", (GLADapiproc) glad_glMultiTexCoord4sv, 2, target, v);\n    glad_glMultiTexCoord4sv(target, v);\n    _post_call_gl_callback(NULL, \"glMultiTexCoord4sv\", (GLADapiproc) glad_glMultiTexCoord4sv, 2, target, v);\n}\nPFNGLMULTITEXCOORD4SVPROC glad_debug_glMultiTexCoord4sv = glad_debug_impl_glMultiTexCoord4sv;\nPFNGLNEWLISTPROC glad_glNewList = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNewList(GLuint list, GLenum mode) {\n    _pre_call_gl_callback(\"glNewList\", (GLADapiproc) glad_glNewList, 2, list, mode);\n    glad_glNewList(list, mode);\n    _post_call_gl_callback(NULL, \"glNewList\", (GLADapiproc) glad_glNewList, 2, list, mode);\n}\nPFNGLNEWLISTPROC glad_debug_glNewList = glad_debug_impl_glNewList;\nPFNGLNORMAL3BPROC glad_glNormal3b = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3b(GLbyte nx, GLbyte ny, GLbyte nz) {\n    _pre_call_gl_callback(\"glNormal3b\", (GLADapiproc) glad_glNormal3b, 3, nx, ny, nz);\n    glad_glNormal3b(nx, ny, nz);\n    _post_call_gl_callback(NULL, \"glNormal3b\", (GLADapiproc) glad_glNormal3b, 3, nx, ny, nz);\n}\nPFNGLNORMAL3BPROC glad_debug_glNormal3b = glad_debug_impl_glNormal3b;\nPFNGLNORMAL3BVPROC glad_glNormal3bv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3bv(const GLbyte * v) {\n    _pre_call_gl_callback(\"glNormal3bv\", (GLADapiproc) glad_glNormal3bv, 1, v);\n    glad_glNormal3bv(v);\n    _post_call_gl_callback(NULL, \"glNormal3bv\", (GLADapiproc) glad_glNormal3bv, 1, v);\n}\nPFNGLNORMAL3BVPROC glad_debug_glNormal3bv = glad_debug_impl_glNormal3bv;\nPFNGLNORMAL3DPROC glad_glNormal3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3d(GLdouble nx, GLdouble ny, GLdouble nz) {\n    _pre_call_gl_callback(\"glNormal3d\", (GLADapiproc) glad_glNormal3d, 3, nx, ny, nz);\n    glad_glNormal3d(nx, ny, nz);\n    _post_call_gl_callback(NULL, \"glNormal3d\", (GLADapiproc) glad_glNormal3d, 3, nx, ny, nz);\n}\nPFNGLNORMAL3DPROC glad_debug_glNormal3d = glad_debug_impl_glNormal3d;\nPFNGLNORMAL3DVPROC glad_glNormal3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glNormal3dv\", (GLADapiproc) glad_glNormal3dv, 1, v);\n    glad_glNormal3dv(v);\n    _post_call_gl_callback(NULL, \"glNormal3dv\", (GLADapiproc) glad_glNormal3dv, 1, v);\n}\nPFNGLNORMAL3DVPROC glad_debug_glNormal3dv = glad_debug_impl_glNormal3dv;\nPFNGLNORMAL3FPROC glad_glNormal3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz) {\n    _pre_call_gl_callback(\"glNormal3f\", (GLADapiproc) glad_glNormal3f, 3, nx, ny, nz);\n    glad_glNormal3f(nx, ny, nz);\n    _post_call_gl_callback(NULL, \"glNormal3f\", (GLADapiproc) glad_glNormal3f, 3, nx, ny, nz);\n}\nPFNGLNORMAL3FPROC glad_debug_glNormal3f = glad_debug_impl_glNormal3f;\nPFNGLNORMAL3FVPROC glad_glNormal3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glNormal3fv\", (GLADapiproc) glad_glNormal3fv, 1, v);\n    glad_glNormal3fv(v);\n    _post_call_gl_callback(NULL, \"glNormal3fv\", (GLADapiproc) glad_glNormal3fv, 1, v);\n}\nPFNGLNORMAL3FVPROC glad_debug_glNormal3fv = glad_debug_impl_glNormal3fv;\nPFNGLNORMAL3IPROC glad_glNormal3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3i(GLint nx, GLint ny, GLint nz) {\n    _pre_call_gl_callback(\"glNormal3i\", (GLADapiproc) glad_glNormal3i, 3, nx, ny, nz);\n    glad_glNormal3i(nx, ny, nz);\n    _post_call_gl_callback(NULL, \"glNormal3i\", (GLADapiproc) glad_glNormal3i, 3, nx, ny, nz);\n}\nPFNGLNORMAL3IPROC glad_debug_glNormal3i = glad_debug_impl_glNormal3i;\nPFNGLNORMAL3IVPROC glad_glNormal3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3iv(const GLint * v) {\n    _pre_call_gl_callback(\"glNormal3iv\", (GLADapiproc) glad_glNormal3iv, 1, v);\n    glad_glNormal3iv(v);\n    _post_call_gl_callback(NULL, \"glNormal3iv\", (GLADapiproc) glad_glNormal3iv, 1, v);\n}\nPFNGLNORMAL3IVPROC glad_debug_glNormal3iv = glad_debug_impl_glNormal3iv;\nPFNGLNORMAL3SPROC glad_glNormal3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3s(GLshort nx, GLshort ny, GLshort nz) {\n    _pre_call_gl_callback(\"glNormal3s\", (GLADapiproc) glad_glNormal3s, 3, nx, ny, nz);\n    glad_glNormal3s(nx, ny, nz);\n    _post_call_gl_callback(NULL, \"glNormal3s\", (GLADapiproc) glad_glNormal3s, 3, nx, ny, nz);\n}\nPFNGLNORMAL3SPROC glad_debug_glNormal3s = glad_debug_impl_glNormal3s;\nPFNGLNORMAL3SVPROC glad_glNormal3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormal3sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glNormal3sv\", (GLADapiproc) glad_glNormal3sv, 1, v);\n    glad_glNormal3sv(v);\n    _post_call_gl_callback(NULL, \"glNormal3sv\", (GLADapiproc) glad_glNormal3sv, 1, v);\n}\nPFNGLNORMAL3SVPROC glad_debug_glNormal3sv = glad_debug_impl_glNormal3sv;\nPFNGLNORMALPOINTERPROC glad_glNormalPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glNormalPointer(GLenum type, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glNormalPointer\", (GLADapiproc) glad_glNormalPointer, 3, type, stride, pointer);\n    glad_glNormalPointer(type, stride, pointer);\n    _post_call_gl_callback(NULL, \"glNormalPointer\", (GLADapiproc) glad_glNormalPointer, 3, type, stride, pointer);\n}\nPFNGLNORMALPOINTERPROC glad_debug_glNormalPointer = glad_debug_impl_glNormalPointer;\nPFNGLOBJECTLABELPROC glad_glObjectLabel = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glObjectLabel(GLenum identifier, GLuint name, GLsizei length, const GLchar * label) {\n    _pre_call_gl_callback(\"glObjectLabel\", (GLADapiproc) glad_glObjectLabel, 4, identifier, name, length, label);\n    glad_glObjectLabel(identifier, name, length, label);\n    _post_call_gl_callback(NULL, \"glObjectLabel\", (GLADapiproc) glad_glObjectLabel, 4, identifier, name, length, label);\n}\nPFNGLOBJECTLABELPROC glad_debug_glObjectLabel = glad_debug_impl_glObjectLabel;\nPFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glObjectPtrLabel(const void * ptr, GLsizei length, const GLchar * label) {\n    _pre_call_gl_callback(\"glObjectPtrLabel\", (GLADapiproc) glad_glObjectPtrLabel, 3, ptr, length, label);\n    glad_glObjectPtrLabel(ptr, length, label);\n    _post_call_gl_callback(NULL, \"glObjectPtrLabel\", (GLADapiproc) glad_glObjectPtrLabel, 3, ptr, length, label);\n}\nPFNGLOBJECTPTRLABELPROC glad_debug_glObjectPtrLabel = glad_debug_impl_glObjectPtrLabel;\nPFNGLORTHOPROC glad_glOrtho = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) {\n    _pre_call_gl_callback(\"glOrtho\", (GLADapiproc) glad_glOrtho, 6, left, right, bottom, top, zNear, zFar);\n    glad_glOrtho(left, right, bottom, top, zNear, zFar);\n    _post_call_gl_callback(NULL, \"glOrtho\", (GLADapiproc) glad_glOrtho, 6, left, right, bottom, top, zNear, zFar);\n}\nPFNGLORTHOPROC glad_debug_glOrtho = glad_debug_impl_glOrtho;\nPFNGLPASSTHROUGHPROC glad_glPassThrough = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPassThrough(GLfloat token) {\n    _pre_call_gl_callback(\"glPassThrough\", (GLADapiproc) glad_glPassThrough, 1, token);\n    glad_glPassThrough(token);\n    _post_call_gl_callback(NULL, \"glPassThrough\", (GLADapiproc) glad_glPassThrough, 1, token);\n}\nPFNGLPASSTHROUGHPROC glad_debug_glPassThrough = glad_debug_impl_glPassThrough;\nPFNGLPIXELMAPFVPROC glad_glPixelMapfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPixelMapfv(GLenum map, GLsizei mapsize, const GLfloat * values) {\n    _pre_call_gl_callback(\"glPixelMapfv\", (GLADapiproc) glad_glPixelMapfv, 3, map, mapsize, values);\n    glad_glPixelMapfv(map, mapsize, values);\n    _post_call_gl_callback(NULL, \"glPixelMapfv\", (GLADapiproc) glad_glPixelMapfv, 3, map, mapsize, values);\n}\nPFNGLPIXELMAPFVPROC glad_debug_glPixelMapfv = glad_debug_impl_glPixelMapfv;\nPFNGLPIXELMAPUIVPROC glad_glPixelMapuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPixelMapuiv(GLenum map, GLsizei mapsize, const GLuint * values) {\n    _pre_call_gl_callback(\"glPixelMapuiv\", (GLADapiproc) glad_glPixelMapuiv, 3, map, mapsize, values);\n    glad_glPixelMapuiv(map, mapsize, values);\n    _post_call_gl_callback(NULL, \"glPixelMapuiv\", (GLADapiproc) glad_glPixelMapuiv, 3, map, mapsize, values);\n}\nPFNGLPIXELMAPUIVPROC glad_debug_glPixelMapuiv = glad_debug_impl_glPixelMapuiv;\nPFNGLPIXELMAPUSVPROC glad_glPixelMapusv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPixelMapusv(GLenum map, GLsizei mapsize, const GLushort * values) {\n    _pre_call_gl_callback(\"glPixelMapusv\", (GLADapiproc) glad_glPixelMapusv, 3, map, mapsize, values);\n    glad_glPixelMapusv(map, mapsize, values);\n    _post_call_gl_callback(NULL, \"glPixelMapusv\", (GLADapiproc) glad_glPixelMapusv, 3, map, mapsize, values);\n}\nPFNGLPIXELMAPUSVPROC glad_debug_glPixelMapusv = glad_debug_impl_glPixelMapusv;\nPFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPixelStoref(GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glPixelStoref\", (GLADapiproc) glad_glPixelStoref, 2, pname, param);\n    glad_glPixelStoref(pname, param);\n    _post_call_gl_callback(NULL, \"glPixelStoref\", (GLADapiproc) glad_glPixelStoref, 2, pname, param);\n}\nPFNGLPIXELSTOREFPROC glad_debug_glPixelStoref = glad_debug_impl_glPixelStoref;\nPFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPixelStorei(GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glPixelStorei\", (GLADapiproc) glad_glPixelStorei, 2, pname, param);\n    glad_glPixelStorei(pname, param);\n    _post_call_gl_callback(NULL, \"glPixelStorei\", (GLADapiproc) glad_glPixelStorei, 2, pname, param);\n}\nPFNGLPIXELSTOREIPROC glad_debug_glPixelStorei = glad_debug_impl_glPixelStorei;\nPFNGLPIXELTRANSFERFPROC glad_glPixelTransferf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPixelTransferf(GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glPixelTransferf\", (GLADapiproc) glad_glPixelTransferf, 2, pname, param);\n    glad_glPixelTransferf(pname, param);\n    _post_call_gl_callback(NULL, \"glPixelTransferf\", (GLADapiproc) glad_glPixelTransferf, 2, pname, param);\n}\nPFNGLPIXELTRANSFERFPROC glad_debug_glPixelTransferf = glad_debug_impl_glPixelTransferf;\nPFNGLPIXELTRANSFERIPROC glad_glPixelTransferi = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPixelTransferi(GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glPixelTransferi\", (GLADapiproc) glad_glPixelTransferi, 2, pname, param);\n    glad_glPixelTransferi(pname, param);\n    _post_call_gl_callback(NULL, \"glPixelTransferi\", (GLADapiproc) glad_glPixelTransferi, 2, pname, param);\n}\nPFNGLPIXELTRANSFERIPROC glad_debug_glPixelTransferi = glad_debug_impl_glPixelTransferi;\nPFNGLPIXELZOOMPROC glad_glPixelZoom = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPixelZoom(GLfloat xfactor, GLfloat yfactor) {\n    _pre_call_gl_callback(\"glPixelZoom\", (GLADapiproc) glad_glPixelZoom, 2, xfactor, yfactor);\n    glad_glPixelZoom(xfactor, yfactor);\n    _post_call_gl_callback(NULL, \"glPixelZoom\", (GLADapiproc) glad_glPixelZoom, 2, xfactor, yfactor);\n}\nPFNGLPIXELZOOMPROC glad_debug_glPixelZoom = glad_debug_impl_glPixelZoom;\nPFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPointParameterf(GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glPointParameterf\", (GLADapiproc) glad_glPointParameterf, 2, pname, param);\n    glad_glPointParameterf(pname, param);\n    _post_call_gl_callback(NULL, \"glPointParameterf\", (GLADapiproc) glad_glPointParameterf, 2, pname, param);\n}\nPFNGLPOINTPARAMETERFPROC glad_debug_glPointParameterf = glad_debug_impl_glPointParameterf;\nPFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPointParameterfv(GLenum pname, const GLfloat * params) {\n    _pre_call_gl_callback(\"glPointParameterfv\", (GLADapiproc) glad_glPointParameterfv, 2, pname, params);\n    glad_glPointParameterfv(pname, params);\n    _post_call_gl_callback(NULL, \"glPointParameterfv\", (GLADapiproc) glad_glPointParameterfv, 2, pname, params);\n}\nPFNGLPOINTPARAMETERFVPROC glad_debug_glPointParameterfv = glad_debug_impl_glPointParameterfv;\nPFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPointParameteri(GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glPointParameteri\", (GLADapiproc) glad_glPointParameteri, 2, pname, param);\n    glad_glPointParameteri(pname, param);\n    _post_call_gl_callback(NULL, \"glPointParameteri\", (GLADapiproc) glad_glPointParameteri, 2, pname, param);\n}\nPFNGLPOINTPARAMETERIPROC glad_debug_glPointParameteri = glad_debug_impl_glPointParameteri;\nPFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPointParameteriv(GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glPointParameteriv\", (GLADapiproc) glad_glPointParameteriv, 2, pname, params);\n    glad_glPointParameteriv(pname, params);\n    _post_call_gl_callback(NULL, \"glPointParameteriv\", (GLADapiproc) glad_glPointParameteriv, 2, pname, params);\n}\nPFNGLPOINTPARAMETERIVPROC glad_debug_glPointParameteriv = glad_debug_impl_glPointParameteriv;\nPFNGLPOINTSIZEPROC glad_glPointSize = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPointSize(GLfloat size) {\n    _pre_call_gl_callback(\"glPointSize\", (GLADapiproc) glad_glPointSize, 1, size);\n    glad_glPointSize(size);\n    _post_call_gl_callback(NULL, \"glPointSize\", (GLADapiproc) glad_glPointSize, 1, size);\n}\nPFNGLPOINTSIZEPROC glad_debug_glPointSize = glad_debug_impl_glPointSize;\nPFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPolygonMode(GLenum face, GLenum mode) {\n    _pre_call_gl_callback(\"glPolygonMode\", (GLADapiproc) glad_glPolygonMode, 2, face, mode);\n    glad_glPolygonMode(face, mode);\n    _post_call_gl_callback(NULL, \"glPolygonMode\", (GLADapiproc) glad_glPolygonMode, 2, face, mode);\n}\nPFNGLPOLYGONMODEPROC glad_debug_glPolygonMode = glad_debug_impl_glPolygonMode;\nPFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPolygonOffset(GLfloat factor, GLfloat units) {\n    _pre_call_gl_callback(\"glPolygonOffset\", (GLADapiproc) glad_glPolygonOffset, 2, factor, units);\n    glad_glPolygonOffset(factor, units);\n    _post_call_gl_callback(NULL, \"glPolygonOffset\", (GLADapiproc) glad_glPolygonOffset, 2, factor, units);\n}\nPFNGLPOLYGONOFFSETPROC glad_debug_glPolygonOffset = glad_debug_impl_glPolygonOffset;\nPFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPolygonStipple(const GLubyte * mask) {\n    _pre_call_gl_callback(\"glPolygonStipple\", (GLADapiproc) glad_glPolygonStipple, 1, mask);\n    glad_glPolygonStipple(mask);\n    _post_call_gl_callback(NULL, \"glPolygonStipple\", (GLADapiproc) glad_glPolygonStipple, 1, mask);\n}\nPFNGLPOLYGONSTIPPLEPROC glad_debug_glPolygonStipple = glad_debug_impl_glPolygonStipple;\nPFNGLPOPATTRIBPROC glad_glPopAttrib = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPopAttrib(void) {\n    _pre_call_gl_callback(\"glPopAttrib\", (GLADapiproc) glad_glPopAttrib, 0);\n    glad_glPopAttrib();\n    _post_call_gl_callback(NULL, \"glPopAttrib\", (GLADapiproc) glad_glPopAttrib, 0);\n}\nPFNGLPOPATTRIBPROC glad_debug_glPopAttrib = glad_debug_impl_glPopAttrib;\nPFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPopClientAttrib(void) {\n    _pre_call_gl_callback(\"glPopClientAttrib\", (GLADapiproc) glad_glPopClientAttrib, 0);\n    glad_glPopClientAttrib();\n    _post_call_gl_callback(NULL, \"glPopClientAttrib\", (GLADapiproc) glad_glPopClientAttrib, 0);\n}\nPFNGLPOPCLIENTATTRIBPROC glad_debug_glPopClientAttrib = glad_debug_impl_glPopClientAttrib;\nPFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPopDebugGroup(void) {\n    _pre_call_gl_callback(\"glPopDebugGroup\", (GLADapiproc) glad_glPopDebugGroup, 0);\n    glad_glPopDebugGroup();\n    _post_call_gl_callback(NULL, \"glPopDebugGroup\", (GLADapiproc) glad_glPopDebugGroup, 0);\n}\nPFNGLPOPDEBUGGROUPPROC glad_debug_glPopDebugGroup = glad_debug_impl_glPopDebugGroup;\nPFNGLPOPMATRIXPROC glad_glPopMatrix = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPopMatrix(void) {\n    _pre_call_gl_callback(\"glPopMatrix\", (GLADapiproc) glad_glPopMatrix, 0);\n    glad_glPopMatrix();\n    _post_call_gl_callback(NULL, \"glPopMatrix\", (GLADapiproc) glad_glPopMatrix, 0);\n}\nPFNGLPOPMATRIXPROC glad_debug_glPopMatrix = glad_debug_impl_glPopMatrix;\nPFNGLPOPNAMEPROC glad_glPopName = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPopName(void) {\n    _pre_call_gl_callback(\"glPopName\", (GLADapiproc) glad_glPopName, 0);\n    glad_glPopName();\n    _post_call_gl_callback(NULL, \"glPopName\", (GLADapiproc) glad_glPopName, 0);\n}\nPFNGLPOPNAMEPROC glad_debug_glPopName = glad_debug_impl_glPopName;\nPFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPrimitiveRestartIndex(GLuint index) {\n    _pre_call_gl_callback(\"glPrimitiveRestartIndex\", (GLADapiproc) glad_glPrimitiveRestartIndex, 1, index);\n    glad_glPrimitiveRestartIndex(index);\n    _post_call_gl_callback(NULL, \"glPrimitiveRestartIndex\", (GLADapiproc) glad_glPrimitiveRestartIndex, 1, index);\n}\nPFNGLPRIMITIVERESTARTINDEXPROC glad_debug_glPrimitiveRestartIndex = glad_debug_impl_glPrimitiveRestartIndex;\nPFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPrioritizeTextures(GLsizei n, const GLuint * textures, const GLfloat * priorities) {\n    _pre_call_gl_callback(\"glPrioritizeTextures\", (GLADapiproc) glad_glPrioritizeTextures, 3, n, textures, priorities);\n    glad_glPrioritizeTextures(n, textures, priorities);\n    _post_call_gl_callback(NULL, \"glPrioritizeTextures\", (GLADapiproc) glad_glPrioritizeTextures, 3, n, textures, priorities);\n}\nPFNGLPRIORITIZETEXTURESPROC glad_debug_glPrioritizeTextures = glad_debug_impl_glPrioritizeTextures;\nPFNGLPUSHATTRIBPROC glad_glPushAttrib = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPushAttrib(GLbitfield mask) {\n    _pre_call_gl_callback(\"glPushAttrib\", (GLADapiproc) glad_glPushAttrib, 1, mask);\n    glad_glPushAttrib(mask);\n    _post_call_gl_callback(NULL, \"glPushAttrib\", (GLADapiproc) glad_glPushAttrib, 1, mask);\n}\nPFNGLPUSHATTRIBPROC glad_debug_glPushAttrib = glad_debug_impl_glPushAttrib;\nPFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPushClientAttrib(GLbitfield mask) {\n    _pre_call_gl_callback(\"glPushClientAttrib\", (GLADapiproc) glad_glPushClientAttrib, 1, mask);\n    glad_glPushClientAttrib(mask);\n    _post_call_gl_callback(NULL, \"glPushClientAttrib\", (GLADapiproc) glad_glPushClientAttrib, 1, mask);\n}\nPFNGLPUSHCLIENTATTRIBPROC glad_debug_glPushClientAttrib = glad_debug_impl_glPushClientAttrib;\nPFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPushDebugGroup(GLenum source, GLuint id, GLsizei length, const GLchar * message) {\n    _pre_call_gl_callback(\"glPushDebugGroup\", (GLADapiproc) glad_glPushDebugGroup, 4, source, id, length, message);\n    glad_glPushDebugGroup(source, id, length, message);\n    _post_call_gl_callback(NULL, \"glPushDebugGroup\", (GLADapiproc) glad_glPushDebugGroup, 4, source, id, length, message);\n}\nPFNGLPUSHDEBUGGROUPPROC glad_debug_glPushDebugGroup = glad_debug_impl_glPushDebugGroup;\nPFNGLPUSHMATRIXPROC glad_glPushMatrix = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPushMatrix(void) {\n    _pre_call_gl_callback(\"glPushMatrix\", (GLADapiproc) glad_glPushMatrix, 0);\n    glad_glPushMatrix();\n    _post_call_gl_callback(NULL, \"glPushMatrix\", (GLADapiproc) glad_glPushMatrix, 0);\n}\nPFNGLPUSHMATRIXPROC glad_debug_glPushMatrix = glad_debug_impl_glPushMatrix;\nPFNGLPUSHNAMEPROC glad_glPushName = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glPushName(GLuint name) {\n    _pre_call_gl_callback(\"glPushName\", (GLADapiproc) glad_glPushName, 1, name);\n    glad_glPushName(name);\n    _post_call_gl_callback(NULL, \"glPushName\", (GLADapiproc) glad_glPushName, 1, name);\n}\nPFNGLPUSHNAMEPROC glad_debug_glPushName = glad_debug_impl_glPushName;\nPFNGLRASTERPOS2DPROC glad_glRasterPos2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos2d(GLdouble x, GLdouble y) {\n    _pre_call_gl_callback(\"glRasterPos2d\", (GLADapiproc) glad_glRasterPos2d, 2, x, y);\n    glad_glRasterPos2d(x, y);\n    _post_call_gl_callback(NULL, \"glRasterPos2d\", (GLADapiproc) glad_glRasterPos2d, 2, x, y);\n}\nPFNGLRASTERPOS2DPROC glad_debug_glRasterPos2d = glad_debug_impl_glRasterPos2d;\nPFNGLRASTERPOS2DVPROC glad_glRasterPos2dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos2dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glRasterPos2dv\", (GLADapiproc) glad_glRasterPos2dv, 1, v);\n    glad_glRasterPos2dv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos2dv\", (GLADapiproc) glad_glRasterPos2dv, 1, v);\n}\nPFNGLRASTERPOS2DVPROC glad_debug_glRasterPos2dv = glad_debug_impl_glRasterPos2dv;\nPFNGLRASTERPOS2FPROC glad_glRasterPos2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos2f(GLfloat x, GLfloat y) {\n    _pre_call_gl_callback(\"glRasterPos2f\", (GLADapiproc) glad_glRasterPos2f, 2, x, y);\n    glad_glRasterPos2f(x, y);\n    _post_call_gl_callback(NULL, \"glRasterPos2f\", (GLADapiproc) glad_glRasterPos2f, 2, x, y);\n}\nPFNGLRASTERPOS2FPROC glad_debug_glRasterPos2f = glad_debug_impl_glRasterPos2f;\nPFNGLRASTERPOS2FVPROC glad_glRasterPos2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos2fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glRasterPos2fv\", (GLADapiproc) glad_glRasterPos2fv, 1, v);\n    glad_glRasterPos2fv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos2fv\", (GLADapiproc) glad_glRasterPos2fv, 1, v);\n}\nPFNGLRASTERPOS2FVPROC glad_debug_glRasterPos2fv = glad_debug_impl_glRasterPos2fv;\nPFNGLRASTERPOS2IPROC glad_glRasterPos2i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos2i(GLint x, GLint y) {\n    _pre_call_gl_callback(\"glRasterPos2i\", (GLADapiproc) glad_glRasterPos2i, 2, x, y);\n    glad_glRasterPos2i(x, y);\n    _post_call_gl_callback(NULL, \"glRasterPos2i\", (GLADapiproc) glad_glRasterPos2i, 2, x, y);\n}\nPFNGLRASTERPOS2IPROC glad_debug_glRasterPos2i = glad_debug_impl_glRasterPos2i;\nPFNGLRASTERPOS2IVPROC glad_glRasterPos2iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos2iv(const GLint * v) {\n    _pre_call_gl_callback(\"glRasterPos2iv\", (GLADapiproc) glad_glRasterPos2iv, 1, v);\n    glad_glRasterPos2iv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos2iv\", (GLADapiproc) glad_glRasterPos2iv, 1, v);\n}\nPFNGLRASTERPOS2IVPROC glad_debug_glRasterPos2iv = glad_debug_impl_glRasterPos2iv;\nPFNGLRASTERPOS2SPROC glad_glRasterPos2s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos2s(GLshort x, GLshort y) {\n    _pre_call_gl_callback(\"glRasterPos2s\", (GLADapiproc) glad_glRasterPos2s, 2, x, y);\n    glad_glRasterPos2s(x, y);\n    _post_call_gl_callback(NULL, \"glRasterPos2s\", (GLADapiproc) glad_glRasterPos2s, 2, x, y);\n}\nPFNGLRASTERPOS2SPROC glad_debug_glRasterPos2s = glad_debug_impl_glRasterPos2s;\nPFNGLRASTERPOS2SVPROC glad_glRasterPos2sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos2sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glRasterPos2sv\", (GLADapiproc) glad_glRasterPos2sv, 1, v);\n    glad_glRasterPos2sv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos2sv\", (GLADapiproc) glad_glRasterPos2sv, 1, v);\n}\nPFNGLRASTERPOS2SVPROC glad_debug_glRasterPos2sv = glad_debug_impl_glRasterPos2sv;\nPFNGLRASTERPOS3DPROC glad_glRasterPos3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos3d(GLdouble x, GLdouble y, GLdouble z) {\n    _pre_call_gl_callback(\"glRasterPos3d\", (GLADapiproc) glad_glRasterPos3d, 3, x, y, z);\n    glad_glRasterPos3d(x, y, z);\n    _post_call_gl_callback(NULL, \"glRasterPos3d\", (GLADapiproc) glad_glRasterPos3d, 3, x, y, z);\n}\nPFNGLRASTERPOS3DPROC glad_debug_glRasterPos3d = glad_debug_impl_glRasterPos3d;\nPFNGLRASTERPOS3DVPROC glad_glRasterPos3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos3dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glRasterPos3dv\", (GLADapiproc) glad_glRasterPos3dv, 1, v);\n    glad_glRasterPos3dv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos3dv\", (GLADapiproc) glad_glRasterPos3dv, 1, v);\n}\nPFNGLRASTERPOS3DVPROC glad_debug_glRasterPos3dv = glad_debug_impl_glRasterPos3dv;\nPFNGLRASTERPOS3FPROC glad_glRasterPos3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos3f(GLfloat x, GLfloat y, GLfloat z) {\n    _pre_call_gl_callback(\"glRasterPos3f\", (GLADapiproc) glad_glRasterPos3f, 3, x, y, z);\n    glad_glRasterPos3f(x, y, z);\n    _post_call_gl_callback(NULL, \"glRasterPos3f\", (GLADapiproc) glad_glRasterPos3f, 3, x, y, z);\n}\nPFNGLRASTERPOS3FPROC glad_debug_glRasterPos3f = glad_debug_impl_glRasterPos3f;\nPFNGLRASTERPOS3FVPROC glad_glRasterPos3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos3fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glRasterPos3fv\", (GLADapiproc) glad_glRasterPos3fv, 1, v);\n    glad_glRasterPos3fv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos3fv\", (GLADapiproc) glad_glRasterPos3fv, 1, v);\n}\nPFNGLRASTERPOS3FVPROC glad_debug_glRasterPos3fv = glad_debug_impl_glRasterPos3fv;\nPFNGLRASTERPOS3IPROC glad_glRasterPos3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos3i(GLint x, GLint y, GLint z) {\n    _pre_call_gl_callback(\"glRasterPos3i\", (GLADapiproc) glad_glRasterPos3i, 3, x, y, z);\n    glad_glRasterPos3i(x, y, z);\n    _post_call_gl_callback(NULL, \"glRasterPos3i\", (GLADapiproc) glad_glRasterPos3i, 3, x, y, z);\n}\nPFNGLRASTERPOS3IPROC glad_debug_glRasterPos3i = glad_debug_impl_glRasterPos3i;\nPFNGLRASTERPOS3IVPROC glad_glRasterPos3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos3iv(const GLint * v) {\n    _pre_call_gl_callback(\"glRasterPos3iv\", (GLADapiproc) glad_glRasterPos3iv, 1, v);\n    glad_glRasterPos3iv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos3iv\", (GLADapiproc) glad_glRasterPos3iv, 1, v);\n}\nPFNGLRASTERPOS3IVPROC glad_debug_glRasterPos3iv = glad_debug_impl_glRasterPos3iv;\nPFNGLRASTERPOS3SPROC glad_glRasterPos3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos3s(GLshort x, GLshort y, GLshort z) {\n    _pre_call_gl_callback(\"glRasterPos3s\", (GLADapiproc) glad_glRasterPos3s, 3, x, y, z);\n    glad_glRasterPos3s(x, y, z);\n    _post_call_gl_callback(NULL, \"glRasterPos3s\", (GLADapiproc) glad_glRasterPos3s, 3, x, y, z);\n}\nPFNGLRASTERPOS3SPROC glad_debug_glRasterPos3s = glad_debug_impl_glRasterPos3s;\nPFNGLRASTERPOS3SVPROC glad_glRasterPos3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos3sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glRasterPos3sv\", (GLADapiproc) glad_glRasterPos3sv, 1, v);\n    glad_glRasterPos3sv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos3sv\", (GLADapiproc) glad_glRasterPos3sv, 1, v);\n}\nPFNGLRASTERPOS3SVPROC glad_debug_glRasterPos3sv = glad_debug_impl_glRasterPos3sv;\nPFNGLRASTERPOS4DPROC glad_glRasterPos4d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos4d(GLdouble x, GLdouble y, GLdouble z, GLdouble w) {\n    _pre_call_gl_callback(\"glRasterPos4d\", (GLADapiproc) glad_glRasterPos4d, 4, x, y, z, w);\n    glad_glRasterPos4d(x, y, z, w);\n    _post_call_gl_callback(NULL, \"glRasterPos4d\", (GLADapiproc) glad_glRasterPos4d, 4, x, y, z, w);\n}\nPFNGLRASTERPOS4DPROC glad_debug_glRasterPos4d = glad_debug_impl_glRasterPos4d;\nPFNGLRASTERPOS4DVPROC glad_glRasterPos4dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos4dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glRasterPos4dv\", (GLADapiproc) glad_glRasterPos4dv, 1, v);\n    glad_glRasterPos4dv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos4dv\", (GLADapiproc) glad_glRasterPos4dv, 1, v);\n}\nPFNGLRASTERPOS4DVPROC glad_debug_glRasterPos4dv = glad_debug_impl_glRasterPos4dv;\nPFNGLRASTERPOS4FPROC glad_glRasterPos4f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w) {\n    _pre_call_gl_callback(\"glRasterPos4f\", (GLADapiproc) glad_glRasterPos4f, 4, x, y, z, w);\n    glad_glRasterPos4f(x, y, z, w);\n    _post_call_gl_callback(NULL, \"glRasterPos4f\", (GLADapiproc) glad_glRasterPos4f, 4, x, y, z, w);\n}\nPFNGLRASTERPOS4FPROC glad_debug_glRasterPos4f = glad_debug_impl_glRasterPos4f;\nPFNGLRASTERPOS4FVPROC glad_glRasterPos4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos4fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glRasterPos4fv\", (GLADapiproc) glad_glRasterPos4fv, 1, v);\n    glad_glRasterPos4fv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos4fv\", (GLADapiproc) glad_glRasterPos4fv, 1, v);\n}\nPFNGLRASTERPOS4FVPROC glad_debug_glRasterPos4fv = glad_debug_impl_glRasterPos4fv;\nPFNGLRASTERPOS4IPROC glad_glRasterPos4i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos4i(GLint x, GLint y, GLint z, GLint w) {\n    _pre_call_gl_callback(\"glRasterPos4i\", (GLADapiproc) glad_glRasterPos4i, 4, x, y, z, w);\n    glad_glRasterPos4i(x, y, z, w);\n    _post_call_gl_callback(NULL, \"glRasterPos4i\", (GLADapiproc) glad_glRasterPos4i, 4, x, y, z, w);\n}\nPFNGLRASTERPOS4IPROC glad_debug_glRasterPos4i = glad_debug_impl_glRasterPos4i;\nPFNGLRASTERPOS4IVPROC glad_glRasterPos4iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos4iv(const GLint * v) {\n    _pre_call_gl_callback(\"glRasterPos4iv\", (GLADapiproc) glad_glRasterPos4iv, 1, v);\n    glad_glRasterPos4iv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos4iv\", (GLADapiproc) glad_glRasterPos4iv, 1, v);\n}\nPFNGLRASTERPOS4IVPROC glad_debug_glRasterPos4iv = glad_debug_impl_glRasterPos4iv;\nPFNGLRASTERPOS4SPROC glad_glRasterPos4s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos4s(GLshort x, GLshort y, GLshort z, GLshort w) {\n    _pre_call_gl_callback(\"glRasterPos4s\", (GLADapiproc) glad_glRasterPos4s, 4, x, y, z, w);\n    glad_glRasterPos4s(x, y, z, w);\n    _post_call_gl_callback(NULL, \"glRasterPos4s\", (GLADapiproc) glad_glRasterPos4s, 4, x, y, z, w);\n}\nPFNGLRASTERPOS4SPROC glad_debug_glRasterPos4s = glad_debug_impl_glRasterPos4s;\nPFNGLRASTERPOS4SVPROC glad_glRasterPos4sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRasterPos4sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glRasterPos4sv\", (GLADapiproc) glad_glRasterPos4sv, 1, v);\n    glad_glRasterPos4sv(v);\n    _post_call_gl_callback(NULL, \"glRasterPos4sv\", (GLADapiproc) glad_glRasterPos4sv, 1, v);\n}\nPFNGLRASTERPOS4SVPROC glad_debug_glRasterPos4sv = glad_debug_impl_glRasterPos4sv;\nPFNGLREADBUFFERPROC glad_glReadBuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glReadBuffer(GLenum src) {\n    _pre_call_gl_callback(\"glReadBuffer\", (GLADapiproc) glad_glReadBuffer, 1, src);\n    glad_glReadBuffer(src);\n    _post_call_gl_callback(NULL, \"glReadBuffer\", (GLADapiproc) glad_glReadBuffer, 1, src);\n}\nPFNGLREADBUFFERPROC glad_debug_glReadBuffer = glad_debug_impl_glReadBuffer;\nPFNGLREADPIXELSPROC glad_glReadPixels = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels) {\n    _pre_call_gl_callback(\"glReadPixels\", (GLADapiproc) glad_glReadPixels, 7, x, y, width, height, format, type, pixels);\n    glad_glReadPixels(x, y, width, height, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glReadPixels\", (GLADapiproc) glad_glReadPixels, 7, x, y, width, height, format, type, pixels);\n}\nPFNGLREADPIXELSPROC glad_debug_glReadPixels = glad_debug_impl_glReadPixels;\nPFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glReadnPixelsARB(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data) {\n    _pre_call_gl_callback(\"glReadnPixelsARB\", (GLADapiproc) glad_glReadnPixelsARB, 8, x, y, width, height, format, type, bufSize, data);\n    glad_glReadnPixelsARB(x, y, width, height, format, type, bufSize, data);\n    _post_call_gl_callback(NULL, \"glReadnPixelsARB\", (GLADapiproc) glad_glReadnPixelsARB, 8, x, y, width, height, format, type, bufSize, data);\n}\nPFNGLREADNPIXELSARBPROC glad_debug_glReadnPixelsARB = glad_debug_impl_glReadnPixelsARB;\nPFNGLRECTDPROC glad_glRectd = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRectd(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2) {\n    _pre_call_gl_callback(\"glRectd\", (GLADapiproc) glad_glRectd, 4, x1, y1, x2, y2);\n    glad_glRectd(x1, y1, x2, y2);\n    _post_call_gl_callback(NULL, \"glRectd\", (GLADapiproc) glad_glRectd, 4, x1, y1, x2, y2);\n}\nPFNGLRECTDPROC glad_debug_glRectd = glad_debug_impl_glRectd;\nPFNGLRECTDVPROC glad_glRectdv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRectdv(const GLdouble * v1, const GLdouble * v2) {\n    _pre_call_gl_callback(\"glRectdv\", (GLADapiproc) glad_glRectdv, 2, v1, v2);\n    glad_glRectdv(v1, v2);\n    _post_call_gl_callback(NULL, \"glRectdv\", (GLADapiproc) glad_glRectdv, 2, v1, v2);\n}\nPFNGLRECTDVPROC glad_debug_glRectdv = glad_debug_impl_glRectdv;\nPFNGLRECTFPROC glad_glRectf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) {\n    _pre_call_gl_callback(\"glRectf\", (GLADapiproc) glad_glRectf, 4, x1, y1, x2, y2);\n    glad_glRectf(x1, y1, x2, y2);\n    _post_call_gl_callback(NULL, \"glRectf\", (GLADapiproc) glad_glRectf, 4, x1, y1, x2, y2);\n}\nPFNGLRECTFPROC glad_debug_glRectf = glad_debug_impl_glRectf;\nPFNGLRECTFVPROC glad_glRectfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRectfv(const GLfloat * v1, const GLfloat * v2) {\n    _pre_call_gl_callback(\"glRectfv\", (GLADapiproc) glad_glRectfv, 2, v1, v2);\n    glad_glRectfv(v1, v2);\n    _post_call_gl_callback(NULL, \"glRectfv\", (GLADapiproc) glad_glRectfv, 2, v1, v2);\n}\nPFNGLRECTFVPROC glad_debug_glRectfv = glad_debug_impl_glRectfv;\nPFNGLRECTIPROC glad_glRecti = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRecti(GLint x1, GLint y1, GLint x2, GLint y2) {\n    _pre_call_gl_callback(\"glRecti\", (GLADapiproc) glad_glRecti, 4, x1, y1, x2, y2);\n    glad_glRecti(x1, y1, x2, y2);\n    _post_call_gl_callback(NULL, \"glRecti\", (GLADapiproc) glad_glRecti, 4, x1, y1, x2, y2);\n}\nPFNGLRECTIPROC glad_debug_glRecti = glad_debug_impl_glRecti;\nPFNGLRECTIVPROC glad_glRectiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRectiv(const GLint * v1, const GLint * v2) {\n    _pre_call_gl_callback(\"glRectiv\", (GLADapiproc) glad_glRectiv, 2, v1, v2);\n    glad_glRectiv(v1, v2);\n    _post_call_gl_callback(NULL, \"glRectiv\", (GLADapiproc) glad_glRectiv, 2, v1, v2);\n}\nPFNGLRECTIVPROC glad_debug_glRectiv = glad_debug_impl_glRectiv;\nPFNGLRECTSPROC glad_glRects = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRects(GLshort x1, GLshort y1, GLshort x2, GLshort y2) {\n    _pre_call_gl_callback(\"glRects\", (GLADapiproc) glad_glRects, 4, x1, y1, x2, y2);\n    glad_glRects(x1, y1, x2, y2);\n    _post_call_gl_callback(NULL, \"glRects\", (GLADapiproc) glad_glRects, 4, x1, y1, x2, y2);\n}\nPFNGLRECTSPROC glad_debug_glRects = glad_debug_impl_glRects;\nPFNGLRECTSVPROC glad_glRectsv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRectsv(const GLshort * v1, const GLshort * v2) {\n    _pre_call_gl_callback(\"glRectsv\", (GLADapiproc) glad_glRectsv, 2, v1, v2);\n    glad_glRectsv(v1, v2);\n    _post_call_gl_callback(NULL, \"glRectsv\", (GLADapiproc) glad_glRectsv, 2, v1, v2);\n}\nPFNGLRECTSVPROC glad_debug_glRectsv = glad_debug_impl_glRectsv;\nPFNGLRENDERMODEPROC glad_glRenderMode = NULL;\nstatic GLint GLAD_API_PTR glad_debug_impl_glRenderMode(GLenum mode) {\n    GLint ret;\n    _pre_call_gl_callback(\"glRenderMode\", (GLADapiproc) glad_glRenderMode, 1, mode);\n    ret = glad_glRenderMode(mode);\n    _post_call_gl_callback((void*) &ret, \"glRenderMode\", (GLADapiproc) glad_glRenderMode, 1, mode);\n    return ret;\n}\nPFNGLRENDERMODEPROC glad_debug_glRenderMode = glad_debug_impl_glRenderMode;\nPFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {\n    _pre_call_gl_callback(\"glRenderbufferStorage\", (GLADapiproc) glad_glRenderbufferStorage, 4, target, internalformat, width, height);\n    glad_glRenderbufferStorage(target, internalformat, width, height);\n    _post_call_gl_callback(NULL, \"glRenderbufferStorage\", (GLADapiproc) glad_glRenderbufferStorage, 4, target, internalformat, width, height);\n}\nPFNGLRENDERBUFFERSTORAGEPROC glad_debug_glRenderbufferStorage = glad_debug_impl_glRenderbufferStorage;\nPFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {\n    _pre_call_gl_callback(\"glRenderbufferStorageMultisample\", (GLADapiproc) glad_glRenderbufferStorageMultisample, 5, target, samples, internalformat, width, height);\n    glad_glRenderbufferStorageMultisample(target, samples, internalformat, width, height);\n    _post_call_gl_callback(NULL, \"glRenderbufferStorageMultisample\", (GLADapiproc) glad_glRenderbufferStorageMultisample, 5, target, samples, internalformat, width, height);\n}\nPFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_debug_glRenderbufferStorageMultisample = glad_debug_impl_glRenderbufferStorageMultisample;\nPFNGLROTATEDPROC glad_glRotated = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z) {\n    _pre_call_gl_callback(\"glRotated\", (GLADapiproc) glad_glRotated, 4, angle, x, y, z);\n    glad_glRotated(angle, x, y, z);\n    _post_call_gl_callback(NULL, \"glRotated\", (GLADapiproc) glad_glRotated, 4, angle, x, y, z);\n}\nPFNGLROTATEDPROC glad_debug_glRotated = glad_debug_impl_glRotated;\nPFNGLROTATEFPROC glad_glRotatef = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {\n    _pre_call_gl_callback(\"glRotatef\", (GLADapiproc) glad_glRotatef, 4, angle, x, y, z);\n    glad_glRotatef(angle, x, y, z);\n    _post_call_gl_callback(NULL, \"glRotatef\", (GLADapiproc) glad_glRotatef, 4, angle, x, y, z);\n}\nPFNGLROTATEFPROC glad_debug_glRotatef = glad_debug_impl_glRotatef;\nPFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSampleCoverage(GLfloat value, GLboolean invert) {\n    _pre_call_gl_callback(\"glSampleCoverage\", (GLADapiproc) glad_glSampleCoverage, 2, value, invert);\n    glad_glSampleCoverage(value, invert);\n    _post_call_gl_callback(NULL, \"glSampleCoverage\", (GLADapiproc) glad_glSampleCoverage, 2, value, invert);\n}\nPFNGLSAMPLECOVERAGEPROC glad_debug_glSampleCoverage = glad_debug_impl_glSampleCoverage;\nPFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSampleCoverageARB(GLfloat value, GLboolean invert) {\n    _pre_call_gl_callback(\"glSampleCoverageARB\", (GLADapiproc) glad_glSampleCoverageARB, 2, value, invert);\n    glad_glSampleCoverageARB(value, invert);\n    _post_call_gl_callback(NULL, \"glSampleCoverageARB\", (GLADapiproc) glad_glSampleCoverageARB, 2, value, invert);\n}\nPFNGLSAMPLECOVERAGEARBPROC glad_debug_glSampleCoverageARB = glad_debug_impl_glSampleCoverageARB;\nPFNGLSCALEDPROC glad_glScaled = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glScaled(GLdouble x, GLdouble y, GLdouble z) {\n    _pre_call_gl_callback(\"glScaled\", (GLADapiproc) glad_glScaled, 3, x, y, z);\n    glad_glScaled(x, y, z);\n    _post_call_gl_callback(NULL, \"glScaled\", (GLADapiproc) glad_glScaled, 3, x, y, z);\n}\nPFNGLSCALEDPROC glad_debug_glScaled = glad_debug_impl_glScaled;\nPFNGLSCALEFPROC glad_glScalef = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glScalef(GLfloat x, GLfloat y, GLfloat z) {\n    _pre_call_gl_callback(\"glScalef\", (GLADapiproc) glad_glScalef, 3, x, y, z);\n    glad_glScalef(x, y, z);\n    _post_call_gl_callback(NULL, \"glScalef\", (GLADapiproc) glad_glScalef, 3, x, y, z);\n}\nPFNGLSCALEFPROC glad_debug_glScalef = glad_debug_impl_glScalef;\nPFNGLSCISSORPROC glad_glScissor = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {\n    _pre_call_gl_callback(\"glScissor\", (GLADapiproc) glad_glScissor, 4, x, y, width, height);\n    glad_glScissor(x, y, width, height);\n    _post_call_gl_callback(NULL, \"glScissor\", (GLADapiproc) glad_glScissor, 4, x, y, width, height);\n}\nPFNGLSCISSORPROC glad_debug_glScissor = glad_debug_impl_glScissor;\nPFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3b(GLbyte red, GLbyte green, GLbyte blue) {\n    _pre_call_gl_callback(\"glSecondaryColor3b\", (GLADapiproc) glad_glSecondaryColor3b, 3, red, green, blue);\n    glad_glSecondaryColor3b(red, green, blue);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3b\", (GLADapiproc) glad_glSecondaryColor3b, 3, red, green, blue);\n}\nPFNGLSECONDARYCOLOR3BPROC glad_debug_glSecondaryColor3b = glad_debug_impl_glSecondaryColor3b;\nPFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3bv(const GLbyte * v) {\n    _pre_call_gl_callback(\"glSecondaryColor3bv\", (GLADapiproc) glad_glSecondaryColor3bv, 1, v);\n    glad_glSecondaryColor3bv(v);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3bv\", (GLADapiproc) glad_glSecondaryColor3bv, 1, v);\n}\nPFNGLSECONDARYCOLOR3BVPROC glad_debug_glSecondaryColor3bv = glad_debug_impl_glSecondaryColor3bv;\nPFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3d(GLdouble red, GLdouble green, GLdouble blue) {\n    _pre_call_gl_callback(\"glSecondaryColor3d\", (GLADapiproc) glad_glSecondaryColor3d, 3, red, green, blue);\n    glad_glSecondaryColor3d(red, green, blue);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3d\", (GLADapiproc) glad_glSecondaryColor3d, 3, red, green, blue);\n}\nPFNGLSECONDARYCOLOR3DPROC glad_debug_glSecondaryColor3d = glad_debug_impl_glSecondaryColor3d;\nPFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glSecondaryColor3dv\", (GLADapiproc) glad_glSecondaryColor3dv, 1, v);\n    glad_glSecondaryColor3dv(v);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3dv\", (GLADapiproc) glad_glSecondaryColor3dv, 1, v);\n}\nPFNGLSECONDARYCOLOR3DVPROC glad_debug_glSecondaryColor3dv = glad_debug_impl_glSecondaryColor3dv;\nPFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3f(GLfloat red, GLfloat green, GLfloat blue) {\n    _pre_call_gl_callback(\"glSecondaryColor3f\", (GLADapiproc) glad_glSecondaryColor3f, 3, red, green, blue);\n    glad_glSecondaryColor3f(red, green, blue);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3f\", (GLADapiproc) glad_glSecondaryColor3f, 3, red, green, blue);\n}\nPFNGLSECONDARYCOLOR3FPROC glad_debug_glSecondaryColor3f = glad_debug_impl_glSecondaryColor3f;\nPFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glSecondaryColor3fv\", (GLADapiproc) glad_glSecondaryColor3fv, 1, v);\n    glad_glSecondaryColor3fv(v);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3fv\", (GLADapiproc) glad_glSecondaryColor3fv, 1, v);\n}\nPFNGLSECONDARYCOLOR3FVPROC glad_debug_glSecondaryColor3fv = glad_debug_impl_glSecondaryColor3fv;\nPFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3i(GLint red, GLint green, GLint blue) {\n    _pre_call_gl_callback(\"glSecondaryColor3i\", (GLADapiproc) glad_glSecondaryColor3i, 3, red, green, blue);\n    glad_glSecondaryColor3i(red, green, blue);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3i\", (GLADapiproc) glad_glSecondaryColor3i, 3, red, green, blue);\n}\nPFNGLSECONDARYCOLOR3IPROC glad_debug_glSecondaryColor3i = glad_debug_impl_glSecondaryColor3i;\nPFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3iv(const GLint * v) {\n    _pre_call_gl_callback(\"glSecondaryColor3iv\", (GLADapiproc) glad_glSecondaryColor3iv, 1, v);\n    glad_glSecondaryColor3iv(v);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3iv\", (GLADapiproc) glad_glSecondaryColor3iv, 1, v);\n}\nPFNGLSECONDARYCOLOR3IVPROC glad_debug_glSecondaryColor3iv = glad_debug_impl_glSecondaryColor3iv;\nPFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3s(GLshort red, GLshort green, GLshort blue) {\n    _pre_call_gl_callback(\"glSecondaryColor3s\", (GLADapiproc) glad_glSecondaryColor3s, 3, red, green, blue);\n    glad_glSecondaryColor3s(red, green, blue);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3s\", (GLADapiproc) glad_glSecondaryColor3s, 3, red, green, blue);\n}\nPFNGLSECONDARYCOLOR3SPROC glad_debug_glSecondaryColor3s = glad_debug_impl_glSecondaryColor3s;\nPFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glSecondaryColor3sv\", (GLADapiproc) glad_glSecondaryColor3sv, 1, v);\n    glad_glSecondaryColor3sv(v);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3sv\", (GLADapiproc) glad_glSecondaryColor3sv, 1, v);\n}\nPFNGLSECONDARYCOLOR3SVPROC glad_debug_glSecondaryColor3sv = glad_debug_impl_glSecondaryColor3sv;\nPFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3ub(GLubyte red, GLubyte green, GLubyte blue) {\n    _pre_call_gl_callback(\"glSecondaryColor3ub\", (GLADapiproc) glad_glSecondaryColor3ub, 3, red, green, blue);\n    glad_glSecondaryColor3ub(red, green, blue);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3ub\", (GLADapiproc) glad_glSecondaryColor3ub, 3, red, green, blue);\n}\nPFNGLSECONDARYCOLOR3UBPROC glad_debug_glSecondaryColor3ub = glad_debug_impl_glSecondaryColor3ub;\nPFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3ubv(const GLubyte * v) {\n    _pre_call_gl_callback(\"glSecondaryColor3ubv\", (GLADapiproc) glad_glSecondaryColor3ubv, 1, v);\n    glad_glSecondaryColor3ubv(v);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3ubv\", (GLADapiproc) glad_glSecondaryColor3ubv, 1, v);\n}\nPFNGLSECONDARYCOLOR3UBVPROC glad_debug_glSecondaryColor3ubv = glad_debug_impl_glSecondaryColor3ubv;\nPFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3ui(GLuint red, GLuint green, GLuint blue) {\n    _pre_call_gl_callback(\"glSecondaryColor3ui\", (GLADapiproc) glad_glSecondaryColor3ui, 3, red, green, blue);\n    glad_glSecondaryColor3ui(red, green, blue);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3ui\", (GLADapiproc) glad_glSecondaryColor3ui, 3, red, green, blue);\n}\nPFNGLSECONDARYCOLOR3UIPROC glad_debug_glSecondaryColor3ui = glad_debug_impl_glSecondaryColor3ui;\nPFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3uiv(const GLuint * v) {\n    _pre_call_gl_callback(\"glSecondaryColor3uiv\", (GLADapiproc) glad_glSecondaryColor3uiv, 1, v);\n    glad_glSecondaryColor3uiv(v);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3uiv\", (GLADapiproc) glad_glSecondaryColor3uiv, 1, v);\n}\nPFNGLSECONDARYCOLOR3UIVPROC glad_debug_glSecondaryColor3uiv = glad_debug_impl_glSecondaryColor3uiv;\nPFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3us(GLushort red, GLushort green, GLushort blue) {\n    _pre_call_gl_callback(\"glSecondaryColor3us\", (GLADapiproc) glad_glSecondaryColor3us, 3, red, green, blue);\n    glad_glSecondaryColor3us(red, green, blue);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3us\", (GLADapiproc) glad_glSecondaryColor3us, 3, red, green, blue);\n}\nPFNGLSECONDARYCOLOR3USPROC glad_debug_glSecondaryColor3us = glad_debug_impl_glSecondaryColor3us;\nPFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColor3usv(const GLushort * v) {\n    _pre_call_gl_callback(\"glSecondaryColor3usv\", (GLADapiproc) glad_glSecondaryColor3usv, 1, v);\n    glad_glSecondaryColor3usv(v);\n    _post_call_gl_callback(NULL, \"glSecondaryColor3usv\", (GLADapiproc) glad_glSecondaryColor3usv, 1, v);\n}\nPFNGLSECONDARYCOLOR3USVPROC glad_debug_glSecondaryColor3usv = glad_debug_impl_glSecondaryColor3usv;\nPFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSecondaryColorPointer(GLint size, GLenum type, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glSecondaryColorPointer\", (GLADapiproc) glad_glSecondaryColorPointer, 4, size, type, stride, pointer);\n    glad_glSecondaryColorPointer(size, type, stride, pointer);\n    _post_call_gl_callback(NULL, \"glSecondaryColorPointer\", (GLADapiproc) glad_glSecondaryColorPointer, 4, size, type, stride, pointer);\n}\nPFNGLSECONDARYCOLORPOINTERPROC glad_debug_glSecondaryColorPointer = glad_debug_impl_glSecondaryColorPointer;\nPFNGLSELECTBUFFERPROC glad_glSelectBuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glSelectBuffer(GLsizei size, GLuint * buffer) {\n    _pre_call_gl_callback(\"glSelectBuffer\", (GLADapiproc) glad_glSelectBuffer, 2, size, buffer);\n    glad_glSelectBuffer(size, buffer);\n    _post_call_gl_callback(NULL, \"glSelectBuffer\", (GLADapiproc) glad_glSelectBuffer, 2, size, buffer);\n}\nPFNGLSELECTBUFFERPROC glad_debug_glSelectBuffer = glad_debug_impl_glSelectBuffer;\nPFNGLSHADEMODELPROC glad_glShadeModel = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glShadeModel(GLenum mode) {\n    _pre_call_gl_callback(\"glShadeModel\", (GLADapiproc) glad_glShadeModel, 1, mode);\n    glad_glShadeModel(mode);\n    _post_call_gl_callback(NULL, \"glShadeModel\", (GLADapiproc) glad_glShadeModel, 1, mode);\n}\nPFNGLSHADEMODELPROC glad_debug_glShadeModel = glad_debug_impl_glShadeModel;\nPFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glShaderSource(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length) {\n    _pre_call_gl_callback(\"glShaderSource\", (GLADapiproc) glad_glShaderSource, 4, shader, count, string, length);\n    glad_glShaderSource(shader, count, string, length);\n    _post_call_gl_callback(NULL, \"glShaderSource\", (GLADapiproc) glad_glShaderSource, 4, shader, count, string, length);\n}\nPFNGLSHADERSOURCEPROC glad_debug_glShaderSource = glad_debug_impl_glShaderSource;\nPFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glStencilFunc(GLenum func, GLint ref, GLuint mask) {\n    _pre_call_gl_callback(\"glStencilFunc\", (GLADapiproc) glad_glStencilFunc, 3, func, ref, mask);\n    glad_glStencilFunc(func, ref, mask);\n    _post_call_gl_callback(NULL, \"glStencilFunc\", (GLADapiproc) glad_glStencilFunc, 3, func, ref, mask);\n}\nPFNGLSTENCILFUNCPROC glad_debug_glStencilFunc = glad_debug_impl_glStencilFunc;\nPFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) {\n    _pre_call_gl_callback(\"glStencilFuncSeparate\", (GLADapiproc) glad_glStencilFuncSeparate, 4, face, func, ref, mask);\n    glad_glStencilFuncSeparate(face, func, ref, mask);\n    _post_call_gl_callback(NULL, \"glStencilFuncSeparate\", (GLADapiproc) glad_glStencilFuncSeparate, 4, face, func, ref, mask);\n}\nPFNGLSTENCILFUNCSEPARATEPROC glad_debug_glStencilFuncSeparate = glad_debug_impl_glStencilFuncSeparate;\nPFNGLSTENCILMASKPROC glad_glStencilMask = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glStencilMask(GLuint mask) {\n    _pre_call_gl_callback(\"glStencilMask\", (GLADapiproc) glad_glStencilMask, 1, mask);\n    glad_glStencilMask(mask);\n    _post_call_gl_callback(NULL, \"glStencilMask\", (GLADapiproc) glad_glStencilMask, 1, mask);\n}\nPFNGLSTENCILMASKPROC glad_debug_glStencilMask = glad_debug_impl_glStencilMask;\nPFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glStencilMaskSeparate(GLenum face, GLuint mask) {\n    _pre_call_gl_callback(\"glStencilMaskSeparate\", (GLADapiproc) glad_glStencilMaskSeparate, 2, face, mask);\n    glad_glStencilMaskSeparate(face, mask);\n    _post_call_gl_callback(NULL, \"glStencilMaskSeparate\", (GLADapiproc) glad_glStencilMaskSeparate, 2, face, mask);\n}\nPFNGLSTENCILMASKSEPARATEPROC glad_debug_glStencilMaskSeparate = glad_debug_impl_glStencilMaskSeparate;\nPFNGLSTENCILOPPROC glad_glStencilOp = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {\n    _pre_call_gl_callback(\"glStencilOp\", (GLADapiproc) glad_glStencilOp, 3, fail, zfail, zpass);\n    glad_glStencilOp(fail, zfail, zpass);\n    _post_call_gl_callback(NULL, \"glStencilOp\", (GLADapiproc) glad_glStencilOp, 3, fail, zfail, zpass);\n}\nPFNGLSTENCILOPPROC glad_debug_glStencilOp = glad_debug_impl_glStencilOp;\nPFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {\n    _pre_call_gl_callback(\"glStencilOpSeparate\", (GLADapiproc) glad_glStencilOpSeparate, 4, face, sfail, dpfail, dppass);\n    glad_glStencilOpSeparate(face, sfail, dpfail, dppass);\n    _post_call_gl_callback(NULL, \"glStencilOpSeparate\", (GLADapiproc) glad_glStencilOpSeparate, 4, face, sfail, dpfail, dppass);\n}\nPFNGLSTENCILOPSEPARATEPROC glad_debug_glStencilOpSeparate = glad_debug_impl_glStencilOpSeparate;\nPFNGLTEXBUFFERPROC glad_glTexBuffer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexBuffer(GLenum target, GLenum internalformat, GLuint buffer) {\n    _pre_call_gl_callback(\"glTexBuffer\", (GLADapiproc) glad_glTexBuffer, 3, target, internalformat, buffer);\n    glad_glTexBuffer(target, internalformat, buffer);\n    _post_call_gl_callback(NULL, \"glTexBuffer\", (GLADapiproc) glad_glTexBuffer, 3, target, internalformat, buffer);\n}\nPFNGLTEXBUFFERPROC glad_debug_glTexBuffer = glad_debug_impl_glTexBuffer;\nPFNGLTEXCOORD1DPROC glad_glTexCoord1d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord1d(GLdouble s) {\n    _pre_call_gl_callback(\"glTexCoord1d\", (GLADapiproc) glad_glTexCoord1d, 1, s);\n    glad_glTexCoord1d(s);\n    _post_call_gl_callback(NULL, \"glTexCoord1d\", (GLADapiproc) glad_glTexCoord1d, 1, s);\n}\nPFNGLTEXCOORD1DPROC glad_debug_glTexCoord1d = glad_debug_impl_glTexCoord1d;\nPFNGLTEXCOORD1DVPROC glad_glTexCoord1dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord1dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glTexCoord1dv\", (GLADapiproc) glad_glTexCoord1dv, 1, v);\n    glad_glTexCoord1dv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord1dv\", (GLADapiproc) glad_glTexCoord1dv, 1, v);\n}\nPFNGLTEXCOORD1DVPROC glad_debug_glTexCoord1dv = glad_debug_impl_glTexCoord1dv;\nPFNGLTEXCOORD1FPROC glad_glTexCoord1f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord1f(GLfloat s) {\n    _pre_call_gl_callback(\"glTexCoord1f\", (GLADapiproc) glad_glTexCoord1f, 1, s);\n    glad_glTexCoord1f(s);\n    _post_call_gl_callback(NULL, \"glTexCoord1f\", (GLADapiproc) glad_glTexCoord1f, 1, s);\n}\nPFNGLTEXCOORD1FPROC glad_debug_glTexCoord1f = glad_debug_impl_glTexCoord1f;\nPFNGLTEXCOORD1FVPROC glad_glTexCoord1fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord1fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glTexCoord1fv\", (GLADapiproc) glad_glTexCoord1fv, 1, v);\n    glad_glTexCoord1fv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord1fv\", (GLADapiproc) glad_glTexCoord1fv, 1, v);\n}\nPFNGLTEXCOORD1FVPROC glad_debug_glTexCoord1fv = glad_debug_impl_glTexCoord1fv;\nPFNGLTEXCOORD1IPROC glad_glTexCoord1i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord1i(GLint s) {\n    _pre_call_gl_callback(\"glTexCoord1i\", (GLADapiproc) glad_glTexCoord1i, 1, s);\n    glad_glTexCoord1i(s);\n    _post_call_gl_callback(NULL, \"glTexCoord1i\", (GLADapiproc) glad_glTexCoord1i, 1, s);\n}\nPFNGLTEXCOORD1IPROC glad_debug_glTexCoord1i = glad_debug_impl_glTexCoord1i;\nPFNGLTEXCOORD1IVPROC glad_glTexCoord1iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord1iv(const GLint * v) {\n    _pre_call_gl_callback(\"glTexCoord1iv\", (GLADapiproc) glad_glTexCoord1iv, 1, v);\n    glad_glTexCoord1iv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord1iv\", (GLADapiproc) glad_glTexCoord1iv, 1, v);\n}\nPFNGLTEXCOORD1IVPROC glad_debug_glTexCoord1iv = glad_debug_impl_glTexCoord1iv;\nPFNGLTEXCOORD1SPROC glad_glTexCoord1s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord1s(GLshort s) {\n    _pre_call_gl_callback(\"glTexCoord1s\", (GLADapiproc) glad_glTexCoord1s, 1, s);\n    glad_glTexCoord1s(s);\n    _post_call_gl_callback(NULL, \"glTexCoord1s\", (GLADapiproc) glad_glTexCoord1s, 1, s);\n}\nPFNGLTEXCOORD1SPROC glad_debug_glTexCoord1s = glad_debug_impl_glTexCoord1s;\nPFNGLTEXCOORD1SVPROC glad_glTexCoord1sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord1sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glTexCoord1sv\", (GLADapiproc) glad_glTexCoord1sv, 1, v);\n    glad_glTexCoord1sv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord1sv\", (GLADapiproc) glad_glTexCoord1sv, 1, v);\n}\nPFNGLTEXCOORD1SVPROC glad_debug_glTexCoord1sv = glad_debug_impl_glTexCoord1sv;\nPFNGLTEXCOORD2DPROC glad_glTexCoord2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord2d(GLdouble s, GLdouble t) {\n    _pre_call_gl_callback(\"glTexCoord2d\", (GLADapiproc) glad_glTexCoord2d, 2, s, t);\n    glad_glTexCoord2d(s, t);\n    _post_call_gl_callback(NULL, \"glTexCoord2d\", (GLADapiproc) glad_glTexCoord2d, 2, s, t);\n}\nPFNGLTEXCOORD2DPROC glad_debug_glTexCoord2d = glad_debug_impl_glTexCoord2d;\nPFNGLTEXCOORD2DVPROC glad_glTexCoord2dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord2dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glTexCoord2dv\", (GLADapiproc) glad_glTexCoord2dv, 1, v);\n    glad_glTexCoord2dv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord2dv\", (GLADapiproc) glad_glTexCoord2dv, 1, v);\n}\nPFNGLTEXCOORD2DVPROC glad_debug_glTexCoord2dv = glad_debug_impl_glTexCoord2dv;\nPFNGLTEXCOORD2FPROC glad_glTexCoord2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord2f(GLfloat s, GLfloat t) {\n    _pre_call_gl_callback(\"glTexCoord2f\", (GLADapiproc) glad_glTexCoord2f, 2, s, t);\n    glad_glTexCoord2f(s, t);\n    _post_call_gl_callback(NULL, \"glTexCoord2f\", (GLADapiproc) glad_glTexCoord2f, 2, s, t);\n}\nPFNGLTEXCOORD2FPROC glad_debug_glTexCoord2f = glad_debug_impl_glTexCoord2f;\nPFNGLTEXCOORD2FVPROC glad_glTexCoord2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord2fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glTexCoord2fv\", (GLADapiproc) glad_glTexCoord2fv, 1, v);\n    glad_glTexCoord2fv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord2fv\", (GLADapiproc) glad_glTexCoord2fv, 1, v);\n}\nPFNGLTEXCOORD2FVPROC glad_debug_glTexCoord2fv = glad_debug_impl_glTexCoord2fv;\nPFNGLTEXCOORD2IPROC glad_glTexCoord2i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord2i(GLint s, GLint t) {\n    _pre_call_gl_callback(\"glTexCoord2i\", (GLADapiproc) glad_glTexCoord2i, 2, s, t);\n    glad_glTexCoord2i(s, t);\n    _post_call_gl_callback(NULL, \"glTexCoord2i\", (GLADapiproc) glad_glTexCoord2i, 2, s, t);\n}\nPFNGLTEXCOORD2IPROC glad_debug_glTexCoord2i = glad_debug_impl_glTexCoord2i;\nPFNGLTEXCOORD2IVPROC glad_glTexCoord2iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord2iv(const GLint * v) {\n    _pre_call_gl_callback(\"glTexCoord2iv\", (GLADapiproc) glad_glTexCoord2iv, 1, v);\n    glad_glTexCoord2iv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord2iv\", (GLADapiproc) glad_glTexCoord2iv, 1, v);\n}\nPFNGLTEXCOORD2IVPROC glad_debug_glTexCoord2iv = glad_debug_impl_glTexCoord2iv;\nPFNGLTEXCOORD2SPROC glad_glTexCoord2s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord2s(GLshort s, GLshort t) {\n    _pre_call_gl_callback(\"glTexCoord2s\", (GLADapiproc) glad_glTexCoord2s, 2, s, t);\n    glad_glTexCoord2s(s, t);\n    _post_call_gl_callback(NULL, \"glTexCoord2s\", (GLADapiproc) glad_glTexCoord2s, 2, s, t);\n}\nPFNGLTEXCOORD2SPROC glad_debug_glTexCoord2s = glad_debug_impl_glTexCoord2s;\nPFNGLTEXCOORD2SVPROC glad_glTexCoord2sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord2sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glTexCoord2sv\", (GLADapiproc) glad_glTexCoord2sv, 1, v);\n    glad_glTexCoord2sv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord2sv\", (GLADapiproc) glad_glTexCoord2sv, 1, v);\n}\nPFNGLTEXCOORD2SVPROC glad_debug_glTexCoord2sv = glad_debug_impl_glTexCoord2sv;\nPFNGLTEXCOORD3DPROC glad_glTexCoord3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord3d(GLdouble s, GLdouble t, GLdouble r) {\n    _pre_call_gl_callback(\"glTexCoord3d\", (GLADapiproc) glad_glTexCoord3d, 3, s, t, r);\n    glad_glTexCoord3d(s, t, r);\n    _post_call_gl_callback(NULL, \"glTexCoord3d\", (GLADapiproc) glad_glTexCoord3d, 3, s, t, r);\n}\nPFNGLTEXCOORD3DPROC glad_debug_glTexCoord3d = glad_debug_impl_glTexCoord3d;\nPFNGLTEXCOORD3DVPROC glad_glTexCoord3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord3dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glTexCoord3dv\", (GLADapiproc) glad_glTexCoord3dv, 1, v);\n    glad_glTexCoord3dv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord3dv\", (GLADapiproc) glad_glTexCoord3dv, 1, v);\n}\nPFNGLTEXCOORD3DVPROC glad_debug_glTexCoord3dv = glad_debug_impl_glTexCoord3dv;\nPFNGLTEXCOORD3FPROC glad_glTexCoord3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord3f(GLfloat s, GLfloat t, GLfloat r) {\n    _pre_call_gl_callback(\"glTexCoord3f\", (GLADapiproc) glad_glTexCoord3f, 3, s, t, r);\n    glad_glTexCoord3f(s, t, r);\n    _post_call_gl_callback(NULL, \"glTexCoord3f\", (GLADapiproc) glad_glTexCoord3f, 3, s, t, r);\n}\nPFNGLTEXCOORD3FPROC glad_debug_glTexCoord3f = glad_debug_impl_glTexCoord3f;\nPFNGLTEXCOORD3FVPROC glad_glTexCoord3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord3fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glTexCoord3fv\", (GLADapiproc) glad_glTexCoord3fv, 1, v);\n    glad_glTexCoord3fv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord3fv\", (GLADapiproc) glad_glTexCoord3fv, 1, v);\n}\nPFNGLTEXCOORD3FVPROC glad_debug_glTexCoord3fv = glad_debug_impl_glTexCoord3fv;\nPFNGLTEXCOORD3IPROC glad_glTexCoord3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord3i(GLint s, GLint t, GLint r) {\n    _pre_call_gl_callback(\"glTexCoord3i\", (GLADapiproc) glad_glTexCoord3i, 3, s, t, r);\n    glad_glTexCoord3i(s, t, r);\n    _post_call_gl_callback(NULL, \"glTexCoord3i\", (GLADapiproc) glad_glTexCoord3i, 3, s, t, r);\n}\nPFNGLTEXCOORD3IPROC glad_debug_glTexCoord3i = glad_debug_impl_glTexCoord3i;\nPFNGLTEXCOORD3IVPROC glad_glTexCoord3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord3iv(const GLint * v) {\n    _pre_call_gl_callback(\"glTexCoord3iv\", (GLADapiproc) glad_glTexCoord3iv, 1, v);\n    glad_glTexCoord3iv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord3iv\", (GLADapiproc) glad_glTexCoord3iv, 1, v);\n}\nPFNGLTEXCOORD3IVPROC glad_debug_glTexCoord3iv = glad_debug_impl_glTexCoord3iv;\nPFNGLTEXCOORD3SPROC glad_glTexCoord3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord3s(GLshort s, GLshort t, GLshort r) {\n    _pre_call_gl_callback(\"glTexCoord3s\", (GLADapiproc) glad_glTexCoord3s, 3, s, t, r);\n    glad_glTexCoord3s(s, t, r);\n    _post_call_gl_callback(NULL, \"glTexCoord3s\", (GLADapiproc) glad_glTexCoord3s, 3, s, t, r);\n}\nPFNGLTEXCOORD3SPROC glad_debug_glTexCoord3s = glad_debug_impl_glTexCoord3s;\nPFNGLTEXCOORD3SVPROC glad_glTexCoord3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord3sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glTexCoord3sv\", (GLADapiproc) glad_glTexCoord3sv, 1, v);\n    glad_glTexCoord3sv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord3sv\", (GLADapiproc) glad_glTexCoord3sv, 1, v);\n}\nPFNGLTEXCOORD3SVPROC glad_debug_glTexCoord3sv = glad_debug_impl_glTexCoord3sv;\nPFNGLTEXCOORD4DPROC glad_glTexCoord4d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord4d(GLdouble s, GLdouble t, GLdouble r, GLdouble q) {\n    _pre_call_gl_callback(\"glTexCoord4d\", (GLADapiproc) glad_glTexCoord4d, 4, s, t, r, q);\n    glad_glTexCoord4d(s, t, r, q);\n    _post_call_gl_callback(NULL, \"glTexCoord4d\", (GLADapiproc) glad_glTexCoord4d, 4, s, t, r, q);\n}\nPFNGLTEXCOORD4DPROC glad_debug_glTexCoord4d = glad_debug_impl_glTexCoord4d;\nPFNGLTEXCOORD4DVPROC glad_glTexCoord4dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord4dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glTexCoord4dv\", (GLADapiproc) glad_glTexCoord4dv, 1, v);\n    glad_glTexCoord4dv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord4dv\", (GLADapiproc) glad_glTexCoord4dv, 1, v);\n}\nPFNGLTEXCOORD4DVPROC glad_debug_glTexCoord4dv = glad_debug_impl_glTexCoord4dv;\nPFNGLTEXCOORD4FPROC glad_glTexCoord4f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord4f(GLfloat s, GLfloat t, GLfloat r, GLfloat q) {\n    _pre_call_gl_callback(\"glTexCoord4f\", (GLADapiproc) glad_glTexCoord4f, 4, s, t, r, q);\n    glad_glTexCoord4f(s, t, r, q);\n    _post_call_gl_callback(NULL, \"glTexCoord4f\", (GLADapiproc) glad_glTexCoord4f, 4, s, t, r, q);\n}\nPFNGLTEXCOORD4FPROC glad_debug_glTexCoord4f = glad_debug_impl_glTexCoord4f;\nPFNGLTEXCOORD4FVPROC glad_glTexCoord4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord4fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glTexCoord4fv\", (GLADapiproc) glad_glTexCoord4fv, 1, v);\n    glad_glTexCoord4fv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord4fv\", (GLADapiproc) glad_glTexCoord4fv, 1, v);\n}\nPFNGLTEXCOORD4FVPROC glad_debug_glTexCoord4fv = glad_debug_impl_glTexCoord4fv;\nPFNGLTEXCOORD4IPROC glad_glTexCoord4i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord4i(GLint s, GLint t, GLint r, GLint q) {\n    _pre_call_gl_callback(\"glTexCoord4i\", (GLADapiproc) glad_glTexCoord4i, 4, s, t, r, q);\n    glad_glTexCoord4i(s, t, r, q);\n    _post_call_gl_callback(NULL, \"glTexCoord4i\", (GLADapiproc) glad_glTexCoord4i, 4, s, t, r, q);\n}\nPFNGLTEXCOORD4IPROC glad_debug_glTexCoord4i = glad_debug_impl_glTexCoord4i;\nPFNGLTEXCOORD4IVPROC glad_glTexCoord4iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord4iv(const GLint * v) {\n    _pre_call_gl_callback(\"glTexCoord4iv\", (GLADapiproc) glad_glTexCoord4iv, 1, v);\n    glad_glTexCoord4iv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord4iv\", (GLADapiproc) glad_glTexCoord4iv, 1, v);\n}\nPFNGLTEXCOORD4IVPROC glad_debug_glTexCoord4iv = glad_debug_impl_glTexCoord4iv;\nPFNGLTEXCOORD4SPROC glad_glTexCoord4s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord4s(GLshort s, GLshort t, GLshort r, GLshort q) {\n    _pre_call_gl_callback(\"glTexCoord4s\", (GLADapiproc) glad_glTexCoord4s, 4, s, t, r, q);\n    glad_glTexCoord4s(s, t, r, q);\n    _post_call_gl_callback(NULL, \"glTexCoord4s\", (GLADapiproc) glad_glTexCoord4s, 4, s, t, r, q);\n}\nPFNGLTEXCOORD4SPROC glad_debug_glTexCoord4s = glad_debug_impl_glTexCoord4s;\nPFNGLTEXCOORD4SVPROC glad_glTexCoord4sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoord4sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glTexCoord4sv\", (GLADapiproc) glad_glTexCoord4sv, 1, v);\n    glad_glTexCoord4sv(v);\n    _post_call_gl_callback(NULL, \"glTexCoord4sv\", (GLADapiproc) glad_glTexCoord4sv, 1, v);\n}\nPFNGLTEXCOORD4SVPROC glad_debug_glTexCoord4sv = glad_debug_impl_glTexCoord4sv;\nPFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glTexCoordPointer\", (GLADapiproc) glad_glTexCoordPointer, 4, size, type, stride, pointer);\n    glad_glTexCoordPointer(size, type, stride, pointer);\n    _post_call_gl_callback(NULL, \"glTexCoordPointer\", (GLADapiproc) glad_glTexCoordPointer, 4, size, type, stride, pointer);\n}\nPFNGLTEXCOORDPOINTERPROC glad_debug_glTexCoordPointer = glad_debug_impl_glTexCoordPointer;\nPFNGLTEXENVFPROC glad_glTexEnvf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexEnvf(GLenum target, GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glTexEnvf\", (GLADapiproc) glad_glTexEnvf, 3, target, pname, param);\n    glad_glTexEnvf(target, pname, param);\n    _post_call_gl_callback(NULL, \"glTexEnvf\", (GLADapiproc) glad_glTexEnvf, 3, target, pname, param);\n}\nPFNGLTEXENVFPROC glad_debug_glTexEnvf = glad_debug_impl_glTexEnvf;\nPFNGLTEXENVFVPROC glad_glTexEnvfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexEnvfv(GLenum target, GLenum pname, const GLfloat * params) {\n    _pre_call_gl_callback(\"glTexEnvfv\", (GLADapiproc) glad_glTexEnvfv, 3, target, pname, params);\n    glad_glTexEnvfv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glTexEnvfv\", (GLADapiproc) glad_glTexEnvfv, 3, target, pname, params);\n}\nPFNGLTEXENVFVPROC glad_debug_glTexEnvfv = glad_debug_impl_glTexEnvfv;\nPFNGLTEXENVIPROC glad_glTexEnvi = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexEnvi(GLenum target, GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glTexEnvi\", (GLADapiproc) glad_glTexEnvi, 3, target, pname, param);\n    glad_glTexEnvi(target, pname, param);\n    _post_call_gl_callback(NULL, \"glTexEnvi\", (GLADapiproc) glad_glTexEnvi, 3, target, pname, param);\n}\nPFNGLTEXENVIPROC glad_debug_glTexEnvi = glad_debug_impl_glTexEnvi;\nPFNGLTEXENVIVPROC glad_glTexEnviv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexEnviv(GLenum target, GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glTexEnviv\", (GLADapiproc) glad_glTexEnviv, 3, target, pname, params);\n    glad_glTexEnviv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glTexEnviv\", (GLADapiproc) glad_glTexEnviv, 3, target, pname, params);\n}\nPFNGLTEXENVIVPROC glad_debug_glTexEnviv = glad_debug_impl_glTexEnviv;\nPFNGLTEXGENDPROC glad_glTexGend = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexGend(GLenum coord, GLenum pname, GLdouble param) {\n    _pre_call_gl_callback(\"glTexGend\", (GLADapiproc) glad_glTexGend, 3, coord, pname, param);\n    glad_glTexGend(coord, pname, param);\n    _post_call_gl_callback(NULL, \"glTexGend\", (GLADapiproc) glad_glTexGend, 3, coord, pname, param);\n}\nPFNGLTEXGENDPROC glad_debug_glTexGend = glad_debug_impl_glTexGend;\nPFNGLTEXGENDVPROC glad_glTexGendv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexGendv(GLenum coord, GLenum pname, const GLdouble * params) {\n    _pre_call_gl_callback(\"glTexGendv\", (GLADapiproc) glad_glTexGendv, 3, coord, pname, params);\n    glad_glTexGendv(coord, pname, params);\n    _post_call_gl_callback(NULL, \"glTexGendv\", (GLADapiproc) glad_glTexGendv, 3, coord, pname, params);\n}\nPFNGLTEXGENDVPROC glad_debug_glTexGendv = glad_debug_impl_glTexGendv;\nPFNGLTEXGENFPROC glad_glTexGenf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexGenf(GLenum coord, GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glTexGenf\", (GLADapiproc) glad_glTexGenf, 3, coord, pname, param);\n    glad_glTexGenf(coord, pname, param);\n    _post_call_gl_callback(NULL, \"glTexGenf\", (GLADapiproc) glad_glTexGenf, 3, coord, pname, param);\n}\nPFNGLTEXGENFPROC glad_debug_glTexGenf = glad_debug_impl_glTexGenf;\nPFNGLTEXGENFVPROC glad_glTexGenfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexGenfv(GLenum coord, GLenum pname, const GLfloat * params) {\n    _pre_call_gl_callback(\"glTexGenfv\", (GLADapiproc) glad_glTexGenfv, 3, coord, pname, params);\n    glad_glTexGenfv(coord, pname, params);\n    _post_call_gl_callback(NULL, \"glTexGenfv\", (GLADapiproc) glad_glTexGenfv, 3, coord, pname, params);\n}\nPFNGLTEXGENFVPROC glad_debug_glTexGenfv = glad_debug_impl_glTexGenfv;\nPFNGLTEXGENIPROC glad_glTexGeni = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexGeni(GLenum coord, GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glTexGeni\", (GLADapiproc) glad_glTexGeni, 3, coord, pname, param);\n    glad_glTexGeni(coord, pname, param);\n    _post_call_gl_callback(NULL, \"glTexGeni\", (GLADapiproc) glad_glTexGeni, 3, coord, pname, param);\n}\nPFNGLTEXGENIPROC glad_debug_glTexGeni = glad_debug_impl_glTexGeni;\nPFNGLTEXGENIVPROC glad_glTexGeniv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexGeniv(GLenum coord, GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glTexGeniv\", (GLADapiproc) glad_glTexGeniv, 3, coord, pname, params);\n    glad_glTexGeniv(coord, pname, params);\n    _post_call_gl_callback(NULL, \"glTexGeniv\", (GLADapiproc) glad_glTexGeniv, 3, coord, pname, params);\n}\nPFNGLTEXGENIVPROC glad_debug_glTexGeniv = glad_debug_impl_glTexGeniv;\nPFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexImage1D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels) {\n    _pre_call_gl_callback(\"glTexImage1D\", (GLADapiproc) glad_glTexImage1D, 8, target, level, internalformat, width, border, format, type, pixels);\n    glad_glTexImage1D(target, level, internalformat, width, border, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glTexImage1D\", (GLADapiproc) glad_glTexImage1D, 8, target, level, internalformat, width, border, format, type, pixels);\n}\nPFNGLTEXIMAGE1DPROC glad_debug_glTexImage1D = glad_debug_impl_glTexImage1D;\nPFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels) {\n    _pre_call_gl_callback(\"glTexImage2D\", (GLADapiproc) glad_glTexImage2D, 9, target, level, internalformat, width, height, border, format, type, pixels);\n    glad_glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glTexImage2D\", (GLADapiproc) glad_glTexImage2D, 9, target, level, internalformat, width, height, border, format, type, pixels);\n}\nPFNGLTEXIMAGE2DPROC glad_debug_glTexImage2D = glad_debug_impl_glTexImage2D;\nPFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels) {\n    _pre_call_gl_callback(\"glTexImage3D\", (GLADapiproc) glad_glTexImage3D, 10, target, level, internalformat, width, height, depth, border, format, type, pixels);\n    glad_glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glTexImage3D\", (GLADapiproc) glad_glTexImage3D, 10, target, level, internalformat, width, height, depth, border, format, type, pixels);\n}\nPFNGLTEXIMAGE3DPROC glad_debug_glTexImage3D = glad_debug_impl_glTexImage3D;\nPFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexParameterIiv(GLenum target, GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glTexParameterIiv\", (GLADapiproc) glad_glTexParameterIiv, 3, target, pname, params);\n    glad_glTexParameterIiv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glTexParameterIiv\", (GLADapiproc) glad_glTexParameterIiv, 3, target, pname, params);\n}\nPFNGLTEXPARAMETERIIVPROC glad_debug_glTexParameterIiv = glad_debug_impl_glTexParameterIiv;\nPFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexParameterIuiv(GLenum target, GLenum pname, const GLuint * params) {\n    _pre_call_gl_callback(\"glTexParameterIuiv\", (GLADapiproc) glad_glTexParameterIuiv, 3, target, pname, params);\n    glad_glTexParameterIuiv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glTexParameterIuiv\", (GLADapiproc) glad_glTexParameterIuiv, 3, target, pname, params);\n}\nPFNGLTEXPARAMETERIUIVPROC glad_debug_glTexParameterIuiv = glad_debug_impl_glTexParameterIuiv;\nPFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexParameterf(GLenum target, GLenum pname, GLfloat param) {\n    _pre_call_gl_callback(\"glTexParameterf\", (GLADapiproc) glad_glTexParameterf, 3, target, pname, param);\n    glad_glTexParameterf(target, pname, param);\n    _post_call_gl_callback(NULL, \"glTexParameterf\", (GLADapiproc) glad_glTexParameterf, 3, target, pname, param);\n}\nPFNGLTEXPARAMETERFPROC glad_debug_glTexParameterf = glad_debug_impl_glTexParameterf;\nPFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexParameterfv(GLenum target, GLenum pname, const GLfloat * params) {\n    _pre_call_gl_callback(\"glTexParameterfv\", (GLADapiproc) glad_glTexParameterfv, 3, target, pname, params);\n    glad_glTexParameterfv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glTexParameterfv\", (GLADapiproc) glad_glTexParameterfv, 3, target, pname, params);\n}\nPFNGLTEXPARAMETERFVPROC glad_debug_glTexParameterfv = glad_debug_impl_glTexParameterfv;\nPFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexParameteri(GLenum target, GLenum pname, GLint param) {\n    _pre_call_gl_callback(\"glTexParameteri\", (GLADapiproc) glad_glTexParameteri, 3, target, pname, param);\n    glad_glTexParameteri(target, pname, param);\n    _post_call_gl_callback(NULL, \"glTexParameteri\", (GLADapiproc) glad_glTexParameteri, 3, target, pname, param);\n}\nPFNGLTEXPARAMETERIPROC glad_debug_glTexParameteri = glad_debug_impl_glTexParameteri;\nPFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexParameteriv(GLenum target, GLenum pname, const GLint * params) {\n    _pre_call_gl_callback(\"glTexParameteriv\", (GLADapiproc) glad_glTexParameteriv, 3, target, pname, params);\n    glad_glTexParameteriv(target, pname, params);\n    _post_call_gl_callback(NULL, \"glTexParameteriv\", (GLADapiproc) glad_glTexParameteriv, 3, target, pname, params);\n}\nPFNGLTEXPARAMETERIVPROC glad_debug_glTexParameteriv = glad_debug_impl_glTexParameteriv;\nPFNGLTEXSTORAGE1DPROC glad_glTexStorage1D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexStorage1D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) {\n    _pre_call_gl_callback(\"glTexStorage1D\", (GLADapiproc) glad_glTexStorage1D, 4, target, levels, internalformat, width);\n    glad_glTexStorage1D(target, levels, internalformat, width);\n    _post_call_gl_callback(NULL, \"glTexStorage1D\", (GLADapiproc) glad_glTexStorage1D, 4, target, levels, internalformat, width);\n}\nPFNGLTEXSTORAGE1DPROC glad_debug_glTexStorage1D = glad_debug_impl_glTexStorage1D;\nPFNGLTEXSTORAGE2DPROC glad_glTexStorage2D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) {\n    _pre_call_gl_callback(\"glTexStorage2D\", (GLADapiproc) glad_glTexStorage2D, 5, target, levels, internalformat, width, height);\n    glad_glTexStorage2D(target, levels, internalformat, width, height);\n    _post_call_gl_callback(NULL, \"glTexStorage2D\", (GLADapiproc) glad_glTexStorage2D, 5, target, levels, internalformat, width, height);\n}\nPFNGLTEXSTORAGE2DPROC glad_debug_glTexStorage2D = glad_debug_impl_glTexStorage2D;\nPFNGLTEXSTORAGE3DPROC glad_glTexStorage3D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) {\n    _pre_call_gl_callback(\"glTexStorage3D\", (GLADapiproc) glad_glTexStorage3D, 6, target, levels, internalformat, width, height, depth);\n    glad_glTexStorage3D(target, levels, internalformat, width, height, depth);\n    _post_call_gl_callback(NULL, \"glTexStorage3D\", (GLADapiproc) glad_glTexStorage3D, 6, target, levels, internalformat, width, height, depth);\n}\nPFNGLTEXSTORAGE3DPROC glad_debug_glTexStorage3D = glad_debug_impl_glTexStorage3D;\nPFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels) {\n    _pre_call_gl_callback(\"glTexSubImage1D\", (GLADapiproc) glad_glTexSubImage1D, 7, target, level, xoffset, width, format, type, pixels);\n    glad_glTexSubImage1D(target, level, xoffset, width, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glTexSubImage1D\", (GLADapiproc) glad_glTexSubImage1D, 7, target, level, xoffset, width, format, type, pixels);\n}\nPFNGLTEXSUBIMAGE1DPROC glad_debug_glTexSubImage1D = glad_debug_impl_glTexSubImage1D;\nPFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels) {\n    _pre_call_gl_callback(\"glTexSubImage2D\", (GLADapiproc) glad_glTexSubImage2D, 9, target, level, xoffset, yoffset, width, height, format, type, pixels);\n    glad_glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glTexSubImage2D\", (GLADapiproc) glad_glTexSubImage2D, 9, target, level, xoffset, yoffset, width, height, format, type, pixels);\n}\nPFNGLTEXSUBIMAGE2DPROC glad_debug_glTexSubImage2D = glad_debug_impl_glTexSubImage2D;\nPFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels) {\n    _pre_call_gl_callback(\"glTexSubImage3D\", (GLADapiproc) glad_glTexSubImage3D, 11, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);\n    glad_glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);\n    _post_call_gl_callback(NULL, \"glTexSubImage3D\", (GLADapiproc) glad_glTexSubImage3D, 11, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);\n}\nPFNGLTEXSUBIMAGE3DPROC glad_debug_glTexSubImage3D = glad_debug_impl_glTexSubImage3D;\nPFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode) {\n    _pre_call_gl_callback(\"glTransformFeedbackVaryings\", (GLADapiproc) glad_glTransformFeedbackVaryings, 4, program, count, varyings, bufferMode);\n    glad_glTransformFeedbackVaryings(program, count, varyings, bufferMode);\n    _post_call_gl_callback(NULL, \"glTransformFeedbackVaryings\", (GLADapiproc) glad_glTransformFeedbackVaryings, 4, program, count, varyings, bufferMode);\n}\nPFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_debug_glTransformFeedbackVaryings = glad_debug_impl_glTransformFeedbackVaryings;\nPFNGLTRANSLATEDPROC glad_glTranslated = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTranslated(GLdouble x, GLdouble y, GLdouble z) {\n    _pre_call_gl_callback(\"glTranslated\", (GLADapiproc) glad_glTranslated, 3, x, y, z);\n    glad_glTranslated(x, y, z);\n    _post_call_gl_callback(NULL, \"glTranslated\", (GLADapiproc) glad_glTranslated, 3, x, y, z);\n}\nPFNGLTRANSLATEDPROC glad_debug_glTranslated = glad_debug_impl_glTranslated;\nPFNGLTRANSLATEFPROC glad_glTranslatef = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glTranslatef(GLfloat x, GLfloat y, GLfloat z) {\n    _pre_call_gl_callback(\"glTranslatef\", (GLADapiproc) glad_glTranslatef, 3, x, y, z);\n    glad_glTranslatef(x, y, z);\n    _post_call_gl_callback(NULL, \"glTranslatef\", (GLADapiproc) glad_glTranslatef, 3, x, y, z);\n}\nPFNGLTRANSLATEFPROC glad_debug_glTranslatef = glad_debug_impl_glTranslatef;\nPFNGLUNIFORM1FPROC glad_glUniform1f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform1f(GLint location, GLfloat v0) {\n    _pre_call_gl_callback(\"glUniform1f\", (GLADapiproc) glad_glUniform1f, 2, location, v0);\n    glad_glUniform1f(location, v0);\n    _post_call_gl_callback(NULL, \"glUniform1f\", (GLADapiproc) glad_glUniform1f, 2, location, v0);\n}\nPFNGLUNIFORM1FPROC glad_debug_glUniform1f = glad_debug_impl_glUniform1f;\nPFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform1fv(GLint location, GLsizei count, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniform1fv\", (GLADapiproc) glad_glUniform1fv, 3, location, count, value);\n    glad_glUniform1fv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform1fv\", (GLADapiproc) glad_glUniform1fv, 3, location, count, value);\n}\nPFNGLUNIFORM1FVPROC glad_debug_glUniform1fv = glad_debug_impl_glUniform1fv;\nPFNGLUNIFORM1IPROC glad_glUniform1i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform1i(GLint location, GLint v0) {\n    _pre_call_gl_callback(\"glUniform1i\", (GLADapiproc) glad_glUniform1i, 2, location, v0);\n    glad_glUniform1i(location, v0);\n    _post_call_gl_callback(NULL, \"glUniform1i\", (GLADapiproc) glad_glUniform1i, 2, location, v0);\n}\nPFNGLUNIFORM1IPROC glad_debug_glUniform1i = glad_debug_impl_glUniform1i;\nPFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform1iv(GLint location, GLsizei count, const GLint * value) {\n    _pre_call_gl_callback(\"glUniform1iv\", (GLADapiproc) glad_glUniform1iv, 3, location, count, value);\n    glad_glUniform1iv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform1iv\", (GLADapiproc) glad_glUniform1iv, 3, location, count, value);\n}\nPFNGLUNIFORM1IVPROC glad_debug_glUniform1iv = glad_debug_impl_glUniform1iv;\nPFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform1ui(GLint location, GLuint v0) {\n    _pre_call_gl_callback(\"glUniform1ui\", (GLADapiproc) glad_glUniform1ui, 2, location, v0);\n    glad_glUniform1ui(location, v0);\n    _post_call_gl_callback(NULL, \"glUniform1ui\", (GLADapiproc) glad_glUniform1ui, 2, location, v0);\n}\nPFNGLUNIFORM1UIPROC glad_debug_glUniform1ui = glad_debug_impl_glUniform1ui;\nPFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform1uiv(GLint location, GLsizei count, const GLuint * value) {\n    _pre_call_gl_callback(\"glUniform1uiv\", (GLADapiproc) glad_glUniform1uiv, 3, location, count, value);\n    glad_glUniform1uiv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform1uiv\", (GLADapiproc) glad_glUniform1uiv, 3, location, count, value);\n}\nPFNGLUNIFORM1UIVPROC glad_debug_glUniform1uiv = glad_debug_impl_glUniform1uiv;\nPFNGLUNIFORM2FPROC glad_glUniform2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform2f(GLint location, GLfloat v0, GLfloat v1) {\n    _pre_call_gl_callback(\"glUniform2f\", (GLADapiproc) glad_glUniform2f, 3, location, v0, v1);\n    glad_glUniform2f(location, v0, v1);\n    _post_call_gl_callback(NULL, \"glUniform2f\", (GLADapiproc) glad_glUniform2f, 3, location, v0, v1);\n}\nPFNGLUNIFORM2FPROC glad_debug_glUniform2f = glad_debug_impl_glUniform2f;\nPFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform2fv(GLint location, GLsizei count, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniform2fv\", (GLADapiproc) glad_glUniform2fv, 3, location, count, value);\n    glad_glUniform2fv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform2fv\", (GLADapiproc) glad_glUniform2fv, 3, location, count, value);\n}\nPFNGLUNIFORM2FVPROC glad_debug_glUniform2fv = glad_debug_impl_glUniform2fv;\nPFNGLUNIFORM2IPROC glad_glUniform2i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform2i(GLint location, GLint v0, GLint v1) {\n    _pre_call_gl_callback(\"glUniform2i\", (GLADapiproc) glad_glUniform2i, 3, location, v0, v1);\n    glad_glUniform2i(location, v0, v1);\n    _post_call_gl_callback(NULL, \"glUniform2i\", (GLADapiproc) glad_glUniform2i, 3, location, v0, v1);\n}\nPFNGLUNIFORM2IPROC glad_debug_glUniform2i = glad_debug_impl_glUniform2i;\nPFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform2iv(GLint location, GLsizei count, const GLint * value) {\n    _pre_call_gl_callback(\"glUniform2iv\", (GLADapiproc) glad_glUniform2iv, 3, location, count, value);\n    glad_glUniform2iv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform2iv\", (GLADapiproc) glad_glUniform2iv, 3, location, count, value);\n}\nPFNGLUNIFORM2IVPROC glad_debug_glUniform2iv = glad_debug_impl_glUniform2iv;\nPFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform2ui(GLint location, GLuint v0, GLuint v1) {\n    _pre_call_gl_callback(\"glUniform2ui\", (GLADapiproc) glad_glUniform2ui, 3, location, v0, v1);\n    glad_glUniform2ui(location, v0, v1);\n    _post_call_gl_callback(NULL, \"glUniform2ui\", (GLADapiproc) glad_glUniform2ui, 3, location, v0, v1);\n}\nPFNGLUNIFORM2UIPROC glad_debug_glUniform2ui = glad_debug_impl_glUniform2ui;\nPFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform2uiv(GLint location, GLsizei count, const GLuint * value) {\n    _pre_call_gl_callback(\"glUniform2uiv\", (GLADapiproc) glad_glUniform2uiv, 3, location, count, value);\n    glad_glUniform2uiv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform2uiv\", (GLADapiproc) glad_glUniform2uiv, 3, location, count, value);\n}\nPFNGLUNIFORM2UIVPROC glad_debug_glUniform2uiv = glad_debug_impl_glUniform2uiv;\nPFNGLUNIFORM3FPROC glad_glUniform3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {\n    _pre_call_gl_callback(\"glUniform3f\", (GLADapiproc) glad_glUniform3f, 4, location, v0, v1, v2);\n    glad_glUniform3f(location, v0, v1, v2);\n    _post_call_gl_callback(NULL, \"glUniform3f\", (GLADapiproc) glad_glUniform3f, 4, location, v0, v1, v2);\n}\nPFNGLUNIFORM3FPROC glad_debug_glUniform3f = glad_debug_impl_glUniform3f;\nPFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform3fv(GLint location, GLsizei count, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniform3fv\", (GLADapiproc) glad_glUniform3fv, 3, location, count, value);\n    glad_glUniform3fv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform3fv\", (GLADapiproc) glad_glUniform3fv, 3, location, count, value);\n}\nPFNGLUNIFORM3FVPROC glad_debug_glUniform3fv = glad_debug_impl_glUniform3fv;\nPFNGLUNIFORM3IPROC glad_glUniform3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform3i(GLint location, GLint v0, GLint v1, GLint v2) {\n    _pre_call_gl_callback(\"glUniform3i\", (GLADapiproc) glad_glUniform3i, 4, location, v0, v1, v2);\n    glad_glUniform3i(location, v0, v1, v2);\n    _post_call_gl_callback(NULL, \"glUniform3i\", (GLADapiproc) glad_glUniform3i, 4, location, v0, v1, v2);\n}\nPFNGLUNIFORM3IPROC glad_debug_glUniform3i = glad_debug_impl_glUniform3i;\nPFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform3iv(GLint location, GLsizei count, const GLint * value) {\n    _pre_call_gl_callback(\"glUniform3iv\", (GLADapiproc) glad_glUniform3iv, 3, location, count, value);\n    glad_glUniform3iv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform3iv\", (GLADapiproc) glad_glUniform3iv, 3, location, count, value);\n}\nPFNGLUNIFORM3IVPROC glad_debug_glUniform3iv = glad_debug_impl_glUniform3iv;\nPFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2) {\n    _pre_call_gl_callback(\"glUniform3ui\", (GLADapiproc) glad_glUniform3ui, 4, location, v0, v1, v2);\n    glad_glUniform3ui(location, v0, v1, v2);\n    _post_call_gl_callback(NULL, \"glUniform3ui\", (GLADapiproc) glad_glUniform3ui, 4, location, v0, v1, v2);\n}\nPFNGLUNIFORM3UIPROC glad_debug_glUniform3ui = glad_debug_impl_glUniform3ui;\nPFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform3uiv(GLint location, GLsizei count, const GLuint * value) {\n    _pre_call_gl_callback(\"glUniform3uiv\", (GLADapiproc) glad_glUniform3uiv, 3, location, count, value);\n    glad_glUniform3uiv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform3uiv\", (GLADapiproc) glad_glUniform3uiv, 3, location, count, value);\n}\nPFNGLUNIFORM3UIVPROC glad_debug_glUniform3uiv = glad_debug_impl_glUniform3uiv;\nPFNGLUNIFORM4FPROC glad_glUniform4f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {\n    _pre_call_gl_callback(\"glUniform4f\", (GLADapiproc) glad_glUniform4f, 5, location, v0, v1, v2, v3);\n    glad_glUniform4f(location, v0, v1, v2, v3);\n    _post_call_gl_callback(NULL, \"glUniform4f\", (GLADapiproc) glad_glUniform4f, 5, location, v0, v1, v2, v3);\n}\nPFNGLUNIFORM4FPROC glad_debug_glUniform4f = glad_debug_impl_glUniform4f;\nPFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform4fv(GLint location, GLsizei count, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniform4fv\", (GLADapiproc) glad_glUniform4fv, 3, location, count, value);\n    glad_glUniform4fv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform4fv\", (GLADapiproc) glad_glUniform4fv, 3, location, count, value);\n}\nPFNGLUNIFORM4FVPROC glad_debug_glUniform4fv = glad_debug_impl_glUniform4fv;\nPFNGLUNIFORM4IPROC glad_glUniform4i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {\n    _pre_call_gl_callback(\"glUniform4i\", (GLADapiproc) glad_glUniform4i, 5, location, v0, v1, v2, v3);\n    glad_glUniform4i(location, v0, v1, v2, v3);\n    _post_call_gl_callback(NULL, \"glUniform4i\", (GLADapiproc) glad_glUniform4i, 5, location, v0, v1, v2, v3);\n}\nPFNGLUNIFORM4IPROC glad_debug_glUniform4i = glad_debug_impl_glUniform4i;\nPFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform4iv(GLint location, GLsizei count, const GLint * value) {\n    _pre_call_gl_callback(\"glUniform4iv\", (GLADapiproc) glad_glUniform4iv, 3, location, count, value);\n    glad_glUniform4iv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform4iv\", (GLADapiproc) glad_glUniform4iv, 3, location, count, value);\n}\nPFNGLUNIFORM4IVPROC glad_debug_glUniform4iv = glad_debug_impl_glUniform4iv;\nPFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) {\n    _pre_call_gl_callback(\"glUniform4ui\", (GLADapiproc) glad_glUniform4ui, 5, location, v0, v1, v2, v3);\n    glad_glUniform4ui(location, v0, v1, v2, v3);\n    _post_call_gl_callback(NULL, \"glUniform4ui\", (GLADapiproc) glad_glUniform4ui, 5, location, v0, v1, v2, v3);\n}\nPFNGLUNIFORM4UIPROC glad_debug_glUniform4ui = glad_debug_impl_glUniform4ui;\nPFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniform4uiv(GLint location, GLsizei count, const GLuint * value) {\n    _pre_call_gl_callback(\"glUniform4uiv\", (GLADapiproc) glad_glUniform4uiv, 3, location, count, value);\n    glad_glUniform4uiv(location, count, value);\n    _post_call_gl_callback(NULL, \"glUniform4uiv\", (GLADapiproc) glad_glUniform4uiv, 3, location, count, value);\n}\nPFNGLUNIFORM4UIVPROC glad_debug_glUniform4uiv = glad_debug_impl_glUniform4uiv;\nPFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) {\n    _pre_call_gl_callback(\"glUniformBlockBinding\", (GLADapiproc) glad_glUniformBlockBinding, 3, program, uniformBlockIndex, uniformBlockBinding);\n    glad_glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);\n    _post_call_gl_callback(NULL, \"glUniformBlockBinding\", (GLADapiproc) glad_glUniformBlockBinding, 3, program, uniformBlockIndex, uniformBlockBinding);\n}\nPFNGLUNIFORMBLOCKBINDINGPROC glad_debug_glUniformBlockBinding = glad_debug_impl_glUniformBlockBinding;\nPFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix2fv\", (GLADapiproc) glad_glUniformMatrix2fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix2fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix2fv\", (GLADapiproc) glad_glUniformMatrix2fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX2FVPROC glad_debug_glUniformMatrix2fv = glad_debug_impl_glUniformMatrix2fv;\nPFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix2x3fv\", (GLADapiproc) glad_glUniformMatrix2x3fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix2x3fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix2x3fv\", (GLADapiproc) glad_glUniformMatrix2x3fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX2X3FVPROC glad_debug_glUniformMatrix2x3fv = glad_debug_impl_glUniformMatrix2x3fv;\nPFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix2x4fv\", (GLADapiproc) glad_glUniformMatrix2x4fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix2x4fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix2x4fv\", (GLADapiproc) glad_glUniformMatrix2x4fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX2X4FVPROC glad_debug_glUniformMatrix2x4fv = glad_debug_impl_glUniformMatrix2x4fv;\nPFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix3fv\", (GLADapiproc) glad_glUniformMatrix3fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix3fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix3fv\", (GLADapiproc) glad_glUniformMatrix3fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX3FVPROC glad_debug_glUniformMatrix3fv = glad_debug_impl_glUniformMatrix3fv;\nPFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix3x2fv\", (GLADapiproc) glad_glUniformMatrix3x2fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix3x2fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix3x2fv\", (GLADapiproc) glad_glUniformMatrix3x2fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX3X2FVPROC glad_debug_glUniformMatrix3x2fv = glad_debug_impl_glUniformMatrix3x2fv;\nPFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix3x4fv\", (GLADapiproc) glad_glUniformMatrix3x4fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix3x4fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix3x4fv\", (GLADapiproc) glad_glUniformMatrix3x4fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX3X4FVPROC glad_debug_glUniformMatrix3x4fv = glad_debug_impl_glUniformMatrix3x4fv;\nPFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix4fv\", (GLADapiproc) glad_glUniformMatrix4fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix4fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix4fv\", (GLADapiproc) glad_glUniformMatrix4fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX4FVPROC glad_debug_glUniformMatrix4fv = glad_debug_impl_glUniformMatrix4fv;\nPFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix4x2fv\", (GLADapiproc) glad_glUniformMatrix4x2fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix4x2fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix4x2fv\", (GLADapiproc) glad_glUniformMatrix4x2fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX4X2FVPROC glad_debug_glUniformMatrix4x2fv = glad_debug_impl_glUniformMatrix4x2fv;\nPFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) {\n    _pre_call_gl_callback(\"glUniformMatrix4x3fv\", (GLADapiproc) glad_glUniformMatrix4x3fv, 4, location, count, transpose, value);\n    glad_glUniformMatrix4x3fv(location, count, transpose, value);\n    _post_call_gl_callback(NULL, \"glUniformMatrix4x3fv\", (GLADapiproc) glad_glUniformMatrix4x3fv, 4, location, count, transpose, value);\n}\nPFNGLUNIFORMMATRIX4X3FVPROC glad_debug_glUniformMatrix4x3fv = glad_debug_impl_glUniformMatrix4x3fv;\nPFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL;\nstatic GLboolean GLAD_API_PTR glad_debug_impl_glUnmapBuffer(GLenum target) {\n    GLboolean ret;\n    _pre_call_gl_callback(\"glUnmapBuffer\", (GLADapiproc) glad_glUnmapBuffer, 1, target);\n    ret = glad_glUnmapBuffer(target);\n    _post_call_gl_callback((void*) &ret, \"glUnmapBuffer\", (GLADapiproc) glad_glUnmapBuffer, 1, target);\n    return ret;\n}\nPFNGLUNMAPBUFFERPROC glad_debug_glUnmapBuffer = glad_debug_impl_glUnmapBuffer;\nPFNGLUSEPROGRAMPROC glad_glUseProgram = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glUseProgram(GLuint program) {\n    _pre_call_gl_callback(\"glUseProgram\", (GLADapiproc) glad_glUseProgram, 1, program);\n    glad_glUseProgram(program);\n    _post_call_gl_callback(NULL, \"glUseProgram\", (GLADapiproc) glad_glUseProgram, 1, program);\n}\nPFNGLUSEPROGRAMPROC glad_debug_glUseProgram = glad_debug_impl_glUseProgram;\nPFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glValidateProgram(GLuint program) {\n    _pre_call_gl_callback(\"glValidateProgram\", (GLADapiproc) glad_glValidateProgram, 1, program);\n    glad_glValidateProgram(program);\n    _post_call_gl_callback(NULL, \"glValidateProgram\", (GLADapiproc) glad_glValidateProgram, 1, program);\n}\nPFNGLVALIDATEPROGRAMPROC glad_debug_glValidateProgram = glad_debug_impl_glValidateProgram;\nPFNGLVERTEX2DPROC glad_glVertex2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex2d(GLdouble x, GLdouble y) {\n    _pre_call_gl_callback(\"glVertex2d\", (GLADapiproc) glad_glVertex2d, 2, x, y);\n    glad_glVertex2d(x, y);\n    _post_call_gl_callback(NULL, \"glVertex2d\", (GLADapiproc) glad_glVertex2d, 2, x, y);\n}\nPFNGLVERTEX2DPROC glad_debug_glVertex2d = glad_debug_impl_glVertex2d;\nPFNGLVERTEX2DVPROC glad_glVertex2dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex2dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glVertex2dv\", (GLADapiproc) glad_glVertex2dv, 1, v);\n    glad_glVertex2dv(v);\n    _post_call_gl_callback(NULL, \"glVertex2dv\", (GLADapiproc) glad_glVertex2dv, 1, v);\n}\nPFNGLVERTEX2DVPROC glad_debug_glVertex2dv = glad_debug_impl_glVertex2dv;\nPFNGLVERTEX2FPROC glad_glVertex2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex2f(GLfloat x, GLfloat y) {\n    _pre_call_gl_callback(\"glVertex2f\", (GLADapiproc) glad_glVertex2f, 2, x, y);\n    glad_glVertex2f(x, y);\n    _post_call_gl_callback(NULL, \"glVertex2f\", (GLADapiproc) glad_glVertex2f, 2, x, y);\n}\nPFNGLVERTEX2FPROC glad_debug_glVertex2f = glad_debug_impl_glVertex2f;\nPFNGLVERTEX2FVPROC glad_glVertex2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex2fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glVertex2fv\", (GLADapiproc) glad_glVertex2fv, 1, v);\n    glad_glVertex2fv(v);\n    _post_call_gl_callback(NULL, \"glVertex2fv\", (GLADapiproc) glad_glVertex2fv, 1, v);\n}\nPFNGLVERTEX2FVPROC glad_debug_glVertex2fv = glad_debug_impl_glVertex2fv;\nPFNGLVERTEX2IPROC glad_glVertex2i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex2i(GLint x, GLint y) {\n    _pre_call_gl_callback(\"glVertex2i\", (GLADapiproc) glad_glVertex2i, 2, x, y);\n    glad_glVertex2i(x, y);\n    _post_call_gl_callback(NULL, \"glVertex2i\", (GLADapiproc) glad_glVertex2i, 2, x, y);\n}\nPFNGLVERTEX2IPROC glad_debug_glVertex2i = glad_debug_impl_glVertex2i;\nPFNGLVERTEX2IVPROC glad_glVertex2iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex2iv(const GLint * v) {\n    _pre_call_gl_callback(\"glVertex2iv\", (GLADapiproc) glad_glVertex2iv, 1, v);\n    glad_glVertex2iv(v);\n    _post_call_gl_callback(NULL, \"glVertex2iv\", (GLADapiproc) glad_glVertex2iv, 1, v);\n}\nPFNGLVERTEX2IVPROC glad_debug_glVertex2iv = glad_debug_impl_glVertex2iv;\nPFNGLVERTEX2SPROC glad_glVertex2s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex2s(GLshort x, GLshort y) {\n    _pre_call_gl_callback(\"glVertex2s\", (GLADapiproc) glad_glVertex2s, 2, x, y);\n    glad_glVertex2s(x, y);\n    _post_call_gl_callback(NULL, \"glVertex2s\", (GLADapiproc) glad_glVertex2s, 2, x, y);\n}\nPFNGLVERTEX2SPROC glad_debug_glVertex2s = glad_debug_impl_glVertex2s;\nPFNGLVERTEX2SVPROC glad_glVertex2sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex2sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glVertex2sv\", (GLADapiproc) glad_glVertex2sv, 1, v);\n    glad_glVertex2sv(v);\n    _post_call_gl_callback(NULL, \"glVertex2sv\", (GLADapiproc) glad_glVertex2sv, 1, v);\n}\nPFNGLVERTEX2SVPROC glad_debug_glVertex2sv = glad_debug_impl_glVertex2sv;\nPFNGLVERTEX3DPROC glad_glVertex3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex3d(GLdouble x, GLdouble y, GLdouble z) {\n    _pre_call_gl_callback(\"glVertex3d\", (GLADapiproc) glad_glVertex3d, 3, x, y, z);\n    glad_glVertex3d(x, y, z);\n    _post_call_gl_callback(NULL, \"glVertex3d\", (GLADapiproc) glad_glVertex3d, 3, x, y, z);\n}\nPFNGLVERTEX3DPROC glad_debug_glVertex3d = glad_debug_impl_glVertex3d;\nPFNGLVERTEX3DVPROC glad_glVertex3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex3dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glVertex3dv\", (GLADapiproc) glad_glVertex3dv, 1, v);\n    glad_glVertex3dv(v);\n    _post_call_gl_callback(NULL, \"glVertex3dv\", (GLADapiproc) glad_glVertex3dv, 1, v);\n}\nPFNGLVERTEX3DVPROC glad_debug_glVertex3dv = glad_debug_impl_glVertex3dv;\nPFNGLVERTEX3FPROC glad_glVertex3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex3f(GLfloat x, GLfloat y, GLfloat z) {\n    _pre_call_gl_callback(\"glVertex3f\", (GLADapiproc) glad_glVertex3f, 3, x, y, z);\n    glad_glVertex3f(x, y, z);\n    _post_call_gl_callback(NULL, \"glVertex3f\", (GLADapiproc) glad_glVertex3f, 3, x, y, z);\n}\nPFNGLVERTEX3FPROC glad_debug_glVertex3f = glad_debug_impl_glVertex3f;\nPFNGLVERTEX3FVPROC glad_glVertex3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex3fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glVertex3fv\", (GLADapiproc) glad_glVertex3fv, 1, v);\n    glad_glVertex3fv(v);\n    _post_call_gl_callback(NULL, \"glVertex3fv\", (GLADapiproc) glad_glVertex3fv, 1, v);\n}\nPFNGLVERTEX3FVPROC glad_debug_glVertex3fv = glad_debug_impl_glVertex3fv;\nPFNGLVERTEX3IPROC glad_glVertex3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex3i(GLint x, GLint y, GLint z) {\n    _pre_call_gl_callback(\"glVertex3i\", (GLADapiproc) glad_glVertex3i, 3, x, y, z);\n    glad_glVertex3i(x, y, z);\n    _post_call_gl_callback(NULL, \"glVertex3i\", (GLADapiproc) glad_glVertex3i, 3, x, y, z);\n}\nPFNGLVERTEX3IPROC glad_debug_glVertex3i = glad_debug_impl_glVertex3i;\nPFNGLVERTEX3IVPROC glad_glVertex3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex3iv(const GLint * v) {\n    _pre_call_gl_callback(\"glVertex3iv\", (GLADapiproc) glad_glVertex3iv, 1, v);\n    glad_glVertex3iv(v);\n    _post_call_gl_callback(NULL, \"glVertex3iv\", (GLADapiproc) glad_glVertex3iv, 1, v);\n}\nPFNGLVERTEX3IVPROC glad_debug_glVertex3iv = glad_debug_impl_glVertex3iv;\nPFNGLVERTEX3SPROC glad_glVertex3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex3s(GLshort x, GLshort y, GLshort z) {\n    _pre_call_gl_callback(\"glVertex3s\", (GLADapiproc) glad_glVertex3s, 3, x, y, z);\n    glad_glVertex3s(x, y, z);\n    _post_call_gl_callback(NULL, \"glVertex3s\", (GLADapiproc) glad_glVertex3s, 3, x, y, z);\n}\nPFNGLVERTEX3SPROC glad_debug_glVertex3s = glad_debug_impl_glVertex3s;\nPFNGLVERTEX3SVPROC glad_glVertex3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex3sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glVertex3sv\", (GLADapiproc) glad_glVertex3sv, 1, v);\n    glad_glVertex3sv(v);\n    _post_call_gl_callback(NULL, \"glVertex3sv\", (GLADapiproc) glad_glVertex3sv, 1, v);\n}\nPFNGLVERTEX3SVPROC glad_debug_glVertex3sv = glad_debug_impl_glVertex3sv;\nPFNGLVERTEX4DPROC glad_glVertex4d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex4d(GLdouble x, GLdouble y, GLdouble z, GLdouble w) {\n    _pre_call_gl_callback(\"glVertex4d\", (GLADapiproc) glad_glVertex4d, 4, x, y, z, w);\n    glad_glVertex4d(x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertex4d\", (GLADapiproc) glad_glVertex4d, 4, x, y, z, w);\n}\nPFNGLVERTEX4DPROC glad_debug_glVertex4d = glad_debug_impl_glVertex4d;\nPFNGLVERTEX4DVPROC glad_glVertex4dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex4dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glVertex4dv\", (GLADapiproc) glad_glVertex4dv, 1, v);\n    glad_glVertex4dv(v);\n    _post_call_gl_callback(NULL, \"glVertex4dv\", (GLADapiproc) glad_glVertex4dv, 1, v);\n}\nPFNGLVERTEX4DVPROC glad_debug_glVertex4dv = glad_debug_impl_glVertex4dv;\nPFNGLVERTEX4FPROC glad_glVertex4f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w) {\n    _pre_call_gl_callback(\"glVertex4f\", (GLADapiproc) glad_glVertex4f, 4, x, y, z, w);\n    glad_glVertex4f(x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertex4f\", (GLADapiproc) glad_glVertex4f, 4, x, y, z, w);\n}\nPFNGLVERTEX4FPROC glad_debug_glVertex4f = glad_debug_impl_glVertex4f;\nPFNGLVERTEX4FVPROC glad_glVertex4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex4fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glVertex4fv\", (GLADapiproc) glad_glVertex4fv, 1, v);\n    glad_glVertex4fv(v);\n    _post_call_gl_callback(NULL, \"glVertex4fv\", (GLADapiproc) glad_glVertex4fv, 1, v);\n}\nPFNGLVERTEX4FVPROC glad_debug_glVertex4fv = glad_debug_impl_glVertex4fv;\nPFNGLVERTEX4IPROC glad_glVertex4i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex4i(GLint x, GLint y, GLint z, GLint w) {\n    _pre_call_gl_callback(\"glVertex4i\", (GLADapiproc) glad_glVertex4i, 4, x, y, z, w);\n    glad_glVertex4i(x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertex4i\", (GLADapiproc) glad_glVertex4i, 4, x, y, z, w);\n}\nPFNGLVERTEX4IPROC glad_debug_glVertex4i = glad_debug_impl_glVertex4i;\nPFNGLVERTEX4IVPROC glad_glVertex4iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex4iv(const GLint * v) {\n    _pre_call_gl_callback(\"glVertex4iv\", (GLADapiproc) glad_glVertex4iv, 1, v);\n    glad_glVertex4iv(v);\n    _post_call_gl_callback(NULL, \"glVertex4iv\", (GLADapiproc) glad_glVertex4iv, 1, v);\n}\nPFNGLVERTEX4IVPROC glad_debug_glVertex4iv = glad_debug_impl_glVertex4iv;\nPFNGLVERTEX4SPROC glad_glVertex4s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex4s(GLshort x, GLshort y, GLshort z, GLshort w) {\n    _pre_call_gl_callback(\"glVertex4s\", (GLADapiproc) glad_glVertex4s, 4, x, y, z, w);\n    glad_glVertex4s(x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertex4s\", (GLADapiproc) glad_glVertex4s, 4, x, y, z, w);\n}\nPFNGLVERTEX4SPROC glad_debug_glVertex4s = glad_debug_impl_glVertex4s;\nPFNGLVERTEX4SVPROC glad_glVertex4sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertex4sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glVertex4sv\", (GLADapiproc) glad_glVertex4sv, 1, v);\n    glad_glVertex4sv(v);\n    _post_call_gl_callback(NULL, \"glVertex4sv\", (GLADapiproc) glad_glVertex4sv, 1, v);\n}\nPFNGLVERTEX4SVPROC glad_debug_glVertex4sv = glad_debug_impl_glVertex4sv;\nPFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib1d(GLuint index, GLdouble x) {\n    _pre_call_gl_callback(\"glVertexAttrib1d\", (GLADapiproc) glad_glVertexAttrib1d, 2, index, x);\n    glad_glVertexAttrib1d(index, x);\n    _post_call_gl_callback(NULL, \"glVertexAttrib1d\", (GLADapiproc) glad_glVertexAttrib1d, 2, index, x);\n}\nPFNGLVERTEXATTRIB1DPROC glad_debug_glVertexAttrib1d = glad_debug_impl_glVertexAttrib1d;\nPFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib1dv(GLuint index, const GLdouble * v) {\n    _pre_call_gl_callback(\"glVertexAttrib1dv\", (GLADapiproc) glad_glVertexAttrib1dv, 2, index, v);\n    glad_glVertexAttrib1dv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib1dv\", (GLADapiproc) glad_glVertexAttrib1dv, 2, index, v);\n}\nPFNGLVERTEXATTRIB1DVPROC glad_debug_glVertexAttrib1dv = glad_debug_impl_glVertexAttrib1dv;\nPFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib1f(GLuint index, GLfloat x) {\n    _pre_call_gl_callback(\"glVertexAttrib1f\", (GLADapiproc) glad_glVertexAttrib1f, 2, index, x);\n    glad_glVertexAttrib1f(index, x);\n    _post_call_gl_callback(NULL, \"glVertexAttrib1f\", (GLADapiproc) glad_glVertexAttrib1f, 2, index, x);\n}\nPFNGLVERTEXATTRIB1FPROC glad_debug_glVertexAttrib1f = glad_debug_impl_glVertexAttrib1f;\nPFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib1fv(GLuint index, const GLfloat * v) {\n    _pre_call_gl_callback(\"glVertexAttrib1fv\", (GLADapiproc) glad_glVertexAttrib1fv, 2, index, v);\n    glad_glVertexAttrib1fv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib1fv\", (GLADapiproc) glad_glVertexAttrib1fv, 2, index, v);\n}\nPFNGLVERTEXATTRIB1FVPROC glad_debug_glVertexAttrib1fv = glad_debug_impl_glVertexAttrib1fv;\nPFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib1s(GLuint index, GLshort x) {\n    _pre_call_gl_callback(\"glVertexAttrib1s\", (GLADapiproc) glad_glVertexAttrib1s, 2, index, x);\n    glad_glVertexAttrib1s(index, x);\n    _post_call_gl_callback(NULL, \"glVertexAttrib1s\", (GLADapiproc) glad_glVertexAttrib1s, 2, index, x);\n}\nPFNGLVERTEXATTRIB1SPROC glad_debug_glVertexAttrib1s = glad_debug_impl_glVertexAttrib1s;\nPFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib1sv(GLuint index, const GLshort * v) {\n    _pre_call_gl_callback(\"glVertexAttrib1sv\", (GLADapiproc) glad_glVertexAttrib1sv, 2, index, v);\n    glad_glVertexAttrib1sv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib1sv\", (GLADapiproc) glad_glVertexAttrib1sv, 2, index, v);\n}\nPFNGLVERTEXATTRIB1SVPROC glad_debug_glVertexAttrib1sv = glad_debug_impl_glVertexAttrib1sv;\nPFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib2d(GLuint index, GLdouble x, GLdouble y) {\n    _pre_call_gl_callback(\"glVertexAttrib2d\", (GLADapiproc) glad_glVertexAttrib2d, 3, index, x, y);\n    glad_glVertexAttrib2d(index, x, y);\n    _post_call_gl_callback(NULL, \"glVertexAttrib2d\", (GLADapiproc) glad_glVertexAttrib2d, 3, index, x, y);\n}\nPFNGLVERTEXATTRIB2DPROC glad_debug_glVertexAttrib2d = glad_debug_impl_glVertexAttrib2d;\nPFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib2dv(GLuint index, const GLdouble * v) {\n    _pre_call_gl_callback(\"glVertexAttrib2dv\", (GLADapiproc) glad_glVertexAttrib2dv, 2, index, v);\n    glad_glVertexAttrib2dv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib2dv\", (GLADapiproc) glad_glVertexAttrib2dv, 2, index, v);\n}\nPFNGLVERTEXATTRIB2DVPROC glad_debug_glVertexAttrib2dv = glad_debug_impl_glVertexAttrib2dv;\nPFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {\n    _pre_call_gl_callback(\"glVertexAttrib2f\", (GLADapiproc) glad_glVertexAttrib2f, 3, index, x, y);\n    glad_glVertexAttrib2f(index, x, y);\n    _post_call_gl_callback(NULL, \"glVertexAttrib2f\", (GLADapiproc) glad_glVertexAttrib2f, 3, index, x, y);\n}\nPFNGLVERTEXATTRIB2FPROC glad_debug_glVertexAttrib2f = glad_debug_impl_glVertexAttrib2f;\nPFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib2fv(GLuint index, const GLfloat * v) {\n    _pre_call_gl_callback(\"glVertexAttrib2fv\", (GLADapiproc) glad_glVertexAttrib2fv, 2, index, v);\n    glad_glVertexAttrib2fv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib2fv\", (GLADapiproc) glad_glVertexAttrib2fv, 2, index, v);\n}\nPFNGLVERTEXATTRIB2FVPROC glad_debug_glVertexAttrib2fv = glad_debug_impl_glVertexAttrib2fv;\nPFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib2s(GLuint index, GLshort x, GLshort y) {\n    _pre_call_gl_callback(\"glVertexAttrib2s\", (GLADapiproc) glad_glVertexAttrib2s, 3, index, x, y);\n    glad_glVertexAttrib2s(index, x, y);\n    _post_call_gl_callback(NULL, \"glVertexAttrib2s\", (GLADapiproc) glad_glVertexAttrib2s, 3, index, x, y);\n}\nPFNGLVERTEXATTRIB2SPROC glad_debug_glVertexAttrib2s = glad_debug_impl_glVertexAttrib2s;\nPFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib2sv(GLuint index, const GLshort * v) {\n    _pre_call_gl_callback(\"glVertexAttrib2sv\", (GLADapiproc) glad_glVertexAttrib2sv, 2, index, v);\n    glad_glVertexAttrib2sv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib2sv\", (GLADapiproc) glad_glVertexAttrib2sv, 2, index, v);\n}\nPFNGLVERTEXATTRIB2SVPROC glad_debug_glVertexAttrib2sv = glad_debug_impl_glVertexAttrib2sv;\nPFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib3d(GLuint index, GLdouble x, GLdouble y, GLdouble z) {\n    _pre_call_gl_callback(\"glVertexAttrib3d\", (GLADapiproc) glad_glVertexAttrib3d, 4, index, x, y, z);\n    glad_glVertexAttrib3d(index, x, y, z);\n    _post_call_gl_callback(NULL, \"glVertexAttrib3d\", (GLADapiproc) glad_glVertexAttrib3d, 4, index, x, y, z);\n}\nPFNGLVERTEXATTRIB3DPROC glad_debug_glVertexAttrib3d = glad_debug_impl_glVertexAttrib3d;\nPFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib3dv(GLuint index, const GLdouble * v) {\n    _pre_call_gl_callback(\"glVertexAttrib3dv\", (GLADapiproc) glad_glVertexAttrib3dv, 2, index, v);\n    glad_glVertexAttrib3dv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib3dv\", (GLADapiproc) glad_glVertexAttrib3dv, 2, index, v);\n}\nPFNGLVERTEXATTRIB3DVPROC glad_debug_glVertexAttrib3dv = glad_debug_impl_glVertexAttrib3dv;\nPFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {\n    _pre_call_gl_callback(\"glVertexAttrib3f\", (GLADapiproc) glad_glVertexAttrib3f, 4, index, x, y, z);\n    glad_glVertexAttrib3f(index, x, y, z);\n    _post_call_gl_callback(NULL, \"glVertexAttrib3f\", (GLADapiproc) glad_glVertexAttrib3f, 4, index, x, y, z);\n}\nPFNGLVERTEXATTRIB3FPROC glad_debug_glVertexAttrib3f = glad_debug_impl_glVertexAttrib3f;\nPFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib3fv(GLuint index, const GLfloat * v) {\n    _pre_call_gl_callback(\"glVertexAttrib3fv\", (GLADapiproc) glad_glVertexAttrib3fv, 2, index, v);\n    glad_glVertexAttrib3fv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib3fv\", (GLADapiproc) glad_glVertexAttrib3fv, 2, index, v);\n}\nPFNGLVERTEXATTRIB3FVPROC glad_debug_glVertexAttrib3fv = glad_debug_impl_glVertexAttrib3fv;\nPFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib3s(GLuint index, GLshort x, GLshort y, GLshort z) {\n    _pre_call_gl_callback(\"glVertexAttrib3s\", (GLADapiproc) glad_glVertexAttrib3s, 4, index, x, y, z);\n    glad_glVertexAttrib3s(index, x, y, z);\n    _post_call_gl_callback(NULL, \"glVertexAttrib3s\", (GLADapiproc) glad_glVertexAttrib3s, 4, index, x, y, z);\n}\nPFNGLVERTEXATTRIB3SPROC glad_debug_glVertexAttrib3s = glad_debug_impl_glVertexAttrib3s;\nPFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib3sv(GLuint index, const GLshort * v) {\n    _pre_call_gl_callback(\"glVertexAttrib3sv\", (GLADapiproc) glad_glVertexAttrib3sv, 2, index, v);\n    glad_glVertexAttrib3sv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib3sv\", (GLADapiproc) glad_glVertexAttrib3sv, 2, index, v);\n}\nPFNGLVERTEXATTRIB3SVPROC glad_debug_glVertexAttrib3sv = glad_debug_impl_glVertexAttrib3sv;\nPFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4Nbv(GLuint index, const GLbyte * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4Nbv\", (GLADapiproc) glad_glVertexAttrib4Nbv, 2, index, v);\n    glad_glVertexAttrib4Nbv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4Nbv\", (GLADapiproc) glad_glVertexAttrib4Nbv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4NBVPROC glad_debug_glVertexAttrib4Nbv = glad_debug_impl_glVertexAttrib4Nbv;\nPFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4Niv(GLuint index, const GLint * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4Niv\", (GLADapiproc) glad_glVertexAttrib4Niv, 2, index, v);\n    glad_glVertexAttrib4Niv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4Niv\", (GLADapiproc) glad_glVertexAttrib4Niv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4NIVPROC glad_debug_glVertexAttrib4Niv = glad_debug_impl_glVertexAttrib4Niv;\nPFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4Nsv(GLuint index, const GLshort * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4Nsv\", (GLADapiproc) glad_glVertexAttrib4Nsv, 2, index, v);\n    glad_glVertexAttrib4Nsv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4Nsv\", (GLADapiproc) glad_glVertexAttrib4Nsv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4NSVPROC glad_debug_glVertexAttrib4Nsv = glad_debug_impl_glVertexAttrib4Nsv;\nPFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4Nub(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w) {\n    _pre_call_gl_callback(\"glVertexAttrib4Nub\", (GLADapiproc) glad_glVertexAttrib4Nub, 5, index, x, y, z, w);\n    glad_glVertexAttrib4Nub(index, x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4Nub\", (GLADapiproc) glad_glVertexAttrib4Nub, 5, index, x, y, z, w);\n}\nPFNGLVERTEXATTRIB4NUBPROC glad_debug_glVertexAttrib4Nub = glad_debug_impl_glVertexAttrib4Nub;\nPFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4Nubv(GLuint index, const GLubyte * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4Nubv\", (GLADapiproc) glad_glVertexAttrib4Nubv, 2, index, v);\n    glad_glVertexAttrib4Nubv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4Nubv\", (GLADapiproc) glad_glVertexAttrib4Nubv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4NUBVPROC glad_debug_glVertexAttrib4Nubv = glad_debug_impl_glVertexAttrib4Nubv;\nPFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4Nuiv(GLuint index, const GLuint * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4Nuiv\", (GLADapiproc) glad_glVertexAttrib4Nuiv, 2, index, v);\n    glad_glVertexAttrib4Nuiv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4Nuiv\", (GLADapiproc) glad_glVertexAttrib4Nuiv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4NUIVPROC glad_debug_glVertexAttrib4Nuiv = glad_debug_impl_glVertexAttrib4Nuiv;\nPFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4Nusv(GLuint index, const GLushort * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4Nusv\", (GLADapiproc) glad_glVertexAttrib4Nusv, 2, index, v);\n    glad_glVertexAttrib4Nusv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4Nusv\", (GLADapiproc) glad_glVertexAttrib4Nusv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4NUSVPROC glad_debug_glVertexAttrib4Nusv = glad_debug_impl_glVertexAttrib4Nusv;\nPFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4bv(GLuint index, const GLbyte * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4bv\", (GLADapiproc) glad_glVertexAttrib4bv, 2, index, v);\n    glad_glVertexAttrib4bv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4bv\", (GLADapiproc) glad_glVertexAttrib4bv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4BVPROC glad_debug_glVertexAttrib4bv = glad_debug_impl_glVertexAttrib4bv;\nPFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4d(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w) {\n    _pre_call_gl_callback(\"glVertexAttrib4d\", (GLADapiproc) glad_glVertexAttrib4d, 5, index, x, y, z, w);\n    glad_glVertexAttrib4d(index, x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4d\", (GLADapiproc) glad_glVertexAttrib4d, 5, index, x, y, z, w);\n}\nPFNGLVERTEXATTRIB4DPROC glad_debug_glVertexAttrib4d = glad_debug_impl_glVertexAttrib4d;\nPFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4dv(GLuint index, const GLdouble * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4dv\", (GLADapiproc) glad_glVertexAttrib4dv, 2, index, v);\n    glad_glVertexAttrib4dv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4dv\", (GLADapiproc) glad_glVertexAttrib4dv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4DVPROC glad_debug_glVertexAttrib4dv = glad_debug_impl_glVertexAttrib4dv;\nPFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {\n    _pre_call_gl_callback(\"glVertexAttrib4f\", (GLADapiproc) glad_glVertexAttrib4f, 5, index, x, y, z, w);\n    glad_glVertexAttrib4f(index, x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4f\", (GLADapiproc) glad_glVertexAttrib4f, 5, index, x, y, z, w);\n}\nPFNGLVERTEXATTRIB4FPROC glad_debug_glVertexAttrib4f = glad_debug_impl_glVertexAttrib4f;\nPFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4fv(GLuint index, const GLfloat * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4fv\", (GLADapiproc) glad_glVertexAttrib4fv, 2, index, v);\n    glad_glVertexAttrib4fv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4fv\", (GLADapiproc) glad_glVertexAttrib4fv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4FVPROC glad_debug_glVertexAttrib4fv = glad_debug_impl_glVertexAttrib4fv;\nPFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4iv(GLuint index, const GLint * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4iv\", (GLADapiproc) glad_glVertexAttrib4iv, 2, index, v);\n    glad_glVertexAttrib4iv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4iv\", (GLADapiproc) glad_glVertexAttrib4iv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4IVPROC glad_debug_glVertexAttrib4iv = glad_debug_impl_glVertexAttrib4iv;\nPFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4s(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w) {\n    _pre_call_gl_callback(\"glVertexAttrib4s\", (GLADapiproc) glad_glVertexAttrib4s, 5, index, x, y, z, w);\n    glad_glVertexAttrib4s(index, x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4s\", (GLADapiproc) glad_glVertexAttrib4s, 5, index, x, y, z, w);\n}\nPFNGLVERTEXATTRIB4SPROC glad_debug_glVertexAttrib4s = glad_debug_impl_glVertexAttrib4s;\nPFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4sv(GLuint index, const GLshort * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4sv\", (GLADapiproc) glad_glVertexAttrib4sv, 2, index, v);\n    glad_glVertexAttrib4sv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4sv\", (GLADapiproc) glad_glVertexAttrib4sv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4SVPROC glad_debug_glVertexAttrib4sv = glad_debug_impl_glVertexAttrib4sv;\nPFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4ubv(GLuint index, const GLubyte * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4ubv\", (GLADapiproc) glad_glVertexAttrib4ubv, 2, index, v);\n    glad_glVertexAttrib4ubv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4ubv\", (GLADapiproc) glad_glVertexAttrib4ubv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4UBVPROC glad_debug_glVertexAttrib4ubv = glad_debug_impl_glVertexAttrib4ubv;\nPFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4uiv(GLuint index, const GLuint * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4uiv\", (GLADapiproc) glad_glVertexAttrib4uiv, 2, index, v);\n    glad_glVertexAttrib4uiv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4uiv\", (GLADapiproc) glad_glVertexAttrib4uiv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4UIVPROC glad_debug_glVertexAttrib4uiv = glad_debug_impl_glVertexAttrib4uiv;\nPFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttrib4usv(GLuint index, const GLushort * v) {\n    _pre_call_gl_callback(\"glVertexAttrib4usv\", (GLADapiproc) glad_glVertexAttrib4usv, 2, index, v);\n    glad_glVertexAttrib4usv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttrib4usv\", (GLADapiproc) glad_glVertexAttrib4usv, 2, index, v);\n}\nPFNGLVERTEXATTRIB4USVPROC glad_debug_glVertexAttrib4usv = glad_debug_impl_glVertexAttrib4usv;\nPFNGLVERTEXATTRIBDIVISORARBPROC glad_glVertexAttribDivisorARB = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribDivisorARB(GLuint index, GLuint divisor) {\n    _pre_call_gl_callback(\"glVertexAttribDivisorARB\", (GLADapiproc) glad_glVertexAttribDivisorARB, 2, index, divisor);\n    glad_glVertexAttribDivisorARB(index, divisor);\n    _post_call_gl_callback(NULL, \"glVertexAttribDivisorARB\", (GLADapiproc) glad_glVertexAttribDivisorARB, 2, index, divisor);\n}\nPFNGLVERTEXATTRIBDIVISORARBPROC glad_debug_glVertexAttribDivisorARB = glad_debug_impl_glVertexAttribDivisorARB;\nPFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI1i(GLuint index, GLint x) {\n    _pre_call_gl_callback(\"glVertexAttribI1i\", (GLADapiproc) glad_glVertexAttribI1i, 2, index, x);\n    glad_glVertexAttribI1i(index, x);\n    _post_call_gl_callback(NULL, \"glVertexAttribI1i\", (GLADapiproc) glad_glVertexAttribI1i, 2, index, x);\n}\nPFNGLVERTEXATTRIBI1IPROC glad_debug_glVertexAttribI1i = glad_debug_impl_glVertexAttribI1i;\nPFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI1iv(GLuint index, const GLint * v) {\n    _pre_call_gl_callback(\"glVertexAttribI1iv\", (GLADapiproc) glad_glVertexAttribI1iv, 2, index, v);\n    glad_glVertexAttribI1iv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI1iv\", (GLADapiproc) glad_glVertexAttribI1iv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI1IVPROC glad_debug_glVertexAttribI1iv = glad_debug_impl_glVertexAttribI1iv;\nPFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI1ui(GLuint index, GLuint x) {\n    _pre_call_gl_callback(\"glVertexAttribI1ui\", (GLADapiproc) glad_glVertexAttribI1ui, 2, index, x);\n    glad_glVertexAttribI1ui(index, x);\n    _post_call_gl_callback(NULL, \"glVertexAttribI1ui\", (GLADapiproc) glad_glVertexAttribI1ui, 2, index, x);\n}\nPFNGLVERTEXATTRIBI1UIPROC glad_debug_glVertexAttribI1ui = glad_debug_impl_glVertexAttribI1ui;\nPFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI1uiv(GLuint index, const GLuint * v) {\n    _pre_call_gl_callback(\"glVertexAttribI1uiv\", (GLADapiproc) glad_glVertexAttribI1uiv, 2, index, v);\n    glad_glVertexAttribI1uiv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI1uiv\", (GLADapiproc) glad_glVertexAttribI1uiv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI1UIVPROC glad_debug_glVertexAttribI1uiv = glad_debug_impl_glVertexAttribI1uiv;\nPFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI2i(GLuint index, GLint x, GLint y) {\n    _pre_call_gl_callback(\"glVertexAttribI2i\", (GLADapiproc) glad_glVertexAttribI2i, 3, index, x, y);\n    glad_glVertexAttribI2i(index, x, y);\n    _post_call_gl_callback(NULL, \"glVertexAttribI2i\", (GLADapiproc) glad_glVertexAttribI2i, 3, index, x, y);\n}\nPFNGLVERTEXATTRIBI2IPROC glad_debug_glVertexAttribI2i = glad_debug_impl_glVertexAttribI2i;\nPFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI2iv(GLuint index, const GLint * v) {\n    _pre_call_gl_callback(\"glVertexAttribI2iv\", (GLADapiproc) glad_glVertexAttribI2iv, 2, index, v);\n    glad_glVertexAttribI2iv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI2iv\", (GLADapiproc) glad_glVertexAttribI2iv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI2IVPROC glad_debug_glVertexAttribI2iv = glad_debug_impl_glVertexAttribI2iv;\nPFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI2ui(GLuint index, GLuint x, GLuint y) {\n    _pre_call_gl_callback(\"glVertexAttribI2ui\", (GLADapiproc) glad_glVertexAttribI2ui, 3, index, x, y);\n    glad_glVertexAttribI2ui(index, x, y);\n    _post_call_gl_callback(NULL, \"glVertexAttribI2ui\", (GLADapiproc) glad_glVertexAttribI2ui, 3, index, x, y);\n}\nPFNGLVERTEXATTRIBI2UIPROC glad_debug_glVertexAttribI2ui = glad_debug_impl_glVertexAttribI2ui;\nPFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI2uiv(GLuint index, const GLuint * v) {\n    _pre_call_gl_callback(\"glVertexAttribI2uiv\", (GLADapiproc) glad_glVertexAttribI2uiv, 2, index, v);\n    glad_glVertexAttribI2uiv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI2uiv\", (GLADapiproc) glad_glVertexAttribI2uiv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI2UIVPROC glad_debug_glVertexAttribI2uiv = glad_debug_impl_glVertexAttribI2uiv;\nPFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI3i(GLuint index, GLint x, GLint y, GLint z) {\n    _pre_call_gl_callback(\"glVertexAttribI3i\", (GLADapiproc) glad_glVertexAttribI3i, 4, index, x, y, z);\n    glad_glVertexAttribI3i(index, x, y, z);\n    _post_call_gl_callback(NULL, \"glVertexAttribI3i\", (GLADapiproc) glad_glVertexAttribI3i, 4, index, x, y, z);\n}\nPFNGLVERTEXATTRIBI3IPROC glad_debug_glVertexAttribI3i = glad_debug_impl_glVertexAttribI3i;\nPFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI3iv(GLuint index, const GLint * v) {\n    _pre_call_gl_callback(\"glVertexAttribI3iv\", (GLADapiproc) glad_glVertexAttribI3iv, 2, index, v);\n    glad_glVertexAttribI3iv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI3iv\", (GLADapiproc) glad_glVertexAttribI3iv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI3IVPROC glad_debug_glVertexAttribI3iv = glad_debug_impl_glVertexAttribI3iv;\nPFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI3ui(GLuint index, GLuint x, GLuint y, GLuint z) {\n    _pre_call_gl_callback(\"glVertexAttribI3ui\", (GLADapiproc) glad_glVertexAttribI3ui, 4, index, x, y, z);\n    glad_glVertexAttribI3ui(index, x, y, z);\n    _post_call_gl_callback(NULL, \"glVertexAttribI3ui\", (GLADapiproc) glad_glVertexAttribI3ui, 4, index, x, y, z);\n}\nPFNGLVERTEXATTRIBI3UIPROC glad_debug_glVertexAttribI3ui = glad_debug_impl_glVertexAttribI3ui;\nPFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI3uiv(GLuint index, const GLuint * v) {\n    _pre_call_gl_callback(\"glVertexAttribI3uiv\", (GLADapiproc) glad_glVertexAttribI3uiv, 2, index, v);\n    glad_glVertexAttribI3uiv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI3uiv\", (GLADapiproc) glad_glVertexAttribI3uiv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI3UIVPROC glad_debug_glVertexAttribI3uiv = glad_debug_impl_glVertexAttribI3uiv;\nPFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI4bv(GLuint index, const GLbyte * v) {\n    _pre_call_gl_callback(\"glVertexAttribI4bv\", (GLADapiproc) glad_glVertexAttribI4bv, 2, index, v);\n    glad_glVertexAttribI4bv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI4bv\", (GLADapiproc) glad_glVertexAttribI4bv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI4BVPROC glad_debug_glVertexAttribI4bv = glad_debug_impl_glVertexAttribI4bv;\nPFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w) {\n    _pre_call_gl_callback(\"glVertexAttribI4i\", (GLADapiproc) glad_glVertexAttribI4i, 5, index, x, y, z, w);\n    glad_glVertexAttribI4i(index, x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertexAttribI4i\", (GLADapiproc) glad_glVertexAttribI4i, 5, index, x, y, z, w);\n}\nPFNGLVERTEXATTRIBI4IPROC glad_debug_glVertexAttribI4i = glad_debug_impl_glVertexAttribI4i;\nPFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI4iv(GLuint index, const GLint * v) {\n    _pre_call_gl_callback(\"glVertexAttribI4iv\", (GLADapiproc) glad_glVertexAttribI4iv, 2, index, v);\n    glad_glVertexAttribI4iv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI4iv\", (GLADapiproc) glad_glVertexAttribI4iv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI4IVPROC glad_debug_glVertexAttribI4iv = glad_debug_impl_glVertexAttribI4iv;\nPFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI4sv(GLuint index, const GLshort * v) {\n    _pre_call_gl_callback(\"glVertexAttribI4sv\", (GLADapiproc) glad_glVertexAttribI4sv, 2, index, v);\n    glad_glVertexAttribI4sv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI4sv\", (GLADapiproc) glad_glVertexAttribI4sv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI4SVPROC glad_debug_glVertexAttribI4sv = glad_debug_impl_glVertexAttribI4sv;\nPFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI4ubv(GLuint index, const GLubyte * v) {\n    _pre_call_gl_callback(\"glVertexAttribI4ubv\", (GLADapiproc) glad_glVertexAttribI4ubv, 2, index, v);\n    glad_glVertexAttribI4ubv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI4ubv\", (GLADapiproc) glad_glVertexAttribI4ubv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI4UBVPROC glad_debug_glVertexAttribI4ubv = glad_debug_impl_glVertexAttribI4ubv;\nPFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) {\n    _pre_call_gl_callback(\"glVertexAttribI4ui\", (GLADapiproc) glad_glVertexAttribI4ui, 5, index, x, y, z, w);\n    glad_glVertexAttribI4ui(index, x, y, z, w);\n    _post_call_gl_callback(NULL, \"glVertexAttribI4ui\", (GLADapiproc) glad_glVertexAttribI4ui, 5, index, x, y, z, w);\n}\nPFNGLVERTEXATTRIBI4UIPROC glad_debug_glVertexAttribI4ui = glad_debug_impl_glVertexAttribI4ui;\nPFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI4uiv(GLuint index, const GLuint * v) {\n    _pre_call_gl_callback(\"glVertexAttribI4uiv\", (GLADapiproc) glad_glVertexAttribI4uiv, 2, index, v);\n    glad_glVertexAttribI4uiv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI4uiv\", (GLADapiproc) glad_glVertexAttribI4uiv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI4UIVPROC glad_debug_glVertexAttribI4uiv = glad_debug_impl_glVertexAttribI4uiv;\nPFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribI4usv(GLuint index, const GLushort * v) {\n    _pre_call_gl_callback(\"glVertexAttribI4usv\", (GLADapiproc) glad_glVertexAttribI4usv, 2, index, v);\n    glad_glVertexAttribI4usv(index, v);\n    _post_call_gl_callback(NULL, \"glVertexAttribI4usv\", (GLADapiproc) glad_glVertexAttribI4usv, 2, index, v);\n}\nPFNGLVERTEXATTRIBI4USVPROC glad_debug_glVertexAttribI4usv = glad_debug_impl_glVertexAttribI4usv;\nPFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glVertexAttribIPointer\", (GLADapiproc) glad_glVertexAttribIPointer, 5, index, size, type, stride, pointer);\n    glad_glVertexAttribIPointer(index, size, type, stride, pointer);\n    _post_call_gl_callback(NULL, \"glVertexAttribIPointer\", (GLADapiproc) glad_glVertexAttribIPointer, 5, index, size, type, stride, pointer);\n}\nPFNGLVERTEXATTRIBIPOINTERPROC glad_debug_glVertexAttribIPointer = glad_debug_impl_glVertexAttribIPointer;\nPFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glVertexAttribPointer\", (GLADapiproc) glad_glVertexAttribPointer, 6, index, size, type, normalized, stride, pointer);\n    glad_glVertexAttribPointer(index, size, type, normalized, stride, pointer);\n    _post_call_gl_callback(NULL, \"glVertexAttribPointer\", (GLADapiproc) glad_glVertexAttribPointer, 6, index, size, type, normalized, stride, pointer);\n}\nPFNGLVERTEXATTRIBPOINTERPROC glad_debug_glVertexAttribPointer = glad_debug_impl_glVertexAttribPointer;\nPFNGLVERTEXPOINTERPROC glad_glVertexPointer = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glVertexPointer(GLint size, GLenum type, GLsizei stride, const void * pointer) {\n    _pre_call_gl_callback(\"glVertexPointer\", (GLADapiproc) glad_glVertexPointer, 4, size, type, stride, pointer);\n    glad_glVertexPointer(size, type, stride, pointer);\n    _post_call_gl_callback(NULL, \"glVertexPointer\", (GLADapiproc) glad_glVertexPointer, 4, size, type, stride, pointer);\n}\nPFNGLVERTEXPOINTERPROC glad_debug_glVertexPointer = glad_debug_impl_glVertexPointer;\nPFNGLVIEWPORTPROC glad_glViewport = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {\n    _pre_call_gl_callback(\"glViewport\", (GLADapiproc) glad_glViewport, 4, x, y, width, height);\n    glad_glViewport(x, y, width, height);\n    _post_call_gl_callback(NULL, \"glViewport\", (GLADapiproc) glad_glViewport, 4, x, y, width, height);\n}\nPFNGLVIEWPORTPROC glad_debug_glViewport = glad_debug_impl_glViewport;\nPFNGLWINDOWPOS2DPROC glad_glWindowPos2d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos2d(GLdouble x, GLdouble y) {\n    _pre_call_gl_callback(\"glWindowPos2d\", (GLADapiproc) glad_glWindowPos2d, 2, x, y);\n    glad_glWindowPos2d(x, y);\n    _post_call_gl_callback(NULL, \"glWindowPos2d\", (GLADapiproc) glad_glWindowPos2d, 2, x, y);\n}\nPFNGLWINDOWPOS2DPROC glad_debug_glWindowPos2d = glad_debug_impl_glWindowPos2d;\nPFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos2dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glWindowPos2dv\", (GLADapiproc) glad_glWindowPos2dv, 1, v);\n    glad_glWindowPos2dv(v);\n    _post_call_gl_callback(NULL, \"glWindowPos2dv\", (GLADapiproc) glad_glWindowPos2dv, 1, v);\n}\nPFNGLWINDOWPOS2DVPROC glad_debug_glWindowPos2dv = glad_debug_impl_glWindowPos2dv;\nPFNGLWINDOWPOS2FPROC glad_glWindowPos2f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos2f(GLfloat x, GLfloat y) {\n    _pre_call_gl_callback(\"glWindowPos2f\", (GLADapiproc) glad_glWindowPos2f, 2, x, y);\n    glad_glWindowPos2f(x, y);\n    _post_call_gl_callback(NULL, \"glWindowPos2f\", (GLADapiproc) glad_glWindowPos2f, 2, x, y);\n}\nPFNGLWINDOWPOS2FPROC glad_debug_glWindowPos2f = glad_debug_impl_glWindowPos2f;\nPFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos2fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glWindowPos2fv\", (GLADapiproc) glad_glWindowPos2fv, 1, v);\n    glad_glWindowPos2fv(v);\n    _post_call_gl_callback(NULL, \"glWindowPos2fv\", (GLADapiproc) glad_glWindowPos2fv, 1, v);\n}\nPFNGLWINDOWPOS2FVPROC glad_debug_glWindowPos2fv = glad_debug_impl_glWindowPos2fv;\nPFNGLWINDOWPOS2IPROC glad_glWindowPos2i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos2i(GLint x, GLint y) {\n    _pre_call_gl_callback(\"glWindowPos2i\", (GLADapiproc) glad_glWindowPos2i, 2, x, y);\n    glad_glWindowPos2i(x, y);\n    _post_call_gl_callback(NULL, \"glWindowPos2i\", (GLADapiproc) glad_glWindowPos2i, 2, x, y);\n}\nPFNGLWINDOWPOS2IPROC glad_debug_glWindowPos2i = glad_debug_impl_glWindowPos2i;\nPFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos2iv(const GLint * v) {\n    _pre_call_gl_callback(\"glWindowPos2iv\", (GLADapiproc) glad_glWindowPos2iv, 1, v);\n    glad_glWindowPos2iv(v);\n    _post_call_gl_callback(NULL, \"glWindowPos2iv\", (GLADapiproc) glad_glWindowPos2iv, 1, v);\n}\nPFNGLWINDOWPOS2IVPROC glad_debug_glWindowPos2iv = glad_debug_impl_glWindowPos2iv;\nPFNGLWINDOWPOS2SPROC glad_glWindowPos2s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos2s(GLshort x, GLshort y) {\n    _pre_call_gl_callback(\"glWindowPos2s\", (GLADapiproc) glad_glWindowPos2s, 2, x, y);\n    glad_glWindowPos2s(x, y);\n    _post_call_gl_callback(NULL, \"glWindowPos2s\", (GLADapiproc) glad_glWindowPos2s, 2, x, y);\n}\nPFNGLWINDOWPOS2SPROC glad_debug_glWindowPos2s = glad_debug_impl_glWindowPos2s;\nPFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos2sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glWindowPos2sv\", (GLADapiproc) glad_glWindowPos2sv, 1, v);\n    glad_glWindowPos2sv(v);\n    _post_call_gl_callback(NULL, \"glWindowPos2sv\", (GLADapiproc) glad_glWindowPos2sv, 1, v);\n}\nPFNGLWINDOWPOS2SVPROC glad_debug_glWindowPos2sv = glad_debug_impl_glWindowPos2sv;\nPFNGLWINDOWPOS3DPROC glad_glWindowPos3d = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos3d(GLdouble x, GLdouble y, GLdouble z) {\n    _pre_call_gl_callback(\"glWindowPos3d\", (GLADapiproc) glad_glWindowPos3d, 3, x, y, z);\n    glad_glWindowPos3d(x, y, z);\n    _post_call_gl_callback(NULL, \"glWindowPos3d\", (GLADapiproc) glad_glWindowPos3d, 3, x, y, z);\n}\nPFNGLWINDOWPOS3DPROC glad_debug_glWindowPos3d = glad_debug_impl_glWindowPos3d;\nPFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos3dv(const GLdouble * v) {\n    _pre_call_gl_callback(\"glWindowPos3dv\", (GLADapiproc) glad_glWindowPos3dv, 1, v);\n    glad_glWindowPos3dv(v);\n    _post_call_gl_callback(NULL, \"glWindowPos3dv\", (GLADapiproc) glad_glWindowPos3dv, 1, v);\n}\nPFNGLWINDOWPOS3DVPROC glad_debug_glWindowPos3dv = glad_debug_impl_glWindowPos3dv;\nPFNGLWINDOWPOS3FPROC glad_glWindowPos3f = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos3f(GLfloat x, GLfloat y, GLfloat z) {\n    _pre_call_gl_callback(\"glWindowPos3f\", (GLADapiproc) glad_glWindowPos3f, 3, x, y, z);\n    glad_glWindowPos3f(x, y, z);\n    _post_call_gl_callback(NULL, \"glWindowPos3f\", (GLADapiproc) glad_glWindowPos3f, 3, x, y, z);\n}\nPFNGLWINDOWPOS3FPROC glad_debug_glWindowPos3f = glad_debug_impl_glWindowPos3f;\nPFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos3fv(const GLfloat * v) {\n    _pre_call_gl_callback(\"glWindowPos3fv\", (GLADapiproc) glad_glWindowPos3fv, 1, v);\n    glad_glWindowPos3fv(v);\n    _post_call_gl_callback(NULL, \"glWindowPos3fv\", (GLADapiproc) glad_glWindowPos3fv, 1, v);\n}\nPFNGLWINDOWPOS3FVPROC glad_debug_glWindowPos3fv = glad_debug_impl_glWindowPos3fv;\nPFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos3i(GLint x, GLint y, GLint z) {\n    _pre_call_gl_callback(\"glWindowPos3i\", (GLADapiproc) glad_glWindowPos3i, 3, x, y, z);\n    glad_glWindowPos3i(x, y, z);\n    _post_call_gl_callback(NULL, \"glWindowPos3i\", (GLADapiproc) glad_glWindowPos3i, 3, x, y, z);\n}\nPFNGLWINDOWPOS3IPROC glad_debug_glWindowPos3i = glad_debug_impl_glWindowPos3i;\nPFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos3iv(const GLint * v) {\n    _pre_call_gl_callback(\"glWindowPos3iv\", (GLADapiproc) glad_glWindowPos3iv, 1, v);\n    glad_glWindowPos3iv(v);\n    _post_call_gl_callback(NULL, \"glWindowPos3iv\", (GLADapiproc) glad_glWindowPos3iv, 1, v);\n}\nPFNGLWINDOWPOS3IVPROC glad_debug_glWindowPos3iv = glad_debug_impl_glWindowPos3iv;\nPFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos3s(GLshort x, GLshort y, GLshort z) {\n    _pre_call_gl_callback(\"glWindowPos3s\", (GLADapiproc) glad_glWindowPos3s, 3, x, y, z);\n    glad_glWindowPos3s(x, y, z);\n    _post_call_gl_callback(NULL, \"glWindowPos3s\", (GLADapiproc) glad_glWindowPos3s, 3, x, y, z);\n}\nPFNGLWINDOWPOS3SPROC glad_debug_glWindowPos3s = glad_debug_impl_glWindowPos3s;\nPFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL;\nstatic void GLAD_API_PTR glad_debug_impl_glWindowPos3sv(const GLshort * v) {\n    _pre_call_gl_callback(\"glWindowPos3sv\", (GLADapiproc) glad_glWindowPos3sv, 1, v);\n    glad_glWindowPos3sv(v);\n    _post_call_gl_callback(NULL, \"glWindowPos3sv\", (GLADapiproc) glad_glWindowPos3sv, 1, v);\n}\nPFNGLWINDOWPOS3SVPROC glad_debug_glWindowPos3sv = glad_debug_impl_glWindowPos3sv;\nstatic void glad_gl_load_GL_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_1_0) return;\n    glad_glAccum = (PFNGLACCUMPROC) load(userptr, \"glAccum\");\n    glad_glAlphaFunc = (PFNGLALPHAFUNCPROC) load(userptr, \"glAlphaFunc\");\n    glad_glBegin = (PFNGLBEGINPROC) load(userptr, \"glBegin\");\n    glad_glBitmap = (PFNGLBITMAPPROC) load(userptr, \"glBitmap\");\n    glad_glBlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, \"glBlendFunc\");\n    glad_glCallList = (PFNGLCALLLISTPROC) load(userptr, \"glCallList\");\n    glad_glCallLists = (PFNGLCALLLISTSPROC) load(userptr, \"glCallLists\");\n    glad_glClear = (PFNGLCLEARPROC) load(userptr, \"glClear\");\n    glad_glClearAccum = (PFNGLCLEARACCUMPROC) load(userptr, \"glClearAccum\");\n    glad_glClearColor = (PFNGLCLEARCOLORPROC) load(userptr, \"glClearColor\");\n    glad_glClearDepth = (PFNGLCLEARDEPTHPROC) load(userptr, \"glClearDepth\");\n    glad_glClearIndex = (PFNGLCLEARINDEXPROC) load(userptr, \"glClearIndex\");\n    glad_glClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, \"glClearStencil\");\n    glad_glClipPlane = (PFNGLCLIPPLANEPROC) load(userptr, \"glClipPlane\");\n    glad_glColor3b = (PFNGLCOLOR3BPROC) load(userptr, \"glColor3b\");\n    glad_glColor3bv = (PFNGLCOLOR3BVPROC) load(userptr, \"glColor3bv\");\n    glad_glColor3d = (PFNGLCOLOR3DPROC) load(userptr, \"glColor3d\");\n    glad_glColor3dv = (PFNGLCOLOR3DVPROC) load(userptr, \"glColor3dv\");\n    glad_glColor3f = (PFNGLCOLOR3FPROC) load(userptr, \"glColor3f\");\n    glad_glColor3fv = (PFNGLCOLOR3FVPROC) load(userptr, \"glColor3fv\");\n    glad_glColor3i = (PFNGLCOLOR3IPROC) load(userptr, \"glColor3i\");\n    glad_glColor3iv = (PFNGLCOLOR3IVPROC) load(userptr, \"glColor3iv\");\n    glad_glColor3s = (PFNGLCOLOR3SPROC) load(userptr, \"glColor3s\");\n    glad_glColor3sv = (PFNGLCOLOR3SVPROC) load(userptr, \"glColor3sv\");\n    glad_glColor3ub = (PFNGLCOLOR3UBPROC) load(userptr, \"glColor3ub\");\n    glad_glColor3ubv = (PFNGLCOLOR3UBVPROC) load(userptr, \"glColor3ubv\");\n    glad_glColor3ui = (PFNGLCOLOR3UIPROC) load(userptr, \"glColor3ui\");\n    glad_glColor3uiv = (PFNGLCOLOR3UIVPROC) load(userptr, \"glColor3uiv\");\n    glad_glColor3us = (PFNGLCOLOR3USPROC) load(userptr, \"glColor3us\");\n    glad_glColor3usv = (PFNGLCOLOR3USVPROC) load(userptr, \"glColor3usv\");\n    glad_glColor4b = (PFNGLCOLOR4BPROC) load(userptr, \"glColor4b\");\n    glad_glColor4bv = (PFNGLCOLOR4BVPROC) load(userptr, \"glColor4bv\");\n    glad_glColor4d = (PFNGLCOLOR4DPROC) load(userptr, \"glColor4d\");\n    glad_glColor4dv = (PFNGLCOLOR4DVPROC) load(userptr, \"glColor4dv\");\n    glad_glColor4f = (PFNGLCOLOR4FPROC) load(userptr, \"glColor4f\");\n    glad_glColor4fv = (PFNGLCOLOR4FVPROC) load(userptr, \"glColor4fv\");\n    glad_glColor4i = (PFNGLCOLOR4IPROC) load(userptr, \"glColor4i\");\n    glad_glColor4iv = (PFNGLCOLOR4IVPROC) load(userptr, \"glColor4iv\");\n    glad_glColor4s = (PFNGLCOLOR4SPROC) load(userptr, \"glColor4s\");\n    glad_glColor4sv = (PFNGLCOLOR4SVPROC) load(userptr, \"glColor4sv\");\n    glad_glColor4ub = (PFNGLCOLOR4UBPROC) load(userptr, \"glColor4ub\");\n    glad_glColor4ubv = (PFNGLCOLOR4UBVPROC) load(userptr, \"glColor4ubv\");\n    glad_glColor4ui = (PFNGLCOLOR4UIPROC) load(userptr, \"glColor4ui\");\n    glad_glColor4uiv = (PFNGLCOLOR4UIVPROC) load(userptr, \"glColor4uiv\");\n    glad_glColor4us = (PFNGLCOLOR4USPROC) load(userptr, \"glColor4us\");\n    glad_glColor4usv = (PFNGLCOLOR4USVPROC) load(userptr, \"glColor4usv\");\n    glad_glColorMask = (PFNGLCOLORMASKPROC) load(userptr, \"glColorMask\");\n    glad_glColorMaterial = (PFNGLCOLORMATERIALPROC) load(userptr, \"glColorMaterial\");\n    glad_glCopyPixels = (PFNGLCOPYPIXELSPROC) load(userptr, \"glCopyPixels\");\n    glad_glCullFace = (PFNGLCULLFACEPROC) load(userptr, \"glCullFace\");\n    glad_glDeleteLists = (PFNGLDELETELISTSPROC) load(userptr, \"glDeleteLists\");\n    glad_glDepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, \"glDepthFunc\");\n    glad_glDepthMask = (PFNGLDEPTHMASKPROC) load(userptr, \"glDepthMask\");\n    glad_glDepthRange = (PFNGLDEPTHRANGEPROC) load(userptr, \"glDepthRange\");\n    glad_glDisable = (PFNGLDISABLEPROC) load(userptr, \"glDisable\");\n    glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC) load(userptr, \"glDrawBuffer\");\n    glad_glDrawPixels = (PFNGLDRAWPIXELSPROC) load(userptr, \"glDrawPixels\");\n    glad_glEdgeFlag = (PFNGLEDGEFLAGPROC) load(userptr, \"glEdgeFlag\");\n    glad_glEdgeFlagv = (PFNGLEDGEFLAGVPROC) load(userptr, \"glEdgeFlagv\");\n    glad_glEnable = (PFNGLENABLEPROC) load(userptr, \"glEnable\");\n    glad_glEnd = (PFNGLENDPROC) load(userptr, \"glEnd\");\n    glad_glEndList = (PFNGLENDLISTPROC) load(userptr, \"glEndList\");\n    glad_glEvalCoord1d = (PFNGLEVALCOORD1DPROC) load(userptr, \"glEvalCoord1d\");\n    glad_glEvalCoord1dv = (PFNGLEVALCOORD1DVPROC) load(userptr, \"glEvalCoord1dv\");\n    glad_glEvalCoord1f = (PFNGLEVALCOORD1FPROC) load(userptr, \"glEvalCoord1f\");\n    glad_glEvalCoord1fv = (PFNGLEVALCOORD1FVPROC) load(userptr, \"glEvalCoord1fv\");\n    glad_glEvalCoord2d = (PFNGLEVALCOORD2DPROC) load(userptr, \"glEvalCoord2d\");\n    glad_glEvalCoord2dv = (PFNGLEVALCOORD2DVPROC) load(userptr, \"glEvalCoord2dv\");\n    glad_glEvalCoord2f = (PFNGLEVALCOORD2FPROC) load(userptr, \"glEvalCoord2f\");\n    glad_glEvalCoord2fv = (PFNGLEVALCOORD2FVPROC) load(userptr, \"glEvalCoord2fv\");\n    glad_glEvalMesh1 = (PFNGLEVALMESH1PROC) load(userptr, \"glEvalMesh1\");\n    glad_glEvalMesh2 = (PFNGLEVALMESH2PROC) load(userptr, \"glEvalMesh2\");\n    glad_glEvalPoint1 = (PFNGLEVALPOINT1PROC) load(userptr, \"glEvalPoint1\");\n    glad_glEvalPoint2 = (PFNGLEVALPOINT2PROC) load(userptr, \"glEvalPoint2\");\n    glad_glFeedbackBuffer = (PFNGLFEEDBACKBUFFERPROC) load(userptr, \"glFeedbackBuffer\");\n    glad_glFinish = (PFNGLFINISHPROC) load(userptr, \"glFinish\");\n    glad_glFlush = (PFNGLFLUSHPROC) load(userptr, \"glFlush\");\n    glad_glFogf = (PFNGLFOGFPROC) load(userptr, \"glFogf\");\n    glad_glFogfv = (PFNGLFOGFVPROC) load(userptr, \"glFogfv\");\n    glad_glFogi = (PFNGLFOGIPROC) load(userptr, \"glFogi\");\n    glad_glFogiv = (PFNGLFOGIVPROC) load(userptr, \"glFogiv\");\n    glad_glFrontFace = (PFNGLFRONTFACEPROC) load(userptr, \"glFrontFace\");\n    glad_glFrustum = (PFNGLFRUSTUMPROC) load(userptr, \"glFrustum\");\n    glad_glGenLists = (PFNGLGENLISTSPROC) load(userptr, \"glGenLists\");\n    glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, \"glGetBooleanv\");\n    glad_glGetClipPlane = (PFNGLGETCLIPPLANEPROC) load(userptr, \"glGetClipPlane\");\n    glad_glGetDoublev = (PFNGLGETDOUBLEVPROC) load(userptr, \"glGetDoublev\");\n    glad_glGetError = (PFNGLGETERRORPROC) load(userptr, \"glGetError\");\n    glad_glGetFloatv = (PFNGLGETFLOATVPROC) load(userptr, \"glGetFloatv\");\n    glad_glGetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, \"glGetIntegerv\");\n    glad_glGetLightfv = (PFNGLGETLIGHTFVPROC) load(userptr, \"glGetLightfv\");\n    glad_glGetLightiv = (PFNGLGETLIGHTIVPROC) load(userptr, \"glGetLightiv\");\n    glad_glGetMapdv = (PFNGLGETMAPDVPROC) load(userptr, \"glGetMapdv\");\n    glad_glGetMapfv = (PFNGLGETMAPFVPROC) load(userptr, \"glGetMapfv\");\n    glad_glGetMapiv = (PFNGLGETMAPIVPROC) load(userptr, \"glGetMapiv\");\n    glad_glGetMaterialfv = (PFNGLGETMATERIALFVPROC) load(userptr, \"glGetMaterialfv\");\n    glad_glGetMaterialiv = (PFNGLGETMATERIALIVPROC) load(userptr, \"glGetMaterialiv\");\n    glad_glGetPixelMapfv = (PFNGLGETPIXELMAPFVPROC) load(userptr, \"glGetPixelMapfv\");\n    glad_glGetPixelMapuiv = (PFNGLGETPIXELMAPUIVPROC) load(userptr, \"glGetPixelMapuiv\");\n    glad_glGetPixelMapusv = (PFNGLGETPIXELMAPUSVPROC) load(userptr, \"glGetPixelMapusv\");\n    glad_glGetPolygonStipple = (PFNGLGETPOLYGONSTIPPLEPROC) load(userptr, \"glGetPolygonStipple\");\n    glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, \"glGetString\");\n    glad_glGetTexEnvfv = (PFNGLGETTEXENVFVPROC) load(userptr, \"glGetTexEnvfv\");\n    glad_glGetTexEnviv = (PFNGLGETTEXENVIVPROC) load(userptr, \"glGetTexEnviv\");\n    glad_glGetTexGendv = (PFNGLGETTEXGENDVPROC) load(userptr, \"glGetTexGendv\");\n    glad_glGetTexGenfv = (PFNGLGETTEXGENFVPROC) load(userptr, \"glGetTexGenfv\");\n    glad_glGetTexGeniv = (PFNGLGETTEXGENIVPROC) load(userptr, \"glGetTexGeniv\");\n    glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC) load(userptr, \"glGetTexImage\");\n    glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC) load(userptr, \"glGetTexLevelParameterfv\");\n    glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC) load(userptr, \"glGetTexLevelParameteriv\");\n    glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, \"glGetTexParameterfv\");\n    glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, \"glGetTexParameteriv\");\n    glad_glHint = (PFNGLHINTPROC) load(userptr, \"glHint\");\n    glad_glIndexMask = (PFNGLINDEXMASKPROC) load(userptr, \"glIndexMask\");\n    glad_glIndexd = (PFNGLINDEXDPROC) load(userptr, \"glIndexd\");\n    glad_glIndexdv = (PFNGLINDEXDVPROC) load(userptr, \"glIndexdv\");\n    glad_glIndexf = (PFNGLINDEXFPROC) load(userptr, \"glIndexf\");\n    glad_glIndexfv = (PFNGLINDEXFVPROC) load(userptr, \"glIndexfv\");\n    glad_glIndexi = (PFNGLINDEXIPROC) load(userptr, \"glIndexi\");\n    glad_glIndexiv = (PFNGLINDEXIVPROC) load(userptr, \"glIndexiv\");\n    glad_glIndexs = (PFNGLINDEXSPROC) load(userptr, \"glIndexs\");\n    glad_glIndexsv = (PFNGLINDEXSVPROC) load(userptr, \"glIndexsv\");\n    glad_glInitNames = (PFNGLINITNAMESPROC) load(userptr, \"glInitNames\");\n    glad_glIsEnabled = (PFNGLISENABLEDPROC) load(userptr, \"glIsEnabled\");\n    glad_glIsList = (PFNGLISLISTPROC) load(userptr, \"glIsList\");\n    glad_glLightModelf = (PFNGLLIGHTMODELFPROC) load(userptr, \"glLightModelf\");\n    glad_glLightModelfv = (PFNGLLIGHTMODELFVPROC) load(userptr, \"glLightModelfv\");\n    glad_glLightModeli = (PFNGLLIGHTMODELIPROC) load(userptr, \"glLightModeli\");\n    glad_glLightModeliv = (PFNGLLIGHTMODELIVPROC) load(userptr, \"glLightModeliv\");\n    glad_glLightf = (PFNGLLIGHTFPROC) load(userptr, \"glLightf\");\n    glad_glLightfv = (PFNGLLIGHTFVPROC) load(userptr, \"glLightfv\");\n    glad_glLighti = (PFNGLLIGHTIPROC) load(userptr, \"glLighti\");\n    glad_glLightiv = (PFNGLLIGHTIVPROC) load(userptr, \"glLightiv\");\n    glad_glLineStipple = (PFNGLLINESTIPPLEPROC) load(userptr, \"glLineStipple\");\n    glad_glLineWidth = (PFNGLLINEWIDTHPROC) load(userptr, \"glLineWidth\");\n    glad_glListBase = (PFNGLLISTBASEPROC) load(userptr, \"glListBase\");\n    glad_glLoadIdentity = (PFNGLLOADIDENTITYPROC) load(userptr, \"glLoadIdentity\");\n    glad_glLoadMatrixd = (PFNGLLOADMATRIXDPROC) load(userptr, \"glLoadMatrixd\");\n    glad_glLoadMatrixf = (PFNGLLOADMATRIXFPROC) load(userptr, \"glLoadMatrixf\");\n    glad_glLoadName = (PFNGLLOADNAMEPROC) load(userptr, \"glLoadName\");\n    glad_glLogicOp = (PFNGLLOGICOPPROC) load(userptr, \"glLogicOp\");\n    glad_glMap1d = (PFNGLMAP1DPROC) load(userptr, \"glMap1d\");\n    glad_glMap1f = (PFNGLMAP1FPROC) load(userptr, \"glMap1f\");\n    glad_glMap2d = (PFNGLMAP2DPROC) load(userptr, \"glMap2d\");\n    glad_glMap2f = (PFNGLMAP2FPROC) load(userptr, \"glMap2f\");\n    glad_glMapGrid1d = (PFNGLMAPGRID1DPROC) load(userptr, \"glMapGrid1d\");\n    glad_glMapGrid1f = (PFNGLMAPGRID1FPROC) load(userptr, \"glMapGrid1f\");\n    glad_glMapGrid2d = (PFNGLMAPGRID2DPROC) load(userptr, \"glMapGrid2d\");\n    glad_glMapGrid2f = (PFNGLMAPGRID2FPROC) load(userptr, \"glMapGrid2f\");\n    glad_glMaterialf = (PFNGLMATERIALFPROC) load(userptr, \"glMaterialf\");\n    glad_glMaterialfv = (PFNGLMATERIALFVPROC) load(userptr, \"glMaterialfv\");\n    glad_glMateriali = (PFNGLMATERIALIPROC) load(userptr, \"glMateriali\");\n    glad_glMaterialiv = (PFNGLMATERIALIVPROC) load(userptr, \"glMaterialiv\");\n    glad_glMatrixMode = (PFNGLMATRIXMODEPROC) load(userptr, \"glMatrixMode\");\n    glad_glMultMatrixd = (PFNGLMULTMATRIXDPROC) load(userptr, \"glMultMatrixd\");\n    glad_glMultMatrixf = (PFNGLMULTMATRIXFPROC) load(userptr, \"glMultMatrixf\");\n    glad_glNewList = (PFNGLNEWLISTPROC) load(userptr, \"glNewList\");\n    glad_glNormal3b = (PFNGLNORMAL3BPROC) load(userptr, \"glNormal3b\");\n    glad_glNormal3bv = (PFNGLNORMAL3BVPROC) load(userptr, \"glNormal3bv\");\n    glad_glNormal3d = (PFNGLNORMAL3DPROC) load(userptr, \"glNormal3d\");\n    glad_glNormal3dv = (PFNGLNORMAL3DVPROC) load(userptr, \"glNormal3dv\");\n    glad_glNormal3f = (PFNGLNORMAL3FPROC) load(userptr, \"glNormal3f\");\n    glad_glNormal3fv = (PFNGLNORMAL3FVPROC) load(userptr, \"glNormal3fv\");\n    glad_glNormal3i = (PFNGLNORMAL3IPROC) load(userptr, \"glNormal3i\");\n    glad_glNormal3iv = (PFNGLNORMAL3IVPROC) load(userptr, \"glNormal3iv\");\n    glad_glNormal3s = (PFNGLNORMAL3SPROC) load(userptr, \"glNormal3s\");\n    glad_glNormal3sv = (PFNGLNORMAL3SVPROC) load(userptr, \"glNormal3sv\");\n    glad_glOrtho = (PFNGLORTHOPROC) load(userptr, \"glOrtho\");\n    glad_glPassThrough = (PFNGLPASSTHROUGHPROC) load(userptr, \"glPassThrough\");\n    glad_glPixelMapfv = (PFNGLPIXELMAPFVPROC) load(userptr, \"glPixelMapfv\");\n    glad_glPixelMapuiv = (PFNGLPIXELMAPUIVPROC) load(userptr, \"glPixelMapuiv\");\n    glad_glPixelMapusv = (PFNGLPIXELMAPUSVPROC) load(userptr, \"glPixelMapusv\");\n    glad_glPixelStoref = (PFNGLPIXELSTOREFPROC) load(userptr, \"glPixelStoref\");\n    glad_glPixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, \"glPixelStorei\");\n    glad_glPixelTransferf = (PFNGLPIXELTRANSFERFPROC) load(userptr, \"glPixelTransferf\");\n    glad_glPixelTransferi = (PFNGLPIXELTRANSFERIPROC) load(userptr, \"glPixelTransferi\");\n    glad_glPixelZoom = (PFNGLPIXELZOOMPROC) load(userptr, \"glPixelZoom\");\n    glad_glPointSize = (PFNGLPOINTSIZEPROC) load(userptr, \"glPointSize\");\n    glad_glPolygonMode = (PFNGLPOLYGONMODEPROC) load(userptr, \"glPolygonMode\");\n    glad_glPolygonStipple = (PFNGLPOLYGONSTIPPLEPROC) load(userptr, \"glPolygonStipple\");\n    glad_glPopAttrib = (PFNGLPOPATTRIBPROC) load(userptr, \"glPopAttrib\");\n    glad_glPopMatrix = (PFNGLPOPMATRIXPROC) load(userptr, \"glPopMatrix\");\n    glad_glPopName = (PFNGLPOPNAMEPROC) load(userptr, \"glPopName\");\n    glad_glPushAttrib = (PFNGLPUSHATTRIBPROC) load(userptr, \"glPushAttrib\");\n    glad_glPushMatrix = (PFNGLPUSHMATRIXPROC) load(userptr, \"glPushMatrix\");\n    glad_glPushName = (PFNGLPUSHNAMEPROC) load(userptr, \"glPushName\");\n    glad_glRasterPos2d = (PFNGLRASTERPOS2DPROC) load(userptr, \"glRasterPos2d\");\n    glad_glRasterPos2dv = (PFNGLRASTERPOS2DVPROC) load(userptr, \"glRasterPos2dv\");\n    glad_glRasterPos2f = (PFNGLRASTERPOS2FPROC) load(userptr, \"glRasterPos2f\");\n    glad_glRasterPos2fv = (PFNGLRASTERPOS2FVPROC) load(userptr, \"glRasterPos2fv\");\n    glad_glRasterPos2i = (PFNGLRASTERPOS2IPROC) load(userptr, \"glRasterPos2i\");\n    glad_glRasterPos2iv = (PFNGLRASTERPOS2IVPROC) load(userptr, \"glRasterPos2iv\");\n    glad_glRasterPos2s = (PFNGLRASTERPOS2SPROC) load(userptr, \"glRasterPos2s\");\n    glad_glRasterPos2sv = (PFNGLRASTERPOS2SVPROC) load(userptr, \"glRasterPos2sv\");\n    glad_glRasterPos3d = (PFNGLRASTERPOS3DPROC) load(userptr, \"glRasterPos3d\");\n    glad_glRasterPos3dv = (PFNGLRASTERPOS3DVPROC) load(userptr, \"glRasterPos3dv\");\n    glad_glRasterPos3f = (PFNGLRASTERPOS3FPROC) load(userptr, \"glRasterPos3f\");\n    glad_glRasterPos3fv = (PFNGLRASTERPOS3FVPROC) load(userptr, \"glRasterPos3fv\");\n    glad_glRasterPos3i = (PFNGLRASTERPOS3IPROC) load(userptr, \"glRasterPos3i\");\n    glad_glRasterPos3iv = (PFNGLRASTERPOS3IVPROC) load(userptr, \"glRasterPos3iv\");\n    glad_glRasterPos3s = (PFNGLRASTERPOS3SPROC) load(userptr, \"glRasterPos3s\");\n    glad_glRasterPos3sv = (PFNGLRASTERPOS3SVPROC) load(userptr, \"glRasterPos3sv\");\n    glad_glRasterPos4d = (PFNGLRASTERPOS4DPROC) load(userptr, \"glRasterPos4d\");\n    glad_glRasterPos4dv = (PFNGLRASTERPOS4DVPROC) load(userptr, \"glRasterPos4dv\");\n    glad_glRasterPos4f = (PFNGLRASTERPOS4FPROC) load(userptr, \"glRasterPos4f\");\n    glad_glRasterPos4fv = (PFNGLRASTERPOS4FVPROC) load(userptr, \"glRasterPos4fv\");\n    glad_glRasterPos4i = (PFNGLRASTERPOS4IPROC) load(userptr, \"glRasterPos4i\");\n    glad_glRasterPos4iv = (PFNGLRASTERPOS4IVPROC) load(userptr, \"glRasterPos4iv\");\n    glad_glRasterPos4s = (PFNGLRASTERPOS4SPROC) load(userptr, \"glRasterPos4s\");\n    glad_glRasterPos4sv = (PFNGLRASTERPOS4SVPROC) load(userptr, \"glRasterPos4sv\");\n    glad_glReadBuffer = (PFNGLREADBUFFERPROC) load(userptr, \"glReadBuffer\");\n    glad_glReadPixels = (PFNGLREADPIXELSPROC) load(userptr, \"glReadPixels\");\n    glad_glRectd = (PFNGLRECTDPROC) load(userptr, \"glRectd\");\n    glad_glRectdv = (PFNGLRECTDVPROC) load(userptr, \"glRectdv\");\n    glad_glRectf = (PFNGLRECTFPROC) load(userptr, \"glRectf\");\n    glad_glRectfv = (PFNGLRECTFVPROC) load(userptr, \"glRectfv\");\n    glad_glRecti = (PFNGLRECTIPROC) load(userptr, \"glRecti\");\n    glad_glRectiv = (PFNGLRECTIVPROC) load(userptr, \"glRectiv\");\n    glad_glRects = (PFNGLRECTSPROC) load(userptr, \"glRects\");\n    glad_glRectsv = (PFNGLRECTSVPROC) load(userptr, \"glRectsv\");\n    glad_glRenderMode = (PFNGLRENDERMODEPROC) load(userptr, \"glRenderMode\");\n    glad_glRotated = (PFNGLROTATEDPROC) load(userptr, \"glRotated\");\n    glad_glRotatef = (PFNGLROTATEFPROC) load(userptr, \"glRotatef\");\n    glad_glScaled = (PFNGLSCALEDPROC) load(userptr, \"glScaled\");\n    glad_glScalef = (PFNGLSCALEFPROC) load(userptr, \"glScalef\");\n    glad_glScissor = (PFNGLSCISSORPROC) load(userptr, \"glScissor\");\n    glad_glSelectBuffer = (PFNGLSELECTBUFFERPROC) load(userptr, \"glSelectBuffer\");\n    glad_glShadeModel = (PFNGLSHADEMODELPROC) load(userptr, \"glShadeModel\");\n    glad_glStencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, \"glStencilFunc\");\n    glad_glStencilMask = (PFNGLSTENCILMASKPROC) load(userptr, \"glStencilMask\");\n    glad_glStencilOp = (PFNGLSTENCILOPPROC) load(userptr, \"glStencilOp\");\n    glad_glTexCoord1d = (PFNGLTEXCOORD1DPROC) load(userptr, \"glTexCoord1d\");\n    glad_glTexCoord1dv = (PFNGLTEXCOORD1DVPROC) load(userptr, \"glTexCoord1dv\");\n    glad_glTexCoord1f = (PFNGLTEXCOORD1FPROC) load(userptr, \"glTexCoord1f\");\n    glad_glTexCoord1fv = (PFNGLTEXCOORD1FVPROC) load(userptr, \"glTexCoord1fv\");\n    glad_glTexCoord1i = (PFNGLTEXCOORD1IPROC) load(userptr, \"glTexCoord1i\");\n    glad_glTexCoord1iv = (PFNGLTEXCOORD1IVPROC) load(userptr, \"glTexCoord1iv\");\n    glad_glTexCoord1s = (PFNGLTEXCOORD1SPROC) load(userptr, \"glTexCoord1s\");\n    glad_glTexCoord1sv = (PFNGLTEXCOORD1SVPROC) load(userptr, \"glTexCoord1sv\");\n    glad_glTexCoord2d = (PFNGLTEXCOORD2DPROC) load(userptr, \"glTexCoord2d\");\n    glad_glTexCoord2dv = (PFNGLTEXCOORD2DVPROC) load(userptr, \"glTexCoord2dv\");\n    glad_glTexCoord2f = (PFNGLTEXCOORD2FPROC) load(userptr, \"glTexCoord2f\");\n    glad_glTexCoord2fv = (PFNGLTEXCOORD2FVPROC) load(userptr, \"glTexCoord2fv\");\n    glad_glTexCoord2i = (PFNGLTEXCOORD2IPROC) load(userptr, \"glTexCoord2i\");\n    glad_glTexCoord2iv = (PFNGLTEXCOORD2IVPROC) load(userptr, \"glTexCoord2iv\");\n    glad_glTexCoord2s = (PFNGLTEXCOORD2SPROC) load(userptr, \"glTexCoord2s\");\n    glad_glTexCoord2sv = (PFNGLTEXCOORD2SVPROC) load(userptr, \"glTexCoord2sv\");\n    glad_glTexCoord3d = (PFNGLTEXCOORD3DPROC) load(userptr, \"glTexCoord3d\");\n    glad_glTexCoord3dv = (PFNGLTEXCOORD3DVPROC) load(userptr, \"glTexCoord3dv\");\n    glad_glTexCoord3f = (PFNGLTEXCOORD3FPROC) load(userptr, \"glTexCoord3f\");\n    glad_glTexCoord3fv = (PFNGLTEXCOORD3FVPROC) load(userptr, \"glTexCoord3fv\");\n    glad_glTexCoord3i = (PFNGLTEXCOORD3IPROC) load(userptr, \"glTexCoord3i\");\n    glad_glTexCoord3iv = (PFNGLTEXCOORD3IVPROC) load(userptr, \"glTexCoord3iv\");\n    glad_glTexCoord3s = (PFNGLTEXCOORD3SPROC) load(userptr, \"glTexCoord3s\");\n    glad_glTexCoord3sv = (PFNGLTEXCOORD3SVPROC) load(userptr, \"glTexCoord3sv\");\n    glad_glTexCoord4d = (PFNGLTEXCOORD4DPROC) load(userptr, \"glTexCoord4d\");\n    glad_glTexCoord4dv = (PFNGLTEXCOORD4DVPROC) load(userptr, \"glTexCoord4dv\");\n    glad_glTexCoord4f = (PFNGLTEXCOORD4FPROC) load(userptr, \"glTexCoord4f\");\n    glad_glTexCoord4fv = (PFNGLTEXCOORD4FVPROC) load(userptr, \"glTexCoord4fv\");\n    glad_glTexCoord4i = (PFNGLTEXCOORD4IPROC) load(userptr, \"glTexCoord4i\");\n    glad_glTexCoord4iv = (PFNGLTEXCOORD4IVPROC) load(userptr, \"glTexCoord4iv\");\n    glad_glTexCoord4s = (PFNGLTEXCOORD4SPROC) load(userptr, \"glTexCoord4s\");\n    glad_glTexCoord4sv = (PFNGLTEXCOORD4SVPROC) load(userptr, \"glTexCoord4sv\");\n    glad_glTexEnvf = (PFNGLTEXENVFPROC) load(userptr, \"glTexEnvf\");\n    glad_glTexEnvfv = (PFNGLTEXENVFVPROC) load(userptr, \"glTexEnvfv\");\n    glad_glTexEnvi = (PFNGLTEXENVIPROC) load(userptr, \"glTexEnvi\");\n    glad_glTexEnviv = (PFNGLTEXENVIVPROC) load(userptr, \"glTexEnviv\");\n    glad_glTexGend = (PFNGLTEXGENDPROC) load(userptr, \"glTexGend\");\n    glad_glTexGendv = (PFNGLTEXGENDVPROC) load(userptr, \"glTexGendv\");\n    glad_glTexGenf = (PFNGLTEXGENFPROC) load(userptr, \"glTexGenf\");\n    glad_glTexGenfv = (PFNGLTEXGENFVPROC) load(userptr, \"glTexGenfv\");\n    glad_glTexGeni = (PFNGLTEXGENIPROC) load(userptr, \"glTexGeni\");\n    glad_glTexGeniv = (PFNGLTEXGENIVPROC) load(userptr, \"glTexGeniv\");\n    glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC) load(userptr, \"glTexImage1D\");\n    glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, \"glTexImage2D\");\n    glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, \"glTexParameterf\");\n    glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, \"glTexParameterfv\");\n    glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, \"glTexParameteri\");\n    glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, \"glTexParameteriv\");\n    glad_glTranslated = (PFNGLTRANSLATEDPROC) load(userptr, \"glTranslated\");\n    glad_glTranslatef = (PFNGLTRANSLATEFPROC) load(userptr, \"glTranslatef\");\n    glad_glVertex2d = (PFNGLVERTEX2DPROC) load(userptr, \"glVertex2d\");\n    glad_glVertex2dv = (PFNGLVERTEX2DVPROC) load(userptr, \"glVertex2dv\");\n    glad_glVertex2f = (PFNGLVERTEX2FPROC) load(userptr, \"glVertex2f\");\n    glad_glVertex2fv = (PFNGLVERTEX2FVPROC) load(userptr, \"glVertex2fv\");\n    glad_glVertex2i = (PFNGLVERTEX2IPROC) load(userptr, \"glVertex2i\");\n    glad_glVertex2iv = (PFNGLVERTEX2IVPROC) load(userptr, \"glVertex2iv\");\n    glad_glVertex2s = (PFNGLVERTEX2SPROC) load(userptr, \"glVertex2s\");\n    glad_glVertex2sv = (PFNGLVERTEX2SVPROC) load(userptr, \"glVertex2sv\");\n    glad_glVertex3d = (PFNGLVERTEX3DPROC) load(userptr, \"glVertex3d\");\n    glad_glVertex3dv = (PFNGLVERTEX3DVPROC) load(userptr, \"glVertex3dv\");\n    glad_glVertex3f = (PFNGLVERTEX3FPROC) load(userptr, \"glVertex3f\");\n    glad_glVertex3fv = (PFNGLVERTEX3FVPROC) load(userptr, \"glVertex3fv\");\n    glad_glVertex3i = (PFNGLVERTEX3IPROC) load(userptr, \"glVertex3i\");\n    glad_glVertex3iv = (PFNGLVERTEX3IVPROC) load(userptr, \"glVertex3iv\");\n    glad_glVertex3s = (PFNGLVERTEX3SPROC) load(userptr, \"glVertex3s\");\n    glad_glVertex3sv = (PFNGLVERTEX3SVPROC) load(userptr, \"glVertex3sv\");\n    glad_glVertex4d = (PFNGLVERTEX4DPROC) load(userptr, \"glVertex4d\");\n    glad_glVertex4dv = (PFNGLVERTEX4DVPROC) load(userptr, \"glVertex4dv\");\n    glad_glVertex4f = (PFNGLVERTEX4FPROC) load(userptr, \"glVertex4f\");\n    glad_glVertex4fv = (PFNGLVERTEX4FVPROC) load(userptr, \"glVertex4fv\");\n    glad_glVertex4i = (PFNGLVERTEX4IPROC) load(userptr, \"glVertex4i\");\n    glad_glVertex4iv = (PFNGLVERTEX4IVPROC) load(userptr, \"glVertex4iv\");\n    glad_glVertex4s = (PFNGLVERTEX4SPROC) load(userptr, \"glVertex4s\");\n    glad_glVertex4sv = (PFNGLVERTEX4SVPROC) load(userptr, \"glVertex4sv\");\n    glad_glViewport = (PFNGLVIEWPORTPROC) load(userptr, \"glViewport\");\n}\nstatic void glad_gl_load_GL_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_1_1) return;\n    glad_glAreTexturesResident = (PFNGLARETEXTURESRESIDENTPROC) load(userptr, \"glAreTexturesResident\");\n    glad_glArrayElement = (PFNGLARRAYELEMENTPROC) load(userptr, \"glArrayElement\");\n    glad_glBindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, \"glBindTexture\");\n    glad_glColorPointer = (PFNGLCOLORPOINTERPROC) load(userptr, \"glColorPointer\");\n    glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC) load(userptr, \"glCopyTexImage1D\");\n    glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, \"glCopyTexImage2D\");\n    glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC) load(userptr, \"glCopyTexSubImage1D\");\n    glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, \"glCopyTexSubImage2D\");\n    glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, \"glDeleteTextures\");\n    glad_glDisableClientState = (PFNGLDISABLECLIENTSTATEPROC) load(userptr, \"glDisableClientState\");\n    glad_glDrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, \"glDrawArrays\");\n    glad_glDrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, \"glDrawElements\");\n    glad_glEdgeFlagPointer = (PFNGLEDGEFLAGPOINTERPROC) load(userptr, \"glEdgeFlagPointer\");\n    glad_glEnableClientState = (PFNGLENABLECLIENTSTATEPROC) load(userptr, \"glEnableClientState\");\n    glad_glGenTextures = (PFNGLGENTEXTURESPROC) load(userptr, \"glGenTextures\");\n    glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, \"glGetPointerv\");\n    glad_glIndexPointer = (PFNGLINDEXPOINTERPROC) load(userptr, \"glIndexPointer\");\n    glad_glIndexub = (PFNGLINDEXUBPROC) load(userptr, \"glIndexub\");\n    glad_glIndexubv = (PFNGLINDEXUBVPROC) load(userptr, \"glIndexubv\");\n    glad_glInterleavedArrays = (PFNGLINTERLEAVEDARRAYSPROC) load(userptr, \"glInterleavedArrays\");\n    glad_glIsTexture = (PFNGLISTEXTUREPROC) load(userptr, \"glIsTexture\");\n    glad_glNormalPointer = (PFNGLNORMALPOINTERPROC) load(userptr, \"glNormalPointer\");\n    glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, \"glPolygonOffset\");\n    glad_glPopClientAttrib = (PFNGLPOPCLIENTATTRIBPROC) load(userptr, \"glPopClientAttrib\");\n    glad_glPrioritizeTextures = (PFNGLPRIORITIZETEXTURESPROC) load(userptr, \"glPrioritizeTextures\");\n    glad_glPushClientAttrib = (PFNGLPUSHCLIENTATTRIBPROC) load(userptr, \"glPushClientAttrib\");\n    glad_glTexCoordPointer = (PFNGLTEXCOORDPOINTERPROC) load(userptr, \"glTexCoordPointer\");\n    glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC) load(userptr, \"glTexSubImage1D\");\n    glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, \"glTexSubImage2D\");\n    glad_glVertexPointer = (PFNGLVERTEXPOINTERPROC) load(userptr, \"glVertexPointer\");\n}\nstatic void glad_gl_load_GL_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_1_2) return;\n    glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC) load(userptr, \"glCopyTexSubImage3D\");\n    glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) load(userptr, \"glDrawRangeElements\");\n    glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC) load(userptr, \"glTexImage3D\");\n    glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC) load(userptr, \"glTexSubImage3D\");\n}\nstatic void glad_gl_load_GL_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_1_3) return;\n    glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, \"glActiveTexture\");\n    glad_glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC) load(userptr, \"glClientActiveTexture\");\n    glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) load(userptr, \"glCompressedTexImage1D\");\n    glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, \"glCompressedTexImage2D\");\n    glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) load(userptr, \"glCompressedTexImage3D\");\n    glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) load(userptr, \"glCompressedTexSubImage1D\");\n    glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, \"glCompressedTexSubImage2D\");\n    glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) load(userptr, \"glCompressedTexSubImage3D\");\n    glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) load(userptr, \"glGetCompressedTexImage\");\n    glad_glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC) load(userptr, \"glLoadTransposeMatrixd\");\n    glad_glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC) load(userptr, \"glLoadTransposeMatrixf\");\n    glad_glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC) load(userptr, \"glMultTransposeMatrixd\");\n    glad_glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC) load(userptr, \"glMultTransposeMatrixf\");\n    glad_glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC) load(userptr, \"glMultiTexCoord1d\");\n    glad_glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC) load(userptr, \"glMultiTexCoord1dv\");\n    glad_glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC) load(userptr, \"glMultiTexCoord1f\");\n    glad_glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC) load(userptr, \"glMultiTexCoord1fv\");\n    glad_glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC) load(userptr, \"glMultiTexCoord1i\");\n    glad_glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC) load(userptr, \"glMultiTexCoord1iv\");\n    glad_glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC) load(userptr, \"glMultiTexCoord1s\");\n    glad_glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC) load(userptr, \"glMultiTexCoord1sv\");\n    glad_glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC) load(userptr, \"glMultiTexCoord2d\");\n    glad_glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC) load(userptr, \"glMultiTexCoord2dv\");\n    glad_glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) load(userptr, \"glMultiTexCoord2f\");\n    glad_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC) load(userptr, \"glMultiTexCoord2fv\");\n    glad_glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC) load(userptr, \"glMultiTexCoord2i\");\n    glad_glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC) load(userptr, \"glMultiTexCoord2iv\");\n    glad_glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC) load(userptr, \"glMultiTexCoord2s\");\n    glad_glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC) load(userptr, \"glMultiTexCoord2sv\");\n    glad_glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC) load(userptr, \"glMultiTexCoord3d\");\n    glad_glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC) load(userptr, \"glMultiTexCoord3dv\");\n    glad_glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC) load(userptr, \"glMultiTexCoord3f\");\n    glad_glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC) load(userptr, \"glMultiTexCoord3fv\");\n    glad_glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC) load(userptr, \"glMultiTexCoord3i\");\n    glad_glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC) load(userptr, \"glMultiTexCoord3iv\");\n    glad_glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC) load(userptr, \"glMultiTexCoord3s\");\n    glad_glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC) load(userptr, \"glMultiTexCoord3sv\");\n    glad_glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC) load(userptr, \"glMultiTexCoord4d\");\n    glad_glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC) load(userptr, \"glMultiTexCoord4dv\");\n    glad_glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC) load(userptr, \"glMultiTexCoord4f\");\n    glad_glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC) load(userptr, \"glMultiTexCoord4fv\");\n    glad_glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC) load(userptr, \"glMultiTexCoord4i\");\n    glad_glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC) load(userptr, \"glMultiTexCoord4iv\");\n    glad_glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC) load(userptr, \"glMultiTexCoord4s\");\n    glad_glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC) load(userptr, \"glMultiTexCoord4sv\");\n    glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, \"glSampleCoverage\");\n}\nstatic void glad_gl_load_GL_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_1_4) return;\n    glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, \"glBlendColor\");\n    glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, \"glBlendEquation\");\n    glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, \"glBlendFuncSeparate\");\n    glad_glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC) load(userptr, \"glFogCoordPointer\");\n    glad_glFogCoordd = (PFNGLFOGCOORDDPROC) load(userptr, \"glFogCoordd\");\n    glad_glFogCoorddv = (PFNGLFOGCOORDDVPROC) load(userptr, \"glFogCoorddv\");\n    glad_glFogCoordf = (PFNGLFOGCOORDFPROC) load(userptr, \"glFogCoordf\");\n    glad_glFogCoordfv = (PFNGLFOGCOORDFVPROC) load(userptr, \"glFogCoordfv\");\n    glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC) load(userptr, \"glMultiDrawArrays\");\n    glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC) load(userptr, \"glMultiDrawElements\");\n    glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC) load(userptr, \"glPointParameterf\");\n    glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC) load(userptr, \"glPointParameterfv\");\n    glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC) load(userptr, \"glPointParameteri\");\n    glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC) load(userptr, \"glPointParameteriv\");\n    glad_glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC) load(userptr, \"glSecondaryColor3b\");\n    glad_glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC) load(userptr, \"glSecondaryColor3bv\");\n    glad_glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC) load(userptr, \"glSecondaryColor3d\");\n    glad_glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC) load(userptr, \"glSecondaryColor3dv\");\n    glad_glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC) load(userptr, \"glSecondaryColor3f\");\n    glad_glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC) load(userptr, \"glSecondaryColor3fv\");\n    glad_glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC) load(userptr, \"glSecondaryColor3i\");\n    glad_glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC) load(userptr, \"glSecondaryColor3iv\");\n    glad_glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC) load(userptr, \"glSecondaryColor3s\");\n    glad_glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC) load(userptr, \"glSecondaryColor3sv\");\n    glad_glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC) load(userptr, \"glSecondaryColor3ub\");\n    glad_glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC) load(userptr, \"glSecondaryColor3ubv\");\n    glad_glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC) load(userptr, \"glSecondaryColor3ui\");\n    glad_glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC) load(userptr, \"glSecondaryColor3uiv\");\n    glad_glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC) load(userptr, \"glSecondaryColor3us\");\n    glad_glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC) load(userptr, \"glSecondaryColor3usv\");\n    glad_glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC) load(userptr, \"glSecondaryColorPointer\");\n    glad_glWindowPos2d = (PFNGLWINDOWPOS2DPROC) load(userptr, \"glWindowPos2d\");\n    glad_glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC) load(userptr, \"glWindowPos2dv\");\n    glad_glWindowPos2f = (PFNGLWINDOWPOS2FPROC) load(userptr, \"glWindowPos2f\");\n    glad_glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC) load(userptr, \"glWindowPos2fv\");\n    glad_glWindowPos2i = (PFNGLWINDOWPOS2IPROC) load(userptr, \"glWindowPos2i\");\n    glad_glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC) load(userptr, \"glWindowPos2iv\");\n    glad_glWindowPos2s = (PFNGLWINDOWPOS2SPROC) load(userptr, \"glWindowPos2s\");\n    glad_glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC) load(userptr, \"glWindowPos2sv\");\n    glad_glWindowPos3d = (PFNGLWINDOWPOS3DPROC) load(userptr, \"glWindowPos3d\");\n    glad_glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC) load(userptr, \"glWindowPos3dv\");\n    glad_glWindowPos3f = (PFNGLWINDOWPOS3FPROC) load(userptr, \"glWindowPos3f\");\n    glad_glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC) load(userptr, \"glWindowPos3fv\");\n    glad_glWindowPos3i = (PFNGLWINDOWPOS3IPROC) load(userptr, \"glWindowPos3i\");\n    glad_glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC) load(userptr, \"glWindowPos3iv\");\n    glad_glWindowPos3s = (PFNGLWINDOWPOS3SPROC) load(userptr, \"glWindowPos3s\");\n    glad_glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC) load(userptr, \"glWindowPos3sv\");\n}\nstatic void glad_gl_load_GL_VERSION_1_5( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_1_5) return;\n    glad_glBeginQuery = (PFNGLBEGINQUERYPROC) load(userptr, \"glBeginQuery\");\n    glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, \"glBindBuffer\");\n    glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, \"glBufferData\");\n    glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, \"glBufferSubData\");\n    glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, \"glDeleteBuffers\");\n    glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC) load(userptr, \"glDeleteQueries\");\n    glad_glEndQuery = (PFNGLENDQUERYPROC) load(userptr, \"glEndQuery\");\n    glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, \"glGenBuffers\");\n    glad_glGenQueries = (PFNGLGENQUERIESPROC) load(userptr, \"glGenQueries\");\n    glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, \"glGetBufferParameteriv\");\n    glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) load(userptr, \"glGetBufferPointerv\");\n    glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) load(userptr, \"glGetBufferSubData\");\n    glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) load(userptr, \"glGetQueryObjectiv\");\n    glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) load(userptr, \"glGetQueryObjectuiv\");\n    glad_glGetQueryiv = (PFNGLGETQUERYIVPROC) load(userptr, \"glGetQueryiv\");\n    glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, \"glIsBuffer\");\n    glad_glIsQuery = (PFNGLISQUERYPROC) load(userptr, \"glIsQuery\");\n    glad_glMapBuffer = (PFNGLMAPBUFFERPROC) load(userptr, \"glMapBuffer\");\n    glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) load(userptr, \"glUnmapBuffer\");\n}\nstatic void glad_gl_load_GL_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_2_0) return;\n    glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, \"glAttachShader\");\n    glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, \"glBindAttribLocation\");\n    glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, \"glBlendEquationSeparate\");\n    glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, \"glCompileShader\");\n    glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, \"glCreateProgram\");\n    glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, \"glCreateShader\");\n    glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, \"glDeleteProgram\");\n    glad_glDeleteShader = (PFNGLDELETESHADERPROC) load(userptr, \"glDeleteShader\");\n    glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, \"glDetachShader\");\n    glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, \"glDisableVertexAttribArray\");\n    glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC) load(userptr, \"glDrawBuffers\");\n    glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, \"glEnableVertexAttribArray\");\n    glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, \"glGetActiveAttrib\");\n    glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, \"glGetActiveUniform\");\n    glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, \"glGetAttachedShaders\");\n    glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, \"glGetAttribLocation\");\n    glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, \"glGetProgramInfoLog\");\n    glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, \"glGetProgramiv\");\n    glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, \"glGetShaderInfoLog\");\n    glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, \"glGetShaderSource\");\n    glad_glGetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, \"glGetShaderiv\");\n    glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, \"glGetUniformLocation\");\n    glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, \"glGetUniformfv\");\n    glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, \"glGetUniformiv\");\n    glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, \"glGetVertexAttribPointerv\");\n    glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load(userptr, \"glGetVertexAttribdv\");\n    glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, \"glGetVertexAttribfv\");\n    glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, \"glGetVertexAttribiv\");\n    glad_glIsProgram = (PFNGLISPROGRAMPROC) load(userptr, \"glIsProgram\");\n    glad_glIsShader = (PFNGLISSHADERPROC) load(userptr, \"glIsShader\");\n    glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, \"glLinkProgram\");\n    glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, \"glShaderSource\");\n    glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, \"glStencilFuncSeparate\");\n    glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, \"glStencilMaskSeparate\");\n    glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, \"glStencilOpSeparate\");\n    glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, \"glUniform1f\");\n    glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, \"glUniform1fv\");\n    glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, \"glUniform1i\");\n    glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, \"glUniform1iv\");\n    glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, \"glUniform2f\");\n    glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, \"glUniform2fv\");\n    glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, \"glUniform2i\");\n    glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, \"glUniform2iv\");\n    glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, \"glUniform3f\");\n    glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, \"glUniform3fv\");\n    glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, \"glUniform3i\");\n    glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, \"glUniform3iv\");\n    glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, \"glUniform4f\");\n    glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, \"glUniform4fv\");\n    glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, \"glUniform4i\");\n    glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, \"glUniform4iv\");\n    glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, \"glUniformMatrix2fv\");\n    glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, \"glUniformMatrix3fv\");\n    glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, \"glUniformMatrix4fv\");\n    glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, \"glUseProgram\");\n    glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, \"glValidateProgram\");\n    glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load(userptr, \"glVertexAttrib1d\");\n    glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load(userptr, \"glVertexAttrib1dv\");\n    glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, \"glVertexAttrib1f\");\n    glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, \"glVertexAttrib1fv\");\n    glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load(userptr, \"glVertexAttrib1s\");\n    glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load(userptr, \"glVertexAttrib1sv\");\n    glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load(userptr, \"glVertexAttrib2d\");\n    glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load(userptr, \"glVertexAttrib2dv\");\n    glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, \"glVertexAttrib2f\");\n    glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, \"glVertexAttrib2fv\");\n    glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load(userptr, \"glVertexAttrib2s\");\n    glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load(userptr, \"glVertexAttrib2sv\");\n    glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load(userptr, \"glVertexAttrib3d\");\n    glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load(userptr, \"glVertexAttrib3dv\");\n    glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, \"glVertexAttrib3f\");\n    glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, \"glVertexAttrib3fv\");\n    glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load(userptr, \"glVertexAttrib3s\");\n    glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load(userptr, \"glVertexAttrib3sv\");\n    glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load(userptr, \"glVertexAttrib4Nbv\");\n    glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load(userptr, \"glVertexAttrib4Niv\");\n    glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load(userptr, \"glVertexAttrib4Nsv\");\n    glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load(userptr, \"glVertexAttrib4Nub\");\n    glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load(userptr, \"glVertexAttrib4Nubv\");\n    glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load(userptr, \"glVertexAttrib4Nuiv\");\n    glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load(userptr, \"glVertexAttrib4Nusv\");\n    glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load(userptr, \"glVertexAttrib4bv\");\n    glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load(userptr, \"glVertexAttrib4d\");\n    glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load(userptr, \"glVertexAttrib4dv\");\n    glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, \"glVertexAttrib4f\");\n    glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, \"glVertexAttrib4fv\");\n    glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load(userptr, \"glVertexAttrib4iv\");\n    glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load(userptr, \"glVertexAttrib4s\");\n    glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load(userptr, \"glVertexAttrib4sv\");\n    glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load(userptr, \"glVertexAttrib4ubv\");\n    glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load(userptr, \"glVertexAttrib4uiv\");\n    glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load(userptr, \"glVertexAttrib4usv\");\n    glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, \"glVertexAttribPointer\");\n}\nstatic void glad_gl_load_GL_VERSION_2_1( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_2_1) return;\n    glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) load(userptr, \"glUniformMatrix2x3fv\");\n    glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) load(userptr, \"glUniformMatrix2x4fv\");\n    glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) load(userptr, \"glUniformMatrix3x2fv\");\n    glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) load(userptr, \"glUniformMatrix3x4fv\");\n    glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC) load(userptr, \"glUniformMatrix4x2fv\");\n    glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) load(userptr, \"glUniformMatrix4x3fv\");\n}\nstatic void glad_gl_load_GL_VERSION_3_0( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_3_0) return;\n    glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC) load(userptr, \"glBeginConditionalRender\");\n    glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) load(userptr, \"glBeginTransformFeedback\");\n    glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, \"glBindBufferBase\");\n    glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, \"glBindBufferRange\");\n    glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC) load(userptr, \"glBindFragDataLocation\");\n    glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, \"glBindFramebuffer\");\n    glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, \"glBindRenderbuffer\");\n    glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load(userptr, \"glBindVertexArray\");\n    glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load(userptr, \"glBlitFramebuffer\");\n    glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, \"glCheckFramebufferStatus\");\n    glad_glClampColor = (PFNGLCLAMPCOLORPROC) load(userptr, \"glClampColor\");\n    glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC) load(userptr, \"glClearBufferfi\");\n    glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC) load(userptr, \"glClearBufferfv\");\n    glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC) load(userptr, \"glClearBufferiv\");\n    glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC) load(userptr, \"glClearBufferuiv\");\n    glad_glColorMaski = (PFNGLCOLORMASKIPROC) load(userptr, \"glColorMaski\");\n    glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, \"glDeleteFramebuffers\");\n    glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, \"glDeleteRenderbuffers\");\n    glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load(userptr, \"glDeleteVertexArrays\");\n    glad_glDisablei = (PFNGLDISABLEIPROC) load(userptr, \"glDisablei\");\n    glad_glEnablei = (PFNGLENABLEIPROC) load(userptr, \"glEnablei\");\n    glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC) load(userptr, \"glEndConditionalRender\");\n    glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) load(userptr, \"glEndTransformFeedback\");\n    glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load(userptr, \"glFlushMappedBufferRange\");\n    glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, \"glFramebufferRenderbuffer\");\n    glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load(userptr, \"glFramebufferTexture1D\");\n    glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, \"glFramebufferTexture2D\");\n    glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load(userptr, \"glFramebufferTexture3D\");\n    glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, \"glFramebufferTextureLayer\");\n    glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, \"glGenFramebuffers\");\n    glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, \"glGenRenderbuffers\");\n    glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load(userptr, \"glGenVertexArrays\");\n    glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, \"glGenerateMipmap\");\n    glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC) load(userptr, \"glGetBooleani_v\");\n    glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC) load(userptr, \"glGetFragDataLocation\");\n    glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, \"glGetFramebufferAttachmentParameteriv\");\n    glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, \"glGetIntegeri_v\");\n    glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, \"glGetRenderbufferParameteriv\");\n    glad_glGetStringi = (PFNGLGETSTRINGIPROC) load(userptr, \"glGetStringi\");\n    glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC) load(userptr, \"glGetTexParameterIiv\");\n    glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC) load(userptr, \"glGetTexParameterIuiv\");\n    glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) load(userptr, \"glGetTransformFeedbackVarying\");\n    glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC) load(userptr, \"glGetUniformuiv\");\n    glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC) load(userptr, \"glGetVertexAttribIiv\");\n    glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC) load(userptr, \"glGetVertexAttribIuiv\");\n    glad_glIsEnabledi = (PFNGLISENABLEDIPROC) load(userptr, \"glIsEnabledi\");\n    glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, \"glIsFramebuffer\");\n    glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, \"glIsRenderbuffer\");\n    glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC) load(userptr, \"glIsVertexArray\");\n    glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load(userptr, \"glMapBufferRange\");\n    glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, \"glRenderbufferStorage\");\n    glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, \"glRenderbufferStorageMultisample\");\n    glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC) load(userptr, \"glTexParameterIiv\");\n    glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC) load(userptr, \"glTexParameterIuiv\");\n    glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) load(userptr, \"glTransformFeedbackVaryings\");\n    glad_glUniform1ui = (PFNGLUNIFORM1UIPROC) load(userptr, \"glUniform1ui\");\n    glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC) load(userptr, \"glUniform1uiv\");\n    glad_glUniform2ui = (PFNGLUNIFORM2UIPROC) load(userptr, \"glUniform2ui\");\n    glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC) load(userptr, \"glUniform2uiv\");\n    glad_glUniform3ui = (PFNGLUNIFORM3UIPROC) load(userptr, \"glUniform3ui\");\n    glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC) load(userptr, \"glUniform3uiv\");\n    glad_glUniform4ui = (PFNGLUNIFORM4UIPROC) load(userptr, \"glUniform4ui\");\n    glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC) load(userptr, \"glUniform4uiv\");\n    glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC) load(userptr, \"glVertexAttribI1i\");\n    glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC) load(userptr, \"glVertexAttribI1iv\");\n    glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC) load(userptr, \"glVertexAttribI1ui\");\n    glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC) load(userptr, \"glVertexAttribI1uiv\");\n    glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC) load(userptr, \"glVertexAttribI2i\");\n    glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC) load(userptr, \"glVertexAttribI2iv\");\n    glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC) load(userptr, \"glVertexAttribI2ui\");\n    glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC) load(userptr, \"glVertexAttribI2uiv\");\n    glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC) load(userptr, \"glVertexAttribI3i\");\n    glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC) load(userptr, \"glVertexAttribI3iv\");\n    glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC) load(userptr, \"glVertexAttribI3ui\");\n    glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC) load(userptr, \"glVertexAttribI3uiv\");\n    glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC) load(userptr, \"glVertexAttribI4bv\");\n    glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC) load(userptr, \"glVertexAttribI4i\");\n    glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC) load(userptr, \"glVertexAttribI4iv\");\n    glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC) load(userptr, \"glVertexAttribI4sv\");\n    glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC) load(userptr, \"glVertexAttribI4ubv\");\n    glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC) load(userptr, \"glVertexAttribI4ui\");\n    glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC) load(userptr, \"glVertexAttribI4uiv\");\n    glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC) load(userptr, \"glVertexAttribI4usv\");\n    glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) load(userptr, \"glVertexAttribIPointer\");\n}\nstatic void glad_gl_load_GL_VERSION_3_1( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_VERSION_3_1) return;\n    glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, \"glBindBufferBase\");\n    glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, \"glBindBufferRange\");\n    glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load(userptr, \"glCopyBufferSubData\");\n    glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) load(userptr, \"glDrawArraysInstanced\");\n    glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) load(userptr, \"glDrawElementsInstanced\");\n    glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load(userptr, \"glGetActiveUniformBlockName\");\n    glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load(userptr, \"glGetActiveUniformBlockiv\");\n    glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load(userptr, \"glGetActiveUniformName\");\n    glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load(userptr, \"glGetActiveUniformsiv\");\n    glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, \"glGetIntegeri_v\");\n    glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load(userptr, \"glGetUniformBlockIndex\");\n    glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load(userptr, \"glGetUniformIndices\");\n    glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) load(userptr, \"glPrimitiveRestartIndex\");\n    glad_glTexBuffer = (PFNGLTEXBUFFERPROC) load(userptr, \"glTexBuffer\");\n    glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load(userptr, \"glUniformBlockBinding\");\n}\nstatic void glad_gl_load_GL_ARB_copy_image( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_ARB_copy_image) return;\n    glad_glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC) load(userptr, \"glCopyImageSubData\");\n}\nstatic void glad_gl_load_GL_ARB_instanced_arrays( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_ARB_instanced_arrays) return;\n    glad_glVertexAttribDivisorARB = (PFNGLVERTEXATTRIBDIVISORARBPROC) load(userptr, \"glVertexAttribDivisorARB\");\n}\nstatic void glad_gl_load_GL_ARB_multisample( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_ARB_multisample) return;\n    glad_glSampleCoverageARB = (PFNGLSAMPLECOVERAGEARBPROC) load(userptr, \"glSampleCoverageARB\");\n}\nstatic void glad_gl_load_GL_ARB_robustness( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_ARB_robustness) return;\n    glad_glGetGraphicsResetStatusARB = (PFNGLGETGRAPHICSRESETSTATUSARBPROC) load(userptr, \"glGetGraphicsResetStatusARB\");\n    glad_glGetnCompressedTexImageARB = (PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) load(userptr, \"glGetnCompressedTexImageARB\");\n    glad_glGetnTexImageARB = (PFNGLGETNTEXIMAGEARBPROC) load(userptr, \"glGetnTexImageARB\");\n    glad_glGetnUniformdvARB = (PFNGLGETNUNIFORMDVARBPROC) load(userptr, \"glGetnUniformdvARB\");\n    glad_glGetnUniformfvARB = (PFNGLGETNUNIFORMFVARBPROC) load(userptr, \"glGetnUniformfvARB\");\n    glad_glGetnUniformivARB = (PFNGLGETNUNIFORMIVARBPROC) load(userptr, \"glGetnUniformivARB\");\n    glad_glGetnUniformuivARB = (PFNGLGETNUNIFORMUIVARBPROC) load(userptr, \"glGetnUniformuivARB\");\n    glad_glReadnPixelsARB = (PFNGLREADNPIXELSARBPROC) load(userptr, \"glReadnPixelsARB\");\n}\nstatic void glad_gl_load_GL_ARB_texture_storage( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_ARB_texture_storage) return;\n    glad_glTexStorage1D = (PFNGLTEXSTORAGE1DPROC) load(userptr, \"glTexStorage1D\");\n    glad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC) load(userptr, \"glTexStorage2D\");\n    glad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC) load(userptr, \"glTexStorage3D\");\n}\nstatic void glad_gl_load_GL_KHR_debug( GLADuserptrloadfunc load, void* userptr) {\n    if(!GLAD_GL_KHR_debug) return;\n    glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) load(userptr, \"glDebugMessageCallback\");\n    glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) load(userptr, \"glDebugMessageControl\");\n    glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) load(userptr, \"glDebugMessageInsert\");\n    glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) load(userptr, \"glGetDebugMessageLog\");\n    glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC) load(userptr, \"glGetObjectLabel\");\n    glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC) load(userptr, \"glGetObjectPtrLabel\");\n    glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, \"glGetPointerv\");\n    glad_glObjectLabel = (PFNGLOBJECTLABELPROC) load(userptr, \"glObjectLabel\");\n    glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC) load(userptr, \"glObjectPtrLabel\");\n    glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) load(userptr, \"glPopDebugGroup\");\n    glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) load(userptr, \"glPushDebugGroup\");\n}\nstatic void glad_gl_free_extensions(char **exts_i) {\n    if (exts_i != NULL) {\n        unsigned int index;\n        for(index = 0; exts_i[index]; index++) {\n            free((void *) (exts_i[index]));\n        }\n        free((void *)exts_i);\n        exts_i = NULL;\n    }\n}\nstatic int glad_gl_get_extensions( const char **out_exts, char ***out_exts_i) {\n#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)\n    if (glad_glGetStringi != NULL && glad_glGetIntegerv != NULL) {\n        unsigned int index = 0;\n        unsigned int num_exts_i = 0;\n        char **exts_i = NULL;\n        glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i);\n        exts_i = (char **) malloc((num_exts_i + 1) * (sizeof *exts_i));\n        if (exts_i == NULL) {\n            return 0;\n        }\n        for(index = 0; index < num_exts_i; index++) {\n            const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index);\n            size_t len = strlen(gl_str_tmp) + 1;\n            char *local_str = (char*) malloc(len * sizeof(char));\n            if(local_str == NULL) {\n                exts_i[index] = NULL;\n                glad_gl_free_extensions(exts_i);\n                return 0;\n            }\n            memcpy(local_str, gl_str_tmp, len * sizeof(char));\n            exts_i[index] = local_str;\n        }\n        exts_i[index] = NULL;\n        *out_exts_i = exts_i;\n        return 1;\n    }\n#else\n    GLAD_UNUSED(out_exts_i);\n#endif\n    if (glad_glGetString == NULL) {\n        return 0;\n    }\n    *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS);\n    return 1;\n}\nstatic int glad_gl_has_extension(const char *exts, char **exts_i, const char *ext) {\n    if(exts_i) {\n        unsigned int index;\n        for(index = 0; exts_i[index]; index++) {\n            const char *e = exts_i[index];\n            if(strcmp(e, ext) == 0) {\n                return 1;\n            }\n        }\n    } else {\n        const char *extensions;\n        const char *loc;\n        const char *terminator;\n        extensions = exts;\n        if(extensions == NULL || ext == NULL) {\n            return 0;\n        }\n        while(1) {\n            loc = strstr(extensions, ext);\n            if(loc == NULL) {\n                return 0;\n            }\n            terminator = loc + strlen(ext);\n            if((loc == extensions || *(loc - 1) == ' ') &&\n                (*terminator == ' ' || *terminator == '\\0')) {\n                return 1;\n            }\n            extensions = terminator;\n        }\n    }\n    return 0;\n}\nstatic GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) {\n    return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name);\n}\nstatic int glad_gl_find_extensions_gl(void) {\n    const char *exts = NULL;\n    char **exts_i = NULL;\n    if (!glad_gl_get_extensions(&exts, &exts_i)) return 0;\n    GLAD_GL_ARB_copy_image = glad_gl_has_extension(exts, exts_i, \"GL_ARB_copy_image\");\n    GLAD_GL_ARB_framebuffer_sRGB = glad_gl_has_extension(exts, exts_i, \"GL_ARB_framebuffer_sRGB\");\n    GLAD_GL_ARB_instanced_arrays = glad_gl_has_extension(exts, exts_i, \"GL_ARB_instanced_arrays\");\n    GLAD_GL_ARB_multisample = glad_gl_has_extension(exts, exts_i, \"GL_ARB_multisample\");\n    GLAD_GL_ARB_robustness = glad_gl_has_extension(exts, exts_i, \"GL_ARB_robustness\");\n    GLAD_GL_ARB_texture_storage = glad_gl_has_extension(exts, exts_i, \"GL_ARB_texture_storage\");\n    GLAD_GL_EXT_framebuffer_sRGB = glad_gl_has_extension(exts, exts_i, \"GL_EXT_framebuffer_sRGB\");\n    GLAD_GL_KHR_debug = glad_gl_has_extension(exts, exts_i, \"GL_KHR_debug\");\n    glad_gl_free_extensions(exts_i);\n    return 1;\n}\nstatic int glad_gl_find_core_gl(void) {\n    int i;\n    const char* version;\n    const char* prefixes[] = {\n        \"OpenGL ES-CM \",\n        \"OpenGL ES-CL \",\n        \"OpenGL ES \",\n        \"OpenGL SC \",\n        NULL\n    };\n    int major = 0;\n    int minor = 0;\n    version = (const char*) glad_glGetString(GL_VERSION);\n    if (!version) return 0;\n    for (i = 0;  prefixes[i];  i++) {\n        const size_t length = strlen(prefixes[i]);\n        if (strncmp(version, prefixes[i], length) == 0) {\n            version += length;\n            break;\n        }\n    }\n    GLAD_IMPL_UTIL_SSCANF(version, \"%d.%d\", &major, &minor);\n    GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;\n    GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1;\n    GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1;\n    GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1;\n    GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1;\n    GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1;\n    GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2;\n    GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2;\n    GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3;\n    GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3;\n    return GLAD_MAKE_VERSION(major, minor);\n}\nint gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) {\n    int version;\n    glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, \"glGetString\");\n    if(glad_glGetString == NULL) return 0;\n    version = glad_gl_find_core_gl();\n    glad_gl_load_GL_VERSION_1_0(load, userptr);\n    glad_gl_load_GL_VERSION_1_1(load, userptr);\n    glad_gl_load_GL_VERSION_1_2(load, userptr);\n    glad_gl_load_GL_VERSION_1_3(load, userptr);\n    glad_gl_load_GL_VERSION_1_4(load, userptr);\n    glad_gl_load_GL_VERSION_1_5(load, userptr);\n    glad_gl_load_GL_VERSION_2_0(load, userptr);\n    glad_gl_load_GL_VERSION_2_1(load, userptr);\n    glad_gl_load_GL_VERSION_3_0(load, userptr);\n    glad_gl_load_GL_VERSION_3_1(load, userptr);\n    if (!glad_gl_find_extensions_gl()) return 0;\n    glad_gl_load_GL_ARB_copy_image(load, userptr);\n    glad_gl_load_GL_ARB_instanced_arrays(load, userptr);\n    glad_gl_load_GL_ARB_multisample(load, userptr);\n    glad_gl_load_GL_ARB_robustness(load, userptr);\n    glad_gl_load_GL_ARB_texture_storage(load, userptr);\n    glad_gl_load_GL_KHR_debug(load, userptr);\n    return version;\n}\nint gladLoadGL( GLADloadfunc load) {\n    return gladLoadGLUserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load);\n}\nvoid gladInstallGLDebug(void) {\n    glad_debug_glAccum = glad_debug_impl_glAccum;\n    glad_debug_glActiveTexture = glad_debug_impl_glActiveTexture;\n    glad_debug_glAlphaFunc = glad_debug_impl_glAlphaFunc;\n    glad_debug_glAreTexturesResident = glad_debug_impl_glAreTexturesResident;\n    glad_debug_glArrayElement = glad_debug_impl_glArrayElement;\n    glad_debug_glAttachShader = glad_debug_impl_glAttachShader;\n    glad_debug_glBegin = glad_debug_impl_glBegin;\n    glad_debug_glBeginConditionalRender = glad_debug_impl_glBeginConditionalRender;\n    glad_debug_glBeginQuery = glad_debug_impl_glBeginQuery;\n    glad_debug_glBeginTransformFeedback = glad_debug_impl_glBeginTransformFeedback;\n    glad_debug_glBindAttribLocation = glad_debug_impl_glBindAttribLocation;\n    glad_debug_glBindBuffer = glad_debug_impl_glBindBuffer;\n    glad_debug_glBindBufferBase = glad_debug_impl_glBindBufferBase;\n    glad_debug_glBindBufferRange = glad_debug_impl_glBindBufferRange;\n    glad_debug_glBindFragDataLocation = glad_debug_impl_glBindFragDataLocation;\n    glad_debug_glBindFramebuffer = glad_debug_impl_glBindFramebuffer;\n    glad_debug_glBindRenderbuffer = glad_debug_impl_glBindRenderbuffer;\n    glad_debug_glBindTexture = glad_debug_impl_glBindTexture;\n    glad_debug_glBindVertexArray = glad_debug_impl_glBindVertexArray;\n    glad_debug_glBitmap = glad_debug_impl_glBitmap;\n    glad_debug_glBlendColor = glad_debug_impl_glBlendColor;\n    glad_debug_glBlendEquation = glad_debug_impl_glBlendEquation;\n    glad_debug_glBlendEquationSeparate = glad_debug_impl_glBlendEquationSeparate;\n    glad_debug_glBlendFunc = glad_debug_impl_glBlendFunc;\n    glad_debug_glBlendFuncSeparate = glad_debug_impl_glBlendFuncSeparate;\n    glad_debug_glBlitFramebuffer = glad_debug_impl_glBlitFramebuffer;\n    glad_debug_glBufferData = glad_debug_impl_glBufferData;\n    glad_debug_glBufferSubData = glad_debug_impl_glBufferSubData;\n    glad_debug_glCallList = glad_debug_impl_glCallList;\n    glad_debug_glCallLists = glad_debug_impl_glCallLists;\n    glad_debug_glCheckFramebufferStatus = glad_debug_impl_glCheckFramebufferStatus;\n    glad_debug_glClampColor = glad_debug_impl_glClampColor;\n    glad_debug_glClear = glad_debug_impl_glClear;\n    glad_debug_glClearAccum = glad_debug_impl_glClearAccum;\n    glad_debug_glClearBufferfi = glad_debug_impl_glClearBufferfi;\n    glad_debug_glClearBufferfv = glad_debug_impl_glClearBufferfv;\n    glad_debug_glClearBufferiv = glad_debug_impl_glClearBufferiv;\n    glad_debug_glClearBufferuiv = glad_debug_impl_glClearBufferuiv;\n    glad_debug_glClearColor = glad_debug_impl_glClearColor;\n    glad_debug_glClearDepth = glad_debug_impl_glClearDepth;\n    glad_debug_glClearIndex = glad_debug_impl_glClearIndex;\n    glad_debug_glClearStencil = glad_debug_impl_glClearStencil;\n    glad_debug_glClientActiveTexture = glad_debug_impl_glClientActiveTexture;\n    glad_debug_glClipPlane = glad_debug_impl_glClipPlane;\n    glad_debug_glColor3b = glad_debug_impl_glColor3b;\n    glad_debug_glColor3bv = glad_debug_impl_glColor3bv;\n    glad_debug_glColor3d = glad_debug_impl_glColor3d;\n    glad_debug_glColor3dv = glad_debug_impl_glColor3dv;\n    glad_debug_glColor3f = glad_debug_impl_glColor3f;\n    glad_debug_glColor3fv = glad_debug_impl_glColor3fv;\n    glad_debug_glColor3i = glad_debug_impl_glColor3i;\n    glad_debug_glColor3iv = glad_debug_impl_glColor3iv;\n    glad_debug_glColor3s = glad_debug_impl_glColor3s;\n    glad_debug_glColor3sv = glad_debug_impl_glColor3sv;\n    glad_debug_glColor3ub = glad_debug_impl_glColor3ub;\n    glad_debug_glColor3ubv = glad_debug_impl_glColor3ubv;\n    glad_debug_glColor3ui = glad_debug_impl_glColor3ui;\n    glad_debug_glColor3uiv = glad_debug_impl_glColor3uiv;\n    glad_debug_glColor3us = glad_debug_impl_glColor3us;\n    glad_debug_glColor3usv = glad_debug_impl_glColor3usv;\n    glad_debug_glColor4b = glad_debug_impl_glColor4b;\n    glad_debug_glColor4bv = glad_debug_impl_glColor4bv;\n    glad_debug_glColor4d = glad_debug_impl_glColor4d;\n    glad_debug_glColor4dv = glad_debug_impl_glColor4dv;\n    glad_debug_glColor4f = glad_debug_impl_glColor4f;\n    glad_debug_glColor4fv = glad_debug_impl_glColor4fv;\n    glad_debug_glColor4i = glad_debug_impl_glColor4i;\n    glad_debug_glColor4iv = glad_debug_impl_glColor4iv;\n    glad_debug_glColor4s = glad_debug_impl_glColor4s;\n    glad_debug_glColor4sv = glad_debug_impl_glColor4sv;\n    glad_debug_glColor4ub = glad_debug_impl_glColor4ub;\n    glad_debug_glColor4ubv = glad_debug_impl_glColor4ubv;\n    glad_debug_glColor4ui = glad_debug_impl_glColor4ui;\n    glad_debug_glColor4uiv = glad_debug_impl_glColor4uiv;\n    glad_debug_glColor4us = glad_debug_impl_glColor4us;\n    glad_debug_glColor4usv = glad_debug_impl_glColor4usv;\n    glad_debug_glColorMask = glad_debug_impl_glColorMask;\n    glad_debug_glColorMaski = glad_debug_impl_glColorMaski;\n    glad_debug_glColorMaterial = glad_debug_impl_glColorMaterial;\n    glad_debug_glColorPointer = glad_debug_impl_glColorPointer;\n    glad_debug_glCompileShader = glad_debug_impl_glCompileShader;\n    glad_debug_glCompressedTexImage1D = glad_debug_impl_glCompressedTexImage1D;\n    glad_debug_glCompressedTexImage2D = glad_debug_impl_glCompressedTexImage2D;\n    glad_debug_glCompressedTexImage3D = glad_debug_impl_glCompressedTexImage3D;\n    glad_debug_glCompressedTexSubImage1D = glad_debug_impl_glCompressedTexSubImage1D;\n    glad_debug_glCompressedTexSubImage2D = glad_debug_impl_glCompressedTexSubImage2D;\n    glad_debug_glCompressedTexSubImage3D = glad_debug_impl_glCompressedTexSubImage3D;\n    glad_debug_glCopyBufferSubData = glad_debug_impl_glCopyBufferSubData;\n    glad_debug_glCopyImageSubData = glad_debug_impl_glCopyImageSubData;\n    glad_debug_glCopyPixels = glad_debug_impl_glCopyPixels;\n    glad_debug_glCopyTexImage1D = glad_debug_impl_glCopyTexImage1D;\n    glad_debug_glCopyTexImage2D = glad_debug_impl_glCopyTexImage2D;\n    glad_debug_glCopyTexSubImage1D = glad_debug_impl_glCopyTexSubImage1D;\n    glad_debug_glCopyTexSubImage2D = glad_debug_impl_glCopyTexSubImage2D;\n    glad_debug_glCopyTexSubImage3D = glad_debug_impl_glCopyTexSubImage3D;\n    glad_debug_glCreateProgram = glad_debug_impl_glCreateProgram;\n    glad_debug_glCreateShader = glad_debug_impl_glCreateShader;\n    glad_debug_glCullFace = glad_debug_impl_glCullFace;\n    glad_debug_glDebugMessageCallback = glad_debug_impl_glDebugMessageCallback;\n    glad_debug_glDebugMessageControl = glad_debug_impl_glDebugMessageControl;\n    glad_debug_glDebugMessageInsert = glad_debug_impl_glDebugMessageInsert;\n    glad_debug_glDeleteBuffers = glad_debug_impl_glDeleteBuffers;\n    glad_debug_glDeleteFramebuffers = glad_debug_impl_glDeleteFramebuffers;\n    glad_debug_glDeleteLists = glad_debug_impl_glDeleteLists;\n    glad_debug_glDeleteProgram = glad_debug_impl_glDeleteProgram;\n    glad_debug_glDeleteQueries = glad_debug_impl_glDeleteQueries;\n    glad_debug_glDeleteRenderbuffers = glad_debug_impl_glDeleteRenderbuffers;\n    glad_debug_glDeleteShader = glad_debug_impl_glDeleteShader;\n    glad_debug_glDeleteTextures = glad_debug_impl_glDeleteTextures;\n    glad_debug_glDeleteVertexArrays = glad_debug_impl_glDeleteVertexArrays;\n    glad_debug_glDepthFunc = glad_debug_impl_glDepthFunc;\n    glad_debug_glDepthMask = glad_debug_impl_glDepthMask;\n    glad_debug_glDepthRange = glad_debug_impl_glDepthRange;\n    glad_debug_glDetachShader = glad_debug_impl_glDetachShader;\n    glad_debug_glDisable = glad_debug_impl_glDisable;\n    glad_debug_glDisableClientState = glad_debug_impl_glDisableClientState;\n    glad_debug_glDisableVertexAttribArray = glad_debug_impl_glDisableVertexAttribArray;\n    glad_debug_glDisablei = glad_debug_impl_glDisablei;\n    glad_debug_glDrawArrays = glad_debug_impl_glDrawArrays;\n    glad_debug_glDrawArraysInstanced = glad_debug_impl_glDrawArraysInstanced;\n    glad_debug_glDrawBuffer = glad_debug_impl_glDrawBuffer;\n    glad_debug_glDrawBuffers = glad_debug_impl_glDrawBuffers;\n    glad_debug_glDrawElements = glad_debug_impl_glDrawElements;\n    glad_debug_glDrawElementsInstanced = glad_debug_impl_glDrawElementsInstanced;\n    glad_debug_glDrawPixels = glad_debug_impl_glDrawPixels;\n    glad_debug_glDrawRangeElements = glad_debug_impl_glDrawRangeElements;\n    glad_debug_glEdgeFlag = glad_debug_impl_glEdgeFlag;\n    glad_debug_glEdgeFlagPointer = glad_debug_impl_glEdgeFlagPointer;\n    glad_debug_glEdgeFlagv = glad_debug_impl_glEdgeFlagv;\n    glad_debug_glEnable = glad_debug_impl_glEnable;\n    glad_debug_glEnableClientState = glad_debug_impl_glEnableClientState;\n    glad_debug_glEnableVertexAttribArray = glad_debug_impl_glEnableVertexAttribArray;\n    glad_debug_glEnablei = glad_debug_impl_glEnablei;\n    glad_debug_glEnd = glad_debug_impl_glEnd;\n    glad_debug_glEndConditionalRender = glad_debug_impl_glEndConditionalRender;\n    glad_debug_glEndList = glad_debug_impl_glEndList;\n    glad_debug_glEndQuery = glad_debug_impl_glEndQuery;\n    glad_debug_glEndTransformFeedback = glad_debug_impl_glEndTransformFeedback;\n    glad_debug_glEvalCoord1d = glad_debug_impl_glEvalCoord1d;\n    glad_debug_glEvalCoord1dv = glad_debug_impl_glEvalCoord1dv;\n    glad_debug_glEvalCoord1f = glad_debug_impl_glEvalCoord1f;\n    glad_debug_glEvalCoord1fv = glad_debug_impl_glEvalCoord1fv;\n    glad_debug_glEvalCoord2d = glad_debug_impl_glEvalCoord2d;\n    glad_debug_glEvalCoord2dv = glad_debug_impl_glEvalCoord2dv;\n    glad_debug_glEvalCoord2f = glad_debug_impl_glEvalCoord2f;\n    glad_debug_glEvalCoord2fv = glad_debug_impl_glEvalCoord2fv;\n    glad_debug_glEvalMesh1 = glad_debug_impl_glEvalMesh1;\n    glad_debug_glEvalMesh2 = glad_debug_impl_glEvalMesh2;\n    glad_debug_glEvalPoint1 = glad_debug_impl_glEvalPoint1;\n    glad_debug_glEvalPoint2 = glad_debug_impl_glEvalPoint2;\n    glad_debug_glFeedbackBuffer = glad_debug_impl_glFeedbackBuffer;\n    glad_debug_glFinish = glad_debug_impl_glFinish;\n    glad_debug_glFlush = glad_debug_impl_glFlush;\n    glad_debug_glFlushMappedBufferRange = glad_debug_impl_glFlushMappedBufferRange;\n    glad_debug_glFogCoordPointer = glad_debug_impl_glFogCoordPointer;\n    glad_debug_glFogCoordd = glad_debug_impl_glFogCoordd;\n    glad_debug_glFogCoorddv = glad_debug_impl_glFogCoorddv;\n    glad_debug_glFogCoordf = glad_debug_impl_glFogCoordf;\n    glad_debug_glFogCoordfv = glad_debug_impl_glFogCoordfv;\n    glad_debug_glFogf = glad_debug_impl_glFogf;\n    glad_debug_glFogfv = glad_debug_impl_glFogfv;\n    glad_debug_glFogi = glad_debug_impl_glFogi;\n    glad_debug_glFogiv = glad_debug_impl_glFogiv;\n    glad_debug_glFramebufferRenderbuffer = glad_debug_impl_glFramebufferRenderbuffer;\n    glad_debug_glFramebufferTexture1D = glad_debug_impl_glFramebufferTexture1D;\n    glad_debug_glFramebufferTexture2D = glad_debug_impl_glFramebufferTexture2D;\n    glad_debug_glFramebufferTexture3D = glad_debug_impl_glFramebufferTexture3D;\n    glad_debug_glFramebufferTextureLayer = glad_debug_impl_glFramebufferTextureLayer;\n    glad_debug_glFrontFace = glad_debug_impl_glFrontFace;\n    glad_debug_glFrustum = glad_debug_impl_glFrustum;\n    glad_debug_glGenBuffers = glad_debug_impl_glGenBuffers;\n    glad_debug_glGenFramebuffers = glad_debug_impl_glGenFramebuffers;\n    glad_debug_glGenLists = glad_debug_impl_glGenLists;\n    glad_debug_glGenQueries = glad_debug_impl_glGenQueries;\n    glad_debug_glGenRenderbuffers = glad_debug_impl_glGenRenderbuffers;\n    glad_debug_glGenTextures = glad_debug_impl_glGenTextures;\n    glad_debug_glGenVertexArrays = glad_debug_impl_glGenVertexArrays;\n    glad_debug_glGenerateMipmap = glad_debug_impl_glGenerateMipmap;\n    glad_debug_glGetActiveAttrib = glad_debug_impl_glGetActiveAttrib;\n    glad_debug_glGetActiveUniform = glad_debug_impl_glGetActiveUniform;\n    glad_debug_glGetActiveUniformBlockName = glad_debug_impl_glGetActiveUniformBlockName;\n    glad_debug_glGetActiveUniformBlockiv = glad_debug_impl_glGetActiveUniformBlockiv;\n    glad_debug_glGetActiveUniformName = glad_debug_impl_glGetActiveUniformName;\n    glad_debug_glGetActiveUniformsiv = glad_debug_impl_glGetActiveUniformsiv;\n    glad_debug_glGetAttachedShaders = glad_debug_impl_glGetAttachedShaders;\n    glad_debug_glGetAttribLocation = glad_debug_impl_glGetAttribLocation;\n    glad_debug_glGetBooleani_v = glad_debug_impl_glGetBooleani_v;\n    glad_debug_glGetBooleanv = glad_debug_impl_glGetBooleanv;\n    glad_debug_glGetBufferParameteriv = glad_debug_impl_glGetBufferParameteriv;\n    glad_debug_glGetBufferPointerv = glad_debug_impl_glGetBufferPointerv;\n    glad_debug_glGetBufferSubData = glad_debug_impl_glGetBufferSubData;\n    glad_debug_glGetClipPlane = glad_debug_impl_glGetClipPlane;\n    glad_debug_glGetCompressedTexImage = glad_debug_impl_glGetCompressedTexImage;\n    glad_debug_glGetDebugMessageLog = glad_debug_impl_glGetDebugMessageLog;\n    glad_debug_glGetDoublev = glad_debug_impl_glGetDoublev;\n    glad_debug_glGetError = glad_debug_impl_glGetError;\n    glad_debug_glGetFloatv = glad_debug_impl_glGetFloatv;\n    glad_debug_glGetFragDataLocation = glad_debug_impl_glGetFragDataLocation;\n    glad_debug_glGetFramebufferAttachmentParameteriv = glad_debug_impl_glGetFramebufferAttachmentParameteriv;\n    glad_debug_glGetGraphicsResetStatusARB = glad_debug_impl_glGetGraphicsResetStatusARB;\n    glad_debug_glGetIntegeri_v = glad_debug_impl_glGetIntegeri_v;\n    glad_debug_glGetIntegerv = glad_debug_impl_glGetIntegerv;\n    glad_debug_glGetLightfv = glad_debug_impl_glGetLightfv;\n    glad_debug_glGetLightiv = glad_debug_impl_glGetLightiv;\n    glad_debug_glGetMapdv = glad_debug_impl_glGetMapdv;\n    glad_debug_glGetMapfv = glad_debug_impl_glGetMapfv;\n    glad_debug_glGetMapiv = glad_debug_impl_glGetMapiv;\n    glad_debug_glGetMaterialfv = glad_debug_impl_glGetMaterialfv;\n    glad_debug_glGetMaterialiv = glad_debug_impl_glGetMaterialiv;\n    glad_debug_glGetObjectLabel = glad_debug_impl_glGetObjectLabel;\n    glad_debug_glGetObjectPtrLabel = glad_debug_impl_glGetObjectPtrLabel;\n    glad_debug_glGetPixelMapfv = glad_debug_impl_glGetPixelMapfv;\n    glad_debug_glGetPixelMapuiv = glad_debug_impl_glGetPixelMapuiv;\n    glad_debug_glGetPixelMapusv = glad_debug_impl_glGetPixelMapusv;\n    glad_debug_glGetPointerv = glad_debug_impl_glGetPointerv;\n    glad_debug_glGetPolygonStipple = glad_debug_impl_glGetPolygonStipple;\n    glad_debug_glGetProgramInfoLog = glad_debug_impl_glGetProgramInfoLog;\n    glad_debug_glGetProgramiv = glad_debug_impl_glGetProgramiv;\n    glad_debug_glGetQueryObjectiv = glad_debug_impl_glGetQueryObjectiv;\n    glad_debug_glGetQueryObjectuiv = glad_debug_impl_glGetQueryObjectuiv;\n    glad_debug_glGetQueryiv = glad_debug_impl_glGetQueryiv;\n    glad_debug_glGetRenderbufferParameteriv = glad_debug_impl_glGetRenderbufferParameteriv;\n    glad_debug_glGetShaderInfoLog = glad_debug_impl_glGetShaderInfoLog;\n    glad_debug_glGetShaderSource = glad_debug_impl_glGetShaderSource;\n    glad_debug_glGetShaderiv = glad_debug_impl_glGetShaderiv;\n    glad_debug_glGetString = glad_debug_impl_glGetString;\n    glad_debug_glGetStringi = glad_debug_impl_glGetStringi;\n    glad_debug_glGetTexEnvfv = glad_debug_impl_glGetTexEnvfv;\n    glad_debug_glGetTexEnviv = glad_debug_impl_glGetTexEnviv;\n    glad_debug_glGetTexGendv = glad_debug_impl_glGetTexGendv;\n    glad_debug_glGetTexGenfv = glad_debug_impl_glGetTexGenfv;\n    glad_debug_glGetTexGeniv = glad_debug_impl_glGetTexGeniv;\n    glad_debug_glGetTexImage = glad_debug_impl_glGetTexImage;\n    glad_debug_glGetTexLevelParameterfv = glad_debug_impl_glGetTexLevelParameterfv;\n    glad_debug_glGetTexLevelParameteriv = glad_debug_impl_glGetTexLevelParameteriv;\n    glad_debug_glGetTexParameterIiv = glad_debug_impl_glGetTexParameterIiv;\n    glad_debug_glGetTexParameterIuiv = glad_debug_impl_glGetTexParameterIuiv;\n    glad_debug_glGetTexParameterfv = glad_debug_impl_glGetTexParameterfv;\n    glad_debug_glGetTexParameteriv = glad_debug_impl_glGetTexParameteriv;\n    glad_debug_glGetTransformFeedbackVarying = glad_debug_impl_glGetTransformFeedbackVarying;\n    glad_debug_glGetUniformBlockIndex = glad_debug_impl_glGetUniformBlockIndex;\n    glad_debug_glGetUniformIndices = glad_debug_impl_glGetUniformIndices;\n    glad_debug_glGetUniformLocation = glad_debug_impl_glGetUniformLocation;\n    glad_debug_glGetUniformfv = glad_debug_impl_glGetUniformfv;\n    glad_debug_glGetUniformiv = glad_debug_impl_glGetUniformiv;\n    glad_debug_glGetUniformuiv = glad_debug_impl_glGetUniformuiv;\n    glad_debug_glGetVertexAttribIiv = glad_debug_impl_glGetVertexAttribIiv;\n    glad_debug_glGetVertexAttribIuiv = glad_debug_impl_glGetVertexAttribIuiv;\n    glad_debug_glGetVertexAttribPointerv = glad_debug_impl_glGetVertexAttribPointerv;\n    glad_debug_glGetVertexAttribdv = glad_debug_impl_glGetVertexAttribdv;\n    glad_debug_glGetVertexAttribfv = glad_debug_impl_glGetVertexAttribfv;\n    glad_debug_glGetVertexAttribiv = glad_debug_impl_glGetVertexAttribiv;\n    glad_debug_glGetnCompressedTexImageARB = glad_debug_impl_glGetnCompressedTexImageARB;\n    glad_debug_glGetnTexImageARB = glad_debug_impl_glGetnTexImageARB;\n    glad_debug_glGetnUniformdvARB = glad_debug_impl_glGetnUniformdvARB;\n    glad_debug_glGetnUniformfvARB = glad_debug_impl_glGetnUniformfvARB;\n    glad_debug_glGetnUniformivARB = glad_debug_impl_glGetnUniformivARB;\n    glad_debug_glGetnUniformuivARB = glad_debug_impl_glGetnUniformuivARB;\n    glad_debug_glHint = glad_debug_impl_glHint;\n    glad_debug_glIndexMask = glad_debug_impl_glIndexMask;\n    glad_debug_glIndexPointer = glad_debug_impl_glIndexPointer;\n    glad_debug_glIndexd = glad_debug_impl_glIndexd;\n    glad_debug_glIndexdv = glad_debug_impl_glIndexdv;\n    glad_debug_glIndexf = glad_debug_impl_glIndexf;\n    glad_debug_glIndexfv = glad_debug_impl_glIndexfv;\n    glad_debug_glIndexi = glad_debug_impl_glIndexi;\n    glad_debug_glIndexiv = glad_debug_impl_glIndexiv;\n    glad_debug_glIndexs = glad_debug_impl_glIndexs;\n    glad_debug_glIndexsv = glad_debug_impl_glIndexsv;\n    glad_debug_glIndexub = glad_debug_impl_glIndexub;\n    glad_debug_glIndexubv = glad_debug_impl_glIndexubv;\n    glad_debug_glInitNames = glad_debug_impl_glInitNames;\n    glad_debug_glInterleavedArrays = glad_debug_impl_glInterleavedArrays;\n    glad_debug_glIsBuffer = glad_debug_impl_glIsBuffer;\n    glad_debug_glIsEnabled = glad_debug_impl_glIsEnabled;\n    glad_debug_glIsEnabledi = glad_debug_impl_glIsEnabledi;\n    glad_debug_glIsFramebuffer = glad_debug_impl_glIsFramebuffer;\n    glad_debug_glIsList = glad_debug_impl_glIsList;\n    glad_debug_glIsProgram = glad_debug_impl_glIsProgram;\n    glad_debug_glIsQuery = glad_debug_impl_glIsQuery;\n    glad_debug_glIsRenderbuffer = glad_debug_impl_glIsRenderbuffer;\n    glad_debug_glIsShader = glad_debug_impl_glIsShader;\n    glad_debug_glIsTexture = glad_debug_impl_glIsTexture;\n    glad_debug_glIsVertexArray = glad_debug_impl_glIsVertexArray;\n    glad_debug_glLightModelf = glad_debug_impl_glLightModelf;\n    glad_debug_glLightModelfv = glad_debug_impl_glLightModelfv;\n    glad_debug_glLightModeli = glad_debug_impl_glLightModeli;\n    glad_debug_glLightModeliv = glad_debug_impl_glLightModeliv;\n    glad_debug_glLightf = glad_debug_impl_glLightf;\n    glad_debug_glLightfv = glad_debug_impl_glLightfv;\n    glad_debug_glLighti = glad_debug_impl_glLighti;\n    glad_debug_glLightiv = glad_debug_impl_glLightiv;\n    glad_debug_glLineStipple = glad_debug_impl_glLineStipple;\n    glad_debug_glLineWidth = glad_debug_impl_glLineWidth;\n    glad_debug_glLinkProgram = glad_debug_impl_glLinkProgram;\n    glad_debug_glListBase = glad_debug_impl_glListBase;\n    glad_debug_glLoadIdentity = glad_debug_impl_glLoadIdentity;\n    glad_debug_glLoadMatrixd = glad_debug_impl_glLoadMatrixd;\n    glad_debug_glLoadMatrixf = glad_debug_impl_glLoadMatrixf;\n    glad_debug_glLoadName = glad_debug_impl_glLoadName;\n    glad_debug_glLoadTransposeMatrixd = glad_debug_impl_glLoadTransposeMatrixd;\n    glad_debug_glLoadTransposeMatrixf = glad_debug_impl_glLoadTransposeMatrixf;\n    glad_debug_glLogicOp = glad_debug_impl_glLogicOp;\n    glad_debug_glMap1d = glad_debug_impl_glMap1d;\n    glad_debug_glMap1f = glad_debug_impl_glMap1f;\n    glad_debug_glMap2d = glad_debug_impl_glMap2d;\n    glad_debug_glMap2f = glad_debug_impl_glMap2f;\n    glad_debug_glMapBuffer = glad_debug_impl_glMapBuffer;\n    glad_debug_glMapBufferRange = glad_debug_impl_glMapBufferRange;\n    glad_debug_glMapGrid1d = glad_debug_impl_glMapGrid1d;\n    glad_debug_glMapGrid1f = glad_debug_impl_glMapGrid1f;\n    glad_debug_glMapGrid2d = glad_debug_impl_glMapGrid2d;\n    glad_debug_glMapGrid2f = glad_debug_impl_glMapGrid2f;\n    glad_debug_glMaterialf = glad_debug_impl_glMaterialf;\n    glad_debug_glMaterialfv = glad_debug_impl_glMaterialfv;\n    glad_debug_glMateriali = glad_debug_impl_glMateriali;\n    glad_debug_glMaterialiv = glad_debug_impl_glMaterialiv;\n    glad_debug_glMatrixMode = glad_debug_impl_glMatrixMode;\n    glad_debug_glMultMatrixd = glad_debug_impl_glMultMatrixd;\n    glad_debug_glMultMatrixf = glad_debug_impl_glMultMatrixf;\n    glad_debug_glMultTransposeMatrixd = glad_debug_impl_glMultTransposeMatrixd;\n    glad_debug_glMultTransposeMatrixf = glad_debug_impl_glMultTransposeMatrixf;\n    glad_debug_glMultiDrawArrays = glad_debug_impl_glMultiDrawArrays;\n    glad_debug_glMultiDrawElements = glad_debug_impl_glMultiDrawElements;\n    glad_debug_glMultiTexCoord1d = glad_debug_impl_glMultiTexCoord1d;\n    glad_debug_glMultiTexCoord1dv = glad_debug_impl_glMultiTexCoord1dv;\n    glad_debug_glMultiTexCoord1f = glad_debug_impl_glMultiTexCoord1f;\n    glad_debug_glMultiTexCoord1fv = glad_debug_impl_glMultiTexCoord1fv;\n    glad_debug_glMultiTexCoord1i = glad_debug_impl_glMultiTexCoord1i;\n    glad_debug_glMultiTexCoord1iv = glad_debug_impl_glMultiTexCoord1iv;\n    glad_debug_glMultiTexCoord1s = glad_debug_impl_glMultiTexCoord1s;\n    glad_debug_glMultiTexCoord1sv = glad_debug_impl_glMultiTexCoord1sv;\n    glad_debug_glMultiTexCoord2d = glad_debug_impl_glMultiTexCoord2d;\n    glad_debug_glMultiTexCoord2dv = glad_debug_impl_glMultiTexCoord2dv;\n    glad_debug_glMultiTexCoord2f = glad_debug_impl_glMultiTexCoord2f;\n    glad_debug_glMultiTexCoord2fv = glad_debug_impl_glMultiTexCoord2fv;\n    glad_debug_glMultiTexCoord2i = glad_debug_impl_glMultiTexCoord2i;\n    glad_debug_glMultiTexCoord2iv = glad_debug_impl_glMultiTexCoord2iv;\n    glad_debug_glMultiTexCoord2s = glad_debug_impl_glMultiTexCoord2s;\n    glad_debug_glMultiTexCoord2sv = glad_debug_impl_glMultiTexCoord2sv;\n    glad_debug_glMultiTexCoord3d = glad_debug_impl_glMultiTexCoord3d;\n    glad_debug_glMultiTexCoord3dv = glad_debug_impl_glMultiTexCoord3dv;\n    glad_debug_glMultiTexCoord3f = glad_debug_impl_glMultiTexCoord3f;\n    glad_debug_glMultiTexCoord3fv = glad_debug_impl_glMultiTexCoord3fv;\n    glad_debug_glMultiTexCoord3i = glad_debug_impl_glMultiTexCoord3i;\n    glad_debug_glMultiTexCoord3iv = glad_debug_impl_glMultiTexCoord3iv;\n    glad_debug_glMultiTexCoord3s = glad_debug_impl_glMultiTexCoord3s;\n    glad_debug_glMultiTexCoord3sv = glad_debug_impl_glMultiTexCoord3sv;\n    glad_debug_glMultiTexCoord4d = glad_debug_impl_glMultiTexCoord4d;\n    glad_debug_glMultiTexCoord4dv = glad_debug_impl_glMultiTexCoord4dv;\n    glad_debug_glMultiTexCoord4f = glad_debug_impl_glMultiTexCoord4f;\n    glad_debug_glMultiTexCoord4fv = glad_debug_impl_glMultiTexCoord4fv;\n    glad_debug_glMultiTexCoord4i = glad_debug_impl_glMultiTexCoord4i;\n    glad_debug_glMultiTexCoord4iv = glad_debug_impl_glMultiTexCoord4iv;\n    glad_debug_glMultiTexCoord4s = glad_debug_impl_glMultiTexCoord4s;\n    glad_debug_glMultiTexCoord4sv = glad_debug_impl_glMultiTexCoord4sv;\n    glad_debug_glNewList = glad_debug_impl_glNewList;\n    glad_debug_glNormal3b = glad_debug_impl_glNormal3b;\n    glad_debug_glNormal3bv = glad_debug_impl_glNormal3bv;\n    glad_debug_glNormal3d = glad_debug_impl_glNormal3d;\n    glad_debug_glNormal3dv = glad_debug_impl_glNormal3dv;\n    glad_debug_glNormal3f = glad_debug_impl_glNormal3f;\n    glad_debug_glNormal3fv = glad_debug_impl_glNormal3fv;\n    glad_debug_glNormal3i = glad_debug_impl_glNormal3i;\n    glad_debug_glNormal3iv = glad_debug_impl_glNormal3iv;\n    glad_debug_glNormal3s = glad_debug_impl_glNormal3s;\n    glad_debug_glNormal3sv = glad_debug_impl_glNormal3sv;\n    glad_debug_glNormalPointer = glad_debug_impl_glNormalPointer;\n    glad_debug_glObjectLabel = glad_debug_impl_glObjectLabel;\n    glad_debug_glObjectPtrLabel = glad_debug_impl_glObjectPtrLabel;\n    glad_debug_glOrtho = glad_debug_impl_glOrtho;\n    glad_debug_glPassThrough = glad_debug_impl_glPassThrough;\n    glad_debug_glPixelMapfv = glad_debug_impl_glPixelMapfv;\n    glad_debug_glPixelMapuiv = glad_debug_impl_glPixelMapuiv;\n    glad_debug_glPixelMapusv = glad_debug_impl_glPixelMapusv;\n    glad_debug_glPixelStoref = glad_debug_impl_glPixelStoref;\n    glad_debug_glPixelStorei = glad_debug_impl_glPixelStorei;\n    glad_debug_glPixelTransferf = glad_debug_impl_glPixelTransferf;\n    glad_debug_glPixelTransferi = glad_debug_impl_glPixelTransferi;\n    glad_debug_glPixelZoom = glad_debug_impl_glPixelZoom;\n    glad_debug_glPointParameterf = glad_debug_impl_glPointParameterf;\n    glad_debug_glPointParameterfv = glad_debug_impl_glPointParameterfv;\n    glad_debug_glPointParameteri = glad_debug_impl_glPointParameteri;\n    glad_debug_glPointParameteriv = glad_debug_impl_glPointParameteriv;\n    glad_debug_glPointSize = glad_debug_impl_glPointSize;\n    glad_debug_glPolygonMode = glad_debug_impl_glPolygonMode;\n    glad_debug_glPolygonOffset = glad_debug_impl_glPolygonOffset;\n    glad_debug_glPolygonStipple = glad_debug_impl_glPolygonStipple;\n    glad_debug_glPopAttrib = glad_debug_impl_glPopAttrib;\n    glad_debug_glPopClientAttrib = glad_debug_impl_glPopClientAttrib;\n    glad_debug_glPopDebugGroup = glad_debug_impl_glPopDebugGroup;\n    glad_debug_glPopMatrix = glad_debug_impl_glPopMatrix;\n    glad_debug_glPopName = glad_debug_impl_glPopName;\n    glad_debug_glPrimitiveRestartIndex = glad_debug_impl_glPrimitiveRestartIndex;\n    glad_debug_glPrioritizeTextures = glad_debug_impl_glPrioritizeTextures;\n    glad_debug_glPushAttrib = glad_debug_impl_glPushAttrib;\n    glad_debug_glPushClientAttrib = glad_debug_impl_glPushClientAttrib;\n    glad_debug_glPushDebugGroup = glad_debug_impl_glPushDebugGroup;\n    glad_debug_glPushMatrix = glad_debug_impl_glPushMatrix;\n    glad_debug_glPushName = glad_debug_impl_glPushName;\n    glad_debug_glRasterPos2d = glad_debug_impl_glRasterPos2d;\n    glad_debug_glRasterPos2dv = glad_debug_impl_glRasterPos2dv;\n    glad_debug_glRasterPos2f = glad_debug_impl_glRasterPos2f;\n    glad_debug_glRasterPos2fv = glad_debug_impl_glRasterPos2fv;\n    glad_debug_glRasterPos2i = glad_debug_impl_glRasterPos2i;\n    glad_debug_glRasterPos2iv = glad_debug_impl_glRasterPos2iv;\n    glad_debug_glRasterPos2s = glad_debug_impl_glRasterPos2s;\n    glad_debug_glRasterPos2sv = glad_debug_impl_glRasterPos2sv;\n    glad_debug_glRasterPos3d = glad_debug_impl_glRasterPos3d;\n    glad_debug_glRasterPos3dv = glad_debug_impl_glRasterPos3dv;\n    glad_debug_glRasterPos3f = glad_debug_impl_glRasterPos3f;\n    glad_debug_glRasterPos3fv = glad_debug_impl_glRasterPos3fv;\n    glad_debug_glRasterPos3i = glad_debug_impl_glRasterPos3i;\n    glad_debug_glRasterPos3iv = glad_debug_impl_glRasterPos3iv;\n    glad_debug_glRasterPos3s = glad_debug_impl_glRasterPos3s;\n    glad_debug_glRasterPos3sv = glad_debug_impl_glRasterPos3sv;\n    glad_debug_glRasterPos4d = glad_debug_impl_glRasterPos4d;\n    glad_debug_glRasterPos4dv = glad_debug_impl_glRasterPos4dv;\n    glad_debug_glRasterPos4f = glad_debug_impl_glRasterPos4f;\n    glad_debug_glRasterPos4fv = glad_debug_impl_glRasterPos4fv;\n    glad_debug_glRasterPos4i = glad_debug_impl_glRasterPos4i;\n    glad_debug_glRasterPos4iv = glad_debug_impl_glRasterPos4iv;\n    glad_debug_glRasterPos4s = glad_debug_impl_glRasterPos4s;\n    glad_debug_glRasterPos4sv = glad_debug_impl_glRasterPos4sv;\n    glad_debug_glReadBuffer = glad_debug_impl_glReadBuffer;\n    glad_debug_glReadPixels = glad_debug_impl_glReadPixels;\n    glad_debug_glReadnPixelsARB = glad_debug_impl_glReadnPixelsARB;\n    glad_debug_glRectd = glad_debug_impl_glRectd;\n    glad_debug_glRectdv = glad_debug_impl_glRectdv;\n    glad_debug_glRectf = glad_debug_impl_glRectf;\n    glad_debug_glRectfv = glad_debug_impl_glRectfv;\n    glad_debug_glRecti = glad_debug_impl_glRecti;\n    glad_debug_glRectiv = glad_debug_impl_glRectiv;\n    glad_debug_glRects = glad_debug_impl_glRects;\n    glad_debug_glRectsv = glad_debug_impl_glRectsv;\n    glad_debug_glRenderMode = glad_debug_impl_glRenderMode;\n    glad_debug_glRenderbufferStorage = glad_debug_impl_glRenderbufferStorage;\n    glad_debug_glRenderbufferStorageMultisample = glad_debug_impl_glRenderbufferStorageMultisample;\n    glad_debug_glRotated = glad_debug_impl_glRotated;\n    glad_debug_glRotatef = glad_debug_impl_glRotatef;\n    glad_debug_glSampleCoverage = glad_debug_impl_glSampleCoverage;\n    glad_debug_glSampleCoverageARB = glad_debug_impl_glSampleCoverageARB;\n    glad_debug_glScaled = glad_debug_impl_glScaled;\n    glad_debug_glScalef = glad_debug_impl_glScalef;\n    glad_debug_glScissor = glad_debug_impl_glScissor;\n    glad_debug_glSecondaryColor3b = glad_debug_impl_glSecondaryColor3b;\n    glad_debug_glSecondaryColor3bv = glad_debug_impl_glSecondaryColor3bv;\n    glad_debug_glSecondaryColor3d = glad_debug_impl_glSecondaryColor3d;\n    glad_debug_glSecondaryColor3dv = glad_debug_impl_glSecondaryColor3dv;\n    glad_debug_glSecondaryColor3f = glad_debug_impl_glSecondaryColor3f;\n    glad_debug_glSecondaryColor3fv = glad_debug_impl_glSecondaryColor3fv;\n    glad_debug_glSecondaryColor3i = glad_debug_impl_glSecondaryColor3i;\n    glad_debug_glSecondaryColor3iv = glad_debug_impl_glSecondaryColor3iv;\n    glad_debug_glSecondaryColor3s = glad_debug_impl_glSecondaryColor3s;\n    glad_debug_glSecondaryColor3sv = glad_debug_impl_glSecondaryColor3sv;\n    glad_debug_glSecondaryColor3ub = glad_debug_impl_glSecondaryColor3ub;\n    glad_debug_glSecondaryColor3ubv = glad_debug_impl_glSecondaryColor3ubv;\n    glad_debug_glSecondaryColor3ui = glad_debug_impl_glSecondaryColor3ui;\n    glad_debug_glSecondaryColor3uiv = glad_debug_impl_glSecondaryColor3uiv;\n    glad_debug_glSecondaryColor3us = glad_debug_impl_glSecondaryColor3us;\n    glad_debug_glSecondaryColor3usv = glad_debug_impl_glSecondaryColor3usv;\n    glad_debug_glSecondaryColorPointer = glad_debug_impl_glSecondaryColorPointer;\n    glad_debug_glSelectBuffer = glad_debug_impl_glSelectBuffer;\n    glad_debug_glShadeModel = glad_debug_impl_glShadeModel;\n    glad_debug_glShaderSource = glad_debug_impl_glShaderSource;\n    glad_debug_glStencilFunc = glad_debug_impl_glStencilFunc;\n    glad_debug_glStencilFuncSeparate = glad_debug_impl_glStencilFuncSeparate;\n    glad_debug_glStencilMask = glad_debug_impl_glStencilMask;\n    glad_debug_glStencilMaskSeparate = glad_debug_impl_glStencilMaskSeparate;\n    glad_debug_glStencilOp = glad_debug_impl_glStencilOp;\n    glad_debug_glStencilOpSeparate = glad_debug_impl_glStencilOpSeparate;\n    glad_debug_glTexBuffer = glad_debug_impl_glTexBuffer;\n    glad_debug_glTexCoord1d = glad_debug_impl_glTexCoord1d;\n    glad_debug_glTexCoord1dv = glad_debug_impl_glTexCoord1dv;\n    glad_debug_glTexCoord1f = glad_debug_impl_glTexCoord1f;\n    glad_debug_glTexCoord1fv = glad_debug_impl_glTexCoord1fv;\n    glad_debug_glTexCoord1i = glad_debug_impl_glTexCoord1i;\n    glad_debug_glTexCoord1iv = glad_debug_impl_glTexCoord1iv;\n    glad_debug_glTexCoord1s = glad_debug_impl_glTexCoord1s;\n    glad_debug_glTexCoord1sv = glad_debug_impl_glTexCoord1sv;\n    glad_debug_glTexCoord2d = glad_debug_impl_glTexCoord2d;\n    glad_debug_glTexCoord2dv = glad_debug_impl_glTexCoord2dv;\n    glad_debug_glTexCoord2f = glad_debug_impl_glTexCoord2f;\n    glad_debug_glTexCoord2fv = glad_debug_impl_glTexCoord2fv;\n    glad_debug_glTexCoord2i = glad_debug_impl_glTexCoord2i;\n    glad_debug_glTexCoord2iv = glad_debug_impl_glTexCoord2iv;\n    glad_debug_glTexCoord2s = glad_debug_impl_glTexCoord2s;\n    glad_debug_glTexCoord2sv = glad_debug_impl_glTexCoord2sv;\n    glad_debug_glTexCoord3d = glad_debug_impl_glTexCoord3d;\n    glad_debug_glTexCoord3dv = glad_debug_impl_glTexCoord3dv;\n    glad_debug_glTexCoord3f = glad_debug_impl_glTexCoord3f;\n    glad_debug_glTexCoord3fv = glad_debug_impl_glTexCoord3fv;\n    glad_debug_glTexCoord3i = glad_debug_impl_glTexCoord3i;\n    glad_debug_glTexCoord3iv = glad_debug_impl_glTexCoord3iv;\n    glad_debug_glTexCoord3s = glad_debug_impl_glTexCoord3s;\n    glad_debug_glTexCoord3sv = glad_debug_impl_glTexCoord3sv;\n    glad_debug_glTexCoord4d = glad_debug_impl_glTexCoord4d;\n    glad_debug_glTexCoord4dv = glad_debug_impl_glTexCoord4dv;\n    glad_debug_glTexCoord4f = glad_debug_impl_glTexCoord4f;\n    glad_debug_glTexCoord4fv = glad_debug_impl_glTexCoord4fv;\n    glad_debug_glTexCoord4i = glad_debug_impl_glTexCoord4i;\n    glad_debug_glTexCoord4iv = glad_debug_impl_glTexCoord4iv;\n    glad_debug_glTexCoord4s = glad_debug_impl_glTexCoord4s;\n    glad_debug_glTexCoord4sv = glad_debug_impl_glTexCoord4sv;\n    glad_debug_glTexCoordPointer = glad_debug_impl_glTexCoordPointer;\n    glad_debug_glTexEnvf = glad_debug_impl_glTexEnvf;\n    glad_debug_glTexEnvfv = glad_debug_impl_glTexEnvfv;\n    glad_debug_glTexEnvi = glad_debug_impl_glTexEnvi;\n    glad_debug_glTexEnviv = glad_debug_impl_glTexEnviv;\n    glad_debug_glTexGend = glad_debug_impl_glTexGend;\n    glad_debug_glTexGendv = glad_debug_impl_glTexGendv;\n    glad_debug_glTexGenf = glad_debug_impl_glTexGenf;\n    glad_debug_glTexGenfv = glad_debug_impl_glTexGenfv;\n    glad_debug_glTexGeni = glad_debug_impl_glTexGeni;\n    glad_debug_glTexGeniv = glad_debug_impl_glTexGeniv;\n    glad_debug_glTexImage1D = glad_debug_impl_glTexImage1D;\n    glad_debug_glTexImage2D = glad_debug_impl_glTexImage2D;\n    glad_debug_glTexImage3D = glad_debug_impl_glTexImage3D;\n    glad_debug_glTexParameterIiv = glad_debug_impl_glTexParameterIiv;\n    glad_debug_glTexParameterIuiv = glad_debug_impl_glTexParameterIuiv;\n    glad_debug_glTexParameterf = glad_debug_impl_glTexParameterf;\n    glad_debug_glTexParameterfv = glad_debug_impl_glTexParameterfv;\n    glad_debug_glTexParameteri = glad_debug_impl_glTexParameteri;\n    glad_debug_glTexParameteriv = glad_debug_impl_glTexParameteriv;\n    glad_debug_glTexStorage1D = glad_debug_impl_glTexStorage1D;\n    glad_debug_glTexStorage2D = glad_debug_impl_glTexStorage2D;\n    glad_debug_glTexStorage3D = glad_debug_impl_glTexStorage3D;\n    glad_debug_glTexSubImage1D = glad_debug_impl_glTexSubImage1D;\n    glad_debug_glTexSubImage2D = glad_debug_impl_glTexSubImage2D;\n    glad_debug_glTexSubImage3D = glad_debug_impl_glTexSubImage3D;\n    glad_debug_glTransformFeedbackVaryings = glad_debug_impl_glTransformFeedbackVaryings;\n    glad_debug_glTranslated = glad_debug_impl_glTranslated;\n    glad_debug_glTranslatef = glad_debug_impl_glTranslatef;\n    glad_debug_glUniform1f = glad_debug_impl_glUniform1f;\n    glad_debug_glUniform1fv = glad_debug_impl_glUniform1fv;\n    glad_debug_glUniform1i = glad_debug_impl_glUniform1i;\n    glad_debug_glUniform1iv = glad_debug_impl_glUniform1iv;\n    glad_debug_glUniform1ui = glad_debug_impl_glUniform1ui;\n    glad_debug_glUniform1uiv = glad_debug_impl_glUniform1uiv;\n    glad_debug_glUniform2f = glad_debug_impl_glUniform2f;\n    glad_debug_glUniform2fv = glad_debug_impl_glUniform2fv;\n    glad_debug_glUniform2i = glad_debug_impl_glUniform2i;\n    glad_debug_glUniform2iv = glad_debug_impl_glUniform2iv;\n    glad_debug_glUniform2ui = glad_debug_impl_glUniform2ui;\n    glad_debug_glUniform2uiv = glad_debug_impl_glUniform2uiv;\n    glad_debug_glUniform3f = glad_debug_impl_glUniform3f;\n    glad_debug_glUniform3fv = glad_debug_impl_glUniform3fv;\n    glad_debug_glUniform3i = glad_debug_impl_glUniform3i;\n    glad_debug_glUniform3iv = glad_debug_impl_glUniform3iv;\n    glad_debug_glUniform3ui = glad_debug_impl_glUniform3ui;\n    glad_debug_glUniform3uiv = glad_debug_impl_glUniform3uiv;\n    glad_debug_glUniform4f = glad_debug_impl_glUniform4f;\n    glad_debug_glUniform4fv = glad_debug_impl_glUniform4fv;\n    glad_debug_glUniform4i = glad_debug_impl_glUniform4i;\n    glad_debug_glUniform4iv = glad_debug_impl_glUniform4iv;\n    glad_debug_glUniform4ui = glad_debug_impl_glUniform4ui;\n    glad_debug_glUniform4uiv = glad_debug_impl_glUniform4uiv;\n    glad_debug_glUniformBlockBinding = glad_debug_impl_glUniformBlockBinding;\n    glad_debug_glUniformMatrix2fv = glad_debug_impl_glUniformMatrix2fv;\n    glad_debug_glUniformMatrix2x3fv = glad_debug_impl_glUniformMatrix2x3fv;\n    glad_debug_glUniformMatrix2x4fv = glad_debug_impl_glUniformMatrix2x4fv;\n    glad_debug_glUniformMatrix3fv = glad_debug_impl_glUniformMatrix3fv;\n    glad_debug_glUniformMatrix3x2fv = glad_debug_impl_glUniformMatrix3x2fv;\n    glad_debug_glUniformMatrix3x4fv = glad_debug_impl_glUniformMatrix3x4fv;\n    glad_debug_glUniformMatrix4fv = glad_debug_impl_glUniformMatrix4fv;\n    glad_debug_glUniformMatrix4x2fv = glad_debug_impl_glUniformMatrix4x2fv;\n    glad_debug_glUniformMatrix4x3fv = glad_debug_impl_glUniformMatrix4x3fv;\n    glad_debug_glUnmapBuffer = glad_debug_impl_glUnmapBuffer;\n    glad_debug_glUseProgram = glad_debug_impl_glUseProgram;\n    glad_debug_glValidateProgram = glad_debug_impl_glValidateProgram;\n    glad_debug_glVertex2d = glad_debug_impl_glVertex2d;\n    glad_debug_glVertex2dv = glad_debug_impl_glVertex2dv;\n    glad_debug_glVertex2f = glad_debug_impl_glVertex2f;\n    glad_debug_glVertex2fv = glad_debug_impl_glVertex2fv;\n    glad_debug_glVertex2i = glad_debug_impl_glVertex2i;\n    glad_debug_glVertex2iv = glad_debug_impl_glVertex2iv;\n    glad_debug_glVertex2s = glad_debug_impl_glVertex2s;\n    glad_debug_glVertex2sv = glad_debug_impl_glVertex2sv;\n    glad_debug_glVertex3d = glad_debug_impl_glVertex3d;\n    glad_debug_glVertex3dv = glad_debug_impl_glVertex3dv;\n    glad_debug_glVertex3f = glad_debug_impl_glVertex3f;\n    glad_debug_glVertex3fv = glad_debug_impl_glVertex3fv;\n    glad_debug_glVertex3i = glad_debug_impl_glVertex3i;\n    glad_debug_glVertex3iv = glad_debug_impl_glVertex3iv;\n    glad_debug_glVertex3s = glad_debug_impl_glVertex3s;\n    glad_debug_glVertex3sv = glad_debug_impl_glVertex3sv;\n    glad_debug_glVertex4d = glad_debug_impl_glVertex4d;\n    glad_debug_glVertex4dv = glad_debug_impl_glVertex4dv;\n    glad_debug_glVertex4f = glad_debug_impl_glVertex4f;\n    glad_debug_glVertex4fv = glad_debug_impl_glVertex4fv;\n    glad_debug_glVertex4i = glad_debug_impl_glVertex4i;\n    glad_debug_glVertex4iv = glad_debug_impl_glVertex4iv;\n    glad_debug_glVertex4s = glad_debug_impl_glVertex4s;\n    glad_debug_glVertex4sv = glad_debug_impl_glVertex4sv;\n    glad_debug_glVertexAttrib1d = glad_debug_impl_glVertexAttrib1d;\n    glad_debug_glVertexAttrib1dv = glad_debug_impl_glVertexAttrib1dv;\n    glad_debug_glVertexAttrib1f = glad_debug_impl_glVertexAttrib1f;\n    glad_debug_glVertexAttrib1fv = glad_debug_impl_glVertexAttrib1fv;\n    glad_debug_glVertexAttrib1s = glad_debug_impl_glVertexAttrib1s;\n    glad_debug_glVertexAttrib1sv = glad_debug_impl_glVertexAttrib1sv;\n    glad_debug_glVertexAttrib2d = glad_debug_impl_glVertexAttrib2d;\n    glad_debug_glVertexAttrib2dv = glad_debug_impl_glVertexAttrib2dv;\n    glad_debug_glVertexAttrib2f = glad_debug_impl_glVertexAttrib2f;\n    glad_debug_glVertexAttrib2fv = glad_debug_impl_glVertexAttrib2fv;\n    glad_debug_glVertexAttrib2s = glad_debug_impl_glVertexAttrib2s;\n    glad_debug_glVertexAttrib2sv = glad_debug_impl_glVertexAttrib2sv;\n    glad_debug_glVertexAttrib3d = glad_debug_impl_glVertexAttrib3d;\n    glad_debug_glVertexAttrib3dv = glad_debug_impl_glVertexAttrib3dv;\n    glad_debug_glVertexAttrib3f = glad_debug_impl_glVertexAttrib3f;\n    glad_debug_glVertexAttrib3fv = glad_debug_impl_glVertexAttrib3fv;\n    glad_debug_glVertexAttrib3s = glad_debug_impl_glVertexAttrib3s;\n    glad_debug_glVertexAttrib3sv = glad_debug_impl_glVertexAttrib3sv;\n    glad_debug_glVertexAttrib4Nbv = glad_debug_impl_glVertexAttrib4Nbv;\n    glad_debug_glVertexAttrib4Niv = glad_debug_impl_glVertexAttrib4Niv;\n    glad_debug_glVertexAttrib4Nsv = glad_debug_impl_glVertexAttrib4Nsv;\n    glad_debug_glVertexAttrib4Nub = glad_debug_impl_glVertexAttrib4Nub;\n    glad_debug_glVertexAttrib4Nubv = glad_debug_impl_glVertexAttrib4Nubv;\n    glad_debug_glVertexAttrib4Nuiv = glad_debug_impl_glVertexAttrib4Nuiv;\n    glad_debug_glVertexAttrib4Nusv = glad_debug_impl_glVertexAttrib4Nusv;\n    glad_debug_glVertexAttrib4bv = glad_debug_impl_glVertexAttrib4bv;\n    glad_debug_glVertexAttrib4d = glad_debug_impl_glVertexAttrib4d;\n    glad_debug_glVertexAttrib4dv = glad_debug_impl_glVertexAttrib4dv;\n    glad_debug_glVertexAttrib4f = glad_debug_impl_glVertexAttrib4f;\n    glad_debug_glVertexAttrib4fv = glad_debug_impl_glVertexAttrib4fv;\n    glad_debug_glVertexAttrib4iv = glad_debug_impl_glVertexAttrib4iv;\n    glad_debug_glVertexAttrib4s = glad_debug_impl_glVertexAttrib4s;\n    glad_debug_glVertexAttrib4sv = glad_debug_impl_glVertexAttrib4sv;\n    glad_debug_glVertexAttrib4ubv = glad_debug_impl_glVertexAttrib4ubv;\n    glad_debug_glVertexAttrib4uiv = glad_debug_impl_glVertexAttrib4uiv;\n    glad_debug_glVertexAttrib4usv = glad_debug_impl_glVertexAttrib4usv;\n    glad_debug_glVertexAttribDivisorARB = glad_debug_impl_glVertexAttribDivisorARB;\n    glad_debug_glVertexAttribI1i = glad_debug_impl_glVertexAttribI1i;\n    glad_debug_glVertexAttribI1iv = glad_debug_impl_glVertexAttribI1iv;\n    glad_debug_glVertexAttribI1ui = glad_debug_impl_glVertexAttribI1ui;\n    glad_debug_glVertexAttribI1uiv = glad_debug_impl_glVertexAttribI1uiv;\n    glad_debug_glVertexAttribI2i = glad_debug_impl_glVertexAttribI2i;\n    glad_debug_glVertexAttribI2iv = glad_debug_impl_glVertexAttribI2iv;\n    glad_debug_glVertexAttribI2ui = glad_debug_impl_glVertexAttribI2ui;\n    glad_debug_glVertexAttribI2uiv = glad_debug_impl_glVertexAttribI2uiv;\n    glad_debug_glVertexAttribI3i = glad_debug_impl_glVertexAttribI3i;\n    glad_debug_glVertexAttribI3iv = glad_debug_impl_glVertexAttribI3iv;\n    glad_debug_glVertexAttribI3ui = glad_debug_impl_glVertexAttribI3ui;\n    glad_debug_glVertexAttribI3uiv = glad_debug_impl_glVertexAttribI3uiv;\n    glad_debug_glVertexAttribI4bv = glad_debug_impl_glVertexAttribI4bv;\n    glad_debug_glVertexAttribI4i = glad_debug_impl_glVertexAttribI4i;\n    glad_debug_glVertexAttribI4iv = glad_debug_impl_glVertexAttribI4iv;\n    glad_debug_glVertexAttribI4sv = glad_debug_impl_glVertexAttribI4sv;\n    glad_debug_glVertexAttribI4ubv = glad_debug_impl_glVertexAttribI4ubv;\n    glad_debug_glVertexAttribI4ui = glad_debug_impl_glVertexAttribI4ui;\n    glad_debug_glVertexAttribI4uiv = glad_debug_impl_glVertexAttribI4uiv;\n    glad_debug_glVertexAttribI4usv = glad_debug_impl_glVertexAttribI4usv;\n    glad_debug_glVertexAttribIPointer = glad_debug_impl_glVertexAttribIPointer;\n    glad_debug_glVertexAttribPointer = glad_debug_impl_glVertexAttribPointer;\n    glad_debug_glVertexPointer = glad_debug_impl_glVertexPointer;\n    glad_debug_glViewport = glad_debug_impl_glViewport;\n    glad_debug_glWindowPos2d = glad_debug_impl_glWindowPos2d;\n    glad_debug_glWindowPos2dv = glad_debug_impl_glWindowPos2dv;\n    glad_debug_glWindowPos2f = glad_debug_impl_glWindowPos2f;\n    glad_debug_glWindowPos2fv = glad_debug_impl_glWindowPos2fv;\n    glad_debug_glWindowPos2i = glad_debug_impl_glWindowPos2i;\n    glad_debug_glWindowPos2iv = glad_debug_impl_glWindowPos2iv;\n    glad_debug_glWindowPos2s = glad_debug_impl_glWindowPos2s;\n    glad_debug_glWindowPos2sv = glad_debug_impl_glWindowPos2sv;\n    glad_debug_glWindowPos3d = glad_debug_impl_glWindowPos3d;\n    glad_debug_glWindowPos3dv = glad_debug_impl_glWindowPos3dv;\n    glad_debug_glWindowPos3f = glad_debug_impl_glWindowPos3f;\n    glad_debug_glWindowPos3fv = glad_debug_impl_glWindowPos3fv;\n    glad_debug_glWindowPos3i = glad_debug_impl_glWindowPos3i;\n    glad_debug_glWindowPos3iv = glad_debug_impl_glWindowPos3iv;\n    glad_debug_glWindowPos3s = glad_debug_impl_glWindowPos3s;\n    glad_debug_glWindowPos3sv = glad_debug_impl_glWindowPos3sv;\n}\nvoid gladUninstallGLDebug(void) {\n    glad_debug_glAccum = glad_glAccum;\n    glad_debug_glActiveTexture = glad_glActiveTexture;\n    glad_debug_glAlphaFunc = glad_glAlphaFunc;\n    glad_debug_glAreTexturesResident = glad_glAreTexturesResident;\n    glad_debug_glArrayElement = glad_glArrayElement;\n    glad_debug_glAttachShader = glad_glAttachShader;\n    glad_debug_glBegin = glad_glBegin;\n    glad_debug_glBeginConditionalRender = glad_glBeginConditionalRender;\n    glad_debug_glBeginQuery = glad_glBeginQuery;\n    glad_debug_glBeginTransformFeedback = glad_glBeginTransformFeedback;\n    glad_debug_glBindAttribLocation = glad_glBindAttribLocation;\n    glad_debug_glBindBuffer = glad_glBindBuffer;\n    glad_debug_glBindBufferBase = glad_glBindBufferBase;\n    glad_debug_glBindBufferRange = glad_glBindBufferRange;\n    glad_debug_glBindFragDataLocation = glad_glBindFragDataLocation;\n    glad_debug_glBindFramebuffer = glad_glBindFramebuffer;\n    glad_debug_glBindRenderbuffer = glad_glBindRenderbuffer;\n    glad_debug_glBindTexture = glad_glBindTexture;\n    glad_debug_glBindVertexArray = glad_glBindVertexArray;\n    glad_debug_glBitmap = glad_glBitmap;\n    glad_debug_glBlendColor = glad_glBlendColor;\n    glad_debug_glBlendEquation = glad_glBlendEquation;\n    glad_debug_glBlendEquationSeparate = glad_glBlendEquationSeparate;\n    glad_debug_glBlendFunc = glad_glBlendFunc;\n    glad_debug_glBlendFuncSeparate = glad_glBlendFuncSeparate;\n    glad_debug_glBlitFramebuffer = glad_glBlitFramebuffer;\n    glad_debug_glBufferData = glad_glBufferData;\n    glad_debug_glBufferSubData = glad_glBufferSubData;\n    glad_debug_glCallList = glad_glCallList;\n    glad_debug_glCallLists = glad_glCallLists;\n    glad_debug_glCheckFramebufferStatus = glad_glCheckFramebufferStatus;\n    glad_debug_glClampColor = glad_glClampColor;\n    glad_debug_glClear = glad_glClear;\n    glad_debug_glClearAccum = glad_glClearAccum;\n    glad_debug_glClearBufferfi = glad_glClearBufferfi;\n    glad_debug_glClearBufferfv = glad_glClearBufferfv;\n    glad_debug_glClearBufferiv = glad_glClearBufferiv;\n    glad_debug_glClearBufferuiv = glad_glClearBufferuiv;\n    glad_debug_glClearColor = glad_glClearColor;\n    glad_debug_glClearDepth = glad_glClearDepth;\n    glad_debug_glClearIndex = glad_glClearIndex;\n    glad_debug_glClearStencil = glad_glClearStencil;\n    glad_debug_glClientActiveTexture = glad_glClientActiveTexture;\n    glad_debug_glClipPlane = glad_glClipPlane;\n    glad_debug_glColor3b = glad_glColor3b;\n    glad_debug_glColor3bv = glad_glColor3bv;\n    glad_debug_glColor3d = glad_glColor3d;\n    glad_debug_glColor3dv = glad_glColor3dv;\n    glad_debug_glColor3f = glad_glColor3f;\n    glad_debug_glColor3fv = glad_glColor3fv;\n    glad_debug_glColor3i = glad_glColor3i;\n    glad_debug_glColor3iv = glad_glColor3iv;\n    glad_debug_glColor3s = glad_glColor3s;\n    glad_debug_glColor3sv = glad_glColor3sv;\n    glad_debug_glColor3ub = glad_glColor3ub;\n    glad_debug_glColor3ubv = glad_glColor3ubv;\n    glad_debug_glColor3ui = glad_glColor3ui;\n    glad_debug_glColor3uiv = glad_glColor3uiv;\n    glad_debug_glColor3us = glad_glColor3us;\n    glad_debug_glColor3usv = glad_glColor3usv;\n    glad_debug_glColor4b = glad_glColor4b;\n    glad_debug_glColor4bv = glad_glColor4bv;\n    glad_debug_glColor4d = glad_glColor4d;\n    glad_debug_glColor4dv = glad_glColor4dv;\n    glad_debug_glColor4f = glad_glColor4f;\n    glad_debug_glColor4fv = glad_glColor4fv;\n    glad_debug_glColor4i = glad_glColor4i;\n    glad_debug_glColor4iv = glad_glColor4iv;\n    glad_debug_glColor4s = glad_glColor4s;\n    glad_debug_glColor4sv = glad_glColor4sv;\n    glad_debug_glColor4ub = glad_glColor4ub;\n    glad_debug_glColor4ubv = glad_glColor4ubv;\n    glad_debug_glColor4ui = glad_glColor4ui;\n    glad_debug_glColor4uiv = glad_glColor4uiv;\n    glad_debug_glColor4us = glad_glColor4us;\n    glad_debug_glColor4usv = glad_glColor4usv;\n    glad_debug_glColorMask = glad_glColorMask;\n    glad_debug_glColorMaski = glad_glColorMaski;\n    glad_debug_glColorMaterial = glad_glColorMaterial;\n    glad_debug_glColorPointer = glad_glColorPointer;\n    glad_debug_glCompileShader = glad_glCompileShader;\n    glad_debug_glCompressedTexImage1D = glad_glCompressedTexImage1D;\n    glad_debug_glCompressedTexImage2D = glad_glCompressedTexImage2D;\n    glad_debug_glCompressedTexImage3D = glad_glCompressedTexImage3D;\n    glad_debug_glCompressedTexSubImage1D = glad_glCompressedTexSubImage1D;\n    glad_debug_glCompressedTexSubImage2D = glad_glCompressedTexSubImage2D;\n    glad_debug_glCompressedTexSubImage3D = glad_glCompressedTexSubImage3D;\n    glad_debug_glCopyBufferSubData = glad_glCopyBufferSubData;\n    glad_debug_glCopyImageSubData = glad_glCopyImageSubData;\n    glad_debug_glCopyPixels = glad_glCopyPixels;\n    glad_debug_glCopyTexImage1D = glad_glCopyTexImage1D;\n    glad_debug_glCopyTexImage2D = glad_glCopyTexImage2D;\n    glad_debug_glCopyTexSubImage1D = glad_glCopyTexSubImage1D;\n    glad_debug_glCopyTexSubImage2D = glad_glCopyTexSubImage2D;\n    glad_debug_glCopyTexSubImage3D = glad_glCopyTexSubImage3D;\n    glad_debug_glCreateProgram = glad_glCreateProgram;\n    glad_debug_glCreateShader = glad_glCreateShader;\n    glad_debug_glCullFace = glad_glCullFace;\n    glad_debug_glDebugMessageCallback = glad_glDebugMessageCallback;\n    glad_debug_glDebugMessageControl = glad_glDebugMessageControl;\n    glad_debug_glDebugMessageInsert = glad_glDebugMessageInsert;\n    glad_debug_glDeleteBuffers = glad_glDeleteBuffers;\n    glad_debug_glDeleteFramebuffers = glad_glDeleteFramebuffers;\n    glad_debug_glDeleteLists = glad_glDeleteLists;\n    glad_debug_glDeleteProgram = glad_glDeleteProgram;\n    glad_debug_glDeleteQueries = glad_glDeleteQueries;\n    glad_debug_glDeleteRenderbuffers = glad_glDeleteRenderbuffers;\n    glad_debug_glDeleteShader = glad_glDeleteShader;\n    glad_debug_glDeleteTextures = glad_glDeleteTextures;\n    glad_debug_glDeleteVertexArrays = glad_glDeleteVertexArrays;\n    glad_debug_glDepthFunc = glad_glDepthFunc;\n    glad_debug_glDepthMask = glad_glDepthMask;\n    glad_debug_glDepthRange = glad_glDepthRange;\n    glad_debug_glDetachShader = glad_glDetachShader;\n    glad_debug_glDisable = glad_glDisable;\n    glad_debug_glDisableClientState = glad_glDisableClientState;\n    glad_debug_glDisableVertexAttribArray = glad_glDisableVertexAttribArray;\n    glad_debug_glDisablei = glad_glDisablei;\n    glad_debug_glDrawArrays = glad_glDrawArrays;\n    glad_debug_glDrawArraysInstanced = glad_glDrawArraysInstanced;\n    glad_debug_glDrawBuffer = glad_glDrawBuffer;\n    glad_debug_glDrawBuffers = glad_glDrawBuffers;\n    glad_debug_glDrawElements = glad_glDrawElements;\n    glad_debug_glDrawElementsInstanced = glad_glDrawElementsInstanced;\n    glad_debug_glDrawPixels = glad_glDrawPixels;\n    glad_debug_glDrawRangeElements = glad_glDrawRangeElements;\n    glad_debug_glEdgeFlag = glad_glEdgeFlag;\n    glad_debug_glEdgeFlagPointer = glad_glEdgeFlagPointer;\n    glad_debug_glEdgeFlagv = glad_glEdgeFlagv;\n    glad_debug_glEnable = glad_glEnable;\n    glad_debug_glEnableClientState = glad_glEnableClientState;\n    glad_debug_glEnableVertexAttribArray = glad_glEnableVertexAttribArray;\n    glad_debug_glEnablei = glad_glEnablei;\n    glad_debug_glEnd = glad_glEnd;\n    glad_debug_glEndConditionalRender = glad_glEndConditionalRender;\n    glad_debug_glEndList = glad_glEndList;\n    glad_debug_glEndQuery = glad_glEndQuery;\n    glad_debug_glEndTransformFeedback = glad_glEndTransformFeedback;\n    glad_debug_glEvalCoord1d = glad_glEvalCoord1d;\n    glad_debug_glEvalCoord1dv = glad_glEvalCoord1dv;\n    glad_debug_glEvalCoord1f = glad_glEvalCoord1f;\n    glad_debug_glEvalCoord1fv = glad_glEvalCoord1fv;\n    glad_debug_glEvalCoord2d = glad_glEvalCoord2d;\n    glad_debug_glEvalCoord2dv = glad_glEvalCoord2dv;\n    glad_debug_glEvalCoord2f = glad_glEvalCoord2f;\n    glad_debug_glEvalCoord2fv = glad_glEvalCoord2fv;\n    glad_debug_glEvalMesh1 = glad_glEvalMesh1;\n    glad_debug_glEvalMesh2 = glad_glEvalMesh2;\n    glad_debug_glEvalPoint1 = glad_glEvalPoint1;\n    glad_debug_glEvalPoint2 = glad_glEvalPoint2;\n    glad_debug_glFeedbackBuffer = glad_glFeedbackBuffer;\n    glad_debug_glFinish = glad_glFinish;\n    glad_debug_glFlush = glad_glFlush;\n    glad_debug_glFlushMappedBufferRange = glad_glFlushMappedBufferRange;\n    glad_debug_glFogCoordPointer = glad_glFogCoordPointer;\n    glad_debug_glFogCoordd = glad_glFogCoordd;\n    glad_debug_glFogCoorddv = glad_glFogCoorddv;\n    glad_debug_glFogCoordf = glad_glFogCoordf;\n    glad_debug_glFogCoordfv = glad_glFogCoordfv;\n    glad_debug_glFogf = glad_glFogf;\n    glad_debug_glFogfv = glad_glFogfv;\n    glad_debug_glFogi = glad_glFogi;\n    glad_debug_glFogiv = glad_glFogiv;\n    glad_debug_glFramebufferRenderbuffer = glad_glFramebufferRenderbuffer;\n    glad_debug_glFramebufferTexture1D = glad_glFramebufferTexture1D;\n    glad_debug_glFramebufferTexture2D = glad_glFramebufferTexture2D;\n    glad_debug_glFramebufferTexture3D = glad_glFramebufferTexture3D;\n    glad_debug_glFramebufferTextureLayer = glad_glFramebufferTextureLayer;\n    glad_debug_glFrontFace = glad_glFrontFace;\n    glad_debug_glFrustum = glad_glFrustum;\n    glad_debug_glGenBuffers = glad_glGenBuffers;\n    glad_debug_glGenFramebuffers = glad_glGenFramebuffers;\n    glad_debug_glGenLists = glad_glGenLists;\n    glad_debug_glGenQueries = glad_glGenQueries;\n    glad_debug_glGenRenderbuffers = glad_glGenRenderbuffers;\n    glad_debug_glGenTextures = glad_glGenTextures;\n    glad_debug_glGenVertexArrays = glad_glGenVertexArrays;\n    glad_debug_glGenerateMipmap = glad_glGenerateMipmap;\n    glad_debug_glGetActiveAttrib = glad_glGetActiveAttrib;\n    glad_debug_glGetActiveUniform = glad_glGetActiveUniform;\n    glad_debug_glGetActiveUniformBlockName = glad_glGetActiveUniformBlockName;\n    glad_debug_glGetActiveUniformBlockiv = glad_glGetActiveUniformBlockiv;\n    glad_debug_glGetActiveUniformName = glad_glGetActiveUniformName;\n    glad_debug_glGetActiveUniformsiv = glad_glGetActiveUniformsiv;\n    glad_debug_glGetAttachedShaders = glad_glGetAttachedShaders;\n    glad_debug_glGetAttribLocation = glad_glGetAttribLocation;\n    glad_debug_glGetBooleani_v = glad_glGetBooleani_v;\n    glad_debug_glGetBooleanv = glad_glGetBooleanv;\n    glad_debug_glGetBufferParameteriv = glad_glGetBufferParameteriv;\n    glad_debug_glGetBufferPointerv = glad_glGetBufferPointerv;\n    glad_debug_glGetBufferSubData = glad_glGetBufferSubData;\n    glad_debug_glGetClipPlane = glad_glGetClipPlane;\n    glad_debug_glGetCompressedTexImage = glad_glGetCompressedTexImage;\n    glad_debug_glGetDebugMessageLog = glad_glGetDebugMessageLog;\n    glad_debug_glGetDoublev = glad_glGetDoublev;\n    glad_debug_glGetError = glad_glGetError;\n    glad_debug_glGetFloatv = glad_glGetFloatv;\n    glad_debug_glGetFragDataLocation = glad_glGetFragDataLocation;\n    glad_debug_glGetFramebufferAttachmentParameteriv = glad_glGetFramebufferAttachmentParameteriv;\n    glad_debug_glGetGraphicsResetStatusARB = glad_glGetGraphicsResetStatusARB;\n    glad_debug_glGetIntegeri_v = glad_glGetIntegeri_v;\n    glad_debug_glGetIntegerv = glad_glGetIntegerv;\n    glad_debug_glGetLightfv = glad_glGetLightfv;\n    glad_debug_glGetLightiv = glad_glGetLightiv;\n    glad_debug_glGetMapdv = glad_glGetMapdv;\n    glad_debug_glGetMapfv = glad_glGetMapfv;\n    glad_debug_glGetMapiv = glad_glGetMapiv;\n    glad_debug_glGetMaterialfv = glad_glGetMaterialfv;\n    glad_debug_glGetMaterialiv = glad_glGetMaterialiv;\n    glad_debug_glGetObjectLabel = glad_glGetObjectLabel;\n    glad_debug_glGetObjectPtrLabel = glad_glGetObjectPtrLabel;\n    glad_debug_glGetPixelMapfv = glad_glGetPixelMapfv;\n    glad_debug_glGetPixelMapuiv = glad_glGetPixelMapuiv;\n    glad_debug_glGetPixelMapusv = glad_glGetPixelMapusv;\n    glad_debug_glGetPointerv = glad_glGetPointerv;\n    glad_debug_glGetPolygonStipple = glad_glGetPolygonStipple;\n    glad_debug_glGetProgramInfoLog = glad_glGetProgramInfoLog;\n    glad_debug_glGetProgramiv = glad_glGetProgramiv;\n    glad_debug_glGetQueryObjectiv = glad_glGetQueryObjectiv;\n    glad_debug_glGetQueryObjectuiv = glad_glGetQueryObjectuiv;\n    glad_debug_glGetQueryiv = glad_glGetQueryiv;\n    glad_debug_glGetRenderbufferParameteriv = glad_glGetRenderbufferParameteriv;\n    glad_debug_glGetShaderInfoLog = glad_glGetShaderInfoLog;\n    glad_debug_glGetShaderSource = glad_glGetShaderSource;\n    glad_debug_glGetShaderiv = glad_glGetShaderiv;\n    glad_debug_glGetString = glad_glGetString;\n    glad_debug_glGetStringi = glad_glGetStringi;\n    glad_debug_glGetTexEnvfv = glad_glGetTexEnvfv;\n    glad_debug_glGetTexEnviv = glad_glGetTexEnviv;\n    glad_debug_glGetTexGendv = glad_glGetTexGendv;\n    glad_debug_glGetTexGenfv = glad_glGetTexGenfv;\n    glad_debug_glGetTexGeniv = glad_glGetTexGeniv;\n    glad_debug_glGetTexImage = glad_glGetTexImage;\n    glad_debug_glGetTexLevelParameterfv = glad_glGetTexLevelParameterfv;\n    glad_debug_glGetTexLevelParameteriv = glad_glGetTexLevelParameteriv;\n    glad_debug_glGetTexParameterIiv = glad_glGetTexParameterIiv;\n    glad_debug_glGetTexParameterIuiv = glad_glGetTexParameterIuiv;\n    glad_debug_glGetTexParameterfv = glad_glGetTexParameterfv;\n    glad_debug_glGetTexParameteriv = glad_glGetTexParameteriv;\n    glad_debug_glGetTransformFeedbackVarying = glad_glGetTransformFeedbackVarying;\n    glad_debug_glGetUniformBlockIndex = glad_glGetUniformBlockIndex;\n    glad_debug_glGetUniformIndices = glad_glGetUniformIndices;\n    glad_debug_glGetUniformLocation = glad_glGetUniformLocation;\n    glad_debug_glGetUniformfv = glad_glGetUniformfv;\n    glad_debug_glGetUniformiv = glad_glGetUniformiv;\n    glad_debug_glGetUniformuiv = glad_glGetUniformuiv;\n    glad_debug_glGetVertexAttribIiv = glad_glGetVertexAttribIiv;\n    glad_debug_glGetVertexAttribIuiv = glad_glGetVertexAttribIuiv;\n    glad_debug_glGetVertexAttribPointerv = glad_glGetVertexAttribPointerv;\n    glad_debug_glGetVertexAttribdv = glad_glGetVertexAttribdv;\n    glad_debug_glGetVertexAttribfv = glad_glGetVertexAttribfv;\n    glad_debug_glGetVertexAttribiv = glad_glGetVertexAttribiv;\n    glad_debug_glGetnCompressedTexImageARB = glad_glGetnCompressedTexImageARB;\n    glad_debug_glGetnTexImageARB = glad_glGetnTexImageARB;\n    glad_debug_glGetnUniformdvARB = glad_glGetnUniformdvARB;\n    glad_debug_glGetnUniformfvARB = glad_glGetnUniformfvARB;\n    glad_debug_glGetnUniformivARB = glad_glGetnUniformivARB;\n    glad_debug_glGetnUniformuivARB = glad_glGetnUniformuivARB;\n    glad_debug_glHint = glad_glHint;\n    glad_debug_glIndexMask = glad_glIndexMask;\n    glad_debug_glIndexPointer = glad_glIndexPointer;\n    glad_debug_glIndexd = glad_glIndexd;\n    glad_debug_glIndexdv = glad_glIndexdv;\n    glad_debug_glIndexf = glad_glIndexf;\n    glad_debug_glIndexfv = glad_glIndexfv;\n    glad_debug_glIndexi = glad_glIndexi;\n    glad_debug_glIndexiv = glad_glIndexiv;\n    glad_debug_glIndexs = glad_glIndexs;\n    glad_debug_glIndexsv = glad_glIndexsv;\n    glad_debug_glIndexub = glad_glIndexub;\n    glad_debug_glIndexubv = glad_glIndexubv;\n    glad_debug_glInitNames = glad_glInitNames;\n    glad_debug_glInterleavedArrays = glad_glInterleavedArrays;\n    glad_debug_glIsBuffer = glad_glIsBuffer;\n    glad_debug_glIsEnabled = glad_glIsEnabled;\n    glad_debug_glIsEnabledi = glad_glIsEnabledi;\n    glad_debug_glIsFramebuffer = glad_glIsFramebuffer;\n    glad_debug_glIsList = glad_glIsList;\n    glad_debug_glIsProgram = glad_glIsProgram;\n    glad_debug_glIsQuery = glad_glIsQuery;\n    glad_debug_glIsRenderbuffer = glad_glIsRenderbuffer;\n    glad_debug_glIsShader = glad_glIsShader;\n    glad_debug_glIsTexture = glad_glIsTexture;\n    glad_debug_glIsVertexArray = glad_glIsVertexArray;\n    glad_debug_glLightModelf = glad_glLightModelf;\n    glad_debug_glLightModelfv = glad_glLightModelfv;\n    glad_debug_glLightModeli = glad_glLightModeli;\n    glad_debug_glLightModeliv = glad_glLightModeliv;\n    glad_debug_glLightf = glad_glLightf;\n    glad_debug_glLightfv = glad_glLightfv;\n    glad_debug_glLighti = glad_glLighti;\n    glad_debug_glLightiv = glad_glLightiv;\n    glad_debug_glLineStipple = glad_glLineStipple;\n    glad_debug_glLineWidth = glad_glLineWidth;\n    glad_debug_glLinkProgram = glad_glLinkProgram;\n    glad_debug_glListBase = glad_glListBase;\n    glad_debug_glLoadIdentity = glad_glLoadIdentity;\n    glad_debug_glLoadMatrixd = glad_glLoadMatrixd;\n    glad_debug_glLoadMatrixf = glad_glLoadMatrixf;\n    glad_debug_glLoadName = glad_glLoadName;\n    glad_debug_glLoadTransposeMatrixd = glad_glLoadTransposeMatrixd;\n    glad_debug_glLoadTransposeMatrixf = glad_glLoadTransposeMatrixf;\n    glad_debug_glLogicOp = glad_glLogicOp;\n    glad_debug_glMap1d = glad_glMap1d;\n    glad_debug_glMap1f = glad_glMap1f;\n    glad_debug_glMap2d = glad_glMap2d;\n    glad_debug_glMap2f = glad_glMap2f;\n    glad_debug_glMapBuffer = glad_glMapBuffer;\n    glad_debug_glMapBufferRange = glad_glMapBufferRange;\n    glad_debug_glMapGrid1d = glad_glMapGrid1d;\n    glad_debug_glMapGrid1f = glad_glMapGrid1f;\n    glad_debug_glMapGrid2d = glad_glMapGrid2d;\n    glad_debug_glMapGrid2f = glad_glMapGrid2f;\n    glad_debug_glMaterialf = glad_glMaterialf;\n    glad_debug_glMaterialfv = glad_glMaterialfv;\n    glad_debug_glMateriali = glad_glMateriali;\n    glad_debug_glMaterialiv = glad_glMaterialiv;\n    glad_debug_glMatrixMode = glad_glMatrixMode;\n    glad_debug_glMultMatrixd = glad_glMultMatrixd;\n    glad_debug_glMultMatrixf = glad_glMultMatrixf;\n    glad_debug_glMultTransposeMatrixd = glad_glMultTransposeMatrixd;\n    glad_debug_glMultTransposeMatrixf = glad_glMultTransposeMatrixf;\n    glad_debug_glMultiDrawArrays = glad_glMultiDrawArrays;\n    glad_debug_glMultiDrawElements = glad_glMultiDrawElements;\n    glad_debug_glMultiTexCoord1d = glad_glMultiTexCoord1d;\n    glad_debug_glMultiTexCoord1dv = glad_glMultiTexCoord1dv;\n    glad_debug_glMultiTexCoord1f = glad_glMultiTexCoord1f;\n    glad_debug_glMultiTexCoord1fv = glad_glMultiTexCoord1fv;\n    glad_debug_glMultiTexCoord1i = glad_glMultiTexCoord1i;\n    glad_debug_glMultiTexCoord1iv = glad_glMultiTexCoord1iv;\n    glad_debug_glMultiTexCoord1s = glad_glMultiTexCoord1s;\n    glad_debug_glMultiTexCoord1sv = glad_glMultiTexCoord1sv;\n    glad_debug_glMultiTexCoord2d = glad_glMultiTexCoord2d;\n    glad_debug_glMultiTexCoord2dv = glad_glMultiTexCoord2dv;\n    glad_debug_glMultiTexCoord2f = glad_glMultiTexCoord2f;\n    glad_debug_glMultiTexCoord2fv = glad_glMultiTexCoord2fv;\n    glad_debug_glMultiTexCoord2i = glad_glMultiTexCoord2i;\n    glad_debug_glMultiTexCoord2iv = glad_glMultiTexCoord2iv;\n    glad_debug_glMultiTexCoord2s = glad_glMultiTexCoord2s;\n    glad_debug_glMultiTexCoord2sv = glad_glMultiTexCoord2sv;\n    glad_debug_glMultiTexCoord3d = glad_glMultiTexCoord3d;\n    glad_debug_glMultiTexCoord3dv = glad_glMultiTexCoord3dv;\n    glad_debug_glMultiTexCoord3f = glad_glMultiTexCoord3f;\n    glad_debug_glMultiTexCoord3fv = glad_glMultiTexCoord3fv;\n    glad_debug_glMultiTexCoord3i = glad_glMultiTexCoord3i;\n    glad_debug_glMultiTexCoord3iv = glad_glMultiTexCoord3iv;\n    glad_debug_glMultiTexCoord3s = glad_glMultiTexCoord3s;\n    glad_debug_glMultiTexCoord3sv = glad_glMultiTexCoord3sv;\n    glad_debug_glMultiTexCoord4d = glad_glMultiTexCoord4d;\n    glad_debug_glMultiTexCoord4dv = glad_glMultiTexCoord4dv;\n    glad_debug_glMultiTexCoord4f = glad_glMultiTexCoord4f;\n    glad_debug_glMultiTexCoord4fv = glad_glMultiTexCoord4fv;\n    glad_debug_glMultiTexCoord4i = glad_glMultiTexCoord4i;\n    glad_debug_glMultiTexCoord4iv = glad_glMultiTexCoord4iv;\n    glad_debug_glMultiTexCoord4s = glad_glMultiTexCoord4s;\n    glad_debug_glMultiTexCoord4sv = glad_glMultiTexCoord4sv;\n    glad_debug_glNewList = glad_glNewList;\n    glad_debug_glNormal3b = glad_glNormal3b;\n    glad_debug_glNormal3bv = glad_glNormal3bv;\n    glad_debug_glNormal3d = glad_glNormal3d;\n    glad_debug_glNormal3dv = glad_glNormal3dv;\n    glad_debug_glNormal3f = glad_glNormal3f;\n    glad_debug_glNormal3fv = glad_glNormal3fv;\n    glad_debug_glNormal3i = glad_glNormal3i;\n    glad_debug_glNormal3iv = glad_glNormal3iv;\n    glad_debug_glNormal3s = glad_glNormal3s;\n    glad_debug_glNormal3sv = glad_glNormal3sv;\n    glad_debug_glNormalPointer = glad_glNormalPointer;\n    glad_debug_glObjectLabel = glad_glObjectLabel;\n    glad_debug_glObjectPtrLabel = glad_glObjectPtrLabel;\n    glad_debug_glOrtho = glad_glOrtho;\n    glad_debug_glPassThrough = glad_glPassThrough;\n    glad_debug_glPixelMapfv = glad_glPixelMapfv;\n    glad_debug_glPixelMapuiv = glad_glPixelMapuiv;\n    glad_debug_glPixelMapusv = glad_glPixelMapusv;\n    glad_debug_glPixelStoref = glad_glPixelStoref;\n    glad_debug_glPixelStorei = glad_glPixelStorei;\n    glad_debug_glPixelTransferf = glad_glPixelTransferf;\n    glad_debug_glPixelTransferi = glad_glPixelTransferi;\n    glad_debug_glPixelZoom = glad_glPixelZoom;\n    glad_debug_glPointParameterf = glad_glPointParameterf;\n    glad_debug_glPointParameterfv = glad_glPointParameterfv;\n    glad_debug_glPointParameteri = glad_glPointParameteri;\n    glad_debug_glPointParameteriv = glad_glPointParameteriv;\n    glad_debug_glPointSize = glad_glPointSize;\n    glad_debug_glPolygonMode = glad_glPolygonMode;\n    glad_debug_glPolygonOffset = glad_glPolygonOffset;\n    glad_debug_glPolygonStipple = glad_glPolygonStipple;\n    glad_debug_glPopAttrib = glad_glPopAttrib;\n    glad_debug_glPopClientAttrib = glad_glPopClientAttrib;\n    glad_debug_glPopDebugGroup = glad_glPopDebugGroup;\n    glad_debug_glPopMatrix = glad_glPopMatrix;\n    glad_debug_glPopName = glad_glPopName;\n    glad_debug_glPrimitiveRestartIndex = glad_glPrimitiveRestartIndex;\n    glad_debug_glPrioritizeTextures = glad_glPrioritizeTextures;\n    glad_debug_glPushAttrib = glad_glPushAttrib;\n    glad_debug_glPushClientAttrib = glad_glPushClientAttrib;\n    glad_debug_glPushDebugGroup = glad_glPushDebugGroup;\n    glad_debug_glPushMatrix = glad_glPushMatrix;\n    glad_debug_glPushName = glad_glPushName;\n    glad_debug_glRasterPos2d = glad_glRasterPos2d;\n    glad_debug_glRasterPos2dv = glad_glRasterPos2dv;\n    glad_debug_glRasterPos2f = glad_glRasterPos2f;\n    glad_debug_glRasterPos2fv = glad_glRasterPos2fv;\n    glad_debug_glRasterPos2i = glad_glRasterPos2i;\n    glad_debug_glRasterPos2iv = glad_glRasterPos2iv;\n    glad_debug_glRasterPos2s = glad_glRasterPos2s;\n    glad_debug_glRasterPos2sv = glad_glRasterPos2sv;\n    glad_debug_glRasterPos3d = glad_glRasterPos3d;\n    glad_debug_glRasterPos3dv = glad_glRasterPos3dv;\n    glad_debug_glRasterPos3f = glad_glRasterPos3f;\n    glad_debug_glRasterPos3fv = glad_glRasterPos3fv;\n    glad_debug_glRasterPos3i = glad_glRasterPos3i;\n    glad_debug_glRasterPos3iv = glad_glRasterPos3iv;\n    glad_debug_glRasterPos3s = glad_glRasterPos3s;\n    glad_debug_glRasterPos3sv = glad_glRasterPos3sv;\n    glad_debug_glRasterPos4d = glad_glRasterPos4d;\n    glad_debug_glRasterPos4dv = glad_glRasterPos4dv;\n    glad_debug_glRasterPos4f = glad_glRasterPos4f;\n    glad_debug_glRasterPos4fv = glad_glRasterPos4fv;\n    glad_debug_glRasterPos4i = glad_glRasterPos4i;\n    glad_debug_glRasterPos4iv = glad_glRasterPos4iv;\n    glad_debug_glRasterPos4s = glad_glRasterPos4s;\n    glad_debug_glRasterPos4sv = glad_glRasterPos4sv;\n    glad_debug_glReadBuffer = glad_glReadBuffer;\n    glad_debug_glReadPixels = glad_glReadPixels;\n    glad_debug_glReadnPixelsARB = glad_glReadnPixelsARB;\n    glad_debug_glRectd = glad_glRectd;\n    glad_debug_glRectdv = glad_glRectdv;\n    glad_debug_glRectf = glad_glRectf;\n    glad_debug_glRectfv = glad_glRectfv;\n    glad_debug_glRecti = glad_glRecti;\n    glad_debug_glRectiv = glad_glRectiv;\n    glad_debug_glRects = glad_glRects;\n    glad_debug_glRectsv = glad_glRectsv;\n    glad_debug_glRenderMode = glad_glRenderMode;\n    glad_debug_glRenderbufferStorage = glad_glRenderbufferStorage;\n    glad_debug_glRenderbufferStorageMultisample = glad_glRenderbufferStorageMultisample;\n    glad_debug_glRotated = glad_glRotated;\n    glad_debug_glRotatef = glad_glRotatef;\n    glad_debug_glSampleCoverage = glad_glSampleCoverage;\n    glad_debug_glSampleCoverageARB = glad_glSampleCoverageARB;\n    glad_debug_glScaled = glad_glScaled;\n    glad_debug_glScalef = glad_glScalef;\n    glad_debug_glScissor = glad_glScissor;\n    glad_debug_glSecondaryColor3b = glad_glSecondaryColor3b;\n    glad_debug_glSecondaryColor3bv = glad_glSecondaryColor3bv;\n    glad_debug_glSecondaryColor3d = glad_glSecondaryColor3d;\n    glad_debug_glSecondaryColor3dv = glad_glSecondaryColor3dv;\n    glad_debug_glSecondaryColor3f = glad_glSecondaryColor3f;\n    glad_debug_glSecondaryColor3fv = glad_glSecondaryColor3fv;\n    glad_debug_glSecondaryColor3i = glad_glSecondaryColor3i;\n    glad_debug_glSecondaryColor3iv = glad_glSecondaryColor3iv;\n    glad_debug_glSecondaryColor3s = glad_glSecondaryColor3s;\n    glad_debug_glSecondaryColor3sv = glad_glSecondaryColor3sv;\n    glad_debug_glSecondaryColor3ub = glad_glSecondaryColor3ub;\n    glad_debug_glSecondaryColor3ubv = glad_glSecondaryColor3ubv;\n    glad_debug_glSecondaryColor3ui = glad_glSecondaryColor3ui;\n    glad_debug_glSecondaryColor3uiv = glad_glSecondaryColor3uiv;\n    glad_debug_glSecondaryColor3us = glad_glSecondaryColor3us;\n    glad_debug_glSecondaryColor3usv = glad_glSecondaryColor3usv;\n    glad_debug_glSecondaryColorPointer = glad_glSecondaryColorPointer;\n    glad_debug_glSelectBuffer = glad_glSelectBuffer;\n    glad_debug_glShadeModel = glad_glShadeModel;\n    glad_debug_glShaderSource = glad_glShaderSource;\n    glad_debug_glStencilFunc = glad_glStencilFunc;\n    glad_debug_glStencilFuncSeparate = glad_glStencilFuncSeparate;\n    glad_debug_glStencilMask = glad_glStencilMask;\n    glad_debug_glStencilMaskSeparate = glad_glStencilMaskSeparate;\n    glad_debug_glStencilOp = glad_glStencilOp;\n    glad_debug_glStencilOpSeparate = glad_glStencilOpSeparate;\n    glad_debug_glTexBuffer = glad_glTexBuffer;\n    glad_debug_glTexCoord1d = glad_glTexCoord1d;\n    glad_debug_glTexCoord1dv = glad_glTexCoord1dv;\n    glad_debug_glTexCoord1f = glad_glTexCoord1f;\n    glad_debug_glTexCoord1fv = glad_glTexCoord1fv;\n    glad_debug_glTexCoord1i = glad_glTexCoord1i;\n    glad_debug_glTexCoord1iv = glad_glTexCoord1iv;\n    glad_debug_glTexCoord1s = glad_glTexCoord1s;\n    glad_debug_glTexCoord1sv = glad_glTexCoord1sv;\n    glad_debug_glTexCoord2d = glad_glTexCoord2d;\n    glad_debug_glTexCoord2dv = glad_glTexCoord2dv;\n    glad_debug_glTexCoord2f = glad_glTexCoord2f;\n    glad_debug_glTexCoord2fv = glad_glTexCoord2fv;\n    glad_debug_glTexCoord2i = glad_glTexCoord2i;\n    glad_debug_glTexCoord2iv = glad_glTexCoord2iv;\n    glad_debug_glTexCoord2s = glad_glTexCoord2s;\n    glad_debug_glTexCoord2sv = glad_glTexCoord2sv;\n    glad_debug_glTexCoord3d = glad_glTexCoord3d;\n    glad_debug_glTexCoord3dv = glad_glTexCoord3dv;\n    glad_debug_glTexCoord3f = glad_glTexCoord3f;\n    glad_debug_glTexCoord3fv = glad_glTexCoord3fv;\n    glad_debug_glTexCoord3i = glad_glTexCoord3i;\n    glad_debug_glTexCoord3iv = glad_glTexCoord3iv;\n    glad_debug_glTexCoord3s = glad_glTexCoord3s;\n    glad_debug_glTexCoord3sv = glad_glTexCoord3sv;\n    glad_debug_glTexCoord4d = glad_glTexCoord4d;\n    glad_debug_glTexCoord4dv = glad_glTexCoord4dv;\n    glad_debug_glTexCoord4f = glad_glTexCoord4f;\n    glad_debug_glTexCoord4fv = glad_glTexCoord4fv;\n    glad_debug_glTexCoord4i = glad_glTexCoord4i;\n    glad_debug_glTexCoord4iv = glad_glTexCoord4iv;\n    glad_debug_glTexCoord4s = glad_glTexCoord4s;\n    glad_debug_glTexCoord4sv = glad_glTexCoord4sv;\n    glad_debug_glTexCoordPointer = glad_glTexCoordPointer;\n    glad_debug_glTexEnvf = glad_glTexEnvf;\n    glad_debug_glTexEnvfv = glad_glTexEnvfv;\n    glad_debug_glTexEnvi = glad_glTexEnvi;\n    glad_debug_glTexEnviv = glad_glTexEnviv;\n    glad_debug_glTexGend = glad_glTexGend;\n    glad_debug_glTexGendv = glad_glTexGendv;\n    glad_debug_glTexGenf = glad_glTexGenf;\n    glad_debug_glTexGenfv = glad_glTexGenfv;\n    glad_debug_glTexGeni = glad_glTexGeni;\n    glad_debug_glTexGeniv = glad_glTexGeniv;\n    glad_debug_glTexImage1D = glad_glTexImage1D;\n    glad_debug_glTexImage2D = glad_glTexImage2D;\n    glad_debug_glTexImage3D = glad_glTexImage3D;\n    glad_debug_glTexParameterIiv = glad_glTexParameterIiv;\n    glad_debug_glTexParameterIuiv = glad_glTexParameterIuiv;\n    glad_debug_glTexParameterf = glad_glTexParameterf;\n    glad_debug_glTexParameterfv = glad_glTexParameterfv;\n    glad_debug_glTexParameteri = glad_glTexParameteri;\n    glad_debug_glTexParameteriv = glad_glTexParameteriv;\n    glad_debug_glTexStorage1D = glad_glTexStorage1D;\n    glad_debug_glTexStorage2D = glad_glTexStorage2D;\n    glad_debug_glTexStorage3D = glad_glTexStorage3D;\n    glad_debug_glTexSubImage1D = glad_glTexSubImage1D;\n    glad_debug_glTexSubImage2D = glad_glTexSubImage2D;\n    glad_debug_glTexSubImage3D = glad_glTexSubImage3D;\n    glad_debug_glTransformFeedbackVaryings = glad_glTransformFeedbackVaryings;\n    glad_debug_glTranslated = glad_glTranslated;\n    glad_debug_glTranslatef = glad_glTranslatef;\n    glad_debug_glUniform1f = glad_glUniform1f;\n    glad_debug_glUniform1fv = glad_glUniform1fv;\n    glad_debug_glUniform1i = glad_glUniform1i;\n    glad_debug_glUniform1iv = glad_glUniform1iv;\n    glad_debug_glUniform1ui = glad_glUniform1ui;\n    glad_debug_glUniform1uiv = glad_glUniform1uiv;\n    glad_debug_glUniform2f = glad_glUniform2f;\n    glad_debug_glUniform2fv = glad_glUniform2fv;\n    glad_debug_glUniform2i = glad_glUniform2i;\n    glad_debug_glUniform2iv = glad_glUniform2iv;\n    glad_debug_glUniform2ui = glad_glUniform2ui;\n    glad_debug_glUniform2uiv = glad_glUniform2uiv;\n    glad_debug_glUniform3f = glad_glUniform3f;\n    glad_debug_glUniform3fv = glad_glUniform3fv;\n    glad_debug_glUniform3i = glad_glUniform3i;\n    glad_debug_glUniform3iv = glad_glUniform3iv;\n    glad_debug_glUniform3ui = glad_glUniform3ui;\n    glad_debug_glUniform3uiv = glad_glUniform3uiv;\n    glad_debug_glUniform4f = glad_glUniform4f;\n    glad_debug_glUniform4fv = glad_glUniform4fv;\n    glad_debug_glUniform4i = glad_glUniform4i;\n    glad_debug_glUniform4iv = glad_glUniform4iv;\n    glad_debug_glUniform4ui = glad_glUniform4ui;\n    glad_debug_glUniform4uiv = glad_glUniform4uiv;\n    glad_debug_glUniformBlockBinding = glad_glUniformBlockBinding;\n    glad_debug_glUniformMatrix2fv = glad_glUniformMatrix2fv;\n    glad_debug_glUniformMatrix2x3fv = glad_glUniformMatrix2x3fv;\n    glad_debug_glUniformMatrix2x4fv = glad_glUniformMatrix2x4fv;\n    glad_debug_glUniformMatrix3fv = glad_glUniformMatrix3fv;\n    glad_debug_glUniformMatrix3x2fv = glad_glUniformMatrix3x2fv;\n    glad_debug_glUniformMatrix3x4fv = glad_glUniformMatrix3x4fv;\n    glad_debug_glUniformMatrix4fv = glad_glUniformMatrix4fv;\n    glad_debug_glUniformMatrix4x2fv = glad_glUniformMatrix4x2fv;\n    glad_debug_glUniformMatrix4x3fv = glad_glUniformMatrix4x3fv;\n    glad_debug_glUnmapBuffer = glad_glUnmapBuffer;\n    glad_debug_glUseProgram = glad_glUseProgram;\n    glad_debug_glValidateProgram = glad_glValidateProgram;\n    glad_debug_glVertex2d = glad_glVertex2d;\n    glad_debug_glVertex2dv = glad_glVertex2dv;\n    glad_debug_glVertex2f = glad_glVertex2f;\n    glad_debug_glVertex2fv = glad_glVertex2fv;\n    glad_debug_glVertex2i = glad_glVertex2i;\n    glad_debug_glVertex2iv = glad_glVertex2iv;\n    glad_debug_glVertex2s = glad_glVertex2s;\n    glad_debug_glVertex2sv = glad_glVertex2sv;\n    glad_debug_glVertex3d = glad_glVertex3d;\n    glad_debug_glVertex3dv = glad_glVertex3dv;\n    glad_debug_glVertex3f = glad_glVertex3f;\n    glad_debug_glVertex3fv = glad_glVertex3fv;\n    glad_debug_glVertex3i = glad_glVertex3i;\n    glad_debug_glVertex3iv = glad_glVertex3iv;\n    glad_debug_glVertex3s = glad_glVertex3s;\n    glad_debug_glVertex3sv = glad_glVertex3sv;\n    glad_debug_glVertex4d = glad_glVertex4d;\n    glad_debug_glVertex4dv = glad_glVertex4dv;\n    glad_debug_glVertex4f = glad_glVertex4f;\n    glad_debug_glVertex4fv = glad_glVertex4fv;\n    glad_debug_glVertex4i = glad_glVertex4i;\n    glad_debug_glVertex4iv = glad_glVertex4iv;\n    glad_debug_glVertex4s = glad_glVertex4s;\n    glad_debug_glVertex4sv = glad_glVertex4sv;\n    glad_debug_glVertexAttrib1d = glad_glVertexAttrib1d;\n    glad_debug_glVertexAttrib1dv = glad_glVertexAttrib1dv;\n    glad_debug_glVertexAttrib1f = glad_glVertexAttrib1f;\n    glad_debug_glVertexAttrib1fv = glad_glVertexAttrib1fv;\n    glad_debug_glVertexAttrib1s = glad_glVertexAttrib1s;\n    glad_debug_glVertexAttrib1sv = glad_glVertexAttrib1sv;\n    glad_debug_glVertexAttrib2d = glad_glVertexAttrib2d;\n    glad_debug_glVertexAttrib2dv = glad_glVertexAttrib2dv;\n    glad_debug_glVertexAttrib2f = glad_glVertexAttrib2f;\n    glad_debug_glVertexAttrib2fv = glad_glVertexAttrib2fv;\n    glad_debug_glVertexAttrib2s = glad_glVertexAttrib2s;\n    glad_debug_glVertexAttrib2sv = glad_glVertexAttrib2sv;\n    glad_debug_glVertexAttrib3d = glad_glVertexAttrib3d;\n    glad_debug_glVertexAttrib3dv = glad_glVertexAttrib3dv;\n    glad_debug_glVertexAttrib3f = glad_glVertexAttrib3f;\n    glad_debug_glVertexAttrib3fv = glad_glVertexAttrib3fv;\n    glad_debug_glVertexAttrib3s = glad_glVertexAttrib3s;\n    glad_debug_glVertexAttrib3sv = glad_glVertexAttrib3sv;\n    glad_debug_glVertexAttrib4Nbv = glad_glVertexAttrib4Nbv;\n    glad_debug_glVertexAttrib4Niv = glad_glVertexAttrib4Niv;\n    glad_debug_glVertexAttrib4Nsv = glad_glVertexAttrib4Nsv;\n    glad_debug_glVertexAttrib4Nub = glad_glVertexAttrib4Nub;\n    glad_debug_glVertexAttrib4Nubv = glad_glVertexAttrib4Nubv;\n    glad_debug_glVertexAttrib4Nuiv = glad_glVertexAttrib4Nuiv;\n    glad_debug_glVertexAttrib4Nusv = glad_glVertexAttrib4Nusv;\n    glad_debug_glVertexAttrib4bv = glad_glVertexAttrib4bv;\n    glad_debug_glVertexAttrib4d = glad_glVertexAttrib4d;\n    glad_debug_glVertexAttrib4dv = glad_glVertexAttrib4dv;\n    glad_debug_glVertexAttrib4f = glad_glVertexAttrib4f;\n    glad_debug_glVertexAttrib4fv = glad_glVertexAttrib4fv;\n    glad_debug_glVertexAttrib4iv = glad_glVertexAttrib4iv;\n    glad_debug_glVertexAttrib4s = glad_glVertexAttrib4s;\n    glad_debug_glVertexAttrib4sv = glad_glVertexAttrib4sv;\n    glad_debug_glVertexAttrib4ubv = glad_glVertexAttrib4ubv;\n    glad_debug_glVertexAttrib4uiv = glad_glVertexAttrib4uiv;\n    glad_debug_glVertexAttrib4usv = glad_glVertexAttrib4usv;\n    glad_debug_glVertexAttribDivisorARB = glad_glVertexAttribDivisorARB;\n    glad_debug_glVertexAttribI1i = glad_glVertexAttribI1i;\n    glad_debug_glVertexAttribI1iv = glad_glVertexAttribI1iv;\n    glad_debug_glVertexAttribI1ui = glad_glVertexAttribI1ui;\n    glad_debug_glVertexAttribI1uiv = glad_glVertexAttribI1uiv;\n    glad_debug_glVertexAttribI2i = glad_glVertexAttribI2i;\n    glad_debug_glVertexAttribI2iv = glad_glVertexAttribI2iv;\n    glad_debug_glVertexAttribI2ui = glad_glVertexAttribI2ui;\n    glad_debug_glVertexAttribI2uiv = glad_glVertexAttribI2uiv;\n    glad_debug_glVertexAttribI3i = glad_glVertexAttribI3i;\n    glad_debug_glVertexAttribI3iv = glad_glVertexAttribI3iv;\n    glad_debug_glVertexAttribI3ui = glad_glVertexAttribI3ui;\n    glad_debug_glVertexAttribI3uiv = glad_glVertexAttribI3uiv;\n    glad_debug_glVertexAttribI4bv = glad_glVertexAttribI4bv;\n    glad_debug_glVertexAttribI4i = glad_glVertexAttribI4i;\n    glad_debug_glVertexAttribI4iv = glad_glVertexAttribI4iv;\n    glad_debug_glVertexAttribI4sv = glad_glVertexAttribI4sv;\n    glad_debug_glVertexAttribI4ubv = glad_glVertexAttribI4ubv;\n    glad_debug_glVertexAttribI4ui = glad_glVertexAttribI4ui;\n    glad_debug_glVertexAttribI4uiv = glad_glVertexAttribI4uiv;\n    glad_debug_glVertexAttribI4usv = glad_glVertexAttribI4usv;\n    glad_debug_glVertexAttribIPointer = glad_glVertexAttribIPointer;\n    glad_debug_glVertexAttribPointer = glad_glVertexAttribPointer;\n    glad_debug_glVertexPointer = glad_glVertexPointer;\n    glad_debug_glViewport = glad_glViewport;\n    glad_debug_glWindowPos2d = glad_glWindowPos2d;\n    glad_debug_glWindowPos2dv = glad_glWindowPos2dv;\n    glad_debug_glWindowPos2f = glad_glWindowPos2f;\n    glad_debug_glWindowPos2fv = glad_glWindowPos2fv;\n    glad_debug_glWindowPos2i = glad_glWindowPos2i;\n    glad_debug_glWindowPos2iv = glad_glWindowPos2iv;\n    glad_debug_glWindowPos2s = glad_glWindowPos2s;\n    glad_debug_glWindowPos2sv = glad_glWindowPos2sv;\n    glad_debug_glWindowPos3d = glad_glWindowPos3d;\n    glad_debug_glWindowPos3dv = glad_glWindowPos3dv;\n    glad_debug_glWindowPos3f = glad_glWindowPos3f;\n    glad_debug_glWindowPos3fv = glad_glWindowPos3fv;\n    glad_debug_glWindowPos3i = glad_glWindowPos3i;\n    glad_debug_glWindowPos3iv = glad_glWindowPos3iv;\n    glad_debug_glWindowPos3s = glad_glWindowPos3s;\n    glad_debug_glWindowPos3sv = glad_glWindowPos3sv;\n}\n#ifdef __cplusplus\n}\n#endif\n#endif /* GLAD_GL_IMPLEMENTATION */\n"
  },
  {
    "path": "kitty/gl.c",
    "content": "/*\n * gl.c\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"gl.h\"\n#include <string.h>\n#include <stddef.h>\n#include \"glfw-wrapper.h\"\n#include \"state.h\"\n#include \"png-reader.h\"\n\n// GL setup and error handling {{{\nstatic void\ncheck_for_gl_error(void UNUSED *ret, const char *name, GLADapiproc UNUSED funcptr, int UNUSED len_args, ...) {\n#define f(msg) fatal(\"OpenGL error: %s (calling function: %s)\", msg, name); break;\n    GLenum code = glad_glGetError();\n    switch(code) {\n        case GL_NO_ERROR: break;\n        case GL_INVALID_ENUM:\n            f(\"An enum value is invalid (GL_INVALID_ENUM)\");\n        case GL_INVALID_VALUE:\n            f(\"An numeric value is invalid (GL_INVALID_VALUE)\");\n        case GL_INVALID_OPERATION:\n            f(\"This operation is invalid (GL_INVALID_OPERATION)\");\n        case GL_INVALID_FRAMEBUFFER_OPERATION:\n            f(\"The framebuffer object is not complete (GL_INVALID_FRAMEBUFFER_OPERATION)\");\n        case GL_OUT_OF_MEMORY:\n            f(\"There is not enough memory left to execute the command. (GL_OUT_OF_MEMORY)\");\n        case GL_STACK_UNDERFLOW:\n            f(\"An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_UNDERFLOW)\");\n        case GL_STACK_OVERFLOW:\n            f(\"An attempt has been made to perform an operation that would cause an internal stack to overflow. (GL_STACK_OVERFLOW)\");\n        default:\n            fatal(\"An unknown OpenGL error occurred with code: %d (calling function: %s)\", code, name);\n            break;\n    }\n}\n\nconst char*\ngl_version_string(void) {\n    static char buf[256];\n    int gl_major = GLAD_VERSION_MAJOR(global_state.gl_version);\n    int gl_minor = GLAD_VERSION_MINOR(global_state.gl_version);\n    const char *gvs = (const char*)glGetString(GL_VERSION);\n    snprintf(buf, sizeof(buf), \"'%s' Detected version: %d.%d\", gvs, gl_major, gl_minor);\n    return buf;\n}\n\nvoid\ngl_init(void) {\n    static bool glad_loaded = false;\n    if (!glad_loaded) {\n        global_state.gl_version = gladLoadGL(glfwGetProcAddress);\n        if (!global_state.gl_version) {\n            fatal(\"Loading the OpenGL library failed\");\n        }\n        if (!global_state.debug_rendering) {\n            gladUninstallGLDebug();\n        }\n        gladSetGLPostCallback(check_for_gl_error);\n#define ARB_TEST(name) \\\n        if (!GLAD_GL_ARB_##name) { \\\n            fatal(\"The OpenGL driver on this system is missing the required extension: ARB_%s\", #name); \\\n        }\n        ARB_TEST(texture_storage);\n#undef ARB_TEST\n#ifdef __APPLE__\n        // See nsgl_context.m srgb is always supported on macOS but its OpenGL\n        // drivers dont report the extensions, so hardcode to true.\n        global_state.supports_framebuffer_srgb = true;\n#else\n        global_state.supports_framebuffer_srgb = (GLAD_GL_ARB_framebuffer_sRGB + GLAD_GL_EXT_framebuffer_sRGB) != 0;\n#endif\n        glad_loaded = true;\n        int gl_major = GLAD_VERSION_MAJOR(global_state.gl_version);\n        int gl_minor = GLAD_VERSION_MINOR(global_state.gl_version);\n        if (global_state.debug_rendering) printf(\"[%.3f] GL version string: %s\\n\", monotonic_t_to_s_double(monotonic()), gl_version_string());\n        if (gl_major < OPENGL_REQUIRED_VERSION_MAJOR || (gl_major == OPENGL_REQUIRED_VERSION_MAJOR && gl_minor < OPENGL_REQUIRED_VERSION_MINOR)) {\n            fatal(\"OpenGL version is %d.%d, version >= %d.%d required for kitty\", gl_major, gl_minor, OPENGL_REQUIRED_VERSION_MAJOR, OPENGL_REQUIRED_VERSION_MINOR);\n        }\n    }\n}\n\nconst char*\ncheck_framebuffer_status(void) {\n    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n    switch (status) {\n        case GL_FRAMEBUFFER_COMPLETE: return NULL;\n        case GL_FRAMEBUFFER_UNDEFINED: return(\"GL_FRAMEBUFFER_UNDEFINED\");\n        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return(\"GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT\");\n        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return(\"GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT\");\n        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: return(\"GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER\");\n        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: return(\"GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER\");\n        case GL_FRAMEBUFFER_UNSUPPORTED: return(\"GL_FRAMEBUFFER_UNSUPPORTED\");\n        case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return(\"GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE\");\n        default: return(\"Unknown error\");\n    }\n}\n\nvoid\nfree_texture(GLuint *tex_id) {\n    glDeleteTextures(1, tex_id);\n    *tex_id = 0;\n}\n\nvoid\nfree_framebuffer(GLuint *fb_id) {\n    glDeleteFramebuffers(1, fb_id);\n    *fb_id = 0;\n}\n\nstatic GLuint output_framebuffer = 0;\n\nvoid\nbind_framebuffer_for_output(unsigned fbid) {\n    glBindFramebuffer(GL_FRAMEBUFFER, fbid ? fbid : output_framebuffer);\n}\n\nvoid\nset_framebuffer_to_use_for_output(unsigned fbid) {\n    output_framebuffer = fbid;\n}\n\nstatic void\nset_blending(bool allowed) {\n    if (allowed) { glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); }  // blending of pre-multiplied colors\n    else { glDisable(GL_BLEND); glBlendFunc(GL_ONE, GL_ZERO); }  // no blending\n}\n\nvoid\ndraw_quad(bool blend, unsigned instance_count) {\n    set_blending(blend);\n    if (instance_count) glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, instance_count);\n    else glDrawArrays(GL_TRIANGLE_FAN, 0, 4);\n}\n\nstatic struct {\n    GLsizei items[16][4];\n    size_t used;\n} saved_viewports;\n\nvoid\nset_gpu_viewport(unsigned w, unsigned h) { glViewport(0, 0, w, h); }\n\nViewport\nget_gpu_viewport(void) {\n    GLsizei v[4];\n    glGetIntegerv(GL_VIEWPORT, v);\n    return (Viewport){.left=v[0], .top=v[1], .width=v[2], .height=v[3]};\n}\n\nvoid\nsave_viewport_using_bottom_left_origin(GLsizei newx, GLsizei newy, GLsizei width, GLsizei height) {\n    if (saved_viewports.used >= arraysz(saved_viewports.items)) fatal(\"Too many nested saved viewports\");\n    GLsizei *saved_viewport = saved_viewports.items[saved_viewports.used++];\n    glGetIntegerv(GL_VIEWPORT, saved_viewport);\n    glViewport(newx, newy, width, height);\n}\n\nvoid\nsave_viewport_using_top_left_origin(GLsizei newx, GLsizei newy, GLsizei width, GLsizei height, GLsizei full_framebuffer_height) {\n    // Converts the viewport defined by the specified arguments which are\n    // assumed to be in the usual co-ord system with origin at top left to the\n    // OpenGL viewport co-ord system with origin at bottom left.\n    // Use restore_viewport() to restore the viewport to what it was before.\n    if (saved_viewports.used >= arraysz(saved_viewports.items)) fatal(\"Too many nested saved viewports\");\n    GLsizei *saved_viewport = saved_viewports.items[saved_viewports.used++];\n    glGetIntegerv(GL_VIEWPORT, saved_viewport);\n    newy = full_framebuffer_height - (newy + height);\n    glViewport(newx, newy, width, height);\n}\n\nvoid\nrestore_viewport(void) {\n    if (!saved_viewports.used) fatal(\"Trying to restore a viewport when none is saved\");\n    GLsizei *saved_viewport = saved_viewports.items[--saved_viewports.used];\n    glViewport(saved_viewport[0], saved_viewport[1], saved_viewport[2], saved_viewport[3]);\n}\n\nvoid\nenable_scissor_using_top_left_origin(Viewport vp, unsigned full_framebuffer_height) {\n    glEnable(GL_SCISSOR_TEST);\n    GLsizei newy = full_framebuffer_height - (vp.top + vp.height);\n    glScissor(vp.left, newy, vp.width, vp.height);\n}\n\nvoid\ndisable_scissor(void) { glDisable(GL_SCISSOR_TEST); }\n\nstatic float\nlinear_to_srgb(float c) { return (c <= 0.0031308f) ? 12.92f * c : 1.055f * powf(c, 1.0f / 2.4f) - 0.055f; }\n\nvoid\nsave_texture_as_png(uint32_t texture_id, const char *filename) {\n    GLint prev_tex = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex);\n    glBindTexture(GL_TEXTURE_2D, texture_id);\n    int width = 0, height = 0;\n    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);\n    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);\n    size_t sz = sizeof(uint32_t) * width * height;\n    uint32_t* data = malloc(sz);\n    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);\n    // assume data is linear and pre-multiplied\n    for (int i = 0; i < width * height; i++) {\n        uint32_t px = data[i];\n        uint8_t r = (px >>  0) & 0xFF; uint8_t g = (px >>  8) & 0xFF; uint8_t b = (px >> 16) & 0xFF;\n        uint8_t a = (px >> 24) & 0xFF; float alpha = a / 255.0f;\n        float rf = 0, gf = 0, bf = 0;\n        if (alpha > 0.0f) { rf = (r / 255.0f) / alpha; gf = (g / 255.0f) / alpha; bf = (b / 255.0f) / alpha; }\n        rf = linear_to_srgb(rf); gf = linear_to_srgb(gf); bf = linear_to_srgb(bf);\n        r = (uint8_t)(rf*255); g = (uint8_t)(gf * 255); b = (uint8_t)(bf * 255);\n        data[i] = (r <<  0) | (g << 8) | (b << 16) | (a << 24);\n    }\n\n    const char *png = png_from_32bit_rgba((char*)data, width, height, &sz, true);\n    if (!sz) fatal(\"Failed to save PNG to %s with error: %s\", filename, png);\n    free(data);\n    FILE* file = fopen(filename, \"wb\");\n    fwrite(png, 1, sz, file);\n    fclose(file);\n    glBindTexture(GL_TEXTURE_2D, prev_tex);\n}\n\n\n// }}}\n\n// Programs {{{\n\nstatic Program programs[64] = {{0}};\n\nGLuint\ncompile_shaders(GLenum shader_type, GLsizei count, const GLchar * const * source) {\n    GLuint shader_id = glCreateShader(shader_type);\n    glShaderSource(shader_id, count, source, NULL);\n    glCompileShader(shader_id);\n    GLint ret = GL_FALSE;\n    glGetShaderiv(shader_id, GL_COMPILE_STATUS, &ret);\n    if (ret != GL_TRUE) {\n        GLsizei len;\n        static char glbuf[4096];\n        glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf);\n        glDeleteShader(shader_id);\n        const char *shader_type_name = \"unknown_type\";\n        switch(shader_type) {\n            case GL_VERTEX_SHADER:\n                shader_type_name = \"vertex\"; break;\n            case GL_FRAGMENT_SHADER:\n                shader_type_name = \"fragment\"; break;\n        }\n        PyErr_Format(PyExc_ValueError, \"Failed to compile GLSL %s shader:\\n%s\", shader_type_name, glbuf);\n        return 0;\n    }\n    return shader_id;\n}\n\nProgram*\nprogram_ptr(int program) { return programs + (size_t)program; }\n\nGLuint\nprogram_id(int program) { return programs[program].id; }\n\n\nvoid\ninit_uniforms(int program) {\n    Program *p = programs + program;\n    glGetProgramiv(p->id, GL_ACTIVE_UNIFORMS, &(p->num_of_uniforms));\n    for (GLint i = 0; i < p->num_of_uniforms; i++) {\n        Uniform *u = p->uniforms + i;\n        glGetActiveUniform(p->id, (GLuint)i, sizeof(u->name)/sizeof(u->name[0]), NULL, &(u->size), &(u->type), u->name);\n        char *l = strchr(u->name, '[');\n        if (l) *l = 0;\n        u->location = glGetUniformLocation(p->id, u->name);\n        u->idx = i;\n    }\n}\n\nGLint\nget_uniform_location(int program, const char *name) {\n    Program *p = programs + program;\n    const size_t n = strlen(name) + 1;\n    for (GLint i = 0; i < p->num_of_uniforms; i++) {\n        Uniform *u = p->uniforms + i;\n        if (strncmp(u->name, name, n) == 0) return u->location;\n    }\n    return -1;\n}\n\nGLint\nget_uniform_information(int program, const char *name, GLenum information_type) {\n    GLint q; GLuint t;\n    const char* names[] = {\"\"};\n    names[0] = name;\n    GLuint pid = program_id(program);\n    glGetUniformIndices(pid, 1, (void*)names, &t);\n    glGetActiveUniformsiv(pid, 1, &t, information_type, &q);\n    return q;\n}\n\nGLint\nattrib_location(int program, const char *name) {\n    GLint ans = glGetAttribLocation(programs[program].id, name);\n    return ans;\n}\n\nGLuint\nblock_index(int program, const char *name) {\n    GLuint ans = glGetUniformBlockIndex(programs[program].id, name);\n    if (ans == GL_INVALID_INDEX) { fatal(\"Could not find block index for %s\", name); }\n    return ans;\n}\n\n\nGLint\nblock_size(int program, GLuint block_index) {\n    GLint ans;\n    glGetActiveUniformBlockiv(programs[program].id, block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ans);\n    return ans;\n}\n\nvoid\nbind_program(int program) {\n    glUseProgram(programs[program].id);\n}\n\nvoid\nunbind_program(void) {\n    glUseProgram(0);\n}\n// }}}\n\n// Buffers {{{\n\ntypedef struct {\n    GLuint id;\n    GLsizeiptr size;\n    GLenum usage;\n} Buffer;\n\n\nstatic Buffer buffers[MAX_CHILDREN * 6 + 4] = {{0}};\n\nstatic ssize_t\ncreate_buffer(GLenum usage) {\n    GLuint buffer_id;\n    glGenBuffers(1, &buffer_id);\n    for (size_t i = 0; i < sizeof(buffers)/sizeof(buffers[0]); i++) {\n        if (buffers[i].id == 0) {\n            buffers[i].id = buffer_id;\n            buffers[i].size = 0;\n            buffers[i].usage = usage;\n            return i;\n        }\n    }\n    glDeleteBuffers(1, &buffer_id);\n    fatal(\"Too many buffers\");\n    return -1;\n}\n\nstatic void\ndelete_buffer(ssize_t buf_idx) {\n    glDeleteBuffers(1, &(buffers[buf_idx].id));\n    buffers[buf_idx].id = 0;\n    buffers[buf_idx].size = 0;\n}\n\nstatic GLuint\nbind_buffer(ssize_t buf_idx) {\n    glBindBuffer(buffers[buf_idx].usage, buffers[buf_idx].id);\n    return buffers[buf_idx].id;\n}\n\nstatic void\nunbind_buffer(ssize_t buf_idx) {\n    glBindBuffer(buffers[buf_idx].usage, 0);\n}\n\nstatic void\nalloc_buffer(ssize_t idx, GLsizeiptr size, GLenum usage) {\n    Buffer *b = buffers + idx;\n    if (b->size == size) return;\n    b->size = size;\n    glBufferData(b->usage, size, NULL, usage);\n}\n\nstatic void*\nmap_buffer(ssize_t idx, GLenum access) {\n    void *ans = glMapBuffer(buffers[idx].usage, access);\n    return ans;\n}\n\nstatic void*\nmap_buffer_range(ssize_t idx, GLbitfield access, int offset, unsigned size) {\n    return glMapBufferRange(buffers[idx].usage, offset, size, access);\n}\n\n\nstatic void\nunmap_buffer(ssize_t idx) {\n    glUnmapBuffer(buffers[idx].usage);\n}\n\n// }}}\n\n// Vertex Array Objects (VAO) {{{\n\ntypedef struct {\n    GLuint id;\n    size_t num_buffers;\n    ssize_t buffers[10];\n} VAO;\n\nstatic VAO vaos[4*MAX_CHILDREN + 10] = {{0}};\n\nssize_t\ncreate_vao(void) {\n    GLuint vao_id;\n    glGenVertexArrays(1, &vao_id);\n    for (size_t i = 0; i < sizeof(vaos)/sizeof(vaos[0]); i++) {\n        if (!vaos[i].id) {\n            vaos[i].id = vao_id;\n            vaos[i].num_buffers = 0;\n            glBindVertexArray(vao_id);\n            return i;\n        }\n    }\n    glDeleteVertexArrays(1, &vao_id);\n    fatal(\"Too many VAOs\");\n    return -1;\n}\n\nsize_t\nadd_buffer_to_vao(ssize_t vao_idx, GLenum usage) {\n    VAO* vao = vaos + vao_idx;\n    if (vao->num_buffers >= sizeof(vao->buffers) / sizeof(vao->buffers[0])) {\n        fatal(\"Too many buffers in a single VAO\");\n    }\n    ssize_t buf = create_buffer(usage);\n    vao->buffers[vao->num_buffers++] = buf;\n    return vao->num_buffers - 1;\n}\n\nstatic void\nadd_located_attribute_to_vao(ssize_t vao_idx, GLint aloc, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {\n    VAO *vao = vaos + vao_idx;\n    if (!vao->num_buffers) fatal(\"You must create a buffer for this attribute first\");\n    ssize_t buf = vao->buffers[vao->num_buffers - 1];\n    bind_buffer(buf);\n    glEnableVertexAttribArray(aloc);\n    switch(data_type) {\n        case GL_BYTE:\n        case GL_UNSIGNED_BYTE:\n        case GL_SHORT:\n        case GL_UNSIGNED_SHORT:\n        case GL_INT:\n        case GL_UNSIGNED_INT:\n            glVertexAttribIPointer(aloc, size, data_type, stride, offset);\n            break;\n        default:\n            glVertexAttribPointer(aloc, size, data_type, GL_FALSE, stride, offset);\n            break;\n    }\n    if (divisor) {\n        glVertexAttribDivisorARB(aloc, divisor);\n    }\n    unbind_buffer(buf);\n}\n\n\nvoid\nadd_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {\n    GLint aloc = attrib_location(p, name);\n    if (aloc == -1) fatal(\"No attribute named: %s found in this program\", name);\n    add_located_attribute_to_vao(vao_idx, aloc, size, data_type, stride, offset, divisor);\n}\n\nvoid\nremove_vao(ssize_t vao_idx) {\n    VAO *vao = vaos + vao_idx;\n    while (vao->num_buffers) {\n        vao->num_buffers--;\n        delete_buffer(vao->buffers[vao->num_buffers]);\n    }\n    glDeleteVertexArrays(1, &(vao->id));\n    vaos[vao_idx].id = 0;\n}\n\nvoid\nbind_vertex_array(ssize_t vao_idx) {\n    glBindVertexArray(vaos[vao_idx].id);\n}\n\nvoid\nunbind_vertex_array(void) {\n    glBindVertexArray(0);\n}\n\nssize_t\nalloc_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage) {\n    ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];\n    bind_buffer(buf_idx);\n    alloc_buffer(buf_idx, size, usage);\n    return buf_idx;\n}\n\nvoid*\nmap_vao_buffer_for_write_only(ssize_t vao_idx, size_t bufnum, int offset, unsigned size) {\n    ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];\n    bind_buffer(buf_idx);\n    return map_buffer_range(buf_idx, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT, offset, size);\n}\n\nvoid*\nalloc_and_map_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, bool frequently_updated) {\n    ssize_t buf_idx = alloc_vao_buffer(vao_idx, size, bufnum, frequently_updated ? GL_STREAM_DRAW : GL_STATIC_DRAW);\n    return map_buffer(buf_idx, GL_WRITE_ONLY);\n}\n\nvoid\nbind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index) {\n    ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];\n    glBindBufferBase(GL_UNIFORM_BUFFER, block_index, buffers[buf_idx].id);\n}\n\nvoid\nunmap_vao_buffer(ssize_t vao_idx, size_t bufnum) {\n    ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];\n    unmap_buffer(buf_idx);\n    unbind_buffer(buf_idx);\n}\n\n// }}}\n"
  },
  {
    "path": "kitty/gl.h",
    "content": "/*\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n#include \"gl-wrapper.h\"\n\ntypedef struct {\n    GLint size, index;\n} UniformBlock;\n\ntypedef struct {\n    GLint offset, stride, size;\n} ArrayInformation;\n\ntypedef struct {\n    char name[256];\n    GLint size, location, idx;\n    GLenum type;\n} Uniform;\n\ntypedef struct {\n    GLuint id;\n    Uniform uniforms[256];\n    GLint num_of_uniforms;\n} Program;\n\ntypedef struct Viewport { unsigned left, top, width, height; } Viewport;\n\nvoid gl_init(void);\nconst char* gl_version_string(void);\nvoid set_gpu_viewport(unsigned w, unsigned h);\nViewport get_gpu_viewport(void);\nvoid draw_quad(bool blend, unsigned instance_count);\nvoid save_texture_as_png(uint32_t texture_id, const char *filename);\nvoid free_texture(GLuint *tex_id);\nvoid free_framebuffer(GLuint *fb_id);\nvoid remove_vao(ssize_t vao_idx);\nvoid init_uniforms(int program);\nGLuint program_id(int program);\nProgram* program_ptr(int program);\nGLuint block_index(int program, const char *name);\nGLint block_size(int program, GLuint block_index);\nGLint get_uniform_location(int program, const char *name);\nGLint get_uniform_information(int program, const char *name, GLenum information_type);\nGLint attrib_location(int program, const char *name);\nssize_t create_vao(void);\nsize_t add_buffer_to_vao(ssize_t vao_idx, GLenum usage);\nvoid add_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor);\nssize_t alloc_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage);\nvoid* alloc_and_map_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, bool frequently_updated);\nvoid unmap_vao_buffer(ssize_t vao_idx, size_t bufnum);\nvoid* map_vao_buffer(ssize_t vao_idx, size_t bufnum, GLenum access);\nvoid* map_vao_buffer_for_write_only(ssize_t vao_idx, size_t bufnum, int offset, unsigned size);\nvoid bind_program(int program);\nvoid bind_vertex_array(ssize_t vao_idx);\nvoid bind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index);\nvoid unbind_vertex_array(void);\nvoid unbind_program(void);\nGLuint compile_shaders(GLenum shader_type, GLsizei count, const GLchar * const * string);\nvoid save_viewport_using_top_left_origin(GLsizei x, GLsizei y, GLsizei width, GLsizei height, GLsizei full_framebuffer_height);\nvoid save_viewport_using_bottom_left_origin(GLsizei x, GLsizei y, GLsizei width, GLsizei height);\nconst char* check_framebuffer_status(void);\nvoid restore_viewport(void);\nvoid bind_framebuffer_for_output(unsigned fbid);\nvoid set_framebuffer_to_use_for_output(unsigned fbid);\nvoid enable_scissor_using_top_left_origin(Viewport, unsigned);\nvoid disable_scissor(void);\n"
  },
  {
    "path": "kitty/glfw-wrapper.c",
    "content": "\n// generated by glfw.py DO NOT edit\n\n#define GFW_EXTERN\n#include \"data-types.h\"\n#include \"glfw-wrapper.h\"\n#include <dlfcn.h>\n\nstatic void* handle = NULL;\n\n#define fail(msg, ...) { snprintf(buf, sizeof(buf), msg, __VA_ARGS__); return buf; }\n\nconst char*\nload_glfw(const char* path) {\n    static char buf[2048];\n    handle = dlopen(path, RTLD_LAZY);\n    if (handle == NULL) fail(\"Failed to dlopen %s with error: %s\", path, dlerror());\n    dlerror();\n\n    *(void **) (&glfwInit_impl) = dlsym(handle, \"glfwInit\");\n    if (glfwInit_impl == NULL) fail(\"Failed to load glfw function glfwInit with error: %s\", dlerror());\n\n    *(void **) (&glfwRunMainLoop_impl) = dlsym(handle, \"glfwRunMainLoop\");\n    if (glfwRunMainLoop_impl == NULL) fail(\"Failed to load glfw function glfwRunMainLoop with error: %s\", dlerror());\n\n    *(void **) (&glfwStopMainLoop_impl) = dlsym(handle, \"glfwStopMainLoop\");\n    if (glfwStopMainLoop_impl == NULL) fail(\"Failed to load glfw function glfwStopMainLoop with error: %s\", dlerror());\n\n    *(void **) (&glfwAddTimer_impl) = dlsym(handle, \"glfwAddTimer\");\n    if (glfwAddTimer_impl == NULL) fail(\"Failed to load glfw function glfwAddTimer with error: %s\", dlerror());\n\n    *(void **) (&glfwUpdateTimer_impl) = dlsym(handle, \"glfwUpdateTimer\");\n    if (glfwUpdateTimer_impl == NULL) fail(\"Failed to load glfw function glfwUpdateTimer with error: %s\", dlerror());\n\n    *(void **) (&glfwRemoveTimer_impl) = dlsym(handle, \"glfwRemoveTimer\");\n    if (glfwRemoveTimer_impl == NULL) fail(\"Failed to load glfw function glfwRemoveTimer with error: %s\", dlerror());\n\n    *(void **) (&glfwSetDrawTextFunction_impl) = dlsym(handle, \"glfwSetDrawTextFunction\");\n    if (glfwSetDrawTextFunction_impl == NULL) fail(\"Failed to load glfw function glfwSetDrawTextFunction with error: %s\", dlerror());\n\n    *(void **) (&glfwSetCurrentSelectionCallback_impl) = dlsym(handle, \"glfwSetCurrentSelectionCallback\");\n    if (glfwSetCurrentSelectionCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetCurrentSelectionCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, \"glfwSetHasCurrentSelectionCallback\");\n    if (glfwSetHasCurrentSelectionCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetHasCurrentSelectionCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetIMECursorPositionCallback_impl) = dlsym(handle, \"glfwSetIMECursorPositionCallback\");\n    if (glfwSetIMECursorPositionCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetIMECursorPositionCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwIsLayerShellSupported_impl) = dlsym(handle, \"glfwIsLayerShellSupported\");\n    if (glfwIsLayerShellSupported_impl == NULL) fail(\"Failed to load glfw function glfwIsLayerShellSupported with error: %s\", dlerror());\n\n    *(void **) (&glfwTerminate_impl) = dlsym(handle, \"glfwTerminate\");\n    if (glfwTerminate_impl == NULL) fail(\"Failed to load glfw function glfwTerminate with error: %s\", dlerror());\n\n    *(void **) (&glfwInitHint_impl) = dlsym(handle, \"glfwInitHint\");\n    if (glfwInitHint_impl == NULL) fail(\"Failed to load glfw function glfwInitHint with error: %s\", dlerror());\n\n    *(void **) (&glfwGetVersion_impl) = dlsym(handle, \"glfwGetVersion\");\n    if (glfwGetVersion_impl == NULL) fail(\"Failed to load glfw function glfwGetVersion with error: %s\", dlerror());\n\n    *(void **) (&glfwGetVersionString_impl) = dlsym(handle, \"glfwGetVersionString\");\n    if (glfwGetVersionString_impl == NULL) fail(\"Failed to load glfw function glfwGetVersionString with error: %s\", dlerror());\n\n    *(void **) (&glfwGetError_impl) = dlsym(handle, \"glfwGetError\");\n    if (glfwGetError_impl == NULL) fail(\"Failed to load glfw function glfwGetError with error: %s\", dlerror());\n\n    *(void **) (&glfwSetErrorCallback_impl) = dlsym(handle, \"glfwSetErrorCallback\");\n    if (glfwSetErrorCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetErrorCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMonitors_impl) = dlsym(handle, \"glfwGetMonitors\");\n    if (glfwGetMonitors_impl == NULL) fail(\"Failed to load glfw function glfwGetMonitors with error: %s\", dlerror());\n\n    *(void **) (&glfwGetPrimaryMonitor_impl) = dlsym(handle, \"glfwGetPrimaryMonitor\");\n    if (glfwGetPrimaryMonitor_impl == NULL) fail(\"Failed to load glfw function glfwGetPrimaryMonitor with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMonitorPos_impl) = dlsym(handle, \"glfwGetMonitorPos\");\n    if (glfwGetMonitorPos_impl == NULL) fail(\"Failed to load glfw function glfwGetMonitorPos with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMonitorWorkarea_impl) = dlsym(handle, \"glfwGetMonitorWorkarea\");\n    if (glfwGetMonitorWorkarea_impl == NULL) fail(\"Failed to load glfw function glfwGetMonitorWorkarea with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMonitorPhysicalSize_impl) = dlsym(handle, \"glfwGetMonitorPhysicalSize\");\n    if (glfwGetMonitorPhysicalSize_impl == NULL) fail(\"Failed to load glfw function glfwGetMonitorPhysicalSize with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMonitorContentScale_impl) = dlsym(handle, \"glfwGetMonitorContentScale\");\n    if (glfwGetMonitorContentScale_impl == NULL) fail(\"Failed to load glfw function glfwGetMonitorContentScale with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMonitorName_impl) = dlsym(handle, \"glfwGetMonitorName\");\n    if (glfwGetMonitorName_impl == NULL) fail(\"Failed to load glfw function glfwGetMonitorName with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMonitorDescription_impl) = dlsym(handle, \"glfwGetMonitorDescription\");\n    if (glfwGetMonitorDescription_impl == NULL) fail(\"Failed to load glfw function glfwGetMonitorDescription with error: %s\", dlerror());\n\n    *(void **) (&glfwSetMonitorUserPointer_impl) = dlsym(handle, \"glfwSetMonitorUserPointer\");\n    if (glfwSetMonitorUserPointer_impl == NULL) fail(\"Failed to load glfw function glfwSetMonitorUserPointer with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMonitorUserPointer_impl) = dlsym(handle, \"glfwGetMonitorUserPointer\");\n    if (glfwGetMonitorUserPointer_impl == NULL) fail(\"Failed to load glfw function glfwGetMonitorUserPointer with error: %s\", dlerror());\n\n    *(void **) (&glfwSetMonitorCallback_impl) = dlsym(handle, \"glfwSetMonitorCallback\");\n    if (glfwSetMonitorCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetMonitorCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwGetVideoModes_impl) = dlsym(handle, \"glfwGetVideoModes\");\n    if (glfwGetVideoModes_impl == NULL) fail(\"Failed to load glfw function glfwGetVideoModes with error: %s\", dlerror());\n\n    *(void **) (&glfwGetVideoMode_impl) = dlsym(handle, \"glfwGetVideoMode\");\n    if (glfwGetVideoMode_impl == NULL) fail(\"Failed to load glfw function glfwGetVideoMode with error: %s\", dlerror());\n\n    *(void **) (&glfwSetGamma_impl) = dlsym(handle, \"glfwSetGamma\");\n    if (glfwSetGamma_impl == NULL) fail(\"Failed to load glfw function glfwSetGamma with error: %s\", dlerror());\n\n    *(void **) (&glfwGetGammaRamp_impl) = dlsym(handle, \"glfwGetGammaRamp\");\n    if (glfwGetGammaRamp_impl == NULL) fail(\"Failed to load glfw function glfwGetGammaRamp with error: %s\", dlerror());\n\n    *(void **) (&glfwSetGammaRamp_impl) = dlsym(handle, \"glfwSetGammaRamp\");\n    if (glfwSetGammaRamp_impl == NULL) fail(\"Failed to load glfw function glfwSetGammaRamp with error: %s\", dlerror());\n\n    *(void **) (&glfwDefaultWindowHints_impl) = dlsym(handle, \"glfwDefaultWindowHints\");\n    if (glfwDefaultWindowHints_impl == NULL) fail(\"Failed to load glfw function glfwDefaultWindowHints with error: %s\", dlerror());\n\n    *(void **) (&glfwWindowHint_impl) = dlsym(handle, \"glfwWindowHint\");\n    if (glfwWindowHint_impl == NULL) fail(\"Failed to load glfw function glfwWindowHint with error: %s\", dlerror());\n\n    *(void **) (&glfwWindowHintString_impl) = dlsym(handle, \"glfwWindowHintString\");\n    if (glfwWindowHintString_impl == NULL) fail(\"Failed to load glfw function glfwWindowHintString with error: %s\", dlerror());\n\n    *(void **) (&glfwCreateWindow_impl) = dlsym(handle, \"glfwCreateWindow\");\n    if (glfwCreateWindow_impl == NULL) fail(\"Failed to load glfw function glfwCreateWindow with error: %s\", dlerror());\n\n    *(void **) (&glfwToggleFullscreen_impl) = dlsym(handle, \"glfwToggleFullscreen\");\n    if (glfwToggleFullscreen_impl == NULL) fail(\"Failed to load glfw function glfwToggleFullscreen with error: %s\", dlerror());\n\n    *(void **) (&glfwIsFullscreen_impl) = dlsym(handle, \"glfwIsFullscreen\");\n    if (glfwIsFullscreen_impl == NULL) fail(\"Failed to load glfw function glfwIsFullscreen with error: %s\", dlerror());\n\n    *(void **) (&glfwAreSwapsAllowed_impl) = dlsym(handle, \"glfwAreSwapsAllowed\");\n    if (glfwAreSwapsAllowed_impl == NULL) fail(\"Failed to load glfw function glfwAreSwapsAllowed with error: %s\", dlerror());\n\n    *(void **) (&glfwGetLayerShellConfig_impl) = dlsym(handle, \"glfwGetLayerShellConfig\");\n    if (glfwGetLayerShellConfig_impl == NULL) fail(\"Failed to load glfw function glfwGetLayerShellConfig with error: %s\", dlerror());\n\n    *(void **) (&glfwSetLayerShellConfig_impl) = dlsym(handle, \"glfwSetLayerShellConfig\");\n    if (glfwSetLayerShellConfig_impl == NULL) fail(\"Failed to load glfw function glfwSetLayerShellConfig with error: %s\", dlerror());\n\n    *(void **) (&glfwDestroyWindow_impl) = dlsym(handle, \"glfwDestroyWindow\");\n    if (glfwDestroyWindow_impl == NULL) fail(\"Failed to load glfw function glfwDestroyWindow with error: %s\", dlerror());\n\n    *(void **) (&glfwWindowShouldClose_impl) = dlsym(handle, \"glfwWindowShouldClose\");\n    if (glfwWindowShouldClose_impl == NULL) fail(\"Failed to load glfw function glfwWindowShouldClose with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowShouldClose_impl) = dlsym(handle, \"glfwSetWindowShouldClose\");\n    if (glfwSetWindowShouldClose_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowShouldClose with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowTitle_impl) = dlsym(handle, \"glfwSetWindowTitle\");\n    if (glfwSetWindowTitle_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowTitle with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowIcon_impl) = dlsym(handle, \"glfwSetWindowIcon\");\n    if (glfwSetWindowIcon_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowIcon with error: %s\", dlerror());\n\n    *(void **) (&glfwGetWindowPos_impl) = dlsym(handle, \"glfwGetWindowPos\");\n    if (glfwGetWindowPos_impl == NULL) fail(\"Failed to load glfw function glfwGetWindowPos with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowPos_impl) = dlsym(handle, \"glfwSetWindowPos\");\n    if (glfwSetWindowPos_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowPos with error: %s\", dlerror());\n\n    *(void **) (&glfwGetWindowSize_impl) = dlsym(handle, \"glfwGetWindowSize\");\n    if (glfwGetWindowSize_impl == NULL) fail(\"Failed to load glfw function glfwGetWindowSize with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowSizeLimits_impl) = dlsym(handle, \"glfwSetWindowSizeLimits\");\n    if (glfwSetWindowSizeLimits_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowSizeLimits with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowSizeIncrements_impl) = dlsym(handle, \"glfwSetWindowSizeIncrements\");\n    if (glfwSetWindowSizeIncrements_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowSizeIncrements with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowAspectRatio_impl) = dlsym(handle, \"glfwSetWindowAspectRatio\");\n    if (glfwSetWindowAspectRatio_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowAspectRatio with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowSize_impl) = dlsym(handle, \"glfwSetWindowSize\");\n    if (glfwSetWindowSize_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowSize with error: %s\", dlerror());\n\n    *(void **) (&glfwGetFramebufferSize_impl) = dlsym(handle, \"glfwGetFramebufferSize\");\n    if (glfwGetFramebufferSize_impl == NULL) fail(\"Failed to load glfw function glfwGetFramebufferSize with error: %s\", dlerror());\n\n    *(void **) (&glfwGetWindowFrameSize_impl) = dlsym(handle, \"glfwGetWindowFrameSize\");\n    if (glfwGetWindowFrameSize_impl == NULL) fail(\"Failed to load glfw function glfwGetWindowFrameSize with error: %s\", dlerror());\n\n    *(void **) (&glfwGetWindowContentScale_impl) = dlsym(handle, \"glfwGetWindowContentScale\");\n    if (glfwGetWindowContentScale_impl == NULL) fail(\"Failed to load glfw function glfwGetWindowContentScale with error: %s\", dlerror());\n\n    *(void **) (&glfwGetDoubleClickInterval_impl) = dlsym(handle, \"glfwGetDoubleClickInterval\");\n    if (glfwGetDoubleClickInterval_impl == NULL) fail(\"Failed to load glfw function glfwGetDoubleClickInterval with error: %s\", dlerror());\n\n    *(void **) (&glfwGetWindowOpacity_impl) = dlsym(handle, \"glfwGetWindowOpacity\");\n    if (glfwGetWindowOpacity_impl == NULL) fail(\"Failed to load glfw function glfwGetWindowOpacity with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowOpacity_impl) = dlsym(handle, \"glfwSetWindowOpacity\");\n    if (glfwSetWindowOpacity_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowOpacity with error: %s\", dlerror());\n\n    *(void **) (&glfwIconifyWindow_impl) = dlsym(handle, \"glfwIconifyWindow\");\n    if (glfwIconifyWindow_impl == NULL) fail(\"Failed to load glfw function glfwIconifyWindow with error: %s\", dlerror());\n\n    *(void **) (&glfwRestoreWindow_impl) = dlsym(handle, \"glfwRestoreWindow\");\n    if (glfwRestoreWindow_impl == NULL) fail(\"Failed to load glfw function glfwRestoreWindow with error: %s\", dlerror());\n\n    *(void **) (&glfwMaximizeWindow_impl) = dlsym(handle, \"glfwMaximizeWindow\");\n    if (glfwMaximizeWindow_impl == NULL) fail(\"Failed to load glfw function glfwMaximizeWindow with error: %s\", dlerror());\n\n    *(void **) (&glfwShowWindow_impl) = dlsym(handle, \"glfwShowWindow\");\n    if (glfwShowWindow_impl == NULL) fail(\"Failed to load glfw function glfwShowWindow with error: %s\", dlerror());\n\n    *(void **) (&glfwHideWindow_impl) = dlsym(handle, \"glfwHideWindow\");\n    if (glfwHideWindow_impl == NULL) fail(\"Failed to load glfw function glfwHideWindow with error: %s\", dlerror());\n\n    *(void **) (&glfwFocusWindow_impl) = dlsym(handle, \"glfwFocusWindow\");\n    if (glfwFocusWindow_impl == NULL) fail(\"Failed to load glfw function glfwFocusWindow with error: %s\", dlerror());\n\n    *(void **) (&glfwRequestWindowAttention_impl) = dlsym(handle, \"glfwRequestWindowAttention\");\n    if (glfwRequestWindowAttention_impl == NULL) fail(\"Failed to load glfw function glfwRequestWindowAttention with error: %s\", dlerror());\n\n    *(void **) (&glfwWindowBell_impl) = dlsym(handle, \"glfwWindowBell\");\n    if (glfwWindowBell_impl == NULL) fail(\"Failed to load glfw function glfwWindowBell with error: %s\", dlerror());\n\n    *(void **) (&glfwGetWindowMonitor_impl) = dlsym(handle, \"glfwGetWindowMonitor\");\n    if (glfwGetWindowMonitor_impl == NULL) fail(\"Failed to load glfw function glfwGetWindowMonitor with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowMonitor_impl) = dlsym(handle, \"glfwSetWindowMonitor\");\n    if (glfwSetWindowMonitor_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowMonitor with error: %s\", dlerror());\n\n    *(void **) (&glfwGetWindowAttrib_impl) = dlsym(handle, \"glfwGetWindowAttrib\");\n    if (glfwGetWindowAttrib_impl == NULL) fail(\"Failed to load glfw function glfwGetWindowAttrib with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowAttrib_impl) = dlsym(handle, \"glfwSetWindowAttrib\");\n    if (glfwSetWindowAttrib_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowAttrib with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowBlur_impl) = dlsym(handle, \"glfwSetWindowBlur\");\n    if (glfwSetWindowBlur_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowBlur with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowUserPointer_impl) = dlsym(handle, \"glfwSetWindowUserPointer\");\n    if (glfwSetWindowUserPointer_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowUserPointer with error: %s\", dlerror());\n\n    *(void **) (&glfwGetWindowUserPointer_impl) = dlsym(handle, \"glfwGetWindowUserPointer\");\n    if (glfwGetWindowUserPointer_impl == NULL) fail(\"Failed to load glfw function glfwGetWindowUserPointer with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowPosCallback_impl) = dlsym(handle, \"glfwSetWindowPosCallback\");\n    if (glfwSetWindowPosCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowPosCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowSizeCallback_impl) = dlsym(handle, \"glfwSetWindowSizeCallback\");\n    if (glfwSetWindowSizeCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowSizeCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowCloseCallback_impl) = dlsym(handle, \"glfwSetWindowCloseCallback\");\n    if (glfwSetWindowCloseCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowCloseCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetApplicationCloseCallback_impl) = dlsym(handle, \"glfwSetApplicationCloseCallback\");\n    if (glfwSetApplicationCloseCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetApplicationCloseCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetSystemColorThemeChangeCallback_impl) = dlsym(handle, \"glfwSetSystemColorThemeChangeCallback\");\n    if (glfwSetSystemColorThemeChangeCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetSystemColorThemeChangeCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetClipboardLostCallback_impl) = dlsym(handle, \"glfwSetClipboardLostCallback\");\n    if (glfwSetClipboardLostCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetClipboardLostCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwGetCurrentSystemColorTheme_impl) = dlsym(handle, \"glfwGetCurrentSystemColorTheme\");\n    if (glfwGetCurrentSystemColorTheme_impl == NULL) fail(\"Failed to load glfw function glfwGetCurrentSystemColorTheme with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowRefreshCallback_impl) = dlsym(handle, \"glfwSetWindowRefreshCallback\");\n    if (glfwSetWindowRefreshCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowRefreshCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowFocusCallback_impl) = dlsym(handle, \"glfwSetWindowFocusCallback\");\n    if (glfwSetWindowFocusCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowFocusCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowOcclusionCallback_impl) = dlsym(handle, \"glfwSetWindowOcclusionCallback\");\n    if (glfwSetWindowOcclusionCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowOcclusionCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowIconifyCallback_impl) = dlsym(handle, \"glfwSetWindowIconifyCallback\");\n    if (glfwSetWindowIconifyCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowIconifyCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowMaximizeCallback_impl) = dlsym(handle, \"glfwSetWindowMaximizeCallback\");\n    if (glfwSetWindowMaximizeCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowMaximizeCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetFramebufferSizeCallback_impl) = dlsym(handle, \"glfwSetFramebufferSizeCallback\");\n    if (glfwSetFramebufferSizeCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetFramebufferSizeCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetWindowContentScaleCallback_impl) = dlsym(handle, \"glfwSetWindowContentScaleCallback\");\n    if (glfwSetWindowContentScaleCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetWindowContentScaleCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwPostEmptyEvent_impl) = dlsym(handle, \"glfwPostEmptyEvent\");\n    if (glfwPostEmptyEvent_impl == NULL) fail(\"Failed to load glfw function glfwPostEmptyEvent with error: %s\", dlerror());\n\n    *(void **) (&glfwGetIgnoreOSKeyboardProcessing_impl) = dlsym(handle, \"glfwGetIgnoreOSKeyboardProcessing\");\n    if (glfwGetIgnoreOSKeyboardProcessing_impl == NULL) fail(\"Failed to load glfw function glfwGetIgnoreOSKeyboardProcessing with error: %s\", dlerror());\n\n    *(void **) (&glfwSetIgnoreOSKeyboardProcessing_impl) = dlsym(handle, \"glfwSetIgnoreOSKeyboardProcessing\");\n    if (glfwSetIgnoreOSKeyboardProcessing_impl == NULL) fail(\"Failed to load glfw function glfwSetIgnoreOSKeyboardProcessing with error: %s\", dlerror());\n\n    *(void **) (&glfwGrabKeyboard_impl) = dlsym(handle, \"glfwGrabKeyboard\");\n    if (glfwGrabKeyboard_impl == NULL) fail(\"Failed to load glfw function glfwGrabKeyboard with error: %s\", dlerror());\n\n    *(void **) (&glfwGetInputMode_impl) = dlsym(handle, \"glfwGetInputMode\");\n    if (glfwGetInputMode_impl == NULL) fail(\"Failed to load glfw function glfwGetInputMode with error: %s\", dlerror());\n\n    *(void **) (&glfwSetInputMode_impl) = dlsym(handle, \"glfwSetInputMode\");\n    if (glfwSetInputMode_impl == NULL) fail(\"Failed to load glfw function glfwSetInputMode with error: %s\", dlerror());\n\n    *(void **) (&glfwRawMouseMotionSupported_impl) = dlsym(handle, \"glfwRawMouseMotionSupported\");\n    if (glfwRawMouseMotionSupported_impl == NULL) fail(\"Failed to load glfw function glfwRawMouseMotionSupported with error: %s\", dlerror());\n\n    *(void **) (&glfwGetKeyName_impl) = dlsym(handle, \"glfwGetKeyName\");\n    if (glfwGetKeyName_impl == NULL) fail(\"Failed to load glfw function glfwGetKeyName with error: %s\", dlerror());\n\n    *(void **) (&glfwGetNativeKeyForKey_impl) = dlsym(handle, \"glfwGetNativeKeyForKey\");\n    if (glfwGetNativeKeyForKey_impl == NULL) fail(\"Failed to load glfw function glfwGetNativeKeyForKey with error: %s\", dlerror());\n\n    *(void **) (&glfwGetKey_impl) = dlsym(handle, \"glfwGetKey\");\n    if (glfwGetKey_impl == NULL) fail(\"Failed to load glfw function glfwGetKey with error: %s\", dlerror());\n\n    *(void **) (&glfwGetMouseButton_impl) = dlsym(handle, \"glfwGetMouseButton\");\n    if (glfwGetMouseButton_impl == NULL) fail(\"Failed to load glfw function glfwGetMouseButton with error: %s\", dlerror());\n\n    *(void **) (&glfwGetCursorPos_impl) = dlsym(handle, \"glfwGetCursorPos\");\n    if (glfwGetCursorPos_impl == NULL) fail(\"Failed to load glfw function glfwGetCursorPos with error: %s\", dlerror());\n\n    *(void **) (&glfwSetCursorPos_impl) = dlsym(handle, \"glfwSetCursorPos\");\n    if (glfwSetCursorPos_impl == NULL) fail(\"Failed to load glfw function glfwSetCursorPos with error: %s\", dlerror());\n\n    *(void **) (&glfwCreateCursor_impl) = dlsym(handle, \"glfwCreateCursor\");\n    if (glfwCreateCursor_impl == NULL) fail(\"Failed to load glfw function glfwCreateCursor with error: %s\", dlerror());\n\n    *(void **) (&glfwCreateStandardCursor_impl) = dlsym(handle, \"glfwCreateStandardCursor\");\n    if (glfwCreateStandardCursor_impl == NULL) fail(\"Failed to load glfw function glfwCreateStandardCursor with error: %s\", dlerror());\n\n    *(void **) (&glfwDestroyCursor_impl) = dlsym(handle, \"glfwDestroyCursor\");\n    if (glfwDestroyCursor_impl == NULL) fail(\"Failed to load glfw function glfwDestroyCursor with error: %s\", dlerror());\n\n    *(void **) (&glfwSetCursor_impl) = dlsym(handle, \"glfwSetCursor\");\n    if (glfwSetCursor_impl == NULL) fail(\"Failed to load glfw function glfwSetCursor with error: %s\", dlerror());\n\n    *(void **) (&glfwSetKeyboardCallback_impl) = dlsym(handle, \"glfwSetKeyboardCallback\");\n    if (glfwSetKeyboardCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetKeyboardCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwUpdateIMEState_impl) = dlsym(handle, \"glfwUpdateIMEState\");\n    if (glfwUpdateIMEState_impl == NULL) fail(\"Failed to load glfw function glfwUpdateIMEState with error: %s\", dlerror());\n\n    *(void **) (&glfwSetMouseButtonCallback_impl) = dlsym(handle, \"glfwSetMouseButtonCallback\");\n    if (glfwSetMouseButtonCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetMouseButtonCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetCursorPosCallback_impl) = dlsym(handle, \"glfwSetCursorPosCallback\");\n    if (glfwSetCursorPosCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetCursorPosCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetCursorEnterCallback_impl) = dlsym(handle, \"glfwSetCursorEnterCallback\");\n    if (glfwSetCursorEnterCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetCursorEnterCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetScrollCallback_impl) = dlsym(handle, \"glfwSetScrollCallback\");\n    if (glfwSetScrollCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetScrollCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetLiveResizeCallback_impl) = dlsym(handle, \"glfwSetLiveResizeCallback\");\n    if (glfwSetLiveResizeCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetLiveResizeCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwSetDropEventCallback_impl) = dlsym(handle, \"glfwSetDropEventCallback\");\n    if (glfwSetDropEventCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetDropEventCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwRequestDropData_impl) = dlsym(handle, \"glfwRequestDropData\");\n    if (glfwRequestDropData_impl == NULL) fail(\"Failed to load glfw function glfwRequestDropData with error: %s\", dlerror());\n\n    *(void **) (&glfwEndDrop_impl) = dlsym(handle, \"glfwEndDrop\");\n    if (glfwEndDrop_impl == NULL) fail(\"Failed to load glfw function glfwEndDrop with error: %s\", dlerror());\n\n    *(void **) (&glfwSetDragSourceCallback_impl) = dlsym(handle, \"glfwSetDragSourceCallback\");\n    if (glfwSetDragSourceCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetDragSourceCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwStartDrag_impl) = dlsym(handle, \"glfwStartDrag\");\n    if (glfwStartDrag_impl == NULL) fail(\"Failed to load glfw function glfwStartDrag with error: %s\", dlerror());\n\n    *(void **) (&glfwJoystickPresent_impl) = dlsym(handle, \"glfwJoystickPresent\");\n    if (glfwJoystickPresent_impl == NULL) fail(\"Failed to load glfw function glfwJoystickPresent with error: %s\", dlerror());\n\n    *(void **) (&glfwGetJoystickAxes_impl) = dlsym(handle, \"glfwGetJoystickAxes\");\n    if (glfwGetJoystickAxes_impl == NULL) fail(\"Failed to load glfw function glfwGetJoystickAxes with error: %s\", dlerror());\n\n    *(void **) (&glfwGetJoystickButtons_impl) = dlsym(handle, \"glfwGetJoystickButtons\");\n    if (glfwGetJoystickButtons_impl == NULL) fail(\"Failed to load glfw function glfwGetJoystickButtons with error: %s\", dlerror());\n\n    *(void **) (&glfwGetJoystickHats_impl) = dlsym(handle, \"glfwGetJoystickHats\");\n    if (glfwGetJoystickHats_impl == NULL) fail(\"Failed to load glfw function glfwGetJoystickHats with error: %s\", dlerror());\n\n    *(void **) (&glfwGetJoystickName_impl) = dlsym(handle, \"glfwGetJoystickName\");\n    if (glfwGetJoystickName_impl == NULL) fail(\"Failed to load glfw function glfwGetJoystickName with error: %s\", dlerror());\n\n    *(void **) (&glfwGetJoystickGUID_impl) = dlsym(handle, \"glfwGetJoystickGUID\");\n    if (glfwGetJoystickGUID_impl == NULL) fail(\"Failed to load glfw function glfwGetJoystickGUID with error: %s\", dlerror());\n\n    *(void **) (&glfwSetJoystickUserPointer_impl) = dlsym(handle, \"glfwSetJoystickUserPointer\");\n    if (glfwSetJoystickUserPointer_impl == NULL) fail(\"Failed to load glfw function glfwSetJoystickUserPointer with error: %s\", dlerror());\n\n    *(void **) (&glfwGetJoystickUserPointer_impl) = dlsym(handle, \"glfwGetJoystickUserPointer\");\n    if (glfwGetJoystickUserPointer_impl == NULL) fail(\"Failed to load glfw function glfwGetJoystickUserPointer with error: %s\", dlerror());\n\n    *(void **) (&glfwJoystickIsGamepad_impl) = dlsym(handle, \"glfwJoystickIsGamepad\");\n    if (glfwJoystickIsGamepad_impl == NULL) fail(\"Failed to load glfw function glfwJoystickIsGamepad with error: %s\", dlerror());\n\n    *(void **) (&glfwSetJoystickCallback_impl) = dlsym(handle, \"glfwSetJoystickCallback\");\n    if (glfwSetJoystickCallback_impl == NULL) fail(\"Failed to load glfw function glfwSetJoystickCallback with error: %s\", dlerror());\n\n    *(void **) (&glfwUpdateGamepadMappings_impl) = dlsym(handle, \"glfwUpdateGamepadMappings\");\n    if (glfwUpdateGamepadMappings_impl == NULL) fail(\"Failed to load glfw function glfwUpdateGamepadMappings with error: %s\", dlerror());\n\n    *(void **) (&glfwGetGamepadName_impl) = dlsym(handle, \"glfwGetGamepadName\");\n    if (glfwGetGamepadName_impl == NULL) fail(\"Failed to load glfw function glfwGetGamepadName with error: %s\", dlerror());\n\n    *(void **) (&glfwGetGamepadState_impl) = dlsym(handle, \"glfwGetGamepadState\");\n    if (glfwGetGamepadState_impl == NULL) fail(\"Failed to load glfw function glfwGetGamepadState with error: %s\", dlerror());\n\n    *(void **) (&glfwSetClipboardDataTypes_impl) = dlsym(handle, \"glfwSetClipboardDataTypes\");\n    if (glfwSetClipboardDataTypes_impl == NULL) fail(\"Failed to load glfw function glfwSetClipboardDataTypes with error: %s\", dlerror());\n\n    *(void **) (&glfwGetClipboard_impl) = dlsym(handle, \"glfwGetClipboard\");\n    if (glfwGetClipboard_impl == NULL) fail(\"Failed to load glfw function glfwGetClipboard with error: %s\", dlerror());\n\n    *(void **) (&glfwGetTime_impl) = dlsym(handle, \"glfwGetTime\");\n    if (glfwGetTime_impl == NULL) fail(\"Failed to load glfw function glfwGetTime with error: %s\", dlerror());\n\n    *(void **) (&glfwMakeContextCurrent_impl) = dlsym(handle, \"glfwMakeContextCurrent\");\n    if (glfwMakeContextCurrent_impl == NULL) fail(\"Failed to load glfw function glfwMakeContextCurrent with error: %s\", dlerror());\n\n    *(void **) (&glfwGetCurrentContext_impl) = dlsym(handle, \"glfwGetCurrentContext\");\n    if (glfwGetCurrentContext_impl == NULL) fail(\"Failed to load glfw function glfwGetCurrentContext with error: %s\", dlerror());\n\n    *(void **) (&glfwSwapBuffers_impl) = dlsym(handle, \"glfwSwapBuffers\");\n    if (glfwSwapBuffers_impl == NULL) fail(\"Failed to load glfw function glfwSwapBuffers with error: %s\", dlerror());\n\n    *(void **) (&glfwSwapInterval_impl) = dlsym(handle, \"glfwSwapInterval\");\n    if (glfwSwapInterval_impl == NULL) fail(\"Failed to load glfw function glfwSwapInterval with error: %s\", dlerror());\n\n    *(void **) (&glfwExtensionSupported_impl) = dlsym(handle, \"glfwExtensionSupported\");\n    if (glfwExtensionSupported_impl == NULL) fail(\"Failed to load glfw function glfwExtensionSupported with error: %s\", dlerror());\n\n    *(void **) (&glfwGetProcAddress_impl) = dlsym(handle, \"glfwGetProcAddress\");\n    if (glfwGetProcAddress_impl == NULL) fail(\"Failed to load glfw function glfwGetProcAddress with error: %s\", dlerror());\n\n    *(void **) (&glfwVulkanSupported_impl) = dlsym(handle, \"glfwVulkanSupported\");\n    if (glfwVulkanSupported_impl == NULL) fail(\"Failed to load glfw function glfwVulkanSupported with error: %s\", dlerror());\n\n    *(void **) (&glfwGetRequiredInstanceExtensions_impl) = dlsym(handle, \"glfwGetRequiredInstanceExtensions\");\n    if (glfwGetRequiredInstanceExtensions_impl == NULL) fail(\"Failed to load glfw function glfwGetRequiredInstanceExtensions with error: %s\", dlerror());\n\n    *(void **) (&glfwGetCocoaWindow_impl) = dlsym(handle, \"glfwGetCocoaWindow\");\n    if (glfwGetCocoaWindow_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwGetNSGLContext_impl) = dlsym(handle, \"glfwGetNSGLContext\");\n    if (glfwGetNSGLContext_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwGetCocoaMonitor_impl) = dlsym(handle, \"glfwGetCocoaMonitor\");\n    if (glfwGetCocoaMonitor_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwSetCocoaTextInputFilter_impl) = dlsym(handle, \"glfwSetCocoaTextInputFilter\");\n    if (glfwSetCocoaTextInputFilter_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwSetCocoaURLOpenCallback_impl) = dlsym(handle, \"glfwSetCocoaURLOpenCallback\");\n    if (glfwSetCocoaURLOpenCallback_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwSetCocoaToggleFullscreenIntercept_impl) = dlsym(handle, \"glfwSetCocoaToggleFullscreenIntercept\");\n    if (glfwSetCocoaToggleFullscreenIntercept_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwSetApplicationShouldHandleReopen_impl) = dlsym(handle, \"glfwSetApplicationShouldHandleReopen\");\n    if (glfwSetApplicationShouldHandleReopen_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwSetApplicationWillFinishLaunching_impl) = dlsym(handle, \"glfwSetApplicationWillFinishLaunching\");\n    if (glfwSetApplicationWillFinishLaunching_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwGetCocoaKeyEquivalent_impl) = dlsym(handle, \"glfwGetCocoaKeyEquivalent\");\n    if (glfwGetCocoaKeyEquivalent_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwCocoaRequestRenderFrame_impl) = dlsym(handle, \"glfwCocoaRequestRenderFrame\");\n    if (glfwCocoaRequestRenderFrame_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwCocoaRecreateGLDrawable_impl) = dlsym(handle, \"glfwCocoaRecreateGLDrawable\");\n    if (glfwCocoaRecreateGLDrawable_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwCocoaSetWindowResizeCallback_impl) = dlsym(handle, \"glfwCocoaSetWindowResizeCallback\");\n    if (glfwCocoaSetWindowResizeCallback_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwGetX11Display_impl) = dlsym(handle, \"glfwGetX11Display\");\n    if (glfwGetX11Display_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwGetX11Window_impl) = dlsym(handle, \"glfwGetX11Window\");\n    if (glfwGetX11Window_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwSetPrimarySelectionString_impl) = dlsym(handle, \"glfwSetPrimarySelectionString\");\n    if (glfwSetPrimarySelectionString_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwCocoaCycleThroughOSWindows_impl) = dlsym(handle, \"glfwCocoaCycleThroughOSWindows\");\n    if (glfwCocoaCycleThroughOSWindows_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwCocoaSetWindowChrome_impl) = dlsym(handle, \"glfwCocoaSetWindowChrome\");\n    if (glfwCocoaSetWindowChrome_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwCocoaRegisterMIMETypes_impl) = dlsym(handle, \"glfwCocoaRegisterMIMETypes\");\n    if (glfwCocoaRegisterMIMETypes_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwGetPrimarySelectionString_impl) = dlsym(handle, \"glfwGetPrimarySelectionString\");\n    if (glfwGetPrimarySelectionString_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwGetNativeKeyForName_impl) = dlsym(handle, \"glfwGetNativeKeyForName\");\n    if (glfwGetNativeKeyForName_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwRequestWaylandFrameEvent_impl) = dlsym(handle, \"glfwRequestWaylandFrameEvent\");\n    if (glfwRequestWaylandFrameEvent_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandActivateWindow_impl) = dlsym(handle, \"glfwWaylandActivateWindow\");\n    if (glfwWaylandActivateWindow_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandMissingCapabilities_impl) = dlsym(handle, \"glfwWaylandMissingCapabilities\");\n    if (glfwWaylandMissingCapabilities_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandRunWithActivationToken_impl) = dlsym(handle, \"glfwWaylandRunWithActivationToken\");\n    if (glfwWaylandRunWithActivationToken_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandSetTitlebarColor_impl) = dlsym(handle, \"glfwWaylandSetTitlebarColor\");\n    if (glfwWaylandSetTitlebarColor_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandSetTitlebarHidden_impl) = dlsym(handle, \"glfwWaylandSetTitlebarHidden\");\n    if (glfwWaylandSetTitlebarHidden_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandRedrawCSDWindowTitle_impl) = dlsym(handle, \"glfwWaylandRedrawCSDWindowTitle\");\n    if (glfwWaylandRedrawCSDWindowTitle_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandIsWindowFullyCreated_impl) = dlsym(handle, \"glfwWaylandIsWindowFullyCreated\");\n    if (glfwWaylandIsWindowFullyCreated_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandBeep_impl) = dlsym(handle, \"glfwWaylandBeep\");\n    if (glfwWaylandBeep_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwWaylandCompositorPID_impl) = dlsym(handle, \"glfwWaylandCompositorPID\");\n    if (glfwWaylandCompositorPID_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwConfigureMomentumScroller_impl) = dlsym(handle, \"glfwConfigureMomentumScroller\");\n    if (glfwConfigureMomentumScroller_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwDBusUserNotify_impl) = dlsym(handle, \"glfwDBusUserNotify\");\n    if (glfwDBusUserNotify_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwDBusSetUserNotificationHandler_impl) = dlsym(handle, \"glfwDBusSetUserNotificationHandler\");\n    if (glfwDBusSetUserNotificationHandler_impl == NULL) dlerror(); // clear error indicator\n\n    *(void **) (&glfwSetX11LaunchCommand_impl) = dlsym(handle, \"glfwSetX11LaunchCommand\");\n    if (glfwSetX11LaunchCommand_impl == NULL) dlerror(); // clear error indicator\n\n    return NULL;\n}\n\nvoid\nunload_glfw(void) {\n    if (handle) { dlclose(handle); handle = NULL; }\n}\n"
  },
  {
    "path": "kitty/glfw-wrapper.h",
    "content": "//\n// THIS FILE IS GENERATED BY glfw.py\n//\n// SAVE YOURSELF SOME TIME, DO NOT MANUALLY EDIT\n//\n\n#pragma once\n#include <stddef.h>\n#include <stdint.h>\n#include \"monotonic.h\"\n\n#ifndef GFW_EXTERN\n#define GFW_EXTERN extern\n#endif\n\n\n/*! @name GLFW version macros\n *  @{ */\n/*! @brief The major version number of the GLFW library.\n *\n *  This is incremented when the API is changed in non-compatible ways.\n *  @ingroup init\n */\n#define GLFW_VERSION_MAJOR          3\n/*! @brief The minor version number of the GLFW library.\n *\n *  This is incremented when features are added to the API but it remains\n *  backward-compatible.\n *  @ingroup init\n */\n#define GLFW_VERSION_MINOR          4\n/*! @brief The revision number of the GLFW library.\n *\n *  This is incremented when a bug fix release is made that does not contain any\n *  API changes.\n *  @ingroup init\n */\n#define GLFW_VERSION_REVISION       0\n/*! @} */\n\n/*! @defgroup hat_state Joystick hat states\n *  @brief Joystick hat states.\n *\n *  See [joystick hat input](@ref joystick_hat) for how these are used.\n *\n *  @ingroup input\n *  @{ */\n#define GLFW_HAT_CENTERED           0\n#define GLFW_HAT_UP                 1\n#define GLFW_HAT_RIGHT              2\n#define GLFW_HAT_DOWN               4\n#define GLFW_HAT_LEFT               8\n#define GLFW_HAT_RIGHT_UP           (GLFW_HAT_RIGHT | GLFW_HAT_UP)\n#define GLFW_HAT_RIGHT_DOWN         (GLFW_HAT_RIGHT | GLFW_HAT_DOWN)\n#define GLFW_HAT_LEFT_UP            (GLFW_HAT_LEFT  | GLFW_HAT_UP)\n#define GLFW_HAT_LEFT_DOWN          (GLFW_HAT_LEFT  | GLFW_HAT_DOWN)\n/*! @} */\n\n/*! @defgroup keys Keyboard keys\n *  @brief Keyboard key IDs.\n *\n *  See [key input](@ref input_key) for how these are used.\n *\n *  These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60),\n *  but re-arranged to map to 7-bit ASCII for printable keys (function keys are\n *  put in the 256+ range).\n *\n *  The naming of the key codes follow these rules:\n *   - The US keyboard layout is used\n *   - Names of printable alphanumeric characters are used (e.g. \"A\", \"R\",\n *     \"3\", etc.)\n *   - For non-alphanumeric characters, Unicode:ish names are used (e.g.\n *     \"COMMA\", \"LEFT_SQUARE_BRACKET\", etc.). Note that some names do not\n *     correspond to the Unicode standard (usually for brevity)\n *   - Keys that lack a clear US mapping are named \"WORLD_x\"\n *   - For non-printable keys, custom names are used (e.g. \"F4\",\n *     \"BACKSPACE\", etc.)\n *\n *  @ingroup input\n *  @{\n */\n\n/* start functional key names (auto generated by gen-key-constants.py do not edit) */\ntypedef enum {\n  GLFW_FKEY_FIRST = 0xe000u,\n  GLFW_FKEY_ESCAPE = 0xe000u,\n  GLFW_FKEY_ENTER = 0xe001u,\n  GLFW_FKEY_TAB = 0xe002u,\n  GLFW_FKEY_BACKSPACE = 0xe003u,\n  GLFW_FKEY_INSERT = 0xe004u,\n  GLFW_FKEY_DELETE = 0xe005u,\n  GLFW_FKEY_LEFT = 0xe006u,\n  GLFW_FKEY_RIGHT = 0xe007u,\n  GLFW_FKEY_UP = 0xe008u,\n  GLFW_FKEY_DOWN = 0xe009u,\n  GLFW_FKEY_PAGE_UP = 0xe00au,\n  GLFW_FKEY_PAGE_DOWN = 0xe00bu,\n  GLFW_FKEY_HOME = 0xe00cu,\n  GLFW_FKEY_END = 0xe00du,\n  GLFW_FKEY_CAPS_LOCK = 0xe00eu,\n  GLFW_FKEY_SCROLL_LOCK = 0xe00fu,\n  GLFW_FKEY_NUM_LOCK = 0xe010u,\n  GLFW_FKEY_PRINT_SCREEN = 0xe011u,\n  GLFW_FKEY_PAUSE = 0xe012u,\n  GLFW_FKEY_MENU = 0xe013u,\n  GLFW_FKEY_F1 = 0xe014u,\n  GLFW_FKEY_F2 = 0xe015u,\n  GLFW_FKEY_F3 = 0xe016u,\n  GLFW_FKEY_F4 = 0xe017u,\n  GLFW_FKEY_F5 = 0xe018u,\n  GLFW_FKEY_F6 = 0xe019u,\n  GLFW_FKEY_F7 = 0xe01au,\n  GLFW_FKEY_F8 = 0xe01bu,\n  GLFW_FKEY_F9 = 0xe01cu,\n  GLFW_FKEY_F10 = 0xe01du,\n  GLFW_FKEY_F11 = 0xe01eu,\n  GLFW_FKEY_F12 = 0xe01fu,\n  GLFW_FKEY_F13 = 0xe020u,\n  GLFW_FKEY_F14 = 0xe021u,\n  GLFW_FKEY_F15 = 0xe022u,\n  GLFW_FKEY_F16 = 0xe023u,\n  GLFW_FKEY_F17 = 0xe024u,\n  GLFW_FKEY_F18 = 0xe025u,\n  GLFW_FKEY_F19 = 0xe026u,\n  GLFW_FKEY_F20 = 0xe027u,\n  GLFW_FKEY_F21 = 0xe028u,\n  GLFW_FKEY_F22 = 0xe029u,\n  GLFW_FKEY_F23 = 0xe02au,\n  GLFW_FKEY_F24 = 0xe02bu,\n  GLFW_FKEY_F25 = 0xe02cu,\n  GLFW_FKEY_F26 = 0xe02du,\n  GLFW_FKEY_F27 = 0xe02eu,\n  GLFW_FKEY_F28 = 0xe02fu,\n  GLFW_FKEY_F29 = 0xe030u,\n  GLFW_FKEY_F30 = 0xe031u,\n  GLFW_FKEY_F31 = 0xe032u,\n  GLFW_FKEY_F32 = 0xe033u,\n  GLFW_FKEY_F33 = 0xe034u,\n  GLFW_FKEY_F34 = 0xe035u,\n  GLFW_FKEY_F35 = 0xe036u,\n  GLFW_FKEY_KP_0 = 0xe037u,\n  GLFW_FKEY_KP_1 = 0xe038u,\n  GLFW_FKEY_KP_2 = 0xe039u,\n  GLFW_FKEY_KP_3 = 0xe03au,\n  GLFW_FKEY_KP_4 = 0xe03bu,\n  GLFW_FKEY_KP_5 = 0xe03cu,\n  GLFW_FKEY_KP_6 = 0xe03du,\n  GLFW_FKEY_KP_7 = 0xe03eu,\n  GLFW_FKEY_KP_8 = 0xe03fu,\n  GLFW_FKEY_KP_9 = 0xe040u,\n  GLFW_FKEY_KP_DECIMAL = 0xe041u,\n  GLFW_FKEY_KP_DIVIDE = 0xe042u,\n  GLFW_FKEY_KP_MULTIPLY = 0xe043u,\n  GLFW_FKEY_KP_SUBTRACT = 0xe044u,\n  GLFW_FKEY_KP_ADD = 0xe045u,\n  GLFW_FKEY_KP_ENTER = 0xe046u,\n  GLFW_FKEY_KP_EQUAL = 0xe047u,\n  GLFW_FKEY_KP_SEPARATOR = 0xe048u,\n  GLFW_FKEY_KP_LEFT = 0xe049u,\n  GLFW_FKEY_KP_RIGHT = 0xe04au,\n  GLFW_FKEY_KP_UP = 0xe04bu,\n  GLFW_FKEY_KP_DOWN = 0xe04cu,\n  GLFW_FKEY_KP_PAGE_UP = 0xe04du,\n  GLFW_FKEY_KP_PAGE_DOWN = 0xe04eu,\n  GLFW_FKEY_KP_HOME = 0xe04fu,\n  GLFW_FKEY_KP_END = 0xe050u,\n  GLFW_FKEY_KP_INSERT = 0xe051u,\n  GLFW_FKEY_KP_DELETE = 0xe052u,\n  GLFW_FKEY_KP_BEGIN = 0xe053u,\n  GLFW_FKEY_MEDIA_PLAY = 0xe054u,\n  GLFW_FKEY_MEDIA_PAUSE = 0xe055u,\n  GLFW_FKEY_MEDIA_PLAY_PAUSE = 0xe056u,\n  GLFW_FKEY_MEDIA_REVERSE = 0xe057u,\n  GLFW_FKEY_MEDIA_STOP = 0xe058u,\n  GLFW_FKEY_MEDIA_FAST_FORWARD = 0xe059u,\n  GLFW_FKEY_MEDIA_REWIND = 0xe05au,\n  GLFW_FKEY_MEDIA_TRACK_NEXT = 0xe05bu,\n  GLFW_FKEY_MEDIA_TRACK_PREVIOUS = 0xe05cu,\n  GLFW_FKEY_MEDIA_RECORD = 0xe05du,\n  GLFW_FKEY_LOWER_VOLUME = 0xe05eu,\n  GLFW_FKEY_RAISE_VOLUME = 0xe05fu,\n  GLFW_FKEY_MUTE_VOLUME = 0xe060u,\n  GLFW_FKEY_LEFT_SHIFT = 0xe061u,\n  GLFW_FKEY_LEFT_CONTROL = 0xe062u,\n  GLFW_FKEY_LEFT_ALT = 0xe063u,\n  GLFW_FKEY_LEFT_SUPER = 0xe064u,\n  GLFW_FKEY_LEFT_HYPER = 0xe065u,\n  GLFW_FKEY_LEFT_META = 0xe066u,\n  GLFW_FKEY_RIGHT_SHIFT = 0xe067u,\n  GLFW_FKEY_RIGHT_CONTROL = 0xe068u,\n  GLFW_FKEY_RIGHT_ALT = 0xe069u,\n  GLFW_FKEY_RIGHT_SUPER = 0xe06au,\n  GLFW_FKEY_RIGHT_HYPER = 0xe06bu,\n  GLFW_FKEY_RIGHT_META = 0xe06cu,\n  GLFW_FKEY_ISO_LEVEL3_SHIFT = 0xe06du,\n  GLFW_FKEY_ISO_LEVEL5_SHIFT = 0xe06eu,\n  GLFW_FKEY_LAST = 0xe06eu\n} GLFWFunctionKey;\n/* end functional key names */\n\n/*! @} */\n\n/*! @defgroup mods Modifier key flags\n *  @brief Modifier key flags.\n *\n *  See [key input](@ref input_key) for how these are used.\n *\n *  @ingroup input\n *  @{ */\n\n/*! @brief If this bit is set one or more Shift keys were held down.\n *\n *  If this bit is set one or more Shift keys were held down.\n */\n#define GLFW_MOD_SHIFT           0x0001\n/*! @brief If this bit is set one or more Alt keys were held down.\n *\n *  If this bit is set one or more Alt keys were held down.\n */\n#define GLFW_MOD_ALT             0x0002\n/*! @brief If this bit is set one or more Alt keys were held down.\n *\n *  If this bit is set one or more Alt keys were held down.\n */\n#define GLFW_MOD_CONTROL         0x0004\n/*! @brief If this bit is set one or more Super keys were held down.\n *\n *  If this bit is set one or more Super keys were held down.\n */\n#define GLFW_MOD_SUPER           0x0008\n/*! @brief If this bit is set one or more Hyper keys were held down.\n *\n *  If this bit is set one or more Hyper keys were held down.\n */\n#define GLFW_MOD_HYPER           0x0010\n/*! @brief If this bit is set one or more Meta keys were held down.\n *\n *  If this bit is set one or more Meta keys were held down.\n */\n#define GLFW_MOD_META            0x0020\n/*! @brief If this bit is set the Caps Lock key is enabled.\n *\n *  If this bit is set the Caps Lock key is enabled and the @ref\n *  GLFW_LOCK_KEY_MODS input mode is set.\n */\n#define GLFW_MOD_CAPS_LOCK       0x0040\n/*! @brief If this bit is set the Num Lock key is enabled.\n *\n *  If this bit is set the Num Lock key is enabled and the @ref\n *  GLFW_LOCK_KEY_MODS input mode is set.\n */\n#define GLFW_MOD_NUM_LOCK        0x0080\n#define GLFW_MOD_LAST            GLFW_MOD_NUM_LOCK\n#define GLFW_LOCK_MASK           (GLFW_MOD_NUM_LOCK | GLFW_MOD_CAPS_LOCK)\n\n/*! @} */\n\n/*! @defgroup buttons Mouse buttons\n *  @brief Mouse button IDs.\n *\n *  See [mouse button input](@ref input_mouse_button) for how these are used.\n *\n *  @ingroup input\n *  @{ */\ntypedef enum GLFWMouseButton {\n    GLFW_MOUSE_BUTTON_1 = 0,\n    GLFW_MOUSE_BUTTON_LEFT = 0,\n    GLFW_MOUSE_BUTTON_2 = 1,\n    GLFW_MOUSE_BUTTON_RIGHT = 1,\n    GLFW_MOUSE_BUTTON_3 = 2,\n    GLFW_MOUSE_BUTTON_MIDDLE = 2,\n    GLFW_MOUSE_BUTTON_4 = 3,\n    GLFW_MOUSE_BUTTON_5 = 4,\n    GLFW_MOUSE_BUTTON_6 = 5,\n    GLFW_MOUSE_BUTTON_7 = 6,\n    GLFW_MOUSE_BUTTON_8 = 7,\n    GLFW_MOUSE_BUTTON_LAST = 7\n} GLFWMouseButton;\n/*! @} */\n\ntypedef enum GLFWColorScheme {\n    GLFW_COLOR_SCHEME_NO_PREFERENCE = 0,\n    GLFW_COLOR_SCHEME_DARK = 1,\n    GLFW_COLOR_SCHEME_LIGHT = 2\n} GLFWColorScheme;\n\ntypedef enum GLFWMomentumType {\n    GLFW_NO_MOMENTUM_DATA = 0,\n    GLFW_MOMENTUM_PHASE_BEGAN = 1,\n    GLFW_MOMENTUM_PHASE_STATIONARY = 2,\n    GLFW_MOMENTUM_PHASE_ACTIVE = 3,\n    GLFW_MOMENTUM_PHASE_ENDED = 4,\n    GLFW_MOMENTUM_PHASE_CANCELED = 5,\n    GLFW_MOMENTUM_PHASE_MAY_BEGIN = 6,\n} GLFWMomentumType;\n\ntypedef enum GLFWOffsetType {\n    GLFW_SCROLL_OFFSET_LINES = 0,\n    GLFW_SCROLL_OFFEST_V120 = 1,\n    GLFW_SCROLL_OFFEST_HIGHRES = 2,\n} GLFWOffsetType;\n\ntypedef struct GLFWScrollEvent {\n    double x_offset, y_offset;  // offsets are scaled by the window scale for HIGHRES\n    struct { double x, y; } unscaled;  // unscaled offsets, aka logical pixels\n    GLFWMomentumType momentum_type;\n    GLFWOffsetType offset_type;\n    int keyboard_modifiers;\n} GLFWScrollEvent;\n\n/*! @defgroup joysticks Joysticks\n *  @brief Joystick IDs.\n *\n *  See [joystick input](@ref joystick) for how these are used.\n *\n *  @ingroup input\n *  @{ */\n#define GLFW_JOYSTICK_1             0\n#define GLFW_JOYSTICK_2             1\n#define GLFW_JOYSTICK_3             2\n#define GLFW_JOYSTICK_4             3\n#define GLFW_JOYSTICK_5             4\n#define GLFW_JOYSTICK_6             5\n#define GLFW_JOYSTICK_7             6\n#define GLFW_JOYSTICK_8             7\n#define GLFW_JOYSTICK_9             8\n#define GLFW_JOYSTICK_10            9\n#define GLFW_JOYSTICK_11            10\n#define GLFW_JOYSTICK_12            11\n#define GLFW_JOYSTICK_13            12\n#define GLFW_JOYSTICK_14            13\n#define GLFW_JOYSTICK_15            14\n#define GLFW_JOYSTICK_16            15\n#define GLFW_JOYSTICK_LAST          GLFW_JOYSTICK_16\n/*! @} */\n\n/*! @defgroup gamepad_buttons Gamepad buttons\n *  @brief Gamepad buttons.\n *\n *  See @ref gamepad for how these are used.\n *\n *  @ingroup input\n *  @{ */\n#define GLFW_GAMEPAD_BUTTON_A               0\n#define GLFW_GAMEPAD_BUTTON_B               1\n#define GLFW_GAMEPAD_BUTTON_X               2\n#define GLFW_GAMEPAD_BUTTON_Y               3\n#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER     4\n#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER    5\n#define GLFW_GAMEPAD_BUTTON_BACK            6\n#define GLFW_GAMEPAD_BUTTON_START           7\n#define GLFW_GAMEPAD_BUTTON_GUIDE           8\n#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB      9\n#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB     10\n#define GLFW_GAMEPAD_BUTTON_DPAD_UP         11\n#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT      12\n#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN       13\n#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT       14\n#define GLFW_GAMEPAD_BUTTON_LAST            GLFW_GAMEPAD_BUTTON_DPAD_LEFT\n\n#define GLFW_GAMEPAD_BUTTON_CROSS       GLFW_GAMEPAD_BUTTON_A\n#define GLFW_GAMEPAD_BUTTON_CIRCLE      GLFW_GAMEPAD_BUTTON_B\n#define GLFW_GAMEPAD_BUTTON_SQUARE      GLFW_GAMEPAD_BUTTON_X\n#define GLFW_GAMEPAD_BUTTON_TRIANGLE    GLFW_GAMEPAD_BUTTON_Y\n/*! @} */\n\n/*! @defgroup gamepad_axes Gamepad axes\n *  @brief Gamepad axes.\n *\n *  See @ref gamepad for how these are used.\n *\n *  @ingroup input\n *  @{ */\n#define GLFW_GAMEPAD_AXIS_LEFT_X        0\n#define GLFW_GAMEPAD_AXIS_LEFT_Y        1\n#define GLFW_GAMEPAD_AXIS_RIGHT_X       2\n#define GLFW_GAMEPAD_AXIS_RIGHT_Y       3\n#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER  4\n#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5\n#define GLFW_GAMEPAD_AXIS_LAST          GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER\n/*! @} */\n\n/*! @defgroup errors Error codes\n *  @brief Error codes.\n *\n *  See [error handling](@ref error_handling) for how these are used.\n *\n *  @ingroup init\n *  @{ */\n/*! @brief No error has occurred.\n *\n *  No error has occurred.\n *\n *  @analysis Yay.\n */\n#define GLFW_NO_ERROR               0\n/*! @brief GLFW has not been initialized.\n *\n *  This occurs if a GLFW function was called that must not be called unless the\n *  library is [initialized](@ref intro_init).\n *\n *  @analysis Application programmer error.  Initialize GLFW before calling any\n *  function that requires initialization.\n */\n#define GLFW_NOT_INITIALIZED        0x00010001\n/*! @brief No context is current for this thread.\n *\n *  This occurs if a GLFW function was called that needs and operates on the\n *  current OpenGL or OpenGL ES context but no context is current on the calling\n *  thread.  One such function is @ref glfwSwapInterval.\n *\n *  @analysis Application programmer error.  Ensure a context is current before\n *  calling functions that require a current context.\n */\n#define GLFW_NO_CURRENT_CONTEXT     0x00010002\n/*! @brief One of the arguments to the function was an invalid enum value.\n *\n *  One of the arguments to the function was an invalid enum value, for example\n *  requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib.\n *\n *  @analysis Application programmer error.  Fix the offending call.\n */\n#define GLFW_INVALID_ENUM           0x00010003\n/*! @brief One of the arguments to the function was an invalid value.\n *\n *  One of the arguments to the function was an invalid value, for example\n *  requesting a non-existent OpenGL or OpenGL ES version like 2.7.\n *\n *  Requesting a valid but unavailable OpenGL or OpenGL ES version will instead\n *  result in a @ref GLFW_VERSION_UNAVAILABLE error.\n *\n *  @analysis Application programmer error.  Fix the offending call.\n */\n#define GLFW_INVALID_VALUE          0x00010004\n/*! @brief A memory allocation failed.\n *\n *  A memory allocation failed.\n *\n *  @analysis A bug in GLFW or the underlying operating system.  Report the bug\n *  to our [issue tracker](https://github.com/glfw/glfw/issues).\n */\n#define GLFW_OUT_OF_MEMORY          0x00010005\n/*! @brief GLFW could not find support for the requested API on the system.\n *\n *  GLFW could not find support for the requested API on the system.\n *\n *  @analysis The installed graphics driver does not support the requested\n *  API, or does not support it via the chosen context creation backend.\n *  Below are a few examples.\n *\n *  @par\n *  Some pre-installed Windows graphics drivers do not support OpenGL.  AMD only\n *  supports OpenGL ES via EGL, while Nvidia and Intel only support it via\n *  a WGL or GLX extension.  macOS does not provide OpenGL ES at all.  The Mesa\n *  EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary\n *  driver.  Older graphics drivers do not support Vulkan.\n */\n#define GLFW_API_UNAVAILABLE        0x00010006\n/*! @brief The requested OpenGL or OpenGL ES version is not available.\n *\n *  The requested OpenGL or OpenGL ES version (including any requested context\n *  or framebuffer hints) is not available on this machine.\n *\n *  @analysis The machine does not support your requirements.  If your\n *  application is sufficiently flexible, downgrade your requirements and try\n *  again.  Otherwise, inform the user that their machine does not match your\n *  requirements.\n *\n *  @par\n *  Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0\n *  comes out before the 4.x series gets that far, also fail with this error and\n *  not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions\n *  will exist.\n */\n#define GLFW_VERSION_UNAVAILABLE    0x00010007\n/*! @brief A platform-specific error occurred that does not match any of the\n *  more specific categories.\n *\n *  A platform-specific error occurred that does not match any of the more\n *  specific categories.\n *\n *  @analysis A bug or configuration error in GLFW, the underlying operating\n *  system or its drivers, or a lack of required resources.  Report the issue to\n *  our [issue tracker](https://github.com/glfw/glfw/issues).\n */\n#define GLFW_PLATFORM_ERROR         0x00010008\n/*! @brief The requested format is not supported or available.\n *\n *  If emitted during window creation, the requested pixel format is not\n *  supported.\n *\n *  If emitted when querying the clipboard, the contents of the clipboard could\n *  not be converted to the requested format.\n *\n *  @analysis If emitted during window creation, one or more\n *  [hard constraints](@ref window_hints_hard) did not match any of the\n *  available pixel formats.  If your application is sufficiently flexible,\n *  downgrade your requirements and try again.  Otherwise, inform the user that\n *  their machine does not match your requirements.\n *\n *  @par\n *  If emitted when querying the clipboard, ignore the error or report it to\n *  the user, as appropriate.\n */\n#define GLFW_FORMAT_UNAVAILABLE     0x00010009\n/*! @brief The specified window does not have an OpenGL or OpenGL ES context.\n *\n *  A window that does not have an OpenGL or OpenGL ES context was passed to\n *  a function that requires it to have one.\n *\n *  @analysis Application programmer error.  Fix the offending call.\n */\n#define GLFW_NO_WINDOW_CONTEXT      0x0001000A\n/*! @brief The requested feature is not provided by the platform.\n *\n *  The requested feature is not provided by the platform, so GLFW is unable to\n *  implement it.  The documentation for each function notes if it could emit\n *  this error.\n *\n *  @analysis Platform or platform version limitation.  The error can be ignored\n *  unless the feature is critical to the application.\n *\n *  @par\n *  A function call that emits this error has no effect other than the error and\n *  updating any existing out parameters.\n */\n#define GLFW_FEATURE_UNAVAILABLE    0x0001000C\n/*! @brief The requested feature is not implemented for the platform.\n *\n *  The requested feature has not yet been implemented in GLFW for this platform.\n *\n *  @analysis An incomplete implementation of GLFW for this platform, hopefully\n *  fixed in a future release.  The error can be ignored unless the feature is\n *  critical to the application.\n *\n *  @par\n *  A function call that emits this error has no effect other than the error and\n *  updating any existing out parameters.\n */\n#define GLFW_FEATURE_UNIMPLEMENTED  0x0001000D\n/*! @} */\n\n/*! @addtogroup window\n *  @{ */\n/*! @brief Input focus window hint and attribute\n *\n *  Input focus [window hint](@ref GLFW_FOCUSED_hint) or\n *  [window attribute](@ref GLFW_FOCUSED_attrib).\n */\n#define GLFW_FOCUSED                0x00020001\n/*! @brief Window iconification window attribute\n *\n *  Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib).\n */\n#define GLFW_ICONIFIED              0x00020002\n/*! @brief Window resize-ability window hint and attribute\n *\n *  Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and\n *  [window attribute](@ref GLFW_RESIZABLE_attrib).\n */\n#define GLFW_RESIZABLE              0x00020003\n/*! @brief Window visibility window hint and attribute\n *\n *  Window visibility [window hint](@ref GLFW_VISIBLE_hint) and\n *  [window attribute](@ref GLFW_VISIBLE_attrib).\n */\n#define GLFW_VISIBLE                0x00020004\n/*! @brief Window decoration window hint and attribute\n *\n *  Window decoration [window hint](@ref GLFW_DECORATED_hint) and\n *  [window attribute](@ref GLFW_DECORATED_attrib).\n */\n#define GLFW_DECORATED              0x00020005\n/*! @brief Window auto-iconification window hint and attribute\n *\n *  Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and\n *  [window attribute](@ref GLFW_AUTO_ICONIFY_attrib).\n */\n#define GLFW_AUTO_ICONIFY           0x00020006\n/*! @brief Window decoration window hint and attribute\n *\n *  Window decoration [window hint](@ref GLFW_FLOATING_hint) and\n *  [window attribute](@ref GLFW_FLOATING_attrib).\n */\n#define GLFW_FLOATING               0x00020007\n/*! @brief Window maximization window hint and attribute\n *\n *  Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and\n *  [window attribute](@ref GLFW_MAXIMIZED_attrib).\n */\n#define GLFW_MAXIMIZED              0x00020008\n/*! @brief Cursor centering window hint\n *\n *  Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint).\n */\n#define GLFW_CENTER_CURSOR          0x00020009\n/*! @brief Window framebuffer transparency hint and attribute\n *\n *  Window framebuffer transparency\n *  [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and\n *  [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib).\n */\n#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A\n/*! @brief Mouse cursor hover window attribute.\n *\n *  Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib).\n */\n#define GLFW_HOVERED                0x0002000B\n/*! @brief Input focus on calling show window hint and attribute\n *\n *  Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or\n *  [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib).\n */\n#define GLFW_FOCUS_ON_SHOW          0x0002000C\n\n/*! @brief Mouse input transparency window hint and attribute\n *\n *  Mouse input transparency [window hint](@ref GLFW_MOUSE_PASSTHROUGH_hint) or\n *  [window attribute](@ref GLFW_MOUSE_PASSTHROUGH_attrib).\n */\n#define GLFW_MOUSE_PASSTHROUGH      0x0002000D\n\n/*! @brief Occlusion window attribute\n *\n *  Occlusion [window attribute](@ref GLFW_OCCLUDED_attrib).\n */\n#define GLFW_OCCLUDED               0x0002000E\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_RED_BITS).\n */\n#define GLFW_RED_BITS               0x00021001\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS).\n */\n#define GLFW_GREEN_BITS             0x00021002\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS).\n */\n#define GLFW_BLUE_BITS              0x00021003\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS).\n */\n#define GLFW_ALPHA_BITS             0x00021004\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS).\n */\n#define GLFW_DEPTH_BITS             0x00021005\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS).\n */\n#define GLFW_STENCIL_BITS           0x00021006\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS).\n */\n#define GLFW_ACCUM_RED_BITS         0x00021007\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS).\n */\n#define GLFW_ACCUM_GREEN_BITS       0x00021008\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS).\n */\n#define GLFW_ACCUM_BLUE_BITS        0x00021009\n/*! @brief Framebuffer bit depth hint.\n *\n *  Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS).\n */\n#define GLFW_ACCUM_ALPHA_BITS       0x0002100A\n/*! @brief Framebuffer auxiliary buffer hint.\n *\n *  Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS).\n */\n#define GLFW_AUX_BUFFERS            0x0002100B\n/*! @brief OpenGL stereoscopic rendering hint.\n *\n *  OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO).\n */\n#define GLFW_STEREO                 0x0002100C\n/*! @brief Framebuffer MSAA samples hint.\n *\n *  Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES).\n */\n#define GLFW_SAMPLES                0x0002100D\n/*! @brief Framebuffer sRGB hint.\n *\n *  Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE).\n */\n#define GLFW_SRGB_CAPABLE           0x0002100E\n/*! @brief Monitor refresh rate hint.\n *\n *  Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE).\n */\n#define GLFW_REFRESH_RATE           0x0002100F\n/*! @brief Framebuffer double buffering hint.\n *\n *  Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER).\n */\n#define GLFW_DOUBLEBUFFER           0x00021010\n\n/*! @brief Context client API hint and attribute.\n *\n *  Context client API [hint](@ref GLFW_CLIENT_API_hint) and\n *  [attribute](@ref GLFW_CLIENT_API_attrib).\n */\n#define GLFW_CLIENT_API             0x00022001\n/*! @brief Context client API major version hint and attribute.\n *\n *  Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint)\n *  and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib).\n */\n#define GLFW_CONTEXT_VERSION_MAJOR  0x00022002\n/*! @brief Context client API minor version hint and attribute.\n *\n *  Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint)\n *  and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib).\n */\n#define GLFW_CONTEXT_VERSION_MINOR  0x00022003\n/*! @brief Context client API revision number hint and attribute.\n *\n *  Context client API revision number\n *  [attribute](@ref GLFW_CONTEXT_REVISION_attrib).\n */\n#define GLFW_CONTEXT_REVISION       0x00022004\n/*! @brief Context robustness hint and attribute.\n *\n *  Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint)\n *  and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib).\n */\n#define GLFW_CONTEXT_ROBUSTNESS     0x00022005\n/*! @brief OpenGL forward-compatibility hint and attribute.\n *\n *  OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint)\n *  and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib).\n */\n#define GLFW_OPENGL_FORWARD_COMPAT  0x00022006\n/*! @brief Debug mode context hint and attribute.\n *\n *  Debug mode context [hint](@ref GLFW_CONTEXT_DEBUG_hint) and\n *  [attribute](@ref GLFW_CONTEXT_DEBUG_attrib).\n */\n#define GLFW_CONTEXT_DEBUG          0x00022007\n/*! @brief Legacy name for compatibility.\n *\n *  This is an alias for compatibility with earlier versions.\n */\n#define GLFW_OPENGL_DEBUG_CONTEXT   GLFW_CONTEXT_DEBUG\n/*! @brief OpenGL profile hint and attribute.\n *\n *  OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and\n *  [attribute](@ref GLFW_OPENGL_PROFILE_attrib).\n */\n#define GLFW_OPENGL_PROFILE         0x00022008\n/*! @brief Context flush-on-release hint and attribute.\n *\n *  Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and\n *  [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib).\n */\n#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009\n/*! @brief Context error suppression hint and attribute.\n *\n *  Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and\n *  [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib).\n */\n#define GLFW_CONTEXT_NO_ERROR       0x0002200A\n/*! @brief Context creation API hint and attribute.\n *\n *  Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and\n *  [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib).\n */\n#define GLFW_CONTEXT_CREATION_API   0x0002200B\n/*! @brief Window content area scaling window\n *  [window hint](@ref GLFW_SCALE_TO_MONITOR).\n */\n#define GLFW_SCALE_TO_MONITOR       0x0002200C\n/*! @brief macOS specific\n *  [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint).\n */\n#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001\n/*! @brief macOS specific\n *  [window hint](@ref GLFW_COCOA_FRAME_NAME_hint).\n */\n#define GLFW_COCOA_FRAME_NAME         0x00023002\n/*! @brief macOS specific\n *  [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint).\n */\n#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003\n/*! @brief macOS specific\n *  [window hint](@ref GLFW_COCOA_COLOR_SPACE_hint).\n */\n#define GLFW_COCOA_COLOR_SPACE 0x00023004\ntypedef enum {\n    DEFAULT_COLORSPACE = 0,\n    SRGB_COLORSPACE = 1,\n    DISPLAY_P3_COLORSPACE = 2,\n} GlfwCocoaColorSpaces;\n/*! @brief Blur Radius. On macOS the actual radius is used. On Linux it is treated as a bool.\n *  [window hint](@ref GLFW_BLUR_RADIUS).\n */\n#define GLFW_BLUR_RADIUS       0x0002305\n\n/*! @brief X11 specific\n *  [window hint](@ref GLFW_X11_CLASS_NAME_hint).\n */\n#define GLFW_X11_CLASS_NAME         0x00024001\n/*! @brief X11 specific\n *  [window hint](@ref GLFW_X11_CLASS_NAME_hint).\n */\n#define GLFW_X11_INSTANCE_NAME      0x00024002\n\n#define GLFW_WAYLAND_APP_ID         0x00025001\n#define GLFW_WAYLAND_BGCOLOR        0x00025002\n#define GLFW_WAYLAND_WINDOW_TAG     0x00025003\n/*! @} */\n\n#define GLFW_NO_API                          0\n#define GLFW_OPENGL_API             0x00030001\n#define GLFW_OPENGL_ES_API          0x00030002\n\n#define GLFW_NO_ROBUSTNESS                   0\n#define GLFW_NO_RESET_NOTIFICATION  0x00031001\n#define GLFW_LOSE_CONTEXT_ON_RESET  0x00031002\n\n#define GLFW_OPENGL_ANY_PROFILE              0\n#define GLFW_OPENGL_CORE_PROFILE    0x00032001\n#define GLFW_OPENGL_COMPAT_PROFILE  0x00032002\n\n#define GLFW_CURSOR                 0x00033001\n#define GLFW_STICKY_KEYS            0x00033002\n#define GLFW_STICKY_MOUSE_BUTTONS   0x00033003\n#define GLFW_LOCK_KEY_MODS          0x00033004\n#define GLFW_RAW_MOUSE_MOTION       0x00033005\n\n#define GLFW_CURSOR_NORMAL          0x00034001\n#define GLFW_CURSOR_HIDDEN          0x00034002\n#define GLFW_CURSOR_DISABLED        0x00034003\n\n#define GLFW_ANY_RELEASE_BEHAVIOR            0\n#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001\n#define GLFW_RELEASE_BEHAVIOR_NONE  0x00035002\n\n#define GLFW_NATIVE_CONTEXT_API     0x00036001\n#define GLFW_EGL_CONTEXT_API        0x00036002\n#define GLFW_OSMESA_CONTEXT_API     0x00036003\n\n#define GLFW_ANGLE_PLATFORM_TYPE_NONE    0x00037001\n#define GLFW_ANGLE_PLATFORM_TYPE_OPENGL  0x00037002\n#define GLFW_ANGLE_PLATFORM_TYPE_OPENGLES 0x00037003\n#define GLFW_ANGLE_PLATFORM_TYPE_D3D9    0x00037004\n#define GLFW_ANGLE_PLATFORM_TYPE_D3D11   0x00037005\n#define GLFW_ANGLE_PLATFORM_TYPE_VULKAN  0x00037007\n#define GLFW_ANGLE_PLATFORM_TYPE_METAL   0x00037008\n\n/*! @defgroup shapes Standard cursor shapes\n *  @brief Standard system cursor shapes.\n *\n *  See [standard cursor creation](@ref cursor_standard) for how these are used.\n *\n *  @ingroup input\n *  @{ */\n\ntypedef enum {\n/* start mouse cursor shapes (auto generated by gen-key-constants.py do not edit) */\n    GLFW_DEFAULT_CURSOR,\n    GLFW_TEXT_CURSOR,\n    GLFW_POINTER_CURSOR,\n    GLFW_HELP_CURSOR,\n    GLFW_WAIT_CURSOR,\n    GLFW_PROGRESS_CURSOR,\n    GLFW_CROSSHAIR_CURSOR,\n    GLFW_CELL_CURSOR,\n    GLFW_VERTICAL_TEXT_CURSOR,\n    GLFW_MOVE_CURSOR,\n    GLFW_E_RESIZE_CURSOR,\n    GLFW_NE_RESIZE_CURSOR,\n    GLFW_NW_RESIZE_CURSOR,\n    GLFW_N_RESIZE_CURSOR,\n    GLFW_SE_RESIZE_CURSOR,\n    GLFW_SW_RESIZE_CURSOR,\n    GLFW_S_RESIZE_CURSOR,\n    GLFW_W_RESIZE_CURSOR,\n    GLFW_EW_RESIZE_CURSOR,\n    GLFW_NS_RESIZE_CURSOR,\n    GLFW_NESW_RESIZE_CURSOR,\n    GLFW_NWSE_RESIZE_CURSOR,\n    GLFW_ZOOM_IN_CURSOR,\n    GLFW_ZOOM_OUT_CURSOR,\n    GLFW_ALIAS_CURSOR,\n    GLFW_COPY_CURSOR,\n    GLFW_NOT_ALLOWED_CURSOR,\n    GLFW_NO_DROP_CURSOR,\n    GLFW_GRAB_CURSOR,\n    GLFW_GRABBING_CURSOR,\n    GLFW_INVALID_CURSOR,\n/* end mouse cursor shapes */\n} GLFWCursorShape;\n/*! @} */\n\n#define GLFW_CONNECTED              0x00040001\n#define GLFW_DISCONNECTED           0x00040002\n\n/*! @addtogroup init\n *  @{ */\n/*! @brief Joystick hat buttons init hint.\n *\n *  Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS).\n */\n#define GLFW_JOYSTICK_HAT_BUTTONS   0x00050001\n/*! @brief ANGLE rendering backend init hint.\n *\n *  ANGLE rendering backend [init hint](@ref GLFW_ANGLE_PLATFORM_TYPE_hint).\n */\n#define GLFW_ANGLE_PLATFORM_TYPE    0x00050002\n#define GLFW_DEBUG_KEYBOARD         0x00050003\n#define GLFW_DEBUG_RENDERING        0x00050004\n/*! @brief macOS specific init hint.\n *\n *  macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint).\n */\n#define GLFW_COCOA_CHDIR_RESOURCES  0x00051001\n/*! @brief macOS specific init hint.\n *\n *  macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint).\n */\n#define GLFW_COCOA_MENUBAR          0x00051002\n#define GLFW_WAYLAND_IME            0x00051003\n/*! @} */\n\n#define GLFW_DONT_CARE              -1\n\n\n/*************************************************************************\n * GLFW API types\n *************************************************************************/\n\n/*! @brief Client API function pointer type.\n *\n *  Generic function pointer used for returning client API function pointers\n *  without forcing a cast from a regular pointer.\n *\n *  @sa @ref context_glext\n *  @sa @ref glfwGetProcAddress\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup context\n */\ntypedef void (*GLFWglproc)(void);\n\n/*! @brief Vulkan API function pointer type.\n *\n *  Generic function pointer used for returning Vulkan API function pointers\n *  without forcing a cast from a regular pointer.\n *\n *  @sa @ref vulkan_proc\n *  @sa @ref glfwGetInstanceProcAddress\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup vulkan\n */\ntypedef void (*GLFWvkproc)(void);\n\n/*! @brief Opaque monitor object.\n *\n *  Opaque monitor object.\n *\n *  @see @ref monitor_object\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\ntypedef struct GLFWmonitor GLFWmonitor;\n\n/*! @brief Opaque window object.\n *\n *  Opaque window object.\n *\n *  @see @ref window_object\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef struct GLFWwindow GLFWwindow;\n\n/*! @brief Opaque cursor object.\n *\n *  Opaque cursor object.\n *\n *  @see @ref cursor_object\n *\n *  @since Added in version 3.1.\n *\n *  @ingroup input\n */\ntypedef struct GLFWcursor GLFWcursor;\n\n/*! @brief Opaque drop data object.\n *\n *  Opaque drop data object representing data from a drag and drop operation.\n *  This object is passed to the drop callback and can be used to query\n *  available MIME types and read the dropped data in chunks.\n *\n *  @see @ref path_drop\n *  @see @ref glfwGetDropMimeTypes\n *  @see @ref glfwReadDropData\n *\n *  @since Added in version 4.0.\n *\n *  @ingroup input\n */\ntypedef enum {\n    GLFW_RELEASE = 0,\n    GLFW_PRESS = 1,\n    GLFW_REPEAT = 2\n} GLFWKeyAction;\n\ntypedef enum {\n    GLFW_IME_NONE,\n    GLFW_IME_PREEDIT_CHANGED,\n    GLFW_IME_COMMIT_TEXT,\n    GLFW_IME_WAYLAND_DONE_EVENT,\n} GLFWIMEState;\n\ntypedef enum {\n    GLFW_IME_UPDATE_FOCUS = 1,\n    GLFW_IME_UPDATE_CURSOR_POSITION = 2\n} GLFWIMEUpdateType;\n\ntypedef struct GLFWIMEUpdateEvent {\n    GLFWIMEUpdateType type;\n    const char *before_text, *at_text, *after_text;\n    bool focused;\n    struct {\n        int left, top, width, height;\n    } cursor;\n} GLFWIMEUpdateEvent;\n\n\ntypedef struct GLFWkeyevent\n{\n    // The [keyboard key](@ref keys) that was pressed or released.\n    uint32_t key, shifted_key, alternate_key;\n\n    // The platform-specific identifier of the key.\n    int native_key;\n\n    // The event action. Either `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.\n    GLFWKeyAction action;\n\n    // Bit field describing which [modifier keys](@ref mods) were held down.\n    int mods;\n\n    // UTF-8 encoded text generated by this key event or empty string or NULL\n    const char *text;\n\n    // Used for Input Method events. Zero for normal key events.\n    //   A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed.\n    //   A value of GLFW_IME_COMMIT_TEXT means the text should be committed.\n    GLFWIMEState ime_state;\n\n    // For internal use only. On Linux it is the actual keycode reported by the windowing system, in contrast\n    // to native_key which can be the result of a compose operation. On macOS it is the same as native_key.\n    uint32_t native_key_id;\n\n    // True if this is a synthesized event on focus change\n    bool fake_event_on_focus_change;\n} GLFWkeyevent;\n\ntypedef enum { GLFW_LAYER_SHELL_NONE, GLFW_LAYER_SHELL_BACKGROUND, GLFW_LAYER_SHELL_PANEL, GLFW_LAYER_SHELL_TOP, GLFW_LAYER_SHELL_OVERLAY } GLFWLayerShellType;\n\ntypedef enum { GLFW_EDGE_TOP, GLFW_EDGE_BOTTOM, GLFW_EDGE_LEFT, GLFW_EDGE_RIGHT, GLFW_EDGE_CENTER, GLFW_EDGE_NONE, GLFW_EDGE_CENTER_SIZED } GLFWEdge;\n\ntypedef enum { GLFW_FOCUS_NOT_ALLOWED, GLFW_FOCUS_EXCLUSIVE, GLFW_FOCUS_ON_DEMAND} GLFWFocusPolicy;\n\ntypedef struct GLFWLayerShellConfig {\n    GLFWLayerShellType type;\n    GLFWEdge edge;\n    struct {\n        GLFWEdge edge;\n        int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;\n    } previous;\n    bool was_toggled_to_fullscreen;\n    char output_name[128];\n    GLFWFocusPolicy focus_policy;\n    unsigned x_size_in_cells, x_size_in_pixels;\n    unsigned y_size_in_cells, y_size_in_pixels;\n    int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;\n    int requested_exclusive_zone, hide_on_focus_loss;\n    unsigned override_exclusive_zone;\n    void (*size_callback)(GLFWwindow *window, float xscale, float yscale, unsigned *cell_width, unsigned *cell_height, double *left_edge_spacing, double *top_edge_spacing, double *right_edge_spacing, double *bottom_edge_spacing);\n    struct { float xscale, yscale; } expected;\n    struct {\n        float background_opacity; int background_blur, color_space;\n    } related;\n} GLFWLayerShellConfig;\n\ntypedef struct GLFWDBUSNotificationData {\n    const char *app_name, *icon, *summary, *body, *category, **actions; size_t num_actions;\n    int32_t timeout; uint8_t urgency; uint32_t replaces; int muted;\n} GLFWDBUSNotificationData;\n\ntypedef enum { GLFW_DROP_ENTER, GLFW_DROP_MOVE, GLFW_DROP_LEAVE, GLFW_DROP_DROP, GLFW_DROP_STATUS_UPDATE, GLFW_DROP_DATA_AVAILABLE } GLFWDropEventType;\n\n/*! @brief Drag operation types.\n *\n *  These constants specify the type of drag operation (copy, move, or generic).\n *\n *  @ingroup input\n */\ntypedef enum {\n    GLFW_DRAG_OPERATION_NONE = 0,  // no operation, drop was not accepted\n    /*! Move the dragged data to the destination. */\n    GLFW_DRAG_OPERATION_MOVE = 1,\n    /*! Copy the dragged data to the destination. */\n    GLFW_DRAG_OPERATION_COPY = 2,\n    /*! Generic drag operation (platform decides semantics). */\n    GLFW_DRAG_OPERATION_GENERIC = 4\n} GLFWDragOperationType;\n\n\ntypedef struct GLFWDropEvent {\n    GLFWDropEventType type;\n    const char **mimes; size_t num_mimes;\n    // Positions are only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE.\n    // They are in window co-ordinates same as for mouse events\n    double xpos, ypos;\n    bool from_self;  // Only valid upto GLFW_DROP_DROP\n    ssize_t (*read_data)(GLFWwindow *w, struct GLFWDropEvent* ev, char *buffer, size_t sz);  // Only valid for GLFW_DROP_DATA_AVAILABLE\n    void (*finish_drop)(GLFWwindow *w, GLFWDragOperationType op); // Only valid for GLFW_DROP_DROP and GLFW_DROP_DATA_AVAILABLE\n} GLFWDropEvent;\ntypedef void (* GLFWdropeventfun)(GLFWwindow*, GLFWDropEvent *event);\n\n\n/*! @brief The function pointer type for error callbacks.\n *\n *  This is the function pointer type for error callbacks.  An error callback\n *  function has the following signature:\n *  @code\n *  void callback_name(int error_code, const char* description)\n *  @endcode\n *\n *  @param[in] error_code An [error code](@ref errors).  Future releases may add\n *  more error codes.\n *  @param[in] description A UTF-8 encoded string describing the error.\n *\n *  @pointer_lifetime The error description string is valid until the callback\n *  function returns.\n *\n *  @sa @ref error_handling\n *  @sa @ref glfwSetErrorCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup init\n */\ntypedef void (* GLFWerrorfun)(int,const char*);\n\n/*! @brief The function pointer type for window position callbacks.\n *\n *  This is the function pointer type for window position callbacks.  A window\n *  position callback function has the following signature:\n *  @code\n *  void callback_name(GLFWwindow* window, int xpos, int ypos)\n *  @endcode\n *\n *  @param[in] window The window that was moved.\n *  @param[in] xpos The new x-coordinate, in screen coordinates, of the\n *  upper-left corner of the content area of the window.\n *  @param[in] ypos The new y-coordinate, in screen coordinates, of the\n *  upper-left corner of the content area of the window.\n *\n *  @sa @ref window_pos\n *  @sa @ref glfwSetWindowPosCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowposfun)(GLFWwindow*,int,int);\n\n/*! @brief The function pointer type for window size callbacks.\n *\n *  This is the function pointer type for window size callbacks.  A window size\n *  callback function has the following signature:\n *  @code\n *  void callback_name(GLFWwindow* window, int width, int height)\n *  @endcode\n *\n *  @param[in] window The window that was resized.\n *  @param[in] width The new width, in screen coordinates, of the window.\n *  @param[in] height The new height, in screen coordinates, of the window.\n *\n *  @sa @ref window_size\n *  @sa @ref glfwSetWindowSizeCallback\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int);\n\n/*! @brief The function pointer type for window close callbacks.\n *\n *  This is the function pointer type for window close callbacks.  A window\n *  close callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window)\n *  @endcode\n *\n *  @param[in] window The window that the user attempted to close.\n *\n *  @sa @ref window_close\n *  @sa @ref glfwSetWindowCloseCallback\n *\n *  @since Added in version 2.5.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowclosefun)(GLFWwindow*);\n\n/*! @brief The function pointer type for application close callbacks.\n *\n *  This is the function pointer type for application close callbacks.  A application\n *  close callback function has the following signature:\n *  @code\n *  void function_name(int flags)\n *  @endcode\n *\n *  @param[in] flags 0 for a user requested application quit, 1 if a fatal error occurred and application should quit ASAP\n *\n *  @sa @ref glfwSetApplicationCloseCallback\n *\n *  @ingroup window\n */\ntypedef void (* GLFWapplicationclosefun)(int);\n\n\n/*! @brief The function pointer type for system color theme change callbacks.\n *\n *  This is the function pointer type for system color theme changes.\n *  @code\n *  void function_name(GLFWColorScheme theme_type, bool is_initial_value)\n *  @endcode\n *\n *  @param[in] theme_type 0 for unknown, 1 for dark and 2 for light\n *  @param[in] is_initial_value true if this is the initial read of the color theme on systems where it is asynchronous such as Linux\n *\n *  @sa @ref glfwSetSystemColorThemeChangeCallback\n *\n *  @ingroup window\n */\ntypedef void (* GLFWsystemcolorthemechangefun)(GLFWColorScheme, bool);\n\n\n/*! @brief The function pointer type for window content refresh callbacks.\n *\n *  This is the function pointer type for window content refresh callbacks.\n *  A window content refresh callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window);\n *  @endcode\n *\n *  @param[in] window The window whose content needs to be refreshed.\n *\n *  @sa @ref window_refresh\n *  @sa @ref glfwSetWindowRefreshCallback\n *\n *  @since Added in version 2.5.\n *  @glfw3 Added window handle parameter.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowrefreshfun)(GLFWwindow*);\n\n/*! @brief The function pointer type for window focus callbacks.\n *\n *  This is the function pointer type for window focus callbacks.  A window\n *  focus callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int focused)\n *  @endcode\n *\n *  @param[in] window The window that gained or lost input focus.\n *  @param[in] focused `true` if the window was given input focus, or\n *  `false` if it lost it.\n *\n *  @sa @ref window_focus\n *  @sa @ref glfwSetWindowFocusCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowfocusfun)(GLFWwindow*,int);\n\n/*! @brief The function signature for window occlusion callbacks.\n *\n *  This is the function signature for window occlusion callback functions.\n *\n *  @param[in] window The window whose occlusion state changed.\n *  @param[in] occluded `true` if the window was occluded, or `false`\n *  if the window is no longer occluded.\n *\n *  @sa @ref window_occlusion\n *  @sa @ref glfwSetWindowOcclusionCallback\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowocclusionfun)(GLFWwindow*, bool);\n\n\n/*! @brief The function pointer type for window iconify callbacks.\n *\n *  This is the function pointer type for window iconify callbacks.  A window\n *  iconify callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int iconified)\n *  @endcode\n *\n *  @param[in] window The window that was iconified or restored.\n *  @param[in] iconified `true` if the window was iconified, or\n *  `false` if it was restored.\n *\n *  @sa @ref window_iconify\n *  @sa @ref glfwSetWindowIconifyCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int);\n\n/*! @brief The function pointer type for window maximize callbacks.\n *\n *  This is the function pointer type for window maximize callbacks.  A window\n *  maximize callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int maximized)\n *  @endcode\n *\n *  @param[in] window The window that was maximized or restored.\n *  @param[in] maximized `true` if the window was maximized, or\n *  `false` if it was restored.\n *\n *  @sa @ref window_maximize\n *  @sa glfwSetWindowMaximizeCallback\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int);\n\n/*! @brief The function pointer type for framebuffer size callbacks.\n *\n *  This is the function pointer type for framebuffer size callbacks.\n *  A framebuffer size callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int width, int height)\n *  @endcode\n *\n *  @param[in] window The window whose framebuffer was resized.\n *  @param[in] width The new width, in pixels, of the framebuffer.\n *  @param[in] height The new height, in pixels, of the framebuffer.\n *\n *  @sa @ref window_fbsize\n *  @sa @ref glfwSetFramebufferSizeCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int);\n\n/*! @brief The function pointer type for window content scale callbacks.\n *\n *  This is the function pointer type for window content scale callbacks.\n *  A window content scale callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, float xscale, float yscale)\n *  @endcode\n *\n *  @param[in] window The window whose content scale changed.\n *  @param[in] xscale The new x-axis content scale of the window.\n *  @param[in] yscale The new y-axis content scale of the window.\n *\n *  @sa @ref window_scale\n *  @sa @ref glfwSetWindowContentScaleCallback\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup window\n */\ntypedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float);\n\n/*! @brief The function pointer type for mouse button callbacks.\n *\n *  This is the function pointer type for mouse button callback functions.\n *  A mouse button callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int button, int action, int mods)\n *  @endcode\n *\n *  @param[in] window The window that received the event.\n *  @param[in] button The [mouse button](@ref buttons) that was pressed or\n *  released.\n *  @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`.  Future releases\n *  may add more actions.\n *  @param[in] mods Bit field describing which [modifier keys](@ref mods) were\n *  held down.\n *\n *  @sa @ref input_mouse_button\n *  @sa @ref glfwSetMouseButtonCallback\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added window handle and modifier mask parameters.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int);\n\n/*! @brief The function pointer type for cursor position callbacks.\n *\n *  This is the function pointer type for cursor position callbacks.  A cursor\n *  position callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, double xpos, double ypos);\n *  @endcode\n *\n *  @param[in] window The window that received the event.\n *  @param[in] xpos The new cursor x-coordinate, relative to the left edge of\n *  the content area.\n *  @param[in] ypos The new cursor y-coordinate, relative to the top edge of the\n *  content area.\n *\n *  @sa @ref cursor_pos\n *  @sa @ref glfwSetCursorPosCallback\n *\n *  @since Added in version 3.0.  Replaces `GLFWmouseposfun`.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWcursorposfun)(GLFWwindow*,double,double);\n\n/*! @brief The function pointer type for cursor enter/leave callbacks.\n *\n *  This is the function pointer type for cursor enter/leave callbacks.\n *  A cursor enter/leave callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, int entered)\n *  @endcode\n *\n *  @param[in] window The window that received the event.\n *  @param[in] entered `true` if the cursor entered the window's content\n *  area, or `false` if it left it.\n *\n *  @sa @ref cursor_enter\n *  @sa @ref glfwSetCursorEnterCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWcursorenterfun)(GLFWwindow*,int);\n\n/*! @brief The function pointer type for scroll callbacks.\n *\n *  This is the function pointer type for scroll callbacks.  A scroll callback\n *  function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, double xoffset, double yoffset)\n *  @endcode\n *\n *  @param[in] window The window that received the event.\n *  @param[in] xoffset The scroll offset along the x-axis.\n *  @param[in] yoffset The scroll offset along the y-axis.\n *  @param[in] flags A bit-mask providing extra data about the event.\n *  flags & 1 will be true if and only if the offset values are \"high-precision\",\n *  typically pixel values. Otherwise the offset values are number of lines.\n *  (flags >> 1) & 7 will have value 1 for the start of momentum scrolling,\n *  value 2 for stationary momentum scrolling, value 3 for momentum scrolling\n *  in progress, value 4 for momentum scrolling ended, value 5 for momentum\n *  scrolling cancelled and value 6 if scrolling may begin soon.\n *  @param[int] mods The keyboard modifiers\n *\n *  @sa @ref scrolling\n *  @sa @ref glfwSetScrollCallback\n *\n *  @since Added in version 3.0.  Replaces `GLFWmousewheelfun`.\n *  @since Changed in version 4.0.  Added `flags` parameter.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWscrollfun)(GLFWwindow*,const GLFWScrollEvent*);\n\n/*! @brief The function pointer type for key callbacks.\n *\n *  This is the function pointer type for key callbacks.  A keyboard\n *  key callback function has the following signature:\n *  @code\n *  void function_name(GLFWwindow* window, uint32_t key, int native_key, int action, int mods)\n *  @endcode\n *  The semantics of this function are that the key that is interacted with on the\n *  keyboard is reported, and the text, if any generated by the key is reported.\n *  So, for example, if on a US-ASCII keyboard the user presses Shift+= GLFW\n *  will report the text \"+\" and the key as GLFW_KEY_EQUAL. The reported key takes into\n *  account any current keyboard maps defined in the OS. So with a dvorak mapping, pressing\n *  the \"s\" key will generate text \"o\" and GLFW_KEY_O.\n *\n *  @param[in] window The window that received the event.\n *  @param[in] ev The key event, see GLFWkeyevent. The data in this event is only valid for\n *  the lifetime of the callback.\n *\n *  @note On X11/Wayland if a modifier other than the modifiers GLFW reports\n *  (ctrl/shift/alt/super) is used, GLFW will report the shifted key rather\n *  than the unshifted key. So for example, if ISO_Shift_Level_5 is used to\n *  convert the key A into UP GLFW will report the key as UP with no modifiers.\n *\n *  @sa @ref input_key\n *  @sa @ref glfwSetKeyboardCallback\n *\n *  @since Added in version 4.0.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);\n\ntypedef enum {\n    GLFW_DRAG_DATA_REQUEST,  // request data for specified mime type\n    GLFW_DRAG_CANCELLED,\n    GLFW_DRAG_FINSHED,\n    GLFW_DRAG_ACCEPTED,  // mimetype was accepted or NULL if drag was accepted but no mime type specified\n    GLFW_DRAG_ACTION_CHANGED,  // action was changed 0 or GLFWDragOperationType\n    GLFW_DRAG_DROPPED,  // drop was performed but no data transferred yet\n} GLFWDragEventType;\n\ntypedef struct GLFWDragSourceItem {\n    const char *mime_type;\n    // Can be on null to provide data when the drag is started should be used only when the data is relatively small\n    const char *optional_data;\n    size_t data_size;\n} GLFWDragSourceItem;\n\ntypedef struct GLFWDragEvent {\n    GLFWDragEventType type;\n    // When the drag event callback is called with a mimetype and no data, the\n    // application should set the data ans data_sz and err_num fields.\n    // Once glfw is done reading the data the drag event callback will be\n    // called with the data pointer unchanged. The application is now free\n    // to delete the data, as needed.\n    const char *mime_type;\n    const char *data; size_t data_sz;\n    int err_num;  // POSIX error code indicating failure fetching data\n    GLFWDragOperationType action;  // can be 0 indicating no action\n} GLFWDragEvent;\n\ntypedef void (* GLFWdragsourcefun)(GLFWwindow* window, GLFWDragEvent *ev);\n\n/*! @brief The function pointer type for drag event callbacks.\n *\n *  This is the function pointer type for drag event callbacks. A drag event\n *  callback function has the following signature:\n *  @code\n *  int function_name(GLFWwindow* window, int event, double xpos, double ypos, const char** mime_types, int* mime_count)\n *  @endcode\n *\n *  @param[in] window The window that received the drag event.\n *  @param[in] event The drag event type: @ref GLFW_DRAG_ENTER, @ref GLFW_DRAG_MOVE,\n *  or @ref GLFW_DRAG_LEAVE.\n *  @param[in] xpos The x-coordinate of the drag position in window coordinates.\n *  @param[in] ypos The y-coordinate of the drag position in window coordinates.\n *  @param[in,out] mime_types A writable array of MIME type strings available from the drag source.\n *  For @ref GLFW_DRAG_ENTER and @ref GLFW_DRAG_MOVE events this is non-NULL and contains all\n *  available MIME types. The callback is responsible for sorting this list by priority and\n *  keeping only the MIME types it wants to accept. The first MIME type in the sorted list\n *  will be used for the drop operation. The strings are only valid for the duration of the\n *  callback; if you need to store them, make copies. For @ref GLFW_DRAG_LEAVE events this\n *  is `NULL`.\n *  @param[in,out] mime_count Pointer to the number of MIME types in the array. The callback\n *  should update this to reflect the new count after sorting and filtering. For\n *  @ref GLFW_DRAG_LEAVE events this is `NULL`.\n *  @return For @ref GLFW_DRAG_ENTER and @ref GLFW_DRAG_MOVE events, return non-zero\n *  to accept the drag or zero to reject it. This allows the application to\n *  dynamically accept or reject the drag based on the current position.\n *  Return value is ignored for @ref GLFW_DRAG_LEAVE events.\n *\n *  @sa @ref drag_events\n *  @sa @ref glfwSetDragCallback\n *  @sa @ref glfwUpdateDragState\n *\n *  @since Added in version 4.0.\n *\n *  @ingroup input\n */\ntypedef int (* GLFWdragfun)(GLFWwindow*, GLFWDragEventType event, double xpos, double ypos, const char** mime_types, int* mime_count);\n\ntypedef void (* GLFWliveresizefun)(GLFWwindow*, bool);\n\n/*! @brief The function pointer type for monitor configuration callbacks.\n *\n *  This is the function pointer type for monitor configuration callbacks.\n *  A monitor callback function has the following signature:\n *  @code\n *  void function_name(GLFWmonitor* monitor, int event)\n *  @endcode\n *\n *  @param[in] monitor The monitor that was connected or disconnected.\n *  @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.  Future\n *  releases may add more events.\n *\n *  @sa @ref monitor_event\n *  @sa @ref glfwSetMonitorCallback\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\ntypedef void (* GLFWmonitorfun)(GLFWmonitor*,int);\n\n/*! @brief The function pointer type for joystick configuration callbacks.\n *\n *  This is the function pointer type for joystick configuration callbacks.\n *  A joystick configuration callback function has the following signature:\n *  @code\n *  void function_name(int jid, int event)\n *  @endcode\n *\n *  @param[in] jid The joystick that was connected or disconnected.\n *  @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.  Future\n *  releases may add more events.\n *\n *  @sa @ref joystick_event\n *  @sa @ref glfwSetJoystickCallback\n *\n *  @since Added in version 3.2.\n *\n *  @ingroup input\n */\ntypedef void (* GLFWjoystickfun)(int,int);\n\ntypedef void (* GLFWuserdatafun)(unsigned long long, void*);\ntypedef void (* GLFWtickcallback)(void*);\ntypedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data);\ntypedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool is_single_glyph);\ntypedef char* (* GLFWcurrentselectionfun)(void);\ntypedef bool (* GLFWhascurrentselectionfun)(void);\ntypedef void (* GLFWclipboarddatafreefun)(void* data);\ntypedef struct GLFWDataChunk {\n    const char *data;\n    size_t sz;\n    GLFWclipboarddatafreefun free;\n    void *iter, *free_data;\n} GLFWDataChunk;\ntypedef enum {\n    GLFW_CLIPBOARD, GLFW_PRIMARY_SELECTION\n} GLFWClipboardType;\ntypedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);\ntypedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);\ntypedef bool (* GLFWimecursorpositionfun)(GLFWwindow *window, GLFWIMEUpdateEvent *ev);\ntypedef void (* GLFWclipboardlostfun )(GLFWClipboardType);\n\n/*! @brief Video mode type.\n *\n *  This describes a single video mode.\n *\n *  @sa @ref monitor_modes\n *  @sa @ref glfwGetVideoMode\n *  @sa @ref glfwGetVideoModes\n *\n *  @since Added in version 1.0.\n *  @glfw3 Added refresh rate member.\n *\n *  @ingroup monitor\n */\ntypedef struct GLFWvidmode\n{\n    /*! The width, in screen coordinates, of the video mode.\n     */\n    int width;\n    /*! The height, in screen coordinates, of the video mode.\n     */\n    int height;\n    /*! The bit depth of the red channel of the video mode.\n     */\n    int redBits;\n    /*! The bit depth of the green channel of the video mode.\n     */\n    int greenBits;\n    /*! The bit depth of the blue channel of the video mode.\n     */\n    int blueBits;\n    /*! The refresh rate, in Hz, of the video mode.\n     */\n    int refreshRate;\n} GLFWvidmode;\n\n/*! @brief Gamma ramp.\n *\n *  This describes the gamma ramp for a monitor.\n *\n *  @sa @ref monitor_gamma\n *  @sa @ref glfwGetGammaRamp\n *  @sa @ref glfwSetGammaRamp\n *\n *  @since Added in version 3.0.\n *\n *  @ingroup monitor\n */\ntypedef struct GLFWgammaramp\n{\n    /*! An array of value describing the response of the red channel.\n     */\n    unsigned short* red;\n    /*! An array of value describing the response of the green channel.\n     */\n    unsigned short* green;\n    /*! An array of value describing the response of the blue channel.\n     */\n    unsigned short* blue;\n    /*! The number of elements in each array.\n     */\n    unsigned int size;\n} GLFWgammaramp;\n\n/*! @brief Image data.\n *\n *  This describes a single 2D image.  See the documentation for each related\n *  function what the expected pixel format is.\n *\n *  @sa @ref cursor_custom\n *  @sa @ref window_icon\n *\n *  @since Added in version 2.1.\n *  @glfw3 Removed format and bytes-per-pixel members.\n *\n *  @ingroup window\n */\ntypedef struct GLFWimage\n{\n    /*! The width, in pixels, of this image.\n     */\n    int width;\n    /*! The height, in pixels, of this image.\n     */\n    int height;\n    /*! The pixel data of this image, arranged left-to-right, top-to-bottom.\n     */\n    const unsigned char* pixels;\n} GLFWimage;\n\n/*! @brief Gamepad input state\n *\n *  This describes the input state of a gamepad.\n *\n *  @sa @ref gamepad\n *  @sa @ref glfwGetGamepadState\n *\n *  @since Added in version 3.3.\n *\n *  @ingroup input\n */\ntypedef struct GLFWgamepadstate\n{\n    /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS`\n     *  or `GLFW_RELEASE`.\n     */\n    unsigned char buttons[15];\n    /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0\n     *  to 1.0 inclusive.\n     */\n    float axes[6];\n} GLFWgamepadstate;\n\n\n/*************************************************************************\n * GLFW API functions\n *************************************************************************/\n\n/*! @brief Initializes the GLFW library.\n *\n *  This function initializes the GLFW library.  Before most GLFW functions can\n *  be used, GLFW must be initialized, and before an application terminates GLFW\n *  should be terminated in order to free any resources allocated during or\n *  after initialization.\n *\n *  If this function fails, it calls @ref glfwTerminate before returning.  If it\n *  succeeds, you should call @ref glfwTerminate before the application exits.\n *\n *  Additional calls to this function after successful initialization but before\n *  termination will return `true` immediately.\n *\n *  @return `true` if successful, or `false` if an\n *  [error](@ref error_handling) occurred.\n *\n *  @errors Possible errors include @ref GLFW_PLATFORM_ERROR.\n *\n *  @remark @macos This function will change the current directory of the\n *  application to the `Contents/Resources` subdirectory of the application's\n *  bundle, if present.  This can be disabled with the @ref\n *  GLFW_COCOA_CHDIR_RESOURCES init hint.\n *\n *  @thread_safety This function must only be called from the main thread.\n *\n *  @sa @ref intro_init\n *  @sa @ref glfwTerminate\n *\n *  @since Added in version 1.0.\n *\n *  @ingroup init\n */\n\n\ntypedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);\ntypedef bool (* GLFWapplicationshouldhandlereopenfun)(int);\ntypedef bool (* GLFWhandleurlopen)(const char*);\ntypedef void (* GLFWapplicationwillfinishlaunchingfun)(bool);\ntypedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);\ntypedef void (* GLFWcocoarenderframefun)(GLFWwindow*);\ntypedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);\ntypedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);\ntypedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, int, const char*);\ntypedef int (*glfwInit_func)(monotonic_t, bool*);\nGFW_EXTERN glfwInit_func glfwInit_impl;\n#define glfwInit glfwInit_impl\n\ntypedef void (*glfwRunMainLoop_func)(GLFWtickcallback, void*);\nGFW_EXTERN glfwRunMainLoop_func glfwRunMainLoop_impl;\n#define glfwRunMainLoop glfwRunMainLoop_impl\n\ntypedef void (*glfwStopMainLoop_func)(void);\nGFW_EXTERN glfwStopMainLoop_func glfwStopMainLoop_impl;\n#define glfwStopMainLoop glfwStopMainLoop_impl\n\ntypedef unsigned long long (*glfwAddTimer_func)(monotonic_t, bool, GLFWuserdatafun, void *, GLFWuserdatafun);\nGFW_EXTERN glfwAddTimer_func glfwAddTimer_impl;\n#define glfwAddTimer glfwAddTimer_impl\n\ntypedef void (*glfwUpdateTimer_func)(unsigned long long, monotonic_t, bool);\nGFW_EXTERN glfwUpdateTimer_func glfwUpdateTimer_impl;\n#define glfwUpdateTimer glfwUpdateTimer_impl\n\ntypedef void (*glfwRemoveTimer_func)(unsigned long);\nGFW_EXTERN glfwRemoveTimer_func glfwRemoveTimer_impl;\n#define glfwRemoveTimer glfwRemoveTimer_impl\n\ntypedef GLFWdrawtextfun (*glfwSetDrawTextFunction_func)(GLFWdrawtextfun);\nGFW_EXTERN glfwSetDrawTextFunction_func glfwSetDrawTextFunction_impl;\n#define glfwSetDrawTextFunction glfwSetDrawTextFunction_impl\n\ntypedef GLFWcurrentselectionfun (*glfwSetCurrentSelectionCallback_func)(GLFWcurrentselectionfun);\nGFW_EXTERN glfwSetCurrentSelectionCallback_func glfwSetCurrentSelectionCallback_impl;\n#define glfwSetCurrentSelectionCallback glfwSetCurrentSelectionCallback_impl\n\ntypedef GLFWhascurrentselectionfun (*glfwSetHasCurrentSelectionCallback_func)(GLFWhascurrentselectionfun);\nGFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl;\n#define glfwSetHasCurrentSelectionCallback glfwSetHasCurrentSelectionCallback_impl\n\ntypedef GLFWimecursorpositionfun (*glfwSetIMECursorPositionCallback_func)(GLFWimecursorpositionfun);\nGFW_EXTERN glfwSetIMECursorPositionCallback_func glfwSetIMECursorPositionCallback_impl;\n#define glfwSetIMECursorPositionCallback glfwSetIMECursorPositionCallback_impl\n\ntypedef bool (*glfwIsLayerShellSupported_func)(void);\nGFW_EXTERN glfwIsLayerShellSupported_func glfwIsLayerShellSupported_impl;\n#define glfwIsLayerShellSupported glfwIsLayerShellSupported_impl\n\ntypedef void (*glfwTerminate_func)(void);\nGFW_EXTERN glfwTerminate_func glfwTerminate_impl;\n#define glfwTerminate glfwTerminate_impl\n\ntypedef void (*glfwInitHint_func)(int, int);\nGFW_EXTERN glfwInitHint_func glfwInitHint_impl;\n#define glfwInitHint glfwInitHint_impl\n\ntypedef void (*glfwGetVersion_func)(int*, int*, int*);\nGFW_EXTERN glfwGetVersion_func glfwGetVersion_impl;\n#define glfwGetVersion glfwGetVersion_impl\n\ntypedef const char* (*glfwGetVersionString_func)(void);\nGFW_EXTERN glfwGetVersionString_func glfwGetVersionString_impl;\n#define glfwGetVersionString glfwGetVersionString_impl\n\ntypedef int (*glfwGetError_func)(const char**);\nGFW_EXTERN glfwGetError_func glfwGetError_impl;\n#define glfwGetError glfwGetError_impl\n\ntypedef GLFWerrorfun (*glfwSetErrorCallback_func)(GLFWerrorfun);\nGFW_EXTERN glfwSetErrorCallback_func glfwSetErrorCallback_impl;\n#define glfwSetErrorCallback glfwSetErrorCallback_impl\n\ntypedef GLFWmonitor** (*glfwGetMonitors_func)(int*);\nGFW_EXTERN glfwGetMonitors_func glfwGetMonitors_impl;\n#define glfwGetMonitors glfwGetMonitors_impl\n\ntypedef GLFWmonitor* (*glfwGetPrimaryMonitor_func)(void);\nGFW_EXTERN glfwGetPrimaryMonitor_func glfwGetPrimaryMonitor_impl;\n#define glfwGetPrimaryMonitor glfwGetPrimaryMonitor_impl\n\ntypedef void (*glfwGetMonitorPos_func)(GLFWmonitor*, int*, int*);\nGFW_EXTERN glfwGetMonitorPos_func glfwGetMonitorPos_impl;\n#define glfwGetMonitorPos glfwGetMonitorPos_impl\n\ntypedef void (*glfwGetMonitorWorkarea_func)(GLFWmonitor*, int*, int*, int*, int*);\nGFW_EXTERN glfwGetMonitorWorkarea_func glfwGetMonitorWorkarea_impl;\n#define glfwGetMonitorWorkarea glfwGetMonitorWorkarea_impl\n\ntypedef void (*glfwGetMonitorPhysicalSize_func)(GLFWmonitor*, int*, int*);\nGFW_EXTERN glfwGetMonitorPhysicalSize_func glfwGetMonitorPhysicalSize_impl;\n#define glfwGetMonitorPhysicalSize glfwGetMonitorPhysicalSize_impl\n\ntypedef void (*glfwGetMonitorContentScale_func)(GLFWmonitor*, float*, float*);\nGFW_EXTERN glfwGetMonitorContentScale_func glfwGetMonitorContentScale_impl;\n#define glfwGetMonitorContentScale glfwGetMonitorContentScale_impl\n\ntypedef const char* (*glfwGetMonitorName_func)(GLFWmonitor*);\nGFW_EXTERN glfwGetMonitorName_func glfwGetMonitorName_impl;\n#define glfwGetMonitorName glfwGetMonitorName_impl\n\ntypedef const char* (*glfwGetMonitorDescription_func)(GLFWmonitor*);\nGFW_EXTERN glfwGetMonitorDescription_func glfwGetMonitorDescription_impl;\n#define glfwGetMonitorDescription glfwGetMonitorDescription_impl\n\ntypedef void (*glfwSetMonitorUserPointer_func)(GLFWmonitor*, void*);\nGFW_EXTERN glfwSetMonitorUserPointer_func glfwSetMonitorUserPointer_impl;\n#define glfwSetMonitorUserPointer glfwSetMonitorUserPointer_impl\n\ntypedef void* (*glfwGetMonitorUserPointer_func)(GLFWmonitor*);\nGFW_EXTERN glfwGetMonitorUserPointer_func glfwGetMonitorUserPointer_impl;\n#define glfwGetMonitorUserPointer glfwGetMonitorUserPointer_impl\n\ntypedef GLFWmonitorfun (*glfwSetMonitorCallback_func)(GLFWmonitorfun);\nGFW_EXTERN glfwSetMonitorCallback_func glfwSetMonitorCallback_impl;\n#define glfwSetMonitorCallback glfwSetMonitorCallback_impl\n\ntypedef const GLFWvidmode* (*glfwGetVideoModes_func)(GLFWmonitor*, int*);\nGFW_EXTERN glfwGetVideoModes_func glfwGetVideoModes_impl;\n#define glfwGetVideoModes glfwGetVideoModes_impl\n\ntypedef const GLFWvidmode* (*glfwGetVideoMode_func)(GLFWmonitor*);\nGFW_EXTERN glfwGetVideoMode_func glfwGetVideoMode_impl;\n#define glfwGetVideoMode glfwGetVideoMode_impl\n\ntypedef void (*glfwSetGamma_func)(GLFWmonitor*, float);\nGFW_EXTERN glfwSetGamma_func glfwSetGamma_impl;\n#define glfwSetGamma glfwSetGamma_impl\n\ntypedef const GLFWgammaramp* (*glfwGetGammaRamp_func)(GLFWmonitor*);\nGFW_EXTERN glfwGetGammaRamp_func glfwGetGammaRamp_impl;\n#define glfwGetGammaRamp glfwGetGammaRamp_impl\n\ntypedef void (*glfwSetGammaRamp_func)(GLFWmonitor*, const GLFWgammaramp*);\nGFW_EXTERN glfwSetGammaRamp_func glfwSetGammaRamp_impl;\n#define glfwSetGammaRamp glfwSetGammaRamp_impl\n\ntypedef void (*glfwDefaultWindowHints_func)(void);\nGFW_EXTERN glfwDefaultWindowHints_func glfwDefaultWindowHints_impl;\n#define glfwDefaultWindowHints glfwDefaultWindowHints_impl\n\ntypedef void (*glfwWindowHint_func)(int, int);\nGFW_EXTERN glfwWindowHint_func glfwWindowHint_impl;\n#define glfwWindowHint glfwWindowHint_impl\n\ntypedef void (*glfwWindowHintString_func)(int, const char*);\nGFW_EXTERN glfwWindowHintString_func glfwWindowHintString_impl;\n#define glfwWindowHintString glfwWindowHintString_impl\n\ntypedef GLFWwindow* (*glfwCreateWindow_func)(int, int, const char*, GLFWmonitor*, GLFWwindow*, const GLFWLayerShellConfig*);\nGFW_EXTERN glfwCreateWindow_func glfwCreateWindow_impl;\n#define glfwCreateWindow glfwCreateWindow_impl\n\ntypedef bool (*glfwToggleFullscreen_func)(GLFWwindow*, unsigned int);\nGFW_EXTERN glfwToggleFullscreen_func glfwToggleFullscreen_impl;\n#define glfwToggleFullscreen glfwToggleFullscreen_impl\n\ntypedef bool (*glfwIsFullscreen_func)(GLFWwindow*, unsigned int);\nGFW_EXTERN glfwIsFullscreen_func glfwIsFullscreen_impl;\n#define glfwIsFullscreen glfwIsFullscreen_impl\n\ntypedef bool (*glfwAreSwapsAllowed_func)(const GLFWwindow*);\nGFW_EXTERN glfwAreSwapsAllowed_func glfwAreSwapsAllowed_impl;\n#define glfwAreSwapsAllowed glfwAreSwapsAllowed_impl\n\ntypedef const GLFWLayerShellConfig* (*glfwGetLayerShellConfig_func)(GLFWwindow*);\nGFW_EXTERN glfwGetLayerShellConfig_func glfwGetLayerShellConfig_impl;\n#define glfwGetLayerShellConfig glfwGetLayerShellConfig_impl\n\ntypedef bool (*glfwSetLayerShellConfig_func)(GLFWwindow*, const GLFWLayerShellConfig*);\nGFW_EXTERN glfwSetLayerShellConfig_func glfwSetLayerShellConfig_impl;\n#define glfwSetLayerShellConfig glfwSetLayerShellConfig_impl\n\ntypedef void (*glfwDestroyWindow_func)(GLFWwindow*);\nGFW_EXTERN glfwDestroyWindow_func glfwDestroyWindow_impl;\n#define glfwDestroyWindow glfwDestroyWindow_impl\n\ntypedef int (*glfwWindowShouldClose_func)(GLFWwindow*);\nGFW_EXTERN glfwWindowShouldClose_func glfwWindowShouldClose_impl;\n#define glfwWindowShouldClose glfwWindowShouldClose_impl\n\ntypedef void (*glfwSetWindowShouldClose_func)(GLFWwindow*, int);\nGFW_EXTERN glfwSetWindowShouldClose_func glfwSetWindowShouldClose_impl;\n#define glfwSetWindowShouldClose glfwSetWindowShouldClose_impl\n\ntypedef void (*glfwSetWindowTitle_func)(GLFWwindow*, const char*);\nGFW_EXTERN glfwSetWindowTitle_func glfwSetWindowTitle_impl;\n#define glfwSetWindowTitle glfwSetWindowTitle_impl\n\ntypedef void (*glfwSetWindowIcon_func)(GLFWwindow*, int, const GLFWimage*);\nGFW_EXTERN glfwSetWindowIcon_func glfwSetWindowIcon_impl;\n#define glfwSetWindowIcon glfwSetWindowIcon_impl\n\ntypedef void (*glfwGetWindowPos_func)(GLFWwindow*, int*, int*);\nGFW_EXTERN glfwGetWindowPos_func glfwGetWindowPos_impl;\n#define glfwGetWindowPos glfwGetWindowPos_impl\n\ntypedef void (*glfwSetWindowPos_func)(GLFWwindow*, int, int);\nGFW_EXTERN glfwSetWindowPos_func glfwSetWindowPos_impl;\n#define glfwSetWindowPos glfwSetWindowPos_impl\n\ntypedef void (*glfwGetWindowSize_func)(GLFWwindow*, int*, int*);\nGFW_EXTERN glfwGetWindowSize_func glfwGetWindowSize_impl;\n#define glfwGetWindowSize glfwGetWindowSize_impl\n\ntypedef void (*glfwSetWindowSizeLimits_func)(GLFWwindow*, int, int, int, int);\nGFW_EXTERN glfwSetWindowSizeLimits_func glfwSetWindowSizeLimits_impl;\n#define glfwSetWindowSizeLimits glfwSetWindowSizeLimits_impl\n\ntypedef void (*glfwSetWindowSizeIncrements_func)(GLFWwindow*, int, int);\nGFW_EXTERN glfwSetWindowSizeIncrements_func glfwSetWindowSizeIncrements_impl;\n#define glfwSetWindowSizeIncrements glfwSetWindowSizeIncrements_impl\n\ntypedef void (*glfwSetWindowAspectRatio_func)(GLFWwindow*, int, int);\nGFW_EXTERN glfwSetWindowAspectRatio_func glfwSetWindowAspectRatio_impl;\n#define glfwSetWindowAspectRatio glfwSetWindowAspectRatio_impl\n\ntypedef void (*glfwSetWindowSize_func)(GLFWwindow*, int, int);\nGFW_EXTERN glfwSetWindowSize_func glfwSetWindowSize_impl;\n#define glfwSetWindowSize glfwSetWindowSize_impl\n\ntypedef void (*glfwGetFramebufferSize_func)(GLFWwindow*, int*, int*);\nGFW_EXTERN glfwGetFramebufferSize_func glfwGetFramebufferSize_impl;\n#define glfwGetFramebufferSize glfwGetFramebufferSize_impl\n\ntypedef void (*glfwGetWindowFrameSize_func)(GLFWwindow*, int*, int*, int*, int*);\nGFW_EXTERN glfwGetWindowFrameSize_func glfwGetWindowFrameSize_impl;\n#define glfwGetWindowFrameSize glfwGetWindowFrameSize_impl\n\ntypedef void (*glfwGetWindowContentScale_func)(GLFWwindow*, float*, float*);\nGFW_EXTERN glfwGetWindowContentScale_func glfwGetWindowContentScale_impl;\n#define glfwGetWindowContentScale glfwGetWindowContentScale_impl\n\ntypedef monotonic_t (*glfwGetDoubleClickInterval_func)(GLFWwindow*);\nGFW_EXTERN glfwGetDoubleClickInterval_func glfwGetDoubleClickInterval_impl;\n#define glfwGetDoubleClickInterval glfwGetDoubleClickInterval_impl\n\ntypedef float (*glfwGetWindowOpacity_func)(GLFWwindow*);\nGFW_EXTERN glfwGetWindowOpacity_func glfwGetWindowOpacity_impl;\n#define glfwGetWindowOpacity glfwGetWindowOpacity_impl\n\ntypedef void (*glfwSetWindowOpacity_func)(GLFWwindow*, float);\nGFW_EXTERN glfwSetWindowOpacity_func glfwSetWindowOpacity_impl;\n#define glfwSetWindowOpacity glfwSetWindowOpacity_impl\n\ntypedef void (*glfwIconifyWindow_func)(GLFWwindow*);\nGFW_EXTERN glfwIconifyWindow_func glfwIconifyWindow_impl;\n#define glfwIconifyWindow glfwIconifyWindow_impl\n\ntypedef void (*glfwRestoreWindow_func)(GLFWwindow*);\nGFW_EXTERN glfwRestoreWindow_func glfwRestoreWindow_impl;\n#define glfwRestoreWindow glfwRestoreWindow_impl\n\ntypedef void (*glfwMaximizeWindow_func)(GLFWwindow*);\nGFW_EXTERN glfwMaximizeWindow_func glfwMaximizeWindow_impl;\n#define glfwMaximizeWindow glfwMaximizeWindow_impl\n\ntypedef void (*glfwShowWindow_func)(GLFWwindow*, bool);\nGFW_EXTERN glfwShowWindow_func glfwShowWindow_impl;\n#define glfwShowWindow glfwShowWindow_impl\n\ntypedef void (*glfwHideWindow_func)(GLFWwindow*);\nGFW_EXTERN glfwHideWindow_func glfwHideWindow_impl;\n#define glfwHideWindow glfwHideWindow_impl\n\ntypedef void (*glfwFocusWindow_func)(GLFWwindow*);\nGFW_EXTERN glfwFocusWindow_func glfwFocusWindow_impl;\n#define glfwFocusWindow glfwFocusWindow_impl\n\ntypedef void (*glfwRequestWindowAttention_func)(GLFWwindow*);\nGFW_EXTERN glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;\n#define glfwRequestWindowAttention glfwRequestWindowAttention_impl\n\ntypedef int (*glfwWindowBell_func)(GLFWwindow*);\nGFW_EXTERN glfwWindowBell_func glfwWindowBell_impl;\n#define glfwWindowBell glfwWindowBell_impl\n\ntypedef GLFWmonitor* (*glfwGetWindowMonitor_func)(GLFWwindow*);\nGFW_EXTERN glfwGetWindowMonitor_func glfwGetWindowMonitor_impl;\n#define glfwGetWindowMonitor glfwGetWindowMonitor_impl\n\ntypedef void (*glfwSetWindowMonitor_func)(GLFWwindow*, GLFWmonitor*, int, int, int, int, int);\nGFW_EXTERN glfwSetWindowMonitor_func glfwSetWindowMonitor_impl;\n#define glfwSetWindowMonitor glfwSetWindowMonitor_impl\n\ntypedef int (*glfwGetWindowAttrib_func)(GLFWwindow*, int);\nGFW_EXTERN glfwGetWindowAttrib_func glfwGetWindowAttrib_impl;\n#define glfwGetWindowAttrib glfwGetWindowAttrib_impl\n\ntypedef void (*glfwSetWindowAttrib_func)(GLFWwindow*, int, int);\nGFW_EXTERN glfwSetWindowAttrib_func glfwSetWindowAttrib_impl;\n#define glfwSetWindowAttrib glfwSetWindowAttrib_impl\n\ntypedef int (*glfwSetWindowBlur_func)(GLFWwindow*, int);\nGFW_EXTERN glfwSetWindowBlur_func glfwSetWindowBlur_impl;\n#define glfwSetWindowBlur glfwSetWindowBlur_impl\n\ntypedef void (*glfwSetWindowUserPointer_func)(GLFWwindow*, void*);\nGFW_EXTERN glfwSetWindowUserPointer_func glfwSetWindowUserPointer_impl;\n#define glfwSetWindowUserPointer glfwSetWindowUserPointer_impl\n\ntypedef void* (*glfwGetWindowUserPointer_func)(GLFWwindow*);\nGFW_EXTERN glfwGetWindowUserPointer_func glfwGetWindowUserPointer_impl;\n#define glfwGetWindowUserPointer glfwGetWindowUserPointer_impl\n\ntypedef GLFWwindowposfun (*glfwSetWindowPosCallback_func)(GLFWwindow*, GLFWwindowposfun);\nGFW_EXTERN glfwSetWindowPosCallback_func glfwSetWindowPosCallback_impl;\n#define glfwSetWindowPosCallback glfwSetWindowPosCallback_impl\n\ntypedef GLFWwindowsizefun (*glfwSetWindowSizeCallback_func)(GLFWwindow*, GLFWwindowsizefun);\nGFW_EXTERN glfwSetWindowSizeCallback_func glfwSetWindowSizeCallback_impl;\n#define glfwSetWindowSizeCallback glfwSetWindowSizeCallback_impl\n\ntypedef GLFWwindowclosefun (*glfwSetWindowCloseCallback_func)(GLFWwindow*, GLFWwindowclosefun);\nGFW_EXTERN glfwSetWindowCloseCallback_func glfwSetWindowCloseCallback_impl;\n#define glfwSetWindowCloseCallback glfwSetWindowCloseCallback_impl\n\ntypedef GLFWapplicationclosefun (*glfwSetApplicationCloseCallback_func)(GLFWapplicationclosefun);\nGFW_EXTERN glfwSetApplicationCloseCallback_func glfwSetApplicationCloseCallback_impl;\n#define glfwSetApplicationCloseCallback glfwSetApplicationCloseCallback_impl\n\ntypedef GLFWsystemcolorthemechangefun (*glfwSetSystemColorThemeChangeCallback_func)(GLFWsystemcolorthemechangefun);\nGFW_EXTERN glfwSetSystemColorThemeChangeCallback_func glfwSetSystemColorThemeChangeCallback_impl;\n#define glfwSetSystemColorThemeChangeCallback glfwSetSystemColorThemeChangeCallback_impl\n\ntypedef GLFWclipboardlostfun (*glfwSetClipboardLostCallback_func)(GLFWclipboardlostfun);\nGFW_EXTERN glfwSetClipboardLostCallback_func glfwSetClipboardLostCallback_impl;\n#define glfwSetClipboardLostCallback glfwSetClipboardLostCallback_impl\n\ntypedef GLFWColorScheme (*glfwGetCurrentSystemColorTheme_func)(bool);\nGFW_EXTERN glfwGetCurrentSystemColorTheme_func glfwGetCurrentSystemColorTheme_impl;\n#define glfwGetCurrentSystemColorTheme glfwGetCurrentSystemColorTheme_impl\n\ntypedef GLFWwindowrefreshfun (*glfwSetWindowRefreshCallback_func)(GLFWwindow*, GLFWwindowrefreshfun);\nGFW_EXTERN glfwSetWindowRefreshCallback_func glfwSetWindowRefreshCallback_impl;\n#define glfwSetWindowRefreshCallback glfwSetWindowRefreshCallback_impl\n\ntypedef GLFWwindowfocusfun (*glfwSetWindowFocusCallback_func)(GLFWwindow*, GLFWwindowfocusfun);\nGFW_EXTERN glfwSetWindowFocusCallback_func glfwSetWindowFocusCallback_impl;\n#define glfwSetWindowFocusCallback glfwSetWindowFocusCallback_impl\n\ntypedef GLFWwindowocclusionfun (*glfwSetWindowOcclusionCallback_func)(GLFWwindow*, GLFWwindowocclusionfun);\nGFW_EXTERN glfwSetWindowOcclusionCallback_func glfwSetWindowOcclusionCallback_impl;\n#define glfwSetWindowOcclusionCallback glfwSetWindowOcclusionCallback_impl\n\ntypedef GLFWwindowiconifyfun (*glfwSetWindowIconifyCallback_func)(GLFWwindow*, GLFWwindowiconifyfun);\nGFW_EXTERN glfwSetWindowIconifyCallback_func glfwSetWindowIconifyCallback_impl;\n#define glfwSetWindowIconifyCallback glfwSetWindowIconifyCallback_impl\n\ntypedef GLFWwindowmaximizefun (*glfwSetWindowMaximizeCallback_func)(GLFWwindow*, GLFWwindowmaximizefun);\nGFW_EXTERN glfwSetWindowMaximizeCallback_func glfwSetWindowMaximizeCallback_impl;\n#define glfwSetWindowMaximizeCallback glfwSetWindowMaximizeCallback_impl\n\ntypedef GLFWframebuffersizefun (*glfwSetFramebufferSizeCallback_func)(GLFWwindow*, GLFWframebuffersizefun);\nGFW_EXTERN glfwSetFramebufferSizeCallback_func glfwSetFramebufferSizeCallback_impl;\n#define glfwSetFramebufferSizeCallback glfwSetFramebufferSizeCallback_impl\n\ntypedef GLFWwindowcontentscalefun (*glfwSetWindowContentScaleCallback_func)(GLFWwindow*, GLFWwindowcontentscalefun);\nGFW_EXTERN glfwSetWindowContentScaleCallback_func glfwSetWindowContentScaleCallback_impl;\n#define glfwSetWindowContentScaleCallback glfwSetWindowContentScaleCallback_impl\n\ntypedef void (*glfwPostEmptyEvent_func)(void);\nGFW_EXTERN glfwPostEmptyEvent_func glfwPostEmptyEvent_impl;\n#define glfwPostEmptyEvent glfwPostEmptyEvent_impl\n\ntypedef bool (*glfwGetIgnoreOSKeyboardProcessing_func)(void);\nGFW_EXTERN glfwGetIgnoreOSKeyboardProcessing_func glfwGetIgnoreOSKeyboardProcessing_impl;\n#define glfwGetIgnoreOSKeyboardProcessing glfwGetIgnoreOSKeyboardProcessing_impl\n\ntypedef void (*glfwSetIgnoreOSKeyboardProcessing_func)(bool);\nGFW_EXTERN glfwSetIgnoreOSKeyboardProcessing_func glfwSetIgnoreOSKeyboardProcessing_impl;\n#define glfwSetIgnoreOSKeyboardProcessing glfwSetIgnoreOSKeyboardProcessing_impl\n\ntypedef bool (*glfwGrabKeyboard_func)(int);\nGFW_EXTERN glfwGrabKeyboard_func glfwGrabKeyboard_impl;\n#define glfwGrabKeyboard glfwGrabKeyboard_impl\n\ntypedef int (*glfwGetInputMode_func)(GLFWwindow*, int);\nGFW_EXTERN glfwGetInputMode_func glfwGetInputMode_impl;\n#define glfwGetInputMode glfwGetInputMode_impl\n\ntypedef void (*glfwSetInputMode_func)(GLFWwindow*, int, int);\nGFW_EXTERN glfwSetInputMode_func glfwSetInputMode_impl;\n#define glfwSetInputMode glfwSetInputMode_impl\n\ntypedef int (*glfwRawMouseMotionSupported_func)(void);\nGFW_EXTERN glfwRawMouseMotionSupported_func glfwRawMouseMotionSupported_impl;\n#define glfwRawMouseMotionSupported glfwRawMouseMotionSupported_impl\n\ntypedef const char* (*glfwGetKeyName_func)(uint32_t, int);\nGFW_EXTERN glfwGetKeyName_func glfwGetKeyName_impl;\n#define glfwGetKeyName glfwGetKeyName_impl\n\ntypedef int (*glfwGetNativeKeyForKey_func)(uint32_t);\nGFW_EXTERN glfwGetNativeKeyForKey_func glfwGetNativeKeyForKey_impl;\n#define glfwGetNativeKeyForKey glfwGetNativeKeyForKey_impl\n\ntypedef GLFWKeyAction (*glfwGetKey_func)(GLFWwindow*, uint32_t);\nGFW_EXTERN glfwGetKey_func glfwGetKey_impl;\n#define glfwGetKey glfwGetKey_impl\n\ntypedef int (*glfwGetMouseButton_func)(GLFWwindow*, int);\nGFW_EXTERN glfwGetMouseButton_func glfwGetMouseButton_impl;\n#define glfwGetMouseButton glfwGetMouseButton_impl\n\ntypedef void (*glfwGetCursorPos_func)(GLFWwindow*, double*, double*);\nGFW_EXTERN glfwGetCursorPos_func glfwGetCursorPos_impl;\n#define glfwGetCursorPos glfwGetCursorPos_impl\n\ntypedef void (*glfwSetCursorPos_func)(GLFWwindow*, double, double);\nGFW_EXTERN glfwSetCursorPos_func glfwSetCursorPos_impl;\n#define glfwSetCursorPos glfwSetCursorPos_impl\n\ntypedef GLFWcursor* (*glfwCreateCursor_func)(const GLFWimage*, int, int, int);\nGFW_EXTERN glfwCreateCursor_func glfwCreateCursor_impl;\n#define glfwCreateCursor glfwCreateCursor_impl\n\ntypedef GLFWcursor* (*glfwCreateStandardCursor_func)(GLFWCursorShape);\nGFW_EXTERN glfwCreateStandardCursor_func glfwCreateStandardCursor_impl;\n#define glfwCreateStandardCursor glfwCreateStandardCursor_impl\n\ntypedef void (*glfwDestroyCursor_func)(GLFWcursor*);\nGFW_EXTERN glfwDestroyCursor_func glfwDestroyCursor_impl;\n#define glfwDestroyCursor glfwDestroyCursor_impl\n\ntypedef void (*glfwSetCursor_func)(GLFWwindow*, GLFWcursor*);\nGFW_EXTERN glfwSetCursor_func glfwSetCursor_impl;\n#define glfwSetCursor glfwSetCursor_impl\n\ntypedef GLFWkeyboardfun (*glfwSetKeyboardCallback_func)(GLFWwindow*, GLFWkeyboardfun);\nGFW_EXTERN glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl;\n#define glfwSetKeyboardCallback glfwSetKeyboardCallback_impl\n\ntypedef void (*glfwUpdateIMEState_func)(GLFWwindow*, const GLFWIMEUpdateEvent*);\nGFW_EXTERN glfwUpdateIMEState_func glfwUpdateIMEState_impl;\n#define glfwUpdateIMEState glfwUpdateIMEState_impl\n\ntypedef GLFWmousebuttonfun (*glfwSetMouseButtonCallback_func)(GLFWwindow*, GLFWmousebuttonfun);\nGFW_EXTERN glfwSetMouseButtonCallback_func glfwSetMouseButtonCallback_impl;\n#define glfwSetMouseButtonCallback glfwSetMouseButtonCallback_impl\n\ntypedef GLFWcursorposfun (*glfwSetCursorPosCallback_func)(GLFWwindow*, GLFWcursorposfun);\nGFW_EXTERN glfwSetCursorPosCallback_func glfwSetCursorPosCallback_impl;\n#define glfwSetCursorPosCallback glfwSetCursorPosCallback_impl\n\ntypedef GLFWcursorenterfun (*glfwSetCursorEnterCallback_func)(GLFWwindow*, GLFWcursorenterfun);\nGFW_EXTERN glfwSetCursorEnterCallback_func glfwSetCursorEnterCallback_impl;\n#define glfwSetCursorEnterCallback glfwSetCursorEnterCallback_impl\n\ntypedef GLFWscrollfun (*glfwSetScrollCallback_func)(GLFWwindow*, GLFWscrollfun);\nGFW_EXTERN glfwSetScrollCallback_func glfwSetScrollCallback_impl;\n#define glfwSetScrollCallback glfwSetScrollCallback_impl\n\ntypedef GLFWliveresizefun (*glfwSetLiveResizeCallback_func)(GLFWwindow*, GLFWliveresizefun);\nGFW_EXTERN glfwSetLiveResizeCallback_func glfwSetLiveResizeCallback_impl;\n#define glfwSetLiveResizeCallback glfwSetLiveResizeCallback_impl\n\ntypedef GLFWdropeventfun (*glfwSetDropEventCallback_func)(GLFWwindow*, GLFWdropeventfun);\nGFW_EXTERN glfwSetDropEventCallback_func glfwSetDropEventCallback_impl;\n#define glfwSetDropEventCallback glfwSetDropEventCallback_impl\n\ntypedef int (*glfwRequestDropData_func)(GLFWwindow*, const char*);\nGFW_EXTERN glfwRequestDropData_func glfwRequestDropData_impl;\n#define glfwRequestDropData glfwRequestDropData_impl\n\ntypedef void (*glfwEndDrop_func)(GLFWwindow*, GLFWDragOperationType);\nGFW_EXTERN glfwEndDrop_func glfwEndDrop_impl;\n#define glfwEndDrop glfwEndDrop_impl\n\ntypedef GLFWdragsourcefun (*glfwSetDragSourceCallback_func)(GLFWwindow*, GLFWdragsourcefun);\nGFW_EXTERN glfwSetDragSourceCallback_func glfwSetDragSourceCallback_impl;\n#define glfwSetDragSourceCallback glfwSetDragSourceCallback_impl\n\ntypedef int (*glfwStartDrag_func)(GLFWwindow*, const GLFWDragSourceItem*, size_t, const GLFWimage*, int, bool);\nGFW_EXTERN glfwStartDrag_func glfwStartDrag_impl;\n#define glfwStartDrag glfwStartDrag_impl\n\ntypedef int (*glfwJoystickPresent_func)(int);\nGFW_EXTERN glfwJoystickPresent_func glfwJoystickPresent_impl;\n#define glfwJoystickPresent glfwJoystickPresent_impl\n\ntypedef const float* (*glfwGetJoystickAxes_func)(int, int*);\nGFW_EXTERN glfwGetJoystickAxes_func glfwGetJoystickAxes_impl;\n#define glfwGetJoystickAxes glfwGetJoystickAxes_impl\n\ntypedef const unsigned char* (*glfwGetJoystickButtons_func)(int, int*);\nGFW_EXTERN glfwGetJoystickButtons_func glfwGetJoystickButtons_impl;\n#define glfwGetJoystickButtons glfwGetJoystickButtons_impl\n\ntypedef const unsigned char* (*glfwGetJoystickHats_func)(int, int*);\nGFW_EXTERN glfwGetJoystickHats_func glfwGetJoystickHats_impl;\n#define glfwGetJoystickHats glfwGetJoystickHats_impl\n\ntypedef const char* (*glfwGetJoystickName_func)(int);\nGFW_EXTERN glfwGetJoystickName_func glfwGetJoystickName_impl;\n#define glfwGetJoystickName glfwGetJoystickName_impl\n\ntypedef const char* (*glfwGetJoystickGUID_func)(int);\nGFW_EXTERN glfwGetJoystickGUID_func glfwGetJoystickGUID_impl;\n#define glfwGetJoystickGUID glfwGetJoystickGUID_impl\n\ntypedef void (*glfwSetJoystickUserPointer_func)(int, void*);\nGFW_EXTERN glfwSetJoystickUserPointer_func glfwSetJoystickUserPointer_impl;\n#define glfwSetJoystickUserPointer glfwSetJoystickUserPointer_impl\n\ntypedef void* (*glfwGetJoystickUserPointer_func)(int);\nGFW_EXTERN glfwGetJoystickUserPointer_func glfwGetJoystickUserPointer_impl;\n#define glfwGetJoystickUserPointer glfwGetJoystickUserPointer_impl\n\ntypedef int (*glfwJoystickIsGamepad_func)(int);\nGFW_EXTERN glfwJoystickIsGamepad_func glfwJoystickIsGamepad_impl;\n#define glfwJoystickIsGamepad glfwJoystickIsGamepad_impl\n\ntypedef GLFWjoystickfun (*glfwSetJoystickCallback_func)(GLFWjoystickfun);\nGFW_EXTERN glfwSetJoystickCallback_func glfwSetJoystickCallback_impl;\n#define glfwSetJoystickCallback glfwSetJoystickCallback_impl\n\ntypedef int (*glfwUpdateGamepadMappings_func)(const char*);\nGFW_EXTERN glfwUpdateGamepadMappings_func glfwUpdateGamepadMappings_impl;\n#define glfwUpdateGamepadMappings glfwUpdateGamepadMappings_impl\n\ntypedef const char* (*glfwGetGamepadName_func)(int);\nGFW_EXTERN glfwGetGamepadName_func glfwGetGamepadName_impl;\n#define glfwGetGamepadName glfwGetGamepadName_impl\n\ntypedef int (*glfwGetGamepadState_func)(int, GLFWgamepadstate*);\nGFW_EXTERN glfwGetGamepadState_func glfwGetGamepadState_impl;\n#define glfwGetGamepadState glfwGetGamepadState_impl\n\ntypedef void (*glfwSetClipboardDataTypes_func)(GLFWClipboardType, const char* const*, size_t, GLFWclipboarditerfun);\nGFW_EXTERN glfwSetClipboardDataTypes_func glfwSetClipboardDataTypes_impl;\n#define glfwSetClipboardDataTypes glfwSetClipboardDataTypes_impl\n\ntypedef void (*glfwGetClipboard_func)(GLFWClipboardType, const char*, GLFWclipboardwritedatafun, void*);\nGFW_EXTERN glfwGetClipboard_func glfwGetClipboard_impl;\n#define glfwGetClipboard glfwGetClipboard_impl\n\ntypedef monotonic_t (*glfwGetTime_func)(void);\nGFW_EXTERN glfwGetTime_func glfwGetTime_impl;\n#define glfwGetTime glfwGetTime_impl\n\ntypedef void (*glfwMakeContextCurrent_func)(GLFWwindow*);\nGFW_EXTERN glfwMakeContextCurrent_func glfwMakeContextCurrent_impl;\n#define glfwMakeContextCurrent glfwMakeContextCurrent_impl\n\ntypedef GLFWwindow* (*glfwGetCurrentContext_func)(void);\nGFW_EXTERN glfwGetCurrentContext_func glfwGetCurrentContext_impl;\n#define glfwGetCurrentContext glfwGetCurrentContext_impl\n\ntypedef void (*glfwSwapBuffers_func)(GLFWwindow*);\nGFW_EXTERN glfwSwapBuffers_func glfwSwapBuffers_impl;\n#define glfwSwapBuffers glfwSwapBuffers_impl\n\ntypedef void (*glfwSwapInterval_func)(int);\nGFW_EXTERN glfwSwapInterval_func glfwSwapInterval_impl;\n#define glfwSwapInterval glfwSwapInterval_impl\n\ntypedef int (*glfwExtensionSupported_func)(const char*);\nGFW_EXTERN glfwExtensionSupported_func glfwExtensionSupported_impl;\n#define glfwExtensionSupported glfwExtensionSupported_impl\n\ntypedef GLFWglproc (*glfwGetProcAddress_func)(const char*);\nGFW_EXTERN glfwGetProcAddress_func glfwGetProcAddress_impl;\n#define glfwGetProcAddress glfwGetProcAddress_impl\n\ntypedef int (*glfwVulkanSupported_func)(void);\nGFW_EXTERN glfwVulkanSupported_func glfwVulkanSupported_impl;\n#define glfwVulkanSupported glfwVulkanSupported_impl\n\ntypedef const char** (*glfwGetRequiredInstanceExtensions_func)(uint32_t*);\nGFW_EXTERN glfwGetRequiredInstanceExtensions_func glfwGetRequiredInstanceExtensions_impl;\n#define glfwGetRequiredInstanceExtensions glfwGetRequiredInstanceExtensions_impl\n\ntypedef void* (*glfwGetCocoaWindow_func)(GLFWwindow*);\nGFW_EXTERN glfwGetCocoaWindow_func glfwGetCocoaWindow_impl;\n#define glfwGetCocoaWindow glfwGetCocoaWindow_impl\n\ntypedef void* (*glfwGetNSGLContext_func)(GLFWwindow*);\nGFW_EXTERN glfwGetNSGLContext_func glfwGetNSGLContext_impl;\n#define glfwGetNSGLContext glfwGetNSGLContext_impl\n\ntypedef uint32_t (*glfwGetCocoaMonitor_func)(GLFWmonitor*);\nGFW_EXTERN glfwGetCocoaMonitor_func glfwGetCocoaMonitor_impl;\n#define glfwGetCocoaMonitor glfwGetCocoaMonitor_impl\n\ntypedef GLFWcocoatextinputfilterfun (*glfwSetCocoaTextInputFilter_func)(GLFWwindow*, GLFWcocoatextinputfilterfun);\nGFW_EXTERN glfwSetCocoaTextInputFilter_func glfwSetCocoaTextInputFilter_impl;\n#define glfwSetCocoaTextInputFilter glfwSetCocoaTextInputFilter_impl\n\ntypedef GLFWhandleurlopen (*glfwSetCocoaURLOpenCallback_func)(GLFWhandleurlopen);\nGFW_EXTERN glfwSetCocoaURLOpenCallback_func glfwSetCocoaURLOpenCallback_impl;\n#define glfwSetCocoaURLOpenCallback glfwSetCocoaURLOpenCallback_impl\n\ntypedef GLFWcocoatogglefullscreenfun (*glfwSetCocoaToggleFullscreenIntercept_func)(GLFWwindow*, GLFWcocoatogglefullscreenfun);\nGFW_EXTERN glfwSetCocoaToggleFullscreenIntercept_func glfwSetCocoaToggleFullscreenIntercept_impl;\n#define glfwSetCocoaToggleFullscreenIntercept glfwSetCocoaToggleFullscreenIntercept_impl\n\ntypedef GLFWapplicationshouldhandlereopenfun (*glfwSetApplicationShouldHandleReopen_func)(GLFWapplicationshouldhandlereopenfun);\nGFW_EXTERN glfwSetApplicationShouldHandleReopen_func glfwSetApplicationShouldHandleReopen_impl;\n#define glfwSetApplicationShouldHandleReopen glfwSetApplicationShouldHandleReopen_impl\n\ntypedef GLFWapplicationwillfinishlaunchingfun (*glfwSetApplicationWillFinishLaunching_func)(GLFWapplicationwillfinishlaunchingfun);\nGFW_EXTERN glfwSetApplicationWillFinishLaunching_func glfwSetApplicationWillFinishLaunching_impl;\n#define glfwSetApplicationWillFinishLaunching glfwSetApplicationWillFinishLaunching_impl\n\ntypedef uint32_t (*glfwGetCocoaKeyEquivalent_func)(uint32_t, int, int*);\nGFW_EXTERN glfwGetCocoaKeyEquivalent_func glfwGetCocoaKeyEquivalent_impl;\n#define glfwGetCocoaKeyEquivalent glfwGetCocoaKeyEquivalent_impl\n\ntypedef void (*glfwCocoaRequestRenderFrame_func)(GLFWwindow*, GLFWcocoarenderframefun);\nGFW_EXTERN glfwCocoaRequestRenderFrame_func glfwCocoaRequestRenderFrame_impl;\n#define glfwCocoaRequestRenderFrame glfwCocoaRequestRenderFrame_impl\n\ntypedef bool (*glfwCocoaRecreateGLDrawable_func)(GLFWwindow*);\nGFW_EXTERN glfwCocoaRecreateGLDrawable_func glfwCocoaRecreateGLDrawable_impl;\n#define glfwCocoaRecreateGLDrawable glfwCocoaRecreateGLDrawable_impl\n\ntypedef GLFWcocoarenderframefun (*glfwCocoaSetWindowResizeCallback_func)(GLFWwindow*, GLFWcocoarenderframefun);\nGFW_EXTERN glfwCocoaSetWindowResizeCallback_func glfwCocoaSetWindowResizeCallback_impl;\n#define glfwCocoaSetWindowResizeCallback glfwCocoaSetWindowResizeCallback_impl\n\ntypedef void* (*glfwGetX11Display_func)(void);\nGFW_EXTERN glfwGetX11Display_func glfwGetX11Display_impl;\n#define glfwGetX11Display glfwGetX11Display_impl\n\ntypedef unsigned long (*glfwGetX11Window_func)(GLFWwindow*);\nGFW_EXTERN glfwGetX11Window_func glfwGetX11Window_impl;\n#define glfwGetX11Window glfwGetX11Window_impl\n\ntypedef void (*glfwSetPrimarySelectionString_func)(GLFWwindow*, const char*);\nGFW_EXTERN glfwSetPrimarySelectionString_func glfwSetPrimarySelectionString_impl;\n#define glfwSetPrimarySelectionString glfwSetPrimarySelectionString_impl\n\ntypedef void (*glfwCocoaCycleThroughOSWindows_func)(bool);\nGFW_EXTERN glfwCocoaCycleThroughOSWindows_func glfwCocoaCycleThroughOSWindows_impl;\n#define glfwCocoaCycleThroughOSWindows glfwCocoaCycleThroughOSWindows_impl\n\ntypedef void (*glfwCocoaSetWindowChrome_func)(GLFWwindow*, unsigned int, bool, unsigned int, int, unsigned int, bool, int, float, bool);\nGFW_EXTERN glfwCocoaSetWindowChrome_func glfwCocoaSetWindowChrome_impl;\n#define glfwCocoaSetWindowChrome glfwCocoaSetWindowChrome_impl\n\ntypedef void (*glfwCocoaRegisterMIMETypes_func)(GLFWwindow*, const char**, size_t);\nGFW_EXTERN glfwCocoaRegisterMIMETypes_func glfwCocoaRegisterMIMETypes_impl;\n#define glfwCocoaRegisterMIMETypes glfwCocoaRegisterMIMETypes_impl\n\ntypedef const char* (*glfwGetPrimarySelectionString_func)(GLFWwindow*);\nGFW_EXTERN glfwGetPrimarySelectionString_func glfwGetPrimarySelectionString_impl;\n#define glfwGetPrimarySelectionString glfwGetPrimarySelectionString_impl\n\ntypedef int (*glfwGetNativeKeyForName_func)(const char*, int);\nGFW_EXTERN glfwGetNativeKeyForName_func glfwGetNativeKeyForName_impl;\n#define glfwGetNativeKeyForName glfwGetNativeKeyForName_impl\n\ntypedef void (*glfwRequestWaylandFrameEvent_func)(GLFWwindow*, unsigned long long, GLFWwaylandframecallbackfunc);\nGFW_EXTERN glfwRequestWaylandFrameEvent_func glfwRequestWaylandFrameEvent_impl;\n#define glfwRequestWaylandFrameEvent glfwRequestWaylandFrameEvent_impl\n\ntypedef void (*glfwWaylandActivateWindow_func)(GLFWwindow*, const char*);\nGFW_EXTERN glfwWaylandActivateWindow_func glfwWaylandActivateWindow_impl;\n#define glfwWaylandActivateWindow glfwWaylandActivateWindow_impl\n\ntypedef const char* (*glfwWaylandMissingCapabilities_func)(void);\nGFW_EXTERN glfwWaylandMissingCapabilities_func glfwWaylandMissingCapabilities_impl;\n#define glfwWaylandMissingCapabilities glfwWaylandMissingCapabilities_impl\n\ntypedef void (*glfwWaylandRunWithActivationToken_func)(GLFWwindow*, GLFWactivationcallback, void*);\nGFW_EXTERN glfwWaylandRunWithActivationToken_func glfwWaylandRunWithActivationToken_impl;\n#define glfwWaylandRunWithActivationToken glfwWaylandRunWithActivationToken_impl\n\ntypedef bool (*glfwWaylandSetTitlebarColor_func)(GLFWwindow*, uint32_t, bool);\nGFW_EXTERN glfwWaylandSetTitlebarColor_func glfwWaylandSetTitlebarColor_impl;\n#define glfwWaylandSetTitlebarColor glfwWaylandSetTitlebarColor_impl\n\ntypedef void (*glfwWaylandSetTitlebarHidden_func)(GLFWwindow*, bool);\nGFW_EXTERN glfwWaylandSetTitlebarHidden_func glfwWaylandSetTitlebarHidden_impl;\n#define glfwWaylandSetTitlebarHidden glfwWaylandSetTitlebarHidden_impl\n\ntypedef void (*glfwWaylandRedrawCSDWindowTitle_func)(GLFWwindow*);\nGFW_EXTERN glfwWaylandRedrawCSDWindowTitle_func glfwWaylandRedrawCSDWindowTitle_impl;\n#define glfwWaylandRedrawCSDWindowTitle glfwWaylandRedrawCSDWindowTitle_impl\n\ntypedef bool (*glfwWaylandIsWindowFullyCreated_func)(GLFWwindow*);\nGFW_EXTERN glfwWaylandIsWindowFullyCreated_func glfwWaylandIsWindowFullyCreated_impl;\n#define glfwWaylandIsWindowFullyCreated glfwWaylandIsWindowFullyCreated_impl\n\ntypedef bool (*glfwWaylandBeep_func)(GLFWwindow*);\nGFW_EXTERN glfwWaylandBeep_func glfwWaylandBeep_impl;\n#define glfwWaylandBeep glfwWaylandBeep_impl\n\ntypedef pid_t (*glfwWaylandCompositorPID_func)(void);\nGFW_EXTERN glfwWaylandCompositorPID_func glfwWaylandCompositorPID_impl;\n#define glfwWaylandCompositorPID glfwWaylandCompositorPID_impl\n\ntypedef void (*glfwConfigureMomentumScroller_func)(double, double, double, unsigned);\nGFW_EXTERN glfwConfigureMomentumScroller_func glfwConfigureMomentumScroller_impl;\n#define glfwConfigureMomentumScroller glfwConfigureMomentumScroller_impl\n\ntypedef unsigned long long (*glfwDBusUserNotify_func)(const GLFWDBUSNotificationData*, GLFWDBusnotificationcreatedfun, void*);\nGFW_EXTERN glfwDBusUserNotify_func glfwDBusUserNotify_impl;\n#define glfwDBusUserNotify glfwDBusUserNotify_impl\n\ntypedef void (*glfwDBusSetUserNotificationHandler_func)(GLFWDBusnotificationactivatedfun);\nGFW_EXTERN glfwDBusSetUserNotificationHandler_func glfwDBusSetUserNotificationHandler_impl;\n#define glfwDBusSetUserNotificationHandler glfwDBusSetUserNotificationHandler_impl\n\ntypedef int (*glfwSetX11LaunchCommand_func)(GLFWwindow*, char**, int);\nGFW_EXTERN glfwSetX11LaunchCommand_func glfwSetX11LaunchCommand_impl;\n#define glfwSetX11LaunchCommand glfwSetX11LaunchCommand_impl\n\nconst char* load_glfw(const char* path);\n"
  },
  {
    "path": "kitty/glfw.c",
    "content": "/*\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"state.h\"\n#include \"cleanup.h\"\n#include \"monotonic.h\"\n#include \"charsets.h\"\n#include \"control-codes.h\"\n#include <structmember.h>\n#include \"glfw-wrapper.h\"\n#ifdef __APPLE__\n#include \"cocoa_window.h\"\n#else\n#include \"freetype_render_ui_text.h\"\n#endif\n#define debug debug_rendering\n\ntypedef struct mouse_cursor {\n    GLFWcursor *glfw;\n    bool initialized, is_custom;\n} mouse_cursor;\n\nstatic mouse_cursor cursors[GLFW_INVALID_CURSOR+1] = {0};\n\nstatic void\napply_swap_interval(int val) {\n    (void)val;\n#ifndef __APPLE__\n    if (val < 0) val = OPT(sync_to_monitor) && !global_state.is_wayland ? 1 : 0;\n    glfwSwapInterval(val);\n#endif\n}\n\nvoid\nget_platform_dependent_config_values(void *glfw_window) {\n    if (OPT(click_interval) < 0) OPT(click_interval) = glfwGetDoubleClickInterval(glfw_window);\n    if (OPT(cursor_blink_interval) < 0) {\n        OPT(cursor_blink_interval) = ms_to_monotonic_t(500ll);\n#ifdef __APPLE__\n        monotonic_t cbi = cocoa_cursor_blink_interval();\n        if (cbi >= 0) OPT(cursor_blink_interval) = cbi / 2;\n#endif\n    }\n}\n\nstatic const char*\nappearance_name(GLFWColorScheme appearance) {\n    const char *which = NULL;\n    switch (appearance) {\n        case GLFW_COLOR_SCHEME_NO_PREFERENCE: which = \"no_preference\"; break;\n        case GLFW_COLOR_SCHEME_DARK: which = \"dark\"; break;\n        case GLFW_COLOR_SCHEME_LIGHT: which = \"light\"; break;\n    }\n    return which;\n}\n\nstatic void\non_system_color_scheme_change(GLFWColorScheme appearance, bool is_initial_value) {\n    const char *which = appearance_name(appearance);\n    debug(\"system color-scheme changed to: %s is_initial_value: %d\\n\", which, is_initial_value);\n    call_boss(on_system_color_scheme_change, \"sO\", which, is_initial_value ? Py_True : Py_False);\n}\n\nstatic void\non_clipboard_lost(GLFWClipboardType which) {\n    call_boss(on_clipboard_lost, \"s\", which == GLFW_CLIPBOARD ? \"clipboard\" : \"primary\");\n}\n\nstatic bool\nis_continuation_byte(unsigned char byte) {\n    return (byte & 0xC0) == 0x80; // Continuation bytes have the form 10xxxxxx\n}\n\nstatic int\nutf8_sequence_length(unsigned char byte) {\n    if ((byte & 0x80) == 0) return 1; // 0xxxxxxx: Single-byte ASCII\n    if ((byte & 0xE0) == 0xC0) return 2; // 110xxxxx: Two-byte sequence\n    if ((byte & 0xF0) == 0xE0) return 3; // 1110xxxx: Three-byte sequence\n    if ((byte & 0xF8) == 0xF0) return 4; // 11110xxx: Four-byte sequence\n    return -1; // Invalid first byte\n}\n\n// Function to remove invalid UTF-8 bytes from the end of a string\nstatic void\nremove_invalid_utf8_from_end(char *str, size_t len) {\n    if (!len) return;\n    // Start from the end of the string and move backward\n    size_t i = len - 1;\n    while (i > 0) {\n        if (is_continuation_byte((unsigned char)str[i])) {\n            // Continue backward to find the start of the potential UTF-8 sequence\n            size_t start = i;\n            while (start > 0 && is_continuation_byte((unsigned char)str[start])) start--;\n            // Check if the sequence is valid\n            int seq_len = utf8_sequence_length((unsigned char)str[start]);\n            if (seq_len > 0 && start + seq_len == len) return; // Valid sequence found, stop trimming\n            // Invalid sequence, trim it\n            str[start] = '\\0';\n            len = start;\n            i = start - 1;\n        } else {\n            // Not a continuation byte, check if it's a valid start byte\n            int seq_len = utf8_sequence_length((unsigned char)str[i]);\n            if (seq_len > 0 && i + seq_len == len) return; // Valid sequence found, stop trimming\n            // Invalid byte, trim it\n            str[i] = '\\0';\n            len = i;\n            i--;\n        }\n    }\n    // Handle the case where the entire string is invalid\n    if (utf8_sequence_length((unsigned char)str[0]) < 0) str[0] = '\\0';\n}\n\nstatic void\nstrip_csi_(const char *title, char *buf, size_t bufsz) {\n    enum { NORMAL, IN_ESC, IN_CSI} state = NORMAL;\n    char *dest = buf, *last = &buf[bufsz-1];\n    *dest = 0; *last = 0;\n\n    for (; *title && dest < last; title++) {\n        const unsigned char ch = *title;\n        switch (state) {\n            case NORMAL: {\n                if (ch == 0x1b) { state = IN_ESC; }\n                else *(dest++) = ch;\n            } break;\n            case IN_ESC: {\n                if (ch == '[') { state = IN_CSI; }\n                else {\n                    if (ch >= ' ' && ch != DEL) *(dest++) = ch;\n                    state = NORMAL;\n                }\n            } break;\n            case IN_CSI: {\n                if (!(('0' <= ch && ch <= '9') || ch == ';' || ch == ':')) {\n                    if (ch > DEL) *(dest++) = ch;  // UTF-8 multibyte\n                    state = NORMAL;\n                }\n            } break;\n        }\n    }\n    *dest = 0;\n    remove_invalid_utf8_from_end(buf, dest - buf);\n}\n\n\nvoid\nupdate_menu_bar_title(PyObject *title UNUSED) {\n#ifdef __APPLE__\n    static char buf[2048];\n    strip_csi_(PyUnicode_AsUTF8(title), buf, arraysz(buf));\n    RAII_PyObject(stitle, PyUnicode_FromString(buf));\n    if (stitle) cocoa_update_menu_bar_title(stitle);\n    else PyErr_Print();\n#endif\n}\n\n\nvoid\nrequest_tick_callback(void) {\n    glfwPostEmptyEvent();\n}\n\nstatic void\nmin_size_for_os_window(OSWindow *window, int *min_width, int *min_height) {\n    *min_width = MAX(8u, window->fonts_data->fcm.cell_width + 1);\n    *min_height = MAX(8u, window->fonts_data->fcm.cell_height + 1);\n}\n\n\nstatic void get_window_dpi(GLFWwindow *w, double *x, double *y);\nstatic void get_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xdpi, double *ydpi);\n\nstatic bool\nset_layer_shell_config_for(OSWindow *w, GLFWLayerShellConfig *lsc) {\n    if (lsc) {\n        lsc->related.background_opacity = effective_os_window_alpha(w);\n        lsc->related.background_blur = OPT(background_blur);\n        lsc->related.color_space = OPT(macos_colorspace);\n        w->hide_on_focus_loss = lsc->hide_on_focus_loss;\n    }\n    return glfwSetLayerShellConfig(w->handle, lsc);\n}\n\nvoid\nupdate_os_window_viewport(OSWindow *window, bool notify_boss) {\n    int w, h, fw, fh;\n    glfwGetFramebufferSize(window->handle, &fw, &fh);\n    glfwGetWindowSize(window->handle, &w, &h);\n    double xdpi = window->fonts_data->logical_dpi_x, ydpi = window->fonts_data->logical_dpi_y, new_xdpi, new_ydpi;\n    float xscale, yscale;\n    get_window_content_scale(window->handle, &xscale, &yscale, &new_xdpi, &new_ydpi);\n\n    if (fw == window->viewport_width && fh == window->viewport_height && w == window->window_width && h == window->window_height && xdpi == new_xdpi && ydpi == new_ydpi) {\n        return; // no change, ignore\n    }\n    int min_width, min_height; min_size_for_os_window(window, &min_width, &min_height);\n    window->viewport_resized_at = monotonic();\n    if (w <= 0 || h <= 0 || fw < min_width || fh < min_height || (xscale >=1 && fw < w) || (yscale >= 1 && fh < h)) {\n        log_error(\"Invalid geometry ignored: framebuffer: %dx%d window: %dx%d scale: %f %f\\n\", fw, fh, w, h, xscale, yscale);\n        if (!window->viewport_updated_at_least_once) {\n            window->viewport_width = min_width; window->viewport_height = min_height;\n            window->window_width = min_width; window->window_height = min_height;\n            window->viewport_x_ratio = 1; window->viewport_y_ratio = 1;\n            window->viewport_size_dirty = true;\n            if (notify_boss) {\n                call_boss(on_window_resize, \"KiiO\", window->id, window->viewport_width, window->viewport_height, Py_False);\n            }\n        }\n        return;\n    }\n    window->viewport_updated_at_least_once = true;\n    window->viewport_width = fw; window->viewport_height = fh;\n    double xr = window->viewport_x_ratio, yr = window->viewport_y_ratio;\n    window->viewport_x_ratio = (double)window->viewport_width / (double)w;\n    window->viewport_y_ratio = (double)window->viewport_height / (double)h;\n    bool dpi_changed = (xr != 0.0 && xr != window->viewport_x_ratio) || (yr != 0.0 && yr != window->viewport_y_ratio) || (xdpi != new_xdpi) || (ydpi != new_ydpi);\n\n    window->viewport_size_dirty = true;\n    window->viewport_width = MAX(window->viewport_width, min_width);\n    window->viewport_height = MAX(window->viewport_height, min_height);\n    window->window_width = MAX(w, min_width);\n    window->window_height = MAX(h, min_height);\n    if (notify_boss) {\n        call_boss(on_window_resize, \"KiiO\", window->id, window->viewport_width, window->viewport_height, dpi_changed ? Py_True : Py_False);\n    }\n    if (dpi_changed && window->is_layer_shell && window->handle) set_layer_shell_config_for(window, NULL);\n}\n\n// callbacks {{{\n\nvoid\nupdate_os_window_references(void) {\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = global_state.os_windows + i;\n        if (w->handle) glfwSetWindowUserPointer(w->handle, w);\n    }\n}\n\nstatic OSWindow*\nos_window_for_glfw_window(GLFWwindow *w) {\n    OSWindow *ans = glfwGetWindowUserPointer(w);\n    if (ans != NULL) return ans;\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        if ((GLFWwindow*)(global_state.os_windows[i].handle) == w) {\n            return global_state.os_windows + i;\n        }\n    }\n    return NULL;\n}\n\nstatic bool\nset_callback_window(GLFWwindow *w) {\n    global_state.callback_os_window = os_window_for_glfw_window(w);\n    return global_state.callback_os_window != NULL;\n}\n\nstatic bool\nis_window_ready_for_callbacks(void) {\n    OSWindow *w = global_state.callback_os_window;\n    if (w->num_tabs == 0) return false;\n    Tab *t = w->tabs + w->active_tab;\n    if (t->num_windows == 0) return false;\n    return true;\n}\n\n#define WINDOW_CALLBACK(name, fmt, ...) call_boss(name, \"K\" fmt, global_state.callback_os_window->id, __VA_ARGS__)\n\nstatic void\nshow_mouse_cursor(GLFWwindow *w) {\n    glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n}\n\nvoid\ncursor_active_callback(monotonic_t now) {\n    if (!global_state.callback_os_window) return;\n    if (OPT(mouse_hide.unhide_wait) == 0) {\n        show_mouse_cursor(global_state.callback_os_window->handle);\n    } else if (OPT(mouse_hide.unhide_wait) > 0) {\n            if (global_state.callback_os_window->mouse_activate_deadline == -1) {\n                global_state.callback_os_window->mouse_activate_deadline = OPT(mouse_hide.unhide_wait) + now;\n                global_state.callback_os_window->mouse_show_threshold = (int) (monotonic_t_to_s_double(OPT(mouse_hide.unhide_wait)) * OPT(mouse_hide.unhide_threshold));\n            } else if (now < global_state.callback_os_window->mouse_activate_deadline) {\n                if (global_state.callback_os_window->mouse_show_threshold > 0) {\n                    global_state.callback_os_window->mouse_show_threshold--;\n                }\n            } else {\n                if (\n                        now < global_state.callback_os_window->mouse_activate_deadline + s_double_to_monotonic_t(0.5) &&\n                        global_state.callback_os_window->mouse_show_threshold == 0\n                ) {\n                    show_mouse_cursor(global_state.callback_os_window->handle);\n                }\n                global_state.callback_os_window->mouse_activate_deadline = -1;\n            }\n    }\n}\n\nstatic void\nwindow_pos_callback(GLFWwindow* window, int x UNUSED, int y UNUSED) {\n    if (!set_callback_window(window)) return;\n#ifdef __APPLE__\n    // Apple needs IME position to be accurate before the next key event\n    OSWindow *osw = global_state.callback_os_window;\n    if (osw->is_focused && is_window_ready_for_callbacks()) {\n        Tab *tab = osw->tabs + osw->active_tab;\n        Window *w = tab->windows + tab->active_window;\n        if (w->render_data.screen) update_ime_position(w, w->render_data.screen);\n    }\n#endif\n    global_state.callback_os_window = NULL;\n}\n\nstatic void\nwindow_close_callback(GLFWwindow* window) {\n    if (!set_callback_window(window)) return;\n    global_state.callback_os_window->close_request = CONFIRMABLE_CLOSE_REQUESTED;\n    global_state.has_pending_closes = true;\n    request_tick_callback();\n    glfwSetWindowShouldClose(window, false);\n    global_state.callback_os_window = NULL;\n}\n\nstatic void\nwindow_occlusion_callback(GLFWwindow *window, bool occluded) {\n    if (!set_callback_window(window)) return;\n    debug(\"OSWindow %llu occlusion state changed, occluded: %d\\n\", global_state.callback_os_window->id, occluded);\n    if (!occluded) global_state.check_for_active_animated_images = true;\n    request_tick_callback();\n    global_state.callback_os_window = NULL;\n}\n\nstatic void\nwindow_iconify_callback(GLFWwindow *window, int iconified) {\n    if (!set_callback_window(window)) return;\n    if (!iconified) global_state.check_for_active_animated_images = true;\n    request_tick_callback();\n    global_state.callback_os_window = NULL;\n}\n\n#ifdef __APPLE__\nstatic void\ncocoa_out_of_sequence_render(OSWindow *window) {\n    make_os_window_context_current(window);\n    window->needs_render = true;\n\n    // On macOS Tahoe, the default framebuffer can become undefined during\n    // screen change events. Try to recover by recreating the drawable.\n    // See https://github.com/kovidgoyal/kitty/issues/9463\n    if (!current_framebuffer_is_ok()) {\n        debug_rendering(\"Cocoa OpenGL framebuffer broken, re-creating\\n\");\n        if (!glfwCocoaRecreateGLDrawable(window->handle) || !current_framebuffer_is_ok()) {\n            debug_rendering(\"Cocoa OpenGL framebuffer re-creation failed\\n\");\n            request_tick_callback();\n            return;\n        }\n    }\n\n    bool rendered = false;\n    if (window->fonts_data->sprite_map) {\n        window->needs_render = true;\n        window->render_state = RENDER_FRAME_READY;\n        rendered = render_os_window(window, monotonic(), true);\n    }\n    if (!rendered) {\n        debug_rendering(\"Cocoa out of sequence render did not happen\\n\");\n        blank_os_window(window);\n        swap_window_buffers(window);\n    }\n    window->needs_render = true;\n}\n\nstatic void\ncocoa_os_window_resized(GLFWwindow *w) {\n    if (!set_callback_window(w)) return;\n    if (global_state.callback_os_window->ignore_resize_events) return;\n    cocoa_out_of_sequence_render(global_state.callback_os_window);\n    global_state.callback_os_window = NULL;\n}\n#endif\n\n\n\nvoid\nchange_live_resize_state(OSWindow *w, bool in_progress) {\n    if (in_progress != w->live_resize.in_progress) {\n        w->live_resize.in_progress = in_progress;\n        w->live_resize.num_of_resize_events = 0;\n#ifdef __APPLE__\n        cocoa_out_of_sequence_render(w);\n#else\n        GLFWwindow *orig_ctx = make_os_window_context_current(w);\n        apply_swap_interval(in_progress ? 0 : -1);\n        if (orig_ctx) glfwMakeContextCurrent(orig_ctx);\n\n#endif\n    }\n}\n\nstatic void\nlive_resize_callback(GLFWwindow *w, bool started) {\n    if (!set_callback_window(w)) return;\n    if (global_state.callback_os_window->ignore_resize_events) return;\n    global_state.callback_os_window->live_resize.from_os_notification = true;\n    change_live_resize_state(global_state.callback_os_window, true);\n    global_state.has_pending_resizes = true;\n    if (!started) {\n        global_state.callback_os_window->live_resize.os_says_resize_complete = true;\n        request_tick_callback();\n    }\n    global_state.callback_os_window = NULL;\n}\n\nstatic void\nframebuffer_size_callback(GLFWwindow *w, int width, int height) {\n    if (!set_callback_window(w)) return;\n    if (global_state.callback_os_window->ignore_resize_events) return;\n    int min_width, min_height; min_size_for_os_window(global_state.callback_os_window, &min_width, &min_height);\n    if (width >= min_width && height >= min_height) {\n        OSWindow *window = global_state.callback_os_window;\n        global_state.has_pending_resizes = true;\n        change_live_resize_state(global_state.callback_os_window, true);\n        window->live_resize.last_resize_event_at = monotonic();\n        window->live_resize.width = MAX(0, width); window->live_resize.height = MAX(0, height);\n        window->live_resize.num_of_resize_events++;\n        make_os_window_context_current(window);\n        set_gpu_viewport(width, height);\n        request_tick_callback();\n    } else log_error(\"Ignoring resize request for tiny size: %dx%d\", width, height);\n    global_state.callback_os_window = NULL;\n}\n\nstatic void\ndpi_change_callback(GLFWwindow *w, float x_scale UNUSED, float y_scale UNUSED) {\n    if (!set_callback_window(w)) return;\n    if (global_state.callback_os_window->ignore_resize_events) return;\n    // Ensure update_os_window_viewport() is called in the near future, it will\n    // take care of DPI changes.\n    OSWindow *window = global_state.callback_os_window;\n    change_live_resize_state(global_state.callback_os_window, true);\n    global_state.has_pending_resizes = true;\n    window->live_resize.last_resize_event_at = monotonic();\n    global_state.callback_os_window = NULL;\n    request_tick_callback();\n}\n\nstatic void\nrefresh_callback(GLFWwindow *w) {\n    if (!set_callback_window(w)) return;\n    if (!global_state.callback_os_window->redraw_count) global_state.callback_os_window->redraw_count++;\n    global_state.callback_os_window = NULL;\n    request_tick_callback();\n}\n\nstatic int mods_at_last_key_or_button_event = 0;\n\n#ifndef __APPLE__\ntypedef struct modifier_key_state {\n    bool left, right;\n} modifier_key_state;\n\nstatic int\nkey_to_modifier(uint32_t key, bool *is_left) {\n    *is_left = false;\n    switch(key) {\n        case GLFW_FKEY_LEFT_SHIFT: *is_left = true; /* fallthrough */\n        case GLFW_FKEY_RIGHT_SHIFT:\n            return GLFW_MOD_SHIFT;\n        case GLFW_FKEY_LEFT_CONTROL: *is_left = true; /* fallthrough */\n        case GLFW_FKEY_RIGHT_CONTROL:\n            return GLFW_MOD_CONTROL;\n        case GLFW_FKEY_LEFT_ALT: *is_left = true; /* fallthrough */\n        case GLFW_FKEY_RIGHT_ALT:\n            return GLFW_MOD_ALT;\n        case GLFW_FKEY_LEFT_SUPER: *is_left = true; /* fallthrough */\n        case GLFW_FKEY_RIGHT_SUPER:\n            return GLFW_MOD_SUPER;\n        case GLFW_FKEY_LEFT_HYPER: *is_left = true; /* fallthrough */\n        case GLFW_FKEY_RIGHT_HYPER:\n            return GLFW_MOD_HYPER;\n        case GLFW_FKEY_LEFT_META: *is_left = true; /* fallthrough */\n        case GLFW_FKEY_RIGHT_META:\n            return GLFW_MOD_META;\n        default:\n            return -1;\n    }\n}\n\n\nstatic void\nupdate_modifier_state_on_modifier_key_event(GLFWkeyevent *ev, int key_modifier, bool is_left) {\n    // Update mods state to be what the kitty keyboard protocol requires, as on Linux modifier key events do not update modifier bits\n    static modifier_key_state all_states[8] = {0};\n    modifier_key_state *state = all_states + MIN((unsigned)__builtin_ctz(key_modifier), sizeof(all_states)-1);\n    const int modifier_was_set_before_event = ev->mods & key_modifier;\n    const bool is_release = ev->action == GLFW_RELEASE;\n    if (modifier_was_set_before_event) {\n        // a press with modifier already set means other modifier key is pressed\n        if (!is_release) { if (is_left) state->right = true; else state->left = true;  }\n    } else {\n        // if modifier is not set before event, means both keys are released\n        state->left = false; state->right = false;\n    }\n    if (is_release) {\n        if (is_left) state->left = false; else state->right = false;\n        if (modifier_was_set_before_event && !state->left && !state->right) ev->mods &= ~key_modifier;\n    } else {\n        if (is_left) state->left = true; else state->right = true;\n        ev->mods |= key_modifier;\n    }\n}\n#endif\n\nstatic void\nkey_callback(GLFWwindow *w, GLFWkeyevent *ev) {\n    if (!set_callback_window(w)) return;\n#ifdef __APPLE__\n    cocoa_clear_dock_badge_if_set();\n#endif\n#ifndef __APPLE__\n    bool is_left;\n    int key_modifier = key_to_modifier(ev->key, &is_left);\n    if (key_modifier != -1) update_modifier_state_on_modifier_key_event(ev, key_modifier, is_left);\n#endif\n    mods_at_last_key_or_button_event = ev->mods;\n    global_state.callback_os_window->cursor_blink_zero_time = monotonic();\n    if (is_window_ready_for_callbacks() && !ev->fake_event_on_focus_change) on_key_input(ev);\n    global_state.callback_os_window = NULL;\n    request_tick_callback();\n}\n\nstatic void\ncursor_enter_callback(GLFWwindow *w, int entered) {\n    if (!set_callback_window(w)) return;\n    double x, y;\n    glfwGetCursorPos(w, &x, &y);\n    monotonic_t now = monotonic();\n    global_state.callback_os_window->last_mouse_activity_at = now;\n    global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;\n    global_state.callback_os_window->mouse_y = y * global_state.callback_os_window->viewport_y_ratio;\n    if (entered) {\n        debug_input(\"Mouse cursor entered window: %llu at %fx%f\\n\", global_state.callback_os_window->id, x, y);\n        cursor_active_callback(now);\n        if (is_window_ready_for_callbacks()) enter_event(mods_at_last_key_or_button_event);\n    } else {\n        debug_input(\"Mouse cursor left window: %llu\\n\", global_state.callback_os_window->id);\n        if (is_window_ready_for_callbacks()) leave_event(mods_at_last_key_or_button_event);\n    }\n    request_tick_callback();\n    global_state.callback_os_window = NULL;\n}\n\nstatic void\nmouse_button_callback(GLFWwindow *w, int button, int action, int mods) {\n    if (!set_callback_window(w)) return;\n#ifdef __APPLE__\n    cocoa_clear_dock_badge_if_set();\n#endif\n    monotonic_t now = monotonic();\n    cursor_active_callback(now);\n    mods_at_last_key_or_button_event = mods;\n    OSWindow *window = global_state.callback_os_window;\n    window->last_mouse_activity_at = now;\n    if (button >= 0 && (unsigned int)button < arraysz(global_state.callback_os_window->mouse_button_pressed)) {\n        if (!window->has_received_cursor_pos_event) {  // ensure mouse position is correct\n            window->has_received_cursor_pos_event = true;\n            double x, y;\n            glfwGetCursorPos(w, &x, &y);\n            window->mouse_x = x * window->viewport_x_ratio;\n            window->mouse_y = y * window->viewport_y_ratio;\n            if (is_window_ready_for_callbacks()) mouse_event(-1, mods, -1);\n        }\n        global_state.callback_os_window->mouse_button_pressed[button] = action == GLFW_PRESS ? true : false;\n        if (is_window_ready_for_callbacks()) mouse_event(button, mods, action);\n    }\n    request_tick_callback();\n    global_state.callback_os_window = NULL;\n}\n\nstatic void\non_mouse_position_update(double x, double y) {\n    monotonic_t now = monotonic();\n    cursor_active_callback(now);\n    global_state.callback_os_window->last_mouse_activity_at = now;\n    global_state.callback_os_window->cursor_blink_zero_time = now;\n    global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;\n    global_state.callback_os_window->mouse_y = y * global_state.callback_os_window->viewport_y_ratio;\n    global_state.callback_os_window->has_received_cursor_pos_event = true;\n    if (is_window_ready_for_callbacks()) mouse_event(-1, mods_at_last_key_or_button_event, -1);\n    request_tick_callback();\n}\n\nstatic void\ncursor_pos_callback(GLFWwindow *w, double x, double y) {\n    if (!set_callback_window(w)) return;\n    on_mouse_position_update(x, y);\n    global_state.callback_os_window = NULL;\n}\n\nstatic void\nscroll_callback(GLFWwindow *w, const GLFWScrollEvent *ev) {\n    if (!set_callback_window(w)) return;\n    monotonic_t now = monotonic();\n    if (OPT(mouse_hide.scroll_unhide)) cursor_active_callback(now);\n    global_state.callback_os_window->last_mouse_activity_at = now;\n    if (is_window_ready_for_callbacks()) scroll_event(ev);\n    request_tick_callback();\n    global_state.callback_os_window = NULL;\n}\n\nstatic id_type focus_counter = 0;\n\nstatic void\nset_os_window_visibility(OSWindow *w, int set_visible, bool move_to_active_screen) {\n    if (set_visible) {\n        glfwShowWindow(w->handle, move_to_active_screen);\n        w->needs_render = true;\n        w->render_state = RENDER_FRAME_NOT_REQUESTED;\n        w->keep_rendering_till_swap = 256;  // try this many times\n        request_tick_callback();\n    } else glfwHideWindow(w->handle);\n}\n\nstatic void\nupdate_os_window_visibility_based_on_focus(id_type timer_id UNUSED, void*d) {\n    OSWindow * osw = os_window_for_id((uintptr_t)d);\n    if (osw && osw->hide_on_focus_loss && !osw->is_focused) set_os_window_visibility(osw, 0, false);\n}\n\nstatic void\nwindow_focus_callback(GLFWwindow *w, int focused) {\n    if (!set_callback_window(w)) return;\n#define osw global_state.callback_os_window\n    debug_input(\"\\x1b[35mon_focus_change\\x1b[m: window id: 0x%llu focused: %d\\n\", osw->id, focused);\n    bool focus_changed = osw->is_focused != focused;\n    osw->is_focused = focused ? true : false;\n    monotonic_t now = monotonic();\n    id_type wid = osw->id;\n    if (focused) {\n        cursor_active_callback(now);\n        focus_in_event();\n        osw->last_focused_counter = ++focus_counter;\n        global_state.check_for_active_animated_images = true;\n    }\n    osw->last_mouse_activity_at = now;\n    osw->cursor_blink_zero_time = now;\n    if (is_window_ready_for_callbacks()) {\n        WINDOW_CALLBACK(on_focus, \"O\", focused ? Py_True : Py_False);\n        if (!osw || osw->id != wid) osw = os_window_for_id(wid);\n        if (osw) {\n            GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_FOCUS, .focused = focused };\n            glfwUpdateIMEState(osw->handle, &ev);\n            if (focused) {\n                Tab *tab = osw->tabs + osw->active_tab;\n                Window *window = tab->windows + tab->active_window;\n                if (window->render_data.screen) update_ime_position(window, window->render_data.screen);\n            }\n        }\n    }\n    request_tick_callback();\n    if (osw && osw->handle && !focused && focus_changed && osw->hide_on_focus_loss && glfwGetWindowAttrib(osw->handle, GLFW_VISIBLE)) {\n        add_main_loop_timer(0, false, update_os_window_visibility_based_on_focus, (void*)(uintptr_t)osw->id, NULL);\n    }\n    osw = NULL;\n#undef osw\n}\n\n#define TAB_DRAG_MIME_NUMBER 400\n\nstatic int\nis_droppable_mime(const char *mime) {\n    static char tab_mime[64] = {0};\n    if (!tab_mime[0]) snprintf(tab_mime, sizeof(tab_mime), \"application/net.kovidgoyal.kitty-tab-%d\", getpid());\n    if (strcmp(mime, tab_mime) == 0) return TAB_DRAG_MIME_NUMBER;\n    if (strcmp(mime, \"text/uri-list\") == 0) return 3;\n    if (strcmp(mime, \"text/plain;charset=utf-8\") == 0) return 2;\n    if (strcmp(mime, \"text/plain\") == 0) return 1;\n    return 0;\n}\n\nstatic size_t\nremove_duplicate_mimes(const char **mimes, size_t count) {\n    // Use simple O(n²) scan since lists are typically small\n    size_t new_count = 0;\n    for (size_t i = 0; i < count; i++) {\n        bool is_duplicate = false;\n        for (size_t j = 0; j < new_count; j++) {\n            if (strcmp(mimes[i], mimes[j]) == 0) { is_duplicate = true; break; }\n        }\n        if (!is_duplicate) {\n            if (new_count != i) SWAP(mimes[i], mimes[new_count]);\n            new_count++;\n        }\n    }\n    return new_count;\n}\n\nstatic void\nupdate_allowed_mimes_for_drop(GLFWDropEvent *ev) {\n    if (ev->mimes && ev->num_mimes) {\n        // Sort MIME types by priority (descending) and keep only accepted ones\n        // Use simple bubble sort since lists are typically small\n        size_t new_count = 0;\n        // Use stack-allocated array for priorities (count is typically small)\n        int priorities[32];\n        int* prio_arr = (ev->num_mimes <= (int)arraysz(priorities)) ? priorities : (int*)malloc(ev->num_mimes * sizeof(int));\n        if (!prio_arr) return;\n        // First pass: filter droppable MIME types and cache priorities\n        for (size_t i = 0; i < ev->num_mimes; i++) {\n            int prio = is_droppable_mime(ev->mimes[i]);\n            if (prio > 0) {\n                // Move this mime to the new_count position\n                if (new_count != i) { SWAP(ev->mimes[i], ev->mimes[new_count]); }\n                prio_arr[new_count] = prio;\n                new_count++;\n            }\n        }\n        // Second pass: sort by cached priorities (descending)\n        for (size_t i = 0; i + 1 < new_count; i++) {\n            for (size_t j = i + 1; j < new_count; j++) {\n                if (prio_arr[j] > prio_arr[i]) {\n                    SWAP(ev->mimes[i], ev->mimes[j]);\n                    SWAP(prio_arr[i], prio_arr[j]);\n                }\n            }\n        }\n        if (prio_arr != priorities) free(prio_arr);\n        ev->num_mimes = new_count;\n    }\n}\n\nstatic void\nread_drop_data(GLFWwindow *window, GLFWDropEvent *ev) {\n    RAII_PyObject(chunk, PyBytes_FromStringAndSize(NULL, 8192));\n#define finish(ok) ev->finish_drop(window, ok ? GLFW_DRAG_OPERATION_COPY : GLFW_DRAG_OPERATION_GENERIC); Py_CLEAR(global_state.drop_dest.data); if (PyErr_Occurred()) PyErr_Print()\n    if (!chunk) { finish(false); return; }\n    ssize_t ret = ev->read_data(window, ev, PyBytes_AS_STRING(chunk), PyBytes_GET_SIZE(chunk));\n    if (ret == 0) {\n        global_state.drop_dest.num_left--;\n        if (!global_state.drop_dest.num_left) {\n            WINDOW_CALLBACK(on_drop, \"OOii\", global_state.drop_dest.data, Py_False,\n                global_state.callback_os_window->last_drag_event.x, global_state.callback_os_window->last_drag_event.y);\n            finish(true);\n        }\n    } else if (ret > 0) {\n        _PyBytes_Resize(&chunk, ret);\n        PyObject *data = chunk;\n        RAII_PyObject(existing, PyDict_GetItemString(global_state.drop_dest.data, ev->mimes[0]));\n        if (existing) {\n            existing = Py_NewRef(existing);  // because PyBytes_Concat steals a reference\n            PyBytes_Concat(&existing, chunk);\n            data = existing;\n        }\n        if (!data || PyDict_SetItemString(global_state.drop_dest.data, ev->mimes[0], data) != 0) { finish(false); }\n    } else {\n        int posix_errno = -ret;\n        WINDOW_CALLBACK(on_drop, \"iOii\", posix_errno, Py_False,\n            global_state.callback_os_window->last_drag_event.x, global_state.callback_os_window->last_drag_event.y);\n        finish(false);\n    }\n#undef finish\n}\n\nstatic void\non_drop(GLFWwindow *window, GLFWDropEvent *ev) {\n    if (!set_callback_window(window)) return;\n    OSWindow *os_window = global_state.callback_os_window;\n    switch (ev->type) {\n        case GLFW_DROP_ENTER:\n        case GLFW_DROP_MOVE:\n            os_window->last_drag_event.x = (int)(ev->xpos * os_window->viewport_x_ratio);\n            os_window->last_drag_event.y = (int)(ev->ypos * os_window->viewport_y_ratio);\n            on_mouse_position_update(ev->xpos, ev->ypos);\n            call_boss(on_drop_move, \"KiiOO\",\n                os_window->id, os_window->last_drag_event.x, os_window->last_drag_event.y,\n                ev->from_self ? Py_True : Py_False, Py_False);\n            /* fallthrough */\n        case GLFW_DROP_STATUS_UPDATE:\n            update_allowed_mimes_for_drop(ev);\n            break;\n        case GLFW_DROP_LEAVE:\n            call_boss(on_drop_move, \"KiiOO\",\n                os_window->id, os_window->last_drag_event.x, os_window->last_drag_event.y,\n                ev->from_self ? Py_True : Py_False, Py_True);\n            break;\n        case GLFW_DROP_DROP:\n            Py_CLEAR(global_state.drop_dest.data);\n            if (ev->from_self) {\n                if (global_state.drag_source.drag_data) {\n                    global_state.drag_source.was_dropped = true;\n                    WINDOW_CALLBACK(on_drop, \"OOii\", global_state.drag_source.drag_data, Py_True,\n                        global_state.callback_os_window->last_drag_event.x, global_state.callback_os_window->last_drag_event.y);\n                } else log_error(\"Got a drop from self but drag_source.drag_data is NULL\");\n                ev->finish_drop(window, GLFW_DRAG_OPERATION_COPY);\n                break;\n            }\n            update_allowed_mimes_for_drop(ev);\n            ev->num_mimes = remove_duplicate_mimes(ev->mimes, ev->num_mimes);\n            global_state.drop_dest.num_left = ev->num_mimes;\n            if (!global_state.drop_dest.num_left || !(global_state.drop_dest.data = PyDict_New())) {\n                ev->finish_drop(window, GLFW_DRAG_OPERATION_GENERIC);\n            }\n            break;\n        case GLFW_DROP_DATA_AVAILABLE:\n            if (!global_state.drop_dest.data) ev->finish_drop(window, GLFW_DRAG_OPERATION_GENERIC);\n            else read_drop_data(window, ev);\n            break;\n    }\n}\n\nstatic void\napplication_close_requested_callback(int flags) {\n    if (flags) {\n        global_state.quit_request = IMPERATIVE_CLOSE_REQUESTED;\n        global_state.has_pending_closes = true;\n        request_tick_callback();\n    } else {\n        if (global_state.quit_request == NO_CLOSE_REQUESTED) {\n            global_state.has_pending_closes = true;\n            global_state.quit_request = CONFIRMABLE_CLOSE_REQUESTED;\n            request_tick_callback();\n        }\n    }\n}\n\n#define ds (global_state.drag_source)\n\nvoid\nfree_drag_source(void) {\n    if (ds.accepted_mime_type) free(ds.accepted_mime_type);\n    Py_CLEAR(ds.drag_data); Py_CLEAR(ds.thumbnails);\n    zero_at_ptr(&ds);\n}\n\nstatic void\ndrag_source_callback(GLFWwindow *window UNUSED, GLFWDragEvent *ev) {\n#define finish \\\n    call_boss(on_drag_source_finished, \"OOsiOO\", \\\n            ds.was_dropped ? Py_True : Py_False, ds.was_canceled ? Py_True: Py_False, \\\n            ds.accepted_mime_type ? ds.accepted_mime_type : \"\", \\\n            ds.action, ds.drag_data ? ds.drag_data : Py_None, ds.needs_toplevel_on_wayland ? Py_True : Py_False); \\\n    free_drag_source();\n\n    switch (ev->type) {\n        case GLFW_DRAG_DATA_REQUEST: // we currently pre-provide all data so this should never happen\n            if (ev->data_sz) {\n                // previously returned data is consumed, free it\n            } else {\n                ev->err_num = ENOENT;\n            }\n            break;\n        case GLFW_DRAG_ACCEPTED:\n            free(ds.accepted_mime_type);\n            ds.accepted_mime_type = ev->mime_type ? strdup(ev->mime_type) : NULL;\n            break;\n        case GLFW_DRAG_ACTION_CHANGED:\n            ds.action = ev->action; break;\n        case GLFW_DRAG_DROPPED:\n            ds.was_dropped = true;\n            if (ev->action == GLFW_DRAG_OPERATION_NONE) {\n                finish\n            }\n            break;\n        case GLFW_DRAG_CANCELLED:\n            ds.was_canceled = true;\n            /* fallthrough */\n        case GLFW_DRAG_FINSHED:\n            finish\n            break;\n    }\n#undef finish\n}\n#undef ds\n\nstatic char*\nget_current_selection(void) {\n    if (!global_state.boss) return NULL;\n    PyObject *ret = PyObject_CallMethod(global_state.boss, \"get_active_selection\", NULL);\n    if (!ret) { PyErr_Print(); return NULL; }\n    char* ans = NULL;\n    if (PyUnicode_Check(ret)) ans = strdup(PyUnicode_AsUTF8(ret));\n    Py_DECREF(ret);\n    return ans;\n}\n\nstatic bool\nhas_current_selection(void) {\n    if (!global_state.boss) return false;\n    PyObject *ret = PyObject_CallMethod(global_state.boss, \"has_active_selection\", NULL);\n    if (!ret) { PyErr_Print(); return false; }\n    bool ans = ret == Py_True;\n    Py_DECREF(ret);\n    return ans;\n}\n\nvoid prepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev);\n\nstatic bool\nget_ime_cursor_position(GLFWwindow *glfw_window, GLFWIMEUpdateEvent *ev) {\n    bool ans = false;\n    OSWindow *osw = os_window_for_glfw_window(glfw_window);\n    if (osw && osw->is_focused && osw->num_tabs > 0) {\n        Tab *tab = osw->tabs + osw->active_tab;\n        if (tab->num_windows > 0) {\n            Window *w = tab->windows + tab->active_window;\n            Screen *screen = w->render_data.screen;\n            if (screen) {\n                prepare_ime_position_update_event(osw, w, screen, ev);\n                ans = true;\n            }\n        }\n    }\n    return ans;\n}\n\n\n#ifdef __APPLE__\nstatic bool\napple_url_open_callback(const char* url) {\n    set_cocoa_pending_action(LAUNCH_URLS, url);\n    return true;\n}\n\n\nbool\ndraw_window_title(double font_sz_pts UNUSED, double ydpi UNUSED, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) {\n    static char buf[2048];\n    strip_csi_(text, buf, arraysz(buf));\n    return cocoa_render_line_of_text(buf, fg, bg, output_buf, width, height);\n}\n\n\nuint8_t*\ndraw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height) {\n    uint8_t *ans = render_single_ascii_char_as_mask(ch, result_width, result_height);\n    if (PyErr_Occurred()) PyErr_Print();\n    return ans;\n}\n\n#else\n\nstatic FreeTypeRenderCtx bold_render_ctx = NULL, normal_render_ctx = NULL;\n\nstatic FreeTypeRenderCtx\nfreetype_render_ctx(bool bold) {\n    FreeTypeRenderCtx *which = bold ? &bold_render_ctx : &normal_render_ctx;\n    if (!*which) {\n        *which = create_freetype_render_context(NULL, bold, false);\n        if (!*which) {\n            if (PyErr_Occurred()) PyErr_Print();\n            return NULL;\n        }\n    }\n    return *which;\n}\n\nstatic bool\ndraw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool is_single_glyph) {\n    if (!set_callback_window(window)) return false;\n    FreeTypeRenderCtx ctx;\n    if (!(ctx = freetype_render_ctx(true))) return false;\n    double xdpi, ydpi;\n    get_window_dpi(window, &xdpi, &ydpi);\n    unsigned px_sz = 2 * height / 3;\n    static char title[2048];\n    if (!is_single_glyph) {\n        snprintf(title, sizeof(title), \" ❭ %s\", text);\n        text = title;\n    }\n    bool ok = render_single_line(ctx, text, px_sz, fg, bg, output_buf, width, height, x_offset, y_offset, right_margin, is_single_glyph);\n    if (!ok && PyErr_Occurred()) PyErr_Print();\n    return ok;\n}\n\nbool\ndraw_window_title(double font_sz_pts, double ydpi, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) {\n    FreeTypeRenderCtx ctx;\n    if (!(ctx = freetype_render_ctx(false))) return false;\n    static char buf[2048];\n    strip_csi_(text, buf, arraysz(buf));\n    unsigned px_sz = (unsigned)(font_sz_pts * ydpi / 72.);\n    px_sz = MIN(px_sz, 3 * height / 4);\n#define RGB2BGR(x) (x & 0xFF000000) | ((x & 0xFF0000) >> 16) | (x & 0x00FF00) | ((x & 0x0000FF) << 16)\n    bool ok = render_single_line(ctx, buf, px_sz, RGB2BGR(fg), RGB2BGR(bg), output_buf, width, height, 0, 0, 0, false);\n#undef RGB2BGR\n    if (!ok && PyErr_Occurred()) PyErr_Print();\n    return ok;\n}\n\nuint8_t*\ndraw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height) {\n    FreeTypeRenderCtx ctx;\n    if (!(ctx = freetype_render_ctx(true))) return false;\n    uint8_t *ans = render_single_ascii_char_as_mask(ctx, ch, result_width, result_height);\n    if (PyErr_Occurred()) PyErr_Print();\n    return ans;\n}\n#endif\n// }}}\n\nstatic void\nset_glfw_mouse_cursor(GLFWwindow *w, GLFWCursorShape shape) {\n    if (!cursors[shape].initialized) {\n        cursors[shape].initialized = true;\n        cursors[shape].glfw = glfwCreateStandardCursor(shape);\n    }\n    if (cursors[shape].glfw) glfwSetCursor(w, cursors[shape].glfw);\n}\n\nstatic void\nset_glfw_mouse_pointer_shape_in_window(GLFWwindow *w, MouseShape type) {\n    switch(type) {\n        case INVALID_POINTER: break;\n        /* start enum to glfw (auto generated by gen-key-constants.py do not edit) */\n        case DEFAULT_POINTER: set_glfw_mouse_cursor(w, GLFW_DEFAULT_CURSOR); break;\n        case TEXT_POINTER: set_glfw_mouse_cursor(w, GLFW_TEXT_CURSOR); break;\n        case POINTER_POINTER: set_glfw_mouse_cursor(w, GLFW_POINTER_CURSOR); break;\n        case HELP_POINTER: set_glfw_mouse_cursor(w, GLFW_HELP_CURSOR); break;\n        case WAIT_POINTER: set_glfw_mouse_cursor(w, GLFW_WAIT_CURSOR); break;\n        case PROGRESS_POINTER: set_glfw_mouse_cursor(w, GLFW_PROGRESS_CURSOR); break;\n        case CROSSHAIR_POINTER: set_glfw_mouse_cursor(w, GLFW_CROSSHAIR_CURSOR); break;\n        case CELL_POINTER: set_glfw_mouse_cursor(w, GLFW_CELL_CURSOR); break;\n        case VERTICAL_TEXT_POINTER: set_glfw_mouse_cursor(w, GLFW_VERTICAL_TEXT_CURSOR); break;\n        case MOVE_POINTER: set_glfw_mouse_cursor(w, GLFW_MOVE_CURSOR); break;\n        case E_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_E_RESIZE_CURSOR); break;\n        case NE_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NE_RESIZE_CURSOR); break;\n        case NW_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NW_RESIZE_CURSOR); break;\n        case N_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_N_RESIZE_CURSOR); break;\n        case SE_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_SE_RESIZE_CURSOR); break;\n        case SW_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_SW_RESIZE_CURSOR); break;\n        case S_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_S_RESIZE_CURSOR); break;\n        case W_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_W_RESIZE_CURSOR); break;\n        case EW_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_EW_RESIZE_CURSOR); break;\n        case NS_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NS_RESIZE_CURSOR); break;\n        case NESW_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NESW_RESIZE_CURSOR); break;\n        case NWSE_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NWSE_RESIZE_CURSOR); break;\n        case ZOOM_IN_POINTER: set_glfw_mouse_cursor(w, GLFW_ZOOM_IN_CURSOR); break;\n        case ZOOM_OUT_POINTER: set_glfw_mouse_cursor(w, GLFW_ZOOM_OUT_CURSOR); break;\n        case ALIAS_POINTER: set_glfw_mouse_cursor(w, GLFW_ALIAS_CURSOR); break;\n        case COPY_POINTER: set_glfw_mouse_cursor(w, GLFW_COPY_CURSOR); break;\n        case NOT_ALLOWED_POINTER: set_glfw_mouse_cursor(w, GLFW_NOT_ALLOWED_CURSOR); break;\n        case NO_DROP_POINTER: set_glfw_mouse_cursor(w, GLFW_NO_DROP_CURSOR); break;\n        case GRAB_POINTER: set_glfw_mouse_cursor(w, GLFW_GRAB_CURSOR); break;\n        case GRABBING_POINTER: set_glfw_mouse_cursor(w, GLFW_GRABBING_CURSOR); break;\n/* end enum to glfw */\n    }\n}\n\nvoid\nset_mouse_cursor(MouseShape type) {\n    if (global_state.callback_os_window) {\n        GLFWwindow *w = (GLFWwindow*)global_state.callback_os_window->handle;\n        set_glfw_mouse_pointer_shape_in_window(w, type);\n    }\n}\n\nstatic GLFWimage logo = {0};\n\nstatic PyObject*\nset_default_window_icon(PyObject UNUSED *self, PyObject *args) {\n    size_t sz;\n    unsigned int width, height;\n    const char *path;\n    uint8_t *data;\n    if(!PyArg_ParseTuple(args, \"s\", &path)) return NULL;\n    if (png_path_to_bitmap(path, &data, &width, &height, &sz)) {\n#ifndef __APPLE__\n        if (!global_state.is_wayland && (width > 128 || height > 128)) {\n            return PyErr_Format(PyExc_ValueError, \"The window icon is too large (%dx%d). On X11 max window icon size is: 128x128. Create a file called ~/.config/kitty.app-128.png containing a 128x128 image to use as the window icon on X11.\", width, height);\n        }\n#endif\n        logo.width = width; logo.height = height;\n        logo.pixels = data;\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nset_os_window_icon(PyObject UNUSED *self, PyObject *args) {\n    size_t sz;\n    unsigned int width, height;\n    PyObject *what = NULL;\n    uint8_t *data;\n    unsigned long long id;\n    if(!PyArg_ParseTuple(args, \"K|O\", &id, &what)) return NULL;\n    OSWindow *os_window = os_window_for_id(id);\n    if (!os_window) { PyErr_Format(PyExc_KeyError, \"No OS Window with id: %llu\", id); return NULL; }\n    if (os_window->is_layer_shell && global_state.is_wayland) Py_RETURN_NONE;\n    if (!what || what == Py_None) {\n        glfwSetWindowIcon(os_window->handle, 0, NULL);\n        Py_RETURN_NONE;\n    }\n    if (PyUnicode_Check(what)) {\n        const char *path = PyUnicode_AsUTF8(what);\n        if (png_path_to_bitmap(path, &data, &width, &height, &sz)) {\n            GLFWimage img = { .pixels = data, .width = width, .height = height };\n            glfwSetWindowIcon(os_window->handle, 1, &img);\n            free(data);\n        } else {\n            PyErr_Format(PyExc_ValueError, \"%s is not a valid PNG image\", path);\n            return NULL;\n        }\n        Py_RETURN_NONE;\n    }\n    RAII_PY_BUFFER(buf);\n    if(!PyArg_ParseTuple(args, \"Ky*\", &id, &buf)) return NULL;\n    if (png_from_data(buf.buf, buf.len, \"<data>\", &data, &width, &height, &sz)) {\n        GLFWimage img = { .pixels = data, .width = width, .height = height };\n        glfwSetWindowIcon(os_window->handle, 1, &img);\n    } else {\n        PyErr_Format(PyExc_ValueError, \"The supplied data of %lu bytes is not a valid PNG image\", (unsigned long)buf.len);\n        return NULL;\n    }\n    Py_RETURN_NONE;\n}\n\n\n\nvoid*\nmake_os_window_context_current(OSWindow *w) {\n    GLFWwindow *current_context = glfwGetCurrentContext();\n    if (w->handle != current_context) {\n        glfwMakeContextCurrent(w->handle);\n        return current_context;\n    }\n    return NULL;\n}\n\nvoid\nget_os_window_size(OSWindow *os_window, int *w, int *h, int *fw, int *fh) {\n    if (w && h) glfwGetWindowSize(os_window->handle, w, h);\n    if (fw && fh) glfwGetFramebufferSize(os_window->handle, fw, fh);\n}\n\nvoid\nset_os_window_size(OSWindow *os_window, int x, int y) {\n    glfwSetWindowSize(os_window->handle, x, y);\n}\n\nvoid\nget_os_window_pos(OSWindow *os_window, int *x, int *y) {\n    glfwGetWindowPos(os_window->handle, x, y);\n}\n\nvoid\nset_os_window_pos(OSWindow *os_window, int x, int y) {\n    glfwSetWindowPos(os_window->handle, x, y);\n}\n\nstatic void\ndpi_from_scale(float xscale, float yscale, double *xdpi, double *ydpi) {\n#ifdef __APPLE__\n    const double factor = 72.0;\n#else\n    const double factor = 96.0;\n#endif\n    *xdpi = xscale * factor;\n    *ydpi = yscale * factor;\n}\n\nstatic void\nget_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xdpi, double *ydpi) {\n    // if you change this function also change createSurface() in wl_window.c\n    *xscale = 1; *yscale = 1;\n    if (w) glfwGetWindowContentScale(w, xscale, yscale);\n    else {\n        GLFWmonitor *monitor = glfwGetPrimaryMonitor();\n        if (monitor) glfwGetMonitorContentScale(monitor, xscale, yscale);\n    }\n    // check for zero, negative, NaN or excessive values of xscale/yscale\n    if (*xscale <= 0.0001 || *xscale != *xscale || *xscale >= 24) *xscale = 1.0;\n    if (*yscale <= 0.0001 || *yscale != *yscale || *yscale >= 24) *yscale = 1.0;\n    dpi_from_scale(*xscale, *yscale, xdpi, ydpi);\n}\n\nstatic void\nget_window_dpi(GLFWwindow *w, double *x, double *y) {\n    float xscale, yscale;\n    get_window_content_scale(w, &xscale, &yscale, x, y);\n}\n\nvoid\nget_os_window_content_scale(OSWindow *os_window, double *xdpi, double *ydpi, float *xscale, float *yscale) {\n    get_window_content_scale(os_window->handle, xscale, yscale, xdpi, ydpi);\n}\n\nstatic bool\ndo_toggle_fullscreen(OSWindow *w, unsigned int flags, bool restore_sizes) {\n    int width, height, x, y;\n    glfwGetWindowSize(w->handle, &width, &height);\n    if (!global_state.is_wayland) glfwGetWindowPos(w->handle, &x, &y);\n    bool was_maximized = glfwGetWindowAttrib(w->handle, GLFW_MAXIMIZED);\n    if (glfwToggleFullscreen(w->handle, flags)) {\n        w->before_fullscreen.is_set = true;\n        w->before_fullscreen.w = width; w->before_fullscreen.h = height; w->before_fullscreen.x = x; w->before_fullscreen.y = y;\n        w->before_fullscreen.was_maximized = was_maximized;\n        return true;\n    }\n    if (w->before_fullscreen.is_set && restore_sizes) {\n        glfwSetWindowSize(w->handle, w->before_fullscreen.w, w->before_fullscreen.h);\n        if (!global_state.is_wayland) glfwSetWindowPos(w->handle, w->before_fullscreen.x, w->before_fullscreen.y);\n        if (w->before_fullscreen.was_maximized) glfwMaximizeWindow(w->handle);\n    }\n    return false;\n}\n\nstatic bool\ntoggle_fullscreen_for_os_window(OSWindow *w) {\n    if (!w || !w->handle) return false;\n    if (!w->is_layer_shell) {\n#ifdef __APPLE__\n        if (!OPT(macos_traditional_fullscreen)) return do_toggle_fullscreen(w, 1, false);\n#endif\n        return do_toggle_fullscreen(w, 0, true);\n    }\n    const GLFWLayerShellConfig *prev = glfwGetLayerShellConfig(w->handle);\n    if (!prev) return false;\n    GLFWLayerShellConfig lsc;\n    memcpy(&lsc, prev, sizeof(lsc));\n    if (prev->type == GLFW_LAYER_SHELL_OVERLAY || prev->type == GLFW_LAYER_SHELL_TOP) {\n        if (prev->was_toggled_to_fullscreen) {\n            lsc.edge = prev->previous.edge;\n            lsc.requested_bottom_margin = prev->previous.requested_bottom_margin;\n            lsc.requested_top_margin = prev->previous.requested_top_margin;\n            lsc.requested_left_margin = prev->previous.requested_left_margin;\n            lsc.requested_right_margin = prev->previous.requested_right_margin;\n            lsc.was_toggled_to_fullscreen = false;\n            glfwSetLayerShellConfig(w->handle, &lsc);\n            return true;\n        }\n        lsc.edge = GLFW_EDGE_CENTER;\n        lsc.previous.edge = prev->edge;\n        lsc.previous.requested_right_margin = prev->requested_right_margin;\n        lsc.previous.requested_left_margin = prev->requested_left_margin;\n        lsc.previous.requested_top_margin = prev->requested_top_margin;\n        lsc.previous.requested_bottom_margin = prev->requested_bottom_margin;\n        lsc.requested_bottom_margin = 0; lsc.requested_top_margin = 0; lsc.requested_left_margin = 0; lsc.requested_right_margin = 0;\n        lsc.was_toggled_to_fullscreen = true;\n        glfwSetLayerShellConfig(w->handle, &lsc);\n        return true;\n    }\n    return false;\n}\n\nbool\nis_os_window_fullscreen(OSWindow *w) {\n    unsigned int flags = 0;\n    if (!w || !w->handle) return false;\n    if (w->is_layer_shell) {\n        const GLFWLayerShellConfig *c = glfwGetLayerShellConfig(w->handle);\n        return c && c->was_toggled_to_fullscreen;\n    }\n#ifdef __APPLE__\n    if (!OPT(macos_traditional_fullscreen)) flags = 1;\n#endif\n    return glfwIsFullscreen(w->handle, flags);\n}\n\nstatic bool\ntoggle_maximized_for_os_window(OSWindow *w) {\n    bool maximized = false;\n    if (w && w->handle && !w->is_layer_shell) {\n        if (glfwGetWindowAttrib(w->handle, GLFW_MAXIMIZED)) {\n            glfwRestoreWindow(w->handle);\n        } else {\n            glfwMaximizeWindow(w->handle);\n            maximized = true;\n        }\n    }\n    return maximized;\n}\n\nstatic void\nchange_state_for_os_window(OSWindow *w, int state) {\n    if (!w || !w->handle) return;\n    switch (state) {\n        case WINDOW_MAXIMIZED:\n            if (!w->is_layer_shell) glfwMaximizeWindow(w->handle);\n            break;\n        case WINDOW_MINIMIZED:\n            if (!w->is_layer_shell) glfwIconifyWindow(w->handle);\n            break;\n        case WINDOW_FULLSCREEN:\n            if (!is_os_window_fullscreen(w)) toggle_fullscreen_for_os_window(w);\n            break;\n        case WINDOW_NORMAL:\n            if (is_os_window_fullscreen(w)) toggle_fullscreen_for_os_window(w);\n            else if (!w->is_layer_shell) glfwRestoreWindow(w->handle);\n            break;\n        case WINDOW_HIDDEN:\n            glfwHideWindow(w->handle); break;\n    }\n}\n\n#ifdef __APPLE__\n\nstatic int\nfilter_option(int key UNUSED, int mods, unsigned int native_key UNUSED, unsigned long flags) {\n    mods &= ~(GLFW_MOD_NUM_LOCK | GLFW_MOD_CAPS_LOCK);\n    if ((mods == GLFW_MOD_ALT) || (mods == (GLFW_MOD_ALT | GLFW_MOD_SHIFT))) {\n        if (OPT(macos_option_as_alt) == 3) return 1;\n        if (cocoa_alt_option_key_pressed(flags)) return 1;\n    }\n    return 0;\n}\n\nstatic bool\non_application_reopen(int has_visible_windows) {\n    if (has_visible_windows) return true;\n    set_cocoa_pending_action(NEW_OS_WINDOW, NULL);\n    return false;\n}\n\nstatic bool\nintercept_cocoa_fullscreen(GLFWwindow *w) {\n    if (!set_callback_window(w)) return false;\n    if (!OPT(macos_traditional_fullscreen)) {\n        // In non traditional fullscreen macOS forces the window to opaque\n        global_state.callback_os_window->background_opacity.os_forces_opaque = !is_os_window_fullscreen(\n                global_state.callback_os_window);\n        return false;\n    }\n    // macOS Split View uses Cocoa fullscreen internally, so the window\n    // can end up with NSWindowStyleMaskFullScreen set even when\n    // macos_traditional_fullscreen is enabled. Redirecting to traditional\n    // fullscreen would crash in setStyleMask: (see #9572).\n    if (glfwIsFullscreen(w, 1)) {\n        global_state.callback_os_window->background_opacity.os_forces_opaque = false;\n        return false;\n    }\n    toggle_fullscreen_for_os_window(global_state.callback_os_window);\n    global_state.callback_os_window = NULL;\n    return true;\n}\n#endif\n\nstatic void\ninit_window_chrome_state(WindowChromeState *s, color_type active_window_bg, float background_opacity) {\n    zero_at_ptr(s);\n    const bool should_blur = background_opacity < 1.f && OPT(background_blur) > 0;\n#define SET_TCOL(val) \\\n        s->use_system_color = false; \\\n        switch (val & 0xff) { \\\n            case 0: s->use_system_color = true; s->color = active_window_bg; break; \\\n            case 1: s->color = active_window_bg; break; \\\n            default: s->color = val >> 8; break; \\\n        }\n\n#ifdef __APPLE__\n    if (OPT(macos_titlebar_color) < 0) {\n        s->use_system_color = true;\n        s->system_color = -OPT(macos_titlebar_color);\n    } else {\n        unsigned long val = OPT(macos_titlebar_color);\n        SET_TCOL(val);\n    }\n    s->macos_colorspace = OPT(macos_colorspace);\n    s->resizable = OPT(macos_window_resizable);\n#else\n    if (global_state.is_wayland) { SET_TCOL(OPT(wayland_titlebar_color)); }\n#endif\n    s->background_blur = should_blur ? OPT(background_blur) : 0;\n    s->hide_window_decorations = OPT(hide_window_decorations);\n    s->show_title_in_titlebar = (OPT(macos_show_window_title_in) & WINDOW) != 0;\n    s->background_opacity = background_opacity;\n}\n\nstatic void\napply_window_chrome_state(GLFWwindow *w, WindowChromeState new_state, int width, int height, bool window_decorations_changed) {\n#ifdef __APPLE__\n    glfwCocoaSetWindowChrome(w,\n        new_state.color, new_state.use_system_color, new_state.system_color,\n        new_state.background_blur, new_state.hide_window_decorations,\n        new_state.show_title_in_titlebar, new_state.macos_colorspace,\n        new_state.background_opacity, new_state.resizable\n    );\n    // Need to resize the window again after hiding decorations or title bar to take up screen space\n    if (window_decorations_changed) glfwSetWindowSize(w, width, height);\n#else\n        if (global_state.is_wayland && glfwWaylandSetTitlebarHidden) {\n            bool titlebar_only = (new_state.hide_window_decorations & 2) != 0;\n            glfwWaylandSetTitlebarHidden(w, titlebar_only);\n        }\n        if (window_decorations_changed) {\n            bool hide_window_decorations = new_state.hide_window_decorations & 1;\n            glfwSetWindowAttrib(w, GLFW_DECORATED, !hide_window_decorations);\n            glfwSetWindowSize(w, width, height);\n        }\n        glfwSetWindowBlur(w, new_state.background_blur);\n        if (global_state.is_wayland) {\n            if (glfwWaylandSetTitlebarColor) glfwWaylandSetTitlebarColor(w, new_state.color, new_state.use_system_color);\n        }\n#endif\n}\n\nvoid\nset_os_window_chrome(OSWindow *w) {\n    if (!w->handle || w->is_layer_shell) return;\n    color_type bg = OPT(background);\n    if (w->num_tabs > w->active_tab) {\n        Tab *tab = w->tabs + w->active_tab;\n        if (tab->num_windows > tab->active_window) {\n            Window *window = tab->windows + tab->active_window;\n            ColorProfile *c;\n            if (window->render_data.screen && (c=window->render_data.screen->color_profile)) {\n                bg = colorprofile_to_color(c, c->overridden.default_bg, c->configured.default_bg).rgb;\n            }\n        }\n    }\n\n    WindowChromeState new_state;\n    init_window_chrome_state(&new_state, bg, effective_os_window_alpha(w));\n    if (memcmp(&new_state, &w->last_window_chrome, sizeof(WindowChromeState)) != 0) {\n        int width, height;\n        glfwGetWindowSize(w->handle, &width, &height);\n        bool window_decorations_changed = new_state.hide_window_decorations != w->last_window_chrome.hide_window_decorations;\n        apply_window_chrome_state(w->handle, new_state, width, height, window_decorations_changed);\n        w->last_window_chrome = new_state;\n    }\n}\n\nstatic PyObject*\nnative_window_handle(GLFWwindow *w) {\n#ifdef __APPLE__\n    void *ans = glfwGetCocoaWindow(w);\n    return PyLong_FromVoidPtr(ans);\n#endif\n    if (glfwGetX11Window) return PyLong_FromUnsignedLong(glfwGetX11Window(w));\n    return Py_None;\n}\n\nstatic PyObject* edge_spacing_func = NULL;\n\nstatic double\nedge_spacing(GLFWEdge which) {\n    const char* edge = \"top\";\n    switch(which) {\n        case GLFW_EDGE_TOP: edge = \"top\"; break;\n        case GLFW_EDGE_BOTTOM: edge = \"bottom\"; break;\n        case GLFW_EDGE_LEFT: edge = \"left\"; break;\n        case GLFW_EDGE_RIGHT: edge = \"right\"; break;\n        case GLFW_EDGE_CENTER: case GLFW_EDGE_NONE: case GLFW_EDGE_CENTER_SIZED: return 0;\n    }\n    if (!edge_spacing_func) {\n        log_error(\"Attempt to call edge_spacing() without first setting edge_spacing_func\");\n        return 100;\n    }\n    RAII_PyObject(ret, PyObject_CallFunction(edge_spacing_func, \"s\", edge));\n    if (!ret) { PyErr_Print(); return 100; }\n    if (!PyFloat_Check(ret)) { log_error(\"edge_spacing_func() return something other than a float\"); return 100; }\n    return PyFloat_AsDouble(ret);\n}\n\nstatic void\ncalculate_layer_shell_window_size(\n    GLFWwindow *window, float xscale, float yscale, unsigned *cell_width, unsigned *cell_height, double *left_edge_spacing, double *top_edge_spacing, double *right_edge_spacing, double *bottom_edge_spacing) {\n    OSWindow *os_window = os_window_for_glfw_window(window);\n    double xdpi, ydpi;\n    dpi_from_scale(xscale, yscale, &xdpi, &ydpi);\n    FONTS_DATA_HANDLE fonts_data = load_fonts_data(os_window ? os_window->fonts_data->font_sz_in_pts : OPT(font_size), xdpi, ydpi);\n    *cell_width = fonts_data->fcm.cell_width; *cell_height = fonts_data->fcm.cell_height;\n    double x_factor = xdpi / 72., y_factor = ydpi / 72.;\n    *left_edge_spacing = edge_spacing(GLFW_EDGE_LEFT) * x_factor;\n    *top_edge_spacing = edge_spacing(GLFW_EDGE_TOP) * y_factor;\n    *right_edge_spacing = edge_spacing(GLFW_EDGE_RIGHT) * x_factor;\n    *bottom_edge_spacing = edge_spacing(GLFW_EDGE_BOTTOM) * y_factor;\n}\n\nstatic PyObject*\nlayer_shell_config_to_python(const GLFWLayerShellConfig *c) {\n    RAII_PyObject(ans, PyDict_New()); if (!ans) return ans;\n#define fl(x) PyLong_FromLong((long)x)\n#define fu(x) PyLong_FromUnsignedLong((unsigned long)x)\n#define b(x) Py_NewRef(x ? Py_True : Py_False)\n#define A(attr, convert) RAII_PyObject(attr, convert(c->attr)); if (!attr) return NULL; if (PyDict_SetItemString(ans, #attr, attr) != 0) return NULL;\n    A(type, fl);\n    A(output_name, PyUnicode_FromString);\n    A(edge, fl);\n    A(focus_policy, fl);\n    A(x_size_in_cells, fu);\n    A(y_size_in_cells, fu);\n    A(x_size_in_pixels, fu);\n    A(y_size_in_pixels, fu);\n    A(requested_top_margin, fl);\n    A(requested_left_margin, fl);\n    A(requested_bottom_margin, fl);\n    A(requested_right_margin, fl);\n    A(requested_exclusive_zone, fl);\n    A(hide_on_focus_loss, b)\n    A(override_exclusive_zone, b);\n#undef A\n#undef fl\n#undef fu\n#undef b\n    return Py_NewRef(ans);\n}\n\nstatic bool\nlayer_shell_config_from_python(PyObject *p, GLFWLayerShellConfig *ans) {\n    memset(ans, 0, sizeof(GLFWLayerShellConfig));\n    ans->size_callback = calculate_layer_shell_window_size;\n#define A(attr, type_check, convert) RAII_PyObject(attr, PyObject_GetAttrString(p, #attr)); if (attr == NULL) return false; if (!type_check(attr)) { PyErr_SetString(PyExc_TypeError, #attr \" not of the correct type\"); return false; }; ans->attr = convert(attr);\n    A(type, PyLong_Check, PyLong_AsLong);\n    A(edge, PyLong_Check, PyLong_AsLong);\n    A(focus_policy, PyLong_Check, PyLong_AsLong);\n    A(x_size_in_cells, PyLong_Check, PyLong_AsUnsignedLong);\n    A(y_size_in_cells, PyLong_Check, PyLong_AsUnsignedLong);\n    A(x_size_in_pixels, PyLong_Check, PyLong_AsUnsignedLong);\n    A(y_size_in_pixels, PyLong_Check, PyLong_AsUnsignedLong);\n    A(requested_top_margin, PyLong_Check, PyLong_AsLong);\n    A(requested_left_margin, PyLong_Check, PyLong_AsLong);\n    A(requested_bottom_margin, PyLong_Check, PyLong_AsLong);\n    A(requested_right_margin, PyLong_Check, PyLong_AsLong);\n    A(requested_exclusive_zone, PyLong_Check, PyLong_AsLong);\n    A(override_exclusive_zone, PyBool_Check, PyLong_AsLong);\n    A(hide_on_focus_loss, PyBool_Check, PyLong_AsLong);\n#undef A\n#define A(attr) { \\\n    RAII_PyObject(attr, PyObject_GetAttrString(p, #attr)); if (attr == NULL) return false; \\\n    if (!PyUnicode_Check(attr)) { PyErr_SetString(PyExc_TypeError, #attr \" not a string\"); return false; };\\\n    Py_ssize_t sz; const char *t = PyUnicode_AsUTF8AndSize(attr, &sz); \\\n    if (sz > (ssize_t)sizeof(ans->attr)-1) { PyErr_Format(PyExc_ValueError, \"%s: %s is too long\", #attr, t); return false; } \\\n    memcpy(ans->attr, t, sz); }\n\n    A(output_name);\n    return true;\n#undef A\n}\n\nstatic void\nos_window_update_size_increments(OSWindow *window) {\n    if (OPT(resize_in_steps)) {\n        if (window->handle && window->fonts_data) glfwSetWindowSizeIncrements(\n                window->handle, window->fonts_data->fcm.cell_width, window->fonts_data->fcm.cell_height);\n    } else {\n        if (window->handle) glfwSetWindowSizeIncrements(\n                window->handle, GLFW_DONT_CARE, GLFW_DONT_CARE);\n    }\n}\n\n\nstatic PyObject*\ncreate_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {\n    int x = INT_MIN, y = INT_MIN, window_state = WINDOW_NORMAL, disallow_override_title = 0;\n    char *title, *wm_class_class, *wm_class_name;\n    PyObject *optional_window_state = NULL, *load_programs = NULL, *get_window_size, *pre_show_callback, *optional_x = NULL, *optional_y = NULL, *layer_shell_config = NULL;\n    static const char* kwlist[] = {\"get_window_size\", \"pre_show_callback\", \"title\", \"wm_class_name\", \"wm_class_class\", \"window_state\", \"load_programs\", \"x\", \"y\", \"disallow_override_title\", \"layer_shell_config\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"OOsss|OOOOpO\", (char**)kwlist,\n        &get_window_size, &pre_show_callback, &title, &wm_class_name, &wm_class_class, &optional_window_state, &load_programs, &optional_x, &optional_y, &disallow_override_title, &layer_shell_config)) return NULL;\n    GLFWLayerShellConfig *lsc = NULL, lsc_stack = {0};\n    if (optional_window_state && optional_window_state != Py_None) {\n        if (!PyLong_Check(optional_window_state)) { PyErr_SetString(PyExc_TypeError, \"window_state must be an int\"); return NULL; }\n        window_state = (int) PyLong_AsLong(optional_window_state);\n    }\n    if (layer_shell_config && layer_shell_config != Py_None ) {\n        if (!glfwIsLayerShellSupported()) {\n            PyErr_SetString(PyExc_RuntimeError, \"The window manager/compositor does not support the primitives needed to make panels.\");\n            return NULL;\n        }\n        lsc = &lsc_stack;\n    } else {\n        if (optional_x && optional_x != Py_None) { if (!PyLong_Check(optional_x)) { PyErr_SetString(PyExc_TypeError, \"x must be an int\"); return NULL;} x = (int)PyLong_AsLong(optional_x); }\n        if (optional_y && optional_y != Py_None) { if (!PyLong_Check(optional_y)) { PyErr_SetString(PyExc_TypeError, \"y must be an int\"); return NULL;} y = (int)PyLong_AsLong(optional_y); }\n        if (window_state < WINDOW_NORMAL || window_state > WINDOW_HIDDEN) window_state = WINDOW_NORMAL;\n    }\n    if (PyErr_Occurred()) return NULL;\n    if (lsc && window_state != WINDOW_HIDDEN) window_state = WINDOW_NORMAL;\n\n    static bool is_first_window = true;\n    if (is_first_window) {\n#ifndef __APPLE__\n        glfwConfigureMomentumScroller(OPT(momentum_scroll), -1, -1, 0);\n#endif\n        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_REQUIRED_VERSION_MAJOR);\n        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_REQUIRED_VERSION_MINOR);\n        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);\n        // We don't use depth and stencil buffers\n        glfwWindowHint(GLFW_DEPTH_BITS, 0);\n        glfwWindowHint(GLFW_STENCIL_BITS, 0);\n        glfwSetApplicationCloseCallback(application_close_requested_callback);\n        glfwSetCurrentSelectionCallback(get_current_selection);\n        glfwSetHasCurrentSelectionCallback(has_current_selection);\n        glfwSetIMECursorPositionCallback(get_ime_cursor_position);\n        glfwSetSystemColorThemeChangeCallback(on_system_color_scheme_change);\n        glfwSetClipboardLostCallback(on_clipboard_lost);\n        // Request SRGB output buffer\n        // Prevents kitty from starting on Wayland + NVIDIA, sigh: https://github.com/kovidgoyal/kitty/issues/7021\n        // Remove after https://github.com/NVIDIA/egl-wayland/issues/85 is fixed.\n        // Also apparently mesa has introduced a bug with sRGB surfaces and Wayland.\n        // Sigh. Wayland is such a pile of steaming crap.\n        // See https://github.com/kovidgoyal/kitty/issues/7174#issuecomment-2000033873\n        // GL_FRAMEBUFFER_SRGB works anyway without this on Wayland.\n        if (!global_state.is_wayland) glfwWindowHint(GLFW_SRGB_CAPABLE, true);\n#ifdef __APPLE__\n        cocoa_set_activation_policy(OPT(macos_hide_from_tasks) || lsc != NULL);\n        glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);\n        glfwSetApplicationShouldHandleReopen(on_application_reopen);\n        glfwSetApplicationWillFinishLaunching(cocoa_application_lifecycle_event);\n#endif\n    }\n    if (OPT(hide_window_decorations) & 1) glfwWindowHint(GLFW_DECORATED, false);\n\n    const bool set_blur = OPT(background_blur) > 0 && OPT(background_opacity) < 1.f;\n    glfwWindowHint(GLFW_BLUR_RADIUS, set_blur ? OPT(background_blur) : 0);\n#ifdef __APPLE__\n    glfwWindowHint(GLFW_COCOA_COLOR_SPACE, OPT(macos_colorspace));\n#else\n    glfwWindowHintString(GLFW_X11_INSTANCE_NAME, wm_class_name);\n    glfwWindowHintString(GLFW_X11_CLASS_NAME, wm_class_class);\n    glfwWindowHintString(GLFW_WAYLAND_APP_ID, wm_class_class);\n#endif\n\n    if (global_state.num_os_windows >= MAX_CHILDREN) {\n        PyErr_SetString(PyExc_ValueError, \"Too many windows\");\n        return NULL;\n    }\n    bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) || OPT(dynamic_background_opacity);\n    glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);\n    uint32_t bgcolor = OPT(background);\n    uint32_t bgalpha = (uint32_t)((MAX(0.f, MIN((OPT(background_opacity) * 255), 255.f))));\n    glfwWindowHint(GLFW_WAYLAND_BGCOLOR, ((bgalpha & 0xff) << 24) | bgcolor);\n    // We use a temp window to avoid the need to set the window size after\n    // creation, which causes a resize event and all the associated processing.\n    // The temp window is used to get the DPI. On Wayland no temp window can be\n    // used, so start with window visible unless hidden window requested.\n    GLFWwindow *common_context = global_state.num_os_windows ? global_state.os_windows[0].handle : NULL;\n    GLFWwindow *temp_window = NULL;\n    glfwWindowHint(GLFW_VISIBLE, window_state != WINDOW_HIDDEN && global_state.is_wayland);\n    float xscale, yscale;\n    double xdpi, ydpi;\n    if (global_state.is_wayland) {\n        // Cannot use temp window on Wayland as scale is only sent by compositor after window is displayed\n        get_window_content_scale(NULL, &xscale, &yscale, &xdpi, &ydpi);\n        for (unsigned i = 0; i < global_state.num_os_windows; i++) {\n            OSWindow *osw = global_state.os_windows + i;\n            if (osw->handle && glfwGetWindowAttrib(osw->handle, GLFW_FOCUSED)) {\n                get_window_content_scale(osw->handle, &xscale, &yscale, &xdpi, &ydpi);\n                break;\n            }\n        }\n    } else {\n#define glfw_failure { \\\n        PyErr_Format(PyExc_OSError, \"Failed to create GLFWwindow. This usually happens because of old/broken OpenGL drivers. kitty requires working OpenGL %d.%d drivers.\", OPENGL_REQUIRED_VERSION_MAJOR, OPENGL_REQUIRED_VERSION_MINOR); \\\n        return NULL; }\n\n        temp_window = glfwCreateWindow(640, 480, \"temp\", NULL, common_context, NULL);\n        if (temp_window == NULL) glfw_failure;\n        get_window_content_scale(temp_window, &xscale, &yscale, &xdpi, &ydpi);\n    }\n    FONTS_DATA_HANDLE fonts_data = load_fonts_data(OPT(font_size), xdpi, ydpi);\n    PyObject *ret = PyObject_CallFunction(get_window_size, \"IIddff\", fonts_data->fcm.cell_width, fonts_data->fcm.cell_height, fonts_data->logical_dpi_x, fonts_data->logical_dpi_y, xscale, yscale);\n    if (ret == NULL) return NULL;\n    int width = PyLong_AsLong(PyTuple_GET_ITEM(ret, 0)), height = PyLong_AsLong(PyTuple_GET_ITEM(ret, 1));\n    Py_CLEAR(ret);\n    if (lsc) {\n        if (!layer_shell_config_from_python(layer_shell_config, lsc)) return NULL;\n        lsc->expected.xscale = xscale; lsc->expected.yscale = yscale;\n    }\n    GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, temp_window ? temp_window : common_context, lsc);\n    if (temp_window) { glfwDestroyWindow(temp_window); temp_window = NULL; }\n    if (glfw_window == NULL) glfw_failure;\n#undef glfw_failure\n    // Set titlebar-only mode before the window becomes visible\n    if (global_state.is_wayland && (OPT(hide_window_decorations) & 2) && glfwWaylandSetTitlebarHidden) {\n        glfwWaylandSetTitlebarHidden(glfw_window, true);\n    }\n    glfwMakeContextCurrent(glfw_window);\n    if (is_first_window) gl_init();\n    bool is_semi_transparent = glfwGetWindowAttrib(glfw_window, GLFW_TRANSPARENT_FRAMEBUFFER);\n    // blank the window once so that there is no initial flash of color\n    // changing, in case the background color is not black\n    blank_canvas(is_semi_transparent ? OPT(background_opacity) : 1.0f, OPT(background), true);\n    apply_swap_interval(-1);\n    // On Wayland the initial swap is allowed only after the first XDG configure event\n    if (glfwAreSwapsAllowed(glfw_window)) glfwSwapBuffers(glfw_window);\n    glfwSetInputMode(glfw_window, GLFW_LOCK_KEY_MODS, true);\n    PyObject *pret = PyObject_CallFunction(pre_show_callback, \"N\", native_window_handle(glfw_window));\n    if (pret == NULL) return NULL;\n    Py_DECREF(pret);\n    if (x != INT_MIN && y != INT_MIN) glfwSetWindowPos(glfw_window, x, y);\n    if (!global_state.is_apple && !global_state.is_wayland && window_state != WINDOW_HIDDEN) glfwShowWindow(glfw_window, false);\n    if (global_state.is_wayland || global_state.is_apple) {\n        float n_xscale, n_yscale;\n        double n_xdpi, n_ydpi;\n        get_window_content_scale(glfw_window, &n_xscale, &n_yscale, &n_xdpi, &n_ydpi);\n        if (n_xdpi != xdpi || n_ydpi != ydpi || lsc) {\n            // this can happen if the window is moved by the OS to a different monitor when shown or with fractional scales on Wayland\n            // it can also happen with layer shell windows if the callback is\n            // called before the window is fully created\n            xdpi = n_xdpi; ydpi = n_ydpi;\n            fonts_data = load_fonts_data(OPT(font_size), xdpi, ydpi);\n        }\n    }\n    if (is_first_window) {\n        PyObject *ret = PyObject_CallNoArgs(load_programs);\n        if (ret == NULL) return NULL;\n        Py_DECREF(ret);\n        get_platform_dependent_config_values(glfw_window);\n        if (!global_state.supports_framebuffer_srgb) {\n            log_error(\"The OpenGL drivers dont support GL_FRAMEBUFFER_SRGB this will cause a small rendering performance penalty\");\n        }\n        is_first_window = false;\n    }\n    OSWindow *w = add_os_window();\n    w->handle = glfw_window;\n    w->disallow_title_changes = disallow_override_title;\n    if (lsc != NULL) {\n        w->is_layer_shell = true;\n        w->hide_on_focus_loss = lsc->hide_on_focus_loss;\n    }\n    update_os_window_references();\n    if (!w->is_layer_shell || (global_state.is_apple && w->is_layer_shell && lsc->focus_policy == GLFW_FOCUS_EXCLUSIVE)) {\n        for (size_t i = 0; i < global_state.num_os_windows; i++) {\n            // On some platforms (macOS) newly created windows don't get the initial focus in event\n            OSWindow *q = global_state.os_windows + i;\n            q->is_focused = q == w ? true : false;\n        }\n    }\n    w->fonts_data = fonts_data;\n    w->shown_once = true;\n    w->last_focused_counter = ++focus_counter;\n    os_window_update_size_increments(w);\n#ifdef __APPLE__\n    if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option);\n    glfwSetCocoaToggleFullscreenIntercept(glfw_window, intercept_cocoa_fullscreen);\n    glfwCocoaSetWindowResizeCallback(glfw_window, cocoa_os_window_resized);\n#endif\n    send_prerendered_sprites_for_window(w);\n    if (logo.pixels && logo.width && logo.height && (!lsc || !global_state.is_wayland)) glfwSetWindowIcon(glfw_window, 1, &logo);\n    set_glfw_mouse_pointer_shape_in_window(glfw_window, OPT(default_pointer_shape));\n    update_os_window_viewport(w, false);\n    glfwSetWindowPosCallback(glfw_window, window_pos_callback);\n    // missing size callback\n    glfwSetWindowCloseCallback(glfw_window, window_close_callback);\n    glfwSetWindowRefreshCallback(glfw_window, refresh_callback);\n    glfwSetWindowFocusCallback(glfw_window, window_focus_callback);\n    glfwSetWindowOcclusionCallback(glfw_window, window_occlusion_callback);\n    glfwSetWindowIconifyCallback(glfw_window, window_iconify_callback);\n    // missing maximize/restore callback\n    glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);\n    glfwSetLiveResizeCallback(glfw_window, live_resize_callback);\n    glfwSetWindowContentScaleCallback(glfw_window, dpi_change_callback);\n    glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);\n    glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);\n    glfwSetCursorEnterCallback(glfw_window, cursor_enter_callback);\n    glfwSetScrollCallback(glfw_window, scroll_callback);\n    glfwSetKeyboardCallback(glfw_window, key_callback);\n\n    glfwSetDragSourceCallback(glfw_window, drag_source_callback);\n    glfwSetDropEventCallback(glfw_window, on_drop);\n    monotonic_t now = monotonic();\n    w->is_focused = true;\n    w->cursor_blink_zero_time = now;\n    w->last_mouse_activity_at = now;\n    w->mouse_activate_deadline = -1;\n    w->mouse_show_threshold = 0;\n    w->background_opacity.supports_transparency = is_semi_transparent;\n    if (want_semi_transparent && !w->background_opacity.supports_transparency) {\n        static bool warned = false;\n        if (!warned) {\n            log_error(\"Failed to enable transparency. This happens when your desktop environment does not support compositing.\");\n            warned = true;\n        }\n    }\n    init_window_chrome_state(&w->last_window_chrome, OPT(background), effective_os_window_alpha(w));\n    if (w->is_layer_shell) {\n        if (global_state.is_apple) set_layer_shell_config_for(w, lsc);\n    } else apply_window_chrome_state(\n            w->handle, w->last_window_chrome, width, height, global_state.is_apple ? OPT(hide_window_decorations) != 0 : false);\n    // Update window state\n    // We do not call glfwWindowHint to set GLFW_MAXIMIZED before the window is created.\n    // That would cause the window to be set to maximize immediately after creation and use the wrong initial size when restored.\n    if (window_state != WINDOW_NORMAL) change_state_for_os_window(w, window_state);\n#ifdef __APPLE__\n    // macOS: Show the window after it is ready\n    if (window_state != WINDOW_HIDDEN) glfwShowWindow(glfw_window, false);\n#endif\n    w->redraw_count = 1;\n    debug(\"OS Window created\\n\");\n    return PyLong_FromUnsignedLongLong(w->id);\n}\n\nvoid\non_os_window_font_size_change(OSWindow *os_window, double new_sz) {\n    double xdpi, ydpi; float xscale, yscale;\n    get_os_window_content_scale(os_window, &xdpi, &ydpi, &xscale, &yscale);\n    os_window->fonts_data = load_fonts_data(new_sz, xdpi, ydpi);\n    os_window_update_size_increments(os_window);\n    if (os_window->is_layer_shell) set_layer_shell_config_for(os_window, NULL);\n}\n\n#ifdef __APPLE__\nstatic bool\nwindow_in_same_cocoa_workspace(void *w, size_t *source_workspaces, size_t source_workspace_count) {\n    static size_t workspaces[64];\n    size_t workspace_count = cocoa_get_workspace_ids(w, workspaces, arraysz(workspaces));\n    for (size_t i = 0; i < workspace_count; i++) {\n        for (size_t s = 0; s < source_workspace_count; s++) {\n            if (source_workspaces[s] == workspaces[i]) return true;\n        }\n    }\n    return false;\n}\n\nstatic void\ncocoa_focus_last_window(id_type source_window_id, size_t *source_workspaces, size_t source_workspace_count) {\n    id_type highest_focus_number = 0;\n    OSWindow *window_to_focus = NULL;\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = global_state.os_windows + i;\n        if (\n                w->id != source_window_id && w->handle && w->shown_once &&\n                w->last_focused_counter >= highest_focus_number && !glfwGetWindowAttrib(w->handle, GLFW_ICONIFIED) &&\n                (!source_workspace_count || window_in_same_cocoa_workspace(glfwGetCocoaWindow(w->handle), source_workspaces, source_workspace_count))\n        ) {\n            highest_focus_number = w->last_focused_counter;\n            window_to_focus = w;\n        }\n    }\n    if (window_to_focus) glfwFocusWindow(window_to_focus->handle);\n}\n#endif\n\nvoid\ndestroy_os_window(OSWindow *w) {\n#ifdef __APPLE__\n    static size_t source_workspaces[64];\n    size_t source_workspace_count = 0;\n#endif\n    if (w->handle) {\n#ifdef __APPLE__\n        source_workspace_count = cocoa_get_workspace_ids(glfwGetCocoaWindow(w->handle), source_workspaces, arraysz(source_workspaces));\n#endif\n        // Ensure mouse cursor is visible and reset to default shape, needed on macOS\n        show_mouse_cursor(w->handle);\n        glfwSetCursor(w->handle, NULL);\n        glfwDestroyWindow(w->handle);\n    }\n    w->handle = NULL;\n#ifdef __APPLE__\n    // On macOS when closing a window, any other existing windows belonging to the same application do not\n    // automatically get focus, so we do it manually.\n    cocoa_focus_last_window(w->id, source_workspaces, source_workspace_count);\n#endif\n}\n\nvoid\nfocus_os_window(OSWindow *w, bool also_raise, const char *activation_token) {\n    if (w->handle) {\n#ifdef __APPLE__\n        if (!also_raise) cocoa_focus_window(glfwGetCocoaWindow(w->handle));\n        else glfwFocusWindow(w->handle);\n        (void)activation_token;\n#else\n        if (global_state.is_wayland && activation_token && activation_token[0] && also_raise) {\n            glfwWaylandActivateWindow(w->handle, activation_token);\n            return;\n        }\n        glfwFocusWindow(w->handle);\n#endif\n    }\n}\n\n// Global functions {{{\nstatic void\nerror_callback(int error, const char* description) {\n    log_error(\"[glfw error %d]: %s\", error, description);\n}\n\n\n#ifndef __APPLE__\nstatic PyObject *dbus_notification_callback = NULL;\n\nstatic PyObject*\ndbus_set_notification_callback(PyObject *self UNUSED, PyObject *callback) {\n    Py_CLEAR(dbus_notification_callback);\n    if (callback && callback != Py_None) {\n        dbus_notification_callback = callback; Py_INCREF(callback);\n        GLFWDBUSNotificationData d = {.timeout=-99999, .urgency=255};\n        if (!glfwDBusUserNotify) {\n            PyErr_SetString(PyExc_RuntimeError, \"Failed to load glfwDBusUserNotify, did you call glfw_init?\");\n            return NULL;\n        }\n        glfwDBusUserNotify(&d, NULL, NULL);\n    }\n    Py_RETURN_NONE;\n}\n\n#define send_dbus_notification_event_to_python(event_type, a, b) { \\\n    if (dbus_notification_callback) { \\\n        const char call_args_fmt[] = {'s', \\\n            _Generic((a), unsigned long : 'k', unsigned long long : 'K'), _Generic((b), unsigned long : 'k', const char* : 's'), '\\0' }; \\\n        RAII_PyObject(ret, PyObject_CallFunction(dbus_notification_callback, call_args_fmt, event_type, a, b)); \\\n        if (!ret) PyErr_Print(); \\\n    } \\\n}\n\n\nstatic void\ndbus_user_notification_activated(uint32_t notification_id, int type, const char* action) {\n    unsigned long nid = notification_id;\n    const char *stype = \"activated\";\n    switch (type) {\n        case 0: stype = \"closed\"; break;\n        case 1: stype = \"activation_token\"; break;\n        case -1: stype = \"capabilities\"; break;\n    }\n    send_dbus_notification_event_to_python(stype, nid, action);\n}\n#endif\n\nstatic PyObject*\nglfw_init(PyObject UNUSED *self, PyObject *args) {\n    const char* path;\n    int debug_keyboard = 0, debug_rendering = 0, wayland_enable_ime = 0;\n    PyObject *edge_sf;\n    if (!PyArg_ParseTuple(args, \"sO|ppp\", &path, &edge_sf, &debug_keyboard, &debug_rendering, &wayland_enable_ime)) return NULL;\n    if (!PyCallable_Check(edge_sf)) { PyErr_SetString(PyExc_TypeError, \"edge_spacing_func must be a callable\"); return NULL; }\n    Py_CLEAR(edge_spacing_func);\n#ifdef __APPLE__\n    cocoa_set_uncaught_exception_handler();\n#endif\n    const char* err = load_glfw(path);\n    if (err) { PyErr_SetString(PyExc_RuntimeError, err); return NULL; }\n    glfwSetErrorCallback(error_callback);\n    glfwInitHint(GLFW_DEBUG_KEYBOARD, debug_keyboard);\n    glfwInitHint(GLFW_DEBUG_RENDERING, debug_rendering);\n    OPT(debug_keyboard) = debug_keyboard != 0;\n    glfwInitHint(GLFW_WAYLAND_IME, wayland_enable_ime != 0);\n#ifdef __APPLE__\n    glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, 0);\n    glfwInitHint(GLFW_COCOA_MENUBAR, 0);\n#else\n    if (glfwDBusSetUserNotificationHandler) {\n        glfwDBusSetUserNotificationHandler(dbus_user_notification_activated);\n    }\n#endif\n    bool supports_window_occlusion = false;\n    bool ok = glfwInit(monotonic_start_time, &supports_window_occlusion);\n    if (ok) {\n#ifdef __APPLE__\n        glfwSetCocoaURLOpenCallback(apple_url_open_callback);\n#else\n        glfwSetDrawTextFunction(draw_text_callback);\n#endif\n        get_window_dpi(NULL, &global_state.default_dpi.x, &global_state.default_dpi.y);\n        edge_spacing_func = edge_sf; Py_INCREF(edge_spacing_func);\n    }\n    return Py_BuildValue(\"OO\", ok ? Py_True : Py_False, supports_window_occlusion ? Py_True : Py_False);\n}\n\nstatic PyObject*\nglfw_terminate(PYNOARG) {\n    for (size_t i = 0; i < arraysz(cursors); i++) {\n        if (cursors[i].is_custom && cursors[i].glfw) {\n            glfwDestroyCursor(cursors[i].glfw);\n            cursors[i] = (mouse_cursor){0};\n        }\n    }\n    glfwTerminate();\n    Py_CLEAR(edge_spacing_func);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nget_physical_dpi(GLFWmonitor *m) {\n    int width = 0, height = 0;\n    glfwGetMonitorPhysicalSize(m, &width, &height);\n    if (width == 0 || height == 0) { PyErr_SetString(PyExc_ValueError, \"Failed to get primary monitor size\"); return NULL; }\n    const GLFWvidmode *vm = glfwGetVideoMode(m);\n    if (vm == NULL) { PyErr_SetString(PyExc_ValueError, \"Failed to get video mode for monitor\"); return NULL; }\n    float dpix = (float)(vm->width / (width / 25.4));\n    float dpiy = (float)(vm->height / (height / 25.4));\n    return Py_BuildValue(\"ff\", dpix, dpiy);\n}\n\nstatic PyObject*\nglfw_get_physical_dpi(PYNOARG) {\n    GLFWmonitor *m = glfwGetPrimaryMonitor();\n    if (m == NULL) { PyErr_SetString(PyExc_ValueError, \"Failed to get primary monitor\"); return NULL; }\n    return get_physical_dpi(m);\n}\n\nstatic PyObject*\nglfw_get_system_color_theme(PyObject UNUSED *self, PyObject *args) {\n    int query_if_unintialized = 1;\n    if (!PyArg_ParseTuple(args, \"|p\", &query_if_unintialized)) return NULL;\n    if (!glfwGetCurrentSystemColorTheme) {\n        PyErr_SetString(PyExc_RuntimeError, \"must initialize GFLW before calling this function\"); return NULL;\n    }\n    const char *which = appearance_name(glfwGetCurrentSystemColorTheme(query_if_unintialized));\n    return PyUnicode_FromString(which);\n}\n\nstatic PyObject*\nglfw_get_key_name(PyObject UNUSED *self, PyObject *args) {\n    int key, native_key;\n    if (!PyArg_ParseTuple(args, \"ii\", &key, &native_key)) return NULL;\n    if (key) {\n        switch (key) {\n            /* start glfw functional key names (auto generated by gen-key-constants.py do not edit) */\n            case GLFW_FKEY_ESCAPE: return PyUnicode_FromString(\"escape\");\n            case GLFW_FKEY_ENTER: return PyUnicode_FromString(\"enter\");\n            case GLFW_FKEY_TAB: return PyUnicode_FromString(\"tab\");\n            case GLFW_FKEY_BACKSPACE: return PyUnicode_FromString(\"backspace\");\n            case GLFW_FKEY_INSERT: return PyUnicode_FromString(\"insert\");\n            case GLFW_FKEY_DELETE: return PyUnicode_FromString(\"delete\");\n            case GLFW_FKEY_LEFT: return PyUnicode_FromString(\"left\");\n            case GLFW_FKEY_RIGHT: return PyUnicode_FromString(\"right\");\n            case GLFW_FKEY_UP: return PyUnicode_FromString(\"up\");\n            case GLFW_FKEY_DOWN: return PyUnicode_FromString(\"down\");\n            case GLFW_FKEY_PAGE_UP: return PyUnicode_FromString(\"page_up\");\n            case GLFW_FKEY_PAGE_DOWN: return PyUnicode_FromString(\"page_down\");\n            case GLFW_FKEY_HOME: return PyUnicode_FromString(\"home\");\n            case GLFW_FKEY_END: return PyUnicode_FromString(\"end\");\n            case GLFW_FKEY_CAPS_LOCK: return PyUnicode_FromString(\"caps_lock\");\n            case GLFW_FKEY_SCROLL_LOCK: return PyUnicode_FromString(\"scroll_lock\");\n            case GLFW_FKEY_NUM_LOCK: return PyUnicode_FromString(\"num_lock\");\n            case GLFW_FKEY_PRINT_SCREEN: return PyUnicode_FromString(\"print_screen\");\n            case GLFW_FKEY_PAUSE: return PyUnicode_FromString(\"pause\");\n            case GLFW_FKEY_MENU: return PyUnicode_FromString(\"menu\");\n            case GLFW_FKEY_F1: return PyUnicode_FromString(\"f1\");\n            case GLFW_FKEY_F2: return PyUnicode_FromString(\"f2\");\n            case GLFW_FKEY_F3: return PyUnicode_FromString(\"f3\");\n            case GLFW_FKEY_F4: return PyUnicode_FromString(\"f4\");\n            case GLFW_FKEY_F5: return PyUnicode_FromString(\"f5\");\n            case GLFW_FKEY_F6: return PyUnicode_FromString(\"f6\");\n            case GLFW_FKEY_F7: return PyUnicode_FromString(\"f7\");\n            case GLFW_FKEY_F8: return PyUnicode_FromString(\"f8\");\n            case GLFW_FKEY_F9: return PyUnicode_FromString(\"f9\");\n            case GLFW_FKEY_F10: return PyUnicode_FromString(\"f10\");\n            case GLFW_FKEY_F11: return PyUnicode_FromString(\"f11\");\n            case GLFW_FKEY_F12: return PyUnicode_FromString(\"f12\");\n            case GLFW_FKEY_F13: return PyUnicode_FromString(\"f13\");\n            case GLFW_FKEY_F14: return PyUnicode_FromString(\"f14\");\n            case GLFW_FKEY_F15: return PyUnicode_FromString(\"f15\");\n            case GLFW_FKEY_F16: return PyUnicode_FromString(\"f16\");\n            case GLFW_FKEY_F17: return PyUnicode_FromString(\"f17\");\n            case GLFW_FKEY_F18: return PyUnicode_FromString(\"f18\");\n            case GLFW_FKEY_F19: return PyUnicode_FromString(\"f19\");\n            case GLFW_FKEY_F20: return PyUnicode_FromString(\"f20\");\n            case GLFW_FKEY_F21: return PyUnicode_FromString(\"f21\");\n            case GLFW_FKEY_F22: return PyUnicode_FromString(\"f22\");\n            case GLFW_FKEY_F23: return PyUnicode_FromString(\"f23\");\n            case GLFW_FKEY_F24: return PyUnicode_FromString(\"f24\");\n            case GLFW_FKEY_F25: return PyUnicode_FromString(\"f25\");\n            case GLFW_FKEY_F26: return PyUnicode_FromString(\"f26\");\n            case GLFW_FKEY_F27: return PyUnicode_FromString(\"f27\");\n            case GLFW_FKEY_F28: return PyUnicode_FromString(\"f28\");\n            case GLFW_FKEY_F29: return PyUnicode_FromString(\"f29\");\n            case GLFW_FKEY_F30: return PyUnicode_FromString(\"f30\");\n            case GLFW_FKEY_F31: return PyUnicode_FromString(\"f31\");\n            case GLFW_FKEY_F32: return PyUnicode_FromString(\"f32\");\n            case GLFW_FKEY_F33: return PyUnicode_FromString(\"f33\");\n            case GLFW_FKEY_F34: return PyUnicode_FromString(\"f34\");\n            case GLFW_FKEY_F35: return PyUnicode_FromString(\"f35\");\n            case GLFW_FKEY_KP_0: return PyUnicode_FromString(\"kp_0\");\n            case GLFW_FKEY_KP_1: return PyUnicode_FromString(\"kp_1\");\n            case GLFW_FKEY_KP_2: return PyUnicode_FromString(\"kp_2\");\n            case GLFW_FKEY_KP_3: return PyUnicode_FromString(\"kp_3\");\n            case GLFW_FKEY_KP_4: return PyUnicode_FromString(\"kp_4\");\n            case GLFW_FKEY_KP_5: return PyUnicode_FromString(\"kp_5\");\n            case GLFW_FKEY_KP_6: return PyUnicode_FromString(\"kp_6\");\n            case GLFW_FKEY_KP_7: return PyUnicode_FromString(\"kp_7\");\n            case GLFW_FKEY_KP_8: return PyUnicode_FromString(\"kp_8\");\n            case GLFW_FKEY_KP_9: return PyUnicode_FromString(\"kp_9\");\n            case GLFW_FKEY_KP_DECIMAL: return PyUnicode_FromString(\"kp_decimal\");\n            case GLFW_FKEY_KP_DIVIDE: return PyUnicode_FromString(\"kp_divide\");\n            case GLFW_FKEY_KP_MULTIPLY: return PyUnicode_FromString(\"kp_multiply\");\n            case GLFW_FKEY_KP_SUBTRACT: return PyUnicode_FromString(\"kp_subtract\");\n            case GLFW_FKEY_KP_ADD: return PyUnicode_FromString(\"kp_add\");\n            case GLFW_FKEY_KP_ENTER: return PyUnicode_FromString(\"kp_enter\");\n            case GLFW_FKEY_KP_EQUAL: return PyUnicode_FromString(\"kp_equal\");\n            case GLFW_FKEY_KP_SEPARATOR: return PyUnicode_FromString(\"kp_separator\");\n            case GLFW_FKEY_KP_LEFT: return PyUnicode_FromString(\"kp_left\");\n            case GLFW_FKEY_KP_RIGHT: return PyUnicode_FromString(\"kp_right\");\n            case GLFW_FKEY_KP_UP: return PyUnicode_FromString(\"kp_up\");\n            case GLFW_FKEY_KP_DOWN: return PyUnicode_FromString(\"kp_down\");\n            case GLFW_FKEY_KP_PAGE_UP: return PyUnicode_FromString(\"kp_page_up\");\n            case GLFW_FKEY_KP_PAGE_DOWN: return PyUnicode_FromString(\"kp_page_down\");\n            case GLFW_FKEY_KP_HOME: return PyUnicode_FromString(\"kp_home\");\n            case GLFW_FKEY_KP_END: return PyUnicode_FromString(\"kp_end\");\n            case GLFW_FKEY_KP_INSERT: return PyUnicode_FromString(\"kp_insert\");\n            case GLFW_FKEY_KP_DELETE: return PyUnicode_FromString(\"kp_delete\");\n            case GLFW_FKEY_KP_BEGIN: return PyUnicode_FromString(\"kp_begin\");\n            case GLFW_FKEY_MEDIA_PLAY: return PyUnicode_FromString(\"media_play\");\n            case GLFW_FKEY_MEDIA_PAUSE: return PyUnicode_FromString(\"media_pause\");\n            case GLFW_FKEY_MEDIA_PLAY_PAUSE: return PyUnicode_FromString(\"media_play_pause\");\n            case GLFW_FKEY_MEDIA_REVERSE: return PyUnicode_FromString(\"media_reverse\");\n            case GLFW_FKEY_MEDIA_STOP: return PyUnicode_FromString(\"media_stop\");\n            case GLFW_FKEY_MEDIA_FAST_FORWARD: return PyUnicode_FromString(\"media_fast_forward\");\n            case GLFW_FKEY_MEDIA_REWIND: return PyUnicode_FromString(\"media_rewind\");\n            case GLFW_FKEY_MEDIA_TRACK_NEXT: return PyUnicode_FromString(\"media_track_next\");\n            case GLFW_FKEY_MEDIA_TRACK_PREVIOUS: return PyUnicode_FromString(\"media_track_previous\");\n            case GLFW_FKEY_MEDIA_RECORD: return PyUnicode_FromString(\"media_record\");\n            case GLFW_FKEY_LOWER_VOLUME: return PyUnicode_FromString(\"lower_volume\");\n            case GLFW_FKEY_RAISE_VOLUME: return PyUnicode_FromString(\"raise_volume\");\n            case GLFW_FKEY_MUTE_VOLUME: return PyUnicode_FromString(\"mute_volume\");\n            case GLFW_FKEY_LEFT_SHIFT: return PyUnicode_FromString(\"left_shift\");\n            case GLFW_FKEY_LEFT_CONTROL: return PyUnicode_FromString(\"left_control\");\n            case GLFW_FKEY_LEFT_ALT: return PyUnicode_FromString(\"left_alt\");\n            case GLFW_FKEY_LEFT_SUPER: return PyUnicode_FromString(\"left_super\");\n            case GLFW_FKEY_LEFT_HYPER: return PyUnicode_FromString(\"left_hyper\");\n            case GLFW_FKEY_LEFT_META: return PyUnicode_FromString(\"left_meta\");\n            case GLFW_FKEY_RIGHT_SHIFT: return PyUnicode_FromString(\"right_shift\");\n            case GLFW_FKEY_RIGHT_CONTROL: return PyUnicode_FromString(\"right_control\");\n            case GLFW_FKEY_RIGHT_ALT: return PyUnicode_FromString(\"right_alt\");\n            case GLFW_FKEY_RIGHT_SUPER: return PyUnicode_FromString(\"right_super\");\n            case GLFW_FKEY_RIGHT_HYPER: return PyUnicode_FromString(\"right_hyper\");\n            case GLFW_FKEY_RIGHT_META: return PyUnicode_FromString(\"right_meta\");\n            case GLFW_FKEY_ISO_LEVEL3_SHIFT: return PyUnicode_FromString(\"iso_level3_shift\");\n            case GLFW_FKEY_ISO_LEVEL5_SHIFT: return PyUnicode_FromString(\"iso_level5_shift\");\n/* end glfw functional key names */\n        }\n        char buf[8] = {0};\n        encode_utf8(key, buf);\n        return PyUnicode_FromString(buf);\n    }\n    if (!glfwGetKeyName) {\n        return PyUnicode_FromFormat(\"0x%x\", native_key);\n    }\n    return Py_BuildValue(\"z\", glfwGetKeyName(key, native_key));\n}\n\n\nstatic PyObject*\nglfw_window_hint(PyObject UNUSED *self, PyObject *args) {\n    int key, val;\n    if (!PyArg_ParseTuple(args, \"ii\", &key, &val)) return NULL;\n    glfwWindowHint(key, val);\n    Py_RETURN_NONE;\n}\n\n\n// }}}\n\nstatic PyObject*\ntoggle_secure_input(PYNOARG) {\n#ifdef __APPLE__\n    cocoa_toggle_secure_keyboard_entry();\n#endif\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nmacos_cycle_through_os_windows(PyObject *self UNUSED, PyObject *backwards) {\n#ifdef __APPLE__\n    glfwCocoaCycleThroughOSWindows(PyObject_IsTrue(backwards));\n#else\n    (void)backwards;\n#endif\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\ncocoa_hide_app(PYNOARG) {\n#ifdef __APPLE__\n    cocoa_hide();\n#endif\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\ncocoa_hide_other_apps(PYNOARG) {\n#ifdef __APPLE__\n    cocoa_hide_others();\n#endif\n    Py_RETURN_NONE;\n}\n\nstatic void\nring_audio_bell(OSWindow *w) {\n    static monotonic_t last_bell_at = -1;\n    monotonic_t now = monotonic();\n    if (last_bell_at >= 0 && now - last_bell_at <= ms_to_monotonic_t(100ll)) return;\n    last_bell_at = now;\n#ifdef __APPLE__\n    (void)w;\n    cocoa_system_beep(OPT(bell_path));\n#else\n    if (OPT(bell_path)) play_canberra_sound(OPT(bell_path), \"kitty bell\", true, \"event\", OPT(bell_theme));\n    else {\n        if (!global_state.is_wayland || !glfwWaylandBeep(w ? w->handle : NULL)) play_canberra_sound(\n                \"bell\", \"kitty bell\", false, \"event\", OPT(bell_theme));\n    }\n#endif\n}\n\nstatic PyObject*\nring_bell(PyObject *self UNUSED, PyObject *args) {\n    unsigned long long os_window_id = 0;\n    if (!PyArg_ParseTuple(args, \"|K\", &os_window_id)) return NULL;\n    OSWindow *w = os_window_for_id(os_window_id);\n    ring_audio_bell(w);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nrequest_attention(PyObject *self UNUSED, PyObject *args) {\n    unsigned long long os_window_id;\n    if (!PyArg_ParseTuple(args, \"K\", &os_window_id)) return NULL;\n    OSWindow *w = os_window_for_id(os_window_id);\n    if (w && w->handle) glfwRequestWindowAttention(w->handle);\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\nget_content_scale_for_window(PYNOARG) {\n    OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;\n    float xscale, yscale;\n    glfwGetWindowContentScale(w->handle, &xscale, &yscale);\n    return Py_BuildValue(\"ff\", xscale, yscale);\n}\n\nstatic void\nactivation_token_callback(GLFWwindow *window UNUSED, const char *token, void *data) {\n    if (!token || !token[0]) {\n        token = \"\";\n        log_error(\"Wayland: Did not get activation token from compositor. Use a better compositor.\");\n    }\n    PyObject *ret = PyObject_CallFunction(data, \"s\", token);\n    if (ret == NULL) PyErr_Print();\n    else Py_DECREF(ret);\n    Py_CLEAR(data);\n}\n\nvoid\nrun_with_activation_token_in_os_window(OSWindow *w, PyObject *callback) {\n    if (global_state.is_wayland) {\n        Py_INCREF(callback);\n        glfwWaylandRunWithActivationToken(w->handle, activation_token_callback, callback);\n    }\n}\n\nstatic PyObject*\ntoggle_fullscreen(PyObject UNUSED *self, PyObject *args) {\n    id_type os_window_id = 0;\n    if (!PyArg_ParseTuple(args, \"|K\", &os_window_id)) return NULL;\n    OSWindow *w = os_window_id ? os_window_for_id(os_window_id) : current_os_window();\n    if (!w) Py_RETURN_NONE;\n    if (toggle_fullscreen_for_os_window(w)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\ntoggle_maximized(PyObject UNUSED *self, PyObject *args) {\n    id_type os_window_id = 0;\n    if (!PyArg_ParseTuple(args, \"|K\", &os_window_id)) return NULL;\n    OSWindow *w = os_window_id ? os_window_for_id(os_window_id) : current_os_window();\n    if (!w) Py_RETURN_NONE;\n    if (toggle_maximized_for_os_window(w)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\ncocoa_minimize_os_window(PyObject UNUSED *self, PyObject *args) {\n    id_type os_window_id = 0;\n    if (!PyArg_ParseTuple(args, \"|K\", &os_window_id)) return NULL;\n#ifdef __APPLE__\n    OSWindow *w = os_window_id ? os_window_for_id(os_window_id) : current_os_window();\n    if (!w || !w->handle || w->is_layer_shell) Py_RETURN_NONE;\n    if (!glfwGetCocoaWindow) { PyErr_SetString(PyExc_RuntimeError, \"Failed to load glfwGetCocoaWindow\"); return NULL; }\n    void *window = glfwGetCocoaWindow(w->handle);\n    if (!window) Py_RETURN_NONE;\n    cocoa_minimize(window);\n#else\n    PyErr_SetString(PyExc_RuntimeError, \"cocoa_minimize_os_window() is only supported on macOS\");\n    return NULL;\n#endif\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nchange_os_window_state(PyObject *self UNUSED, PyObject *args) {\n    int state;\n    id_type wid = 0;\n    if (!PyArg_ParseTuple(args, \"i|K\", &state, &wid)) return NULL;\n    OSWindow *w = wid ? os_window_for_id(wid) : current_os_window();\n    if (!w || !w->handle) Py_RETURN_NONE;\n    if (state < WINDOW_NORMAL || state > WINDOW_MINIMIZED) {\n        PyErr_SetString(PyExc_ValueError, \"Unknown window state\");\n        return NULL;\n    }\n    change_state_for_os_window(w, state);\n    Py_RETURN_NONE;\n}\n\nvoid\nrequest_window_attention(id_type kitty_window_id, bool audio_bell) {\n    OSWindow *w = os_window_for_kitty_window(kitty_window_id);\n    if (w) {\n        if (audio_bell) ring_audio_bell(w);\n        if (OPT(window_alert_on_bell)) glfwRequestWindowAttention(w->handle);\n#ifdef __APPLE__\n        if (OPT(macos_dock_badge_on_bell)) cocoa_set_dock_badge(\"!\");\n#endif\n        glfwPostEmptyEvent();\n    }\n}\n\nvoid\nset_os_window_title(OSWindow *w, const char *title) {\n    if (!title) {\n        if (global_state.is_wayland) glfwWaylandRedrawCSDWindowTitle(w->handle);\n        return;\n    }\n    static char buf[2048];\n    strip_csi_(title, buf, arraysz(buf));\n    glfwSetWindowTitle(w->handle, buf);\n}\n\nvoid\nhide_mouse(OSWindow *w) {\n    glfwSetInputMode(w->handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);\n    w->mouse_activate_deadline = -1;\n}\n\nbool\nis_mouse_hidden(OSWindow *w) {\n    return w->handle && glfwGetInputMode(w->handle, GLFW_CURSOR) == GLFW_CURSOR_HIDDEN;\n}\n\n\nvoid\nswap_window_buffers(OSWindow *os_window) {\n    if (glfwAreSwapsAllowed(os_window->handle)) {\n        glfwSwapBuffers(os_window->handle);\n        os_window->keep_rendering_till_swap = 0;\n    }\n}\n\nvoid\nwakeup_main_loop(void) {\n    glfwPostEmptyEvent();\n}\n\nbool\nshould_os_window_be_rendered(OSWindow* w) {\n    return (\n            glfwGetWindowAttrib(w->handle, GLFW_ICONIFIED)\n            || !glfwGetWindowAttrib(w->handle, GLFW_VISIBLE)\n            || glfwGetWindowAttrib(w->handle, GLFW_OCCLUDED)\n            || !glfwAreSwapsAllowed(w->handle)\n       ) ? false : true;\n}\n\nstatic PyObject*\nprimary_monitor_size(PYNOARG) {\n    GLFWmonitor* monitor = glfwGetPrimaryMonitor();\n    const GLFWvidmode* mode = glfwGetVideoMode(monitor);\n    if (mode == NULL) { PyErr_SetString(PyExc_ValueError, \"Failed to get video mode for primary monitor\"); return NULL; }\n    return Py_BuildValue(\"ii\", mode->width, mode->height);\n}\n\nstatic PyObject*\nget_monitor_workarea(PYNOARG) {\n    int count = 0;\n    GLFWmonitor **monitors = glfwGetMonitors(&count);\n    if (count <= 0 || !monitors) return PyTuple_New(0);\n    RAII_PyObject(result, PyTuple_New(count)); if (!result) return NULL;\n    for (int i = 0; i < count; i++) {\n        int xpos, ypos, width, height;\n        glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height);\n        PyObject *monitor_workarea = Py_BuildValue(\"iiii\", xpos, ypos, width, height);\n        if (!monitor_workarea) return NULL;\n        PyTuple_SET_ITEM(result, i, monitor_workarea);\n    }\n    return Py_NewRef(result);\n}\n\nstatic PyObject*\nget_monitor_names(PYNOARG) {\n    int count = 0;\n    GLFWmonitor **monitors = glfwGetMonitors(&count);\n    if (count <= 0 || !monitors) return PyTuple_New(0);\n    RAII_PyObject(result, PyTuple_New(count)); if (!result) return NULL;\n    for (int i = 0; i < count; i++) {\n        const char *name = glfwGetMonitorName(monitors[i]);\n        const char *description = glfwGetMonitorDescription(monitors[i]);\n        PyObject *x = Py_BuildValue(\"ss\", name, description);\n        if (!x) return NULL;\n        PyTuple_SET_ITEM(result, i, x);\n    }\n    return Py_NewRef(result);\n}\n\n\nstatic PyObject*\nprimary_monitor_content_scale(PYNOARG) {\n    GLFWmonitor* monitor = glfwGetPrimaryMonitor();\n    float xscale = 1.0, yscale = 1.0;\n    if (monitor) glfwGetMonitorContentScale(monitor, &xscale, &yscale);\n    return Py_BuildValue(\"ff\", xscale, yscale);\n}\n\nstatic PyObject*\nx11_display(PYNOARG) {\n    if (glfwGetX11Display) {\n        return PyLong_FromVoidPtr(glfwGetX11Display());\n    } else log_error(\"Failed to load glfwGetX11Display\");\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nwayland_compositor_data(PYNOARG) {\n    pid_t pid = -1;\n    const char *missing_capabilities = NULL;\n    if (global_state.is_wayland && glfwWaylandCompositorPID) {\n        pid = glfwWaylandCompositorPID();\n        missing_capabilities = glfwWaylandMissingCapabilities();\n    }\n    return Py_BuildValue(\"Ls\", (long long)pid, missing_capabilities);\n}\n\nstatic PyObject*\nx11_window_id(PyObject UNUSED *self, PyObject *os_wid) {\n    OSWindow *w = os_window_for_id(PyLong_AsUnsignedLongLong(os_wid));\n    if (!w) { PyErr_SetString(PyExc_ValueError, \"No OSWindow with the specified id found\"); return NULL; }\n    if (!glfwGetX11Window) { PyErr_SetString(PyExc_RuntimeError, \"Failed to load glfwGetX11Window\"); return NULL; }\n    return PyLong_FromUnsignedLong(glfwGetX11Window(w->handle));\n}\n\nstatic PyObject*\ncocoa_window_id(PyObject UNUSED *self, PyObject *os_wid) {\n    OSWindow *w = os_window_for_id(PyLong_AsUnsignedLongLong(os_wid));\n    if (!w) { PyErr_SetString(PyExc_ValueError, \"No OSWindow with the specified id found\"); return NULL; }\n    if (!glfwGetCocoaWindow) { PyErr_SetString(PyExc_RuntimeError, \"Failed to load glfwGetCocoaWindow\"); return NULL; }\n#ifdef __APPLE__\n    return Py_BuildValue(\"l\", (long)cocoa_window_number(glfwGetCocoaWindow(w->handle)));\n#else\n    PyErr_SetString(PyExc_RuntimeError, \"cocoa_window_id() is only supported on Mac\");\n    return NULL;\n#endif\n}\n\nstatic GLFWCursorShape\npointer_name_to_glfw_name(const char *name) {\n    /* start name to glfw (auto generated by gen-key-constants.py do not edit) */\n    if (strcmp(name, \"arrow\") == 0) return GLFW_DEFAULT_CURSOR;\n    if (strcmp(name, \"beam\") == 0) return GLFW_TEXT_CURSOR;\n    if (strcmp(name, \"text\") == 0) return GLFW_TEXT_CURSOR;\n    if (strcmp(name, \"pointer\") == 0) return GLFW_POINTER_CURSOR;\n    if (strcmp(name, \"hand\") == 0) return GLFW_POINTER_CURSOR;\n    if (strcmp(name, \"help\") == 0) return GLFW_HELP_CURSOR;\n    if (strcmp(name, \"wait\") == 0) return GLFW_WAIT_CURSOR;\n    if (strcmp(name, \"progress\") == 0) return GLFW_PROGRESS_CURSOR;\n    if (strcmp(name, \"crosshair\") == 0) return GLFW_CROSSHAIR_CURSOR;\n    if (strcmp(name, \"cell\") == 0) return GLFW_CELL_CURSOR;\n    if (strcmp(name, \"vertical-text\") == 0) return GLFW_VERTICAL_TEXT_CURSOR;\n    if (strcmp(name, \"move\") == 0) return GLFW_MOVE_CURSOR;\n    if (strcmp(name, \"e-resize\") == 0) return GLFW_E_RESIZE_CURSOR;\n    if (strcmp(name, \"ne-resize\") == 0) return GLFW_NE_RESIZE_CURSOR;\n    if (strcmp(name, \"nw-resize\") == 0) return GLFW_NW_RESIZE_CURSOR;\n    if (strcmp(name, \"n-resize\") == 0) return GLFW_N_RESIZE_CURSOR;\n    if (strcmp(name, \"se-resize\") == 0) return GLFW_SE_RESIZE_CURSOR;\n    if (strcmp(name, \"sw-resize\") == 0) return GLFW_SW_RESIZE_CURSOR;\n    if (strcmp(name, \"s-resize\") == 0) return GLFW_S_RESIZE_CURSOR;\n    if (strcmp(name, \"w-resize\") == 0) return GLFW_W_RESIZE_CURSOR;\n    if (strcmp(name, \"ew-resize\") == 0) return GLFW_EW_RESIZE_CURSOR;\n    if (strcmp(name, \"ns-resize\") == 0) return GLFW_NS_RESIZE_CURSOR;\n    if (strcmp(name, \"nesw-resize\") == 0) return GLFW_NESW_RESIZE_CURSOR;\n    if (strcmp(name, \"nwse-resize\") == 0) return GLFW_NWSE_RESIZE_CURSOR;\n    if (strcmp(name, \"zoom-in\") == 0) return GLFW_ZOOM_IN_CURSOR;\n    if (strcmp(name, \"zoom-out\") == 0) return GLFW_ZOOM_OUT_CURSOR;\n    if (strcmp(name, \"alias\") == 0) return GLFW_ALIAS_CURSOR;\n    if (strcmp(name, \"copy\") == 0) return GLFW_COPY_CURSOR;\n    if (strcmp(name, \"not-allowed\") == 0) return GLFW_NOT_ALLOWED_CURSOR;\n    if (strcmp(name, \"no-drop\") == 0) return GLFW_NO_DROP_CURSOR;\n    if (strcmp(name, \"grab\") == 0) return GLFW_GRAB_CURSOR;\n    if (strcmp(name, \"grabbing\") == 0) return GLFW_GRABBING_CURSOR;\n/* end name to glfw */\n    return GLFW_INVALID_CURSOR;\n}\n\nstatic PyObject*\nis_css_pointer_name_valid(PyObject *self UNUSED, PyObject *name) {\n    if (!PyUnicode_Check(name)) { PyErr_SetString(PyExc_TypeError, \"pointer name must be a string\"); return NULL; }\n    const char *q = PyUnicode_AsUTF8(name);\n    if (strcmp(q, \"default\") == 0) { Py_RETURN_TRUE; }\n    if (pointer_name_to_glfw_name(q) == GLFW_INVALID_CURSOR) { Py_RETURN_FALSE; }\n    Py_RETURN_TRUE;\n}\n\nstatic const char*\nglfw_name_to_css_pointer_name(GLFWCursorShape q) {\n    switch(q) {\n        case GLFW_INVALID_CURSOR: return \"\";\n        /* start glfw to css (auto generated by gen-key-constants.py do not edit) */\n        case GLFW_DEFAULT_CURSOR: return \"default\";\n        case GLFW_TEXT_CURSOR: return \"text\";\n        case GLFW_POINTER_CURSOR: return \"pointer\";\n        case GLFW_HELP_CURSOR: return \"help\";\n        case GLFW_WAIT_CURSOR: return \"wait\";\n        case GLFW_PROGRESS_CURSOR: return \"progress\";\n        case GLFW_CROSSHAIR_CURSOR: return \"crosshair\";\n        case GLFW_CELL_CURSOR: return \"cell\";\n        case GLFW_VERTICAL_TEXT_CURSOR: return \"vertical-text\";\n        case GLFW_MOVE_CURSOR: return \"move\";\n        case GLFW_E_RESIZE_CURSOR: return \"e-resize\";\n        case GLFW_NE_RESIZE_CURSOR: return \"ne-resize\";\n        case GLFW_NW_RESIZE_CURSOR: return \"nw-resize\";\n        case GLFW_N_RESIZE_CURSOR: return \"n-resize\";\n        case GLFW_SE_RESIZE_CURSOR: return \"se-resize\";\n        case GLFW_SW_RESIZE_CURSOR: return \"sw-resize\";\n        case GLFW_S_RESIZE_CURSOR: return \"s-resize\";\n        case GLFW_W_RESIZE_CURSOR: return \"w-resize\";\n        case GLFW_EW_RESIZE_CURSOR: return \"ew-resize\";\n        case GLFW_NS_RESIZE_CURSOR: return \"ns-resize\";\n        case GLFW_NESW_RESIZE_CURSOR: return \"nesw-resize\";\n        case GLFW_NWSE_RESIZE_CURSOR: return \"nwse-resize\";\n        case GLFW_ZOOM_IN_CURSOR: return \"zoom-in\";\n        case GLFW_ZOOM_OUT_CURSOR: return \"zoom-out\";\n        case GLFW_ALIAS_CURSOR: return \"alias\";\n        case GLFW_COPY_CURSOR: return \"copy\";\n        case GLFW_NOT_ALLOWED_CURSOR: return \"not-allowed\";\n        case GLFW_NO_DROP_CURSOR: return \"no-drop\";\n        case GLFW_GRAB_CURSOR: return \"grab\";\n        case GLFW_GRABBING_CURSOR: return \"grabbing\";\n/* end glfw to css */\n    }\n    return \"\";\n}\n\nstatic PyObject*\npointer_name_to_css_name(PyObject *self UNUSED, PyObject *name) {\n    if (!PyUnicode_Check(name)) { PyErr_SetString(PyExc_TypeError, \"pointer name must be a string\"); return NULL; }\n    GLFWCursorShape s = pointer_name_to_glfw_name(PyUnicode_AsUTF8(name));\n    return PyUnicode_FromString(glfw_name_to_css_pointer_name(s));\n}\n\nstatic PyObject*\nset_custom_cursor(PyObject *self UNUSED, PyObject *args) {\n    int x=0, y=0;\n    Py_ssize_t sz;\n    PyObject *images;\n    const char *shape;\n    if (!PyArg_ParseTuple(args, \"sO!|ii\", &shape, &PyTuple_Type, &images, &x, &y)) return NULL;\n    static GLFWimage gimages[16] = {{0}};\n    size_t count = MIN((size_t)PyTuple_GET_SIZE(images), arraysz(gimages));\n    for (size_t i = 0; i < count; i++) {\n        if (!PyArg_ParseTuple(PyTuple_GET_ITEM(images, i), \"s#ii\", &gimages[i].pixels, &sz, &gimages[i].width, &gimages[i].height)) return NULL;\n        if ((Py_ssize_t)gimages[i].width * gimages[i].height * 4 != sz) {\n            PyErr_SetString(PyExc_ValueError, \"The image data size does not match its width and height\");\n            return NULL;\n        }\n    }\n    GLFWCursorShape gshape = pointer_name_to_glfw_name(shape);\n    if (gshape == GLFW_INVALID_CURSOR) { PyErr_Format(PyExc_KeyError, \"Unknown pointer shape: %s\", shape); return NULL; }\n    GLFWcursor *c = glfwCreateCursor(gimages, x, y, count);\n    if (c == NULL) { PyErr_SetString(PyExc_ValueError, \"Failed to create custom cursor from specified images\"); return NULL; }\n    if (cursors[gshape].initialized && cursors[gshape].is_custom && cursors[gshape].glfw) {\n        glfwDestroyCursor(cursors[gshape].glfw);\n    }\n    cursors[gshape].initialized = true; cursors[gshape].is_custom = true; cursors[gshape].glfw = c;\n    Py_RETURN_NONE;\n}\n\n#ifdef __APPLE__\nvoid\nget_cocoa_key_equivalent(uint32_t key, int mods, char *cocoa_key, size_t key_sz, int *cocoa_mods) {\n    memset(cocoa_key, 0, key_sz);\n    uint32_t ans = glfwGetCocoaKeyEquivalent(key, mods, cocoa_mods);\n    if (ans) encode_utf8(ans, cocoa_key);\n}\n\nstatic void\ncocoa_frame_request_callback(GLFWwindow *window) {\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        if (global_state.os_windows[i].handle == window) {\n            global_state.os_windows[i].render_state = RENDER_FRAME_READY;\n            global_state.os_windows[i].last_render_frame_received_at = monotonic();\n            request_tick_callback();\n            break;\n        }\n    }\n}\n\nvoid\nrequest_frame_render(OSWindow *w) {\n    glfwCocoaRequestRenderFrame(w->handle, cocoa_frame_request_callback);\n    w->render_state = RENDER_FRAME_REQUESTED;\n}\n\nstatic PyObject*\npy_recreate_global_menu(PyObject *self UNUSED, PyObject *args UNUSED) {\n    cocoa_recreate_global_menu();\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npy_clear_global_shortcuts(PyObject *self UNUSED, PyObject *args UNUSED) {\n    cocoa_clear_global_shortcuts();\n    Py_RETURN_NONE;\n}\n#else\n\nstatic void\nwayland_frame_request_callback(id_type os_window_id) {\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        if (global_state.os_windows[i].id == os_window_id) {\n            global_state.os_windows[i].render_state = RENDER_FRAME_READY;\n            global_state.os_windows[i].last_render_frame_received_at = monotonic();\n            request_tick_callback();\n            break;\n        }\n    }\n}\n\nvoid\nrequest_frame_render(OSWindow *w) {\n    // Some Wayland compositors are too fragile to handle multiple\n    // render frame requests, see https://github.com/kovidgoyal/kitty/issues/2329\n    if (w->render_state != RENDER_FRAME_REQUESTED) {\n        w->render_state = RENDER_FRAME_REQUESTED;\n        glfwRequestWaylandFrameEvent(w->handle, w->id, wayland_frame_request_callback);\n    }\n}\n\nvoid\ndbus_notification_created_callback(unsigned long long notification_id, uint32_t new_notification_id, void* data UNUSED) {\n    unsigned long new_id = new_notification_id;\n    send_dbus_notification_event_to_python(\"created\", notification_id, new_id);\n}\n\nstatic PyObject*\ndbus_send_notification(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    int timeout = -1, urgency = 1; unsigned int replaces = 0;\n    GLFWDBUSNotificationData d = {0};\n    static const char* kwlist[] = {\"app_name\", \"app_icon\", \"title\", \"body\", \"actions\", \"timeout\", \"urgency\", \"replaces\", \"category\", \"muted\", NULL};\n    PyObject *actions = NULL;\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"ssssO!|iiIsp\", (char**)kwlist,\n        &d.app_name, &d.icon, &d.summary, &d.body, &PyDict_Type, &actions, &timeout, &urgency, &replaces, &d.category, &d.muted)) return NULL;\n    if (!glfwDBusUserNotify) {\n        PyErr_SetString(PyExc_RuntimeError, \"Failed to load glfwDBusUserNotify, did you call glfw_init?\");\n        return NULL;\n    }\n    d.timeout = timeout;\n    d.urgency = urgency & 3;\n    d.replaces = replaces;\n    RAII_ALLOC(const char*, aclist, calloc(2*PyDict_Size(actions), sizeof(d.actions[0])));\n    if (!aclist) { return PyErr_NoMemory(); }\n    PyObject *key, *value; Py_ssize_t pos = 0;\n    d.num_actions = 0;\n    while (PyDict_Next(actions, &pos, &key, &value)) {\n        if (!PyUnicode_Check(key) || !PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, \"actions must be strings\"); return NULL; }\n        if (PyUnicode_GET_LENGTH(key) == 0 || PyUnicode_GET_LENGTH(value) == 0) { PyErr_SetString(PyExc_TypeError, \"actions must be non-empty strings\"); return NULL; }\n        aclist[d.num_actions] = PyUnicode_AsUTF8(key); if (!aclist[d.num_actions++]) return NULL;\n        aclist[d.num_actions] = PyUnicode_AsUTF8(value); if (!aclist[d.num_actions++]) return NULL;\n    }\n    d.actions = aclist;\n    unsigned long long notification_id = glfwDBusUserNotify(&d, dbus_notification_created_callback, NULL);\n    return PyLong_FromUnsignedLongLong(notification_id);\n}\n\nstatic PyObject*\ndbus_close_notification(PyObject *self UNUSED, PyObject *args) {\n    unsigned int id;\n    if (!PyArg_ParseTuple(args, \"I\", &id)) return NULL;\n    GLFWDBUSNotificationData d = {.timeout=-9999, .urgency=255};\n    if (!glfwDBusUserNotify) {\n        PyErr_SetString(PyExc_RuntimeError, \"Failed to load glfwDBusUserNotify, did you call glfw_init?\");\n        return NULL;\n    }\n    if (glfwDBusUserNotify(&d, NULL, &id)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\n\n#endif\n\nstatic PyObject*\nget_click_interval(PyObject *self UNUSED, PyObject *args UNUSED) {\n    return PyFloat_FromDouble(monotonic_t_to_s_double(OPT(click_interval)));\n}\n\nid_type\nadd_main_loop_timer(monotonic_t interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback) {\n    return glfwAddTimer(interval, repeats, callback, callback_data, free_callback);\n}\n\nvoid\nupdate_main_loop_timer(id_type timer_id, monotonic_t interval, bool enabled) {\n    glfwUpdateTimer(timer_id, interval, enabled);\n}\n\nvoid\nremove_main_loop_timer(id_type timer_id) {\n    glfwRemoveTimer(timer_id);\n}\n\nvoid\nrun_main_loop(tick_callback_fun cb, void* cb_data) {\n    glfwRunMainLoop(cb, cb_data);\n}\n\nvoid\nstop_main_loop(void) {\n    glfwStopMainLoop();\n}\n\nstatic PyObject*\nstrip_csi(PyObject *self UNUSED, PyObject *src) {\n    if (!PyUnicode_Check(src)) { PyErr_SetString(PyExc_TypeError, \"Unicode string expected\"); return NULL; }\n    Py_ssize_t sz;\n    const char *title = PyUnicode_AsUTF8AndSize(src, &sz);\n    if (!title) return NULL;\n    RAII_ALLOC(char, buf, malloc(sz + 1));\n    if (!buf) { return PyErr_NoMemory(); }\n    strip_csi_(title, buf, sz + 1);\n    return PyUnicode_FromString(buf);\n}\n\nvoid\nset_ignore_os_keyboard_processing(bool enabled) {\n    glfwSetIgnoreOSKeyboardProcessing(enabled);\n}\n\nstatic void\ndecref_pyobj(void *x) {\n    Py_XDECREF(x);\n}\n\nstatic GLFWDataChunk\nget_clipboard_data(const char *mime_type, void *iter, GLFWClipboardType ct) {\n    GLFWDataChunk ans = {.iter=iter, .free=decref_pyobj};\n    if (global_state.boss == NULL) return ans;\n    if (iter == NULL) {\n        PyObject *c = PyObject_GetAttrString(global_state.boss, ct == GLFW_PRIMARY_SELECTION ? \"primary_selection\" : \"clipboard\");\n        if (c == NULL) { return ans; }\n        PyObject *i = PyObject_CallFunction(c, \"s\", mime_type);\n        Py_DECREF(c);\n        if (!i) { return ans; }\n        ans.iter = i;\n        return ans;\n    }\n    if (mime_type == NULL) {\n        Py_XDECREF(iter);\n        return ans;\n    }\n\n    PyObject *ret = PyObject_CallFunctionObjArgs(iter, NULL);\n    if (ret == NULL) return ans;\n    ans.data = PyBytes_AS_STRING(ret);\n    ans.sz = PyBytes_GET_SIZE(ret);\n    ans.free_data = ret;\n    return ans;\n}\n\nstatic PyObject*\nset_clipboard_data_types(PyObject *self UNUSED, PyObject *args) {\n    PyObject *mta;\n    int ctype;\n    if (!PyArg_ParseTuple(args, \"iO!\", &ctype, &PyTuple_Type, &mta)) return NULL;\n    if (glfwSetClipboardDataTypes) {\n        const char **mime_types = calloc(PyTuple_GET_SIZE(mta), sizeof(char*));\n        if (!mime_types) return PyErr_NoMemory();\n        for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(mta); i++) mime_types[i] = PyUnicode_AsUTF8(PyTuple_GET_ITEM(mta, i));\n        glfwSetClipboardDataTypes(ctype, mime_types, PyTuple_GET_SIZE(mta), get_clipboard_data);\n        free(mime_types);\n    } else log_error(\"GLFW not initialized cannot set clipboard data\");\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic bool\nwrite_clipboard_data(void *callback, const char *data, size_t sz) {\n    Py_ssize_t z = sz;\n    if (data == NULL) {\n        PyErr_SetString(PyExc_RuntimeError, \"is_self_offer\");\n        return false;\n    }\n    PyObject *ret = PyObject_CallFunction(callback, \"y#\", data, z);\n    bool ok = false;\n    if (ret != NULL) { ok = true; Py_DECREF(ret); }\n    return ok;\n}\n\nstatic PyObject*\nget_clipboard_mime(PyObject *self UNUSED, PyObject *args) {\n    int ctype;\n    const char *mime;\n    PyObject *callback;\n    if (!PyArg_ParseTuple(args, \"izO\", &ctype, &mime, &callback)) return NULL;\n    glfwGetClipboard(ctype, mime, write_clipboard_data, callback);\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nis_layer_shell_supported(PyObject *self UNUSED, PyObject *args UNUSED) {\n    return Py_NewRef(glfwIsLayerShellSupported() ? Py_True : Py_False);\n}\n\nstatic PyObject*\ntoggle_os_window_visibility(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    unsigned long long wid;\n    int set_visible = -1, move_to_active_screen = 0;\n    static const char* kwlist[] = {\"os_window_id\", \"visible\", \"move_to_active_screen\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(\n        args, kw, \"K|pp\", (char**)kwlist, &wid, &set_visible, &move_to_active_screen)) return NULL;\n    OSWindow *w = os_window_for_id(wid);\n    if (!w || !w->handle) Py_RETURN_FALSE;\n    bool is_visible = glfwGetWindowAttrib(w->handle, GLFW_VISIBLE) != 0;\n    if (set_visible == -1) set_visible = !is_visible;\n    else if (set_visible == is_visible) Py_RETURN_FALSE;\n    set_os_window_visibility(w, set_visible, move_to_active_screen);\n    Py_RETURN_TRUE;\n}\n\nstatic PyObject*\nlayer_shell_config_for_os_window(PyObject *self UNUSED, PyObject *wid) {\n    if (!PyLong_Check(wid)) { PyErr_SetString(PyExc_TypeError, \"os_window_id must be a int\"); return NULL; }\n    id_type id = PyLong_AsUnsignedLongLong(wid);\n    OSWindow *w = os_window_for_id(id);\n    if (!w || !w->handle) Py_RETURN_NONE;\n    const GLFWLayerShellConfig *c = glfwGetLayerShellConfig(w->handle);\n    if (!c) Py_RETURN_NONE;\n    return layer_shell_config_to_python(c);\n}\n\nstatic PyObject*\nset_layer_shell_config(PyObject *self UNUSED, PyObject *args) {\n    unsigned long long wid; PyObject *pylsc;\n    if (!PyArg_ParseTuple(args, \"KO\", &wid, &pylsc)) return NULL;\n    OSWindow *window = os_window_for_id(wid);\n    if (!window || !window->handle || !window->is_layer_shell) Py_RETURN_FALSE;\n    GLFWLayerShellConfig lsc = {0};\n    if (!layer_shell_config_from_python(pylsc, &lsc)) return NULL;\n    return Py_NewRef(set_layer_shell_config_for(window, &lsc) ? Py_True : Py_False);\n}\n\nstatic PyObject*\ngrab_keyboard(PyObject *self UNUSED, PyObject *action) {\n    return Py_NewRef(glfwGrabKeyboard(action == Py_None ? 2 : PyObject_IsTrue(action)) ? Py_True : Py_False);\n}\n\nstatic PyObject*\ndraw_single_line_of_text(PyObject *self UNUSED, PyObject *args) {\n    unsigned long long os_window_id;\n    const char *text;\n    unsigned int fg, bg;\n    int width, padding_y = 2;\n    if (!PyArg_ParseTuple(args, \"KsIIi|i\", &os_window_id, &text, &fg, &bg, &width, &padding_y)) return NULL;\n    OSWindow *w = os_window_for_id(os_window_id);\n    if (!w || !w->fonts_data) {\n        PyErr_SetString(PyExc_KeyError, \"OS Window with specified id does not exist or has no fonts data\");\n        return NULL;\n    }\n    double font_sz_pts = w->fonts_data->font_sz_in_pts;\n    double ydpi = w->fonts_data->logical_dpi_y;\n    size_t height = (size_t)w->fonts_data->fcm.cell_height + padding_y;\n    size_t buf_sz = (size_t)width * height * 4;\n    RAII_PyObject(ans, PyBytes_FromStringAndSize(NULL, buf_sz)); if (!ans) return NULL;\n    if (!draw_window_title(font_sz_pts, ydpi, text, fg, bg, (uint8_t*)PyBytes_AS_STRING(ans), width, height)) {\n        if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, \"Failed to render text\");\n        return NULL;\n    }\n    return Py_NewRef(ans);\n}\n\nstatic bool\nget_thumbnail(PyObject *thumbnails, GLFWimage *thumbnail, int idx) {\n    RAII_PyObject(t, PySequence_GetItem(thumbnails, idx));\n    if (!PyTuple_Check(t) || PyTuple_GET_SIZE(t) != 3) { PyErr_SetString(PyExc_TypeError, \"thumbnail must be a 3-tuple\"); return false; }\n    thumbnail->pixels = (uint8_t*)PyBytes_AS_STRING(PyTuple_GET_ITEM(t, 0));\n    thumbnail->width = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(t, 1));\n    thumbnail->height = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(t, 2));\n    return true;\n}\n\nstatic PyObject*\nchange_drag_thumbnail(PyObject *self UNUSED, PyObject *args) {\n    unsigned long long os_window_id; int idx = -1;\n    if (!PyArg_ParseTuple(args, \"K|i\", &os_window_id, &idx)) return NULL;\n    if (global_state.drag_source.thumbnail_idx == idx) Py_RETURN_NONE;\n    OSWindow *w = os_window_for_id(os_window_id);\n    if (!w || !w->handle) { PyErr_SetString(PyExc_KeyError, \"OS Window with specified id does not exist\"); return NULL; }\n    GLFWimage thumbnail = {0};\n    if (idx >=0 && global_state.drag_source.thumbnails && idx < PySequence_Size(global_state.drag_source.thumbnails)) {\n        if (!get_thumbnail(global_state.drag_source.thumbnails, &thumbnail, idx)) return NULL;\n        global_state.drag_source.thumbnail_idx = idx;\n    } else global_state.drag_source.thumbnail_idx = -1;\n    errno = glfwStartDrag(w->handle, NULL, 0, thumbnail.pixels ? &thumbnail : NULL, -2, false);\n    if (errno != 0) {\n        PyErr_SetFromErrno(PyExc_OSError);\n        return NULL;\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nstart_drag_with_data(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    static const char* kwlist[] = {\"os_window_id\", \"data_map\", \"thumbnails\", \"operations\", NULL};\n    unsigned long long os_window_id; PyObject *data_map;\n    int operations = GLFW_DRAG_OPERATION_MOVE;\n    PyObject *thumbnails = NULL;\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"KO!|Oi\", (char**)kwlist,\n            &os_window_id, &PyDict_Type, &data_map, &thumbnails, &operations)) return NULL;\n    OSWindow *w = os_window_for_id(os_window_id);\n    if (!w || !w->handle) { PyErr_SetString(PyExc_KeyError, \"OS Window with specified id does not exist\"); return NULL; }\n    GLFWimage thumbnail = {0};\n    if (!PySequence_Check(thumbnails)) { PyErr_SetString(PyExc_TypeError, \"thumbnails must be a sequence\"); return NULL; }\n    if (thumbnails && PySequence_Size(thumbnails) && !get_thumbnail(thumbnails, &thumbnail, 0)) return NULL;\n    RAII_ALLOC(GLFWDragSourceItem, items, calloc(PyDict_Size(data_map), sizeof(GLFWDragSourceItem)));\n    if (!items) { PyErr_NoMemory(); return NULL; }\n    PyObject *key, *value; Py_ssize_t pos = 0; size_t num = 0;\n    bool needs_toplevel_on_wayland = false;\n    while (PyDict_Next(data_map, &pos, &key, &value)) {\n        if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, \"data_map must have string keys\"); return NULL; }\n        if (!PyBytes_Check(value)) { PyErr_SetString(PyExc_TypeError, \"data_map must have bytes values\"); return NULL; }\n        GLFWDragSourceItem *item = items + num++;\n        item->mime_type = PyUnicode_AsUTF8(key);\n        item->optional_data = PyBytes_AS_STRING(value); item->data_size = PyBytes_GET_SIZE(value);\n        if (global_state.is_wayland && is_droppable_mime(item->mime_type) == TAB_DRAG_MIME_NUMBER)\n            needs_toplevel_on_wayland = true;\n    }\n    free_drag_source();\n    global_state.drag_source.is_active = true;\n    global_state.drag_source.needs_toplevel_on_wayland = needs_toplevel_on_wayland;\n    global_state.drag_source.drag_data = Py_NewRef(data_map);\n    if (thumbnails) global_state.drag_source.thumbnails = Py_NewRef(thumbnails);\n    global_state.drag_source.thumbnail_idx = thumbnail.pixels ? 0 : -1;\n    errno = glfwStartDrag(w->handle, items, num, thumbnail.pixels ? &thumbnail : NULL, operations, needs_toplevel_on_wayland);\n    if (errno != 0) {\n        PyErr_SetFromErrno(PyExc_OSError);\n        return NULL;\n    }\n    Py_RETURN_NONE;\n}\n\n// Boilerplate {{{\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(set_custom_cursor, METH_VARARGS),\n    METHODB(is_css_pointer_name_valid, METH_O),\n    {\"toggle_os_window_visibility\", (PyCFunction)(void (*) (void))(toggle_os_window_visibility), METH_VARARGS | METH_KEYWORDS, NULL},\n    METHODB(layer_shell_config_for_os_window, METH_O),\n    METHODB(set_layer_shell_config, METH_VARARGS),\n    METHODB(grab_keyboard, METH_O),\n    METHODB(pointer_name_to_css_name, METH_O),\n    {\"create_os_window\", (PyCFunction)(void (*) (void))(create_os_window), METH_VARARGS | METH_KEYWORDS, NULL},\n    {\"start_drag_with_data\", (PyCFunction)(void (*) (void))(start_drag_with_data), METH_VARARGS | METH_KEYWORDS, NULL},\n    METHODB(change_drag_thumbnail, METH_VARARGS),\n    METHODB(draw_single_line_of_text, METH_VARARGS),\n    METHODB(set_default_window_icon, METH_VARARGS),\n    METHODB(set_os_window_icon, METH_VARARGS),\n    METHODB(set_clipboard_data_types, METH_VARARGS),\n    METHODB(get_clipboard_mime, METH_VARARGS),\n    METHODB(toggle_secure_input, METH_NOARGS),\n    METHODB(macos_cycle_through_os_windows, METH_O),\n    METHODB(get_content_scale_for_window, METH_NOARGS),\n    METHODB(ring_bell, METH_VARARGS),\n    METHODB(request_attention, METH_VARARGS),\n    METHODB(toggle_fullscreen, METH_VARARGS),\n    METHODB(toggle_maximized, METH_VARARGS),\n    METHODB(change_os_window_state, METH_VARARGS),\n    METHODB(glfw_window_hint, METH_VARARGS),\n    METHODB(x11_display, METH_NOARGS),\n    METHODB(wayland_compositor_data, METH_NOARGS),\n    METHODB(get_click_interval, METH_NOARGS),\n    METHODB(is_layer_shell_supported, METH_NOARGS),\n    METHODB(x11_window_id, METH_O),\n    METHODB(strip_csi, METH_O),\n#ifndef __APPLE__\n    METHODB(dbus_close_notification, METH_VARARGS),\n    METHODB(dbus_set_notification_callback, METH_O),\n    {\"dbus_send_notification\", (PyCFunction)(void (*) (void))(dbus_send_notification), METH_KEYWORDS | METH_VARARGS, NULL},\n#else\n    {\"cocoa_recreate_global_menu\", (PyCFunction)py_recreate_global_menu, METH_NOARGS, \"\"},\n    {\"cocoa_clear_global_shortcuts\", (PyCFunction)py_clear_global_shortcuts, METH_NOARGS, \"\"},\n#endif\n    METHODB(cocoa_window_id, METH_O),\n    METHODB(cocoa_hide_app, METH_NOARGS),\n    METHODB(cocoa_hide_other_apps, METH_NOARGS),\n    METHODB(cocoa_minimize_os_window, METH_VARARGS),\n    {\"glfw_init\", (PyCFunction)glfw_init, METH_VARARGS, \"\"},\n    {\"glfw_terminate\", (PyCFunction)glfw_terminate, METH_NOARGS, \"\"},\n    {\"glfw_get_physical_dpi\", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, \"\"},\n    {\"glfw_get_key_name\", (PyCFunction)glfw_get_key_name, METH_VARARGS, \"\"},\n    {\"glfw_get_system_color_theme\", (PyCFunction)glfw_get_system_color_theme, METH_VARARGS, \"\"},\n    {\"glfw_primary_monitor_size\", (PyCFunction)primary_monitor_size, METH_NOARGS, \"\"},\n    {\"glfw_get_monitor_workarea\", (PyCFunction)get_monitor_workarea, METH_NOARGS, \"\"},\n    {\"glfw_get_monitor_names\", (PyCFunction)get_monitor_names, METH_NOARGS, \"\"},\n    {\"glfw_primary_monitor_content_scale\", (PyCFunction)primary_monitor_content_scale, METH_NOARGS, \"\"},\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nvoid cleanup_glfw(void) {\n    if (logo.pixels) free((void*)logo.pixels);\n    logo.pixels = NULL;\n    Py_CLEAR(edge_spacing_func);\n#ifndef __APPLE__\n    Py_CLEAR(dbus_notification_callback);\n    release_freetype_render_context(bold_render_ctx);\n    release_freetype_render_context(normal_render_ctx);\n#endif\n}\n\nbool\ninit_glfw(PyObject *m) {\n    if (PyModule_AddFunctions(m, module_methods) != 0) return false;\n    register_at_exit_cleanup_func(GLFW_CLEANUP_FUNC, cleanup_glfw);\n\n// constants {{{\n#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;\n    ADDC(GLFW_DRAG_OPERATION_MOVE);\n    ADDC(GLFW_DRAG_OPERATION_COPY);\n    ADDC(GLFW_DRAG_OPERATION_GENERIC);\n    ADDC(GLFW_RELEASE);\n    ADDC(GLFW_PRESS);\n    ADDC(GLFW_REPEAT);\n    ADDC(true); ADDC(false);\n    ADDC(GLFW_PRIMARY_SELECTION); ADDC(GLFW_CLIPBOARD);\n    ADDC(GLFW_LAYER_SHELL_NONE); ADDC(GLFW_LAYER_SHELL_PANEL); ADDC(GLFW_LAYER_SHELL_BACKGROUND); ADDC(GLFW_LAYER_SHELL_TOP); ADDC(GLFW_LAYER_SHELL_OVERLAY);\n    ADDC(GLFW_FOCUS_NOT_ALLOWED); ADDC(GLFW_FOCUS_EXCLUSIVE); ADDC(GLFW_FOCUS_ON_DEMAND);\n    ADDC(GLFW_EDGE_TOP); ADDC(GLFW_EDGE_BOTTOM); ADDC(GLFW_EDGE_LEFT); ADDC(GLFW_EDGE_RIGHT); ADDC(GLFW_EDGE_CENTER); ADDC(GLFW_EDGE_NONE);\n    ADDC(GLFW_EDGE_CENTER_SIZED);\n    ADDC(GLFW_COLOR_SCHEME_NO_PREFERENCE); ADDC(GLFW_COLOR_SCHEME_DARK); ADDC(GLFW_COLOR_SCHEME_LIGHT);\n\n    /* start glfw functional keys (auto generated by gen-key-constants.py do not edit) */\n    ADDC(GLFW_FKEY_ESCAPE);\n    ADDC(GLFW_FKEY_ENTER);\n    ADDC(GLFW_FKEY_TAB);\n    ADDC(GLFW_FKEY_BACKSPACE);\n    ADDC(GLFW_FKEY_INSERT);\n    ADDC(GLFW_FKEY_DELETE);\n    ADDC(GLFW_FKEY_LEFT);\n    ADDC(GLFW_FKEY_RIGHT);\n    ADDC(GLFW_FKEY_UP);\n    ADDC(GLFW_FKEY_DOWN);\n    ADDC(GLFW_FKEY_PAGE_UP);\n    ADDC(GLFW_FKEY_PAGE_DOWN);\n    ADDC(GLFW_FKEY_HOME);\n    ADDC(GLFW_FKEY_END);\n    ADDC(GLFW_FKEY_CAPS_LOCK);\n    ADDC(GLFW_FKEY_SCROLL_LOCK);\n    ADDC(GLFW_FKEY_NUM_LOCK);\n    ADDC(GLFW_FKEY_PRINT_SCREEN);\n    ADDC(GLFW_FKEY_PAUSE);\n    ADDC(GLFW_FKEY_MENU);\n    ADDC(GLFW_FKEY_F1);\n    ADDC(GLFW_FKEY_F2);\n    ADDC(GLFW_FKEY_F3);\n    ADDC(GLFW_FKEY_F4);\n    ADDC(GLFW_FKEY_F5);\n    ADDC(GLFW_FKEY_F6);\n    ADDC(GLFW_FKEY_F7);\n    ADDC(GLFW_FKEY_F8);\n    ADDC(GLFW_FKEY_F9);\n    ADDC(GLFW_FKEY_F10);\n    ADDC(GLFW_FKEY_F11);\n    ADDC(GLFW_FKEY_F12);\n    ADDC(GLFW_FKEY_F13);\n    ADDC(GLFW_FKEY_F14);\n    ADDC(GLFW_FKEY_F15);\n    ADDC(GLFW_FKEY_F16);\n    ADDC(GLFW_FKEY_F17);\n    ADDC(GLFW_FKEY_F18);\n    ADDC(GLFW_FKEY_F19);\n    ADDC(GLFW_FKEY_F20);\n    ADDC(GLFW_FKEY_F21);\n    ADDC(GLFW_FKEY_F22);\n    ADDC(GLFW_FKEY_F23);\n    ADDC(GLFW_FKEY_F24);\n    ADDC(GLFW_FKEY_F25);\n    ADDC(GLFW_FKEY_F26);\n    ADDC(GLFW_FKEY_F27);\n    ADDC(GLFW_FKEY_F28);\n    ADDC(GLFW_FKEY_F29);\n    ADDC(GLFW_FKEY_F30);\n    ADDC(GLFW_FKEY_F31);\n    ADDC(GLFW_FKEY_F32);\n    ADDC(GLFW_FKEY_F33);\n    ADDC(GLFW_FKEY_F34);\n    ADDC(GLFW_FKEY_F35);\n    ADDC(GLFW_FKEY_KP_0);\n    ADDC(GLFW_FKEY_KP_1);\n    ADDC(GLFW_FKEY_KP_2);\n    ADDC(GLFW_FKEY_KP_3);\n    ADDC(GLFW_FKEY_KP_4);\n    ADDC(GLFW_FKEY_KP_5);\n    ADDC(GLFW_FKEY_KP_6);\n    ADDC(GLFW_FKEY_KP_7);\n    ADDC(GLFW_FKEY_KP_8);\n    ADDC(GLFW_FKEY_KP_9);\n    ADDC(GLFW_FKEY_KP_DECIMAL);\n    ADDC(GLFW_FKEY_KP_DIVIDE);\n    ADDC(GLFW_FKEY_KP_MULTIPLY);\n    ADDC(GLFW_FKEY_KP_SUBTRACT);\n    ADDC(GLFW_FKEY_KP_ADD);\n    ADDC(GLFW_FKEY_KP_ENTER);\n    ADDC(GLFW_FKEY_KP_EQUAL);\n    ADDC(GLFW_FKEY_KP_SEPARATOR);\n    ADDC(GLFW_FKEY_KP_LEFT);\n    ADDC(GLFW_FKEY_KP_RIGHT);\n    ADDC(GLFW_FKEY_KP_UP);\n    ADDC(GLFW_FKEY_KP_DOWN);\n    ADDC(GLFW_FKEY_KP_PAGE_UP);\n    ADDC(GLFW_FKEY_KP_PAGE_DOWN);\n    ADDC(GLFW_FKEY_KP_HOME);\n    ADDC(GLFW_FKEY_KP_END);\n    ADDC(GLFW_FKEY_KP_INSERT);\n    ADDC(GLFW_FKEY_KP_DELETE);\n    ADDC(GLFW_FKEY_KP_BEGIN);\n    ADDC(GLFW_FKEY_MEDIA_PLAY);\n    ADDC(GLFW_FKEY_MEDIA_PAUSE);\n    ADDC(GLFW_FKEY_MEDIA_PLAY_PAUSE);\n    ADDC(GLFW_FKEY_MEDIA_REVERSE);\n    ADDC(GLFW_FKEY_MEDIA_STOP);\n    ADDC(GLFW_FKEY_MEDIA_FAST_FORWARD);\n    ADDC(GLFW_FKEY_MEDIA_REWIND);\n    ADDC(GLFW_FKEY_MEDIA_TRACK_NEXT);\n    ADDC(GLFW_FKEY_MEDIA_TRACK_PREVIOUS);\n    ADDC(GLFW_FKEY_MEDIA_RECORD);\n    ADDC(GLFW_FKEY_LOWER_VOLUME);\n    ADDC(GLFW_FKEY_RAISE_VOLUME);\n    ADDC(GLFW_FKEY_MUTE_VOLUME);\n    ADDC(GLFW_FKEY_LEFT_SHIFT);\n    ADDC(GLFW_FKEY_LEFT_CONTROL);\n    ADDC(GLFW_FKEY_LEFT_ALT);\n    ADDC(GLFW_FKEY_LEFT_SUPER);\n    ADDC(GLFW_FKEY_LEFT_HYPER);\n    ADDC(GLFW_FKEY_LEFT_META);\n    ADDC(GLFW_FKEY_RIGHT_SHIFT);\n    ADDC(GLFW_FKEY_RIGHT_CONTROL);\n    ADDC(GLFW_FKEY_RIGHT_ALT);\n    ADDC(GLFW_FKEY_RIGHT_SUPER);\n    ADDC(GLFW_FKEY_RIGHT_HYPER);\n    ADDC(GLFW_FKEY_RIGHT_META);\n    ADDC(GLFW_FKEY_ISO_LEVEL3_SHIFT);\n    ADDC(GLFW_FKEY_ISO_LEVEL5_SHIFT);\n/* end glfw functional keys */\n// --- Modifiers ---------------------------------------------------------------\n    ADDC(GLFW_MOD_SHIFT);\n    ADDC(GLFW_MOD_CONTROL);\n    ADDC(GLFW_MOD_ALT);\n    ADDC(GLFW_MOD_SUPER);\n    ADDC(GLFW_MOD_HYPER);\n    ADDC(GLFW_MOD_META);\n    ADDC(GLFW_MOD_KITTY);\n    ADDC(GLFW_MOD_CAPS_LOCK);\n    ADDC(GLFW_MOD_NUM_LOCK);\n\n// --- Mouse -------------------------------------------------------------------\n    ADDC(GLFW_MOUSE_BUTTON_1);\n    ADDC(GLFW_MOUSE_BUTTON_2);\n    ADDC(GLFW_MOUSE_BUTTON_3);\n    ADDC(GLFW_MOUSE_BUTTON_4);\n    ADDC(GLFW_MOUSE_BUTTON_5);\n    ADDC(GLFW_MOUSE_BUTTON_6);\n    ADDC(GLFW_MOUSE_BUTTON_7);\n    ADDC(GLFW_MOUSE_BUTTON_8);\n    ADDC(GLFW_MOUSE_BUTTON_LAST);\n    ADDC(GLFW_MOUSE_BUTTON_LEFT);\n    ADDC(GLFW_MOUSE_BUTTON_RIGHT);\n    ADDC(GLFW_MOUSE_BUTTON_MIDDLE);\n\n\n// --- Joystick ----------------------------------------------------------------\n    ADDC(GLFW_JOYSTICK_1);\n    ADDC(GLFW_JOYSTICK_2);\n    ADDC(GLFW_JOYSTICK_3);\n    ADDC(GLFW_JOYSTICK_4);\n    ADDC(GLFW_JOYSTICK_5);\n    ADDC(GLFW_JOYSTICK_6);\n    ADDC(GLFW_JOYSTICK_7);\n    ADDC(GLFW_JOYSTICK_8);\n    ADDC(GLFW_JOYSTICK_9);\n    ADDC(GLFW_JOYSTICK_10);\n    ADDC(GLFW_JOYSTICK_11);\n    ADDC(GLFW_JOYSTICK_12);\n    ADDC(GLFW_JOYSTICK_13);\n    ADDC(GLFW_JOYSTICK_14);\n    ADDC(GLFW_JOYSTICK_15);\n    ADDC(GLFW_JOYSTICK_16);\n    ADDC(GLFW_JOYSTICK_LAST);\n\n\n// --- Error codes -------------------------------------------------------------\n    ADDC(GLFW_NOT_INITIALIZED);\n    ADDC(GLFW_NO_CURRENT_CONTEXT);\n    ADDC(GLFW_INVALID_ENUM);\n    ADDC(GLFW_INVALID_VALUE);\n    ADDC(GLFW_OUT_OF_MEMORY);\n    ADDC(GLFW_API_UNAVAILABLE);\n    ADDC(GLFW_VERSION_UNAVAILABLE);\n    ADDC(GLFW_PLATFORM_ERROR);\n    ADDC(GLFW_FORMAT_UNAVAILABLE);\n\n// ---\n    ADDC(GLFW_FOCUSED);\n    ADDC(GLFW_ICONIFIED);\n    ADDC(GLFW_RESIZABLE);\n    ADDC(GLFW_VISIBLE);\n    ADDC(GLFW_DECORATED);\n    ADDC(GLFW_AUTO_ICONIFY);\n    ADDC(GLFW_FLOATING);\n\n// ---\n    ADDC(GLFW_RED_BITS);\n    ADDC(GLFW_GREEN_BITS);\n    ADDC(GLFW_BLUE_BITS);\n    ADDC(GLFW_ALPHA_BITS);\n    ADDC(GLFW_DEPTH_BITS);\n    ADDC(GLFW_STENCIL_BITS);\n    ADDC(GLFW_ACCUM_RED_BITS);\n    ADDC(GLFW_ACCUM_GREEN_BITS);\n    ADDC(GLFW_ACCUM_BLUE_BITS);\n    ADDC(GLFW_ACCUM_ALPHA_BITS);\n    ADDC(GLFW_AUX_BUFFERS);\n    ADDC(GLFW_STEREO);\n    ADDC(GLFW_SAMPLES);\n    ADDC(GLFW_SRGB_CAPABLE);\n    ADDC(GLFW_REFRESH_RATE);\n    ADDC(GLFW_DOUBLEBUFFER);\n\n// ---\n    ADDC(GLFW_CLIENT_API);\n    ADDC(GLFW_CONTEXT_VERSION_MAJOR);\n    ADDC(GLFW_CONTEXT_VERSION_MINOR);\n    ADDC(GLFW_CONTEXT_REVISION);\n    ADDC(GLFW_CONTEXT_ROBUSTNESS);\n    ADDC(GLFW_OPENGL_FORWARD_COMPAT);\n    ADDC(GLFW_CONTEXT_DEBUG);\n    ADDC(GLFW_OPENGL_PROFILE);\n\n// ---\n    ADDC(GLFW_OPENGL_API);\n    ADDC(GLFW_OPENGL_ES_API);\n\n// ---\n    ADDC(GLFW_NO_ROBUSTNESS);\n    ADDC(GLFW_NO_RESET_NOTIFICATION);\n    ADDC(GLFW_LOSE_CONTEXT_ON_RESET);\n\n// ---\n    ADDC(GLFW_OPENGL_ANY_PROFILE);\n    ADDC(GLFW_OPENGL_CORE_PROFILE);\n    ADDC(GLFW_OPENGL_COMPAT_PROFILE);\n\n// ---\n    ADDC(GLFW_CURSOR);\n    ADDC(GLFW_STICKY_KEYS);\n    ADDC(GLFW_STICKY_MOUSE_BUTTONS);\n\n// ---\n    ADDC(GLFW_CURSOR_NORMAL);\n    ADDC(GLFW_CURSOR_HIDDEN);\n    ADDC(GLFW_CURSOR_DISABLED);\n\n// ---\n    ADDC(GLFW_CONNECTED);\n    ADDC(GLFW_DISCONNECTED);\n#undef ADDC\n// }}}\n\n    return true;\n}\n"
  },
  {
    "path": "kitty/glyph-cache.c",
    "content": "/*\n * glyph-cache.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"glyph-cache.h\"\ntypedef struct SpritePosKey {\n    glyph_index ligature_index, count, cell_count, keysz_in_bytes;\n    uint8_t scale, subscale, multicell_y, vertical_align;\n    glyph_index key[];\n} SpritePosKey;\nstatic_assert(sizeof(SpritePosKey) == sizeof(glyph_index) * 4 + sizeof(uint8_t) * 4, \"Fix the ordering of SpritePosKey\");\n\n#define NAME sprite_pos_map\n#define KEY_TY const SpritePosKey*\n#define VAL_TY SpritePosition*\nstatic uint64_t sprite_pos_map_hash(KEY_TY key);\n#define HASH_FN sprite_pos_map_hash\nstatic bool sprite_pos_map_cmpr(KEY_TY a, KEY_TY b);\n#define CMPR_FN sprite_pos_map_cmpr\n#define MA_NAME Key\n#define MA_BLOCK_SIZE 16u\nstatic_assert(MA_BLOCK_SIZE > sizeof(SpritePosKey) + 2, \"increase arena block size\");\n#define MA_ARENA_NUM_BLOCKS (2048u / MA_BLOCK_SIZE)\n#include \"arena.h\"\n#define MA_NAME Val\n#define MA_BLOCK_SIZE sizeof(VAL_TY)\n#define MA_ARENA_NUM_BLOCKS (2048u / MA_BLOCK_SIZE)\n#include \"arena.h\"\n\n\n#include \"kitty-verstable.h\"\n\nstatic uint64_t\nsprite_pos_map_hash(const SpritePosKey *key) {\n    return vt_hash_bytes(key, key->keysz_in_bytes + sizeof(SpritePosKey));\n}\n\nstatic bool\nsprite_pos_map_cmpr(const SpritePosKey *a, const SpritePosKey *b) {\n    return a->keysz_in_bytes == b->keysz_in_bytes && memcmp(a, b, a->keysz_in_bytes + sizeof(SpritePosKey)) == 0;\n}\n\n\ntypedef struct HashTable {\n    sprite_pos_map table;\n    KeyMonotonicArena keys;\n    ValMonotonicArena vals;\n    struct { SpritePosKey *key; size_t capacity; } scratch;\n} HashTable;\n\nSPRITE_POSITION_MAP_HANDLE\ncreate_sprite_position_hash_table(void) {\n    HashTable *ans = calloc(1, sizeof(HashTable));\n    if (ans) vt_init(&ans->table);\n    return (SPRITE_POSITION_MAP_HANDLE)ans;\n}\n\nSpritePosition*\nfind_or_create_sprite_position(\n    SPRITE_POSITION_MAP_HANDLE map_, glyph_index *glyphs, glyph_index count, glyph_index ligature_index, glyph_index cell_count,\n    uint8_t scale, uint8_t subscale, uint8_t multicell_y, uint8_t vertical_align, bool *created\n) {\n    HashTable *ht = (HashTable*)map_;\n    sprite_pos_map *map = &ht->table;\n    const size_t keysz_in_bytes = count * sizeof(glyph_index);\n    if (!ht->scratch.key || keysz_in_bytes > ht->scratch.capacity) {\n        const size_t newsz = sizeof(ht->scratch.key[0]) + keysz_in_bytes + 64;\n        ht->scratch.key = realloc(ht->scratch.key, newsz);\n        if (!ht->scratch.key) { ht->scratch.capacity = 0; return NULL; }\n        ht->scratch.capacity = newsz - sizeof(ht->scratch.key[0]);\n        memset(ht->scratch.key, 0, newsz);\n    }\n#define scratch ht->scratch.key\n    scratch->keysz_in_bytes = keysz_in_bytes;\n    scratch->count = count; scratch->ligature_index = ligature_index; scratch->cell_count = cell_count;\n    scratch->scale = scale; scratch->subscale = subscale; scratch->multicell_y = multicell_y; scratch->vertical_align = vertical_align;\n    memcpy(scratch->key, glyphs, keysz_in_bytes);\n    sprite_pos_map_itr n = vt_get(map, scratch);\n    if (!vt_is_end(n)) { *created = false; return n.data->val; }\n\n    SpritePosKey *key = Key_get(&ht->keys, sizeof(SpritePosKey) + scratch->keysz_in_bytes);\n    if (!key) return NULL;\n    SpritePosition *val = Val_get(&ht->vals, sizeof(SpritePosition));\n    if (!val) return NULL;\n    memcpy(key, scratch, sizeof(scratch[0]) + scratch->keysz_in_bytes);\n    if (vt_is_end(vt_insert(map, key, val))) return NULL;\n    *created = true;\n    return val;\n#undef scratch\n}\n\nvoid\nfree_sprite_position_hash_table(SPRITE_POSITION_MAP_HANDLE *map) {\n    HashTable **mapref = (HashTable**)map;\n    if (*mapref) {\n        vt_cleanup(&mapref[0]->table);\n        Key_free_all(&mapref[0]->keys);\n        Val_free_all(&mapref[0]->vals);\n        free(mapref[0]->scratch.key);\n        free(mapref[0]); mapref[0] = NULL;\n    }\n}\n\n\n#define NAME glyph_props_map\n#define KEY_TY glyph_index\n#define VAL_TY GlyphProperties\n#include \"kitty-verstable.h\"\n\nGLYPH_PROPERTIES_MAP_HANDLE\ncreate_glyph_properties_hash_table(void) {\n    glyph_props_map *ans = calloc(1, sizeof(glyph_props_map));\n    if (ans) vt_init(ans);\n    return (GLYPH_PROPERTIES_MAP_HANDLE)ans;\n}\n\nGlyphProperties\nfind_glyph_properties(GLYPH_PROPERTIES_MAP_HANDLE map_, glyph_index glyph) {\n    glyph_props_map *map = (glyph_props_map*)map_;\n    glyph_props_map_itr n = vt_get(map, glyph);\n    if (vt_is_end(n)) return (GlyphProperties){0};\n    return n.data->val;\n}\n\nbool\nset_glyph_properties(GLYPH_PROPERTIES_MAP_HANDLE map_, glyph_index glyph, GlyphProperties val) {\n    glyph_props_map *map = (glyph_props_map*)map_;\n    return !vt_is_end(vt_insert(map, glyph, val));\n}\n\n\nvoid\nfree_glyph_properties_hash_table(GLYPH_PROPERTIES_MAP_HANDLE *map_) {\n    glyph_props_map **mapref = (glyph_props_map**)map_;\n    if (*mapref) {\n        vt_cleanup(*mapref); free(*mapref); *mapref = NULL;\n    }\n}\n"
  },
  {
    "path": "kitty/glyph-cache.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\ntypedef union SpritePosition {\n    struct {\n        sprite_index idx : sizeof(sprite_index) * 8;\n        bool rendered : 1;\n        bool colored : 1;\n        uint32_t : 30;\n    };\n    uint64_t val;\n} SpritePosition;\nstatic_assert(sizeof(SpritePosition) == sizeof(uint64_t), \"Fix ordering of SpritePosition\");\n\ntypedef struct {int x;} *SPRITE_POSITION_MAP_HANDLE;\n\nSPRITE_POSITION_MAP_HANDLE\ncreate_sprite_position_hash_table(void);\nvoid\nfree_sprite_position_hash_table(SPRITE_POSITION_MAP_HANDLE *handle);\nSpritePosition*\nfind_or_create_sprite_position(SPRITE_POSITION_MAP_HANDLE map, glyph_index *glyphs, glyph_index count, glyph_index ligature_index, glyph_index cell_count, uint8_t scale, uint8_t subscale, uint8_t multicell_y, uint8_t vertical_align, bool *created);\n\n\ntypedef union GlyphProperties {\n    struct {\n        uint8_t special_set : 1;\n        uint8_t special_val : 1;\n        uint8_t empty_set : 1;\n        uint8_t empty_val : 1;\n    };\n    uint8_t val;\n} GlyphProperties;\n\ntypedef struct {int x;} *GLYPH_PROPERTIES_MAP_HANDLE;\n\nGLYPH_PROPERTIES_MAP_HANDLE\ncreate_glyph_properties_hash_table(void);\n\nvoid free_glyph_properties_hash_table(GLYPH_PROPERTIES_MAP_HANDLE *handle);\nGlyphProperties\nfind_glyph_properties(GLYPH_PROPERTIES_MAP_HANDLE map, glyph_index glyph);\nbool\nset_glyph_properties(GLYPH_PROPERTIES_MAP_HANDLE map, glyph_index glyph, GlyphProperties val);\n"
  },
  {
    "path": "kitty/graphics.c",
    "content": "/*\n * graphics.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define GRAPHICS_INTERNAL_APIS\n#include \"graphics.h\"\n#include \"state.h\"\n#include \"disk-cache.h\"\n#include \"iqsort.h\"\n#include \"safe-wrappers.h\"\n\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <sys/mman.h>\n#include <stdlib.h>\n\n#include <zlib.h>\n#include <structmember.h>\n#include \"png-reader.h\"\nPyTypeObject GraphicsManager_Type;\n\n#define MAX_IMAGE_DIMENSION 10000u\n#define DEFAULT_STORAGE_LIMIT 320u * (1024u * 1024u)\n#define REPORT_ERROR(...) { log_error(__VA_ARGS__); }\n#define RAII_CoalescedFrameData(name, initializer) __attribute__((cleanup(cfd_free))) CoalescedFrameData name = initializer\n\n// caching {{{\n#define member_size(type, member) sizeof(((type *)0)->member)\n#define CACHE_KEY_BUFFER_SIZE (member_size(ImageAndFrame, image_id) + member_size(ImageAndFrame, frame_id))\n\nstatic size_t\ncache_key(const ImageAndFrame x, char *key) {\n    memcpy(key, &x.image_id, sizeof(x.image_id));\n    memcpy(key + sizeof(x.image_id), &x.frame_id, sizeof(x.frame_id));\n    return CACHE_KEY_BUFFER_SIZE;\n}\n#define CK(x) key, cache_key(x, key)\n\nstatic bool\nadd_to_cache(GraphicsManager *self, const ImageAndFrame x, const void *data, const size_t sz) {\n    char key[CACHE_KEY_BUFFER_SIZE];\n    return add_to_disk_cache(self->disk_cache, CK(x), data, sz);\n}\n\nstatic bool\nremove_from_cache(GraphicsManager *self, const ImageAndFrame x) {\n    char key[CACHE_KEY_BUFFER_SIZE];\n    return remove_from_disk_cache(self->disk_cache, CK(x));\n}\n\nstatic bool\nread_from_cache(const GraphicsManager *self, const ImageAndFrame x, void **data, size_t *sz) {\n    char key[CACHE_KEY_BUFFER_SIZE];\n    return read_from_disk_cache_simple(self->disk_cache, CK(x), data, sz, false);\n}\n\nstatic size_t\ncache_size(const GraphicsManager *self) { return disk_cache_total_size(self->disk_cache); }\n#undef CK\n// }}}\n\n\nstatic inline id_type\nnext_id(id_type *counter) {\n    id_type ans = ++(*counter);\n    if (UNLIKELY(ans == 0)) ans = ++(*counter);\n    return ans;\n}\nstatic const unsigned PARENT_DEPTH_LIMIT = 8;\n\nGraphicsManager*\ngrman_alloc(bool for_paused_rendering) {\n    GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0);\n    self->render_data.capacity = 64;\n    self->render_data.item = calloc(self->render_data.capacity, sizeof(self->render_data.item[0]));\n    self->storage_limit = DEFAULT_STORAGE_LIMIT;\n    if (self->render_data.item == NULL) {\n        PyErr_NoMemory();\n        Py_CLEAR(self); return NULL;\n    }\n    if (!for_paused_rendering) {\n        self->disk_cache = create_disk_cache();\n        if (!self->disk_cache) { Py_CLEAR(self); return NULL; }\n    }\n    vt_init(&self->images_by_internal_id);\n    return self;\n}\n\n#define iter_refs(img) vt_create_for_loop(ref_map_itr, i, &((img)->refs_by_internal_id))\n\nstatic void\nfree_refs_data(Image *img) {\n    iter_refs(img) free(i.data->val);\n    vt_cleanup(&img->refs_by_internal_id);\n}\n\nstatic void\nfree_load_data(LoadData *ld) {\n    free(ld->buf); ld->buf_used = 0; ld->buf_capacity = 0; ld->buf = NULL;\n    if (ld->mapped_file) munmap(ld->mapped_file, ld->mapped_file_sz);\n    ld->mapped_file = NULL; ld->mapped_file_sz = 0;\n    ld->loading_for = (const ImageAndFrame){0};\n}\n\nstatic void*\nclear_texture_ref(TextureRef **x) {\n    if (*x) {\n        if ((*x)->refcnt < 2) {\n            if ((*x)->id) free_texture(&(*x)->id);\n            free(*x); *x = NULL;\n        } else (*x)->refcnt--;\n    }\n    return NULL;\n}\n\nstatic TextureRef*\nincref_texture_ref(TextureRef *ref) {\n    if (ref) ref->refcnt++;\n    return ref;\n}\n\nstatic TextureRef*\nnew_texture_ref(void) {\n    TextureRef *ans = calloc(1, sizeof(TextureRef));\n    if (!ans) fatal(\"Out of memory allocating a TextureRef\");\n    ans->refcnt = 1;\n    return ans;\n}\n\nstatic uint32_t\ntexture_id_for_img(Image *img) {\n    return img->texture ? img->texture->id : 0;\n}\n\nstatic void\nfree_image_resources(GraphicsManager *self, Image *img) {\n    clear_texture_ref(&img->texture);\n    if (self->disk_cache) {\n        ImageAndFrame key = { .image_id=img->internal_id, .frame_id = img->root_frame.id };\n        if (!remove_from_cache(self, key) && PyErr_Occurred()) PyErr_Print();\n        for (unsigned i = 0; i < img->extra_framecnt; i++) {\n            key.frame_id = img->extra_frames[i].id;\n            if (!remove_from_cache(self, key) && PyErr_Occurred()) PyErr_Print();\n        }\n    }\n    if (img->extra_frames) {\n        free(img->extra_frames);\n        img->extra_frames = NULL;\n    }\n    free_refs_data(img);\n    self->used_storage = img->used_storage <= self->used_storage ? self->used_storage - img->used_storage : 0;\n}\n\nstatic void\nfree_image(GraphicsManager *self, Image *img) {\n    free_image_resources(self, img);\n    free(img);\n}\n\n#define iter_images(grman) vt_create_for_loop(image_map_itr, i, &((grman)->images_by_internal_id))\n\nstatic void\nfree_all_images(GraphicsManager *self) {\n    iter_images(self) free_image(self, i.data->val);\n    vt_cleanup(&self->images_by_internal_id);\n}\n\nstatic void\ndealloc(GraphicsManager* self) {\n    free_all_images(self);\n    free(self->render_data.item);\n    Py_CLEAR(self->disk_cache);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic Image*\nimg_by_internal_id(const GraphicsManager *self, id_type id) {\n    image_map_itr i = vt_get((image_map*)&self->images_by_internal_id, id);\n    return vt_is_end(i) ? NULL : i.data->val;\n}\n\nstatic Image*\nimg_by_client_id(const GraphicsManager *self, uint32_t id) {\n    iter_images(((GraphicsManager*)self)) if (i.data->val->client_id == id) return i.data->val;\n    return NULL;\n}\n\nstatic Image*\nimg_by_client_number(const GraphicsManager *self, uint32_t number) {\n    // get the newest image with the specified number\n    Image *ans = NULL;\n    iter_images(((GraphicsManager*)self)) {\n        Image *img = i.data->val;\n        if (img->client_number == number && (!ans || img->internal_id > ans->internal_id)) ans = img;\n    }\n    return ans;\n}\n\nstatic ImageRef*\nref_by_internal_id(const Image *img, id_type id) {\n    ref_map_itr i = vt_get(&((Image *)img)->refs_by_internal_id, id);\n    return vt_is_end(i) ? NULL : i.data->val;\n}\n\n\nstatic ImageRef*\nref_by_client_id(const Image *img, uint32_t id) {\n    iter_refs((Image*)img) if (i.data->val->client_id == id) return i.data->val;\n    return NULL;\n}\n\nstatic void\nset_layers_dirty(GraphicsManager *self) {\n    self->layers_dirty = true;\n}\n\nstatic image_map_itr\nremove_image_itr(GraphicsManager *self, image_map_itr i) {\n    free_image(self, i.data->val);\n    set_layers_dirty(self);\n    return vt_erase_itr(&self->images_by_internal_id, i);\n}\n\nstatic void\nremove_image(GraphicsManager *self, Image *img) {\n    image_map_itr i = vt_get(&self->images_by_internal_id, img->internal_id);\n    if (!vt_is_end(i)) remove_image_itr(self, i);\n}\n\nstatic void\nremove_images(GraphicsManager *self, bool(*predicate)(Image*), id_type skip_image_internal_id) {\n    for (image_map_itr i = vt_first(&self->images_by_internal_id); !vt_is_end(i);) {\n        Image *img = i.data->val;\n        if (img->internal_id != skip_image_internal_id && predicate(img)) i = remove_image_itr(self, i);\n        else i = vt_next(i);\n    }\n}\n\nvoid\ngrman_pause_rendering(GraphicsManager *self, GraphicsManager *dest) {\n    make_window_context_current(dest->window_id);\n    free_all_images(dest);\n    dest->render_data.count = 0;\n    if (self == NULL) return;\n    dest->window_id = self->window_id;\n    dest->layers_dirty = true;\n    dest->last_scrolled_by = 0;\n    dest->last_scroll_offset_lines = 0.0f;\n\n    iter_images(self) {\n        Image *clone = calloc(1, sizeof(Image)), *img = i.data->val;\n        if (!clone) continue;\n        memcpy(clone, img, sizeof(*clone));\n        memset(&clone->refs_by_internal_id, 0, sizeof(clone->refs_by_internal_id));\n        vt_init(&clone->refs_by_internal_id);\n        clone->extra_frames = NULL;\n        iter_refs(img) {\n            ImageRef *cr = malloc(sizeof(ImageRef));\n            if (cr) {\n                memcpy(cr, i.data->val, sizeof(*cr));\n                vt_insert(&clone->refs_by_internal_id, cr->internal_id, cr);\n            }\n        }\n        clone->texture = incref_texture_ref(img->texture);\n        vt_insert(&dest->images_by_internal_id, clone->internal_id, clone);\n    }\n}\n\n// Loading image data {{{\n\nstatic bool\ntrim_predicate(Image *img) {\n    return !img->root_frame_data_loaded || !vt_size(&img->refs_by_internal_id);\n}\n\nstatic void\napply_storage_quota(GraphicsManager *self, size_t storage_limit, id_type currently_added_image_internal_id) {\n    // First remove unreferenced images, even if they have an id\n    remove_images(self, trim_predicate, currently_added_image_internal_id);\n    if (self->used_storage < storage_limit) return;\n    size_t num_images = vt_size(&self->images_by_internal_id);\n    RAII_ALLOC(Image*, sorted, malloc(num_images * sizeof(Image*)));\n    if (!sorted) fatal(\"Out of memory\");\n    Image **p = sorted;\n    iter_images(self) { *p++ = i.data->val; }\n#define oldest_img_first(a, b) ((*a)->atime < (*b)->atime)\n    QSORT(Image*, sorted, num_images, oldest_img_first);\n#undef oldest_img_first\n\n    for (p = sorted; self->used_storage > storage_limit && num_images; p++, num_images--) remove_image(self, *p);\n    if (!num_images || !vt_size(&self->images_by_internal_id)) self->used_storage = 0;  // sanity check\n}\n\nstatic char command_response[512] = {0};\n\nstatic void\nset_command_failed_response(const char *code, const char *fmt, ...) {\n    va_list args;\n    va_start(args, fmt);\n    const size_t sz = sizeof(command_response)/sizeof(command_response[0]);\n    const int num = snprintf(command_response, sz, \"%s:\", code);\n    vsnprintf(command_response + num, sz - num, fmt, args);\n    va_end(args);\n}\n\n// Decode formats {{{\n#define ABRT(code, ...) { set_command_failed_response(#code, __VA_ARGS__); goto err; }\n\nstatic bool\nmmap_img_file(GraphicsManager *self, int fd, size_t sz, off_t offset) {\n    if (!sz) {\n        struct stat s;\n        if (fstat(fd, &s) != 0) ABRT(EBADF, \"Failed to fstat() the fd: %d file with error: [%d] %s\", fd, errno, strerror(errno));\n        sz = s.st_size;\n    }\n    void *addr = mmap(0, sz, PROT_READ, MAP_SHARED, fd, offset);\n    if (addr == MAP_FAILED) ABRT(EBADF, \"Failed to map image file fd: %d at offset: %zd with size: %zu with error: [%d] %s\", fd, offset, sz, errno, strerror(errno));\n    self->currently_loading.mapped_file = addr;\n    self->currently_loading.mapped_file_sz = sz;\n    return true;\nerr:\n    return false;\n}\n\n\nstatic const char*\nzlib_strerror(int ret) {\n#define Z(x) case x: return #x;\n    static char buf[128];\n    switch(ret) {\n        case Z_ERRNO:\n            return strerror(errno);\n        default:\n            snprintf(buf, sizeof(buf)/sizeof(buf[0]), \"Unknown error: %d\", ret);\n            return buf;\n        Z(Z_STREAM_ERROR);\n        Z(Z_DATA_ERROR);\n        Z(Z_MEM_ERROR);\n        Z(Z_BUF_ERROR);\n        Z(Z_VERSION_ERROR);\n    }\n#undef Z\n}\n\nstatic bool\ninflate_zlib(LoadData *load_data, uint8_t *buf, size_t bufsz) {\n    bool ok = false;\n    z_stream z;\n    uint8_t *decompressed = malloc(load_data->data_sz);\n    if (decompressed == NULL) fatal(\"Out of memory allocating decompression buffer\");\n    z.zalloc = Z_NULL;\n    z.zfree = Z_NULL;\n    z.opaque = Z_NULL;\n    z.avail_in = bufsz;\n    z.next_in = (Bytef*)buf;\n    z.avail_out = load_data->data_sz;\n    z.next_out = decompressed;\n    int ret;\n    if ((ret = inflateInit(&z)) != Z_OK) ABRT(ENOMEM, \"Failed to initialize inflate with error: %s\", zlib_strerror(ret));\n    if ((ret = inflate(&z, Z_FINISH)) != Z_STREAM_END) ABRT(EINVAL, \"Failed to inflate image data with error: %s\", zlib_strerror(ret));\n    if (z.avail_out) ABRT(EINVAL, \"Image data size post inflation does not match expected size\");\n    free_load_data(load_data);\n    load_data->buf_capacity = load_data->data_sz;\n    load_data->buf = decompressed;\n    load_data->buf_used = load_data->data_sz;\n    ok = true;\nerr:\n    inflateEnd(&z);\n    if (!ok) free(decompressed);\n    return ok;\n}\n\nstatic void\npng_error_handler(png_read_data *d UNUSED, const char *code, const char *msg) {\n    set_command_failed_response(code, \"%s\", msg);\n}\n\nstatic bool\ninflate_png(LoadData *load_data, uint8_t *buf, size_t bufsz) {\n    png_read_data d = {.err_handler=png_error_handler};\n    inflate_png_inner(&d, buf, bufsz, MAX_IMAGE_DIMENSION);\n    if (d.ok) {\n        free_load_data(load_data);\n        load_data->buf = d.decompressed;\n        load_data->buf_capacity = d.sz;\n        load_data->buf_used = d.sz;\n        load_data->data_sz = d.sz;\n        load_data->width = d.width; load_data->height = d.height;\n    }\n    else free(d.decompressed);\n    free(d.row_pointers);\n    return d.ok;\n}\n#undef ABRT\n// }}}\n\nstatic bool\nadd_trim_predicate(Image *img) {\n    return !img->root_frame_data_loaded || (!img->client_id && !vt_size(&img->refs_by_internal_id));\n}\n\nstatic void\nprint_png_read_error(png_read_data *d, const char *code, const char* msg) {\n    if (d->error.used >= d->error.capacity) {\n        size_t cap = MAX(2 * d->error.capacity, 1024 + d->error.used);\n        d->error.buf = realloc(d->error.buf, cap);\n        if (!d->error.buf) return;\n        d->error.capacity = cap;\n    }\n    d->error.used += snprintf(d->error.buf + d->error.used, d->error.capacity - d->error.used, \"%s: %s \", code, msg);\n}\n\nbool\npng_from_data(void *png_data, size_t png_data_sz, const char *path_for_error_messages, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) {\n    png_read_data d = {.err_handler=print_png_read_error};\n    inflate_png_inner(&d, png_data, png_data_sz, MAX_IMAGE_DIMENSION);\n    if (!d.ok) {\n        log_error(\"Failed to decode PNG image at: %s with error: %s\", path_for_error_messages, d.error.used > 0 ? d.error.buf : \"\");\n        free(d.decompressed); free(d.row_pointers); free(d.error.buf);\n        return false;\n    }\n    *data = d.decompressed;\n    free(d.row_pointers); free(d.error.buf);\n    *sz = d.sz;\n    *height = d.height; *width = d.width;\n    return true;\n}\n\nbool\npng_from_file_pointer(FILE *fp, const char *path_for_error_messages, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) {\n    size_t capacity = 16*1024, pos = 0;\n    unsigned char *buf = malloc(capacity);\n    if (!buf) { log_error(\"Out of memory reading PNG file at: %s\", path_for_error_messages); fclose(fp); return false; }\n    while (!feof(fp)) {\n        if (capacity - pos < 1024) {\n            capacity *= 2;\n            unsigned char *new_buf = realloc(buf, capacity);\n            if (!new_buf) {\n                free(buf);\n                log_error(\"Out of memory reading PNG file at: %s\", path_for_error_messages); fclose(fp); return false;\n            }\n            buf = new_buf;\n        }\n        pos += fread(buf + pos, sizeof(char), capacity - pos, fp);\n        int saved_errno = errno;\n        if (ferror(fp) && saved_errno != EINTR) {\n            log_error(\"Failed while reading from file: %s with error: %s\", path_for_error_messages, strerror(saved_errno));\n            free(buf);\n            return false;\n        }\n    }\n    bool ret = png_from_data(buf, pos, path_for_error_messages, data, width, height, sz);\n    free(buf);\n    return ret;\n}\n\nbool\npng_path_to_bitmap(const char* path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) {\n    FILE* fp = fopen(path, \"r\");\n    if (fp == NULL) {\n        log_error(\"The PNG image: %s could not be opened with error: %s\", path, strerror(errno));\n        return false;\n    }\n    bool ret = png_from_file_pointer(fp, path, data, width, height, sz);\n    fclose(fp); fp = NULL;\n    return ret;\n}\n\nbool\nimage_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) {\n    *data = NULL; *sz = 0; *width = 0; *height = 0;\n    RAII_PyObject(module, PyImport_ImportModule(\"kitty.render_cache\"));\n#define fail_on_python_error { log_error(\"Failed to convert image at %s to bitmap with python error:\", path); PyErr_Print(); return false; }\n    if (!module) fail_on_python_error;\n    RAII_PyObject(irc, PyObject_GetAttrString(module, \"default_image_render_cache\"));\n    if (!irc) fail_on_python_error;\n    RAII_PyObject(ret, PyObject_CallFunction(irc, \"s\", path));\n    if (!ret) fail_on_python_error;\n    size_t w = PyLong_AsSize_t(PyTuple_GET_ITEM(ret, 0));\n    size_t h = PyLong_AsSize_t(PyTuple_GET_ITEM(ret, 1));\n    int fd = PyLong_AsLong(PyTuple_GET_ITEM(ret, 2));\n#undef fail_on_python_error\n    size_t data_size = 8 + w * h * 4;\n    *data = mmap(NULL, data_size, PROT_READ, MAP_PRIVATE, fd, 0);\n    int saved_errno = errno;\n    safe_close(fd, __FILE__, __LINE__);\n    if (*data == MAP_FAILED) {\n        log_error(\"Failed to mmap bitmap data for image at %s with error: %s\", path, strerror(saved_errno));\n        return false;\n    }\n    *sz = data_size; *width = w; *height = h;\n    return true;\n}\n\nstatic Image*\nfind_or_create_image(GraphicsManager *self, uint32_t id, bool *existing) {\n    if (id) {\n        Image *img = img_by_client_id(self, id);\n        if (img) {\n            *existing = true;\n            return img;\n        }\n    }\n    *existing = false;\n    Image *ans = calloc(1, sizeof(Image));\n    if (!ans) fatal(\"Out of memory allocating Image object\");\n    ans->internal_id = next_id(&self->image_id_counter);\n    ans->texture = new_texture_ref();\n    vt_init(&ans->refs_by_internal_id);\n    if (vt_is_end(vt_insert(&self->images_by_internal_id, ans->internal_id, ans))) fatal(\"Out of memory\");\n    return ans;\n}\n\nstatic uint32_t\nget_free_client_id(const GraphicsManager *self) {\n    size_t num_images = vt_size(&((GraphicsManager*)self)->images_by_internal_id);\n    if (!num_images) return 1;\n    RAII_ALLOC(uint32_t, client_ids, malloc(num_images * sizeof(uint32_t)));\n    if (!client_ids) fatal(\"Out of memory\");\n    size_t count = 0;\n    iter_images((GraphicsManager*)self) {\n        Image *img = i.data->val;\n        if (img->client_id) client_ids[count++] = img->client_id;\n    }\n    if (!count) return 1;\n#define int_lt(a, b) ((*a)<(*b))\n    QSORT(uint32_t, client_ids, count, int_lt)\n#undef int_lt\n    uint32_t prev_id = 0, ans = 1;\n    for (size_t i = 0; i < count; i++) {\n        if (client_ids[i] == prev_id) continue;\n        prev_id = client_ids[i];\n        if (client_ids[i] != ans) break;\n        ans = client_ids[i] + 1;\n    }\n    return ans;\n}\n\n#define ABRT(code, ...) { set_command_failed_response(code, __VA_ARGS__); self->currently_loading.loading_completed_successfully = false; free_load_data(&self->currently_loading); return NULL; }\n\n#define MAX_DATA_SZ (4u * 100000000u)\nenum FORMATS { RGB=24, RGBA=32, PNG=100 };\n\nstatic Image*\nload_image_data(GraphicsManager *self, Image *img, const GraphicsCommand *g, const unsigned char transmission_type, const uint32_t data_fmt, const uint8_t *payload) {\n    int fd;\n    static char fname[2056] = {0};\n    LoadData *load_data = &self->currently_loading;\n\n    switch(transmission_type) {\n        case 'd':  // direct\n            if (load_data->buf_capacity - load_data->buf_used < g->payload_sz) {\n                if (load_data->buf_used + g->payload_sz > MAX_DATA_SZ || data_fmt != PNG) ABRT(\"EFBIG\", \"Too much data\");\n                load_data->buf_capacity = MIN(2 * load_data->buf_capacity, MAX_DATA_SZ);\n                load_data->buf = realloc(load_data->buf, load_data->buf_capacity);\n                if (load_data->buf == NULL) {\n                    load_data->buf_capacity = 0; load_data->buf_used = 0;\n                    ABRT(\"ENOMEM\", \"Out of memory\");\n                }\n            }\n            memcpy(load_data->buf + load_data->buf_used, payload, g->payload_sz);\n            load_data->buf_used += g->payload_sz;\n            if (!g->more) { load_data->loading_completed_successfully = true; load_data->loading_for = (const ImageAndFrame){0}; }\n            break;\n        case 'f': // file\n        case 't': // temporary file\n        case 's': // POSIX shared memory\n            if (g->payload_sz > 2048) ABRT(\"EINVAL\", \"Filename too long\");\n            snprintf(fname, sizeof(fname)/sizeof(fname[0]), \"%.*s\", (int)g->payload_sz, payload);\n            if (transmission_type == 's') fd = safe_shm_open(fname, O_RDONLY, 0);\n            else fd = safe_open(fname, O_CLOEXEC | O_RDONLY | O_NONBLOCK, 0);  // O_NONBLOCK so that opening a FIFO pipe does not block\n            if (fd == -1) ABRT(\"EBADF\", \"Failed to open file for graphics transmission with error: [%d] %s\", errno, strerror(errno));\n            if (global_state.boss && transmission_type != 's') {\n                RAII_PyObject(cret_, PyObject_CallMethod(global_state.boss, \"is_ok_to_read_image_file\", \"si\", fname, fd));\n                if (cret_ == NULL) {\n                    PyErr_Print();\n                    ABRT(\"EBADF\", \"Failed to check file for read permission\");\n                }\n                if (cret_ != Py_True) {\n                    log_error(\"Refusing to read image file as permission was denied\");\n                    ABRT(\"EPERM\", \"Permission denied to read image file\");\n                }\n            }\n            load_data->loading_completed_successfully = mmap_img_file(self, fd, g->data_sz, g->data_offset);\n            safe_close(fd, __FILE__, __LINE__);\n            if (transmission_type == 't' && strstr(fname, \"tty-graphics-protocol\") != NULL) {\n                if (global_state.boss) { call_boss(safe_delete_temp_file, \"s\", fname); }\n                else unlink(fname);\n            }\n            else if (transmission_type == 's') shm_unlink(fname);\n            if (!load_data->loading_completed_successfully) return NULL;\n            break;\n        default:\n            ABRT(\"EINVAL\", \"Unknown transmission type: %c\", g->transmission_type);\n    }\n    return img;\n}\n\nstatic Image*\nprocess_image_data(GraphicsManager *self, Image* img, const GraphicsCommand *g, const unsigned char transmission_type, const uint32_t data_fmt) {\n    bool needs_processing = g->compressed || data_fmt == PNG;\n    if (needs_processing) {\n        uint8_t *buf; size_t bufsz;\n#define IB { if (self->currently_loading.buf) { buf = self->currently_loading.buf; bufsz = self->currently_loading.buf_used; } else { buf = self->currently_loading.mapped_file; bufsz = self->currently_loading.mapped_file_sz; } }\n        switch(g->compressed) {\n            case 'z':\n                IB;\n                if (!inflate_zlib(&self->currently_loading, buf, bufsz)) {\n                    self->currently_loading.loading_completed_successfully = false; return NULL;\n                }\n                break;\n            case 0:\n                break;\n            default:\n                ABRT(\"EINVAL\", \"Unknown image compression: %c\", g->compressed);\n        }\n        switch(data_fmt) {\n            case PNG:\n                IB;\n                if (!inflate_png(&self->currently_loading, buf, bufsz)) {\n                    self->currently_loading.loading_completed_successfully = false; return NULL;\n                }\n                break;\n            default: break;\n        }\n#undef IB\n        self->currently_loading.data = self->currently_loading.buf;\n        if (self->currently_loading.buf_used < self->currently_loading.data_sz) {\n            ABRT(\"ENODATA\", \"Insufficient image data: %zu < %zu\", self->currently_loading.buf_used, self->currently_loading.data_sz);\n        }\n        if (self->currently_loading.mapped_file) {\n            munmap(self->currently_loading.mapped_file, self->currently_loading.mapped_file_sz);\n            self->currently_loading.mapped_file = NULL; self->currently_loading.mapped_file_sz = 0;\n        }\n    } else {\n        if (transmission_type == 'd') {\n            if (self->currently_loading.buf_used < self->currently_loading.data_sz) {\n                ABRT(\"ENODATA\", \"Insufficient image data: %zu < %zu\",  self->currently_loading.buf_used, self->currently_loading.data_sz);\n            } else self->currently_loading.data = self->currently_loading.buf;\n        } else {\n            if (self->currently_loading.mapped_file_sz < self->currently_loading.data_sz) {\n                ABRT(\"ENODATA\", \"Insufficient image data: %zu < %zu\",  self->currently_loading.mapped_file_sz, self->currently_loading.data_sz);\n            } else self->currently_loading.data = self->currently_loading.mapped_file;\n        }\n        self->currently_loading.loading_completed_successfully = true;\n    }\n    return img;\n}\n\nstatic Image*\ninitialize_load_data(GraphicsManager *self, const GraphicsCommand *g, Image *img, const unsigned char transmission_type, const uint32_t data_fmt, const uint32_t frame_id) {\n    free_load_data(&self->currently_loading);\n    self->currently_loading = (const LoadData){0};\n    self->currently_loading.start_command = *g;\n    self->currently_loading.width = g->data_width; self->currently_loading.height = g->data_height;\n    switch(data_fmt) {\n        case PNG:\n            if (g->data_sz > MAX_DATA_SZ) ABRT(\"EINVAL\", \"PNG data size too large\");\n            self->currently_loading.is_4byte_aligned = true;\n            self->currently_loading.is_opaque = false;\n            self->currently_loading.data_sz = g->data_sz ? g->data_sz : 1024 * 100;\n            break;\n        case RGB:\n        case RGBA:\n            self->currently_loading.data_sz = (size_t)g->data_width * g->data_height * (data_fmt / 8);\n            if (!self->currently_loading.data_sz) ABRT(\"EINVAL\", \"Zero width/height not allowed\");\n            self->currently_loading.is_4byte_aligned = data_fmt == RGBA || (self->currently_loading.width % 4 == 0);\n            self->currently_loading.is_opaque = data_fmt == RGB;\n            break;\n        default:\n            ABRT(\"EINVAL\", \"Unknown image format: %u\", data_fmt);\n    }\n    self->currently_loading.loading_for.image_id = img->internal_id;\n    self->currently_loading.loading_for.frame_id = frame_id;\n    if (transmission_type == 'd') {\n        self->currently_loading.buf_capacity = self->currently_loading.data_sz + (g->compressed ? 1024 : 10);  // compression header\n        self->currently_loading.buf = malloc(self->currently_loading.buf_capacity);\n        self->currently_loading.buf_used = 0;\n        if (self->currently_loading.buf == NULL) {\n            self->currently_loading.buf_capacity = 0; self->currently_loading.buf_used = 0;\n            ABRT(\"ENOMEM\", \"Out of memory\");\n        }\n    }\n    return img;\n}\n\n#define INIT_CHUNKED_LOAD { \\\n    self->currently_loading.start_command.more = g->more; \\\n    self->currently_loading.start_command.payload_sz = g->payload_sz; \\\n    g = &self->currently_loading.start_command; \\\n    tt = g->transmission_type ? g->transmission_type : 'd'; \\\n    fmt = g->format ? g->format : RGBA; \\\n}\n\nstatic void\nupload_to_gpu(GraphicsManager *self, Image *img, const bool is_opaque, const bool is_4byte_aligned, const uint8_t *data) {\n    if (!self->context_made_current_for_this_command) {\n        if (!self->window_id) return;\n        if (!make_window_context_current(self->window_id)) return;\n        self->context_made_current_for_this_command = true;\n    }\n    if (img->texture) {\n        send_image_to_gpu(&img->texture->id, data, img->width, img->height, is_opaque, is_4byte_aligned, true, REPEAT_CLAMP);\n    }\n}\n\nstatic Image*\nhandle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, bool *is_dirty, uint32_t iid, bool is_query) {\n    bool existing, init_img = true;\n    Image *img = NULL;\n    unsigned char tt = g->transmission_type ? g->transmission_type : 'd';\n    uint32_t fmt = g->format ? g->format : RGBA;\n    if (tt == 'd' && self->currently_loading.loading_for.image_id) init_img = false;\n    if (init_img) {\n        self->currently_loading.loading_for = (const ImageAndFrame){0};\n        if (g->data_width > MAX_IMAGE_DIMENSION || g->data_height > MAX_IMAGE_DIMENSION) ABRT(\"EINVAL\", \"Image too large, width or height greater than %u\", MAX_IMAGE_DIMENSION);\n        remove_images(self, add_trim_predicate, 0);\n        img = find_or_create_image(self, iid, &existing);\n        if (existing) {\n            free_image_resources(self, img);\n            img->texture = new_texture_ref();\n            img->root_frame_data_loaded = false;\n            img->is_drawn = false;\n            img->current_frame_shown_at = 0;\n            img->extra_framecnt = 0;\n            img->current_frame_index = 0;\n            img->animation_duration = 0;\n            img->animation_state = ANIMATION_STOPPED;\n            img->max_loops = 0; img->current_loop = 0;\n            *is_dirty = true;\n            set_layers_dirty(self);\n        } else {\n            img->client_id = iid;\n            img->client_number = g->image_number;\n            if (!img->client_id && img->client_number) {\n                img->client_id = get_free_client_id(self);\n                iid = img->client_id;\n            }\n        }\n        img->atime = monotonic(); img->used_storage = 0;\n        if (!initialize_load_data(self, g, img, tt, fmt, 0)) return NULL;\n        self->currently_loading.start_command.id = iid;\n    } else {\n        INIT_CHUNKED_LOAD;\n        img = img_by_internal_id(self, self->currently_loading.loading_for.image_id);\n        if (img == NULL) {\n            self->currently_loading.loading_for = (const ImageAndFrame){0};\n            ABRT(\"EILSEQ\", \"More payload loading refers to non-existent image\");\n        }\n    }\n    img = load_image_data(self, img, g, tt, fmt, payload);\n    if (!img || !self->currently_loading.loading_completed_successfully) return NULL;\n        self->currently_loading.loading_for = (const ImageAndFrame){0};\n    img = process_image_data(self, img, g, tt, fmt);\n    if (!img) return NULL;\n    size_t required_sz = (size_t)(self->currently_loading.is_opaque ? 3 : 4) * self->currently_loading.width * self->currently_loading.height;\n    if (self->currently_loading.data_sz != required_sz) ABRT(\"EINVAL\", \"Image dimensions: %ux%u do not match data size: %zu, expected size: %zu\", self->currently_loading.width, self->currently_loading.height, self->currently_loading.data_sz, required_sz);\n    if (self->currently_loading.loading_completed_successfully) {\n        img->width = self->currently_loading.width;\n        img->height = self->currently_loading.height;\n        if (img->root_frame.id) remove_from_cache(self, (const ImageAndFrame){.image_id=img->internal_id, .frame_id=img->root_frame.id});\n        img->root_frame = (const Frame){\n            .id = ++img->frame_id_counter,\n            .is_opaque = self->currently_loading.is_opaque,\n            .is_4byte_aligned = self->currently_loading.is_4byte_aligned,\n            .width = img->width, .height = img->height,\n        };\n        if (!is_query) {\n            if (!add_to_cache(self, (const ImageAndFrame){.image_id = img->internal_id, .frame_id=img->root_frame.id}, self->currently_loading.data, self->currently_loading.data_sz)) {\n                if (PyErr_Occurred()) PyErr_Print();\n                ABRT(\"ENOSPC\", \"Failed to store image data in disk cache\");\n            }\n            upload_to_gpu(self, img, img->root_frame.is_opaque, img->root_frame.is_4byte_aligned, self->currently_loading.data);\n            self->used_storage += required_sz;\n            img->used_storage = required_sz;\n        }\n        img->root_frame_data_loaded = true;\n    }\n    return img;\n#undef MAX_DATA_SZ\n}\n\nstatic const char*\nfinish_command_response(const GraphicsCommand *g, bool data_loaded) {\n    static char rbuf[sizeof(command_response)/sizeof(command_response[0]) + 128];\n    bool is_ok_response = !command_response[0];\n    if (g->quiet) {\n        if (is_ok_response || g->quiet > 1) return NULL;\n    }\n    if (g->id || g->image_number) {\n        if (is_ok_response) {\n            if (!data_loaded) return NULL;\n            snprintf(command_response, 10, \"OK\");\n        }\n        size_t pos = 0;\n        rbuf[pos++] = 'G';\n#define print(fmt, ...) if (arraysz(rbuf) - 1 > pos) pos += snprintf(rbuf + pos, arraysz(rbuf) - 1 - pos, fmt, __VA_ARGS__)\n        if (g->id) print(\"i=%u\", g->id);\n        if (g->image_number) print(\",I=%u\", g->image_number);\n        if (g->placement_id) print(\",p=%u\", g->placement_id);\n        if (g->num_lines && (g->action == 'f' || g->action == 'a')) print(\",r=%u\", g->num_lines);\n        print(\";%s\", command_response);\n        return rbuf;\n#undef print\n    }\n    return NULL;\n}\n\n// }}}\n\n// Displaying images {{{\n\nstatic void\nupdate_src_rect(ImageRef *ref, Image *img) {\n    // The src rect in OpenGL co-ords [0, 1] with origin at top-left corner of image\n    ref->src_rect.left = (float)ref->src_x / (float)img->width;\n    ref->src_rect.right = (float)(ref->src_x + ref->src_width) / (float)img->width;\n    ref->src_rect.top = (float)ref->src_y / (float)img->height;\n    ref->src_rect.bottom = (float)(ref->src_y + ref->src_height) / (float)img->height;\n}\n\nstatic void\nupdate_dest_rect(ImageRef *ref, uint32_t num_cols, uint32_t num_rows, CellPixelSize cell) {\n    uint32_t t;\n    if (num_cols == 0) {\n        if (num_rows == 0) {\n            t = (uint32_t)(ref->src_width + ref->cell_x_offset);\n            num_cols = t / cell.width;\n            if (t > num_cols * cell.width) num_cols += 1;\n        } else {\n            double height_px = cell.height * num_rows + ref->cell_y_offset;\n            double width_px = height_px * ref->src_width / (double) ref->src_height;\n            num_cols = (uint32_t)ceil(width_px / cell.width);\n        }\n    }\n    if (num_rows == 0) {\n        if (num_cols == 0) {\n            t = (uint32_t)(ref->src_height + ref->cell_y_offset);\n            num_rows = t / cell.height;\n            if (t > num_rows * cell.height) num_rows += 1;\n        } else {\n            double width_px = cell.width * num_cols + ref->cell_x_offset;\n            double height_px = width_px * ref->src_height / (double)ref->src_width;\n            num_rows = (uint32_t)ceil(height_px / cell.height);\n        }\n    }\n    ref->effective_num_rows = num_rows;\n    ref->effective_num_cols = num_cols;\n}\n\nstatic ImageRef*\ncreate_ref(Image *img, ImageRef *clone_from) {\n    ImageRef *ans = calloc(1, sizeof(ImageRef));\n    if (!ans) fatal(\"Out of memory creating ImageRef\");\n    if (clone_from) *ans = *clone_from;\n    ans->internal_id = next_id(&img->ref_id_counter);\n    if (vt_is_end(vt_insert(&img->refs_by_internal_id, ans->internal_id, ans))) fatal(\"Out of memory\");\n    return ans;\n}\n\nstatic inline bool\nis_cell_image(const ImageRef *self) { return self->virtual_ref_id != 0; }\n\n// Create a real image ref for a virtual image ref (placement) positioned in the\n// given cells. This is used for images positioned using Unicode placeholders.\n//\n// The image is resized to fit a box of cells with dimensions\n// `image_ref->columns` by `image_ref->rows`. The parameters `img_col`,\n// `img_row, `columns`, `rows` describe a part of this box that we want to\n// display.\n//\n// Parameters:\n// - `self` - the graphics manager\n// - `screen_row` - the starting row of the screen\n// - `screen_col` - the starting column of the screen\n// - `image_id` - the id of the image\n// - `placement_id` - the id of the placement (0 to find it automatically), it\n//                    must be a virtual placement\n// - `img_col` - the column of the image box we want to start with (base 0)\n// - `img_row` - the row of the image box we want to start with (base 0)\n// - `columns` - the number of columns we want to display\n// - `rows` - the number of rows we want to display\n// - `cell` - the size of a screen cell\nvoid grman_put_cell_image(GraphicsManager *self, uint32_t screen_row,\n                            uint32_t screen_col, uint32_t image_id,\n                            uint32_t placement_id, uint32_t img_col,\n                            uint32_t img_row, uint32_t columns, uint32_t rows,\n                            CellPixelSize cell) {\n    Image *img = img_by_client_id(self, image_id);\n    if (img == NULL) return;\n\n    ImageRef *virt_img_ref = NULL;\n    if (placement_id) {\n        // Find the placement by the id. It must be a virtual placement.\n        iter_refs(img) { ImageRef *r = i.data->val;\n            if (r->is_virtual_ref && r->client_id == placement_id) {\n                virt_img_ref = r;\n                break;\n            }\n        }\n    } else {\n        // Find the first virtual image placement.\n        iter_refs(img) { ImageRef *r = i.data->val;\n            if (r->is_virtual_ref) {\n                virt_img_ref = r;\n                break;\n            }\n        }\n    }\n\n    if (!virt_img_ref) return;\n\n    // Create the ref structure on stack first. We will not create a real\n    // reference if the image is completely out of bounds.\n    ImageRef ref = {0};\n    ref.virtual_ref_id = virt_img_ref->internal_id;\n\n    uint32_t img_rows = virt_img_ref->num_rows;\n    uint32_t img_columns = virt_img_ref->num_cols;\n    // If the number of columns or rows for the image is not set, compute them\n    // in such a way that the image is as close as possible to its natural size.\n    if (img_columns == 0)\n        img_columns = (img->width + cell.width - 1) / cell.width;\n    if (img_rows == 0) img_rows = (img->height + cell.height - 1) / cell.height;\n\n    ref.start_row = screen_row;\n    ref.start_column = screen_col;\n    ref.num_cols = columns;\n    ref.num_rows = rows;\n\n    // The image is fit to the destination box of size\n    //    (cell.width * img_columns) by (cell.height * img_rows)\n    // The conversion from source (image) coordinates to destination (box)\n    // coordinates is done by the following formula:\n    //    x_dst = x_src * x_scale + x_offset\n    //    y_dst = y_src * y_scale + y_offset\n    float x_offset, y_offset, x_scale, y_scale;\n\n    // Fit the image to the box while preserving aspect ratio\n    if (img->width * img_rows * cell.height > img->height * img_columns * cell.width) {\n        // Fit to width and center vertically.\n        x_offset = 0;\n        x_scale = (float)(img_columns * cell.width) / MAX(1u, img->width);\n        y_scale = x_scale;\n        y_offset = (img_rows * cell.height - img->height * y_scale) / 2;\n    } else {\n        // Fit to height and center horizontally.\n        y_offset = 0;\n        y_scale = (float)(img_rows * cell.height) / MAX(1u, img->height);\n        x_scale = y_scale;\n        x_offset = (img_columns * cell.width - img->width * x_scale) / 2;\n    }\n\n    // Now we can compute source (image) coordinates from destination (box)\n    // coordinates by formula:\n    //     x_src = (x_dst - x_offset) / x_scale\n    //     y_src = (y_dst - y_offset) / y_scale\n\n    // Destination (box) coordinates of the rectangle we want to display.\n    uint32_t x_dst = img_col * cell.width;\n    uint32_t y_dst = img_row * cell.height;\n    uint32_t w_dst = columns * cell.width;\n    uint32_t h_dst = rows * cell.height;\n\n    // Compute the source coordinates of the rectangle.\n    ref.src_x = (x_dst - x_offset) / x_scale;\n    ref.src_y = (y_dst - y_offset) / y_scale;\n    ref.src_width = w_dst / x_scale;\n    ref.src_height = h_dst / y_scale;\n\n    // If the top left corner is out of bounds of the source image, we can\n    // adjust cell offsets and the starting row/column. And if the rectangle is\n    // completely out of bounds, we can avoid creating a real reference. This\n    // is just an optimization, the image will be displayed correctly even if we\n    // do not do this.\n    if (ref.src_x < 0) {\n        ref.src_width += ref.src_x;\n        ref.cell_x_offset = (uint32_t)(-ref.src_x * x_scale);\n        ref.src_x = 0;\n        uint32_t col_offset = ref.cell_x_offset / cell.width;\n        ref.cell_x_offset %= cell.width;\n        ref.start_column += col_offset;\n        if (ref.num_cols <= col_offset) return;\n        ref.num_cols -= col_offset;\n    }\n    if (ref.src_y < 0) {\n        ref.src_height += ref.src_y;\n        ref.cell_y_offset = (uint32_t)(-ref.src_y * y_scale);\n        ref.src_y = 0;\n        uint32_t row_offset = ref.cell_y_offset / cell.height;\n        ref.cell_y_offset %= cell.height;\n        ref.start_row += row_offset;\n        if (ref.num_rows <= row_offset) return;\n        ref.num_rows -= row_offset;\n    }\n\n    // For the bottom right corner we can remove only completely empty rows and\n    // columns.\n    if (ref.src_x + ref.src_width > img->width) {\n        float redundant_w = ref.src_x + ref.src_width - img->width;\n        uint32_t redundant_cols = (uint32_t)(redundant_w * x_scale) / cell.width;\n        if (ref.num_cols <= redundant_cols) return;\n        ref.src_width -= redundant_cols * cell.width / x_scale;\n        ref.num_cols -= redundant_cols;\n    }\n    if (ref.src_y + ref.src_height > img->height) {\n        float redundant_h = ref.src_y + ref.src_height - img->height;\n        uint32_t redundant_rows = (uint32_t)(redundant_h * y_scale) / cell.height;\n        if (ref.num_rows <= redundant_rows) return;\n        ref.src_height -= redundant_rows * cell.height / y_scale;\n        ref.num_rows -= redundant_rows;\n    }\n\n    // The cursor will be drawn on top of the image.\n    ref.z_index = -1;\n\n    // Create a real ref.\n    ImageRef *real_ref = create_ref(img, &ref);\n\n    img->atime = monotonic();\n    set_layers_dirty(self);\n\n    update_src_rect(real_ref, img);\n    update_dest_rect(real_ref, ref.num_cols, ref.num_rows, cell);\n}\n\nstatic void remove_ref(Image *img, ImageRef *ref);\nstatic ref_map_itr remove_ref_itr(Image *img, ref_map_itr x);\n\nstatic bool\nhas_good_ancestry(GraphicsManager *self, ImageRef *ref) {\n    ImageRef *r = ref;\n    unsigned depth = 0;\n    while (r->parent.img) {\n        if (r == ref && depth) {\n            set_command_failed_response(\"ECYCLE\", \"This parent reference creates a cycle\");\n            return false;\n        }\n        if (depth++ >= PARENT_DEPTH_LIMIT) {\n            set_command_failed_response(\"ETOODEEP\", \"Too many levels of parent references\");\n            return false;\n        }\n        Image *parent = img_by_internal_id(self, r->parent.img);\n        if (!parent) {\n            set_command_failed_response(\"ENOENT\", \"One of the ancestors of this ref with image id: %llu not found\", r->parent.img);\n            return false;\n        }\n        ImageRef *parent_ref = ref_by_internal_id(parent, r->parent.ref);\n        if (!parent_ref) {\n            set_command_failed_response(\"ENOENT\", \"One of the ancestors of this ref with image id: %llu and ref id: %llu not found\", r->parent.img, r->parent.ref);\n            return false;\n        }\n        r = parent_ref;\n    }\n    return true;\n}\n\nstatic uint32_t\nhandle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, bool *is_dirty, Image *img, CellPixelSize cell) {\n    if (g->unicode_placement && g->parent_id) {\n        set_command_failed_response(\"EINVAL\", \"Put command creating a virtual placement cannot refer to a parent\"); return g->id;\n    }\n    if (img == NULL) {\n        if (g->id) img = img_by_client_id(self, g->id);\n        else if (g->image_number) img = img_by_client_number(self, g->image_number);\n        if (img == NULL) { set_command_failed_response(\"ENOENT\", \"Put command refers to non-existent image with id: %u and number: %u\", g->id, g->image_number); return g->id; }\n    }\n    if (!img->root_frame_data_loaded) { set_command_failed_response(\"ENOENT\", \"Put command refers to image with id: %u that could not load its data\", g->id); return img->client_id; }\n    id_type parent_id = 0, parent_placement_id = 0;\n    if (g->parent_id) {\n        Image *parent = img_by_client_id(self, g->parent_id);\n        if (!parent) {\n            set_command_failed_response(\"ENOPARENT\", \"Put command refers to a parent image with id: %u that does not exist\", g->parent_id);\n            return g->id;\n        }\n        if (!vt_size(&parent->refs_by_internal_id)) {\n            set_command_failed_response(\"ENOPARENT\", \"Put command refers to a parent image with id: %u that has no placements\", g->parent_id);\n            return g->id;\n        }\n        ImageRef *parent_ref = vt_first(&parent->refs_by_internal_id).data->val;\n        if (g->parent_placement_id) {\n            parent_ref = ref_by_client_id(parent, g->parent_placement_id);\n            if (!parent_ref) {\n                set_command_failed_response(\"ENOPARENT\", \"Put command refers to a parent image placement with id: %u and placement id: %u that does not exist\", g->parent_id, g->parent_placement_id);\n                return g->id;\n            }\n        }\n        parent_id = parent->internal_id;\n        parent_placement_id = parent_ref->internal_id;\n    }\n    ImageRef *ref = NULL;\n    if (g->placement_id && img->client_id) {\n        iter_refs(img) { ImageRef *r = i.data->val;\n            if (r->client_id == g->placement_id) {\n                ref = r;\n                if (parent_id && parent_id == img->internal_id && parent_placement_id && parent_placement_id == r->internal_id) {\n                    set_command_failed_response(\"EINVAL\", \"Put command refers to itself as its own parent\");\n                    return g->id;\n                }\n                if (parent_id && parent_placement_id) {\n                    id_type rp = ref->parent.img, rpp = ref->parent.ref;\n                    ref->parent.img = parent_id; ref->parent.ref = parent_placement_id;\n                    bool ok = has_good_ancestry(self, ref);\n                    ref->parent.img = rp; ref->parent.ref = rpp;\n                    if (!ok) return g->id;\n                }\n                break;\n            }\n        }\n    }\n    if (ref == NULL) ref = create_ref(img, NULL);\n\n    *is_dirty = true;\n    set_layers_dirty(self);\n    img->atime = monotonic();\n    ref->src_x = g->x_offset; ref->src_y = g->y_offset; ref->src_width = g->width ? g->width : img->width; ref->src_height = g->height ? g->height : img->height;\n    ref->src_width = MIN(ref->src_width, img->width - ((float)img->width > ref->src_x ? ref->src_x : (float)img->width));\n    ref->src_height = MIN(ref->src_height, img->height - ((float)img->height > ref->src_y ? ref->src_y : (float)img->height));\n    ref->z_index = g->z_index;\n    ref->start_row = c->y; ref->start_column = c->x;\n    ref->cell_x_offset = MIN(g->cell_x_offset, cell.width - 1);\n    ref->cell_y_offset = MIN(g->cell_y_offset, cell.height - 1);\n    ref->num_cols = g->num_cells; ref->num_rows = g->num_lines;\n    if (img->client_id) ref->client_id = g->placement_id;\n    update_src_rect(ref, img);\n    update_dest_rect(ref, g->num_cells, g->num_lines, cell);\n    ref->parent.img = parent_id;\n    ref->parent.ref = parent_placement_id;\n    ref->parent.offset.x = g->offset_from_parent_x;\n    ref->parent.offset.y = g->offset_from_parent_y;\n    ref->is_virtual_ref = false;\n    if (g->unicode_placement) {\n        ref->is_virtual_ref = true;\n        ref->start_row = ref->start_column = 0;\n    }\n    if (ref->parent.img) {\n        if (!has_good_ancestry(self, ref)) {\n            remove_ref(img, ref);\n            return g->id;\n        }\n    } else {\n        // Move the cursor, the screen will take care of ensuring it is in bounds\n        if (g->cursor_movement != 1 && !g->unicode_placement) {\n            c->x += ref->effective_num_cols;\n            if (ref->effective_num_rows) c->y += ref->effective_num_rows - 1;\n        }\n    }\n    return img->client_id;\n}\n\nvoid\ngpu_data_for_image(ImageRenderData *ans, float left, float top, float right, float bottom) {\n    // For dest rect: x-axis is from -1 to 1, y axis is from 1 to -1\n    static const ImageRef source_rect = { .src_rect = { .left=0, .top=0, .bottom=1, .right=1 }};\n    ans->src_rect = source_rect.src_rect;\n    ans->dest_rect = (ImageRect){ .left = left, .right = right, .top = top, .bottom = bottom };\n    ans->group_count = 1;\n}\n\nstatic bool\nresolve_cell_ref(const Image *img, id_type virt_ref_id, int32_t *start_row, int32_t *start_column) {\n    *start_row = 0; *start_column = 0;\n    bool found = false;\n    iter_refs((Image*)img) { ImageRef *ref = i.data->val;\n        if (ref->virtual_ref_id == virt_ref_id) {\n            if (!found || ref->start_row < *start_row) *start_row = ref->start_row;\n            if (!found || ref->start_column < *start_column) *start_column = ref->start_column;\n            found = true;\n        }\n    }\n    return found;\n}\n\nstatic bool\nresolve_parent_offset(const GraphicsManager *self, const ImageRef *ref, int32_t *start_row, int32_t *start_column, bool *has_virtual_ancestor) {\n    *start_row = 0; *start_column = 0; *has_virtual_ancestor = false;\n    int32_t x = 0, y = 0;\n    unsigned depth = 0;\n    ImageRef cell_ref = {0};\n    while (ref->parent.img) {\n        if (depth++ >= PARENT_DEPTH_LIMIT) return false;  // either a cycle or too many ancestors\n        Image *img = img_by_internal_id(self, ref->parent.img);\n        if (!img) return false;\n        ImageRef *parent = ref_by_internal_id(img, ref->parent.ref);\n        if (!parent) return false;\n        if (parent->is_virtual_ref) {\n            *has_virtual_ancestor = true;\n            if (!resolve_cell_ref(img, parent->internal_id, &cell_ref.start_row, &cell_ref.start_column)) return false;\n            parent = &cell_ref;\n        }\n        x += ref->parent.offset.x;\n        y += ref->parent.offset.y;\n        ref = parent;\n    }\n    *start_row = ref->start_row + y;\n    *start_column = ref->start_column + x;\n    return true;\n}\n\n\nbool\ngrman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scroll_offset_lines, float screen_left, float screen_top, float dx, float dy, unsigned int num_cols, unsigned int num_rows, CellPixelSize cell) {\n    if (self->last_scrolled_by != scrolled_by || self->last_scroll_offset_lines != scroll_offset_lines) set_layers_dirty(self);\n    self->last_scrolled_by = scrolled_by;\n    self->last_scroll_offset_lines = scroll_offset_lines;\n    if (!self->layers_dirty) return false;\n    self->layers_dirty = false;\n    size_t i;\n    self->num_of_below_refs = 0;\n    self->num_of_negative_refs = 0;\n    self->num_of_positive_refs = 0;\n    ImageRect r;\n    float screen_width = dx * num_cols, screen_height = dy * num_rows;\n    float screen_bottom = screen_top - screen_height;\n    float screen_width_px = num_cols * cell.width;\n    float screen_height_px = num_rows * cell.height;\n    float y0 = screen_top - dy * ((float)scrolled_by + scroll_offset_lines);\n\n    // Iterate over all visible refs and create render data\n    self->render_data.count = 0;\n\n    for (image_map_itr imgitr = vt_first(&self->images_by_internal_id); !vt_is_end(imgitr); ) {\n        Image *img = imgitr.data->val;\n        bool was_drawn = img->is_drawn, ref_removed = false;\n        img->is_drawn = false;\n\n        for (ref_map_itr refitr = vt_first(&img->refs_by_internal_id); !vt_is_end(refitr); ) {\n            ImageRef *ref = refitr.data->val;\n            if (ref->is_virtual_ref) { refitr = vt_next(refitr); continue; }\n            int32_t start_row = ref->start_row, start_column = ref->start_column;\n            if (ref->parent.img) {\n                bool has_virtual_ancestor;\n                if (!resolve_parent_offset(self, ref, &start_row, &start_column, &has_virtual_ancestor)) {\n                    if (!has_virtual_ancestor) {\n                        refitr = remove_ref_itr(img, refitr);\n                        ref_removed = true;\n                    } else refitr = vt_next(refitr);\n                    continue;\n                }\n            }\n            r.top = y0 - start_row * dy - dy * (float)ref->cell_y_offset / (float)cell.height;\n            r.left = screen_left + start_column * dx + dx * (float)ref->cell_x_offset / (float) cell.width;\n\n            int32_t nr = ref->num_rows, nc = ref->num_cols;\n            if (nr) {\n                r.bottom = y0 - (start_row + nr) * dy;\n                if (nc) r.right = screen_left + (start_column + nc) * dx;\n                else {\n                    double height_px = (((double)r.top - r.bottom) / screen_height) * screen_height_px;\n                    double width_px = height_px * ref->src_width / (double) ref->src_height;\n                    r.right = r.left + (float)((width_px / screen_width_px) * screen_width);\n                }\n            } else {\n                if (nc) r.right = screen_left + (start_column + nc) * dx;\n                else r.right = r.left + screen_width * (float)ref->src_width / screen_width_px;\n                double width_px = (((double)r.right - r.left) / screen_width) * screen_width_px;\n                double height_px = width_px * ref->src_height / (double)ref->src_width;\n                r.bottom = r.top - (float)((height_px / screen_height_px) * screen_height);\n            }\n\n            if (r.top <= screen_bottom || r.bottom >= screen_top) { refitr = vt_next(refitr); continue; }  // not visible\n\n            if (ref->z_index < ((int32_t)INT32_MIN/2))\n                self->num_of_below_refs++;\n            else if (ref->z_index < 0)\n                self->num_of_negative_refs++;\n            else\n                self->num_of_positive_refs++;\n            ensure_space_for(&(self->render_data), item, ImageRenderData, self->render_data.count + 1, capacity, 64, true);\n            ImageRenderData *rd = self->render_data.item + self->render_data.count;\n            zero_at_ptr(rd);\n            rd->dest_rect = r; rd->src_rect = ref->src_rect;\n            self->render_data.count++;\n            rd->z_index = ref->z_index; rd->image_id = img->internal_id; rd->ref_id = ref->internal_id;\n            rd->texture_id = texture_id_for_img(img);\n            img->is_drawn = true;\n            refitr = vt_next(refitr);\n        }\n        if (ref_removed && !vt_size(&img->refs_by_internal_id)) {\n            imgitr = remove_image_itr(self, imgitr);\n            continue;\n        }\n        if (img->is_drawn && !was_drawn && img->animation_state != ANIMATION_STOPPED && img->extra_framecnt && img->animation_duration) {\n            self->has_images_needing_animation = true;\n            global_state.check_for_active_animated_images = true;\n        }\n        imgitr = vt_next(imgitr);\n    }\n    if (!self->render_data.count) return false;\n    // Sort visible refs in draw order (z-index, img, ref)\n#define lt(a, b) ( (a)->z_index < (b)->z_index || ((a)->z_index == (b)->z_index && ( \\\n                (a)->image_id < (b)->image_id || ((a)->image_id == (b)->image_id && a->ref_id < b->ref_id))) )\n    QSORT(ImageRenderData, self->render_data.item, self->render_data.count, lt);\n#undef lt\n    // Calculate the group counts\n    i = 0;\n    while (i < self->render_data.count) {\n        id_type num_identical = 1, image_id = self->render_data.item[i].image_id, start = i;\n        while (++i < self->render_data.count) {\n            if (self->render_data.item[i].image_id != image_id) break;\n            num_identical++;\n        }\n        while (num_identical > 0) {\n            self->render_data.item[start++].group_count = num_identical--;\n        }\n    }\n    return true;\n}\n\n// }}}\n\n// Animation {{{\n#define DEFAULT_GAP 40\n\nstatic Frame*\ncurrent_frame(Image *img) {\n    if (img->current_frame_index > img->extra_framecnt) return NULL;\n    return img->current_frame_index ? img->extra_frames + img->current_frame_index - 1 : &img->root_frame;\n}\n\nstatic Frame*\nframe_for_id(Image *img, const uint32_t frame_id) {\n    if (img->root_frame.id == frame_id) return &img->root_frame;\n    for (unsigned i = 0; i < img->extra_framecnt; i++) {\n        if (img->extra_frames[i].id == frame_id) return img->extra_frames + i;\n    }\n    return NULL;\n}\n\nstatic Frame*\nframe_for_number(Image *img, const uint32_t frame_number) {\n    switch(frame_number) {\n        case 1:\n            return &img->root_frame;\n        case 0:\n            return NULL;\n        default:\n            if (frame_number - 2 < img->extra_framecnt) return img->extra_frames + frame_number - 2;\n            return NULL;\n    }\n}\n\nstatic void\nchange_gap(Image *img, Frame *f, int32_t gap) {\n    uint32_t prev_gap = f->gap;\n    f->gap = MAX(0, gap);\n    img->animation_duration = prev_gap < img->animation_duration ? img->animation_duration - prev_gap : 0;\n    img->animation_duration += f->gap;\n}\n\ntypedef struct {\n    uint8_t *buf;\n    bool is_4byte_aligned, is_opaque;\n} CoalescedFrameData;\n\nstatic void\nblend_on_opaque(uint8_t *under_px, const uint8_t *over_px) {\n    const float alpha = (float)over_px[3] / 255.f;\n    const float alpha_op = 1.f - alpha;\n    for (unsigned i = 0; i < 3; i++) under_px[i] = (uint8_t)(over_px[i] * alpha + under_px[i] * alpha_op);\n}\n\nstatic void\nalpha_blend(uint8_t *dest_px, const uint8_t *src_px) {\n    if (src_px[3]) {\n        const float dest_a = (float)dest_px[3] / 255.f, src_a = (float)src_px[3] / 255.f;\n        const float alpha = src_a + dest_a * (1.f - src_a);\n        dest_px[3] = (uint8_t)(255 * alpha);\n        if (!dest_px[3]) { dest_px[0] = 0; dest_px[1] = 0; dest_px[2] = 0; return; }\n        for (unsigned i = 0; i < 3; i++) dest_px[i] = (uint8_t)((src_px[i] * src_a + dest_px[i] * dest_a * (1.f - src_a))/alpha);\n    }\n}\n\ntypedef struct {\n    bool needs_blending;\n    uint32_t over_px_sz, under_px_sz;\n    uint32_t over_width, over_height, under_width, under_height, over_offset_x, over_offset_y, under_offset_x, under_offset_y;\n    uint32_t stride;\n} ComposeData;\n\n#define COPY_RGB under_px[0] = over_px[0]; under_px[1] = over_px[1]; under_px[2] = over_px[2];\n#define COPY_PIXELS \\\n    if (d.needs_blending) { \\\n        if (d.under_px_sz == 3) { \\\n            ROW_ITER PIX_ITER blend_on_opaque(under_px, over_px); }} \\\n        } else { \\\n            ROW_ITER PIX_ITER alpha_blend(under_px, over_px); }} \\\n        } \\\n    } else { \\\n        if (d.under_px_sz == 4) { \\\n            if (d.over_px_sz == 4) { \\\n                ROW_ITER PIX_ITER COPY_RGB under_px[3] = over_px[3]; }} \\\n            } else { \\\n                ROW_ITER PIX_ITER COPY_RGB under_px[3] = 255; }} \\\n            } \\\n        } else { \\\n            ROW_ITER PIX_ITER COPY_RGB }} \\\n        } \\\n    } \\\n\n\nstatic void\ncompose_rectangles(const ComposeData d, uint8_t *under_data, const uint8_t *over_data) {\n    // compose two equal sized, non-overlapping rectangles at different offsets\n    // does not do bounds checking on the data arrays\n    const bool can_copy_rows = !d.needs_blending && d.over_px_sz == d.under_px_sz;\n    const unsigned min_width = MIN(d.under_width, d.over_width);\n#define ROW_ITER for (unsigned y = 0; y < d.under_height && y < d.over_height; y++) { \\\n        uint8_t *under_row = under_data + (y + d.under_offset_y) * d.under_px_sz * d.stride + (d.under_offset_x * d.under_px_sz); \\\n        const uint8_t *over_row = over_data + (y + d.over_offset_y) * d.over_px_sz * d.stride + (d.over_offset_x * d.over_px_sz);\n    if (can_copy_rows) {\n        ROW_ITER memcpy(under_row, over_row, (size_t)d.over_px_sz * min_width);}\n        return;\n    }\n#define PIX_ITER for (unsigned x = 0; x < min_width; x++) { \\\n        uint8_t *under_px = under_row + (d.under_px_sz * x); \\\n        const uint8_t *over_px = over_row + (d.over_px_sz * x);\n    COPY_PIXELS\n#undef PIX_ITER\n#undef ROW_ITER\n}\n\nstatic void\ncompose(const ComposeData d, uint8_t *under_data, const uint8_t *over_data) {\n    const bool can_copy_rows = !d.needs_blending && d.over_px_sz == d.under_px_sz;\n    unsigned min_row_sz = d.over_offset_x < d.under_width ? d.under_width - d.over_offset_x : 0;\n    min_row_sz = MIN(min_row_sz, d.over_width);\n#define ROW_ITER for (unsigned y = 0; y + d.over_offset_y < d.under_height && y < d.over_height; y++) { \\\n        uint8_t *under_row = under_data + (y + d.over_offset_y) * d.under_px_sz * d.under_width + d.under_px_sz * d.over_offset_x; \\\n        const uint8_t *over_row = over_data + y * d.over_px_sz * d.over_width;\n#define END_ITER }\n    if (can_copy_rows) {\n        ROW_ITER memcpy(under_row, over_row, (size_t)d.over_px_sz * min_row_sz); END_ITER\n        return;\n    }\n#define PIX_ITER for (unsigned x = 0; x < min_row_sz; x++) { \\\n        uint8_t *under_px = under_row + (d.under_px_sz * x); \\\n        const uint8_t *over_px = over_row + (d.over_px_sz * x);\n    COPY_PIXELS\n#undef COPY_RGB\n#undef PIX_ITER\n#undef ROW_ITER\n#undef END_ITER\n}\n\nstatic CoalescedFrameData\nget_coalesced_frame_data_standalone(const Image *img, const Frame *f, uint8_t *frame_data) {\n    CoalescedFrameData ans = {0};\n    bool is_full_frame = f->width == img->width && f->height == img->height && !f->x && !f->y;\n    if (is_full_frame) {\n        ans.buf = frame_data;\n        ans.is_4byte_aligned = f->is_4byte_aligned;\n        ans.is_opaque = f->is_opaque;\n        return ans;\n    }\n    const unsigned bytes_per_pixel = f->is_opaque ? 3 : 4;\n    uint8_t *base;\n    if (f->bgcolor) {\n        base = malloc((size_t)img->width * img->height * bytes_per_pixel);\n        if (base) {\n            uint8_t *p = base;\n            const uint8_t r = (f->bgcolor >> 24) & 0xff,\n                  g = (f->bgcolor >> 16) & 0xff, b = (f->bgcolor >> 8) & 0xff, a = f->bgcolor & 0xff;\n            if (bytes_per_pixel == 4) {\n                for (uint32_t i = 0; i < img->width * img->height; i++) {\n                    *(p++) = r; *(p++) = g; *(p++) = b; *(p++) = a;\n                }\n            } else {\n                for (uint32_t i = 0; i < img->width * img->height; i++) {\n                    *(p++) = r; *(p++) = g; *(p++) = b;\n                }\n            }\n        }\n    } else base = calloc((size_t)img->width * img->height, bytes_per_pixel);\n    if (!base) { free(frame_data); return ans; }\n    ComposeData d = {\n        .over_px_sz = bytes_per_pixel, .under_px_sz = bytes_per_pixel,\n        .over_width = f->width, .over_height = f->height, .over_offset_x = f->x, .over_offset_y = f->y,\n        .under_width = img->width, .under_height = img->height,\n        .needs_blending = f->alpha_blend && !f->is_opaque\n    };\n    compose(d, base, frame_data);\n    ans.buf = base;\n    ans.is_4byte_aligned = bytes_per_pixel == 4 || (img->width % 4) == 0;\n    ans.is_opaque = f->is_opaque;\n    free(frame_data);\n    return ans;\n}\n\n\nstatic CoalescedFrameData\nget_coalesced_frame_data_impl(GraphicsManager *self, Image *img, const Frame *f, unsigned count) {\n    CoalescedFrameData ans = {0};\n    if (count > 32) return ans;  // prevent stack overflows, infinite recursion\n    size_t frame_data_sz; void *frame_data;\n    ImageAndFrame key = {.image_id = img->internal_id, .frame_id = f->id};\n    if (!read_from_cache(self, key, &frame_data, &frame_data_sz)) return ans;\n    if (!f->base_frame_id) return get_coalesced_frame_data_standalone(img, f, frame_data);\n    Frame *base = frame_for_id(img, f->base_frame_id);\n    if (!base) { free(frame_data); return ans; }\n    CoalescedFrameData base_data = get_coalesced_frame_data_impl(self, img, base, count + 1);\n    if (!base_data.buf) { free(frame_data); return ans; }\n    ComposeData d = {\n        .over_px_sz = f->is_opaque ? 3 : 4,\n        .under_px_sz = base_data.is_opaque ? 3 : 4,\n        .over_width = f->width, .over_height = f->height, .over_offset_x = f->x, .over_offset_y = f->y,\n        .under_width = img->width, .under_height = img->height,\n        .needs_blending = f->alpha_blend && !f->is_opaque\n    };\n    compose(d, base_data.buf, frame_data);\n    free(frame_data);\n    return base_data;\n}\n\nstatic CoalescedFrameData\nget_coalesced_frame_data(GraphicsManager *self, Image *img, const Frame *f) {\n    return get_coalesced_frame_data_impl(self, img, f, 0);\n}\n\nstatic void\nupdate_current_frame(GraphicsManager *self, Image *img, const CoalescedFrameData *data) {\n    bool needs_load = data == NULL;\n    CoalescedFrameData cfd;\n    if (needs_load) {\n        Frame *f = current_frame(img);\n        if (f == NULL) return;\n        cfd = get_coalesced_frame_data(self, img, f);\n        if (!cfd.buf) {\n            if (PyErr_Occurred()) PyErr_Print();\n            return;\n        }\n        data = &cfd;\n    }\n    upload_to_gpu(self, img, data->is_opaque, data->is_4byte_aligned, data->buf);\n    if (needs_load) free(data->buf);\n    img->current_frame_shown_at = monotonic();\n}\n\nstatic bool\nreference_chain_too_large(Image *img, const Frame *frame) {\n    uint32_t limit = img->width * img->height * 2;\n    uint32_t drawn_area = frame->width * frame->height;\n    unsigned num = 1;\n    while (drawn_area < limit && num < 5) {\n        if (!frame->base_frame_id || !(frame = frame_for_id(img, frame->base_frame_id))) break;\n        drawn_area += frame->width * frame->height;\n        num++;\n    }\n    return num >= 5 || drawn_area >= limit;\n}\n\nstatic Image*\nhandle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, Image *img, const uint8_t *payload, bool *is_dirty) {\n    uint32_t frame_number = g->frame_number, fmt = g->format ? g->format : RGBA;\n    if (!frame_number || frame_number > img->extra_framecnt + 2) frame_number = img->extra_framecnt + 2;\n    bool is_new_frame = frame_number == img->extra_framecnt + 2;\n    g->frame_number = frame_number;\n    unsigned char tt = g->transmission_type ? g->transmission_type : 'd';\n    if (tt == 'd' && self->currently_loading.loading_for.image_id == img->internal_id) {\n        INIT_CHUNKED_LOAD;\n    } else {\n        self->currently_loading.loading_for = (const ImageAndFrame){0};\n        if (g->data_width > MAX_IMAGE_DIMENSION || g->data_height > MAX_IMAGE_DIMENSION) ABRT(\"EINVAL\", \"Image too large, width or height greater than %u\", MAX_IMAGE_DIMENSION);\n        if (!initialize_load_data(self, g, img, tt, fmt, frame_number - 1)) return NULL;\n    }\n    LoadData *load_data = &self->currently_loading;\n    img = load_image_data(self, img, g, tt, fmt, payload);\n    if (!img || !load_data->loading_completed_successfully) return NULL;\n    self->currently_loading.loading_for = (const ImageAndFrame){0};\n    img = process_image_data(self, img, g, tt, fmt);\n    if (!img || !load_data->loading_completed_successfully) return img;\n\n    const unsigned long bytes_per_pixel = load_data->is_opaque ? 3 : 4;\n    if (load_data->data_sz < bytes_per_pixel * load_data->width * load_data->height)\n        ABRT(\"ENODATA\", \"Insufficient image data %zu < %zu\", load_data->data_sz, bytes_per_pixel * g->data_width * g->data_height);\n    if (load_data->width > img->width)\n        ABRT(\"EINVAL\", \"Frame width %u larger than image width: %u\", load_data->width, img->width);\n    if (load_data->height > img->height)\n        ABRT(\"EINVAL\", \"Frame height %u larger than image height: %u\", load_data->height, img->height);\n    if (is_new_frame && cache_size(self) + load_data->data_sz > self->storage_limit * 5) {\n        remove_images(self, trim_predicate, img->internal_id);\n        if (cache_size(self) + load_data->data_sz > self->storage_limit * 5)\n            ABRT(\"ENOSPC\", \"Cache size exceeded cannot add new frames\");\n    }\n\n    Frame transmitted_frame = {\n        .width = load_data->width, .height = load_data->height,\n        .x = g->x_offset, .y = g->y_offset,\n        .is_4byte_aligned = load_data->is_4byte_aligned,\n        .is_opaque = load_data->is_opaque,\n        .alpha_blend = g->compose_mode != 1 && !load_data->is_opaque,\n        .gap = g->gap > 0 ? g->gap : (g->gap < 0) ? 0 : DEFAULT_GAP,\n        .bgcolor = g->bgcolor,\n    };\n    Frame *frame;\n    if (is_new_frame) {\n        transmitted_frame.id = ++img->frame_id_counter;\n        Frame *frames = realloc(img->extra_frames, sizeof(img->extra_frames[0]) * (img->extra_framecnt + 1));\n        if (!frames) ABRT(\"ENOMEM\", \"Out of memory\");\n        img->extra_frames = frames;\n        img->extra_framecnt++;\n        frame = img->extra_frames + frame_number - 2;\n        const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = transmitted_frame.id };\n        if (g->other_frame_number) {\n            Frame *other_frame = frame_for_number(img, g->other_frame_number);\n            if (!other_frame) {\n                img->extra_framecnt--;\n                ABRT(\"EINVAL\", \"No frame with number: %u found\", g->other_frame_number);\n            }\n            if (other_frame->base_frame_id && reference_chain_too_large(img, other_frame)) {\n                // since there is a long reference chain to render this frame, make\n                // it a fully coalesced key frame, for performance\n                CoalescedFrameData cfd = get_coalesced_frame_data(self, img, other_frame);\n                if (!cfd.buf) ABRT(\"EINVAL\", \"Failed to get data from frame referenced by frame: %u\", frame_number);\n                ComposeData d = {\n                    .over_px_sz = transmitted_frame.is_opaque ? 3 : 4, .under_px_sz = cfd.is_opaque ? 3: 4,\n                    .over_width = transmitted_frame.width, .over_height = transmitted_frame.height,\n                    .over_offset_x = transmitted_frame.x, .over_offset_y = transmitted_frame.y,\n                    .under_width = img->width, .under_height = img->height,\n                    .needs_blending = transmitted_frame.alpha_blend && !transmitted_frame.is_opaque\n                };\n                compose(d, cfd.buf, load_data->data);\n                free_load_data(load_data);\n                load_data->data = cfd.buf; load_data->data_sz = (size_t)img->width * img->height * d.under_px_sz;\n                transmitted_frame.width = img->width; transmitted_frame.height = img->height;\n                transmitted_frame.x = 0; transmitted_frame.y = 0;\n                transmitted_frame.is_4byte_aligned = cfd.is_4byte_aligned;\n                transmitted_frame.is_opaque = cfd.is_opaque;\n            } else {\n                transmitted_frame.base_frame_id = other_frame->id;\n            }\n        }\n        *frame = transmitted_frame;\n        if (!add_to_cache(self, key, load_data->data, load_data->data_sz)) {\n            img->extra_framecnt--;\n            if (PyErr_Occurred()) PyErr_Print();\n            ABRT(\"ENOSPC\", \"Failed to cache data for image frame\");\n        }\n        img->animation_duration += frame->gap;\n        if (img->animation_state == ANIMATION_LOADING) {\n            self->has_images_needing_animation = true;\n            global_state.check_for_active_animated_images = true;\n        }\n    } else {\n        frame = frame_for_number(img, frame_number);\n        if (!frame) ABRT(\"EINVAL\", \"No frame with number: %u found\", frame_number);\n        if (g->gap != 0) change_gap(img, frame, transmitted_frame.gap);\n        CoalescedFrameData cfd = get_coalesced_frame_data(self, img, frame);\n        if (!cfd.buf) ABRT(\"EINVAL\", \"No data associated with frame number: %u\", frame_number);\n        frame->alpha_blend = false; frame->base_frame_id = 0; frame->bgcolor = 0;\n        frame->is_opaque = cfd.is_opaque; frame->is_4byte_aligned = cfd.is_4byte_aligned;\n        frame->x = 0; frame->y = 0; frame->width = img->width; frame->height = img->height;\n        const unsigned bytes_per_pixel = frame->is_opaque ? 3: 4;\n        ComposeData d = {\n            .over_px_sz = transmitted_frame.is_opaque ? 3 : 4, .under_px_sz = bytes_per_pixel,\n            .over_width = transmitted_frame.width, .over_height = transmitted_frame.height,\n            .over_offset_x = transmitted_frame.x, .over_offset_y = transmitted_frame.y,\n            .under_width = frame->width, .under_height = frame->height,\n            .needs_blending = transmitted_frame.alpha_blend && !transmitted_frame.is_opaque\n        };\n        compose(d, cfd.buf, load_data->data);\n        const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = frame->id };\n        bool added = add_to_cache(self, key, cfd.buf, (size_t)bytes_per_pixel * frame->width * frame->height);\n        if (added && frame == current_frame(img)) {\n            update_current_frame(self, img, &cfd);\n            *is_dirty = true;\n        }\n        free(cfd.buf);\n        if (!added) {\n            if (PyErr_Occurred()) PyErr_Print();\n            ABRT(\"ENOSPC\", \"Failed to cache data for image frame\");\n        }\n    }\n    return img;\n}\n\n#undef ABRT\n\nstatic Image*\nhandle_delete_frame_command(GraphicsManager *self, const GraphicsCommand *g, bool *is_dirty) {\n    if (!g->id && !g->image_number) {\n        REPORT_ERROR(\"Delete frame data command without image id or number\");\n        return NULL;\n    }\n    Image *img = g->id ? img_by_client_id(self, g->id) : img_by_client_number(self, g->image_number);\n    if (!img) {\n        REPORT_ERROR(\"Animation command refers to non-existent image with id: %u and number: %u\", g->id, g->image_number);\n        return NULL;\n    }\n    uint32_t frame_number = MIN(img->extra_framecnt + 1, g->frame_number);\n    if (!frame_number) frame_number = 1;\n    if (!img->extra_framecnt) return g->delete_action == 'F' ? img : NULL;\n    *is_dirty = true;\n    ImageAndFrame key = {.image_id=img->internal_id};\n    bool remove_root = frame_number == 1;\n    uint32_t removed_gap = 0;\n    if (remove_root) {\n        key.frame_id = img->root_frame.id;\n        remove_from_cache(self, key);\n        if (PyErr_Occurred()) PyErr_Print();\n        removed_gap = img->root_frame.gap;\n        img->root_frame = img->extra_frames[0];\n    }\n    unsigned removed_idx = remove_root ? 0 : frame_number - 2;\n    if (!remove_root) {\n        key.frame_id = img->extra_frames[removed_idx].id;\n        removed_gap = img->extra_frames[removed_idx].gap;\n        remove_from_cache(self, key);\n    }\n    img->animation_duration = removed_gap < img->animation_duration ? img->animation_duration - removed_gap : 0;\n    if (PyErr_Occurred()) PyErr_Print();\n    if (removed_idx < img->extra_framecnt - 1) memmove(img->extra_frames + removed_idx, img->extra_frames + removed_idx + 1, sizeof(img->extra_frames[0]) * (img->extra_framecnt - 1 - removed_idx));\n    img->extra_framecnt--;\n    if (img->current_frame_index > img->extra_framecnt) {\n        img->current_frame_index = img->extra_framecnt;\n        update_current_frame(self, img, NULL);\n        return NULL;\n    }\n    if (removed_idx == img->current_frame_index) update_current_frame(self, img, NULL);\n    else if (removed_idx < img->current_frame_index) img->current_frame_index--;\n    return NULL;\n}\n\nstatic void\nhandle_animation_control_command(GraphicsManager *self, bool *is_dirty, const GraphicsCommand *g, Image *img) {\n    if (g->frame_number) {\n        uint32_t frame_idx = g->frame_number - 1;\n        if (frame_idx <= img->extra_framecnt) {\n            Frame *f = frame_idx ? img->extra_frames + frame_idx - 1 : &img->root_frame;\n            if (g->gap) change_gap(img, f, g->gap);\n        }\n    }\n    if (g->other_frame_number) {\n        uint32_t frame_idx = g->other_frame_number - 1;\n        if (frame_idx != img->current_frame_index && frame_idx <= img->extra_framecnt) {\n            img->current_frame_index = frame_idx;\n            *is_dirty = true;\n            update_current_frame(self, img, NULL);\n        }\n    }\n    if (g->animation_state) {\n        AnimationState old_state = img->animation_state;\n        switch(g->animation_state) {\n            case 1:\n                img->animation_state = ANIMATION_STOPPED; break;\n            case 2:\n                img->animation_state = ANIMATION_LOADING; break;\n            case 3:\n                img->animation_state = ANIMATION_RUNNING; break;\n            default:\n                break;\n        }\n        if (img->animation_state == ANIMATION_STOPPED) {\n            img->current_loop = 0;\n        } else {\n            if (old_state == ANIMATION_STOPPED) { img->current_frame_shown_at = monotonic(); img->is_drawn = true; }\n            self->has_images_needing_animation = true;\n            global_state.check_for_active_animated_images = true;\n        }\n        img->current_loop = 0;\n    }\n    if (g->loop_count) {\n        img->max_loops = g->loop_count - 1;\n        global_state.check_for_active_animated_images = true;\n    }\n}\n\nstatic bool\nimage_is_animatable(const Image *img) {\n    return img->animation_state != ANIMATION_STOPPED && img->extra_framecnt && img->is_drawn && img->animation_duration && (\n            !img->max_loops || img->current_loop < img->max_loops);\n}\n\nbool\nscan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t *minimum_gap, bool os_window_context_set) {\n    bool dirtied = false;\n    *minimum_gap = MONOTONIC_T_MAX;\n    if (!self->has_images_needing_animation) return dirtied;\n    self->has_images_needing_animation = false;\n    self->context_made_current_for_this_command = os_window_context_set;\n    iter_images(self) { Image *img = i.data->val;\n        if (image_is_animatable(img)) {\n            Frame *f = current_frame(img);\n            if (f) {\n                self->has_images_needing_animation = true;\n                monotonic_t next_frame_at = img->current_frame_shown_at + ms_to_monotonic_t(f->gap);\n                if (now >= next_frame_at) {\n                    do {\n                        uint32_t next = (img->current_frame_index + 1) % (img->extra_framecnt + 1);\n                        if (!next) {\n                            if (img->animation_state == ANIMATION_LOADING) goto skip_image;\n                            if (++img->current_loop >= img->max_loops && img->max_loops) goto skip_image;\n                        }\n                        img->current_frame_index = next;\n                    } while (!current_frame(img)->gap);\n                    dirtied = true;\n                    update_current_frame(self, img, NULL);\n                    f = current_frame(img);\n                    next_frame_at = img->current_frame_shown_at + ms_to_monotonic_t(f->gap);\n                }\n                if (next_frame_at > now && next_frame_at - now < *minimum_gap) *minimum_gap = next_frame_at - now;\n            }\n        }\n        skip_image:;\n    }\n    return dirtied;\n}\n// }}}\n\n// {{{ composition a=c\nstatic void\ncfd_free(CoalescedFrameData *p) { free((p)->buf); p->buf = NULL; }\n\nstatic void\nhandle_compose_command(GraphicsManager *self, bool *is_dirty, const GraphicsCommand *g, Image *img) {\n    Frame *src_frame = frame_for_number(img, g->frame_number);\n    if (!src_frame) {\n        set_command_failed_response(\"ENOENT\", \"No source frame number %u exists in image id: %u\\n\", g->frame_number, img->client_id);\n        return;\n    }\n    Frame *dest_frame = frame_for_number(img, g->other_frame_number);\n    if (!dest_frame) {\n        set_command_failed_response(\"ENOENT\", \"No destination frame number %u exists in image id: %u\\n\", g->other_frame_number, img->client_id);\n        return;\n    }\n    const unsigned int width = g->width ? g->width : img->width;\n    const unsigned int height = g->height ? g->height : img->height;\n    const unsigned int dest_x = g->x_offset, dest_y = g->y_offset, src_x = g->cell_x_offset, src_y = g->cell_y_offset;\n    if (dest_x + width > img->width || dest_y + height > img->height) {\n        set_command_failed_response(\"EINVAL\", \"The destination rectangle is out of bounds\");\n        return;\n    }\n    if (src_x + width > img->width || src_y + height > img->height) {\n        set_command_failed_response(\"EINVAL\", \"The source rectangle is out of bounds\");\n        return;\n    }\n    if (src_frame == dest_frame) {\n        bool x_overlaps = MAX(src_x, dest_x) < (MIN(src_x, dest_x) + width);\n        bool y_overlaps = MAX(src_y, dest_y) < (MIN(src_y, dest_y) + height);\n        if (x_overlaps && y_overlaps) {\n            set_command_failed_response(\"EINVAL\", \"The source and destination rectangles overlap and the src and destination frames are the same\");\n            return;\n        }\n    }\n\n    RAII_CoalescedFrameData(src_data, get_coalesced_frame_data(self, img, src_frame));\n    if (!src_data.buf) {\n        set_command_failed_response(\"EINVAL\", \"Failed to get data for src frame: %u\", g->frame_number - 1);\n        return;\n    }\n    RAII_CoalescedFrameData(dest_data, get_coalesced_frame_data(self, img, dest_frame));\n    if (!dest_data.buf) {\n        set_command_failed_response(\"EINVAL\", \"Failed to get data for destination frame: %u\", g->other_frame_number - 1);\n        return;\n    }\n    ComposeData d = {\n        .over_px_sz = src_data.is_opaque ? 3 : 4, .under_px_sz = dest_data.is_opaque ? 3: 4,\n        .needs_blending = !g->compose_mode && !src_data.is_opaque,\n        .over_offset_x = src_x, .over_offset_y = src_y,\n        .under_offset_x = dest_x, .under_offset_y = dest_y,\n        .over_width = width, .over_height = height, .under_width = width, .under_height = height,\n        .stride = img->width\n    };\n    compose_rectangles(d, dest_data.buf, src_data.buf);\n    const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = dest_frame->id };\n    if (!add_to_cache(self, key, dest_data.buf, ((size_t)(dest_data.is_opaque ? 3 : 4)) * img->width * img->height)) {\n        if (PyErr_Occurred()) PyErr_Print();\n        set_command_failed_response(\"ENOSPC\", \"Failed to store image data in disk cache\");\n    }\n    // frame is now a fully coalesced frame\n    dest_frame->x = 0; dest_frame->y = 0; dest_frame->width = img->width; dest_frame->height = img->height;\n    dest_frame->base_frame_id = 0; dest_frame->bgcolor = 0;\n    *is_dirty = (g->other_frame_number - 1) == img->current_frame_index;\n    if (*is_dirty) update_current_frame(self, img, &dest_data);\n}\n// }}}\n\n// Image lifetime/scrolling {{{\n\nstatic ref_map_itr\nremove_ref_itr(Image *img, ref_map_itr x) {\n    free(x.data->val);\n    return vt_erase_itr(&img->refs_by_internal_id, x);\n}\n\n\nstatic void\nremove_ref(Image *img, ImageRef *ref) {\n    ref_map_itr i = vt_get(&img->refs_by_internal_id, ref->internal_id);\n    if (vt_is_end(i)) return;\n    remove_ref_itr(img, i);\n}\n\nstatic void\nfilter_refs(GraphicsManager *self, const void* data, bool free_images, bool (*filter_func)(const ImageRef*, Image*, const void*, CellPixelSize), CellPixelSize cell, bool only_first_image, bool free_only_matched) {\n    for (image_map_itr ii = vt_first(&self->images_by_internal_id); !vt_is_end(ii); ) { Image *img = ii.data->val;\n        bool matched = false;\n        for (ref_map_itr ri = vt_first(&img->refs_by_internal_id); !vt_is_end(ri); ) { ImageRef *ref = ri.data->val;\n            if (filter_func(ref, img, data, cell)) {\n                ri = remove_ref_itr(img, ri);\n                set_layers_dirty(self);\n                matched = true;\n            } else ri = vt_next(ri);\n        }\n        if ((!free_only_matched || matched) && !vt_size(&img->refs_by_internal_id) && (free_images || img->client_id == 0)) ii = remove_image_itr(self, ii);\n        else ii = vt_next(ii);\n        if (only_first_image && matched) break;\n    }\n}\n\n\nstatic void\nmodify_refs(GraphicsManager *self, const void* data, bool (*filter_func)(ImageRef*, Image*, const void*, CellPixelSize), CellPixelSize cell) {\n    for (image_map_itr ii = vt_first(&self->images_by_internal_id); !vt_is_end(ii); ) { Image *img = ii.data->val;\n        for (ref_map_itr ri = vt_first(&img->refs_by_internal_id); !vt_is_end(ri); ) { ImageRef *ref = ri.data->val;\n            if (filter_func(ref, img, data, cell)) ri = remove_ref_itr(img, ri);\n            else ri = vt_next(ri);\n        }\n        if (!vt_size(&img->refs_by_internal_id) && img->client_id == 0 && img->client_number == 0) {\n            // references have all scrolled off the history buffer and the image has no way to reference it\n            // to create new references so remove it.\n            ii = remove_image_itr(self, ii);\n        } else ii = vt_next(ii);\n    }\n}\n\n\nstatic bool\nscroll_filter_func(ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {\n    if (ref->is_virtual_ref) return false;\n    ScrollData *d = (ScrollData*)data;\n    ref->start_row += d->amt;\n    return ref->start_row + (int32_t)ref->effective_num_rows <= d->limit;\n}\n\nstatic bool\nref_within_region(const ImageRef *ref, index_type margin_top, index_type margin_bottom) {\n    return ref->start_row >= (int32_t)margin_top && ref->start_row + (int32_t)ref->effective_num_rows - 1 <= (int32_t)margin_bottom;\n}\n\nstatic bool\nref_outside_region(const ImageRef *ref, index_type margin_top, index_type margin_bottom) {\n    return ref->start_row + (int32_t)ref->effective_num_rows <= (int32_t)margin_top || ref->start_row > (int32_t)margin_bottom;\n}\n\nstatic bool\nscroll_filter_margins_func(ImageRef* ref, Image* img, const void* data, CellPixelSize cell) {\n    if (ref->is_virtual_ref) return false;\n    ScrollData *d = (ScrollData*)data;\n    if (ref_within_region(ref, d->margin_top, d->margin_bottom)) {\n        ref->start_row += d->amt;\n        if (ref_outside_region(ref, d->margin_top, d->margin_bottom)) return true;\n        // Clip the image if scrolling has resulted in part of it being outside the page area\n        uint32_t clip_amt, clipped_rows;\n        if (ref->start_row < (int32_t)d->margin_top) {\n            // image moved up\n            clipped_rows = d->margin_top - ref->start_row;\n            clip_amt = cell.height * clipped_rows;\n            if (ref->src_height <= clip_amt) return true;\n            ref->src_y += clip_amt; ref->src_height -= clip_amt;\n            ref->effective_num_rows -= clipped_rows;\n            update_src_rect(ref, img);\n            ref->start_row += clipped_rows;\n        } else if (ref->start_row + (int32_t)ref->effective_num_rows - 1 > (int32_t)d->margin_bottom) {\n            // image moved down\n            clipped_rows = ref->start_row + ref->effective_num_rows - 1 - d->margin_bottom;\n            clip_amt = cell.height * clipped_rows;\n            if (ref->src_height <= clip_amt) return true;\n            ref->src_height -= clip_amt;\n            ref->effective_num_rows -= clipped_rows;\n            update_src_rect(ref, img);\n        }\n        return ref_outside_region(ref, d->margin_top, d->margin_bottom);\n    }\n    return false;\n}\n\nvoid\ngrman_scroll_images(GraphicsManager *self, const ScrollData *data, CellPixelSize cell) {\n    if (vt_size(&self->images_by_internal_id)) {\n        set_layers_dirty(self);\n        modify_refs(self, data, data->has_margins ? scroll_filter_margins_func : scroll_filter_func, cell);\n    }\n}\n\nstatic bool\ncell_image_row_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {\n    if (ref->is_virtual_ref || !is_cell_image(ref))\n        return false;\n    int32_t top = *(int32_t *)data;\n    int32_t bottom = *((int32_t *)data + 1);\n    return ref_within_region(ref, top, bottom);\n}\n\nstatic bool\ncell_image_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data UNUSED, CellPixelSize cell UNUSED) {\n    return !ref->is_virtual_ref && is_cell_image(ref);\n}\n\n// Remove cell images within the given region.\nvoid\ngrman_remove_cell_images(GraphicsManager *self, int32_t top, int32_t bottom) {\n    CellPixelSize dummy = {0};\n    int32_t data[] = {top, bottom};\n    filter_refs(self, data, false, cell_image_row_filter_func, dummy, false, true);\n}\n\nvoid\ngrman_remove_all_cell_images(GraphicsManager *self) {\n    CellPixelSize dummy = {0};\n    filter_refs(self, NULL, false, cell_image_filter_func, dummy, false, true);\n}\n\n\nstatic bool\nclear_filter_func(const ImageRef *ref, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {\n    if (ref->is_virtual_ref) return false;\n    return ref->start_row + (int32_t)ref->effective_num_rows > 0;\n}\n\nstatic bool\nclear_filter_func_noncell(const ImageRef *ref, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {\n    if (ref->is_virtual_ref || is_cell_image(ref)) return false;\n    return ref->start_row + (int32_t)ref->effective_num_rows > 0;\n}\n\nstatic bool\nclear_all_filter_func(const ImageRef *ref UNUSED, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {\n    if (ref->is_virtual_ref) return false;\n    return true;\n}\n\nvoid\ngrman_clear(GraphicsManager *self, bool all, CellPixelSize cell) {\n    filter_refs(self, NULL, true, all ? clear_all_filter_func : clear_filter_func, cell, false, false);\n}\n\nstatic bool\nid_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelSize cell UNUSED) {\n    const GraphicsCommand *g = data;\n    if (g->id && img->client_id == g->id) return !g->placement_id || ref->client_id == g->placement_id;\n    return false;\n}\n\nstatic bool\nid_range_filter_func(const ImageRef *ref UNUSED, Image *img, const void *data, CellPixelSize cell UNUSED) {\n    const GraphicsCommand *g = data;\n    return img->client_id && g->x_offset <= img->client_id && img->client_id <= g->y_offset;\n}\n\n\nstatic bool\nx_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {\n    if (ref->is_virtual_ref || is_cell_image(ref)) return false;\n    const GraphicsCommand *g = data;\n    return ref->start_column <= (int32_t)g->x_offset - 1 && ((int32_t)g->x_offset - 1) < ((int32_t)(ref->start_column + ref->effective_num_cols));\n}\n\nstatic bool\ny_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {\n    if (ref->is_virtual_ref || is_cell_image(ref)) return false;\n    const GraphicsCommand *g = data;\n    return ref->start_row <= (int32_t)g->y_offset - 1 && ((int32_t)g->y_offset - 1) < ((int32_t)(ref->start_row + ref->effective_num_rows));\n}\n\nstatic bool\nz_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {\n    if (ref->is_virtual_ref || is_cell_image(ref)) return false;\n    const GraphicsCommand *g = data;\n    return ref->z_index == g->z_index;\n}\n\n\nstatic bool\npoint_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelSize cell) {\n    if (ref->is_virtual_ref || is_cell_image(ref)) return false;\n    return x_filter_func(ref, img, data, cell) && y_filter_func(ref, img, data, cell);\n}\n\nstatic bool\npoint3d_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelSize cell) {\n    if (ref->is_virtual_ref || is_cell_image(ref)) return false;\n    return z_filter_func(ref, img, data, cell) && point_filter_func(ref, img, data, cell);\n}\n\n\nstatic void\nhandle_delete_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, bool *is_dirty, CellPixelSize cell) {\n    if (self->currently_loading.loading_for.image_id) free_load_data(&self->currently_loading);\n    GraphicsCommand d;\n    if (!g->placement_id) {\n        // special case freeing of images with no refs by id or number as\n        // filter_refs doesnt handle this\n        Image *img = NULL;\n        switch(g->delete_action) {\n            case 'I': img = img_by_client_id(self, g->id); break;\n            case 'N': img = img_by_client_number(self, g->image_number); break;\n            case 'R': {\n                for (image_map_itr ii = vt_first(&self->images_by_internal_id); !vt_is_end(ii); ) {\n                    img = ii.data->val;\n                    if (id_range_filter_func(NULL, img, g, cell) && !vt_size(&img->refs_by_internal_id)) ii = remove_image_itr(self, ii);\n                    else ii = vt_next(ii);\n                }\n            } img = NULL; break;\n        }\n        if (img && !vt_size(&img->refs_by_internal_id)) { remove_image(self, img); goto end; }\n    }\n    switch (g->delete_action) {\n#define I(u, data, func) filter_refs(self, data, g->delete_action == u, func, cell, false, true); *is_dirty = true; break\n#define D(l, u, data, func) case l: case u: I(u, data, func)\n#define G(l, u, func) D(l, u, g, func)\n        case 0:\n        D('a', 'A', NULL, clear_filter_func_noncell);\n        G('i', 'I', id_filter_func);\n        G('r', 'R', id_range_filter_func);\n        G('p', 'P', point_filter_func);\n        G('q', 'Q', point3d_filter_func);\n        G('x', 'X', x_filter_func);\n        G('y', 'Y', y_filter_func);\n        G('z', 'Z', z_filter_func);\n        case 'c':\n        case 'C':\n            d.x_offset = c->x + 1; d.y_offset = c->y + 1;\n            I('C', &d, point_filter_func);\n        case 'n':\n        case 'N': {\n            Image *img = img_by_client_number(self, g->image_number);\n            if (img) {\n                for (ref_map_itr ri = vt_first(&img->refs_by_internal_id); !vt_is_end(ri); ) { ImageRef *ref = ri.data->val;\n                    if (!g->placement_id || g->placement_id == ref->client_id) {\n                        ri = remove_ref_itr(img, ri);\n                        set_layers_dirty(self);\n                    } else ri = vt_next(ri);\n                }\n                if (!vt_size(&img->refs_by_internal_id) && (g->delete_action == 'N' || img->client_id == 0)) remove_image(self, img);\n            }\n        } break;\n        case 'f':\n        case 'F': {\n            Image *img = handle_delete_frame_command(self, g, is_dirty);\n            if (img != NULL) {\n                remove_image(self, img);\n                *is_dirty = true;\n            }\n            break;\n        }\n        default:\n            REPORT_ERROR(\"Unknown graphics command delete action: %c\", g->delete_action);\n            break;\n#undef G\n#undef D\n#undef I\n    }\nend:\n    if (!vt_size(&self->images_by_internal_id) && self->render_data.count) self->render_data.count = 0;\n}\n\n// }}}\n\nvoid\ngrman_resize(GraphicsManager *self, index_type old_lines UNUSED, index_type lines UNUSED, index_type old_columns, index_type columns, index_type num_content_lines_before, index_type num_content_lines_after) {\n    ImageRef *ref; Image *img;\n    set_layers_dirty(self);\n    if (columns == old_columns && num_content_lines_before > num_content_lines_after) {\n        const unsigned int vertical_shrink_size = num_content_lines_before - num_content_lines_after;\n        iter_images(self) { img = i.data->val;\n            iter_refs(img) { ref = i.data->val;\n                if (ref->is_virtual_ref || is_cell_image(ref)) continue;\n                ref->start_row -= vertical_shrink_size;\n            }\n        }\n    }\n}\n\nvoid\ngrman_rescale(GraphicsManager *self, CellPixelSize cell) {\n    ImageRef *ref; Image *img;\n    set_layers_dirty(self);\n    iter_images(self) { img = i.data->val;\n        iter_refs(img) { ref = i.data->val;\n            if (ref->is_virtual_ref || is_cell_image(ref)) continue;\n            ref->cell_x_offset = MIN(ref->cell_x_offset, cell.width - 1);\n            ref->cell_y_offset = MIN(ref->cell_y_offset, cell.height - 1);\n            update_dest_rect(ref, ref->num_cols, ref->num_rows, cell);\n        }\n    }\n}\n\nconst char*\ngrman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty, CellPixelSize cell) {\n    const char *ret = NULL;\n    command_response[0] = 0;\n    self->context_made_current_for_this_command = false;\n\n    if (g->id && g->image_number) {\n        set_command_failed_response(\"EINVAL\", \"Must not specify both image id and image number\");\n        return finish_command_response(g, false);\n    }\n\n    switch(g->action) {\n        case 0:\n        case 't':\n        case 'T':\n        case 'q': {\n            uint32_t iid = g->id, q_iid = iid;\n            bool is_query = g->action == 'q';\n            if (is_query) { iid = 0; if (!q_iid) { REPORT_ERROR(\"Query graphics command without image id\"); break; } }\n            Image *image = handle_add_command(self, g, payload, is_dirty, iid, is_query);\n            if (!self->currently_loading.loading_for.image_id) free_load_data(&self->currently_loading);\n            GraphicsCommand *lg = &self->currently_loading.start_command;\n            if (g->quiet) lg->quiet = g->quiet;\n            if (is_query) ret = finish_command_response(&(const GraphicsCommand){.id=q_iid, .quiet=g->quiet}, image != NULL);\n            else ret = finish_command_response(lg, image != NULL);\n            if (lg->action == 'T' && image && image->root_frame_data_loaded) handle_put_command(self, lg, c, is_dirty, image, cell);\n            id_type added_image_id = image ? image->internal_id : 0;\n            if (g->action == 'q') remove_images(self, add_trim_predicate, 0);\n            if (self->used_storage > self->storage_limit) apply_storage_quota(self, self->storage_limit, added_image_id);\n            break;\n        }\n        case 'a':\n        case 'f': {\n            if (!g->id && !g->image_number && !self->currently_loading.loading_for.image_id) {\n                REPORT_ERROR(\"Add frame data command without image id or number\");\n                break;\n            }\n            Image *img;\n            if (self->currently_loading.loading_for.image_id) img = img_by_internal_id(self, self->currently_loading.loading_for.image_id);\n            else img = g->id ? img_by_client_id(self, g->id) : img_by_client_number(self, g->image_number);\n            if (!img) {\n                set_command_failed_response(\"ENOENT\", \"Animation command refers to non-existent image with id: %u and number: %u\", g->id, g->image_number);\n                ret = finish_command_response(g, false);\n            } else {\n                GraphicsCommand ag = *g;\n                if (ag.action == 'f') {\n                    img = handle_animation_frame_load_command(self, &ag, img, payload, is_dirty);\n                    if (!self->currently_loading.loading_for.image_id) free_load_data(&self->currently_loading);\n                    if (g->quiet) ag.quiet = g->quiet;\n                    else ag.quiet = self->currently_loading.start_command.quiet;\n                    ret = finish_command_response(&ag, img != NULL);\n                } else if (ag.action == 'a') {\n                    handle_animation_control_command(self, is_dirty, &ag, img);\n                }\n            }\n            break;\n        }\n        case 'p': {\n            if (!g->id && !g->image_number) {\n                REPORT_ERROR(\"Put graphics command without image id or number\");\n                break;\n            }\n            uint32_t image_id = handle_put_command(self, g, c, is_dirty, NULL, cell);\n            GraphicsCommand rg = *g; rg.id = image_id;\n            ret = finish_command_response(&rg, true);\n            break;\n        }\n        case 'd':\n            handle_delete_command(self, g, c, is_dirty, cell);\n            break;\n        case 'c':\n            if (!g->id && !g->image_number) {\n                REPORT_ERROR(\"Compose frame data command without image id or number\");\n                break;\n            }\n            Image *img = g->id ? img_by_client_id(self, g->id) : img_by_client_number(self, g->image_number);\n            if (!img) {\n                set_command_failed_response(\"ENOENT\", \"Animation command refers to non-existent image with id: %u and number: %u\", g->id, g->image_number);\n                ret = finish_command_response(g, false);\n            } else {\n                handle_compose_command(self, is_dirty, g, img);\n                ret = finish_command_response(g, true);\n            }\n            break;\n        default:\n            REPORT_ERROR(\"Unknown graphics command action: %c\", g->action);\n            break;\n    }\n    return ret;\n}\n\n\n// Boilerplate {{{\nstatic PyObject *\nnew_graphicsmanager_object(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {\n    PyObject *ans = (PyObject*)grman_alloc(false);\n    if (ans == NULL) PyErr_NoMemory();\n    return ans;\n}\n\nstatic PyObject*\nimage_as_dict(GraphicsManager *self, Image *img) {\n#define U(x) #x, (unsigned int)(img->x)\n#define B(x) #x, img->x ? Py_True : Py_False\n    PyObject *frames = PyTuple_New(img->extra_framecnt);\n    for (unsigned i = 0; i < img->extra_framecnt; i++) {\n        Frame *f = img->extra_frames + i;\n        CoalescedFrameData cfd = get_coalesced_frame_data(self, img, f);\n        if (!cfd.buf) { PyErr_SetString(PyExc_RuntimeError, \"Failed to get data for frame\"); return NULL; }\n        PyTuple_SET_ITEM(frames, i, Py_BuildValue(\n            \"{sI sI sy#}\",\n            \"gap\", f->gap,\n            \"id\", f->id,\n            \"data\", cfd.buf, (Py_ssize_t)((cfd.is_opaque ? 3 : 4) * img->width * img->height)\n        ));\n        free(cfd.buf);\n        if (PyErr_Occurred()) { Py_CLEAR(frames); return NULL; }\n    }\n    CoalescedFrameData cfd = get_coalesced_frame_data(self, img, &img->root_frame);\n    if (!cfd.buf) { PyErr_SetString(PyExc_RuntimeError, \"Failed to get data for root frame\"); return NULL; }\n    PyObject *ans = Py_BuildValue(\"{sI sI sI sI sI sI sI \" \"sO sI sO \" \"sI sI sI \" \"sI sy# sN}\",\n        \"texture_id\", texture_id_for_img(img), U(client_id), U(width), U(height), U(internal_id),\n        \"refs.count\", (unsigned int)vt_size(&img->refs_by_internal_id), U(client_number),\n\n        B(root_frame_data_loaded), U(animation_state), \"is_4byte_aligned\", img->root_frame.is_4byte_aligned ? Py_True : Py_False,\n\n        U(current_frame_index), \"root_frame_gap\", img->root_frame.gap, U(current_frame_index),\n\n        U(animation_duration), \"data\", cfd.buf, (Py_ssize_t)((cfd.is_opaque ? 3 : 4) * img->width * img->height), \"extra_frames\", frames\n    );\n    free(cfd.buf);\n    return ans;\n#undef B\n#undef U\n}\n\n#define W(x) static PyObject* py##x(GraphicsManager UNUSED *self, PyObject *args)\n#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;\n\nW(image_for_client_id) {\n    unsigned long id = PyLong_AsUnsignedLong(args);\n    bool existing = false;\n    Image *img = find_or_create_image(self, id, &existing);\n    if (!existing) { Py_RETURN_NONE; }\n    return image_as_dict(self, img);\n}\n\nW(image_for_client_number) {\n    unsigned long num = PyLong_AsUnsignedLong(args);\n    Image *img = img_by_client_number(self, num);\n    if (!img) Py_RETURN_NONE;\n    return image_as_dict(self, img);\n}\n\nW(shm_write) {\n    const char *name, *data;\n    Py_ssize_t sz;\n    PA(\"ss#\", &name, &data, &sz);\n    int fd = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);\n    if (fd == -1) { PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); return NULL; }\n    int ret = ftruncate(fd, sz);\n    if (ret != 0) { safe_close(fd, __FILE__, __LINE__); PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); return NULL; }\n    void *addr = mmap(0, sz, PROT_WRITE, MAP_SHARED, fd, 0);\n    if (addr == MAP_FAILED) { safe_close(fd, __FILE__, __LINE__); PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); return NULL; }\n    memcpy(addr, data, sz);\n    if (munmap(addr, sz) != 0) { safe_close(fd, __FILE__, __LINE__); PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); return NULL; }\n    safe_close(fd, __FILE__, __LINE__);\n    Py_RETURN_NONE;\n}\n\nW(shm_unlink) {\n    char *name;\n    PA(\"s\", &name);\n    int ret = shm_unlink(name);\n    if (ret == -1) { PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); return NULL; }\n    Py_RETURN_NONE;\n}\n\nW(update_layers) {\n    unsigned int scrolled_by, sx, sy; float xstart, ystart, dx, dy, scroll_offset_lines = 0.0f;\n    CellPixelSize cell;\n    PA(\"IffffIIII|f\", &scrolled_by, &xstart, &ystart, &dx, &dy, &sx, &sy, &cell.width, &cell.height, &scroll_offset_lines);\n    grman_update_layers(self, scrolled_by, scroll_offset_lines, xstart, ystart, dx, dy, sx, sy, cell);\n    PyObject *ans = PyTuple_New(self->render_data.count);\n    for (size_t i = 0; i < self->render_data.count; i++) {\n        ImageRenderData *r = self->render_data.item + i;\n#define R(which) Py_BuildValue(\"{sf sf sf sf}\", \"left\", r->which.left, \"top\", r->which.top, \"right\", r->which.right, \"bottom\", r->which.bottom)\n        PyTuple_SET_ITEM(ans, i,\n            Py_BuildValue(\"{sN sN sI si sK sK}\", \"src_rect\", R(src_rect), \"dest_rect\", R(dest_rect), \"group_count\", r->group_count, \"z_index\", r->z_index, \"image_id\", r->image_id, \"ref_id\", r->ref_id)\n        );\n#undef R\n    }\n    return ans;\n}\n\n#define M(x, va) {#x, (PyCFunction)py##x, va, \"\"}\n\nstatic PyMethodDef methods[] = {\n    M(image_for_client_id, METH_O),\n    M(image_for_client_number, METH_O),\n    M(update_layers, METH_VARARGS),\n    {NULL}  /* Sentinel */\n};\n\nstatic PyObject*\nget_image_count(GraphicsManager *self, void* closure UNUSED) {\n    return PyLong_FromSize_t(vt_size(&self->images_by_internal_id));\n}\n\nstatic PyGetSetDef getsets[] = {\n    {\"image_count\", (getter)get_image_count, NULL, NULL, NULL},\n    {NULL},\n};\n\nstatic PyMemberDef members[] = {\n    {\"storage_limit\", T_PYSSIZET, offsetof(GraphicsManager, storage_limit), 0, \"storage_limit\"},\n    {\"disk_cache\", T_OBJECT_EX, offsetof(GraphicsManager, disk_cache), READONLY, \"disk_cache\"},\n    {NULL},\n};\n\nPyTypeObject GraphicsManager_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.GraphicsManager\",\n    .tp_basicsize = sizeof(GraphicsManager),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"GraphicsManager\",\n    .tp_new = new_graphicsmanager_object,\n    .tp_methods = methods,\n    .tp_members = members,\n    .tp_getset = getsets,\n};\n\nstatic PyObject*\npycreate_canvas(PyObject *self UNUSED, PyObject *args) {\n    unsigned int bytes_per_pixel;\n    unsigned int over_width, width, height, x, y;\n    Py_ssize_t over_sz;\n    const uint8_t *over_data;\n    if (!PyArg_ParseTuple(args, \"y#IIIIII\", &over_data, &over_sz, &over_width, &x, &y, &width, &height, &bytes_per_pixel)) return NULL;\n    size_t canvas_sz = (size_t)width * height * bytes_per_pixel;\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, canvas_sz);\n    if (!ans) return NULL;\n\n    uint8_t* canvas = (uint8_t*)PyBytes_AS_STRING(ans);\n    memset(canvas, 0, canvas_sz);\n    ComposeData cd = {\n        .needs_blending = bytes_per_pixel == 4,\n        .over_width = over_width, .over_height = over_sz / (bytes_per_pixel * over_width),\n        .under_width = width, .under_height = height,\n        .over_px_sz = bytes_per_pixel, .under_px_sz = bytes_per_pixel,\n        .over_offset_x = x, .over_offset_y = y\n    };\n    compose(cd, canvas, over_data);\n\n    return ans;\n}\n\nstatic PyMethodDef module_methods[] = {\n    M(shm_write, METH_VARARGS),\n    M(shm_unlink, METH_VARARGS),\n    M(create_canvas, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\n\nbool\ninit_graphics(PyObject *module) {\n    if (PyType_Ready(&GraphicsManager_Type) < 0) return false;\n    if (PyModule_AddObject(module, \"GraphicsManager\", (PyObject *)&GraphicsManager_Type) != 0) return false;\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    if (PyModule_AddIntMacro(module, IMAGE_PLACEHOLDER_CHAR) != 0) return false;\n    Py_INCREF(&GraphicsManager_Type);\n    return true;\n}\n\nvoid grman_mark_layers_dirty(GraphicsManager *self) { set_layers_dirty(self); }\nvoid grman_set_window_id(GraphicsManager *self, id_type id) { self->window_id = id; }\nbool grman_has_images(GraphicsManager *self) { return self->num_of_below_refs + self->num_of_negative_refs + self->num_of_positive_refs > 0; }\nGraphicsRenderData grman_render_data(GraphicsManager *self) {\n    GraphicsRenderData ans = {\n        .count=self->render_data.count, .capacity=self->render_data.capacity, .images=self->render_data.item,\n        .num_of_below_refs=self->num_of_below_refs, .num_of_negative_refs=self->num_of_negative_refs,\n        .num_of_positive_refs=self->num_of_positive_refs,\n    };\n    return ans;\n}\n// }}}\n"
  },
  {
    "path": "kitty/graphics.h",
    "content": "/*\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include \"data-types.h\"\n#include \"monotonic.h\"\n\ntypedef struct {\n    unsigned char action, transmission_type, compressed, delete_action;\n    uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet, parent_id, parent_placement_id;\n    uint32_t width, height, x_offset, y_offset;\n    union { uint32_t cursor_movement, compose_mode; };\n    union { uint32_t cell_x_offset; };\n    union { uint32_t cell_y_offset, bgcolor; };\n    union { uint32_t data_width, animation_state; };\n    union { uint32_t data_height, loop_count; };\n    union { uint32_t num_lines, frame_number; };\n    union { uint32_t num_cells, other_frame_number; };\n    union { int32_t z_index, gap; };\n    size_t payload_sz;\n    bool unicode_placement;\n    int32_t offset_from_parent_x, offset_from_parent_y;\n} GraphicsCommand;\n\ntypedef struct {\n    float left, top, right, bottom;\n} ImageRect;\n\ntypedef struct {\n    ImageRect src_rect, dest_rect;\n    uint32_t texture_id, group_count;\n    int z_index;\n    id_type image_id, ref_id;\n} ImageRenderData;\n\ntypedef struct {\n    uint32_t texture_id;\n    unsigned int height, width;\n    uint8_t* bitmap;\n    uint32_t refcnt, id;\n    size_t mmap_size;\n} BackgroundImage;\n\n\n#ifdef GRAPHICS_INTERNAL_APIS\ntypedef struct {\n    float src_width, src_height, src_x, src_y;\n    uint32_t cell_x_offset, cell_y_offset, num_cols, num_rows, effective_num_rows, effective_num_cols;\n    int32_t z_index;\n    int32_t start_row, start_column;\n    uint32_t client_id;\n    ImageRect src_rect;\n    // Indicates whether this reference represents a cell ref that should be\n    // removed when the corresponding cells are modified.\n    // The internal id of the virtual ref this cell image was created from. Is a cell ref if this is non-zero.\n    id_type virtual_ref_id;\n    // Virtual refs are not displayed but they can be used as prototypes for\n    // refs placed using unicode placeholders.\n    bool is_virtual_ref;\n\n    struct {\n        id_type img, ref;\n        struct { int32_t x, y; } offset;\n    } parent;\n\n    id_type internal_id;\n} ImageRef;\n\ntypedef struct {\n    uint32_t gap, id, width, height, x, y, base_frame_id, bgcolor;\n    bool is_opaque, is_4byte_aligned, alpha_blend;\n} Frame;\n\ntypedef enum { ANIMATION_STOPPED = 0, ANIMATION_LOADING = 1, ANIMATION_RUNNING = 2} AnimationState;\n\ntypedef struct TextureRef {\n    uint32_t id, refcnt;\n} TextureRef;\n\n#define NAME ref_map\n#define KEY_TY id_type\n#define VAL_TY ImageRef*\n#include \"kitty-verstable.h\"\n\ntypedef struct {\n    uint32_t client_id, client_number, width, height;\n    TextureRef *texture;\n    id_type internal_id;\n\n    bool root_frame_data_loaded;\n    id_type ref_id_counter;\n    Frame *extra_frames, root_frame;\n    uint32_t current_frame_index, frame_id_counter;\n    uint64_t animation_duration;\n    size_t extra_framecnt;\n    monotonic_t atime;\n    size_t used_storage;\n    bool is_drawn;\n    AnimationState animation_state;\n    uint32_t max_loops, current_loop;\n    monotonic_t current_frame_shown_at;\n    ref_map refs_by_internal_id;\n} Image;\n\ntypedef struct {\n    id_type image_id;\n    uint32_t frame_id;\n} ImageAndFrame;\n\ntypedef struct {\n    uint8_t *buf;\n    size_t buf_capacity, buf_used;\n\n    uint8_t *mapped_file;\n    size_t mapped_file_sz;\n\n    size_t data_sz;\n    uint8_t *data;\n    bool is_4byte_aligned;\n    bool is_opaque, loading_completed_successfully;\n    uint32_t width, height;\n    GraphicsCommand start_command;\n    ImageAndFrame loading_for;\n} LoadData;\n\n#define NAME image_map\n#define KEY_TY id_type\n#define VAL_TY Image*\n#include \"kitty-verstable.h\"\n\ntypedef struct {\n    PyObject_HEAD\n\n    size_t storage_limit;\n    LoadData currently_loading;\n    id_type image_id_counter;\n    struct {\n        size_t count, capacity;\n        ImageRenderData *item;\n    } render_data;\n    bool layers_dirty;\n    // The number of images below MIN_ZINDEX / 2, then the number of refs between MIN_ZINDEX / 2 and -1 inclusive, then the number of refs above 0 inclusive.\n    size_t num_of_below_refs, num_of_negative_refs, num_of_positive_refs;\n    unsigned int last_scrolled_by;\n    float last_scroll_offset_lines;\n    size_t used_storage;\n    PyObject *disk_cache;\n    bool has_images_needing_animation, context_made_current_for_this_command;\n    id_type window_id;\n    image_map images_by_internal_id;\n} GraphicsManager;\n#else\ntypedef struct {int x;} *GraphicsManager;\n#endif\n\ntypedef struct {\n    int32_t amt, limit;\n    index_type margin_top, margin_bottom;\n    bool has_margins;\n} ScrollData;\n\n\nstatic inline float\ngl_size(const unsigned int sz, const unsigned int viewport_size) {\n    // convert pixel sz to OpenGL coordinate system.\n    const float px = 2.f / viewport_size;\n    return px * sz;\n}\n\nstatic inline float\ngl_pos_x(const int px_from_left_margin, const unsigned int viewport_size) {\n    const float px = 2.f / viewport_size;\n    return -1.f + px_from_left_margin * px;\n}\n\nstatic inline float\ntex_pos_x(const int px_from_left_margin, const unsigned texture_width) {\n    return px_from_left_margin / (float)texture_width;\n}\n\nstatic inline float\ngl_pos_y(const int px_from_top_margin, const unsigned int viewport_size) {\n    const float px = 2.f / viewport_size;\n    return 1.f - px_from_top_margin * px;\n}\n\nstatic inline float\ntex_pos_y(const int px_from_top_margin, const unsigned texture_height) {\n    const int px_from_bottom_margin = texture_height - px_from_top_margin;\n    return px_from_bottom_margin / (float)texture_height;\n}\n\n\ntypedef struct GraphicsRenderData {\n    size_t count, capacity, num_of_below_refs, num_of_negative_refs, num_of_positive_refs;\n    ImageRenderData *images;\n} GraphicsRenderData;\n\nGraphicsManager* grman_alloc(bool for_paused_rendering);\nvoid grman_clear(GraphicsManager*, bool, CellPixelSize fg);\nconst char* grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty, CellPixelSize fg);\nvoid grman_put_cell_image(GraphicsManager *self, uint32_t row, uint32_t col, uint32_t image_id, uint32_t placement_id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, CellPixelSize cell);\nbool grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scroll_offset_lines, float screen_left, float screen_top, float dx, float dy, unsigned int num_cols, unsigned int num_rows, CellPixelSize);\nvoid grman_scroll_images(GraphicsManager *self, const ScrollData*, CellPixelSize fg);\nvoid grman_resize(GraphicsManager*, index_type, index_type, index_type, index_type, index_type, index_type);\nvoid grman_rescale(GraphicsManager *self, CellPixelSize fg);\nvoid grman_remove_cell_images(GraphicsManager *self, int32_t top, int32_t bottom);\nvoid grman_remove_all_cell_images(GraphicsManager *self);\nvoid gpu_data_for_image(ImageRenderData *ans, float left, float top, float right, float bottom);\nbool png_from_file_pointer(FILE* fp, const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);\nbool png_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);\nbool png_from_data(void *png_data, size_t png_data_sz, const char *path_for_error_messages, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);\nbool image_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);\nbool scan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t *minimum_gap, bool os_window_context_set);\nvoid grman_pause_rendering(GraphicsManager *self, GraphicsManager *dest);\nvoid grman_mark_layers_dirty(GraphicsManager *self);\nvoid grman_set_window_id(GraphicsManager *self, id_type id);\nbool grman_has_images(GraphicsManager *self);\nGraphicsRenderData grman_render_data(GraphicsManager *self);\n"
  },
  {
    "path": "kitty/graphics_fragment.glsl",
    "content": "#pragma kitty_include_shader <alpha_blend.glsl>\n#pragma kitty_include_shader <utils.glsl>\n#define ALPHA_TYPE\n\nuniform sampler2D image;\n#ifdef ALPHA_MASK\nuniform vec3 amask_fg;\nuniform vec4 amask_bg_premult;\n#else\nuniform float extra_alpha;\n#endif\n\nin vec2 texcoord;\nout vec4 output_color;\n\nvoid main() {\n    vec4 color = texture(image, texcoord);\n#ifdef ALPHA_MASK\n    color = vec4(amask_fg, color.r);\n    color = vec4_premul(color);\n    color = alpha_blend_premul(color, amask_bg_premult);\n#else\n    color.a *= extra_alpha;\n#if TEXTURE_IS_NOT_PREMULTIPLIED\n    color = vec4_premul(color);\n#endif\n#endif\n    output_color = color;\n}\n"
  },
  {
    "path": "kitty/graphics_vertex.glsl",
    "content": "uniform vec4 src_rect, dest_rect;\n#pragma kitty_include_shader <blit_common.glsl>\n"
  },
  {
    "path": "kitty/guess_mime_type.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport stat\nfrom contextlib import suppress\n\nknown_extensions = {\n    'asciidoc': 'text/asciidoctor',\n    'conf': 'text/config',\n    'md': 'text/markdown',\n    'pyj': 'text/rapydscript-ng',\n    'recipe': 'text/python',\n    'rst': 'text/restructured-text',\n    'rb': 'text/ruby',\n    'toml': 'text/toml',\n    'vim': 'text/vim',\n    'yaml': 'text/yaml',\n    'js': 'text/javascript',\n    'json': 'text/json',\n    'nix': 'text/nix',\n}\n\n\ntext_mimes = (\n    'application/x-sh',\n    'application/x-csh',\n    'application/x-shellscript',\n    'application/javascript',\n    'application/json',\n    'application/xml',\n    'application/x-yaml',\n    'application/yaml',\n    'application/x-toml',\n    'application/x-lua',\n    'application/toml',\n    'application/rss+xml',\n    'application/xhtml+xml',\n    'application/x-tex',\n    'application/x-latex',\n)\n\n\ndef is_special_file(path: str) -> str | None:\n    name = os.path.basename(path)\n    lname = name.lower()\n    if lname == 'makefile' or lname.startswith('makefile.'):\n        return 'text/makefile'\n    if '.' not in name and name.endswith('rc'):\n        return 'text/plain'  # rc file\n    return None\n\n\ndef is_folder(path: str) -> bool:\n    with suppress(OSError):\n        return os.path.isdir(path)\n    return False\n\n\ndef initialize_mime_database() -> None:\n    if hasattr(initialize_mime_database, 'inited'):\n        return\n    setattr(initialize_mime_database, 'inited', True)\n    from mimetypes import init\n    init(None)\n    from kitty.constants import config_dir\n    local_defs = os.path.join(config_dir, 'mime.types')\n    if os.path.exists(local_defs):\n        init((local_defs,))\n\n\ndef clear_mime_cache() -> None:\n    if hasattr(initialize_mime_database, 'inited'):\n        delattr(initialize_mime_database, 'inited')\n\n\ndef guess_type(path: str, allow_filesystem_access: bool = False) -> str | None:\n    is_dir = is_exe = False\n\n    if allow_filesystem_access:\n        with suppress(OSError):\n            st = os.stat(path)\n            is_dir = bool(stat.S_ISDIR(st.st_mode))\n            is_exe = bool(not is_dir and st.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) and os.access(path, os.X_OK))\n\n    if is_dir:\n        return 'inode/directory'\n    from mimetypes import guess_type as stdlib_guess_type\n    initialize_mime_database()\n    mt = None\n    with suppress(Exception):\n        mt = stdlib_guess_type(path)[0]\n    if not mt:\n        ext = path.rpartition('.')[-1].lower()\n        mt = known_extensions.get(ext)\n    if mt in text_mimes:\n        mt = f'text/{mt.split(\"/\", 1)[-1]}'\n    mt = mt or is_special_file(path)\n    if not mt:\n        if is_dir:\n            mt = 'inode/directory'  # type: ignore\n        elif is_exe:\n            mt = 'inode/executable'\n    return mt\n"
  },
  {
    "path": "kitty/history.c",
    "content": "/*\n * history.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"wcswidth.h\"\n#include \"lineops.h\"\n#include \"charsets.h\"\n#include \"resize.h\"\n#include <structmember.h>\n#include \"../3rdparty/ringbuf/ringbuf.h\"\n\nextern PyTypeObject Line_Type;\n#define SEGMENT_SIZE 2048\n\nstatic void\nadd_segment(HistoryBuf *self, index_type num) {\n    self->segments = realloc(self->segments, sizeof(HistoryBufSegment) * (self->num_segments + num));\n    if (self->segments == NULL) fatal(\"Out of memory allocating new history buffer segment\");\n    const size_t cpu_cells_size = self->xnum * SEGMENT_SIZE * sizeof(CPUCell);\n    const size_t gpu_cells_size = self->xnum * SEGMENT_SIZE * sizeof(GPUCell);\n    const size_t segment_size = cpu_cells_size + gpu_cells_size + SEGMENT_SIZE * sizeof(LineAttrs);\n    char *mem = calloc(num, segment_size);\n    if (!mem) fatal(\"Out of memory allocating new history buffer segment\");\n    char *needs_free = mem;\n    for (HistoryBufSegment *s = self->segments + self->num_segments; s < self->segments + self->num_segments + num; s++, mem += segment_size) {\n        s->cpu_cells = (CPUCell*)mem;\n        s->gpu_cells = (GPUCell*)(((uint8_t*)s->cpu_cells) + cpu_cells_size);\n        s->line_attrs = (LineAttrs*)(((uint8_t*)s->gpu_cells) + gpu_cells_size);\n        s->mem = NULL;\n    }\n    self->segments[self->num_segments].mem = needs_free;\n    self->num_segments += num;\n}\n\nstatic void\nfree_segment(HistoryBufSegment *s) {\n    free(s->mem); zero_at_ptr(s);\n}\n\nstatic index_type\nsegment_for(HistoryBuf *self, index_type y) {\n    index_type seg_num = y / SEGMENT_SIZE;\n    while (UNLIKELY(seg_num >= self->num_segments && SEGMENT_SIZE * self->num_segments < self->ynum)) add_segment(self, 1);\n    if (UNLIKELY(seg_num >= self->num_segments)) fatal(\"Out of bounds access to history buffer line number: %u\", y);\n    return seg_num;\n}\n\n#define seg_ptr(which, stride) { \\\n    index_type seg_num = segment_for(self, y); \\\n    y -= seg_num * SEGMENT_SIZE; \\\n    return self->segments[seg_num].which + y * stride; \\\n}\n\nstatic CPUCell*\ncpu_lineptr(HistoryBuf *self, index_type y) {\n    seg_ptr(cpu_cells, self->xnum);\n}\n\nstatic GPUCell*\ngpu_lineptr(HistoryBuf *self, index_type y) {\n    seg_ptr(gpu_cells, self->xnum);\n}\n\n\nstatic LineAttrs*\nattrptr(HistoryBuf *self, index_type y) {\n    seg_ptr(line_attrs, 1);\n}\n\nstatic size_t\ninitial_pagerhist_ringbuf_sz(size_t pagerhist_sz) { return MIN(1024u * 1024u, pagerhist_sz); }\n\nstatic PagerHistoryBuf*\nalloc_pagerhist(size_t pagerhist_sz) {\n    PagerHistoryBuf *ph;\n    if (!pagerhist_sz) return NULL;\n    ph = calloc(1, sizeof(PagerHistoryBuf));\n    if (!ph) return NULL;\n    size_t sz = initial_pagerhist_ringbuf_sz(pagerhist_sz);\n    ph->ringbuf = ringbuf_new(sz);\n    if (!ph->ringbuf) { free(ph); return NULL; }\n    ph->maximum_size = pagerhist_sz;\n    return ph;\n}\n\nstatic void\nfree_pagerhist(HistoryBuf *self) {\n    if (self->pagerhist && self->pagerhist->ringbuf) ringbuf_free((ringbuf_t*)&self->pagerhist->ringbuf);\n    free(self->pagerhist);\n    self->pagerhist = NULL;\n}\n\nstatic bool\npagerhist_extend(PagerHistoryBuf *ph, size_t minsz) {\n    size_t buffer_size = ringbuf_capacity(ph->ringbuf);\n    if (buffer_size >= ph->maximum_size) return false;\n    size_t newsz = MIN(ph->maximum_size, buffer_size + MAX(1024u * 1024u, minsz));\n    ringbuf_t newbuf = ringbuf_new(newsz);\n    if (!newbuf) return false;\n    size_t count = ringbuf_bytes_used(ph->ringbuf);\n    if (count) ringbuf_copy(newbuf, ph->ringbuf, count);\n    ringbuf_free((ringbuf_t*)&ph->ringbuf);\n    ph->ringbuf = newbuf;\n    return true;\n}\n\nstatic void\npagerhist_clear(HistoryBuf *self) {\n    if (self->pagerhist && self->pagerhist->ringbuf) {\n        ringbuf_reset(self->pagerhist->ringbuf);\n        size_t rsz = initial_pagerhist_ringbuf_sz(self->pagerhist->maximum_size);\n        void *rbuf = ringbuf_new(rsz);\n        if (rbuf) {\n            ringbuf_free((ringbuf_t*)&self->pagerhist->ringbuf);\n            self->pagerhist->ringbuf = rbuf;\n        }\n    }\n}\n\nstatic HistoryBuf*\ncreate_historybuf(PyTypeObject *type, unsigned int xnum, unsigned int ynum, unsigned int pagerhist_sz, TextCache *tc) {\n    if (xnum == 0 || ynum == 0) {\n        PyErr_SetString(PyExc_ValueError, \"Cannot create an empty history buffer\");\n        return NULL;\n    }\n    HistoryBuf *self = (HistoryBuf *)type->tp_alloc(type, 0);\n    if (self != NULL) {\n        self->xnum = xnum;\n        self->ynum = ynum;\n        self->num_segments = 0;\n        add_segment(self, 1);\n        self->text_cache = tc_incref(tc);\n        self->line = alloc_line(self->text_cache);\n        self->line->xnum = xnum;\n        self->pagerhist = alloc_pagerhist(pagerhist_sz);\n    }\n    return self;\n}\n\nstatic PyObject *\nnew_history_object(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {\n    unsigned int xnum = 1, ynum = 1, pagerhist_sz = 0;\n    if (!PyArg_ParseTuple(args, \"II|I\", &ynum, &xnum, &pagerhist_sz)) return NULL;\n    TextCache *tc = tc_alloc();\n    if (!tc) return PyErr_NoMemory();\n    HistoryBuf *ans = create_historybuf(type, xnum, ynum, pagerhist_sz, tc);\n    tc_decref(tc);\n    return (PyObject*)ans;\n}\n\nstatic void\ndealloc(HistoryBuf* self) {\n    Py_CLEAR(self->line);\n    for (size_t i = 0; i < self->num_segments; i++) free_segment(self->segments + i);\n    free(self->segments);\n    free_pagerhist(self);\n    tc_decref(self->text_cache);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic index_type\nindex_of(HistoryBuf *self, index_type lnum) {\n    // The index (buffer position) of the line with line number lnum\n    // This is reverse indexing, i.e. lnum = 0 corresponds to the *last* line in the buffer.\n    if (self->count == 0) return 0;\n    index_type idx = self->count - 1 - MIN(self->count - 1, lnum);\n    return (self->start_of_data + idx) % self->ynum;\n}\n\nstatic bool\nhb_line_is_continued(HistoryBuf *self, index_type num) {\n    if (num == 0) {\n        size_t sz;\n        if (self->pagerhist && self->pagerhist->ringbuf && (sz = ringbuf_bytes_used(self->pagerhist->ringbuf)) > 0) {\n            size_t pos = ringbuf_findchr(self->pagerhist->ringbuf, '\\n', sz - 1);\n            if (pos >= sz) return true;  // ringbuf does not end with a newline\n        }\n        return false;\n    }\n    return cpu_lineptr(self, num - 1)[self->xnum-1].next_char_was_wrapped;\n}\n\nstatic void\ninit_line(HistoryBuf *self, index_type num, Line *l) {\n    // Initialize the line l, setting its pointer to the offsets for the line at index (buffer position) num\n    l->cpu_cells = cpu_lineptr(self, num);\n    l->gpu_cells = gpu_lineptr(self, num);\n    l->attrs = *attrptr(self, num);\n}\n\nvoid\nhistorybuf_init_line(HistoryBuf *self, index_type lnum, Line *l) {\n    init_line(self, index_of(self, lnum), l);\n}\n\nbool\nhistorybuf_is_line_continued(HistoryBuf *self, index_type lnum) {\n    return hb_line_is_continued(self, index_of(self, lnum));\n}\n\nbool\nhistory_buf_endswith_wrap(HistoryBuf *self) {\n    return cpu_lineptr(self, index_of(self, 0))[self->xnum-1].next_char_was_wrapped;\n}\n\nCPUCell*\nhistorybuf_cpu_cells(HistoryBuf *self, index_type lnum) {\n    return cpu_lineptr(self, index_of(self, lnum));\n}\n\nvoid\nhistorybuf_mark_line_clean(HistoryBuf *self, index_type y) {\n    attrptr(self, index_of(self, y))->has_dirty_text = false;\n}\n\nvoid\nhistorybuf_mark_line_dirty(HistoryBuf *self, index_type y) {\n    attrptr(self, index_of(self, y))->has_dirty_text = true;\n}\n\nvoid\nhistorybuf_set_line_has_image_placeholders(HistoryBuf *self, index_type y, bool val) {\n    attrptr(self, index_of(self, y))->has_image_placeholders = val;\n}\n\nvoid\nhistorybuf_clear(HistoryBuf *self) {\n    pagerhist_clear(self);\n    self->count = 0;\n    self->start_of_data = 0;\n    for (size_t i = 0; i < self->num_segments; i++) free_segment(self->segments + i);\n    free(self->segments); self->segments = NULL;\n    self->num_segments = 0;\n    add_segment(self, 1);\n}\n\nstatic bool\npagerhist_write_bytes(PagerHistoryBuf *ph, const uint8_t *buf, size_t sz) {\n    if (sz > ph->maximum_size) return false;\n    if (!sz) return true;\n    size_t space_in_ringbuf = ringbuf_bytes_free(ph->ringbuf);\n    if (sz > space_in_ringbuf) pagerhist_extend(ph, sz);\n    ringbuf_memcpy_into(ph->ringbuf, buf, sz);\n    return true;\n}\n\nstatic bool\npagerhist_ensure_start_is_valid_utf8(PagerHistoryBuf *ph) {\n    uint8_t scratch[8];\n    size_t num = ringbuf_memcpy_from(scratch, ph->ringbuf, arraysz(scratch));\n    uint32_t codep;\n    UTF8State state = UTF8_ACCEPT;\n    size_t count = 0;\n    size_t last_reject_at = 0;\n    while (count < num) {\n        decode_utf8(&state, &codep, scratch[count++]);\n        if (state == UTF8_ACCEPT) break;\n        if (state == UTF8_REJECT) { state = UTF8_ACCEPT; last_reject_at = count; }\n    }\n    if (last_reject_at) {\n        ringbuf_memmove_from(scratch, ph->ringbuf, last_reject_at);\n        return true;\n    }\n    return false;\n}\n\nstatic bool\npagerhist_write_ucs4(PagerHistoryBuf *ph, const Py_UCS4 *buf, size_t sz) {\n    uint8_t scratch[4];\n    for (size_t i = 0; i < sz; i++) {\n        unsigned int num = encode_utf8(buf[i], (char*)scratch);\n        if (!pagerhist_write_bytes(ph, scratch, num)) return false;\n    }\n    return true;\n}\n\nstatic void\npagerhist_push(HistoryBuf *self, ANSIBuf *as_ansi_buf) {\n    PagerHistoryBuf *ph = self->pagerhist;\n    if (!ph) return;\n    Line l = {.xnum=self->xnum, .text_cache=self->text_cache};\n    init_line(self, self->start_of_data, &l);\n    ANSILineState s = {.output_buf=as_ansi_buf};\n    as_ansi_buf->len = 0;\n    line_as_ansi(&l, &s, 0, l.xnum, 0, true);\n    pagerhist_write_bytes(ph, (const uint8_t*)\"\\x1b[m\", 3);\n    if (pagerhist_write_ucs4(ph, as_ansi_buf->buf, as_ansi_buf->len)) {\n        char line_end[2]; size_t num = 0;\n        line_end[num++] = '\\r';\n        if (!l.cpu_cells[l.xnum - 1].next_char_was_wrapped) line_end[num++] = '\\n';\n        pagerhist_write_bytes(ph, (const uint8_t*)line_end, num);\n    }\n}\n\nstatic index_type\nhistorybuf_push(HistoryBuf *self, ANSIBuf *as_ansi_buf, bool *needs_clear) {\n    index_type idx = (self->start_of_data + self->count) % self->ynum;\n    if (self->count == self->ynum) {\n        pagerhist_push(self, as_ansi_buf);\n        self->start_of_data = (self->start_of_data + 1) % self->ynum;\n        *needs_clear = true;\n    } else {\n        self->count++;\n        *needs_clear = false;\n    }\n    return idx;\n}\n\nvoid\nhistorybuf_add_line(HistoryBuf *self, const Line *line, ANSIBuf *as_ansi_buf) {\n    bool needs_clear;\n    index_type idx = historybuf_push(self, as_ansi_buf, &needs_clear);\n    init_line(self, idx, self->line);\n    copy_line(line, self->line);\n    *attrptr(self, idx) = line->attrs;\n}\n\nbool\nhistorybuf_pop_line(HistoryBuf *self, Line *line) {\n    if (self->count <= 0) return false;\n    index_type idx = (self->start_of_data + self->count - 1) % self->ynum;\n    init_line(self, idx, line);\n    self->count--;\n    return true;\n}\n\nvoid\nhistorybuf_delete_newest_lines(HistoryBuf *self, index_type count) {\n    if (!count) return;\n    count = MIN(self->count, count);\n    self->count -= count - 1;\n    // now nuke multi cell chars that overlap onto the last line\n    index_type idx = (self->start_of_data + self->count - 1) % self->ynum;\n    init_line(self, idx, self->line);\n    CPUCell *cells = self->line->cpu_cells;\n    self->count--;\n    for (index_type x = 0; x < self->line->xnum; x++) {\n        CPUCell *c = cells + x;\n        if (c->is_multicell && c->y) {\n            for (index_type i = 0, pos = self->count; i < c->y && pos; i++, pos--) {\n                index_type idx = (self->start_of_data + pos - 1) % self->ynum;\n                init_line(self, idx, self->line);\n                CPUCell *m = self->line->cpu_cells + x;\n                cell_set_char(m, 0); m->is_multicell = false;\n                clear_sprite_position(self->line->gpu_cells[x]);\n            }\n        }\n    }\n}\n\nstatic PyObject*\nline(HistoryBuf *self, PyObject *val) {\n#define line_doc \"Return the line with line number val. This buffer grows upwards, i.e. 0 is the most recently added line\"\n    if (self->count == 0) { PyErr_SetString(PyExc_IndexError, \"This buffer is empty\"); return NULL; }\n    index_type lnum = PyLong_AsUnsignedLong(val);\n    if (lnum >= self->count) { PyErr_SetString(PyExc_IndexError, \"Out of bounds\"); return NULL; }\n    init_line(self, index_of(self, lnum), self->line);\n    Py_INCREF(self->line);\n    return (PyObject*)self->line;\n}\n\nstatic PyObject*\n__str__(HistoryBuf *self) {\n    PyObject *lines = PyTuple_New(self->count);\n    if (lines == NULL) return PyErr_NoMemory();\n    RAII_ANSIBuf(buf);\n    for (index_type i = 0; i < self->count; i++) {\n        init_line(self, index_of(self, i), self->line);\n        PyObject *t = line_as_unicode(self->line, false, &buf);\n        if (t == NULL) { Py_CLEAR(lines); return NULL; }\n        PyTuple_SET_ITEM(lines, i, t);\n    }\n    PyObject *sep = PyUnicode_FromString(\"\\n\");\n    PyObject *ans = PyUnicode_Join(sep, lines);\n    Py_CLEAR(lines); Py_CLEAR(sep);\n    return ans;\n}\n\nstatic PyObject*\npush(HistoryBuf *self, PyObject *args) {\n#define push_doc \"Push a line into this buffer, removing the oldest line, if necessary\"\n    Line *line;\n    if (!PyArg_ParseTuple(args, \"O!\", &Line_Type, &line)) return NULL;\n    ANSIBuf as_ansi_buf = {0};\n    historybuf_add_line(self, line, &as_ansi_buf);\n    free(as_ansi_buf.buf);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nas_ansi(HistoryBuf *self, PyObject *callback) {\n#define as_ansi_doc \"as_ansi(callback) -> The contents of this buffer as ANSI escaped text. callback is called with each successive line.\"\n    Line l = {.xnum=self->xnum, .text_cache=self->text_cache};\n    ANSIBuf output = {0}; ANSILineState s = {.output_buf=&output};\n    for(unsigned int i = 0; i < self->count; i++) {\n        init_line(self, i, &l);\n        output.len = 0;\n        line_as_ansi(&l, &s, 0, l.xnum, 0, true);\n        if (!l.cpu_cells[l.xnum - 1].next_char_was_wrapped) {\n            ensure_space_for(&output, buf, Py_UCS4, output.len + 1, capacity, 2048, false);\n            output.buf[output.len++] = '\\n';\n        }\n        PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output.buf, output.len);\n        if (ans == NULL) { PyErr_NoMemory(); goto end; }\n        PyObject *ret = PyObject_CallFunctionObjArgs(callback, ans, NULL);\n        Py_CLEAR(ans);\n        if (ret == NULL) goto end;\n        Py_CLEAR(ret);\n    }\nend:\n    free(output.buf);\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic char_type\npagerhist_remove_char(PagerHistoryBuf *ph, unsigned *count, uint8_t record[8]) {\n    uint32_t codep; UTF8State state = UTF8_ACCEPT;\n    *count = 0;\n    size_t num = ringbuf_bytes_used(ph->ringbuf);\n    while (num--) {\n        record[*count] = ringbuf_move_char(ph->ringbuf);\n        decode_utf8(&state, &codep, record[*count]);\n        *count += 1;\n        if (state == UTF8_REJECT) { codep = 0; break; }\n        if (state == UTF8_ACCEPT) break;\n    }\n    return codep;\n}\n\nstatic void\npagerhist_rewrap_to(HistoryBuf *self, index_type cells_in_line) {\n    PagerHistoryBuf *ph = self->pagerhist;\n    if (!ph->ringbuf || !ringbuf_bytes_used(ph->ringbuf)) return;\n    PagerHistoryBuf *nph = calloc(1, sizeof(PagerHistoryBuf));\n    if (!nph) return;\n    nph->maximum_size = ph->maximum_size;\n    nph->ringbuf = ringbuf_new(MIN(ph->maximum_size, ringbuf_capacity(ph->ringbuf) + 4096));\n    if (!nph->ringbuf) { free(nph); return ; }\n    ssize_t ch_width = 0;\n    unsigned count;\n    uint8_t record[8];\n    index_type num_in_current_line = 0;\n    char_type ch;\n    WCSState wcs_state;\n    initialize_wcs_state(&wcs_state);\n\n#define WRITE_CHAR() { \\\n    if (num_in_current_line + ch_width > cells_in_line) { \\\n        pagerhist_write_bytes(nph, (const uint8_t*)\"\\r\", 1); \\\n        num_in_current_line = 0; \\\n    }\\\n    if (ch_width >= 0 || (int)num_in_current_line >= -ch_width) num_in_current_line += ch_width; \\\n    pagerhist_write_bytes(nph, record, count); \\\n}\n\n    while (ringbuf_bytes_used(ph->ringbuf)) {\n        ch = pagerhist_remove_char(ph, &count, record);\n        if (ch == '\\n') {\n            initialize_wcs_state(&wcs_state);\n            ch_width = 1;\n            WRITE_CHAR();\n            num_in_current_line = 0;\n        } else if (ch != '\\r') {\n            ch_width = wcswidth_step(&wcs_state, ch);\n            WRITE_CHAR();\n        }\n    }\n    free_pagerhist(self);\n    self->pagerhist = nph;\n#undef WRITE_CHAR\n}\n\nstatic PyObject*\npagerhist_write(HistoryBuf *self, PyObject *what) {\n    if (self->pagerhist && self->pagerhist->maximum_size) {\n        if (PyBytes_Check(what)) pagerhist_write_bytes(self->pagerhist, (const uint8_t*)PyBytes_AS_STRING(what), PyBytes_GET_SIZE(what));\n        else if (PyUnicode_Check(what) && PyUnicode_READY(what) == 0) {\n            Py_UCS4 *buf = PyUnicode_AsUCS4Copy(what);\n            if (buf) {\n                pagerhist_write_ucs4(self->pagerhist, buf, PyUnicode_GET_LENGTH(what));\n                PyMem_Free(buf);\n            }\n        }\n    }\n    Py_RETURN_NONE;\n}\n\nstatic const uint8_t*\nreverse_find(const uint8_t *haystack, size_t haystack_sz, const uint8_t *needle) {\n    const size_t needle_sz = strlen((const char*)needle);\n    if (!needle_sz || needle_sz > haystack_sz) return NULL;\n    const uint8_t *p = haystack + haystack_sz - (needle_sz - 1);\n    while (--p >= haystack) {\n        if (*p == needle[0] && memcmp(p, needle, MIN(needle_sz, haystack_sz - (p - haystack))) == 0) return p;\n    }\n    return NULL;\n}\n\nstatic PyObject*\npagerhist_as_bytes(HistoryBuf *self, PyObject *args) {\n    int upto_output_start = 0;\n    if (!PyArg_ParseTuple(args, \"|p\", &upto_output_start)) return NULL;\n#define ph self->pagerhist\n    if (!ph || !ringbuf_bytes_used(ph->ringbuf)) return PyBytes_FromStringAndSize(\"\", 0);\n    pagerhist_ensure_start_is_valid_utf8(ph);\n    if (ph->rewrap_needed) pagerhist_rewrap_to(self, self->xnum);\n\n    size_t sz = ringbuf_bytes_used(ph->ringbuf);\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, sz);\n    if (!ans) return NULL;\n    uint8_t *buf = (uint8_t*)PyBytes_AS_STRING(ans);\n    ringbuf_memcpy_from(buf, ph->ringbuf, sz);\n    if (upto_output_start) {\n        const uint8_t *p = reverse_find(buf, sz, (const uint8_t*)\"\\x1b]133;C\\x1b\\\\\");\n        if (p) {\n            PyObject *t = PyBytes_FromStringAndSize((const char*)p, sz - (p - buf));\n            Py_DECREF(ans); ans = t;\n        }\n    }\n    return ans;\n#undef ph\n}\n\nstatic PyObject *\npagerhist_as_text(HistoryBuf *self, PyObject *args) {\n    PyObject *ans = NULL;\n    PyObject *bytes = pagerhist_as_bytes(self, args);\n    if (bytes) {\n        ans = PyUnicode_DecodeUTF8(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes), \"ignore\");\n        Py_DECREF(bytes);\n    }\n    return ans;\n}\n\ntypedef struct {\n    Line line;\n    HistoryBuf *self;\n} GetLineWrapper;\n\nstatic Line*\nget_line(HistoryBuf *self, index_type y, Line *l) {\n    init_line(self, index_of(self, self->count - y - 1), l);\n    return l;\n}\n\nstatic Line*\nget_line_wrapper(void *x, int y) {\n    GetLineWrapper *glw = x;\n    get_line(glw->self, y, &glw->line);\n    return &glw->line;\n}\n\nPyObject*\nas_text_history_buf(HistoryBuf *self, PyObject *args, ANSIBuf *output) {\n    GetLineWrapper glw = {.self=self};\n    glw.line.xnum = self->xnum;\n    glw.line.text_cache = self->text_cache;\n    PyObject *ans = as_text_generic(args, &glw, get_line_wrapper, self->count, output, true);\n    return ans;\n}\n\n\nstatic PyObject*\ndirty_lines(HistoryBuf *self, PyObject *a UNUSED) {\n#define dirty_lines_doc \"dirty_lines() -> Line numbers of all lines that have dirty text.\"\n    PyObject *ans = PyList_New(0);\n    for (index_type i = 0; i < self->count; i++) {\n        if (attrptr(self, i)->has_dirty_text) {\n            PyList_Append(ans, PyLong_FromUnsignedLong(i));\n        }\n    }\n    return ans;\n}\n\nstatic PyObject*\npagerhist_rewrap(HistoryBuf *self, PyObject *xnum) {\n    if (self->pagerhist) {\n        pagerhist_rewrap_to(self, PyLong_AsUnsignedLong(xnum));\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nis_continued(HistoryBuf *self, PyObject *val) {\n#define is_continued_doc \"is_continued(y) -> Whether the line y is continued or not\"\n    unsigned long y = PyLong_AsUnsignedLong(val);\n    if (y >= self->count) { PyErr_SetString(PyExc_ValueError, \"Out of bounds.\"); return NULL; }\n    index_type num = index_of(self, self->count - y - 1);\n    if (hb_line_is_continued(self, num)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nendswith_wrap(HistoryBuf *self, PyObject *val UNUSED) {\n#define endswith_wrap_doc \"endswith_wrap() -> Whether the last line is wrapped at the end of the buffer\"\n    if (history_buf_endswith_wrap(self)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\n\n\n// Boilerplate {{{\nstatic PyObject* rewrap(HistoryBuf *self, PyObject *args);\n#define rewrap_doc \"\"\n\nstatic PyMethodDef methods[] = {\n    METHOD(line, METH_O)\n    METHOD(is_continued, METH_O)\n    METHOD(endswith_wrap, METH_NOARGS)\n    METHOD(as_ansi, METH_O)\n    METHODB(pagerhist_write, METH_O),\n    METHODB(pagerhist_rewrap, METH_O),\n    METHODB(pagerhist_as_text, METH_VARARGS),\n    METHODB(pagerhist_as_bytes, METH_VARARGS),\n    METHOD(dirty_lines, METH_NOARGS)\n    METHOD(push, METH_VARARGS)\n    METHOD(rewrap, METH_VARARGS)\n    {NULL, NULL, 0, NULL}  /* Sentinel */\n};\n\nstatic PyMemberDef members[] = {\n    {\"xnum\", T_UINT, offsetof(HistoryBuf, xnum), READONLY, \"xnum\"},\n    {\"ynum\", T_UINT, offsetof(HistoryBuf, ynum), READONLY, \"ynum\"},\n    {\"count\", T_UINT, offsetof(HistoryBuf, count), READONLY, \"count\"},\n    {NULL}  /* Sentinel */\n};\n\nPyTypeObject HistoryBuf_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.HistoryBuf\",\n    .tp_basicsize = sizeof(HistoryBuf),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"History buffers\",\n    .tp_methods = methods,\n    .tp_members = members,\n    .tp_str = (reprfunc)__str__,\n    .tp_new = new_history_object\n};\n\nINIT_TYPE(HistoryBuf)\n\nHistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns, unsigned int pagerhist_sz, TextCache *tc) {\n    return create_historybuf(&HistoryBuf_Type, columns, lines, pagerhist_sz, tc);\n}\n\n// }}}\n\nstatic void\nhistory_buf_set_last_char_as_continuation(HistoryBuf *self, index_type y, bool wrapped) {\n    if (self->count > 0) {\n        cpu_lineptr(self, index_of(self, y))[self->xnum-1].next_char_was_wrapped = wrapped;\n    }\n}\n\nindex_type\nhistorybuf_next_dest_line(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued) {\n    history_buf_set_last_char_as_continuation(self, 0, continued);\n    bool needs_clear;\n    index_type idx = historybuf_push(self, as_ansi_buf, &needs_clear);\n    *attrptr(self, idx) = src_line->attrs;\n    init_line(self, idx, dest_line);\n    if (needs_clear) {\n        zero_at_ptr_count(dest_line->cpu_cells, dest_line->xnum);\n        zero_at_ptr_count(dest_line->gpu_cells, dest_line->xnum);\n    }\n    return dest_y + 1;\n}\n\nHistoryBuf*\nhistorybuf_alloc_for_rewrap(unsigned int columns, HistoryBuf *self) {\n    if (!self) return NULL;\n    HistoryBuf *ans = alloc_historybuf(self->ynum, columns, 0, self->text_cache);\n    if (ans) {\n        if (ans->num_segments < self->num_segments) add_segment(ans, self->num_segments - ans->num_segments);\n        ans->count = 0; ans->start_of_data = 0;\n    }\n    return ans;\n}\n\nvoid\nhistorybuf_finish_rewrap(HistoryBuf *dest, HistoryBuf *src) {\n    for (index_type i = 0; i < dest->count; i++) attrptr(dest, (dest->start_of_data + i) % dest->ynum)->has_dirty_text = true;\n    dest->pagerhist = src->pagerhist; src->pagerhist = NULL;\n    if (dest->pagerhist && dest->xnum != src->xnum && ringbuf_bytes_used(dest->pagerhist->ringbuf)) dest->pagerhist->rewrap_needed = true;\n}\n\nvoid\nhistorybuf_fast_rewrap(HistoryBuf *dest, HistoryBuf *src) {\n    for (index_type i = 0; i < src->num_segments; i++) {\n        memcpy(dest->segments[i].cpu_cells, src->segments[i].cpu_cells, SEGMENT_SIZE * src->xnum * sizeof(CPUCell));\n        memcpy(dest->segments[i].gpu_cells, src->segments[i].gpu_cells, SEGMENT_SIZE * src->xnum * sizeof(GPUCell));\n        memcpy(dest->segments[i].line_attrs, src->segments[i].line_attrs, SEGMENT_SIZE * sizeof(LineAttrs));\n    }\n    dest->count = src->count; dest->start_of_data = src->start_of_data;\n}\n\n\nstatic PyObject*\nrewrap(HistoryBuf *self, PyObject *args) {\n    unsigned xnum;\n    if (!PyArg_ParseTuple(args, \"I\", &xnum)) return NULL;\n    ANSIBuf as_ansi_buf = {0};\n    LineBuf *dummy = alloc_linebuf(4, self->xnum, self->text_cache);\n    if (!dummy) return PyErr_NoMemory();\n    RAII_PyObject(cleanup, (PyObject*)dummy); (void)cleanup;\n    TrackCursor cursors[1] = {{.is_sentinel=true}};\n    ResizeResult r = resize_screen_buffers(dummy, self, 8, xnum, &as_ansi_buf, cursors);\n    free(as_ansi_buf.buf);\n    if (!r.ok) return PyErr_NoMemory();\n    Py_CLEAR(r.lb);\n    return (PyObject*)r.hb;\n}\n"
  },
  {
    "path": "kitty/history.h",
    "content": "/*\n * history.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"line.h\"\n\ntypedef struct {\n    GPUCell *gpu_cells;\n    CPUCell *cpu_cells;\n    LineAttrs *line_attrs;\n    void *mem;\n} HistoryBufSegment;\n\ntypedef struct {\n    void *ringbuf;\n    size_t maximum_size;\n    bool rewrap_needed;\n} PagerHistoryBuf;\n\n\ntypedef struct {\n    PyObject_HEAD\n\n    index_type xnum, ynum, num_segments;\n    HistoryBufSegment *segments;\n    PagerHistoryBuf *pagerhist;\n    Line *line;\n    TextCache *text_cache;\n    index_type start_of_data, count;\n} HistoryBuf;\n\n\nHistoryBuf* alloc_historybuf(unsigned int, unsigned int, unsigned int, TextCache *tc);\nHistoryBuf *historybuf_alloc_for_rewrap(unsigned int columns, HistoryBuf *self);\nvoid historybuf_finish_rewrap(HistoryBuf *dest, HistoryBuf *src);\nvoid historybuf_fast_rewrap(HistoryBuf *dest, HistoryBuf *src);\nindex_type historybuf_next_dest_line(HistoryBuf *self, ANSIBuf *as_ansi_buf, Line *src_line, index_type dest_y, Line *dest_line, bool continued);\nbool historybuf_is_line_continued(HistoryBuf *self, index_type lnum);\nvoid historybuf_delete_newest_lines(HistoryBuf *self, index_type count);\n"
  },
  {
    "path": "kitty/hsluv.glsl",
    "content": "/*\nHSLUV-GLSL v4.2\nHSLUV is a human-friendly alternative to HSL. ( http://www.hsluv.org )\nGLSL port by William Malo ( https://github.com/williammalo )\nPut this code in your fragment shader.\n*/\n\n// stripped down and optimized (branchless) version\n\nfloat divide(float num, float denom) {\n    return num / (abs(denom) + 1e-15) * sign(denom);\n}\n\nvec3 divide(vec3 num, vec3 denom) {\n    return num / (abs(denom) + 1e-15) * sign(denom);\n}\n\nvec3 hsluv_intersectLineLine(vec3 line1x, vec3 line1y, vec3 line2x, vec3 line2y) {\n    return (line1y - line2y) / (line2x - line1x);\n}\n\nvec3 hsluv_distanceFromPole(vec3 pointx,vec3 pointy) {\n    return sqrt(pointx*pointx + pointy*pointy);\n}\n\nvec3 hsluv_lengthOfRayUntilIntersect(float theta, vec3 x, vec3 y) {\n    vec3 len = divide(y, sin(theta) - x * cos(theta));\n    len = mix(len, vec3(1000.0), step(len, vec3(0.0)));\n    return len;\n}\n\nfloat hsluv_maxSafeChromaForL(float L){\n    mat3 m2 = mat3(\n         3.2409699419045214  ,-0.96924363628087983 , 0.055630079696993609,\n        -1.5373831775700935  , 1.8759675015077207  ,-0.20397695888897657 ,\n        -0.49861076029300328 , 0.041555057407175613, 1.0569715142428786\n    );\n    float sub0 = L + 16.0;\n    float sub1 = sub0 * sub0 * sub0 * .000000641;\n    float sub2 = mix(L / 903.2962962962963, sub1, step(0.0088564516790356308, sub1));\n\n    vec3 top1   = (284517.0 * m2[0] - 94839.0  * m2[2]) * sub2;\n    vec3 bottom = (632260.0 * m2[2] - 126452.0 * m2[1]) * sub2;\n    vec3 top2   = (838422.0 * m2[2] + 769860.0 * m2[1] + 731718.0 * m2[0]) * L * sub2;\n\n    vec3 bounds0x = top1 / bottom;\n    vec3 bounds0y = top2 / bottom;\n\n    vec3 bounds1x =              top1 / (bottom+126452.0);\n    vec3 bounds1y = (top2-769860.0*L) / (bottom+126452.0);\n\n    vec3 xs0 = hsluv_intersectLineLine(bounds0x, bounds0y, -1.0/bounds0x, vec3(0.0) );\n    vec3 xs1 = hsluv_intersectLineLine(bounds1x, bounds1y, -1.0/bounds1x, vec3(0.0) );\n\n    vec3 lengths0 = hsluv_distanceFromPole( xs0, bounds0y + xs0 * bounds0x );\n    vec3 lengths1 = hsluv_distanceFromPole( xs1, bounds1y + xs1 * bounds1x );\n\n    return  min(lengths0.r,\n            min(lengths1.r,\n            min(lengths0.g,\n            min(lengths1.g,\n            min(lengths0.b,\n                lengths1.b)))));\n}\n\nfloat hsluv_maxChromaForLH(float L, float H) {\n\n    float hrad = radians(H);\n\n    mat3 m2 = mat3(\n         3.2409699419045214  ,-0.96924363628087983 , 0.055630079696993609,\n        -1.5373831775700935  , 1.8759675015077207  ,-0.20397695888897657 ,\n        -0.49861076029300328 , 0.041555057407175613, 1.0569715142428786\n    );\n    float sub1 = pow(L + 16.0, 3.0) / 1560896.0;\n    float sub2 = mix(L / 903.2962962962963, sub1, step(0.0088564516790356308, sub1));\n\n    vec3 top1   = (284517.0 * m2[0] - 94839.0  * m2[2]) * sub2;\n    vec3 bottom = (632260.0 * m2[2] - 126452.0 * m2[1]) * sub2;\n    vec3 top2   = (838422.0 * m2[2] + 769860.0 * m2[1] + 731718.0 * m2[0]) * L * sub2;\n\n    vec3 bound0x = top1 / bottom;\n    vec3 bound0y = top2 / bottom;\n\n    vec3 bound1x =              top1 / (bottom+126452.0);\n    vec3 bound1y = (top2-769860.0*L) / (bottom+126452.0);\n\n    vec3 lengths0 = hsluv_lengthOfRayUntilIntersect(hrad, bound0x, bound0y );\n    vec3 lengths1 = hsluv_lengthOfRayUntilIntersect(hrad, bound1x, bound1y );\n\n    return  min(lengths0.r,\n            min(lengths1.r,\n            min(lengths0.g,\n            min(lengths1.g,\n            min(lengths0.b,\n                lengths1.b)))));\n}\n\nvec3 hsluv_fromLinear(vec3 c) {\n    return mix(c * 12.92, 1.055 * pow(max(c, vec3(0)), vec3(1.0 / 2.4)) - 0.055, step(0.0031308, c));\n}\n\nvec3 hsluv_toLinear(vec3 c) {\n    return mix(c / 12.92, pow(max((c + 0.055) / (1.0 + 0.055), vec3(0)), vec3(2.4)), step(0.04045, c));\n}\n\nfloat hsluv_yToL(float Y){\n    return mix(Y * 903.2962962962963, 116.0 * pow(max(Y, 0), 1.0 / 3.0) - 16.0, step(0.0088564516790356308, Y));\n}\n\nfloat hsluv_lToY(float L) {\n    return mix(L / 903.2962962962963, pow((max(L, 0) + 16.0) / 116.0, 3.0), step(8.0, L));\n}\n\nvec3 xyzToRgb(vec3 tuple) {\n    const mat3 m = mat3(\n        3.2409699419045214  ,-1.5373831775700935 ,-0.49861076029300328 ,\n       -0.96924363628087983 , 1.8759675015077207 , 0.041555057407175613,\n        0.055630079696993609,-0.20397695888897657, 1.0569715142428786  );\n    return hsluv_fromLinear(tuple*m);\n}\n\nvec3 rgbToXyz(vec3 tuple) {\n    const mat3 m = mat3(\n        0.41239079926595948 , 0.35758433938387796, 0.18048078840183429 ,\n        0.21263900587151036 , 0.71516867876775593, 0.072192315360733715,\n        0.019330818715591851, 0.11919477979462599, 0.95053215224966058\n    );\n    return hsluv_toLinear(tuple) * m;\n}\n\nvec3 xyzToLuv(vec3 tuple){\n    float X = tuple.x;\n    float Y = tuple.y;\n    float Z = tuple.z;\n\n    float L = hsluv_yToL(Y);\n    float div = 1. / max(dot(tuple, vec3(1, 15, 3)), 1e-15);\n\n    return vec3(\n        1.,\n        (52. * (X*div) - 2.57179),\n        (117.* (Y*div) - 6.08816)\n    ) * L;\n}\n\n\nvec3 luvToXyz(vec3 tuple) {\n    float L = tuple.x;\n\n    float U = divide(tuple.y, 13.0 * L) + 0.19783000664283681;\n    float V = divide(tuple.z, 13.0 * L) + 0.468319994938791;\n\n    float Y = hsluv_lToY(L);\n    float X = 2.25 * U * Y / V;\n    float Z = (3./V - 5.)*Y - (X/3.);\n\n    return vec3(X, Y, Z);\n}\n\nvec3 luvToLch(vec3 tuple) {\n    float L = tuple.x;\n    float U = tuple.y;\n    float V = tuple.z;\n\n    float C = length(tuple.yz);\n    float H = degrees(atan(V,U));\n    H += 360.0 * step(H, 0.0);\n\n    return vec3(L, C, H);\n}\n\nvec3 lchToLuv(vec3 tuple) {\n    float hrad = radians(tuple.b);\n    return vec3(\n        tuple.r,\n        cos(hrad) * tuple.g,\n        sin(hrad) * tuple.g\n    );\n}\n\nvec3 hsluvToLch(vec3 tuple) {\n    tuple.g *= hsluv_maxChromaForLH(tuple.b, tuple.r) * .01;\n    return tuple.bgr;\n}\n\nvec3 lchToHsluv(vec3 tuple) {\n    tuple.g = divide(tuple.g, hsluv_maxChromaForLH(tuple.r, tuple.b) * .01);\n    return tuple.bgr;\n}\n\nvec3 lchToRgb(vec3 tuple) {\n    return xyzToRgb(luvToXyz(lchToLuv(tuple)));\n}\n\nvec3 rgbToLch(vec3 tuple) {\n    return luvToLch(xyzToLuv(rgbToXyz(tuple)));\n}\n\nvec3 hsluvToRgb(vec3 tuple) {\n    return lchToRgb(hsluvToLch(tuple));\n}\n\nvec3 rgbToHsluv(vec3 tuple) {\n    return lchToHsluv(rgbToLch(tuple));\n}\n\nvec3 luvToRgb(vec3 tuple){\n    return xyzToRgb(luvToXyz(tuple));\n}\n"
  },
  {
    "path": "kitty/hyperlink.c",
    "content": "/*\n * hyperlink.c\n * Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"hyperlink.h\"\n#include \"lineops.h\"\n#include <string.h>\n\n#define MAX_KEY_LEN 2048\n#define MAX_ID_LEN 256\n\n#define NAME hyperlink_map\n#define KEY_TY const char*\n#define VAL_TY hyperlink_id_type\n#include \"kitty-verstable.h\"\n#define hyperlink_for_loop vt_create_for_loop(hyperlink_map_itr, itr, &pool->map)\n\ntypedef const char* hyperlink;\ntypedef struct HyperLinks {\n    hyperlink *items;\n    size_t count, capacity;\n} HyperLinks;\n\ntypedef struct {\n    HyperLinks array;\n    hyperlink_map map;\n    hyperlink_id_type adds_since_last_gc;\n} HyperLinkPool;\n\nstatic void\nfree_hyperlink_items(HyperLinks array) { for (size_t i = 1; i < array.count; i++) free((void*)array.items[i]); }\n\nstatic void\nclear_pool(HyperLinkPool *pool) {\n    if (pool->array.items) {\n        free_hyperlink_items(pool->array);\n        free(pool->array.items);\n    }\n    vt_cleanup(&pool->map);\n    zero_at_ptr(&(pool->array));\n    pool->adds_since_last_gc = 0;\n}\n\nHYPERLINK_POOL_HANDLE\nalloc_hyperlink_pool(void) {\n    HyperLinkPool *ans = calloc(1, sizeof(HyperLinkPool));\n    if (ans) vt_init(&ans->map);\n    return (HYPERLINK_POOL_HANDLE)ans;\n}\n\n\nvoid\nclear_hyperlink_pool(HYPERLINK_POOL_HANDLE h) {\n    if (h) clear_pool((HyperLinkPool*)h);\n}\n\n\nvoid\nfree_hyperlink_pool(HYPERLINK_POOL_HANDLE h) {\n    if (h) {\n        HyperLinkPool *pool = (HyperLinkPool*)h;\n        clear_pool(pool);\n        free(pool);\n    }\n}\n\nstatic const char*\ndupstr(const char *src, size_t len) {\n    char *ans = malloc(len+1);\n    if (!ans) fatal(\"Out of memory\");\n    memcpy(ans, src, len); ans[len] = 0;\n    return ans;\n}\n\nstatic void\nprocess_cell(HyperLinkPool *pool, hyperlink_id_type *map, HyperLinks clone, CPUCell *c) {\n    if (!c->hyperlink_id) return;\n    if (c->hyperlink_id >= clone.count) { c->hyperlink_id = 0; return; }\n    hyperlink_id_type new_id = map[c->hyperlink_id];\n    if (!new_id) {\n        new_id = pool->array.count++;\n        map[c->hyperlink_id] = new_id;\n        pool->array.items[new_id] = clone.items[c->hyperlink_id]; clone.items[c->hyperlink_id] = NULL;\n        if (vt_is_end(vt_insert(&pool->map, pool->array.items[new_id], new_id))) fatal(\"Out of memory\");\n    }\n    c->hyperlink_id = new_id;\n}\n\nstatic void\nremap_hyperlink_ids(Screen *self, bool preserve_hyperlinks_in_history, hyperlink_id_type *map, HyperLinks clone) {\n    HyperLinkPool *pool = (HyperLinkPool*)self->hyperlink_pool;\n    if (self->historybuf->count && preserve_hyperlinks_in_history) {\n        for (index_type y = self->historybuf->count; y-- > 0;) {\n            CPUCell *cells = historybuf_cpu_cells(self->historybuf, y);\n            for (index_type x = 0; x < self->historybuf->xnum; x++) process_cell(pool, map, clone, cells + x);\n        }\n    }\n    LineBuf *second = self->linebuf, *first = second == self->main_linebuf ? self->alt_linebuf : self->main_linebuf;\n    for (index_type i = 0; i < self->lines * self->columns; i++) process_cell(pool, map, clone, first->cpu_cell_buf + i);\n    for (index_type i = 0; i < self->lines * self->columns; i++) process_cell(pool, map, clone, second->cpu_cell_buf + i);\n}\n\nstatic void\n_screen_garbage_collect_hyperlink_pool(Screen *screen, bool preserve_hyperlinks_in_history) {\n    HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;\n    if (!pool->array.count) return;\n    pool->adds_since_last_gc = 0;\n    RAII_ALLOC(hyperlink_id_type, map, calloc(pool->array.count, sizeof(hyperlink_id_type)));\n    RAII_ALLOC(void, buf, malloc(pool->array.count * sizeof(pool->array.items[0])));\n    if (!map || !buf) fatal(\"Out of memory\");\n    HyperLinks clone = {.capacity=pool->array.count, .count=pool->array.count, .items=buf};\n    memcpy(buf, pool->array.items, pool->array.count * sizeof(pool->array.items[0]));\n    vt_cleanup(&pool->map);\n    pool->array.count = 1;  // First id must be 1\n    remap_hyperlink_ids(screen, preserve_hyperlinks_in_history, map, clone);\n    free_hyperlink_items(clone);\n}\n\nvoid\nscreen_garbage_collect_hyperlink_pool(Screen *screen) { _screen_garbage_collect_hyperlink_pool(screen, true); }\n\n\nhyperlink_id_type\nget_id_for_hyperlink(Screen *screen, const char *id, const char *url) {\n    if (!url) return 0;\n    HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;\n    static char key[MAX_KEY_LEN] = {0};\n    int keylen = snprintf(key, MAX_KEY_LEN-1, \"%.*s:%s\", MAX_ID_LEN, id ? id : \"\", url);\n    if (keylen < 0) keylen = strlen(key);\n    else keylen = MIN(keylen, MAX_KEY_LEN - 2);  // snprintf returns how many chars it would have written in case of truncation\n    key[keylen] = 0;\n    hyperlink_map_itr itr = vt_get(&pool->map, key);\n    if (!vt_is_end(itr)) return itr.data->val;\n    if (pool->array.count >= HYPERLINK_MAX_NUMBER-1) {\n        screen_garbage_collect_hyperlink_pool(screen);\n        if (pool->array.count >= HYPERLINK_MAX_NUMBER - 128) {\n            log_error(\"Too many hyperlinks, discarding hyperlinks in scrollback\");\n            _screen_garbage_collect_hyperlink_pool(screen, false);\n            if (pool->array.count >= HYPERLINK_MAX_NUMBER) {\n                log_error(\"Too many hyperlinks, discarding hyperlink: %s\", key);\n                return 0;\n            }\n        }\n    }\n    if (!pool->array.count) pool->array.count = 1;  // First id must be 1\n    ensure_space_for(&(pool->array), items, hyperlink, pool->array.count + 1, capacity, 256, false);\n    hyperlink_id_type new_id = pool->array.count++;\n    pool->array.items[new_id] = dupstr(key, keylen);\n    if (vt_is_end(vt_insert(&pool->map, pool->array.items[new_id], new_id))) fatal(\"Out of memory\");\n    // If there have been a lot of hyperlink adds do a garbage collect so as\n    // not to leak too much memory over unused hyperlinks\n    if (++pool->adds_since_last_gc > 8192) screen_garbage_collect_hyperlink_pool(screen);\n    return new_id;\n}\n\nconst char*\nget_hyperlink_for_id(const HYPERLINK_POOL_HANDLE handle, hyperlink_id_type id, bool only_url) {\n    HyperLinkPool *pool = (HyperLinkPool*)handle;\n    if (id >= pool->array.count) return NULL;\n    return only_url ? strstr(pool->array.items[id], \":\") + 1 : pool->array.items[id];\n}\n\nPyObject*\nscreen_hyperlinks_as_set(Screen *screen) {\n    HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;\n    RAII_PyObject(ans, PySet_New(0));\n    if (ans) {\n        hyperlink_for_loop {\n            RAII_PyObject(e, Py_BuildValue(\"sH\", itr.data->key, itr.data->val));\n            if (!e || PySet_Add(ans, e) != 0) return NULL;\n        }\n    }\n    Py_XINCREF(ans); return ans;\n}\n"
  },
  {
    "path": "kitty/hyperlink.h",
    "content": "/*\n * Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"screen.h\"\n\nHYPERLINK_POOL_HANDLE alloc_hyperlink_pool(void);\nvoid free_hyperlink_pool(HYPERLINK_POOL_HANDLE);\nvoid clear_hyperlink_pool(HYPERLINK_POOL_HANDLE);\nhyperlink_id_type get_id_for_hyperlink(Screen*, const char*, const char*);\nPyObject* screen_hyperlinks_as_set(Screen *screen);\nvoid screen_garbage_collect_hyperlink_pool(Screen *screen);\n"
  },
  {
    "path": "kitty/iqsort.h",
    "content": "/* $Id: qsort.h,v 1.5 2008-01-28 18:16:49 mjt Exp $\n * Adopted from GNU glibc by Mjt.\n * See stdlib/qsort.c in glibc */\n\n/* Copyright (C) 1991, 1992, 1996, 1997, 1999 Free Software Foundation, Inc.\n   This file is part of the GNU C Library.\n   Written by Douglas C. Schmidt (schmidt@ics.uci.edu).\n\n   The GNU C Library is free software; you can redistribute it and/or\n   modify it under the terms of the GNU Lesser General Public\n   License as published by the Free Software Foundation; either\n   version 2.1 of the License, or (at your option) any later version.\n\n   The GNU C Library is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n   Lesser General Public License for more details.\n\n   You should have received a copy of the GNU Lesser General Public\n   License along with the GNU C Library; if not, write to the Free\n   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n   02111-1307 USA.  */\n\n/* in-line qsort implementation.  Differs from traditional qsort() routine\n * in that it is a macro, not a function, and instead of passing an address\n * of a comparison routine to the function, it is possible to inline\n * comparison routine, thus speeding up sorting a lot.\n *\n * Usage:\n *  #include \"iqsort.h\"\n *  #define islt(a,b) (strcmp((*a),(*b))<0)\n *  char *arr[];\n *  int n;\n *  QSORT(char*, arr, n, islt);\n *\n * The \"prototype\" and 4 arguments are:\n *  QSORT(TYPE,BASE,NELT,ISLT)\n *  1) type of each element, TYPE,\n *  2) address of the beginning of the array, of type TYPE*,\n *  3) number of elements in the array, and\n *  4) comparison routine.\n * Array pointer and number of elements are referenced only once.\n * This is similar to a call\n *  qsort(BASE,NELT,sizeof(TYPE),ISLT)\n * with the difference in last parameter.\n * Note the islt macro/routine (it receives pointers to two elements):\n * the only condition of interest is whenever one element is less than\n * another, no other conditions (greater than, equal to etc) are tested.\n * So, for example, to define integer sort, use:\n *  #define islt(a,b) ((*a)<(*b))\n *  QSORT(int, arr, n, islt)\n *\n * The macro could be used to implement a sorting function (see examples\n * below), or to implement the sorting algorithm inline.  That is, either\n * create a sorting function and use it whenever you want to sort something,\n * or use QSORT() macro directly instead a call to such routine.  Note that\n * the macro expands to quite some code (compiled size of int qsort on x86\n * is about 700..800 bytes).\n *\n * Using this macro directly it isn't possible to implement traditional\n * qsort() routine, because the macro assumes sizeof(element) == sizeof(TYPE),\n * while qsort() allows element size to be different.\n *\n * Several ready-to-use examples:\n *\n * Sorting array of integers:\n * void int_qsort(int *arr, unsigned n) {\n * #define int_lt(a,b) ((*a)<(*b))\n *   QSORT(int, arr, n, int_lt);\n * }\n *\n * Sorting array of string pointers:\n * void str_qsort(char *arr[], unsigned n) {\n * #define str_lt(a,b) (strcmp((*a),(*b)) < 0)\n *   QSORT(char*, arr, n, str_lt);\n * }\n *\n * Sorting array of structures:\n *\n * struct elt {\n *   int key;\n *   ...\n * };\n * void elt_qsort(struct elt *arr, unsigned n) {\n * #define elt_lt(a,b) ((a)->key < (b)->key)\n *  QSORT(struct elt, arr, n, elt_lt);\n * }\n *\n * And so on.\n */\n\n/* Swap two items pointed to by A and B using temporary buffer t. */\n#define _QSORT_SWAP(a, b, t) ((void)((t = *a), (*a = *b), (*b = t)))\n\n/* Discontinue quicksort algorithm when partition gets below this size.\n   This particular magic number was chosen to work best on a Sun 4/260. */\n#define _QSORT_MAX_THRESH 4\n\n/* Stack node declarations used to store unfulfilled partition obligations\n * (inlined in QSORT).\ntypedef struct {\n  QSORT_TYPE *_lo, *_hi;\n} qsort_stack_node;\n */\n\n/* The next 4 #defines implement a very fast in-line stack abstraction. */\n/* The stack needs log (total_elements) entries (we could even subtract\n   log(MAX_THRESH)).  Since total_elements has type unsigned, we get as\n   upper bound for log (total_elements):\n   bits per byte (CHAR_BIT) * sizeof(unsigned).  */\n#define _QSORT_STACK_SIZE\t(8 * sizeof(unsigned))\n#define _QSORT_PUSH(top, low, high)\t\\\n\t(((top->_lo = (low)), (top->_hi = (high)), ++top))\n#define\t_QSORT_POP(low, high, top)\t\\\n\t((--top, (low = top->_lo), (high = top->_hi)))\n#define\t_QSORT_STACK_NOT_EMPTY\t(_stack < _top)\n\n\n/* Order size using quicksort.  This implementation incorporates\n   four optimizations discussed in Sedgewick:\n\n   1. Non-recursive, using an explicit stack of pointer that store the\n      next array partition to sort.  To save time, this maximum amount\n      of space required to store an array of SIZE_MAX is allocated on the\n      stack.  Assuming a 32-bit (64 bit) integer for size_t, this needs\n      only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).\n      Pretty cheap, actually.\n\n   2. Chose the pivot element using a median-of-three decision tree.\n      This reduces the probability of selecting a bad pivot value and\n      eliminates certain extraneous comparisons.\n\n   3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving\n      insertion sort to order the MAX_THRESH items within each partition.\n      This is a big win, since insertion sort is faster for small, mostly\n      sorted array segments.\n\n   4. The larger of the two sub-partitions is always pushed onto the\n      stack first, with the algorithm then concentrating on the\n      smaller partition.  This *guarantees* no more than log (total_elems)\n      stack size is needed (actually O(1) in this case)!  */\n\n/* The main code starts here... */\n#define QSORT(QSORT_TYPE,QSORT_BASE,QSORT_NELT,QSORT_LT)\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\n  QSORT_TYPE *const _base = (QSORT_BASE);\t\t\t\t\\\n  const unsigned _elems = (QSORT_NELT);\t\t\t\t\t\\\n  QSORT_TYPE _hold;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n  /* Don't declare two variables of type QSORT_TYPE in a single\t\t\\\n   * statement: eg `TYPE a, b;', in case if TYPE is a pointer,\t\t\\\n   * expands to `type* a, b;' which isn't what we want.\t\t\t\\\n   */\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n  if (_elems > _QSORT_MAX_THRESH) {\t\t\t\t\t\\\n    QSORT_TYPE *_lo = _base;\t\t\t\t\t\t\\\n    QSORT_TYPE *_hi = _lo + _elems - 1;\t\t\t\t\t\\\n    struct {\t\t\t\t\t\t\t\t\\\n      QSORT_TYPE *_hi; QSORT_TYPE *_lo;\t\t\t\t\t\\\n    } _stack[_QSORT_STACK_SIZE], *_top = _stack + 1;\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n    while (_QSORT_STACK_NOT_EMPTY) {\t\t\t\t\t\\\n      QSORT_TYPE *_left_ptr; QSORT_TYPE *_right_ptr;\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n      /* Select median value from among LO, MID, and HI. Rearrange\t\\\n         LO and HI so the three values are sorted. This lowers the\t\\\n         probability of picking a pathological pivot value and\t\t\\\n         skips a comparison for both the LEFT_PTR and RIGHT_PTR in\t\\\n         the while loops. */\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n      QSORT_TYPE *_mid = _lo + ((_hi - _lo) >> 1);\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n      if (QSORT_LT (_mid, _lo))\t\t\t\t\t\t\\\n        _QSORT_SWAP (_mid, _lo, _hold);\t\t\t\t\t\\\n      if (QSORT_LT (_hi, _mid))\t{\t\t\t\t\t\\\n        _QSORT_SWAP (_mid, _hi, _hold);\t\t\t\t\t\\\n        if (QSORT_LT (_mid, _lo))\t\t\t\t\t\\\n          _QSORT_SWAP (_mid, _lo, _hold);\t\t\t\t\\\n      } \t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n      _left_ptr  = _lo + 1;\t\t\t\t\t\t\\\n      _right_ptr = _hi - 1;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n      /* Here's the famous ``collapse the walls'' section of quicksort.\t\\\n         Gotta like those tight inner loops!  They are the main reason\t\\\n         that this algorithm runs much faster than others. */\t\t\\\n      do {\t\t\t\t\t\t\t\t\\\n        while (QSORT_LT (_left_ptr, _mid))\t\t\t\t\\\n         ++_left_ptr;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n        while (QSORT_LT (_mid, _right_ptr))\t\t\t\t\\\n          --_right_ptr;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n        if (_left_ptr < _right_ptr) {\t\t\t\t\t\\\n          _QSORT_SWAP (_left_ptr, _right_ptr, _hold);\t\t\t\\\n          if (_mid == _left_ptr)\t\t\t\t\t\\\n            _mid = _right_ptr;\t\t\t\t\t\t\\\n          else if (_mid == _right_ptr)\t\t\t\t\t\\\n            _mid = _left_ptr;\t\t\t\t\t\t\\\n          ++_left_ptr;\t\t\t\t\t\t\t\\\n          --_right_ptr;\t\t\t\t\t\t\t\\\n        }\t\t\t\t\t\t\t\t\\\n        else if (_left_ptr == _right_ptr) {\t\t\t\t\\\n          ++_left_ptr;\t\t\t\t\t\t\t\\\n          --_right_ptr;\t\t\t\t\t\t\t\\\n          break;\t\t\t\t\t\t\t\\\n        }\t\t\t\t\t\t\t\t\\\n      } while (_left_ptr <= _right_ptr);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n     /* Set up pointers for next iteration.  First determine whether\t\\\n        left and right partitions are below the threshold size.  If so,\t\\\n        ignore one or both.  Otherwise, push the larger partition's\t\\\n        bounds on the stack and continue sorting the smaller one. */\t\\\n\t\t\t\t\t\t\t\t\t\\\n      if (_right_ptr - _lo <= _QSORT_MAX_THRESH) {\t\t\t\\\n        if (_hi - _left_ptr <= _QSORT_MAX_THRESH)\t\t\t\\\n          /* Ignore both small partitions. */\t\t\t\t\\\n          _QSORT_POP (_lo, _hi, _top);\t\t\t\t\t\\\n        else\t\t\t\t\t\t\t\t\\\n          /* Ignore small left partition. */\t\t\t\t\\\n          _lo = _left_ptr;\t\t\t\t\t\t\\\n      }\t\t\t\t\t\t\t\t\t\\\n      else if (_hi - _left_ptr <= _QSORT_MAX_THRESH)\t\t\t\\\n        /* Ignore small right partition. */\t\t\t\t\\\n        _hi = _right_ptr;\t\t\t\t\t\t\\\n      else if (_right_ptr - _lo > _hi - _left_ptr) {\t\t\t\\\n        /* Push larger left partition indices. */\t\t\t\\\n        _QSORT_PUSH (_top, _lo, _right_ptr);\t\t\t\t\\\n        _lo = _left_ptr;\t\t\t\t\t\t\\\n      }\t\t\t\t\t\t\t\t\t\\\n      else {\t\t\t\t\t\t\t\t\\\n        /* Push larger right partition indices. */\t\t\t\\\n        _QSORT_PUSH (_top, _left_ptr, _hi);\t\t\t\t\\\n        _hi = _right_ptr;\t\t\t\t\t\t\\\n      }\t\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n  }\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n  /* Once the BASE array is partially sorted by quicksort the rest\t\\\n     is completely sorted using insertion sort, since this is efficient\t\\\n     for partitions below MAX_THRESH size. BASE points to the\t\t\\\n     beginning of the array to sort, and END_PTR points at the very\t\\\n     last element in the array (*not* one beyond it!). */\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n  {\t\t\t\t\t\t\t\t\t\\\n    QSORT_TYPE *const _end_ptr = _base + _elems - 1;\t\t\t\\\n    QSORT_TYPE *_tmp_ptr = _base;\t\t\t\t\t\\\n    register QSORT_TYPE *_run_ptr;\t\t\t\t\t\\\n    QSORT_TYPE *_thresh;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n    _thresh = _base + _QSORT_MAX_THRESH;\t\t\t\t\\\n    if (_thresh > _end_ptr)\t\t\t\t\t\t\\\n      _thresh = _end_ptr;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n    /* Find smallest element in first threshold and place it at the\t\\\n       array's beginning.  This is the smallest array element,\t\t\\\n       and the operation speeds up insertion sort's inner loop. */\t\\\n\t\t\t\t\t\t\t\t\t\\\n    for (_run_ptr = _tmp_ptr + 1; _run_ptr <= _thresh; ++_run_ptr)\t\\\n      if (QSORT_LT (_run_ptr, _tmp_ptr))\t\t\t\t\\\n        _tmp_ptr = _run_ptr;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n    if (_tmp_ptr != _base)\t\t\t\t\t\t\\\n      _QSORT_SWAP (_tmp_ptr, _base, _hold);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n    /* Insertion sort, running from left-hand-side\t\t\t\\\n     * up to right-hand-side.  */\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n    _run_ptr = _base + 1;\t\t\t\t\t\t\\\n    while (++_run_ptr <= _end_ptr) {\t\t\t\t\t\\\n      _tmp_ptr = _run_ptr - 1;\t\t\t\t\t\t\\\n      while (QSORT_LT (_run_ptr, _tmp_ptr))\t\t\t\t\\\n        --_tmp_ptr;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n      ++_tmp_ptr;\t\t\t\t\t\t\t\\\n      if (_tmp_ptr != _run_ptr) {\t\t\t\t\t\\\n        QSORT_TYPE *_trav = _run_ptr + 1;\t\t\t\t\\\n        while (--_trav >= _run_ptr) {\t\t\t\t\t\\\n          QSORT_TYPE *_hi; QSORT_TYPE *_lo;\t\t\t\t\\\n          _hold = *_trav;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n          for (_hi = _lo = _trav; --_lo >= _tmp_ptr; _hi = _lo)\t\t\\\n            *_hi = *_lo;\t\t\t\t\t\t\\\n          *_hi = _hold;\t\t\t\t\t\t\t\\\n        }\t\t\t\t\t\t\t\t\\\n      }\t\t\t\t\t\t\t\t\t\\\n    }\t\t\t\t\t\t\t\t\t\\\n  }\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n}\n"
  },
  {
    "path": "kitty/key_encoding.c",
    "content": "/*\n * key_encoding.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"keys.h\"\n#include \"charsets.h\"\n\ntypedef enum { SHIFT=1, ALT=2, CTRL=4, SUPER=8, HYPER=16, META=32, CAPS_LOCK=64, NUM_LOCK=128} ModifierMasks;\ntypedef enum { PRESS = 0, REPEAT = 1, RELEASE = 2} KeyAction;\n#define LOCK_MASK (CAPS_LOCK | NUM_LOCK)\ntypedef struct {\n    uint32_t key, shifted_key, alternate_key;\n    struct {\n        bool shift, alt, ctrl, super, hyper, meta, numlock, capslock;\n        unsigned value;\n        char encoded[4];\n    } mods;\n    KeyAction action;\n    bool cursor_key_mode, disambiguate, report_all_event_types, report_alternate_key, report_text, embed_text;\n    const char *text;\n    bool has_text;\n} KeyEvent;\n\ntypedef struct {\n    uint32_t key, shifted_key, alternate_key;\n    bool add_alternates, has_mods, add_actions, add_text;\n    char encoded_mods[4];\n    const char *text;\n    KeyAction action;\n} EncodingData;\n\nstatic void\nconvert_glfw_mods(int mods, KeyEvent *ev, const unsigned key_encoding_flags) {\n    if (!key_encoding_flags) mods &= ~GLFW_LOCK_MASK;\n    ev->mods.alt = (mods & GLFW_MOD_ALT) > 0, ev->mods.ctrl = (mods & GLFW_MOD_CONTROL) > 0, ev->mods.shift = (mods & GLFW_MOD_SHIFT) > 0, ev->mods.super = (mods & GLFW_MOD_SUPER) > 0, ev->mods.hyper = (mods & GLFW_MOD_HYPER) > 0, ev->mods.meta = (mods & GLFW_MOD_META) > 0;\n    ev->mods.numlock = (mods & GLFW_MOD_NUM_LOCK) > 0, ev->mods.capslock = (mods & GLFW_MOD_CAPS_LOCK) > 0;\n    ev->mods.value = ev->mods.shift ? SHIFT : 0;\n    if (ev->mods.alt) ev->mods.value |= ALT;\n    if (ev->mods.ctrl) ev->mods.value |= CTRL;\n    if (ev->mods.super) ev->mods.value |= SUPER;\n    if (ev->mods.hyper) ev->mods.value |= HYPER;\n    if (ev->mods.meta) ev->mods.value |= META;\n    if (ev->mods.capslock) ev->mods.value |= CAPS_LOCK;\n    if (ev->mods.numlock) ev->mods.value |= NUM_LOCK;\n    snprintf(ev->mods.encoded, sizeof(ev->mods.encoded), \"%u\", ev->mods.value + 1);\n}\n\n\nstatic void\ninit_encoding_data(EncodingData *ans, const KeyEvent *ev) {\n    ans->add_actions = ev->report_all_event_types && ev->action != PRESS;\n    ans->has_mods = ev->mods.encoded[0] && ( ev->mods.encoded[0] != '1' || ev->mods.encoded[1] );\n    ans->add_alternates = ev->report_alternate_key && ((ev->shifted_key > 0 && ev->mods.shift) || ev->alternate_key > 0);\n    if (ans->add_alternates) { if (ev->mods.shift) ans->shifted_key = ev->shifted_key; ans->alternate_key = ev->alternate_key; }\n    ans->action = ev->action;\n    ans->key = ev->key;\n    ans->add_text = ev->embed_text && ev->text && ev->text[0];\n    ans->text = ev->text;\n    memcpy(ans->encoded_mods, ev->mods.encoded, sizeof(ans->encoded_mods));\n}\n\nstatic int\nserialize(const EncodingData *data, char *output, const char csi_trailer) {\n    int pos = 0;\n    bool second_field_not_empty = data->has_mods || data->add_actions;\n    bool third_field_not_empty = data->add_text;\n#define P(fmt, ...) pos += snprintf(output + pos, KEY_BUFFER_SIZE - 2 <= pos ? 0 : KEY_BUFFER_SIZE - 2 - pos, fmt, __VA_ARGS__)\n    P(\"\\x1b%s\", \"[\");\n    if (data->key != 1 || data->add_alternates || second_field_not_empty || third_field_not_empty) P(\"%u\", data->key);\n    if (data->add_alternates) {\n        P(\"%s\", \":\");\n        if (data->shifted_key) P(\"%u\", data->shifted_key);\n        if (data->alternate_key) P(\":%u\", data->alternate_key);\n    }\n    if (second_field_not_empty || third_field_not_empty) {\n        P(\"%s\", \";\");\n        if (second_field_not_empty) P(\"%s\", data->encoded_mods);\n        if (data->add_actions) P(\":%u\", data->action + 1);\n    }\n    if (third_field_not_empty) {\n        const char *p = data->text;\n        uint32_t codep; UTF8State state = UTF8_ACCEPT;\n        bool first = true;\n        while(*p) {\n            if (decode_utf8(&state, &codep, *p) == UTF8_ACCEPT) {\n                if (first) { P(\";%u\", codep); first = false; }\n                else P(\":%u\", codep);\n            }\n            p++;\n        }\n    }\n#undef P\n    output[pos++] = csi_trailer;\n    output[pos] = 0;\n    return pos;\n}\n\nstatic uint32_t\nconvert_kp_key_to_normal_key(uint32_t key_number) {\n    switch(key_number) {\n#define S(x) case GLFW_FKEY_KP_##x: key_number = GLFW_FKEY_##x; break;\n        S(ENTER) S(HOME) S(END) S(INSERT) S(DELETE) S(PAGE_UP) S(PAGE_DOWN)\n        S(UP) S(DOWN) S(LEFT) S(RIGHT)\n#undef S\n        case GLFW_FKEY_KP_0:\n        case GLFW_FKEY_KP_9: key_number = '0' + (key_number - GLFW_FKEY_KP_0); break;\n        case GLFW_FKEY_KP_DECIMAL: key_number = '.'; break;\n        case GLFW_FKEY_KP_DIVIDE: key_number = '/'; break;\n        case GLFW_FKEY_KP_MULTIPLY: key_number = '*'; break;\n        case GLFW_FKEY_KP_SUBTRACT: key_number = '-'; break;\n        case GLFW_FKEY_KP_ADD: key_number = '+'; break;\n        case GLFW_FKEY_KP_EQUAL: key_number = '='; break;\n    }\n    return key_number;\n}\n\nstatic int\nlegacy_functional_key_encoding_with_modifiers(uint32_t key_number, const KeyEvent *ev, char *output) {\n    const char *prefix = ev->mods.value & ALT ? \"\\x1b\" : \"\";\n    const char *main_bytes = \"\";\n    switch (key_number) {\n        case GLFW_FKEY_ENTER:\n            main_bytes = \"\\x0d\";\n            break;\n        case GLFW_FKEY_ESCAPE:\n            main_bytes = \"\\x1b\";\n            break;\n        case GLFW_FKEY_BACKSPACE:\n            main_bytes = ev->mods.value & CTRL ? \"\\x08\" : \"\\x7f\";\n            break;\n        case GLFW_FKEY_TAB:\n            if (ev->mods.value & SHIFT) {\n                prefix = ev->mods.value & ALT ? \"\\x1b\\x1b\" : \"\\x1b\";\n                main_bytes = \"[Z\";\n            } else {\n                main_bytes = \"\\t\";\n            }\n            break;\n        default:\n            return -1;\n    }\n    return snprintf(output, KEY_BUFFER_SIZE, \"%s%s\", prefix, main_bytes);\n}\n\nstatic int\nencode_function_key(const KeyEvent *ev, char *output) {\n#define SIMPLE(val) return snprintf(output, KEY_BUFFER_SIZE, \"%s\", val);\n    char csi_trailer = 'u';\n    uint32_t key_number = ev->key;\n    bool legacy_mode = !ev->report_all_event_types && !ev->disambiguate && !ev->report_text;\n\n    if (ev->cursor_key_mode && legacy_mode && !ev->mods.value) {\n        switch(key_number) {\n            case GLFW_FKEY_UP: SIMPLE(\"\\x1bOA\");\n            case GLFW_FKEY_DOWN: SIMPLE(\"\\x1bOB\");\n            case GLFW_FKEY_RIGHT: SIMPLE(\"\\x1bOC\");\n            case GLFW_FKEY_LEFT: SIMPLE(\"\\x1bOD\");\n            case GLFW_FKEY_KP_BEGIN: SIMPLE(\"\\x1bOE\");\n            case GLFW_FKEY_END: SIMPLE(\"\\x1bOF\");\n            case GLFW_FKEY_HOME: SIMPLE(\"\\x1bOH\");\n            default: break;\n        }\n    }\n    if (!ev->mods.value) {\n        if (!ev->disambiguate && !ev->report_text && key_number == GLFW_FKEY_ESCAPE) SIMPLE(\"\\x1b\");\n        if (legacy_mode) {\n            switch(key_number) {\n                case GLFW_FKEY_F1: SIMPLE(\"\\x1bOP\");\n                case GLFW_FKEY_F2: SIMPLE(\"\\x1bOQ\");\n                case GLFW_FKEY_F3: SIMPLE(\"\\x1bOR\");\n                case GLFW_FKEY_F4: SIMPLE(\"\\x1bOS\");\n                default: break;\n            }\n        }\n        if (!ev->report_text) {\n            switch(key_number) {\n                case GLFW_FKEY_ENTER: if (ev->action == RELEASE) return -1; SIMPLE(\"\\r\");\n                case GLFW_FKEY_BACKSPACE: if (ev->action == RELEASE) return -1; SIMPLE(\"\\x7f\");\n                case GLFW_FKEY_TAB: if (ev->action == RELEASE) return -1; SIMPLE(\"\\t\");\n                default: break;\n            }\n        }\n    } else if (legacy_mode) {\n        int num = legacy_functional_key_encoding_with_modifiers(key_number, ev, output);\n        if (num > -1) return num;\n    }\n    if (!(ev->mods.value & ~LOCK_MASK) && !ev->report_text) {\n        switch(key_number) {\n            case GLFW_FKEY_ENTER: if (ev->action == RELEASE) return -1; SIMPLE(\"\\r\");\n            case GLFW_FKEY_BACKSPACE: if (ev->action == RELEASE) return -1; SIMPLE(\"\\x7f\");\n            case GLFW_FKEY_TAB: if (ev->action == RELEASE) return -1; SIMPLE(\"\\t\");\n            default: break;\n        }\n    }\n#undef SIMPLE\n#define S(number, trailer) key_number = number; csi_trailer = trailer; break\n    switch(key_number) {\n        /* start special numbers (auto generated by gen-key-constants.py do not edit) */\n        case GLFW_FKEY_ESCAPE: S(27, 'u');\n        case GLFW_FKEY_ENTER: S(13, 'u');\n        case GLFW_FKEY_TAB: S(9, 'u');\n        case GLFW_FKEY_BACKSPACE: S(127, 'u');\n        case GLFW_FKEY_INSERT: S(2, '~');\n        case GLFW_FKEY_DELETE: S(3, '~');\n        case GLFW_FKEY_LEFT: S(1, 'D');\n        case GLFW_FKEY_RIGHT: S(1, 'C');\n        case GLFW_FKEY_UP: S(1, 'A');\n        case GLFW_FKEY_DOWN: S(1, 'B');\n        case GLFW_FKEY_PAGE_UP: S(5, '~');\n        case GLFW_FKEY_PAGE_DOWN: S(6, '~');\n        case GLFW_FKEY_HOME: S(1, 'H');\n        case GLFW_FKEY_END: S(1, 'F');\n        case GLFW_FKEY_F1: S(1, 'P');\n        case GLFW_FKEY_F2: S(1, 'Q');\n        case GLFW_FKEY_F3: S(13, '~');\n        case GLFW_FKEY_F4: S(1, 'S');\n        case GLFW_FKEY_F5: S(15, '~');\n        case GLFW_FKEY_F6: S(17, '~');\n        case GLFW_FKEY_F7: S(18, '~');\n        case GLFW_FKEY_F8: S(19, '~');\n        case GLFW_FKEY_F9: S(20, '~');\n        case GLFW_FKEY_F10: S(21, '~');\n        case GLFW_FKEY_F11: S(23, '~');\n        case GLFW_FKEY_F12: S(24, '~');\n        case GLFW_FKEY_KP_BEGIN: S(1, 'E');\n/* end special numbers */\n        case GLFW_FKEY_MENU:\n            // use the same encoding as xterm for this key in legacy mode (F16)\n            if (legacy_mode) { S(29, '~'); }\n            break;\n        default: break;\n    }\n#undef S\n    EncodingData ed = {0};\n    init_encoding_data(&ed, ev);\n    ed.key = key_number;\n    ed.add_alternates = false;\n    return serialize(&ed, output, csi_trailer);\n}\n\nstatic char\nctrled_key(const char key) { // {{{\n    switch(key) {\n        /* start ctrl mapping (auto generated by gen-key-constants.py do not edit) */\n        case ' ': return 0;\n        case '/': return 31;\n        case '0': return 48;\n        case '1': return 49;\n        case '2': return 0;\n        case '3': return 27;\n        case '4': return 28;\n        case '5': return 29;\n        case '6': return 30;\n        case '7': return 31;\n        case '8': return 127;\n        case '9': return 57;\n        case '?': return 127;\n        case '@': return 0;\n        case '[': return 27;\n        case '\\\\': return 28;\n        case ']': return 29;\n        case '^': return 30;\n        case '_': return 31;\n        case 'a': return 1;\n        case 'b': return 2;\n        case 'c': return 3;\n        case 'd': return 4;\n        case 'e': return 5;\n        case 'f': return 6;\n        case 'g': return 7;\n        case 'h': return 8;\n        case 'i': return 9;\n        case 'j': return 10;\n        case 'k': return 11;\n        case 'l': return 12;\n        case 'm': return 13;\n        case 'n': return 14;\n        case 'o': return 15;\n        case 'p': return 16;\n        case 'q': return 17;\n        case 'r': return 18;\n        case 's': return 19;\n        case 't': return 20;\n        case 'u': return 21;\n        case 'v': return 22;\n        case 'w': return 23;\n        case 'x': return 24;\n        case 'y': return 25;\n        case 'z': return 26;\n        case '~': return 30;\n/* end ctrl mapping */\n        default:\n            return key;\n    }\n} // }}}\n\nstatic int\nencode_printable_ascii_key_legacy(const KeyEvent *ev, char *output) {\n    unsigned mods = ev->mods.value;\n    if (!mods) return snprintf(output, KEY_BUFFER_SIZE, \"%c\", (char)ev->key);\n\n    char key = ev->key;\n    if (mods & SHIFT) {\n        const char shifted = ev->shifted_key;\n        if (shifted && shifted != key && (!(mods & CTRL) || key < 'a' || key > 'z')) {\n            key = shifted;\n            mods &= ~SHIFT;\n        }\n    }\n\n    if (ev->mods.value == SHIFT)\n        return snprintf(output, KEY_BUFFER_SIZE, \"%c\", key);\n    if (mods == ALT)\n        return snprintf(output, KEY_BUFFER_SIZE, \"\\x1b%c\", key);\n    if (mods == CTRL)\n        return snprintf(output, KEY_BUFFER_SIZE, \"%c\", ctrled_key(key));\n    if (mods == (CTRL | ALT))\n        return snprintf(output, KEY_BUFFER_SIZE, \"\\x1b%c\", ctrled_key(key));\n    if (key == ' ') {\n        if (mods == (CTRL | SHIFT)) return snprintf(output, KEY_BUFFER_SIZE, \"%c\", ctrled_key(key));\n        if (mods == (ALT  | SHIFT)) return snprintf(output, KEY_BUFFER_SIZE, \"\\x1b%c\", key);\n    }\n    return 0;\n}\n\nstatic bool\nis_legacy_ascii_key(uint32_t key) {\n    START_ALLOW_CASE_RANGE\n    switch (key) {\n        case 'a' ... 'z':\n        case '0' ... '9':\n        case '!':\n        case '@':\n        case '#':\n        case '$':\n        case '%':\n        case '^':\n        case '&':\n        case '*':\n        case '(':\n        case ')':\n        case '`':\n        case '~':\n        case '-':\n        case '_':\n        case '=':\n        case '+':\n        case '[':\n        case '{':\n        case ']':\n        case '}':\n        case '\\\\':\n        case '|':\n        case ';':\n        case ':':\n        case '\\'':\n        case '\"':\n        case ',':\n        case '<':\n        case '.':\n        case '>':\n        case '/':\n        case '?':\n        case ' ':\n            return true;\n        default:\n            return false;\n    }\n    END_ALLOW_CASE_RANGE\n}\n\nstatic int\nencode_key(const KeyEvent *ev, char *output) {\n    if (!ev->report_all_event_types && ev->action == RELEASE) return 0;\n    if (GLFW_FKEY_FIRST <= ev->key && ev->key <= GLFW_FKEY_LAST) return encode_function_key(ev, output);\n    EncodingData ed = {0};\n    init_encoding_data(&ed, ev);\n    bool simple_encoding_ok = !ed.add_actions && !ed.add_alternates && !ed.add_text;\n\n    if (simple_encoding_ok) {\n        if (!ed.has_mods) {\n            if (ev->report_text) return serialize(&ed, output, 'u');\n            return encode_utf8(ev->key, output);\n        }\n        if (!ev->disambiguate && !ev->report_text) {\n            if (is_legacy_ascii_key(ev->key) || (ev->shifted_key && is_legacy_ascii_key(ev->shifted_key))) {\n                int ret = encode_printable_ascii_key_legacy(ev, output);\n                if (ret > 0) return ret;\n            }\n            unsigned mods = ev->mods.value;\n            if ((mods == CTRL || mods == ALT || mods == (CTRL | ALT)) && ev->alternate_key && !is_legacy_ascii_key(ev->key) && is_legacy_ascii_key(ev->alternate_key)) {\n                KeyEvent alternate = *ev;\n                alternate.key = ev->alternate_key;\n                alternate.alternate_key = 0;\n                alternate.shifted_key = 0;\n                int ret = encode_printable_ascii_key_legacy(&alternate, output);\n                if (ret > 0) return ret;\n            }\n        }\n    }\n\n    return serialize(&ed, output, 'u');\n}\n\nstatic bool\nstartswith_ascii_control_char(const char *p) {\n    if (!p || !*p) return true;\n    uint32_t codep; UTF8State state = UTF8_ACCEPT;\n    while(*p) {\n        if (decode_utf8(&state, &codep, *p) == UTF8_ACCEPT) {\n            return codep < 32 || codep == 127;\n        }\n        state = UTF8_ACCEPT;\n        p++;\n    }\n    return false;\n}\n\nint\nencode_glfw_key_event(const GLFWkeyevent *e, const bool cursor_key_mode, const unsigned key_encoding_flags, char *output) {\n    KeyEvent ev = {\n        .key = e->key, .shifted_key = e->shifted_key, .alternate_key = e->alternate_key,\n        .text = e->text,\n        .cursor_key_mode = cursor_key_mode,\n        .disambiguate = key_encoding_flags & 1,\n        .report_all_event_types = key_encoding_flags & 2,\n        .report_alternate_key = key_encoding_flags & 4,\n        .report_text = key_encoding_flags & 8,\n        .embed_text = key_encoding_flags & 16\n    };\n    if (!ev.report_text && is_modifier_key(e->key)) return 0;\n    ev.has_text = e->text && !startswith_ascii_control_char(e->text);\n    if (!ev.key && !ev.has_text) return 0;\n    bool send_text_standalone = !ev.report_text;\n    if (!ev.disambiguate && !ev.report_text && GLFW_FKEY_KP_0 <= ev.key && ev.key <= GLFW_FKEY_KP_BEGIN) {\n        ev.key = convert_kp_key_to_normal_key(ev.key);\n    }\n    switch (e->action) {\n        case GLFW_PRESS: ev.action = PRESS; break;\n        case GLFW_REPEAT: ev.action = REPEAT; break;\n        case GLFW_RELEASE: ev.action = RELEASE; break;\n    }\n    if (send_text_standalone && ev.has_text && (ev.action == PRESS || ev.action == REPEAT)) return SEND_TEXT_TO_CHILD;\n    convert_glfw_mods(e->mods, &ev, key_encoding_flags);\n    return encode_key(&ev, output);\n}\n"
  },
  {
    "path": "kitty/key_encoding.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom enum import IntEnum\nfrom functools import lru_cache\nfrom typing import NamedTuple\n\nfrom . import fast_data_types as defines\nfrom .fast_data_types import KeyEvent as WindowSystemKeyEvent\nfrom .key_names import character_key_name_aliases, functional_key_name_aliases\nfrom .types import ParsedShortcut\n\n# number name mappings {{{\n# start csi mapping (auto generated by gen-key-constants.py do not edit)\nfunctional_key_number_to_name_map = {\n    57344: 'ESCAPE',\n    57345: 'ENTER',\n    57346: 'TAB',\n    57347: 'BACKSPACE',\n    57348: 'INSERT',\n    57349: 'DELETE',\n    57350: 'LEFT',\n    57351: 'RIGHT',\n    57352: 'UP',\n    57353: 'DOWN',\n    57354: 'PAGE_UP',\n    57355: 'PAGE_DOWN',\n    57356: 'HOME',\n    57357: 'END',\n    57358: 'CAPS_LOCK',\n    57359: 'SCROLL_LOCK',\n    57360: 'NUM_LOCK',\n    57361: 'PRINT_SCREEN',\n    57362: 'PAUSE',\n    57363: 'MENU',\n    57364: 'F1',\n    57365: 'F2',\n    57366: 'F3',\n    57367: 'F4',\n    57368: 'F5',\n    57369: 'F6',\n    57370: 'F7',\n    57371: 'F8',\n    57372: 'F9',\n    57373: 'F10',\n    57374: 'F11',\n    57375: 'F12',\n    57376: 'F13',\n    57377: 'F14',\n    57378: 'F15',\n    57379: 'F16',\n    57380: 'F17',\n    57381: 'F18',\n    57382: 'F19',\n    57383: 'F20',\n    57384: 'F21',\n    57385: 'F22',\n    57386: 'F23',\n    57387: 'F24',\n    57388: 'F25',\n    57389: 'F26',\n    57390: 'F27',\n    57391: 'F28',\n    57392: 'F29',\n    57393: 'F30',\n    57394: 'F31',\n    57395: 'F32',\n    57396: 'F33',\n    57397: 'F34',\n    57398: 'F35',\n    57399: 'KP_0',\n    57400: 'KP_1',\n    57401: 'KP_2',\n    57402: 'KP_3',\n    57403: 'KP_4',\n    57404: 'KP_5',\n    57405: 'KP_6',\n    57406: 'KP_7',\n    57407: 'KP_8',\n    57408: 'KP_9',\n    57409: 'KP_DECIMAL',\n    57410: 'KP_DIVIDE',\n    57411: 'KP_MULTIPLY',\n    57412: 'KP_SUBTRACT',\n    57413: 'KP_ADD',\n    57414: 'KP_ENTER',\n    57415: 'KP_EQUAL',\n    57416: 'KP_SEPARATOR',\n    57417: 'KP_LEFT',\n    57418: 'KP_RIGHT',\n    57419: 'KP_UP',\n    57420: 'KP_DOWN',\n    57421: 'KP_PAGE_UP',\n    57422: 'KP_PAGE_DOWN',\n    57423: 'KP_HOME',\n    57424: 'KP_END',\n    57425: 'KP_INSERT',\n    57426: 'KP_DELETE',\n    57427: 'KP_BEGIN',\n    57428: 'MEDIA_PLAY',\n    57429: 'MEDIA_PAUSE',\n    57430: 'MEDIA_PLAY_PAUSE',\n    57431: 'MEDIA_REVERSE',\n    57432: 'MEDIA_STOP',\n    57433: 'MEDIA_FAST_FORWARD',\n    57434: 'MEDIA_REWIND',\n    57435: 'MEDIA_TRACK_NEXT',\n    57436: 'MEDIA_TRACK_PREVIOUS',\n    57437: 'MEDIA_RECORD',\n    57438: 'LOWER_VOLUME',\n    57439: 'RAISE_VOLUME',\n    57440: 'MUTE_VOLUME',\n    57441: 'LEFT_SHIFT',\n    57442: 'LEFT_CONTROL',\n    57443: 'LEFT_ALT',\n    57444: 'LEFT_SUPER',\n    57445: 'LEFT_HYPER',\n    57446: 'LEFT_META',\n    57447: 'RIGHT_SHIFT',\n    57448: 'RIGHT_CONTROL',\n    57449: 'RIGHT_ALT',\n    57450: 'RIGHT_SUPER',\n    57451: 'RIGHT_HYPER',\n    57452: 'RIGHT_META',\n    57453: 'ISO_LEVEL3_SHIFT',\n    57454: 'ISO_LEVEL5_SHIFT'}\ncsi_number_to_functional_number_map = {\n    2: 57348,\n    3: 57349,\n    5: 57354,\n    6: 57355,\n    7: 57356,\n    8: 57357,\n    9: 57346,\n    11: 57364,\n    12: 57365,\n    13: 57345,\n    14: 57367,\n    15: 57368,\n    17: 57369,\n    18: 57370,\n    19: 57371,\n    20: 57372,\n    21: 57373,\n    23: 57374,\n    24: 57375,\n    27: 57344,\n    127: 57347}\nletter_trailer_to_csi_number_map = {'A': 57352, 'B': 57353, 'C': 57351, 'D': 57350, 'E': 57427, 'F': 8, 'H': 7, 'P': 11, 'Q': 12, 'S': 14}\ntilde_trailers = {57348, 57349, 57354, 57355, 57366, 57368, 57369, 57370, 57371, 57372, 57373, 57374, 57375}\n# end csi mapping\n# }}}\n\n\n@lru_cache(2)\ndef get_name_to_functional_number_map() -> dict[str, int]:\n    return {v: k for k, v in functional_key_number_to_name_map.items()}\n\n\n@lru_cache(2)\ndef get_functional_to_csi_number_map() -> dict[int, int]:\n    return {v: k for k, v in csi_number_to_functional_number_map.items()}\n\n\n@lru_cache(2)\ndef get_csi_number_to_letter_trailer_map() -> dict[int, str]:\n    return {v: k for k, v in letter_trailer_to_csi_number_map.items()}\n\n\nPRESS: int = 1\nREPEAT: int = 2\nRELEASE: int = 4\n\n\nclass EventType(IntEnum):\n    PRESS = PRESS\n    REPEAT = REPEAT\n    RELEASE = RELEASE\n\n\n@lru_cache(maxsize=128)\ndef parse_shortcut(spec: str) -> ParsedShortcut:\n    if spec.endswith('+'):\n        spec = f'{spec[:-1]}plus'\n    parts = spec.split('+')\n    key_name = parts[-1]\n    key_name = functional_key_name_aliases.get(key_name.upper(), key_name)\n    is_functional_key = key_name.upper() in get_name_to_functional_number_map()\n    if is_functional_key:\n        key_name = key_name.upper()\n    else:\n        key_name = character_key_name_aliases.get(key_name.upper(), key_name)\n    mod_val = 0\n    if len(parts) > 1:\n        mods = tuple(config_mod_map.get(x.upper(), META << 8) for x in parts[:-1])\n        for x in mods:\n            mod_val |= x\n    return ParsedShortcut(mod_val, key_name)\n\n\nclass KeyEvent(NamedTuple):\n    type: EventType = EventType.PRESS\n    mods: int = 0\n    key: str = ''\n    text: str = ''\n    shifted_key: str = ''\n    alternate_key: str = ''\n    shift: bool = False\n    alt: bool = False\n    ctrl: bool = False\n    super: bool = False\n    hyper: bool = False\n    meta: bool = False\n    caps_lock: bool = False\n    num_lock: bool = False\n\n    def matches(self, spec: str | ParsedShortcut, types: int = EventType.PRESS | EventType.REPEAT) -> bool:\n        mods = self.mods_without_locks\n        if not self.type & types:\n            return False\n        if isinstance(spec, str):\n            spec = parse_shortcut(spec)\n        if (mods, self.key) == spec:\n            return True\n        is_shifted = bool(self.shifted_key and self.shift)\n        if is_shifted and (mods & ~SHIFT, self.shifted_key) == spec:\n            return True\n        return False\n\n    def matches_without_mods(self, spec: str | ParsedShortcut, types: int = EventType.PRESS | EventType.REPEAT) -> bool:\n        if not self.type & types:\n            return False\n        if isinstance(spec, str):\n            spec = parse_shortcut(spec)\n        return self.key == spec[1]\n\n    def matches_text(self, text: str, case_sensitive: bool = False) -> bool:\n        if case_sensitive:\n            return self.text == text\n        return self.text.lower() == text.lower()\n\n    @property\n    def is_release(self) -> bool:\n        return self.type is EventType.RELEASE\n\n    @property\n    def mods_without_locks(self) -> int:\n        return self.mods & ~(NUM_LOCK | CAPS_LOCK)\n\n    @property\n    def has_mods(self) -> bool:\n        return bool(self.mods_without_locks)\n\n    def as_window_system_event(self) -> WindowSystemKeyEvent:\n        action = defines.GLFW_PRESS\n        if self.type is EventType.REPEAT:\n            action = defines.GLFW_REPEAT\n        elif self.type is EventType.RELEASE:\n            action = defines.GLFW_RELEASE\n        mods = 0\n        if self.mods:\n            if self.shift:\n                mods |= defines.GLFW_MOD_SHIFT\n            if self.alt:\n                mods |= defines.GLFW_MOD_ALT\n            if self.ctrl:\n                mods |= defines.GLFW_MOD_CONTROL\n            if self.super:\n                mods |= defines.GLFW_MOD_SUPER\n            if self.hyper:\n                mods |= defines.GLFW_MOD_HYPER\n            if self.meta:\n                mods |= defines.GLFW_MOD_META\n            if self.caps_lock:\n                mods |= defines.GLFW_MOD_CAPS_LOCK\n            if self.num_lock:\n                mods |= defines.GLFW_MOD_NUM_LOCK\n\n        fnm = get_name_to_functional_number_map()\n\n        def as_num(key: str) -> int:\n            return (fnm.get(key) or ord(key)) if key else 0\n\n        return WindowSystemKeyEvent(\n            key=as_num(self.key), shifted_key=as_num(self.shifted_key),\n            alternate_key=as_num(self.alternate_key), mods=mods,\n            action=action, text=self.text)\n\n\nSHIFT, ALT, CTRL, SUPER, HYPER, META, CAPS_LOCK, NUM_LOCK = 1, 2, 4, 8, 16, 32, 64, 128\nenter_key = KeyEvent(key='ENTER')\nbackspace_key = KeyEvent(key='BACKSPACE')\nconfig_mod_map = {\n    'SHIFT': SHIFT,\n    '⇧': SHIFT,\n    'ALT': ALT,\n    'OPTION': ALT,\n    'OPT': ALT,\n    '⌥': ALT,\n    'SUPER': SUPER,\n    'COMMAND': SUPER,\n    'CMD': SUPER,\n    '⌘': SUPER,\n    'CONTROL': CTRL,\n    'CTRL': CTRL,\n    '⌃': CTRL,\n    'HYPER': HYPER,\n    'META': META,\n    'NUM_LOCK': NUM_LOCK,\n    'CAPS_LOCK': CAPS_LOCK,\n}\n\n\ndef decode_key_event(csi: str, csi_type: str) -> KeyEvent:\n    parts = csi.split(';')\n\n    def get_sub_sections(x: str, missing: int = 0) -> tuple[int, ...]:\n        return tuple(int(y) if y else missing for y in x.split(':'))\n\n    first_section = get_sub_sections(parts[0])\n    second_section = get_sub_sections(parts[1], 1) if len(parts) > 1 else ()\n    third_section = get_sub_sections(parts[2]) if len(parts) > 2 else ()\n    mods = (second_section[0] - 1) if second_section else 0\n    action = second_section[1] if len(second_section) > 1 else 1\n    keynum = first_section[0]\n    if csi_type in 'ABCDEHFPQRS':\n        keynum = letter_trailer_to_csi_number_map[csi_type]\n\n    def key_name(num: int) -> str:\n        if not num:\n            return ''\n        if num != 13:\n            num = csi_number_to_functional_number_map.get(num, num)\n            ans = functional_key_number_to_name_map.get(num)\n        else:\n            ans = 'ENTER' if csi_type == 'u' else 'F3'\n        if ans is None:\n            ans = chr(num)\n        return ans\n\n    return KeyEvent(\n        mods=mods, shift=bool(mods & SHIFT), alt=bool(mods & ALT),\n        ctrl=bool(mods & CTRL), super=bool(mods & SUPER),\n        hyper=bool(mods & HYPER), meta=bool(mods & META),\n        caps_lock=bool(mods & CAPS_LOCK), num_lock=bool(mods & NUM_LOCK),\n        key=key_name(keynum),\n        shifted_key=key_name(first_section[1] if len(first_section) > 1 else 0),\n        alternate_key=key_name(first_section[2] if len(first_section) > 2 else 0),\n        type={1: EventType.PRESS, 2: EventType.REPEAT, 3: EventType.RELEASE}[action],\n        text=''.join(map(chr, third_section))\n    )\n\n\ndef csi_number_for_name(key_name: str) -> int:\n    if not key_name:\n        return 0\n    if key_name in ('F3', 'ENTER'):\n        return 13\n    fn = get_name_to_functional_number_map().get(key_name)\n    if fn is None:\n        return ord(key_name)\n    return get_functional_to_csi_number_map().get(fn, fn)\n\n\ndef encode_key_event(key_event: KeyEvent) -> str:\n    key = csi_number_for_name(key_event.key)\n    shifted_key = csi_number_for_name(key_event.shifted_key)\n    alternate_key = csi_number_for_name(key_event.alternate_key)\n    lt = get_csi_number_to_letter_trailer_map()\n    if key_event.key == 'ENTER':\n        trailer = 'u'\n    else:\n        trailer = lt.get(key, 'u')\n    if trailer != 'u':\n        key = 1\n    mods = key_event.mods\n    text = key_event.text\n    ans = '\\033['\n    if key != 1 or mods or shifted_key or alternate_key or text:\n        ans += f'{key}'\n    if shifted_key or alternate_key:\n        ans += ':' + (f'{shifted_key}' if shifted_key else '')\n        if alternate_key:\n            ans += f':{alternate_key}'\n    action = 1\n    if key_event.type is EventType.REPEAT:\n        action = 2\n    elif key_event.type is EventType.RELEASE:\n        action = 3\n    if mods or action > 1 or text:\n        m = 0\n        if key_event.shift:\n            m |= 1\n        if key_event.alt:\n            m |= 2\n        if key_event.ctrl:\n            m |= 4\n        if key_event.super:\n            m |= 8\n        if key_event.hyper:\n            m |= 16\n        if key_event.meta:\n            m |= 32\n        if key_event.caps_lock:\n            m |= 64\n        if key_event.num_lock:\n            m |= 128\n        if action > 1 or m:\n            ans += f';{m+1}'\n            if action > 1:\n                ans += f':{action}'\n        elif text:\n            ans += ';'\n    if text:\n        ans += ';' + ':'.join(map(str, map(ord, text)))\n    fn = get_name_to_functional_number_map().get(key_event.key)\n    if fn is not None and fn in tilde_trailers:\n        trailer = '~'\n    return ans + trailer\n\n\ndef decode_key_event_as_window_system_key(text: str) -> WindowSystemKeyEvent | None:\n    csi, trailer = text[2:-1], text[-1]\n    try:\n        k = decode_key_event(csi, trailer)\n    except Exception:\n        return None\n    return k.as_window_system_event()\n"
  },
  {
    "path": "kitty/key_names.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nfrom collections.abc import Callable\nfrom contextlib import suppress\nfrom typing import Optional\n\nfrom .constants import is_macos\n\nfunctional_key_name_aliases: dict[str, str] = {\n    'ESC': 'ESCAPE',\n    'PGUP': 'PAGE_UP',\n    'PAGEUP': 'PAGE_UP',\n    'PGDN': 'PAGE_DOWN',\n    'PAGEDOWN': 'PAGE_DOWN',\n    'RETURN': 'ENTER',\n    'ARROWUP': 'UP',\n    'ARROWDOWN': 'DOWN',\n    'ARROWRIGHT': 'RIGHT',\n    'ARROWLEFT': 'LEFT',\n    'DEL': 'DELETE',\n    'KP_PLUS': 'KP_ADD',\n    'KP_MINUS': 'KP_SUBTRACT',\n}\n\n\ncharacter_key_name_aliases: dict[str, str] = {\n    'SPC': ' ',\n    'SPACE': ' ',\n    'STAR': '*',\n    'MULTIPLY': '*',\n    'PLUS': '+',\n    'MINUS': '-',\n    'BAR': '|',\n    'PIPE': '|',\n    'HYPHEN': '-',\n    'EQUAL': '=',\n    'UNDERSCORE': '_',\n    'COMMA': ',',\n    'PERIOD': '.',\n    'DOT': '.',\n    'SLASH': '/',\n    'BACKSLASH': '\\\\',\n    'TILDE': '~',\n    'GRAVE': '`',\n    'GRAVE_ACCENT': '`',\n    'APOSTROPHE': \"'\",\n    'SEMICOLON': ';',\n    'COLON': ':',\n    'LEFT_BRACKET': '[',\n    'RIGHT_BRACKET': ']',\n}\nLookupFunc = Callable[[str, bool], Optional[int]]\n\n\ndef null_lookup(name: str, case_sensitive: bool = False) -> int | None:\n    return None\n\n\nif is_macos:\n    def get_key_name_lookup() -> LookupFunc:\n        return null_lookup\nelse:\n    def load_libxkb_lookup() -> LookupFunc:\n        import ctypes\n        for suffix in ('.0', ''):\n            with suppress(Exception):\n                lib = ctypes.CDLL(f'libxkbcommon.so{suffix}')\n                break\n        else:\n            from ctypes.util import find_library\n            lname = find_library('xkbcommon')\n            if lname is None:\n                raise RuntimeError('Failed to find libxkbcommon')\n            lib = ctypes.CDLL(lname)\n\n        f = lib.xkb_keysym_from_name\n        f.argtypes = [ctypes.c_char_p, ctypes.c_int]\n        f.restype = ctypes.c_int\n\n        def xkb_lookup(name: str, case_sensitive: bool = False) -> int | None:\n            q = name.encode('utf-8')\n            return f(q, int(case_sensitive)) or None\n\n        return xkb_lookup\n\n    def get_key_name_lookup() -> LookupFunc:\n        ans: LookupFunc | None = getattr(get_key_name_lookup, 'ans', None)\n        if ans is None:\n            try:\n                ans = load_libxkb_lookup()\n            except Exception as e:\n                print('Failed to load libxkbcommon.xkb_keysym_from_name with error:', e, file=sys.stderr)\n                ans = null_lookup\n            setattr(get_key_name_lookup, 'ans', ans)\n        return ans\n"
  },
  {
    "path": "kitty/keys.c",
    "content": "/*\n * keys.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"state.h\"\n#include \"keys.h\"\n#include \"screen.h\"\n#include \"glfw-wrapper.h\"\n#include <structmember.h>\n\n#ifndef __APPLE__\n#include <xkbcommon/xkbcommon.h>\n#endif\n\n// python KeyEvent object {{{\ntypedef struct {\n    PyObject_HEAD\n    PyObject *key, *shifted_key, *alternate_key;\n    PyObject *mods, *action, *native_key, *ime_state;\n    PyObject *text;\n} PyKeyEvent;\n\nstatic PyObject* convert_glfw_key_event_to_python(const GLFWkeyevent *ev);\n\nstatic PyObject*\nnew_keyevent_object(PyTypeObject *type UNUSED, PyObject *args, PyObject *kw) {\n    static char *kwds[] = {\"key\", \"shifted_key\", \"alternate_key\", \"mods\", \"action\", \"native_key\", \"ime_state\", \"text\", NULL};\n    GLFWkeyevent ev = {.action=GLFW_PRESS};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"I|IIiiiiz\", kwds, &ev.key, &ev.shifted_key, &ev.alternate_key, &ev.mods, &ev.action, &ev.native_key, &ev.ime_state, &ev.text)) return NULL;\n    return convert_glfw_key_event_to_python(&ev);\n}\n\nbool\nis_modifier_key(const uint32_t key) {\n    START_ALLOW_CASE_RANGE\n    switch (key) {\n        case GLFW_FKEY_LEFT_SHIFT ... GLFW_FKEY_ISO_LEVEL5_SHIFT:\n        case GLFW_FKEY_CAPS_LOCK:\n        case GLFW_FKEY_SCROLL_LOCK:\n        case GLFW_FKEY_NUM_LOCK:\n            return true;\n        default:\n            return false;\n    }\n    END_ALLOW_CASE_RANGE\n}\n\nstatic bool\nis_no_action_key(const uint32_t key, const uint32_t native_key) {\n    switch (native_key) {\n#ifndef __APPLE__\n        case XKB_KEY_XF86Fn:\n        case XKB_KEY_XF86WakeUp:\n            return true;\n#endif\n        default:\n            return is_modifier_key(key);\n    }\n}\n\nstatic void\ndealloc(PyKeyEvent* self) {\n    Py_CLEAR(self->key); Py_CLEAR(self->shifted_key); Py_CLEAR(self->alternate_key);\n    Py_CLEAR(self->mods); Py_CLEAR(self->action); Py_CLEAR(self->native_key); Py_CLEAR(self->ime_state);\n    Py_CLEAR(self->text);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic PyMemberDef members[] = {\n#define M(x) {#x, T_OBJECT, offsetof(PyKeyEvent, x), READONLY, #x}\n    M(key), M(shifted_key), M(alternate_key),\n    M(mods), M(action), M(native_key), M(ime_state),\n    M(text),\n    {NULL},\n#undef M\n};\n\nPyTypeObject PyKeyEvent_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.KeyEvent\",\n    .tp_basicsize = sizeof(PyKeyEvent),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"A key event\",\n    .tp_members = members,\n    .tp_new = new_keyevent_object,\n};\n\nstatic PyObject*\nconvert_glfw_key_event_to_python(const GLFWkeyevent *ev) {\n    PyKeyEvent *self = (PyKeyEvent*)PyKeyEvent_Type.tp_alloc(&PyKeyEvent_Type, 0);\n    if (!self) return NULL;\n#define C(x) { unsigned long t = ev->x; self->x = PyLong_FromUnsignedLong(t); if (self->x == NULL) { Py_CLEAR(self); return NULL; } }\n    C(key); C(shifted_key); C(alternate_key); C(mods); C(action); C(native_key); C(ime_state);\n#undef C\n    self->text = PyUnicode_FromString(ev->text ? ev->text : \"\");\n    if (!self->text) { Py_CLEAR(self); return NULL; }\n    return (PyObject*)self;\n}\n// }}}\n\nstatic Window*\nactive_window(void) {\n    Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;\n    Window *w = t->windows + t->active_window;\n    if (!w->render_data.screen) return NULL;\n    if (w->redirect_keys_to_overlay) {\n        for (unsigned i = 0; i < t->num_windows; i++) {\n            if (t->windows[i].id == w->redirect_keys_to_overlay && w->render_data.screen) return t->windows + i;\n        }\n    }\n    return w;\n}\n\nvoid\nupdate_ime_focus(OSWindow *osw, bool focused) {\n    if (!osw || !osw->handle) return;\n    GLFWIMEUpdateEvent ev = { .focused = focused, .type = GLFW_IME_UPDATE_FOCUS };\n    glfwUpdateIMEState(osw->handle, &ev);\n}\n\nvoid\nprepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev) {\n    unsigned int cell_width = osw->fonts_data->fcm.cell_width, cell_height = osw->fonts_data->fcm.cell_height;\n    unsigned int left = w->render_data.geometry.left, top = w->render_data.geometry.top;\n    if (screen_is_overlay_active(screen)) {\n        left += screen->overlay_line.cursor_x * cell_width;\n        top += MIN(screen->overlay_line.ynum + screen->scrolled_by, screen->lines - 1) * cell_height;\n    } else {\n        left += screen->cursor->x * cell_width;\n        top += screen->cursor->y * cell_height;\n    }\n    ev->cursor.left = left; ev->cursor.top = top; ev->cursor.width = cell_width; ev->cursor.height = cell_height;\n}\n\nvoid\nupdate_ime_position(Window* w UNUSED, Screen *screen UNUSED) {\n    GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION };\n#ifndef __APPLE__\n    prepare_ime_position_update_event(global_state.callback_os_window, w, screen, &ev);\n#endif\n    glfwUpdateIMEState(global_state.callback_os_window->handle, &ev);\n}\n\nconst char*\nformat_mods(unsigned mods) {\n    static char buf[128];\n    char *p = buf, *s;\n#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, x)\n    pr(\"mods: \");\n    s = p;\n    if (mods & GLFW_MOD_CONTROL) pr(\"ctrl+\");\n    if (mods & GLFW_MOD_ALT) pr(\"alt+\");\n    if (mods & GLFW_MOD_SHIFT) pr(\"shift+\");\n    if (mods & GLFW_MOD_SUPER) pr(\"super+\");\n    if (mods & GLFW_MOD_HYPER) pr(\"hyper+\");\n    if (mods & GLFW_MOD_META) pr(\"meta+\");\n    if (mods & GLFW_MOD_CAPS_LOCK) pr(\"capslock+\");\n    if (mods & GLFW_MOD_NUM_LOCK) pr(\"numlock+\");\n    if (p == s) pr(\"none\");\n    else p--;\n    pr(\" \");\n#undef pr\n    return buf;\n}\n\nstatic void\nsend_key_to_child(id_type window_id, Screen *screen, const GLFWkeyevent *ev) {\n    const int action = ev->action;\n    const uint32_t key = ev->key, native_key = ev->native_key;\n    const char *text = ev->text ? ev->text : \"\";\n\n    if (action == GLFW_REPEAT && !screen->modes.mDECARM) {\n        debug(\"discarding repeat key event as DECARM is off\\n\");\n        return;\n    }\n    if ((screen->scrolled_by || screen->pixel_scroll_offset_y != 0) && action == GLFW_PRESS && !is_no_action_key(key, native_key)) {\n        screen_history_scroll(screen, SCROLL_FULL, false);  // scroll back to bottom\n    }\n    char encoded_key[KEY_BUFFER_SIZE] = {0};\n    int size = encode_glfw_key_event(ev, screen->modes.mDECCKM, screen_current_key_encoding_flags(screen), encoded_key);\n    if (size == SEND_TEXT_TO_CHILD) {\n        schedule_write_to_child(window_id, 1, text, strlen(text));\n        debug(\"sent key as text to child (window_id: %llu): %s\\n\", window_id, text);\n    } else if (size > 0) {\n        if (size == 1 && screen->modes.mHANDLE_TERMIOS_SIGNALS) {\n            if (screen_send_signal_for_key(screen, *encoded_key)) return;\n        }\n        schedule_write_to_child(window_id, 1, encoded_key, size);\n        if (OPT(debug_keyboard)) {\n            debug(\"sent encoded key to child (window_id: %llu): \", window_id);\n            for (int ki = 0; ki < size; ki++) {\n                if (encoded_key[ki] == 27) { debug(\"^[ \"); }\n                else if (encoded_key[ki] == ' ') { debug(\"SPC \"); }\n                else if (isprint(encoded_key[ki])) { debug(\"%c \", encoded_key[ki]); }\n                else { debug(\"0x%x \", encoded_key[ki]); }\n            }\n            debug(\"\\n\");\n        }\n    } else {\n        debug(\"ignoring as keyboard mode does not support encoding this event\\n\");\n    }\n}\n\nvoid\ndispatch_buffered_keys(Window *w) {\n    if (!w->render_data.screen || !w->buffered_keys.count) return;\n    GLFWkeyevent *keys = w->buffered_keys.key_data;\n    for (size_t i = 0; i < w->buffered_keys.count; i++) {\n        debug(\"Sending previously buffered key \");\n        send_key_to_child(w->id, w->render_data.screen, keys + i);\n    }\n    free(w->buffered_keys.key_data); zero_at_ptr(&w->buffered_keys);\n}\n\nvoid\non_key_input(const GLFWkeyevent *ev) {\n    Window *w = active_window();\n    const int action = ev->action, mods = ev->mods;\n    const uint32_t key = ev->key, native_key = ev->native_key;\n    const char *text = ev->text ? ev->text : \"\";\n\n    if (OPT(debug_keyboard)) {\n        if (!key && !native_key && text[0]) {\n            debug(\"\\x1b[33mon_IME_input\\x1b[m: text: %s \", text);\n        } else {\n            debug(\"\\x1b[33mon_key_input\\x1b[m: glfw key: 0x%x native_code: 0x%x action: %s %stext: '%s' state: %d \",\n                    key, native_key,\n                    (action == GLFW_RELEASE ? \"RELEASE\" : (action == GLFW_PRESS ? \"PRESS\" : \"REPEAT\")),\n                    format_mods(mods), text, ev->ime_state);\n        }\n    }\n    if (!w) { debug(\"no active window, ignoring\\n\"); return; }\n    send_pending_click_to_window(w, -1);\n    if (OPT(mouse_hide.hide_wait) < 0 && !is_no_action_key(key, native_key)) hide_mouse(global_state.callback_os_window);\n    Screen *screen = w->render_data.screen;\n    id_type active_window_id = w->id;\n\n    switch(ev->ime_state) {\n        case GLFW_IME_WAYLAND_DONE_EVENT:\n            // If we update IME position here it sends GNOME's text input system into\n            // an infinite loop. See https://github.com/kovidgoyal/kitty/issues/5105\n            // and also: https://github.com/kovidgoyal/kitty/pull/7283\n            screen_update_overlay_text(screen, text);\n            debug(\"handled wayland IME done event\\n\");\n            return;\n        case GLFW_IME_PREEDIT_CHANGED:\n            screen_update_overlay_text(screen, text);\n            update_ime_position(w, screen);\n            debug(\"updated pre-edit text: '%s'\\n\", text);\n            return;\n        case GLFW_IME_COMMIT_TEXT:\n            if (*text) {\n                schedule_write_to_child(w->id, 1, text, strlen(text));\n                debug(\"committed pre-edit text: %s sent to child as text.\\n\", text);\n            } else debug(\"committed pre-edit text: (null)\\n\");\n            screen_update_overlay_text(screen, NULL);\n            return;\n        case GLFW_IME_NONE:\n            // for macOS, update ime position on every key input\n            // because the position is required before next input\n            // On Linux this is needed by Fig integration: https://github.com/kovidgoyal/kitty/issues/5241\n            update_ime_position(w, screen);\n            break;\n        default:\n            debug(\"invalid state, ignoring\\n\");\n            return;\n    }\n    bool dispatch_ok = true, consumed = false;\n#define dispatch_key_event(name) { \\\n    PyObject *ke = NULL, *ret = NULL; \\\n    ke = convert_glfw_key_event_to_python(ev); if (!ke) { PyErr_Print(); return; }; \\\n    ret = PyObject_CallMethod(global_state.boss, #name, \"O\", ke); Py_CLEAR(ke); \\\n    if (ret == NULL) { PyErr_Print(); dispatch_ok = false; } \\\n    else { consumed = ret == Py_True; Py_CLEAR(ret); } \\\n    w = window_for_window_id(active_window_id); \\\n}\n    if (action == GLFW_PRESS || action == GLFW_REPEAT) {\n        w->last_special_key_pressed = 0;\n        dispatch_key_event(dispatch_possible_special_key);\n        if (dispatch_ok) {\n            if (consumed) {\n                debug(\"handled as shortcut\\n\");\n                if (w) w->last_special_key_pressed = key;\n                return;\n            }\n        }\n        if (!w) return;\n        screen = w->render_data.screen;\n    } else if (w->last_special_key_pressed == key) {\n        w->last_special_key_pressed = 0;\n        debug(\"ignoring release event for previous press that was handled as shortcut\\n\");\n        return;\n    }\n    if (w->buffered_keys.enabled) {\n        if (w->buffered_keys.capacity < w->buffered_keys.count + 1) {\n            w->buffered_keys.capacity = MAX(16u, w->buffered_keys.capacity + 8);\n            GLFWkeyevent *new = malloc(w->buffered_keys.capacity * sizeof(GLFWkeyevent));\n            if (!new) fatal(\"Out of memory\");\n            memcpy(new, w->buffered_keys.key_data, w->buffered_keys.count * sizeof(new[0]));\n            w->buffered_keys.key_data = new;\n        }\n        GLFWkeyevent *k = w->buffered_keys.key_data;\n        k[w->buffered_keys.count++] = *ev;\n        debug(\"buffering key until child is ready\\n\");\n    } else send_key_to_child(w->id, screen, ev);\n#undef dispatch_key_event\n}\n\nvoid\nfake_scroll(Window *w, int amount, bool upwards) {\n    if (!w) return;\n    int key = upwards ? GLFW_FKEY_UP : GLFW_FKEY_DOWN;\n    GLFWkeyevent ev = {.key = key };\n    char encoded_key[KEY_BUFFER_SIZE] = {0};\n    Screen *screen = w->render_data.screen;\n    uint8_t flags = screen_current_key_encoding_flags(screen);\n    while (amount-- > 0) {\n        ev.action = GLFW_PRESS;\n        int size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, flags, encoded_key);\n        if (size > 0) schedule_write_to_child(w->id, 1, encoded_key, size);\n        ev.action = GLFW_RELEASE;\n        size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, flags, encoded_key);\n        if (size > 0) schedule_write_to_child(w->id, 1, encoded_key, size);\n    }\n}\n\n#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)\n#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;\n#define M(name, arg_type) {#name, (PyCFunction)(void (*) (void))(py##name), arg_type, NULL}\n\nPYWRAP1(key_for_native_key_name) {\n    const char *name;\n    int case_sensitive = 0;\n    PA(\"s|p\", &name, &case_sensitive);\n#ifndef __APPLE__\n    if (glfwGetNativeKeyForName) {  // if this function is called before GLFW is initialized glfwGetNativeKeyForName will be NULL\n        int native_key = glfwGetNativeKeyForName(name, case_sensitive);\n        if (native_key) return Py_BuildValue(\"i\", native_key);\n    }\n#endif\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npyencode_key_for_tty(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    static char *kwds[] = {\"key\", \"shifted_key\", \"alternate_key\", \"mods\", \"action\", \"key_encoding_flags\", \"text\", \"cursor_key_mode\", NULL};\n    unsigned int key = 0, shifted_key = 0, alternate_key = 0, mods = 0, action = GLFW_PRESS, key_encoding_flags = 0;\n    const char *text = NULL;\n    int cursor_key_mode = 0;\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"I|IIIIIzp\", kwds, &key, &shifted_key, &alternate_key, &mods, &action, &key_encoding_flags, &text, &cursor_key_mode)) return NULL;\n    GLFWkeyevent ev = { .key = key, .shifted_key = shifted_key, .alternate_key = alternate_key, .text = text, .action = action, .mods = mods };\n    char output[KEY_BUFFER_SIZE+1] = {0};\n    int num = encode_glfw_key_event(&ev, cursor_key_mode, key_encoding_flags, output);\n    if (num == SEND_TEXT_TO_CHILD) return PyUnicode_FromString(text);\n    return PyUnicode_FromStringAndSize(output, MAX(0, num));\n}\n\nstatic PyObject*\npyinject_key(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    static char *kwds[] = {\"key\", \"shifted_key\", \"alternate_key\", \"mods\", \"action\", \"text\", \"os_window_id\", NULL};\n    unsigned int key = 0, shifted_key = 0, alternate_key = 0, mods = 0, action = GLFW_PRESS;\n    unsigned long long os_window_id = 0;\n    const char *text = NULL;\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"I|IIIIzK\", kwds, &key, &shifted_key, &alternate_key, &mods, &action, &text, &os_window_id)) return NULL;\n    id_type orig = global_state.callback_os_window ? global_state.callback_os_window->id : 0;\n    bool found = false;\n    if (os_window_id) {\n        for (size_t i = 0; i < global_state.num_os_windows && !found; i++) {\n            if (global_state.os_windows[i].id == os_window_id) {\n                global_state.callback_os_window = global_state.os_windows + i;\n                found = true;\n            }\n        }\n        if (!found) { PyErr_Format(PyExc_IndexError, \"Could not find OS Window with id: %llu\", os_window_id); return NULL; }\n    } else {\n        if (!global_state.callback_os_window) {\n            for (size_t i = 0; i < global_state.num_os_windows && !found; i++) {\n                if (global_state.os_windows[i].is_focused) {\n                    global_state.callback_os_window = global_state.os_windows + i;\n                    found = true;\n                }\n            }\n            if (!found && ! global_state.num_os_windows) { PyErr_SetString(PyExc_Exception, \"No OS Windows available to inject key presses into\"); return NULL; }\n            global_state.callback_os_window = global_state.os_windows;\n            found = true;\n        }\n    }\n    GLFWkeyevent ev = { .key = key, .shifted_key = shifted_key, .alternate_key = alternate_key, .text = text, .action = action, .mods = mods };\n    on_key_input(&ev);\n    if (orig) {\n        found = false;\n        for (size_t i = 0; i < global_state.num_os_windows && !found; i++) {\n            if (global_state.os_windows[i].id == orig) {\n                global_state.callback_os_window = global_state.os_windows + i; found = true;\n            }\n        }\n        if (!found) global_state.callback_os_window = NULL;\n    } else global_state.callback_os_window = NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npyis_modifier_key(PyObject *self UNUSED, PyObject *a) {\n    unsigned long key = PyLong_AsUnsignedLong(a);\n    if (PyErr_Occurred()) return NULL;\n    if (is_modifier_key(key)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyMethodDef module_methods[] = {\n    M(key_for_native_key_name, METH_VARARGS),\n    M(encode_key_for_tty, METH_VARARGS | METH_KEYWORDS),\n    M(inject_key, METH_VARARGS | METH_KEYWORDS),\n    M(is_modifier_key, METH_O),\n    {0}\n};\n\n// SingleKey {{{\ntypedef uint64_t keybitfield;\n#define KEY_BITS 51\n#define MOD_BITS 12\n#if 1 << (MOD_BITS-1) < GLFW_MOD_KITTY\n#error \"Not enough mod bits\"\n#endif\ntypedef union Key {\n    struct {\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        keybitfield mods : MOD_BITS;\n        keybitfield is_native: 1;\n        keybitfield key : KEY_BITS;\n#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        keybitfield key : KEY_BITS;\n        keybitfield is_native: 1;\n        keybitfield mods : MOD_BITS;\n#else\n#error \"Unsupported endianness\"\n#endif\n    };\n    keybitfield val;\n} Key;\n\nstatic PyTypeObject SingleKey_Type;\nstatic char *SingleKey_kwds[] = {\"mods\", \"is_native\", \"key\", NULL};\ntypedef struct {\n    PyObject_HEAD\n\n    Key key;\n    bool defined_with_kitty_mod;\n} SingleKey;\n\nstatic inline void\nSingleKey_set_vals(SingleKey *self, long long key, unsigned short mods, int is_native) {\n    if (key >= 0 && (unsigned long long)key <= BIT_MASK(keybitfield, KEY_BITS)) {\n        keybitfield k = (keybitfield)(unsigned long long)key;\n        self->key.key = k & BIT_MASK(keybitfield, KEY_BITS);\n    }\n    if (!(mods & 1 << (MOD_BITS + 1))) self->key.mods = mods & BIT_MASK(uint32_t, MOD_BITS);\n    if (is_native > -1) self->key.is_native = is_native ? 1 : 0;\n}\n\nstatic PyObject *\nSingleKey_new(PyTypeObject *type, PyObject *args, PyObject *kw) {\n    long long key = -1; unsigned short mods = 1 << (MOD_BITS + 1); int is_native = -1;\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"|HpL\", SingleKey_kwds, &mods, &is_native, &key)) return NULL;\n    SingleKey *self = (SingleKey *)type->tp_alloc(type, 0);\n    if (self) SingleKey_set_vals(self, key, mods, is_native);\n    return (PyObject*)self;\n}\n\nstatic void\nSingleKey_dealloc(SingleKey* self) {\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic PyObject*\nSingleKey_repr(PyObject *s) {\n    SingleKey *self = (SingleKey*)s;\n    char buf[128];\n    int pos = 0;\n    pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, \"SingleKey(\");\n    unsigned int mods = self->key.mods;\n    if (mods) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, \"mods=%u, \", mods);\n    if (self->key.is_native) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, \"is_native=True, \");\n    unsigned long long key = self->key.key;\n    if (key) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, \"key=%llu, \", key);\n    if (buf[pos-1] == ' ') pos -= 2;\n    pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, \")\");\n    return PyUnicode_FromString(buf);\n}\n\nstatic PyObject*\nSingleKey_get_key(SingleKey *self, void UNUSED *closure) {\n    const unsigned long long val = self->key.key;\n    return PyLong_FromUnsignedLongLong(val);\n}\n\nstatic PyObject*\nSingleKey_get_mods(SingleKey *self, void UNUSED *closure) {\n    const unsigned long mods = self->key.mods;\n    return PyLong_FromUnsignedLong(mods);\n\n}\n\nstatic PyObject*\nSingleKey_get_is_native(SingleKey *self, void UNUSED *closure) {\n    if (self->key.is_native) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nSingleKey_defined_with_kitty_mod(SingleKey *self, void UNUSED *closure) {\n    if (self->defined_with_kitty_mod || (self->key.mods & GLFW_MOD_KITTY)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\n\nstatic PyGetSetDef SingleKey_getsetters[] = {\n    {\"key\", (getter)SingleKey_get_key, NULL, \"The key as an integer\", NULL},\n    {\"mods\", (getter)SingleKey_get_mods, NULL, \"The modifiers as an integer\", NULL},\n    {\"is_native\", (getter)SingleKey_get_is_native, NULL, \"A bool\", NULL},\n    {\"defined_with_kitty_mod\", (getter)SingleKey_defined_with_kitty_mod, NULL, \"A bool\", NULL},\n    {NULL}  /* Sentinel */\n};\n\nstatic Py_hash_t\nSingleKey_hash(PyObject *self) {\n    Py_hash_t ans = ((SingleKey*)self)->key.val;\n    if (ans == -1) ans = -2;\n    return ans;\n}\n\nstatic PyObject*\nSingleKey_richcompare(PyObject *self, PyObject *other, int op) {\n    if (!PyObject_TypeCheck(other, &SingleKey_Type)) { PyErr_SetString(PyExc_TypeError, \"Cannot compare SingleKey to other objects\"); return NULL; }\n    SingleKey *a = (SingleKey*)self, *b = (SingleKey*)other;\n    Py_RETURN_RICHCOMPARE(a->key.val, b->key.val, op);\n}\n\nstatic Py_ssize_t\nSingleKey___len__(PyObject *self UNUSED) {\n    return 3;\n}\n\nstatic PyObject *\nSingleKey_item(PyObject *o, Py_ssize_t i) {\n    SingleKey *self = (SingleKey*)o;\n    switch(i) {\n        case 0:\n            return SingleKey_get_mods(self, NULL);\n        case 1:\n            return SingleKey_get_is_native(self, NULL);\n        case 2:\n            return SingleKey_get_key(self, NULL);\n    }\n    PyErr_SetString(PyExc_IndexError, \"tuple index out of range\");\n    return NULL;\n}\n\nstatic PySequenceMethods SingleKey_sequence_methods = {\n    .sq_length = SingleKey___len__,\n    .sq_item = SingleKey_item,\n};\n\nstatic PyObject*\nSingleKey_resolve_kitty_mod(SingleKey *self, PyObject *km) {\n    if (!(self->key.mods & GLFW_MOD_KITTY)) { Py_INCREF(self); return (PyObject*)self; }\n    unsigned long kitty_mod = PyLong_AsUnsignedLong(km);\n    if (PyErr_Occurred()) return NULL;\n    SingleKey *ans = (SingleKey*)SingleKey_Type.tp_alloc(&SingleKey_Type, 0);\n    if (!ans) return NULL;\n    ans->key.val = self->key.val;\n    ans->key.mods = (ans->key.mods & ~GLFW_MOD_KITTY) | kitty_mod;\n    ans->defined_with_kitty_mod = true;\n    return (PyObject*)ans;\n}\n\nstatic PyObject*\nSingleKey_replace(SingleKey *self, PyObject *args, PyObject *kw) {\n    long long key = -2; unsigned short mods = 1 << (MOD_BITS + 1); int is_native = -1;\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"|HpL\", SingleKey_kwds, &mods, &is_native, &key)) return NULL;\n    SingleKey *ans = (SingleKey*)SingleKey_Type.tp_alloc(&SingleKey_Type, 0);\n    if (ans) {\n        if (key == -1) key = 0;\n        ans->key.val = self->key.val;\n        SingleKey_set_vals(ans, key, mods, is_native);\n    }\n    return (PyObject*)ans;\n}\n\nstatic PyMethodDef SingleKey_methods[] = {\n    {\"_replace\", (PyCFunction)(void (*) (void))SingleKey_replace, METH_VARARGS | METH_KEYWORDS, \"\"},\n    {\"resolve_kitty_mod\", (PyCFunction)SingleKey_resolve_kitty_mod, METH_O, \"\"},\n    {NULL}  /* Sentinel */\n};\n\n\nstatic PyTypeObject SingleKey_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.SingleKey\",\n    .tp_basicsize = sizeof(SingleKey),\n    .tp_dealloc = (destructor)SingleKey_dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Compact and fast representation of a single key as defined in the config\",\n    .tp_new = SingleKey_new,\n    .tp_hash = SingleKey_hash,\n    .tp_richcompare = SingleKey_richcompare,\n    .tp_as_sequence = &SingleKey_sequence_methods,\n    .tp_repr = SingleKey_repr,\n    .tp_methods = SingleKey_methods,\n    .tp_getset = SingleKey_getsetters,\n}; // }}}\n\n\nbool\ninit_keys(PyObject *module) {\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    if (PyType_Ready(&PyKeyEvent_Type) < 0) return false;\n    if (PyModule_AddObject(module, \"KeyEvent\", (PyObject *)&PyKeyEvent_Type) != 0) return 0;\n    Py_INCREF(&PyKeyEvent_Type);\n    ADD_TYPE(SingleKey);\n    return true;\n}\n"
  },
  {
    "path": "kitty/keys.h",
    "content": "/*\n * keys.h\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n\n#pragma once\n#include \"data-types.h\"\n#include \"glfw-wrapper.h\"\n#include <limits.h>\n\n#define KEY_BUFFER_SIZE 128\n#define SEND_TEXT_TO_CHILD INT_MIN\n#define debug debug_input\n\nint\nencode_glfw_key_event(const GLFWkeyevent *e, const bool cursor_key_mode, const unsigned flags, char *output);\n\nbool\nis_modifier_key(const uint32_t key);\n"
  },
  {
    "path": "kitty/keys.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Callable, Iterable, Iterator\nfrom gettext import gettext as _\nfrom typing import TYPE_CHECKING, Any, Optional\n\nfrom .constants import is_macos\nfrom .fast_data_types import (\n    GLFW_MOD_ALT,\n    GLFW_MOD_CONTROL,\n    GLFW_MOD_HYPER,\n    GLFW_MOD_META,\n    GLFW_MOD_SHIFT,\n    GLFW_MOD_SUPER,\n    KeyEvent,\n    SingleKey,\n    get_boss,\n    get_options,\n    grab_keyboard,\n    is_modifier_key,\n    ring_bell,\n    set_ignore_os_keyboard_processing,\n)\nfrom .options.types import Options\nfrom .options.utils import KeyboardMode, KeyDefinition, KeyMap\nfrom .typing_compat import ScreenType\n\nif TYPE_CHECKING:\n    from .window import Window\n\nmod_mask = GLFW_MOD_ALT | GLFW_MOD_CONTROL | GLFW_MOD_SHIFT | GLFW_MOD_SUPER | GLFW_MOD_META | GLFW_MOD_HYPER\n\n\ndef keyboard_mode_name(screen: ScreenType) -> str:\n    flags = screen.current_key_encoding_flags()\n    if flags:\n        return 'kitty'\n    return 'application' if screen.cursor_key_mode else 'normal'\n\n\ndef get_shortcut(keymap: KeyMap, ev: KeyEvent) -> list[KeyDefinition] | None:\n    mods = ev.mods & mod_mask\n    ans = keymap.get(SingleKey(mods, False, ev.key))\n    if ans is None and ev.shifted_key and mods & GLFW_MOD_SHIFT:\n        ans = keymap.get(SingleKey(mods & (~GLFW_MOD_SHIFT), False, ev.shifted_key))\n    if ans is None:\n        ans = keymap.get(SingleKey(mods, True, ev.native_key))\n    return ans\n\n\ndef shortcut_matches(s: SingleKey, ev: KeyEvent) -> bool:\n    mods = ev.mods & mod_mask\n    smods = s.mods & mod_mask\n    if s.is_native:\n        return s.key == ev.native_key and smods == mods\n    if s.key == ev.key and mods == smods:\n        return True\n    if ev.shifted_key and mods & GLFW_MOD_SHIFT and (mods & ~GLFW_MOD_SHIFT) == smods and ev.shifted_key == s.key:\n        return True\n    return False\n\n\nclass Mappings:\n    \"Manage all keyboard mappings\"\n\n    def __init__(self, global_shortcuts: dict[str, SingleKey] | None = None, callback_on_mode_change: Callable[[], Any] = lambda: None) -> None:\n        self.keyboard_mode_stack: list[KeyboardMode] = []\n        self.mode_timeout_timer_id: int | None = None\n        self.update_keymap(global_shortcuts)\n        self.callback_on_mode_change = callback_on_mode_change\n\n    @property\n    def current_keyboard_mode_name(self) -> str:\n        return self.keyboard_mode_stack[-1].name if self.keyboard_mode_stack else ''\n\n    def update_keymap(self, global_shortcuts: dict[str, SingleKey] | None = None) -> None:\n        if global_shortcuts is None:\n            global_shortcuts = self.set_cocoa_global_shortcuts(self.get_options()) if is_macos else {}\n        self.global_shortcuts_map: KeyMap = {v: [KeyDefinition(definition=k)] for k, v in global_shortcuts.items()}\n        self.global_shortcuts = global_shortcuts\n        self.keyboard_modes = self.get_options().keyboard_modes.copy()\n        km = self.keyboard_modes[''].keymap\n        self.keyboard_modes[''].keymap = km = km.copy()\n        for sc in self.global_shortcuts.values():\n            km.pop(sc, None)\n\n    def clear_keyboard_modes(self) -> None:\n        had_mode = bool(self.keyboard_mode_stack)\n        self._cancel_mode_timeout()\n        self.keyboard_mode_stack = []\n        self.set_ignore_os_keyboard_processing(False)\n        if had_mode:\n            self.callback_on_mode_change()\n\n    def pop_keyboard_mode(self) -> bool:\n        passthrough = True\n        if self.keyboard_mode_stack:\n            self._cancel_mode_timeout()\n            self.keyboard_mode_stack.pop()\n            if not self.keyboard_mode_stack:\n                self.set_ignore_os_keyboard_processing(False)\n            passthrough = False\n            self.callback_on_mode_change()\n        return passthrough\n\n    def pop_keyboard_mode_if_is(self, name: str) -> bool:\n        if self.keyboard_mode_stack and self.keyboard_mode_stack[-1].name == name:\n            return self.pop_keyboard_mode()\n        return False\n\n    def _push_keyboard_mode(self, mode: KeyboardMode) -> None:\n        self.keyboard_mode_stack.append(mode)\n        self.set_ignore_os_keyboard_processing(True)\n        self._start_mode_timeout(mode)\n        self.callback_on_mode_change()\n\n    def push_keyboard_mode(self, new_mode: str) -> None:\n        mode = self.keyboard_modes[new_mode]\n        self._push_keyboard_mode(mode)\n\n    def _start_mode_timeout(self, mode: KeyboardMode) -> None:\n        if mode.timeout > 0:\n            from .fast_data_types import add_timer\n\n            self._cancel_mode_timeout()\n            self.mode_timeout_timer_id = mode.timeout_timer_id = add_timer(\n                    self._on_mode_timeout, mode.timeout, False)\n\n    def _cancel_mode_timeout(self) -> None:\n        if self.mode_timeout_timer_id is not None:\n            from .fast_data_types import remove_timer\n\n            remove_timer(self.mode_timeout_timer_id)\n            self.mode_timeout_timer_id = None\n\n    def _on_mode_timeout(self, timer_id: int | None) -> None:\n        self.mode_timeout_timer_id = None\n        if self.keyboard_mode_stack:\n            self.pop_keyboard_mode()\n\n    def _get_effective_timeout(self, key_def: KeyDefinition) -> float:\n        if key_def.options.timeout is not None:\n            return key_def.options.timeout\n        return self.get_options().map_timeout\n\n    def matching_key_actions(self, candidates: Iterable[KeyDefinition]) -> list[KeyDefinition]:\n        w = self.get_active_window()\n        matches = []\n        has_sequence_match = False\n        for x in candidates:\n            is_applicable = False\n            if x.options.when_focus_on:\n                try:\n                    if w and w in self.match_windows(x.options.when_focus_on):\n                        is_applicable = True\n                except Exception:\n                    self.clear_keyboard_modes()\n                    self.show_error(\n                        _('Invalid key mapping'), _('The match expression {0} is not valid for {1}').format(x.options.when_focus_on, '--when-focus-on')\n                    )\n                    return []\n            else:\n                is_applicable = True\n            if is_applicable:\n                matches.append(x)\n                if x.is_sequence:\n                    has_sequence_match = True\n        if has_sequence_match:\n            last_terminal_idx = -1\n            for i, x in enumerate(matches):\n                if not x.rest:\n                    last_terminal_idx = i\n            if last_terminal_idx > -1:\n                if last_terminal_idx == len(matches) - 1:\n                    matches = matches[last_terminal_idx:]\n                else:\n                    matches = matches[last_terminal_idx + 1 :]\n            q = matches[-1].options.when_focus_on\n            matches = [x for x in matches if x.options.when_focus_on == q]\n        elif matches:\n            matches = [matches[-1]]\n        return matches\n\n    def dispatch_possible_special_key(self, ev: KeyEvent) -> bool:\n        # Handles shortcuts, return True if the key was consumed\n        is_root_mode = not self.keyboard_mode_stack\n        mode = self.keyboard_modes[''] if is_root_mode else self.keyboard_mode_stack[-1]\n        key_action = get_shortcut(mode.keymap, ev)\n        if key_action is None and self.global_shortcuts_map and (global_key_action := get_shortcut(self.global_shortcuts_map, ev)) is not None:\n            if grab_keyboard(None):\n                # the shortcuts in the global menubar will have been bypassed so trigger them here\n                key_action = global_key_action\n            else:\n                return True\n        if key_action is None:\n            if is_modifier_key(ev.key):\n                return False\n            if not is_root_mode:\n                if mode.sequence_keys is not None:\n                    self.pop_keyboard_mode()\n                    w = self.get_active_window()\n                    if w is not None:\n                        w.send_key_sequence(*mode.sequence_keys)\n                    return False\n                if mode.on_unknown in ('beep', 'ignore'):\n                    if mode.on_unknown == 'beep':\n                        self.ring_bell()\n                    return True\n                if mode.on_unknown == 'passthrough':\n                    return False\n            if not self.pop_keyboard_mode():\n                self.ring_bell()\n                return True\n        else:\n            final_actions = self.matching_key_actions(key_action)\n            if final_actions:\n                mode_pos = len(self.keyboard_mode_stack) - 1\n                if final_actions[0].is_sequence:\n                    if mode.sequence_keys is None:\n                        sm = KeyboardMode('__sequence__')\n                        sm.on_action = 'end'\n                        sm.sequence_keys = [ev]\n                        sm.timeout = self._get_effective_timeout(final_actions[0])\n                        for fa in final_actions:\n                            sm.keymap[fa.rest[0]].append(fa.shift_sequence_and_copy())\n                        self._push_keyboard_mode(sm)\n                        self.debug_print('\\n\\x1b[35mKeyPress\\x1b[m matched sequence prefix, ', end='')\n                    else:\n                        if len(final_actions) == 1 and not final_actions[0].rest:\n                            self.pop_keyboard_mode()\n                            consumed = self.combine(final_actions[0].definition)\n                            if not consumed:\n                                w = self.get_active_window()\n                                if w is not None:\n                                    w.send_key_sequence(*mode.sequence_keys)\n                            return consumed\n                        mode.sequence_keys.append(ev)\n                        self._start_mode_timeout(mode)\n                        self.debug_print('\\n\\x1b[35mKeyPress\\x1b[m matched sequence prefix, ', end='')\n                        mode.keymap.clear()\n                        for fa in final_actions:\n                            mode.keymap[fa.rest[0]].append(fa.shift_sequence_and_copy())\n                    return True\n                final_action = final_actions[0]\n                consumed = self.combine(final_action.definition)\n                if consumed and not is_root_mode and mode.on_action == 'end':\n                    if mode_pos < len(self.keyboard_mode_stack) and self.keyboard_mode_stack[mode_pos] is mode:\n                        del self.keyboard_mode_stack[mode_pos]\n                        self.callback_on_mode_change()\n                        if not self.keyboard_mode_stack:\n                            self.set_ignore_os_keyboard_processing(False)\n                elif not is_root_mode and mode_pos < len(self.keyboard_mode_stack) and self.keyboard_mode_stack[mode_pos] is mode:\n                    self._start_mode_timeout(mode)\n                return consumed\n        return False\n\n    # System integration {{{\n    def get_active_window(self) -> Optional['Window']:\n        return get_boss().active_window\n\n    def match_windows(self, expr: str) -> Iterator['Window']:\n        return get_boss().match_windows(expr)\n\n    def show_error(self, title: str, msg: str) -> None:\n        return get_boss().show_error(title, msg)\n\n    def ring_bell(self) -> None:\n        if self.get_options().enable_audio_bell:\n            ring_bell()\n\n    def combine(self, action_definition: str) -> bool:\n        return get_boss().combine(action_definition)\n\n    def set_ignore_os_keyboard_processing(self, on: bool) -> None:\n        set_ignore_os_keyboard_processing(on)\n\n    def get_options(self) -> Options:\n        return get_options()\n\n    def debug_print(self, *args: Any, end: str = '\\n') -> None:\n        b = get_boss()\n        if b.args.debug_keyboard:\n            print(*args, end=end, flush=True)\n\n    def set_cocoa_global_shortcuts(self, opts: Options) -> dict[str, SingleKey]:\n        from .main import set_cocoa_global_shortcuts\n\n        return set_cocoa_global_shortcuts(opts)\n\n    # }}}\n"
  },
  {
    "path": "kitty/kittens.c",
    "content": "/*\n * kittens.c\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"monotonic.h\"\n\n#define CMD_BUF_SZ 2048\n\n\nstatic bool\nappend_buf(char buf[CMD_BUF_SZ], size_t *pos, PyObject *ans) {\n    if (*pos) {\n        PyObject *bytes = PyBytes_FromStringAndSize(buf, *pos);\n        if (!bytes) { PyErr_NoMemory(); return false; }\n        int ret = PyList_Append(ans, bytes);\n        Py_CLEAR(bytes);\n        if (ret != 0) return false;\n        *pos = 0;\n    }\n    return true;\n}\n\nstatic bool\nadd_char(char buf[CMD_BUF_SZ], size_t *pos, char ch, PyObject *ans) {\n    if (*pos >= CMD_BUF_SZ) {\n        if (!append_buf(buf, pos, ans)) return false;\n    }\n    buf[*pos] = ch;\n    *pos += 1;\n    return true;\n}\n\nstatic bool\nread_response(int fd, monotonic_t timeout, PyObject *ans) {\n    static char buf[CMD_BUF_SZ];\n    size_t pos = 0;\n    enum ReadState {START, STARTING_ESC, P, AT, K, I, T, T2, Y, HYPHEN, C, M, BODY, TRAILING_ESC};\n    enum ReadState state = START;\n    char ch;\n    monotonic_t end_time = monotonic() + timeout;\n    while(monotonic() <= end_time) {\n        ssize_t len = read(fd, &ch, 1);\n        if (len == 0) continue;\n        if (len < 0) {\n            if (errno == EINTR || errno == EAGAIN) continue;\n            PyErr_SetFromErrno(PyExc_OSError);\n            return false;\n        }\n        end_time = monotonic() + timeout;\n        switch(state) {\n            case START:\n                if (ch == 0x1b) state = STARTING_ESC;\n                if (ch == 0x03) { PyErr_SetString(PyExc_KeyboardInterrupt, \"User pressed Ctrl+C\"); return false; }\n                break;\n#define CASE(curr, q, next) case curr: state = ch == q ? next : START; break;\n            CASE(STARTING_ESC, 'P', P);\n            CASE(P, '@', AT);\n            CASE(AT, 'k', K);\n            CASE(K, 'i', I);\n            CASE(I, 't', T);\n            CASE(T, 't', T2);\n            CASE(T2, 'y', Y);\n            CASE(Y, '-', HYPHEN);\n            CASE(HYPHEN, 'c', C);\n            CASE(C, 'm', M);\n            CASE(M, 'd', BODY);\n            case BODY:\n                if (ch == 0x1b) { state = TRAILING_ESC; }\n                else {\n                    if (!add_char(buf, &pos, ch, ans)) return false;\n                }\n                break;\n            case TRAILING_ESC:\n                if (ch == '\\\\') return append_buf(buf, &pos, ans);\n                if (!add_char(buf, &pos, 0x1b, ans)) return false;\n                if (!add_char(buf, &pos, ch, ans)) return false;\n                state = BODY;\n                break;\n        }\n    }\n    PyErr_SetString(PyExc_TimeoutError,\n            \"Timed out while waiting to read command response.\"\n            \" Make sure you are running this command from within the kitty terminal.\"\n            \" If you want to run commands from outside, then you have to setup a\"\n            \" socket with the --listen-on command line flag.\");\n    return false;\n}\n\nstatic PyObject*\nread_command_response(PyObject *self UNUSED, PyObject *args) {\n    double timeout;\n    int fd;\n    PyObject *ans;\n    if (!PyArg_ParseTuple(args, \"idO!\", &fd, &timeout, &PyList_Type, &ans)) return NULL;\n    if (!read_response(fd, s_double_to_monotonic_t(timeout), ans)) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nparse_input_from_terminal(PyObject *self UNUSED, PyObject *args) {\n    enum State { NORMAL, ESC, CSI, ST, ESC_ST };\n    enum State state = NORMAL;\n    PyObject *uo, *text_callback, *dcs_callback, *csi_callback, *osc_callback, *pm_callback, *apc_callback, *callback;\n    int inbp = 0;\n    if (!PyArg_ParseTuple(args, \"OOOOOOUp\", &text_callback, &dcs_callback, &csi_callback, &osc_callback, &pm_callback, &apc_callback, &uo, &inbp)) return NULL;\n    Py_ssize_t sz = PyUnicode_GET_LENGTH(uo), pos = 0, start = 0, count = 0, consumed = 0;\n    callback = text_callback;\n    int kind = PyUnicode_KIND(uo);\n    void *data = PyUnicode_DATA(uo);\n    bool in_bracketed_paste_mode = inbp != 0;\n#define CALL(cb, s_, num_) {\\\n    PyObject *fcb = cb; \\\n    Py_ssize_t s = s_, num = num_; \\\n    if (in_bracketed_paste_mode && fcb != text_callback) { \\\n        fcb = text_callback; num += 2; s -= 2; \\\n    } \\\n    if (num > 0) { \\\n        PyObject *ret = PyObject_CallFunction(fcb, \"N\", PyUnicode_Substring(uo, s, s + num));  \\\n        if (ret == NULL) return NULL; \\\n        Py_DECREF(ret); \\\n    } \\\n    consumed = s_ + num_; \\\n    count = 0; \\\n}\n    START_ALLOW_CASE_RANGE;\n    while (pos < sz) {\n        Py_UCS4 ch = PyUnicode_READ(kind, data, pos);\n        switch(state) {\n            case NORMAL:\n                if (ch == 0x1b) {\n                    state = ESC;\n                    CALL(text_callback, start, count);\n                    start = pos;\n                } else count++;\n                break;\n            case ESC:\n                start = pos;\n                count = 0;\n                switch(ch) {\n                    case 'P':\n                        state = ST; callback = dcs_callback; break;\n                    case '[':\n                        state = CSI; callback = csi_callback; break;\n                    case ']':\n                        state = ST; callback = osc_callback; break;\n                    case '^':\n                        state = ST; callback = pm_callback; break;\n                    case '_':\n                        state = ST; callback = apc_callback; break;\n                    default:\n                        state = NORMAL; break;\n                }\n                break;\n            case CSI:\n                count++;\n                switch (ch) {\n                    case 'a' ... 'z':\n                    case 'A' ... 'Z':\n                    case '@':\n                    case '`':\n                    case '{':\n                    case '|':\n                    case '}':\n                    case '~':\n#define IBP(w)  ch == '~' && PyUnicode_READ(kind, data, start + 1) == '2' && PyUnicode_READ(kind, data, start + 2) == '0' && PyUnicode_READ(kind, data, start + 3) == w\n                        if (IBP('1')) in_bracketed_paste_mode = false;\n                        CALL(callback, start + 1, count);\n                        if (IBP('0')) in_bracketed_paste_mode = true;\n#undef IBP\n                        state = NORMAL;\n                        start = pos + 1;\n                        break;\n                }\n                break;\n            case ESC_ST:\n                if (ch == '\\\\') {\n                    CALL(callback, start + 1, count);\n                    state = NORMAL; start = pos + 1;\n                    consumed += 2;\n                } else count += 2;\n                break;\n            case ST:\n                if (ch == 0x1b) { state = ESC_ST; }\n                else count++;\n                break;\n        }\n        pos++;\n    }\n    if (state == NORMAL && count > 0) CALL(text_callback, start, count);\n    return PyUnicode_Substring(uo, consumed, sz);\n    END_ALLOW_CASE_RANGE;\n#undef CALL\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(parse_input_from_terminal, METH_VARARGS),\n    METHODB(read_command_response, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nbool\ninit_kittens(PyObject *module) {\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    return true;\n}\n"
  },
  {
    "path": "kitty/kitty-verstable.h",
    "content": "/*\n * kitty-verstable.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"../3rdparty/verstable.h\"\n#include <stdint.h>\n#include <xxhash.h>\n\n#ifndef __kitty_verstable_extra_hash_functions__\n#define __kitty_verstable_extra_hash_functions__\n\n#define vt_hash_bytes XXH3_64bits\n\nstatic inline uint64_t vt_hash_float(float x) { return vt_hash_integer((uint64_t)x); }\nstatic inline bool vt_cmpr_float(float a, float b) { return a == b; }\n#define vt_create_for_loop(itr_type, itr, table) for (itr_type itr = vt_first(table); !vt_is_end(itr); itr = vt_next(itr))\n\n#endif\n"
  },
  {
    "path": "kitty/launch.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport shutil\nfrom collections.abc import Container, Iterable, Iterator, Sequence\nfrom contextlib import suppress\nfrom typing import Any, Callable, Literal, NamedTuple, TypedDict\n\nfrom .boss import Boss\nfrom .child import Child\nfrom .cli import parse_args\nfrom .cli_stub import LaunchCLIOptions\nfrom .clipboard import set_clipboard_string, set_primary_selection\nfrom .fast_data_types import add_timer, get_boss, get_options, get_os_window_title, patch_color_profiles\nfrom .options.utils import env as parse_env\nfrom .tabs import Tab, TabManager\nfrom .types import LayerShellConfig, OverlayType, run_once\nfrom .utils import get_editor, log_error, resolve_custom_file, which\nfrom .window import CwdRequest, CwdRequestType, Watchers, Window\n\n\nclass LaunchSpec(NamedTuple):\n    opts: LaunchCLIOptions\n    args: list[str]\n\n\n# Options definition {{{\nenv_docs = '''\\\ntype=list\nEnvironment variables to set in the child process. Can be specified multiple\ntimes to set different environment variables. Syntax: :code:`name=value`. Using\n:code:`name=` will set to empty string and just :code:`name` will remove the\nenvironment variable.\n'''\n\n\nremote_control_password_docs = '''\\\ntype=list\nRestrict the actions remote control is allowed to take. This works like\n:opt:`remote_control_password`. You can specify a password and list of actions\njust as for :opt:`remote_control_password`. For example::\n\n    --remote-control-password '\"my passphrase\" get-* set-colors'\n\nThis password will be in effect for this window only.\nNote that any passwords you have defined for :opt:`remote_control_password`\nin :file:`kitty.conf` are also in effect. You can override them by using the same password here.\nYou can also disable all :opt:`remote_control_password` global passwords for this window, by using::\n\n    --remote-control-password '!'\n\nThis option only takes effect if :option:`--allow-remote-control`\nis also specified. Can be specified multiple times to create multiple passwords.\nThis option was added to kitty in version 0.26.0\n'''\n\n\n@run_once\ndef options_spec() -> str:\n    return f\"\"\"\n--source-window\nA match expression to select the window from which data such as title, colors, env vars,\nscreen contents, etc. are copied. When not specified the currently active window is used.\nSee :ref:`search_syntax` for the syntax used for specifying windows.\n\n\n--window-title --title\nThe title to set for the new window. By default, title is controlled by the\nchild process. The special value :code:`current` will copy the title from the\n:option:`source window <launch --source-window>`.\n\n\n--tab-title\nThe title for the new tab if launching in a new tab. By default, the title\nof the active window in the tab is used as the tab title. The special value\n:code:`current` will copy the title from the tab containing the\n:option:`source window <launch --source-window>`.\n\n\n--type\ntype=choices\ndefault=window\nchoices=window,tab,os-window,os-panel,overlay,overlay-main,background,clipboard,primary\nWhere to launch the child process:\n\n:code:`window`\n    A new :term:`kitty window <window>` in the current tab\n\n:code:`tab`\n    A new :term:`tab` in the current OS window. Not available when the\n    :doc:`launch <launch>` command is used in :ref:`startup sessions <sessions>`.\n\n:code:`os-window`\n    A new :term:`operating system window <os_window>`.  Not available when the\n    :doc:`launch <launch>` command is used in :ref:`startup sessions <sessions>`.\n\n:code:`overlay`\n    An :term:`overlay window <overlay>` covering the current active kitty window\n\n:code:`overlay-main`\n    An :term:`overlay window <overlay>` covering the current active kitty window.\n    Unlike a plain overlay window, this window is considered as a :italic:`main`\n    window which means it is used as the active window for getting the current working\n    directory, the input text for kittens, launch commands, etc. Useful if this overlay is\n    intended to run for a long time as a primary window.\n\n:code:`background`\n    The process will be run in the :italic:`background`, without a kitty\n    window. Note that if :option:`kitten @ launch --allow-remote-control` is\n    specified the :envvar:`KITTY_LISTEN_ON` environment variable will be set to\n    a dedicated socket pair file descriptor that the process can use for remote\n    control.\n\n:code:`clipboard`, :code:`primary`\n    These two are meant to work with :option:`--stdin-source <launch --stdin-source>` to copy\n    data to the :italic:`system clipboard` or :italic:`primary selection`.\n\n:code:`os-panel`\n    Similar to :code:`os-window`, except that it creates the new OS Window as a desktop panel.\n    Only works on platforms that support this, such as Wayand compositors that support the layer\n    shell protocol. Use the :option:`kitten @ launch --os-panel` option to configure the panel.\n\n#placeholder_for_formatting#\n\n\n--keep-focus --dont-take-focus\ntype=bool-set\nKeep the focus on the currently active window instead of switching to the newly\nopened window.\n\n\n--cwd\ncompletion=type:directory kwds:current,oldest,last_reported,root\nThe working directory for the newly launched child. Use the special value\n:code:`current` to use the working directory of the :option:`source window <launch --source-window>`.\nThe special value :code:`last_reported` uses the last working directory reported\nby the shell (needs :ref:`shell_integration` to work). The special value\n:code:`oldest` works like :code:`current` but uses the working directory of the\noldest foreground process associated with the currently active window rather\nthan the newest foreground process. Finally, the special value :code:`root`\nrefers to the process that was originally started when the window was created.\n\nWhen opening in the same working directory as the current window causes the new\nwindow to connect to a remote host, you can use the :option:`--hold-after-ssh`\nflag to prevent the new window from closing when the connection is terminated.\n\n\n--add-to-session\nAdd the newly created window/tab to the specified session. Use :code:`.` to add\nto the session of the :option:`source window <launch --source-window>`, if any. See :ref:`sessions`\nfor what a session is and how to use one. By default, the window is added to the\nsession of the :option:`source window <launch --source-window>` when :option:`launch --cwd`\nis set to use the the working directory from that window, otherwise the newly created window\ndoes not belong to any session. To force adding to no session, use the value :code:`!`.\nAdding a newly created window to a session is purely temporary, it does not change the actual\nsession file, for that you have to resave the session. Note that using this flag in a launch\ncommand within a session file has no effect as the window is always added to the\nsession belonging to that file.\n\n\n--env\n{env_docs}\n\n\n--var\ntype=list\nUser variables to set in the created window. Can be specified multiple\ntimes to set different user variables. Syntax: :code:`name=value`. Using\n:code:`name=` will set to empty string.\n\n\n--hold\ntype=bool-set\nKeep the window open even after the command being executed exits, at a shell prompt.\nThe shell will be run after the launched command exits.\n\n\n--copy-colors\ntype=bool-set\nSet the colors of the newly created window to be the same as the colors in the\n:option:`source window <launch --source-window>`.\n\n\n--copy-cmdline\ntype=bool-set\nIgnore any specified command line and instead use the command line from the\n:option:`source window <launch --source-window>`.\n\n\n--copy-env\ntype=bool-set\nCopy the environment variables from the :option:`source window <launch --source-window>` into the newly\nlaunched child process. Note that this only copies the environment when the\nwindow was first created, as it is not possible to get updated environment variables\nfrom arbitrary processes. To copy that environment, use either the :ref:`clone-in-kitty\n<clone_shell>` feature or the kitty remote control feature with :option:`kitten @ launch --copy-env`.\n\n\n--location\ntype=choices\ndefault=default\nchoices=first,after,before,neighbor,last,vsplit,hsplit,split,default\nWhere to place the newly created window when it is added to a tab which already\nhas existing windows in it. :code:`after` and :code:`before` place the new\nwindow before or after the active window. :code:`neighbor` is a synonym for\n:code:`after`. Also applies to creating a new tab, where the value of\n:code:`after` will cause the new tab to be placed next to the current tab\ninstead of at the end. The values of :code:`vsplit`, :code:`hsplit` and\n:code:`split` are only used by the :code:`splits` layout and control if the new\nwindow is placed in a vertical, horizontal or automatic split with the currently\nactive window. The default is to place the window in a layout dependent manner,\ntypically, after the currently active window. See :option:`--next-to <launch --next-to>`\nto use a window other than the currently active window.\n\n\n--next-to\nA match expression to select the window next to which the new window is created.\nSee :ref:`search_syntax` for the syntax for specifying windows. If not specified\ndefaults to the active window. When used via remote control\nthis option is ignored unless the matched window is in the target tab (default active tab).\nWhen using :option:`--type <launch --type>` of :code:`tab`, the tab will be created\nin the OS Window containing the matched window.\n\n\n--bias\ntype=float\ndefault=0\nThe bias used to alter the size of the window.\nIt controls what fraction of available space the window takes. The exact meaning\nof bias depends on the current layout.\n\n* Splits layout: The bias is interpreted as a percentage between 0 and 100.\nWhen splitting a window into two, the new window will take up the specified fraction\nof the space allotted to the original window and the original window will take up\nthe remainder of the space.\n\n* Vertical/horizontal layout: The bias is interpreted as adding/subtracting from the\nnormal size of the window. It should be a number between -90 and 90. This number is\nthe percentage of the OS Window size that should be added to the window size.\nSo for example, if a window would normally have been size 50 in the layout inside an\nOS Window that is size 80 high and --bias -10 is used it will become *approximately*\nsize 42 high. Note that sizes are approximations, you cannot use this method to\ncreate windows of fixed sizes.\n\n* Tall layout: If the window being created is the *first* window in a column, then\nthe bias is interpreted as a percentage, as for the splits layout, splitting the OS\nWindow width between columns. If the window is a second or subsequent window in a column\nthe bias is interpreted as adding/subtracting from the window size as for the vertical\nlayout above.\n\n* Fat layout: Same as tall layout except it goes by rows instead of columns.\n\n* Grid layout: The bias is interpreted the same way as for the Vertical and Horizontal\nlayouts, as something to be added/subtracted to the normal size. However, the\nsince in a grid layout there are rows *and* columns, the bias on the first window in a column\noperates on the columns. Any later windows in that column operate on the row.\nSo, for example, if you bias the first window in a grid layout it will change the width\nof the first column, the second window, the width of the second column, the third window,\nthe height of the second row and so on.\n\nThe bias option was introduced in kitty version 0.36.0.\n\n\n--allow-remote-control\ntype=bool-set\nPrograms running in this window can control kitty (even if remote control is not\nenabled in :file:`kitty.conf`). Note that any program with the right level of\npermissions can still write to the pipes of any other program on the same\ncomputer and therefore can control kitty. It can, however, be useful to block\nprograms running on other computers (for example, over SSH) or as other users.\nSee :option:`--remote-control-password` for ways to restrict actions allowed by\nremote control.\n\n\n--remote-control-password\n{remote_control_password_docs}\n\n--stdin-source\ntype=choices\ndefault=none\nchoices=none,@selection,@screen,@screen_scrollback,@alternate,@alternate_scrollback,@first_cmd_output_on_screen,@last_cmd_output,@last_visited_cmd_output\nPass the screen contents as :file:`STDIN` to the child process.\n\n:code:`@selection`\n    is the currently selected text in the :option:`source window <launch --source-window>`.\n\n:code:`@screen`\n    is the contents of the :option:`source window <launch --source-window>`.\n\n:code:`@screen_scrollback`\n    is the same as :code:`@screen`, but includes the scrollback buffer as well.\n\n:code:`@alternate`\n    is the secondary screen of the :option:`source window <launch --source-window>`. For example if you run\n    a full screen terminal application, the secondary screen will\n    be the screen you return to when quitting the application.\n\n:code:`@first_cmd_output_on_screen`\n    is the output from the first command run in the shell on screen.\n\n:code:`@last_cmd_output`\n    is the output from the last command run in the shell.\n\n:code:`@last_visited_cmd_output`\n    is the first output below the last scrolled position via :ac:`scroll_to_prompt`,\n    this needs :ref:`shell integration <shell_integration>` to work.\n\n#placeholder_for_formatting#\n\n\n--stdin-add-formatting\ntype=bool-set\nWhen using :option:`--stdin-source <launch --stdin-source>` add formatting\nescape codes, without this only plain text will be sent.\n\n\n--stdin-add-line-wrap-markers\ntype=bool-set\nWhen using :option:`--stdin-source <launch --stdin-source>` add a carriage\nreturn at every line wrap location (where long lines are wrapped at screen\nedges). This is useful if you want to pipe to program that wants to duplicate\nthe screen layout of the screen.\n\n\n--marker\nCreate a marker that highlights text in the newly created window. The syntax is\nthe same as for the :ac:`toggle_marker` action (see :doc:`/marks`).\n\n\n--os-window-class\nSet the :italic:`WM_CLASS` property on X11 and the application id property on\nWayland for the newly created OS window when using :option:`--type=os-window\n<launch --type>`. Defaults to whatever is used by the parent kitty process,\nwhich in turn defaults to :code:`kitty`.\n\n\n--os-window-name\nSet the :italic:`WM_NAME` property on X11 for the newly created OS Window when\nusing :option:`--type=os-window <launch --type>`. Defaults to\n:option:`--os-window-class <launch --os-window-class>`.\n\n\n--os-window-title\nSet the title for the newly created OS window. This title will override any\ntitles set by programs running in kitty. The special value :code:`current` will\ncopy the title from the OS Window containing the\n:option:`source window <launch --source-window>`.\n\n\n--os-window-state\ntype=choices\ndefault=normal\nchoices=normal,fullscreen,maximized,minimized\nThe initial state for the newly created OS Window.\n\n\n--logo\ncompletion=type:file ext:png group:\"PNG images\" relative:conf\nPath to a PNG image to use as the logo for the newly created window. See\n:opt:`window_logo_path`. Relative paths are resolved from the kitty configuration directory.\n\n\n--logo-position\nThe position for the window logo. Only takes effect if :option:`--logo` is\nspecified. See :opt:`window_logo_position`.\n\n\n--logo-alpha\ntype=float\ndefault=-1\nThe amount the window logo should be faded into the background. Only takes\neffect if :option:`--logo` is specified. See :opt:`window_logo_alpha`.\n\n\n--color\ntype=list\nChange colors in the newly launched window. You can either specify a path to a\n:file:`.conf` file with the same syntax as :file:`kitty.conf` to read the colors\nfrom, or specify them individually, for example::\n\n    --color background=white --color foreground=red\n\n\n--spacing\ntype=list\nSet the margin and padding for the newly created window.\nFor example: :code:`margin=20` or :code:`padding-left=10` or :code:`margin-h=30`. The shorthand form sets\nall values, the :code:`*-h` and :code:`*-v` variants set horizontal and vertical values.\nCan be specified multiple times. Note that this is ignored for overlay windows as these use the settings\nfrom the base window.\n\n\n--watcher -w\ntype=list\ncompletion=type:file ext:py relative:conf group:\"Python scripts\"\nPath to a Python file. Appropriately named functions in this file will be called\nfor various events, such as when the window is resized, focused or closed. See\nthe section on watchers in the launch command documentation: :ref:`watchers`.\nRelative paths are resolved relative to the :ref:`kitty config directory\n<confloc>`. Global watchers for all windows can be specified with\n:opt:`watcher` in :file:`kitty.conf`.\n\n\n--os-panel\ntype=list\nOptions to control the creation of desktop panels. Takes the same settings\nas the :doc:`panel kitten </kittens/panel>`, except for :option:`--override <kitty +kitten panel --override>`\nand :option:`--config <kitty +kitten panel --config>`. Can be specified multiple times.\nFor example, to create a desktop panel at the bottom of the screen two lines high::\n\n    launch --type os-panel --os-panel lines=2 --os-panel edge=bottom sh -c \"echo; echo; echo hello; sleep 5s\"\n\n\n--hold-after-ssh\ntype=bool-set\nWhen using :option:`--cwd`:code:`=current` or similar from a window that is running the ssh kitten,\nthe new window will run a local shell after disconnecting from the remote host, when this option\nis specified.\n\"\"\"\n# }}}\n\n\ndef parse_launch_args(args: Sequence[str] | None = None) -> LaunchSpec:\n    args = list(args or ())\n    try:\n        opts, args = parse_args(result_class=LaunchCLIOptions, args=args, ospec=options_spec)\n    except SystemExit as e:\n        raise ValueError(str(e)) from e\n    return LaunchSpec(opts, args)\n\n\ndef get_env(opts: LaunchCLIOptions, active_child: Child | None = None, base_env: dict[str,str] | None = None) -> dict[str, str]:\n    env: dict[str, str] = {}\n    if opts.copy_env and active_child:\n        env.update(active_child.foreground_environ)\n    if base_env is not None:\n        env.update(base_env)\n    for x in opts.env:\n        for k, v in parse_env(x, env):\n            env[k] = v\n    return env\n\n\ndef layer_shell_config_from_panel_opts(panel_opts: Iterable[str]) -> LayerShellConfig:\n    from kittens.panel.main import layer_shell_config, parse_panel_args\n    args = [('' if x.startswith('--') else '--') + x for x in panel_opts]\n    try:\n        opts, _ = parse_panel_args(args)\n    except SystemExit as e:\n        raise ValueError(str(e))\n    return layer_shell_config(opts)\n\n\ndef tab_for_window(boss: Boss, opts: LaunchCLIOptions, target_tab: Tab | None, next_to: Window | None, add_to_session: str) -> Tab:\n\n    def create_tab(tm: TabManager | None = None) -> Tab:\n        if tm is None:\n            if opts.type == 'os-panel':\n                oswid = boss.add_os_panel(layer_shell_config_from_panel_opts(opts.os_panel), opts.os_window_class, opts.os_window_name)\n            else:\n                oswid = boss.add_os_window(\n                    wclass=opts.os_window_class,\n                    wname=opts.os_window_name,\n                    window_state=opts.os_window_state,\n                    override_title=opts.os_window_title or None)\n            tm = boss.os_window_map[oswid]\n        tab = tm.new_tab(empty_tab=True, location=opts.location)\n        if opts.tab_title:\n            tab.set_title(opts.tab_title)\n        tab.created_in_session_name = add_to_session\n        return tab\n\n    if opts.type == 'tab':\n        tm = boss.active_tab_manager\n        if next_to is not None and (tab := next_to.tabref()) is not None and (qtm := tab.tab_manager_ref()) is not None:\n            tm = qtm\n        if target_tab is not None:\n            tm = target_tab.tab_manager_ref() or tm\n        tab = create_tab(tm)\n    elif opts.type in ('os-window', 'os-panel'):\n        tab = create_tab()\n    else:\n        if target_tab is not None:\n            tab = target_tab\n        else:\n            tab = boss.active_tab\n            if next_to is not None and (qtab := next_to.tabref()) is not None:\n                tab = qtab\n            tab = tab or create_tab()\n    return tab\n\n\nwatcher_modules: dict[str, Any] = {}\n\n\ndef load_watch_modules(watchers: Iterable[str]) -> Watchers | None:\n    if not watchers:\n        return None\n    import runpy\n    ans = Watchers()\n    boss = get_boss()\n    for path in watchers:\n        path = resolve_custom_file(path)\n        m = watcher_modules.get(path, None)\n        if m is None:\n            try:\n                m = runpy.run_path(path, run_name='__kitty_watcher__')\n            except Exception as err:\n                import traceback\n                log_error(traceback.format_exc())\n                log_error(f'Failed to load watcher from {path} with error: {err}')\n                watcher_modules[path] = False\n                continue\n            watcher_modules[path] = m\n            w = m.get('on_load')\n            if callable(w):\n                try:\n                    w(boss, {})\n                except Exception as err:\n                    import traceback\n                    log_error(traceback.format_exc())\n                    log_error(f'Failed to call on_load() in watcher from {path} with error: {err}')\n        if m is False:\n            continue\n        w = m.get('on_close')\n        if callable(w):\n            ans.on_close.append(w)\n        w = m.get('on_resize')\n        if callable(w):\n            ans.on_resize.append(w)\n        w = m.get('on_focus_change')\n        if callable(w):\n            ans.on_focus_change.append(w)\n        w = m.get('on_set_user_var')\n        if callable(w):\n            ans.on_set_user_var.append(w)\n        w = m.get('on_title_change')\n        if callable(w):\n            ans.on_title_change.append(w)\n        w = m.get('on_cmd_startstop')\n        if callable(w):\n            ans.on_cmd_startstop.append(w)\n        w = m.get('on_color_scheme_preference_change')\n        if callable(w):\n            ans.on_color_scheme_preference_change.append(w)\n        w = m.get('on_tab_bar_dirty')\n        if callable(w):\n            ans.on_tab_bar_dirty.append(w)\n        w = m.get('on_quit')\n        if callable(w):\n            ans.on_quit.append(w)\n    return ans\n\n\nclass LaunchKwds(TypedDict):\n\n    allow_remote_control: bool\n    remote_control_passwords: dict[str, Sequence[str]] | None\n    cwd_from: CwdRequest | None\n    cwd: str | None\n    location: str | None\n    override_title: str | None\n    copy_colors_from: Window | None\n    marker: str | None\n    cmd: list[str] | None\n    overlay_for: int | None\n    stdin: bytes | None\n    hold: bool\n    bias: float | None\n    hold_after_ssh: bool\n\n\ndef apply_colors(window: Window, spec: Sequence[str]) -> None:\n    from .colors import parse_colors\n    colors, transparent_background_colors = parse_colors(spec)\n    profiles = window.screen.color_profile,\n    patch_color_profiles(colors, transparent_background_colors, profiles, True)\n\n\ndef parse_var(defn: Iterable[str]) -> Iterator[tuple[str, str]]:\n    for item in defn:\n        a, sep, b = item.partition('=')\n        yield a, b\n\n\nclass ForceWindowLaunch:\n\n    def __init__(self) -> None:\n        self.force = False\n\n    def __bool__(self) -> bool:\n        return self.force\n\n    def __call__(self, force: bool) -> 'ForceWindowLaunch':\n        self.force = force\n        return self\n\n    def __enter__(self) -> None:\n        pass\n\n    def __exit__(self, *a: object) -> None:\n        self.force = False\n\n\nforce_window_launch = ForceWindowLaunch()\nnon_window_launch_types = 'background', 'clipboard', 'primary'\n\n\ndef parse_remote_control_passwords(allow_remote_control: bool, passwords: Sequence[str]) -> dict[str, Sequence[str]] | None:\n    remote_control_restrictions: dict[str, Sequence[str]] | None = None\n    if allow_remote_control and passwords:\n        from kitty.options.utils import remote_control_password\n        remote_control_restrictions = {}\n        for rcp in passwords:\n            for pw, rcp_items in remote_control_password(rcp, {}):\n                remote_control_restrictions[pw] = rcp_items\n    return remote_control_restrictions\n\n\ndef _launch(\n    boss: Boss,\n    opts: LaunchCLIOptions,\n    args: list[str],\n    target_tab: Tab | None = None,\n    force_target_tab: bool = False,\n    is_clone_launch: str = '',\n    rc_from_window: Window | None = None,\n    base_env: dict[str, str] | None = None,\n    child_death_callback: Callable[[int, Exception | None], None] | None = None,\n    startup_command_via_shell_integration: Sequence[str] | str = (),\n) -> Window | None:\n    source_window = boss.active_window_for_cwd\n    if opts.source_window:\n        for qw in boss.match_windows(opts.source_window, rc_from_window):\n            source_window = qw\n            break\n    source_child = source_window.child if source_window else None\n    next_to = boss.active_window\n    if opts.next_to:\n        for qw in boss.match_windows(opts.next_to, rc_from_window):\n            next_to = qw\n            break\n    if target_tab and next_to and next_to not in target_tab:\n        next_to = None\n    if opts.window_title == 'current':\n        opts.window_title = source_window.title if source_window else None\n    if opts.tab_title == 'current':\n        atab = boss.active_tab\n        if source_window and (qt := source_window.tabref()):\n            atab = qt\n        opts.tab_title = atab.effective_title if atab else None\n    if opts.os_window_title == 'current':\n        tm = boss.active_tab_manager\n        if source_window and (qt := source_window.tabref()) and (qr := qt.tab_manager_ref()):\n            tm = qr\n        opts.os_window_title = get_os_window_title(tm.os_window_id) if tm else None\n    env = get_env(opts, source_child, base_env)\n    kw: LaunchKwds = {\n        'allow_remote_control': opts.allow_remote_control,\n        'remote_control_passwords': parse_remote_control_passwords(opts.allow_remote_control, opts.remote_control_password),\n        'cwd_from': None,\n        'cwd': None,\n        'location': None,\n        'override_title': opts.window_title or None,\n        'copy_colors_from': None,\n        'marker': opts.marker or None,\n        'cmd': None,\n        'overlay_for': None,\n        'stdin': None,\n        'hold': False,\n        'bias': None,\n        'hold_after_ssh': False\n    }\n    spacing = {}\n    if opts.spacing:\n        from .rc.set_spacing import parse_spacing_settings, patch_window_edges\n        spacing = parse_spacing_settings(opts.spacing)\n    if opts.bias:\n        kw['bias'] = max(-100, min(opts.bias, 100))\n    if opts.cwd:\n        if opts.cwd == 'current':\n            if source_window:\n                kw['cwd_from'] = CwdRequest(source_window)\n        elif opts.cwd == 'last_reported':\n            if source_window:\n                kw['cwd_from'] = CwdRequest(source_window, CwdRequestType.last_reported)\n        elif opts.cwd == 'oldest':\n            if source_window:\n                kw['cwd_from'] = CwdRequest(source_window, CwdRequestType.oldest)\n        elif opts.cwd == 'root':\n            if source_window:\n                kw['cwd_from'] = CwdRequest(source_window, CwdRequestType.root)\n        else:\n            kw['cwd'] = opts.cwd\n    if opts.hold_after_ssh:\n        if opts.cwd not in ('current', 'last_reported', 'oldest'):\n            raise ValueError(\"--hold-after-ssh can only be supplied if --cwd=current or similar is also supplied\")\n        kw['hold_after_ssh'] = True\n\n    if opts.location != 'default':\n        kw['location'] = opts.location\n    if opts.copy_colors and source_window:\n        kw['copy_colors_from'] = source_window\n    pipe_data: dict[str, Any] = {}\n    if opts.stdin_source != 'none':\n        q = str(opts.stdin_source)\n        if opts.stdin_add_formatting:\n            if q in ('@screen', '@screen_scrollback', '@alternate', '@alternate_scrollback',\n                     '@first_cmd_output_on_screen', '@last_cmd_output', '@last_visited_cmd_output'):\n                q = f'@ansi_{q[1:]}'\n        if opts.stdin_add_line_wrap_markers:\n            q += '_wrap'\n        penv, stdin = boss.process_stdin_source(window=source_window, stdin=q, copy_pipe_data=pipe_data)\n        if stdin:\n            kw['stdin'] = stdin\n            if penv:\n                env.update(penv)\n\n    cmd = args or None\n    if opts.copy_cmdline and source_child:\n        cmd = source_child.foreground_cmdline\n    active = boss.active_window\n    if cmd:\n        final_cmd: list[str] = []\n        for x in cmd:\n            if source_window and not opts.copy_cmdline:\n                if x == '@selection':\n                    s = boss.data_for_at(which=x, window=source_window)\n                    if s:\n                        x = s\n                elif x == '@active-kitty-window-id':\n                    if active:\n                        x = str(active.id)\n                elif x == '@input-line-number':\n                    if 'input_line_number' in pipe_data:\n                        x = str(pipe_data['input_line_number'])\n                elif x == '@line-count':\n                    if 'lines' in pipe_data:\n                        x = str(pipe_data['lines'])\n                elif x in ('@cursor-x', '@cursor-y', '@scrolled-by', '@first-line-on-screen', '@last-line-on-screen'):\n                    if source_window is not None:\n                        screen = source_window.screen\n                        if x == '@scrolled-by':\n                            x = str(screen.scrolled_by)\n                        elif x == '@cursor-x':\n                            x = str(screen.cursor.x + 1)\n                        elif x == '@cursor-y':\n                            x = str(screen.cursor.y + 1)\n                        elif x == '@first-line-on-screen':\n                            x = str(screen.visual_line(0) or '')\n                        elif x == '@last-line-on-screen':\n                            x = str(screen.visual_line(screen.lines - 1) or '')\n            final_cmd.append(x)\n        if rc_from_window is None and final_cmd:\n            exe = which(final_cmd[0])\n            if exe:\n                final_cmd[0] = exe\n        kw['cmd'] = final_cmd\n    if force_window_launch and opts.type not in non_window_launch_types:\n        opts.type = 'window'\n    if next_to and opts.type in non_window_launch_types:\n        next_to = None\n    base_for_overlay = next_to\n    if target_tab and (not base_for_overlay or base_for_overlay not in target_tab):\n        base_for_overlay = target_tab.active_window\n    if opts.type in ('overlay', 'overlay-main') and base_for_overlay:\n        kw['overlay_for'] = base_for_overlay.id\n    if opts.type == 'background':\n        cmd = kw['cmd']\n        if not cmd:\n            raise ValueError('The cmd to run must be specified when running a background process')\n        boss.run_background_process(\n            cmd, cwd=kw['cwd'], cwd_from=kw['cwd_from'], env=env or None, stdin=kw['stdin'],\n            allow_remote_control=kw['allow_remote_control'], remote_control_passwords=kw['remote_control_passwords'],\n            notify_on_death=child_death_callback,\n        )\n    elif opts.type in ('clipboard', 'primary'):\n        stdin = kw.get('stdin')\n        if stdin is not None:\n            if opts.type == 'clipboard':\n                set_clipboard_string(stdin)\n                boss.handle_clipboard_loss('clipboard')\n            else:\n                set_primary_selection(stdin)\n                boss.handle_clipboard_loss('primary')\n        if child_death_callback is not None:\n            child_death_callback(0, None)\n    else:\n        add_to_session = opts.add_to_session or ''\n        match add_to_session:\n            case '.':\n                add_to_session = source_window.created_in_session_name if source_window else ''\n            case '!':\n                add_to_session = ''\n            case '':\n                if kw['cwd_from'] is not None and source_window:\n                    add_to_session = source_window.created_in_session_name\n        kw['hold'] = opts.hold\n        if force_target_tab and target_tab is not None:\n            tab = target_tab\n        else:\n            tab = tab_for_window(boss, opts, target_tab, next_to, add_to_session)\n        watchers = load_watch_modules(opts.watcher)\n        with Window.set_ignore_focus_changes_for_new_windows(opts.keep_focus):\n            new_window: Window = tab.new_window(\n                env=env or None, watchers=watchers or None, is_clone_launch=is_clone_launch, next_to=next_to,\n                startup_command_via_shell_integration=startup_command_via_shell_integration, **kw)\n            new_window.created_in_session_name = add_to_session\n            if child_death_callback is not None:\n                boss.monitor_pid(new_window.child.pid or 0, child_death_callback)\n            if new_window.creation_spec:\n                if opts.watcher:\n                    new_window.creation_spec = new_window.creation_spec._replace(watchers=tuple(opts.watcher))\n                if opts.spacing:\n                    new_window.creation_spec = new_window.creation_spec._replace(spacing=tuple(opts.spacing))\n                if opts.color:\n                    new_window.creation_spec = new_window.creation_spec._replace(colors=tuple(opts.color))\n        if spacing:\n            patch_window_edges(new_window, spacing)\n            tab.relayout()\n        if opts.color:\n            apply_colors(new_window, opts.color)\n        if opts.keep_focus:\n            if active:\n                boss.set_active_window(active, switch_os_window_if_needed=True, for_keep_focus=True)\n            if not Window.initial_ignore_focus_changes_context_manager_in_operation:\n                new_window.ignore_focus_changes = False\n        if opts.logo:\n            new_window.set_logo(opts.logo, opts.logo_position or '', opts.logo_alpha)\n        if opts.type == 'overlay-main':\n            new_window.overlay_type = OverlayType.main\n        if opts.var:\n            vars = tuple(parse_var(opts.var))\n            if new_window.creation_spec:\n                new_window.creation_spec = new_window.creation_spec._replace(user_vars=vars)\n            for key, val in vars:\n                new_window.set_user_var(key, val)\n        return new_window\n    return None\n\n\ndef launch(\n    boss: Boss,\n    opts: LaunchCLIOptions,\n    args: list[str],\n    target_tab: Tab | None = None,\n    force_target_tab: bool = False,\n    is_clone_launch: str = '',\n    rc_from_window: Window | None = None,\n    base_env: dict[str, str] | None = None,\n    child_death_callback: Callable[[int, Exception | None], None] | None = None,\n    startup_command_via_shell_integration: Sequence[str] | str = (),\n) -> Window | None:\n    active = boss.active_window\n    if opts.keep_focus and active:\n        orig, active.ignore_focus_changes = active.ignore_focus_changes, True\n    try:\n        return _launch(\n            boss, opts, args, target_tab, force_target_tab, is_clone_launch, rc_from_window, base_env,\n            child_death_callback, startup_command_via_shell_integration)\n    finally:\n        if opts.keep_focus and active:\n            active.ignore_focus_changes = orig\n\n@run_once\ndef clone_safe_opts() -> frozenset[str]:\n    return frozenset((\n        'window_title', 'tab_title', 'type', 'keep_focus', 'cwd', 'env', 'var', 'hold',\n        'location', 'os_window_class', 'os_window_name', 'os_window_title', 'os_window_state',\n        'logo', 'logo_position', 'logo_alpha', 'color', 'spacing', 'next_to', 'hold_after_ssh'\n    ))\n\n\ndef parse_opts_for_clone(args: list[str]) -> tuple[LaunchCLIOptions, list[str]]:\n    unsafe, unsafe_args = parse_launch_args(args)\n    default_opts, default_args = parse_launch_args()\n    # only copy safe options, those that dont lead to local code exec\n    for x in clone_safe_opts():\n        setattr(default_opts, x, getattr(unsafe, x))\n    return default_opts, unsafe_args\n\n\ndef parse_null_env(text: str) -> dict[str, str]:\n    ans = {}\n    for line in text.split('\\0'):\n        if line:\n            try:\n                k, v = line.split('=', 1)\n            except ValueError:\n                continue\n            ans[k] = v\n    return ans\n\n\ndef parse_message(msg: str, simple: Container[str]) -> Iterator[tuple[str, str]]:\n    from base64 import standard_b64decode\n    for x in msg.split(','):\n        try:\n            k, v = x.split('=', 1)\n        except ValueError:\n            continue\n        if k not in simple:\n            v = standard_b64decode(v).decode('utf-8', 'replace')\n        yield k, v\n\n\nclass EditCmd:\n    abort_signaled: Literal['closed', 'replaced', 'disconnected', ''] = ''\n\n    def __init__(self, msg: str, child_is_remote: bool) -> None:\n        self.child_is_remote = child_is_remote\n        self.tdir = ''\n        self.args: list[str] = []\n        self.cwd = self.file_name = self.file_localpath = ''\n        self.file_data = b''\n        self.file_inode = -1, -1\n        self.file_size = -1\n        self.version = 0\n        self.source_window_id = self.editor_window_id = -1\n        simple = 'file_inode', 'file_data', 'abort_signaled', 'version'\n        for k, v in parse_message(msg, simple):\n            if k == 'file_inode':\n                q = map(int, v.split(':'))\n                self.file_inode = next(q), next(q)\n                self.file_size = next(q)\n            elif k == 'a':\n                self.args.append(v)\n            elif k == 'file_data':\n                import base64\n                self.file_data = base64.standard_b64decode(v)\n            elif k == 'version':\n                self.version = int(v)\n            else:\n                setattr(self, k, v)\n        if self.abort_signaled:\n            return\n        if self.version > 0:\n            raise ValueError(f'Unsupported version received in edit protocol: {self.version}')\n        self.opts, extra_args = parse_opts_for_clone(['--type=overlay'] + self.args)\n        self.file_spec = extra_args.pop()\n        self.line_number = 0\n        import re\n        pat = re.compile(r'\\+(-?\\d+)')\n        for x in extra_args:\n            m = pat.match(x)\n            if m is not None:\n                self.line_number = int(m.group(1))\n        self.file_name = os.path.basename(self.file_spec)\n        self.file_localpath = os.path.normpath(os.path.join(self.cwd, self.file_spec))\n        self.is_local_file = False\n        with suppress(OSError):\n            st = os.stat(self.file_localpath)\n            self.is_local_file = (st.st_dev, st.st_ino) == self.file_inode and os.access(self.file_localpath, os.W_OK | os.R_OK)\n        if not self.is_local_file:\n            import tempfile\n            self.tdir = tempfile.mkdtemp()\n            self.file_localpath = os.path.join(self.tdir, self.file_name)\n            with open(self.file_localpath, 'wb') as f:\n                f.write(self.file_data)\n        self.file_data = b''\n        self.last_mod_time = self.file_mod_time\n        if not self.opts.cwd:\n            self.opts.cwd = os.path.dirname(self.file_localpath)\n\n    def __del__(self) -> None:\n        if self.tdir:\n            if not self.abort_signaled == 'disconnected':\n                with suppress(OSError):\n                    shutil.rmtree(self.tdir)\n            self.tdir = ''\n\n    def read_data(self) -> bytes:\n        with open(self.file_localpath, 'rb') as f:\n            return f.read()\n\n    @property\n    def file_mod_time(self) -> int:\n        return os.stat(self.file_localpath).st_mtime_ns\n\n    def schedule_check(self) -> None:\n        if not self.abort_signaled:\n            add_timer(self.check_status, 1.0, False)\n\n    def on_edit_window_close(self, window: Window) -> None:\n        self.check_status()\n\n    def check_status(self, timer_id: int | None = None) -> None:\n        if self.abort_signaled:\n            return\n        boss = get_boss()\n        source_window = boss.window_id_map.get(self.source_window_id)\n        if source_window is not None and not self.is_local_file:\n            mtime = self.file_mod_time\n            if mtime != self.last_mod_time:\n                self.last_mod_time = mtime\n                data = self.read_data()\n                self.send_data(source_window, 'UPDATE', data)\n        editor_window = boss.window_id_map.get(self.editor_window_id)\n        if editor_window is None:\n            edits_in_flight.pop(self.source_window_id, None)\n            if source_window is not None:\n                self.send_data(source_window, 'DONE')\n            self.abort_signaled = self.abort_signaled or 'closed'\n        else:\n            self.schedule_check()\n\n    def send_data(self, window: Window, data_type: str, data: bytes = b'') -> None:\n        if not self.is_local_file and self.child_is_remote and not window.child_is_remote:\n            self.abort_signaled = 'disconnected'\n            get_boss().show_error(\n                'edit-in-kitty', f'Failed to sync file due to the SSH connection dropping. Your local changes can still be found at {self.file_localpath}'\n            )\n            return\n        window.write_to_child(f'KITTY_DATA_START\\n{data_type}\\n')\n        if data:\n            import base64\n            mv = memoryview(base64.standard_b64encode(data))\n            while mv:\n                window.write_to_child(bytes(mv[:512]))\n                window.write_to_child('\\n')\n                mv = mv[512:]\n        window.write_to_child('KITTY_DATA_END\\n')\n\n\n@run_once\ndef excluded_env_vars() -> frozenset[str]:\n    return frozenset({\n        'HOME', 'LOGNAME', 'USER', 'PWD',\n        # some people export these. We want the shell rc files to recreate them\n        'PS0', 'PS1', 'PS2', 'PS3', 'PS4', 'RPS1', 'PROMPT_COMMAND', 'SHLVL',\n        # conda state env vars\n        'CONDA_SHLVL', 'CONDA_PREFIX', 'CONDA_PROMPT_MODIFIER', 'CONDA_EXE', 'CONDA_PYTHON_EXE', '_CE_CONDA', '_CE_M',\n        # skip SSH environment variables\n        'SSH_CLIENT', 'SSH_CONNECTION', 'SSH_ORIGINAL_COMMAND', 'SSH_TTY', 'SSH2_TTY',\n        'SSH_TUNNEL', 'SSH_USER_AUTH', 'SSH_AUTH_SOCK',\n        # Dont clone KITTY_WINDOW_ID\n        'KITTY_WINDOW_ID',\n        # Bash variables from \"bind -x\" and \"complete -C\" (needed not to confuse bash-preexec)\n        'READLINE_ARGUMENT', 'READLINE_LINE', 'READLINE_MARK', 'READLINE_POINT',\n        'COMP_LINE', 'COMP_POINT', 'COMP_TYPE',\n        # GPG gpg-agent\n        'GPG_TTY',\n        # Session variables of XDG\n        'XDG_SESSION_CLASS', 'XDG_SESSION_ID', 'XDG_SESSION_TYPE',\n        # Session variables of GNU Screen\n        'STY', 'WINDOW',\n        # Session variables of interactive shell plugins\n        'ATUIN_SESSION', 'ATUIN_HISTORY_ID',\n        'BLE_SESSION_ID', '_ble_util_fdlist_cloexec', '_ble_util_fdvars_export',\n        '_GITSTATUS_CLIENT_PID', '_GITSTATUS_REQ_FD', '_GITSTATUS_RESP_FD', 'GITSTATUS_DAEMON_PID',\n        'MCFLY_SESSION_ID',\n        'STARSHIP_SESSION_KEY',\n    })\n\n\ndef is_excluded_env_var(x: str) -> bool:\n    # conda state env vars for multi-level virtual environments CONDA_PREFIX_*\n    return x in excluded_env_vars() or x.startswith('CONDA_PREFIX_')\n\n\nclass CloneCmd:\n\n    def __init__(self, msg: str) -> None:\n        self.args: list[str] = []\n        self.env: dict[str, str] | None = None\n        self.cwd = ''\n        self.shell = ''\n        self.envfmt = 'default'\n        self.pid = -1\n        self.bash_version = ''\n        self.history = ''\n        self.parse_message(msg)\n        self.opts = parse_opts_for_clone(self.args)[0]\n\n    def parse_message(self, msg: str) -> None:\n        simple = 'pid', 'envfmt', 'shell', 'bash_version'\n        for k, v in parse_message(msg, simple):\n            if k in simple:\n                if k == 'pid':\n                    self.pid = int(v)\n                else:\n                    setattr(self, k, v)\n            elif k == 'a':\n                self.args.append(v)\n            elif k == 'env':\n                if self.envfmt == 'bash':\n                    from .bash import parse_bash_env\n                    env = parse_bash_env(v, self.bash_version)\n                else:\n                    env = parse_null_env(v)\n                self.env = {k: v for k, v in env.items() if not is_excluded_env_var(k)}\n            elif k == 'cwd':\n                self.cwd = v\n            elif k == 'history':\n                self.history = v\n\n\nedits_in_flight: dict[int, EditCmd] = {}\n\n\ndef remote_edit(msg: str, window: Window) -> None:\n    c = EditCmd(msg, window.child_is_remote)\n    if c.abort_signaled:\n        q = edits_in_flight.pop(window.id, None)\n        if q is not None:\n            q.abort_signaled = c.abort_signaled\n        return\n    cmdline = get_editor(path_to_edit=c.file_localpath, line_number=c.line_number)\n    c.opts.source_window = c.opts.next_to = f'id:{window.id}'\n    w = launch(get_boss(), c.opts, cmdline)\n    if w is not None:\n        c.source_window_id = window.id\n        c.editor_window_id = w.id\n        q = edits_in_flight.pop(window.id, None)\n        if q is not None:\n            q.abort_signaled = 'replaced'\n        edits_in_flight[window.id] = c\n        w.actions_on_close.append(c.on_edit_window_close)\n        c.schedule_check()\n\n\ndef clone_and_launch(msg: str, window: Window) -> None:\n    from .shell_integration import serialize_env\n    c = CloneCmd(msg)\n    if c.cwd and not c.opts.cwd:\n        c.opts.cwd = c.cwd\n    c.opts.copy_colors = True\n    c.opts.copy_env = False\n    if c.opts.type in non_window_launch_types:\n        c.opts.type = 'window'\n    env_to_serialize = c.env or {}\n    if env_to_serialize.get('PATH') and env_to_serialize.get('VIRTUAL_ENV'):\n        # only pass VIRTUAL_ENV if it is currently active\n        if f\"{env_to_serialize['VIRTUAL_ENV']}/bin\" not in env_to_serialize['PATH'].split(os.pathsep):\n            del env_to_serialize['VIRTUAL_ENV']\n    env_to_serialize['KITTY_CLONE_SOURCE_STRATEGIES'] = ',' + ','.join(get_options().clone_source_strategies) + ','\n    is_clone_launch = serialize_env(c.shell, env_to_serialize)\n    ssh_kitten_cmdline = window.ssh_kitten_cmdline()\n    if ssh_kitten_cmdline:\n        from kittens.ssh.utils import patch_cmdline, set_cwd_in_cmdline, set_env_in_cmdline\n        cmdline = ssh_kitten_cmdline\n        if c.opts.cwd:\n            set_cwd_in_cmdline(c.opts.cwd, cmdline)\n            c.opts.cwd = None\n        if c.env:\n            set_env_in_cmdline({\n                'KITTY_IS_CLONE_LAUNCH': is_clone_launch,\n            }, cmdline)\n            c.env = None\n        if c.opts.env:\n            for entry in reversed(c.opts.env):\n                patch_cmdline('env', entry, cmdline)\n            c.opts.env = []\n    else:\n        try:\n            cmdline = window.child.cmdline_of_pid(c.pid)\n        except Exception:\n            cmdline = []\n        if not cmdline:\n            cmdline = list(window.child.argv)\n        if cmdline and cmdline[0].startswith('-'):  # on macOS, run via run-shell kitten\n            if window.child.is_default_shell:\n                cmdline = window.child.unmodified_argv\n            else:\n                cmdline[0] = cmdline[0][1:]\n                cmdline[0] = which(cmdline[0]) or cmdline[0]\n        if cmdline and cmdline[0] == window.child.final_argv0:\n            cmdline[0] = window.child.final_exe\n        if cmdline and cmdline == [window.child.final_exe] + window.child.argv[1:]:\n            cmdline = window.child.unmodified_argv\n    c.opts.source_window = f'id:{window.id}'\n    c.opts.next_to = c.opts.next_to or c.opts.source_window\n    launch(get_boss(), c.opts, cmdline, is_clone_launch=is_clone_launch)\n"
  },
  {
    "path": "kitty/launcher/cli-parser.h",
    "content": "/*\n * cli-parser.h\n * Copyright (C) 2025 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <Python.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <errno.h>\n#include \"../iqsort.h\"\n\n#ifndef RAII_PyObject\nstatic inline void cleanup_decref2(PyObject **p) { Py_CLEAR(*p); }\n#define RAII_PyObject(name, initializer) __attribute__((cleanup(cleanup_decref2))) PyObject *name = initializer\n\n#undef MAX\n#define MAX(x, y) __extension__ ({ \\\n    const __typeof__ (x) __a__ = (x); const __typeof__ (y) __b__ = (y); \\\n        __a__ > __b__ ? __a__ : __b__;})\n#endif\n\ntypedef enum CLIValueType { CLI_VALUE_STRING, CLI_VALUE_BOOL, CLI_VALUE_INT, CLI_VALUE_FLOAT, CLI_VALUE_LIST, CLI_VALUE_CHOICE } CLIValueType;\ntypedef struct CLIValue {\n    CLIValueType type;\n    long long intval;\n    double floatval;\n    bool boolval;\n    const char* strval;\n    struct {\n        const char* * items;\n        size_t count, capacity;\n    } listval;\n} CLIValue;\n\n#define NAME cli_hash\n#define KEY_TY const char*\n#define VAL_TY CLIValue\n#include \"../kitty-verstable.h\"\n#define value_map_for_loop(x) vt_create_for_loop(cli_hash_itr, itr, x)\n\n#define NAME alias_hash\n#define KEY_TY const char*\n#define VAL_TY const char*\n#include \"../kitty-verstable.h\"\n#define alias_map_for_loop(x) vt_create_for_loop(alias_hash_itr, itr, x)\n\ntypedef struct FlagSpec {\n    CLIValue defval;\n    const char *dest;\n} FlagSpec;\n\n#define NAME flag_hash\n#define KEY_TY const char*\n#define VAL_TY FlagSpec\n#include \"../kitty-verstable.h\"\n#define flag_map_for_loop(x) vt_create_for_loop(flag_hash_itr, itr, x)\n\n\ntypedef struct CLISpec {\n    cli_hash value_map;\n    alias_hash alias_map;\n    flag_hash flag_map, disabled_map;\n    char **argv; int argc;  // leftover args\n    char **original_argv; int original_argc;  // original args\n    const char* errmsg;\n    struct {\n        struct { char *buf; size_t capacity, used; } *items;\n        size_t count, capacity;\n    } blocks;\n} CLISpec;\n\nstatic void\nout_of_memory(int line) {\n    fprintf(stderr, \"Out of memory at %s:%d\\n\", __FILE__, line);\n    exit(1);\n}\n#define OOM out_of_memory(__LINE__)\n\nstatic void*\nalloc_for_cli(CLISpec *spec, size_t sz) {\n    sz++;\n    if (!spec->blocks.capacity) {\n        spec->blocks.capacity = 8;\n        spec->blocks.items = calloc(spec->blocks.capacity, sizeof(spec->blocks.items[0]));\n        if (!spec->blocks.items) return NULL;\n        spec->blocks.count = 1;\n    }\n#define block spec->blocks.items[spec->blocks.count-1]\n    if (block.used + sz >= block.capacity) {\n        if (block.capacity) {  // need new block\n            spec->blocks.count++;\n            if (spec->blocks.count >= spec->blocks.capacity) {\n                spec->blocks.capacity *= 2;\n                spec->blocks.items = realloc(spec->blocks.items, spec->blocks.capacity * sizeof(spec->blocks.items[0]));\n                if (!spec->blocks.items) return NULL;\n            }\n        }\n        block.capacity = MAX(sz, 8192u);\n        block.buf = malloc(block.capacity);\n        if (!block.buf) return NULL;\n        block.used = 0;\n    }\n    char *ans = block.buf + block.used;\n    ans[sz-1] = 0;\n    block.used += sz;\n    // keep returned memory regions aligned to size of pointer\n    size_t extra = sz % sizeof(void*);\n    if (extra) block.used += sizeof(void*) - extra;\n    return ans;\n#undef block\n}\n\n#define set_err(fmt, ...) { \\\n    int sz = snprintf(NULL, 0, fmt, __VA_ARGS__); \\\n    char *buf = alloc_for_cli(spec, sz + 4);  \\\n    if (!buf) OOM; \\\n    snprintf(buf, sz + 4, fmt, __VA_ARGS__); spec->errmsg = buf; \\\n}\n\nstatic ssize_t\nlevenshtein_distance(size_t *cache, const char *a, const char *b) {\n    if (a == b) return 0;\n    const size_t length = strlen(a);\n    const size_t bLength = strlen(b);\n    if (length == 0) return bLength;\n    if (bLength == 0) return length;\n    size_t index = 0, bIndex = 0, distance = 0, bDistance = 0, result = 0;\n    char code = 0;\n\n    // initialize the vector.\n    while (index < length) {\n        cache[index] = index + 1;\n        index++;\n    }\n\n    while (bIndex < bLength) {\n        code = b[bIndex];\n        result = distance = bIndex++;\n        index = SIZE_MAX;\n\n        while (++index < length) {\n            bDistance = code == a[index] ? distance : distance + 1;\n            distance = cache[index];\n\n            cache[index] = result = distance > result\n                ? bDistance > result\n                ? result + 1\n                : bDistance\n                : bDistance > distance\n                ? distance + 1\n                : bDistance;\n        }\n    }\n    return result;\n}\n\nstatic bool\nadd_to_listval(CLISpec *spec, CLIValue *v, const char *val) {\n    if (v->listval.count + 1 >= v->listval.capacity) {\n        size_t cap = MAX(64u, v->listval.capacity * 2u);\n        char **new = alloc_for_cli(spec, cap * sizeof(v->listval.items[0]));\n        if (!new) return false;\n        v->listval.capacity = cap;\n        if (v->listval.count) memcpy(new, v->listval.items, sizeof(new[0]) * v->listval.count);\n        v->listval.items = (void*)new;\n    }\n    v->listval.items[v->listval.count++] = val;\n    return true;\n}\n\nstatic bool\nuse_ansi_escape_codes(void) {\n    static bool checked = false, ans;\n    if (!checked) { ans = isatty(STDERR_FILENO); checked = true; }\n    return ans;\n}\n\nstatic const char*\nformatted_text(CLISpec *spec, const char *start_code, const char *text, const char *end_code) {\n    if (!use_ansi_escape_codes()) return text;\n    static const char *fmt =  \"\\x1b[%sm%s\\x1b[%sm\";\n    int sz = snprintf(NULL, 0, fmt, start_code, text, end_code);\n    char *ans = alloc_for_cli(spec, sz+1);\n    snprintf(ans, sz+1, fmt, start_code, text, end_code);\n    return ans;\n}\n\n#define red_text(text) formatted_text(spec, \"91\", text, \"39\")\n#define yellow_text(text) formatted_text(spec, \"93\", text, \"39\")\n#define green_text(text) formatted_text(spec, \"32\", text, \"39\")\n#define italic_text(text) formatted_text(spec, \"3\", text, \"23\")\n\ntypedef struct similiar_alias {\n    const char *alias;\n    ssize_t distance;\n} similiar_alias;\n\nstatic const char*\ndest_for_alias(CLISpec *spec, const char *alias) {\n    alias_hash_itr itr = vt_get(&spec->alias_map, alias);\n    if (vt_is_end(itr)) {\n        const char *match_key = NULL, *match_val = NULL;\n        size_t total = 0;\n        alias_hash matches; vt_init(&matches);\n        alias_map_for_loop(&spec->alias_map) {\n            if (strstr(itr.data->key, alias) == itr.data->key) {\n                total += strlen(itr.data->key) + 8;\n                if (!match_key) { match_key = itr.data->key; match_val = itr.data->val; }\n                if (vt_is_end(vt_insert(&matches, itr.data->val, itr.data->key))) OOM;\n            }\n        }\n        if (match_key) {\n            if (vt_size(&matches) == 1) { vt_cleanup(&matches); return match_val; }\n            total += 256 + total;\n            char *buf = alloc_for_cli(spec, total);\n            if (!buf) OOM;\n            int n = snprintf(buf, total, \"The flag %s is ambiguous. Possible matches:\", yellow_text(alias));\n            alias_map_for_loop(&matches) {\n                if ((ssize_t)total > n) n += snprintf(buf + n, total - n, \" %s,\", itr.data->val);\n            }\n            vt_cleanup(&matches);\n            buf[n-1] = '.';\n            spec->errmsg = buf;\n            return NULL;\n        }\n        size_t *cache = alloc_for_cli(spec, sizeof(size_t) * strlen(alias));\n        size_t num_aliases = vt_size(&spec->alias_map);\n        similiar_alias *candidates =  alloc_for_cli(spec, sizeof(similiar_alias) * num_aliases);\n        size_t num_candidates = 0;\n        alias_map_for_loop(&spec->alias_map) {\n            const char *q = itr.data->key;\n            ssize_t d = levenshtein_distance(cache, alias, q);\n            if (d < 0) break;\n            if (d < 3) candidates[num_candidates++] = (similiar_alias){.alias=q, .distance=d};\n        }\n        if (num_candidates) {\n#define lt(a, b) (a->distance < b->distance)\n            QSORT(similiar_alias, candidates, num_candidates, lt);\n            set_err(\"Unknown flag: %s. Did you mean: %s?\", red_text(alias), green_text(candidates[0].alias));\n            return NULL;\n#undef lt\n        }\n        set_err(\"Unknown flag: %s use --help.\", red_text(alias));\n        return NULL;\n    }\n    return itr.data->val;\n}\n\nstatic bool\nis_alias_bool(CLISpec* spec, const char *alias, const char **dest_out) {\n    *dest_out = dest_for_alias(spec, alias);\n    if (!*dest_out) return false;\n    flag_hash_itr itr = vt_get(&spec->flag_map, *dest_out);\n    return itr.data->val.defval.type == CLI_VALUE_BOOL;\n}\n\nstatic void\nadd_list_value(CLISpec *spec, const char *dest, const char *val) {\n    cli_hash_itr itr = vt_get_or_insert(&spec->value_map, dest, (CLIValue){.type=CLI_VALUE_LIST});\n    if (vt_is_end(itr)) OOM;\n    CLIValue v = itr.data->val;\n    if (!add_to_listval(spec, &v, val)) OOM;\n    if (vt_is_end(vt_insert(&spec->value_map, dest, v))) OOM;\n}\n\nstatic bool\nprocess_cli_arg(CLISpec* spec, const char *alias, const char *payload, const char *dest) {\n    if (!dest) dest = dest_for_alias(spec, alias);\n    if (!dest) return false;\n    flag_hash_itr itr = vt_get(&spec->flag_map, dest);\n    const FlagSpec *flag = &itr.data->val;\n    CLIValue val = {.type=flag->defval.type};\n#define streq(q) (strcmp(payload, #q) == 0)\n    switch(val.type) {\n        case CLI_VALUE_STRING: val.strval = payload; break;\n        case CLI_VALUE_BOOL:\n            if (payload) {\n                if (streq(y) || streq(yes) || streq(true)) val.boolval = true;\n                else if (streq(n) || streq(no) || streq(false)) val.boolval = false;\n                else {\n                    set_err(\"%s is an invalid value for %s. Valid values are: %s, %s, %s, %s, %s and %s.\",\n                            red_text(payload[0] ? payload : \"<empty>\"), green_text(alias), italic_text(\"y\"), italic_text(\"yes\"), italic_text(\"true\"), italic_text(\"n\"), italic_text(\"no\"), italic_text(\"false\"));\n                    return false;\n                }\n            } else val.boolval = !flag->defval.boolval;\n            break;\n        case CLI_VALUE_CHOICE:\n            val.strval = NULL;\n            for (size_t c = 0; c < flag->defval.listval.count; c++) {\n                if (strcmp(payload, flag->defval.listval.items[c]) == 0) { val.strval = payload; break; }\n            }\n            if (!val.strval) {\n                size_t bufsz = 0;\n                for (size_t c = 0; c < flag->defval.listval.count; c++) bufsz += strlen(flag->defval.listval.items[c]) + 8;\n                bufsz += 256 + strlen(alias) + strlen(payload) + bufsz;\n                char *buf = alloc_for_cli(spec, bufsz);\n                int n = snprintf(buf, bufsz, \"%s is an invalid value for %s. Valid values are:\",\n                        red_text(payload[0] ? payload : \"<empty>\"), green_text(alias));\n                for (size_t c = 0; c < flag->defval.listval.count; c++)\n                    if ((ssize_t)bufsz > n) n += snprintf(buf + n, bufsz - n, \" %s,\", italic_text(flag->defval.listval.items[c]));\n                buf[n-1] = '.';\n                spec->errmsg = buf;\n                return false;\n            }\n            break;\n        case CLI_VALUE_INT:\n            errno = 0; val.intval = strtoll(payload, NULL, 10);\n            if (errno) {\n                set_err(\"%s is an invalid value for %s, it must be an integer number.\", red_text(payload), green_text(alias));\n                return false;\n            } break;\n        case CLI_VALUE_FLOAT:\n            errno = 0; val.floatval = strtod(payload, NULL);\n            if (errno) {\n                set_err(\"%s is an invalid value for %s, it must be a number.\", red_text(payload), green_text(alias));\n                return false;\n            } break;\n        case CLI_VALUE_LIST: add_list_value(spec, flag->dest, payload); return true;\n    }\n    if (vt_is_end(vt_insert(&spec->value_map, flag->dest, val))) OOM;\n    return true;\n#undef streq\n}\n\nstatic void\nalloc_cli_spec(CLISpec *spec) {\n    vt_init(&spec->value_map);\n    vt_init(&spec->alias_map);\n    vt_init(&spec->flag_map);\n    vt_init(&spec->disabled_map);\n}\n\nstatic void\ndealloc_cli_spec(void *v) {\n    CLISpec *spec = v;\n    for (size_t i = 0; i < spec->blocks.count; i++) free(spec->blocks.items[i].buf);\n    free(spec->blocks.items);\n    vt_cleanup(&spec->value_map);\n    vt_cleanup(&spec->alias_map);\n    vt_cleanup(&spec->flag_map);\n    vt_cleanup(&spec->disabled_map);\n}\n\n#define RAII_CLISpec(name) __attribute__((cleanup(dealloc_cli_spec))) CLISpec name = {0}; alloc_cli_spec(&name)\n\nstatic bool\nparse_cli_loop(CLISpec *spec, bool save_original_argv, int argc, char **argv) {\n    enum { NORMAL, EXPECTING_ARG } state = NORMAL;\n    spec->argc = 0; spec->argv = NULL; spec->errmsg = NULL; spec->original_argc = argc; spec->original_argv = NULL;\n    if (save_original_argv) {\n        char **copy = alloc_for_cli(spec, sizeof(char*) * (argc + 1));\n        if (!copy) OOM;\n        copy[argc] = NULL;\n        for (int i = 0; i < argc; i++) {\n            size_t len = strlen(argv[i]);\n            copy[i] = alloc_for_cli(spec, len);\n            if (!copy[i]) OOM;\n            memcpy(copy[i], argv[i], len);\n        }\n        spec->original_argv = argv;\n        argv = copy;\n    }\n    char flag[3] = {'-', 0, 0};\n    const char *current_option = NULL;\n    const char *dest_for_current_arg = NULL;\n    for (int i = 1; i < argc; i++) {\n        char *arg = argv[i];\n        switch(state) {\n            case NORMAL: {\n                if (arg[0] == '-') {\n                    const bool is_long_opt = arg[1] == '-';\n                    if (is_long_opt && arg[2] == 0) {\n                        spec->argc = argc - i - 1;\n                        if (spec->argc > 0) spec->argv = argv + i + 1;\n                        return true;\n                    }\n                    char *has_equal = strchr(arg, '=');\n                    const char *payload = NULL;\n                    if (has_equal) {\n                        *has_equal = 0;\n                        payload = has_equal + 1;\n                    }\n                    if (is_long_opt) {\n                        if (is_alias_bool(spec, arg, &dest_for_current_arg)) {\n                            if (!process_cli_arg(spec, arg, payload, dest_for_current_arg)) return false;\n                        } else {\n                            if (spec->errmsg) return false;\n                            if (has_equal) {\n                                if (!process_cli_arg(spec, arg, payload, dest_for_current_arg)) return false;\n                            } else {\n                                state = EXPECTING_ARG;\n                                current_option = arg;\n                            }\n                        }\n                    } else {\n                        for (const char *letter = arg + 1; *letter; letter++) {\n                            flag[1] = *letter;\n                            if (letter[1]) {\n                                if (!process_cli_arg(spec, flag, NULL, NULL)) return false;\n                            } else {\n                                if (is_alias_bool(spec, flag, &dest_for_current_arg) || payload) {\n                                    if (!process_cli_arg(spec, flag, payload, dest_for_current_arg)) return false;\n                                } else {\n                                    if (spec->errmsg) return false;\n                                    state = EXPECTING_ARG;\n                                    current_option = flag;\n                                }\n                                if (spec->errmsg) return false;\n                            }\n                        }\n                    }\n                } else {\n                    spec->argc = argc - i;\n                    if (spec->argc > 0) spec->argv = argv + i;\n                    return true;\n                }\n            } break;\n            case EXPECTING_ARG: {\n                if (current_option && !process_cli_arg(spec, current_option, arg, NULL)) return false;\n                current_option = NULL; state = NORMAL;\n            } break;\n        }\n    }\n    if (state == EXPECTING_ARG) set_err(\"The %s flag must be followed by an argument.\", yellow_text(current_option ? current_option : \"\"));\n    return spec->errmsg != NULL;\n}\n\n#ifdef FOR_LAUNCHER\nstatic void\noutput_argv(const char *name, int argc, char **argv) {\n    printf(\"%s:\", name);\n    for (int i = 0; i < argc; i++) printf(\"\\x1e%s\", argv[i]);\n    printf(\"\\n\");\n}\n\nstatic void\noutput_values_for_testing(CLISpec *spec) {\n    value_map_for_loop(&spec->value_map) {\n        CLIValue v = itr.data->val;\n        switch (v.type) {\n            case CLI_VALUE_STRING: case CLI_VALUE_CHOICE:\n                printf(\"%s: %s\", itr.data->key, v.strval ? v.strval : \"\"); break;\n            case CLI_VALUE_BOOL:\n                printf(\"%s: %d\", itr.data->key, v.boolval); break;\n            case CLI_VALUE_INT:\n                printf(\"%s: %lld\", itr.data->key, v.intval); break;\n            case CLI_VALUE_FLOAT:\n                printf(\"%s: %f\", itr.data->key, v.floatval); break;\n            case CLI_VALUE_LIST:\n                output_argv(itr.data->key, v.listval.count, (char**)v.listval.items);\n                break;\n        }\n        printf(\"\\n\");\n    }\n}\n\nstatic void\noutput_for_testing(CLISpec *spec) {\n    output_argv(\"original_argv\", spec->original_argc, spec->original_argv);\n    output_argv(\"argv\", spec->argc, spec->argv);\n    output_values_for_testing(spec);\n}\n\nstatic CLIValue\nget_cli_val(CLISpec *spec, const char *name) {\n    cli_hash_itr itr = vt_get(&spec->value_map, name);\n    if (vt_is_end(itr)) {\n        flag_hash_itr itr = vt_get(&spec->flag_map, name);\n        if (vt_is_end(itr)) return (CLIValue){0};\n        return itr.data->val.defval;\n    }\n    return itr.data->val;\n}\n\nstatic bool\nget_bool_cli_val(CLISpec *spec, const char *name) {\n    return get_cli_val(spec, name).boolval;\n}\n\nstatic const char*\nget_string_cli_val(CLISpec *spec, const char *name) {\n    return get_cli_val(spec, name).strval;\n}\n#endif\n\nstatic bool\nclival_as_python(const CLIValue *v, PyObject *is_seen, const char *dest, PyObject *ans) {\n#define S(fv) { \\\n    RAII_PyObject(temp, Py_BuildValue(\"NO\", fv, is_seen)); if (!temp) return false; \\\n    if (PyDict_SetItemString(ans, dest, temp) != 0) return false; \\\n}\n        switch (v->type) {\n            case CLI_VALUE_BOOL: S(PyBool_FromLong((long)v->boolval)); break;\n            case CLI_VALUE_STRING: if (v->strval) { S(PyUnicode_FromString(v->strval)); } else { S(Py_NewRef(Py_None)); } break;\n            case CLI_VALUE_CHOICE: S(PyUnicode_FromString(v->strval)); break;\n            case CLI_VALUE_INT: S(PyLong_FromLongLong(v->intval)); break;\n            case CLI_VALUE_FLOAT: S(PyFloat_FromDouble(v->floatval)); break;\n            case CLI_VALUE_LIST: {\n                RAII_PyObject(l, PyList_New(v->listval.count)); if (!l) return false;\n                for (size_t i = 0; i < v->listval.count; i++) {\n                    PyObject *x = PyUnicode_FromString(v->listval.items[i]); if (!x) return false;\n                    PyList_SET_ITEM(l, i, x);\n                }\n                S(Py_NewRef(l));\n            } break;\n        }\n#undef S\n        return true;\n}\n\nstatic PyObject*\ncli_parse_result_as_python(CLISpec *spec) {\n    if (PyErr_Occurred()) return NULL;\n    if (spec->errmsg) {\n        PyErr_SetString(PyExc_ValueError, spec->errmsg); return NULL;\n    }\n    RAII_PyObject(ans, PyDict_New()); if (!ans) return NULL;\n    flag_map_for_loop(&spec->flag_map) {\n        const FlagSpec *flag = &itr.data->val;\n        cli_hash_itr i = vt_get(&spec->value_map, flag->dest);\n        PyObject *is_seen = vt_is_end(i) ? Py_False : Py_True;\n        const CLIValue *v = is_seen == Py_True ? &i.data->val : &flag->defval;\n        if (!clival_as_python(v, is_seen, flag->dest, ans)) return NULL;\n    }\n    flag_map_for_loop(&spec->disabled_map) {\n        const FlagSpec *flag = &itr.data->val;\n        if (!clival_as_python(&flag->defval, Py_False, flag->dest, ans)) return NULL;\n    }\n    RAII_PyObject(leftover_args, PyList_New(spec->argc)); if (!leftover_args) return NULL;\n    for (int i = 0; i < spec->argc; i++) {\n        PyObject *t = PyUnicode_FromString(spec->argv[i]);\n        if (!t) return NULL;\n        PyList_SET_ITEM(leftover_args, i, t);\n    }\n    return Py_BuildValue(\"OO\", ans, leftover_args);\n}\n\n#ifndef FOR_LAUNCHER\nstatic PyObject*\nparse_cli_from_python_spec(PyObject *self, PyObject *args) {\n    (void)self; PyObject *pyargs, *names_map, *defval_map;\n    if (!PyArg_ParseTuple(args, \"O!O!O!\", &PyList_Type, &pyargs, &PyDict_Type, &names_map, &PyDict_Type, &defval_map)) return NULL;\n    int argc = PyList_GET_SIZE(pyargs);\n    RAII_CLISpec(spec);\n    char **argv = alloc_for_cli(&spec, sizeof(char*) * (argc + 2));\n    if (!argv) return PyErr_NoMemory();\n    argv[0] = \"parse_cli_from_python_spec\";\n    for (int i = 0; i < argc; i++) {\n        Py_ssize_t sz;\n        const char *src = PyUnicode_AsUTF8AndSize(PyList_GET_ITEM(pyargs, i), &sz);\n        argv[i + 1] = alloc_for_cli(&spec, sz);\n        if (!argv[i + 1]) return PyErr_NoMemory();\n        memcpy(argv[i + 1], src, sz);\n    }\n    argv[++argc] = 0;\n    PyObject *key = NULL, *optdef = NULL;\n    Py_ssize_t pos = 0;\n    while (PyDict_Next(names_map, &pos, &key, &optdef)) {\n        FlagSpec flag = {.dest=PyUnicode_AsUTF8(key)};\n        RAII_PyObject(pytype, PyObject_GetAttrString(optdef, \"type\"));\n        if (!pytype) return NULL;\n        const char *type = PyUnicode_AsUTF8(pytype);\n        PyObject *defval = PyDict_GetItemWithError(defval_map, key); if (!defval && PyErr_Occurred()) return NULL;\n        RAII_PyObject(pyaliases, PyObject_GetAttrString(optdef, \"aliases\"));\n        if (!pyaliases) return NULL;\n        for (int a = 0; a < PyTuple_GET_SIZE(pyaliases); a++) {\n            const char *alias = PyUnicode_AsUTF8(PyTuple_GET_ITEM(pyaliases, a));\n            if (vt_is_end(vt_insert(&spec.alias_map, alias, flag.dest))) return PyErr_NoMemory();\n        }\n        if (strstr(type, \"bool-\") == type) {\n            flag.defval.type = CLI_VALUE_BOOL;\n            flag.defval.boolval = PyObject_IsTrue(defval);\n        } else if (strcmp(type, \"int\") == 0) {\n            flag.defval.type = CLI_VALUE_INT;\n            flag.defval.intval = PyLong_AsLongLong(defval);\n        } else if (strcmp(type, \"float\") == 0) {\n            flag.defval.type = CLI_VALUE_FLOAT;\n            flag.defval.floatval = PyFloat_AsDouble(defval);\n        } else if (strcmp(type, \"list\") == 0) {\n            flag.defval.type = CLI_VALUE_LIST;\n            if (PyObject_IsTrue(defval)) {\n                for (ssize_t l = 0; l < PyList_GET_SIZE(defval); l++) add_to_listval(&spec, &flag.defval, PyUnicode_AsUTF8(PyList_GET_ITEM(defval, l)));\n            }\n        } else if (strcmp(type, \"choices\") == 0) {\n            flag.defval.type = CLI_VALUE_CHOICE;\n            flag.defval.strval = PyUnicode_AsUTF8(defval);\n            RAII_PyObject(pyc, PyObject_GetAttrString(optdef, \"choices\"));\n            if (!pyc) return NULL;\n            flag.defval.listval.items = alloc_for_cli(&spec, PyTuple_GET_SIZE(pyc) * sizeof(char*));\n            if (!flag.defval.listval.items) return PyErr_NoMemory();\n            flag.defval.listval.count = PyTuple_GET_SIZE(pyc);\n            flag.defval.listval.capacity = PyTuple_GET_SIZE(pyc);\n            for (size_t n = 0; n < flag.defval.listval.count; n++) {\n                flag.defval.listval.items[n] = PyUnicode_AsUTF8(PyTuple_GET_ITEM(pyc, n));\n                if (!flag.defval.listval.items[n]) return NULL;\n            }\n        } else {\n            flag.defval.type = CLI_VALUE_STRING;\n            flag.defval.strval = PyUnicode_Check(defval) ? PyUnicode_AsUTF8(defval) : NULL;\n        }\n        if (vt_is_end(vt_insert(&spec.flag_map, flag.dest, flag))) return PyErr_NoMemory();\n    }\n    if (PyErr_Occurred()) return NULL;\n    parse_cli_loop(&spec, false, argc, argv);\n    PyObject *ans = cli_parse_result_as_python(&spec);\n    return ans;\n}\n#endif\n"
  },
  {
    "path": "kitty/launcher/cmdline.c",
    "content": "/*\n * cmdline.c\n * Copyright (C) 2025 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define _POSIX_C_SOURCE 200809L\n#include \"shlex.h\"\n#include \"utils.h\"\n#include \"launcher.h\"\n#ifdef __APPLE__\n#include <os/log.h>\n#endif\n\nvoid\nfree_argv_array(argv_array *a) {\n    if (a && a->needs_free) {\n        free(a->buf); free(a->argv);\n        *a = (argv_array){0};\n    }\n}\n\nstatic bool\nadd_to_argv(argv_array *a, const char* arg, size_t sz) {\n    if (a->count + 2 > a->capacity) {\n        size_t cap = a->capacity * 2;\n        if (!cap) cap = 256;\n        void *m = realloc(a->argv, cap * sizeof(a->argv[0]));\n        if (!m) return false;\n        a->argv = m;\n        a->argv[a->count] = 0;\n        a->capacity = cap;\n        a->needs_free = true;\n    }\n    memcpy(a->buf + a->pos, arg, sz);\n    a->argv[a->count++] = a->buf + a->pos;\n    a->argv[a->count] = 0;\n    a->pos += sz;\n    a->buf[a->pos++] = 0;\n    return true;\n}\n\nbool\nget_argv_from(const char *filename, const char *argv0, argv_array *final_ans) {\n    (void)get_config_dir;\n    if (!filename || !filename[0]) return true;\n    size_t src_sz;\n    char* src = read_full_file(filename, &src_sz);\n    if (!src) {\n        if (errno == ENOENT || errno == ENOTDIR) return true;\n#ifdef __APPLE__\n        int saved = errno;\n        os_log_error(OS_LOG_DEFAULT, \"Failed to read from %{public}s with error: %{darwin.errno}d\", filename, errno);\n        errno = saved;\n#endif\n        fprintf(stderr, \"Failed to read from %s \", filename); perror(\"with error\");\n        return true;\n    }\n    ShlexState s = {0};\n    argv_array ans = {0};\n    bool ok = false;\n    ans.buf = malloc(src_sz + strlen(argv0) + 64);\n    if (!ans.buf) goto oom;\n    ans.needs_free = true;\n    if (!add_to_argv(&ans, argv0, strlen(argv0))) goto oom;\n    if (!alloc_shlex_state(&s, src, src_sz, false)) goto oom;\n    bool keep_going = true;\n    while (keep_going) {\n        ssize_t q = next_word(&s);\n        switch(q) {\n            case -1: fprintf(stderr, \"Failed to parse %s with error: %s\\n\", filename, s.err); goto end;\n            case -2: keep_going = false; break;\n            default:\n                if (ans.count == 1 && strcmp(s.buf, \"kitty\") == 0) continue;\n                if (!add_to_argv(&ans, s.buf, q)) goto oom;\n                break;\n        }\n    }\n    ok = true;\noom:\n    if (!ok) {\n        errno = ENOMEM;\n        fprintf(stderr, \"Failed to read from %s \", filename); perror(\"with error\");\n    }\nend:\n    free(src); dealloc_shlex_state(&s);\n    if (ok) *final_ans = ans;\n    else free_argv_array(final_ans);\n    return ok;\n}\n\n"
  },
  {
    "path": "kitty/launcher/launcher.h",
    "content": "/*\n * launcher.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stddef.h>\n\ntypedef struct CLIOptions {\n    const char *session, *instance_group;\n    bool wait_for_single_instance_window_close;\n    int open_url_count; char **open_urls;\n} CLIOptions;\n\n\ntypedef struct argv_array {\n    char **argv, *buf; size_t capacity, count, pos;\n    bool needs_free;\n} argv_array;\n\n\nvoid single_instance_main(int argc, char *argv[], const CLIOptions *opts);\nbool get_argv_from(const char *filename, const char* argv0, argv_array *ans);\nvoid free_argv_array(argv_array *a);\n"
  },
  {
    "path": "kitty/launcher/main.c",
    "content": "/*\n * launcher.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define PY_SSIZE_T_CLEAN\n#include <Python.h>\n\n#include <libgen.h>\n#ifdef __APPLE__\n#include <mach-o/dyld.h>\n#include <sys/syslimits.h>\n#include <sys/stat.h>\n#include <os/log.h>\n#else\n#include <limits.h>\n#endif\n#include \"launcher.h\"\n#include \"utils.h\"\n#define FOR_LAUNCHER\n#include \"cli-parser-data_generated.h\"\n\n#ifndef KITTY_LIB_PATH\n#define KITTY_LIB_PATH \"../..\"\n#endif\n#ifndef KITTY_LIB_DIR_NAME\n#define KITTY_LIB_DIR_NAME \"lib\"\n#endif\n\nstatic void cleanup_free(void *p) { free(*(void**) p); }\n#define RAII_ALLOC(type, name, initializer) __attribute__((cleanup(cleanup_free))) type *name = initializer\n\nstatic bool being_tested = false;\n\n#ifndef __FreeBSD__\nstatic bool\nsafe_realpath(const char* src, char *buf, size_t buf_sz) {\n    RAII_ALLOC(char, ans, realpath(src, NULL));\n    if (ans == NULL) return false;\n    safe_snprintf(buf, buf_sz, \"%s\", ans);\n    return true;\n}\n#endif\n\ntypedef struct {\n    const char *exe, *exe_dir, *lc_ctype, *lib_dir, *config_dir;\n    CLISpec *cli_spec;\n    bool launched_by_launch_services, is_quick_access_terminal;\n} RunData;\n\nstatic bool\nset_kitty_run_data(RunData *run_data, bool from_source, wchar_t *extensions_dir) {\n    PyObject *ans = PyDict_New();\n    if (!ans) { PyErr_Print(); return false; }\n    PyObject *exe_dir = PyUnicode_DecodeFSDefaultAndSize(run_data->exe_dir, strlen(run_data->exe_dir));\n    if (exe_dir == NULL) { fprintf(stderr, \"Fatal error: cannot decode exe_dir: %s\\n\", run_data->exe_dir); PyErr_Print(); Py_CLEAR(ans); return false; }\n#define S(key, val) { if (!val) { PyErr_Print(); Py_CLEAR(ans); return false; } int ret = PyDict_SetItemString(ans, #key, val); Py_CLEAR(val); if (ret != 0) { PyErr_Print(); Py_CLEAR(ans); return false; } }\n    S(bundle_exe_dir, exe_dir);\n    if (from_source) {\n        PyObject *one = Py_True; Py_INCREF(one);\n        S(from_source, one);\n    }\n    if (run_data->lc_ctype) {\n        PyObject *ctype = PyUnicode_DecodeLocaleAndSize(run_data->lc_ctype, strlen(run_data->lc_ctype), NULL);\n        S(lc_ctype_before_python, ctype);\n    }\n    if (extensions_dir) {\n        PyObject *ed = PyUnicode_FromWideChar(extensions_dir, -1);\n        S(extensions_dir, ed);\n    }\n    PyObject *lbls = run_data->launched_by_launch_services ? Py_True : Py_False;\n    Py_INCREF(lbls);\n    S(launched_by_launch_services, lbls);\n    lbls = run_data->is_quick_access_terminal ? Py_True : Py_False;\n    Py_INCREF(lbls);\n    S(is_quick_access_terminal_app, lbls);\n\n    char buf[PATH_MAX + 1];\n    if (run_data->config_dir == NULL) {\n        if (get_config_dir(buf, sizeof(buf))) run_data->config_dir = buf;\n    }\n    if (run_data->config_dir) {\n        PyObject *cdir = PyUnicode_DecodeFSDefaultAndSize(run_data->config_dir, strlen(run_data->config_dir));\n        if (!cdir) { PyErr_Print(); return false; }\n        S(config_dir, cdir);\n    }\n    PyObject *cli_flags = cli_parse_result_as_python(run_data->cli_spec);\n    if (!cli_flags) {\n        if (PyErr_Occurred()) PyErr_Print();\n        return false;\n    }\n    S(cli_flags, cli_flags);\n\n#undef S\n    int ret = PySys_SetObject(\"kitty_run_data\", ans);\n    Py_CLEAR(ans);\n    if (ret != 0) { PyErr_Print(); return false; }\n    return true;\n}\n\n\n#ifdef FOR_BUNDLE\n#include <bypy-freeze.h>\n\nstatic void\ncanonicalize_path_wide(const char *srcpath, wchar_t *dest, size_t sz) {\n    char buf[sz + 1];\n    lexical_absolute_path(srcpath, buf, sz);\n    buf[sz] = 0;\n    mbstowcs(dest, buf, sz - 1);\n    dest[sz-1] = 0;\n}\n\nstatic int\nrun_embedded(RunData *run_data) {\n    bypy_pre_initialize_interpreter(false);\n    char extensions_dir_full[PATH_MAX+1] = {0}, python_home_full[PATH_MAX+1] = {0};\n#ifdef __APPLE__\n    const char *python_relpath = \"../Resources/Python/lib\";\n#else\n    const char *python_relpath = \"../\" KITTY_LIB_DIR_NAME;\n#endif\n    safe_snprintf(extensions_dir_full, PATH_MAX, \"%s/%s/kitty-extensions\", run_data->exe_dir, python_relpath);\n    wchar_t extensions_dir[PATH_MAX];\n    canonicalize_path_wide(extensions_dir_full, extensions_dir, PATH_MAX);\n    safe_snprintf(python_home_full, PATH_MAX, \"%s/%s/python%s\", run_data->exe_dir, python_relpath, PYVER);\n    wchar_t python_home[PATH_MAX];\n    canonicalize_path_wide(python_home_full, python_home, PATH_MAX);\n    bypy_initialize_interpreter(\n            L\"kitty\", python_home, L\"kitty_main\", extensions_dir, run_data->cli_spec->original_argc, run_data->cli_spec->original_argv);\n    if (!set_kitty_run_data(run_data, false, extensions_dir)) return 1;\n    set_sys_bool(\"frozen\", true);\n    return bypy_run_interpreter();\n}\n\n#else\n\nstatic int\nrun_embedded(RunData *run_data) {\n    bool from_source = false;\n#ifdef FROM_SOURCE\n    from_source = true;\n#endif\n    PyStatus status;\n    PyPreConfig preconfig;\n    PyPreConfig_InitPythonConfig(&preconfig);\n    preconfig.utf8_mode = 1;\n    preconfig.coerce_c_locale = 1;\n#ifdef SET_PYTHON_HOME\n    preconfig.isolated = 1;\n#endif\n    status = Py_PreInitialize(&preconfig);\n    if (PyStatus_Exception(status)) goto fail;\n    PyConfig config;\n    PyConfig_InitPythonConfig(&config);\n    config.parse_argv = 0;\n    config.optimization_level = 2;\n    status = PyConfig_SetBytesArgv(&config, run_data->cli_spec->original_argc, run_data->cli_spec->original_argv);\n    if (PyStatus_Exception(status)) goto fail;\n    status = PyConfig_SetBytesString(&config, &config.executable, run_data->exe);\n    if (PyStatus_Exception(status)) goto fail;\n    status = PyConfig_SetBytesString(&config, &config.run_filename, run_data->lib_dir);\n    if (PyStatus_Exception(status)) goto fail;\n#ifdef SET_PYTHON_HOME\n#ifndef __APPLE__\n    char pyhome[256];\n    safe_snprintf(pyhome, sizeof(pyhome), \"%s/%s\", run_data->lib_dir, SET_PYTHON_HOME);\n    status = PyConfig_SetBytesString(&config, &config.home, pyhome);\n    if (PyStatus_Exception(status)) goto fail;\n#endif\n    config.isolated = 1;\n#endif\n    status = Py_InitializeFromConfig(&config);\n    if (PyStatus_Exception(status))  goto fail;\n    PyConfig_Clear(&config);\n    if (!set_kitty_run_data(run_data, from_source, NULL)) return 1;\n    PySys_SetObject(\"frozen\", Py_False);\n    return Py_RunMain();\nfail:\n    PyConfig_Clear(&config);\n    if (PyStatus_IsExit(status)) return status.exitcode;\n    single_instance_main(-1, NULL, NULL);\n    Py_ExitStatusException(status);\n}\n\n#endif\n\n// read_exe_path() {{{\n#ifdef __APPLE__\nstatic bool\nread_exe_path(char *exe, size_t buf_sz) {\n    (void)buf_sz;\n    uint32_t size = PATH_MAX;\n    char apple[PATH_MAX+1] = {0};\n    if (_NSGetExecutablePath(apple, &size) != 0) { fprintf(stderr, \"Failed to get path to executable\\n\"); return false; }\n    if (!safe_realpath(apple, exe, buf_sz)) { fprintf(stderr, \"realpath() failed on the executable's path\\n\"); return false; }\n    return true;\n}\n#elif defined(__FreeBSD__)\n#include <sys/param.h>\n#include <sys/sysctl.h>\n\nstatic bool\nread_exe_path(char *exe, size_t buf_sz) {\n    int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };\n    size_t length = buf_sz;\n    int error = sysctl(name, 4, exe, &length, NULL, 0);\n    if (error < 0 || length <= 1) {\n        fprintf(stderr, \"failed to get path to executable, sysctl() failed\\n\");\n        return false;\n    }\n    return true;\n}\n#elif defined(__NetBSD__)\n\nstatic bool\nread_exe_path(char *exe, size_t buf_sz) {\n    if (!safe_realpath(\"/proc/curproc/exe\", exe, buf_sz)) { fprintf(stderr, \"Failed to read /proc/curproc/exe\\n\"); return false; }\n    return true;\n}\n\n#elif defined(__OpenBSD__)\nstatic bool\nread_exe_path(char *exe, size_t buf_sz) {\n    const char *path = getenv(\"PATH\");\n    if (!path) { fprintf(stderr, \"No PATH environment variable set, aborting\\n\"); return false; }\n    char buf[PATH_MAX + 1] = {0};\n    strncpy(buf, path, PATH_MAX);\n    char *token = strtok(buf, \":\");\n    while (token != NULL) {\n        char q[PATH_MAX + 1] = {0};\n        safe_snprintf(q, PATH_MAX, \"%s/kitty\", token);\n        if (safe_realpath(q, exe, buf_sz)) return true;\n        token = strtok(NULL, \":\");\n    }\n    fprintf(stderr, \"kitty not found in PATH aborting\\n\");\n    return false;\n}\n\n#else\n\nstatic bool\nread_exe_path(char *exe, size_t buf_sz) {\n    if (!safe_realpath(\"/proc/self/exe\", exe, buf_sz)) { fprintf(stderr, \"Failed to read /proc/self/exe\\n\"); return false; }\n    return true;\n}\n#endif // }}}\n\nstatic bool\nis_valid_fd(int fd)\n{\n    // This is copied from the python source code as we need the exact same semantics\n    // to prevent python from giving us None for sys.stdout and friends.\n#if defined(F_GETFD) && ( \\\n        defined(__linux__) || \\\n        defined(__APPLE__) || \\\n        defined(__wasm__))\n    return fcntl(fd, F_GETFD) >= 0;\n#elif defined(__linux__)\n    int fd2 = dup(fd);\n    if (fd2 >= 0) {\n        close(fd2);\n    }\n    return (fd2 >= 0);\n#else\n    struct stat st;\n    return (fstat(fd, &st) == 0);\n#endif\n}\n\nstatic bool\nreopen_to_null(const char *mode, FILE *stream) {\n    errno = 0;\n    while (true) {\n        if (freopen(\"/dev/null\", mode, stream) != NULL) return true;\n        if (errno == EINTR) continue;\n        perror(\"Failed to re-open STDIO handle to /dev/null\");\n        return false;\n    }\n}\n\nstatic bool\nensure_working_stdio(void) {\n#define C(which, mode) { \\\n    int fd = fileno(which); \\\n    if (fd < 0) { if (!reopen_to_null(mode, which)) return false; } \\\n    else if (!is_valid_fd(fd)) { \\\n        close(fd); if (!reopen_to_null(mode, which)) return false; \\\n    }}\n    C(stdin, \"r\") C(stdout, \"w\") C(stderr, \"w\")\n    return true;\n#undef C\n}\n\nstatic bool\nis_wrapped_kitten(const char *arg) {\n    char buf[64];\n    safe_snprintf(buf, sizeof(buf)-1, \" %s \", arg);\n    return strstr(\" \" WRAPPED_KITTENS \" \", buf);\n}\n\nstatic void\nexec_kitten(int argc, char *argv[], char *exe_dir) {\n    char exe[PATH_MAX+1] = {0};\n    safe_snprintf(exe, PATH_MAX, \"%s/kitten\", exe_dir);\n    argv[0] = \"kitten\";\n    if (being_tested) {\n        printf(\"kitten_exe: %s\\n\", exe);\n        output_argv(\"argv\", argc, argv);\n        exit(0);\n    }\n    errno = 0;\n    execv(exe, argv);\n    fprintf(stderr, \"Failed to execute kitten (%s) with error: %s\\n\", exe, strerror(errno));\n    exit(1);\n}\n\nstatic bool\nparse_and_check_kitty_cli(CLISpec *cli_spec, int argc, char **argv) {\n    parse_cli_for_kitty(cli_spec, argc, argv);\n    if (cli_spec->errmsg) {\n        fprintf(stderr, \"%s\\n\", cli_spec->errmsg);\n#ifdef __APPLE__\n        os_log_error(OS_LOG_DEFAULT, \"%{public}s\", cli_spec->errmsg);\n#endif\n        return false;\n    }\n    return true;\n}\n\nstatic bool\nparse_and_check_panel_kitten_cli(CLISpec *cli_spec, int argc, char **argv) {\n    parse_cli_for_panel_kitten(cli_spec, argc, argv);\n    if (cli_spec->errmsg) {\n        fprintf(stderr, \"%s\\n\", cli_spec->errmsg);\n#ifdef __APPLE__\n        os_log_error(OS_LOG_DEFAULT, \"%{public}s\", cli_spec->errmsg);\n#endif\n        return false;\n    }\n    return true;\n}\n\nstatic int\noffset_for_plus_subcommand(int argc, char **argv, const char *name) {\n    int offset = 0;\n#define arg_eq(num, what) (strcmp(argv[num], what) == 0)\n    if (argc > 1 && argv[1][0] == '+' && strcmp(argv[1] + 1, name) == 0) {\n        offset = 1;\n    } else if (argc > 2 && arg_eq(1, \"+\") && arg_eq(2, name)) {\n        offset = 2;\n    }\n#undef arg_eq\n    return offset;\n}\n\nstatic void\nhandle_fast_commandline(CLISpec *cli_spec, const char *instance_group_prefix) {\n    CLIOptions opts = {0};\n    RAII_CLISpec(subcommand_cli_spec);\n#define swap_cli_spec \\\n            subcommand_cli_spec.original_argc = cli_spec->original_argc; \\\n            subcommand_cli_spec.original_argv = cli_spec->original_argv; \\\n            cli_spec = &subcommand_cli_spec;\n    if (instance_group_prefix == NULL) {\n        // Look for +open\n        int offset = offset_for_plus_subcommand(cli_spec->original_argc, cli_spec->original_argv, \"open\");\n        if (offset) {\n            if (!parse_and_check_kitty_cli(&subcommand_cli_spec, cli_spec->original_argc - offset, cli_spec->original_argv + offset)) exit(1);\n            swap_cli_spec;\n            opts.open_url_count = cli_spec->argc;\n            opts.open_urls = cli_spec->argv;\n        }\n    } else {\n        parse_and_check_panel_kitten_cli(\n            &subcommand_cli_spec, cli_spec->original_argc, cli_spec->original_argv);\n        swap_cli_spec;\n    }\n    if (get_bool_cli_val(cli_spec, \"help\")) return;\n    if (get_bool_cli_val(cli_spec, \"version\")) {\n        if (isatty(STDOUT_FILENO)) {\n            printf(\"\\x1b[3mkitty\\x1b[23m \\x1b[32m%s\\x1b[39m created by \\x1b[1;34mKovid Goyal\\x1b[22;39m\\n\", KITTY_VERSION);\n        } else {\n            printf(\"kitty %s created by Kovid Goyal\\n\", KITTY_VERSION);\n        }\n        exit(0);\n    }\n    opts.session = get_string_cli_val(cli_spec, \"session\");\n#ifdef __APPLE__\n    char pid_str[32];\n    snprintf(pid_str, sizeof(pid_str), \"%d\", getpid());\n    const char *ekfd = getenv(\"KITTY_EXEC_FOR_DETACH\");\n    bool is_exec_for_detach = ekfd && strcmp(getenv(\"KITTY_EXEC_FOR_DETACH\"), pid_str) == 0;\n    if (is_exec_for_detach) unsetenv(\"KITTY_EXEC_FOR_DETACH\");\n#else\n    bool is_exec_for_detach = false;\n#endif\n    if (get_bool_cli_val(cli_spec, \"detach\") && !is_exec_for_detach) {\n        const char *detached_log = get_string_cli_val(cli_spec, \"detached_log\");\n        if (being_tested) {\n            printf(\"detach: true\\n\");\n            printf(\"detached_log: %s\\n\", detached_log ? detached_log : \"\");\n            printf(\"session: %s\\n\", opts.session ? opts.session : \"\");\n            exit(0);\n        } else {\n            int fds[2] = {0};\n            if (pipe(fds) == -1) {\n                perror(\"failed to create a pipe\"); exit(1);\n            }\n\n#define reopen_or_fail(path, mode, which) { errno = 0; if (freopen(path, mode, which) == NULL) { int s = errno; fprintf(stderr, \"Failed to redirect %s to %s with error: \", #which, path); errno = s; perror(NULL); exit(1); } setlinebuf(which); }\n            if (!(opts.session && ((opts.session[0] == '-' && opts.session[1] == 0) || strcmp(opts.session, \"/dev/stdin\") == 0)))\n                reopen_or_fail(\"/dev/null\", \"rb\", stdin);\n            if (!detached_log || !detached_log[0]) detached_log = \"/dev/null\";\n            reopen_or_fail(detached_log, \"ab\", stdout);\n            reopen_or_fail(detached_log, \"ab\", stderr);\n#undef reopen_or_fail\n            if (fork() != 0) {\n                // wait until child has done setsid() before exiting so that it doesnt get a SIGHUP,\n                // see: https://github.com/kovidgoyal/kitty/issues/8680\n                char buf[4];\n                errno = 0; while (close(fds[1]) != 0 && errno == EINTR);\n                errno = 0; while(read(fds[0], buf, sizeof(buf)) == -1 && errno == EINTR);\n                exit(0);\n            }\n            errno = 0; while (close(fds[0]) != 0 && errno == EINTR);\n            setsid();\n            errno = 0; while (close(fds[1]) != 0 && errno == EINTR);\n#ifdef __APPLE__\n            // fork() without exec() is unsafe on macOS. It used to work since\n            // in this case we havent yet loaded any major Cocoa libraries but\n            // in Tahoe 26.2 Apple broke that as well. So now just exec() on --detach\n            snprintf(pid_str, sizeof(pid_str), \"%d\", getpid());\n            setenv(\"KITTY_EXEC_FOR_DETACH\", pid_str, 1);\n            char exe_path[PATH_MAX] = {0};\n            read_exe_path(exe_path, PATH_MAX);\n            execv(exe_path, cli_spec->original_argv);\n            fprintf(stderr, \"Failed to execv() for --detach with exe_path: %s\\n\", exe_path);\n            exit(1);\n#endif\n        }\n    }\n    unsetenv(\"KITTY_SI_DATA\");\n    if (get_bool_cli_val(cli_spec, \"single_instance\")) {\n        char igbuf[256];\n        opts.wait_for_single_instance_window_close = get_bool_cli_val(cli_spec, \"wait_for_single_instance_window_close\");\n        opts.instance_group = get_string_cli_val(cli_spec, \"instance_group\");\n        if (instance_group_prefix && instance_group_prefix[0]) {\n            opts.instance_group = get_string_cli_val(cli_spec, \"instance_group\");\n            if (opts.instance_group && opts.instance_group[0]) {\n                safe_snprintf(igbuf, sizeof(igbuf), \"%s-%s\", instance_group_prefix, opts.instance_group ? opts.instance_group : \"\");\n                opts.instance_group = igbuf;\n            } else {\n                opts.instance_group = instance_group_prefix;\n            }\n        }\n        if (being_tested) {\n            output_argv(\"argv\", cli_spec->original_argc, cli_spec->original_argv);\n            output_argv(\"open_urls\", opts.open_url_count, opts.open_urls);\n            output_values_for_testing(cli_spec);\n            printf(\"single_instance: 1\\n\");\n            printf(\"instance_group: %s\\n\", opts.instance_group ? opts.instance_group : \"\");\n            printf(\"session: %s\\n\", opts.session ? opts.session : \"\");\n            exit(0);\n        } else {\n            single_instance_main(cli_spec->original_argc, cli_spec->original_argv, &opts);\n        }\n    }\n}\n\nstatic bool\ndelegate_to_kitten_if_possible(int argc, char **argv, char* exe_dir) {\n    if (argc > 1 && argv[1][0] == '@') exec_kitten(argc, argv, exe_dir);\n    int offset = offset_for_plus_subcommand(argc, argv, \"kitten\");\n    if (offset && argc > offset+1) {\n        const char *kitten = argv[offset + 1];\n        if (is_wrapped_kitten(kitten)) exec_kitten(argc - offset, argv + offset, exe_dir);\n        if (strcmp(kitten, \"panel\") == 0) {\n            offset++;\n            CLISpec t = {.original_argv = argv + offset, .original_argc=argc - offset};\n            handle_fast_commandline(&t, \"panel\");\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic bool\nendswith(const char *str, const char *suffix) {\n    size_t strLen = strlen(str);\n    size_t suffixLen = strlen(suffix);\n    if (suffixLen > strLen) return false;\n    return strcmp(str + strLen - suffixLen, suffix) == 0;\n}\n\nstatic void\noutput_test_data(RunData *rd) {\n    printf(\"launched_by_launch_services: %d\\n\", rd->launched_by_launch_services);\n    printf(\"is_quick_access_terminal: %d\\n\", rd->is_quick_access_terminal);\n    char buf[PATH_MAX + 1];\n    if (rd->config_dir == NULL) {\n        if (get_config_dir(buf, sizeof(buf))) rd->config_dir = buf;\n    }\n    printf(\"config_dir: %s\\n\", rd->config_dir ? rd->config_dir : \"\");\n    output_for_testing(rd->cli_spec);\n}\n\nint\nmain(int argc_, char *argv_[], char* envp[]) {\n    if (argc_ < 1 || !argv_) { fprintf(stderr, \"Invalid argc/argv\\n\"); return 1; }\n    if (argc_ > 1 && strcmp(argv_[1], \"+testing-launcher-code\") == 0) {\n        being_tested = true;\n        memmove(argv_ + 1, argv_ + 2, (--argc_ - 1) * sizeof(argv_[0]));\n    }\n    if (!ensure_working_stdio()) return 1;\n    char exe[PATH_MAX+1] = {0};\n    if (!read_exe_path(exe, sizeof(exe))) return 1;\n    char exe_dir_buf[PATH_MAX+1] = {0};\n    strncpy(exe_dir_buf, exe, sizeof(exe_dir_buf));\n    char *exe_dir = dirname(exe_dir_buf);\n\n    RAII_ALLOC(const char, lc_ctype, NULL);\n    bool launched_by_launch_services = false;\n    const char *config_dir = NULL;\n    bool is_quick_access_terminal = false;\n    argv_array argva = {.argv = argv_, .count = argc_};\n#ifdef __APPLE__\n    lc_ctype = getenv(\"LC_CTYPE\");\n    if (lc_ctype) lc_ctype = strdup(lc_ctype);\n    char abuf[PATH_MAX+1];\n    is_quick_access_terminal = endswith(exe, \"/kitty-quick-access\");\n    if (getenv(\"KITTY_LAUNCHED_BY_LAUNCH_SERVICES\")) {\n        launched_by_launch_services = true;\n        unsetenv(\"KITTY_LAUNCHED_BY_LAUNCH_SERVICES\");\n        if (!get_config_dir(abuf, sizeof(abuf))) abuf[0] = 0;\n        config_dir = abuf;\n        if (launched_by_launch_services && config_dir[0]) {\n            char cbuf[PATH_MAX];\n            safe_snprintf(cbuf, sizeof(cbuf), \"%s/macos-launch-services-cmdline\", config_dir);\n            if (!get_argv_from(cbuf, argva.argv[0], &argva)) exit(1);\n        }\n    }\n#else\n    (void)endswith;\n#endif\n    (void)read_full_file;\n    RAII_CLISpec(cli_spec);\n    bool handle_fast_commandline_called = delegate_to_kitten_if_possible(argva.count, argva.argv, exe_dir);\n    bool ok = parse_and_check_kitty_cli(&cli_spec, argva.count, argva.argv);\n    if (!ok) return 1;\n    if (!handle_fast_commandline_called) handle_fast_commandline(&cli_spec, NULL);\n    int ret=0;\n    char lib[PATH_MAX+1] = {0};\n    if (KITTY_LIB_PATH[0] == '/') {\n        safe_snprintf(lib, PATH_MAX, \"%s\", KITTY_LIB_PATH);\n    } else {\n        safe_snprintf(lib, PATH_MAX, \"%s/%s\", exe_dir, KITTY_LIB_PATH);\n    }\n    RunData run_data = {\n        .exe = exe, .exe_dir = exe_dir, .lib_dir = lib, .cli_spec = &cli_spec, .lc_ctype = lc_ctype,\n        .launched_by_launch_services=launched_by_launch_services, .config_dir = config_dir, .is_quick_access_terminal=is_quick_access_terminal,\n    };\n    if (being_tested) output_test_data(&run_data);\n    else ret = run_embedded(&run_data);\n    free_argv_array(&argva);\n    single_instance_main(-1, NULL, NULL);\n    if (!being_tested) Py_FinalizeEx();\n    return ret;\n}\n"
  },
  {
    "path": "kitty/launcher/shlex.h",
    "content": "/*\n * shlex.h\n * Copyright (C) 2025 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <sys/types.h>\n#include <stdbool.h>\n#include <stdlib.h>\n\ntypedef enum { NORMAL, WORD, STRING_WITHOUT_ESCAPES, STRING_WITH_ESCAPES, ANSI_C_QUOTED } ShlexEnum;\n\ntypedef struct {\n    const char *src;\n    bool support_ansi_c_quoting, allow_empty;\n    char *buf;\n    size_t src_sz, src_pos, word_start, buf_pos;\n    ShlexEnum state;\n    const char *err;\n} ShlexState;\n\n\nstatic bool\nalloc_shlex_state(ShlexState *s, const char *src, size_t src_sz, bool support_ansi_c_quoting) {\n    *s = (ShlexState){\n        // for NULL termination and some safety we add 16 bytes\n        .src=src, .src_sz=src_sz, .support_ansi_c_quoting=support_ansi_c_quoting, .buf=malloc(16 + src_sz)\n    };\n    return s->buf != NULL;\n}\n\nstatic void\ndealloc_shlex_state(ShlexState *s) {\n    free(s->buf); s->buf = NULL;\n    *s = (ShlexState){0};\n}\n#define WHITESPACE ' ': case '\\n': case '\\t': case '\\r'\n#define STRING_WITH_ESCAPES_DELIM '\"'\n#define STRING_WITHOUT_ESCAPES_DELIM '\\''\n#define ESCAPE_CHAR '\\\\'\n\nstatic void\nstart_word(ShlexState *self) {\n    self->word_start = self->src_pos - 1;\n    self->buf_pos = 0;\n}\n\nstatic void\nwrite_ch(ShlexState *self, char ch) {\n    self->buf[self->buf_pos++] = ch;\n}\n\nstatic unsigned\nencode_utf8(unsigned long ch, char* dest) {\n    if (ch < 0x80) { // only lower 7 bits can be 1\n        dest[0] = (char)ch;  // 0xxxxxxx\n        return 1;\n    }\n    if (ch < 0x800) { // only lower 11 bits can be 1\n        dest[0] = (ch>>6) | 0xC0; // 110xxxxx\n        dest[1] = (ch & 0x3F) | 0x80;  // 10xxxxxx\n        return 2;\n    }\n    if (ch < 0x10000) { // only lower 16 bits can be 1\n        dest[0] = (ch>>12) | 0xE0; // 1110xxxx\n        dest[1] = ((ch>>6) & 0x3F) | 0x80;  // 10xxxxxx\n        dest[2] = (ch & 0x3F) | 0x80;       // 10xxxxxx\n        return 3;\n    }\n    if (ch < 0x110000) { // only lower 21 bits can be 1\n        dest[0] = (ch>>18) | 0xF0; // 11110xxx\n        dest[1] = ((ch>>12) & 0x3F) | 0x80; // 10xxxxxx\n        dest[2] = ((ch>>6) & 0x3F) | 0x80;  // 10xxxxxx\n        dest[3] = (ch & 0x3F) | 0x80; // 10xxxxxx\n        return 4;\n    }\n    return 0;\n}\n\nstatic void\nwrite_unich(ShlexState *self, unsigned long ch) {\n    self->buf_pos += encode_utf8(ch, self->buf + self->buf_pos);\n}\n\n\nstatic size_t\nget_word(ShlexState *self) {\n    size_t ans = self->buf_pos; self->buf_pos = 0;\n    self->buf[ans] = 0;\n    self->allow_empty = false;\n    return ans;\n}\n\nstatic char\nread_ch(ShlexState *self) {\n    return self->src[self->src_pos++];\n}\n\nstatic bool\nwrite_escape_ch(ShlexState *self) {\n    if (self->src_pos < self->src_sz) {\n        char nch = read_ch(self);\n        write_ch(self, nch);\n        return true;\n    }\n    return false;\n}\n\nstatic bool\nwrite_control_ch(ShlexState *self) {\n    if (self->src_pos >= self->src_sz) {\n        self->err = \"Trailing \\\\c escape at end of input data\";\n        return false;\n    }\n    char ch = read_ch(self);\n    write_ch(self, ch & 0x1f);\n    return true;\n}\n\nstatic void\nread_valid_digits(ShlexState *self, int max, char *output, bool(*is_valid)(char ch)) {\n    for (int i = 0; i < max && self->src_pos < self->src_sz; i++, output++) {\n        char ch = read_ch(self);\n        if (!is_valid(ch)) { self->src_pos--; break; }\n        *output = ch;\n    }\n}\n\nstatic bool\nis_octal_digit(char ch) { return '0' <= ch && ch <= '7'; }\n\nstatic bool\nis_hex_digit(char ch) { return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F'); }\n\nstatic void\nwrite_octal_ch(ShlexState *self, char ch) {\n    char chars[4] = {ch, 0, 0, 0};\n    read_valid_digits(self, 2, chars + 1, is_octal_digit);\n    write_unich(self, strtol(chars, NULL, 8));\n}\n\nstatic bool\nwrite_unicode_ch(ShlexState *self, int max) {\n    char chars[16] = {0};\n    read_valid_digits(self, max, chars, is_hex_digit);\n    if (!chars[0]) { self->err = \"Trailing unicode escape at end of input data\"; return false; }\n    write_unich(self, strtol(chars, NULL, 16));\n    return true;\n}\n\nstatic bool\nwrite_ansi_escape_ch(ShlexState *self) {\n    if (self->src_pos >= self->src_sz) { self->err = \"Trailing backslash at end of input data\"; return false; }\n    char ch = read_ch(self);\n    switch(ch) {\n        case 'a': write_ch(self, '\\a'); return true;\n        case 'b': write_ch(self, '\\b'); return true;\n        case 'e': case 'E': write_ch(self, 0x1b); return true;\n        case 'f': write_ch(self, '\\f'); return true;\n        case 'n': write_ch(self, '\\n'); return true;\n        case 'r': write_ch(self, '\\r'); return true;\n        case 't': write_ch(self, '\\t'); return true;\n        case 'v': write_ch(self, '\\v'); return true;\n        case '\\\\': write_ch(self, '\\\\'); return true;\n        case '\\'': write_ch(self, '\\''); return true;\n        case '\\\"': write_ch(self, '\\\"'); return true;\n        case '\\?': write_ch(self, '\\?'); return true;\n\n        case 'c': return write_control_ch(self);\n        case 'x': return write_unicode_ch(self, 2);\n        case 'u': return write_unicode_ch(self, 4);\n        case 'U': return write_unicode_ch(self, 8);\n        case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': write_octal_ch(self, ch); return true;\n        default:\n            write_ch(self, ch); return true;\n    }\n}\n\nstatic void\nset_state(ShlexState *self, ShlexEnum s) {\n    self->state = s;\n}\n\nstatic ssize_t\nnext_word(ShlexState *self) {\n#define write_escaped_or_fail() if (!write_escape_ch(self)) { self->err = \"Trailing backslash at end of input data\"; return -1; }\n    char prev_word_ch = 0;\n    while (self->src_pos < self->src_sz) {\n        char ch = read_ch(self);\n        switch(self->state) {\n            case NORMAL:\n                switch(ch) {\n                    case WHITESPACE: break;\n                    case STRING_WITH_ESCAPES_DELIM: set_state(self, STRING_WITH_ESCAPES); start_word(self); break;\n                    case STRING_WITHOUT_ESCAPES_DELIM: set_state(self, STRING_WITHOUT_ESCAPES); start_word(self); break;\n                    case ESCAPE_CHAR: start_word(self); write_escaped_or_fail(); set_state(self, WORD); break;\n                    default: set_state(self, WORD); start_word(self); write_ch(self, ch); prev_word_ch = ch; break;\n                }\n                break;\n            case WORD:\n                switch(ch) {\n                    case WHITESPACE: set_state(self, NORMAL); if (self->buf_pos || self->allow_empty) return get_word(self); break;\n                    case STRING_WITH_ESCAPES_DELIM: set_state(self, STRING_WITH_ESCAPES); break;\n                    case STRING_WITHOUT_ESCAPES_DELIM:\n                        if (self->support_ansi_c_quoting && prev_word_ch == '$') { self->buf_pos--; set_state(self, ANSI_C_QUOTED); }\n                        else set_state(self, STRING_WITHOUT_ESCAPES);\n                        break;\n                    case ESCAPE_CHAR: write_escaped_or_fail(); break;\n                    default: write_ch(self, ch); prev_word_ch = ch; break;\n                } break;\n            case STRING_WITHOUT_ESCAPES:\n                switch(ch) {\n                    case STRING_WITHOUT_ESCAPES_DELIM: set_state(self, WORD); self->allow_empty = true; break;\n                    default: write_ch(self, ch); break;\n                } break;\n            case STRING_WITH_ESCAPES:\n                switch(ch) {\n                    case STRING_WITH_ESCAPES_DELIM: set_state(self, WORD); self->allow_empty = true; break;\n                    case ESCAPE_CHAR: write_escaped_or_fail(); break;\n                    default: write_ch(self, ch); break;\n                } break;\n            case ANSI_C_QUOTED:\n                switch(ch) {\n                    case STRING_WITHOUT_ESCAPES_DELIM: set_state(self, WORD); self->allow_empty = true; break;\n                    case ESCAPE_CHAR: if (!write_ansi_escape_ch(self)) return -1; break;\n                    default: write_ch(self, ch); break;\n                } break;\n        }\n    }\n    switch (self->state) {\n        case WORD:\n            self->state = NORMAL;\n            if (self->buf_pos || self->allow_empty) return get_word(self);\n            break;\n        case STRING_WITH_ESCAPES: case STRING_WITHOUT_ESCAPES: case ANSI_C_QUOTED:\n            self->err = \"Unterminated string at the end of input\";\n            self->state = NORMAL;\n            return -1;\n        case NORMAL:\n            break;\n    }\n    return -2;\n#undef write_escaped_or_fail\n}\n\n\n"
  },
  {
    "path": "kitty/launcher/single-instance.c",
    "content": "/*\n * single-instance.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n// We rely on data-types.h including Python.h which defines _DARWIN_C_SOURCE\n// which we need for _CS_DARWIN_USER_CACHE_DIR\n#include \"../data-types.h\"\n\n#include \"launcher.h\"\n#include \"../safe-wrappers.h\"\n#include <stdbool.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <pwd.h>\n#include <sys/types.h>\n#include <stdlib.h>\n\n#define CHARSETS_STORAGE static inline\n#define NO_SINGLE_BYTE_CHARSETS\n#include \"../charsets.c\"\n\n#define fail_on_errno(msg) { perror(msg); do_exit(1); }\n\nvoid\nlog_error(const char *fmt, ...) {\n    va_list ar;\n    va_start(ar, fmt);\n    vfprintf(stderr, fmt, ar);\n    va_end(ar);\n}\n\ntypedef struct cleanup_data {\n    int fd1, fd2;\n    bool close_fd1, close_fd2;\n    char path1[sizeof(struct sockaddr_un) + 16], path2[sizeof(struct sockaddr_un) + 16];\n} cleanup_data;\n\nstruct {\n    cleanup_data si, notify;\n} cleanup_entries = {0};\n\nstatic void\ndo_cleanup(cleanup_data *d) {\n    if (d->path1[0]) unlink(d->path1);\n    if (d->path2[0]) unlink(d->path2);\n    if (d->close_fd1) safe_close(d->fd1, __FILE__, __LINE__);\n    if (d->close_fd2) safe_close(d->fd2, __FILE__, __LINE__);\n}\n\nstatic void\ncleanup(void) {\n    do_cleanup(&cleanup_entries.notify);\n    do_cleanup(&cleanup_entries.si);\n}\n\nstatic void\ndo_exit(int code) {\n    cleanup();\n    exit(code);\n}\n\n\n#ifndef __APPLE__\nstatic bool\nis_ok_tmpdir(const char *x) {\n    if (!x || !x[0]) return false;\n    char path[2048];\n    snprintf(path, sizeof(path), \"%s/kitty-si-test-tmpdir-XXXXXXXXXXXX\", x);\n    int fd = safe_mkstemp(path);\n    if (fd > -1) {\n        safe_close(fd, __FILE__, __LINE__);\n        unlink(path);\n        return true;\n    }\n    return false;\n}\n#endif\n\nstatic void\nget_socket_dir(char *output, size_t output_capacity) {\n#define ret_if_ok(x) if (is_ok_tmpdir(x)) { if (snprintf(output, output_capacity, \"%s\", x) < output_capacity-1); return; }\n#ifdef __APPLE__\n    if (confstr(_CS_DARWIN_USER_CACHE_DIR, output, output_capacity)) return;\n    snprintf(output, output_capacity, \"%s\", \"/Library/Caches\");\n#else\n#define test_env(x) { const char *e = getenv(#x); ret_if_ok(e); }\n    test_env(XDG_RUNTIME_DIR); test_env(TMPDIR); test_env(TEMP); test_env(TMP);\n\n    ret_if_ok(\"/tmp\"); ret_if_ok(\"/var/tmp\"); ret_if_ok(\"/usr/tmp\");\n\n    test_env(HOME);\n\n    const char *home = getpwuid(geteuid())->pw_dir;\n    ret_if_ok(home);\n\n    if (getcwd(output, output_capacity)) return;\n    snprintf(output, output_capacity, \"%s\", \".\");\n#undef test_env\n#endif\n}\n\nstatic void\nset_single_instance_socket(int fd) {\n    if (listen(fd, 5) != 0) fail_on_errno(\"Failed to listen on single instance socket\");\n    char buf[256];\n    snprintf(buf, sizeof(buf), \"%d\", fd);\n    setenv(\"KITTY_SI_DATA\", buf, 1);\n}\n\ntypedef struct membuf {\n    char *data;\n    size_t used, capacity;\n} membuf;\n\nstatic void\nwrite_to_membuf(membuf *m, void *data, size_t sz) {\n    ensure_space_for(m, data, char, m->used + sz, capacity, 8192, false);\n    memcpy(m->data + m->used, data, sz); m->used += sz;\n}\n\nstatic void\nwrite_escaped_char(membuf *m, char ch) {\n    char buf[8];\n    int n = snprintf(buf, sizeof(buf), \"\\\\u%04x\", ch);\n    write_to_membuf(m, buf, n);\n}\n\nstatic void\nwrite_json_string(membuf *m, const char *src, size_t src_len) {\n    ensure_space_for(m, data, char, m->used + 2 + 8 * src_len, capacity, 8192, false);\n    m->data[m->used++] = '\"';\n    uint32_t codep = 0;\n    UTF8State state = 0, prev = UTF8_ACCEPT;\n    for (size_t i = 0; i < src_len; i++) {\n        switch(decode_utf8(&state, &codep, src[i])) {\n            case UTF8_ACCEPT:\n                switch(codep) {\n                    case '\"': write_to_membuf(m, \"\\\\\\\"\", 2); break;\n                    case '\\\\': write_to_membuf(m, \"\\\\\\\\\", 2); break;\n                    case '\\t': write_to_membuf(m, \"\\\\t\", 2); break;\n                    case '\\n': write_to_membuf(m, \"\\\\n\", 2); break;\n                    case '\\r': write_to_membuf(m, \"\\\\r\", 2); break;\nSTART_ALLOW_CASE_RANGE\n                    case 0 ... 8: case 11: case 12: case 14 ... 31:\n                        write_escaped_char(m, codep); break;\nEND_ALLOW_CASE_RANGE\n                    default: m->used += encode_utf8(codep, m->data + m->used);\n                }\n                break;\n            case UTF8_REJECT:\n                state = UTF8_ACCEPT;\n                if (prev != UTF8_ACCEPT && i > 0) i--;\n                break;\n        }\n        prev = state;\n    }\n    m->data[m->used++] = '\"';\n}\n\nstatic void\nwrite_json_string_array(membuf *m, int argc, char *argv[]) {\n    write_to_membuf(m, \"[\", 1);\n    for (int i = 0; i < argc; i++) {\n        if (i) write_to_membuf(m, \",\", 1);\n        write_json_string(m, argv[i], strlen(argv[i]));\n    }\n    write_to_membuf(m, \"]\", 1);\n}\n\nstatic void\nread_till_eof(FILE *f, membuf *m) {\n    while (!feof(f)) {\n        ensure_space_for(m, data, char, m->used + 8192, capacity, 4*8192, false);\n        m->used += fread(m->data, 1, m->capacity - m->used, f);\n        if (ferror(f)) { fclose(f); fail_on_errno(\"Failed to read from session file\"); }\n    }\n    // ensure NULL termination\n    write_to_membuf(m, \"\\0\", 1); m->used--;\n    fclose(f);\n}\n\n\nstatic bool\nbind_unix_socket(int s, const char *basename, struct sockaddr_un *addr, cleanup_data *cleanup) {\n    addr->sun_family = AF_UNIX;\n    const size_t blen = strlen(basename);\n    // First try abstract socket\n    addr->sun_path[0] = 0;\n    memcpy(addr->sun_path + 1, basename, blen + 1);\n    if (safe_bind(s, (struct sockaddr*)addr, sizeof(sa_family_t) + 1 + blen) > -1) return true;\n    if (errno != ENOENT) return false;\n    // Try an actual filesystem file\n    get_socket_dir(addr->sun_path, sizeof(addr->sun_path) - blen - 2);\n    size_t dlen = strlen(addr->sun_path);\n    while (dlen && addr->sun_path[dlen-1] == '/') dlen--;\n    if (snprintf(addr->sun_path + dlen, sizeof(addr->sun_path) - dlen, \"/%s\", basename) < blen + 1) {\n        fprintf(stderr, \"Socket directory has path too long for single instance socket file %s\\n\", addr->sun_path);\n        do_exit(1);\n    }\n    // First lock the socket file using a separate lock file\n    char lock_file_path[sizeof(addr->sun_path) + 16];\n    snprintf(lock_file_path, sizeof(lock_file_path), \"%s.lock\", addr->sun_path);\n    int fd = safe_open(lock_file_path, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR);\n    if (fd == -1) return false;\n    cleanup->close_fd2 = true; cleanup->fd2 = fd;\n    snprintf(cleanup->path2, sizeof(cleanup->path2), \"%s\", lock_file_path);\n    if (safe_lockf(fd, F_TLOCK, 0) != 0) {\n        int saved_errno = errno;\n        safe_close(fd, __FILE__, __LINE__);\n        errno = saved_errno;\n        if (errno == EAGAIN || errno == EACCES) errno = EADDRINUSE;  // client\n        return false;\n    }\n    // First unlink the socket file and then try to bind it.\n    if (unlink(addr->sun_path) != 0 && errno != ENOENT) return false;\n    if (safe_bind(s, (struct sockaddr*)addr, sizeof(*addr)) > -1) {\n        snprintf(cleanup->path1, sizeof(cleanup->path1), \"%s\", addr->sun_path);\n        return true;\n    }\n    return false;\n}\n\nstatic int\ncreate_unix_socket(void) {\n    int s = socket(AF_UNIX, SOCK_STREAM, 0);\n    if (s < 0) fail_on_errno(\"Failed to create single instance socket object\");\n    int flags;\n    if ((flags = fcntl(s, F_GETFD)) == -1) fail_on_errno(\"Failed to get fcntl flags for single instance socket\");\n    if (fcntl(s, F_SETFD, flags | FD_CLOEXEC) == -1) fail_on_errno(\"Failed to set single instance socket to CLOEXEC\");\n    return s;\n}\n\nextern char **environ;\n\nstatic void\ntalk_to_instance(int s, struct sockaddr_un *server_addr, int argc, char *argv[], const CLIOptions *opts) {\n    cleanup_entries.si.path2[0] = 0; cleanup_entries.si.path1[0] = 0;\n    membuf session_data = {0};\n    char *session_path = NULL;\n    if (opts->session && opts->session[0]) {\n        if (strcmp(opts->session, \"none\") == 0) {\n            session_data.data = \"none\"; session_data.used = 4;\n        } else if (strcmp(opts->session, \"-\") == 0) {\n            read_till_eof(stdin, &session_data);\n            session_path = malloc(PATH_MAX + 8);\n            if (!session_path) fail_on_errno(\"Failed to alloc space for session_path\");\n            if (getcwd(session_path, PATH_MAX + 1) == NULL) fail_on_errno(\"Failed to getcwd()\");\n            char *p = session_path + strlen(session_path);\n            *(p++) = '/'; *(p++) = '-'; *p = 0;\n        } else {\n            FILE *f = safe_fopen(opts->session, \"r\");\n            if (f == NULL) fail_on_errno(\"Failed to open session file for reading\");\n            read_till_eof(f, &session_data);\n            session_path = realpath(opts->session, NULL);\n            if (session_path == NULL) fail_on_errno(\"Failed to call realpath() on session file\");\n        }\n    }\n    membuf output = {0};\n#define w(literal) write_to_membuf(&output, literal, sizeof(literal)-1)\n    w(\"{\\\"cmd\\\":\\\"new_instance\\\",\\\"session_data\\\":\");\n    if (session_data.used) write_json_string(&output, session_data.data, session_data.used);\n    else write_json_string(&output, \"\", 0);\n    w(\",\\\"session_arg\\\":\");\n    if (opts->session && opts->session[0]) write_json_string(&output, opts->session, strlen(opts->session));\n    else write_json_string(&output, \"\", 0);\n    w(\",\\\"session_path\\\":\");\n    if (session_path && session_path[0]) write_json_string(&output, session_path, strlen(session_path));\n    else write_json_string(&output, \"\", 0);\n    free(session_path);\n    w(\",\\\"args\\\":\"); write_json_string_array(&output, argc, argv);\n    char cwd[4096];\n    if (!getcwd(cwd, sizeof(cwd))) fail_on_errno(\"Failed to get cwd\");\n    w(\",\\\"cwd\\\":\"); write_json_string(&output, cwd, strlen(cwd));\n    w(\",\\\"environ\\\":{\");\n    char **e = environ;\n    for (; *e; e++) {\n        const char *eq = strchr(*e, '=');\n        if (eq) {\n            if (e != environ) write_to_membuf(&output, \",\", 1);\n            write_json_string(&output, *e, eq - *e);\n            w(\":\");\n            write_json_string(&output, eq + 1, strlen(eq + 1));\n        }\n    }\n    w(\"}\");\n\n    w(\",\\\"cmdline_args_for_open\\\":\");\n    if (opts->open_url_count) write_json_string_array(&output, opts->open_url_count, opts->open_urls);\n    else w(\"[]\");\n\n    w(\",\\\"notify_on_os_window_death\\\":\");\n    int notify_socket = -1;\n    if (opts->wait_for_single_instance_window_close) {\n        notify_socket = create_unix_socket();\n        cleanup_entries.notify.fd1 = notify_socket; cleanup_entries.notify.close_fd1 = true;\n        struct sockaddr_un server_addr;\n        char addr[128];\n        snprintf(addr, sizeof(addr), \"kitty-os-window-close-notify-%d-%d\", getpid(), geteuid());\n        if (!bind_unix_socket(notify_socket, addr, &server_addr, &cleanup_entries.notify)) fail_on_errno(\"Failed to bind notification socket\");\n        size_t len = strlen(server_addr.sun_path);\n        if (len == 0) len = 1 + strlen(server_addr.sun_path +1);\n        if (listen(notify_socket, 5) != 0) fail_on_errno(\"Failed to listen on notify socket\");\n        write_json_string(&output, server_addr.sun_path, len);\n    } else w(\"null\");\n\n    w(\"}\");\n#undef w\n    size_t addr_len = sizeof(sa_family_t);\n    if (!server_addr->sun_path[0]) addr_len += 1 + strlen(server_addr->sun_path + 1);\n    else addr_len = sizeof(*server_addr);\n    if (safe_connect(s, (struct sockaddr*)server_addr, addr_len) != 0) {\n        fail_on_errno(\"Failed to connect to single instance socket\");\n    }\n    size_t pos = 0;\n    while (pos < output.used) {\n        errno = 0;\n        ssize_t nbytes = write(s, output.data + pos, output.used - pos);\n        if (nbytes <= 0) {\n            if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) continue;\n            break;\n        }\n        pos += nbytes;\n    }\n    if (pos < output.used) fail_on_errno(\"Failed to write message to single instance socket\");\n    shutdown(s, SHUT_RDWR);\n    safe_close(s, __FILE__, __LINE__);\n    if (notify_socket > -1) {\n        int fd = safe_accept(notify_socket, NULL, NULL);\n        if (fd < 0) fail_on_errno(\"Failed to accept connection on notify socket\");\n        char rbuf;\n        while (true) {\n            ssize_t n = recv(notify_socket, &rbuf, 1, 0);\n            if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) continue;\n            break;\n        }\n        shutdown(notify_socket, SHUT_RDWR);\n        safe_close(notify_socket, __FILE__, __LINE__);\n    }\n}\n\nvoid\nsingle_instance_main(int argc, char *argv[], const CLIOptions *opts) {\n    if (argc == -1) { cleanup(); return; }\n    struct sockaddr_un server_addr;\n    char addr_buf[sizeof(server_addr.sun_path)-1];\n    if (opts->instance_group) snprintf(addr_buf, sizeof(addr_buf), \"kitty-ipc-%d-%s\", geteuid(), opts->instance_group);\n    else snprintf(addr_buf, sizeof(addr_buf), \"kitty-ipc-%d\", geteuid());\n\n    int s = create_unix_socket();\n    cleanup_entries.si.fd1 = s; cleanup_entries.si.close_fd1 = true;\n    if (!bind_unix_socket(s, addr_buf, &server_addr, &cleanup_entries.si)) {\n        if (errno == EADDRINUSE) { talk_to_instance(s, &server_addr, argc, argv, opts); do_exit(0); }\n        else fail_on_errno(\"Failed to bind single instance socket\");\n    } else set_single_instance_socket(s);\n}\n"
  },
  {
    "path": "kitty/launcher/utils.h",
    "content": "/*\n * utils.h\n * Copyright (C) 2025 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include <stddef.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <pwd.h>\n#include <unistd.h>\n#include <stdio.h>\n#include <string.h>\n#include <limits.h>\n#include <errno.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <limits.h>\n\n\nstatic const char* home = NULL;\n\nstatic void\nensure_home_path(void) {\n    if (home) return;\n    home = getenv(\"HOME\");\n    if (!home || !home[0]) {\n        struct passwd* pw = getpwuid(geteuid());\n        if (pw) home = pw->pw_dir;\n    }\n    if (!home || !home[0]) {\n        fprintf(stderr, \"Fatal error: Cannot determine home directory\\n\"); exit(1);\n    }\n}\n\n#define safe_snprintf(buf, sz, fmt, ...) { int n = snprintf(buf, sz, fmt, __VA_ARGS__); if (n < 0 || (size_t)n >= sz) { fprintf(stderr, \"Out of buffer space calling sprintf for format: %s at line: %d\\n\", fmt, __LINE__); exit(1); }}\n\nstatic const char*\nhome_path_for(const char *username) {\n    struct passwd* pw = getpwnam(username);\n    if (pw) return pw->pw_dir;\n    return NULL;\n}\n\nstatic void\nexpand_tilde(const char* path, char *ans, size_t ans_sz) {\n    if (path[0] != '~') {\n        safe_snprintf(ans, ans_sz, \"%s\", path);\n        return;\n    }\n    const char *prefix = NULL, *sep = \"\";\n    if (path[1] == '/' || path[1] == '\\0') {\n        // If the path is \"~\" or \"~/something\", get the current user's home directory\n        ensure_home_path();\n        prefix = home;\n    } else {\n        // If the path is \"~user/something\", get the specified user's home directory\n        char *slash = (char*)strchr(path, '/');  // path is temporarily modified here and then restored\n        if (slash) {\n            *slash = 0; prefix = home_path_for(path + 1); *slash = '/';\n        } else prefix = home_path_for(path + 1);\n        if (prefix) path = slash ? slash - 1 : \"a\";\n        else {\n            prefix = \"\";\n            path--;\n        }\n    }\n    // Construct the expanded path\n    safe_snprintf(ans, ans_sz, \"%s%s%s\", prefix, sep, path + 1);\n}\n\nstatic size_t\nclean_path(char *path) {\n    char *write_ptr = path;\n    char* read_ptr = path;\n    while (*read_ptr) {\n        if (read_ptr[0] != '/') { *write_ptr++ = *read_ptr++; continue; }\n        // we have /\n        if (read_ptr[1] == '/') { read_ptr++; continue; } // skip one slash of double slash\n        if (read_ptr[1] != '.') { *write_ptr++ = *read_ptr++; continue; }\n        // we have /.\n        if (read_ptr[2] == '/' || !read_ptr[2]) { read_ptr += 2; continue; } // skip /./\n        if (read_ptr[2] != '.') { *write_ptr++ = *read_ptr++; continue; }\n        // we have /..\n        if (read_ptr[3] == '/' || !read_ptr[3]) {\n            read_ptr += 3;\n            while (write_ptr > path) {\n                write_ptr--;\n                if (*write_ptr == '/') break;\n            }\n        } else *write_ptr++ = *read_ptr++;\n    }\n    // remove trailing slashes\n    while (write_ptr > path + 1 && *(write_ptr - 1) == '/') write_ptr--;\n    // Null-terminate the normalized path\n    *write_ptr++ = '\\0';\n    return write_ptr - path - 1;\n}\n\nstatic size_t\nlexical_absolute_path(const char* relative, char *output, size_t outsz) {\n    size_t rlen = strlen(relative);\n    char *limit = output + outsz;\n    char* write_ptr = output;      // Points to the location to write normalized characters\n#define _ensure_space(n) if (write_ptr + n + 1 >= limit) { fprintf(stderr, \"Out of buffer space making absolute path for: %s with cwd: %s\\n\", relative, output); exit(1); }\n    if (relative[0] != '/') {\n        if (!getcwd(output, outsz)) {\n            perror(\"Getting the current working directory failed with error\");\n            exit(1);\n        }\n        size_t cwdlen = strlen(output);\n        write_ptr = output + cwdlen;\n        _ensure_space(cwdlen + rlen + 2);\n        if (rlen && cwdlen && *(write_ptr - 1) != '/') *(write_ptr++) = '/';\n    } else { _ensure_space(rlen + 2); }\n#undef _ensure_space\n    // Append the relative path\n    memcpy(write_ptr, relative, rlen);\n    *(write_ptr + rlen) = 0;\n    size_t ans = clean_path(output);\n    // Ensure the path is not empty\n    if (output[0] == '\\0') {\n        output[0] = '/'; output[1] = 0;\n        ans = 1;\n    }\n    return ans;\n}\n\nstatic bool\nmakedirs_cleaned(char *path, int mode, struct stat *buffer) {\n    if (stat(path, buffer) == 0) {\n        if (S_ISDIR(buffer->st_mode)) return true;\n        errno = ENOTDIR;\n        return false;\n    }\n    if (errno == ENOTDIR) return false;\n    char *p = strrchr(path, '/');\n    if (p && p > path) {\n        p[0] = 0;\n        bool parent_created = makedirs_cleaned(path, mode, buffer);\n        p[0] = '/';\n        if (!parent_created) return false;\n    }\n    // Now parent exists\n    return mkdir(path, mode) == 0;\n}\n\nstatic bool\nmakedirs(const char *path, int mode) {\n    struct stat buffer;\n    char pbuf[PATH_MAX];\n    lexical_absolute_path(path, pbuf, sizeof(pbuf));\n    return makedirs_cleaned(pbuf, mode, &buffer);\n}\n\nstatic bool\nis_dir_ok_for_config(char *q) {\n    size_t len = strlen(q);\n    memcpy(q + len, \"/kitty\", sizeof(\"/kitty\"));\n    len += sizeof(\"/kitty\") - 1;\n    memcpy(q + len, \"/kitty.conf\", sizeof(\"/kitty.conf\"));\n    if (access(q, F_OK) != 0) return false;\n    q[len] = 0;\n    return access(q, W_OK) == 0;\n}\n\nstatic bool\nget_config_dir(char *output, size_t outputsz) {\n    const char *q;\n    char buf1[PATH_MAX], buf2[PATH_MAX];\n#define expand(x, dest, sz) { expand_tilde(x, buf1, sizeof(buf1)); lexical_absolute_path(buf1, dest, sz); }\n    q = getenv(\"KITTY_CONFIG_DIRECTORY\"); if (q && q[0]) { expand(q, output, outputsz); return true; }\n#define check_and_ret(x) if (x && x[0]) { expand(x, output, outputsz); if (is_dir_ok_for_config(output)) return true; }\n    q = getenv(\"XDG_CONFIG_HOME\"); check_and_ret(q);\n    check_and_ret(\"~/.config\");\n#ifdef __APPLE__\n    check_and_ret(\"~/Library/Preferences\");\n#endif\n    q = getenv(\"XDG_CONFIG_DIRS\");\n    if (q && q[0]) {\n        safe_snprintf(buf2, sizeof(buf2), \"%s\", q);\n        char *s, *token = strtok_r(buf2, \":\", &s);\n        while (token) {\n            check_and_ret(token);\n            token = strtok_r(NULL, \":\", &s);\n        }\n    }\n    q = getenv(\"XDG_CONFIG_HOME\");\n    if (!q || !q[0]) q = \"~/.config\";\n    expand(q, buf2, sizeof(buf2));\n    safe_snprintf(output, outputsz, \"%s/kitty\", buf2);\n    if (makedirs(output, 0755)) return true;\n    return false;\n#undef expand\n#undef check_and_ret\n}\n\n\nstatic ssize_t\nsafe_read_stream(void* ptr, size_t size, FILE* stream) {\n    errno = 0;\n    ssize_t total = 0, bytes_to_read = size;\n    while (total < bytes_to_read) {\n        size_t n = fread((char*)ptr + total, 1, bytes_to_read - total, stream);\n        if (n > 0) total += n;\n        else {\n            if (!ferror(stream)) break;  // eof\n            if (errno != EINTR) return -1;\n            clearerr(stream);\n        }\n    }\n    return total;\n}\n\nstatic char*\nread_full_file(const char* filename, size_t *sz) {\n    FILE* file = NULL;\n    errno = EINTR;\n    while (file == NULL && errno == EINTR) file = fopen(filename, \"rb\");\n    if (!file) return NULL;\n    fseek(file, 0, SEEK_END);\n    unsigned long file_size = ftell(file);\n    rewind(file);\n    char* buffer = (char*)malloc(file_size + 1); // +1 for the null terminator\n    if (!buffer) {\n        errno = EINTR; while (errno == EINTR && fclose(file) != 0);\n        errno = ENOMEM;\n        return NULL;\n    }\n    ssize_t q = safe_read_stream(buffer, file_size, file);\n    int saved = errno;\n    errno = EINTR; while (errno == EINTR && fclose(file) != 0);\n    errno = saved;\n    if (q < 0) { free(buffer); buffer = NULL; if (sz) *sz = 0; }\n    else { if (sz) { *sz = q; } buffer[q] = 0; }\n    return buffer;\n}\n"
  },
  {
    "path": "kitty/layout/__init__.py",
    "content": ""
  },
  {
    "path": "kitty/layout/base.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Generator, Iterable, Iterator, Sequence\nfrom functools import partial\nfrom itertools import repeat\nfrom typing import Any, Callable, NamedTuple\n\nfrom kitty.borders import BorderColor\nfrom kitty.fast_data_types import BOTTOM_EDGE, RIGHT_EDGE, Region, get_options, set_active_window, viewport_for_window\nfrom kitty.options.types import Options\nfrom kitty.types import Edges, NeighborsMap, WindowGeometry, WindowMapper, WindowResizeDragData\nfrom kitty.typing_compat import WindowType\nfrom kitty.window_list import WindowGroup, WindowList\n\n\nclass BorderLine(NamedTuple):\n    edges: Edges = Edges()\n    color: BorderColor = BorderColor.inactive\n    window_id: int = 0\n    horizontal: bool = False\n\n\nclass LayoutOpts:\n\n    def __init__(self, data: dict[str, str]):\n        pass\n\n    def serialized(self) -> dict[str, Any]:\n        return {}\n\n\nclass LayoutData(NamedTuple):\n    content_pos: int = 0\n    cells_per_window: int = 0\n    space_before: int = 0\n    space_after: int = 0\n    content_size: int = 0\n\n\nDecorationPairs = Sequence[tuple[int, int]]\nLayoutDimension = Generator[LayoutData, None, None]\nListOfWindows = list[WindowType]\n\n\nclass LayoutGlobalData:\n    draw_minimal_borders: bool = True\n    draw_active_borders: bool = True\n    alignment_x: int = 0\n    alignment_y: int = 0\n\n    central: Region = Region((0, 0, 199, 199, 200, 200))\n    cell_width: int = 20\n    cell_height: int = 20\n\n\nlgd = LayoutGlobalData()\n\n\ndef idx_for_id(win_id: int, windows: Iterable[WindowType]) -> int | None:\n    for i, w in enumerate(windows):\n        if w.id == win_id:\n            return i\n    return None\n\n\ndef effective_draw_minimal_borders(opts: Options, has_more_than_one_visible_group: bool = True) -> bool:\n    ans = opts.draw_minimal_borders and sum(opts.window_margin_width) == 0\n    if not has_more_than_one_visible_group and opts.draw_window_borders_for_single_window:\n        ans = False\n    return ans\n\n\ndef set_layout_options(opts: Options) -> None:\n    lgd.draw_minimal_borders = effective_draw_minimal_borders(opts)\n    lgd.draw_active_borders = opts.active_border_color is not None\n    lgd.alignment_x = -1 if opts.placement_strategy.endswith('left') else 1 if opts.placement_strategy.endswith('right') else 0\n    lgd.alignment_y = -1 if opts.placement_strategy.startswith('top') else 1 if opts.placement_strategy.startswith('bottom') else 0\n\n\ndef convert_bias_map(bias: dict[int, float], number_of_windows: int, number_of_cells: int) -> Sequence[float]:\n    cells_per_window, extra = divmod(number_of_cells, number_of_windows)\n    cell_map = list(repeat(cells_per_window, number_of_windows))\n    cell_map[-1] += extra\n    base_bias = [x / number_of_cells for x in cell_map]\n    return distribute_indexed_bias(base_bias, bias)\n\n\ndef calculate_cells_map(\n    bias: None | Sequence[float] | dict[int, float],\n    number_of_windows: int, number_of_cells: int\n) -> list[int]:\n    if isinstance(bias, dict):\n        bias = convert_bias_map(bias, number_of_windows, number_of_cells)\n    cells_per_window = number_of_cells // number_of_windows\n    if bias is not None and number_of_windows > 1 and number_of_windows == len(bias) and cells_per_window > 5:\n        cells_map = [int(b * number_of_cells) for b in bias]\n        while min(cells_map) < 5:\n            maxi, mini = map(cells_map.index, (max(cells_map), min(cells_map)))\n            if maxi == mini:\n                break\n            cells_map[mini] += 1\n            cells_map[maxi] -= 1\n    else:\n        cells_map = list(repeat(cells_per_window, number_of_windows))\n    extra = number_of_cells - sum(cells_map)\n    if extra > 0:\n        cells_map[-1] += extra\n    return cells_map\n\n\ndef layout_dimension(\n    start_at: int, length: int, cell_length: int,\n    decoration_pairs: DecorationPairs,\n    alignment: int = 0,\n    bias: None | Sequence[float] | dict[int, float] = None\n) -> LayoutDimension:\n    number_of_windows = len(decoration_pairs)\n    number_of_cells = length // cell_length\n    dec_vals: Iterable[int] = map(sum, decoration_pairs)\n    space_needed_for_decorations = sum(dec_vals)\n    extra = length - number_of_cells * cell_length\n    while extra < space_needed_for_decorations:\n        number_of_cells -= 1\n        extra = length - number_of_cells * cell_length\n    cells_map = calculate_cells_map(bias, number_of_windows, number_of_cells)\n    assert sum(cells_map) == number_of_cells\n\n    extra = length - number_of_cells * cell_length - space_needed_for_decorations\n    pos = start_at  # start\n    if alignment > 0:  # end\n        pos += extra\n    elif alignment == 0:  # center\n        pos += extra // 2\n    last_i = len(cells_map) - 1\n\n    for i, cells_per_window in enumerate(cells_map):\n        before_dec, after_dec = decoration_pairs[i]\n        pos += before_dec\n        if i == 0:\n            before_space = pos - start_at\n        else:\n            before_space = before_dec\n        content_size = cells_per_window * cell_length\n        if i == last_i:\n            after_space = (start_at + length) - (pos + content_size)\n        else:\n            after_space = after_dec\n        yield LayoutData(pos, cells_per_window, before_space, after_space, content_size)\n        pos += content_size + after_space\n\n\nclass Rect(NamedTuple):\n    left: int\n    top: int\n    right: int\n    bottom: int\n\n\ndef blank_rects_for_window(wg: WindowGeometry) -> Generator[Rect, None, None]:\n    left_width, right_width = wg.spaces.left, wg.spaces.right\n    top_height, bottom_height = wg.spaces.top, wg.spaces.bottom\n    if left_width > 0:\n        yield Rect(wg.left - left_width, wg.top - top_height, wg.left, wg.bottom + bottom_height)\n    if top_height > 0:\n        yield Rect(wg.left, wg.top - top_height, wg.right + right_width, wg.top)\n    if right_width > 0:\n        yield Rect(wg.right, wg.top, wg.right + right_width, wg.bottom + bottom_height)\n    if bottom_height > 0:\n        yield Rect(wg.left, wg.bottom, wg.right, wg.bottom + bottom_height)\n\n\ndef window_geometry(xstart: int, xnum: int, ystart: int, ynum: int, left: int, top: int, right: int, bottom: int) -> WindowGeometry:\n    return WindowGeometry(\n        left=xstart, top=ystart, xnum=max(0, xnum), ynum=max(0, ynum),\n        right=xstart + lgd.cell_width * xnum, bottom=ystart + lgd.cell_height * ynum,\n        spaces=Edges(left, top, right, bottom)\n    )\n\n\ndef window_geometry_from_layouts(x: LayoutData, y: LayoutData) -> WindowGeometry:\n    return window_geometry(x.content_pos, x.cells_per_window, y.content_pos, y.cells_per_window, x.space_before, y.space_before, x.space_after, y.space_after)\n\n\ndef layout_single_window(\n    xdecoration_pairs: DecorationPairs,\n    ydecoration_pairs: DecorationPairs,\n    xalignment: int = 0,\n    yalignment: int = 0,\n) -> WindowGeometry:\n    x = next(layout_dimension(lgd.central.left, lgd.central.width, lgd.cell_width, xdecoration_pairs, alignment=xalignment))\n    y = next(layout_dimension(lgd.central.top, lgd.central.height, lgd.cell_height, ydecoration_pairs, alignment=yalignment))\n    return window_geometry_from_layouts(x, y)\n\n\ndef safe_increment_bias(old_val: float, increment: float = 0) -> float:\n    return max(0.1, min(old_val + increment, 0.9))\n\n\ndef normalize_biases(biases: list[float]) -> list[float]:\n    s = sum(biases)\n    if s == 1.0:\n        return biases\n    return [x/s for x in biases]\n\n\ndef distribute_indexed_bias(base_bias: Sequence[float], index_bias_map: dict[int, float]) -> Sequence[float]:\n    if not index_bias_map:\n        return base_bias\n    ans = list(base_bias)\n    limit = len(ans)\n    for row, increment in index_bias_map.items():\n        if row >= limit or not increment:\n            continue\n        other_increment = -increment / (limit - 1)\n        ans = [safe_increment_bias(b, increment if i == row else other_increment) for i, b in enumerate(ans)]\n    return normalize_biases(ans)\n\n\ndef create_window_id_map_for_unserialize(all_windows: WindowList) -> dict[int, int]:\n    window_id_map = {}\n    for w in all_windows:\n        if w.serialized_id:\n            window_id_map[w.serialized_id] = w.id\n    return window_id_map\n\n\nclass Layout:\n\n    name: str = ''\n    needs_window_borders = True\n    must_draw_borders = False  # can be overridden to customize behavior from kittens\n    layout_opts = LayoutOpts({})\n    only_active_window_visible = False\n\n    def __init__(self, os_window_id: int, tab_id: int, layout_opts: str = '') -> None:\n        self.set_owner(os_window_id, tab_id)\n        # A set of rectangles corresponding to the blank spaces at the edges of\n        # this layout, i.e. spaces that are not covered by any window\n        self.blank_rects: list[Rect] = []\n        self.layout_opts = self.parse_layout_opts(layout_opts)\n        assert self.name is not None\n        self.full_name = f'{self.name}:{layout_opts}' if layout_opts else self.name\n        self.remove_all_biases()\n\n    def set_owner(self, os_window_id: int, tab_id: int) -> None:\n        # Useful when moving a layout from one tab to another typically a detached tab being re-attached\n        self.os_window_id = os_window_id\n        self.tab_id = tab_id\n        self.set_active_window_in_os_window = partial(set_active_window, os_window_id, tab_id)\n\n    def bias_increment_for_cell(self, all_windows: WindowList, is_horizontal: bool) -> float:\n        self._set_dimensions(all_windows)\n        return self.calculate_bias_increment_for_a_single_cell(all_windows, is_horizontal)\n\n    def calculate_bias_increment_for_a_single_cell(self, all_windows: WindowList, is_horizontal: bool) -> float:\n        if is_horizontal:\n            return (lgd.cell_width + 1) / lgd.central.width\n        return (lgd.cell_height + 1) / lgd.central.height\n\n    def apply_bias(self, window_id: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:\n        return False\n\n    def remove_all_biases(self) -> bool:\n        return False\n\n    def modify_size_of_window(self, all_windows: WindowList, window_id: int, increment: float, is_horizontal: bool = True) -> bool:\n        idx = all_windows.group_idx_for_window(window_id)\n        if idx is None or not increment:\n            return False\n        return self.apply_bias(idx, increment, all_windows, is_horizontal)\n    drag_resize_window = modify_size_of_window\n\n    def parse_layout_opts(self, layout_opts: str | None = None) -> LayoutOpts:\n        data: dict[str, str] = {}\n        if layout_opts:\n            for x in layout_opts.split(';'):\n                k, v = x.partition('=')[::2]\n                if k and v:\n                    data[k] = v\n        return type(self.layout_opts)(data)\n\n    def nth_window(self, all_windows: WindowList, num: int) -> WindowType | None:\n        return all_windows.active_window_in_nth_group(num, clamp=True)\n\n    def activate_nth_window(self, all_windows: WindowList, num: int) -> None:\n        all_windows.set_active_group_idx(num)\n\n    def next_window(self, all_windows: WindowList, delta: int = 1) -> None:\n        all_windows.activate_next_window_group(delta)\n\n    def neighbors(self, all_windows: WindowList) -> NeighborsMap:\n        w = all_windows.active_window\n        assert w is not None\n        return self.neighbors_for_window(w, all_windows)\n\n    def move_window(self, all_windows: WindowList, delta: int = 1) -> bool:\n        if all_windows.num_groups < 2 or not delta:\n            return False\n\n        return all_windows.move_window_group(by=delta)\n\n    def move_window_to_group(self, all_windows: WindowList, group: int) -> bool:\n        return all_windows.move_window_group(to_group=group)\n\n    def add_window(\n        self, all_windows: WindowList, window: WindowType, location: str | None = None,\n        overlay_for: int | None = None, put_overlay_behind: bool = False, bias: float | None = None,\n        next_to: WindowType | None = None,\n    ) -> WindowType | None:\n        if overlay_for is not None:\n            underlay = all_windows.id_map.get(overlay_for)\n            if underlay is not None:\n                window.margin, window.padding = underlay.margin.copy(), underlay.padding.copy()\n                all_windows.add_window(window, group_of=overlay_for, head_of_group=put_overlay_behind)\n                return underlay\n        if location == 'neighbor':\n            location = 'after'\n        self.add_non_overlay_window(all_windows, window, location, bias, next_to)\n        return None\n\n    def add_non_overlay_window(\n        self, all_windows: WindowList, window: WindowType, location: str | None, bias: float | None = None, next_to: WindowType | None = None\n    ) -> None:\n        before = False\n        next_to = next_to or all_windows.active_window\n        if location is not None:\n            if location in ('after', 'vsplit', 'hsplit'):\n                pass\n            elif location == 'before':\n                before = True\n            elif location == 'first':\n                before = True\n                next_to = None\n            elif location == 'last':\n                next_to = None\n        all_windows.add_window(window, next_to=next_to, before=before)\n        if bias is not None:\n            idx = all_windows.group_idx_for_window(window)\n            if idx is not None:\n                self._set_dimensions(all_windows)\n                self._bias_slot(all_windows, idx, bias)\n\n    def _bias_slot(self, all_windows: WindowList, idx: int, bias: float) -> bool:\n        fractional_bias = max(10, min(abs(bias), 90)) / 100\n        h, v = self.calculate_bias_increment_for_a_single_cell(all_windows, True), self.calculate_bias_increment_for_a_single_cell(all_windows, False)\n        nh, nv = lgd.central.width / lgd.cell_width, lgd.central.height / lgd.cell_height\n        f = max(-90, min(bias, 90)) / 100.\n        return self.bias_slot(all_windows, idx, fractional_bias, h * nh *f, v * nv * f)\n\n    def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:\n        return False\n\n    def update_visibility(self, all_windows: WindowList) -> None:\n        active_window = all_windows.active_window\n        for window, is_group_leader in all_windows.iter_windows_with_visibility():\n            is_visible = window is active_window or (is_group_leader and not self.only_active_window_visible)\n            window.set_visible_in_layout(is_visible)\n\n    def _set_dimensions(self, all_windows: WindowList) -> None:\n        lgd.central, tab_bar, vw, vh, lgd.cell_width, lgd.cell_height = viewport_for_window(self.os_window_id)\n        # Update lgd.draw_minimal_borders based on the current number of visible windows\n        # and the draw_window_borders_for_single_window option\n        opts = get_options()\n        lgd.draw_minimal_borders = effective_draw_minimal_borders(opts, all_windows.has_more_than_one_visible_group)\n\n    def __call__(self, all_windows: WindowList) -> None:\n        self._set_dimensions(all_windows)\n        self.update_visibility(all_windows)\n        self.blank_rects = []\n        # Set show_title_bar flag on each visible window before layout\n        min_windows = get_options().window_title_bar_min_windows\n        visible_groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))\n        show_title_bar = min_windows > 0 and len(visible_groups) >= min_windows\n        for wg in visible_groups:\n            for w in wg.windows:\n                w.show_title_bar = show_title_bar\n        self.do_layout(all_windows)\n\n    def layout_single_window_group(self, wg: WindowGroup, add_blank_rects: bool = True) -> None:\n        bw = 1 if self.must_draw_borders else 0\n        xdecoration_pairs = ((\n            wg.decoration('left', border_mult=bw, is_single_window=True),\n            wg.decoration('right', border_mult=bw, is_single_window=True),\n        ),)\n        ydecoration_pairs = ((\n            wg.decoration('top', border_mult=bw, is_single_window=True),\n            wg.decoration('bottom', border_mult=bw, is_single_window=True),\n        ),)\n        geom = layout_single_window(xdecoration_pairs, ydecoration_pairs, xalignment=lgd.alignment_x, yalignment=lgd.alignment_y)\n        wg.set_geometry(geom)\n        if add_blank_rects:\n            self.blank_rects.extend(blank_rects_for_window(geom))\n\n    def xlayout(\n        self,\n        groups: Iterator[WindowGroup],\n        bias: None | Sequence[float] | dict[int, float] = None,\n        start: int | None = None,\n        size: int | None = None,\n        offset: int = 0,\n        border_mult: int = 1\n    ) -> LayoutDimension:\n        decoration_pairs = tuple(\n            (g.decoration('left', border_mult=border_mult), g.decoration('right', border_mult=border_mult)) for i, g in\n            enumerate(groups) if i >= offset\n        )\n        if start is None:\n            start = lgd.central.left\n        if size is None:\n            size = lgd.central.width\n        return layout_dimension(start, size, lgd.cell_width, decoration_pairs, bias=bias, alignment=lgd.alignment_x)\n\n    def ylayout(\n        self,\n        groups: Iterator[WindowGroup],\n        bias: None | Sequence[float] | dict[int, float] = None,\n        start: int | None = None,\n        size: int | None = None,\n        offset: int = 0,\n        border_mult: int = 1\n    ) -> LayoutDimension:\n        decoration_pairs = tuple(\n            (g.decoration('top', border_mult=border_mult), g.decoration('bottom', border_mult=border_mult)) for i, g in\n            enumerate(groups) if i >= offset\n        )\n        if start is None:\n            start = lgd.central.top\n        if size is None:\n            size = lgd.central.height\n        return layout_dimension(start, size, lgd.cell_height, decoration_pairs, bias=bias, alignment=lgd.alignment_y)\n\n    def set_window_group_geometry(self, wg: WindowGroup, xl: LayoutData, yl: LayoutData) -> WindowGeometry:\n        geom = window_geometry_from_layouts(xl, yl)\n        wg.set_geometry(geom)\n        self.blank_rects.extend(blank_rects_for_window(geom))\n        return geom\n\n    def do_layout(self, windows: WindowList) -> None:\n        raise NotImplementedError()\n\n    def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:\n        return {}\n\n    def compute_needs_borders_map(self, all_windows: WindowList) -> dict[int, bool]:\n        return all_windows.compute_needs_borders_map(lgd.draw_active_borders)\n\n    def get_minimal_borders(self, windows: WindowList) -> Iterator[BorderLine]:\n        self._set_dimensions(windows)\n        yield from self.minimal_borders(windows)\n\n    def minimal_borders(self, windows: WindowList) -> Iterator[BorderLine]:\n        yield from ()\n\n    def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> bool | None:\n        pass\n\n    def layout_state(self) -> dict[str, Any]:\n        return {}\n\n    def set_layout_state(self, layout_state: dict[str, Any], map_group_id: WindowMapper) -> bool:\n        return True\n\n    def drag_resize_target_windows(\n        self, click_window: WindowType, x: float, y: float, edges: int, all_windows: WindowList,\n    ) -> WindowResizeDragData:\n        return WindowResizeDragData(click_window.id, bool(edges & RIGHT_EDGE), click_window.id, bool(edges & BOTTOM_EDGE))\n\n    def serialize(self, all_windows: WindowList) -> dict[str, Any]:\n        ans = self.layout_state()\n        ans['opts'] = self.layout_opts.serialized()\n        ans['class'] = self.__class__.__name__\n        ans['all_windows'] = all_windows.serialize_layout_state()\n        return ans\n\n    def unserialize(\n        self, s: dict[str, Any], all_windows: WindowList,\n        window_id_mapper: Callable[[WindowList], dict[int, int]] = create_window_id_map_for_unserialize,\n    ) -> bool:\n        if s.get('class') != self.__class__.__name__:\n            return False\n        window_id_map = create_window_id_map_for_unserialize(all_windows)\n        m = all_windows.unserialize_layout_state(s['all_windows'], window_id_map)\n        if m is None:\n            return False\n        return self.set_layout_state(s, m.get)\n"
  },
  {
    "path": "kitty/layout/grid.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Callable, Generator, Iterator, Sequence\nfrom functools import lru_cache\nfrom itertools import repeat\nfrom math import ceil, floor\nfrom typing import Any\n\nfrom kitty.borders import BorderColor\nfrom kitty.types import Edges, NeighborsMap, WindowMapper\nfrom kitty.typing_compat import WindowType\nfrom kitty.window_list import WindowGroup, WindowList\n\nfrom .base import BorderLine, Layout, LayoutData, LayoutDimension, ListOfWindows, layout_dimension, lgd\nfrom .tall import neighbors_for_tall_window\n\n\n@lru_cache\ndef calc_grid_size(n: int) -> tuple[int, int, int, int]:\n    if n <= 5:\n        ncols = 1 if n == 1 else 2\n    else:\n        for ncols in range(3, (n // 2) + 1):\n            if ncols * ncols >= n:\n                break\n    nrows = n // ncols\n    special_rows = n - (nrows * (ncols - 1))\n    special_col = 0 if special_rows < nrows else ncols - 1\n    return ncols, nrows, special_rows, special_col\n\n\nclass Grid(Layout):\n\n    name: str = 'grid'\n    no_minimal_window_borders = True\n\n    def remove_all_biases(self) -> bool:\n        self.biased_rows: dict[int, float] = {}\n        self.biased_cols: dict[int, float] = {}\n        return True\n\n    def column_layout(\n        self,\n        num: int,\n        bias: Sequence[float] | None = None,\n    ) -> LayoutDimension:\n        decoration_pairs = tuple(repeat((0, 0), num))\n        return layout_dimension(lgd.central.left, lgd.central.width, lgd.cell_width, decoration_pairs, bias=bias, alignment=lgd.alignment_x)\n\n    def row_layout(\n        self,\n        num: int,\n        bias: Sequence[float] | None = None,\n    ) -> LayoutDimension:\n        decoration_pairs = tuple(repeat((0, 0), num))\n        return layout_dimension(lgd.central.top, lgd.central.height, lgd.cell_height, decoration_pairs, bias=bias, alignment=lgd.alignment_y)\n\n    def variable_layout(self, layout_func: Callable[..., LayoutDimension], num_windows: int, biased_map: dict[int, float]) -> LayoutDimension:\n        return layout_func(num_windows, bias=biased_map if num_windows > 1 else None)\n\n    def position_for_window_idx(self, idx: int, num_windows: int, ncols:int , nrows: int, special_rows: int, special_col: int) -> tuple[int, int]:\n        row_num = col_num = 0\n\n        def on_col_done(col_windows: list[int]) -> None:\n            nonlocal col_num, row_num\n            row_num = 0\n            col_num += 1\n\n        for window_idx, xl, yl in self.layout_windows(\n                num_windows, nrows, ncols, special_rows, special_col, on_col_done):\n            if idx == window_idx:\n                return row_num, col_num\n            row_num += 1\n        return 0, 0\n\n    def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:\n        num_windows = all_windows.num_groups\n        ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)\n        row_num, col_num = self.position_for_window_idx(idx, num_windows, ncols, nrows, special_rows, special_col)\n        if row_num == 0:\n            b = self.biased_cols\n            layout_func = self.column_layout\n            bias_idx = col_num\n            increment = cell_increment_bias_h\n        else:\n            b = self.biased_rows\n            layout_func = self.row_layout\n            bias_idx = row_num\n            increment = cell_increment_bias_v\n        before_layout = tuple(self.variable_layout(layout_func, num_windows, b))\n        b[bias_idx] = increment\n        return tuple(self.variable_layout(layout_func, num_windows, b)) == before_layout\n\n    def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:\n        num_windows = all_windows.num_groups\n        ncols, nrows, special_rows, special_col = calc_grid_size(num_windows)\n        row_num, col_num = self.position_for_window_idx(idx, num_windows, ncols, nrows, special_rows, special_col)\n\n        if is_horizontal:\n            b = self.biased_cols\n            if ncols < 2:\n                return False\n            bias_idx = col_num\n            attr = 'biased_cols'\n\n            def layout_func(windows: ListOfWindows, bias: Sequence[float] | None = None) -> LayoutDimension:\n                return self.column_layout(num_windows, bias=bias)\n\n        else:\n            b = self.biased_rows\n            if max(nrows, special_rows) < 2:\n                return False\n            bias_idx = row_num\n            attr = 'biased_rows'\n\n            def layout_func(windows: ListOfWindows, bias: Sequence[float] | None = None) -> LayoutDimension:\n                return self.row_layout(num_windows, bias=bias)\n\n        before_layout = tuple(self.variable_layout(layout_func, num_windows, b))\n        candidate = b.copy()\n        before = candidate.get(bias_idx, 0)\n        candidate[bias_idx] = before + increment\n        if before_layout == tuple(self.variable_layout(layout_func, num_windows, candidate)):\n            return False\n        setattr(self, attr, candidate)\n        return True\n\n    def layout_windows(\n        self,\n        num_windows: int,\n        nrows: int, ncols: int,\n        special_rows: int, special_col: int,\n        on_col_done: Callable[[list[int]], None] = lambda col_windows: None\n    ) -> Generator[tuple[int, LayoutData, LayoutData], None, None]:\n        # Distribute windows top-to-bottom, left-to-right (i.e. in columns)\n        xlayout = self.variable_layout(self.column_layout, ncols, self.biased_cols)\n        yvals_normal = tuple(self.variable_layout(self.row_layout, nrows, self.biased_rows))\n        yvals_special = yvals_normal if special_rows == nrows else tuple(self.variable_layout(self.row_layout, special_rows, self.biased_rows))\n        pos = 0\n        for col in range(ncols):\n            rows = special_rows if col == special_col else nrows\n            yls = yvals_special if col == special_col else yvals_normal\n            xl = next(xlayout)\n            col_windows = []\n            for i, yl in enumerate(yls):\n                window_idx = pos + i\n                yield window_idx, xl, yl\n                col_windows.append(window_idx)\n            pos += rows\n            on_col_done(col_windows)\n\n    def do_layout(self, all_windows: WindowList) -> None:\n        n = all_windows.num_groups\n        if n == 1:\n            self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))\n            return\n        ncols, nrows, special_rows, special_col = calc_grid_size(n)\n        groups = tuple(all_windows.iter_all_layoutable_groups())\n        win_col_map: list[list[WindowGroup]] = []\n\n        def on_col_done(col_windows: list[int]) -> None:\n            col_windows_w = [groups[i] for i in col_windows]\n            win_col_map.append(col_windows_w)\n\n        def extents(ld: LayoutData) -> tuple[int, int]:\n            start = ld.content_pos - ld.space_before\n            size = ld.space_before + ld.space_after + ld.content_size\n            return start, size\n\n        def layout(ld: LayoutData, cell_length: int, before_dec: int, after_dec: int) -> LayoutData:\n            start, size = extents(ld)\n            space_needed_for_decorations = before_dec + after_dec\n            content_size = size - space_needed_for_decorations\n            number_of_cells = content_size // cell_length\n            cell_area = number_of_cells * cell_length\n            extra = content_size - cell_area\n            if lgd.alignment_x == 0:  # center\n                before_dec += extra // 2\n            elif lgd.alignment_x > 0:  # end\n                before_dec += extra\n            return LayoutData(start + before_dec, number_of_cells, before_dec, size - cell_area - before_dec, cell_area)\n\n        def position_window_in_grid_cell(window_idx: int, xl: LayoutData, yl: LayoutData) -> None:\n            wg = groups[window_idx]\n            edges = Edges(wg.decoration('left'), wg.decoration('top'), wg.decoration('right'), wg.decoration('bottom'))\n            xl = layout(xl, lgd.cell_width, edges.left, edges.right)\n            yl = layout(yl, lgd.cell_height, edges.top, edges.bottom)\n            self.set_window_group_geometry(wg, xl, yl)\n\n        for window_idx, xl, yl in self.layout_windows(\n                n, nrows, ncols, special_rows, special_col, on_col_done):\n            position_window_in_grid_cell(window_idx, xl, yl)\n\n    def minimal_borders(self, all_windows: WindowList) -> Iterator[BorderLine]:\n        n = all_windows.num_groups\n        if not lgd.draw_minimal_borders or n < 2:\n            return\n        needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)\n        ncols, nrows, special_rows, special_col = calc_grid_size(n)\n        is_first_row: set[int] = set()\n        is_last_row: set[int] = set()\n        is_first_column: set[int] = set()\n        is_last_column: set[int] = set()\n        groups = tuple(all_windows.iter_all_layoutable_groups())\n        bw = groups[0].effective_border()\n        if not bw:\n            return\n        xl: LayoutData = LayoutData()\n        yl: LayoutData = LayoutData()\n        prev_col_windows: list[int] = []\n        layout_data_map: dict[int, tuple[LayoutData, LayoutData]] = {}\n\n        def on_col_done(col_windows: list[int]) -> None:\n            nonlocal prev_col_windows, is_first_column\n            if col_windows:\n                is_first_row.add(groups[col_windows[0]].id)\n                is_last_row.add(groups[col_windows[-1]].id)\n            if not prev_col_windows:\n                is_first_column = {groups[x].id for x in col_windows}\n            prev_col_windows = col_windows\n\n        all_groups_in_order: list[WindowGroup] = []\n        for window_idx, xl, yl in self.layout_windows(n, nrows, ncols, special_rows, special_col, on_col_done):\n            wg = groups[window_idx]\n            all_groups_in_order.append(wg)\n            layout_data_map[wg.id] = xl, yl\n        is_last_column = {groups[x].id for x in prev_col_windows}\n        active_group = all_windows.active_group\n\n        def ends(yl: LayoutData) -> tuple[int, int]:\n            return yl.content_pos - yl.space_before, yl.content_pos + yl.content_size + yl.space_after\n\n        def borders_for_window(gid: int, color: BorderColor, wid: int) -> Iterator[BorderLine]:\n            xl, yl = layout_data_map[gid]\n            left, right = ends(xl)\n            top, bottom = ends(yl)\n            first_row, last_row = gid in is_first_row, gid in is_last_row\n            first_column, last_column = gid in is_first_column, gid in is_last_column\n\n            # Horizontal\n            if not first_row:\n                yield BorderLine(Edges(left, top, right, top + bw), color, -wid, True)\n            if not last_row:\n                yield BorderLine(Edges(left, bottom - bw, right, bottom), color, wid, True)\n\n            # Vertical\n            if not first_column:\n                yield BorderLine(Edges(left, top, left + bw, bottom), color, -wid, False)\n            if not last_column:\n                yield BorderLine(Edges(right - bw, top, right, bottom), color, wid, False)\n\n        for wg in all_groups_in_order:\n            color = BorderColor.inactive\n            if needs_borders_map.get(wg.id):\n                color = BorderColor.active if wg is active_group else BorderColor.bell\n            wid = wg.active_window_id\n            yield from borders_for_window(wg.id, color, wid)\n\n    def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:\n        n = all_windows.num_groups\n        if n < 4:\n            return neighbors_for_tall_window(1, window, all_windows)\n\n        wg = all_windows.group_for_window(window)\n        assert wg is not None\n        ncols, nrows, special_rows, special_col = calc_grid_size(n)\n        blank_row: list[int | None] = [None for i in range(ncols)]\n        matrix = tuple(blank_row[:] for j in range(max(nrows, special_rows)))\n        wi = all_windows.iter_all_layoutable_groups()\n        pos_map: dict[int, tuple[int, int]] = {}\n        col_counts: list[int] = []\n        for col in range(ncols):\n            rows = special_rows if col == special_col else nrows\n            for row in range(rows):\n                w = next(wi)\n                matrix[row][col] = wid = w.id\n                pos_map[wid] = row, col\n            col_counts.append(rows)\n        row, col = pos_map[wg.id]\n\n        def neighbors(row: int, col: int) -> list[int]:\n            try:\n                ans = matrix[row][col]\n            except IndexError:\n                ans = None\n            return [] if ans is None else [ans]\n\n        def side(row: int, col: int, delta: int) -> list[int]:\n            neighbor_col = col + delta\n            neighbor_nrows = col_counts[neighbor_col]\n            nrows = col_counts[col]\n            if neighbor_nrows == nrows:\n                return neighbors(row, neighbor_col)\n\n            start_row = floor(neighbor_nrows * row / nrows)\n            end_row = ceil(neighbor_nrows * (row + 1) / nrows)\n            xs = []\n            for neighbor_row in range(start_row, end_row):\n                xs.extend(neighbors(neighbor_row, neighbor_col))\n            return xs\n\n        ans: NeighborsMap = {}\n        if row:\n            ans['top'] = neighbors(row-1, col)\n        if bottom := neighbors(row + 1, col):\n            ans['bottom'] = bottom\n        if col and (left := side(row, col, -1)):\n            ans['left'] = left\n        if col < ncols - 1:\n            ans['right'] = side(row, col, 1)\n        return ans\n\n    def layout_state(self) -> dict[str, Any]:\n        return {\n            'biased_cols': self.biased_cols,\n            'biased_rows': self.biased_rows\n        }\n\n    def set_layout_state(self, layout_state: dict[str, Any], map_group_id: WindowMapper) -> bool:\n        self.biased_rows = {int(k): v for k, v in layout_state['biased_rows'].items()}\n        self.biased_cols = {int(k): v for k, v in layout_state['biased_cols'].items()}\n        return True\n"
  },
  {
    "path": "kitty/layout/interface.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom .base import Layout\nfrom .grid import Grid\nfrom .splits import Splits\nfrom .stack import Stack\nfrom .tall import Fat, Tall\nfrom .vertical import Horizontal, Vertical\n\nall_layouts: dict[str, type[Layout]] = {\n    Stack.name: Stack,\n    Tall.name: Tall,\n    Fat.name: Fat,\n    Vertical.name: Vertical,\n    Horizontal.name: Horizontal,\n    Grid.name: Grid,\n    Splits.name: Splits,\n}\n\nKeyType = tuple[str, int, int, str]\n\n\nclass CreateLayoutObjectFor:\n    cache: dict[KeyType, Layout] = {}\n\n    def __call__(\n        self,\n        name: str,\n        os_window_id: int,\n        tab_id: int,\n        layout_opts: str = ''\n    ) -> Layout:\n        key = name, os_window_id, tab_id, layout_opts\n        ans = create_layout_object_for.cache.get(key)\n        if ans is None:\n            name, layout_opts = name.partition(':')[::2]\n            ans = create_layout_object_for.cache[key] = all_layouts[name](\n                os_window_id, tab_id, layout_opts)\n        return ans\n\n\ncreate_layout_object_for = CreateLayoutObjectFor()\n\n\ndef evict_cached_layouts(tab_id: int) -> None:\n    remove = [key for key in create_layout_object_for.cache if key[2] == tab_id]\n    for key in remove:\n        del create_layout_object_for.cache[key]\n"
  },
  {
    "path": "kitty/layout/splits.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Collection, Generator, Iterator, Sequence\nfrom typing import Any, Optional, TypedDict, Union\n\nfrom kitty.borders import BorderColor\nfrom kitty.fast_data_types import BOTTOM_EDGE, LEFT_EDGE, RIGHT_EDGE, TOP_EDGE\nfrom kitty.types import Edges, NeighborsMap, WindowGeometry, WindowMapper, WindowResizeDragData\nfrom kitty.typing_compat import EdgeLiteral, WindowType\nfrom kitty.window_list import WindowGroup, WindowList\n\nfrom .base import BorderLine, Layout, LayoutOpts, blank_rects_for_window, lgd, window_geometry_from_layouts\n\n\nclass SerializedPair(TypedDict, total=False):\n    horizontal: bool  # default to True if absent\n    bias: float  # default to 0.5 if absent\n    one: Union[int, 'SerializedPair']  # default to None if absent\n    two: Union[int, 'SerializedPair']  # default to None if absent\n\n\nclass Pair:\n\n    def __init__(self, horizontal: bool = True):\n        self.horizontal = horizontal\n        self.one: Pair | int | None = None\n        self.two: Pair | int | None = None\n        self.bias = 0.5\n        self.top = self.left = self.width = self.height = 0\n        self.between_borders: tuple[Sequence[BorderLine], Sequence[BorderLine]] | None = None\n        self.first_extent = self.second_extent = Edges()  # not including between_borders\n        self.border_width: int = 0\n\n    def serialize(self) -> SerializedPair:\n        ans: SerializedPair = {}\n        if not self.horizontal:\n            ans['horizontal'] = False\n        if self.bias != 0.5:\n            ans['bias'] = self.bias\n        if self.one is not None:\n            ans['one'] = self.one.serialize() if isinstance(self.one, Pair) else self.one\n        if self.two is not None:\n            ans['two'] = self.two.serialize() if isinstance(self.two, Pair) else self.two\n        return ans\n\n    def unserialize(self, s: SerializedPair, map_window_id: WindowMapper) -> None:\n        self.bias = s.get('bias', 0.5)\n        self.horizontal = s.get('horizontal', True)\n\n        def unserialize(x: int | SerializedPair | None) -> int | Pair | None:\n            if x is None:\n                return None\n            if isinstance(x, int):\n                return map_window_id(x)\n            ans = Pair()\n            ans.unserialize(x, map_window_id)\n            return ans if ans.one or ans.two else None\n        self.one = unserialize(s.get('one'))\n        self.two = unserialize(s.get('two'))\n\n    def __repr__(self) -> str:\n        return 'Pair(horizontal={}, bias={:.2f}, one={}, two={}, between_borders={})'.format(\n                self.horizontal, self.bias, self.one, self.two, self.between_borders)\n\n    def all_window_ids(self) -> Generator[int, None, None]:\n        if self.one is not None:\n            if isinstance(self.one, Pair):\n                yield from self.one.all_window_ids()\n            else:\n                yield self.one\n        if self.two is not None:\n            if isinstance(self.two, Pair):\n                yield from self.two.all_window_ids()\n            else:\n                yield self.two\n\n    def self_and_descendants(self) -> Generator['Pair', None, None]:\n        yield self\n        if isinstance(self.one, Pair):\n            yield from self.one.self_and_descendants()\n        if isinstance(self.two, Pair):\n            yield from self.two.self_and_descendants()\n\n    def pair_for_window(self, window_id: int) -> Optional['Pair']:\n        if self.one == window_id or self.two == window_id:\n            return self\n        ans = None\n        if isinstance(self.one, Pair):\n            ans = self.one.pair_for_window(window_id)\n        if ans is None and isinstance(self.two, Pair):\n            ans = self.two.pair_for_window(window_id)\n        return ans\n\n    def swap_windows(self, a: int, b: int) -> None:\n        pa = self.pair_for_window(a)\n        pb = self.pair_for_window(b)\n        if pa is None or pb is None:\n            return\n        if pa.one == a:\n            if pb.one == b:\n                pa.one, pb.one = pb.one, pa.one\n            else:\n                pa.one, pb.two = pb.two, pa.one\n        else:\n            if pb.one == b:\n                pa.two, pb.one = pb.one, pa.two\n            else:\n                pa.two, pb.two = pb.two, pa.two\n\n    def parent(self, root: 'Pair') -> Optional['Pair']:\n        for q in root.self_and_descendants():\n            if q.one is self or q.two is self:\n                return q\n        return None\n\n    def remove_windows(self, window_ids: Collection[int]) -> None:\n        if isinstance(self.one, int) and self.one in window_ids:\n            self.one = None\n        if isinstance(self.two, int) and self.two in window_ids:\n            self.two = None\n        if self.one is None and self.two is not None:\n            self.one, self.two = self.two, None\n\n    @property\n    def is_redundant(self) -> bool:\n        return self.one is None or self.two is None\n\n    def collapse_redundant_pairs(self) -> None:\n        while isinstance(self.one, Pair) and self.one.is_redundant:\n            self.one = self.one.one or self.one.two\n        while isinstance(self.two, Pair) and self.two.is_redundant:\n            self.two = self.two.one or self.two.two\n        if isinstance(self.one, Pair):\n            self.one.collapse_redundant_pairs()\n        if isinstance(self.two, Pair):\n            self.two.collapse_redundant_pairs()\n\n    def balanced_add(self, window_id: int) -> 'Pair':\n        if self.one is None or self.two is None:\n            if self.one is None:\n                if self.two is None:\n                    self.one = window_id\n                    return self\n                self.one, self.two = self.two, self.one\n            self.two = window_id\n            return self\n        if isinstance(self.one, Pair) and isinstance(self.two, Pair):\n            one_count = sum(1 for _ in self.one.all_window_ids())\n            two_count = sum(1 for _ in self.two.all_window_ids())\n            q = self.one if one_count < two_count else self.two\n            return q.balanced_add(window_id)\n        if not isinstance(self.one, Pair) and not isinstance(self.two, Pair):\n            pair = Pair(horizontal=self.horizontal)\n            pair.balanced_add(self.one)\n            pair.balanced_add(self.two)\n            self.one, self.two = pair, window_id\n            return self\n        if isinstance(self.one, Pair):\n            window_to_be_split = self.two\n            self.two = pair = Pair(horizontal=self.horizontal)\n        else:\n            window_to_be_split = self.one\n            self.one = pair = Pair(horizontal=self.horizontal)\n        assert isinstance(window_to_be_split, int)\n        pair.balanced_add(window_to_be_split)\n        pair.balanced_add(window_id)\n        return pair\n\n    def split_and_add(self, existing_window_id: int, new_window_id: int, horizontal: bool, after: bool) -> 'Pair':\n        q = (existing_window_id, new_window_id) if after else (new_window_id, existing_window_id)\n        if self.is_redundant:\n            pair = self\n            pair.horizontal = horizontal\n            self.one, self.two = q\n            final_pair = pair\n\n        else:\n            pair = Pair(horizontal=horizontal)\n            if self.one == existing_window_id:\n                self.one = pair\n            else:\n                self.two = pair\n            for wid in q:\n                qp = pair.balanced_add(wid)\n                if wid == new_window_id:\n                    final_pair = qp\n        return final_pair\n\n    def apply_window_geometry(\n        self, window_id: int,\n        window_geometry: WindowGeometry,\n        id_window_map: dict[int, WindowGroup],\n        layout_object: Layout\n    ) -> None:\n        wg = id_window_map[window_id]\n        wg.set_geometry(window_geometry)\n        layout_object.blank_rects.extend(blank_rects_for_window(window_geometry))\n\n    def effective_border(self, id_window_map: dict[int, WindowGroup]) -> int:\n        for wid in self.all_window_ids():\n            return id_window_map[wid].effective_border()\n        return 0\n\n    def minimum_width(self, id_window_map: dict[int, WindowGroup]) -> int:\n        if self.one is None or self.two is None or not self.horizontal:\n            return lgd.cell_width\n        bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0\n        ans = 2 * bw\n        if isinstance(self.one, Pair):\n            ans += self.one.minimum_width(id_window_map)\n        else:\n            ans += lgd.cell_width\n        if isinstance(self.two, Pair):\n            ans += self.two.minimum_width(id_window_map)\n        else:\n            ans += lgd.cell_width\n        return ans\n\n    def minimum_height(self, id_window_map: dict[int, WindowGroup]) -> int:\n        if self.one is None or self.two is None or self.horizontal:\n            return lgd.cell_height\n        bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0\n        ans = 2 * bw\n        if isinstance(self.one, Pair):\n            ans += self.one.minimum_height(id_window_map)\n        else:\n            ans += lgd.cell_height\n        if isinstance(self.two, Pair):\n            ans += self.two.minimum_height(id_window_map)\n        else:\n            ans += lgd.cell_height\n        return ans\n\n    def layout_pair(\n        self,\n        left: int, top: int, width: int, height: int,\n        id_window_map: dict[int, WindowGroup],\n        layout_object: Layout\n    ) -> None:\n        self.between_borders = None\n        self.left, self.top, self.width, self.height = left, top, width, height\n        self.first_extent = self.second_extent = Edges(left, top, left + width, top + height)\n        self.border_width = bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0\n        border_mult = 0 if lgd.draw_minimal_borders else 1\n        bw2 = bw * 2\n        if self.one is None or self.two is None:\n            q = self.one or self.two\n            if isinstance(q, Pair):\n                return q.layout_pair(left, top, width, height, id_window_map, layout_object)\n            if q is None:\n                return\n            wg = id_window_map[q]\n            xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width, border_mult=border_mult))\n            yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height, border_mult=border_mult))\n            geom = window_geometry_from_layouts(xl, yl)\n            self.apply_window_geometry(q, geom, id_window_map, layout_object)\n            return\n        one: list[BorderLine] = []\n        two: list[BorderLine] = []\n        self.between_borders = one, two\n        if self.horizontal:\n            min_w1 = self.one.minimum_width(id_window_map) if isinstance(self.one, Pair) else lgd.cell_width\n            min_w2 = self.two.minimum_width(id_window_map) if isinstance(self.two, Pair) else lgd.cell_width\n            w1 = max(min_w1, int(self.bias * width) - bw)\n            w2 = width - w1 - bw2\n            if w2 < min_w2 and w1 >= min_w1 + bw2:\n                w2 = min_w2\n                w1 = width - w2\n            bleft = left + w1\n            self.first_extent = Edges(left, top, left + w1, top + height)\n            if isinstance(self.one, Pair):\n                self.one.layout_pair(left, top, w1, height, id_window_map, layout_object)\n                if bw:\n                    for etop, ebottom, window_id in self.one.edge_border(RIGHT_EDGE, id_window_map):\n                        one.append(BorderLine(Edges(bleft, etop, bleft + bw, ebottom), window_id=window_id, horizontal=False))\n            else:\n                wg = id_window_map[self.one]\n                yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height, border_mult=border_mult))\n                xl = next(layout_object.xlayout(iter((wg,)), start=left, size=w1, border_mult=border_mult))\n                geom = window_geometry_from_layouts(xl, yl)\n                self.apply_window_geometry(self.one, geom, id_window_map, layout_object)\n                if bw:\n                    one.append(BorderLine(Edges(bleft, top, bleft + bw, top + height), window_id=wg.active_window_id, horizontal=False))\n            left += w1 + bw2\n            self.second_extent = Edges(left, top, left + w2, top + height)\n            if isinstance(self.two, Pair):\n                self.two.layout_pair(left, top, w2, height, id_window_map, layout_object)\n                if bw:\n                    for etop, ebottom, window_id in self.two.edge_border(LEFT_EDGE, id_window_map):\n                        two.append(BorderLine(Edges(left - bw, etop, left, ebottom), window_id=window_id, horizontal=False))\n            else:\n                wg = id_window_map[self.two]\n                if bw:\n                    two.append(BorderLine(Edges(left - bw, top, left, top + height), window_id=-wg.active_window_id, horizontal=False))\n                xl = next(layout_object.xlayout(iter((wg,)), start=left, size=w2, border_mult=border_mult))\n                yl = next(layout_object.ylayout(iter((wg,)), start=top, size=height, border_mult=border_mult))\n                geom = window_geometry_from_layouts(xl, yl)\n                self.apply_window_geometry(self.two, geom, id_window_map, layout_object)\n        else:\n            min_h1 = self.one.minimum_height(id_window_map) if isinstance(self.one, Pair) else lgd.cell_height\n            min_h2 = self.two.minimum_height(id_window_map) if isinstance(self.two, Pair) else lgd.cell_height\n            h1 = max(min_h1, int(self.bias * height) - bw)\n            h2 = height - h1 - bw2\n            if h2 < min_h2 and h1 >= min_h1 + bw2:\n                h2 = min_h2\n                h1 = height - h2\n            btop = top + h1\n            self.first_extent = Edges(left, top, left + width, top + h1)\n            if isinstance(self.one, Pair):\n                self.one.layout_pair(left, top, width, h1, id_window_map, layout_object)\n                if bw:\n                    for eleft, eright, window_id in self.one.edge_border(BOTTOM_EDGE, id_window_map):\n                        one.append(BorderLine(Edges(eleft, btop, eright, btop + bw), window_id=window_id, horizontal=True))\n            else:\n                wg = id_window_map[self.one]\n                xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width, border_mult=border_mult))\n                yl = next(layout_object.ylayout(iter((wg,)), start=top, size=h1, border_mult=border_mult))\n                geom = window_geometry_from_layouts(xl, yl)\n                self.apply_window_geometry(self.one, geom, id_window_map, layout_object)\n                if bw:\n                    one.append(BorderLine(Edges(left, btop, left + width, btop + bw), window_id=wg.active_window_id, horizontal=True))\n            top += bw2 + h1\n            self.second_extent = Edges(left, top, left + width, top + h2)\n            if isinstance(self.two, Pair):\n                self.two.layout_pair(left, top, width, h2, id_window_map, layout_object)\n                if bw:\n                    for eleft, eright, window_id in self.two.edge_border(TOP_EDGE, id_window_map):\n                        two.append(BorderLine(Edges(eleft, top - bw, eright, top), window_id=window_id, horizontal=True))\n            else:\n                wg = id_window_map[self.two]\n                if bw:\n                    two.append(BorderLine(Edges(left, top - bw, left + width, top), window_id=-wg.active_window_id, horizontal=True))\n                xl = next(layout_object.xlayout(iter((wg,)), start=left, size=width, border_mult=border_mult))\n                yl = next(layout_object.ylayout(iter((wg,)), start=top, size=h2, border_mult=border_mult))\n                geom = window_geometry_from_layouts(xl, yl)\n                self.apply_window_geometry(self.two, geom, id_window_map, layout_object)\n\n    def edge_border(self, which: int, id_group_map: dict[int, WindowGroup]) -> Iterator[tuple[int, int, int]]:\n        mult = 1 if which & (RIGHT_EDGE | BOTTOM_EDGE) else -1\n\n        def edge(x: int, p: Pair) -> tuple[int, int, int]:\n            wid = id_group_map[x].active_window_id * mult\n            if which & (LEFT_EDGE | RIGHT_EDGE):\n                return p.top, p.top + p.height, wid\n            return p.left, p.left + p.width, wid\n\n        def edges(x: int | Pair, parent: Pair) -> Iterator[tuple[int, int, int]]:\n            if isinstance(x, int):\n                yield edge(x, parent)\n            else:\n                yield from x.edge_border(which, id_group_map)\n\n        if self.two is None or self.one is None:\n            x = self.one or self.two\n            if x is not None:\n                yield from edges(x, self)\n            return\n\n        def as_pair(e: Edges, gid: int) -> Pair:\n            g1 = Pair()\n            g1.one = g1.two = gid\n            g1.left, g1.top, g1.width, g1.height = e.left, e.top, e.right - e.left, e.bottom - e.top\n            return g1\n\n        needs_vertical_edges = which in (LEFT_EDGE, RIGHT_EDGE)\n        if self.horizontal == needs_vertical_edges:\n            yield from edges(self.one if which in (LEFT_EDGE, TOP_EDGE) else self.two, self)\n        else:\n            g1 = as_pair(self.first_extent, self.one) if isinstance(self.one, int) else self.one\n            g2 = as_pair(self.second_extent, self.two) if isinstance(self.two, int) else self.two\n            yield from edges(self.one, g1)\n            first_id = second_id = 0\n            if isinstance(self.one, int):\n                first_id = self.one\n            if isinstance(self.two, int):\n                second_id = self.two\n            if self.horizontal:\n                start = g1.left + g1.width\n                if isinstance(self.one, Pair):\n                    first_id = self.one.corner_group_id(which | RIGHT_EDGE)\n                if isinstance(self.two, Pair):\n                    second_id = self.two.corner_group_id(which | LEFT_EDGE)\n            else:\n                start = g1.top + g1.height\n                if isinstance(self.one, Pair):\n                    first_id = self.one.corner_group_id(which | BOTTOM_EDGE)\n                if isinstance(self.two, Pair):\n                    second_id = self.two.corner_group_id(which | TOP_EDGE)\n            if g := id_group_map.get(first_id):\n                first_id = g.active_window_id\n            if g := id_group_map.get(second_id):\n                second_id = g.active_window_id\n            yield start, start + self.border_width, first_id * mult\n            yield start + self.border_width, start + 2*self.border_width, second_id * mult\n            yield from edges(self.two, g2)\n\n    def corner_group_id(self, which: int) -> int:\n        if self.is_redundant:\n            q = self.one or self.two\n        elif self.horizontal:\n            q = self.one if which & LEFT_EDGE else self.two\n        else:\n            q = self.one if which & TOP_EDGE else self.two\n        if q is None:\n            return 0\n        return q if isinstance(q, int) else q.corner_group_id(which)\n\n    def set_bias(self, window_id: int, bias: int) -> None:\n        b = max(0, min(bias, 100)) / 100\n        self.bias = b if window_id == self.one else (1. - b)\n\n    def modify_size_of_child(self, which: int, increment: float, is_horizontal: bool, layout_object: 'Splits') -> bool:\n        if is_horizontal == self.horizontal and not self.is_redundant:\n            if which == 2:\n                increment *= -1\n            new_bias = max(0, min(self.bias + increment, 1))\n            if new_bias != self.bias:\n                self.bias = new_bias\n                return True\n            return False\n        parent = self.parent(layout_object.pairs_root)\n        if parent is not None:\n            which = 1 if parent.one is self else 2\n            return parent.modify_size_of_child(which, increment, is_horizontal, layout_object)\n        return False\n\n    def neighbors_for_window(self, window_id: int, ans: NeighborsMap, layout_object: 'Splits', all_windows: WindowList) -> None:\n\n        def quadrant(is_horizontal: bool, is_first: bool) -> tuple[EdgeLiteral, EdgeLiteral]:\n            if is_horizontal:\n                if is_first:\n                    return 'left', 'right'\n                return 'right', 'left'\n            if is_first:\n                return 'top', 'bottom'\n            return 'bottom', 'top'\n\n        geometries = {group.id: group.geometry for group in all_windows.groups if group.geometry}\n\n        def extend(other: Union[int, 'Pair', None], edge: EdgeLiteral, which: EdgeLiteral) -> None:\n            if not ans.get(which) and other:\n                if isinstance(other, Pair):\n                    neighbors = (\n                        w for w in other.edge_windows(edge)\n                        if is_neighbouring_geometry(geometries[w], geometries[window_id], which))\n                    ans.setdefault(which, []).extend(neighbors)\n                else:\n                    ans.setdefault(which, []).append(other)\n\n        def is_neighbouring_geometry(a: WindowGeometry, b: WindowGeometry, direction: str) -> bool:\n            def edges(g: WindowGeometry) -> tuple[int, int]:\n                return (g.top, g.bottom) if direction in ['left', 'right'] else (g.left, g.right)\n\n            a1, a2 = edges(a)\n            b1, b2 = edges(b)\n\n            return a1 < b2 and a2 > b1\n\n        other = self.two if self.one == window_id else self.one\n        extend(other, *quadrant(self.horizontal, self.one == window_id))\n\n        child = self\n        while True:\n            parent = child.parent(layout_object.pairs_root)\n            if parent is None:\n                break\n            other = parent.two if child is parent.one else parent.one\n            extend(other, *quadrant(parent.horizontal, child is parent.one))\n            child = parent\n\n    def edge_windows(self, edge: str) -> Generator[int, None, None]:\n        if self.is_redundant:\n            q = self.one or self.two\n            if q:\n                if isinstance(q, Pair):\n                    yield from q.edge_windows(edge)\n                else:\n                    yield q\n        edges = ('left', 'right') if self.horizontal else ('top', 'bottom')\n        if edge in edges:\n            q = self.one if edge in ('left', 'top') else self.two\n            if q:\n                if isinstance(q, Pair):\n                    yield from q.edge_windows(edge)\n                else:\n                    yield q\n        else:\n            for q in (self.one, self.two):\n                if q:\n                    if isinstance(q, Pair):\n                        yield from q.edge_windows(edge)\n                    else:\n                        yield q\n\n    def is_group_on_second(self, gid: int) -> bool:\n        if self.one == gid:\n            return False\n        if self.two == gid:\n            return True\n        if not isinstance(self.two, Pair):\n            return False\n        return self.two.is_group_on_second(gid)\n\n    def find_window_in_tree(self, window_id: int) -> 'list[tuple[Pair, bool]] | None':\n        # Returns list of (pair, is_in_one) from self down to the pair containing window_id.\n        if self.one == window_id:\n            return [(self, True)]\n        if self.two == window_id:\n            return [(self, False)]\n        if isinstance(self.one, Pair):\n            path = self.one.find_window_in_tree(window_id)\n            if path is not None:\n                return [(self, True)] + path\n        if isinstance(self.two, Pair):\n            path = self.two.find_window_in_tree(window_id)\n            if path is not None:\n                return [(self, False)] + path\n        return None\n\n    def path_from_root(self, target: 'Pair') -> 'list[str] | None':\n        if self is target:\n            return []\n        if isinstance(self.one, Pair):\n            sub = self.one.path_from_root(target)\n            if sub is not None:\n                return ['one'] + sub\n        if isinstance(self.two, Pair):\n            sub = self.two.path_from_root(target)\n            if sub is not None:\n                return ['two'] + sub\n        return None\n\n    def pair_at_path(self, path: 'list[str]') -> 'Pair | None':\n        current: Pair = self\n        for step in path:\n            child = current.one if step == 'one' else current.two\n            if not isinstance(child, Pair):\n                return None\n            current = child\n        return current\n\n\nclass SplitsLayoutOpts(LayoutOpts):\n\n    default_axis_is_horizontal: bool | None = True\n\n    def __init__(self, data: dict[str, str]):\n        q = data.get('split_axis', 'horizontal')\n        if q == 'auto':\n            self.default_axis_is_horizontal = None\n        else:\n            self.default_axis_is_horizontal = q == 'horizontal'\n\n    def serialized(self) -> dict[str, Any]:\n        return {'default_axis_is_horizontal': self.default_axis_is_horizontal}\n\n\nclass Splits(Layout):\n    name = 'splits'\n    needs_all_windows = True\n    layout_opts = SplitsLayoutOpts({})\n    no_minimal_window_borders = True\n\n    @property\n    def default_axis_is_horizontal(self) -> bool | None:\n        return self.layout_opts.default_axis_is_horizontal\n\n    @property\n    def pairs_root(self) -> Pair:\n        root: Pair | None = getattr(self, '_pairs_root', None)\n        if root is None:\n            horizontal = self.default_axis_is_horizontal\n            if horizontal is None:\n                horizontal = True\n            self._pairs_root = root = Pair(horizontal=horizontal)\n        return root\n\n    @pairs_root.setter\n    def pairs_root(self, root: Pair) -> None:\n        self._pairs_root = root\n\n    def remove_windows(self, *windows_to_remove: int) -> None:\n        root = self.pairs_root\n        for pair in root.self_and_descendants():\n            pair.remove_windows(windows_to_remove)\n        root.collapse_redundant_pairs()\n        if root.one is None or root.two is None:\n            q = root.one or root.two\n            if isinstance(q, Pair):\n                self.pairs_root = q\n\n    def do_layout(self, all_windows: WindowList) -> None:\n        groups = tuple(all_windows.iter_all_layoutable_groups())\n        root = self.pairs_root\n        all_present_group_ids = {g.id for g in groups}\n        already_placed_group_ids = frozenset(root.all_window_ids())\n        if groups_to_remove := already_placed_group_ids - all_present_group_ids:\n            self.remove_windows(*groups_to_remove)\n        if groups_to_add := all_present_group_ids - already_placed_group_ids:\n            id_idx_map = {g.id: i for i, g in enumerate(groups)}\n            for gid in sorted(groups_to_add, key=id_idx_map.__getitem__):\n                root.balanced_add(gid)\n\n        if len(groups) == 1:\n            self.layout_single_window_group(groups[0])\n        else:\n            id_group_map = {g.id: g for g in groups}\n            root.layout_pair(lgd.central.left, lgd.central.top, lgd.central.width, lgd.central.height, id_group_map, self)\n\n    def add_non_overlay_window(\n        self,\n        all_windows: WindowList,\n        window: WindowType,\n        location: str | None,\n        bias: float | None = None,\n        next_to: WindowType | None = None,\n    ) -> None:\n        horizontal = self.default_axis_is_horizontal\n        after = True\n        if location == 'vsplit':\n            horizontal = True\n        elif location == 'hsplit':\n            horizontal = False\n        elif location in ('before', 'first'):\n            after = False\n        aw = next_to or all_windows.active_window\n        if bias:\n            bias = max(0, min(abs(bias), 100)) / 100\n        if aw is not None and (ag := all_windows.group_for_window(aw)) is not None:\n            group_id = ag.id\n            pair = self.pairs_root.pair_for_window(group_id)\n            if pair is not None:\n                if location == 'split' or horizontal is None:\n                    wwidth = aw.geometry.right - aw.geometry.left\n                    wheight = aw.geometry.bottom - aw.geometry.top\n                    horizontal = wwidth >= wheight\n                target_group = all_windows.add_window(window, next_to=aw, before=not after)\n                parent_pair = pair.split_and_add(group_id, target_group.id, horizontal, after)\n                if bias is not None:\n                    parent_pair.bias = bias if parent_pair.one == target_group.id else (1 - bias)\n                return\n        all_windows.add_window(window)\n        g = all_windows.group_for_window(window)\n        assert g is not None\n        p = self.pairs_root.balanced_add(g.id)\n        if bias is not None:\n            p.bias = bias\n\n    def modify_size_of_window(\n        self,\n        all_windows: WindowList,\n        window_id: int,\n        increment: float,\n        is_horizontal: bool = True\n    ) -> bool:\n        grp = all_windows.group_for_window(window_id)\n        if grp is None:\n            return False\n        pair = self.pairs_root.pair_for_window(grp.id)\n        if pair is None:\n            return False\n        which = 1 if pair.one == grp.id else 2\n        return pair.modify_size_of_child(which, increment, is_horizontal, self)\n\n    def remove_all_biases(self) -> bool:\n        for pair in self.pairs_root.self_and_descendants():\n            pair.bias = 0.5\n        return True\n\n    def minimal_borders(self, all_windows: WindowList) -> Iterator[BorderLine]:\n        groups = tuple(all_windows.iter_all_layoutable_groups())\n        window_count = len(groups)\n        if not lgd.draw_minimal_borders or window_count < 2:\n            return\n        needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)\n        ag = all_windows.active_group\n        active_group_id = -1 if ag is None else ag.id\n\n        border_color_map = {}\n        for grp_id, needs_borders in needs_borders_map.items():\n            if needs_borders:\n                wid = g.active_window_id if (g := all_windows.group_for_id(grp_id)) else 0\n                if wid:\n                    color = BorderColor.active if grp_id is active_group_id else BorderColor.bell\n                    border_color_map[wid] = color\n\n        for pair in self.pairs_root.self_and_descendants():\n            if pair.between_borders:\n                for which in pair.between_borders:\n                    for bb in which:\n                        yield bb._replace(color=border_color_map.get(abs(bb.window_id), BorderColor.inactive))\n\n    def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:\n        wg = all_windows.group_for_window(window)\n        assert wg is not None\n        pair = self.pairs_root.pair_for_window(wg.id)\n        ans: NeighborsMap = {}\n        if pair is not None:\n            pair.neighbors_for_window(wg.id, ans, self, all_windows)\n        return ans\n\n    def move_window(self, all_windows: WindowList, delta: int = 1) -> bool:\n        before = all_windows.active_group\n        if before is None:\n            return False\n        before_idx = all_windows.active_group_idx\n        moved = super().move_window(all_windows, delta)\n        after = all_windows.groups[before_idx]\n        if moved and before.id != after.id:\n            self.pairs_root.swap_windows(before.id, after.id)\n        return moved\n\n    def move_window_to_group(self, all_windows: WindowList, group: int) -> bool:\n        before = all_windows.active_group\n        if before is None:\n            return False\n        before_idx = all_windows.active_group_idx\n        moved = super().move_window_to_group(all_windows, group)\n        after = all_windows.groups[before_idx]\n        if moved and before.id != after.id:\n            self.pairs_root.swap_windows(before.id, after.id)\n        return moved\n\n    def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> bool | None:\n        if action_name == 'rotate':\n            args = args or ('90',)\n            try:\n                amt = int(args[0])\n            except Exception:\n                amt = 90\n            if amt not in (90, 180, 270):\n                amt = 90\n            rotate = amt in (90, 270)\n            swap = amt in (180, 270)\n            wg = all_windows.active_group\n            if wg is not None:\n                pair = self.pairs_root.pair_for_window(wg.id)\n                if pair is not None and not pair.is_redundant:\n                    if rotate:\n                        pair.horizontal = not pair.horizontal\n                    if swap:\n                        pair.one, pair.two = pair.two, pair.one\n                    return True\n        elif action_name == 'move_to_screen_edge':\n            count = 0\n            for wid in self.pairs_root.all_window_ids():\n                count += 1\n                if count > 2:\n                    break\n            if count > 1:\n                args = args or ('left',)\n                which = args[0]\n                horizontal = which in ('left', 'right')\n                wg = all_windows.active_group\n                if wg is not None:\n                    if count == 2:  # special case, a single split\n                        pair = self.pairs_root.pair_for_window(wg.id)\n                        if pair is not None:\n                            pair.horizontal = horizontal\n                            if which in ('left', 'top'):\n                                if pair.one != wg.id:\n                                    pair.one, pair.two = pair.two, pair.one\n                                    pair.bias = 1. - pair.bias\n                            else:\n                                if pair.one == wg.id:\n                                    pair.one, pair.two = pair.two, pair.one\n                                    pair.bias = 1. - pair.bias\n                            return True\n                    else:\n                        self.remove_windows(wg.id)\n                        new_root = Pair(horizontal)\n                        if which in ('left', 'top'):\n                            new_root.balanced_add(wg.id)\n                            new_root.two = self.pairs_root\n                        else:\n                            new_root.one = self.pairs_root\n                            new_root.two = wg.id\n                        self.pairs_root = new_root\n                        return True\n        elif action_name == 'bias':\n            args = args or ('50',)\n            bias = int(args[0])\n            wg = all_windows.active_group\n            if wg is not None:\n                pair = self.pairs_root.pair_for_window(wg.id)\n                if pair is not None:\n                    pair.set_bias(wg.id, bias)\n                    return True\n        elif action_name == 'maximize':\n            args = args or ('horizontal',)\n            axis = args[0]\n            is_horizontal = axis == 'horizontal'\n            wg = all_windows.active_group\n            if wg is not None:\n                key = (wg.id, is_horizontal)\n                maximized_biases: dict[tuple[int, bool], list[tuple[Pair, float]]] = getattr(self, '_maximized_biases', {})\n                if key in maximized_biases:\n                    # Already maximized along this axis for this window — toggle back\n                    current_pair_ids = {id(p) for p in self.pairs_root.self_and_descendants()}\n                    for pair_ref, saved_bias in maximized_biases.pop(key):\n                        if id(pair_ref) in current_pair_ids:\n                            pair_ref.bias = saved_bias\n                    self._maximized_biases = maximized_biases\n                    return True\n                else:\n                    # Undo any existing maximize along the same axis (different window)\n                    stale_keys = [k for k in maximized_biases if k[1] == is_horizontal]\n                    if stale_keys:\n                        current_pair_ids = {id(p) for p in self.pairs_root.self_and_descendants()}\n                        for k in stale_keys:\n                            for pair_ref, saved_bias in maximized_biases.pop(k):\n                                if id(pair_ref) in current_pair_ids:\n                                    pair_ref.bias = saved_bias\n                    # Maximize: set biases along the path to give maximum space to active window\n                    # Only adjust pairs whose split axis matches the requested direction:\n                    # horizontal maximize expands width (affects horizontal/side-by-side splits),\n                    # vertical maximize expands height (affects vertical/top-bottom splits).\n                    tree_path = self.pairs_root.find_window_in_tree(wg.id)\n                    if tree_path is not None:\n                        saved_biases: list[tuple[Pair, float]] = []\n                        for pair, is_in_one in tree_path:\n                            if pair.horizontal == is_horizontal and not pair.is_redundant:\n                                saved_biases.append((pair, pair.bias))\n                                pair.bias = 1.0 if is_in_one else 0.0\n                        if saved_biases:\n                            maximized_biases[key] = saved_biases\n                            self._maximized_biases = maximized_biases\n                    return True\n\n        return None\n\n    def drag_resize_window(self, all_windows: WindowList, pair_id: int, increment: float, is_horizontal: bool = True) -> bool:\n        for pair in self.pairs_root.self_and_descendants():\n            if id(pair) == pair_id:\n                new_bias = max(0, min(pair.bias + increment, 1))\n                if new_bias != pair.bias:\n                    pair.bias = new_bias\n                    return True\n                break\n        return False\n\n    def drag_resize_target_windows(\n        self, click_window: WindowType, x: float, y: float, edges: int, all_windows: WindowList,\n    ) -> WindowResizeDragData:\n        is_right, is_bottom = bool(edges & RIGHT_EDGE), bool(edges & BOTTOM_EDGE)\n        is_leading_edge = not (is_right or is_bottom)\n        ans = WindowResizeDragData(None, is_right, None, is_bottom)\n        if (wg := all_windows.group_for_window(click_window)) is None or (pair := self.pairs_root.pair_for_window(wg.id)) is None:\n            return ans\n        pair_parent_map = {}\n        for p in self.pairs_root.self_and_descendants():\n            if isinstance(p.one, Pair):\n                pair_parent_map[p.one] = p\n            if isinstance(p.two, Pair):\n                pair_parent_map[p.two] = p\n        p = pair\n        def size_increases_forwards(p: Pair) -> bool:\n            in_leading_half = not p.is_group_on_second(wg.id)\n            if p is pair:\n                return is_leading_edge != in_leading_half\n            parent = pair_parent_map.get(p) or Pair()\n            if parent.horizontal != p.horizontal and is_leading_edge:\n                return True\n            return not in_leading_half\n\n        def ancestor_with_neighboring_border_of_same_orientation(p: Pair) -> Pair | None:\n            horizontal = bool(edges & (LEFT_EDGE | RIGHT_EDGE))\n            while (q := pair_parent_map.get(p)):\n                if q.horizontal == horizontal:\n                    if q.between_borders:\n                        return q\n                    break\n                p = q\n            return None\n\n        def pair_or_parent(p: Pair) -> tuple[Pair, bool]:\n            in_leading_half = not p.is_group_on_second(wg.id)\n            if is_leading_edge == in_leading_half and p is pair and (parent := ancestor_with_neighboring_border_of_same_orientation(p)):\n                # special case for leading edge of one or trailing edge of two with parent being same orientation\n                return parent, True\n            return p, size_increases_forwards(p)\n\n        while ans.horizontal_id is None or ans.vertical_id is None:\n            if p.is_redundant:\n                continue\n            if ans.horizontal_id is None and p.horizontal:\n                p, fwd = pair_or_parent(p)\n                ans = ans._replace(horizontal_id=id(p), width_increases_rightwards=fwd)\n            if ans.vertical_id is None and not p.horizontal:\n                p, fwd = pair_or_parent(p)\n                ans = ans._replace(vertical_id=id(p), height_increases_downwards=fwd)\n            if (parent := pair_parent_map.get(p)) is None:\n                break\n            p = parent\n        return ans\n\n    def layout_state(self) -> dict[str, Any]:\n        ans: dict[str, Any] = {'pairs': self.pairs_root.serialize()}\n        maximized_biases: dict[tuple[int, bool], list[tuple[Pair, float]]] = getattr(self, '_maximized_biases', {})\n        if maximized_biases:\n            serialized_maximized = []\n            for (window_id, is_horizontal), saved_biases_list in maximized_biases.items():\n                entries = []\n                for pair_ref, saved_bias in saved_biases_list:\n                    path = self.pairs_root.path_from_root(pair_ref)\n                    if path is not None:\n                        entries.append({'path': path, 'bias': saved_bias})\n                if entries:\n                    serialized_maximized.append({\n                        'window_id': window_id,\n                        'is_horizontal': is_horizontal,\n                        'saved_biases': entries,\n                    })\n            if serialized_maximized:\n                ans['maximized'] = serialized_maximized\n        return ans\n\n    def set_layout_state(self, layout_state: dict[str, Any], map_group_id: WindowMapper) -> bool:\n        new_root = Pair()\n        new_root.unserialize(layout_state['pairs'], map_group_id)\n        before = frozenset(self.pairs_root.all_window_ids())\n        if before == frozenset(new_root.all_window_ids()):\n            self.pairs_root = new_root\n            self.layout_opts = SplitsLayoutOpts(layout_state['opts'])\n            if 'maximized' in layout_state:\n                maximized_biases: dict[tuple[int, bool], list[tuple[Pair, float]]] = {}\n                for entry in layout_state['maximized']:\n                    new_window_id = map_group_id(entry['window_id'])\n                    if new_window_id is None:\n                        continue\n                    is_horizontal: bool = entry['is_horizontal']\n                    saved_biases_list: list[tuple[Pair, float]] = []\n                    for saved in entry['saved_biases']:\n                        pair = new_root.pair_at_path(saved['path'])\n                        if pair is not None:\n                            saved_biases_list.append((pair, saved['bias']))\n                    if saved_biases_list:\n                        maximized_biases[(new_window_id, is_horizontal)] = saved_biases_list\n                if maximized_biases:\n                    self._maximized_biases = maximized_biases\n            return True\n        return False\n"
  },
  {
    "path": "kitty/layout/stack.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom kitty.types import NeighborsMap\nfrom kitty.typing_compat import WindowType\nfrom kitty.window_list import WindowList\n\nfrom .base import Layout\n\n\nclass Stack(Layout):\n\n    name = 'stack'\n    needs_window_borders = False\n    only_active_window_visible = True\n\n    def do_layout(self, all_windows: WindowList) -> None:\n        active_group = all_windows.active_group\n        for group in all_windows.iter_all_layoutable_groups():\n            self.layout_single_window_group(group, add_blank_rects=group is active_group)\n\n    def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:\n        wg = all_windows.group_for_window(window)\n        assert wg is not None\n        groups = tuple(all_windows.iter_all_layoutable_groups())\n        idx = groups.index(wg)\n        before = [] if wg is groups[0] else [groups[idx-1].id]\n        after = [] if wg is groups[-1] else [groups[idx+1].id]\n        return {'top': before, 'left': before, 'right': after, 'bottom': after}\n"
  },
  {
    "path": "kitty/layout/tall.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nfrom collections.abc import Generator, Iterator, Sequence\nfrom itertools import islice, repeat\nfrom typing import Any\n\nfrom kitty.borders import BorderColor\nfrom kitty.conf.utils import to_bool\nfrom kitty.fast_data_types import BOTTOM_EDGE, RIGHT_EDGE\nfrom kitty.types import Edges, NeighborsMap, WindowMapper, WindowResizeDragData\nfrom kitty.typing_compat import EdgeLiteral, WindowType\nfrom kitty.window_list import WindowGroup, WindowList\n\nfrom .base import (\n    BorderLine,\n    Layout,\n    LayoutData,\n    LayoutDimension,\n    LayoutOpts,\n    lgd,\n    normalize_biases,\n    safe_increment_bias,\n)\nfrom .vertical import borders\n\n\ndef drag_resize_target_windows(\n        click_window: WindowType,\n        edges: int,\n        x: float, y: float,\n        num_full_size_windows: int,\n        all_windows: WindowList,\n        main_is_horizontal: bool = True\n) -> WindowResizeDragData:\n    groups = tuple(all_windows.iter_all_layoutable_groups())\n    horizontal = vertical = click_window\n    min_dist = float(sys.maxsize)\n    height_increases_downwards = bool(edges & BOTTOM_EDGE)\n    width_increases_rightwards = bool(edges & RIGHT_EDGE)\n    for gr in groups[num_full_size_windows:]:\n        if gr.windows:\n            w = gr.windows[-1]\n            g = w.geometry\n            if main_is_horizontal:\n                if (dist := min(abs(g.top - y), abs(g.bottom - y))) < min_dist:\n                    min_dist = dist\n                    vertical = w\n                    height_increases_downwards = y > g.top + (g.bottom - g.top) / 2\n            else:\n                if (dist := min(abs(g.left - x), abs(g.right - x))) < min_dist:\n                    min_dist = dist\n                    horizontal = w\n                    width_increases_rightwards = x > g.left + (g.right - g.left) / 2\n    return WindowResizeDragData(horizontal.id, width_increases_rightwards, vertical.id, height_increases_downwards)\n\n\ndef neighbors_for_tall_window(\n        num_full_size_windows: int,\n        window: WindowType,\n        all_windows: WindowList,\n        mirrored: bool = False,\n        main_is_horizontal: bool = True\n) -> NeighborsMap:\n    wg = all_windows.group_for_window(window)\n    assert wg is not None\n    groups = tuple(all_windows.iter_all_layoutable_groups())\n    idx = groups.index(wg)\n    prev = None if idx == 0 else groups[idx-1]\n    nxt = None if idx == len(groups) - 1 else groups[idx+1]\n    ans: NeighborsMap = {}\n    main_before: EdgeLiteral = 'left' if main_is_horizontal else 'top'\n    main_after: EdgeLiteral = 'right' if main_is_horizontal else 'bottom'\n    cross_before: EdgeLiteral = 'top' if main_is_horizontal else 'left'\n    cross_after: EdgeLiteral = 'bottom' if main_is_horizontal else 'right'\n    if mirrored:\n        main_before, main_after = main_after, main_before\n    if prev is not None:\n        ans[main_before] = [prev.id]\n    if idx < num_full_size_windows - 1:\n        if nxt is not None:\n            ans[main_after] = [nxt.id]\n    elif idx == num_full_size_windows - 1:\n        ans[main_after] = [w.id for w in groups[idx+1:]]\n    else:\n        ans[main_before] = [groups[num_full_size_windows - 1].id]\n        if idx > num_full_size_windows and prev is not None:\n            ans[cross_before] = [prev.id]\n        if nxt is not None:\n            ans[cross_after] = [nxt.id]\n    return ans\n\n\nclass TallLayoutOpts(LayoutOpts):\n    bias: int = 50\n    full_size: int = 1\n    mirrored: bool = False\n\n    def __init__(self, data: dict[str, str]):\n        try:\n            self.full_size = int(data.get('full_size', 1))\n        except Exception:\n            self.full_size = 1\n        self.full_size = max(1, min(self.full_size, 100))\n        try:\n            self.bias = int(data.get('bias', 50))\n        except Exception:\n            self.bias = 50\n        rv: bool | str = data.get('mirrored', 'false')\n        self.mirrored = to_bool(rv) if isinstance(rv, str) else bool(rv)\n\n    def serialized(self) -> dict[str, Any]:\n        return {'full_size': self.full_size, 'bias': self.bias, 'mirrored': 'y' if self.mirrored else 'n'}\n\n    def build_bias_list(self) -> tuple[float, ...]:\n        b = self.bias / 100\n        b = max(0.1, min(b, 0.9))\n        return tuple(repeat(b / self.full_size, self.full_size)) + (1.0 - b,)\n\n\ndef set_bias(biases: Sequence[float], idx: int, target: float) -> list[float]:\n    remainder = 1 - target\n    previous_remainder = sum(x for i, x in enumerate(biases) if i != idx)\n    ans = [1. for i in range(len(biases))]\n    for i in range(len(biases)):\n        if i == idx:\n            ans[i] = target\n        else:\n            ans[i] = remainder * biases[i] / previous_remainder\n    return ans\n\n\nclass Tall(Layout):\n\n    name = 'tall'\n    main_is_horizontal = True\n    no_minimal_window_borders = True\n    layout_opts = TallLayoutOpts({})\n    main_axis_layout = Layout.xlayout\n    perp_axis_layout = Layout.ylayout\n\n    @property\n    def num_full_size_windows(self) -> int:\n        return self.layout_opts.full_size\n\n    def remove_all_biases(self) -> bool:\n        self.main_bias: list[float] = list(self.layout_opts.build_bias_list())\n        self.biased_map: dict[int, float] = {}\n        return True\n\n    def variable_layout(self, all_windows: WindowList, biased_map: dict[int, float]) -> LayoutDimension:\n        num = all_windows.num_groups - self.num_full_size_windows\n        bias = biased_map if num > 1 else None\n        return self.perp_axis_layout(all_windows.iter_all_layoutable_groups(), bias=bias, offset=self.num_full_size_windows)\n\n    def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:\n        if idx < len(self.main_bias):\n            before_main_bias = self.main_bias\n            self.main_bias = set_bias(self.main_bias, idx, fractional_bias)\n            return self.main_bias != before_main_bias\n\n        before_layout = tuple(self.variable_layout(all_windows, self.biased_map))\n        self.biased_map[idx - self.num_full_size_windows] = cell_increment_bias_v if self.main_is_horizontal else cell_increment_bias_h\n        after_layout = tuple(self.variable_layout(all_windows, self.biased_map))\n        return before_layout == after_layout\n\n    def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:\n        num_windows = all_windows.num_groups\n        if self.main_is_horizontal == is_horizontal:\n            before_main_bias = self.main_bias\n            ncols = self.num_full_size_windows + 1\n            biased_col = idx if idx < self.num_full_size_windows else (ncols - 1)\n            self.main_bias = [\n                safe_increment_bias(self.main_bias[i], increment * (1 if i == biased_col else -1)) for i in range(ncols)\n            ]\n            self.main_bias = normalize_biases(self.main_bias)\n            return self.main_bias != before_main_bias\n\n        num_of_short_windows = num_windows - self.num_full_size_windows\n        if idx < self.num_full_size_windows or num_of_short_windows < 2:\n            return False\n        idx -= self.num_full_size_windows\n        before_layout = tuple(self.variable_layout(all_windows, self.biased_map))\n        before = self.biased_map.get(idx, 0.)\n        candidate = self.biased_map.copy()\n        candidate[idx] = after = before + increment\n        if before_layout == tuple(self.variable_layout(all_windows, candidate)):\n            return False\n        self.biased_map = candidate\n        return before != after\n\n    def simple_layout(self, all_windows: WindowList) -> Generator[tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]:\n        num = all_windows.num_groups\n        is_fat = not self.main_is_horizontal\n        mirrored = self.layout_opts.mirrored\n        groups = tuple(all_windows.iter_all_layoutable_groups())\n        main_bias = self.main_bias[::-1] if mirrored else self.main_bias\n        if mirrored:\n            groups = tuple(reversed(groups))\n        main_bias = normalize_biases(main_bias[:num])\n        xlayout = self.main_axis_layout(iter(groups), bias=main_bias)\n        for wg, xl in zip(groups, xlayout):\n            yl = next(self.perp_axis_layout(iter((wg,))))\n            if is_fat:\n                xl, yl = yl, xl\n            yield wg, xl, yl, True\n\n    def full_layout(self, all_windows: WindowList) -> Generator[tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]:\n        is_fat = not self.main_is_horizontal\n        mirrored = self.layout_opts.mirrored\n        groups = tuple(all_windows.iter_all_layoutable_groups())\n        main_bias = self.main_bias[::-1] if mirrored else self.main_bias\n\n        start = lgd.central.top if is_fat else lgd.central.left\n        size = 0\n        if mirrored:\n            fsg = groups[:self.num_full_size_windows + 1]\n            xlayout = self.main_axis_layout(reversed(fsg), bias=main_bias)\n            for i, wg in enumerate(reversed(fsg)):\n                xl = next(xlayout)\n                if i == 0:\n                    size = xl.content_size + xl.space_before + xl.space_after\n                    continue\n                yl = next(self.perp_axis_layout(iter((wg,))))\n                if is_fat:\n                    xl, yl = yl, xl\n                yield wg, xl, yl, True\n        else:\n            xlayout = self.main_axis_layout(islice(groups, self.num_full_size_windows + 1), bias=main_bias)\n            for i, wg in enumerate(groups):\n                if i >= self.num_full_size_windows:\n                    break\n                xl = next(xlayout)\n                yl = next(self.perp_axis_layout(iter((wg,))))\n                start = xl.content_pos + xl.content_size + xl.space_after\n                if is_fat:\n                    xl, yl = yl, xl\n                yield wg, xl, yl, True\n            size = 1 + (lgd.central.bottom if is_fat else lgd.central.right) - start\n\n        ylayout = self.variable_layout(all_windows, self.biased_map)\n        for i, wg in enumerate(all_windows.iter_all_layoutable_groups()):\n            if i < self.num_full_size_windows:\n                continue\n            yl = next(ylayout)\n            xl = next(self.main_axis_layout(iter((wg,)), start=start, size=size))\n            if is_fat:\n                xl, yl = yl, xl\n            yield wg, xl, yl, False\n\n    def do_layout(self, all_windows: WindowList) -> None:\n        num = all_windows.num_groups\n        if num == 1:\n            self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))\n            return\n        layouts = (self.simple_layout if num <= self.num_full_size_windows + 1 else self.full_layout)(all_windows)\n        for wg, xl, yl, is_full_size in layouts:\n            self.set_window_group_geometry(wg, xl, yl)\n\n    def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap:\n        return neighbors_for_tall_window(self.num_full_size_windows, window, windows, self.layout_opts.mirrored, self.main_is_horizontal)\n\n    def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> bool | None:\n        if action_name == 'increase_num_full_size_windows':\n            self.layout_opts.full_size += 1\n            self.main_bias = list(self.layout_opts.build_bias_list())\n            return True\n        if action_name == 'decrease_num_full_size_windows':\n            if self.layout_opts.full_size > 1:\n                self.layout_opts.full_size -= 1\n                self.main_bias = list(self.layout_opts.build_bias_list())\n                return True\n        if action_name == 'mirror':\n            action = (args or ('toggle',))[0]\n            ok = False\n            if action == 'toggle':\n                self.layout_opts.mirrored = not self.layout_opts.mirrored\n                ok = True\n            else:\n                new_val = to_bool(action)\n                if new_val != self.layout_opts.mirrored:\n                    self.layout_opts.mirrored = new_val\n                    ok = True\n            return ok\n        if action_name == 'bias':\n            if len(args) == 0:\n                raise ValueError('layout_action bias must contain at least one number between 10 and 90')\n            biases = args[0].split()\n            if len(biases) == 1:\n                biases.append(\"50\")\n            try:\n                i = biases.index(str(self.layout_opts.bias)) + 1\n            except ValueError:\n                i = 0\n            try:\n                self.layout_opts.bias = int(biases[i % len(biases)])\n                self.remove_all_biases()\n                return True\n            except Exception:\n                return False\n        return None\n\n    def minimal_borders(self, all_windows: WindowList) -> Iterator[BorderLine]:\n        num = all_windows.num_groups\n        if num < 2 or not lgd.draw_minimal_borders:\n            return\n        try:\n            bw = next(all_windows.iter_all_layoutable_groups()).effective_border()\n        except StopIteration:\n            bw = 0\n        if not bw:\n            return\n        if num <= self.num_full_size_windows + 1:\n            layout = (x[:3] for x in self.simple_layout(all_windows))\n            yield from borders(layout, self.main_is_horizontal, all_windows)\n            return\n        main_layouts: list[tuple[WindowGroup, LayoutData, LayoutData]] = []\n        perp_borders: list[BorderLine] = []\n        layouts = (self.simple_layout if num <= self.num_full_size_windows else self.full_layout)(all_windows)\n        needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)\n        active_group = all_windows.active_group\n        mirrored = self.layout_opts.mirrored\n        for wg, xl, yl, is_full_size in layouts:\n            if is_full_size:\n                main_layouts.append((wg, xl, yl))\n            else:\n                color = BorderColor.inactive\n                if needs_borders_map.get(wg.id):\n                    color = BorderColor.active if wg is active_group else BorderColor.bell\n                wid = wg.active_window_id\n                mult = 1 if mirrored else -1\n                def h(left: int, right: int, top: int, mult: int) -> None:\n                    e = Edges(left, top, right, top + bw)\n                    perp_borders.append(BorderLine(e, color, mult*wid, True))\n                def v(top: int, bottom: int, left: int, mult: int) -> None:\n                    e = Edges(left, top, left + bw, bottom)\n                    perp_borders.append(BorderLine(e, color, mult*wid, False))\n\n                if self.main_is_horizontal:\n                    h(xl.content_pos - xl.space_before, xl.content_pos + xl.content_size + xl.space_after,\n                      yl.content_pos - yl.space_before, -1)\n                    v(yl.content_pos - yl.space_before, yl.content_pos + yl.content_size + yl.space_after,\n                      xl.content_pos + ((xl.content_size + xl.space_after - bw) if mirrored else -xl.space_before), mult)\n                    h(xl.content_pos - xl.space_before, xl.content_pos + xl.content_size + xl.space_after,\n                      yl.content_pos + yl.content_size + yl.space_after - bw, 1)\n                else:\n                    v(yl.content_pos - yl.space_before, yl.content_pos + yl.content_size + yl.space_after,\n                      xl.content_pos - xl.space_before, -1)\n                    h(xl.content_pos - xl.space_before, xl.content_pos + xl.content_size + xl.space_after,\n                      yl.content_pos + ((yl.content_size + yl.space_after - bw) if mirrored else -yl.space_before), mult)\n                    v(yl.content_pos - yl.space_before, yl.content_pos + yl.content_size + yl.space_after,\n                      xl.content_pos + xl.content_size + xl.space_after - bw, 1)\n\n        mirrored = self.layout_opts.mirrored\n        yield from borders(\n            main_layouts, self.main_is_horizontal, all_windows,\n            start_offset=int(not mirrored), end_offset=int(mirrored)\n        )\n        yield from perp_borders[1:-1]\n\n    def layout_state(self) -> dict[str, Any]:\n        return {\n            'main_bias': self.main_bias,\n            'biased_map': self.biased_map\n        }\n\n    def set_layout_state(self, layout_state: dict[str, Any], map_group_id: WindowMapper) -> bool:\n        self.main_bias = layout_state['main_bias']\n        self.biased_map = {int(k): v for k, v in layout_state['biased_map'].items()}\n        self.layout_opts = TallLayoutOpts(layout_state['opts'])\n        return True\n\n    def drag_resize_target_windows(\n        self, click_window: WindowType, x: float, y: float, edges: int, all_windows: WindowList,\n    ) -> WindowResizeDragData:\n        return drag_resize_target_windows(click_window, edges, x, y, self.num_full_size_windows, all_windows, self.main_is_horizontal)\n\n\nclass Fat(Tall):\n\n    name = 'fat'\n    main_is_horizontal = False\n    main_axis_layout = Layout.ylayout\n    perp_axis_layout = Layout.xlayout\n"
  },
  {
    "path": "kitty/layout/vertical.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Generator, Iterable\nfrom typing import Any\n\nfrom kitty.borders import BorderColor\nfrom kitty.types import Edges, NeighborsMap, WindowMapper\nfrom kitty.typing_compat import EdgeLiteral, WindowType\nfrom kitty.window_list import WindowGroup, WindowList\n\nfrom .base import BorderLine, Layout, LayoutData, LayoutDimension, lgd\n\n\ndef borders(\n    data: Iterable[tuple[WindowGroup, LayoutData, LayoutData]],\n    is_horizontal: bool,\n    all_windows: WindowList,\n    start_offset: int = 1, end_offset: int = 1\n) -> Generator[BorderLine, None, None]:\n    borders: list[BorderLine] = []\n    active_group = all_windows.active_group\n    needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders)\n    try:\n        bw = next(all_windows.iter_all_layoutable_groups()).effective_border()\n    except StopIteration:\n        bw = 0\n    if not bw:\n        return\n\n    for wg, xl, yl in data:\n        if is_horizontal:\n            e1 = Edges(\n                xl.content_pos - xl.space_before, yl.content_pos - yl.space_before,\n                xl.content_pos - xl.space_before + bw, yl.content_pos + yl.content_size + yl.space_after\n            )\n            e2 = Edges(\n                xl.content_pos + xl.content_size + xl.space_after - bw, yl.content_pos - yl.space_before,\n                xl.content_pos + xl.content_size + xl.space_after, yl.content_pos + yl.content_size + yl.space_after\n            )\n        else:\n            e1 = Edges(\n                xl.content_pos - xl.space_before, yl.content_pos - yl.space_before,\n                xl.content_pos + xl.content_size + xl.space_after, yl.content_pos - yl.space_before + bw\n            )\n            e2 = Edges(\n                xl.content_pos - xl.space_before, yl.content_pos + yl.content_size + yl.space_after - bw,\n                xl.content_pos + xl.content_size + xl.space_after, yl.content_pos + yl.content_size + yl.space_after\n            )\n        color = BorderColor.inactive\n        if needs_borders_map.get(wg.id):\n            color = BorderColor.active if wg is active_group else BorderColor.bell\n        borders.append(BorderLine(e1, color, -wg.active_window_id, not is_horizontal))\n        borders.append(BorderLine(e2, color, wg.active_window_id, not is_horizontal))\n\n    last_idx = len(borders) - 1 - end_offset\n    for i, x in enumerate(borders):\n        if start_offset <= i <= last_idx:\n            yield x\n\n\nclass Vertical(Layout):\n\n    name = 'vertical'\n    main_is_horizontal = False\n    no_minimal_window_borders = True\n    main_axis_layout = Layout.ylayout\n    perp_axis_layout = Layout.xlayout\n\n    def variable_layout(self, all_windows: WindowList, biased_map: dict[int, float]) -> LayoutDimension:\n        num_windows = all_windows.num_groups\n        bias = biased_map if num_windows > 1 else None\n        return self.main_axis_layout(all_windows.iter_all_layoutable_groups(), bias=bias)\n\n    def fixed_layout(self, wg: WindowGroup) -> LayoutDimension:\n        return self.perp_axis_layout(iter((wg,)), border_mult=0 if lgd.draw_minimal_borders else 1)\n\n    def remove_all_biases(self) -> bool:\n        self.biased_map: dict[int, float] = {}\n        return True\n\n    def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool:\n        if self.main_is_horizontal != is_horizontal:\n            return False\n        num_windows = all_windows.num_groups\n        if num_windows < 2:\n            return False\n        before_layout = list(self.variable_layout(all_windows, self.biased_map))\n        candidate = self.biased_map.copy()\n        before = candidate.get(idx, 0)\n        candidate[idx] = before + increment\n        if before_layout == list(self.variable_layout(all_windows, candidate)):\n            return False\n        self.biased_map = candidate\n        return True\n\n    def bias_slot(self, all_windows: WindowList, idx: int, fractional_bias: float, cell_increment_bias_h: float, cell_increment_bias_v: float) -> bool:\n        before_layout = tuple(self.variable_layout(all_windows, self.biased_map))\n        self.biased_map[idx] = cell_increment_bias_h if self.main_is_horizontal else cell_increment_bias_v\n        after_layout = tuple(self.variable_layout(all_windows, self.biased_map))\n        return before_layout == after_layout\n\n    def generate_layout_data(self, all_windows: WindowList) -> Generator[tuple[WindowGroup, LayoutData, LayoutData], None, None]:\n        ylayout = self.variable_layout(all_windows, self.biased_map)\n        for wg, yl in zip(all_windows.iter_all_layoutable_groups(), ylayout):\n            xl = next(self.fixed_layout(wg))\n            if self.main_is_horizontal:\n                xl, yl = yl, xl\n            yield wg, xl, yl\n\n    def do_layout(self, all_windows: WindowList) -> None:\n        window_count = all_windows.num_groups\n        if window_count == 1:\n            self.layout_single_window_group(next(all_windows.iter_all_layoutable_groups()))\n            return\n        for wg, xl, yl in self.generate_layout_data(all_windows):\n            self.set_window_group_geometry(wg, xl, yl)\n\n    def minimal_borders(self, all_windows: WindowList) -> Generator[BorderLine, None, None]:\n        window_count = all_windows.num_groups\n        if window_count < 2 or not lgd.draw_minimal_borders:\n            return\n        yield from borders(self.generate_layout_data(all_windows), self.main_is_horizontal, all_windows)\n\n    def neighbors_for_window(self, window: WindowType, all_windows: WindowList) -> NeighborsMap:\n        wg = all_windows.group_for_window(window)\n        assert wg is not None\n        groups = tuple(all_windows.iter_all_layoutable_groups())\n        idx = groups.index(wg)\n        lg = len(groups)\n        if lg > 1:\n            after = [groups[(idx - 1 + lg) % lg].id]\n            before = [groups[(idx + 1) % lg].id]\n        else:\n            before, after = [], []\n        ans: NeighborsMap = {}\n        akey: EdgeLiteral = 'top'\n        bkey: EdgeLiteral = 'bottom'\n        if self.main_is_horizontal:\n            akey, bkey = 'left', 'right'\n        if before:\n            ans[bkey] = before\n        if after:\n            ans[akey] = after\n        return ans\n\n    def layout_state(self) -> dict[str, Any]:\n        return {'biased_map': self.biased_map}\n\n    def set_layout_state(self, layout_state: dict[str, Any], map_group_id: WindowMapper) -> bool:\n        self.biased_map = {int(k): v for k, v in layout_state['biased_map'].items()}\n        return True\n\nclass Horizontal(Vertical):\n\n    name = 'horizontal'\n    main_is_horizontal = True\n    main_axis_layout = Layout.xlayout\n    perp_axis_layout = Layout.ylayout\n"
  },
  {
    "path": "kitty/line-buf.c",
    "content": "/*\n * line-buf.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"lineops.h\"\n#include \"resize.h\"\n\n#include <structmember.h>\n\nextern PyTypeObject Line_Type;\nextern PyTypeObject HistoryBuf_Type;\n\nstatic CPUCell*\ncpu_lineptr(LineBuf *linebuf, index_type y) {\n    return linebuf->cpu_cell_buf + y * linebuf->xnum;\n}\n\nstatic GPUCell*\ngpu_lineptr(LineBuf *linebuf, index_type y) {\n    return linebuf->gpu_cell_buf + y * linebuf->xnum;\n}\n\nstatic void\nclear_chars_to(LineBuf* linebuf, index_type y, char_type ch) {\n    clear_chars_in_line(cpu_lineptr(linebuf, y), gpu_lineptr(linebuf, y), linebuf->xnum, ch);\n}\n\nvoid\nlinebuf_clear(LineBuf *self, char_type ch) {\n    zero_at_ptr_count(self->cpu_cell_buf, self->xnum * self->ynum);\n    zero_at_ptr_count(self->gpu_cell_buf, self->xnum * self->ynum);\n    zero_at_ptr_count(self->line_attrs, self->ynum);\n    for (index_type i = 0; i < self->ynum; i++) self->line_map[i] = i;\n    if (ch != 0) {\n        for (index_type i = 0; i < self->ynum; i++) {\n            clear_chars_to(self, i, ch);\n            self->line_attrs[i].val = 0;\n            self->line_attrs[i].has_dirty_text = true;\n        }\n    }\n}\n\nvoid\nlinebuf_mark_line_dirty(LineBuf *self, index_type y) {\n    self->line_attrs[y].has_dirty_text = true;\n}\n\nvoid\nlinebuf_mark_line_clean(LineBuf *self, index_type y) {\n    self->line_attrs[y].has_dirty_text = false;\n}\n\nvoid\nlinebuf_set_line_has_image_placeholders(LineBuf *self, index_type y, bool val) {\n    self->line_attrs[y].has_image_placeholders = val;\n}\n\nvoid\nlinebuf_clear_attrs_and_dirty(LineBuf *self, index_type y) {\n    self->line_attrs[y].val = 0;\n    self->line_attrs[y].has_dirty_text = true;\n}\n\nstatic PyObject*\nclear(LineBuf *self, PyObject *a UNUSED) {\n#define clear_doc \"Clear all lines in this LineBuf\"\n    linebuf_clear(self, BLANK_CHAR);\n    Py_RETURN_NONE;\n}\n\nLineBuf *\nalloc_linebuf_(PyTypeObject *cls, unsigned int lines, unsigned int columns, TextCache *text_cache) {\n    if (columns > 5000 || lines > 50000) {\n        PyErr_SetString(PyExc_ValueError, \"Number of rows or columns is too large.\");\n        return NULL;\n    }\n\n    const size_t area = columns * lines;\n    if (area == 0) {\n        PyErr_SetString(PyExc_ValueError, \"Cannot create an empty LineBuf\");\n        return NULL;\n    }\n\n    LineBuf *self = (LineBuf*)cls->tp_alloc(cls, 0);\n    if (self != NULL) {\n        self->xnum = columns;\n        self->ynum = lines;\n        self->cpu_cell_buf = PyMem_Calloc(1, area * (sizeof(CPUCell) + sizeof(GPUCell)) + lines * (sizeof(index_type) + sizeof(index_type) + sizeof(LineAttrs)));\n        if (!self->cpu_cell_buf) { Py_CLEAR(self); return NULL; }\n        self->gpu_cell_buf = (GPUCell*)(self->cpu_cell_buf + area);\n        self->line_map = (index_type*)(self->gpu_cell_buf + area);\n        self->scratch = self->line_map + lines;\n        self->text_cache = tc_incref(text_cache);\n        self->line = alloc_line(self->text_cache);\n        self->line_attrs = (LineAttrs*)(self->scratch + lines);\n        self->line->xnum = columns;\n        for(index_type i = 0; i < lines; i++) {\n            self->line_map[i] = i;\n            if (BLANK_CHAR != 0) clear_chars_to(self, i, BLANK_CHAR);\n        }\n    }\n    return self;\n}\n\nstatic PyObject *\nnew_linebuf_object(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {\n    unsigned int xnum = 1, ynum = 1;\n\n    if (!PyArg_ParseTuple(args, \"II\", &ynum, &xnum)) return NULL;\n    TextCache *tc = tc_alloc();\n    if (!tc) return PyErr_NoMemory();\n    PyObject *ans = (PyObject*)alloc_linebuf_(type, ynum, xnum, tc);\n    tc_decref(tc);\n    return ans;\n}\n\nstatic void\ndealloc(LineBuf* self) {\n    self->text_cache = tc_decref(self->text_cache);\n    PyMem_Free(self->cpu_cell_buf);\n    Py_CLEAR(self->line);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nvoid\nlinebuf_init_cells(LineBuf *lb, index_type idx, CPUCell **c, GPUCell **g) {\n    const index_type ynum = lb->line_map[idx];\n    *c = cpu_lineptr(lb, ynum);\n    *g = gpu_lineptr(lb, ynum);\n}\n\nCPUCell*\nlinebuf_cpu_cells_for_line(LineBuf *lb, index_type idx) {\n    const index_type ynum = lb->line_map[idx];\n    return cpu_lineptr(lb, ynum);\n}\n\nstatic void\ninit_line(LineBuf *lb, Line *l, index_type ynum) {\n    l->cpu_cells = cpu_lineptr(lb, ynum);\n    l->gpu_cells = gpu_lineptr(lb, ynum);\n}\n\nvoid\nlinebuf_init_line_at(LineBuf *self, index_type idx, Line *line) {\n    line->ynum = idx;\n    line->xnum = self->xnum;\n    line->attrs = self->line_attrs[idx];\n    init_line(self, line, self->line_map[idx]);\n}\n\nvoid\nlinebuf_init_line(LineBuf *self, index_type idx) {\n    linebuf_init_line_at(self, idx, self->line);\n}\n\nvoid\nlinebuf_clear_lines(LineBuf *self, const Cursor *cursor, index_type start, index_type end) {\n#if BLANK_CHAR != 0\n#error This implementation is incorrect for BLANK_CHAR != 0\n#endif\n#define lineptr(which, i) which##_lineptr(self, self->line_map[i])\n    GPUCell *first_gpu_line = lineptr(gpu, start);\n    const GPUCell gc = cursor_as_gpu_cell(cursor);\n    memset_array(first_gpu_line, gc, self->xnum);\n    const size_t cpu_stride = sizeof(CPUCell) * self->xnum;\n    memset(lineptr(cpu, start), 0, cpu_stride);\n    const size_t gpu_stride = sizeof(GPUCell) * self->xnum;\n    linebuf_clear_attrs_and_dirty(self, start);\n    for (index_type i = start + 1; i < end; i++) {\n        memset(lineptr(cpu, i), 0, cpu_stride);\n        memcpy(lineptr(gpu, i), first_gpu_line, gpu_stride);\n        linebuf_clear_attrs_and_dirty(self, i);\n    }\n#undef lineptr\n}\n\nstatic PyObject*\nline(LineBuf *self, PyObject *y) {\n#define line_doc      \"Return the specified line as a Line object. Note the Line Object is a live view into the underlying buffer. And only a single line object can be used at a time.\"\n    unsigned long idx = PyLong_AsUnsignedLong(y);\n    if (idx >= self->ynum) {\n        PyErr_SetString(PyExc_IndexError, \"Line number too large\");\n        return NULL;\n    }\n    linebuf_init_line(self, idx);\n    Py_INCREF(self->line);\n    return (PyObject*)self->line;\n}\n\nCPUCell*\nlinebuf_cpu_cell_at(LineBuf *self, index_type x, index_type y) {\n    return &cpu_lineptr(self, self->line_map[y])[x];\n}\n\nbool\nlinebuf_line_ends_with_continuation(LineBuf *self, index_type y) {\n    return y < self->ynum ? cpu_lineptr(self, self->line_map[y])[self->xnum - 1].next_char_was_wrapped : false;\n}\n\nvoid\nlinebuf_set_last_char_as_continuation(LineBuf *self, index_type y, bool continued) {\n    if (y < self->ynum) {\n        cpu_lineptr(self, self->line_map[y])[self->xnum - 1].next_char_was_wrapped = continued;\n    }\n}\n\n\nstatic PyObject*\nset_attribute(LineBuf *self, PyObject *args) {\n#define set_attribute_doc \"set_attribute(which, val) -> Set the attribute on all cells in the line.\"\n    unsigned int val;\n    char *which;\n    if (!PyArg_ParseTuple(args, \"sI\", &which, &val)) return NULL;\n    for (index_type y = 0; y < self->ynum; y++) {\n        if (!set_named_attribute_on_line(gpu_lineptr(self, y), which, val, self->xnum)) {\n            PyErr_SetString(PyExc_KeyError, \"Unknown cell attribute\"); return NULL;\n        }\n        self->line_attrs[y].has_dirty_text = true;\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nset_continued(LineBuf *self, PyObject *args) {\n#define set_continued_doc \"set_continued(y, val) -> Set the continued values for the specified line.\"\n    unsigned int y;\n    int val;\n    if (!PyArg_ParseTuple(args, \"Ip\", &y, &val)) return NULL;\n    if (y > self->ynum || y < 1) { PyErr_SetString(PyExc_ValueError, \"Out of bounds.\"); return NULL; }\n    linebuf_set_last_char_as_continuation(self, y-1, val);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\ndirty_lines(LineBuf *self, PyObject *a UNUSED) {\n#define dirty_lines_doc \"dirty_lines() -> Line numbers of all lines that have dirty text.\"\n    PyObject *ans = PyList_New(0);\n    for (index_type i = 0; i < self->ynum; i++) {\n        if (self->line_attrs[i].has_dirty_text) {\n            PyList_Append(ans, PyLong_FromUnsignedLong(i));\n        }\n    }\n    return ans;\n}\n\nstatic bool\nallocate_line_storage(Line *line, bool initialize) {\n    if (initialize) {\n        line->cpu_cells = PyMem_Calloc(line->xnum, sizeof(CPUCell));\n        line->gpu_cells = PyMem_Calloc(line->xnum, sizeof(GPUCell));\n        if (line->cpu_cells == NULL || line->gpu_cells) { PyErr_NoMemory(); return false; }\n        if (BLANK_CHAR != 0) clear_chars_in_line(line->cpu_cells, line->gpu_cells, line->xnum, BLANK_CHAR);\n    } else {\n        line->cpu_cells = PyMem_Malloc(line->xnum * sizeof(CPUCell));\n        line->gpu_cells = PyMem_Malloc(line->xnum * sizeof(GPUCell));\n        if (line->cpu_cells == NULL || line->gpu_cells == NULL) { PyErr_NoMemory(); return false; }\n    }\n    line->needs_free = 1;\n    return true;\n}\n\nstatic PyObject*\ncreate_line_copy_inner(LineBuf* self, index_type y) {\n    Line src, *line;\n    line = alloc_line(self->text_cache);\n    if (line == NULL) return PyErr_NoMemory();\n    src.xnum = self->xnum; line->xnum = self->xnum;\n    if (!allocate_line_storage(line, 0)) { Py_CLEAR(line); return PyErr_NoMemory(); }\n    line->ynum = y;\n    line->attrs = self->line_attrs[y];\n    init_line(self, &src, self->line_map[y]);\n    copy_line(&src, line);\n    return (PyObject*)line;\n}\n\nstatic PyObject*\ncreate_line_copy(LineBuf *self, PyObject *ynum) {\n#define create_line_copy_doc \"Create a new Line object that is a copy of the line at ynum. Note that this line has its own copy of the data and does not refer to the data in the LineBuf.\"\n    index_type y = (index_type)PyLong_AsUnsignedLong(ynum);\n    if (y >= self->ynum) { PyErr_SetString(PyExc_ValueError, \"Out of bounds\"); return NULL; }\n    return create_line_copy_inner(self, y);\n}\n\nstatic PyObject*\ncopy_line_to(LineBuf *self, PyObject *args) {\n#define copy_line_to_doc \"Copy the line at ynum to the provided line object.\"\n    unsigned int y;\n    Line src, *dest;\n    if (!PyArg_ParseTuple(args, \"IO!\", &y, &Line_Type, &dest)) return NULL;\n    src.xnum = self->xnum; dest->xnum = self->xnum;\n    dest->ynum = y;\n    dest->attrs = self->line_attrs[y];\n    init_line(self, &src, self->line_map[y]);\n    copy_line(&src, dest);\n    Py_RETURN_NONE;\n}\n\nstatic void\nclear_line_(Line *l, index_type xnum) {\n#if BLANK_CHAR != 0\n#error This implementation is incorrect for BLANK_CHAR != 0\n#endif\n    zero_at_ptr_count(l->cpu_cells, xnum);\n    zero_at_ptr_count(l->gpu_cells, xnum);\n    l->attrs.has_dirty_text = false;\n}\n\nvoid\nlinebuf_clear_line(LineBuf *self, index_type y, bool clear_attrs) {\n#if BLANK_CHAR != 0\n#error This implementation is incorrect for BLANK_CHAR != 0\n#endif\n    index_type ym = self->line_map[y];\n    CPUCell *c = cpu_lineptr(self, ym); GPUCell *g = gpu_lineptr(self, ym);\n    zero_at_ptr_count(c, self->xnum); zero_at_ptr_count(g, self->xnum);\n    if (clear_attrs) self->line_attrs[y].val = 0;\n}\n\nstatic PyObject*\nclear_line(LineBuf *self, PyObject *val) {\n#define clear_line_doc \"clear_line(y) -> Clear the specified line\"\n    index_type y = (index_type)PyLong_AsUnsignedLong(val);\n    if (y >= self->ynum) { PyErr_SetString(PyExc_ValueError, \"Out of bounds\"); return NULL; }\n    linebuf_clear_line(self, y, true);\n    Py_RETURN_NONE;\n}\n\nvoid\nlinebuf_index(LineBuf* self, index_type top, index_type bottom) {\n    if (top >= self->ynum - 1 || bottom >= self->ynum || bottom <= top) return;\n    index_type old_top = self->line_map[top];\n    LineAttrs old_attrs = self->line_attrs[top];\n    const index_type num = bottom - top;\n    memmove(self->line_map + top, self->line_map + top + 1, sizeof(self->line_map[0]) * num);\n    memmove(self->line_attrs + top, self->line_attrs + top + 1, sizeof(self->line_attrs[0]) * num);\n    self->line_map[bottom] = old_top;\n    self->line_attrs[bottom] = old_attrs;\n}\n\nstatic PyObject*\npyw_index(LineBuf *self, PyObject *args) {\n#define index_doc \"index(top, bottom) -> Scroll all lines in the range [top, bottom] by one upwards. After scrolling, bottom will be top.\"\n    unsigned int top, bottom;\n    if (!PyArg_ParseTuple(args, \"II\", &top, &bottom)) return NULL;\n    linebuf_index(self, top, bottom);\n    Py_RETURN_NONE;\n}\n\nvoid\nlinebuf_reverse_index(LineBuf *self, index_type top, index_type bottom) {\n    if (top >= self->ynum - 1 || bottom >= self->ynum || bottom <= top) return;\n    index_type old_bottom = self->line_map[bottom];\n    LineAttrs old_attrs = self->line_attrs[bottom];\n    for (index_type i = bottom; i > top; i--) {\n        self->line_map[i] = self->line_map[i - 1];\n        self->line_attrs[i] = self->line_attrs[i - 1];\n    }\n    self->line_map[top] = old_bottom;\n    self->line_attrs[top] = old_attrs;\n}\n\nstatic PyObject*\nreverse_index(LineBuf *self, PyObject *args) {\n#define reverse_index_doc \"reverse_index(top, bottom) -> Scroll all lines in the range [top, bottom] by one down. After scrolling, top will be bottom.\"\n    unsigned int top, bottom;\n    if (!PyArg_ParseTuple(args, \"II\", &top, &bottom)) return NULL;\n    linebuf_reverse_index(self, top, bottom);\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\nis_continued(LineBuf *self, PyObject *val) {\n#define is_continued_doc \"is_continued(y) -> Whether the line y is continued or not\"\n    unsigned long y = PyLong_AsUnsignedLong(val);\n    if (y >= self->ynum) { PyErr_SetString(PyExc_ValueError, \"Out of bounds.\"); return NULL; }\n    if (y > 0 && linebuf_line_ends_with_continuation(self, y-1)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nvoid\nlinebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom) {\n    index_type i;\n    if (y >= self->ynum || y > bottom || bottom >= self->ynum) return;\n    index_type ylimit = bottom + 1;\n    if (ylimit < y || (num = MIN(ylimit - y, num)) < 1) return;\n    const size_t scratch_sz = sizeof(self->scratch[0]) * num;\n    memcpy(self->scratch, self->line_map + ylimit - num, scratch_sz);\n    for (i = ylimit - 1; i >= y + num; i--) {\n        self->line_map[i] = self->line_map[i - num];\n        self->line_attrs[i] = self->line_attrs[i - num];\n    }\n    memcpy(self->line_map + y, self->scratch, scratch_sz);\n    Line l;\n    for (i = y; i < y + num; i++) {\n        init_line(self, &l, self->line_map[i]);\n        clear_line_(&l, self->xnum);\n        self->line_attrs[i].val = 0;\n    }\n}\n\nstatic PyObject*\ninsert_lines(LineBuf *self, PyObject *args) {\n#define insert_lines_doc \"insert_lines(num, y, bottom) -> Insert num blank lines at y, only changing lines in the range [y, bottom].\"\n    unsigned int y, num, bottom;\n    if (!PyArg_ParseTuple(args, \"III\", &num, &y, &bottom)) return NULL;\n    linebuf_insert_lines(self, num, y, bottom);\n    Py_RETURN_NONE;\n}\n\nvoid\nlinebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom) {\n    index_type i;\n    index_type ylimit = bottom + 1;\n    num = MIN(bottom + 1 - y, num);\n    if (y >= self->ynum || y > bottom || bottom >= self->ynum || num < 1) return;\n    const size_t scratch_sz = sizeof(self->scratch[0]) * num;\n    memcpy(self->scratch, self->line_map + y, scratch_sz);\n    for (i = y; i < ylimit && i + num < self->ynum; i++) {\n        self->line_map[i] = self->line_map[i + num];\n        self->line_attrs[i] = self->line_attrs[i + num];\n    }\n    memcpy(self->line_map + ylimit - num, self->scratch, scratch_sz);\n    Line l;\n    for (i = ylimit - num; i < ylimit; i++) {\n        init_line(self, &l, self->line_map[i]);\n        clear_line_(&l, self->xnum);\n        self->line_attrs[i].val = 0;\n    }\n}\n\nstatic PyObject*\ndelete_lines(LineBuf *self, PyObject *args) {\n#define delete_lines_doc \"delete_lines(num, y, bottom) -> Delete num lines at y, only changing lines in the range [y, bottom].\"\n    unsigned int y, num, bottom;\n    if (!PyArg_ParseTuple(args, \"III\", &num, &y, &bottom)) return NULL;\n    linebuf_delete_lines(self, num, y, bottom);\n    Py_RETURN_NONE;\n}\n\nvoid\nlinebuf_copy_line_to(LineBuf *self, Line *line, index_type where) {\n    init_line(self, self->line, self->line_map[where]);\n    copy_line(line, self->line);\n    self->line_attrs[where] = line->attrs;\n    self->line_attrs[where].has_dirty_text = true;\n}\n\nstatic PyObject*\nas_ansi(LineBuf *self, PyObject *callback) {\n#define as_ansi_doc \"as_ansi(callback) -> The contents of this buffer as ANSI escaped text. callback is called with each successive line.\"\n    Line l = {.xnum=self->xnum, .text_cache=self->text_cache};\n    // remove trailing empty lines\n    index_type ylimit = self->ynum - 1;\n    ANSIBuf output = {0}; ANSILineState s = {.output_buf=&output};\n    do {\n        init_line(self, &l, self->line_map[ylimit]);\n        output.len = 0;\n        line_as_ansi(&l, &s, 0, l.xnum, 0, true);\n        if (output.len) break;\n        ylimit--;\n    } while(ylimit > 0);\n\n    for(index_type i = 0; i <= ylimit; i++) {\n        bool output_newline = !linebuf_line_ends_with_continuation(self, i);\n        output.len = 0;\n        init_line(self, &l, self->line_map[i]);\n        line_as_ansi(&l, &s, 0, l.xnum, 0, true);\n        if (output_newline) {\n            ensure_space_for(&output, buf, Py_UCS4, output.len + 1, capacity, 2048, false);\n            output.buf[output.len++] = 10; // 10 = \\n\n        }\n        PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output.buf, output.len);\n        if (ans == NULL) { PyErr_NoMemory(); goto end; }\n        PyObject *ret = PyObject_CallFunctionObjArgs(callback, ans, NULL);\n        Py_CLEAR(ans);\n        if (ret == NULL) goto end;\n        Py_CLEAR(ret);\n    }\nend:\n    free(output.buf);\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic Line*\nget_line(void *x, int y) {\n    LineBuf *self = (LineBuf*)x;\n    linebuf_init_line(self, MAX(0, y));\n    return self->line;\n}\n\nstatic PyObject*\nas_text(LineBuf *self, PyObject *args) {\n    ANSIBuf output = {0};\n    PyObject* ans = as_text_generic(args, self, get_line, self->ynum, &output, false);\n    free(output.buf);\n    return ans;\n}\n\n\nstatic PyObject*\n__str__(LineBuf *self) {\n    RAII_PyObject(lines, PyTuple_New(self->ynum));\n    RAII_ANSIBuf(buf);\n    if (lines == NULL) return PyErr_NoMemory();\n    for (index_type i = 0; i < self->ynum; i++) {\n        init_line(self, self->line, self->line_map[i]);\n        PyObject *t = line_as_unicode(self->line, false, &buf);\n        if (t == NULL) return NULL;\n        PyTuple_SET_ITEM(lines, i, t);\n    }\n    RAII_PyObject(sep, PyUnicode_FromString(\"\\n\"));\n    return PyUnicode_Join(sep, lines);\n}\n\n// Boilerplate {{{\nstatic PyObject*\ncopy_old(LineBuf *self, PyObject *y);\n#define copy_old_doc \"Copy the contents of the specified LineBuf to this LineBuf. Both must have the same number of columns, but the number of lines can be different, in which case the bottom lines are copied.\"\n\nstatic PyObject*\nrewrap(LineBuf *self, PyObject *args);\n#define rewrap_doc \"rewrap(new_screen) -> Fill up new screen (which can have different size to this screen) with as much of the contents of this screen as will fit. Return lines that overflow.\"\n\nstatic PyMethodDef methods[] = {\n    METHOD(line, METH_O)\n    METHOD(clear_line, METH_O)\n    METHOD(copy_old, METH_O)\n    METHOD(copy_line_to, METH_VARARGS)\n    METHOD(create_line_copy, METH_O)\n    METHOD(rewrap, METH_VARARGS)\n    METHOD(clear, METH_NOARGS)\n    METHOD(as_ansi, METH_O)\n    METHODB(as_text, METH_VARARGS),\n    METHOD(set_attribute, METH_VARARGS)\n    METHOD(set_continued, METH_VARARGS)\n    METHOD(dirty_lines, METH_NOARGS)\n    {\"index\", (PyCFunction)pyw_index, METH_VARARGS, NULL},\n    METHOD(reverse_index, METH_VARARGS)\n    METHOD(insert_lines, METH_VARARGS)\n    METHOD(delete_lines, METH_VARARGS)\n    METHOD(is_continued, METH_O)\n    {NULL, NULL, 0, NULL}  /* Sentinel */\n};\n\nstatic PyMemberDef members[] = {\n    {\"xnum\", T_UINT, offsetof(LineBuf, xnum), READONLY, \"xnum\"},\n    {\"ynum\", T_UINT, offsetof(LineBuf, ynum), READONLY, \"ynum\"},\n    {NULL}  /* Sentinel */\n};\n\nPyTypeObject LineBuf_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.LineBuf\",\n    .tp_basicsize = sizeof(LineBuf),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Line buffers\",\n    .tp_methods = methods,\n    .tp_members = members,\n    .tp_str = (reprfunc)__str__,\n    .tp_new = new_linebuf_object\n};\n\nINIT_TYPE(LineBuf)\n// }}}\n\nstatic PyObject*\ncopy_old(LineBuf *self, PyObject *y) {\n    if (!PyObject_TypeCheck(y, &LineBuf_Type)) { PyErr_SetString(PyExc_TypeError, \"Not a LineBuf object\"); return NULL; }\n    LineBuf *other = (LineBuf*)y;\n    if (other->xnum != self->xnum) { PyErr_SetString(PyExc_ValueError, \"LineBuf has a different number of columns\"); return NULL; }\n    Line sl = {.text_cache=self->text_cache}, ol = {.text_cache=self->text_cache};\n    sl.xnum = self->xnum; ol.xnum = other->xnum;\n\n    for (index_type i = 0; i < MIN(self->ynum, other->ynum); i++) {\n        index_type s = self->ynum - 1 - i, o = other->ynum - 1 - i;\n        self->line_attrs[s] = other->line_attrs[o];\n        s = self->line_map[s]; o = other->line_map[o];\n        init_line(self, &sl, s); init_line(other, &ol, o);\n        copy_line(&ol, &sl);\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nrewrap(LineBuf *self, PyObject *args) {\n    unsigned int lines, columns;\n    if (!PyArg_ParseTuple(args, \"II\", &lines, &columns)) return NULL;\n    TrackCursor cursors[1] = {{.is_sentinel=true}};\n    ANSIBuf as_ansi_buf = {0};\n    ResizeResult r = resize_screen_buffers(self, NULL, lines, columns, &as_ansi_buf, cursors);\n    free(as_ansi_buf.buf);\n    if (!r.ok) return PyErr_NoMemory();\n    return Py_BuildValue(\"NII\", r.lb, r.num_content_lines_before, r.num_content_lines_after);\n}\n\n\nLineBuf *\nalloc_linebuf(unsigned int lines, unsigned int columns, TextCache *tc) { return alloc_linebuf_(&LineBuf_Type, lines, columns, tc); }\n"
  },
  {
    "path": "kitty/line-buf.h",
    "content": "/*\n * line-buf.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"line.h\"\n#include \"text-cache.h\"\n\ntypedef struct {\n    PyObject_HEAD\n\n    GPUCell *gpu_cell_buf;\n    CPUCell *cpu_cell_buf;\n    index_type xnum, ynum, *line_map, *scratch;\n    LineAttrs *line_attrs;\n    Line *line;\n    TextCache *text_cache;\n} LineBuf;\n\n\nLineBuf* alloc_linebuf(unsigned int, unsigned int, TextCache*);\n"
  },
  {
    "path": "kitty/line.c",
    "content": "/*\n * line.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n\n#include \"state.h\"\n#include \"unicode-data.h\"\n#include \"lineops.h\"\n#include \"charsets.h\"\n#include \"control-codes.h\"\n\nextern PyTypeObject Cursor_Type;\nstatic_assert(sizeof(char_type) == sizeof(Py_UCS4), \"Need to perform conversion to Py_UCS4\");\n\nstatic void\ndealloc(Line* self) {\n    if (self->needs_free) {\n        PyMem_Free(self->cpu_cells);\n        PyMem_Free(self->gpu_cells);\n    }\n    tc_decref(self->text_cache);\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic unsigned\nnonnegative_integer_as_utf32(unsigned num, ANSIBuf *output) {\n    unsigned num_digits = 0;\n    if (!num) num_digits = 1;\n    else {\n        unsigned temp = num;\n        while (temp > 0) {\n            temp /= 10;\n            num_digits++;\n        }\n    }\n    ensure_space_for(output, buf, output->buf[0], output->len + num_digits, capacity, 2048, false);\n    if (!num) output->buf[output->len++] = '0';\n    else {\n        char_type *result = output->buf + output->len;\n        unsigned i = num_digits - 1;\n        do {\n            uint32_t digit = num % 10;\n            result[i--] = '0' + digit;\n            num /= 10;\n            output->len++;\n        } while (num > 0);\n    }\n    return num_digits;\n}\n\nstatic void\nensure_space_in_ansi_output_buf(ANSILineState *s, size_t extra) {\n    ensure_space_for(s->output_buf, buf, s->output_buf->buf[0], s->output_buf->len + extra, capacity, 2048, false);\n}\n\nstatic unsigned\nwrite_multicell_ansi_prefix(ANSILineState *s, const CPUCell *mcd) {\n    ensure_space_in_ansi_output_buf(s, 128);\n    s->current_multicell_state = mcd;\n    s->escape_code_written = true;\n    unsigned pos = s->output_buf->len;\n#define w(x) s->output_buf->buf[s->output_buf->len++] = x\n    w(0x1b); w(']');\n    for (unsigned i = 0; i < sizeof(xstr(TEXT_SIZE_CODE)) - 1; i++) w(xstr(TEXT_SIZE_CODE)[i]);\n    w(';');\n    if (!mcd->natural_width) {\n        w('w'); w('='); nonnegative_integer_as_utf32(mcd->width, s->output_buf); w(':');\n    }\n    if (mcd->scale > 1) {\n        w('s'); w('='); nonnegative_integer_as_utf32(mcd->scale, s->output_buf); w(':');\n    }\n    if (mcd->subscale_n) {\n        w('n'); w('='); nonnegative_integer_as_utf32(mcd->subscale_n, s->output_buf); w(':');\n    }\n    if (mcd->subscale_d) {\n        w('d'); w('='); nonnegative_integer_as_utf32(mcd->subscale_d, s->output_buf); w(':');\n    }\n    if (mcd->valign) {\n        w('v'); w('='); nonnegative_integer_as_utf32(mcd->valign, s->output_buf); w(':');\n    }\n    if (mcd->halign) {\n        w('h'); w('='); nonnegative_integer_as_utf32(mcd->halign, s->output_buf); w(':');\n    }\n    if (s->output_buf->buf[s->output_buf->len - 1] == ':') s->output_buf->len--;\n    w(';');\n#undef w\n    return s->output_buf->len - pos;\n}\n\nstatic void\nclose_multicell(ANSILineState *s) {\n    if (s->current_multicell_state) {\n        ensure_space_in_ansi_output_buf(s, 1);\n        s->output_buf->buf[s->output_buf->len++] = '\\a';\n        s->current_multicell_state = NULL;\n    }\n}\n\nstatic void\nstart_multicell_if_needed(ANSILineState *s, const CPUCell *c) {\n    if (!c->natural_width || c->scale > 1 || c->subscale_n || c->subscale_d || c->valign || c->halign) write_multicell_ansi_prefix(s, c);\n}\n\nstatic bool\nmulticell_is_continuation_of_previous(const CPUCell *prev, const CPUCell *curr) {\n    if (prev->scale != curr->scale || prev->subscale_n != curr->subscale_n || prev->subscale_d != curr->subscale_d || prev->valign != curr->valign || prev->halign != curr->halign) return false;\n    if (prev->natural_width) return curr->natural_width;\n    return prev->width == curr->width && !curr->natural_width;\n}\n\nstatic index_type\ntext_in_cell_ansi(ANSILineState *s, const CPUCell *c, TextCache *tc, bool skip_multiline_non_zero_lines) {\n    index_type num_cells_to_skip_for_tab = 0;\n    if (c->is_multicell) {\n        if (c->x || (skip_multiline_non_zero_lines && c->y)) return num_cells_to_skip_for_tab;\n        if (s->current_multicell_state) {\n            if (!multicell_is_continuation_of_previous(s->current_multicell_state, c)) {\n                close_multicell(s);\n                start_multicell_if_needed(s, c);\n            }\n        } else start_multicell_if_needed(s, c);\n    } else close_multicell(s);\n\n    size_t pos = s->output_buf->len;\n    if (c->ch_is_idx) {\n        tc_chars_at_index_ansi(tc, c->ch_or_idx, s->output_buf);\n    } else {\n        ensure_space_in_ansi_output_buf(s, 2);\n        s->output_buf->buf[s->output_buf->len++] = c->ch_or_idx;\n    }\n    if (s->output_buf->len > pos) {\n        switch (s->output_buf->buf[pos]) {\n            case 0: s->output_buf->buf[pos] = ' '; break;\n            case '\\t': {\n                index_type n = s->output_buf->len - pos;\n                if (n > 1) {\n                    num_cells_to_skip_for_tab = s->output_buf->buf[s->output_buf->len - n + 1];\n                    s->output_buf->len -= n - 1;\n                }\n            } break;\n        }\n    }\n    return num_cells_to_skip_for_tab;\n}\n\n\nunsigned int\nline_length(Line *self) {\n    index_type last = self->xnum - 1;\n    for (index_type i = 0; i < self->xnum; i++) {\n        if (!cell_is_char(self->cpu_cells + last - i, BLANK_CHAR)) return self->xnum - i;\n    }\n    return 0;\n}\n\n// URL detection {{{\n\nstatic bool\nis_hostname_char(char_type ch) {\n    return ch == '[' || ch == ']' || is_url_char(ch);\n}\n\nstatic bool\nis_hostname_lc(const ListOfChars *lc) {\n    for (size_t i = 0; i < lc->count; i++) if (!is_hostname_char(lc->chars[i])) return false;\n    return true;\n}\n\nstatic bool\nis_url_lc(const ListOfChars *lc) {\n    for (size_t i = 0; i < lc->count; i++) if (!is_url_char(lc->chars[i])) return false;\n    return true;\n}\n\nindex_type\nnext_char_pos(const Line *self, index_type x, index_type num) {\n    const CPUCell *ans = self->cpu_cells + x, *limit = self->cpu_cells + self->xnum;\n    while (num-- && ans < limit) ans += ans->is_multicell ? mcd_x_limit(ans) - ans->x : 1;\n    return ans - self->cpu_cells;\n}\n\nindex_type\nprev_char_pos(const Line *self, index_type x, index_type num) {\n    const CPUCell *ans = self->cpu_cells + x, *limit = self->cpu_cells - 1;\n    if (ans->is_multicell) ans -= ans->x;\n    while (num-- && --ans > limit) if (ans->is_multicell) ans -= ans->x;\n    return ans > limit ? (index_type)(ans - self->cpu_cells) : self->xnum;\n}\n\n\nstatic index_type\nfind_colon_slash(Line *self, index_type x, index_type limit, ListOfChars *lc, index_type scale) {\n    // Find :// at or before x\n    index_type pos = MIN(x, self->xnum - 1);\n    enum URL_PARSER_STATES {ANY, FIRST_SLASH, SECOND_SLASH};\n    enum URL_PARSER_STATES state = ANY;\n    limit = MAX(2u, limit);\n    if (pos < limit) return 0;\n    const CPUCell *c = self->cpu_cells + pos;\n    index_type n;\n#define next_char_is(num, ch) ((n = next_char_pos(self, pos, num)) < self->xnum && cell_is_char(self->cpu_cells + n, ch) && cell_scale(self->cpu_cells + n) == scale)\n    if (cell_is_char(c, ':')) {\n        if (next_char_is(1, '/') && next_char_is(2, '/')) state = SECOND_SLASH;\n    } else if (cell_is_char(c, '/')) {\n        if (next_char_is(1, '/')) state = FIRST_SLASH;\n    }\n#undef next_char_is\n\n    do {\n        text_in_cell(c, self->text_cache, lc);\n        if (!is_hostname_lc(lc)) return false;\n        switch(state) {\n            case ANY:\n                if (cell_is_char(c, '/')) state = FIRST_SLASH;\n                break;\n            case FIRST_SLASH:\n                state = cell_is_char(c, '/') ? SECOND_SLASH : ANY;\n                break;\n            case SECOND_SLASH:\n                if (cell_is_char(c, ':')) return pos;\n                state = cell_is_char(c, '/') ? SECOND_SLASH : ANY;\n                break;\n        }\n        pos = prev_char_pos(self, pos, 1);\n        if (pos >= self->xnum) break;\n        c = self->cpu_cells + pos;\n        if (cell_scale(c) != scale) break;\n    } while(pos >= limit);\n    return 0;\n}\n\nstatic bool\nprefix_matches(Line *self, index_type at, const char_type* prefix, index_type prefix_len, index_type scale) {\n    if (prefix_len > at) return false;\n    while (prefix_len--) {\n        at = prev_char_pos(self, at, 1);\n        if (at >= self->xnum || cell_scale(self->cpu_cells + at) != scale || !cell_is_char(self->cpu_cells + at, prefix[prefix_len])) return false;\n    }\n    return true;\n}\n\nstatic bool\nhas_url_prefix_at(Line *self, const index_type at, index_type *ans, index_type scale) {\n    for (size_t i = 0; i < OPT(url_prefixes.num); i++) {\n        index_type prefix_len = OPT(url_prefixes.values[i].len);\n        if (at < prefix_len) continue;\n        if (prefix_matches(self, at, OPT(url_prefixes.values[i].string), prefix_len, scale)) {\n            *ans = prev_char_pos(self, at, prefix_len);\n            if (*ans < self->xnum) return true;\n        }\n    }\n    return false;\n}\n\n#define MIN_URL_LEN 5\n\nstatic bool\nhas_url_beyond_colon_slash(Line *self, const index_type x, ListOfChars *lc, const index_type scale) {\n    unsigned num_of_slashes = 0;\n    index_type pos = x, num_chars = 0;\n    while ((pos = next_char_pos(self, pos, 1)) < self->xnum && num_chars++ < MIN_URL_LEN + 2) {\n        const CPUCell *c = self->cpu_cells + pos;\n        if (cell_scale(c) != scale) return false;\n        text_in_cell(c, self->text_cache, lc);\n        if (num_of_slashes < 3) {\n            if (!is_hostname_lc(lc)) return false;\n            if (lc->count == 1 && lc->chars[0] == '/') num_of_slashes++;\n        } else {\n            for (size_t n = 0; n < lc->count; n++) if (!is_url_char(lc->chars[n])) return false;\n        }\n    }\n    return true;\n}\n\nindex_type\nline_url_start_at(Line *self, index_type x, ListOfChars *lc) {\n    // Find the starting cell for a URL that contains the position x. A URL is defined as\n    // known-prefix://url-chars. If no URL is found self->xnum is returned.\n    if (self->cpu_cells[x].is_multicell && self->cpu_cells[x].x) x = x > self->cpu_cells[x].x ? x - self->cpu_cells[x].x : 0;\n    if (x >= self->xnum || self->xnum <= MIN_URL_LEN + 3) return self->xnum;\n    index_type ds_pos = 0, t, scale = cell_scale(self->cpu_cells + x);\n    // First look for :// ahead of x\n    ds_pos = find_colon_slash(self, x + OPT(url_prefixes).max_prefix_len + 3, x < 2 ? 0 : x - 2, lc, scale);\n    if (ds_pos != 0 && has_url_beyond_colon_slash(self, ds_pos, lc, scale)) {\n        if (has_url_prefix_at(self, ds_pos, &t, scale) && t <= x) return t;\n    }\n    ds_pos = find_colon_slash(self, x, 0, lc, scale);\n    if (ds_pos == 0 || self->xnum < ds_pos + MIN_URL_LEN + 3 || !has_url_beyond_colon_slash(self, ds_pos, lc, scale)) return self->xnum;\n    if (has_url_prefix_at(self, ds_pos, &t, scale)) return t;\n    return self->xnum;\n}\n\nstatic bool\nis_pos_ok_for_url(Line *self, index_type x, bool in_hostname, index_type last_hostname_char_pos, ListOfChars *lc) {\n    if (x >= self->xnum) return false;\n    text_in_cell(self->cpu_cells + x, self->text_cache, lc);\n    if (in_hostname && x <= last_hostname_char_pos) return is_hostname_lc(lc);\n    return is_url_lc(lc);\n}\n\nindex_type\nline_url_end_at(Line *self, index_type x, bool check_short, char_type sentinel, bool next_line_starts_with_url_chars, bool in_hostname, index_type last_hostname_char_pos, ListOfChars *lc) {\n    index_type ans = x;\n#define is_not_ok(n) ((sentinel && cell_is_char(self->cpu_cells + n, sentinel)) || !is_pos_ok_for_url(self, n, in_hostname, last_hostname_char_pos, lc))\n    if (x >= self->xnum || (check_short && self->xnum <= MIN_URL_LEN + 3) || is_not_ok(x)) return 0;\n    index_type n = ans;\n    while ((n = next_char_pos(self, ans, 1)) < self->xnum) {\n        if (is_not_ok(n)) break;\n        ans = n;\n    }\n#undef is_not_ok\n    if (next_char_pos(self, ans, 1) < self->xnum || !next_line_starts_with_url_chars) {\n        while (ans > x && !self->cpu_cells[ans].ch_is_idx && can_strip_from_end_of_url(self->cpu_cells[ans].ch_or_idx)) {\n            n = prev_char_pos(self, ans, 1);\n            if (n >= self->xnum || n < x) break;\n            ans = n;\n        }\n    }\n    return ans;\n}\n\nbool\nline_startswith_url_chars(Line *self, bool in_hostname, ListOfChars *lc) {\n    text_in_cell(self->cpu_cells, self->text_cache, lc);\n    if (in_hostname) return is_hostname_lc(lc);\n    return is_url_lc(lc);\n}\n\nindex_type\nfind_char(Line *self, index_type start, char_type ch) {\n    do {\n        if (cell_is_char(self->cpu_cells + start, ch)) return start;\n    } while ((start = next_char_pos(self, start, 1)) < self->xnum);\n    return self->xnum;\n}\n\nchar_type\nget_url_sentinel(Line *line, index_type url_start) {\n    char_type before = 0, sentinel;\n    if (url_start > 0 && url_start < line->xnum) {\n        index_type n = prev_char_pos(line, url_start, 1);\n        if (n < line->xnum) before = cell_first_char(line->cpu_cells + n, line->text_cache);\n    }\n    switch(before) {\n        case '\"':\n        case '\\'':\n        case '*':\n            sentinel = before; break;\n        case '(':\n            sentinel = ')'; break;\n        case '[':\n            sentinel = ']'; break;\n        case '{':\n            sentinel = '}'; break;\n        case '<':\n            sentinel = '>'; break;\n        default:\n            sentinel = 0; break;\n    }\n    return sentinel;\n}\n\n\n\nstatic PyObject*\nurl_start_at(Line *self, PyObject *x) {\n#define url_start_at_doc \"url_start_at(x) -> Return the start cell number for a URL containing x or self->xnum if not found\"\n    RAII_ListOfChars(lc);\n    return PyLong_FromUnsignedLong((unsigned long)line_url_start_at(self, PyLong_AsUnsignedLong(x), &lc));\n}\n\nstatic PyObject*\nurl_end_at(Line *self, PyObject *args) {\n#define url_end_at_doc \"url_end_at(x) -> Return the end cell number for a URL containing x or 0 if not found\"\n    unsigned int x, sentinel = 0;\n    int next_line_starts_with_url_chars = 0;\n    if (!PyArg_ParseTuple(args, \"I|Ip\", &x, &sentinel, &next_line_starts_with_url_chars)) return NULL;\n    RAII_ListOfChars(lc);\n    return PyLong_FromUnsignedLong((unsigned long)line_url_end_at(self, x, true, sentinel, next_line_starts_with_url_chars, false, self->xnum, &lc));\n}\n\n// }}}\n\nstatic PyObject*\ntext_at(Line* self, Py_ssize_t xval) {\n#define text_at_doc \"[x] -> Return the text in the specified cell\"\n    if ((unsigned)xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, \"Column number out of bounds\"); return NULL; }\n    const CPUCell *cell = self->cpu_cells + xval;\n    if (cell->ch_is_idx) {\n        RAII_ListOfChars(lc);\n        tc_chars_at_index(self->text_cache, cell->ch_or_idx, &lc);\n        if (cell->is_multicell) {\n            if (cell->x || cell->y || !lc.count) return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, lc.chars, 0);\n            return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, lc.chars + 1, lc.count - 1);\n        }\n        return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, lc.chars, lc.count);\n    }\n    Py_UCS4 ch = cell->ch_or_idx;\n    return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, &ch, 1);\n}\n\nsize_t\ncell_as_unicode_for_fallback(const ListOfChars *lc, Py_UCS4 *buf, size_t sz) {\n    size_t n = 1;\n    buf[0] = lc->chars[0] ? lc->chars[0] : ' ';\n    if (buf[0] != '\\t') {\n        for (unsigned i = 1; i < lc->count && n < sz; i++) {\n            if (lc->chars[i] != VS15 && lc->chars[i] != VS16) buf[n++] = lc->chars[i];\n        }\n    } else buf[0] = ' ';\n    return n;\n}\n\nsize_t\ncell_as_utf8_for_fallback(const ListOfChars *lc, char *buf, size_t sz) {\n    char_type ch = lc->chars[0] ? lc->chars[0] : ' ';\n    bool include_cc = true;\n    if (ch == '\\t') { ch = ' '; include_cc = false; }\n    size_t n = encode_utf8(ch, buf);\n    if (include_cc) {\n        for (unsigned i = 1; i < lc->count && sz > n + 4; i++) {\n            char_type ch = lc->chars[i];\n            if (ch != VS15 && ch != VS16) n += encode_utf8(ch, buf + n);\n        }\n    }\n    buf[n] = 0;\n    return n;\n}\n\nbool\nunicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const bool add_trailing_newline, const bool skip_zero_cells, bool skip_multiline_non_zero_lines, ANSIBuf *buf) {\n    static const size_t initial_cap = 4096;\n    ListOfChars lc;\n    if (!buf->buf) {\n        buf->buf = malloc(initial_cap * sizeof(buf->buf[0]));\n        if (!buf->buf) return false;\n        buf->capacity = initial_cap;\n    }\n    for (index_type i = start; i < limit; i++) {\n        lc.chars = buf->buf + buf->len; lc.capacity = buf->capacity - buf->len;\n        while (!text_in_cell_without_alloc(self->cpu_cells + i, self->text_cache, &lc)) {\n            size_t ns = MAX(initial_cap, 2 * buf->capacity);\n            char_type *np = realloc(buf->buf, ns);\n            if (!np) return false;\n            buf->capacity = ns; buf->buf = np;\n            lc.chars = buf->buf + buf->len; lc.capacity = buf->capacity - buf->len;\n        }\n        if (self->cpu_cells[i].is_multicell && (self->cpu_cells[i].x || (skip_multiline_non_zero_lines && self->cpu_cells[i].y))) continue;\n        if (!lc.chars[0]) {\n            if (skip_zero_cells) continue;\n            lc.chars[0] = ' ';\n        }\n        if (lc.chars[0] == '\\t') {\n            buf->len++;\n            unsigned num_cells_to_skip_for_tab = lc.count > 1 ? lc.chars[1] : 0;\n            while (num_cells_to_skip_for_tab && i + 1 < limit && cell_is_char(self->cpu_cells+i+1, ' ')) {\n                i++;\n                num_cells_to_skip_for_tab--;\n            }\n        } else buf->len += include_cc ? lc.count : 1;\n    }\n    if (add_trailing_newline && !self->cpu_cells[self->xnum-1].next_char_was_wrapped && buf->len < buf->capacity) buf->buf[buf->len++] = '\\n';\n    return true;\n}\n\nPyObject *\nline_as_unicode(Line* self, bool skip_zero_cells, ANSIBuf *buf) {\n    size_t before = buf->len;\n    if (!unicode_in_range(self, 0, xlimit_for_line(self), true, false, skip_zero_cells, true, buf)) return PyErr_NoMemory();\n    PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf->buf + before, buf->len - before);\n    buf->len = before;\n    return ans;\n}\n\nstatic PyObject*\nsprite_at(Line* self, PyObject *x) {\n#define sprite_at_doc \"[x] -> Return the sprite in the specified cell\"\n    unsigned long xval = PyLong_AsUnsignedLong(x);\n    if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, \"Column number out of bounds\"); return NULL; }\n    GPUCell *c = self->gpu_cells + xval;\n    return Py_BuildValue(\"I\", (unsigned int)c->sprite_idx);\n}\n\nstatic void\nwrite_sgr(const char *val, ANSIBuf *output) {\n#define W(c) output->buf[output->len++] = c\n    W(0x1b); W('[');\n    for (size_t i = 0; val[i] != 0 && i < 122; i++) W(val[i]);\n    W('m');\n#undef W\n}\n\nstatic void\nwrite_hyperlink(hyperlink_id_type hid, ANSIBuf *output) {\n#define W(c) output->buf[output->len++] = c\n    const char *key = hid ? get_hyperlink_for_id(output->hyperlink_pool, hid, false) : NULL;\n    if (!key) hid = 0;\n    output->active_hyperlink_id = hid;\n    W(0x1b); W(']'); W('8');\n    if (!hid) {\n        W(';'); W(';');\n    } else {\n        const char* partition = strstr(key, \":\");\n        W(';');\n        if (partition != key) {\n            W('i'); W('d'); W('=');\n            while (key != partition) W(*(key++));\n        }\n        W(';');\n        while(*(++partition))  W(*partition);\n    }\n    W(0x1b); W('\\\\');\n#undef W\n}\n\nstatic void\nwrite_mark(const char *mark, ANSIBuf *output) {\n#define W(c) output->buf[output->len++] = c\n    W(0x1b); W(']'); W('1'); W('3'); W('3'); W(';');\n    for (size_t i = 0; mark[i] != 0 && i < 32; i++) W(mark[i]);\n    W(0x1b); W('\\\\');\n#undef W\n\n}\n\nstatic void\nwrite_sgr_to_ansi_buf(ANSILineState *s, const char *val) {\n    close_multicell(s);\n    ensure_space_in_ansi_output_buf(s, 128);\n    s->escape_code_written = true;\n    write_sgr(val, s->output_buf);\n}\n\nstatic void\nwrite_ch_to_ansi_buf(ANSILineState *s, char_type ch) {\n    close_multicell(s);\n    ensure_space_in_ansi_output_buf(s, 1);\n    s->output_buf->buf[s->output_buf->len++] = ch;\n}\n\nstatic void\nwrite_hyperlink_to_ansi_buf(ANSILineState *s, hyperlink_id_type hid) {\n    close_multicell(s);\n    ensure_space_in_ansi_output_buf(s, 2256);\n    s->escape_code_written = true;\n    write_hyperlink(hid, s->output_buf);\n}\n\nstatic void\nwrite_mark_to_ansi_buf(ANSILineState *s, const char *m) {\n    close_multicell(s);\n    ensure_space_in_ansi_output_buf(s, 64);\n    s->escape_code_written = true;\n    write_mark(m, s->output_buf);\n}\n\nbool\nline_as_ansi(Line *self, ANSILineState *s, index_type start_at, index_type stop_before, char_type prefix_char, bool skip_multiline_non_zero_lines) {\n    s->limit = MIN(stop_before, xlimit_for_line(self));\n    s->current_multicell_state = NULL;\n    s->escape_code_written = false;\n    if (prefix_char) write_ch_to_ansi_buf(s, prefix_char);\n\n    if (start_at == 0) {\n        switch (self->attrs.prompt_kind) {\n            case UNKNOWN_PROMPT_KIND:\n                break;\n            case PROMPT_START: write_mark_to_ansi_buf(s, \"A\"); break;\n            case SECONDARY_PROMPT: write_mark_to_ansi_buf(s, \"A;k=s\"); break;\n            case OUTPUT_START: write_mark_to_ansi_buf(s, \"C\"); break;\n        }\n    }\n    if (s->limit <= start_at) {\n        if (s->output_buf->active_hyperlink_id && start_at == 0 && !self->cpu_cells[0].hyperlink_id) write_hyperlink_to_ansi_buf(s, 0);\n        return s->escape_code_written;\n    }\n\n    static const GPUCell blank_cell = { 0 };\n    GPUCell *cell;\n    if (s->prev_gpu_cell == NULL) s->prev_gpu_cell = &blank_cell;\n    const CellAttrs mask_for_sgr = {.val=SGR_MASK};\n\n#define CMP_ATTRS (cell->attrs.val & mask_for_sgr.val) != (s->prev_gpu_cell->attrs.val & mask_for_sgr.val)\n#define CMP(x) (cell->x != s->prev_gpu_cell->x)\n\n    for (s->pos=start_at; s->pos < s->limit; s->pos++) {\n        if (s->output_buf->hyperlink_pool) {\n            hyperlink_id_type hid = self->cpu_cells[s->pos].hyperlink_id;\n            if (hid != s->output_buf->active_hyperlink_id) write_hyperlink_to_ansi_buf(s, hid);\n        }\n        cell = &self->gpu_cells[s->pos];\n        if (CMP_ATTRS || CMP(fg) || CMP(bg) || CMP(decoration_fg)) {\n            const char *sgr = cell_as_sgr(cell, s->prev_gpu_cell);\n            if (*sgr) write_sgr_to_ansi_buf(s, sgr);\n        }\n\n        index_type num_cells_to_skip_for_tab = text_in_cell_ansi(\n            s, self->cpu_cells + s->pos, self->text_cache, skip_multiline_non_zero_lines);\n        s->prev_gpu_cell = cell;\n        const CPUCell *next = self->cpu_cells + s->pos + 1;\n        while (num_cells_to_skip_for_tab && s->pos + 1 < s->limit && cell_is_char(next, ' ')) {\n            num_cells_to_skip_for_tab--; s->pos++; next++;\n        }\n    }\n    close_multicell(s);\n    if (s->output_buf->active_hyperlink_id && s->limit < self->xnum && !self->cpu_cells[s->limit].hyperlink_id)\n        write_hyperlink_to_ansi_buf(s, 0);\n    return s->escape_code_written;\n#undef CMP_ATTRS\n#undef CMP\n}\n\nstatic PyObject*\nas_ansi(Line* self, PyObject *a UNUSED) {\n#define as_ansi_doc \"Return the line's contents with ANSI (SGR) escape codes for formatting\"\n    ANSIBuf output = {0}; ANSILineState s = {.output_buf=&output};\n    line_as_ansi(self, &s, 0, self->xnum, 0, true);\n    PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output.buf, output.len);\n    free(output.buf);\n    return ans;\n}\n\nstatic PyObject*\nlast_char_has_wrapped_flag(Line* self, PyObject *a UNUSED) {\n#define last_char_has_wrapped_flag_doc \"Return True if the last cell of this line has the wrapped flags set\"\n    if (self->cpu_cells[self->xnum - 1].next_char_was_wrapped) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nset_wrapped_flag(Line* self, PyObject *is_wrapped) {\n    self->cpu_cells[self->xnum-1].next_char_was_wrapped = PyObject_IsTrue(is_wrapped);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\n__repr__(Line* self) {\n    RAII_ANSIBuf(buf);\n    RAII_PyObject(s, line_as_unicode(self, false, &buf));\n    if (s != NULL) return PyObject_Repr(s);\n    return NULL;\n}\n\nstatic PyObject*\n__str__(Line* self) {\n    RAII_ANSIBuf(buf);\n    return line_as_unicode(self, false, &buf);\n}\n\n\nstatic PyObject*\nwidth(Line *self, PyObject *val) {\n#define width_doc \"width(x) -> the width of the character at x\"\n    unsigned long x = PyLong_AsUnsignedLong(val);\n    if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, \"Out of bounds\"); return NULL; }\n    const CPUCell *c = self->cpu_cells + x;\n    if (!cell_has_text(c)) return 0;\n    unsigned long ans = 1;\n    if (c->is_multicell) ans = c->x || c->y ? 0 : c->width;\n    return PyLong_FromUnsignedLong(ans);\n}\n\nstatic PyObject*\nadd_combining_char(Line* self, PyObject *args) {\n#define add_combining_char_doc \"add_combining_char(x, ch) -> Add the specified character as a combining char to the specified cell.\"\n    int new_char;\n    unsigned int x;\n    if (!PyArg_ParseTuple(args, \"IC\", &x, &new_char)) return NULL;\n    if (x >= self->xnum) {\n        PyErr_SetString(PyExc_ValueError, \"Column index out of bounds\");\n        return NULL;\n    }\n    CPUCell *cell = self->cpu_cells + x;\n    if (cell->is_multicell) { PyErr_SetString(PyExc_IndexError, \"cannot set combining char in a multicell\"); return NULL; }\n    RAII_ListOfChars(lc);\n    text_in_cell(cell, self->text_cache, &lc);\n    ensure_space_for_chars(&lc, lc.count + 1);\n    lc.chars[lc.count++] = new_char;\n    cell->ch_or_idx = tc_get_or_insert_chars(self->text_cache, &lc);\n    cell->ch_is_idx = true;\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\nset_text(Line* self, PyObject *args) {\n#define set_text_doc \"set_text(src, offset, sz, cursor) -> Set the characters and attributes from the specified text and cursor\"\n    PyObject *src;\n    Py_ssize_t offset, sz, limit;\n    Cursor *cursor;\n    int kind;\n    void *buf;\n\n    if (!PyArg_ParseTuple(args, \"UnnO!\", &src, &offset, &sz, &Cursor_Type, &cursor)) return NULL;\n    if (PyUnicode_READY(src) != 0) {\n        PyErr_NoMemory();\n        return NULL;\n    }\n    kind = PyUnicode_KIND(src);\n    buf = PyUnicode_DATA(src);\n    limit = offset + sz;\n    if (PyUnicode_GET_LENGTH(src) < limit) {\n        PyErr_SetString(PyExc_ValueError, \"Out of bounds offset/sz\");\n        return NULL;\n    }\n    CellAttrs attrs = cursor_to_attrs(cursor);\n    color_type fg = (cursor->sgr.fg & COL_MASK), bg = cursor->sgr.bg & COL_MASK;\n    color_type dfg = cursor->sgr.decoration_fg & COL_MASK;\n\n    for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) {\n        self->cpu_cells[i] = (CPUCell){0};\n        self->cpu_cells[i].ch_or_idx = PyUnicode_READ(kind, buf, offset);\n        self->gpu_cells[i].attrs = attrs;\n        self->gpu_cells[i].fg = fg;\n        self->gpu_cells[i].bg = bg;\n        self->gpu_cells[i].decoration_fg = dfg;\n    }\n\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\ncursor_from(Line* self, PyObject *args) {\n#define cursor_from_doc \"cursor_from(x, y=0) -> Create a cursor object based on the formatting attributes at the specified x position. The y value of the cursor is set as specified.\"\n    unsigned int x, y = 0;\n    Cursor* ans;\n    if (!PyArg_ParseTuple(args, \"I|I\", &x, &y)) return NULL;\n    if (x >= self->xnum) {\n        PyErr_SetString(PyExc_ValueError, \"Out of bounds x\");\n        return NULL;\n    }\n    ans = alloc_cursor();\n    if (ans == NULL) { PyErr_NoMemory(); return NULL; }\n    ans->x = x; ans->y = y;\n    attrs_to_cursor(self->gpu_cells[x].attrs, ans);\n    ans->sgr.fg = self->gpu_cells[x].fg; ans->sgr.bg = self->gpu_cells[x].bg;\n    ans->sgr.decoration_fg = self->gpu_cells[x].decoration_fg & COL_MASK;\n\n    return (PyObject*)ans;\n}\n\nvoid\nline_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch) {\n    const CPUCell cc = {.ch_or_idx=ch};\n    if (at + num > self->xnum) num = self->xnum > at ? self->xnum - at : 0;\n    memset_array(self->cpu_cells + at, cc, num);\n}\n\nstatic PyObject*\nclear_text(Line* self, PyObject *args) {\n#define clear_text_doc \"clear_text(at, num, ch=BLANK_CHAR) -> Clear characters in the specified range, preserving formatting.\"\n    unsigned int at, num;\n    int ch = BLANK_CHAR;\n    if (!PyArg_ParseTuple(args, \"II|C\", &at, &num, &ch)) return NULL;\n    line_clear_text(self, at, num, ch);\n    Py_RETURN_NONE;\n}\n\nvoid\nline_apply_cursor(Line *self, const Cursor *cursor, unsigned int at, unsigned int num, bool clear_char) {\n    GPUCell gc = cursor_as_gpu_cell(cursor);\n    if (clear_char) {\n#if BLANK_CHAR != 0\n#error This implementation is incorrect for BLANK_CHAR != 0\n#endif\n        if (at + num > self->xnum) { num = at < self->xnum ? self->xnum - at : 0; }\n        memset(self->cpu_cells + at, 0, num * sizeof(CPUCell));\n        memset_array(self->gpu_cells + at, gc, num);\n    } else {\n        for (index_type i = at; i < self->xnum && i < at + num; i++) {\n            gc.attrs.mark = self->gpu_cells[i].attrs.mark;\n            gc.sprite_idx = self->gpu_cells[i].sprite_idx;\n            memcpy(self->gpu_cells + i, &gc, sizeof(gc));\n        }\n    }\n}\n\nstatic PyObject*\napply_cursor(Line* self, PyObject *args) {\n#define apply_cursor_doc \"apply_cursor(cursor, at=0, num=1, clear_char=False) -> Apply the formatting attributes from cursor to the specified characters in this line.\"\n    Cursor* cursor;\n    unsigned int at=0, num=1;\n    int clear_char = 0;\n    if (!PyArg_ParseTuple(args, \"O!|IIp\", &Cursor_Type, &cursor, &at, &num, &clear_char)) return NULL;\n    line_apply_cursor(self, cursor, at, num, clear_char & 1);\n    Py_RETURN_NONE;\n}\n\nstatic color_type\nresolve_color(const ColorProfile *cp, color_type val, color_type defval) {\n    switch(val & 0xff) {\n        case 1:\n            return cp->color_table[(val >> 8) & 0xff];\n        case 2:\n            return val >> 8;\n        default:\n            return defval;\n    }\n}\n\nbool\ncolors_for_cell(Line *self, const ColorProfile *cp, index_type *x, color_type *fg, color_type *bg, bool *reversed) {\n    if (*x >= self->xnum) return false;\n    while (self->cpu_cells[*x].is_multicell && self->cpu_cells[*x].x && *x) (*x)--;\n    *fg = resolve_color(cp, self->gpu_cells[*x].fg, *fg);\n    *bg = resolve_color(cp, self->gpu_cells[*x].bg, *bg);\n    if (self->gpu_cells[*x].attrs.reverse) {\n        color_type t = *fg;\n        *fg = *bg;\n        *bg = t;\n        *reversed = true;\n    }\n    return true;\n}\n\nchar_type\nline_get_char(Line *self, index_type at) {\n    if (self->cpu_cells[at].ch_is_idx) {\n        RAII_ListOfChars(lc);\n        text_in_cell(self->cpu_cells + at, self->text_cache, &lc);\n        if (self->cpu_cells[at].is_multicell && (self->cpu_cells[at].x || self->cpu_cells[at].y)) return 0;\n        return lc.chars[0];\n    } else return self->cpu_cells[at].ch_or_idx;\n}\n\n\nstatic void\nline_set_char(Line *self, unsigned int at, uint32_t ch, Cursor *cursor, hyperlink_id_type hyperlink_id) {\n    GPUCell *g = self->gpu_cells + at;\n    if (cursor != NULL) {\n        g->attrs = cursor_to_attrs(cursor);\n        g->fg = cursor->sgr.fg & COL_MASK;\n        g->bg = cursor->sgr.bg & COL_MASK;\n        g->decoration_fg = cursor->sgr.decoration_fg & COL_MASK;\n    }\n    CPUCell *c = self->cpu_cells + at;\n    *c = (CPUCell){0};\n    cell_set_char(c, ch);\n    c->hyperlink_id = hyperlink_id;\n    if (OPT(underline_hyperlinks) == UNDERLINE_ALWAYS && hyperlink_id) {\n        g->decoration_fg = ((OPT(url_color) & COL_MASK) << 8) | 2;\n        g->attrs.decoration = OPT(url_style);\n    }\n}\n\nstatic PyObject*\nset_char(Line *self, PyObject *args) {\n#define set_char_doc \"set_char(at, ch, width=1, cursor=None, hyperlink_id=0) -> Set the character at the specified cell. If cursor is not None, also set attributes from that cursor.\"\n    unsigned int at, width=1;\n    int ch;\n    Cursor *cursor = NULL;\n    unsigned int hyperlink_id = 0;\n\n    if (!PyArg_ParseTuple(args, \"IC|IO!I\", &at, &ch, &width, &Cursor_Type, &cursor, &hyperlink_id)) return NULL;\n    if (at >= self->xnum) {\n        PyErr_SetString(PyExc_ValueError, \"Out of bounds\");\n        return NULL;\n    }\n    if (width != 1) {\n        PyErr_SetString(PyExc_NotImplementedError, \"TODO: Implement setting wide char\"); return NULL;\n    }\n    line_set_char(self, at, ch, cursor, hyperlink_id);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nset_attribute(Line *self, PyObject *args) {\n#define set_attribute_doc \"set_attribute(which, val) -> Set the attribute on all cells in the line.\"\n    unsigned int val;\n    char *which;\n    if (!PyArg_ParseTuple(args, \"sI\", &which, &val)) return NULL;\n    if (!set_named_attribute_on_line(self->gpu_cells, which, val, self->xnum)) {\n        PyErr_SetString(PyExc_KeyError, \"Unknown cell attribute\"); return NULL;\n    }\n    Py_RETURN_NONE;\n}\n\nstatic int\ncolor_as_sgr(char *buf, size_t sz, unsigned long val, unsigned simple_code, unsigned aix_code, unsigned complex_code) {\n    switch(val & 0xff) {\n        case 1:\n            val >>= 8;\n            if (val < 16 && simple_code) {\n                return snprintf(buf, sz, \"%lu;\", (val < 8) ? simple_code + val : aix_code + (val - 8));\n            }\n            return snprintf(buf, sz, \"%u:5:%lu;\", complex_code, val);\n        case 2:\n            return snprintf(buf, sz, \"%u:2:%lu:%lu:%lu;\", complex_code, (val >> 24) & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff);\n        default:\n            return snprintf(buf, sz, \"%u;\", complex_code + 1);  // reset\n    }\n}\n\nstatic const char*\ndecoration_as_sgr(uint8_t decoration) {\n    switch(decoration) {\n        case 1: return \"4;\";\n        case 2: return \"4:2;\";\n        case 3: return \"4:3;\";\n        case 4: return \"4:4\";\n        case 5: return \"4:5\";\n        default: return \"24;\";\n    }\n}\n\n\nconst char*\ncell_as_sgr(const GPUCell *cell, const GPUCell *prev) {\n    static char buf[128];\n#define SZ sizeof(buf) - (p - buf) - 2\n#define P(s) { size_t len = strlen(s); if (SZ > len) { memcpy(p, s, len); p += len; } }\n    char *p = buf;\n#define CA cell->attrs\n#define PA prev->attrs\n    bool intensity_differs = CA.bold != PA.bold || CA.dim != PA.dim;\n    if (intensity_differs) {\n        if (CA.bold && CA.dim) { if (!PA.bold) P(\"1;\"); if (!PA.dim) P(\"2;\"); }\n        else {\n            P(\"22;\"); if (CA.bold) P(\"1;\"); if (CA.dim) P(\"2;\");\n        }\n    }\n    if (CA.italic != PA.italic) P(CA.italic ? \"3;\" : \"23;\");\n    if (CA.reverse != PA.reverse) P(CA.reverse ? \"7;\" : \"27;\");\n    if (CA.strike != PA.strike) P(CA.strike ? \"9;\" : \"29;\");\n    if (CA.blink != PA.blink) P(CA.blink ? \"5;\" : \"25;\");\n    if (cell->fg != prev->fg) p += color_as_sgr(p, SZ, cell->fg, 30, 90, 38);\n    if (cell->bg != prev->bg) p += color_as_sgr(p, SZ, cell->bg, 40, 100, 48);\n    if (cell->decoration_fg != prev->decoration_fg) p += color_as_sgr(p, SZ, cell->decoration_fg, 0, 0, DECORATION_FG_CODE);\n    if (CA.decoration != PA.decoration) P(decoration_as_sgr(CA.decoration));\n#undef PA\n#undef CA\n#undef P\n#undef SZ\n    if (p > buf) *(p - 1) = 0;  // remove trailing semi-colon\n    *p = 0;  // ensure string is null-terminated\n    return buf;\n}\n\n\nstatic Py_ssize_t\n__len__(PyObject *self) {\n    return (Py_ssize_t)(((Line*)self)->xnum);\n}\n\nstatic int\n__eq__(Line *a, Line *b) {\n    return a->xnum == b->xnum && memcmp(a->cpu_cells, b->cpu_cells, sizeof(CPUCell) * a->xnum) == 0 && memcmp(a->gpu_cells, b->gpu_cells, sizeof(GPUCell) * a->xnum) == 0;\n}\n\nbool\nline_has_mark(Line *line, uint16_t mark) {\n    for (index_type x = 0; x < line->xnum; x++) {\n        const uint16_t m = line->gpu_cells[x].attrs.mark;\n        if (m && (!mark || mark == m)) return true;\n    }\n    return false;\n}\n\nstatic void\nreport_marker_error(PyObject *marker) {\n    if (!PyObject_HasAttrString(marker, \"error_reported\")) {\n        PyErr_Print();\n        if (PyObject_SetAttrString(marker, \"error_reported\", Py_True) != 0) PyErr_Clear();\n    } else PyErr_Clear();\n}\n\nstatic void\napply_mark(Line *line, const uint16_t mark, index_type *cell_pos, unsigned int *match_pos) {\n#define MARK { line->gpu_cells[x].attrs.mark = mark; }\n    index_type x = *cell_pos;\n    MARK;\n    (*match_pos)++;\n    RAII_ListOfChars(lc); text_in_cell(line->cpu_cells + x, line->text_cache, &lc);\n    if (lc.chars[0]) {\n        if (lc.chars[0] == '\\t') {\n            unsigned num_cells_to_skip_for_tab = lc.count > 1 ? lc.chars[1] : 0;\n            while (num_cells_to_skip_for_tab && x + 1 < line->xnum && cell_is_char(line->cpu_cells+x+1, ' ')) {\n                x++;\n                num_cells_to_skip_for_tab--;\n                MARK;\n            }\n        } else if (line->cpu_cells[x].is_multicell) {\n            *match_pos += lc.count - 1;\n            index_type x_limit = MIN(line->xnum, x + mcd_x_limit(line->cpu_cells + x));\n            for (; x < x_limit; x++) { MARK; }\n            x--;\n        } else {\n            *match_pos += lc.count - 1;\n        }\n    }\n    *cell_pos = x + 1;\n#undef MARK\n}\n\nstatic void\napply_marker(PyObject *marker, Line *line, const PyObject *text) {\n    unsigned int l=0, r=0, col=0, match_pos=0;\n    PyObject *pl = PyLong_FromVoidPtr(&l), *pr = PyLong_FromVoidPtr(&r), *pcol = PyLong_FromVoidPtr(&col);\n    if (!pl || !pr || !pcol) { PyErr_Clear(); return; }\n    PyObject *iter = PyObject_CallFunctionObjArgs(marker, text, pl, pr, pcol, NULL);\n    Py_DECREF(pl); Py_DECREF(pr); Py_DECREF(pcol);\n\n    if (iter == NULL) { report_marker_error(marker); return; }\n    PyObject *match;\n    index_type x = 0;\n    while ((match = PyIter_Next(iter)) && x < line->xnum) {\n        Py_DECREF(match);\n        while (match_pos < l && x < line->xnum) {\n            apply_mark(line, 0, &x, &match_pos);\n        }\n        uint16_t am = (col & MARK_MASK);\n        while(x < line->xnum && match_pos <= r) {\n            apply_mark(line, am, &x, &match_pos);\n        }\n\n    }\n    Py_DECREF(iter);\n    while(x < line->xnum) line->gpu_cells[x++].attrs.mark = 0;\n    if (PyErr_Occurred()) report_marker_error(marker);\n}\n\nvoid\nmark_text_in_line(PyObject *marker, Line *line, ANSIBuf *buf) {\n    if (!marker) {\n        for (index_type i = 0; i < line->xnum; i++)  line->gpu_cells[i].attrs.mark = 0;\n        return;\n    }\n    PyObject *text = line_as_unicode(line, false, buf);\n    if (PyUnicode_GET_LENGTH(text) > 0) {\n        apply_marker(marker, line, text);\n    } else {\n        for (index_type i = 0; i < line->xnum; i++)  line->gpu_cells[i].attrs.mark = 0;\n    }\n    Py_DECREF(text);\n}\n\nPyObject*\nas_text_generic(PyObject *args, void *container, get_line_func get_line, index_type lines, ANSIBuf *ansibuf, bool add_trailing_newline) {\n#define APPEND(x) { PyObject* retval = PyObject_CallFunctionObjArgs(callback, x, NULL); if (!retval) return NULL; Py_DECREF(retval); }\n#define APPEND_AND_DECREF(x) { if (x == NULL) { if (PyErr_Occurred()) return NULL; Py_RETURN_NONE; } PyObject* retval = PyObject_CallFunctionObjArgs(callback, x, NULL); Py_CLEAR(x); if (!retval) return NULL; Py_DECREF(retval); }\n    PyObject *callback;\n    int as_ansi = 0, insert_wrap_markers = 0;\n    if (!PyArg_ParseTuple(args, \"O|pp\", &callback, &as_ansi, &insert_wrap_markers)) return NULL;\n    PyObject *t = NULL;\n    RAII_PyObject(nl, PyUnicode_FromString(\"\\n\"));\n    RAII_PyObject(cr, PyUnicode_FromString(\"\\r\"));\n    RAII_PyObject(sgr_reset, PyUnicode_FromString(\"\\x1b[m\"));\n    if (nl == NULL || cr == NULL || sgr_reset == NULL) return NULL;\n    ANSILineState s = {.output_buf=ansibuf};\n    ansibuf->active_hyperlink_id = 0;\n    bool need_newline = false;\n    for (index_type y = 0; y < lines; y++) {\n        Line *line = get_line(container, y);\n        if (!line) { if (PyErr_Occurred()) return NULL; break; }\n        if (need_newline) APPEND(nl);\n        ansibuf->len = 0;\n        if (as_ansi) {\n            // less has a bug where it resets colors when it sees a \\r, so work\n            // around it by resetting SGR at the start of every line. This is\n            // pretty sad performance wise, but I guess it will remain as it\n            // makes writing pagers easier.\n            // see https://github.com/kovidgoyal/kitty/issues/2381\n            s.prev_gpu_cell = NULL;\n            line_as_ansi(line, &s, 0, line->xnum, 0, true);\n            t = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, ansibuf->buf, ansibuf->len);\n            if (t && ansibuf->len > 0) APPEND(sgr_reset);\n        } else {\n            t = line_as_unicode(line, false, ansibuf);\n        }\n        APPEND_AND_DECREF(t);\n        if (insert_wrap_markers) APPEND(cr);\n        need_newline = !line->cpu_cells[line->xnum-1].next_char_was_wrapped;\n    }\n    if (need_newline && add_trailing_newline) APPEND(nl);\n    if (ansibuf->active_hyperlink_id) {\n        ansibuf->active_hyperlink_id = 0;\n        t = PyUnicode_FromString(\"\\x1b]8;;\\x1b\\\\\");\n        APPEND_AND_DECREF(t);\n    }\n    Py_RETURN_NONE;\n#undef APPEND\n#undef APPEND_AND_DECREF\n}\n\n// Boilerplate {{{\nstatic PyObject*\ncopy_char(Line* self, PyObject *args);\n#define copy_char_doc \"copy_char(src, to, dest) -> Copy the character at src to the character dest in the line `to`\"\n\n#define hyperlink_ids_doc \"hyperlink_ids() -> Tuple of hyper link ids at every cell\"\nstatic PyObject*\nhyperlink_ids(Line *self, PyObject *args UNUSED) {\n    PyObject *ans = PyTuple_New(self->xnum);\n    for (index_type x = 0; x < self->xnum; x++) {\n        PyTuple_SET_ITEM(ans, x, PyLong_FromUnsignedLong(self->cpu_cells[x].hyperlink_id));\n    }\n    return ans;\n}\n\nstatic PyObject *\nrichcmp(PyObject *obj1, PyObject *obj2, int op);\n\n\nstatic PySequenceMethods sequence_methods = {\n    .sq_length = __len__,\n    .sq_item = (ssizeargfunc)text_at\n};\n\nstatic PyMethodDef methods[] = {\n    METHOD(add_combining_char, METH_VARARGS)\n    METHOD(set_text, METH_VARARGS)\n    METHOD(cursor_from, METH_VARARGS)\n    METHOD(apply_cursor, METH_VARARGS)\n    METHOD(clear_text, METH_VARARGS)\n    METHOD(copy_char, METH_VARARGS)\n    METHOD(set_char, METH_VARARGS)\n    METHOD(set_attribute, METH_VARARGS)\n    METHOD(as_ansi, METH_NOARGS)\n    METHOD(last_char_has_wrapped_flag, METH_NOARGS)\n    METHODB(set_wrapped_flag, METH_O),\n    METHOD(hyperlink_ids, METH_NOARGS)\n    METHOD(width, METH_O)\n    METHOD(url_start_at, METH_O)\n    METHOD(url_end_at, METH_VARARGS)\n    METHOD(sprite_at, METH_O)\n\n    {NULL}  /* Sentinel */\n};\n\nPyTypeObject Line_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.Line\",\n    .tp_basicsize = sizeof(Line),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_repr = (reprfunc)__repr__,\n    .tp_str = (reprfunc)__str__,\n    .tp_as_sequence = &sequence_methods,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_richcompare = richcmp,\n    .tp_doc = \"Lines\",\n    .tp_methods = methods,\n};\n\nLine *alloc_line(TextCache *tc) {\n    Line *ans = (Line*)Line_Type.tp_alloc(&Line_Type, 0);\n    if (ans) ans->text_cache = tc_incref(tc);\n    return ans;\n}\n\nRICHCMP(Line)\nINIT_TYPE(Line)\n// }}}\n\nstatic PyObject*\ncopy_char(Line* self, PyObject *args) {\n    unsigned int src, dest;\n    Line *to;\n    if (!PyArg_ParseTuple(args, \"IO!I\", &src, &Line_Type, &to, &dest)) return NULL;\n    if (src >= self->xnum || dest >= to->xnum) {\n        PyErr_SetString(PyExc_ValueError, \"Out of bounds\");\n        return NULL;\n    }\n    COPY_CELL(self, src, to, dest);\n    Py_RETURN_NONE;\n}\n"
  },
  {
    "path": "kitty/line.h",
    "content": "/*\n * line.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"text-cache.h\"\n\ntypedef union CellAttrs {\n    struct {\n        uint16_t decoration : 3;\n        uint16_t bold : 1;\n        uint16_t italic : 1;\n        uint16_t reverse : 1;\n        uint16_t strike : 1;\n        uint16_t dim : 1;\n        uint16_t blink: 1;\n        uint16_t mark : 2;\n        uint32_t : 21;\n    };\n    uint32_t val;\n} CellAttrs;\nstatic_assert(sizeof(CellAttrs) == sizeof(uint32_t), \"Fix the ordering of CellAttrs\");\n\n#define WIDTH_MASK (3u)\n#define DECORATION_MASK (7u)\n#define SGR_MASK (~(((CellAttrs){.mark=MARK_MASK}).val))\n#define MAX_NUM_CODEPOINTS_PER_CELL 24u\n// Text presentation selector\n#define VS15 0xfe0e\n// Emoji presentation selector\n#define VS16 0xfe0f\n\ntypedef struct {\n    color_type fg, bg, decoration_fg;\n    sprite_index sprite_idx;\n    CellAttrs attrs;\n} GPUCell;\nstatic_assert(sizeof(GPUCell) == 20, \"Fix the ordering of GPUCell\");\n\n#define SCALE_BITS 3\n#define WIDTH_BITS 3\n#define SUBSCALE_BITS 4\n#define VALIGN_BITS 2\n#define HALIGN_BITS 2\n\ntypedef union CPUCell {\n    struct {\n    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n        char_type ch_or_idx: sizeof(char_type) * 8 - 1;\n        char_type ch_is_idx: 1;\n    #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        char_type ch_is_idx: 1;\n        char_type ch_or_idx: sizeof(char_type) * 8 - 1;\n    #else\n    #error \"Unsupported endianness\"\n    #endif\n        char_type hyperlink_id: sizeof(hyperlink_id_type) * 8;\n        char_type next_char_was_wrapped : 1;\n        char_type is_multicell : 1;\n        char_type natural_width: 1;\n        char_type scale: SCALE_BITS;\n        char_type subscale_n: SUBSCALE_BITS;\n        char_type subscale_d: SUBSCALE_BITS;\n        char_type x : WIDTH_BITS + SCALE_BITS;\n        char_type y : SCALE_BITS;\n        char_type width: WIDTH_BITS;\n        char_type valign: VALIGN_BITS;\n        char_type halign: HALIGN_BITS;\n        char_type temp_flag: 1;\n        char_type : 15;\n    };\n    struct {\n        char_type ch_and_idx: sizeof(char_type) * 8;\n        char_type : 32;\n        char_type : 32;\n    };\n} CPUCell;\nstatic_assert(sizeof(CPUCell) == 12, \"Fix the ordering of CPUCell\");\n\ntypedef union LineAttrs {\n    struct {\n        uint8_t has_dirty_text : 1;\n        uint8_t has_image_placeholders : 1;\n        uint8_t prompt_kind : 2;\n        uint8_t : 4;\n    };\n    uint8_t val;\n} LineAttrs ;\nstatic_assert(sizeof(LineAttrs) == sizeof(uint8_t), \"Fix the ordering of LineAttrs\");\n\n\ntypedef struct {\n    PyObject_HEAD\n\n    GPUCell *gpu_cells;\n    CPUCell *cpu_cells;\n    index_type xnum, ynum;\n    bool needs_free;\n    LineAttrs attrs;\n    TextCache *text_cache;\n} Line;\n\ntypedef struct MultiCellCommand {\n    unsigned int width, scale, subscale_n, subscale_d, vertical_align, horizontal_align;\n    size_t payload_sz;\n} MultiCellCommand;\n\ntypedef struct ANSILineOutput {\n    const GPUCell *prev_gpu_cell;\n    const CPUCell *current_multicell_state;\n    index_type pos, limit;\n    ANSIBuf *output_buf;\n    bool escape_code_written;\n} ANSILineState;\n\nstatic inline void cleanup_ansibuf(ANSIBuf *b) { free(b->buf); zero_at_ptr(b); }\n#define RAII_ANSIBuf(name) __attribute__((cleanup(cleanup_ansibuf))) ANSIBuf name = {0}\n\nLine* alloc_line(TextCache *text_cache);\nvoid apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, int *params, unsigned int count, bool is_group);\nconst char* cell_as_sgr(const GPUCell *, const GPUCell *);\nstatic inline bool cell_has_text(const CPUCell *c) { return c->ch_and_idx != 0; }\nstatic inline void cell_set_char(CPUCell *c, char_type ch) { c->ch_and_idx = ch & 0x7fffffff; }\nstatic inline bool cell_is_char(const CPUCell *c, char_type ch) { return c->ch_and_idx == ch; }\nstatic inline index_type cell_scale(const CPUCell *c) { return c->is_multicell ? c->scale : 1; }\nstatic inline unsigned num_codepoints_in_cell(const CPUCell *c, const TextCache *tc) {\n    unsigned ans;\n    if (c->ch_is_idx) {\n        ans = tc_num_codepoints(tc, c->ch_or_idx);\n        if (c->is_multicell) ans--;\n    } else ans = c->ch_or_idx ? 1 : 0;\n    return ans;\n}\nstatic inline unsigned mcd_x_limit(const CPUCell* mcd) { return mcd->scale * mcd->width; }\n\nstatic inline void\ntext_in_cell(const CPUCell *c, const TextCache *tc, ListOfChars *ans) {\n    if (c->ch_is_idx) {\n        tc_chars_at_index(tc, c->ch_or_idx, ans);\n    } else {\n        ans->count = 1;\n        ans->chars[0] = c->ch_or_idx;\n    }\n}\n\nstatic inline bool\ntext_in_cell_without_alloc(const CPUCell *c, const TextCache *tc, ListOfChars *ans) {\n    if (c->ch_is_idx) {\n        if (!tc_chars_at_index_without_alloc(tc, c->ch_or_idx, ans)) return false;\n        return true;\n    }\n    ans->count = 1;\n    if (ans->capacity < 1) return false;\n    ans->chars[0] = c->ch_or_idx;\n    return true;\n}\n\nstatic inline void\ncell_set_chars(CPUCell *c, TextCache *tc, const ListOfChars *lc) {\n    if (lc->count <= 1) cell_set_char(c, lc->chars[0]);\n    else {\n        c->ch_or_idx = tc_get_or_insert_chars(tc,  lc);\n        c->ch_is_idx = true;\n    }\n}\n\nstatic inline char_type\ncell_first_char(const CPUCell *c, const TextCache *tc) {\n    if (c->ch_is_idx) {\n        if (c->is_multicell && (c->x || c->y)) return 0;\n        return tc_first_char_at_index(tc, c->ch_or_idx);\n    }\n    return c->ch_or_idx;\n}\n\nstatic inline CellAttrs\ncursor_to_attrs(const Cursor *c) {\n    CellAttrs ans = {\n        .decoration=c->sgr.decoration, .bold=c->sgr.bold, .italic=c->sgr.italic, .reverse=c->sgr.reverse,\n        .strike=c->sgr.strikethrough, .dim=c->sgr.dim, .blink=c->sgr.blink};\n    return ans;\n}\n\nstatic inline void\nattrs_to_cursor(const CellAttrs attrs, Cursor *c) {\n    c->sgr.decoration = attrs.decoration; c->sgr.bold = attrs.bold;  c->sgr.italic = attrs.italic;\n    c->sgr.reverse = attrs.reverse; c->sgr.strikethrough = attrs.strike; c->sgr.dim = attrs.dim;\n    c->sgr.blink = attrs.blink;\n}\n\n#define cursor_as_gpu_cell(cursor) {.attrs=cursor_to_attrs(cursor), .fg=(cursor->sgr.fg & COL_MASK), .bg=(cursor->sgr.bg & COL_MASK), .decoration_fg=cursor->sgr.decoration_fg & COL_MASK}\n\n\n"
  },
  {
    "path": "kitty/linear2srgb.glsl",
    "content": "float srgb2linear(float x) {\n    // sRGB to linear conversion\n    float lower = x / 12.92;\n    float upper = pow((x + 0.055f) / 1.055f, 2.4f);\n\n    return mix(lower, upper, step(0.04045f, x));\n}\n\nfloat linear2srgb(float x) {\n    // Linear to sRGB conversion.\n    float lower = 12.92 * x;\n    float upper = 1.055 * pow(x, 1.0f / 2.4f) - 0.055f;\n\n    return mix(lower, upper, step(0.0031308f, x));\n}\n\nvec3 linear2srgb(vec3 x) {\n    vec3 lower = 12.92 * x;\n    vec3 upper = 1.055 * pow(x, vec3(1.0f / 2.4f)) - 0.055f;\n    return mix(lower, upper, step(0.0031308f, x));\n}\n\nvec3 srgb2linear(vec3 c) {\n    return vec3(srgb2linear(c.r), srgb2linear(c.g), srgb2linear(c.b));\n}\n"
  },
  {
    "path": "kitty/lineops.h",
    "content": "/*\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"history.h\"\n#include \"line-buf.h\"\n\n#define set_attribute_on_line(cells, which, val, xnum) { \\\n    for (index_type i__ = 0; i__ < xnum; i__++) cells[i__].attrs.which = val; }\n\nstatic inline bool\nset_named_attribute_on_line(GPUCell *cells, const char* which, uint16_t val, index_type xnum) {\n    // Set a single attribute on all cells in the line\n#define s(q) if (strcmp(#q, which) == 0) { set_attribute_on_line(cells, q, val, xnum); return true; }\n    s(reverse); s(strike); s(dim); s(mark); s(bold); s(italic); s(decoration);\n    return false;\n#undef s\n}\n\n\nstatic inline void\ncopy_line(const Line *src, Line *dest) {\n    memcpy(dest->cpu_cells, src->cpu_cells, sizeof(CPUCell) * MIN(src->xnum, dest->xnum));\n    memcpy(dest->gpu_cells, src->gpu_cells, sizeof(GPUCell) * MIN(src->xnum, dest->xnum));\n}\n\nstatic inline void\nclear_chars_in_line(CPUCell *cpu_cells, GPUCell *gpu_cells, index_type xnum, char_type ch) {\n    // Clear only the char part of each cell, the rest must have been cleared by a memset or similar\n    if (ch) {\n        static const CellAttrs empty = {0};\n        const CPUCell c = {.ch_or_idx=ch};\n        for (index_type i = 0; i < xnum; i++) { cpu_cells[i] = c; gpu_cells[i].attrs = empty; }\n    }\n}\n\nstatic inline index_type\nxlimit_for_line(const Line *line) {\n    index_type xlimit = line->xnum;\n    while (xlimit > 0 && !line->cpu_cells[xlimit - 1].ch_and_idx) xlimit--;\n    return xlimit;\n}\n\nstatic inline void\nline_save_cells(Line *line, index_type start, index_type num, GPUCell *gpu_cells, CPUCell *cpu_cells) {\n    memcpy(gpu_cells + start, line->gpu_cells + start, sizeof(GPUCell) * num);\n    memcpy(cpu_cells + start, line->cpu_cells + start, sizeof(CPUCell) * num);\n}\n\nstatic inline void\nline_reset_cells(Line *line, index_type start, index_type num, GPUCell *gpu_cells, CPUCell *cpu_cells) {\n    memcpy(line->gpu_cells + start, gpu_cells + start, sizeof(GPUCell) * num);\n    memcpy(line->cpu_cells + start, cpu_cells + start, sizeof(CPUCell) * num);\n}\n\nstatic inline bool\nline_is_empty(const Line *line) {\n#if BLANK_CHAR != 0\n#error This implementation is incorrect for BLANK_CHAR != 0\n#endif\n    for (index_type i = 0; i < line->xnum; i++) if (line->cpu_cells[i].ch_and_idx) return false;\n    return true;\n}\n\ntypedef Line*(get_line_func)(void *, int);\nvoid line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch);\nvoid line_apply_cursor(Line *self, const Cursor *cursor, unsigned int at, unsigned int num, bool clear_char);\nchar_type line_get_char(Line *self, index_type at);\nindex_type line_url_start_at(Line *self, index_type x, ListOfChars *lc);\nindex_type line_url_end_at(Line *self, index_type x, bool, char_type, bool, bool, index_type, ListOfChars*);\nbool line_startswith_url_chars(Line*, bool, ListOfChars*);\nchar_type get_url_sentinel(Line *line, index_type url_start);\nindex_type find_char(Line *self, index_type start, char_type ch);\nindex_type next_char_pos(const Line *self, index_type x, index_type num);\nindex_type prev_char_pos(const Line *self, index_type x, index_type num);\nbool line_as_ansi(Line *self, ANSILineState *s, index_type start_at, index_type stop_before, char_type prefix_char, bool skip_multiline_non_zero_lines) __attribute__((nonnull));\nunsigned int line_length(Line *self);\nsize_t cell_as_unicode_for_fallback(const ListOfChars *lc, Py_UCS4 *buf, size_t sz);\nsize_t cell_as_utf8_for_fallback(const ListOfChars *lc, char *buf, size_t sz);\nbool unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const bool add_trailing_newline, const bool skip_zero_cells, bool skip_multiline_non_zero_lines, ANSIBuf*);\nPyObject* line_as_unicode(Line *, bool, ANSIBuf*);\n\nvoid linebuf_init_line(LineBuf *, index_type);\nvoid linebuf_init_line_at(LineBuf *, index_type, Line*);\nvoid linebuf_init_cells(LineBuf *lb, index_type ynum, CPUCell **c, GPUCell **g);\nCPUCell* linebuf_cpu_cells_for_line(LineBuf *lb, index_type idx);\nvoid linebuf_clear(LineBuf *, char_type ch);\nvoid linebuf_clear_lines(LineBuf *self, const Cursor *cursor, index_type start, index_type end);\nvoid linebuf_index(LineBuf* self, index_type top, index_type bottom);\nvoid linebuf_reverse_index(LineBuf *self, index_type top, index_type bottom);\nvoid linebuf_clear_line(LineBuf *self, index_type y, bool clear_attrs);\nvoid linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom);\nvoid linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom);\nvoid linebuf_copy_line_to(LineBuf *, Line *, index_type);\nvoid linebuf_mark_line_dirty(LineBuf *self, index_type y);\nvoid linebuf_clear_attrs_and_dirty(LineBuf *self, index_type y);\nvoid linebuf_mark_line_clean(LineBuf *self, index_type y);\nvoid linebuf_set_line_has_image_placeholders(LineBuf *self, index_type y, bool val);\nvoid linebuf_set_last_char_as_continuation(LineBuf *self, index_type y, bool continued);\nCPUCell* linebuf_cpu_cell_at(LineBuf *self, index_type x, index_type y);\nbool linebuf_line_ends_with_continuation(LineBuf *self, index_type y);\nvoid linebuf_refresh_sprite_positions(LineBuf *self);\nvoid historybuf_add_line(HistoryBuf *self, const Line *line, ANSIBuf*);\nbool historybuf_pop_line(HistoryBuf *, Line *);\nvoid historybuf_init_line(HistoryBuf *self, index_type num, Line *l);\nbool history_buf_endswith_wrap(HistoryBuf *self);\nCPUCell* historybuf_cpu_cells(HistoryBuf *self, index_type num);\nvoid historybuf_mark_line_clean(HistoryBuf *self, index_type y);\nvoid historybuf_mark_line_dirty(HistoryBuf *self, index_type y);\nvoid historybuf_set_line_has_image_placeholders(HistoryBuf *self, index_type y, bool val);\nvoid historybuf_refresh_sprite_positions(HistoryBuf *self);\nvoid historybuf_clear(HistoryBuf *self);\nvoid mark_text_in_line(PyObject *marker, Line *line, ANSIBuf *buf);\nbool line_has_mark(Line *, uint16_t mark);\nPyObject* as_text_generic(PyObject *args, void *container, get_line_func get_line, index_type lines, ANSIBuf *ansibuf, bool add_trailing_newline);\nbool colors_for_cell(Line *self, const ColorProfile *cp, index_type *x, color_type *fg, color_type *bg, bool *reversed);\n"
  },
  {
    "path": "kitty/logging.c",
    "content": "/*\n * logging.c\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"charsets.h\"\n#include <stdlib.h>\n#include <stdarg.h>\n#include <time.h>\n#include <sys/time.h>\n#ifdef __APPLE__\n#include <os/log.h>\n#endif\n\n\nstatic bool use_os_log = false;\n\nvoid\nlog_error(const char *fmt, ...) {\n    int n = 0;\n    va_list ar;\n    va_start(ar, fmt);\n    n = vsnprintf(NULL, 0, fmt, ar);\n    va_end(ar);\n    if (n < 0) return;\n    size_t size = 5 * (size_t)n + 8;\n    RAII_ALLOC(unsigned char, arena, calloc(size, sizeof(char)));\n    if (!arena) return;\n    va_start(ar, fmt);\n    n = vsnprintf((char*)arena, size, fmt, ar);\n    va_end(ar);\n    unsigned char *sanbuf = arena + n + 1;\n\n    char utf8buf[4];\n    START_ALLOW_CASE_RANGE\n    size_t j = 0;\n    for (unsigned char *x = arena; x < arena + n; x++) {\n        switch(*x) {\n            case C0_EXCEPT_NL_SPACE_TAB_DEL: {\n                const uint32_t ch = 0x2400 + *x;\n                const unsigned sz = encode_utf8(ch, utf8buf);\n                for (unsigned c = 0; c < sz; c++, j++) sanbuf[j] = utf8buf[c];\n            } break;\n            case 0x7f:\n                sanbuf[j++] = 0xe2; sanbuf[j++] = 0x90; sanbuf[j++] = 0xa1; // U+2421\n                break;\n            default:\n                sanbuf[j++] = *x;\n                break;\n        }\n    }\n    sanbuf[j] = 0;\n    END_ALLOW_CASE_RANGE\n\n    if (!use_os_log) {  // Apple's os_log already records timestamps\n        fprintf(stderr, \"[%.3f] \", monotonic_t_to_s_double(monotonic()));\n    }\n    // To see os_log messages from kitty, use:\n    // log show --predicate 'processImagePath contains \"kitty\" and messageType == error'\n#ifdef __APPLE__\n    if (use_os_log) os_log_error(OS_LOG_DEFAULT, \"%{public}s\", sanbuf);\n#endif\n    if (!use_os_log) fprintf(stderr, \"%s\\n\", sanbuf);\n#undef bufprint\n}\n\nstatic PyObject*\nlog_error_string(PyObject *self UNUSED, PyObject *args) {\n    const char *msg;\n    if (!PyArg_ParseTuple(args, \"s\", &msg)) return NULL;\n    log_error(\"%s\", msg);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nset_use_os_log(PyObject *self UNUSED, PyObject *args) {\n    use_os_log = PyObject_IsTrue(args) ? true : false;\n    Py_RETURN_NONE;\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(log_error_string, METH_VARARGS),\n    METHODB(set_use_os_log, METH_O),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nbool\ninit_logging(PyObject *module) {\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    return true;\n}\n"
  },
  {
    "path": "kitty/loop-utils.c",
    "content": "/*\n * loop-utils.c\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"loop-utils.h\"\n#include \"safe-wrappers.h\"\n\n#ifndef HAS_SIGNAL_FD\nstatic int signal_write_fd = -1;\n\nstatic void\nhandle_signal(int sig_num UNUSED, siginfo_t *si, void *ucontext UNUSED) {\n    int save_err = errno;\n    char *buf = (char*)si;\n    size_t sz = sizeof(siginfo_t);\n    while (signal_write_fd != -1 && sz) {\n        // as long as sz is less than PIPE_BUF write will either write all or return -1 with EAGAIN\n        // so we are guaranteed atomic writes\n        ssize_t ret = write(signal_write_fd, buf, sz);\n        if (ret <= 0) {\n            if (errno == EINTR) continue;\n            break;\n        }\n        sz -= ret;\n        buf += ret;\n    }\n    errno = save_err;\n}\n#endif\n\nstatic bool\ninit_signal_handlers(LoopData *ld) {\n    ld->signal_read_fd = -1;\n    sigemptyset(&ld->signals);\n    for (size_t i = 0; i < ld->num_handled_signals; i++) sigaddset(&ld->signals, ld->handled_signals[i]);\n#ifdef HAS_SIGNAL_FD\n    if (ld->num_handled_signals) {\n        if (sigprocmask(SIG_BLOCK, &ld->signals, NULL) == -1) return false;\n        ld->signal_read_fd = signalfd(-1, &ld->signals, SFD_NONBLOCK | SFD_CLOEXEC);\n        if (ld->signal_read_fd == -1) return false;\n    }\n#else\n    ld->signal_fds[0] = -1; ld->signal_fds[1] = -1;\n    if (ld->num_handled_signals) {\n        if (!self_pipe(ld->signal_fds, true)) return false;\n        signal_write_fd = ld->signal_fds[1];\n        ld->signal_read_fd = ld->signal_fds[0];\n        struct sigaction act = {.sa_sigaction=handle_signal, .sa_flags=SA_SIGINFO | SA_RESTART, .sa_mask = ld->signals};\n        for (size_t i = 0; i < ld->num_handled_signals; i++) { if (sigaction(ld->handled_signals[i], &act, NULL) != 0) return false; }\n    }\n#endif\n    return true;\n}\n\nbool\ninit_loop_data(LoopData *ld, ...) {\n    ld->num_handled_signals = 0;\n    va_list valist;\n    va_start(valist, ld);\n    while (true) {\n        int sig = va_arg(valist, int);\n        if (!sig) break;\n        ld->handled_signals[ld->num_handled_signals++] = sig;\n    }\n    va_end(valist);\n#ifdef HAS_EVENT_FD\n    ld->wakeup_read_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);\n    if (ld->wakeup_read_fd < 0) return false;\n#else\n    if (!self_pipe(ld->wakeup_fds, true)) return false;\n    ld->wakeup_read_fd = ld->wakeup_fds[0];\n#endif\n    return init_signal_handlers(ld);\n}\n\n#define CLOSE(which, idx) if (ld->which[idx] > -1) { safe_close(ld->which[idx], __FILE__, __LINE__); ld->which[idx] = -1; }\n\nstatic void\nremove_signal_handlers(LoopData *ld) {\n#ifndef HAS_SIGNAL_FD\n    signal_write_fd = -1;\n    CLOSE(signal_fds, 0); CLOSE(signal_fds, 1);\n#endif\n    if (ld->signal_read_fd > -1) {\n#ifdef HAS_SIGNAL_FD\n        safe_close(ld->signal_read_fd, __FILE__, __LINE__);\n        sigprocmask(SIG_UNBLOCK, &ld->signals, NULL);\n#endif\n        for (size_t i = 0; i < ld->num_handled_signals; i++) signal(ld->num_handled_signals, SIG_DFL);\n    }\n    ld->signal_read_fd = -1;\n    ld->num_handled_signals = 0;\n}\n\nvoid\nfree_loop_data(LoopData *ld) {\n#ifndef HAS_EVENT_FD\n    CLOSE(wakeup_fds, 0); CLOSE(wakeup_fds, 1);\n#endif\n#undef CLOSE\n#ifdef HAS_EVENT_FD\n    safe_close(ld->wakeup_read_fd, __FILE__, __LINE__);\n#endif\n    ld->wakeup_read_fd = -1;\n    remove_signal_handlers(ld);\n}\n\n\nvoid\nwakeup_loop(LoopData *ld, bool in_signal_handler, const char *loop_name) {\n    while(true) {\n#ifdef HAS_EVENT_FD\n        static const int64_t value = 1;\n        ssize_t ret = write(ld->wakeup_read_fd, &value, sizeof value);\n#else\n        ssize_t ret = write(ld->wakeup_fds[1], \"w\", 1);\n#endif\n        if (ret < 0) {\n            if (errno == EINTR) continue;\n            if (!in_signal_handler) log_error(\"Failed to write to %s wakeup fd with error: %s\", loop_name, strerror(errno));\n        }\n        break;\n    }\n}\n\n\nvoid\nread_signals(int fd, handle_signal_func callback, void *data) {\n#ifdef HAS_SIGNAL_FD\n    static struct signalfd_siginfo fdsi[32];\n    siginfo_t si;\n    while (true) {\n        ssize_t s = read(fd, &fdsi, sizeof(fdsi));\n        if (s < 0) {\n            if (errno == EINTR) continue;\n            if (errno == EAGAIN) break;\n            log_error(\"Call to read() from read_signals() failed with error: %s\", strerror(errno));\n            break;\n        }\n        if (s == 0) break;\n        size_t num_signals = s / sizeof(struct signalfd_siginfo);\n        if (num_signals == 0 || num_signals * sizeof(struct signalfd_siginfo) != (size_t)s) {\n            log_error(\"Incomplete signal read from signalfd\");\n            break;\n        }\n        for (size_t i = 0; i < num_signals; i++) {\n            si.si_signo = fdsi[i].ssi_signo;\n            si.si_code = fdsi[i].ssi_code;\n            si.si_pid = fdsi[i].ssi_pid;\n            si.si_uid = fdsi[i].ssi_uid;\n            si.si_addr = (void*)(uintptr_t)fdsi[i].ssi_addr;\n            si.si_status = fdsi[i].ssi_status;\n            si.si_value.sival_int = fdsi[i].ssi_int;\n            if (!callback(&si, data)) break;\n        }\n    }\n#else\n    static char buf[sizeof(siginfo_t) * 8];\n    static size_t buf_pos = 0;\n    while(true) {\n        ssize_t len = read(fd, buf + buf_pos, sizeof(buf) - buf_pos);\n        if (len < 0) {\n            if (errno == EINTR) continue;\n            if (errno != EWOULDBLOCK && errno != EAGAIN) log_error(\"Call to read() from read_signals() failed with error: %s\", strerror(errno));\n            break;\n        }\n        buf_pos += len;\n        bool keep_going = true;\n        while (keep_going && buf_pos >= sizeof(siginfo_t)) {\n            keep_going = callback((siginfo_t*)buf, data);\n            buf_pos -= sizeof(siginfo_t);\n            memmove(buf, buf + sizeof(siginfo_t), buf_pos);\n        }\n        if (len == 0) break;\n    }\n#endif\n}\n\nstatic LoopData python_loop_data = {0};\n\nstatic PyObject*\ninit_signal_handlers_py(PyObject *self UNUSED, PyObject *args) {\n    if (python_loop_data.num_handled_signals) { PyErr_SetString(PyExc_RuntimeError, \"signal handlers already initialized\"); return NULL; }\n#ifndef HAS_SIGNAL_FD\n    if (signal_write_fd > -1) { PyErr_SetString(PyExc_RuntimeError, \"signal handlers already initialized\"); return NULL; }\n#endif\n    for (Py_ssize_t i = 0; i < MIN(PyTuple_GET_SIZE(args), (Py_ssize_t)arraysz(python_loop_data.handled_signals)); i++) {\n        python_loop_data.handled_signals[python_loop_data.num_handled_signals++] = PyLong_AsLong(PyTuple_GET_ITEM(args, i));\n    }\n    if (!init_signal_handlers(&python_loop_data)) return PyErr_SetFromErrno(PyExc_OSError);\n#ifdef HAS_SIGNAL_FD\n    return Py_BuildValue(\"ii\", python_loop_data.signal_read_fd, -1);\n#else\n    return Py_BuildValue(\"ii\", python_loop_data.signal_fds[0], python_loop_data.signal_fds[1]);\n#endif\n}\n\nstatic PyTypeObject SigInfoType;\nstatic PyStructSequence_Field sig_info_fields[] = {\n    {\"si_signo\", \"Signal number\"}, {\"si_code\", \"Signal code\"}, {\"si_pid\", \"Sending Process id\"},\n    {\"si_uid\", \"Real user id of sending process\"}, {\"si_addr\", \"Address of faulting instruction as int\"},\n    {\"si_status\", \"Exit value or signal\"}, {\"sival_int\", \"Signal value as int\"}, {\"sival_ptr\", \"Signal value as pointer int\"},\n    {NULL, NULL}\n};\nstatic PyStructSequence_Desc sig_info_desc = {\"SigInfo\", NULL, sig_info_fields, 6};\n\nstatic bool\nhandle_signal_callback_py(const siginfo_t* siginfo, void *data) {\n    if (PyErr_Occurred()) return false;\n    PyObject *callback = data;\n    PyObject *ans = PyStructSequence_New(&SigInfoType);\n    int pos = 0;\n#define S(x) { PyObject *t = x; if (t) { PyStructSequence_SET_ITEM(ans, pos, x); } else { Py_CLEAR(ans); return false; } pos++; }\n    if (ans) {\n        S(PyLong_FromLong((long)siginfo->si_signo));\n        S(PyLong_FromLong((long)siginfo->si_code));\n        S(PyLong_FromLong((long)siginfo->si_pid));\n        S(PyLong_FromLong((long)siginfo->si_uid));\n        S(PyLong_FromVoidPtr(siginfo->si_addr));\n        S(PyLong_FromLong((long)siginfo->si_status));\n        S(PyLong_FromLong((long)siginfo->si_value.sival_int));\n        S(PyLong_FromVoidPtr(siginfo->si_value.sival_ptr));\n        PyObject *ret = PyObject_CallFunctionObjArgs(callback, ans, NULL);\n        Py_CLEAR(ans); Py_CLEAR(ret);\n    }\n    return (PyErr_Occurred()) ? false : true;\n#undef S\n}\n\nstatic PyObject*\nread_signals_py(PyObject *self UNUSED, PyObject *args) {\n    int fd; PyObject *callback;\n    if (!PyArg_ParseTuple(args, \"iO\", &fd, &callback)) return NULL;\n    if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, \"callback must be callable\"); return NULL; }\n    read_signals(fd, handle_signal_callback_py, callback);\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\nremove_signal_handlers_py(PyObject *self UNUSED, PyObject *args UNUSED) {\n    if (python_loop_data.num_handled_signals) {\n        remove_signal_handlers(&python_loop_data);\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyMethodDef methods[] = {\n    {\"install_signal_handlers\", init_signal_handlers_py, METH_VARARGS, \"Initialize an fd to read signals from\" },\n    {\"read_signals\", read_signals_py, METH_VARARGS, \"Read pending signals from the specified fd\" },\n    {\"remove_signal_handlers\", remove_signal_handlers_py, METH_NOARGS, \"Remove signal handlers\" },\n    { NULL, NULL, 0, NULL },\n};\n\nbool\ninit_loop_utils(PyObject *module) {\n    if (PyStructSequence_InitType2(&SigInfoType, &sig_info_desc) != 0) return false;\n    Py_INCREF((PyObject *) &SigInfoType);\n    PyModule_AddObject(module, \"SigInfo\", (PyObject *) &SigInfoType);\n\n    return PyModule_AddFunctions(module, methods) == 0;\n}\n"
  },
  {
    "path": "kitty/loop-utils.h",
    "content": "/*\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n#include <fcntl.h>\n#include <unistd.h>\n#include <signal.h>\n\n#ifdef __has_include\n#if __has_include(<sys/signalfd.h>)\n#define HAS_SIGNAL_FD\n#include <sys/signalfd.h>\n#endif\n\n#if __has_include(<sys/eventfd.h>)\n#define HAS_EVENT_FD\n#include <sys/eventfd.h>\n#endif\n#else\n#define HAS_SIGNAL_FD\n#include <sys/signalfd.h>\n#define HAS_EVENT_FD\n#include <sys/eventfd.h>\n#endif\n\ntypedef struct {\n#ifndef HAS_EVENT_FD\n    int wakeup_fds[2];\n#endif\n#ifndef HAS_SIGNAL_FD\n    int signal_fds[2];\n#endif\n    sigset_t signals;\n    int wakeup_read_fd;\n    int signal_read_fd;\n    int handled_signals[16];\n    size_t num_handled_signals;\n} LoopData;\ntypedef bool(*handle_signal_func)(const siginfo_t* siginfo, void *data);\n\nbool init_loop_data(LoopData *ld, ...);\nvoid free_loop_data(LoopData *ld);\nvoid wakeup_loop(LoopData *ld, bool in_signal_handler, const char*);\nvoid read_signals(int fd, handle_signal_func callback, void *data);\n\nstatic inline bool\nself_pipe(int fds[2], bool nonblock) {\n#ifdef __APPLE__\n    int flags;\n    flags = pipe(fds);\n    if (flags != 0) return false;\n    for (int i = 0; i < 2; i++) {\n        flags = fcntl(fds[i], F_GETFD);\n        if (flags == -1) {  return false; }\n        if (fcntl(fds[i], F_SETFD, flags | FD_CLOEXEC) == -1) { return false; }\n        if (nonblock) {\n            flags = fcntl(fds[i], F_GETFL);\n            if (flags == -1) { return false; }\n            if (fcntl(fds[i], F_SETFL, flags | O_NONBLOCK) == -1) { return false; }\n        }\n    }\n    return true;\n#else\n    int flags = O_CLOEXEC;\n    if (nonblock) flags |= O_NONBLOCK;\n    return pipe2(fds, flags) == 0;\n#endif\n}\n\nstatic inline void\ndrain_fd(int fd) {\n    static uint8_t drain_buf[1024];\n    while(true) {\n        ssize_t len = read(fd, drain_buf, sizeof(drain_buf));\n        if (len < 0) {\n            if (errno == EINTR) continue;\n            break;\n        }\n        if (len > 0) continue;\n        break;\n    }\n}\n"
  },
  {
    "path": "kitty/macos_process_info.c",
    "content": "/*\n * macos_process_info.c\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n\n#include <sys/sysctl.h>\ntypedef void* rusage_info_t;  // needed for libproc.h\n#include <libproc.h>\n\nstatic PyObject*\ncwd_of_process(PyObject *self UNUSED, PyObject *pid_) {\n    if (!PyLong_Check(pid_)) { PyErr_SetString(PyExc_TypeError, \"pid must be an int\"); return NULL; }\n    long pid = PyLong_AsLong(pid_);\n    if (pid < 0) { PyErr_SetString(PyExc_TypeError, \"pid cannot be negative\"); return NULL; }\n    struct proc_vnodepathinfo vpi;\n    int ret = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));\n    if (ret < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }\n    return PyUnicode_FromString(vpi.pvi_cdir.vip_path);\n}\n\nstatic PyObject*\nabspath_of_process(PyObject *self UNUSED, PyObject *pid_) {\n    if (!PyLong_Check(pid_)) { PyErr_SetString(PyExc_TypeError, \"pid must be an int\"); return NULL; }\n    pid_t pid = PyLong_AsLong(pid_);\n    if (pid < 0) { PyErr_SetString(PyExc_TypeError, \"pid cannot be negative\"); return NULL; }\n    char pathbuf[PROC_PIDPATHINFO_MAXSIZE];\n    int ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));\n    if (ret < 0) {\n        PyErr_SetFromErrno(PyExc_OSError); return NULL;\n    } else if (ret == 0) {\n        errno = EINVAL;\n        PyErr_SetFromErrno(PyExc_OSError); return NULL;\n    }\n    return PyUnicode_FromString(pathbuf);\n}\n\n// Read the maximum argument size for processes\nstatic int\nget_argmax(void) {\n    int argmax;\n    int mib[] = { CTL_KERN, KERN_ARGMAX };\n    size_t size = sizeof(argmax);\n\n    if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0)\n        return argmax;\n    return 0;\n}\n\nstatic PyObject*\nget_all_processes(PyObject *self UNUSED, PyObject *args UNUSED) {\n    pid_t num = proc_listallpids(NULL, 0);\n    if (num <= 0) return PyTuple_New(0);\n    size_t sz = sizeof(pid_t) * num * 2;\n    pid_t *buf = malloc(sz);\n    if (!buf) return PyErr_NoMemory();\n    num = proc_listallpids(buf, sz);\n    if (num <= 0) { free(buf); return PyTuple_New(0); }\n    PyObject *ans = PyTuple_New(num);\n    if (!ans) { free(buf); return NULL; }\n    for (pid_t i = 0; i < num; i++) {\n        long long pid = buf[i];\n        PyObject *t = PyLong_FromLongLong(pid);\n        if (!t) { free(buf); Py_CLEAR(ans); return NULL; }\n        PyTuple_SET_ITEM(ans, i, t);\n    }\n    return ans;\n}\n\nstatic PyObject*\ncmdline_of_process(PyObject *self UNUSED, PyObject *pid_) {\n    // Taken from psutil, with thanks (BSD 3-clause license)\n    int mib[3];\n    int nargs;\n    size_t len;\n    char *procargs = NULL;\n    char *arg_ptr;\n    char *arg_end;\n    char *curr_arg;\n    size_t argmax;\n\n    PyObject *py_arg = NULL;\n    PyObject *py_retlist = NULL;\n    if (!PyLong_Check(pid_)) { PyErr_SetString(PyExc_TypeError, \"pid must be an int\"); goto error; }\n    long pid = PyLong_AsLong(pid_);\n    if (pid < 0) { PyErr_SetString(PyExc_TypeError, \"pid cannot be negative\"); goto error; }\n\n    // special case for PID 0 (kernel_task) where cmdline cannot be fetched\n    if (pid == 0)\n        return Py_BuildValue(\"[]\");\n\n    // read argmax and allocate memory for argument space.\n    argmax = get_argmax();\n    if (!argmax) {\n        PyErr_SetFromErrno(PyExc_OSError);\n        goto error;\n    }\n\n    procargs = (char *)malloc(argmax);\n    if (NULL == procargs) {\n        PyErr_SetFromErrno(PyExc_OSError);\n        goto error;\n    }\n\n    // read argument space\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROCARGS2;\n    mib[2] = (pid_t)pid;\n    if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {\n        // In case of zombie process or non-existent process we'll get EINVAL.\n        if (errno == EINVAL)\n            PyErr_Format(PyExc_ValueError, \"process with pid %ld either does not exist or is a zombie or you dont have permission\", pid);\n        else\n            PyErr_SetFromErrno(PyExc_OSError);\n        goto error;\n    }\n\n    arg_end = &procargs[argmax];\n    // copy the number of arguments to nargs\n    memcpy(&nargs, procargs, sizeof(nargs));\n\n    arg_ptr = procargs + sizeof(nargs);\n    len = strlen(arg_ptr);\n    arg_ptr += len + 1;\n\n    if (arg_ptr == arg_end) {\n        free(procargs);\n        return Py_BuildValue(\"[]\");\n    }\n\n    // skip ahead to the first argument\n    for (; arg_ptr < arg_end; arg_ptr++) {\n        if (*arg_ptr != '\\0')\n            break;\n    }\n\n    // iterate through arguments\n    curr_arg = arg_ptr;\n    py_retlist = Py_BuildValue(\"[]\");\n    if (!py_retlist)\n        goto error;\n    while (arg_ptr < arg_end && nargs > 0) {\n        if (*arg_ptr++ == '\\0') {\n            py_arg = PyUnicode_DecodeFSDefault(curr_arg);\n            if (! py_arg)\n                goto error;\n            if (PyList_Append(py_retlist, py_arg))\n                goto error;\n            Py_DECREF(py_arg);\n            // iterate to next arg and decrement # of args\n            curr_arg = arg_ptr;\n            nargs--;\n        }\n    }\n\n    free(procargs);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_arg);\n    Py_XDECREF(py_retlist);\n    if (procargs != NULL)\n        free(procargs);\n    return NULL;\n\n}\n\nPyObject *\nenviron_of_process(PyObject *self UNUSED, PyObject *pid_) {\n    // Taken from psutil, with thanks (BSD 3-clause license)\n    int mib[3];\n    int nargs;\n    char *procargs = NULL;\n    char *procenv = NULL;\n    char *arg_ptr;\n    char *arg_end;\n    char *env_start;\n    size_t argmax;\n    PyObject *py_ret = NULL;\n    if (!PyLong_Check(pid_)) { PyErr_SetString(PyExc_TypeError, \"pid must be an int\"); goto error; }\n    long pid = PyLong_AsLong(pid_);\n    if (pid < 0) { PyErr_SetString(PyExc_TypeError, \"pid cannot be negative\"); goto error; }\n\n    // special case for PID 0 (kernel_task) where cmdline cannot be fetched\n    if (pid == 0)\n        goto empty;\n\n    // read argmax and allocate memory for argument space.\n    argmax = get_argmax();\n    if (! argmax) {\n        PyErr_SetFromErrno(PyExc_OSError);\n        goto error;\n    }\n\n    procargs = (char *)malloc(argmax);\n    if (NULL == procargs) {\n        PyErr_SetFromErrno(PyExc_OSError);\n        goto error;\n    }\n\n    // read argument space\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROCARGS2;\n    mib[2] = (pid_t)pid;\n    if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {\n        // In case of zombie process or a non-existent process we'll get EINVAL\n        // to NSP and _psosx.py will translate it to ZP.\n        if (errno == EINVAL)\n            PyErr_Format(PyExc_ValueError, \"process with pid %ld either does not exist or is a zombie or you dont have permission\", pid);\n        else\n            PyErr_SetFromErrno(PyExc_OSError);\n        goto error;\n    }\n\n    arg_end = &procargs[argmax];\n    // copy the number of arguments to nargs\n    memcpy(&nargs, procargs, sizeof(nargs));\n\n    // skip executable path\n    arg_ptr = procargs + sizeof(nargs);\n    arg_ptr = memchr(arg_ptr, '\\0', arg_end - arg_ptr);\n\n    if (arg_ptr == NULL || arg_ptr == arg_end)\n        goto empty;\n\n    // skip ahead to the first argument\n    for (; arg_ptr < arg_end; arg_ptr++) {\n        if (*arg_ptr != '\\0')\n            break;\n    }\n\n    // iterate through arguments\n    while (arg_ptr < arg_end && nargs > 0) {\n        if (*arg_ptr++ == '\\0')\n            nargs--;\n    }\n\n    // build an environment variable block\n    env_start = arg_ptr;\n\n    procenv = calloc(1, arg_end - arg_ptr);\n    if (procenv == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    while (*arg_ptr != '\\0' && arg_ptr < arg_end) {\n        char *s = memchr(arg_ptr + 1, '\\0', arg_end - arg_ptr);\n\n        if (s == NULL)\n            break;\n\n        memcpy(procenv + (arg_ptr - env_start), arg_ptr, s - arg_ptr);\n\n        arg_ptr = s + 1;\n    }\n\n    py_ret = PyUnicode_DecodeFSDefaultAndSize(\n        procenv, arg_ptr - env_start + 1);\n    if (!py_ret) {\n        // XXX: don't want to free() this as per:\n        // https://github.com/giampaolo/psutil/issues/926\n        // It sucks but not sure what else to do.\n        procargs = NULL;\n        goto error;\n    }\n\n    free(procargs);\n    free(procenv);\n\n    return py_ret;\n\nempty:\n    if (procargs != NULL)\n        free(procargs);\n    return Py_BuildValue(\"s\", \"\");\n\nerror:\n    Py_XDECREF(py_ret);\n    free(procargs);\n    free(procenv);\n    return NULL;\n}\n\n\nstatic PyMethodDef module_methods[] = {\n    {\"cwd_of_process\", (PyCFunction)cwd_of_process, METH_O, \"\"},\n    {\"abspath_of_process\", (PyCFunction)abspath_of_process, METH_O, \"\"},\n    {\"cmdline_of_process\", (PyCFunction)cmdline_of_process, METH_O, \"\"},\n    {\"environ_of_process\", (PyCFunction)environ_of_process, METH_O, \"\"},\n    {\"get_all_processes\", (PyCFunction)get_all_processes, METH_NOARGS, \"\"},\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\n\nbool\ninit_macos_process_info(PyObject *module) {\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    return true;\n}\n"
  },
  {
    "path": "kitty/main.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport locale\nimport os\nimport shutil\nimport sys\nfrom collections.abc import Generator, Sequence\nfrom contextlib import contextmanager, suppress\nfrom gettext import gettext as _\n\nfrom .borders import load_borders_program\nfrom .boss import Boss\nfrom .child import set_default_env, set_LANG_in_default_env\nfrom .cli import create_opts, parse_args\nfrom .cli_stub import CLIOptions\nfrom .colors import theme_colors\nfrom .conf.utils import BadLine\nfrom .config import cached_values_for\nfrom .constants import (\n    appname,\n    beam_cursor_data_file,\n    clear_handled_signals,\n    glfw_path,\n    is_macos,\n    is_quick_access_terminal_app,\n    is_wayland,\n    kitten_exe,\n    kitty_exe,\n    launched_by_launch_services,\n    logo_png_file,\n    running_in_kitty,\n    supports_window_occlusion,\n    website_url,\n)\nfrom .fast_data_types import (\n    GLFW_MOD_ALT,\n    GLFW_MOD_SHIFT,\n    SingleKey,\n    create_os_window,\n    free_font_data,\n    glfw_get_monitor_names,\n    glfw_get_monitor_workarea,\n    glfw_init,\n    glfw_terminate,\n    grab_keyboard,\n    is_layer_shell_supported,\n    load_png_data,\n    mask_kitty_signals_process_wide,\n    run_at_exit_cleanup_functions,\n    set_custom_cursor,\n    set_default_window_icon,\n    set_options,\n    set_use_os_log,\n)\nfrom .fonts.render import dump_font_debug, set_font_family\nfrom .options.types import Options\nfrom .options.utils import DELETE_ENV_VAR\nfrom .os_window_size import edge_spacing, initial_window_size_func\nfrom .session import create_sessions, get_os_window_sizing_data\nfrom .shaders import CompileError, load_shader_programs\nfrom .types import LayerShellConfig\nfrom .utils import (\n    cleanup_ssh_control_masters,\n    expandvars,\n    get_custom_window_icon,\n    log_error,\n    parse_os_window_state,\n    read_shell_environment,\n    safe_mtime,\n    startup_notification_handler,\n)\n\n\ndef set_custom_ibeam_cursor() -> None:\n    with open(beam_cursor_data_file, 'rb') as f:\n        data = f.read()\n    rgba_data, width, height = load_png_data(data)\n    c2x = os.path.splitext(beam_cursor_data_file)\n    with open(f'{c2x[0]}@2x{c2x[1]}', 'rb') as f:\n        data = f.read()\n    rgba_data2, width2, height2 = load_png_data(data)\n    images = (rgba_data, width, height), (rgba_data2, width2, height2)\n    try:\n        set_custom_cursor(\"beam\", images, 4, 8)\n    except Exception as e:\n        log_error(f'Failed to set custom beam cursor with error: {e}')\n\n\ndef load_all_shaders() -> None:\n    try:\n        load_shader_programs()\n        load_borders_program()\n    except CompileError as err:\n        raise SystemExit(err)\n\n\ndef init_glfw_module(glfw_module: str = 'wayland', debug_keyboard: bool = False, debug_rendering: bool = False, wayland_enable_ime: bool = True) -> None:\n    ok, swo = glfw_init(glfw_path(glfw_module), edge_spacing, debug_keyboard, debug_rendering, wayland_enable_ime)\n    if not ok:\n        raise SystemExit('GLFW initialization failed')\n    supports_window_occlusion(swo)\n\n\ndef init_glfw(opts: Options, debug_keyboard: bool = False, debug_rendering: bool = False) -> str:\n    glfw_module = 'cocoa' if is_macos else ('wayland' if is_wayland(opts) else 'x11')\n    init_glfw_module(glfw_module, debug_keyboard, debug_rendering, wayland_enable_ime=opts.wayland_enable_ime)\n    return glfw_module\n\n\ndef get_macos_shortcut_for(\n    func_map: dict[tuple[str, ...], list[SingleKey]], defn: str = 'new_os_window', lookup_name: str = ''\n) -> SingleKey | None:\n    # for maximum robustness we should use opts.alias_map to resolve\n    # aliases however this requires parsing everything on startup which could\n    # be potentially slow. Lets just hope the user doesn't alias these\n    # functions.\n    ans = None\n    candidates = []\n    qkey = tuple(defn.split())\n    candidates = func_map[qkey]\n    if candidates:\n        from .fast_data_types import cocoa_set_global_shortcut\n        alt_mods = GLFW_MOD_ALT, GLFW_MOD_ALT | GLFW_MOD_SHIFT\n        # Reverse list so that later defined keyboard shortcuts take priority over earlier defined ones\n        for candidate in reversed(candidates):\n            if candidate.mods in alt_mods:\n                # Option based shortcuts dont work in the global menubar,\n                # presumably because Apple reserves them for IME, see\n                # https://github.com/kovidgoyal/kitty/issues/3515\n                continue\n            if cocoa_set_global_shortcut(lookup_name or qkey[0], candidate[0], candidate[2]):\n                ans = candidate\n                break\n    return ans\n\n\ndef set_macos_app_custom_icon() -> None:\n    custom_icon_mtime, custom_icon_path = get_custom_window_icon()\n    if custom_icon_mtime is not None and custom_icon_path is not None:\n        from .fast_data_types import cocoa_set_app_icon, cocoa_set_dock_icon\n        krd = getattr(sys, 'kitty_run_data')\n        bundle_path = os.path.dirname(os.path.dirname(krd.get('bundle_exe_dir')))\n        icon_sentinel = os.path.join(bundle_path, 'Icon\\r')\n        sentinel_mtime = safe_mtime(icon_sentinel)\n        if sentinel_mtime is None or sentinel_mtime < custom_icon_mtime:\n            try:\n                cocoa_set_app_icon(custom_icon_path, bundle_path)\n            except (FileNotFoundError, OSError) as e:\n                log_error(str(e))\n                log_error('Failed to set custom app icon, ignoring')\n        # macOS Dock does not reload icons until it is restarted, so we set\n        # the application icon here. This will revert when kitty quits, but\n        # can't be helped since there appears to be no way to get the dock\n        # to reload short of killing it.\n        cocoa_set_dock_icon(custom_icon_path)\n\n\ndef get_icon128_path(base_path: str) -> str:\n    # max icon size on X11 64bits is 128x128\n    path, ext = os.path.splitext(base_path)\n    return f'{path}-128{ext}'\n\n\ndef set_window_icon() -> None:\n    custom_icon_path = get_custom_window_icon()[1]\n    is_x11 = not is_macos and not is_wayland()\n    try:\n        if custom_icon_path is not None:\n            custom_icon128_path = get_icon128_path(custom_icon_path)\n            if is_x11 and safe_mtime(custom_icon128_path) is not None:\n                set_default_window_icon(custom_icon128_path)\n            else:\n                set_default_window_icon(custom_icon_path)\n        else:\n            if is_x11:\n                set_default_window_icon(get_icon128_path(logo_png_file))\n    except ValueError as err:\n        log_error(err)\n\n\ndef set_cocoa_global_shortcuts(opts: Options) -> dict[str, SingleKey]:\n    global_shortcuts: dict[str, SingleKey] = {}\n    if is_macos:\n        from collections import defaultdict\n        func_map = defaultdict(list)\n        for single_key, v in opts.keyboard_modes[''].keymap.items():\n            kd = v[-1]  # the last definition is the active one\n            if kd.is_suitable_for_global_shortcut:\n                parts = tuple(kd.definition.split())\n                func_map[parts].append(single_key)\n\n        for ac in ('new_os_window', 'close_os_window', 'close_tab', 'edit_config_file', 'previous_tab',\n                   'next_tab', 'new_tab', 'new_window', 'close_window', 'toggle_macos_secure_keyboard_entry',\n                   'toggle_fullscreen', 'macos_cycle_through_os_windows', 'macos_cycle_through_os_windows_backwards',\n                   'hide_macos_app', 'hide_macos_other_apps', 'minimize_macos_window', 'quit', 'search_scrollback'):\n            val = get_macos_shortcut_for(func_map, ac)\n            if val is not None:\n                global_shortcuts[ac] = val\n        val = get_macos_shortcut_for(func_map, 'clear_terminal reset active', lookup_name='reset_terminal')\n        if val is not None:\n            global_shortcuts['reset_terminal'] = val\n        val = get_macos_shortcut_for(func_map, 'clear_terminal to_cursor active', lookup_name='clear_terminal_and_scrollback')\n        if val is not None:\n            global_shortcuts['clear_terminal_and_scrollback'] = val\n        val = get_macos_shortcut_for(func_map, 'clear_terminal scrollback active', lookup_name='clear_scrollback')\n        if val is not None:\n            global_shortcuts['clear_scrollback'] = val\n        val = get_macos_shortcut_for(func_map, 'clear_terminal to_cursor_scroll active', lookup_name='clear_screen')\n        if val is not None:\n            global_shortcuts['clear_screen'] = val\n        val = get_macos_shortcut_for(func_map, 'clear_terminal last_command active', lookup_name='clear_last_command')\n        if val is not None:\n            global_shortcuts['clear_last_command'] = val\n        val = get_macos_shortcut_for(func_map, 'load_config_file', lookup_name='reload_config')\n        if val is not None:\n            global_shortcuts['reload_config'] = val\n        val = get_macos_shortcut_for(func_map, f'open_url {website_url()}', lookup_name='open_kitty_website')\n        if val is not None:\n            global_shortcuts['open_kitty_website'] = val\n    return global_shortcuts\n\n\n_is_panel_kitten = False\n\n\ndef is_panel_kitten() -> bool:\n    return _is_panel_kitten\n\n\ndef list_monitors(json_output: bool = False) -> None:\n    monitor_names = glfw_get_monitor_names()\n    has_descriptions = False\n    for (name, desc) in monitor_names:\n        if desc:\n            has_descriptions = True\n            break\n\n    if json_output:\n        if has_descriptions:\n            monitors_list_of_dict = [{'name': name, 'description': desc} for name, desc in monitor_names]\n        else:\n            monitors_list_of_dict = [{'name': name} for name, _ in monitor_names]\n        json.dump(monitors_list_of_dict, sys.stdout, indent=2, sort_keys=True)\n        print()\n        return\n\n    isatty = sys.stdout.isatty()\n    for (name, desc) in monitor_names:\n        if isatty:\n            name = f'\\x1b[32m{name}\\x1b[39m'  # ]]\n        print(name)\n        if desc:\n            print(f'\\t{desc}')\n        if has_descriptions:\n            print()\n\n\ndef _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (), talk_fd: int = -1) -> None:\n    global _is_panel_kitten\n    _is_panel_kitten = run_app.cached_values_name == 'panel'\n    if _is_panel_kitten and run_app.layer_shell_config and run_app.layer_shell_config.output_name in ('list', 'listjson'):\n        list_monitors(run_app.layer_shell_config.output_name == 'listjson')\n        return\n    if is_macos:\n        global_shortcuts = set_cocoa_global_shortcuts(opts)\n        if opts.macos_custom_beam_cursor:\n            set_custom_ibeam_cursor()\n        set_macos_app_custom_icon()\n    else:\n        global_shortcuts = {}\n        set_window_icon()\n    if _is_panel_kitten and not is_layer_shell_supported():\n        raise SystemExit('Cannot create panels as the window manager/compositor does not support the necessary protocols')\n    pos_x, pos_y = None, None\n    if args.grab_keyboard:\n        grab_keyboard(True)\n    with cached_values_for(run_app.cached_values_name) as cached_values:\n        if not _is_panel_kitten and not is_wayland():\n            if opts.remember_window_position:\n                cached_workarea = tuple(tuple(x) for x in cached_values.get('monitor-workarea', ()))\n                if cached_workarea and glfw_get_monitor_workarea() == tuple(cached_workarea):\n                    pos_x, pos_y = cached_values.get('window-pos', (None, None))\n            if args.position:\n                pos_x, pos_y = map(int, args.position.lower().partition('x')[::2])\n        startup_session_error: tuple[Exception, str] | None = None\n        try:\n            startup_sessions = tuple(create_sessions(opts, args, default_session=opts.startup_session))\n        except Exception as e:\n            startup_session_error = (e, (getattr(args, 'session', '') or opts.startup_session or ''))\n            if getattr(args, 'session', ''):\n                args.session = ''\n            startup_sessions = tuple(create_sessions(opts, args))\n        wincls = (startup_sessions[0].os_window_class if startup_sessions else '') or args.cls or appname\n        winname = (startup_sessions[0].os_window_name if startup_sessions else '') or args.name or wincls or appname\n        window_state = (args.start_as if args.start_as and args.start_as != 'normal' else None) or (\n            getattr(startup_sessions[0], 'os_window_state', None) if startup_sessions else None\n        )\n        wstate = parse_os_window_state(window_state) if window_state is not None else None\n\n        with startup_notification_handler(extra_callback=run_app.first_window_callback) as pre_show_callback:\n            window_id = create_os_window(\n                    run_app.initial_window_size_func(get_os_window_sizing_data(opts, startup_sessions[0] if startup_sessions else None), cached_values),\n                    pre_show_callback,\n                    args.title or appname, winname,\n                    wincls, wstate, load_all_shaders, disallow_override_title=bool(args.title), layer_shell_config=run_app.layer_shell_config, x=pos_x, y=pos_y)\n        boss = Boss(opts, args, cached_values, global_shortcuts, talk_fd)\n        boss.start(window_id, startup_sessions)\n        if args.debug_font_fallback:\n            dump_font_debug()\n        if bad_lines or boss.misc_config_errors:\n            boss.show_bad_config_lines(bad_lines, boss.misc_config_errors)\n            boss.misc_config_errors = []\n        if startup_session_error:\n            boss.show_error(_('The startup session was invalid'), _(\n                'Loading the start session file {0} failed, with error:\\n{1}').format(startup_session_error[1], startup_session_error[0]))\n        try:\n            boss.child_monitor.main_loop()\n        finally:\n            boss.destroy()\n\n\nclass AppRunner:\n\n    def __init__(self) -> None:\n        self.cached_values_name = 'main'\n        self.first_window_callback = lambda window_handle: None\n        self.layer_shell_config: LayerShellConfig | None = None\n        self.initial_window_size_func = initial_window_size_func\n\n    def __call__(self, opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (), talk_fd: int = -1) -> None:\n        if theme_colors.refresh():\n            theme_colors.patch_opts(opts, args.debug_rendering)\n        set_options(opts, is_wayland(), args.debug_rendering, args.debug_font_fallback)\n        try:\n            set_font_family(opts, add_builtin_nerd_font=True)\n            _run_app(opts, args, bad_lines, talk_fd)\n        finally:\n            set_options(None)\n            free_font_data()  # must free font data before glfw/freetype/fontconfig/opengl etc are finalized\n            if is_macos:\n                from kitty.fast_data_types import (\n                    cocoa_set_notification_activated_callback,\n                )\n                cocoa_set_notification_activated_callback(None)\n\n\nrun_app = AppRunner()\n\n\ndef ensure_macos_locale() -> None:\n    # Ensure the LANG env var is set. See\n    # https://github.com/kovidgoyal/kitty/issues/90\n    from .fast_data_types import cocoa_get_lang, locale_is_valid\n    if 'LANG' not in os.environ:\n        lang_code, country_code, identifier = cocoa_get_lang()\n        lang = 'en_US'\n        if identifier and locale_is_valid(identifier):\n            lang = identifier\n        elif lang_code and country_code and locale_is_valid(f'{lang_code}_{country_code}'):\n            lang = f'{lang_code}_{country_code}'\n        elif lang_code:\n            if lang_code != 'en':\n                with suppress(OSError):\n                    found = sorted(x for x in os.listdir('/usr/share/locale') if x.startswith(f'{lang_code}_'))\n                    if found:\n                        lang = found[0].partition('.')[0]\n        os.environ['LANG'] = f'{lang}.UTF-8'\n        set_LANG_in_default_env(os.environ['LANG'])\n\n\n@contextmanager\ndef setup_profiling() -> Generator[None, None, None]:\n    try:\n        from .fast_data_types import start_profiler, stop_profiler\n        do_profile = True\n    except ImportError:\n        do_profile = False\n    if do_profile:\n        start_profiler('/tmp/kitty-profile.log')\n    yield\n    if do_profile:\n        import subprocess\n        stop_profiler()\n        exe = kitty_exe()\n        cg = '/tmp/kitty-profile.callgrind'\n        print('Post processing profile data for', exe, '...')\n        with open(cg, 'wb') as f:\n            subprocess.call(['pprof', '--callgrind', exe, '/tmp/kitty-profile.log'], stdout=f)\n        try:\n            subprocess.Popen(['kcachegrind', cg], preexec_fn=clear_handled_signals)\n        except FileNotFoundError:\n            subprocess.call(['pprof', '--text', exe, '/tmp/kitty-profile.log'])\n            print('To view the graphical call data, use: kcachegrind', cg)\n\n\ndef expand_listen_on(listen_on: str, from_config_file: bool, env: dict[str, str]) -> str:\n    if from_config_file and listen_on == 'none':\n        return ''\n    listen_on = expandvars(listen_on, env)\n    if '{kitty_pid}' not in listen_on and from_config_file and listen_on.startswith('unix:'):\n        listen_on += '-{kitty_pid}'\n    listen_on = listen_on.replace('{kitty_pid}', str(os.getpid()))\n    if listen_on.startswith('unix:'):\n        path = listen_on[len('unix:'):]\n        if not path.startswith('@'):\n            if path.startswith('~'):\n                listen_on = f'unix:{os.path.expanduser(path)}'\n            elif not os.path.isabs(path):\n                import tempfile\n                listen_on = f'unix:{os.path.join(tempfile.gettempdir(), path)}'\n    elif listen_on.startswith('tcp:') or listen_on.startswith('tcp6:'):\n        if from_config_file:  # use a random port\n            listen_on = ':'.join(listen_on.split(':', 2)[:2]) + ':0'\n    return listen_on\n\n\ndef safe_samefile(a: str, b: str) -> bool:\n    with suppress(OSError):\n        return os.path.samefile(a, b)\n    return os.path.abspath(os.path.realpath(a)) == os.path.abspath(os.path.realpath(b))\n\n\ndef prepend_if_not_present(path: str, paths_serialized: str) -> str:\n    # prepend a path only if path/kitty is not already present, even as a symlink\n    pq = os.path.join(path, 'kitty')\n    for candidate in paths_serialized.split(os.pathsep):\n        q = os.path.join(candidate, 'kitty')\n        if safe_samefile(q, pq):\n            return paths_serialized\n    return path + os.pathsep + paths_serialized\n\n\ndef ensure_kitty_in_path() -> None:\n    # Ensure the correct kitty is in PATH\n    krd = getattr(sys, 'kitty_run_data')\n    rpath = krd.get('bundle_exe_dir')\n    if not rpath:\n        return\n    if rpath:\n        modify_path = is_macos or getattr(sys, 'frozen', False) or krd.get('from_source')\n        existing = shutil.which('kitty')\n        if modify_path or not existing:\n            env_path = os.environ.get('PATH', '')\n            correct_kitty = os.path.join(rpath, 'kitty')\n            if not existing or not safe_samefile(existing, correct_kitty):\n                os.environ['PATH'] = prepend_if_not_present(rpath, env_path)\n\n\ndef ensure_kitten_in_path() -> None:\n    correct_kitten = kitten_exe()\n    existing = shutil.which('kitten')\n    if existing and safe_samefile(existing, correct_kitten):\n        return\n    env_path = os.environ.get('PATH', '')\n    os.environ['PATH'] = prepend_if_not_present(os.path.dirname(correct_kitten), env_path)\n\n\ndef setup_manpath(env: dict[str, str]) -> None:\n    # Ensure kitty manpages are available in frozen builds\n    if not getattr(sys, 'frozen', False):\n        return\n    from .constants import local_docs\n    mp = os.environ.get('MANPATH', env.get('MANPATH', ''))\n    d = os.path.dirname\n    kitty_man = os.path.join(d(d(d(local_docs()))), 'man')\n    if not mp:\n        env['MANPATH'] = f'{kitty_man}:'\n    elif mp.startswith(':'):\n        env['MANPATH'] = f':{kitty_man}:{mp}'\n    else:\n        env['MANPATH'] = f'{kitty_man}:{mp}'\n\n\ndef setup_environment(opts: Options, cli_opts: CLIOptions) -> None:\n    from_config_file = False\n    if not cli_opts.listen_on:\n        cli_opts.listen_on = opts.listen_on\n        from_config_file = True\n    if vars := opts.env.pop('read_from_shell', ''):\n        import fnmatch\n        import re\n        senv = read_shell_environment(opts)\n        patterns = tuple(re.compile(fnmatch.translate(x.strip())) for x in vars.split() if x.strip())\n        if patterns:\n            for k, v in senv.items():\n                for pat in patterns:\n                    if pat.match(k) is not None:\n                        opts.env[k] = v\n                        break\n    if cli_opts.listen_on:\n        cli_opts.listen_on = expand_listen_on(cli_opts.listen_on, from_config_file, opts.env)\n    env = opts.env.copy()\n    ensure_kitty_in_path()\n    ensure_kitten_in_path()\n    kitty_path = shutil.which('kitty')\n    if kitty_path:\n        child_path = env.get('PATH')\n        # if child_path is None it will be inherited from os.environ,\n        # the other values mean the user doesn't want a PATH\n        if child_path not in ('', DELETE_ENV_VAR) and child_path is not None:\n            env['PATH'] = prepend_if_not_present(os.path.dirname(kitty_path), env['PATH'])\n    setup_manpath(env)\n    set_default_env(env)\n\n\ndef set_locale() -> None:\n    if is_macos:\n        ensure_macos_locale()\n    try:\n        locale.setlocale(locale.LC_ALL, '')\n    except Exception:\n        log_error('Failed to set locale with LANG:', os.environ.get('LANG'))\n        old_lang = os.environ.pop('LANG', None)\n        if old_lang is not None:\n            try:\n                locale.setlocale(locale.LC_ALL, '')\n            except Exception:\n                log_error('Failed to set locale with no LANG')\n            os.environ['LANG'] = old_lang\n            set_LANG_in_default_env(old_lang)\n\n\ndef kitty_main(called_from_panel: bool = False) -> None:\n    running_in_kitty(True)\n\n    args = sys.argv[1:]\n    try:\n        cwd_ok = os.path.isdir(os.getcwd())\n    except Exception:\n        cwd_ok = False\n    if not cwd_ok:\n        os.chdir(os.path.expanduser('~'))\n    cli_flags = None\n    if getattr(sys, 'cmdline_args_for_open', False):\n        usage: str | None = 'file_or_url ...'\n        appname: str | None = 'kitty +open'\n        msg: str | None = (\n            'Run kitty and open the specified files or URLs in it, using launch-actions.conf. For details'\n            ' see https://sw.kovidgoyal.net/kitty/open_actions/#scripting-the-opening-of-files-with-kitty-on-macos'\n            '\\n\\nAll the normal kitty options can be used.')\n    else:\n        if not called_from_panel:\n            cli_flags = getattr(sys, 'kitty_run_data', {}).get('cli_flags', None)\n        usage = msg = appname = None\n    cli_opts, rest = parse_args(args=args, result_class=CLIOptions, usage=usage, message=msg, appname=appname, preparsed_from_c=cli_flags)\n    if getattr(sys, 'cmdline_args_for_open', False):\n        setattr(sys, 'cmdline_args_for_open', rest)\n        cli_opts.args = []\n    else:\n        cli_opts.args = rest\n    talk_fd = -1\n    if cli_opts.single_instance:\n        si_data = os.environ.pop('KITTY_SI_DATA', '')\n        if si_data:\n            talk_fd = int(si_data)\n\n    if cli_opts.detach:\n        if cli_opts.session == '-':\n            from .session import PreReadSession\n            cli_opts.session = PreReadSession(sys.stdin.read(), os.environ, '-', os.path.join(os.getcwd(), '-'))\n    if cli_opts.replay_commands:\n        from kitty.client import main as client_main\n        client_main(cli_opts.replay_commands)\n        return\n    bad_lines: list[BadLine] = []\n    opts = create_opts(cli_opts, accumulate_bad_lines=bad_lines)\n    if is_quick_access_terminal_app:\n        opts.macos_hide_from_tasks = True\n    setup_environment(opts, cli_opts)\n\n    # set_locale on macOS uses cocoa APIs when LANG is not set, so we have to\n    # call it after the fork\n    try:\n        set_locale()\n    except Exception:\n        log_error('Failed to set locale, ignoring')\n    with suppress(AttributeError):  # python compiled without threading\n        sys.setswitchinterval(1000.0)  # we have only a single python thread\n\n    if cli_opts.watcher:\n        from .window import global_watchers\n        global_watchers.set_extra(cli_opts.watcher)\n        log_error('The --watcher command line option has been deprecated in favor of using the watcher option in kitty.conf')\n    # mask the signals now as on some platforms the display backend starts\n    # threads. These threads must not handle the masked signals, to ensure\n    # kitty can handle them. See https://github.com/kovidgoyal/kitty/issues/4636\n    mask_kitty_signals_process_wide()\n    init_glfw(opts, cli_opts.debug_keyboard, cli_opts.debug_rendering)\n    try:\n        with setup_profiling():\n            # Avoid needing to launch threads to reap zombies\n            run_app(opts, cli_opts, bad_lines, talk_fd)\n    finally:\n        glfw_terminate()\n        cleanup_ssh_control_masters()\n\n\n\ndef main(called_from_panel: bool = False) -> None:\n    global redirected_for_quick_access\n    try:\n        if is_macos and launched_by_launch_services and not called_from_panel:\n            with suppress(OSError):\n                os.chdir(os.path.expanduser('~'))\n            if is_quick_access_terminal_app:\n                # we were started by launch services, use the kitten to read\n                # the config and re-run\n                os.execl(kitten_exe(), kitten_exe(), 'quick-access-terminal')\n            set_use_os_log(True)\n        kitty_main(called_from_panel)\n    except Exception:\n        import traceback\n        tb = traceback.format_exc()\n        log_error(tb)\n        raise SystemExit(1)\n    finally:\n        # we cant rely on this running during module unloading of fast_data_types as Python fails\n        # to unload the module, due to reference cycles, I am guessing.\n        run_at_exit_cleanup_functions()\n"
  },
  {
    "path": "kitty/marks.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport re\nfrom collections.abc import Callable, Generator, Iterable, Sequence\nfrom ctypes import POINTER, c_uint, c_void_p, cast\nfrom re import Pattern\nfrom typing import Union\n\nfrom .utils import resolve_custom_file\n\npointer_to_uint = POINTER(c_uint)\n\n\nMarkerFunc = Callable[[str, int, int, int], Generator[None, None, None]]\n\n\ndef get_output_variables(left_address: int, right_address: int, color_address: int) -> tuple[c_uint, c_uint, c_uint]:\n    return (\n        cast(c_void_p(left_address), pointer_to_uint).contents,\n        cast(c_void_p(right_address), pointer_to_uint).contents,\n        cast(c_void_p(color_address), pointer_to_uint).contents,\n    )\n\n\ndef marker_from_regex(expression: Union[str, 'Pattern[str]'], color: int, flags: int = re.UNICODE) -> MarkerFunc:\n    color = max(1, min(color, 3))\n    if isinstance(expression, str):\n        pat = re.compile(expression, flags=flags)\n    else:\n        pat = expression\n\n    def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:\n        left, right, colorv = get_output_variables(left_address, right_address, color_address)\n        colorv.value = color\n        for match in pat.finditer(text):\n            left.value = match.start()\n            right.value = match.end() - 1\n            yield\n\n    return marker\n\n\ndef marker_from_multiple_regex(regexes: Iterable[tuple[int, str]], flags: int = re.UNICODE) -> MarkerFunc:\n    expr = ''\n    color_map = {}\n    for i, (color, spec) in enumerate(regexes):\n        grp = f'mcg{i}'\n        expr += f'|(?P<{grp}>{spec})'\n        color_map[grp] = color\n    expr = expr[1:]\n    pat = re.compile(expr, flags=flags)\n\n    def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:\n        left, right, color = get_output_variables(left_address, right_address, color_address)\n        for match in pat.finditer(text):\n            left.value = match.start()\n            right.value = match.end() - 1\n            grp = match.lastgroup\n            color.value = color_map[grp] if grp is not None else 0\n            yield\n\n    return marker\n\n\ndef marker_from_text(expression: str, color: int) -> MarkerFunc:\n    return marker_from_regex(re.escape(expression), color)\n\n\ndef marker_from_function(func: Callable[[str], Iterable[tuple[int, int, int]]]) -> MarkerFunc:\n    def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]:\n        left, right, colorv = get_output_variables(left_address, right_address, color_address)\n        for (ll, r, c) in func(text):\n            left.value = ll\n            right.value = r\n            colorv.value = c\n            yield\n\n    return marker\n\n\ndef marker_from_spec(ftype: str, spec: str | Sequence[tuple[int, str]], flags: int) -> MarkerFunc:\n    if ftype == 'regex':\n        assert not isinstance(spec, str)\n        if len(spec) == 1:\n            return marker_from_regex(spec[0][1], spec[0][0], flags=flags)\n        return marker_from_multiple_regex(spec, flags=flags)\n    if ftype == 'function':\n        import runpy\n        assert isinstance(spec, str)\n        path = resolve_custom_file(spec)\n        return marker_from_function(runpy.run_path(path, run_name='__marker__')[\"marker\"])\n    raise ValueError(f'Unknown marker type: {ftype}')\n"
  },
  {
    "path": "kitty/modes.h",
    "content": "/*\n * modes.h\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n/* *Line Feed/New Line Mode*: When enabled, causes a received\n   LF, FF, or VT to move the cursor to the first column of\n   the next line.\n*/\n#define LNM 20\n\n/* *Insert/Replace Mode*: When enabled, new display characters move\n   old display characters to the right. Characters moved past the\n   right margin are lost. Otherwise, new display characters replace\n   old display characters at the cursor position.\n*/\n#define IRM 4\n\n\n// Private modes.\n\n// Arrow keys send application sequences or cursor movement commands\n#define DECCKM (1 << 5)\n\n// *Column Mode*: selects the number of columns per line (80 or 132)\n// on the screen.\n#define DECCOLM (3 << 5)\n\n// Scroll speed\n#define DECSCLM (4 << 5)\n\n// *Screen Mode*: toggles screen-wide reverse-video mode.\n#define DECSCNM  (5 << 5)\n\n// Auto-repeat of keys\n#define DECARM (8 << 5)\n\n/* *Origin Mode*: allows cursor addressing relative to a user-defined\n   origin. This mode resets when the terminal is powered up or reset.\n   It does not affect the erase in display (ED) function.\n*/\n#define DECOM  (6 << 5)\n\n// *Auto Wrap Mode*: selects where received graphic characters appear\n// when the cursor is at the right margin.\n#define DECAWM (7 << 5)\n\n// Toggle cursor blinking\n#define CONTROL_CURSOR_BLINK (12 << 5)\n\n// *Text Cursor Enable Mode*: determines if the text cursor is visible.\n#define DECTCEM (25 << 5)\n\n// National Replacement Character Set Mode\n#define DECNRCM (42 << 5)\n\n// xterm mouse protocol\n#define MOUSE_BUTTON_TRACKING (1000 << 5)\n#define MOUSE_MOTION_TRACKING  (1002 << 5)\n#define MOUSE_MOVE_TRACKING (1003 << 5)\n#define FOCUS_TRACKING (1004 << 5)\n#define MOUSE_UTF8_MODE (1005 << 5)\n#define MOUSE_SGR_MODE (1006 << 5)\n#define MOUSE_URXVT_MODE (1015 << 5)\n#define MOUSE_SGR_PIXEL_MODE (1016 << 5)\n\n// Save cursor (DECSC)\n#define SAVE_CURSOR (1048 << 5)\n\n// Alternate screen buffer\n#define TOGGLE_ALT_SCREEN_1 (47 << 5)\n#define TOGGLE_ALT_SCREEN_2 (1047 << 5)\n#define ALTERNATE_SCREEN  (1049 << 5)\n\n// Bracketed paste mode\n// https://cirw.in/blog/bracketed-paste\n#define BRACKETED_PASTE (2004 << 5)\n#define BRACKETED_PASTE_START \"200~\"\n#define BRACKETED_PASTE_END  \"201~\"\n\n// Pending updates mode\n#define PENDING_UPDATE (2026 << 5)\n\n// Notification of color preference change\n#define COLOR_PREFERENCE_NOTIFICATION (2031 << 5)\n\n// In-band resize notification mode\n#define INBAND_RESIZE_NOTIFICATION (2048 << 5)\n\n// Paste events mode https://rockorager.dev/misc/bracketed-paste-mime/\n#define PASTE_EVENTS (5522 << 5)\n\n// Handle Ctrl-C/Ctrl-Z mode\n#define HANDLE_TERMIOS_SIGNALS (19997 << 5)\n"
  },
  {
    "path": "kitty/monotonic.c",
    "content": "/*\n * monotonic.c\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define _POSIX_C_SOURCE 200809L\n#define MONOTONIC_IMPLEMENTATION\n#include \"monotonic.h\"\n"
  },
  {
    "path": "kitty/monotonic.h",
    "content": "/*\n * monotonic.h\n * Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n\n#include <stdint.h>\n\n#define MONOTONIC_T_MAX INT64_MAX\n#define MONOTONIC_T_MIN INT64_MIN\n#define MONOTONIC_T_1e6 1000000ll\n#define MONOTONIC_T_1e3 1000ll\n#define MONOTONIC_T_1e9 1000000000ll\n\ntypedef int64_t monotonic_t;\n\nstatic inline monotonic_t\ns_double_to_monotonic_t(double time) {\n    return (monotonic_t)(time * 1e9);\n}\n\nstatic inline monotonic_t\nms_double_to_monotonic_t(double time) {\n    return (monotonic_t)(time * 1e6);\n}\n\nstatic inline monotonic_t\ns_to_monotonic_t(monotonic_t time) {\n    return time * MONOTONIC_T_1e9;\n}\n\nstatic inline monotonic_t\nms_to_monotonic_t(monotonic_t time) {\n    return time * MONOTONIC_T_1e6;\n}\n\nstatic inline int\nmonotonic_t_to_ms(monotonic_t time) {\n    return (int)(time / MONOTONIC_T_1e6);\n}\n\nstatic inline int\nmonotonic_t_to_us(monotonic_t time) {\n    return (int)(time / MONOTONIC_T_1e3);\n}\n\n\nstatic inline double\nmonotonic_t_to_s_double(monotonic_t time) {\n    return ((double)time) / 1e9;\n}\n\nextern monotonic_t monotonic_start_time;\nextern monotonic_t monotonic_(void);\n\nstatic inline monotonic_t\nmonotonic(void) {\n    return monotonic_() - monotonic_start_time;\n}\n\nstatic inline void\ninit_monotonic(void) {\n    monotonic_start_time = monotonic_();\n}\n\nextern int timed_debug_print(const char *fmt, ...) __attribute__((format(printf, 1, 2)));\n\n#ifdef MONOTONIC_IMPLEMENTATION\n#include <time.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\nmonotonic_t monotonic_start_time = 0;\n\nstatic inline monotonic_t\ncalc_nano_time(struct timespec time) {\n    return ((monotonic_t)time.tv_sec * MONOTONIC_T_1e9) + (monotonic_t)time.tv_nsec;\n}\n\nmonotonic_t\nmonotonic_(void) {\n    struct timespec ts = {0};\n#ifdef CLOCK_HIGHRES\n    clock_gettime(CLOCK_HIGHRES, &ts);\n#elif CLOCK_MONOTONIC_RAW\n    clock_gettime(CLOCK_MONOTONIC_RAW, &ts);\n#else\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n#endif\n    return calc_nano_time(ts);\n}\n\nint\ntimed_debug_print(const char *fmt, ...) {\n    int result;\n    static int starting_print = 1;\n    if (starting_print) fprintf(stderr, \"[%.3f] \", monotonic_t_to_s_double(monotonic()));\n    va_list args;\n    va_start(args, fmt);\n    result = vfprintf(stderr, fmt, args);\n    va_end(args);\n    starting_print = fmt && strchr(fmt, '\\n') != NULL;\n    return result;\n}\n#endif\n"
  },
  {
    "path": "kitty/mouse.c",
    "content": "/*\n * mouse.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"state.h\"\n#include \"screen.h\"\n#include \"charsets.h\"\n#include <limits.h>\n#include <math.h>\n#include \"glfw-wrapper.h\"\n#include \"control-codes.h\"\n\nextern PyTypeObject Screen_Type;\n\nstatic MouseShape mouse_cursor_shape = TEXT_POINTER;\ntypedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE, LEAVE } MouseAction;\n#define debug debug_input\n\n// Encoding of mouse events {{{\n#define SHIFT_INDICATOR  (1 << 2)\n#define ALT_INDICATOR (1 << 3)\n#define CONTROL_INDICATOR (1 << 4)\n#define MOTION_INDICATOR  (1 << 5)\n#define SCROLL_BUTTON_INDICATOR (1 << 6)\n#define EXTRA_BUTTON_INDICATOR (1 << 7)\n#define LEAVE_INDICATOR (1 << 8)\n\n\nstatic unsigned int\nbutton_map(int button) {\n    switch(button) {\n        case GLFW_MOUSE_BUTTON_LEFT:\n            return 1;\n        case GLFW_MOUSE_BUTTON_RIGHT:\n            return 3;\n        case GLFW_MOUSE_BUTTON_MIDDLE:\n            return 2;\n        case GLFW_MOUSE_BUTTON_4:\n        case GLFW_MOUSE_BUTTON_5:\n        case GLFW_MOUSE_BUTTON_6:\n        case GLFW_MOUSE_BUTTON_7:\n        case GLFW_MOUSE_BUTTON_8:\n            return button + 5;\n        default:\n            return UINT_MAX;\n    }\n}\n\nstatic unsigned int\nencode_button(unsigned int button) {\n    if (button >= 8 && button <= 11) {\n        return (button - 8) | EXTRA_BUTTON_INDICATOR;\n    } else if (button >= 4 && button <= 7) {\n        return (button - 4) | SCROLL_BUTTON_INDICATOR;\n    } else if (button >= 1 && button <= 3) {\n        return button - 1;\n    } else {\n        return UINT_MAX;\n    }\n}\n\nstatic char mouse_event_buf[64];\n\nstatic int\nencode_mouse_event_impl(const MousePosition *mpos, int mouse_tracking_protocol, int button, MouseAction action, int mods) {\n    unsigned int cb = encode_button(button);\n    switch (action) {\n        case MOVE:\n            if (cb == UINT_MAX) cb = 3;\n            cb += 32;\n            break;\n        case LEAVE:\n            if (mouse_tracking_protocol != SGR_PIXEL_PROTOCOL) return 0;\n            cb = LEAVE_INDICATOR | MOTION_INDICATOR;\n            break;\n        default:\n            if (cb == UINT_MAX) return 0;\n            break;\n    }\n    if (action == DRAG || action == MOVE) cb |= MOTION_INDICATOR;\n    else if (action == RELEASE && mouse_tracking_protocol < SGR_PROTOCOL) cb = 3;\n    if (mods & GLFW_MOD_SHIFT) cb |= SHIFT_INDICATOR;\n    if (mods & GLFW_MOD_ALT) cb |= ALT_INDICATOR;\n    if (mods & GLFW_MOD_CONTROL) cb |= CONTROL_INDICATOR;\n    int x = mpos->cell_x + 1, y = mpos->cell_y + 1;\n    switch(mouse_tracking_protocol) {\n        case SGR_PIXEL_PROTOCOL:\n            x = (int)round(mpos->global_x);\n            y = (int)round(mpos->global_y);\n            /* fallthrough */\n        case SGR_PROTOCOL:\n            return snprintf(mouse_event_buf, sizeof(mouse_event_buf), \"<%d;%d;%d%s\", cb, x, y, action == RELEASE ? \"m\" : \"M\");\n            break;\n        case URXVT_PROTOCOL:\n            return snprintf(mouse_event_buf, sizeof(mouse_event_buf), \"%d;%d;%dM\", cb + 32, x, y);\n            break;\n        case UTF8_PROTOCOL:\n            mouse_event_buf[0] = 'M'; mouse_event_buf[1] = cb + 32;\n            unsigned int sz = 2;\n            sz += encode_utf8(x + 32, mouse_event_buf + sz);\n            sz += encode_utf8(y + 32, mouse_event_buf + sz);\n            return sz;\n            break;\n        default:\n            if (x > 223 || y > 223) return 0;\n            else {\n                mouse_event_buf[0] = 'M'; mouse_event_buf[1] = cb + 32; mouse_event_buf[2] = x + 32; mouse_event_buf[3] = y + 32;\n                return 4;\n            }\n            break;\n    }\n    return 0;\n}\n\nstatic int\nencode_mouse_event(Window *w, int button, MouseAction action, int mods) {\n    Screen *screen = w->render_data.screen;\n    return encode_mouse_event_impl(&w->mouse_pos, screen->modes.mouse_tracking_protocol, button, action, mods);\n}\n\nstatic int\nencode_mouse_button(Window *w, int button, MouseAction action, int mods) {\n    if (button == GLFW_MOUSE_BUTTON_LEFT) {\n        switch(action) {\n            case PRESS:\n                global_state.tracked_drag_in_window = w->id;\n                global_state.tracked_drag_button = button;\n                break;\n            case RELEASE:\n                global_state.tracked_drag_in_window = 0;\n                global_state.tracked_drag_button = -1;\n                break;\n            default:\n                break;\n        }\n    }\n    return encode_mouse_event(w, button_map(button), action, mods);\n}\n\nstatic int\nencode_mouse_scroll(Window *w, int button, int mods) {\n    return encode_mouse_event(w, button, PRESS, mods);\n}\n\n// }}}\n\nstatic Window*\nwindow_for_id(id_type window_id) {\n    if (global_state.callback_os_window && global_state.callback_os_window->num_tabs) {\n        Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;\n        for (unsigned int i = 0; i < t->num_windows; i++) {\n            Window *w = t->windows + i;\n            if (w->id == window_id) return w;\n        }\n    }\n    return window_for_window_id(window_id);\n}\n\nstatic void\nupdate_scrollbar_hover_state(Window *w, bool hovering) {\n    if (!w) return;\n    bool changed = w->scrollbar.is_hovering != hovering;\n    w->scrollbar.is_hovering = hovering;\n\n    if (changed && global_state.callback_os_window) {\n        global_state.callback_os_window->needs_render = true;\n        request_tick_callback();\n    }\n}\n\nstatic void\nset_currently_hovered_window(id_type window_id, int modifiers) {\n    if (global_state.mouse_hover_in_window != window_id) {\n        Window *left_window = window_for_id(global_state.mouse_hover_in_window);\n        global_state.mouse_hover_in_window = window_id;\n        if (left_window) {\n            if (left_window->scrollbar.is_hovering) update_scrollbar_hover_state(left_window, false);\n            if (left_window->render_data.screen) screen_mark_url(left_window->render_data.screen, 0, 0, 0, 0);\n            int sz = encode_mouse_event(left_window, 0, LEAVE, modifiers);\n            if (sz > 0) {\n                mouse_event_buf[sz] = 0;\n                write_escape_code_to_child(left_window->render_data.screen, ESC_CSI, mouse_event_buf);\n                debug(\"Sent mouse leave event to window: %llu\\n\", left_window->id);\n            }\n        }\n    }\n}\n\nstatic bool\ndispatch_mouse_event(Window *w, int button, int count, int modifiers, bool grabbed) {\n    bool handled = false;\n    if (w->render_data.screen && w->render_data.screen->callbacks != Py_None) {\n        PyObject *callback_ret = PyObject_CallMethod(w->render_data.screen->callbacks, \"on_mouse_event\", \"{si si si sO}\",\n            \"button\", button, \"repeat_count\", count, \"mods\", modifiers, \"grabbed\", grabbed ? Py_True : Py_False);\n        if (callback_ret == NULL) PyErr_Print();\n        else {\n            handled = callback_ret == Py_True;\n            Py_DECREF(callback_ret);\n        }\n        if (OPT(debug_keyboard)) {\n            const char *evname = \"move\";\n            switch(count) {\n                case -3: evname = \"doubleclick\"; break;\n                case -2: evname = \"click\"; break;\n                case -1: evname = \"release\"; break;\n                case 1: evname = \"press\"; break;\n                case 2: evname = \"doublepress\"; break;\n                case 3: evname = \"triplepress\"; break;\n            }\n            const char *bname = \"unknown\";\n            switch(button) {\n                case GLFW_MOUSE_BUTTON_LEFT: bname = \"left\"; break;\n                case GLFW_MOUSE_BUTTON_MIDDLE: bname = \"middle\"; break;\n                case GLFW_MOUSE_BUTTON_RIGHT: bname = \"right\"; break;\n                case GLFW_MOUSE_BUTTON_4: bname = \"b4\"; break;\n                case GLFW_MOUSE_BUTTON_5: bname = \"b5\"; break;\n                case GLFW_MOUSE_BUTTON_6: bname = \"b6\"; break;\n                case GLFW_MOUSE_BUTTON_7: bname = \"b7\"; break;\n                case GLFW_MOUSE_BUTTON_8: bname = \"b8\"; break;\n            }\n            debug(\"\\x1b[33mon_mouse_input\\x1b[m: %s button: %s %sgrabbed: %d handled_in_kitty: %d\\n\", evname, bname, format_mods(modifiers), grabbed, handled);\n        }\n    }\n    return handled;\n}\n\nstatic unsigned int\nwindow_left(Window *w) {\n    return w->render_data.geometry.left - w->padding.left;\n}\n\nstatic unsigned int\nwindow_right(Window *w) {\n    return w->render_data.geometry.right + w->padding.right;\n}\n\nstatic unsigned int\nwindow_top(Window *w) {\n    return w->render_data.geometry.top - w->padding.top;\n}\n\nstatic unsigned int\nwindow_bottom(Window *w) {\n    return w->render_data.geometry.bottom + w->padding.bottom;\n}\n\nstatic bool\ncontains_mouse(Window *w) {\n    double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;\n    return (w->visible && window_left(w) <= x && x < window_right(w) && window_top(w) <= y && y < window_bottom(w));\n}\n\nstatic bool\nborder_contains_mouse(BorderRect *br, double tolerance, Edge *edges) {\n    bool ans = false;\n    double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;\n    if ((int)br->px.left - tolerance <= x && x < (int)br->px.right + tolerance && (int)br->px.top - tolerance <= y && y < (int)br->px.bottom + tolerance) {\n        ans = true;\n        if (!br->horizontal) *edges |= br->border_type < 0 ? LEFT_EDGE : RIGHT_EDGE;\n        else *edges |= br->border_type < 0 ? TOP_EDGE : BOTTOM_EDGE;\n    }\n    return ans;\n}\n\n\nstatic double\ndistance_to_window(Window *w) {\n    double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;\n    double cx = (window_left(w) + window_right(w)) / 2.0;\n    double cy = (window_top(w) + window_bottom(w)) / 2.0;\n    return (x - cx) * (x - cx) + (y - cy) * (y - cy);\n}\n\nstatic bool clamp_to_window = false;\n\nstatic bool\ncell_for_pos(Window *w, unsigned int *x, unsigned int *y, bool *in_left_half_of_cell, OSWindow *os_window) {\n    WindowGeometry *g = &w->render_data.geometry;\n    Screen *screen = w->render_data.screen;\n    if (!screen) return false;\n    unsigned int qx = 0, qy = 0;\n    bool in_left_half = true;\n    double mouse_x = global_state.callback_os_window->mouse_x;\n    double mouse_y = global_state.callback_os_window->mouse_y;\n    double left = g->left, top = g->top, right = g->right, bottom = g->bottom;\n    w->mouse_pos.global_x = mouse_x - left; w->mouse_pos.global_y = mouse_y - top;\n    if (clamp_to_window) {\n        mouse_x = MIN(MAX(mouse_x, left), right);\n        mouse_y = MIN(MAX(mouse_y, top), bottom);\n    }\n    if (mouse_x < left || mouse_y < top || mouse_x > right || mouse_y > bottom) return false;\n    if (mouse_x >= g->right) {\n        qx = screen->columns - 1;\n        in_left_half = false;\n    } else if (mouse_x >= g->left) {\n        double xval = (double)(mouse_x - g->left) / os_window->fonts_data->fcm.cell_width;\n        double fxval = floor(xval);\n        qx = (unsigned int)fxval;\n        in_left_half = (xval - fxval <= 0.5) ? true : false;\n    }\n    if (mouse_y >= g->bottom) qy = screen->lines - 1;\n    else if (mouse_y >= g->top) {\n        qy = (unsigned int)((double)(mouse_y - g->top - screen->pixel_scroll_offset_y) / os_window->fonts_data->fcm.cell_height);\n    }\n    if (qx < screen->columns && qy < screen->lines) {\n        *x = qx; *y = qy;\n        *in_left_half_of_cell = in_left_half;\n        return true;\n    }\n    return false;\n}\n\n#define HANDLER(name) static void name(Window UNUSED *w, int UNUSED button, int UNUSED modifiers, unsigned int UNUSED window_idx)\n\nstatic void\nset_mouse_cursor_when_dragging(Screen *screen) {\n    MouseShape expected_shape = OPT(pointer_shape_when_dragging);\n    if (screen && screen->selections.count && screen->selections.items[0].rectangle_select) expected_shape = OPT(pointer_shape_when_dragging_rectangle);\n    if (mouse_cursor_shape != expected_shape) {\n        mouse_cursor_shape = expected_shape;\n        set_mouse_cursor(mouse_cursor_shape);\n    }\n}\n\nstatic void\nupdate_drag(Window *w) {\n    Screen *screen = w->render_data.screen;\n    if (screen && screen->selections.in_progress) {\n        screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, (SelectionUpdate){0});\n    }\n    set_mouse_cursor_when_dragging(screen);\n}\n\nstatic bool\ndo_drag_scroll(Window *w, bool upwards) {\n    Screen *screen = w->render_data.screen;\n    if (screen->linebuf == screen->main_linebuf) {\n        screen_history_scroll(screen, SCROLL_LINE, upwards);\n        update_drag(w);\n        if (mouse_cursor_shape != DEFAULT_POINTER) {\n            mouse_cursor_shape = DEFAULT_POINTER;\n            set_mouse_cursor(mouse_cursor_shape);\n        }\n        return true;\n    }\n    return false;\n}\n\nbool\ndrag_scroll(Window *w, OSWindow *frame) {\n    unsigned int margin = frame->fonts_data->fcm.cell_height / 2;\n    double y = frame->mouse_y;\n    bool upwards = y <= (w->render_data.geometry.top + margin);\n    if (upwards || y >= w->render_data.geometry.bottom - margin) {\n        if (do_drag_scroll(w, upwards)) {\n            frame->last_mouse_activity_at = monotonic();\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic void\nextend_selection(Window *w, bool ended, bool extend_nearest) {\n    Screen *screen = w->render_data.screen;\n    if (screen_has_selection(screen)) {\n        screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell,\n                (SelectionUpdate){.ended=ended, .set_as_nearest_extend=extend_nearest});\n    }\n}\n\nstatic void\nset_mouse_cursor_for_screen(Screen *screen) {\n    MouseShape s = screen_pointer_shape(screen);\n    if (s != INVALID_POINTER) {\n        mouse_cursor_shape = s;\n    } else {\n        if (screen->modes.mouse_tracking_mode == NO_TRACKING) {\n            mouse_cursor_shape = OPT(default_pointer_shape);\n        } else {\n            mouse_cursor_shape = OPT(pointer_shape_when_grabbed);\n        }\n    }\n}\n\nstatic void\nhandle_mouse_movement_in_kitty(Window *w, int button, bool mouse_cell_changed) {\n    Screen *screen = w->render_data.screen;\n    if (screen->selections.in_progress && (button == global_state.active_drag_button)) {\n        monotonic_t now = monotonic();\n        if ((now - w->last_drag_scroll_at) >= ms_to_monotonic_t(20ll) || mouse_cell_changed) {\n            update_drag(w);\n            w->last_drag_scroll_at = now;\n        }\n    }\n\n}\n\nstatic void\ndetect_url(Screen *screen, unsigned int x, unsigned int y) {\n    int hid = screen_detect_url(screen, x, y);\n    screen->current_hyperlink_under_mouse.id = 0;\n    if (hid != 0) {\n        mouse_cursor_shape = POINTER_POINTER;\n        if (hid > 0) {\n            screen->current_hyperlink_under_mouse.id = (hyperlink_id_type)hid;\n            screen->current_hyperlink_under_mouse.x = x;\n            screen->current_hyperlink_under_mouse.y = y;\n        }\n    } else set_mouse_cursor_for_screen(screen);\n}\n\nstatic bool\nshould_handle_in_kitty(Window *w, Screen *screen, int button) {\n    bool in_tracking_mode = (\n        screen->modes.mouse_tracking_mode == ANY_MODE ||\n        (screen->modes.mouse_tracking_mode == MOTION_MODE && button >= 0));\n    return !in_tracking_mode || global_state.active_drag_in_window == w->id;\n}\n\nstatic bool\nset_mouse_position(Window *w, bool *mouse_cell_changed, bool *cell_half_changed) {\n    unsigned int x = 0, y = 0;\n    bool in_left_half_of_cell = false;\n    if (!cell_for_pos(w, &x, &y, &in_left_half_of_cell, global_state.callback_os_window)) return false;\n    *mouse_cell_changed = x != w->mouse_pos.cell_x || y != w->mouse_pos.cell_y;\n    *cell_half_changed = in_left_half_of_cell != w->mouse_pos.in_left_half_of_cell;\n    w->mouse_pos.cell_x = x; w->mouse_pos.cell_y = y;\n    w->mouse_pos.in_left_half_of_cell = in_left_half_of_cell;\n    return true;\n}\n\n// Scrollbar {{{\ntypedef enum {\n    SCROLLBAR_HIT_NONE,\n    SCROLLBAR_HIT_TRACK,\n    SCROLLBAR_HIT_THUMB\n} ScrollbarHitType;\n\ntypedef struct {\n    double left, right, top, bottom;\n    double width, gap, hitbox_expansion;\n} ScrollbarGeometry;\n\n\nstatic bool\nvalidate_scrollbar_state(const Window *w) {\n    return w && w->render_data.screen &&\n           w->render_data.screen->historybuf &&\n           w->render_data.screen->historybuf->count > 0;\n}\n\nstatic ScrollbarGeometry\ncalculate_scrollbar_geometry(const Window *w) {\n    ScrollbarGeometry geom = {0};\n    if (!w || !w->render_data.screen) return geom;\n\n    const WindowGeometry *g = &w->render_data.geometry;\n    unsigned cell_width = w->render_data.screen->cell_size.width;\n    geom.width = (double)OPT(scrollbar_width) * cell_width;\n    if (w->scrollbar.is_hovering) geom.width = (double)OPT(scrollbar_hover_width) * cell_width;\n    geom.gap = (double)OPT(scrollbar_gap) * cell_width;\n    geom.hitbox_expansion = (double)OPT(scrollbar_hitbox_expansion) * cell_width;\n\n    double right_edge = g->right + g->spaces.right;\n    geom.left = right_edge - geom.gap - geom.width - geom.hitbox_expansion;\n    geom.right = right_edge + geom.gap;\n    geom.top = g->top - g->spaces.top;\n    geom.bottom = g->bottom + g->spaces.bottom;\n\n    return geom;\n}\n\nstatic ScrollbarHitType\nget_scrollbar_hit_type(const Window *w, double mouse_x, double mouse_y) {\n    if (!w || !validate_scrollbar_state(w)) return SCROLLBAR_HIT_NONE;\n\n    ScrollbarGeometry geom = calculate_scrollbar_geometry(w);\n\n    if (mouse_x < geom.left || mouse_x > geom.right ||\n        mouse_y < geom.top || mouse_y > geom.bottom) {\n        return SCROLLBAR_HIT_NONE;\n    }\n\n    OSWindow *os_window = global_state.callback_os_window;\n    if (!os_window) return SCROLLBAR_HIT_TRACK;\n    double mouse_window_fraction = mouse_y / os_window->viewport_height;\n    unsigned cell_width = w->render_data.screen->cell_size.width;\n    double hitbox_expansion_fraction = (double)(OPT(scrollbar_hitbox_expansion) * cell_width) / os_window->viewport_height;\n\n    if (mouse_window_fraction >= (w->scrollbar.thumb_top - hitbox_expansion_fraction) &&\n        mouse_window_fraction <= (w->scrollbar.thumb_bottom + hitbox_expansion_fraction)) {\n        return SCROLLBAR_HIT_THUMB;\n    }\n\n    return SCROLLBAR_HIT_TRACK;\n}\n\nstatic void\nhandle_scrollbar_track_click(Window *w, double mouse_y) {\n    if (!w) return;\n    Screen *screen = w->render_data.screen;\n    if (!validate_scrollbar_state(w)) return;\n\n    if (OPT(scrollbar_jump_on_click)) {\n        ScrollbarGeometry geom = calculate_scrollbar_geometry(w);\n        double scrollbar_height = geom.bottom - geom.top;\n        double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;\n        double target_scrolled_by = screen->historybuf->count * (1.0 - mouse_pane_fraction);\n        screen_history_scroll_to_absolute(screen, target_scrolled_by);\n    } else {\n        OSWindow *os_window = global_state.callback_os_window;\n        if (!os_window) return;\n        double mouse_window_fraction = mouse_y / os_window->viewport_height;\n        bool click_above_thumb = mouse_window_fraction < w->scrollbar.thumb_top;\n        screen_history_scroll(screen, SCROLL_PAGE, click_above_thumb);\n    }\n}\n\nstatic void\nend_drag(Window *w) {\n    Screen *screen = w->render_data.screen;\n    global_state.active_drag_in_window = 0;\n    global_state.active_drag_button = -1;\n    w->last_drag_scroll_at = 0;\n    w->scrollbar.is_dragging = false;\n\n    if (global_state.callback_os_window &&\n        get_scrollbar_hit_type(w,\n            global_state.callback_os_window->mouse_x,\n            global_state.callback_os_window->mouse_y\n        ) == SCROLLBAR_HIT_NONE) {\n        mouse_cursor_shape = TEXT_POINTER;\n        set_mouse_cursor(mouse_cursor_shape);\n    }\n\n    if (screen->selections.in_progress) {\n        screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, (SelectionUpdate){.ended=true});\n    }\n}\n\n\nstatic void\nstart_scrollbar_drag(Window *w, double mouse_y) {\n    if (!w) return;\n    Screen *screen = w->render_data.screen;\n    if (!validate_scrollbar_state(w)) return;\n\n    ScrollbarGeometry geom = calculate_scrollbar_geometry(w);\n    double scrollbar_height = geom.bottom - geom.top;\n    double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;\n    w->scrollbar.is_dragging = true;\n    w->scrollbar.drag_start_y = mouse_pane_fraction;\n    w->scrollbar.drag_start_scrolled_by = screen->scrolled_by;\n}\n\nstatic void\nhandle_scrollbar_drag(Window *w, double mouse_y) {\n    if (!w || !w->scrollbar.is_dragging || !validate_scrollbar_state(w)) return;\n    Screen *screen = w->render_data.screen;\n    ScrollbarGeometry geom = calculate_scrollbar_geometry(w);\n    double scrollbar_height = geom.bottom - geom.top;\n    double mouse_pane_fraction = (mouse_y - geom.top) / scrollbar_height;\n    double delta_y = mouse_pane_fraction - w->scrollbar.drag_start_y;\n    double visible_fraction = (double)screen->lines / (screen->lines + screen->historybuf->count);\n    unsigned cell_height = screen->cell_size.height;\n    double min_thumb_height_fraction = ((double)OPT(scrollbar_min_handle_height) * cell_height) / scrollbar_height;\n    double thumb_height = MAX(min_thumb_height_fraction, visible_fraction);\n    double available_space = 1.0 - thumb_height;\n\n    if (available_space > 0) {\n        double scroll_fraction = delta_y / available_space;\n        double target = w->scrollbar.drag_start_scrolled_by - scroll_fraction * screen->historybuf->count;\n        double new_scrolled_by;\n        if (target < 0) new_scrolled_by = 0;\n        else if (target > screen->historybuf->count) new_scrolled_by = screen->historybuf->count;\n        else new_scrolled_by = target;\n        screen_history_scroll_to_absolute(screen, new_scrolled_by);\n    }\n}\n\nstatic const MouseShape scrollbar_drag_mouse_cursor = NS_RESIZE_POINTER;\n\nstatic bool\nhandle_scrollbar_mouse(Window *w, int button, MouseAction action, int modifiers UNUSED) {\n    if (!w || !OPT(scrollbar_interactive) || !global_state.callback_os_window) return false;\n\n    double mouse_x = global_state.callback_os_window->mouse_x;\n    double mouse_y = global_state.callback_os_window->mouse_y;\n\n    if (action == MOVE && w->scrollbar.is_dragging) {\n        handle_scrollbar_drag(w, mouse_y);\n        mouse_cursor_shape = scrollbar_drag_mouse_cursor;\n        set_mouse_cursor(mouse_cursor_shape);\n        return true;\n    }\n    if (global_state.active_drag_in_window == w->id || global_state.tracked_drag_in_window == w->id) return false;\n\n    ScrollbarHitType hit_type = get_scrollbar_hit_type(w, mouse_x, mouse_y);\n    bool hovering = (hit_type != SCROLLBAR_HIT_NONE);\n    update_scrollbar_hover_state(w, hovering);\n\n    if (!hovering) return false;\n\n    mouse_cursor_shape = DEFAULT_POINTER;\n    set_mouse_cursor(mouse_cursor_shape);\n\n    if (button == GLFW_MOUSE_BUTTON_LEFT && action != MOVE) {\n        bool is_release = (action == RELEASE);\n\n        if (is_release) {\n            if (w->scrollbar.is_dragging) {\n                end_drag(w);\n            } else if (hit_type == SCROLLBAR_HIT_TRACK) {\n                handle_scrollbar_track_click(w, mouse_y);\n            }\n        } else {\n            if (hit_type == SCROLLBAR_HIT_THUMB) {\n                start_scrollbar_drag(w, mouse_y);\n                global_state.active_drag_in_window = w->id;\n                global_state.active_drag_button = button;\n            }\n        }\n    }\n\n    return true;\n}\n// }}}\n\n\nHANDLER(handle_move_event) {\n    modifiers &= ~GLFW_LOCK_MASK;\n\n    if (handle_scrollbar_mouse(w, -1, MOVE, modifiers)) return;\n\n    if (OPT(focus_follows_mouse)) {\n        Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;\n        if (window_idx != t->active_window) {\n            call_boss(switch_focus_to_in_active_tab, \"K\", t->windows[window_idx].id);\n        }\n    }\n    bool mouse_cell_changed = false;\n    bool cell_half_changed = false;\n    if (!set_mouse_position(w, &mouse_cell_changed, &cell_half_changed)) {\n        if (w->scrollbar.is_hovering) {\n            update_scrollbar_hover_state(w, false);\n        }\n        return;\n    }\n    Screen *screen = w->render_data.screen;\n    if (OPT(detect_urls)) detect_url(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y);\n    if (should_handle_in_kitty(w, screen, button)) {\n        handle_mouse_movement_in_kitty(w, button, mouse_cell_changed | cell_half_changed);\n    } else {\n        if (!mouse_cell_changed && screen->modes.mouse_tracking_protocol != SGR_PIXEL_PROTOCOL) return;\n        int sz = encode_mouse_button(w, button, button >=0 ? DRAG : MOVE, modifiers);\n        if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf); }\n    }\n}\n\nstatic double\ndistance(double x1, double y1, double x2, double y2) {\n    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));\n}\n\nstatic void\nclear_click_queue(Window *w, int button) {\n    if (0 <= button && button <= (ssize_t)arraysz(w->click_queues)) w->click_queues[button].length = 0;\n}\n\n#define N(n) (q->clicks[q->length - n])\n\nstatic double\nradius_for_multiclick(void) {\n    return 0.5 * (global_state.callback_os_window ? global_state.callback_os_window->fonts_data->fcm.cell_height : 8);\n}\n\nstatic bool\nrelease_is_click(const Window *w, int button) {\n    const ClickQueue *q = &w->click_queues[button];\n    monotonic_t now = monotonic();\n    return (q->length > 0 && distance(N(1).x, N(1).y, MAX(0, w->mouse_pos.global_x), MAX(0, w->mouse_pos.global_y)) <= radius_for_multiclick() && now - N(1).at < OPT(click_interval));\n}\n\nstatic unsigned\nmulti_click_count(const Window *w, int button) {\n    const ClickQueue *q = &w->click_queues[button];\n    double multi_click_allowed_radius = radius_for_multiclick();\n    if (q->length > 2) {\n        // possible triple-click\n        if (\n                N(1).at - N(3).at <= 2 * OPT(click_interval) &&\n                distance(N(1).x, N(1).y, N(3).x, N(3).y) <= multi_click_allowed_radius\n           ) return 3;\n    }\n    if (q->length > 1) {\n        // possible double-click\n        if (\n                N(1).at - N(2).at <= OPT(click_interval) &&\n                distance(N(1).x, N(1).y, N(2).x, N(2).y) <= multi_click_allowed_radius\n           ) return 2;\n    }\n    return q->length ? 1 : 0;\n}\n\n\nstatic void\nadd_press(Window *w, int button, int modifiers) {\n    if (button < 0 || button >= (ssize_t)arraysz(w->click_queues)) return;\n    modifiers &= ~GLFW_LOCK_MASK;\n    ClickQueue *q = &w->click_queues[button];\n    if (q->length == CLICK_QUEUE_SZ) { memmove(q->clicks, q->clicks + 1, sizeof(Click) * (CLICK_QUEUE_SZ - 1)); q->length--; }\n    monotonic_t now = monotonic();\n    static unsigned long num = 0;\n    N(0).at = now; N(0).button = button; N(0).modifiers = modifiers; N(0).x = MAX(0, w->mouse_pos.global_x); N(0).y = MAX(0, w->mouse_pos.global_y); N(0).num = ++num;\n    q->length++;\n    Screen *screen = w->render_data.screen;\n    int count = multi_click_count(w, button);\n    if (count > 1) {\n        if (screen) dispatch_mouse_event(w, button, count, modifiers, screen->modes.mouse_tracking_mode != 0);\n        if (count > 2) q->length = 0;\n    }\n}\n#undef N\n\nbool\nmouse_open_url(Window *w) {\n    Screen *screen = w->render_data.screen;\n    detect_url(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y);\n    return screen_open_url(screen);\n}\n\nbool\nmouse_set_last_visited_cmd_output(Window *w) {\n    Screen *screen = w->render_data.screen;\n    return screen_set_last_visited_prompt(screen, w->mouse_pos.cell_y);\n}\n\nbool\nmouse_select_cmd_output(Window *w) {\n    Screen *screen = w->render_data.screen;\n    return screen_select_cmd_output(screen, w->mouse_pos.cell_y);\n}\n\nbool\nmove_cursor_to_mouse_if_at_shell_prompt(Window *w) {\n    Screen *screen = w->render_data.screen;\n    int y = screen_cursor_at_a_shell_prompt(screen);\n    if (y < 0 || (unsigned)y > w->mouse_pos.cell_y) return false;\n\n    bool is_relative;\n    if (screen_prompt_supports_click_events(screen, &is_relative)) {\n        MousePosition mpos = w->mouse_pos;\n        if (is_relative) mpos.cell_y -= y;\n        int sz = encode_mouse_event_impl(&mpos, SGR_PROTOCOL, 1, PRESS, 0);\n        if (sz > 0) {\n            mouse_event_buf[sz] = 0;\n            write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf);\n            return true;\n        }\n        return false;\n    } else {\n        return screen_fake_move_cursor_to_position(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y);\n    }\n}\n\n\nvoid\nsend_pending_click_to_window(Window *w, int i) {\n    const id_type wid = w->id;\n    if (i < 0) {\n        while(true) {\n            w = window_for_id(wid);\n            if (!w || !w->pending_clicks.num) break;\n            send_pending_click_to_window(w, w->pending_clicks.num - 1);\n        }\n        return;\n    }\n    PendingClick pc = w->pending_clicks.clicks[i];\n    remove_i_from_array(w->pending_clicks.clicks, (unsigned)i, w->pending_clicks.num);\n    const ClickQueue *q = &w->click_queues[pc.button];\n    // only send click if no presses have happened since the release that triggered\n    // the click or if the subsequent press is too far or too late for a double click\n    if (!q->length) return;\n#define press(n) q->clicks[q->length - n]\n    if (\n            press(1).at <= pc.at || // latest press is before click release\n            (q->length > 1 && press(2).num == pc.press_num &&  (   // penultimate press is the press that belongs to this click\n                press(1).at - press(2).at > OPT(click_interval) ||  // too long between the presses for it to be a double click\n                distance(press(1).x, press(1).y, press(2).x, press(2).y) > pc.radius_for_multiclick  // presses are too far apart\n            ))\n    ) {\n        MousePosition current_pos = w->mouse_pos;\n        w->mouse_pos = pc.mouse_pos;\n        dispatch_mouse_event(w, pc.button, pc.count, pc.modifiers, pc.grabbed);\n        w = window_for_id(wid);\n        if (w) w->mouse_pos = current_pos;\n    }\n#undef press\n}\n\nstatic void\ndispatch_possible_click(Window *w, int button, int modifiers) {\n    Screen *screen = w->render_data.screen;\n    int count = multi_click_count(w, button);\n    if (release_is_click(w, button)) {\n        ensure_space_for(&(w->pending_clicks), clicks, PendingClick, w->pending_clicks.num + 1, capacity, 4, true);\n        PendingClick *pc = w->pending_clicks.clicks + w->pending_clicks.num++;\n        zero_at_ptr(pc);\n        const ClickQueue *q = &w->click_queues[button];\n        pc->press_num = q->length ? q->clicks[q->length - 1].num : 0;\n        pc->window_id = w->id;\n        pc->mouse_pos = w->mouse_pos;\n        pc->at = monotonic();\n        pc->button = button;\n        pc->count = count == 2 ? -3 : -2;\n        pc->modifiers = modifiers;\n        pc->grabbed = screen->modes.mouse_tracking_mode != 0;\n        pc->radius_for_multiclick = radius_for_multiclick();\n        add_main_loop_timer(OPT(click_interval), false, dispatch_pending_clicks, NULL, NULL);\n    }\n}\n\nHANDLER(handle_button_event) {\n    modifiers &= ~GLFW_LOCK_MASK;\n    OSWindow *osw = global_state.callback_os_window;\n    if (!osw) return;\n\n    Tab *t = osw->tabs + osw->active_tab;\n    bool is_release = !osw->mouse_button_pressed[button];\n\n    if (button == GLFW_MOUSE_BUTTON_LEFT && osw->suppress_left_mouse_release) {\n        osw->suppress_left_mouse_release = false;\n        if (is_release) return;\n    }\n\n    if (handle_scrollbar_mouse(w, button, is_release ? RELEASE : PRESS, modifiers)) return;\n\n    bool suppress_child_forwarding = false;\n    if (osw->is_focused && window_idx != t->active_window && !is_release) {\n        call_boss(switch_focus_to_in_active_tab, \"K\", t->windows[window_idx].id);\n        if (button == GLFW_MOUSE_BUTTON_LEFT) {\n            // Treat split-focus transfer clicks as focus-only for child processes:\n            // suppress forwarding the left press and matching release to the child\n            // to avoid release-without-press reports. Still allow kitty to process\n            // the event internally (e.g., start text selection via click-and-drag).\n            osw->suppress_left_mouse_release = true;\n            suppress_child_forwarding = true;\n        }\n    }\n\n    Screen *screen = w->render_data.screen;\n    if (!screen) return;\n\n    bool a, b;\n    if (!set_mouse_position(w, &a, &b)) return;\n    id_type wid = w->id;\n    if (!dispatch_mouse_event(w, button, is_release ? -1 : 1, modifiers, screen->modes.mouse_tracking_mode != 0)) {\n        if (!suppress_child_forwarding && screen->modes.mouse_tracking_mode != 0) {\n            int sz = encode_mouse_button(w, button, is_release ? RELEASE : PRESS, modifiers);\n            if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf); }\n        }\n    }\n    // the windows array might have been re-alloced in dispatch_mouse_event\n    w = NULL;\n    for (size_t i = 0; i < t->num_windows && !w; i++) if (t->windows[i].id == wid) w = t->windows + i;\n    if (w) {\n        if (is_release) dispatch_possible_click(w, button, modifiers);\n        else add_press(w, button, modifiers);\n    }\n}\n\nstatic int\ncurrently_pressed_button(void) {\n    for (int i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) {\n        if (global_state.callback_os_window->mouse_button_pressed[i]) return i;\n    }\n    return -1;\n}\n\nHANDLER(handle_event) {\n    modifiers &= ~GLFW_LOCK_MASK;\n    set_mouse_cursor_for_screen(w->render_data.screen);\n    set_currently_hovered_window(w->id, modifiers);\n    if (button == -1) {\n        button = currently_pressed_button();\n        handle_move_event(w, button, modifiers, window_idx);\n    } else {\n        handle_button_event(w, button, modifiers, window_idx);\n    }\n}\n\nstatic void\nhandle_window_title_bar_mouse(Window *w, int button, int modifiers, int action) {\n    OSWindow *osw = global_state.callback_os_window;\n    if (osw && button > -1) {\n        call_boss(handle_window_title_bar_mouse, \"KKiii\", osw->id, w->id, button, modifiers, action);\n    }\n}\n\nstatic void\nhandle_tab_bar_mouse(int button, int modifiers, int action) {\n    set_currently_hovered_window(0, modifiers);\n    OSWindow *w = global_state.callback_os_window;\n    // dont report motion events, as they are expensive and useless\n    if (w && (button > -1 || global_state.tab_being_dragged.id)) {\n        call_boss(handle_tab_bar_mouse, \"Kddiii\", w->id, w->mouse_x, w->mouse_y, button, modifiers, action);\n    }\n}\n\nstatic bool\nmouse_in_region(Region *r) {\n    if (r->left == r->right) return false;\n    if (global_state.callback_os_window->mouse_y < r->top || global_state.callback_os_window->mouse_y >= r->bottom) return false;\n    if (global_state.callback_os_window->mouse_x < r->left || global_state.callback_os_window->mouse_x >= r->right) return false;\n    return true;\n}\n\nstatic unsigned\nnum_visible_windows(Tab *t) {\n    unsigned ans = t->num_windows;\n    for (unsigned i = 0; i < t->num_windows; i++) if (!t->windows[i].visible) ans--;\n    return ans;\n}\n\ntypedef struct MouseRegion {\n    unsigned window_idx;\n    bool in_tab_bar;\n    bool in_title_bar;\n    Edge window_border;\n    Window *window;\n} MouseRegion;\n\nstatic MouseRegion\nmouse_region(bool detect_borders, bool detect_title_bar) {\n    MouseRegion ans = {0};\n    Region central, tab_bar;\n    const OSWindow* w = global_state.callback_os_window;\n    os_window_regions(w, &central, &tab_bar);\n    const bool in_central = mouse_in_region(&central);\n    if (!in_central) {\n        if (\n                (tab_bar.top < central.top && w->mouse_y < central.top) ||\n                (tab_bar.bottom > central.bottom && w->mouse_y >= central.bottom)\n           ) ans.in_tab_bar = true;\n    }\n    if (in_central && w->num_tabs > 0) {\n        Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;\n        if (detect_borders && num_visible_windows(t) > 1) {\n            id_type window_id = 0;\n            double dpi = (w->fonts_data->logical_dpi_x + w->fonts_data->logical_dpi_y) / 2.;\n            double tolerance = ((long)round((OPT(window_drag_tolerance) * (dpi / 72.0))));\n            BorderRect *closest_vert = NULL, *closest_horiz = NULL;\n            double closest_vert_dist = (double)UINT_MAX, closest_horiz_dist = (double)UINT_MAX;\n            bool is_within_border_without_tolerance = false;\n            for (unsigned i = 0; i < t->border_rects.num_border_rects; i++) {\n                BorderRect *br = t->border_rects.rect_buf + i;\n                if (!br->border_type) continue;\n                Edge edges = 0;\n                if (border_contains_mouse(br, 0, &edges)) {\n                    ans.window_border |= edges;\n                    is_within_border_without_tolerance = true;\n                    if (edges & (LEFT_EDGE | RIGHT_EDGE)) { closest_vert_dist = -1; closest_vert = NULL; }\n                    else { closest_horiz_dist = -1; closest_horiz = NULL; }\n                    window_id = br->border_type < 0 ? -br->border_type : br->border_type;\n                } else if (border_contains_mouse(br, tolerance, &edges)) {\n                    unsigned width = br->px.right - br->px.left, height = br->px.bottom - br->px.top;\n                    if (!br->horizontal) {\n                        double d = br->px.left + width/2. - w->mouse_x; d = d*d;\n                        if (d < closest_vert_dist) { closest_vert_dist = d; closest_vert = br; }\n                    } else {\n                        double d = br->px.top + height/2. - w->mouse_y; d = d*d;\n                        if (d < closest_horiz_dist) { closest_horiz_dist = d; closest_horiz = br; }\n                    }\n                }\n            }\n            if (!is_within_border_without_tolerance) {\n                if (closest_vert && border_contains_mouse(closest_vert, tolerance, &ans.window_border) && !window_id)\n                    window_id = closest_vert->border_type < 0 ? -closest_vert->border_type : closest_vert->border_type;\n                if (closest_horiz && border_contains_mouse(closest_horiz, tolerance, &ans.window_border) && !window_id)\n                    window_id = closest_horiz->border_type < 0 ? -closest_horiz->border_type : closest_horiz->border_type;\n            }\n            if (ans.window_border) {\n                if (window_id) {\n                    for (unsigned int i = 0; i < t->num_windows; i++)\n                        if (t->windows[i].id == window_id) {\n                            ans.window = t->windows + i;\n                            ans.window_idx = i;\n                            break;\n                        }\n                }\n                return ans;\n            }\n        }\n        for (unsigned int i = 0; i < t->num_windows; i++) {\n            Window *win = t->windows + i;\n            if (contains_mouse(win) && win->render_data.screen) {\n                ans.window_idx = i; ans.window = win; break;\n            } else if (detect_title_bar && win->visible) {\n                const WindowRenderData *trd = &win->window_title_render_data;\n                if (trd->screen && trd->geometry.right > trd->geometry.left && trd->geometry.bottom > trd->geometry.top) {\n                    if (w->mouse_x >= trd->geometry.left && w->mouse_x < trd->geometry.right &&\n                            w->mouse_y >= trd->geometry.top && w->mouse_y < trd->geometry.bottom) {\n                        ans.in_title_bar = true; ans.window = win; ans.window_idx = i;\n                        break;\n                    }\n                }\n            }\n        }\n    }\n    return ans;\n}\n\nstatic Window*\nclosest_window_for_event(unsigned int *window_idx) {\n    Window *ans = NULL;\n    double closest_distance = UINT_MAX;\n    if (global_state.callback_os_window->num_tabs > 0) {\n        Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;\n        for (unsigned int i = 0; i < t->num_windows; i++) {\n            Window *w = t->windows + i;\n            if (w->visible) {\n                double d = distance_to_window(w);\n                if (d < closest_distance) { ans = w; closest_distance = d; *window_idx = i; }\n            }\n        }\n    }\n    return ans;\n}\n\nvoid\nfocus_in_event(void) {\n    // Ensure that no URL is highlighted and the mouse cursor is in default shape\n    mouse_cursor_shape = TEXT_POINTER;\n    MouseRegion r = mouse_region(false, false);\n    if (r.window && r.window->render_data.screen) {\n        screen_mark_url(r.window->render_data.screen, 0, 0, 0, 0);\n        set_mouse_cursor_for_screen(r.window->render_data.screen);\n    }\n    set_mouse_cursor(mouse_cursor_shape);\n}\n\nvoid\nupdate_mouse_pointer_shape(void) {\n    mouse_cursor_shape = TEXT_POINTER;\n    MouseRegion r = mouse_region(false, true);\n    if (r.in_tab_bar) {\n        mouse_cursor_shape = POINTER_POINTER;\n    } else if (r.in_title_bar) {\n        mouse_cursor_shape = POINTER_POINTER;\n    } else if (r.window) {\n        if (handle_scrollbar_mouse(r.window, -1, MOVE, 0)) {\n            mouse_cursor_shape = scrollbar_drag_mouse_cursor;\n        } else if (r.window->render_data.screen) {\n            screen_mark_url(r.window->render_data.screen, 0, 0, 0, 0);\n            set_mouse_cursor_for_screen(r.window->render_data.screen);\n        }\n    }\n    set_mouse_cursor(mouse_cursor_shape);\n}\n\nvoid\nleave_event(int modifiers) {\n    if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;\n    set_currently_hovered_window(0, modifiers);\n}\n\nvoid\nenter_event(int modifiers) {\n#ifdef __APPLE__\n    // On cocoa there is no way to configure the window manager to\n    // focus windows on mouse enter, so we do it ourselves\n    if (OPT(focus_follows_mouse) && !global_state.callback_os_window->is_focused) {\n        id_type wid = global_state.callback_os_window->id;\n        focus_os_window(global_state.callback_os_window, false, NULL);\n        if (!global_state.callback_os_window) {\n            global_state.callback_os_window = os_window_for_id(wid);\n            if (!global_state.callback_os_window) return;\n        }\n    }\n#endif\n    // If the mouse is grabbed send a move event to update the cursor position\n    // since the last report.\n    if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;\n    MouseRegion r = mouse_region(false, false);\n    Window *w = r.window;\n    set_currently_hovered_window(w ? w->id : 0, modifiers);\n    if (!w || r.in_tab_bar || r.in_title_bar) return;\n\n    if (handle_scrollbar_mouse(w, -1, MOVE, modifiers)) return;\n\n    bool mouse_cell_changed = false, cell_half_changed = false;\n    if (!set_mouse_position(w, &mouse_cell_changed, &cell_half_changed)) return;\n    Screen *screen = w->render_data.screen;\n    int button = currently_pressed_button();\n    if (!screen || should_handle_in_kitty(w, screen, button)) return;\n    int sz = encode_mouse_button(w, button, button >=0 ? DRAG : MOVE, modifiers);\n    if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf); }\n}\n\n\ntypedef enum MouseSelectionType {\n    MOUSE_SELECTION_NORMAL,\n    MOUSE_SELECTION_EXTEND,\n    MOUSE_SELECTION_RECTANGLE,\n    MOUSE_SELECTION_WORD,\n    MOUSE_SELECTION_LINE,\n    MOUSE_SELECTION_LINE_FROM_POINT,\n    MOUSE_SELECTION_WORD_AND_LINE_FROM_POINT,\n    MOUSE_SELECTION_MOVE_END,\n    MOUSE_SELECTION_UPTO_SURROUNDING_WHITESPACE,\n} MouseSelectionType;\n\n\nvoid\nmouse_selection(Window *w, int code, int button) {\n    global_state.active_drag_in_window = w->id;\n    global_state.active_drag_button = button;\n    Screen *screen = w->render_data.screen;\n    index_type start, end;\n    unsigned int y1, y2;\n#define S(mode) {\\\n        screen_start_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, false, mode); \\\n        screen_update_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, (SelectionUpdate){.start_extended_selection=true}); }\n\n    switch((MouseSelectionType)code) {\n        case MOUSE_SELECTION_NORMAL:\n            screen_start_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, false, EXTEND_CELL);\n            break;\n        case MOUSE_SELECTION_RECTANGLE:\n            screen_start_selection(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, w->mouse_pos.in_left_half_of_cell, true, EXTEND_CELL);\n            break;\n        case MOUSE_SELECTION_WORD:\n            if (screen_selection_range_for_word(screen, w->mouse_pos.cell_x, w->mouse_pos.cell_y, &y1, &y2, &start, &end, true)) S(EXTEND_WORD);\n            break;\n        case MOUSE_SELECTION_LINE:\n            if (screen_selection_range_for_line(screen, w->mouse_pos.cell_y, &start, &end)) S(EXTEND_LINE);\n            break;\n        case MOUSE_SELECTION_LINE_FROM_POINT:\n            if (screen_selection_range_for_line(screen, w->mouse_pos.cell_y, &start, &end) && end > w->mouse_pos.cell_x) S(EXTEND_LINE_FROM_POINT);\n            break;\n        case MOUSE_SELECTION_WORD_AND_LINE_FROM_POINT:\n            if (screen_selection_range_for_line(screen, w->mouse_pos.cell_y, &start, &end) && end > w->mouse_pos.cell_x) S(EXTEND_WORD_AND_LINE_FROM_POINT);\n            break;\n        case MOUSE_SELECTION_EXTEND:\n            extend_selection(w, false, true);\n            break;\n        case MOUSE_SELECTION_MOVE_END:\n            extend_selection(w, false, false);\n            break;\n        case MOUSE_SELECTION_UPTO_SURROUNDING_WHITESPACE:\n            // TODO: Implement me for people migrating from urxvt\n            break;\n    }\n    set_mouse_cursor_when_dragging(screen);\n#undef S\n}\n\nstatic const char*\nborder_name(int edges) {\n    switch(edges) {\n        case 0: return \"none\";\n        case LEFT_EDGE: return \"left\";\n        case RIGHT_EDGE: return \"right\";\n        case TOP_EDGE: return \"top\";\n        case BOTTOM_EDGE: return \"bottom\";\n        case LEFT_EDGE | TOP_EDGE: return \"top-left\";\n        case LEFT_EDGE | BOTTOM_EDGE: return \"bottom-left\";\n        case RIGHT_EDGE | TOP_EDGE: return \"top-right\";\n        case RIGHT_EDGE | BOTTOM_EDGE: return \"bottom-right\";\n    }\n    return \"unknown\";\n}\n\nvoid\nmouse_event(const int button, int modifiers, int action) {\n    MouseShape old_cursor = mouse_cursor_shape;\n    unsigned int window_idx = 0;\n    Window *w = NULL; OSWindow *osw = global_state.callback_os_window;\n\n    if (OPT(debug_keyboard)) {\n        if (button < 0) { debug(\"%s x: %.1f y: %.1f \", \"\\x1b[36mMove\\x1b[m\", osw->mouse_x, osw->mouse_y); }\n        else { debug(\"%s mouse_button: %d %s\", action == GLFW_RELEASE ? \"\\x1b[32mRelease\\x1b[m\" : \"\\x1b[31mPress\\x1b[m\", button, format_mods(modifiers)); }\n    }\n    if (global_state.redirect_mouse_handling) {\n        MouseRegion r= mouse_region(false, false); w = r.window;\n        call_boss(mouse_event, \"OK iiii dd\",\n                (r.in_tab_bar ? Py_True : Py_False), (w ? w->id : 0),\n                action, modifiers, button, currently_pressed_button(),\n                osw->mouse_x, osw->mouse_y\n        );\n        debug(\"mouse handling redirected\\n\");\n        return;\n    }\n    if (global_state.active_drag_in_window) {\n        if (button == -1) {  // drag move\n            w = window_for_id(global_state.active_drag_in_window);\n            if (w) {\n                if (currently_pressed_button() == global_state.active_drag_button) {\n                    clamp_to_window = true;\n                    Tab *t = osw->tabs + osw->active_tab;\n                    for (window_idx = 0; window_idx < t->num_windows && t->windows[window_idx].id != w->id; window_idx++);\n                    handle_move_event(w, currently_pressed_button(), modifiers, window_idx);\n                    clamp_to_window = false;\n                    debug(\"handled as drag move\\n\");\n                    return;\n                }\n            }\n        }\n        else if (action == GLFW_RELEASE && button == global_state.active_drag_button) {\n            w = window_for_id(global_state.active_drag_in_window);\n            if (w) {\n                end_drag(w);\n                // Clear any stale suppress flag that was set during a focus-transfer\n                // press, since the drag release bypasses handle_button_event where\n                // it would normally be cleared.\n                if (osw) osw->suppress_left_mouse_release = false;\n                debug(\"handled as drag end\\n\");\n                dispatch_possible_click(w, button, modifiers);\n                return;\n            }\n        }\n    }\n    if (global_state.tracked_drag_in_window) {\n        if (button == -1) {  // drag move\n            w = window_for_id(global_state.tracked_drag_in_window);\n            if (w) {\n                if (currently_pressed_button() == GLFW_MOUSE_BUTTON_LEFT) {\n                    if (w->render_data.screen->modes.mouse_tracking_mode >= MOTION_MODE && w->render_data.screen->modes.mouse_tracking_protocol == SGR_PIXEL_PROTOCOL) {\n                        clamp_to_window = true;\n                        Tab *t = osw->tabs + osw->active_tab;\n                        for (window_idx = 0; window_idx < t->num_windows && t->windows[window_idx].id != w->id; window_idx++);\n                        handle_move_event(w, global_state.tracked_drag_button, modifiers, window_idx);\n                        clamp_to_window = false;\n                        debug(\"sent to child as drag move\\n\");\n                        return;\n                    }\n                }\n            }\n        } else if (action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT) {\n            w = window_for_id(global_state.tracked_drag_in_window);\n            if (w && w->render_data.screen->modes.mouse_tracking_mode >= BUTTON_MODE && w->render_data.screen->modes.mouse_tracking_protocol >= SGR_PROTOCOL) {\n                global_state.tracked_drag_in_window = 0;\n                clamp_to_window = true;\n                Tab *t = osw->tabs + osw->active_tab;\n                for (window_idx = 0; window_idx < t->num_windows && t->windows[window_idx].id != w->id; window_idx++);\n                debug(\"sent to child as drag end\\n\");\n                handle_button_event(w, button, modifiers, window_idx);\n                clamp_to_window = false;\n                return;\n            }\n        }\n    }\n    if (global_state.active_drag_resize) {\n        if (button < 0) {\n            call_boss(drag_resize_update, \"dd\", osw->mouse_x, osw->mouse_y);\n            debug(\"drag resize updated\\n\");\n        } else if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {\n            call_boss(drag_resize_end, \"\");\n            global_state.active_drag_resize = 0;\n            mouse_cursor_shape = DEFAULT_POINTER;\n            set_mouse_cursor(mouse_cursor_shape);\n            debug(\"drag resize ended\\n\");\n        }\n        return;\n    }\n    MouseRegion r = mouse_region(true, true);\n    w = r.window; window_idx = r.window_idx;\n    set_currently_hovered_window(w && !r.window_border && !r.in_title_bar ? w->id : 0, modifiers);\n\n    if (r.in_tab_bar || global_state.tab_being_dragged.id) {\n        mouse_cursor_shape = POINTER_POINTER;\n        handle_tab_bar_mouse(button, modifiers, action);\n        debug(\"handled by tab bar\\n\");\n    } else if (r.in_title_bar && r.window) {\n        mouse_cursor_shape = POINTER_POINTER;\n        handle_window_title_bar_mouse(r.window, button, modifiers, action);\n        debug(\"handled by window title bar\\n\");\n    } else if (r.window_border) {\n        debug(\"window border: %s window id: %llu\\n\", border_name(r.window_border), w ? w->id : 0);\n        if (r.window_border & LEFT_EDGE) {\n            if (r.window_border & TOP_EDGE) mouse_cursor_shape = NWSE_RESIZE_POINTER;\n            else if (r.window_border & BOTTOM_EDGE) mouse_cursor_shape = NESW_RESIZE_POINTER;\n            else mouse_cursor_shape = EW_RESIZE_POINTER;\n        } else if (r.window_border & RIGHT_EDGE) {\n            if (r.window_border & TOP_EDGE) mouse_cursor_shape = NESW_RESIZE_POINTER;\n            else if (r.window_border & BOTTOM_EDGE) mouse_cursor_shape = NWSE_RESIZE_POINTER;\n            else mouse_cursor_shape = EW_RESIZE_POINTER;\n        } else if (r.window_border & (TOP_EDGE | BOTTOM_EDGE)) mouse_cursor_shape = NS_RESIZE_POINTER;\n        if (w && button == GLFW_MOUSE_BUTTON_LEFT && w->render_data.screen) {\n            RAII_PyObject(retval, PyObject_CallMethod(\n                global_state.boss, \"drag_resize_start\", \"iddKII\", r.window_border,\n                osw->mouse_x, osw->mouse_y, w->id,\n                w->render_data.screen->cell_size.width, w->render_data.screen->cell_size.height));\n            if (retval == NULL) { PyErr_Print(); return; }\n            if (PyObject_IsTrue(retval)) global_state.active_drag_resize = w->id;\n        }\n    } else if (w) {\n        debug(\"grabbed: %d\\n\", w->render_data.screen->modes.mouse_tracking_mode != 0);\n        handle_event(w, button, modifiers, window_idx);\n    } else if (button == GLFW_MOUSE_BUTTON_LEFT && osw->mouse_button_pressed[button]) {\n        // initial click, clamp it to the closest window\n        w = closest_window_for_event(&window_idx);\n        if (w) {\n            clamp_to_window = true;\n            debug(\"grabbed: %d\\n\", w->render_data.screen->modes.mouse_tracking_mode != 0);\n            handle_event(w, button, modifiers, window_idx);\n            clamp_to_window = false;\n        } else debug(\"no window for event\\n\");\n    } else {\n        mouse_cursor_shape = DEFAULT_POINTER;\n        debug(\"\\n\");\n    }\n    if (mouse_cursor_shape != old_cursor) set_mouse_cursor(mouse_cursor_shape);\n}\n\nstatic int\nscale_scroll(MouseTrackingMode mouse_tracking_mode, double offset, GLFWOffsetType offset_type, double *pending_scroll_pixels, int cell_size) {\n// scale the scroll by the multiplier unless the mouse is grabbed. If the mouse is grabbed only change direction.\n#define SCALE_SCROLL(which) { double scale = OPT(which); if (mouse_tracking_mode) scale /= fabs(scale); offset *= scale; }\n    int s = 0;\n    switch (offset_type) {\n        case GLFW_SCROLL_OFFEST_HIGHRES: {\n            SCALE_SCROLL(touch_scroll_multiplier);\n            double pixels = *pending_scroll_pixels + offset;\n            if (fabs(pixels) < cell_size) {\n                *pending_scroll_pixels = pixels;\n                return 0;\n            }\n            s = (int)round(pixels) / cell_size;\n            *pending_scroll_pixels = pixels - s * cell_size;\n        } break;\n        case GLFW_SCROLL_OFFEST_V120: {\n            SCALE_SCROLL(wheel_scroll_multiplier);\n            const double offset_lines = offset / 120.;\n            const double pixels = *pending_scroll_pixels + offset_lines * cell_size;\n            if (fabs(pixels) < cell_size) {\n                *pending_scroll_pixels = pixels;\n                return 0;\n            }\n            s = (int)round(pixels) / cell_size;\n            *pending_scroll_pixels = pixels - s * cell_size;\n        } break;\n        case GLFW_SCROLL_OFFSET_LINES: {\n            SCALE_SCROLL(wheel_scroll_multiplier);\n            s = (int) round(offset);\n            if (offset != 0) {\n                const int min_lines = mouse_tracking_mode ? 1 : OPT(wheel_scroll_min_lines);\n                if (min_lines > 0 && abs(s) < min_lines) s = offset > 0 ? min_lines : -min_lines;\n                // Always add the minimum number of lines when it is negative\n                else if (min_lines < 0) s = offset > 0 ? s - min_lines : s + min_lines;\n                // apparently on cocoa some mice generate really small yoffset values\n                // when scrolling slowly https://github.com/kovidgoyal/kitty/issues/1238\n                if (s == 0) s = offset > 0 ? 1 : -1;\n            }\n            *pending_scroll_pixels = 0;\n        } break;\n    }\n    return s;\n#undef SCALE_SCROLL\n}\n\nstatic const char*\nscroll_offset_type(GLFWOffsetType t) {\n    switch(t) {\n        case GLFW_SCROLL_OFFSET_LINES: return \"lines\";\n        case GLFW_SCROLL_OFFEST_V120: return \"v120\";\n        case GLFW_SCROLL_OFFEST_HIGHRES: return \"highres\";\n    }\n    return \"\";\n}\n\nstatic const char*\nscroll_phase(GLFWMomentumType t) {\n    switch(t) {\n        case GLFW_NO_MOMENTUM_DATA: return \"none\";\n        case GLFW_MOMENTUM_PHASE_MAY_BEGIN: return \"may_begin\";\n        case GLFW_MOMENTUM_PHASE_BEGAN: return \"began\";\n        case GLFW_MOMENTUM_PHASE_ACTIVE: return \"active\";\n        case GLFW_MOMENTUM_PHASE_STATIONARY: return \"stationary\";\n        case GLFW_MOMENTUM_PHASE_CANCELED: return \"cancelled\";\n        case GLFW_MOMENTUM_PHASE_ENDED: return \"ended\";\n    }\n    return \"\";\n}\n\n\nstatic inline bool\npixel_scroll_enabled_for_screen(const Screen *screen) {\n    return OPT(pixel_scroll) && screen->linebuf == screen->main_linebuf;\n}\n\nvoid\nscroll_event(const GLFWScrollEvent *ev) {\n    debug(\"\\x1b[36mScroll\\x1b[m type=%s x: %f y: %f momentum: %s modifiers: %s\\n\", scroll_offset_type(ev->offset_type), ev->x_offset, ev->y_offset, scroll_phase(ev->momentum_type), format_mods(ev->keyboard_modifiers));\n    static id_type window_for_momentum_scroll = 0;\n    static bool main_screen_for_momentum_scroll = false;\n    // allow scroll events even if window is not currently focused (in\n    // which case on some platforms such as macOS the mouse location is zeroed so\n    // window_for_event() does not work).\n    OSWindow *osw = global_state.callback_os_window;\n    if (!osw->is_focused && osw->handle) {\n        double mouse_x, mouse_y;\n        glfwGetCursorPos((GLFWwindow*)osw->handle, &mouse_x, &mouse_y);\n        osw->mouse_x = mouse_x * osw->viewport_x_ratio;\n        osw->mouse_y = mouse_y * osw->viewport_y_ratio;\n    }\n    MouseRegion r = mouse_region(false, true);\n    Window *w = r.window;\n    if (!w && !r.in_tab_bar) {\n        // fallback to last active window\n        Tab *t = osw->tabs + osw->active_tab;\n        if (t) w = t->windows + t->active_window;\n    }\n    if (!w) return;\n    // Also update mouse cursor position while kitty OS window is not focused.\n    // Allows scroll events to be delivered to the child with correct pointer coordinates even when\n    // the window is not focused on macOS\n    if (!osw->is_focused) {\n        unsigned int x = 0, y = 0;\n        bool in_left_half_of_cell;\n        if (cell_for_pos(w, &x, &y, &in_left_half_of_cell, osw)) {\n            w->mouse_pos.cell_x = x; w->mouse_pos.cell_y = y;\n            w->mouse_pos.in_left_half_of_cell = in_left_half_of_cell;\n        }\n    }\n    Screen *screen = w->render_data.screen;\n\n    switch(ev->momentum_type) {\n        case GLFW_NO_MOMENTUM_DATA:\n            break;\n        case GLFW_MOMENTUM_PHASE_BEGAN:\n            window_for_momentum_scroll = w->id;\n            main_screen_for_momentum_scroll = screen->linebuf == screen->main_linebuf;\n            break;\n        case GLFW_MOMENTUM_PHASE_STATIONARY: case GLFW_MOMENTUM_PHASE_ACTIVE:\n            if (window_for_momentum_scroll != w->id || main_screen_for_momentum_scroll != (screen->linebuf == screen->main_linebuf)) return;\n            break;\n        case GLFW_MOMENTUM_PHASE_ENDED: case GLFW_MOMENTUM_PHASE_CANCELED:\n            window_for_momentum_scroll = 0;\n            break;\n        case GLFW_MOMENTUM_PHASE_MAY_BEGIN:\n            break;\n    }\n    if (ev->y_offset != 0.0) {\n        if (screen->modes.mouse_tracking_mode == NO_TRACKING && pixel_scroll_enabled_for_screen(screen) && (ev->offset_type == GLFW_SCROLL_OFFEST_HIGHRES || ev->offset_type == GLFW_SCROLL_OFFEST_V120)) {\n            double delta_pixels;\n            if (ev->offset_type == GLFW_SCROLL_OFFEST_HIGHRES) {\n                delta_pixels = ev->y_offset * OPT(touch_scroll_multiplier);\n            } else {\n                const double offset_lines = (ev->y_offset / 120.) * OPT(wheel_scroll_multiplier);\n                delta_pixels = offset_lines * global_state.callback_os_window->fonts_data->fcm.cell_height;\n            }\n            screen->pending_scroll_pixels_y = 0.0;\n            if (screen_apply_pixel_scroll(screen, delta_pixels) && screen->selections.in_progress) update_drag(w);\n        } else {\n            int s = scale_scroll(screen->modes.mouse_tracking_mode, ev->y_offset, ev->offset_type, &screen->pending_scroll_pixels_y, global_state.callback_os_window->fonts_data->fcm.cell_height);\n            if (s) {\n                bool upwards = s > 0;\n                if (screen->modes.mouse_tracking_mode) {\n                    int sz = encode_mouse_scroll(w, upwards ? 4 : 5, ev->keyboard_modifiers);\n                    if (sz > 0) {\n                        mouse_event_buf[sz] = 0;\n                        for (s = abs(s); s > 0; s--) {\n                            write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf);\n                        }\n                    }\n                } else {\n                        if (screen->linebuf == screen->main_linebuf) {\n                            screen_history_scroll(screen, abs(s), upwards);\n                            if (screen->selections.in_progress) update_drag(w);\n                        }\n                        else fake_scroll(w, abs(s), upwards);\n                    }\n                }\n        }\n    }\n    if (ev->x_offset != 0.0) {\n        int s = scale_scroll(screen->modes.mouse_tracking_mode, ev->x_offset, ev->offset_type, &screen->pending_scroll_pixels_x, global_state.callback_os_window->fonts_data->fcm.cell_width);\n        if (s) {\n            if (screen->modes.mouse_tracking_mode) {\n                int sz = encode_mouse_scroll(w, s > 0 ? 6 : 7, ev->keyboard_modifiers);\n                if (sz > 0) {\n                    mouse_event_buf[sz] = 0;\n                    for (s = abs(s); s > 0; s--) {\n                        write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf);\n                    }\n                }\n            }\n        }\n    }\n}\n\nstatic PyObject*\nsend_mouse_event(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    Screen *screen;\n    int x, y, px=0, py=0, in_left_half_of_cell=0;\n\n    int button, action, mods;\n    static const char* kwlist[] = {\"screen\", \"cell_x\", \"cell_y\", \"button\", \"action\", \"mods\", \"pixel_x\", \"pixel_y\", \"in_left_half_of_cell\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"O!iiiii|iip\", (char**)kwlist,\n                &Screen_Type, &screen, &x, &y, &button, &action, &mods, &px, &py, &in_left_half_of_cell)) return NULL;\n\n    MouseTrackingMode mode = screen->modes.mouse_tracking_mode;\n    if (mode == ANY_MODE || (mode == MOTION_MODE && action != MOVE) || (mode == BUTTON_MODE && (action == PRESS || action == RELEASE))) {\n        MousePosition mpos = {.cell_x = x, .cell_y = y, .global_x = px, .global_y = py, .in_left_half_of_cell = in_left_half_of_cell};\n        int sz = encode_mouse_event_impl(&mpos, screen->modes.mouse_tracking_protocol, button, action, mods);\n        if (sz > 0) {\n            mouse_event_buf[sz] = 0;\n            write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf);\n            Py_RETURN_TRUE;\n        }\n    }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\ntest_encode_mouse(PyObject *self UNUSED, PyObject *args) {\n    unsigned int x, y;\n    int mouse_tracking_protocol, button, action, mods;\n    if (!PyArg_ParseTuple(args, \"IIiiii\", &x, &y, &mouse_tracking_protocol, &button, &action, &mods)) return NULL;\n    MousePosition mpos = {.cell_x = x - 1, .cell_y = y - 1};\n    int sz = encode_mouse_event_impl(&mpos, mouse_tracking_protocol, button, action, mods);\n    return PyUnicode_FromStringAndSize(mouse_event_buf, sz);\n}\n\nstatic PyObject*\nmock_mouse_selection(PyObject *self UNUSED, PyObject *args) {\n    PyObject *capsule;\n    int button, code;\n    if (!PyArg_ParseTuple(args, \"O!ii\", &PyCapsule_Type, &capsule, &button, &code)) return NULL;\n    Window *w = PyCapsule_GetPointer(capsule, \"Window\");\n    if (!w) return NULL;\n    mouse_selection(w, code, button);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nsend_mock_mouse_event_to_window(PyObject *self UNUSED, PyObject *args) {\n    PyObject *capsule;\n    int button, modifiers, is_release, clear_clicks, in_left_half_of_cell;\n    unsigned int x, y;\n    if (!PyArg_ParseTuple(args, \"O!iipIIpp\", &PyCapsule_Type, &capsule, &button, &modifiers, &is_release, &x, &y, &clear_clicks, &in_left_half_of_cell)) return NULL;\n    Window *w = PyCapsule_GetPointer(capsule, \"Window\");\n    if (!w) return NULL;\n    if (clear_clicks) clear_click_queue(w, button);\n    bool mouse_cell_changed = x != w->mouse_pos.cell_x || y != w->mouse_pos.cell_y || w->mouse_pos.in_left_half_of_cell != in_left_half_of_cell;\n    w->mouse_pos.global_x = 10 * x; w->mouse_pos.global_y = 20 * y;\n    w->mouse_pos.cell_x = x; w->mouse_pos.cell_y = y;\n    w->mouse_pos.in_left_half_of_cell = in_left_half_of_cell;\n    static int last_button_pressed = GLFW_MOUSE_BUTTON_LEFT;\n    if (button < 0) {\n        if (button == -2) do_drag_scroll(w, true);\n        else if (button == -3) do_drag_scroll(w, false);\n        else handle_mouse_movement_in_kitty(w, last_button_pressed, mouse_cell_changed);\n    } else {\n        if (global_state.active_drag_in_window && is_release && button == global_state.active_drag_button) {\n            end_drag(w);\n        } else {\n            dispatch_mouse_event(w, button, is_release ? -1 : 1, modifiers, false);\n            if (!is_release) {\n                last_button_pressed = button;\n                add_press(w, button, modifiers);\n            }\n        }\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyMethodDef module_methods[] = {\n    {\"send_mouse_event\", (PyCFunction)(void (*) (void))(send_mouse_event), METH_VARARGS | METH_KEYWORDS, NULL},\n    METHODB(test_encode_mouse, METH_VARARGS),\n    METHODB(send_mock_mouse_event_to_window, METH_VARARGS),\n    METHODB(mock_mouse_selection, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nbool\ninit_mouse(PyObject *module) {\n    PyModule_AddIntMacro(module, PRESS);\n    PyModule_AddIntMacro(module, RELEASE);\n    PyModule_AddIntMacro(module, DRAG);\n    PyModule_AddIntMacro(module, MOVE);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_NORMAL);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_EXTEND);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_RECTANGLE);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_WORD);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_LINE);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_LINE_FROM_POINT);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_WORD_AND_LINE_FROM_POINT);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_MOVE_END);\n    PyModule_AddIntMacro(module, MOUSE_SELECTION_UPTO_SURROUNDING_WHITESPACE);\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    return true;\n}\n"
  },
  {
    "path": "kitty/multiprocessing.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n# Monkeypatch the stdlib multiprocessing module to work with the embedded python\n# in kitty, when using the spawn launcher.\n\n\nimport os\nimport sys\nfrom collections.abc import Callable, Sequence\nfrom concurrent.futures import ProcessPoolExecutor\nfrom multiprocessing import context, get_all_start_methods, get_context, spawn, util\nfrom typing import TYPE_CHECKING, Any, Union\n\nfrom .constants import kitty_exe\n\norig_spawn_passfds = util.spawnv_passfds\norig_executable = spawn.get_executable()\n\nif TYPE_CHECKING:\n    from collections.abc import Buffer\n    from typing import SupportsIndex, SupportsInt\n\n    if sys.version_info[:2] >= (3, 14):\n        ArgsType = Sequence[Union[str, Buffer, SupportsInt, SupportsIndex]]\n    else:\n        from _typeshed import ReadableBuffer, SupportsTrunc\n        ArgsType = Sequence[Union[str, ReadableBuffer, SupportsInt, SupportsIndex, SupportsTrunc]]\nelse:\n    ArgsType = Sequence[str]\n\n\ndef spawnv_passfds(path: bytes, args: ArgsType, passfds: Sequence[int]) -> int:\n    if '-c' in args:\n        idx = args.index('-c')\n        patched_args = [spawn.get_executable(), '+runpy'] + list(args)[idx + 1:]\n    else:\n        idx = args.index('--multiprocessing-fork')\n        prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'\n        prog %= ', '.join(str(item) for item in args[idx+1:])\n        patched_args = [spawn.get_executable(), '+runpy', prog]\n    return orig_spawn_passfds(os.fsencode(kitty_exe()), patched_args, passfds)\n\n\ndef monkey_patch_multiprocessing() -> None:\n    # Use kitty to run the worker process used by multiprocessing\n    spawn.set_executable(kitty_exe())\n    util.spawnv_passfds = spawnv_passfds\n\n\ndef unmonkey_patch_multiprocessing() -> None:\n    spawn.set_executable(orig_executable)\n    util.spawnv_passfds = orig_spawn_passfds\n\n\ndef get_process_pool_executor(\n    prefer_fork: bool = False,\n    max_workers: int | None = None,\n    initializer: Callable[..., None] | None = None,\n    initargs: tuple[Any, ...] = ()\n) -> ProcessPoolExecutor:\n    if prefer_fork and 'fork' in get_all_start_methods():\n        ctx: context.DefaultContext | context.ForkContext = get_context('fork')\n    else:\n        monkey_patch_multiprocessing()\n        ctx = get_context()\n    try:\n        return ProcessPoolExecutor(max_workers=max_workers, initializer=initializer, initargs=initargs, mp_context=ctx)\n    except TypeError:\n        return ProcessPoolExecutor(max_workers=max_workers, initializer=initializer, initargs=initargs)\n\n\ndef test_spawn() -> None:\n    monkey_patch_multiprocessing()\n    import shutil\n    import subprocess\n    from queue import Empty\n    try:\n        from multiprocessing import get_context\n        ctx = get_context('spawn')\n        q = ctx.Queue()\n        p = ctx.Process(target=q.put, args=('hello',))\n        p.start()\n        try:\n            x = q.get(timeout=8)\n        except Empty:\n            p.join()\n            rc = p.exitcode\n            if rc == 0:\n                raise TimeoutError('Timed out waiting for response from spawned process')\n            if shutil.which('coredumpctl'):\n                subprocess.run(['sh', '-c', 'echo bt | coredumpctl debug'])\n            raise SystemExit(f'Spawned process exited with return code: {rc}')\n        assert x == 'hello'\n        p.join()\n    finally:\n        unmonkey_patch_multiprocessing()\n"
  },
  {
    "path": "kitty/notifications.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nfrom collections import OrderedDict\nfrom collections.abc import Callable, Iterator, Sequence\nfrom contextlib import suppress\nfrom enum import Enum\nfrom functools import partial\nfrom itertools import count\nfrom typing import Any, NamedTuple, Set\nfrom weakref import ReferenceType, ref\n\nfrom .constants import cache_dir, config_dir, is_macos, logo_png_file, standard_icon_names, standard_sound_names, supports_window_occlusion\nfrom .fast_data_types import (\n    ESC_OSC,\n    StreamingBase64Decoder,\n    add_timer,\n    base64_decode,\n    current_focused_os_window_id,\n    get_boss,\n    get_options,\n    os_window_is_invisible,\n)\nfrom .types import run_once\nfrom .typing_compat import WindowType\nfrom .utils import get_custom_window_icon, log_error, sanitize_control_codes\n\ndebug_desktop_integration = False  # set by NotificationManager\n\n\ndef image_type(data: bytes) -> str:\n    if data[:8] == b\"\\211PNG\\r\\n\\032\\n\":\n        return 'png'\n    if data[:6] in (b'GIF87a', b'GIF89a'):\n        return 'gif'\n    if data[:2] == b'\\xff\\xd8':\n        return 'jpeg'\n    return 'unknown'\n\n\nclass IconDataCache:\n\n    def __init__(self, base_cache_dir: str = '', max_cache_size: int = 128 * 1024 * 1024):\n        self.max_cache_size = max_cache_size\n        self.key_map: dict[str, str] = {}\n        self.hash_map: 'OrderedDict[str, Set[str]]' = OrderedDict()\n        self.base_cache_dir = base_cache_dir\n        self.cache_dir = ''\n        self.total_size = 0\n        import struct\n        self.seed: int = struct.unpack(\"!Q\", os.urandom(8))[0]\n\n    def _ensure_state(self) -> str:\n        if not self.cache_dir:\n            self.cache_dir = os.path.join(self.base_cache_dir or cache_dir(), 'notifications-icons', str(os.getpid()))\n            os.makedirs(self.cache_dir, exist_ok=True, mode=0o700)\n            b = get_boss()\n            if hasattr(b, 'atexit'):\n                b.atexit.rmtree(self.cache_dir)\n        return self.cache_dir\n\n    def __del__(self) -> None:\n        if self.cache_dir:\n            import shutil\n            with suppress(FileNotFoundError):\n                shutil.rmtree(self.cache_dir)\n            self.cache_dir = ''\n\n    def keys(self) -> Iterator[str]:\n        yield from self.key_map.keys()\n\n    def hash(self, data: bytes) -> str:\n        from kittens.transfer.rsync import xxh128_hash_with_seed\n        d = xxh128_hash_with_seed(data, self.seed)\n        return d.hex() + '.' + image_type(data)\n\n    def add_icon(self, key: str, data: bytes) -> str:\n        self._ensure_state()\n        data_hash = self.hash(data)\n        path = os.path.join(self.cache_dir, data_hash)\n        if not os.path.exists(path):\n            with open(path, 'wb') as f:\n                f.write(data)\n            self.total_size += len(data)\n            self.hash_map[data_hash] = self.hash_map.pop(data_hash, set()) | {key} # mark this data as being used recently\n        if key:\n            self.key_map[key] = data_hash\n        self.prune()\n        return path\n\n    def get_icon(self, key: str) -> str:\n        self._ensure_state()\n        data_hash = self.key_map.get(key)\n        if data_hash:\n            self.hash_map[data_hash] = self.hash_map.pop(data_hash, set()) | {key} # mark this data as being used recently\n            return os.path.join(self.cache_dir, data_hash)\n        return ''\n\n    def clear(self) -> None:\n        while self.hash_map:\n            data_hash, keys = self.hash_map.popitem(False)\n            for key in keys:\n                self.key_map.pop(key, None)\n            self._remove_data_file(data_hash)\n\n    def prune(self) -> None:\n        self._ensure_state()\n        while self.total_size > self.max_cache_size and self.hash_map:\n            data_hash, keys = self.hash_map.popitem(False)\n            for key in keys:\n                self.key_map.pop(key, None)\n            self._remove_data_file(data_hash)\n\n    def _remove_data_file(self, data_hash: str) -> None:\n        path = os.path.join(self.cache_dir, data_hash)\n        with suppress(FileNotFoundError):\n            sz = os.path.getsize(path)\n            os.remove(path)\n            self.total_size -= sz\n\n    def remove_icon(self, key: str) -> None:\n        self._ensure_state()\n        data_hash = self.key_map.pop(key, None)\n        if data_hash:\n            for key in self.hash_map.pop(data_hash, set()):\n                self.key_map.pop(key, None)\n            self._remove_data_file(data_hash)\n\n\nclass Urgency(Enum):\n    Low = 0\n    Normal = 1\n    Critical = 2\n\n\nclass PayloadType(Enum):\n    unknown = ''\n    title = 'title'\n    body = 'body'\n    query = '?'\n    close = 'close'\n    icon = 'icon'\n    alive = 'alive'\n    buttons = 'buttons'\n\n    @property\n    def is_text(self) -> bool:\n        return self in (PayloadType.title, PayloadType.body, PayloadType.buttons)\n\n\nclass OnlyWhen(Enum):\n    unset = ''\n    always = 'always'\n    unfocused = 'unfocused'\n    invisible = 'invisible'\n\n\nclass Action(Enum):\n    focus = 'focus'\n    report = 'report'\n\n\nclass DataStore:\n\n    def __init__(self, max_size: int = 4 * 1024 * 1024) -> None:\n        self.buf: list[bytes] = []\n        self.current_size = 0\n        self.max_size = max_size\n        self.truncated = 0\n\n    def __call__(self, data: bytes) -> None:\n        if data:\n            if self.current_size > self.max_size:\n                self.truncated += len(data)\n            else:\n                self.current_size += len(data)\n                self.buf.append(data)\n\n    def finalise(self) -> bytes:\n        return b''.join(self.buf)\n\n\nclass EncodedDataStore:\n\n    def __init__(self, data_store: DataStore) -> None:\n        self.decoder = StreamingBase64Decoder()\n        self.data_store = data_store\n\n    @property\n    def truncated(self) -> int:\n        return self.data_store.truncated\n\n    def add_unencoded_data(self, data: str | bytes) -> None:\n        if isinstance(data, str):\n            data = data.encode('utf-8')\n        self.flush_encoded_data()\n        self.data_store(data)\n\n    def add_base64_data(self, data: str | bytes) -> None:\n        if isinstance(data, str):\n            data = data.encode('ascii')\n        try:\n            decoded = self.decoder.decode(data)\n        except ValueError:\n            log_error('Ignoring invalid base64 encoded data in notification request')\n        else:\n            self.data_store(decoded)\n\n    def flush_encoded_data(self) -> None:\n        if self.decoder.needs_more_data():\n            log_error('Received incomplete encoded data for notification request')\n        self.decoder.reset()\n\n    def finalise(self) -> bytes:\n        self.flush_encoded_data()\n        return self.data_store.finalise()\n\n\ndef limit_size(x: str, limit: int = 1024) -> str:\n    if len(x) > limit:\n        x = x[:limit]\n    return x\n\n\nclass NotificationCommand:\n\n    # data received from client and eventually displayed/processed\n    title: str = ''\n    body: str = ''\n    actions: frozenset[Action] = frozenset((Action.focus,))\n    only_when: OnlyWhen = OnlyWhen.unset\n    urgency: Urgency | None = None\n    icon_data_key: str = ''\n    icon_names: tuple[str, ...] = ()\n    application_name: str = ''\n    notification_types: tuple[str, ...] = ()\n    timeout: int = -2\n    buttons: tuple[str, ...] = ()\n    sound_name: str = ''\n\n    # event callbacks\n    on_activation: Callable[['NotificationCommand', int], None] | None = None\n    on_close: Callable[['NotificationCommand'], None] | None = None\n    on_update: Callable[['NotificationCommand', 'NotificationCommand'], None] | None = None\n\n    # metadata\n    identifier: str = ''\n    done: bool = True\n    channel_id: int = 0\n    desktop_notification_id: int = -1\n    close_response_requested: bool | None = None\n    icon_path: str = ''\n\n    # payload handling\n    current_payload_type: PayloadType = PayloadType.title\n    current_payload_buffer: EncodedDataStore | None = None\n\n    # desktop integration specific fields\n    created_by_desktop: bool = False\n    activation_token: str = ''\n\n    def __init__(self, icon_data_cache: 'ReferenceType[IconDataCache]', log: 'Log') -> None:\n        self.icon_data_cache_ref = icon_data_cache\n        self.log = log\n\n    @property\n    def report_requested(self) -> bool:\n        return Action.report in self.actions\n\n    @property\n    def focus_requested(self) -> bool:\n        return Action.focus in self.actions\n\n    def __repr__(self) -> str:\n        fields = {}\n        for x in ('title', 'body', 'identifier', 'actions', 'urgency', 'done'):\n            val = getattr(self, x)\n            if val:\n                fields[x] = val\n        return f'NotificationCommand{fields}'\n\n    def parse_metadata(self, metadata: str, prev: 'NotificationCommand') -> tuple[PayloadType, bool]:\n        payload_type = PayloadType.title\n        payload_is_encoded = False\n        if metadata:\n            for part in metadata.split(':'):\n                if not part:\n                    continue\n                k, v = part.split('=', 1)\n                if k == 'p':\n                    try:\n                        payload_type = PayloadType(v)\n                    except ValueError:\n                        payload_type = PayloadType.unknown\n                elif k == 'i':\n                    self.identifier = sanitize_id(v)\n                elif k == 'e':\n                    payload_is_encoded = v == '1'\n                elif k == 'd':\n                    self.done = v != '0'\n                elif k == 'a':\n                    for ax in v.split(','):\n                        if remove := ax.startswith('-'):\n                            ax = ax.lstrip('+-')\n                        try:\n                            ac = Action(ax)\n                        except ValueError:\n                            pass\n                        else:\n                            if remove:\n                                self.actions -= {ac}\n                            else:\n                                self.actions = self.actions.union({ac})\n                elif k == 'o':\n                    with suppress(ValueError):\n                        self.only_when = OnlyWhen(v)\n                elif k == 'u':\n                    with suppress(Exception):\n                        self.urgency = Urgency(int(v))\n                elif k == 'c':\n                    self.close_response_requested = v != '0'\n                elif k == 'g':\n                    self.icon_data_key = sanitize_id(v)\n                elif k == 'n':\n                    try:\n                        self.icon_names += (base64_decode(v).decode('utf-8'),)\n                    except Exception:\n                        self.log(f'Ignoring invalid icon name in notification: {v!r}')\n                elif k == 'f':\n                    try:\n                        self.application_name = base64_decode(v).decode('utf-8')\n                    except Exception:\n                        self.log(f'Ignoring invalid application_name in notification: {v!r}')\n                elif k == 't':\n                    try:\n                        self.notification_types += (base64_decode(v).decode('utf-8'),)\n                    except Exception:\n                        self.log(f'Ignoring invalid notification type in notification: {v!r}')\n                elif k == 'w':\n                    try:\n                        self.timeout = max(-1, int(v))\n                    except Exception:\n                        self.log(f'Ignoring invalid timeout in notification: {v!r}')\n                elif k == 's':\n                    try:\n                        self.sound_name = base64_decode(v).decode('utf-8')\n                    except Exception:\n                        self.log(f'Ignoring invalid sound name in notification: {v!r}')\n        if not prev.done and prev.identifier == self.identifier:\n            self.merge_metadata(prev)\n        return payload_type, payload_is_encoded\n\n    def merge_metadata(self, prev: 'NotificationCommand') -> None:\n        self.actions = prev.actions.union(self.actions)\n        self.title = prev.title\n        self.body = prev.body\n        if self.only_when is OnlyWhen.unset:\n            self.only_when = prev.only_when\n        if self.urgency is None:\n            self.urgency = prev.urgency\n        if self.close_response_requested is None:\n            self.close_response_requested = prev.close_response_requested\n        if not self.icon_data_key:\n            self.icon_data_key = prev.icon_data_key\n        if prev.icon_names:\n            self.icon_names = prev.icon_names + self.icon_names\n        if not self.application_name:\n            self.application_name = prev.application_name\n        if prev.notification_types:\n            self.notification_types = prev.notification_types + self.notification_types\n        if prev.buttons:\n            self.buttons += prev.buttons\n        if not self.sound_name:\n            self.sound_name = prev.sound_name\n        if self.timeout < -1:\n            self.timeout = prev.timeout\n        self.icon_path = prev.icon_path\n\n    def create_payload_buffer(self, payload_type: PayloadType) -> EncodedDataStore:\n        self.current_payload_type = payload_type\n        return EncodedDataStore(DataStore())\n\n    def set_payload(self, payload_type: PayloadType, payload_is_encoded: bool, payload: str, prev_cmd: 'NotificationCommand') -> None:\n        if prev_cmd.current_payload_type is payload_type:\n            self.current_payload_type = payload_type\n            self.current_payload_buffer = prev_cmd.current_payload_buffer\n            prev_cmd.current_payload_buffer = None\n        else:\n            if prev_cmd.current_payload_buffer:\n                self.current_payload_type = prev_cmd.current_payload_type\n                self.commit_data(prev_cmd.current_payload_buffer.finalise(), prev_cmd.current_payload_buffer.truncated)\n        if self.current_payload_buffer is None:\n            self.current_payload_buffer = self.create_payload_buffer(payload_type)\n        if payload_is_encoded:\n            self.current_payload_buffer.add_base64_data(payload)\n        else:\n            self.current_payload_buffer.add_unencoded_data(payload)\n\n    def commit_data(self, data: bytes, truncated: int) -> None:\n        if not data:\n            return\n        if self.current_payload_type.is_text:\n            if truncated:\n                text = ' too long, truncated'\n            else:\n                text = data.decode('utf-8', 'replace')\n        if self.current_payload_type is PayloadType.title:\n            self.title = limit_size(self.title + text)\n        elif self.current_payload_type is PayloadType.body:\n            self.body = limit_size(self.body + text)\n        elif self.current_payload_type is PayloadType.icon:\n            if truncated:\n                self.log('Ignoring too long notification icon data')\n            else:\n                icd = self.icon_data_cache_ref()\n                if icd:\n                    self.icon_path = icd.add_icon(self.icon_data_key, data)\n        elif self.current_payload_type is PayloadType.buttons:\n            self.buttons += tuple(limit_size(x, 256) for x in text.split('\\u2028') if x)\n            self.buttons = self.buttons[:8]\n\n    def finalise(self) -> None:\n        if self.current_payload_buffer:\n            self.commit_data(self.current_payload_buffer.finalise(), self.current_payload_buffer.truncated)\n            self.current_payload_buffer = None\n        if self.icon_data_key and not self.icon_path:\n            icd = self.icon_data_cache_ref()\n            if icd:\n                self.icon_path = icd.get_icon(self.icon_data_key)\n        if self.title:\n            self.title = sanitize_text(self.title)\n            self.body = sanitize_text(self.body)\n        else:\n            self.title = sanitize_text(self.body)\n            self.body = ''\n        self.urgency = Urgency.Normal if self.urgency is None else self.urgency\n        self.close_response_requested = bool(self.close_response_requested)\n        self.timeout = max(-1, self.timeout)\n        self.sound_name = self.sound_name or 'system'\n\n    def matches_rule_item(self, location:str, query:str) -> bool:\n        import re\n        pat = re.compile(query)\n        if location == 'type':\n            for x in self.notification_types:\n                if pat.search(x) is not None:\n                    return True\n        val = {'title': self.title, 'body': self.body, 'app': self.application_name}[location]\n        return pat.search(val) is not None\n\n    def matches_rule(self, rule: str) -> bool:\n        if rule == 'all':\n            return True\n        from .search_query_parser import search\n        def get_matches(location: str, query: str, candidates: set['NotificationCommand']) -> set['NotificationCommand']:\n            return {x for x in candidates if x.matches_rule_item(location, query)}\n        try:\n            return self in search(rule, ('title', 'body', 'app', 'type'), {self}, get_matches)\n        except Exception as e:\n            self.log(f'Ignoring invalid filter_notification rule: {rule} with error: {e}')\n        return False\n\n\nclass DesktopIntegration:\n\n    supports_close_events: bool = True\n    supports_body: bool = True\n    supports_buttons: bool = True\n    supports_sound: bool = True\n    supports_sound_names: str = 'xdg-names'\n    supports_timeout_natively: bool = True\n\n    def __init__(self, notification_manager: 'NotificationManager'):\n        self.notification_manager = notification_manager\n        self.initialize()\n\n    def initialize(self) -> None:\n        pass\n\n    def query_live_notifications(self, channel_id: int, identifier: str) -> None:\n        raise NotImplementedError('Implement me in subclass')\n\n    def close_notification(self, desktop_notification_id: int) -> bool:\n        raise NotImplementedError('Implement me in subclass')\n\n    def notify(self, nc: NotificationCommand, existing_desktop_notification_id: int | None) -> int:\n        raise NotImplementedError('Implement me in subclass')\n\n    def on_new_version_notification_activation(self, cmd: NotificationCommand, which: int) -> None:\n        from .update_check import notification_activated\n        notification_activated()\n\n    def payload_type_supported(self, x: PayloadType) -> bool:\n        if x is PayloadType.body and not self.supports_body:\n            return False\n        if x is PayloadType.buttons and not self.supports_buttons:\n            return False\n        return True\n\n    def query_response(self, identifier: str) -> str:\n        actions = ','.join(x.value for x in Action)\n        when = ','.join(x.value for x in OnlyWhen if x.value)\n        urgency = ','.join(str(x.value) for x in Urgency)\n        i = f'i={identifier or \"0\"}:'\n        p = ','.join(x.value for x in PayloadType if x.value and self.payload_type_supported(x))\n        c = ':c=1' if self.supports_close_events else ''\n        s = 'system,silent,' + ','.join(sorted(standard_sound_names))\n        return f'99;{i}p=?;a={actions}:o={when}:u={urgency}:p={p}{c}:w=1:s={s}'\n\n\nclass MacOSNotificationCategory(NamedTuple):\n    id: str\n    buttons: tuple[str, ...] = ()\n    button_ids: tuple[str, ...] = ()\n\n\nclass MacOSIntegration(DesktopIntegration):\n\n    supports_close_events: bool = False\n    supports_sound_names: str = ''\n    supports_timeout_natively: bool = False\n\n    def initialize(self) -> None:\n        from .fast_data_types import cocoa_set_notification_activated_callback\n        self.id_counter = count(start=1)\n        self.live_notification_queries: list[tuple[int, str]] = []\n        self.failed_icons: OrderedDict[str, bool] = OrderedDict()\n        self.icd_key_prefix = os.urandom(16).hex()\n        self.category_cache: OrderedDict[tuple[str, ...], MacOSNotificationCategory] = OrderedDict()\n        self.category_id_counter = count(start=2)\n        self.buttons_id_counter = count(start=1)\n        self.default_category = MacOSNotificationCategory('1')\n        self.current_categories: frozenset[MacOSNotificationCategory] = frozenset()\n        cocoa_set_notification_activated_callback(self.notification_activated)\n\n    def query_live_notifications(self, channel_id: int, identifier: str) -> None:\n        from .fast_data_types import cocoa_live_delivered_notifications\n        if not cocoa_live_delivered_notifications():\n            self.notification_manager.send_live_response(channel_id, identifier, ())\n        else:\n            self.live_notification_queries.append((channel_id, identifier))\n\n    def close_notification(self, desktop_notification_id: int) -> bool:\n        from .fast_data_types import cocoa_remove_delivered_notification\n        close_succeeded = cocoa_remove_delivered_notification(str(desktop_notification_id))\n        if debug_desktop_integration:\n            log_error(f'Close request for {desktop_notification_id=} {\"succeeded\" if close_succeeded else \"failed\"}')\n        return close_succeeded\n\n    def get_icon_for_name(self, name: str) -> str:\n        from .fast_data_types import cocoa_bundle_image_as_png\n        if name in self.failed_icons:\n            return ''\n        image_type, image_name = 1, name\n        if sic := standard_icon_names.get(name):\n            image_name = sic[1]\n            image_type = 2\n        icd = self.notification_manager.icon_data_cache\n        icd_key = self.icd_key_prefix + name\n        ans = icd.get_icon(icd_key)\n        if ans:\n            return ans\n        try:\n            data = cocoa_bundle_image_as_png(image_name, image_type=image_type)\n        except Exception as err:\n            if debug_desktop_integration:\n                self.notification_manager.log(f'Failed to get icon for {name} with error: {err}')\n            self.failed_icons[name] = True\n            if len(self.failed_icons) > 256:\n                self.failed_icons.popitem(False)\n        else:\n            return icd.add_icon(icd_key, data)\n        return ''\n\n    def category_for_notification(self, nc: NotificationCommand) -> MacOSNotificationCategory:\n        key = nc.buttons\n        if not key:\n            return self.default_category\n        if ans := self.category_cache.get(key):\n            self.category_cache.pop(key)\n            self.category_cache[key] = ans\n            return ans\n        ans = self.category_cache[key] = MacOSNotificationCategory(\n            str(next(self.category_id_counter)), nc.buttons, tuple(str(next(self.buttons_id_counter)) for x in nc.buttons)\n        )\n        if len(self.category_cache) > 32:\n            self.category_cache.popitem(False)\n        return ans\n\n    def notify(self, nc: NotificationCommand, existing_desktop_notification_id: int | None) -> int:\n        desktop_notification_id = existing_desktop_notification_id or next(self.id_counter)\n        from .fast_data_types import cocoa_send_notification\n        # If the body is not set macos makes the title the body and uses\n        # \"kitty\" as the title. So use a single space for the body in this\n        # case. Although https://developer.apple.com/documentation/usernotifications/unnotificationcontent/body?language=objc\n        # says printf style strings are stripped this does not actually happen, so dont double % for %% escaping.\n        body = (nc.body or ' ')\n        assert nc.urgency is not None\n        image_path = ''\n        if nc.icon_names:\n            for name in nc.icon_names:\n                if image_path := self.get_icon_for_name(name):\n                    break\n        image_path = image_path or nc.icon_path\n        if not image_path and nc.application_name:\n            image_path = self.get_icon_for_name(nc.application_name)\n        category = self.category_for_notification(nc)\n        categories = tuple(self.category_cache.values())\n        sc = frozenset(categories)\n        if sc == self.current_categories:\n            categories = ()\n        else:\n            self.current_categories = sc\n\n        cocoa_send_notification(\n            nc.application_name or 'kitty', str(desktop_notification_id), nc.title, body,\n            category=category, categories=categories, image_path=image_path, urgency=nc.urgency.value,\n            muted=nc.sound_name == 'silent' or nc.sound_name in standard_sound_names,\n        )\n        return desktop_notification_id\n\n    def notification_activated(self, event: str, ident: str, button_id: str) -> None:\n        if event == 'live':\n            live_ids = tuple(int(x) for x in ident.split(',') if x)\n            if debug_desktop_integration:\n                log_error(f'Live notifications: {live_ids}')\n            self.notification_manager.purge_dead_notifications(live_ids)\n            self.live_notification_queries, queries = [], self.live_notification_queries\n            for channel_id, req_id in queries:\n                self.notification_manager.send_live_response(channel_id, req_id, live_ids)\n            return\n        if debug_desktop_integration:\n            log_error(f'Notification {ident=} {event=} {button_id=}')\n        try:\n            desktop_notification_id = int(ident)\n        except Exception:\n            log_error(f'Got unexpected notification activated event with id: {ident!r} from cocoa')\n            return\n        if event == 'created':\n            n = self.notification_manager.notification_created(desktop_notification_id)\n            # so that we purge dead notifications, check for live notifications\n            # after a few seconds, cant check right away as cocoa does not\n            # report the created notification as live.\n            add_timer(self.check_live_delivered_notifications, 5.0, False)\n            if n and n.sound_name in standard_sound_names:\n                from .fast_data_types import cocoa_play_system_sound_by_id_async\n                cocoa_play_system_sound_by_id_async(standard_sound_names[n.sound_name][1])\n        elif event == 'activated':\n            self.notification_manager.notification_activated(desktop_notification_id, 0)\n        elif event == 'creation_failed':\n            self.notification_manager.notification_closed(desktop_notification_id)\n        elif event == 'closed':  # sadly Crapple never delivers these events\n            self.notification_manager.notification_closed(desktop_notification_id)\n        elif event == 'button':\n            if n := self.notification_manager.in_progress_notification_commands.get(desktop_notification_id):\n                if debug_desktop_integration:\n                    log_error('Button matches notification:', n)\n                for c in self.current_categories:\n                    if c.buttons == n.buttons and button_id in c.button_ids:\n                        if debug_desktop_integration:\n                            log_error('Button number:', c.button_ids.index(button_id) + 1)\n                        self.notification_manager.notification_activated(desktop_notification_id, c.button_ids.index(button_id) + 1)\n                        break\n                else:\n                    if debug_desktop_integration:\n                        log_error('No category found with buttons:', n.buttons)\n                        log_error('Current categories:', self.current_categories)\n\n    def check_live_delivered_notifications(self, *a: object) -> None:\n        from .fast_data_types import cocoa_live_delivered_notifications\n        cocoa_live_delivered_notifications()\n\n\nclass FreeDesktopIntegration(DesktopIntegration):\n\n    supports_body_markup: bool = True\n\n    def initialize(self) -> None:\n        from .fast_data_types import dbus_set_notification_callback\n        dbus_set_notification_callback(self.dispatch_event_from_desktop)\n        # map the id returned by the notification daemon to the\n        # desktop_notification_id we use for the notification\n        self.dbus_to_desktop: 'OrderedDict[int, int]' = OrderedDict()\n        self.desktop_to_dbus: dict[int, int] = {}\n\n    def query_live_notifications(self, channel_id: int, identifier: str) -> None:\n        self.notification_manager.send_live_response(channel_id, identifier, tuple(self.desktop_to_dbus))\n\n    def close_notification(self, desktop_notification_id: int) -> bool:\n        from .fast_data_types import dbus_close_notification\n        close_succeeded = False\n        if dbus_id := self.get_dbus_notification_id(desktop_notification_id, 'close_request'):\n            close_succeeded = dbus_close_notification(dbus_id)\n            if debug_desktop_integration:\n                log_error(f'Close request for {desktop_notification_id=} {\"succeeded\" if close_succeeded else \"failed\"}')\n        return close_succeeded\n\n    def get_desktop_notification_id(self, dbus_notification_id: int, event: str) -> int | None:\n        q = self.dbus_to_desktop.get(dbus_notification_id)\n        if q is None:\n            if debug_desktop_integration:\n                log_error(f'Could not find desktop_notification_id for {dbus_notification_id=} for event {event}')\n        return q\n\n    def get_dbus_notification_id(self, desktop_notification_id: int, event: str) ->int | None:\n        q = self.desktop_to_dbus.get(desktop_notification_id)\n        if q is None:\n            if debug_desktop_integration:\n                log_error(f'Could not find dbus_notification_id for {desktop_notification_id=} for event {event}')\n        return q\n\n    def created(self, dbus_notification_id: int, desktop_notification_id: int) -> None:\n        self.dbus_to_desktop[desktop_notification_id] = dbus_notification_id\n        self.desktop_to_dbus[dbus_notification_id] = desktop_notification_id\n        if len(self.dbus_to_desktop) > 128:\n            k, v = self.dbus_to_desktop.popitem(False)\n            self.desktop_to_dbus.pop(v, None)\n        if n := self.notification_manager.notification_created(dbus_notification_id):\n            # self.supports_sound does not tell us if the notification server\n            # supports named sounds or not so we play the named sound\n            # ourselves and tell the server to mute any sound it might play.\n            if n.sound_name not in ('system', 'silent'):\n                sn = standard_sound_names[n.sound_name][0] if n.sound_name in standard_sound_names else n.sound_name\n                from .fast_data_types import play_desktop_sound_async\n                play_desktop_sound_async(sn, event_id='desktop notification')\n\n    def dispatch_event_from_desktop(self, event_type: str, dbus_notification_id: int, extra: int | str) -> None:\n        if event_type == 'capabilities':\n            capabilities = frozenset(str(extra).splitlines())\n            self.supports_body = 'body' in capabilities\n            self.supports_buttons = 'actions' in capabilities\n            self.supports_body_markup = 'body-markup' in capabilities\n            self.supports_sound = 'sound' in capabilities\n            if debug_desktop_integration:\n                log_error('Got notification server capabilities:', capabilities)\n            return\n        if debug_desktop_integration:\n            log_error(f'Got notification event from desktop: {event_type=} {dbus_notification_id=} {extra=}')\n        if event_type == 'created':\n            self.created(dbus_notification_id, int(extra))\n            return\n        if desktop_notification_id := self.get_desktop_notification_id(dbus_notification_id, event_type):\n            if event_type == 'activation_token':\n                self.notification_manager.notification_activation_token_received(desktop_notification_id, str(extra))\n            elif event_type == 'activated':\n                button = 0 if extra == 'default' else int(extra)\n                self.notification_manager.notification_activated(desktop_notification_id, button)\n            elif event_type == 'closed':\n                self.notification_manager.notification_closed(desktop_notification_id)\n\n    def notify(self, nc: NotificationCommand, existing_desktop_notification_id: int | None) -> int:\n        from .fast_data_types import dbus_send_notification\n        from .xdg import icon_exists, icon_for_appname\n        app_icon = ''\n        if nc.icon_names:\n            for name in nc.icon_names:\n                if sn := standard_icon_names.get(name):\n                    app_icon = sn[0]\n                    break\n                if icon_exists(name):\n                    app_icon = name\n                    break\n            if not app_icon:\n                app_icon = nc.icon_path or nc.icon_names[0]\n        else:\n            app_icon = nc.icon_path or icon_for_appname(nc.application_name)\n        if not app_icon:\n            app_icon = get_custom_window_icon()[1] or logo_png_file\n\n        body = nc.body\n        if self.supports_body_markup:\n            body = body.replace('<', '<\\u200c').replace('&', '&\\u200c')  # prevent HTML markup from being recognized\n        assert nc.urgency is not None\n        replaces_dbus_id = 0\n        if existing_desktop_notification_id:\n            replaces_dbus_id = self.get_dbus_notification_id(existing_desktop_notification_id, 'notify') or 0\n        actions = {'default': ' '}  # dbus requires string to not be empty\n        for i, b in enumerate(nc.buttons):\n            actions[str(i+1)] = b\n        desktop_notification_id = dbus_send_notification(\n            app_name=nc.application_name or 'kitty', app_icon=app_icon, title=nc.title, body=body, actions=actions,\n            timeout=nc.timeout, urgency=nc.urgency.value, replaces=replaces_dbus_id,\n            category=(nc.notification_types or ('',))[0], muted=nc.sound_name == 'silent' or nc.sound_name != 'system',\n        )\n        if debug_desktop_integration:\n            log_error(f'Requested creation of notification with {desktop_notification_id=}')\n        if existing_desktop_notification_id and replaces_dbus_id:\n            self.dbus_to_desktop.pop(replaces_dbus_id, None)\n            self.desktop_to_dbus.pop(existing_desktop_notification_id, None)\n        return desktop_notification_id\n\n\nclass UIState(NamedTuple):\n    has_keyboard_focus: bool\n    is_visible: bool\n\n\nclass Channel:\n\n    def window_for_id(self, channel_id: int) -> WindowType | None:\n        boss = get_boss()\n        if channel_id:\n            return boss.window_id_map.get(channel_id)\n        return boss.active_window\n\n    def ui_state(self, channel_id: int) -> UIState:\n        has_focus = is_visible = False\n        boss = get_boss()\n        if w := self.window_for_id(channel_id):\n            os_window_active = w.os_window_id == current_focused_os_window_id()\n            has_focus = w.is_active and os_window_active\n            is_visible = os_window_active\n            if supports_window_occlusion():\n                is_visible = not os_window_is_invisible(w.os_window_id)\n            is_visible = is_visible and w.tabref() is boss.active_tab and w.is_visible_in_layout\n        return UIState(has_focus, is_visible)\n\n    def send(self, channel_id: int, osc_escape_code: str) -> bool:\n        if w := self.window_for_id(channel_id):\n            if not w.destroyed:\n                w.screen.send_escape_code_to_child(ESC_OSC, osc_escape_code)\n                return True\n        return False\n\n    def focus(self, channel_id: int, activation_token: str) -> None:\n        if debug_desktop_integration:\n            log_error(f'Focusing window: {channel_id} with activation_token: {activation_token}')\n        boss = get_boss()\n        if w := self.window_for_id(channel_id):\n            boss.set_active_window(w, switch_os_window_if_needed=True, activation_token=activation_token)\n\n\nsanitize_text = sanitize_control_codes\n\n@run_once\ndef sanitize_identifier_pat() -> 're.Pattern[str]':\n    return re.compile(r'[^a-zA-Z0-9-_+.]+')\n\n\ndef sanitize_id(v: str) -> str:\n    return sanitize_identifier_pat().sub('', v)[:512]\n\n\nclass Log:\n    def __call__(self, *a: Any, **kw: str) -> None:\n        log_error(*a, **kw)\n\n\nclass NotificationManager:\n\n    def __init__(\n        self,\n        desktop_integration: MacOSIntegration | FreeDesktopIntegration | None = None,\n        channel: Channel = Channel(),\n        log: Log = Log(),\n        debug: bool = False,\n        base_cache_dir: str = '',\n    ):\n        global debug_desktop_integration\n        debug_desktop_integration = debug\n        if desktop_integration is None:\n            self.desktop_integration = MacOSIntegration(self) if is_macos else FreeDesktopIntegration(self)\n        else:\n            self.desktop_integration = desktop_integration\n        self.channel = channel\n        self.base_cache_dir = base_cache_dir\n        self.log = log\n        self.icon_data_cache = IconDataCache(base_cache_dir=self.base_cache_dir)\n        script_path = os.path.join(config_dir, 'notifications.py')\n        self.filter_script: Callable[[NotificationCommand], bool] = lambda nc: False\n        if os.path.exists(script_path):\n            import runpy\n            try:\n                m = runpy.run_path(script_path)\n                self.filter_script = m['main']\n            except Exception as e:\n                self.log(f'Failed to load {script_path} with error: {e}')\n        self.reset()\n\n    def reset(self) -> None:\n        self.icon_data_cache.clear()\n        self.in_progress_notification_commands: 'OrderedDict[int, NotificationCommand]' = OrderedDict()\n        self.in_progress_notification_commands_by_client_id: dict[str, NotificationCommand] = {}\n        self.pending_commands: dict[int, NotificationCommand] = {}\n\n    def notification_created(self, desktop_notification_id: int) -> NotificationCommand | None:\n        if n := self.in_progress_notification_commands.get(desktop_notification_id):\n            n.created_by_desktop = True\n            if n.timeout > 0 and not self.desktop_integration.supports_timeout_natively:\n                add_timer(partial(self.expire_notification, desktop_notification_id, id(n)), n.timeout / 1000, False)\n            return n\n        return None\n\n    def notification_activation_token_received(self, desktop_notification_id: int, token: str) -> None:\n        if n := self.in_progress_notification_commands.get(desktop_notification_id):\n            n.activation_token = token\n\n    def notification_activated(self, desktop_notification_id: int, button: int) -> None:\n        if n := self.in_progress_notification_commands.get(desktop_notification_id):\n            if not n.close_response_requested:\n                self.purge_notification(n)\n            if n.focus_requested:\n                self.channel.focus(n.channel_id, n.activation_token)\n            if n.report_requested:\n                self.channel.send(n.channel_id, f'99;i={n.identifier or \"0\"};{button or \"\"}')\n            if n.on_activation:\n                try:\n                    n.on_activation(n, button)\n                except Exception as e:\n                    self.log('Notification on_activation handler failed with error:', e)\n\n    def notification_replaced(self, old_cmd: NotificationCommand, new_cmd: NotificationCommand) -> None:\n        if old_cmd.desktop_notification_id != new_cmd.desktop_notification_id:\n            self.in_progress_notification_commands.pop(old_cmd.desktop_notification_id, None)\n        if old_cmd.on_update is not None:\n            try:\n                old_cmd.on_update(old_cmd, new_cmd)\n            except Exception as e:\n                self.log('Notification on_update handler failed with error:', e)\n\n    def notification_closed(self, desktop_notification_id: int) -> None:\n        if n := self.in_progress_notification_commands.get(desktop_notification_id):\n            self.purge_notification(n)\n            if n.close_response_requested and self.desktop_integration.supports_close_events:\n                self.send_closed_response(n.channel_id, n.identifier)\n            if n.on_close is not None:\n                try:\n                    n.on_close(n)\n                except Exception as e:\n                    self.log('Notification on_close handler failed with error:', e)\n\n    def create_notification_cmd(self) -> NotificationCommand:\n        return NotificationCommand(ref(self.icon_data_cache), self.log)\n\n    def send_test_notification(self) -> None:\n        boss = get_boss()\n        if w := boss.active_window:\n            from time import monotonic\n            cmd = self.create_notification_cmd()\n            now = monotonic()\n            cmd.title = f'Test {now}'\n            cmd.body = f'At: {now}'\n            cmd.on_activation = print\n            self.notify_with_command(cmd, w.id)\n\n    def send_new_version_notification(self, version: str) -> None:\n        cmd = self.create_notification_cmd()\n        cmd.title = 'kitty update available!'\n        cmd.body = f'kitty version {version} released'\n        cmd.on_activation = self.desktop_integration.on_new_version_notification_activation\n        self.notify_with_command(cmd, 0)\n\n    def is_notification_allowed(self, cmd: NotificationCommand, channel_id: int, apply_filter_rules: bool = True) -> bool:\n        if cmd.only_when is not OnlyWhen.always and cmd.only_when is not OnlyWhen.unset:\n            ui_state = self.channel.ui_state(channel_id)\n            if ui_state.has_keyboard_focus:\n                return False\n            if cmd.only_when is OnlyWhen.invisible and ui_state.is_visible:\n                return False\n        return True\n\n    @property\n    def filter_rules(self) -> Iterator[str]:\n        return iter(get_options().filter_notification.keys())\n\n    def is_notification_filtered(self, cmd: NotificationCommand) -> bool:\n        if self.filter_script(cmd):\n            self.log(f'Notification {cmd.title!r} filtered out by script')\n            return True\n        for rule in self.filter_rules:\n            if cmd.matches_rule(rule):\n                self.log(f'Notification {cmd.title!r} filtered out by filter_notification rule: {rule}')\n                return True\n        return False\n\n    def notify_with_command(self, cmd: NotificationCommand, channel_id: int) -> int | None:\n        cmd.channel_id = channel_id\n        cmd.finalise()\n        if not cmd.title or not self.is_notification_allowed(cmd, channel_id) or self.is_notification_filtered(cmd):\n            return None\n        existing_desktop_notification_id: int | None = None\n        existing_cmd = self.in_progress_notification_commands_by_client_id.get(cmd.identifier) if cmd.identifier else None\n        if existing_cmd:\n            existing_desktop_notification_id = existing_cmd.desktop_notification_id\n        desktop_notification_id = self.desktop_integration.notify(cmd, existing_desktop_notification_id)\n        self.register_in_progress_notification(cmd, desktop_notification_id)\n        if existing_cmd:\n            self.notification_replaced(existing_cmd, cmd)\n        if not self.desktop_integration.supports_close_events and cmd.close_response_requested:\n            self.send_closed_response(channel_id, cmd.identifier, untracked=True)\n        return desktop_notification_id\n\n    def expire_notification(self, desktop_notification_id: int, command_id: int, timer_id: int) -> None:\n        if n := self.in_progress_notification_commands.get(desktop_notification_id):\n            if id(n) == command_id:\n                self.desktop_integration.close_notification(desktop_notification_id)\n\n    def register_in_progress_notification(self, cmd: NotificationCommand, desktop_notification_id: int) -> None:\n        cmd.desktop_notification_id = desktop_notification_id\n        self.in_progress_notification_commands[desktop_notification_id] = cmd\n        if cmd.identifier:\n            self.in_progress_notification_commands_by_client_id[cmd.identifier] = cmd\n        if len(self.in_progress_notification_commands) > 128:\n            _, cmd = self.in_progress_notification_commands.popitem(False)\n            self.in_progress_notification_commands_by_client_id.pop(cmd.identifier, None)\n\n    def parse_notification_cmd(\n        self, prev_cmd: NotificationCommand, channel_id: int, raw: str\n    ) -> NotificationCommand | None:\n        metadata, payload = raw.partition(';')[::2]\n        cmd = self.create_notification_cmd()\n        try:\n            payload_type, payload_is_encoded = cmd.parse_metadata(metadata, prev_cmd)\n        except Exception:\n            self.log('Malformed metadata section in OSC 99: ' + metadata)\n            return None\n        if payload_type is PayloadType.query:\n            self.channel.send(channel_id, self.desktop_integration.query_response(cmd.identifier))\n            return None\n        if payload_type is PayloadType.alive:\n            if cmd.identifier:\n                self.desktop_integration.query_live_notifications(channel_id, cmd.identifier)\n            return None\n        if payload_type is PayloadType.close:\n            if cmd.identifier:\n                to_close = self.in_progress_notification_commands_by_client_id.get(cmd.identifier)\n                if to_close:\n                    if not self.desktop_integration.close_notification(to_close.desktop_notification_id):\n                        if to_close.close_response_requested:\n                            self.send_closed_response(to_close.channel_id, to_close.identifier)\n                        self.purge_notification(to_close)\n            return None\n\n        if payload_type is PayloadType.unknown:\n            self.log(f'OSC 99: unknown payload type: {payload_type}, ignoring payload')\n            payload = ''\n\n        cmd.set_payload(payload_type, payload_is_encoded, payload, prev_cmd)\n        return cmd\n\n    def send_closed_response(self, channel_id: int, client_id: str, untracked: bool = False) -> None:\n        payload = 'untracked' if untracked else ''\n        self.channel.send(channel_id, f'99;i={client_id}:p={PayloadType.close.value};{payload}')\n\n    def send_live_response(self, channel_id: int, client_id: str, live_desktop_ids: Sequence[int]) -> None:\n        ids = []\n        for desktop_notification_id in live_desktop_ids:\n            if n := self.in_progress_notification_commands.get(desktop_notification_id):\n                if n.identifier and n.channel_id == channel_id:\n                    ids.append(n.identifier)\n        self.channel.send(channel_id, f'99;i={client_id}:p={PayloadType.alive.value};{\",\".join(ids)}')\n\n    def purge_dead_notifications(self, live_desktop_ids: Sequence[int]) -> None:\n        for d in set(self.in_progress_notification_commands) - set(live_desktop_ids):\n            if debug_desktop_integration:\n                log_error(f'Purging dead notification {d} from list of live notifications:', live_desktop_ids)\n            self.purge_notification(self.in_progress_notification_commands[d])\n\n    def purge_notification(self, cmd: NotificationCommand) -> None:\n        self.in_progress_notification_commands_by_client_id.pop(cmd.identifier, None)\n        self.in_progress_notification_commands.pop(cmd.desktop_notification_id, None)\n\n    def handle_notification_cmd(self, channel_id: int, osc_code: int, raw: str) -> None:\n        if osc_code == 99:\n            cmd = self.pending_commands.pop(channel_id, None) or self.create_notification_cmd()\n            q = self.parse_notification_cmd(cmd, channel_id, raw)\n            if q is not None:\n                if q.done:\n                    self.notify_with_command(q, channel_id)\n                else:\n                    self.pending_commands[channel_id] = q\n        elif osc_code == 9:\n            n = self.create_notification_cmd()\n            n.title = raw\n            self.notify_with_command(n, channel_id)\n        elif osc_code == 777:\n            n = self.create_notification_cmd()\n            parts = raw.split(';', 1)\n            n.title, n.body = parts[0], (parts[1] if len(parts) > 1 else '')\n            self.notify_with_command(n, channel_id)\n\n    def close_notification(self, desktop_notification_id: int) -> None:\n        self.desktop_integration.close_notification(desktop_notification_id)\n\n    def cleanup(self) -> None:\n        del self.icon_data_cache\n"
  },
  {
    "path": "kitty/open_actions.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport posixpath\nimport shlex\nfrom collections.abc import Iterable, Iterator\nfrom contextlib import suppress\nfrom typing import Any, NamedTuple, cast\nfrom urllib.parse import ParseResult, unquote, urlparse\n\nfrom .conf.utils import KeyAction, to_cmdline_implementation\nfrom .constants import config_dir\nfrom .fast_data_types import get_options\nfrom .guess_mime_type import guess_type\nfrom .options.utils import ActionAlias, MapType, resolve_aliases_and_parse_actions\nfrom .types import run_once\nfrom .typing_compat import MatchType\nfrom .utils import expandvars, get_editor, log_error, resolved_shell\n\n\nclass MatchCriteria(NamedTuple):\n    type: MatchType\n    value: str\n\n\nclass OpenAction(NamedTuple):\n    match_criteria: tuple[MatchCriteria, ...]\n    actions: tuple[KeyAction, ...]\n\n\ndef parse(lines: Iterable[str]) -> Iterator[OpenAction]:\n    match_criteria: list[MatchCriteria] = []\n    raw_actions: list[str] = []\n    alias_map: dict[str, list[ActionAlias]] = {}\n    entries = []\n\n    for line in lines:\n        line = line.strip()\n        if line.startswith('#'):\n            continue\n        if not line:\n            if match_criteria and raw_actions:\n                entries.append((tuple(match_criteria), tuple(raw_actions)))\n            match_criteria = []\n            raw_actions = []\n            continue\n        parts = line.split(maxsplit=1)\n        if len(parts) != 2:\n            continue\n        key, rest = parts\n        key = key.lower()\n        if key == 'action':\n            raw_actions.append(rest)\n        elif key in ('mime', 'ext', 'protocol', 'file', 'path', 'url', 'fragment_matches'):\n            if key != 'url':\n                rest = rest.lower()\n            match_criteria.append(MatchCriteria(cast(MatchType, key), rest))\n        elif key == 'action_alias':\n            try:\n                alias_name, alias_val = rest.split(maxsplit=1)\n            except Exception:\n                continue\n            alias_map[alias_name] = [ActionAlias(alias_name, alias_val)]\n        else:\n            log_error(f'Ignoring malformed open actions line: {line}')\n\n    if match_criteria and raw_actions:\n        entries.append((tuple(match_criteria), tuple(raw_actions)))\n\n    with to_cmdline_implementation.filter_env_vars(\n        'URL', 'FILE_PATH', 'FILE', 'FRAGMENT', 'URL_PATH', 'NETLOC',\n        EDITOR=shlex.join(get_editor()),\n        SHELL=resolved_shell(get_options())[0]\n    ):\n        for (mc, action_defns) in entries:\n            actions: list[KeyAction] = []\n            for defn in action_defns:\n                actions.extend(resolve_aliases_and_parse_actions(defn, alias_map, MapType.OPEN_ACTION))\n            yield OpenAction(mc, tuple(actions))\n\n\ndef url_matches_criterion(purl: 'ParseResult', url: str, unquoted_path: str, mc: MatchCriteria) -> bool:\n    if mc.type == 'url':\n        import re\n        try:\n            pat = re.compile(mc.value)\n        except re.error:\n            return False\n        return pat.search(unquote(url)) is not None\n\n    if mc.type == 'mime':\n        import fnmatch\n        mt = guess_type(unquoted_path, allow_filesystem_access=purl.scheme in ('', 'file'))\n        if not mt:\n            return False\n        mt = mt.lower()\n        for mpat in mc.value.split(','):\n            mpat = mpat.strip()\n            with suppress(Exception):\n                if fnmatch.fnmatchcase(mt, mpat):\n                    return True\n        return False\n\n    if mc.type == 'ext':\n        if not purl.path:\n            return False\n        path = unquoted_path.lower()\n        for ext in mc.value.split(','):\n            ext = ext.strip()\n            if path.endswith(f'.{ext}'):\n                return True\n        return False\n\n    if mc.type == 'protocol':\n        protocol = (purl.scheme or 'file').lower()\n        for key in mc.value.split(','):\n            if key.strip() == protocol:\n                return True\n        return False\n\n    if mc.type == 'fragment_matches':\n        import re\n        try:\n            pat = re.compile(mc.value)\n        except re.error:\n            return False\n\n        return pat.search(unquote(purl.fragment)) is not None\n\n    if mc.type == 'path':\n        import fnmatch\n        try:\n            return fnmatch.fnmatchcase(unquoted_path.lower(), mc.value)\n        except Exception:\n            return False\n\n    if mc.type == 'file':\n        import fnmatch\n        try:\n            fname = posixpath.basename(unquoted_path)\n        except Exception:\n            return False\n        try:\n            return fnmatch.fnmatchcase(fname.lower(), mc.value)\n        except Exception:\n            return False\n\n\ndef url_matches_criteria(purl: 'ParseResult', url: str, unquoted_path: str, criteria: Iterable[MatchCriteria]) -> bool:\n    for x in criteria:\n        try:\n            if not url_matches_criterion(purl, url, unquoted_path, x):\n                return False\n        except Exception:\n            return False\n    return True\n\n\ndef actions_for_url_from_list(url: str, actions: Iterable[OpenAction]) -> Iterator[KeyAction]:\n    try:\n        purl = urlparse(url)\n    except Exception:\n        return\n    path = unquote(purl.path)\n    up = purl.path\n    netloc = unquote(purl.netloc) if purl.netloc else ''\n    if purl.query:\n        up += f'?{purl.query}'\n    frag = unquote(purl.fragment) if purl.fragment else ''\n    if frag:\n        up += f'#{purl.fragment}'\n\n    env = {\n        'URL': url,\n        'FILE_PATH': path,\n        'URL_PATH': up,\n        'FILE': posixpath.basename(path),\n        'FRAGMENT': frag,\n        'NETLOC': netloc,\n    }\n\n    def expand(x: Any) -> Any:\n        as_bytes = isinstance(x, bytes)\n        if as_bytes:\n            x = x.decode('utf-8')\n        if isinstance(x, str):\n            ans = expandvars(x, env, fallback_to_os_env=False)\n            if as_bytes:\n                return ans.encode('utf-8')\n            return ans\n        return x\n\n    for action in actions:\n        if url_matches_criteria(purl, url, path, action.match_criteria):\n            for ac in action.actions:\n                yield ac._replace(args=tuple(map(expand, ac.args)))\n            return\n\n\nactions_cache: dict[str, tuple[os.stat_result, tuple[OpenAction, ...]]] = {}\n\n\ndef load_actions_from_path(path: str) -> tuple[OpenAction, ...]:\n    try:\n        st = os.stat(path)\n    except OSError:\n        return ()\n    x = actions_cache.get(path)\n    if x is None or x[0].st_mtime != st.st_mtime:\n        with open(path) as f:\n            actions_cache[path] = st, tuple(parse(f))\n    else:\n        return x[1]\n    return actions_cache[path][1]\n\n\ndef load_open_actions() -> tuple[OpenAction, ...]:\n    return load_actions_from_path(os.path.join(config_dir, 'open-actions.conf'))\n\n\ndef load_launch_actions() -> tuple[OpenAction, ...]:\n    return load_actions_from_path(os.path.join(config_dir, 'launch-actions.conf'))\n\n\ndef clear_caches() -> None:\n    actions_cache.clear()\n\n\n@run_once\ndef default_open_actions() -> tuple[OpenAction, ...]:\n    return tuple(parse('''\\\n# Open kitty HTML docs links\nprotocol kitty+doc\naction show_kitty_doc $URL_PATH\n'''.splitlines()))\n\n\n@run_once\ndef default_launch_actions() -> tuple[OpenAction, ...]:\n    return tuple(parse('''\\\n# Open script files. Change confirm-always to confirm-never or confirm-if-needed to\n# disable confirmation for all or executable files respectively.\nprotocol file\next sh,command,tool\naction launch --hold --type=os-window kitten __shebang__ confirm-always $FILE_PATH $SHELL\n\n# Open shell specific script files\nprotocol file\next fish,bash,zsh\naction launch --hold --type=os-window kitten __shebang__ confirm-always $FILE_PATH __ext__\n\n# Open directories\nprotocol file\nmime inode/directory\naction launch --type=os-window --cwd -- $FILE_PATH\n\n# Open executable file. Remove kitten __confirm_and_run_exe__ to execute\n# without confirmation.\nprotocol file\nmime inode/executable,application/vnd.microsoft.portable-executable\naction launch --hold --type=os-window -- kitten __confirm_and_run_exe__ $FILE_PATH\n\n# Open text files without fragments in the editor\nprotocol file\nmime text/*\naction launch --type=os-window -- $EDITOR -- $FILE_PATH\n\n# Open image files with icat\nprotocol file\nmime image/*\naction launch --type=os-window kitten icat --hold -- $FILE_PATH\n\n# Open ssh URLs with ssh command\nprotocol ssh\naction launch --type=os-window ssh -- $URL\n'''.splitlines()))\n\n\ndef actions_for_url(url: str, actions_spec: str | None = None) -> Iterator[KeyAction]:\n    if actions_spec is None:\n        actions = load_open_actions()\n    else:\n        actions = tuple(parse(actions_spec.splitlines()))\n    found = False\n    for action in actions_for_url_from_list(url, actions):\n        found = True\n        yield action\n    if not found:\n        yield from actions_for_url_from_list(url, default_open_actions())\n\n\ndef actions_for_launch(url: str) -> Iterator[KeyAction]:\n    # Custom launch actions using kitty URL scheme needs to be prefixed with `kitty:///launch/`\n    if url.startswith('kitty://') and not url.startswith('kitty:///launch/'):\n        return\n    found = False\n    for action in actions_for_url_from_list(url, load_launch_actions()):\n        found = True\n        yield action\n    if not found:\n        yield from actions_for_url_from_list(url, default_launch_actions())\n"
  },
  {
    "path": "kitty/options/__init__.py",
    "content": ""
  },
  {
    "path": "kitty/options/definition.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n# After editing this file run ./gen-config.py to apply the changes\n\nimport string\n\nfrom kitty.conf.types import Action, Definition\nfrom kitty.constants import website_url\nfrom kitty.options.utils import pointer_shape_names\n\ndefinition = Definition(\n    'kitty',\n    Action('map', 'parse_map', {'keyboard_modes': 'KeyboardModeMap', 'alias_map': 'AliasMap'},\n           ['KeyDefinition', 'kitty.fast_data_types.SingleKey']),\n    Action('mouse_map', 'parse_mouse_map', {'mousemap': 'MouseMap'}, ['MouseMapping']),\n    has_color_table=True,\n)\ndefinition.add_deprecation('deprecated_hide_window_decorations_aliases', 'x11_hide_window_decorations', 'macos_hide_titlebar')\ndefinition.add_deprecation('deprecated_macos_show_window_title_in_menubar_alias', 'macos_show_window_title_in_menubar')\ndefinition.add_deprecation('deprecated_send_text', 'send_text')\ndefinition.add_deprecation('deprecated_adjust_line_height', 'adjust_line_height', 'adjust_column_width', 'adjust_baseline')\ndefinition.add_deprecation('deprecated_scrollback_indicator_opacity', 'scrollback_indicator_opacity')\n\nagr = definition.add_group\negr = definition.end_group\nopt = definition.add_option\nmap = definition.add_map\nmma = definition.add_mouse_map\n\n# fonts {{{\nagr('fonts', 'Fonts', '''\nkitty has very powerful font management. You can configure individual font faces\nand even specify special fonts for particular characters.\n''')\n\nopt('font_family', 'monospace', option_type='parse_font_spec',\n    long_text='''\nYou can specify different fonts for the bold/italic/bold-italic variants.\nThe easiest way to select fonts is to run the ``kitten choose-fonts`` command\nwhich will present a nice UI for you to select the fonts you want with previews\nand support for selecting variable fonts and font features. If you want to learn\nto select fonts manually, read the :ref:`font specification syntax <font_spec_syntax>`.\n'''\n    )\n\nopt('bold_font', 'auto', option_type='parse_font_spec')\n\nopt('italic_font', 'auto', option_type='parse_font_spec')\n\nopt('bold_italic_font', 'auto', option_type='parse_font_spec')\n\nopt('font_size', '11.0',\n    option_type='to_font_size', ctype='double',\n    long_text='Font size (in pts).'\n    )\n\nopt('force_ltr', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nkitty does not support BIDI (bidirectional text), however, for RTL scripts,\nwords are automatically displayed in RTL. That is to say, in an RTL script, the\nwords \"HELLO WORLD\" display in kitty as \"WORLD HELLO\", and if you try to select\na substring of an RTL-shaped string, you will get the character that would be\nthere had the string been LTR. For example, assuming the Hebrew word\nירושלים, selecting the character that on the screen appears to be ם actually\nwrites into the selection buffer the character י. kitty's default behavior is\nuseful in conjunction with a filter to reverse the word order, however, if you\nwish to manipulate RTL glyphs, it can be very challenging to work with, so this\noption is provided to turn it off. Furthermore, this option can be used with the\ncommand line program :link:`GNU FriBidi\n<https://github.com/fribidi/fribidi#executable>` to get BIDI support, because it\nwill force kitty to always treat the text as LTR, which FriBidi expects for\nterminals.\n'''\n    )\n\nopt('+symbol_map', 'U+E0A0-U+E0A3,U+E0C0-U+E0C7 PowerlineSymbols',\n    option_type='symbol_map',\n    add_to_default=False,\n    long_text='''\nMap the specified Unicode codepoints to a particular font. Useful if you need\nspecial rendering for some symbols, such as for Powerline. Avoids the need for\npatched fonts. Each Unicode code point is specified in the form ``U+<code\npoint in hexadecimal>``. You can specify multiple code points, separated by\ncommas and ranges separated by hyphens. This option can be specified multiple\ntimes. The syntax is::\n\n    symbol_map codepoints Font Family Name\n'''\n    )\n\nopt('+narrow_symbols', 'U+E0A0-U+E0A3,U+E0C0-U+E0C7 1',\n    option_type='narrow_symbols',\n    add_to_default=False,\n    long_text='''\nUsually, for Private Use Unicode characters and some symbol/dingbat characters,\nif the character is followed by one or more spaces, kitty will use those extra\ncells to render the character larger, if the character in the font has a wide\naspect ratio. Using this option you can force kitty to restrict the specified\ncode points to render in the specified number of cells (defaulting to one cell).\nThis option can be specified multiple times. The syntax is::\n\n    narrow_symbols codepoints [optionally the number of cells]\n'''\n    )\n\n\nopt('disable_ligatures', 'never',\n    option_type='disable_ligatures', ctype='int',\n    long_text='''\nChoose how you want to handle multi-character ligatures. The default is to\nalways render them. You can tell kitty to not render them when the cursor is\nover them by using :code:`cursor` to make editing easier, or have kitty never\nrender them at all by using :code:`always`, if you don't like them. The ligature\nstrategy can be set per-window either using the kitty remote control facility\nor by defining shortcuts for it in :file:`kitty.conf`, for example::\n\n    map alt+1 disable_ligatures_in active always\n    map alt+2 disable_ligatures_in all never\n    map alt+3 disable_ligatures_in tab cursor\n\nNote that this refers to programming ligatures, typically implemented using the\n:code:`calt` OpenType feature. For disabling general ligatures, use the\n:opt:`font_features` option.\n'''\n    )\n\nopt('+font_features', 'none', option_type='font_features', ctype='!font_features',\n    add_to_default=False, long_text='''\nChoose exactly which OpenType features to enable or disable. Note that for the\nmain fonts, features can be specified when selecting the font using the choose-fonts kitten.\nThis setting is useful for fallback fonts.\n\nSome fonts might have features worthwhile in a terminal. For example, Fira Code\nincludes a discretionary feature, :code:`zero`, which in that font changes the\nappearance of the zero (0), to make it more easily distinguishable from Ø. Fira\nCode also includes other discretionary features known as Stylistic Sets which\nhave the tags :code:`ss01` through :code:`ss20`.\n\nFor the exact syntax to use for individual features, see the\n:link:`HarfBuzz documentation\n<https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`.\n\nNote that this code is indexed by PostScript name, and not the font family. This\nallows you to define very precise feature settings; e.g. you can disable a\nfeature in the italic font but not in the regular font.\n\nOn Linux, font features are first read from the FontConfig database and then\nthis option is applied, so they can be configured in a single, central place.\n\nTo get the PostScript name for a font, use the ``fc-scan file.ttf`` command on Linux\nor the `Font Book tool on macOS <https://apple.stackexchange.com/questions/79875/how-can-i-get-the-postscript-name-of-a-ttf-font-installed-in-os-x>`__.\n\nEnable alternate zero and oldstyle numerals::\n\n    font_features FiraCode-Retina +zero +onum\n\nEnable only alternate zero in the bold font::\n\n    font_features FiraCode-Bold +zero\n\nDisable the normal ligatures, but keep the :code:`calt` feature which (in this\nfont) breaks up monotony::\n\n    font_features TT2020StyleB-Regular -liga +calt\n\nIn conjunction with :opt:`force_ltr`, you may want to disable Arabic shaping\nentirely, and only look at their isolated forms if they show up in a document.\nYou can do this with e.g.::\n\n    font_features UnifontMedium +isol -medi -fina -init\n'''\n    )\n\nopt('+modify_font', '',\n    option_type='modify_font', ctype='!modify_font',\n    add_to_default=False,\n    long_text='''\nModify font characteristics such as the position or thickness of the underline\nand strikethrough. The modifications can have the suffix :code:`px` for pixels\nor :code:`%` for percentage of original value. No suffix means use pts.\nFor example::\n\n    modify_font underline_position -2\n    modify_font underline_thickness 150%\n    modify_font strikethrough_thickness 200%\n    modify_font strikethrough_position 2px\n\nAdditionally, you can modify the size of the cell in which each font glyph is\nrendered and the baseline at which the glyph is placed in the cell.\nFor example::\n\n    modify_font cell_width 80%\n    modify_font cell_height -2px\n    modify_font baseline 3\n\nNote that modifying the baseline will automatically adjust the underline and\nstrikethrough positions by the same amount. Increasing the baseline raises\nglyphs inside the cell and decreasing it lowers them. Decreasing the cell size\nmight cause rendering artifacts, so use with care.\n''')\n\nopt('box_drawing_scale', '0.001, 1, 1.5, 2',\n    option_type='box_drawing_scale', ctype='!box_drawing_scale',\n    long_text='''\nThe sizes of the lines used for the box drawing Unicode characters. These values\nare in pts. They will be scaled by the monitor DPI to arrive at a pixel value.\nThere must be four values corresponding to thin, normal, thick, and very thick\nlines.\n'''\n    )\n\nopt('undercurl_style', 'thin-sparse', ctype='undercurl_style',\n    choices=('thin-sparse', 'thin-dense', 'thick-sparse', 'thick-dense'),\n    long_text='''\nThe style with which undercurls are rendered. This option takes the form\n:code:`(thin|thick)-(sparse|dense)`. Thin and thick control the thickness of the\nundercurl. Sparse and dense control how often the curl oscillates. With sparse\nthe curl will peak once per character, with dense twice. Changing this\noption dynamically via reloading the config or remote control is undefined.\n'''\n    )\n\n\nopt('underline_exclusion', '1', option_type='underline_exclusion', ctype='!underline_exclusion', long_text='''\nBy default kitty renders gaps in underlines when they overlap with descenders\n(the parts of letters below the baseline, such as for y, q, p etc.). This option\ncontrols the thickness of the gaps. It can be either a unitless number in which\ncase it is a fraction of the underline thickness as specified in the font or\nit can have a suffix of :code:`px` for pixels or :code:`pt` for points. Set to zero\nto disable the gaps. Changing this option dynamically via reloading the config or remote\ncontrol is undefined.\n''')\n\n\nopt('text_composition_strategy', 'platform',\n    ctype='!text_composition_strategy',\n    long_text='''\nControl how kitty composites text glyphs onto the background color. The default\nvalue of :code:`platform` tries for text rendering as close to \"native\" for\nthe platform kitty is running on as possible.\n\nA value of :code:`legacy` uses the old (pre kitty 0.28) strategy for how glyphs\nare composited. This will make dark text on light backgrounds look thicker and\nlight text on dark backgrounds thinner. It might also make some text appear like\nthe strokes are uneven.\n\nYou can fine tune the actual contrast curve used for glyph composition by\nspecifying up to two space-separated numbers for this setting.\n\nThe first number is the gamma adjustment, which controls the thickness of dark\ntext on light backgrounds. Increasing the value will make text appear thicker.\nThe default value for this is :code:`1.0` on Linux and :code:`1.7` on macOS.\nValid values are :code:`0.01` and above. The result is scaled based on the\nluminance difference between the background and the foreground. Dark text on\nlight backgrounds receives the full impact of the curve while light text on dark\nbackgrounds is affected very little.\n\nThe second number is an additional multiplicative contrast. It is percentage\nranging from :code:`0` to :code:`100`. The default value is :code:`0` on Linux\nand :code:`30` on macOS.\n\nIf you wish to achieve similar looking thickness in light and dark themes, a good way\nto experiment is start by setting the value to :code:`1.0 0` and use a dark theme.\nThen adjust the second parameter until it looks good. Then switch to a light theme\nand adjust the first parameter until the perceived thickness matches the dark theme.\n''')\n\nopt('text_fg_override_threshold', '0', option_type='text_fg_override_threshold', long_text='''\nA setting to prevent low contrast between foreground and background colors.\nUseful when working with applications that use colors that do not contrast\nwell with your preferred color scheme. The default value is :code:`0`, which means no color overriding is performed.\nThere are two modes of operation:\n\nA value with the suffix :code:`ratio` represents the minimum accepted contrast ratio between the foreground and background color.\nPossible values range from :code:`0.0 ratio` to :code:`21.0 ratio`.\nFor example, to meet :link:`WCAG level AA <https://en.wikipedia.org/wiki/Web_Content_Accessibility_Guidelines>`\na value of :code:`4.5 ratio` can be provided.\nThe algorithm is implemented using :link:`HSLuv <https://www.hsluv.org/>` which enables it to change\nthe perceived lightness of a color just as much as needed without really changing its hue and saturation.\n\nA value with the suffix :code:`%` represents the minimum accepted difference in luminance\nbetween the foreground and background color, below which kitty will override the foreground color.\nIt is percentage ranging from :code:`0 %` to :code:`100 %`. If the difference in luminance of the\nforeground and background is below this threshold, the foreground color will be set\nto white if the background is dark or black if the background is light.\n\nWARNING: Some programs use characters (such as block characters) for graphics\ndisplay and may expect to be able to set the foreground and background to the\nsame color (or similar colors). If you see unexpected stripes, dots, lines,\nincorrect color, no color where you expect color, or any kind of graphic\ndisplay problem try setting :opt:`text_fg_override_threshold` to :code:`0` to\nsee if this is the cause of the problem or consider using the :code:`ratio` mode of operation\ndescribed above instead of the :code:`%` mode of operation.\n''')\n\negr()  # }}}\n\n\n# cursor {{{\nagr('cursor', 'Text cursor customization')\n\nopt('cursor', '#cccccc',\n    option_type='to_color_or_none',\n    long_text='''\nDefault text cursor color. If set to the special value :code:`none` the cursor will\nbe rendered with a \"reverse video\" effect. Its color will be the color of the\ntext in the cell it is over and the text will be rendered with the background\ncolor of the cell. Note that if the program running in the terminal sets a\ncursor color, this takes precedence. Also, the cursor colors are modified if\nthe cell background and foreground colors have very low contrast. Note that some\nthemes set this value, so if you want to override it, place your value after\nthe lines where the theme file is included.\n'''\n    )\n\nopt('cursor_text_color', '#111111',\n    option_type='cursor_text_color',\n    long_text='''\nThe color of text under the cursor. If you want it rendered with the\nbackground color of the cell underneath instead, use the special keyword:\n`background`. Note that if :opt:`cursor` is set to :code:`none` then this option\nis ignored. Note that some themes set this value, so if you want to override it,\nplace your value after the lines where the theme file is included.\n'''\n    )\n\nopt('cursor_shape', 'block',\n    option_type='to_cursor_shape', ctype='int',\n    long_text='''\nThe cursor shape can be one of :code:`block`, :code:`beam`, :code:`underline`.\nNote that when reloading the config this will be changed only if the cursor\nshape has not been set by the program running in the terminal. This sets the\ndefault cursor shape, applications running in the terminal can override it. In\nparticular, :ref:`shell integration <shell_integration>` in kitty sets the\ncursor shape to :code:`beam` at shell prompts. You can avoid this by setting\n:opt:`shell_integration` to :code:`no-cursor`.\n'''\n    )\n\nopt('cursor_shape_unfocused', 'hollow', option_type='to_cursor_unfocused_shape', ctype='int', long_text='''\nDefines the text cursor shape when the OS window is not focused. The unfocused\ncursor shape can be one of :code:`block`, :code:`beam`, :code:`underline`,\n:code:`hollow` and :code:`unchanged` (leave the cursor shape as it is).\n''')\n\nopt('cursor_beam_thickness', '1.5',\n    option_type='positive_float', ctype='float',\n    long_text='The thickness of the beam cursor (in pts).'\n    )\n\nopt('cursor_underline_thickness', '2.0',\n    option_type='positive_float', ctype='float',\n    long_text='The thickness of the underline cursor (in pts).'\n    )\n\nopt('cursor_blink_interval', '-1', option_type='cursor_blink_interval', ctype='!cursor_blink_interval', long_text='''\nThe interval to blink the cursor (in seconds). Set to zero to disable blinking.\nNegative values mean use system default. Note that the minimum interval will be\nlimited to :opt:`repaint_delay`. You can also animate the cursor blink by specifying\nan :term:`easing function`. For example, setting this to option to :code:`0.5 ease-in-out`\nwill cause the cursor blink to be animated over a second, in the first half of the second\nit will go from opaque to transparent and then back again over the next half. You can specify\ndifferent easing functions for the two halves, for example: :code:`-1 linear ease-out`. kitty\nsupports all the :link:`CSS easing functions <https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function>`.\nNote that turning on animations uses extra power as it means the screen is redrawn multiple times\nper blink interval. See also, :opt:`cursor_stop_blinking_after`. This setting also controls blinking\ntext, which blinks in exact rhythm with the cursor.\n''')\n\nopt('cursor_stop_blinking_after', '15.0',\n    option_type='positive_float', ctype='time',\n    long_text='''\nStop blinking cursor after the specified number of seconds of keyboard\ninactivity. Set to zero to never stop blinking. This setting also controls\nblinking text, which blinks in exact rhythm with the cursor.\n''')\n\nopt('cursor_trail', '0',\n    option_type='positive_int', ctype='time-ms',\n    long_text='''\nSet this to a value larger than zero to enable a \"cursor trail\" animation.\nThis is an animation that shows a \"trail\" following the movement of the text cursor.\nIt makes it easy to follow large cursor jumps and makes for a cool visual effect\nof the cursor zooming around the screen. The actual value of this option\ncontrols when the animation is triggered. It is a number of milliseconds. The\ntrail animation only follows cursors that have stayed in their position for longer\nthan the specified number of milliseconds. This prevents trails from appearing\nfor cursors that rapidly change their positions during UI updates in complex applications.\nSee :opt:`cursor_trail_decay` to control the animation speed and :opt:`cursor_trail_start_threshold`\nto control when a cursor trail is started.\n''')\n\nopt('cursor_trail_decay', '0.1 0.4',\n    option_type='cursor_trail_decay',\n    ctype='!cursor_trail_decay',\n    long_text='''\nControls the decay times for the cursor trail effect when the :opt:`cursor_trail`\nis enabled. This option accepts two positive float values specifying the\nfastest and slowest decay times in seconds. The first value corresponds to the\nfastest decay time (minimum), and the second value corresponds to the slowest\ndecay time (maximum). The second value must be equal to or greater than the\nfirst value. Smaller values result in a faster decay of the cursor trail.\nAdjust these values to control how quickly the cursor trail fades away.\n''')\n\nopt('cursor_trail_start_threshold', '2',\n    option_type='positive_int', ctype='int',\n    long_text='''\nSet the distance threshold for starting the cursor trail. This option accepts a\npositive integer value that represents the minimum number of cells the\ncursor must move before the trail is started. When the cursor moves less than\nthis threshold, the trail is skipped, reducing unnecessary cursor trail\nanimation.\n''')\n\nopt('cursor_trail_color', 'none',\n    option_type='to_color_or_none',\n    ctype='!cursor_trail_color',\n    long_text='''\nSet the color of the cursor trail when :opt:`cursor_trail` is enabled.\nIf set to 'none' (the default), the cursor trail will use the cursor's\nbackground color. Otherwise, specify a color value (e.g., #ff0000 for red,\nor a named color like 'red'). This allows you to customize the appearance\nof the cursor trail independently of the cursor color.\n''')\n\negr()  # }}}\n\n# scrollback {{{\nagr('scrollback', 'Scrollback')\n\nopt('scrollback_lines', '2000',\n    option_type='scrollback_lines',\n    long_text='''\nNumber of lines of history to keep in memory for scrolling back. Memory is\nallocated on demand. Negative numbers are (effectively) infinite scrollback.\nNote that using very large scrollback is not recommended as it can slow down\nperformance of the terminal and also use large amounts of RAM. Instead, consider\nusing :opt:`scrollback_pager_history_size`. Note that on config reload if this\nis changed it will only affect newly created windows, not existing ones.\n'''\n    )\n\nopt('scrollbar', 'scrolled', ctype='scrollbar', choices=(\n    'scrolled', 'always', 'never', 'hovered', 'scrolled-and-hovered'), long_text='''\\\nControl when the scrollbar is displayed.\n\n:code:`scrolled`\n    means when the scrolling backwards has started.\n:code:`hovered`\n    means when the mouse is hovering on the right edge of the window.\n:code:`scrolled-and-hovered`\n    means when the mouse is over the scrollbar region *and* scrolling backwards has started.\n:code:`always`\n    means whenever any scrollback is present\n:code:`never`\n    means disable the scrollbar.\n''')\n\nopt('scrollbar_interactive', 'yes', option_type='to_bool', ctype='bool', long_text='''\nIf disabled, the scrollbar will not be controllable via the mouse and all mouse events\nwill pass through the scrollbar.''')\n\nopt('scrollbar_jump_on_click', 'yes', option_type='to_bool', ctype='bool', long_text='''\nWhen enabled clicking in the scrollbar track will cause the scroll position to\njump to the clicked location, otherwise the scroll position will only move\ntowards the position by a single screenful, which is how traditional scrollbars behave.''')\n\nopt('scrollbar_width', '0.5', option_type='positive_float', ctype='float', long_text='''\nThe width of the scroll bar in units of cell width.\n''')\n\nopt('scrollbar_hover_width', '1', option_type='positive_float', ctype='float', long_text='''\nThe width of the scroll bar when the mouse is hovering over it, in units of cell width.\n''')\n\nopt('scrollbar_handle_opacity', '0.5', option_type='positive_float', ctype='float', long_text='''\nThe opacity of the scrollbar handle, 0 being fully transparent and 1 being full opaque.\n''')\n\nopt('scrollbar_radius', '0.3', option_type='positive_float', ctype='float', long_text='''\nThe radius (curvature) of the scrollbar handle in units of cell width. Should be less than\n:opt:`scrollbar_width`.\n''')\n\nopt('scrollbar_gap', '0.1', option_type='positive_float', ctype='float', long_text='''\nThe gap between the scrollbar and the window edge in units of cell width.\n''')\n\nopt('scrollbar_min_handle_height', '1', option_type='positive_float', ctype='float', long_text='''\nThe minimum height of the scrollbar handle in units of cell height. Prevents the handle\nfrom becoming too small when there is a lot of scrollback.''')\n\nopt('scrollbar_hitbox_expansion', '0.25', option_type='positive_float', ctype='float', long_text='''\nThe extra area around the handle to allow easier grabbing of the scollbar in units of cell width.''')\n\nopt('scrollbar_track_opacity', '0', option_type='positive_float', ctype='float', long_text='''\nThe opacity of the scrollbar track, 0 being fully transparent and 1 being full opaque.\n''')\n\nopt('scrollbar_track_hover_opacity', '0.1', option_type='positive_float', ctype='float', long_text='''\nThe opacity of the scrollbar track when the mouse is over the scrollbar,\n0 being fully transparent and 1 being full opaque.\n''')\n\nopt('scrollbar_handle_color', 'foreground', option_type='scrollbar_color', ctype='uint', long_text='''\nThe color of the scrollbar handle. A value of :code:`foreground` means to use\nthe current foreground text color, a value of :code:`selection_background` means to\nuse the current selection background color. Also, you can use an\narbitrary color, such as :code:`#12af59` or :code:`red`.\n''')\n\nopt('scrollbar_track_color', 'foreground', option_type='scrollbar_color', ctype='uint', long_text='''\nThe color of the scrollbar track. A value of :code:`foreground` means to use\nthe current foreground text color, a value of :code:`selection_background` means to\nuse the current selection background color. Also, you can use an\narbitrary color, such as :code:`#12af59` or :code:`red`.\n''')\n\nopt('scrollback_pager', 'less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER',\n    option_type='to_cmdline',\n    long_text='''\nProgram with which to view scrollback in a new window. The scrollback buffer is\npassed as STDIN to this program. If you change it, make sure the program you use\ncan handle ANSI escape sequences for colors and text formatting.\nINPUT_LINE_NUMBER in the command line above will be replaced by an integer\nrepresenting which line should be at the top of the screen. Similarly\nCURSOR_LINE and CURSOR_COLUMN will be replaced by the current cursor position or\nset to 0 if there is no cursor, for example, when showing the last command\noutput.\n\nIf you would rather use neovim to view the scrollback, use something like this::\n\n    scrollback_pager nvim --cmd 'set eventignore=FileType' +'nnoremap q ZQ' +'call nvim_open_term(0, {})' +'set nomodified nolist' +'$' -\n\nThe above works for neovim 0.12 and newer. There is also a dedicated plugin\n:link:`kitty-scrollback.nvim <https://github.com/mikesmithgh/kitty-scrollback.nvim>`\nyou can use with more features that works with older neovim as well.\n'''\n    )\n\nopt('scrollback_pager_history_size', '0',\n    option_type='scrollback_pager_history_size', ctype='uint',\n    long_text='''\nSeparate scrollback history size (in MB), used only for browsing the scrollback\nbuffer with pager. This separate buffer is not available for interactive\nscrolling but will be piped to the pager program when viewing scrollback buffer\nin a separate window. The current implementation stores the data in UTF-8, so\napproximately 10000 lines per megabyte at 100 chars per line, for pure ASCII,\nunformatted text. A value of zero or less disables this feature. The maximum\nallowed size is 4GB. Note that on config reload if this is changed it will only\naffect newly created windows, not existing ones.\n'''\n    )\n\nopt('scrollback_fill_enlarged_window', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='Fill new space with lines from the scrollback buffer after enlarging a window.'\n    )\n\nopt('wheel_scroll_multiplier', '5.0',\n    option_type='float', ctype='double',\n    long_text='''\nMultiplier for the number of lines scrolled by the mouse wheel. Note that this\nis only used for low precision scrolling devices, not for high precision\nscrolling devices on platforms such as macOS and Wayland. Use negative numbers\nto change scroll direction. See also :opt:`wheel_scroll_min_lines`.\n'''\n    )\n\nopt('wheel_scroll_min_lines', '1',\n    option_type='int', ctype='int',\n    long_text='''\nThe minimum number of lines scrolled by the mouse wheel. The :opt:`scroll\nmultiplier <wheel_scroll_multiplier>` only takes effect after it reaches this\nnumber. Note that this is only used for low precision scrolling devices like\nwheel mice that scroll by very small amounts when using the wheel. With a\nnegative number, the minimum number of lines will always be added.\n'''\n    )\n\nopt('touch_scroll_multiplier', '1.0',\n    option_type='float', ctype='double',\n    long_text='''\nMultiplier for the number of lines scrolled by a touchpad. Note that this is\nonly used for high precision scrolling devices on platforms such as macOS and\nWayland. Use negative numbers to change scroll direction.\n'''\n    )\n\nopt('pixel_scroll', 'yes', option_type='to_bool', ctype='bool', long_text='''\nEnable per-pixel scrolling, in the kitty scrollback buffer, when using high precision\ninput devices (for example touchpads). When enabled, kitty's own scrollback will move\nby sub-line increments instead of only whole lines. This does not affect applications\nrunning inside the terminal (for example full-screen TUIs) that handle scrolling\nthemselves.\n''')\n\nopt('momentum_scroll', '0.96', option_type='unit_float', ctype='float', long_text='''\nThe amount of friction to apply to slow down momentum (inertial) scrolling. A number\nfrom 0 to 1, with 0 meaning no momentum scrolling and 1 meaning infinite scrolling.\nNote that this setting only applies on platforms such as Wayland, that do not provide\nnative momentum scrolling. On macOS, the native OS based momentum scrolling is used.\nAlso, momentum scrolling only applies to \"finger\" based devices such as touchpads\nand touchscreens. Changes to this setting only take effect after a kitty restart.\n''')\negr()  # }}}\n\n\n# mouse {{{\nagr('mouse', 'Mouse')\n\nopt('mouse_hide_wait', '3.0',\n    macos_default='0.0',\n    option_type='mouse_hide_wait', ctype='!mouse_hide_wait',\n    long_text='''\nHide mouse cursor after the specified number of seconds of the mouse not being\nused. Set to zero to disable mouse cursor hiding. Set to a negative value to\nhide the mouse cursor immediately when typing text. Disabled by default on macOS\nas getting it to work robustly with the ever-changing sea of bugs that is Cocoa\nis too much effort.\n\nBy default, once the cursor is hidden, it is immediately unhidden on any\nfurther mouse events.\n\nTwo formats are supported:\n - :code:`<hide-wait>`\n - :code:`<hide-wait> <unhide-wait> <unhide-threshold> <scroll-unhide>`\n\nTo change the unhide behavior, the optional parameters :code:`<unhide-wait>`,\n:code:`<unhide-threshold>`, and :code:`<scroll-unhide>` may be set.\n\n:code:`<unhide-wait>`\n    Waits for the specified number of seconds after mouse events before unhiding the\n    mouse cursor. Set to zero to unhide mouse cursor immediately on mouse activity.\n    This is useful to prevent the mouse cursor from unhiding on accidental swipes on\n    the trackpad.\n\n:code:`<unhide-threshold>`\n    Sets the threshold of mouse activity required to unhide the mouse cursor, when\n    the <unhide-wait> option is non-zero. When <unhide-wait> is zero, this has no\n    effect.\n\n    For example, if :code:`<unhide-threshold>` is 40 and :code:`<unhide-wait>` is 2.5, when kitty\n    detects a mouse event, it records the number of mouse events in the next 2.5\n    seconds, and checks if that exceeds 40 * 2.5 = 100. If it does, then the mouse\n    cursor is unhidden, otherwise nothing happens.\n\n:code:`<scroll-unhide>`\n    Controls what mouse events may unhide the mouse cursor. If enabled, both scroll\n    and movement events may unhide the cursor. If disabled, only mouse movements can\n    unhide the cursor.\n\nExamples of valid values:\n - :code:`0.0`\n - :code:`1.0`\n - :code:`-1.0`\n - :code:`0.1 3.0 40 yes`\n'''\n    )\n\nopt('url_color', '#0087bd',\n    option_type='to_color', ctype='color_as_int',\n    long_text='''\nThe color and style for highlighting URLs on mouse-over. :opt:`url_style` can\nbe one of: :code:`none`, :code:`straight`, :code:`double`, :code:`curly`,\n:code:`dotted`, :code:`dashed`.\n'''\n    )\n\nopt('url_style', 'curly',\n    option_type='url_style', ctype='uint',\n    )\n\nopt('open_url_with', 'default',\n    option_type='to_cmdline',\n    long_text='''\nThe program to open clicked URLs. The special value :code:`default` will first\nlook for any URL handlers defined via the :doc:`open_actions` facility and if\nnon are found, it will use the Operating System's default URL handler\n(:program:`open` on macOS and :program:`xdg-open` on Linux).\n'''\n    )\n\nopt('url_prefixes', 'file ftp ftps gemini git gopher http https irc ircs kitty mailto news sftp ssh',\n    option_type='url_prefixes', ctype='!url_prefixes',\n    long_text='''\nThe set of URL prefixes to look for when detecting a URL under the mouse cursor.\n'''\n    )\n\nopt('detect_urls', 'yes',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nDetect URLs under the mouse. Detected URLs are highlighted with an underline and\nthe mouse cursor becomes a hand over them. Even if this option is disabled, URLs\nare still clickable. See also the :opt:`underline_hyperlinks` option to control\nhow hyperlinks (as opposed to plain text URLs) are displayed.\n'''\n    )\n\nopt('url_excluded_characters', '',\n    option_type='python_string', ctype='!url_excluded_characters',\n    long_text='''\nAdditional characters to be disallowed from URLs, when detecting URLs under the\nmouse cursor. By default, all characters that are legal in URLs are allowed.\nAdditionally, newlines are allowed (but stripped). This is to accommodate\nprograms such as mutt that add hard line breaks even for continued lines.\n:code:`\\\\\\\\n` can be added to this option to disable this behavior. Special\ncharacters can be specified using backslash escapes, to specify a backslash use\na double backslash.\n'''\n    )\n\nopt('show_hyperlink_targets', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nWhen the mouse hovers over a terminal hyperlink, show the actual URL that will\nbe activated when the hyperlink is clicked.\n''')\n\n\nopt('underline_hyperlinks', 'hover', choices=('hover', 'always', 'never'),\n    ctype='underline_hyperlinks', long_text='''\nControl how hyperlinks are underlined. They can either be underlined on mouse\n:code:`hover`, :code:`always` (i.e. permanently underlined) or :code:`never` which means\nthat kitty will not apply any underline styling to hyperlinks. Note that the value of :code:`always`\nonly applies to real (OSC 8) hyperlinks not text that is detected to be a URL on mouse hover.\nUses the :opt:`url_style` and :opt:`url_color` settings for the underline style. Note\nthat reloading the config and changing this value to/from :code:`always` will only\naffect text subsequently received by kitty.\n''')\n\n\nopt('copy_on_select', 'no',\n    option_type='copy_on_select',\n    long_text='''\nCopy to clipboard or a private buffer on select. With this set to\n:code:`clipboard`, selecting text with the mouse will cause the text to be\ncopied to clipboard. Useful on platforms such as macOS that do not have the\nconcept of primary selection. You can instead specify a name such as :code:`a1`\nto copy to a private kitty buffer. Map a shortcut with the\n:code:`paste_from_buffer` action to paste from this private buffer.\nFor example::\n\n    copy_on_select a1\n    map shift+cmd+v paste_from_buffer a1\n\nNote that copying to the clipboard is a security risk, as all programs,\nincluding websites open in your browser can read the contents of the system\nclipboard.\n'''\n    )\n\nopt('clear_selection_on_clipboard_loss', 'no', option_type='to_bool', long_text='''\nWhen the contents of the clipboard no longer reflect the current selection, clear it.\nThis is primarily useful on platforms such as Linux where selecting text automatically\ncopies it to a special \"primary selection\" clipboard or if you have :opt:`copy_on_select`\nset to :code:`clipboard`.\n\nNote that on macOS the system does not provide notifications when the clipboard owner\nis changed, so there, copying to clipboard in a non-kitty application will not clear\nselections even if :opt:`copy_on_select` is enabled.\n''')\n\nopt('paste_actions', 'quote-urls-at-prompt,confirm',\n    option_type='paste_actions',\n    long_text='''\nA comma separated list of actions to take when pasting or dropping text into the terminal.\nThe supported paste actions are:\n\n:code:`quote-urls-at-prompt`:\n    If the text being pasted is a URL and the cursor is at a shell prompt,\n    automatically quote the URL (needs :opt:`shell_integration`).\n:code:`replace-dangerous-control-codes`\n    Replace dangerous control codes from pasted text, without confirmation.\n:code:`replace-newline`\n    Replace the newline character from pasted text, without confirmation.\n:code:`confirm`:\n    Confirm the paste if the text to be pasted contains any terminal control codes\n    as this can be dangerous, leading to code execution if the shell/program running\n    in the terminal does not properly handle these.\n:code:`confirm-if-large`\n    Confirm the paste if it is very large (larger than 16KB) as pasting\n    large amounts of text into shells can be very slow.\n:code:`filter`:\n    Run the filter_paste() function from the file :file:`paste-actions.py` in\n    the kitty config directory on the pasted text. The text returned by the\n    function will be actually pasted.\n:code:`no-op`:\n    Has no effect.\n'''\n    )\n\nopt('strip_trailing_spaces', 'never',\n    choices=('always', 'never', 'smart'),\n    long_text='''\nRemove spaces at the end of lines when copying to clipboard. A value of\n:code:`smart` will do it when using normal selections, but not rectangle\nselections. A value of :code:`always` will always do it.\n'''\n    )\n\nopt('select_by_word_characters', '@-./_~?&=%+#',\n    ctype='!select_by_word_characters',\n    long_text='''\nCharacters considered part of a word when double clicking. In addition to these\ncharacters any character that is marked as an alphanumeric character in the\nUnicode database will be matched.\n'''\n    )\n\nopt('select_by_word_characters_forward', '',\n    ctype='!select_by_word_characters_forward',\n    long_text='''\nCharacters considered part of a word when extending the selection forward on\ndouble clicking. In addition to these characters any character that is marked\nas an alphanumeric character in the Unicode database will be matched.\n\nIf empty (default) :opt:`select_by_word_characters` will be used for both\ndirections.\n'''\n    )\n\nopt('click_interval', '-1.0',\n    option_type='float', ctype='time',\n    long_text='''\nThe interval between successive clicks to detect double/triple clicks (in\nseconds). Negative numbers will use the system default instead, if available, or\nfallback to 0.5.\n'''\n    )\n\nopt('focus_follows_mouse', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nSet the active window to the window under the mouse when moving the mouse around.\nOn macOS, this will also cause the OS Window under the mouse to be focused automatically when the\nmouse enters it.\n'''\n    )\n\nopt('pointer_shape_when_grabbed', 'arrow',\n    choices=pointer_shape_names, ctype='pointer_shape',\n    long_text='''\nThe shape of the mouse pointer when the program running in the terminal grabs\nthe mouse.\n'''\n    )\n\nopt('default_pointer_shape', 'beam',\n    choices=pointer_shape_names, ctype='pointer_shape',\n    long_text='''\nThe default shape of the mouse pointer.\n'''\n    )\n\nopt('pointer_shape_when_dragging', 'beam crosshair', option_type='pointer_shape_when_dragging', ctype='!dragging_pointer_shape', long_text='''\nThe default shape of the mouse pointer when dragging across text. The optional second value\nsets the shape when dragging in rectangular selection mode.\n'''\n    )\n\n\n# mouse.mousemap {{{\nagr('mouse.mousemap', 'Mouse actions', '''\nMouse buttons can be mapped to perform arbitrary actions. The syntax is:\n\n.. code-block:: none\n\n    mouse_map button-name event-type modes action\n\nWhere :code:`button-name` is one of :code:`left`, :code:`middle`, :code:`right`,\n:code:`b1` ... :code:`b8` with added keyboard modifiers. For example:\n:code:`ctrl+shift+left` refers to holding the :kbd:`Ctrl+Shift` keys while\nclicking with the left mouse button. The value :code:`b1` ... :code:`b8` can be\nused to refer to up to eight buttons on a mouse.\n\n:code:`event-type` is one of :code:`press`, :code:`release`,\n:code:`doublepress`, :code:`triplepress`, :code:`click`, :code:`doubleclick`.\n:code:`modes` indicates whether the action is performed when the mouse is\ngrabbed by the program running in the terminal, or not. The values are\n:code:`grabbed` or :code:`ungrabbed` or a comma separated combination of them.\n:code:`grabbed` refers to when the program running in the terminal has requested\nmouse events. Note that the click and double click events have a delay of\n:opt:`click_interval` to disambiguate from double and triple presses.\n\nYou can run kitty with the :option:`kitty --debug-input` command line option\nto see mouse events. See the builtin actions below to get a sense of what is\npossible.\n\nIf you want to unmap a button, map it to nothing. For example, to disable\nopening of URLs with a plain click::\n\n    mouse_map left click ungrabbed\n\nSee all the mappable actions including mouse actions :doc:`here </actions>`.\n\n.. note::\n    Once a selection is started, releasing the button that started it will\n    automatically end it and no release event will be dispatched.\n''')\n\nopt('clear_all_mouse_actions', 'no',\n    option_type='clear_all_mouse_actions',\n    long_text='''\nRemove all mouse action definitions up to this point. Useful, for instance, to\nremove the default mouse actions.\n'''\n    )\n\nmma('Click the link under the mouse or move the cursor',\n    'click_url_or_select left click ungrabbed mouse_handle_click selection link prompt',\n    long_text='''\nFirst check for a selection and if one exists do nothing. Then check for a link\nunder the mouse cursor and if one exists, click it. Finally check if the click\nhappened at the current shell prompt and if so, move the cursor to the click\nlocation. Note that this requires :ref:`shell integration <shell_integration>`\nto work.\n'''\n    )\n\nmma('Click the link under the mouse or move the cursor even when grabbed',\n    'click_url_or_select_grabbed shift+left click grabbed,ungrabbed mouse_handle_click selection link prompt',\n    long_text='''\nSame as above, except that the action is performed even when the mouse is\ngrabbed by the program running in the terminal.\n'''\n    )\n\nmma('Click the link under the mouse cursor',\n    'click_url ctrl+shift+left release grabbed,ungrabbed mouse_handle_click link',\n    long_text='''\nVariant with :kbd:`Ctrl+Shift` is present because the simple click based version\nhas an unavoidable delay of :opt:`click_interval`, to disambiguate clicks from\ndouble clicks.\n'''\n    )\n\nmma('Discard press event for link click',\n    'click_url_discard ctrl+shift+left press grabbed discard_event',\n    long_text='''\nPrevent this press event from being sent to the program that has grabbed the\nmouse, as the corresponding release event is used to open a URL.\n'''\n    )\n\n\nmma('Paste from the primary selection',\n    'paste_selection middle release ungrabbed paste_from_selection',\n    )\n\nmma('Start selecting text',\n    'start_simple_selection left press ungrabbed mouse_selection normal',\n    )\n\nmma('Start selecting text in a rectangle',\n    'start_rectangle_selection ctrl+alt+left press ungrabbed mouse_selection rectangle',\n    )\n\nmma('Select a word',\n    'select_word left doublepress ungrabbed mouse_selection word',\n    )\n\nmma('Select a line',\n    'select_line left triplepress ungrabbed mouse_selection line',\n    )\n\nmma('Select line from point',\n    'select_line_from_point ctrl+alt+left triplepress ungrabbed mouse_selection line_from_point',\n    long_text='Select from the clicked point to the end of the line.'\n    ' If you would like to select the word at the point and then extend to the rest of the line,'\n    ' change `line_from_point` to `word_and_line_from_point`.'\n    )\n\nmma('Extend the current selection',\n    'extend_selection right press ungrabbed mouse_selection extend',\n    long_text='''\nIf you want only the end of the selection to be moved instead of the nearest\nboundary, use :code:`move-end` instead of :code:`extend`.\n'''\n    )\n\nmma('Extend the current selection with shift',\n    'extend_selection_shift shift+left press ungrabbed mouse_selection extend',\n    long_text='''\nIf you want only the end of the selection to be moved instead of the nearest\nboundary, use :code:`move-end` instead of :code:`extend`.\n'''\n    )\n\nmma('Paste from the primary selection even when grabbed',\n    'paste_selection_grabbed shift+middle release ungrabbed,grabbed paste_selection',\n    )\n\nmma('Discard press event for middle click paste',\n    'paste_selection_grabbed shift+middle press grabbed discard_event',\n    )\n\nmma('Start selecting text even when grabbed',\n    'start_simple_selection_grabbed shift+left press grabbed mouse_selection normal',\n    )\n\nmma('Start selecting text in a rectangle even when grabbed',\n    'start_rectangle_selection_grabbed ctrl+shift+alt+left press ungrabbed,grabbed mouse_selection rectangle',\n    )\n\nmma('Select a word even when grabbed',\n    'select_word_grabbed shift+left doublepress ungrabbed,grabbed mouse_selection word',\n    )\n\nmma('Select a line even when grabbed',\n    'select_line_grabbed shift+left triplepress ungrabbed,grabbed mouse_selection line',\n    )\n\nmma('Select line from point even when grabbed',\n    'select_line_from_point_grabbed ctrl+shift+alt+left triplepress ungrabbed,grabbed mouse_selection line_from_point',\n    long_text='Select from the clicked point to the end of the line even when grabbed.'\n    ' If you would like to select the word at the point and then extend to the rest of the line,'\n    ' change `line_from_point` to `word_and_line_from_point`.'\n    )\n\nmma('Extend the current selection even when grabbed',\n    'extend_selection_grabbed shift+right press ungrabbed,grabbed mouse_selection extend',\n    )\n\nmma('Show clicked command output in pager',\n    'show_clicked_cmd_output_ungrabbed ctrl+shift+right press ungrabbed mouse_show_command_output',\n    long_text='Requires :ref:`shell integration <shell_integration>` to work.'\n    )\negr()  # }}}\negr()  # }}}\n\n\n# performance {{{\nagr('performance', 'Performance tuning')\n\nopt('repaint_delay', '10',\n    option_type='positive_int', ctype='time-ms',\n    long_text='''\nDelay between screen updates (in milliseconds). Decreasing it, increases\nframes-per-second (FPS) at the cost of more CPU usage. The default value yields\n~100 FPS which is more than sufficient for most uses. Note that to actually\nachieve 100 FPS, you have to either set :opt:`sync_to_monitor` to :code:`no` or\nuse a monitor with a high refresh rate. Also, to minimize latency when there is\npending input to be processed, this option is ignored.\n'''\n    )\n\nopt('input_delay', '3',\n    option_type='positive_int', ctype='time-ms',\n    long_text='''\nDelay before input from the program running in the terminal is processed (in\nmilliseconds). Note that decreasing it will increase responsiveness, but also\nincrease CPU usage and might cause flicker in full screen programs that redraw\nthe entire screen on each loop, because kitty is so fast that partial screen\nupdates will be drawn. This setting is ignored when the input buffer is almost full.\n'''\n    )\n\nopt('sync_to_monitor', 'yes',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nSync screen updates to the refresh rate of the monitor. This prevents\n:link:`screen tearing <https://en.wikipedia.org/wiki/Screen_tearing>` when\nscrolling. However, it limits the rendering speed to the refresh rate of your\nmonitor. With a very high speed mouse/high keyboard repeat rate, you may notice\nsome slight input latency. If so, set this to :code:`no`.\n'''\n    )\negr()  # }}}\n\n\n# bell {{{\nagr('bell', 'Terminal bell')\n\nopt('enable_audio_bell', 'yes',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nThe audio bell. Useful to disable it in environments that require silence.\n'''\n    )\n\nopt('visual_bell_duration', '0.0',\n    option_type='visual_bell_duration', ctype='!visual_bell_duration',\n    long_text='''\nThe visual bell duration (in seconds). Flash the screen when a bell occurs for\nthe specified number of seconds. Set to zero to disable. The flash is animated, fading\nin and out over the specified duration. The :term:`easing function` used for the fading can be controlled.\nFor example, :code:`2.0 linear` will casuse the flash to fade in and out linearly. The default\nif unspecified is to use :code:`ease-in-out` which fades slowly at the start, middle and end.\nYou can specify different easing functions for the fade-in and fade-out parts, like this:\n:code:`2.0 ease-in linear`. kitty\nsupports all the :link:`CSS easing functions <https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function>`.\n'''\n    )\n\nopt('visual_bell_color', 'none',\n    option_type='to_color_or_none',\n    long_text='''\nThe color used by visual bell. Set to :code:`none` will fall back to selection\nbackground color. If you feel that the visual bell is too bright, you can\nset it to a darker color.\n'''\n    )\n\nopt('window_alert_on_bell', 'yes',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nRequest window attention on bell. Makes the dock icon bounce on macOS or the\ntaskbar flash on Linux.\n'''\n    )\n\nopt('bell_on_tab', '\"🔔 \"',\n    option_type='bell_on_tab',\n    long_text='''\nSome text or a Unicode symbol to show on the tab if a window in the tab that\ndoes not have focus has a bell. If you want to use leading or trailing\nspaces, surround the text with quotes. See :opt:`tab_title_template` for how\nthis is rendered.\n\nFor backwards compatibility, values of :code:`yes`, :code:`y` and :code:`true`\nare converted to the default bell symbol and :code:`no`, :code:`n`,\n:code:`false` and :code:`none` are converted to the empty string.\n'''\n    )\n\nopt('command_on_bell', 'none',\n    option_type='to_cmdline',\n    long_text='''\nProgram to run when a bell occurs. The environment variable\n:envvar:`KITTY_CHILD_CMDLINE` can be used to get the program running in the\nwindow in which the bell occurred.\n'''\n    )\n\nopt('macos_dock_badge_on_bell', 'yes',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nShow a badge on kitty's dock icon when a bell occurs and kitty is not the\nactive application (macOS only). The badge is automatically cleared when kitty\nregains focus.\n'''\n    )\n\nopt('bell_path', 'none',\n    option_type='config_or_absolute_path', ctype='!bell_path',\n    long_text='''\nPath to a sound file to play as the bell sound. If set to :code:`none`, the\nsystem default bell sound is used. Must be in a format supported by the\noperating systems sound API, such as WAV or OGA on Linux (libcanberra) or AIFF,\nMP3 or WAV on macOS (NSSound). Relative paths are resolved\nwith respect to the kitty config directory.\n'''\n    )\n\nopt('linux_bell_theme', '__custom', ctype='!bell_theme',\n    long_text='''\nThe XDG Sound Theme kitty will use to play the bell sound.\nOn Wayland, when the compositor supports it, it is asked to play the system default\nbell sound, and this setting has no effect. Note that Hyprland claims to support this\nprotocol, but :link:`does not actually play a sound <https://github.com/hyprwm/Hyprland/issues/10488>`.\nThis setting defaults to the custom theme name specified in the\n:link:`XDG Sound theme specification <https://specifications.freedesktop.org/sound-theme-spec/latest/sound_lookup.html>,\nfalling back to the default freedesktop theme if it does not exist.\nTo change your sound theme desktop wide, create :file:`~/.local/share/sounds/__custom/index.theme` with the contents:\n\n    [Sound Theme]\n\n    Inherits=name-of-the-sound-theme-you-want-to-use\n\nReplace :code:`name-of-the-sound-theme-you-want-to-use` with the actual theme name. Now all compliant applications\nshould use sounds from this theme.\n''')\n\negr()  # }}}\n\n\n# window {{{\nagr('window', 'Window layout')\n\nopt('remember_window_size', 'yes',\n    option_type='to_bool',\n    long_text='''\nIf enabled, the :term:`OS Window <os_window>` size will be remembered so that\nnew instances of kitty will have the same size as the previous instance.\nIf disabled, the :term:`OS Window <os_window>` will initially have size\nconfigured by initial_window_width/height, in pixels. You can use a suffix of\n\"c\" on the width/height values to have them interpreted as number of cells\ninstead of pixels.\n'''\n    )\n\nopt('initial_window_width', '640',\n    option_type='window_size',\n    )\n\nopt('initial_window_height', '400',\n    option_type='window_size',\n    )\n\nopt('remember_window_position', 'no',\n    option_type='to_bool',\n    long_text='''\nIf enabled, the :term:`OS Window <os_window>` position will be remembered so that\nnew instances of kitty will have the same position as the previous instance.\nIf disabled, the :term:`OS Window <os_window>` will be placed by the window manager.\nNote that remembering of position only works if the underlying desktop environment/window\nmanager supports it. It never works on Wayland. See also :option:`kitty --position` to\nspecify the position when launching kitty.\n'''\n    )\n\nopt('enabled_layouts', '*',\n    option_type='to_layout_names',\n    long_text='''\nThe enabled window layouts. A comma separated list of layout names. The special\nvalue :code:`all` means all layouts. The first listed layout will be used as the\nstartup layout. Default configuration is all layouts in alphabetical order. For\na list of available layouts, see the :ref:`layouts`.\n'''\n    )\n\nopt('window_resize_step_cells', '2',\n    option_type='positive_int',\n    long_text='''\nThe step size (in units of cell width/cell height) to use when resizing kitty\nwindows in a layout with the shortcut :sc:`start_resizing_window`. The cells\nvalue is used for horizontal resizing, and the lines value is used for vertical\nresizing.\n'''\n    )\n\nopt('window_resize_step_lines', '2',\n    option_type='positive_int',\n    )\n\nopt('window_border_width', '0.5pt',\n    option_type='window_border_width',\n    long_text='''\nThe width of window borders. Can be either in pixels (px) or pts (pt). Values in\npts will be rounded to the nearest number of pixels based on screen resolution.\nIf not specified, the unit is assumed to be pts. Note that borders are displayed\nonly when more than one window is visible. They are meant to separate multiple\nwindows.\n'''\n    )\n\nopt('draw_minimal_borders', 'yes',\n    option_type='to_bool',\n    long_text='''\nDraw only the minimum borders needed. This means that only the borders that\nseparate the window from a neighbor are drawn. Note that setting a\nnon-zero :opt:`window_margin_width` overrides this and causes all borders to be\ndrawn.\n'''\n    )\n\nopt('draw_window_borders_for_single_window', 'no',\n    option_type='to_bool',\n    long_text='''\nDraw borders around a window even when there is only a single window visible. When\nenabled and there is only a single window, full borders are drawn around it (as if\n:opt:`draw_minimal_borders` is false). The border will show in the active color when\nthe window is focused and the OS window has focus, and in the inactive color when the\nOS window loses focus. This provides a clear visual indicator of whether the kitty\nwindow is focused. When there are multiple windows visible, this option has no effect\nand normal border drawing rules apply.\n'''\n    )\n\nopt('window_margin_width', '0',\n    option_type='edge_width',\n    long_text='''\nThe window margin (in pts) (blank area outside the border). A single value sets\nall four sides. Two values set the vertical and horizontal sides. Three values\nset top, horizontal and bottom. Four values set top, right, bottom and left.\n'''\n    )\n\nopt('single_window_margin_width', '-1',\n    option_type='optional_edge_width',\n    long_text='''\nThe window margin to use when only a single window is visible (in pts). Negative\nvalues will cause the value of :opt:`window_margin_width` to be used instead. A\nsingle value sets all four sides. Two values set the vertical and horizontal\nsides. Three values set top, horizontal and bottom. Four values set top, right,\nbottom and left.\n'''\n    )\n\nopt('window_padding_width', '0',\n    option_type='edge_width',\n    long_text='''\nThe window padding (in pts) (blank area between the text and the window border).\nA single value sets all four sides. Two values set the vertical and horizontal\nsides. Three values set top, horizontal and bottom. Four values set top, right,\nbottom and left.\n'''\n    )\n\nopt('single_window_padding_width', '-1',\n    option_type='optional_edge_width',\n    long_text='''\nThe window padding to use when only a single window is visible (in pts). Negative\nvalues will cause the value of :opt:`window_padding_width` to be used instead. A\nsingle value sets all four sides. Two values set the vertical and horizontal\nsides. Three values set top, horizontal and bottom. Four values set top, right,\nbottom and left.\n'''\n    )\n\nopt('placement_strategy', 'center',\n    choices=('top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'),\n    long_text='''\nWhen the window size is not an exact multiple of the cell size, the cell area of\nthe terminal window will have some extra padding on the sides. You can control\nhow that padding is distributed with this option. Using a value of\n:code:`center` means the cell area will be placed centrally. A value of\n:code:`top-left` means the padding will be only at the bottom and right edges.\nThe value can be one of: :code:`top-left`, :code:`top`, :code:`top-right`,\n:code:`left`, :code:`center`, :code:`right`, :code:`bottom-left`,\n:code:`bottom`, :code:`bottom-right`.\n'''\n    )\n\nopt('active_border_color', '#00ff00',\n    option_type='to_color_or_none', ctype='active_border_color',\n    long_text='''\nThe color for the border of the active window. Set this to :code:`none` to not\ndraw borders around the active window.\n'''\n    )\n\nopt('inactive_border_color', '#cccccc',\n    option_type='to_color', ctype='color_as_int',\n    long_text='The color for the border of inactive windows.'\n    )\n\nopt('bell_border_color', '#ff5a00',\n    option_type='to_color', ctype='color_as_int',\n    long_text='The color for the border of inactive windows in which a bell has occurred.'\n    )\n\nopt('inactive_text_alpha', '1.0',\n    option_type='unit_float', ctype='float',\n    long_text='''\nFade the text in inactive windows by the specified amount (a number between zero\nand one, with zero being fully faded).\n'''\n    )\n\nopt('hide_window_decorations', 'no',\n    option_type='hide_window_decorations', ctype='uint',\n    long_text='''\nHide the window decorations (title-bar and window borders) with :code:`yes`. On\nmacOS, :code:`titlebar-only` and :code:`titlebar-and-corners` can be used to\nonly hide the titlebar and the rounded corners.\n\nOn Wayland, :code:`titlebar-only` can be used to hide the titlebar while keeping\nthe window shadow for resizing. On compositors that have server-side\ndecorations (such as anything but GNOME), both :code:`yes` and :code:`titlebar-only` force\nclient-side decoration mode.\n\nWhether this works and exactly what effect it has depends on the window manager/operating\nsystem. Note that the effects of changing this option when reloading config\nare undefined. When using :code:`titlebar-only` on macOS, it is useful to also set\n:opt:`window_margin_width` and :opt:`placement_strategy` to prevent the rounded\ncorners from clipping text. Or use :code:`titlebar-and-corners`.\n'''\n    )\n\nopt('window_logo_path', 'none',\n    option_type='config_or_absolute_path', ctype='!window_logo_path',\n    long_text='''\nPath to a logo image. Must be in PNG/JPEG/WEBP/GIF/TIFF/BMP format. Relative paths are interpreted\nrelative to the kitty config directory. The logo is displayed in a corner of\nevery kitty window. The position is controlled by :opt:`window_logo_position`.\nIndividual windows can be configured to have different logos either using the\n:ac:`launch` action or the :doc:`remote control <remote-control>` facility.\n'''\n    )\n\nopt('window_logo_position', 'bottom-right',\n    choices=('top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'),\n    ctype='bganchor',\n    long_text='''\nWhere to position the window logo in the window. The value can be one of:\n:code:`top-left`, :code:`top`, :code:`top-right`, :code:`left`, :code:`center`,\n:code:`right`, :code:`bottom-left`, :code:`bottom`, :code:`bottom-right`.\n'''\n    )\n\nopt('window_logo_alpha', '0.5',\n    option_type='unit_float', ctype='float',\n    long_text='''\nThe amount the logo should be faded into the background. With zero being fully\nfaded and one being fully opaque.\n'''\n    )\n\nopt('window_logo_scale', '0', option_type='window_logo_scale', ctype='!window_logo_scale', long_text='''\nThe percentage (0-100] of the window size to which the logo should scale. Using a single\nnumber means the logo is scaled to that percentage of the shortest window dimension, while preserving\naspect ratio of the logo image.\n\nUsing two numbers means the width and height of the logo are scaled to the respective\npercentage of the window's width and height.\n\nUsing zero as the percentage disables scaling in that dimension. A single zero (the default)\ndisables all scaling of the window logo.\n''')\n\nopt('resize_debounce_time', '0.1 0.5',\n    option_type='resize_debounce_time', ctype='!resize_debounce_time',\n    long_text='''\nThe time to wait (in seconds) before asking the program running in kitty to resize and\nredraw the screen during a live resize of the OS window, when no new resize\nevents have been received, i.e. when resizing is either paused or finished.\nOn platforms such as macOS, where the operating system sends events corresponding\nto the start and end of a live resize, the second number is used for\nredraw-after-pause since kitty can distinguish between a pause and end of\nresizing. On such systems the first number is ignored and redraw is immediate\nafter end of resize. On other systems only the first number is used so that kitty\nis \"ready\" quickly after the end of resizing, while not also continuously\nredrawing, to save energy.\n'''\n    )\n\nopt('resize_in_steps', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nResize the OS window in steps as large as the cells, instead of with the usual\npixel accuracy. Combined with :opt:`initial_window_width` and\n:opt:`initial_window_height` in number of cells, this option can be used to keep\nthe margins as small as possible when resizing the OS window. Note that this\ndoes not currently work on Wayland.\n'''\n    )\n\nopt('visual_window_select_characters', defval=string.digits[1:] + '0' + string.ascii_uppercase,\n    option_type='visual_window_select_characters',\n    long_text=r'''\nThe list of characters for visual window selection. For example, for selecting a\nwindow to focus on with :sc:`focus_visible_window`. The value should be a series\nof unique numbers or alphabets, case insensitive, from the set :code:`0-9A-Z\\`-=[];',./\\\\`.\nSpecify your preference as a string of characters.\n'''\n    )\n\nopt('confirm_os_window_close', '-1', option_type='confirm_close', long_text='''\nAsk for confirmation when closing an OS window or a tab with at least this\nnumber of kitty windows in it by window manager (e.g. clicking the window close\nbutton or pressing the operating system shortcut to close windows) or by the\n:ac:`close_tab` action. A value of zero disables confirmation. This confirmation\nalso applies to requests to quit the entire application (all OS windows, via the\n:ac:`quit` action). Negative values are converted to positive ones, however,\nwith :opt:`shell_integration` enabled, using negative values means windows\nsitting at a shell prompt are not counted, only windows where some command is\ncurrently running. You can also have backgrounded jobs prevent closing,\nby adding :code:`count-background` to the setting, for example: :code:`-1 count-background`.\nNote that if you want confirmation when closing individual windows,\nyou can map the :ac:`close_window_with_confirmation` action.\n''')\n\nopt('window_drag_tolerance', '2', option_type='float', ctype='double', long_text='''\nControl dragging window borders to resize kitty windows. This is the tolerance in pts\nfor the region around window borders where pressing the left mouse button\nwill start the dragging of window borders. Use a large negative value such as -200 to disable\ndragging of borders. Note that because kitty uses layouts, dragging borders does not\nactually resize the window itself, but instead, the layout row/column/slot, which can result\nin multiple windows getting resized.\n''')\n\nopt('window_title_bar', 'top',\n    choices=('top', 'bottom'),\n    long_text='''\nControl the position of the window title bar relative to the window content.\nUse :opt:`window_title_bar_min_windows` to control when title bars are shown.\nUse :opt:`window_title_template` to format the displayed window title.\n'''\n    )\n\nopt('window_title_bar_min_windows', '0',\n    option_type='positive_int',\n    long_text='''\nThe minimum number of visible windows in a tab before window title bars\nare shown. A value of :code:`0` means never show. :code:`1` means always\nshow. :code:`2` or more means show only when at least that many windows\nare visible. Similar to :opt:`tab_bar_min_tabs` for the tab bar.\n'''\n)\n\nopt('window_title_template', '\"{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.window}{progress_percent}{title}\"',\n    option_type='tab_title_template',\n    long_text='''\nA template to render the window title bar text. Uses the same template syntax as\n:opt:`tab_title_template`. Available variables include: :code:`{title}`,\n:code:`{bell_symbol}`, :code:`{activity_symbol}`, :code:`{progress_percent}`,\n:code:`{custom}`, :code:`{fmt}`, :code:`{is_active}`.\nYou can also provide a custom :code:`draw_window_title(data)` function in\n:file:`window_title_bar.py` in the kitty config directory, exposed as :code:`{custom}`.\n'''\n    )\n\nopt('active_window_title_template', 'none',\n    option_type='tab_title_template',\n    long_text='''\nTemplate to use for the active window title bar. If not set (the value\n:code:`none`), the :opt:`window_title_template` is used.\n'''\n    )\n\nopt('window_title_bar_active_foreground', 'none',\n    option_type='to_color_or_none', ctype='color_or_none_as_int',\n    long_text='''\nForeground color for the active window title bar. Defaults to\nthe corresponding tab bar color (:code:`active_tab_foreground`) when set to :code:`none`.\n'''\n    )\n\nopt('window_title_bar_active_background', 'none',\n    option_type='to_color_or_none', ctype='color_or_none_as_int',\n    long_text='''\nBackground color for the active window title bar. Defaults to\nthe corresponding tab bar color (:code:`active_tab_background`) when set to :code:`none`.\n'''\n    )\n\nopt('window_title_bar_inactive_foreground', 'none',\n    option_type='to_color_or_none', ctype='color_or_none_as_int',\n    long_text='''\nForeground color for inactive window title bars. Defaults to\nthe corresponding tab bar color (:code:`inactive_tab_foreground`) when set to :code:`none`.\n'''\n    )\n\nopt('window_title_bar_inactive_background', 'none',\n    option_type='to_color_or_none', ctype='color_or_none_as_int',\n    long_text='''\nBackground color for inactive window title bars. Defaults to\nthe corresponding tab bar color (:code:`inactive_tab_background`) when set to :code:`none`.\n'''\n    )\n\nopt('window_title_bar_align', 'center',\n    choices=('left', 'center', 'right'),\n    long_text='Horizontal alignment of the text in window title bars.'\n    )\negr()  # }}}\n\n\n# tabbar {{{\nagr('tabbar', 'Tab bar')\n\nopt('tab_bar_edge', 'bottom',\n    option_type='tab_bar_edge', ctype='int',\n    long_text='The edge to show the tab bar on, :code:`top` or :code:`bottom`.'\n    )\n\nopt('tab_bar_margin_width', '0.0',\n    option_type='positive_float',\n    long_text='The margin to the left and right of the tab bar (in pts).'\n    )\n\nopt('tab_bar_margin_height', '0.0 0.0',\n    option_type='tab_bar_margin_height', ctype='!tab_bar_margin_height',\n    long_text='''\nThe margin above and below the tab bar (in pts). The first number is the margin\nbetween the edge of the OS Window and the tab bar. The second number is the\nmargin between the tab bar and the contents of the current tab.\n'''\n    )\n\nopt('tab_bar_style', 'fade',\n    choices=('fade', 'hidden', 'powerline', 'separator', 'slant', 'custom'),\n    ctype='!tab_bar_style',\n    long_text='''\nThe tab bar style, can be one of:\n\n:code:`fade`\n    Each tab's edges fade into the background color. (See also :opt:`tab_fade`)\n:code:`slant`\n    Tabs look like the tabs in a physical file.\n:code:`separator`\n    Tabs are separated by a configurable separator. (See also\n    :opt:`tab_separator`)\n:code:`powerline`\n    Tabs are shown as a continuous line with \"fancy\" separators.\n    (See also :opt:`tab_powerline_style`)\n:code:`custom`\n    A user-supplied Python function called draw_tab is loaded from the file\n    :file:`tab_bar.py` in the kitty config directory. For examples of how to\n    write such a function, see the functions named :code:`draw_tab_with_*` in\n    kitty's source code: :file:`kitty/tab_bar.py`. See also\n    :disc:`this discussion <4447>`\n    for examples from kitty users.\n:code:`hidden`\n    The tab bar is hidden. If you use this, you might want to create\n    a mapping for the :ac:`select_tab` action which presents you with a list of\n    tabs and allows for easy switching to a tab.\n'''\n    )\n\nopt('tab_bar_filter', '', long_text='''\nA :ref:`search expression <search_syntax>`. Only tabs that match this expression\nwill be shown in the tab bar. The currently active tab is :italic:`always` shown,\nregardless of whether it matches or not. When using this option, the tab bar may\nbe displayed with less tabs than specified in :opt:`tab_bar_min_tabs`, as evaluating\nthe filter is expensive and is done only at display time. This is most useful when\nusing :ref:`sessions <sessions>`. An expression of :code:`session:~ or session:^$`\nwill show only tabs that belong to the current session or no session. The various\ntab navigation actions such as :ac:`goto_tab`, :ac:`next_tab`, :ac:`previous_tab`, etc.\nare automatically restricted to work only on matching tabs.\n''')\n\nopt('tab_bar_align', 'left',\n    choices=('left', 'center', 'right'),\n    long_text='''\nThe horizontal alignment of the tab bar, can be one of: :code:`left`,\n:code:`center`, :code:`right`.\n'''\n    )\n\nopt('tab_bar_min_tabs', '2', option_type='tab_bar_min_tabs',\n    long_text='The minimum number of tabs that must exist before the tab bar is shown.'\n)\n\nopt('tab_switch_strategy', 'previous',\n    choices=('last', 'left', 'previous', 'right'),\n    long_text='''\nThe algorithm to use when switching to a tab when the current tab is closed. The\ndefault of :code:`previous` will switch to the last used tab. A value of\n:code:`left` will switch to the tab to the left of the closed tab. A value of\n:code:`right` will switch to the tab to the right of the closed tab. A value of\n:code:`last` will switch to the right-most tab.\n'''\n    )\n\nopt('tab_fade', '0.25 0.5 0.75 1',\n    option_type='tab_fade',\n    long_text='''\nControl how each tab fades into the background when using :code:`fade` for the\n:opt:`tab_bar_style`. Each number is an alpha (between zero and one) that\ncontrols how much the corresponding cell fades into the background, with zero\nbeing no fade and one being full fade. You can change the number of cells used\nby adding/removing entries to this list.\n'''\n    )\n\nopt('tab_separator', '\" ┇\"',\n    option_type='tab_separator',\n    long_text='''\nThe separator between tabs in the tab bar when using :code:`separator` as the\n:opt:`tab_bar_style`.\n'''\n    )\n\nopt('tab_powerline_style', 'angled',\n    choices=('angled', 'round', 'slanted'),\n    long_text='''\nThe powerline separator style between tabs in the tab bar when using\n:code:`powerline` as the :opt:`tab_bar_style`, can be one of: :code:`angled`,\n:code:`slanted`, :code:`round`.\n'''\n    )\n\nopt('tab_activity_symbol', 'none',\n    option_type='tab_activity_symbol',\n    long_text='''\nSome text or a Unicode symbol to show on the tab if a window in the tab that\ndoes not have focus has some activity. If you want to use leading or trailing\nspaces, surround the text with quotes. See :opt:`tab_title_template` for how\nthis is rendered.\n'''\n    )\n\nopt('tab_title_max_length', '0',\n    option_type='positive_int',\n    long_text='''\nThe maximum number of cells that can be used to render the text in a tab.\nA value of zero means that no limit is applied.\n'''\n    )\n\nopt('tab_title_template', '\"{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{tab.last_focused_progress_percent}{title}\"',\n    option_type='tab_title_template',\n    long_text='''\nA template to render the tab title. The default just renders the title with\noptional symbols for bell and activity. If you wish to include the tab-index as\nwell, use something like: :code:`{index}:{title}`. Useful if you have shortcuts\nmapped for :code:`goto_tab N`. If you prefer to see the index as a superscript,\nuse :code:`{sup.index}`. All data available is:\n\n:code:`title`\n    The current tab title.\n:code:`index`\n    The tab index usable with :ac:`goto_tab N <goto_tab>` shortcuts.\n:code:`layout_name`\n    The current layout name.\n:code:`session_name`\n    The name of the kitty session file from which this tab was created, if any.\n:code:`active_session_name`\n    The name of the kitty session file from which the active window in this tab was created, if any.\n:code:`num_windows`\n    The number of windows in the tab.\n:code:`num_window_groups`\n    The number of window groups (a window group is a window and all of its overlay windows) in the tab.\n:code:`tab.active_wd`\n    The working directory of the currently active window in the tab\n    (expensive, requires syscall). Use :code:`tab.active_oldest_wd` to get\n    the directory of the oldest foreground process rather than the newest.\n:code:`tab.active_exe`\n    The name of the executable running in the foreground of the currently\n    active window in the tab (expensive, requires syscall). Use\n    :code:`tab.active_oldest_exe` for the oldest foreground process.\n:code:`max_title_length`\n    The maximum title length available.\n:code:`keyboard_mode`\n    The name of the current :ref:`keyboard mode <modal_mappings>` or the empty string if no keyboard mode is active.\n:code:`tab.last_focused_progress_percent`\n    If a command running in a window reports the progress for a task, show this progress as a percentage\n    from the most recently focused window in the tab. Empty string if no progress is reported.\n:code:`tab.progress_percent`\n    If a command running in a window reports the progress for a task, show this progress as a percentage\n    from all windows in the tab, averaged. Empty string is no progress is reported.\n:code:`custom`\n    This will call a function named :code:`draw_title(data)` from the file :file:`tab_bar.py` placed in\n    the kitty config directory. The function will be passed a dictionary of data, the same data that\n    can be used in this template. It can then perform arbitrarily complex processing and return a string.\n    For example: :code:`tab_title_template \"{custom}\"` will use the output of the function as the tab title.\n    Any print statements in the :code:`draw_title()` will print to the STDOUT of the kitty process, useful\n    for debugging.\n\n\nNote that formatting is done by Python's string formatting machinery, so you can\nuse, for instance, :code:`{layout_name[:2].upper()}` to show only the first two\nletters of the layout name, upper-cased. If you want to style the text, you can\nuse styling directives, for example:\n``{fmt.fg.red}red{fmt.fg.tab}normal{fmt.bg._00FF00}greenbg{fmt.bg.tab}``.\nSimilarly, for bold and italic:\n``{fmt.bold}bold{fmt.nobold}normal{fmt.italic}italic{fmt.noitalic}``.\nThe 256 eight terminal colors can be used as ``fmt.fg.color0`` through ``fmt.fg.color255``.\nNote that for backward compatibility, if :code:`{bell_symbol}` or\n:code:`{activity_symbol}` are not present in the template, they are prepended to\nit.\n'''\n    )\n\nopt('active_tab_title_template', 'none',\n    option_type='active_tab_title_template',\n    long_text='''\nTemplate to use for active tabs. If not specified falls back to\n:opt:`tab_title_template`.\n'''\n    )\n\nopt('active_tab_foreground', '#000',\n    option_type='to_color',\n    long_text='Tab bar colors and styles.'\n    )\n\nopt('active_tab_background', '#eee',\n    option_type='to_color',\n    )\n\nopt('active_tab_font_style', 'bold-italic',\n    option_type='tab_font_style',\n    )\n\nopt('inactive_tab_foreground', '#444',\n    option_type='to_color',\n    )\n\nopt('inactive_tab_background', '#999',\n    option_type='to_color',\n    )\n\nopt('inactive_tab_font_style', 'normal',\n    option_type='tab_font_style',\n    )\n\nopt('tab_bar_background', 'none',\n    option_type='to_color_or_none', ctype='color_or_none_as_int',\n    long_text='''\nBackground color for the tab bar. Defaults to using the terminal background\ncolor.\n''')\n\nopt('tab_bar_margin_color', 'none',\n    option_type='to_color_or_none', ctype='color_or_none_as_int',\n    long_text='''\nColor for the tab bar margin area. Defaults to using the terminal background\ncolor for margins above and below the tab bar. For side margins the default\ncolor is chosen to match the background color of the neighboring tab, unless\nthe window is translucent, in which case the default background is used as it\nlooks better.\n''')\n\nopt(\n    'tab_bar_drag_threshold',\n    '5',\n    option_type='positive_int',\n    long_text=\"\"\"\nControl when dragging of tabs to re-order them happens.\nThe value is the drag threshold in pixels, the distance the mouse must move\nbefore a drag begins. A value of zero disables tab dragging entirely.\nDragging a tab to another kitty window moves it there, while dragging\noutside any kitty window detaches it into a new OS window.\n\nNote that on Wayland, :link:`because of poor design\n<https://gitlab.freedesktop.org/wayland/wayland/-/issues/140>` cancelling\na drag will detach the tab. This is worked around for compositors that support\n:link:`xdg-toplevel-drag <https://wayland.app/protocols/xdg-toplevel-drag-v1>`.\n\"\"\",\n)\negr()  # }}}\n\n\n# colors {{{\nagr('colors', 'Color scheme')\n\nopt('foreground', '#dddddd',\n    option_type='to_color', ctype='color_as_int',\n    long_text='The foreground and background colors.'\n    )\n\nopt('background', '#000000',\n    option_type='to_color', ctype='color_as_int',\n    )\n\nopt(\n    'background_opacity',\n    '1.0',\n    option_type='unit_float',\n    ctype='float',\n    long_text=\"\"\"\nThe opacity of the terminal background color. A number between zero and one, where one is\nopaque and zero is fully transparent. This will only work if supported by the\nOS (for instance, when using a compositor under X11). Note that it only sets\nthe background color's opacity in cells that have the same background color as\nthe default terminal background, so that things like the status bar in vim,\npowerline prompts, etc. still look good. But it means that if you use a color\ntheme with a background color in your editor, it will not be rendered as\ntransparent. Instead you should change the default background color in your\nkitty config and not use a background color in the editor color scheme. Or use\nthe escape codes to set the terminals default colors in a shell script to\nlaunch your editor. See also :opt:`transparent_background_colors`.\nBe aware that using a value less than 1.0 is a (possibly\nsignificant) performance hit. When using a low value for this setting, it is\ndesirable that you set the :opt:`background` color to a color the matches the\ngeneral color of the desktop background, for best text rendering. Note also,\nthat this setting does not apply to the :opt:`background_image`, if any. The\nbackground image can itself have transparency via its alpha channel if desired,\nand that will be respected.\n\nIf you want to dynamically change transparency of windows, set\n:opt:`dynamic_background_opacity` to :code:`yes` (this is off by default as it\nhas a performance cost). Changing this option when reloading the config will\nonly work if :opt:`dynamic_background_opacity` was enabled in the original\nconfig.\n\"\"\",\n)\n\nopt('background_blur', '0', option_type='int', ctype='int',\n    long_text='''\nSet to a positive value to enable background blur (blurring of the visuals\nbehind a transparent window) on platforms that support it. Only takes effect\nwhen :opt:`background_opacity` is less than one. On macOS, this will also\ncontrol the :italic:`blur radius` (amount of blurring). Setting it to too high\na value will cause severe performance issues and/or rendering artifacts.\nUsually, values up to 64 work well. Note that this might cause performance issues,\ndepending on how the platform implements it, so use with care. Currently supported\non macOS and Wayland, when the compositor supports the background blur extension.\n''')\n\nopt('transparent_background_colors', '', option_type='transparent_background_colors', long_text='''\nA space separated list of upto 7 colors, with opacity. When the background color of a cell matches one of these colors,\nit is rendered semi-transparent using the specified opacity.\n\nUseful in more complex UIs like editors where you could want more than a single background color\nto be rendered as transparent, for instance, for a cursor highlight line background or a highlighted block.\nTerminal applications can set this color using :ref:`The kitty color control <color_control>`\nescape code.\n\nThe syntax for specifying colors is: :code:`color@opacity`, where the :code:`@opacity`\npart is optional. When unspecified, the value of :opt:`background_opacity` is used. For example::\n\n    transparent_background_colors red@0.5 #00ff00@0.3\n\nNote that you must also set :opt:`background_opacity` to something less than 1 for this setting to work properly.\n'''\n)\n\nopt('dynamic_background_opacity', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nAllow changing of the :opt:`background_opacity` dynamically, using either\nkeyboard shortcuts (:sc:`increase_background_opacity` and\n:sc:`decrease_background_opacity`) or the remote control facility. Changing\nthis option by reloading the config is not supported.\n'''\n    )\n\n\nopt('background_image', 'none',\n    option_type='config_or_absolute_path', ctype='!background_image',\n    long_text='Path to a background image. Must be in PNG/JPEG/WEBP/TIFF/GIF/BMP format.'\n    ' Note that when using :ref:`auto_color_scheme` this option is overridden by the color scheme file and must be set inside it to take effect.'\n    )\n\nopt('background_image_layout', 'tiled',\n    choices=('mirror-tiled', 'scaled', 'tiled', 'clamped', 'centered', 'cscaled'),\n    ctype='bglayout',\n    long_text='''\nWhether to tile, scale or clamp the background image. The value can be one of\n:code:`tiled`, :code:`mirror-tiled`, :code:`scaled`, :code:`clamped`, :code:`centered`\nor :code:`cscaled`. The :code:`scaled` and :code:`cscaled` values scale the image to the\nwindow size, with :code:`cscaled` preserving the image aspect ratio.\nNote that when using :ref:`auto_color_scheme` this option is overridden by the color scheme file and must be set inside it to take effect.\n'''\n    )\n\nopt('background_image_linear', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='When background image is scaled, whether linear interpolation should be used.'\n' Note that when using :ref:`auto_color_scheme` this option is overridden by the color scheme file and must be set inside it to take effect.'\n    )\n\nopt('background_tint', '0.0',\n    option_type='unit_float', ctype='float',\n    long_text='''\nHow much to tint the background image by the background color. This option\nmakes it easier to read the text. Tinting is done using the current background\ncolor for each window. This option applies only if :opt:`background_image` is set.\nNote that when using :ref:`auto_color_scheme` this option is overridden by the color scheme file and must be set inside it to take effect.\n'''\n    )\n\nopt('background_tint_gaps', '1.0',\n    option_type='unit_float', ctype='float',\n    long_text='''\nHow much to tint the background image at the window gaps by the background\ncolor, after applying :opt:`background_tint`. Since this is multiplicative\nwith :opt:`background_tint`, it can be used to lighten the tint over the window\ngaps for a *separated* look.\nNote that when using :ref:`auto_color_scheme` this option is overridden by the color scheme file and must be set inside it to take effect.\n'''\n    )\n\nopt('dim_opacity', '0.4',\n    option_type='unit_float', ctype='float',\n    long_text='''\nHow much to dim text that has the DIM/FAINT attribute set. One means no dimming\nand zero means fully dimmed (i.e. invisible).\n'''\n    )\n\nopt('selection_foreground', '#000000',\n    option_type='to_color_or_none',\n    long_text='''\nThe foreground and background colors for text selected with the mouse. Setting\nboth of these to :code:`none` will cause a \"reverse video\" effect for\nselections, where the selection will be the cell text color and the text will\nbecome the cell background color. Setting only selection_foreground to\n:code:`none` will cause the foreground color to be used unchanged. Note that\nthese colors can be overridden by the program running in the terminal.\n'''\n    )\n\nopt('selection_background', '#fffacd',\n    option_type='to_color_or_none',\n    )\n\n\n# colors.table {{{\nagr('colors.table', 'The color table', '''\nThe 256 terminal colors. There are 8 basic colors, each color has a dull and\nbright version, for the first 16 colors. You can set the remaining 240 colors as\ncolor16 to color255.\n''')\n\nopt('color0', '#000000',\n    option_type='to_color',\n    long_text='black'\n    )\n\nopt('color8', '#767676',\n    option_type='to_color',\n    )\n\nopt('color1', '#cc0403',\n    option_type='to_color',\n    long_text='red'\n    )\n\nopt('color9', '#f2201f',\n    option_type='to_color',\n    )\n\nopt('color2', '#19cb00',\n    option_type='to_color',\n    long_text='green'\n    )\n\nopt('color10', '#23fd00',\n    option_type='to_color',\n    )\n\nopt('color3', '#cecb00',\n    option_type='to_color',\n    long_text='yellow'\n    )\n\nopt('color11', '#fffd00',\n    option_type='to_color',\n    )\n\nopt('color4', '#0d73cc',\n    option_type='to_color',\n    long_text='blue'\n    )\n\nopt('color12', '#1a8fff',\n    option_type='to_color',\n    )\n\nopt('color5', '#cb1ed1',\n    option_type='to_color',\n    long_text='magenta'\n    )\n\nopt('color13', '#fd28ff',\n    option_type='to_color',\n    )\n\nopt('color6', '#0dcdcd',\n    option_type='to_color',\n    long_text='cyan'\n    )\n\nopt('color14', '#14ffff',\n    option_type='to_color',\n    )\n\nopt('color7', '#dddddd',\n    option_type='to_color',\n    long_text='white'\n    )\n\nopt('color15', '#ffffff',\n    option_type='to_color',\n    )\n\nopt('mark1_foreground', 'black',\n    option_type='to_color',\n    long_text='Color for marks of type 1'\n    )\n\nopt('mark1_background', '#98d3cb',\n    option_type='to_color',\n    long_text='Color for marks of type 1 (light steel blue)'\n    )\n\nopt('mark2_foreground', 'black',\n    option_type='to_color',\n    long_text='Color for marks of type 2'\n    )\n\nopt('mark2_background', '#f2dcd3',\n    option_type='to_color',\n    long_text='Color for marks of type 1 (beige)'\n    )\n\nopt('mark3_foreground', 'black',\n    option_type='to_color',\n    long_text='Color for marks of type 3'\n    )\n\nopt('mark3_background', '#f274bc',\n    option_type='to_color',\n    long_text='Color for marks of type 3 (violet)'\n    )\n\nopt('color16', '#000000',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color17', '#00005f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color18', '#000087',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color19', '#0000af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color20', '#0000d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color21', '#0000ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color22', '#005f00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color23', '#005f5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color24', '#005f87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color25', '#005faf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color26', '#005fd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color27', '#005fff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color28', '#008700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color29', '#00875f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color30', '#008787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color31', '#0087af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color32', '#0087d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color33', '#0087ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color34', '#00af00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color35', '#00af5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color36', '#00af87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color37', '#00afaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color38', '#00afd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color39', '#00afff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color40', '#00d700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color41', '#00d75f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color42', '#00d787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color43', '#00d7af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color44', '#00d7d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color45', '#00d7ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color46', '#00ff00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color47', '#00ff5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color48', '#00ff87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color49', '#00ffaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color50', '#00ffd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color51', '#00ffff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color52', '#5f0000',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color53', '#5f005f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color54', '#5f0087',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color55', '#5f00af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color56', '#5f00d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color57', '#5f00ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color58', '#5f5f00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color59', '#5f5f5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color60', '#5f5f87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color61', '#5f5faf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color62', '#5f5fd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color63', '#5f5fff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color64', '#5f8700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color65', '#5f875f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color66', '#5f8787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color67', '#5f87af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color68', '#5f87d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color69', '#5f87ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color70', '#5faf00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color71', '#5faf5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color72', '#5faf87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color73', '#5fafaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color74', '#5fafd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color75', '#5fafff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color76', '#5fd700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color77', '#5fd75f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color78', '#5fd787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color79', '#5fd7af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color80', '#5fd7d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color81', '#5fd7ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color82', '#5fff00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color83', '#5fff5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color84', '#5fff87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color85', '#5fffaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color86', '#5fffd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color87', '#5fffff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color88', '#870000',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color89', '#87005f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color90', '#870087',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color91', '#8700af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color92', '#8700d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color93', '#8700ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color94', '#875f00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color95', '#875f5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color96', '#875f87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color97', '#875faf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color98', '#875fd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color99', '#875fff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color100', '#878700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color101', '#87875f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color102', '#878787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color103', '#8787af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color104', '#8787d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color105', '#8787ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color106', '#87af00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color107', '#87af5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color108', '#87af87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color109', '#87afaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color110', '#87afd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color111', '#87afff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color112', '#87d700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color113', '#87d75f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color114', '#87d787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color115', '#87d7af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color116', '#87d7d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color117', '#87d7ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color118', '#87ff00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color119', '#87ff5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color120', '#87ff87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color121', '#87ffaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color122', '#87ffd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color123', '#87ffff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color124', '#af0000',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color125', '#af005f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color126', '#af0087',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color127', '#af00af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color128', '#af00d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color129', '#af00ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color130', '#af5f00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color131', '#af5f5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color132', '#af5f87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color133', '#af5faf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color134', '#af5fd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color135', '#af5fff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color136', '#af8700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color137', '#af875f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color138', '#af8787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color139', '#af87af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color140', '#af87d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color141', '#af87ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color142', '#afaf00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color143', '#afaf5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color144', '#afaf87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color145', '#afafaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color146', '#afafd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color147', '#afafff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color148', '#afd700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color149', '#afd75f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color150', '#afd787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color151', '#afd7af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color152', '#afd7d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color153', '#afd7ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color154', '#afff00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color155', '#afff5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color156', '#afff87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color157', '#afffaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color158', '#afffd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color159', '#afffff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color160', '#d70000',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color161', '#d7005f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color162', '#d70087',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color163', '#d700af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color164', '#d700d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color165', '#d700ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color166', '#d75f00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color167', '#d75f5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color168', '#d75f87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color169', '#d75faf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color170', '#d75fd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color171', '#d75fff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color172', '#d78700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color173', '#d7875f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color174', '#d78787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color175', '#d787af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color176', '#d787d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color177', '#d787ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color178', '#d7af00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color179', '#d7af5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color180', '#d7af87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color181', '#d7afaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color182', '#d7afd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color183', '#d7afff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color184', '#d7d700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color185', '#d7d75f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color186', '#d7d787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color187', '#d7d7af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color188', '#d7d7d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color189', '#d7d7ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color190', '#d7ff00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color191', '#d7ff5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color192', '#d7ff87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color193', '#d7ffaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color194', '#d7ffd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color195', '#d7ffff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color196', '#ff0000',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color197', '#ff005f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color198', '#ff0087',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color199', '#ff00af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color200', '#ff00d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color201', '#ff00ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color202', '#ff5f00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color203', '#ff5f5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color204', '#ff5f87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color205', '#ff5faf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color206', '#ff5fd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color207', '#ff5fff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color208', '#ff8700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color209', '#ff875f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color210', '#ff8787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color211', '#ff87af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color212', '#ff87d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color213', '#ff87ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color214', '#ffaf00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color215', '#ffaf5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color216', '#ffaf87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color217', '#ffafaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color218', '#ffafd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color219', '#ffafff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color220', '#ffd700',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color221', '#ffd75f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color222', '#ffd787',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color223', '#ffd7af',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color224', '#ffd7d7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color225', '#ffd7ff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color226', '#ffff00',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color227', '#ffff5f',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color228', '#ffff87',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color229', '#ffffaf',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color230', '#ffffd7',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color231', '#ffffff',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color232', '#080808',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color233', '#121212',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color234', '#1c1c1c',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color235', '#262626',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color236', '#303030',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color237', '#3a3a3a',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color238', '#444444',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color239', '#4e4e4e',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color240', '#585858',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color241', '#626262',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color242', '#6c6c6c',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color243', '#767676',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color244', '#808080',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color245', '#8a8a8a',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color246', '#949494',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color247', '#9e9e9e',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color248', '#a8a8a8',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color249', '#b2b2b2',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color250', '#bcbcbc',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color251', '#c6c6c6',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color252', '#d0d0d0',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color253', '#dadada',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color254', '#e4e4e4',\n    option_type='to_color',\n    documented=False,\n    )\n\nopt('color255', '#eeeeee',\n    option_type='to_color',\n    documented=False,\n    )\negr()  # }}}\n\n# colors.wide_gamut {{{\nagr('colors.wide_gamut', 'Wide gamut color formats', '''\nkitty supports modern wide gamut color formats including OKLCH and CIE LAB for precise\ncolor specification. These formats can be used anywhere a color value is accepted\n(foreground, background, color0-color255, etc.).\n\nFor detailed documentation on wide gamut color formats, syntax, and examples,\nsee :doc:`/wide-gamut-colors`.\n''')\n\negr()  # }}}\negr()  # }}}\n\n\n# advanced {{{\nagr('advanced', 'Advanced')\n\nopt('shell', '.',\n    long_text='''\nThe shell program to execute. The default value of :code:`.` means to use\nthe value of of the :envvar:`SHELL` environment variable or if unset,\nwhatever shell is set as the default shell for the current user. Note that on\nmacOS if you change this, you might need to add :code:`--login` and\n:code:`--interactive` to ensure that the shell starts in interactive mode and\nreads its startup rc files. Environment variables are expanded in this setting.\n'''\n    )\n\nopt('editor', '.',\n    long_text='''\nThe terminal based text editor (such as :program:`vim` or :program:`nano`) to\nuse when editing the kitty config file or similar tasks.\n\nThe default value of :code:`.` means to use the environment variables\n:envvar:`VISUAL` and :envvar:`EDITOR` in that order. If these variables aren't\nset, kitty will run your :opt:`shell` (:code:`$SHELL -l -i -c env`) to see if\nyour shell startup rc files set :envvar:`VISUAL` or :envvar:`EDITOR`. If that\ndoesn't work, kitty will cycle through various known editors (:program:`vim`,\n:program:`emacs`, etc.) and take the first one that exists on your system.\n'''\n    )\n\nopt('close_on_child_death', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nClose the window when the child process (usually the shell) exits. With the default value\n:code:`no`, the terminal will remain open when the child exits as long as there\nare still other processes outputting to the terminal (for example disowned or\nbackgrounded processes). When enabled with :code:`yes`, the window will close as\nsoon as the child process exits. Note that setting it to :code:`yes` means that\nany background processes still using the terminal can fail silently because\ntheir stdout/stderr/stdin no longer work.\n'''\n    )\n\nopt('+remote_control_password', '',\n    option_type='remote_control_password',\n    add_to_default=False, has_secret=True,\n    long_text='''\nAllow other programs to control kitty using passwords. This option can be\nspecified multiple times to add multiple passwords. If no passwords are present\nkitty will ask the user for permission if a program tries to use remote control\nwith a password. A password can also *optionally* be associated with a set of\nallowed remote control actions. For example::\n\n    remote_control_password \"my passphrase\" get-colors set-colors focus-window focus-tab\n\nOnly the specified actions will be allowed when using this password.\nGlob patterns can be used too, for example::\n\n    remote_control_password \"my passphrase\" set-tab-* resize-*\n\nTo get a list of available actions, run::\n\n    kitten @ --help\n\nA set of actions to be allowed when no password is sent can be specified by\nusing an empty password. For example::\n\n    remote_control_password \"\" *-colors\n\nFinally, the path to a python module can be specified that provides a function\n:code:`is_cmd_allowed` that is used to check every remote control command.\nFor example::\n\n    remote_control_password \"my passphrase\" my_rc_command_checker.py\n\nRelative paths are resolved from the kitty configuration directory.\nSee :ref:`rc_custom_auth` for details.\n''')\n\nopt('allow_remote_control', 'no',\n    choices=('password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true'),\n    long_text='''\nAllow other programs to control kitty. If you turn this on, other programs can\ncontrol all aspects of kitty, including sending text to kitty windows, opening\nnew windows, closing windows, reading the content of windows, etc. Note that\nthis even works over SSH connections. The default setting of :code:`no`\nprevents any form of remote control. The meaning of the various values are:\n\n:code:`password`\n    Remote control requests received over both the TTY device and the socket\n    are confirmed based on passwords, see :opt:`remote_control_password`.\n\n:code:`socket-only`\n    Remote control requests received over a socket are accepted\n    unconditionally. Requests received over the TTY are denied.\n    See :opt:`listen_on`.\n\n:code:`socket`\n    Remote control requests received over a socket are accepted\n    unconditionally. Requests received over the TTY are confirmed based on\n    password.\n\n:code:`no`\n    Remote control is completely disabled.\n\n:code:`yes`\n    Remote control requests are always accepted.\n'''\n    )\n\nopt('listen_on', 'none',\n    long_text='''\nListen to the specified socket for remote control connections. Note that this\nwill apply to all kitty instances. It can be overridden by the :option:`kitty\n--listen-on` command line option. For UNIX sockets, such as\n:code:`unix:${TEMP}/mykitty` or :code:`unix:@mykitty` (on Linux). Environment\nvariables are expanded and relative paths are resolved with respect to the\ntemporary directory. If :code:`{kitty_pid}` is present, then it is replaced by\nthe PID of the kitty process, otherwise the PID of the kitty process is\nappended to the value, with a hyphen. For TCP sockets such as\n:code:`tcp:localhost:0` a random port is always used even if a non-zero port\nnumber is specified.  See the help for :option:`kitty --listen-on` for more\ndetails. Note that this will be ignored unless :opt:`allow_remote_control` is\nset to either: :code:`yes`, :code:`socket` or :code:`socket-only`.\nChanging this option by reloading the config is not supported.\n'''\n    )\n\nopt(\n    '+env',\n    '',\n    option_type='env',\n    add_to_default=False,\n    long_text='''\nSpecify the environment variables to be set in all child processes. Using the\nname with an equal sign (e.g. :code:`env VAR=`) will set it to the empty string.\nSpecifying only the name (e.g. :code:`env VAR`) will remove the variable from\nthe child process' environment. Note that environment variables are expanded\nrecursively, for example::\n\n    env VAR1=a\n    env VAR2=${HOME}/${VAR1}/b\n\nThe value of :code:`VAR2` will be :code:`<path to home directory>/a/b`.\n\nUse the special\nvalue :code:`read_from_shell` to have kitty read the specified variables from\nyour :opt:`login shell <shell>` configuration.\nUseful if your shell startup files setup a bunch of environment variables that you want available to kitty and\nin kitty session files. Each variable name is treated as a glob pattern to match. For example:\n:code:`env read_from_shell=PATH LANG LC_* XDG_* EDITOR VISUAL`. Note that these variables are only\nread after the configuration is fully processed, thus they are not available for recursive expansion and\nthey will override any variables set by other :opt:`env` directives.\n''',\n)\n\nopt('+filter_notification', '', option_type='filter_notification', add_to_default=False, long_text='''\nSpecify rules to filter out notifications sent by applications running in kitty.\nCan be specified multiple times to create multiple filter rules. A rule specification\nis of the form :code:`field:regexp`. A filter rule\ncan match on any of the fields: :code:`title`, :code:`body`, :code:`app`, :code:`type`.\nThe special value of :code:`all` filters out all notifications. Rules can be combined\nusing Boolean operators. Some examples::\n\n    filter_notification title:hello or body:\"abc.*def\"\n    # filter out notification from vim except for ones about updates, (?i)\n    # makes matching case insensitive.\n    filter_notification app:\"[ng]?vim\" and not body:\"(?i)update\"\n    # filter out all notifications\n    filter_notification all\n\nThe field :code:`app` is the name of the application sending the notification and :code:`type`\nis the type of the notification. Not all applications will send these fields, so you can also\nmatch on the title and body of the notification text. More sophisticated programmatic filtering\nand custom actions on notifications can be done by creating a notifications.py file in the\nkitty config directory (:file:`~/.config/kitty`). An annotated sample is\n:link:`available <https://github.com/kovidgoyal/kitty/blob/master/docs/notifications.py>`.\n''')\n\nopt('+watcher', '',\n    option_type='store_multiple',\n    add_to_default=False,\n    long_text='''\nPath to python file which will be loaded for :ref:`watchers`. Can be specified\nmore than once to load multiple watchers. The watchers will be added to every\nkitty window. Relative paths are resolved relative to the kitty config\ndirectory. Note that reloading the config will only affect windows created after\nthe reload.\n'''\n    )\n\nopt('+exe_search_path', '',\n    option_type='store_multiple',\n    add_to_default=False,\n    long_text='''\nControl where kitty finds the programs to run. The default search order is:\nFirst search the system wide :code:`PATH`, then :file:`~/.local/bin` and\n:file:`~/bin`. If still not found, the :code:`PATH` defined in the login shell\nafter sourcing all its startup files is tried. Finally, if present, the\n:code:`PATH` specified by the :opt:`env` option is tried.\n\nThis option allows you to prepend, append, or remove paths from this search\norder. It can be specified multiple times for multiple paths. A simple path will\nbe prepended to the search order. A path that starts with the :code:`+` sign\nwill be append to the search order, after :file:`~/bin` above. A path that\nstarts with the :code:`-` sign will be removed from the entire search order.\nFor example::\n\n    exe_search_path /some/prepended/path\n    exe_search_path +/some/appended/path\n    exe_search_path -/some/excluded/path\n\n'''\n    )\n\nopt('update_check_interval', '24',\n    option_type='float',\n    long_text='''\nThe interval to periodically check if an update to kitty is available (in\nhours). If an update is found, a system notification is displayed informing you\nof the available update. The default is to check every 24 hours, set to zero to\ndisable. Update checking is only done by the official binary builds. Distro\npackages or source builds do not do update checking. Changing this option by\nreloading the config is not supported.\n'''\n    )\n\nopt('startup_session', 'none',\n    option_type='config_or_absolute_path',\n    long_text='''\nPath to a session file to use for all kitty instances. Can be overridden by\nusing the :option:`kitty --session` :code:`=none` command line option for individual\ninstances. See :ref:`sessions` in the kitty documentation for details. Note that\nrelative paths are interpreted with respect to the kitty config directory.\nEnvironment variables in the path are expanded. Changing this option by\nreloading the config is not supported. Note that if kitty is invoked with command line\narguments specifying a command to run, this option is ignored.\n'''\n    )\n\nopt('clipboard_control', 'write-clipboard write-primary read-clipboard-ask read-primary-ask',\n    option_type='clipboard_control',\n    long_text='''\nAllow programs running in kitty to read and write from the clipboard. You can\ncontrol exactly which actions are allowed. The possible actions are:\n:code:`write-clipboard`, :code:`read-clipboard`, :code:`write-primary`,\n:code:`read-primary`, :code:`read-clipboard-ask`, :code:`read-primary-ask`. The\ndefault is to allow writing to the clipboard and primary selection and to ask\nfor permission when a program tries to read from the clipboard. Note that\ndisabling the read confirmation is a security risk as it means that any program,\neven the ones running on a remote server via SSH can read your clipboard. See\nalso :opt:`clipboard_max_size`.\n'''\n    )\n\nopt('clipboard_max_size', '512',\n    option_type='positive_float',\n    long_text='''\nThe maximum size (in MB) of data from programs running in kitty that will be\nstored for writing to the system clipboard. A value of zero means no size limit\nis applied. See also :opt:`clipboard_control`.\n'''\n    )\n\nopt('file_transfer_confirmation_bypass', '', has_secret=True, long_text='''\nThe password that can be supplied to the :doc:`file transfer kitten\n</kittens/transfer>` to skip the transfer confirmation prompt. This should only\nbe used when initiating transfers from trusted computers, over trusted networks\nor encrypted transports, as it allows any programs running on the remote machine\nto read/write to the local filesystem, without permission.\n'''\n    )\n\nopt('allow_hyperlinks', 'yes',\n    option_type='allow_hyperlinks', ctype='bool',\n    long_text='''\nProcess :term:`hyperlink <hyperlinks>` escape sequences (OSC 8). If disabled OSC\n8 escape sequences are ignored. Otherwise they become clickable links, that you\ncan click with the mouse or by using the :doc:`hints kitten </kittens/hints>`.\nThe special value of :code:`ask` means that kitty will ask before opening the\nlink when clicked.\n'''\n    )\n\nopt('shell_integration', 'enabled',\n    option_type='shell_integration',\n    long_text='''\nEnable shell integration on supported shells. This enables features such as\njumping to previous prompts, browsing the output of the previous command in a\npager, etc. on supported shells. Set to :code:`disabled` to turn off shell\nintegration, completely. It is also possible to disable individual features, set\nto a space separated list of these values: :code:`no-rc`, :code:`no-cursor`,\n:code:`no-title`, :code:`no-cwd`, :code:`no-prompt-mark`, :code:`no-complete`, :code:`no-sudo`.\nSee :ref:`Shell integration <shell_integration>` for details.\n'''\n    )\n\nopt('allow_cloning', 'ask',\n    choices=('yes', 'y', 'true', 'no', 'n', 'false', 'ask'),\n    long_text='''\nControl whether programs running in the terminal can request new windows to be\ncreated. The canonical example is :ref:`clone-in-kitty <clone_shell>`. By\ndefault, kitty will ask for permission for each clone request. Allowing cloning\nunconditionally gives programs running in the terminal (including over SSH)\npermission to execute arbitrary code, as the user who is running the terminal,\non the computer that the terminal is running on.\n'''\n    )\n\nopt('clone_source_strategies', 'venv,conda,env_var,path',\n    option_type='clone_source_strategies',\n    long_text='''\nControl what shell code is sourced when running :command:`clone-in-kitty`\nin the newly cloned window. The supported strategies are:\n\n:code:`venv`\n    Source the file :file:`$VIRTUAL_ENV/bin/activate`. This is used by the\n    Python stdlib venv module and allows cloning venvs automatically.\n:code:`conda`\n    Run :code:`conda activate $CONDA_DEFAULT_ENV`. This supports the virtual\n    environments created by :program:`conda`.\n:code:`env_var`\n    Execute the contents of the environment variable\n    :envvar:`KITTY_CLONE_SOURCE_CODE` with :code:`eval`.\n:code:`path`\n    Source the file pointed to by the environment variable\n    :envvar:`KITTY_CLONE_SOURCE_PATH`.\n\nThis option must be a comma separated list of the above values. Only the\nfirst valid match, in the order specified, is sourced.\n'''\n    )\n\nopt('notify_on_cmd_finish', 'never', option_type='notify_on_cmd_finish', long_text='''\nShow a desktop notification when a long-running command finishes\n(needs :opt:`shell_integration`).\nThe possible values are:\n\n:code:`never`\n    Never send a notification.\n\n:code:`unfocused`\n    Only send a notification when the window does not have keyboard focus.\n\n:code:`invisible`\n    Only send a notification when the window both is unfocused and not visible\n    to the user, for example, because it is in an inactive tab or its OS window\n    is not currently visible (on platforms that support OS window visibility querying\n    this considers an OS Window visible iff it is active).\n\n:code:`always`\n    Always send a notification, regardless of window state.\n\nThere are two optional arguments:\n\nFirst, the minimum duration for what is considered a\nlong running command. The default is 5 seconds. Specify a second argument\nto set the duration. For example: :code:`invisible 15`.\nDo not set the value too small, otherwise a command that launches a new OS Window\nand exits will spam a notification.\n\nSecond, the action to perform. The default is :code:`notify`. The possible values are:\n\n:code:`notify`\n    Send a desktop notification. The subsequent arguments are optional and specify when\n    the notification is automatically cleared. The set of possible events when the notification is\n    cleared are: :code:`focus` and :code:`next`. :code:`focus` means that when the notification\n    policy is :code:`unfocused` or :code:`invisible` the notification is automatically cleared\n    when the window regains focus. The value of :code:`next` means that the previous notification\n    is cleared when the next notification is shown. The default when no arguments are specified\n    is: :code:`focus next`.\n\n:code:`bell`\n    Ring the terminal bell.\n\n:code:`notify-bell`\n    Send a desktop notification and ring the terminal bell.\n    The arguments are the same as for `notify`.\n\n:code:`command`\n    Run a custom command. All subsequent arguments are the cmdline to run.\n\nSome more examples::\n\n    # Send a notification when a command takes more than 5 seconds in an unfocused window\n    notify_on_cmd_finish unfocused\n    # Send a notification when a command takes more than 10 seconds in a invisible window\n    notify_on_cmd_finish invisible 10.0\n    # Ring a bell when a command takes more than 10 seconds in a invisible window\n    notify_on_cmd_finish invisible 10.0 bell\n    # Run 'notify-send' when a command takes more than 10 seconds in a invisible window\n    # Here %c is replaced by the current command line and %s by the job exit code\n    notify_on_cmd_finish invisible 10.0 command notify-send \"job finished with status: %s\" %c\n    # Do not clear previous notification when next command finishes or window regains focus\n    notify_on_cmd_finish invisible 5.0 notify\n\n'''\n    )\n\nopt('term', 'xterm-kitty',\n    long_text='''\nThe value of the :envvar:`TERM` environment variable to set. Changing this can\nbreak many terminal programs, only change it if you know what you are doing, not\nbecause you read some advice on \"Stack Overflow\" to change it. The\n:envvar:`TERM` variable is used by various programs to get information about the\ncapabilities and behavior of the terminal. If you change it, depending on what\nprograms you run, and how different the terminal you are changing it to is,\nvarious things from key-presses, to colors, to various advanced features may not\nwork. Changing this option by reloading the config will only affect newly\ncreated windows.\n'''\n    )\n\nopt('terminfo_type', 'path', choices=('path', 'direct', 'none'),\n    long_text='''\nThe value of the :envvar:`TERMINFO` environment variable to set. This variable is\nused by programs running in the terminal to search for terminfo databases. The default value\nof :code:`path` causes kitty to set it to a filesystem location containing the\nkitty terminfo database. A value of :code:`direct` means put the entire database into\nthe env var directly. This can be useful when connecting to containers, for example. But,\nnote that not all software supports this. A value of :code:`none` means do not touch the variable.\n'''\n    )\n\n\nopt('forward_stdio', 'no', option_type='to_bool', long_text='''\nForward STDOUT and STDERR of the kitty process to child processes.\nThis is useful for debugging as it\nallows child processes to print to kitty's STDOUT directly. For example,\n:code:`echo hello world >&$KITTY_STDIO_FORWARDED` in a shell will print\nto the parent kitty's STDOUT. Sets the :code:`KITTY_STDIO_FORWARDED=fdnum`\nenvironment variable so child processes know about the forwarding. Note that\non macOS this prevents the shell from being run via the login utility so getlogin()\nwill not work in programs run in this session.\n''')\n\nopt('+menu_map', '',\n    option_type='menu_map', add_to_default=False, ctype='!menu_map',\n    long_text='''\nSpecify entries for various menus in kitty. Currently only the global menubar on macOS\nis supported. For example::\n\n   menu_map global \"Actions::Launch something special\" launch --hold --type=os-window sh -c \"echo hello world\"\n\nThis will create a menu entry named \"Launch something special\" in an \"Actions\" menu in the macOS global menubar.\nSub-menus can be created by adding more levels separated by the :code:`::` characters.\n'''\n    )\n\n\negr()  # }}}\n\n\n# os {{{\nagr('os', 'OS specific tweaks')\n\nopt('wayland_titlebar_color', 'system', option_type='titlebar_color', ctype='uint', long_text='''\nThe color of the kitty window's titlebar on Wayland systems with client\nside window decorations such as GNOME. A value of :code:`system` means to use\nthe default system colors, a value of :code:`background` means to use the\nbackground color of the currently active kitty window and finally you can use an\narbitrary color, such as :code:`#12af59` or :code:`red`.\n'''\n    )\n\nopt('macos_titlebar_color', 'system', option_type='macos_titlebar_color', ctype='int', long_text='''\nThe color of the kitty window's titlebar on macOS. A value of\n:code:`system` means to use the default system color, :code:`light` or\n:code:`dark` can also be used to set it explicitly. A value of\n:code:`background` means to use the background color of the currently active\nwindow and finally you can use an arbitrary color, such as :code:`#12af59` or\n:code:`red`.\n'''\n    )\n\nopt('macos_option_as_alt', 'no',\n    option_type='macos_option_as_alt', ctype='uint',\n    long_text='''\nUse the :kbd:`Option` key as an :kbd:`Alt` key on macOS. With this set to\n:code:`no`, kitty will use the macOS native :kbd:`Option+Key` to enter Unicode\ncharacter behavior. This will break any :kbd:`Alt+Key` keyboard shortcuts in\nyour terminal programs, but you can use the macOS Unicode input technique. You\ncan use the values: :code:`left`, :code:`right` or :code:`both` to use only the\nleft, right or both :kbd:`Option` keys as :kbd:`Alt`, instead. Note that kitty\nitself always treats :kbd:`Option` the same as :kbd:`Alt`. This means you cannot\nuse this option to configure different kitty shortcuts for :kbd:`Option+Key`\nvs. :kbd:`Alt+Key`. Also, any kitty shortcuts using :kbd:`Option/Alt+Key` will\ntake priority, so that any such key presses will not be passed to terminal\nprograms running inside kitty. Changing this option by reloading the config is\nnot supported.\n'''\n    )\n\nopt('macos_hide_from_tasks', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nHide the kitty window from running tasks on macOS (:kbd:`⌘+Tab` and the Dock).\nChanging this option by reloading the config is not supported.\n'''\n    )\n\nopt('macos_quit_when_last_window_closed', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nHave kitty quit when all the top-level windows are closed on macOS. By default,\nkitty will stay running, even with no open windows, as is the expected behavior\non macOS.\n'''\n    )\n\nopt('macos_window_resizable', 'yes',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nDisable this if you want kitty top-level OS windows to not be resizable on\nmacOS.\n'''\n    )\n\nopt('macos_thicken_font', '0',\n    option_type='positive_float', ctype='float',\n    long_text='''\nDraw an extra border around the font with the given width, to increase\nlegibility at small font sizes on macOS. For example, a value of :code:`0.75`\nwill result in rendering that looks similar to sub-pixel antialiasing at common\nfont sizes. Note that in modern kitty, this option is obsolete (although still\nsupported). Consider using :opt:`text_composition_strategy` instead.\n'''\n    )\n\nopt('macos_traditional_fullscreen', 'no',\n    option_type='to_bool', ctype='bool',\n    long_text='''\nUse the macOS traditional full-screen transition, that is faster, but less\npretty.\n'''\n    )\n\nopt('macos_show_window_title_in', 'all',\n    choices=('all', 'menubar', 'none', 'window'), ctype='window_title_in',\n    long_text='''\nControl where the window title is displayed on macOS. A value of :code:`window`\nwill show the title of the currently active window at the top of the macOS\nwindow. A value of :code:`menubar` will show the title of the currently active\nwindow in the macOS global menu bar, making use of otherwise wasted space. A\nvalue of :code:`all` will show the title in both places, and :code:`none` hides\nthe title. See :opt:`macos_menubar_title_max_length` for how to control the\nlength of the title in the menu bar.\n'''\n    )\n\nopt('macos_menubar_title_max_length', '0',\n    option_type='positive_int', ctype='int',\n    long_text='''\nThe maximum number of characters from the window title to show in the macOS\nglobal menu bar. Values less than one means that there is no maximum limit.\n'''\n    )\n\nopt('macos_custom_beam_cursor', 'no',\n    option_type='to_bool',\n    long_text='''\nUse a custom mouse cursor for macOS that is easier to see on both light\nand dark backgrounds. Nowadays, the default macOS cursor already comes with a\nwhite border. WARNING: this might make your mouse cursor invisible on\ndual GPU machines. Changing this option by reloading the config is not supported.\n'''\n    )\n\nopt('macos_colorspace', 'srgb',\n    choices=('srgb', 'default', 'displayp3'), ctype='macos_colorspace',\n    long_text='''\nThe colorspace in which to interpret terminal colors. The default of\n:code:`srgb` will cause colors to match those seen in web browsers. The value of\n:code:`default` will use whatever the native colorspace of the display is.\nThe value of :code:`displayp3` will use Apple's special snowflake display P3\ncolor space, which will result in over saturated (brighter) colors with some\ncolor shift. Reloading configuration will change this value only for newly\ncreated OS windows.\n''')\n\n\nopt('linux_display_server', 'auto',\n    choices=('auto', 'wayland', 'x11'),\n    long_text='''\nChoose between Wayland and X11 backends. By default, an appropriate backend\nbased on the system state is chosen automatically. Set it to :code:`x11` or\n:code:`wayland` to force the choice. Changing this option by reloading the\nconfig is not supported.\n'''\n    )\n\nopt('wayland_enable_ime', 'yes', option_type='to_bool', ctype='bool', long_text='''\nEnable Input Method Extension on Wayland. This is typically used for\ninputting text in East Asian languages. However, its implementation in\nWayland is often buggy and introduces latency into the input loop,\nso disable this if you know you dont need it. Changing this option\nby reloading the config is not supported, it will not have any effect.\n''')\negr()  # }}}\n\n\n# shortcuts {{{\nagr('shortcuts', 'Keyboard shortcuts', '''\nKeys are identified simply by their lowercase Unicode characters. For example:\n:code:`a` for the :kbd:`A` key, :code:`[` for the left square bracket key, etc.\nFor functional keys, such as :kbd:`Enter` or :kbd:`Escape`, the names are present\nat :ref:`Functional key definitions <functional>`. For modifier keys, the names\nare :kbd:`ctrl` (:kbd:`control`, :kbd:`⌃`), :kbd:`shift` (:kbd:`⇧`), :kbd:`alt`\n(:kbd:`opt`, :kbd:`option`, :kbd:`⌥`), :kbd:`super` (:kbd:`cmd`, :kbd:`command`,\n:kbd:`⌘`).\n\nSimple shortcut mapping is done with the :code:`map` directive. For full details\non advanced mapping including modal and per application maps, see :doc:`mapping`.\nSome quick examples to illustrate common tasks::\n\n    # unmap a keyboard shortcut, passing it to the program running in kitty\n    map kitty_mod+space\n    # completely ignore a keyboard event\n    map ctrl+alt+f1 discard_event\n    # combine multiple actions\n    map kitty_mod+e combine : new_window : next_layout\n    # multi-key shortcuts\n    map ctrl+x>ctrl+y>z action\n\nYou can browse and trigger these actions by pressing :sc:`command_palette` to\nrun the command palette. The full list of actions that can be mapped to\nkey presses is available :doc:`here </actions>`.\n''')\n\nopt('kitty_mod', 'ctrl+shift',\n    option_type='to_modifiers',\n    long_text='''\nSpecial modifier key alias for default shortcuts. You can change the value of\nthis option to alter all default shortcuts that use :opt:`kitty_mod`.\n'''\n    )\n\nopt('clear_all_shortcuts', 'no',\n    option_type='clear_all_shortcuts',\n    long_text='''\nRemove all shortcut definitions up to this point. Useful, for instance, to\nremove the default shortcuts.\n'''\n    )\n\nopt('map_timeout', '0.0', option_type='positive_float', long_text='''\nThe default timeout (in seconds) for multi-key mappings and modal keyboard modes.\nIf you press the first key(s) of a multi-key mapping and don't press the next\nkey within this timeout, the mapping is cancelled and the mode is exited. A value\nof zero disables the timeout. This can be overridden for specific modes using the\n:code:`--timeout` option when creating a keyboard mode with :code:`--new-mode`.\nFor example::\n\n    # 2 second timeout for all mappings\n    map_timeout 2.0\n\n    # This mode will have a 5 second timeout (overrides the global 2 second timeout)\n    map --new-mode resize --timeout 5.0 kitty_mod+r\n''')\n\nopt('+action_alias', 'launch_tab launch --type=tab --cwd=current',\n    option_type='action_alias',\n    add_to_default=False,\n    long_text='''\nDefine action aliases to avoid repeating the same options in multiple mappings.\nAliases can be defined for any action and will be expanded recursively. For\nexample, the above alias allows you to create mappings to launch a new tab in\nthe current working directory without duplication::\n\n    map f1 launch_tab vim\n    map f2 launch_tab emacs\n\nSimilarly, to alias kitten invocation::\n\n    action_alias hints kitten hints --hints-offset=0\n'''\n    )\n\nopt('+kitten_alias', 'hints hints --hints-offset=0',\n    option_type='kitten_alias',\n    add_to_default=False,\n    long_text='''\nLike :opt:`action_alias` above, but specifically for kittens. Generally, prefer\nto use :opt:`action_alias`. This option is a legacy version, present for\nbackwards compatibility. It causes all invocations of the aliased kitten to be\nsubstituted. So the example above will cause all invocations of the hints kitten\nto have the :option:`--hints-offset=0 <kitty +kitten hints --hints-offset>`\noption applied.\n'''\n    )\n\n\n# shortcuts.clipboard {{{\nagr('shortcuts.clipboard', 'Clipboard')\n\nmap('Copy to clipboard',\n    'copy_to_clipboard kitty_mod+c copy_to_clipboard',\n    long_text='''\nThere is also a :ac:`copy_or_interrupt` action that can be optionally mapped\nto :kbd:`Ctrl+C`. It will copy only if there is a selection and send an\ninterrupt otherwise. Similarly, :ac:`copy_and_clear_or_interrupt` will copy\nand clear the selection or send an interrupt if there is no selection.\nThe :ac:`copy_or_noop` action will copy if there is a selection and pass\nthe key through to the application running in the terminal if there is no selection.\n'''\n    )\nmap('Copy to clipboard or pass through',\n    'copy_or_noop cmd+c copy_or_noop',\n    only='macos',\n    )\n\nmap('Paste from clipboard',\n    'paste_from_clipboard kitty_mod+v paste_from_clipboard',\n    )\nmap('Paste from clipboard',\n    'paste_from_clipboard cmd+v paste_from_clipboard',\n    only='macos',\n    )\n\nmap('Paste from selection',\n    'paste_from_selection kitty_mod+s paste_from_selection',\n    )\nmap('Paste from selection',\n    'paste_from_selection shift+insert paste_from_selection',\n    )\n\nmap('Pass selection to program',\n    'pass_selection_to_program kitty_mod+o pass_selection_to_program',\n    long_text='''\nYou can also pass the contents of the current selection to any program with\n:ac:`pass_selection_to_program`. By default, the system's open program is used,\nbut you can specify your own, the selection will be passed as a command line\nargument to the program. For example::\n\n    map kitty_mod+o pass_selection_to_program firefox\n\nYou can pass the current selection to a terminal program running in a new kitty\nwindow, by using the :code:`@selection` placeholder::\n\n    map kitty_mod+y new_window less @selection\n'''\n    )\negr()  # }}}\n\n\n# shortcuts.scrolling {{{\nagr('shortcuts.scrolling', 'Scrolling')\n\nmap('Scroll line up',\n    'scroll_line_up kitty_mod+up scroll_line_up',\n    )\nmap('Scroll line up',\n    'scroll_line_up kitty_mod+k scroll_line_up',\n    )\nmap('Scroll line up',\n    'scroll_line_up opt+cmd+page_up scroll_line_up',\n    only='macos',\n    )\nmap('Scroll line up',\n    'scroll_line_up cmd+up scroll_line_up',\n    only='macos',\n    )\n\nmap('Scroll line down',\n    'scroll_line_down kitty_mod+down scroll_line_down',\n    )\nmap('Scroll line down',\n    'scroll_line_down kitty_mod+j scroll_line_down',\n    )\nmap('Scroll line down',\n    'scroll_line_down opt+cmd+page_down scroll_line_down',\n    only='macos',\n    )\nmap('Scroll line down',\n    'scroll_line_down cmd+down scroll_line_down',\n    only='macos',\n    )\n\nmap('Scroll page up',\n    'scroll_page_up kitty_mod+page_up scroll_page_up',\n    )\nmap('Scroll page up',\n    'scroll_page_up cmd+page_up scroll_page_up',\n    only='macos',\n    )\n\nmap('Scroll page down',\n    'scroll_page_down kitty_mod+page_down scroll_page_down',\n    )\nmap('Scroll page down',\n    'scroll_page_down cmd+page_down scroll_page_down',\n    only='macos',\n    )\n\nmap('Scroll to top',\n    'scroll_home kitty_mod+home scroll_home',\n    )\nmap('Scroll to top',\n    'scroll_home cmd+home scroll_home',\n    only='macos',\n    )\n\nmap('Scroll to bottom',\n    'scroll_end kitty_mod+end scroll_end',\n    )\nmap('Scroll to bottom',\n    'scroll_end cmd+end scroll_end',\n    only='macos',\n    )\n\nmap('Scroll to previous shell prompt',\n    'scroll_to_previous_prompt kitty_mod+z scroll_to_prompt -1',\n    long_text='''\nUse a parameter of :code:`0` for :ac:`scroll_to_prompt` to scroll to the last\njumped to or the last clicked position. Requires :ref:`shell integration\n<shell_integration>` to work.\n'''\n    )\n\nmap('Scroll to next shell prompt', 'scroll_to_next_prompt kitty_mod+x scroll_to_prompt 1')\n\nmap('Browse scrollback buffer in pager',\n    'show_scrollback kitty_mod+h show_scrollback',\n    long_text='''\nYou can pipe the contents of the current screen and history buffer as\n:file:`STDIN` to an arbitrary program using :option:`launch --stdin-source`.\nFor example, the following opens the scrollback buffer in less in an\n:term:`overlay` window::\n\n    map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay less +G -R\n\nFor more details on piping screen and buffer contents to external programs,\nsee :doc:`launch`.\n'''\n    )\n\nmap('Browse output of the last shell command in pager',\n    'show_last_command_output kitty_mod+g show_last_command_output',\n    long_text='''\nYou can also define additional shortcuts to get the command output.\nFor example, to get the first command output on screen::\n\n    map f1 show_first_command_output_on_screen\n\nTo get the command output that was last accessed by a keyboard action or mouse\naction::\n\n    map f1 show_last_visited_command_output\n\nYou can pipe the output of the last command run in the shell using the\n:ac:`launch` action. For example, the following opens the output in less in an\n:term:`overlay` window::\n\n    map f1 launch --stdin-source=@last_cmd_output --stdin-add-formatting --type=overlay less +G -R\n\nTo get the output of the first command on the screen, use :code:`@first_cmd_output_on_screen`.\nTo get the output of the last jumped to command, use :code:`@last_visited_cmd_output`.\n\nRequires :ref:`shell integration <shell_integration>` to work.\n'''\n    )\n\nmap('Search the scrollback within a pager',\n    'search_scrollback kitty_mod+/ search_scrollback',\n    long_text='''\nSearch for currently selected text in the scrollback using the configured :opt:`scrollback_pager`.\nAssumes that pressing the :kbd:`/` key triggers search mode in the pager. If you want to create\na manual mapping with a special pager for this, you can use something like:\n\n    map f1 combine : launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay mypager : send_key /\n\nFor more sophisticated control, such as using the current selection, use :ac:`remote_control_script`.\n''')\n\nmap('Search the scrollback within a pager', 'search_scrollback cmd+f search_scrollback', only='macos')\n\n\negr()  # }}}\n\n\n# shortcuts.window {{{\nagr('shortcuts.window', 'Window management')\n\nmap('New window',\n    'new_window kitty_mod+enter new_window',\n    long_text='''\nYou can open a new :term:`kitty window <window>` running an arbitrary program,\nfor example::\n\n    map kitty_mod+y launch mutt\n\nYou can open a new window with the current working directory set to the working\ndirectory of the current window using::\n\n    map ctrl+alt+enter launch --cwd=current\n\nYou can open a new window that is allowed to control kitty via\nthe kitty remote control facility with :option:`launch --allow-remote-control`.\nAny programs running in that window will be allowed to control kitty.\nFor example::\n\n    map ctrl+enter launch --allow-remote-control some_program\n\nYou can open a new window next to the currently active window or as the first\nwindow, with::\n\n    map ctrl+n launch --location=neighbor\n    map ctrl+f launch --location=first\n\nFor more details, see :doc:`launch`.\n'''\n    )\nmap('New window',\n    'new_window cmd+enter new_window',\n    only='macos',\n    )\n\nmap('New OS window',\n    'new_os_window kitty_mod+n new_os_window',\n    long_text='''\nWorks like :ac:`new_window` above, except that it opens a top-level :term:`OS\nwindow <os_window>`. In particular you can use :ac:`new_os_window_with_cwd` to\nopen a window with the current working directory.\n'''\n    )\nmap('New OS window',\n    'new_os_window cmd+n new_os_window',\n    only='macos',\n    )\n\nmap('Close window',\n    'close_window kitty_mod+w close_window',\n    )\nmap('Close window',\n    'close_window shift+cmd+d close_window',\n    only='macos',\n    )\n\nmap('Next window',\n    'next_window kitty_mod+] next_window',\n    )\n\nmap('Previous window',\n    'previous_window kitty_mod+[ previous_window',\n    )\n\nmap('Move window forward',\n    'move_window_forward kitty_mod+f move_window_forward',\n    )\n\nmap('Move window backward',\n    'move_window_backward kitty_mod+b move_window_backward',\n    )\n\nmap('Move window to top',\n    'move_window_to_top kitty_mod+` move_window_to_top',\n    )\n\nmap('Start resizing window',\n    'start_resizing_window kitty_mod+r start_resizing_window',\n    )\nmap('Start resizing window',\n    'start_resizing_window cmd+r start_resizing_window',\n    only='macos',\n    )\n\nmap('First window',\n    'first_window kitty_mod+1 first_window',\n    )\nmap('First window',\n    'first_window cmd+1 first_window',\n    only='macos',\n    )\n\nmap('Second window',\n    'second_window kitty_mod+2 second_window',\n    )\nmap('Second window',\n    'second_window cmd+2 second_window',\n    only='macos',\n    )\n\nmap('Third window',\n    'third_window kitty_mod+3 third_window',\n    )\nmap('Third window',\n    'third_window cmd+3 third_window',\n    only='macos',\n    )\n\nmap('Fourth window',\n    'fourth_window kitty_mod+4 fourth_window',\n    )\nmap('Fourth window',\n    'fourth_window cmd+4 fourth_window',\n    only='macos',\n    )\n\nmap('Fifth window',\n    'fifth_window kitty_mod+5 fifth_window',\n    )\nmap('Fifth window',\n    'fifth_window cmd+5 fifth_window',\n    only='macos',\n    )\n\nmap('Sixth window',\n    'sixth_window kitty_mod+6 sixth_window',\n    )\nmap('Sixth window',\n    'sixth_window cmd+6 sixth_window',\n    only='macos',\n    )\n\nmap('Seventh window',\n    'seventh_window kitty_mod+7 seventh_window',\n    )\nmap('Seventh window',\n    'seventh_window cmd+7 seventh_window',\n    only='macos',\n    )\n\nmap('Eighth window',\n    'eighth_window kitty_mod+8 eighth_window',\n    )\nmap('Eighth window',\n    'eighth_window cmd+8 eighth_window',\n    only='macos',\n    )\n\nmap('Ninth window',\n    'ninth_window kitty_mod+9 ninth_window',\n    )\nmap('Ninth window',\n    'ninth_window cmd+9 ninth_window',\n    only='macos',\n    )\n\nmap('Tenth window',\n    'tenth_window kitty_mod+0 tenth_window',\n    )\n\nmap('Visually select and focus window', 'focus_visible_window kitty_mod+f7 focus_visible_window',\n    long_text='''\nDisplay overlay numbers and alphabets on the window, and switch the focus to the\nwindow when you press the key. When there are only two windows, the focus will\nbe switched directly without displaying the overlay. You can change the overlay\ncharacters and their order with option :opt:`visual_window_select_characters`.\n'''\n    )\nmap('Visually swap window with another', 'swap_with_window kitty_mod+f8 swap_with_window',\n    long_text='''\nWorks like :ac:`focus_visible_window` above, but swaps the window.\n'''\n    )\negr()  # }}}\n\n\n# shortcuts.tab {{{\nagr('shortcuts.tab', 'Tab management')\n\nmap('Next tab',\n    'next_tab kitty_mod+right next_tab',\n    )\nmap('Next tab',\n    'next_tab shift+cmd+] next_tab',\n    only='macos',\n    )\nmap('Next tab',\n    'next_tab ctrl+tab next_tab',\n    )\n\nmap('Previous tab',\n    'previous_tab kitty_mod+left previous_tab',\n    )\nmap('Previous tab',\n    'previous_tab shift+cmd+[ previous_tab',\n    only='macos',\n    )\nmap('Previous tab',\n    'previous_tab ctrl+shift+tab previous_tab',\n    )\n\nmap('New tab',\n    'new_tab kitty_mod+t new_tab',\n    )\nmap('New tab',\n    'new_tab cmd+t new_tab',\n    only='macos',\n    )\n\nmap('Close tab',\n    'close_tab kitty_mod+q close_tab',\n    )\nmap('Close tab',\n    'close_tab cmd+w close_tab',\n    only='macos',\n    )\n\nmap('Close OS window',\n    'close_os_window shift+cmd+w close_os_window',\n    only='macos',\n    )\n\nmap('Move tab forward',\n    'move_tab_forward kitty_mod+. move_tab_forward',\n    )\n\nmap('Move tab backward',\n    'move_tab_backward kitty_mod+, move_tab_backward',\n    )\n\nmap('Set tab title',\n    'set_tab_title kitty_mod+alt+t set_tab_title',\n    )\nmap('Set tab title',\n    'set_tab_title shift+cmd+i set_tab_title',\n    only='macos',\n    )\negr('''\nYou can also create shortcuts to go to specific :term:`tabs <tab>`, with\n:code:`1` being the first tab, :code:`2` the second tab and :code:`-1` being the\npreviously active tab, :code:`-2` being the tab active before the previously active tab and so on.\nAny number larger than the number of tabs goes to the last tab and any number less\nthan the number of previously used tabs in the history goes to the oldest previously used tab in the history::\n\n    map ctrl+alt+1 goto_tab 1\n    map ctrl+alt+2 goto_tab 2\n\nJust as with :ac:`new_window` above, you can also pass the name of arbitrary\ncommands to run when using :ac:`new_tab` and :ac:`new_tab_with_cwd`. Finally,\nif you want the new tab to open next to the current tab rather than at the\nend of the tabs list, use::\n\n    map ctrl+t new_tab !neighbor [optional cmd to run]\n''')  # }}}\n\n\n# shortcuts.layout {{{\nagr('shortcuts.layout', 'Layout management')\n\nmap('Next layout',\n    'next_layout kitty_mod+l next_layout',\n    )\negr('''\nYou can also create shortcuts to switch to specific :term:`layouts <layout>`::\n\n    map ctrl+alt+t goto_layout tall\n    map ctrl+alt+s goto_layout stack\n\nSimilarly, to switch back to the previous layout::\n\n    map ctrl+alt+p last_used_layout\n\nThere is also a :ac:`toggle_layout` action that switches to the named layout or\nback to the previous layout if in the named layout. Useful to temporarily \"zoom\"\nthe active window by switching to the stack layout::\n\n    map ctrl+alt+z toggle_layout stack\n''')  # }}}\n\n\n# shortcuts.fonts {{{\nagr('shortcuts.fonts', 'Font sizes', '''\nYou can change the font size for all top-level kitty OS windows at a time or\nonly the current one.\n''')\n\nmap('Increase font size',\n    'increase_font_size kitty_mod+equal change_font_size all +2.0',\n    )\nmap('Increase font size',\n    'increase_font_size kitty_mod+plus change_font_size all +2.0',\n    )\nmap('Increase font size',\n    'increase_font_size kitty_mod+kp_add change_font_size all +2.0',\n    )\nmap('Increase font size',\n    'increase_font_size cmd+plus change_font_size all +2.0',\n    only='macos',\n    )\nmap('Increase font size',\n    'increase_font_size cmd+equal change_font_size all +2.0',\n    only='macos',\n    )\nmap('Increase font size',\n    'increase_font_size shift+cmd+equal change_font_size all +2.0',\n    only='macos',\n    )\n\nmap('Decrease font size',\n    'decrease_font_size kitty_mod+minus change_font_size all -2.0',\n    )\nmap('Decrease font size',\n    'decrease_font_size kitty_mod+kp_subtract change_font_size all -2.0',\n    )\nmap('Decrease font size',\n    'decrease_font_size cmd+minus change_font_size all -2.0',\n    only='macos',\n    )\nmap('Decrease font size',\n    'decrease_font_size shift+cmd+minus change_font_size all -2.0',\n    only='macos',\n    )\n\nmap('Reset font size',\n    'reset_font_size kitty_mod+backspace change_font_size all 0',\n    )\nmap('Reset font size',\n    'reset_font_size cmd+0 change_font_size all 0',\n    only='macos',\n    )\negr('''\nTo setup shortcuts for specific font sizes::\n\n    map kitty_mod+f6 change_font_size all 10.0\n\nTo setup shortcuts to change only the current OS window's font size::\n\n    map kitty_mod+f6 change_font_size current 10.0\n\nTo setup shortcuts to multiply/divide the font size::\n\n    map kitty_mod+f6 change_font_size all *2.0\n    map kitty_mod+f6 change_font_size all /2.0\n''')  # }}}\n\n\n# shortcuts.selection {{{\nagr('shortcuts.selection', 'Select and act on visible text', '''\nUse the hints kitten to select text and either pass it to an external program or\ninsert it into the terminal or copy it to the clipboard.\n''')\n\nmap('Open URL',\n    'open_url kitty_mod+e open_url_with_hints',\n    long_text='''\nOpen a currently visible URL using the keyboard. The program used to open the\nURL is specified in :opt:`open_url_with`.\n'''\n    )\n\nmap('Insert selected path',\n    'insert_selected_path kitty_mod+p>f kitten hints --type path --program -',\n    long_text='''\nSelect a path/filename and insert it into the terminal. Useful, for instance to\nrun :program:`git` commands on a filename output from a previous :program:`git`\ncommand.\n'''\n    )\n\nmap('Open selected path',\n    'open_selected_path kitty_mod+p>shift+f kitten hints --type path',\n    long_text='Select a path/filename and open it with the default open program.'\n    )\n\nmap('Insert chosen file',\n    'insert_chosen_file kitty_mod+p>c kitten choose-files',\n    long_text='''\nSelect a file using the :doc:`choose-files </kittens/choose-files>` kitten and insert\nit into the terminal.\n'''\n    )\n\nmap('Insert chosen directory',\n    'insert_chosen_directory kitty_mod+p>d kitten choose-files --mode=dir',\n    long_text='''\nSelect a directory using the :doc:`choose-files </kittens/choose-files>` kitten and insert\nit into the terminal.\n'''\n    )\n\n\nmap('Insert selected line',\n    'insert_selected_line kitty_mod+p>l kitten hints --type line --program -',\n    long_text='''\nSelect a line of text and insert it into the terminal. Useful for the output of\nthings like: ``ls -1``.\n'''\n    )\n\nmap('Insert selected word',\n    'insert_selected_word kitty_mod+p>w kitten hints --type word --program -',\n    long_text='Select words and insert into terminal.'\n    )\n\nmap('Insert selected hash',\n    'insert_selected_hash kitty_mod+p>h kitten hints --type hash --program -',\n    long_text='''\nSelect something that looks like a hash and insert it into the terminal. Useful\nwith :program:`git`, which uses SHA1 hashes to identify commits.\n'''\n    )\n\nmap('Open the selected file at the selected line',\n    'goto_file_line kitty_mod+p>n kitten hints --type linenum',\n    long_text='''\nSelect something that looks like :code:`filename:linenum` and open it in\nyour default editor at the specified line number.\n'''\n    )\n\nmap('Open the selected hyperlink',\n    'open_selected_hyperlink kitty_mod+p>y kitten hints --type hyperlink',\n    long_text='''\nSelect a :term:`hyperlink <hyperlinks>` (i.e. a URL that has been marked as such\nby the terminal program, for example, by ``ls --hyperlink=auto``).\n'''\n    )\negr('''\nThe hints kitten has many more modes of operation that you can map to different\nshortcuts. For a full description see :doc:`hints kitten </kittens/hints>`.\n''')  # }}}\n\n\n# shortcuts.misc {{{\nagr('shortcuts.misc', 'Miscellaneous')\n\nmap('Show documentation',\n    'show_kitty_doc kitty_mod+f1 show_kitty_doc overview')\n\nmap('Command palette',\n    'command_palette kitty_mod+f3 command_palette')\n\nmap('Toggle fullscreen',\n    'toggle_fullscreen kitty_mod+f11 toggle_fullscreen',\n    )\nmap('Toggle fullscreen',\n    'toggle_fullscreen ctrl+cmd+f toggle_fullscreen',\n    only='macos',\n    )\n\nmap('Toggle maximized',\n    'toggle_maximized kitty_mod+f10 toggle_maximized',\n    )\n\nmap('Toggle macOS secure keyboard entry',\n    'toggle_macos_secure_keyboard_entry opt+cmd+s toggle_macos_secure_keyboard_entry',\n    only='macos',\n    )\n\nmap('macOS Cycle through OS Windows', 'macos_cycle_through_os_windows cmd+` macos_cycle_through_os_windows', only='macos')\nmap('macOS Cycle through OS Windows backwards', 'macos_cycle_through_os_windows_backwards cmd+shift+` macos_cycle_through_os_windows_backwards', only='macos')\n\nmap('Unicode input',\n    'input_unicode_character kitty_mod+u kitten unicode_input',\n    )\nmap('Unicode input',\n    'input_unicode_character ctrl+cmd+space kitten unicode_input',\n    only='macos',\n    )\n\nmap('Edit config file',\n    'edit_config_file kitty_mod+f2 edit_config_file',\n    )\nmap('Edit config file',\n    'edit_config_file cmd+, edit_config_file',\n    only='macos',\n    )\n\nmap('Open the kitty command shell',\n    'kitty_shell kitty_mod+escape kitty_shell window',\n    long_text='''\nOpen the kitty shell in a new :code:`window` / :code:`tab` / :code:`overlay` /\n:code:`os_window` to control kitty using commands.\n'''\n    )\n\nmap('Increase background opacity',\n    'increase_background_opacity kitty_mod+a>m set_background_opacity +0.1',\n    )\n\nmap('Decrease background opacity',\n    'decrease_background_opacity kitty_mod+a>l set_background_opacity -0.1',\n    )\n\nmap('Make background fully opaque',\n    'full_background_opacity kitty_mod+a>1 set_background_opacity 1',\n    )\n\nmap('Reset background opacity',\n    'reset_background_opacity kitty_mod+a>d set_background_opacity default',\n    )\n\nmap('Reset the terminal',\n    'reset_terminal kitty_mod+delete clear_terminal reset active',\n    long_text=r'''\nYou can create shortcuts to clear/reset the terminal. For example::\n\n    # Reset the terminal\n    map f1 clear_terminal reset active\n    # Clear the terminal screen by erasing all contents\n    map f1 clear_terminal clear active\n    # Clear the terminal scrollback by erasing it\n    map f1 clear_terminal scrollback active\n    # Scroll the contents of the screen into the scrollback\n    map f1 clear_terminal scroll active\n    # Clear everything on screen up to the line with the cursor or the start of the current prompt (needs shell integration)\n    map f1 clear_terminal to_cursor active\n    # Same as above except cleared lines are moved into scrollback\n    map f1 clear_terminal to_cursor_scroll active\n    # Erase the last command and its output (needs shell integration to work)\n    map f1 clear_terminal last_command active\n\nIf you want to operate on all kitty windows instead of just the current one, use\n:italic:`all` instead of :italic:`active`.\n\nSome useful functions that can be defined in the shell rc files to perform various kinds of\nclearing of the current window:\n\n.. code-block:: sh\n\n    clear-only-screen() {\n        printf \"\\e[H\\e[2J\"\n    }\n\n    clear-screen-and-scrollback() {\n        printf \"\\e[H\\e[3J\"\n    }\n\n    clear-screen-saving-contents-in-scrollback() {\n        printf \"\\e[H\\e[22J\"\n    }\n\nFor instance, using these escape codes, it is possible to remap :kbd:`Ctrl+L`\nto both scroll the current screen contents into the scrollback buffer and clear\nthe screen, instead of just clearing the screen. For ZSH, in :file:`~/.zshrc`, add:\n\n.. code-block:: zsh\n\n    ctrl_l() {\n        builtin print -rn -- $'\\r\\e[0J\\e[H\\e[22J' >\"$TTY\"\n        builtin zle .reset-prompt\n        builtin zle -R\n    }\n    zle -N ctrl_l\n    bindkey '^l' ctrl_l\n\nAlternatively, you can just add :code:`map ctrl+l clear_terminal to_cursor_scroll active` to :file:`kitty.conf` which\nworks with no changes to the shell rc files, but only clears up to the prompt, it does not clear any text at the prompt itself.\n'''\n    )\n\nmap('Reset the terminal',\n    'reset_terminal opt+cmd+r clear_terminal reset active',\n    only='macos',\n    )\n\nmap('Clear to start',\n    'clear_terminal_and_scrollback cmd+k clear_terminal to_cursor active',\n    only='macos',\n    )\n\nmap('Clear scrollback',\n    'clear_scrollback option+cmd+k clear_terminal scrollback active',\n    only='macos',\n    )\n\nmap('Clear the last command',\n    'clear_last_command cmd+l clear_terminal last_command active',\n    only='macos',\n    )\n\nmap('Clear screen',\n    'clear_screen cmd+ctrl+l clear_terminal to_cursor_scroll active',\n    only='macos',\n    )\n\n\n\nmap('Reload kitty.conf',\n    'reload_config_file kitty_mod+f5 load_config_file',\n    long_text='''\nReload :file:`kitty.conf`, applying any changes since the last time it was\nloaded. Note that a handful of options cannot be dynamically changed and\nrequire a full restart of kitty. Particularly, when changing shortcuts for\nactions located on the macOS global menu bar, a full restart is needed. You can\nalso map a keybinding to load a different config file, for example::\n\n    map f5 load_config /path/to/alternative/kitty.conf\n\nNote that all options from the original :file:`kitty.conf` are discarded, in\nother words the new configuration *replace* the old ones.\n'''\n    )\n\nmap('Reload kitty.conf',\n    'reload_config_file ctrl+cmd+, load_config_file',\n    only='macos'\n    )\n\nmap('Debug kitty configuration',\n    'debug_config kitty_mod+f6 debug_config',\n    long_text='''\nShow details about exactly what configuration kitty is running with and its host\nenvironment. Useful for debugging issues.\n'''\n    )\n\nmap('Debug kitty configuration',\n    'debug_config opt+cmd+, debug_config',\n    only='macos'\n    )\n\n\nmap('Send arbitrary text on key presses',\n    'send_text ctrl+shift+alt+h send_text all Hello World',\n    add_to_default=False,\n    long_text='''\nYou can tell kitty to send arbitrary (UTF-8) encoded text to the client program\nwhen pressing specified shortcut keys. For example::\n\n    map ctrl+alt+a send_text all Special text\n\nThis will send \"Special text\" when you press the :kbd:`Ctrl+Alt+A` key\ncombination. The text to be sent decodes :link:`ANSI C escapes <https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html>`\nso you can use escapes like :code:`\\\\\\\\e` to send control codes or :code:`\\\\\\\\u21fb` to send\nUnicode characters (or you can just input the Unicode characters directly as\nUTF-8 text). You can use ``kitten show-key`` to get the key escape\ncodes you want to emulate.\n\nThe first argument to :code:`send_text` is the keyboard modes in which to\nactivate the shortcut. The possible values are :code:`normal`,\n:code:`application`, :code:`kitty` or a comma separated combination of them.\nThe modes :code:`normal` and :code:`application` refer to the DECCKM\ncursor key mode for terminals, and :code:`kitty` refers to the kitty extended\nkeyboard protocol. The special value :code:`all` means all of them.\n\nSome more examples::\n\n    # Output a word and move the cursor to the start of the line (like typing and pressing Home)\n    map ctrl+alt+a send_text normal Word\\\\e[H\n    map ctrl+alt+a send_text application Word\\\\eOH\n    # Run a command at a shell prompt (like typing the command and pressing Enter)\n    map ctrl+alt+a send_text normal,application some command with arguments\\\\r\n'''\n    )\n\nmap('Open kitty Website',\n    f'open_kitty_website shift+cmd+/ open_url {website_url()}',\n    only='macos',\n    )\n\nmap('Hide macOS kitty application',\n    'hide_macos_app cmd+h hide_macos_app',\n    only='macos',\n    )\n\nmap('Hide macOS other applications',\n    'hide_macos_other_apps opt+cmd+h hide_macos_other_apps',\n    only='macos',\n    )\n\nmap('Minimize macOS window',\n    'minimize_macos_window cmd+m minimize_macos_window',\n    only='macos',\n    )\n\nmap('Quit kitty',\n    'quit cmd+q quit',\n    only='macos',\n    )\negr()  # }}}\negr()  # }}}\n"
  },
  {
    "path": "kitty/options/parse.py",
    "content": "# generated by gen-config.py DO NOT edit\n\n# isort: skip_file\nimport typing\nimport collections.abc  # noqa: F401, RUF100\nfrom kitty.conf.utils import (\n    merge_dicts, positive_float, positive_int, python_string, to_bool, to_cmdline, to_color,\n    to_color_or_none, unit_float\n)\nfrom kitty.options.utils import (\n    action_alias, active_tab_title_template, allow_hyperlinks, bell_on_tab, box_drawing_scale,\n    clear_all_mouse_actions, clear_all_shortcuts, clipboard_control, clone_source_strategies,\n    config_or_absolute_path, confirm_close, copy_on_select, cursor_blink_interval, cursor_text_color,\n    cursor_trail_decay, deprecated_adjust_line_height, deprecated_hide_window_decorations_aliases,\n    deprecated_macos_show_window_title_in_menubar_alias, deprecated_scrollback_indicator_opacity,\n    deprecated_send_text, disable_ligatures, edge_width, env, filter_notification, font_features,\n    hide_window_decorations, macos_option_as_alt, macos_titlebar_color, menu_map, modify_font,\n    mouse_hide_wait, narrow_symbols, notify_on_cmd_finish, optional_edge_width, parse_font_spec,\n    parse_map, parse_mouse_map, paste_actions, pointer_shape_when_dragging, remote_control_password,\n    resize_debounce_time, scrollback_lines, scrollback_pager_history_size, scrollbar_color,\n    shell_integration, store_multiple, symbol_map, tab_activity_symbol, tab_bar_edge,\n    tab_bar_margin_height, tab_bar_min_tabs, tab_fade, tab_font_style, tab_separator,\n    tab_title_template, text_fg_override_threshold, titlebar_color, to_cursor_shape,\n    to_cursor_unfocused_shape, to_font_size, to_layout_names, to_modifiers,\n    transparent_background_colors, underline_exclusion, url_prefixes, url_style, visual_bell_duration,\n    visual_window_select_characters, window_border_width, window_logo_scale, window_size\n)\n\n\nclass Parser:\n\n    def action_alias(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in action_alias(val):\n            ans[\"action_alias\"][k] = v\n\n    def active_border_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['active_border_color'] = to_color_or_none(val)\n\n    def active_tab_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['active_tab_background'] = to_color(val)\n\n    def active_tab_font_style(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['active_tab_font_style'] = tab_font_style(val)\n\n    def active_tab_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['active_tab_foreground'] = to_color(val)\n\n    def active_tab_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['active_tab_title_template'] = active_tab_title_template(val)\n\n    def active_window_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['active_window_title_template'] = tab_title_template(val)\n\n    def allow_cloning(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_allow_cloning:\n            raise ValueError(f\"The value {val} is not a valid choice for allow_cloning\")\n        ans[\"allow_cloning\"] = val\n\n    choices_for_allow_cloning = frozenset(('yes', 'y', 'true', 'no', 'n', 'false', 'ask'))\n\n    def allow_hyperlinks(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['allow_hyperlinks'] = allow_hyperlinks(val)\n\n    def allow_remote_control(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_allow_remote_control:\n            raise ValueError(f\"The value {val} is not a valid choice for allow_remote_control\")\n        ans[\"allow_remote_control\"] = val\n\n    choices_for_allow_remote_control = frozenset(('password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true'))\n\n    def background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['background'] = to_color(val)\n\n    def background_blur(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['background_blur'] = int(val)\n\n    def background_image(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['background_image'] = config_or_absolute_path(val)\n\n    def background_image_layout(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_background_image_layout:\n            raise ValueError(f\"The value {val} is not a valid choice for background_image_layout\")\n        ans[\"background_image_layout\"] = val\n\n    choices_for_background_image_layout = frozenset(('mirror-tiled', 'scaled', 'tiled', 'clamped', 'centered', 'cscaled'))\n\n    def background_image_linear(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['background_image_linear'] = to_bool(val)\n\n    def background_opacity(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['background_opacity'] = unit_float(val)\n\n    def background_tint(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['background_tint'] = unit_float(val)\n\n    def background_tint_gaps(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['background_tint_gaps'] = unit_float(val)\n\n    def bell_border_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['bell_border_color'] = to_color(val)\n\n    def bell_on_tab(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['bell_on_tab'] = bell_on_tab(val)\n\n    def bell_path(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['bell_path'] = config_or_absolute_path(val)\n\n    def bold_font(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['bold_font'] = parse_font_spec(val)\n\n    def bold_italic_font(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['bold_italic_font'] = parse_font_spec(val)\n\n    def box_drawing_scale(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['box_drawing_scale'] = box_drawing_scale(val)\n\n    def clear_all_mouse_actions(self, val: str, ans: dict[str, typing.Any]) -> None:\n        clear_all_mouse_actions(val, ans)\n\n    def clear_all_shortcuts(self, val: str, ans: dict[str, typing.Any]) -> None:\n        clear_all_shortcuts(val, ans)\n\n    def clear_selection_on_clipboard_loss(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['clear_selection_on_clipboard_loss'] = to_bool(val)\n\n    def click_interval(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['click_interval'] = float(val)\n\n    def clipboard_control(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['clipboard_control'] = clipboard_control(val)\n\n    def clipboard_max_size(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['clipboard_max_size'] = positive_float(val)\n\n    def clone_source_strategies(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['clone_source_strategies'] = clone_source_strategies(val)\n\n    def close_on_child_death(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['close_on_child_death'] = to_bool(val)\n\n    def color0(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color0'] = to_color(val)\n\n    def color1(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color1'] = to_color(val)\n\n    def color2(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color2'] = to_color(val)\n\n    def color3(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color3'] = to_color(val)\n\n    def color4(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color4'] = to_color(val)\n\n    def color5(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color5'] = to_color(val)\n\n    def color6(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color6'] = to_color(val)\n\n    def color7(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color7'] = to_color(val)\n\n    def color8(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color8'] = to_color(val)\n\n    def color9(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color9'] = to_color(val)\n\n    def color10(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color10'] = to_color(val)\n\n    def color11(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color11'] = to_color(val)\n\n    def color12(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color12'] = to_color(val)\n\n    def color13(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color13'] = to_color(val)\n\n    def color14(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color14'] = to_color(val)\n\n    def color15(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color15'] = to_color(val)\n\n    def color16(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color16'] = to_color(val)\n\n    def color17(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color17'] = to_color(val)\n\n    def color18(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color18'] = to_color(val)\n\n    def color19(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color19'] = to_color(val)\n\n    def color20(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color20'] = to_color(val)\n\n    def color21(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color21'] = to_color(val)\n\n    def color22(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color22'] = to_color(val)\n\n    def color23(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color23'] = to_color(val)\n\n    def color24(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color24'] = to_color(val)\n\n    def color25(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color25'] = to_color(val)\n\n    def color26(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color26'] = to_color(val)\n\n    def color27(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color27'] = to_color(val)\n\n    def color28(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color28'] = to_color(val)\n\n    def color29(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color29'] = to_color(val)\n\n    def color30(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color30'] = to_color(val)\n\n    def color31(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color31'] = to_color(val)\n\n    def color32(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color32'] = to_color(val)\n\n    def color33(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color33'] = to_color(val)\n\n    def color34(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color34'] = to_color(val)\n\n    def color35(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color35'] = to_color(val)\n\n    def color36(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color36'] = to_color(val)\n\n    def color37(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color37'] = to_color(val)\n\n    def color38(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color38'] = to_color(val)\n\n    def color39(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color39'] = to_color(val)\n\n    def color40(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color40'] = to_color(val)\n\n    def color41(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color41'] = to_color(val)\n\n    def color42(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color42'] = to_color(val)\n\n    def color43(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color43'] = to_color(val)\n\n    def color44(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color44'] = to_color(val)\n\n    def color45(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color45'] = to_color(val)\n\n    def color46(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color46'] = to_color(val)\n\n    def color47(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color47'] = to_color(val)\n\n    def color48(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color48'] = to_color(val)\n\n    def color49(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color49'] = to_color(val)\n\n    def color50(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color50'] = to_color(val)\n\n    def color51(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color51'] = to_color(val)\n\n    def color52(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color52'] = to_color(val)\n\n    def color53(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color53'] = to_color(val)\n\n    def color54(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color54'] = to_color(val)\n\n    def color55(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color55'] = to_color(val)\n\n    def color56(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color56'] = to_color(val)\n\n    def color57(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color57'] = to_color(val)\n\n    def color58(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color58'] = to_color(val)\n\n    def color59(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color59'] = to_color(val)\n\n    def color60(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color60'] = to_color(val)\n\n    def color61(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color61'] = to_color(val)\n\n    def color62(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color62'] = to_color(val)\n\n    def color63(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color63'] = to_color(val)\n\n    def color64(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color64'] = to_color(val)\n\n    def color65(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color65'] = to_color(val)\n\n    def color66(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color66'] = to_color(val)\n\n    def color67(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color67'] = to_color(val)\n\n    def color68(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color68'] = to_color(val)\n\n    def color69(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color69'] = to_color(val)\n\n    def color70(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color70'] = to_color(val)\n\n    def color71(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color71'] = to_color(val)\n\n    def color72(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color72'] = to_color(val)\n\n    def color73(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color73'] = to_color(val)\n\n    def color74(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color74'] = to_color(val)\n\n    def color75(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color75'] = to_color(val)\n\n    def color76(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color76'] = to_color(val)\n\n    def color77(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color77'] = to_color(val)\n\n    def color78(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color78'] = to_color(val)\n\n    def color79(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color79'] = to_color(val)\n\n    def color80(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color80'] = to_color(val)\n\n    def color81(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color81'] = to_color(val)\n\n    def color82(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color82'] = to_color(val)\n\n    def color83(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color83'] = to_color(val)\n\n    def color84(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color84'] = to_color(val)\n\n    def color85(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color85'] = to_color(val)\n\n    def color86(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color86'] = to_color(val)\n\n    def color87(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color87'] = to_color(val)\n\n    def color88(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color88'] = to_color(val)\n\n    def color89(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color89'] = to_color(val)\n\n    def color90(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color90'] = to_color(val)\n\n    def color91(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color91'] = to_color(val)\n\n    def color92(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color92'] = to_color(val)\n\n    def color93(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color93'] = to_color(val)\n\n    def color94(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color94'] = to_color(val)\n\n    def color95(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color95'] = to_color(val)\n\n    def color96(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color96'] = to_color(val)\n\n    def color97(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color97'] = to_color(val)\n\n    def color98(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color98'] = to_color(val)\n\n    def color99(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color99'] = to_color(val)\n\n    def color100(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color100'] = to_color(val)\n\n    def color101(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color101'] = to_color(val)\n\n    def color102(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color102'] = to_color(val)\n\n    def color103(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color103'] = to_color(val)\n\n    def color104(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color104'] = to_color(val)\n\n    def color105(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color105'] = to_color(val)\n\n    def color106(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color106'] = to_color(val)\n\n    def color107(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color107'] = to_color(val)\n\n    def color108(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color108'] = to_color(val)\n\n    def color109(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color109'] = to_color(val)\n\n    def color110(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color110'] = to_color(val)\n\n    def color111(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color111'] = to_color(val)\n\n    def color112(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color112'] = to_color(val)\n\n    def color113(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color113'] = to_color(val)\n\n    def color114(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color114'] = to_color(val)\n\n    def color115(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color115'] = to_color(val)\n\n    def color116(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color116'] = to_color(val)\n\n    def color117(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color117'] = to_color(val)\n\n    def color118(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color118'] = to_color(val)\n\n    def color119(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color119'] = to_color(val)\n\n    def color120(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color120'] = to_color(val)\n\n    def color121(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color121'] = to_color(val)\n\n    def color122(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color122'] = to_color(val)\n\n    def color123(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color123'] = to_color(val)\n\n    def color124(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color124'] = to_color(val)\n\n    def color125(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color125'] = to_color(val)\n\n    def color126(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color126'] = to_color(val)\n\n    def color127(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color127'] = to_color(val)\n\n    def color128(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color128'] = to_color(val)\n\n    def color129(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color129'] = to_color(val)\n\n    def color130(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color130'] = to_color(val)\n\n    def color131(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color131'] = to_color(val)\n\n    def color132(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color132'] = to_color(val)\n\n    def color133(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color133'] = to_color(val)\n\n    def color134(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color134'] = to_color(val)\n\n    def color135(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color135'] = to_color(val)\n\n    def color136(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color136'] = to_color(val)\n\n    def color137(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color137'] = to_color(val)\n\n    def color138(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color138'] = to_color(val)\n\n    def color139(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color139'] = to_color(val)\n\n    def color140(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color140'] = to_color(val)\n\n    def color141(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color141'] = to_color(val)\n\n    def color142(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color142'] = to_color(val)\n\n    def color143(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color143'] = to_color(val)\n\n    def color144(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color144'] = to_color(val)\n\n    def color145(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color145'] = to_color(val)\n\n    def color146(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color146'] = to_color(val)\n\n    def color147(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color147'] = to_color(val)\n\n    def color148(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color148'] = to_color(val)\n\n    def color149(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color149'] = to_color(val)\n\n    def color150(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color150'] = to_color(val)\n\n    def color151(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color151'] = to_color(val)\n\n    def color152(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color152'] = to_color(val)\n\n    def color153(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color153'] = to_color(val)\n\n    def color154(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color154'] = to_color(val)\n\n    def color155(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color155'] = to_color(val)\n\n    def color156(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color156'] = to_color(val)\n\n    def color157(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color157'] = to_color(val)\n\n    def color158(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color158'] = to_color(val)\n\n    def color159(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color159'] = to_color(val)\n\n    def color160(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color160'] = to_color(val)\n\n    def color161(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color161'] = to_color(val)\n\n    def color162(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color162'] = to_color(val)\n\n    def color163(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color163'] = to_color(val)\n\n    def color164(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color164'] = to_color(val)\n\n    def color165(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color165'] = to_color(val)\n\n    def color166(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color166'] = to_color(val)\n\n    def color167(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color167'] = to_color(val)\n\n    def color168(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color168'] = to_color(val)\n\n    def color169(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color169'] = to_color(val)\n\n    def color170(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color170'] = to_color(val)\n\n    def color171(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color171'] = to_color(val)\n\n    def color172(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color172'] = to_color(val)\n\n    def color173(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color173'] = to_color(val)\n\n    def color174(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color174'] = to_color(val)\n\n    def color175(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color175'] = to_color(val)\n\n    def color176(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color176'] = to_color(val)\n\n    def color177(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color177'] = to_color(val)\n\n    def color178(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color178'] = to_color(val)\n\n    def color179(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color179'] = to_color(val)\n\n    def color180(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color180'] = to_color(val)\n\n    def color181(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color181'] = to_color(val)\n\n    def color182(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color182'] = to_color(val)\n\n    def color183(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color183'] = to_color(val)\n\n    def color184(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color184'] = to_color(val)\n\n    def color185(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color185'] = to_color(val)\n\n    def color186(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color186'] = to_color(val)\n\n    def color187(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color187'] = to_color(val)\n\n    def color188(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color188'] = to_color(val)\n\n    def color189(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color189'] = to_color(val)\n\n    def color190(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color190'] = to_color(val)\n\n    def color191(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color191'] = to_color(val)\n\n    def color192(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color192'] = to_color(val)\n\n    def color193(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color193'] = to_color(val)\n\n    def color194(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color194'] = to_color(val)\n\n    def color195(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color195'] = to_color(val)\n\n    def color196(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color196'] = to_color(val)\n\n    def color197(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color197'] = to_color(val)\n\n    def color198(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color198'] = to_color(val)\n\n    def color199(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color199'] = to_color(val)\n\n    def color200(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color200'] = to_color(val)\n\n    def color201(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color201'] = to_color(val)\n\n    def color202(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color202'] = to_color(val)\n\n    def color203(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color203'] = to_color(val)\n\n    def color204(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color204'] = to_color(val)\n\n    def color205(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color205'] = to_color(val)\n\n    def color206(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color206'] = to_color(val)\n\n    def color207(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color207'] = to_color(val)\n\n    def color208(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color208'] = to_color(val)\n\n    def color209(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color209'] = to_color(val)\n\n    def color210(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color210'] = to_color(val)\n\n    def color211(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color211'] = to_color(val)\n\n    def color212(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color212'] = to_color(val)\n\n    def color213(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color213'] = to_color(val)\n\n    def color214(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color214'] = to_color(val)\n\n    def color215(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color215'] = to_color(val)\n\n    def color216(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color216'] = to_color(val)\n\n    def color217(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color217'] = to_color(val)\n\n    def color218(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color218'] = to_color(val)\n\n    def color219(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color219'] = to_color(val)\n\n    def color220(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color220'] = to_color(val)\n\n    def color221(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color221'] = to_color(val)\n\n    def color222(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color222'] = to_color(val)\n\n    def color223(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color223'] = to_color(val)\n\n    def color224(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color224'] = to_color(val)\n\n    def color225(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color225'] = to_color(val)\n\n    def color226(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color226'] = to_color(val)\n\n    def color227(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color227'] = to_color(val)\n\n    def color228(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color228'] = to_color(val)\n\n    def color229(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color229'] = to_color(val)\n\n    def color230(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color230'] = to_color(val)\n\n    def color231(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color231'] = to_color(val)\n\n    def color232(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color232'] = to_color(val)\n\n    def color233(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color233'] = to_color(val)\n\n    def color234(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color234'] = to_color(val)\n\n    def color235(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color235'] = to_color(val)\n\n    def color236(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color236'] = to_color(val)\n\n    def color237(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color237'] = to_color(val)\n\n    def color238(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color238'] = to_color(val)\n\n    def color239(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color239'] = to_color(val)\n\n    def color240(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color240'] = to_color(val)\n\n    def color241(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color241'] = to_color(val)\n\n    def color242(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color242'] = to_color(val)\n\n    def color243(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color243'] = to_color(val)\n\n    def color244(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color244'] = to_color(val)\n\n    def color245(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color245'] = to_color(val)\n\n    def color246(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color246'] = to_color(val)\n\n    def color247(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color247'] = to_color(val)\n\n    def color248(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color248'] = to_color(val)\n\n    def color249(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color249'] = to_color(val)\n\n    def color250(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color250'] = to_color(val)\n\n    def color251(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color251'] = to_color(val)\n\n    def color252(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color252'] = to_color(val)\n\n    def color253(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color253'] = to_color(val)\n\n    def color254(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color254'] = to_color(val)\n\n    def color255(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['color255'] = to_color(val)\n\n    def command_on_bell(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['command_on_bell'] = to_cmdline(val)\n\n    def confirm_os_window_close(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['confirm_os_window_close'] = confirm_close(val)\n\n    def copy_on_select(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['copy_on_select'] = copy_on_select(val)\n\n    def cursor(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor'] = to_color_or_none(val)\n\n    def cursor_beam_thickness(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_beam_thickness'] = positive_float(val)\n\n    def cursor_blink_interval(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_blink_interval'] = cursor_blink_interval(val)\n\n    def cursor_shape(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_shape'] = to_cursor_shape(val)\n\n    def cursor_shape_unfocused(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_shape_unfocused'] = to_cursor_unfocused_shape(val)\n\n    def cursor_stop_blinking_after(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_stop_blinking_after'] = positive_float(val)\n\n    def cursor_text_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_text_color'] = cursor_text_color(val)\n\n    def cursor_trail(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_trail'] = positive_int(val)\n\n    def cursor_trail_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_trail_color'] = to_color_or_none(val)\n\n    def cursor_trail_decay(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_trail_decay'] = cursor_trail_decay(val)\n\n    def cursor_trail_start_threshold(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_trail_start_threshold'] = positive_int(val)\n\n    def cursor_underline_thickness(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['cursor_underline_thickness'] = positive_float(val)\n\n    def default_pointer_shape(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_default_pointer_shape:\n            raise ValueError(f\"The value {val} is not a valid choice for default_pointer_shape\")\n        ans[\"default_pointer_shape\"] = val\n\n    choices_for_default_pointer_shape = frozenset(('arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'cell', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing'))\n\n    def detect_urls(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['detect_urls'] = to_bool(val)\n\n    def dim_opacity(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['dim_opacity'] = unit_float(val)\n\n    def disable_ligatures(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['disable_ligatures'] = disable_ligatures(val)\n\n    def draw_minimal_borders(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['draw_minimal_borders'] = to_bool(val)\n\n    def draw_window_borders_for_single_window(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['draw_window_borders_for_single_window'] = to_bool(val)\n\n    def dynamic_background_opacity(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['dynamic_background_opacity'] = to_bool(val)\n\n    def editor(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['editor'] = str(val)\n\n    def enable_audio_bell(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['enable_audio_bell'] = to_bool(val)\n\n    def enabled_layouts(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['enabled_layouts'] = to_layout_names(val)\n\n    def env(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in env(val, ans[\"env\"]):\n            ans[\"env\"][k] = v\n\n    def exe_search_path(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in store_multiple(val, ans[\"exe_search_path\"]):\n            ans[\"exe_search_path\"][k] = v\n\n    def file_transfer_confirmation_bypass(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['file_transfer_confirmation_bypass'] = str(val)\n\n    def filter_notification(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in filter_notification(val, ans[\"filter_notification\"]):\n            ans[\"filter_notification\"][k] = v\n\n    def focus_follows_mouse(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['focus_follows_mouse'] = to_bool(val)\n\n    def font_family(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['font_family'] = parse_font_spec(val)\n\n    def font_features(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in font_features(val):\n            ans[\"font_features\"][k] = v\n\n    def font_size(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['font_size'] = to_font_size(val)\n\n    def force_ltr(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['force_ltr'] = to_bool(val)\n\n    def foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['foreground'] = to_color(val)\n\n    def forward_stdio(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['forward_stdio'] = to_bool(val)\n\n    def hide_window_decorations(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['hide_window_decorations'] = hide_window_decorations(val)\n\n    def inactive_border_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['inactive_border_color'] = to_color(val)\n\n    def inactive_tab_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['inactive_tab_background'] = to_color(val)\n\n    def inactive_tab_font_style(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['inactive_tab_font_style'] = tab_font_style(val)\n\n    def inactive_tab_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['inactive_tab_foreground'] = to_color(val)\n\n    def inactive_text_alpha(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['inactive_text_alpha'] = unit_float(val)\n\n    def initial_window_height(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['initial_window_height'] = window_size(val)\n\n    def initial_window_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['initial_window_width'] = window_size(val)\n\n    def input_delay(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['input_delay'] = positive_int(val)\n\n    def italic_font(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['italic_font'] = parse_font_spec(val)\n\n    def kitten_alias(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in action_alias(val):\n            ans[\"kitten_alias\"][k] = v\n\n    def kitty_mod(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['kitty_mod'] = to_modifiers(val)\n\n    def linux_bell_theme(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['linux_bell_theme'] = str(val)\n\n    def linux_display_server(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_linux_display_server:\n            raise ValueError(f\"The value {val} is not a valid choice for linux_display_server\")\n        ans[\"linux_display_server\"] = val\n\n    choices_for_linux_display_server = frozenset(('auto', 'wayland', 'x11'))\n\n    def listen_on(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['listen_on'] = str(val)\n\n    def macos_colorspace(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_macos_colorspace:\n            raise ValueError(f\"The value {val} is not a valid choice for macos_colorspace\")\n        ans[\"macos_colorspace\"] = val\n\n    choices_for_macos_colorspace = frozenset(('srgb', 'default', 'displayp3'))\n\n    def macos_custom_beam_cursor(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_custom_beam_cursor'] = to_bool(val)\n\n    def macos_dock_badge_on_bell(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_dock_badge_on_bell'] = to_bool(val)\n\n    def macos_hide_from_tasks(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_hide_from_tasks'] = to_bool(val)\n\n    def macos_menubar_title_max_length(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_menubar_title_max_length'] = positive_int(val)\n\n    def macos_option_as_alt(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_option_as_alt'] = macos_option_as_alt(val)\n\n    def macos_quit_when_last_window_closed(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_quit_when_last_window_closed'] = to_bool(val)\n\n    def macos_show_window_title_in(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_macos_show_window_title_in:\n            raise ValueError(f\"The value {val} is not a valid choice for macos_show_window_title_in\")\n        ans[\"macos_show_window_title_in\"] = val\n\n    choices_for_macos_show_window_title_in = frozenset(('all', 'menubar', 'none', 'window'))\n\n    def macos_thicken_font(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_thicken_font'] = positive_float(val)\n\n    def macos_titlebar_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_titlebar_color'] = macos_titlebar_color(val)\n\n    def macos_traditional_fullscreen(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_traditional_fullscreen'] = to_bool(val)\n\n    def macos_window_resizable(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['macos_window_resizable'] = to_bool(val)\n\n    def map_timeout(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['map_timeout'] = positive_float(val)\n\n    def mark1_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['mark1_background'] = to_color(val)\n\n    def mark1_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['mark1_foreground'] = to_color(val)\n\n    def mark2_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['mark2_background'] = to_color(val)\n\n    def mark2_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['mark2_foreground'] = to_color(val)\n\n    def mark3_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['mark3_background'] = to_color(val)\n\n    def mark3_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['mark3_foreground'] = to_color(val)\n\n    def menu_map(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in menu_map(val, ans[\"menu_map\"]):\n            ans[\"menu_map\"][k] = v\n\n    def modify_font(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in modify_font(val):\n            ans[\"modify_font\"][k] = v\n\n    def momentum_scroll(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['momentum_scroll'] = unit_float(val)\n\n    def mouse_hide_wait(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['mouse_hide_wait'] = mouse_hide_wait(val)\n\n    def narrow_symbols(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in narrow_symbols(val):\n            ans[\"narrow_symbols\"][k] = v\n\n    def notify_on_cmd_finish(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['notify_on_cmd_finish'] = notify_on_cmd_finish(val)\n\n    def open_url_with(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['open_url_with'] = to_cmdline(val)\n\n    def paste_actions(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['paste_actions'] = paste_actions(val)\n\n    def pixel_scroll(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['pixel_scroll'] = to_bool(val)\n\n    def placement_strategy(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_placement_strategy:\n            raise ValueError(f\"The value {val} is not a valid choice for placement_strategy\")\n        ans[\"placement_strategy\"] = val\n\n    choices_for_placement_strategy = frozenset(('top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'))\n\n    def pointer_shape_when_dragging(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['pointer_shape_when_dragging'] = pointer_shape_when_dragging(val)\n\n    def pointer_shape_when_grabbed(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_pointer_shape_when_grabbed:\n            raise ValueError(f\"The value {val} is not a valid choice for pointer_shape_when_grabbed\")\n        ans[\"pointer_shape_when_grabbed\"] = val\n\n    choices_for_pointer_shape_when_grabbed = choices_for_default_pointer_shape\n\n    def remember_window_position(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['remember_window_position'] = to_bool(val)\n\n    def remember_window_size(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['remember_window_size'] = to_bool(val)\n\n    def remote_control_password(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in remote_control_password(val, ans[\"remote_control_password\"]):\n            ans[\"remote_control_password\"][k] = v\n\n    def repaint_delay(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['repaint_delay'] = positive_int(val)\n\n    def resize_debounce_time(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['resize_debounce_time'] = resize_debounce_time(val)\n\n    def resize_in_steps(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['resize_in_steps'] = to_bool(val)\n\n    def scrollback_fill_enlarged_window(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollback_fill_enlarged_window'] = to_bool(val)\n\n    def scrollback_lines(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollback_lines'] = scrollback_lines(val)\n\n    def scrollback_pager(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollback_pager'] = to_cmdline(val)\n\n    def scrollback_pager_history_size(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollback_pager_history_size'] = scrollback_pager_history_size(val)\n\n    def scrollbar(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_scrollbar:\n            raise ValueError(f\"The value {val} is not a valid choice for scrollbar\")\n        ans[\"scrollbar\"] = val\n\n    choices_for_scrollbar = frozenset(('scrolled', 'always', 'never', 'hovered', 'scrolled-and-hovered'))\n\n    def scrollbar_gap(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_gap'] = positive_float(val)\n\n    def scrollbar_handle_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_handle_color'] = scrollbar_color(val)\n\n    def scrollbar_handle_opacity(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_handle_opacity'] = positive_float(val)\n\n    def scrollbar_hitbox_expansion(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_hitbox_expansion'] = positive_float(val)\n\n    def scrollbar_hover_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_hover_width'] = positive_float(val)\n\n    def scrollbar_interactive(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_interactive'] = to_bool(val)\n\n    def scrollbar_jump_on_click(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_jump_on_click'] = to_bool(val)\n\n    def scrollbar_min_handle_height(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_min_handle_height'] = positive_float(val)\n\n    def scrollbar_radius(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_radius'] = positive_float(val)\n\n    def scrollbar_track_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_track_color'] = scrollbar_color(val)\n\n    def scrollbar_track_hover_opacity(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_track_hover_opacity'] = positive_float(val)\n\n    def scrollbar_track_opacity(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_track_opacity'] = positive_float(val)\n\n    def scrollbar_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['scrollbar_width'] = positive_float(val)\n\n    def select_by_word_characters(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['select_by_word_characters'] = str(val)\n\n    def select_by_word_characters_forward(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['select_by_word_characters_forward'] = str(val)\n\n    def selection_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['selection_background'] = to_color_or_none(val)\n\n    def selection_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['selection_foreground'] = to_color_or_none(val)\n\n    def shell(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['shell'] = str(val)\n\n    def shell_integration(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['shell_integration'] = shell_integration(val)\n\n    def show_hyperlink_targets(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['show_hyperlink_targets'] = to_bool(val)\n\n    def single_window_margin_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['single_window_margin_width'] = optional_edge_width(val)\n\n    def single_window_padding_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['single_window_padding_width'] = optional_edge_width(val)\n\n    def startup_session(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['startup_session'] = config_or_absolute_path(val)\n\n    def strip_trailing_spaces(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_strip_trailing_spaces:\n            raise ValueError(f\"The value {val} is not a valid choice for strip_trailing_spaces\")\n        ans[\"strip_trailing_spaces\"] = val\n\n    choices_for_strip_trailing_spaces = frozenset(('always', 'never', 'smart'))\n\n    def symbol_map(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in symbol_map(val):\n            ans[\"symbol_map\"][k] = v\n\n    def sync_to_monitor(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['sync_to_monitor'] = to_bool(val)\n\n    def tab_activity_symbol(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_activity_symbol'] = tab_activity_symbol(val)\n\n    def tab_bar_align(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_tab_bar_align:\n            raise ValueError(f\"The value {val} is not a valid choice for tab_bar_align\")\n        ans[\"tab_bar_align\"] = val\n\n    choices_for_tab_bar_align = frozenset(('left', 'center', 'right'))\n\n    def tab_bar_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_bar_background'] = to_color_or_none(val)\n\n    def tab_bar_drag_threshold(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_bar_drag_threshold'] = positive_int(val)\n\n    def tab_bar_edge(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_bar_edge'] = tab_bar_edge(val)\n\n    def tab_bar_filter(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_bar_filter'] = str(val)\n\n    def tab_bar_margin_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_bar_margin_color'] = to_color_or_none(val)\n\n    def tab_bar_margin_height(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_bar_margin_height'] = tab_bar_margin_height(val)\n\n    def tab_bar_margin_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_bar_margin_width'] = positive_float(val)\n\n    def tab_bar_min_tabs(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_bar_min_tabs'] = tab_bar_min_tabs(val)\n\n    def tab_bar_style(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_tab_bar_style:\n            raise ValueError(f\"The value {val} is not a valid choice for tab_bar_style\")\n        ans[\"tab_bar_style\"] = val\n\n    choices_for_tab_bar_style = frozenset(('fade', 'hidden', 'powerline', 'separator', 'slant', 'custom'))\n\n    def tab_fade(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_fade'] = tab_fade(val)\n\n    def tab_powerline_style(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_tab_powerline_style:\n            raise ValueError(f\"The value {val} is not a valid choice for tab_powerline_style\")\n        ans[\"tab_powerline_style\"] = val\n\n    choices_for_tab_powerline_style = frozenset(('angled', 'round', 'slanted'))\n\n    def tab_separator(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_separator'] = tab_separator(val)\n\n    def tab_switch_strategy(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_tab_switch_strategy:\n            raise ValueError(f\"The value {val} is not a valid choice for tab_switch_strategy\")\n        ans[\"tab_switch_strategy\"] = val\n\n    choices_for_tab_switch_strategy = frozenset(('last', 'left', 'previous', 'right'))\n\n    def tab_title_max_length(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_title_max_length'] = positive_int(val)\n\n    def tab_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['tab_title_template'] = tab_title_template(val)\n\n    def term(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['term'] = str(val)\n\n    def terminfo_type(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_terminfo_type:\n            raise ValueError(f\"The value {val} is not a valid choice for terminfo_type\")\n        ans[\"terminfo_type\"] = val\n\n    choices_for_terminfo_type = frozenset(('path', 'direct', 'none'))\n\n    def text_composition_strategy(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['text_composition_strategy'] = str(val)\n\n    def text_fg_override_threshold(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['text_fg_override_threshold'] = text_fg_override_threshold(val)\n\n    def touch_scroll_multiplier(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['touch_scroll_multiplier'] = float(val)\n\n    def transparent_background_colors(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['transparent_background_colors'] = transparent_background_colors(val)\n\n    def undercurl_style(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_undercurl_style:\n            raise ValueError(f\"The value {val} is not a valid choice for undercurl_style\")\n        ans[\"undercurl_style\"] = val\n\n    choices_for_undercurl_style = frozenset(('thin-sparse', 'thin-dense', 'thick-sparse', 'thick-dense'))\n\n    def underline_exclusion(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['underline_exclusion'] = underline_exclusion(val)\n\n    def underline_hyperlinks(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_underline_hyperlinks:\n            raise ValueError(f\"The value {val} is not a valid choice for underline_hyperlinks\")\n        ans[\"underline_hyperlinks\"] = val\n\n    choices_for_underline_hyperlinks = frozenset(('hover', 'always', 'never'))\n\n    def update_check_interval(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['update_check_interval'] = float(val)\n\n    def url_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['url_color'] = to_color(val)\n\n    def url_excluded_characters(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['url_excluded_characters'] = python_string(val)\n\n    def url_prefixes(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['url_prefixes'] = url_prefixes(val)\n\n    def url_style(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['url_style'] = url_style(val)\n\n    def visual_bell_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['visual_bell_color'] = to_color_or_none(val)\n\n    def visual_bell_duration(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['visual_bell_duration'] = visual_bell_duration(val)\n\n    def visual_window_select_characters(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['visual_window_select_characters'] = visual_window_select_characters(val)\n\n    def watcher(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k, v in store_multiple(val, ans[\"watcher\"]):\n            ans[\"watcher\"][k] = v\n\n    def wayland_enable_ime(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['wayland_enable_ime'] = to_bool(val)\n\n    def wayland_titlebar_color(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['wayland_titlebar_color'] = titlebar_color(val)\n\n    def wheel_scroll_min_lines(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['wheel_scroll_min_lines'] = int(val)\n\n    def wheel_scroll_multiplier(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['wheel_scroll_multiplier'] = float(val)\n\n    def window_alert_on_bell(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_alert_on_bell'] = to_bool(val)\n\n    def window_border_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_border_width'] = window_border_width(val)\n\n    def window_drag_tolerance(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_drag_tolerance'] = float(val)\n\n    def window_logo_alpha(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_logo_alpha'] = unit_float(val)\n\n    def window_logo_path(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_logo_path'] = config_or_absolute_path(val)\n\n    def window_logo_position(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_window_logo_position:\n            raise ValueError(f\"The value {val} is not a valid choice for window_logo_position\")\n        ans[\"window_logo_position\"] = val\n\n    choices_for_window_logo_position = choices_for_placement_strategy\n\n    def window_logo_scale(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_logo_scale'] = window_logo_scale(val)\n\n    def window_margin_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_margin_width'] = edge_width(val)\n\n    def window_padding_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_padding_width'] = edge_width(val)\n\n    def window_resize_step_cells(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_resize_step_cells'] = positive_int(val)\n\n    def window_resize_step_lines(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_resize_step_lines'] = positive_int(val)\n\n    def window_title_bar(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_window_title_bar:\n            raise ValueError(f\"The value {val} is not a valid choice for window_title_bar\")\n        ans[\"window_title_bar\"] = val\n\n    choices_for_window_title_bar = frozenset(('top', 'bottom'))\n\n    def window_title_bar_active_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_title_bar_active_background'] = to_color_or_none(val)\n\n    def window_title_bar_active_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_title_bar_active_foreground'] = to_color_or_none(val)\n\n    def window_title_bar_align(self, val: str, ans: dict[str, typing.Any]) -> None:\n        val = val.lower()\n        if val not in self.choices_for_window_title_bar_align:\n            raise ValueError(f\"The value {val} is not a valid choice for window_title_bar_align\")\n        ans[\"window_title_bar_align\"] = val\n\n    choices_for_window_title_bar_align = choices_for_tab_bar_align\n\n    def window_title_bar_inactive_background(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_title_bar_inactive_background'] = to_color_or_none(val)\n\n    def window_title_bar_inactive_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_title_bar_inactive_foreground'] = to_color_or_none(val)\n\n    def window_title_bar_min_windows(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_title_bar_min_windows'] = positive_int(val)\n\n    def window_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:\n        ans['window_title_template'] = tab_title_template(val)\n\n    def x11_hide_window_decorations(self, val: str, ans: dict[str, typing.Any]) -> None:\n        deprecated_hide_window_decorations_aliases('x11_hide_window_decorations', val, ans)\n\n    def macos_hide_titlebar(self, val: str, ans: dict[str, typing.Any]) -> None:\n        deprecated_hide_window_decorations_aliases('macos_hide_titlebar', val, ans)\n\n    def macos_show_window_title_in_menubar(self, val: str, ans: dict[str, typing.Any]) -> None:\n        deprecated_macos_show_window_title_in_menubar_alias('macos_show_window_title_in_menubar', val, ans)\n\n    def send_text(self, val: str, ans: dict[str, typing.Any]) -> None:\n        deprecated_send_text('send_text', val, ans)\n\n    def adjust_line_height(self, val: str, ans: dict[str, typing.Any]) -> None:\n        deprecated_adjust_line_height('adjust_line_height', val, ans)\n\n    def adjust_column_width(self, val: str, ans: dict[str, typing.Any]) -> None:\n        deprecated_adjust_line_height('adjust_column_width', val, ans)\n\n    def adjust_baseline(self, val: str, ans: dict[str, typing.Any]) -> None:\n        deprecated_adjust_line_height('adjust_baseline', val, ans)\n\n    def scrollback_indicator_opacity(self, val: str, ans: dict[str, typing.Any]) -> None:\n        deprecated_scrollback_indicator_opacity('scrollback_indicator_opacity', val, ans)\n\n    def map(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k in parse_map(val):\n            ans['map'].append(k)\n\n    def mouse_map(self, val: str, ans: dict[str, typing.Any]) -> None:\n        for k in parse_mouse_map(val):\n            ans['mouse_map'].append(k)\n\n\ndef create_result_dict() -> dict[str, typing.Any]:\n    return {\n        'action_alias': {},\n        'env': {},\n        'exe_search_path': {},\n        'filter_notification': {},\n        'font_features': {},\n        'kitten_alias': {},\n        'menu_map': {},\n        'modify_font': {},\n        'narrow_symbols': {},\n        'remote_control_password': {},\n        'symbol_map': {},\n        'watcher': {},\n        'map': [],\n        'mouse_map': [],\n    }\n\n\nactions: frozenset[str] = frozenset(('map', 'mouse_map'))\n\n\ndef merge_result_dicts(defaults: dict[str, typing.Any], vals: dict[str, typing.Any]) -> dict[str, typing.Any]:\n    ans = {}\n    for k, v in defaults.items():\n        if isinstance(v, dict):\n            ans[k] = merge_dicts(v, vals.get(k, {}))\n        elif k in actions:\n            ans[k] = v + vals.get(k, [])\n        else:\n            ans[k] = vals.get(k, v)\n    return ans\n\n\nparser = Parser()\n\n\ndef parse_conf_item(key: str, val: str, ans: dict[str, typing.Any]) -> bool:\n    func = getattr(parser, key, None)\n    if func is not None:\n        func(val, ans)\n        return True\n    return False\n"
  },
  {
    "path": "kitty/options/to-c-generated.h",
    "content": "// generated by gen-config.py DO NOT edit\n// vim:fileencoding=utf-8\n#pragma once\n#include \"to-c.h\"\n\n\n\nstatic void\nconvert_from_python_font_size(PyObject *val, Options *opts) {\n    opts->font_size = PyFloat_AsDouble(val);\n}\n\nstatic void\nconvert_from_opts_font_size(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"font_size\");\n    if (ret == NULL) return;\n    convert_from_python_font_size(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_force_ltr(PyObject *val, Options *opts) {\n    opts->force_ltr = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_force_ltr(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"force_ltr\");\n    if (ret == NULL) return;\n    convert_from_python_force_ltr(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_disable_ligatures(PyObject *val, Options *opts) {\n    opts->disable_ligatures = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_disable_ligatures(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"disable_ligatures\");\n    if (ret == NULL) return;\n    convert_from_python_disable_ligatures(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_font_features(PyObject *val, Options *opts) {\n    font_features(val, opts);\n}\n\nstatic void\nconvert_from_opts_font_features(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"font_features\");\n    if (ret == NULL) return;\n    convert_from_python_font_features(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_modify_font(PyObject *val, Options *opts) {\n    modify_font(val, opts);\n}\n\nstatic void\nconvert_from_opts_modify_font(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"modify_font\");\n    if (ret == NULL) return;\n    convert_from_python_modify_font(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_box_drawing_scale(PyObject *val, Options *opts) {\n    box_drawing_scale(val, opts);\n}\n\nstatic void\nconvert_from_opts_box_drawing_scale(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"box_drawing_scale\");\n    if (ret == NULL) return;\n    convert_from_python_box_drawing_scale(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_undercurl_style(PyObject *val, Options *opts) {\n    opts->undercurl_style = undercurl_style(val);\n}\n\nstatic void\nconvert_from_opts_undercurl_style(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"undercurl_style\");\n    if (ret == NULL) return;\n    convert_from_python_undercurl_style(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_underline_exclusion(PyObject *val, Options *opts) {\n    underline_exclusion(val, opts);\n}\n\nstatic void\nconvert_from_opts_underline_exclusion(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"underline_exclusion\");\n    if (ret == NULL) return;\n    convert_from_python_underline_exclusion(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_text_composition_strategy(PyObject *val, Options *opts) {\n    text_composition_strategy(val, opts);\n}\n\nstatic void\nconvert_from_opts_text_composition_strategy(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"text_composition_strategy\");\n    if (ret == NULL) return;\n    convert_from_python_text_composition_strategy(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_shape(PyObject *val, Options *opts) {\n    opts->cursor_shape = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_cursor_shape(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_shape\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_shape(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_shape_unfocused(PyObject *val, Options *opts) {\n    opts->cursor_shape_unfocused = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_cursor_shape_unfocused(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_shape_unfocused\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_shape_unfocused(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_beam_thickness(PyObject *val, Options *opts) {\n    opts->cursor_beam_thickness = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_cursor_beam_thickness(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_beam_thickness\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_beam_thickness(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_underline_thickness(PyObject *val, Options *opts) {\n    opts->cursor_underline_thickness = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_cursor_underline_thickness(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_underline_thickness\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_underline_thickness(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_blink_interval(PyObject *val, Options *opts) {\n    cursor_blink_interval(val, opts);\n}\n\nstatic void\nconvert_from_opts_cursor_blink_interval(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_blink_interval\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_blink_interval(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_stop_blinking_after(PyObject *val, Options *opts) {\n    opts->cursor_stop_blinking_after = parse_s_double_to_monotonic_t(val);\n}\n\nstatic void\nconvert_from_opts_cursor_stop_blinking_after(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_stop_blinking_after\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_stop_blinking_after(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_trail(PyObject *val, Options *opts) {\n    opts->cursor_trail = parse_ms_long_to_monotonic_t(val);\n}\n\nstatic void\nconvert_from_opts_cursor_trail(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_trail\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_trail(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_trail_decay(PyObject *val, Options *opts) {\n    cursor_trail_decay(val, opts);\n}\n\nstatic void\nconvert_from_opts_cursor_trail_decay(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_trail_decay\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_trail_decay(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_trail_start_threshold(PyObject *val, Options *opts) {\n    opts->cursor_trail_start_threshold = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_cursor_trail_start_threshold(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_trail_start_threshold\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_trail_start_threshold(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_cursor_trail_color(PyObject *val, Options *opts) {\n    cursor_trail_color(val, opts);\n}\n\nstatic void\nconvert_from_opts_cursor_trail_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"cursor_trail_color\");\n    if (ret == NULL) return;\n    convert_from_python_cursor_trail_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar(PyObject *val, Options *opts) {\n    opts->scrollbar = scrollbar(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_interactive(PyObject *val, Options *opts) {\n    opts->scrollbar_interactive = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_interactive(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_interactive\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_interactive(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_jump_on_click(PyObject *val, Options *opts) {\n    opts->scrollbar_jump_on_click = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_jump_on_click(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_jump_on_click\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_jump_on_click(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_width(PyObject *val, Options *opts) {\n    opts->scrollbar_width = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_width(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_width\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_width(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_hover_width(PyObject *val, Options *opts) {\n    opts->scrollbar_hover_width = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_hover_width(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_hover_width\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_hover_width(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_handle_opacity(PyObject *val, Options *opts) {\n    opts->scrollbar_handle_opacity = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_handle_opacity(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_handle_opacity\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_handle_opacity(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_radius(PyObject *val, Options *opts) {\n    opts->scrollbar_radius = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_radius(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_radius\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_radius(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_gap(PyObject *val, Options *opts) {\n    opts->scrollbar_gap = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_gap(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_gap\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_gap(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_min_handle_height(PyObject *val, Options *opts) {\n    opts->scrollbar_min_handle_height = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_min_handle_height(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_min_handle_height\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_min_handle_height(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_hitbox_expansion(PyObject *val, Options *opts) {\n    opts->scrollbar_hitbox_expansion = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_hitbox_expansion(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_hitbox_expansion\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_hitbox_expansion(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_track_opacity(PyObject *val, Options *opts) {\n    opts->scrollbar_track_opacity = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_track_opacity(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_track_opacity\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_track_opacity(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_track_hover_opacity(PyObject *val, Options *opts) {\n    opts->scrollbar_track_hover_opacity = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_track_hover_opacity(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_track_hover_opacity\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_track_hover_opacity(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_handle_color(PyObject *val, Options *opts) {\n    opts->scrollbar_handle_color = PyLong_AsUnsignedLong(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_handle_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_handle_color\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_handle_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollbar_track_color(PyObject *val, Options *opts) {\n    opts->scrollbar_track_color = PyLong_AsUnsignedLong(val);\n}\n\nstatic void\nconvert_from_opts_scrollbar_track_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollbar_track_color\");\n    if (ret == NULL) return;\n    convert_from_python_scrollbar_track_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollback_pager_history_size(PyObject *val, Options *opts) {\n    opts->scrollback_pager_history_size = PyLong_AsUnsignedLong(val);\n}\n\nstatic void\nconvert_from_opts_scrollback_pager_history_size(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollback_pager_history_size\");\n    if (ret == NULL) return;\n    convert_from_python_scrollback_pager_history_size(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_scrollback_fill_enlarged_window(PyObject *val, Options *opts) {\n    opts->scrollback_fill_enlarged_window = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_scrollback_fill_enlarged_window(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"scrollback_fill_enlarged_window\");\n    if (ret == NULL) return;\n    convert_from_python_scrollback_fill_enlarged_window(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_wheel_scroll_multiplier(PyObject *val, Options *opts) {\n    opts->wheel_scroll_multiplier = PyFloat_AsDouble(val);\n}\n\nstatic void\nconvert_from_opts_wheel_scroll_multiplier(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"wheel_scroll_multiplier\");\n    if (ret == NULL) return;\n    convert_from_python_wheel_scroll_multiplier(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_wheel_scroll_min_lines(PyObject *val, Options *opts) {\n    opts->wheel_scroll_min_lines = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_wheel_scroll_min_lines(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"wheel_scroll_min_lines\");\n    if (ret == NULL) return;\n    convert_from_python_wheel_scroll_min_lines(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_touch_scroll_multiplier(PyObject *val, Options *opts) {\n    opts->touch_scroll_multiplier = PyFloat_AsDouble(val);\n}\n\nstatic void\nconvert_from_opts_touch_scroll_multiplier(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"touch_scroll_multiplier\");\n    if (ret == NULL) return;\n    convert_from_python_touch_scroll_multiplier(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_pixel_scroll(PyObject *val, Options *opts) {\n    opts->pixel_scroll = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_pixel_scroll(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"pixel_scroll\");\n    if (ret == NULL) return;\n    convert_from_python_pixel_scroll(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_momentum_scroll(PyObject *val, Options *opts) {\n    opts->momentum_scroll = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_momentum_scroll(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"momentum_scroll\");\n    if (ret == NULL) return;\n    convert_from_python_momentum_scroll(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_mouse_hide_wait(PyObject *val, Options *opts) {\n    mouse_hide_wait(val, opts);\n}\n\nstatic void\nconvert_from_opts_mouse_hide_wait(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"mouse_hide_wait\");\n    if (ret == NULL) return;\n    convert_from_python_mouse_hide_wait(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_url_color(PyObject *val, Options *opts) {\n    opts->url_color = color_as_int(val);\n}\n\nstatic void\nconvert_from_opts_url_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"url_color\");\n    if (ret == NULL) return;\n    convert_from_python_url_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_url_style(PyObject *val, Options *opts) {\n    opts->url_style = PyLong_AsUnsignedLong(val);\n}\n\nstatic void\nconvert_from_opts_url_style(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"url_style\");\n    if (ret == NULL) return;\n    convert_from_python_url_style(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_url_prefixes(PyObject *val, Options *opts) {\n    url_prefixes(val, opts);\n}\n\nstatic void\nconvert_from_opts_url_prefixes(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"url_prefixes\");\n    if (ret == NULL) return;\n    convert_from_python_url_prefixes(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_detect_urls(PyObject *val, Options *opts) {\n    opts->detect_urls = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_detect_urls(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"detect_urls\");\n    if (ret == NULL) return;\n    convert_from_python_detect_urls(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_url_excluded_characters(PyObject *val, Options *opts) {\n    url_excluded_characters(val, opts);\n}\n\nstatic void\nconvert_from_opts_url_excluded_characters(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"url_excluded_characters\");\n    if (ret == NULL) return;\n    convert_from_python_url_excluded_characters(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_show_hyperlink_targets(PyObject *val, Options *opts) {\n    opts->show_hyperlink_targets = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_show_hyperlink_targets(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"show_hyperlink_targets\");\n    if (ret == NULL) return;\n    convert_from_python_show_hyperlink_targets(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_underline_hyperlinks(PyObject *val, Options *opts) {\n    opts->underline_hyperlinks = underline_hyperlinks(val);\n}\n\nstatic void\nconvert_from_opts_underline_hyperlinks(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"underline_hyperlinks\");\n    if (ret == NULL) return;\n    convert_from_python_underline_hyperlinks(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_select_by_word_characters(PyObject *val, Options *opts) {\n    select_by_word_characters(val, opts);\n}\n\nstatic void\nconvert_from_opts_select_by_word_characters(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"select_by_word_characters\");\n    if (ret == NULL) return;\n    convert_from_python_select_by_word_characters(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_select_by_word_characters_forward(PyObject *val, Options *opts) {\n    select_by_word_characters_forward(val, opts);\n}\n\nstatic void\nconvert_from_opts_select_by_word_characters_forward(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"select_by_word_characters_forward\");\n    if (ret == NULL) return;\n    convert_from_python_select_by_word_characters_forward(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_click_interval(PyObject *val, Options *opts) {\n    opts->click_interval = parse_s_double_to_monotonic_t(val);\n}\n\nstatic void\nconvert_from_opts_click_interval(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"click_interval\");\n    if (ret == NULL) return;\n    convert_from_python_click_interval(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_focus_follows_mouse(PyObject *val, Options *opts) {\n    opts->focus_follows_mouse = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_focus_follows_mouse(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"focus_follows_mouse\");\n    if (ret == NULL) return;\n    convert_from_python_focus_follows_mouse(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_pointer_shape_when_grabbed(PyObject *val, Options *opts) {\n    opts->pointer_shape_when_grabbed = pointer_shape(val);\n}\n\nstatic void\nconvert_from_opts_pointer_shape_when_grabbed(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"pointer_shape_when_grabbed\");\n    if (ret == NULL) return;\n    convert_from_python_pointer_shape_when_grabbed(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_default_pointer_shape(PyObject *val, Options *opts) {\n    opts->default_pointer_shape = pointer_shape(val);\n}\n\nstatic void\nconvert_from_opts_default_pointer_shape(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"default_pointer_shape\");\n    if (ret == NULL) return;\n    convert_from_python_default_pointer_shape(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_pointer_shape_when_dragging(PyObject *val, Options *opts) {\n    dragging_pointer_shape(val, opts);\n}\n\nstatic void\nconvert_from_opts_pointer_shape_when_dragging(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"pointer_shape_when_dragging\");\n    if (ret == NULL) return;\n    convert_from_python_pointer_shape_when_dragging(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_repaint_delay(PyObject *val, Options *opts) {\n    opts->repaint_delay = parse_ms_long_to_monotonic_t(val);\n}\n\nstatic void\nconvert_from_opts_repaint_delay(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"repaint_delay\");\n    if (ret == NULL) return;\n    convert_from_python_repaint_delay(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_input_delay(PyObject *val, Options *opts) {\n    opts->input_delay = parse_ms_long_to_monotonic_t(val);\n}\n\nstatic void\nconvert_from_opts_input_delay(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"input_delay\");\n    if (ret == NULL) return;\n    convert_from_python_input_delay(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_sync_to_monitor(PyObject *val, Options *opts) {\n    opts->sync_to_monitor = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_sync_to_monitor(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"sync_to_monitor\");\n    if (ret == NULL) return;\n    convert_from_python_sync_to_monitor(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_enable_audio_bell(PyObject *val, Options *opts) {\n    opts->enable_audio_bell = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_enable_audio_bell(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"enable_audio_bell\");\n    if (ret == NULL) return;\n    convert_from_python_enable_audio_bell(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_visual_bell_duration(PyObject *val, Options *opts) {\n    visual_bell_duration(val, opts);\n}\n\nstatic void\nconvert_from_opts_visual_bell_duration(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"visual_bell_duration\");\n    if (ret == NULL) return;\n    convert_from_python_visual_bell_duration(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_alert_on_bell(PyObject *val, Options *opts) {\n    opts->window_alert_on_bell = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_window_alert_on_bell(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_alert_on_bell\");\n    if (ret == NULL) return;\n    convert_from_python_window_alert_on_bell(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_dock_badge_on_bell(PyObject *val, Options *opts) {\n    opts->macos_dock_badge_on_bell = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_macos_dock_badge_on_bell(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_dock_badge_on_bell\");\n    if (ret == NULL) return;\n    convert_from_python_macos_dock_badge_on_bell(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_bell_path(PyObject *val, Options *opts) {\n    bell_path(val, opts);\n}\n\nstatic void\nconvert_from_opts_bell_path(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"bell_path\");\n    if (ret == NULL) return;\n    convert_from_python_bell_path(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_linux_bell_theme(PyObject *val, Options *opts) {\n    bell_theme(val, opts);\n}\n\nstatic void\nconvert_from_opts_linux_bell_theme(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"linux_bell_theme\");\n    if (ret == NULL) return;\n    convert_from_python_linux_bell_theme(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_active_border_color(PyObject *val, Options *opts) {\n    opts->active_border_color = active_border_color(val);\n}\n\nstatic void\nconvert_from_opts_active_border_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"active_border_color\");\n    if (ret == NULL) return;\n    convert_from_python_active_border_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_inactive_border_color(PyObject *val, Options *opts) {\n    opts->inactive_border_color = color_as_int(val);\n}\n\nstatic void\nconvert_from_opts_inactive_border_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"inactive_border_color\");\n    if (ret == NULL) return;\n    convert_from_python_inactive_border_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_bell_border_color(PyObject *val, Options *opts) {\n    opts->bell_border_color = color_as_int(val);\n}\n\nstatic void\nconvert_from_opts_bell_border_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"bell_border_color\");\n    if (ret == NULL) return;\n    convert_from_python_bell_border_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_inactive_text_alpha(PyObject *val, Options *opts) {\n    opts->inactive_text_alpha = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_inactive_text_alpha(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"inactive_text_alpha\");\n    if (ret == NULL) return;\n    convert_from_python_inactive_text_alpha(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_hide_window_decorations(PyObject *val, Options *opts) {\n    opts->hide_window_decorations = PyLong_AsUnsignedLong(val);\n}\n\nstatic void\nconvert_from_opts_hide_window_decorations(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"hide_window_decorations\");\n    if (ret == NULL) return;\n    convert_from_python_hide_window_decorations(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_logo_path(PyObject *val, Options *opts) {\n    window_logo_path(val, opts);\n}\n\nstatic void\nconvert_from_opts_window_logo_path(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_logo_path\");\n    if (ret == NULL) return;\n    convert_from_python_window_logo_path(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_logo_position(PyObject *val, Options *opts) {\n    opts->window_logo_position = bganchor(val);\n}\n\nstatic void\nconvert_from_opts_window_logo_position(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_logo_position\");\n    if (ret == NULL) return;\n    convert_from_python_window_logo_position(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_logo_alpha(PyObject *val, Options *opts) {\n    opts->window_logo_alpha = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_window_logo_alpha(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_logo_alpha\");\n    if (ret == NULL) return;\n    convert_from_python_window_logo_alpha(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_logo_scale(PyObject *val, Options *opts) {\n    window_logo_scale(val, opts);\n}\n\nstatic void\nconvert_from_opts_window_logo_scale(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_logo_scale\");\n    if (ret == NULL) return;\n    convert_from_python_window_logo_scale(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_resize_debounce_time(PyObject *val, Options *opts) {\n    resize_debounce_time(val, opts);\n}\n\nstatic void\nconvert_from_opts_resize_debounce_time(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"resize_debounce_time\");\n    if (ret == NULL) return;\n    convert_from_python_resize_debounce_time(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_resize_in_steps(PyObject *val, Options *opts) {\n    opts->resize_in_steps = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_resize_in_steps(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"resize_in_steps\");\n    if (ret == NULL) return;\n    convert_from_python_resize_in_steps(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_drag_tolerance(PyObject *val, Options *opts) {\n    opts->window_drag_tolerance = PyFloat_AsDouble(val);\n}\n\nstatic void\nconvert_from_opts_window_drag_tolerance(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_drag_tolerance\");\n    if (ret == NULL) return;\n    convert_from_python_window_drag_tolerance(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_title_bar_active_foreground(PyObject *val, Options *opts) {\n    opts->window_title_bar_active_foreground = color_or_none_as_int(val);\n}\n\nstatic void\nconvert_from_opts_window_title_bar_active_foreground(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_title_bar_active_foreground\");\n    if (ret == NULL) return;\n    convert_from_python_window_title_bar_active_foreground(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_title_bar_active_background(PyObject *val, Options *opts) {\n    opts->window_title_bar_active_background = color_or_none_as_int(val);\n}\n\nstatic void\nconvert_from_opts_window_title_bar_active_background(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_title_bar_active_background\");\n    if (ret == NULL) return;\n    convert_from_python_window_title_bar_active_background(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_title_bar_inactive_foreground(PyObject *val, Options *opts) {\n    opts->window_title_bar_inactive_foreground = color_or_none_as_int(val);\n}\n\nstatic void\nconvert_from_opts_window_title_bar_inactive_foreground(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_title_bar_inactive_foreground\");\n    if (ret == NULL) return;\n    convert_from_python_window_title_bar_inactive_foreground(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_window_title_bar_inactive_background(PyObject *val, Options *opts) {\n    opts->window_title_bar_inactive_background = color_or_none_as_int(val);\n}\n\nstatic void\nconvert_from_opts_window_title_bar_inactive_background(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"window_title_bar_inactive_background\");\n    if (ret == NULL) return;\n    convert_from_python_window_title_bar_inactive_background(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_tab_bar_edge(PyObject *val, Options *opts) {\n    opts->tab_bar_edge = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_tab_bar_edge(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"tab_bar_edge\");\n    if (ret == NULL) return;\n    convert_from_python_tab_bar_edge(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_tab_bar_margin_height(PyObject *val, Options *opts) {\n    tab_bar_margin_height(val, opts);\n}\n\nstatic void\nconvert_from_opts_tab_bar_margin_height(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"tab_bar_margin_height\");\n    if (ret == NULL) return;\n    convert_from_python_tab_bar_margin_height(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_tab_bar_style(PyObject *val, Options *opts) {\n    tab_bar_style(val, opts);\n}\n\nstatic void\nconvert_from_opts_tab_bar_style(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"tab_bar_style\");\n    if (ret == NULL) return;\n    convert_from_python_tab_bar_style(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_tab_bar_background(PyObject *val, Options *opts) {\n    opts->tab_bar_background = color_or_none_as_int(val);\n}\n\nstatic void\nconvert_from_opts_tab_bar_background(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"tab_bar_background\");\n    if (ret == NULL) return;\n    convert_from_python_tab_bar_background(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_tab_bar_margin_color(PyObject *val, Options *opts) {\n    opts->tab_bar_margin_color = color_or_none_as_int(val);\n}\n\nstatic void\nconvert_from_opts_tab_bar_margin_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"tab_bar_margin_color\");\n    if (ret == NULL) return;\n    convert_from_python_tab_bar_margin_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_foreground(PyObject *val, Options *opts) {\n    opts->foreground = color_as_int(val);\n}\n\nstatic void\nconvert_from_opts_foreground(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"foreground\");\n    if (ret == NULL) return;\n    convert_from_python_foreground(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_background(PyObject *val, Options *opts) {\n    opts->background = color_as_int(val);\n}\n\nstatic void\nconvert_from_opts_background(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"background\");\n    if (ret == NULL) return;\n    convert_from_python_background(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_background_opacity(PyObject *val, Options *opts) {\n    opts->background_opacity = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_background_opacity(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"background_opacity\");\n    if (ret == NULL) return;\n    convert_from_python_background_opacity(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_background_blur(PyObject *val, Options *opts) {\n    opts->background_blur = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_background_blur(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"background_blur\");\n    if (ret == NULL) return;\n    convert_from_python_background_blur(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_dynamic_background_opacity(PyObject *val, Options *opts) {\n    opts->dynamic_background_opacity = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_dynamic_background_opacity(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"dynamic_background_opacity\");\n    if (ret == NULL) return;\n    convert_from_python_dynamic_background_opacity(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_background_image(PyObject *val, Options *opts) {\n    background_image(val, opts);\n}\n\nstatic void\nconvert_from_opts_background_image(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"background_image\");\n    if (ret == NULL) return;\n    convert_from_python_background_image(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_background_image_layout(PyObject *val, Options *opts) {\n    opts->background_image_layout = bglayout(val);\n}\n\nstatic void\nconvert_from_opts_background_image_layout(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"background_image_layout\");\n    if (ret == NULL) return;\n    convert_from_python_background_image_layout(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_background_image_linear(PyObject *val, Options *opts) {\n    opts->background_image_linear = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_background_image_linear(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"background_image_linear\");\n    if (ret == NULL) return;\n    convert_from_python_background_image_linear(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_background_tint(PyObject *val, Options *opts) {\n    opts->background_tint = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_background_tint(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"background_tint\");\n    if (ret == NULL) return;\n    convert_from_python_background_tint(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_background_tint_gaps(PyObject *val, Options *opts) {\n    opts->background_tint_gaps = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_background_tint_gaps(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"background_tint_gaps\");\n    if (ret == NULL) return;\n    convert_from_python_background_tint_gaps(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_dim_opacity(PyObject *val, Options *opts) {\n    opts->dim_opacity = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_dim_opacity(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"dim_opacity\");\n    if (ret == NULL) return;\n    convert_from_python_dim_opacity(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_close_on_child_death(PyObject *val, Options *opts) {\n    opts->close_on_child_death = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_close_on_child_death(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"close_on_child_death\");\n    if (ret == NULL) return;\n    convert_from_python_close_on_child_death(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_allow_hyperlinks(PyObject *val, Options *opts) {\n    opts->allow_hyperlinks = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_allow_hyperlinks(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"allow_hyperlinks\");\n    if (ret == NULL) return;\n    convert_from_python_allow_hyperlinks(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_menu_map(PyObject *val, Options *opts) {\n    menu_map(val, opts);\n}\n\nstatic void\nconvert_from_opts_menu_map(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"menu_map\");\n    if (ret == NULL) return;\n    convert_from_python_menu_map(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_wayland_titlebar_color(PyObject *val, Options *opts) {\n    opts->wayland_titlebar_color = PyLong_AsUnsignedLong(val);\n}\n\nstatic void\nconvert_from_opts_wayland_titlebar_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"wayland_titlebar_color\");\n    if (ret == NULL) return;\n    convert_from_python_wayland_titlebar_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_titlebar_color(PyObject *val, Options *opts) {\n    opts->macos_titlebar_color = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_macos_titlebar_color(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_titlebar_color\");\n    if (ret == NULL) return;\n    convert_from_python_macos_titlebar_color(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_option_as_alt(PyObject *val, Options *opts) {\n    opts->macos_option_as_alt = PyLong_AsUnsignedLong(val);\n}\n\nstatic void\nconvert_from_opts_macos_option_as_alt(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_option_as_alt\");\n    if (ret == NULL) return;\n    convert_from_python_macos_option_as_alt(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_hide_from_tasks(PyObject *val, Options *opts) {\n    opts->macos_hide_from_tasks = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_macos_hide_from_tasks(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_hide_from_tasks\");\n    if (ret == NULL) return;\n    convert_from_python_macos_hide_from_tasks(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_quit_when_last_window_closed(PyObject *val, Options *opts) {\n    opts->macos_quit_when_last_window_closed = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_macos_quit_when_last_window_closed(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_quit_when_last_window_closed\");\n    if (ret == NULL) return;\n    convert_from_python_macos_quit_when_last_window_closed(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_window_resizable(PyObject *val, Options *opts) {\n    opts->macos_window_resizable = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_macos_window_resizable(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_window_resizable\");\n    if (ret == NULL) return;\n    convert_from_python_macos_window_resizable(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_thicken_font(PyObject *val, Options *opts) {\n    opts->macos_thicken_font = PyFloat_AsFloat(val);\n}\n\nstatic void\nconvert_from_opts_macos_thicken_font(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_thicken_font\");\n    if (ret == NULL) return;\n    convert_from_python_macos_thicken_font(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_traditional_fullscreen(PyObject *val, Options *opts) {\n    opts->macos_traditional_fullscreen = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_macos_traditional_fullscreen(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_traditional_fullscreen\");\n    if (ret == NULL) return;\n    convert_from_python_macos_traditional_fullscreen(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_show_window_title_in(PyObject *val, Options *opts) {\n    opts->macos_show_window_title_in = window_title_in(val);\n}\n\nstatic void\nconvert_from_opts_macos_show_window_title_in(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_show_window_title_in\");\n    if (ret == NULL) return;\n    convert_from_python_macos_show_window_title_in(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_menubar_title_max_length(PyObject *val, Options *opts) {\n    opts->macos_menubar_title_max_length = PyLong_AsLong(val);\n}\n\nstatic void\nconvert_from_opts_macos_menubar_title_max_length(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_menubar_title_max_length\");\n    if (ret == NULL) return;\n    convert_from_python_macos_menubar_title_max_length(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_macos_colorspace(PyObject *val, Options *opts) {\n    opts->macos_colorspace = macos_colorspace(val);\n}\n\nstatic void\nconvert_from_opts_macos_colorspace(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"macos_colorspace\");\n    if (ret == NULL) return;\n    convert_from_python_macos_colorspace(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic void\nconvert_from_python_wayland_enable_ime(PyObject *val, Options *opts) {\n    opts->wayland_enable_ime = PyObject_IsTrue(val);\n}\n\nstatic void\nconvert_from_opts_wayland_enable_ime(PyObject *py_opts, Options *opts) {\n    PyObject *ret = PyObject_GetAttrString(py_opts, \"wayland_enable_ime\");\n    if (ret == NULL) return;\n    convert_from_python_wayland_enable_ime(ret, opts);\n    Py_DECREF(ret);\n}\n\nstatic bool\nconvert_opts_from_python_opts(PyObject *py_opts, Options *opts) {\n    convert_from_opts_font_size(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_force_ltr(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_disable_ligatures(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_font_features(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_modify_font(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_box_drawing_scale(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_undercurl_style(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_underline_exclusion(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_text_composition_strategy(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_shape(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_shape_unfocused(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_beam_thickness(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_underline_thickness(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_blink_interval(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_stop_blinking_after(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_trail(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_trail_decay(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_trail_start_threshold(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_cursor_trail_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_interactive(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_jump_on_click(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_width(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_hover_width(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_handle_opacity(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_radius(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_gap(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_min_handle_height(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_hitbox_expansion(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_track_opacity(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_track_hover_opacity(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_handle_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollbar_track_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollback_pager_history_size(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_scrollback_fill_enlarged_window(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_wheel_scroll_multiplier(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_wheel_scroll_min_lines(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_touch_scroll_multiplier(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_pixel_scroll(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_momentum_scroll(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_mouse_hide_wait(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_url_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_url_style(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_url_prefixes(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_detect_urls(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_url_excluded_characters(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_show_hyperlink_targets(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_underline_hyperlinks(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_select_by_word_characters(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_select_by_word_characters_forward(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_click_interval(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_focus_follows_mouse(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_pointer_shape_when_grabbed(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_default_pointer_shape(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_pointer_shape_when_dragging(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_repaint_delay(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_input_delay(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_sync_to_monitor(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_enable_audio_bell(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_visual_bell_duration(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_alert_on_bell(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_dock_badge_on_bell(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_bell_path(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_linux_bell_theme(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_active_border_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_inactive_border_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_bell_border_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_inactive_text_alpha(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_hide_window_decorations(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_logo_path(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_logo_position(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_logo_alpha(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_logo_scale(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_resize_debounce_time(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_resize_in_steps(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_drag_tolerance(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_title_bar_active_foreground(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_title_bar_active_background(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_title_bar_inactive_foreground(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_window_title_bar_inactive_background(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_tab_bar_edge(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_tab_bar_margin_height(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_tab_bar_style(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_tab_bar_background(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_tab_bar_margin_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_foreground(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_background(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_background_opacity(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_background_blur(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_dynamic_background_opacity(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_background_image(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_background_image_layout(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_background_image_linear(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_background_tint(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_background_tint_gaps(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_dim_opacity(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_close_on_child_death(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_allow_hyperlinks(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_menu_map(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_wayland_titlebar_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_titlebar_color(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_option_as_alt(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_hide_from_tasks(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_quit_when_last_window_closed(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_window_resizable(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_thicken_font(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_traditional_fullscreen(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_show_window_title_in(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_menubar_title_max_length(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_macos_colorspace(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    convert_from_opts_wayland_enable_ime(py_opts, opts);\n    if (PyErr_Occurred()) return false;\n    return true;\n}\n"
  },
  {
    "path": "kitty/options/to-c.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"../state.h\"\n#include \"../colors.h\"\n#include \"../fonts.h\"\n\nstatic inline float\nPyFloat_AsFloat(PyObject *o) {\n    return (float)PyFloat_AsDouble(o);\n}\n\nstatic inline color_type\ncolor_as_int(PyObject *color) {\n    if (!PyObject_TypeCheck(color, &Color_Type)) { PyErr_SetString(PyExc_TypeError, \"Not a Color object\"); return 0; }\n    Color *c = (Color*)color;\n    return c->color.val & 0xffffff;\n}\n\nstatic inline color_type\ncolor_or_none_as_int(PyObject *color) {\n    if (color == Py_None) return 0;\n    return color_as_int(color);\n}\n\nstatic inline color_type\nactive_border_color(PyObject *color) {\n    if (color == Py_None) return 0x00ff00;\n    return color_as_int(color);\n}\n\n\nstatic inline monotonic_t\nparse_s_double_to_monotonic_t(PyObject *val) {\n    return s_double_to_monotonic_t(PyFloat_AsDouble(val));\n}\n\nstatic inline monotonic_t\nparse_ms_long_to_monotonic_t(PyObject *val) {\n    return ms_to_monotonic_t(PyLong_AsUnsignedLong(val));\n}\n\nstatic inline WindowTitleIn\nwindow_title_in(PyObject *title_in) {\n    const char *in = PyUnicode_AsUTF8(title_in);\n    switch(in[0]) {\n        case 'a': return ALL;\n        case 'w': return WINDOW;\n        case 'm': return MENUBAR;\n        case 'n': return NONE;\n        default: break;\n    }\n    return ALL;\n}\n\nstatic inline ScrollbarVisibilityPolicy\nscrollbar(PyObject *src) {\n    const char *q = PyUnicode_AsUTF8(src);\n    switch (q[0]) {\n        case 'a': return SCROLLBAR_ALWAYS;\n        case 'n': return SCROLLBAR_NEVER;\n        case 'h': return SCROLLBAR_ON_HOVERED;\n        case 's':\n            return strcmp(q, \"scrolled\") == 0 ? SCROLLBAR_ON_SCROLLED : SCROLLBAR_ON_SCROLL_AND_HOVER;\n    }\n    return SCROLLBAR_ON_SCROLLED;\n}\n\nstatic inline unsigned\nundercurl_style(PyObject *x) {\n    RAII_PyObject(thick, PyUnicode_FromString(\"thick\"));\n    RAII_PyObject(dense, PyUnicode_FromString(\"dense\"));\n    unsigned ans = 0;\n    int ret;\n    switch ((ret = PyUnicode_Find(x, dense, 0, PyUnicode_GET_LENGTH(x), 1))) {\n        case -2: PyErr_Clear(); case -1: break;\n        default: ans |= 1;\n    }\n    switch ((ret = PyUnicode_Find(x, thick, 0, PyUnicode_GET_LENGTH(x), 1))) {\n        case -2: PyErr_Clear(); case -1: break;\n        default: ans |= 2;\n    }\n    return ans;\n}\n\nstatic inline UnderlineHyperlinks\nunderline_hyperlinks(PyObject *x) {\n    const char *in = PyUnicode_AsUTF8(x);\n    switch(in[0]) {\n        case 'a': return UNDERLINE_ALWAYS;\n        case 'n': return UNDERLINE_NEVER;\n        default : return UNDERLINE_ON_HOVER;\n    }\n}\n\nstatic inline BackgroundImageLayout\nbglayout(PyObject *layout_name) {\n    const char *name = PyUnicode_AsUTF8(layout_name);\n    switch(name[0]) {\n        case 't': return TILING;\n        case 'm': return MIRRORED;\n        case 's': return SCALED;\n        case 'c': {\n            return name[1] == 'l' ? CLAMPED : (name[1] == 's' ? CENTER_SCALED : CENTER_CLAMPED);\n        }\n        default: break;\n    }\n    return TILING;\n}\n\nstatic inline ImageAnchorPosition\nbganchor(PyObject *anchor_name) {\n    const char *name = PyUnicode_AsUTF8(anchor_name);\n    ImageAnchorPosition anchor = {0.5f, 0.5f, 0.5f, 0.5f};\n    if (strstr(name, \"top\") != NULL) {\n        anchor.canvas_y = 0.f; anchor.image_y = 0.f;\n    } else if (strstr(name, \"bottom\") != NULL) {\n        anchor.canvas_y = 1.f; anchor.image_y = 1.f;\n    }\n    if (strstr(name, \"left\") != NULL) {\n        anchor.canvas_x = 0.f; anchor.image_x = 0.f;\n    } else if (strstr(name, \"right\") != NULL) {\n        anchor.canvas_x = 1.f; anchor.image_x = 1.f;\n    }\n    return anchor;\n}\n\n#define STR_SETTER(name) { \\\n    free(opts->name); opts->name = NULL; \\\n    if (src == Py_None || !PyUnicode_Check(src)) return; \\\n    Py_ssize_t sz; \\\n    const char *s = PyUnicode_AsUTF8AndSize(src, &sz); \\\n    opts->name = calloc(sz + 1, sizeof(s[0])); \\\n    if (opts->name) memcpy(opts->name, s, sz); \\\n}\n\nstatic inline void\nbackground_image(PyObject *src, Options *opts) { STR_SETTER(background_image); }\n\nstatic inline void\nbell_path(PyObject *src, Options *opts) { STR_SETTER(bell_path); }\n\nstatic inline void\nbell_theme(PyObject *src, Options *opts) { STR_SETTER(bell_theme); }\n\nstatic inline void\nwindow_logo_path(PyObject *src, Options *opts) { STR_SETTER(default_window_logo); }\n\n#undef STR_SETTER\n\nstatic void\nadd_easing_function(Animation *a, PyObject *e, double y_at_start, double y_at_end) {\n#define G(name) RAII_PyObject(name, PyObject_GetAttrString(e, #name))\n#define D(container, idx) PyFloat_AsDouble(PyTuple_GET_ITEM(container, idx))\n#define EQ(x, val) (PyUnicode_CompareWithASCIIString((x), val) == 0)\n    G(type);\n    if (EQ(type, \"cubic-bezier\")) {\n        G(cubic_bezier_points);\n        add_cubic_bezier_animation(a, y_at_start, y_at_end, D(cubic_bezier_points, 0), D(cubic_bezier_points, 1), D(cubic_bezier_points, 2), D(cubic_bezier_points, 3));\n    } else if (EQ(type, \"linear\")) {\n        G(linear_x); G(linear_y);\n        size_t count = PyTuple_GET_SIZE(linear_x);\n        RAII_ALLOC(double, x, malloc(2 * sizeof(double) * count));\n        if (x) {\n            double *y = x + count;\n            for (size_t i = 0; i < count; i++) {\n                x[i] = D(linear_x, i); y[i] = D(linear_y, i);\n            }\n            add_linear_animation(a, y_at_start, y_at_end, count, x, y);\n        }\n    } else if (EQ(type, \"steps\")) {\n        G(num_steps); G(jump_type);\n        EasingStep jt = EASING_STEP_END;\n        if (EQ(jump_type, \"start\")) jt = EASING_STEP_START;\n        else if (EQ(jump_type, \"none\")) jt = EASING_STEP_NONE;\n        else if (EQ(jump_type, \"both\")) jt = EASING_STEP_BOTH;\n        add_steps_animation(a, y_at_start, y_at_end, PyLong_AsSize_t(num_steps), jt);\n    }\n#undef EQ\n#undef D\n#undef G\n}\n\n#define parse_animation(duration, name, start, end) \\\n    opts->duration = parse_s_double_to_monotonic_t(PyTuple_GET_ITEM(src, 0)); \\\n    opts->animation.name = free_animation(opts->animation.name); \\\n    if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 1)) && (opts->animation.name = alloc_animation()) != NULL) { \\\n        add_easing_function(opts->animation.name, PyTuple_GET_ITEM(src, 1), start, end); \\\n        if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 2))) { \\\n            add_easing_function(opts->animation.name, PyTuple_GET_ITEM(src, 2), end, start); \\\n        } else { \\\n            add_easing_function(opts->animation.name, PyTuple_GET_ITEM(src, 1), end, start); \\\n        } \\\n    } \\\n\nstatic inline void\ncursor_blink_interval(PyObject *src, Options *opts) {\n    parse_animation(cursor_blink_interval, cursor, 1, 0);\n}\n\nstatic inline void\nvisual_bell_duration(PyObject *src, Options *opts) {\n    parse_animation(visual_bell_duration, visual_bell, 0, 1);\n}\n\n#undef parse_animation\n\nstatic inline void\nmouse_hide_wait(PyObject *val, Options *opts) {\n    if (!PyTuple_Check(val) || PyTuple_GET_SIZE(val) != 4) {\n        PyErr_SetString(PyExc_TypeError, \"mouse_hide_wait is not a 4-item tuple\");\n        return;\n    }\n    opts->mouse_hide.hide_wait = parse_s_double_to_monotonic_t(PyTuple_GET_ITEM(val, 0));\n    opts->mouse_hide.unhide_wait = parse_s_double_to_monotonic_t(PyTuple_GET_ITEM(val, 1));\n    opts->mouse_hide.unhide_threshold = PyLong_AsLong(PyTuple_GET_ITEM(val, 2));\n    opts->mouse_hide.scroll_unhide = PyObject_IsTrue(PyTuple_GET_ITEM(val, 3));\n}\n\nstatic inline void\ncursor_trail_decay(PyObject *src, Options *opts) {\n    opts->cursor_trail_decay_fast = PyFloat_AsFloat(PyTuple_GET_ITEM(src, 0));\n    opts->cursor_trail_decay_slow = PyFloat_AsFloat(PyTuple_GET_ITEM(src, 1));\n}\n\nstatic inline void\ncursor_trail_color(PyObject *src, Options *opts) {\n    opts->cursor_trail_color = color_or_none_as_int(src);\n}\n\nstatic void\nparse_font_mod_size(PyObject *val, float *sz, AdjustmentUnit *unit) {\n    PyObject *mv = PyObject_GetAttrString(val, \"mod_value\");\n    if (mv) {\n        *sz = PyFloat_AsFloat(PyTuple_GET_ITEM(mv, 0));\n        long u = PyLong_AsLong(PyTuple_GET_ITEM(mv, 1));\n        switch (u) { case POINT: case PERCENT: case PIXEL: *unit = u; break; }\n    }\n}\n\nstatic inline void\nmodify_font(PyObject *mf, Options *opts) {\n#define S(which) { PyObject *v = PyDict_GetItemString(mf, #which); if (v) parse_font_mod_size(v, &opts->which.val, &opts->which.unit); else zero_at_ptr(&opts->which); }\n    S(underline_position); S(underline_thickness); S(strikethrough_thickness); S(strikethrough_position);\n    S(cell_height); S(cell_width); S(baseline);\n#undef S\n}\n\nstatic inline void\nfree_font_features(Options *opts) {\n    if (opts->font_features.entries) {\n        for (size_t i = 0; i < opts->font_features.num; i++) {\n            free((void*)opts->font_features.entries[i].psname);\n            free((void*)opts->font_features.entries[i].features);\n        }\n        free(opts->font_features.entries);\n    }\n    memset(&opts->font_features, 0, sizeof(opts->font_features));\n}\n\nstatic inline void\nfont_features(PyObject *mf, Options *opts) {\n    free_font_features(opts);\n    opts->font_features.num = PyDict_GET_SIZE(mf);\n    if (!opts->font_features.num) return;\n    opts->font_features.entries = calloc(opts->font_features.num, sizeof(opts->font_features.entries[0]));\n    if (!opts->font_features.entries) { PyErr_NoMemory(); return; }\n    PyObject *key, *value;\n    Py_ssize_t pos = 0, i = 0;\n    while (PyDict_Next(mf, &pos, &key, &value)) {\n        __typeof__(opts->font_features.entries) e = opts->font_features.entries + i++;\n        Py_ssize_t psname_sz; const char *psname = PyUnicode_AsUTF8AndSize(key, &psname_sz);\n        e->psname = strndup(psname, psname_sz);\n        if (!e->psname) { PyErr_NoMemory(); return; }\n        e->num = PyTuple_GET_SIZE(value);\n        if (e->num) {\n            e->features = calloc(e->num, sizeof(e->features[0]));\n            if (!e->features) { PyErr_NoMemory(); return; }\n            for (size_t n = 0; n < e->num; n++) {\n                ParsedFontFeature *f = (ParsedFontFeature*)PyTuple_GET_ITEM(value, n);\n                e->features[n] = f->feature;\n            }\n        }\n    }\n}\n\nstatic inline MouseShape\npointer_shape(PyObject *shape_name) {\n    const char *name = PyUnicode_AsUTF8(shape_name);\n    if (!name) return TEXT_POINTER;\n    /* start pointer shapes (auto generated by gen-key-constants.py do not edit) */\n    else if (strcmp(name, \"arrow\") == 0) return DEFAULT_POINTER;\n    else if (strcmp(name, \"beam\") == 0) return TEXT_POINTER;\n    else if (strcmp(name, \"text\") == 0) return TEXT_POINTER;\n    else if (strcmp(name, \"pointer\") == 0) return POINTER_POINTER;\n    else if (strcmp(name, \"hand\") == 0) return POINTER_POINTER;\n    else if (strcmp(name, \"help\") == 0) return HELP_POINTER;\n    else if (strcmp(name, \"wait\") == 0) return WAIT_POINTER;\n    else if (strcmp(name, \"progress\") == 0) return PROGRESS_POINTER;\n    else if (strcmp(name, \"crosshair\") == 0) return CROSSHAIR_POINTER;\n    else if (strcmp(name, \"cell\") == 0) return CELL_POINTER;\n    else if (strcmp(name, \"vertical-text\") == 0) return VERTICAL_TEXT_POINTER;\n    else if (strcmp(name, \"move\") == 0) return MOVE_POINTER;\n    else if (strcmp(name, \"e-resize\") == 0) return E_RESIZE_POINTER;\n    else if (strcmp(name, \"ne-resize\") == 0) return NE_RESIZE_POINTER;\n    else if (strcmp(name, \"nw-resize\") == 0) return NW_RESIZE_POINTER;\n    else if (strcmp(name, \"n-resize\") == 0) return N_RESIZE_POINTER;\n    else if (strcmp(name, \"se-resize\") == 0) return SE_RESIZE_POINTER;\n    else if (strcmp(name, \"sw-resize\") == 0) return SW_RESIZE_POINTER;\n    else if (strcmp(name, \"s-resize\") == 0) return S_RESIZE_POINTER;\n    else if (strcmp(name, \"w-resize\") == 0) return W_RESIZE_POINTER;\n    else if (strcmp(name, \"ew-resize\") == 0) return EW_RESIZE_POINTER;\n    else if (strcmp(name, \"ns-resize\") == 0) return NS_RESIZE_POINTER;\n    else if (strcmp(name, \"nesw-resize\") == 0) return NESW_RESIZE_POINTER;\n    else if (strcmp(name, \"nwse-resize\") == 0) return NWSE_RESIZE_POINTER;\n    else if (strcmp(name, \"zoom-in\") == 0) return ZOOM_IN_POINTER;\n    else if (strcmp(name, \"zoom-out\") == 0) return ZOOM_OUT_POINTER;\n    else if (strcmp(name, \"alias\") == 0) return ALIAS_POINTER;\n    else if (strcmp(name, \"copy\") == 0) return COPY_POINTER;\n    else if (strcmp(name, \"not-allowed\") == 0) return NOT_ALLOWED_POINTER;\n    else if (strcmp(name, \"no-drop\") == 0) return NO_DROP_POINTER;\n    else if (strcmp(name, \"grab\") == 0) return GRAB_POINTER;\n    else if (strcmp(name, \"grabbing\") == 0) return GRABBING_POINTER;\n/* end pointer shapes */\n    return TEXT_POINTER;\n}\n\nstatic inline void\ndragging_pointer_shape(PyObject *parts, Options *opts) {\n    opts->pointer_shape_when_dragging = pointer_shape(PyTuple_GET_ITEM(parts, 0));\n    opts->pointer_shape_when_dragging_rectangle = pointer_shape(PyTuple_GET_ITEM(parts, 1));\n}\n\nstatic inline int\nmacos_colorspace(PyObject *csname) {\n    if (PyUnicode_CompareWithASCIIString(csname, \"srgb\") == 0) return 1;\n    if (PyUnicode_CompareWithASCIIString(csname, \"displayp3\") == 0) return 2;\n    return 0;\n}\n\nstatic inline void\nfree_url_prefixes(Options *opts) {\n    opts->url_prefixes.num = 0;\n    opts->url_prefixes.max_prefix_len = 0;\n    if (opts->url_prefixes.values) {\n        free(opts->url_prefixes.values);\n        opts->url_prefixes.values = NULL;\n    }\n}\n\nstatic inline void\nurl_prefixes(PyObject *up, Options *opts) {\n    if (!PyTuple_Check(up)) { PyErr_SetString(PyExc_TypeError, \"url_prefixes must be a tuple\"); return; }\n    free_url_prefixes(opts);\n    opts->url_prefixes.values = calloc(PyTuple_GET_SIZE(up), sizeof(UrlPrefix));\n    if (!opts->url_prefixes.values) { PyErr_NoMemory(); return; }\n    opts->url_prefixes.num = PyTuple_GET_SIZE(up);\n    for (size_t i = 0; i < opts->url_prefixes.num; i++) {\n        PyObject *t = PyTuple_GET_ITEM(up, i);\n        if (!PyUnicode_Check(t)) { PyErr_SetString(PyExc_TypeError, \"url_prefixes must be strings\"); return; }\n        opts->url_prefixes.values[i].len = MIN(arraysz(opts->url_prefixes.values[i].string) - 1, (size_t)PyUnicode_GET_LENGTH(t));\n        int kind = PyUnicode_KIND(t);\n        opts->url_prefixes.max_prefix_len = MAX(opts->url_prefixes.max_prefix_len, opts->url_prefixes.values[i].len);\n        for (size_t x = 0; x < opts->url_prefixes.values[i].len; x++) {\n            opts->url_prefixes.values[i].string[x] = PyUnicode_READ(kind, PyUnicode_DATA(t), x);\n        }\n    }\n}\n\nstatic inline void\nfree_menu_map(Options *opts) {\n    if (opts->global_menu.entries) {\n        for (size_t i=0; i < opts->global_menu.count; i++) {\n            struct MenuItem *e = opts->global_menu.entries + i;\n            if (e->definition) { free((void*)e->definition); }\n            if (e->location) {\n                for (size_t l=0; l < e->location_count; l++) { free((void*)e->location[l]); }\n                free(e->location);\n            }\n        }\n        free(opts->global_menu.entries); opts->global_menu.entries = NULL;\n    }\n    opts->global_menu.count = 0;\n}\n\nstatic inline void\nmenu_map(PyObject *entry_dict, Options *opts) {\n    if (!PyDict_Check(entry_dict)) { PyErr_SetString(PyExc_TypeError, \"menu_map entries must be a dict\"); return; }\n    free_menu_map(opts);\n    size_t maxnum = PyDict_Size(entry_dict);\n    opts->global_menu.count = 0;\n    opts->global_menu.entries = calloc(maxnum, sizeof(opts->global_menu.entries[0]));\n    if (!opts->global_menu.entries) { PyErr_NoMemory(); return; }\n\n    PyObject *key, *value;\n    Py_ssize_t pos = 0;\n\n    while (PyDict_Next(entry_dict, &pos, &key, &value)) {\n        if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) > 1 && PyUnicode_Check(value) && PyUnicode_CompareWithASCIIString(PyTuple_GET_ITEM(key, 0), \"global\") == 0) {\n            struct MenuItem *e = opts->global_menu.entries + opts->global_menu.count++;\n            e->location_count = PyTuple_GET_SIZE(key) - 1;\n            e->location = calloc(e->location_count, sizeof(e->location[0]));\n            if (!e->location) { PyErr_NoMemory(); return; }\n            e->definition = strdup(PyUnicode_AsUTF8(value));\n            if (!e->definition) { PyErr_NoMemory(); return; }\n            for (size_t i = 0; i < e->location_count; i++) {\n                e->location[i] = strdup(PyUnicode_AsUTF8(PyTuple_GET_ITEM(key, i+1)));\n                if (!e->location[i]) { PyErr_NoMemory(); return; }\n            }\n        }\n    }\n}\n\nstatic inline void\nunderline_exclusion(PyObject *val, Options *opts) {\n    if (!PyTuple_Check(val)) { PyErr_SetString(PyExc_TypeError, \"underline_exclusion must be a tuple\"); return; }\n    opts->underline_exclusion.thickness = PyFloat_AsFloat(PyTuple_GET_ITEM(val, 0));\n    if (!PyUnicode_GET_LENGTH(PyTuple_GET_ITEM(val, 1))) opts->underline_exclusion.unit = 0;\n    else if (PyUnicode_CompareWithASCIIString(PyTuple_GET_ITEM(val, 1), \"px\")) opts->underline_exclusion.unit = 1;\n    else if (PyUnicode_CompareWithASCIIString(PyTuple_GET_ITEM(val, 1), \"pt\")) opts->underline_exclusion.unit = 2;\n    else opts->underline_exclusion.unit = 0;\n}\n\nstatic inline void\nbox_drawing_scale(PyObject *val, Options *opts) {\n    for (unsigned i = 0; i < MIN(arraysz(opts->box_drawing_scale), (size_t)PyTuple_GET_SIZE(val)); i++) {\n        opts->box_drawing_scale[i] = PyFloat_AsFloat(PyTuple_GET_ITEM(val, i));\n    }\n}\n\nstatic inline void\ntext_composition_strategy(PyObject *val, Options *opts) {\n    if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, \"text_composition_strategy must be a string\"); return; }\n    opts->text_old_gamma = false;\n    opts->text_gamma_adjustment = 1.0f; opts->text_contrast = 0.f;\n    if (PyUnicode_CompareWithASCIIString(val, \"platform\") == 0) {\n#ifdef __APPLE__\n        opts->text_gamma_adjustment = 1.7f; opts->text_contrast = 30.f;\n#endif\n    }\n    else if (PyUnicode_CompareWithASCIIString(val, \"legacy\") == 0) {\n        opts->text_old_gamma = true;\n    } else {\n        RAII_PyObject(parts, PyUnicode_Split(val, NULL, 2));\n        int size = PyList_GET_SIZE(parts);\n        if (size < 1 || 2 < size) { PyErr_SetString(PyExc_ValueError, \"text_composition_strategy must be of the form 'number [number]'\"); return; }\n\n        if (size > 0) {\n            RAII_PyObject(ga, PyFloat_FromString(PyList_GET_ITEM(parts, 0)));\n            if (PyErr_Occurred()) return;\n            opts->text_gamma_adjustment = MAX(0.01f, PyFloat_AsFloat(ga));\n        }\n\n        if (size > 1) {\n            RAII_PyObject(contrast, PyFloat_FromString(PyList_GET_ITEM(parts, 1)));\n            if (PyErr_Occurred()) return;\n            opts->text_contrast = MAX(0.0f, PyFloat_AsFloat(contrast));\n            opts->text_contrast = MIN(100.0f, opts->text_contrast);\n        }\n    }\n}\n\nstatic char_type*\nlist_of_chars(PyObject *chars) {\n    if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, \"list_of_chars must be a string\"); return NULL; }\n    char_type *ans = calloc(PyUnicode_GET_LENGTH(chars) + 1, sizeof(char_type));\n    if (ans) {\n        for (ssize_t i = 0; i < PyUnicode_GET_LENGTH(chars); i++) {\n            ans[i] = PyUnicode_READ(PyUnicode_KIND(chars), PyUnicode_DATA(chars), i);\n        }\n    }\n    return ans;\n}\n\nstatic inline void\nurl_excluded_characters(PyObject *chars, Options *opts) {\n    free(opts->url_excluded_characters);\n    opts->url_excluded_characters = list_of_chars(chars);\n}\n\nstatic inline void\nselect_by_word_characters(PyObject *chars, Options *opts) {\n    free(opts->select_by_word_characters);\n    opts->select_by_word_characters = list_of_chars(chars);\n}\n\nstatic inline void\nselect_by_word_characters_forward(PyObject *chars, Options *opts) {\n    free(opts->select_by_word_characters_forward);\n    opts->select_by_word_characters_forward = list_of_chars(chars);\n}\n\nstatic inline void\ntab_bar_style(PyObject *val, Options *opts) {\n    opts->tab_bar_hidden = PyUnicode_CompareWithASCIIString(val, \"hidden\") == 0 ? true: false;\n}\n\nstatic inline void\ntab_bar_margin_height(PyObject *val, Options *opts) {\n    if (!PyTuple_Check(val) || PyTuple_GET_SIZE(val) != 2) {\n        PyErr_SetString(PyExc_TypeError, \"tab_bar_margin_height is not a 2-item tuple\");\n        return;\n    }\n    opts->tab_bar_margin_height.outer = PyFloat_AsDouble(PyTuple_GET_ITEM(val, 0));\n    opts->tab_bar_margin_height.inner = PyFloat_AsDouble(PyTuple_GET_ITEM(val, 1));\n}\n\nstatic inline void\nwindow_logo_scale(PyObject *src, Options *opts) {\n    opts->window_logo_scale.width = PyFloat_AsFloat(PyTuple_GET_ITEM(src, 0));\n    opts->window_logo_scale.height = PyFloat_AsFloat(PyTuple_GET_ITEM(src, 1));\n}\n\nstatic inline void\nresize_debounce_time(PyObject *src, Options *opts) {\n    opts->resize_debounce_time.on_end = s_double_to_monotonic_t(PyFloat_AsDouble(PyTuple_GET_ITEM(src, 0)));\n    opts->resize_debounce_time.on_pause = s_double_to_monotonic_t(PyFloat_AsDouble(PyTuple_GET_ITEM(src, 1)));\n}\n\nstatic inline void\nfree_allocs_in_options(Options *opts) {\n    free_menu_map(opts);\n    free_url_prefixes(opts);\n    free_font_features(opts);\n#define F(x) free(opts->x); opts->x = NULL;\n    F(select_by_word_characters); F(url_excluded_characters); F(select_by_word_characters_forward);\n    F(background_image); F(bell_path); F(bell_theme); F(default_window_logo);\n#undef F\n}\n"
  },
  {
    "path": "kitty/options/types.py",
    "content": "# generated by gen-config.py DO NOT edit\n\n# isort: skip_file\nimport typing\nimport collections.abc  # noqa: F401, RUF100\nfrom array import array\nfrom kitty.constants import is_macos\nimport kitty.constants\nfrom kitty.fast_data_types import Color, SingleKey\nimport kitty.fast_data_types\nfrom kitty.fonts import FontSpec\nimport kitty.fonts\nfrom kitty.options.utils import (\n    AliasMap, KeyDefinition, KeyboardModeMap, MouseHideWait, MouseMap, MouseMapping, NotifyOnCmdFinish,\n    TabBarMarginHeight\n)\nimport kitty.options.utils\nfrom kitty.types import FloatEdges\nimport kitty.types\n\nchoices_for_allow_cloning = typing.Literal['yes', 'y', 'true', 'no', 'n', 'false', 'ask']\nchoices_for_allow_remote_control = typing.Literal['password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true']\nchoices_for_background_image_layout = typing.Literal['mirror-tiled', 'scaled', 'tiled', 'clamped', 'centered', 'cscaled']\nchoices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'cell', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing']\nchoices_for_linux_display_server = typing.Literal['auto', 'wayland', 'x11']\nchoices_for_macos_colorspace = typing.Literal['srgb', 'default', 'displayp3']\nchoices_for_macos_show_window_title_in = typing.Literal['all', 'menubar', 'none', 'window']\nchoices_for_placement_strategy = typing.Literal['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right']\nchoices_for_pointer_shape_when_grabbed = choices_for_default_pointer_shape\nchoices_for_scrollbar = typing.Literal['scrolled', 'always', 'never', 'hovered', 'scrolled-and-hovered']\nchoices_for_strip_trailing_spaces = typing.Literal['always', 'never', 'smart']\nchoices_for_tab_bar_align = typing.Literal['left', 'center', 'right']\nchoices_for_tab_bar_style = typing.Literal['fade', 'hidden', 'powerline', 'separator', 'slant', 'custom']\nchoices_for_tab_powerline_style = typing.Literal['angled', 'round', 'slanted']\nchoices_for_tab_switch_strategy = typing.Literal['last', 'left', 'previous', 'right']\nchoices_for_terminfo_type = typing.Literal['path', 'direct', 'none']\nchoices_for_undercurl_style = typing.Literal['thin-sparse', 'thin-dense', 'thick-sparse', 'thick-dense']\nchoices_for_underline_hyperlinks = typing.Literal['hover', 'always', 'never']\nchoices_for_window_logo_position = choices_for_placement_strategy\nchoices_for_window_title_bar = typing.Literal['top', 'bottom']\nchoices_for_window_title_bar_align = choices_for_tab_bar_align\n\noption_names = (\n    'action_alias',\n    'active_border_color',\n    'active_tab_background',\n    'active_tab_font_style',\n    'active_tab_foreground',\n    'active_tab_title_template',\n    'active_window_title_template',\n    'allow_cloning',\n    'allow_hyperlinks',\n    'allow_remote_control',\n    'background',\n    'background_blur',\n    'background_image',\n    'background_image_layout',\n    'background_image_linear',\n    'background_opacity',\n    'background_tint',\n    'background_tint_gaps',\n    'bell_border_color',\n    'bell_on_tab',\n    'bell_path',\n    'bold_font',\n    'bold_italic_font',\n    'box_drawing_scale',\n    'clear_all_mouse_actions',\n    'clear_all_shortcuts',\n    'clear_selection_on_clipboard_loss',\n    'click_interval',\n    'clipboard_control',\n    'clipboard_max_size',\n    'clone_source_strategies',\n    'close_on_child_death',\n    'color0',\n    'color1',\n    'color2',\n    'color3',\n    'color4',\n    'color5',\n    'color6',\n    'color7',\n    'color8',\n    'color9',\n    'color10',\n    'color11',\n    'color12',\n    'color13',\n    'color14',\n    'color15',\n    'color16',\n    'color17',\n    'color18',\n    'color19',\n    'color20',\n    'color21',\n    'color22',\n    'color23',\n    'color24',\n    'color25',\n    'color26',\n    'color27',\n    'color28',\n    'color29',\n    'color30',\n    'color31',\n    'color32',\n    'color33',\n    'color34',\n    'color35',\n    'color36',\n    'color37',\n    'color38',\n    'color39',\n    'color40',\n    'color41',\n    'color42',\n    'color43',\n    'color44',\n    'color45',\n    'color46',\n    'color47',\n    'color48',\n    'color49',\n    'color50',\n    'color51',\n    'color52',\n    'color53',\n    'color54',\n    'color55',\n    'color56',\n    'color57',\n    'color58',\n    'color59',\n    'color60',\n    'color61',\n    'color62',\n    'color63',\n    'color64',\n    'color65',\n    'color66',\n    'color67',\n    'color68',\n    'color69',\n    'color70',\n    'color71',\n    'color72',\n    'color73',\n    'color74',\n    'color75',\n    'color76',\n    'color77',\n    'color78',\n    'color79',\n    'color80',\n    'color81',\n    'color82',\n    'color83',\n    'color84',\n    'color85',\n    'color86',\n    'color87',\n    'color88',\n    'color89',\n    'color90',\n    'color91',\n    'color92',\n    'color93',\n    'color94',\n    'color95',\n    'color96',\n    'color97',\n    'color98',\n    'color99',\n    'color100',\n    'color101',\n    'color102',\n    'color103',\n    'color104',\n    'color105',\n    'color106',\n    'color107',\n    'color108',\n    'color109',\n    'color110',\n    'color111',\n    'color112',\n    'color113',\n    'color114',\n    'color115',\n    'color116',\n    'color117',\n    'color118',\n    'color119',\n    'color120',\n    'color121',\n    'color122',\n    'color123',\n    'color124',\n    'color125',\n    'color126',\n    'color127',\n    'color128',\n    'color129',\n    'color130',\n    'color131',\n    'color132',\n    'color133',\n    'color134',\n    'color135',\n    'color136',\n    'color137',\n    'color138',\n    'color139',\n    'color140',\n    'color141',\n    'color142',\n    'color143',\n    'color144',\n    'color145',\n    'color146',\n    'color147',\n    'color148',\n    'color149',\n    'color150',\n    'color151',\n    'color152',\n    'color153',\n    'color154',\n    'color155',\n    'color156',\n    'color157',\n    'color158',\n    'color159',\n    'color160',\n    'color161',\n    'color162',\n    'color163',\n    'color164',\n    'color165',\n    'color166',\n    'color167',\n    'color168',\n    'color169',\n    'color170',\n    'color171',\n    'color172',\n    'color173',\n    'color174',\n    'color175',\n    'color176',\n    'color177',\n    'color178',\n    'color179',\n    'color180',\n    'color181',\n    'color182',\n    'color183',\n    'color184',\n    'color185',\n    'color186',\n    'color187',\n    'color188',\n    'color189',\n    'color190',\n    'color191',\n    'color192',\n    'color193',\n    'color194',\n    'color195',\n    'color196',\n    'color197',\n    'color198',\n    'color199',\n    'color200',\n    'color201',\n    'color202',\n    'color203',\n    'color204',\n    'color205',\n    'color206',\n    'color207',\n    'color208',\n    'color209',\n    'color210',\n    'color211',\n    'color212',\n    'color213',\n    'color214',\n    'color215',\n    'color216',\n    'color217',\n    'color218',\n    'color219',\n    'color220',\n    'color221',\n    'color222',\n    'color223',\n    'color224',\n    'color225',\n    'color226',\n    'color227',\n    'color228',\n    'color229',\n    'color230',\n    'color231',\n    'color232',\n    'color233',\n    'color234',\n    'color235',\n    'color236',\n    'color237',\n    'color238',\n    'color239',\n    'color240',\n    'color241',\n    'color242',\n    'color243',\n    'color244',\n    'color245',\n    'color246',\n    'color247',\n    'color248',\n    'color249',\n    'color250',\n    'color251',\n    'color252',\n    'color253',\n    'color254',\n    'color255',\n    'command_on_bell',\n    'confirm_os_window_close',\n    'copy_on_select',\n    'cursor',\n    'cursor_beam_thickness',\n    'cursor_blink_interval',\n    'cursor_shape',\n    'cursor_shape_unfocused',\n    'cursor_stop_blinking_after',\n    'cursor_text_color',\n    'cursor_trail',\n    'cursor_trail_color',\n    'cursor_trail_decay',\n    'cursor_trail_start_threshold',\n    'cursor_underline_thickness',\n    'default_pointer_shape',\n    'detect_urls',\n    'dim_opacity',\n    'disable_ligatures',\n    'draw_minimal_borders',\n    'draw_window_borders_for_single_window',\n    'dynamic_background_opacity',\n    'editor',\n    'enable_audio_bell',\n    'enabled_layouts',\n    'env',\n    'exe_search_path',\n    'file_transfer_confirmation_bypass',\n    'filter_notification',\n    'focus_follows_mouse',\n    'font_family',\n    'font_features',\n    'font_size',\n    'force_ltr',\n    'foreground',\n    'forward_stdio',\n    'hide_window_decorations',\n    'inactive_border_color',\n    'inactive_tab_background',\n    'inactive_tab_font_style',\n    'inactive_tab_foreground',\n    'inactive_text_alpha',\n    'initial_window_height',\n    'initial_window_width',\n    'input_delay',\n    'italic_font',\n    'kitten_alias',\n    'kitty_mod',\n    'linux_bell_theme',\n    'linux_display_server',\n    'listen_on',\n    'macos_colorspace',\n    'macos_custom_beam_cursor',\n    'macos_dock_badge_on_bell',\n    'macos_hide_from_tasks',\n    'macos_menubar_title_max_length',\n    'macos_option_as_alt',\n    'macos_quit_when_last_window_closed',\n    'macos_show_window_title_in',\n    'macos_thicken_font',\n    'macos_titlebar_color',\n    'macos_traditional_fullscreen',\n    'macos_window_resizable',\n    'map',\n    'map_timeout',\n    'mark1_background',\n    'mark1_foreground',\n    'mark2_background',\n    'mark2_foreground',\n    'mark3_background',\n    'mark3_foreground',\n    'menu_map',\n    'modify_font',\n    'momentum_scroll',\n    'mouse_hide_wait',\n    'mouse_map',\n    'narrow_symbols',\n    'notify_on_cmd_finish',\n    'open_url_with',\n    'paste_actions',\n    'pixel_scroll',\n    'placement_strategy',\n    'pointer_shape_when_dragging',\n    'pointer_shape_when_grabbed',\n    'remember_window_position',\n    'remember_window_size',\n    'remote_control_password',\n    'repaint_delay',\n    'resize_debounce_time',\n    'resize_in_steps',\n    'scrollback_fill_enlarged_window',\n    'scrollback_lines',\n    'scrollback_pager',\n    'scrollback_pager_history_size',\n    'scrollbar',\n    'scrollbar_gap',\n    'scrollbar_handle_color',\n    'scrollbar_handle_opacity',\n    'scrollbar_hitbox_expansion',\n    'scrollbar_hover_width',\n    'scrollbar_interactive',\n    'scrollbar_jump_on_click',\n    'scrollbar_min_handle_height',\n    'scrollbar_radius',\n    'scrollbar_track_color',\n    'scrollbar_track_hover_opacity',\n    'scrollbar_track_opacity',\n    'scrollbar_width',\n    'select_by_word_characters',\n    'select_by_word_characters_forward',\n    'selection_background',\n    'selection_foreground',\n    'shell',\n    'shell_integration',\n    'show_hyperlink_targets',\n    'single_window_margin_width',\n    'single_window_padding_width',\n    'startup_session',\n    'strip_trailing_spaces',\n    'symbol_map',\n    'sync_to_monitor',\n    'tab_activity_symbol',\n    'tab_bar_align',\n    'tab_bar_background',\n    'tab_bar_drag_threshold',\n    'tab_bar_edge',\n    'tab_bar_filter',\n    'tab_bar_margin_color',\n    'tab_bar_margin_height',\n    'tab_bar_margin_width',\n    'tab_bar_min_tabs',\n    'tab_bar_style',\n    'tab_fade',\n    'tab_powerline_style',\n    'tab_separator',\n    'tab_switch_strategy',\n    'tab_title_max_length',\n    'tab_title_template',\n    'term',\n    'terminfo_type',\n    'text_composition_strategy',\n    'text_fg_override_threshold',\n    'touch_scroll_multiplier',\n    'transparent_background_colors',\n    'undercurl_style',\n    'underline_exclusion',\n    'underline_hyperlinks',\n    'update_check_interval',\n    'url_color',\n    'url_excluded_characters',\n    'url_prefixes',\n    'url_style',\n    'visual_bell_color',\n    'visual_bell_duration',\n    'visual_window_select_characters',\n    'watcher',\n    'wayland_enable_ime',\n    'wayland_titlebar_color',\n    'wheel_scroll_min_lines',\n    'wheel_scroll_multiplier',\n    'window_alert_on_bell',\n    'window_border_width',\n    'window_drag_tolerance',\n    'window_logo_alpha',\n    'window_logo_path',\n    'window_logo_position',\n    'window_logo_scale',\n    'window_margin_width',\n    'window_padding_width',\n    'window_resize_step_cells',\n    'window_resize_step_lines',\n    'window_title_bar',\n    'window_title_bar_active_background',\n    'window_title_bar_active_foreground',\n    'window_title_bar_align',\n    'window_title_bar_inactive_background',\n    'window_title_bar_inactive_foreground',\n    'window_title_bar_min_windows',\n    'window_title_template',\n)\n\n\nclass Options:\n    active_border_color: kitty.fast_data_types.Color | None = Color(0, 255, 0)\n    active_tab_background: Color = Color(238, 238, 238)\n    active_tab_font_style: tuple[bool, bool] = (True, True)\n    active_tab_foreground: Color = Color(0, 0, 0)\n    active_tab_title_template: str | None = None\n    active_window_title_template: str = 'none'\n    allow_cloning: choices_for_allow_cloning = 'ask'\n    allow_hyperlinks: int = 1\n    allow_remote_control: choices_for_allow_remote_control = 'no'\n    background: Color = Color(0, 0, 0)\n    background_blur: int = 0\n    background_image: str | None = None\n    background_image_layout: choices_for_background_image_layout = 'tiled'\n    background_image_linear: bool = False\n    background_opacity: float = 1.0\n    background_tint: float = 0\n    background_tint_gaps: float = 1.0\n    bell_border_color: Color = Color(255, 90, 0)\n    bell_on_tab: str = '🔔 '\n    bell_path: str | None = None\n    bold_font: FontSpec = FontSpec(family=None, style=None, postscript_name=None, full_name=None, system='auto', axes=(), variable_name=None, features=(), created_from_string='auto')\n    bold_italic_font: FontSpec = FontSpec(family=None, style=None, postscript_name=None, full_name=None, system='auto', axes=(), variable_name=None, features=(), created_from_string='auto')\n    box_drawing_scale: tuple[float, float, float, float] = (0.001, 1.0, 1.5, 2.0)\n    clear_all_mouse_actions: bool = False\n    clear_all_shortcuts: bool = False\n    clear_selection_on_clipboard_loss: bool = False\n    click_interval: float = -1.0\n    clipboard_control: tuple[str, ...] = ('write-clipboard', 'write-primary', 'read-clipboard-ask', 'read-primary-ask')\n    clipboard_max_size: float = 512.0\n    clone_source_strategies: frozenset[str] = frozenset({'conda', 'env_var', 'path', 'venv'})\n    close_on_child_death: bool = False\n    command_on_bell: list[str] = ['none']\n    confirm_os_window_close: tuple[int, bool] = (-1, False)\n    copy_on_select: str = ''\n    cursor: kitty.fast_data_types.Color | None = Color(204, 204, 204)\n    cursor_beam_thickness: float = 1.5\n    cursor_blink_interval: tuple[float, kitty.options.utils.EasingFunction, kitty.options.utils.EasingFunction] = (-1.0, kitty.options.utils.EasingFunction(), kitty.options.utils.EasingFunction())\n    cursor_shape: int = 1\n    cursor_shape_unfocused: int = 4\n    cursor_stop_blinking_after: float = 15.0\n    cursor_text_color: kitty.fast_data_types.Color | None = Color(17, 17, 17)\n    cursor_trail: int = 0\n    cursor_trail_color: kitty.fast_data_types.Color | None = None\n    cursor_trail_decay: tuple[float, float] = (0.1, 0.4)\n    cursor_trail_start_threshold: int = 2\n    cursor_underline_thickness: float = 2.0\n    default_pointer_shape: choices_for_default_pointer_shape = 'beam'\n    detect_urls: bool = True\n    dim_opacity: float = 0.4\n    disable_ligatures: int = 0\n    draw_minimal_borders: bool = True\n    draw_window_borders_for_single_window: bool = False\n    dynamic_background_opacity: bool = False\n    editor: str = '.'\n    enable_audio_bell: bool = True\n    enabled_layouts: list[str] = ['fat', 'grid', 'horizontal', 'splits', 'stack', 'tall', 'vertical']\n    file_transfer_confirmation_bypass: str = ''\n    focus_follows_mouse: bool = False\n    font_family: FontSpec = FontSpec(family=None, style=None, postscript_name=None, full_name=None, system='monospace', axes=(), variable_name=None, features=(), created_from_string='monospace')\n    font_size: float = 11.0\n    force_ltr: bool = False\n    foreground: Color = Color(221, 221, 221)\n    forward_stdio: bool = False\n    hide_window_decorations: int = 0\n    inactive_border_color: Color = Color(204, 204, 204)\n    inactive_tab_background: Color = Color(153, 153, 153)\n    inactive_tab_font_style: tuple[bool, bool] = (False, False)\n    inactive_tab_foreground: Color = Color(68, 68, 68)\n    inactive_text_alpha: float = 1.0\n    initial_window_height: tuple[int, str] = (400, 'px')\n    initial_window_width: tuple[int, str] = (640, 'px')\n    input_delay: int = 3\n    italic_font: FontSpec = FontSpec(family=None, style=None, postscript_name=None, full_name=None, system='auto', axes=(), variable_name=None, features=(), created_from_string='auto')\n    kitty_mod: int = 5\n    linux_bell_theme: str = '__custom'\n    linux_display_server: choices_for_linux_display_server = 'auto'\n    listen_on: str = 'none'\n    macos_colorspace: choices_for_macos_colorspace = 'srgb'\n    macos_custom_beam_cursor: bool = False\n    macos_dock_badge_on_bell: bool = True\n    macos_hide_from_tasks: bool = False\n    macos_menubar_title_max_length: int = 0\n    macos_option_as_alt: int = 0\n    macos_quit_when_last_window_closed: bool = False\n    macos_show_window_title_in: choices_for_macos_show_window_title_in = 'all'\n    macos_thicken_font: float = 0\n    macos_titlebar_color: int = 0\n    macos_traditional_fullscreen: bool = False\n    macos_window_resizable: bool = True\n    map_timeout: float = 0\n    mark1_background: Color = Color(152, 211, 203)\n    mark1_foreground: Color = Color(0, 0, 0)\n    mark2_background: Color = Color(242, 220, 211)\n    mark2_foreground: Color = Color(0, 0, 0)\n    mark3_background: Color = Color(242, 116, 188)\n    mark3_foreground: Color = Color(0, 0, 0)\n    momentum_scroll: float = 0.96\n    mouse_hide_wait: MouseHideWait = MouseHideWait(hide_wait=0.0, show_wait=0.0, show_threshold=40, scroll_show=True) if is_macos else MouseHideWait(hide_wait=3.0, show_wait=0.0, show_threshold=40, scroll_show=True)\n    notify_on_cmd_finish: NotifyOnCmdFinish = NotifyOnCmdFinish(when='never', duration=5.0, action='notify', cmdline=(), clear_on=('focus', 'next'))\n    open_url_with: list[str] = ['default']\n    paste_actions: frozenset[str] = frozenset({'confirm', 'quote-urls-at-prompt'})\n    pixel_scroll: bool = True\n    placement_strategy: choices_for_placement_strategy = 'center'\n    pointer_shape_when_dragging: tuple[str, str] = ('beam', 'crosshair')\n    pointer_shape_when_grabbed: choices_for_pointer_shape_when_grabbed = 'arrow'\n    remember_window_position: bool = False\n    remember_window_size: bool = True\n    repaint_delay: int = 10\n    resize_debounce_time: tuple[float, float] = (0.1, 0.5)\n    resize_in_steps: bool = False\n    scrollback_fill_enlarged_window: bool = False\n    scrollback_lines: int = 2000\n    scrollback_pager: list[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER']\n    scrollback_pager_history_size: int = 0\n    scrollbar: choices_for_scrollbar = 'scrolled'\n    scrollbar_gap: float = 0.1\n    scrollbar_handle_color: int = 0\n    scrollbar_handle_opacity: float = 0.5\n    scrollbar_hitbox_expansion: float = 0.25\n    scrollbar_hover_width: float = 1.0\n    scrollbar_interactive: bool = True\n    scrollbar_jump_on_click: bool = True\n    scrollbar_min_handle_height: float = 1.0\n    scrollbar_radius: float = 0.3\n    scrollbar_track_color: int = 0\n    scrollbar_track_hover_opacity: float = 0.1\n    scrollbar_track_opacity: float = 0\n    scrollbar_width: float = 0.5\n    select_by_word_characters: str = '@-./_~?&=%+#'\n    select_by_word_characters_forward: str = ''\n    selection_background: kitty.fast_data_types.Color | None = Color(255, 250, 205)\n    selection_foreground: kitty.fast_data_types.Color | None = Color(0, 0, 0)\n    shell: str = '.'\n    shell_integration: frozenset[str] = frozenset({'enabled'})\n    show_hyperlink_targets: bool = False\n    single_window_margin_width: FloatEdges = FloatEdges(left=-1.0, top=-1.0, right=-1.0, bottom=-1.0)\n    single_window_padding_width: FloatEdges = FloatEdges(left=-1.0, top=-1.0, right=-1.0, bottom=-1.0)\n    startup_session: str | None = None\n    strip_trailing_spaces: choices_for_strip_trailing_spaces = 'never'\n    sync_to_monitor: bool = True\n    tab_activity_symbol: str = ''\n    tab_bar_align: choices_for_tab_bar_align = 'left'\n    tab_bar_background: kitty.fast_data_types.Color | None = None\n    tab_bar_drag_threshold: int = 5\n    tab_bar_edge: int = 8\n    tab_bar_filter: str = ''\n    tab_bar_margin_color: kitty.fast_data_types.Color | None = None\n    tab_bar_margin_height: TabBarMarginHeight = TabBarMarginHeight(outer=0, inner=0)\n    tab_bar_margin_width: float = 0\n    tab_bar_min_tabs: int = 2\n    tab_bar_style: choices_for_tab_bar_style = 'fade'\n    tab_fade: tuple[float, ...] = (0.25, 0.5, 0.75, 1.0)\n    tab_powerline_style: choices_for_tab_powerline_style = 'angled'\n    tab_separator: str = ' ┇'\n    tab_switch_strategy: choices_for_tab_switch_strategy = 'previous'\n    tab_title_max_length: int = 0\n    tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{tab.last_focused_progress_percent}{title}'\n    term: str = 'xterm-kitty'\n    terminfo_type: choices_for_terminfo_type = 'path'\n    text_composition_strategy: str = 'platform'\n    text_fg_override_threshold: tuple[float, typing.Literal['%', 'ratio']] = (0.0, '%')\n    touch_scroll_multiplier: float = 1.0\n    transparent_background_colors: tuple[tuple[kitty.fast_data_types.Color, float], ...] = ()\n    undercurl_style: choices_for_undercurl_style = 'thin-sparse'\n    underline_exclusion: tuple[float, typing.Literal['', 'px', 'pt']] = (1.0, '')\n    underline_hyperlinks: choices_for_underline_hyperlinks = 'hover'\n    update_check_interval: float = 24.0\n    url_color: Color = Color(0, 135, 189)\n    url_excluded_characters: str = ''\n    url_prefixes: tuple[str, ...] = ('file', 'ftp', 'ftps', 'gemini', 'git', 'gopher', 'http', 'https', 'irc', 'ircs', 'kitty', 'mailto', 'news', 'sftp', 'ssh')\n    url_style: int = 3\n    visual_bell_color: kitty.fast_data_types.Color | None = None\n    visual_bell_duration: tuple[float, kitty.options.utils.EasingFunction, kitty.options.utils.EasingFunction] = (0.0, kitty.options.utils.EasingFunction(), kitty.options.utils.EasingFunction())\n    visual_window_select_characters: str = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'\n    wayland_enable_ime: bool = True\n    wayland_titlebar_color: int = 0\n    wheel_scroll_min_lines: int = 1\n    wheel_scroll_multiplier: float = 5.0\n    window_alert_on_bell: bool = True\n    window_border_width: tuple[float, str] = (0.5, 'pt')\n    window_drag_tolerance: float = 2.0\n    window_logo_alpha: float = 0.5\n    window_logo_path: str | None = None\n    window_logo_position: choices_for_window_logo_position = 'bottom-right'\n    window_logo_scale: tuple[float, float] = (0, -1.0)\n    window_margin_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0)\n    window_padding_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0)\n    window_resize_step_cells: int = 2\n    window_resize_step_lines: int = 2\n    window_title_bar: choices_for_window_title_bar = 'top'\n    window_title_bar_active_background: kitty.fast_data_types.Color | None = None\n    window_title_bar_active_foreground: kitty.fast_data_types.Color | None = None\n    window_title_bar_align: choices_for_window_title_bar_align = 'center'\n    window_title_bar_inactive_background: kitty.fast_data_types.Color | None = None\n    window_title_bar_inactive_foreground: kitty.fast_data_types.Color | None = None\n    window_title_bar_min_windows: int = 0\n    window_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.window}{progress_percent}{title}'\n    action_alias: dict[str, str] = {}\n    env: dict[str, str] = {}\n    exe_search_path: dict[str, str] = {}\n    filter_notification: dict[str, str] = {}\n    font_features: dict[str, tuple[kitty.fast_data_types.ParsedFontFeature, ...]] = {}\n    kitten_alias: dict[str, str] = {}\n    menu_map: dict[tuple[str, ...], str] = {}\n    modify_font: dict[str, kitty.fonts.FontModification] = {}\n    narrow_symbols: dict[tuple[int, int], int] = {}\n    remote_control_password: dict[str, collections.abc.Sequence[str]] = {}\n    symbol_map: dict[tuple[int, int], str] = {}\n    watcher: dict[str, str] = {}\n    map: list[kitty.options.utils.KeyDefinition] = []\n    keyboard_modes: KeyboardModeMap = {}\n    alias_map: AliasMap = AliasMap()\n    mouse_map: list[kitty.options.utils.MouseMapping] = []\n    mousemap: MouseMap = {}\n    color_table: \"array[int]\" = array(\"L\", (\n        0x000000, 0xcc0403, 0x19cb00, 0xcecb00, 0x0d73cc, 0xcb1ed1, 0x0dcdcd, 0xdddddd,\n        0x767676, 0xf2201f, 0x23fd00, 0xfffd00, 0x1a8fff, 0xfd28ff, 0x14ffff, 0xffffff,\n        0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f,\n        0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af,\n        0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff,\n        0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f,\n        0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af,\n        0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff,\n        0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f,\n        0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af,\n        0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,\n        0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,\n        0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af,\n        0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff,\n        0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,\n        0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,\n        0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,\n        0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,\n        0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af,\n        0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff,\n        0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,\n        0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af,\n        0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,\n        0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,\n        0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af,\n        0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,\n        0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,\n        0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af,\n        0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff,\n        0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,\n        0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,\n        0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee,\n    ))\n    config_paths: tuple[str, ...] = ()\n    all_config_paths: tuple[str, ...] = ()\n    config_overrides: tuple[str, ...] = ()\n\n    def __init__(self, options_dict: dict[str, typing.Any] | None = None) -> None:\n        self.color_table = array(self.color_table.typecode, self.color_table)\n        if options_dict is not None:\n            null = object()\n            for key in option_names:\n                val = options_dict.get(key, null)\n                if val is not null:\n                    setattr(self, key, val)\n\n    @property\n    def _fields(self) -> tuple[str, ...]:\n        return option_names\n\n    def __iter__(self) -> typing.Iterator[str]:\n        return iter(self._fields)\n\n    def __len__(self) -> int:\n        return len(self._fields)\n\n    def _copy_of_val(self, name: str) -> typing.Any:\n        ans = getattr(self, name)\n        if isinstance(ans, dict):\n            ans = ans.copy()\n        elif isinstance(ans, list):\n            ans = ans[:]\n        return ans\n\n    def _asdict(self) -> dict[str, typing.Any]:\n        return {k: self._copy_of_val(k) for k in self}\n\n    def _replace(self, **kw: typing.Any) -> \"Options\":\n        ans = Options()\n        for name in self:\n            setattr(ans, name, self._copy_of_val(name))\n        for name, val in kw.items():\n            setattr(ans, name, val)\n        return ans\n\n    def __getitem__(self, key: int | str) -> typing.Any:\n        k = option_names[key] if isinstance(key, int) else key\n        try:\n            return getattr(self, k)\n        except AttributeError:\n            pass\n        raise KeyError(f\"No option named: {k}\")\n\n    def __getattr__(self, key: str) -> typing.Any:\n        if key.startswith(\"color\"):\n            q = key[5:]\n            if q.isdigit():\n                k = int(q)\n                if 0 <= k <= 255:\n                    x = self.color_table[k]\n                    return Color((x >> 16) & 255, (x >> 8) & 255, x & 255)\n        raise AttributeError(key)\n\n    def __setattr__(self, key: str, val: typing.Any) -> typing.Any:\n        if key.startswith(\"color\"):\n            q = key[5:]\n            if q.isdigit():\n                k = int(q)\n                if 0 <= k <= 255:\n                    self.color_table[k] = int(val)\n                    return\n        object.__setattr__(self, key, val)\n\n\ndefaults = Options()\n\ndefaults.action_alias = {}\ndefaults.env = {}\ndefaults.exe_search_path = {}\ndefaults.filter_notification = {}\ndefaults.font_features = {}\ndefaults.kitten_alias = {}\ndefaults.menu_map = {}\ndefaults.modify_font = {}\ndefaults.narrow_symbols = {}\ndefaults.remote_control_password = {}\ndefaults.symbol_map = {}\ndefaults.watcher = {}\n\ndefaults.map = [\n    # copy_to_clipboard\n    KeyDefinition(trigger=SingleKey(mods=256, key=99), definition='copy_to_clipboard'),\n    # paste_from_clipboard\n    KeyDefinition(trigger=SingleKey(mods=256, key=118), definition='paste_from_clipboard'),\n    # paste_from_selection\n    KeyDefinition(trigger=SingleKey(mods=256, key=115), definition='paste_from_selection'),\n    # paste_from_selection\n    KeyDefinition(trigger=SingleKey(mods=1, key=57348), definition='paste_from_selection'),\n    # pass_selection_to_program\n    KeyDefinition(trigger=SingleKey(mods=256, key=111), definition='pass_selection_to_program'),\n    # scroll_line_up\n    KeyDefinition(trigger=SingleKey(mods=256, key=57352), definition='scroll_line_up'),\n    # scroll_line_up\n    KeyDefinition(trigger=SingleKey(mods=256, key=107), definition='scroll_line_up'),\n    # scroll_line_down\n    KeyDefinition(trigger=SingleKey(mods=256, key=57353), definition='scroll_line_down'),\n    # scroll_line_down\n    KeyDefinition(trigger=SingleKey(mods=256, key=106), definition='scroll_line_down'),\n    # scroll_page_up\n    KeyDefinition(trigger=SingleKey(mods=256, key=57354), definition='scroll_page_up'),\n    # scroll_page_down\n    KeyDefinition(trigger=SingleKey(mods=256, key=57355), definition='scroll_page_down'),\n    # scroll_home\n    KeyDefinition(trigger=SingleKey(mods=256, key=57356), definition='scroll_home'),\n    # scroll_end\n    KeyDefinition(trigger=SingleKey(mods=256, key=57357), definition='scroll_end'),\n    # scroll_to_previous_prompt\n    KeyDefinition(trigger=SingleKey(mods=256, key=122), definition='scroll_to_prompt -1'),\n    # scroll_to_next_prompt\n    KeyDefinition(trigger=SingleKey(mods=256, key=120), definition='scroll_to_prompt 1'),\n    # show_scrollback\n    KeyDefinition(trigger=SingleKey(mods=256, key=104), definition='show_scrollback'),\n    # show_last_command_output\n    KeyDefinition(trigger=SingleKey(mods=256, key=103), definition='show_last_command_output'),\n    # search_scrollback\n    KeyDefinition(trigger=SingleKey(mods=256, key=47), definition='search_scrollback'),\n    # new_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=57345), definition='new_window'),\n    # new_os_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=110), definition='new_os_window'),\n    # close_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=119), definition='close_window'),\n    # next_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=93), definition='next_window'),\n    # previous_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=91), definition='previous_window'),\n    # move_window_forward\n    KeyDefinition(trigger=SingleKey(mods=256, key=102), definition='move_window_forward'),\n    # move_window_backward\n    KeyDefinition(trigger=SingleKey(mods=256, key=98), definition='move_window_backward'),\n    # move_window_to_top\n    KeyDefinition(trigger=SingleKey(mods=256, key=96), definition='move_window_to_top'),\n    # start_resizing_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=114), definition='start_resizing_window'),\n    # first_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=49), definition='first_window'),\n    # second_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=50), definition='second_window'),\n    # third_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=51), definition='third_window'),\n    # fourth_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=52), definition='fourth_window'),\n    # fifth_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=53), definition='fifth_window'),\n    # sixth_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=54), definition='sixth_window'),\n    # seventh_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=55), definition='seventh_window'),\n    # eighth_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=56), definition='eighth_window'),\n    # ninth_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=57), definition='ninth_window'),\n    # tenth_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=48), definition='tenth_window'),\n    # focus_visible_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=57370), definition='focus_visible_window'),\n    # swap_with_window\n    KeyDefinition(trigger=SingleKey(mods=256, key=57371), definition='swap_with_window'),\n    # next_tab\n    KeyDefinition(trigger=SingleKey(mods=256, key=57351), definition='next_tab'),\n    # next_tab\n    KeyDefinition(trigger=SingleKey(mods=4, key=57346), definition='next_tab'),\n    # previous_tab\n    KeyDefinition(trigger=SingleKey(mods=256, key=57350), definition='previous_tab'),\n    # previous_tab\n    KeyDefinition(trigger=SingleKey(mods=5, key=57346), definition='previous_tab'),\n    # new_tab\n    KeyDefinition(trigger=SingleKey(mods=256, key=116), definition='new_tab'),\n    # close_tab\n    KeyDefinition(trigger=SingleKey(mods=256, key=113), definition='close_tab'),\n    # move_tab_forward\n    KeyDefinition(trigger=SingleKey(mods=256, key=46), definition='move_tab_forward'),\n    # move_tab_backward\n    KeyDefinition(trigger=SingleKey(mods=256, key=44), definition='move_tab_backward'),\n    # set_tab_title\n    KeyDefinition(trigger=SingleKey(mods=258, key=116), definition='set_tab_title'),\n    # next_layout\n    KeyDefinition(trigger=SingleKey(mods=256, key=108), definition='next_layout'),\n    # increase_font_size\n    KeyDefinition(trigger=SingleKey(mods=256, key=61), definition='change_font_size all +2.0'),\n    # increase_font_size\n    KeyDefinition(trigger=SingleKey(mods=256, key=43), definition='change_font_size all +2.0'),\n    # increase_font_size\n    KeyDefinition(trigger=SingleKey(mods=256, key=57413), definition='change_font_size all +2.0'),\n    # decrease_font_size\n    KeyDefinition(trigger=SingleKey(mods=256, key=45), definition='change_font_size all -2.0'),\n    # decrease_font_size\n    KeyDefinition(trigger=SingleKey(mods=256, key=57412), definition='change_font_size all -2.0'),\n    # reset_font_size\n    KeyDefinition(trigger=SingleKey(mods=256, key=57347), definition='change_font_size all 0'),\n    # open_url\n    KeyDefinition(trigger=SingleKey(mods=256, key=101), definition='open_url_with_hints'),\n    # insert_selected_path\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(key=102),), definition='kitten hints --type path --program -'),\n    # open_selected_path\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(mods=1, key=102),), definition='kitten hints --type path'),\n    # insert_chosen_file\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(key=99),), definition='kitten choose-files'),\n    # insert_chosen_directory\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(key=100),), definition='kitten choose-files --mode=dir'),\n    # insert_selected_line\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(key=108),), definition='kitten hints --type line --program -'),\n    # insert_selected_word\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(key=119),), definition='kitten hints --type word --program -'),\n    # insert_selected_hash\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(key=104),), definition='kitten hints --type hash --program -'),\n    # goto_file_line\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(key=110),), definition='kitten hints --type linenum'),\n    # open_selected_hyperlink\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=112), rest=(SingleKey(key=121),), definition='kitten hints --type hyperlink'),\n    # show_kitty_doc\n    KeyDefinition(trigger=SingleKey(mods=256, key=57364), definition='show_kitty_doc overview'),\n    # command_palette\n    KeyDefinition(trigger=SingleKey(mods=256, key=57366), definition='command_palette'),\n    # toggle_fullscreen\n    KeyDefinition(trigger=SingleKey(mods=256, key=57374), definition='toggle_fullscreen'),\n    # toggle_maximized\n    KeyDefinition(trigger=SingleKey(mods=256, key=57373), definition='toggle_maximized'),\n    # input_unicode_character\n    KeyDefinition(trigger=SingleKey(mods=256, key=117), definition='kitten unicode_input'),\n    # edit_config_file\n    KeyDefinition(trigger=SingleKey(mods=256, key=57365), definition='edit_config_file'),\n    # kitty_shell\n    KeyDefinition(trigger=SingleKey(mods=256, key=57344), definition='kitty_shell window'),\n    # increase_background_opacity\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=97), rest=(SingleKey(key=109),), definition='set_background_opacity +0.1'),\n    # decrease_background_opacity\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=97), rest=(SingleKey(key=108),), definition='set_background_opacity -0.1'),\n    # full_background_opacity\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=97), rest=(SingleKey(key=49),), definition='set_background_opacity 1'),\n    # reset_background_opacity\n    KeyDefinition(is_sequence=True, trigger=SingleKey(mods=256, key=97), rest=(SingleKey(key=100),), definition='set_background_opacity default'),\n    # reset_terminal\n    KeyDefinition(trigger=SingleKey(mods=256, key=57349), definition='clear_terminal reset active'),\n    # reload_config_file\n    KeyDefinition(trigger=SingleKey(mods=256, key=57368), definition='load_config_file'),\n    # debug_config\n    KeyDefinition(trigger=SingleKey(mods=256, key=57369), definition='debug_config'),\n]\n\nif is_macos:\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=99), definition='copy_or_noop'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=118), definition='paste_from_clipboard'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=57354), definition='scroll_line_up'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=57352), definition='scroll_line_up'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=57355), definition='scroll_line_down'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=57353), definition='scroll_line_down'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=57354), definition='scroll_page_up'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=57355), definition='scroll_page_down'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=57356), definition='scroll_home'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=57357), definition='scroll_end'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=102), definition='search_scrollback'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=57345), definition='new_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=110), definition='new_os_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=100), definition='close_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=114), definition='start_resizing_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=49), definition='first_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=50), definition='second_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=51), definition='third_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=52), definition='fourth_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=53), definition='fifth_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=54), definition='sixth_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=55), definition='seventh_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=56), definition='eighth_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=57), definition='ninth_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=93), definition='next_tab'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=91), definition='previous_tab'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=116), definition='new_tab'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=119), definition='close_tab'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=119), definition='close_os_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=105), definition='set_tab_title'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=43), definition='change_font_size all +2.0'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=61), definition='change_font_size all +2.0'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=61), definition='change_font_size all +2.0'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=45), definition='change_font_size all -2.0'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=45), definition='change_font_size all -2.0'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=48), definition='change_font_size all 0'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=12, key=102), definition='toggle_fullscreen'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=115), definition='toggle_macos_secure_keyboard_entry'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=96), definition='macos_cycle_through_os_windows'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=96), definition='macos_cycle_through_os_windows_backwards'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=12, key=32), definition='kitten unicode_input'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=44), definition='edit_config_file'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=114), definition='clear_terminal reset active'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=107), definition='clear_terminal to_cursor active'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=107), definition='clear_terminal scrollback active'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=108), definition='clear_terminal last_command active'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=12, key=108), definition='clear_terminal to_cursor_scroll active'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=12, key=44), definition='load_config_file'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=44), definition='debug_config'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=9, key=47), definition='open_url https://sw.kovidgoyal.net/kitty/'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=104), definition='hide_macos_app'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=10, key=104), definition='hide_macos_other_apps'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=109), definition='minimize_macos_window'))\n    defaults.map.append(KeyDefinition(trigger=SingleKey(mods=8, key=113), definition='quit'))\n\ndefaults.mouse_map = [\n    # click_url_or_select\n    MouseMapping(repeat_count=-2, definition='mouse_handle_click selection link prompt'),\n    # click_url_or_select_grabbed\n    MouseMapping(mods=1, repeat_count=-2, grabbed=True, definition='mouse_handle_click selection link prompt'),\n    # click_url_or_select_grabbed\n    MouseMapping(mods=1, repeat_count=-2, definition='mouse_handle_click selection link prompt'),\n    # click_url\n    MouseMapping(mods=5, repeat_count=-1, grabbed=True, definition='mouse_handle_click link'),\n    # click_url\n    MouseMapping(mods=5, repeat_count=-1, definition='mouse_handle_click link'),\n    # click_url_discard\n    MouseMapping(mods=5, grabbed=True, definition='discard_event'),\n    # paste_selection\n    MouseMapping(button=2, repeat_count=-1, definition='paste_from_selection'),\n    # start_simple_selection\n    MouseMapping(definition='mouse_selection normal'),\n    # start_rectangle_selection\n    MouseMapping(mods=6, definition='mouse_selection rectangle'),\n    # select_word\n    MouseMapping(repeat_count=2, definition='mouse_selection word'),\n    # select_line\n    MouseMapping(repeat_count=3, definition='mouse_selection line'),\n    # select_line_from_point\n    MouseMapping(mods=6, repeat_count=3, definition='mouse_selection line_from_point'),\n    # extend_selection\n    MouseMapping(button=1, definition='mouse_selection extend'),\n    # extend_selection_shift\n    MouseMapping(mods=1, definition='mouse_selection extend'),\n    # paste_selection_grabbed\n    MouseMapping(button=2, mods=1, repeat_count=-1, grabbed=True, definition='paste_selection'),\n    # paste_selection_grabbed\n    MouseMapping(button=2, mods=1, repeat_count=-1, definition='paste_selection'),\n    # paste_selection_grabbed\n    MouseMapping(button=2, mods=1, grabbed=True, definition='discard_event'),\n    # start_simple_selection_grabbed\n    MouseMapping(mods=1, grabbed=True, definition='mouse_selection normal'),\n    # start_rectangle_selection_grabbed\n    MouseMapping(mods=7, grabbed=True, definition='mouse_selection rectangle'),\n    # start_rectangle_selection_grabbed\n    MouseMapping(mods=7, definition='mouse_selection rectangle'),\n    # select_word_grabbed\n    MouseMapping(mods=1, repeat_count=2, grabbed=True, definition='mouse_selection word'),\n    # select_word_grabbed\n    MouseMapping(mods=1, repeat_count=2, definition='mouse_selection word'),\n    # select_line_grabbed\n    MouseMapping(mods=1, repeat_count=3, grabbed=True, definition='mouse_selection line'),\n    # select_line_grabbed\n    MouseMapping(mods=1, repeat_count=3, definition='mouse_selection line'),\n    # select_line_from_point_grabbed\n    MouseMapping(mods=7, repeat_count=3, grabbed=True, definition='mouse_selection line_from_point'),\n    # select_line_from_point_grabbed\n    MouseMapping(mods=7, repeat_count=3, definition='mouse_selection line_from_point'),\n    # extend_selection_grabbed\n    MouseMapping(button=1, mods=1, grabbed=True, definition='mouse_selection extend'),\n    # extend_selection_grabbed\n    MouseMapping(button=1, mods=1, definition='mouse_selection extend'),\n    # show_clicked_cmd_output_ungrabbed\n    MouseMapping(button=1, mods=5, definition='mouse_show_command_output'),\n]\n\n\nnullable_colors = frozenset({\n    'cursor',\n    'cursor_text_color',\n    'cursor_trail_color',\n    'visual_bell_color',\n    'active_border_color',\n    'window_title_bar_active_foreground',\n    'window_title_bar_active_background',\n    'window_title_bar_inactive_foreground',\n    'window_title_bar_inactive_background',\n    'tab_bar_background',\n    'tab_bar_margin_color',\n    'selection_foreground',\n    'selection_background'\n})\n\nspecial_colors = frozenset({\n    'scrollbar_handle_color',\n    'scrollbar_track_color',\n    'wayland_titlebar_color',\n    'macos_titlebar_color'\n})\n\n\nsecret_options = ('remote_control_password', 'file_transfer_confirmation_bypass')"
  },
  {
    "path": "kitty/options/utils.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport enum\nimport re\nimport sys\nfrom collections import defaultdict\nfrom collections.abc import Callable, Container, Iterable, Iterator, Sequence\nfrom contextlib import suppress\nfrom dataclasses import dataclass, fields\nfrom functools import lru_cache\nfrom typing import (\n    Any,\n    Generic,\n    Literal,\n    NamedTuple,\n    TypeVar,\n    cast,\n    get_args,\n)\n\nimport kitty.fast_data_types as defines\nfrom kitty.conf.utils import (\n    CurrentlyParsing,\n    KeyAction,\n    KeyFuncWrapper,\n    currently_parsing,\n    number_with_unit,\n    percent,\n    positive_float,\n    positive_int,\n    python_string,\n    to_bool,\n    to_cmdline,\n    to_color,\n    uniq,\n    unit_float,\n)\nfrom kitty.constants import is_macos\nfrom kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_HOLLOW, CURSOR_UNDERLINE, NO_CURSOR_SHAPE, Color, Shlex, SingleKey\nfrom kitty.fonts import FontModification, FontSpec, ModificationType, ModificationUnit, ModificationValue\nfrom kitty.key_names import character_key_name_aliases, functional_key_name_aliases, get_key_name_lookup\nfrom kitty.rgb import color_as_int\nfrom kitty.types import FloatEdges, MouseEvent\nfrom kitty.utils import expandvars, log_error, resolve_abs_or_config_path, shlex_split\n\nKeyMap = dict[SingleKey, list['KeyDefinition']]\nMouseMap = dict[MouseEvent, str]\nKeySequence = tuple[SingleKey, ...]\nMINIMUM_FONT_SIZE = 4\ndefault_tab_separator = ' ┇'\nmod_map = {'⌃': 'CONTROL', 'CTRL': 'CONTROL', '⇧': 'SHIFT', '⌥': 'ALT', 'OPTION': 'ALT', 'OPT': 'ALT',\n           '⌘': 'SUPER', 'COMMAND': 'SUPER', 'CMD': 'SUPER', 'KITTY_MOD': 'KITTY'}\ncharacter_key_name_aliases_with_ascii_lowercase: dict[str, str] = character_key_name_aliases.copy()\nfor x in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':\n    character_key_name_aliases_with_ascii_lowercase[x] = x.lower()\nsequence_sep = '>'\nmouse_button_map = {'left': 'b1', 'middle': 'b3', 'right': 'b2'}\nmouse_trigger_count_map = {'doubleclick': -3, 'click': -2, 'release': -1, 'press': 1, 'doublepress': 2, 'triplepress': 3}\nFuncArgsType = tuple[str, Sequence[Any]]\nfunc_with_args = KeyFuncWrapper[FuncArgsType]()\nDELETE_ENV_VAR = '_delete_this_env_var_'\n\n\nclass MapType(enum.Enum):\n    MAP = 'map'\n    MOUSE_MAP = 'mouse_map'\n    OPEN_ACTION = 'open_action'\n\n\nclass InvalidMods(ValueError):\n    pass\n\n\n# Actions {{{\n@func_with_args(\n    'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',\n    'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd',\n    'launch', 'mouse_handle_click', 'show_error', 'goto_session', 'save_as_session',\n    'close_session',\n    )\ndef shlex_parse(func: str, rest: str) -> FuncArgsType:\n    return func, to_cmdline(rest)\n\n\ndef parse_send_text_bytes(text: str) -> bytes:\n    return defines.expand_ansi_c_escapes(text).encode('utf-8')\n\n\n@func_with_args('scroll_prompt_to_top')\ndef scroll_prompt_to_top(func: str, rest: str) -> FuncArgsType:\n    return func, [to_bool(rest) if rest else False]\n\n\n@func_with_args('send_text')\ndef send_text_parse(func: str, rest: str) -> FuncArgsType:\n    args = rest.split(maxsplit=1)\n    mode = ''\n    data = b''\n    if len(args) > 1:\n        mode = args[0]\n        try:\n            data = parse_send_text_bytes(args[1])\n        except Exception:\n            log_error('Ignoring invalid send_text string: ' + args[1])\n    return func, [mode, data]\n\n\n@func_with_args('send_key')\ndef send_key(func: str, rest: str) -> FuncArgsType:\n    return func, rest.split()\n\n\n@func_with_args('run_kitten', 'run_simple_kitten', 'kitten')\ndef kitten_parse(func: str, rest: str) -> FuncArgsType:\n    parts = to_cmdline(rest)\n    if func == 'kitten':\n        return func, parts\n    return 'kitten', parts[1:]\n\n\n@func_with_args('open_url')\ndef open_url_parse(func: str, rest: str) -> FuncArgsType:\n    from urllib.parse import urlparse\n    url = ''\n    try:\n        url = python_string(rest)\n        tokens = urlparse(url)\n        if not all((tokens.scheme, tokens.netloc,)):\n            raise ValueError('Invalid URL')\n    except Exception:\n        log_error('Ignoring invalid URL string: ' + rest)\n    return func, (url,)\n\n\n@func_with_args('goto_tab')\ndef goto_tab_parse(func: str, rest: str) -> FuncArgsType:\n    n = int(rest)\n    if n < 0:\n        n += 1  # goto_tab subtracts 1 from its argument, this maps both zero and -1 to previous tab for backwards compat.\n    return func, (n,)\n\n\n@func_with_args('detach_window')\ndef detach_window_parse(func: str, rest: str) -> FuncArgsType:\n    if rest not in ('new', 'new-tab', 'new-tab-left', 'new-tab-right', 'ask', 'tab-prev', 'tab-left', 'tab-right'):\n        log_error(f'Ignoring invalid detach_window argument: {rest}')\n        rest = 'new'\n    return func, (rest,)\n\n\n@func_with_args('close_window_with_confirmation')\ndef close_window_with_confirmation(func: str, rest: str) -> FuncArgsType:\n    ignore_shell = rest == 'ignore-shell'\n    return func, (ignore_shell,)\n\n\n@func_with_args('detach_tab')\ndef detach_tab_parse(func: str, rest: str) -> FuncArgsType:\n    if rest not in ('new', 'ask'):\n        log_error(f'Ignoring invalid detach_tab argument: {rest}')\n        rest = 'new'\n    return func, (rest,)\n\n\n@func_with_args(\n    'set_background_opacity', 'goto_layout', 'toggle_layout', 'toggle_tab', 'kitty_shell', 'show_kitty_doc',\n    'set_tab_title', 'push_keyboard_mode', 'dump_lines_with_attrs', 'set_window_title', 'simulate_color_scheme_preference_change',\n)\ndef simple_parse(func: str, rest: str) -> FuncArgsType:\n    return func, (rest,)\n\n\n@func_with_args('set_font_size')\ndef float_parse(func: str, rest: str) -> FuncArgsType:\n    return func, (float(rest),)\n\n\n@func_with_args('signal_child')\ndef signal_child_parse(func: str, rest: str) -> FuncArgsType:\n    import signal\n    signals = []\n    for q in rest.split():\n        try:\n            signum = getattr(signal, q.upper())\n        except AttributeError:\n            log_error(f'Unknown signal: {rest} ignoring')\n        else:\n            signals.append(signum)\n    return func, tuple(signals)\n\n\n@func_with_args('change_font_size')\ndef parse_change_font_size(func: str, rest: str) -> tuple[str, tuple[bool, str | None, float]]:\n    vals = rest.strip().split(maxsplit=1)\n    if len(vals) != 2:\n        log_error(f'Invalid change_font_size specification: {rest}, treating it as default')\n        return func, (True, None, 0)\n    c_all = vals[0].lower() == 'all'\n    sign: str | None = None\n    amt = vals[1]\n    if amt[0] in '+-*/':\n        sign = amt[0]\n        amt = amt[1:]\n    return func, (c_all, sign, float(amt.strip()))\n\n\n@func_with_args('clear_terminal')\ndef clear_terminal(func: str, rest: str) -> FuncArgsType:\n    vals = rest.strip().split(maxsplit=1)\n    if len(vals) != 2:\n        log_error('clear_terminal needs two arguments, using defaults')\n        args = ['reset', True]\n    else:\n        action = vals[0].lower()\n        if action not in ('reset', 'scroll', 'scrollback', 'clear', 'to_cursor', 'to_cursor_scroll', 'last_command'):\n            log_error(f'{action} is unknown for clear_terminal, using reset')\n            action = 'reset'\n        args = [action, vals[1].lower() == 'active']\n    return func, args\n\n\n@func_with_args('copy_to_buffer')\ndef copy_to_buffer(func: str, rest: str) -> FuncArgsType:\n    return func, [rest]\n\n\n@func_with_args('paste_from_buffer')\ndef paste_from_buffer(func: str, rest: str) -> FuncArgsType:\n    return func, [rest]\n\n\n@func_with_args('paste')\ndef paste_parse(func: str, rest: str) -> FuncArgsType:\n    text = ''\n    try:\n        text = defines.expand_ansi_c_escapes(rest)\n    except Exception:\n        log_error('Ignoring invalid paste string: ' + rest)\n    return func, [text]\n\n\n@func_with_args('neighboring_window')\ndef neighboring_window(func: str, rest: str) -> FuncArgsType:\n    rest = rest.lower()\n    rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)\n    if rest not in ('left', 'right', 'top', 'bottom'):\n        log_error(f'Invalid neighbor specification: {rest}')\n        rest = 'right'\n    return func, [rest]\n\n\n@func_with_args('resize_window')\ndef resize_window(func: str, rest: str) -> FuncArgsType:\n    vals = rest.strip().split(maxsplit=1)\n    if len(vals) > 2:\n        log_error('resize_window needs one or two arguments, using defaults')\n        args = ['wider', 1]\n    else:\n        quality = vals[0].lower()\n        if quality not in ('reset', 'taller', 'shorter', 'wider', 'narrower'):\n            log_error(f'Invalid quality specification: {quality}')\n            quality = 'wider'\n        increment = 1\n        if len(vals) == 2:\n            try:\n                increment = int(vals[1])\n            except Exception:\n                log_error(f'Invalid increment specification: {vals[1]}')\n        args = [quality, increment]\n    return func, args\n\n\n@func_with_args('move_window')\ndef move_window(func: str, rest: str) -> FuncArgsType:\n    rest = rest.lower()\n    rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)\n    prest: int | str = rest\n    try:\n        prest = int(prest)\n    except Exception:\n        if prest not in ('left', 'right', 'top', 'bottom'):\n            log_error(f'Invalid move_window specification: {rest}')\n            prest = 0\n    return func, [prest]\n\n\n@func_with_args('pipe')\ndef pipe(func: str, rest: str) -> FuncArgsType:\n    r = list(shlex_split(rest))\n    if len(r) < 3:\n        log_error('Too few arguments to pipe function')\n        r = ['none', 'none', 'true']\n    return func, r\n\n\n@func_with_args('set_colors')\ndef set_colors(func: str, rest: str) -> FuncArgsType:\n    r = list(shlex_split(rest))\n    if len(r) < 1:\n        log_error('Too few arguments to set_colors function')\n    return func, r\n\n\n@func_with_args('remote_control')\ndef remote_control(func: str, rest: str) -> FuncArgsType:\n    func, args = shlex_parse(func, rest)\n    if len(args) < 1:\n        log_error('Too few arguments to remote_control function')\n    return func, args\n\n\n@func_with_args('remote_control_script')\ndef remote_control_script(func: str, rest: str) -> FuncArgsType:\n    func, args = shlex_parse(func, rest)\n    if len(args) < 1:\n        log_error('Too few arguments to remote_control_script function')\n    return func, args\n\n\n@func_with_args('nth_os_window', 'nth_window', 'visual_window_select_action_trigger', 'next_layout')\ndef single_integer_arg(func: str, rest: str) -> FuncArgsType:\n    try:\n        num = int(rest)\n    except Exception:\n        if rest:\n            log_error(f'Invalid number for {func}: {rest}')\n        num = 1\n    return func, [num]\n\n\n@func_with_args('scroll_to_prompt')\ndef scroll_to_prompt(func: str, rest: str) -> FuncArgsType:\n    vals = rest.strip().split()\n    args = [-1, 0]\n    if len(vals) > 2:\n        log_error('scroll_to_prompt needs one or two arguments, using defaults')\n    else:\n        try:\n            args[0] = int(vals[0])\n        except Exception:\n            log_error(f'{vals[0]} is not a valid number of prompts to jump for scroll_to_prompt')\n        if len(vals) == 2:\n            try:\n                args[1] = int(vals[1])\n            except Exception:\n                log_error(f'{vals[1]} is not a valid scroll offset for scroll_to_prompt')\n    return func, args\n\n\n@func_with_args('sleep')\ndef sleep(func: str, sleep_time: str) -> FuncArgsType:\n    mult = 1\n    sleep_time = sleep_time or '1'\n    if sleep_time[-1] in 'shmd':\n        mult = {'s': 1, 'm': 60, 'h': 3600, 'd': 24 * 3600}[sleep_time[-1]]\n        sleep_time = sleep_time[:-1]\n    return func, [abs(float(sleep_time)) * mult]\n\n\n@func_with_args('disable_ligatures_in')\ndef disable_ligatures_in(func: str, rest: str) -> FuncArgsType:\n    parts = rest.split(maxsplit=1)\n    if len(parts) == 1:\n        where, strategy = 'active', parts[0]\n    else:\n        where, strategy = parts\n    if where not in ('active', 'all', 'tab'):\n        raise ValueError(f'{where} is not a valid set of windows to disable ligatures in')\n    if strategy not in ('never', 'always', 'cursor'):\n        raise ValueError(f'{strategy} is not a valid disable ligatures strategy')\n    return func, [where, strategy]\n\n\n@func_with_args('layout_action')\ndef layout_action(func: str, rest: str) -> FuncArgsType:\n    parts = rest.split(maxsplit=1)\n    if not parts:\n        raise ValueError('layout_action must have at least one argument')\n    return func, [parts[0], tuple(parts[1:])]\n\n\ndef parse_marker_spec(ftype: str, parts: Sequence[str]) -> tuple[str, str | tuple[tuple[int, str], ...], int]:\n    flags = re.UNICODE\n    if ftype in ('text', 'itext', 'regex', 'iregex'):\n        if ftype.startswith('i'):\n            flags |= re.IGNORECASE\n        if not parts or len(parts) % 2 != 0:\n            raise ValueError('Mark group number and text/regex are not specified in pairs: {}'.format(' '.join(parts)))\n        ans = []\n        for i in range(0, len(parts), 2):\n            try:\n                color = max(1, min(int(parts[i]), 3))\n            except Exception:\n                raise ValueError(f'Mark group in marker specification is not an integer: {parts[i]}')\n            sspec = parts[i + 1]\n            if 'regex' not in ftype:\n                sspec = re.escape(sspec)\n            ans.append((color, sspec))\n        ftype = 'regex'\n        spec: str | tuple[tuple[int, str], ...] = tuple(ans)\n    elif ftype == 'function':\n        spec = ' '.join(parts)\n    else:\n        raise ValueError(f'Unknown marker type: {ftype}')\n    return ftype, spec, flags\n\n\n@func_with_args('toggle_marker')\ndef toggle_marker(func: str, rest: str) -> FuncArgsType:\n    parts = rest.split(maxsplit=1)\n    if len(parts) != 2:\n        raise ValueError(f'{rest} is not a valid marker specification')\n    ftype, spec = parts\n    parts = list(shlex_split(spec))\n    return func, list(parse_marker_spec(ftype, parts))\n\n\n@func_with_args('scroll_to_mark')\ndef scroll_to_mark(func: str, rest: str) -> FuncArgsType:\n    parts = rest.split()\n    if not parts or not rest:\n        return func, [True, 0]\n    if len(parts) == 1:\n        q = parts[0].lower()\n        if q in ('prev', 'previous', 'next'):\n            return func, [q != 'next', 0]\n        try:\n            return func, [True, max(0, min(int(q), 3))]\n        except Exception:\n            raise ValueError(f'{rest} is not a valid scroll_to_mark destination')\n    return func, [parts[0] != 'next', max(0, min(int(parts[1]), 3))]\n\n\n@func_with_args('mouse_selection')\ndef mouse_selection(func: str, rest: str) -> FuncArgsType:\n    cmap = getattr(mouse_selection, 'code_map', None)\n    if cmap is None:\n        cmap = {\n            'normal': defines.MOUSE_SELECTION_NORMAL,\n            'extend': defines.MOUSE_SELECTION_EXTEND,\n            'move-end': defines.MOUSE_SELECTION_MOVE_END,\n            'rectangle': defines.MOUSE_SELECTION_RECTANGLE,\n            'word': defines.MOUSE_SELECTION_WORD,\n            'line': defines.MOUSE_SELECTION_LINE,\n            'line_from_point': defines.MOUSE_SELECTION_LINE_FROM_POINT,\n            'word_and_line_from_point': defines.MOUSE_SELECTION_WORD_AND_LINE_FROM_POINT,\n            'upto_surrounding_whitespace': defines.MOUSE_SELECTION_UPTO_SURROUNDING_WHITESPACE,\n        }\n        setattr(mouse_selection, 'code_map', cmap)\n    return func, [cmap[rest]]\n\n\n@func_with_args('load_config_file')\ndef load_config_file(func: str, rest: str) -> FuncArgsType:\n    return func, list(shlex_split(rest))\n# }}}\n\n\ndef parse_mods(parts: Iterable[str], sc: str) -> int | None:\n\n    def map_mod(m: str) -> str:\n        return mod_map.get(m, m)\n\n    mods = 0\n    for m in parts:\n        try:\n            mods |= getattr(defines, f'GLFW_MOD_{map_mod(m.upper())}')\n        except AttributeError:\n            if m.upper() != 'NONE':\n                log_error(f'Shortcut: {sc} has unknown modifier, ignoring')\n            return None\n\n    return mods\n\n\ndef to_modifiers(val: str) -> int:\n    return parse_mods(val.split('+'), val) or 0\n\n\ndef parse_shortcut(sc: str) -> SingleKey:\n    if sc.endswith('+') and len(sc) > 1:\n        sc = f'{sc[:-1]}plus'\n    parts = sc.split('+')\n    mods = 0\n    if len(parts) > 1:\n        mods = parse_mods(parts[:-1], sc) or 0\n        if not mods:\n            raise InvalidMods('Invalid shortcut')\n    q = parts[-1]\n    q = character_key_name_aliases_with_ascii_lowercase.get(q.upper(), q)\n    is_native = False\n    if q.startswith('0x'):\n        try:\n            key = int(q, 16)\n        except Exception:\n            key = 0\n        else:\n            is_native = True\n    else:\n        try:\n            key = ord(q)\n        except Exception:\n            uq = q.upper()\n            uq = functional_key_name_aliases.get(uq, uq)\n            x: int | None = getattr(defines, f'GLFW_FKEY_{uq}', None)\n            if x is None:\n                lf = get_key_name_lookup()\n                key = lf(q, False) or 0\n                is_native = key > 0\n            else:\n                key = x\n\n    return SingleKey(mods, is_native, key or 0)\n\n\ndef to_font_size(x: str) -> float:\n    return max(MINIMUM_FONT_SIZE, float(x))\n\n\ndef disable_ligatures(x: str) -> int:\n    cmap = {'never': 0, 'cursor': 1, 'always': 2}\n    return cmap.get(x.lower(), 0)\n\n\ndef box_drawing_scale(x: str) -> tuple[float, float, float, float]:\n    ans = tuple(float(q.strip()) for q in x.split(','))\n    if len(ans) != 4:\n        raise ValueError('Invalid box_drawing scale, must have four entries')\n    return ans[0], ans[1], ans[2], ans[3]\n\n\ndef cursor_text_color(x: str) -> Color | None:\n    if x.lower() == 'background':\n        return None\n    return to_color(x)\n\n\ncshapes = {\n    'block': CURSOR_BLOCK,\n    'beam': CURSOR_BEAM,\n    'underline': CURSOR_UNDERLINE\n}\ncshapes_unfocused = {\n    'block': CURSOR_BLOCK,\n    'beam': CURSOR_BEAM,\n    'underline': CURSOR_UNDERLINE,\n    'hollow': CURSOR_HOLLOW,\n    'unchanged': NO_CURSOR_SHAPE,\n}\n\n\ndef to_cursor_shape(x: str) -> int:\n    try:\n        return cshapes[x.lower()]\n    except KeyError:\n        raise ValueError(\n            'Invalid cursor shape: {} allowed values are {}'.format(\n                x, ', '.join(cshapes)\n            )\n        )\n\n\ndef to_cursor_unfocused_shape(x: str) -> int:\n    try:\n        return cshapes_unfocused[x.lower()]\n    except KeyError:\n        raise ValueError(\n            'Invalid unfocused cursor shape: {} allowed values are {}'.format(\n                x, ', '.join(cshapes_unfocused)\n            )\n        )\n\ndef cursor_trail_decay(x: str) -> tuple[float, float]:\n    fast, slow = map(positive_float, x.split())\n    slow = max(slow, fast)\n    return fast, slow\n\ndef scrollback_lines(x: str) -> int:\n    ans = int(x)\n    if ans < 0:\n        ans = 2 ** 32 - 1\n    return ans\n\n\ndef scrollback_pager_history_size(x: str) -> int:\n    ans = int(max(0, float(x)) * 1024 * 1024)\n    return min(ans, 4096 * 1024 * 1024 - 1)\n\n\n# \"single\" for backwards compat\nurl_style_map = {'none': 0, 'single': 1, 'straight': 1, 'double': 2, 'curly': 3, 'dotted': 4, 'dashed': 5}\n\n\ndef url_style(x: str) -> int:\n    return url_style_map.get(x, url_style_map['curly'])\n\n\ndef url_prefixes(x: str) -> tuple[str, ...]:\n    return tuple(a.lower() for a in x.replace(',', ' ').split())\n\n\ndef copy_on_select(raw: str) -> str:\n    q = raw.lower()\n    # boolean values special cased for backwards compat\n    if q in ('y', 'yes', 'true', 'clipboard'):\n        return 'clipboard'\n    if q in ('n', 'no', 'false', ''):\n        return ''\n    return raw\n\n\ndef window_size(val: str) -> tuple[int, str]:\n    val = val.lower()\n    unit = 'cells' if val.endswith('c') else 'px'\n    return positive_int(val.rstrip('c')), unit\n\n\ndef parse_layout_names(parts: Iterable[str]) -> list[str]:\n    from kitty.layout.interface import all_layouts\n    ans = []\n    for p in parts:\n        p = p.lower()\n        if p in ('*', 'all'):\n            ans.extend(sorted(all_layouts))\n            continue\n        name = p.partition(':')[0]\n        if name not in all_layouts:\n            raise ValueError(f'The window layout {p} is unknown')\n        ans.append(p)\n    return uniq(ans)\n\n\ndef to_layout_names(raw: str) -> list[str]:\n    return parse_layout_names(x.strip() for x in raw.split(','))\n\n\ndef window_border_width(x: str | int | float) -> tuple[float, str]:\n    unit = 'pt'\n    if isinstance(x, str):\n        trailer = x[-2:]\n        if trailer in ('px', 'pt'):\n            unit = trailer\n            val = float(x[:-2])\n        else:\n            val = float(x)\n    else:\n        val = float(x)\n    return max(0, val), unit\n\n\ndef edge_width(x: str, converter: Callable[[str], float] = positive_float) -> FloatEdges:\n    parts = str(x).split()\n    num = len(parts)\n    if num == 1:\n        val = converter(parts[0])\n        return FloatEdges(val, val, val, val)\n    if num == 2:\n        v = converter(parts[0])\n        h = converter(parts[1])\n        return FloatEdges(h, v, h, v)\n    if num == 3:\n        top, h, bottom = map(converter, parts)\n        return FloatEdges(h, top, h, bottom)\n    top, right, bottom, left = map(converter, parts)\n    return FloatEdges(left, top, right, bottom)\n\n\ndef optional_edge_width(x: str) -> FloatEdges:\n    return edge_width(x, float)\n\n\ndef hide_window_decorations(x: str) -> int:\n    if x == 'titlebar-only':\n        return 0b10\n    if x == 'titlebar-and-corners':\n        return 0b100\n    if to_bool(x):\n        return 0b01\n    return 0b00\n\n\ndef resize_draw_strategy(x: str) -> int:\n    cmap = {'static': 0, 'scale': 1, 'blank': 2, 'size': 3}\n    return cmap.get(x.lower(), 0)\n\n\ndef window_logo_scale(x: str) -> tuple[float, float]:\n    parts = x.split(maxsplit=1)\n    if len(parts) == 1:\n        return positive_float(parts[0]), -1.0\n    return positive_float(parts[0]), positive_float(parts[1])\n\n\ndef resize_debounce_time(x: str) -> tuple[float, float]:\n    parts = x.split(maxsplit=1)\n    if len(parts) == 1:\n        return positive_float(parts[0]), 0.5\n    return positive_float(parts[0]), positive_float(parts[1])\n\n\ndef visual_window_select_characters(x: str) -> str:\n    import string\n    valid_characters = string.digits + string.ascii_uppercase + \"-=[]\\\\;',./`\"\n    ans = x.upper()\n    ans_chars = set(ans)\n    if not ans_chars.issubset(set(valid_characters)):\n        raise ValueError(f'Invalid characters in visual_window_select_characters: {x} Only numbers (0-9) and alphabets (a-z,A-Z) are allowed. Ignoring.')\n    if len(ans_chars) < len(x):\n        raise ValueError(f'Invalid characters in visual_window_select_characters: {x} Contains identical numbers or alphabets, case insensitive. Ignoring.')\n    return ans\n\n\ndef tab_separator(x: str) -> str:\n    for q in '\\'\"':\n        if x.startswith(q) and x.endswith(q):\n            x = x[1:-1]\n            if not x:\n                return ''\n            break\n    if not x.strip():\n        x = ('\\xa0' * len(x)) if x else default_tab_separator\n    return x\n\n\ndef tab_bar_edge(x: str) -> int:\n    return {'top': defines.TOP_EDGE, 'bottom': defines.BOTTOM_EDGE}.get(x.lower(), defines.BOTTOM_EDGE)\n\n\ndef tab_font_style(x: str) -> tuple[bool, bool]:\n    return {\n        'bold-italic': (True, True),\n        'bold': (True, False),\n        'italic': (False, True)\n    }.get(x.lower().replace('_', '-'), (False, False))\n\n\ndef tab_bar_min_tabs(x: str) -> int:\n    return max(1, positive_int(x))\n\n\ndef tab_fade(x: str) -> tuple[float, ...]:\n    return tuple(map(unit_float, x.split()))\n\n\ndef tab_activity_symbol(x: str) -> str:\n    if x == 'none':\n        return ''\n    return tab_title_template(x)\n\n\ndef bell_on_tab(x: str) -> str:\n    xl = x.lower()\n    if xl in ('yes', 'y', 'true'):\n        return '🔔 '\n    if xl in ('no', 'n', 'false', 'none'):\n        return ''\n    return tab_title_template(x)\n\n\ndef tab_title_template(x: str) -> str:\n    if x:\n        for q in '\\'\"':\n            if x.startswith(q) and x.endswith(q):\n                x = x[1:-1]\n                break\n    return x\n\n\ndef active_tab_title_template(x: str) -> str | None:\n    x = tab_title_template(x)\n    return None if x == 'none' else x\n\n\ndef text_fg_override_threshold(x: str) -> tuple[float, Literal['%', 'ratio']]:\n    val, unit = number_with_unit(x, '%', 'ratio')\n    return val, cast(Literal['%', 'ratio'], unit)\n\n\nClearOn = Literal['next', 'focus']\ndefault_clear_on: tuple[ClearOn, ...] = 'focus', 'next'\nall_clear_on = get_args(ClearOn)\n\n\nclass NotifyOnCmdFinish(NamedTuple):\n    when: str = 'never'\n    duration: float = 5.0\n    action: str = 'notify'\n    cmdline: tuple[str, ...] = ()\n    clear_on: tuple[ClearOn, ...] = default_clear_on\n\n\ndef notify_on_cmd_finish(x: str) -> NotifyOnCmdFinish:\n    parts = x.split(maxsplit=3)\n    if parts[0] not in ('never', 'unfocused', 'invisible', 'always'):\n        raise ValueError(f'Unknown notify_on_cmd_finish value: {parts[0]}')\n    when = parts[0]\n    duration = 5.0\n    if len(parts) > 1:\n        duration = float(parts[1])\n    action = 'notify'\n    cmdline: tuple[str, ...] = ()\n    clear_on = default_clear_on\n    if len(parts) > 2:\n        if parts[2] not in ('notify', 'bell', 'notify-bell', 'command'):\n            raise ValueError(f'Unknown notify_on_cmd_finish action: {parts[2]}')\n        action = parts[2]\n        if action.startswith('notify'):\n            if len(parts) > 3:\n                con: list[ClearOn] = []\n                for x in parts[3].split():\n                    if x not in all_clear_on:\n                        raise ValueError(\n                            f'notify_on_cmd_finish: notify clear_on value \"{x}\" is invalid. Valid values are: {\", \".join(all_clear_on)}')\n                    con.append(cast(ClearOn, x))\n                clear_on = tuple(con)\n        elif action == 'command':\n            if len(parts) > 3:\n                cmdline = tuple(to_cmdline(parts[3]))\n            else:\n                raise ValueError('notify_on_cmd_finish `command` action needs a command line')\n    return NotifyOnCmdFinish(when, duration, action, cmdline, clear_on)\n\n\ndef config_or_absolute_path(x: str, env: dict[str, str] | None = None) -> str | None:\n    if not x or x.lower() == 'none':\n        return None\n    return resolve_abs_or_config_path(x, env)\n\n\ndef filter_notification(val: str, current_val: dict[str, str]) -> Iterable[tuple[str, str]]:\n    yield val, ''\n\n\ndef remote_control_password(val: str, current_val: dict[str, str]) -> Iterable[tuple[str, Sequence[str]]]:\n    val = val.strip()\n    if val:\n        parts = to_cmdline(val, expand=False)\n        if parts[0].startswith('-'):\n            # this is done so in the future we can add --options to the cmd\n            # line of remote_control_password\n            raise ValueError('Passwords are not allowed to start with hyphens, ignoring this password')\n        if len(parts) == 1:\n            yield parts[0], ()\n        else:\n            yield parts[0], tuple(parts[1:])\n\n\ndef clipboard_control(x: str) -> tuple[str, ...]:\n    return tuple(x.lower().split())\n\n\ndef allow_hyperlinks(x: str) -> int:\n    if x == 'ask':\n        return 0b11\n    return 1 if to_bool(x) else 0\n\n\ndef color_with_special_values(x: str, special_values: dict[str, int], error_msg: str) -> int:\n    x = x.strip('\"')\n    if (ans := special_values.get(x)) is not None:\n        return ans & 0xff\n    try:\n        return (color_as_int(to_color(x)) << 8) | len(special_values)\n    except Exception:\n        log_error(error_msg.format(x=x))\n    return 0\n\n\ndef titlebar_color(x: str) -> int:\n    return color_with_special_values(\n        x,\n        {'system': 0, 'background': 1},\n        'Ignoring invalid title bar color: {x}'\n    )\n\n\ndef scrollbar_color(x: str) -> int:\n    return color_with_special_values(\n        x,\n        {'foreground': 0, 'selection_background': 1},\n        'Ignoring invalid scroll bar color: {x}'\n    )\n\n\ndef macos_titlebar_color(x: str) -> int:\n    x = x.strip('\"')\n    if x == 'light':\n        return -1\n    if x == 'dark':\n        return -2\n    return titlebar_color(x)\n\n\ndef macos_option_as_alt(x: str) -> int:\n    x = x.lower()\n    if x == 'both':\n        return 0b11\n    if x == 'left':\n        return 0b10\n    if x == 'right':\n        return 0b01\n    if to_bool(x):\n        return 0b11\n    return 0\n\n\nclass TabBarMarginHeight(NamedTuple):\n    outer: float = 0\n    inner: float = 0\n\n    def __bool__(self) -> bool:\n        return (self.outer + self.inner) > 0\n\n\ndef tab_bar_margin_height(x: str) -> TabBarMarginHeight:\n    parts = x.split(maxsplit=1)\n    if len(parts) != 2:\n        log_error(f'Invalid tab_bar_margin_height: {x}, ignoring')\n        return TabBarMarginHeight()\n    ans = map(positive_float, parts)\n    return TabBarMarginHeight(next(ans), next(ans))\n\n\ndef clone_source_strategies(x: str) -> frozenset[str]:\n    return frozenset({'venv', 'conda', 'path', 'env_var'} & set(x.lower().split(',')))\n\n\ndef clear_all_mouse_actions(val: str, dict_with_parse_results: dict[str, Any] | None = None) -> bool:\n    ans = to_bool(val)\n    if ans and dict_with_parse_results is not None:\n        dict_with_parse_results['mouse_map'] = [None]\n    return ans\n\n\ndef clear_all_shortcuts(val: str, dict_with_parse_results: dict[str, Any] | None = None) -> bool:\n    ans = to_bool(val)\n    if ans and dict_with_parse_results is not None:\n        dict_with_parse_results['map'] = [None]\n    return ans\n\n\ndef font_features(val: str) -> Iterable[tuple[str, tuple[defines.ParsedFontFeature, ...]]]:\n    if val == 'none':\n        return\n    parts = val.split()\n    if len(parts) < 2:\n        log_error(f\"Ignoring invalid font_features {val}\")\n        return\n    if parts[0]:\n        features = []\n        for feat in parts[1:]:\n            try:\n                features.append(defines.ParsedFontFeature(feat))\n            except ValueError:\n                log_error(f'Ignoring invalid font feature: {feat}')\n        yield parts[0], tuple(features)\n\n\ndef modify_font(val: str) -> Iterable[tuple[str, FontModification]]:\n    parts = val.split()\n    pos, plen = 0, len(parts)\n    if plen < 2:\n        log_error(f\"Ignoring invalid modify_font: {val}\")\n        return\n    mtype: ModificationType | None = getattr(ModificationType, parts[pos], None)\n    if mtype is None:\n        log_error(f\"Ignoring invalid modify_font with unknown modification type: {parts[pos]}\")\n        return\n    pos += 1\n    font_name = ''\n    if mtype is ModificationType.size:\n        font_name = parts[pos]\n        pos += 1\n    if plen - pos < 1:\n        log_error(f\"Ignoring invalid modify_font: {val}\")\n        return\n    sz = parts[pos]\n    pos += 1\n    munit = ModificationUnit.pt\n    if sz.endswith('%'):\n        munit = ModificationUnit.percent\n        sz = sz[:-1]\n    elif sz.endswith('px'):\n        munit = ModificationUnit.pixel\n        sz = sz[:-2]\n    try:\n        mvalue = float(sz)\n    except Exception:\n        log_error(f'Ignoring modify_font with invalid size: {sz}')\n        return\n    key = mtype.name\n    if font_name:\n        key += f':{font_name}'\n    yield key, FontModification(mtype, ModificationValue(mvalue, munit), font_name)\n\n\ndef env(val: str, current_val: dict[str, str]) -> Iterable[tuple[str, str]]:\n    val = val.strip()\n    if val:\n        if '=' in val:\n            key, v = val.split('=', 1)\n            key, v = key.strip(), v.strip()\n            if key:\n                if v:\n                    v = expandvars(v, current_val)\n                yield key, v\n        else:\n            yield val, DELETE_ENV_VAR\n\n\ndef store_multiple(val: str, current_val: Container[str]) -> Iterable[tuple[str, str]]:\n    val = val.strip()\n    if val not in current_val:\n        yield val, val\n\n\ndef menu_map(val: str, current_val: Container[str]) -> Iterable[tuple[tuple[str, ...], str]]:\n    parts = val.split(maxsplit=1)\n    if len(parts) != 2:\n        raise ValueError(f'Ignoring invalid menu action: {val}')\n    if parts[0] != 'global':\n        raise ValueError(f'Unknown menu type: {parts[0]}. Known types: global')\n    start = 0\n    if parts[1].startswith('\"'):\n        start = 1\n        idx = parts[1].find('\"', 1)\n        if idx == -1:\n            raise ValueError(f'The menu entry name in {val} must end with a double quote')\n    else:\n        idx = parts[1].find(' ')\n        if idx == -1:\n            raise ValueError(f'The menu entry {val} must have an action')\n    location = ('global',) + tuple(parts[1][start:idx].split('::'))\n    yield location, parts[1][idx+1:].lstrip()\n\n\nallowed_shell_integration_values = frozenset({'enabled', 'disabled', 'no-rc', 'no-cursor', 'no-title', 'no-prompt-mark', 'no-complete', 'no-cwd', 'no-sudo'})\n\n\ndef shell_integration(x: str) -> frozenset[str]:\n    q = frozenset(x.lower().split())\n    if not q.issubset(allowed_shell_integration_values):\n        log_error(f'Invalid shell integration options: {q - allowed_shell_integration_values}, ignoring')\n        return q & allowed_shell_integration_values or frozenset({'invalid'})\n    return q\n\n\ndef confirm_close(x: str) -> tuple[int, bool]:\n    parts = x.split(maxsplit=1)\n    num = int(parts[0])\n    allow_background = len(parts) > 1 and parts[1] == 'count-background'\n    return num, allow_background\n\n\ndef underline_exclusion(x: str) -> tuple[float, Literal['', 'px', 'pt']]:\n    try:\n        return float(x), ''\n    except Exception:\n        unit: Literal['pt', 'px'] = x[-2:]  # type: ignore\n        if unit not in ('px', 'pt'):\n            raise ValueError(f'Invalid underline_exclusion with unrecognized unit: {x}')\n        try:\n            val = float(x[:-2])\n        except Exception:\n            raise ValueError(f'Invalid underline_exclusion with non numeric value: {x}')\n        return val, unit\n\n\ndef paste_actions(x: str) -> frozenset[str]:\n    s = frozenset({'quote-urls-at-prompt', 'confirm', 'filter', 'confirm-if-large', 'replace-dangerous-control-codes', 'replace-newline', 'no-op'})\n    q = frozenset(x.lower().split(','))\n    if not q.issubset(s):\n        raise ValueError(f'Invalid paste actions: {q - s}, ignoring')\n    return q\n\n\ndef action_alias(val: str) -> Iterable[tuple[str, str]]:\n    parts = val.split(maxsplit=1)\n    if len(parts) > 1:\n        alias_name, rest = parts\n        yield alias_name, rest\n\n\nkitten_alias = action_alias\n\n\ndef symbol_map_parser(val: str, min_size: int = 2) -> Iterable[tuple[tuple[int, int], str]]:\n    parts = val.split()\n\n    if len(parts) < min_size:\n        raise ValueError('must have codepoints AND font name')\n    family = ' '.join(parts[1:])\n\n    def to_chr(x: str) -> int:\n        if not x.startswith('U+'):\n            raise ValueError(f'{x} is not a unicode codepoint of the form U+number')\n        return int(x[2:], 16)\n\n    for x in parts[0].split(','):\n        a_, b_ = x.replace('–', '-').partition('-')[::2]\n        b_ = b_ or a_\n        a, b = map(to_chr, (a_, b_))\n        if b < a or max(a, b) > sys.maxunicode or min(a, b) < 1:\n            raise ValueError(f'Invalid range: {a:x} - {b:x}')\n        yield (a, b), family\n\n\ndef symbol_map(val: str) -> Iterable[tuple[tuple[int, int], str]]:\n    yield from symbol_map_parser(val)\n\n\ndef narrow_symbols(val: str) -> Iterable[tuple[tuple[int, int], int]]:\n    for x, y in symbol_map_parser(val, min_size=1):\n        yield x, int(y or 1)\n\n\ndef parse_key_action(action: str, action_type: MapType = MapType.MAP) -> KeyAction:\n    parts = action.strip().split(maxsplit=1)\n    func = parts[0]\n    if len(parts) == 1:\n        return KeyAction(func, ())\n    rest = parts[1]\n    parser = func_with_args.get(func)\n    if parser is None:\n        raise KeyError(f'Unknown action: {func}')\n    func, args = parser(func, rest)\n    return KeyAction(func, tuple(args))\n\n\nclass ActionAlias(NamedTuple):\n    name: str\n    value: str\n    replace_second_arg: bool = False\n\n\nclass AliasMap:\n\n    def __init__(self) -> None:\n        self.aliases: dict[str, list[ActionAlias]] = {}\n\n    def append(self, name: str, aa: ActionAlias) -> None:\n        self.aliases.setdefault(name, []).append(aa)\n\n    def update(self, aa: 'AliasMap') -> None:\n        self.aliases.update(aa.aliases)\n\n    @lru_cache(maxsize=256)\n    def resolve_aliases(self, definition: str, map_type: MapType = MapType.MAP) -> tuple[KeyAction, ...]:\n        return tuple(resolve_aliases_and_parse_actions(definition, self.aliases, map_type))\n\n\ndef build_action_aliases(raw: dict[str, str], first_arg_replacement: str = '') -> AliasMap:\n    ans = AliasMap()\n    if first_arg_replacement:\n        for alias_name, rest in raw.items():\n            ans.append(first_arg_replacement, ActionAlias(alias_name, rest, True))\n    else:\n        for alias_name, rest in raw.items():\n            ans.append(alias_name, ActionAlias(alias_name, rest))\n    return ans\n\n\ndef resolve_aliases_and_parse_actions(\n    defn: str, aliases: dict[str, list[ActionAlias]], map_type: MapType\n) -> Iterator[KeyAction]:\n    if not defn:\n        return\n    parts = defn.split(maxsplit=1)\n    if len(parts) == 1:\n        possible_alias = defn\n        rest = ''\n    else:\n        possible_alias = parts[0]\n        rest = parts[1]\n    for alias in aliases.get(possible_alias, ()):\n        if alias.replace_second_arg:  # kitten_alias\n            if not rest:\n                continue\n            parts = rest.split(maxsplit=1)\n            if parts[0] != alias.name:\n                continue\n            new_defn = f'{possible_alias} {alias.value}{f\" {parts[1]}\" if len(parts) > 1 else \"\"}'\n            new_aliases = aliases.copy()\n            new_aliases[possible_alias] = [a for a in aliases[possible_alias] if a is not alias]\n            yield from resolve_aliases_and_parse_actions(new_defn, new_aliases, map_type)\n            return\n        else:  # action_alias\n            new_defn = f'{alias.value} {rest}' if rest else alias.value\n            new_aliases = aliases.copy()\n            new_aliases.pop(possible_alias)\n            yield from resolve_aliases_and_parse_actions(new_defn, new_aliases, map_type)\n            return\n\n    if possible_alias == 'combine':\n        sep, rest = rest.split(maxsplit=1)\n        parts = re.split(fr'\\s*{re.escape(sep)}\\s*', rest)\n        for x in parts:\n            if x:\n                yield from resolve_aliases_and_parse_actions(x, aliases, map_type)\n    else:\n        yield parse_key_action(defn, map_type)\n\n\nclass BaseDefinition:\n    no_op_actions = frozenset(('noop', 'no-op', 'no_op'))\n    map_type: MapType = MapType.MAP\n    definition_location: CurrentlyParsing\n\n    def __init__(self, definition: str = '') -> None:\n        if definition in BaseDefinition.no_op_actions:\n            definition = ''\n        self.definition = definition\n        self.definition_location = currently_parsing.__copy__()\n\n    def pretty_repr(self, *fields: str) -> str:\n        kwds = []\n        defaults = self.__class__()\n        for f in fields:\n            val = getattr(self, f)\n            if val != getattr(defaults, f):\n                kwds.append(f'{f}={val!r}')\n        if self.definition:\n            kwds.append(f'definition={self.definition!r}')\n        return f'{self.__class__.__name__}({\", \".join(kwds)})'\n\n\ndef resolve_key_mods(kitty_mod: int, mods: int) -> int:\n    return SingleKey(mods=mods).resolve_kitty_mod(kitty_mod).mods\n\n\nclass MouseMapping(BaseDefinition):\n    map_type: MapType = MapType.MOUSE_MAP\n\n    def __init__(\n        self, button: int = 0, mods: int = 0, repeat_count: int = 1, grabbed: bool = False,\n        definition: str = ''\n    ):\n        super().__init__(definition)\n        self.button = button\n        self.mods = mods\n        self.repeat_count = repeat_count\n        self.grabbed = grabbed\n\n    def __repr__(self) -> str:\n        return self.pretty_repr('button', 'mods', 'repeat_count', 'grabbed')\n\n    def resolve_and_copy(self, kitty_mod: int) -> 'MouseMapping':\n        ans = MouseMapping(\n            self.button, resolve_key_mods(kitty_mod, self.mods), self.repeat_count, self.grabbed,\n            self.definition)\n        ans.definition_location = self.definition_location\n        return ans\n\n    @property\n    def trigger(self) -> MouseEvent:\n        return MouseEvent(self.button, self.mods, self.repeat_count, self.grabbed)\n\n\nT = TypeVar('T')\n\n\nclass LiteralField(Generic[T]):\n    def __init__(self, vals: tuple[T, ...]):\n        self._vals = vals\n\n    def __set_name__(self, owner: object, name: str) -> None:\n        self._name = \"_\" + name\n\n    def __get__(self, obj: object, type: type | None = None) -> T:\n        if obj is None:\n            return self._vals[0]\n        return getattr(obj, self._name, self._vals[0])\n\n    def __set__(self, obj: object, value: str) -> None:\n        if value not in self._vals:\n            raise KeyError(f'Invalid value for {self._name[1:]}: {value!r}')\n        object.__setattr__(obj, self._name, value)\n\n\nOnUnknown = Literal['beep', 'end', 'ignore', 'passthrough']\nOnAction = Literal['keep', 'end']\n\n\n@dataclass(init=False, frozen=True)\nclass KeyMapOptions:\n    when_focus_on: str = ''\n    new_mode: str = ''\n    mode: str = ''\n    on_unknown: LiteralField[OnUnknown] = LiteralField[OnUnknown](get_args(OnUnknown))\n    on_action: LiteralField[OnAction] = LiteralField[OnAction](get_args(OnAction))\n    timeout: float | None = None\n\n\ndefault_key_map_options = KeyMapOptions()\nallowed_key_map_options = frozenset(f.name for f in fields(KeyMapOptions))\n\n\nclass KeyDefinition(BaseDefinition):\n\n    def __init__(\n        self, is_sequence: bool = False, trigger: SingleKey = SingleKey(),\n        rest: tuple[SingleKey, ...] = (), definition: str = '',\n        options: KeyMapOptions = default_key_map_options\n    ):\n        super().__init__(definition)\n        self.is_sequence = is_sequence\n        self.trigger = trigger\n        self.rest = rest\n        self.options = options\n\n    @property\n    def is_suitable_for_global_shortcut(self) -> bool:\n        return not self.options.when_focus_on and not self.options.mode and not self.options.new_mode and not self.is_sequence\n\n    @property\n    def full_key_sequence_to_trigger(self) -> tuple[SingleKey, ...]:\n        return (self.trigger,) + self.rest\n\n    @property\n    def unique_identity_within_keymap(self) -> tuple[tuple[SingleKey, ...], str]:\n        return self.full_key_sequence_to_trigger, self.options.when_focus_on\n\n    def __repr__(self) -> str:\n        return self.pretty_repr('is_sequence', 'trigger', 'rest', 'options')\n\n    def human_repr(self) -> str:\n        ans = self.definition or 'no-op'\n        if self.options.when_focus_on:\n            ans = f'[--when-focus-on={self.options.when_focus_on}]{ans}'\n        return ans\n\n    def shift_sequence_and_copy(self) -> 'KeyDefinition':\n        return KeyDefinition(self.is_sequence, self.trigger, self.rest[1:], self.definition, self.options)\n\n    def resolve_and_copy(self, kitty_mod: int) -> 'KeyDefinition':\n        def r(k: SingleKey) -> SingleKey:\n            return k.resolve_kitty_mod(kitty_mod)\n        ans = KeyDefinition(\n            self.is_sequence, r(self.trigger), tuple(map(r, self.rest)),\n            self.definition, self.options\n        )\n        ans.definition_location = self.definition_location\n        return ans\n\n\nclass KeyboardMode:\n\n    on_unknown: OnUnknown = get_args(OnUnknown)[0]\n    on_action : OnAction = get_args(OnAction)[0]\n    sequence_keys: list[defines.KeyEvent] | None = None\n    timeout: float = 0.0\n    timeout_timer_id: int | None = None\n\n    def __init__(self, name: str = '') -> None:\n        self.name = name\n        self.keymap: KeyMap = defaultdict(list)\n\n\nKeyboardModeMap = dict[str, KeyboardMode]\nkey_map_option_converters: defaultdict[str, Callable[[str], Any]] = defaultdict(lambda: (lambda x: x))\nkey_map_option_converters['timeout'] = float\n\n\ndef parse_options_for_map(val: str) -> tuple[KeyMapOptions, str]:\n    expecting_arg = ''\n    ans = KeyMapOptions()\n    s = Shlex(val)\n    while (tok := s.next_word())[0] > -1:\n        x = tok[1]\n        if expecting_arg:\n            object.__setattr__(ans, expecting_arg, key_map_option_converters[expecting_arg](x))\n            expecting_arg = ''\n        elif x.startswith('--'):\n            expecting_arg = x[2:]\n            k, sep, v = expecting_arg.partition('=')\n            k = k.replace('-', '_')\n            expecting_arg = k\n            if expecting_arg not in allowed_key_map_options:\n                raise KeyError(f'The map option {x} is unknown. Allowed options: {\", \".join(allowed_key_map_options)}')\n            if sep == '=':\n                object.__setattr__(ans, k, key_map_option_converters[k](v))\n                expecting_arg = ''\n        else:\n            return ans, val[tok[0]:]\n    return ans, ''\n\n\ndef parse_map(val: str) -> Iterable[KeyDefinition]:\n    parts = val.split(maxsplit=1)\n    options = default_key_map_options\n    if len(parts) == 2:\n        sc, action = parts\n        if sc.startswith('--'):\n            options, leftover = parse_options_for_map(val)\n            parts = leftover.split(maxsplit=1)\n            if len(parts) == 1:\n                sc, action = parts[0], ''\n            else:\n                sc = parts[0]\n                action = ' '.join(parts[1:])\n    else:\n        sc, action = val, ''\n    sc, action = sc.strip().strip(sequence_sep), action.strip()\n    if not sc:\n        return\n    is_sequence = sequence_sep in sc\n    if is_sequence:\n        trigger: SingleKey | None = None\n        restl: list[SingleKey] = []\n        for part in sc.split(sequence_sep):\n            try:\n                mods, is_native, key = parse_shortcut(part)\n            except InvalidMods:\n                return\n            if key == 0:\n                if mods is not None:\n                    log_error(f'Shortcut: {sc} has unknown key, ignoring')\n                return\n            if trigger is None:\n                trigger = SingleKey(mods, is_native, key)\n            else:\n                restl.append(SingleKey(mods, is_native, key))\n        rest = tuple(restl)\n    else:\n        try:\n            mods, is_native, key = parse_shortcut(sc)\n        except InvalidMods:\n            return\n        if key == 0:\n            if mods is not None:\n                log_error(f'Shortcut: {sc} has unknown key, ignoring')\n            return\n    if is_sequence:\n        if trigger is not None:\n            yield KeyDefinition(True, trigger, rest, definition=action, options=options)\n    else:\n        assert key is not None\n        yield KeyDefinition(False, SingleKey(mods, is_native, key), definition=action, options=options)\n\n\ndef parse_mouse_map(val: str) -> Iterable[MouseMapping]:\n    parts = val.split(maxsplit=3)\n    if len(parts) == 4:\n        xbutton, event, modes, action = parts\n    elif len(parts) > 2:\n        xbutton, event, modes = parts\n        action = ''\n    else:\n        log_error(f'Ignoring invalid mouse action: {val}')\n        return\n    kparts = xbutton.split('+')\n    if len(kparts) > 1:\n        mparts, obutton = kparts[:-1], kparts[-1].lower()\n        mods = parse_mods(mparts, obutton)\n        if mods is None:\n            return\n    else:\n        obutton = parts[0].lower()\n        mods = 0\n    try:\n        b = mouse_button_map.get(obutton, obutton)[1:]\n        button = getattr(defines, f'GLFW_MOUSE_BUTTON_{b}')\n    except Exception:\n        log_error(f'Mouse button: {xbutton} not recognized, ignoring')\n        return\n    try:\n        count = mouse_trigger_count_map[event.lower()]\n    except KeyError:\n        log_error(f'Mouse event type: {event} not recognized, ignoring')\n        return\n    specified_modes = frozenset(modes.lower().split(','))\n    if specified_modes - {'grabbed', 'ungrabbed'}:\n        log_error(f'Mouse modes: {modes} not recognized, ignoring')\n        return\n    for mode in sorted(specified_modes):\n        yield MouseMapping(button, mods, count, mode == 'grabbed', definition=action)\n\n\ndef parse_font_spec(spec: str) -> FontSpec:\n    return FontSpec.from_setting(spec)\n\n\nJumpTypes = Literal['start', 'end', 'none', 'both']\n\n\nclass EasingFunction(NamedTuple):\n    type: Literal['steps', 'linear', 'cubic-bezier', ''] = ''\n\n    num_steps: int = 0\n    jump_type: JumpTypes = 'end'\n\n    linear_x: tuple[float, ...] = ()\n    linear_y: tuple[float, ...] = ()\n\n    cubic_bezier_points: tuple[float, ...] = ()\n\n    def __repr__(self) -> str:\n        fields = ', '.join(f'{f}={getattr(self, f)!r}' for f in self._fields if getattr(self, f) != self._field_defaults[f])\n        return f'kitty.options.utils.EasingFunction({fields})'\n\n    def __bool__(self) -> bool:\n        return bool(self.type)\n\n    @classmethod\n    def cubic_bezier(cls, params: str) -> 'EasingFunction':\n        parts = params.replace(',', ' ').split()\n        if len(parts) != 4:\n            raise ValueError('cubic-bezier easing function must have four points')\n        return cls(type='cubic-bezier', cubic_bezier_points=(\n            unit_float(parts[0]), float(parts[1]), unit_float(parts[2]), float(parts[3])))\n\n    @classmethod\n    def linear(cls, params: str) -> 'EasingFunction':\n        parts = params.split(',')\n        if len(parts) < 2:\n            raise ValueError('Must specify at least two points for the linear easing function')\n        xaxis: list[float] = []\n        yaxis: list[float] = []\n\n        def balance(end: float) -> None:\n            extra = len(yaxis) - len(xaxis)\n            if extra <= 0:\n                return\n            start = xaxis[-1] if xaxis else 0.\n            delta = (end - start) / max(1, extra - 1)\n            if delta <= 0.:\n                raise ValueError(f'Linear easing curve must have strictly increasing points: {params} does not')\n            if xaxis:\n                for i in range(extra):\n                    xaxis.append(start + (i+1) * delta)\n            else:\n                for i in range(extra):\n                    xaxis.append(i * delta)\n\n        def add_point(y: float, x: float | None = None) -> None:\n            if x is None:\n                yaxis.append(y)\n            else:\n                x = unit_float(x)\n                balance(x)\n                xaxis.append(x)\n                yaxis.append(y)\n\n        for r in parts:\n            points = r.strip().split()\n            y = unit_float(points[0])\n            if len(points) == 1:\n                add_point(y)\n            elif len(points) == 2:\n                add_point(y, percent(points[1]))\n            elif len(points) == 3:\n                add_point(y, percent(points[1]))\n                add_point(y, percent(points[2]))\n            else:\n                raise ValueError(f'{r} has too many points for a linear easing curve parameter')\n        balance(1)\n        return cls(type='linear', linear_x=tuple(xaxis), linear_y=tuple(yaxis))\n\n    @classmethod\n    def steps(cls, params: str) -> 'EasingFunction':\n        parts = params.replace(',', ' ').split()\n        jump_type: JumpTypes = 'end'\n        if len(parts) == 2:\n            n = int(parts[0])\n            jt = parts[1]\n            mapping: dict[str, JumpTypes] = {\n                'jump-start': 'start', 'start': 'start', 'end': 'end', 'jump-end': 'end', 'jump-none': 'none', 'jump-both': 'both'\n            }\n            try:\n                jump_type = mapping[jt.lower()]\n            except KeyError:\n                raise KeyError(f'{jt} is not a valid jump type for a linear easing function')\n            if jump_type == 'none':\n                n = max(2, n)\n            else:\n                n = max(1, n)\n        else:\n            n = max(1, int(parts[0]))\n        return cls(type='steps', jump_type=jump_type, num_steps=n)\n\n\ndef parse_animation(spec: str, interval: float = -1.) -> tuple[float, EasingFunction, EasingFunction]:\n    with suppress(Exception):\n        interval = float(spec)\n        return interval, EasingFunction(), EasingFunction()\n\n    m = [EasingFunction(), EasingFunction()]\n    def parse_func(func_name: str, params: str) -> None:\n        idx = 1 if m[0] else 0\n        if m[idx]:\n            raise ValueError(f'{spec} specified more than two easing functions')\n        if func_name == 'cubic-bezier':\n            m[idx] = EasingFunction.cubic_bezier(params)\n        elif func_name == 'linear':\n            m[idx] = EasingFunction.linear(params)\n        elif func_name == 'steps':\n            m[idx] = EasingFunction.steps(params)\n        else:\n            raise KeyError(f'{func_name} is not a valid easing function')\n\n    for match in re.finditer(r'([-+.0-9a-zA-Z]+)(?:\\(([^)]*)\\)){0,1}', spec):\n        func_name, params = match.group(1, 2)\n        if params:\n            parse_func(func_name, params)\n            continue\n        with suppress(Exception):\n            interval = float(func_name)\n            continue\n        if func_name == 'ease-in-out':\n            parse_func('cubic-bezier', '0.42, 0, 0.58, 1')\n        elif func_name == 'linear':\n            parse_func('cubic-bezier', '0, 0, 1, 1')\n        elif func_name == 'ease':\n            parse_func('cubic-bezier', '0.25, 0.1, 0.25, 1')\n        elif func_name == 'ease-out':\n            parse_func('cubic-bezier', '0, 0, 0.58, 1')\n        elif func_name == 'ease-in':\n            parse_func('cubic-bezier', '0.42, 0, 1, 1')\n        elif func_name == 'step-start':\n            parse_func('steps', '1, start')\n        elif func_name == 'step-end':\n            parse_func('steps', '1, end')\n        else:\n            raise KeyError(f'{func_name} is not a valid easing function')\n    return interval, m[0], m[1]\n\n\ndef cursor_blink_interval(spec: str) -> tuple[float, EasingFunction, EasingFunction]:\n    return parse_animation(spec)\n\n\nclass MouseHideWait(NamedTuple):\n    hide_wait: float\n    show_wait: float\n    show_threshold: int\n    scroll_show: bool\n\n\ndef mouse_hide_wait(x: str) -> MouseHideWait:\n    parts = x.split(maxsplit=3)\n    if len(parts) != 1 and len(parts) != 4:\n        log_error(f'Invalid mouse_hide_wait: {x}, ignoring')\n        return MouseHideWait(3.0, 0.0, 40, True)\n    if len(parts) == 1:\n        return MouseHideWait(float(parts[0]), 0.0, 40, True)\n    else:\n        return MouseHideWait(float(parts[0]), float(parts[1]), int(parts[2]), to_bool(parts[3]))\n\n\ndef visual_bell_duration(spec: str) -> tuple[float, EasingFunction, EasingFunction]:\n    return parse_animation(spec, interval=0.)\n\n\npointer_shape_names = (\n# start pointer shape names (auto generated by gen-key-constants.py do not edit)\n    'arrow',\n    'beam',\n    'text',\n    'pointer',\n    'hand',\n    'help',\n    'wait',\n    'progress',\n    'crosshair',\n    'cell',\n    'vertical-text',\n    'move',\n    'e-resize',\n    'ne-resize',\n    'nw-resize',\n    'n-resize',\n    'se-resize',\n    'sw-resize',\n    's-resize',\n    'w-resize',\n    'ew-resize',\n    'ns-resize',\n    'nesw-resize',\n    'nwse-resize',\n    'zoom-in',\n    'zoom-out',\n    'alias',\n    'copy',\n    'not-allowed',\n    'no-drop',\n    'grab',\n    'grabbing',\n# end pointer shape names\n)\n\n\ndef pointer_shape_when_dragging(spec: str) -> tuple[str, str]:\n    parts = spec.split(maxsplit=1)\n    first = parts[0]\n    if first not in pointer_shape_names:\n        raise ValueError(f'{first} is not a valid pointer shape name')\n    second = parts[1] if len(parts) > 1 else first\n    if second not in pointer_shape_names:\n        raise ValueError(f'{second} is not a valid pointer shape name')\n    return first, second\n\n\ndef transparent_background_colors(spec: str) -> tuple[tuple[Color, float], ...]:\n    if not spec:\n        return ()\n    ans: list[tuple[Color, float]] = []\n    seen: dict[Color, int] = {}\n    for part in spec.split():\n        col, sep, alpha = part.partition('@')\n        c = to_color(col)\n        o = max(-1, min(float(alpha) if alpha else -1, 1))\n        if (idx := seen.get(c)) is not None:\n            ans[idx] = c, o\n            continue\n        seen[c] = len(ans)\n        ans.append((c, o))\n    return tuple(ans[:7])\n\n\ndef deprecated_hide_window_decorations_aliases(key: str, val: str, ans: dict[str, Any]) -> None:\n    if not hasattr(deprecated_hide_window_decorations_aliases, key):\n        setattr(deprecated_hide_window_decorations_aliases, key, True)\n        log_error(f'The option {key} is deprecated. Use hide_window_decorations instead.')\n    if to_bool(val):\n        if is_macos and key == 'macos_hide_titlebar' or (not is_macos and key == 'x11_hide_window_decorations'):\n            ans['hide_window_decorations'] = True\n\n\ndef deprecated_macos_show_window_title_in_menubar_alias(key: str, val: str, ans: dict[str, Any]) -> None:\n    if not hasattr(deprecated_macos_show_window_title_in_menubar_alias, key):\n        setattr(deprecated_macos_show_window_title_in_menubar_alias, 'key', True)\n        log_error(f'The option {key} is deprecated. Use macos_show_window_title_in menubar instead.')\n    macos_show_window_title_in = ans.get('macos_show_window_title_in', 'all')\n    if to_bool(val):\n        if macos_show_window_title_in == 'none':\n            macos_show_window_title_in = 'menubar'\n        elif macos_show_window_title_in == 'window':\n            macos_show_window_title_in = 'all'\n    else:\n        if macos_show_window_title_in == 'all':\n            macos_show_window_title_in = 'window'\n        elif macos_show_window_title_in == 'menubar':\n            macos_show_window_title_in = 'none'\n    ans['macos_show_window_title_in'] = macos_show_window_title_in\n\n\ndef deprecated_send_text(key: str, val: str, ans: dict[str, Any]) -> None:\n    parts = val.split(' ')\n\n    def abort(msg: str) -> None:\n        log_error(f'Send text: {val} is invalid ({msg}), ignoring')\n\n    if len(parts) < 3:\n        return abort('Incomplete')\n    mode, sc = parts[:2]\n    text = ' '.join(parts[2:])\n    key_str = f'{sc} send_text {mode} {text}'\n    for k in parse_map(key_str):\n        ans['map'].append(k)\n\n\ndef deprecated_adjust_line_height(key: str, x: str, opts_dict: dict[str, Any]) -> None:\n    fm = {'adjust_line_height': 'cell_height', 'adjust_baseline': 'baseline', 'adjust_column_width': 'cell_width'}[key]\n    mtype = getattr(ModificationType, fm)\n    if x.endswith('%'):\n        ans = float(x[:-1].strip())\n        if ans < 0:\n            log_error(f'Percentage adjustments of {key} must be positive numbers')\n            return\n        opts_dict['modify_font'][fm] = FontModification(mtype, ModificationValue(ans, ModificationUnit.percent))\n    else:\n        opts_dict['modify_font'][fm] = FontModification(mtype, ModificationValue(int(x), ModificationUnit.pixel))\n\n\ndef deprecated_scrollback_indicator_opacity(key: str, val: str, ans: dict[str, Any]) -> None:\n    if not hasattr(deprecated_scrollback_indicator_opacity, key):\n        setattr(deprecated_scrollback_indicator_opacity, key, True)\n        log_error(f'The option {key} is deprecated. Use scrollbar instead.')\n    op = unit_float(val)\n    if op <= 0.001:\n        ans['scrollbar'] = 'never'\n    else:\n        ans['scrollbar_handle_opacity'] = op\n"
  },
  {
    "path": "kitty/os_window_size.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Callable\nfrom typing import Any, NamedTuple\n\nfrom .constants import is_macos, is_wayland\nfrom .fast_data_types import get_options\nfrom .options.types import Options\nfrom .types import FloatEdges\nfrom .typing_compat import EdgeLiteral\nfrom .utils import log_error\n\n\nclass WindowSize(NamedTuple):\n\n    size: int\n    unit: str\n\n\nclass WindowSizes(NamedTuple):\n\n    width: WindowSize\n    height: WindowSize\n\n\nclass WindowSizeData(NamedTuple):\n    initial_window_sizes: WindowSizes\n    remember_window_size: bool\n    single_window_margin_width: FloatEdges\n    window_margin_width: FloatEdges\n    single_window_padding_width: FloatEdges\n    window_padding_width: FloatEdges\n\n\ndef sanitize_window_size(x: Any) -> int:\n    ans = int(x)\n    return max(20, min(ans, 50000))\n\n\ndef edge_spacing(which: EdgeLiteral, opts: WindowSizeData | Options | None = None) -> float:\n    if opts is None:\n        opts = get_options()\n    margin: float = getattr(opts.single_window_margin_width, which)\n    if margin < 0:\n        margin = getattr(opts.window_margin_width, which)\n\n    padding: float = getattr(opts.single_window_padding_width, which)\n    if padding < 0:\n        padding = getattr(opts.window_padding_width, which)\n    return float(padding + margin)\n\n\n\ndef initial_window_size_func(opts: WindowSizeData, cached_values: dict[str, Any]) -> Callable[[int, int, float, float, float, float], tuple[int, int]]:\n\n    if 'window-size' in cached_values and opts.remember_window_size:\n        ws = cached_values['window-size']\n        try:\n            w, h = map(sanitize_window_size, ws)\n\n            def initial_window_size(*a: Any) -> tuple[int, int]:\n                return w, h\n            return initial_window_size\n        except Exception:\n            log_error('Invalid cached window size, ignoring')\n\n    w, w_unit = opts.initial_window_sizes.width\n    h, h_unit = opts.initial_window_sizes.height\n\n    def get_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> tuple[int, int]:\n        if not is_macos and not is_wayland():\n            # Not sure what the deal with scaling on X11 is\n            xscale = yscale = 1\n\n        def effective_margin(which: EdgeLiteral) -> float:\n            ans: float = getattr(opts.single_window_margin_width, which)\n            if ans < 0:\n                ans = getattr(opts.window_margin_width, which)\n            return ans\n\n        def effective_padding(which: EdgeLiteral) -> float:\n            ans: float = getattr(opts.single_window_padding_width, which)\n            if ans < 0:\n                ans = getattr(opts.window_padding_width, which)\n            return ans\n\n        if w_unit == 'cells':\n            spacing = effective_margin('left') + effective_margin('right')\n            spacing += effective_padding('left') + effective_padding('right')\n            width = cell_width * w / xscale + (dpi_x / 72) * spacing + 1\n        else:\n            width = w\n        if h_unit == 'cells':\n            spacing = effective_margin('top') + effective_margin('bottom')\n            spacing += effective_padding('top') + effective_padding('bottom')\n            height = cell_height * h / yscale + (dpi_y / 72) * spacing + 1\n        else:\n            height = h\n        return int(width), int(height)\n\n    return get_window_size\n"
  },
  {
    "path": "kitty/parse-graphics-command.h",
    "content": "// This file is generated by apc_parsers.py do not edit!\n\n#pragma once\n\n#include \"base64.h\"\n\nstatic inline void parse_graphics_code(PS *self, uint8_t *parser_buf,\n                                       const size_t parser_buf_pos) {\n  unsigned int pos = 1;\n\n  enum PARSER_STATES { KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE, PAYLOAD };\n  enum PARSER_STATES state = KEY, value_state = FLAG;\n  GraphicsCommand g = {0};\n  unsigned int i, code;\n  uint64_t lcode;\n  int64_t accumulator;\n  bool is_negative;\n  (void)is_negative;\n  size_t sz;\n\n  enum KEYS {\n    action = 'a',\n    delete_action = 'd',\n    transmission_type = 't',\n    compressed = 'o',\n    format = 'f',\n    more = 'm',\n    id = 'i',\n    image_number = 'I',\n    placement_id = 'p',\n    quiet = 'q',\n    width = 'w',\n    height = 'h',\n    x_offset = 'x',\n    y_offset = 'y',\n    data_height = 'v',\n    data_width = 's',\n    data_sz = 'S',\n    data_offset = 'O',\n    num_cells = 'c',\n    num_lines = 'r',\n    cell_x_offset = 'X',\n    cell_y_offset = 'Y',\n    z_index = 'z',\n    cursor_movement = 'C',\n    unicode_placement = 'U',\n    parent_id = 'P',\n    parent_placement_id = 'Q',\n    offset_from_parent_x = 'H',\n    offset_from_parent_y = 'V'\n  };\n\n  enum KEYS key = 'a';\n  if (parser_buf[pos] == ';')\n    state = AFTER_VALUE;\n\n  while (pos < parser_buf_pos) {\n    switch (state) {\n    case KEY:\n      key = parser_buf[pos++];\n      state = EQUAL;\n      switch (key) {\n      case action:\n        value_state = FLAG;\n        break;\n      case delete_action:\n        value_state = FLAG;\n        break;\n      case transmission_type:\n        value_state = FLAG;\n        break;\n      case compressed:\n        value_state = FLAG;\n        break;\n      case format:\n        value_state = UINT;\n        break;\n      case more:\n        value_state = UINT;\n        break;\n      case id:\n        value_state = UINT;\n        break;\n      case image_number:\n        value_state = UINT;\n        break;\n      case placement_id:\n        value_state = UINT;\n        break;\n      case quiet:\n        value_state = UINT;\n        break;\n      case width:\n        value_state = UINT;\n        break;\n      case height:\n        value_state = UINT;\n        break;\n      case x_offset:\n        value_state = UINT;\n        break;\n      case y_offset:\n        value_state = UINT;\n        break;\n      case data_height:\n        value_state = UINT;\n        break;\n      case data_width:\n        value_state = UINT;\n        break;\n      case data_sz:\n        value_state = UINT;\n        break;\n      case data_offset:\n        value_state = UINT;\n        break;\n      case num_cells:\n        value_state = UINT;\n        break;\n      case num_lines:\n        value_state = UINT;\n        break;\n      case cell_x_offset:\n        value_state = UINT;\n        break;\n      case cell_y_offset:\n        value_state = UINT;\n        break;\n      case z_index:\n        value_state = INT;\n        break;\n      case cursor_movement:\n        value_state = UINT;\n        break;\n      case unicode_placement:\n        value_state = UINT;\n        break;\n      case parent_id:\n        value_state = UINT;\n        break;\n      case parent_placement_id:\n        value_state = UINT;\n        break;\n      case offset_from_parent_x:\n        value_state = INT;\n        break;\n      case offset_from_parent_y:\n        value_state = INT;\n        break;\n      default:\n        REPORT_ERROR(\"Malformed GraphicsCommand control block, invalid key \"\n                     \"character: 0x%x\",\n                     key);\n        return;\n      }\n      break;\n\n    case EQUAL:\n      if (parser_buf[pos++] != '=') {\n        REPORT_ERROR(\"Malformed GraphicsCommand control block, no = after key, \"\n                     \"found: 0x%x instead\",\n                     parser_buf[pos - 1]);\n        return;\n      }\n      state = value_state;\n      break;\n\n    case FLAG:\n      switch (key) {\n\n      case action: {\n        g.action = parser_buf[pos++];\n        if (g.action != 'T' && g.action != 'a' && g.action != 'c' &&\n            g.action != 'd' && g.action != 'f' && g.action != 'p' &&\n            g.action != 'q' && g.action != 't') {\n          REPORT_ERROR(\"Malformed GraphicsCommand control block, unknown flag \"\n                       \"value for action: 0x%x\",\n                       g.action);\n          return;\n        };\n      } break;\n\n      case delete_action: {\n        g.delete_action = parser_buf[pos++];\n        if (g.delete_action != 'A' && g.delete_action != 'C' &&\n            g.delete_action != 'F' && g.delete_action != 'I' &&\n            g.delete_action != 'N' && g.delete_action != 'P' &&\n            g.delete_action != 'Q' && g.delete_action != 'R' &&\n            g.delete_action != 'X' && g.delete_action != 'Y' &&\n            g.delete_action != 'Z' && g.delete_action != 'a' &&\n            g.delete_action != 'c' && g.delete_action != 'f' &&\n            g.delete_action != 'i' && g.delete_action != 'n' &&\n            g.delete_action != 'p' && g.delete_action != 'q' &&\n            g.delete_action != 'r' && g.delete_action != 'x' &&\n            g.delete_action != 'y' && g.delete_action != 'z') {\n          REPORT_ERROR(\"Malformed GraphicsCommand control block, unknown flag \"\n                       \"value for delete_action: 0x%x\",\n                       g.delete_action);\n          return;\n        };\n      } break;\n\n      case transmission_type: {\n        g.transmission_type = parser_buf[pos++];\n        if (g.transmission_type != 'd' && g.transmission_type != 'f' &&\n            g.transmission_type != 's' && g.transmission_type != 't') {\n          REPORT_ERROR(\"Malformed GraphicsCommand control block, unknown flag \"\n                       \"value for transmission_type: 0x%x\",\n                       g.transmission_type);\n          return;\n        };\n      } break;\n\n      case compressed: {\n        g.compressed = parser_buf[pos++];\n        if (g.compressed != 'z') {\n          REPORT_ERROR(\"Malformed GraphicsCommand control block, unknown flag \"\n                       \"value for compressed: 0x%x\",\n                       g.compressed);\n          return;\n        };\n      } break;\n\n      default:\n        break;\n      }\n      state = AFTER_VALUE;\n      break;\n\n    case INT:\n#define READ_UINT                                                              \\\n  for (i = pos, accumulator = 0; i < MIN(parser_buf_pos, pos + 10); i++) {     \\\n    int64_t n = parser_buf[i] - '0';                                           \\\n    if (n < 0 || n > 9)                                                        \\\n      break;                                                                   \\\n    accumulator += n * digit_multipliers[i - pos];                             \\\n  }                                                                            \\\n  if (i == pos) {                                                              \\\n    REPORT_ERROR(\"Malformed GraphicsCommand control block, expecting an \"      \\\n                 \"integer value for key: %c\",                                  \\\n                 key & 0xFF);                                                  \\\n    return;                                                                    \\\n  }                                                                            \\\n  lcode = accumulator / digit_multipliers[i - pos - 1];                        \\\n  pos = i;                                                                     \\\n  if (lcode > UINT32_MAX) {                                                    \\\n    REPORT_ERROR(                                                              \\\n        \"Malformed GraphicsCommand control block, number is too large\");       \\\n    return;                                                                    \\\n  }                                                                            \\\n  code = lcode;\n\n      is_negative = false;\n      if (parser_buf[pos] == '-') {\n        is_negative = true;\n        pos++;\n      }\n#define I(x)                                                                   \\\n  case x:                                                                      \\\n    g.x = is_negative ? 0 - (int32_t)code : (int32_t)code;                     \\\n    break\n      READ_UINT;\n      switch (key) {\n        I(z_index);\n        I(offset_from_parent_x);\n        I(offset_from_parent_y);\n      default:\n        break;\n      }\n      state = AFTER_VALUE;\n      break;\n#undef I\n    case UINT:\n      READ_UINT;\n#define U(x)                                                                   \\\n  case x:                                                                      \\\n    g.x = code;                                                                \\\n    break\n      switch (key) {\n        U(format);\n        U(more);\n        U(id);\n        U(image_number);\n        U(placement_id);\n        U(quiet);\n        U(width);\n        U(height);\n        U(x_offset);\n        U(y_offset);\n        U(data_height);\n        U(data_width);\n        U(data_sz);\n        U(data_offset);\n        U(num_cells);\n        U(num_lines);\n        U(cell_x_offset);\n        U(cell_y_offset);\n        U(cursor_movement);\n        U(unicode_placement);\n        U(parent_id);\n        U(parent_placement_id);\n      default:\n        break;\n      }\n      state = AFTER_VALUE;\n      break;\n#undef U\n#undef READ_UINT\n\n    case AFTER_VALUE:\n      switch (parser_buf[pos++]) {\n      default:\n        REPORT_ERROR(\"Malformed GraphicsCommand control block, expecting a , \"\n                     \"or semi-colon after a value, found: 0x%x\",\n                     parser_buf[pos - 1]);\n        return;\n      case ',':\n        state = KEY;\n        break;\n      case ';':\n        state = PAYLOAD;\n        break;\n      }\n      break;\n\n    case PAYLOAD: {\n      sz = parser_buf_pos - pos;\n      g.payload_sz = MAX(BUF_EXTRA, sz);\n      if (!base64_decode8(parser_buf + pos, sz, parser_buf, &g.payload_sz)) {\n        g.payload_sz = MAX(BUF_EXTRA, sz);\n        REPORT_ERROR(\"Failed to parse GraphicsCommand command payload with \"\n                     \"error:     invalid base64 data in chunk of size: %zu \"\n                     \"with output buffer size: %zu\",\n                     sz, g.payload_sz);\n        return;\n      }\n      pos = parser_buf_pos;\n    } break;\n\n    } // end switch\n  } // end while\n\n  switch (state) {\n  case EQUAL:\n    REPORT_ERROR(\"Malformed GraphicsCommand control block, no = after key\");\n    return;\n  case INT:\n  case UINT:\n    REPORT_ERROR(\n        \"Malformed GraphicsCommand control block, expecting an integer value\");\n    return;\n  case FLAG:\n    REPORT_ERROR(\n        \"Malformed GraphicsCommand control block, expecting a flag value\");\n    return;\n  default:\n    break;\n  }\n\n  REPORT_VA_COMMAND(\n      \"K s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI \"\n      \"sI sI sI sI si si si ss#}\",\n      self->window_id, \"graphics_command\",\n\n      \"action\", g.action, \"delete_action\", g.delete_action, \"transmission_type\",\n      g.transmission_type, \"compressed\", g.compressed,\n\n      \"format\", (unsigned int)g.format, \"more\", (unsigned int)g.more, \"id\",\n      (unsigned int)g.id, \"image_number\", (unsigned int)g.image_number,\n      \"placement_id\", (unsigned int)g.placement_id, \"quiet\",\n      (unsigned int)g.quiet, \"width\", (unsigned int)g.width, \"height\",\n      (unsigned int)g.height, \"x_offset\", (unsigned int)g.x_offset, \"y_offset\",\n      (unsigned int)g.y_offset, \"data_height\", (unsigned int)g.data_height,\n      \"data_width\", (unsigned int)g.data_width, \"data_sz\",\n      (unsigned int)g.data_sz, \"data_offset\", (unsigned int)g.data_offset,\n      \"num_cells\", (unsigned int)g.num_cells, \"num_lines\",\n      (unsigned int)g.num_lines, \"cell_x_offset\", (unsigned int)g.cell_x_offset,\n      \"cell_y_offset\", (unsigned int)g.cell_y_offset, \"cursor_movement\",\n      (unsigned int)g.cursor_movement, \"unicode_placement\",\n      (unsigned int)g.unicode_placement, \"parent_id\", (unsigned int)g.parent_id,\n      \"parent_placement_id\", (unsigned int)g.parent_placement_id,\n\n      \"z_index\", (int)g.z_index, \"offset_from_parent_x\",\n      (int)g.offset_from_parent_x, \"offset_from_parent_y\",\n      (int)g.offset_from_parent_y,\n\n      \"\", (char *)parser_buf, g.payload_sz);\n\n  screen_handle_graphics_command(self->screen, &g, parser_buf);\n}\n"
  },
  {
    "path": "kitty/parse-multicell-command.h",
    "content": "// This file is generated by apc_parsers.py do not edit!\n\n#pragma once\n\n#include \"base64.h\"\n\nstatic inline void parse_multicell_code(PS *self, uint8_t *parser_buf,\n                                        const size_t parser_buf_pos) {\n  unsigned int pos = 0;\n  size_t payload_start = 0;\n  enum PARSER_STATES { KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE, PAYLOAD };\n  enum PARSER_STATES state = KEY, value_state = FLAG;\n  MultiCellCommand g = {0};\n  unsigned int i, code;\n  uint64_t lcode;\n  int64_t accumulator;\n  bool is_negative;\n  (void)is_negative;\n  size_t sz;\n\n  enum KEYS {\n    width = 'w',\n    scale = 's',\n    subscale_n = 'n',\n    subscale_d = 'd',\n    vertical_align = 'v',\n    horizontal_align = 'h'\n  };\n\n  enum KEYS key = 'a';\n  if (parser_buf[pos] == ';')\n    state = AFTER_VALUE;\n\n  while (pos < parser_buf_pos) {\n    switch (state) {\n    case KEY:\n      key = parser_buf[pos++];\n      state = EQUAL;\n      switch (key) {\n      case width:\n        value_state = UINT;\n        break;\n      case scale:\n        value_state = UINT;\n        break;\n      case subscale_n:\n        value_state = UINT;\n        break;\n      case subscale_d:\n        value_state = UINT;\n        break;\n      case vertical_align:\n        value_state = UINT;\n        break;\n      case horizontal_align:\n        value_state = UINT;\n        break;\n      default:\n        REPORT_ERROR(\"Malformed MultiCellCommand control block, invalid key \"\n                     \"character: 0x%x\",\n                     key);\n        return;\n      }\n      break;\n\n    case EQUAL:\n      if (parser_buf[pos++] != '=') {\n        REPORT_ERROR(\"Malformed MultiCellCommand control block, no = after \"\n                     \"key, found: 0x%x instead\",\n                     parser_buf[pos - 1]);\n        return;\n      }\n      state = value_state;\n      break;\n\n    case FLAG:\n      switch (key) {\n\n      default:\n        break;\n      }\n      state = AFTER_VALUE;\n      break;\n\n    case INT:\n#define READ_UINT                                                              \\\n  for (i = pos, accumulator = 0; i < MIN(parser_buf_pos, pos + 10); i++) {     \\\n    int64_t n = parser_buf[i] - '0';                                           \\\n    if (n < 0 || n > 9)                                                        \\\n      break;                                                                   \\\n    accumulator += n * digit_multipliers[i - pos];                             \\\n  }                                                                            \\\n  if (i == pos) {                                                              \\\n    REPORT_ERROR(\"Malformed MultiCellCommand control block, expecting an \"     \\\n                 \"integer value for key: %c\",                                  \\\n                 key & 0xFF);                                                  \\\n    return;                                                                    \\\n  }                                                                            \\\n  lcode = accumulator / digit_multipliers[i - pos - 1];                        \\\n  pos = i;                                                                     \\\n  if (lcode > UINT32_MAX) {                                                    \\\n    REPORT_ERROR(                                                              \\\n        \"Malformed MultiCellCommand control block, number is too large\");      \\\n    return;                                                                    \\\n  }                                                                            \\\n  code = lcode;\n\n      is_negative = false;\n      if (parser_buf[pos] == '-') {\n        is_negative = true;\n        pos++;\n      }\n#define I(x)                                                                   \\\n  case x:                                                                      \\\n    g.x = is_negative ? 0 - (int32_t)code : (int32_t)code;                     \\\n    break\n      READ_UINT;\n      switch (key) {\n        ;\n      default:\n        break;\n      }\n      state = AFTER_VALUE;\n      break;\n#undef I\n    case UINT:\n      READ_UINT;\n#define U(x)                                                                   \\\n  case x:                                                                      \\\n    g.x = code;                                                                \\\n    break\n      switch (key) {\n        U(width);\n        U(scale);\n        U(subscale_n);\n        U(subscale_d);\n        U(vertical_align);\n        U(horizontal_align);\n      default:\n        break;\n      }\n      state = AFTER_VALUE;\n      break;\n#undef U\n#undef READ_UINT\n\n    case AFTER_VALUE:\n      switch (parser_buf[pos++]) {\n      default:\n        REPORT_ERROR(\"Malformed MultiCellCommand control block, expecting a : \"\n                     \"or semi-colon after a value, found: 0x%x\",\n                     parser_buf[pos - 1]);\n        return;\n      case ':':\n        state = KEY;\n        break;\n      case ';':\n        state = PAYLOAD;\n        break;\n      }\n      break;\n\n    case PAYLOAD: {\n      sz = parser_buf_pos - pos;\n      payload_start = pos;\n      g.payload_sz = sz;\n      pos = parser_buf_pos;\n    } break;\n\n    } // end switch\n  } // end while\n\n  switch (state) {\n  case EQUAL:\n    REPORT_ERROR(\"Malformed MultiCellCommand control block, no = after key\");\n    return;\n  case INT:\n  case UINT:\n    REPORT_ERROR(\n        \"Malformed MultiCellCommand control block, expecting an integer value\");\n    return;\n  case FLAG:\n    REPORT_ERROR(\n        \"Malformed MultiCellCommand control block, expecting a flag value\");\n    return;\n  default:\n    break;\n  }\n\n  REPORT_VA_COMMAND(\n      \"K s { sI sI sI sI sI sI  ss#}\", self->window_id, \"multicell_command\",\n\n      \"width\", (unsigned int)g.width, \"scale\", (unsigned int)g.scale,\n      \"subscale_n\", (unsigned int)g.subscale_n, \"subscale_d\",\n      (unsigned int)g.subscale_d, \"vertical_align\",\n      (unsigned int)g.vertical_align, \"horizontal_align\",\n      (unsigned int)g.horizontal_align,\n\n      \"\", (char *)parser_buf + payload_start, g.payload_sz);\n\n  screen_handle_multicell_command(self->screen, &g, parser_buf + payload_start);\n}\n"
  },
  {
    "path": "kitty/png-reader.c",
    "content": "/*\n * png-reader.c\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"png-reader.h\"\n#include \"cleanup.h\"\n#include \"state.h\"\n#include <lcms2.h>\n\n\nstatic cmsHPROFILE srgb_profile = NULL;\nstruct fake_file { const uint8_t *buf; size_t sz, cur; };\n\nstatic void\nread_png_from_buffer(png_structp png, png_bytep out, png_size_t length) {\n    struct fake_file *f = png_get_io_ptr(png);\n    if (f) {\n        size_t amt = MIN(length, f->sz - f->cur);\n        memcpy(out, f->buf + f->cur, amt);\n        f->cur += amt;\n    }\n}\n\nstruct custom_error_handler {\n    jmp_buf jb;\n    png_read_data *d;\n};\n\nstatic void\nread_png_error_handler(png_structp png_ptr, png_const_charp msg) {\n    struct custom_error_handler *eh;\n    eh = png_get_error_ptr(png_ptr);\n    if (eh == NULL) fatal(\"read_png_error_handler: could not retrieve error handler\");\n    if(eh->d->err_handler) eh->d->err_handler(eh->d, \"EBADPNG\", msg);\n    longjmp(eh->jb, 1);\n}\n\nstatic void\nread_png_warn_handler(png_structp UNUSED png_ptr, png_const_charp msg) {\n    if (global_state.debug_rendering) log_error(\"libpng WARNING: %s\", msg);\n}\n\n#define ABRT(code, msg) { if(d->err_handler) d->err_handler(d, #code, msg); goto err; }\n\nvoid\ninflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz, int max_image_dimension) {\n    struct fake_file f = {.buf = buf, .sz = bufsz};\n    png_structp png = NULL;\n    png_infop info = NULL;\n    struct custom_error_handler eh = {.d = d};\n    png = png_create_read_struct(PNG_LIBPNG_VER_STRING, &eh, read_png_error_handler, read_png_warn_handler);\n    if (!png) ABRT(ENOMEM, \"Failed to create PNG read structure\");\n    info = png_create_info_struct(png);\n    if (!info) ABRT(ENOMEM, \"Failed to create PNG info structure\");\n\n    if (setjmp(eh.jb)) goto err;\n\n    png_set_read_fn(png, &f, read_png_from_buffer);\n    png_read_info(png, info);\n    png_byte color_type, bit_depth;\n    d->width      = png_get_image_width(png, info);\n    d->height     = png_get_image_height(png, info);\n    // libpng uses too much memory for overly large images\n    if (d->width > max_image_dimension || d->height > max_image_dimension) {\n        ABRT(ENOMEM, \"PNG image is too large\");\n    }\n    color_type = png_get_color_type(png, info);\n    bit_depth  = png_get_bit_depth(png, info);\n    double image_gamma;\n    int intent;\n    cmsHPROFILE input_profile = NULL;\n    cmsHTRANSFORM colorspace_transform = NULL;\n    if (png_get_sRGB(png, info, &intent)) {\n        // do nothing since we output sRGB\n    } else if (png_get_gAMA(png, info, &image_gamma)) {\n        if (image_gamma != 0 && fabs(image_gamma - 1.0/2.2) > 0.0001) png_set_gamma(png, 2.2, image_gamma);\n    } else {\n        // Look for an embedded color profile\n        png_charp name;\n        int compression_type;\n        png_bytep profdata;\n        png_uint_32 proflen;\n        if (png_get_iCCP(png, info, &name, &compression_type, &profdata, &proflen) & PNG_INFO_iCCP) {\n            input_profile = cmsOpenProfileFromMem(profdata, proflen);\n            if (input_profile) {\n                if (!srgb_profile) {\n                    srgb_profile = cmsCreate_sRGBProfile();\n                    if (!srgb_profile) ABRT(ENOMEM, \"Out of memory allocating sRGB colorspace profile\");\n                }\n                colorspace_transform = cmsCreateTransform(\n                    input_profile, TYPE_RGBA_8, srgb_profile, TYPE_RGBA_8, INTENT_PERCEPTUAL, 0);\n\n            }\n        }\n    }\n\n    // Ensure we get RGBA data out of libpng\n    if (bit_depth == 16) png_set_strip_16(png);\n    if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);\n    // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.\n    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png);\n\n    if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png);\n\n    // These color_type don't have an alpha channel then fill it with 0xff.\n    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) png_set_filler(png, 0xFF, PNG_FILLER_AFTER);\n\n    if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png);\n    png_read_update_info(png, info);\n\n    png_uint_32 rowbytes = png_get_rowbytes(png, info);\n    d->sz = sizeof(png_byte) * rowbytes * d->height;\n    d->decompressed = malloc(d->sz + 16);\n    if (d->decompressed == NULL) ABRT(ENOMEM, \"Out of memory allocating decompression buffer for PNG\");\n    d->row_pointers = malloc(d->height * sizeof(png_bytep));\n    if (d->row_pointers == NULL) ABRT(ENOMEM, \"Out of memory allocating row_pointers buffer for PNG\");\n    for (size_t i = 0; i < (size_t)d->height; i++) d->row_pointers[i] = d->decompressed + i * rowbytes * sizeof(png_byte);\n    png_read_image(png, d->row_pointers);\n\n    if (colorspace_transform) {\n        for (int i = 0; i < d->height; i++) {\n            cmsDoTransform(colorspace_transform, d->row_pointers[i], d->row_pointers[i], d->width);\n        }\n        cmsDeleteTransform(colorspace_transform);\n    }\n    if (input_profile) cmsCloseProfile(input_profile);\n\n    d->ok = true;\nerr:\n    if (png) png_destroy_read_struct(&png, info ? &info : NULL, NULL);\n    return;\n}\n\n// Structure to hold memory write state\ntypedef struct {\n    unsigned char* buffer;\n    size_t size, capacity;\n} png_memory_write_state;\n\n// Custom write function for writing PNG data to memory\nstatic void\npng_write_to_memory(png_structp png_ptr, png_bytep data, png_size_t length) {\n    png_memory_write_state* state = (png_memory_write_state*)png_get_io_ptr(png_ptr);\n    if (state->size + length > state->capacity) {\n        // Double the capacity or add enough space for the new data, whichever is larger\n        size_t new_capacity = state->capacity * 2;\n        if (new_capacity < state->size + length) new_capacity = state->size + length;\n        unsigned char* new_buffer = realloc(state->buffer, new_capacity);\n        if (!new_buffer) {\n            png_error(png_ptr, \"Failed to allocate memory for PNG buffer\");\n            return;\n        }\n        state->buffer = new_buffer;\n        state->capacity = new_capacity;\n    }\n    // Copy the data to the buffer\n    memcpy(state->buffer + state->size, data, length);\n    state->size += length;\n}\nstatic void png_flush_memory(png_structp png_ptr) { (void)png_ptr; }\n\nstatic const char*\ncreate_png_from_data(const char *data, size_t width, size_t height, size_t stride, size_t *out_size, bool flip_vertically, int color_type) {\n    *out_size = 0;\n    png_memory_write_state state = {.capacity=width*height * sizeof(uint32_t)};\n    state.buffer = malloc(state.capacity);\n    if (!state.buffer) return \"Out of memory\";\n    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);\n    if (!png_ptr) { free(state.buffer); return \"Failed to create PNG write struct\"; }\n    png_infop info_ptr = png_create_info_struct(png_ptr);\n    if (!info_ptr) {\n        free(state.buffer); png_destroy_write_struct(&png_ptr, NULL);\n        return \"Failed to create PNG info struct\";\n    }\n    if (setjmp(png_jmpbuf(png_ptr))) {\n        png_destroy_write_struct(&png_ptr, &info_ptr); free(state.buffer);\n        return(\"Error during PNG creation\\n\");\n    }\n    png_set_write_fn(png_ptr, &state, png_write_to_memory, png_flush_memory);\n    png_set_IHDR(png_ptr, info_ptr, width, height, 8, color_type,\n                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);\n    // Allocate memory for row pointers\n    png_bytep *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);\n    if (!row_pointers) {\n        png_destroy_write_struct(&png_ptr, &info_ptr);\n        free(state.buffer);\n        return (\"Failed to allocate memory for row pointers\");\n    }\n    if (flip_vertically) for (size_t y = 0; y < height; y++) row_pointers[height - 1 - y] = (png_byte*)&data[y * stride];\n    else for (size_t y = 0; y < height; y++) row_pointers[y] = (png_byte*)&data[y * stride];\n    png_write_info(png_ptr, info_ptr);\n    png_write_image(png_ptr, row_pointers);\n    png_write_end(png_ptr, NULL);\n    png_destroy_write_struct(&png_ptr, &info_ptr);\n    free(row_pointers);\n    *out_size = state.size;\n    return (char*)state.buffer;\n}\n\nconst char*\npng_from_32bit_rgba(const char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically) {\n    return create_png_from_data(data, width, height, 4 * width, out_size, flip_vertically, PNG_COLOR_TYPE_RGBA);\n}\n\nPyObject*\npng_from_32bit_rgba_data(PyObject *self UNUSED, PyObject *args) {\n    int flip_vertically = 0; const char* data; Py_ssize_t len;\n    unsigned width, height;\n    if (!PyArg_ParseTuple(args, \"y#II|p\", &data, &len, &width, &height, &flip_vertically)) return NULL;\n    size_t out_size;\n    const char *out = create_png_from_data(data, width, height, 4 * width, &out_size, flip_vertically, PNG_COLOR_TYPE_RGBA);\n    PyObject *ans = PyBytes_FromStringAndSize(out, out_size);\n    free((void*)out);\n    return ans;\n}\n\n\nconst char*\npng_from_24bit_rgb(const char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically) {\n    return create_png_from_data(data, width, height, 3 * width, out_size, flip_vertically, PNG_COLOR_TYPE_RGB);\n}\n\n\nstatic void\npng_error_handler(png_read_data *d UNUSED, const char *code, const char *msg) {\n    if (!PyErr_Occurred()) PyErr_Format(PyExc_ValueError, \"[%s] %s\", code, msg);\n}\n\nstatic PyObject*\nload_png_data(PyObject *self UNUSED, PyObject *args) {\n    Py_ssize_t sz;\n    const char *data;\n    if (!PyArg_ParseTuple(args, \"s#\", &data, &sz)) return NULL;\n    png_read_data d = {.err_handler=png_error_handler};\n    inflate_png_inner(&d, (const uint8_t*)data, sz, 10000);\n    PyObject *ans = NULL;\n    if (d.ok && !PyErr_Occurred()) {\n        ans = Py_BuildValue(\"y#ii\", d.decompressed, (int)d.sz, d.width, d.height);\n    } else {\n        if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, \"Unknown error while reading PNG data\");\n    }\n    free(d.decompressed);\n    free(d.row_pointers);\n    return ans;\n}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(load_png_data, METH_VARARGS),\n    METHODB(png_from_32bit_rgba_data, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\n\nstatic void\nunload(void) {\n    if (srgb_profile) cmsCloseProfile(srgb_profile);\n    srgb_profile = NULL;\n}\n\nbool\ninit_png_reader(PyObject *module) {\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    register_at_exit_cleanup_func(PNG_READER_CLEANUP_FUNC, unload);\n    return true;\n}\n"
  },
  {
    "path": "kitty/png-reader.h",
    "content": "/*\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <png.h>\n\ntypedef struct png_read_data png_read_data;\n\ntypedef void(*png_error_handler_func)(png_read_data *d, const char*, const char*);\n\ntypedef struct png_read_data {\n    uint8_t *decompressed;\n    bool ok;\n    png_bytep *row_pointers;\n    int width, height;\n    size_t sz;\n    png_error_handler_func err_handler;\n    struct {\n        char *buf;\n        size_t used, capacity;\n    } error;\n} png_read_data;\n\nvoid inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz, int max_image_dimension);\nconst char* png_from_32bit_rgba(const char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically);\nconst char* png_from_24bit_rgb(const char *data, size_t width, size_t height, size_t *out_size, bool flip_vertically);\n"
  },
  {
    "path": "kitty/print-graphics.h",
    "content": "/*\n * Copyright (C) 2026 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include <stdint.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include \"base64.h\"\n\n// Write the kitty graphics protocol escape codes for a 32-bit RGBA image to fp.\nstatic inline void\nfprint_rgba32(FILE *fp, const uint32_t *rgba, uint32_t width, uint32_t height) {\n    const uint8_t *data = (const uint8_t *)rgba;\n    const size_t total_bytes = (size_t)width * height * sizeof(uint32_t);\n    uint8_t b64_buf[4096];\n    size_t offset = 0;\n    bool first = true;\n    while (offset < total_bytes) {\n        size_t chunk = total_bytes - offset;\n        if (chunk > 3072) chunk = 3072;\n        size_t b64_len = sizeof(b64_buf);\n        base64_encode8(data + offset, chunk, b64_buf, &b64_len, false);\n        offset += chunk;\n        const bool last = (offset >= total_bytes);\n        if (first) {\n            fprintf(fp, \"\\x1b_Ga=T,q=2,f=32,s=%u,v=%u,m=%d;\", width, height, last ? 0 : 1);\n            first = false;\n        } else fprintf(fp, \"\\x1b_Gm=%d;\", last ? 0 : 1);\n        fwrite(b64_buf, 1, b64_len, fp);\n        fputs(\"\\x1b\\\\\", fp);\n    }\n}\n\nstatic inline void\nfprint_argb32(FILE *fp, const uint32_t *rgba, uint32_t width, uint32_t height) {\n    const size_t total_bytes = (size_t)width * height * sizeof(uint32_t);\n    uint8_t b64_buf[4096];\n    uint32_t rgba_buf[3072/4];\n    size_t offset = 0;\n    bool first = true;\n    while (offset < total_bytes) {\n        size_t chunk = total_bytes - offset;\n        if (chunk > sizeof(rgba_buf)) chunk = sizeof(rgba_buf);\n        size_t b64_len = sizeof(b64_buf);\n        for (size_t i = 0; i < chunk/4; i++) {\n            uint32_t argb = rgba[offset/4 + i];\n            rgba_buf[i] = ((argb & 0x00FFFFFF) << 8) | ((argb & 0xFF000000) >> 24);\n        }\n        base64_encode8((const uint8_t*)rgba_buf, chunk, b64_buf, &b64_len, false);\n        offset += chunk;\n        const bool last = (offset >= total_bytes);\n        if (first) {\n            fprintf(fp, \"\\x1b_Ga=T,q=2,f=32,s=%u,v=%u,m=%d;\", width, height, last ? 0 : 1);\n            first = false;\n        } else fprintf(fp, \"\\x1b_Gm=%d;\", last ? 0 : 1);\n        fwrite(b64_buf, 1, b64_len, fp);\n        fputs(\"\\x1b\\\\\", fp);\n    }\n}\n\nstatic inline void\nfprint_abgr32(FILE *fp, const uint32_t *src, uint32_t width, uint32_t height) {\n    const size_t total_bytes = (size_t)width * height * sizeof(uint32_t);\n    uint8_t b64_buf[4096];\n    uint32_t rgba_buf[3072/4];\n    size_t offset = 0;\n    bool first = true;\n    while (offset < total_bytes) {\n        size_t chunk = total_bytes - offset;\n        if (chunk > sizeof(rgba_buf)) chunk = sizeof(rgba_buf);\n        size_t b64_len = sizeof(b64_buf);\n        for (size_t i = 0; i < chunk/4; i++) {\n            uint32_t abgr = src[offset/4 + i];\n            rgba_buf[i] = __builtin_bswap32(abgr);\n        }\n        base64_encode8((const uint8_t*)rgba_buf, chunk, b64_buf, &b64_len, false);\n        offset += chunk;\n        const bool last = (offset >= total_bytes);\n        if (first) {\n            fprintf(fp, \"\\x1b_Ga=T,q=2,f=32,s=%u,v=%u,m=%d;\", width, height, last ? 0 : 1);\n            first = false;\n        } else fprintf(fp, \"\\x1b_Gm=%d;\", last ? 0 : 1);\n        fwrite(b64_buf, 1, b64_len, fp);\n        fputs(\"\\x1b\\\\\", fp);\n    }\n}\n\n\nstatic inline void\nprint_rgba32(const uint32_t *rgba, uint32_t width, uint32_t height) { fprint_rgba32(stdout, rgba, width, height); }\nstatic inline void\nprint_argb32(const uint32_t *rgba, uint32_t width, uint32_t height) { fprint_argb32(stdout, rgba, width, height); }\nstatic inline void\nprint_abgr32(const uint32_t *rgba, uint32_t width, uint32_t height) { fprint_abgr32(stdout, rgba, width, height); }\n"
  },
  {
    "path": "kitty/progress.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom enum import Enum\n\nfrom .fast_data_types import monotonic\nfrom .utils import log_error\n\n\nclass ProgressState(Enum):\n    unset = 0\n    set = 1\n    error = 2\n    indeterminate = 3\n    paused = 4\n\n\nclass Progress:\n\n    state: ProgressState = ProgressState.unset\n    percent: int = 0\n    last_update_at: float = 0.\n    clear_timeout: float = 60.0\n    finished_clear_timeout: float = 5.0\n\n    def update(self, st: int, percent: int = -1) -> None:\n        self.last_update_at = monotonic()\n        if st == 0:\n            self.state = ProgressState.unset\n            self.percent = 0\n        elif st == 1:\n            self.state = ProgressState.set\n            self.percent = max(0, min(percent, 100))\n        elif st == 2:\n            self.state = ProgressState.error\n            self.percent = 0\n        elif st == 3:\n            self.state = ProgressState.indeterminate\n            self.percent = 0\n        elif st == 4:\n            self.state = ProgressState.paused\n            if percent > -1:\n                self.percent = max(0, min(percent, 100))\n        else:\n            log_error(f'Unknown OSC 9;4 state: {st}')\n\n    def clear_progress(self) -> bool:\n        time_since_last_update = monotonic() - self.last_update_at\n        threshold = self.finished_clear_timeout if self.percent == 100 and self.state is ProgressState.set else self.clear_timeout\n        if time_since_last_update >= threshold:\n            self.state = ProgressState.unset\n            self.percent = 0\n            return True\n        return False\n"
  },
  {
    "path": "kitty/rc/__init__.py",
    "content": ""
  },
  {
    "path": "kitty/rc/action.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import (\n    MATCH_WINDOW_OPTION,\n    ArgsType,\n    Boss,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    RemoteControlErrorWithoutTraceback,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import ActionRCOptions as CLIOptions\n\n\nclass Action(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    action+/str: The action to perform. Of the form: action [optional args...]\n    match_window/str: Window to run the action on\n    self/bool: Whether to use the window this command is run in as the active window\n    '''\n\n    short_desc = 'Run the specified mappable action'\n    desc = (\n        'Run the specified mappable action. For a list of all available mappable actions, see :doc:`actions`.'\n        ' Any arguments for ACTION should follow the action. Note that parsing of arguments is action dependent'\n        ' so for best results specify all arguments as single string on the command line in the same format as you would'\n        ' use for that action in kitty.conf.'\n    )\n    options_spec = '''\\\n--self\ntype=bool-set\nRun the action on the window this command is run in instead of the active window.\n\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response indicating the success of the action. Note that\nusing this option means that you will not be notified of failures.\n''' + '\\n\\n' + MATCH_WINDOW_OPTION\n\n    args = RemoteCommand.Args(\n        spec='ACTION [ARGS FOR ACTION...]', json_field='action', minimum_count=1,\n        completion=RemoteCommand.CompletionSpec.from_string('type:special group:complete_actions')\n    )\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'action': ' '.join(args), 'self': opts.self, 'match_window': opts.match}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        w = self.windows_for_match_payload(boss, window, payload_get)\n        if w:\n            window = w[0]\n        ac = payload_get('action')\n        if not ac:\n            raise RemoteControlErrorWithoutTraceback('Must specify an action')\n\n        try:\n            consumed = boss.combine(str(ac), window, raise_error=True)\n        except (Exception, SystemExit) as e:\n            raise RemoteControlErrorWithoutTraceback(str(e))\n\n        if not consumed:\n            raise RemoteControlErrorWithoutTraceback(f'Unknown action: {ac}')\n        return None\n\n\naction = Action()\n"
  },
  {
    "path": "kitty/rc/base.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Callable, Iterable, Iterator\nfrom contextlib import suppress\nfrom dataclasses import dataclass, field\nfrom io import BytesIO\nfrom typing import TYPE_CHECKING, Any, NoReturn, Optional, Union, cast\n\nfrom kitty.cli import get_defaults_from_seq, parse_args\nfrom kitty.cli_stub import RCOptions as R\nfrom kitty.constants import list_kitty_resources, running_in_kitty\nfrom kitty.simple_cli_definitions import CompletionSpec, parse_option_spec\nfrom kitty.types import AsyncResponse\n\nif TYPE_CHECKING:\n    from kitty.boss import Boss as B\n    from kitty.tabs import Tab as T\n    from kitty.window import Window as W\n    Window = W\n    Boss = B\n    Tab = T\nelse:\n    Boss = Window = Tab = object\nRCOptions = R\n\n\nclass NoResponse:\n    pass\n\n\nclass RemoteControlError(Exception):\n    pass\n\n\nclass RemoteControlErrorWithoutTraceback(Exception):\n\n    hide_traceback = True\n\n\nclass MatchError(ValueError):\n\n    hide_traceback = True\n\n    def __init__(self, expression: str, target: str = 'windows'):\n        ValueError.__init__(self, f'No matching {target} for expression: {expression}')\n\n\nclass OpacityError(ValueError):\n\n    hide_traceback = True\n\n\nclass UnknownLayout(ValueError):\n\n    hide_traceback = True\n\n\nclass StreamError(ValueError):\n\n    hide_traceback = True\n\n\nclass PayloadGetter:\n\n    def __init__(self, cmd: 'RemoteCommand', payload: dict[str, Any]):\n        self.payload = payload\n        self.cmd = cmd\n\n    def __call__(self, key: str, opt_name: str | None = None, missing: Any = None) -> Any:\n        ans = self.payload.get(key, payload_get)\n        if ans is not payload_get:\n            return ans\n        return self.cmd.get_default(opt_name or key, missing=missing)\n\n\nno_response = NoResponse()\npayload_get = object()\nResponseType = Union[bool, str, None, NoResponse, AsyncResponse]\nCmdReturnType = Union[dict[str, Any], list[Any], tuple[Any, ...], str, int, float, bool]\nCmdGenerator = Iterator[CmdReturnType]\nPayloadType = Optional[Union[CmdReturnType, CmdGenerator]]\nPayloadGetType = PayloadGetter\nArgsType = list[str]\nImageCompletion = CompletionSpec.from_string('type:file group:\"Images\" ext:png,jpg,jpeg,webp,gif,bmp,tiff')\nSUPPORTED_IMAGE_FORMATS = tuple(x.upper() for x in ImageCompletion.extensions if x != 'jpg')\n\n\nMATCH_WINDOW_OPTION = '''\\\n--match -m\nThe window to match. Match specifications are of the form: :italic:`field:query`.\nWhere :italic:`field` can be one of: :code:`id`, :code:`title`, :code:`pid`, :code:`cwd`, :code:`cmdline`, :code:`num`,\n:code:`env`, :code:`var`, :code:`state`, :code:`neighbor`, :code:`session` and :code:`recent`.\n:italic:`query` is the expression to match. Expressions can be either a number or a regular expression, and can be\n:ref:`combined using Boolean operators <search_syntax>`.\n\nThe special value :code:`all` matches all windows.\n\nFor numeric fields: :code:`id`, :code:`pid`, :code:`num` and :code:`recent`, the expression is interpreted as\na number, not a regular expression. Negative values for :code:`id` match from the highest id number down, in particular,\n-1 is the most recently created window.\n\nThe field :code:`num` refers to the window position in the current tab, starting from zero and counting clockwise (this\nis the same as the order in which the windows are reported by the :ref:`kitten @ ls <at-ls>` command).\n\nThe window id of the current window is available as the :envvar:`KITTY_WINDOW_ID` environment variable.\n\nThe field :code:`recent` refers to recently active windows in the currently active tab, with zero being the currently\nactive window, one being the previously active window and so on.\n\nThe field :code:`neighbor` refers to a neighbor of the active window in the specified direction, which can be:\n:code:`left`, :code:`right`, :code:`top` or :code:`bottom`.\n\nThe field :code:`session` matches windows that were created in the specified session.\nUse the expression :code:`^$` to match windows that were not created in a session and\n:code:`.` to match the currently active session and :code:`~` to match either the currently\nactive session or the last active session when no session is active.\n\nWhen using the :code:`env` field to match on environment variables, you can specify only the environment variable name\nor a name and value, for example, :code:`env:MY_ENV_VAR=2`.\n\nSimilarly, the :code:`var` field matches on user variables set on the window. You can specify name or name and value\nas with the :code:`env` field.\n\nThe field :code:`state` matches on the state of the window. Supported states\nare: :code:`active`, :code:`focused`, :code:`needs_attention`,\n:code:`parent_active`, :code:`parent_focused`, :code:`focused_os_window`,\n:code:`self`, :code:`overlay_parent`.  Active windows are the windows that are\nactive in their parent tab. There is only one focused window and it is the\nwindow to which keyboard events are delivered. If no window is focused, the\nlast focused window is matched. The value :code:`focused_os_window` matches\nall windows in the currently focused OS window. The value :code:`self` matches\nthe window in which the remote control command is run. The value\n:code:`overlay_parent` matches the window that is under the :code:`self`\nwindow, when the self window is an overlay.\n\nNote that you can use the :ref:`kitten @ ls <at-ls>` command to get a list of windows.\n'''\nMATCH_TAB_OPTION = '''\\\n--match -m\nThe tab to match. Match specifications are of the form: :italic:`field:query`.\nWhere :italic:`field` can be one of: :code:`id`, :code:`index`, :code:`title`, :code:`window_id`, :code:`window_title`,\n:code:`pid`, :code:`cwd`, :code:`cmdline` :code:`env`, :code:`var`, :code:`state`, :code:`session` and :code:`recent`.\n:italic:`query` is the expression to match. Expressions can be either a number or a regular expression, and can be\n:ref:`combined using Boolean operators <search_syntax>`.\n\nThe special value :code:`all` matches all tabs.\n\nFor numeric fields: :code:`id`, :code:`index`, :code:`window_id`, :code:`pid` and :code:`recent`, the\nexpression is interpreted as a number, not a regular expression. Negative values for :code:`id`/:code:`window_id` match\nfrom the highest id number down, in particular, -1 is the most recently created tab/window.\n\nWhen using :code:`title` or :code:`id`, first a matching tab is looked for, and if not found a matching window is looked\nfor, and the tab for that window is used.\n\nYou can also use :code:`window_id` and :code:`window_title` to match the tab that contains the window with the specified\nid or title.\n\nThe :code:`index` number is used to match the nth tab in the currently active OS window.\nThe :code:`recent` number matches recently active tabs in the currently active OS window, with zero being the currently\nactive tab, one the previously active tab and so on.\n\nThe field :code:`session` matches tabs that were created in the specified session.\nUse the expression :code:`^$` to match windows that were not created in a session and\n:code:`.` to match the currently active session and :code:`~` to match either the currently\nactive session or the last active session when no session is active.\n\nWhen using the :code:`env` field to match on environment variables, you can specify only the environment variable name\nor a name and value, for example, :code:`env:MY_ENV_VAR=2`. Tabs containing any window with the specified environment\nvariables are matched. Similarly, :code:`var` matches tabs containing any window with the specified user variable.\n\nThe field :code:`state` matches on the state of the tab. Supported states are:\n:code:`active`, :code:`focused`, :code:`needs_attention`, :code:`parent_active`, :code:`parent_focused`\nand :code:`focused_os_window`.\nActive tabs are the tabs that are active in their parent OS window. There is only one focused tab\nand it is the tab to which keyboard events are delivered. If no tab is focused, the last focused tab is matched.\nThe value :code:`focused_os_window` matches all tabs in the currently focused OS window.\n\nNote that you can use the :ref:`kitten @ ls <at-ls>` command to get a list of tabs.\n'''\n\n\nclass ParsingOfArgsFailed(ValueError):\n    pass\n\n\nclass AsyncResponder:\n\n    def __init__(self, payload_get: PayloadGetType, window: Window | None) -> None:\n        self.async_id: str = payload_get('async_id', missing='')\n        self.peer_id: int = payload_get('peer_id', missing=0)\n        self.window_id: int = getattr(window, 'id', 0)\n\n    def send_data(self, data: Any) -> None:\n        from kitty.remote_control import send_response_to_client\n        send_response_to_client(data=data, peer_id=self.peer_id, window_id=self.window_id, async_id=self.async_id)\n\n    def send_error(self, error: str) -> None:\n        from kitty.remote_control import send_response_to_client\n        send_response_to_client(error=error, peer_id=self.peer_id, window_id=self.window_id, async_id=self.async_id)\n\n\n@dataclass(frozen=True)\nclass ArgsHandling:\n\n    json_field: str = ''\n    count: int | None = None\n    spec: str = ''\n    completion: CompletionSpec = field(default_factory=CompletionSpec)\n    value_if_unspecified: tuple[str, ...] = ()\n    minimum_count: int = -1\n    first_rest: tuple[str, str] | None = None\n    special_parse: str = ''\n    args_choices: Callable[[], Iterable[str]] | None = None\n\n    @property\n    def args_count(self) -> int | None:\n        if not self.spec:\n            return 0\n        return self.count\n\n    def as_go_completion_code(self, go_name: str) -> Iterator[str]:\n        c = self.args_count\n        if c is not None:\n            yield f'{go_name}.StopCompletingAtArg = {c}'\n        if self.completion:\n            yield from self.completion.as_go_code(go_name + '.ArgCompleter', ' = ')\n\n    def as_go_code(self, cmd_name: str, field_types: dict[str, str], handled_fields: set[str]) -> Iterator[str]:\n        c = self.args_count\n        if c == 0:\n            yield f'if len(args) != 0 {{ return fmt.Errorf(\"%s\", \"Unknown extra argument(s) supplied to {cmd_name}\") }}'\n            return\n        if c is not None:\n            yield f'if len(args) != {c} {{ return fmt.Errorf(\"%s\", \"Must specify exactly {c} argument(s) for {cmd_name}\") }}'\n        if self.value_if_unspecified:\n            yield 'if len(args) == 0 {'\n            for x in self.value_if_unspecified:\n                yield f'args = append(args, \"{x}\")'\n            yield '}'\n        if self.minimum_count > -1:\n            if self.minimum_count == 1:\n                yield f'if len(args) < {self.minimum_count} {{ return fmt.Errorf(\"%s\", \"Must specify at least one argument to {cmd_name}\") }}'\n            else:\n                yield f'if len(args) < {self.minimum_count} {{ return fmt.Errorf(\"%s\", \"Must specify at least {self.minimum_count} arguments to {cmd_name}\") }}'\n        if self.args_choices:\n            achoices = tuple(self.args_choices())\n            yield 'achoices := map[string]bool{' + ' '.join(f'\"{x}\":true,' for x in achoices) + '}'\n            yield 'for _, a := range args {'\n            yield 'if !achoices[a] { return fmt.Errorf(\"Not a valid choice: %s. Allowed values are: %s\", a, \"' + ', '.join(achoices) + '\") }'\n            yield '}'\n        if self.json_field:\n            jf = self.json_field\n            dest = f'payload.{jf.capitalize()}'\n            jt = field_types[jf]\n            if self.first_rest:\n                yield f'payload.{self.first_rest[0].capitalize()} = escaped_string(args[0])'\n                yield f'payload.{self.first_rest[1].capitalize()} = escape_list_of_strings(args[1:])'\n                handled_fields.add(self.first_rest[0])\n                handled_fields.add(self.first_rest[1])\n                return\n            handled_fields.add(self.json_field)\n            if self.special_parse:\n                if self.special_parse.startswith('!'):\n                    yield f'io_data.multiple_payload_generator, err = {self.special_parse[1:]}'\n                elif self.special_parse.startswith('+'):\n                    fields, sp = self.special_parse[1:].split(':', 1)\n                    handled_fields.update(set(fields.split(',')))\n                    if sp.startswith('!'):\n                        yield f'io_data.multiple_payload_generator, err = {sp[1:]}'\n                    else:\n                        yield f'err = {sp}'\n                else:\n                    yield f'{dest}, err = {self.special_parse}'\n                yield 'if err != nil { return err }'\n                return\n            if jt == 'list.str':\n                yield f'{dest} = escape_list_of_strings(args)'\n                return\n            if jt == 'str':\n                if c == 1:\n                    yield f'{dest} = escaped_string(args[0])'\n                else:\n                    yield f'{dest} = escaped_string(strings.Join(args, \" \"))'\n                return\n            if jt.startswith('choices.'):\n                yield f'if len(args) != 1 {{ return fmt.Errorf(\"%s\", \"Must specify exactly 1 argument for {cmd_name}\") }}'\n                choices = \", \".join(f'\"{x}\"' for x in jt.split('.')[1:])\n                yield 'switch(args[0]) {'\n                yield f'case {choices}:\\n\\t{dest} = args[0]'\n                yield f'default: return fmt.Errorf(\"%s is not a valid choice. Allowed values: %s\", args[0], `{choices}`)'\n                yield '}'\n                return\n            if jt == 'dict.str':\n                yield f'{dest} = parse_key_val_args(args)'\n                return\n        raise TypeError(f'Unknown args handling for cmd: {cmd_name}')\n\n\nclass StreamInFlight:\n\n    def __init__(self) -> None:\n        self.stream_id = ''\n        self.tempfile: BytesIO | None = None\n\n    def handle_data(self, stream_id: str, data: bytes) -> AsyncResponse | BytesIO:\n        from ..remote_control import close_active_stream\n        def abort_stream() -> None:\n            close_active_stream(self.stream_id)\n            self.stream_id = ''\n            if self.tempfile is not None:\n                self.tempfile.close()\n                self.tempfile = None\n\n        if stream_id != self.stream_id:\n            abort_stream()\n            self.stream_id = stream_id\n        if self.tempfile is None:\n            self.tempfile = BytesIO()\n        t = self.tempfile\n        if data:\n            if (t.tell() + len(data)) > 128 * 1024 * 1024:\n                abort_stream()\n                raise StreamError('Too much data being sent')\n            t.write(data)\n            return AsyncResponse()\n        close_active_stream(self.stream_id)\n        self.stream_id = ''\n        self.tempfile = None\n        t.flush()\n        return t\n\n\nclass RemoteCommand:\n    Args = ArgsHandling\n    CompletionSpec = CompletionSpec\n\n    name: str = ''\n    short_desc: str = ''\n    desc: str = ''\n    args: ArgsHandling = ArgsHandling()\n    options_spec: str | None = None\n    response_timeout: float = 10.  # seconds\n    string_return_is_error: bool = False\n    defaults: dict[str, Any] | None = None\n    is_asynchronous: bool = False\n    options_class: type[RCOptions] = RCOptions\n    protocol_spec: str = ''\n    argspec = args_count = args_completion = ArgsHandling()\n    field_to_option_map: dict[str, str] | None = None\n    reads_streaming_data: bool = False\n    disallow_responses: bool = False\n\n    def __init__(self) -> None:\n        self.desc = self.desc or self.short_desc\n        self.name = self.__class__.__module__.split('.')[-1].replace('_', '-')\n        self.stream_in_flight = StreamInFlight()\n\n    def fatal(self, msg: str) -> NoReturn:\n        if running_in_kitty():\n            raise RemoteControlError(msg)\n        raise SystemExit(msg)\n\n    def get_default(self, name: str, missing: Any = None) -> Any:\n        if self.options_spec:\n            if self.defaults is None:\n                self.defaults = get_defaults_from_seq(parse_option_spec(self.options_spec)[0])\n            return self.defaults.get(name, missing)\n        return missing\n\n    def windows_for_match_payload(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> list['Window']:\n        if payload_get('all'):\n            windows = list(boss.all_windows)\n        else:\n            self_window = window\n            if payload_get('self') in (None, True):\n                window = window or boss.active_window\n            else:\n                window = boss.active_window or window\n            windows = [window] if window else []\n            if payload_get('match'):\n                windows = list(boss.match_windows(payload_get('match'), self_window))\n                if not windows:\n                    raise MatchError(payload_get('match'))\n        return windows\n\n    def tabs_for_match_payload(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> list['Tab']:\n        if payload_get('all'):\n            return list(boss.all_tabs)\n        match = payload_get('match')\n        if match:\n            tabs = list(boss.match_tabs(match))\n            if not tabs:\n                raise MatchError(match, 'tabs')\n            return tabs\n        if window and payload_get('self') in (None, True):\n            if q := window.tabref():\n                return [q]\n        t = boss.active_tab\n        if t:\n            return [t]\n        return []\n\n    def windows_for_payload(\n        self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType,\n        window_match_name: str = 'match_window', tab_match_name: str = 'match_tab',\n    ) -> list['Window']:\n        if payload_get('all'):\n            windows = list(boss.all_windows)\n        else:\n            window = window or boss.active_window\n            windows = [window] if window else []\n            if payload_get(window_match_name):\n                windows = list(boss.match_windows(payload_get(window_match_name), window))\n                if not windows:\n                    raise MatchError(payload_get(window_match_name))\n            if payload_get(tab_match_name):\n                tabs = tuple(boss.match_tabs(payload_get(tab_match_name)))\n                if not tabs:\n                    raise MatchError(payload_get(tab_match_name), 'tabs')\n                windows = []\n                for tab in tabs:\n                    windows += list(tab)\n        return windows\n\n    def create_async_responder(self, payload_get: PayloadGetType, window: Window | None) -> AsyncResponder:\n        return AsyncResponder(payload_get, window)\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: Any, args: ArgsType) -> PayloadType:\n        raise NotImplementedError()\n\n    def response_from_kitty(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> ResponseType:\n        raise NotImplementedError()\n\n    def cancel_async_request(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> None:\n        pass\n\n    def handle_streamed_data(self, data: bytes, payload_get: PayloadGetType) -> BytesIO | AsyncResponse:\n        stream_id = payload_get('stream_id')\n        if not stream_id or not isinstance(stream_id, str):\n            raise StreamError('No stream_id in rc payload')\n        return self.stream_in_flight.handle_data(stream_id, data)\n\n\ndef cli_params_for(command: RemoteCommand) -> tuple[Callable[[], str], str, str, str]:\n    return (command.options_spec or '\\n').format, command.args.spec, command.desc, f'kitten @ {command.name}'\n\n\ndef parse_subcommand_cli(command: RemoteCommand, args: ArgsType) -> tuple[Any, ArgsType]:\n    opts, items = parse_args(args[1:], *cli_params_for(command), result_class=command.options_class)\n    if command.args.args_count is not None and command.args.args_count != len(items):\n        if command.args.args_count == 0:\n            raise SystemExit(f'Unknown extra argument(s) supplied to {command.name}')\n        raise SystemExit(f'Must specify exactly {command.args.args_count} argument(s) for {command.name}')\n    return opts, items\n\n\ndef display_subcommand_help(func: RemoteCommand) -> None:\n    with suppress(SystemExit):\n        parse_args(['--help'], (func.options_spec or '\\n').format, func.args.spec, func.desc, func.name)\n\n\ndef command_for_name(cmd_name: str) -> RemoteCommand:\n    from importlib import import_module\n    cmd_name = cmd_name.replace('-', '_')\n    try:\n        m = import_module(f'kitty.rc.{cmd_name}')\n    except ImportError:\n        raise KeyError(f'Unknown kitty remote control command: {cmd_name}')\n    return cast(RemoteCommand, getattr(m, cmd_name))\n\n\ndef all_command_names() -> frozenset[str]:\n\n    def ok(name: str) -> bool:\n        root, _, ext = name.rpartition('.')\n        return bool(ext in ('py', 'pyc', 'pyo') and root and root not in ('base', '__init__'))\n\n    return frozenset({x.rpartition('.')[0] for x in filter(ok, list_kitty_resources('kitty.rc'))})\n"
  },
  {
    "path": "kitty/rc/close_tab.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, MatchError, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import CloseTabRCOptions as CLIOptions\n\n\nclass CloseTab(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: Which tab to close\n    self/bool: Boolean indicating whether to close the tab of the window the command is run in\n    ignore_no_match/bool: Boolean indicating whether no matches should be ignored or return an error\n    '''\n\n    short_desc = 'Close the specified tabs'\n    desc = '''\\\nClose an arbitrary set of tabs. The :code:`--match` option can be used to\nspecify complex sets of tabs to close. For example, to close all non-focused\ntabs in the currently focused OS window, use::\n\n    kitten @ close-tab --match \"not state:focused and state:parent_focused\"\n'''\n    options_spec = MATCH_TAB_OPTION + '''\\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response indicating the success of the action. Note that\nusing this option means that you will not be notified of failures.\n\n\n--self\ntype=bool-set\nClose the tab of the window this command is run in, rather than the active tab.\n\n\n--ignore-no-match\ntype=bool-set\nDo not return an error if no tabs are matched to be closed.\n'''\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'self': opts.self, 'ignore_no_match': opts.ignore_no_match}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        try:\n            tabs = self.tabs_for_match_payload(boss, window, payload_get)\n        except MatchError:\n            if payload_get('ignore_no_match'):\n                return None\n            raise\n        for tab in tuple(tabs):\n            if tab:\n                boss.close_tab_no_confirm(tab)\n        return None\n\n\nclose_tab = CloseTab()\n"
  },
  {
    "path": "kitty/rc/close_window.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import CloseWindowRCOptions as CLIOptions\n\n\nclass CloseWindow(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    match/str: Which window to close\n    self/bool: Boolean indicating whether to close the window the command is run in\n    ignore_no_match/bool: Boolean indicating whether no matches should be ignored or return an error\n    '''\n\n    short_desc = 'Close the specified windows'\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response indicating the success of the action. Note that\nusing this option means that you will not be notified of failures.\n\n\n--self\ntype=bool-set\nClose the window this command is run in, rather than the active window.\n\n\n--ignore-no-match\ntype=bool-set\nDo not return an error if no windows are matched to be closed.\n'''\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'self': opts.self, 'ignore_no_match': opts.ignore_no_match}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        try:\n            windows = self.windows_for_match_payload(boss, window, payload_get)\n        except MatchError:\n            if payload_get('ignore_no_match'):\n                return None\n            raise\n        for window in tuple(windows):\n            if window:\n                boss.mark_window_for_close(window)\n        return None\n\n\nclose_window = CloseWindow()\n"
  },
  {
    "path": "kitty/rc/create_marker.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom kitty.options.utils import parse_marker_spec\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import CreateMarkerRCOptions as CLIOptions\n\n\nclass CreateMarker(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: Which window to create the marker in\n    self/bool: Boolean indicating whether to create marker in the window the command is run in\n    marker_spec/list.str: A list or arguments that define the marker specification, for example: ['text', '1', 'ERROR']\n    '''\n\n    short_desc = 'Create a marker that highlights specified text'\n    desc = (\n        'Create a marker which can highlight text in the specified window. For example:'\n        ' :code:`create_marker text 1 ERROR`. For full details see: :doc:`marks`'\n    )\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\n--self\ntype=bool-set\nApply marker to the window this command is run in, rather than the active window.\n'''\n    args = RemoteCommand.Args(spec='MARKER SPECIFICATION', json_field='marker_spec', minimum_count=2)\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if len(args) < 2:\n            self.fatal('Invalid marker specification: {}'.format(' '.join(args)))\n        try:\n            parse_marker_spec(args[0], args[1:])\n        except Exception as err:\n            self.fatal(f\"Failed to parse marker specification {' '.join(args)} with error: {err}\")\n        return {'match': opts.match, 'self': opts.self, 'marker_spec': args}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        args = payload_get('marker_spec')\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                window.set_marker(args)\n        return None\n\n\ncreate_marker = CreateMarker()\n"
  },
  {
    "path": "kitty/rc/detach_tab.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, MatchError, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import DetachTabRCOptions as CLIOptions\n\n\nclass DetachTab(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: Which tab to detach\n    target_tab/str: Which tab to move the detached tab to the OS window it is run in\n    self/bool: Boolean indicating whether to detach the tab the command is run in\n    '''\n\n    short_desc = 'Detach the specified tabs and place them in a different/new OS window'\n    desc = (\n        'Detach the specified tabs and either move them into a new OS window'\n        ' or add them to the OS window containing the tab specified by :option:`kitten @ detach-tab --target-tab`'\n    )\n    options_spec = MATCH_TAB_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--target-tab -t') + '''\\n\n--self\ntype=bool-set\nDetach the tab this command is run in, rather than the active tab.\n'''\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'target_tab': opts.target_tab, 'self': opts.self}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        match = payload_get('target_tab')\n        kwargs = {}\n        if match:\n            targets = tuple(boss.match_tabs(match))\n            if not targets:\n                raise MatchError(match, 'tabs')\n            if targets[0]:\n                kwargs['target_os_window_id'] = targets[0].os_window_id\n\n        for tab in self.tabs_for_match_payload(boss, window, payload_get):\n            if tab:\n                boss._move_tab_to(tab=tab, **kwargs)\n        return None\n\n\ndetach_tab = DetachTab()\n"
  },
  {
    "path": "kitty/rc/detach_window.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import DetachWindowRCOptions as CLIOptions\n\n\nclass DetachWindow(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: Which window to detach\n    target_tab/str: Which tab to move the detached window to\n    self/bool: Boolean indicating whether to detach the window the command is run in\n    stay_in_tab/bool: Boolean indicating focus should remain in the active tab after windows are moved\n    '''\n\n    short_desc = 'Detach the specified windows and place them in a different/new tab'\n    desc = (\n        'Detach the specified windows and either move them into a new tab, a new OS window'\n        ' or add them to the specified tab. Use the special value :code:`new` for :option:`kitten @ detach-window --target-tab`'\n        ' to move to a new tab. If no target tab is specified the windows are moved to a new OS window.'\n    )\n    options_spec = (\n        MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--target-tab -t') +\n        '''Use the special value :code:`new` to move to a new tab.\n\n\n--self\ntype=bool-set\nDetach the window this command is run in, rather than the active window.\n\n\n--stay-in-tab\ntype=bool-set\nKeep the focus on a window in the currently focused tab after moving the specified windows.\n''')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'target_tab': opts.target_tab, 'self': opts.self, 'stay_in_tab': opts.stay_in_tab}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        windows = self.windows_for_match_payload(boss, window, payload_get)\n        match = payload_get('target_tab')\n        target_tab_id: str | int | None = None\n        newval: str | int = 'new'\n        if match:\n            if match == 'new':\n                target_tab_id = newval\n            else:\n                tabs = tuple(boss.match_tabs(match))\n                if not tabs:\n                    raise MatchError(match, 'tabs')\n                target_tab_id = tabs[0].id\n        kwargs = {'target_os_window_id': newval} if target_tab_id is None else {'target_tab_id': target_tab_id}\n        tab = boss.active_tab\n        for window in windows:\n            if window:\n                boss._move_window_to(window=window, **kwargs)\n        if payload_get('stay_in_tab') and tab is not None:\n            tab.make_active()\n        return None\n\n\ndetach_window = DetachWindow()\n"
  },
  {
    "path": "kitty/rc/disable_ligatures.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import DisableLigaturesRCOptions as CLIOptions\n\n\nclass DisableLigatures(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    strategy+/choices.never.always.cursor: One of :code:`never`, :code:`always` or :code:`cursor`\n    match_window/str: Window to change opacity in\n    match_tab/str: Tab to change opacity in\n    all/bool: Boolean indicating operate on all windows\n    '''\n\n    short_desc = 'Control ligature rendering'\n    desc = (\n        'Control ligature rendering for the specified windows/tabs (defaults to active window). The :italic:`STRATEGY`'\n        ' can be one of: :code:`never`, :code:`always`, :code:`cursor`.'\n    )\n    options_spec = '''\\\n--all -a\ntype=bool-set\nBy default, ligatures are only affected in the active window. This option will\ncause ligatures to be changed in all windows.\n\n''' + '\\n\\n' + MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')\n    args = RemoteCommand.Args(spec='STRATEGY', count=1, json_field='strategy')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if not args:\n            self.fatal(\n                'You must specify the STRATEGY for disabling ligatures, must be one of'\n                ' never, always or cursor')\n        strategy = args[0]\n        if strategy not in ('never', 'always', 'cursor'):\n            self.fatal(f'{strategy} is not a valid disable_ligatures strategy')\n        return {\n            'strategy': strategy, 'match_window': opts.match, 'match_tab': opts.match_tab,\n            'all': opts.all,\n        }\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        windows = self.windows_for_payload(boss, window, payload_get)\n        boss.disable_ligatures_in(windows, payload_get('strategy'))\n        return None\n# }}}\n\n\ndisable_ligatures = DisableLigatures()\n"
  },
  {
    "path": "kitty/rc/env.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import Any\n\nfrom .base import ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\n\nclass Env(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    env+/dict.str: Dictionary of environment variables to values. When a env var ends with = it is removed from the environment.\n    '''\n\n    short_desc = 'Change environment variables seen by future children'\n    desc = (\n        'Change the environment variables that will be seen in newly launched windows.'\n        ' Similar to the :opt:`env` option in :file:`kitty.conf`, but affects running kitty instances.'\n        ' If no = is present, the variable is removed from the environment.'\n    )\n    args = RemoteCommand.Args(spec='env_var1=val env_var2=val ...', minimum_count=1, json_field='env')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: Any, args: ArgsType) -> PayloadType:\n        if len(args) < 1:\n            self.fatal('Must specify at least one env var to set')\n        env = {}\n        for x in args:\n            if '=' in x:\n                key, val = x.split('=', 1)\n                env[key] = val\n            else:\n                env[x + '='] = ''\n        return {'env': env}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        from kitty.child import default_env, set_default_env\n        from kitty.utils import expandvars\n        new_env = payload_get('env') or {}\n        env = default_env().copy()\n        for k, v in new_env.items():\n            if k.endswith('='):\n                env.pop(k[:-1], None)\n            else:\n                env[k] = expandvars(v or '', env)\n        set_default_env(env)\n        return None\n\n\nenv = Env()\n"
  },
  {
    "path": "kitty/rc/focus_tab.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import FocusTabRCOptions as CLIOptions\n\n\nclass FocusTab(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: The tab to focus\n    '''\n\n    short_desc = 'Focus the specified tab'\n    desc = 'The active window in the specified tab will be focused.'\n    options_spec = MATCH_TAB_OPTION + '''\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response indicating the success of the action. Note that\nusing this option means that you will not be notified of failures.\n'''\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'no_response': opts.no_response}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        for tab in self.tabs_for_match_payload(boss, window, payload_get):\n            if tab:\n                window = tab.active_window\n                if window:\n                    boss.set_active_window(window, switch_os_window_if_needed=True)\n                else:\n                    boss.set_active_tab(tab)\n                break\n        return None\n\n\nfocus_tab = FocusTab()\n"
  },
  {
    "path": "kitty/rc/focus_window.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import FocusWindowRCOptions as CLIOptions\n\n\nclass FocusWindow(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    match/str: The window to focus\n    '''\n\n    short_desc = 'Focus the specified window'\n    desc = 'Focus the specified window, if no window is specified, focus the window this command is run inside.'\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response from kitty. This means that even if no matching window is found,\nthe command will exit with a success code.\n'''\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                boss.set_active_window(window, switch_os_window_if_needed=True)\n                break\n        return None\n\n\nfocus_window = FocusWindow()\n"
  },
  {
    "path": "kitty/rc/get_colors.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom kitty.fast_data_types import Color\nfrom kitty.rgb import color_as_sharp, color_from_int\nfrom kitty.utils import natsort_ints\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import GetColorsRCOptions as CLIOptions\n\n\nclass GetColors(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: The window to get the colors for\n    configured/bool: Boolean indicating whether to get configured or current colors\n    '''\n\n    short_desc = 'Get terminal colors'\n    desc = (\n        'Get the terminal colors for the specified window (defaults to active window).'\n        ' Colors will be output to STDOUT in the same syntax as used for :file:`kitty.conf`.'\n        '\\n\\nTo get a single color use:'\n        '\\n  get-colors | grep \"^background \" | tr -s | cut -d\" \" -f2'\n        '\\n\\nChange background above to whatever color you are interested in.'\n    )\n    options_spec = '''\\\n--configured -c\ntype=bool-set\nInstead of outputting the colors for the specified window, output the currently\nconfigured colors.\n\n''' + '\\n\\n' + MATCH_WINDOW_OPTION\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'configured': opts.configured, 'match': opts.match}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        from kitty.fast_data_types import get_options\n        opts = get_options()\n        ans = {k: getattr(opts, k) for k in opts if isinstance(getattr(opts, k), Color)}\n        if not payload_get('configured'):\n            windows = self.windows_for_match_payload(boss, window, payload_get)\n            if windows and windows[0]:\n                for k, v in windows[0].current_colors.items():\n                    if v is None:\n                        ans.pop(k, None)\n                    elif isinstance(v, int):\n                        ans[k] = color_from_int(v)\n                tab = windows[0].tabref()\n                tm = None if tab is None else tab.tab_manager_ref()\n                if tm is not None:\n                    ans.update(tm.tab_bar.current_colors)\n        all_keys = natsort_ints(ans)\n        maxlen = max(map(len, all_keys))\n        return '\\n'.join(('{:%ds} {}' % maxlen).format(key, color_as_sharp(ans[key])) for key in all_keys)\n# }}}\n\n\nget_colors = GetColors()\n"
  },
  {
    "path": "kitty/rc/get_text.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import GetTextRCOptions as CLIOptions\n\n\nclass GetText(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: The window to get text from\n    extent/choices.screen.first_cmd_output_on_screen.last_cmd_output.last_visited_cmd_output.all.selection: \\\n        One of :code:`screen`, :code:`first_cmd_output_on_screen`, :code:`last_cmd_output`, \\\n        :code:`last_visited_cmd_output`, :code:`all`, or :code:`selection`\n    ansi/bool: Boolean, if True send ANSI formatting codes\n    cursor/bool: Boolean, if True send cursor position/style as ANSI codes\n    wrap_markers/bool: Boolean, if True add wrap markers to output\n    clear_selection/bool: Boolean, if True clear the selection in the matched window\n    self/bool: Boolean, if True use window the command was run in\n    '''\n\n    short_desc = 'Get text from the specified window'\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\n--extent\ndefault=screen\nchoices=screen, all, selection, first_cmd_output_on_screen, last_cmd_output, last_visited_cmd_output, last_non_empty_output\nWhat text to get. The default of :code:`screen` means all text currently on the screen.\n:code:`all` means all the screen+scrollback and :code:`selection` means the\ncurrently selected text. :code:`first_cmd_output_on_screen` means the output of the first\ncommand that was run in the window on screen. :code:`last_cmd_output` means\nthe output of the last command that was run in the window. :code:`last_visited_cmd_output` means\nthe first command output below the last scrolled position via scroll_to_prompt.\n:code:`last_non_empty_output` is the output from the last command run in the window that had\nsome non empty output. The last four require :ref:`shell_integration` to be enabled.\n\n\n--ansi\ntype=bool-set\nBy default, only plain text is returned. With this flag, the text will\ninclude the ANSI formatting escape codes for colors, bold, italic, etc.\n\n\n--add-cursor\ntype=bool-set\nAdd ANSI escape codes specifying the cursor position and style to the end of the text.\n\n\n--add-wrap-markers\ntype=bool-set\nAdd carriage returns at every line wrap location (where long lines are wrapped at\nscreen edges).\n\n\n--clear-selection\ntype=bool-set\nClear the selection in the matched window, if any.\n\n\n--self\ntype=bool-set\nGet text from the window this command is run in, rather than the active window.\n'''\n\n    field_to_option_map = {'wrap_markers': 'add_wrap_markers', 'cursor': 'add_cursor'}\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {\n            'match': opts.match,\n            'extent': opts.extent,\n            'ansi': opts.ansi,\n            'cursor': opts.add_cursor,\n            'wrap_markers': opts.add_wrap_markers,\n            'clear_selection': opts.clear_selection,\n            'self': opts.self,\n        }\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        from kitty.window import CommandOutput\n        windows = self.windows_for_match_payload(boss, window, payload_get)\n        if windows and windows[0]:\n            window = windows[0]\n        else:\n            return None\n        if payload_get('extent') == 'selection':\n            ans = window.text_for_selection(as_ansi=payload_get('ansi'))\n        elif payload_get('extent') == 'first_cmd_output_on_screen':\n            ans = window.cmd_output(\n                CommandOutput.first_on_screen,\n                as_ansi=bool(payload_get('ansi')),\n                add_wrap_markers=bool(payload_get('wrap_markers')),\n            )\n        elif payload_get('extent') == 'last_cmd_output':\n            ans = window.cmd_output(\n                CommandOutput.last_run,\n                as_ansi=bool(payload_get('ansi')),\n                add_wrap_markers=bool(payload_get('wrap_markers')),\n            )\n        elif payload_get('extent') == 'last_non_empty_output':\n            ans = window.cmd_output(\n                CommandOutput.last_non_empty,\n                as_ansi=bool(payload_get('ansi')),\n                add_wrap_markers=bool(payload_get('wrap_markers')),\n            )\n        elif payload_get('extent') == 'last_visited_cmd_output':\n            ans = window.cmd_output(\n                CommandOutput.last_visited,\n                as_ansi=bool(payload_get('ansi')),\n                add_wrap_markers=bool(payload_get('wrap_markers')),\n            )\n        else:\n            ans = window.as_text(\n                as_ansi=bool(payload_get('ansi')),\n                add_history=payload_get('extent') == 'all',\n                add_cursor=bool(payload_get('cursor')),\n                add_wrap_markers=bool(payload_get('wrap_markers')),\n            )\n        if payload_get('clear_selection'):\n            window.clear_selection()\n        return ans\n\n\nget_text = GetText()\n"
  },
  {
    "path": "kitty/rc/goto_layout.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Iterable\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, UnknownLayout, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import GotoLayoutRCOptions as CLIOptions\n\n\ndef layout_names() -> Iterable[str]:\n    from kitty.layout.interface import all_layouts\n    return all_layouts.keys()\n\n\nclass GotoLayout(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    layout+/str: The new layout name\n    match/str: Which tab to change the layout of\n    '''\n\n    short_desc = 'Set the window layout'\n    desc = (\n        'Set the window layout in the specified tabs (or the active tab if not specified).'\n        ' You can use special match value :code:`all` to set the layout in all tabs.'\n        ' In case there are multiple layouts with the same name but different options,'\n        ' specify the full layout definition or a unique prefix of the full definition.'\n    )\n    options_spec = MATCH_TAB_OPTION\n    args = RemoteCommand.Args(\n        spec='LAYOUT_NAME', count=1, json_field='layout',\n        completion=RemoteCommand.CompletionSpec.from_string('type:keyword group:\"Layout\" kwds:' + ','.join(layout_names())),\n        )\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if len(args) != 1:\n            self.fatal('Exactly one layout must be specified')\n        return {'layout': args[0], 'match': opts.match}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        tabs = self.tabs_for_match_payload(boss, window, payload_get)\n        for tab in tabs:\n            if tab:\n                try:\n                    tab.goto_layout(payload_get('layout'), raise_exception=True)\n                except ValueError:\n                    raise UnknownLayout('The layout {} is unknown or disabled or the name is ambiguous'.format(payload_get('layout')))\n        return None\n\n\ngoto_layout = GotoLayout()\n"
  },
  {
    "path": "kitty/rc/kitten.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import KittenRCOptions as CLIOptions\n\n\nclass Kitten(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    kitten+/str: The name of the kitten to run\n    args/list.str: Arguments to pass to the kitten as a list\n    match/str: The window to run the kitten over\n    '''\n\n    short_desc = 'Run a kitten'\n    desc = (\n        'Run a kitten over the specified windows (active window by default).'\n        ' The :italic:`kitten_name` can be either the name of a builtin kitten'\n        ' or the path to a Python file containing a custom kitten. If a relative path'\n        ' is used it is searched for in the :ref:`kitty config directory <confloc>`. If the kitten is a'\n        ' :italic:`no_ui` kitten and its handle response method returns a string or boolean, this'\n        ' is printed out to stdout.'\n    )\n    options_spec = MATCH_WINDOW_OPTION\n    args = RemoteCommand.Args(spec='kitten_name', json_field='kitten', minimum_count=1, first_rest=('kitten', 'args'))\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if len(args) < 1:\n            self.fatal('Must specify kitten name')\n        return {'match': opts.match, 'args': list(args)[1:], 'kitten': args[0]}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        retval = None\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                retval = boss.run_kitten_with_metadata(payload_get('kitten'), args=tuple(payload_get('args') or ()), window=window)\n                break\n        if isinstance(retval, (str, bool)):\n            return retval\n        return None\n\n\nkitten = Kitten()\n"
  },
  {
    "path": "kitty/rc/last_used_layout.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import LastUsedLayoutRCOptions as CLIOptions\n\n\nclass LastUsedLayout(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    match/str: Which tab to change the layout of\n    all/bool: Boolean to match all tabs\n    '''\n\n    short_desc = 'Switch to the last used layout'\n    desc = (\n        'Switch to the last used window layout in the specified tabs (or the active tab if not specified).'\n    )\n    options_spec = '''\\\n--all -a\ntype=bool-set\nChange the layout in all tabs.\n\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response from kitty. This means that even if no matching tab is found,\nthe command will exit with a success code.\n''' + '\\n\\n\\n' + MATCH_TAB_OPTION\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'all': opts.all, 'no_response': opts.no_response}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        for tab in self.tabs_for_match_payload(boss, window, payload_get):\n            if tab:\n                tab.last_used_layout()\n        return None\n\n\nlast_used_layout = LastUsedLayout()\n"
  },
  {
    "path": "kitty/rc/launch.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nfrom typing import TYPE_CHECKING\n\nfrom kitty.cli_stub import LaunchCLIOptions\nfrom kitty.launch import launch as do_launch\nfrom kitty.launch import options_spec as launch_options_spec\nfrom kitty.launch import parse_launch_args\nfrom kitty.types import AsyncResponse\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import LaunchRCOptions as CLIOptions\n\n\nclass Launch(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    args+/list.str: The command line to run in the new window, as a list, use an empty list to run the default shell\n    match/str: The tab to open the new window in\n    next_to/str: The window next to which to create the new window or empty string to use active window\n    source_window/str: The window to use as source for data or empty string to use active window\n    window_title/str: Title for the new window\n    cwd/str: Working directory for the new window\n    add_to_session/str: Session name to add created window/tab to\n    env/list.str: List of environment variables of the form NAME=VALUE\n    var/list.str: List of user variables of the form NAME=VALUE\n    os_panel/list.str: List of panel settings\n    tab_title/str: Title for the new tab\n    type/choices.window.tab.os-window.os-panel.overlay.overlay-main.background.clipboard.primary: The type of window to open\n    keep_focus/bool: Boolean indicating whether the current window should retain focus or not\n    copy_colors/bool: Boolean indicating whether to copy the colors from the current window\n    copy_cmdline/bool: Boolean indicating whether to copy the cmdline from the current window\n    copy_env/list.str=copy_local_env: List of strings representing the local env vars\n    hold/bool: Boolean indicating whether to keep window open after cmd exits\n    location/choices.first.after.before.neighbor.last.vsplit.hsplit.split.default: Where in the tab to open the new window\n    allow_remote_control/bool: Boolean indicating whether to allow remote control from the new window\n    remote_control_password/list.str: A list of remote control passwords\n    stdin_source/choices.none.@selection.@screen.@screen_scrollback.@alternate.@alternate_scrollback.\\\n        @first_cmd_output_on_screen.@last_cmd_output.@last_visited_cmd_output: Where to get stdin for the process from\n    stdin_add_formatting/bool: Boolean indicating whether to add formatting codes to stdin\n    stdin_add_line_wrap_markers/bool: Boolean indicating whether to add line wrap markers to stdin\n    spacing/list.str: A list of spacing specifications, see the docs for the set-spacing command\n    marker/str: Specification for marker for new window, for example: \"text 1 ERROR\"\n    logo/str: Path to window logo\n    logo_position/str: Window logo position as string or empty string to use default\n    logo_alpha/float: Window logo alpha or -1 to use default\n    self/bool: Boolean, if True use tab the command was run in\n    os_window_title/str: Title for OS Window\n    os_window_name/str: WM_NAME for OS Window\n    os_window_class/str: WM_CLASS for OS Window\n    os_window_state/choices.normal.fullscreen.maximized.minimized: The initial state for OS Window\n    color/list.str: list of color specifications such as foreground=red\n    watcher/list.str: list of paths to watcher files\n    bias/float: The bias with which to create the new window in the current layout\n    wait_for_child_to_exit/bool: Boolean indicating whether to wait and return child exit code\n    hold_after_ssh/bool: Boolean indicating whether to run a local shell after exiting the ssh session cloned via cwd=current or similar\n    '''\n\n    short_desc = 'Run an arbitrary process in a new window/tab'\n    desc = (\n        'Prints out the id of the newly opened window. Any command line arguments'\n        ' are assumed to be the command line used to run in the new window, if none'\n        ' are provided, the default shell is run. For example::\\n\\n'\n        '    kitten @ launch --title=Email mutt'\n    )\n    options_spec = MATCH_TAB_OPTION + '\\n\\n' + '''\\\n--wait-for-child-to-exit\ntype=bool-set\nWait until the launched program exits and print out its exit code. The exit code is\nprinted out instead of the window id. If the program exited normally its exit code is printed, which\nis always greater than or equal to zero. If the program was killed by a signal, the symbolic name\nof the SIGNAL is printed, if available, otherwise the signal number with a leading minus sign is printed.\n\n\n--response-timeout\ntype=float\ndefault=86400\nThe time in seconds to wait for the started process to exit, when using the :option:`--wait-for-child-to-exit`\noption. Defaults to one day.\n\n\n--no-response\ntype=bool-set\nDo not print out the id of the newly created window.\n\n\n--self\ntype=bool-set\nIf specified the tab containing the window this command is run in is used\ninstead of the active tab\n    ''' + '\\n\\n' + launch_options_spec().replace(':option:`launch', ':option:`kitten @ launch')\n    args = RemoteCommand.Args(spec='[CMD ...]', json_field='args', completion=RemoteCommand.CompletionSpec.from_string(\n        'type:special group:cli.CompleteExecutableFirstArg'))\n    is_asynchronous = True\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        ans = {'args': args or []}\n        for attr, val in opts.__dict__.items():\n            ans[attr] = val\n        # ans['wait_for_child_to_exit'] = opts.wait_for_child_to_exit\n        return ans\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        # responder.send_data(getattr(w, 'id', 0))\n        default_opts = parse_launch_args()[0]\n        opts = LaunchCLIOptions()\n        for key, default_value in default_opts.__dict__.items():\n            if key == 'copy_env':\n                continue\n            val = payload_get(key)\n            if val is None:\n                val = default_value\n            setattr(opts, key, val)\n        ceval = payload_get('copy_env')\n        opts.copy_env = False\n        base_env: dict[str, str] | None = None\n        if ceval:\n            if isinstance(ceval, list):\n                base_env = {}\n                for x in ceval:\n                    k, v = x.partition('=')[::2]\n                    base_env[k] = v\n            elif isinstance(ceval, bool):\n                opts.copy_env = ceval\n        target_tab = None\n        tabs = self.tabs_for_match_payload(boss, window, payload_get)\n        if tabs and tabs[0]:\n            target_tab = tabs[0]\n\n        # Create responder before defining callback to avoid closure issue\n        responder = None\n        if payload_get('wait_for_child_to_exit') and not payload_get('no_response'):\n            responder = self.create_async_responder(payload_get, window)\n\n        def on_child_death(exit_status: int, exc: Exception | None) -> None:\n            code = os.waitstatus_to_exitcode(exit_status)\n            ans = str(code)\n            if code < 0:\n                try:\n                    from signal import Signals\n                    ans = Signals(-code).name\n                except ValueError:\n                    pass\n            if responder is not None:\n                responder.send_data(ans)\n\n        w = do_launch(\n            boss, opts, payload_get('args') or [], target_tab=target_tab, rc_from_window=window, base_env=base_env,\n            child_death_callback=on_child_death if payload_get('wait_for_child_to_exit') and not payload_get('no_response') else None)\n        if payload_get('no_response'):\n            return None\n\n        if not payload_get('wait_for_child_to_exit'):\n            return str(0 if w is None else w.id)\n\n        return AsyncResponse()\n\n    def cancel_async_request(self, boss: 'Boss', window: Window | None, payload_get: PayloadGetType) -> None:\n        pass\n\nlaunch = Launch()\n"
  },
  {
    "path": "kitty/rc/load_config.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom kitty.constants import appname\n\nfrom .base import (\n    ArgsType,\n    Boss,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import LoadConfigRCOptions as CLIOptions\n\n\nclass LoadConfig(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    paths/list.str: List of config file paths to load\n    override/list.str: List of individual config overrides\n    ignore_overrides/bool: Whether to apply previous overrides\n    '''\n\n    short_desc = '(Re)load a config file'\n    desc = (\n        '(Re)load the specified kitty.conf config files(s). If no files are specified the previously specified config file is reloaded.'\n        ' Note that the specified paths must exist and be readable by the kitty process on the computer that process is running on.'\n        ' Relative paths are resolved with respect to the kitty config directory on the computer running kitty.'\n    )\n    options_spec = f'''\\\n--ignore-overrides\ntype=bool-set\nBy default, any config overrides previously specified at the kitty invocation command line\nor a previous load-config-file command are respected. Use this option to have them ignored instead.\n\n\n--override -o\ntype=list\ncompletion=type:special group:complete_kitty_override\nOverride individual configuration options, can be specified multiple times.\nSyntax: :italic:`name=value`. For example: :option:`{appname} -o` font_size=20\n\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response indicating the success of the action. Note that\nusing this option means that you will not be notified of failures.\n'''\n\n    args = RemoteCommand.Args(spec='CONF_FILE ...', json_field='paths',\n                              completion=RemoteCommand.CompletionSpec.from_string('type:file group:\"CONF files\", ext:conf'))\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'paths': args, 'override': opts.override, 'ignore_overrides': opts.ignore_overrides}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        from kitty.cli import parse_override\n        from kitty.utils import resolve_abs_or_config_path\n        paths = tuple(map(resolve_abs_or_config_path, payload_get('paths', missing=())))\n        boss.load_config_file(\n            *paths, apply_overrides=not payload_get('ignore_overrides', missing=False),\n            overrides=tuple(map(parse_override, payload_get('override', missing=())))\n        )\n        return None\n\n\nload_config = LoadConfig()\n"
  },
  {
    "path": "kitty/rc/ls.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nfrom collections.abc import Callable\nfrom typing import TYPE_CHECKING\n\nfrom kitty.constants import appname\n\nfrom .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Tab, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import LSRCOptions as CLIOptions\n\n\nclass LS(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    all_env_vars/bool: Whether to send all environment variables for every window rather than just differing ones\n    match/str: Window to change colors in\n    match_tab/str: Tab to change colors in\n    self/bool: Boolean indicating whether to list only the window the command is run in\n    output_format/str: Output in json or session format\n    '''\n\n    short_desc = 'List tabs/windows'\n    desc = (\n        'List windows. The list is returned as JSON tree. The top-level is a list of'\n        f' operating system {appname} windows. Each OS window has an :italic:`id` and a list'\n        ' of :italic:`tabs`. Each tab has its own :italic:`id`, a :italic:`title` and a list of :italic:`windows`.'\n        ' Each window has an :italic:`id`, :italic:`title`, :italic:`current working directory`, :italic:`process id (PID)`,'\n        ' :italic:`command-line` and :italic:`environment` of the process running in the window. Additionally, when'\n        ' running the command inside a kitty window, that window can be identified by the :italic:`is_self` parameter.\\n\\n'\n        'You can use these criteria to select windows/tabs for the other commands.\\n\\n'\n        'You can limit the windows/tabs in the output by using the :option:`--match` and :option:`--match-tab` options.'\n    )\n    options_spec = '''\\\n--all-env-vars\ntype=bool-set\nShow all environment variables in output, not just differing ones.\n\n\n--self\ntype=bool-set\nOnly list the window this command is run in.\n\n\n--output-format\ntype=choices\nchoices=json,session\ndefault=json\nOutput in JSON or kitty session format\n''' + '\\n\\n' + MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t', 1)\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'all_env_vars': opts.all_env_vars, 'match': opts.match, 'match_tab': opts.match_tab}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        tab_filter: Callable[[Tab], bool] | None = None\n        window_filter: Callable[[Window], bool] | None = None\n\n        if payload_get('self'):\n            def wf(w: Window) -> bool:\n                return w is window\n            window_filter = wf\n        elif payload_get('match') is not None or payload_get('match_tab') is not None:\n            window_ids = frozenset(w.id for w in self.windows_for_payload(boss, window, payload_get, window_match_name='match'))\n            def wf(w: Window) -> bool:\n                return w.id in window_ids\n            window_filter = wf\n        elif payload_get('output_format') == 'session':\n            return \"\\n\".join(boss.serialize_state_as_session())\n\n        data = list(boss.list_os_windows(window, tab_filter, window_filter))\n        if not payload_get('all_env_vars'):\n            all_env_blocks: list[dict[str, str]] = []\n            common_env_vars: set[tuple[str, str]] = set()\n            for osw in data:\n                for tab in osw.get('tabs', ()):\n                    for w in tab.get('windows', ()):\n                        env: dict[str, str] = w.get('env', {})\n                        frozen_env = set(env.items())\n                        if all_env_blocks:\n                            common_env_vars &= frozen_env\n                        else:\n                            common_env_vars = frozen_env\n                        all_env_blocks.append(env)\n            if common_env_vars and len(all_env_blocks) > 1:\n                remove_env_vars = {k for k, v in common_env_vars}\n                for env in all_env_blocks:\n                    for r in remove_env_vars:\n                        env.pop(r, None)\n        return json.dumps(data, indent=2, sort_keys=True)\n\n\nls = LS()\n"
  },
  {
    "path": "kitty/rc/new_window.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import NewWindowRCOptions as CLIOptions\n\n\nclass NewWindow(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    args+/list.str: The command line to run in the new window, as a list, use an empty list to run the default shell\n    match/str: The tab to open the new window in\n    title/str: Title for the new window\n    cwd/str: Working directory for the new window\n    keep_focus/bool: Boolean indicating whether the current window should retain focus or not\n    window_type/choices.kitty.os: One of :code:`kitty` or :code:`os`\n    new_tab/bool: Boolean indicating whether to open a new tab\n    tab_title/str: Title for the new tab\n    '''\n\n    short_desc = 'Open new window'\n    desc = (\n        'DEPRECATED: Use the :ref:`launch <at-launch>` command instead.\\n\\n'\n        'Open a new window in the specified tab. If you use the :option:`kitten @ new-window --match` option'\n        ' the first matching tab is used. Otherwise the currently active tab is used.'\n        ' Prints out the id of the newly opened window'\n        ' (unless :option:`--no-response` is used). Any command line arguments'\n        ' are assumed to be the command line used to run in the new window, if none'\n        ' are provided, the default shell is run. For example::\\n\\n'\n        '    kitten @ new-window --title Email mutt'\n    )\n    options_spec = MATCH_TAB_OPTION + '''\\n\n--title\nThe title for the new window. By default it will use the title set by the\nprogram running in it.\n\n\n--cwd\nThe initial working directory for the new window. Defaults to whatever\nthe working directory for the kitty process you are talking to is.\n\n\n--keep-focus --dont-take-focus\ntype=bool-set\nKeep the current window focused instead of switching to the newly opened window.\n\n\n--window-type\ndefault=kitty\nchoices=kitty,os\nWhat kind of window to open. A kitty window or a top-level OS window.\n\n\n--new-tab\ntype=bool-set\nOpen a new tab.\n\n\n--tab-title\nSet the title of the tab, when open a new tab.\n\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response giving the id of the newly opened window. Note that\nusing this option means that you will not be notified of failures and that\nthe id of the new window will not be printed out.\n'''\n    args = RemoteCommand.Args(spec='[CMD ...]', json_field='args')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        ans = {'args': args or [], 'type': 'window'}\n        for attr, val in opts.__dict__.items():\n            if attr == 'new_tab':\n                if val:\n                    ans['type'] = 'tab'\n            elif attr == 'window_type':\n                if val == 'os' and ans['type'] != 'tab':\n                    ans['type'] = 'os-window'\n            else:\n                ans[attr] = val\n        return ans\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        from .launch import launch\n        return launch.response_from_kitty(boss, window, payload_get)\n\n\nnew_window = NewWindow()\n"
  },
  {
    "path": "kitty/rc/remove_marker.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import RemoveMarkerRCOptions as CLIOptions\n\n\nclass RemoveMarker(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: Which window to remove the marker from\n    self/bool: Boolean indicating whether to detach the window the command is run in\n    '''\n\n    short_desc = 'Remove the currently set marker, if any.'\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\n--self\ntype=bool-set\nApply marker to the window this command is run in, rather than the active window.\n'''\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'self': opts.self}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                window.remove_marker()\n        return None\n\n\nremove_marker = RemoveMarker()\n"
  },
  {
    "path": "kitty/rc/resize_os_window.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import (\n    MATCH_WINDOW_OPTION,\n    ArgsType,\n    Boss,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    RemoteControlErrorWithoutTraceback,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import ResizeOSWindowRCOptions as CLIOptions\n\n\nclass ResizeOSWindow(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    match/str: Which window to resize\n    self/bool: Boolean indicating whether to close the window the command is run in\n    incremental/bool: Boolean indicating whether to adjust the size incrementally\n    action/choices.resize.toggle-fullscreen.toggle-maximized.toggle-visibility.hide.show.os-panel: The action to perform\n    unit/choices.cells.pixels: One of :code:`cells` or :code:`pixels`\n    width/int: Integer indicating desired window width\n    height/int: Integer indicating desired window height\n    os_panel/list.str: Settings for modifying the OS Panel\n    '''\n\n    short_desc = 'Resize/show/hide/etc. the specified OS Windows'\n    desc = (\n        'Resize (or other operations) on the specified OS Windows.'\n        ' Note that some window managers/environments do not allow applications to resize'\n        ' their windows, for example, tiling window managers.\\n\\nTo modify OS Panels created with the'\n        ' panel kitten, use :option:`--action`=:code:`os-panel`. Specify the modifications in the same syntax as used'\n        ' by the panel kitten, without the leading dashes. Use the :option:`--incremental` option to only change'\n        ' the specified panel settings. For example, move the panel to bottom edge and make it two lines tall:'\n        ' :code:`--action=os-panel --incremental lines=2 edge=bottom`'\n    )\n    args = RemoteCommand.Args(spec='[OS Panel settings ...]', json_field='os_panel', special_parse='escape_list_of_strings(args), nil')\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\n--action\ndefault=resize\nchoices=resize,toggle-fullscreen,toggle-maximized,toggle-visibility,hide,show,os-panel\nThe action to perform.\n\n\n--unit\ndefault=cells\nchoices=cells,pixels\nThe unit in which to interpret specified sizes.\n\n\n--width\ndefault=0\ntype=int\nChange the width of the window. Zero leaves the width unchanged.\n\n\n--height\ndefault=0\ntype=int\nChange the height of the window. Zero leaves the height unchanged.\n\n\n--incremental\ntype=bool-set\nTreat the specified sizes as increments on the existing window size\ninstead of absolute sizes. When using :option:`--action`=:code:`os-panel`,\nonly the specified settings are changed, otherwise non-specified settings\nkeep their current value.\n\n\n--self\ntype=bool-set\nResize the window this command is run in, rather than the active window.\n\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response indicating the success of the action. Note that\nusing this option means that you will not be notified of failures.\n'''\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {\n            'match': opts.match, 'action': opts.action, 'unit': opts.unit,\n            'width': opts.width, 'height': opts.height, 'self': opts.self,\n            'incremental': opts.incremental, 'os_panel': args,\n        }\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        from kitty.fast_data_types import (\n            get_os_window_size,\n            layer_shell_config_for_os_window,\n            set_layer_shell_config,\n            toggle_fullscreen,\n            toggle_os_window_visibility,\n        )\n        windows = self.windows_for_match_payload(boss, window, payload_get)\n        if windows:\n            ac = payload_get('action')\n            for os_window_id in {w.os_window_id for w in windows if w}:\n                metrics = get_os_window_size(os_window_id)\n                if metrics is None:\n                    raise RemoteControlErrorWithoutTraceback(f'The OS Window {os_window_id} does not exist')\n                panels = payload_get('os_panel')\n                is_panel = metrics['is_layer_shell']\n                if ac == 'os-panel':\n                    if not is_panel:\n                        raise RemoteControlErrorWithoutTraceback(\n                            f'The OS Window {os_window_id} is not a panel you should not use the --action=resize option to resize it')\n                    if not panels:\n                        raise RemoteControlErrorWithoutTraceback('Must specify at least one panel setting')\n                    if payload_get('incremental'):\n                        existing = layer_shell_config_for_os_window(os_window_id)\n                        if existing is None:\n                            raise RemoteControlErrorWithoutTraceback(\n                                f'The OS Window {os_window_id} has no panel configuration')\n                        from kittens.panel.main import incrementally_update_layer_shell_config\n                        try:\n                            lsc = incrementally_update_layer_shell_config(existing, panels)\n                        except Exception as e:\n                            raise RemoteControlErrorWithoutTraceback(str(e))\n                    else:\n                        from kitty.launch import layer_shell_config_from_panel_opts\n                        try:\n                            lsc = layer_shell_config_from_panel_opts(panels)\n                        except Exception as e:\n                            raise RemoteControlErrorWithoutTraceback(\n                                f'Invalid panel options specified: {e}')\n                    if not set_layer_shell_config(os_window_id, lsc):\n                        raise RemoteControlErrorWithoutTraceback(f'Failed to change panel configuration for OS Window {os_window_id}')\n                elif ac == 'toggle-visibility':\n                    toggle_os_window_visibility(os_window_id)\n                elif ac == 'hide':\n                    toggle_os_window_visibility(os_window_id, False)\n                elif ac == 'show':\n                    toggle_os_window_visibility(os_window_id, True)\n                elif ac == 'toggle-fullscreen':\n                    if not toggle_fullscreen(os_window_id):\n                        raise RemoteControlErrorWithoutTraceback(\n                            f'The OS Window {os_window_id} is a desktop panel that cannot be made fullscreen')\n                elif is_panel:\n                    raise RemoteControlErrorWithoutTraceback(\n                        f'The OS Window {os_window_id} is a desktop panel, no actions other than resizing are supported for it')\n                elif ac == 'resize':\n                    boss.resize_os_window(\n                        os_window_id, width=payload_get('width'), height=payload_get('height'),\n                        unit=payload_get('unit'), incremental=payload_get('incremental'), metrics=metrics,\n                    )\n                elif ac == 'toggle-maximized':\n                    boss.toggle_maximized(os_window_id)\n        return None\n\n\nresize_os_window = ResizeOSWindow()\n"
  },
  {
    "path": "kitty/rc/resize_window.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import ResizeWindowRCOptions as CLIOptions\n\n\nclass ResizeWindow(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    match/str: Which window to resize\n    self/bool: Boolean indicating whether to resize the window the command is run in\n    increment/int: Integer specifying the resize increment\n    axis/choices.horizontal.vertical.reset: One of :code:`horizontal, vertical` or :code:`reset`\n    '''\n\n    short_desc = 'Resize the specified windows'\n    desc = (\n        'Resize the specified windows in the current layout.'\n        ' Note that not all layouts can resize all windows in all directions.'\n    )\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\n--increment -i\ntype=int\ndefault=2\nThe number of cells to change the size by, can be negative to decrease the size.\n\n\n--axis -a\ntype=choices\nchoices=horizontal,vertical,reset\ndefault=horizontal\nThe axis along which to resize. If :code:`horizontal`,\nit will make the window wider or narrower by the specified increment.\nIf :code:`vertical`, it will make the window taller or shorter by the specified increment.\nThe special value :code:`reset` will reset the layout to its default configuration.\n\n\n--self\ntype=bool-set\nResize the window this command is run in, rather than the active window.\n'''\n    string_return_is_error = True\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'increment': opts.increment, 'axis': opts.axis, 'self': opts.self}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        windows = self.windows_for_match_payload(boss, window, payload_get)\n        resized: bool | None | str = False\n        if windows and windows[0]:\n            resized = boss.resize_layout_window(\n                windows[0], increment=payload_get('increment'), is_horizontal=payload_get('axis') == 'horizontal',\n                reset=payload_get('axis') == 'reset'\n            )\n        return resized\n\n\nresize_window = ResizeWindow()\n"
  },
  {
    "path": "kitty/rc/run.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nfrom base64 import standard_b64decode, standard_b64encode\nfrom typing import TYPE_CHECKING\n\nfrom kitty.launch import env_docs, remote_control_password_docs\nfrom kitty.options.utils import env as parse_env\nfrom kitty.types import AsyncResponse\n\nfrom .base import (\n    ArgsType,\n    Boss,\n    CmdGenerator,\n    ParsingOfArgsFailed,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import RunRCOptions as CLIOptions\n\n\nclass Run(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    data+/str: Chunk of STDIN data, base64 encoded no more than 4096 bytes. Must send an empty chunk to indicate end of data.\n    cmdline+/list.str: The command line to run\n    env/list.str: List of environment variables of the form NAME=VALUE\n    allow_remote_control/bool: A boolean indicating whether to allow remote control\n    remote_control_password/list.str: A list of remote control passwords\n    '''\n\n    short_desc = 'Run a program on the computer in which kitty is running and get the output'\n    desc = (\n        'Run the specified program on the computer in which kitty is running. When STDIN is not a TTY it is forwarded'\n        ' to the program as its STDIN. STDOUT and STDERR from the the program are forwarded here. The exit status of this'\n        ' invocation will be the exit status of the executed program. If you wish to just run a program without waiting for a response, '\n        ' use @ launch --type=background instead.'\n    )\n\n    options_spec = f'''\\n\n--env\n{env_docs}\n\n\n--allow-remote-control\ntype=bool-set\nThe executed program will have privileges to run remote control commands in kitty.\n\n\n--remote-control-password\n{remote_control_password_docs}\n'''\n    args = RemoteCommand.Args(\n        spec='CMD ...', json_field='data', special_parse='+cmdline:!read_run_data(io_data, args, &payload)', minimum_count=1,\n        completion=RemoteCommand.CompletionSpec.from_string('type:special group:cli.CompleteExecutableFirstArg')\n    )\n    reads_streaming_data = True\n    is_asynchronous = True\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if not args:\n            self.fatal('Must specify command to run')\n        import secrets\n        ret = {\n            'stream_id': secrets.token_urlsafe(),\n            'cmdline': args,\n            'env': opts.env,\n            'allow_remote_control': opts.allow_remote_control,\n            'remote_control_password': opts.remote_control_password,\n            'data': '',\n        }\n        def pipe() -> CmdGenerator:\n            if sys.stdin.isatty():\n                yield ret\n            else:\n                limit = 4096\n                while True:\n                    data = sys.stdin.buffer.read(limit)\n                    if not data:\n                        break\n                    ret['data'] = standard_b64encode(data).decode(\"ascii\")\n                    yield ret\n        return pipe()\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        import os\n        import tempfile\n        data = payload_get('data')\n        q = self.handle_streamed_data(standard_b64decode(data) if data else b'', payload_get)\n        if isinstance(q, AsyncResponse):\n            return q\n        stdin_data = q.getvalue()\n        from kitty.launch import parse_remote_control_passwords\n        cmdline = payload_get('cmdline')\n        allow_remote_control = payload_get('allow_remote_control')\n        pw = payload_get('remote_control_password')\n        rcp = parse_remote_control_passwords(allow_remote_control, pw)\n        if not cmdline:\n            raise ParsingOfArgsFailed('No cmdline to run specified')\n        responder = self.create_async_responder(payload_get, window)\n        stdout, stderr = tempfile.TemporaryFile(), tempfile.TemporaryFile()\n\n        def on_death(exit_status: int, err: Exception | None) -> None:\n            with stdout, stderr:\n                if err:\n                    responder.send_error(f'Failed to run: {cmdline} with err: {err}')\n                else:\n                    exit_code = os.waitstatus_to_exitcode(exit_status)\n                    stdout.seek(0)\n                    stderr.seek(0)\n                    responder.send_data({\n                        'stdout': standard_b64encode(stdout.read()).decode('ascii'),\n                        'stderr': standard_b64encode(stderr.read()).decode('ascii'),\n                        'exit_code': exit_code, 'exit_status': exit_status,\n                    })\n\n        env: dict[str, str] = {}\n        for x in payload_get('env') or ():\n            for k, v in parse_env(x, env):\n                env[k] = v\n\n        boss.run_background_process(\n            cmdline, env=env, stdin=stdin_data, stdout=stdout.fileno(), stderr=stderr.fileno(),\n            notify_on_death=on_death, remote_control_passwords=rcp, allow_remote_control=allow_remote_control\n        )\n        return AsyncResponse()\n\n\nrun = Run()\n"
  },
  {
    "path": "kitty/rc/scroll_window.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import ScrollWindowRCOptions as CLIOptions\n\n\nclass ScrollWindow(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    amount+/list.scroll_amount: The amount to scroll, a two item list with the first item being \\\n             either a number or the keywords, start and end. \\\n             And the second item being either 'p' for pages or 'l' for lines or 'u'\n             for unscrolling by lines, or 'r' for scrolling to prompt.\n    match/str: The window to scroll\n    '''\n\n    short_desc = 'Scroll the specified windows'\n    desc = (\n        'Scroll the specified windows, if no window is specified, scroll the window this command is run inside.'\n        ' :italic:`SCROLL_AMOUNT` can be either the keywords :code:`start` or :code:`end` or an'\n        ' argument of the form :italic:`<number>[unit][+-]`. :code:`unit` can be :code:`l` for lines, :code:`p` for pages,'\n        ' :code:`u` for unscroll and :code:`r` for scroll to prompt. If unspecified, :code:`l` is the default.'\n        ' For example, :code:`30.5` will scroll down 30 and a half lines, :code:`2p-`'\n        ' will scroll up 2 pages and :code:`0.5p` will scroll down half page.'\n        ' :code:`3u` will *unscroll* by 3 lines, which means that 3 lines will move from the'\n        ' scrollback buffer onto the top of the screen. :code:`1r-` will scroll to the previous prompt and :code:`1r` to the next prompt.'\n        ' See :ac:`scroll_to_prompt` for details on how scrolling to prompt works.'\n    )\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response indicating the success of the action. Note that\nusing this option means that you will not be notified of failures.\n'''\n    args = RemoteCommand.Args(spec='SCROLL_AMOUNT', count=1, special_parse='parse_scroll_amount(args[0])', json_field='amount')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if len(args) < 1:\n            self.fatal('Scroll amount must be specified')\n        amt = args[0]\n        amount: tuple[str | float, str | None] = (amt, None)\n        if amt not in ('start', 'end'):\n            pages = 'p' in amt\n            unscroll = 'u' in amt\n            prompt = 'r' in amt\n            mult = -1 if amt.endswith('-') and not unscroll else 1\n            q = float(amt.rstrip('+-plur'))\n            if (unscroll or prompt) and not q.is_integer():\n                self.fatal('The number must be an integer')\n            amount = q * mult, 'p' if pages else ('u' if unscroll else ('r' if prompt else 'l'))\n\n        # defaults to scroll the window this command is run in\n        return {'match': opts.match, 'amount': amount, 'self': True}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        amt = payload_get('amount')\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                if amt[0] in ('start', 'end'):\n                    (window.scroll_home if amt[0] == 'start' else window.scroll_end)()\n                else:\n                    amt, unit = amt\n                    match unit:\n                        case 'u':\n                            window.screen.reverse_scroll(int(abs(amt)), True)\n                        case 'r':\n                            window.scroll_to_prompt(int(amt))\n                        case 'l':\n                            window.scroll_fractional_lines(amt)\n                        case 'p':\n                            if not isinstance(amt, int) and not amt.is_integer():\n                                amt = round(window.screen.lines * amt)\n                                unit = 'line'\n                            direction = 'up' if amt < 0 else 'down'\n                            func = getattr(window, f'scroll_{unit}_{direction}')\n                            for i in range(int(abs(amt))):\n                                func()\n        return None\n\n\nscroll_window = ScrollWindow()\n"
  },
  {
    "path": "kitty/rc/select_window.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING, Optional\n\nfrom kitty.types import AsyncResponse\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SelectWindowRCOptions as CLIOptions\n    from kitty.tabs import Tab\n\n\nclass SelectWindow(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    match/str: The tab to open the new window in\n    self/bool: Boolean, if True use tab the command was run in\n    title/str: A title for this selection\n    exclude_active/bool: Exclude the currently active window from the list to pick\n    reactivate_prev_tab/bool: Reactivate the previously activated tab when finished\n    '''\n\n    short_desc = 'Visually select a window in the specified tab'\n    desc = (\n        'Prints out the id of the selected window. Other commands'\n        ' can then be chained to make use of it.'\n    )\n    options_spec = MATCH_TAB_OPTION + '\\n\\n' + '''\\\n--response-timeout\ntype=float\ndefault=60\nThe time in seconds to wait for the user to select a window.\n\n\n--self\ntype=bool-set\nSelect window from the tab containing the window this command is run in,\ninstead of the active tab.\n\n\n--title\nA title that will be displayed to the user to describe what this selection is for.\n\n\n--exclude-active\ntype=bool-set\nExclude the currently active window from the list of windows to pick.\n\n\n--reactivate-prev-tab\ntype=bool-set\nWhen the selection is finished, the tab in the same OS window that was activated\nbefore the selection will be reactivated. The last activated OS window will also\nbe refocused.\n'''\n    is_asynchronous = True\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        ans = {'self': opts.self, 'match': opts.match, 'title': opts.title, 'exclude_active': opts.exclude_active,\n               'reactivate_prev_tab': opts.reactivate_prev_tab}\n        return ans\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        responder = self.create_async_responder(payload_get, window)\n\n        def callback(tab: Optional['Tab'], window: Window | None) -> None:\n            if window:\n                responder.send_data(window.id)\n            else:\n                responder.send_error('No window selected')\n        for tab in self.tabs_for_match_payload(boss, window, payload_get):\n            if tab:\n                if payload_get('exclude_active'):\n                    wids = tab.all_window_ids_except_active_window\n                else:\n                    wids = set()\n                boss.visual_window_select_action(\n                    tab, callback, payload_get('title') or 'Choose window', only_window_ids=wids,\n                    reactivate_prev_tab=payload_get('reactivate_prev_tab')\n                )\n                break\n        return AsyncResponse()\n\n    def cancel_async_request(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> None:\n        boss.cancel_current_visual_select()\n\n\nselect_window = SelectWindow()\n"
  },
  {
    "path": "kitty/rc/send_key.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import (\n    MATCH_TAB_OPTION,\n    MATCH_WINDOW_OPTION,\n    ArgsType,\n    Boss,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SendKeyRCOptions as CLIOptions\n\n\nclass SendKey(RemoteCommand):\n    disallow_responses = True\n    protocol_spec = __doc__ = '''\n    keys+/list.str: The keys to send\n    match/str: A string indicating the window to send text to\n    match_tab/str: A string indicating the tab to send text to\n    all/bool: A boolean indicating all windows should be matched.\n    exclude_active/bool: A boolean that prevents sending text to the active window\n    '''\n    short_desc = 'Send arbitrary key presses to the specified windows'\n    desc = (\n        'Send arbitrary key presses to specified windows. All specified keys are sent first as press events'\n        ' then as release events in reverse order. Keys are sent to the programs running in the windows.'\n        ' They are sent only if the current keyboard mode for the program supports the particular key.'\n        ' For example: send-key ctrl+a ctrl+b. Note that errors are not reported, for technical reasons,'\n        ' so send-key always succeeds, even if no key was sent to any window.'\n   )\n    # since send-key can send data over the tty to the window in which it was\n    # run --no-reponse is always in effect for it, hence errors are not\n    # reported.\n    options_spec = MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t') + '''\\n\n--all\ntype=bool-set\nMatch all windows.\n\n\n--exclude-active\ntype=bool-set\nDo not send text to the active window, even if it is one of the matched windows.\n'''\n    args = RemoteCommand.Args(spec='[KEYS TO SEND ...]', json_field='keys')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        ret = {'match': opts.match, 'keys': args, 'match_tab': opts.match_tab, 'all': opts.all, 'exclude_active': opts.exclude_active}\n        return ret\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        windows = self.windows_for_payload(boss, None, payload_get, window_match_name='match')\n        keys = payload_get('keys')\n        for w in windows:\n            w.send_key(*keys)\n        return None\n\n\nsend_key = SendKey()\n"
  },
  {
    "path": "kitty/rc/send_text.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport base64\nimport sys\nfrom typing import TYPE_CHECKING, Any\n\nfrom kitty.fast_data_types import KeyEvent as WindowSystemKeyEvent\nfrom kitty.fast_data_types import get_boss\nfrom kitty.key_encoding import decode_key_event_as_window_system_key\nfrom kitty.options.utils import parse_send_text_bytes\nfrom kitty.utils import sanitize_for_bracketed_paste\n\nfrom .base import (\n    MATCH_TAB_OPTION,\n    MATCH_WINDOW_OPTION,\n    ArgsType,\n    Boss,\n    CmdGenerator,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SendTextRCOptions as CLIOptions\n\n\nclass Session:\n    id: str\n    window_ids: set[int]\n\n    def __init__(self, id: str):\n        self.id = id\n        self.window_ids = set()\n\n\nsessions_map: dict[str, Session] = {}\n\n\nclass SessionAction:\n\n    def __init__(self, sid: str):\n        self.sid = sid\n\n\nclass ClearSession(SessionAction):\n\n    def __call__(self, *a: Any) -> None:\n        s = sessions_map.pop(self.sid, None)\n        if s is not None:\n            boss = get_boss()\n            for wid in s.window_ids:\n                qw = boss.window_id_map.get(wid)\n                if qw is not None:\n                    qw.screen.render_unfocused_cursor = False\n\n\nclass FocusChangedSession(SessionAction):\n\n    def __call__(self, window: Window, focused: bool) -> None:\n        s = sessions_map.get(self.sid)\n        if s is not None:\n            boss = get_boss()\n            for wid in s.window_ids:\n                qw = boss.window_id_map.get(wid)\n                if qw is not None:\n                    qw.screen.render_unfocused_cursor = focused\n\n\nclass SendText(RemoteCommand):\n    disallow_responses = True\n    protocol_spec = __doc__ = '''\n    data+/str: The data being sent. Can be either: text: followed by text or base64: followed by standard base64 encoded bytes\n    match/str: A string indicating the window to send text to\n    match_tab/str: A string indicating the tab to send text to\n    all/bool: A boolean indicating all windows should be matched.\n    exclude_active/bool: A boolean that prevents sending text to the active window\n    session_id/str: A string that identifies a \"broadcast session\"\n    bracketed_paste/choices.disable.auto.enable: Whether to wrap the text in bracketed paste escape codes\n    '''\n    short_desc = 'Send arbitrary text to specified windows'\n    desc = (\n        'Send arbitrary text to specified windows. The text follows Python'\n        ' escaping rules. So you can use :link:`escapes <https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html>`'\n        \" like :code:`'\\\\\\\\e'` to send control codes\"\n        \" and :code:`'\\\\\\\\u21fa'` to send Unicode characters. Remember to use single-quotes otherwise\"\n        ' the backslash is interpreted as a shell escape character. If you use the :option:`kitten @ send-text --match` option'\n        ' the text will be sent to all matched windows. By default, text is sent to'\n        ' only the currently active window. Note that errors are not reported, for technical reasons,'\n        ' so send-text always succeeds, even if no text was sent to any window.'\n    )\n    # since send-text can send data over the tty to the window in which it was\n    # run --no-reponse is always in effect for it, hence errors are not\n    # reported.\n    options_spec = MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t') + '''\\n\n--all\ntype=bool-set\nMatch all windows.\n\n\n--exclude-active\ntype=bool-set\nDo not send text to the active window, even if it is one of the matched windows.\n\n\n--stdin\ntype=bool-set\nRead the text to be sent from :italic:`stdin`. Note that in this case the text is sent as is,\nnot interpreted for escapes. If stdin is a terminal, you can press :kbd:`Ctrl+D` to end reading.\n\n\n--from-file\nPath to a file whose contents you wish to send. Note that in this case the file contents\nare sent as is, not interpreted for escapes.\n\n\n--bracketed-paste\nchoices=disable,auto,enable\ndefault=disable\nWhen sending text to a window, wrap the text in bracketed paste escape codes. The default is to not do this.\nA value of :code:`auto` means, bracketed paste will be used only if the program running in the window has turned\non bracketed paste mode.\n'''\n    args = RemoteCommand.Args(spec='[TEXT TO SEND]', json_field='data', special_parse='+session_id:parse_send_text(io_data, args)')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        limit = 1024\n        ret = {\n            'match': opts.match, 'data': '', 'match_tab': opts.match_tab, 'all': opts.all, 'exclude_active': opts.exclude_active,\n            'bracketed_paste': opts.bracketed_paste,\n        }\n\n        def pipe() -> CmdGenerator:\n            if sys.stdin.isatty():\n                ret['exclude_active'] = True\n                keep_going = True\n                from kitty.utils import TTYIO\n                with TTYIO(read_with_timeout=False) as tty:\n                    while keep_going:\n                        if not tty.wait_till_read_available():\n                            break\n                        data = tty.read(limit)\n                        if not data:\n                            break\n                        decoded_data = data.decode('utf-8')\n                        if '\\x04' in decoded_data:\n                            decoded_data = decoded_data[:decoded_data.index('\\x04')]\n                            keep_going = False\n                        ret['data'] = f'text:{decoded_data}'\n                        yield ret\n            else:\n                while True:\n                    data = sys.stdin.buffer.read(limit)\n                    if not data:\n                        break\n                    ret['data'] = f'base64:{base64.standard_b64encode(data).decode(\"ascii\")}'\n                    yield ret\n\n        def chunks(text: str) -> CmdGenerator:\n            data = parse_send_text_bytes(text)\n            while data:\n                b = base64.standard_b64encode(data[:limit]).decode(\"ascii\")\n                ret['data'] = f'base64:{b}'\n                yield ret\n                data = data[limit:]\n\n        def file_pipe(path: str) -> CmdGenerator:\n            with open(path, 'rb') as f:\n                while True:\n                    data = f.read(limit)\n                    if not data:\n                        break\n                    ret['data'] = f'base64:{base64.standard_b64encode(data).decode(\"ascii\")}'\n                    yield ret\n\n        sources = []\n        if opts.stdin:\n            sources.append(pipe())\n\n        if opts.from_file:\n            sources.append(file_pipe(opts.from_file))\n\n        text = ' '.join(args)\n        sources.append(chunks(text))\n\n        def chain() -> CmdGenerator:\n            for src in sources:\n                yield from src\n        return chain()\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        sid = payload_get('session_id', '')\n        windows = self.windows_for_payload(boss, window, payload_get, window_match_name='match')\n        pdata: str = payload_get('data')\n        encoding, _, q = pdata.partition(':')\n        session = ''\n        if encoding == 'text':\n            data: bytes | WindowSystemKeyEvent = q.encode('utf-8')\n        elif encoding == 'base64':\n            data = base64.standard_b64decode(q)\n        elif encoding == 'kitty-key':\n            bdata = base64.standard_b64decode(q)\n            candidate = decode_key_event_as_window_system_key(bdata.decode('ascii'))\n            if candidate is None:\n                raise ValueError(f'Could not decode window system key: {q}')\n            data = candidate\n        elif encoding == 'session':\n            session = q\n        else:\n            raise TypeError(f'Invalid encoding for send-text data: {encoding}')\n        exclude_active = payload_get('exclude_active')\n        actual_windows = (w for w in windows if w is not None and (not exclude_active or w is not boss.active_window))\n\n        def create_or_update_session() -> Session:\n            s = sessions_map.setdefault(sid, Session(sid))\n            return s\n        if session == 'end':\n            s = create_or_update_session()\n            for w in actual_windows:\n                w.screen.render_unfocused_cursor = False\n                s.window_ids.discard(w.id)\n            ClearSession(sid)()\n        elif session == 'start':\n            s = create_or_update_session()\n            if window is not None:\n\n                def is_ok(x: Any) -> bool:\n                    return not isinstance(x, SessionAction) or x.sid != sid\n\n                window.actions_on_removal = list(filter(is_ok, window.actions_on_removal))\n                window.actions_on_focus_change = list(filter(is_ok, window.actions_on_focus_change))\n                window.actions_on_removal.append(ClearSession(sid))\n                window.actions_on_focus_change.append(FocusChangedSession(sid))\n            for w in actual_windows:\n                w.screen.render_unfocused_cursor = True\n                s.window_ids.add(w.id)\n        else:\n            bp = payload_get('bracketed_paste')\n            if sid:\n                s = create_or_update_session()\n            for w in actual_windows:\n                if sid:\n                    w.screen.render_unfocused_cursor = True\n                    s.window_ids.add(w.id)\n                if isinstance(data, WindowSystemKeyEvent):\n                    kdata = w.encoded_key(data)\n                    if kdata:\n                        w.write_to_child(kdata)\n                else:\n                    if bp == 'enable' or (bp == 'auto' and w.screen.in_bracketed_paste_mode):\n                        data = b'\\x1b[200~' + sanitize_for_bracketed_paste(data) + b'\\x1b[201~'\n                    w.write_to_child(data)\n        return None\n\n\nsend_text = SendText()\n"
  },
  {
    "path": "kitty/rc/set_background_image.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nfrom base64 import standard_b64decode, standard_b64encode\nfrom io import BytesIO\nfrom typing import TYPE_CHECKING\n\nfrom kitty.types import AsyncResponse\nfrom kitty.utils import is_png\n\nfrom .base import (\n    MATCH_WINDOW_OPTION,\n    SUPPORTED_IMAGE_FORMATS,\n    ArgsType,\n    Boss,\n    CmdGenerator,\n    ImageCompletion,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetBackgroundImageRCOptions as CLIOptions\n\n\nlayout_choices = 'tiled,scaled,mirror-tiled,clamped,configured'\n\n\nclass SetBackgroundImage(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    data+/str: Chunk of at most 512 bytes of PNG data, base64 encoded. Must send an empty chunk to indicate end of image. \\\n    Or the special value - to indicate image must be removed.\n    match/str: Window to change opacity in\n    layout/choices.{layout_choices.replace(\",\", \".\")}: The image layout\n    all/bool: Boolean indicating operate on all windows\n    configured/bool: Boolean indicating if the configured value should be changed\n    '''\n\n    short_desc = 'Set the background image'\n    desc = (\n        'Set the background image for the specified OS windows. You must specify the path to an image that'\n        ' will be used as the background. If you specify the special value :code:`none` then any existing image will'\n        ' be removed. Supported image formats are: '\n    ) + ', '.join(SUPPORTED_IMAGE_FORMATS)\n    options_spec = f'''\\\n--all -a\ntype=bool-set\nBy default, background image is only changed for the currently active OS window. This option will\ncause the image to be changed in all windows.\n\n\n--configured -c\ntype=bool-set\nChange the configured background image which is used for new OS windows.\n\n\n--layout\ntype=choices\nchoices={layout_choices}\ndefault=configured\nHow the image should be displayed. A value of :code:`configured` will use the configured value.\n\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response from kitty. This means that even if setting the background image\nfailed, the command will exit with a success code.\n''' + '\\n\\n' + MATCH_WINDOW_OPTION\n    args = RemoteCommand.Args(spec='PATH_TO_PNG_IMAGE', count=1, json_field='data', special_parse='!read_window_logo(io_data, args[0])',\n                              completion=ImageCompletion)\n    reads_streaming_data = True\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if len(args) != 1:\n            self.fatal('Must specify path to exactly one PNG image')\n        path = os.path.expanduser(args[0])\n        import secrets\n        ret = {\n            'match': opts.match,\n            'configured': opts.configured,\n            'layout': opts.layout,\n            'all': opts.all,\n            'stream_id': secrets.token_urlsafe(),\n        }\n        if path.lower() == 'none':\n            ret['data'] = '-'\n            return ret\n        if not is_png(path):\n            self.fatal(f'{path} is not a PNG image')\n\n        def file_pipe(path: str) -> CmdGenerator:\n            with open(path, 'rb') as f:\n                while True:\n                    data = f.read(512)\n                    if not data:\n                        break\n                    ret['data'] = standard_b64encode(data).decode('ascii')\n                    yield ret\n            ret['data'] = ''\n            yield ret\n        return file_pipe(path)\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        data = payload_get('data')\n        windows = self.windows_for_payload(boss, window, payload_get, window_match_name='match')\n        os_windows = tuple({w.os_window_id for w in windows if w})\n        layout = payload_get('layout')\n        if data == '-':\n            path = None\n            tfile = BytesIO()\n        else:\n            q = self.handle_streamed_data(standard_b64decode(data) if data else b'', payload_get)\n            if isinstance(q, AsyncResponse):\n                return q\n            path = '/image/from/remote/control'\n            tfile = q\n\n        try:\n            boss.set_background_image(path, os_windows, payload_get('configured'), layout, tfile.getvalue())\n        except ValueError as err:\n            err.hide_traceback = True  # type: ignore\n            raise\n        return None\n\n\nset_background_image = SetBackgroundImage()\n"
  },
  {
    "path": "kitty/rc/set_background_opacity.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import (\n    MATCH_TAB_OPTION,\n    MATCH_WINDOW_OPTION,\n    ArgsType,\n    Boss,\n    OpacityError,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetBackgroundOpacityRCOptions as CLIOptions\n\n\nclass SetBackgroundOpacity(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    opacity+/float: A number between 0 and 1\n    match_window/str: Window to change opacity in\n    match_tab/str: Tab to change opacity in\n    all/bool: Boolean indicating operate on all windows\n    toggle/bool: Boolean indicating if opacity should be toggled between the default and the specified value\n    '''\n\n    short_desc = 'Set the background opacity'\n    desc = (\n        'Set the background opacity for the specified windows. This will only work if you have turned on'\n        ' :opt:`dynamic_background_opacity` in :file:`kitty.conf`. The background opacity affects all kitty windows in a'\n        ' single OS window. For example::\\n\\n'\n        '    kitten @ set-background-opacity 0.5'\n    )\n    options_spec = '''\\\n--all -a\ntype=bool-set\nBy default, background opacity are only changed for the currently active OS window. This option will\ncause background opacity to be changed in all windows.\n\n\n--toggle\ntype=bool-set\nWhen specified, the background opacity for the matching OS windows will be reset to default if it is currently\nequal to the specified value, otherwise it will be set to the specified value.\n''' + '\\n\\n' + MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')\n    args = RemoteCommand.Args(spec='OPACITY', count=1, json_field='opacity', special_parse='parse_opacity(args[0])')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        opacity = max(0, min(float(args[0]), 1))\n        return {\n            'opacity': opacity, 'match_window': opts.match,\n            'all': opts.all, 'match_tab': opts.match_tab, 'toggle': opts.toggle,\n        }\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        from kitty.fast_data_types import background_opacity_of, get_options\n        opts = get_options()\n        if not opts.dynamic_background_opacity:\n            raise OpacityError('You must turn on the dynamic_background_opacity option in kitty.conf to be able to set background opacity')\n        windows = self.windows_for_payload(boss, window, payload_get)\n        for os_window_id in {w.os_window_id for w in windows if w}:\n            val: float = payload_get('opacity') or 0.\n            if payload_get('toggle'):\n                current = background_opacity_of(os_window_id)\n                # GLFW represents opacity as a float internally, but python's\n                # \"float\" type has double precision, so we can't rely on precise\n                # equality here\n                if current is not None and abs(current - val) <= 0.0001:\n                    val = opts.background_opacity\n            boss._set_os_window_background_opacity(os_window_id, val)\n        return None\n\n\nset_background_opacity = SetBackgroundOpacity()\n"
  },
  {
    "path": "kitty/rc/set_colors.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom kitty.cli import emph\nfrom kitty.fast_data_types import Color\n\nfrom .base import (\n    MATCH_TAB_OPTION,\n    MATCH_WINDOW_OPTION,\n    ArgsType,\n    Boss,\n    ParsingOfArgsFailed,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetColorsRCOptions as CLIOptions\n\n\nclass SetColors(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    colors+/dict.colors: An object mapping names to colors as 24-bit RGB integers or null for nullable colors. Or a string for transparent_background_colors.\n    match_window/str: Window to change colors in\n    match_tab/str: Tab to change colors in\n    all/bool: Boolean indicating change colors everywhere or not\n    configured/bool: Boolean indicating whether to change the configured colors. Must be True if reset is True\n    reset/bool: Boolean indicating colors should be reset to startup values\n    '''\n\n    short_desc = 'Set terminal colors'\n    desc = (\n        'Set the terminal colors for the specified windows/tabs (defaults to active window).'\n        ' You can either specify the path to a conf file'\n        ' (in the same format as :file:`kitty.conf`) to read the colors from or you can specify individual colors,'\n        ' for example::\\n\\n'\n        '    kitten @ set-colors foreground=red background=white'\n    )\n    options_spec = '''\\\n--all -a\ntype=bool-set\nBy default, colors are only changed for the currently active window. This option will\ncause colors to be changed in all windows.\n\n\n--configured -c\ntype=bool-set\nAlso change the configured colors (i.e. the colors kitty will use for new\nwindows or after a reset).\n\n\n--reset\ntype=bool-set\nRestore all colors to the values they had at kitty startup. Note that if you specify\nthis option, any color arguments are ignored and :option:`kitten @ set-colors --configured` and :option:`kitten @ set-colors --all` are implied.\n''' + '\\n\\n' + MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')\n    args = RemoteCommand.Args(spec='COLOR_OR_FILE ...', json_field='colors', special_parse='parse_colors_and_files(args)',\n                              completion=RemoteCommand.CompletionSpec.from_string('type:file group:\"CONF files\", ext:conf'))\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        final_colors: dict[str, int | None | str] = {}\n        transparent_background_colors: tuple[tuple[Color, float], ...] = ()\n        from kitty.colors import parse_colors\n        if not opts.reset:\n            try:\n                fc, transparent_background_colors = parse_colors(args)\n            except FileNotFoundError as err:\n                raise ParsingOfArgsFailed(f'The colors configuration file {emph(err.filename)} was not found.') from err\n            except Exception as err:\n                raise ParsingOfArgsFailed(str(err)) from err\n            final_colors.update(fc)\n        if transparent_background_colors:\n            final_colors['transparent_background_colors'] = ' '.join(f'{c.as_sharp}@{f}' for c, f in transparent_background_colors)\n        ans = {\n            'match_window': opts.match, 'match_tab': opts.match_tab,\n            'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset,\n            'colors': final_colors, 'reset': opts.reset,\n        }\n        return ans\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        from kitty.colors import patch_colors\n        windows = self.windows_for_payload(boss, window, payload_get)\n        colors: dict[str, int | None] = payload_get('colors') or {}\n        if payload_get('reset'):\n            colors = {k: None if v is None else int(v) for k, v in boss.color_settings_at_startup.items()}\n        tbc = colors.get('transparent_background_colors')\n        if tbc:\n            from kitty.options.utils import transparent_background_colors\n            parsed_tbc = transparent_background_colors(str(tbc))\n        else:\n            parsed_tbc = ()\n        patch_colors(colors, parsed_tbc, bool(payload_get('configured')), windows=windows)\n        return None\n\n\nset_colors = SetColors()\n"
  },
  {
    "path": "kitty/rc/set_enabled_layouts.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Iterable\nfrom typing import TYPE_CHECKING\n\nfrom kitty.fast_data_types import get_options\nfrom kitty.options.utils import parse_layout_names\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetEnabledLayoutsRCOptions as CLIOptions\n\n\ndef layout_names() -> Iterable[str]:\n    from kitty.layout.interface import all_layouts\n    return all_layouts.keys()\n\n\nclass SetEnabledLayouts(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    layouts+/list.str: The list of layout names\n    match/str: Which tab to change the layout of\n    configured/bool: Boolean indicating whether to change the configured value\n    '''\n\n    short_desc = 'Set the enabled layouts in tabs'\n    desc = (\n        'Set the enabled layouts in the specified tabs (or the active tab if not specified).'\n        ' You can use special match value :code:`all` to set the enabled layouts in all tabs. If the'\n        ' current layout of the tab is not included in the enabled layouts, its layout is changed'\n        ' to the first enabled layout.'\n    )\n    options_spec = MATCH_TAB_OPTION + '''\\n\\n\n--configured\ntype=bool-set\nChange the default enabled layout value so that the new value takes effect for all newly created tabs\nas well.\n'''\n    args = RemoteCommand.Args(\n        spec='LAYOUT ...', minimum_count=1, json_field='layouts',\n        completion=RemoteCommand.CompletionSpec.from_string('type:keyword group:\"Layout\" kwds:' + ','.join(layout_names())),\n        args_choices=layout_names)\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if len(args) < 1:\n            self.fatal('At least one layout must be specified')\n        a: list[str] = []\n        for x in args:\n            a.extend(y.strip() for y in x.split(','))\n        try:\n            layouts = parse_layout_names(a)\n        except ValueError as err:\n            self.fatal(str(err))\n        return {'layouts': layouts, 'match': opts.match}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        tabs = self.tabs_for_match_payload(boss, window, payload_get)\n        layouts = parse_layout_names(payload_get('layouts'))\n        if payload_get('configured'):\n            get_options().enabled_layouts = list(layouts)\n        for tab in tabs:\n            if tab:\n                tab.set_enabled_layouts(layouts)\n        return None\n\n\nset_enabled_layouts = SetEnabledLayouts()\n"
  },
  {
    "path": "kitty/rc/set_font_size.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetFontSizeRCOptions as CLIOptions\n\n\nclass SetFontSize(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    size+/float: The new font size in pts (a positive number). If absent is assumed to be zero which means reset to default.\n    all/bool: Boolean whether to change font size in the current window or all windows\n    increment_op/choices.+.-.*./: The string ``+``, ``-``, ``*`` or ``/`` to interpret size as an increment\n    '''\n\n    short_desc = 'Set the font size in the active top-level OS window'\n    desc = (\n        'Sets the font size to the specified size, in pts. Note'\n        ' that in kitty all sub-windows in the same OS window'\n        ' must have the same font size. A value of zero'\n        ' resets the font size to default. Prefixing the value'\n        ' with a :code:`+`, :code:`-`, :code:`*` or :code:`/` changes the font size by the specified'\n        ' amount. Use -- before using - to have it not mistaken for a option. For example:'\n        ' kitten @ set-font-size -- -2'\n    )\n    args = RemoteCommand.Args(spec='FONT_SIZE', count=1, special_parse='+increment_op:parse_set_font_size(args[0], &payload)', json_field='size')\n    options_spec = '''\\\n--all -a\ntype=bool-set\nBy default, the font size is only changed in the active OS window,\nthis option will cause it to be changed in all OS windows. It also changes\nthe font size for any newly created OS Windows in the future.\n'''\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if not args:\n            self.fatal('No font size specified')\n        fs = args[0]\n        inc = fs[0] if fs and fs[0] in '+-' else None\n        return {'size': abs(float(fs)), 'all': opts.all, 'increment_op': inc}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        boss.change_font_size(\n            payload_get('all'),\n            payload_get('increment_op'), payload_get('size') or 0)\n        return None\n\n\nset_font_size = SetFontSize()\n"
  },
  {
    "path": "kitty/rc/set_spacing.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom collections.abc import Iterable\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetSpacingRCOptions as CLIOptions\n    from kitty.options.types import Options\n\n\ndef patch_window_edges(w: Window, s: dict[str, float | None]) -> None:\n    for k, v in s.items():\n        which, edge = k.lower().split('-', 1)\n        if edge == 'left':\n            w.patch_edge_width(which, 'left', v)\n        elif edge == 'right':\n            w.patch_edge_width(which, 'right', v)\n        elif edge == 'top':\n            w.patch_edge_width(which, 'top', v)\n        elif edge == 'bottom':\n            w.patch_edge_width(which, 'bottom', v)\n\n\ndef patch_configured_edges(opts: 'Options', s: dict[str, float | None]) -> None:\n    for k, val in s.items():\n        if val is None:\n            continue\n        which, edge = k.lower().split('-', 1)\n        q = f'window_{which}_width'\n        new_edges = getattr(opts, q)._replace(**{edge: val})\n        setattr(opts, q, new_edges)\n\n\ndef parse_spacing_settings(args: Iterable[str]) -> dict[str, float | None]:\n    mapper: dict[str, list[str]] = {}\n    for q in ('margin', 'padding'):\n        mapper[q] = f'{q}-left {q}-top {q}-right {q}-bottom'.split()\n        mapper[f'{q}-h'] = mapper[f'{q}-horizontal'] = f'{q}-left {q}-right'.split()\n        mapper[f'{q}-v'] = mapper[f'{q}-vertical'] = f'{q}-top {q}-bottom'.split()\n        for edge in ('left', 'top', 'right', 'bottom'):\n            mapper[f'{q}-{edge}'] = [f'{q}-{edge}']\n    settings: dict[str, float | None] = {}\n    for spec in args:\n        parts = spec.split('=', 1)\n        if len(parts) != 2:\n            raise ValueError(f'{spec} is not a valid setting')\n        which = mapper.get(parts[0].lower())\n        if not which:\n            raise ValueError(f'{parts[0]} is not a valid edge specification')\n        if parts[1].lower() == 'default':\n            val = None\n        else:\n            try:\n                val = float(parts[1])\n            except Exception:\n                raise ValueError(f'{parts[1]} is not a number')\n        for q in which:\n            settings[q] = val\n    return settings\n\n\nclass SetSpacing(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    settings+/dict.spacing: An object mapping margins/paddings using canonical form {'margin-top': 50, 'padding-left': null} etc\n    match_window/str: Window to change paddings and margins in\n    match_tab/str: Tab to change paddings and margins in\n    all/bool: Boolean indicating change paddings and margins everywhere or not\n    configured/bool: Boolean indicating whether to change the configured paddings and margins. Must be True if reset is True\n    '''\n\n    short_desc = 'Set window paddings and margins'\n    desc = (\n        'Set the paddings and margins for the specified windows (defaults to active window).'\n        ' For example: :code:`margin=20` or :code:`padding-left=10` or :code:`margin-h=30`. The shorthand form sets'\n        ' all values, the :code:`*-h` and :code:`*-v` variants set horizontal and vertical values.'\n        ' The special value :code:`default` resets to using the default value.'\n        ' If you specify a tab rather than a window, all windows in that tab are affected.'\n    )\n    options_spec = '''\\\n--all -a\ntype=bool-set\nBy default, settings are only changed for the currently active window. This option will\ncause paddings and margins to be changed in all windows.\n\n\n--configured -c\ntype=bool-set\nAlso change the configured paddings and margins (i.e. the settings kitty will use for new\nwindows).\n''' + '\\n\\n' + MATCH_WINDOW_OPTION + '\\n\\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')\n    args = RemoteCommand.Args(spec='MARGIN_OR_PADDING ...', minimum_count=1, json_field='settings', special_parse='parse_set_spacing(args)')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if not args:\n            self.fatal('At least one setting must be specified')\n        try:\n            settings = parse_spacing_settings(args)\n        except Exception as e:\n            self.fatal(str(e))\n        ans = {\n            'match_window': opts.match, 'match_tab': opts.match_tab,\n            'all': opts.all, 'configured': opts.configured,\n            'settings': settings\n        }\n        return ans\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        windows = self.windows_for_payload(boss, window, payload_get)\n        settings: dict[str, float | None] = payload_get('settings')\n        dirtied_tabs = {}\n        from kitty.fast_data_types import get_options\n        if payload_get('configured'):\n            patch_configured_edges(get_options(), settings)\n\n        for w in windows:\n            if w:\n                patch_window_edges(w, settings)\n                tab = w.tabref()\n                if tab is not None:\n                    dirtied_tabs[tab.id] = tab\n\n        for tab in dirtied_tabs.values():\n            tab.relayout()\n        return None\n\n\nset_spacing = SetSpacing()\n"
  },
  {
    "path": "kitty/rc/set_tab_color.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom kitty.rgb import to_color\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, ParsingOfArgsFailed, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetTabColorRCOptions as CLIOptions\n\n\nvalid_color_names = frozenset('active_fg active_bg inactive_fg inactive_bg'.split())\n\n\ndef parse_colors(args: ArgsType) -> dict[str, int | None]:\n    ans: dict[str, int | None] = {}\n    for spec in args:\n        key, val = spec.split('=', 1)\n        key = key.lower()\n        if key.lower() not in valid_color_names:\n            raise KeyError(f'{key} is not a valid color name')\n        if val.lower() == 'none':\n            col: int | None = None\n        else:\n            q = to_color(val, validate=True)\n            if q is not None:\n                col = int(q)\n        ans[key.lower()] = col\n    return ans\n\n\nclass SetTabColor(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    colors+/dict.colors: An object mapping names to colors as 24-bit RGB integers. A color value of null indicates it should be unset.\n    match/str: Which tab to change the color of\n    self/bool: Boolean indicating whether to use the tab of the window the command is run in\n    '''\n\n    short_desc = 'Change the color of the specified tabs in the tab bar'\n    desc = f'''\n{short_desc}\n\nThe foreground and background colors when active and inactive can be overridden using this command. \\\nThe syntax for specifying colors is: active_fg=color active_bg=color inactive_fg=color \\\ninactive_bg=color. Where color can be either a color name or a value of the form #rrggbb or \\\nthe keyword NONE to revert to using the default colors.\n'''\n    options_spec = MATCH_TAB_OPTION + '''\\n\n--self\ntype=bool-set\nClose the tab this command is run in, rather than the active tab.\n'''\n    args = RemoteCommand.Args(spec='COLORS', json_field='colors', minimum_count=1, special_parse='parse_tab_colors(args)')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        try:\n            colors = parse_colors(args)\n        except Exception as err:\n            raise ParsingOfArgsFailed(str(err)) from err\n        if not colors:\n            raise ParsingOfArgsFailed('No colors specified')\n        return {'match': opts.match, 'self': opts.self, 'colors': colors}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        colors = payload_get('colors')\n        s = {k: None if colors[k] is None else int(colors[k]) for k in valid_color_names if k in colors}\n        for tab in self.tabs_for_match_payload(boss, window, payload_get):\n            if tab:\n                for k, v in s.items():\n                    setattr(tab, k, v)\n                tab.mark_tab_bar_dirty()\n        return None\n\n\nset_tab_color = SetTabColor()\n"
  },
  {
    "path": "kitty/rc/set_tab_title.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetTabTitleRCOptions as CLIOptions\n\n\nclass SetTabTitle(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    title+/str: The new title\n    match/str: Which tab to change the title of\n    '''\n\n    short_desc = 'Set the tab title'\n    desc = (\n        'Set the title for the specified tabs. If you use the :option:`kitten @ set-tab-title --match` option'\n        ' the title will be set for all matched tabs. By default, only the tab'\n        ' in which the command is run is affected. If you do not specify a title, the'\n        ' title of the currently active window in the tab is used.'\n    )\n    options_spec = MATCH_TAB_OPTION\n    args = RemoteCommand.Args(spec='TITLE ...', json_field='title', special_parse='expand_ansi_c_escapes_in_args(args...)')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'title': ' '.join(args), 'match': opts.match}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        for tab in self.tabs_for_match_payload(boss, window, payload_get):\n            if tab:\n                tab.set_title(payload_get('title'))\n        return None\n\n\nset_tab_title = SetTabTitle()\n"
  },
  {
    "path": "kitty/rc/set_user_vars.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetUserVarsRCOptions as CLIOptions\n\n\nclass SetUserVars(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    var/list.str: List of user variables of the form NAME=VALUE\n    match/str: Which windows to change the title in\n    '''\n\n    short_desc = 'Set user variables on a window'\n    desc = (\n        'Set user variables for the specified windows. If you use the :option:`kitten @ set-user-vars --match` option'\n        ' the variables will be set for all matched windows. By default, only the window'\n        ' in which the command is run is affected. If you do not specify any variables, the'\n        ' current variables are printed out, one per line. To unset a variable specify just its name.'\n    )\n    options_spec = MATCH_WINDOW_OPTION\n    args = RemoteCommand.Args(json_field='var', spec='[NAME=VALUE ...]')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        return {'match': opts.match, 'var': args, 'self': True}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        val = {}\n        for x in payload_get('var') or ():\n            a, sep, b = x.partition('=')\n            if sep:\n                val[a] = b\n            else:\n                val[a] = None\n        lines = []\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                if val:\n                    for k, v in val.items():\n                        window.set_user_var(k, v)\n                else:\n                    lines.append('\\n'.join(f'{k}={v}' for k, v in window.user_vars.items()))\n        return '\\n\\n'.join(lines)\n\n\nset_user_vars = SetUserVars()\n"
  },
  {
    "path": "kitty/rc/set_window_logo.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nfrom base64 import standard_b64decode, standard_b64encode\nfrom io import BytesIO\nfrom typing import TYPE_CHECKING\n\nfrom kitty.types import AsyncResponse\nfrom kitty.utils import is_png\n\nfrom .base import (\n    MATCH_WINDOW_OPTION,\n    SUPPORTED_IMAGE_FORMATS,\n    ArgsType,\n    Boss,\n    CmdGenerator,\n    ImageCompletion,\n    PayloadGetType,\n    PayloadType,\n    RCOptions,\n    RemoteCommand,\n    ResponseType,\n    Window,\n)\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetWindowLogoRCOptions as CLIOptions\n\n\nclass SetWindowLogo(RemoteCommand):\n    protocol_spec = __doc__ = '''\n    data+/str: Chunk of PNG data, base64 encoded no more than 2048 bytes. Must send an empty chunk to indicate end of image. \\\n    Or the special value :code:`-` to indicate image must be removed.\n    position/str: The logo position as a string, empty string means default\n    alpha/float: The logo alpha between :code:`0` and :code:`1`. :code:`-1` means use default\n    match/str: Which window to change the logo in\n    self/bool: Boolean indicating whether to act on the window the command is run in\n    '''\n\n    short_desc = 'Set the window logo'\n    desc = (\n        'Set the logo image for the specified windows. You must specify the path to an image that'\n        ' will be used as the logo. If you specify the special value :code:`none` then any existing logo will'\n        ' be removed. Supported image formats are: '\n    ) + ', '.join(SUPPORTED_IMAGE_FORMATS)\n\n    options_spec = MATCH_WINDOW_OPTION + '''\\n\n--self\ntype=bool-set\nAct on the window this command is run in, rather than the active window.\n\n\n--position\nThe position for the window logo. See :opt:`window_logo_position`.\n\n\n--alpha\ntype=float\ndefault=-1\nThe amount the window logo should be faded into the background.\nSee :opt:`window_logo_position`.\n\n\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response from kitty. This means that even if setting the image\nfailed, the command will exit with a success code.\n'''\n    args = RemoteCommand.Args(spec='PATH_TO_PNG_IMAGE', count=1, json_field='data', special_parse='!read_window_logo(io_data, args[0])',\n                              completion=ImageCompletion)\n    reads_streaming_data = True\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        if len(args) != 1:\n            self.fatal('Must specify path to exactly one PNG image')\n        path = os.path.expanduser(args[0])\n        import secrets\n        ret = {\n            'match': opts.match,\n            'self': opts.self,\n            'alpha': opts.alpha,\n            'position': opts.position,\n            'stream_id': secrets.token_urlsafe(),\n        }\n        if path.lower() == 'none':\n            ret['data'] = '-'\n            return ret\n        if not is_png(path):\n            self.fatal(f'{path} is not a PNG image')\n\n        def file_pipe(path: str) -> CmdGenerator:\n            with open(path, 'rb') as f:\n                while True:\n                    data = f.read(512)\n                    if not data:\n                        break\n                    ret['data'] = standard_b64encode(data).decode('ascii')\n                    yield ret\n            ret['data'] = ''\n            yield ret\n        return file_pipe(path)\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        data = payload_get('data')\n        alpha = float(payload_get('alpha', '-1'))\n        position = payload_get('position') or ''\n        if data == '-':\n            path = ''\n            tfile = BytesIO()\n        else:\n            q = self.handle_streamed_data(standard_b64decode(data) if data else b'', payload_get)\n            if isinstance(q, AsyncResponse):\n                return q\n            import hashlib\n            path = '/from/remote/control/' + hashlib.sha1(q.getvalue()).hexdigest()\n            tfile = q\n\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                window.set_logo(path, position, alpha, tfile.getvalue())\n        return None\n\n\nset_window_logo = SetWindowLogo()\n"
  },
  {
    "path": "kitty/rc/set_window_title.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SetWindowTitleRCOptions as CLIOptions\n\n\nclass SetWindowTitle(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    title/str: The new title\n    match/str: Which windows to change the title in\n    temporary/bool: Boolean indicating if the change is temporary or permanent\n    '''\n\n    short_desc = 'Set the window title'\n    desc = (\n        'Set the title for the specified windows. If you use the :option:`kitten @ set-window-title --match` option'\n        ' the title will be set for all matched windows. By default, only the window'\n        ' in which the command is run is affected. If you do not specify a title, the'\n        ' last title set by the child process running in the window will be used.'\n    )\n    options_spec = '''\\\n--temporary\ntype=bool-set\nBy default, the title will be permanently changed and programs running in the window will not be able to change it\nagain. If you want to allow other programs to change it afterwards, use this option.\n    ''' + '\\n\\n' + MATCH_WINDOW_OPTION\n    args = RemoteCommand.Args(json_field='title', spec='[TITLE ...]', special_parse='expand_ansi_c_escapes_in_args(args...)')\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        ans = {'match': opts.match, 'temporary': opts.temporary}\n        title = ' '.join(args)\n        if title:\n            ans['title'] = title\n        # defaults to set the window title this command is run in\n        ans['self'] = True\n        return ans\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        title = payload_get('title')\n        if payload_get('temporary') and title is not None:\n            title = memoryview(title.encode('utf-8'))\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                if payload_get('temporary'):\n                    window.override_title = None\n                    window.title_changed(title)\n                else:\n                    window.set_title(title)\n        return None\n\n\nset_window_title = SetWindowTitle()\n"
  },
  {
    "path": "kitty/rc/signal_child.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window\n\nif TYPE_CHECKING:\n    from kitty.cli_stub import SignalChildRCOptions as CLIOptions\n\n\nclass SignalChild(RemoteCommand):\n\n    protocol_spec = __doc__ = '''\n    signals+/list.str: The signals, a list of names, such as :code:`SIGTERM`, :code:`SIGKILL`, :code:`SIGUSR1`, etc.\n    match/str: Which windows to send the signals to\n    '''\n\n    short_desc = 'Send a signal to the foreground process in the specified windows'\n    desc = (\n        'Send one or more signals to the foreground process in the specified windows.'\n        ' If you use the :option:`kitten @ signal-child --match` option'\n        ' the signal will be sent for all matched windows. By default, only the active'\n        ' window is affected. If you do not specify any signals, :code:`SIGINT` is sent by default.'\n        ' You can also map :ac:`signal_child` to a shortcut in :file:`kitty.conf`, for example::\\n\\n'\n        '    map f1 signal_child SIGTERM'\n    )\n    options_spec = '''\\\n--no-response\ntype=bool-set\ndefault=false\nDon't wait for a response indicating the success of the action. Note that\nusing this option means that you will not be notified of failures.\n    ''' + '\\n\\n' + MATCH_WINDOW_OPTION\n    args = RemoteCommand.Args(json_field='signals', spec='[SIGNAL_NAME ...]', value_if_unspecified=('SIGINT',))\n\n    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:\n        # defaults to signal the window this command is run in\n        return {'match': opts.match, 'self': True, 'signals': [x.upper() for x in args] or ['SIGINT']}\n\n    def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:\n        import signal\n        signals = tuple(getattr(signal, x) for x in payload_get('signals'))\n        for window in self.windows_for_match_payload(boss, window, payload_get):\n            if window:\n                window.signal_child(*signals)\n        return None\n\n\nsignal_child = SignalChild()\n"
  },
  {
    "path": "kitty/remote_control.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport base64\nimport json\nimport os\nimport re\nimport sys\nfrom collections.abc import Iterable, Iterator, Sequence\nfrom contextlib import suppress\nfrom functools import lru_cache, partial\nfrom time import time_ns\nfrom types import GeneratorType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Optional,\n    cast,\n)\n\nfrom .cli import parse_args\nfrom .cli_stub import RCOptions\nfrom .constants import RC_ENCRYPTION_PROTOCOL_VERSION, appname, version\nfrom .fast_data_types import (\n    AES256GCMDecrypt,\n    AES256GCMEncrypt,\n    EllipticCurveKey,\n    get_boss,\n    get_options,\n    monotonic,\n    read_command_response,\n    send_data_to_peer,\n)\nfrom .rc.base import NoResponse, PayloadGetter, all_command_names, command_for_name\nfrom .types import AsyncResponse\nfrom .typing_compat import BossType, WindowType\nfrom .utils import TTYIO, log_error, parse_address_spec, resolve_custom_file\n\nactive_async_requests: dict[str, float] = {}\nactive_streams: dict[str, str] = {}\nif TYPE_CHECKING:\n    from .window import Window\n\n\ndef encode_response_for_peer(response: Any) -> bytes:\n    return b'\\x1bP@kitty-cmd' + json.dumps(response).encode('utf-8') + b'\\x1b\\\\'\n\n\ndef parse_cmd(serialized_cmd: memoryview, encryption_key: EllipticCurveKey) -> dict[str, Any]:\n    # See https://github.com/python/cpython/issues/74379 for why we cant use\n    # memoryview directly :((\n    try:\n        pcmd = json.loads(bytes(serialized_cmd))\n    except Exception:\n        log_error('Failed to parse JSON payload of remote command, ignoring it')\n        return {}\n    if not isinstance(pcmd, dict) or 'version' not in pcmd:\n        log_error('JSON payload of remote command is invalid, must be an object with a version field')\n        return {}\n    pcmd.pop('password', None)\n    if 'encrypted' in pcmd:\n        if pcmd.get('enc_proto', '1') != RC_ENCRYPTION_PROTOCOL_VERSION:\n            log_error(f'Ignoring encrypted rc command with unsupported protocol: {pcmd.get(\"enc_proto\")}')\n            return {}\n        pubkey = pcmd.get('pubkey', '')\n        if not pubkey:\n            log_error('Ignoring encrypted rc command without a public key')\n        d = AES256GCMDecrypt(encryption_key.derive_secret(base64.b85decode(pubkey)), base64.b85decode(pcmd['iv']), base64.b85decode(pcmd['tag']))\n        data = d.add_data_to_be_decrypted(base64.b85decode(pcmd['encrypted']), True)\n        pcmd = json.loads(data)\n        if not isinstance(pcmd, dict) or 'version' not in pcmd:\n            return {}\n        delta = time_ns() - pcmd.pop('timestamp')\n        if abs(delta) > 5 * 60 * 1e9:\n            log_error(\n                f'Ignoring encrypted rc command with timestamp {delta / 1e9:.1f} seconds from now.'\n                ' Could be an attempt at a replay attack or an incorrect clock on a remote machine.')\n            return {}\n    return pcmd\n\n\nclass CMDChecker:\n\n    def __call__(self, pcmd: dict[str, Any], window: Optional['Window'], from_socket: bool, extra_data: dict[str, Any]) -> bool | None:\n        return False\n\n\n@lru_cache(maxsize=64)\ndef is_cmd_allowed_loader(path: str) -> CMDChecker:\n    import runpy\n    try:\n        m = runpy.run_path(path)\n        func: CMDChecker = m['is_cmd_allowed']\n    except Exception as e:\n        log_error(f'Failed to load cmd check function from {path} with error: {e}')\n        func = CMDChecker()\n    return func\n\n\n@lru_cache(maxsize=1024)\ndef fnmatch_pattern(pat: str) -> 're.Pattern[str]':\n    from fnmatch import translate\n    return re.compile(translate(pat))\n\n\ndef remote_control_allowed(\n    pcmd: dict[str, Any], remote_control_passwords: dict[str, Sequence[str]] | None,\n    window: Optional['Window'], extra_data: dict[str, Any]\n) -> bool:\n    if not remote_control_passwords:\n        return True\n    pw = pcmd.get('password', '')\n    auth_items = remote_control_passwords.get(pw)\n    if pw == '!':\n        auth_items = None\n    if auth_items is None:\n        if '!' in remote_control_passwords:\n            raise PermissionError()\n        return False\n    from .remote_control import password_authorizer\n    pa = password_authorizer(auth_items)\n    if not pa.is_cmd_allowed(pcmd, window, False, extra_data):\n        raise PermissionError()\n    return True\n\n\nclass PasswordAuthorizer:\n\n    def __init__(self, auth_items: Iterable[str]) -> None:\n        self.command_patterns = []\n        self.function_checkers = []\n        self.name = ''\n        for item in auth_items:\n            if item.endswith('.py'):\n                path = os.path.abspath(resolve_custom_file(item))\n                self.function_checkers.append(is_cmd_allowed_loader(path))\n            else:\n                self.command_patterns.append(fnmatch_pattern(item))\n\n    def is_cmd_allowed(self, pcmd: dict[str, Any], window: Optional['Window'], from_socket: bool, extra_data: dict[str, Any]) -> bool:\n        cmd_name = pcmd.get('cmd')\n        if not cmd_name:\n            return False\n        if not self.function_checkers and not self.command_patterns:\n            return True\n        for x in self.command_patterns:\n            if x.match(cmd_name) is not None:\n                return True\n        for f in self.function_checkers:\n            try:\n                ret = f(pcmd, window, from_socket, extra_data)\n            except Exception as e:\n                import traceback\n                traceback.print_exc()\n                log_error(f'There was an error using a custom RC auth function, blocking the remote command. Error: {e}')\n                ret = False\n            if ret is not None:\n                return ret\n        return False\n\n\n@lru_cache(maxsize=256)\ndef password_authorizer(auth_items: frozenset[str]) -> PasswordAuthorizer:\n    return PasswordAuthorizer(auth_items)\n\n\nuser_password_allowed: dict[str, bool] = {}\n\n\ndef is_cmd_allowed(pcmd: dict[str, Any], window: Optional['Window'], from_socket: bool, extra_data: dict[str, Any]) -> bool | None:\n    sid = pcmd.get('stream_id', '')\n    if sid and active_streams.get(sid, '') == pcmd['cmd']:\n        return True\n    if 'cancel_async' in pcmd and pcmd.get('async_id'):\n        # we allow these without authentication as they are sent on error\n        # conditions and we can't have users prompted for these. The worst side\n        # effect of a malicious cancel_async request is that it can prevent\n        # another async request from getting a result, if it knows the async_id\n        # of that request.\n        return True\n    pw = pcmd.get('password', '')\n    if not pw:\n        auth_items = get_options().remote_control_password.get('')\n        if auth_items is None:\n            return False\n        pa = password_authorizer(auth_items)\n        return pa.is_cmd_allowed(pcmd, window, from_socket, extra_data)\n    q = user_password_allowed.get(pw)\n    if q is not None:\n        return q\n    auth_items = get_options().remote_control_password.get(pw)\n    if auth_items is None:\n        return None\n    pa = password_authorizer(auth_items)\n    return pa.is_cmd_allowed(pcmd, window, from_socket, extra_data)\n\n\ndef set_user_password_allowed(pwd: str, allowed: bool = True) -> None:\n    user_password_allowed[pwd] = allowed\n\n\ndef close_active_stream(stream_id: str) -> None:\n    active_streams.pop(stream_id, None)\n\n\ndef handle_cmd(\n    boss: BossType, window: WindowType | None, cmd: dict[str, Any], peer_id: int, self_window: WindowType | None\n) -> dict[str, Any] | None | AsyncResponse:\n    v = cmd['version']\n    no_response = cmd.get('no_response', False)\n    if tuple(v)[:2] > version[:2]:\n        if no_response:\n            return None\n        return {'ok': False, 'error': 'The kitty client you are using to send remote commands is newer than this kitty instance. This is not supported.'}\n    c = command_for_name(cmd['cmd'])\n    payload = cmd.get('payload') or {}\n    payload['peer_id'] = peer_id\n    async_id = str(cmd.get('async', ''))\n    stream_id = str(cmd.get('stream_id', ''))\n    stream = bool(cmd.get('stream', False))\n    if (stream or stream_id) and not c.reads_streaming_data:\n        return {'ok': False, 'error': 'Streaming send of data is not supported for this command'}\n    if stream_id:\n        payload['stream_id'] = stream_id\n        active_streams[stream_id] = cmd['cmd']\n        if len(active_streams) > 32:\n            oldest = next(iter(active_streams))\n            del active_streams[oldest]\n    if async_id:\n        payload['async_id'] = async_id\n        if 'cancel_async' in cmd:\n            active_async_requests.pop(async_id, None)\n            c.cancel_async_request(boss, self_window or window, PayloadGetter(c, payload))\n            return None\n        active_async_requests[async_id] = monotonic()\n        if len(active_async_requests) > 32:\n            oldest = next(iter(active_async_requests))\n            del active_async_requests[oldest]\n    try:\n        ans = c.response_from_kitty(boss, self_window or window, PayloadGetter(c, payload))\n    except Exception:\n        if no_response:  # don't report errors if --no-response was used\n            return None\n        raise\n    if isinstance(ans, NoResponse):\n        return None\n    if isinstance(ans, AsyncResponse):\n        if stream:\n            return {'ok': True, 'stream': True}\n        return ans\n    response: dict[str, Any] = {'ok': True}\n    if ans is not None:\n        response['data'] = ans\n    if not no_response:\n        return response\n    return None\n\n\nglobal_options_spec = partial('''\\\n--to\nAn address for the kitty instance to control. Corresponds to the address given\nto the kitty instance via the :option:`kitty --listen-on` option or the\n:opt:`listen_on` setting in :file:`kitty.conf`. If not specified, the\nenvironment variable :envvar:`KITTY_LISTEN_ON` is checked. If that is also not\nfound, messages are sent to the controlling terminal for this process, i.e.\nthey will only work if this process is run within a kitty window.\n\n\n--password\nA password to use when contacting kitty. This will cause kitty to ask the user\nfor permission to perform the specified action, unless the password has been\naccepted before or is pre-configured in :file:`kitty.conf`. To use a blank password\nspecify :option:`kitten @ --use-password` as :code:`always`.\n\n\n--password-file\ncompletion=type:file relative:conf kwds:-\ndefault=rc-pass\nA file from which to read the password. Trailing whitespace is ignored. Relative\npaths are resolved from the kitty configuration directory. Use - to read from STDIN.\nUse :code:`fd:num` to read from the file descriptor :code:`num`.\nUsed if no :option:`kitten @ --password` is supplied. Defaults to checking for the\n:file:`rc-pass` file in the kitty configuration directory.\n\n\n--password-env\ndefault=KITTY_RC_PASSWORD\nThe name of an environment variable to read the password from.\nUsed if no :option:`kitten @ --password-file` is supplied. Defaults\nto checking the environment variable :envvar:`KITTY_RC_PASSWORD`.\n\n\n--use-password\ndefault=if-available\nchoices=if-available,never,always\nIf no password is available, kitty will usually just send the remote control command\nwithout a password. This option can be used to force it to :code:`always` or :code:`never` use\nthe supplied password. If set to always and no password is provided, the blank password is used.\n'''.format, appname=appname)\n\n\ndef encode_send(send: Any) -> bytes:\n    es = ('@kitty-cmd' + json.dumps(send)).encode('ascii')\n    return b'\\x1bP' + es + b'\\x1b\\\\'\n\n\nclass SocketClosed(EOFError):\n    pass\n\n\nclass SocketIO:\n\n    def __init__(self, to: str):\n        self.family, self.address = parse_address_spec(to)[:2]\n\n    def __enter__(self) -> None:\n        import socket\n        self.socket = socket.socket(self.family)\n        self.socket.setblocking(True)\n        self.socket.connect(self.address)\n\n    def __exit__(self, *a: Any) -> None:\n        import socket\n        with suppress(OSError):  # on some OSes such as macOS the socket is already closed at this point\n            self.socket.shutdown(socket.SHUT_RDWR)\n        self.socket.close()\n\n    def send(self, data: bytes | Iterable[str | bytes]) -> None:\n        import socket\n        with self.socket.makefile('wb') as out:\n            if isinstance(data, bytes):\n                out.write(data)\n            else:\n                for chunk in data:\n                    if isinstance(chunk, str):\n                        chunk = chunk.encode('utf-8')\n                    out.write(chunk)\n                    out.flush()\n        self.socket.shutdown(socket.SHUT_WR)\n\n    def simple_recv(self, timeout: float) -> bytes:\n        dcs = re.compile(br'\\x1bP@kitty-cmd([^\\x1b]+)\\x1b\\\\')\n        self.socket.settimeout(timeout)\n        st = monotonic()\n        with self.socket.makefile('rb') as src:\n            data = src.read()\n        m = dcs.search(data)\n        if m is None:\n            if monotonic() - st > timeout:\n                raise TimeoutError('Timed out while waiting to read cmd response')\n            raise SocketClosed('Remote control connection was closed by kitty without any response being received')\n        return bytes(m.group(1))\n\n\nclass RCIO(TTYIO):\n\n    def simple_recv(self, timeout: float) -> bytes:\n        ans: list[bytes] = []\n        read_command_response(self.tty_fd, timeout, ans)\n        return b''.join(ans)\n\n\ndef do_io(\n    to: str | None, original_cmd: dict[str, Any], no_response: bool, response_timeout: float, encrypter: 'CommandEncrypter'\n) -> dict[str, Any]:\n    payload = original_cmd.get('payload')\n    if not isinstance(payload, GeneratorType):\n        send_data: bytes | Iterator[bytes] = encode_send(encrypter(original_cmd))\n    else:\n        def send_generator() -> Iterator[bytes]:\n            assert payload is not None\n            for chunk in payload:\n                original_cmd['payload'] = chunk\n                yield encode_send(encrypter(original_cmd))\n        send_data = send_generator()\n\n    io: SocketIO | RCIO = SocketIO(to) if to else RCIO()\n    with io:\n        io.send(send_data)\n        if no_response:\n            return {'ok': True}\n        received = io.simple_recv(timeout=response_timeout)\n\n    return cast(dict[str, Any], json.loads(received.decode('ascii')))\n\n\ncli_msg = (\n    'Control {appname} by sending it commands. Set the'\n    ' :opt:`allow_remote_control` option in :file:`kitty.conf` or use a password, for this'\n    ' to work.'\n).format(appname=appname)\n\n\ndef parse_rc_args(args: list[str]) -> tuple[RCOptions, list[str]]:\n    cmap = {name: command_for_name(name) for name in sorted(all_command_names())}\n    cmds = (f'  :green:`{cmd.name}`\\n    {cmd.short_desc}' for c, cmd in cmap.items())\n    msg = cli_msg + (\n            '\\n\\n:title:`Commands`:\\n{cmds}\\n\\n'\n            'You can get help for each individual command by using:\\n'\n            '{appname} @ :italic:`command` -h').format(appname=appname, cmds='\\n'.join(cmds))\n    return parse_args(args[1:], global_options_spec, 'command ...', msg, f'{appname} @', result_class=RCOptions)\n\n\ndef encode_as_base85(data: bytes) -> str:\n    return base64.b85encode(data).decode('ascii')\n\n\nclass CommandEncrypter:\n\n    encrypts: bool = True\n\n    def __init__(self, pubkey: bytes, encryption_version: str, password: str) -> None:\n        skey = EllipticCurveKey()\n        self.secret = skey.derive_secret(pubkey)\n        self.pubkey = skey.public\n        self.encryption_version = encryption_version\n        self.password = password\n\n    def __call__(self, cmd: dict[str, Any]) -> dict[str, Any]:\n        encrypter = AES256GCMEncrypt(self.secret)\n        cmd['timestamp'] = time_ns()\n        cmd['password'] = self.password\n        raw = json.dumps(cmd).encode('utf-8')\n        encrypted = encrypter.add_data_to_be_encrypted(raw, True)\n        ans = {\n            'version': version, 'iv': encode_as_base85(encrypter.iv), 'tag': encode_as_base85(encrypter.tag),\n            'pubkey': encode_as_base85(self.pubkey), 'encrypted': encode_as_base85(encrypted),\n        }\n        if self.encryption_version != '1':\n            ans['enc_proto'] = self.encryption_version\n        return ans\n\n    def adjust_response_timeout_for_password(self, response_timeout: float) -> float:\n        return max(response_timeout, 120)\n\n\nclass NoEncryption(CommandEncrypter):\n\n    encrypts: bool = False\n\n    def __init__(self) -> None: ...\n\n    def __call__(self, cmd: dict[str, Any]) -> dict[str, Any]:\n        return cmd\n\n    def adjust_response_timeout_for_password(self, response_timeout: float) -> float:\n        return response_timeout\n\n\ndef create_basic_command(name: str, payload: Any = None, no_response: bool = False, is_asynchronous: bool = False) -> dict[str, Any]:\n    ans = {'cmd': name, 'version': version, 'no_response': no_response}\n    if payload is not None:\n        ans['payload'] = payload\n    if is_asynchronous:\n        from kitty.short_uuid import uuid4\n        ans['async'] = uuid4()\n    return ans\n\n\ndef send_response_to_client(data: Any = None, error: str = '', peer_id: int = 0, window_id: int = 0, async_id: str = '') -> None:\n    if active_async_requests.pop(async_id, None) is None:\n        return\n    if error:\n        response: dict[str, bool | int | str] = {'ok': False, 'error': error}\n    else:\n        response = {'ok': True, 'data': data}\n    if peer_id > 0:\n        send_data_to_peer(peer_id, encode_response_for_peer(response))\n    elif window_id > 0:\n        w = get_boss().window_id_map.get(window_id)\n        if w is not None:\n            w.send_cmd_response(response)\n\n\ndef get_password(opts: RCOptions) -> str:\n    if opts.use_password == 'never':\n        return ''\n    ans = ''\n    if opts.password:\n        ans = opts.password\n    if not ans and opts.password_file:\n        if opts.password_file == '-':\n            if sys.stdin.isatty():\n                from getpass import getpass\n                ans = getpass()\n            else:\n                ans = sys.stdin.read().rstrip()\n                try:\n                    tty_fd = os.open(os.ctermid(), os.O_RDONLY | os.O_CLOEXEC)\n                except OSError:\n                    pass\n                else:\n                    with open(tty_fd, closefd=True):\n                        os.dup2(tty_fd, sys.stdin.fileno())\n        else:\n            try:\n                with open(resolve_custom_file(opts.password_file)) as f:\n                    ans = f.read().rstrip()\n            except OSError:\n                pass\n    if not ans and opts.password_env:\n        ans = os.environ.get(opts.password_env, '')\n    if not ans and opts.use_password == 'always':\n        raise SystemExit('No password was found')\n    if ans and len(ans) > 1024:\n        raise SystemExit('Specified password is too long')\n    return ans\n\n\ndef get_pubkey() -> tuple[str, bytes]:\n    raw = os.environ.get('KITTY_PUBLIC_KEY', '')\n    if not raw:\n        raise SystemExit('Password usage requested but KITTY_PUBLIC_KEY environment variable is not available')\n    version, pubkey = raw.split(':', 1)\n    if version != RC_ENCRYPTION_PROTOCOL_VERSION:\n        raise SystemExit('KITTY_PUBLIC_KEY has unknown version, if you are running on a remote system, update kitty on this system')\n    from base64 import b85decode\n    return version, b85decode(pubkey)\n"
  },
  {
    "path": "kitty/render_cache.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport time\nfrom collections.abc import Iterator\nfrom contextlib import closing, suppress\nfrom functools import partial\n\nfrom .constants import cache_dir, kitten_exe\nfrom .utils import lock_file, unlock_file\n\n\nclass ImageRenderCache:\n\n    lock_file_name = '.lock'\n\n    def __init__(self, subdirname: str = 'rgba', max_entries: int = 32, cache_path: str = ''):\n        self.subdirname = subdirname\n        self.cache_path = cache_path\n        self.cache_dir = ''\n        self.max_entries = max_entries\n\n    def ensure_subdir(self) -> None:\n        if not self.cache_dir:\n            import stat\n            x = os.path.abspath(os.path.join(self.cache_path or cache_dir(), self.subdirname))\n            os.makedirs(x, mode=stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC, exist_ok=True)\n            self.cache_dir = x\n\n    def __enter__(self) -> None:\n        self.ensure_subdir()\n        self.lock_file = open(os.path.join(self.cache_dir, self.lock_file_name), 'wb')\n        try:\n            lock_file(self.lock_file)\n        except Exception:\n            self.lock_file.close()\n            raise\n\n    def __exit__(self, *a: object) -> None:\n        with closing(self.lock_file):\n            unlock_file(self.lock_file)\n\n    def entries(self) -> 'Iterator[os.DirEntry[str]]':\n        for x in os.scandir(self.cache_dir):\n            if x.name != self.lock_file_name:\n                yield x\n\n    def prune_entries(self) -> None:\n        entries = list(self.entries())\n        if len(entries) <= self.max_entries:\n            return\n        def sort_key(e: 'os.DirEntry[str]') -> float:\n            with suppress(OSError):\n                st = e.stat()\n                return st.st_mtime\n            return 0.\n\n        entries.sort(key=sort_key, reverse=True)\n        for e in entries[self.max_entries:]:\n            with suppress(FileNotFoundError):\n                os.remove(e.path)\n\n    def touch(self, path: str) -> None:\n        os.utime(path, follow_symlinks=False)\n\n    def render_image(self, src_path: str, output_path: str) -> None:\n        import stat\n        import subprocess\n        try:\n            with open(src_path, 'rb') as src, open(output_path, 'wb', opener=partial(os.open, mode=stat.S_IREAD | stat.S_IWRITE)) as output:\n                cp = subprocess.run([kitten_exe(), '__convert_image__', 'RGBA'], stdin=src, stdout=output, stderr=subprocess.PIPE)\n                if cp.returncode != 0:\n                    raise ValueError(f'Failed to convert {src_path} to RGBA data with error: {cp.stderr.decode(\"utf-8\", \"replace\")}')\n                if output.seek(0, os.SEEK_END) < 8:\n                    raise ValueError(f'Failed to convert {src_path} to RGBA data, no output written. stderr: {cp.stderr.decode(\"utf-8\", \"replace\")}')\n        except Exception:\n            with suppress(Exception):\n                os.unlink(output_path)\n            raise\n\n    def read_metadata(self, output_path: str) -> tuple[int, int, int]:\n        with open(output_path, 'rb') as f:\n            header = f.read(8)\n            import struct\n            width, height = struct.unpack('<II', header)\n            f.seek(0)\n            return width, height, os.dup(f.fileno())\n\n    def render(self, src_path: str) -> str:\n        import struct\n        from hashlib import sha256\n        src_info = os.stat(src_path)\n        output_name = sha256(struct.pack('@qqqq', src_info.st_dev, src_info.st_ino, src_info.st_size, src_info.st_mtime_ns)).hexdigest()\n\n        with self:\n            output_path = os.path.join(self.cache_dir, output_name)\n            with suppress(OSError):\n                self.touch(output_path)\n                return output_path\n            self.render_image(src_path, output_path)\n            self.prune_entries()\n            return output_path\n\n    def __call__(self, src: str) -> tuple[int, int, int]:\n        return self.read_metadata(self.render(src))\n\n\nclass ImageRenderCacheForTesting(ImageRenderCache):\n\n    def __init__(self, cache_path: str):\n        super().__init__(max_entries=2, cache_path=cache_path)\n        self.current_time = time.time_ns()\n        self.num_of_renders = 0\n\n    def render_image(self, src_path: str, output_path: str) -> None:\n        super().render_image(src_path, output_path)\n        self.touch(output_path)\n        self.num_of_renders += 1\n\n    def touch(self, path:str) -> None:\n        self.current_time += 3 * int(1e9)\n        os.utime(path, ns=(self.current_time, self.current_time))\n\n\ndefault_image_render_cache = ImageRenderCache()\n"
  },
  {
    "path": "kitty/resize.c",
    "content": "/*\n * resize.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"resize.h\"\n#include \"lineops.h\"\n\ntypedef struct Rewrap {\n    struct {\n        LineBuf *lb;\n        HistoryBuf *hb;\n        index_type x, y, hb_count;\n        Line line, scratch_line;\n    } src, dest;\n    ANSIBuf *as_ansi_buf;\n    TrackCursor *cursors;\n    LineBuf *sb;\n\n    index_type num_content_lines_before, src_x_limit;\n    bool prev_src_line_ended_with_wrap, current_src_line_has_multline_cells, current_dest_line_has_multiline_cells;\n    bool dest_line_from_linebuf, src_is_in_linebuf;\n\n} Rewrap;\n\nstatic void\nsetup_line(TextCache *tc, index_type xnum, Line *l) {\n    l->text_cache = tc;\n    l->xnum = xnum;\n}\n\n#define src_xnum (r->src.lb->xnum)\n#define dest_xnum (r->dest.lb->xnum)\n\nstatic void\nexclude_empty_lines_at_bottom(Rewrap *r) {\n    index_type first, i;\n    bool is_empty = true;\n    // Find the first line that contains some content\n#define self (r->src.lb)\n    first = self->ynum;\n    do {\n        first--;\n        CPUCell *cells = linebuf_cpu_cells_for_line(self, first);\n        for(i = 0; i < self->xnum; i++) {\n            if (cells[i].ch_or_idx || cells[i].ch_is_idx) { is_empty = false; break; }\n        }\n    } while(is_empty && first > 0);\n    if (!is_empty) r->num_content_lines_before = first + 1;\n#undef self\n}\n\nstatic void\ninit_src_line_basic(Rewrap *r, index_type y, Line *dest, bool update_state) {\n    if (r->src_is_in_linebuf) {\n        linebuf_init_line_at(r->src.lb, y - r->src.hb_count, dest);\n    } else if (y >= r->src.hb_count) {\n        if (update_state) r->src_is_in_linebuf = true;\n        linebuf_init_line_at(r->src.lb, y - r->src.hb_count, dest);\n    } else {\n        // historybuf_init_line uses reverse indexing\n        historybuf_init_line(r->src.hb, r->src.hb->count - y - 1, dest);\n    }\n}\n\nstatic bool\ninit_src_line(Rewrap *r) {\n    bool newline_needed = !r->prev_src_line_ended_with_wrap;\n    init_src_line_basic(r, r->src.y, &r->src.line, true);\n    r->src_x_limit = src_xnum;\n    r->prev_src_line_ended_with_wrap = r->src.line.cpu_cells[src_xnum - 1].next_char_was_wrapped;\n    r->src.line.cpu_cells[src_xnum - 1].next_char_was_wrapped = false;\n    // Trim trailing blanks\n    while (r->src_x_limit && r->src.line.cpu_cells[r->src_x_limit - 1].ch_and_idx == BLANK_CHAR) r->src_x_limit--;\n    r->src.x = 0;\n    r->current_src_line_has_multline_cells = false;\n    for (index_type i = 0; i < r->src_x_limit; i++) if (r->src.line.cpu_cells[i].is_multicell && r->src.line.cpu_cells[i].scale > 1) {\n        r->current_src_line_has_multline_cells = true;\n        break;\n    }\n    return newline_needed;\n}\n\n#define set_dest_line_attrs(dest_y) r->dest.lb->line_attrs[dest_y] = r->src.line.attrs; r->src.line.attrs.prompt_kind = UNKNOWN_PROMPT_KIND;\n\nstatic void\nfirst_dest_line(Rewrap *r) {\n    if (r->src.hb_count) {\n        historybuf_next_dest_line(r->dest.hb, r->as_ansi_buf, &r->src.line, 0, &r->dest.line, false);\n        r->src.line.attrs.prompt_kind = UNKNOWN_PROMPT_KIND;\n    } else {\n        r->dest_line_from_linebuf = true;\n        linebuf_init_line_at(r->dest.lb, 0, &r->dest.line);\n        set_dest_line_attrs(0);\n    }\n}\n\nstatic index_type\nlinebuf_next_dest_line(Rewrap *r, bool continued) {\n#define dest_y r->dest.y\n    LineBuf *dest = r->dest.lb;\n    linebuf_set_last_char_as_continuation(dest, dest_y, continued);\n    if (dest_y >= dest->ynum - 1) {\n        linebuf_index(dest, 0, dest->ynum - 1);\n        if (r->dest.hb != NULL) {\n            linebuf_init_line(dest, dest->ynum - 1);\n            dest->line->attrs.has_dirty_text = true;\n            historybuf_add_line(r->dest.hb, dest->line, r->as_ansi_buf);\n        }\n        linebuf_clear_line(dest, dest->ynum - 1, true);\n    } else dest_y++;\n    linebuf_init_line_at(dest, dest_y, &r->dest.line);\n    set_dest_line_attrs(dest_y);\n    return dest_y;\n#undef dest_y\n}\n\n\nstatic void\nnext_dest_line(Rewrap *r, bool continued) {\n    r->dest.x = 0;\n    r->current_dest_line_has_multiline_cells = false;\n    if (r->dest_line_from_linebuf) {\n        r->dest.y = linebuf_next_dest_line(r, continued);\n    } else if (r->src_is_in_linebuf) {\n        r->dest_line_from_linebuf = true;\n        r->dest.y = 0;\n        linebuf_init_line_at(r->dest.lb, 0, &r->dest.line);\n        set_dest_line_attrs(0);\n        if (continued && r->dest.hb && r->dest.hb->count) {\n            historybuf_init_line(r->dest.hb, 0, r->dest.hb->line);\n            r->dest.hb->line->cpu_cells[dest_xnum-1].next_char_was_wrapped = true;\n        }\n    } else {\n        r->dest.y = historybuf_next_dest_line(r->dest.hb, r->as_ansi_buf, &r->src.line, r->dest.y, &r->dest.line, continued);\n        r->src.line.attrs.prompt_kind = UNKNOWN_PROMPT_KIND;\n    }\n    if (r->sb->line_attrs[0].has_dirty_text) {\n        CPUCell *cpu_cells; GPUCell *gpu_cells;\n        linebuf_init_cells(r->sb, 0, &cpu_cells, &gpu_cells);\n        memcpy(r->dest.line.cpu_cells, cpu_cells, dest_xnum * sizeof(cpu_cells[0]));\n        memcpy(r->dest.line.gpu_cells, gpu_cells, dest_xnum * sizeof(gpu_cells[0]));\n        r->current_dest_line_has_multiline_cells = true;\n    }\n    linebuf_index(r->sb, 0, r->sb->ynum - 1);\n    if (r->sb->line_attrs[r->sb->ynum - 1].has_dirty_text) {\n        linebuf_clear_line(r->sb, r->sb->ynum - 1, true);\n    }\n}\n\nstatic void\nupdate_tracked_cursors(Rewrap *r, index_type num_cells, index_type src_y, index_type dest_y, index_type x_limit) {\n    if (!r->src_is_in_linebuf) return;\n    src_y -= r->src.hb_count;\n    for (TrackCursor *t = r->cursors; !t->is_sentinel; t++) {\n        if (t->y == src_y && r->src.x <= t->x && (t->x < r->src.x + num_cells || t->x >= x_limit)) {\n            t->dest_y = dest_y;\n            t->dest_x = r->dest.x + (t->x - r->src.x);\n            if (t->dest_x > dest_xnum) t->dest_x = dest_xnum;\n        }\n    }\n}\n\nstatic bool\nfind_space_in_dest_line(Rewrap *r, index_type num_cells) {\n    while (r->dest.x + num_cells <= dest_xnum) {\n        index_type before = r->dest.x;\n        for (index_type x = r->dest.x; x < r->dest.x + num_cells; x++) {\n            if (r->dest.line.cpu_cells[x].is_multicell) {\n                r->dest.x = x + mcd_x_limit(r->dest.line.cpu_cells + x);\n                break;\n            }\n        }\n        if (before == r->dest.x) return true;\n    }\n    return false;\n}\n\nstatic void\nfind_space_in_dest(Rewrap *r, index_type num_cells) {\n    while (!find_space_in_dest_line(r, num_cells)) next_dest_line(r, true);\n}\n\nstatic void\ncopy_range(Line *src, index_type src_at, Line* dest, index_type dest_at, index_type num) {\n    memcpy(dest->cpu_cells + dest_at, src->cpu_cells + src_at, num * sizeof(CPUCell));\n    memcpy(dest->gpu_cells + dest_at, src->gpu_cells + src_at, num * sizeof(GPUCell));\n}\n\nstatic void\ncopy_multiline_extra_lines(Rewrap *r, CPUCell *src_cell, index_type mc_width) {\n    for (index_type i = 1; i < src_cell->scale; i++) {\n        init_src_line_basic(r, r->src.y + i, &r->src.scratch_line, false);\n        linebuf_init_line_at(r->sb, i - 1, &r->dest.scratch_line);\n        linebuf_mark_line_dirty(r->sb, i - 1);\n        copy_range(&r->src.scratch_line, r->src.x, &r->dest.scratch_line, r->dest.x, mc_width);\n        update_tracked_cursors(r, mc_width, r->src.y + i, r->dest.y + i, src_xnum + 10000 /* ensure cursor is moved only if in region being copied */);\n    }\n}\n\n\nstatic void\nmultiline_copy_src_to_dest(Rewrap *r) {\n    CPUCell *c; index_type mc_width;\n    while (r->src.x < r->src_x_limit) {\n        c = &r->src.line.cpu_cells[r->src.x];\n        if (c->is_multicell) {\n            mc_width = mcd_x_limit(c);\n            if (mc_width > dest_xnum) {\n                update_tracked_cursors(r, mc_width, r->src.y, r->dest.y, r->src_x_limit);\n                r->src.x += mc_width;\n                continue;\n            } else if (c->y) {\n                r->src.x += mc_width;\n                continue;\n            }\n        } else mc_width = 1;\n        find_space_in_dest(r, mc_width);\n        copy_range(&r->src.line, r->src.x, &r->dest.line, r->dest.x, mc_width);\n        update_tracked_cursors(r, mc_width, r->src.y, r->dest.y, r->src_x_limit);\n        if (c->scale > 1) copy_multiline_extra_lines(r, c, mc_width);\n        r->src.x += mc_width; r->dest.x += mc_width;\n    }\n}\n\n\nstatic void\nfast_copy_src_to_dest(Rewrap *r) {\n    CPUCell *c;\n    while (r->src.x < r->src_x_limit) {\n        if (r->dest.x >= dest_xnum) {\n            next_dest_line(r, true);\n            if (r->current_dest_line_has_multiline_cells) {\n                multiline_copy_src_to_dest(r);\n                return;\n            }\n        }\n        index_type num = MIN(r->src_x_limit - r->src.x, dest_xnum - r->dest.x);\n        if (num && (c = &r->src.line.cpu_cells[r->src.x + num - 1])->is_multicell && c->x != mcd_x_limit(c) - 1) {\n            // we have a split multicell at the right edge of the copy region\n            multiline_copy_src_to_dest(r);\n            return;\n        }\n        copy_range(&r->src.line, r->src.x, &r->dest.line, r->dest.x, num);\n        update_tracked_cursors(r, num, r->src.y, r->dest.y, r->src_x_limit);\n        r->src.x += num; r->dest.x += num;\n    }\n}\n\n\nstatic void\nrewrap(Rewrap *r) {\n    r->src.hb_count = r->src.hb ? r->src.hb->count : 0;\n    // Fast path\n    if (r->dest.lb->xnum == r->src.lb->xnum && r->dest.lb->ynum == r->src.lb->ynum) {\n        memcpy(r->dest.lb->line_map, r->src.lb->line_map, sizeof(index_type) * r->src.lb->ynum);\n        memcpy(r->dest.lb->line_attrs, r->src.lb->line_attrs, sizeof(LineAttrs) * r->src.lb->ynum);\n        memcpy(r->dest.lb->cpu_cell_buf, r->src.lb->cpu_cell_buf, (size_t)r->src.lb->xnum * r->src.lb->ynum * sizeof(CPUCell));\n        memcpy(r->dest.lb->gpu_cell_buf, r->src.lb->gpu_cell_buf, (size_t)r->src.lb->xnum * r->src.lb->ynum * sizeof(GPUCell));\n        r->num_content_lines_before = r->src.lb->ynum;\n        if (r->dest.hb && r->src.hb) historybuf_fast_rewrap(r->dest.hb, r->src.hb);\n        r->dest.y = r->src.lb->ynum - 1;\n        return;\n    }\n\n    setup_line(r->src.lb->text_cache, src_xnum, &r->src.line);\n    setup_line(r->src.lb->text_cache, dest_xnum, &r->dest.line);\n    setup_line(r->src.lb->text_cache, src_xnum, &r->src.scratch_line);\n    setup_line(r->src.lb->text_cache, dest_xnum, &r->dest.scratch_line);\n\n    exclude_empty_lines_at_bottom(r);\n\n    for (; r->src.y < r->num_content_lines_before + r->src.hb_count; r->src.y++) {\n        if (init_src_line(r)) {\n            if (r->src.y) next_dest_line(r, false);\n            else first_dest_line(r);\n        }\n        if (r->current_src_line_has_multline_cells || r->current_dest_line_has_multiline_cells) multiline_copy_src_to_dest(r);\n        else fast_copy_src_to_dest(r);\n    }\n}\n\nResizeResult\nresize_screen_buffers(LineBuf *lb, HistoryBuf *hb, index_type lines, index_type columns, ANSIBuf *as_ansi_buf, TrackCursor *cursors) {\n    ResizeResult ans = {0};\n    ans.lb = alloc_linebuf(lines, columns, lb->text_cache);\n    if (!ans.lb) return ans;\n    RAII_PyObject(raii_nlb, (PyObject*)ans.lb); (void) raii_nlb;\n    if (hb) {\n        ans.hb = historybuf_alloc_for_rewrap(columns, hb);\n        if (!ans.hb) return ans;\n    }\n    RAII_PyObject(raii_nhb, (PyObject*)ans.hb); (void) raii_nhb;\n    Rewrap r = {\n        .src = {.lb=lb, .hb=hb}, .dest = {.lb=ans.lb, .hb=ans.hb},\n        .as_ansi_buf = as_ansi_buf, .cursors = cursors,\n    };\n    r.sb = alloc_linebuf(SCALE_BITS << 1, columns, lb->text_cache);\n    if (!r.sb) return ans;\n    RAII_PyObject(scratch, (PyObject*)r.sb); (void)scratch;\n    for (TrackCursor *t = cursors; !t->is_sentinel; t++) { t->dest_x = t->x; t->dest_y = t->y; }\n    rewrap(&r);\n    ans.num_content_lines_before = r.num_content_lines_before;\n    ans.num_content_lines_after = MIN(r.dest.y + 1, ans.lb->ynum);\n    if (hb) historybuf_finish_rewrap(ans.hb, hb);\n    for (unsigned i = 0; i < ans.num_content_lines_after; i++) linebuf_mark_line_dirty(ans.lb, i);\n    for (TrackCursor *t = cursors; !t->is_sentinel; t++) { t->dest_x = MIN(t->dest_x, columns); t->dest_y = MIN(t->dest_y, lines); }\n    Py_INCREF(raii_nlb); Py_XINCREF(raii_nhb);\n    ans.ok = true;\n    return ans;\n}\n\nstatic void\nnuke_in_line(CPUCell *cp, GPUCell *gp, index_type start, index_type x_limit) {\n    for (index_type x = start; x < x_limit; x++) {\n        cell_set_char(cp + x, 0); cp[x].is_multicell = false;\n        clear_sprite_position(gp[x]);\n    }\n}\n\nstatic void\nnuke_multicell_char_at(LineBuf *lb, index_type x_, index_type y_) {\n    CPUCell *cp; GPUCell *gp;\n    linebuf_init_cells(lb, y_, &cp, &gp);\n    index_type num_lines_above = cp[x_].y;\n    index_type y_max_limit = MIN(lb->ynum, y_ + cp[x_].scale - num_lines_above);\n    while (cp[x_].x && x_ > 0) x_--;\n    index_type x_limit = MIN(lb->xnum, x_ + mcd_x_limit(&cp[x_]));\n    for (index_type y = y_; y < y_max_limit; y++) {\n        linebuf_init_cells(lb, y, &cp, &gp);\n        nuke_in_line(cp, gp, x_, x_limit);\n    }\n    for (int y = (int)y_ - 1; y > -1 && num_lines_above; y--, num_lines_above--) {\n        linebuf_init_cells(lb, y, &cp, &gp);\n        nuke_in_line(cp, gp, x_, x_limit);\n    }\n}\n\n\nResizeResult\nresize_screen_buffer_without_rewrap(LineBuf *lb, index_type lines, index_type columns, TrackCursor *cursors) {\n    ResizeResult ans = {0};\n    ans.lb = alloc_linebuf(lines, columns, lb->text_cache);\n    if (!ans.lb) return ans;\n    Rewrap r = { .src = {.lb=lb},};\n    exclude_empty_lines_at_bottom(&r);\n    ans.num_content_lines_before = r.num_content_lines_before;\n    ans.num_content_lines_after = MIN(lines, r.num_content_lines_before);\n\n    index_type xcommon = MIN(lb->xnum, ans.lb->xnum);\n    for (index_type y = 0; y < ans.num_content_lines_after; y++) {\n        linebuf_init_line(lb, y); linebuf_init_line(ans.lb, y);\n        ans.lb->line_attrs[y] = lb->line_attrs[y]; ans.lb->line_attrs[y].has_dirty_text = true;\n        memcpy(ans.lb->line->cpu_cells, lb->line->cpu_cells, xcommon * sizeof(lb->line->cpu_cells[0]));\n        memcpy(ans.lb->line->gpu_cells, lb->line->gpu_cells, xcommon * sizeof(lb->line->gpu_cells[0]));\n        if (xcommon > lb->line->xnum) {\n            // extend the colors/styles of the last cell to edge\n            GPUCell e = lb->line->gpu_cells[xcommon-1]; clear_sprite_position(e);\n            for (index_type x = xcommon; x < ans.lb->line->xnum; x++) ans.lb->line->gpu_cells[x] = e;\n        } else if (xcommon < lb->line->xnum) {\n            // remove multicell chars that were split at the right edge\n            index_type last_x = xcommon - 1;\n            CPUCell *c = ans.lb->line->cpu_cells + last_x;\n            if (c->is_multicell && c->x + 1u < mcd_x_limit(c)) {\n                while (ans.lb->line->cpu_cells[last_x].x && last_x > 0) last_x--;\n                nuke_in_line(ans.lb->line->cpu_cells, ans.lb->line->gpu_cells, last_x, ans.lb->line->xnum);\n            }\n        }\n    }\n    // Set bg color for extra lines at bottom\n    if (ans.num_content_lines_before < lines) {\n        linebuf_init_line(lb, lb->ynum-1); GPUCell *g = lb->line->gpu_cells;\n        for (index_type y = ans.num_content_lines_after; y < ans.lb->ynum; y++) {\n            linebuf_init_line(ans.lb, y);\n            for (index_type x = 0; x < ans.lb->xnum; x++) ans.lb->line->gpu_cells[x].bg = g->bg;\n        }\n    } else if (ans.num_content_lines_after < ans.num_content_lines_before) {\n        // delete multicell chars split at the bottom\n        linebuf_init_line(ans.lb, ans.num_content_lines_after-1);\n        for (index_type x = 0; x < ans.lb->xnum; x++) {\n            CPUCell *c = ans.lb->line->cpu_cells + x;\n            if (c->is_multicell && c->y < c->scale-1) nuke_multicell_char_at(ans.lb, x, ans.num_content_lines_after-1);\n        }\n    }\n    for (TrackCursor *tc = cursors; !tc->is_sentinel; tc++) {\n        tc->dest_x = MIN(tc->x, ans.lb->xnum-1);\n        tc->dest_y = MIN(tc->y, ans.lb->ynum-1);\n    }\n    ans.ok = true;\n    return ans;\n}\n"
  },
  {
    "path": "kitty/resize.h",
    "content": "/*\n * resize.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include \"line-buf.h\"\n#include \"history.h\"\n\ntypedef struct TrackCursor {\n    index_type x, y;\n    index_type dest_x, dest_y;\n    bool is_sentinel;\n} TrackCursor;\n\ntypedef struct ResizeResult {\n    LineBuf *lb; HistoryBuf *hb;\n    bool ok;\n    index_type num_content_lines_before, num_content_lines_after;\n} ResizeResult;\n\nResizeResult\nresize_screen_buffers(LineBuf *lb, HistoryBuf *hb, index_type lines, index_type columns, ANSIBuf *as_ansi_buf, TrackCursor *cursors);\nResizeResult\nresize_screen_buffer_without_rewrap(LineBuf *lb, index_type lines, index_type columns, TrackCursor *cursors);\n"
  },
  {
    "path": "kitty/rgb.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom .fast_data_types import Color\n\n\ndef alpha_blend_channel(top_color: int, bottom_color: int, alpha: float) -> int:\n    return int(alpha * top_color + (1 - alpha) * bottom_color)\n\n\ndef alpha_blend(top_color: Color, bottom_color: Color, alpha: float) -> Color:\n    return Color(\n            alpha_blend_channel(top_color.red, bottom_color.red, alpha),\n            alpha_blend_channel(top_color.green, bottom_color.green, alpha),\n            alpha_blend_channel(top_color.blue, bottom_color.blue, alpha)\n    )\n\n\ndef color_from_int(x: int) -> Color:\n    return Color((x >> 16) & 255, (x >> 8) & 255, x & 255)\n\n\ndef color_as_int(x: Color) -> int:\n    return int(x)\n\n\ndef color_as_sharp(x: Color) -> str:\n    return x.as_sharp\n\n\ndef color_as_sgr(x: Color) -> str:\n    return x.as_sgr\n\n\ndef to_color(raw: str, validate: bool = False) -> Color | None:\n    if (val := Color.parse_color(raw)) is None and validate:\n        raise ValueError(f'Invalid color name: {raw!r}')\n    return val\n"
  },
  {
    "path": "kitty/rounded_rect_fragment.glsl",
    "content": "#pragma kitty_include_shader <alpha_blend.glsl>\n#pragma kitty_include_shader <utils.glsl>\n\nin vec2 dimensions;\nout vec4 output_color;\n\nuniform vec4 rect;\nuniform vec2 params;\nuniform vec4 color;\nuniform vec4 background_color;\n\n// Signed distance function for a rounded rectangle\nfloat rounded_rectangle_sdf(vec2 p, vec2 b, float r) {\n    // signed distance field\n    // first term is used for points outside the rectangle\n    vec2 q = abs(p) - b;\n    return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;\n}\n\nvoid main() {\n    vec2 size = rect.ba, origin = rect.xy;\n    float thickness = params[0], corner_radius = params[1];\n    // Position must be relative to the center of the rectangle of (size) located at (origin)\n    vec2 position = gl_FragCoord.xy - size / 2.0 - origin;\n    // Calculate distance to rounded rectangle\n    float dist = rounded_rectangle_sdf(position, size*0.5 - corner_radius, corner_radius);\n\n    // The below is for a filled rounded rect\n    // float alpha = 1.0 - smoothstep(0.0, 1.0, dist);\n    // vec4 ans = color; ans.a *= alpha;\n    // output_color = alpha_blend(ans, background_color);\n\n    // The border is outer - inner rects\n    float outer_edge = -dist, inner_edge = outer_edge - thickness;\n    // Smooth borders (anti-alias)\n    const float step_size = 1.0;  // controls how blurred the aliasing causes the rect to be\n    float alpha = smoothstep(-step_size, step_size, outer_edge) - smoothstep(-step_size, step_size, inner_edge);\n    vec4 ans = color; ans.a *= alpha;\n    // pre-multiplied output\n    output_color = alpha_blend(ans, background_color);\n}\n"
  },
  {
    "path": "kitty/rounded_rect_vertex.glsl",
    "content": "#define left 0\n#define top 1\n#define right 2\n#define bottom 3\n\nconst ivec2 vertex_pos_map[4] = ivec2[4](\n    ivec2(right, top),\n    ivec2(right, bottom),\n    ivec2(left, bottom),\n    ivec2(left, top)\n);\nconst vec4 dest_rect = vec4(-1, 1, 1, -1);\n\nvoid main() {\n    ivec2 pos = vertex_pos_map[gl_VertexID];\n    gl_Position = vec4(dest_rect[pos.x], dest_rect[pos.y], 0, 1);\n}\n"
  },
  {
    "path": "kitty/rowcolumn-diacritics.c",
    "content": "// Unicode data, built from the Unicode Standard 17.0.0\n// Code generated by wcwidth.py, DO NOT EDIT.\n\n#include \"data-types.h\"\n\nSTART_ALLOW_CASE_RANGE\n\nint diacritic_to_num(char_type code) {\n\tswitch (code) {\n\t\tcase 0x305 ... 0x306:\n\t\treturn code - 0x305 + 1;\n\t\tcase 0x30d ... 0x30f:\n\t\treturn code - 0x30d + 2;\n\t\tcase 0x310 ... 0x311:\n\t\treturn code - 0x310 + 4;\n\t\tcase 0x312 ... 0x313:\n\t\treturn code - 0x312 + 5;\n\t\tcase 0x33d ... 0x340:\n\t\treturn code - 0x33d + 6;\n\t\tcase 0x346 ... 0x347:\n\t\treturn code - 0x346 + 9;\n\t\tcase 0x34a ... 0x34d:\n\t\treturn code - 0x34a + 10;\n\t\tcase 0x350 ... 0x353:\n\t\treturn code - 0x350 + 13;\n\t\tcase 0x357 ... 0x358:\n\t\treturn code - 0x357 + 16;\n\t\tcase 0x35b ... 0x35c:\n\t\treturn code - 0x35b + 17;\n\t\tcase 0x363 ... 0x370:\n\t\treturn code - 0x363 + 18;\n\t\tcase 0x483 ... 0x488:\n\t\treturn code - 0x483 + 31;\n\t\tcase 0x592 ... 0x596:\n\t\treturn code - 0x592 + 36;\n\t\tcase 0x597 ... 0x59a:\n\t\treturn code - 0x597 + 40;\n\t\tcase 0x59c ... 0x5a2:\n\t\treturn code - 0x59c + 43;\n\t\tcase 0x5a8 ... 0x5aa:\n\t\treturn code - 0x5a8 + 49;\n\t\tcase 0x5ab ... 0x5ad:\n\t\treturn code - 0x5ab + 51;\n\t\tcase 0x5af ... 0x5b0:\n\t\treturn code - 0x5af + 53;\n\t\tcase 0x5c4 ... 0x5c5:\n\t\treturn code - 0x5c4 + 54;\n\t\tcase 0x610 ... 0x618:\n\t\treturn code - 0x610 + 55;\n\t\tcase 0x657 ... 0x65c:\n\t\treturn code - 0x657 + 63;\n\t\tcase 0x65d ... 0x65f:\n\t\treturn code - 0x65d + 68;\n\t\tcase 0x6d6 ... 0x6dd:\n\t\treturn code - 0x6d6 + 70;\n\t\tcase 0x6df ... 0x6e3:\n\t\treturn code - 0x6df + 77;\n\t\tcase 0x6e4 ... 0x6e5:\n\t\treturn code - 0x6e4 + 81;\n\t\tcase 0x6e7 ... 0x6e9:\n\t\treturn code - 0x6e7 + 82;\n\t\tcase 0x6eb ... 0x6ed:\n\t\treturn code - 0x6eb + 84;\n\t\tcase 0x730 ... 0x731:\n\t\treturn code - 0x730 + 86;\n\t\tcase 0x732 ... 0x734:\n\t\treturn code - 0x732 + 87;\n\t\tcase 0x735 ... 0x737:\n\t\treturn code - 0x735 + 89;\n\t\tcase 0x73a ... 0x73b:\n\t\treturn code - 0x73a + 91;\n\t\tcase 0x73d ... 0x73e:\n\t\treturn code - 0x73d + 92;\n\t\tcase 0x73f ... 0x742:\n\t\treturn code - 0x73f + 93;\n\t\tcase 0x743 ... 0x744:\n\t\treturn code - 0x743 + 96;\n\t\tcase 0x745 ... 0x746:\n\t\treturn code - 0x745 + 97;\n\t\tcase 0x747 ... 0x748:\n\t\treturn code - 0x747 + 98;\n\t\tcase 0x749 ... 0x74b:\n\t\treturn code - 0x749 + 99;\n\t\tcase 0x7eb ... 0x7f2:\n\t\treturn code - 0x7eb + 101;\n\t\tcase 0x7f3 ... 0x7f4:\n\t\treturn code - 0x7f3 + 108;\n\t\tcase 0x816 ... 0x81a:\n\t\treturn code - 0x816 + 109;\n\t\tcase 0x81b ... 0x824:\n\t\treturn code - 0x81b + 113;\n\t\tcase 0x825 ... 0x828:\n\t\treturn code - 0x825 + 122;\n\t\tcase 0x829 ... 0x82e:\n\t\treturn code - 0x829 + 125;\n\t\tcase 0x951 ... 0x952:\n\t\treturn code - 0x951 + 130;\n\t\tcase 0x953 ... 0x955:\n\t\treturn code - 0x953 + 131;\n\t\tcase 0xf82 ... 0xf84:\n\t\treturn code - 0xf82 + 133;\n\t\tcase 0xf86 ... 0xf88:\n\t\treturn code - 0xf86 + 135;\n\t\tcase 0x135d ... 0x1360:\n\t\treturn code - 0x135d + 137;\n\t\tcase 0x17dd ... 0x17de:\n\t\treturn code - 0x17dd + 140;\n\t\tcase 0x193a ... 0x193b:\n\t\treturn code - 0x193a + 141;\n\t\tcase 0x1a17 ... 0x1a18:\n\t\treturn code - 0x1a17 + 142;\n\t\tcase 0x1a75 ... 0x1a7d:\n\t\treturn code - 0x1a75 + 143;\n\t\tcase 0x1b6b ... 0x1b6c:\n\t\treturn code - 0x1b6b + 151;\n\t\tcase 0x1b6d ... 0x1b74:\n\t\treturn code - 0x1b6d + 152;\n\t\tcase 0x1cd0 ... 0x1cd3:\n\t\treturn code - 0x1cd0 + 159;\n\t\tcase 0x1cda ... 0x1cdc:\n\t\treturn code - 0x1cda + 162;\n\t\tcase 0x1ce0 ... 0x1ce1:\n\t\treturn code - 0x1ce0 + 164;\n\t\tcase 0x1dc0 ... 0x1dc2:\n\t\treturn code - 0x1dc0 + 165;\n\t\tcase 0x1dc3 ... 0x1dca:\n\t\treturn code - 0x1dc3 + 167;\n\t\tcase 0x1dcb ... 0x1dcd:\n\t\treturn code - 0x1dcb + 174;\n\t\tcase 0x1dd1 ... 0x1de7:\n\t\treturn code - 0x1dd1 + 176;\n\t\tcase 0x1dfe ... 0x1dff:\n\t\treturn code - 0x1dfe + 198;\n\t\tcase 0x20d0 ... 0x20d2:\n\t\treturn code - 0x20d0 + 199;\n\t\tcase 0x20d4 ... 0x20d8:\n\t\treturn code - 0x20d4 + 201;\n\t\tcase 0x20db ... 0x20dd:\n\t\treturn code - 0x20db + 205;\n\t\tcase 0x20e1 ... 0x20e2:\n\t\treturn code - 0x20e1 + 207;\n\t\tcase 0x20e7 ... 0x20e8:\n\t\treturn code - 0x20e7 + 208;\n\t\tcase 0x20e9 ... 0x20ea:\n\t\treturn code - 0x20e9 + 209;\n\t\tcase 0x20f0 ... 0x20f1:\n\t\treturn code - 0x20f0 + 210;\n\t\tcase 0x2cef ... 0x2cf2:\n\t\treturn code - 0x2cef + 211;\n\t\tcase 0x2de0 ... 0x2e00:\n\t\treturn code - 0x2de0 + 214;\n\t\tcase 0xa66f ... 0xa670:\n\t\treturn code - 0xa66f + 246;\n\t\tcase 0xa67c ... 0xa67e:\n\t\treturn code - 0xa67c + 247;\n\t\tcase 0xa6f0 ... 0xa6f2:\n\t\treturn code - 0xa6f0 + 249;\n\t\tcase 0xa8e0 ... 0xa8f2:\n\t\treturn code - 0xa8e0 + 251;\n\t\tcase 0xaab0 ... 0xaab1:\n\t\treturn code - 0xaab0 + 269;\n\t\tcase 0xaab2 ... 0xaab4:\n\t\treturn code - 0xaab2 + 270;\n\t\tcase 0xaab7 ... 0xaab9:\n\t\treturn code - 0xaab7 + 272;\n\t\tcase 0xaabe ... 0xaac0:\n\t\treturn code - 0xaabe + 274;\n\t\tcase 0xaac1 ... 0xaac2:\n\t\treturn code - 0xaac1 + 276;\n\t\tcase 0xfe20 ... 0xfe27:\n\t\treturn code - 0xfe20 + 277;\n\t\tcase 0x10a0f ... 0x10a10:\n\t\treturn code - 0x10a0f + 284;\n\t\tcase 0x10a38 ... 0x10a39:\n\t\treturn code - 0x10a38 + 285;\n\t\tcase 0x1d185 ... 0x1d18a:\n\t\treturn code - 0x1d185 + 286;\n\t\tcase 0x1d1aa ... 0x1d1ae:\n\t\treturn code - 0x1d1aa + 291;\n\t\tcase 0x1d242 ... 0x1d245:\n\t\treturn code - 0x1d242 + 295;\n\t}\n\treturn 0;\n}\n\nEND_ALLOW_CASE_RANGE\n"
  },
  {
    "path": "kitty/safe-wrappers.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include \"data-types.h\"\n#include <fcntl.h>\n#include <sys/mman.h>\n#include <sys/socket.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nstatic inline int\nsafe_lockf(int fd, int function, off_t size) {\n    while (true) {\n        int ret = lockf(fd, function, size);\n        if (ret != 0 && (errno == EINTR)) continue;\n        return ret;\n    }\n}\n\nstatic inline int\nsafe_connect(int socket_fd, struct sockaddr *addr, socklen_t addrlen) {\n    while (true) {\n        int ret = connect(socket_fd, addr, addrlen);\n        if (ret < 0 && (errno == EINTR || errno == EAGAIN)) continue;\n        return ret;\n    }\n}\n\nstatic inline int\nsafe_bind(int socket_fd, struct sockaddr *addr, socklen_t addrlen) {\n    while (true) {\n        int ret = bind(socket_fd, addr, addrlen);\n        if (ret < 0 && (errno == EINTR || errno == EAGAIN)) continue;\n        return ret;\n    }\n}\n\nstatic inline int\nsafe_accept(int socket_fd, struct sockaddr *addr, socklen_t *addrlen) {\n    while (true) {\n        int ret = accept(socket_fd, addr, addrlen);\n        if (ret < 0 && (errno == EINTR || errno == EAGAIN)) continue;\n        return ret;\n    }\n}\n\nstatic inline int\nsafe_mkstemp(char *template) {\n    while (true) {\n        int fd = mkstemp(template);\n        if (fd == -1 && errno == EINTR) continue;\n        if (fd > -1) {\n            int flags = fcntl(fd, F_GETFD);\n            if (flags > -1) fcntl(fd, F_SETFD, flags | FD_CLOEXEC);\n        }\n        return fd;\n    }\n}\n\nstatic inline int\nsafe_open(const char *path, int flags, mode_t mode) {\n    while (true) {\n        int fd = open(path, flags, mode);\n        if (fd == -1 && errno == EINTR) continue;\n        return fd;\n    }\n}\n\nstatic inline FILE*\nsafe_fopen(const char *path, const char *mode) {\n    while (true) {\n        FILE *f = fopen(path, mode);\n        if (f == NULL && (errno == EINTR || errno == EAGAIN)) continue;\n        return f;\n    }\n}\n\n\nstatic inline int\nsafe_shm_open(const char *path, int flags, mode_t mode) {\n    while (true) {\n        int fd = shm_open(path, flags, mode);\n        if (fd == -1 && errno == EINTR) continue;\n        return fd;\n    }\n}\n\n\nstatic inline void\nsafe_close(int fd, const char* file UNUSED, const int line UNUSED) {\n#if 0\n    printf(\"Closing fd: %d from file: %s line: %d\\n\", fd, file, line);\n#endif\n    while(close(fd) != 0 && errno == EINTR);\n}\n\nstatic inline int\nsafe_dup(int a) {\n    int ret;\n    while((ret = dup(a)) < 0 && errno == EINTR);\n    return ret;\n}\n\nstatic inline int\nsafe_dup2(int a, int b) {\n    int ret;\n    while((ret = dup2(a, b)) < 0 && errno == EINTR);\n    return ret;\n}\n"
  },
  {
    "path": "kitty/screen.c",
    "content": "/*\n * screen.c\n * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define EXTRA_INIT { \\\n    PyModule_AddIntMacro(module, SCROLL_LINE); PyModule_AddIntMacro(module, SCROLL_PAGE); PyModule_AddIntMacro(module, SCROLL_FULL); \\\n    PyModule_AddIntMacro(module, EXTEND_CELL); PyModule_AddIntMacro(module, EXTEND_WORD); PyModule_AddIntMacro(module, EXTEND_LINE); \\\n    PyModule_AddIntMacro(module, SCALE_BITS); PyModule_AddIntMacro(module, WIDTH_BITS); PyModule_AddIntMacro(module, SUBSCALE_BITS); \\\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false; \\\n}\n\n#include \"data-types.h\"\n#include \"control-codes.h\"\n#include \"screen.h\"\n#include \"state.h\"\n#include \"iqsort.h\"\n#include \"fonts.h\"\n#include \"charsets.h\"\n#include \"lineops.h\"\n#include \"hyperlink.h\"\n#include <structmember.h>\n#include <limits.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include \"unicode-data.h\"\n#include \"modes.h\"\n#include \"char-props.h\"\n#include \"wcswidth.h\"\n#include <stdalign.h>\n#include <stdio.h>\n#include \"keys.h\"\n#include \"vt-parser.h\"\n#include \"resize.h\"\n\nstatic const ScreenModes empty_modes = {0, .mDECAWM=true, .mDECTCEM=true, .mDECARM=true};\n\n#define CSI_REP_MAX_REPETITIONS 65535u\n\n// Constructor/destructor {{{\n\nstatic void\nclear_selection(Selections *selections) {\n    selections->in_progress = false;\n    selections->extend_mode = EXTEND_CELL;\n    selections->count = 0;\n}\n\nstatic void\nclear_all_selections(Screen *self) { clear_selection(&self->selections); clear_selection(&self->url_ranges); }\n\n\nstatic void\ninit_tabstops(bool *tabstops, index_type count) {\n    // In terminfo we specify the number of initial tabstops (it) as 8\n    for (unsigned int t=0; t < count; t++) {\n        tabstops[t] = t % 8 == 0 ? true : false;\n    }\n}\n\nstatic bool\ninit_overlay_line(Screen *self, index_type columns, bool keep_active) {\n    PyMem_Free(self->overlay_line.cpu_cells);\n    PyMem_Free(self->overlay_line.gpu_cells);\n    PyMem_Free(self->overlay_line.original_line.cpu_cells);\n    PyMem_Free(self->overlay_line.original_line.gpu_cells);\n    self->overlay_line.cpu_cells = PyMem_Calloc(columns, sizeof(CPUCell));\n    self->overlay_line.gpu_cells = PyMem_Calloc(columns, sizeof(GPUCell));\n    self->overlay_line.original_line.cpu_cells = PyMem_Calloc(columns, sizeof(CPUCell));\n    self->overlay_line.original_line.gpu_cells = PyMem_Calloc(columns, sizeof(GPUCell));\n    if (!self->overlay_line.cpu_cells || !self->overlay_line.gpu_cells ||\n        !self->overlay_line.original_line.cpu_cells || !self->overlay_line.original_line.gpu_cells) {\n        PyErr_NoMemory(); return false;\n    }\n    if (!keep_active) {\n        self->overlay_line.is_active = false;\n        self->overlay_line.xnum = 0;\n    }\n    self->overlay_line.is_dirty = true;\n    self->overlay_line.ynum = 0;\n    self->overlay_line.xstart = 0;\n    self->overlay_line.cursor_x = 0;\n    self->overlay_line.last_ime_pos.x = 0;\n    self->overlay_line.last_ime_pos.y = 0;\n\n    return true;\n}\n\nstatic void deactivate_overlay_line(Screen *self);\nstatic void update_overlay_position(Screen *self);\nstatic void render_overlay_line(Screen *self, Line *line, FONTS_DATA_HANDLE fonts_data);\nstatic void update_overlay_line_data(Screen *self, uint8_t *data);\n\n#define CALLBACK(...) \\\n    if (self->callbacks != Py_None) { \\\n        PyObject *callback_ret = PyObject_CallMethod(self->callbacks, __VA_ARGS__); \\\n        if (callback_ret == NULL) PyErr_Print(); else Py_DECREF(callback_ret); \\\n    }\n\nstatic PyObject*\nnew_screen_object(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {\n    Screen *self;\n    int ret = 0;\n    PyObject *callbacks = Py_None, *test_child = Py_None;\n    unsigned int columns=80, lines=24, scrollback=0, cell_width=10, cell_height=20;\n    id_type window_id=0;\n    if (!PyArg_ParseTuple(args, \"|OIIIIIKO\", &callbacks, &lines, &columns, &scrollback, &cell_width, &cell_height, &window_id, &test_child)) return NULL;\n\n    self = (Screen *)type->tp_alloc(type, 0);\n    if (self != NULL) {\n        if ((ret = pthread_mutex_init(&self->write_buf_lock, NULL)) != 0) {\n            Py_CLEAR(self); PyErr_Format(PyExc_RuntimeError, \"Failed to create Screen write_buf_lock mutex: %s\", strerror(ret));\n            return NULL;\n        }\n        self->vt_parser = alloc_vt_parser(window_id);\n        if (self->vt_parser == NULL) { Py_CLEAR(self); return PyErr_NoMemory(); }\n        self->text_cache = tc_alloc(); if (!self->text_cache) { Py_CLEAR(self); return PyErr_NoMemory(); }\n        self->reload_all_gpu_data = true;\n        self->cell_size.width = cell_width; self->cell_size.height = cell_height;\n        self->columns = columns; self->lines = lines;\n        self->write_buf_sz = BUFSIZ;\n        self->write_buf = PyMem_RawMalloc(self->write_buf_sz);\n        if (self->write_buf == NULL) { Py_CLEAR(self); return PyErr_NoMemory(); }\n        self->window_id = window_id;\n        self->modes = empty_modes;\n        self->saved_modes = empty_modes;\n        self->is_dirty = true;\n        self->scroll_changed = false;\n        self->margin_top = 0; self->margin_bottom = self->lines - 1;\n        self->history_line_added_count = 0;\n        reset_vt_parser(self->vt_parser);\n        self->callbacks = callbacks; Py_INCREF(callbacks);\n        self->test_child = test_child; Py_INCREF(test_child);\n        self->cursor = alloc_cursor();\n        self->color_profile = alloc_color_profile();\n        self->main_linebuf = alloc_linebuf(lines, columns, self->text_cache); self->alt_linebuf = alloc_linebuf(lines, columns, self->text_cache);\n        self->linebuf = self->main_linebuf;\n        self->historybuf = alloc_historybuf(MAX(scrollback, lines), columns, OPT(scrollback_pager_history_size), self->text_cache);\n        self->main_grman = grman_alloc(false);\n        self->alt_grman = grman_alloc(false);\n        self->active_hyperlink_id = 0;\n\n        self->grman = self->main_grman;\n        self->disable_ligatures = OPT(disable_ligatures);\n        self->main_tabstops = PyMem_Calloc(2 * self->columns, sizeof(bool));\n        self->lc = alloc_list_of_chars();\n        if (\n            self->cursor == NULL || self->main_linebuf == NULL || self->alt_linebuf == NULL ||\n            self->main_tabstops == NULL || self->historybuf == NULL || self->main_grman == NULL ||\n            self->alt_grman == NULL || self->color_profile == NULL || self->lc == NULL\n        ) {\n            Py_CLEAR(self); return NULL;\n        }\n        grman_set_window_id(self->main_grman, self->window_id);\n        grman_set_window_id(self->alt_grman, self->window_id);\n        self->alt_tabstops = self->main_tabstops + self->columns;\n        self->tabstops = self->main_tabstops;\n        init_tabstops(self->main_tabstops, self->columns);\n        init_tabstops(self->alt_tabstops, self->columns);\n        self->key_encoding_flags = self->main_key_encoding_flags;\n        if (!init_overlay_line(self, self->columns, false)) { Py_CLEAR(self); return NULL; }\n        self->hyperlink_pool = alloc_hyperlink_pool();\n        if (!self->hyperlink_pool) { Py_CLEAR(self); return PyErr_NoMemory(); }\n        self->as_ansi_buf.hyperlink_pool = self->hyperlink_pool;\n    }\n    return (PyObject*) self;\n}\n\nstatic Line* range_line_(Screen *self, int y);\n\nvoid\nscreen_reset(Screen *self) {\n    screen_pause_rendering(self, false, 0);\n    self->extra_cursors.count = 0; zero_at_ptr(&self->extra_cursors.color); self->extra_cursors.dirty = true;\n    self->main_pointer_shape_stack.count = 0; self->alternate_pointer_shape_stack.count = 0;\n    if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self, true, true);\n    if (screen_is_overlay_active(self)) {\n        deactivate_overlay_line(self);\n        // Cancel IME composition\n        update_ime_position_for_window(self->window_id, false, -1);\n    }\n    Py_CLEAR(self->last_reported_cwd);\n    self->cursor_render_info.render_even_when_unfocused = false;\n    memset(self->main_key_encoding_flags, 0, sizeof(self->main_key_encoding_flags));\n    memset(self->alt_key_encoding_flags, 0, sizeof(self->alt_key_encoding_flags));\n    self->display_window_char = 0;\n    self->prompt_settings.val = 0;\n    self->last_graphic_char = 0;\n    self->main_savepoint.is_valid = false;\n    self->alt_savepoint.is_valid = false;\n    linebuf_clear(self->linebuf, BLANK_CHAR);\n    historybuf_clear(self->historybuf);\n    clear_hyperlink_pool(self->hyperlink_pool);\n    grman_clear(self->main_grman, false, self->cell_size);  // dont delete images in scrollback\n    grman_clear(self->alt_grman, true, self->cell_size);\n    self->modes = empty_modes;\n    self->saved_modes = empty_modes;\n    self->active_hyperlink_id = 0;\n    zero_at_ptr(&self->color_profile->overridden);\n    reset_vt_parser(self->vt_parser);\n    zero_at_ptr(&self->charset);\n    self->margin_top = 0; self->margin_bottom = self->lines - 1;\n    screen_normal_keypad_mode(self);\n    init_tabstops(self->main_tabstops, self->columns);\n    init_tabstops(self->alt_tabstops, self->columns);\n    cursor_reset(self->cursor);\n    self->is_dirty = true;\n    clear_all_selections(self);\n    screen_cursor_position(self, 1, 1);\n    set_dynamic_color(self, 111, NULL);  // does default_bg_changed processing\n    colorprofile_reset(self->color_profile);\n    CALLBACK(\"on_reset\", NULL)\n}\n\nvoid\nscreen_dirty_sprite_positions(Screen *self) {\n    self->is_dirty = true;\n    for (index_type i = 0; i < self->lines; i++) {\n        linebuf_mark_line_dirty(self->main_linebuf, i);\n        linebuf_mark_line_dirty(self->alt_linebuf, i);\n    }\n    for (index_type i = 0; i < self->historybuf->count; i++) historybuf_mark_line_dirty(self->historybuf, i);\n}\n\ntypedef struct CursorTrack {\n    index_type num_content_lines;\n    bool is_beyond_content;\n    struct { index_type x, y; } before;\n    struct { index_type x, y; } after;\n    struct { index_type x, y; } temp;\n} CursorTrack;\n\nstatic bool\nrewrap(Screen *screen, unsigned int lines, unsigned int columns, index_type *nclb, index_type *ncla, CursorTrack *cursor, CursorTrack *main_saved_cursor, CursorTrack *alt_saved_cursor, bool main_is_active) {\n    TrackCursor cursors[3];\n    cursors[2].is_sentinel = true;\n    cursors[0] = (TrackCursor){.x=main_saved_cursor->before.x, .y=main_saved_cursor->before.y};\n    if (main_is_active) cursors[1] = (TrackCursor){.x=cursor->before.x, .y=cursor->before.y};\n    else cursors[1].is_sentinel = true;\n    ResizeResult mr = resize_screen_buffers(screen->main_linebuf, screen->historybuf, lines, columns, &screen->as_ansi_buf, cursors);\n    if (!mr.ok) { PyErr_NoMemory(); return false; }\n    main_saved_cursor->temp.x = cursors[0].dest_x; main_saved_cursor->temp.y = cursors[0].dest_y;\n    if (main_is_active) { cursor->temp.x = cursors[1].dest_x; cursor->temp.y = cursors[1].dest_y; }\n\n    cursors[0] = (TrackCursor){.x=alt_saved_cursor->before.x, .y=alt_saved_cursor->before.y};\n    if (!main_is_active) cursors[1] = (TrackCursor){.x=cursor->before.x, .y=cursor->before.y};\n    else cursors[1].is_sentinel = true;\n    ResizeResult ar = resize_screen_buffer_without_rewrap(screen->alt_linebuf, lines, columns, cursors);\n    if (!ar.ok) {\n        Py_DecRef((PyObject*)ar.lb); PyErr_NoMemory(); return false;\n    }\n    alt_saved_cursor->temp.x = cursors[0].dest_x; alt_saved_cursor->temp.y = cursors[0].dest_y;\n    if (!main_is_active) { cursor->temp.x = cursors[1].dest_x; cursor->temp.y = cursors[1].dest_y; }\n    Py_CLEAR(screen->main_linebuf); Py_CLEAR(screen->alt_linebuf); Py_CLEAR(screen->historybuf);\n    screen->main_linebuf = mr.lb; screen->historybuf = mr.hb; screen->alt_linebuf = ar.lb;\n    screen->linebuf = main_is_active ? screen->main_linebuf : screen->alt_linebuf;\n    if (main_is_active) {\n        *nclb = mr.num_content_lines_before; *ncla = mr.num_content_lines_after;\n    } else {\n        *nclb = ar.num_content_lines_before; *ncla = ar.num_content_lines_after;\n    }\n    return true;\n}\n\nstatic bool\nis_selection_empty(const Selection *s) {\n    int start_y = (int)s->start.y - (int)s->start_scrolled_by, end_y = (int)s->end.y - (int)s->end_scrolled_by;\n    return s->start.x == s->end.x && s->start.in_left_half_of_cell == s->end.in_left_half_of_cell && start_y == end_y;\n}\n\nstatic bool\nselection_intersects_screen_lines(const Selections *selections, int a, int b) {\n    if (a > b) SWAP(a, b);\n    for (size_t i = 0; i < selections->count; i++) {\n        const Selection *s = selections->items + i;\n        if (!is_selection_empty(s)) {\n            int start = (int)s->start.y - s->start_scrolled_by;\n            int end = (int)s->end.y - s->end_scrolled_by;\n            int top = MIN(start, end);\n            int bottom = MAX(start, end);\n            if ((top <= a && bottom >= a) || (top >= a && top <= b)) return true;\n        }\n    }\n    return false;\n}\n\n\nstatic void\nindex_selection(const Screen *self, Selections *selections, bool up, index_type top, index_type bottom) {\n    const bool needs_special_handling = self->linebuf == self->alt_linebuf && (top > 0 || bottom < self->lines - 1);\n    for (size_t i = 0; i < selections->count; i++) {\n        Selection *s = selections->items + i;\n        if (needs_special_handling) {\n            if (is_selection_empty(s)) continue;\n            int start = (int)s->start.y - s->start_scrolled_by;\n            int end = (int)s->end.y - s->end_scrolled_by;\n            int stop = MIN(start, end);\n            int sbottom = MAX(start, end);\n            if (stop < (int)top) {\n                if (sbottom < (int)top) continue;\n                clear_selection(selections); return;\n            } else {\n                if (stop > (int)bottom) continue;\n                if (sbottom > (int)bottom) { clear_selection(selections); return; }\n            }\n        }\n        if (up) {\n            if (s->start.y == 0) s->start_scrolled_by += 1;\n            else {\n                s->start.y--;\n                if (s->input_start.y) s->input_start.y--;\n                if (s->input_current.y) s->input_current.y--;\n                if (s->initial_extent.start.y) s->initial_extent.start.y--;\n                if (s->initial_extent.end.y) s->initial_extent.end.y--;\n            }\n            if (s->end.y == 0) s->end_scrolled_by += 1;\n            else s->end.y--;\n        } else {\n            if (s->start.y >= self->lines - 1) s->start_scrolled_by -= 1;\n            else {\n                s->start.y++;\n                if (s->input_start.y < self->lines - 1) s->input_start.y++;\n                if (s->input_current.y < self->lines - 1) s->input_current.y++;\n            }\n            if (s->end.y >= self->lines - 1) s->end_scrolled_by -= 1;\n            else s->end.y++;\n        }\n    }\n}\n\n\n#define INDEX_GRAPHICS(amtv) { \\\n    bool is_main = self->linebuf == self->main_linebuf; \\\n    static ScrollData s; \\\n    s.amt = amtv; s.limit = is_main ? -self->historybuf->ynum : 0; \\\n    s.has_margins = self->margin_top != 0 || self->margin_bottom != self->lines - 1; \\\n    s.margin_top = top; s.margin_bottom = bottom; \\\n    grman_scroll_images(self->grman, &s, self->cell_size); \\\n}\n\n\n#define INDEX_DOWN \\\n    linebuf_reverse_index(self->linebuf, top, bottom); \\\n    linebuf_clear_line(self->linebuf, top, true); \\\n    if (self->linebuf == self->main_linebuf && self->last_visited_prompt.is_set) { \\\n        if (self->last_visited_prompt.scrolled_by > 0) self->last_visited_prompt.scrolled_by--; \\\n        else if(self->last_visited_prompt.y < self->lines - 1) self->last_visited_prompt.y++; \\\n        else self->last_visited_prompt.is_set = false; \\\n    } \\\n    INDEX_GRAPHICS(1) \\\n    self->is_dirty = true; \\\n    index_selection(self, &self->selections, false, top, bottom); \\\n    clear_selection(&self->url_ranges);\n\n\nstatic void\nnuke_in_line(CPUCell *cp, GPUCell *gp, index_type start, index_type x_limit, char_type ch) {\n    for (index_type x = start; x < x_limit; x++) {\n        cell_set_char(cp + x, ch); cp[x].is_multicell = false;\n        clear_sprite_position(gp[x]);\n    }\n}\n\nstatic void\nnuke_multicell_char_at(Screen *self, index_type x_, index_type y_, bool replace_with_spaces) {\n    CPUCell *cp; GPUCell *gp;\n    linebuf_init_cells(self->linebuf, y_, &cp, &gp);\n    index_type num_lines_above = cp[x_].y;\n    index_type y_max_limit = MIN(self->lines, y_ + cp[x_].scale - num_lines_above);\n    while (cp[x_].x && x_ > 0) x_--;\n    index_type x_limit = MIN(self->columns, x_ + mcd_x_limit(&cp[x_]));\n    char_type ch = replace_with_spaces ? ' ' : 0;\n    for (index_type y = y_; y < y_max_limit; y++) {\n        linebuf_init_cells(self->linebuf, y, &cp, &gp);\n        nuke_in_line(cp, gp, x_, x_limit, ch); linebuf_mark_line_dirty(self->linebuf, y);\n    }\n    int y_min_limit = -1;\n    if (self->linebuf == self->main_linebuf) y_min_limit = -(self->historybuf->count + 1);\n    for (int y = (int)y_ - 1; y > y_min_limit && num_lines_above; y--, num_lines_above--) {\n        Line *line = range_line_(self, y); cp = line->cpu_cells; gp = line->gpu_cells;\n        nuke_in_line(cp, gp, x_, x_limit, ch);\n        if (y > -1) linebuf_mark_line_dirty(self->linebuf, y);\n        else historybuf_mark_line_dirty(self->historybuf, -(y + 1));\n    }\n    self->is_dirty = true;\n}\n\nstatic void\nnuke_multiline_char_intersecting_with(Screen *self, index_type x_start, index_type x_limit, index_type y_start, index_type y_limit, bool replace_with_spaces) {\n    for (index_type y = y_start; y < y_limit; y++) {\n        CPUCell *cp; GPUCell *gp;\n        linebuf_init_cells(self->linebuf, y, &cp, &gp);\n        for (index_type x = x_start; x < x_limit; x++) {\n            if (cp[x].is_multicell && cp[x].scale > 1) nuke_multicell_char_at(self, x, y, replace_with_spaces);\n        }\n    }\n}\n\nstatic void\nnuke_multicell_char_intersecting_with(Screen *self, index_type x_start, index_type x_limit, index_type y_start, index_type y_limit, bool replace_with_spaces) {\n    for (index_type y = y_start; y < y_limit; y++) {\n        CPUCell *cp; GPUCell *gp;\n        linebuf_init_cells(self->linebuf, y, &cp, &gp);\n        for (index_type x = x_start; x < x_limit; x++) {\n            if (cp[x].is_multicell) nuke_multicell_char_at(self, x, y, replace_with_spaces);\n        }\n    }\n}\n\n\nstatic void\nnuke_split_multicell_char_at_left_boundary(Screen *self, index_type x, index_type y, bool replace_with_spaces) {\n    CPUCell *cp = linebuf_cpu_cells_for_line(self->linebuf, y);\n    if (cp[x].is_multicell && cp[x].x) {\n        nuke_multicell_char_at(self, x, y, replace_with_spaces);  // remove split multicell char at left edge\n    }\n}\n\nstatic void\nnuke_split_multicell_char_at_right_boundary(Screen *self, index_type x, index_type y, bool replace_with_spaces) {\n    CPUCell *cp = linebuf_cpu_cells_for_line(self->linebuf, y);\n    CPUCell *c = cp + x;\n    if (c->is_multicell) {\n        unsigned max_x = mcd_x_limit(c) - 1;\n        if (c->x < max_x) {\n            nuke_multicell_char_at(self, x, y, replace_with_spaces);\n        }\n    }\n}\n\nstatic void\nnuke_incomplete_single_line_multicell_chars_in_range(\n    Screen *self, index_type start, index_type limit, index_type y, bool replace_with_spaces\n) {\n    CPUCell *cpu_cells; GPUCell *gpu_cells;\n    linebuf_init_cells(self->linebuf, y, &cpu_cells, &gpu_cells);\n    for (index_type x = start; x < limit; x++) {\n        if (cpu_cells[x].is_multicell) {\n            index_type mcd_x_limit = x + cpu_cells[x].width - cpu_cells[x].x;\n            if (cpu_cells[x].x || mcd_x_limit > limit) nuke_in_line(cpu_cells, gpu_cells, x, MIN(mcd_x_limit, limit), replace_with_spaces ? ' ': 0);\n            x = mcd_x_limit - 1;\n        }\n    }\n}\n\n\nstatic index_type\nprevent_current_prompt_from_rewrapping(Screen *self, LineBuf *prompt_copy, index_type *num_of_prompt_lines_above_cursor) {\n    index_type num_of_prompt_lines = 0; *num_of_prompt_lines_above_cursor = 0;\n    if (!self->prompt_settings.redraws_prompts_at_all) return num_of_prompt_lines;\n    int y = self->cursor->y;\n    while (y >= 0) {\n        linebuf_init_line(self->main_linebuf, y);\n        Line *line = self->linebuf->line;\n        switch (line->attrs.prompt_kind) {\n            case UNKNOWN_PROMPT_KIND:\n                break;\n            case PROMPT_START:\n            case SECONDARY_PROMPT:\n                goto found;\n                break;\n            case OUTPUT_START:\n                return num_of_prompt_lines;\n        }\n        y--;\n    }\nfound:\n    if (y < 0) return num_of_prompt_lines;\n    // we have identified a prompt at which the cursor is present, the shell\n    // will redraw this prompt. However when doing so it gets confused if the\n    // cursor vertical position relative to the first prompt line changes. This\n    // can easily be seen for instance in zsh when a right side prompt is used\n    // so when resizing, simply blank all lines after the current\n    // prompt and trust the shell to redraw them.\n    LineBuf *orig = self->linebuf; self->linebuf = self->main_linebuf;\n    // technically only need to nuke partial multichar cells but since we dont\n    // know what the shell will do in terms of clearing, best to be safe and\n    // nuke all\n    nuke_multiline_char_intersecting_with(self, 0, self->columns, y, self->main_linebuf->ynum, true);\n    self->linebuf = orig;\n    for (; y < (int)self->main_linebuf->ynum; y++) {\n        linebuf_init_line(self->main_linebuf, y);\n        linebuf_copy_line_to(prompt_copy, self->main_linebuf->line, num_of_prompt_lines++);\n        linebuf_clear_line(self->main_linebuf, y, false);\n        if (y <= (int)self->cursor->y) {\n            linebuf_init_line(self->main_linebuf, y);\n            // this is needed because screen_resize() checks to see if the cursor is beyond the content,\n            // so insert some fake content\n            cell_set_char(self->main_linebuf->line->cpu_cells, ' ');\n            if (y < (int)self->cursor->y) (*num_of_prompt_lines_above_cursor)++;\n        }\n    }\n    return num_of_prompt_lines;\n}\n\nstatic bool\nlinebuf_is_line_continued(LineBuf *linebuf, index_type y) {\n    return y ? linebuf_line_ends_with_continuation(linebuf, y - 1) : false;\n}\n\nstatic bool\npreserve_blank_output_start_line(Cursor *cursor, LineBuf *linebuf) {\n    if (cursor->x == 0 && cursor->y < linebuf->ynum && !linebuf_is_line_continued(linebuf, cursor->y)) {\n        linebuf_init_line(linebuf, cursor->y);\n        if (!cell_has_text(linebuf->line->cpu_cells)) {\n            // we have a blank output start line, we need it to be preserved by\n            // reflow, so insert a dummy char\n            cell_set_char(linebuf->line->cpu_cells + cursor->x++, '<');\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic void\nremove_blank_output_line_reservation_marker(Cursor *cursor, LineBuf *linebuf) {\n    if (cursor->y < linebuf->ynum) {\n        linebuf_init_line(linebuf, cursor->y);\n        cell_set_char(linebuf->line->cpu_cells, 0);\n        cursor->x = 0;\n    }\n}\n\nstatic bool\nscreen_resize(Screen *self, unsigned int lines, unsigned int columns) {\n    screen_pause_rendering(self, false, 0);\n    lines = MAX(1u, lines); columns = MAX(1u, columns);\n\n    bool is_main = self->linebuf == self->main_linebuf;\n    index_type num_content_lines_before, num_content_lines_after;\n    bool main_has_blank_line = false, alt_has_blank_line = false;\n    if (is_main) {\n        main_has_blank_line = preserve_blank_output_start_line(self->cursor, self->linebuf);\n        if (self->alt_savepoint.is_valid) alt_has_blank_line = preserve_blank_output_start_line(&self->alt_savepoint.cursor, self->alt_linebuf);\n    } else {\n        if (self->main_savepoint.is_valid) main_has_blank_line = preserve_blank_output_start_line(&self->main_savepoint.cursor, self->main_linebuf);\n        alt_has_blank_line = preserve_blank_output_start_line(self->cursor, self->linebuf);\n    }\n    unsigned int lines_after_cursor_before_resize = self->lines - self->cursor->y;\n    CursorTrack cursor = {.before = {self->cursor->x, self->cursor->y}};\n    CursorTrack main_saved_cursor = {.before = {self->main_savepoint.cursor.x, self->main_savepoint.cursor.y}};\n    CursorTrack alt_saved_cursor = {.before = {self->alt_savepoint.cursor.x, self->alt_savepoint.cursor.y}};\n#define setup_cursor(which) { \\\n    which.after.x = which.temp.x; which.after.y = which.temp.y; \\\n    which.is_beyond_content = num_content_lines_before > 0 && self->cursor->y >= num_content_lines_before; \\\n    which.num_content_lines = num_content_lines_after; \\\n}\n    // Resize overlay and blank lines\n    if (!init_overlay_line(self, columns, true)) return false;\n\n    // Resize main linebuf\n    RAII_PyObject(prompt_copy, NULL);\n    index_type num_of_prompt_lines = 0, num_of_prompt_lines_above_cursor = 0;\n    if (is_main) {\n        prompt_copy = (PyObject*)alloc_linebuf(self->lines, self->columns, self->text_cache);\n        num_of_prompt_lines = prevent_current_prompt_from_rewrapping(self, (LineBuf*)prompt_copy, &num_of_prompt_lines_above_cursor);\n    }\n    if (!rewrap(self, lines, columns, &num_content_lines_before, &num_content_lines_after, &cursor, &main_saved_cursor, &alt_saved_cursor, is_main)) return false;\n    setup_cursor(cursor);\n    /* printf(\"old_cursor: (%u, %u) new_cursor: (%u, %u) beyond_content: %d\\n\", self->cursor->x, self->cursor->y, cursor.after.x, cursor.after.y, cursor.is_beyond_content); */\n    setup_cursor(main_saved_cursor);\n    grman_remove_all_cell_images(self->main_grman);\n    grman_resize(self->main_grman, self->lines, lines, self->columns, columns, num_content_lines_before, num_content_lines_after);\n    setup_cursor(alt_saved_cursor);\n    grman_remove_all_cell_images(self->alt_grman);\n    grman_resize(self->alt_grman, self->lines, lines, self->columns, columns, num_content_lines_before, num_content_lines_after);\n#undef setup_cursor\n    /* printf(\"\\nold_size: (%u, %u) new_size: (%u, %u)\\n\", self->columns, self->lines, columns, lines); */\n    self->lines = lines; self->columns = columns;\n    self->margin_top = 0; self->margin_bottom = self->lines - 1;\n\n    PyMem_Free(self->main_tabstops);\n    self->main_tabstops = PyMem_Calloc(2*self->columns, sizeof(bool));\n    if (self->main_tabstops == NULL) { PyErr_NoMemory(); return false; }\n    self->alt_tabstops = self->main_tabstops + self->columns;\n    self->tabstops = self->main_tabstops;\n    init_tabstops(self->main_tabstops, self->columns);\n    init_tabstops(self->alt_tabstops, self->columns);\n    self->is_dirty = true;\n    clear_all_selections(self);\n    self->last_visited_prompt.is_set = false;\n#define S(c, w) c->x = MIN(w.after.x, self->columns - 1); c->y = MIN(w.after.y, self->lines - 1);\n    S(self->cursor, cursor);\n    S((&(self->main_savepoint.cursor)), main_saved_cursor);\n    S((&(self->alt_savepoint.cursor)), alt_saved_cursor);\n#undef S\n    if (cursor.is_beyond_content) {\n        self->cursor->y = cursor.num_content_lines;\n        if (self->cursor->y >= self->lines) { self->cursor->y = self->lines - 1; screen_index(self); }\n    }\n    if (is_main && OPT(scrollback_fill_enlarged_window)) {\n        const unsigned int top = 0, bottom = self->lines-1;\n        Savepoint *sp = is_main ? &self->main_savepoint : &self->alt_savepoint;\n        while (self->cursor->y + 1 < self->lines && self->lines - self->cursor->y > lines_after_cursor_before_resize) {\n            if (!historybuf_pop_line(self->historybuf, self->alt_linebuf->line)) break;\n            INDEX_DOWN;\n            linebuf_copy_line_to(self->main_linebuf, self->alt_linebuf->line, 0);\n            self->cursor->y++;\n            sp->cursor.y = MIN(sp->cursor.y + 1, self->lines - 1);\n        }\n    }\n    if (main_has_blank_line) remove_blank_output_line_reservation_marker(is_main ? self->cursor : &self->main_savepoint.cursor, self->main_linebuf);\n    if (alt_has_blank_line) remove_blank_output_line_reservation_marker(is_main ? &self->alt_savepoint.cursor : self->cursor, self->alt_linebuf);\n    if (num_of_prompt_lines) {\n        // Copy the old prompt lines without any reflow this prevents\n        // flickering of prompt during resize. The flicker is caused by the\n        // prompt being first cleared by kitty then sometime later redrawn by\n        // the shell.\n        LineBuf *src = (LineBuf*)prompt_copy;\n        for (index_type\n                src_line = 0,\n                y = num_of_prompt_lines_above_cursor <= self->cursor->y ? self->cursor->y - num_of_prompt_lines_above_cursor : 0;\n\n                src_line < num_of_prompt_lines && y < self->lines;\n\n                y++, src_line++\n        ) {\n            linebuf_init_line(src, src_line);\n            linebuf_copy_line_to(self->main_linebuf, src->line, y);\n        }\n    }\n    return true;\n}\n\nvoid\nscreen_rescale_images(Screen *self) {\n    grman_remove_all_cell_images(self->main_grman);\n    grman_remove_all_cell_images(self->alt_grman);\n    grman_rescale(self->main_grman, self->cell_size);\n    grman_rescale(self->alt_grman, self->cell_size);\n}\n\n\nstatic PyObject*\nreset_callbacks(Screen *self, PyObject *a UNUSED) {\n    Py_CLEAR(self->callbacks);\n    self->callbacks = Py_None;\n    Py_INCREF(self->callbacks);\n    Py_RETURN_NONE;\n}\n\nstatic void\ndealloc(Screen* self) {\n    pthread_mutex_destroy(&self->write_buf_lock);\n    free_vt_parser(self->vt_parser); self->vt_parser = NULL;\n    self->text_cache = tc_decref(self->text_cache);\n    Py_CLEAR(self->main_grman);\n    Py_CLEAR(self->alt_grman);\n    Py_CLEAR(self->last_reported_cwd);\n    PyMem_RawFree(self->write_buf);\n    Py_CLEAR(self->callbacks);\n    Py_CLEAR(self->test_child);\n    Py_CLEAR(self->cursor);\n    Py_CLEAR(self->main_linebuf);\n    Py_CLEAR(self->alt_linebuf);\n    Py_CLEAR(self->historybuf);\n    Py_CLEAR(self->color_profile);\n    Py_CLEAR(self->marker);\n    PyMem_Free(self->overlay_line.cpu_cells);\n    PyMem_Free(self->overlay_line.gpu_cells);\n    PyMem_Free(self->overlay_line.original_line.cpu_cells);\n    PyMem_Free(self->overlay_line.original_line.gpu_cells);\n    Py_CLEAR(self->overlay_line.overlay_text);\n    PyMem_Free(self->main_tabstops);\n    Py_CLEAR(self->paused_rendering.linebuf);\n    Py_CLEAR(self->paused_rendering.grman);\n    free(self->selections.items);\n    free(self->url_ranges.items);\n    free(self->paused_rendering.url_ranges.items);\n    free(self->paused_rendering.selections.items);\n    free_hyperlink_pool(self->hyperlink_pool);\n    free(self->as_ansi_buf.buf);\n    free(self->last_rendered_window_char.canvas);\n    free(self->extra_cursors.locations); free(self->paused_rendering.extra_cursors.locations);\n    if (self->lc) { cleanup_list_of_chars(self->lc); free(self->lc); self->lc = NULL; }\n    Py_TYPE(self)->tp_free((PyObject*)self);\n} // }}}\n\n// Draw text {{{\ntypedef struct text_loop_state {\n    bool image_placeholder_marked;\n    const CPUCell cc; const GPUCell g;\n    CPUCell *cp; GPUCell *gp;\n    GraphemeSegmentationResult seg;\n    struct {\n        index_type x, y; CPUCell *cc;\n    } prev;\n} text_loop_state;\n\nstatic void\ncontinue_to_next_line(Screen *self) {\n    linebuf_set_last_char_as_continuation(self->linebuf, self->cursor->y, true);\n    self->cursor->x = 0;\n    screen_linefeed(self);\n}\n\nstatic bool\nselection_has_screen_line(const Selections *selections, const int y) {\n    for (size_t i = 0; i < selections->count; i++) {\n        const Selection *s = selections->items + i;\n        if (!is_selection_empty(s)) {\n            int start = (int)s->start.y - s->start_scrolled_by;\n            int end = (int)s->end.y - s->end_scrolled_by;\n            int top = MIN(start, end);\n            int bottom = MAX(start, end);\n            if (top <= y && y <= bottom) return true;\n        }\n    }\n    return false;\n}\n\nstatic void\nclear_intersecting_selections(Screen *self, index_type y) {\n    if (selection_has_screen_line(&self->selections, y)) clear_selection(&self->selections);\n    if (selection_has_screen_line(&self->url_ranges, y)) clear_selection(&self->url_ranges);\n}\n\nstatic void\ninit_prev_cell(Screen *self, text_loop_state *s) {\n    zero_at_ptr(&s->prev);\n    if (self->cursor->x) {\n        s->prev.y = self->cursor->y;\n        s->prev.x = self->cursor->x - 1;\n        s->prev.cc = linebuf_cpu_cell_at(self->linebuf, s->prev.x, s->prev.y);\n    } else if (self->cursor->y) {\n        s->prev.y = self->cursor->y - 1;\n        s->prev.x = self->columns - 1;\n        s->prev.cc = linebuf_cpu_cell_at(self->linebuf, s->prev.x, s->prev.y);\n        if (!s->prev.cc->next_char_was_wrapped) s->prev.cc = NULL;\n    }\n}\nstatic void\ninit_segmentation_state(Screen *self, text_loop_state *s) {\n    init_prev_cell(self, s);\n    grapheme_segmentation_reset(&s->seg);\n    if (s->prev.cc) {\n        text_in_cell(s->prev.cc, self->text_cache, self->lc);\n        for (index_type i = 0; i < self->lc->count; i++) s->seg = grapheme_segmentation_step(s->seg, char_props_for(self->lc->chars[i]));\n    }\n}\n\nstatic void\ninit_text_loop_line(Screen *self, text_loop_state *s) {\n    linebuf_init_cells(self->linebuf, self->cursor->y, &s->cp, &s->gp);\n    clear_intersecting_selections(self, self->cursor->y);\n    linebuf_mark_line_dirty(self->linebuf, self->cursor->y);\n    s->image_placeholder_marked = false;\n    init_segmentation_state(self, s);\n}\n\nstatic void\nzero_cells(text_loop_state *s, CPUCell *c, GPUCell *g) { *c = s->cc; *g = s->g; }\n\ntypedef Line*(linefunc_t)(Screen*, int);\n\nstatic void\ninit_line_(Screen *self, index_type y, Line *line) {\n    linebuf_init_line_at(self->linebuf, y, line);\n}\n\n\nstatic Line*\ninit_line(Screen *self, index_type y) {\n    init_line_(self, y, self->linebuf->line);\n    return self->linebuf->line;\n}\n\nstatic void\nvisual_line(Screen *self, int y_, Line *line) {\n    index_type y = MAX(0, y_);\n    if (self->scrolled_by) {\n        if (y < self->scrolled_by) {\n            historybuf_init_line(self->historybuf, self->scrolled_by - 1 - y, line);\n            return;\n        }\n        y -= self->scrolled_by;\n    }\n    init_line_(self, y, line);\n}\n\nstatic Line*\nvisual_line_(Screen *self, int y_) {\n    index_type y = MAX(0, y_);\n    if (self->scrolled_by) {\n        if (y < self->scrolled_by) {\n            historybuf_init_line(self->historybuf, self->scrolled_by - 1 - y, self->historybuf->line);\n            return self->historybuf->line;\n        }\n        y -= self->scrolled_by;\n    }\n    return init_line(self, y);\n}\n\nstatic bool\nvisual_line_is_continued(Screen *self, int y_) {\n    index_type y = MAX(0, y_);\n    if (self->scrolled_by) {\n        if (y < self->scrolled_by) return historybuf_is_line_continued(self->historybuf, self->scrolled_by - 1 - y);\n        y -= self->scrolled_by;\n    }\n    if (y) return linebuf_is_line_continued(self->linebuf, y);\n    return self->linebuf == self->main_linebuf ? history_buf_endswith_wrap(self->historybuf) : false;\n}\n\nstatic Line*\nrange_line_(Screen *self, int y) {\n    if (y < 0) {\n        historybuf_init_line(self->historybuf, -(y + 1), self->historybuf->line);\n        return self->historybuf->line;\n    }\n    return init_line(self, y);\n}\n\nstatic void\nrange_line(Screen *self, int y, Line *line) {\n    if (y < 0) historybuf_init_line(self->historybuf, -(y + 1), line);\n    else init_line_(self, y, line);\n}\n\nstatic Line*\nchecked_range_line(Screen *self, int y) {\n    if (-(int)self->historybuf->count <= y && y < (int)self->lines) return range_line_(self, y);\n    return NULL;\n}\n\nstatic bool\nrange_line_is_continued(Screen *self, int y) {\n    if (!(-(int)self->historybuf->count <= y && y < (int)self->lines)) return false;\n    if (y < 0) return historybuf_is_line_continued(self->historybuf, -(y + 1));\n    if (y) return linebuf_is_line_continued(self->linebuf, y);\n    return self->linebuf == self->main_linebuf ? history_buf_endswith_wrap(self->historybuf) : false;\n}\n\nstatic void\ninsert_characters(Screen *self, index_type at, index_type num, index_type y, bool replace_with_spaces) {\n    // insert num chars at x=at setting them to the value of the num chars at [at, at + num)\n    // multiline chars at x >= at are deleted and multicell chars split at x=at\n    // and x=at + num - 1 are deleted\n    nuke_multiline_char_intersecting_with(self, at, self->columns, y, y + 1, replace_with_spaces);\n    nuke_split_multicell_char_at_left_boundary(self, at, y, replace_with_spaces);\n    CPUCell *cp; GPUCell *gp;\n    linebuf_init_cells(self->linebuf, y, &cp, &gp);\n    // right shift\n    for(index_type i = self->columns - 1; i >= at + num; i--) {\n        cp[i] = cp[i - num]; gp[i] = gp[i - num];\n    }\n    nuke_incomplete_single_line_multicell_chars_in_range(self, at, at + num, y, replace_with_spaces);\n    nuke_split_multicell_char_at_right_boundary(self, self->columns - 1, y, replace_with_spaces);\n}\n\nstatic bool\nhalve_multicell_width(Screen *self, index_type x_, index_type y_) {\n    CPUCell *cp; GPUCell *gp;\n    linebuf_init_cells(self->linebuf, y_, &cp, &gp);\n    int y_min_limit = -1;\n    if (self->linebuf == self->main_linebuf) y_min_limit = -(self->historybuf->count + 1);\n    int expected_y_min_limit = ((int)y_) - cp[x_].scale;\n    if (expected_y_min_limit < y_min_limit) return false;\n    y_min_limit = expected_y_min_limit;\n    unsigned new_width = cp[x_].width / 2;\n    while (cp[x_].x && x_ > 0) x_--;\n    const index_type ws = mcd_x_limit(&cp[x_]);\n    const index_type x_limit = MIN(self->columns, x_ + ws);\n    const index_type half_x_limit = MIN(self->columns, x_ + ws / 2);\n    int y_max_limit = MIN(self->lines, y_ + cp[x_].scale);\n    for (int y = y_min_limit + 1; y < y_max_limit; y++) {\n        Line *line = range_line_(self, y); cp = line->cpu_cells; gp = line->gpu_cells;\n        for (index_type x = x_; x < half_x_limit; x++) cp[x].width = new_width;\n        for (index_type x = half_x_limit; x < x_limit; x++) {\n            cp[x] = (CPUCell){0}; clear_sprite_position(gp[x]);\n        }\n        if (y > -1) linebuf_mark_line_dirty(self->linebuf, y);\n    }\n    self->is_dirty = true;\n    return true;\n}\n\nvoid\nset_active_hyperlink(Screen *self, char *id, char *url) {\n    if (OPT(allow_hyperlinks)) {\n        if (!url || !url[0]) {\n            self->active_hyperlink_id = 0;\n            return;\n        }\n        self->active_hyperlink_id = get_id_for_hyperlink(self, id, url);\n    }\n}\n\nstatic bool\nadd_combining_char(Screen *self, char_type ch, index_type x, index_type y) {\n    CPUCell *cpu_cells = linebuf_cpu_cells_for_line(self->linebuf, y);\n    CPUCell *cell = cpu_cells + x;\n    if (!cell_has_text(cell) || (cell->is_multicell && cell->y)) return false; // don't allow adding combining chars to a null cell\n    text_in_cell(cell, self->text_cache, self->lc);\n    if (self->lc->count >= MAX_NUM_CODEPOINTS_PER_CELL) return false; // don't allow too many combining chars to prevent DoS attacks\n    ensure_space_for_chars(self->lc, self->lc->count + 1);\n    self->lc->chars[self->lc->count++] = ch;\n    cell->ch_or_idx = tc_get_or_insert_chars(self->text_cache, self->lc);\n    cell->ch_is_idx = true;\n    if (cell->is_multicell) {\n        char_type ch_and_idx = cell->ch_and_idx;\n        while (cell->x && x) cell = cpu_cells + --x;\n        index_type x_limit = MIN(x + mcd_x_limit(cell), self->columns);\n        for (index_type v = y; v < y + cell->scale; v++) {\n            cpu_cells = linebuf_cpu_cells_for_line(self->linebuf, v);\n            for (index_type h = x; h < x_limit; h++) cpu_cells[h].ch_and_idx = ch_and_idx;\n            linebuf_mark_line_dirty(self->linebuf, v);\n        }\n    }\n    return true;\n}\n\n\nstatic bool\nhas_multiline_cells_in_span(const CPUCell *cells, const index_type start, const index_type count) {\n    for (index_type x = start; x < start + count; x++) if (cells[x].y) return true;\n    return false;\n}\n\nstatic bool\nmove_cursor_past_multicell(Screen *self, index_type required_width) {\n    if (required_width > self->columns) return false;\n    index_type orig_x = self->cursor->x, orig_y = self->cursor->y;\n    while(true) {\n        CPUCell *cp = linebuf_cpu_cells_for_line(self->linebuf, self->cursor->y);\n        while (self->cursor->x + required_width <= self->columns) {\n            if (!has_multiline_cells_in_span(cp, self->cursor->x, required_width)) {\n                if (cp[self->cursor->x].is_multicell) nuke_multicell_char_at(self, self->cursor->x, self->cursor->y, cp[self->cursor->x].x != 0);\n                return true;\n            }\n            self->cursor->x++;\n        }\n        if (self->modes.mDECAWM || has_multiline_cells_in_span(cp, self->columns - required_width, required_width)) {\n            continue_to_next_line(self);\n        } else {\n            self->cursor->x = self->columns - required_width;\n            if (cp[self->cursor->x].is_multicell) nuke_multicell_char_at(self, self->cursor->x, self->cursor->y, cp[self->cursor->x].x != 0);\n            return true;\n        }\n    }\n    self->cursor->x = orig_x; self->cursor->y = orig_y;\n    return false;\n}\n\nstatic void\nmove_widened_char_past_multiline_chars(Screen *self, text_loop_state *s, CPUCell* cpu_cell, GPUCell *gpu_cell, index_type xpos, index_type ypos) {\n    index_type before = self->cursor->y;\n    self->cursor->x = xpos; self->cursor->y = ypos;\n    if (move_cursor_past_multicell(self, 2)) {\n        CPUCell *cp; GPUCell *gp;\n        clear_sprite_position(*gpu_cell);\n        linebuf_init_cells(self->linebuf, self->cursor->y, &cp, &gp);\n        cp[self->cursor->x] = *cpu_cell; gp[self->cursor->x] = *gpu_cell;\n        self->cursor->x++;\n        cp[self->cursor->x] = *cpu_cell; gp[self->cursor->x] = *gpu_cell;\n        cp[self->cursor->x].x = 1;\n        self->cursor->x++;\n    }\n    *cpu_cell = (CPUCell){0}; *gpu_cell = (GPUCell){0};\n    if (self->cursor->y == before) init_segmentation_state(self, s);\n    else init_text_loop_line(self, s);\n}\n\nstatic bool\nis_emoji_presentation_base(char_type ch) {\n    return char_props_for(ch).is_emoji_presentation_base == 1;\n}\n\nstatic void\ndraw_combining_char(Screen *self, text_loop_state *s, char_type ch) {\n    CPUCell *cp; GPUCell *gp;\n    linebuf_init_cells(self->linebuf, s->prev.y, &cp, &gp);\n    index_type xpos = s->prev.x;\n    while (xpos && cp[xpos].is_multicell && cp[xpos].x) xpos--;\n    if (!add_combining_char(self, ch, xpos, s->prev.y) || self->lc->count < 2) return;\n    unsigned base_pos = self->lc->count -  2;\n    if (ch == VS16) {  // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji\n        CPUCell *cpu_cell = cp + xpos;\n        GPUCell *gpu_cell = gp + xpos;\n        if (self->lc->chars[base_pos + 1] == VS16 && !cpu_cell->is_multicell && is_emoji_presentation_base(self->lc->chars[base_pos])) {\n            cpu_cell->is_multicell = true;\n            cpu_cell->width = 2;\n            cpu_cell->natural_width = true;\n            if (!cpu_cell->scale) cpu_cell->scale = 1;\n            if (xpos + 1 < self->columns) {\n                CPUCell *second = cp + xpos + 1;\n                if (second->is_multicell) {\n                    if (second->y) {\n                        move_widened_char_past_multiline_chars(self, s, cpu_cell, gpu_cell, xpos, s->prev.y);\n                        return;\n                    }\n                    nuke_multicell_char_at(self, xpos + 1, s->prev.y, false);\n                }\n                zero_cells(s, second, gp + xpos + 1);\n                self->cursor->x++;\n                *second = *cpu_cell; second->x = 1;\n            } else {\n                move_widened_char_past_multiline_chars(self, s, cpu_cell, gpu_cell, xpos, s->prev.y);\n            }\n        }\n    } else if (ch == VS15) {\n        const CPUCell *cpu_cell = cp + xpos;\n        if (self->lc->chars[base_pos + 1] == VS15 && cpu_cell->is_multicell && cpu_cell->width == 2 && is_emoji_presentation_base(self->lc->chars[base_pos])) {\n            index_type deltax = (cpu_cell->scale * cpu_cell->width) / 2;\n            if (halve_multicell_width(self, xpos, s->prev.y)) {\n                self->cursor->x -= deltax;\n                init_segmentation_state(self, s);\n            }\n        }\n    }\n}\n\nstatic void\nscreen_on_input(Screen *self) {\n    if (!self->has_activity_since_last_focus && !self->has_focus && self->callbacks != Py_None) {\n        PyObject *ret = PyObject_CallMethod(self->callbacks, \"on_activity_since_last_focus\", NULL);\n        if (ret == NULL) PyErr_Print();\n        else {\n            if (ret == Py_True) self->has_activity_since_last_focus = true;\n            Py_DECREF(ret);\n        }\n    }\n}\n\nstatic void\nreplace_multicell_char_under_cursor_with_spaces(Screen *self) {\n    nuke_multicell_char_at(self, self->cursor->x, self->cursor->y, true);\n}\n\nstatic void\nscreen_change_charset(Screen *self, uint32_t which) {\n    switch(which) {\n        case 0:\n            self->charset.current_num = 0;\n            self->charset.current = self->charset.zero;\n            break;\n        case 1:\n            self->charset.current_num = 1;\n            self->charset.current = self->charset.one;\n            break;\n    }\n}\n\nvoid\nscreen_designate_charset(Screen *self, uint32_t which, uint32_t as) {\n    switch(which) {\n        case 0:\n            self->charset.zero = translation_table(as);\n            if (self->charset.current_num == 0) self->charset.current = self->charset.zero;\n            break;\n        case 1:\n            self->charset.one = translation_table(as);\n            if (self->charset.current_num == 1) self->charset.current = self->charset.one;\n            break;\n    }\n}\n\n\nstatic uint32_t\nmap_char(Screen *self, const uint32_t ch) {\n    return UNLIKELY(self->charset.current && ch < 256) ? self->charset.current[ch] : ch;\n}\n\nstatic void\ndraw_control_char(Screen *self, text_loop_state *s, uint32_t ch) {\n    switch (ch) {\n        case BEL:\n            screen_bell(self); break;\n        case BS: {\n            index_type before = self->cursor->y;\n            screen_backspace(self);\n            if (before == self->cursor->y) init_segmentation_state(self, s);\n            else init_text_loop_line(self, s);\n            } break;\n        case HT:\n            if (UNLIKELY(self->cursor->x >= self->columns)) {\n                if (self->modes.mDECAWM) {\n                    // xterm discards the TAB in this case so match its behavior\n                    continue_to_next_line(self);\n                    init_text_loop_line(self, s);\n                } else if (self->columns > 0){\n                    self->cursor->x = self->columns - 1;\n                    if (s->cp[self->cursor->x].is_multicell) {\n                        if (s->cp[self->cursor->x].y) move_cursor_past_multicell(self, 1);\n                        else replace_multicell_char_under_cursor_with_spaces(self);\n                    }\n                    screen_tab(self);\n                }\n            } else screen_tab(self);\n            init_segmentation_state(self, s);\n            break;\n        case SI:\n            screen_change_charset(self, 0); break;\n        case SO:\n            screen_change_charset(self, 1); break;\n        case LF:\n        case VT:\n        case FF:\n            screen_linefeed(self); init_text_loop_line(self, s); break;\n        case CR:\n            screen_carriage_return(self); init_segmentation_state(self, s); break;\n        default:\n            break;\n    }\n}\n\nstatic void\ndraw_text_loop(Screen *self, const uint32_t *chars, size_t num_chars, text_loop_state *s) {\n    init_text_loop_line(self, s);\n    int char_width;\n    for (size_t i = 0; i < num_chars; i++) {\n        uint32_t ch = map_char(self, chars[i]);\n        if (ch < DEL && s->seg.grapheme_break <= GBP_None) {  // fast path for printable ASCII\n            if (ch < ' ') {\n                draw_control_char(self, s, ch);\n                continue;\n            }\n            char_width = 1;\n            s->seg = (GraphemeSegmentationResult){.grapheme_break=GBP_None};\n        } else {\n            CharProps cp = char_props_for(ch);\n            if (cp.is_invalid) {\n                if (ch < ' ') draw_control_char(self, s, ch);\n                continue;\n            }\n            s->seg = grapheme_segmentation_step(s->seg, cp);\n            if (UNLIKELY(s->seg.add_to_current_cell && s->prev.cc)) {\n                draw_combining_char(self, s, ch);\n                continue;\n            }\n            char_width = wcwidth_std(cp);\n            if (UNLIKELY(char_width < 1)) {\n                if (char_width == 0) {\n                    // Preserve zero width chars as combining chars even though\n                    // they were not added to the prev cell by grapheme segmentation.\n                    // Zero width chars can only be represented as combining chars.\n                    if (s->prev.cc) draw_combining_char(self, s, ch);\n                    continue;\n                }\n                char_width = 1;\n            }\n        }\n\n        if (self->cursor->x < self->columns && s->cp[self->cursor->x].is_multicell) {\n            if (s->cp[self->cursor->x].y) {\n                move_cursor_past_multicell(self, 1);\n                init_text_loop_line(self, s);\n            } else nuke_multicell_char_at(self, self->cursor->x, self->cursor->y, s->cp[self->cursor->x].x != 0);\n        }\n\n        self->last_graphic_char = ch;\n        if (UNLIKELY(self->columns < self->cursor->x + (unsigned int)char_width)) {\n            if (self->modes.mDECAWM) {\n                continue_to_next_line(self);\n                init_text_loop_line(self, s);\n            } else self->cursor->x = self->columns - char_width;\n            CPUCell *c = &s->cp[self->cursor->x];\n            if (c->is_multicell) {\n                if (c->y) { move_cursor_past_multicell(self, char_width); init_text_loop_line(self, s); }\n                nuke_multicell_char_at(self, self->cursor->x, self->cursor->y, c->x > 0);\n            }\n        }\n        if (self->modes.mIRM) insert_characters(self, self->cursor->x, char_width, self->cursor->y, true);\n        if (UNLIKELY(!s->image_placeholder_marked && ch == IMAGE_PLACEHOLDER_CHAR)) {\n            linebuf_set_line_has_image_placeholders(self->linebuf, self->cursor->y, true);\n            s->image_placeholder_marked = true;\n        }\n        CPUCell *fc = s->cp + self->cursor->x;\n        if (char_width == 2) {\n            CPUCell *second = fc + 1;\n            if (second->is_multicell) {\n                if (second->y) {\n                    self->cursor->x++;\n                    move_cursor_past_multicell(self, 2);\n                    fc = s->cp + self->cursor->x; second = fc + 1;\n                } else nuke_multicell_char_at(self, self->cursor->x + 1, self->cursor->y, true);\n            }\n            zero_cells(s, fc, s->gp + self->cursor->x);\n            *fc = (CPUCell){.ch_or_idx=ch, .is_multicell=true, .width=2, .scale=1, .natural_width=true, .hyperlink_id=s->cc.hyperlink_id};\n            *second = *fc; second->x = 1;\n            s->gp[self->cursor->x + 1] = s->gp[self->cursor->x];\n            s->prev.y = self->cursor->y; s->prev.x = self->cursor->x; s->prev.cc = fc;\n            self->cursor->x += 2;\n        } else {\n            zero_cells(s, fc, s->gp + self->cursor->x);\n            cell_set_char(fc, ch);\n            s->prev.y = self->cursor->y; s->prev.x = self->cursor->x; s->prev.cc = fc;\n            self->cursor->x++;\n            fc->is_multicell = false;\n        }\n    }\n#undef init_line\n}\n\n#define PREPARE_FOR_DRAW_TEXT \\\n    const bool force_underline = OPT(underline_hyperlinks) == UNDERLINE_ALWAYS && self->active_hyperlink_id != 0; \\\n    CellAttrs attrs = cursor_to_attrs(self->cursor); \\\n    if (force_underline) attrs.decoration = OPT(url_style); \\\n    text_loop_state s={ \\\n        .cc=(CPUCell){.hyperlink_id=self->active_hyperlink_id}, \\\n        .g=(GPUCell){ \\\n            .attrs=attrs, \\\n            .fg=self->cursor->sgr.fg & COL_MASK, .bg=self->cursor->sgr.bg & COL_MASK, \\\n            .decoration_fg=force_underline ? ((OPT(url_color) & COL_MASK) << 8) | 2 : self->cursor->sgr.decoration_fg & COL_MASK, \\\n        } \\\n    };\n\nstatic void\ndraw_text(Screen *self, const uint32_t *chars, size_t num_chars) {\n    PREPARE_FOR_DRAW_TEXT;\n    self->is_dirty = true;\n    draw_text_loop(self, chars, num_chars, &s);\n}\n\nvoid\nscreen_draw_text(Screen *self, const uint32_t *chars, size_t num_chars) {\n    screen_on_input(self);\n    draw_text(self, chars, num_chars);\n}\n\nstatic void\ndraw_codepoint(Screen *self, char_type ch) {\n    uint32_t lch = self->last_graphic_char;\n    draw_text(self, &ch, 1);\n    self->last_graphic_char = lch;\n}\n\nvoid\nscreen_align(Screen *self) {\n    self->margin_top = 0; self->margin_bottom = self->lines - 1;\n    screen_cursor_position(self, 1, 1);\n    linebuf_clear(self->linebuf, 'E');\n}\n\nstatic size_t\ndecode_utf8_safe_string(const uint8_t *src, size_t sz, uint32_t *dest) {\n    // dest must be an array of size at least sz\n    uint32_t codep = 0;\n    UTF8State state = 0, prev = UTF8_ACCEPT;\n    size_t i = 0, d = 0;\n    for (; i < sz; i++) {\n        switch(decode_utf8(&state, &codep, src[i])) {\n            case UTF8_ACCEPT:\n                // Ignore C0 and C1 chars\n                if (codep >= ' ' && !(DEL <= codep && codep <= 159)) dest[d++] = codep;\n                break;\n            case UTF8_REJECT:\n                state = UTF8_ACCEPT;\n                if (prev != UTF8_ACCEPT && i > 0) i--;\n                break;\n        }\n        prev = state;\n    }\n    return d;\n}\n\nstatic void\nhandle_fixed_width_multicell_command(Screen *self, CPUCell mcd, ListOfChars *lc) {\n    index_type width = mcd.width * mcd.scale;\n    index_type height = mcd.scale;\n    index_type max_height = self->margin_bottom - self->margin_top + 1;\n    if (width > self->columns || height > max_height) return;\n    lc->count = MIN(lc->count, MAX_NUM_CODEPOINTS_PER_CELL);\n    PREPARE_FOR_DRAW_TEXT;\n    mcd.hyperlink_id = s.cc.hyperlink_id;\n    cell_set_chars(&mcd, self->text_cache, lc);\n    move_cursor_past_multicell(self, width);\n    if (height > 1) {\n        index_type available_height = self->margin_bottom - self->cursor->y + 1;\n        if (height > available_height) {\n            index_type extra_lines = height - available_height;\n            screen_scroll(self, extra_lines);\n            self->cursor->y -= extra_lines;\n        }\n    }\n    if (self->modes.mIRM) {\n        for (index_type y = self->cursor->y; y < self->cursor->y + height; y++) {\n            if (self->modes.mIRM) insert_characters(self, self->cursor->x, width, y, true);\n        }\n    }\n    for (index_type y = self->cursor->y; y < self->cursor->y + height; y++) {\n        linebuf_init_cells(self->linebuf, y, &s.cp, &s.gp);\n        linebuf_mark_line_dirty(self->linebuf, y);\n        mcd.x = 0; mcd.y = y - self->cursor->y;\n        for (index_type x = self->cursor->x; x < self->cursor->x + width; x++, mcd.x++) {\n            if (s.cp[x].is_multicell) nuke_multicell_char_at(self, x, y, s.cp[x].x + s.cp[x].y > 0);\n            s.cp[x] = mcd; s.gp[x] = s.g;\n        }\n    }\n    self->cursor->x += width;\n    self->is_dirty = true;\n}\n\nstatic void\nhandle_variable_width_multicell_command(Screen *self, CPUCell mcd, ListOfChars *lc) {\n    ensure_space_for_chars(lc, lc->count + 1); lc->chars[lc->count] = 0;\n    mcd.width = wcswidth_string(lc->chars);\n    if (!mcd.width) { lc->count = 0; return; }\n    handle_fixed_width_multicell_command(self, mcd, lc);\n}\n\nvoid\nscreen_handle_multicell_command(Screen *self, const MultiCellCommand *cmd, const uint8_t *payload) {\n    screen_on_input(self);\n    if (!cmd->payload_sz) return;\n    ensure_space_for_chars(self->lc, cmd->payload_sz + 1);\n    self->lc->count = decode_utf8_safe_string(payload, cmd->payload_sz, self->lc->chars);\n    if (!self->lc->count) return;\n#define M(x) ( (1u << x) - 1u)\n    CPUCell mcd = {\n        .width=MIN(cmd->width, M(WIDTH_BITS)), .scale=MAX(1u, MIN(cmd->scale, M(SCALE_BITS))),\n        .subscale_n=MIN(cmd->subscale_n, M(SUBSCALE_BITS)), .subscale_d=MIN(cmd->subscale_d, M(SUBSCALE_BITS)),\n        .valign=MIN(cmd->vertical_align, M(VALIGN_BITS)), .halign=MIN(cmd->horizontal_align, M(HALIGN_BITS)),\n        .is_multicell=true\n    };\n#undef M\n    if (mcd.width) handle_fixed_width_multicell_command(self, mcd, self->lc);\n    else {\n        RAII_ListOfChars(lc);\n        GraphemeSegmentationResult s; grapheme_segmentation_reset(&s);\n        mcd.natural_width = true;\n        for (unsigned i = 0; i < self->lc->count; i++) {\n            char_type ch = self->lc->chars[i];\n            CharProps cp = char_props_for(ch);\n            if (cp.is_invalid) continue;\n            if ((s = grapheme_segmentation_step(s, cp)).add_to_current_cell || (wcwidth_std(cp) == 0 && lc.count)) lc.chars[lc.count++] = ch;\n            else {\n                if (lc.count) handle_variable_width_multicell_command(self, mcd, &lc);\n                switch(wcwidth_std(cp)) {\n                    case 0: case -1: lc.count = 0; break;\n                    default: lc.chars[0] = ch; lc.count = 1; break;\n                }\n            }\n        }\n        if (lc.count) handle_variable_width_multicell_command(self, mcd, &lc);\n    }\n}\n\n// }}}\n\n// Graphics {{{\n\nvoid\nscreen_alignment_display(Screen *self) {\n    // https://www.vt100.net/docs/vt510-rm/DECALN.html\n    screen_cursor_position(self, 1, 1);\n    self->margin_top = 0; self->margin_bottom = self->lines - 1;\n    for (unsigned int y = 0; y < self->linebuf->ynum; y++) {\n        linebuf_init_line(self->linebuf, y);\n        line_clear_text(self->linebuf->line, 0, self->linebuf->xnum, 'E');\n        linebuf_mark_line_dirty(self->linebuf, y);\n    }\n}\n\nvoid\nselect_graphic_rendition(Screen *self, int *params, unsigned int count, bool is_group, Region *region_) {\n    if (region_) {\n        Region region = *region_;\n        if (!region.top) region.top = 1;\n        if (!region.left) region.left = 1;\n        if (!region.bottom) region.bottom = self->lines;\n        if (!region.right) region.right = self->columns;\n        if (self->modes.mDECOM) {\n            region.top += self->margin_top; region.bottom += self->margin_top;\n        }\n        region.left -= 1; region.top -= 1; region.right -= 1; region.bottom -= 1;  // switch to zero based indexing\n        if (self->modes.mDECSACE) {\n            index_type x = MIN(region.left, self->columns - 1);\n            index_type num = region.right >= x ? region.right - x + 1 : 0;\n            num = MIN(num, self->columns - x);\n            for (index_type y = region.top; y < MIN(region.bottom + 1, self->lines); y++) {\n                linebuf_init_line(self->linebuf, y);\n                apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count, is_group);\n            }\n        } else {\n            index_type x, num;\n            if (region.top == region.bottom) {\n                linebuf_init_line(self->linebuf, region.top);\n                x = MIN(region.left, self->columns-1);\n                num = MIN(self->columns - x, region.right - x + 1);\n                apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count, is_group);\n            } else {\n                for (index_type y = region.top; y < MIN(region.bottom + 1, self->lines); y++) {\n                    if (y == region.top) { x = MIN(region.left, self->columns - 1); num = self->columns - x; }\n                    else if (y == region.bottom) { x = 0; num = MIN(region.right + 1, self->columns); }\n                    else { x = 0; num = self->columns; }\n                    linebuf_init_line(self->linebuf, y);\n                    apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count, is_group);\n                }\n            }\n        }\n    } else {\n        cursor_from_sgr(self->cursor, params, count, is_group);\n        self->sgr_blink_was_used |= self->cursor->sgr.blink;\n    }\n}\n\nstatic void\nwrite_to_test_child(Screen *self, const char *data, size_t sz) {\n    PyObject *r = PyObject_CallMethod(self->test_child, \"write\", \"y#\", data, sz); if (r == NULL) PyErr_Print(); Py_CLEAR(r);\n}\n\nstatic bool\nwrite_to_child(Screen *self, const char *data, size_t sz) {\n    bool written = false;\n    if (self->window_id) written = schedule_write_to_child(self->window_id, 1, data, sz);\n    if (self->test_child != Py_None) { write_to_test_child(self, data, sz); }\n    return written;\n}\n\nstatic void\nget_prefix_and_suffix_for_escape_code(unsigned char which, const char ** prefix, const char ** suffix) {\n    *suffix = \"\\033\\\\\";\n    switch(which) {\n        case ESC_DCS:\n            *prefix = \"\\033P\";\n            break;\n        case ESC_CSI:\n            *prefix = \"\\033[\"; *suffix = \"\";\n            break;\n        case ESC_OSC:\n            *prefix = \"\\033]\";\n            break;\n        case ESC_PM:\n            *prefix = \"\\033^\";\n            break;\n        case ESC_APC:\n            *prefix = \"\\033_\";\n            break;\n        default:\n            fatal(\"Unknown escape code to write: %u\", which);\n    }\n}\n\nbool\nwrite_escape_code_to_child(Screen *self, unsigned char which, const char *data) {\n    bool written = false;\n    const char *prefix, *suffix;\n    get_prefix_and_suffix_for_escape_code(which, &prefix, &suffix);\n    if (self->window_id) {\n        if (suffix[0]) {\n            written = schedule_write_to_child(self->window_id, 3, prefix, strlen(prefix), data, strlen(data), suffix, strlen(suffix));\n        } else {\n            written = schedule_write_to_child(self->window_id, 2, prefix, strlen(prefix), data, strlen(data));\n        }\n    }\n    if (self->test_child != Py_None) {\n        write_to_test_child(self, prefix, strlen(prefix));\n        write_to_test_child(self, data, strlen(data));\n        if (suffix[0]) write_to_test_child(self, suffix, strlen(suffix));\n    }\n    return written;\n}\n\nstatic bool\nwrite_escape_code_to_child_python(Screen *self, unsigned char which, PyObject *data) {\n    bool written = false;\n    const char *prefix, *suffix;\n    get_prefix_and_suffix_for_escape_code(which, &prefix, &suffix);\n    if (self->window_id) written = schedule_write_to_child_python(self->window_id, prefix, data, suffix);\n    if (self->test_child != Py_None) {\n        write_to_test_child(self, prefix, strlen(prefix));\n        for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(data); i++) {\n            PyObject *t = PyTuple_GET_ITEM(data, i);\n            if (PyBytes_Check(t)) write_to_test_child(self, PyBytes_AS_STRING(t), PyBytes_GET_SIZE(t));\n            else {\n                Py_ssize_t sz;\n                const char *d = PyUnicode_AsUTF8AndSize(t, &sz);\n                if (d) write_to_test_child(self, d, sz);\n            }\n        }\n        if (suffix[0]) write_to_test_child(self, suffix, strlen(suffix));\n    }\n    return written;\n}\n\nstatic bool\ncursor_within_margins(Screen *self) {\n    return self->margin_top <= self->cursor->y && self->cursor->y <= self->margin_bottom;\n}\n\nstatic inline void\nreset_pixel_scroll(Screen *self, unsigned val) { self->pixel_scroll_offset_y = val; }\n\n\n// Remove all cell images from a portion of the screen and mark lines that\n// contain image placeholders as dirty to make sure they are redrawn. This is\n// needed when we perform commands that may move some lines without marking them\n// as dirty (like screen_insert_lines) and at the same time don't move image\n// references (i.e. unlike screen_scroll, which moves everything).\nstatic void\nscreen_dirty_line_graphics(Screen *self, const unsigned int top, const unsigned int bottom, const bool main_buf) {\n    bool need_to_remove = false;\n    const unsigned int limit = MIN(bottom+1, self->lines);\n    LineBuf *linebuf = main_buf ? self->main_linebuf : self->alt_linebuf;\n    for (unsigned int y = top; y < limit; y++) {\n        if (linebuf->line_attrs[y].has_image_placeholders) {\n            need_to_remove = true;\n            linebuf_mark_line_dirty(linebuf, y);\n            self->is_dirty = true;\n        }\n    }\n    if (need_to_remove)\n        grman_remove_cell_images(main_buf ? self->main_grman : self->alt_grman, top, bottom);\n}\n\nvoid\nscreen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload) {\n    unsigned int x = self->cursor->x, y = self->cursor->y;\n    const char *response = grman_handle_command(self->grman, cmd, payload, self->cursor, &self->is_dirty, self->cell_size);\n    if (response != NULL) write_escape_code_to_child(self, ESC_APC, response);\n    if (x != self->cursor->x || y != self->cursor->y) {\n        bool in_margins = cursor_within_margins(self);\n        if (self->cursor->x >= self->columns) { self->cursor->x = 0; self->cursor->y++; }\n        if (self->cursor->y > self->margin_bottom) screen_scroll(self, self->cursor->y - self->margin_bottom);\n        screen_ensure_bounds(self, false, in_margins);\n    }\n    if (cmd->unicode_placement) {\n        // Make sure the placeholders are redrawn if we add or change a virtual placement.\n        screen_dirty_line_graphics(self, 0, self->lines, self->linebuf == self->main_linebuf);\n    }\n}\n// }}}\n\n// Modes {{{\n\n\nvoid\nscreen_toggle_screen_buffer(Screen *self, bool save_cursor, bool clear_alt_screen) {\n    bool to_alt = self->linebuf == self->main_linebuf;\n    self->active_hyperlink_id = 0;\n    reset_pixel_scroll(self, 0);\n    if (to_alt) {\n        if (clear_alt_screen) {\n            linebuf_clear(self->alt_linebuf, BLANK_CHAR);\n            grman_clear(self->alt_grman, true, self->cell_size);\n        }\n        if (save_cursor) screen_save_cursor(self);\n        self->linebuf = self->alt_linebuf;\n        self->tabstops = self->alt_tabstops;\n        self->key_encoding_flags = self->alt_key_encoding_flags;\n        self->grman = self->alt_grman;\n        screen_cursor_position(self, 1, 1);\n        cursor_reset(self->cursor);\n    } else {\n        self->linebuf = self->main_linebuf;\n        self->tabstops = self->main_tabstops;\n        self->key_encoding_flags = self->main_key_encoding_flags;\n        if (save_cursor) screen_restore_cursor(self);\n        self->grman = self->main_grman;\n    }\n    screen_history_scroll(self, SCROLL_FULL, false);\n    self->is_dirty = true;\n    grman_mark_layers_dirty(self->grman);\n    clear_all_selections(self);\n    if (self->extra_cursors.count) {\n        self->extra_cursors.count = 0;\n        self->extra_cursors.dirty = true;\n    }\n    global_state.check_for_active_animated_images = true;\n}\n\nvoid screen_normal_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI\nvoid screen_alternate_keypad_mode(Screen UNUSED *self) {}  // Not implemented as this is handled by the GUI\n\nstatic void\nset_mode_from_const(Screen *self, unsigned int mode, bool val) {\n#define SIMPLE_MODE(name) \\\n    case name: \\\n        self->modes.m##name = val; break;\n\n#define MOUSE_MODE(name, attr, value) \\\n    case name: \\\n        self->modes.attr = val ? value : 0; break;\n\n    bool private;\n    switch(mode) {\n        SIMPLE_MODE(LNM)\n        SIMPLE_MODE(PASTE_EVENTS)\n        SIMPLE_MODE(IRM)\n        SIMPLE_MODE(DECARM)\n        SIMPLE_MODE(BRACKETED_PASTE)\n        SIMPLE_MODE(FOCUS_TRACKING)\n        SIMPLE_MODE(COLOR_PREFERENCE_NOTIFICATION)\n        SIMPLE_MODE(HANDLE_TERMIOS_SIGNALS)\n        MOUSE_MODE(MOUSE_BUTTON_TRACKING, mouse_tracking_mode, BUTTON_MODE)\n        MOUSE_MODE(MOUSE_MOTION_TRACKING, mouse_tracking_mode, MOTION_MODE)\n        MOUSE_MODE(MOUSE_MOVE_TRACKING, mouse_tracking_mode, ANY_MODE)\n        MOUSE_MODE(MOUSE_UTF8_MODE, mouse_tracking_protocol, UTF8_PROTOCOL)\n        MOUSE_MODE(MOUSE_SGR_MODE, mouse_tracking_protocol, SGR_PROTOCOL)\n        MOUSE_MODE(MOUSE_SGR_PIXEL_MODE, mouse_tracking_protocol, SGR_PIXEL_PROTOCOL)\n        MOUSE_MODE(MOUSE_URXVT_MODE, mouse_tracking_protocol, URXVT_PROTOCOL)\n\n        case DECSCLM:\n        case DECNRCM:\n            break;  // we ignore these modes\n        case DECCKM:\n            self->modes.mDECCKM = val;\n            break;\n        case DECTCEM:\n            self->modes.mDECTCEM = val;\n            break;\n        case DECSCNM:\n            // Render screen in reverse video\n            if (self->modes.mDECSCNM != val) {\n                self->modes.mDECSCNM = val;\n                self->is_dirty = true;\n            }\n            break;\n        case DECOM:\n            self->modes.mDECOM = val;\n            // According to `vttest`, DECOM should also home the cursor, see\n            // vttest/main.c:369.\n            screen_cursor_position(self, 1, 1);\n            break;\n        case DECAWM:\n            self->modes.mDECAWM = val; break;\n        case DECCOLM:\n            self->modes.mDECCOLM = val;\n            if (val) {\n                // When DECCOLM mode is set, the screen is erased and the cursor\n                // moves to the home position.\n                screen_erase_in_display(self, 2, false);\n                screen_cursor_position(self, 1, 1);\n            }\n            break;\n        case CONTROL_CURSOR_BLINK:\n            self->cursor->non_blinking = !val;\n            break;\n        case SAVE_CURSOR:\n            screen_save_cursor(self);\n            break;\n        case TOGGLE_ALT_SCREEN_1:\n        case TOGGLE_ALT_SCREEN_2:\n        case ALTERNATE_SCREEN:\n            if (val && self->linebuf == self->main_linebuf) screen_toggle_screen_buffer(self, mode == ALTERNATE_SCREEN, mode == ALTERNATE_SCREEN);\n            else if (!val && self->linebuf != self->main_linebuf) screen_toggle_screen_buffer(self, mode == ALTERNATE_SCREEN, mode == ALTERNATE_SCREEN);\n            break;\n        case 7727 << 5:\n            log_error(\"Application escape mode is not supported, the extended keyboard protocol should be used instead\");\n            break;\n        case PENDING_MODE << 5:\n            if (!screen_pause_rendering(self, val, 0)) {\n                log_error(\"Pending mode change to already current mode (%d) requested. Either pending mode expired or there is an application bug.\", val);\n            }\n            break;\n        case INBAND_RESIZE_NOTIFICATION:\n            self->modes.mINBAND_RESIZE_NOTIFICATION = val;\n            if (val) CALLBACK(\"notify_child_of_resize\", NULL);\n            break;\n        default:\n            private = mode >= 1 << 5;\n            if (private) mode >>= 5;\n            log_error(\"%s %s %u %s\", ERROR_PREFIX, \"Unsupported screen mode: \", mode, private ? \"(private)\" : \"\");\n    }\n#undef SIMPLE_MODE\n#undef MOUSE_MODE\n}\n\nvoid\nscreen_set_mode(Screen *self, unsigned int mode) {\n    set_mode_from_const(self, mode, true);\n}\n\nvoid\nscreen_decsace(Screen *self, unsigned int val) {\n    self->modes.mDECSACE = val == 2 ? true : false;\n}\n\nvoid\nscreen_reset_mode(Screen *self, unsigned int mode) {\n    set_mode_from_const(self, mode, false);\n}\n\nvoid\nscreen_modify_other_keys(Screen *self, unsigned val, unsigned val2) {\n    // Only report an error about modifyOtherKeys if the kitty keyboard\n    // protocol is not in effect and the application is trying to turn it on.\n    // There are some applications that try to enable both.\n    if (\n        self->test_child == Py_None && !screen_current_key_encoding_flags(self) &&\n        val == 4 && val2 != INT_MAX && val2 != 0\n    ) {\n        log_error(\"The application is trying to use xterm's modifyOtherKeys. This is superseded by the kitty keyboard protocol https://sw.kovidgoyal.net/kitty/keyboard-protocol. The application should be updated to use that.\");\n    }\n}\n\nuint8_t\nscreen_current_key_encoding_flags(Screen *self) {\n    for (unsigned i = arraysz(self->main_key_encoding_flags); i-- > 0; ) {\n        if (self->key_encoding_flags[i] & 0x80) return self->key_encoding_flags[i] & 0x7f;\n    }\n    return 0;\n}\n\nvoid\nscreen_report_key_encoding_flags(Screen *self) {\n    char buf[16] = {0};\n    debug_input(\"\\x1b[35mReporting key encoding flags: %u\\x1b[39m\\n\", screen_current_key_encoding_flags(self));\n    snprintf(buf, sizeof(buf), \"?%uu\", screen_current_key_encoding_flags(self));\n    write_escape_code_to_child(self, ESC_CSI, buf);\n}\n\nvoid\nscreen_set_key_encoding_flags(Screen *self, uint32_t val, uint32_t how) {\n    unsigned idx = 0;\n    for (unsigned i = arraysz(self->main_key_encoding_flags); i-- > 0; ) {\n        if (self->key_encoding_flags[i] & 0x80) { idx = i; break; }\n    }\n    uint8_t q = val & 0x7f;\n    if (how == 1) self->key_encoding_flags[idx] = q;\n    else if (how == 2) self->key_encoding_flags[idx] |= q;\n    else if (how == 3) self->key_encoding_flags[idx] &= ~q;\n    self->key_encoding_flags[idx] |= 0x80;\n    debug_input(\"\\x1b[35mSet key encoding flags to: %u\\x1b[39m\\n\", screen_current_key_encoding_flags(self));\n}\n\nvoid\nscreen_push_key_encoding_flags(Screen *self, uint32_t val) {\n    uint8_t q = val & 0x7f;\n    const unsigned sz = arraysz(self->main_key_encoding_flags);\n    unsigned current_idx = 0;\n    for (unsigned i = arraysz(self->main_key_encoding_flags); i-- > 0; ) {\n        if (self->key_encoding_flags[i] & 0x80) { current_idx = i; break; }\n    }\n    if (current_idx == sz - 1) memmove(self->key_encoding_flags, self->key_encoding_flags + 1, (sz - 1) * sizeof(self->main_key_encoding_flags[0]));\n    else self->key_encoding_flags[current_idx++] |= 0x80;\n    self->key_encoding_flags[current_idx] = 0x80 | q;\n    debug_input(\"\\x1b[35mPushed key encoding flags to: %u\\x1b[39m\\n\", screen_current_key_encoding_flags(self));\n}\n\nvoid\nscreen_pop_key_encoding_flags(Screen *self, uint32_t num) {\n    for (unsigned i = arraysz(self->main_key_encoding_flags); num && i-- > 0; ) {\n        if (self->key_encoding_flags[i] & 0x80) { num--; self->key_encoding_flags[i] = 0; }\n    }\n    debug_input(\"\\x1b[35mPopped key encoding flags to: %u\\x1b[39m\\n\", screen_current_key_encoding_flags(self));\n}\n\n// }}}\n\n// Cursor {{{\n\nMouseShape\nscreen_pointer_shape(Screen *self) {\n    if (self->linebuf == self->main_linebuf) {\n        if (self->main_pointer_shape_stack.count) return self->main_pointer_shape_stack.stack[self->main_pointer_shape_stack.count-1];\n    } else {\n        if (self->alternate_pointer_shape_stack.count) return self->alternate_pointer_shape_stack.stack[self->alternate_pointer_shape_stack.count-1];\n    }\n    return INVALID_POINTER;\n}\n\nstatic PyObject*\ncurrent_pointer_shape(Screen *self, PyObject *args UNUSED) {\n    MouseShape s = screen_pointer_shape(self);\n    const char *ans = \"0\";\n    switch(s) {\n        case INVALID_POINTER: break;\n        /* start enum to css (auto generated by gen-key-constants.py do not edit) */\n        case DEFAULT_POINTER: ans = \"default\"; break;\n        case TEXT_POINTER: ans = \"text\"; break;\n        case POINTER_POINTER: ans = \"pointer\"; break;\n        case HELP_POINTER: ans = \"help\"; break;\n        case WAIT_POINTER: ans = \"wait\"; break;\n        case PROGRESS_POINTER: ans = \"progress\"; break;\n        case CROSSHAIR_POINTER: ans = \"crosshair\"; break;\n        case CELL_POINTER: ans = \"cell\"; break;\n        case VERTICAL_TEXT_POINTER: ans = \"vertical-text\"; break;\n        case MOVE_POINTER: ans = \"move\"; break;\n        case E_RESIZE_POINTER: ans = \"e-resize\"; break;\n        case NE_RESIZE_POINTER: ans = \"ne-resize\"; break;\n        case NW_RESIZE_POINTER: ans = \"nw-resize\"; break;\n        case N_RESIZE_POINTER: ans = \"n-resize\"; break;\n        case SE_RESIZE_POINTER: ans = \"se-resize\"; break;\n        case SW_RESIZE_POINTER: ans = \"sw-resize\"; break;\n        case S_RESIZE_POINTER: ans = \"s-resize\"; break;\n        case W_RESIZE_POINTER: ans = \"w-resize\"; break;\n        case EW_RESIZE_POINTER: ans = \"ew-resize\"; break;\n        case NS_RESIZE_POINTER: ans = \"ns-resize\"; break;\n        case NESW_RESIZE_POINTER: ans = \"nesw-resize\"; break;\n        case NWSE_RESIZE_POINTER: ans = \"nwse-resize\"; break;\n        case ZOOM_IN_POINTER: ans = \"zoom-in\"; break;\n        case ZOOM_OUT_POINTER: ans = \"zoom-out\"; break;\n        case ALIAS_POINTER: ans = \"alias\"; break;\n        case COPY_POINTER: ans = \"copy\"; break;\n        case NOT_ALLOWED_POINTER: ans = \"not-allowed\"; break;\n        case NO_DROP_POINTER: ans = \"no-drop\"; break;\n        case GRAB_POINTER: ans = \"grab\"; break;\n        case GRABBING_POINTER: ans = \"grabbing\"; break;\n/* end enum to css */\n    }\n    return PyUnicode_FromString(ans);\n}\n\nstatic PyObject*\nchange_pointer_shape(Screen *self, PyObject *args) {\n    char op; const char *css_name, *b;\n    if (!PyArg_ParseTuple(args, \"ss\", &b, &css_name)) return NULL;\n    op = b[0];\n    uint8_t *count, *stack;\n    if (self->main_linebuf == self->linebuf) { count = &self->main_pointer_shape_stack.count; stack = self->main_pointer_shape_stack.stack; }\n    else { count = &self->alternate_pointer_shape_stack.count; stack = self->alternate_pointer_shape_stack.stack; }\n    if (op == '<') {\n        if (*count) *count -= 1;\n    } else {\n        MouseShape s = INVALID_POINTER;\n        if (css_name[0] == 0) s = INVALID_POINTER;\n        /* start css to enum (auto generated by gen-key-constants.py do not edit) */\n        else if (strcmp(\"default\", css_name) == 0) s = DEFAULT_POINTER;\n        else if (strcmp(\"text\", css_name) == 0) s = TEXT_POINTER;\n        else if (strcmp(\"pointer\", css_name) == 0) s = POINTER_POINTER;\n        else if (strcmp(\"help\", css_name) == 0) s = HELP_POINTER;\n        else if (strcmp(\"wait\", css_name) == 0) s = WAIT_POINTER;\n        else if (strcmp(\"progress\", css_name) == 0) s = PROGRESS_POINTER;\n        else if (strcmp(\"crosshair\", css_name) == 0) s = CROSSHAIR_POINTER;\n        else if (strcmp(\"cell\", css_name) == 0) s = CELL_POINTER;\n        else if (strcmp(\"vertical-text\", css_name) == 0) s = VERTICAL_TEXT_POINTER;\n        else if (strcmp(\"move\", css_name) == 0) s = MOVE_POINTER;\n        else if (strcmp(\"e-resize\", css_name) == 0) s = E_RESIZE_POINTER;\n        else if (strcmp(\"ne-resize\", css_name) == 0) s = NE_RESIZE_POINTER;\n        else if (strcmp(\"nw-resize\", css_name) == 0) s = NW_RESIZE_POINTER;\n        else if (strcmp(\"n-resize\", css_name) == 0) s = N_RESIZE_POINTER;\n        else if (strcmp(\"se-resize\", css_name) == 0) s = SE_RESIZE_POINTER;\n        else if (strcmp(\"sw-resize\", css_name) == 0) s = SW_RESIZE_POINTER;\n        else if (strcmp(\"s-resize\", css_name) == 0) s = S_RESIZE_POINTER;\n        else if (strcmp(\"w-resize\", css_name) == 0) s = W_RESIZE_POINTER;\n        else if (strcmp(\"ew-resize\", css_name) == 0) s = EW_RESIZE_POINTER;\n        else if (strcmp(\"ns-resize\", css_name) == 0) s = NS_RESIZE_POINTER;\n        else if (strcmp(\"nesw-resize\", css_name) == 0) s = NESW_RESIZE_POINTER;\n        else if (strcmp(\"nwse-resize\", css_name) == 0) s = NWSE_RESIZE_POINTER;\n        else if (strcmp(\"zoom-in\", css_name) == 0) s = ZOOM_IN_POINTER;\n        else if (strcmp(\"zoom-out\", css_name) == 0) s = ZOOM_OUT_POINTER;\n        else if (strcmp(\"alias\", css_name) == 0) s = ALIAS_POINTER;\n        else if (strcmp(\"copy\", css_name) == 0) s = COPY_POINTER;\n        else if (strcmp(\"not-allowed\", css_name) == 0) s = NOT_ALLOWED_POINTER;\n        else if (strcmp(\"no-drop\", css_name) == 0) s = NO_DROP_POINTER;\n        else if (strcmp(\"grab\", css_name) == 0) s = GRAB_POINTER;\n        else if (strcmp(\"grabbing\", css_name) == 0) s = GRABBING_POINTER;\n        else if (strcmp(\"left_ptr\", css_name) == 0) s = DEFAULT_POINTER;\n        else if (strcmp(\"xterm\", css_name) == 0) s = TEXT_POINTER;\n        else if (strcmp(\"ibeam\", css_name) == 0) s = TEXT_POINTER;\n        else if (strcmp(\"pointing_hand\", css_name) == 0) s = POINTER_POINTER;\n        else if (strcmp(\"hand2\", css_name) == 0) s = POINTER_POINTER;\n        else if (strcmp(\"hand\", css_name) == 0) s = POINTER_POINTER;\n        else if (strcmp(\"question_arrow\", css_name) == 0) s = HELP_POINTER;\n        else if (strcmp(\"whats_this\", css_name) == 0) s = HELP_POINTER;\n        else if (strcmp(\"clock\", css_name) == 0) s = WAIT_POINTER;\n        else if (strcmp(\"watch\", css_name) == 0) s = WAIT_POINTER;\n        else if (strcmp(\"half-busy\", css_name) == 0) s = PROGRESS_POINTER;\n        else if (strcmp(\"left_ptr_watch\", css_name) == 0) s = PROGRESS_POINTER;\n        else if (strcmp(\"tcross\", css_name) == 0) s = CROSSHAIR_POINTER;\n        else if (strcmp(\"plus\", css_name) == 0) s = CELL_POINTER;\n        else if (strcmp(\"cross\", css_name) == 0) s = CELL_POINTER;\n        else if (strcmp(\"fleur\", css_name) == 0) s = MOVE_POINTER;\n        else if (strcmp(\"pointer-move\", css_name) == 0) s = MOVE_POINTER;\n        else if (strcmp(\"right_side\", css_name) == 0) s = E_RESIZE_POINTER;\n        else if (strcmp(\"top_right_corner\", css_name) == 0) s = NE_RESIZE_POINTER;\n        else if (strcmp(\"top_left_corner\", css_name) == 0) s = NW_RESIZE_POINTER;\n        else if (strcmp(\"top_side\", css_name) == 0) s = N_RESIZE_POINTER;\n        else if (strcmp(\"bottom_right_corner\", css_name) == 0) s = SE_RESIZE_POINTER;\n        else if (strcmp(\"bottom_left_corner\", css_name) == 0) s = SW_RESIZE_POINTER;\n        else if (strcmp(\"bottom_side\", css_name) == 0) s = S_RESIZE_POINTER;\n        else if (strcmp(\"left_side\", css_name) == 0) s = W_RESIZE_POINTER;\n        else if (strcmp(\"sb_h_double_arrow\", css_name) == 0) s = EW_RESIZE_POINTER;\n        else if (strcmp(\"split_h\", css_name) == 0) s = EW_RESIZE_POINTER;\n        else if (strcmp(\"sb_v_double_arrow\", css_name) == 0) s = NS_RESIZE_POINTER;\n        else if (strcmp(\"split_v\", css_name) == 0) s = NS_RESIZE_POINTER;\n        else if (strcmp(\"size_bdiag\", css_name) == 0) s = NESW_RESIZE_POINTER;\n        else if (strcmp(\"size-bdiag\", css_name) == 0) s = NESW_RESIZE_POINTER;\n        else if (strcmp(\"size_fdiag\", css_name) == 0) s = NWSE_RESIZE_POINTER;\n        else if (strcmp(\"size-fdiag\", css_name) == 0) s = NWSE_RESIZE_POINTER;\n        else if (strcmp(\"zoom_in\", css_name) == 0) s = ZOOM_IN_POINTER;\n        else if (strcmp(\"zoom_out\", css_name) == 0) s = ZOOM_OUT_POINTER;\n        else if (strcmp(\"dnd-link\", css_name) == 0) s = ALIAS_POINTER;\n        else if (strcmp(\"dnd-copy\", css_name) == 0) s = COPY_POINTER;\n        else if (strcmp(\"forbidden\", css_name) == 0) s = NOT_ALLOWED_POINTER;\n        else if (strcmp(\"crossed_circle\", css_name) == 0) s = NOT_ALLOWED_POINTER;\n        else if (strcmp(\"dnd-no-drop\", css_name) == 0) s = NO_DROP_POINTER;\n        else if (strcmp(\"openhand\", css_name) == 0) s = GRAB_POINTER;\n        else if (strcmp(\"hand1\", css_name) == 0) s = GRAB_POINTER;\n        else if (strcmp(\"closedhand\", css_name) == 0) s = GRABBING_POINTER;\n        else if (strcmp(\"dnd-none\", css_name) == 0) s = GRABBING_POINTER;\n/* end css to enum */\n        if (s == INVALID_POINTER && css_name[0] != 0) { PyErr_Format(PyExc_KeyError, \"Not a known pointer shape: %s\", css_name); return NULL; }\n        if (op == '=') {\n            if (!*count) *count += 1;\n            stack[*count - 1] = s;\n        } else if (op == '>') {\n            if ((*count + 1u) >= arraysz(self->main_pointer_shape_stack.stack)) {\n                remove_i_from_array(stack, 0, *count);\n            }\n            *count += 1;\n            stack[*count - 1] = s;\n        } else {\n            PyErr_SetString(PyExc_KeyError, \"Not a known stack operation\");\n            return NULL;\n        }\n    }\n    Py_RETURN_NONE;\n}\n\nbool\nscreen_is_cursor_visible(const Screen *self) {\n    return self->paused_rendering.expires_at ? self->paused_rendering.cursor_visible : self->modes.mDECTCEM;\n}\n\nvoid\nscreen_backspace(Screen *self) {\n    screen_cursor_move(self, 1, -1, true);\n}\n\nvoid\nscreen_tab(Screen *self) {\n    // Move to the next tab space, or the end of the screen if there aren't anymore left.\n    unsigned int found = 0;\n    for (unsigned int i = self->cursor->x + 1; i < self->columns; i++) {\n        if (self->tabstops[i]) { found = i; break; }\n    }\n    if (!found) found = self->columns - 1;\n    if (found != self->cursor->x) {\n        if (self->cursor->x < self->columns) {\n            CPUCell *cpu_cell = linebuf_cpu_cells_for_line(self->linebuf, self->cursor->y) + self->cursor->x;\n            combining_type diff = found - self->cursor->x;\n            bool ok = true;\n            for (combining_type i = 0; i < diff; i++) {\n                CPUCell *c = cpu_cell + i;\n                if (cell_has_text(c) && !cell_is_char(c, ' ')) { ok = false; break; }\n            }\n            if (ok) {\n                for (combining_type i = 0; i < diff; i++) {\n                    CPUCell *c = cpu_cell + i;\n                    cell_set_char(c, ' ');\n                }\n                self->lc->count = 2; self->lc->chars[0] = '\\t'; self->lc->chars[1] = diff;\n                cell_set_chars(cpu_cell, self->text_cache, self->lc);\n            }\n        }\n        self->cursor->x = found;\n    }\n}\n\nvoid\nscreen_backtab(Screen *self, unsigned int count) {\n    // Move back count tabs\n    if (!count) count = 1;\n    int i;\n    while (count > 0 && self->cursor->x > 0) {\n        count--;\n        for (i = self->cursor->x - 1; i >= 0; i--) {\n            if (self->tabstops[i]) { self->cursor->x = i; break; }\n        }\n        if (i <= 0) self->cursor->x = 0;\n    }\n}\n\nvoid\nscreen_clear_tab_stop(Screen *self, unsigned int how) {\n    switch(how) {\n        case 0:\n            if (self->cursor->x < self->columns) self->tabstops[self->cursor->x] = false;\n            break;\n        case 2:\n            break;  // no-op\n        case 3:\n            for (unsigned int i = 0; i < self->columns; i++) self->tabstops[i] = false;\n            break;\n        default:\n            log_error(\"%s %s %u\", ERROR_PREFIX, \"Unsupported clear tab stop mode: \", how);\n            break;\n    }\n}\n\nvoid\nscreen_set_tab_stop(Screen *self) {\n    if (self->cursor->x < self->columns)\n        self->tabstops[self->cursor->x] = true;\n}\n\nvoid\nscreen_cursor_move(Screen *self, unsigned int count/*=1*/, int move_direction/*=-1*/, bool allow_move_to_previous_line) {\n    if (count == 0) count = 1;\n    bool in_margins = cursor_within_margins(self);\n    if (move_direction > 0) {\n        self->cursor->x += count;\n        screen_ensure_bounds(self, false, in_margins);\n    } else {\n        index_type top = in_margins && self->modes.mDECOM ? self->margin_top : 0;\n        while (count > 0) {\n            if (count <= self->cursor->x) {\n                self->cursor->x -= count;\n                count = 0;\n            } else {\n                if (self->cursor->x > 0) {\n                    count -= self->cursor->x;\n                    self->cursor->x = 0;\n                } else {\n                    if (self->cursor->y == top || !allow_move_to_previous_line) count = 0;\n                    else {\n                        count--; self->cursor->y--;\n                        self->cursor->x = self->columns-1;\n                    }\n                }\n            }\n        }\n    }\n}\n\nvoid\nscreen_cursor_forward(Screen *self, unsigned int count/*=1*/) {\n    screen_cursor_move(self, count, 1, false);\n}\n\nvoid\nscreen_cursor_up(Screen *self, unsigned int count/*=1*/, bool do_carriage_return/*=false*/, int move_direction/*=-1*/) {\n    bool in_margins = cursor_within_margins(self);\n    if (count == 0) count = 1;\n    if (move_direction < 0 && count > self->cursor->y) self->cursor->y = 0;\n    else self->cursor->y += move_direction * count;\n    if (do_carriage_return) self->cursor->x = 0;\n    screen_ensure_bounds(self, true, in_margins);\n}\n\nvoid\nscreen_cursor_up1(Screen *self, unsigned int count/*=1*/) {\n    screen_cursor_up(self, count, true, -1);\n}\n\nvoid\nscreen_cursor_down(Screen *self, unsigned int count/*=1*/) {\n    screen_cursor_up(self, count, false, 1);\n}\n\nvoid\nscreen_cursor_down1(Screen *self, unsigned int count/*=1*/) {\n    screen_cursor_up(self, count, true, 1);\n}\n\nvoid\nscreen_cursor_to_column(Screen *self, unsigned int column) {\n    unsigned int x = MAX(column, 1u) - 1;\n    if (x != self->cursor->x) {\n        self->cursor->x = x;\n        screen_ensure_bounds(self, false, cursor_within_margins(self));\n    }\n}\n\n#define INDEX_UP(add_to_history) \\\n    linebuf_index(self->linebuf, top, bottom); \\\n    INDEX_GRAPHICS(-1) \\\n    if (add_to_history) { \\\n        /* Only add to history when no top margin has been set */ \\\n        linebuf_init_line(self->linebuf, bottom); \\\n        historybuf_add_line(self->historybuf, self->linebuf->line, &self->as_ansi_buf); \\\n        self->history_line_added_count++; \\\n        if (self->last_visited_prompt.is_set) { \\\n            if (self->last_visited_prompt.scrolled_by < self->historybuf->count) self->last_visited_prompt.scrolled_by++; \\\n            else self->last_visited_prompt.is_set = false; \\\n        } \\\n    } \\\n    linebuf_clear_line(self->linebuf, bottom, true); \\\n    self->is_dirty = true; \\\n    index_selection(self, &self->selections, true, top, bottom); \\\n    clear_selection(&self->url_ranges);\n\nvoid\nscreen_index(Screen *self) {\n    // Move cursor down one line, scrolling screen if needed\n    unsigned int top = self->margin_top, bottom = self->margin_bottom;\n    if (self->cursor->y == bottom) {\n        const bool add_to_history = self->linebuf == self->main_linebuf && self->margin_top == 0;\n        INDEX_UP(add_to_history);\n    } else screen_cursor_down(self, 1);\n}\n\nstatic void\nscreen_index_without_adding_to_history(Screen *self) {\n    // Move cursor down one line, scrolling screen if needed\n    unsigned int top = self->margin_top, bottom = self->margin_bottom;\n    if (self->cursor->y == bottom) {\n        INDEX_UP(false);\n    } else screen_cursor_down(self, 1);\n}\n\n\nvoid\nscreen_scroll(Screen *self, unsigned int count) {\n    // Scroll the screen up by count lines, not moving the cursor\n    unsigned int top = self->margin_top, bottom = self->margin_bottom;\n    const bool add_to_history = self->linebuf == self->main_linebuf && self->margin_top == 0;\n    while (count > 0) {\n        count--;\n        INDEX_UP(add_to_history);\n    }\n}\n\nvoid\nscreen_reverse_index(Screen *self) {\n    // Move cursor up one line, scrolling screen if needed\n    unsigned int top = self->margin_top, bottom = self->margin_bottom;\n    if (self->cursor->y == top) {\n        INDEX_DOWN;\n    } else screen_cursor_up(self, 1, false, -1);\n}\n\nstatic void\n_reverse_scroll(Screen *self, unsigned int count, bool fill_from_scrollback) {\n    // Scroll the screen down by count lines, not moving the cursor\n    unsigned int top = self->margin_top, bottom = self->margin_bottom;\n    fill_from_scrollback = fill_from_scrollback && self->linebuf == self->main_linebuf;\n    if (fill_from_scrollback) {\n        unsigned limit = MAX(self->lines, self->historybuf->count);\n        count = MIN(limit, count);\n    } else count = MIN(self->lines, count);\n    while (count-- > 0) {\n        bool copied = false;\n        if (fill_from_scrollback) copied = historybuf_pop_line(self->historybuf, self->alt_linebuf->line);\n        INDEX_DOWN;\n        if (copied) linebuf_copy_line_to(self->main_linebuf, self->alt_linebuf->line, 0);\n    }\n}\n\nvoid\nscreen_reverse_scroll(Screen *self, unsigned int count) {\n    _reverse_scroll(self, count, false);\n}\n\nvoid\nscreen_reverse_scroll_and_fill_from_scrollback(Screen *self, unsigned int count) {\n    _reverse_scroll(self, count, true);\n}\n\n\nvoid\nscreen_carriage_return(Screen *self) {\n    self->cursor->x = 0;\n}\n\nvoid\nscreen_linefeed(Screen *self) {\n    bool in_margins = cursor_within_margins(self);\n    screen_index(self);\n    if (self->modes.mLNM) screen_carriage_return(self);\n    screen_ensure_bounds(self, false, in_margins);\n}\n\n#define buffer_push(self, ans) { \\\n    ans = (self)->buf + (((self)->start_of_data + (self)->count) % SAVEPOINTS_SZ); \\\n    if ((self)->count == SAVEPOINTS_SZ) (self)->start_of_data = ((self)->start_of_data + 1) % SAVEPOINTS_SZ; \\\n    else (self)->count++; \\\n}\n\n#define buffer_pop(self, ans) { \\\n    if ((self)->count == 0) ans = NULL; \\\n    else { \\\n        (self)->count--; \\\n        ans = (self)->buf + (((self)->start_of_data + (self)->count) % SAVEPOINTS_SZ); \\\n    } \\\n}\n\nvoid\nscreen_save_cursor(Screen *self) {\n    Savepoint *sp = self->linebuf == self->main_linebuf ? &self->main_savepoint : &self->alt_savepoint;\n    cursor_copy_to(self->cursor, &(sp->cursor));\n    sp->mDECOM = self->modes.mDECOM;\n    sp->mDECAWM = self->modes.mDECAWM;\n    sp->mDECSCNM = self->modes.mDECSCNM;\n    memcpy(&sp->charset, &self->charset, sizeof(self->charset));\n    sp->is_valid = true;\n}\n\nstatic void\ncopy_specific_mode(Screen *self, unsigned int mode, const ScreenModes *src, ScreenModes *dest) {\n#define SIMPLE_MODE(name) case name: dest->m##name = src->m##name; break;\n#define SIDE_EFFECTS(name) case name: if (do_side_effects) set_mode_from_const(self, name, src->m##name); else dest->m##name = src->m##name; break;\n\n    const bool do_side_effects = dest == &self->modes;\n\n    switch(mode) {\n        SIMPLE_MODE(LNM)  // kitty extension\n        SIMPLE_MODE(IRM)  // kitty extension\n        SIMPLE_MODE(DECARM)\n        SIMPLE_MODE(BRACKETED_PASTE)\n        SIMPLE_MODE(FOCUS_TRACKING)\n        SIMPLE_MODE(COLOR_PREFERENCE_NOTIFICATION)\n        SIMPLE_MODE(PASTE_EVENTS)\n        SIMPLE_MODE(INBAND_RESIZE_NOTIFICATION)\n        SIMPLE_MODE(DECCKM)\n        SIMPLE_MODE(DECTCEM)\n        SIMPLE_MODE(DECAWM)\n        case MOUSE_BUTTON_TRACKING: case MOUSE_MOTION_TRACKING: case MOUSE_MOVE_TRACKING:\n            dest->mouse_tracking_mode = src->mouse_tracking_mode; break;\n        case MOUSE_UTF8_MODE: case MOUSE_SGR_MODE: case MOUSE_URXVT_MODE:\n            dest->mouse_tracking_protocol = src->mouse_tracking_protocol; break;\n        case DECSCLM:\n        case DECNRCM:\n            break;  // we ignore these modes\n        case DECSCNM:\n            if (dest->mDECSCNM != src->mDECSCNM) {\n                dest->mDECSCNM = src->mDECSCNM;\n                if (do_side_effects) self->is_dirty = true;\n            }\n            break;\n        SIDE_EFFECTS(DECOM)\n        SIDE_EFFECTS(DECCOLM)\n    }\n#undef SIMPLE_MODE\n#undef SIDE_EFFECTS\n}\n\nvoid\nscreen_save_mode(Screen *self, unsigned int mode) { // XTSAVE\n    copy_specific_mode(self, mode, &self->modes, &self->saved_modes);\n}\n\nvoid\nscreen_restore_mode(Screen *self, unsigned int mode) { // XTRESTORE\n    copy_specific_mode(self, mode, &self->saved_modes, &self->modes);\n}\n\nstatic void\ncopy_specific_modes(Screen *self, const ScreenModes *src, ScreenModes *dest) {\n    copy_specific_mode(self, LNM, src, dest);\n    copy_specific_mode(self, IRM, src, dest);\n    copy_specific_mode(self, DECARM, src, dest);\n    copy_specific_mode(self, BRACKETED_PASTE, src, dest);\n    copy_specific_mode(self, FOCUS_TRACKING, src, dest);\n    copy_specific_mode(self, COLOR_PREFERENCE_NOTIFICATION, src, dest);\n    copy_specific_mode(self, INBAND_RESIZE_NOTIFICATION, src, dest);\n    copy_specific_mode(self, PASTE_EVENTS, src, dest);\n    copy_specific_mode(self, DECCKM, src, dest);\n    copy_specific_mode(self, DECTCEM, src, dest);\n    copy_specific_mode(self, DECAWM, src, dest);\n    copy_specific_mode(self, MOUSE_BUTTON_TRACKING, src, dest);\n    copy_specific_mode(self, MOUSE_UTF8_MODE, src, dest);\n    copy_specific_mode(self, DECSCNM, src, dest);\n}\n\nvoid\nscreen_save_modes(Screen *self) {\n    // kitty extension to XTSAVE that saves a bunch of no side-effect modes\n    copy_specific_modes(self, &self->modes, &self->saved_modes);\n}\n\nvoid\nscreen_restore_cursor(Screen *self) {\n    Savepoint *sp = self->linebuf == self->main_linebuf ? &self->main_savepoint : &self->alt_savepoint;\n    if (!sp->is_valid) {\n        screen_cursor_position(self, 1, 1);\n        screen_reset_mode(self, DECOM);\n        screen_reset_mode(self, DECSCNM);\n        zero_at_ptr(&self->charset);\n    } else {\n        set_mode_from_const(self, DECOM, sp->mDECOM);\n        set_mode_from_const(self, DECAWM, sp->mDECAWM);\n        set_mode_from_const(self, DECSCNM, sp->mDECSCNM);\n        cursor_copy_to(&(sp->cursor), self->cursor);\n        memcpy(&self->charset, &sp->charset, sizeof(self->charset));\n        screen_ensure_bounds(self, false, false);\n    }\n}\n\nvoid\nscreen_restore_modes(Screen *self) {\n    // kitty extension to XTRESTORE that saves a bunch of no side-effect modes\n    copy_specific_modes(self, &self->saved_modes, &self->modes);\n}\n\nvoid\nscreen_ensure_bounds(Screen *self, bool force_use_margins/*=false*/, bool in_margins) {\n    unsigned int top, bottom;\n    if (in_margins && (force_use_margins || self->modes.mDECOM)) {\n        top = self->margin_top; bottom = self->margin_bottom;\n    } else {\n        top = 0; bottom = self->lines - 1;\n    }\n    self->cursor->x = MIN(self->cursor->x, self->columns - 1);\n    self->cursor->y = MAX(top, MIN(self->cursor->y, bottom));\n}\n\nvoid\nscreen_cursor_position(Screen *self, unsigned int line, unsigned int column) {\n    bool in_margins = cursor_within_margins(self);\n    line = (line == 0 ? 1 : line) - 1;\n    column = (column == 0 ? 1: column) - 1;\n    if (self->modes.mDECOM) {\n        line += self->margin_top;\n        line = MAX(self->margin_top, MIN(line, self->margin_bottom));\n    }\n    self->cursor->position_changed_by_client_at = self->parsing_at;\n    self->cursor->x = column; self->cursor->y = line;\n    screen_ensure_bounds(self, false, in_margins);\n}\n\nvoid\nscreen_cursor_to_line(Screen *self, unsigned int line) {\n    screen_cursor_position(self, line, self->cursor->x + 1);\n}\n\nint\nscreen_cursor_at_a_shell_prompt(const Screen *self) {\n    if (self->cursor->y >= self->lines || self->linebuf != self->main_linebuf || !screen_is_cursor_visible(self)) return -1;\n    for (index_type y=self->cursor->y + 1; y-- > 0; ) {\n        switch(self->linebuf->line_attrs[y].prompt_kind) {\n            case OUTPUT_START:\n                return -1;\n            case PROMPT_START:\n            case SECONDARY_PROMPT:\n                return y;\n            case UNKNOWN_PROMPT_KIND:\n                break;\n        }\n    }\n    return -1;\n}\n\nbool\nscreen_prompt_supports_click_events(const Screen *self, bool *is_relative) {\n    *is_relative = (bool) self->prompt_settings.relative_click_events;\n    return (bool) self->prompt_settings.supports_click_events;\n}\n\nbool\nscreen_fake_move_cursor_to_position(Screen *self, index_type start_x, index_type start_y) {\n    SelectionBoundary a = {.x=start_x, .y=start_y}, b = {.x=self->cursor->x, .y=self->cursor->y};\n    SelectionBoundary *start, *end; int key;\n    if (a.y < b.y || (a.y == b.y && a.x < b.x)) { start = &a; end = &b; key = GLFW_FKEY_LEFT; }\n    else { start = &b; end = &a; key = GLFW_FKEY_RIGHT; }\n    unsigned int count = 0;\n\n    for (unsigned y = start->y, x = start->x; y <= end->y && y < self->lines; y++) {\n        unsigned x_limit = y == end->y ? end->x : self->columns;\n        x_limit = MIN(x_limit, self->columns);\n        bool found_non_empty_cell = false;\n        while (x < x_limit) {\n            const CPUCell *c = linebuf_cpu_cell_at(self->linebuf, x, y);\n            if (!cell_has_text(c)) {\n                // we only stop counting the cells in the line at an empty cell\n                // if at least one non-empty cell is found. zsh uses empty cells\n                // between the end of the text ad the right prompt. fish uses empty\n                // cells at the start of a line when editing multiline text\n                if (!found_non_empty_cell) { x++; continue; }\n                count += 1;\n                break;\n            }\n            found_non_empty_cell = true;\n            if (c->is_multicell) {\n                x += mcd_x_limit(c);\n            } else x++;\n            count += 1;  // zsh requires a single arrow press to move past dualwidth chars\n        }\n        if (!found_non_empty_cell) count++;  // blank line\n        x = 0;\n    }\n    if (count) {\n        char output[KEY_BUFFER_SIZE+1] = {0};\n        if (self->prompt_settings.uses_special_keys_for_cursor_movement) {\n            const char *k = key == GLFW_FKEY_RIGHT ? \"1\" : \"1;1\";\n            int num = snprintf(output, KEY_BUFFER_SIZE, \"\\x1b[%su\", k);\n            for (unsigned i = 0; i < count; i++) write_to_child(self, output, num);\n        } else {\n            GLFWkeyevent ev = { .key = key, .action = GLFW_PRESS };\n            int num = encode_glfw_key_event(&ev, false, 0, output);\n            if (num != SEND_TEXT_TO_CHILD) {\n                for (unsigned i = 0; i < count; i++) write_to_child(self, output, num);\n            }\n        }\n    }\n    return count > 0;\n}\n\n// }}}\n\n// Editing {{{\n\nvoid\nscreen_erase_in_line(Screen *self, unsigned int how, bool private) {\n    /*Erases a line in a specific way.\n\n        :param int how: defines the way the line should be erased in:\n\n            * ``0`` -- Erases from cursor to end of line, including cursor\n              position.\n            * ``1`` -- Erases from beginning of line to cursor,\n              including cursor position.\n            * ``2`` -- Erases complete line.\n        :param bool private: when ``True`` character attributes are left\n                             unchanged.\n        */\n    unsigned int s = 0, n = 0;\n    switch(how) {\n        case 0:\n            s = self->cursor->x;\n            n = self->columns - self->cursor->x;\n            break;\n        case 1:\n            n = self->cursor->x + 1;\n            break;\n        case 2:\n            n = self->columns;\n            break;\n        default:\n            break;\n    }\n    if (n > 0) {\n        nuke_multicell_char_intersecting_with(self, s, n, self->cursor->y, self->cursor->y + 1, false);\n        screen_dirty_line_graphics(self, self->cursor->y, self->cursor->y, self->linebuf == self->main_linebuf);\n        linebuf_init_line(self->linebuf, self->cursor->y);\n        if (private) {\n            line_clear_text(self->linebuf->line, s, n, BLANK_CHAR);\n        } else {\n            line_apply_cursor(self->linebuf->line, self->cursor, s, n, true);\n        }\n        self->is_dirty = true;\n        clear_intersecting_selections(self, self->cursor->y);\n        linebuf_mark_line_dirty(self->linebuf, self->cursor->y);\n    }\n}\n\nstatic void\ndirty_scroll(Screen *self) {\n    self->scroll_changed = true;\n    screen_pause_rendering(self, false, 0);\n}\n\nstatic void\nscreen_clear_scrollback(Screen *self) {\n    historybuf_clear(self->historybuf);\n    reset_pixel_scroll(self, 0);\n    if (self->scrolled_by != 0) {\n        self->scrolled_by = 0;\n        dirty_scroll(self);\n    }\n    LineBuf *orig = self->linebuf; self->linebuf = self->main_linebuf;\n    CPUCell *cells = linebuf_cpu_cells_for_line(self->linebuf, 0);\n    for (index_type x = 0; x < self->columns; x++) {\n        CPUCell *c = cells + x;\n        if (c->is_multicell && c->y > 0) {  // multiline char that extended into scrollback\n            nuke_multicell_char_at(self, x, 0, false);\n        }\n    }\n    self->linebuf = orig;\n}\n\nstatic Line* visual_line_(Screen *self, int y_);\n\nstatic void\nscreen_move_into_scrollback(Screen *self) {\n    if (self->linebuf != self->main_linebuf || self->margin_top != 0 || self->margin_bottom != self->lines - 1) return;\n    unsigned int num_of_lines_to_move = self->lines;\n    while (num_of_lines_to_move) {\n        Line *line = visual_line_(self, num_of_lines_to_move-1);\n        if (!line_is_empty(line)) break;\n        num_of_lines_to_move--;\n    }\n    if (num_of_lines_to_move) {\n        unsigned int top, bottom;\n        const bool add_to_history = self->linebuf == self->main_linebuf && self->margin_top == 0;\n        for (; num_of_lines_to_move; num_of_lines_to_move--) {\n            top = 0, bottom = num_of_lines_to_move - 1;\n            INDEX_UP(add_to_history);\n        }\n    }\n}\n\nvoid\nscreen_erase_in_display(Screen *self, unsigned int how, bool private) {\n    /* Erases display in a specific way.\n\n        :param int how: defines the way the screen should be erased:\n\n            * ``0`` -- Erases from cursor to end of screen, including\n              cursor position.\n            * ``1`` -- Erases from beginning of screen to cursor,\n              including cursor position.\n            * ``2`` -- Erases complete display. All lines are erased\n              and changed to single-width. Cursor does not move.\n            * ``22`` -- Copy screen contents into scrollback if in main screen,\n              then do the same as ``2``.\n            * ``3`` -- Erase complete display and scrollback buffer as well.\n        :param bool private: when ``True`` character attributes are left unchanged\n    */\n    unsigned int a, b;\n    bool nuke_multicell_chars = true;\n    switch(how) {\n        case 0:\n            a = self->cursor->y + 1; b = self->lines; break;\n        case 1:\n            a = 0; b = self->cursor->y; break;\n        case 22:\n            screen_move_into_scrollback(self);\n            nuke_multicell_chars = false;  // they have been moved into scrollback and we would get double deletions\n            how = 2;\n            /* fallthrough */\n        case 2:\n        case 3:\n            if (self->extra_cursors.count) {\n                self->extra_cursors.count = 0;\n                self->extra_cursors.dirty = true;\n            }\n            grman_clear(self->grman, how == 3, self->cell_size);\n            a = 0; b = self->lines; nuke_multicell_chars = false;\n            break;\n        default:\n            return;\n    }\n    if (b > a) {\n        if (how != 3) screen_dirty_line_graphics(self, a, b, self->linebuf == self->main_linebuf);\n        if (private) {\n            for (unsigned int i=a; i < b; i++) {\n                linebuf_init_line(self->linebuf, i);\n                line_clear_text(self->linebuf->line, 0, self->columns, BLANK_CHAR);\n                linebuf_set_last_char_as_continuation(self->linebuf, i, false);\n                linebuf_clear_attrs_and_dirty(self->linebuf, i);\n            }\n        } else linebuf_clear_lines(self->linebuf, self->cursor, a, b);\n        if (nuke_multicell_chars) nuke_multicell_char_intersecting_with(self, 0, self->columns, a, b, false);\n        self->is_dirty = true;\n        if (selection_intersects_screen_lines(&self->selections, a, b)) clear_selection(&self->selections);\n        if (selection_intersects_screen_lines(&self->url_ranges, a, b)) clear_selection(&self->url_ranges);\n    }\n    if (how < 2) {\n        screen_erase_in_line(self, how, private);\n        if (how == 1) linebuf_clear_attrs_and_dirty(self->linebuf, self->cursor->y);\n    }\n    if (how == 3 && self->linebuf == self->main_linebuf) {\n        screen_clear_scrollback(self);\n    }\n}\n\nvoid\nscreen_insert_lines(Screen *self, unsigned int count) {\n    unsigned int top = self->margin_top, bottom = self->margin_bottom;\n    if (count == 0) count = 1;\n    if (top <= self->cursor->y && self->cursor->y <= bottom) {\n        // remove split multiline chars at top edge\n        CPUCell *cells = linebuf_cpu_cells_for_line(self->linebuf, self->cursor->y);\n        for (index_type x = 0; x < self->columns; x++) {\n            if (cells[x].is_multicell && cells[x].y) nuke_multicell_char_at(self, x, self->cursor->y, false);\n        }\n        screen_dirty_line_graphics(self, top, bottom, self->linebuf == self->main_linebuf);\n        linebuf_insert_lines(self->linebuf, count, self->cursor->y, bottom);\n        self->is_dirty = true;\n        clear_all_selections(self);\n        screen_carriage_return(self);\n        // remove split multiline chars at bottom of screen\n        cells = linebuf_cpu_cells_for_line(self->linebuf, bottom);\n        for (index_type x = 0; x < self->columns; x++) {\n            if (cells[x].is_multicell) {\n                index_type y_limit = cells[x].scale;\n                if (cells[x].y + 1u < y_limit) {\n                    index_type orig = self->lines;\n                    self->lines = bottom + 1;\n                    nuke_multicell_char_at(self, x, bottom, false);\n                    self->lines = orig;\n                }\n            }\n        }\n    }\n}\n\nstatic void\nscreen_scroll_until_cursor_prompt(Screen *self, bool add_to_scrollback) {\n    bool in_margins = cursor_within_margins(self);\n    int q = screen_cursor_at_a_shell_prompt(self);\n    unsigned int y = q > -1 ? (unsigned int)q : self->cursor->y;\n    unsigned int num_lines_to_scroll = MIN(self->margin_bottom, y);\n    unsigned int final_y = num_lines_to_scroll <= self->cursor->y ? self->cursor->y - num_lines_to_scroll : 0;\n    self->cursor->y = self->margin_bottom;\n    if (add_to_scrollback) while (num_lines_to_scroll--) screen_index(self);\n    else while (num_lines_to_scroll--) screen_index_without_adding_to_history(self);\n    self->cursor->y = final_y;\n    screen_ensure_bounds(self, false, in_margins);\n}\n\nstatic void\nscreen_delete_lines_impl(Screen *self, index_type start, index_type count, index_type top, index_type bottom) {\n    index_type y = start;\n    nuke_multiline_char_intersecting_with(self, 0, self->columns, y, y + 1, false);\n    y += count;\n    y = MIN(bottom, y);\n    nuke_multiline_char_intersecting_with(self, 0, self->columns, y, y + 1, false);\n    screen_dirty_line_graphics(self, top, bottom, self->linebuf == self->main_linebuf);\n    linebuf_delete_lines(self->linebuf, count, start, bottom);\n    self->is_dirty = true;\n    clear_all_selections(self);\n}\n\nvoid\nscreen_delete_lines(Screen *self, unsigned int count) {\n    unsigned int top = self->margin_top, bottom = self->margin_bottom;\n    if (count == 0) count = 1;\n    if (top <= self->cursor->y && self->cursor->y <= bottom) {\n        screen_delete_lines_impl(self, self->cursor->y, count, top, bottom);\n        screen_carriage_return(self);\n    }\n}\n\nvoid\nscreen_insert_characters(Screen *self, unsigned int count) {\n    const unsigned int bottom = self->lines ? self->lines - 1 : 0;\n    if (count == 0) count = 1;\n    if (self->cursor->y <= bottom) {\n        unsigned int x = self->cursor->x;\n        unsigned int num = MIN(self->columns - x, count);\n        insert_characters(self, x, num, self->cursor->y, false);\n        linebuf_init_line(self->linebuf, self->cursor->y);\n        line_apply_cursor(self->linebuf->line, self->cursor, x, num, true);\n        linebuf_mark_line_dirty(self->linebuf, self->cursor->y);\n        self->is_dirty = true;\n        clear_intersecting_selections(self, self->cursor->y);\n    }\n}\n\nvoid\nscreen_repeat_character(Screen *self, unsigned int count) {\n    if (self->last_graphic_char) {\n        if (count == 0) count = 1;\n        unsigned int num = MIN(count, CSI_REP_MAX_REPETITIONS);\n        alignas(64) uint32_t buf[64];\n        for (unsigned i = 0; i < arraysz(buf); i++) buf[i] = self->last_graphic_char;\n        for (unsigned i = 0; i < num; i += arraysz(buf)) screen_draw_text(self, buf, MIN(num - i, arraysz(buf)));\n    }\n}\n\nstatic void\nremove_characters(Screen *self, index_type at, index_type num, index_type y, bool replace_with_spaces) {\n    // delete num chars at x=at setting them to the value of the num chars at [at + num, at + num + num)\n    // multiline chars at x >= at are deleted and multicell chars split at x=at\n    // and x=at + num - 1 are deleted\n    nuke_multiline_char_intersecting_with(self, at, self->columns, y, y + 1, replace_with_spaces);\n    nuke_split_multicell_char_at_left_boundary(self, at, y, replace_with_spaces);\n    CPUCell *cp; GPUCell *gp;\n    linebuf_init_cells(self->linebuf, y, &cp, &gp);\n    // left shift\n    for (index_type i = at; i < self->columns - num; i++) {\n        cp[i] = cp[i+num]; gp[i] = gp[i+num];\n    }\n    nuke_incomplete_single_line_multicell_chars_in_range(self, at, self->columns, y, replace_with_spaces);\n}\n\nvoid\nscreen_delete_characters(Screen *self, unsigned int count) {\n    // Delete characters, later characters are moved left\n    const unsigned int bottom = self->lines ? self->lines - 1 : 0;\n    if (count == 0) count = 1;\n    if (self->cursor->y <= bottom) {\n        unsigned int x = self->cursor->x;\n        unsigned int num = MIN(self->columns - x, count);\n        remove_characters(self, x, num, self->cursor->y, false);\n        linebuf_init_line(self->linebuf, self->cursor->y);\n        line_apply_cursor(self->linebuf->line, self->cursor, self->columns - num, num, true);\n        linebuf_mark_line_dirty(self->linebuf, self->cursor->y);\n        self->is_dirty = true;\n        clear_intersecting_selections(self, self->cursor->y);\n    }\n}\n\nvoid\nscreen_erase_characters(Screen *self, unsigned int count) {\n    // Delete characters clearing the cells\n    if (count == 0) count = 1;\n    unsigned int x = self->cursor->x;\n    unsigned int num = MIN(self->columns - x, count);\n    nuke_multicell_char_intersecting_with(self, x, x + num, self->cursor->y, self->cursor->y + 1, false);\n    linebuf_init_line(self->linebuf, self->cursor->y);\n    line_apply_cursor(self->linebuf->line, self->cursor, x, num, true);\n    linebuf_mark_line_dirty(self->linebuf, self->cursor->y);\n    self->is_dirty = true;\n    clear_intersecting_selections(self, self->cursor->y);\n}\n\n// }}}\n\n// Device control {{{\n\nbool\nscreen_invert_colors(Screen *self) {\n    return self->paused_rendering.expires_at ? self->paused_rendering.inverted : (self->modes.mDECSCNM ? true : false);\n}\n\nvoid\nscreen_bell(Screen *self) {\n    if (self->ignore_bells.start) {\n        monotonic_t now = monotonic();\n        if (now < self->ignore_bells.start + self->ignore_bells.duration) {\n            self->ignore_bells.start = now;\n            return;\n        }\n        self->ignore_bells.start = 0;\n    }\n    request_window_attention(self->window_id, OPT(enable_audio_bell));\n    if (OPT(visual_bell_duration) > 0.0f) self->start_visual_bell_at = monotonic();\n    CALLBACK(\"on_bell\", NULL);\n}\n\nvoid\nreport_device_attributes(Screen *self, unsigned int mode, char start_modifier) {\n    if (mode == 0) {\n        switch(start_modifier) {\n            case 0:\n                CALLBACK(\"on_da1\", NULL);\n                break;\n            case '>':\n                write_escape_code_to_child(self, ESC_CSI, \">1;\" xstr(PRIMARY_VERSION) \";\" xstr(SECONDARY_VERSION) \"c\");  // VT-220 + primary version + secondary version\n                break;\n        }\n    }\n}\n\nvoid\nscreen_xtversion(Screen *self, unsigned int mode) {\n    if (mode == 0) {\n        write_escape_code_to_child(self, ESC_DCS, \">|kitty(\" XT_VERSION \")\");\n    }\n}\n\nvoid\nscreen_report_size(Screen *self, unsigned which, unsigned modifier) {\n    char buf[32] = {0};\n    unsigned code = 0, width = 0, height = 0;\n    switch(which) {\n        case 14:\n            code = 4;\n            width = self->cell_size.width * self->columns;\n            height = self->cell_size.height * self->lines;\n            if (modifier == 2 && self->window_id) {\n                OSWindow *osw = os_window_for_kitty_window(self->window_id);\n                if (osw) {\n                    int w, h, fw, fh; get_os_window_size(osw, &w, &h, &fw, &fh);\n                    width = fw; height = fh;\n                }\n            }\n            break;\n        case 16:\n            code = 6;\n            width = self->cell_size.width;\n            height = self->cell_size.height;\n            break;\n        case 18:\n            code = 8;\n            width = self->columns;\n            height = self->lines;\n            break;\n    }\n    if (code) {\n        snprintf(buf, sizeof(buf), \"%u;%u;%ut\", code, height, width);\n        write_escape_code_to_child(self, ESC_CSI, buf);\n    }\n}\n\nvoid\nscreen_manipulate_title_stack(Screen *self, unsigned int op, unsigned int which) {\n    CALLBACK(\"manipulate_title_stack\", \"OOO\",\n        op == 23 ? Py_True : Py_False,\n        which == 0 || which == 2 ? Py_True : Py_False,\n        which == 0 || which == 1 ? Py_True : Py_False\n    );\n}\n\nvoid\nreport_device_status(Screen *self, unsigned int which, bool private) {\n    unsigned int x, y;\n    static char buf[64];\n    switch(which) {\n        case 5:  // device status\n            write_escape_code_to_child(self, ESC_CSI, \"0n\");\n            break;\n        case 6:  // cursor position\n            x = self->cursor->x; y = self->cursor->y;\n            if (x >= self->columns) {\n                if (y < self->lines - 1) { x = 0; y++; }\n                else x--;\n            }\n            if (self->modes.mDECOM) y -= MAX(y, self->margin_top);\n            // 1-based indexing\n            int sz = snprintf(buf, sizeof(buf) - 1, \"%s%u;%uR\", (private ? \"?\": \"\"), y + 1, x + 1);\n            if (sz > 0) write_escape_code_to_child(self, ESC_CSI, buf);\n            break;\n        case 996: // https://github.com/contour-terminal/contour/blob/master/docs/vt-extensions/color-palette-update-notifications.md\n            if (private) {\n                CALLBACK(\"report_color_scheme_preference\", NULL);\n            } break;\n    }\n}\n\nvoid\nreport_mode_status(Screen *self, unsigned int which, bool private) {\n    unsigned int q = private ? which << 5 : which;\n    unsigned int ans = 0;\n    char buf[50] = {0};\n    switch(q) {\n#define KNOWN_MODE(x) \\\n        case x: \\\n            ans = self->modes.m##x ? 1 : 2; break;\n        KNOWN_MODE(LNM);\n        KNOWN_MODE(IRM);\n        KNOWN_MODE(DECTCEM);\n        KNOWN_MODE(DECSCNM);\n        KNOWN_MODE(DECOM);\n        KNOWN_MODE(DECAWM);\n        KNOWN_MODE(DECCOLM);\n        KNOWN_MODE(DECARM);\n        KNOWN_MODE(DECCKM);\n        KNOWN_MODE(BRACKETED_PASTE);\n        KNOWN_MODE(FOCUS_TRACKING);\n        KNOWN_MODE(COLOR_PREFERENCE_NOTIFICATION);\n        KNOWN_MODE(INBAND_RESIZE_NOTIFICATION);\n        KNOWN_MODE(PASTE_EVENTS);\n#undef KNOWN_MODE\n        case ALTERNATE_SCREEN:\n            ans = self->linebuf == self->alt_linebuf ? 1 : 2; break;\n        case MOUSE_BUTTON_TRACKING:\n            ans = self->modes.mouse_tracking_mode == BUTTON_MODE ? 1 : 2; break;\n        case MOUSE_MOTION_TRACKING:\n            ans = self->modes.mouse_tracking_mode == MOTION_MODE ? 1 : 2; break;\n        case MOUSE_MOVE_TRACKING:\n            ans = self->modes.mouse_tracking_mode == ANY_MODE ? 1 : 2; break;\n        case MOUSE_SGR_MODE:\n            ans = self->modes.mouse_tracking_protocol == SGR_PROTOCOL ? 1 : 2; break;\n        case MOUSE_UTF8_MODE:\n            ans = self->modes.mouse_tracking_protocol == UTF8_PROTOCOL ? 1 : 2; break;\n        case MOUSE_SGR_PIXEL_MODE:\n            ans = self->modes.mouse_tracking_protocol == SGR_PIXEL_PROTOCOL ? 1 : 2; break;\n        case PENDING_UPDATE:\n            ans = self->paused_rendering.expires_at ? 1 : 2; break;\n    }\n    int sz = snprintf(buf, sizeof(buf) - 1, \"%s%u;%u$y\", (private ? \"?\" : \"\"), which, ans);\n    if (sz > 0) write_escape_code_to_child(self, ESC_CSI, buf);\n}\n\nvoid\nscreen_set_margins(Screen *self, unsigned int top, unsigned int bottom) {\n    if (!top) top = 1;\n    if (!bottom) bottom = self->lines;\n    top = MIN(self->lines, top);\n    bottom = MIN(self->lines, bottom);\n    top--; bottom--;  // 1 based indexing\n    if (bottom > top) {\n        // Even though VT102 and VT220 require DECSTBM to ignore regions\n        // of width less than 2, some programs (like aptitude for example)\n        // rely on it. Practicality beats purity.\n        self->margin_top = top; self->margin_bottom = bottom;\n        // The cursor moves to the home position when the top and\n        // bottom margins of the scrolling region (DECSTBM) changes.\n        screen_cursor_position(self, 1, 1);\n    }\n}\n\nvoid\nscreen_set_cursor(Screen *self, unsigned int mode, uint8_t secondary) {\n    uint8_t shape; bool blink;\n    switch(secondary) {\n        case 0: // DECLL\n            break;\n        case '\"':  // DECCSA\n            break;\n        case ' ': // DECSCUSR\n            shape = 0; blink = true;\n            if (mode > 0) {\n                blink = mode % 2;\n                shape = (mode < 3) ? CURSOR_BLOCK : (mode < 5) ? CURSOR_UNDERLINE : (mode < 7) ? CURSOR_BEAM : NO_CURSOR_SHAPE;\n            }\n            self->cursor->shape = shape; self->cursor->non_blinking = !blink;\n            break;\n    }\n}\n\n#define NAME multi_cursor_map\n#define KEY_TY index_type\n#define VAL_TY uint8_t\n#include \"kitty-verstable.h\"\n\nunsigned\nscreen_multi_cursor_count(const Screen *self) {\n    return self->paused_rendering.expires_at ? self->paused_rendering.extra_cursors.count : self->extra_cursors.count;\n}\n\nvoid\nscreen_multi_cursor(Screen *self, int queried_shape, int *params, unsigned num_params) {\n    // printf(\"%d;\", queried_shape); for (unsigned i = 0; i < num_params; i++) {printf(\"%d:\", params[i]);} printf(\"\\n\");\n    if (!num_params) {\n#define pr(...) { int n = snprintf(p, sz - (p - buf), __VA_ARGS__); if (n >= 0 && (unsigned)n <= (sz - (p - buf))) p += n; }\n        if (params == NULL) {\n            write_escape_code_to_child(self, ESC_CSI, \">1;2;3;29;30;40;100;101 q\");\n        } else if (queried_shape == 100) {\n            size_t sz = self->extra_cursors.count * 32 + 64;\n            RAII_ALLOC(char, buf, malloc(sz)); sz -= 4;\n            if (buf) {\n                char *p = buf + snprintf(buf, sz, \">100;\");\n                for (unsigned i = 0; i < self->extra_cursors.count; i++) {\n                    index_type cell = self->extra_cursors.locations[i].cell, shape = self->extra_cursors.locations[i].shape;\n                    index_type y = cell / self->columns, x = cell - (y * self->columns);\n                    pr(\"%d:2:%u:%u;\", shape > 3 ? 29 : (int)shape, y+1, x+1);\n                }\n                if (*(p-1) == ';') p--;\n                *(p++) = ' '; *(p++) = 'q'; *(p++) = 0;\n                write_escape_code_to_child(self, ESC_CSI, buf);\n            }\n        } else if (queried_shape == 101) {\n            char buf[64], *p = buf; size_t sz = sizeof(buf);\n            pr(\">101;30:\"); DynamicColor ecc = self->extra_cursors.color.text;\n#define o() switch(ecc.type) { \\\n                case COLOR_NOT_SET: pr(\"0\"); break; \\\n                case COLOR_IS_SPECIAL: pr(\"1\"); break; \\\n                case COLOR_IS_INDEX: pr(\"5:%u\", ecc.rgb & 0xff); break;  \\\n                case COLOR_IS_RGB:  pr(\"2:%u:%u:%u\", (ecc.rgb >> 16) & 0xff, (ecc.rgb >> 8) & 0xff, ecc.rgb & 0xff); break; \\\n            } \\\n\n            o(); pr(\";40:\"); ecc = self->extra_cursors.color.cursor; o();\n#undef o\n            pr(\" q\");\n            write_escape_code_to_child(self, ESC_CSI, buf);\n        }\n        return;\n#undef pr\n    }\n    if (queried_shape == 30 || queried_shape == 40) {\n        DynamicColor *ecc = queried_shape == 40 ? &self->extra_cursors.color.cursor : &self->extra_cursors.color.text;\n        self->extra_cursors.dirty = true;\n        switch (params[0]) {\n            case 0: ecc->type = COLOR_NOT_SET; break;\n            case 1: ecc->type = COLOR_IS_SPECIAL; break;\n            case 2: if (num_params > 3) {\n                ecc->type = COLOR_IS_RGB;\n                ecc->rgb = ((params[1] & 0xff) << 16) | ((params[2] & 0xff) << 8) | (params[3] & 0xff);\n            } break;\n            case 5: if (num_params > 1) {\n                ecc->type = COLOR_IS_INDEX;\n                ecc->rgb = params[1] & 0xff;\n            } break;\n        }\n        return;\n    }\n    uint8_t shape = 0;\n    switch(queried_shape) {\n        case 29: shape = 4; break;\n        case 0: case 1: case 2: case 3: shape = queried_shape; break;\n        default: return;\n    }\n    self->extra_cursors.dirty = true;\n    int type = params[0]; params++; num_params--;\n    int extra[2];\n    switch (type) {\n    case 0:\n        extra[0] = MIN(self->cursor->y, self->lines-1) + 1;\n        extra[1] = MIN(self->cursor->x, self->columns-1) + 1;\n        params = extra; num_params = 2;\n        /* fallthrough */\n    case 2: {\n        multi_cursor_map s; vt_init(&s);\n        for (unsigned i = 0; i < self->extra_cursors.count; i++) {\n            vt_insert(&s, self->extra_cursors.locations[i].cell, self->extra_cursors.locations[i].shape);\n        }\n        for (unsigned i = 0; i+1 < num_params; i+=2) {\n            index_type y = params[i]-1, x = params[i+1]-1;\n            if (!shape) { vt_erase(&s, y * self->columns + x); }\n            else if (y < self->lines && x < self->columns) vt_insert(&s, y * self->columns + x, shape);\n        }\n        self->extra_cursors.count = vt_size(&s);\n        ensure_space_for(&self->extra_cursors, locations, ExtraCursor, self->extra_cursors.count, capacity, 20 * 80, false);\n        self->extra_cursors.count = 0;\n        vt_create_for_loop(multi_cursor_map_itr, i, &s) {\n            self->extra_cursors.locations[self->extra_cursors.count++] = (ExtraCursor){\n                .shape = i.data->val, .cell = i.data->key};\n        }\n        vt_cleanup(&s);\n    } break;\n    case 4: {\n        if (num_params < 4) {  // full screen\n            switch(shape) {\n                default: self->extra_cursors.count = 0; break;\n                case 1: case 2: case 3: case 4:\n                    ensure_space_for(&self->extra_cursors, locations, ExtraCursor, self->lines * self->columns, capacity, 20 * 80, false);\n                    self->extra_cursors.count = self->lines * self->columns;\n                    for (index_type cell = 0; cell < self->lines * self->columns; cell++) {\n                        self->extra_cursors.locations[cell].shape = shape;\n                        self->extra_cursors.locations[cell].cell = cell;\n                    }\n                    break;\n            }\n            break;\n        }\n        unsigned count = 0;\n        for (unsigned i = 0; i < self->extra_cursors.count; i++) {\n            bool in_some_region = false;\n            index_type y = self->extra_cursors.locations[i].cell / self->columns, x = self->extra_cursors.locations[i].cell - (self->columns * y);\n            for (unsigned i = 0; i + 3 < num_params && !in_some_region; i += 4) {\n                index_type top = params[i]-1, left = params[i+1]-1, bottom = params[i+2]-1, right = params[i+3]-1;\n                in_some_region = top <= y && y <= bottom && left <= x && x <= right;\n            }\n            if (!in_some_region) self->extra_cursors.locations[count++] = self->extra_cursors.locations[i];\n        }\n        self->extra_cursors.count = count;\n        if (shape) {\n            for (unsigned i = 0; i + 3 < num_params; i += 4) {\n                index_type top = params[i]-1, left = params[i+1]-1, bottom = params[i+2]-1, right = params[i+3]-1;\n                bottom = MIN(bottom, self->lines-1); right = MIN(right, self->columns -1);\n                if (right < left || bottom < top) continue;\n                size_t xnum = right + 1 - left, ynum = bottom + 1 - top;\n                ensure_space_for(&self->extra_cursors, locations, ExtraCursor,\n                        self->extra_cursors.count + xnum * ynum, capacity, 20 * 80, false);\n                for (index_type y = top; y <= bottom; y++) {\n                    for (index_type x = left; x <= right; x++) {\n                        self->extra_cursors.locations[self->extra_cursors.count++] = (ExtraCursor){\n                            .shape=shape, .cell=y*self->columns + x};\n                    }\n                }\n            }\n        }\n    } break;\n    }\n}\n\nvoid\nset_title(Screen *self, PyObject *title) {\n    CALLBACK(\"title_changed\", \"O\", title);\n}\n\nvoid\nosc_context(Screen *self, PyObject *ctx) {\n    CALLBACK(\"osc_context\", \"O\", ctx);\n}\n\nvoid\ndesktop_notify(Screen *self, unsigned int osc_code, PyObject *data) {\n    CALLBACK(\"desktop_notify\", \"IO\", osc_code, data);\n}\n\nvoid\nset_icon(Screen *self, PyObject *icon) {\n    CALLBACK(\"icon_changed\", \"O\", icon);\n}\n\nvoid\nset_dynamic_color(Screen *self, unsigned int code, PyObject *color) {\n    if (color == NULL) { CALLBACK(\"set_dynamic_color\", \"I\", code); }\n    else { CALLBACK(\"set_dynamic_color\", \"IO\", code, color); }\n}\n\nvoid\ncolor_control(Screen *self, unsigned int code, PyObject *spec) {\n    if (spec) CALLBACK(\"color_control\", \"IO\", code, spec);\n}\n\nvoid\nclipboard_control(Screen *self, int code, PyObject *data) {\n    if (code == 52 || code == -52) { CALLBACK(\"clipboard_control\", \"OO\", data, code == -52 ? Py_True: Py_False); }\n    else { CALLBACK(\"clipboard_control\", \"OO\", data, Py_None);}\n}\n\nvoid\nfile_transmission(Screen *self, PyObject *data) {\n    CALLBACK(\"file_transmission\", \"O\", data);\n}\n\nstatic void\nparse_prompt_mark(Screen *self, char *buf, PromptKind *pk) {\n    char *saveptr, *str = buf;\n    while (true) {\n        const char *token = strtok_r(str, \";\", &saveptr); str = NULL;\n        if (token == NULL) return;\n        if (strcmp(token, \"k=s\") == 0) *pk = SECONDARY_PROMPT;\n        else if (strcmp(token, \"redraw=0\") == 0) self->prompt_settings.redraws_prompts_at_all = 0;\n        else if (strcmp(token, \"special_key=1\") == 0) self->prompt_settings.uses_special_keys_for_cursor_movement = 1;\n        else if (strcmp(token, \"click_events=1\") == 0) {\n            self->prompt_settings.supports_click_events = 1;\n            self->prompt_settings.relative_click_events = 0;\n        } else if (strcmp(token, \"click_events=2\") == 0) {\n            self->prompt_settings.supports_click_events = 1;\n            self->prompt_settings.relative_click_events = 1;\n        }\n    }\n}\n\nvoid\nshell_prompt_marking(Screen *self, char *buf) {\n    if (self->cursor->y < self->lines) {\n        char ch = buf[0];\n        switch (ch) {\n            case 'A': {\n                PromptKind pk = PROMPT_START;\n                self->prompt_settings.redraws_prompts_at_all = 1;\n                self->prompt_settings.uses_special_keys_for_cursor_movement = 0;\n                parse_prompt_mark(self, buf+1, &pk);\n                self->linebuf->line_attrs[self->cursor->y].prompt_kind = pk;\n                if (pk == PROMPT_START) CALLBACK(\"cmd_output_marking\", \"O\", Py_False);\n            } break;\n            case 'C': {\n                self->linebuf->line_attrs[self->cursor->y].prompt_kind = OUTPUT_START;\n                const char *cmdline = \"\";\n                if (strstr(buf + 1, \";cmdline\") == buf + 1) {\n                    cmdline = buf + 2;\n                }\n                RAII_PyObject(c, PyUnicode_DecodeUTF8(cmdline, strlen(cmdline), \"replace\"));\n                if (c) { CALLBACK(\"cmd_output_marking\", \"OO\", Py_True, c); }\n                else PyErr_Print();\n            } break;\n            case 'D': {\n                const char *exit_status = buf[1] == ';' ? buf + 2 : \"\";\n                CALLBACK(\"cmd_output_marking\", \"Os\", Py_None, exit_status);\n            } break;\n        }\n    }\n}\n\nstatic bool\nscreen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump, int scroll_offset) {\n    if (self->linebuf != self->main_linebuf) return false;\n    unsigned int old = self->scrolled_by;\n    if (num_of_prompts_to_jump == 0) {\n        if (!self->last_visited_prompt.is_set || self->last_visited_prompt.scrolled_by > self->historybuf->count || self->last_visited_prompt.y >= self->lines) return false;\n        self->scrolled_by = self->last_visited_prompt.scrolled_by;\n    } else {\n        int delta = num_of_prompts_to_jump < 0 ? -1 : 1;\n        num_of_prompts_to_jump = num_of_prompts_to_jump < 0 ? -num_of_prompts_to_jump : num_of_prompts_to_jump;\n        int y = -self->scrolled_by;\n#define ensure_y_ok if (y >= (int)self->lines || -y > (int)self->historybuf->count) return false;\n        ensure_y_ok;\n        y += scroll_offset;\n        while (num_of_prompts_to_jump) {\n            y += delta;\n            ensure_y_ok;\n            if (range_line_(self, y)->attrs.prompt_kind == PROMPT_START) {\n                num_of_prompts_to_jump--;\n            }\n        }\n        y -= scroll_offset;\n#undef ensure_y_ok\n        self->scrolled_by = y >= 0 ? 0 : -y;\n        screen_set_last_visited_prompt(self, 0);\n    }\n    if (old != self->scrolled_by) {\n        reset_pixel_scroll(self, 0);\n        dirty_scroll(self);\n    }\n    return old != self->scrolled_by;\n}\n\nvoid\nset_color_table_color(Screen *self, unsigned int code, PyObject *color) {\n    if (color == NULL) { CALLBACK(\"set_color_table_color\", \"I\", code); }\n    else { CALLBACK(\"set_color_table_color\", \"IO\", code, color); }\n}\n\nvoid\nprocess_cwd_notification(Screen *self, unsigned int code, const char *data, size_t sz) {\n    if (code == 7) {\n        PyObject *x = PyBytes_FromStringAndSize(data, sz);\n        if (x) {\n            Py_CLEAR(self->last_reported_cwd);\n            self->last_reported_cwd = x;\n        } else { PyErr_Clear(); }\n    }  // we ignore OSC 6 document reporting as we dont have a use for it\n}\n\nbool\nscreen_send_signal_for_key(Screen *self, char key) {\n    int ret = 0;\n    if (self->callbacks != Py_None) {\n        int cchar = key;\n        PyObject *callback_ret = PyObject_CallMethod(self->callbacks, \"send_signal_for_key\", \"c\", cchar);\n        if (callback_ret) {\n            ret = PyObject_IsTrue(callback_ret);\n            Py_DECREF(callback_ret);\n        } else { PyErr_Print(); }\n    }\n    return ret != 0;\n}\n\nvoid\nscreen_push_colors(Screen *self, unsigned int idx) {\n    if (colorprofile_push_colors(self->color_profile, idx)) self->color_profile->dirty = true;\n}\n\nvoid\nscreen_pop_colors(Screen *self, unsigned int idx) {\n    color_type bg_before = colorprofile_to_color(self->color_profile, self->color_profile->overridden.default_bg, self->color_profile->configured.default_bg).rgb;\n    if (colorprofile_pop_colors(self->color_profile, idx)) {\n        self->color_profile->dirty = true;\n        color_type bg_after = colorprofile_to_color(self->color_profile, self->color_profile->overridden.default_bg, self->color_profile->configured.default_bg).rgb;\n        CALLBACK(\"color_profile_popped\", \"O\", bg_before == bg_after ? Py_False : Py_True);\n    }\n}\n\nvoid\nscreen_report_color_stack(Screen *self) {\n    unsigned int idx, count;\n    colorprofile_report_stack(self->color_profile, &idx, &count);\n    char buf[128] = {0};\n    snprintf(buf, arraysz(buf), \"%u;%u#Q\", idx, count);\n    write_escape_code_to_child(self, ESC_CSI, buf);\n}\n\nvoid screen_handle_kitty_dcs(Screen *self, const char *callback_name, PyObject *cmd) {\n    CALLBACK(callback_name, \"O\", cmd);\n}\n\nvoid\nscreen_request_capabilities(Screen *self, char c, const char *query) {\n    static char buf[128];\n    int shape = 0;\n    switch(c) {\n        case '+': {\n            CALLBACK(\"request_capabilities\", \"s\", query);\n        } break;\n        case '$':\n            // report status DECRQSS\n            if (strcmp(\" q\", query) == 0) {\n                // cursor shape DECSCUSR\n                switch(self->cursor->shape) {\n                    case NO_CURSOR_SHAPE: case CURSOR_HOLLOW: case NUM_OF_CURSOR_SHAPES:\n                        shape = 1; break;\n                    case CURSOR_BLOCK:\n                        shape = self->cursor->non_blinking ? 2 : 0; break;\n                    case CURSOR_UNDERLINE:\n                        shape = self->cursor->non_blinking ? 4 : 3; break;\n                    case CURSOR_BEAM:\n                        shape = self->cursor->non_blinking ? 6 : 5; break;\n                }\n                shape = snprintf(buf, sizeof(buf), \"1$r%d q\", shape);\n            } else if (strcmp(\"m\", query) == 0) {\n                // SGR\n                const char *s = cursor_as_sgr(self->cursor);\n                if (s && s[0]) shape = snprintf(buf, sizeof(buf), \"1$r0;%sm\", s);\n                else shape = snprintf(buf, sizeof(buf), \"1$rm\");\n            } else if (strcmp(\"r\", query) == 0) { // DECSTBM\n                shape = snprintf(buf, sizeof(buf), \"1$r%u;%ur\", self->margin_top + 1, self->margin_bottom + 1);\n            } else if (strcmp(\"*x\", query) == 0) { // DECSACE\n                shape = snprintf(buf, sizeof(buf), \"1$r%d*x\", self->modes.mDECSACE ? 1 : 0);\n            } else {\n                shape = snprintf(buf, sizeof(buf), \"0$r\");\n            }\n            if (shape > 0) write_escape_code_to_child(self, ESC_DCS, buf);\n            break;\n    }\n}\n\n// }}}\n\n// Rendering {{{\n\nvoid\nscreen_check_pause_rendering(Screen *self, monotonic_t now) {\n    if (self->paused_rendering.expires_at && now > self->paused_rendering.expires_at) screen_pause_rendering(self, false, 0);\n}\n\nstatic bool\ncopy_selections(Selections *dest, const Selections *src) {\n    if (dest->capacity < src->count) {\n        dest->items = realloc(dest->items, sizeof(dest->items[0]) * src->count);\n        if (!dest->items) { dest->capacity = 0; dest->count = 0; return false; }\n        dest->capacity = src->count;\n    }\n    dest->count = src->count;\n    for (unsigned i = 0; i < dest->count; i++) memcpy(dest->items + i, src->items + i, sizeof(dest->items[0]));\n    dest->last_rendered_count = src->last_rendered_count;\n    return true;\n}\n\nbool\nscreen_pause_rendering(Screen *self, bool pause, int for_in_ms) {\n    if (!pause) {\n        if (!self->paused_rendering.expires_at) return false;\n        self->paused_rendering.expires_at = 0;\n        // ensure cell data is updated on GPU\n        self->is_dirty = true;\n        // ensure selection data is updated on GPU\n        self->selections.last_rendered_count = SIZE_MAX; self->url_ranges.last_rendered_count = SIZE_MAX;\n        self->extra_cursors.dirty = true;\n        // free grman data\n        grman_pause_rendering(NULL, self->paused_rendering.grman);\n        // free extra cursors\n        free(self->paused_rendering.extra_cursors.locations); zero_at_ptr(&self->paused_rendering.extra_cursors);\n        return true;\n    }\n    if (self->paused_rendering.expires_at) return false;\n    if (!self->paused_rendering.grman) self->paused_rendering.grman = grman_alloc(true);\n    if (!self->paused_rendering.grman) return false;\n    if (for_in_ms <= 0) for_in_ms = 2000;\n    self->paused_rendering.expires_at = monotonic() + ms_to_monotonic_t(for_in_ms);\n    self->paused_rendering.inverted = self->modes.mDECSCNM;\n    self->paused_rendering.scrolled_by = self->scrolled_by;\n    self->paused_rendering.cell_data_updated = false;\n    self->paused_rendering.cursor_visible = self->modes.mDECTCEM;\n    memcpy(&self->paused_rendering.cursor, self->cursor, sizeof(self->paused_rendering.cursor));\n    memcpy(&self->paused_rendering.color_profile, self->color_profile, sizeof(self->paused_rendering.color_profile));\n    if (!self->paused_rendering.linebuf || self->paused_rendering.linebuf->xnum != self->columns || self->paused_rendering.linebuf->ynum != self->lines) {\n        if (self->paused_rendering.linebuf) Py_CLEAR(self->paused_rendering.linebuf);\n        self->paused_rendering.linebuf = alloc_linebuf(self->lines, self->columns, self->text_cache);\n        if (!self->paused_rendering.linebuf) { PyErr_Clear(); self->paused_rendering.expires_at = 0; return false; }\n    }\n    for (index_type y = 0; y < self->lines; y++) {\n        Line *src = visual_line_(self, y);\n        linebuf_init_line(self->paused_rendering.linebuf, y);\n        copy_line(src, self->paused_rendering.linebuf->line);\n        self->paused_rendering.linebuf->line_attrs[y] = src->attrs;\n    }\n    copy_selections(&self->paused_rendering.selections, &self->selections);\n    copy_selections(&self->paused_rendering.url_ranges, &self->url_ranges);\n    if (self->extra_cursors.count) {\n        self->paused_rendering.extra_cursors.locations = calloc(self->extra_cursors.count, sizeof(self->extra_cursors.locations[0]));\n        if (self->paused_rendering.extra_cursors.locations) {\n            self->paused_rendering.extra_cursors.count = self->extra_cursors.count;\n            self->paused_rendering.extra_cursors.dirty = self->extra_cursors.dirty;\n            memcpy(self->paused_rendering.extra_cursors.locations, self->extra_cursors.locations, sizeof(self->extra_cursors.locations[0]) * self->extra_cursors.count);\n        }\n    }\n    grman_pause_rendering(self->grman, self->paused_rendering.grman);\n    return true;\n}\n\nstatic color_type\neffective_cell_edge_color(char_type ch, color_type fg, color_type bg, bool is_left_edge) {\n    START_ALLOW_CASE_RANGE\n    if (ch == 0x2588) return fg; // full block\n    if (is_left_edge) {\n        switch (ch) {\n            case 0x2589 ... 0x258f: // left eighth blocks\n            case 0xe0b0: case 0xe0b4: case 0xe0b8: case 0xe0bc:  // powerline blocks\n            case 0x1fb6a: // 🭪\n                return fg;\n        }\n    } else {\n        switch (ch) {\n            case 0x2590:  // right half block\n            case 0x1fb87 ... 0x1fb8b:  // eighth right blocks\n            case 0xe0b2: case 0xe0b6: case 0xe0ba: case 0xe0be:\n            case 0x1fb68: // 🭨\n                return fg;\n        }\n    }\n    return bg;\n    END_ALLOW_CASE_RANGE\n}\n\n\nbool\nget_line_edge_colors(Screen *self, color_type *left, color_type *right) {\n    // Return the color at the left and right edges of the line with the cursor on it\n    Line *line = range_line_(self, self->cursor->y);\n    if (!line) return false;\n    color_type left_cell_fg = OPT(foreground), left_cell_bg = OPT(background), right_cell_bg = OPT(background), right_cell_fg = OPT(foreground);\n    index_type cell_color_x = 0;\n    char_type left_char = line_get_char(line, cell_color_x);\n    bool reversed = false;\n    colors_for_cell(line, self->color_profile, &cell_color_x, &left_cell_fg, &left_cell_bg, &reversed);\n    if (line->xnum > 0) cell_color_x = line->xnum - 1;\n    char_type right_char = line_get_char(line, cell_color_x);\n    colors_for_cell(line, self->color_profile, &cell_color_x, &right_cell_fg, &right_cell_bg, &reversed);\n    *left = effective_cell_edge_color(left_char, left_cell_fg, left_cell_bg, true);\n    *right = effective_cell_edge_color(right_char, right_cell_fg, right_cell_bg, false);\n    return true;\n}\n\n\nstatic void\nupdate_line_data(Line *line, unsigned int dest_y, uint8_t *data) {\n    size_t base = sizeof(GPUCell) * dest_y * line->xnum;\n    memcpy(data + base, line->gpu_cells, line->xnum * sizeof(GPUCell));\n}\n\nstatic void\nupdate_line_data_blank(unsigned xnum, unsigned int dest_y, uint8_t *data) {\n    const size_t sz = xnum * sizeof(GPUCell);\n    memset(data + sz * dest_y, 0, sz);\n}\n\n\nstatic Line*\nrender_line_for_virtual_y(Screen *self, int y, Line *line, index_type *lnum, bool *is_history) {\n    if (y < (int)self->scrolled_by) {\n        int idx = (int)self->scrolled_by - 1 - y;\n        if (idx >= 0 && (unsigned)idx < self->historybuf->count) {\n            historybuf_init_line(self->historybuf, idx, line);\n            line->xnum = self->columns;\n            line->ynum = (index_type)MIN(MAX(y, 0), (int)self->lines - 1);\n            *lnum = (index_type)idx;\n            *is_history = true;\n            return line;\n        }\n        return NULL;\n    }\n    y -= self->scrolled_by;\n    if (y >= 0 && y < (int)self->lines) {\n        linebuf_init_line_at(self->linebuf, (index_type)y, line);\n        *lnum = (index_type)y;\n        *is_history = false;\n        return line;\n    }\n    return NULL;\n}\n\n\nstatic void\nscreen_reset_dirty(Screen *self) {\n    self->is_dirty = false;\n    self->history_line_added_count = 0;\n}\n\nstatic bool\nscreen_has_marker(Screen *self) {\n    return self->marker != NULL;\n}\n\nstatic uint32_t diacritic_to_rowcolumn(char_type c) {\n    return diacritic_to_num(c);\n}\n\nstatic uint32_t color_to_id(color_type c) {\n    // Just take 24 most significant bits of the color. This works both for\n    // 24-bit and 8-bit colors.\n    return (c >> 8) & 0xffffff;\n}\n\n// Scan the line and create cell images in place of unicode placeholders\n// reserved for image placement.\nstatic void\nscreen_render_line_graphics(Screen *self, Line *line, int32_t row) {\n    // If there are no image placeholders now, no need to rescan the line.\n    if (!line->attrs.has_image_placeholders)\n        return;\n    // Remove existing images.\n    grman_remove_cell_images(self->grman, row, row);\n    // The placeholders might be erased. We will update the attribute.\n    line->attrs.has_image_placeholders = false;\n    index_type i;\n    uint32_t run_length = 0;\n    uint32_t prev_img_id_lower24bits = 0;\n    uint32_t prev_placement_id = 0;\n    // Note that the following values are 1-based, zero means unknown or incorrect.\n    uint32_t prev_img_id_higher8bits = 0;\n    uint32_t prev_img_row = 0;\n    uint32_t prev_img_col = 0;\n    for (i = 0; i < line->xnum; i++) {\n        CPUCell *cpu_cell = line->cpu_cells + i;\n        GPUCell *gpu_cell = line->gpu_cells + i;\n        uint32_t cur_img_id_lower24bits = 0;\n        uint32_t cur_placement_id = 0;\n        uint32_t cur_img_id_higher8bits = 0;\n        uint32_t cur_img_row = 0;\n        uint32_t cur_img_col = 0;\n        if (cell_first_char(cpu_cell, self->text_cache) == IMAGE_PLACEHOLDER_CHAR) {\n            line->attrs.has_image_placeholders = true;\n            // The lower 24 bits of the image id are encoded in the foreground\n            // color, and the placement id is (optionally) in the underline color.\n            cur_img_id_lower24bits = color_to_id(gpu_cell->fg);\n            cur_placement_id = color_to_id(gpu_cell->decoration_fg);\n            text_in_cell(cpu_cell, self->text_cache, self->lc);\n            // If the char has diacritics, use them as row and column indices.\n            if (self->lc->count > 1 && self->lc->chars[1])\n                cur_img_row = diacritic_to_rowcolumn(self->lc->chars[1]);\n            if (self->lc->count > 2 && self->lc->chars[2])\n                cur_img_col = diacritic_to_rowcolumn(self->lc->chars[2]);\n            // The third diacritic is used to encode the higher 8 bits of the\n            // image id (optional).\n            if (self->lc->count > 3 && self->lc->chars[3])\n                cur_img_id_higher8bits = diacritic_to_rowcolumn(self->lc->chars[3]);\n        }\n        // The current run is continued if the lower 24 bits of the image id and\n        // the placement id are the same as in the previous cell and everything\n        // else is unknown or compatible with the previous cell.\n        if (run_length > 0 && cur_img_id_lower24bits == prev_img_id_lower24bits &&\n            cur_placement_id == prev_placement_id &&\n            (!cur_img_row || cur_img_row == prev_img_row) &&\n            (!cur_img_col || cur_img_col == prev_img_col + 1) &&\n            (!cur_img_id_higher8bits || cur_img_id_higher8bits == prev_img_id_higher8bits)) {\n            // This cell continues the current run.\n            run_length++;\n            // If some values are unknown, infer them from the previous cell.\n            cur_img_row = MAX(prev_img_row, 1u);\n            cur_img_col = prev_img_col + 1;\n            cur_img_id_higher8bits = MAX(prev_img_id_higher8bits, 1u);\n        } else {\n            // This cell breaks the current run. Render the current run if it\n            // has a non-zero length.\n            if (run_length > 0) {\n                uint32_t img_id = prev_img_id_lower24bits | (prev_img_id_higher8bits - 1) << 24;\n                grman_put_cell_image(\n                    self->grman, row, i - run_length, img_id,\n                    prev_placement_id, prev_img_col - run_length,\n                    prev_img_row - 1, run_length, 1, self->cell_size);\n            }\n            // Start a new run.\n            if (cell_first_char(cpu_cell, self->text_cache) == IMAGE_PLACEHOLDER_CHAR) {\n                run_length = 1;\n                if (!cur_img_col) cur_img_col = 1;\n                if (!cur_img_row) cur_img_row = 1;\n                if (!cur_img_id_higher8bits) cur_img_id_higher8bits = 1;\n            }\n        }\n        prev_img_id_lower24bits = cur_img_id_lower24bits;\n        prev_img_id_higher8bits = cur_img_id_higher8bits;\n        prev_placement_id = cur_placement_id;\n        prev_img_row = cur_img_row;\n        prev_img_col = cur_img_col;\n    }\n    if (run_length > 0) {\n        // Render the last run.\n        uint32_t img_id = prev_img_id_lower24bits | (prev_img_id_higher8bits - 1) << 24;\n        grman_put_cell_image(self->grman, row, i - run_length, img_id,\n                             prev_placement_id, prev_img_col - run_length,\n                             prev_img_row - 1, run_length, 1, self->cell_size);\n    }\n}\n\n// This functions is similar to screen_update_cell_data, but it only updates\n// line graphics (cell images) and then marks lines as clean. It's used\n// exclusively for testing unicode placeholders.\nstatic void\nscreen_update_only_line_graphics_data(Screen *self) {\n    unsigned int history_line_added_count = self->history_line_added_count;\n    index_type lnum = 0;\n    if (self->scrolled_by) self->scrolled_by = MIN(self->scrolled_by + history_line_added_count, self->historybuf->count);\n    screen_reset_dirty(self);\n    self->scroll_changed = false;\n    const unsigned int render_lines = render_lines_for_screen(self);\n    const int render_row_offset = pixel_scroll_enabled(self);\n    Line line = {.text_cache = self->text_cache};\n    for (unsigned int render_row = 0; render_row < render_lines; render_row++) {\n        const int virtual_y = (int)render_row - render_row_offset;\n        bool is_history = false;\n        Line *linep = render_line_for_virtual_y(self, virtual_y, &line, &lnum, &is_history);\n        if (linep) {\n            screen_render_line_graphics(self, linep, virtual_y - (int)self->scrolled_by);\n            if (linep->attrs.has_dirty_text) {\n                if (is_history) historybuf_mark_line_clean(self->historybuf, lnum);\n                else linebuf_mark_line_clean(self->linebuf, lnum);\n            }\n        }\n    }\n}\n\nvoid\nscreen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_data, bool cursor_has_moved) {\n    if (self->paused_rendering.expires_at) {\n        if (!self->paused_rendering.cell_data_updated) {\n            LineBuf *linebuf = self->paused_rendering.linebuf;\n            for (index_type y = 0; y < self->lines; y++) {\n                linebuf_init_line(linebuf, y);\n                if (linebuf->line->attrs.has_dirty_text) {\n                    render_line(fonts_data, linebuf->line, y, &self->paused_rendering.cursor, self->disable_ligatures, self->lc);\n                    screen_render_line_graphics(self, linebuf->line, y);\n                    if (linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(\n                            self->marker, linebuf->line, &self->as_ansi_buf);\n                    linebuf_mark_line_clean(linebuf, y);\n                }\n                update_line_data(linebuf->line, y, address);\n            }\n        }\n        return;\n    }\n    const bool is_overlay_active = screen_is_overlay_active(self);\n    unsigned int history_line_added_count = self->history_line_added_count;\n    screen_reset_dirty(self);\n    update_overlay_position(self);\n    const bool force_history_render = pixel_scroll_enabled(self) && self->scroll_changed;\n    if (self->scrolled_by) self->scrolled_by = MIN(self->scrolled_by + history_line_added_count, self->historybuf->count);\n    self->scroll_changed = false;\n    const unsigned int render_lines = render_lines_for_screen(self);\n    const int render_row_offset = pixel_scroll_enabled(self);\n    Line line = {.text_cache = self->text_cache};\n    for (unsigned int render_row = 0; render_row < render_lines; render_row++) {\n        const int virtual_y = (int)render_row - render_row_offset;\n        bool is_history = false;\n        index_type lnum = 0;\n        Line *linep = render_line_for_virtual_y(self, virtual_y, &line, &lnum, &is_history);\n        if (linep == NULL) {\n            update_line_data_blank(self->columns, render_row, address);\n            continue;\n        }\n        if (is_history) {\n            // we render line graphics even if the line is not dirty as graphics commands received after\n            // the unicode placeholder was first scanned can alter it.\n            screen_render_line_graphics(self, linep, virtual_y - (int)self->scrolled_by);\n            if (force_history_render || linep->attrs.has_dirty_text) {\n                render_line(fonts_data, linep, lnum, self->cursor, self->disable_ligatures, self->lc);\n                if (screen_has_marker(self)) mark_text_in_line(self->marker, linep, &self->as_ansi_buf);\n                historybuf_mark_line_clean(self->historybuf, lnum);\n            }\n        } else {\n            if (linep->attrs.has_dirty_text ||\n                (cursor_has_moved && (self->cursor->y == lnum || self->last_rendered.cursor.y == lnum))) {\n                render_line(fonts_data, linep, lnum, self->cursor, self->disable_ligatures, self->lc);\n                screen_render_line_graphics(self, linep, virtual_y - (int)self->scrolled_by);\n                if (linep->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line(\n                        self->marker, linep, &self->as_ansi_buf);\n                if (is_overlay_active && lnum == self->overlay_line.ynum) render_overlay_line(self, linep, fonts_data);\n                linebuf_mark_line_clean(self->linebuf, lnum);\n            }\n        }\n        update_line_data(linep, render_row, address);\n    }\n    if (is_overlay_active && self->overlay_line.ynum + self->scrolled_by < self->lines) {\n        if (self->overlay_line.is_dirty) {\n            linebuf_init_line(self->linebuf, self->overlay_line.ynum);\n            render_overlay_line(self, self->linebuf->line, fonts_data);\n        }\n        update_overlay_line_data(self, address);\n    }\n}\n\nstatic bool\nselection_boundary_less_than(const SelectionBoundary *a, const SelectionBoundary *b) {\n    // y -values must be absolutized (aka adjusted with scrolled_by)\n    // this means the oldest line has the highest value and is thus the least\n    if (a->y > b->y) return true;\n    if (a->y < b->y) return false;\n    if (a->x < b->x) return true;\n    if (a->x > b->x) return false;\n    if (a->in_left_half_of_cell && !b->in_left_half_of_cell) return true;\n    return false;\n}\n\nstatic index_type\nnum_cells_between_selection_boundaries(const Screen *self, const SelectionBoundary *a, const SelectionBoundary *b) {\n    const SelectionBoundary *before, *after;\n    if (selection_boundary_less_than(a, b)) { before = a; after = b; }\n    else { before = b; after = a; }\n    index_type ans = 0;\n    if (before->y + 1 < after->y) ans += self->columns * (after->y - before->y - 1);\n    if (before->y == after->y) ans += after->x - before->x;\n    else ans += (self->columns - before->x) + after->x;\n    return ans;\n}\n\nstatic index_type\nnum_lines_between_selection_boundaries(const SelectionBoundary *a, const SelectionBoundary *b) {\n    const SelectionBoundary *before, *after;\n    if (selection_boundary_less_than(a, b)) { before = a; after = b; }\n    else { before = b; after = a; }\n    return before->y - after->y;\n}\n\nstatic bool\nselection_is_left_to_right(const Selection *self) {\n    return self->input_start.x < self->input_current.x || (self->input_start.x == self->input_current.x && self->input_start.in_left_half_of_cell);\n}\n\nstatic void\niteration_data(const Selection *sel, IterationData *ans, unsigned x_limit, int min_y, unsigned add_scrolled_by) {\n    memset(ans, 0, sizeof(IterationData));\n    const SelectionBoundary *start = &sel->start, *end = &sel->end;\n    int start_y = (int)start->y - sel->start_scrolled_by, end_y = (int)end->y - sel->end_scrolled_by;\n    // empty selection\n    if (start->x == end->x && start_y == end_y && start->in_left_half_of_cell == end->in_left_half_of_cell) return;\n\n    if (sel->rectangle_select) {\n        // empty selection\n        if (start->x == end->x && (!start->in_left_half_of_cell || end->in_left_half_of_cell)) return;\n\n        ans->y = MIN(start_y, end_y); ans->y_limit = MAX(start_y, end_y) + 1;\n        index_type x, x_limit;\n        bool left_to_right = selection_is_left_to_right(sel);\n\n        if (start->x == end->x) {\n            x = start->x; x_limit = start->x + 1;\n        } else {\n            if (left_to_right) {\n                x = start->x + (start->in_left_half_of_cell ? 0 : 1);\n                x_limit = 1 + end->x + (end->in_left_half_of_cell ? -1: 0);\n            } else {\n                x = end->x + (end->in_left_half_of_cell ? 0 : 1);\n                x_limit = 1 + start->x + (start->in_left_half_of_cell ? -1 : 0);\n            }\n        }\n        ans->first.x = x; ans->body.x = x; ans->last.x = x;\n        ans->first.x_limit = x_limit; ans->body.x_limit = x_limit; ans->last.x_limit = x_limit;\n    } else {\n        index_type line_limit = x_limit;\n\n        if (start_y == end_y) {\n            if (start->x == end->x) {\n                if (start->in_left_half_of_cell && !end->in_left_half_of_cell) {\n                    // single cell selection\n                    ans->first.x = start->x; ans->body.x = start->x; ans->last.x = start->x;\n                    ans->first.x_limit = start->x + 1; ans->body.x_limit = start->x + 1; ans->last.x_limit = start->x + 1;\n                } else return; // empty selection\n            }\n            // single line selection\n            else if (start->x <= end->x) {\n                ans->first.x = start->x + (start->in_left_half_of_cell ? 0 : 1);\n                ans->first.x_limit = 1 + end->x + (end->in_left_half_of_cell ? -1 : 0);\n            } else {\n                ans->first.x = end->x + (end->in_left_half_of_cell ? 0 : 1);\n                ans->first.x_limit = 1 + start->x + (start->in_left_half_of_cell ? -1 : 0);\n            }\n        } else if (start_y < end_y) { // downwards\n            ans->body.x_limit = line_limit;\n            ans->first.x_limit = line_limit;\n            ans->first.x = start->x + (start->in_left_half_of_cell ? 0 : 1);\n            ans->last.x_limit = 1 + end->x + (end->in_left_half_of_cell ? -1 : 0);\n        } else { // upwards\n            ans->body.x_limit = line_limit;\n            ans->first.x_limit = line_limit;\n            ans->first.x = end->x + (end->in_left_half_of_cell ? 0 : 1);\n            ans->last.x_limit = 1 + start->x + (start->in_left_half_of_cell ? -1 : 0);\n        }\n        ans->y = MIN(start_y, end_y); ans->y_limit = MAX(start_y, end_y) + 1;\n\n    }\n    ans->y += add_scrolled_by; ans->y_limit += add_scrolled_by;\n    ans->y = MAX(ans->y, min_y);\n    ans->y_limit = MAX(ans->y, ans->y_limit);  // iteration is from y to y_limit\n}\n\nstatic XRange\nxrange_for_iteration(const IterationData *idata, const int y, const Line *line) {\n    XRange ans = {.x_limit=xlimit_for_line(line)};\n    if (y == idata->y) {\n        ans.x_limit = MIN(idata->first.x_limit, ans.x_limit);\n        ans.x = idata->first.x;\n    } else if (y == idata->y_limit - 1) {\n        ans.x_limit = MIN(idata->last.x_limit, ans.x_limit);\n        ans.x = idata->last.x;\n    } else {\n        ans.x_limit = MIN(idata->body.x_limit, ans.x_limit);\n        ans.x = idata->body.x;\n    }\n    return ans;\n}\n\nstatic XRange\nxrange_for_iteration_with_multicells(const IterationData *idata, const int y, const Line *line) {\n    XRange ans = xrange_for_iteration(idata, y, line);\n    if (ans.x_limit > ans.x) {\n        CPUCell *c; index_type ml;\n        if (ans.x && (c = &line->cpu_cells[ans.x])->is_multicell && c->x) ans.x = ans.x > c->x ? ans.x - c->x : 0;\n        if (ans.x_limit < line->xnum && (c = &line->cpu_cells[ans.x_limit-1])->is_multicell && c->x + 1u < (ml = mcd_x_limit(c))) {\n            ans.x_limit += ml - 1 - c->x; if (ans.x_limit > line->xnum) ans.x_limit = line->xnum;\n        }\n    }\n    return ans;\n}\n\nstatic bool\niteration_data_is_empty(const Screen *self, const IterationData *idata) {\n    if (idata->y >= idata->y_limit) return true;\n    index_type xl = MIN(idata->first.x_limit, self->columns);\n    if (idata->first.x < xl) return false;\n    xl = MIN(idata->body.x_limit, self->columns);\n    if (idata->body.x < xl) return false;\n    xl = MIN(idata->last.x_limit, self->columns);\n    if (idata->last.x < xl) return false;\n    return true;\n}\n\nstatic void\napply_selection(Screen *self, uint8_t *data, Selection *s, uint8_t set_mask, int extra_leading_rows) {\n    iteration_data(s, &s->last_rendered, self->columns, -self->historybuf->count, 0);\n    Line *line;\n    const int y_min = MAX(-extra_leading_rows - (int)self->scrolled_by, s->last_rendered.y),\n          y_limit = MIN(s->last_rendered.y_limit, (int)self->lines - (int)self->scrolled_by);\n    for (int y = y_min; y < y_limit; y++) {\n        if (self->paused_rendering.expires_at) {\n            linebuf_init_line(self->paused_rendering.linebuf, y);\n            line = self->paused_rendering.linebuf->line;\n        } else {\n            line = checked_range_line(self, y);\n            if (!line) continue;\n        }\n        const int y_in_data = (y + extra_leading_rows + self->scrolled_by);\n        uint8_t *line_start = data + self->columns * y_in_data;\n        XRange xr = xrange_for_iteration_with_multicells(&s->last_rendered, y, line);\n        for (index_type x = xr.x; x < xr.x_limit; x++) {\n            line_start[x] |= set_mask;\n            CPUCell *c = &line->cpu_cells[x];\n            if (c->is_multicell && c->scale > 1) {\n                for (int ym = MAX(0, y_in_data - c->y); ym < y_in_data; ym++) data[self->columns * ym + x] |= set_mask;\n                for (int ym = y_in_data + 1; ym < MIN((int)self->lines + extra_leading_rows, y_in_data + c->scale - c->y); ym++)\n                    data[self->columns * ym + x] |= set_mask;\n            }\n        }\n    }\n    s->last_rendered.y = MAX(0, s->last_rendered.y);\n}\n\nbool\nscreen_has_selection(Screen *self) {\n    IterationData idata;\n    for (size_t i = 0; i < self->selections.count; i++) {\n        Selection *s = self->selections.items + i;\n        if (!is_selection_empty(s)) {\n            iteration_data(s, &idata, self->columns, -self->historybuf->count, self->scrolled_by);\n            if (!iteration_data_is_empty(self, &idata)) return true;\n        }\n    }\n    return false;\n}\n\nvoid\nscreen_apply_selection(Screen *self, void *address_, size_t size) {\n    uint8_t *address = address_;\n    memset(address, 0, size);\n    const int offset = pixel_scroll_enabled(self);\n    Selections *sel = self->paused_rendering.expires_at ? &self->paused_rendering.selections : &self->selections;\n    for (size_t i = 0; i < sel->count; i++) apply_selection(self, address, sel->items + i, 1, offset);\n    sel->last_rendered_count = sel->count;\n    sel = self->paused_rendering.expires_at ? &self->paused_rendering.url_ranges : &self->url_ranges;\n    for (size_t i = 0; i < sel->count; i++) {\n        Selection *s = sel->items + i;\n        if (OPT(underline_hyperlinks) == UNDERLINE_NEVER && s->is_hyperlink) continue;\n        apply_selection(self, address, s, 2, offset);\n    }\n    sel->last_rendered_count = sel->count;\n    address += offset * self->columns; size -= offset * self->columns;\n    ExtraCursors *ec = self->paused_rendering.expires_at ? &self->paused_rendering.extra_cursors : &self->extra_cursors;\n    for (unsigned i = 0; i < ec->count; i++) {\n        if (ec->locations[i].cell < size) address[ec->locations[i].cell] |= (ec->locations[i].shape & 7) << 2;\n    }\n    ec->dirty = false;\n}\n\nstatic index_type\nlimit_without_trailing_whitespace(const Line *line, index_type limit) {\n    if (!limit) return limit;\n    if (limit > line->xnum) limit = line->xnum;\n    while (limit > 0) {\n        const CPUCell *cell = line->cpu_cells + limit - 1;\n        if (cell->is_multicell && (cell->x || cell->y)) { limit--; continue; }\n        if (cell->ch_is_idx) break;\n        switch(cell->ch_or_idx) {\n            case ' ': case '\\t': case '\\n': case '\\r': case 0: break;\n            default:\n                return limit;\n        }\n        limit--;\n    }\n    return limit;\n}\n\nstatic void\nflag_selection_to_extract_text(Screen *self, const Selection *s, int *miny, int *y_limit) {\n    IterationData idata;\n    bool has_history = self->linebuf == self->main_linebuf;\n    iteration_data(s, &idata, self->columns, has_history ? -self->historybuf->count : 0, 0);\n    Line *line;\n    *miny = idata.y; *y_limit = MIN(idata.y_limit, (int)self->lines);\n    if (*miny >= *y_limit) return;\n    static const int max_scale = ( (1u << SCALE_BITS) - 1u);\n    for (int y = idata.y - max_scale; y < *y_limit; y++) {\n        line = checked_range_line(self, y);\n        if (line) for (index_type x = 0; x < line->xnum; x++) line->cpu_cells[x].temp_flag = 0;\n    }\n    Line temp = {.xnum=self->columns, .text_cache=self->text_cache};\n    for (int y = idata.y; y < *y_limit; y++) {\n        range_line(self, y, &temp);\n        CPUCell *c;\n        XRange xr = xrange_for_iteration_with_multicells(&idata, y, &temp);\n        for (index_type x = xr.x; x < xr.x_limit; x++) {\n            c = temp.cpu_cells + x;\n            c->temp_flag = 1;\n            if (c->is_multicell && c->y) {\n                for (int ym = y - c->y; ym < y; ym++) {\n                    line = checked_range_line(self, ym);\n                    if (line) {\n                        line->cpu_cells[x].temp_flag = 1;\n                        *miny = MIN(*miny, ym);\n                    }\n                }\n            }\n        }\n    }\n    // remove lines from bottom that contain only y > 0 cells from multicell\n    while (*y_limit > *miny) {\n        range_line(self, *y_limit - 1, &temp);\n        for (index_type x = 0; x < temp.xnum; x++) {\n            if (temp.cpu_cells[x].temp_flag && temp.cpu_cells[x].ch_and_idx && (!temp.cpu_cells[x].is_multicell || !temp.cpu_cells[x].y)) return;\n        }\n        (*y_limit)--;\n    }\n}\n\nstatic PyObject*\ntext_for_range(Screen *self, const Selection *sel, bool insert_newlines, bool strip_trailing_whitespace) {\n    int min_y, y_limit;\n    flag_selection_to_extract_text(self, sel, &min_y, &y_limit);\n    if (min_y >= y_limit) return PyTuple_New(0);\n    size_t before = self->as_ansi_buf.len;\n    RAII_PyObject(ans, PyTuple_New(y_limit - min_y));\n    RAII_PyObject(nl, PyUnicode_FromString(\"\\n\"));\n    RAII_PyObject(empty, PyUnicode_FromString(\"\"));\n    if (!ans || !nl || !empty) return NULL;\n    for (int i = 0, y = min_y; y < y_limit; y++, i++) {\n        Line *line = range_line_(self, y);\n        index_type x_limit = line->xnum, x_start = 0;\n        while (x_limit && !line->cpu_cells[x_limit - 1].temp_flag) x_limit--;\n        while (x_start < x_limit && !line->cpu_cells[x_start].temp_flag) x_start++;\n        bool is_only_whitespace_line = false;\n        if (strip_trailing_whitespace) {\n            index_type new_limit = limit_without_trailing_whitespace(line, x_limit);\n            if (new_limit != x_limit) {\n                x_limit = new_limit;\n                is_only_whitespace_line = new_limit <= x_start;\n            }\n        }\n        const bool is_first_line = y == min_y, is_last_line = y + 1 >= y_limit;\n        const bool add_trailing_newline = insert_newlines && !is_last_line;\n        PyObject *text = NULL;\n        if (x_limit <= x_start && (is_only_whitespace_line || line_is_empty(line))) {\n            // we want a newline on only whitespace lines even if they are continued\n            text = add_trailing_newline ? nl : empty;\n            text = Py_NewRef(text);\n        } else {\n            while (x_start < x_limit) {\n                index_type end = x_start;\n                while (end < x_limit && line->cpu_cells[end].temp_flag) end++;\n                if (!unicode_in_range(line, x_start, end, true, add_trailing_newline, false, !is_first_line, &self->as_ansi_buf)) return PyErr_NoMemory();\n                x_start = MAX(x_start + 1, end);\n            }\n            text = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, self->as_ansi_buf.buf + before, self->as_ansi_buf.len - before);\n        }\n        self->as_ansi_buf.len = before;\n        if (!text) return NULL;\n        PyTuple_SET_ITEM(ans, i, text);\n    }\n    return Py_NewRef(ans);\n}\n\nstatic PyObject*\nansi_for_range(Screen *self, const Selection *sel, bool insert_newlines, bool strip_trailing_whitespace) {\n    int min_y, y_limit;\n    flag_selection_to_extract_text(self, sel, &min_y, &y_limit);\n    if (min_y >= y_limit) return PyTuple_New(0);\n    ANSILineState s = {.output_buf=&self->as_ansi_buf};\n    s.output_buf->active_hyperlink_id = 0; s.output_buf->len = 0;\n    RAII_PyObject(ans, PyTuple_New(y_limit - min_y + 1));\n    RAII_PyObject(nl, PyUnicode_FromString(\"\\n\"));\n    RAII_PyObject(empty_string, PyUnicode_FromString(\"\"));\n    if (!ans || !nl || !empty_string) return NULL;\n    bool has_escape_codes = false;\n    bool need_newline = false;\n    for (int i = 0, y = min_y; y < y_limit && i < PyTuple_GET_SIZE(ans) - 1; y++, i++) {\n        const bool is_first_line = y == min_y;\n        s.output_buf->len = 0;\n        Line *line = range_line_(self, y);\n        index_type x_limit = line->xnum, x_start = 0;\n        while (x_limit && !line->cpu_cells[x_limit - 1].temp_flag) x_limit--;\n        while (x_start < x_limit && !line->cpu_cells[x_start].temp_flag) x_start++;\n        bool is_only_whitespace_line = false;\n        if (strip_trailing_whitespace) {\n            index_type new_limit = limit_without_trailing_whitespace(line, x_limit);\n            if (new_limit != x_limit) {\n                x_limit = new_limit;\n                is_only_whitespace_line = new_limit <= x_start;\n            }\n        }\n\n        if (x_limit <= x_start && (is_only_whitespace_line || line_is_empty(line))) {\n            // we want a newline on only whitespace lines even if they are continued\n            if (insert_newlines) need_newline = true;\n            PyTuple_SET_ITEM(ans, i, Py_NewRef(need_newline ? nl : empty_string));\n        } else {\n            char_type prefix_char = need_newline ? '\\n' : 0;\n            while (x_start < x_limit) {\n                index_type end = x_start;\n                while (end < x_limit && line->cpu_cells[end].temp_flag) end++;\n                if (line_as_ansi(line, &s, x_start, end, prefix_char, !is_first_line)) has_escape_codes = true;\n                need_newline = insert_newlines && !line->cpu_cells[line->xnum-1].next_char_was_wrapped;\n                prefix_char = 0;\n                x_start = MAX(x_start + 1, end);\n            }\n            PyObject *t = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, s.output_buf->buf, s.output_buf->len);\n            if (!t) return NULL;\n            PyTuple_SET_ITEM(ans, i, t);\n        }\n    }\n    PyObject *t = PyUnicode_FromFormat(\"%s%s\", has_escape_codes ? \"\\x1b[m\" : \"\", s.output_buf->active_hyperlink_id ? \"\\x1b]8;;\\x1b\\\\\" : \"\");\n    if (!t) return NULL;\n    PyTuple_SET_ITEM(ans, PyTuple_GET_SIZE(ans) - 1, t);\n    return Py_NewRef(ans);\n}\n\n\nstatic hyperlink_id_type\nhyperlink_id_for_range(Screen *self, const Selection *sel) {\n    IterationData idata;\n    iteration_data(sel, &idata, self->columns, -self->historybuf->count, 0);\n    for (int y = idata.y; y < idata.y_limit && y < (int)self->lines; y++) {\n        Line *line = range_line_(self, y);\n        XRange xr = xrange_for_iteration(&idata, y, line);\n        for (index_type x = xr.x; x < xr.x_limit; x++) {\n            if (line->cpu_cells[x].hyperlink_id) return line->cpu_cells[x].hyperlink_id;\n        }\n    }\n    return 0;\n}\n\nstatic PyObject*\nextend_tuple(PyObject *a, PyObject *b) {\n    Py_ssize_t bs = PyTuple_GET_SIZE(b);\n    if (bs < 1) return a;\n    Py_ssize_t off = PyTuple_GET_SIZE(a);\n    if (_PyTuple_Resize(&a, off + bs) != 0) return NULL;\n    for (Py_ssize_t y = 0; y < bs; y++) {\n        PyObject *t = PyTuple_GET_ITEM(b, y);\n        Py_INCREF(t);\n        PyTuple_SET_ITEM(a, off + y, t);\n    }\n    return a;\n}\n\nstatic PyObject*\ncurrent_url_text(Screen *self, PyObject *args UNUSED) {\n    RAII_PyObject(empty_string, PyUnicode_FromString(\"\"));\n    if (!empty_string) return NULL;\n    RAII_PyObject(ans, NULL);\n    for (size_t i = 0; i < self->url_ranges.count; i++) {\n        Selection *s = self->url_ranges.items + i;\n        if (!is_selection_empty(s)) {\n            RAII_PyObject(temp, text_for_range(self, s, false, false));\n            if (!temp) return NULL;\n            RAII_PyObject(text, PyUnicode_Join(empty_string, temp));\n            if (!text) return NULL;\n            if (ans) {\n                PyObject *t = PyUnicode_Concat(ans, text);\n                if (!t) return NULL;\n                Py_CLEAR(ans); ans = t;\n            } else ans = Py_NewRef(text);\n        }\n    }\n    return Py_NewRef(ans ? ans : Py_None);\n}\n\n\nbool\nscreen_open_url(Screen *self) {\n    if (!self->url_ranges.count) return false;\n    hyperlink_id_type hid = hyperlink_id_for_range(self, self->url_ranges.items);\n    if (hid) {\n        const char *url = get_hyperlink_for_id(self->hyperlink_pool, hid, true);\n        if (url) {\n            CALLBACK(\"open_url\", \"sH\", url, hid);\n            return true;\n        }\n    }\n    PyObject *text = current_url_text(self, NULL);\n    if (!text) {\n        if (PyErr_Occurred()) PyErr_Print();\n        return false;\n    }\n    bool found = false;\n    if (PyUnicode_Check(text)) {\n        CALLBACK(\"open_url\", \"OH\", text, 0);\n        found = true;\n    }\n    Py_CLEAR(text);\n    return found;\n}\n\n// }}}\n\n// URLs {{{\nstatic index_type\nget_last_hostname_char_pos(Line *line, index_type url_start) {\n    index_type slash_count = 0;\n    while (url_start < line->xnum) {\n        index_type pos = find_char(line, url_start, '/');\n        if (pos >= line->xnum) return line->xnum;\n        if (++slash_count > 2) return prev_char_pos(line, pos, 1);\n        url_start = next_char_pos(line, pos, 1);\n    }\n    return line->xnum;\n}\n\nstatic void\nextend_url(Screen *screen, Line *line, index_type *x, index_type *y, char_type sentinel, bool newlines_allowed, index_type last_hostname_char_pos, index_type scale) {\n    unsigned int count = 0;\n    bool has_newline = false;\n    index_type orig_y = *y;\n    while (count++ < 20) {\n        bool in_hostname = last_hostname_char_pos >= line->xnum;\n        has_newline = !line->cpu_cells[line->xnum-1].next_char_was_wrapped;\n        if (next_char_pos(line, *x, 1) < line->xnum || (!newlines_allowed && has_newline)) break;\n        bool next_line_starts_with_url_chars = false;\n        line = screen_visual_line(screen, *y + 2 * scale);\n        if (line) {\n            next_line_starts_with_url_chars = line_startswith_url_chars(line, in_hostname, screen->lc);\n            has_newline = !visual_line_is_continued(screen, *y + 2 * scale);\n            if (next_line_starts_with_url_chars && has_newline && !newlines_allowed) next_line_starts_with_url_chars = false;\n            if (sentinel && next_line_starts_with_url_chars && cell_is_char(line->cpu_cells, sentinel)) next_line_starts_with_url_chars = false;\n        }\n        line = screen_visual_line(screen, *y + scale);\n        if (!line) break;\n        if (in_hostname) {\n            last_hostname_char_pos = find_char(line, 0, '/');\n            if (last_hostname_char_pos < line->xnum) {\n                last_hostname_char_pos = prev_char_pos(line, last_hostname_char_pos, 1);\n                if (last_hostname_char_pos >= line->xnum) in_hostname = false;\n            }\n        }\n        index_type new_x = line_url_end_at(line, 0, false, sentinel, next_line_starts_with_url_chars, in_hostname, last_hostname_char_pos, screen->lc);\n        if (!new_x && !line_startswith_url_chars(line, in_hostname, screen->lc)) break;\n        *y += scale; *x = new_x;\n    }\n    if (sentinel && *x == 0 && *y > orig_y) {\n        line = screen_visual_line(screen, *y);\n        if (line && cell_is_char(line->cpu_cells, sentinel)) {\n            *y -= scale;\n            *x = line->xnum - 1;\n            if (line->cpu_cells[*x].is_multicell) *x -= line->cpu_cells[*x].x;\n        }\n    }\n}\n\nint\nscreen_detect_url(Screen *screen, unsigned int x, unsigned int y) {\n    bool has_url = false;\n    index_type url_start, url_end = 0;\n    Line *line = screen_visual_line(screen, y);\n    if (!line || x >= screen->columns) return 0;\n    if (line->cpu_cells[x].is_multicell && line->cpu_cells[x].scale > 1 && line->cpu_cells[x].y) {\n        if (line->cpu_cells[x].y > y) return 0;\n        y -= line->cpu_cells[x].y;\n        line = screen_visual_line(screen, y);\n    }\n    if (line->cpu_cells[x].is_multicell && line->cpu_cells[x].x) x = x > line->cpu_cells[x].x ? x - line->cpu_cells[x].x : 0;\n    hyperlink_id_type hid;\n    if ((hid = line->cpu_cells[x].hyperlink_id)) {\n        screen_mark_hyperlink(screen, x, y);\n        return hid;\n    }\n    char_type sentinel = 0;\n    const bool newlines_allowed = !is_excluded_from_url('\\n');\n    index_type last_hostname_char_pos = screen->columns;\n    url_start = line_url_start_at(line, x, screen->lc);\n    Line scratch = {.xnum=line->xnum, .text_cache=line->text_cache};\n    index_type scale = 1;\n    if (url_start < line->xnum) {\n        scale = cell_scale(line->cpu_cells + url_start);\n        bool next_line_starts_with_url_chars = false;\n        if (y + scale < screen->lines) {\n            visual_line(screen, y + scale, &scratch);\n            next_line_starts_with_url_chars = line_startswith_url_chars(&scratch, last_hostname_char_pos >= line->xnum, screen->lc);\n            if (next_line_starts_with_url_chars && !newlines_allowed && !visual_line_is_continued(screen, y + scale)) next_line_starts_with_url_chars = false;\n        }\n        sentinel = get_url_sentinel(line, url_start);\n        last_hostname_char_pos = get_last_hostname_char_pos(line, url_start);\n        url_end = line_url_end_at(line, x, true, sentinel, next_line_starts_with_url_chars, x <= last_hostname_char_pos, last_hostname_char_pos, screen->lc);\n    }\n    has_url = url_end > url_start;\n    if (has_url) {\n        index_type y_extended = y;\n        extend_url(screen, line, &url_end, &y_extended, sentinel, newlines_allowed, last_hostname_char_pos, scale);\n        screen_mark_url(screen, url_start, y, url_end, y_extended);\n    } else {\n        screen_mark_url(screen, 0, 0, 0, 0);\n    }\n    return has_url ? -1 : 0;\n}\n\n// }}}\n\n// IME Overlay {{{\nbool\nscreen_is_overlay_active(Screen *self) {\n    return self->overlay_line.is_active;\n}\n\nstatic void\ndeactivate_overlay_line(Screen *self) {\n    if (self->overlay_line.is_active && self->overlay_line.xnum && self->overlay_line.ynum < self->lines) {\n        self->is_dirty = true;\n        linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);\n    }\n    self->overlay_line.is_active = false;\n    self->overlay_line.is_dirty = true;\n    self->overlay_line.ynum = 0;\n    self->overlay_line.xstart = 0;\n    self->overlay_line.cursor_x = 0;\n}\n\nvoid\nscreen_update_overlay_text(Screen *self, const char *utf8_text) {\n    if (screen_is_overlay_active(self)) deactivate_overlay_line(self);\n    if (!utf8_text || !utf8_text[0]) return;\n    PyObject *text = PyUnicode_FromString(utf8_text);\n    if (!text) return;\n    Py_XDECREF(self->overlay_line.overlay_text);\n    // Calculate the total number of cells for initial overlay cursor position\n    RAII_PyObject(text_len, wcswidth_std(NULL, text));\n    self->overlay_line.overlay_text = text;\n    self->overlay_line.is_active = true;\n    self->overlay_line.is_dirty = true;\n    self->overlay_line.xstart = self->cursor->x;\n    self->overlay_line.xnum = !text_len ? 0 : PyLong_AsLong(text_len);\n    self->overlay_line.text_len = self->overlay_line.xnum;\n    self->overlay_line.cursor_x = MIN(self->overlay_line.xstart + self->overlay_line.xnum, self->columns);\n    self->overlay_line.ynum = self->cursor->y;\n    cursor_copy_to(self->cursor, &(self->overlay_line.original_line.cursor));\n    linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);\n    self->is_dirty = true;\n    // Since we are typing, scroll to the bottom\n    if (self->scrolled_by != 0) {\n        self->scrolled_by = 0;\n        reset_pixel_scroll(self, 0);\n        dirty_scroll(self);\n    }\n}\n\nstatic void\nscreen_draw_overlay_line(Screen *self) {\n    if (!self->overlay_line.overlay_text) return;\n    // Right-align the overlay to ensure that the pre-edit text just entered is visible when the cursor is near the end of the line.\n    index_type xstart = self->overlay_line.text_len <= self->columns ? self->columns - self->overlay_line.text_len : 0;\n    if (self->overlay_line.xstart < xstart) xstart = self->overlay_line.xstart;\n    index_type columns_exceeded = self->overlay_line.text_len <= self->columns ? 0 : self->overlay_line.text_len - self->columns;\n    bool orig_line_wrap_mode = self->modes.mDECAWM;\n    bool orig_cursor_enable_mode = self->modes.mDECTCEM;\n    bool orig_insert_replace_mode = self->modes.mIRM;\n    self->modes.mDECAWM = false;\n    self->modes.mDECTCEM = false;\n    self->modes.mIRM = false;\n    Cursor *orig_cursor = self->cursor;\n    self->cursor = &(self->overlay_line.original_line.cursor);\n    self->cursor->sgr.reverse ^= true;\n    self->cursor->x = xstart;\n    self->cursor->y = self->overlay_line.ynum;\n    self->overlay_line.xnum = 0;\n    if (xstart > 0) {\n        // remove any multicell characters temporarily that intersect the left boundary,\n        // the characters are not actually removed, just deleted on this line\n        CPUCell *c = self->linebuf->line->cpu_cells + xstart;\n        while (c->is_multicell && c->x && c < self->linebuf->line->cpu_cells + self->columns) {\n            c->is_multicell = false; c->ch_or_idx = ' '; c->ch_is_idx = false;\n            c++;\n        }\n    }\n    index_type before;\n    const int kind = PyUnicode_KIND(self->overlay_line.overlay_text);\n    const void *data = PyUnicode_DATA(self->overlay_line.overlay_text);\n    const Py_ssize_t sz = PyUnicode_GET_LENGTH(self->overlay_line.overlay_text);\n    for (Py_ssize_t pos = 0; pos < sz; pos++) {\n        before = self->cursor->x;\n        draw_codepoint(self, PyUnicode_READ(kind, data, pos));\n        index_type len = self->cursor->x - before;\n        if (columns_exceeded > 0) {\n            // Reset the cursor to maintain right alignment when the overlay exceeds the screen width.\n            if (columns_exceeded > len) {\n                columns_exceeded -= len;\n                len = 0;\n            } else {\n                len = len > columns_exceeded ? len - columns_exceeded : 0;\n                columns_exceeded = 0;\n                if (len > 0) {\n                    // When the last character is a split multicell, make sure the next character is visible.\n                    CPUCell *c = self->linebuf->line->cpu_cells + len - 1;\n                    if (c->is_multicell) {\n                        if (c->x < mcd_x_limit(c) - 1) {\n                            do {\n                                c->is_multicell = false; c->ch_is_idx = false; c->ch_or_idx = ' ';\n                                if (!c->x) break;\n                                c--;\n                            } while(c->is_multicell && c >= self->linebuf->line->cpu_cells);\n                        }\n                    }\n                }\n            }\n            self->cursor->x = len;\n        }\n        self->overlay_line.xnum += len;\n    }\n    self->overlay_line.cursor_x = self->cursor->x;\n    self->cursor->sgr.reverse ^= true;\n    self->cursor = orig_cursor;\n    self->modes.mDECAWM = orig_line_wrap_mode;\n    self->modes.mDECTCEM = orig_cursor_enable_mode;\n    self->modes.mIRM = orig_insert_replace_mode;\n}\n\nstatic void\nupdate_overlay_position(Screen *self) {\n    if (screen_is_overlay_active(self) && screen_is_cursor_visible(self)) {\n        bool cursor_update = false;\n        if (self->cursor->x != self->overlay_line.xstart) {\n            cursor_update = true;\n            self->overlay_line.xstart = self->cursor->x;\n            self->overlay_line.cursor_x = MIN(self->overlay_line.xstart + self->overlay_line.xnum, self->columns);\n        }\n        if (self->cursor->y != self->overlay_line.ynum) {\n            cursor_update = true;\n            linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);\n            self->overlay_line.ynum = self->cursor->y;\n        }\n        if (cursor_update) {\n            linebuf_mark_line_dirty(self->linebuf, self->overlay_line.ynum);\n            self->overlay_line.is_dirty = true;\n            self->is_dirty = true;\n        }\n    }\n}\n\nstatic void\nrender_overlay_line(Screen *self, Line *line, FONTS_DATA_HANDLE fonts_data) {\n#define ol self->overlay_line\n    line_save_cells(line, 0, line->xnum, ol.original_line.gpu_cells, ol.original_line.cpu_cells);\n    screen_draw_overlay_line(self);\n    render_line(fonts_data, line, ol.ynum, self->cursor, self->disable_ligatures, self->lc);\n    line_save_cells(line, 0, line->xnum, ol.gpu_cells, ol.cpu_cells);\n    line_reset_cells(line, 0, line->xnum, ol.original_line.gpu_cells, ol.original_line.cpu_cells);\n    ol.is_dirty = false;\n    const index_type y = MIN(ol.ynum + self->scrolled_by, self->lines - 1);\n    if (ol.last_ime_pos.x != ol.cursor_x || ol.last_ime_pos.y != y) {\n        ol.last_ime_pos.x = ol.cursor_x; ol.last_ime_pos.y = y;\n        update_ime_position_for_window(self->window_id, false, 0);\n    }\n#undef ol\n}\n\nstatic void\nupdate_overlay_line_data(Screen *self, uint8_t *data) {\n    const int render_row_offset = pixel_scroll_enabled(self);\n    const size_t base = sizeof(GPUCell) * (self->overlay_line.ynum + self->scrolled_by + render_row_offset) * self->columns;\n    memcpy(data + base, self->overlay_line.gpu_cells, self->columns * sizeof(GPUCell));\n}\n\n// }}}\n\n// Python interface {{{\n#define WRAP0(name) static PyObject* name(Screen *self, PyObject *a UNUSED) { screen_##name(self); Py_RETURN_NONE; }\n#define WRAP0x(name) static PyObject* xxx_##name(Screen *self, PyObject *a UNUSED) { screen_##name(self); Py_RETURN_NONE; }\n#define WRAP1(name, defval) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; if(!PyArg_ParseTuple(args, \"|I\", &v)) return NULL; screen_##name(self, v); Py_RETURN_NONE; }\n#define WRAP1B(name, defval) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; int b=false; if(!PyArg_ParseTuple(args, \"|Ip\", &v, &b)) return NULL; screen_##name(self, v, b); Py_RETURN_NONE; }\n#define WRAP1E(name, defval, ...) static PyObject* name(Screen *self, PyObject *args) { unsigned int v=defval; if(!PyArg_ParseTuple(args, \"|I\", &v)) return NULL; screen_##name(self, v, __VA_ARGS__); Py_RETURN_NONE; }\n#define WRAP2(name, defval1, defval2) static PyObject* name(Screen *self, PyObject *args) { unsigned int a=defval1, b=defval2; if(!PyArg_ParseTuple(args, \"|II\", &a, &b)) return NULL; screen_##name(self, a, b); Py_RETURN_NONE; }\n#define WRAP2B(name) static PyObject* name(Screen *self, PyObject *args) { unsigned int a, b; int p; if(!PyArg_ParseTuple(args, \"IIp\", &a, &b, &p)) return NULL; screen_##name(self, a, b, (bool)p); Py_RETURN_NONE; }\n\nWRAP0(garbage_collect_hyperlink_pool)\n\nstatic PyObject*\nhas_selection(Screen *self, PyObject *a UNUSED) {\n    if (screen_has_selection(self)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nhyperlinks_as_set(Screen *self, PyObject *args UNUSED) {\n    return screen_hyperlinks_as_set(self);\n}\n\nstatic PyObject*\nhyperlink_for_id(Screen *self, PyObject *val) {\n    unsigned long id = PyLong_AsUnsignedLong(val);\n    if (id > HYPERLINK_MAX_NUMBER) { PyErr_SetString(PyExc_IndexError, \"Out of bounds\"); return NULL; }\n    return Py_BuildValue(\"s\", get_hyperlink_for_id(self->hyperlink_pool, id, true));\n}\n\nstatic Line* get_visual_line(void *x, int y) { return visual_line_(x, y); }\nstatic Line* get_range_line(void *x, int y) { return range_line_(x, y); }\n\nstatic PyObject*\nas_text(Screen *self, PyObject *args) {\n    return as_text_generic(args, self, get_visual_line, self->lines, &self->as_ansi_buf, false);\n}\n\nstatic PyObject*\nas_text_non_visual(Screen *self, PyObject *args) {\n    return as_text_generic(args, self, get_range_line, self->lines, &self->as_ansi_buf, false);\n}\n\nstatic PyObject*\nas_text_for_history_buf(Screen *self, PyObject *args) {\n    return as_text_history_buf(self->historybuf, args, &self->as_ansi_buf);\n}\n\nstatic PyObject*\nas_text_generic_wrapper(Screen *self, PyObject *args, get_line_func get_line) {\n    return as_text_generic(args, self, get_line, self->lines, &self->as_ansi_buf, false);\n}\n\nstatic PyObject*\nas_text_alternate(Screen *self, PyObject *args) {\n    LineBuf *original = self->linebuf;\n    self->linebuf = original == self->main_linebuf ? self->alt_linebuf : self->main_linebuf;\n    PyObject *ans = as_text_generic_wrapper(self, args, get_range_line);\n    self->linebuf = original;\n    return ans;\n}\n\ntypedef struct OutputOffset {\n    Screen *screen;\n    int start;\n    unsigned num_lines;\n    bool reached_upper_limit;\n} OutputOffset;\n\nstatic Line*\nget_line_from_offset(void *x, int y) {\n    OutputOffset *r = x;\n    return range_line_(r->screen, r->start + y);\n}\n\nstatic bool\nfind_cmd_output(Screen *self, OutputOffset *oo, index_type start_screen_y, unsigned int scrolled_by, int direction, bool on_screen_only) {\n    bool found_prompt = false, found_output = false, found_next_prompt = false;\n    int start = 0, end = 0;\n    int init_y = start_screen_y - scrolled_by, y1 = init_y, y2 = init_y;\n    const int upward_limit = -self->historybuf->count;\n    const int downward_limit = self->lines - 1;\n    const int screen_limit = -scrolled_by + downward_limit;\n    Line *line = NULL;\n\n    // find around\n    if (direction == 0) {\n        line = checked_range_line(self, y1);\n        if (line && line->attrs.prompt_kind == PROMPT_START) {\n            found_prompt = true;\n            // change direction to downwards to find command output\n            direction = 1;\n        } else if (line && line->attrs.prompt_kind == OUTPUT_START) {\n            found_output = true; start = y1;\n            found_prompt = true;\n            direction = 1;\n        }\n        y1--; y2++;\n    }\n\n    // find upwards\n    if (direction <= 0) {\n        // find around: only needs to find the first output start\n        // find upwards: find prompt after the output, and the first output\n        while (y1 >= upward_limit) {\n            line = checked_range_line(self, y1);\n            if (line && line->attrs.prompt_kind == PROMPT_START) {\n                if (direction == 0) {\n                    found_prompt = true;\n                    break;\n                }\n                found_next_prompt = true; end = y1;\n            } else if (line && line->attrs.prompt_kind == OUTPUT_START) {\n                found_output = true; start = y1;\n                found_prompt = true;\n                break;\n            }\n            y1--;\n        }\n        if (y1 < upward_limit) {\n            oo->reached_upper_limit = true;\n            found_output = direction != 0; start = upward_limit;\n            found_prompt = direction != 0;\n        }\n    }\n\n    // find downwards\n    if (direction >= 0) {\n        while (y2 <= downward_limit) {\n            if (on_screen_only && !found_output && y2 > screen_limit) break;\n            line = checked_range_line(self, y2);\n            if (line && line->attrs.prompt_kind == PROMPT_START) {\n                if (!found_prompt) {\n                    if (direction == 0) {\n                        found_next_prompt = true; end = y2;\n                        break;\n                    }\n                    found_prompt = true;\n                } else if (!found_output) {\n                    // skip fetching wrapped prompt lines\n                    while (range_line_is_continued(self, y2)) {\n                        y2++;\n                    }\n                } else if (!found_next_prompt) {\n                    found_next_prompt = true; end = y2;\n                    break;\n                }\n            } else if (line && line->attrs.prompt_kind == OUTPUT_START && !found_output) {\n                found_output = true; start = y2;\n                if (!found_prompt) found_prompt = true;\n            }\n            y2++;\n        }\n    }\n\n    if (found_next_prompt) {\n        oo->num_lines = end >= start ? end - start : 0;\n    } else if (found_output) {\n        end = (direction < 0 ? MIN(init_y, downward_limit) : downward_limit) + 1;\n        oo->num_lines = end >= start ? end - start : 0;\n    } else return false;\n    oo->start = start;\n    return oo->num_lines > 0;\n}\n\nstatic PyObject*\nerase_last_command(Screen *self, PyObject *args) {\n    int include_prompt = 1;\n    if (!PyArg_ParseTuple(args, \"|p\", &include_prompt)) return NULL;\n    OutputOffset oo = {.screen=self};\n    if (self->linebuf != self->main_linebuf || !find_cmd_output(self, &oo, self->cursor->y + self->scrolled_by, self->scrolled_by, -1, false)) Py_RETURN_FALSE;\n    if (include_prompt) {\n        int y = oo.start - 1; Line *line;\n        while ((line = checked_range_line(self, y))) {\n            oo.start--; oo.num_lines++; y--;\n            if (line->attrs.prompt_kind == PROMPT_START) break;\n        }\n    }\n    index_type num_lines_to_erase_in_screen = oo.start >= 0 ? oo.num_lines : oo.num_lines + oo.start;\n    num_lines_to_erase_in_screen = MIN(self->cursor->y, num_lines_to_erase_in_screen);\n    if (num_lines_to_erase_in_screen) {\n        screen_delete_lines_impl(self, self->cursor->y - num_lines_to_erase_in_screen, num_lines_to_erase_in_screen, 0, self->lines - 1);\n        self->cursor->y -= num_lines_to_erase_in_screen;\n    }\n    if (oo.num_lines > num_lines_to_erase_in_screen) {\n        index_type num_of_lines_to_erase_from_history = oo.num_lines - num_lines_to_erase_in_screen;\n        historybuf_delete_newest_lines(self->historybuf, num_of_lines_to_erase_from_history);\n    }\n    Py_RETURN_TRUE;\n}\n\nstatic PyObject*\ncmd_output(Screen *self, PyObject *args) {\n    unsigned int which = 0;\n    RAII_PyObject(which_args, PyTuple_GetSlice(args, 0, 1));\n    RAII_PyObject(as_text_args, PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)));\n    if (!which_args || !as_text_args) return NULL;\n    if (!PyArg_ParseTuple(which_args, \"I\", &which)) return NULL;\n    if (self->linebuf != self->main_linebuf) Py_RETURN_NONE;\n    OutputOffset oo = {.screen=self};\n    bool found = false;\n\n    switch (which) {\n        case 0: // last run cmd\n            // When scrolled, the starting point of the search for the last command output\n            // is actually out of the screen, so add the number of scrolled lines\n            found = find_cmd_output(self, &oo, self->cursor->y + self->scrolled_by, self->scrolled_by, -1, false);\n            break;\n        case 1: // first on screen\n            found = find_cmd_output(self, &oo, 0, self->scrolled_by, 1, true);\n            break;\n        case 2: // last visited cmd\n            if (self->last_visited_prompt.scrolled_by <= self->historybuf->count && self->last_visited_prompt.is_set) {\n                found = find_cmd_output(self, &oo, self->last_visited_prompt.y, self->last_visited_prompt.scrolled_by, 0, false);\n            } break;\n        case 3: { // last non-empty output\n            int y = self->cursor->y;\n            Line *line;\n            bool reached_upper_limit = false;\n            while (!found && !reached_upper_limit) {\n                line = checked_range_line(self, y);\n                if (!line || (line->attrs.prompt_kind == OUTPUT_START)) {\n                    int start = line ? y : y + 1; reached_upper_limit = !line;\n                    int y2 = start; unsigned int num_lines = 0;\n                    bool found_content = false;\n                    while ((line = checked_range_line(self, y2)) && line->attrs.prompt_kind != PROMPT_START) {\n                        if (!found_content) found_content = !line_is_empty(line);\n                        num_lines++; y2++;\n                    }\n                    if (found_content) {\n                        found = true;\n                        oo.reached_upper_limit = reached_upper_limit;\n                        oo.start = start; oo.num_lines = num_lines;\n                        break;\n                    }\n                }\n                y--;\n            }\n        } break;\n        default:\n            PyErr_Format(PyExc_KeyError, \"%u is not a valid type of command\", which);\n            return NULL;\n    }\n    if (found) {\n        RAII_PyObject(ret, as_text_generic(as_text_args, &oo, get_line_from_offset, oo.num_lines, &self->as_ansi_buf, false));\n        if (!ret) return NULL;\n    }\n    if (oo.reached_upper_limit && self->linebuf == self->main_linebuf && OPT(scrollback_pager_history_size) > 0) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nbool\nscreen_set_last_visited_prompt(Screen *self, index_type y) {\n    if (y >= self->lines) return false;\n    self->last_visited_prompt.scrolled_by = self->scrolled_by;\n    self->last_visited_prompt.y = y;\n    self->last_visited_prompt.is_set = true;\n    return true;\n}\n\nbool\nscreen_select_cmd_output(Screen *self, index_type y) {\n    if (y >= self->lines) return false;\n    OutputOffset oo = {.screen=self};\n    if (!find_cmd_output(self, &oo, y, self->scrolled_by, 0, true)) return false;\n\n    screen_start_selection(self, 0, y, true, false, EXTEND_LINE);\n    Selection *s = self->selections.items;\n#define S(which, offset_y, scrolled_by) \\\n    if (offset_y < 0) { \\\n        s->scrolled_by = -(offset_y); s->which.y = 0; \\\n    } else { \\\n        s->scrolled_by = 0; s->which.y = offset_y; \\\n    }\n    S(start, oo.start, start_scrolled_by);\n    S(end, oo.start + (int)oo.num_lines - 1, end_scrolled_by);\n#undef S\n    s->start.x = 0; s->start.in_left_half_of_cell = true;\n    s->end.x = self->columns; s->end.in_left_half_of_cell = false;\n    self->selections.in_progress = false;\n\n    call_boss(set_primary_selection, NULL);\n    return true;\n}\n\nstatic PyObject*\nscreen_truncate_point_for_length(PyObject UNUSED *self, PyObject *args) {\n    PyObject *str; unsigned int num_cells, start_pos = 0;\n    if (!PyArg_ParseTuple(args, \"UI|I\", &str, &num_cells, &start_pos)) return NULL;\n    if (PyUnicode_READY(str) != 0) return NULL;\n    int kind = PyUnicode_KIND(str);\n    void *data = PyUnicode_DATA(str);\n    Py_ssize_t len = PyUnicode_GET_LENGTH(str), i;\n    char_type prev_ch = 0;\n    int prev_width = 0;\n    bool in_sgr = false;\n    unsigned long width_so_far = 0;\n    for (i = start_pos; i < len && width_so_far < num_cells; i++) {\n        char_type ch = PyUnicode_READ(kind, data, i);\n        if (in_sgr) {\n            if (ch == 'm') in_sgr = false;\n            continue;\n        }\n        if (ch == 0x1b && i + 1 < len && PyUnicode_READ(kind, data, i + 1) == '[') { in_sgr = true; continue; }\n        if (ch == 0xfe0f) {\n            if (is_emoji_presentation_base(prev_ch) && prev_width == 1) {\n                width_so_far += 1;\n                prev_width = 2;\n            } else prev_width = 0;\n        } else {\n            int w = wcwidth_std(char_props_for(ch));\n            switch(w) {\n                case -1:\n                case 0:\n                    prev_width = 0; break;\n                case 2:\n                    prev_width = 2; break;\n                default:\n                    prev_width = 1; break;\n            }\n            if (width_so_far + prev_width > num_cells) { break; }\n            width_so_far += prev_width;\n        }\n        prev_ch = ch;\n\n    }\n    return PyLong_FromUnsignedLong(i);\n}\n\n\nstatic PyObject*\nline(Screen *self, PyObject *val) {\n    unsigned long y = PyLong_AsUnsignedLong(val);\n    if (y >= self->lines) { PyErr_SetString(PyExc_IndexError, \"Out of bounds\"); return NULL; }\n    linebuf_init_line(self->linebuf, y);\n    Py_INCREF(self->linebuf->line);\n    return (PyObject*) self->linebuf->line;\n}\n\nLine*\nscreen_visual_line(Screen *self, index_type y) {\n    if (y >= self->lines) return NULL;\n    return visual_line_(self, y);\n}\n\nstatic PyObject*\npyvisual_line(Screen *self, PyObject *args) {\n    // The line corresponding to the yth visual line, taking into account scrolling\n    unsigned int y;\n    if (!PyArg_ParseTuple(args, \"I\", &y)) return NULL;\n    if (y >= self->lines) { Py_RETURN_NONE; }\n    return Py_BuildValue(\"O\", visual_line_(self, y));\n}\n\nstatic PyObject*\ndraw(Screen *self, PyObject *src) {\n    if (!PyUnicode_Check(src)) { PyErr_SetString(PyExc_TypeError, \"A unicode string is required\"); return NULL; }\n    if (PyUnicode_READY(src) != 0) { return PyErr_NoMemory(); }\n    Py_UCS4 *buf = PyUnicode_AsUCS4Copy(src);\n    if (!buf) return NULL;\n    draw_text(self, buf, PyUnicode_GetLength(src));\n    PyMem_Free(buf);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\napply_sgr(Screen *self, PyObject *src) {\n    if (!PyUnicode_Check(src)) { PyErr_SetString(PyExc_TypeError, \"A unicode string is required\"); return NULL; }\n    if (PyUnicode_READY(src) != 0) { return PyErr_NoMemory(); }\n    Py_ssize_t sz;\n    const char *s = PyUnicode_AsUTF8AndSize(src, &sz);\n    if (s == NULL) return NULL;\n    if (!parse_sgr(self, (const uint8_t*)s, sz, \"parse_sgr\", false)) {\n        PyErr_Format(PyExc_ValueError, \"Invalid SGR: %s\", PyUnicode_AsUTF8(src));\n        return NULL;\n    }\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nreset_mode(Screen *self, PyObject *args) {\n    int private = false;\n    unsigned int mode;\n    if (!PyArg_ParseTuple(args, \"I|p\", &mode, &private)) return NULL;\n    if (private) mode <<= 5;\n    screen_reset_mode(self, mode);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\n_select_graphic_rendition(Screen *self, PyObject *args) {\n    int params[256] = {0};\n    for (int i = 0; i < PyTuple_GET_SIZE(args); i++) { params[i] = PyLong_AsLong(PyTuple_GET_ITEM(args, i)); }\n    select_graphic_rendition(self, params, PyTuple_GET_SIZE(args), false, NULL);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nset_mode(Screen *self, PyObject *args) {\n    int private = false;\n    unsigned int mode;\n    if (!PyArg_ParseTuple(args, \"I|p\", &mode, &private)) return NULL;\n    if (private) mode <<= 5;\n    screen_set_mode(self, mode);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nreset_dirty(Screen *self, PyObject *a UNUSED) {\n    screen_reset_dirty(self);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nset_window_char(Screen *self, PyObject *a) {\n    const char *text = \"\";\n    if (!PyArg_ParseTuple(a, \"|s\", &text)) return NULL;\n    self->display_window_char = text[0];\n    self->is_dirty = true;\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\nis_using_alternate_linebuf(Screen *self, PyObject *a UNUSED) {\n    if (self->linebuf == self->alt_linebuf) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nWRAP1E(cursor_move, 1, -1, true)\nWRAP1B(erase_in_line, 0)\nWRAP1B(erase_in_display, 0)\nstatic PyObject* scroll_until_cursor_prompt(Screen *self, PyObject *args) { int b=false; if(!PyArg_ParseTuple(args, \"|p\", &b)) return NULL; screen_scroll_until_cursor_prompt(self, b); Py_RETURN_NONE; }\n\nWRAP0(clear_scrollback)\n\n#define MODE_GETSET(name, uname) \\\n    static PyObject* name##_get(Screen *self, void UNUSED *closure) { PyObject *ans = self->modes.m##uname ? Py_True : Py_False; Py_INCREF(ans); return ans; } \\\n    static int name##_set(Screen *self, PyObject *val, void UNUSED *closure) { if (val == NULL) { PyErr_SetString(PyExc_TypeError, \"Cannot delete attribute\"); return -1; } set_mode_from_const(self, uname, PyObject_IsTrue(val) ? true : false); return 0; }\n\nMODE_GETSET(in_bracketed_paste_mode, BRACKETED_PASTE)\nMODE_GETSET(focus_tracking_enabled, FOCUS_TRACKING)\nMODE_GETSET(color_preference_notification, COLOR_PREFERENCE_NOTIFICATION)\nMODE_GETSET(in_band_resize_notification, INBAND_RESIZE_NOTIFICATION)\nMODE_GETSET(paste_events, PASTE_EVENTS)\nMODE_GETSET(auto_repeat_enabled, DECARM)\nMODE_GETSET(cursor_visible, DECTCEM)\nMODE_GETSET(cursor_key_mode, DECCKM)\n\nstatic PyObject* disable_ligatures_get(Screen *self, void UNUSED *closure) {\n    const char *ans = NULL;\n    switch(self->disable_ligatures) {\n        case DISABLE_LIGATURES_NEVER:\n            ans = \"never\";\n            break;\n        case DISABLE_LIGATURES_CURSOR:\n            ans = \"cursor\";\n            break;\n        case DISABLE_LIGATURES_ALWAYS:\n            ans = \"always\";\n            break;\n    }\n    return PyUnicode_FromString(ans);\n}\n\nstatic int disable_ligatures_set(Screen *self, PyObject *val, void UNUSED *closure) {\n    if (val == NULL) { PyErr_SetString(PyExc_TypeError, \"Cannot delete attribute\"); return -1; }\n    if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, \"unicode string expected\"); return -1; }\n    if (PyUnicode_READY(val) != 0) return -1;\n    const char *q = PyUnicode_AsUTF8(val);\n    DisableLigature dl = DISABLE_LIGATURES_NEVER;\n    if (strcmp(q, \"always\") == 0) dl = DISABLE_LIGATURES_ALWAYS;\n    else if (strcmp(q, \"cursor\") == 0) dl = DISABLE_LIGATURES_CURSOR;\n    if (dl != self->disable_ligatures) {\n        self->disable_ligatures = dl;\n        screen_dirty_sprite_positions(self);\n    }\n    return 0;\n}\n\nstatic PyObject*\nrender_unfocused_cursor_get(Screen *self, void UNUSED *closure) {\n    if (self->cursor_render_info.render_even_when_unfocused) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic int\nrender_unfocused_cursor_set(Screen *self, PyObject *val, void UNUSED *closure) {\n    if (val == NULL) { PyErr_SetString(PyExc_TypeError, \"Cannot delete attribute\"); return -1; }\n    self->cursor_render_info.render_even_when_unfocused = PyObject_IsTrue(val);\n    return 0;\n}\n\nstatic PyObject*\ncursor_up(Screen *self, PyObject *args) {\n    unsigned int count = 1;\n    int do_carriage_return = false, move_direction = -1;\n    if (!PyArg_ParseTuple(args, \"|Ipi\", &count, &do_carriage_return, &move_direction)) return NULL;\n    screen_cursor_up(self, count, do_carriage_return, move_direction);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nupdate_selection(Screen *self, PyObject *args) {\n    unsigned int x, y;\n    int in_left_half_of_cell = 0, ended = 1, nearest = 0;\n    if (!PyArg_ParseTuple(args, \"II|ppp\", &x, &y, &in_left_half_of_cell, &ended, &nearest)) return NULL;\n    screen_update_selection(self, x, y, in_left_half_of_cell, (SelectionUpdate){.ended = ended, .set_as_nearest_extend=nearest});\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nclear_selection_(Screen *s, PyObject *args UNUSED) {\n    clear_selection(&s->selections);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nresize(Screen *self, PyObject *args) {\n    unsigned int a=1, b=1;\n    if(!PyArg_ParseTuple(args, \"|II\", &a, &b)) return NULL;\n    screen_resize(self, a, b);\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nWRAP0x(index)\nWRAP0(reverse_index)\nWRAP0(reset)\nWRAP0(set_tab_stop)\nWRAP1(clear_tab_stop, 0)\nWRAP0(backspace)\nWRAP0(tab)\nWRAP0(linefeed)\nWRAP0(carriage_return)\nWRAP2(set_margins, 1, 1)\nWRAP2(detect_url, 0, 0)\nWRAP0(rescale_images)\n\nstatic PyObject*\ncurrent_key_encoding_flags(Screen *self, PyObject *args UNUSED) {\n    unsigned long ans = screen_current_key_encoding_flags(self);\n    return PyLong_FromUnsignedLong(ans);\n}\n\nstatic PyObject*\nignore_bells_for(Screen *self, PyObject *args) {\n    double duration = 1;\n    if (!PyArg_ParseTuple(args, \"|d\", &duration)) return NULL;\n    self->ignore_bells.start = monotonic();\n    self->ignore_bells.duration = s_double_to_monotonic_t(duration);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nstart_selection(Screen *self, PyObject *args) {\n    unsigned int x, y;\n    int rectangle_select = 0, extend_mode = EXTEND_CELL, in_left_half_of_cell = 1;\n    if (!PyArg_ParseTuple(args, \"II|pip\", &x, &y, &rectangle_select, &extend_mode, &in_left_half_of_cell)) return NULL;\n    screen_start_selection(self, x, y, in_left_half_of_cell, rectangle_select, extend_mode);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nis_rectangle_select(Screen *self, PyObject *a UNUSED) {\n    if (self->selections.count && self->selections.items[0].rectangle_select) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\ncopy_colors_from(Screen *self, Screen *other) {\n    copy_color_profile(self->color_profile, other->color_profile);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\ntext_for_selections(Screen *self, Selections *selections, bool ansi, bool strip_trailing_whitespace) {\n    PyObject *lines = NULL;\n    for (size_t i = 0; i < selections->count; i++) {\n        PyObject *temp = ansi ? ansi_for_range(self, selections->items +i, true, strip_trailing_whitespace) : text_for_range(self, selections->items + i, true, strip_trailing_whitespace);\n        if (temp) {\n            if (lines) {\n                lines = extend_tuple(lines, temp);\n                Py_DECREF(temp);\n            } else lines = temp;\n        } else break;\n    }\n    if (PyErr_Occurred()) { Py_CLEAR(lines); return NULL; }\n    if (!lines) lines = PyTuple_New(0);\n    return lines;\n}\n\nstatic PyObject*\ntext_for_selection(Screen *self, PyObject *args) {\n    int ansi = 0, strip_trailing_whitespace = 0;\n    if (!PyArg_ParseTuple(args, \"|pp\", &ansi, &strip_trailing_whitespace)) return NULL;\n    return text_for_selections(self, &self->selections, ansi, strip_trailing_whitespace);\n}\n\nstatic PyObject*\ntext_for_marked_url(Screen *self, PyObject *args) {\n    int ansi = 0, strip_trailing_whitespace = 0;\n    if (!PyArg_ParseTuple(args, \"|pp\", &ansi, &strip_trailing_whitespace)) return NULL;\n    return text_for_selections(self, &self->url_ranges, ansi, strip_trailing_whitespace);\n}\n\nstatic bool\ncell_is_blank(const CPUCell *c) {\n    return !cell_has_text(c) || cell_is_char(c, ' ');\n}\n\nstatic void\nscreen_selection_range_for_line_(Line *line, index_type *start, index_type *end) {\n    index_type xlimit = line->xnum, xstart = 0;\n    while (xlimit > 0 && cell_is_blank(line->cpu_cells + xlimit - 1)) xlimit--;\n    while (xstart < xlimit && cell_is_blank(line->cpu_cells + xstart)) xstart++;\n    *start = xstart; *end = xlimit > 0 ? xlimit - 1 : 0;\n}\n\nbool\nscreen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end) {\n    if (y >= self->lines) { return false; }\n    screen_selection_range_for_line_(visual_line_(self, y), start, end);\n    return true;\n}\n\nstatic bool\nis_opt_word_char(char_type ch, bool forward) {\n    if (forward && OPT(select_by_word_characters_forward)) {\n        for (const char_type *p = OPT(select_by_word_characters_forward); *p; p++) {\n            if (ch == *p) return true;\n        }\n        if (*OPT(select_by_word_characters_forward)) {\n            return false;\n        }\n    }\n    if (OPT(select_by_word_characters)) {\n        for (const char_type *p = OPT(select_by_word_characters); *p; p++) {\n            if (ch == *p) return true;\n        }\n    }\n    return false;\n}\n\nstatic bool\nis_char_ok_for_word_extension(Line* line, index_type x, bool forward) {\n    char_type ch = cell_first_char(line->cpu_cells + x, line->text_cache);\n    if (char_props_for(ch).is_word_char || is_opt_word_char(ch, forward)) return true;\n    // pass : from :// so that common URLs are matched\n    return ch == ':' && x + 2 < line->xnum && cell_is_char(line->cpu_cells + x + 1, '/') && cell_is_char(line->cpu_cells + x + 2,  '/');\n}\n\nbool\nscreen_selection_range_for_word(Screen *self, const index_type x, const index_type y, index_type *y1, index_type *y2, index_type *s, index_type *e, bool initial_selection) {\n    if (y >= self->lines || x >= self->columns) return false;\n    index_type start, end;\n    Line *line = visual_line_(self, y);\n    *y1 = y;\n    *y2 = y;\n#define is_ok(x, forward) is_char_ok_for_word_extension(line, x, forward)\n    if (!is_ok(x, false)) {\n        if (initial_selection) return false;\n        *s = x; *e = x;\n        return true;\n    }\n    start = x; end = x;\n    while(true) {\n        while(start > 0 && is_ok(start - 1, false)) start--;\n        if (start > 0 || !visual_line_is_continued(self, y) || *y1 == 0) break;\n        line = visual_line_(self, *y1 - 1);\n        if (!is_ok(self->columns - 1, false)) break;\n        (*y1)--; start = self->columns - 1;\n    }\n    line = visual_line_(self, *y2);\n    while(true) {\n        while(end < self->columns - 1 && is_ok(end + 1, true)) end++;\n        if (end < self->columns - 1 || *y2 >= self->lines - 1) break;\n        line = visual_line_(self, *y2 + 1);\n        if (!visual_line_is_continued(self, *y2 + 1) || !is_ok(0, true)) break;\n        (*y2)++; end = 0;\n    }\n    *s = start; *e = end;\n    return true;\n#undef is_ok\n}\n\nvoid\nscreen_history_scroll_to_absolute(Screen *self, double target_scrolled_by) {\n    if (self->linebuf != self->main_linebuf) return;\n    index_type target_scrolled_by_line = (index_type)target_scrolled_by;\n    unsigned pixel_scroll_offset_y = (unsigned)((target_scrolled_by - target_scrolled_by_line) * self->cell_size.height);\n    if (!OPT(pixel_scroll)) pixel_scroll_offset_y = 0;\n    if (target_scrolled_by_line > self->historybuf->count) target_scrolled_by_line = self->historybuf->count;\n    if (target_scrolled_by_line >= self->historybuf->count) pixel_scroll_offset_y = 0;\n    if (target_scrolled_by_line != self->scrolled_by || self->pixel_scroll_offset_y != pixel_scroll_offset_y) {\n        self->scrolled_by = target_scrolled_by_line;\n        reset_pixel_scroll(self, pixel_scroll_offset_y);\n        dirty_scroll(self);\n    }\n}\n\nbool\nscreen_apply_pixel_scroll(Screen *self, double delta_pixels) {\n    if (!pixel_scroll_enabled(self)) return false;\n    if (!self->historybuf->count) return false;\n    const double cell_height = (double)self->cell_size.height;\n    if (cell_height <= 0.0 || delta_pixels == 0.0) return false;\n\n    double total = self->pixel_scroll_offset_y + (double)self->scrolled_by * cell_height + delta_pixels;\n    const double max_total = (double)self->historybuf->count * cell_height;\n    if (total < 0.0) total = 0.0;\n    if (total > max_total) total = max_total;\n    const unsigned int new_scrolled_by = (unsigned int)floor(total / cell_height);\n    const unsigned offset = (unsigned)(total - (double)new_scrolled_by * cell_height);\n    bool changed = false;\n    if (new_scrolled_by != self->scrolled_by) {\n        self->scrolled_by = new_scrolled_by;\n        changed = true;\n    }\n\n    if (offset != self->pixel_scroll_offset_y) {\n        self->pixel_scroll_offset_y = offset;\n        changed = true;\n    }\n    if (changed) dirty_scroll(self);\n    return changed;\n}\n\nbool\nscreen_history_scroll(Screen *self, int amt, bool upwards) {\n    switch(amt) {\n        case SCROLL_LINE:\n            amt = 1;\n            break;\n        case SCROLL_PAGE:\n            amt = self->lines - 1;\n            break;\n        case SCROLL_FULL:\n            amt = self->historybuf->count;\n            break;\n        default:\n            amt = MAX(0, amt);\n            break;\n    }\n    if (!upwards) {\n        amt = MIN((unsigned int)amt, self->scrolled_by);\n        amt *= -1;\n    }\n    unsigned int new_scroll = MIN(self->scrolled_by + amt, self->historybuf->count);\n    if (new_scroll != self->scrolled_by || (new_scroll == 0 && self->pixel_scroll_offset_y != 0)) {\n        self->scrolled_by = new_scroll;\n        reset_pixel_scroll(self, 0);\n        dirty_scroll(self);\n        return true;\n    }\n    return false;\n}\n\nstatic bool\nscreen_fractional_scroll(Screen *self, double amt) {\n    if (amt == 0) return false;\n    index_type before_scrolled_by = self->scrolled_by;\n    double before_pixels = self->pixel_scroll_offset_y;\n    double integral_part, fractional_part = modf(amt, &integral_part);\n    int lines = (int)integral_part;\n    int pixels = (int)(fractional_part * self->cell_size.height);\n    if (amt > 0) {  // downwards\n        if (fractional_part != 0) pixels = MAX(1, pixels);\n        if (lines > (int)self->scrolled_by) {\n            self->scrolled_by = 0; self->pixel_scroll_offset_y = 0;\n        } else {\n            self->scrolled_by -= lines;\n            if (pixels <= (int)self->pixel_scroll_offset_y) self->pixel_scroll_offset_y -= pixels;\n            else {\n                self->pixel_scroll_offset_y = 0;\n                if (self->scrolled_by) {\n                    self->scrolled_by--; self->pixel_scroll_offset_y = self->cell_size.height - pixels;\n                }\n            }\n        }\n    } else {\n        if (fractional_part != 0) pixels = MIN(-1, pixels);\n        self->pixel_scroll_offset_y -= pixels;  // pixels is negative\n        if (self->pixel_scroll_offset_y >= self->cell_size.height) {\n            self->pixel_scroll_offset_y = 0; self->scrolled_by++;\n        }\n        self->scrolled_by = MIN(self->scrolled_by - lines, self->historybuf->count);\n        if (self->scrolled_by >= self->historybuf->count) self->pixel_scroll_offset_y = 0;\n    }\n    if (self->scrolled_by != before_scrolled_by || self->pixel_scroll_offset_y != before_pixels) {\n        dirty_scroll(self);\n        return true;\n    }\n    return false;\n}\n\nstatic PyObject*\nfractional_scroll(Screen *self, PyObject *amt) {\n    double y;\n    if (PyFloat_Check(amt)) y = PyFloat_AS_DOUBLE(amt);\n    else if (PyLong_Check(amt)) y = PyLong_AsDouble(amt);\n    else { PyErr_SetString(PyExc_TypeError, \"amt must be a float\"); return NULL; }\n    return Py_NewRef(screen_fractional_scroll(self, y) ? Py_True : Py_False);\n}\n\nstatic PyObject*\nscroll(Screen *self, PyObject *args) {\n    int amt, upwards;\n    if (!PyArg_ParseTuple(args, \"ip\", &amt, &upwards)) return NULL;\n    if (screen_history_scroll(self, amt, upwards)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nscroll_to_prompt(Screen *self, PyObject *args) {\n    int num_of_prompts = -1;\n    int scroll_offset = 0;\n    if (!PyArg_ParseTuple(args, \"|ii\", &num_of_prompts, &scroll_offset)) return NULL;\n    if (screen_history_scroll_to_prompt(self, num_of_prompts, scroll_offset)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nset_last_visited_prompt(Screen *self, PyObject *args) {\n    index_type visual_y = 0;\n    if (!PyArg_ParseTuple(args, \"|I\", &visual_y)) return NULL;\n    if (screen_set_last_visited_prompt(self, visual_y)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nbool\nscreen_is_selection_dirty(Screen *self) {\n    IterationData q;\n    if (self->paused_rendering.expires_at) return false;\n    if (self->scrolled_by != self->last_rendered.scrolled_by) return true;\n    if (self->selections.last_rendered_count != self->selections.count || self->url_ranges.last_rendered_count != self->url_ranges.count || self->extra_cursors.dirty) return true;\n    for (size_t i = 0; i < self->selections.count; i++) {\n        iteration_data(self->selections.items + i, &q, self->columns, 0, self->scrolled_by);\n        if (memcmp(&q, &self->selections.items[i].last_rendered, sizeof(IterationData)) != 0) return true;\n    }\n    for (size_t i = 0; i < self->url_ranges.count; i++) {\n        iteration_data(self->url_ranges.items + i, &q, self->columns, 0, self->scrolled_by);\n        if (memcmp(&q, &self->url_ranges.items[i].last_rendered, sizeof(IterationData)) != 0) return true;\n    }\n    return false;\n}\n\nvoid\nscreen_start_selection(Screen *self, index_type x, index_type y, bool in_left_half_of_cell, bool rectangle_select, SelectionExtendMode extend_mode) {\n    screen_pause_rendering(self, false, 0);\n#define A(attr, val) self->selections.items->attr = val;\n    ensure_space_for(&self->selections, items, Selection, self->selections.count + 1, capacity, 1, false);\n    memset(self->selections.items, 0, sizeof(Selection));\n    self->selections.count = 1;\n    self->selections.in_progress = true;\n    self->selections.extend_mode = extend_mode;\n    self->selections.items[0].last_rendered.y = INT_MAX;\n    A(start.x, x); A(end.x, x); A(start.y, y); A(end.y, y); A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by);\n    A(rectangle_select, rectangle_select); A(start.in_left_half_of_cell, in_left_half_of_cell); A(end.in_left_half_of_cell, in_left_half_of_cell);\n    A(input_start.x, x); A(input_start.y, y); A(input_start.in_left_half_of_cell, in_left_half_of_cell);\n    A(input_current.x, x); A(input_current.y, y); A(input_current.in_left_half_of_cell, in_left_half_of_cell);\n#undef A\n}\n\nstatic void\nadd_url_range(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y, bool is_hyperlink) {\n#define A(attr, val) r->attr = val;\n    ensure_space_for(&self->url_ranges, items, Selection, self->url_ranges.count + 8, capacity, 8, false);\n    Selection *r = self->url_ranges.items + self->url_ranges.count++;\n    memset(r, 0, sizeof(Selection));\n    r->last_rendered.y = INT_MAX;\n    r->is_hyperlink = is_hyperlink;\n    A(start.x, start_x); A(end.x, end_x); A(start.y, start_y); A(end.y, end_y);\n    A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by);\n    A(start.in_left_half_of_cell, true);\n#undef A\n}\n\nvoid\nscreen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y) {\n    self->url_ranges.count = 0;\n    if (start_x || start_y || end_x || end_y) add_url_range(self, start_x, start_y, end_x, end_y, false);\n}\n\nstatic bool\nmark_hyperlinks_in_line(Screen *self, Line *line, hyperlink_id_type id, index_type y, bool *found_nonzero_multiline) {\n    index_type start = 0;\n    bool found = false;\n    bool in_range = false;\n    *found_nonzero_multiline = false;\n    for (index_type x = 0; x < line->xnum; x++) {\n        bool has_hyperlink = line->cpu_cells[x].hyperlink_id == id;\n        bool is_nonzero_multiline = line->cpu_cells[x].is_multicell && line->cpu_cells[x].y > 0;\n        if (has_hyperlink && is_nonzero_multiline) {\n            has_hyperlink = false;\n            *found_nonzero_multiline = true;\n        }\n        if (in_range) {\n            if (!has_hyperlink) {\n                add_url_range(self, start, y, x - 1, y, true);\n                in_range = false;\n                start = 0;\n            }\n        } else {\n            if (has_hyperlink) {\n                start = x; in_range = true;\n                found = true;\n            }\n        }\n    }\n    if (in_range) add_url_range(self, start, y, self->columns - 1, y, true);\n    return found;\n}\n\nstatic void\nsort_ranges(const Screen *self, Selections *s) {\n    IterationData a;\n    for (size_t i = 0; i < s->count; i++) {\n        iteration_data(s->items + i, &a, self->columns, 0, 0);\n        s->items[i].sort_x = a.first.x;\n        s->items[i].sort_y = a.y;\n    }\n#define range_lt(a, b) ((a)->sort_y < (b)->sort_y || ((a)->sort_y == (b)->sort_y && (a)->sort_x < (b)->sort_x))\n    QSORT(Selection, s->items, s->count, range_lt);\n#undef range_lt\n}\n\nhyperlink_id_type\nscreen_mark_hyperlink(Screen *self, index_type x, index_type y) {\n    self->url_ranges.count = 0;\n    Line *line = screen_visual_line(self, y);\n    hyperlink_id_type id = line->cpu_cells[x].hyperlink_id;\n    if (!id) return 0;\n    index_type ypos = y, last_marked_line = y;\n    bool found_nonzero_multiline;\n    do {\n        if (mark_hyperlinks_in_line(self, line, id, ypos, &found_nonzero_multiline) || found_nonzero_multiline) last_marked_line = ypos;\n        if (ypos == 0) break;\n        ypos--;\n        line = screen_visual_line(self, ypos);\n    } while (last_marked_line - ypos < 5);\n    ypos = y + 1; last_marked_line = y;\n    while (ypos < self->lines - 1 && ypos - last_marked_line < 5) {\n        line = screen_visual_line(self, ypos);\n        if (mark_hyperlinks_in_line(self, line, id, ypos, &found_nonzero_multiline)) last_marked_line = ypos;\n        ypos++;\n    }\n    if (self->url_ranges.count > 1) sort_ranges(self, &self->url_ranges);\n    return id;\n}\n\nstatic index_type\ncontinue_line_upwards(Screen *self, index_type top_line, SelectionBoundary *start, SelectionBoundary *end) {\n    while (top_line > 0 && visual_line_is_continued(self, top_line)) {\n        if (!screen_selection_range_for_line(self, top_line - 1, &start->x, &end->x)) break;\n        top_line--;\n    }\n    return top_line;\n}\n\nstatic index_type\ncontinue_line_upwards_scrollback(Screen *self, int top_line, SelectionBoundary *start, SelectionBoundary *end) {\n    index_type num_in_scrollback = 0;\n    Line *line = NULL;\n    while (range_line_is_continued(self, top_line) && (line = range_line_(self, top_line-1))) {\n        screen_selection_range_for_line_(line, &start->x, &end->x) ;\n        top_line--; num_in_scrollback++;\n    }\n    return num_in_scrollback;\n}\n\n\nstatic index_type\ncontinue_line_downwards(Screen *self, index_type bottom_line, SelectionBoundary *start, SelectionBoundary *end) {\n    while (bottom_line + 1 < self->lines && visual_line_is_continued(self, bottom_line + 1)) {\n        if (!screen_selection_range_for_line(self, bottom_line + 1, &start->x, &end->x)) break;\n        bottom_line++;\n    }\n    return bottom_line;\n}\n\nstatic int\nclamp_selection_input_to_multicell(Screen *self, const Selection *s, index_type x, index_type y, bool in_left_half_of_cell) {\n    int delta = 0;\n    int abs_y = y - self->scrolled_by, abs_start_y = s->start.y - s->start_scrolled_by;\n    if (abs_y == abs_start_y) return delta;\n    Line *line = checked_range_line(self, abs_start_y);\n    CPUCell *start, *current;\n    if (!line || s->start.x >= line->xnum || !(start = &line->cpu_cells[s->start.x])->is_multicell || start->scale < 2) return delta;\n    int abs_start_top = abs_start_y - start->y;\n    line = checked_range_line(self, abs_y);\n    if (x > s->start.x && in_left_half_of_cell) x--;\n    else if (x < s->start.x && !in_left_half_of_cell) x++;\n    if (!line || x >= line->xnum) return delta;\n    current = line->cpu_cells + x;\n    if (!current->is_multicell) return delta;\n    int abs_current_top = abs_y - current->y;\n    if (current->scale == start->scale && current->subscale_n == start->subscale_n && current->subscale_d == start->subscale_d && abs_current_top == abs_start_top) delta = abs_y - abs_start_y;\n    return delta;\n}\n\nstatic void\ndo_update_selection(Screen *self, Selection *s, index_type x, index_type y, bool in_left_half_of_cell, SelectionUpdate upd) {\n    s->input_current.x = x; s->input_current.y = y;\n    s->input_current.in_left_half_of_cell = in_left_half_of_cell;\n    SelectionBoundary start, end, *a = &s->start, *b = &s->end, abs_start, abs_end, abs_current_input;\n#define set_abs(which, initializer, scrolled_by) which = initializer; which.y = scrolled_by + self->lines - 1 - which.y;\n    set_abs(abs_start, s->start, s->start_scrolled_by);\n    set_abs(abs_end, s->end, s->end_scrolled_by);\n    set_abs(abs_current_input, s->input_current, self->scrolled_by);\n    bool return_word_sel_to_start_line = false;\n    if (upd.set_as_nearest_extend || self->selections.extension_in_progress) {\n        self->selections.extension_in_progress = true;\n        bool start_is_nearer = false;\n        if (self->selections.extend_mode == EXTEND_LINE || self->selections.extend_mode == EXTEND_LINE_FROM_POINT || self->selections.extend_mode == EXTEND_WORD_AND_LINE_FROM_POINT) {\n            if (abs_start.y == abs_end.y) {\n                if (abs_current_input.y == abs_start.y) start_is_nearer = selection_boundary_less_than(&abs_start, &abs_end) ? (abs_current_input.x <= abs_start.x) : (abs_current_input.x <= abs_end.x);\n                else start_is_nearer = selection_boundary_less_than(&abs_start, &abs_end) ? (abs_current_input.y > abs_start.y) : (abs_current_input.y < abs_end.y);\n            } else {\n                start_is_nearer = num_lines_between_selection_boundaries(&abs_start, &abs_current_input) < num_lines_between_selection_boundaries(&abs_end, &abs_current_input);\n            }\n        } else start_is_nearer = num_cells_between_selection_boundaries(self, &abs_start, &abs_current_input) < num_cells_between_selection_boundaries(self, &abs_end, &abs_current_input);\n        if (start_is_nearer) s->adjusting_start = true;\n    } else if (!upd.start_extended_selection && self->selections.extend_mode != EXTEND_CELL) {\n        SelectionBoundary abs_initial_start, abs_initial_end;\n        set_abs(abs_initial_start, s->initial_extent.start, s->initial_extent.scrolled_by);\n        set_abs(abs_initial_end, s->initial_extent.end, s->initial_extent.scrolled_by);\n        if (self->selections.extend_mode == EXTEND_WORD) {\n            if (abs_current_input.y == abs_initial_start.y && abs_start.y != abs_end.y) {\n                if (abs_start.y != abs_initial_start.y) s->adjusting_start = true;\n                else if (abs_end.y != abs_initial_start.y) s->adjusting_start = false;\n                else s->adjusting_start = selection_boundary_less_than(&abs_current_input, &abs_initial_end);\n                return_word_sel_to_start_line = true;\n            } else {\n                if (s->adjusting_start) s->adjusting_start = selection_boundary_less_than(&abs_current_input, &abs_initial_end);\n                else s->adjusting_start = selection_boundary_less_than(&abs_current_input, &abs_initial_start);\n            }\n        } else {\n            const unsigned int initial_line = abs_initial_start.y;\n            if (initial_line == abs_current_input.y) {\n                s->adjusting_start = false;\n                s->start = s->initial_extent.start; s->start_scrolled_by = s->initial_extent.scrolled_by;\n                s->end = s->initial_extent.end; s->end_scrolled_by = s->initial_extent.scrolled_by;\n            }\n            else {\n                s->adjusting_start = abs_current_input.y > initial_line;\n            }\n        }\n    }\n#undef set_abs\n    bool adjusted_boundary_is_before;\n    if (s->adjusting_start) adjusted_boundary_is_before = selection_boundary_less_than(&abs_start, &abs_end);\n    else { adjusted_boundary_is_before = selection_boundary_less_than(&abs_end, &abs_start); }\n\n    switch(self->selections.extend_mode) {\n        case EXTEND_WORD: {\n            if (!s->adjusting_start) { a = &s->end; b = &s->start; }\n            const bool word_found_at_cursor = screen_selection_range_for_word(self, s->input_current.x, s->input_current.y, &start.y, &end.y, &start.x, &end.x, true);\n            bool adjust_both_ends = is_selection_empty(s);\n            if (return_word_sel_to_start_line) {\n                index_type ox = a->x;\n                if (s->adjusting_start) { *a = s->initial_extent.start; if (ox < a->x) a->x = ox; }\n                else { *a = s->initial_extent.end; if (ox > a->x) a->x = ox; }\n            } else if (word_found_at_cursor) {\n                if (adjusted_boundary_is_before) {\n                    *a = start; a->in_left_half_of_cell = true;\n                    if (adjust_both_ends) { *b = end; b->in_left_half_of_cell = false; }\n                } else {\n                    *a = end; a->in_left_half_of_cell = false;\n                    if (adjust_both_ends) { *b = start; b->in_left_half_of_cell = true; }\n                }\n                if (s->adjusting_start || adjust_both_ends) s->start_scrolled_by = self->scrolled_by;\n                if (!s->adjusting_start || adjust_both_ends) s->end_scrolled_by = self->scrolled_by;\n            } else {\n                *a = s->input_current;\n                if (s->adjusting_start) s->start_scrolled_by = self->scrolled_by; else s->end_scrolled_by = self->scrolled_by;\n            }\n            break;\n        }\n        case EXTEND_LINE_FROM_POINT:\n        case EXTEND_WORD_AND_LINE_FROM_POINT:\n        case EXTEND_LINE: {\n            bool adjust_both_ends = is_selection_empty(s);\n            if (s->adjusting_start || adjust_both_ends) s->start_scrolled_by = self->scrolled_by;\n            if (!s->adjusting_start || adjust_both_ends) s->end_scrolled_by = self->scrolled_by;\n            index_type top_line, bottom_line;\n            SelectionBoundary up_start, up_end, down_start, down_end;\n            if (adjust_both_ends) {\n                // empty initial selection\n                top_line = s->input_current.y; bottom_line = s->input_current.y;\n                if (screen_selection_range_for_line(self, top_line, &up_start.x, &up_end.x)) {\n#define S \\\n    s->start.y = top_line; s->end.y = bottom_line; \\\n    s->start.in_left_half_of_cell = true; s->end.in_left_half_of_cell = false; \\\n    s->start.x = up_start.x; s->end.x = bottom_line == top_line ? up_end.x : down_end.x;\n                    down_start = up_start; down_end = up_end;\n                    bottom_line = continue_line_downwards(self, bottom_line, &down_start, &down_end);\n                    if (self->selections.extend_mode == EXTEND_LINE_FROM_POINT) {\n                        if (x <= up_end.x) {\n                            S; s->start.x = MAX(x, up_start.x);\n                        }\n                    } else if (self->selections.extend_mode == EXTEND_WORD_AND_LINE_FROM_POINT) {\n                        if (x <= up_end.x) {\n                            S; s->start.x = MAX(x, up_start.x);\n                        }\n                        const bool word_found_at_cursor = screen_selection_range_for_word(self, s->input_current.x, s->input_current.y, &start.y, &end.y, &start.x, &end.x, true);\n                        if (word_found_at_cursor) {\n                            *a = start; a->in_left_half_of_cell = true;\n                        }\n                    } else {\n                        top_line = continue_line_upwards(self, top_line, &up_start, &up_end);\n                        S;\n                        // extend into scrollback if needed\n                        if (top_line == 0 && self->linebuf == self->main_linebuf) {\n                            index_type num_in_scrollback = continue_line_upwards_scrollback(\n                                    self, top_line, &up_start, &up_end);\n                            if (num_in_scrollback) {\n                                s->start_scrolled_by += num_in_scrollback;\n                                s->start.x = up_start.x;\n                            }\n                        }\n                    }\n                }\n#undef S\n            } else {\n                // extending an existing selection\n                top_line = s->input_current.y; bottom_line = s->input_current.y;\n                if (screen_selection_range_for_line(self, top_line, &up_start.x, &up_end.x)) {\n                    down_start = up_start; down_end = up_end;\n                    top_line = continue_line_upwards(self, top_line, &up_start, &up_end);\n                    bottom_line = continue_line_downwards(self, bottom_line, &down_start, &down_end);\n                    if (!s->adjusting_start) { a = &s->end; b = &s->start; }\n                    if (adjusted_boundary_is_before) {\n                        a->in_left_half_of_cell = true; a->x = up_start.x; a->y = top_line;\n                        // extend into scrollback if needed\n                        if (top_line == 0 && self->linebuf == self->main_linebuf) {\n                            index_type num_in_scrollback = continue_line_upwards_scrollback(\n                                    self, top_line, &up_start, &up_end);\n                            if (num_in_scrollback) {\n                                s->start_scrolled_by += num_in_scrollback;\n                                s->start.x = up_start.x;\n                            }\n                        }\n                    } else {\n                        a->in_left_half_of_cell = false; a->x = down_end.x; a->y = bottom_line;\n                    }\n                    // allow selecting whitespace at the start of the top line\n                    if (a->y == top_line && s->input_current.y == top_line && s->input_current.x < a->x && adjusted_boundary_is_before) a->x = s->input_current.x;\n                }\n            }\n        }\n        break;\n        case EXTEND_CELL:\n            if (s->adjusting_start) b = &s->start;\n            b->x = x; b->y = y; b->in_left_half_of_cell = in_left_half_of_cell;\n            if (s->adjusting_start) s->start_scrolled_by = self->scrolled_by; else s->end_scrolled_by = self->scrolled_by;\n            break;\n    }\n    if (!self->selections.in_progress) {\n        s->adjusting_start = false;\n        self->selections.extension_in_progress = false;\n        call_boss(set_primary_selection, NULL);\n    } else {\n        if (upd.start_extended_selection && self->selections.extend_mode != EXTEND_CELL) {\n            s->initial_extent.start = s->start; s->initial_extent.end = s->end;\n            s->initial_extent.scrolled_by = s->start_scrolled_by;\n        }\n    }\n}\n\nvoid\nscreen_update_selection(Screen *self, index_type x, index_type y, bool in_left_half_of_cell, SelectionUpdate upd) {\n    if (!self->selections.count) return;\n    self->selections.in_progress = !upd.ended;\n    Selection *s = self->selections.items;\n    int delta = clamp_selection_input_to_multicell(self, s, x, y, in_left_half_of_cell);\n    index_type orig = self->scrolled_by;\n    if (delta) {\n        int new_y = y - delta;\n        if (new_y < 0) {\n            y = 0; self->scrolled_by += - new_y;\n        } else y = new_y;\n    }\n    do_update_selection(self, s, x, y, in_left_half_of_cell, upd);\n    self->scrolled_by = orig;\n}\n\nstatic PyObject*\nmark_as_dirty(Screen *self, PyObject *a UNUSED) {\n    self->is_dirty = true;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nreload_all_gpu_data(Screen *self, PyObject *a UNUSED) {\n    self->reload_all_gpu_data = true;\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\ncurrent_char_width(Screen *self, PyObject *a UNUSED) {\n#define current_char_width_doc \"The width of the character under the cursor\"\n    unsigned long ans = 1;\n    if (self->cursor->x < self->columns && self->cursor->y < self->lines) {\n        const CPUCell *c = linebuf_cpu_cells_for_line(self->linebuf, self->cursor->y) + self->cursor->x;\n        if (c->is_multicell) {\n            if (c->x || c->y) ans = 0;\n            else ans = c->width;\n        }\n    }\n    return PyLong_FromUnsignedLong(ans);\n}\n\nstatic PyObject*\nis_main_linebuf(Screen *self, PyObject *a UNUSED) {\n    PyObject *ans = (self->linebuf == self->main_linebuf) ? Py_True : Py_False;\n    Py_INCREF(ans);\n    return ans;\n}\n\nstatic PyObject*\ntoggle_alt_screen(Screen *self, PyObject *a UNUSED) {\n    screen_toggle_screen_buffer(self, true, true);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npause_rendering(Screen *self, PyObject *args) {\n    int msec = 100;\n    int pause = 1;\n    if (!PyArg_ParseTuple(args, \"|pi\", &msec)) return NULL;\n    if (screen_pause_rendering(self, pause, msec)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nsend_escape_code_to_child(Screen *self, PyObject *args) {\n    int code;\n    PyObject *O;\n    if (!PyArg_ParseTuple(args, \"iO\", &code, &O)) return NULL;\n    bool written = false;\n    if (PyBytes_Check(O)) written = write_escape_code_to_child(self, code, PyBytes_AS_STRING(O));\n    else if (PyUnicode_Check(O)) {\n        const char *t = PyUnicode_AsUTF8(O);\n        if (t) written = write_escape_code_to_child(self, code, t);\n        else return NULL;\n    } else if (PyTuple_Check(O)) written = write_escape_code_to_child_python(self, code, O);\n    else PyErr_SetString(PyExc_TypeError, \"escape code must be str, bytes or tuple\");\n    if (PyErr_Occurred()) return NULL;\n    if (written) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; }\n}\n\nstatic void\nscreen_mark_all(Screen *self) {\n    for (index_type y = 0; y < self->main_linebuf->ynum; y++) {\n        linebuf_init_line(self->main_linebuf, y);\n        mark_text_in_line(self->marker, self->main_linebuf->line, &self->as_ansi_buf);\n    }\n    for (index_type y = 0; y < self->alt_linebuf->ynum; y++) {\n        linebuf_init_line(self->alt_linebuf, y);\n        mark_text_in_line(self->marker, self->alt_linebuf->line, &self->as_ansi_buf);\n    }\n    for (index_type y = 0; y < self->historybuf->count; y++) {\n        historybuf_init_line(self->historybuf, y, self->historybuf->line);\n        mark_text_in_line(self->marker, self->historybuf->line, &self->as_ansi_buf);\n    }\n    self->is_dirty = true;\n}\n\nstatic PyObject*\nset_marker(Screen *self, PyObject *args) {\n    PyObject *marker = NULL;\n    if (!PyArg_ParseTuple(args, \"|O\", &marker)) return NULL;\n    if (!marker) {\n        if (self->marker) {\n            Py_CLEAR(self->marker);\n            screen_mark_all(self);\n        }\n        Py_RETURN_NONE;\n    }\n    if (!PyCallable_Check(marker)) {\n        PyErr_SetString(PyExc_TypeError, \"marker must be a callable\");\n        return NULL;\n    }\n    self->marker = marker;\n    Py_INCREF(marker);\n    screen_mark_all(self);\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\nscroll_to_next_mark(Screen *self, PyObject *args) {\n    int backwards = 1;\n    unsigned int mark = 0;\n    if (!PyArg_ParseTuple(args, \"|Ip\", &mark, &backwards)) return NULL;\n    if (!screen_has_marker(self) || self->linebuf == self->alt_linebuf) Py_RETURN_FALSE;\n    if (backwards) {\n        for (unsigned int y = self->scrolled_by; y < self->historybuf->count; y++) {\n            historybuf_init_line(self->historybuf, y, self->historybuf->line);\n            if (line_has_mark(self->historybuf->line, mark)) {\n                screen_history_scroll(self, y - self->scrolled_by + 1, true);\n                Py_RETURN_TRUE;\n            }\n        }\n    } else {\n        Line *line;\n        for (unsigned int y = self->scrolled_by; y > 0; y--) {\n            if (y > self->lines) {\n                historybuf_init_line(self->historybuf, y - self->lines, self->historybuf->line);\n                line = self->historybuf->line;\n            } else {\n                linebuf_init_line(self->linebuf, self->lines - y);\n                line = self->linebuf->line;\n            }\n            if (line_has_mark(line, mark)) {\n                screen_history_scroll(self, self->scrolled_by - y + 1, false);\n                Py_RETURN_TRUE;\n            }\n        }\n    }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nmarked_cells(Screen *self, PyObject *o UNUSED) {\n    RAII_PyObject(ans, PyList_New(0));\n    if (!ans) return ans;\n    for (index_type y = 0; y < self->lines; y++) {\n        linebuf_init_line(self->linebuf, y);\n        for (index_type x = 0; x < self->columns; x++) {\n            GPUCell *gpu_cell = self->linebuf->line->gpu_cells + x;\n            const unsigned int mark = gpu_cell->attrs.mark;\n            if (mark) {\n                RAII_PyObject(t, Py_BuildValue(\"III\", x, y, mark));\n                if (!t) { return NULL; }\n                if (PyList_Append(ans, t) != 0) return NULL;\n            }\n        }\n    }\n    return Py_NewRef(ans);\n}\n\nstatic PyObject*\npaste_(Screen *self, PyObject *bytes, bool allow_bracketed_paste) {\n    const char *data; Py_ssize_t sz;\n    if (PyBytes_Check(bytes)) {\n        data = PyBytes_AS_STRING(bytes); sz = PyBytes_GET_SIZE(bytes);\n    } else if (PyMemoryView_Check(bytes)) {\n        RAII_PyObject(mv, PyMemoryView_GetContiguous(bytes, PyBUF_READ, PyBUF_C_CONTIGUOUS));\n        if (mv == NULL) return NULL;\n        Py_buffer *buf = PyMemoryView_GET_BUFFER(mv);\n        data = buf->buf;\n        sz = buf->len;\n    } else {\n        PyErr_SetString(PyExc_TypeError, \"Must paste() bytes\"); return NULL;\n    }\n    if (allow_bracketed_paste && self->modes.mBRACKETED_PASTE) write_escape_code_to_child(self, ESC_CSI, BRACKETED_PASTE_START);\n    write_to_child(self, data, sz);\n    if (allow_bracketed_paste && self->modes.mBRACKETED_PASTE) write_escape_code_to_child(self, ESC_CSI, BRACKETED_PASTE_END);\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\npaste(Screen *self, PyObject *bytes) {\n    return paste_(self, bytes, true);\n}\n\nstatic PyObject*\npaste_bytes(Screen *self, PyObject *bytes) {\n    return paste_(self, bytes, false);\n}\n\nstatic PyObject*\nfocus_changed(Screen *self, PyObject *has_focus_) {\n    bool previous = self->has_focus;\n    bool has_focus = PyObject_IsTrue(has_focus_) ? true : false;\n    if (has_focus != previous) {\n        self->has_focus = has_focus;\n        if (has_focus) self->has_activity_since_last_focus = false;\n        else if (screen_is_overlay_active(self)) deactivate_overlay_line(self);\n        if (self->modes.mFOCUS_TRACKING) write_escape_code_to_child(self, ESC_CSI, has_focus ? \"I\" : \"O\");\n        Py_RETURN_TRUE;\n    }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nhas_focus(Screen *self, PyObject *args UNUSED) {\n    if (self->has_focus) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nhas_activity_since_last_focus(Screen *self, PyObject *args UNUSED) {\n    if (self->has_activity_since_last_focus) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nWRAP2(cursor_position, 1, 1)\n\n#define COUNT_WRAP(name) WRAP1(name, 1)\nCOUNT_WRAP(insert_lines)\nCOUNT_WRAP(delete_lines)\nCOUNT_WRAP(delete_characters)\nCOUNT_WRAP(erase_characters)\nCOUNT_WRAP(cursor_up1)\nCOUNT_WRAP(cursor_down)\nCOUNT_WRAP(cursor_down1)\nCOUNT_WRAP(cursor_forward)\n\nstatic PyObject*\npy_insert_characters(Screen *self, PyObject *count_) {\n    if (!PyLong_Check(count_)) { PyErr_SetString(PyExc_TypeError, \"count must be an integer\"); return NULL; }\n    unsigned long count = PyLong_AsUnsignedLong(count_);\n    screen_insert_characters(self, count);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nscreen_is_emoji_presentation_base(PyObject UNUSED *self, PyObject *code_) {\n    unsigned long code = PyLong_AsUnsignedLong(code_);\n    if (is_emoji_presentation_base(code)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nhyperlink_at(Screen *self, PyObject *args) {\n    unsigned int x, y;\n    if (!PyArg_ParseTuple(args, \"II\", &x, &y)) return NULL;\n    screen_mark_hyperlink(self, x, y);\n    if (!self->url_ranges.count) Py_RETURN_NONE;\n    hyperlink_id_type hid = hyperlink_id_for_range(self, self->url_ranges.items);\n    if (!hid) Py_RETURN_NONE;\n    const char *url = get_hyperlink_for_id(self->hyperlink_pool, hid, true);\n    return Py_BuildValue(\"s\", url);\n}\n\nstatic PyObject*\nreverse_scroll(Screen *self, PyObject *args) {\n    int fill_from_scrollback = 0;\n    unsigned int amt;\n    if (!PyArg_ParseTuple(args, \"I|p\", &amt, &fill_from_scrollback)) return NULL;\n    _reverse_scroll(self, amt, fill_from_scrollback);\n    Py_RETURN_NONE;\n}\n\n\nstatic PyObject*\nscroll_prompt_to_bottom(Screen *self, PyObject *args UNUSED) {\n    if (self->linebuf != self->main_linebuf || !self->historybuf->count) Py_RETURN_NONE;\n    int q = screen_cursor_at_a_shell_prompt(self);\n    index_type limit_y = q > -1 ? (unsigned int)q : self->cursor->y;\n    index_type y = self->lines - 1;\n    // not before prompt or cursor line\n    while (y > limit_y) {\n        Line *line = checked_range_line(self, y);\n        if (!line || line_length(line)) break;\n        y--;\n    }\n    // don't scroll back beyond the history buffer range\n    unsigned int count = MIN(self->lines - (y + 1), self->historybuf->count);\n    if (count > 0) {\n        _reverse_scroll(self, count, true);\n        screen_cursor_down(self, count);\n    }\n    // always scroll to the bottom\n    if (self->scrolled_by != 0) {\n        self->scrolled_by = 0;\n        reset_pixel_scroll(self, 0);\n        dirty_scroll(self);\n    }\n    Py_RETURN_NONE;\n}\n\nstatic void\ndump_line_with_attrs(Screen *self, int y, PyObject *accum) {\n    Line *line = range_line_(self, y);\n    RAII_PyObject(u, PyUnicode_FromFormat(\"\\x1b[31m%d: \\x1b[39m\", y++));\n    if (!u) return;\n    RAII_PyObject(r1, PyObject_CallOneArg(accum, u));\n    if (!r1) return;\n#define call_string(s) { RAII_PyObject(ret, PyObject_CallFunction(accum, \"s\", s)); if (!ret) return; }\n    switch (line->attrs.prompt_kind) {\n        case UNKNOWN_PROMPT_KIND: break;\n        case PROMPT_START: call_string(\"\\x1b[32mprompt \\x1b[39m\"); break;\n        case SECONDARY_PROMPT: call_string(\"\\x1b[32msecondary_prompt \\x1b[39m\"); break;\n        case OUTPUT_START: call_string(\"\\x1b[33moutput \\x1b[39m\"); break;\n    }\n    if (range_line_is_continued(self, y)) call_string(\"continued \");\n    if (line->attrs.has_dirty_text) call_string(\"dirty \");\n    call_string(\"\\n\");\n    RAII_PyObject(t, line_as_unicode(line, false, &self->as_ansi_buf)); if (!t) return;\n    RAII_PyObject(r2, PyObject_CallOneArg(accum, t)); if (!r2) return;\n    call_string(\"\\n\");\n#undef call_string\n}\n\nstatic PyObject*\ndump_lines_with_attrs(Screen *self, PyObject *args) {\n    PyObject *accum; int which_screen = -1;\n    if (!PyArg_ParseTuple(args, \"O|i\", &accum, &which_screen)) return NULL;\n    LineBuf *orig = self->linebuf;\n    switch(which_screen) {\n        case 0: self->linebuf = self->main_linebuf; break;\n        case 1: self->linebuf = self->alt_linebuf; break;\n    }\n    int y = (self->linebuf == self->main_linebuf) ? -self->historybuf->count : 0;\n    while (y < (int)self->lines && !PyErr_Occurred()) dump_line_with_attrs(self, y++, accum);\n    self->linebuf = orig;\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\ncursor_at_prompt(Screen *self, PyObject *args UNUSED) {\n    int y = screen_cursor_at_a_shell_prompt(self);\n    if (y > -1) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\nline_edge_colors(Screen *self, PyObject *a UNUSED) {\n    color_type left, right;\n    if (!get_line_edge_colors(self, &left, &right)) { PyErr_SetString(PyExc_IndexError, \"Line number out of range\"); return NULL; }\n    return Py_BuildValue(\"kk\", (unsigned long)left, (unsigned long)right);\n}\n\nstatic PyObject*\ncurrent_selections(Screen *self, PyObject *a UNUSED) {\n    PyObject *ans = PyBytes_FromStringAndSize(NULL, (Py_ssize_t)render_lines_for_screen(self) * self->columns);\n    if (!ans) return NULL;\n    screen_apply_selection(self, PyBytes_AS_STRING(ans), PyBytes_GET_SIZE(ans));\n    return ans;\n}\n\nWRAP0(update_only_line_graphics_data)\nWRAP0(bell)\n\n#define MND(name, args) {#name, (PyCFunction)name, args, #name},\n#define MODEFUNC(name) MND(name, METH_NOARGS) MND(set_##name, METH_O)\n\nstatic PyObject*\ntest_create_write_buffer(Screen *screen UNUSED, PyObject *args UNUSED) {\n    size_t s;\n    uint8_t *buf = vt_parser_create_write_buffer(screen->vt_parser, &s);\n    return PyMemoryView_FromMemory((char*)buf, s, PyBUF_WRITE);\n}\n\nstatic PyObject*\ntest_commit_write_buffer(Screen *screen, PyObject *args) {\n    RAII_PY_BUFFER(srcbuf); RAII_PY_BUFFER(destbuf);\n    if (!PyArg_ParseTuple(args, \"y*y*\", &srcbuf, &destbuf)) return NULL;\n    size_t s = MIN(srcbuf.len, destbuf.len);\n    memcpy(destbuf.buf, srcbuf.buf, s);\n    vt_parser_commit_write(screen->vt_parser, s);\n    return PyLong_FromSize_t(s);\n}\n\nstatic PyObject*\ntest_parse_written_data(Screen *screen, PyObject *args) {\n    ParseData pd = {.now=monotonic()};\n    if (!PyArg_ParseTuple(args, \"|O\", &pd.dump_callback)) return NULL;\n    if (pd.dump_callback && pd.dump_callback != Py_None) parse_worker_dump(screen, &pd, true);\n    else parse_worker(screen, &pd, true);\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nmulticell_data_as_dict(CPUCell mcd) {\n    return Py_BuildValue(\"{sI sI sI sI sO sI sI}\",\n            \"scale\", (unsigned int)mcd.scale, \"width\", (unsigned int)mcd.width,\n            \"subscale_n\", (unsigned int)mcd.subscale_n, \"subscale_d\", (unsigned int)mcd.subscale_d,\n            \"natural_width\", mcd.natural_width ? Py_True : Py_False, \"vertical_align\", mcd.valign, \"horizontal_align\", mcd.halign);\n}\n\nstatic PyObject*\ncpu_cell_as_dict(CPUCell *c, TextCache *tc, ListOfChars *lc, HYPERLINK_POOL_HANDLE h) {\n    text_in_cell(c, tc, lc);\n    RAII_PyObject(mcd, c->is_multicell ? multicell_data_as_dict(*c) : Py_NewRef(Py_None));\n    if ((c->is_multicell && (c->x + c->y)) || (lc->count == 1 && lc->chars[0] == 0)) lc->count = 0;\n    RAII_PyObject(text, PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, lc->chars, lc->count));\n    const char *url = c->hyperlink_id ? get_hyperlink_for_id(h, c->hyperlink_id, false) : NULL;\n    RAII_PyObject(hyperlink, url ? PyUnicode_FromString(url) : Py_NewRef(Py_None));\n    return Py_BuildValue(\"{sO sO sI sI sO sO}\",\n        \"text\", text, \"hyperlink\", hyperlink, \"x\", (unsigned int)c->x, \"y\", (unsigned int)c->y,\n        \"mcd\", mcd, \"next_char_was_wrapped\", c->next_char_was_wrapped ? Py_True : Py_False\n    );\n}\n\nstatic PyObject*\ncpu_cells(Screen *self, PyObject *args) {\n    int y, x = -1;\n    if (!PyArg_ParseTuple(args, \"i|i\", &y, &x)) return NULL;\n    if (y >= (int)self->lines) { PyErr_SetString(PyExc_IndexError, \"y out of bounds\"); return NULL; }\n    CPUCell *cells;\n    if (y >= 0) cells = linebuf_cpu_cells_for_line(self->linebuf, y);\n    else {\n        Line *l = self->linebuf == self->main_linebuf ? checked_range_line(self, y) : NULL;\n        if (!l) { PyErr_SetString(PyExc_IndexError, \"y out of bounds\"); return NULL; }\n        cells = l->cpu_cells;\n    }\n    if (x > -1) {\n        if (x >= (int)self->columns) { PyErr_SetString(PyExc_IndexError, \"x out of bounds\"); return NULL; }\n        return cpu_cell_as_dict(cells + x, self->text_cache, self->lc, self->hyperlink_pool);\n    }\n    index_type start_x = 0, x_limit = self->columns;\n    RAII_PyObject(ans, PyTuple_New(x_limit - start_x));\n    if (ans) {\n        for (index_type x = start_x; x < x_limit; x++) {\n            PyObject *d = cpu_cell_as_dict(cells + x, self->text_cache, self->lc, self->hyperlink_pool);\n            if (!d) return NULL;\n            PyTuple_SET_ITEM(ans, x, d);\n        }\n    }\n    return Py_NewRef(ans);\n}\n\nstatic PyObject*\ntest_ch_and_idx(PyObject *self UNUSED, PyObject *val) {\n    CPUCell c = {0};\n    if (PyLong_Check(val)) {\n        unsigned long x = PyLong_AsUnsignedLong(val);\n        c.ch_and_idx = x;\n    } else if (PyTuple_Check(val)) {\n        c.ch_is_idx = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(val, 0));\n        c.ch_or_idx = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(val, 1));\n    }\n    unsigned long is_idx = c.ch_is_idx, idx = c.ch_or_idx, ca = c.ch_and_idx;\n    return Py_BuildValue(\"kkk\", is_idx, idx, ca);\n}\n\nstatic PyMethodDef methods[] = {\n    METHODB(test_create_write_buffer, METH_NOARGS),\n    METHODB(test_commit_write_buffer, METH_VARARGS),\n    METHODB(test_parse_written_data, METH_VARARGS),\n    MND(line_edge_colors, METH_NOARGS)\n    MND(line, METH_O)\n    MND(dump_lines_with_attrs, METH_VARARGS)\n    MND(cpu_cells, METH_VARARGS)\n    MND(cursor_at_prompt, METH_NOARGS)\n    {\"visual_line\", (PyCFunction)pyvisual_line, METH_VARARGS, \"\"},\n    MND(current_url_text, METH_NOARGS)\n    MND(draw, METH_O)\n    MND(apply_sgr, METH_O)\n    MND(cursor_position, METH_VARARGS)\n    MND(erase_last_command, METH_VARARGS)\n    MND(set_window_char, METH_VARARGS)\n    MND(set_mode, METH_VARARGS)\n    MND(reset_mode, METH_VARARGS)\n    MND(reset, METH_NOARGS)\n    MND(reset_dirty, METH_NOARGS)\n    MND(is_using_alternate_linebuf, METH_NOARGS)\n    MND(is_main_linebuf, METH_NOARGS)\n    MND(cursor_move, METH_VARARGS)\n    MND(erase_in_line, METH_VARARGS)\n    MND(erase_in_display, METH_VARARGS)\n    MND(clear_scrollback, METH_NOARGS)\n    MND(scroll_until_cursor_prompt, METH_VARARGS)\n    MND(hyperlinks_as_set, METH_NOARGS)\n    MND(garbage_collect_hyperlink_pool, METH_NOARGS)\n    MND(hyperlink_for_id, METH_O)\n    MND(reverse_scroll, METH_VARARGS)\n    MND(scroll_prompt_to_bottom, METH_NOARGS)\n    METHOD(current_char_width, METH_NOARGS)\n    MND(insert_lines, METH_VARARGS)\n    MND(delete_lines, METH_VARARGS)\n    {\"insert_characters\", (PyCFunction)py_insert_characters, METH_O, \"\"},\n    MND(delete_characters, METH_VARARGS)\n    MND(erase_characters, METH_VARARGS)\n    MND(current_pointer_shape, METH_NOARGS)\n    MND(change_pointer_shape, METH_VARARGS)\n    MND(cursor_up, METH_VARARGS)\n    MND(cursor_up1, METH_VARARGS)\n    MND(cursor_down, METH_VARARGS)\n    MND(cursor_down1, METH_VARARGS)\n    MND(cursor_forward, METH_VARARGS)\n    {\"index\", (PyCFunction)xxx_index, METH_VARARGS, \"\"},\n    {\"has_selection\", (PyCFunction)has_selection, METH_VARARGS, \"\"},\n    MND(as_text, METH_VARARGS)\n    MND(as_text_non_visual, METH_VARARGS)\n    MND(as_text_for_history_buf, METH_VARARGS)\n    MND(as_text_alternate, METH_VARARGS)\n    MND(cmd_output, METH_VARARGS)\n    MND(tab, METH_NOARGS)\n    MND(backspace, METH_NOARGS)\n    MND(linefeed, METH_NOARGS)\n    MND(carriage_return, METH_NOARGS)\n    MND(set_tab_stop, METH_NOARGS)\n    MND(clear_tab_stop, METH_VARARGS)\n    MND(start_selection, METH_VARARGS)\n    MND(update_selection, METH_VARARGS)\n    {\"clear_selection\", (PyCFunction)clear_selection_, METH_NOARGS, \"\"},\n    MND(reverse_index, METH_NOARGS)\n    MND(mark_as_dirty, METH_NOARGS)\n    MND(reload_all_gpu_data, METH_NOARGS)\n    MND(resize, METH_VARARGS)\n    MND(ignore_bells_for, METH_VARARGS)\n    MND(set_margins, METH_VARARGS)\n    MND(detect_url, METH_VARARGS)\n    MND(rescale_images, METH_NOARGS)\n    MND(current_key_encoding_flags, METH_NOARGS)\n    MND(text_for_selection, METH_VARARGS)\n    MND(text_for_marked_url, METH_VARARGS)\n    MND(is_rectangle_select, METH_NOARGS)\n    MND(scroll, METH_VARARGS)\n    MND(fractional_scroll, METH_O)\n    MND(scroll_to_prompt, METH_VARARGS)\n    MND(set_last_visited_prompt, METH_VARARGS)\n    MND(send_escape_code_to_child, METH_VARARGS)\n    MND(pause_rendering, METH_VARARGS)\n    MND(hyperlink_at, METH_VARARGS)\n    MND(toggle_alt_screen, METH_NOARGS)\n    MND(reset_callbacks, METH_NOARGS)\n    MND(paste, METH_O)\n    MND(paste_bytes, METH_O)\n    MND(focus_changed, METH_O)\n    MND(has_focus, METH_NOARGS)\n    MND(has_activity_since_last_focus, METH_NOARGS)\n    MND(copy_colors_from, METH_O)\n    MND(set_marker, METH_VARARGS)\n    MND(marked_cells, METH_NOARGS)\n    MND(scroll_to_next_mark, METH_VARARGS)\n    MND(update_only_line_graphics_data, METH_NOARGS)\n    MND(bell, METH_NOARGS)\n    MND(current_selections, METH_NOARGS)\n    {\"select_graphic_rendition\", (PyCFunction)_select_graphic_rendition, METH_VARARGS, \"\"},\n\n    {NULL}  /* Sentinel */\n};\n\nstatic PyGetSetDef getsetters[] = {\n    GETSET(in_bracketed_paste_mode)\n    GETSET(color_preference_notification)\n    GETSET(auto_repeat_enabled)\n    GETSET(focus_tracking_enabled)\n    GETSET(in_band_resize_notification)\n    GETSET(paste_events)\n    GETSET(cursor_visible)\n    GETSET(cursor_key_mode)\n    GETSET(disable_ligatures)\n    GETSET(render_unfocused_cursor)\n    {NULL}  /* Sentinel */\n};\n\n#if UINT_MAX == UINT32_MAX\n#define T_COL T_UINT\n#elif ULONG_MAX == UINT32_MAX\n#define T_COL T_ULONG\n#else\n#error Neither int nor long is 4-bytes in size\n#endif\n\nstatic PyMemberDef members[] = {\n    {\"callbacks\", T_OBJECT_EX, offsetof(Screen, callbacks), 0, \"callbacks\"},\n    {\"cursor\", T_OBJECT_EX, offsetof(Screen, cursor), READONLY, \"cursor\"},\n    {\"vt_parser\", T_OBJECT_EX, offsetof(Screen, vt_parser), READONLY, \"vt_parser\"},\n    {\"last_reported_cwd\", T_OBJECT, offsetof(Screen, last_reported_cwd), READONLY, \"last_reported_cwd\"},\n    {\"grman\", T_OBJECT_EX, offsetof(Screen, grman), READONLY, \"grman\"},\n    {\"color_profile\", T_OBJECT_EX, offsetof(Screen, color_profile), READONLY, \"color_profile\"},\n    {\"linebuf\", T_OBJECT_EX, offsetof(Screen, linebuf), READONLY, \"linebuf\"},\n    {\"main_linebuf\", T_OBJECT_EX, offsetof(Screen, main_linebuf), READONLY, \"main_linebuf\"},\n    {\"historybuf\", T_OBJECT_EX, offsetof(Screen, historybuf), READONLY, \"historybuf\"},\n    {\"scrolled_by\", T_UINT, offsetof(Screen, scrolled_by), READONLY, \"scrolled_by\"},\n    {\"lines\", T_UINT, offsetof(Screen, lines), READONLY, \"lines\"},\n    {\"columns\", T_UINT, offsetof(Screen, columns), READONLY, \"columns\"},\n    {\"margin_top\", T_UINT, offsetof(Screen, margin_top), READONLY, \"margin_top\"},\n    {\"margin_bottom\", T_UINT, offsetof(Screen, margin_bottom), READONLY, \"margin_bottom\"},\n    {\"history_line_added_count\", T_UINT, offsetof(Screen, history_line_added_count), 0, \"history_line_added_count\"},\n    {NULL}\n};\n\nPyTypeObject Screen_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.Screen\",\n    .tp_basicsize = sizeof(Screen),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Screen\",\n    .tp_methods = methods,\n    .tp_members = members,\n    .tp_new = new_screen_object,\n    .tp_getset = getsetters,\n};\n\nstatic PyMethodDef module_methods[] = {\n    {\"is_emoji_presentation_base\", (PyCFunction)screen_is_emoji_presentation_base, METH_O, \"\"},\n    {\"truncate_point_for_length\", (PyCFunction)screen_truncate_point_for_length, METH_VARARGS, \"\"},\n    {\"test_ch_and_idx\", test_ch_and_idx, METH_O, \"\"},\n    {NULL}  /* Sentinel */\n};\n\nINIT_TYPE(Screen)\n// }}}\n"
  },
  {
    "path": "kitty/screen.h",
    "content": "/*\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"vt-parser.h\"\n#include \"graphics.h\"\n#include \"monotonic.h\"\n#include \"line-buf.h\"\n#include \"history.h\"\n\ntypedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } ScrollType;\n\ntypedef struct {\n    bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM, mDECCKM, mCOLOR_PREFERENCE_NOTIFICATION,\n         mBRACKETED_PASTE, mFOCUS_TRACKING, mDECSACE, mHANDLE_TERMIOS_SIGNALS, mINBAND_RESIZE_NOTIFICATION,\n         mPASTE_EVENTS;\n    MouseTrackingMode mouse_tracking_mode;\n    MouseTrackingProtocol mouse_tracking_protocol;\n} ScreenModes;\n\ntypedef struct {\n    unsigned int x, y;\n    bool in_left_half_of_cell;\n} SelectionBoundary;\n\ntypedef enum SelectionExtendModes { EXTEND_CELL, EXTEND_WORD, EXTEND_LINE, EXTEND_LINE_FROM_POINT, EXTEND_WORD_AND_LINE_FROM_POINT } SelectionExtendMode;\n\ntypedef struct {\n    index_type x, x_limit;\n} XRange;\n\ntypedef struct {\n    int y, y_limit;\n    XRange first, body, last;\n} IterationData;\n\ntypedef struct {\n    SelectionBoundary start, end, input_start, input_current;\n    unsigned int start_scrolled_by, end_scrolled_by;\n    bool rectangle_select, adjusting_start, is_hyperlink;\n    IterationData last_rendered;\n    int sort_y, sort_x;\n    struct {\n        SelectionBoundary start, end;\n        unsigned int scrolled_by;\n    } initial_extent;\n} Selection;\n\ntypedef struct {\n    Selection *items;\n    size_t count, capacity, last_rendered_count;\n    bool in_progress, extension_in_progress;\n    SelectionExtendMode extend_mode;\n} Selections;\n\n#define SAVEPOINTS_SZ 256\n\ntypedef struct CharsetState {\n    uint32_t *zero, *one, *current, current_num;\n} CharsetState;\n\ntypedef struct {\n    Cursor cursor;\n    bool mDECOM, mDECAWM, mDECSCNM;\n    CharsetState charset;\n    bool is_valid;\n} Savepoint;\n\n\ntypedef struct {\n    PyObject *overlay_text;\n    CPUCell *cpu_cells;\n    GPUCell *gpu_cells;\n    index_type xstart, ynum, xnum, cursor_x, text_len;\n    bool is_active;\n    bool is_dirty;\n    struct {\n        CPUCell *cpu_cells;\n        GPUCell *gpu_cells;\n        Cursor cursor;\n    } original_line;\n    struct {\n        index_type x, y;\n    } last_ime_pos;\n} OverlayLine;\n\ntypedef struct ExtraCursor {\n    CursorShape shape;\n    index_type cell;\n} ExtraCursor;\n\ntypedef struct ExtraCursors {\n    ExtraCursor *locations;\n    unsigned count, capacity;\n    struct { DynamicColor cursor, text; } color;\n    bool dirty;\n} ExtraCursors;\n\n\ntypedef struct {\n    PyObject_HEAD\n\n    unsigned int columns, lines, margin_top, margin_bottom, scrolled_by, pixel_scroll_offset_y;\n    double pending_scroll_pixels_x, pending_scroll_pixels_y;\n    CellPixelSize cell_size;\n    OverlayLine overlay_line;\n    id_type window_id;\n    Selections selections, url_ranges;\n    struct {\n        unsigned int scrolled_by;\n        index_type lines, columns;\n        color_type cursor_bg;\n        CursorRenderInfo cursor;\n    } last_rendered;\n    bool is_dirty, scroll_changed, reload_all_gpu_data, sgr_blink_was_used;\n    Cursor *cursor;\n    Savepoint main_savepoint, alt_savepoint;\n    PyObject *callbacks, *test_child;\n    TextCache *text_cache;\n    LineBuf *linebuf, *main_linebuf, *alt_linebuf;\n    GraphicsManager *grman, *main_grman, *alt_grman;\n    HistoryBuf *historybuf;\n    unsigned int history_line_added_count;\n    bool *tabstops, *main_tabstops, *alt_tabstops;\n    ScreenModes modes, saved_modes;\n    ColorProfile *color_profile;\n    monotonic_t start_visual_bell_at;\n\n    uint8_t *write_buf;\n    size_t write_buf_sz, write_buf_used;\n    pthread_mutex_t write_buf_lock;\n\n    CursorRenderInfo cursor_render_info;\n\n    DisableLigature disable_ligatures;\n    PyObject *marker;\n    bool has_focus;\n    bool has_activity_since_last_focus;\n    hyperlink_id_type active_hyperlink_id;\n    HYPERLINK_POOL_HANDLE hyperlink_pool;\n    ANSIBuf as_ansi_buf;\n    char_type last_graphic_char;\n    uint8_t main_key_encoding_flags[8], alt_key_encoding_flags[8], *key_encoding_flags;\n    struct {\n        monotonic_t start, duration;\n    } ignore_bells;\n    union {\n        struct {\n            unsigned int redraws_prompts_at_all: 1;\n            unsigned int uses_special_keys_for_cursor_movement: 1;\n            unsigned int supports_click_events: 1;\n            unsigned int relative_click_events: 1;\n        };\n        unsigned int val;\n    } prompt_settings;\n    char display_window_char;\n    struct {\n        char ch;\n        uint8_t *canvas;\n        size_t requested_height, width_px, height_px;\n    } last_rendered_window_char;\n    struct {\n        unsigned int scrolled_by;\n        index_type y;\n        bool is_set;\n    } last_visited_prompt;\n    PyObject *last_reported_cwd;\n    struct {\n        hyperlink_id_type id;\n        index_type x, y;\n    } current_hyperlink_under_mouse;\n    struct {\n        uint8_t stack[16], count;\n    } main_pointer_shape_stack, alternate_pointer_shape_stack;\n    Parser *vt_parser;\n    struct {\n        monotonic_t expires_at;\n        Cursor cursor;\n        ColorProfile color_profile;\n        bool inverted, cell_data_updated, cursor_visible;\n        unsigned int scrolled_by;\n        LineBuf *linebuf;\n        GraphicsManager *grman;\n        Selections selections, url_ranges;\n        ExtraCursors extra_cursors;\n    } paused_rendering;\n    CharsetState charset;\n    ListOfChars *lc;\n    monotonic_t parsing_at;\n    ExtraCursors extra_cursors;\n} Screen;\n\n#define pixel_scroll_enabled(screen) (OPT(pixel_scroll) && !screen->paused_rendering.expires_at && screen->linebuf == screen->main_linebuf)\n#define render_lines_for_screen(screen) (screen->lines + pixel_scroll_enabled(screen))\n\nvoid screen_align(Screen*);\nvoid screen_restore_cursor(Screen *);\nvoid screen_save_cursor(Screen *);\nvoid screen_restore_modes(Screen *);\nvoid screen_restore_mode(Screen *, unsigned int);\nvoid screen_save_modes(Screen *);\nvoid screen_save_mode(Screen *, unsigned int);\nbool write_escape_code_to_child(Screen *self, unsigned char which, const char *data);\nvoid screen_cursor_position(Screen*, unsigned int, unsigned int);\nvoid screen_cursor_move(Screen *self, unsigned int count/*=1*/, int move_direction/*=-1*/, bool allow_move_to_previous_line);\nvoid screen_erase_in_line(Screen *, unsigned int, bool);\nvoid screen_erase_in_display(Screen *, unsigned int, bool);\nvoid screen_draw_text(Screen *self, const uint32_t *chars, size_t num_chars);\nvoid screen_ensure_bounds(Screen *self, bool use_margins, bool cursor_was_within_margins);\nvoid screen_toggle_screen_buffer(Screen *self, bool, bool);\nvoid screen_normal_keypad_mode(Screen *self);\nvoid screen_alternate_keypad_mode(Screen *self);\nvoid screen_change_default_color(Screen *self, unsigned int which, uint32_t col);\nvoid screen_alignment_display(Screen *self);\nvoid screen_reverse_index(Screen *self);\nvoid screen_index(Screen *self);\nvoid screen_scroll(Screen *self, unsigned int count);\nvoid screen_reverse_scroll(Screen *self, unsigned int count);\nvoid screen_reverse_scroll_and_fill_from_scrollback(Screen *self, unsigned int count);\nvoid screen_reset(Screen *self);\nvoid screen_set_tab_stop(Screen *self);\nvoid screen_tab(Screen *self);\nvoid screen_backtab(Screen *self, unsigned int);\nvoid screen_clear_tab_stop(Screen *self, unsigned int how);\nvoid screen_set_mode(Screen *self, unsigned int mode);\nvoid screen_reset_mode(Screen *self, unsigned int mode);\nvoid screen_decsace(Screen *self, unsigned int);\nvoid screen_xtversion(Screen *self, unsigned int);\nvoid screen_insert_characters(Screen *self, unsigned int count);\nvoid screen_cursor_up(Screen *self, unsigned int count/*=1*/, bool do_carriage_return/*=false*/, int move_direction/*=-1*/);\nvoid screen_set_cursor(Screen *self, unsigned int mode, uint8_t secondary);\nvoid screen_cursor_to_column(Screen *self, unsigned int column);\nvoid screen_cursor_down(Screen *self, unsigned int count/*=1*/);\nvoid screen_cursor_forward(Screen *self, unsigned int count/*=1*/);\nvoid screen_cursor_down1(Screen *self, unsigned int count/*=1*/);\nvoid screen_cursor_up1(Screen *self, unsigned int count/*=1*/);\nvoid screen_cursor_to_line(Screen *screen, unsigned int line);\nMouseShape screen_pointer_shape(Screen *self);\nvoid screen_insert_lines(Screen *self, unsigned int count/*=1*/);\nvoid screen_delete_lines(Screen *self, unsigned int count/*=1*/);\nvoid screen_repeat_character(Screen *self, unsigned int count);\nvoid screen_delete_characters(Screen *self, unsigned int count);\nvoid screen_erase_characters(Screen *self, unsigned int count);\nvoid screen_set_margins(Screen *self, unsigned int top, unsigned int bottom);\nvoid screen_push_colors(Screen *, unsigned int);\nvoid screen_pop_colors(Screen *, unsigned int);\nvoid screen_report_color_stack(Screen *);\nvoid screen_handle_kitty_dcs(Screen *, const char *callback_name, PyObject *cmd);\nvoid set_title(Screen *self, PyObject*);\nvoid osc_context(Screen *self, PyObject *ctx);\nvoid desktop_notify(Screen *self, unsigned int, PyObject*);\nvoid set_icon(Screen *self, PyObject*);\nvoid set_dynamic_color(Screen *self, unsigned int code, PyObject*);\nvoid color_control(Screen *self, unsigned int code, PyObject*);\nvoid clipboard_control(Screen *self, int code, PyObject*);\nvoid shell_prompt_marking(Screen *self, char *buf);\nvoid file_transmission(Screen *self, PyObject*);\nvoid set_color_table_color(Screen *self, unsigned int code, PyObject*);\nvoid process_cwd_notification(Screen *self, unsigned int code, const char*, size_t);\nvoid screen_request_capabilities(Screen *, char, const char *);\nvoid report_device_attributes(Screen *self, unsigned int UNUSED mode, char start_modifier);\nvoid select_graphic_rendition(Screen *self, int *params, unsigned int count, bool is_group, Region *r);\nvoid report_device_status(Screen *self, unsigned int which, bool UNUSED);\nvoid report_mode_status(Screen *self, unsigned int which, bool);\nvoid screen_apply_selection(Screen *self, void *address, size_t size);\nbool screen_is_selection_dirty(Screen *self);\nbool screen_has_selection(Screen*);\nbool screen_invert_colors(Screen *self);\nvoid screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE, bool cursor_has_moved);\nbool screen_is_cursor_visible(const Screen *self);\nunsigned screen_multi_cursor_count(const Screen *self);\nbool screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end);\nbool screen_selection_range_for_word(Screen *self, const index_type x, const index_type y, index_type *, index_type *, index_type *start, index_type *end, bool);\nvoid screen_start_selection(Screen *self, index_type x, index_type y, bool, bool, SelectionExtendMode);\ntypedef struct SelectionUpdate {\n    bool ended, start_extended_selection, set_as_nearest_extend;\n} SelectionUpdate;\nvoid screen_update_selection(Screen *self, index_type x, index_type y, bool in_left_half, SelectionUpdate upd);\nbool screen_history_scroll(Screen *self, int amt, bool upwards);\nvoid screen_history_scroll_to_absolute(Screen *self, double target_scrolled_by);\nbool screen_apply_pixel_scroll(Screen *self, double delta_pixels);\nPyObject* as_text_history_buf(HistoryBuf *self, PyObject *args, ANSIBuf *output);\nLine* screen_visual_line(Screen *self, index_type y);\nvoid screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y);\nvoid set_active_hyperlink(Screen*, char*, char*);\nhyperlink_id_type screen_mark_hyperlink(Screen*, index_type, index_type);\nvoid screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload);\nvoid screen_handle_multicell_command(Screen *self, const MultiCellCommand *cmd, const uint8_t *payload);\nbool screen_open_url(Screen*);\nbool screen_set_last_visited_prompt(Screen*, index_type);\nbool screen_select_cmd_output(Screen*, index_type);\nvoid screen_dirty_sprite_positions(Screen *self);\nvoid screen_rescale_images(Screen *self);\nvoid screen_report_size(Screen *, unsigned which, unsigned modifier);\nvoid screen_manipulate_title_stack(Screen *, unsigned int op, unsigned int which);\nbool screen_is_overlay_active(Screen *self);\nvoid screen_update_overlay_text(Screen *self, const char *utf8_text);\nvoid screen_set_key_encoding_flags(Screen *self, uint32_t val, uint32_t how);\nvoid screen_push_key_encoding_flags(Screen *self, uint32_t val);\nvoid screen_pop_key_encoding_flags(Screen *self, uint32_t num);\nuint8_t screen_current_key_encoding_flags(Screen *self);\nvoid screen_modify_other_keys(Screen *self, unsigned, unsigned);\nvoid screen_report_key_encoding_flags(Screen *self);\nint screen_detect_url(Screen *screen, unsigned int x, unsigned int y);\nint screen_cursor_at_a_shell_prompt(const Screen *);\nbool screen_prompt_supports_click_events(const Screen *, bool *is_relative);\nbool screen_fake_move_cursor_to_position(Screen *, index_type x, index_type y);\nbool screen_send_signal_for_key(Screen *, char key);\nbool get_line_edge_colors(Screen *self, color_type *left, color_type *right);\nbool parse_sgr(Screen *screen, const uint8_t *buf, unsigned int num, const char *report_name, bool is_deccara);\nbool screen_pause_rendering(Screen *self, bool pause, int for_in_ms);\nvoid screen_check_pause_rendering(Screen *self, monotonic_t now);\nvoid screen_designate_charset(Screen *self, uint32_t which, uint32_t as);\nvoid screen_multi_cursor(Screen *self, int queried_shape, int *params, unsigned num_params);\n#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);\nDECLARE_CH_SCREEN_HANDLER(bell)\nDECLARE_CH_SCREEN_HANDLER(backspace)\nDECLARE_CH_SCREEN_HANDLER(tab)\nDECLARE_CH_SCREEN_HANDLER(linefeed)\nDECLARE_CH_SCREEN_HANDLER(carriage_return)\n#undef DECLARE_CH_SCREEN_HANDLER\n"
  },
  {
    "path": "kitty/screenshot_fragment.glsl",
    "content": "#pragma kitty_include_shader <linear2srgb.glsl>\n\nuniform sampler2D image;\nuniform vec2 src_size;  // Source texture size in pixels\n\nin vec2 texcoord;\nout vec4 output_color;\n\nvoid main() {\n    // The input texture contains sRGB colors with premultiplied alpha.\n    // We need to output unpremultiplied sRGB colors with proper downscaling.\n\n    // For proper downscaling, we need to:\n    // 1. Sample neighboring pixels\n    // 2. Convert from sRGB to linear (unpremultiplying first)\n    // 3. Average in linear space\n    // 4. Convert back to sRGB\n    // 5. Output unpremultiplied\n\n    // Calculate the texel size\n    vec2 texel_size = 1.0 / src_size;\n\n    // Sample a 2x2 grid for better quality downscaling\n    // This provides basic bilinear-like filtering in linear space\n    vec2 tc = texcoord;\n\n    vec4 s00 = texture(image, tc + vec2(-0.25, -0.25) * texel_size);\n    vec4 s10 = texture(image, tc + vec2( 0.25, -0.25) * texel_size);\n    vec4 s01 = texture(image, tc + vec2(-0.25,  0.25) * texel_size);\n    vec4 s11 = texture(image, tc + vec2( 0.25,  0.25) * texel_size);\n\n    // Unpremultiply and convert to linear for each sample\n    vec3 linear00 = s00.a > 0.0 ? srgb2linear(s00.rgb / s00.a) : vec3(0.0);\n    vec3 linear10 = s10.a > 0.0 ? srgb2linear(s10.rgb / s10.a) : vec3(0.0);\n    vec3 linear01 = s01.a > 0.0 ? srgb2linear(s01.rgb / s01.a) : vec3(0.0);\n    vec3 linear11 = s11.a > 0.0 ? srgb2linear(s11.rgb / s11.a) : vec3(0.0);\n\n    // Average the alpha values\n    float avg_alpha = (s00.a + s10.a + s01.a + s11.a) * 0.25;\n\n    // For proper downsampling with transparency, weight colors by their alpha\n    // This ensures partially transparent pixels contribute proportionally\n    vec3 weighted_sum = linear00 * s00.a + linear10 * s10.a + linear01 * s01.a + linear11 * s11.a;\n    float total_weight = s00.a + s10.a + s01.a + s11.a;\n\n    // Calculate the weighted average color in linear space\n    vec3 avg_linear = total_weight > 0.0 ? weighted_sum / total_weight : vec3(0.0);\n\n    // Convert back to sRGB\n    vec3 srgb_color = linear2srgb(avg_linear);\n\n    // Output unpremultiplied sRGB color\n    output_color = vec4(srgb_color, avg_alpha);\n}\n"
  },
  {
    "path": "kitty/screenshot_vertex.glsl",
    "content": "uniform vec4 src_rect, dest_rect;\n#pragma kitty_include_shader <blit_common.glsl>\n"
  },
  {
    "path": "kitty/search_query_parser.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\nimport re\nfrom collections.abc import Callable, Iterator, Sequence\nfrom enum import Enum\nfrom functools import lru_cache\nfrom gettext import gettext as _\nfrom typing import NamedTuple, TypeVar\n\nfrom .types import run_once\n\n\nclass ParseException(Exception):\n\n    hide_traceback = True\n\n    @property\n    def msg(self) -> str:\n        if len(self.args) > 0:\n            return str(self.args[0])\n        return \"\"\n\n\nclass ExpressionType(Enum):\n    OR = 1\n    AND = 2\n    NOT = 3\n    TOKEN = 4\n\n\nclass TokenType(Enum):\n    OPCODE = 1\n    WORD = 2\n    QUOTED_WORD = 3\n    EOF = 4\n\n\nT = TypeVar('T')\nGetMatches = Callable[[str, str, set[T]], set[T]]\n\n\nclass SearchTreeNode:\n    type = ExpressionType.OR\n\n    def __init__(self, type: ExpressionType) -> None:\n        self.type = type\n\n    def search(self, universal_set: set[T], get_matches: GetMatches[T]) -> set[T]:\n        return self(universal_set, get_matches)\n\n    def __call__(self, candidates: set[T], get_matches: GetMatches[T]) -> set[T]:\n        return set()\n\n    def iter_token_nodes(self) -> Iterator['TokenNode']:\n        return iter(())\n\n\nclass OrNode(SearchTreeNode):\n\n    def __init__(self, lhs: SearchTreeNode, rhs: SearchTreeNode) -> None:\n        self.lhs = lhs\n        self.rhs = rhs\n\n    def __call__(self, candidates: set[T], get_matches: GetMatches[T]) -> set[T]:\n        lhs = self.lhs(candidates, get_matches)\n        return lhs.union(self.rhs(candidates.difference(lhs), get_matches))\n\n    def iter_token_nodes(self) -> Iterator['TokenNode']:\n        yield from self.lhs.iter_token_nodes()\n        yield from self.rhs.iter_token_nodes()\n\n\nclass AndNode(SearchTreeNode):\n    type = ExpressionType.AND\n\n    def __init__(self, lhs: SearchTreeNode, rhs: SearchTreeNode) -> None:\n        self.lhs = lhs\n        self.rhs = rhs\n\n    def __call__(self, candidates: set[T], get_matches: GetMatches[T]) -> set[T]:\n        lhs = self.lhs(candidates, get_matches)\n        return self.rhs(lhs, get_matches)\n\n    def iter_token_nodes(self) -> Iterator['TokenNode']:\n        yield from self.lhs.iter_token_nodes()\n        yield from self.rhs.iter_token_nodes()\n\n\nclass NotNode(SearchTreeNode):\n    type = ExpressionType.NOT\n\n    def __init__(self, rhs: SearchTreeNode) -> None:\n        self.rhs = rhs\n\n    def __call__(self, candidates: set[T], get_matches: GetMatches[T]) -> set[T]:\n        return candidates.difference(self.rhs(candidates, get_matches))\n\n    def iter_token_nodes(self) -> Iterator['TokenNode']:\n        yield from self.rhs.iter_token_nodes()\n\n\nclass TokenNode(SearchTreeNode):\n    type = ExpressionType.TOKEN\n\n    def __init__(self, location: str, query: str) -> None:\n        self.location = location\n        self.query = query\n\n    def __call__(self, candidates: set[T], get_matches: GetMatches[T]) -> set[T]:\n        return get_matches(self.location, self.query, candidates)\n\n    def iter_token_nodes(self) -> Iterator['TokenNode']:\n        yield self\n\n\nclass Token(NamedTuple):\n    type: TokenType\n    val: str\n\n\n@run_once\ndef lex_scanner() -> Callable[[str], tuple[list[Token], str]]:\n    return getattr(re, 'Scanner')([  # type: ignore\n            (r'[()]', lambda x, t: Token(TokenType.OPCODE, t)),\n            (r'@.+?:[^\")\\s]+', lambda x, t: Token(TokenType.WORD, str(t))),\n            (r'[^\"()\\s]+', lambda x, t: Token(TokenType.WORD, str(t))),\n            (r'\".*?((?<!\\\\)\")', lambda x, t: Token(TokenType.QUOTED_WORD, t[1:-1])),\n            (r'\\s+',              None)\n    ], flags=re.DOTALL).scan\n\n\n@run_once\ndef replacements() -> tuple[tuple[str, str], ...]:\n    return tuple(('\\\\' + x, chr(i + 1)) for i, x in enumerate('\\\\\"()'))\n\n\nclass NoLocation(ParseException):\n\n    def __init__(self, tt: str):\n        a, sep, b = tt.partition(':')\n        if sep == ':':\n            super().__init__(f'{a} is not a recognized location in {tt}')\n        else:\n            super().__init__(f'No location specified before {tt}')\n\n\nclass Parser:\n\n    def __init__(self, allow_no_location: bool = False) -> None:\n        self.current_token = 0\n        self.tokens: list[Token] = []\n        self.allow_no_location = allow_no_location\n\n    def token(self, advance: bool = False) -> str | None:\n        if self.is_eof():\n            return None\n        res = self.tokens[self.current_token].val\n        if advance:\n            self.current_token += 1\n        return res\n\n    def lcase_token(self, advance: bool = False) -> str | None:\n        if self.is_eof():\n            return None\n        res = self.tokens[self.current_token].val\n        if advance:\n            self.current_token += 1\n        return res.lower()\n\n    def token_type(self) -> TokenType:\n        if self.is_eof():\n            return TokenType.EOF\n        return self.tokens[self.current_token].type\n\n    def is_eof(self) -> bool:\n        return self.current_token >= len(self.tokens)\n\n    def advance(self) -> None:\n        self.current_token += 1\n\n    def tokenize(self, expr: str) -> list[Token]:\n        # Strip out escaped backslashes, quotes and parens so that the\n        # lex scanner doesn't get confused. We put them back later.\n        for k, v in replacements():\n            expr = expr.replace(k, v)\n        tokens, leftover = lex_scanner()(expr)\n        if leftover:\n            raise ParseException(_('Extra characters at end of search'))\n\n        def unescape(x: str) -> str:\n            for k, v in replacements():\n                x = x.replace(v, k[1:])\n            return x\n\n        return [\n            Token(tt, unescape(tv) if tt in (TokenType.WORD, TokenType.QUOTED_WORD) else tv)\n            for tt, tv in tokens\n        ]\n\n    def parse(self, expr: str, locations: Sequence[str]) -> SearchTreeNode:\n        self.locations = locations\n        self.tokens = self.tokenize(expr)\n        self.current_token = 0\n        prog = self.or_expression()\n        if not self.is_eof():\n            raise ParseException(_('Extra characters at end of search'))\n        return prog\n\n    def or_expression(self) -> SearchTreeNode:\n        lhs = self.and_expression()\n        if self.lcase_token() == 'or':\n            self.advance()\n            return OrNode(lhs, self.or_expression())\n        return lhs\n\n    def and_expression(self) -> SearchTreeNode:\n        lhs = self.not_expression()\n        if self.lcase_token() == 'and':\n            self.advance()\n            return AndNode(lhs, self.and_expression())\n\n        # Account for the optional 'and'\n        if ((self.token_type() in (TokenType.WORD, TokenType.QUOTED_WORD) or self.token() == '(') and self.lcase_token() != 'or'):\n            return AndNode(lhs, self.and_expression())\n        return lhs\n\n    def not_expression(self) -> SearchTreeNode:\n        if self.lcase_token() == 'not':\n            self.advance()\n            return NotNode(self.not_expression())\n        return self.location_expression()\n\n    def location_expression(self) -> SearchTreeNode:\n        if self.token_type() == TokenType.OPCODE and self.token() == '(':\n            self.advance()\n            res = self.or_expression()\n            if self.token_type() != TokenType.OPCODE or self.token(advance=True) != ')':\n                raise ParseException(_('missing )'))\n            return res\n        if self.token_type() not in (TokenType.WORD, TokenType.QUOTED_WORD):\n            raise ParseException(_('Invalid syntax. Expected a lookup name or a word'))\n\n        return self.base_token()\n\n    def base_token(self) -> SearchTreeNode:\n        if self.token_type() is TokenType.QUOTED_WORD:\n            tt = self.token(advance=True)\n            assert tt is not None\n            if self.allow_no_location:\n                return TokenNode('all', tt)\n            raise NoLocation(tt)\n\n        tt = self.token(advance=True)\n        assert tt is not None\n        words = tt.split(':')\n        # The complexity here comes from having colon-separated search\n        # values. That forces us to check that the first \"word\" in a colon-\n        # separated group is a valid location. If not, then the token must\n        # be reconstructed. We also have the problem that locations can be\n        # followed by quoted strings that appear as the next token. and that\n        # tokens can be a sequence of colons.\n\n        # We have a location if there is more than one word and the first\n        # word is in locations. This check could produce a \"wrong\" answer if\n        # the search string is something like 'author: \"foo\"' because it\n        # will be interpreted as 'author:\"foo\"'. I am choosing to accept the\n        # possible error. The expression should be written '\"author:\" foo'\n        if len(words) > 1 and words[0].lower() in self.locations:\n            loc = words[0].lower()\n            words = words[1:]\n            if len(words) == 1 and self.token_type() == TokenType.QUOTED_WORD:\n                tt = self.token(advance=True)\n                assert tt is not None\n                return TokenNode(loc, tt)\n            return TokenNode(loc.lower(), ':'.join(words))\n\n        if self.allow_no_location:\n            return TokenNode('all', ':'.join(words))\n        raise NoLocation(tt)\n\n\n@lru_cache(maxsize=64)\ndef build_tree(query: str, locations: str | tuple[str, ...], allow_no_location: bool = False) -> SearchTreeNode:\n    if isinstance(locations, str):\n        locations = tuple(locations.split())\n    p = Parser(allow_no_location)\n    try:\n        return p.parse(query, locations)\n    except RuntimeError as e:\n        raise ParseException(f'Failed to parse {query!r}, too much recursion required') from e\n\n\ndef search(\n    query: str, locations: str | tuple[str, ...], universal_set: set[T], get_matches: GetMatches[T],\n    allow_no_location: bool = False,\n) -> set[T]:\n    return build_tree(query, locations, allow_no_location).search(universal_set, get_matches)\n"
  },
  {
    "path": "kitty/session.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport os\nimport re\nimport shlex\nimport sys\nfrom collections.abc import Callable, Generator, Iterator, Mapping\nfrom contextlib import suppress\nfrom functools import partial\nfrom gettext import gettext as _\nfrom typing import TYPE_CHECKING, Any, Optional, Sequence, Union\n\nfrom .cli_stub import CLIOptions, GotoSessionOptions, SaveAsSessionOptions\nfrom .constants import config_dir, unserialize_launch_flag\nfrom .fast_data_types import get_options\nfrom .layout.interface import all_layouts\nfrom .options.types import Options\nfrom .options.utils import resize_window, to_layout_names, window_size\nfrom .os_window_size import WindowSize, WindowSizeData, WindowSizes\nfrom .typing_compat import BossType, SpecialWindowInstance, WindowType\nfrom .utils import expandvars, log_error, resolve_custom_file, resolved_shell, shlex_split\n\nif TYPE_CHECKING:\n    from .launch import LaunchSpec\n    from .window import CwdRequest\n\n\ndef get_os_window_sizing_data(opts: Options, session: Optional['Session'] = None) -> WindowSizeData:\n    if session is None or session.os_window_size is None:\n        sizes = WindowSizes(WindowSize(*opts.initial_window_width), WindowSize(*opts.initial_window_height))\n    else:\n        sizes = session.os_window_size\n    return WindowSizeData(\n        sizes, opts.remember_window_size, opts.single_window_margin_width, opts.window_margin_width,\n        opts.single_window_padding_width, opts.window_padding_width)\n\n\nResizeSpec = tuple[str, int]\n\n\nclass WindowSpec:\n\n    def __init__(\n        self, launch_spec: Union['LaunchSpec', 'SpecialWindowInstance'], serialized_id: int = 0,\n        run_command_at_shell_startup: Sequence[str] | str = ()\n):\n        self.launch_spec = launch_spec\n        self.resize_spec: ResizeSpec | None = None\n        self.focus_matching_window_spec: str = ''\n        self.is_background_process = False\n        self.serialized_id = serialized_id\n        self.run_command_at_shell_startup = run_command_at_shell_startup\n        if hasattr(launch_spec, 'opts'):  # LaunchSpec\n            from .launch import LaunchSpec\n            assert isinstance(launch_spec, LaunchSpec)\n            self.is_background_process = launch_spec.opts.type == 'background'\n\n\nclass Tab:\n\n    def __init__(self, opts: Options, name: str):\n        self.windows: list[WindowSpec] = []\n        self.pending_resize_spec: ResizeSpec | None = None\n        self.pending_focus_matching_window: str = ''\n        self.name = name.strip()\n        self.active_window_idx = -1\n        self.enabled_layouts = opts.enabled_layouts\n        self.layout = (self.enabled_layouts or ['tall'])[0]\n        self.layout_state: dict[str, Any] | None = None\n        self.cwd: str | None = None\n        self.next_title: str | None = None\n\n    @property\n    def has_non_background_processes(self) -> bool:\n        for w in self.windows:\n            if not w.is_background_process:\n                return True\n        return False\n\n\nclass Session:\n\n    session_name: str = ''\n    num_of_windows_in_definition: int = 0\n\n    def __init__(self, default_title: str | None = None):\n        self.tabs: list[Tab] = []\n        self.active_tab_idx = 0\n        self.focus_tab_spec: str | None = None\n        self.default_title = default_title\n        self.os_window_size: WindowSizes | None = None\n        self.os_window_class: str | None = None\n        self.os_window_name: str | None = None\n        self.os_window_state: str | None = None\n        self.os_window_title: str | None = None\n        self.focus_os_window: bool = False\n\n    @property\n    def has_non_background_processes(self) -> bool:\n        for t in self.tabs:\n            if t.has_non_background_processes:\n                return True\n        return False\n\n    def add_tab(self, opts: Options, name: str = '') -> None:\n        if self.tabs and not self.tabs[-1].windows:\n            del self.tabs[-1]\n        self.tabs.append(Tab(opts, name))\n\n    def set_next_title(self, title: str) -> None:\n        self.tabs[-1].next_title = title.strip()\n\n    def set_layout(self, val: str) -> None:\n        if val.partition(':')[0] not in all_layouts:\n            raise ValueError(f'{val} is not a valid layout')\n        self.tabs[-1].layout = val\n\n    def set_layout_state(self, val: str) -> None:\n        self.tabs[-1].layout_state = json.loads(val)\n\n    def add_window(self, cmd: None | str | list[str], expand: Callable[[str], str] = lambda x: x) -> None:\n        from .launch import parse_launch_args\n        needs_expandvars = False\n        if isinstance(cmd, str) and cmd:\n            needs_expandvars = True\n            cmd = list(shlex_split(cmd))\n        serialize_data: dict[str, Any] = {'id': 0, 'cmd_at_shell_startup': ()}\n        if cmd and cmd[0].startswith(unserialize_launch_flag):\n            serialize_data = json.loads(cmd[0][len(unserialize_launch_flag):])\n            del cmd[0]\n        spec = parse_launch_args(cmd)\n        if needs_expandvars:\n            assert isinstance(cmd, list)\n            limit = len(cmd)\n            if len(spec.args):\n                with suppress(ValueError):\n                    limit = cmd.index(spec.args[0])\n            cmd = [(expand(x) if i < limit else x) for i, x in enumerate(cmd)]\n            spec = parse_launch_args(cmd)\n\n        t = self.tabs[-1]\n        if t.next_title and not spec.opts.window_title:\n            spec.opts.window_title = t.next_title\n        spec.opts.cwd = spec.opts.cwd or t.cwd\n        t.windows.append(WindowSpec(\n            spec, serialized_id=serialize_data['id'],\n            run_command_at_shell_startup=serialize_data.get('cmd_at_shell_startup', ())))\n        t.next_title = None\n        if t.pending_resize_spec is not None:\n            t.windows[-1].resize_spec = t.pending_resize_spec\n            t.pending_resize_spec = None\n        if t.pending_focus_matching_window:\n            t.windows[-1].focus_matching_window_spec = t.pending_focus_matching_window\n            t.pending_focus_matching_window = ''\n\n    def resize_window(self, args: list[str]) -> None:\n        s = resize_window('resize_window', shlex.join(args))[1]\n        spec: ResizeSpec = s[0], s[1]\n        t = self.tabs[-1]\n        if t.windows:\n            t.windows[-1].resize_spec = spec\n        else:\n            t.pending_resize_spec = spec\n\n    def focus_matching_window(self, spec: str) -> None:\n        t = self.tabs[-1]\n        if t.windows:\n            t.windows[-1].focus_matching_window_spec = spec\n        else:\n            t.pending_focus_matching_window = spec\n\n    def add_special_window(self, sw: 'SpecialWindowInstance') -> None:\n        self.tabs[-1].windows.append(WindowSpec(sw))\n\n    def focus(self) -> None:\n        self.active_tab_idx = max(0, len(self.tabs) - 1)\n        self.tabs[-1].active_window_idx = max(0, len(self.tabs[-1].windows) - 1)\n\n    def focus_tab(self, spec: str) -> None:\n        self.focus_tab_spec = spec\n\n    def set_enabled_layouts(self, raw: str) -> None:\n        self.tabs[-1].enabled_layouts = to_layout_names(raw)\n        if self.tabs[-1].layout not in self.tabs[-1].enabled_layouts:\n            self.tabs[-1].layout = self.tabs[-1].enabled_layouts[0]\n\n    def set_cwd(self, val: str, session_base_dir: str) -> None:\n        if val:\n            val = os.path.expanduser(val)\n            if not os.path.isabs(val):\n                val = os.path.abspath(os.path.join(session_base_dir, val))\n            self.tabs[-1].cwd = val\n        elif session_base_dir:\n            self.tabs[-1].cwd = session_base_dir\n\n\nSESSION_FILE_EXTENSIONS = {'session', 'kitty-session', 'kitty_session'}\n\n\ndef has_session_extension(path: str) -> bool:\n    name = os.path.basename(path)\n    return name.rpartition('.')[2] in SESSION_FILE_EXTENSIONS\n\n\ndef session_arg_to_name(session_arg: str) -> str:\n    if session_arg in ('-', '/dev/stdin', 'none'):\n        session_arg = ''\n    session_name = os.path.basename(session_arg)\n    if has_session_extension(session_name):\n        session_name = session_name.rpartition('.')[0]\n    return session_name\n\n\ndef resolve_session_arg_path(path: str) -> str:\n    path = os.path.expanduser(path)\n    if not os.path.isabs(path):\n        path = os.path.join(config_dir, path)\n    return os.path.abspath(path)\n\n\ndef parse_session(\n    raw: str, opts: Options, environ: Mapping[str, str] | None = None, session_arg: str = '', session_path: str = ''\n) -> Generator[Session, None, None]:\n    session_name = session_arg_to_name(session_arg)\n    if session_path:\n        session_base_dir = os.path.dirname(os.path.abspath(session_path))\n        if session_name:\n            seen_session_paths[session_name] = session_path\n            append_to_session_history(session_name)\n    else:\n        session_base_dir = os.getcwd()\n\n    def finalize_session(ans: Session) -> Session:\n        ans.session_name = session_name\n        ans.num_of_windows_in_definition = sum(len(t.windows) for t in ans.tabs)\n        from .tabs import SpecialWindow\n        for t in ans.tabs:\n            if not t.windows:\n                t.windows.append(WindowSpec(SpecialWindow(cmd=resolved_shell(opts))))\n        return ans\n\n    if environ is None:\n        environ = os.environ\n    expand = partial(expandvars, env=environ, fallback_to_os_env=False)\n    ans = Session()\n    ans.add_tab(opts)\n    for line in raw.splitlines():\n        line = line.strip()\n        if line and not line.startswith('#'):\n            parts = line.split(maxsplit=1)\n            if len(parts) == 1:\n                cmd, rest = parts[0], ''\n            else:\n                cmd, rest = parts\n            cmd, rest = cmd.strip(), rest.strip()\n            if cmd not in ('launch', 'set_layout_state'):\n                rest = expand(rest)\n            if cmd == 'new_tab':\n                ans.add_tab(opts, rest)\n            elif cmd == 'new_os_window':\n                yield finalize_session(ans)\n                ans = Session()\n                ans.add_tab(opts, rest)\n            elif cmd == 'layout':\n                ans.set_layout(rest)\n            elif cmd == 'launch':\n                ans.add_window(rest, expand)\n            elif cmd == 'focus':\n                ans.focus()\n            elif cmd == 'focus_tab':\n                ans.focus_tab(rest)\n            elif cmd == 'focus_os_window':\n                ans.focus_os_window = True\n            elif cmd == 'enabled_layouts':\n                ans.set_enabled_layouts(rest)\n            elif cmd == 'cd':\n                ans.set_cwd(rest, session_base_dir)\n            elif cmd == 'title':\n                ans.set_next_title(rest)\n            elif cmd == 'os_window_size':\n                w, h = map(window_size, rest.split(maxsplit=1))\n                ans.os_window_size = WindowSizes(WindowSize(*w), WindowSize(*h))\n            elif cmd == 'os_window_class':\n                ans.os_window_class = rest\n            elif cmd == 'os_window_name':\n                ans.os_window_name = rest\n            elif cmd == 'os_window_title':\n                ans.os_window_title = rest\n            elif cmd == 'os_window_state':\n                ans.os_window_state = rest\n            elif cmd == 'resize_window':\n                ans.resize_window(rest.split())\n            elif cmd == 'focus_matching_window':\n                ans.focus_matching_window(rest)\n            elif cmd == 'set_layout_state':\n                ans.set_layout_state(rest)\n            else:\n                raise ValueError(f'Unknown command in session file: {cmd}')\n    yield finalize_session(ans)\n\n\nclass PreReadSession(str):\n\n    associated_environ: Mapping[str, str]\n    session_arg: str\n    session_path: str\n\n    def __new__(cls, val: str, associated_environ: Mapping[str, str], session_arg: str, session_path: str) -> 'PreReadSession':\n        ans: PreReadSession = str.__new__(cls, val)\n        ans.associated_environ = associated_environ\n        ans.session_arg = session_arg\n        ans.session_path = session_path\n        return ans\n\n\ndef create_sessions(\n    opts: Options,\n    args: CLIOptions | None = None,\n    special_window: Optional['SpecialWindowInstance'] = None,\n    cwd_from: Optional['CwdRequest'] = None,\n    respect_cwd: bool = False,\n    default_session: str | None = None,\n    env_when_no_session: dict[str, str] | None = None,\n) -> Iterator[Session]:\n    if args and args.session:\n        if args.session == \"none\":\n            default_session = \"none\"\n        else:\n            session_arg = args.session\n            session_path = ''\n            environ: Mapping[str, str] | None = None\n            if isinstance(args.session, PreReadSession):\n                session_data = '' + str(args.session)\n                environ = args.session.associated_environ\n                session_arg = args.session.session_arg\n                session_path = args.session.session_path\n            else:\n                if args.session == '-':\n                    f = sys.stdin\n                else:\n                    f = open(resolve_custom_file(args.session))\n                with f:\n                    session_data = f.read()\n                    session_path = f.name\n            yield from parse_session(session_data, opts, environ=environ, session_arg=session_arg, session_path=session_path)\n            return\n    if default_session and default_session != 'none' and not getattr(args, 'args', None):\n        session_arg = session_arg_to_name(default_session)\n        try:\n            with open(default_session) as f:\n                session_data = f.read()\n                session_path = f.name\n        except OSError:\n            log_error(f'Failed to read from session file, ignoring: {default_session}')\n        else:\n            yield from parse_session(session_data, opts, session_arg=session_arg, session_path=session_path)\n            return\n    ans = Session()\n    current_layout = opts.enabled_layouts[0] if opts.enabled_layouts else 'tall'\n    ans.add_tab(opts)\n    ans.tabs[-1].layout = current_layout\n    if args is not None:\n        ans.os_window_class = args.cls\n        ans.os_window_name = args.name\n        ans.os_window_title = args.title\n    if special_window is None:\n        cmd = args.args if args and args.args else resolved_shell(opts)\n        from kitty.tabs import SpecialWindow\n        cwd: str | None = args.directory if respect_cwd and args else None\n        special_window = SpecialWindow(cmd, cwd_from=cwd_from, cwd=cwd, env=env_when_no_session, hold=bool(args and args.hold))\n    ans.add_special_window(special_window)\n    yield ans\n\n\ndef window_for_session_name(boss: BossType, session_name: str) -> WindowType | None:\n    windows = [w for w in boss.all_windows if w.created_in_session_name == session_name]\n    if not windows:\n        tabs = (t for t in boss.all_tabs if t.created_in_session_name == session_name)\n        windows = [t.active_window for t in tabs if t.active_window]\n        if not windows:\n            os_windows = (tm for tm in boss.all_tab_managers if tm.created_in_session_name == session_name)\n            windows = [tm.active_window for tm in os_windows if tm.active_window]\n    if windows:\n        def skey(w: WindowType) -> float:\n            return w.last_focused_at\n        windows.sort(key=skey, reverse=True)\n        return windows[0]\n    return None\n\n\nseen_session_paths: dict[str, str] = {}\n\n\ndef create_session(boss: BossType, path: str) -> tuple[str, bool]:\n    session_name = ''\n    created_new_os_window = False\n    for i, s in enumerate(create_sessions(get_options(), default_session=path)):\n        if i == 0:\n            session_name = s.session_name\n            if s.num_of_windows_in_definition == 0:  # leading new_os_window\n                continue\n            tm = boss.active_tab_manager\n            if tm is None:\n                os_window_id = boss.add_os_window(s)\n                created_new_os_window = True\n            else:\n                os_window_id = tm.os_window_id\n                tm.add_tabs_from_session(s, session_name)\n        else:\n            os_window_id = boss.add_os_window(s)\n            created_new_os_window = True\n        if s.focus_os_window:\n            boss.focus_os_window(os_window_id)\n    return session_name, created_new_os_window\n\n\ngoto_session_history: list[str] = []\n\n\ndef append_to_session_history(name: str) -> None:\n    with suppress(ValueError):\n        goto_session_history.remove(name)\n    goto_session_history.append(name)\n\n\ndef most_recent_session() -> str:\n    return goto_session_history[-1] if goto_session_history else ''\n\n\ndef switch_to_session(boss: BossType, session_name: str) -> bool:\n    w = window_for_session_name(boss, session_name)\n    if w is not None:\n        append_to_session_history(session_name)\n        boss.set_active_window(w, switch_os_window_if_needed=True)\n        return True\n    return False\n\n\ndef resolve_session_path_and_name(path: str) -> tuple[str, str]:\n    path = resolve_session_arg_path(path)\n    return path, session_arg_to_name(path)\n\n\ndef get_all_known_sessions() -> dict[str, str]:\n    opts = get_options()\n    all_known_sessions = seen_session_paths.copy()\n    for km in opts.keyboard_modes.values():\n        for kdefs in km.keymap.values():\n            for kd in kdefs:\n                for key_action in opts.alias_map.resolve_aliases(kd.definition, 'map'):\n                    if key_action.func == 'goto_session':\n                        path = ''\n                        for x in key_action.args:\n                            if isinstance(x, str) and not x.startswith('-'):\n                                path = x\n                                break\n                        if path:\n                            path, session_name = resolve_session_path_and_name(path)\n                            if session_name not in all_known_sessions:\n                                all_known_sessions[session_name] = path\n    return all_known_sessions\n\n\ndef close_session_with_confirm(boss: BossType, cmdline: Sequence[str]) -> None:\n    if not cmdline:\n        names = sorted(boss.all_loaded_session_names, key=lambda x: x.lower())\n        if not names:\n            boss.ring_bell_if_allowed()\n            return\n        if len(names) == 1:\n            return close_session_with_confirm(boss, names)\n        def chosen(name: str | None) -> None:\n            if name:\n                close_session_with_confirm(boss, (name,))\n        boss.choose_entry(\n            _('Select a session to close'), ((name, name) for name in names), chosen)\n        return\n    if len(cmdline) != 1:\n        boss.show_error(_('Invalid close_session specification'), _('{} is not a valid argument to close_session').format(shlex.join(cmdline)))\n        return\n    path_or_name = cmdline[0]\n    if path_or_name == '.':\n        if name := boss.active_session:\n            close_session_with_confirm(boss, (name,))\n        else:\n            boss.ring_bell_if_allowed()\n        return\n    if '/' in path_or_name:\n        path_to_name = {v: k for k, v in get_all_known_sessions().items()}\n        name = path_to_name.get(path_or_name, '')\n        if not name:\n            boss.ring_bell_if_allowed()\n            return\n    else:\n        name = path_or_name\n    windows = tuple(w for w in boss.all_windows if w.created_in_session_name == name)\n    if not windows:\n        return\n    msg, num_active_windows = boss.close_windows_with_confirmation_msg(windows, boss.active_window)\n    x = get_options().confirm_os_window_close[0]\n    num = num_active_windows if x < 0 else len(windows)\n    needs_confirmation = x != 0 and num >= abs(x)\n\n    def do_close(confirmed: bool) -> None:\n        if confirmed:\n            boss.close_windows_no_confirm(windows)\n\n    if needs_confirmation:\n        msg = msg or _('It has {} windows?').format(num)\n        msg = _('Are you sure you want to close this session?') + ' ' + msg\n        boss.confirm(msg, do_close, window=boss.active_window, title=_('Close session?'))\n    else:\n        do_close(True)\n\n\ndef choose_session_from_map(\n    boss: BossType, opts: GotoSessionOptions, session_map: Mapping[str, str], title: str\n) -> bool:\n    if not session_map:\n        return False\n    hmap = {n: len(goto_session_history)-i for i, n in enumerate(goto_session_history)}\n    if opts.sort_by == 'alphabetical':\n        def skey(name: str) -> tuple[int, str]:\n            return 0, name.lower()\n    else:\n        def skey(name: str) -> tuple[int, str]:\n            return hmap.get(name, len(goto_session_history)), name.lower()\n    names = sorted(session_map, key=skey)\n\n    def chosen(name: str | None) -> None:\n        if name:\n            goto_session(boss, (session_map[name],))\n    boss.choose_entry(title, ((name, name) for name in names), chosen)\n    return True\n\n\ndef choose_session(boss: BossType, opts: GotoSessionOptions) -> None:\n    all_known_sessions = get_all_known_sessions()\n    if opts.active_only:\n        all_active_sessions = boss.all_loaded_session_names\n        all_known_sessions = {name: all_known_sessions[name] for name in all_active_sessions if name in all_known_sessions}\n    choose_session_from_map(boss, opts, all_known_sessions, _('Select a session to activate'))\n\n\ndef choose_session_in_directory(boss: BossType, opts: GotoSessionOptions, directory_path: str) -> None:\n    try:\n        with os.scandir(directory_path) as entries:\n            session_map = {\n                session_arg_to_name(entry.path): entry.path\n                for entry in entries\n                if entry.is_file() and has_session_extension(entry.name)\n            }\n    except OSError as e:\n        boss.show_error(\n            _('Failed to list sessions'),\n            _('Could not list session files in {0} with error: {1}').format(directory_path, e))\n        return\n    session_map = {name: path for name, path in session_map.items() if name}\n    if not choose_session_from_map(\n        boss, opts, session_map, _('Select a session to activate from {0}').format(directory_path)\n    ):\n        boss.show_error(\n            _('No session files found'), _('No session files were found inside {0}').format(directory_path))\n\n\ndef parse_goto_session_cmdline(args: list[str]) -> tuple[GotoSessionOptions, list[str]]:\n    from kitty.cli import cached_parse_cmdline\n    ans = GotoSessionOptions()\n    leftover_args = cached_parse_cmdline(goto_session_options(), args, ans)\n    return ans, leftover_args\n\n\ndef goto_session_options() -> str:\n    return '''\n--sort-by\nchoices=recent,alphabetical\ndefault=recent\nWhen interactively choosing sessions from a list, how to sort the list.\n\n\n--active-only\ntype=bool-set\nOnly consider active sessions.\n'''\n\n\ndef goto_previous_session(boss: BossType, idx: int) -> None:\n    if boss.active_session:\n        nidx = max(0, len(goto_session_history) - 1 + idx)\n        if nidx < len(goto_session_history):\n            switch_to_session(boss, goto_session_history[nidx])\n            return\n    else:\n        if goto_session_history:\n            switch_to_session(boss, goto_session_history[-1])\n            return\n    boss.ring_bell_if_allowed()\n\n\ndef goto_session(boss: BossType, cmdline: Sequence[str]) -> None:\n    if len(cmdline) == 1 and re.match(r'-\\d+', cmdline[0]) is not None:\n        # special case for backwards compat goto_session -1\n        return goto_previous_session(boss, int(cmdline[0]))\n    try:\n        opts, cmdline = parse_goto_session_cmdline(list(cmdline))\n    except Exception as e:\n        boss.show_error(_('Invalid goto_session command'), _(\n            'The command goto_session {0} is invalid with error: {1}').format(shlex.join(cmdline), e))\n        return\n    if not cmdline:\n        choose_session(boss, opts)\n        return\n    path = cmdline[0]\n    if len(cmdline) == 1:  # goto_session -- -1\n        try:\n            idx = int(path)\n        except Exception:\n            idx = 0\n        if idx < 0:\n            return goto_previous_session(boss, idx)\n    resolved_path = resolve_session_arg_path(path)\n    if os.path.isdir(resolved_path):\n        choose_session_in_directory(boss, opts, resolved_path)\n        return\n    path, session_name = resolve_session_path_and_name(resolved_path)\n    if not session_name:\n        boss.show_error(_('Invalid session'), _('{} is not a valid path for a session').format(path))\n        return\n    if switch_to_session(boss, session_name):\n        return\n    try:\n        session_name, created_new_os_window = create_session(boss, path)\n    except Exception:\n        import traceback\n        tb = traceback.format_exc()\n        boss.show_error(_('Failed to create session'), _('Could not create session from {0} with error:\\n{1}').format(path, tb))\n    else:\n        # Ensure newly created session is focused needed when it doesn't create its own OS Windows.\n        if not created_new_os_window:\n            switch_to_session(boss, session_name)\n\n\nsave_as_session_message = '''\\\nSave the current state of kitty as a session file for easy re-use. If the path at which to save the session\nfile is not specified, kitty will prompt you for one. If the path is :code:`.` it will save the session\nto the path of the currently active session, if there is one, otherwise prompt you for a path.\n'''\n\n\ndef save_as_session_options() -> str:\n    return '''\n--save-only\ntype=bool-set\nOnly save the specified session file, dont open it in an editor to review after saving.\n\n\n--use-foreground-process\ntype=bool-set\nWhen saving windows that were started with the default shell but are currently running some\nother process inside that shell, save that process so that when the session is used\nboth the shell **and** the process running inside it are re-started. This is most useful\nwhen you have opened programs like editors or similar inside windows that started out running\nthe shell and you want to preserve that. WARNING: Be careful when using this option, if you are\nrunning some dangerous command like :file:`rm` or :file:`mv` or similar in a shell, it will be re-run when\nthe session is executed if you use this option. Note that this option requires :ref:`shell_integration`\nto work.\n\n\n--relocatable\ntype=bool-set\nWhen saving the working directory for windows, do so as paths relative to the directory in which\nthe session file will be saved. This allows the session file to work even when its containing\ndirectory is moved elsewhere.\n\n\n--match\nIf specified, only save all windows (and their parent tabs/OS Windows) that match the specified\nsearch expression. See :ref:`search_syntax` for details on the search language. In particular if\nyou want to only save windows that are present in the currently active session,\nuse :code:`--match=session:.`.\n\n\n--base-dir\nWhen specified, relative session filenames will be saved to this directory instead of the current\nworking directory. This is useful when kitty is launched from locations where the working directory\nis not your home directory, such as from system-wide shortcuts. Note that :code:`--relocatable` is\ntypically not used with :code:`--base-dir`, since relocatable is meant for session files that are\nco-located with their project directories.\n'''\n\n\ndef save_as_session_part2(boss: BossType, opts: SaveAsSessionOptions, path: str) -> None:\n    if not path:\n        return\n    from .config import atomic_save\n    if opts.base_dir and not os.path.isabs(path):\n        base_dir = os.path.abspath(os.path.expanduser(opts.base_dir))\n        path = os.path.join(base_dir, path)\n    path = os.path.abspath(os.path.expanduser(path))\n    session = '\\n'.join(boss.serialize_state_as_session(path, opts))\n    os.makedirs(os.path.dirname(path), exist_ok=True)\n    atomic_save(session.encode(), path)\n    if not opts.save_only:\n        boss.edit_file(path)\n\n\ndef parse_save_as_options_spec_args(args: list[str]) -> tuple[SaveAsSessionOptions, list[str]]:\n    from kitty.cli import cached_parse_cmdline\n    ans = SaveAsSessionOptions()\n    leftover_args = cached_parse_cmdline(save_as_session_options(), args, ans)\n    return ans, leftover_args\n\n\ndef default_save_as_session_opts() -> SaveAsSessionOptions:\n    return parse_save_as_options_spec_args([])[0]\n\n\ndef save_as_session(boss: BossType, cmdline: Sequence[str]) -> None:\n    opts, args = parse_save_as_options_spec_args(list(cmdline))\n    path = args[0] if args else ''\n    if path == '.':\n        sn = boss.active_session\n        path = seen_session_paths.get(sn) or ''\n    if path:\n        save_as_session_part2(boss, opts, path)\n    else:\n        boss.get_save_filepath(_(\n            'Enter the path at which to save the session, usually session files are given the .kitty-session file extension'),\n                               partial(save_as_session_part2, boss, opts))\n"
  },
  {
    "path": "kitty/shaders.c",
    "content": "/*\n * shaders.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"fonts.h\"\n#include \"gl.h\"\n#include \"cleanup.h\"\n#include \"colors.h\"\n#include <stddef.h>\n#include <string.h>\n#include \"text-cache.h\"\n#include \"window_logo.h\"\n#include \"srgb_gamma.h\"\n#include \"uniforms_generated.h\"\n#include \"state.h\"\n\nenum {\n    CELL_PROGRAM, CELL_FG_PROGRAM, CELL_BG_PROGRAM, CELL_PROGRAM_SENTINEL,\n    BORDERS_PROGRAM,\n    GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM,\n    BGIMAGE_PROGRAM,\n    TINT_PROGRAM,\n    TRAIL_PROGRAM,\n    BLIT_PROGRAM,\n    SCREENSHOT_PROGRAM,\n    ROUNDED_RECT_PROGRAM,\n    NUM_PROGRAMS\n};\nenum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, SPRITE_DECORATIONS_MAP_UNIT };\n\ntypedef struct UIRenderData {\n    unsigned screen_width, screen_height, cell_width, cell_height, screen_left, screen_top, full_framebuffer_width, full_framebuffer_height;\n    Window *window; Screen *screen; OSWindow *os_window;\n    GraphicsRenderData grd;\n    WindowLogoRenderData *window_logo;\n    float bg_alpha, inactive_text_alpha;\n    bool has_background_image;\n    color_type background_color; // RGB only\n} UIRenderData;\n\nstatic inline float\nrow_offset_for_screen(const Screen *screen) {\n    if (!pixel_scroll_enabled(screen) || !screen->cell_size.height) return 0.f;\n    return -1.f + (float)(screen->pixel_scroll_offset_y / (double)screen->cell_size.height);\n}\n\nstatic inline float\nscroll_offset_lines_for_screen(const Screen *screen) {\n    if (!pixel_scroll_enabled(screen) || !screen->cell_size.height) return 0.f;\n    return (float)(screen->pixel_scroll_offset_y / (double)screen->cell_size.height);\n}\n\n// Sprites {{{\ntypedef struct {\n    int xnum, ynum, x, y, z, last_num_of_layers, last_ynum;\n    GLuint texture_id;\n    GLint max_texture_size, max_array_texture_layers;\n    struct decorations_map {\n        GLuint texture_id;\n        unsigned width, height;\n        size_t count;\n    } decorations_map;\n} SpriteMap;\n\nstatic const SpriteMap NEW_SPRITE_MAP = { .xnum = 1, .ynum = 1, .last_num_of_layers = 1, .last_ynum = -1 };\nstatic GLint max_texture_size = 0, max_array_texture_layers = 0;\n\nstatic GLfloat\nsrgb_color(uint8_t color) {\n    return srgb_lut[color];\n}\n\nstatic void\ncolor_vec3(GLint location, color_type color) {\n    glUniform3f(location, srgb_lut[(color >> 16) & 0xFF], srgb_lut[(color >> 8) & 0xFF], srgb_lut[color & 0xFF]);\n}\n\nstatic void\ncolor_vec4_premult(GLint location, color_type color, GLfloat alpha) {\n    glUniform4f(location, srgb_lut[(color >> 16) & 0xFF]*alpha, srgb_lut[(color >> 8) & 0xFF]*alpha, srgb_lut[color & 0xFF]*alpha, alpha);\n}\n\nstatic void\ncolor_vec4(GLint location, color_type color, GLfloat alpha) {\n    glUniform4f(location, srgb_lut[(color >> 16) & 0xFF], srgb_lut[(color >> 8) & 0xFF], srgb_lut[color & 0xFF], alpha);\n}\n\n\n\nstatic void\nclear_current_framebuffer(void) { glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); }\n\nSPRITE_MAP_HANDLE\nalloc_sprite_map(void) {\n    if (!max_texture_size) {\n        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &(max_texture_size));\n        glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &(max_array_texture_layers));\n#ifdef __APPLE__\n        // Since on Apple we could have multiple GPUs, with different capabilities,\n        // upper bound the values according to the data from https://developer.apple.com/graphicsimaging/opengl/capabilities/\n        max_texture_size = MIN(8192, max_texture_size);\n        max_array_texture_layers = MIN(512, max_array_texture_layers);\n#endif\n        sprite_tracker_set_limits(max_texture_size, max_array_texture_layers);\n    }\n    SpriteMap *ans = calloc(1, sizeof(SpriteMap));\n    if (!ans) fatal(\"Out of memory allocating a sprite map\");\n    *ans = NEW_SPRITE_MAP;\n    ans->max_texture_size = max_texture_size;\n    ans->max_array_texture_layers = max_array_texture_layers;\n    return (SPRITE_MAP_HANDLE)ans;\n}\n\nvoid\nfree_sprite_data(FONTS_DATA_HANDLE fg) {\n    SpriteMap *sprite_map = (SpriteMap*)fg->sprite_map;\n    if (sprite_map) {\n        if (sprite_map->texture_id) free_texture(&sprite_map->texture_id);\n        if (sprite_map->decorations_map.texture_id) free_texture(&sprite_map->decorations_map.texture_id);\n        free(sprite_map);\n        fg->sprite_map = NULL;\n    }\n}\n\n\nstatic void\ncopy_32bit_texture(GLuint old_texture, GLuint new_texture, GLenum texture_type) {\n    // requires new texture to be at least as big as old texture. Assumes textures are 32bits per pixel\n    GLint width, height, layers;\n    glBindTexture(texture_type, old_texture);\n    glGetTexLevelParameteriv(texture_type, 0, GL_TEXTURE_WIDTH, &width);\n    glGetTexLevelParameteriv(texture_type, 0, GL_TEXTURE_HEIGHT, &height);\n    glGetTexLevelParameteriv(texture_type, 0, GL_TEXTURE_DEPTH, &layers);\n    if (GLAD_GL_ARB_copy_image) { glCopyImageSubData(old_texture, texture_type, 0, 0, 0, 0, new_texture, texture_type, 0, 0, 0, 0, width, height, layers); return; }\n\n    static bool copy_image_warned = false;\n    // ARB_copy_image not available, do a slow roundtrip copy\n    if (!copy_image_warned) {\n        copy_image_warned = true;\n        log_error(\"WARNING: Your system's OpenGL implementation does not have glCopyImageSubData, falling back to a slower implementation\");\n    }\n\n    GLint internal_format;\n    glGetTexLevelParameteriv(texture_type, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);\n    GLenum format, type;\n    switch(internal_format) {\n        case GL_R8UI: case GL_R8I: case GL_R16UI: case GL_R16I: case GL_R32UI: case GL_R32I: case GL_RG8UI: case GL_RG8I:\n        case GL_RG16UI: case GL_RG16I: case GL_RG32UI: case GL_RG32I: case GL_RGB8UI: case GL_RGB8I: case GL_RGB16UI:\n        case GL_RGB16I: case GL_RGB32UI: case GL_RGB32I: case GL_RGBA8UI: case GL_RGBA8I: case GL_RGBA16UI: case GL_RGBA16I:\n        case GL_RGBA32UI: case GL_RGBA32I:\n            format = GL_RED_INTEGER;\n            type = GL_UNSIGNED_INT;\n            break;\n        default:\n            format = GL_RGBA;\n            type = GL_UNSIGNED_INT_8_8_8_8;\n            break;\n    }\n    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);\n    RAII_ALLOC(uint8_t, pixels, malloc((size_t)width * height * layers * 4u));\n    if (!pixels) fatal(\"Out of memory\");\n    glGetTexImage(texture_type, 0, format, type, pixels);\n    glBindTexture(texture_type, new_texture);\n    glPixelStorei(GL_PACK_ALIGNMENT, 4);\n    if (texture_type == GL_TEXTURE_2D_ARRAY) glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layers, format, type, pixels);\n    else glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, pixels);\n}\n\nstatic GLuint\nsetup_new_sprites_texture(GLenum texture_type) {\n    GLuint tex;\n    glGenTextures(1, &tex);\n    glBindTexture(texture_type, tex);\n    // We use GL_NEAREST otherwise glyphs that touch the edge of the cell\n    // often show a border between cells\n    glTexParameteri(texture_type, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n    glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n    glTexParameteri(texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n    glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n    return tex;\n}\n\nstatic void\nrealloc_sprite_decorations_texture_if_needed(FONTS_DATA_HANDLE fg) {\n#define dm (sm->decorations_map)\n    SpriteMap *sm = (SpriteMap*)fg->sprite_map;\n    size_t current_capacity = (size_t)dm.width * dm.height;\n    if (dm.count < current_capacity && dm.texture_id) return;\n    GLint new_capacity = dm.count + 256;\n    GLint width = new_capacity, height = 1;\n    if (new_capacity > sm->max_texture_size) {\n        width = sm->max_texture_size;\n        height = 1 + new_capacity / width;\n    }\n    if (height > sm->max_texture_size) fatal(\"Max texture size too small for sprite decorations map, maybe switch to using a GL_TEXTURE_2D_ARRAY\");\n    const GLenum texture_type = GL_TEXTURE_2D;\n    GLuint tex = setup_new_sprites_texture(texture_type);\n    glTexImage2D(texture_type, 0, GL_R32UI, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, NULL);\n    if (dm.texture_id) {  // copy data from old texture\n        copy_32bit_texture(dm.texture_id, tex, texture_type);\n        free_texture(&dm.texture_id);\n    }\n    glBindTexture(texture_type, 0);\n    dm.texture_id = tex; dm.width = width; dm.height = height;\n#undef dm\n}\n\nstatic void\nrealloc_sprite_texture(FONTS_DATA_HANDLE fg) {\n    unsigned int xnum, ynum, z, znum, width, height;\n    sprite_tracker_current_layout(fg, &xnum, &ynum, &z);\n    znum = z + 1;\n    SpriteMap *sprite_map = (SpriteMap*)fg->sprite_map;\n    width = xnum * fg->fcm.cell_width; height = ynum * (fg->fcm.cell_height + 1);\n    const GLenum texture_type = GL_TEXTURE_2D_ARRAY;\n    GLuint tex = setup_new_sprites_texture(texture_type);\n    glTexStorage3D(texture_type, 1, GL_SRGB8_ALPHA8, width, height, znum);\n    if (sprite_map->texture_id) { // copy old texture data into new texture\n        copy_32bit_texture(sprite_map->texture_id, tex, texture_type);\n        free_texture(&sprite_map->texture_id);\n    }\n    glBindTexture(texture_type, 0);\n    sprite_map->last_num_of_layers = znum;\n    sprite_map->last_ynum = ynum;\n    sprite_map->texture_id = tex;\n}\n\nstatic void\nensure_sprite_map(FONTS_DATA_HANDLE fg) {\n    SpriteMap *sprite_map = (SpriteMap*)fg->sprite_map;\n    if (!sprite_map->texture_id) realloc_sprite_texture(fg);\n    if (!sprite_map->decorations_map.texture_id) realloc_sprite_decorations_texture_if_needed(fg);\n    // We have to rebind since we don't know if the texture was ever bound\n    // in the context of the current OSWindow\n    glActiveTexture(GL_TEXTURE0 + SPRITE_DECORATIONS_MAP_UNIT);\n    glBindTexture(GL_TEXTURE_2D, sprite_map->decorations_map.texture_id);\n    glActiveTexture(GL_TEXTURE0 + SPRITE_MAP_UNIT);\n    glBindTexture(GL_TEXTURE_2D_ARRAY, sprite_map->texture_id);\n}\n\nvoid\nsend_sprite_to_gpu(FONTS_DATA_HANDLE fg, sprite_index idx, pixel *buf, sprite_index decoration_idx) {\n    SpriteMap *sprite_map = (SpriteMap*)fg->sprite_map;\n    unsigned int xnum, ynum, znum, x, y, z;\n#define dm (sprite_map->decorations_map)\n    if (idx >= dm.count) dm.count = idx + 1;\n    realloc_sprite_decorations_texture_if_needed(fg);\n    div_t d = div(idx, dm.width);\n    x = d.rem; y = d.quot;\n    glActiveTexture(GL_TEXTURE0 + SPRITE_DECORATIONS_MAP_UNIT);\n    glBindTexture(GL_TEXTURE_2D, dm.texture_id);\n    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);\n    glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &decoration_idx);\n#undef dm\n    sprite_tracker_current_layout(fg, &xnum, &ynum, &znum);\n    if ((int)znum >= sprite_map->last_num_of_layers || (znum == 0 && (int)ynum > sprite_map->last_ynum)) {\n        realloc_sprite_texture(fg);\n        sprite_tracker_current_layout(fg, &xnum, &ynum, &znum);\n    }\n    glActiveTexture(GL_TEXTURE0 + SPRITE_MAP_UNIT);\n    glBindTexture(GL_TEXTURE_2D_ARRAY, sprite_map->texture_id);\n    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);\n    sprite_index_to_pos(idx, xnum, ynum, &x, &y, &z);\n    x *= fg->fcm.cell_width; y *= (fg->fcm.cell_height + 1);\n    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, fg->fcm.cell_width, fg->fcm.cell_height + 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, buf);\n}\n\nvoid\nsend_image_to_gpu(GLuint *tex_id, const void* data, GLsizei width, GLsizei height, bool is_opaque, bool is_4byte_aligned, bool linear, RepeatStrategy repeat) {\n    if (!(*tex_id)) { glGenTextures(1, tex_id);  }\n    glBindTexture(GL_TEXTURE_2D, *tex_id);\n    glPixelStorei(GL_UNPACK_ALIGNMENT, is_4byte_aligned ? 4 : 1);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);\n    RepeatStrategy r;\n    switch (repeat) {\n        case REPEAT_MIRROR:\n            r = GL_MIRRORED_REPEAT; break;\n        case REPEAT_CLAMP: {\n            static const GLfloat border_color[4] = {0};\n            glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);\n            r = GL_CLAMP_TO_BORDER;\n            break;\n        }\n        default:\n            r = GL_REPEAT;\n    }\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, r);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, r);\n    glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, width, height, 0, is_opaque ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, data);\n}\n\n// }}}\n\n// Rounded rect {{{\ntypedef struct {\n    Rounded_rectUniforms uniforms;\n} RoundedRectProgramLayout;\nstatic RoundedRectProgramLayout rounded_rect_program_layout;\n\nstatic double\nthickness_as_float(const OSWindow *os_window, unsigned level) {\n    level = MIN(level, arraysz(OPT(box_drawing_scale)));\n    double pts = OPT(box_drawing_scale)[level];\n    double dpi = (os_window->fonts_data->logical_dpi_x + os_window->fonts_data->logical_dpi_y) / 2.0;\n    return pts * dpi / 72.0;\n}\n\n\nstatic void\ndraw_rounded_rect(\n    const OSWindow *os_window, Viewport rect, unsigned framebuffer_height,\n    unsigned thickness_level, unsigned corner_radius_px,\n    color_type srgb_color, color_type srgb_background, float bg_alpha\n) {\n    float thickness = (float)thickness_as_float(os_window, thickness_level);\n    bind_program(ROUNDED_RECT_PROGRAM);\n    color_vec4(rounded_rect_program_layout.uniforms.color, srgb_color, 1.f);\n    color_vec4(rounded_rect_program_layout.uniforms.background_color, srgb_background, bg_alpha);\n    // y co-ord has to be changed to co-ord system with origin at bottom left\n    float y = (float)framebuffer_height - (float)(rect.top + rect.height);\n    glUniform4f(rounded_rect_program_layout.uniforms.rect, rect.left, y, rect.width, rect.height);\n    glUniform2f(rounded_rect_program_layout.uniforms.params, thickness, corner_radius_px);\n    save_viewport_using_top_left_origin(rect.left, rect.top, rect.width, rect.height, framebuffer_height);\n    draw_quad(true, 0);\n    restore_viewport();\n}\n// }}}\n\n// Cell {{{\n\ntypedef struct {\n    UniformBlock render_data;\n    ArrayInformation color_table;\n    CellUniforms uniforms;\n} CellProgramLayout;\nstatic CellProgramLayout cell_program_layouts[NUM_PROGRAMS];\n\ntypedef struct {\n    GraphicsUniforms uniforms;\n} GraphicsProgramLayout;\nstatic GraphicsProgramLayout graphics_program_layouts[NUM_PROGRAMS];\n\ntypedef struct {\n    BgimageUniforms uniforms;\n} BGImageProgramLayout;\nstatic BGImageProgramLayout bgimage_program_layout;\n\ntypedef struct {\n    TintUniforms uniforms;\n} TintProgramLayout;\nstatic TintProgramLayout tint_program_layout;\n\ntypedef struct {\n    TrailUniforms uniforms;\n} TrailProgramLayout;\nstatic TrailProgramLayout trail_program_layout;\n\ntypedef struct {\n    BlitUniforms uniforms;\n} BlitProgramLayout;\nstatic BlitProgramLayout blit_program_layout;\n\ntypedef struct {\n    ScreenshotUniforms uniforms;\n} ScreenshotProgramLayout;\nstatic ScreenshotProgramLayout screenshot_program_layout;\n\nstatic void\ninit_cell_program(void) {\n    for (int i = CELL_PROGRAM; i < CELL_PROGRAM_SENTINEL; i++) {\n        cell_program_layouts[i].render_data.index = block_index(i, \"CellRenderData\");\n        cell_program_layouts[i].render_data.size = block_size(i, cell_program_layouts[i].render_data.index);\n        cell_program_layouts[i].color_table.size = get_uniform_information(i, \"color_table[0]\", GL_UNIFORM_SIZE);\n        cell_program_layouts[i].color_table.offset = get_uniform_information(i, \"color_table[0]\", GL_UNIFORM_OFFSET);\n        cell_program_layouts[i].color_table.stride = get_uniform_information(i, \"color_table[0]\", GL_UNIFORM_ARRAY_STRIDE);\n        get_uniform_locations_cell(i, &cell_program_layouts[i].uniforms);\n        bind_program(i);\n        glUniform1fv(cell_program_layouts[i].uniforms.gamma_lut, arraysz(srgb_lut), srgb_lut);\n    }\n\n    // Sanity check to ensure the attribute location binding worked\n#define C(p, name, expected) { int aloc = attrib_location(p, #name); if (aloc != expected && aloc != -1) fatal(\"The attribute location for %s is %d != %d in program: %d\", #name, aloc, expected, p); }\n    for (int p = CELL_PROGRAM; p < CELL_PROGRAM_SENTINEL; p++) {\n        C(p, colors, 0); C(p, sprite_idx, 1); C(p, is_selected, 2); C(p, decorations_sprite_map, 3);\n    }\n#undef C\n    for (int i = GRAPHICS_PROGRAM; i <= GRAPHICS_ALPHA_MASK_PROGRAM; i++) {\n        get_uniform_locations_graphics(i, &graphics_program_layouts[i].uniforms);\n    }\n    get_uniform_locations_bgimage(BGIMAGE_PROGRAM, &bgimage_program_layout.uniforms);\n    get_uniform_locations_tint(TINT_PROGRAM, &tint_program_layout.uniforms);\n    get_uniform_locations_trail(TRAIL_PROGRAM, &trail_program_layout.uniforms);\n    get_uniform_locations_blit(BLIT_PROGRAM, &blit_program_layout.uniforms);\n    get_uniform_locations_screenshot(SCREENSHOT_PROGRAM, &screenshot_program_layout.uniforms);\n    get_uniform_locations_rounded_rect(ROUNDED_RECT_PROGRAM, &rounded_rect_program_layout.uniforms);\n}\n\n#define CELL_BUFFERS enum { cell_data_buffer, selection_buffer, uniform_buffer };\n\nssize_t\ncreate_cell_vao(void) {\n    ssize_t vao_idx = create_vao();\n#define A(name, size, dtype, offset, stride) \\\n    add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \\\n            /*size=*/size, /*dtype=*/dtype, /*stride=*/stride, /*offset=*/offset, /*divisor=*/1);\n#define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(GPUCell, offset)), sizeof(GPUCell))\n\n    add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);\n    A1(sprite_idx, 2, GL_UNSIGNED_INT, sprite_idx);\n    A1(colors, 3, GL_UNSIGNED_INT, fg);\n\n    add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);\n    A(is_selected, 1, GL_UNSIGNED_BYTE, NULL, 0);\n\n    size_t bufnum = add_buffer_to_vao(vao_idx, GL_UNIFORM_BUFFER);\n    alloc_vao_buffer(vao_idx, cell_program_layouts[CELL_PROGRAM].render_data.size, bufnum, GL_STREAM_DRAW);\n\n    return vao_idx;\n#undef A\n#undef A1\n}\n\nssize_t\ncreate_graphics_vao(void) {\n    ssize_t vao_idx = create_vao();\n    add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);\n    add_attribute_to_vao(GRAPHICS_PROGRAM, vao_idx, \"src\", 4, GL_FLOAT, 0, NULL, 0);\n    return vao_idx;\n}\n\n#define IS_SPECIAL_COLOR(name) (screen->color_profile->overridden.name.type == COLOR_IS_SPECIAL || (screen->color_profile->overridden.name.type == COLOR_NOT_SET && screen->color_profile->configured.name.type == COLOR_IS_SPECIAL))\n\nstatic void\npick_cursor_color(color_type cell_fg, color_type cell_bg, color_type *cursor_fg, color_type *cursor_bg, color_type default_fg, color_type default_bg) {\n    ARGB32 fg, bg, dfg, dbg;\n    fg.rgb = cell_fg; bg.rgb = cell_bg;\n    *cursor_fg = cell_bg; *cursor_bg = cell_fg;\n    double cell_contrast = rgb_contrast(fg, bg);\n    if (cell_contrast < 2.5) {\n        dfg.rgb = default_fg; dbg.rgb = default_bg;\n        if (rgb_contrast(dfg, dbg) > cell_contrast) {\n            *cursor_fg = default_bg; *cursor_bg = default_fg;\n        }\n    }\n}\n\nstatic bool\nhas_bgimage(OSWindow *w) {\n    return w->bgimage && w->bgimage->texture_id > 0;\n}\n\nstatic color_type\ncell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, const CursorRenderInfo *cursor, OSWindow *os_window, float inactive_text_alpha, float bg_alpha) {\n    struct GPUCellRenderData {\n        GLfloat use_cell_bg_for_selection_fg, use_cell_fg_for_selection_color, use_cell_for_selection_bg;\n\n        GLuint default_fg, highlight_fg, highlight_bg, main_cursor_fg, main_cursor_bg, url_color, url_style, inverted, extra_cursor_fg, extra_cursor_bg;\n\n        GLuint columns, lines, sprites_xnum, sprites_ynum, cursor_shape, cell_width, cell_height;\n        GLuint cursor_x1, cursor_x2, cursor_y1, cursor_y2;\n        GLfloat cursor_opacity, inactive_text_alpha, dim_opacity, blink_opacity;\n\n        GLuint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7;\n        GLfloat bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7;\n    };\n    // Send the uniform data\n    ColorProfile *cp = screen->paused_rendering.expires_at ? &screen->paused_rendering.color_profile : screen->color_profile;\n    const bool color_table_needs_upload = cp->dirty || screen->reload_all_gpu_data;\n    const unsigned sz = color_table_needs_upload ? cell_program_layouts[CELL_PROGRAM].render_data.size : cell_program_layouts[CELL_PROGRAM].color_table.offset;\n    struct GPUCellRenderData *rd = (struct GPUCellRenderData*)map_vao_buffer_for_write_only(vao_idx, uniform_buffer, 0, sz);\n    if (color_table_needs_upload) {\n        copy_color_table_to_buffer(cp, (GLuint*)rd, cell_program_layouts[CELL_PROGRAM].color_table.offset / sizeof(GLuint), cell_program_layouts[CELL_PROGRAM].color_table.stride / sizeof(GLuint));\n    }\n#define COLOR(name) colorprofile_to_color(cp, cp->overridden.name, cp->configured.name).rgb\n    rd->default_fg = COLOR(default_fg);\n    rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg);\n    rd->extra_cursor_fg = screen->extra_cursors.color.text.val;\n    rd->extra_cursor_bg = screen->extra_cursors.color.cursor.val;\n    rd->bg_colors0 = COLOR(default_bg);\n    rd->bg_opacities0 = bg_alpha;\n#define SETBG(which) { \\\n    colorprofile_to_transparent_color(cp, which - 1, &rd->bg_colors##which, &rd->bg_opacities##which); }\n    SETBG(1); SETBG(2); SETBG(3); SETBG(4); SETBG(5); SETBG(6); SETBG(7);\n#undef SETBG\n    // selection\n    if (IS_SPECIAL_COLOR(highlight_fg)) {\n        if (IS_SPECIAL_COLOR(highlight_bg)) {\n            rd->use_cell_bg_for_selection_fg = 1.f; rd->use_cell_fg_for_selection_color = 0.f;\n        } else {\n            rd->use_cell_bg_for_selection_fg = 0.f; rd->use_cell_fg_for_selection_color = 1.f;\n        }\n    } else {\n        rd->use_cell_bg_for_selection_fg = 0.f; rd->use_cell_fg_for_selection_color = 0.f;\n    }\n    rd->use_cell_for_selection_bg = IS_SPECIAL_COLOR(highlight_bg) ? 1. : 0.;\n    // Cursor position\n    Line *line_for_cursor = NULL;\n    rd->cursor_opacity = MAX(0, MIN(cursor->cursor_opacity, 1));\n    rd->blink_opacity = MAX(0, MIN(cursor->text_blink_opacity, 1));\n    if (rd->cursor_opacity != 0 && cursor->is_visible) {\n        rd->cursor_x1 = cursor->x, rd->cursor_y1 = cursor->y;\n        rd->cursor_x2 = cursor->x, rd->cursor_y2 = cursor->y;\n        if (pixel_scroll_enabled(screen)) {\n            rd->cursor_y1 += 1;\n            rd->cursor_y2 += 1;\n        }\n        CursorShape cs = (cursor->is_focused || OPT(cursor_shape_unfocused) == NO_CURSOR_SHAPE) ? cursor->shape : OPT(cursor_shape_unfocused);\n        rd->cursor_shape = cs;\n        color_type cell_fg = rd->default_fg, cell_bg = rd->bg_colors0;\n        index_type cell_color_x = cursor->x;\n        bool reversed = false;\n        if (cursor->x < screen->columns && cursor->y < screen->lines) {\n            if (screen->paused_rendering.expires_at) {\n                linebuf_init_line(screen->paused_rendering.linebuf, cursor->y); line_for_cursor = screen->paused_rendering.linebuf->line;\n            } else {\n                linebuf_init_line(screen->linebuf, cursor->y); line_for_cursor = screen->linebuf->line;\n            }\n        }\n        if (line_for_cursor) {\n            colors_for_cell(line_for_cursor, cp, &cell_color_x, &cell_fg, &cell_bg, &reversed);\n            const CPUCell *cursor_cell;\n            const bool large_cursor = ((cursor_cell = &line_for_cursor->cpu_cells[cursor->x])->is_multicell) && cursor_cell->x == 0 && cursor_cell->y == 0;\n            if (large_cursor) {\n                switch(cs) {\n                    case CURSOR_BEAM:\n                        rd->cursor_y2 += cursor_cell->scale - 1; break;\n                    case CURSOR_UNDERLINE:\n                        rd->cursor_y1 += cursor_cell->scale - 1;\n                        rd->cursor_y2 = rd->cursor_y1;\n                        rd->cursor_x2 += mcd_x_limit(cursor_cell) - 1;\n                        break;\n                    case CURSOR_BLOCK:\n                        rd->cursor_y2 += cursor_cell->scale - 1;\n                        rd->cursor_x2 += mcd_x_limit(cursor_cell) - 1;\n                        break;\n                    case CURSOR_HOLLOW: case NUM_OF_CURSOR_SHAPES: case NO_CURSOR_SHAPE: break;\n                };\n            }\n        }\n        // If you change the following algorithm remember to change it in the cell shader for extra cursors too\n        if (IS_SPECIAL_COLOR(cursor_color)) {\n            if (line_for_cursor) pick_cursor_color(cell_fg, cell_bg, &rd->main_cursor_fg, &rd->main_cursor_bg, rd->default_fg, rd->bg_colors0);\n            else { rd->main_cursor_fg = rd->bg_colors0; rd->main_cursor_bg = rd->default_fg; }\n            if (cell_bg == cell_fg) {\n                rd->main_cursor_fg = rd->bg_colors0; rd->main_cursor_bg = rd->default_fg;\n            } else { rd->main_cursor_fg = cell_bg; rd->main_cursor_bg = cell_fg; }\n        } else {\n            rd->main_cursor_bg = COLOR(cursor_color);\n            if (IS_SPECIAL_COLOR(cursor_text_color)) rd->main_cursor_fg = cell_bg;\n            else rd->main_cursor_fg = COLOR(cursor_text_color);\n        }\n        // store last rendered cursor color for trail rendering\n        screen->last_rendered.cursor_bg = rd->main_cursor_bg;\n    } else {\n        rd->cursor_shape = 0;\n        rd->cursor_x1 = screen->columns + 1; rd->cursor_x2 = screen->columns;\n        rd->cursor_y1 = screen->lines + 1; rd->cursor_y2 = screen->lines;\n    }\n\n    rd->columns = screen->columns; rd->lines = screen->lines;\n\n    unsigned int x, y, z;\n    sprite_tracker_current_layout(os_window->fonts_data, &x, &y, &z);\n    rd->sprites_xnum = x; rd->sprites_ynum = y;\n    rd->inverted = screen_invert_colors(screen) ? 1 : 0;\n    rd->cell_width = os_window->fonts_data->fcm.cell_width;\n    rd->cell_height = os_window->fonts_data->fcm.cell_height;\n    rd->inactive_text_alpha = inactive_text_alpha;\n    rd->dim_opacity = OPT(dim_opacity);\n\n#undef COLOR\n    rd->url_color = OPT(url_color); rd->url_style = OPT(url_style);\n    color_type default_bg = rd->bg_colors0;\n    unmap_vao_buffer(vao_idx, uniform_buffer); rd = NULL;\n    return default_bg;\n}\n\nstatic bool\ncell_prepare_to_render(ssize_t vao_idx, Screen *screen, FONTS_DATA_HANDLE fonts_data) {\n    size_t sz;\n    CELL_BUFFERS;\n    void *address;\n    bool changed = false;\n\n    ensure_sprite_map(fonts_data);\n    const Cursor *cursor = screen->paused_rendering.expires_at ? &screen->paused_rendering.cursor : screen->cursor;\n\n    bool cursor_pos_changed = cursor->x != screen->last_rendered.cursor.x \\\n                           || cursor->y != screen->last_rendered.cursor.y;\n    bool disable_ligatures = screen->disable_ligatures == DISABLE_LIGATURES_CURSOR;\n    bool screen_resized = screen->last_rendered.columns != screen->columns || screen->last_rendered.lines != screen->lines;\n\n#define update_cell_data { \\\n        const unsigned int render_lines = render_lines_for_screen(screen); \\\n        sz = sizeof(GPUCell) * render_lines * screen->columns; \\\n        address = alloc_and_map_vao_buffer(vao_idx, sz, cell_data_buffer, true); \\\n        screen_update_cell_data(screen, address, fonts_data, disable_ligatures && cursor_pos_changed); \\\n        unmap_vao_buffer(vao_idx, cell_data_buffer); address = NULL; \\\n        changed = true; \\\n}\n\n    if (screen->paused_rendering.expires_at) {\n        if (!screen->paused_rendering.cell_data_updated) update_cell_data;\n    } else if (screen->reload_all_gpu_data || screen->scroll_changed || screen->is_dirty || screen_resized || (disable_ligatures && cursor_pos_changed)) update_cell_data;\n\n#define update_selection_data { \\\n    const unsigned int render_lines = render_lines_for_screen(screen); \\\n    sz = (size_t)render_lines * screen->columns; \\\n    address = alloc_and_map_vao_buffer(vao_idx, sz, selection_buffer, true); \\\n    screen_apply_selection(screen, address, sz); \\\n    unmap_vao_buffer(vao_idx, selection_buffer); address = NULL; \\\n    changed = true; \\\n}\n\n#define update_graphics_data(grman) \\\n    grman_update_layers(grman, screen->scrolled_by, scroll_offset_lines_for_screen(screen), -1.f, 1.f, 2.f/screen->columns, 2.f/screen->lines, screen->columns, screen->lines, screen->cell_size)\n\n    if (screen->paused_rendering.expires_at) {\n        if (!screen->paused_rendering.cell_data_updated) {\n            update_selection_data; update_graphics_data(screen->paused_rendering.grman);\n        }\n        screen->paused_rendering.cell_data_updated = true;\n        screen->last_rendered.scrolled_by = screen->paused_rendering.scrolled_by;\n    } else {\n        if (screen->reload_all_gpu_data || screen_resized || screen_is_selection_dirty(screen)) update_selection_data;\n        if (update_graphics_data(screen->grman)) changed = true;\n        screen->last_rendered.scrolled_by = screen->scrolled_by;\n    }\n#undef update_selection_data\n#undef update_cell_data\n    screen->last_rendered.columns = screen->columns;\n    screen->last_rendered.lines = screen->lines;\n    screen->last_rendered.cursor = screen->cursor_render_info;\n\n    return changed;\n}\n\nstatic void\ndraw_graphics(int program, ImageRenderData *data, GLuint start, GLuint count, float extra_alpha) {\n    bind_program(program);\n    if (program != GRAPHICS_ALPHA_MASK_PROGRAM) glUniform1f(graphics_program_layouts[program].uniforms.extra_alpha, extra_alpha);\n    glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT);\n    GraphicsUniforms *u = &graphics_program_layouts[program].uniforms;\n    for (GLuint i=0; i < count;) {\n        ImageRenderData *group = data + start + i;\n        glBindTexture(GL_TEXTURE_2D, group->texture_id);\n        if (group->group_count == 0) { i++; continue; }\n        for (GLuint k=0; k < group->group_count; k++, i++) {\n            ImageRenderData *rd = data + start + i;\n            glUniform4f(u->src_rect, rd->src_rect.left, rd->src_rect.top, rd->src_rect.right, rd->src_rect.bottom);\n            glUniform4f(u->dest_rect, rd->dest_rect.left, rd->dest_rect.top, rd->dest_rect.right, rd->dest_rect.bottom);\n            draw_quad(true, 0);\n        }\n    }\n}\n\nstatic ImageRenderData*\nload_alpha_mask_texture(size_t width, size_t height, uint8_t *canvas) {\n    static ImageRenderData data = {.group_count=1};\n    if (!data.texture_id) { glGenTextures(1, &data.texture_id); }\n    glBindTexture(GL_TEXTURE_2D, data.texture_id);\n    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, canvas);\n    return &data;\n}\n\nstatic void\nsetup_texture_as_render_target(unsigned width, unsigned height, GLuint *texture_id, GLuint *framebuffer_id) {\n    glGenTextures(1, texture_id); glGenFramebuffers(1, framebuffer_id);\n    glBindTexture(GL_TEXTURE_2D, *texture_id);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n    // We use GL_RGBA16 to avoid incorrect colors due to quantization loss when\n    // blending, see https://github.com/kovidgoyal/kitty/issues/8953\n    static struct { bool ok; int fmt; } status = { false, GL_RGBA16};\n    glTexImage2D(GL_TEXTURE_2D, 0, status.fmt, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n    bind_framebuffer_for_output(*framebuffer_id);\n    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texture_id, 0);\n    if (!status.ok) {\n        if (check_framebuffer_status() == NULL) {\n            status.ok = true;\n        } else {\n            if (status.fmt == GL_RGBA16) {\n                // Driver does not support 16 bit FBO so let it choose the\n                // format. It will probably end up choosing 8 bit but\n                // inaccurate colors are better than completely broken rendering.\n                // See https://github.com/kovidgoyal/kitty/issues/9068\n                status.fmt = GL_RGBA;\n                free_framebuffer(framebuffer_id);\n                free_texture(texture_id);\n                setup_texture_as_render_target(width, height, texture_id, framebuffer_id);\n                log_error(\"WARNING: Your GPU driver does not support 16bit textures as framebuffer targets, some colors may be slightly inaccurate.\");\n            } else {\n                fatal(\"Your GPU driver does not support indirect rendering to a GL_RGBA texture via a framebuffer\");\n            }\n        }\n    }\n}\n\nstatic void\nset_cell_uniforms(bool force) {\n    static bool constants_set = false;\n    if (!constants_set || force) {\n        float text_contrast = 1.0f + OPT(text_contrast) * 0.01f;\n        float text_gamma_adjustment = OPT(text_gamma_adjustment) < 0.01f ? 1.0f : 1.0f / OPT(text_gamma_adjustment);\n        for (int i = GRAPHICS_PROGRAM; i <= GRAPHICS_ALPHA_MASK_PROGRAM; i++) {\n            bind_program(i); glUniform1i(graphics_program_layouts[i].uniforms.image, GRAPHICS_UNIT);\n        }\n        for (int i = CELL_PROGRAM; i < CELL_PROGRAM_SENTINEL; i++) {\n            bind_program(i); const CellUniforms *cu = &cell_program_layouts[i].uniforms;\n            glUniform1i(cu->sprites, SPRITE_MAP_UNIT);\n            glUniform1i(cu->sprite_decorations_map, SPRITE_DECORATIONS_MAP_UNIT);\n            glUniform1f(cu->text_contrast, text_contrast);\n            glUniform1f(cu->text_gamma_adjustment, text_gamma_adjustment);\n        }\n        bind_program(BLIT_PROGRAM); glUniform1i(blit_program_layout.uniforms.image, GRAPHICS_UNIT);\n        bind_program(SCREENSHOT_PROGRAM); glUniform1i(screenshot_program_layout.uniforms.image, GRAPHICS_UNIT);\n        constants_set = true;\n    }\n}\n\n\n// UI Layer {{{\nstatic Animation *default_visual_bell_animation = NULL;\n\nstatic bool\nhas_visual_bell(Screen *screen) {\n    return screen->start_visual_bell_at > 0;\n\n}\n\nstatic float\nget_visual_bell_intensity(Screen *screen) {\n    if (screen->start_visual_bell_at > 0) {\n        if (!default_visual_bell_animation) {\n            default_visual_bell_animation = alloc_animation();\n            if (!default_visual_bell_animation) fatal(\"Out of memory\");\n            add_cubic_bezier_animation(default_visual_bell_animation, 0, 1, EASE_IN_OUT);\n            add_cubic_bezier_animation(default_visual_bell_animation, 1, 0, EASE_IN_OUT);\n        }\n        const monotonic_t progress = monotonic() - screen->start_visual_bell_at;\n        const monotonic_t duration = OPT(visual_bell_duration) / 2;\n        if (progress <= duration) {\n            Animation *a = animation_is_valid(OPT(animation.visual_bell)) ? OPT(animation.visual_bell) : default_visual_bell_animation;\n            return (float)apply_easing_curve(a, progress / (double)duration, duration);\n        }\n        screen->start_visual_bell_at = 0;\n    }\n    return 0.0f;\n}\n\nstatic void\ndraw_visual_bell_flash(GLfloat intensity, const color_type flash) {\n    bind_program(TINT_PROGRAM);\n    GLfloat attenuation = 0.4f;\n#define C(shift) srgb_color((flash >> shift) & 0xFF)\n    const GLfloat r = C(16), g = C(8), b = C(0);\n    const GLfloat max_channel = r > g ? (r > b ? r : b) : (g > b ? g : b);\n#undef C\n#define C(x) (x * intensity * attenuation)\n    if (max_channel > 0.45) attenuation = 0.6f;  // light color\n    glUniform4f(tint_program_layout.uniforms.tint_color, C(r), C(g), C(b), C(1));\n#undef C\n    glUniform4f(tint_program_layout.uniforms.edges, -1, 1, 1, -1);\n    draw_quad(true, 0);\n}\n\nstatic void\ndraw_visual_bell(const UIRenderData *ui) {\n    if (!has_visual_bell(ui->screen)) return;\n    Screen *screen = ui->screen;\n    float intensity = get_visual_bell_intensity(screen);\n    if (intensity <= 0) return;\n#define COLOR(name, fallback) colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name, screen->color_profile->overridden.fallback, screen->color_profile->configured.fallback)\n    color_type flash = !IS_SPECIAL_COLOR(highlight_bg) ? COLOR(visual_bell_color, highlight_bg) : COLOR(visual_bell_color, default_fg);\n    draw_visual_bell_flash(intensity, flash);\n#undef COLOR\n}\n\nstatic bool\nhas_scrollbar(Window *w, Screen *screen) {\n    if (screen->linebuf != screen->main_linebuf || !screen->historybuf->count) return false;\n    switch (OPT(scrollbar)) {\n        case SCROLLBAR_NEVER: return false;\n        case SCROLLBAR_ALWAYS: return true;\n        case SCROLLBAR_ON_SCROLLED: return screen->scrolled_by > 0;\n        case SCROLLBAR_ON_HOVERED: return w->scrollbar.is_hovering;\n        case SCROLLBAR_ON_SCROLL_AND_HOVER: return screen->scrolled_by > 0 && w->scrollbar.is_hovering;\n    }\n    return false;\n}\n\nstatic unsigned\nrender_a_bar(const UIRenderData *ui, WindowBarData *bar, PyObject *title, bool along_bottom) {\n    unsigned border_width = (unsigned)ceil(thickness_as_float(ui->os_window, 1));\n    unsigned bar_height = ui->cell_height + 2;\n    unsigned bar_width = ui->screen_width - 2 * border_width;\n    if (!bar->buf || bar->width != bar_width || bar->height != bar_height) {\n        free(bar->buf);\n        bar->buf = malloc((size_t)4 * bar_width * bar_height);\n        if (!bar->buf) return 0;\n        bar->height = bar_height;\n        bar->width = bar_width;\n        bar->needs_render = true;\n    }\n#define RGBCOL(which, fallback) ( 0xff000000 | colorprofile_to_color_with_fallback(ui->screen->color_profile, ui->screen->color_profile->overridden.which, ui->screen->color_profile->configured.which, ui->screen->color_profile->overridden.fallback, ui->screen->color_profile->configured.fallback))\n    color_type fg = RGBCOL(default_fg, default_fg), bg = RGBCOL(default_bg, default_bg);\n#undef RGBCOL\n    if (bar->last_drawn_title_object_id != title || bar->needs_render) {\n        static char titlebuf[2048] = {0};\n        if (!title) return 0;\n        snprintf(titlebuf, arraysz(titlebuf), \" %s\", PyUnicode_AsUTF8(title));\n        if (!draw_window_title(ui->os_window->fonts_data->font_sz_in_pts, ui->os_window->fonts_data->logical_dpi_y, titlebuf, fg, bg, bar->buf, bar_width, bar_height)) return 0;\n        Py_CLEAR(bar->last_drawn_title_object_id);\n        bar->last_drawn_title_object_id = Py_NewRef(title);\n    }\n    static ImageRenderData data = {.group_count=1};\n    gpu_data_for_image(&data, -1, 1, 1, -1);\n    glGenTextures(1, &data.texture_id);\n    glBindTexture(GL_TEXTURE_2D, data.texture_id);\n    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n    glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, bar_width, bar_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bar->buf);\n    bind_program(GRAPHICS_PROGRAM);\n    Viewport border_rect = {\n        .height=bar_height + 2 * border_width, .left=ui->screen_left, .width=ui->screen_width, .top=ui->screen_top};\n    if (along_bottom) border_rect.top += ui->screen_height - border_rect.height;\n    const unsigned sh = ui->full_framebuffer_height;\n    // first blank the area to be drawn to background\n    enable_scissor_using_top_left_origin(border_rect, sh);\n    blank_canvas(ui->bg_alpha, bg, false);\n    disable_scissor();\n    // then draw the rendered text\n    save_viewport_using_top_left_origin(\n        border_rect.left + border_width, border_rect.top + border_width, bar_width, bar_height, sh);\n    draw_graphics(GRAPHICS_PROGRAM, &data, 0, 1, 1.f);\n    restore_viewport();\n    free_texture(&data.texture_id);\n    // finally draw border with transparent bg\n    draw_rounded_rect(ui->os_window, border_rect, sh, 1, ui->cell_width, fg, bg, 0.f);\n    return border_rect.height;\n}\n\nstatic bool\nhas_hyperlink_target(OSWindow *os_window, Window *w, Screen *screen) {\n    return OPT(show_hyperlink_targets) && screen->current_hyperlink_under_mouse.id && w && !is_mouse_hidden(os_window) && global_state.mouse_hover_in_window == w->id;\n}\n\nstatic void\ndraw_hyperlink_target(const UIRenderData *ui) {\n    if (!has_hyperlink_target(ui->os_window, ui->window, ui->screen)) return;\n    Screen *screen = ui->screen;\n    const bool along_bottom = screen->current_hyperlink_under_mouse.y < 3;\n    Window *window = ui->window;\n    WindowBarData *bd = &window->url_target_bar_data;\n    if (bd->hyperlink_id_for_title_object != screen->current_hyperlink_under_mouse.id) {\n        bd->hyperlink_id_for_title_object = screen->current_hyperlink_under_mouse.id;\n        Py_CLEAR(bd->last_drawn_title_object_id);\n        const char *url = get_hyperlink_for_id(screen->hyperlink_pool, bd->hyperlink_id_for_title_object, true);\n        if (url == NULL) url = \"\";\n        bd->last_drawn_title_object_id = PyObject_CallMethod(global_state.boss, \"sanitize_url_for_display_to_user\", \"s\", url);\n        if (bd->last_drawn_title_object_id == NULL) { PyErr_Print(); return; }\n        bd->needs_render = true;\n    }\n    if (bd->last_drawn_title_object_id == NULL) return;\n    PyObject *ref = Py_NewRef(bd->last_drawn_title_object_id);  // render_a_bar clears bd->last_drawn_title_object_id\n    render_a_bar(ui, &window->title_bar_data, bd->last_drawn_title_object_id, along_bottom);\n    Py_DECREF(ref);\n}\n\nstatic bool\nhas_window_number(Window *w, Screen *screen) {\n    return w != NULL && screen->display_window_char != 0;\n}\n\nstatic void\ndraw_window_number(const UIRenderData *ui) {\n    if (!has_window_number(ui->window, ui->screen)) return;\n    unsigned title_bar_height = 0, requested_height = ui->screen_height;\n    if (ui->window->title && PyUnicode_Check(ui->window->title) && (requested_height > (ui->cell_height + 1) * 2)) {\n        title_bar_height = render_a_bar(ui, &ui->window->title_bar_data, ui->window->title, false);\n    }\n    unsigned height_for_letter = ui->screen_height - title_bar_height - ui->cell_height;\n    unsigned width_for_letter = ui->screen_width - ui->cell_width;\n    requested_height = MIN(12 * ui->cell_height, MIN(height_for_letter, width_for_letter));\n    if (requested_height < 4) return;\n#define lr ui->screen->last_rendered_window_char\n    if (!lr.canvas || lr.ch != ui->screen->display_window_char || lr.requested_height != requested_height) {\n        free(lr.canvas); lr.canvas = NULL;\n        lr.requested_height = requested_height; lr.height_px = requested_height; lr.ch = 0;\n        lr.canvas = draw_single_ascii_char(ui->screen->display_window_char, &lr.width_px, &lr.height_px);\n        if (lr.height_px < 4 || lr.width_px < 4 || !lr.canvas) return;\n        lr.ch = ui->screen->display_window_char;\n    }\n    unsigned letter_x = 0, letter_y = title_bar_height;\n    if (lr.width_px < ui->screen_width) letter_x = (ui->screen_width - lr.width_px) / 2;\n    if (lr.height_px + title_bar_height < ui->screen_height) letter_y += (ui->screen_height - lr.height_px - title_bar_height) / 2;\n    bind_program(GRAPHICS_ALPHA_MASK_PROGRAM);\n    ImageRenderData *ird = load_alpha_mask_texture(lr.width_px, lr.height_px, lr.canvas);\n    gpu_data_for_image(ird, -1, 1, 1, -1);\n    glUniform1i(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.image, GRAPHICS_UNIT);\n    color_type digit_color = colorprofile_to_color_with_fallback(ui->screen->color_profile, ui->screen->color_profile->overridden.highlight_bg, ui->screen->color_profile->configured.highlight_bg, ui->screen->color_profile->overridden.default_fg, ui->screen->color_profile->configured.default_fg);\n    color_vec3(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.amask_fg, digit_color);\n    glUniform4f(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.amask_bg_premult, 0.f, 0.f, 0.f, 0.f);\n    save_viewport_using_top_left_origin(\n        ui->screen_left + letter_x, ui->screen_top + letter_y, lr.width_px, lr.height_px, ui->full_framebuffer_height);\n    draw_graphics(GRAPHICS_ALPHA_MASK_PROGRAM, ird, 0, 1, 1.f);\n    restore_viewport();\n#undef lr\n}\n\n// Helper function to extract and apply opacity to color components\nstatic void\nset_color_uniform_with_opacity(color_type color, float opacity) {\n    float r = srgb_color((color >> 16) & 0xFF) * opacity;\n    float g = srgb_color((color >> 8) & 0xFF) * opacity;\n    float b = srgb_color(color & 0xFF) * opacity;\n    glUniform4f(tint_program_layout.uniforms.tint_color, r, g, b, opacity);\n}\n\nstatic color_type\nscrollbar_color(Screen *screen, unsigned val) {\n    switch (val & 0xff) {\n#define C(which) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.which, screen->color_profile->configured.which).rgb\n        case 0: return C(default_fg);\n        case 1: return C(highlight_bg);\n#undef C\n        default: return val >> 8;\n    }\n}\n\nstatic void\ndraw_scrollbar(const UIRenderData *ui) {\n    Screen *screen = ui->screen;\n    Window *window = ui->window;\n    if (!window || !screen || !has_scrollbar(window, screen)) return;\n\n    color_type bar_color = scrollbar_color(screen, OPT(scrollbar_handle_color)), track_color = scrollbar_color(screen, OPT(scrollbar_track_color));\n    double cell_frac = (double)screen->pixel_scroll_offset_y / screen->cell_size.height;\n    if (!OPT(pixel_scroll)) cell_frac = 0;\n    float bar_frac = (float)(screen->scrolled_by + cell_frac) / MAX(1u, (float)screen->historybuf->count);\n    float opacity = OPT(scrollbar_handle_opacity);\n    float track_opacity = window->scrollbar.is_hovering ? OPT(scrollbar_track_hover_opacity) : OPT(scrollbar_track_opacity);\n    GLsizei scrollbar_width_px = (GLsizei)(OPT(scrollbar_width) * ui->cell_width);\n    if (window->scrollbar.is_hovering) scrollbar_width_px = (GLsizei)(OPT(scrollbar_hover_width) * ui->cell_width);\n    GLsizei scrollbar_gap_px = (GLsizei)(OPT(scrollbar_gap) * ui->cell_width);\n    unsigned scrollbar_radius = (unsigned)(OPT(scrollbar_radius) * ui->cell_width);\n\n    // Calculate window boundaries including padding\n    GLsizei window_right_edge = ui->screen_left + ui->screen_width + window->render_data.geometry.spaces.right;\n    GLsizei window_top_edge = ui->screen_top - window->render_data.geometry.spaces.top;\n    GLsizei window_height = ui->screen_height + window->render_data.geometry.spaces.top + window->render_data.geometry.spaces.bottom;\n\n    // Position scrollbar on right side with gap\n    GLsizei scrollbar_left = window_right_edge - scrollbar_width_px - scrollbar_gap_px;\n    GLsizei scrollbar_top = window_top_edge + scrollbar_gap_px;\n    GLsizei scrollbar_height = window_height - 2 * scrollbar_gap_px;\n\n    // Calculate thumb size and position\n    float visible_fraction = (float)screen->lines / (float)(screen->lines + screen->historybuf->count);\n    float min_thumb_height_fraction = (OPT(scrollbar_min_handle_height) * ui->cell_height) / (float)window_height;\n    float thumb_height_fraction = MAX(min_thumb_height_fraction, visible_fraction);\n\n    // Convert to OpenGL coordinates (range -1.0 to 1.0, total span = 2.0)\n    const float GL_COORD_SPAN = 2.0f;\n    float thumb_height_gl = thumb_height_fraction * GL_COORD_SPAN;\n    float available_space = GL_COORD_SPAN - thumb_height_gl;\n    float thumb_bottom_gl = -1.0f + available_space * bar_frac;\n    float thumb_top_gl = thumb_bottom_gl + thumb_height_gl;\n\n    // Store thumb position for mouse interaction (normalized window coordinates)\n    float scrollbar_top_in_window = (float)(window_top_edge + scrollbar_gap_px) / (float)ui->full_framebuffer_height;\n    float scrollbar_height_in_window = (float)(window_height - 2 * scrollbar_gap_px) / (float)ui->full_framebuffer_height;\n    float thumb_top_fraction = (1.0f - thumb_top_gl) / 2.0f;\n    float thumb_bottom_fraction = (1.0f - thumb_bottom_gl) / 2.0f;\n    window->scrollbar.thumb_top = scrollbar_top_in_window + thumb_top_fraction * scrollbar_height_in_window;\n    window->scrollbar.thumb_bottom = scrollbar_top_in_window + thumb_bottom_fraction * scrollbar_height_in_window;\n\n    // Set viewport for scrollbar area\n    save_viewport_using_top_left_origin(\n        scrollbar_left, scrollbar_top, scrollbar_width_px, scrollbar_height,\n        ui->full_framebuffer_height\n    );\n\n    // Draw scrollbar track (background)\n    if (track_opacity > 0) {\n        bind_program(TINT_PROGRAM);\n        set_color_uniform_with_opacity(track_color, track_opacity);\n        glUniform4f(tint_program_layout.uniforms.edges, -1.f, 1.f, 1.f, -1.f);\n        draw_quad(true, 0);\n    }\n\n    // Draw scrollbar thumb (handle)\n    if (scrollbar_radius > 0) {\n        // Rounded thumb - use separate viewport and rounded rect program\n        GLsizei thumb_height_px = (GLsizei)(thumb_height_fraction * scrollbar_height);\n        GLsizei thumb_top_px = scrollbar_top + (GLsizei)(thumb_top_fraction * scrollbar_height);\n\n        restore_viewport();\n\n        bind_program(ROUNDED_RECT_PROGRAM);\n        color_vec4(rounded_rect_program_layout.uniforms.color, bar_color, opacity);\n        color_vec4(rounded_rect_program_layout.uniforms.background_color, 0, 0.0f);\n\n        float y = (float)ui->full_framebuffer_height - (float)(thumb_top_px + thumb_height_px);\n        glUniform4f(rounded_rect_program_layout.uniforms.rect,\n                    (float)scrollbar_left, y,\n                    (float)scrollbar_width_px, (float)thumb_height_px);\n\n        float thickness = (float)MAX(scrollbar_width_px, thumb_height_px);\n        glUniform2f(rounded_rect_program_layout.uniforms.params, thickness, (float)scrollbar_radius);\n\n        save_viewport_using_top_left_origin(scrollbar_left, thumb_top_px,\n                                           scrollbar_width_px, thumb_height_px,\n                                           ui->full_framebuffer_height);\n        draw_quad(true, 0);\n        restore_viewport();\n    } else {\n        set_color_uniform_with_opacity(bar_color, opacity);\n        glUniform4f(tint_program_layout.uniforms.edges, -1.f, thumb_top_gl, 1.f, thumb_bottom_gl);\n        draw_quad(true, 0);\n        restore_viewport();\n    }\n}\n\nstatic void\ndraw_window_logo(const UIRenderData *ui) {\n    struct { unsigned width, height; int left, top; } w;\n    WindowLogoRenderData *wl = ui->window_logo;\n    w.height = wl->instance->height; w.width = wl->instance->width;\n    if (OPT(window_logo_scale.width) > 0 || OPT(window_logo_scale.height) > 0) {\n        unsigned scaled_wl_width = ui->screen_width, scaled_wl_height = ui->screen_height;\n\n        // [sx] Scales logo to sx % of the viewports shortest dimension, preserving aspect ratio\n        if (OPT(window_logo_scale.height) < 0) {\n            if (ui->screen_height < ui->screen_width) {\n                scaled_wl_height = (int)(ui->screen_height * OPT(window_logo_scale.width) / 100);\n                scaled_wl_width = wl->instance->width * scaled_wl_height / wl->instance->height;\n            } else {\n                scaled_wl_width = (int)(ui->screen_width * OPT(window_logo_scale.width) / 100);\n                scaled_wl_height = wl->instance->height * scaled_wl_width / wl->instance->width;\n            }\n        }\n        // [0 sy] Scales logo's y dimension to sy % of viewporty keeping original x dimension\n        else if (OPT(window_logo_scale.width) == 0.0) {\n            scaled_wl_height = (int)(scaled_wl_height * OPT(window_logo_scale.height) / 100);\n            scaled_wl_width = wl->instance->width;\n        }\n        // [sx 0] Scales logo's x dimension to sx % of viewportx keeping original y dimension\n        else if (OPT(window_logo_scale.height) == 0.0) {\n            scaled_wl_width = (int)(scaled_wl_width * OPT(window_logo_scale.width) / 100);\n            scaled_wl_height = wl->instance->height;\n        }\n        // [sx sy] Scales logo's x and y dimension to sx and sy % of viewportx and viewporty respectively\n        else {\n            scaled_wl_height = (int)(scaled_wl_height * OPT(window_logo_scale.height) / 100);\n            scaled_wl_width = (int)(scaled_wl_width * OPT(window_logo_scale.width) / 100);\n        }\n        w.width = scaled_wl_width; w.height = scaled_wl_height;\n    }\n    w.left = (int)(ui->screen_width * wl->position.canvas_x - w.width * wl->position.image_x);\n    w.top = (int)(ui->screen_height * wl->position.canvas_y - w.height * wl->position.image_y);\n    float left = gl_pos_x(w.left, ui->screen_width), top = gl_pos_y(w.top, ui->screen_height);\n    ImageRenderData d = {.texture_id = wl->instance->texture_id};\n    gpu_data_for_image(&d, left, top, left + gl_size(w.width, ui->screen_width), top - gl_size(w.height, ui->screen_height));\n    draw_graphics(GRAPHICS_PROGRAM, &d, 0, 1, ui->inactive_text_alpha * OPT(window_logo_alpha));\n}\n\nbool\nscreen_needs_rendering_in_layers(OSWindow *os_window, Window *w, Screen *screen) {\n    const bool has_ui = w && (has_visual_bell(screen) || has_scrollbar(w, screen) || has_hyperlink_target(os_window, w, screen) || has_window_number(w, screen) || w->window_logo.id);\n    GraphicsManager *grman = screen->paused_rendering.expires_at && screen->paused_rendering.grman ? screen->paused_rendering.grman : screen->grman;\n    return has_ui || grman_has_images(grman);\n}\n\nbool\ncurrent_framebuffer_is_ok(void) {\n    return check_framebuffer_status() == NULL;\n}\n\n// }}}\n\nenum { DRAW_NEITHER_BG = 0, DRAW_DEFAULT_BG = 1, DRAW_NON_DEFAULT_BG = 2, DRAW_BOTH_BG = 3};\n\nstatic void\ncall_cell_program(int program, const UIRenderData *ui, ssize_t vao_idx, bool for_final_output, unsigned draw_bg_bitfield) {\n    bind_program(program);\n    CELL_BUFFERS;\n    bind_vao_uniform_buffer(vao_idx, uniform_buffer, cell_program_layouts[program].render_data.index);\n    glUniform1ui(cell_program_layouts[program].uniforms.draw_bg_bitfield, draw_bg_bitfield);\n    glUniform1f(cell_program_layouts[program].uniforms.row_offset, row_offset_for_screen(ui->screen));\n    if (for_final_output) glEnable(GL_FRAMEBUFFER_SRGB);\n    draw_quad(!for_final_output, render_lines_for_screen(ui->screen) * ui->screen->columns);\n    if (for_final_output) glDisable(GL_FRAMEBUFFER_SRGB);\n}\n\nstatic void\ndraw_cells_without_layers(const UIRenderData *ui, ssize_t vao_idx) {\n    call_cell_program(CELL_PROGRAM, ui, vao_idx, true, DRAW_BOTH_BG);\n}\n\nstatic void\ndraw_tint(const UIRenderData *ui) {\n    bind_program(TINT_PROGRAM);\n    color_vec4_premult(tint_program_layout.uniforms.tint_color, ui->background_color, OPT(background_tint));\n    glUniform4f(tint_program_layout.uniforms.edges, -1, 1, 1, -1);\n    draw_quad(true, 0);\n}\n\nstatic void\ndraw_cells_with_layers(const UIRenderData *ui, ssize_t vao_idx) {\n    if (ui->has_background_image && OPT(background_tint) > 0) draw_tint(ui);\n    const bool has_content_between_background_and_foreground = ui->window_logo != NULL || ui->grd.num_of_below_refs > 0 || ui->grd.num_of_negative_refs > 0;\n    if (has_content_between_background_and_foreground) {\n        if (!ui->has_background_image) call_cell_program(CELL_BG_PROGRAM, ui, vao_idx, false, DRAW_DEFAULT_BG);\n        if (ui->window_logo != NULL) draw_window_logo(ui);\n        if (ui->grd.num_of_below_refs > 0) draw_graphics(\n                GRAPHICS_PROGRAM, ui->grd.images, 0, ui->grd.num_of_below_refs, ui->inactive_text_alpha);\n        call_cell_program(CELL_BG_PROGRAM, ui, vao_idx, false, DRAW_NON_DEFAULT_BG);\n        if (ui->grd.num_of_negative_refs) draw_graphics(\n                GRAPHICS_PROGRAM, ui->grd.images, ui->grd.num_of_below_refs, ui->grd.num_of_negative_refs,\n                ui->inactive_text_alpha);\n        call_cell_program(CELL_FG_PROGRAM, ui, vao_idx, false, DRAW_NEITHER_BG);\n    } else call_cell_program(CELL_PROGRAM, ui, vao_idx, false, ui->has_background_image ? DRAW_NON_DEFAULT_BG : DRAW_BOTH_BG);\n\n    if (ui->grd.num_of_positive_refs > 0) draw_graphics(\n            GRAPHICS_PROGRAM, ui->grd.images, ui->grd.num_of_below_refs + ui->grd.num_of_negative_refs,\n            ui->grd.num_of_positive_refs, ui->inactive_text_alpha);\n\n    draw_visual_bell(ui);\n    draw_scrollbar(ui);\n    draw_hyperlink_target(ui);\n    draw_window_number(ui);\n}\n\nvoid\nblank_canvas(float background_opacity, color_type color_in_srgb, bool for_final_output) {\n    if (for_final_output) {\n        // we need to write pre-multiplied sRGB color to framebuffer\n#define C(shift) ((((color_in_srgb >> shift) & 0xFF) / 255.f) * background_opacity)\n        glClearColor(C(16), C(8), C(0), background_opacity);\n#undef C\n    } else {\n        // we need to write pre-multiplied linear color to framebuffer\n#define C(shift) srgb_color((color_in_srgb >> shift) & 0xFF) * background_opacity\n        glClearColor(C(16), C(8), C(0), background_opacity);\n#undef C\n    }\n    glClear(GL_COLOR_BUFFER_BIT);\n}\n\nbool\nsend_cell_data_to_gpu(ssize_t vao_idx, Screen *screen, OSWindow *os_window) {\n    bool changed = false;\n    if (os_window->fonts_data) {\n        if (cell_prepare_to_render(vao_idx, screen, os_window->fonts_data)) changed = true;\n    }\n    return changed;\n}\n\nvoid\ndraw_cells(const WindowRenderData *srd, OSWindow *os_window, bool is_active_window, bool is_tab_bar, bool is_single_window, Window *window) {\n    Screen *screen = srd->screen;\n    CELL_BUFFERS;\n    bind_vertex_array(srd->vao_idx);\n    // We draw with inactive text alpha if:\n    // - We're not drawing the tab bar\n    // - There's only a single window and the os window is not focused\n    // - There are multiple windows and the current window is not active\n    float current_inactive_text_alpha = is_tab_bar || (!is_single_window && is_active_window) || (is_single_window && screen->cursor_render_info.is_focused) ? 1.0f : (float)OPT(inactive_text_alpha);\n    float bg_alpha = effective_os_window_alpha(os_window);\n\n    color_type default_bg = cell_update_uniform_block(\n            srd->vao_idx, screen, uniform_buffer, &screen->cursor_render_info, os_window, current_inactive_text_alpha, bg_alpha);\n    set_cell_uniforms(screen->reload_all_gpu_data);\n    WindowLogoRenderData *wl;\n    if (window && (wl = &window->window_logo) && wl->id && (wl->instance = find_window_logo(global_state.all_window_logos, wl->id)) && wl->instance->load_from_disk_ok) {\n        if (!window->window_logo.instance->texture_id) {\n            set_on_gpu_state(window->window_logo.instance, true);\n        }\n    } else wl = NULL;\n    GraphicsManager *grman = screen->paused_rendering.expires_at && screen->paused_rendering.grman ? screen->paused_rendering.grman : screen->grman;\n    UIRenderData ui = {\n        .screen_width = srd->geometry.right - srd->geometry.left,\n        .screen_height = srd->geometry.bottom - srd->geometry.top,\n        .cell_width = os_window->fonts_data->fcm.cell_width,\n        .cell_height = os_window->fonts_data->fcm.cell_height,\n        .screen_left = srd->geometry.left, .screen_top = srd->geometry.top,\n        .full_framebuffer_width = os_window->viewport_width, .full_framebuffer_height = os_window->viewport_height,\n        .window = window, .screen = screen, .os_window = os_window, .grd = grman_render_data(grman), .window_logo = wl,\n        .inactive_text_alpha = current_inactive_text_alpha, .has_background_image = has_bgimage(os_window),\n        .background_color = default_bg, .bg_alpha=effective_os_window_alpha(os_window),\n    };\n    screen->reload_all_gpu_data = false;\n    save_viewport_using_top_left_origin(\n        ui.screen_left, ui.screen_top, ui.screen_width, ui.screen_height, ui.full_framebuffer_height);\n    if (ui.os_window->needs_layers) draw_cells_with_layers(&ui, srd->vao_idx);\n    else draw_cells_without_layers(&ui, srd->vao_idx);\n    restore_viewport();\n}\n// }}}\n\n// Borders {{{\n\ntypedef struct BorderProgramLayout {\n    BorderUniforms uniforms;\n} BorderProgramLayout;\nstatic BorderProgramLayout border_program_layout;\n\nstatic void\ninit_borders_program(void) {\n    get_uniform_locations_border(BORDERS_PROGRAM, &border_program_layout.uniforms);\n    bind_program(BORDERS_PROGRAM);\n    glUniform1fv(border_program_layout.uniforms.gamma_lut, 256, srgb_lut);\n}\n\nssize_t\ncreate_border_vao(void) {\n    ssize_t vao_idx = create_vao();\n\n    add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);\n    add_attribute_to_vao(BORDERS_PROGRAM, vao_idx, \"rect\",\n            /*size=*/4, /*dtype=*/GL_FLOAT, /*stride=*/sizeof(BorderRect), /*offset=*/(void*)offsetof(BorderRect, left), /*divisor=*/1);\n    add_attribute_to_vao(BORDERS_PROGRAM, vao_idx, \"rect_color\",\n            /*size=*/1, /*dtype=*/GL_UNSIGNED_INT, /*stride=*/sizeof(BorderRect), /*offset=*/(void*)(offsetof(BorderRect, color)), /*divisor=*/1);\n\n    return vao_idx;\n}\n\nvoid\ndraw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg, OSWindow *w) {\n    float background_opacity = effective_os_window_alpha(w);\n    if (!num_border_rects) return;\n    bind_program(BORDERS_PROGRAM); bind_vertex_array(vao_idx);\n    const bool has_background_image = has_bgimage(w);\n    if (has_background_image) background_opacity = OPT(background_tint) * OPT(background_tint_gaps);\n    if (rect_data_is_dirty) {\n        const size_t sz = sizeof(BorderRect) * num_border_rects;\n        void *borders_buf_address = alloc_and_map_vao_buffer(vao_idx, sz, 0, false);\n        if (borders_buf_address) memcpy(borders_buf_address, rect_buf, sz);\n        unmap_vao_buffer(vao_idx, 0);\n    }\n    color_type default_bg = (num_visible_windows > 1 && !all_windows_have_same_bg) ? OPT(background) : active_window_bg;\n    GLuint colors[9] = {\n        default_bg, OPT(active_border_color), OPT(inactive_border_color), 0,\n        OPT(bell_border_color), OPT(tab_bar_background), OPT(tab_bar_margin_color),\n        w->tab_bar_edge_color.left, w->tab_bar_edge_color.right\n    };\n    glUniform1uiv(border_program_layout.uniforms.colors, arraysz(colors), colors);\n    glUniform1f(border_program_layout.uniforms.background_opacity, background_opacity);\n    if (!w->needs_layers) glEnable(GL_FRAMEBUFFER_SRGB);\n    draw_quad(has_background_image, num_border_rects);\n    if (!w->needs_layers) glDisable(GL_FRAMEBUFFER_SRGB);\n    unbind_program(); unbind_vertex_array();\n}\n\n// }}}\n\n// Cursor Trail {{{\nvoid\ndraw_cursor_trail(CursorTrail *trail, Window *active_window) {\n    bind_program(TRAIL_PROGRAM);\n\n    glUniform4fv(trail_program_layout.uniforms.x_coords, 1, trail->corner_x);\n    glUniform4fv(trail_program_layout.uniforms.y_coords, 1, trail->corner_y);\n\n    glUniform2fv(trail_program_layout.uniforms.cursor_edge_x, 1, trail->cursor_edge_x);\n    glUniform2fv(trail_program_layout.uniforms.cursor_edge_y, 1, trail->cursor_edge_y);\n\n    color_type trail_color = OPT(cursor_trail_color);\n    if (trail_color == 0) {  // 0 means \"none\" was specified\n        trail_color = active_window ? active_window->render_data.screen->last_rendered.cursor_bg : OPT(foreground);\n    }\n    color_vec3(trail_program_layout.uniforms.trail_color, trail_color);\n\n    glUniform1f(trail_program_layout.uniforms.trail_opacity, trail->opacity);\n\n    draw_quad(true, 0);\n    unbind_program();\n}\n\n// }}}\n\n// OSWindow {{{\nstatic void\ndraw_bg_image(OSWindow *os_window, Tab *tab) {\n    if (!has_bgimage(os_window)) return;\n    BackgroundImageRenderSettings s = {\n        .os_window.width = os_window->viewport_width, .os_window.height = os_window->viewport_height,\n        .instance_id = os_window->bgimage->id, .layout=OPT(background_image_layout),\n        .linear=OPT(background_image_linear), .bgcolor=OPT(background), .opacity=effective_os_window_alpha(os_window),\n    };\n    GLfloat iwidth = os_window->bgimage->width, iheight = os_window->bgimage->height;\n    GLfloat vwidth = s.os_window.width, vheight = s.os_window.height;\n    if (CENTER_SCALED == OPT(background_image_layout)) {\n        GLfloat ifrac = iwidth / iheight;\n        if (ifrac > (vwidth / vheight)) {\n            iheight = vheight;\n            iwidth = iheight * ifrac;\n        } else {\n            iwidth = vwidth;\n            iheight = iwidth / ifrac;\n        }\n    }\n    GLfloat tiled = 0.f;;\n    GLfloat left = -1.0, top = 1.0, right = 1.0, bottom = -1.0;\n    switch (OPT(background_image_layout)) {\n        case TILING: case MIRRORED: case CLAMPED:\n            tiled = 1.f; break;\n        case SCALED:\n            break;\n        case CENTER_CLAMPED:\n        case CENTER_SCALED: {\n            GLfloat wfrac = (vwidth - iwidth) / vwidth;\n            GLfloat hfrac = (vheight - iheight) / vheight;\n            left += wfrac;\n            right -= wfrac;\n            top -= hfrac;\n            bottom += hfrac;\n        } break;\n    }\n    bind_program(BGIMAGE_PROGRAM);\n    // altough we dont use this VO we need to ensure *some* VAO is bound at this point.\n    bind_vertex_array(tab->border_rects.vao_idx);\n    glUniform4f(bgimage_program_layout.uniforms.sizes, vwidth, vheight, iwidth, iheight);\n    glUniform1f(bgimage_program_layout.uniforms.tiled, tiled);\n    glUniform4f(bgimage_program_layout.uniforms.positions, left, top, right, bottom);\n    glUniform1i(bgimage_program_layout.uniforms.image, GRAPHICS_UNIT);\n    color_vec4(bgimage_program_layout.uniforms.background, s.bgcolor, s.opacity);\n    glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT);\n    glBindTexture(GL_TEXTURE_2D, os_window->bgimage->texture_id);\n    draw_quad(false, 0);\n    unbind_program();\n}\n\nstatic void\ngpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height) {\n    float width_frac = 2 * MIN(1, width / (float)screen_width_px), height_frac = 2 * MIN(1, height / (float)screen_height_px);\n    float hmargin = (2 - width_frac) / 2;\n    float vmargin = (2 - height_frac) / 2;\n    gpu_data_for_image(ans, -1 + hmargin, 1 - vmargin, -1 + hmargin + width_frac, 1 - vmargin - height_frac);\n}\n\nstatic void\ndraw_centered_alpha_mask(size_t screen_width, size_t screen_height, size_t width, size_t height, uint8_t *canvas, float background_opacity) {\n    ImageRenderData *data = load_alpha_mask_texture(width, height, canvas);\n    gpu_data_for_centered_image(data, screen_width, screen_height, width, height);\n    bind_program(GRAPHICS_ALPHA_MASK_PROGRAM);\n    glUniform1i(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.image, GRAPHICS_UNIT);\n    color_vec3(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.amask_fg, OPT(foreground));\n    color_vec4_premult(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.amask_bg_premult, OPT(background), background_opacity);\n    draw_graphics(GRAPHICS_ALPHA_MASK_PROGRAM, data, 0, 1, 1.0);\n}\n\nstatic void\ndraw_resizing_text(OSWindow *w) {\n    if (monotonic() - w->created_at > ms_to_monotonic_t(1000) && w->live_resize.num_of_resize_events > 1) {\n        char text[32] = {0};\n        unsigned int width = w->live_resize.width, height = w->live_resize.height;\n        snprintf(text, sizeof(text), \"%u by %u cells\", width / w->fonts_data->fcm.cell_width, height / w->fonts_data->fcm.cell_height);\n        StringCanvas rendered = render_simple_text(w->fonts_data, text);\n        if (rendered.canvas) {\n            draw_centered_alpha_mask(width, height, rendered.width, rendered.height, rendered.canvas, MAX(0.2f, MIN(OPT(background_opacity), 0.8f)));\n            free(rendered.canvas);\n        }\n    }\n}\n\nvoid\nblank_os_window(OSWindow *osw) {\n    color_type color = OPT(background);\n    if (osw->num_tabs > 0) {\n        Tab *t = osw->tabs + osw->active_tab;\n        if (t->num_windows == 1) {\n            Window *w = t->windows + t->active_window;\n            Screen *s = w->render_data.screen;\n            if (s) {\n                color = colorprofile_to_color(s->color_profile, s->color_profile->overridden.default_bg, s->color_profile->configured.default_bg).rgb;\n            }\n        }\n    }\n    blank_canvas(effective_os_window_alpha(osw), color, true);\n}\n\nstatic void\nstart_os_window_rendering(OSWindow *os_window, Tab *tab) {\n    // note that during live resize rendering is done in layers\n    if (os_window->live_resize.in_progress) blank_os_window(os_window);\n    if (os_window->needs_layers) {\n        // Ensure the global shared texture is large enough for this window\n        if (global_state.layers_render_texture.width < os_window->viewport_width ||\n                global_state.layers_render_texture.height < os_window->viewport_height) {\n            if (global_state.layers_render_texture.texture_id) free_texture(&global_state.layers_render_texture.texture_id);\n            if (global_state.layers_render_texture.framebuffer_id) free_framebuffer(&global_state.layers_render_texture.framebuffer_id);\n            unsigned new_w = (unsigned)MAX(global_state.layers_render_texture.width, os_window->viewport_width);\n            unsigned new_h = (unsigned)MAX(global_state.layers_render_texture.height, os_window->viewport_height);\n            setup_texture_as_render_target(new_w, new_h, &global_state.layers_render_texture.texture_id, &global_state.layers_render_texture.framebuffer_id);\n            global_state.layers_render_texture.width = (int)new_w;\n            global_state.layers_render_texture.height = (int)new_h;\n            global_state.layers_render_texture.texture_generation++;\n        }\n        // Create per-window framebuffer if needed and attach the global texture to it\n        if (!os_window->indirect_output.framebuffer_id) glGenFramebuffers(1, &os_window->indirect_output.framebuffer_id);\n        if (os_window->indirect_output.attached_texture_generation != global_state.layers_render_texture.texture_generation) {\n            bind_framebuffer_for_output(os_window->indirect_output.framebuffer_id);\n            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, global_state.layers_render_texture.texture_id, 0);\n            os_window->indirect_output.attached_texture_generation = global_state.layers_render_texture.texture_generation;\n        }\n        set_framebuffer_to_use_for_output(os_window->indirect_output.framebuffer_id);\n        bind_framebuffer_for_output(0);\n        save_viewport_using_bottom_left_origin(0, 0, os_window->viewport_width, os_window->viewport_height);\n        clear_current_framebuffer();\n        draw_bg_image(os_window, tab);\n    }\n}\n\nstatic void\nstop_os_window_rendering(OSWindow *os_window, Tab *tab, Window *active_window) {\n    if (OPT(cursor_trail) && tab->cursor_trail.needs_render) draw_cursor_trail(&tab->cursor_trail, active_window);\n    if (os_window->needs_layers) {\n        set_framebuffer_to_use_for_output(0);\n        bind_framebuffer_for_output(0);\n        bind_program(BLIT_PROGRAM);\n        glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT);\n        glBindTexture(GL_TEXTURE_2D, global_state.layers_render_texture.texture_id);\n        float sx = global_state.layers_render_texture.width > 0 ? (float)os_window->viewport_width / (float)global_state.layers_render_texture.width : 1.f;\n        float sy = global_state.layers_render_texture.height > 0 ? (float)os_window->viewport_height / (float)global_state.layers_render_texture.height : 1.f;\n        glUniform4f(blit_program_layout.uniforms.src_rect, 0, sy, sx, 0);\n        glUniform4f(blit_program_layout.uniforms.dest_rect, -1, 1, 1, -1);\n        restore_viewport();\n        if (os_window->live_resize.in_progress) save_viewport_using_top_left_origin(\n                0, 0, os_window->viewport_width, os_window->viewport_height, os_window->live_resize.height);\n        draw_quad(false, 0);\n        if (os_window->live_resize.in_progress) {\n            restore_viewport();\n            draw_resizing_text(os_window);\n        }\n    }\n}\n\nvoid\nsetup_os_window_for_rendering(OSWindow *os_window, Tab *tab, Window *active_window, bool start) {\n    if (start) start_os_window_rendering(os_window, tab);\n    else stop_os_window_rendering(os_window, tab, active_window);\n}\n\n// Take a screenshot of the OS Window, must be called immediately after\n// the OSWindow is rendered into the back buffer and before the buffers\n// are swapped. If thumb_w or thumb_h are zero the are set to the corresponding\n// dimension of the source region (viewport or central region without tab bar).\n// Takes a screenshot of a rectangular region of the OSWindow's framebuffer.\n// The region parameter specifies which part of the framebuffer to capture.\n// Scaling is performed on the GPU using the SCREENSHOT_PROGRAM shader for better performance.\n// The shader properly handles sRGB color space conversion and downscaling.\n// Setting the thumbnail dimensions to zero disables scaling.\nvoid\ntake_screenshot_of_rectangular_region(OSWindow *os_window, Region region, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h) {\n    unsigned vw = os_window->viewport_width;\n    unsigned vh = os_window->viewport_height;\n\n    // Calculate the source region dimensions\n    unsigned src_height = region.bottom - region.top;\n    unsigned src_width = region.right - region.left;\n\n    if (!*thumb_w) *thumb_w = src_width;\n    if (!*thumb_h) *thumb_h = src_height;\n    *thumb_w = MIN(src_width, *thumb_w);\n    *thumb_h = MIN(src_height, *thumb_h);\n\n    // Create a texture to hold the current framebuffer content\n    GLuint src_texture = 0;\n    glGenTextures(1, &src_texture);\n    glBindTexture(GL_TEXTURE_2D, src_texture);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\n    // Copy the current framebuffer to the texture\n    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, vw, vh, 0);\n\n    // Create a temporary framebuffer for GPU-based scaling\n    GLuint temp_texture = 0, temp_framebuffer = 0;\n    setup_texture_as_render_target(*thumb_w, *thumb_h, &temp_texture, &temp_framebuffer);\n\n    // Save current state\n    GLint current_framebuffer;\n    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_framebuffer);\n\n    // Bind our temporary framebuffer for rendering\n    bind_framebuffer_for_output(temp_framebuffer);\n    save_viewport_using_bottom_left_origin(0, 0, *thumb_w, *thumb_h);\n\n    // Use the screenshot program to render the scaled framebuffer with proper color space handling\n    bind_program(SCREENSHOT_PROGRAM);\n\n    // Set source rectangle (normalized coordinates: 0 to 1)\n    // Note: OpenGL texture origin is bottom-left, but Region uses top-left origin\n    // Convert from screen coordinates (top-left origin) to OpenGL texture coordinates (bottom-left origin)\n    float src_left_norm = (float)region.left / (float)vw;\n    float src_right_norm = (float)region.right / (float)vw;\n    float src_bottom_norm = (float)(vh - region.bottom) / (float)vh;\n    float src_top_norm = (float)(vh - region.top) / (float)vh;\n    glUniform4f(screenshot_program_layout.uniforms.src_rect, src_left_norm, src_top_norm, src_right_norm, src_bottom_norm);\n\n    // Set destination rectangle (NDC coordinates: -1 to 1)\n    glUniform4f(screenshot_program_layout.uniforms.dest_rect, -1.0f, -1.0f, 1.0f, 1.0f);\n\n    // Set the source texture size for proper downscaling\n    glUniform2f(screenshot_program_layout.uniforms.src_size, (float)vw, (float)vh);\n\n    // Bind the source texture\n    glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT);\n    glBindTexture(GL_TEXTURE_2D, src_texture);\n\n    // Draw the scaled quad\n    draw_quad(false, 0);\n\n    // Read the scaled result\n    glPixelStorei(GL_PACK_ALIGNMENT, 1);\n    glReadPixels(0, 0, *thumb_w, *thumb_h, GL_RGBA, GL_UNSIGNED_BYTE, dst_buf);\n    glPixelStorei(GL_PACK_ALIGNMENT, 4);\n\n    // Restore previous state\n    bind_framebuffer_for_output(current_framebuffer);\n    restore_viewport();\n\n    // Clean up temporary resources\n    free_texture(&src_texture);\n    free_texture(&temp_texture);\n    free_framebuffer(&temp_framebuffer);\n}\n// }}}\n\n// Python API {{{\n\nstatic PyObject*\npygpu_driver_version_string(PyObject *self UNUSED, PyObject *args UNUSED) {\n    return PyUnicode_FromString(global_state.gl_version ? gl_version_string() : \"\");\n}\n\nstatic bool\nattach_shaders(PyObject *sources, GLuint program_id, GLenum shader_type) {\n    RAII_ALLOC(const GLchar*, c_sources, calloc(PyTuple_GET_SIZE(sources), sizeof(GLchar*)));\n    for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(sources); i++) {\n        PyObject *temp = PyTuple_GET_ITEM(sources, i);\n        if (!PyUnicode_Check(temp)) { PyErr_SetString(PyExc_TypeError, \"shaders must be strings\"); return false; }\n        c_sources[i] = PyUnicode_AsUTF8(temp);\n    }\n    GLuint shader_id = compile_shaders(shader_type, PyTuple_GET_SIZE(sources), c_sources);\n    if (shader_id == 0) return false;\n    glAttachShader(program_id, shader_id);\n    glDeleteShader(shader_id);\n    return true;\n}\n\nstatic PyObject*\ncompile_program(PyObject UNUSED *self, PyObject *args) {\n    PyObject *vertex_shaders, *fragment_shaders;\n    int which, allow_recompile = 0;\n    if (!PyArg_ParseTuple(args, \"iO!O!|p\", &which, &PyTuple_Type, &vertex_shaders, &PyTuple_Type, &fragment_shaders, &allow_recompile)) return NULL;\n    if (which < 0 || which >= NUM_PROGRAMS) { PyErr_Format(PyExc_ValueError, \"Unknown program: %d\", which); return NULL; }\n    Program *program = program_ptr(which);\n    if (program->id != 0) {\n        if (allow_recompile) { glDeleteProgram(program->id); program->id = 0; }\n        else { PyErr_SetString(PyExc_ValueError, \"program already compiled\"); return NULL; }\n    }\n#define fail_compile() { glDeleteProgram(program->id); return NULL; }\n    program->id = glCreateProgram();\n    if (!attach_shaders(vertex_shaders, program->id, GL_VERTEX_SHADER)) fail_compile();\n    if (!attach_shaders(fragment_shaders, program->id, GL_FRAGMENT_SHADER)) fail_compile();\n    glLinkProgram(program->id);\n    GLint ret = GL_FALSE;\n    glGetProgramiv(program->id, GL_LINK_STATUS, &ret);\n    if (ret != GL_TRUE) {\n        GLsizei len;\n        static char glbuf[4096];\n        glGetProgramInfoLog(program->id, sizeof(glbuf), &len, glbuf);\n        PyErr_Format(PyExc_ValueError, \"Failed to link GLSL shaders:\\n%s\", glbuf);\n        fail_compile();\n    }\n#undef fail_compile\n    init_uniforms(which);\n    return Py_BuildValue(\"I\", program->id);\n}\n\n#define PYWRAP0(name) static PyObject* py##name(PYNOARG)\n#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)\n#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;\n#define ONE_INT(name) PYWRAP1(name) { name(PyLong_AsSsize_t(args)); Py_RETURN_NONE; }\n#define TWO_INT(name) PYWRAP1(name) { int a, b; PA(\"ii\", &a, &b); name(a, b); Py_RETURN_NONE; }\n#define NO_ARG(name) PYWRAP0(name) { name(); Py_RETURN_NONE; }\n#define NO_ARG_INT(name) PYWRAP0(name) { return PyLong_FromSsize_t(name()); }\n\nONE_INT(bind_program)\nNO_ARG(unbind_program)\n\nPYWRAP0(create_vao) {\n    int ans = create_vao();\n    if (ans < 0) return NULL;\n    return Py_BuildValue(\"i\", ans);\n}\n\nONE_INT(bind_vertex_array)\nNO_ARG(unbind_vertex_array)\nTWO_INT(unmap_vao_buffer)\n\nNO_ARG(init_borders_program)\n\nNO_ARG(init_cell_program)\n\nstatic PyObject*\nsprite_map_set_limits(PyObject UNUSED *self, PyObject *args) {\n    unsigned int w, h;\n    if(!PyArg_ParseTuple(args, \"II\", &w, &h)) return NULL;\n    sprite_tracker_set_limits(w, h);\n    max_texture_size = w; max_array_texture_layers = h;\n    Py_RETURN_NONE;\n}\n\n#define M(name, arg_type) {#name, (PyCFunction)name, arg_type, NULL}\n#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}\nstatic PyMethodDef module_methods[] = {\n    M(compile_program, METH_VARARGS),\n    M(sprite_map_set_limits, METH_VARARGS),\n    MW(create_vao, METH_NOARGS),\n    MW(gpu_driver_version_string, METH_NOARGS),\n    MW(bind_vertex_array, METH_O),\n    MW(unbind_vertex_array, METH_NOARGS),\n    MW(unmap_vao_buffer, METH_VARARGS),\n    MW(bind_program, METH_O),\n    MW(unbind_program, METH_NOARGS),\n    MW(init_borders_program, METH_NOARGS),\n    MW(init_cell_program, METH_NOARGS),\n\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nstatic void\nfinalize(void) {\n    default_visual_bell_animation = free_animation(default_visual_bell_animation);\n}\n\nbool\ninit_shaders(PyObject *module) {\n#define C(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return false; }\n    C(CELL_PROGRAM); C(CELL_FG_PROGRAM); C(CELL_BG_PROGRAM); C(BORDERS_PROGRAM);\n    C(GRAPHICS_PROGRAM); C(GRAPHICS_PREMULT_PROGRAM); C(GRAPHICS_ALPHA_MASK_PROGRAM);\n    C(BGIMAGE_PROGRAM); C(TINT_PROGRAM); C(TRAIL_PROGRAM); C(BLIT_PROGRAM); C(SCREENSHOT_PROGRAM); C(ROUNDED_RECT_PROGRAM);\n    C(GLSL_VERSION);\n    C(GL_VERSION);\n    C(GL_VENDOR);\n    C(GL_SHADING_LANGUAGE_VERSION);\n    C(GL_RENDERER);\n    C(GL_TRIANGLE_FAN); C(GL_TRIANGLE_STRIP); C(GL_TRIANGLES); C(GL_LINE_LOOP);\n    C(GL_COLOR_BUFFER_BIT);\n    C(GL_VERTEX_SHADER);\n    C(GL_FRAGMENT_SHADER);\n    C(GL_TRUE);\n    C(GL_FALSE);\n    C(GL_COMPILE_STATUS);\n    C(GL_LINK_STATUS);\n    C(GL_MAX_ARRAY_TEXTURE_LAYERS); C(GL_TEXTURE_BINDING_BUFFER); C(GL_MAX_TEXTURE_BUFFER_SIZE);\n    C(GL_MAX_TEXTURE_SIZE);\n    C(GL_TEXTURE_2D_ARRAY);\n    C(GL_LINEAR); C(GL_CLAMP_TO_EDGE); C(GL_NEAREST);\n    C(GL_TEXTURE_MIN_FILTER); C(GL_TEXTURE_MAG_FILTER);\n    C(GL_TEXTURE_WRAP_S); C(GL_TEXTURE_WRAP_T);\n    C(GL_UNPACK_ALIGNMENT);\n    C(GL_R8); C(GL_RED); C(GL_UNSIGNED_BYTE); C(GL_UNSIGNED_SHORT); C(GL_R32UI); C(GL_RGB32UI); C(GL_RGBA);\n    C(GL_TEXTURE_BUFFER); C(GL_STATIC_DRAW); C(GL_STREAM_DRAW); C(GL_DYNAMIC_DRAW);\n    C(GL_SRC_ALPHA); C(GL_ONE_MINUS_SRC_ALPHA);\n    C(GL_WRITE_ONLY); C(GL_READ_ONLY); C(GL_READ_WRITE);\n    C(GL_BLEND); C(GL_FLOAT); C(GL_UNSIGNED_INT); C(GL_ARRAY_BUFFER); C(GL_UNIFORM_BUFFER);\n\n#undef C\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    register_at_exit_cleanup_func(SHADERS_CLEANUP_FUNC, finalize);\n    return true;\n}\n// }}}\n"
  },
  {
    "path": "kitty/shaders.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport re\nfrom collections.abc import Callable, Iterator\nfrom functools import lru_cache, partial\nfrom itertools import count\nfrom typing import Any, Literal, NamedTuple, Optional\n\nfrom .constants import read_kitty_resource\nfrom .fast_data_types import (\n    BGIMAGE_PROGRAM,\n    BLINK,\n    BLIT_PROGRAM,\n    CELL_BG_PROGRAM,\n    CELL_FG_PROGRAM,\n    CELL_PROGRAM,\n    COLOR_IS_INDEX,\n    COLOR_IS_RGB,\n    COLOR_IS_SPECIAL,\n    COLOR_NOT_SET,\n    DECORATION,\n    DECORATION_MASK,\n    DIM,\n    GLSL_VERSION,\n    GRAPHICS_ALPHA_MASK_PROGRAM,\n    GRAPHICS_PREMULT_PROGRAM,\n    GRAPHICS_PROGRAM,\n    MARK,\n    MARK_MASK,\n    REVERSE,\n    ROUNDED_RECT_PROGRAM,\n    SCREENSHOT_PROGRAM,\n    STRIKETHROUGH,\n    TINT_PROGRAM,\n    TRAIL_PROGRAM,\n    compile_program,\n    get_options,\n    init_cell_program,\n)\n\n\ndef identity(x: str) -> str:\n    return x\n\n\nclass CompileError(ValueError):\n    pass\n\n\nclass Program:\n\n    include_pat: Optional['re.Pattern[str]'] = None\n    filename_number_base: int = 7893000\n\n    def __init__(self, name: str, vertex_name: str = '', fragment_name: str = '') -> None:\n        self.name = name\n        self.filename_number_counter = count(self.filename_number_base + 1)\n        self.filename_map: dict[str, int] = {}\n        if Program.include_pat is None:\n            Program.include_pat = re.compile(r'^#pragma\\s+kitty_include_shader\\s+<(.+?)>', re.MULTILINE)\n        self.vertex_name = vertex_name or f'{name}_vertex.glsl'\n        self.fragment_name = fragment_name or f'{name}_fragment.glsl'\n        self.original_vertex_sources = tuple(self._load_sources(self.vertex_name, set()))\n        self.original_fragment_sources = tuple(self._load_sources(self.fragment_name, set()))\n        self.vertex_sources = self.original_vertex_sources\n        self.fragment_sources = self.original_fragment_sources\n\n    def _load_sources(self, name: str, seen: set[str], level: int = 0) -> Iterator[str]:\n        if level == 0:\n            yield f'#version {GLSL_VERSION}\\n'\n        if name in seen:\n            return\n        seen.add(name)\n        self.filename_map[name] = fnum = next(self.filename_number_counter)\n        src = read_kitty_resource(name).decode('utf-8')\n        pos = 0\n        lnum = 0\n        assert Program.include_pat is not None\n        for m in Program.include_pat.finditer(src):\n            prefix = src[pos:m.start()]\n            if prefix:\n                yield f'\\n#line {lnum} {fnum}\\n{prefix}'\n                lnum += prefix.count('\\n')\n            iname = m.group(1)\n            yield from self._load_sources(iname, seen, level+1)\n            pos = m.end()\n        if pos < len(src):\n            yield f'\\n#line {lnum} {fnum}\\n{src[pos:]}'\n\n    def apply_to_sources(self, vertex: Callable[[str], str] = identity, frag: Callable[[str], str] = identity) -> None:\n        self.vertex_sources = self.original_vertex_sources if vertex is identity else tuple(map(vertex, self.original_vertex_sources))\n        self.fragment_sources = self.original_fragment_sources if frag is identity else tuple(map(frag, self.original_fragment_sources))\n\n    def compile(self, program_id: int, allow_recompile: bool = False) -> None:\n        cerr: CompileError = CompileError()\n        try:\n            compile_program(program_id, self.vertex_sources, self.fragment_sources, allow_recompile)\n            return\n        except ValueError as err:\n            lines = str(err).splitlines()\n            msg = []\n            pat = re.compile(r'\\b(' + str(self.filename_number_base).replace('0', r'\\d') + r')\\b')\n            rmap = {str(v): k for k, v in self.filename_map.items()}\n\n            def sub(m: 're.Match[str]') -> str:\n                return rmap.get(m.group(1), m.group(1))\n\n            for line in lines:\n                msg.append(pat.sub(sub, line))\n            cerr = CompileError('\\n'.join(msg))\n        raise cerr\n\n\n@lru_cache(maxsize=64)\ndef program_for(name: str) -> Program:\n    return Program(name)\n\n\nclass MultiReplacer:\n\n    pat: Optional['re.Pattern[str]'] = None\n\n    def __init__(self, **replacements: Any):\n        self.replacements = {k: str(v) for k, v in replacements.items()}\n        if MultiReplacer.pat is None:\n            MultiReplacer.pat = re.compile(r'\\{([A-Z_]+)\\}')\n\n    def _sub(self, m: 're.Match[str]') -> str:\n        return self.replacements.get(m.group(1), m.group(1))\n\n    def __call__(self, src: str) -> str:\n        assert self.pat is not None\n        return self.pat.sub(self._sub, src)\n\nnull_replacer = MultiReplacer()\n\n\nclass TextFgOverrideThreshold(NamedTuple):\n    value: float = 0\n    unit: Literal['%', 'ratio'] = '%'\n    scaled_value: float = 0\n\n\nclass LoadShaderPrograms:\n    text_fg_override_threshold: TextFgOverrideThreshold = TextFgOverrideThreshold()\n    text_old_gamma: bool = False\n    cell_program_replacer: MultiReplacer = null_replacer\n\n    @property\n    def needs_recompile(self) -> bool:\n        opts = get_options()\n        return (\n            opts.text_fg_override_threshold != (self.text_fg_override_threshold.value, self.text_fg_override_threshold.unit)\n            or (opts.text_composition_strategy == 'legacy') != self.text_old_gamma\n        )\n\n    def recompile_if_needed(self) -> None:\n        if self.needs_recompile:\n            self(allow_recompile=True)\n\n    def __call__(self, allow_recompile: bool = False) -> None:\n        opts = get_options()\n        self.text_old_gamma = opts.text_composition_strategy == 'legacy'\n\n        text_fg_override_threshold: float = opts.text_fg_override_threshold[0]\n        match opts.text_fg_override_threshold[1]:\n            case '%':\n                text_fg_override_threshold = max(0, min(text_fg_override_threshold, 100.0)) * 0.01\n            case 'ratio':\n                text_fg_override_threshold = max(0, min(text_fg_override_threshold, 21.0))\n        self.text_fg_override_threshold = TextFgOverrideThreshold(\n                opts.text_fg_override_threshold[0], opts.text_fg_override_threshold[1], text_fg_override_threshold)\n\n        cell = program_for('cell')\n        if self.cell_program_replacer is null_replacer:\n            self.cell_program_replacer = MultiReplacer(\n                REVERSE_SHIFT=REVERSE,\n                STRIKE_SHIFT=STRIKETHROUGH,\n                DIM_SHIFT=DIM,\n                BLINK_SHIFT=BLINK,\n                DECORATION_SHIFT=DECORATION,\n                MARK_SHIFT=MARK,\n                MARK_MASK=MARK_MASK,\n                DECORATION_MASK=DECORATION_MASK,\n                COLOR_NOT_SET=COLOR_NOT_SET,\n                COLOR_IS_SPECIAL=COLOR_IS_SPECIAL,\n                COLOR_IS_INDEX=COLOR_IS_INDEX,\n                COLOR_IS_RGB=COLOR_IS_RGB,\n            )\n\n        def resolve_cell_defines(only_fg: int, only_bg: int, src: str) -> str:\n            r = self.cell_program_replacer.replacements\n            r['ONLY_FOREGROUND'] = str(only_fg)\n            r['ONLY_BACKGROUND'] = str(only_bg)\n            r['DO_FG_OVERRIDE'] = '1' if self.text_fg_override_threshold.scaled_value else '0'\n            r['FG_OVERRIDE_ALGO'] = '1' if self.text_fg_override_threshold.unit == '%' else '2'\n            r['FG_OVERRIDE_THRESHOLD'] = str(self.text_fg_override_threshold.scaled_value)\n            r['TEXT_NEW_GAMMA'] = '0' if self.text_old_gamma else '1'\n            return self.cell_program_replacer(src)\n        for prog, (only_fg, only_bg) in {\n                CELL_PROGRAM: (0, 0), CELL_FG_PROGRAM: (1, 0), CELL_BG_PROGRAM: (0, 1),\n        }.items():\n            fn = partial(resolve_cell_defines, only_fg, only_bg)\n            cell.apply_to_sources(vertex=fn, frag=fn)\n            cell.compile(prog, allow_recompile)\n        graphics = program_for('graphics')\n\n        def resolve_graphics_fragment_defines(which: str, is_premult: bool, f: str) -> str:\n            ans = f.replace('#define ALPHA_TYPE', f'#define {which}', 1)\n            return ans.replace('TEXTURE_IS_NOT_PREMULTIPLIED', '0' if is_premult else '1')\n\n        for p, (which, is_premult) in {\n            GRAPHICS_PROGRAM: ('IMAGE', False),\n            GRAPHICS_ALPHA_MASK_PROGRAM: ('ALPHA_MASK', False),\n            GRAPHICS_PREMULT_PROGRAM: ('IMAGE', True),\n        }.items():\n            graphics.apply_to_sources(frag=partial(resolve_graphics_fragment_defines, which, is_premult))\n            graphics.compile(p, allow_recompile)\n\n        program_for('bgimage').compile(BGIMAGE_PROGRAM, allow_recompile)\n        program_for('tint').compile(TINT_PROGRAM, allow_recompile)\n        program_for('trail').compile(TRAIL_PROGRAM, allow_recompile)\n        program_for('blit').compile(BLIT_PROGRAM, allow_recompile)\n        program_for('screenshot').compile(SCREENSHOT_PROGRAM, allow_recompile)\n        program_for('rounded_rect').compile(ROUNDED_RECT_PROGRAM, allow_recompile)\n        init_cell_program()\n\n\nload_shader_programs = LoadShaderPrograms()\n"
  },
  {
    "path": "kitty/shell_integration.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport re\nimport subprocess\nfrom collections.abc import Callable\nfrom contextlib import suppress\nfrom typing import Iterable\n\nfrom .constants import shell_integration_dir\nfrom .fast_data_types import get_options\nfrom .options.types import Options, defaults\nfrom .types import run_once\nfrom .utils import log_error, which\n\n\ndef setup_fish_env(env: dict[str, str], argv: list[str]) -> None:\n    val = env.get('XDG_DATA_DIRS')\n    env['KITTY_FISH_XDG_DATA_DIR'] = shell_integration_dir\n    if not val:\n        env['XDG_DATA_DIRS'] = shell_integration_dir\n    else:\n        dirs = list(filter(None, val.split(os.pathsep)))\n        dirs.insert(0, shell_integration_dir)\n        env['XDG_DATA_DIRS'] = os.pathsep.join(dirs)\n\n\ndef is_new_zsh_install(env: dict[str, str], zdotdir: str | None) -> bool:\n    # if ZDOTDIR is empty, zsh will read user rc files from /\n    # if there aren't any, it'll run zsh-newuser-install\n    # the latter will bail if there are rc files in $HOME\n    if not zdotdir:\n        zdotdir = env.get('HOME', os.path.expanduser('~'))\n        assert isinstance(zdotdir, str)\n        if zdotdir == '~':\n            return True\n    for q in ('.zshrc', '.zshenv', '.zprofile', '.zlogin'):\n        if os.path.exists(os.path.join(zdotdir, q)):\n            return False\n    return True\n\n\ndef get_zsh_zdotdir_from_global_zshenv(env: dict[str, str], argv: list[str]) -> str | None:\n    exe = which(argv[0], only_system=True) or 'zsh'\n    with suppress(Exception):\n        return subprocess.check_output([exe, '--norcs', '--interactive', '-c', 'echo -n $ZDOTDIR'], env=env).decode('utf-8')\n    return None\n\n\ndef setup_zsh_env(env: dict[str, str], argv: list[str]) -> None:\n    zdotdir = env.get('ZDOTDIR')\n    if is_new_zsh_install(env, zdotdir):\n        if zdotdir is None:\n            # Try to get ZDOTDIR from /etc/zshenv, when all startup files are not present\n            zdotdir = get_zsh_zdotdir_from_global_zshenv(env, argv)\n            if zdotdir is None or is_new_zsh_install(env, zdotdir):\n                return\n        else:\n            # dont prevent zsh-newuser-install from running\n            # zsh-newuser-install never runs as root but we assume that it does\n            return\n    if zdotdir is not None:\n        env['KITTY_ORIG_ZDOTDIR'] = zdotdir\n    else:\n        # KITTY_ORIG_ZDOTDIR can be set at this point if, for example, the global\n        # zshenv overrides ZDOTDIR; we try to limit the damage in this case\n        env.pop('KITTY_ORIG_ZDOTDIR', None)\n    env['ZDOTDIR'] = os.path.join(shell_integration_dir, 'zsh')\n\n\ndef setup_bash_env(env: dict[str, str], argv: list[str]) -> None:\n    inject = {'1'}\n    posix_env = rcfile = ''\n    remove_args = set()\n\n    expecting_multi_chars_opt = True\n    expecting_option_arg = False\n    interactive_opt = False\n    expecting_file_arg = False\n    file_arg_set = False\n\n    for i in range(1, len(argv)):\n        arg = argv[i]\n        if expecting_file_arg:\n            file_arg_set = True\n            break\n        if expecting_option_arg:\n            expecting_option_arg = False\n            continue\n        if arg in ('-', '--'):\n            if not expecting_file_arg:\n                expecting_file_arg = True\n            continue\n        elif len(arg) > 1 and arg[1] != '-' and (arg[0] == '-' or arg.startswith('+O')):\n            expecting_multi_chars_opt = False\n            options = arg.lstrip('-+')\n            # shopt option\n            if 'O' in options:\n                t = options.split('O', maxsplit=1)\n                if not t[1]:\n                    expecting_option_arg = True\n                options = t[0]\n            # command string\n            if 'c' in options:\n                # non-interactive shell\n                # also skip `bash -ic` interactive mode with command string\n                return\n            # read from stdin and follow with args\n            if 's' in options:\n                break\n            # interactive option\n            if 'i' in options:\n                interactive_opt = True\n        elif arg.startswith('--') and expecting_multi_chars_opt:\n            if arg == '--posix':\n                inject.add('posix')\n                posix_env = env.get('ENV', '')\n                remove_args.add(i)\n            elif arg == '--norc':\n                inject.add('no-rc')\n                remove_args.add(i)\n            elif arg == '--noprofile':\n                inject.add('no-profile')\n                remove_args.add(i)\n            elif arg in ('--rcfile', '--init-file') and i + 1 < len(argv):\n                expecting_option_arg = True\n                rcfile = argv[i+1]\n                remove_args |= {i, i+1}\n        else:\n            file_arg_set = True\n            break\n    if file_arg_set and not interactive_opt:\n        # non-interactive shell\n        return\n    env['ENV'] = os.path.join(shell_integration_dir, 'bash', 'kitty.bash')\n    env['KITTY_BASH_INJECT'] = ' '.join(inject)\n    if posix_env:\n        env['KITTY_BASH_POSIX_ENV'] = posix_env\n    if rcfile:\n        env['KITTY_BASH_RCFILE'] = rcfile\n    for i in sorted(remove_args, reverse=True):\n        del argv[i]\n    if 'HISTFILE' not in env and 'posix' not in inject:\n        # In POSIX mode the default history file is ~/.sh_history instead of ~/.bash_history\n        env['HISTFILE'] = os.path.expanduser('~/.bash_history')\n        env['KITTY_BASH_UNEXPORT_HISTFILE'] = '1'\n    argv.insert(1, '--posix')\n\n\ndef as_str_literal(x: str) -> str:\n    parts = x.split(\"'\")\n    return '\"\\'\"'.join(f\"'{x}'\" for x in parts)\n\n\ndef as_fish_str_literal(x: str) -> str:\n    x = x.replace('\\\\', '\\\\\\\\').replace(\"'\", \"\\\\'\")\n    return f\"'{x}'\"\n\n\ndef posix_serialize_env(env: dict[str, str], prefix: str = 'builtin export', sep: str = '=') -> str:\n    ans = []\n    for k, v in env.items():\n        ans.append(f'{prefix} {as_str_literal(k)}{sep}{as_str_literal(v)}')\n    return '\\n'.join(ans)\n\n\ndef fish_serialize_env(env: dict[str, str]) -> str:\n    ans = []\n    for k, v in env.items():\n        ans.append(f'set -gx {as_fish_str_literal(k)} {as_fish_str_literal(v)}')\n    return '\\n'.join(ans)\n\n\nENV_MODIFIERS = {\n    'fish': setup_fish_env,\n    'zsh': setup_zsh_env,\n    'bash': setup_bash_env,\n}\n\nENV_SERIALIZERS: dict[str, Callable[[dict[str, str]], str]] = {\n    'zsh':  posix_serialize_env,\n    'bash': posix_serialize_env,\n    'fish': fish_serialize_env,\n}\n\nQUOTERES =  {\n    'fish': as_fish_str_literal\n}\n\ndef get_supported_shell_name(path: str) -> str | None:\n    name = os.path.basename(path)\n    if name.lower().endswith('.exe'):\n        name = name.rpartition('.')[0]\n    if name.startswith('-'):\n        name = name[1:]\n    return name if name in ENV_MODIFIERS else None\n\n\ndef shell_integration_allows_rc_modification(opts: Options) -> bool:\n    return not (opts.shell_integration & {'disabled', 'no-rc'})\n\n\ndef serialize_env(path: str, env: dict[str, str]) -> str:\n    if not env:\n        return ''\n    name = get_supported_shell_name(path)\n    if not name:\n        raise ValueError(f'{path} is not a supported shell')\n    return ENV_SERIALIZERS[name](env)\n\n\n@run_once\ndef unsafe_pat() -> re.Pattern[str]:\n    return re.compile(r'[^\\w@%+=:,./-]', re.ASCII)\n\n\ndef join(path: str, cmd: Iterable[str]) -> str:\n    name = get_supported_shell_name(path)\n    _find_unsafe = unsafe_pat().search\n    if not name:\n        raise ValueError(f'{path} is not a supported shell')\n    q = QUOTERES.get(name, as_str_literal)\n    def quote(x: str) -> str:\n        return x if _find_unsafe(x) is None else q(x)\n    return ' '.join(map(quote, cmd))\n\n\ndef get_effective_ksi_env_var(opts: Options | None = None) -> str:\n    opts = opts or get_options()\n    if 'disabled' in opts.shell_integration:\n        return ''\n    # Use the default when shell_integration is empty due to misconfiguration\n    if 'invalid' in opts.shell_integration:\n        return ' '.join(defaults.shell_integration)\n    return ' '.join(opts.shell_integration)\n\n\ndef modify_shell_environ(opts: Options, env: dict[str, str], argv: list[str]) -> None:\n    shell = get_supported_shell_name(argv[0])\n    ksi = get_effective_ksi_env_var(opts)\n    if shell is None or not ksi:\n        return\n    env['KITTY_SHELL_INTEGRATION'] = ksi\n    if not shell_integration_allows_rc_modification(opts):\n        return\n    f = ENV_MODIFIERS.get(shell)\n    if f is not None:\n        try:\n            f(env, argv)\n        except Exception:\n            import traceback\n            traceback.print_exc()\n            log_error(f'Failed to setup shell integration for: {shell}')\n"
  },
  {
    "path": "kitty/shlex.c",
    "content": "/*\n * shlex.c\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"unicodeobject.h\"\n#include \"launcher/shlex.h\"\n\ntypedef struct {\n    PyObject_HEAD\n    ShlexState state;\n    PyObject *src;\n    bool yielded;\n    void *data; int kind;\n    size_t unicode_pos, src_pos_at_last_unicode_pos;\n} Shlex;\n\n\nstatic PyObject *\nnew_shlex_object(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {\n    Shlex *self;\n    self = (Shlex *)type->tp_alloc(type, 0);\n    if (self) {\n        const char *src; Py_ssize_t sz;\n        int support_ansi_c_quoting;\n        if (!PyArg_ParseTuple(args, \"s#|p\", &src, &sz, &support_ansi_c_quoting)) return NULL;\n        if (!alloc_shlex_state(&self->state, src, sz, support_ansi_c_quoting != 0)) return PyErr_NoMemory();\n        self->src = PyTuple_GetItem(args, 0);\n        self->data = PyUnicode_DATA(self->src);\n        self->kind = PyUnicode_KIND(self->src);\n        Py_INCREF(self->src);\n    }\n    return (PyObject*) self;\n}\n\nstatic void\ndealloc(Shlex* self) {\n    Py_CLEAR(self->src); dealloc_shlex_state(&self->state);\n}\n\nstatic size_t\nadvance_unicode_pos(Shlex *self) {\n    ssize_t num_bytes = self->state.word_start - self->src_pos_at_last_unicode_pos;\n    self->src_pos_at_last_unicode_pos = self->state.word_start;\n    char buf[8];\n    while (num_bytes > 0) {\n        Py_UCS4 ch = PyUnicode_READ(self->kind, self->data, self->unicode_pos);\n        num_bytes -= encode_utf8(ch, buf);\n        self->unicode_pos++;\n    }\n    return self->unicode_pos;\n}\n\nstatic PyObject*\nnext_word_with_position(Shlex *self, PyObject *args UNUSED) {\n    ssize_t len = next_word(&self->state);\n    unsigned long pos = advance_unicode_pos(self);\n    switch(len) {\n        case -1: PyErr_SetString(PyExc_ValueError, self->state.err); return NULL;\n        case -2:\n            if (self->yielded) return Py_BuildValue(\"is#\", -1, self->state.buf, 0);\n            len = 0;\n            /* fallthrough */\n        default:\n            self->yielded = true;\n            return Py_BuildValue(\"ks#\", pos, self->state.buf, (Py_ssize_t)len);\n    }\n}\n\nstatic PyObject*\nnext(PyObject *self_) {\n    Shlex *self = (Shlex*)self_;\n    ssize_t len = next_word(&self->state);\n    switch(len) {\n        case -1: PyErr_SetString(PyExc_ValueError, self->state.err); return NULL;\n        case -2:\n            if (self->yielded) { PyErr_SetNone(PyExc_StopIteration); return NULL; }\n            len = 0;\n            /* fallthrough */\n        default:\n            self->yielded = true;\n            return PyUnicode_FromStringAndSize(self->state.buf, (Py_ssize_t)len);\n    }\n}\n\nstatic PyObject*\niter(PyObject *s) { return Py_NewRef(s); }\n\nstatic PyMethodDef methods[] = {\n    {\"next_word\", (PyCFunction)next_word_with_position, METH_NOARGS, \"\"},\n    {NULL}  /* Sentinel */\n};\n\nPyTypeObject Shlex_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.Shlex\",\n    .tp_basicsize = sizeof(Shlex),\n    .tp_dealloc = (destructor)dealloc,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"Lexing like a shell\",\n    .tp_iternext = next,\n    .tp_new = new_shlex_object,\n    .tp_iter = iter,\n    .tp_methods = methods,\n};\n\nINIT_TYPE(Shlex)\n"
  },
  {
    "path": "kitty/shm.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n# This is present in the python stdlib (version 3.7) in\n# multiprocessing.shared_memory. However, it is crippled in various ways, most\n# notably using extremely small filenames.\n\nimport errno\nimport mmap\nimport os\nimport secrets\nimport stat\nimport struct\nfrom typing import Literal, cast\n\nfrom kitty.fast_data_types import SHM_NAME_MAX, shm_open, shm_unlink\n\n\ndef make_filename(prefix: str) -> str:\n    \"Create a random filename for the shared memory object.\"\n    # number of random bytes to use for name. Use a largeish value\n    # to make double unlink safe.\n    if not prefix.startswith('/'):\n        # FreeBSD requires name to start with /\n        prefix = '/' + prefix\n    plen = len(prefix.encode('utf-8'))\n    safe_length = min(plen + 64, SHM_NAME_MAX)\n    if safe_length - plen < 2:\n        raise OSError(errno.ENAMETOOLONG, f'SHM filename prefix {prefix} is too long')\n    nbytes = (safe_length - plen) // 2\n    name = prefix + secrets.token_hex(nbytes)\n    return name\n\n\nclass SharedMemory:\n    '''\n    Create or access randomly named shared memory. To create call with empty name and specific size.\n    To access call with name only.\n\n    WARNING: The actual size of the shared memory may be larger than the requested size.\n    '''\n    _fd: int = -1\n    _name: str = ''\n    _mmap: mmap.mmap | None = None\n    _size: int = 0\n    size_fmt = '!I'\n    num_bytes_for_size = struct.calcsize(size_fmt)\n\n    def __init__(\n        self, name: str = '', size: int = 0, readonly: bool = False,\n        mode: int = stat.S_IREAD | stat.S_IWRITE,\n        prefix: str = 'kitty-',\n        unlink_on_exit: bool = False, ignore_close_failure: bool = False\n    ):\n        self.unlink_on_exit = unlink_on_exit\n        self.ignore_close_failure = ignore_close_failure\n        if size < 0:\n            raise TypeError(\"'size' must be a non-negative integer\")\n        if size and name:\n            raise TypeError('Cannot specify both name and size')\n        if not name:\n            flags = os.O_CREAT | os.O_EXCL\n            if not size:\n                raise TypeError(\"'size' must be > 0\")\n        else:\n            flags = 0\n        flags |= os.O_RDONLY if readonly else os.O_RDWR\n\n        tries = 30\n        while not name and tries > 0:\n            tries -= 1\n            q = make_filename(prefix)\n            try:\n                self._fd = shm_open(q, flags, mode)\n                name = q\n            except FileExistsError:\n                continue\n        if tries <= 0:\n            raise OSError(f'Failed to create a uniquely named SHM file, try shortening the prefix from: {prefix}')\n        if self._fd < 0:\n            self._fd = shm_open(name, flags, mode)\n        self._name = name\n        try:\n            if flags & os.O_CREAT and size:\n                if hasattr(os, 'posix_fallocate'):\n                    os.posix_fallocate(self._fd, 0, size)\n                else:\n                    os.ftruncate(self._fd, size)\n            self.stats = os.fstat(self._fd)\n            size = self.stats.st_size\n            self._mmap = mmap.mmap(self._fd, size, access=mmap.ACCESS_READ if readonly else mmap.ACCESS_WRITE)\n        except OSError:\n            self.unlink()\n            raise\n\n        self._size = size\n\n    def read(self, sz: int = 0) -> bytes:\n        if sz <= 0:\n            sz = self.size\n        return self.mmap.read(sz)\n\n    def write(self, data: bytes) -> None:\n        self.mmap.write(data)\n\n    def tell(self) -> int:\n        return self.mmap.tell()\n\n    def seek(self, pos: int, whence: int = os.SEEK_SET) -> None:\n        self.mmap.seek(pos, cast(Literal[0, 1, 2, 3, 4], max(0, min(whence, 4))))\n\n    def flush(self) -> None:\n        self.mmap.flush()\n\n    def write_data_with_size(self, data: str | bytes) -> None:\n        if isinstance(data, str):\n            data = data.encode('utf-8')\n        sz = struct.pack(self.size_fmt, len(data))\n        self.write(sz)\n        self.write(data)\n\n    def read_data_with_size(self) -> bytes:\n        sz = struct.unpack(self.size_fmt, self.read(self.num_bytes_for_size))[0]\n        return self.read(sz)\n\n    def __del__(self) -> None:\n        try:\n            self.close()\n        except OSError:\n            pass\n\n    def __enter__(self) -> 'SharedMemory':\n        return self\n\n    def __exit__(self, *a: object) -> None:\n        self.close()\n        if self.unlink_on_exit:\n            self.unlink()\n\n    @property\n    def size(self) -> int:\n        return self._size\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    @property\n    def mmap(self) -> mmap.mmap:\n        ans = self._mmap\n        if ans is None:\n            raise RuntimeError('Cannot access the mmap of a closed shared memory object')\n        return ans\n\n    def fileno(self) -> int:\n        return self._fd\n\n    def __repr__(self) -> str:\n        return f'{self.__class__.__name__}({self.name!r}, size={self.size})'\n\n    def close(self) -> None:\n        \"\"\"Closes access to the shared memory from this instance but does\n        not destroy the shared memory block.\"\"\"\n        if self._mmap is not None:\n            try:\n                self._mmap.close()\n            except BufferError:\n                if not self.ignore_close_failure:\n                    raise\n            self._mmap = None\n        if self._fd >= 0:\n            os.close(self._fd)\n            self._fd = -1\n\n    def unlink(self) -> None:\n        \"\"\"Requests that the underlying shared memory block be destroyed.\n\n        In order to ensure proper cleanup of resources, unlink should be\n        called once (and only once) across all processes which have access\n        to the shared memory block.\"\"\"\n        if self._name:\n            try:\n                shm_unlink(self._name)\n            except FileNotFoundError:\n                pass\n            self._name = ''\n"
  },
  {
    "path": "kitty/short_uuid.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport math\nimport string\nimport uuid as _uuid\nfrom collections.abc import Sequence\n\n\ndef num_to_string(number: int, alphabet: Sequence[str], alphabet_len: int, pad_to_length: int | None = None) -> str:\n    ans = []\n    number = max(0, number)\n    while number:\n        number, digit = divmod(number, alphabet_len)\n        ans.append(alphabet[digit])\n    if pad_to_length is not None and pad_to_length > len(ans):\n        ans.append(alphabet[0] * (pad_to_length - len(ans)))\n    return ''.join(ans)\n\n\ndef string_to_num(string: str, alphabet_map: dict[str, int], alphabet_len: int) -> int:\n    ans = 0\n    for char in reversed(string):\n        ans = ans * alphabet_len + alphabet_map[char]\n    return ans\n\n\nescape_code_safe_alphabet = string.ascii_letters + string.digits + string.punctuation + ' '\nhuman_alphabet = (string.digits + string.ascii_letters)[2:]\n\n\nclass ShortUUID:\n\n    def __init__(self, alphabet: str = human_alphabet):\n        self.alphabet = tuple(sorted(alphabet))\n        self.alphabet_len = len(self.alphabet)\n        self.alphabet_map = {c: i for i, c in enumerate(self.alphabet)}\n        self.uuid_pad_len = int(math.ceil(math.log(1 << 128, self.alphabet_len)))\n\n    def uuid4(self, pad_to_length: int | None = None) -> str:\n        if pad_to_length is None:\n            pad_to_length = self.uuid_pad_len\n        return num_to_string(_uuid.uuid4().int, self.alphabet, self.alphabet_len, pad_to_length)\n\n    def uuid5(self, namespace: _uuid.UUID, name: str, pad_to_length: int | None = None) -> str:\n        if pad_to_length is None:\n            pad_to_length = self.uuid_pad_len\n        return num_to_string(_uuid.uuid5(namespace, name).int, self.alphabet, self.alphabet_len, pad_to_length)\n\n    def decode(self, encoded: str) -> _uuid.UUID:\n        return _uuid.UUID(int=string_to_num(encoded, self.alphabet_map, self.alphabet_len))\n\n\n_global_instance = ShortUUID()\nuuid4 = _global_instance.uuid4\nuuid5 = _global_instance.uuid5\ndecode = _global_instance.decode\n_escape_code_instance: ShortUUID | None = None\n\n\ndef uuid4_for_escape_code() -> str:\n    global _escape_code_instance\n    if _escape_code_instance is None:\n        _escape_code_instance = ShortUUID(escape_code_safe_alphabet)\n    return _escape_code_instance.uuid4()\n"
  },
  {
    "path": "kitty/simd-string-128.c",
    "content": "/*\n * simd-string-128.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define KITTY_SIMD_LEVEL 128\n#include \"simd-string-impl.h\"\n"
  },
  {
    "path": "kitty/simd-string-256.c",
    "content": "/*\n * simd-string-128.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#define KITTY_SIMD_LEVEL 256\n#include \"simd-string-impl.h\"\n"
  },
  {
    "path": "kitty/simd-string-impl.h",
    "content": "/*\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include \"data-types.h\"\n#include \"simd-string.h\"\n#include <stdalign.h>\n\n#ifndef KITTY_SIMD_LEVEL\n#define KITTY_SIMD_LEVEL 128\n#endif\n#define CONCAT(A, B) A##B\n#define CONCAT_EXPAND(A, B) CONCAT(A,B)\n#define FUNC(name) CONCAT_EXPAND(name##_, KITTY_SIMD_LEVEL)\n\n#ifdef KITTY_NO_SIMD\n#define NOSIMD { fatal(\"No SIMD implementations for this CPU\"); }\nbool FUNC(utf8_decode_to_esc)(UTF8Decoder *d UNUSED, const uint8_t *src UNUSED, size_t src_sz UNUSED) NOSIMD\nconst uint8_t* FUNC(find_either_of_two_bytes)(const uint8_t *haystack UNUSED, const size_t sz UNUSED, const uint8_t a UNUSED, const uint8_t b UNUSED) NOSIMD\nvoid FUNC(xor_data64)(const uint8_t key[64] UNUSED, uint8_t* data UNUSED, const size_t data_sz UNUSED) NOSIMD\n#undef NOSIMD\n#else\n\n#include \"charsets.h\"\n\n// Boilerplate {{{\nSTART_IGNORE_DIAGNOSTIC(\"-Wfloat-conversion\")\nSTART_IGNORE_DIAGNOSTIC(\"-Wpedantic\")\n#if  defined(__clang__) && __clang_major__ > 13\n_Pragma(\"clang diagnostic push\")\n_Pragma(\"clang diagnostic ignored \\\"-Wbitwise-instead-of-logical\\\"\")\n#endif\n#include <simde/x86/avx2.h>\n#include <simde/arm/neon.h>\n#if  defined(__clang__) && __clang_major__ > 13\n_Pragma(\"clang diagnostic pop\")\n#endif\nEND_IGNORE_DIAGNOSTIC\nEND_IGNORE_DIAGNOSTIC\n\n\n#ifndef _MM_SHUFFLE\n#define _MM_SHUFFLE(z, y, x, w) (((z) << 6) | ((y) << 4) | ((x) << 2) | (w))\n#endif\n#define integer_t CONCAT_EXPAND(CONCAT_EXPAND(simde__m, KITTY_SIMD_LEVEL), i)\n#define shift_right_by_bytes128 simde_mm_srli_si128\n#define is_zero FUNC(is_zero)\n\n#if KITTY_SIMD_LEVEL == 128\n#define set1_epi8(x) simde_mm_set1_epi8((char)(x))\n#define set_epi8 simde_mm_set_epi8\n#define add_epi8 simde_mm_add_epi8\n#define load_unaligned simde_mm_loadu_si128\n#define load_aligned(x) simde_mm_load_si128((const integer_t*)(x))\n#define store_unaligned simde_mm_storeu_si128\n#define store_aligned(dest, vec) simde_mm_store_si128((integer_t*)dest, vec)\n#define cmpeq_epi8 simde_mm_cmpeq_epi8\n#define cmplt_epi8 simde_mm_cmplt_epi8\n#define cmpgt_epi8 simde_mm_cmpgt_epi8\n#define or_si simde_mm_or_si128\n#define and_si simde_mm_and_si128\n#define xor_si simde_mm_xor_si128\n#define andnot_si simde_mm_andnot_si128\n#define movemask_epi8 simde_mm_movemask_epi8\n#define extract_lower_quarter_as_chars simde_mm_cvtepu8_epi32\n#define blendv_epi8 simde_mm_blendv_epi8\n#define shift_left_by_bits16 simde_mm_slli_epi16\n#define shift_right_by_bits32 simde_mm_srli_epi32\n#define shuffle_epi8 simde_mm_shuffle_epi8\n#define numbered_bytes() set_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)\n#define reverse_numbered_bytes() simde_mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)\n// output[i] = MAX(0, a[i] - b[1i])\n#define subtract_saturate_epu8 simde_mm_subs_epu8\n#define subtract_epi8 simde_mm_sub_epi8\n#define create_zero_integer simde_mm_setzero_si128\n#define create_all_ones_integer() simde_mm_set1_epi64x(-1)\n#define sum_bytes sum_bytes_128\n#define zero_upper()\n\nstatic inline int\nFUNC(is_zero)(const integer_t a) { return simde_mm_testz_si128(a, a); }\n\n#define GA(LA) LA(1) LA(2) LA(3) LA(4) LA(5) LA(6) LA(7) LA(8) LA(9) LA(10) LA(11) LA(12) LA(13) LA(14) LA(15)\n#define L(n) case n: return simde_mm_srli_si128(A, n);\n#define R(n) case n: return simde_mm_slli_si128(A, n);\n#define shift_left_by_bytes_macro(A, n) { switch(n) { default: return A; GA(L) } }\n#define shift_right_by_bytes_macro(A, n) { switch(n) { default: return A; GA(R) } }\n\nstatic inline integer_t shift_right_by_bytes(const integer_t A, unsigned n) { shift_right_by_bytes_macro(A, n) }\nstatic inline integer_t shift_left_by_bytes(const integer_t A, unsigned n) { shift_left_by_bytes_macro(A, n) }\n\n#define w(dir, word, num) static inline integer_t shift_##dir##_by_##word(const integer_t A) { shift_##dir##_by_bytes_macro(A, num); }\n\nw(right, one_byte, 1)\nw(right, two_bytes, 2)\nw(right, four_bytes, 4)\nw(right, eight_bytes, 8)\nw(right, sixteen_bytes, 16)\nw(left, one_byte, 1)\nw(left, two_bytes, 2)\nw(left, four_bytes, 4)\nw(left, eight_bytes, 8)\nw(left, sixteen_bytes, 16)\n#undef w\n#undef GA\n#undef L\n#undef R\n#undef shift_right_by_bytes_macro\n#undef shift_left_by_bytes_macro\n\n#else\n\n#if defined(SIMDE_ARCH_AMD64) || defined(SIMDE_ARCH_X86)\n#define zero_upper _mm256_zeroupper\n#else\n#define zero_upper()\n#endif\n#define set1_epi8(x) simde_mm256_set1_epi8((char)(x))\n#define set_epi8 simde_mm256_set_epi8\n#define add_epi8 simde_mm256_add_epi8\n#define load_unaligned simde_mm256_loadu_si256\n#define load_aligned(x) simde_mm256_load_si256((const integer_t*)(x))\n#define store_unaligned simde_mm256_storeu_si256\n#define store_aligned(dest, vec) simde_mm256_store_si256((integer_t*)dest, vec)\n#define cmpeq_epi8 simde_mm256_cmpeq_epi8\n#define cmpgt_epi8 simde_mm256_cmpgt_epi8\n#define cmplt_epi8(a, b) cmpgt_epi8(b, a)\n#define or_si simde_mm256_or_si256\n#define and_si simde_mm256_and_si256\n#define xor_si simde_mm256_xor_si256\n#define andnot_si simde_mm256_andnot_si256\n#define movemask_epi8 simde_mm256_movemask_epi8\n#define extract_lower_half_as_chars simde_mm256_cvtepu8_epi32\n#define blendv_epi8 simde_mm256_blendv_epi8\n#define subtract_saturate_epu8 simde_mm256_subs_epu8\n#define subtract_epi8 simde_mm256_sub_epi8\n#define shift_left_by_bits16 simde_mm256_slli_epi16\n#define shift_right_by_bits32 simde_mm256_srli_epi32\n#define create_zero_integer simde_mm256_setzero_si256\n#define create_all_ones_integer() simde_mm256_set1_epi64x(-1)\n#define numbered_bytes() set_epi8(31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)\n#define reverse_numbered_bytes() simde_mm256_setr_epi8(31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)\n\nstatic inline int\nFUNC(is_zero)(const integer_t a) { return simde_mm256_testz_si256(a, a); }\n\n#define GA(LA) LA(1) LA(2) LA(3) LA(4) LA(5) LA(6) LA(7) LA(8) LA(9) LA(10) LA(11) LA(12) LA(13) LA(14) LA(15)\n#define GB(LA) LA(17) LA(18) LA(19) LA(20) LA(21) LA(22) LA(23) LA(24) LA(25) LA(26) LA(27) LA(28) LA(29) LA(30) LA(31)\n#define RA(n) case n: return simde_mm256_alignr_epi8(A, simde_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 2, 0)), 16 - n);\n#define RB(n)  case n: return simde_mm256_slli_si256(simde_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 2, 0)), n - 16); \\\n\n#define shift_right_by_bytes_macro(A, n) { \\\n    switch(n) { \\\n        default: return A; \\\n        GA(RA) \\\n        case 16: return simde_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 2, 0)); \\\n        GB(RB) \\\n    } \\\n}\n\n#define LA(n) case n: return simde_mm256_alignr_epi8(simde_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1)), A, n);\n#define LB(n) case n: return simde_mm256_srli_si256(simde_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1)), n - 16);\n#define shift_left_by_bytes_macro(A, n) { \\\n    switch(n) { \\\n        default: return A; \\\n        GA(LA) \\\n        case 16: return simde_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1)); \\\n        GB(LB) \\\n    } \\\n}\n\n\nstatic inline integer_t shift_right_by_bytes(const integer_t A, unsigned n) { shift_right_by_bytes_macro(A, n) }\nstatic inline integer_t shift_left_by_bytes(const integer_t A, unsigned n) { shift_left_by_bytes_macro(A, n) }\n\n#define w(dir, word, num) static inline integer_t shift_##dir##_by_##word(const integer_t A) { shift_##dir##_by_bytes_macro(A, num); }\n\nw(right, one_byte, 1)\nw(right, two_bytes, 2)\nw(right, four_bytes, 4)\nw(right, eight_bytes, 8)\nw(right, sixteen_bytes, 16)\nw(left, one_byte, 1)\nw(left, two_bytes, 2)\nw(left, four_bytes, 4)\nw(left, eight_bytes, 8)\nw(left, sixteen_bytes, 16)\n#undef LA\n#undef LB\n#undef GA\n#undef GB\n#undef RA\n#undef RB\n#undef w\n#undef shift_right_by_bytes_macro\n#undef shift_left_by_bytes_macro\n\nstatic inline integer_t shuffle_impl256(const integer_t value, const integer_t shuffle) {\n#define K0 simde_mm256_setr_epi8( \\\n        0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, \\\n        -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16)\n\n#define K1 simde_mm256_setr_epi8( \\\n        -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, \\\n        0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70)\n\n    return or_si(\n            simde_mm256_shuffle_epi8(value, add_epi8(shuffle, K0)),\n            simde_mm256_shuffle_epi8(simde_mm256_permute4x64_epi64(value, 0x4E), simde_mm256_add_epi8(shuffle, K1))\n    );\n#undef K0\n#undef K1\n}\n\n#define shuffle_epi8 shuffle_impl256\n#define sum_bytes(x) (sum_bytes_128(simde_mm256_extracti128_si256(x, 0)) + sum_bytes_128(simde_mm256_extracti128_si256(x, 1)))\n#endif\n\n#define print_register_as_bytes(r) { \\\n    printf(\"%s:\\n\", #r); \\\n    alignas(64) uint8_t data[sizeof(r)]; \\\n    store_unaligned((integer_t*)data, r); \\\n    for (unsigned i = 0; i < sizeof(integer_t); i++) { \\\n        uint8_t ch = data[i]; \\\n        if (' ' <= ch && ch < 0x7f) printf(\"_%c \", ch); else printf(\"%.2x \", ch); \\\n    } \\\n    printf(\"\\n\"); \\\n}\n\n#if 0\n#define debug_register print_register_as_bytes\n#define debug printf\n#else\n#define debug_register(...)\n#define debug(...)\n#endif\n\n#if (defined(__arm64__) && defined(__APPLE__)) || defined(__aarch64__)\n// See https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon\n\nstatic inline uint64_t\nmovemask_arm128(const simde__m128i vec) {\n    simde_uint8x8_t res = simde_vshrn_n_u16(simde_vreinterpretq_u16_u8((simde_uint8x16_t)vec), 4);\n    return simde_vget_lane_u64(simde_vreinterpret_u64_u8(res), 0);\n}\n\n#if KITTY_SIMD_LEVEL == 128\n\nstatic inline int\nbytes_to_first_match(const integer_t vec) { const uint64_t m = movemask_arm128(vec); return m ? (__builtin_ctzll(m) >> 2) : -1; }\n\nstatic inline int\nbytes_to_first_match_ignoring_leading_n(const integer_t vec, uintptr_t num_ignored) {\n    uint64_t m = movemask_arm128(vec);\n    m >>= num_ignored << 2;\n    return m ? (__builtin_ctzll(m) >> 2) : -1;\n}\n\n#else\n\nstatic inline int\nbytes_to_first_match(const integer_t vec) {\n    if (is_zero(vec)) return -1;\n    simde__m128i v = simde_mm256_extracti128_si256(vec, 0);\n    if (!simde_mm_testz_si128(v, v)) return __builtin_ctzll(movemask_arm128(v)) >> 2;\n    v = simde_mm256_extracti128_si256(vec, 1);\n    return 16 + (__builtin_ctzll(movemask_arm128(v)) >> 2);\n}\n\nstatic inline int\nbytes_to_first_match_ignoring_leading_n(const integer_t vec, uintptr_t num_ignored) {\n    uint64_t m;\n    int offset;\n    if (num_ignored < 16) {\n        m = ((uint64_t)movemask_arm128(simde_mm256_extracti128_si256(vec, 0))) >> (num_ignored << 2);\n        if (m) return (__builtin_ctzll(m) >> 2);\n        offset = 16 - num_ignored;\n        num_ignored = 0;\n    } else {\n        num_ignored -= 16;\n        offset = 0;\n    }\n    m = ((uint64_t)movemask_arm128(simde_mm256_extracti128_si256(vec, 1))) >> (num_ignored << 2);\n    return m ? (offset + (__builtin_ctzll(m) >> 2)) : -1;\n}\n#endif\n\n#else\n\nstatic inline int\nbytes_to_first_match(const integer_t vec) {\n    return is_zero(vec) ? -1 : __builtin_ctz(movemask_epi8(vec));\n}\n\nstatic inline int\nbytes_to_first_match_ignoring_leading_n(const integer_t vec, const uintptr_t num_ignored) {\n    uint32_t mask = movemask_epi8(vec);\n    mask >>= num_ignored;\n    return mask ? __builtin_ctz(mask) : -1;\n}\n\n\n#endif\n\n\n// }}}\n\nstatic inline integer_t\nzero_last_n_bytes(const integer_t vec, const char n) {\n    integer_t mask = create_all_ones_integer();\n    mask = shift_left_by_bytes(mask, n);\n    return and_si(mask, vec);\n}\n\n#define KEY_SIZE 64\nvoid\nFUNC(xor_data64)(const uint8_t key[KEY_SIZE], uint8_t* data, const size_t data_sz) {\n    // First process unaligned bytes at the start of data\n    const uintptr_t unaligned_bytes = KEY_SIZE - ((uintptr_t)data & (KEY_SIZE - 1));\n    if (data_sz <= unaligned_bytes) { for (unsigned i = 0; i < data_sz; i++) data[i] ^= key[i]; return; }\n    for (unsigned i = 0; i < unaligned_bytes; i++) data[i] ^= key[i];\n\n    // Rotate the key by unaligned_bytes\n    alignas(sizeof(integer_t)) char aligned_key[KEY_SIZE];\n    memcpy(aligned_key, key + unaligned_bytes, KEY_SIZE - unaligned_bytes);\n    memcpy(aligned_key + KEY_SIZE - unaligned_bytes, key, unaligned_bytes);\n\n    const integer_t v1 = load_aligned(aligned_key), v2 = load_aligned(aligned_key + sizeof(integer_t));\n#if KITTY_SIMD_LEVEL == 128\n    const integer_t v3 = load_aligned(aligned_key + 2*sizeof(integer_t)), v4 = load_aligned(aligned_key + 3 * sizeof(integer_t));\n#endif\n    // Process KEY_SIZE aligned chunks using SIMD\n    integer_t d;\n    uint8_t *p = data + unaligned_bytes, *limit = data + data_sz;\n    const uintptr_t trailing_bytes = (uintptr_t)limit & (KEY_SIZE - 1);\n    limit -= trailing_bytes;\n    // p is aligned to first KEY_SIZE boundary >= data and limit is aligned to first KEY_SIZE boundary <= (data + data_sz)\n#define do_one(which) d = load_aligned(p); store_aligned(p, xor_si(which, d)); p += sizeof(integer_t);\n    while (p < limit) {\n        do_one(v1); do_one(v2);\n#if KITTY_SIMD_LEVEL == 128\n        do_one(v3); do_one(v4);\n#endif\n    }\n#undef do_one\n    // Process remaining trailing_bytes\n    for (unsigned i = 0; i < trailing_bytes; i++) limit[i] ^= aligned_key[i];\n    zero_upper(); return;\n}\n#undef KEY_SIZE\n\n#define check_chunk() if (n > -1) { \\\n    const uint8_t *ans = haystack + n; \\\n    zero_upper(); \\\n    return ans < limit ? ans : NULL; \\\n}\n\n#define find_match(haystack, sz, get_test_vec) { \\\n    const uint8_t* limit = haystack + sz; \\\n    integer_t chunk; int n; \\\n\\\n    { /* first chunk which is possibly unaligned */  \\\n        const uintptr_t addr = (uintptr_t)haystack; \\\n        const uintptr_t unaligned_bytes = addr & (sizeof(integer_t) - 1); \\\n        chunk = load_aligned(haystack - unaligned_bytes); /* this is an aligned load from the first aligned pos before haystack */ \\\n        n = bytes_to_first_match_ignoring_leading_n(get_test_vec(chunk), unaligned_bytes); \\\n        check_chunk(); \\\n        haystack += sizeof(integer_t) - unaligned_bytes; \\\n    } \\\n\\\n    /* Iterate over aligned chunks */ \\\n    for (; haystack < limit; haystack += sizeof(integer_t)) { \\\n        chunk = load_aligned(haystack); \\\n        n = bytes_to_first_match(get_test_vec(chunk)); \\\n        check_chunk(); \\\n    } \\\n    zero_upper(); \\\n    return NULL;\\\n}\n\nconst uint8_t*\nFUNC(find_either_of_two_bytes)(const uint8_t *haystack, const size_t sz, const uint8_t a, const uint8_t b) {\n    if (!sz) return NULL;\n    const integer_t a_vec = set1_epi8(a), b_vec = set1_epi8(b);\n#define get_test_from_chunk(chunk) (or_si(cmpeq_epi8(chunk, a_vec), cmpeq_epi8(chunk, b_vec)))\n    find_match(haystack, sz, get_test_from_chunk);\n#undef get_test_from_chunk\n}\n\n#undef check_chunk\n\n#define output_increment sizeof(integer_t)/sizeof(uint32_t)\n\nstatic inline void\nFUNC(output_plain_ascii)(UTF8Decoder *d, integer_t vec, size_t src_sz) {\n    utf8_decoder_ensure_capacity(d, src_sz);\n#if KITTY_SIMD_LEVEL == 128\n    for (const uint32_t *p = d->output.storage + d->output.pos, *limit = p + src_sz; p < limit; p += output_increment) {\n        const integer_t unpacked = extract_lower_quarter_as_chars(vec);\n        store_unaligned((integer_t*)p, unpacked);\n        vec = shift_right_by_bytes128(vec, output_increment);\n    }\n#else\n    const uint32_t *p = d->output.storage + d->output.pos, *limit = p + src_sz;\n    simde__m128i x = simde_mm256_extracti128_si256(vec, 0);\n    integer_t unpacked = extract_lower_half_as_chars(x);\n    store_unaligned((integer_t*)p, unpacked); p += output_increment;\n    if (p < limit) {\n        x = shift_right_by_bytes128(x, output_increment);\n        unpacked = extract_lower_half_as_chars(x);\n        store_unaligned((integer_t*)p, unpacked); p += output_increment;\n        if (p < limit) {\n            x = simde_mm256_extracti128_si256(vec, 1);\n            unpacked = extract_lower_half_as_chars(x);\n            store_unaligned((integer_t*)p, unpacked); p += output_increment;\n            if (p < limit) {\n                x = shift_right_by_bytes128(x, output_increment);\n                unpacked = extract_lower_half_as_chars(x);\n                store_unaligned((integer_t*)p, unpacked); p += output_increment;\n            }\n        }\n    }\n#endif\n    d->output.pos += src_sz;\n}\n\nstatic inline void\nFUNC(output_unicode)(UTF8Decoder *d, integer_t output1, integer_t output2, integer_t output3, const size_t num_codepoints) {\n    utf8_decoder_ensure_capacity(d, 64);\n#if KITTY_SIMD_LEVEL == 128\n    for (const uint32_t *p = d->output.storage + d->output.pos, *limit = p + num_codepoints; p < limit; p += output_increment) {\n        const integer_t unpacked1 = extract_lower_quarter_as_chars(output1);\n        const integer_t unpacked2 = shift_right_by_one_byte(extract_lower_quarter_as_chars(output2));\n        const integer_t unpacked3 = shift_right_by_two_bytes(extract_lower_quarter_as_chars(output3));\n        const integer_t unpacked = or_si(or_si(unpacked1, unpacked2), unpacked3);\n        store_unaligned((integer_t*)p, unpacked);\n        output1 = shift_right_by_bytes128(output1, output_increment);\n        output2 = shift_right_by_bytes128(output2, output_increment);\n        output3 = shift_right_by_bytes128(output3, output_increment);\n    }\n#else\n    uint32_t *p = d->output.storage + d->output.pos;\n    const uint32_t *limit = p + num_codepoints;\n    simde__m128i x1, x2, x3;\n#define chunk() { \\\n        const integer_t unpacked1 = extract_lower_half_as_chars(x1); \\\n        const integer_t unpacked2 = shift_right_by_one_byte(extract_lower_half_as_chars(x2)); \\\n        const integer_t unpacked3 = shift_right_by_two_bytes(extract_lower_half_as_chars(x3)); \\\n        store_unaligned((integer_t*)p, or_si(or_si(unpacked1, unpacked2), unpacked3)); \\\n        p += output_increment; \\\n}\n#define extract(which) x1 = simde_mm256_extracti128_si256(output1, which); x2 = simde_mm256_extracti128_si256(output2, which); x3 = simde_mm256_extracti128_si256(output3, which);\n#define shift() x1 = shift_right_by_bytes128(x1, output_increment); x2 = shift_right_by_bytes128(x2, output_increment); x3 = shift_right_by_bytes128(x3, output_increment);\n    extract(0); chunk();\n    if (p < limit) {\n        shift(); chunk();\n        if (p < limit) {\n            extract(1); chunk();\n            if (p < limit) {\n                shift(); chunk();\n            }\n        }\n    }\n#undef chunk\n#undef extract\n#undef shift\n#endif\n    d->output.pos += num_codepoints;\n}\n#undef output_increment\n\nstatic inline unsigned\nsum_bytes_128(simde__m128i v) {\n    // Use _mm_sad_epu8 to perform a sum of absolute differences against zero\n    // This sums up all 8-bit integers in the 128-bit vector and packs the result into a 64-bit integer\n    simde__m128i sum = simde_mm_sad_epu8(v, simde_mm_setzero_si128());\n\n    // At this point, the sum of the first half is in the lower 64 bits, and the sum of the second half is in the upper 64 bits.\n    // Extract the lower and upper 64-bit sums and add them together.\n    const unsigned lower_sum = simde_mm_cvtsi128_si32(sum); // Extracts the lower 32 bits\n    const unsigned upper_sum = simde_mm_cvtsi128_si32(simde_mm_srli_si128(sum, 8)); // Extracts the upper 32 bits\n\n    return lower_sum + upper_sum; // Final sum of all bytes\n}\n\n#define do_one_byte \\\n    const uint8_t ch = src[pos++]; \\\n    switch (decode_utf8(&d->state.cur, &d->state.codep, ch)) { \\\n        case UTF8_ACCEPT: \\\n            d->output.storage[d->output.pos++] = d->state.codep; \\\n            break; \\\n        case UTF8_REJECT: { \\\n                const bool prev_was_accept = d->state.prev == UTF8_ACCEPT; \\\n                zero_at_ptr(&d->state); \\\n                d->output.storage[d->output.pos++] = 0xfffd; \\\n                if (!prev_was_accept) { \\\n                    pos--; \\\n                    continue; /* so that prev is correct */ \\\n                } \\\n        } break; \\\n    } \\\n    d->state.prev = d->state.cur;\n\nstatic inline size_t\nscalar_decode_to_accept(UTF8Decoder *d, const uint8_t *src, size_t src_sz) {\n    size_t pos = 0;\n    utf8_decoder_ensure_capacity(d, src_sz);\n    while (pos < src_sz && d->state.cur != UTF8_ACCEPT) {\n        do_one_byte\n    }\n    return pos;\n}\n\nstatic inline size_t\nscalar_decode_all(UTF8Decoder *d, const uint8_t *src, size_t src_sz) {\n    size_t pos = 0;\n    utf8_decoder_ensure_capacity(d, src_sz);\n    while (pos < src_sz) {\n        do_one_byte\n    }\n    return pos;\n}\n\n#undef do_one_byte\n\nbool\nFUNC(utf8_decode_to_esc)(UTF8Decoder *d, const uint8_t *src_data, size_t src_len) {\n    // Based on the algorithm described in: https://woboq.com/blog/utf-8-processing-using-simd.html\n#ifdef compare_with_scalar\n    UTF8Decoder debugdec ={0};\n    memcpy(&debugdec.state, &d->state, sizeof(debugdec.state));\n    bool scalar_sentinel_found = utf8_decode_to_esc_scalar(&debugdec, src_data, src_len);\n#endif\n    d->output.pos = 0; d->num_consumed = 0;\n    if (d->state.cur != UTF8_ACCEPT) {\n        // Finish the trailing sequence only\n        d->num_consumed = scalar_decode_to_accept(d, src_data, src_len);\n        src_data += d->num_consumed; src_len -= d->num_consumed;\n    }\n    const integer_t esc_vec = set1_epi8(0x1b);\n    const integer_t zero = create_zero_integer(), one = set1_epi8(1), two = set1_epi8(2), three = set1_epi8(3), four = set1_epi8(4), numbered = numbered_bytes();\n    const uint8_t *limit = src_data + src_len, *p = src_data, *start_of_current_chunk = src_data;\n    bool sentinel_found = false;\n    unsigned chunk_src_sz = 0;\n    unsigned num_of_trailing_bytes = 0;\n\n    while (p < limit && !sentinel_found) {\n        chunk_src_sz = MIN((size_t)(limit - p), sizeof(integer_t));\n        integer_t vec = load_unaligned((integer_t*)p);\n        start_of_current_chunk = p;\n        p += chunk_src_sz;\n\n        const integer_t esc_cmp = cmpeq_epi8(vec, esc_vec);\n        int num_of_bytes_to_first_esc = bytes_to_first_match(esc_cmp);\n        if (num_of_bytes_to_first_esc > -1 && (unsigned)num_of_bytes_to_first_esc < chunk_src_sz) {\n            sentinel_found = true;\n            chunk_src_sz = num_of_bytes_to_first_esc;\n            d->num_consumed += chunk_src_sz + 1;  // esc is also consumed\n            if (!chunk_src_sz) continue;\n        } else d->num_consumed += chunk_src_sz;\n\n        if (chunk_src_sz < sizeof(integer_t)) vec = zero_last_n_bytes(vec, sizeof(integer_t) - chunk_src_sz);\n\n        num_of_trailing_bytes = 0;\n        bool check_for_trailing_bytes = !sentinel_found;\n\n        debug_register(vec);\n        int32_t ascii_mask;\n\n#define abort_with_invalid_utf8() { \\\n    scalar_decode_all(d, start_of_current_chunk, chunk_src_sz + num_of_trailing_bytes); \\\n    d->num_consumed += num_of_trailing_bytes; \\\n    break; \\\n}\n\n#define handle_trailing_bytes() if (num_of_trailing_bytes) { \\\n    if (p >= limit) { \\\n        scalar_decode_all(d, p - num_of_trailing_bytes, num_of_trailing_bytes); \\\n        d->num_consumed += num_of_trailing_bytes; \\\n        break; \\\n    } \\\n    p -= num_of_trailing_bytes; \\\n}\n\nstart_classification:\n        // Check if we have pure ASCII and use fast path\n        ascii_mask = movemask_epi8(vec);\n        if (!ascii_mask) { // no bytes with high bit (0x80) set, so just plain ASCII\n            FUNC(output_plain_ascii)(d, vec, chunk_src_sz);\n            handle_trailing_bytes();\n            continue;\n        }\n        // Classify the bytes by whether they may be the start of a 2-byte, 3-byte, or 4-byte sequence.\n        // This is only an initial, potential classification.\n        // 0xC0 and 0xC1 are initially classified as potential starter bytes of 2-byte sequences.\n        // And 0xF5..0xFF are initially classified as potential starter bytes of 4-byte sequences.\n        // They will be marked as actually invalid later in the chunk_is_invalid checks.\n        integer_t state = set1_epi8(0x80);\n        const integer_t vec_signed = add_epi8(vec, state); // needed because cmplt_epi8 works only on signed chars\n        // state now has 0x80 on all bytes\n        const integer_t bytes_indicating_start_of_two_byte_sequence = cmplt_epi8(set1_epi8(0xc0 - 1 - 0x80), vec_signed);\n        state = blendv_epi8(state, set1_epi8(0xc2), bytes_indicating_start_of_two_byte_sequence);\n        // state now has 0xc2 on all bytes that start a 2 or more byte sequence and 0x80 on the rest\n        const integer_t bytes_indicating_start_of_three_byte_sequence = cmplt_epi8(set1_epi8(0xe0 - 1 - 0x80), vec_signed);\n        state = blendv_epi8(state, set1_epi8(0xe3), bytes_indicating_start_of_three_byte_sequence);\n        const integer_t bytes_indicating_start_of_four_byte_sequence = cmplt_epi8(set1_epi8(0xf0 - 1 - 0x80), vec_signed);\n        state = blendv_epi8(state, set1_epi8(0xf4), bytes_indicating_start_of_four_byte_sequence);\n        // state now has 0xc2 on all bytes that start a 2 byte sequence, 0xe3 on start of 3-byte, 0xf4 on 4-byte start and 0x80 on rest\n        debug_register(state);\n        const integer_t mask = and_si(state, set1_epi8(0xf8));  // keep upper 5 bits of state\n        debug_register(mask);\n        const integer_t count = and_si(state, set1_epi8(0x7));  // keep lower 3 bits of state\n        debug_register(count);\n        // count contains the number of bytes in the sequence for the start byte of every sequence and zero elsewhere\n        // shift 02 bytes by 1 and subtract 1\n        const integer_t count_subs1 = subtract_saturate_epu8(count, one);\n        integer_t counts = add_epi8(count, shift_right_by_one_byte(count_subs1));\n        // shift 03 and 04 bytes by 2 and subtract 2\n        counts = add_epi8(counts, shift_right_by_two_bytes(subtract_saturate_epu8(counts, two)));\n        // counts now contains the number of bytes remaining in each utf-8 sequence of 2 or more bytes\n        debug_register(counts);\n        // check for an incomplete trailing utf8 sequence\n        if (check_for_trailing_bytes && !is_zero(cmplt_epi8(one, and_si(counts, cmpeq_epi8(numbered, set1_epi8(chunk_src_sz - 1)))))) {\n            // The value of counts at the last byte is > 1 indicating we have a trailing incomplete sequence\n            check_for_trailing_bytes = false;\n            if (start_of_current_chunk[chunk_src_sz-1] >= 0xc0) num_of_trailing_bytes = 1;      // 2-, 3- and 4-byte characters with only 1 byte left\n            else if (chunk_src_sz > 1 && start_of_current_chunk[chunk_src_sz-2] >= 0xe0) num_of_trailing_bytes = 2; // 3- and 4-byte characters with only 1 byte left\n            else if (chunk_src_sz > 2 && start_of_current_chunk[chunk_src_sz-3] >= 0xf0) num_of_trailing_bytes = 3; // 4-byte characters with only 3 bytes left\n            chunk_src_sz -= num_of_trailing_bytes;\n            d->num_consumed -= num_of_trailing_bytes;\n            if (!chunk_src_sz) { abort_with_invalid_utf8(); }\n            vec = zero_last_n_bytes(vec, sizeof(integer_t) - chunk_src_sz);\n            goto start_classification;\n        }\n\n        // The next section performs detailed validation of the chunk's byte sequences.\n        // It accumulates validation errors into a chunk_is_invalid vector.\n        // When chunk_is_invalid has any non-zero byte, then the chunk contains invalid UTF-8.\n        // chunk_is_invalid is a vector, and not a bitmask or boolean,\n        // because the or_si SIMD operation is empirically faster than movemask_epi8 with |= or ||=.\n        integer_t chunk_is_invalid;\n\n        // Only bytes within the ASCII range should have counts[i] == 0, and vice versa.\n        // Detect any mismatch between the two conditions for each chunk byte.\n        // If there is any mismatch, then the chunk has invalid UTF-8, so set all bytes in chunk_is_invalid to 0xFF;\n        // otherwise the chunk might be valid, so set all bytes in chunk_is_invalid to 0x00.\n        // Without this, \"\\x80\" would incorrectly be decoded as a \"\\x00\".\n        // This also validates that continuation bytes' positions do not have ASCII bytes (< 0x80).\n        // Without this, \"\\xe0\\xa0\\x7f\\x01\" would incorrectly be decoded as \"\\x00\\x01\".\n        // In that example, 0x7F has an ascii_mask bit of 0 (i.e., it is within 0x00..0x7F),\n        // but it has a counts value of 1, not 0 (i.e., it is the last remaining byte of a multi-byte sequence).\n        // Therefore there is a count mismatch, indicating that the chunk is ill-formed UTF-8.\n        // (If the following \"\\x01\" were absent, and the \"\\x7f\" were the last byte of the chunk,\n        // then the `check_for_trailing_bytes` validation above detects the error as a trailing incomplete sequence.)\n        const int ascii_sequence_count_mismatches = ascii_mask ^ movemask_epi8(cmpgt_epi8(counts, zero));\n        chunk_is_invalid = set1_epi8(ascii_sequence_count_mismatches ? 0xff : 0x00);\n\n        // Validate 2-byte sequence starter bytes: 0xC0..0xC1 are invalid (overlong encodings for U+0000..U+007F).\n        // Without this, \"\\xc0\\x80\" would incorrectly be decoded as a \"\\x00\".\n        chunk_is_invalid = or_si(chunk_is_invalid, and_si(bytes_indicating_start_of_two_byte_sequence, cmplt_epi8(vec, set1_epi8(0xc2))));\n\n        // Validate 4-byte sequence starter bytes: 0xF5..0xFF are invalid (out of Unicode codespace).\n        // Without this, \"\\xff\\x80\\x80\\x80\" would incorrectly be decoded as an ill-formed \"\\U003C0000\".\n        chunk_is_invalid = or_si(chunk_is_invalid, and_si(bytes_indicating_start_of_four_byte_sequence, cmpgt_epi8(vec, set1_epi8(0xf4))));\n\n        // Validate that all continuation bytes' positions do not have non-ASCII starter bytes (>=0xC0).\n        // If counts[i] > count[i], the chunk byte at i is in the middle of a previous sequence but also classified as a starter byte.\n        // Without this, \"\\xf0\\x90\\xc2\\x80\" would have overlapping sequences, and it would be incorrectly decoded elsewhere as an empty string.\n        chunk_is_invalid = or_si(chunk_is_invalid, andnot_si(cmplt_epi8(vec, set1_epi8(0xc0)), cmpgt_epi8(counts, count)));\n\n        // Validate second bytes of E0-starting 3-byte sequences.\n        // 0xE0 must be followed by 0xA0..0xBF (not 0x80..0x9F) to avoid overlong encodings.\n        // Without this, \"\\xe0\\x80\\x80\" would incorrectly be decoded as a \"\\x00\".\n        const integer_t e0_starter_bytes = cmpeq_epi8(vec, set1_epi8(0xe0));\n        const integer_t e0_first_follower_bytes = shift_right_by_one_byte(e0_starter_bytes);\n        chunk_is_invalid = or_si(chunk_is_invalid, and_si(e0_first_follower_bytes, cmplt_epi8(and_si(e0_first_follower_bytes, vec), set1_epi8(0xa0))));\n\n        // Validate second bytes of ED-starting 3-byte sequences.\n        // 0xED must be followed by 0x80..0x9F (not 0xA0..0xBF) to avoid UTF-16 surrogates.\n        // Without this, \"\\xed\\xa0\\x80\" would incorrectly be decoded as an isolated surrogate \"\\uD800\".\n        const integer_t ed_starter_bytes = cmpeq_epi8(vec, set1_epi8(0xed));\n        const integer_t ed_first_follower_bytes = shift_right_by_one_byte(ed_starter_bytes);\n        chunk_is_invalid = or_si(chunk_is_invalid, and_si(ed_first_follower_bytes, cmpgt_epi8(and_si(ed_first_follower_bytes, vec), set1_epi8(0x9f))));\n\n        // Validate second bytes of F0-starting 4-byte sequences.\n        // F0 must be followed by 0x90..0xBF (not 0x80..0x8F) to avoid overlong encodings.\n        // Without this, \"\\xf0\\x80\\x80\\x80\" would incorrectly be decoded as a \"\\x0000\".\n        const integer_t f0_starter_bytes = cmpeq_epi8(vec, set1_epi8(0xf0));\n        const integer_t f0_first_follower_bytes = shift_right_by_one_byte(f0_starter_bytes);\n        chunk_is_invalid = or_si(chunk_is_invalid, and_si(f0_first_follower_bytes, cmplt_epi8(and_si(f0_first_follower_bytes, vec), set1_epi8(0x90))));\n\n        // Validate second bytes of F4-starting 4-byte sequences.\n        // F4 must be followed by 0x80..0x8F (not 0x90..0xBF) to stay within the Unicode codespace.\n        // Without this, \"\\xf4\\x90\\x80\\x80\" would incorrectly be decoded as an ill-formed \"\\U00110000\".\n        const integer_t f4_starter_bytes = cmpeq_epi8(vec, set1_epi8(0xf4));\n        const integer_t f4_first_follower_bytes = shift_right_by_one_byte(f4_starter_bytes);\n        chunk_is_invalid = or_si(chunk_is_invalid, and_si(f4_first_follower_bytes, cmpgt_epi8(and_si(f4_first_follower_bytes, vec), set1_epi8(0x8f))));\n\n        // Check for any accumulated validation errors and, if found,\n        // fall back to slow scalar decoding of this chunk,\n        // which handles replacement of invalid sequences with U+FFFD\n        if (!is_zero(chunk_is_invalid)) { abort_with_invalid_utf8(); }\n\n        // Process the bytes storing the three resulting bytes that make up the unicode codepoint\n        // mask all control bits so that we have only useful bits left\n        vec = andnot_si(mask, vec);\n        debug_register(vec);\n\n        // Now calculate the three output vectors\n\n        // The lowest byte is made up of 6 bits from locations with counts == 1 and the lowest two bits from locations with count == 2\n        // In addition, the ASCII bytes are copied unchanged from vec\n        const integer_t vec_non_ascii = andnot_si(cmpeq_epi8(counts, zero), vec);\n        debug_register(vec_non_ascii);\n        integer_t output1 = blendv_epi8(vec,\n                or_si(\n                    // there are no count == 1 locations without a count == 2 location to its left so we dont need to AND with count2_locations\n                    vec, and_si(shift_left_by_bits16(shift_right_by_one_byte(vec_non_ascii), 6), set1_epi8(0xc0))\n                ),\n                cmpeq_epi8(counts, one)\n        );\n        debug_register(output1);\n\n        // The next byte is made up of 4 bits (5, 4, 3, 2) from locations with count == 2 and the first 4 bits from locations with count == 3\n        const integer_t count2_locations = cmpeq_epi8(counts, two), count3_locations = cmpeq_epi8(counts, three);\n        integer_t output2 = and_si(vec, count2_locations);\n        output2 = shift_right_by_bits32(output2, 2);  // selects the bits 5, 4, 3, 2\n        // select the first 4 bits from locs with count == 3 by shifting count 3 locations right by one byte and left by 4 bits\n        output2 = or_si(output2,\n            and_si(set1_epi8(0xf0),\n                shift_left_by_bits16(shift_right_by_one_byte(and_si(count3_locations, vec_non_ascii)), 4)\n            )\n        );\n        output2 = and_si(output2, count2_locations); // keep only the count2 bytes\n        output2 = shift_right_by_one_byte(output2);\n        debug_register(output2);\n\n        // The last byte is made up of bits 5 and 6 from count == 3 and 3 bits from count == 4\n        integer_t output3 = and_si(three, shift_right_by_bits32(vec, 4));  // bits 5 and 6 from count == 3\n        const integer_t count4_locations = cmpeq_epi8(counts, four);\n        // 3 bits from count == 4 locations, placed at count == 3 locations shifted left by 2 bits\n        output3 = or_si(output3,\n            and_si(set1_epi8(0xfc),\n                shift_left_by_bits16(shift_right_by_one_byte(and_si(count4_locations, vec_non_ascii)), 2)\n            )\n        );\n        output3 = and_si(output3, count3_locations);  // keep only count3 bytes\n        output3 = shift_right_by_two_bytes(output3);\n        debug_register(output3);\n\n        // Shuffle bytes to remove continuation bytes\n        integer_t shifts = count_subs1;  // number of bytes we need to skip for each UTF-8 sequence\n        // propagate the shifts to all subsequent bytes by shift and add\n        shifts = add_epi8(shifts, shift_right_by_one_byte(shifts));\n        shifts = add_epi8(shifts, shift_right_by_two_bytes(shifts));\n        shifts = add_epi8(shifts, shift_right_by_four_bytes(shifts));\n        shifts = add_epi8(shifts, shift_right_by_eight_bytes(shifts));\n#if KITTY_SIMD_LEVEL == 256\n        shifts = add_epi8(shifts, shift_right_by_sixteen_bytes(shifts));\n#endif\n        // zero the shifts for discarded continuation bytes\n        shifts = and_si(shifts, cmplt_epi8(counts, two));\n        // now we need to convert shifts into a mask for the shuffle. The mask has each byte of the\n        // form 0000xxxx the lower four bits indicating the destination location for the byte. For 256 bit shuffle we use lower 5 bits.\n        // First we move the numbers in shifts to discard the unwanted UTF-8 sequence bytes. We note that the numbers\n        // are bounded by sizeof(integer_t) and so we need at most 4 (for 128 bit) or 5 (for 256 bit) moves. The numbers are\n        // monotonic from left to right and change value only at the end of a UTF-8 sequence. We move them leftwards, accumulating the\n        // moves bit-by-bit.\n#define move(shifts, amt, which_bit) blendv_epi8(shifts, shift_left_by_##amt(shifts), shift_left_by_##amt(shift_left_by_bits16(shifts, 8 - which_bit)))\n        shifts = move(shifts, one_byte, 1);\n        shifts = move(shifts, two_bytes, 2);\n        shifts = move(shifts, four_bytes, 3);\n        shifts = move(shifts, eight_bytes, 4);\n#if KITTY_SIMD_LEVEL == 256\n        shifts = move(shifts, sixteen_bytes, 5);\n#endif\n#undef move\n        // convert the shifts into a suitable mask for shuffle by adding the byte number to each byte\n        shifts = add_epi8(shifts, numbered);\n        debug_register(shifts);\n\n        output1 = shuffle_epi8(output1, shifts);\n        output2 = shuffle_epi8(output2, shifts);\n        output3 = shuffle_epi8(output3, shifts);\n        debug_register(output1);\n        debug_register(output2);\n        debug_register(output3);\n\n        const unsigned num_of_discarded_bytes = sum_bytes(count_subs1);\n        const unsigned num_codepoints = chunk_src_sz - num_of_discarded_bytes;\n        debug(\"num_of_discarded_bytes: %u num_codepoints: %u\\n\", num_of_discarded_bytes, num_codepoints);\n        FUNC(output_unicode)(d, output1, output2, output3, num_codepoints);\n        handle_trailing_bytes();\n    }\n#ifdef compare_with_scalar\n    if (debugdec.output.pos != d->output.pos || debugdec.num_consumed != d->num_consumed ||\n        memcmp(d->output.storage, debugdec.output.storage, d->output.pos * sizeof(d->output.storage[0])) != 0 ||\n        sentinel_found != scalar_sentinel_found || debugdec.state.cur != d->state.cur\n    ) {\n        fprintf(stderr, \"vector decode output differs from scalar: input_sz=%zu consumed=(%u %u) output_sz=(%u %u) sentinel=(%d %d) state_changed: %d output_different: %d\\n\",\n                src_len, debugdec.num_consumed, d->num_consumed, debugdec.output.pos, d->output.pos, scalar_sentinel_found, sentinel_found,\n                debugdec.state.cur != d->state.cur,\n                memcmp(d->output.storage, debugdec.output.storage, MIN(d->output.pos, debugdec.output.pos) * sizeof(d->output.storage[0]))\n        );\n        fprintf(stderr, \"\\\"\");\n        for (unsigned i = 0; i < src_len; i++) {\n            if (32 <= src_data[i] && src_data[i] < 0x7f && src_data[i] != '\"') fprintf(stderr, \"%c\", src_data[i]);\n            else fprintf(stderr, \"\\\\x%x\", src_data[i]);\n        }\n        fprintf(stderr, \"\\\"\\n\");\n    }\n    utf8_decoder_free(&debugdec);\n#endif\n    zero_upper();\n    return sentinel_found;\n#undef abort_with_invalid_utf8\n#undef handle_trailing_bytes\n}\n\n\n#undef FUNC\n#undef integer_t\n#undef set1_epi8\n#undef set_epi8\n#undef load_unaligned\n#undef load_aligned\n#undef store_unaligned\n#undef store_aligned\n#undef cmpeq_epi8\n#undef cmplt_epi8\n#undef cmpgt_epi8\n#undef or_si\n#undef and_si\n#undef xor_si\n#undef andnot_si\n#undef movemask_epi8\n#undef CONCAT\n#undef CONCAT_EXPAND\n#undef KITTY_SIMD_LEVEL\n#undef shift_right_by_one_byte\n#undef shift_right_by_two_bytes\n#undef shift_right_by_four_bytes\n#undef shift_right_by_eight_bytes\n#undef shift_right_by_sixteen_bytes\n#undef shift_left_by_one_byte\n#undef shift_left_by_two_bytes\n#undef shift_left_by_four_bytes\n#undef shift_left_by_eight_bytes\n#undef shift_left_by_sixteen_bytes\n#undef shift_left_by_bits16\n#undef shift_right_by_bits32\n#undef shift_right_by_bytes128\n#undef extract_lower_quarter_as_chars\n#undef extract_lower_half_as_chars\n#undef blendv_epi8\n#undef add_epi8\n#undef subtract_saturate_epu8\n#undef subtract_epi8\n#undef create_zero_integer\n#undef create_all_ones_integer\n#undef shuffle_epi8\n#undef numbered_bytes\n#undef reverse_numbered_bytes\n#undef sum_bytes\n#undef is_zero\n#undef zero_upper\n#undef print_register_as_bytes\n#endif // KITTY_NO_SIMD\n"
  },
  {
    "path": "kitty/simd-string.c",
    "content": "/*\n * simd-string.c\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"charsets.h\"\n#include \"simd-string.h\"\nstatic bool has_sse4_2 = false, has_avx2 = false;\n\n// xor_data64 {{{\nstatic void xor_data64_scalar(const uint8_t key[64], uint8_t* data, const size_t data_sz) { for (size_t i = 0; i < data_sz; i++) data[i] ^= key[i & 63]; }\nstatic void (*xor_data64_impl)(const uint8_t key[64], uint8_t* data, const size_t data_sz) = xor_data64_scalar;\nvoid xor_data64(const uint8_t key[64], uint8_t* data, const size_t data_sz) { xor_data64_impl(key, data, data_sz); }\n// }}}\n\n// find_either_of_two_bytes {{{\nstatic const uint8_t*\nfind_either_of_two_bytes_scalar(const uint8_t *haystack, const size_t sz, const uint8_t x, const uint8_t y) {\n    for (const uint8_t *limit = haystack + sz; haystack < limit; haystack++) {\n        if (*haystack == x || *haystack == y) return haystack;\n    }\n    return NULL;\n}\n\nstatic const uint8_t* (*find_either_of_two_bytes_impl)(const uint8_t*, const size_t, const uint8_t, const uint8_t) = find_either_of_two_bytes_scalar;\n\nconst uint8_t*\nfind_either_of_two_bytes(const uint8_t *haystack, const size_t sz, const uint8_t a, const uint8_t b) {\n    return (uint8_t*)find_either_of_two_bytes_impl(haystack, sz, a, b);\n}\n// }}}\n\n// UTF-8 {{{\n\nbool\nutf8_decode_to_esc_scalar(UTF8Decoder *d, const uint8_t *src, const size_t src_sz) {\n    d->output.pos = 0; d->num_consumed = 0;\n    utf8_decoder_ensure_capacity(d, src_sz);\n    while (d->num_consumed < src_sz) {\n        const uint8_t ch = src[d->num_consumed++];\n        if (ch == 0x1b) {\n            if (d->state.cur != UTF8_ACCEPT) d->output.storage[d->output.pos++] = 0xfffd;\n            zero_at_ptr(&d->state);\n            return true;\n        } else {\n            switch(decode_utf8(&d->state.cur, &d->state.codep, ch)) {\n                case UTF8_ACCEPT:\n                    d->output.storage[d->output.pos++] = d->state.codep;\n                    break;\n                case UTF8_REJECT: {\n                    const bool prev_was_accept = d->state.prev == UTF8_ACCEPT;\n                    zero_at_ptr(&d->state);\n                    d->output.storage[d->output.pos++] = 0xfffd;\n                    if (!prev_was_accept && d->num_consumed) {\n                        d->num_consumed--;\n                        continue; // so that prev is correct\n                    }\n                } break;\n            }\n        }\n        d->state.prev = d->state.cur;\n    }\n    return false;\n}\n\nstatic bool (*utf8_decode_to_esc_impl)(UTF8Decoder *d, const uint8_t *src, size_t src_sz) = utf8_decode_to_esc_scalar;\n\nbool\nutf8_decode_to_esc(UTF8Decoder *d, const uint8_t *src, size_t src_sz) {\n    return utf8_decode_to_esc_impl(d, src, src_sz);\n}\n\n// }}}\n\n// Boilerplate {{{\nstatic PyObject*\ntest_utf8_decode_to_sentinel(PyObject *self UNUSED, PyObject *args) {\n    const uint8_t *src; Py_ssize_t src_sz;\n    int which_function = 0;\n    static UTF8Decoder d = {0};\n    if (!PyArg_ParseTuple(args, \"s#|i\", &src, &src_sz, &which_function)) return NULL;\n    bool found_sentinel = false;\n    bool(*func)(UTF8Decoder*, const uint8_t*, size_t sz) = utf8_decode_to_esc;\n    switch (which_function) {\n        case -1:\n            zero_at_ptr(&d); Py_RETURN_NONE;\n        case 1:\n            func = utf8_decode_to_esc_scalar; break;\n        case 2:\n            func = utf8_decode_to_esc_128; break;\n        case 3:\n            func = utf8_decode_to_esc_256; break;\n    }\n    RAII_PyObject(ans, PyUnicode_FromString(\"\"));\n    ssize_t p = 0;\n    while (p < src_sz && !found_sentinel) {\n        found_sentinel = func(&d, src + p, src_sz - p);\n        p += d.num_consumed;\n        if (d.output.pos) {\n            RAII_PyObject(temp, PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, d.output.storage, d.output.pos));\n            PyObject *t = PyUnicode_Concat(ans, temp);\n            Py_DECREF(ans);\n            ans = t;\n        }\n    }\n    utf8_decoder_free(&d);\n    return Py_BuildValue(\"OOi\", found_sentinel ? Py_True : Py_False, ans, p);\n}\n\nstatic PyObject*\ntest_find_either_of_two_bytes(PyObject *self UNUSED, PyObject *args) {\n    RAII_PY_BUFFER(buf);\n    int which_function = 0, align_offset = 0;\n    const uint8_t*(*func)(const uint8_t*, const size_t sz, const uint8_t, const uint8_t) = find_either_of_two_bytes;\n    unsigned char a, b;\n    if (!PyArg_ParseTuple(args, \"s*BB|ii\", &buf, &a, &b, &which_function, &align_offset)) return NULL;\n    switch (which_function) {\n        case 1:\n            func = find_either_of_two_bytes_scalar; break;\n        case 2:\n            func = find_either_of_two_bytes_128; break;\n        case 3:\n            func = find_either_of_two_bytes_256; break;\n        case 0: break;\n        default:\n            PyErr_SetString(PyExc_ValueError, \"Unknown which_function\");\n            return NULL;\n    }\n    uint8_t *abuf;\n    if (posix_memalign((void**)&abuf, 64, 256 + buf.len) != 0) {\n        return PyErr_NoMemory();\n    }\n    uint8_t *p = abuf;\n    memset(p, '<', 64 + align_offset); p += 64 + align_offset;\n    memcpy(p, buf.buf, buf.len);\n    memset(p + buf.len, '>', 64);\n    const uint8_t *ans = func(p, buf.len, a, b);\n    free(abuf);\n    if (ans == NULL) return PyLong_FromLong(-1);\n    unsigned long long n = ans - p;\n    return PyLong_FromUnsignedLongLong(n);\n}\n\nstatic PyObject*\ntest_xor64(PyObject *self UNUSED, PyObject *args) {\n    RAII_PY_BUFFER(buf);\n    RAII_PY_BUFFER(key);\n    int which_function = 0, align_offset = 0;\n    void (*func)(const uint8_t key[64], uint8_t* data, const size_t data_sz) = xor_data64;\n    if (!PyArg_ParseTuple(args, \"s*s*|ii\", &key, &buf, &which_function, &align_offset)) return NULL;\n    switch (which_function) {\n        case 1:\n            func = xor_data64_scalar; break;\n        case 2:\n            func = xor_data64_128; break;\n        case 3:\n            func = xor_data64_256; break;\n        case 0: break;\n        default:\n            PyErr_SetString(PyExc_ValueError, \"Unknown which_function\");\n            return NULL;\n    }\n    uint8_t *abuf;\n    if (posix_memalign((void**)&abuf, 64, 256 + buf.len) != 0) {\n        return PyErr_NoMemory();\n    }\n    uint8_t *p = abuf;\n    memset(p, '<', 64 + align_offset); p += 64 + align_offset;\n    memcpy(p, buf.buf, buf.len);\n    memset(p + buf.len, '>', 64);\n    func(key.buf, p, buf.len);\n    PyObject *ans = NULL;\n    for (int i = 0; i < 64 + align_offset; i++) if (abuf[i] != '<') { PyErr_SetString(PyExc_SystemError, \"xor wrote before start of data region\"); }\n    for (int i = 0; i < 64; i++) if (p[i + buf.len] != '>') { PyErr_SetString(PyExc_SystemError, \"xor wrote after end of data region\"); }\n    if (!PyErr_Occurred()) ans = PyBytes_FromStringAndSize((const char*)p, buf.len);\n    free(abuf);\n    return ans;\n}\n\n\n// }}}\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(test_utf8_decode_to_sentinel, METH_VARARGS),\n    METHODB(test_find_either_of_two_bytes, METH_VARARGS),\n    METHODB(test_xor64, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nbool\ninit_simd(void *x) {\n    PyObject *module = (PyObject*)x;\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n#define A(x, val) { Py_INCREF(Py_##val); if (0 != PyModule_AddObject(module, #x, Py_##val)) return false; }\n#define do_check() { has_sse4_2 = __builtin_cpu_supports(\"sse4.2\") != 0; has_avx2 = __builtin_cpu_supports(\"avx2\") != 0; }\n\n#ifdef __APPLE__\n#ifdef __arm64__\n    // simde takes care of NEON on Apple Silicon\n    // ARM has only 128 bit registers but using the avx2 code is still slightly faster\n    has_sse4_2 = true; has_avx2 = true;\n#else\n    do_check();\n    // On GitHub actions there are some weird macOS machines which report avx2 not available but sse4.2 is available and then\n    // SIGILL when using basic sse instructions\n    if (!has_avx2 && has_sse4_2) {\n        const char *ci = getenv(\"CI\");\n        if (ci && strcmp(ci, \"true\") == 0) has_sse4_2 = false;\n    }\n#endif\n#else\n#ifdef __aarch64__\n    // no idea how to probe ARM cpu for NEON support. This file uses pretty\n    // basic AVX2 and SSE4.2 intrinsics, so hopefully they work on ARM\n    // ARM has only 128 bit registers but using the avx2 code is still slightly faster\n    has_sse4_2 = true; has_avx2 = true;\n#elif !defined(KITTY_NO_SIMD)\n    do_check();\n#endif\n#endif\n    const char *simd_env = getenv(\"KITTY_SIMD\");\n    if (simd_env) {\n        has_sse4_2 = strcmp(simd_env, \"128\") == 0;\n        has_avx2 = strcmp(simd_env, \"256\") == 0;\n    }\n\n#undef do_check\n    if (has_avx2) {\n        A(has_avx2, True);\n        find_either_of_two_bytes_impl = find_either_of_two_bytes_256;\n        utf8_decode_to_esc_impl = utf8_decode_to_esc_256;\n        xor_data64_impl = xor_data64_256;\n    } else {\n        A(has_avx2, False);\n    }\n    if (has_sse4_2) {\n        A(has_sse4_2, True);\n        if (find_either_of_two_bytes_impl == find_either_of_two_bytes_scalar) find_either_of_two_bytes_impl = find_either_of_two_bytes_128;\n        if (utf8_decode_to_esc_impl == utf8_decode_to_esc_scalar) utf8_decode_to_esc_impl = utf8_decode_to_esc_128;\n        if (xor_data64_impl == xor_data64_scalar) xor_data64_impl = xor_data64_128;\n    } else {\n        A(has_sse4_2, False);\n    }\n#undef A\n    return true;\n}\n"
  },
  {
    "path": "kitty/simd-string.h",
    "content": "/*\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n#include <stddef.h>\n#include <stdalign.h>\n\ntypedef void (*control_byte_callback)(void *data, uint8_t ch);\ntypedef void (*output_chars_callback)(void *data, const uint32_t *chars, unsigned count);\n\ntypedef struct UTF8Decoder {\n    struct { uint32_t *storage; unsigned pos, capacity; } output;\n    struct { uint32_t cur, prev, codep; } state;\n    unsigned num_consumed;\n} UTF8Decoder;\n\nstatic inline void utf8_decoder_reset(UTF8Decoder *self) { zero_at_ptr(&self->state); }\n\nbool utf8_decode_to_esc(UTF8Decoder *d, const uint8_t *src, size_t src_sz);\nbool utf8_decode_to_esc_scalar(UTF8Decoder *d, const uint8_t *src, const size_t src_sz);\n\nstatic inline void utf8_decoder_ensure_capacity(UTF8Decoder *d, unsigned sz) {\n    if (d->output.pos + sz > d->output.capacity) {\n        d->output.capacity = d->output.pos + sz + 4096;\n        // allow for overwrite of upto 64 bytes\n        d->output.storage = realloc(d->output.storage, d->output.capacity * sizeof(d->output.storage[0]) + 64);\n        if (!d->output.storage) fatal(\"Out of memory for UTF8Decoder output buffer at capacity: %u\", d->output.capacity);\n    }\n}\n\nstatic inline void utf8_decoder_free(UTF8Decoder *d) {\n    free(d->output.storage);\n    zero_at_ptr(&(d->output));\n}\n\n\n// Pass a PyModule PyObject* as the argument. Must be called once at application startup\nbool init_simd(void* module);\n\n// Returns pointer to first position in haystack that contains either of the\n// two chars or NULL if not found.\nconst uint8_t* find_either_of_two_bytes(const uint8_t *haystack, const size_t sz, const uint8_t a, const uint8_t b);\n\n// XOR data with the 64 byte key\nvoid xor_data64(const uint8_t key[64], uint8_t* data, const size_t data_sz);\n\n// SIMD implementations, internal use\nbool utf8_decode_to_esc_128(UTF8Decoder *d, const uint8_t *src, size_t src_sz);\nbool utf8_decode_to_esc_256(UTF8Decoder *d, const uint8_t *src, size_t src_sz);\nconst uint8_t* find_either_of_two_bytes_128(const uint8_t *haystack, const size_t sz, const uint8_t a, const uint8_t b);\nconst uint8_t* find_either_of_two_bytes_256(const uint8_t *haystack, const size_t sz, const uint8_t a, const uint8_t b);\nvoid xor_data64_128(const uint8_t key[64], uint8_t* data, const size_t data_sz);\nvoid xor_data64_256(const uint8_t key[64], uint8_t* data, const size_t data_sz);\n"
  },
  {
    "path": "kitty/simple_cli_definitions.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\n# This module must be runnable by a vanilla python interpreter\n# as it is used to generate C code when building kitty\n\nimport re\nimport sys\nfrom enum import Enum, auto\nfrom functools import lru_cache\nfrom typing import Any, Iterator, NamedTuple, Sequence\n\nif getattr(sys, 'running_from_setup', False):\n    is_macos = 'darwin' in sys.platform.lower()\n    from shlex import split as psplit\n\n    def shlex_split(text: str) -> Iterator[str]:\n        yield from psplit(text)\nelse:\n    from kitty.constants import appname, is_macos\n    from kitty.utils import shlex_split as ksplit\n    def shlex_split(text: str) -> Iterator[str]:\n        yield from ksplit(text)\n\n\ndef serialize_as_go_string(x: str) -> str:\n    return x.replace('\\\\', '\\\\\\\\').replace('\\n', '\\\\n').replace('\"', '\\\\\"')\n\n\nclass CompletionType(Enum):\n    file = auto()\n    directory = auto()\n    keyword = auto()\n    special = auto()\n    none = auto()\n\n\nclass CompletionRelativeTo(Enum):\n    cwd = auto()\n    config_dir = auto()\n\n\nclass CompletionSpec(NamedTuple):\n\n    type: CompletionType = CompletionType.none\n    kwds: tuple[str,...] = ()\n    extensions: tuple[str,...] = ()\n    mime_patterns: tuple[str,...] = ()\n    group: str = ''\n    relative_to: CompletionRelativeTo = CompletionRelativeTo.cwd\n\n    @staticmethod\n    def from_string(raw: str) -> 'CompletionSpec':\n        typ = CompletionType.none\n        kwds: tuple[str, ...] = ()\n        extensions: tuple[str, ...] = ()\n        mime_patterns: tuple[str, ...] = ()\n        group = ''\n        relative_to = CompletionRelativeTo.cwd\n        for x in shlex_split(raw):\n            ck, vv = x.split(':', 1)\n            if ck == 'type':\n                typ = getattr(CompletionType, vv)\n            elif ck == 'kwds':\n                kwds += tuple(vv.split(','))\n            elif ck == 'ext':\n                extensions += tuple(vv.split(','))\n            elif ck == 'group':\n                group = vv\n            elif ck == 'mime':\n                mime_patterns += tuple(vv.split(','))\n            elif ck == 'relative':\n                if vv == 'conf':\n                    relative_to = CompletionRelativeTo.config_dir\n                else:\n                    raise ValueError(f'Unknown completion relative to value: {vv}')\n            else:\n                raise KeyError(f'Unknown completion property: {ck}')\n        return CompletionSpec(\n            type=typ, kwds=kwds, extensions=extensions, mime_patterns=mime_patterns, group=group, relative_to=relative_to)\n\n    def as_go_code(self, go_name: str, sep: str = ': ') -> Iterator[str]:\n        completers = []\n        if self.kwds:\n            kwds = (f'\"{serialize_as_go_string(x)}\"' for x in self.kwds)\n            g = (self.group if self.type is CompletionType.keyword else '') or \"Keywords\"\n            completers.append(f'cli.NamesCompleter(\"{serialize_as_go_string(g)}\", ' + ', '.join(kwds) + ')')\n        relative_to = 'CONFIG' if self.relative_to is CompletionRelativeTo.config_dir else 'CWD'\n        if self.type is CompletionType.file:\n            g = serialize_as_go_string(self.group or 'Files')\n            added = False\n            if self.extensions:\n                added = True\n                pats = (f'\"*.{ext}\"' for ext in self.extensions)\n                completers.append(f'cli.FnmatchCompleter(\"{g}\", cli.{relative_to}, ' + ', '.join(pats) + ')')\n            if self.mime_patterns:\n                added = True\n                completers.append(f'cli.MimepatCompleter(\"{g}\", cli.{relative_to}, ' + ', '.join(f'\"{p}\"' for p in self.mime_patterns) + ')')\n            if not added:\n                completers.append(f'cli.FnmatchCompleter(\"{g}\", cli.{relative_to}, \"*\")')\n        if self.type is CompletionType.directory:\n            g = serialize_as_go_string(self.group or 'Directories')\n            completers.append(f'cli.DirectoryCompleter(\"{g}\", cli.{relative_to})')\n        if self.type is CompletionType.special:\n            completers.append(self.group)\n        if len(completers) > 1:\n            yield f'{go_name}{sep}cli.ChainCompleters(' + ', '.join(completers) + ')'\n        elif completers:\n            yield f'{go_name}{sep}{completers[0]}'\n\n\nclass OptionDefinition(NamedTuple):\n    dest: str = ''\n    name: str = ''\n    aliases: tuple[str, ...] = ()\n    help: str = ''\n    choices: tuple[str, ...] = ()\n    type: str = ''\n    default: str | None = None\n    condition: bool = False\n    completion: CompletionSpec = CompletionSpec()\n\n\n\nOptionSpecSeq = Sequence[str | OptionDefinition]\n\n\n@lru_cache(64)\ndef parse_option_spec(spec: str | None = None) -> tuple[OptionSpecSeq, OptionSpecSeq]:\n    if spec is None:\n        spec = kitty_options_spec()\n    NORMAL, METADATA, HELP = 'NORMAL', 'METADATA', 'HELP'\n    state = NORMAL\n    lines = spec.splitlines()\n    prev_line = ''\n    prev_indent = 0\n    seq: list[str | OptionDefinition] = []\n    disabled: list[str | OptionDefinition] = []\n    mpat = re.compile('([a-z]+)=(.+)')\n    current_cmd = empty_cmd = OptionDefinition()\n\n    def indent_of_line(x: str) -> int:\n        return len(x) - len(x.lstrip())\n\n    for line in lines:\n        line = line.rstrip()\n        if state is NORMAL:\n            if not line:\n                continue\n            if line.startswith('# '):\n                seq.append(line[2:])\n                continue\n            if line.startswith('--'):\n                parts = line.split(' ')\n                defdest = parts[0][2:].replace('-', '_')\n                current_cmd = OptionDefinition(dest=defdest, aliases=tuple(parts), name=defdest, condition=True)\n                state = METADATA\n                continue\n            raise ValueError(f'Invalid option spec, unexpected line: {line}')\n        elif state is METADATA:\n            m = mpat.match(line)\n            if m is None:\n                state = HELP\n                current_cmd = current_cmd._replace(help=current_cmd.help + line)\n            else:\n                k, v = m.group(1), m.group(2)\n                if k == 'choices':\n                    vals = tuple(x.strip() for x in v.split(','))\n                    if not current_cmd.type:\n                        current_cmd = current_cmd._replace(type='choices')\n                    if current_cmd.type != 'choices':\n                        raise ValueError(f'Cannot specify choices for an option of type: {current_cmd.type}')\n                    current_cmd = current_cmd._replace(choices=tuple(vals))\n                    if current_cmd.default is None:\n                        current_cmd = current_cmd._replace(default=vals[0])\n                else:\n                    if k == 'default':\n                        current_cmd = current_cmd._replace(default=v)\n                    elif k == 'type':\n                        if v == 'choice':\n                            v = 'choices'\n                        current_cmd = current_cmd._replace(type=v)\n                    elif k == 'dest':\n                        current_cmd = current_cmd._replace(dest=v)\n                    elif k == 'condition':\n                        current_cmd = current_cmd._replace(condition=bool(eval(v)))\n                    elif k == 'completion':\n                        current_cmd = current_cmd._replace(completion=CompletionSpec.from_string(v))\n        elif state is HELP:\n            if line:\n                current_indent = indent_of_line(line)\n                if current_indent > 1:\n                    if prev_indent == 0:\n                        current_cmd = current_cmd._replace(help=current_cmd.help + '\\n')\n                    else:\n                        line = line.strip()\n                prev_indent = current_indent\n                spc = '' if current_cmd.help.endswith('\\n') else ' '\n                current_cmd = current_cmd._replace(help=current_cmd.help + spc + line)\n            else:\n                prev_indent = 0\n                if prev_line:\n                    h = '\\n' if current_cmd.help.endswith('::') else '\\n\\n'\n                    current_cmd = current_cmd._replace(help=current_cmd.help + h)\n                else:\n                    state = NORMAL\n                    (seq if current_cmd.condition else disabled).append(current_cmd)\n                    current_cmd = empty_cmd\n        prev_line = line\n    if current_cmd is not empty_cmd:\n        (seq if current_cmd.condition else disabled).append(current_cmd)\n\n    return seq, disabled\n\n\ndef defval_for_opt(opt: OptionDefinition) -> Any:\n    dv: Any = opt.default\n    typ = opt.type\n    if typ.startswith('bool-'):\n        if dv is None:\n            dv = False if typ == 'bool-set' else True\n        else:\n            dv = dv.lower() in ('true', 'yes', 'y')\n    elif typ == 'list':\n        dv = list(shlex_split(dv)) if dv else []\n    elif typ in ('int', 'float'):\n        dv = (int if typ == 'int' else float)(dv or 0)\n    return dv\n\n\ndef get_option_maps(seq: OptionSpecSeq) -> tuple[dict[str, OptionDefinition], dict[str, OptionDefinition], dict[str, Any]]:\n    names_map: dict[str, OptionDefinition] = {}\n    alias_map: dict[str, OptionDefinition] = {}\n    values_map: dict[str, Any] = {}\n    for opt in seq:\n        if isinstance(opt, str):\n            continue\n        for alias in opt.aliases:\n            alias_map[alias] = opt\n        name = opt.dest\n        names_map[name] = opt\n        values_map[name] = defval_for_opt(opt)\n    return names_map, alias_map, values_map\n\n\ndef c_str(x: str) -> str:\n    x = x.replace('\\\\', r'\\\\')\n    return f'\"{x}\"'\n\n\ndef add_list_values(*values: str) -> Iterator[str]:\n    yield f'\\tflag.defval.listval.items = alloc_for_cli(spec, {len(values)} * sizeof(flag.defval.listval.items[0]));'\n    yield '\\tif (!flag.defval.listval.items) OOM;'\n    yield f'\\tflag.defval.listval.count = {len(values)};'\n    yield f'\\tflag.defval.listval.capacity = {len(values)};'\n    for n, value in enumerate(values):\n        yield f'\\tflag.defval.listval.items[{n}] = {c_str(value)};'\n\n\ndef generate_c_for_opt(name: str, defval: Any, opt: OptionDefinition) -> Iterator[str]:\n    yield f'\\tflag = (FlagSpec){{.dest={c_str(name)},}};'\n    match opt.type:\n        case 'bool-set' | 'bool-reset':\n            yield '\\tflag.defval.type = CLI_VALUE_BOOL;'\n            yield f'\\tflag.defval.boolval = {\"true\" if defval else \"false\"};'\n        case 'int':\n            yield '\\tflag.defval.type = CLI_VALUE_INT;'\n            yield f'\\tflag.defval.intval = {defval};'\n        case 'float':\n            yield '\\tflag.defval.type = CLI_VALUE_FLOAT;'\n            yield f'\\tflag.defval.floatval = {defval};'\n        case 'list':\n            yield '\\tflag.defval.type = CLI_VALUE_LIST;'\n            if defval:\n                yield from add_list_values(*defval)\n        case 'choices':\n            yield '\\tflag.defval.type = CLI_VALUE_CHOICE;'\n            yield f'\\tflag.defval.strval = {c_str(defval)};'\n            yield from add_list_values(*opt.choices)\n        case _:\n            yield '\\tflag.defval.type = CLI_VALUE_STRING;'\n            if defval is not None:\n                yield f'\\tflag.defval.strval = {c_str(defval)};'\n\n\ndef generate_c_parser_for(funcname: str, spec: str) -> Iterator[str]:\n    seq, disabled = parse_option_spec(spec)\n    names_map, _, defaults_map = get_option_maps(seq)\n    if 'help' not in names_map:\n        names_map['help'] = OptionDefinition(type='bool-set', aliases=('--help', '-h'))\n        defaults_map['help'] = False\n    if 'version' not in names_map:\n        names_map['version'] = OptionDefinition(type='bool-set', aliases=('--version', '-v'))\n        defaults_map['version'] = False\n\n    yield f'static void\\nparse_cli_for_{funcname}(CLISpec *spec, int argc, char **argv) {{'  # }}\n    yield '\\tFlagSpec flag;'\n    for name, opt in names_map.items():\n        for alias in opt.aliases:\n            yield f'\\tif (vt_is_end(vt_insert(&spec->alias_map, {c_str(alias)}, {c_str(name)}))) OOM;'\n        yield from generate_c_for_opt(name, defaults_map[name], opt)\n        yield '\\tif (vt_is_end(vt_insert(&spec->flag_map, flag.dest, flag))) OOM;'\n    for d in disabled:\n        if not isinstance(d, str):\n            yield from generate_c_for_opt(d.dest, defval_for_opt(d), d)\n            yield '\\tif (vt_is_end(vt_insert(&spec->disabled_map, flag.dest, flag))) OOM;'\n\n    yield '\\tparse_cli_loop(spec, true, argc, argv);'\n    yield '}'\n\n\ndef generate_c_parsers() -> Iterator[str]:\n    yield '#pragma once'\n    yield '// generated by simple_cli_definitions.py do NOT edit!'\n    yield '#include \"cli-parser.h\"'\n    yield from generate_c_parser_for('kitty', kitty_options_spec())\n    yield ''\n    yield ''\n    yield from generate_c_parser_for('panel_kitten', build_panel_cli_spec({}))\n    yield ''\n\n\n# kitty CLI spec {{{\ngrab_keyboard_docs = \"\"\"\\\nGrab the keyboard. This means global shortcuts defined in the OS will be passed to kitty instead. Useful if\nyou want to create an OS modal window. How well this\nworks depends on the OS/window manager/desktop environment. On Wayland it works only if the compositor implements\nthe :link:`inhibit-keyboard-shortcuts protocol <https://wayland.app/protocols/keyboard-shortcuts-inhibit-unstable-v1>`.\nOn macOS Apple doesn't allow applications to grab the keyboard without special permissions, so it doesn't work.\n\"\"\"\n\nlisten_on_defn = f'''\\\n--listen-on\ncompletion=type:special group:complete_kitty_listen_on\nListen on the specified socket address for control messages. For example,\n:option:`{appname} --listen-on`=unix:/tmp/mykitty or :option:`{appname}\n--listen-on`=tcp:localhost:12345. On Linux systems, you can also use abstract\nUNIX sockets, not associated with a file, like this: :option:`{appname}\n--listen-on`=unix:@mykitty. Environment variables are expanded and relative\npaths are resolved with respect to the temporary directory. To control kitty,\nyou can send commands to it with :italic:`kitten @` using the\n:option:`kitten @ --to` option to specify this address. Note that if you run\n:italic:`kitten @` within a kitty window, there is no need to specify the\n:option:`kitten @ --to` option as it will automatically read from the\nenvironment. Note that this will be ignored unless :opt:`allow_remote_control`\nis set to either: :code:`yes`, :code:`socket` or :code:`socket-only`. This can\nalso be specified in :file:`kitty.conf`.\n'''\n\nwait_for_single_instance_defn = f'''\\\n--wait-for-single-instance-window-close\ntype=bool-set\nNormally, when using :option:`{appname} --single-instance`, :italic:`{appname}`\nwill open a new window in an existing instance and quit immediately. With this\noption, it will not quit till the newly opened window is closed. Note that if no\nprevious instance is found, then :italic:`{appname}` will wait anyway,\nregardless of this option.\n'''\n\nCONFIG_HELP = '''\\\nSpecify a path to the configuration file(s) to use. All configuration files are\nmerged onto the builtin :file:`{conf_name}.conf`, overriding the builtin values.\nThis option can be specified multiple times to read multiple configuration files\nin sequence, which are merged. Use the special value :code:`NONE` to not load\nany config file.\n\nIf this option is not specified, config files are searched for in the order:\n:file:`$XDG_CONFIG_HOME/{appname}/{conf_name}.conf`,\n:file:`~/.config/{appname}/{conf_name}.conf`,{macos_confpath}\n:file:`$XDG_CONFIG_DIRS/{appname}/{conf_name}.conf`. The first one that exists\nis used as the config file.\n\nIf the environment variable :envvar:`KITTY_CONFIG_DIRECTORY` is specified, that\ndirectory is always used and the above searching does not happen.\n\nIf :file:`/etc/xdg/{appname}/{conf_name}.conf` exists, it is merged before (i.e.\nwith lower priority) than any user config files. It can be used to specify\nsystem-wide defaults for all users. You can use either :code:`-` or\n:file:`/dev/stdin` to read the config from STDIN.\n'''.replace(\n    '{macos_confpath}',\n    (' :file:`~/Library/Preferences/{appname}/{conf_name}.conf`,' if is_macos else ''), 1\n)\n\n\ndef kitty_options_spec() -> str:\n    if not hasattr(kitty_options_spec, 'ans'):\n        OPTIONS = \"\"\"\n--class --app-id\ndest=cls\ndefault={appname}\ncondition=not is_macos\nOn Wayland set the :italic:`application id`. On X11 set the class part of the :italic:`WM_CLASS` window property.\n\n\n--name --os-window-tag\ncondition=not is_macos\nOn Wayland, set the :italic:`window tag`, when specified.\nOn X11, set the name part of the :italic:`WM_CLASS` property, when unset, defaults to using the\nvalue from :option:`{appname} --class`.\n\n\n--title -T\nSet the OS window title. This will override any title set by the program running\ninside kitty, permanently fixing the OS window's title. So only use this if you\nare running a program that does not set titles.\n\n\n--config -c\ntype=list\ncompletion=type:file ext:conf group:\"Config files\" kwds:none,NONE\n{config_help}\n\n\n--override -o\ntype=list\ncompletion=type:special group:complete_kitty_override\nOverride individual configuration options, can be specified multiple times.\nSyntax: :italic:`name=value`. For example: :option:`{appname} -o` font_size=20\n\n\n--directory --working-directory -d\ndefault=.\ncompletion=type:directory\nChange to the specified directory when launching.\n\n\n--detach\ntype=bool-set\nDetach from the controlling terminal, if any. On macOS\nuse :code:`open -a kitty.app -n` instead.\n\n\n--detached-log\nPath to a log file to store STDOUT/STDERR when using :option:`--detach`\n\n\n--session\ncompletion=type:file ext:session relative:conf group:\"Session files\"\nPath to a file containing the startup :italic:`session` (tabs, windows, layout,\nprograms). Use - to read from STDIN. See :ref:`sessions` for details and\nan example. Environment variables in the file name are expanded,\nrelative paths are resolved relative to the kitty configuration directory.\nThe special value :code:`none` means no session will be used, even if\nthe :opt:`startup_session` option has been specified in kitty.conf.\nNote that using this option means the command line arguments to kitty specifying\na program to run are ignored.\n\n\n--hold\ntype=bool-set\nRemain open, at a shell prompt, after child process exits. Note that this only\naffects the first window. You can quit by either using the close window\nshortcut or running the exit command.\n\n\n--single-instance -1\ntype=bool-set\nIf specified only a single instance of :italic:`{appname}` will run. New\ninvocations will instead create a new top-level window in the existing\n:italic:`{appname}` instance. This allows :italic:`{appname}` to share a single\nsprite cache on the GPU and also reduces startup time. You can also have\nseparate groups of :italic:`{appname}` instances by using the :option:`{appname}\n--instance-group` option.\n\n\n--instance-group\nUsed in combination with the :option:`{appname} --single-instance` option. All\n:italic:`{appname}` invocations with the same :option:`{appname}\n--instance-group` will result in new windows being created in the first\n:italic:`{appname}` instance within that group.\n\n\n{wait_for_single_instance_defn}\n\n\n{listen_on_defn} To start in headless mode,\nwithout an actual window, use :option:`{appname} --start-as`=hidden.\n\n\n--start-as\ntype=choices\ndefault=normal\nchoices=normal,fullscreen,maximized,minimized,hidden\nControl how the initial kitty OS window is created. Note that\nthis is applies to all OS Windows if you use the :option:`{appname} --session`\noption to create multiple OS Windows. Any OS Windows state\nspecified in the session file gets overriden.\n\n\n--position\nThe position, for example 10x20, on screen at which to place the first kitty OS Window.\nThis may or may not work depending on the policies of the desktop\nenvironment/window manager. It never works on Wayland.\nSee also :opt:`remember_window_position` to have kitty automatically try\nto restore the previous window position.\n\n\n--grab-keyboard\ntype=bool-set\n{grab_keyboard_docs}\n\n\n# Debugging options\n\n--version -v\ntype=bool-set\nThe current {appname} version.\n\n\n--dump-commands\ntype=bool-set\nOutput commands received from child process to STDOUT.\n\n\n--replay-commands\nReplay previously dumped commands. Specify the path to a dump file previously\ncreated by :option:`{appname} --dump-commands`. You\ncan open a new kitty window to replay the commands with::\n\n    {appname} sh -c \"{appname} --replay-commands /path/to/dump/file; read\"\n\n\n--dump-bytes\nPath to file in which to store the raw bytes received from the child process.\n\n\n--debug-rendering --debug-gl\ntype=bool-set\nDebug rendering commands. This will cause all OpenGL calls to check for errors\ninstead of ignoring them. Also prints out miscellaneous debug information.\nUseful when debugging rendering problems.\n\n\n--debug-input --debug-keyboard\ndest=debug_keyboard\ntype=bool-set\nPrint out key and mouse events as they are received.\n\n\n--debug-font-fallback\ntype=bool-set\nPrint out information about the selection of fallback fonts for characters not\npresent in the main font.\n\n\n--watcher\ncompletion=type:file ext:py relative:conf group:\"Watcher files\"\nThis option is deprecated in favor of the :opt:`watcher` option in\n:file:`{conf_name}.conf` and should not be used.\n\n\n--execute -e\ntype=bool-set\n!\n\"\"\"\n        setattr(kitty_options_spec, 'ans', OPTIONS.format(\n            appname=appname, conf_name=appname, listen_on_defn=listen_on_defn,\n            grab_keyboard_docs=grab_keyboard_docs, wait_for_single_instance_defn=wait_for_single_instance_defn,\n            config_help=CONFIG_HELP.format(appname=appname, conf_name=appname\n        )))\n    ans: str = getattr(kitty_options_spec, 'ans')\n    return ans\n# }}}\n\n\n# panel CLI spec {{{\npanel_defaults = {\n    'lines': '1', 'columns': '1',\n    'margin_left': '0', 'margin_top': '0', 'margin_right': '0', 'margin_bottom': '0',\n    'edge': 'top', 'layer': 'bottom', 'override': '', 'cls': f'{appname}-panel',\n    'focus_policy': 'not-allowed', 'exclusive_zone': '-1', 'override_exclusive_zone': 'no',\n    'single_instance': 'no', 'instance_group': '', 'toggle_visibility': 'no',\n    'start_as_hidden': 'no', 'detach': 'no', 'detached_log': '',\n}\n\ndef build_panel_cli_spec(defaults: dict[str, str]) -> str:\n    d = panel_defaults.copy()\n    d.update(defaults)\n    return r'''\n--lines\ndefault={lines}\nThe number of lines shown in the panel. Ignored for background, centered, and vertical panels.\nIf it has the suffix :code:`px` then it sets the height of the panel in pixels instead of lines.\n\n\n--columns\ndefault={columns}\nThe number of columns shown in the panel. Ignored for background, centered, and horizontal panels.\nIf it has the suffix :code:`px` then it sets the width of the panel in pixels instead of columns.\n\n\n--margin-top\ntype=int\ndefault={margin_top}\nSet the top margin for the panel, in pixels. Has no effect for bottom edge panels.\nOnly works on macOS and Wayland compositors that supports the wlr layer shell protocol.\n\n\n--margin-left\ntype=int\ndefault={margin_left}\nSet the left margin for the panel, in pixels. Has no effect for right edge panels.\nOnly works on macOS and Wayland compositors that supports the wlr layer shell protocol.\n\n\n--margin-bottom\ntype=int\ndefault={margin_bottom}\nSet the bottom margin for the panel, in pixels. Has no effect for top edge panels.\nOnly works on macOS and Wayland compositors that supports the wlr layer shell protocol.\n\n\n--margin-right\ntype=int\ndefault={margin_right}\nSet the right margin for the panel, in pixels. Has no effect for left edge panels.\nOnly works on macOS and Wayland compositors that supports the wlr layer shell protocol.\n\n\n--edge\nchoices=top,bottom,left,right,background,center,center-sized,none\ndefault={edge}\nWhich edge of the screen to place the panel on. Note that some window managers\n(such as i3) do not support placing docked windows on the left and right edges.\nThe value :code:`background` means make the panel the \"desktop wallpaper\".\nNote that when using sway if you set a background in your sway config it will\ncover the background drawn using this kitten.\nAdditionally, there are three more values: :code:`center`, :code:`center-sized` and :code:`none`.\nThe value :code:`center` anchors the panel to all sides and covers the entire\ndisplay (on macOS the part of the display not covered by titlebar and dock).\nThe panel can be shrunk and placed using the margin parameters.\nThe value :code:`none` anchors the panel to the top left corner and should be\nplaced using the margin parameters. Its size is set by :option:`--lines`\nand :option:`--columns`. The value :code:`center-sized` is just like :code:`none` except\nthat the panel is centered instead of in the top left corner and the margins have no effect.\n\n\n--layer\nchoices=background,bottom,top,overlay\ndefault={layer}\nOn a Wayland compositor that supports the wlr layer shell protocol, specifies the layer\non which the panel should be drawn. This parameter is ignored and set to\n:code:`background` if :option:`--edge` is set to :code:`background`. On macOS, maps\nthese to appropriate NSWindow *levels*.\n\n\n--config -c\ntype=list\nPath to config file to use for kitty when drawing the panel.\n\n\n--override -o\ntype=list\ndefault={override}\nOverride individual kitty configuration options, can be specified multiple times.\nSyntax: :italic:`name=value`. For example: :option:`kitty +kitten panel -o` font_size=20\n\n\n--output-name\nThe panel can only be displayed on a single monitor (output) at a time. This allows\nyou to specify which output is used, by name. If not specified the compositor will choose an\noutput automatically, typically the last output the user interacted with or the primary monitor.\nUse the special value :code:`list` to get a list of available outputs. Use :code:`listjson` for\na json encoded output. Note that on Wayland the output can only be set at panel creation time,\nit cannot be changed after creation, nor is there anyway to display a single panel on all outputs.\nPlease complain to the Wayland developers about this.\n\n\n--class --app-id\ndest=cls\ndefault={cls}\ncondition=not is_macos\nOn Wayland set the :italic:`namespace` of the layer shell surface. On X11 set the class part of the :italic:`WM_CLASS` window property.\n\n\n--name --os-window-tag\ncondition=not is_macos\nOn X11 sets the name part of the :italic:`WM_CLASS` property on X11,\nwhen unspecified uses the value from :option:`{appname} --class` on X11.\n\n\n--focus-policy\nchoices=not-allowed,exclusive,on-demand\ndefault={focus_policy}\nOn a Wayland compositor that supports the wlr layer shell protocol, specify the focus policy for keyboard\ninteractivity with the panel. Please refer to the wlr layer shell protocol documentation for more details.\nNote that different Wayland compositors behave very differently with :code:`exclusive`, your mileage may vary.\nOn macOS, :code:`exclusive` and :code:`on-demand` are currently the same.\n\n\n--hide-on-focus-loss\ntype=bool-set\nAutomatically hide the panel window when it loses focus. Using this option will force :option:`--focus-policy`\nto :code:`on-demand`. Note that on Wayland, depending on the compositor, this can result in the window never\nbecoming visible.\n\n\n--grab-keyboard\ntype=bool-set\n{grab_keyboard_docs}\n\n\n--exclusive-zone\ntype=int\ndefault={exclusive_zone}\nOn a Wayland compositor that supports the wlr layer shell protocol, request a given exclusive zone for the panel.\nPlease refer to the wlr layer shell documentation for more details on the meaning of exclusive and its value.\nIf :option:`--edge` is set to anything other than :code:`center` or :code:`none`, this flag will not have any\neffect unless the flag :option:`--override-exclusive-zone` is also set.\nIf :option:`--edge` is set to :code:`background`, this option has no effect.\nIgnored on X11 and macOS.\n\n\n--override-exclusive-zone\ntype=bool-set\ndefault={override_exclusive_zone}\nOn a Wayland compositor that supports the wlr layer shell protocol, override the default exclusive zone.\nThis has effect only if :option:`--edge` is set to :code:`top`, :code:`left`, :code:`bottom` or :code:`right`.\nIgnored on X11 and macOS.\n\n\n--single-instance -1\ntype=bool-set\ndefault={single_instance}\nIf specified only a single instance of the panel will run. New\ninvocations will instead create a new top-level window in the existing\npanel instance.\n\n\n--instance-group\ndefault={instance_group}\nUsed in combination with the :option:`--single-instance` option. All\npanel invocations with the same :option:`--instance-group` will result\nin new panels being created in the first panel instance within that group.\n\n\n{wait_for_single_instance_defn}\n\n\n{listen_on_defn}\n\n\n--toggle-visibility\ntype=bool-set\ndefault={toggle_visibility}\nWhen set and using :option:`--single-instance` will toggle the visibility of the\nexisting panel rather than creating a new one.\n\n\n--move-to-active-monitor\ntype=bool-set\ndefault=false\nWhen set and using :option:`--toggle-visibility` to show an existing panel, the panel\nis moved to the active monitor (typically the monitor with the mouse on it).\nThis works only if the underlying OS supports it. It is currently supported on macOS only.\n\n\n--start-as-hidden\ntype=bool-set\ndefault={start_as_hidden}\nStart in hidden mode, useful with :option:`--toggle-visibility`.\n\n\n--detach\ntype=bool-set\ndefault={detach}\nDetach from the controlling terminal, if any, running in an independent child process,\nthe parent process exits immediately.\n\n\n--detached-log\ndefault={detached_log}\nPath to a log file to store STDOUT/STDERR when using :option:`--detach`\n\n\n--debug-rendering\ntype=bool-set\nFor internal debugging use.\n\n\n--debug-input\ntype=bool-set\nFor internal debugging use.\n'''.format(\n    appname=appname, listen_on_defn=listen_on_defn, grab_keyboard_docs=grab_keyboard_docs,\n    wait_for_single_instance_defn=wait_for_single_instance_defn, **d)\n\n\ndef panel_options_spec() -> str:\n    return build_panel_cli_spec(panel_defaults)\n\n# }}}\n"
  },
  {
    "path": "kitty/srgb_gamma.h",
    "content": "// Generated by gen-srgb-lut.py DO NOT edit\n\nstatic const GLfloat srgb_lut[256] = {\n    0.00000000f, 0.00030353f, 0.00060705f, 0.00091058f, 0.00121411f, 0.00151763f, 0.00182116f, 0.00212469f, 0.00242822f, 0.00273174f, 0.00303527f, 0.00334654f, 0.00367651f, 0.00402472f, 0.00439144f, 0.00477695f,\n    0.00518152f, 0.00560539f, 0.00604883f, 0.00651209f, 0.00699541f, 0.00749903f, 0.00802319f, 0.00856813f, 0.00913406f, 0.00972122f, 0.01032982f, 0.01096009f, 0.01161225f, 0.01228649f, 0.01298303f, 0.01370208f,\n    0.01444384f, 0.01520851f, 0.01599629f, 0.01680738f, 0.01764195f, 0.01850022f, 0.01938236f, 0.02028856f, 0.02121901f, 0.02217388f, 0.02315337f, 0.02415763f, 0.02518686f, 0.02624122f, 0.02732089f, 0.02842604f,\n    0.02955683f, 0.03071344f, 0.03189603f, 0.03310477f, 0.03433981f, 0.03560131f, 0.03688945f, 0.03820437f, 0.03954624f, 0.04091520f, 0.04231141f, 0.04373503f, 0.04518620f, 0.04666509f, 0.04817182f, 0.04970657f,\n    0.05126946f, 0.05286065f, 0.05448028f, 0.05612849f, 0.05780543f, 0.05951124f, 0.06124605f, 0.06301002f, 0.06480327f, 0.06662594f, 0.06847817f, 0.07036010f, 0.07227185f, 0.07421357f, 0.07618538f, 0.07818742f,\n    0.08021982f, 0.08228271f, 0.08437621f, 0.08650046f, 0.08865559f, 0.09084171f, 0.09305896f, 0.09530747f, 0.09758735f, 0.09989873f, 0.10224173f, 0.10461648f, 0.10702310f, 0.10946171f, 0.11193243f, 0.11443537f,\n    0.11697067f, 0.11953843f, 0.12213877f, 0.12477182f, 0.12743768f, 0.13013648f, 0.13286832f, 0.13563333f, 0.13843162f, 0.14126329f, 0.14412847f, 0.14702727f, 0.14995979f, 0.15292615f, 0.15592646f, 0.15896084f,\n    0.16202938f, 0.16513219f, 0.16826940f, 0.17144110f, 0.17464740f, 0.17788842f, 0.18116424f, 0.18447499f, 0.18782077f, 0.19120168f, 0.19461783f, 0.19806932f, 0.20155625f, 0.20507874f, 0.20863687f, 0.21223076f,\n    0.21586050f, 0.21952620f, 0.22322796f, 0.22696587f, 0.23074005f, 0.23455058f, 0.23839757f, 0.24228112f, 0.24620133f, 0.25015828f, 0.25415209f, 0.25818285f, 0.26225066f, 0.26635560f, 0.27049779f, 0.27467731f,\n    0.27889426f, 0.28314874f, 0.28744084f, 0.29177065f, 0.29613827f, 0.30054379f, 0.30498731f, 0.30946892f, 0.31398871f, 0.31854678f, 0.32314321f, 0.32777810f, 0.33245154f, 0.33716362f, 0.34191442f, 0.34670406f,\n    0.35153260f, 0.35640014f, 0.36130678f, 0.36625260f, 0.37123768f, 0.37626212f, 0.38132601f, 0.38642943f, 0.39157248f, 0.39675523f, 0.40197778f, 0.40724021f, 0.41254261f, 0.41788507f, 0.42326767f, 0.42869050f,\n    0.43415364f, 0.43965717f, 0.44520119f, 0.45078578f, 0.45641102f, 0.46207700f, 0.46778380f, 0.47353150f, 0.47932018f, 0.48514994f, 0.49102085f, 0.49693300f, 0.50288646f, 0.50888132f, 0.51491767f, 0.52099557f,\n    0.52711513f, 0.53327640f, 0.53947949f, 0.54572446f, 0.55201140f, 0.55834039f, 0.56471151f, 0.57112483f, 0.57758044f, 0.58407842f, 0.59061884f, 0.59720179f, 0.60382734f, 0.61049557f, 0.61720656f, 0.62396039f,\n    0.63075714f, 0.63759687f, 0.64447968f, 0.65140564f, 0.65837482f, 0.66538730f, 0.67244316f, 0.67954247f, 0.68668531f, 0.69387176f, 0.70110189f, 0.70837578f, 0.71569350f, 0.72305513f, 0.73046074f, 0.73791041f,\n    0.74540421f, 0.75294222f, 0.76052450f, 0.76815115f, 0.77582222f, 0.78353779f, 0.79129794f, 0.79910274f, 0.80695226f, 0.81484657f, 0.82278575f, 0.83076988f, 0.83879901f, 0.84687323f, 0.85499261f, 0.86315721f,\n    0.87136712f, 0.87962240f, 0.88792312f, 0.89626935f, 0.90466117f, 0.91309865f, 0.92158186f, 0.93011086f, 0.93868573f, 0.94730654f, 0.95597335f, 0.96468625f, 0.97344529f, 0.98225055f, 0.99110210f, 1.00000000f\n};\n"
  },
  {
    "path": "kitty/state.c",
    "content": "/*\n * state.c\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"cleanup.h\"\n#include \"options/to-c-generated.h\"\n#include <math.h>\n#include <sys/mman.h>\n\nGlobalState global_state = {{0}};\n\n#define REMOVER(array, qid, count, destroy, capacity) { \\\n    for (size_t i = 0; i < count; i++) { \\\n        if (array[i].id == qid) { \\\n            destroy(array + i); \\\n            zero_at_i(array, i); \\\n            remove_i_from_array(array, i, count); \\\n            break; \\\n        } \\\n    }}\n\n#define WITH_OS_WINDOW(os_window_id) \\\n    for (size_t o = 0; o < global_state.num_os_windows; o++) { \\\n        OSWindow *os_window = global_state.os_windows + o; \\\n        if (os_window->id == os_window_id) {\n#define END_WITH_OS_WINDOW break; }}\n\n#define WITH_TAB(os_window_id, tab_id) \\\n    for (size_t o = 0, tab_found = 0; o < global_state.num_os_windows && !tab_found; o++) { \\\n        OSWindow *osw = global_state.os_windows + o; \\\n        if (osw->id == os_window_id) { \\\n            for (size_t t = 0; t < osw->num_tabs; t++) { \\\n                if (osw->tabs[t].id == tab_id) { \\\n                    Tab *tab = osw->tabs + t;\n#define END_WITH_TAB tab_found = 1; break; }}}}\n\n#define WITH_WINDOW(os_window_id, tab_id, window_id) \\\n    for (size_t o = 0, window_found = 0; o < global_state.num_os_windows && !window_found; o++) { \\\n        OSWindow *osw = global_state.os_windows + o; \\\n        if (osw->id == os_window_id) { \\\n            for (size_t t = 0; t < osw->num_tabs && !window_found; t++) { \\\n                if (osw->tabs[t].id == tab_id) { \\\n                    Tab *tab = osw->tabs + t; \\\n                    for (size_t w = 0; w < tab->num_windows; w++) { \\\n                        if (tab->windows[w].id == window_id) { \\\n                            Window *window = tab->windows + w;\n#define END_WITH_WINDOW window_found = 1; break; }}}}}}\n\n\n#define WITH_OS_WINDOW_REFS \\\n    id_type cb_window_id = 0, focused_window_id = 0; \\\n    if (global_state.callback_os_window) cb_window_id = global_state.callback_os_window->id; \\\n\n#define END_WITH_OS_WINDOW_REFS \\\n    if (cb_window_id || focused_window_id) { \\\n        global_state.callback_os_window = NULL; \\\n        for (size_t wn = 0; wn < global_state.num_os_windows; wn++) { \\\n            OSWindow *wp = global_state.os_windows + wn; \\\n            if (wp->id == cb_window_id && cb_window_id) global_state.callback_os_window = wp; \\\n    }}\n\nstatic double\ndpi_for_os_window(const OSWindow *os_window) {\n    double dpi = (os_window->fonts_data->logical_dpi_x + os_window->fonts_data->logical_dpi_y) / 2.;\n    if (dpi == 0) dpi = (global_state.default_dpi.x + global_state.default_dpi.y) / 2.;\n    return dpi;\n}\n\nstatic double\ndpi_for_os_window_id(id_type os_window_id) {\n    double dpi = 0;\n    if (os_window_id) {\n        WITH_OS_WINDOW(os_window_id)\n            dpi = dpi_for_os_window(os_window);\n        END_WITH_OS_WINDOW\n    }\n    if (dpi == 0) {\n        dpi = (global_state.default_dpi.x + global_state.default_dpi.y) / 2.;\n    }\n    return dpi;\n}\n\nstatic long\npt_to_px_for_os_window(double pt, const OSWindow *w) {\n    const double dpi = dpi_for_os_window(w);\n    return ((long)round((pt * (dpi / 72.0))));\n}\n\nstatic long\npt_to_px(double pt, id_type os_window_id) {\n    const double dpi = dpi_for_os_window_id(os_window_id);\n    return ((long)round((pt * (dpi / 72.0))));\n}\n\n\nOSWindow*\ncurrent_os_window(void) {\n    if (global_state.callback_os_window) return global_state.callback_os_window;\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        if (global_state.os_windows[i].is_focused) return global_state.os_windows + i;\n    }\n    return global_state.os_windows;\n}\n\nstatic id_type\nlast_focused_os_window_id(void) {\n    id_type ans = 0, max_fc_count = 0;\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = &global_state.os_windows[i];\n        if (w->last_focused_counter > max_fc_count) {\n            ans = w->id; max_fc_count = w->last_focused_counter;\n        }\n    }\n    return ans;\n}\n\nstatic id_type\ncurrent_focused_os_window_id(void) {\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = &global_state.os_windows[i];\n        if (w->is_focused) { return w->id; }\n    }\n    return 0;\n}\n\n\nOSWindow*\nos_window_for_id(id_type os_window_id) {\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = global_state.os_windows + i;\n        if (w->id == os_window_id) return w;\n    }\n    return NULL;\n}\n\nOSWindow*\nos_window_for_kitty_window(id_type kitty_window_id) {\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = global_state.os_windows + i;\n        for (size_t t = 0; t < w->num_tabs; t++) {\n            Tab *tab = w->tabs + t;\n            for (size_t c = 0; c < tab->num_windows; c++) {\n                if (tab->windows[c].id == kitty_window_id) return w;\n            }\n        }\n    }\n    return NULL;\n}\n\nWindow*\nwindow_for_window_id(id_type kitty_window_id) {\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = global_state.os_windows + i;\n        for (size_t t = 0; t < w->num_tabs; t++) {\n            Tab *tab = w->tabs + t;\n            for (size_t c = 0; c < tab->num_windows; c++) {\n                if (tab->windows[c].id == kitty_window_id) return tab->windows + c;\n            }\n        }\n    }\n    return NULL;\n}\n\nstatic void\nfree_bgimage_bitmap(BackgroundImage *bgimage) {\n    if (!bgimage->bitmap) return;\n    if (bgimage->mmap_size) {\n        if (munmap(bgimage->bitmap, bgimage->mmap_size) != 0) log_error(\"Failed to unmap BackgroundImage with error: %s\", strerror(errno));\n    } else free(bgimage->bitmap);\n    bgimage->bitmap = NULL;\n    bgimage->mmap_size = 0;\n}\n\nstatic void\nsend_bgimage_to_gpu(BackgroundImageLayout layout, BackgroundImage *bgimage) {\n    RepeatStrategy r = REPEAT_DEFAULT;\n    switch (layout) {\n        case SCALED:\n        case CLAMPED:\n        case CENTER_CLAMPED:\n        case CENTER_SCALED:\n            r = REPEAT_CLAMP; break;\n        case MIRRORED:\n            r = REPEAT_MIRROR; break;\n        case TILING:\n            r = REPEAT_DEFAULT; break;\n    }\n    bgimage->texture_id = 0;\n    size_t delta = bgimage->mmap_size ? bgimage->mmap_size - ((size_t)4) * bgimage->width * bgimage->height : 0;\n    send_image_to_gpu(&bgimage->texture_id, bgimage->bitmap + delta, bgimage->width,\n            bgimage->height, false, true, OPT(background_image_linear), r);\n    free_bgimage_bitmap(bgimage);\n}\n\nstatic void\nfree_bgimage(BackgroundImage **bgimage, bool release_texture) {\n    if (*bgimage && (*bgimage)->refcnt) {\n        (*bgimage)->refcnt--;\n        if ((*bgimage)->refcnt == 0) {\n            free_bgimage_bitmap(*bgimage);\n            if (release_texture) free_texture(&(*bgimage)->texture_id);\n            free(*bgimage);\n        }\n    }\n    bgimage = NULL;\n}\n\nOSWindow*\nadd_os_window(void) {\n    WITH_OS_WINDOW_REFS\n    ensure_space_for(&global_state, os_windows, OSWindow, global_state.num_os_windows + 1, capacity, 1, true);\n    OSWindow *ans = global_state.os_windows + global_state.num_os_windows++;\n    zero_at_ptr(ans);\n    ans->id = ++global_state.os_window_id_counter;\n    ans->tab_bar_render_data.vao_idx = create_cell_vao();\n    ans->background_opacity.alpha = OPT(background_opacity);\n    ans->created_at = monotonic();\n\n    bool wants_bg = OPT(background_image) && OPT(background_image)[0] != 0;\n    if (wants_bg) {\n        if (!global_state.bgimage) {\n            global_state.bgimage = calloc(1, sizeof(BackgroundImage));\n            if (!global_state.bgimage) fatal(\"Out of memory allocating the global bg image object\");\n            global_state.bgimage->refcnt++;\n            if (image_path_to_bitmap(OPT(background_image), &global_state.bgimage->bitmap, &global_state.bgimage->width, &global_state.bgimage->height, &global_state.bgimage->mmap_size)) {\n                send_bgimage_to_gpu(OPT(background_image_layout), global_state.bgimage);\n            }\n        }\n        if (global_state.bgimage->texture_id) {\n            ans->bgimage = global_state.bgimage;\n            ans->bgimage->refcnt++;\n        }\n    }\n\n    END_WITH_OS_WINDOW_REFS\n    return ans;\n}\n\nstatic id_type\nadd_tab(id_type os_window_id) {\n    WITH_OS_WINDOW(os_window_id)\n        make_os_window_context_current(os_window);\n        ensure_space_for(os_window, tabs, Tab, os_window->num_tabs + 1, capacity, 1, true);\n        zero_at_i(os_window->tabs, os_window->num_tabs);\n        os_window->tabs[os_window->num_tabs].id = ++global_state.tab_id_counter;\n        os_window->tabs[os_window->num_tabs].border_rects.vao_idx = create_border_vao();\n        return os_window->tabs[os_window->num_tabs++].id;\n    END_WITH_OS_WINDOW\n    return 0;\n}\n\nstatic void\ncreate_gpu_resources_for_window(Window *w) {\n    w->render_data.vao_idx = create_cell_vao();\n    w->window_title_render_data.vao_idx = create_cell_vao();\n}\n\nstatic void\nrelease_gpu_resources_for_window(Window *w) {\n    if (w->render_data.vao_idx > -1) remove_vao(w->render_data.vao_idx);\n    w->render_data.vao_idx = -1;\n    if (w->window_title_render_data.vao_idx > -1) remove_vao(w->window_title_render_data.vao_idx);\n    w->window_title_render_data.vao_idx = -1;\n    Py_CLEAR(w->window_title_render_data.screen);\n}\n\nstatic bool\nset_window_logo(Window *w, const char *path, const ImageAnchorPosition pos, float alpha, bool is_default, char *png_data, size_t png_data_size) {\n    bool ok = false;\n    if (path && path[0]) {\n        window_logo_id_t wl = find_or_create_window_logo(global_state.all_window_logos, path, png_data, png_data_size);\n        if (wl) {\n            if (w->window_logo.id) decref_window_logo(global_state.all_window_logos, w->window_logo.id);\n            w->window_logo.id = wl;\n            w->window_logo.position = pos;\n            w->window_logo.alpha = alpha;\n            ok = true;\n        }\n    } else {\n        if (w->window_logo.id) {\n            decref_window_logo(global_state.all_window_logos, w->window_logo.id);\n            w->window_logo.id = 0;\n        }\n        ok = true;\n    }\n    w->window_logo.using_default = is_default;\n    if (ok && w->render_data.screen) w->render_data.screen->is_dirty = true;\n    return ok;\n}\n\nstatic void\ninitialize_window(Window *w, PyObject *title, bool init_gpu_resources) {\n    w->id = ++global_state.window_id_counter;\n    w->visible = true;\n    w->title = title;\n    Py_XINCREF(title);\n    w->scrollbar.is_hovering = false;\n    if (!set_window_logo(w, OPT(default_window_logo), OPT(window_logo_position), OPT(window_logo_alpha), true, NULL, 0)) {\n        log_error(\"Failed to load default window logo: %s\", OPT(default_window_logo));\n        if (PyErr_Occurred()) PyErr_Print();\n    }\n    if (init_gpu_resources) create_gpu_resources_for_window(w);\n    else {\n        w->render_data.vao_idx = -1;\n    }\n}\n\nstatic id_type\nadd_window(id_type os_window_id, id_type tab_id, PyObject *title) {\n    WITH_TAB(os_window_id, tab_id);\n        ensure_space_for(tab, windows, Window, tab->num_windows + 1, capacity, 1, true);\n        make_os_window_context_current(osw);\n        zero_at_i(tab->windows, tab->num_windows);\n        initialize_window(tab->windows + tab->num_windows, title, true);\n        return tab->windows[tab->num_windows++].id;\n    END_WITH_TAB;\n    return 0;\n}\n\nstatic void\nupdate_window_title(id_type os_window_id, id_type tab_id, id_type window_id, PyObject *title) {\n    WITH_WINDOW(os_window_id, tab_id, window_id)\n        Py_CLEAR(window->title);\n        if (title) window->title = Py_NewRef(title);\n    END_WITH_WINDOW;\n}\n\nvoid\nset_os_window_title_from_window(Window *w, OSWindow *os_window) {\n    if (os_window->disallow_title_changes || os_window->title_is_overriden) return;\n    if (w->title && w->title != os_window->window_title) {\n        Py_CLEAR(os_window->window_title);\n        os_window->window_title = Py_NewRef(w->title);\n        set_os_window_title(os_window, PyUnicode_AsUTF8(w->title));\n    }\n}\n\nvoid\nupdate_os_window_title(OSWindow *os_window) {\n    if (os_window->num_tabs) {\n        Tab *tab = os_window->tabs + os_window->active_tab;\n        if (tab->num_windows) {\n            Window *w = tab->windows + tab->active_window;\n            set_os_window_title_from_window(w, os_window);\n        }\n    }\n}\n\nstatic void\ndestroy_window(Window *w) {\n    free(w->pending_clicks.clicks); zero_at_ptr(&w->pending_clicks);\n    free(w->buffered_keys.key_data); zero_at_ptr(&w->buffered_keys);\n    Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title);\n    Py_CLEAR(w->title_bar_data.last_drawn_title_object_id);\n    free(w->title_bar_data.buf); w->title_bar_data.buf = NULL;\n    Py_CLEAR(w->url_target_bar_data.last_drawn_title_object_id);\n    free(w->url_target_bar_data.buf); w->url_target_bar_data.buf = NULL;\n    release_gpu_resources_for_window(w);\n    if (w->window_logo.id) {\n        decref_window_logo(global_state.all_window_logos, w->window_logo.id);\n        w->window_logo.id = 0;\n    }\n}\n\nstatic void\nremove_window_inner(Tab *tab, id_type id) {\n    id_type active_window_id = 0;\n    if (tab->active_window < tab->num_windows) active_window_id = tab->windows[tab->active_window].id;\n    REMOVER(tab->windows, id, tab->num_windows, destroy_window, tab->capacity);\n    if (active_window_id) {\n        for (unsigned int w = 0; w < tab->num_windows; w++) {\n            if (tab->windows[w].id == active_window_id) {\n                tab->active_window = w; return;\n            }\n        }\n    }\n    if (tab->active_window >= tab->num_windows) tab->active_window = 0;\n}\n\nstatic void\nremove_window(id_type os_window_id, id_type tab_id, id_type id) {\n    WITH_TAB(os_window_id, tab_id);\n        make_os_window_context_current(osw);\n        remove_window_inner(tab, id);\n    END_WITH_TAB;\n}\n\ntypedef struct {\n    unsigned int num_windows, capacity;\n    Window *windows;\n} DetachedWindows;\n\nstatic DetachedWindows detached_windows = {0};\n\n\nstatic void\nadd_detached_window(Window *w) {\n    ensure_space_for(&detached_windows, windows, Window, detached_windows.num_windows + 1, capacity, 8, true);\n    memcpy(detached_windows.windows + detached_windows.num_windows++, w, sizeof(Window));\n}\n\nstatic void\ndetach_window(id_type os_window_id, id_type tab_id, id_type id) {\n    WITH_TAB(os_window_id, tab_id);\n        for (size_t i = 0; i < tab->num_windows; i++) {\n            if (tab->windows[i].id == id) {\n                make_os_window_context_current(osw);\n                release_gpu_resources_for_window(&tab->windows[i]);\n                add_detached_window(tab->windows + i);\n                zero_at_i(tab->windows, i);\n                remove_i_from_array(tab->windows, i, tab->num_windows);\n                if (tab->active_window >= tab->num_windows) tab->active_window = tab->num_windows ? tab->num_windows - 1 : 0;\n                break;\n            }\n        }\n    END_WITH_TAB;\n}\n\n\nstatic void\nresize_screen(OSWindow *os_window, Screen *screen, bool has_graphics) {\n    if (screen) {\n        screen->cell_size.width = os_window->fonts_data->fcm.cell_width;\n        screen->cell_size.height = os_window->fonts_data->fcm.cell_height;\n        screen_dirty_sprite_positions(screen);\n        if (has_graphics) screen_rescale_images(screen);\n    }\n}\n\nstatic void\nattach_window(id_type os_window_id, id_type tab_id, id_type id) {\n    WITH_TAB(os_window_id, tab_id);\n        for (size_t i = 0; i < detached_windows.num_windows; i++) {\n            if (detached_windows.windows[i].id == id) {\n                ensure_space_for(tab, windows, Window, tab->num_windows + 1, capacity, 1, true);\n                Window *w = tab->windows + tab->num_windows++;\n                memcpy(w, detached_windows.windows + i, sizeof(Window));\n                zero_at_i(detached_windows.windows, i);\n                remove_i_from_array(detached_windows.windows, i, detached_windows.num_windows);\n                make_os_window_context_current(osw);\n                create_gpu_resources_for_window(w);\n                if (\n                    w->render_data.screen->cell_size.width != osw->fonts_data->fcm.cell_width ||\n                    w->render_data.screen->cell_size.height != osw->fonts_data->fcm.cell_height\n                ) resize_screen(osw, w->render_data.screen, true);\n                else screen_dirty_sprite_positions(w->render_data.screen);\n                w->render_data.screen->reload_all_gpu_data = true;\n                break;\n            }\n        }\n    END_WITH_TAB;\n}\n\nstatic void\ndestroy_tab(Tab *tab) {\n    for (size_t i = tab->num_windows; i > 0; i--) remove_window_inner(tab, tab->windows[i - 1].id);\n    remove_vao(tab->border_rects.vao_idx);\n    free(tab->border_rects.rect_buf); tab->border_rects.rect_buf = NULL;\n    free(tab->windows); tab->windows = NULL;\n}\n\nstatic void\nremove_tab_inner(OSWindow *os_window, id_type id) {\n    id_type active_tab_id = 0;\n    if (os_window->active_tab < os_window->num_tabs) active_tab_id = os_window->tabs[os_window->active_tab].id;\n    make_os_window_context_current(os_window);\n    REMOVER(os_window->tabs, id, os_window->num_tabs, destroy_tab, os_window->capacity);\n    if (active_tab_id) {\n        for (unsigned int i = 0; i < os_window->num_tabs; i++) {\n            if (os_window->tabs[i].id == active_tab_id) {\n                os_window->active_tab = i; break;\n            }\n        }\n    }\n}\n\nstatic void\nremove_tab(id_type os_window_id, id_type id) {\n    WITH_OS_WINDOW(os_window_id)\n        remove_tab_inner(os_window, id);\n    END_WITH_OS_WINDOW\n}\n\nstatic void\ndestroy_os_window_item(OSWindow *w) {\n    for (size_t t = w->num_tabs; t > 0; t--) {\n        Tab *tab = w->tabs + t - 1;\n        remove_tab_inner(w, tab->id);\n    }\n    Py_CLEAR(w->window_title); Py_CLEAR(w->tab_bar_render_data.screen);\n    remove_vao(w->tab_bar_render_data.vao_idx);\n    free(w->tabs); w->tabs = NULL;\n    free_bgimage(&w->bgimage, true);\n    zero_at_ptr(&w->bgimage);\n    if (w->indirect_output.framebuffer_id) free_framebuffer(&w->indirect_output.framebuffer_id);\n}\n\nbool\nremove_os_window(id_type os_window_id) {\n    bool found = false;\n    WITH_OS_WINDOW(os_window_id)\n        found = true;\n        make_os_window_context_current(os_window);\n    END_WITH_OS_WINDOW\n    if (found) {\n        WITH_OS_WINDOW_REFS\n            REMOVER(global_state.os_windows, os_window_id, global_state.num_os_windows, destroy_os_window_item, global_state.capacity);\n        END_WITH_OS_WINDOW_REFS\n        update_os_window_references();\n        if (global_state.num_os_windows == 0) {\n            if (global_state.layers_render_texture.texture_id) free_texture(&global_state.layers_render_texture.texture_id);\n            if (global_state.layers_render_texture.framebuffer_id) free_framebuffer(&global_state.layers_render_texture.framebuffer_id);\n            global_state.layers_render_texture.width = 0; global_state.layers_render_texture.height = 0;\n        }\n    }\n    return found;\n}\n\nstatic void\nmark_os_window_dirty(id_type os_window_id) {\n    WITH_OS_WINDOW(os_window_id)\n        os_window->needs_render = true;\n    END_WITH_OS_WINDOW\n}\n\nstatic void\nset_active_tab(id_type os_window_id, unsigned int idx) {\n    WITH_OS_WINDOW(os_window_id)\n        os_window->active_tab = idx;\n        os_window->needs_render = true;\n    END_WITH_OS_WINDOW\n}\n\nstatic void\nset_active_window(id_type os_window_id, id_type tab_id, id_type window_id) {\n    WITH_TAB(os_window_id, tab_id)\n        tab->active_window = 0;\n        for (unsigned w = 0; w < tab->num_windows; w++) {\n            if (tab->windows[w].id == window_id) {\n                tab->active_window = w; break;\n            }\n        }\n        osw->needs_render = true;\n        set_os_window_chrome(osw);\n    END_WITH_TAB;\n}\n\nstatic bool\nbuffer_keys_in_window(id_type os_window_id, id_type tab_id, id_type window_id, bool enable) {\n    WITH_WINDOW(os_window_id, tab_id, window_id)\n        window->buffered_keys.enabled = enable;\n        if (!enable) dispatch_buffered_keys(window);\n        return true;\n    END_WITH_WINDOW;\n    return false;\n}\n\nstatic bool\nset_redirect_keys_to_overlay(id_type os_window_id, id_type tab_id, id_type window_id, id_type overlay_id) {\n    WITH_WINDOW(os_window_id, tab_id, window_id)\n        window->redirect_keys_to_overlay = overlay_id;\n        return true;\n    END_WITH_WINDOW;\n    return false;\n}\n\n\nstatic void\nswap_tabs(id_type os_window_id, unsigned int a, unsigned int b) {\n    WITH_OS_WINDOW(os_window_id)\n        Tab t = os_window->tabs[b];\n        os_window->tabs[b] = os_window->tabs[a];\n        os_window->tabs[a] = t;\n    END_WITH_OS_WINDOW\n}\n\nstatic PyObject*\npyreorder_tabs(PyObject *self UNUSED, PyObject *args) {\n    if (PyTuple_GET_SIZE(args) < 2) Py_RETURN_NONE;\n    id_type os_window_id = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args, 0));\n    WITH_OS_WINDOW(os_window_id)\n        if (PyTuple_GET_SIZE(args) != (Py_ssize_t)(os_window->num_tabs + 1)) { PyErr_SetString(PyExc_ValueError, \"number of tabs not correct\"); return NULL; }\n        if (!os_window->num_tabs) Py_RETURN_NONE;\n        RAII_ALLOC(Tab, tabs, calloc(os_window->capacity, sizeof(Tab)));\n        RAII_ALLOC(char, used, calloc(os_window->num_tabs, sizeof(char)));\n        if (!tabs || !used) { return PyErr_NoMemory(); }\n        for (Py_ssize_t i = 1; i < PyTuple_GET_SIZE(args); i++) {\n            id_type tab_id = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(args, i));\n            bool found = false;\n            for (size_t t = 0; t < os_window->num_tabs; t++) {\n                if (os_window->tabs[t].id == tab_id) {\n                    tabs[i-1] = os_window->tabs[t];\n                    found = true;\n                    if (used[t]) {\n                        PyErr_SetString(PyExc_ValueError, \"duplicate tab ids found\"); return NULL;\n                    }\n                    used[t] = 1;\n                    break;\n                }\n            }\n            if (!found) { PyErr_SetString(PyExc_ValueError, \"tab id not found\"); return NULL; }\n        }\n        free(os_window->tabs); os_window->tabs = tabs; tabs = NULL;\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npyset_borders_rects(PyObject *self UNUSED, PyObject *args) {\n    id_type os_window_id, tab_id;\n    PyObject *rects;\n    if (!PyArg_ParseTuple(args, \"KKO!\", &os_window_id, &tab_id, &PyList_Type, &rects)) return NULL;\n    WITH_TAB(os_window_id, tab_id)\n        BorderRects *br = &tab->border_rects;\n        br->is_dirty = true;\n        br->num_border_rects = PyList_GET_SIZE(rects);\n        ensure_space_for(br, rect_buf, BorderRect, br->num_border_rects + 1, capacity, 32, false);\n        for (unsigned i = 0; i < br->num_border_rects; i++) {\n            PyObject *pr = PyList_GET_ITEM(rects, i);\n            unsigned long color; long long border_type;\n            BorderRect *r = br->rect_buf + i;\n            int horizontal;\n            if (!PyArg_ParseTuple(\n                pr, \"IIIIkLp\", &r->px.left, &r->px.top, &r->px.right, &r->px.bottom, &color, &border_type, &horizontal\n            )) return NULL;\n            r->left = gl_pos_x(r->px.left, osw->viewport_width);\n            r->top = gl_pos_y(r->px.top, osw->viewport_height);\n            r->right = r->left + gl_size(r->px.right - r->px.left, osw->viewport_width);\n            r->bottom = r->top - gl_size(r->px.bottom - r->px.top, osw->viewport_height);\n            r->color = color; r->border_type = border_type; r->horizontal = horizontal;\n        }\n    END_WITH_TAB\n    Py_RETURN_NONE;\n}\n\n\nvoid\nos_window_regions(const OSWindow *os_window, Region *central, Region *tab_bar) {\n    if (!OPT(tab_bar_hidden) && os_window->num_tabs && !os_window->has_too_few_tabs) {\n        long margin_outer = pt_to_px_for_os_window(OPT(tab_bar_margin_height.outer), os_window);\n        long margin_inner = pt_to_px_for_os_window(OPT(tab_bar_margin_height.inner), os_window);\n        central->left = 0; central->right = os_window->viewport_width;\n        unsigned tab_bar_height = os_window->fonts_data->fcm.cell_height + margin_inner + margin_outer;\n        switch(OPT(tab_bar_edge)) {\n            case TOP_EDGE:\n                central->top = tab_bar_height;\n                central->bottom = os_window->viewport_height;\n                central->top = MIN(central->top, central->bottom);\n                tab_bar->top = margin_outer;\n                break;\n            default:\n                central->top = 0;\n                long bottom = os_window->viewport_height - tab_bar_height;\n                central->bottom = MAX(0, bottom);\n                tab_bar->top = central->bottom + margin_inner;\n                break;\n        }\n        tab_bar->left = central->left; tab_bar->right = central->right;\n        tab_bar->bottom = tab_bar->top + os_window->fonts_data->fcm.cell_height;\n    } else {\n        zero_at_ptr(tab_bar);\n        central->left = 0; central->top = 0; central->right = os_window->viewport_width;\n        central->bottom = os_window->viewport_height;\n    }\n}\n\nvoid\nmark_os_window_for_close(OSWindow* w, CloseRequest cr) {\n    global_state.has_pending_closes = true;\n    w->close_request = cr;\n}\n\nstatic bool\nowners_for_window_id(id_type window_id, OSWindow **os_window, Tab **tab) {\n    if (os_window) *os_window = NULL;\n    if (tab) *tab = NULL;\n    for (size_t o = 0; o < global_state.num_os_windows; o++) {\n        OSWindow *osw = global_state.os_windows + o;\n        for (size_t t = 0; t < osw->num_tabs; t++) {\n            Tab *qtab = osw->tabs + t;\n            for (size_t w = 0; w < qtab->num_windows; w++) {\n                Window *window = qtab->windows + w;\n                if (window->id == window_id) {\n                    if (os_window) *os_window = osw;\n                    if (tab) *tab = qtab;\n                    return true;\n    }}}}\n    return false;\n}\n\n\nbool\nmake_window_context_current(id_type window_id) {\n    OSWindow *os_window;\n    if (owners_for_window_id(window_id, &os_window, NULL)) {\n        make_os_window_context_current(os_window);\n        return true;\n    }\n    return false;\n}\n\nvoid\ndispatch_pending_clicks(id_type timer_id UNUSED, void *data UNUSED) {\n    bool dispatched = false;\n    do {  // dispatching a click can cause windows/tabs/etc to close so do it one at a time.\n        const monotonic_t now = monotonic();\n        dispatched = false;\n        for (size_t o = 0; o < global_state.num_os_windows && !dispatched; o++) {\n            OSWindow *osw = global_state.os_windows + o;\n            for (size_t t = 0; t < osw->num_tabs && !dispatched; t++) {\n                Tab *qtab = osw->tabs + t;\n                for (size_t w = 0; w < qtab->num_windows && !dispatched; w++) {\n                    Window *window = qtab->windows + w;\n                    for (size_t i = 0; i < window->pending_clicks.num && !dispatched; i++) {\n                        if (now - window->pending_clicks.clicks[i].at >= OPT(click_interval)) {\n                            dispatched = true;\n                            send_pending_click_to_window(window, i);\n                        }\n                    }\n                }\n            }\n        }\n    } while (dispatched);\n}\n\nbool\nupdate_ime_position_for_window(id_type window_id, bool force, int update_focus) {\n    for (size_t o = 0; o < global_state.num_os_windows; o++) {\n        OSWindow *osw = global_state.os_windows + o;\n        for (size_t t = 0; t < osw->num_tabs; t++) {\n            Tab *qtab = osw->tabs + t;\n            for (size_t w = 0; w < qtab->num_windows; w++) {\n                Window *window = qtab->windows + w;\n                if (window->id == window_id) {\n                    // The screen may not be ready after the new window is created and focused, and still needs to enable IME.\n                    if ((window->render_data.screen && (force || osw->is_focused)) || update_focus > 0) {\n                        OSWindow *orig = global_state.callback_os_window;\n                        global_state.callback_os_window = osw;\n                        if (update_focus) update_ime_focus(osw, update_focus > 0);\n                        if (update_focus >= 0 && window->render_data.screen) {\n                            update_ime_position(window, window->render_data.screen);\n                        }\n                        global_state.callback_os_window = orig;\n                        return true;\n                    }\n                    return false;\n                }\n            }\n        }\n    }\n    return false;\n}\n\n\n// Python API {{{\n#define PYWRAP0(name) static PyObject* py##name(PYNOARG)\n#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)\n#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;\n#define ONE_UINT(name) PYWRAP1(name) { name((unsigned int)PyLong_AsUnsignedLong(args)); Py_RETURN_NONE; }\n#define TWO_UINT(name) PYWRAP1(name) { unsigned int a, b; PA(\"II\", &a, &b); name(a, b); Py_RETURN_NONE; }\n#define THREE_UINT(name) PYWRAP1(name) { unsigned int a, b, c; PA(\"III\", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }\n#define TWO_ID(name) PYWRAP1(name) { id_type a, b; PA(\"KK\", &a, &b); name(a, b); Py_RETURN_NONE; }\n#define THREE_ID(name) PYWRAP1(name) { id_type a, b, c; PA(\"KKK\", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }\n#define THREE_ID_OBJ(name) PYWRAP1(name) { id_type a, b, c; PyObject *o; PA(\"KKKO\", &a, &b, &c, &o); name(a, b, c, o); Py_RETURN_NONE; }\n#define K(name) PYWRAP1(name) { id_type a; PA(\"K\", &a); name(a); Py_RETURN_NONE; }\n#define KI(name) PYWRAP1(name) { id_type a; unsigned int b; PA(\"KI\", &a, &b); name(a, b); Py_RETURN_NONE; }\n#define KII(name) PYWRAP1(name) { id_type a; unsigned int b, c; PA(\"KII\", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }\n#define KKI(name) PYWRAP1(name) { id_type a, b; unsigned int c; PA(\"KKI\", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }\n#define KKK(name) PYWRAP1(name) { id_type a, b, c; PA(\"KKK\", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }\n#define KKII(name) PYWRAP1(name) { id_type a, b; unsigned int c, d; PA(\"KKII\", &a, &b, &c, &d); name(a, b, c, d); Py_RETURN_NONE; }\n#define KKKK(name) PYWRAP1(name) { id_type a, b, c, d; PA(\"KKKK\", &a, &b, &c, &d); name(a, b, c, d); Py_RETURN_NONE; }\n#define KK5I(name) PYWRAP1(name) { id_type a, b; unsigned int c, d, e, f, g; PA(\"KKIIIII\", &a, &b, &c, &d, &e, &f, &g); name(a, b, c, d, e, f, g); Py_RETURN_NONE; }\n#define BOOL_SET(name) PYWRAP1(set_##name) { global_state.name = PyObject_IsTrue(args); Py_RETURN_NONE; }\n#define dict_iter(d) { \\\n    PyObject *key, *value; Py_ssize_t pos = 0; \\\n    while (PyDict_Next(d, &pos, &key, &value))\n\nPYWRAP1(update_ime_position_for_window) {\n    id_type window_id;\n    int force = 0;\n    int update_focus = 0;\n    PA(\"K|pi\", &window_id, &force, &update_focus);\n    if (update_ime_position_for_window(window_id, force, update_focus)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nPYWRAP0(next_window_id) {\n    return PyLong_FromUnsignedLongLong(global_state.window_id_counter + 1);\n}\n\nPYWRAP0(last_focused_os_window_id) {\n    return PyLong_FromUnsignedLongLong(last_focused_os_window_id());\n}\n\nPYWRAP0(current_focused_os_window_id) {\n    return PyLong_FromUnsignedLongLong(current_focused_os_window_id());\n}\n\n\nPYWRAP1(handle_for_window_id) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        return PyLong_FromVoidPtr(os_window->handle);\n    END_WITH_OS_WINDOW\n    PyErr_SetString(PyExc_ValueError, \"No such window\");\n    return NULL;\n}\n\n\nPYWRAP0(get_options) {\n    if (!global_state.options_object) {\n        PyErr_SetString(PyExc_RuntimeError, \"Must call set_options() before using get_options()\");\n        return NULL;\n    }\n    Py_INCREF(global_state.options_object);\n    return global_state.options_object;\n}\n\nPYWRAP1(set_options) {\n    PyObject *opts;\n    int is_wayland = 0, debug_rendering = 0, debug_font_fallback = 0;\n    PA(\"O|ppp\", &opts, &is_wayland, &debug_rendering, &debug_font_fallback);\n    if (opts == Py_None) {\n        Py_CLEAR(global_state.options_object);\n        Py_RETURN_NONE;\n    }\n#ifdef __APPLE__\n    global_state.is_apple = true;\n    global_state.has_render_frames = true;\n#endif\n    global_state.is_wayland = is_wayland ? true : false;\n    if (global_state.is_wayland) global_state.has_render_frames = true;\n    global_state.debug_rendering = debug_rendering ? true : false;\n    global_state.debug_font_fallback = debug_font_fallback ? true : false;\n    if (!convert_opts_from_python_opts(opts, &global_state.opts)) return NULL;\n    global_state.options_object = opts;\n    Py_INCREF(global_state.options_object);\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(set_ignore_os_keyboard_processing) {\n    set_ignore_os_keyboard_processing(PyObject_IsTrue(args));\n    Py_RETURN_NONE;\n}\n\nstatic void\ninit_window_render_data(WindowRenderData *d, const WindowGeometry g, Screen *screen) {\n    d->geometry = g;\n    Py_CLEAR(d->screen); d->screen = (Screen*)Py_NewRef(screen);\n}\n\nPYWRAP1(set_tab_bar_render_data) {\n    WindowGeometry g;\n    id_type os_window_id;\n    Screen *screen;\n    PA(\"KOIIII\", &os_window_id, &screen, &g.left, &g.top, &g.right, &g.bottom);\n    WITH_OS_WINDOW(os_window_id)\n        init_window_render_data(&os_window->tab_bar_render_data, g, screen);\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(set_window_title_bar_render_data) {\n    WindowGeometry g = {0};\n    id_type os_window_id, tab_id, window_id;\n    Screen *screen;\n    PA(\"KKKOIIII\", &os_window_id, &tab_id, &window_id, &screen, &g.left, &g.top, &g.right, &g.bottom);\n    WITH_WINDOW(os_window_id, tab_id, window_id)\n        init_window_render_data(&window->window_title_render_data, g, screen);\n        screen->reload_all_gpu_data = true;\n    END_WITH_WINDOW\n    Py_RETURN_NONE;\n}\n\nstatic PyTypeObject RegionType;\nstatic PyStructSequence_Field region_fields[] = {\n    {\"left\", \"\"}, {\"top\", \"\"}, {\"right\", \"\"}, {\"bottom\", \"\"}, {\"width\", \"\"}, {\"height\", \"\"}, {NULL, NULL}\n};\nstatic PyStructSequence_Desc region_desc = {\"Region\", NULL, region_fields, 6};\n\nstatic PyObject*\nwrap_region(Region *r) {\n    PyObject *ans = PyStructSequence_New(&RegionType);\n    if (ans) {\n        PyStructSequence_SET_ITEM(ans, 0, PyLong_FromUnsignedLong(r->left));\n        PyStructSequence_SET_ITEM(ans, 1, PyLong_FromUnsignedLong(r->top));\n        PyStructSequence_SET_ITEM(ans, 2, PyLong_FromUnsignedLong(r->right));\n        PyStructSequence_SET_ITEM(ans, 3, PyLong_FromUnsignedLong(r->bottom));\n        PyStructSequence_SET_ITEM(ans, 4, PyLong_FromUnsignedLong(r->right - r->left));\n        PyStructSequence_SET_ITEM(ans, 5, PyLong_FromUnsignedLong(r->bottom - r->top));\n    }\n    return ans;\n}\n\nPYWRAP1(viewport_for_window) {\n    id_type os_window_id;\n    int vw = 100, vh = 100;\n    unsigned int cell_width = 1, cell_height = 1;\n    PA(\"K\", &os_window_id);\n    Region central = {0}, tab_bar = {0};\n    WITH_OS_WINDOW(os_window_id)\n        os_window_regions(os_window, &central, &tab_bar);\n        vw = os_window->viewport_width; vh = os_window->viewport_height;\n        cell_width = os_window->fonts_data->fcm.cell_width; cell_height = os_window->fonts_data->fcm.cell_height;\n        goto end;\n    END_WITH_OS_WINDOW\nend:\n    return Py_BuildValue(\"NNiiII\", wrap_region(&central), wrap_region(&tab_bar), vw, vh, cell_width, cell_height);\n}\n\nPYWRAP1(cell_size_for_window) {\n    id_type os_window_id;\n    unsigned int cell_width = 0, cell_height = 0;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        cell_width = os_window->fonts_data->fcm.cell_width; cell_height = os_window->fonts_data->fcm.cell_height;\n        goto end;\n    END_WITH_OS_WINDOW\nend:\n    return Py_BuildValue(\"II\", cell_width, cell_height);\n}\n\n\nPYWRAP1(os_window_has_background_image) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        if (os_window->bgimage && os_window->bgimage->texture_id > 0) { Py_RETURN_TRUE; }\n    END_WITH_OS_WINDOW\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(mark_os_window_for_close) {\n    id_type os_window_id;\n    CloseRequest cr = IMPERATIVE_CLOSE_REQUESTED;\n    PA(\"K|i\", &os_window_id, &cr);\n    WITH_OS_WINDOW(os_window_id)\n        mark_os_window_for_close(os_window, cr);\n        Py_RETURN_TRUE;\n    END_WITH_OS_WINDOW\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(set_application_quit_request) {\n    CloseRequest cr = IMPERATIVE_CLOSE_REQUESTED;\n    PA(\"|i\", &cr);\n    global_state.quit_request = cr;\n    global_state.has_pending_closes = true;\n    request_tick_callback();\n    Py_RETURN_NONE;\n}\n\nPYWRAP0(current_application_quit_request) {\n    return Py_BuildValue(\"i\", global_state.quit_request);\n}\n\nPYWRAP1(focus_os_window) {\n    id_type os_window_id;\n    int also_raise = 1;\n    const char *activation_token = NULL;\n    PA(\"K|pz\", &os_window_id, &also_raise, &activation_token);\n    WITH_OS_WINDOW(os_window_id)\n        if (!os_window->is_focused || (activation_token && activation_token[0])) focus_os_window(os_window, also_raise, activation_token);\n        Py_RETURN_TRUE;\n    END_WITH_OS_WINDOW\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(run_with_activation_token) {\n    for (size_t o = 0; o < global_state.num_os_windows; o++) {\n        OSWindow *os_window = global_state.os_windows + o;\n        if (os_window->is_focused) {\n            run_with_activation_token_in_os_window(os_window, args);\n            Py_RETURN_TRUE;\n        }\n    }\n    id_type os_window_id = last_focused_os_window_id();\n    if (!os_window_id) {\n        if (!global_state.num_os_windows) Py_RETURN_FALSE;\n        os_window_id = global_state.os_windows[0].id;\n    }\n    for (size_t o = 0; o < global_state.num_os_windows; o++) {\n        OSWindow *os_window = global_state.os_windows + o;\n        if (os_window->id == os_window_id) {\n            run_with_activation_token_in_os_window(os_window, args);\n            Py_RETURN_TRUE;\n        }\n    }\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(set_os_window_chrome) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        set_os_window_chrome(os_window);\n        Py_RETURN_TRUE;\n    END_WITH_OS_WINDOW\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(mark_tab_bar_dirty) {\n    id_type os_window_id; int should_be_shown;\n    PA(\"Kp\", &os_window_id, &should_be_shown);\n    WITH_OS_WINDOW(os_window_id)\n        os_window->has_too_few_tabs = !should_be_shown;\n        os_window->tab_bar_data_updated = false;\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(is_tab_bar_visible) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    if (!OPT(tab_bar_hidden)) {\n        WITH_OS_WINDOW(os_window_id)\n            return (os_window->num_tabs == 0 || os_window->has_too_few_tabs) ? Py_NewRef(Py_False) : Py_NewRef(Py_True);\n        END_WITH_OS_WINDOW\n    }\n    Py_RETURN_FALSE;\n}\n\n\nPYWRAP1(change_background_opacity) {\n    id_type os_window_id;\n    float opacity;\n    PA(\"Kf\", &os_window_id, &opacity);\n    WITH_OS_WINDOW(os_window_id)\n        os_window->background_opacity.alpha = opacity;\n        if (!os_window->redraw_count) os_window->redraw_count++;\n        set_os_window_chrome(os_window);  // on macOS titlebar opacity can depend on background_opacity\n        Py_RETURN_TRUE;\n    END_WITH_OS_WINDOW\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(background_opacity_of) {\n    id_type os_window_id = PyLong_AsUnsignedLongLong(args);\n    if (PyErr_Occurred()) return NULL;\n    WITH_OS_WINDOW(os_window_id)\n        return PyFloat_FromDouble((double)effective_os_window_alpha(os_window));\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(set_window_padding) {\n    id_type os_window_id, tab_id, window_id;\n    unsigned int left, top, right, bottom;\n    PA(\"KKKIIII\", &os_window_id, &tab_id, &window_id, &left, &top, &right, &bottom);\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n        window->padding.left = left; window->padding.top = top; window->padding.right = right; window->padding.bottom = bottom;\n    END_WITH_WINDOW;\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(set_window_render_data) {\n#define B(name) &(g.name)\n#define S(name) &(g.spaces.name)\n    id_type os_window_id, tab_id, window_id;\n    WindowGeometry g = {0};\n    Screen *screen;\n    PA(\"KKKOIIIIIIII\", &os_window_id, &tab_id, &window_id, &screen,\n       B(left), B(top), B(right), B(bottom),\n       S(left), S(top), S(right), S(bottom));\n\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n        init_window_render_data(&window->render_data, g, screen);\n    END_WITH_WINDOW;\n    Py_RETURN_NONE;\n#undef B\n#undef S\n}\n\nPYWRAP1(update_window_visibility) {\n    id_type os_window_id, tab_id, window_id;\n    int visible;\n    PA(\"KKKp\", &os_window_id, &tab_id, &window_id, &visible);\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n        bool was_visible = window->visible & 1;\n        window->visible = visible & 1;\n        if (!was_visible && window->visible) global_state.check_for_active_animated_images = true;\n    END_WITH_WINDOW;\n    Py_RETURN_NONE;\n}\n\n\nPYWRAP1(sync_os_window_title) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        update_os_window_title(os_window);\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(set_os_window_title) {\n    id_type os_window_id;\n    PyObject *title;\n    PA(\"KU\", &os_window_id, &title);\n    WITH_OS_WINDOW(os_window_id)\n        if (os_window->disallow_title_changes) break;\n        if (PyUnicode_GetLength(title)) {\n            os_window->title_is_overriden = true;\n            Py_XDECREF(os_window->window_title);\n            os_window->window_title = title;\n            Py_INCREF(title);\n            set_os_window_title(os_window, PyUnicode_AsUTF8(title));\n        } else {\n            os_window->title_is_overriden = false;\n            if (os_window->window_title) set_os_window_title(os_window, PyUnicode_AsUTF8(os_window->window_title));\n            update_os_window_title(os_window);\n        }\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(get_os_window_title) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        if (os_window->window_title) return Py_BuildValue(\"O\", os_window->window_title);\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(os_window_is_invisible) {\n    id_type os_window_id = PyLong_AsUnsignedLongLong(args);\n    if (PyErr_Occurred()) return NULL;\n    WITH_OS_WINDOW(os_window_id)\n        if (should_os_window_be_rendered(os_window)) { Py_RETURN_FALSE; }\n        Py_RETURN_TRUE;\n    END_WITH_OS_WINDOW\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(pt_to_px) {\n    double pt;\n    id_type os_window_id = 0;\n    PA(\"d|K\", &pt, &os_window_id);\n    return PyLong_FromLong(pt_to_px(pt, os_window_id));\n}\n\nPYWRAP1(global_font_size) {\n    double set_val = -1;\n    PA(\"|d\", &set_val);\n    if (set_val > 0) OPT(font_size) = set_val;\n    return Py_BuildValue(\"d\", OPT(font_size));\n}\n\nPYWRAP1(os_window_font_size) {\n    id_type os_window_id;\n    int force = 0;\n    double new_sz = -1;\n    PA(\"K|dp\", &os_window_id, &new_sz, &force);\n    WITH_OS_WINDOW(os_window_id)\n        if (new_sz > 0 && (force || new_sz != os_window->fonts_data->font_sz_in_pts)) {\n            on_os_window_font_size_change(os_window, new_sz);\n            send_prerendered_sprites_for_window(os_window);\n            resize_screen(os_window, os_window->tab_bar_render_data.screen, false);\n            for (size_t ti = 0; ti < os_window->num_tabs; ti++) {\n                Tab *tab = os_window->tabs + ti;\n                for (size_t wi = 0; wi < tab->num_windows; wi++) {\n                    Window *w = tab->windows + wi;\n                    resize_screen(os_window, w->render_data.screen, true);\n                }\n            }\n            // On Wayland with CSD title needs to be re-rendered in a different font size\n            if (os_window->window_title && global_state.is_wayland) set_os_window_title(os_window, NULL);\n        }\n        return Py_BuildValue(\"d\", os_window->fonts_data->font_sz_in_pts);\n    END_WITH_OS_WINDOW\n    return Py_BuildValue(\"d\", 0.0);\n}\n\nPYWRAP1(set_os_window_size) {\n    id_type os_window_id;\n    int width, height;\n    PA(\"Kii\", &os_window_id, &width, &height);\n    WITH_OS_WINDOW(os_window_id)\n        set_os_window_size(os_window, width, height);\n        Py_RETURN_TRUE;\n    END_WITH_OS_WINDOW\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(get_os_window_size) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        double xdpi, ydpi;\n        float xscale, yscale;\n        int width, height, fw, fh;\n        get_os_window_size(os_window, &width, &height, &fw, &fh);\n        get_os_window_content_scale(os_window, &xdpi, &ydpi, &xscale, &yscale);\n        unsigned int cell_width = os_window->fonts_data->fcm.cell_width, cell_height = os_window->fonts_data->fcm.cell_height;\n        return Py_BuildValue(\"{si si si si sf sf sd sd sI sI sO}\",\n            \"width\", width, \"height\", height, \"framebuffer_width\", fw, \"framebuffer_height\", fh,\n            \"xscale\", xscale, \"yscale\", yscale, \"xdpi\", xdpi, \"ydpi\", ydpi,\n            \"cell_width\", cell_width, \"cell_height\", cell_height, \"is_layer_shell\", os_window->is_layer_shell ? Py_True : Py_False);\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(get_os_window_pos) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        int x, y;\n        get_os_window_pos(os_window, &x, &y);\n        return Py_BuildValue(\"ii\", x, y);\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(set_os_window_pos) {\n    id_type os_window_id;\n    int x, y;\n    PA(\"Kii\", &os_window_id, &x, &y);\n    WITH_OS_WINDOW(os_window_id)\n        set_os_window_pos(os_window, x, y);\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(set_boss) {\n    Py_CLEAR(global_state.boss);\n    global_state.boss = args;\n    Py_INCREF(global_state.boss);\n    Py_RETURN_NONE;\n}\n\nPYWRAP0(get_boss) {\n    if (global_state.boss) {\n        Py_INCREF(global_state.boss);\n        return global_state.boss;\n    }\n    Py_RETURN_NONE;\n}\n\nPYWRAP0(apply_options_update) {\n    for (size_t o = 0; o < global_state.num_os_windows; o++) {\n        OSWindow *os_window = global_state.os_windows + o;\n        get_platform_dependent_config_values(os_window->handle);\n        os_window->background_opacity.alpha = OPT(background_opacity);\n        set_os_window_chrome(os_window);\n        if (!os_window->redraw_count) os_window->redraw_count++;\n        for (size_t t = 0; t < os_window->num_tabs; t++) {\n            Tab *tab = os_window->tabs + t;\n            for (size_t w = 0; w < tab->num_windows; w++) {\n                Window *window = tab->windows + w;\n                if (window->window_logo.using_default) {\n                    set_window_logo(window, OPT(default_window_logo), OPT(window_logo_position), OPT(window_logo_alpha), true, NULL, 0);\n                }\n            }\n        }\n    }\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(patch_global_colors) {\n    PyObject *spec;\n    int configured;\n    if (!PyArg_ParseTuple(args, \"Op\", &spec, &configured)) return NULL;\n#define P(name) { \\\n    PyObject *val = PyDict_GetItemString(spec, #name); \\\n    if (val) { \\\n        if (val == Py_None) OPT(name) = 0; \\\n        else if (PyLong_Check(val)) OPT(name) = PyLong_AsLong(val); \\\n    } \\\n}\n    P(active_border_color); P(inactive_border_color); P(bell_border_color); P(tab_bar_background);\n    P(tab_bar_margin_color); P(macos_titlebar_color); P(wayland_titlebar_color);\n    P(scrollbar_handle_color); P(scrollbar_track_color);\n    if (configured) {\n        P(background); P(url_color);\n    }\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(update_tab_bar_edge_colors) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id)\n        if (os_window->tab_bar_render_data.screen) {\n            if (get_line_edge_colors(os_window->tab_bar_render_data.screen, &os_window->tab_bar_edge_color.left, &os_window->tab_bar_edge_color.right)) { Py_RETURN_TRUE; }\n        }\n    END_WITH_OS_WINDOW\n    Py_RETURN_FALSE;\n}\n\nstatic PyObject*\npyset_background_image(PyObject *self UNUSED, PyObject *args, PyObject *kw) {\n    const char *path;\n    PyObject *layout_name = NULL, *pylinear = NULL, *pytint = NULL, *pytint_gaps = NULL;\n    PyObject *os_window_ids;\n    int configured = 0;\n    char *png_data = NULL; Py_ssize_t png_data_size = 0;\n    static char *kwds[] = {\"path\", \"os_window_ids\", \"configured\", \"layout_name\", \"png_data\", \"linear\", \"tint\", \"tint_gaps\", NULL};\n    if (!PyArg_ParseTupleAndKeywords(args, kw, \"zO!|pOy#OOO\", kwds, &path, &PyTuple_Type, &os_window_ids, &configured, &layout_name, &png_data, &png_data_size, &pylinear, &pytint, &pytint_gaps)) return NULL;\n    size_t size;\n    BackgroundImageLayout layout = PyUnicode_Check(layout_name) ? bglayout(layout_name) : OPT(background_image_layout);\n    BackgroundImage *bgimage = NULL;\n    if (path) {\n        bgimage = calloc(1, sizeof(BackgroundImage));\n        if (!bgimage) return PyErr_NoMemory();\n        bool ok;\n        if (png_data && png_data_size) {\n            ok = png_from_data(png_data, png_data_size, path, &bgimage->bitmap, &bgimage->width, &bgimage->height, &size);\n        } else {\n            ok = image_path_to_bitmap(path, &bgimage->bitmap, &bgimage->width, &bgimage->height, &bgimage->mmap_size);\n        }\n        if (!ok) {\n            PyErr_Format(PyExc_ValueError, \"Failed to load image from: %s\", path);\n            free(bgimage);\n            return NULL;\n        }\n        static uint32_t bgimage_id_counter = 0;\n        bgimage->id = ++bgimage_id_counter;\n        send_bgimage_to_gpu(layout, bgimage);\n        bgimage->refcnt++;\n    }\n    if (configured) {\n        free_bgimage(&global_state.bgimage, true);\n        global_state.bgimage = bgimage;\n        if (bgimage) bgimage->refcnt++;\n        OPT(background_image_layout) = layout;\n        if (pylinear && pylinear != Py_None) convert_from_python_background_image_linear(pylinear, &global_state.opts);\n        if (pytint && pytint != Py_None) convert_from_python_background_tint(pytint, &global_state.opts);\n        if (pytint_gaps && pytint_gaps != Py_None) convert_from_python_background_tint_gaps(pytint_gaps, &global_state.opts);\n    }\n    for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(os_window_ids); i++) {\n        id_type os_window_id = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(os_window_ids, i));\n        WITH_OS_WINDOW(os_window_id)\n            make_os_window_context_current(os_window);\n            free_bgimage(&os_window->bgimage, true);\n            os_window->bgimage = bgimage;\n            os_window->render_calls = 0;\n            if (bgimage) bgimage->refcnt++;\n        END_WITH_OS_WINDOW\n    }\n    if (bgimage) free_bgimage(&bgimage, true);\n    Py_RETURN_NONE;\n}\n\nPYWRAP0(destroy_global_data) {\n    Py_CLEAR(global_state.boss);\n    free(global_state.os_windows); global_state.os_windows = NULL;\n    Py_RETURN_NONE;\n}\n\nPYWRAP0(wakeup_main_loop) {\n    wakeup_main_loop();\n    Py_RETURN_NONE;\n}\n\nstatic void\ndestroy_mock_window(PyObject *capsule) {\n    Window *w = PyCapsule_GetPointer(capsule, \"Window\");\n    if (w) {\n        destroy_window(w);\n        PyMem_Free(w);\n    }\n}\n\nstatic PyObject*\npycreate_mock_window(PyObject *self UNUSED, PyObject *args) {\n    Screen *screen;\n    PyObject *title = NULL;\n    if (!PyArg_ParseTuple(args, \"O|U\", &screen, &title)) return NULL;\n    Window *w = PyMem_Calloc(sizeof(Window), 1);\n    if (!w) return NULL;\n    Py_INCREF(screen);\n    PyObject *ans = PyCapsule_New(w, \"Window\", destroy_mock_window);\n    if (ans != NULL) {\n        initialize_window(w, title, false);\n        w->render_data.screen = screen;\n    }\n    return ans;\n}\n\nstatic bool\nclick_mouse_url(id_type os_window_id, id_type tab_id, id_type window_id) {\n    bool clicked = false;\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n    clicked = mouse_open_url(window);\n    END_WITH_WINDOW;\n    return clicked;\n}\n\nstatic bool\nclick_mouse_cmd_output(id_type os_window_id, id_type tab_id, id_type window_id, int select_cmd_output) {\n    bool handled = false;\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n    handled = mouse_set_last_visited_cmd_output(window);\n    if (select_cmd_output && handled) handled = mouse_select_cmd_output(window);\n    END_WITH_WINDOW;\n    return handled;\n}\n\nstatic bool\nmove_cursor_to_mouse_if_in_prompt(id_type os_window_id, id_type tab_id, id_type window_id) {\n    bool moved = false;\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n    moved = move_cursor_to_mouse_if_at_shell_prompt(window);\n    END_WITH_WINDOW;\n    return moved;\n}\n\nstatic PyObject*\npyupdate_pointer_shape(PyObject *self UNUSED, PyObject *args) {\n    id_type os_window_id;\n    PA(\"K\", &os_window_id);\n    WITH_OS_WINDOW(os_window_id);\n    OSWindow *orig = global_state.callback_os_window;\n    global_state.callback_os_window = os_window;\n    update_mouse_pointer_shape();\n    global_state.callback_os_window = orig;\n    END_WITH_OS_WINDOW;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\npymouse_selection(PyObject *self UNUSED, PyObject *args) {\n    id_type os_window_id, tab_id, window_id;\n    int code, button;\n    PA(\"KKKii\", &os_window_id, &tab_id, &window_id, &code, &button);\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n    mouse_selection(window, code, button);\n    END_WITH_WINDOW;\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(get_window_logo_settings_if_not_default) {\n    id_type os_window_id, tab_id, window_id;\n    PA(\"KKK\", &os_window_id, &tab_id, &window_id);\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n        if (window->window_logo.instance != NULL && window->window_logo.id && !window->window_logo.using_default) {\n            WindowLogo *wl = find_window_logo(global_state.all_window_logos, window->window_logo.id);\n            if (wl != NULL && wl->load_from_disk_ok) {\n                const char *path = window_logo_path_for_id(global_state.all_window_logos, window->window_logo.id);\n                if (path) {\n                    ImageAnchorPosition *p = &window->window_logo.position;\n                    return Py_BuildValue(\"sfN\", path, window->window_logo.alpha, Py_BuildValue(\n                        \"ffff\", p->image_x, p->image_y, p->canvas_x, p->canvas_y));\n                }\n            }\n        }\n    END_WITH_WINDOW;\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(set_window_logo) {\n    id_type os_window_id, tab_id, window_id;\n    const char *path; PyObject *position;\n    float alpha = 0.5;\n    char *png_data = NULL; Py_ssize_t png_data_size = 0;\n    PA(\"KKKsUf|y#\", &os_window_id, &tab_id, &window_id, &path, &position, &alpha, &png_data, &png_data_size);\n    bool ok = false;\n    WITH_WINDOW(os_window_id, tab_id, window_id);\n    ok = set_window_logo(window, path, PyObject_IsTrue(position) ? bganchor(position) : OPT(window_logo_position), (0 <= alpha && alpha <= 1) ? alpha : OPT(window_logo_alpha), false, png_data, png_data_size);\n    END_WITH_WINDOW;\n    if (ok) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\n\nPYWRAP1(click_mouse_url) {\n    id_type a, b, c; PA(\"KKK\", &a, &b, &c);\n    if (click_mouse_url(a, b, c)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(click_mouse_cmd_output) {\n    id_type a, b, c; int d; PA(\"KKKp\", &a, &b, &c, &d);\n    if (click_mouse_cmd_output(a, b, c, d)) { Py_RETURN_TRUE; }\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(move_cursor_to_mouse_if_in_prompt) {\n    id_type a, b, c; PA(\"KKK\", &a, &b, &c);\n    if (move_cursor_to_mouse_if_in_prompt(a, b, c)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nPYWRAP1(redirect_mouse_handling) {\n    global_state.redirect_mouse_handling = PyObject_IsTrue(args) ? true : false;\n    Py_RETURN_NONE;\n}\n\nPYWRAP1(buffer_keys_in_window) {\n    int enabled = 1;\n    id_type a, b, c; PA(\"KKK|p\", &a, &b, &c, &enabled);\n    if (buffer_keys_in_window(a, b, c, enabled)) Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\nTHREE_ID_OBJ(update_window_title)\nTHREE_ID(remove_window)\nTHREE_ID(detach_window)\nTHREE_ID(attach_window)\nPYWRAP1(add_tab) { return PyLong_FromUnsignedLongLong(add_tab(PyLong_AsUnsignedLongLong(args))); }\nPYWRAP1(add_window) { PyObject *title; id_type a, b; PA(\"KKO\", &a, &b, &title); return PyLong_FromUnsignedLongLong(add_window(a, b, title)); }\nPYWRAP0(current_os_window) { OSWindow *w = current_os_window(); if (!w) Py_RETURN_NONE; return PyLong_FromUnsignedLongLong(w->id); }\nTWO_ID(remove_tab)\nKI(set_active_tab)\nK(mark_os_window_dirty)\nKKK(set_active_window)\nKII(swap_tabs)\nKKKK(set_redirect_keys_to_overlay)\n\nstatic PyObject*\nos_window_focus_counters(PyObject *self UNUSED, PyObject *args UNUSED) {\n    RAII_PyObject(ans, PyDict_New());\n    for (size_t i = 0; i < global_state.num_os_windows; i++) {\n        OSWindow *w = &global_state.os_windows[i];\n        RAII_PyObject(key, PyLong_FromUnsignedLongLong(w->id));\n        RAII_PyObject(val, PyLong_FromUnsignedLongLong(w->last_focused_counter));\n        if (!key || !val) return NULL;\n        if (PyDict_SetItem(ans, key, val) != 0) return NULL;\n    }\n    Py_INCREF(ans);\n    return ans;\n}\n\nstatic PyObject*\nget_mouse_data_for_window(PyObject *self UNUSED, PyObject *args) {\n    id_type os_window_id, tab_id, window_id;\n    PA(\"KKK\", &os_window_id, &tab_id, &window_id);\n    WITH_WINDOW(os_window_id, tab_id, window_id)\n        return Py_BuildValue(\"{sI sI sO}\", \"cell_x\", window->mouse_pos.cell_x, \"cell_y\", window->mouse_pos.cell_y,\n                \"in_left_half_of_cell\", window->mouse_pos.in_left_half_of_cell ? Py_True: Py_False);\n    END_WITH_WINDOW\n    Py_RETURN_NONE;\n}\n\n#define tbd global_state.tab_being_dragged\nstatic PyObject*\nset_tab_being_dragged(PyObject *self UNUSED, PyObject *args) {\n    zero_at_ptr(&tbd);\n    if (!PyArg_ParseTuple(args, \"|Kpdd\", &tbd.id, &tbd.drag_started, &tbd.x, &tbd.y)) return NULL;\n    Py_RETURN_NONE;\n}\n\nstatic PyObject*\nget_tab_being_dragged(PyObject *self UNUSED, PyObject *args UNUSED) {\n    return Py_BuildValue(\"KOdd\", tbd.id, tbd.drag_started ? Py_True : Py_False, tbd.x, tbd.y);\n}\n#undef tbd\n\nstatic PyObject*\nrequest_callback_with_thumbnail(PyObject *self UNUSED, PyObject *args) {\n    unsigned long long os_window_id, window_id = 0;\n    const char *callback; int include_tab_bar = 0;\n    double scale = 0.25; unsigned max_width = 480;\n    if (!PyArg_ParseTuple(args, \"sK|KpdI\", &callback, &os_window_id, &window_id, &include_tab_bar, &scale, &max_width)) return NULL;\n    WITH_OS_WINDOW(os_window_id)\n        global_state.thumbnail_callback.os_window = os_window->id;\n        global_state.thumbnail_callback.window = window_id;\n        global_state.thumbnail_callback.include_tab_bar = include_tab_bar;\n        snprintf(global_state.thumbnail_callback.callback, arraysz(global_state.thumbnail_callback.callback), \"%s\", callback);\n        global_state.thumbnail_callback.max_width = max_width;\n        global_state.thumbnail_callback.scale = scale;\n        mark_os_window_dirty(os_window_id);\n    END_WITH_OS_WINDOW\n    Py_RETURN_NONE;\n}\n\n\n#define M(name, arg_type) {#name, (PyCFunction)name, arg_type, NULL}\n#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}\n\nstatic PyMethodDef module_methods[] = {\n    M(os_window_focus_counters, METH_NOARGS),\n    M(get_mouse_data_for_window, METH_VARARGS),\n    M(request_callback_with_thumbnail, METH_VARARGS),\n    M(set_tab_being_dragged, METH_VARARGS),\n    M(get_tab_being_dragged, METH_NOARGS),\n    MW(update_pointer_shape, METH_VARARGS),\n    MW(current_os_window, METH_NOARGS),\n    MW(next_window_id, METH_NOARGS),\n    MW(last_focused_os_window_id, METH_NOARGS),\n    MW(current_focused_os_window_id, METH_NOARGS),\n    MW(set_options, METH_VARARGS),\n    MW(get_options, METH_NOARGS),\n    MW(click_mouse_url, METH_VARARGS),\n    MW(click_mouse_cmd_output, METH_VARARGS),\n    MW(move_cursor_to_mouse_if_in_prompt, METH_VARARGS),\n    MW(redirect_mouse_handling, METH_O),\n    MW(mouse_selection, METH_VARARGS),\n    MW(set_window_logo, METH_VARARGS),\n    MW(get_window_logo_settings_if_not_default, METH_VARARGS),\n    MW(set_ignore_os_keyboard_processing, METH_O),\n    MW(handle_for_window_id, METH_VARARGS),\n    MW(update_ime_position_for_window, METH_VARARGS),\n    MW(pt_to_px, METH_VARARGS),\n    MW(add_tab, METH_O),\n    MW(add_window, METH_VARARGS),\n    MW(update_window_title, METH_VARARGS),\n    MW(remove_tab, METH_VARARGS),\n    MW(remove_window, METH_VARARGS),\n    MW(detach_window, METH_VARARGS),\n    MW(attach_window, METH_VARARGS),\n    MW(set_active_tab, METH_VARARGS),\n    MW(mark_os_window_dirty, METH_VARARGS),\n    MW(set_redirect_keys_to_overlay, METH_VARARGS),\n    MW(buffer_keys_in_window, METH_VARARGS),\n    MW(set_active_window, METH_VARARGS),\n    MW(swap_tabs, METH_VARARGS),\n    MW(reorder_tabs, METH_VARARGS),\n    MW(set_borders_rects, METH_VARARGS),\n    MW(set_tab_bar_render_data, METH_VARARGS),\n    MW(set_window_title_bar_render_data, METH_VARARGS),\n    MW(set_window_render_data, METH_VARARGS),\n    MW(set_window_padding, METH_VARARGS),\n    MW(viewport_for_window, METH_VARARGS),\n    MW(cell_size_for_window, METH_VARARGS),\n    MW(os_window_has_background_image, METH_VARARGS),\n    MW(mark_os_window_for_close, METH_VARARGS),\n    MW(set_application_quit_request, METH_VARARGS),\n    MW(current_application_quit_request, METH_NOARGS),\n    MW(set_os_window_chrome, METH_VARARGS),\n    MW(focus_os_window, METH_VARARGS),\n    MW(mark_tab_bar_dirty, METH_VARARGS),\n    MW(is_tab_bar_visible, METH_VARARGS),\n    MW(run_with_activation_token, METH_O),\n    MW(change_background_opacity, METH_VARARGS),\n    MW(background_opacity_of, METH_O),\n    MW(update_window_visibility, METH_VARARGS),\n    MW(sync_os_window_title, METH_VARARGS),\n    MW(get_os_window_title, METH_VARARGS),\n    MW(set_os_window_title, METH_VARARGS),\n    MW(get_os_window_pos, METH_VARARGS),\n    MW(set_os_window_pos, METH_VARARGS),\n    MW(global_font_size, METH_VARARGS),\n    {\"set_background_image\", (PyCFunction)(void (*) (void))pyset_background_image, METH_VARARGS | METH_KEYWORDS, \"\"},\n    MW(os_window_font_size, METH_VARARGS),\n    MW(set_os_window_size, METH_VARARGS),\n    MW(get_os_window_size, METH_VARARGS),\n    MW(os_window_is_invisible, METH_O),\n    MW(update_tab_bar_edge_colors, METH_VARARGS),\n    MW(set_boss, METH_O),\n    MW(get_boss, METH_NOARGS),\n    MW(apply_options_update, METH_NOARGS),\n    MW(patch_global_colors, METH_VARARGS),\n    MW(create_mock_window, METH_VARARGS),\n    MW(destroy_global_data, METH_NOARGS),\n    MW(wakeup_main_loop, METH_NOARGS),\n\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\nstatic void\nfinalize(void) {\n    while(detached_windows.num_windows--) {\n        destroy_window(&detached_windows.windows[detached_windows.num_windows]);\n    }\n    if (detached_windows.windows) free(detached_windows.windows);\n    detached_windows.capacity = 0;\n#define F(x) free(OPT(x)); OPT(x) = NULL;\n    F(background_image); F(bell_path); F(bell_theme); F(default_window_logo);\n#undef F\n    Py_CLEAR(global_state.options_object);\n    free_animation(OPT(animation.cursor));\n    free_animation(OPT(animation.visual_bell));\n    // we leak the texture here since it is not guaranteed\n    // that freeing the texture will work during shutdown and\n    // the GPU driver should take care of it when the OpenGL context is\n    // destroyed.\n    free_bgimage(&global_state.bgimage, false);\n    free_window_logo_table(&global_state.all_window_logos);\n    global_state.bgimage = NULL;\n    free_drag_source();\n    Py_CLEAR(global_state.drop_dest.data);\n    zero_at_ptr(&global_state.drop_dest);\n\n    free_allocs_in_options(&global_state.opts);\n}\n\nbool\ninit_state(PyObject *module) {\n    OPT(font_size) = 11.0;\n#ifdef __APPLE__\n#define DPI 72.0\n#else\n#define DPI 96.0\n#endif\n    global_state.default_dpi.x = DPI; global_state.default_dpi.y = DPI;\n    global_state.all_window_logos = alloc_window_logo_table();\n    if (!global_state.all_window_logos) { PyErr_NoMemory(); return false; }\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n    if (PyStructSequence_InitType2(&RegionType, &region_desc) != 0) return false;\n    Py_INCREF((PyObject *) &RegionType);\n    PyModule_AddObject(module, \"Region\", (PyObject *) &RegionType);\n    PyModule_AddIntConstant(module, \"IMPERATIVE_CLOSE_REQUESTED\", IMPERATIVE_CLOSE_REQUESTED);\n    PyModule_AddIntConstant(module, \"NO_CLOSE_REQUESTED\", NO_CLOSE_REQUESTED);\n    PyModule_AddIntConstant(module, \"CLOSE_BEING_CONFIRMED\", CLOSE_BEING_CONFIRMED);\n    PyModule_AddIntMacro(module, WINDOW_NORMAL);\n    PyModule_AddIntMacro(module, WINDOW_FULLSCREEN);\n    PyModule_AddIntMacro(module, WINDOW_MAXIMIZED);\n    PyModule_AddIntMacro(module, WINDOW_HIDDEN);\n    PyModule_AddIntMacro(module, WINDOW_MINIMIZED);\n    PyModule_AddIntMacro(module, LEFT_EDGE);\n    PyModule_AddIntMacro(module, RIGHT_EDGE);\n    PyModule_AddIntMacro(module, TOP_EDGE);\n    PyModule_AddIntMacro(module, BOTTOM_EDGE);\n    register_at_exit_cleanup_func(STATE_CLEANUP_FUNC, finalize);\n    return true;\n}\n// }}}\n"
  },
  {
    "path": "kitty/state.h",
    "content": "/*\n * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n#include \"data-types.h\"\n#include \"animation.h\"\n#include \"screen.h\"\n#include \"monotonic.h\"\n#include \"window_logo.h\"\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpedantic\"\n#include <hb.h>\n#pragma GCC diagnostic pop\n\n#define OPT(name) global_state.opts.name\n#define debug_rendering(...) if (global_state.debug_rendering) { timed_debug_print(__VA_ARGS__); }\n#define debug_input(...) if (OPT(debug_keyboard)) { timed_debug_print(__VA_ARGS__); }\n#define debug_fonts(...) if (global_state.debug_font_fallback) { timed_debug_print(__VA_ARGS__); }\n\ntypedef enum { LEFT_EDGE = 1, TOP_EDGE = 2, RIGHT_EDGE = 4, BOTTOM_EDGE = 8 } Edge;\ntypedef enum { REPEAT_MIRROR, REPEAT_CLAMP, REPEAT_DEFAULT } RepeatStrategy;\ntypedef enum { WINDOW_NORMAL, WINDOW_FULLSCREEN, WINDOW_MAXIMIZED, WINDOW_MINIMIZED, WINDOW_HIDDEN } WindowState;\n\ntypedef struct UrlPrefix {\n    char_type string[16];\n    size_t len;\n} UrlPrefix;\n\ntypedef enum AdjustmentUnit { POINT = 0, PERCENT = 1, PIXEL = 2 } AdjustmentUnit;\ntypedef enum UnderlineHyperlinks { UNDERLINE_ON_HOVER = 0, UNDERLINE_ALWAYS = 1, UNDERLINE_NEVER = 2 } UnderlineHyperlinks;\n\nstruct MenuItem {\n    const char* *location;\n    size_t location_count;\n    const char *definition;\n};\n\ntypedef struct Options {\n    monotonic_t visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, click_interval;\n    struct {\n        monotonic_t hide_wait, unhide_wait;\n        int unhide_threshold;\n        bool scroll_unhide;\n    } mouse_hide;\n    double wheel_scroll_multiplier, touch_scroll_multiplier;\n    int wheel_scroll_min_lines;\n    bool pixel_scroll;\n    bool enable_audio_bell;\n    CursorShape cursor_shape, cursor_shape_unfocused;\n    float cursor_beam_thickness;\n    float cursor_underline_thickness;\n    monotonic_t cursor_trail;\n    float cursor_trail_decay_fast;\n    float cursor_trail_decay_slow;\n    color_type cursor_trail_color;\n    float cursor_trail_start_threshold;\n    unsigned int url_style;\n    unsigned int scrollback_pager_history_size;\n    bool scrollback_fill_enlarged_window;\n    char_type *select_by_word_characters;\n    char_type *select_by_word_characters_forward;\n    color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color, tab_bar_background, tab_bar_margin_color,\n        window_title_bar_active_foreground, window_title_bar_active_background, window_title_bar_inactive_foreground, window_title_bar_inactive_background;\n    monotonic_t repaint_delay, input_delay;\n    bool focus_follows_mouse;\n    unsigned int hide_window_decorations;\n    bool macos_hide_from_tasks, macos_quit_when_last_window_closed, macos_window_resizable, macos_traditional_fullscreen;\n    unsigned int macos_option_as_alt;\n    float macos_thicken_font;\n    WindowTitleIn macos_show_window_title_in;\n    char *bell_path, *bell_theme;\n    float background_opacity, dim_opacity;\n\n    ScrollbarVisibilityPolicy scrollbar;\n    bool scrollbar_interactive, scrollbar_jump_on_click;\n    float scrollbar_width, scrollbar_radius, scrollbar_gap, scrollbar_min_handle_height, scrollbar_hitbox_expansion;\n    float scrollbar_hover_width, scrollbar_handle_opacity, scrollbar_track_opacity, scrollbar_track_hover_opacity;\n    color_type scrollbar_handle_color, scrollbar_track_color;\n\n    float text_contrast, text_gamma_adjustment;\n    bool text_old_gamma;\n\n    char *background_image, *default_window_logo;\n    BackgroundImageLayout background_image_layout;\n    ImageAnchorPosition window_logo_position;\n    bool background_image_linear;\n    float background_tint, background_tint_gaps, window_logo_alpha;\n    struct { float width, height; } window_logo_scale;\n\n    bool dynamic_background_opacity;\n    float inactive_text_alpha;\n    Edge tab_bar_edge;\n    DisableLigature disable_ligatures;\n    bool force_ltr;\n    bool resize_in_steps;\n    bool sync_to_monitor;\n    bool close_on_child_death;\n    bool window_alert_on_bell;\n    bool macos_dock_badge_on_bell;\n    bool debug_keyboard;\n    bool allow_hyperlinks;\n    struct { monotonic_t on_end, on_pause; } resize_debounce_time;\n    MouseShape pointer_shape_when_grabbed;\n    MouseShape default_pointer_shape;\n    MouseShape pointer_shape_when_dragging, pointer_shape_when_dragging_rectangle;\n    struct {\n        UrlPrefix *values;\n        size_t num, max_prefix_len;\n    } url_prefixes;\n    char_type *url_excluded_characters;\n    bool detect_urls;\n    bool tab_bar_hidden;\n    double font_size;\n    struct {\n        double outer, inner;\n    } tab_bar_margin_height;\n    long macos_menubar_title_max_length;\n    int macos_colorspace;\n    struct {\n        float val; AdjustmentUnit unit;\n    } underline_position, underline_thickness, strikethrough_position, strikethrough_thickness, cell_width, cell_height, baseline;\n    bool show_hyperlink_targets;\n    UnderlineHyperlinks underline_hyperlinks;\n    int background_blur;\n    long macos_titlebar_color;\n    unsigned long wayland_titlebar_color;\n    struct { struct MenuItem *entries; size_t count; } global_menu;\n    bool wayland_enable_ime;\n    struct {\n        size_t num;\n        struct {\n            const char *psname;\n            size_t num;\n            hb_feature_t *features;\n        } *entries;\n    } font_features;\n    struct { Animation *cursor, *visual_bell; } animation;\n    unsigned undercurl_style;\n    struct { float thickness; int unit; } underline_exclusion;\n    float box_drawing_scale[4];\n    double momentum_scroll;\n    double window_drag_tolerance;\n} Options;\n\ntypedef struct WindowLogoRenderData {\n    window_logo_id_t id;\n    WindowLogo *instance;\n    ImageAnchorPosition position;\n    float alpha;\n    bool using_default;\n} WindowLogoRenderData;\n\ntypedef struct {\n    unsigned int left, top, right, bottom;\n    struct {\n        unsigned int left, top, right, bottom;\n    } spaces;\n} WindowGeometry;\n\ntypedef struct WindowRenderData {\n    ssize_t vao_idx;\n    WindowGeometry geometry;\n    Screen *screen;\n} WindowRenderData;\n\ntypedef struct Click {\n    monotonic_t at;\n    int button, modifiers;\n    double x, y;\n    unsigned long num;\n} Click;\n\n#define CLICK_QUEUE_SZ 3\ntypedef struct ClickQueue {\n    Click clicks[CLICK_QUEUE_SZ];\n    unsigned int length;\n} ClickQueue;\n\ntypedef struct MousePosition {\n    unsigned int cell_x, cell_y;\n    double global_x, global_y;\n    bool in_left_half_of_cell;\n} MousePosition;\n\ntypedef struct PendingClick {\n    id_type window_id;\n    int button, count, modifiers;\n    bool grabbed;\n    monotonic_t at;\n    MousePosition mouse_pos;\n    unsigned long press_num;\n    double radius_for_multiclick;\n} PendingClick;\n\n\ntypedef struct WindowBarData {\n    unsigned width, height;\n    uint8_t *buf;\n    PyObject *last_drawn_title_object_id;\n    hyperlink_id_type hyperlink_id_for_title_object;\n    bool needs_render;\n} WindowBarData;\n\ntypedef struct Window {\n    id_type id;\n    bool visible;\n    PyObject *title;\n    WindowRenderData render_data;\n    WindowRenderData window_title_render_data;\n    WindowLogoRenderData window_logo;\n    MousePosition mouse_pos;\n    struct {\n        unsigned int left, top, right, bottom;\n    } padding;\n    ClickQueue click_queues[8];\n    monotonic_t last_drag_scroll_at;\n    uint32_t last_special_key_pressed;\n    WindowBarData title_bar_data, url_target_bar_data;\n    id_type redirect_keys_to_overlay;\n    struct {\n        bool enabled;\n        void *key_data;\n        size_t count, capacity;\n    } buffered_keys;\n    struct {\n        PendingClick *clicks;\n        size_t num, capacity;\n    } pending_clicks;\n    struct {\n        double thumb_top, thumb_bottom;\n        bool is_dragging;\n        double drag_start_y;\n        double drag_start_scrolled_by;\n        bool is_hovering;\n    } scrollbar;\n} Window;\n\ntypedef struct BorderRect {\n    float left, top, right, bottom;\n    struct { unsigned left, top, right, bottom; } px;\n    uint32_t color;\n    long long border_type;\n    bool horizontal;\n} BorderRect;\n\ntypedef struct BorderRects {\n    BorderRect *rect_buf;\n    unsigned int num_border_rects, capacity;\n    bool is_dirty;\n    ssize_t vao_idx;\n} BorderRects;\n\ntypedef struct CursorTrail {\n    bool needs_render;\n    monotonic_t updated_at;\n    float opacity;\n    float corner_x[4];\n    float corner_y[4];\n    float cursor_edge_x[2];\n    float cursor_edge_y[2];\n} CursorTrail;\n\ntypedef struct Tab {\n    id_type id;\n    unsigned int active_window, num_windows, capacity;\n    Window *windows;\n    BorderRects border_rects;\n    CursorTrail cursor_trail;\n} Tab;\n\nenum RENDER_STATE { RENDER_FRAME_NOT_REQUESTED, RENDER_FRAME_REQUESTED, RENDER_FRAME_READY };\ntypedef enum { NO_CLOSE_REQUESTED, CONFIRMABLE_CLOSE_REQUESTED, CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED } CloseRequest;\n\ntypedef struct LiveResizeInfo {\n    monotonic_t last_resize_event_at;\n    bool in_progress;\n    bool from_os_notification;\n    bool os_says_resize_complete;\n    unsigned int width, height, num_of_resize_events;\n} LiveResizeInfo;\n\ntypedef struct WindowChromeState {\n    color_type color;\n    bool use_system_color;\n    unsigned system_color;\n    int background_blur;\n    unsigned hide_window_decorations;\n    bool show_title_in_titlebar;\n    bool resizable;\n    int macos_colorspace;\n    float background_opacity;\n} WindowChromeState;\n\ntypedef struct BackgroundImageRenderSettings {\n    struct { unsigned width, height; } os_window;\n    unsigned instance_id;\n    BackgroundImageLayout layout;\n    bool linear; uint32_t bgcolor; float opacity;\n} BackgroundImageRenderSettings;\n\ntypedef struct OSWindow {\n    void *handle;\n    id_type id;\n    monotonic_t created_at;\n    struct {\n        int x, y, w, h;\n        bool is_set, was_maximized;\n    } before_fullscreen;\n    int viewport_width, viewport_height, window_width, window_height;\n    double viewport_x_ratio, viewport_y_ratio;\n    Tab *tabs;\n    BackgroundImage *bgimage;\n    struct {\n        uint32_t framebuffer_id, attached_texture_generation;\n    } indirect_output;\n    unsigned int active_tab, num_tabs, capacity, last_active_tab, last_num_tabs, last_active_window_id;\n    bool focused_at_last_render, needs_render, needs_layers;\n    unsigned keep_rendering_till_swap;\n    WindowRenderData tab_bar_render_data;\n    struct {\n        color_type left, right;\n    } tab_bar_edge_color;\n    bool tab_bar_data_updated;\n    bool is_focused;\n    monotonic_t cursor_blink_zero_time, last_mouse_activity_at, mouse_activate_deadline;\n    int mouse_show_threshold;\n    bool has_received_cursor_pos_event;\n    double mouse_x, mouse_y;\n    bool mouse_button_pressed[32];\n    bool has_too_few_tabs;\n    bool suppress_left_mouse_release;\n    PyObject *window_title;\n    bool disallow_title_changes, title_is_overriden;\n    bool viewport_size_dirty, viewport_updated_at_least_once;\n    monotonic_t viewport_resized_at;\n    LiveResizeInfo live_resize;\n    bool has_pending_resizes, shown_once, ignore_resize_events;\n    unsigned int redraw_count;\n    WindowChromeState last_window_chrome;\n    struct { float alpha; bool os_forces_opaque, supports_transparency; } background_opacity;\n    FONTS_DATA_HANDLE fonts_data;\n    id_type temp_font_group_id;\n    enum RENDER_STATE render_state;\n    monotonic_t last_render_frame_received_at;\n    uint64_t render_calls;\n    id_type last_focused_counter;\n    CloseRequest close_request;\n    bool is_layer_shell, hide_on_focus_loss;\n    struct { int x, y; } last_drag_event;\n} OSWindow;\n\nstatic inline float\neffective_os_window_alpha(OSWindow *w) {\n    return (!w->background_opacity.supports_transparency || w->background_opacity.os_forces_opaque) ?\n        1.f : w->background_opacity.alpha;\n}\n\ntypedef struct GlobalState {\n    Options opts;\n\n    id_type os_window_id_counter, tab_id_counter, window_id_counter;\n    PyObject *boss;\n    BackgroundImage *bgimage;\n    OSWindow *os_windows;\n    size_t num_os_windows, capacity;\n    OSWindow *callback_os_window;\n    bool is_wayland, is_apple;\n    bool has_render_frames;\n    bool debug_rendering, debug_font_fallback;\n    bool has_pending_resizes, has_pending_closes;\n    bool check_for_active_animated_images;\n    struct { double x, y; } default_dpi;\n    id_type active_drag_in_window, tracked_drag_in_window, mouse_hover_in_window, active_drag_resize;\n    int active_drag_button, tracked_drag_button;\n    CloseRequest quit_request;\n    bool redirect_mouse_handling;\n    WindowLogoTable *all_window_logos;\n    int gl_version;\n    bool supports_framebuffer_srgb;\n    PyObject *options_object;\n\n    struct {\n        PyObject *data;\n        id_type os_window_id;\n        double x, y;\n        size_t num_left;\n    } drop_dest;\n\n    struct {\n        bool is_active, was_dropped, was_canceled, needs_toplevel_on_wayland;\n        char *accepted_mime_type;\n        int action, thumbnail_idx;\n        PyObject *drag_data, *thumbnails;\n    } drag_source;\n    struct {\n        id_type os_window, window;\n        char callback[32];\n        bool include_tab_bar;\n        double scale; unsigned max_width;\n    } thumbnail_callback;\n    struct {\n        id_type id; bool drag_started;\n        double x, y;\n    } tab_being_dragged;\n    struct {\n        uint32_t texture_id, framebuffer_id, texture_generation;\n        int width, height;\n    } layers_render_texture;\n} GlobalState;\n\nextern GlobalState global_state;\n\n#define call_boss(name, ...) if (global_state.boss) { \\\n    PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \\\n    if (cret_ == NULL) { PyErr_Print(); } \\\n    else Py_DECREF(cret_); \\\n}\n\nstatic inline void\nsprite_index_to_pos(unsigned idx, unsigned xnum, unsigned ynum, unsigned *x, unsigned *y, unsigned *z) {\n    div_t r = div(idx & 0x7fffffff, ynum * xnum), r2 = div(r.rem, xnum);\n    *z = r.quot; *y = r2.quot; *x = r2.rem;\n}\n\n\nvoid gl_init(void);\nvoid remove_vao(ssize_t vao_idx);\nbool remove_os_window(id_type os_window_id);\nvoid* make_os_window_context_current(OSWindow *w);\nvoid set_os_window_size(OSWindow *os_window, int x, int y);\nvoid get_os_window_size(OSWindow *os_window, int *w, int *h, int *fw, int *fh);\nvoid get_os_window_pos(OSWindow *os_window, int *x, int *y);\nvoid set_os_window_pos(OSWindow *os_window, int x, int y);\nvoid get_os_window_content_scale(OSWindow *os_window, double *xdpi, double *ydpi, float *xscale, float *yscale);\nvoid update_os_window_references(void);\nvoid mark_os_window_for_close(OSWindow* w, CloseRequest cr);\nvoid update_os_window_viewport(OSWindow *window, bool notify_boss);\nbool should_os_window_be_rendered(OSWindow* w);\nvoid wakeup_main_loop(void);\nbool make_window_context_current(id_type);\nvoid hide_mouse(OSWindow *w);\nbool is_mouse_hidden(OSWindow *w);\nvoid destroy_os_window(OSWindow *w);\nvoid focus_os_window(OSWindow *w, bool also_raise, const char *activation_token);\nvoid run_with_activation_token_in_os_window(OSWindow *w, PyObject *callback);\nvoid set_os_window_title(OSWindow *w, const char *title);\nOSWindow* os_window_for_kitty_window(id_type);\nOSWindow* os_window_for_id(id_type);\nOSWindow* add_os_window(void);\nOSWindow* current_os_window(void);\nvoid os_window_regions(const OSWindow*, Region *main, Region *tab_bar);\nbool drag_scroll(Window *, OSWindow*);\nvoid draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, color_type, unsigned int, bool, OSWindow *w);\nssize_t create_cell_vao(void);\nssize_t create_graphics_vao(void);\nssize_t create_border_vao(void);\nbool send_cell_data_to_gpu(ssize_t, Screen *, OSWindow *);\nvoid draw_cells(const WindowRenderData*, OSWindow *, bool, bool, bool, Window*);\nbool update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_window);\nvoid set_gpu_viewport(unsigned w, unsigned h);\nvoid free_texture(uint32_t*);\nvoid free_framebuffer(uint32_t*);\nvoid send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool, bool, RepeatStrategy);\nvoid send_sprite_to_gpu(FONTS_DATA_HANDLE fg, sprite_index, pixel*, sprite_index);\nvoid blank_canvas(float, color_type, bool);\nvoid blank_os_window(OSWindow *);\nvoid set_os_window_chrome(OSWindow *w);\nFONTS_DATA_HANDLE load_fonts_data(double, double, double);\nvoid send_prerendered_sprites_for_window(OSWindow *w);\n#ifdef __APPLE__\n#include \"cocoa_window.h\"\n#endif\nvoid request_frame_render(OSWindow *w);\nvoid request_tick_callback(void);\ntypedef void (* timer_callback_fun)(id_type, void*);\ntypedef void (* tick_callback_fun)(void*);\nid_type add_main_loop_timer(monotonic_t interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback);\nvoid remove_main_loop_timer(id_type timer_id);\nvoid update_main_loop_timer(id_type timer_id, monotonic_t interval, bool enabled);\nvoid run_main_loop(tick_callback_fun, void*);\nvoid stop_main_loop(void);\nvoid on_os_window_font_size_change(OSWindow *window, double new_sz);\nvoid set_os_window_title_from_window(Window *w, OSWindow *os_window);\nvoid update_os_window_title(OSWindow *os_window);\nvoid fake_scroll(Window *w, int amount, bool upwards);\nWindow* window_for_window_id(id_type kitty_window_id);\nbool mouse_open_url(Window *w);\nbool mouse_set_last_visited_cmd_output(Window *w);\nbool mouse_select_cmd_output(Window *w);\nbool move_cursor_to_mouse_if_at_shell_prompt(Window *w);\nvoid mouse_selection(Window *w, int code, int button);\nconst char* format_mods(unsigned mods);\nvoid dispatch_pending_clicks(id_type, void*);\nvoid send_pending_click_to_window(Window*, int);\nvoid get_platform_dependent_config_values(void *glfw_window);\nbool draw_window_title(double, double, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height);\nuint8_t* draw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height);\nbool is_os_window_fullscreen(OSWindow *);\nvoid update_ime_focus(OSWindow* osw, bool focused);\nvoid update_ime_position(Window* w, Screen *screen);\nbool update_ime_position_for_window(id_type window_id, bool force, int update_focus);\nvoid set_ignore_os_keyboard_processing(bool enabled);\nvoid update_menu_bar_title(PyObject *title UNUSED);\nvoid change_live_resize_state(OSWindow*, bool);\nbool render_os_window(OSWindow *w, monotonic_t now, bool scan_for_animated_images);\nvoid update_mouse_pointer_shape(void);\nvoid adjust_window_size_for_csd(OSWindow *w, int width, int height, int *adjusted_width, int *adjusted_height);\nvoid dispatch_buffered_keys(Window *w);\nbool screen_needs_rendering_in_layers(OSWindow *os_window, Window *w, Screen *screen);\nvoid setup_os_window_for_rendering(OSWindow*, Tab*, Window*, bool);\nvoid swap_window_buffers(OSWindow *w);\nvoid take_screenshot_of_rectangular_region(OSWindow *os_window, Region region, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h);\nbool current_framebuffer_is_ok(void);\n"
  },
  {
    "path": "kitty/systemd.c",
    "content": "/*\n * systemd.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\n#include \"cleanup.h\"\n#include <dlfcn.h>\n\n#define FUNC(name, restype, ...) typedef restype (*name##_func)(__VA_ARGS__); static name##_func name = NULL\n#define LOAD_FUNC(name) {\\\n    *(void **) (&name) = dlsym(systemd.lib, #name); \\\n    if (!name) { \\\n        const char* error = dlerror(); \\\n        if (error != NULL) { \\\n            log_error(\"Failed to load the function %s with error: %s\", #name, error); return; \\\n        } \\\n    } \\\n}\n\ntypedef struct sd_bus sd_bus;\n\nstatic struct {\n    void *lib;\n    sd_bus *user_bus;\n    bool initialized, functions_loaded, ok;\n} systemd = {0};\n\ntypedef struct {\n    const char *name;\n    const char *message;\n    int _need_free;\n    int64_t filler;  // just in case systemd ever increases the size of this struct\n} sd_bus_error;\ntypedef struct sd_bus_message sd_bus_message;\n\nFUNC(sd_bus_default_user, int, sd_bus**);\nFUNC(sd_bus_message_unref, sd_bus_message*, sd_bus_message*);\nFUNC(sd_bus_error_free, void, sd_bus_error*);\nFUNC(sd_bus_unref, sd_bus*, sd_bus*);\nFUNC(sd_bus_message_new_method_call, int, sd_bus *, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member);\nFUNC(sd_bus_message_append, int, sd_bus_message *m, const char *types, ...);\nFUNC(sd_bus_message_open_container, int, sd_bus_message *m, char type, const char *contents);\nFUNC(sd_bus_message_close_container, int, sd_bus_message *m);\nFUNC(sd_pid_get_user_slice, int, pid_t pid, char **slice);\nFUNC(sd_bus_call, int, sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply);\n\nstatic void\nensure_initialized(void) {\n    if (systemd.initialized) return;\n    systemd.initialized = true;\n\n    const char* libnames[] = {\n#if defined(_KITTY_SYSTEMD_LIBRARY)\n        _KITTY_SYSTEMD_LIBRARY,\n#else\n        \"libsystemd.so\",\n        // some installs are missing the .so symlink, so try the full name\n        \"libsystemd.so.0\",\n        \"libsystemd.so.0.38.0\",\n#endif\n        NULL\n    };\n    for (int i = 0; libnames[i]; i++) {\n        systemd.lib = dlopen(libnames[i], RTLD_LAZY);\n        if (systemd.lib) break;\n    }\n    if (systemd.lib == NULL) {\n        log_error(\"Failed to load %s with error: %s\\n\", libnames[0], dlerror());\n        return;\n    }\n    LOAD_FUNC(sd_bus_default_user);\n    LOAD_FUNC(sd_bus_message_unref);\n    LOAD_FUNC(sd_bus_error_free);\n    LOAD_FUNC(sd_bus_unref);\n    LOAD_FUNC(sd_bus_message_new_method_call);\n    LOAD_FUNC(sd_bus_message_append);\n    LOAD_FUNC(sd_bus_message_open_container);\n    LOAD_FUNC(sd_bus_message_close_container);\n    LOAD_FUNC(sd_pid_get_user_slice);\n    LOAD_FUNC(sd_bus_call);\n    systemd.functions_loaded = true;\n\n    int ret = sd_bus_default_user(&systemd.user_bus);\n    if (ret < 0) { log_error(\"Failed to open systemd user bus with error: %s\", strerror(-ret)); return; }\n    systemd.ok = true;\n}\n\nstatic inline void err_cleanup(sd_bus_error *p) { sd_bus_error_free(p); }\n#define RAII_bus_error(name) __attribute__((cleanup(err_cleanup))) sd_bus_error name = {0};\nstatic inline void msg_cleanup(sd_bus_message **p) { sd_bus_message_unref(*p); }\n#define RAII_message(name) __attribute__((cleanup(msg_cleanup))) sd_bus_message *name = NULL;\n\n#define SYSTEMD_DESTINATION \"org.freedesktop.systemd1\"\n#define SYSTEMD_PATH \"/org/freedesktop/systemd1\"\n#define SYSTEMD_INTERFACE \"org.freedesktop.systemd1.Manager\"\n\nstatic bool\nset_systemd_error(int r, const char *msg) {\n    RAII_PyObject(m, PyUnicode_FromFormat(\"Failed to %s: %s\", msg, strerror(-r)));\n    if (m) {\n        RAII_PyObject(e, Py_BuildValue(\"(iO)\", -r, m));\n        if (e) PyErr_SetObject(PyExc_OSError, e);\n    }\n    return false;\n}\n\nstatic bool\nset_reply_error(const char* func_name, int r, const sd_bus_error *err) {\n    RAII_PyObject(m, PyUnicode_FromFormat(\"Failed to call %s: %s: %s\", func_name, err->name, err->message));\n    if (m) {\n        RAII_PyObject(e, Py_BuildValue(\"(iO)\", -r, m));\n        if (e) PyErr_SetObject(PyExc_OSError, e);\n    }\n    return false;\n}\n\nstatic bool\nmove_pid_into_new_scope(pid_t pid, const char* scope_name, const char *description) {\n    pid_t parent_pid = getpid();\n    RAII_bus_error(err); RAII_message(m); RAII_message(reply);\n    int r;\n#define checked_call(func, ...) if ((r = func(__VA_ARGS__)) < 0) { return set_systemd_error(r, #func); }\n    checked_call(sd_bus_message_new_method_call, systemd.user_bus, &m, SYSTEMD_DESTINATION, SYSTEMD_PATH, SYSTEMD_INTERFACE, \"StartTransientUnit\");\n    // mode is \"fail\" which means it will fail if a unit with scope_name already exists\n    checked_call(sd_bus_message_append, m, \"ss\", scope_name, \"fail\");\n    checked_call(sd_bus_message_open_container, m, 'a', \"(sv)\");\n    if (description && description[0]) {\n        checked_call(sd_bus_message_append, m, \"(sv)\", \"Description\", \"s\", description);\n    }\n    RAII_ALLOC(char, slice, NULL);\n    if (sd_pid_get_user_slice(parent_pid, &slice) >= 0) {\n        checked_call(sd_bus_message_append, m, \"(sv)\", \"Slice\", \"s\", slice);\n    } else {\n        // Fallback\n        checked_call(sd_bus_message_append, m, \"(sv)\", \"Slice\", \"s\", \"kitty.slice\");\n    }\n\n    // Add the PID to this scope\n    checked_call(sd_bus_message_open_container, m, 'r', \"sv\");\n    checked_call(sd_bus_message_append, m, \"s\", \"PIDs\");\n    checked_call(sd_bus_message_open_container, m, 'v', \"au\");\n    checked_call(sd_bus_message_open_container, m, 'a', \"u\");\n    checked_call(sd_bus_message_append, m, \"u\", pid);\n    checked_call(sd_bus_message_close_container, m); // au\n    checked_call(sd_bus_message_close_container, m); // v\n    checked_call(sd_bus_message_close_container, m); // (sv)\n\n    // If something in this process group is OOMkilled dont kill the rest of\n    // the process group. Since typically the shell is not causing the OOM\n    // something being run inside it is.\n    checked_call(sd_bus_message_append, m, \"(sv)\", \"OOMPolicy\", \"s\", \"continue\");\n\n    // Make sure shells are terminated with SIGHUP not just SIGTERM\n    checked_call(sd_bus_message_append, m, \"(sv)\", \"SendSIGHUP\", \"b\", true);\n\n    // Unload this unit in failed state as well\n    checked_call(sd_bus_message_append, m, \"(sv)\", \"CollectMode\", \"s\", \"inactive-or-failed\");\n\n    // Only kill the main process on stop\n    checked_call(sd_bus_message_append, m, \"(sv)\", \"KillMode\", \"s\", \"process\");\n\n    checked_call(sd_bus_message_close_container, m); // End properties a(sv)\n                                                     //\n    checked_call(sd_bus_message_append, m, \"a(sa(sv))\", 0);  // No auxiliary units\n                                                             //\n    if ((r=sd_bus_call(systemd.user_bus, m, 0 /* timeout default */, &err, &reply)) < 0) return set_reply_error(\"StartTransientUnit\", r, &err);\n\n    return true;\n#undef checked_call\n}\n\nstatic void\nfinalize(void) {\n    if (systemd.user_bus) sd_bus_unref(systemd.user_bus);\n    if (systemd.lib) dlclose(systemd.lib);\n    memset(&systemd, 0, sizeof(systemd));\n}\n\nstatic bool\nensure_initialized_and_useable(void) {\n    ensure_initialized();\n    if (!systemd.ok) {\n        if (!systemd.lib) PyErr_SetString(PyExc_NotImplementedError, \"Could not load libsystemd\");\n        else if (!systemd.functions_loaded) PyErr_SetString(PyExc_NotImplementedError, \"Could not load libsystemd functions\");\n        else PyErr_SetString(PyExc_NotImplementedError, \"Could not connect to systemd user bus\");\n        return false;\n    }\n    return true;\n}\n\nstatic PyObject*\nsystemd_move_pid_into_new_scope(PyObject *self UNUSED, PyObject *args) {\n    long pid; const char *scope_name, *description;\n    if (!PyArg_ParseTuple(args, \"lss\", &pid, &scope_name, &description)) return NULL;\n#ifdef __APPLE__\n    (void)ensure_initialized_and_useable; (void)move_pid_into_new_scope;\n    PyErr_SetString(PyExc_NotImplementedError, \"not supported on this platform\");\n#else\n    if (!ensure_initialized_and_useable()) return NULL;\n    move_pid_into_new_scope(pid, scope_name, description);\n#endif\n    if (PyErr_Occurred()) return NULL;\n    Py_RETURN_NONE;\n}\n\n\nstatic PyMethodDef module_methods[] = {\n    METHODB(systemd_move_pid_into_new_scope, METH_VARARGS),\n    {NULL, NULL, 0, NULL}        /* Sentinel */\n};\n\n\nbool\ninit_systemd_module(PyObject *module) {\n    register_at_exit_cleanup_func(SYSTEMD_CLEANUP_FUNC, finalize);\n    if (PyModule_AddFunctions(module, module_methods) != 0) return false;\n\n    return true;\n}\n"
  },
  {
    "path": "kitty/tab_bar.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nfrom collections.abc import Callable, Sequence\nfrom functools import lru_cache, partial, wraps\nfrom string import Formatter as StringFormatter\nfrom typing import (\n    Any,\n    NamedTuple,\n)\n\nfrom .borders import Border, BorderColor\nfrom .constants import config_dir\nfrom .fast_data_types import (\n    BOTTOM_EDGE,\n    DECAWM,\n    Color,\n    Region,\n    Screen,\n    background_opacity_of,\n    cell_size_for_window,\n    get_boss,\n    get_options,\n    pt_to_px,\n    set_tab_bar_render_data,\n    update_tab_bar_edge_colors,\n    viewport_for_window,\n    wcswidth,\n)\nfrom .progress import ProgressState\nfrom .rgb import alpha_blend, color_as_sgr, color_from_int, to_color\nfrom .types import WindowGeometry, run_once\nfrom .typing_compat import EdgeLiteral, PowerlineStyle\nfrom .utils import color_as_int, log_error, sgr_sanitizer_pat\n\n\nclass TabBarData(NamedTuple):\n    title: str\n    is_active: bool\n    needs_attention: bool\n    tab_id: int\n    os_window_id: int\n    num_windows: int\n    num_window_groups: int\n    layout_name: str\n    has_activity_since_last_focus: bool\n    active_fg: int | None\n    active_bg: int | None\n    inactive_fg: int | None\n    inactive_bg: int | None\n    num_of_windows_with_progress: int\n    total_progress: int\n    last_focused_window_with_progress_id: int\n    session_name: str\n    active_session_name: str\n\n\nclass DrawData(NamedTuple):\n    leading_spaces: int\n    sep: str\n    trailing_spaces: int\n    bell_on_tab: str\n    alpha: Sequence[float]\n    active_fg: Color\n    active_bg: Color\n    inactive_fg: Color\n    inactive_bg: Color\n    default_bg: Color\n    title_template: str\n    active_title_template: str | None\n    tab_activity_symbol: str\n    powerline_style: PowerlineStyle\n    tab_bar_edge: EdgeLiteral\n    max_tab_title_length: int\n    os_window_id: int\n\n    def tab_fg(self, tab: TabBarData) -> int:\n        if tab.is_active:\n            if tab.active_fg is not None:\n                return tab.active_fg\n            return int(self.active_fg)\n        if tab.inactive_fg is not None:\n            return tab.inactive_fg\n        return int(self.inactive_fg)\n\n    def tab_bg(self, tab: TabBarData) -> int:\n        if tab.is_active:\n            if tab.active_bg is not None:\n                return tab.active_bg\n            return int(self.active_bg)\n        if tab.inactive_bg is not None:\n            return tab.inactive_bg\n        return int(self.inactive_bg)\n\n\ndef as_rgb(x: int) -> int:\n    return (x << 8) | 2\n\n\n@lru_cache\ndef report_template_failure(template: str, e: str) -> None:\n    log_error(f'Invalid tab title template: \"{template}\" with error: {e}')\n\n\n@lru_cache\ndef compile_template(template: str) -> Any:\n    try:\n        return compile('f\"\"\"' + template + '\"\"\"', '<template>', 'eval')\n    except Exception as e:\n        report_template_failure(template, str(e))\n\n\nclass ColorFormatter:\n\n    draw_data: DrawData\n    tab_data: TabBarData\n\n    def __init__(self, which: str):\n        self.which = which\n\n    def __getattr__(self, name: str) -> str:\n        q = name\n        if q == 'default':\n            ans = '9'\n        elif q == 'tab':\n            col = color_from_int((self.draw_data.tab_bg if self.which == '4' else self.draw_data.tab_fg)(self.tab_data))\n            ans = f'8{color_as_sgr(col)}'\n        elif q.startswith('color'):\n            ans = f'8:5:{int(q[5:])}'\n        else:\n            if name.startswith('_'):\n                q = f'#{name[1:]}'\n            c = to_color(q)\n            if c is None:\n                raise AttributeError(f'{name} is not a valid color')\n            ans = f'8{color_as_sgr(c)}'\n        return f'\\x1b[{self.which}{ans}m'\n\n\nclass Formatter:\n    reset = '\\x1b[0m'\n    fg = ColorFormatter('3')\n    bg = ColorFormatter('4')\n    bold = '\\x1b[1m'\n    nobold = '\\x1b[22m'\n    italic = '\\x1b[3m'\n    noitalic = '\\x1b[23m'\n\n\n@run_once\ndef super_sub_maps() -> tuple[dict[int, int], dict[int, int]]:\n    import string\n    sup_table = str.maketrans(\n        string.ascii_lowercase + string.ascii_uppercase + string.digits + '+-=()',\n        'ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖqʳˢᵗᵘᵛʷˣʸᶻ' 'ᴬᴮᶜᴰᴱᶠᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾQᴿˢᵀᵁⱽᵂˣʸᶻ' '⁰¹²³⁴⁵⁶⁷⁸⁹' '⁺⁻⁼⁽⁾')\n    sub_table = str.maketrans(\n        string.ascii_lowercase + string.ascii_uppercase + string.digits + '+-=()',\n        'ₐbcdₑfgₕᵢⱼₖₗₘₙₒₚqᵣₛₜᵤᵥwₓyz' 'ₐbcdₑfgₕᵢⱼₖₗₘₙₒₚqᵣₛₜᵤᵥwₓyz' '₀₁₂₃₄₅₆₇₈₉' '₊₋₌₍₎')\n    return sup_table, sub_table\n\n\nclass SupSub:\n\n    def __init__(self, data: dict[str, Any], is_subscript: bool = False):\n        self.__data = data\n        self.__is_subscript = is_subscript\n\n    def __getattr__(self, name: str) -> str:\n        name = str(self.__data.get(name, name))\n        table = super_sub_maps()[int(self.__is_subscript)]\n        return name.translate(table)\n\n\nclass ExtraData:\n    prev_tab: TabBarData | None = None\n    next_tab: TabBarData | None = None\n    # true if the draw_tab function is called just for layout. In such cases,\n    # if drawing is expensive the draw_tab function should avoid drawing and\n    # just move the cursor to its final position, as if drawing was performed.\n    for_layout: bool = False\n\n\ndef draw_attributed_string(title: str, screen: Screen) -> None:\n    if '\\x1b' in title:\n        for x in sgr_sanitizer_pat(for_splitting=True).split(title):\n            if x.startswith('\\x1b') and x.endswith('m'):\n                screen.apply_sgr(x[2:-1])\n            else:\n                screen.draw(x)\n    else:\n        screen.draw(title)\n\n\n@lru_cache(maxsize=16)\ndef template_has_field(template: str, field: str) -> bool:\n    q = StringFormatter()\n    for (literal_text, field_name, format_spec, conversion) in q.parse(template):\n        if field_name and field in field_name.split():\n            return True\n    return False\n\n\nclass TabAccessor:\n\n    def __init__(self, tab_id: int):\n        self.tab_id = tab_id\n\n    @property\n    def active_wd(self) -> str:\n        tab = get_boss().tab_for_id(self.tab_id)\n        return (tab.get_cwd_of_active_window() if tab else '') or ''\n\n    @property\n    def active_oldest_wd(self) -> str:\n        tab = get_boss().tab_for_id(self.tab_id)\n        return (tab.get_cwd_of_active_window(oldest=True) if tab else '') or ''\n\n    @property\n    def active_exe(self) -> str:\n        tab = get_boss().tab_for_id(self.tab_id)\n        return os.path.basename((tab.get_exe_of_active_window() if tab else '') or '')\n\n    @property\n    def active_oldest_exe(self) -> str:\n        tab = get_boss().tab_for_id(self.tab_id)\n        return os.path.basename((tab.get_exe_of_active_window(oldest=True) if tab else '') or '')\n\n    @property\n    def last_focused_progress_percent(self) -> str:\n        tab = get_boss().tab_for_id(self.tab_id)\n        if not tab or not tab.last_focused_window_with_progress_id:\n            return ''\n        w = get_boss().window_id_map.get(tab.last_focused_window_with_progress_id)\n        if w is None:\n            return ''\n        if w.progress.state is ProgressState.error:\n            return '  '\n        if w.progress.state is ProgressState.unset:\n            return ''\n        if w.progress.state is ProgressState.indeterminate:\n            return '  '\n        p = f'{w.progress.percent}% '\n        if w.progress.state is ProgressState.paused:\n            return f'  {p}'\n        return p\n\n    @property\n    def progress_percent(self) -> str:\n        tab = get_boss().tab_for_id(self.tab_id)\n        if not tab or not tab.last_focused_window_with_progress_id:\n            return ''\n        if tab.num_of_windows_with_progress <= 1:\n            return self.last_focused_progress_percent\n        p = int(tab.total_progress / tab.num_of_windows_with_progress)\n        return f'{p}% '\n\n\n\nsafe_builtins = {\n    'max': max, 'min': min, 'str': str, 'repr': repr, 'abs': abs, 'len': len, 'chr': chr, 'ord': ord, 're': re,\n    'wcswidth': wcswidth,\n}\n\n\ndef apply_title_template(draw_data: DrawData, tab: TabBarData, index: int, max_title_length: int = 0) -> str:\n    ta = TabAccessor(tab.tab_id)\n    data = {\n        'index': index,\n        'layout_name': tab.layout_name,\n        'num_windows': tab.num_windows,\n        'num_window_groups': tab.num_window_groups,\n        'title': tab.title,\n        'tab': ta,\n    }\n    if draw_data.max_tab_title_length > 0:\n        max_title_length = min(max_title_length, draw_data.max_tab_title_length)\n    ColorFormatter.draw_data = draw_data\n    ColorFormatter.tab_data = tab\n    boss = get_boss()\n    eval_locals = {\n        'index': index,\n        'layout_name': tab.layout_name,\n        'session_name': tab.session_name,\n        'active_session_name': tab.active_session_name,\n        'num_windows': tab.num_windows,\n        'num_window_groups': tab.num_window_groups,\n        'title': tab.title,\n        'tab': ta,\n        'tab_id': tab.tab_id,\n        'fmt': Formatter,\n        'sup': SupSub(data),\n        'sub': SupSub(data, True),\n        'bell_symbol': draw_data.bell_on_tab if tab.needs_attention else '',\n        'activity_symbol': draw_data.tab_activity_symbol if tab.has_activity_since_last_focus else '',\n        'max_title_length': max_title_length,\n        'keyboard_mode': boss.mappings.current_keyboard_mode_name,\n    }\n    template = draw_data.title_template\n    if tab.is_active and draw_data.active_title_template is not None:\n        template = draw_data.active_title_template\n    prefix = ''\n    if eval_locals['bell_symbol'] and not template_has_field(template, 'bell_symbol'):\n        prefix = '{bell_symbol}'\n    if eval_locals['activity_symbol'] and not template_has_field(template, 'activity_symbol'):\n        prefix += '{activity_symbol}'\n    if prefix:\n        template = '{fmt.fg.red}' + prefix + '{fmt.fg.tab}' + template\n    eval_locals['custom'] = load_custom_draw_title(eval_locals)\n    try:\n        title: str = eval(compile_template(template), {'__builtins__': safe_builtins}, eval_locals)\n    except Exception as e:\n        report_template_failure(template, str(e))\n        title = tab.title\n    return title\n\n\ndef draw_title(draw_data: DrawData, screen: Screen, tab: TabBarData, index: int, max_title_length: int = 0) -> None:\n    title = apply_title_template(draw_data, tab, index, max_title_length)\n    before_draw = screen.cursor.x\n    draw_attributed_string(title, screen)\n    if draw_data.max_tab_title_length > 0:\n        x_limit = before_draw + draw_data.max_tab_title_length\n        if screen.cursor.x > x_limit:\n            screen.cursor.x = x_limit - 1\n            screen.draw('…')\n\n\nDrawTabFunc = Callable[[DrawData, Screen, TabBarData, int, int, int, bool, ExtraData], int]\n\n\ndef draw_tab_with_slant(\n    draw_data: DrawData, screen: Screen, tab: TabBarData,\n    before: int, max_tab_length: int, index: int, is_last: bool,\n    extra_data: ExtraData\n) -> int:\n    orig_fg = screen.cursor.fg\n    left_sep, right_sep = ('', '') if draw_data.tab_bar_edge == 'top' else ('', '')\n    tab_bg = screen.cursor.bg\n    slant_fg = as_rgb(color_as_int(draw_data.default_bg))\n\n    def draw_sep(which: str) -> None:\n        screen.cursor.bg = tab_bg\n        screen.cursor.fg = slant_fg\n        screen.draw(which)\n        screen.cursor.bg = tab_bg\n        screen.cursor.fg = orig_fg\n\n    max_tab_length += 1\n    if max_tab_length <= 1:\n        screen.draw('…')\n    elif max_tab_length == 2:\n        screen.draw('…|')\n    elif max_tab_length < 6:\n        draw_sep(left_sep)\n        screen.draw((' ' if max_tab_length == 5 else '') + '…' + (' ' if max_tab_length >= 4 else ''))\n        draw_sep(right_sep)\n    else:\n        draw_sep(left_sep)\n        screen.draw(' ')\n        draw_title(draw_data, screen, tab, index, max_tab_length)\n        extra = screen.cursor.x - before - max_tab_length\n        if extra >= 0:\n            screen.cursor.x -= extra + 3\n            screen.draw('…')\n        elif extra == -1:\n            screen.cursor.x -= 2\n            screen.draw('…')\n        screen.draw(' ')\n        draw_sep(right_sep)\n\n    return screen.cursor.x\n\n\ndef draw_tab_with_separator(\n    draw_data: DrawData, screen: Screen, tab: TabBarData,\n    before: int, max_tab_length: int, index: int, is_last: bool,\n    extra_data: ExtraData\n) -> int:\n    if draw_data.leading_spaces:\n        screen.draw(' ' * draw_data.leading_spaces)\n    draw_title(draw_data, screen, tab, index, max_tab_length)\n    trailing_spaces = min(max_tab_length - 1, draw_data.trailing_spaces)\n    max_tab_length -= trailing_spaces\n    extra = screen.cursor.x - before - max_tab_length\n    if extra > 0:\n        screen.cursor.x -= extra + 1\n        screen.draw('…')\n    if trailing_spaces:\n        screen.draw(' ' * trailing_spaces)\n    end = screen.cursor.x\n    screen.cursor.bold = screen.cursor.italic = False\n    screen.cursor.fg = 0\n    if not is_last:\n        screen.cursor.bg = as_rgb(color_as_int(draw_data.inactive_bg))\n        screen.draw(draw_data.sep)\n    screen.cursor.bg = 0\n    return end\n\n\ndef draw_tab_with_fade(\n    draw_data: DrawData, screen: Screen, tab: TabBarData,\n    before: int, max_tab_length: int, index: int, is_last: bool,\n    extra_data: ExtraData\n) -> int:\n    orig_bg = screen.cursor.bg\n    tab_bg = color_from_int(orig_bg >> 8)\n    fade_colors = [as_rgb(color_as_int(alpha_blend(tab_bg, draw_data.default_bg, alpha))) for alpha in draw_data.alpha]\n    for bg in fade_colors:\n        screen.cursor.bg = bg\n        screen.draw(' ')\n    screen.cursor.bg = orig_bg\n    draw_title(draw_data, screen, tab, index, max(0, max_tab_length - 8))\n    extra = screen.cursor.x - before - max_tab_length\n    if extra > 0:\n        screen.cursor.x = before\n        draw_title(draw_data, screen, tab, index, max(0, max_tab_length - 4))\n        extra = screen.cursor.x - before - max_tab_length\n        if extra > 0:\n            screen.cursor.x -= extra + 1\n            screen.draw('…')\n    for bg in reversed(fade_colors):\n        if extra >= 0:\n            break\n        extra += 1\n        screen.cursor.bg = bg\n        screen.draw(' ')\n    end = screen.cursor.x\n    screen.cursor.bg = as_rgb(color_as_int(draw_data.default_bg))\n    screen.draw(' ')\n    return end\n\n\npowerline_symbols: dict[PowerlineStyle, tuple[str, str]] = {\n    'slanted': ('', '╱'),\n    'round': ('', '')\n}\n\n\ndef draw_tab_with_powerline(\n    draw_data: DrawData, screen: Screen, tab: TabBarData,\n    before: int, max_tab_length: int, index: int, is_last: bool,\n    extra_data: ExtraData\n) -> int:\n    tab_bg = screen.cursor.bg\n    tab_fg = screen.cursor.fg\n    default_bg = as_rgb(int(draw_data.default_bg))\n    if extra_data.next_tab:\n        next_tab_bg = as_rgb(draw_data.tab_bg(extra_data.next_tab))\n        needs_soft_separator = next_tab_bg == tab_bg\n    else:\n        next_tab_bg = default_bg\n        needs_soft_separator = False\n\n    separator_symbol, soft_separator_symbol = powerline_symbols.get(draw_data.powerline_style, ('', ''))\n    min_title_length = 1 + 2\n    start_draw = 2\n\n    if screen.cursor.x == 0:\n        screen.cursor.bg = tab_bg\n        screen.draw(' ')\n        start_draw = 1\n\n    screen.cursor.bg = tab_bg\n    if min_title_length >= max_tab_length:\n        screen.draw('…')\n    else:\n        draw_title(draw_data, screen, tab, index, max_tab_length)\n        extra = screen.cursor.x + start_draw - before - max_tab_length\n        if extra > 0 and extra + 1 < screen.cursor.x:\n            screen.cursor.x -= extra + 1\n            screen.draw('…')\n\n    if not needs_soft_separator:\n        screen.draw(' ')\n        screen.cursor.fg = tab_bg\n        screen.cursor.bg = next_tab_bg\n        screen.draw(separator_symbol)\n    else:\n        prev_fg = screen.cursor.fg\n        if tab_bg == tab_fg:\n            screen.cursor.fg = default_bg\n        elif tab_bg != default_bg:\n            c1 = draw_data.inactive_bg.contrast(draw_data.default_bg)\n            c2 = draw_data.inactive_bg.contrast(draw_data.inactive_fg)\n            if c1 < c2:\n                screen.cursor.fg = default_bg\n        screen.draw(f' {soft_separator_symbol}')\n        screen.cursor.fg = prev_fg\n\n    end = screen.cursor.x\n    if end < screen.columns:\n        screen.draw(' ')\n    return end\n\n\n@run_once\ndef load_custom_draw_tab_module() -> dict[str, Any]:\n    import runpy\n    import traceback\n    try:\n        return runpy.run_path(os.path.join(config_dir, 'tab_bar.py'))\n    except FileNotFoundError:\n        return {}\n    except Exception as e:\n        traceback.print_exc()\n        log_error(f'Failed to load custom tab_bar.py module with error: {e}')\n        return {}\n\n\n@run_once\ndef load_custom_draw_tab() -> DrawTabFunc:\n    m = load_custom_draw_tab_module()\n    func: DrawTabFunc | None = m.get('draw_tab')\n    if func is None:\n        return draw_tab_with_fade\n\n    @wraps(func)\n    def draw_tab(\n        draw_data: DrawData, screen: Screen, tab: TabBarData,\n        before: int, max_tab_length: int, index: int, is_last: bool,\n        extra_data: ExtraData\n    ) -> int:\n        try:\n            return func(draw_data, screen, tab, before, max_tab_length, index, is_last, extra_data)\n        except Exception as e:\n            log_error(f'Custom draw tab function failed with error: {e}')\n            return draw_tab_with_fade(draw_data, screen, tab, before, max_tab_length, index, is_last, extra_data)\n\n    return draw_tab\n\n\ndef clear_caches() -> None:\n    load_custom_draw_tab.clear_cached()\n    load_custom_draw_tab_module.clear_cached()\n\n\nclass CustomDrawTitleFunc:\n\n    def __init__(self, data: dict[str, Any], implementation: Callable[[dict[str, Any]], str] | None = None):\n        self._implementation = implementation\n        self._data = {} if implementation is None else data.copy()\n\n    def __str__(self) -> str:\n        if self._implementation is None:\n            return ''\n        return str(self._implementation(self._data))\n    __repr__ = __str__\n\n\ndef load_custom_draw_title(data: dict[str, Any]) -> CustomDrawTitleFunc:\n    m = load_custom_draw_tab_module()\n    return CustomDrawTitleFunc(data, m.get('draw_title'))\n\n\nclass CellRange(NamedTuple):\n    start: int\n    end: int\n\n\nclass TabExtent(NamedTuple):\n    tab_id: int\n    cell_range: CellRange\n\n    def shifted(self, shift: int) -> 'TabExtent':\n        return TabExtent(self.tab_id, CellRange(self.cell_range.start + shift, self.cell_range.end + shift))\n\n\nclass TabBar:\n\n    def __init__(self, os_window_id: int):\n        self.os_window_id = os_window_id\n        self.last_laid_out_tabs: Sequence[TabBarData] = ()\n        self.num_tabs = 1\n        self.data_buffer_size = 0\n        self.blank_rects: tuple[Border, ...] = ()\n        self.tab_extents: Sequence[TabExtent] = ()\n        self.laid_out_once = False\n        self.apply_options()\n\n    def apply_options(self) -> None:\n        opts = get_options()\n        self.dirty = True\n        self.margin_width = pt_to_px(opts.tab_bar_margin_width, self.os_window_id)\n        self.cell_width, cell_height = cell_size_for_window(self.os_window_id)\n        if not hasattr(self, 'screen'):\n            self.screen = s = Screen(None, 1, 10, 0, self.cell_width, cell_height)\n        else:\n            s = self.screen\n        s.color_profile.default_fg = opts.inactive_tab_foreground\n        s.color_profile.default_bg = opts.tab_bar_background or opts.background\n        sep = opts.tab_separator\n        self.trailing_spaces = self.leading_spaces = 0\n        while sep and sep[0] == ' ':\n            sep = sep[1:]\n            self.trailing_spaces += 1\n        while sep and sep[-1] == ' ':\n            self.leading_spaces += 1\n            sep = sep[:-1]\n        self.sep = sep\n        self.active_font_style = opts.active_tab_font_style\n        self.inactive_font_style = opts.inactive_tab_font_style\n\n        self.active_bg = as_rgb(color_as_int(opts.active_tab_background))\n        self.active_fg = as_rgb(color_as_int(opts.active_tab_foreground))\n        self.draw_data = DrawData(\n            self.leading_spaces, self.sep, self.trailing_spaces, opts.bell_on_tab,\n            opts.tab_fade, opts.active_tab_foreground, opts.active_tab_background,\n            opts.inactive_tab_foreground, opts.inactive_tab_background,\n            opts.tab_bar_background or opts.background, opts.tab_title_template,\n            opts.active_tab_title_template,\n            opts.tab_activity_symbol,\n            opts.tab_powerline_style,\n            'bottom' if opts.tab_bar_edge == BOTTOM_EDGE else 'top',\n            opts.tab_title_max_length, self.os_window_id,\n        )\n        ts = opts.tab_bar_style\n        if ts == 'separator':\n            self.draw_func: DrawTabFunc = draw_tab_with_separator\n        elif ts == 'powerline':\n            self.draw_func = draw_tab_with_powerline\n        elif ts == 'slant':\n            self.draw_func = draw_tab_with_slant\n        elif ts == 'custom':\n            self.draw_func = load_custom_draw_tab()\n        else:\n            self.draw_func = draw_tab_with_fade\n        if opts.tab_bar_align == 'center':\n            self.align: Callable[[], None] = partial(self.align_with_factor, 2)\n        elif opts.tab_bar_align == 'right':\n            self.align = self.align_with_factor\n        else:\n            self.align = lambda: None\n\n    def patch_colors(self, spec: dict[str, int | None]) -> None:\n        opts = get_options()\n        atf = spec.get('active_tab_foreground')\n        if isinstance(atf, int):\n            self.active_fg = (atf << 8) | 2\n            self.draw_data = self.draw_data._replace(active_fg=color_from_int(atf))\n        atb = spec.get('active_tab_background')\n        if isinstance(atb, int):\n            self.active_bg = (atb << 8) | 2\n            self.draw_data = self.draw_data._replace(active_bg=color_from_int(atb))\n        itb = spec.get('inactive_tab_background')\n        if isinstance(itb, int):\n            self.draw_data = self.draw_data._replace(inactive_bg=color_from_int(itb))\n        if 'tab_bar_background' in spec:\n            val = spec['tab_bar_background']\n            if val is None:\n                val = color_as_int(opts.background)\n            self.draw_data = self.draw_data._replace(default_bg=color_from_int(val))\n        elif not opts.tab_bar_background:\n            self.draw_data = self.draw_data._replace(default_bg=opts.background)\n        bg = spec.get('tab_bar_background', False)\n        if bg is None:\n            bg = color_as_int(opts.background)\n        elif bg is False:\n            bg = color_as_int(opts.tab_bar_background or opts.background)\n        fg = spec.get('inactive_tab_foreground')\n        if fg is None:\n            fg = color_as_int(opts.inactive_tab_foreground)\n        else:\n            ifg = color_from_int(fg)\n            if ifg is not None:\n                self.draw_data = self.draw_data._replace(inactive_fg=ifg)\n        self.screen.color_profile.reload_from_opts()\n        self.screen.color_profile.default_fg = color_from_int(fg)\n        self.screen.color_profile.default_bg = color_from_int(bg)\n\n    @property\n    def current_colors(self) -> dict[str, Color]:\n        return {\n            'active_tab_foreground': self.draw_data.active_fg,\n            'inactive_tab_foreground': self.draw_data.inactive_fg,\n            'active_tab_background': self.draw_data.active_bg,\n            'inactive_tab_background': self.draw_data.inactive_bg,\n            'tab_bar_background': self.draw_data.default_bg,\n        }\n\n    def update_blank_rects(self, central: Region, tab_bar: Region, vw: int, vh: int) -> None:\n        opts = get_options()\n        blank_rects: list[Border] = []\n        bg = BorderColor.tab_bar_margin_color if opts.tab_bar_margin_color is not None else BorderColor.default_bg\n        if opts.tab_bar_margin_height:\n            if opts.tab_bar_edge == BOTTOM_EDGE:\n                if opts.tab_bar_margin_height.outer:\n                    blank_rects.append(Border(0, tab_bar.bottom, vw, vh, bg))\n                if opts.tab_bar_margin_height.inner:\n                    blank_rects.append(Border(0, central.bottom, vw, tab_bar.top, bg))\n            else: # top\n                if opts.tab_bar_margin_height.outer:\n                    blank_rects.append(Border(0, 0, vw, tab_bar.top, bg))\n                if opts.tab_bar_margin_height.inner:\n                    blank_rects.append(Border(0, tab_bar.bottom, vw, central.top, bg))\n        g = self.window_geometry\n        left_bg = right_bg = bg\n        if opts.tab_bar_margin_color is None and (\n                opacity := background_opacity_of(self.os_window_id)) is not None and opacity >= 1:\n            left_bg = BorderColor.tab_bar_left_edge_color\n            right_bg = BorderColor.tab_bar_right_edge_color\n        if g.left > 0:\n            blank_rects.append(Border(0, g.top, g.left, g.bottom, left_bg))\n        if g.right < vw:\n            blank_rects.append(Border(g.right, g.top, vw, g.bottom, right_bg))\n        self.blank_rects = tuple(blank_rects)\n\n    def layout(self) -> None:\n        central, tab_bar, vw, vh, cell_width, cell_height = viewport_for_window(self.os_window_id)\n        if tab_bar.width < 2:\n            return\n        self.cell_width = cell_width\n        s = self.screen\n        available_width = tab_bar.width - 2 * self.margin_width\n        ncells = max(4, available_width // cell_width)\n        s.resize(1, ncells)\n        s.reset_mode(DECAWM)\n        cell_area_width = ncells * cell_width\n        available_width_for_left_margin = max(0, tab_bar.width - self.margin_width - cell_area_width)\n        extra_width = max(0, tab_bar.width - 2 * self.margin_width - cell_area_width)\n        left_margin = min(self.margin_width + extra_width // 2, available_width_for_left_margin)\n        self.laid_out_once = True\n        self.window_geometry = g = WindowGeometry(\n            left_margin, tab_bar.top, left_margin + cell_area_width, tab_bar.bottom, s.columns, s.lines)\n        self.update_blank_rects(central, tab_bar, vw, vh)\n        set_tab_bar_render_data(self.os_window_id, self.screen, *g[:4])\n\n    def update(self, data: Sequence[TabBarData]) -> None:\n        if not self.laid_out_once:\n            return\n        s = self.screen\n        last_tab = data[-1] if data else None\n        ed = ExtraData()\n        self.last_laid_out_tabs = data\n\n        def draw_tab(i: int, tab: TabBarData, cell_ranges: list[TabExtent], max_tab_length: int) -> None:\n            ed.prev_tab = data[i - 1] if i > 0 else None\n            ed.next_tab = data[i + 1] if i + 1 < len(data) else None\n            s.cursor.bg = as_rgb(self.draw_data.tab_bg(t))\n            s.cursor.fg = as_rgb(self.draw_data.tab_fg(t))\n            s.cursor.bold, s.cursor.italic = self.active_font_style if t.is_active else self.inactive_font_style\n            before = s.cursor.x\n            end = self.draw_func(self.draw_data, s, t, before, max_tab_length, i + 1, t is last_tab, ed)\n            s.cursor.bg = s.cursor.fg = 0\n            cell_ranges.append(TabExtent(tab_id=tab.tab_id, cell_range=CellRange(before, end)))\n            if not ed.for_layout and t is not last_tab and s.cursor.x > s.columns - max_tab_lengths[i+1]:\n                # Stop if there is no space for next tab\n                s.cursor.x = s.columns - 2\n                s.cursor.bg = as_rgb(color_as_int(self.draw_data.default_bg))\n                s.cursor.fg = as_rgb(0xff0000)\n                s.draw(' …')\n                raise StopIteration()\n\n        unconstrained_tab_length = max(1, s.columns - 2)\n        ideal_tab_lengths = [i for i in range(len(data))]\n        default_max_tab_length = max(1, (s.columns // max(1, len(data))) - 1)\n        max_tab_lengths = [default_max_tab_length for _ in range(len(data))]\n        active_idx = 0\n        extra = 0\n        ed.for_layout = True\n        for i, t in enumerate(data):\n            s.cursor.x = 0\n            draw_tab(i, t, [], unconstrained_tab_length)\n            ideal_tab_lengths[i] = tl = max(1, s.cursor.x)\n            if t.is_active:\n                active_idx = i\n            if tl < default_max_tab_length:\n                max_tab_lengths[i] = tl\n                extra += default_max_tab_length - tl\n        if extra > 0:\n            if ideal_tab_lengths[active_idx] > max_tab_lengths[active_idx]:\n                d = min(extra, ideal_tab_lengths[active_idx] - max_tab_lengths[active_idx])\n                max_tab_lengths[active_idx] += d\n                extra -= d\n            if extra > 0:\n                over_achievers = tuple(i for i in range(len(data)) if ideal_tab_lengths[i] > max_tab_lengths[i])\n                if over_achievers:\n                    amt_per_over_achiever = extra // len(over_achievers)\n                    if amt_per_over_achiever > 0:\n                        for i in over_achievers:\n                            max_tab_lengths[i] += amt_per_over_achiever\n\n        s.cursor.x = 0\n        s.erase_in_line(2, False)\n        cr: list[TabExtent] = []\n        ed.for_layout = False\n        for i, t in enumerate(data):\n            try:\n                draw_tab(i, t, cr, max_tab_lengths[i])\n            except StopIteration:\n                break\n        self.tab_extents = cr\n        s.erase_in_line(0, False)  # Ensure no long titles bleed after the last tab\n        self.align()\n        update_tab_bar_edge_colors(self.os_window_id)\n\n    def align_with_factor(self, factor: int = 1) -> None:\n        if not self.tab_extents:\n            return\n        end = self.tab_extents[-1].cell_range[1]\n        if end < self.screen.columns - 1:\n            shift = (self.screen.columns - end) // factor\n            self.screen.cursor.x = 0\n            self.screen.insert_characters(shift)\n            self.tab_extents = tuple(te.shifted(shift) for te in self.tab_extents)\n\n    def destroy(self) -> None:\n        self.screen.reset_callbacks()\n        del self.screen\n\n    def tab_id_at(self, x: int) -> int:\n        if self.laid_out_once:\n            x = (x - self.window_geometry.left) // self.cell_width\n            for te in self.tab_extents:\n                if te.cell_range.start <= x <= te.cell_range.end:\n                    return te.tab_id\n        return 0\n"
  },
  {
    "path": "kitty/tabs.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport math\nimport os\nimport re\nimport stat\nimport weakref\nfrom collections import deque\nfrom collections.abc import Callable, Generator, Iterable, Iterator, Sequence\nfrom contextlib import suppress\nfrom functools import wraps\nfrom gettext import gettext as _\nfrom typing import Any, Concatenate, Deque, NamedTuple, Optional, ParamSpec, TypeVar, cast\n\nfrom .borders import Border, Borders\nfrom .child import Child\nfrom .cli_stub import CLIOptions, SaveAsSessionOptions\nfrom .constants import appname\nfrom .fast_data_types import (\n    GLFW_MOUSE_BUTTON_LEFT,\n    GLFW_MOUSE_BUTTON_MIDDLE,\n    GLFW_PRESS,\n    GLFW_RELEASE,\n    add_tab,\n    attach_window,\n    buffer_keys_in_window,\n    current_focused_os_window_id,\n    detach_window,\n    draw_single_line_of_text,\n    get_boss,\n    get_click_interval,\n    get_options,\n    get_tab_being_dragged,\n    is_tab_bar_visible,\n    last_focused_os_window_id,\n    mark_tab_bar_dirty,\n    monotonic,\n    next_window_id,\n    remove_tab,\n    remove_window,\n    reorder_tabs,\n    replace_c0_codes_except_nl_space_tab,\n    request_callback_with_thumbnail,\n    ring_bell,\n    set_active_tab,\n    set_active_window,\n    set_redirect_keys_to_overlay,\n    set_tab_being_dragged,\n    start_drag_with_data,\n    swap_tabs,\n    sync_os_window_title,\n)\nfrom .layout.base import Layout\nfrom .layout.interface import create_layout_object_for, evict_cached_layouts\nfrom .progress import ProgressState\nfrom .tab_bar import TabBar, TabBarData, apply_title_template\nfrom .types import ac\nfrom .typing_compat import EdgeLiteral, SessionTab, SessionType, TypedDict\nfrom .utils import cmdline_for_hold, color_as_int, log_error, platform_window_id, resolved_shell, shlex_split, which\nfrom .window import CwdRequest, Watchers, Window, WindowCreationSpec, WindowDict, global_watchers\nfrom .window_list import WindowList\n\nP = ParamSpec('P')\nT = TypeVar('T')\n\n\ndef update_tab_bar_visibility(func: Callable[Concatenate['TabManager', P], T]) -> Callable[Concatenate['TabManager', P], T]:\n    @wraps(func)\n    def wrapper(self: 'TabManager', *args: P.args, **kwargs: P.kwargs) -> T:\n        visible_before = is_tab_bar_visible(self.os_window_id)\n        try:\n            return func(self, *args, **kwargs)\n        finally:\n            if visible_before != self.tab_bar_should_be_visible:\n                if not self.tab_bar_hidden:\n                    self.layout_tab_bar()\n                    self.resize(only_tabs=True)\n    return cast(Callable[Concatenate['TabManager', P], T], wrapper)\n\n\nclass TabMouseEvent(NamedTuple):\n    button: int\n    modifiers: int\n    action: int\n    at: float\n    tab_id: int = 0\n\n\nclass TabDict(TypedDict):\n    id: int\n    is_focused: bool\n    is_active: bool\n    title: str\n    title_overridden: bool\n    layout: str\n    layout_state: dict[str, Any]\n    layout_opts: dict[str, Any]\n    enabled_layouts: list[str]\n    windows: list[WindowDict]\n    groups: list[dict[str, Any]]\n    active_window_history: list[int]\n\n\nclass SpecialWindowInstance(NamedTuple):\n    cmd: list[str] | None\n    stdin: bytes | None\n    override_title: str | None\n    cwd_from: CwdRequest | None\n    cwd: str | None\n    overlay_for: int | None\n    env: dict[str, str] | None\n    watchers: Watchers | None\n    overlay_behind: bool\n    hold: bool\n\n\ndef SpecialWindow(\n    cmd: list[str] | None,\n    stdin: bytes | None = None,\n    override_title: str | None = None,\n    cwd_from: CwdRequest | None = None,\n    cwd: str | None = None,\n    overlay_for: int | None = None,\n    env: dict[str, str] | None = None,\n    watchers: Watchers | None = None,\n    overlay_behind: bool = False,\n    hold: bool = False,\n) -> SpecialWindowInstance:\n    return SpecialWindowInstance(cmd, stdin, override_title, cwd_from, cwd, overlay_for, env, watchers, overlay_behind, hold)\n\n\ndef add_active_id_to_history(items: Deque[int], item_id: int, maxlen: int = 64) -> None:\n    with suppress(ValueError):\n        items.remove(item_id)\n    items.append(item_id)\n    if len(items) > maxlen:\n        items.popleft()\n\n\nclass Tab:  # {{{\n\n    active_fg: int | None = None\n    active_bg: int | None = None\n    inactive_fg: int | None = None\n    inactive_bg: int | None = None\n    confirm_close_window_id: int = 0\n    renaming_in_window: int = 0\n    num_of_windows_with_progress: int = 0\n    total_progress: int = 0\n    has_indeterminate_progress: bool = False\n    last_focused_window_with_progress_id: int = 0\n    allow_relayouts: bool = True\n\n    def __init__(\n        self,\n        tab_manager: 'TabManager',\n        session_tab: Optional['SessionTab'] = None,\n        special_window: SpecialWindowInstance | None = None,\n        cwd_from: CwdRequest | None = None,\n        no_initial_window: bool = False,\n        session_name: str = '',\n    ):\n        self.created_in_session_name = session_name\n        self.tab_manager_ref = weakref.ref(tab_manager)\n        self.os_window_id: int = tab_manager.os_window_id\n        self.id: int = add_tab(self.os_window_id)\n        if not self.id:\n            raise Exception(f'No OS window with id {self.os_window_id} found, or tab counter has wrapped')\n        self.args = tab_manager.args\n        self.name = getattr(session_tab, 'name', '')\n        self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or get_options().enabled_layouts]\n        self.borders = Borders(self.os_window_id, self.id)\n        self.windows: WindowList = WindowList(self)\n        self._last_used_layout: str | None = None\n        self._current_layout_name: str | None = None\n        self.cwd = self.args.directory\n        if no_initial_window:\n            self._set_current_layout(self.enabled_layouts[0])\n        elif session_tab is None:\n            sl = self.enabled_layouts[0]\n            self._set_current_layout(sl)\n            if special_window is None:\n                self.new_window(cwd_from=cwd_from)\n            else:\n                self.new_special_window(special_window)\n        else:\n            if session_tab.cwd:\n                self.cwd = session_tab.cwd\n            l0 = session_tab.layout\n            self._set_current_layout(l0)\n            self.startup(session_tab)\n\n    def update_progress(self) -> None:\n        self.num_of_windows_with_progress = 0\n        self.total_progress = 0\n        self.last_focused_window_with_progress_id = 0\n        self.has_indeterminate_progress = False\n        focused_at = 0.\n        for window in self:\n            p = window.progress\n            if p.state is ProgressState.unset:\n                continue\n            if p.state in (ProgressState.set, ProgressState.paused):\n                self.total_progress += p.percent\n                self.num_of_windows_with_progress += 1\n            elif p.state is ProgressState.indeterminate:\n                self.has_indeterminate_progress = True\n            if window.last_focused_at > focused_at or (not window.last_focused_at and window.id > self.last_focused_window_with_progress_id):\n                focused_at = window.last_focused_at\n                self.last_focused_window_with_progress_id = window.id\n        self.mark_tab_bar_dirty()\n        tm = self.tab_manager_ref()\n        if tm is not None:\n            tm.update_progress()\n\n    def has_single_window_visible(self) -> bool:\n        if self.current_layout.only_active_window_visible:\n            return True\n        for i, g in enumerate(self.windows.iter_all_layoutable_groups(only_visible=True)):\n            if i > 0:\n                return False\n        return True\n\n    def set_enabled_layouts(self, val: Iterable[str]) -> None:\n        self.enabled_layouts = [x.lower() for x in val] or ['tall']\n        if self.current_layout.name not in self.enabled_layouts:\n            self._set_current_layout(self.enabled_layouts[0])\n        self.relayout()\n\n    def apply_options(self, is_active: bool) -> None:\n        aw = self.active_window\n        for window in self:\n            window.apply_options(is_active and aw is window)\n        self.set_enabled_layouts(get_options().enabled_layouts)\n\n    def take_over_from(self, other_tab: 'Tab') -> None:\n        self.name, self.cwd = other_tab.name, other_tab.cwd\n        self.enabled_layouts = list(other_tab.enabled_layouts)\n        self._last_used_layout = other_tab._last_used_layout\n        if clname := other_tab._current_layout_name:\n            cl = other_tab.current_layout\n            other_tab._set_current_layout(clname)\n            cl.set_owner(self.os_window_id, self.id)\n            self.current_layout: Layout = cl\n            self._current_layout_name = clname\n            self.mark_tab_bar_dirty()\n        for window in other_tab.windows:\n            detach_window(other_tab.os_window_id, other_tab.id, window.id)\n        self.windows = other_tab.windows\n        self.windows.change_tab(self)\n        other_tab.windows = WindowList(other_tab)\n        for window in self.windows:\n            window.change_tab(self)\n            attach_window(self.os_window_id, self.id, window.id)\n        self.active_window_changed()\n        self.relayout()\n\n    def _set_current_layout(self, layout_name: str) -> None:\n        self._last_used_layout = self._current_layout_name\n        self.current_layout = self.create_layout_object(layout_name)\n        self._current_layout_name = layout_name\n        self.mark_tab_bar_dirty()\n\n    def startup(self, session_tab: SessionTab) -> None:\n        self.allow_relayouts = False\n        try:\n            self._startup(session_tab)\n        finally:\n            self.allow_relayouts = True\n        self.relayout()\n\n    def _startup(self, session_tab: SessionTab) -> None:\n        target_tab = self\n        boss = get_boss()\n        active_window_id = 0\n        did_focus_matching_spec = False\n        first_window_id = 0\n        for i, window in enumerate(session_tab.windows):\n            spec = window.launch_spec\n            launched_window: Window | None = None\n            if isinstance(spec, SpecialWindowInstance):\n                launched_window = self.new_special_window(spec)\n                if launched_window is not None:\n                    launched_window.created_in_session_name = self.created_in_session_name\n            else:\n                from .launch import launch\n                spec.opts.add_to_session = self.created_in_session_name\n                launched_window = launch(\n                    boss, spec.opts, spec.args, target_tab=target_tab, force_target_tab=True,\n                    startup_command_via_shell_integration=window.run_command_at_shell_startup)\n                if launched_window is not None:\n                    launched_window.serialized_id = window.serialized_id\n            if launched_window is not None:\n                if not first_window_id:\n                    first_window_id = launched_window.id\n                if session_tab.active_window_idx == i:\n                    active_window_id = launched_window.id\n                    did_focus_matching_spec = False\n            if window.resize_spec is not None:\n                self.resize_window(*window.resize_spec)\n            if window.focus_matching_window_spec:\n                # include windows from this tab when matching windows\n                all_windows = list(boss.all_windows)\n                awq = {w.id for w in all_windows}\n                all_windows.extend(w for w in self if w.id not in awq)\n                for w in boss.match_windows(\n                    window.focus_matching_window_spec, launched_window or boss.active_window, all_windows\n                ):\n                    tab = w.tabref()\n                    if tab:\n                        did_focus_matching_spec = True\n                        active_window_id = 0\n                        target_tab = tab or self\n                        tm = tab.tab_manager_ref()\n                        if tm and boss.active_tab is not target_tab:\n                            tm.set_active_tab(target_tab)\n                        if target_tab.active_window is not w:\n                            target_tab.set_active_window(w)\n                        boss.focus_os_window(w.os_window_id)\n\n        if not did_focus_matching_spec and not active_window_id:\n            active_window_id = first_window_id\n        if active_window_id and not did_focus_matching_spec:\n            self.windows.set_active_window_group_for(active_window_id)\n        if session_tab.layout_state:\n            self.current_layout.unserialize(session_tab.layout_state, self.windows)\n\n    def serialize_state(self) -> dict[str, Any]:\n        return {\n            'version': 1,\n            'id': self.id,\n            'window_list': self.windows.serialize_state(),\n            'current_layout': self._current_layout_name,\n            'last_used_layout': self._last_used_layout,\n            'layout_state': self.current_layout.serialize(self.windows),\n            'enabled_layouts': self.enabled_layouts,\n            'name': self.name,\n        }\n\n    def serialize_state_as_session(self, session_path: str, matched_windows: frozenset[Window] | None, ser_opts: SaveAsSessionOptions) -> list[str]:\n        import shlex\n        launch_cmds = []\n        active_idx = self.windows.active_group_idx\n        groups = tuple(self.windows.iter_all_layoutable_groups())\n        session_base_dir = os.path.dirname(session_path) if session_path else ''\n        def make_relative(cwd: str) -> str:\n            if session_base_dir and ser_opts.relocatable:\n                cwd = os.path.relpath(cwd, session_base_dir)\n            return cwd\n        most_common_cwd = ''\n        cwds = {w.id: make_relative(w.cwd_for_serialization) for g in groups for w in g}\n        if cwds:\n            from collections import Counter\n            most_common_cwd, _ = Counter(cwds.values()).most_common(1)[0]\n        for i, g in enumerate(groups):\n            gw: list[str] = []\n            for window in g:\n                if matched_windows is not None and window not in matched_windows:\n                    continue\n                cwd = cwds[window.id]\n                lc = window.as_launch_command(ser_opts, '' if cwd == most_common_cwd else cwd, is_overlay=bool(gw))\n                if lc:\n                    gw.append(shlex.join(lc))\n            if gw:\n                launch_cmds.extend(gw)\n                if i == active_idx:\n                    launch_cmds.append('focus')\n        if launch_cmds:\n            enabled_layouts = list(self.enabled_layouts)\n            layout = self._current_layout_name\n            if layout not in enabled_layouts:\n                enabled_layouts.append(layout)\n            return [\n                '',\n                f'new_tab {self.name}'.rstrip(),\n                f'layout {layout}',\n                f'enabled_layouts {\",\".join(enabled_layouts)}',\n                f'set_layout_state {json.dumps(self.current_layout.serialize(self.windows))}',\n                f'cd {most_common_cwd}',\n                ''\n            ] + launch_cmds\n        return []\n\n    def data_for_tab_bar(self, is_active: bool) -> TabBarData:\n        t = self\n        title = t.name or t.title or appname\n        needs_attention = False\n        has_activity_since_last_focus = False\n        for w in t:\n            if w.needs_attention:\n                needs_attention = True\n            if w.has_activity_since_last_focus:\n                has_activity_since_last_focus = True\n        return TabBarData(\n            title, is_active, needs_attention, t.id, t.os_window_id,\n            len(t), t.num_window_groups, t.current_layout.name or '',\n            has_activity_since_last_focus, t.active_fg, t.active_bg,\n            t.inactive_fg, t.inactive_bg, t.num_of_windows_with_progress,\n            t.total_progress, t.last_focused_window_with_progress_id,\n            t.created_in_session_name, t.active_session_name,\n        )\n\n    def active_window_changed(self) -> None:\n        w = self.active_window\n        set_active_window(self.os_window_id, self.id, 0 if w is None else w.id)\n        self.mark_tab_bar_dirty()\n        self.relayout_borders()\n        self.current_layout.update_visibility(self.windows)\n\n    def mark_tab_bar_dirty(self) -> None:\n        tm = self.tab_manager_ref()\n        if tm is not None:\n            tm.mark_tab_bar_dirty()\n\n    @property\n    def active_window(self) -> Window | None:\n        return self.windows.active_window\n\n    @property\n    def active_window_for_cwd(self) -> Window | None:\n        return self.windows.active_group_main\n\n    @property\n    def title(self) -> str:\n        w = self.active_window\n        return w.title if w else appname\n\n    @property\n    def effective_title(self) -> str:\n        return self.name or self.title\n\n    def get_cwd_of_active_window(self, oldest: bool = False) -> str | None:\n        w = self.active_window\n        return w.get_cwd_of_child(oldest) if w else None\n\n    def get_exe_of_active_window(self, oldest: bool = False) -> str | None:\n        w = self.active_window\n        return w.get_exe_of_child(oldest) if w else None\n\n    def set_title(self, title: str) -> None:\n        self.name = title or ''\n        self.mark_tab_bar_dirty()\n\n    def update_window_title_bars(self) -> None:\n        active_group = self.windows.active_group\n        for wg in self.windows.iter_all_layoutable_groups(only_visible=True):\n            is_active = wg is active_group\n            for w in wg.windows:\n                w.update_title_bar(is_active=is_active)\n\n    def title_changed(self, window: Window) -> None:\n        self.update_window_title_bars()\n        if window is self.active_window:\n            tm = self.tab_manager_ref()\n            if tm is not None:\n                tm.title_changed(self)\n\n    def on_bell(self, window: Window) -> None:\n        self.mark_tab_bar_dirty()\n\n    def relayout(self) -> None:\n        if self.allow_relayouts:\n            if self.windows:\n                self.current_layout(self.windows)\n            self.relayout_borders()\n\n    def relayout_borders(self) -> None:\n        tm = self.tab_manager_ref()\n        if tm is not None:\n            ly = self.current_layout\n            opts = get_options()\n            draw_borders = (\n                ly.must_draw_borders or opts.draw_window_borders_for_single_window or\n                (ly.needs_window_borders and self.windows.has_more_than_one_visible_group)\n            )\n            self.borders(\n                all_windows=self.windows,\n                current_layout=ly, tab_bar_rects=tm.tab_bar_rects,\n                draw_window_borders=draw_borders\n            )\n            self.update_window_title_bars()\n\n    def create_layout_object(self, name: str) -> Layout:\n        return create_layout_object_for(name, self.os_window_id, self.id)\n\n    @ac('lay', 'Go to the next enabled layout. Can optionally supply an integer to jump by the specified number.')\n    def next_layout(self, delta: int = 1) -> None:\n        if len(self.enabled_layouts) > 1:\n            for i, layout_name in enumerate(self.enabled_layouts):\n                if layout_name == self.current_layout.full_name:\n                    idx = i\n                    break\n            else:\n                idx = -1\n            if abs(delta) >= len(self.enabled_layouts):\n                mult = -1 if delta < 0 else 1\n                delta = mult * (abs(delta) % len(self.enabled_layouts))\n            nl = self.enabled_layouts[(idx + delta + len(self.enabled_layouts)) % len(self.enabled_layouts)]\n            self._set_current_layout(nl)\n            self.relayout()\n\n    @ac('lay', 'Go to the previously used layout')\n    def last_used_layout(self) -> None:\n        if len(self.enabled_layouts) > 1 and self._last_used_layout and self._last_used_layout != self._current_layout_name:\n            self._set_current_layout(self._last_used_layout)\n            self.relayout()\n\n    @ac('lay', '''\n        Switch to the named layout\n        In case there are multiple layouts with the same name and different options,\n        specify the full layout definition or a unique prefix of the full definition.\n\n        For example::\n\n            map f1 goto_layout tall\n            map f2 goto_layout fat:bias=20\n        ''')\n    def goto_layout(self, layout_name: str, raise_exception: bool = False) -> None:\n        layout_name = layout_name.lower()\n        q, has_colon, rest = layout_name.partition(':')\n        matches = []\n        prefix_matches = []\n        matched_layout = ''\n        for candidate in self.enabled_layouts:\n            x, _, _ = candidate.partition(':')\n            if x == q:\n                if candidate == layout_name:\n                    matched_layout = candidate\n                    break\n                if candidate.startswith(layout_name):\n                    prefix_matches.append(candidate)\n                matches.append(x)\n\n        if not matched_layout:\n            if len(prefix_matches) == 1:\n                matched_layout = prefix_matches[0]\n            elif len(matches) == 1:\n                matched_layout = matches[0]\n        if matched_layout:\n            self._set_current_layout(matched_layout)\n            self.relayout()\n        else:\n            if len(matches) == 0:\n                if raise_exception:\n                    raise ValueError(layout_name)\n                log_error(f'Unknown or disabled layout: {layout_name}')\n            elif len(matches) != 1:\n                if raise_exception:\n                    raise ValueError(layout_name)\n                log_error(f'Multiple layouts match: {layout_name}')\n\n    @ac('lay', '''\n        Toggle the named layout\n\n        Switches to the named layout if another layout is current, otherwise\n        switches to the last used layout. Useful to \"zoom\" a window temporarily\n        by switching to the stack layout. See also :opt:`scrollback_fill_enlarged_window`\n        if you would like content from the scrollback buffer to scroll down into the\n        zoomed window. For example::\n\n            map f1 toggle_layout stack\n        ''')\n    def toggle_layout(self, layout_name: str) -> None:\n        if self._current_layout_name == layout_name:\n            self.last_used_layout()\n        else:\n            self.goto_layout(layout_name)\n\n    def resize_window_by(self, window_id: int, increment: float, is_horizontal: bool) -> str | None:\n        increment_as_percent = self.current_layout.bias_increment_for_cell(self.windows, is_horizontal) * increment\n        if self.current_layout.modify_size_of_window(self.windows, window_id, increment_as_percent, is_horizontal):\n            self.relayout()\n            return None\n        return 'Could not resize'\n\n    def drag_resize_window(self, object_id: int, increment: float, is_horizontal: bool) -> bool:\n        increment_as_percent = self.current_layout.bias_increment_for_cell(self.windows, is_horizontal) * increment\n        if resized := self.current_layout.drag_resize_window(self.windows, object_id, increment_as_percent, is_horizontal):\n            self.relayout()\n        return resized\n\n    @ac('win', '''\n        Resize the active window by the specified amount\n\n        See :ref:`window_resizing` for details.\n        ''')\n    def resize_window(self, quality: str, increment: int) -> None:\n        if quality == 'reset':\n            self.reset_window_sizes()\n            return\n        if increment < 1:\n            raise ValueError(increment)\n        is_horizontal = quality in ('wider', 'narrower')\n        increment *= 1 if quality in ('wider', 'taller') else -1\n        w = self.active_window\n        if w is not None and self.resize_window_by(\n                w.id, increment, is_horizontal) is not None:\n            if get_options().enable_audio_bell:\n                ring_bell(self.os_window_id)\n\n    @ac('win', 'Reset window sizes undoing any dynamic resizing of windows')\n    def reset_window_sizes(self) -> None:\n        if self.current_layout.remove_all_biases():\n            self.relayout()\n\n    @ac('lay', 'Perform a layout specific action. See :doc:`layouts` for details')\n    def layout_action(self, action_name: str, args: Sequence[str]) -> None:\n        ret = self.current_layout.layout_action(action_name, args, self.windows)\n        if ret is None:\n            if get_options().enable_audio_bell:\n                ring_bell(self.os_window_id)\n            return\n        self.relayout()\n\n    def launch_child(\n        self,\n        use_shell: bool = False,\n        cmd: list[str] | None = None,\n        stdin: bytes | None = None,\n        cwd_from: CwdRequest | None = None,\n        cwd: str | None = None,\n        env: dict[str, str] | None = None,\n        is_clone_launch: str = '',\n        add_listen_on_env_var: bool = True,\n        hold: bool = False,\n        pass_fds: tuple[int, ...] = (),\n        remote_control_fd: int = -1,\n        hold_after_ssh: bool = False,\n        startup_command_via_shell_integration: Sequence[str] | str = (),\n    ) -> Child:\n        check_for_suitability = True\n        if cmd is None:\n            if use_shell:\n                cmd = resolved_shell(get_options())\n                check_for_suitability = False\n            else:\n                if self.args.args:\n                    cmd = list(self.args.args)\n                else:\n                    cmd = resolved_shell(get_options())\n                    check_for_suitability = False\n        if check_for_suitability:\n            old_exe = cmd[0]\n            if not os.path.isabs(old_exe):\n                actual_exe = which(old_exe)\n                old_exe = actual_exe if actual_exe else os.path.abspath(old_exe)\n            try:\n                is_executable = os.access(old_exe, os.X_OK)\n            except OSError:\n                pass\n            else:\n                try:\n                    st = os.stat(old_exe)\n                except OSError:\n                    pass\n                else:\n                    if stat.S_ISDIR(st.st_mode):\n                        cwd = old_exe\n                        cmd = resolved_shell(get_options())\n                    elif not is_executable:\n                        with suppress(OSError):\n                            with open(old_exe) as f:\n                                if f.read(2) == '#!':\n                                    line = f.read(4096).splitlines()[0]\n                                    cmd[:0] = shlex_split(line)\n                                else:\n                                    cmd[:0] = [resolved_shell(get_options())[0]]\n                                cmd[0] = which(cmd[0]) or cmd[0]\n                                cmd = cmdline_for_hold(cmd)\n        fenv: dict[str, str] = {}\n        if env:\n            fenv.update(env)\n        fenv['KITTY_WINDOW_ID'] = str(next_window_id())\n        pwid = platform_window_id(self.os_window_id)\n        if pwid is not None:\n            fenv['WINDOWID'] = str(pwid)\n        ans = Child(\n                cmd, cwd or self.cwd, stdin, fenv, cwd_from, is_clone_launch=is_clone_launch,\n                add_listen_on_env_var=add_listen_on_env_var, hold=hold, pass_fds=pass_fds,\n                remote_control_fd=remote_control_fd, hold_after_ssh=hold_after_ssh,\n                startup_command_via_shell_integration=startup_command_via_shell_integration)\n        ans.fork()\n        return ans\n\n    def _add_window(\n        self, window: Window, location: str | None = None, overlay_for: int | None = None,\n        overlay_behind: bool = False, bias: float | None = None, next_to: Window | None = None,\n    ) -> None:\n        self.current_layout.add_window(self.windows, window, location, overlay_for, put_overlay_behind=overlay_behind, bias=bias, next_to=next_to)\n        if overlay_behind and (w := self.active_window):\n            set_redirect_keys_to_overlay(self.os_window_id, self.id, w.id, window.id)\n            buffer_keys_in_window(self.os_window_id, self.id, window.id, True)\n            window.keys_redirected_till_ready_from = w.id\n        self.mark_tab_bar_dirty()\n        self.relayout()\n\n    def new_window(\n        self,\n        use_shell: bool = True,\n        cmd: list[str] | None = None,\n        stdin: bytes | None = None,\n        override_title: str | None = None,\n        cwd_from: CwdRequest | None = None,\n        cwd: str | None = None,\n        overlay_for: int | None = None,\n        env: dict[str, str] | None = None,\n        location: str | None = None,\n        copy_colors_from: Window | None = None,\n        allow_remote_control: bool = False,\n        marker: str | None = None,\n        watchers: Watchers | None = None,\n        overlay_behind: bool = False,\n        is_clone_launch: str = '',\n        remote_control_passwords: dict[str, Sequence[str]] | None = None,\n        hold: bool = False,\n        bias: float | None = None,\n        pass_fds: tuple[int, ...] = (),\n        remote_control_fd: int = -1,\n        next_to: Window | None = None,\n        hold_after_ssh: bool = False,\n        startup_command_via_shell_integration: Sequence[str] | str = (),\n    ) -> Window:\n        cs = WindowCreationSpec(\n            use_shell=use_shell, cmd=cmd, has_stdin=bool(stdin), override_title=override_title, cwd_from=cwd_from,\n            cwd=cwd, overlay_for=overlay_for, env=None if env is None else tuple(env.items()), location=location,\n            copy_colors_from=None if copy_colors_from is None else copy_colors_from.id,\n            allow_remote_control=allow_remote_control,\n            remote_control_passwords=None if remote_control_passwords is None else remote_control_passwords.copy(),\n            marker=marker, overlay_behind=overlay_behind, is_clone_launch=is_clone_launch, hold=hold, bias=bias,\n            hold_after_ssh=hold_after_ssh,\n        )\n        child = self.launch_child(\n            use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env,\n            is_clone_launch=is_clone_launch, add_listen_on_env_var=False if allow_remote_control and remote_control_passwords else True,\n            hold=hold, pass_fds=pass_fds, remote_control_fd=remote_control_fd, hold_after_ssh=hold_after_ssh,\n            startup_command_via_shell_integration=startup_command_via_shell_integration,\n        )\n        window = Window(\n            self, child, self.args, override_title=override_title,\n            copy_colors_from=copy_colors_from, watchers=watchers,\n            allow_remote_control=allow_remote_control, remote_control_passwords=remote_control_passwords\n        )\n        window.creation_spec = cs\n        # Must add child before laying out so that resize_pty succeeds\n        get_boss().add_child(window)\n        self._add_window(window, location=location, overlay_for=overlay_for, overlay_behind=overlay_behind, bias=bias, next_to=next_to)\n        if marker:\n            try:\n                window.set_marker(marker)\n            except Exception:\n                import traceback\n                traceback.print_exc()\n        return window\n\n    def new_special_window(\n            self,\n            special_window: SpecialWindowInstance,\n            location: str | None = None,\n            copy_colors_from: Window | None = None,\n            allow_remote_control: bool = False,\n            remote_control_passwords: dict[str, Sequence[str]] | None = None,\n            pass_fds: tuple[int, ...] = (),\n            remote_control_fd: int = -1,\n    ) -> Window:\n        return self.new_window(\n            use_shell=False, cmd=special_window.cmd, stdin=special_window.stdin,\n            override_title=special_window.override_title,\n            cwd_from=special_window.cwd_from, cwd=special_window.cwd, overlay_for=special_window.overlay_for,\n            env=special_window.env, location=location, copy_colors_from=copy_colors_from,\n            allow_remote_control=allow_remote_control, watchers=special_window.watchers, overlay_behind=special_window.overlay_behind,\n            hold=special_window.hold, remote_control_passwords=remote_control_passwords, pass_fds=pass_fds, remote_control_fd=remote_control_fd,\n        )\n\n    @ac('win', 'Close all windows in the tab other than the currently active window')\n    def close_other_windows_in_tab(self) -> None:\n        if len(self.windows) > 1:\n            active_window = self.active_window\n            for window in tuple(self.windows):\n                if window is not active_window:\n                    self.remove_window(window)\n\n    def move_window_to_top_of_group(self, window: Window) -> bool:\n        return self.windows.move_window_to_top_of_group(window)\n\n    def overlay_parent(self, window: Window) -> Window | None:\n        prev: Window | None = None\n        for x in self.windows.windows_in_group_of(window):\n            if x is window:\n                break\n            prev = x\n        return prev\n\n    def remove_window(self, window: Window, destroy: bool = True, do_post_removal_update: bool = True) -> None:\n        self.windows.remove_window(window)\n        if destroy:\n            remove_window(self.os_window_id, self.id, window.id)\n        else:\n            detach_window(self.os_window_id, self.id, window.id)\n        if do_post_removal_update:\n            self.post_window_removal_update()\n\n    def post_window_removal_update(self) -> None:\n        self.mark_tab_bar_dirty()\n        self.relayout()\n        active_window = self.active_window\n        if active_window:\n            self.title_changed(active_window)\n        set_active_window(self.os_window_id, self.id, active_window.id if active_window else 0)\n\n    def detach_window(self, window: Window) -> tuple[Window, ...]:\n        windows = list(self.windows.windows_in_group_of(window))\n        for w in reversed(windows):\n            self.remove_window(w, destroy=False, do_post_removal_update=False)\n        self.post_window_removal_update()\n        return tuple(windows)\n\n    def attach_window(self, window: Window, overlay_for: int | None = None) -> None:\n        window.change_tab(self)\n        attach_window(self.os_window_id, self.id, window.id)\n        self._add_window(window, overlay_for=overlay_for)\n\n    def attach_windows(self, windows: Iterable[Window]) -> None:\n        overlay_for: int | None = None\n        for window in windows:\n            self.attach_window(window, overlay_for)\n            overlay_for = window.id\n\n    def set_active_window(self, x: Window | int, for_keep_focus: Window | None = None) -> None:\n        if (w := self.windows.window_for_id(x) if isinstance(x, int) else x) is not None:\n            self.windows.set_active_window_group_for(w, for_keep_focus=for_keep_focus)\n            self.windows.move_window_to_top_of_group(w)\n\n    def get_nth_window(self, n: int) -> Window | None:\n        if self.windows:\n            return self.current_layout.nth_window(self.windows, n)\n        return None\n\n    @ac('win', '''\n        Focus the nth window if positive or the previously active windows if negative. When the number is larger\n        than the number of windows focus the last window. For example::\n\n            # focus the previously active window\n            map ctrl+p nth_window -1\n            # focus the first window\n            map ctrl+1 nth_window 0\n        ''')\n    def nth_window(self, num: int = 0) -> None:\n        if self.windows:\n            if num < 0:\n                self.windows.make_previous_group_active(-num)\n            elif self.windows.num_groups:\n                self.current_layout.activate_nth_window(self.windows, min(num, self.windows.num_groups - 1))\n            self.relayout_borders()\n\n    @ac('win', 'Focus the first window')\n    def first_window(self) -> None:\n        self.nth_window(0)\n\n    @ac('win', 'Focus the second window')\n    def second_window(self) -> None:\n        self.nth_window(1)\n\n    @ac('win', 'Focus the third window')\n    def third_window(self) -> None:\n        self.nth_window(2)\n\n    @ac('win', 'Focus the fourth window')\n    def fourth_window(self) -> None:\n        self.nth_window(3)\n\n    @ac('win', 'Focus the fifth window')\n    def fifth_window(self) -> None:\n        self.nth_window(4)\n\n    @ac('win', 'Focus the sixth window')\n    def sixth_window(self) -> None:\n        self.nth_window(5)\n\n    @ac('win', 'Focus the seventh window')\n    def seventh_window(self) -> None:\n        self.nth_window(6)\n\n    @ac('win', 'Focus the eighth window')\n    def eighth_window(self) -> None:\n        self.nth_window(7)\n\n    @ac('win', 'Focus the ninth window')\n    def ninth_window(self) -> None:\n        self.nth_window(8)\n\n    @ac('win', 'Focus the tenth window')\n    def tenth_window(self) -> None:\n        self.nth_window(9)\n\n    def _next_window(self, delta: int = 1) -> None:\n        if len(self.windows) > 1:\n            self.current_layout.next_window(self.windows, delta)\n            self.relayout_borders()\n\n    @ac('win', 'Focus the next window in the current tab. Does not traverse overlay windows.')\n    def next_window(self) -> None:\n        self._next_window()\n\n    @ac('win', 'Focus the previous window in the current tab. Does not traverse overlay windows.')\n    def previous_window(self) -> None:\n        self._next_window(-1)\n\n    prev_window = previous_window\n\n    def most_recent_group(self, groups: Sequence[int]) -> int | None:\n        groups_set = frozenset(groups)\n\n        for window_id in reversed(self.windows.active_window_history):\n            group = self.windows.group_for_window(window_id)\n            if group and group.id in groups_set:\n                return group.id\n\n        if groups:\n            return groups[0]\n        return None\n\n    def nth_active_window_id(self, n: int = 0) -> int:\n        if n <= 0:\n            return self.active_window.id if self.active_window else 0\n        ids = tuple(reversed(self.windows.active_window_history))\n        return ids[min(n - 1, len(ids) - 1)] if ids else 0\n\n    def neighboring_group_id(self, which: EdgeLiteral) -> int | None:\n        neighbors = self.current_layout.neighbors(self.windows)\n        candidates = neighbors.get(which)\n        if candidates:\n            return self.most_recent_group(candidates)\n        return None\n\n    @ac('win', '''\n        Focus the neighboring window in the current tab\n\n        For example::\n\n            map ctrl+left neighboring_window left\n            map ctrl+down neighboring_window bottom\n        ''')\n    def neighboring_window(self, which: EdgeLiteral) -> None:\n        neighbor = self.neighboring_group_id(which)\n        if neighbor:\n            self.windows.set_active_group(neighbor)\n\n    @ac('win', '''\n        Move the window in the specified direction\n\n        For example::\n\n            map ctrl+left move_window left\n            map ctrl+down move_window bottom\n        ''')\n    def move_window(self, delta: EdgeLiteral | int = 1) -> None:\n        if isinstance(delta, int):\n            if self.current_layout.move_window(self.windows, delta):\n                self.relayout()\n        elif isinstance(delta, str):\n            neighbor = self.neighboring_group_id(delta)\n            if neighbor:\n                if self.current_layout.move_window_to_group(self.windows, neighbor):\n                    self.relayout()\n\n    def swap_active_window_with(self, window_id: int) -> None:\n        group = self.windows.group_for_window(window_id)\n        if group is not None:\n            w = self.active_window\n            if w is not None and w.id != window_id:\n                if self.current_layout.move_window_to_group(self.windows, group.id):\n                    self.relayout()\n\n    @property\n    def all_window_ids_except_active_window(self) -> set[int]:\n        all_window_ids = {w.id for w in self}\n        aw = self.active_window\n        if aw is not None:\n            all_window_ids.discard(aw.id)\n        return all_window_ids\n\n    @ac('win', '''\n        Focus a visible window by pressing the number of the window. Window numbers are displayed\n        over the windows for easy selection in this mode. See :opt:`visual_window_select_characters`.\n        ''')\n    def focus_visible_window(self) -> None:\n        def callback(tab: Tab | None, window: Window | None) -> None:\n            if tab and window:\n                tab.set_active_window(window)\n\n        get_boss().visual_window_select_action(self, callback, 'Choose window to switch to', only_window_ids=self.all_window_ids_except_active_window)\n\n    @ac('win', 'Swap the current window with another window in the current tab, selected visually. See :opt:`visual_window_select_characters`')\n    def swap_with_window(self) -> None:\n        def callback(tab: Tab | None, window: Window | None) -> None:\n            if tab and window:\n                tab.swap_active_window_with(window.id)\n        get_boss().visual_window_select_action(self, callback, 'Choose window to swap with', only_window_ids=self.all_window_ids_except_active_window)\n\n    @ac('win', 'Move active window to the top (make it the first window)')\n    def move_window_to_top(self) -> None:\n        n = self.windows.active_group_idx\n        if n > 0:\n            self.move_window(-n)\n\n    @ac('win', 'Move active window forward (swap it with the next window)')\n    def move_window_forward(self) -> None:\n        self.move_window()\n\n    @ac('win', 'Move active window backward (swap it with the previous window)')\n    def move_window_backward(self) -> None:\n        self.move_window(-1)\n\n    def list_windows(self, self_window: Window | None = None, window_filter: Callable[[Window], bool] | None = None) -> Generator[WindowDict, None, None]:\n        active_window = self.active_window\n        cl = self.current_layout\n        for w in self:\n            if window_filter is None or window_filter(w):\n                yield w.as_dict(\n                    is_active=w is active_window,\n                    is_focused=w.os_window_id == current_focused_os_window_id() and w is active_window,\n                    is_self=w is self_window,\n                    neighbors_map=cl.neighbors_for_window(w, self.windows)\n                )\n\n    def list_groups(self) -> list[dict[str, Any]]:\n        return [g.as_simple_dict() for g in self.windows.groups]\n\n    def matches_query(\n        self, field: str, query: str, active_tab_manager: Optional['TabManager'] = None,\n        active_session: str = '', most_recent_session: str = ''\n    ) -> bool:\n        if field == 'title':\n            return re.search(query, self.effective_title) is not None\n        if field == 'id':\n            return query == str(self.id)\n        if field in ('window_id', 'window_title'):\n            field = field.partition('_')[-1]\n            for w in self:\n                if w.matches_query(field, query):\n                    return True\n            return False\n        if field == 'index':\n            if active_tab_manager and len(active_tab_manager.tabs):\n                idx = (int(query) + len(active_tab_manager.tabs)) % len(active_tab_manager.tabs)\n                return active_tab_manager.tabs[idx] is self\n            return False\n        if field == 'recent':\n            if active_tab_manager and len(active_tab_manager.tabs):\n                return self is active_tab_manager.nth_active_tab(int(query))\n            return False\n        if field == 'state':\n            if query == 'active':\n                tm = self.tab_manager_ref()\n                return tm is not None and self is tm.active_tab\n            if query == 'focused':\n                return active_tab_manager is not None and self is active_tab_manager.active_tab and self.os_window_id == last_focused_os_window_id()\n            if query == 'needs_attention':\n                for w in self:\n                    if w.needs_attention:\n                        return True\n            if query == 'parent_active':\n                return active_tab_manager is not None and self.tab_manager_ref() is active_tab_manager\n            if query == 'parent_focused':\n                return active_tab_manager is not None and self.tab_manager_ref() is active_tab_manager and self.os_window_id == last_focused_os_window_id()\n            if query == 'focused_os_window':\n                return self.os_window_id == last_focused_os_window_id()\n            return False\n        if field == 'session':\n            match query:\n                case '.':\n                    return self.created_in_session_name == active_session\n                case '~':\n                    return self.created_in_session_name == active_session or self.created_in_session_name == most_recent_session\n            return re.search(query, self.created_in_session_name) is not None\n        return False\n\n    def __iter__(self) -> Iterator[Window]:\n        return iter(self.windows)\n\n    def __len__(self) -> int:\n        return len(self.windows)\n\n    @property\n    def num_window_groups(self) -> int:\n        return self.windows.num_groups\n\n    @property\n    def active_session_name(self) -> str:\n        w = self.active_window\n        return '' if w is None else w.created_in_session_name\n\n    def __contains__(self, window: Window) -> bool:\n        return window in self.windows\n\n    def destroy(self) -> None:\n        evict_cached_layouts(self.id)\n        for w in self.windows:\n            w.destroy()\n        self.windows = WindowList(self)\n\n    def __repr__(self) -> str:\n        return f'Tab(title={self.effective_title}, id={self.id})'\n\n    def make_active(self) -> None:\n        tm = self.tab_manager_ref()\n        if tm is not None:\n            tm.set_active_tab(self)\n# }}}\n\n\nclass TabBeingDropped(NamedTuple):\n    data: TabBarData\n    tab_ids: Sequence[int] = ()\n    last_drop_move_x: int = -1\n\n\nclass TabManager:  # {{{\n\n    confirm_close_window_id: int = 0\n    num_of_windows_with_progress: int = 0\n    total_progress: int = 0\n    has_indeterminate_progress: bool = False\n    tab_being_dropped: TabBeingDropped | None = None\n\n    def __init__(self, os_window_id: int, args: CLIOptions, wm_class: str, wm_name: str, startup_session: SessionType | None = None):\n        self.os_window_id = os_window_id\n        self.wm_class = wm_class\n        self.created_in_session_name = startup_session.session_name if startup_session else ''\n        self.recent_mouse_events: Deque[TabMouseEvent] = deque()\n        self.recent_title_bar_mouse_events: Deque[TabMouseEvent] = deque()\n        self.wm_name = wm_name\n        self.args = args\n        self.tab_bar_hidden = get_options().tab_bar_style == 'hidden'\n        self.tabs: list[Tab] = []\n        self.active_tab_history: Deque[int] = deque()\n        self.tab_bar = TabBar(self.os_window_id)\n        self._active_tab_idx = 0\n\n        if startup_session is not None:\n            self.add_tabs_from_session(startup_session)\n\n    @update_tab_bar_visibility\n    def add_tabs_from_session(self, session: SessionType, session_name: str = '') -> None:\n        active_tab = self.active_tab\n        added_tabs = []\n        for i, t in enumerate(session.tabs):\n            tab = Tab(self, session_tab=t, session_name=session_name or self.created_in_session_name)\n            self.tabs.append(tab)\n            added_tabs.append(tab)\n            if i == session.active_tab_idx:\n                active_tab = tab\n\n        # Handle focus_tab_spec if specified\n        if session.focus_tab_spec is not None:\n            spec = session.focus_tab_spec.strip()\n            # Try to parse as a plain number (index)\n            try:\n                idx = int(spec)\n                # Clamp to valid range\n                idx = max(0, min(idx, len(added_tabs) - 1))\n                active_tab = added_tabs[idx]\n            except ValueError:\n                # Not a plain number, treat as match expression\n                from .fast_data_types import get_boss\n                boss = get_boss()\n                matched_tabs = list(boss.match_tabs(spec, self.tabs))\n                if matched_tabs:\n                    active_tab = matched_tabs[0]\n\n        if active_tab is not None:\n            idx = self.tabs.index(active_tab)\n            self._set_active_tab(idx)\n            # We need to update last_focused_at so that switch_to_session is\n            # called after the session is created respects the result of\n            # focus_tab.\n            if (at := self.active_tab) and (w := at.active_window):\n                w.last_focused_at = monotonic()\n        active_tab = self.active_tab\n        for tab in added_tabs:\n            w = tab.active_window\n            for q in tab:\n                q.focus_changed(w is q and tab is active_tab)\n\n    @property\n    def active_tab_idx(self) -> int:\n        return self._active_tab_idx\n\n    @active_tab_idx.setter\n    def active_tab_idx(self, val: int) -> None:\n        new_active_tab_idx = max(0, min(val, len(self.tabs) - 1))\n        if new_active_tab_idx == self._active_tab_idx:\n            return\n        try:\n            old_active_tab: Tab | None = self.tabs[self._active_tab_idx]\n        except Exception:\n            old_active_tab = None\n        else:\n            assert old_active_tab is not None\n            add_active_id_to_history(self.active_tab_history, old_active_tab.id)\n        self._active_tab_idx = new_active_tab_idx\n        try:\n            new_active_tab: Tab | None = self.tabs[self._active_tab_idx]\n        except Exception:\n            new_active_tab = None\n        if old_active_tab is not new_active_tab:\n            if old_active_tab is not None:\n                w = old_active_tab.active_window\n                if w is not None:\n                    w.focus_changed(False)\n            if new_active_tab is not None:\n                w = new_active_tab.active_window\n                if w is not None:\n                    w.focus_changed(True)\n\n    def refresh_sprite_positions(self) -> None:\n        if not self.tab_bar_hidden:\n            self.tab_bar.screen.refresh_sprite_positions()\n\n    @property\n    def tab_bar_should_be_visible(self) -> bool:\n        if self.tab_being_dropped is not None:\n            return True  # keep tab bar visible in the dest\n        count = get_options().tab_bar_min_tabs\n        if count < 1:\n            return True\n        tab_id, drag_started = get_tab_being_dragged()[:2]\n        if drag_started and self.tab_for_id(tab_id) is not None:\n            return True  # keep tab bar visible in the source\n        for t in self.tabs_to_be_shown_in_tab_bar:\n            count -= 1\n            if count < 1:\n                return True\n        return count < 1\n\n    def _set_active_tab(self, idx: int, store_in_history: bool = True) -> None:\n        if store_in_history:\n            self.active_tab_idx = idx\n        else:\n            self._active_tab_idx = idx\n        set_active_tab(self.os_window_id, idx)\n\n    def layout_tab_bar(self) -> None:\n        # set tab_bar_should_be_visible so that tab_bar.layout() gets correct dimensions\n        self.mark_tab_bar_dirty()\n        self.tab_bar.layout()\n\n    @property\n    def any_window(self) -> Window | None:\n        for t in self:\n            for w in t:\n                return w\n        return None\n\n    def mark_tab_bar_dirty(self) -> None:\n        should_be_shown = not self.tab_bar_hidden and self.tab_bar_should_be_visible\n        mark_tab_bar_dirty(self.os_window_id, should_be_shown)\n        w = self.active_window or self.any_window\n        if w is not None:\n            data = {'tab_manager': self}\n            boss = get_boss()\n            for watcher in global_watchers().on_tab_bar_dirty:\n                watcher(boss, w, data)\n\n    def update_tab_bar_data(self) -> None:\n        self.tab_bar.update(self.tab_bar_data)\n\n    def title_changed(self, tab: Tab) -> None:\n        self.mark_tab_bar_dirty()\n        if tab is self.active_tab:\n            sync_os_window_title(self.os_window_id)\n\n    def resize(self, only_tabs: bool = False) -> None:\n        if not only_tabs:\n            if not self.tab_bar_hidden:\n                self.layout_tab_bar()\n        for tab in self.tabs:\n            tab.relayout()\n\n    def set_active_tab_idx(self, idx: int) -> None:\n        self._set_active_tab(idx)\n        tab = self.active_tab\n        if tab is not None:\n            tab.relayout_borders()\n        self.mark_tab_bar_dirty()\n\n    @update_tab_bar_visibility\n    def set_active_tab(self, tab: Tab, for_keep_focus: Tab | None = None) -> bool:\n        try:\n            idx = self.tabs.index(tab)\n        except Exception:\n            return False\n        self.set_active_tab_idx(idx)\n        h = self.active_tab_history\n        if for_keep_focus and len(h) > 2 and h[-2] == for_keep_focus.id and h[-1] != for_keep_focus.id:\n            h.pop()\n            h.pop()\n        return True\n\n    @property\n    def tabs_to_be_shown_in_tab_bar(self) -> Iterable[Tab]:\n        f = get_options().tab_bar_filter\n        if f:\n            at = self.active_tab\n            m = frozenset(get_boss().match_tabs(f, all_tabs=self))\n            return (t for t in self if t is at or t in m)\n        return self.tabs\n\n    def next_tab(self, delta: int = 1) -> None:\n        if (len(tabs := tuple(self.tabs_to_be_shown_in_tab_bar))) == len(self.tabs):\n            if (num := len(tabs)) > 1:\n                self.set_active_tab_idx((self.active_tab_idx + num + delta) % num)\n        else:\n            num = len(tabs)\n            at = self.active_tab\n            if at is not None:\n                active_idx = tabs.index(at)\n                new_active_tab = (active_idx + num + delta) % num\n                self.set_active_tab(tabs[new_active_tab])\n\n    def toggle_tab(self, match_expression: str) -> None:\n        tabs = set(get_boss().match_tabs(match_expression, all_tabs=self))\n        if not tabs:\n            get_boss().show_error(_('No matching tab'), _('No tab found matching the expression: {}').format(match_expression))\n            return\n        if self.active_tab and self.active_tab in tabs:\n            self.goto_tab(-1)\n        else:\n            for x in tabs:\n                self.set_active_tab(x)\n                break\n\n    def tab_at_location(self, loc: str) -> Tab | None:\n        tabs = tuple(self.tabs_to_be_shown_in_tab_bar)\n        if loc == 'prev':\n            if self.active_tab_history:\n                return self.tab_for_id(self.active_tab_history[-1])\n        elif loc in ('left', 'right'):\n            delta = -1 if loc == 'left' else 1\n            if (at := self.active_tab) is not None:\n                try:\n                    active_idx = tabs.index(at)\n                except ValueError:\n                    return None\n                idx = (len(tabs) + active_idx + delta) % len(tabs)\n                return tabs[idx]\n        return None\n\n    def goto_tab(self, tab_num: int) -> None:\n        tabs = tuple(self.tabs_to_be_shown_in_tab_bar)\n        if tab_num >= len(tabs):\n            tab_num = max(0, len(tabs) - 1)\n        if tab_num >= 0:\n            self.set_active_tab(tabs[tab_num])\n        elif self.active_tab_history:\n            try:\n                old_active_tab_id = self.active_tab_history[tab_num]\n            except IndexError:\n                old_active_tab_id = self.active_tab_history[0]\n            if tab := self.tab_for_id(old_active_tab_id):\n                self.set_active_tab(tab)\n\n    def nth_active_tab(self, n: int = 0) -> Tab | None:\n        if n <= 0:\n            return self.active_tab\n        tab_ids = tuple(reversed(self.active_tab_history))\n        return self.tab_for_id(tab_ids[min(n - 1, len(tab_ids) - 1)]) if tab_ids else None\n\n    def __iter__(self) -> Iterator[Tab]:\n        return iter(self.tabs)\n\n    def __len__(self) -> int:\n        return len(self.tabs)\n\n    def list_tabs(\n        self, self_window: Window | None = None,\n        tab_filter: Callable[[Tab], bool] | None = None,\n        window_filter: Callable[[Window], bool] | None = None\n    ) -> Generator[TabDict, None, None]:\n        active_tab = self.active_tab\n        for tab in self:\n            if tab_filter is None or tab_filter(tab):\n                windows = list(tab.list_windows(self_window, window_filter))\n                if windows:\n                    yield {\n                        'id': tab.id,\n                        'is_focused': tab is active_tab and tab.os_window_id == current_focused_os_window_id(),\n                        'is_active': tab is active_tab,\n                        'title': tab.name or tab.title,\n                        'title_overridden': bool(tab.name),\n                        'layout': str(tab.current_layout.name),\n                        'layout_state': tab.current_layout.serialize(tab.windows),\n                        'layout_opts': tab.current_layout.layout_opts.serialized(),\n                        'enabled_layouts': tab.enabled_layouts,\n                        'windows': windows,\n                        'groups': tab.list_groups(),\n                        'active_window_history': list(tab.windows.active_window_history),\n                    }\n\n    def serialize_state(self) -> dict[str, Any]:\n        return {\n            'version': 1,\n            'id': self.os_window_id,\n            'tabs': [tab.serialize_state() for tab in self],\n            'active_tab_idx': self.active_tab_idx,\n        }\n\n    def serialize_state_as_session(\n        self, session_path: str, matched_windows: frozenset[Window] | None, ser_opts: SaveAsSessionOptions,\n        is_first: bool = False\n    ) -> list[str]:\n        ans = []\n        active_tab_index = -1\n        for i, tab in enumerate(self.tabs):\n            if tab is self.active_tab:\n                active_tab_index = i\n            ans.extend(tab.serialize_state_as_session(session_path, matched_windows, ser_opts))\n        if ans:\n            prefix = [] if is_first else ['', '', 'new_os_window']\n            if self.wm_class and self.wm_class != appname:\n                prefix.append(f'os_window_class {self.wm_class}')\n            if self.wm_name and self.wm_name != appname:\n                prefix.append(f'os_window_name {self.wm_name}')\n            ans = prefix + ans\n            # Add focus_tab command to preserve the active tab\n            if active_tab_index >= 0:\n                ans.append('')\n                ans.append(f'focus_tab {active_tab_index}')\n        return ans\n\n    @property\n    def active_tab(self) -> Tab | None:\n        return self.tabs[self.active_tab_idx] if 0 <= self.active_tab_idx < len(self.tabs) else None\n\n    @property\n    def active_window(self) -> Window | None:\n        return t.active_window if (t := self.active_tab) else None\n\n    def tab_for_id(self, tab_id: int) -> Tab | None:\n        for t in self.tabs:\n            if t.id == tab_id:\n                return t\n        return None\n\n    def move_tab(self, delta: int = 1) -> None:\n        tabs = tuple(self.tabs_to_be_shown_in_tab_bar)\n        if len(tabs) > 1:\n            if (at := self.active_tab) is None:\n                return\n            try:\n                filtered_idx = tabs.index(at)\n            except ValueError:\n                return\n            new_active_tab = tabs[(filtered_idx + len(tabs) + delta) % len(tabs)]\n            idx = self.tabs.index(at)\n            nidx = self.tabs.index(new_active_tab)\n            step = 1 if idx < nidx else -1\n            for i in range(idx, nidx, step):\n                self.swap_tabs(i, i + step)\n            self._set_active_tab(nidx)\n            self.mark_tab_bar_dirty()\n\n    @update_tab_bar_visibility\n    def new_tab(\n        self,\n        special_window: SpecialWindowInstance | None = None,\n        cwd_from: CwdRequest | None = None,\n        as_neighbor: bool = False,\n        empty_tab: bool = False,\n        location: str = 'last',\n    ) -> Tab:\n        idx = len(self.tabs)\n        tabs = tuple(self.tabs_to_be_shown_in_tab_bar)\n        orig_active_tab_idx = 0\n        with suppress(ValueError):\n            orig_active_tab_idx = tabs.index(self.active_tab)\n        session_name = ''\n        if cwd_from is not None and (sw := cwd_from.window):\n            session_name = sw.created_in_session_name\n            if not session_name and (sw_tab := sw.tabref()):\n                session_name = sw_tab.created_in_session_name\n        t = Tab(self, no_initial_window=True, session_name=session_name) if empty_tab else Tab(\n                self, special_window=special_window, cwd_from=cwd_from, session_name=session_name)\n        if not empty_tab and session_name:\n            for w in t:\n                w.created_in_session_name = session_name\n        self.tabs.append(t)\n        tabs = tabs + (t,)\n        if as_neighbor:\n            location = 'after'\n        if location == 'neighbor':\n            location = 'after'\n        if location == 'default':\n            location = 'last'\n        if len(tabs) > 1 and location != 'last':\n            if location == 'first':\n                desired_idx = 0\n            else:\n                desired_idx = orig_active_tab_idx + (0 if location == 'before' else 1)\n            desired_idx = self.tabs.index(tabs[desired_idx])\n            if idx != desired_idx:\n                for i in range(idx, desired_idx, -1):\n                    self.swap_tabs(i, i-1)\n                idx = desired_idx\n        self._set_active_tab(idx)\n        self.mark_tab_bar_dirty()\n        return t\n\n    @update_tab_bar_visibility\n    def remove(self, removed_tab: Tab) -> None:\n        active_tab_before_removal = self.active_tab\n        tabs = tuple(self.tabs_to_be_shown_in_tab_bar)\n        try:\n            idx_before_removal = tabs.index(active_tab_before_removal)\n        except Exception:\n            idx_before_removal = -1\n        remove_tab(self.os_window_id, removed_tab.id)\n        self.tabs.remove(removed_tab)\n        while True:\n            try:\n                self.active_tab_history.remove(removed_tab.id)\n            except ValueError:\n                break\n\n        def remove_from_end_of_active_history(tab: Tab) -> None:\n            while self.active_tab_history and self.active_tab_history[-1] == tab.id:\n                self.active_tab_history.pop()\n\n        def previous_active_tab() -> Tab | None:\n            while self.active_tab_history:\n                tab_id = self.active_tab_history.pop()\n                if tab_id != removed_tab.id:\n                    if (ans := self.tab_for_id(tab_id)) is not None:\n                        return ans\n            return self.tabs[0] if self.tabs else None\n\n        if active_tab_before_removal is removed_tab:\n            if len(tabs) == 0 or (len(tabs) == 1 and removed_tab is tabs[0]):\n                tab = previous_active_tab()\n                if tab is None:\n                    self._active_tab_idx = 0\n                else:\n                    self._set_active_tab(self.tabs.index(tab), store_in_history=False)\n            else:\n                next_active_tab: Tab | None = None\n                match get_options().tab_switch_strategy:\n                    case 'previous':\n                        while self.active_tab_history and next_active_tab is None:\n                            tab_id = self.active_tab_history.pop()\n                            next_active_tab = self.tab_for_id(tab_id)\n                            if next_active_tab not in tabs:\n                                next_active_tab = None\n                    case 'left':\n                        tab_id = tabs.index(active_tab_before_removal)\n                        if tab_id > 0:\n                            next_active_tab = tabs[tab_id - 1]\n                            remove_from_end_of_active_history(next_active_tab)\n                    case 'right':\n                        tab_id = tabs.index(active_tab_before_removal)\n                        if tab_id < len(tabs) - 1:\n                            next_active_tab = tabs[tab_id + 1]\n                            remove_from_end_of_active_history(next_active_tab)\n                    case 'last':\n                        next_active_tab = tabs[-1]\n                        remove_from_end_of_active_history(next_active_tab)\n                if next_active_tab not in self.tabs:\n                    if idx_before_removal > -1 and (left_tabs := tuple(t for t in tabs if t is not removed_tab)):\n                        next_active_tab = left_tabs[max(0, min(idx_before_removal, len(left_tabs) - 1))]\n                    else:\n                        next_active_tab = self.tabs[max(0, min(self.active_tab_idx, len(self.tabs) - 1))]\n                self._set_active_tab(self.tabs.index(next_active_tab), store_in_history=False)\n        else:\n            if len(self.tabs):\n                if active_tab_before_removal is None:\n                    self._set_active_tab(0, store_in_history=False)\n                else:\n                    self._set_active_tab(self.tabs.index(active_tab_before_removal), store_in_history=False)\n            else:\n                self._active_tab_idx = 0\n        self.mark_tab_bar_dirty()\n        removed_tab.destroy()\n\n    @property\n    def tab_bar_data(self) -> Sequence[TabBarData]:\n        at = self.active_tab\n        tab_being_dragged_from_here = False\n        dragged_tab_id, drag_started = get_tab_being_dragged()[:2]\n        if drag_started:\n            tab_being_dragged_from_here = self.tab_for_id(dragged_tab_id) is not None\n        if self.tab_being_dropped is None:\n            if tab_being_dragged_from_here:\n                return tuple(t.data_for_tab_bar(t is at) for t in self.tabs_to_be_shown_in_tab_bar if t.id != dragged_tab_id)\n            return tuple(t.data_for_tab_bar(t is at) for t in self.tabs_to_be_shown_in_tab_bar)\n        tmap = {t.id:t for t in self.tabs}\n        at = self.active_tab\n        ans = []\n        for tid in self.tab_being_dropped.tab_ids:\n            if tid == dragged_tab_id:\n                ans.append(self.tab_being_dropped.data)\n            else:\n                tab = tmap[tid]\n                ans.append(tab.data_for_tab_bar(tab is at))\n        return ans\n\n    def apply_tab_ordering(self, tab_ids: Sequence[int]) -> None:\n        id_map = {t.id:t for t in self.tabs}\n        ordered_ids = frozenset(tab_ids)\n        positions = (i for i, t in enumerate(self.tabs) if t.id in ordered_ids)\n        for pos, tab_id in zip(positions, tab_ids):\n            self.tabs[pos] = id_map[tab_id]\n        reorder_tabs(self.os_window_id, *(t.id for t in self.tabs))\n\n    @update_tab_bar_visibility\n    def on_tab_drop_move(self, tab_id: int = 0, is_dest: bool = False, x: int = 0, y: int = 0) -> None:\n        if not is_dest:\n            if self.tab_being_dropped:\n                self.tab_being_dropped = None\n                self.layout_tab_bar()\n            return\n        if self.tab_bar_should_be_visible:\n            all_tabs = [t.tab_id for t in self.tab_bar.last_laid_out_tabs]\n        else:\n            all_tabs = [t.tab_id for t in self.tab_bar_data]\n        force_update = False\n        if self.tab_being_dropped is None:\n            tab = get_boss().tab_for_id(tab_id)\n            if tab is None:\n                return\n            tab_data = tab.data_for_tab_bar(tab is get_boss().active_tab)\n            if tab_id not in all_tabs:\n                all_tabs.append(tab_id)\n            _, _, start_x, _ = get_tab_being_dragged()\n            self.tab_being_dropped = TabBeingDropped(data=tab_data, tab_ids=all_tabs, last_drop_move_x=int(start_x))\n            mouse_moved_left = False\n            force_update = True\n        if x == self.tab_being_dropped.last_drop_move_x and not force_update:\n            return\n        mouse_moved_left = x < self.tab_being_dropped.last_drop_move_x\n        old_tab_ids = self.tab_being_dropped.tab_ids\n        idx_under_mouse = -1\n        if (tab_id_under_mouse := self.tab_bar.tab_id_at(x)):\n            with suppress(Exception):\n                idx_under_mouse = old_tab_ids.index(tab_id_under_mouse)\n        if idx_under_mouse < 0:\n            idx_under_mouse = 0 if x < 20 else len(old_tab_ids) - 1\n        old_idx_under_mouse = old_tab_ids.index(tab_id)\n        idx_moved_left = old_idx_under_mouse > idx_under_mouse\n        new_tab_ids = old_tab_ids\n        if mouse_moved_left == idx_moved_left:\n            new_tab_ids = list(old_tab_ids)\n            new_tab_ids[idx_under_mouse], new_tab_ids[old_idx_under_mouse] = new_tab_ids[old_idx_under_mouse], new_tab_ids[idx_under_mouse]\n        self.tab_being_dropped = self.tab_being_dropped._replace(last_drop_move_x=x, tab_ids=new_tab_ids)\n        if force_update or self.tab_being_dropped.tab_ids != old_tab_ids:\n            self.layout_tab_bar()\n\n    @update_tab_bar_visibility\n    def on_tab_drop(self, x: int, y: int, bypass_move: bool = False) -> None:\n        if (td := self.tab_being_dropped) is None:\n            return\n        if (tab := get_boss().tab_for_id(td.data.tab_id)) is None:\n            return\n        if not bypass_move:\n            self.on_tab_drop_move(td.data.tab_id, True, x, y)\n        if (td := self.tab_being_dropped) is None:\n            return\n        self.tab_being_dropped = None\n        atid = self.active_tab.id if self.active_tab else 0\n        set_tab_being_dragged()\n        if tab.os_window_id != self.os_window_id:\n            if (t := get_boss()._move_tab_to(tab, self.os_window_id)) is not None:\n                n = list(td.tab_ids)\n                idx = n.index(td.data.tab_id)\n                n[idx] = t.id\n                td = td._replace(tab_ids=n)\n        self.apply_tab_ordering(td.tab_ids)\n        if atid and tab.os_window_id == self.os_window_id and (tab := self.tab_for_id(atid)):\n            idx = self.tabs.index(tab)\n            self._set_active_tab(idx, store_in_history=False)\n        self.layout_tab_bar()\n\n    def swap_tabs(self, idx: int, nidx: int) -> None:\n        if idx != nidx:\n            self.tabs[idx], self.tabs[nidx] = self.tabs[nidx], self.tabs[idx]\n            swap_tabs(self.os_window_id, idx, nidx)\n\n    def start_tab_drag(self, pixels: bytes, width: int, height: int) -> None:\n        dragged_tab_id = get_tab_being_dragged()[0]\n        for i, tab in enumerate(self.tabs_to_be_shown_in_tab_bar):\n            if tab.id == dragged_tab_id:\n                td = tab.data_for_tab_bar(tab is self.active_tab)\n                title = apply_title_template(self.tab_bar.draw_data, td, i+1)\n                title = re.sub(r'\\x1b\\[.+?[a-zA-Z]', '', title).strip()  # strip CSI codes ]\n                title = replace_c0_codes_except_nl_space_tab(title.encode()).decode()\n                title = re.sub(r'\\n', ' ', title)\n                opts = get_options()\n                if td.is_active:\n                    fg = color_as_int(opts.active_tab_foreground)\n                    bg = color_as_int(opts.active_tab_background)\n                else:\n                    fg = color_as_int(opts.inactive_tab_foreground)\n                    bg = color_as_int(opts.inactive_tab_background)\n                title_pixels = draw_single_line_of_text(self.os_window_id, title, 0xff000000 | fg, 0xff000000 | bg, width)\n                title_height = len(title_pixels) // (width * 4)\n                thumbnails = ((title_pixels, width, title_height), (title_pixels + pixels, width, title_height + height))\n                drag_data = {\n                    f'application/net.kovidgoyal.kitty-tab-{os.getpid()}': str(tab.id).encode(),\n                }\n                start_drag_with_data(self.os_window_id, drag_data, thumbnails)\n                break\n        else:\n            set_tab_being_dragged()\n\n    def handle_tab_bar_mouse(self, x: float, y: float, button: int, modifiers: int, action: int) -> None:\n        if button == -1:  # motion\n            dragged_tab_id, drag_started, start_x, start_y = get_tab_being_dragged()\n            if dragged_tab_id and self.tab_for_id(dragged_tab_id) is not None and not drag_started:\n                threshold = get_options().tab_bar_drag_threshold\n                if threshold and math.sqrt((x-start_x)**2 + (y-start_y)**2) > threshold:\n                    set_tab_being_dragged(dragged_tab_id, True, start_x, start_y)\n                    request_callback_with_thumbnail(\"start_tab_drag\", self.os_window_id)\n            return\n\n        tab = self.tab_for_id(self.tab_bar.tab_id_at(int(x)))\n        now = monotonic()\n        if tab is None:\n            if button == GLFW_MOUSE_BUTTON_LEFT and action == GLFW_RELEASE and len(self.recent_mouse_events) > 2:\n                ci = get_click_interval()\n                prev, prev2 = self.recent_mouse_events[-1], self.recent_mouse_events[-2]\n                if (\n                    prev.button == button and prev2.button == button and\n                    prev.action == GLFW_PRESS and prev2.action == GLFW_RELEASE and\n                    prev.tab_id == 0 and prev2.tab_id == 0 and\n                    now - prev.at <= ci and now - prev2.at <= 2 * ci\n                ):  # double click\n                    self.new_tab()\n                    self.recent_mouse_events.clear()\n                    return\n        else:\n            if button == GLFW_MOUSE_BUTTON_LEFT:\n                if action == GLFW_PRESS:\n                    set_tab_being_dragged(tab.id, False, x, y)\n                else:\n                    drag_started = get_tab_being_dragged()[1]\n                    if not drag_started:\n                        if len(self.recent_mouse_events) > 2:\n                            ci = get_click_interval()\n                            prev, prev2 = self.recent_mouse_events[-1], self.recent_mouse_events[-2]\n                            if (\n                                prev.button == button and prev2.button == button and\n                                prev.action == GLFW_PRESS and prev2.action == GLFW_RELEASE and\n                                prev.tab_id == tab.id and prev2.tab_id == tab.id and\n                                now - prev.at <= ci and now - prev2.at <= 2 * ci\n                            ):  # double click on tab\n                                self.set_active_tab(tab)\n                                get_boss().set_tab_title()\n                                self.recent_mouse_events.clear()\n                                set_tab_being_dragged()\n                                return\n                        self.set_active_tab(tab)\n                        set_tab_being_dragged()\n            elif button == GLFW_MOUSE_BUTTON_MIDDLE:\n                if action == GLFW_RELEASE and self.recent_mouse_events:\n                    p = self.recent_mouse_events[-1]\n                    if p.button == button and p.action == GLFW_PRESS and p.tab_id == tab.id:\n                        get_boss().close_tab(tab)\n        self.recent_mouse_events.append(TabMouseEvent(button, modifiers, action, now, tab.id if tab else 0))\n        if len(self.recent_mouse_events) > 5:\n            self.recent_mouse_events.popleft()\n\n    def handle_window_title_bar_mouse(self, window_id: int, button: int, modifiers: int, action: int) -> None:\n        now = monotonic()\n        boss = get_boss()\n        if button == GLFW_MOUSE_BUTTON_LEFT:\n            if action == GLFW_PRESS:\n                if (w := boss.window_id_map.get(window_id)) is not None:\n                    get_boss().set_active_window(w, switch_os_window_if_needed=True)\n            elif action == GLFW_RELEASE and len(self.recent_title_bar_mouse_events) > 2:\n                ci = get_click_interval()\n                prev, prev2 = self.recent_title_bar_mouse_events[-1], self.recent_title_bar_mouse_events[-2]\n                if (\n                    prev.button == button and prev2.button == button and\n                    prev.action == GLFW_PRESS and prev2.action == GLFW_RELEASE and\n                    prev.tab_id == window_id and prev2.tab_id == window_id and\n                    now - prev.at <= ci and now - prev2.at <= 2 * ci\n                ):  # double click on window title bar\n                    if (w := boss.window_id_map.get(window_id)) is not None:\n                        w.set_window_title()\n                    self.recent_title_bar_mouse_events.clear()\n                    return\n        self.recent_title_bar_mouse_events.append(TabMouseEvent(button, modifiers, action, now, window_id))\n        if len(self.recent_title_bar_mouse_events) > 5:\n            self.recent_title_bar_mouse_events.popleft()\n\n    def update_progress(self) -> None:\n        self.num_of_windows_with_progress = 0\n        self.total_progress = 0\n        self.has_indeterminate_progress = False\n        for tab in self:\n            if tab.num_of_windows_with_progress:\n                self.total_progress += tab.total_progress\n                self.num_of_windows_with_progress += tab.num_of_windows_with_progress\n            if tab.has_indeterminate_progress:\n                self.has_indeterminate_progress = True\n        get_boss().update_progress_in_dock()\n\n    @property\n    def tab_bar_rects(self) -> tuple[Border, ...]:\n        return self.tab_bar.blank_rects if self.tab_bar_should_be_visible else ()\n\n    def destroy(self) -> None:\n        for t in self:\n            t.destroy()\n        self.tab_bar.destroy()\n        del self.tab_bar\n        del self.tabs\n\n    def apply_options(self) -> None:\n        at = self.active_tab\n        for tab in self:\n            tab.apply_options(at is tab)\n        self.tab_bar_hidden = get_options().tab_bar_style == 'hidden'\n        self.tab_bar.apply_options()\n        self.update_tab_bar_data()\n        self.layout_tab_bar()\n# }}}\n"
  },
  {
    "path": "kitty/terminfo.h",
    "content": "static const uint8_t terminfo_data[3721] = {\n26, 1, 21, 0, 28, 0, 15, 0, 105, 1, 189, 5, 120, 116, 101, 114, 109, 45, 107, 105, 116, 116, 121, 124, 75, 111, 118, 73, 100, 84, 84, 89, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 80, 0, 8, 0, 24, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 255, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 38, 0, 42, 0, 46, 0, 255, 255, 57, 0, 74, 0, 76, 0, 80, 0, 87, 0, 255, 255, 89, 0, 102, 0, 255, 255, 106, 0, 110, 0, 120, 0, 124, 0, 128, 0, 255, 255, 135, 0, 139, 0, 144, 0, 149, 0, 255, 255, 158, 0, 163, 0, 255, 255, 255, 255, 168, 0, 173, 0, 178, 0, 183, 0, 192, 0, 196, 0, 203, 0, 255, 255, 212, 0, 217, 0, 223, 0, 229, 0, 255, 255, 247, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 0, 255, 255, 253, 0, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 4, 1, 255, 255, 255, 255, 255, 255, 255, 255, 8, 1, 12, 1, 18, 1, 22, 1, 26, 1, 30, 1, 36, 1, 42, 1, 48, 1, 54, 1, 60, 1, 64, 1, 255, 255, 69, 1, 255, 255, 73, 1, 78, 1, 83, 1, 87, 1, 94, 1, 255, 255, 101, 1, 105, 1, 111, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 117, 1, 126, 1, 135, 1, 144, 1, 153, 1, 162, 1, 171, 1, 180, 1, 189, 1, 198, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 207, 1, 227, 1, 255, 255, 255, 255, 255, 255, 234, 1, 237, 1, 248, 1, 251, 1, 253, 1, 0, 2, 93, 2, 255, 255, 96, 2, 98, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 103, 2, 255, 255, 168, 2, 255, 255, 255, 255, 172, 2, 178, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 188, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 192, 2, 255, 255, 255, 255, 255, 255, 255, 255, 199, 2, 255, 255, 255, 255, 206, 2, 255, 255, 255, 255, 255, 255, 255, 255, 213, 2, 220, 2, 227, 2, 255, 255, 255, 255, 234, 2, 255, 255, 241, 2, 255, 255, 255, 255, 255, 255, 248, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 5, 3, 11, 3, 18, 3, 25, 3, 33, 3, 40, 3, 48, 3, 56, 3, 64, 3, 72, 3, 80, 3, 88, 3, 96, 3, 104, 3, 111, 3, 118, 3, 126, 3, 133, 3, 141, 3, 149, 3, 157, 3, 165, 3, 173, 3, 181, 3, 189, 3, 197, 3, 204, 3, 211, 3, 219, 3, 226, 3, 234, 3, 242, 3, 250, 3, 2, 4, 10, 4, 18, 4, 26, 4, 34, 4, 41, 4, 48, 4, 56, 4, 63, 4, 71, 4, 79, 4, 87, 4, 95, 4, 103, 4, 111, 4, 119, 4, 127, 4, 134, 4, 141, 4, 149, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 154, 4, 165, 4, 170, 4, 189, 4, 193, 4, 202, 4, 209, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 47, 5, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 52, 5, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 58, 5, 255, 255, 255, 255, 255, 255, 62, 5, 125, 5, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 49, 50, 104, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 63, 49, 50, 59, 50, 53, 104, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 93, 50, 59, 27, 92, 0, 27, 40, 48, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 49, 48, 52, 57, 104, 0, 27, 91, 50, 109, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 27, 40, 66, 0, 27, 40, 66, 27, 91, 109, 0, 27, 91, 63, 49, 48, 52, 57, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 7, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 79, 72, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 49, 59, 50, 66, 0, 27, 91, 49, 59, 50, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 0, 27, 91, 63, 49, 104, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 37, 112, 49, 37, 99, 27, 91, 37, 112, 50, 37, 123, 49, 125, 37, 45, 37, 100, 98, 0, 27, 93, 27, 92, 27, 99, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 37, 63, 37, 112, 57, 37, 116, 27, 40, 48, 37, 101, 27, 40, 66, 37, 59, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 37, 63, 37, 112, 53, 37, 116, 59, 50, 37, 59, 109, 0, 27, 72, 0, 9, 0, 27, 93, 50, 59, 0, 43, 43, 44, 44, 45, 45, 46, 46, 48, 48, 96, 96, 97, 97, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 79, 69, 0, 27, 79, 70, 0, 27, 91, 49, 59, 50, 69, 0, 27, 91, 51, 59, 50, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 50, 72, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 68, 0, 27, 91, 54, 59, 50, 126, 0, 27, 91, 53, 59, 50, 126, 0, 27, 91, 49, 59, 50, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 59, 50, 80, 0, 27, 91, 49, 59, 50, 81, 0, 27, 91, 49, 51, 59, 50, 126, 0, 27, 91, 49, 59, 50, 83, 0, 27, 91, 49, 53, 59, 50, 126, 0, 27, 91, 49, 55, 59, 50, 126, 0, 27, 91, 49, 56, 59, 50, 126, 0, 27, 91, 49, 57, 59, 50, 126, 0, 27, 91, 50, 48, 59, 50, 126, 0, 27, 91, 50, 49, 59, 50, 126, 0, 27, 91, 50, 51, 59, 50, 126, 0, 27, 91, 50, 52, 59, 50, 126, 0, 27, 91, 49, 59, 53, 80, 0, 27, 91, 49, 59, 53, 81, 0, 27, 91, 49, 51, 59, 53, 126, 0, 27, 91, 49, 59, 53, 83, 0, 27, 91, 49, 53, 59, 53, 126, 0, 27, 91, 49, 55, 59, 53, 126, 0, 27, 91, 49, 56, 59, 53, 126, 0, 27, 91, 49, 57, 59, 53, 126, 0, 27, 91, 50, 48, 59, 53, 126, 0, 27, 91, 50, 49, 59, 53, 126, 0, 27, 91, 50, 51, 59, 53, 126, 0, 27, 91, 50, 52, 59, 53, 126, 0, 27, 91, 49, 59, 54, 80, 0, 27, 91, 49, 59, 54, 81, 0, 27, 91, 49, 51, 59, 54, 126, 0, 27, 91, 49, 59, 54, 83, 0, 27, 91, 49, 53, 59, 54, 126, 0, 27, 91, 49, 55, 59, 54, 126, 0, 27, 91, 49, 56, 59, 54, 126, 0, 27, 91, 49, 57, 59, 54, 126, 0, 27, 91, 50, 48, 59, 54, 126, 0, 27, 91, 50, 49, 59, 54, 126, 0, 27, 91, 50, 51, 59, 54, 126, 0, 27, 91, 50, 52, 59, 54, 126, 0, 27, 91, 49, 59, 51, 80, 0, 27, 91, 49, 59, 51, 81, 0, 27, 91, 49, 51, 59, 51, 126, 0, 27, 91, 49, 59, 51, 83, 0, 27, 91, 49, 53, 59, 51, 126, 0, 27, 91, 49, 55, 59, 51, 126, 0, 27, 91, 49, 56, 59, 51, 126, 0, 27, 91, 49, 57, 59, 51, 126, 0, 27, 91, 50, 48, 59, 51, 126, 0, 27, 91, 50, 49, 59, 51, 126, 0, 27, 91, 50, 51, 59, 51, 126, 0, 27, 91, 50, 52, 59, 51, 126, 0, 27, 91, 49, 59, 52, 80, 0, 27, 91, 49, 59, 52, 81, 0, 27, 91, 49, 51, 59, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 37, 91, 59, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 93, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 49, 48, 52, 7, 0, 27, 93, 52, 59, 37, 112, 49, 37, 100, 59, 114, 103, 98, 58, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 27, 92, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 0, 4, 0, 0, 0, 79, 0, 162, 0, 75, 4, 1, 1, 1, 1, 0, 0, 9, 0, 18, 0, 25, 0, 37, 0, 56, 0, 63, 0, 70, 0, 75, 0, 81, 0, 143, 0, 154, 0, 164, 0, 176, 0, 182, 0, 191, 0, 200, 0, 207, 0, 214, 0, 221, 0, 228, 0, 235, 0, 242, 0, 249, 0, 0, 1, 7, 1, 14, 1, 21, 1, 28, 1, 35, 1, 42, 1, 49, 1, 56, 1, 63, 1, 70, 1, 77, 1, 84, 1, 91, 1, 98, 1, 105, 1, 112, 1, 119, 1, 126, 1, 133, 1, 140, 1, 147, 1, 154, 1, 161, 1, 168, 1, 175, 1, 182, 1, 189, 1, 196, 1, 203, 1, 210, 1, 217, 1, 224, 1, 231, 1, 238, 1, 245, 1, 252, 1, 3, 2, 10, 2, 17, 2, 24, 2, 31, 2, 38, 2, 45, 2, 52, 2, 59, 2, 66, 2, 73, 2, 80, 2, 87, 2, 91, 2, 95, 2, 101, 2, 127, 2, 153, 2, 0, 0, 3, 0, 6, 0, 9, 0, 17, 0, 20, 0, 23, 0, 26, 0, 29, 0, 32, 0, 35, 0, 38, 0, 41, 0, 44, 0, 51, 0, 57, 0, 60, 0, 65, 0, 68, 0, 71, 0, 74, 0, 80, 0, 86, 0, 92, 0, 98, 0, 104, 0, 109, 0, 114, 0, 119, 0, 124, 0, 129, 0, 133, 0, 138, 0, 143, 0, 148, 0, 153, 0, 158, 0, 164, 0, 170, 0, 176, 0, 182, 0, 188, 0, 194, 0, 200, 0, 206, 0, 212, 0, 218, 0, 223, 0, 228, 0, 233, 0, 238, 0, 243, 0, 249, 0, 255, 0, 5, 1, 11, 1, 17, 1, 23, 1, 29, 1, 35, 1, 41, 1, 47, 1, 53, 1, 59, 1, 65, 1, 71, 1, 77, 1, 83, 1, 89, 1, 95, 1, 101, 1, 107, 1, 111, 1, 116, 1, 121, 1, 126, 1, 131, 1, 136, 1, 141, 1, 147, 1, 152, 1, 160, 1, 168, 1, 27, 91, 63, 50, 48, 48, 52, 108, 0, 27, 91, 63, 50, 48, 48, 52, 104, 0, 27, 93, 49, 49, 50, 7, 0, 27, 93, 49, 50, 59, 37, 112, 49, 37, 115, 7, 0, 27, 93, 53, 50, 59, 37, 112, 49, 37, 115, 59, 37, 112, 50, 37, 115, 27, 92, 0, 27, 91, 50, 48, 49, 126, 0, 27, 91, 50, 48, 48, 126, 0, 27, 91, 62, 99, 0, 27, 91, 50, 32, 113, 0, 27, 91, 53, 56, 58, 50, 58, 37, 112, 49, 37, 123, 54, 53, 53, 51, 54, 125, 37, 47, 37, 100, 58, 37, 112, 49, 37, 123, 50, 53, 54, 125, 37, 47, 37, 123, 50, 53, 53, 125, 37, 38, 37, 100, 58, 37, 112, 49, 37, 123, 50, 53, 53, 125, 37, 38, 37, 100, 37, 59, 109, 0, 27, 91, 52, 58, 37, 112, 49, 37, 100, 109, 0, 27, 91, 37, 112, 49, 37, 100, 32, 113, 0, 27, 80, 61, 37, 112, 49, 37, 100, 115, 27, 92, 0, 27, 91, 62, 48, 113, 0, 27, 91, 63, 49, 48, 48, 52, 108, 0, 27, 91, 63, 49, 48, 48, 52, 104, 0, 27, 91, 49, 59, 51, 69, 0, 27, 91, 49, 59, 52, 69, 0, 27, 91, 49, 59, 53, 69, 0, 27, 91, 49, 59, 54, 69, 0, 27, 91, 49, 59, 55, 69, 0, 27, 91, 51, 59, 51, 126, 0, 27, 91, 51, 59, 52, 126, 0, 27, 91, 51, 59, 53, 126, 0, 27, 91, 51, 59, 54, 126, 0, 27, 91, 51, 59, 55, 126, 0, 27, 91, 49, 59, 50, 66, 0, 27, 91, 49, 59, 51, 66, 0, 27, 91, 49, 59, 52, 66, 0, 27, 91, 49, 59, 53, 66, 0, 27, 91, 49, 59, 54, 66, 0, 27, 91, 49, 59, 55, 66, 0, 27, 91, 49, 59, 51, 70, 0, 27, 91, 49, 59, 52, 70, 0, 27, 91, 49, 59, 53, 70, 0, 27, 91, 49, 59, 54, 70, 0, 27, 91, 49, 59, 55, 70, 0, 27, 91, 49, 59, 51, 72, 0, 27, 91, 49, 59, 52, 72, 0, 27, 91, 49, 59, 53, 72, 0, 27, 91, 49, 59, 54, 72, 0, 27, 91, 49, 59, 55, 72, 0, 27, 91, 50, 59, 51, 126, 0, 27, 91, 50, 59, 52, 126, 0, 27, 91, 50, 59, 53, 126, 0, 27, 91, 50, 59, 54, 126, 0, 27, 91, 50, 59, 55, 126, 0, 27, 91, 49, 59, 51, 68, 0, 27, 91, 49, 59, 52, 68, 0, 27, 91, 49, 59, 53, 68, 0, 27, 91, 49, 59, 54, 68, 0, 27, 91, 49, 59, 55, 68, 0, 27, 91, 54, 59, 51, 126, 0, 27, 91, 54, 59, 52, 126, 0, 27, 91, 54, 59, 53, 126, 0, 27, 91, 54, 59, 54, 126, 0, 27, 91, 54, 59, 55, 126, 0, 27, 91, 53, 59, 51, 126, 0, 27, 91, 53, 59, 52, 126, 0, 27, 91, 53, 59, 53, 126, 0, 27, 91, 53, 59, 54, 126, 0, 27, 91, 53, 59, 55, 126, 0, 27, 91, 49, 59, 51, 67, 0, 27, 91, 49, 59, 52, 67, 0, 27, 91, 49, 59, 53, 67, 0, 27, 91, 49, 59, 54, 67, 0, 27, 91, 49, 59, 55, 67, 0, 27, 91, 49, 59, 50, 65, 0, 27, 91, 49, 59, 51, 65, 0, 27, 91, 49, 59, 52, 65, 0, 27, 91, 49, 59, 53, 65, 0, 27, 91, 49, 59, 54, 65, 0, 27, 91, 49, 59, 55, 65, 0, 27, 91, 73, 0, 27, 91, 79, 0, 27, 91, 50, 57, 109, 0, 27, 91, 52, 56, 58, 50, 58, 37, 112, 49, 37, 100, 58, 37, 112, 50, 37, 100, 58, 37, 112, 51, 37, 100, 109, 0, 27, 91, 51, 56, 58, 50, 58, 37, 112, 49, 37, 100, 58, 37, 112, 50, 37, 100, 58, 37, 112, 51, 37, 100, 109, 0, 27, 91, 57, 109, 0, 83, 117, 0, 84, 99, 0, 88, 70, 0, 102, 117, 108, 108, 107, 98, 100, 0, 66, 68, 0, 66, 69, 0, 67, 114, 0, 67, 115, 0, 77, 115, 0, 80, 69, 0, 80, 83, 0, 82, 86, 0, 83, 101, 0, 83, 101, 116, 117, 108, 99, 0, 83, 109, 117, 108, 120, 0, 83, 115, 0, 83, 121, 110, 99, 0, 88, 82, 0, 102, 100, 0, 102, 101, 0, 107, 66, 69, 71, 51, 0, 107, 66, 69, 71, 52, 0, 107, 66, 69, 71, 53, 0, 107, 66, 69, 71, 54, 0, 107, 66, 69, 71, 55, 0, 107, 68, 67, 51, 0, 107, 68, 67, 52, 0, 107, 68, 67, 53, 0, 107, 68, 67, 54, 0, 107, 68, 67, 55, 0, 107, 68, 78, 0, 107, 68, 78, 51, 0, 107, 68, 78, 52, 0, 107, 68, 78, 53, 0, 107, 68, 78, 54, 0, 107, 68, 78, 55, 0, 107, 69, 78, 68, 51, 0, 107, 69, 78, 68, 52, 0, 107, 69, 78, 68, 53, 0, 107, 69, 78, 68, 54, 0, 107, 69, 78, 68, 55, 0, 107, 72, 79, 77, 51, 0, 107, 72, 79, 77, 52, 0, 107, 72, 79, 77, 53, 0, 107, 72, 79, 77, 54, 0, 107, 72, 79, 77, 55, 0, 107, 73, 67, 51, 0, 107, 73, 67, 52, 0, 107, 73, 67, 53, 0, 107, 73, 67, 54, 0, 107, 73, 67, 55, 0, 107, 76, 70, 84, 51, 0, 107, 76, 70, 84, 52, 0, 107, 76, 70, 84, 53, 0, 107, 76, 70, 84, 54, 0, 107, 76, 70, 84, 55, 0, 107, 78, 88, 84, 51, 0, 107, 78, 88, 84, 52, 0, 107, 78, 88, 84, 53, 0, 107, 78, 88, 84, 54, 0, 107, 78, 88, 84, 55, 0, 107, 80, 82, 86, 51, 0, 107, 80, 82, 86, 52, 0, 107, 80, 82, 86, 53, 0, 107, 80, 82, 86, 54, 0, 107, 80, 82, 86, 55, 0, 107, 82, 73, 84, 51, 0, 107, 82, 73, 84, 52, 0, 107, 82, 73, 84, 53, 0, 107, 82, 73, 84, 54, 0, 107, 82, 73, 84, 55, 0, 107, 85, 80, 0, 107, 85, 80, 51, 0, 107, 85, 80, 52, 0, 107, 85, 80, 53, 0, 107, 85, 80, 54, 0, 107, 85, 80, 55, 0, 107, 120, 73, 78, 0, 107, 120, 79, 85, 84, 0, 114, 109, 120, 120, 0, 115, 101, 116, 114, 103, 98, 98, 0, 115, 101, 116, 114, 103, 98, 102, 0, 115, 109, 120, 120, 0, };\n"
  },
  {
    "path": "kitty/terminfo.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nfrom binascii import hexlify, unhexlify\nfrom collections.abc import Generator\nfrom typing import Literal, cast\n\nfrom kitty.options.types import Options\n\n\ndef modify_key_bytes(keybytes: bytes, amt: int) -> bytes:\n    if amt == 0:\n        return keybytes\n    ans = bytearray(keybytes)\n    samt = str(amt).encode('ascii')\n    if ans[-1] == ord('~'):\n        return bytes(ans[:-1] + bytearray(b';' + samt + b'~'))\n    if ans[1] == ord('O'):\n        return bytes(ans[:1] + bytearray(b'[1;' + samt) + ans[-1:])\n    raise ValueError(f'Unknown key type in key: {keybytes!r}')\n\n\ndef encode_keystring(keybytes: bytes) -> str:\n    return keybytes.decode('ascii').replace('\\033', r'\\E')\n\n\nnames = Options.term, 'KovIdTTY'\n\ntermcap_aliases = {\n    'TN': 'name'\n}\n\nbool_capabilities = {\n    # auto_right_margin (terminal has automatic margins)\n    'am',\n    # auto_left_margin (cursor wraps on CUB1 from 0 to last column on prev line). This prevents ncurses\n    # from using BS (backspace) to position the cursor. See https://github.com/kovidgoyal/kitty/issues/8841\n    # It also allows using backspace with multi-line edits in cooked mode. Foot\n    # is the only other modern terminal I know of that implements this.\n    'bw',\n    # can_change (terminal can redefine existing colors)\n    'ccc',\n    # has_meta key (i.e. sets the eight bit)\n    'km',\n    # prtr_silent (printer will not echo on screen)\n    'mc5i',\n    # move_insert_mode (safe to move while in insert mode)\n    'mir',\n    # move_standout_mode (safe to move while in standout mode)\n    'msgr',\n    # no_pad_char (pad character does not exist)\n    'npc',\n    # eat_newline_glitch (newline ignored after 80 columns)\n    'xenl',\n    # has extra status line (window title)\n    'hs',\n    # Terminfo extension used by tmux to detect true color support (non-standard)\n    'Tc',\n    # Indicates support for styled and colored underlines (non-standard) as\n    # described at: https://sw.kovidgoyal.net/kitty/underlines/\n    'Su',\n    # Indicates support for full keyboard mode (non-standard) as\n    # described at:\n    # https://github.com/kovidgoyal/kitty/blob/master/protocol-extensions.asciidoc\n    'fullkbd',\n    # Terminal supports focus events: https://lists.gnu.org/archive/html/bug-ncurses/2023-10/msg00117.html\n    'XF',\n\n    # The following are entries that we don't use\n    # # background color erase\n    # 'bce',\n}\n\ntermcap_aliases.update({\n    'am': 'am',\n    'bw': 'bw',\n    'cc': 'ccc',\n    'km': 'km',\n    '5i': 'mc5i',\n    'mi': 'mir',\n    'ms': 'msgr',\n    'NP': 'npc',\n    'xn': 'xenl',\n    'hs': 'hs',\n})\n\nnumeric_capabilities = {\n    # maximum number of colors on screen\n    'colors': 256,\n    'cols': 80,\n    'lines': 24,\n    # tabs initially every # spaces\n    'it': 8,\n    # maximum number of color-pairs on the screen\n    'pairs': 32767,\n}\n\ntermcap_aliases.update({\n    'Co': 'colors',\n    'pa': 'pairs',\n    'li': 'lines',\n    'co': 'cols',\n    'it': 'it',\n})\n\nstring_capabilities = {\n    # graphics charset pairs\n    'acsc': r'++\\,\\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~',\n    # The audible bell character\n    'bel': r'^G',\n    # Escape code for bold\n    'bold': r'\\E[1m',\n    'blink': r'\\E[5m',\n    # Back tab\n    'cbt': r'\\E[Z',\n    'kcbt': r'\\E[Z',\n    # Make cursor invisible\n    'civis': r'\\E[?25l',\n    # Clear screen\n    'clear': r'\\E[H\\E[2J',\n    # Clear scrollback. This is disabled because the clear program on Linux by default, not as\n    # an option, uses it and nukes the scrollback. What's more this behavior was silently changed\n    # around 2013. Given clear is maintained as part of ncurses this kind of crap is no surprise.\n    # 'E3': r'\\E[3J',\n    # Make cursor appear normal\n    'cnorm': r'\\E[?12h\\E[?25h',\n    # Carriage return\n    'cr': r'^M',  # CR (carriage return \\r)\n    # Change scroll region\n    'csr': r'\\E[%i%p1%d;%p2%dr',\n    # Move cursor to the left by the specified amount\n    'cub': r'\\E[%p1%dD',\n    'cub1': r'^H',  # BS (backspace)\n    # Move cursor down specified number of lines\n    'cud': r'\\E[%p1%dB',\n    'cud1': r'^J',  # LF (line-feed \\n)\n    # Move cursor to the right by the specified amount\n    'cuf': r'\\E[%p1%dC',\n    'cuf1': r'\\E[C',\n    # Move cursor up specified number of lines\n    'cuu': r'\\E[%p1%dA',\n    'cuu1': r'\\E[A',\n    # Move cursor to specified location\n    'cup': r'\\E[%i%p1%d;%p2%dH',\n    # Make cursor very visible\n    'cvvis': r'\\E[?12;25h',\n    # Delete the specified number of characters\n    'dch': r'\\E[%p1%dP',\n    'dch1': r'\\E[P',\n    # Turn on half bright mode\n    'dim': r'\\E[2m',\n    # Delete the specified number of lines\n    'dl': r'\\E[%p1%dM',\n    'dl1': r'\\E[M',\n    # Erase specified number of characters\n    'ech': r'\\E[%p1%dX',\n    # Clear to end of screen\n    'ed': r'\\E[J',\n    # Clear to end of line\n    'el': r'\\E[K',\n    # Clear to start of line\n    'el1': r'\\E[1K',\n    # visible bell\n    'flash': r'\\E[?5h$<100/>\\E[?5l',\n    # Home cursor\n    'home': r'\\E[H',\n    # Move cursor to column\n    'hpa': r'\\E[%i%p1%dG',\n    # Move to next tab\n    'ht': r'^I',\n    # Set tabstop at current position\n    'hts': r'\\EH',\n    # Insert specified number of characters\n    'ich': r'\\E[%p1%d@',\n    # Insert specified number of lines\n    'il': r'\\E[%p1%dL',\n    'il1': r'\\E[L',\n    # scroll up by specified amount\n    'ind': r'^J',\n    'indn': r'\\E[%p1%dS',\n    # initialize color (set dynamic colors)\n    'initc': r'\\E]4;%p1%d;rgb\\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\\E\\\\',\n    # Set all colors to original values\n    'oc': r'\\E]104\\007',\n    # turn on blank mode (characters invisible)\n    # 'invis': r'\\E[8m',\n    # Backspace\n    'kbs': r'\\177',\n    # Mouse event has occurred\n    'kmous': r'\\E[M',\n\n    # These break mouse events in htop so they are disabled\n    # Turn on mouse reporting\n    # 'XM': '\\E[?1006;1004;1000%?%p1%{1}%=%th%el%;',\n    # Expected format for mouse reporting escape codes\n    # 'xm': r'\\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;',\n    # Scroll backwards (reverse index)\n\n    'kri': r'\\E[1;2A',\n    # scroll forwards (index)\n    'kind': r'\\E[1;2B',\n    # Restore cursor\n    'rc': r'\\E8',\n    # Repeat preceding character\n    'rep': r'%p1%c\\E[%p2%{1}%-%db',\n    # Reverse video\n    'rev': r'\\E[7m',\n    # Scroll backwards the specified number of lines (reverse index)\n    'ri': r'\\EM',\n    'rin': r'\\E[%p1%dT',\n    # Turn off automatic margins\n    'rmam': r'\\E[?7l',\n    # Exit alternate screen\n    'rmcup': r'\\E[?1049l',\n    # Exit insert mode\n    'rmir': r'\\E[4l',\n    # Exit application keypad mode\n    'rmkx': r'\\E[?1l',\n    # Exit standout mode\n    'rmso': r'\\E[27m',\n    # Exit underline mode\n    'rmul': r'\\E[24m',\n    # Exit strikethrough mode\n    'rmxx': r'\\E[29m',\n    # Reset string1 (empty OSC sequence to exit OSC/OTH modes, and regular reset)\n    'rs1': r'\\E]\\E\\\\\\Ec',\n    # Save cursor\n    'sc': r'\\E7',\n    # Set background color\n    'setab': r'\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m',\n    # Set foreground color\n    'setaf': r'\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m',\n    # Set attributes\n    'sgr': r'%?%p9%t\\E(0%e\\E(B%;\\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;%?%p5%t;2%;m',\n    # Clear all attributes\n    'sgr0': r'\\E(B\\E[m',\n    # Reset color pair to its original value\n    'op': r'\\E[39;49m',\n    # Turn on automatic margins\n    'smam': r'\\E[?7h',\n    # Start alternate screen\n    'smcup': r'\\E[?1049h',\n    # Enter insert mode\n    'smir': r'\\E[4h',\n    # Enter application keymap mode\n    'smkx': r'\\E[?1h',\n    # Enter standout mode\n    'smso': r'\\E[7m',\n    # Enter underline mode\n    'smul': r'\\E[4m',\n    'Smulx': r'\\E[4:%p1%dm',  # this is a non-standard extension that some terminals use, so match them\n    # Enter strikethrough mode\n    'smxx': r'\\E[9m',\n    'Sync': r'\\EP=%p1%ds\\E\\\\',  # this is a non-standard extension supported by tmux for synchronized updates\n    # Clear all tab stops\n    'tbc': r'\\E[3g',\n    # To status line (used to set window titles)\n    'tsl': r'\\E]2;',\n    # From status line (end window title string)\n    'fsl': r'^G',\n    # Disable status line (clear window title)\n    'dsl': r'\\E]2;\\E\\\\',\n    # Move to specified line\n    'vpa': r'\\E[%i%p1%dd',\n    # Enter italics mode\n    'sitm': r'\\E[3m',\n    # Leave italics mode\n    'ritm': r'\\E[23m',\n    # Select alternate charset\n    'smacs': r'\\E(0',\n    'rmacs': r'\\E(B',\n    # Special keys\n    # 'khlp': r'',\n    # 'kund': r'',\n    # 'ka1': r'',\n    # 'ka3': r'',\n    # 'kc1': r'',\n    # 'kc3': r'',\n    # Set RGB foreground color (non-standard used by neovim)\n    'setrgbf': r'\\E[38:2:%p1%d:%p2%d:%p3%dm',\n    # Set RGB background color (non-standard used by neovim)\n    'setrgbb': r'\\E[48:2:%p1%d:%p2%d:%p3%dm',\n    # DECSCUSR Set cursor style\n    'Ss': r'\\E[%p1%d\\sq',\n    # DECSCUSR Reset cursor style to power-on default\n    'Se': r'\\E[2\\sq',\n    # Set cursor color\n    'Cs': r'\\E]12;%p1%s\\007',\n    # Reset cursor color\n    'Cr': r'\\E]112\\007',\n    # Indicates support for styled and colored underlines (non-standard) as\n    # described at: https://sw.kovidgoyal.net/kitty/underlines/\n    # 'Setulc' is equivalent to the 'Su' boolean capability. Until\n    # standardized, specify both for application compatibility.\n    'Setulc': r'\\E[58:2:%p1%{65536}%/%d:%p1%{256}%/%{255}%&%d:%p1%{255}%&%d%;m',\n\n    # The following entries are for compatibility with xterm,\n    # and shell scripts using e.g. `tput u7` to emit a CPR escape\n    # See https://invisible-island.net/ncurses/terminfo.src.html\n    # and INTERPRETATION OF USER CAPABILITIES\n    'u6': r'\\E[%i%d;%dR',\n    'u7': r'\\E[6n',\n    'u8': r'\\E[?%[;0123456789]c',\n    'u9': r'\\E[c',\n\n    # Bracketed paste, added to ncurses 6.4 in 2023\n    'PS': r'\\E[200~',\n    'PE': r'\\E[201~',\n    'BE': r'\\E[?2004h',\n    'BD': r'\\E[?2004l',\n\n    # XTVERSION\n    'XR': r'\\E[>0q',\n    # OSC 52 clipboard access\n    'Ms': r'\\E]52;%p1%s;%p2%s\\E\\\\',\n    # Send device attributes (report version)\n    'RV': r'\\E[>c',\n    # Focus In and Out events\n    'kxIN': r'\\E[I',\n    'kxOUT': r'\\E[O',\n    # Enable/disable focus reporting\n    # Add to ncurses in: https://lists.gnu.org/archive/html/bug-ncurses/2023-10/msg00117.html\n    'fe': r'\\E[?1004h',\n    'fd': r'\\E[?1004l',\n\n    # The following are entries that we don't use\n    # # turn on blank mode, (characters invisible)\n    # 'invis': r'\\E[8m',\n    # # init2 string\n    # 'is2': r'\\E[!p\\E[?3;4l\\E[4l\\E>',\n    # # Enter/send key\n    # 'kent': r'\\EOM',\n    # # reset2\n    # 'rs2': r'\\E[!p\\E[?3;4l\\E[4l\\E>',\n}\n\nstring_capabilities.update({\n    f'kf{n}':\n        encode_keystring(modify_key_bytes(b'\\033' + value, 0))\n    for n, value in zip(range(1, 13),\n                        b'OP OQ OR OS [15~ [17~ [18~ [19~ [20~ [21~ [23~ [24~'.split())\n})\n\nstring_capabilities.update({\n    f'kf{offset + n}':\n        encode_keystring(modify_key_bytes(b'\\033' + value, mod))\n    for offset, mod in {12: 2, 24: 5, 36: 6, 48: 3, 60: 4}.items()\n    for n, value in zip(range(1, 13),\n                        b'OP OQ [13~ OS [15~ [17~ [18~ [19~ [20~ [21~ [23~ [24~'.split())\n    if offset + n < 64\n})\n\nstring_capabilities.update({\n    name.format(unmod=unmod, key=key):\n        encode_keystring(modify_key_bytes(b'\\033' + value, mod))\n    for unmod, key, value in zip(\n        'cuu1 cud1 cuf1 cub1 beg end home ich1 dch1 pp  np'.split(),\n        'UP   DN   RIT  LFT  BEG END HOM  IC   DC   PRV NXT'.split(),\n        b'OA  OB   OC   OD   OE  OF  OH   [2~  [3~  [5~ [6~'.split())\n    for name, mod in {\n        'k{unmod}': 0, 'k{key}': 2, 'k{key}3': 3, 'k{key}4': 4,\n        'k{key}5': 5, 'k{key}6': 6, 'k{key}7': 7}.items()\n})\n\ntermcap_aliases.update({\n    'ac': 'acsc',\n    'bl': 'bel',\n    'md': 'bold',\n    'mb': 'blink',\n    'bt': 'cbt',\n    'kB': 'kcbt',\n    'cl': 'clear',\n    'vi': 'civis',\n    'vs': 'cvvis',\n    've': 'cnorm',\n    'cr': 'cr',\n    'cs': 'csr',\n    'LE': 'cub',\n    'le': 'cub1',\n    'DO': 'cud',\n    'do': 'cud1',\n    'UP': 'cuu',\n    'up': 'cuu1',\n    'nd': 'cuf1',\n    'RI': 'cuf',\n    'cm': 'cup',\n    'DC': 'dch',\n    'dc': 'dch1',\n    'mh': 'dim',\n    'DL': 'dl',\n    'dl': 'dl1',\n    'ec': 'ech',\n    'cd': 'ed',\n    'ce': 'el',\n    'cb': 'el1',\n    'vb': 'flash',\n    'ho': 'home',\n    'ch': 'hpa',\n    'ta': 'ht',\n    'st': 'hts',\n    'IC': 'ich',\n    'AL': 'il',\n    'al': 'il1',\n    'sf': 'ind',\n    'SF': 'indn',\n    'Ic': 'initc',\n    'oc': 'oc',\n    # 'mk': 'invis',\n    'kb': 'kbs',\n    'kl': 'kcub1',\n    'kd': 'kcud1',\n    'kr': 'kcuf1',\n    'ku': 'kcuu1',\n    'kh': 'khome',\n    '@7': 'kend',\n    'kI': 'kich1',\n    'kD': 'kdch1',\n    'Km': 'kmous',\n    'kN': 'knp',\n    'kP': 'kpp',\n    'kR': 'kri',\n    'kF': 'kind',\n    'rc': 'rc',\n    'rp': 'rep',\n    'mr': 'rev',\n    'sr': 'ri',\n    'SR': 'rin',\n    'RA': 'rmam',\n    'te': 'rmcup',\n    'ei': 'rmir',\n    'se': 'rmso',\n    'ue': 'rmul',\n    'Te': 'rmxx',\n    'r1': 'rs1',\n    'sc': 'sc',\n    'AB': 'setab',\n    'AF': 'setaf',\n    'sa': 'sgr',\n    'me': 'sgr0',\n    'op': 'op',\n    'SA': 'smam',\n    'ti': 'smcup',\n    'im': 'smir',\n    'so': 'smso',\n    'us': 'smul',\n    'Ts': 'smxx',\n    'ct': 'tbc',\n    'cv': 'vpa',\n    'ZH': 'sitm',\n    'ZR': 'ritm',\n    'as': 'smacs',\n    'ae': 'rmacs',\n    'ks': 'smkx',\n    'ke': 'rmkx',\n    '#2': 'kHOM',\n    '#3': 'kIC',\n    '#4': 'kLFT',\n    '*4': 'kDC',\n    '*7': 'kEND',\n    '%c': 'kNXT',\n    '%e': 'kPRV',\n    '%i': 'kRIT',\n    '%1': 'khlp',\n    '&8': 'kund',\n    'K1': 'ka1',\n    'K3': 'ka3',\n    'K4': 'kc1',\n    'K5': 'kc3',\n    'ts': 'tsl',\n    'fs': 'fsl',\n    'ds': 'dsl',\n\n    'u6': 'u6',\n    'u7': 'u7',\n    'u8': 'u8',\n    'u9': 'u9',\n\n    # 'ut': 'bce',\n    # 'ds': 'dsl',\n    # 'fs': 'fsl',\n    # 'mk': 'invis',\n    # 'is': 'is2',\n    # '@8': 'kent',\n    # 'r2': 'rs2',\n})\n\ntermcap_aliases.update({\n    tc: f'kf{n}'\n    for n, tc in enumerate(\n        'k1 k2 k3 k4 k5 k6 k7 k8 k9 k; F1 F2 F3 F4 F5 F6 F7 F8 F9 FA '\n        'FB FC FD FE FF FG FH FI FJ FK FL FM FN FO FP FQ FR FS FT FU '\n        'FV FW FX FY FZ Fa Fb Fc Fd Fe Ff Fg Fh Fi Fj Fk Fl Fm Fn Fo '\n        'Fp Fq Fr'.split(), 1)})\n\nqueryable_capabilities = cast(dict[str, str], numeric_capabilities.copy())\nqueryable_capabilities.update(string_capabilities)\nextra = (bool_capabilities | numeric_capabilities.keys() | string_capabilities.keys()) - set(termcap_aliases.values())\nno_termcap_for = frozenset(\n    'XR XM xm Ms RV kxIN kxOUT Cr Cs Se Ss Setulc Su Smulx Sync Tc PS PE BE BD setrgbf setrgbb fullkbd kUP kDN kbeg kBEG fe fd XF'.split() + [\n        f'k{key}{mod}'\n        for key in 'UP DN RIT LFT BEG END HOM IC DC PRV NXT'.split()\n        for mod in range(3, 8)])\nif extra - no_termcap_for:\n    raise Exception(f'Termcap aliases not complete, missing: {extra - no_termcap_for}')\ndel extra\n\n\ndef generate_terminfo() -> str:\n    # Use ./build-terminfo to update definition files\n    ans = ['|'.join(names)]\n    ans.extend(sorted(bool_capabilities))\n    ans.extend(f'{k}#{numeric_capabilities[k]}' for k in sorted(numeric_capabilities))\n    ans.extend(f'{k}={string_capabilities[k]}' for k in sorted(string_capabilities))\n    return ',\\n\\t'.join(ans) + ',\\n'\n\n\noctal_escape = re.compile(r'\\\\([0-7]{3})')\nescape_escape = re.compile(r'\\\\[eE]')\n\n\ndef key_as_bytes(name: str) -> bytes:\n    ans = string_capabilities[name]\n    ans = octal_escape.sub(lambda m: chr(int(m.group(1), 8)), ans)\n    ans = escape_escape.sub('\\033', ans)\n    return ans.encode('ascii')\n\n\ndef get_capabilities(query_string: str, opts: 'Options', window_id: int = 0, os_window_id: int = 0) -> Generator[str, None, None]:\n    from .fast_data_types import ERROR_PREFIX\n\n    def result(encoded_query_name: str, x: str | Literal[True] | None = None) -> str:\n        if x is None:\n            return f'0+r{encoded_query_name}'\n        if x is True:\n            return f'1+r{encoded_query_name}'\n        return f'1+r{encoded_query_name}={hexlify(str(x).encode(\"utf-8\")).decode(\"ascii\")}'\n\n    for encoded_query_name in query_string.split(';'):\n        name = qname = unhexlify(encoded_query_name).decode('utf-8')\n        if name in ('TN', 'name'):\n            yield result(encoded_query_name, names[0])\n        elif name.startswith('kitty-query-'):\n            from kittens.query_terminal.main import get_result\n            name = name[len('kitty-query-'):]\n            rval = get_result(name, window_id, os_window_id)\n            if rval is None:\n                from .utils import log_error\n                log_error('Unknown kitty terminfo query:', name)\n                yield result(encoded_query_name)\n            else:\n                yield result(encoded_query_name, rval)\n        elif name in ('query-os-name', 'query-os_name'):\n            # https://github.com/kovidgoyal/kitty/issues/9217\n            yield result(encoded_query_name, os.uname().sysname)\n        else:\n            if name in bool_capabilities:\n                yield result(encoded_query_name, True)\n                continue\n            try:\n                val = queryable_capabilities[name]\n            except KeyError:\n                try:\n                    qname = termcap_aliases[name]\n                    val = queryable_capabilities[qname]\n                except Exception:\n                    from .utils import log_error\n                    log_error(ERROR_PREFIX, 'Unknown terminfo property:', name)\n                    yield result(encoded_query_name)\n                    continue\n            if qname in string_capabilities and '%' not in val:\n                val = key_as_bytes(qname).decode('ascii')\n            yield result(encoded_query_name, val)\n"
  },
  {
    "path": "kitty/text-cache.c",
    "content": "/*\n * text-cache.c\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"data-types.h\"\ntypedef struct Chars {\n    const char_type *chars;\n    size_t count;\n} Chars;\nstatic_assert(sizeof(Chars) == sizeof(void*) + sizeof(size_t), \"reorder Chars\");\n\n#define NAME chars_map\n#define KEY_TY Chars\n#define VAL_TY char_type\nstatic uint64_t hash_chars(Chars k);\nstatic bool cmpr_chars(Chars a, Chars b);\n#define HASH_FN hash_chars\n#define CMPR_FN cmpr_chars\n#include \"kitty-verstable.h\"\n\n#define MA_NAME Chars\n#define MA_BLOCK_SIZE 16u\n#define MA_ARENA_NUM_BLOCKS 128u\n#include \"arena.h\"\n\ntypedef struct TextCache {\n    struct { Chars *items; size_t capacity; char_type count; } array;\n    chars_map map;\n    unsigned refcnt;\n    CharsMonotonicArena arena;\n} TextCache;\nstatic uint64_t hash_chars(Chars k) { return vt_hash_bytes(k.chars, sizeof(k.chars[0]) * k.count); }\nstatic bool cmpr_chars(Chars a, Chars b) { return a.count == b.count && memcmp(a.chars, b.chars, sizeof(a.chars[0]) * a.count) == 0; }\n\n#define TEXT_CACHE_IMPLEMENTATION\n#include \"text-cache.h\"\n\nTextCache*\ntc_alloc(void) {\n    TextCache *ans = calloc(1, sizeof(TextCache));\n    if (!ans) return NULL;\n    ans->array.capacity = 256;\n    ans->array.items = malloc(ans->array.capacity * sizeof(ans->array.items[0]));\n    if (!ans->array.items) { free(ans); ans = NULL; return ans; }\n    vt_init(&ans->map);\n    ans->refcnt = 1;\n    return ans;\n}\n\nstatic void\nfree_text_cache(TextCache *self) {\n    vt_cleanup(&self->map);\n    Chars_free_all(&self->arena);\n    free(self->array.items);\n    free(self);\n}\n\nTextCache*\ntc_incref(TextCache *self) { if (self) { self->refcnt++; } return self; }\n\nTextCache*\ntc_decref(TextCache *self) {\n    if (self) {\n        if (self->refcnt < 2) free_text_cache(self);\n        else self->refcnt--;\n    }\n    return NULL;\n}\n\nchar_type\ntc_first_char_at_index(const TextCache *self, char_type idx) {\n    if (self->array.count > idx) return self->array.items[idx].chars[0];\n    return 0;\n}\n\nchar_type\ntc_last_char_at_index(const TextCache *self, char_type idx) {\n    if (self->array.count > idx) return self->array.items[idx].chars[self->array.items[idx].count-1];\n    return 0;\n}\n\n\nvoid\ntc_chars_at_index(const TextCache *self, char_type idx, ListOfChars *ans) {\n    if (self->array.count > idx) {\n        ensure_space_for_chars(ans, self->array.items[idx].count);\n        ans->count = self->array.items[idx].count;\n        memcpy(ans->chars, self->array.items[idx].chars, sizeof(ans->chars[0]) * ans->count);\n    } else {\n        ans->count = 0;\n    }\n}\n\nbool\ntc_chars_at_index_without_alloc(const TextCache *self, char_type idx, ListOfChars *ans) {\n    if (self->array.count > idx) {\n        ans->count = self->array.items[idx].count;\n        if (ans->capacity < ans->count) return false;\n        memcpy(ans->chars, self->array.items[idx].chars, sizeof(ans->chars[0]) * ans->count);\n    } else {\n        ans->count = 0;\n    }\n    return true;\n}\n\n\nunsigned\ntc_num_codepoints(const TextCache *self, char_type idx) {\n     return self->array.count > idx ? self->array.items[idx].count : 0;\n}\n\nunsigned\ntc_chars_at_index_ansi(const TextCache *self, char_type idx, ANSIBuf *output) {\n    unsigned count = 0;\n    if (self->array.count > idx) {\n        count = self->array.items[idx].count;\n        // we ensure space for one extra byte for ANSI escape code trailer if multicell\n        ensure_space_for(output, buf, output->buf[0], output->len + count + 1, capacity, 2048, false);\n        memcpy(output->buf + output->len, self->array.items[idx].chars, sizeof(output->buf[0]) * count);\n        output->len += count;\n    }\n    return count;\n}\n\nstatic char_type\ncopy_and_insert(TextCache *self, const Chars key) {\n    if (self->array.count > MAX_CHAR_TYPE_VALUE) fatal(\"Too many items in TextCache\");\n    ensure_space_for(&(self->array), items, Chars, self->array.count + 1, capacity, 256, false);\n    char_type *copy = Chars_get(&self->arena, key.count * sizeof(key.chars[0]));\n    if (!copy) fatal(\"Out of memory\");\n    memcpy(copy, key.chars, key.count * sizeof(key.chars[0]));\n    char_type ans = self->array.count;\n    Chars *k = self->array.items + self->array.count++;\n    k->count = key.count; k->chars = copy;\n    chars_map_itr i = vt_insert(&self->map, *k, ans);\n    if (vt_is_end(i)) fatal(\"Out of memory\");\n    return ans;\n}\n\nchar_type\ntc_get_or_insert_chars(TextCache *self, const ListOfChars *chars) {\n    Chars key = {.count=chars->count, .chars=chars->chars};\n    chars_map_itr i = vt_get(&self->map, key);\n    if (vt_is_end(i)) return copy_and_insert(self, key);\n    return i.data->val;\n}\n"
  },
  {
    "path": "kitty/text-cache.h",
    "content": "/*\n * text-cache.h\n * Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\ntypedef struct ListOfChars {\n    char_type *chars;\n    size_t count, capacity;\n} ListOfChars;\n\n#define LIST_OF_CHARS_STACK_SIZE 4\nstatic inline void cleanup_list_of_chars(ListOfChars *lc) { if (lc->capacity > LIST_OF_CHARS_STACK_SIZE) free(lc->chars); }\n#define RAII_ListOfChars(name) char_type name##lcbuf[LIST_OF_CHARS_STACK_SIZE]; __attribute__((cleanup(cleanup_list_of_chars))) ListOfChars name = {.chars=name##lcbuf, .capacity = LIST_OF_CHARS_STACK_SIZE};\nstatic inline ListOfChars* alloc_list_of_chars(void) {\n    ListOfChars *ans = calloc(1, sizeof(ListOfChars));\n    if (ans) {\n        ans->capacity = LIST_OF_CHARS_STACK_SIZE * 2;\n        ans->chars = malloc(ans->capacity * sizeof(ans->chars[0]));\n        if (!ans->chars) { free(ans); ans = NULL; }\n    }\n    return ans;\n}\n\nstatic inline void\nensure_space_for_chars(ListOfChars *lc, size_t count) {\n    if (lc->capacity >= count) return;\n    if (lc->capacity > LIST_OF_CHARS_STACK_SIZE) {\n        ensure_space_for(lc, chars, char_type, count, capacity, count, false);\n    } else {\n        lc->capacity = count + LIST_OF_CHARS_STACK_SIZE;\n        void *chars = malloc(lc->capacity * sizeof(lc->chars[0]));\n        if (!chars) fatal(\"Out of memory allocating LCChars char space\");\n        memcpy(chars, lc->chars, LIST_OF_CHARS_STACK_SIZE * sizeof(lc->chars[0]));\n        lc->chars = chars;\n    }\n}\n\n#ifndef TEXT_CACHE_IMPLEMENTATION\ntypedef struct {int x; } *TextCache;\n#endif\n\nTextCache* tc_alloc(void);\nTextCache* tc_incref(TextCache *self);\nTextCache* tc_decref(TextCache *self);\nvoid tc_chars_at_index(const TextCache *self, char_type idx, ListOfChars *ans);\nunsigned tc_chars_at_index_ansi(const TextCache *self, char_type idx, ANSIBuf *output);\nchar_type tc_get_or_insert_chars(TextCache *self, const ListOfChars *chars);\nchar_type tc_first_char_at_index(const TextCache *self, char_type idx);\nchar_type tc_last_char_at_index(const TextCache *self, char_type idx);\nbool tc_chars_at_index_without_alloc(const TextCache *self, char_type idx, ListOfChars *ans);\nunsigned tc_num_codepoints(const TextCache *self, char_type idx);\n"
  },
  {
    "path": "kitty/threading.h",
    "content": "/*\n * Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stdio.h>\n#include <pthread.h>\n#if defined(__FreeBSD__) || defined(__OpenBSD__)\n#define FREEBSD_SET_NAME\n#endif\n#if defined(__APPLE__)\n// I can't figure out how to get pthread.h to include this definition on macOS. MACOSX_DEPLOYMENT_TARGET does not work.\nextern int pthread_setname_np(const char *name);\n#elif defined(FREEBSD_SET_NAME)\n// Function has a different name on FreeBSD\nvoid pthread_set_name_np(pthread_t tid, const char *name);\n#else\n// Need _GNU_SOURCE for pthread_setname_np on linux and that causes other issues on systems with old glibc\nextern int pthread_setname_np(pthread_t, const char *name);\n#endif\n\nstatic inline void\nset_thread_name(const char *name) {\n    int ret;\n#if defined(__APPLE__)\n    ret = pthread_setname_np(name);\n#elif defined(FREEBSD_SET_NAME)\n    pthread_set_name_np(pthread_self(), name);\n    ret = 0;\n#else\n    ret = pthread_setname_np(pthread_self(), name);\n#endif\n    if (ret != 0) perror(\"Failed to set thread name\");\n}\n"
  },
  {
    "path": "kitty/tint_fragment.glsl",
    "content": "uniform vec4 tint_color;\nout vec4 color;  // must be in linear space and pre-multiplied\n\nvoid main() {\n    color = tint_color;\n}\n"
  },
  {
    "path": "kitty/tint_vertex.glsl",
    "content": "\nuniform vec4 edges;\n\nvoid main() {\n    float left = edges[0];\n    float top = edges[1];\n    float right = edges[2];\n    float bottom = edges[3];\n    vec2 pos_map[] = vec2[4](\n        vec2(left, top),\n        vec2(left, bottom),\n        vec2(right, bottom),\n        vec2(right, top)\n    );\n\n\n    gl_Position = vec4(pos_map[gl_VertexID], 0, 1);\n}\n"
  },
  {
    "path": "kitty/trail_fragment.glsl",
    "content": "uniform vec2 cursor_edge_x;\nuniform vec2 cursor_edge_y;\nuniform vec3 trail_color;\nuniform float trail_opacity;\n\nin vec2 frag_pos;\nout vec4 final_color;\n\nvoid main() {\n    float opacity = trail_opacity;\n    // Dont render if fragment is within cursor area\n    float in_x = step(cursor_edge_x[0], frag_pos.x) * step(frag_pos.x, cursor_edge_x[1]);\n    float in_y = step(cursor_edge_y[1], frag_pos.y) * step(frag_pos.y, cursor_edge_y[0]);\n    opacity *= 1.0f - in_x * in_y;\n    final_color = vec4(trail_color * opacity, opacity);\n}\n"
  },
  {
    "path": "kitty/trail_vertex.glsl",
    "content": "uniform vec4 x_coords;\nuniform vec4 y_coords;\n\nout vec2 frag_pos;\n\nvoid main() {\n    vec2 pos = vec2(x_coords[gl_VertexID], y_coords[gl_VertexID]);\n    gl_Position = vec4(pos, 1.0, 1.0);\n    frag_pos = pos;\n}\n"
  },
  {
    "path": "kitty/types.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom collections.abc import Callable, Iterator, Mapping, Sequence\nfrom enum import Enum\nfrom functools import update_wrapper\nfrom typing import TYPE_CHECKING, Any, Generic, NamedTuple, TypedDict, TypeVar, Union\n\nif TYPE_CHECKING:\n    from kitty.fast_data_types import SingleKey\n\n_T = TypeVar('_T')\n\n\nclass SingleInstanceData(TypedDict):\n    cmd: str\n    args: Sequence[str]\n    cmdline_args_for_open: Sequence[str]\n    cwd: str\n    session_data: str\n    session_arg: str\n    session_path: str\n    environ: Mapping[str, str]\n    notify_on_os_window_death: str | None\n\n\nclass OverlayType(Enum):\n    transient = 'transient'\n    main = 'main'\n\n\nclass ParsedShortcut(NamedTuple):\n    mods: int\n    key_name: str\n\n\nclass Edges(NamedTuple):\n    left: int = 0\n    top: int = 0\n    right: int = 0\n    bottom: int = 0\n\n\nclass FloatEdges(NamedTuple):\n    left: float = 0\n    top: float = 0\n    right: float = 0\n    bottom: float = 0\n\n\nclass WindowGeometry(NamedTuple):\n    left: int\n    top: int\n    right: int\n    bottom: int\n    xnum: int\n    ynum: int\n    spaces: Edges = Edges()\n\n\nclass SignalInfo(NamedTuple):\n    si_signo: int\n    si_code: int\n    si_pid: int\n    si_uid: int\n    si_addr: int\n    si_status: int\n    sival_int: int\n    sival_ptr: int\n\n\nclass LayerShellConfig(NamedTuple):\n    type: int = 0\n    edge: int = 0\n    focus_policy: int = 0\n    output_name: str = ''\n    x_size_in_pixels: int = 0\n    y_size_in_pixels: int = 0\n    x_size_in_cells: int = 0\n    y_size_in_cells: int = 0\n    requested_top_margin: int = 0\n    requested_left_margin: int = 0\n    requested_bottom_margin: int = 0\n    requested_right_margin: int = 0\n    requested_exclusive_zone: int = -1\n    override_exclusive_zone: bool = False\n    hide_on_focus_loss: bool = False\n\n\ndef mod_to_names(mods: int, has_kitty_mod: bool = False, kitty_mod: int = 0) -> Iterator[str]:\n    if has_kitty_mod:\n        mods &= ~kitty_mod\n        yield 'kitty_mod'\n    for name, val in modmap().items():\n        if mods & val:\n            yield name\n\n\ndef human_repr_of_single_key(self: 'SingleKey', kitty_mod: int) -> str:\n    from .fast_data_types import glfw_get_key_name\n    names = []\n    names = list(mod_to_names(self.mods, self.defined_with_kitty_mod, kitty_mod))\n    if self.key > 0:\n        kname = (glfw_get_key_name(0, self.key) if self.is_native else glfw_get_key_name(self.key, 0)) or f'{self.key}'\n        kname = {' ': 'space'}.get(kname, kname)\n        names.append(kname)\n    return '+'.join(names)\n\n\nclass Shortcut(NamedTuple):\n    keys: tuple['SingleKey', ...]\n\n    def human_repr(self, kitty_mod: int = 0) -> str:\n        return ' > '.join(human_repr_of_single_key(k, kitty_mod) for k in self.keys)\n\n\nclass MouseEvent(NamedTuple):\n    button: int = 0\n    mods: int = 0\n    repeat_count: int = 1\n    grabbed: bool = False\n\n    def human_repr(self, kitty_mod: int = 0) -> str:\n        from .options.utils import mouse_button_map, mouse_trigger_count_map\n\n        def mouse_button_num_to_name(num: int) -> str:\n            button_map = {v: k for k, v in mouse_button_map.items()}\n            name = f'b{num+1}'\n            return button_map.get(name, name)\n\n        def mouse_trigger_count_to_name(count: int) -> str:\n            trigger_count_map = {str(v): k for k, v in mouse_trigger_count_map.items()}\n            k = str(count)\n            return trigger_count_map.get(k, k)\n\n        names = list(mod_to_names(self.mods)) + [mouse_button_num_to_name(self.button)]\n        when = mouse_trigger_count_to_name(self.repeat_count)\n        grabbed = 'grabbed' if self.grabbed else 'ungrabbed'\n        return ' '.join(('+'.join(names), when, grabbed))\n\n\nclass WindowSystemMouseEvent(NamedTuple):\n    in_tab_bar: bool\n    window_id: int\n    action: int\n    modifiers: int\n    button: int\n    currently_pressed_button: int\n    x: float\n    y: float\n\n\nConvertibleToNumbers = Union[str, bytes, int, float]\n\n\nclass AsyncResponse:\n    pass\n\n\nif TYPE_CHECKING:\n    class RunOnce(Generic[_T]):\n\n        def __init__(self, func: Callable[[], _T]): ...\n        def __call__(self) -> _T: ...\n        def set_override(self, val: _T) -> None: ...\n        def clear_override(self) -> None: ...\n        def clear_cached(self) -> None: ...\nelse:\n    class RunOnce:\n\n        def __init__(self, f):\n            self._override = RunOnce\n            self._cached_result = RunOnce\n            update_wrapper(self, f)\n\n        def __call__(self):\n            if self._override is not RunOnce:\n                return self._override\n            if self._cached_result is RunOnce:\n                self._cached_result = self.__wrapped__()\n            return self._cached_result\n\n        def clear_cached(self):\n            self._cached_result = RunOnce\n\n        def set_override(self, val):\n            self._override = val\n\n        def clear_override(self):\n            self._override = RunOnce\n\n\ndef run_once(f: Callable[[], _T]) -> 'RunOnce[_T]':\n    return RunOnce(f)\n\n\n@run_once\ndef modmap() -> dict[str, int]:\n    from .constants import is_macos\n    from .fast_data_types import (\n        GLFW_MOD_ALT,\n        GLFW_MOD_CAPS_LOCK,\n        GLFW_MOD_CONTROL,\n        GLFW_MOD_HYPER,\n        GLFW_MOD_META,\n        GLFW_MOD_NUM_LOCK,\n        GLFW_MOD_SHIFT,\n        GLFW_MOD_SUPER,\n    )\n\n    return {'ctrl': GLFW_MOD_CONTROL, 'shift': GLFW_MOD_SHIFT, ('opt' if is_macos else 'alt'): GLFW_MOD_ALT,\n            ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER, 'hyper': GLFW_MOD_HYPER, 'meta': GLFW_MOD_META,\n            'caps_lock': GLFW_MOD_CAPS_LOCK, 'num_lock': GLFW_MOD_NUM_LOCK}\n\n\nif TYPE_CHECKING:\n    from typing import Literal\n    ActionGroup = Literal['cp', 'sc', 'win', 'tab', 'fs', 'mouse', 'mk', 'lay', 'misc', 'debug', 'session']\nelse:\n    ActionGroup = str\n\n\nclass ActionSpec(NamedTuple):\n    group: str\n    doc: str\n\n\ndef ac(group: ActionGroup, doc: str) -> Callable[[_T], _T]:\n    def w(f: _T) -> _T:\n        setattr(f, 'action_spec', ActionSpec(group, doc))\n        return f\n    return w\n\n\nWindowMapper = Callable[[int], int | None]\nDecoratedFunc = TypeVar('DecoratedFunc', bound=Callable[..., Any])\n\n\nclass NeighborsMap(TypedDict, total=False):\n    left: list[int]\n    top: list[int]\n    right: list[int]\n    bottom: list[int]\n\n\nclass WindowResizeDragData(NamedTuple):\n    horizontal_id: int | None = None\n    width_increases_rightwards: bool = True\n    vertical_id: int | None = None\n    height_increases_downwards: bool = True\n\n\nclass WindowResizeDrag(NamedTuple):\n    is_active: bool = False\n    cell_width: int = 0\n    cell_height: int = 0\n    initial_x: float = 0\n    initial_y: float = 0\n    last_step_x: float = 0\n    last_step_y: float = 0\n    tab_id: int = 0\n    data: WindowResizeDragData = WindowResizeDragData()\n\n    def __bool__(self) -> bool:\n        return self.is_active\n"
  },
  {
    "path": "kitty/typing_compat.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nBossType = ChildType = TabType = WindowType = ScreenType = object\nBadLineType = SequenceMap = KeyActionType = AliasMap = object\nAddressFamily = PopenType = Socket = StartupCtx = object\nSessionTab = SessionType = LayoutType = SpecialWindowInstance = object\nMarkType = RemoteCommandType = object\nKeyEventType = ImageManagerType = KittyCommonOpts = HandlerType = object\nGRT_t = GRT_a = GRT_d = GRT_f = GRT_m = GRT_o = GRT_C = object\nScreenSize = KittensKeyActionType = MouseEvent = MouseButton = AbstractEventLoop = object\nTermManagerType = LoopType = Debug = GraphicsCommandType = object\nReadableBuffer = WriteableBuffer = bytearray\n\nCompletedProcess = tuple\nTypedDict = dict\nEdgeLiteral = str\nUnderlineLiteral = str\nPowerlineStyle = str\nMatchType = str\nProtocol = object\nOptionsProtocol = object\nNotRequired = tuple\nCoreTextFont = FontConfigPattern = dict\n"
  },
  {
    "path": "kitty/typing_compat.pyi",
    "content": "import array\nimport mmap\nfrom asyncio import AbstractEventLoop as AbstractEventLoop\nfrom socket import AddressFamily as AddressFamily\nfrom socket import socket as Socket\nfrom subprocess import CompletedProcess as CompletedProcess\nfrom subprocess import Popen as PopenType\nfrom typing import Literal\nfrom typing import NotRequired as NotRequired\nfrom typing import Protocol as Protocol\nfrom typing import TypedDict as TypedDict\n\nfrom kittens.hints.main import Mark as MarkType\nfrom kittens.tui.handler import Handler as HandlerType\nfrom kittens.tui.images import GraphicsCommand as GraphicsCommandType\nfrom kittens.tui.images import ImageManager as ImageManagerType\nfrom kittens.tui.loop import Debug as Debug\nfrom kittens.tui.loop import Loop as LoopType\nfrom kittens.tui.loop import MouseButton as MouseButton\nfrom kittens.tui.loop import MouseEvent as MouseEvent\nfrom kittens.tui.loop import TermManager as TermManagerType\n\nfrom .boss import Boss as BossType\nfrom .child import Child as ChildType\nfrom .conf.utils import BadLine as BadLineType\nfrom .conf.utils import KeyAction as KeyActionType\nfrom .config import KittyCommonOpts\nfrom .fast_data_types import CoreTextFont as CoreTextFont\nfrom .fast_data_types import FontConfigPattern as FontConfigPattern\nfrom .fast_data_types import Screen as ScreenType\nfrom .fast_data_types import StartupCtx as StartupCtx\nfrom .key_encoding import KeyEvent as KeyEventType\nfrom .layout.base import Layout as LayoutType\nfrom .options.utils import AliasMap as AliasMap\nfrom .options.utils import KeyMap as KeyMap\nfrom .rc.base import RemoteCommand as RemoteCommandType\nfrom .session import Session as SessionType\nfrom .session import Tab as SessionTab\nfrom .tabs import SpecialWindowInstance as SpecialWindowInstance\nfrom .tabs import Tab as TabType\nfrom .utils import ScreenSize as ScreenSize\nfrom .window import Window as WindowType\n\nEdgeLiteral = Literal['left', 'top', 'right', 'bottom']\nUnderlineLiteral = Literal['straight', 'double', 'curly', 'dotted', 'dashed']\nMatchType = Literal['mime', 'ext', 'protocol', 'file', 'path', 'url', 'fragment_matches']\nPowerlineStyle = Literal['angled', 'slanted', 'round']\nGRT_a = Literal['t', 'T', 'q', 'p', 'd', 'f', 'a', 'c']\nGRT_f = Literal[24, 32, 100]\nGRT_t = Literal['d', 'f', 't', 's']\nGRT_o = Literal['z', 'z']  # two z's to workaround a bug in ruff\nGRT_m = Literal[0, 1]\nGRT_C = Literal[0, 1]\nGRT_d = Literal['a', 'A', 'c', 'C', 'i', 'I', 'p', 'P', 'q', 'Q', 'x', 'X', 'y', 'Y', 'z', 'Z', 'f', 'F']\nReadableBuffer = bytes | bytearray | memoryview | array.array[int] | mmap.mmap\nWriteableBuffer = bytearray | memoryview | array.array[int] | mmap.mmap\n\n\n\nclass WindowSystemMouseEvent(TypedDict):\n    button: int\n    count: int\n    mods: int\n\n\n__all__ = (\n    'EdgeLiteral', 'MatchType', 'GRT_a', 'GRT_f', 'GRT_t', 'GRT_o', 'GRT_m', 'GRT_d',\n    'GraphicsCommandType', 'HandlerType', 'AbstractEventLoop', 'AddressFamily', 'Socket', 'CompletedProcess',\n    'PopenType', 'Protocol', 'TypedDict', 'MarkType', 'ImageManagerType', 'Debug', 'LoopType', 'MouseEvent',\n    'TermManagerType', 'BossType', 'ChildType', 'BadLineType', 'MouseButton', 'NotRequired',\n    'KeyActionType', 'KeyMap', 'KittyCommonOpts', 'AliasMap', 'CoreTextFont', 'WindowSystemMouseEvent',\n    'FontConfigPattern', 'ScreenType', 'StartupCtx', 'KeyEventType', 'LayoutType', 'PowerlineStyle',\n    'RemoteCommandType', 'SessionType', 'SessionTab', 'SpecialWindowInstance', 'TabType', 'ScreenSize', 'WindowType',\n    'ReadableBuffer', 'WriteableBuffer',\n)\n"
  },
  {
    "path": "kitty/unicode-data.h",
    "content": "#pragma once\n#include \"data-types.h\"\n#include \"state.h\"\n#include \"char-props.h\"\n\n// Converts row/column diacritics to numbers.\nint diacritic_to_num(char_type ch);\n\nstatic inline bool\nis_excluded_from_url(uint32_t ch) {\n    if (OPT(url_excluded_characters)) {\n        for (const char_type *p = OPT(url_excluded_characters); *p; p++) {\n            if (ch == *p) return true;\n        }\n    }\n    return false;\n}\n\nstatic inline bool\nis_url_legal_char(uint32_t ch) {\n    START_ALLOW_CASE_RANGE\n    // See https://url.spec.whatwg.org/#url-code-points\n    if (ch < 0xa0) {\n        switch (ch) {\n            case '!': case '$': case '&': case '\\'': case '/': case ':': case ';': case '@': case '_': case '~':\n            case '(': case ')': case '*': case '+': case ',': case '-': case '.': case '=': case '?': case '%': case '#':\n            case 'a' ... 'z':\n            case 'A' ... 'Z':\n            case '0' ... '9':\n                return true;\n            default:\n                return false;\n        }\n    }\n    if (ch > 0x10fffd) return false;  // outside valid unicode range\n    if (0xd800 <= ch && ch <= 0xdfff) return false; // leading or trailing surrogate\n    // non-characters\n    switch (ch) {\n        case 0xfdd0 ... 0xfdef:\n        case 0xFFFE: case 0xFFFF: case 0x1FFFE: case 0x1FFFF: case 0x2FFFE: case 0x2FFFF:\n        case 0x3FFFE: case 0x3FFFF: case 0x4FFFE: case 0x4FFFF: case 0x5FFFE: case 0x5FFFF:\n        case 0x6FFFE: case 0x6FFFF: case 0x7FFFE: case 0x7FFFF: case 0x8FFFE: case 0x8FFFF:\n        case 0x9FFFE: case 0x9FFFF: case 0xAFFFE: case 0xAFFFF: case 0xBFFFE: case 0xBFFFF:\n        case 0xCFFFE: case 0xCFFFF: case 0xDFFFE: case 0xDFFFF: case 0xEFFFE: case 0xEFFFF:\n        case 0xFFFFE: case 0xFFFFF:\n            return false;\n        default:\n            return true;\n    }\n    END_ALLOW_CASE_RANGE\n}\n\nstatic inline bool\nis_url_char(uint32_t ch) {\n    return is_url_legal_char(ch) && !is_excluded_from_url(ch);\n}\n\nstatic inline bool\ncan_strip_from_end_of_url(uint32_t ch) {\n    // remove trailing punctuation\n    return (char_props_for(ch).is_punctuation &&\n        ch != '/' && ch != '&' && ch != '-' && ch != ')' && ch != ']' && ch != '}' && ch != '*'\n    );\n}\n\nstatic inline bool\nis_flag_codepoint(char_type ch) {\n    return 0x1F1E6 <= ch && ch <= 0x1F1FF;\n}\n"
  },
  {
    "path": "kitty/update_check.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport subprocess\nimport time\nfrom contextlib import suppress\nfrom typing import NamedTuple\nfrom urllib.request import urlopen\n\nfrom .config import atomic_save\nfrom .constants import Version, cache_dir, clear_handled_signals, kitty_exe, version, website_url\nfrom .fast_data_types import add_timer, get_boss, monitor_pid\nfrom .utils import log_error, open_url\n\nCHANGELOG_URL = website_url('changelog')\nRELEASED_VERSION_URL = website_url() + 'current-version.txt'\nCHECK_INTERVAL = 24 * 60 * 60.\n\n\nclass Notification(NamedTuple):\n    version: Version\n    time_of_last_notification: float\n    notification_count: int\n\n\ndef notification_activated() -> None:\n    open_url(CHANGELOG_URL)\n\n\ndef version_notification_log() -> str:\n    override = getattr(version_notification_log, 'override', None)\n    if isinstance(override, str):\n        return override\n    return os.path.join(cache_dir(), 'new-version-notifications-1.txt')\n\n\ndef notify_new_version(release_version: Version) -> None:\n    get_boss().notification_manager.send_new_version_notification('.'.join(map(str, release_version)))\n\n\ndef get_released_version() -> str:\n    try:\n        raw = urlopen(RELEASED_VERSION_URL).read().decode('utf-8').strip()\n    except Exception:\n        raw = '0.0.0'\n    return str(raw)\n\n\ndef parse_line(line: str) -> Notification:\n    parts = line.split(',')\n    version, timestamp, count = parts\n    parts = version.split('.')\n    v = Version(int(parts[0]), int(parts[1]), int(parts[2]))\n    return Notification(v, float(timestamp), int(count))\n\n\ndef read_cache() -> dict[Version, Notification]:\n    notified_versions = {}\n    with suppress(FileNotFoundError):\n        with open(version_notification_log()) as f:\n            for line in f:\n                try:\n                    n = parse_line(line)\n                except Exception:\n                    continue\n                notified_versions[n.version] = n\n    return notified_versions\n\n\ndef already_notified(version: tuple[int, int, int]) -> bool:\n    notified_versions = read_cache()\n    return version in notified_versions\n\n\ndef save_notification(version: Version) -> None:\n    notified_versions = read_cache()\n    if version in notified_versions:\n        v = notified_versions[version]\n        notified_versions[version] = v._replace(time_of_last_notification=time.time(), notification_count=v.notification_count + 1)\n    else:\n        notified_versions[version] = Notification(version, time.time(), 1)\n    lines = []\n    for version in sorted(notified_versions):\n        n = notified_versions[version]\n        lines.append('{},{},{}'.format(\n            '.'.join(map(str, n.version)),\n            n.time_of_last_notification,\n            n.notification_count))\n    atomic_save('\\n'.join(lines).encode('utf-8'), version_notification_log())\n\n\ndef process_current_release(raw: str) -> None:\n    release_version = Version(*tuple(map(int, raw.split('.'))))\n    if release_version > version and not already_notified(release_version):\n        save_notification(release_version)\n        notify_new_version(release_version)\n\n\ndef run_worker() -> None:\n    import random\n    import time\n    time.sleep(random.randint(1000, 4000) / 1000)\n    with suppress(BrokenPipeError):  # happens if parent process is killed before us\n        print(get_released_version())\n\n\ndef update_check() -> bool:\n    try:\n        p = subprocess.Popen([\n            kitty_exe(), '+runpy',\n            'from kitty.update_check import run_worker; run_worker()'\n        ], stdout=subprocess.PIPE, preexec_fn=clear_handled_signals)\n    except Exception as e:\n        log_error(f'Failed to run kitty for update check, with error: {e}')\n        return False\n    monitor_pid(p.pid)\n    get_boss().set_update_check_process(p)\n    return True\n\n\ndef update_check_callback(timer_id: int | None) -> None:\n    update_check()\n\n\ndef run_update_check(interval: float = CHECK_INTERVAL) -> None:\n    if update_check():\n        add_timer(update_check_callback, interval)\n"
  },
  {
    "path": "kitty/utils.glsl",
    "content": "// Return 0 if x < 1 otherwise 1\n#define zero_or_one(x) step(1.f, x)\n// condition must be zero or one. When 1 thenval is returned otherwise elseval\n#define if_one_then(condition, thenval, elseval) mix(elseval, thenval, condition)\n// a < b ? thenval : elseval\n#define if_less_than(a, b, thenval, elseval) mix(thenval, elseval, step(b, a))\n\nvec4 vec4_premul(vec3 rgb, float a) {\n    return vec4(rgb * a, a);\n}\n\nvec4 vec4_premul(vec4 rgba) {\n    return vec4(rgba.rgb * rgba.a, rgba.a);\n}\n"
  },
  {
    "path": "kitty/utils.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport fcntl\nimport math\nimport os\nimport re\nimport string\nimport sys\nfrom collections.abc import Callable, Generator, Iterable, Iterator, Mapping, Sequence\nfrom contextlib import contextmanager, suppress\nfrom functools import lru_cache\nfrom re import Match, Pattern\nfrom types import MappingProxyType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    BinaryIO,\n    NamedTuple,\n    NoReturn,\n    Optional,\n    cast,\n)\n\nfrom .constants import (\n    clear_handled_signals,\n    config_dir,\n    is_macos,\n    is_wayland,\n    kitten_exe,\n    runtime_dir,\n    shell_path,\n    ssh_control_master_template,\n)\nfrom .fast_data_types import WINDOW_FULLSCREEN, WINDOW_HIDDEN, WINDOW_MAXIMIZED, WINDOW_MINIMIZED, WINDOW_NORMAL, Color, Shlex, get_options, monotonic, open_tty\nfrom .fast_data_types import timed_debug_print as _timed_debug_print\nfrom .types import run_once\nfrom .typing_compat import AddressFamily, PopenType, StartupCtx\n\nif TYPE_CHECKING:\n    import tarfile\n\n    from .fast_data_types import OSWindowSize\n    from .options.types import Options\nelse:\n    Options = object\n\n\nclass Flag:\n\n    def __init__(self, initial_val: bool = True) -> None:\n        self.val = initial_val\n\n    def __enter__(self) -> None:\n        self.val ^= True\n\n    def __exit__(self, *a: object) -> None:\n        self.val ^= True\n\n    def __bool__(self) -> bool:\n        return self.val\n\n\ndisallow_expand_vars = Flag(False)\n\n\ndef expandvars(val: str, env: Mapping[str, str] = {}, fallback_to_os_env: bool = True) -> str:\n    '''\n    Expand $VAR and ${VAR} Use $$ for a literal $\n    '''\n\n    def sub(m: 'Match[str]') -> str:\n        key = m.group(1) or m.group(2)\n        result = env.get(key)\n        if result is None and fallback_to_os_env:\n            result = os.environ.get(key)\n        if result is None:\n            result = m.group()\n        return result\n\n    if disallow_expand_vars or '$' not in val:\n        return val\n\n    return re.sub(r'\\$(?:(\\w+)|\\{([^}]+)\\})', sub, val.replace('$$', '\\0')).replace('\\0', '$')\n\n\n@lru_cache(maxsize=2)\ndef sgr_sanitizer_pat(for_splitting: bool = False) -> 're.Pattern[str]':\n    pat = '\\033\\\\[.*?m'\n    if for_splitting:\n        return re.compile(f'({pat})')\n    return re.compile(pat)\n\n\n@run_once\ndef kitty_ansi_sanitizer_pat() -> 're.Pattern[str]':\n    # removes ANSI sequences generated by kitty's ANSI output routines. Not\n    # suitable for stripping general ANSI sequences\n    return re.compile(r'\\x1b(?:\\[[0-9;:]*?m|\\].*?\\x1b\\\\)')\n\n\ndef platform_window_id(os_window_id: int) -> int | None:\n    if is_macos:\n        from .fast_data_types import cocoa_window_id\n        with suppress(Exception):\n            return cocoa_window_id(os_window_id)\n    if not is_wayland():\n        from .fast_data_types import x11_window_id\n        with suppress(Exception):\n            return x11_window_id(os_window_id)\n    return None\n\n\ndef safe_print(*a: Any, **k: Any) -> None:\n    with suppress(Exception):\n        print(*a, **k)\n\n\ndef log_error(*a: Any, **k: str) -> None:\n    from .fast_data_types import log_error_string\n    output = getattr(log_error, 'redirect', log_error_string)\n    with suppress(Exception):\n        msg = k.get('sep', ' ').join(map(str, a)) + k.get('end', '')\n        output(msg)\n\n\n@contextmanager\ndef suppress_error_logging() -> Iterator[None]:\n    before = getattr(log_error, 'redirect', suppress_error_logging)\n    setattr(log_error, 'redirect', lambda *a: None)\n    try:\n        yield\n    finally:\n        if before is suppress_error_logging:\n            delattr(log_error, 'redirect')\n        else:\n            setattr(log_error, 'redirect', before)\n\n\ndef ceil_int(x: float) -> int:\n    return int(math.ceil(x))\n\n\ndef sanitize_title(x: str) -> str:\n    return re.sub(r'\\s+', ' ', re.sub(r'[\\0-\\x19\\x80-\\x9f]', '', x))\n\n\ndef color_as_int(val: Color) -> int:\n    return int(val) & 0xffffff\n\n\ndef color_from_int(val: int) -> Color:\n    return Color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)\n\n\nclass ScreenSize(NamedTuple):\n    rows: int\n    cols: int\n    width: int\n    height: int\n    cell_width: int\n    cell_height: int\n\n\ndef read_screen_size(fd: int = -1) -> ScreenSize:\n    import array\n    import fcntl\n    import termios\n    buf = array.array('H', [0, 0, 0, 0])\n    if fd < 0:\n        fd = sys.stdout.fileno()\n    fcntl.ioctl(fd, termios.TIOCGWINSZ, cast(bytearray, buf))\n    rows, cols, width, height = tuple(buf)\n    cell_width, cell_height = width // (cols or 1), height // (rows or 1)\n    return ScreenSize(rows, cols, width, height, cell_width, cell_height)\n\n\nclass ScreenSizeGetter:\n    changed = True\n    Size = ScreenSize\n    ans: ScreenSize | None = None\n\n    def __init__(self, fd: int | None):\n        if fd is None:\n            fd = sys.stdout.fileno()\n        self.fd = fd\n\n    def __call__(self) -> ScreenSize:\n        if self.changed:\n            self.ans = read_screen_size(self.fd)\n            self.changed = False\n        return cast(ScreenSize, self.ans)\n\n\n@lru_cache(maxsize=64, typed=True)\ndef screen_size_function(fd: int | None = None) -> ScreenSizeGetter:\n    return ScreenSizeGetter(fd)\n\n\ndef fit_image(width: int, height: int, pwidth: int, pheight: int) -> tuple[int, int]:\n    from math import floor\n    if height > pheight:\n        corrf = pheight / float(height)\n        width, height = floor(corrf * width), pheight\n    if width > pwidth:\n        corrf = pwidth / float(width)\n        width, height = pwidth, floor(corrf * height)\n    if height > pheight:\n        corrf = pheight / float(height)\n        width, height = floor(corrf * width), pheight\n\n    return int(width), int(height)\n\n\ndef base64_encode(\n    integer: int,\n    chars: str = string.ascii_uppercase + string.ascii_lowercase + string.digits +\n    '+/'\n) -> str:\n    ans = ''\n    while True:\n        integer, remainder = divmod(integer, 64)\n        ans = chars[remainder] + ans\n        if integer == 0:\n            break\n    return ans\n\n\ndef command_for_open(program: str | list[str] = 'default') -> list[str]:\n    if isinstance(program, str):\n        from .conf.utils import to_cmdline\n        program = to_cmdline(program)\n    if program == ['default']:\n        cmd = ['open'] if is_macos else ['xdg-open']\n    else:\n        cmd = program\n    return cmd\n\n\ndef open_cmd(cmd: Iterable[str] | list[str], arg: None | Iterable[str] | str = None,\n             cwd: str | None = None, extra_env: dict[str, str] | None = None) -> 'PopenType[bytes]':\n    import subprocess\n    if arg is not None:\n        cmd = list(cmd)\n        if isinstance(arg, str):\n            cmd.append(arg)\n        else:\n            cmd.extend(arg)\n    env: dict[str, str] | None = None\n    if extra_env:\n        env = os.environ.copy()\n        env.update(extra_env)\n    return subprocess.Popen(\n        tuple(cmd), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cwd or None,\n        preexec_fn=clear_handled_signals, env=env)\n\n\ndef open_url(url: str, program: str | list[str] = 'default', cwd: str | None = None, extra_env: dict[str, str] | None = None) -> 'PopenType[bytes]':\n    return open_cmd(command_for_open(program), url, cwd=cwd, extra_env=extra_env)\n\n\ndef init_startup_notification_x11(window_handle: int, startup_id: str | None = None) -> Optional['StartupCtx']:\n    # https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt\n    from kitty.fast_data_types import init_x11_startup_notification\n    sid = startup_id or os.environ.pop('DESKTOP_STARTUP_ID', None)  # ensure child processes don't get this env var\n    if not sid:\n        return None\n    from .fast_data_types import x11_display\n    display = x11_display()\n    if not display:\n        return None\n    return init_x11_startup_notification(display, window_handle, sid)\n\n\ndef end_startup_notification_x11(ctx: 'StartupCtx') -> None:\n    from kitty.fast_data_types import end_x11_startup_notification\n    end_x11_startup_notification(ctx)\n\n\ndef init_startup_notification(window_handle: int | None, startup_id: str | None = None) -> Optional['StartupCtx']:\n    if is_macos or is_wayland():\n        return None\n    if window_handle is None:\n        log_error('Could not perform startup notification as window handle not present')\n        return None\n    try:\n        try:\n            return init_startup_notification_x11(window_handle, startup_id)\n        except OSError as e:\n            if not str(e).startswith(\"Failed to load libstartup-notification\"):\n                raise e\n            log_error(\n                f'{e}. This has two main effects:',\n                'There will be no startup feedback and when using --single-instance, kitty windows may start on an incorrect desktop/workspace.')\n    except Exception:\n        import traceback\n        traceback.print_exc()\n    return None\n\n\ndef end_startup_notification(ctx: Optional['StartupCtx']) -> None:\n    if not ctx:\n        return\n    if is_macos or is_wayland():\n        return\n    try:\n        end_startup_notification_x11(ctx)\n    except Exception:\n        import traceback\n        traceback.print_exc()\n\n\nclass startup_notification_handler:\n\n    # WARNING: This only works on X11 on other platforms extra_callback will be called\n    # after the window is shown, not before, as they do not do two stage window\n    # creation.\n\n    def __init__(self, do_notify: bool = True, startup_id: str | None = None, extra_callback: Callable[[int], None] | None = None):\n        self.do_notify = do_notify\n        self.startup_id = startup_id\n        self.extra_callback = extra_callback\n        self.ctx: Optional['StartupCtx'] = None\n\n    def __enter__(self) -> Callable[[int], None]:\n\n        def pre_show_callback(window_handle: int) -> None:\n            if self.extra_callback is not None:\n                self.extra_callback(window_handle)\n            if self.do_notify:\n                self.ctx = init_startup_notification(window_handle, self.startup_id)\n\n        return pre_show_callback\n\n    def __exit__(self, *a: Any) -> None:\n        if self.ctx is not None:\n            end_startup_notification(self.ctx)\n\n\ndef unix_socket_directories() -> Iterator[str]:\n    import tempfile\n    home = os.path.expanduser('~')\n    candidates = [tempfile.gettempdir(), home]\n    if is_macos:\n        from .fast_data_types import user_cache_dir\n        candidates = [user_cache_dir(), '/Library/Caches']\n    else:\n        if os.environ.get('XDG_RUNTIME_DIR'):\n            candidates.insert(0, os.environ['XDG_RUNTIME_DIR'])\n    for loc in candidates:\n        if os.access(loc, os.W_OK | os.R_OK | os.X_OK):\n            yield loc\n\n\ndef unix_socket_paths(name: str, ext: str = '.lock') -> Generator[str, None, None]:\n    home = os.path.expanduser('~')\n    for loc in unix_socket_directories():\n        filename = ('.' if loc == home else '') + name + ext\n        yield os.path.join(loc, filename)\n\n\ndef parse_address_spec(spec: str) -> tuple[AddressFamily, tuple[str, int] | str, str | None]:\n    import socket\n    try:\n        protocol, rest = spec.split(':', 1)\n    except ValueError:\n        raise ValueError(f'Invalid listen-on value: {spec} must be of the form protocol:address')\n    socket_path = None\n    address: str | tuple[str, int] = ''\n    if protocol == 'unix':\n        family = socket.AF_UNIX\n        address = rest\n        if address.startswith('@') and len(address) > 1:\n            address = '\\0' + address[1:]\n        else:\n            socket_path = address\n    elif protocol in ('tcp', 'tcp6'):\n        family = socket.AF_INET if protocol == 'tcp' else socket.AF_INET6\n        if rest.startswith('['):  # ]\n            host = rest[1:]\n            host, sep, leftover = host.rpartition(']')\n            _, port = leftover.rsplit(':', 1)\n            if ':' in host and protocol == 'tcp':\n                family = socket.AF_INET6\n        else:\n            host, port = rest.rsplit(':', 1)\n        address = host, int(port)\n    else:\n        raise ValueError(f'Unknown protocol in listen-on value: {spec}')\n    return family, address, socket_path\n\n\ndef parse_os_window_state(state: str) -> int:\n    match state:\n        case 'normal':\n            return WINDOW_NORMAL\n        case 'maximized':\n            return WINDOW_MAXIMIZED\n        case 'minimized':\n            return WINDOW_MINIMIZED\n        case 'fullscreen' | 'fullscreened':\n            return WINDOW_FULLSCREEN\n        case 'hidden':\n            return WINDOW_HIDDEN\n        case _:\n            return WINDOW_NORMAL\n\n\ndef write_all(fd: int, data: str | bytes, block_until_written: bool = True) -> None:\n    if isinstance(data, str):\n        data = data.encode('utf-8')\n    mvd = memoryview(data)\n    while len(mvd) > 0:\n        try:\n            n = os.write(fd, mvd)\n        except BlockingIOError:\n            if not block_until_written:\n                raise\n            continue\n        if not n:\n            break\n        mvd = mvd[n:]\n\n\nclass TTYIO:\n\n    def __init__(self, read_with_timeout: bool = True):\n        self.read_with_timeout = read_with_timeout\n\n    def __enter__(self) -> 'TTYIO':\n        self.tty_fd, self.original_termios = open_tty(self.read_with_timeout)\n        return self\n\n    def __exit__(self, *a: Any) -> None:\n        from .fast_data_types import close_tty\n        close_tty(self.tty_fd, self.original_termios)\n\n    def wait_till_read_available(self) -> bool:\n        if self.read_with_timeout:\n            raise ValueError('Cannot wait when TTY is set to read with timeout')\n        import select\n        rd = select.select([self.tty_fd], [], [])[0]\n        return bool(rd)\n\n    def read(self, limit: int) -> bytes:\n        return os.read(self.tty_fd, limit)\n\n    def send(self, data: str | bytes | Iterable[str | bytes]) -> None:\n        if isinstance(data, (str, bytes)):\n            write_all(self.tty_fd, data)\n        else:\n            for chunk in data:\n                write_all(self.tty_fd, chunk)\n\n    def recv(self, more_needed: Callable[[bytes], bool], timeout: float, sz: int = 1) -> None:\n        fd = self.tty_fd\n        start_time = monotonic()\n        while timeout > monotonic() - start_time:\n            # will block for 0.1 secs waiting for data because we have set\n            # VMIN=0 VTIME=1 in termios\n            data = os.read(fd, sz)\n            if data and not more_needed(data):\n                break\n\n\ndef set_echo(fd: int = -1, on: bool = False) -> tuple[int, list[int | list[bytes | int]]]:\n    import termios\n    if fd < 0:\n        fd = sys.stdin.fileno()\n    old = termios.tcgetattr(fd)\n    new = termios.tcgetattr(fd)\n    if on:\n        new[3] |= termios.ECHO\n    else:\n        new[3] &= ~termios.ECHO\n    termios.tcsetattr(fd, termios.TCSADRAIN, new)\n    return fd, old\n\n\n@contextmanager\ndef no_echo(fd: int = -1) -> Iterator[None]:\n    import termios\n    fd, old = set_echo(fd)\n    try:\n        yield\n    finally:\n        termios.tcsetattr(fd, termios.TCSADRAIN, old)\n\n\ndef natsort_ints(iterable: Iterable[str]) -> list[str]:\n\n    def convert(text: str) -> int | str:\n        return int(text) if text.isdigit() else text\n\n    def alphanum_key(key: str) -> tuple[int | str, ...]:\n        return tuple(map(convert, re.split(r'(\\d+)', key)))\n\n    return sorted(iterable, key=alphanum_key)\n\n\ndef get_hostname(fallback: str = '') -> str:\n    import socket\n    try:\n        return socket.gethostname() or fallback\n    except Exception:\n        return fallback\n\n\ndef resolve_editor_cmd(editor: str, shell_env: Mapping[str, str]) -> str | None:\n    import shlex\n    editor_cmd = list(shlex_split(editor))\n    editor_exe = (editor_cmd or ('',))[0]\n    if editor_exe and os.path.isabs(editor_exe):\n        return editor\n    if not editor_exe:\n        return None\n\n    def patched(exe: str) -> str:\n        editor_cmd[0] = exe\n        return ' '.join(map(shlex.quote, editor_cmd))\n\n    if shell_env is os.environ:\n        q = which(editor_exe, only_system=True)\n        if q:\n            return patched(q)\n    elif 'PATH' in shell_env:\n        import shutil\n        q = shutil.which(editor_exe, path=shell_env['PATH'])\n        if q:\n            return patched(q)\n    return None\n\n\ndef get_editor_from_env(env: Mapping[str, str]) -> str | None:\n    for var in ('VISUAL', 'EDITOR'):\n        editor = env.get(var)\n        if editor:\n            editor = resolve_editor_cmd(editor, env)\n            if editor:\n                return editor\n    return None\n\n\ndef get_editor_from_env_vars(opts: Options | None = None) -> list[str]:\n    from .child import default_env\n    editor = get_editor_from_env(default_env())\n    if not editor:\n        shell_env = read_shell_environment(opts)\n        editor = get_editor_from_env(shell_env)\n\n    for ans in (editor, 'vim', 'nvim', 'vi', 'emacs', 'hx', 'kak', 'micro', 'nano', 'vis'):\n        if ans and which(next(shlex_split(ans)), only_system=True):\n            break\n    else:\n        ans = 'vim'\n    return list(shlex_split(ans))\n\n\ndef get_editor(opts: Options | None = None, path_to_edit: str = '', line_number: int = 0) -> list[str]:\n    if opts is None:\n        try:\n            opts = get_options()\n        except RuntimeError:\n            # we are in a kitten\n            from .cli import create_default_opts\n            opts = create_default_opts()\n    if opts.editor == '.':\n        ans = get_editor_from_env_vars()\n    else:\n        ans = list(shlex_split(opts.editor))\n    ans[0] = os.path.expanduser(ans[0])\n    if path_to_edit:\n        if line_number:\n            eq = os.path.basename(ans[0]).lower()\n            if eq in ('code', 'code.exe'):\n                path_to_edit += f':{line_number}'\n                ans.append('--goto')\n            else:\n                ans.append(f'+{line_number}')\n        ans.append(path_to_edit)\n    return ans\n\n\ndef edit_file(path: str = '') -> NoReturn:\n    ' This exists for: map whatever launch kitty +runpy \"from kitty.utils import *; edit_file()\" to edit kitty config '\n    from .config import prepare_config_file_for_editing\n    editor = get_editor()\n    path = path or prepare_config_file_for_editing()\n    editor.append(path)\n    os.execlp(editor[0], *editor)\n\n\ndef is_path_in_temp_dir(path: str) -> bool:\n    if not path:\n        return False\n\n    def abspath(x: str | None) -> str:\n        if x:\n            x = os.path.abspath(os.path.realpath(x))\n        return x or ''\n\n    import tempfile\n    path = abspath(path)\n    candidates = frozenset(map(abspath, ('/tmp', '/dev/shm', os.environ.get('TMPDIR', None), tempfile.gettempdir())))\n    for q in candidates:\n        if q and path.startswith(q):\n            return True\n    return False\n\n\ndef is_ok_to_read_image_file(path: str, fd: int) -> bool:\n    import stat\n    path = os.path.abspath(os.path.realpath(path))\n    try:\n        path_stat = os.stat(path, follow_symlinks=True)\n        fd_stat = os.fstat(fd)\n    except OSError:\n        return False\n    if not os.path.samestat(path_stat, fd_stat):\n        return False\n    parts = path.split(os.sep)[1:]\n    if len(parts) < 1:\n        return False\n    if parts[0] in ('sys', 'proc', 'dev'):\n        if parts[0] == 'dev':\n            return len(parts) > 2 and parts[1] == 'shm'\n        return False\n    return stat.S_ISREG(fd_stat.st_mode)\n\n\ndef resolve_abs_or_config_path(path: str, env: Mapping[str, str] | None = None, conf_dir: str | None = None) -> str:\n    path = os.path.expanduser(path)\n    path = expandvars(path, env or {})\n    if not os.path.isabs(path):\n        path = os.path.join(conf_dir or config_dir, path)\n    return path\n\n\ndef resolve_custom_file(path: str) -> str:\n    opts: Options | None = None\n    with suppress(RuntimeError):\n        opts = get_options()\n    return resolve_abs_or_config_path(path, opts.env if opts else {})\n\n\ndef func_name(f: Any) -> str:\n    if hasattr(f, '__name__'):\n        return str(f.__name__)\n    if hasattr(f, 'func') and hasattr(f.func, '__name__'):\n        return str(f.func.__name__)\n    return str(f)\n\n\ndef resolved_shell(opts: Options | None = None) -> list[str]:\n    q: str = getattr(opts, 'shell', '.')\n    if q == '.':\n        ans = [shell_path]\n    else:\n        env = {}\n        if opts is not None:\n            env['TERM'] = opts.term\n        if 'SHELL' not in os.environ:\n            env['SHELL'] = shell_path\n        if 'HOME' not in os.environ:\n            env['HOME'] = os.path.expanduser('~')\n        if 'USER' not in os.environ:\n            import pwd\n            env['USER'] = pwd.getpwuid(os.geteuid()).pw_name\n        def expand(x: str) -> str:\n            return expandvars(x, env)\n        ans = list(map(expand, shlex_split(q)))\n    return ans\n\n\n@run_once\ndef system_paths_on_macos() -> tuple[str, ...]:\n    entries, seen = [], set()\n\n    def add_from_file(x: str) -> None:\n        try:\n            f = open(x)\n        except (FileNotFoundError, PermissionError):\n            return\n        with f:\n            for line in f:\n                line = line.strip()\n                if line and not line.startswith('#') and line not in seen:\n                    if os.path.isdir(line):\n                        seen.add(line)\n                        entries.append(line)\n    try:\n        files = os.listdir('/etc/paths.d')\n    except (FileNotFoundError, PermissionError):\n        files = []\n    for name in sorted(files):\n        add_from_file(os.path.join('/etc/paths.d', name))\n    add_from_file('/etc/paths')\n    return tuple(entries)\n\n\ndef which(name: str, only_system: bool = False) -> str | None:\n    if os.sep in name:\n        return name\n    import shutil\n\n    opts: Options | None = None\n    with suppress(RuntimeError):\n        opts = get_options()\n\n    tried_paths = set()\n    paths = []\n    append_paths = []\n    if opts and opts.exe_search_path:\n        for x in opts.exe_search_path:\n            x = x.strip()\n            if x:\n                if x[0] == '-':\n                    tried_paths.add(os.path.expanduser(x[1:]))\n                elif x[0] == '+':\n                    append_paths.append(os.path.expanduser(x[1:]))\n                else:\n                    paths.append(os.path.expanduser(x))\n    ep = os.environ.get('PATH')\n    if ep:\n        paths.extend(ep.split(os.pathsep))\n    paths.append(os.path.expanduser('~/.local/bin'))\n    paths.append(os.path.expanduser('~/bin'))\n    paths.extend(append_paths)\n    ans = shutil.which(name, path=os.pathsep.join(x for x in paths if x not in tried_paths))\n    if ans:\n        return ans\n    # In case PATH is messed up try a default set of paths\n    if is_macos:\n        system_paths = system_paths_on_macos()\n    else:\n        system_paths = ('/usr/local/bin', '/opt/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin')\n    tried_paths |= set(paths)\n    system_paths = tuple(x for x in system_paths if x not in tried_paths)\n    if system_paths:\n        ans = shutil.which(name, path=os.pathsep.join(system_paths))\n        if ans:\n            return ans\n        tried_paths |= set(system_paths)\n    if only_system or opts is None:\n        return None\n    shell_env = read_shell_environment(opts)\n    for xenv in (shell_env, opts.env):\n        q = xenv.get('PATH')\n        if q:\n            paths = [x for x in xenv['PATH'].split(os.pathsep) if x not in tried_paths]\n            ans = shutil.which(name, path=os.pathsep.join(paths))\n            if ans:\n                return ans\n            tried_paths |= set(paths)\n    return None\n\n\n@lru_cache(4)\ndef read_resolved_shell_environment(shell: tuple[str, ...]) -> MappingProxyType[str, str]:\n    import subprocess\n    cmdline = list(shell)\n    if '-l' not in cmdline and '--login' not in cmdline:\n        cmdline += ['-l']\n    if '-i' not in cmdline and '--interactive' not in cmdline:\n        cmdline += ['-i']\n    q = os.path.basename(cmdline[0]).lower()\n    has_builtin = q in ('bash', 'zsh')\n    cmd = 'builtin command env -0' if has_builtin else 'command env -0'\n    ans: MappingProxyType[str, str] = MappingProxyType({})\n\n    from .child import openpty\n    master, slave = openpty()\n    os.set_blocking(master, False)\n    try:\n        p = subprocess.Popen(\n            cmdline + ['-c', cmd], stdout=slave, stdin=slave, stderr=slave, start_new_session=True, close_fds=True,\n            preexec_fn=clear_handled_signals)\n    except FileNotFoundError:\n        log_error(f'Could not find shell {cmdline[0]} to read environment')\n        return ans\n    with os.fdopen(master, 'rb') as stdout, os.fdopen(slave, 'wb'):\n        raw = b''\n        from time import monotonic\n        start_time = monotonic()\n        ret: int | None = None\n        while monotonic() - start_time < 1.5:\n            try:\n                ret = p.wait(0.01)\n            except subprocess.TimeoutExpired:\n                ret = None\n            with suppress(Exception):\n                raw += stdout.read()\n            if ret is not None:\n                break\n        if ret is None:\n            log_error(f'Timed out waiting for shell {cmdline} to quit while reading shell environment')\n            p.kill()\n        elif ret == 0:\n            while True:\n                try:\n                    x = stdout.read()\n                except Exception:\n                    break\n                if not x:\n                    break\n                raw += x\n            draw = raw.decode('utf-8', 'replace')\n            env = {}\n            for line in draw.split('\\0'):\n                k, sep, v = line.partition('=')\n                if k and v and sep:\n                    env[k] = v\n            ans = MappingProxyType(env)\n        else:\n            log_error(f'Failed to run shell {cmdline} to read its environment')\n    return ans\n\n\ndef read_shell_environment(opts: Options | None = None) -> MappingProxyType[str, str]:\n    shell = resolved_shell(opts)\n    return read_resolved_shell_environment(tuple(shell))\n\n\ndef parse_uri_list(text: str) -> Generator[str, None, None]:\n    ' Get paths from file:// URLs '\n    from urllib.parse import unquote, urlparse\n    for line in text.splitlines():\n        if not line or line.startswith('#'):\n            continue\n        if not line.startswith('file://'):\n            yield line\n            continue\n        try:\n            purl = urlparse(line, allow_fragments=False)\n        except Exception:\n            yield line\n            continue\n        if purl.path:\n            yield unquote(purl.path)\n\n\ndef edit_config_file() -> None:\n    from kitty.config import prepare_config_file_for_editing\n    p = prepare_config_file_for_editing()\n    editor = get_editor()\n    os.execvp(editor[0], editor + [p])\n\n\nclass SSHConnectionData(NamedTuple):\n    binary: str\n    hostname: str\n    port: int | None = None\n    identity_file: str = ''\n    extra_args: tuple[tuple[str, str], ...] = ()\n\n\ndef get_new_os_window_size(\n    metrics: 'OSWindowSize', width: int, height: int, unit: str, incremental: bool = False, has_window_scaling: bool = True\n) -> tuple[int, int]:\n    if unit == 'cells':\n        cw = metrics['cell_width']\n        ch = metrics['cell_height']\n        width *= cw\n        height *= ch\n        if has_window_scaling:\n            width = round(width / metrics['xscale'])\n            height = round(height / metrics['yscale'])\n    if incremental:\n        w = metrics['width'] + width\n        h = metrics['height'] + height\n    else:\n        w = width or metrics['width']\n        h = height or metrics['height']\n    return w, h\n\n\ndef get_all_processes() -> Iterable[int]:\n    if is_macos:\n        from kitty.fast_data_types import get_all_processes as f\n        yield from f()\n    else:\n        for c in os.listdir('/proc'):\n            if c.isdigit():\n                yield int(c)\n\n\ndef is_kitty_gui_cmdline(*cmd: str) -> bool:\n    if not cmd:\n        return False\n    if os.path.basename(cmd[0]) != 'kitty':\n        return False\n    if len(cmd) == 1:\n        return True\n    s = cmd[1][:1]\n    if s == '@':\n        return False\n    if s == '+':\n        if cmd[1] == '+':\n            return len(cmd) > 2 and cmd[2] == 'open'\n        return cmd[1] == '+open'\n    return True\n\n\ndef reload_conf_in_all_kitties() -> None:\n    import signal\n\n    from kitty.child import cmdline_of_pid\n\n    for pid in get_all_processes():\n        try:\n            cmd = cmdline_of_pid(pid)\n        except Exception:\n            continue\n        if cmd and is_kitty_gui_cmdline(*cmd):\n            os.kill(pid, signal.SIGUSR1)\n\n\n@run_once\ndef control_codes_pat() -> 'Pattern[str]':\n    return re.compile('[\\x00-\\x09\\x0b-\\x1f\\x7f-\\x9f]')\n\n\ndef sanitize_control_codes(text: str, replace_with: str = '') -> str:\n    return control_codes_pat().sub(replace_with, text)\n\n\ndef hold_till_enter() -> None:\n    import subprocess\n\n    from .constants import kitten_exe\n    subprocess.Popen([kitten_exe(), '__hold_till_enter__']).wait()\n\n\ndef cleanup_ssh_control_masters() -> None:\n    import glob\n    import subprocess\n    try:\n        files = frozenset(glob.glob(os.path.join(runtime_dir(), ssh_control_master_template.format(\n            kitty_pid=os.getpid(), ssh_placeholder='*'))))\n    except OSError:\n        return\n    workers = tuple(subprocess.Popen([\n        'ssh', '-o', f'ControlPath={x}', '-O', 'exit', 'kitty-unused-host-name'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,\n        preexec_fn=clear_handled_signals) for x in files)\n    for w in workers:\n        w.wait()\n    for x in files:\n        with suppress(OSError):\n            os.remove(x)\n\n\ndef path_from_osc7_url(url: str | bytes) -> str:\n    if isinstance(url, bytes):\n        url = url.decode('utf-8')\n    if url.startswith('kitty-shell-cwd://'):\n        return '/' + url.split('/', 3)[-1]\n    if url.startswith('file://'):\n        from urllib.parse import unquote, urlparse\n        return unquote(urlparse(url).path)\n    return ''\n\n\n@run_once\ndef macos_version() -> tuple[int, ...]:\n    # platform.mac_ver does not work thanks to Apple's stupid \"hardening\", so just use sw_vers\n    import subprocess\n    try:\n        o = subprocess.check_output(['sw_vers', '-productVersion'], stderr=subprocess.STDOUT).decode()\n    except Exception:\n        return 0, 0, 0\n    return tuple(map(int, o.strip().split('.')))\n\n\n@lru_cache(maxsize=2)\ndef less_version(less_exe: str = 'less') -> int:\n    import subprocess\n    o = subprocess.check_output([less_exe, '-V'], stderr=subprocess.STDOUT).decode()\n    m = re.match(r'less (\\d+)', o)\n    if m is None:\n        raise ValueError(f'Invalid version string for less: {o}')\n    return int(m.group(1))\n\n\ndef is_pid_alive(pid: int) -> bool:\n    try:\n        os.kill(pid, 0)\n    except ProcessLookupError:\n        return False\n    except Exception:\n        pass\n    return True\n\n\ndef safer_fork() -> int:\n    pid = os.fork()\n    if pid:\n        # master\n        import ssl\n        ssl.RAND_add(os.urandom(32), 0.0)\n    else:\n        # child\n        import atexit\n        atexit._clear()\n    return pid\n\n\ndef docs_url(which: str = '', local_docs_root: str | None = '') -> str:\n    from urllib.parse import quote\n\n    from .conf.types import resolve_ref\n    from .constants import local_docs, website_url\n    if local_docs_root is None:\n        ld = ''\n    else:\n        ld = local_docs_root or local_docs()\n    base, frag = which.partition('#')[::2]\n    base = base.strip('/')\n    if frag.startswith('ref='):\n        ref = frag[4:]\n        which = resolve_ref(ref, lambda x: x)\n        if which.startswith('https://') or which.startswith('http://'):\n            return which\n        base, frag = which.partition('#')[::2]\n        base = base.strip('/')\n    if ld:\n        base = base or 'index'\n        url = f'file://{ld}/' + quote(base) + '.html'\n    else:\n        url = website_url(base)\n    if frag:\n        url += '#' + frag\n    return url\n\n\ndef sanitize_for_bracketed_paste(text: bytes) -> bytes:\n    pat = re.compile(b'(?:(?:\\033\\\\\\x5b)|(?:\\x9b))201~')\n    while True:\n        new_text = pat.sub(b'', text)\n        if new_text == text:\n            break\n        text = new_text\n    return text\n\n\n@lru_cache(maxsize=64)\ndef sanitize_url_for_display_to_user(url: str) -> str:\n    from urllib.parse import unquote, urlparse, urlunparse\n    try:\n        purl = urlparse(url)\n        if purl.netloc:\n            purl = purl._replace(netloc=purl.netloc.encode('idna').decode('ascii'))\n        if purl.path:\n            purl = purl._replace(path=unquote(purl.path))\n        url = urlunparse(purl)\n    except Exception as e:\n        log_error(e)\n        url = 'Unparseable URL: ' + url\n    return url\n\n\ndef extract_all_from_tarfile_safely(tf: 'tarfile.TarFile', dest: str) -> None:\n    # Ensure that all extracted items are within dest\n\n    def is_within_directory(directory: str, target: str) -> bool:\n        abs_directory = os.path.abspath(directory)\n        abs_target = os.path.abspath(target)\n        prefix = os.path.commonprefix((abs_directory, abs_target))\n        return prefix == abs_directory\n\n    def safe_extract(tar: 'tarfile.TarFile', path: str = \".\", numeric_owner: bool = False) -> None:\n        for member in tar.getmembers():\n            member_path = os.path.join(path, member.name)\n            if not is_within_directory(path, member_path):\n                raise ValueError(f'Attempted path traversal in tar file: {member.name}')\n        tar.extractall(path, tar.getmembers(), numeric_owner=numeric_owner)\n\n    safe_extract(tf, dest)\n\n\ndef is_png(path: str) -> bool:\n    if path:\n        with suppress(Exception), open(path, 'rb') as f:\n            header = f.read(8)\n            return header.startswith(b'\\211PNG\\r\\n\\032\\n')\n    return False\n\n\ndef cmdline_for_hold(cmd: Sequence[str] = (), opts: Optional['Options'] = None) -> list[str]:\n    if opts is None:\n        with suppress(RuntimeError):\n            opts = get_options()\n    if opts is None:\n        from .options.types import defaults\n        opts = defaults\n    ksi = ' '.join(opts.shell_integration)\n    import shlex\n    shell = shlex.join(resolved_shell(opts))\n    return [kitten_exe(), 'run-shell', f'--shell={shell}', f'--shell-integration={ksi}', '--env=KITTY_HOLD=1'] + list(cmd)\n\n\ndef safe_mtime(path: str) -> float | None:\n    with suppress(OSError):\n        return os.path.getmtime(path)\n    return None\n\n\n@run_once\ndef get_custom_window_icon() -> tuple[float, str] | tuple[None, None]:\n    filenames = ['kitty.app.png']\n    if is_macos:\n        # On macOS, prefer icns to png.\n        filenames.insert(0, 'kitty.app.icns')\n    for name in filenames:\n        custom_icon_path = os.path.join(config_dir, name)\n        custom_icon_mtime = safe_mtime(custom_icon_path)\n        if custom_icon_mtime is not None:\n            return custom_icon_mtime, custom_icon_path\n    return None, None\n\n\ndef key_val_matcher(items: Iterable[tuple[str, str]], key_pat: 're.Pattern[str]', val_pat: Optional['re.Pattern[str]']) -> bool:\n    for key, val in items:\n        if key_pat.search(key) is not None and (\n                val_pat is None or val_pat.search(val) is not None):\n            return True\n    return False\n\n\ndef shlex_split(text: str, allow_ansi_quoted_strings: bool = False) -> Iterator[str]:\n    yield from Shlex(text, allow_ansi_quoted_strings)\n\n\ndef shlex_split_with_positions(text: str, allow_ansi_quoted_strings: bool = False) -> Iterator[tuple[int, str]]:\n    s = Shlex(text, allow_ansi_quoted_strings)\n    while (q := s.next_word())[0] > -1:\n        yield q\n\n\ndef timed_debug_print(*a: Any, sep: str = ' ', end: str = '\\n') -> None:\n    _timed_debug_print(sep.join(map(str, a)) + end)\n\n\ndef lock_file(f: BinaryIO) -> None:\n    if not f.writable():\n        raise ValueError('Cannot lock files not opened in writable mode')\n    fcntl.lockf(f, fcntl.LOCK_EX)\n\n\ndef unlock_file(f: BinaryIO) -> None:\n    if not f.writable():\n        raise ValueError('Cannot unlock files not opened in writable mode')\n    fcntl.lockf(f, fcntl.LOCK_UN)\n"
  },
  {
    "path": "kitty/utmp.c",
    "content": "#include \"data-types.h\"\n#if __has_include(<utmpx.h>)\n#include <utmpx.h>\n#include <signal.h>\n\nstatic bool\npid_exists(pid_t pid) {\n    if (pid < 1) return false;\n    if (kill(pid, 0) >= 0) return true;\n    return errno != ESRCH;\n}\n\nstatic PyObject*\nnum_users(PyObject *const self UNUSED, PyObject *const args UNUSED) {\n    size_t users = 0;\n    struct utmpx *ut;\n    Py_BEGIN_ALLOW_THREADS\n    setutxent();\n    while ((ut = getutxent())) {\n        if (ut->ut_type == USER_PROCESS && ut->ut_user[0] && pid_exists(ut->ut_pid)) users++;\n    }\n    endutxent();\n    Py_END_ALLOW_THREADS\n    return PyLong_FromSize_t(users);\n}\n#else\nstatic PyObject*\nnum_users(PyObject *const self UNUSED, PyObject *const args UNUSED) {\n    PyErr_SetString(PyExc_RuntimeError, \"Counting the number of users is not supported\");\n    return NULL;\n}\n#endif\n\nstatic PyMethodDef methods[] = {\n    {\"num_users\", num_users, METH_NOARGS, \"Get the number of users using UTMP data\" },\n    { NULL, NULL, 0, NULL },\n};\n\nbool\ninit_utmp(PyObject *module) {\n    return PyModule_AddFunctions(module, methods) == 0;\n}\n"
  },
  {
    "path": "kitty/vt-parser.c",
    "content": "/*\n * vt-parser.c\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n// TODO: Test clipboard kitten with 52 and 5522\n// TODO: Test screen_request_capabilities\n\n#include \"vt-parser.h\"\n#include \"screen.h\"\n#include \"control-codes.h\"\n#include \"state.h\"\n#include \"simd-string.h\"\n#include <stdalign.h>\n\n#define BUF_SZ (1024u*1024u)\n// The extra bytes are so loads of large integers such as for AVX 512 dont read past the end of the buffer\n#define BUF_EXTRA (512u/8u)\n#define MAX_ESCAPE_CODE_LENGTH (BUF_SZ / 4u)\n#define MAX_CSI_PARAMS 256u\n\n\n// Macros {{{\n\n#define SET_STATE(x) \\\n    self->vte_state = VTE_##x;\n\n#define DIGIT '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9'\n\nstatic void\n_report_unknown_escape_code(PyObject *dump_callback, id_type window_id, const char *name, const uint8_t *payload) {\n    char buf[1024];\n    if (strlen((const char*)payload) < 64) snprintf(buf, sizeof(buf), \"Unknown %s escape code: %.64s\", name, payload);\n    else snprintf(buf, sizeof(buf), \"Unknown %s escape code: %.64s...\", name, payload);\n    if (dump_callback) {\n        Py_XDECREF(PyObject_CallFunction(dump_callback, \"Kss\", window_id, \"error\", buf)); PyErr_Clear();\n    } else log_error(ERROR_PREFIX \" \" \"%s\", buf);\n}\n\n#define REPORT_UKNOWN_ESCAPE_CODE(name, data) _report_unknown_escape_code(self->dump_callback, self->window_id, name, data);\n\n#ifdef DUMP_COMMANDS\n\nstatic void\n_report_error(PyObject *dump_callback, id_type window_id, const char *fmt, ...) {\n    va_list argptr;\n    va_start(argptr, fmt);\n    RAII_PyObject(temp, PyUnicode_FromFormatV(fmt, argptr));\n    va_end(argptr);\n    if (temp != NULL) {\n        RAII_PyObject(wid, PyLong_FromUnsignedLongLong(window_id));\n        RAII_PyObject(err, PyUnicode_FromString(\"error\"));\n        if (wid && err) Py_XDECREF(PyObject_CallFunctionObjArgs(dump_callback, wid, err, temp, NULL));\n    }\n    PyErr_Clear();\n}\n\nstatic void\n_report_params(PyObject *dump_callback, id_type window_id, const char *name, int *params, unsigned int count, bool is_group, Region *r) {\n    static char buf[MAX_CSI_PARAMS*3] = {0};\n    unsigned int i, p=0;\n    if (r) p += snprintf(buf + p, sizeof(buf) - 2, \"%u;%u;%u;%u;\", r->top, r->left, r->bottom, r->right);\n    const char *fmt = is_group ? \"%i:\" : \"%i;\";\n    for(i = 0; i < count && p < arraysz(buf)-20; i++) {\n        int n = snprintf(buf + p, arraysz(buf) - p, fmt, params[i]);\n        if (n < 0) break;\n        p += n;\n    }\n    buf[count ? p-1 : p] = 0;\n    Py_XDECREF(PyObject_CallFunction(dump_callback, \"Kss\", window_id, name, buf)); PyErr_Clear();\n}\n\nstatic void\n_report_params_with_first(PyObject *dump_callback, id_type window_id, const char *name, int first_param, int *params, unsigned count) {\n    static char buf[MAX_CSI_PARAMS*3] = {0};\n    unsigned int i, p=0;\n    p += snprintf(buf + p, sizeof(buf) - 2, \"%d;\", first_param);\n    for(i = 0; i < count && p < arraysz(buf)-20; i++) {\n        int n = snprintf(buf + p, arraysz(buf) - p, \"%i:\", params[i]);\n        if (n < 0) break;\n        p += n;\n    }\n    buf[count ? p-1 : p] = 0;\n    Py_XDECREF(PyObject_CallFunction(dump_callback, \"Kss\", window_id, name, buf)); PyErr_Clear();\n}\n\n\n#define DUMP_UNUSED\n\n#define REPORT_ERROR(...) _report_error(self->dump_callback, self->window_id, __VA_ARGS__);\n\n#define REPORT_COMMAND1(name) \\\n        Py_XDECREF(PyObject_CallFunction(self->dump_callback, \"Ks\", self->window_id, #name)); PyErr_Clear();\n\n#define REPORT_COMMAND2(name, x) \\\n        Py_XDECREF(PyObject_CallFunction(self->dump_callback, \"Ksi\", self->window_id, #name, (int)x)); PyErr_Clear();\n\n#define REPORT_COMMAND3(name, x, y) \\\n        Py_XDECREF(PyObject_CallFunction(self->dump_callback, \"Ksii\", self->window_id, #name, (int)x, (int)y)); PyErr_Clear();\n\n#define GET_MACRO(_1,_2,_3,NAME,...) NAME\n#define REPORT_COMMAND(...) GET_MACRO(__VA_ARGS__, REPORT_COMMAND3, REPORT_COMMAND2, REPORT_COMMAND1, SENTINEL)(__VA_ARGS__)\n#define REPORT_VA_COMMAND(...) Py_XDECREF(PyObject_CallFunction(self->dump_callback, __VA_ARGS__)); PyErr_Clear();\n\n#define REPORT_DRAW(chars, num) { \\\n    for (unsigned i = 0; i < (num); i++) { \\\n        uint32_t rd_ch = (chars)[i]; \\\n        switch(rd_ch) { \\\n            case BEL: REPORT_COMMAND(screen_bell); break; \\\n            case BS: REPORT_COMMAND(screen_backspace); break; \\\n            case HT: REPORT_COMMAND(screen_tab); break; \\\n            case SI: REPORT_COMMAND(screen_change_charset, 0); break; \\\n            case SO: REPORT_COMMAND(screen_change_charset, 1); break; \\\n            case LF: case VT: case FF: REPORT_COMMAND(screen_linefeed); break; \\\n            case CR: REPORT_COMMAND(screen_carriage_return); break; \\\n            default: \\\n                if (rd_ch >= ' ') { \\\n                    RAII_PyObject(t, PyObject_CallFunction(self->dump_callback, \"KsC\", self->window_id, \"draw\", rd_ch)); \\\n                    if (t == NULL) PyErr_Clear(); \\\n                } \\\n        } \\\n    } \\\n}\n\n\n#define REPORT_PARAMS(name, params, num, is_group, region) _report_params(self->dump_callback, self->window_id, name, params, num, is_group, region)\n\n#define REPORT_PARAMS_WITH_FIRST(name, first, params, num) _report_params_with_first(self->dump_callback, self->window_id, name, first, params, num)\n\n#define REPORT_OSC(name, string) \\\n    Py_XDECREF(PyObject_CallFunction(self->dump_callback, \"KsO\", self->window_id, #name, string)); PyErr_Clear();\n\n#define REPORT_OSC2(name, code, string) \\\n    Py_XDECREF(PyObject_CallFunction(self->dump_callback, \"KsiO\", self->window_id, #name, code, string)); PyErr_Clear();\n\n#define REPORT_HYPERLINK(id, url) \\\n    Py_XDECREF(PyObject_CallFunction(self->dump_callback, \"Kszz\", self->window_id, \"set_active_hyperlink\", id, url)); PyErr_Clear();\n\n#else\n#define REPORT_ERROR(...) log_error(ERROR_PREFIX \" \" __VA_ARGS__);\n#define REPORT_COMMAND(...)\n#define REPORT_VA_COMMAND(...)\n#define REPORT_DRAW(...)\n#define REPORT_PARAMS(...)\n#define REPORT_PARAMS_WITH_FIRST(...)\n#define REPORT_OSC(name, string)\n#define REPORT_OSC2(name, code, string)\n#define REPORT_HYPERLINK(id, url)\n\n#endif\n// }}}\n\n// Utils {{{\nstatic const int64_t digit_multipliers[] = {\n 10000000000000000l,\n 1000000000000000l,\n 100000000000000l,\n 10000000000000l,\n 1000000000000l,\n 100000000000l,\n 10000000000l,\n 1000000000l,\n 100000000l,\n 10000000l,\n 1000000l,\n 100000l,\n 10000l,\n 1000l,\n 100l,\n 1l\n};\n\n// }}}\n\n// Data structures {{{\ntypedef enum VTEState {\n    VTE_NORMAL, VTE_ESC = ESC, VTE_CSI = ESC_CSI, VTE_OSC = ESC_OSC, VTE_DCS = ESC_DCS, VTE_APC = ESC_APC, VTE_PM = ESC_PM, VTE_SOS = ESC_SOS\n} VTEState;\n\nstatic inline const char*\nvte_state_name(VTEState s) {\n    switch(s) {\n        case VTE_NORMAL: return \"VTE_NORMAL\";\n        case VTE_ESC: return \"VTE_ESC\";\n        case VTE_CSI: return \"VTE_CSI\";\n        case VTE_OSC: return \"VTE_OSC\";\n        case VTE_DCS: return \"VTE_DCS\";\n        case VTE_APC: return \"VTE_APC\";\n        case VTE_PM: return \"VTE_PM\";\n        case VTE_SOS: return \"VTE_SOS\";\n    }\n    static char buf[16];\n    snprintf(buf, sizeof(buf), \"VTE_0x%x\", s);\n    return buf;\n}\n\ntypedef enum { CSI_START, CSI_BODY, CSI_POST_SECONDARY } CSIState;\n\ntypedef struct ParsedCSI {\n    char primary, secondary, trailer;\n    CSIState state;\n    unsigned num_params, num_digits;\n    bool is_valid;\n    uint64_t accumulator; int mult;\n    int params[MAX_CSI_PARAMS];\n    uint8_t is_sub_param[MAX_CSI_PARAMS];\n} ParsedCSI;\n\ntypedef struct PS {\n    alignas(BUF_EXTRA) uint8_t buf[BUF_SZ + BUF_EXTRA];\n    UTF8Decoder utf8_decoder;\n\n    id_type window_id;\n\n    VTEState vte_state;\n    ParsedCSI csi;\n\n    // these are temporary variables set only for duration of a parse call\n    PyObject *dump_callback;\n    Screen *screen;\n    monotonic_t now, new_input_at;\n    pthread_mutex_t lock;\n\n    // The buffer\n    struct { size_t consumed, pos, sz; } read;\n    struct { size_t offset, sz, pending; } write;\n} PS;\n\nstatic void\nreset_csi(ParsedCSI *csi) {\n    csi->num_params = 0; csi->primary = 0; csi->secondary = 0;\n    csi->trailer = 0; csi->state = CSI_START; csi->num_digits = 0;\n    csi->is_valid = false; csi->accumulator = 0; csi->mult = 1;\n}\n// }}}\n\n// Normal mode {{{\n\nstatic void\ndispatch_single_byte_control(PS *self, uint32_t ch) {\n    REPORT_DRAW(&ch, 1);\n    screen_draw_text(self->screen, &ch, 1);\n}\n\nstatic void\nconsume_normal(PS *self) {\n    do {\n        const bool sentinel_found = utf8_decode_to_esc(&self->utf8_decoder, self->buf + self->read.pos, self->read.sz - self->read.pos);\n        self->read.pos += self->utf8_decoder.num_consumed;\n        if (self->utf8_decoder.output.pos) {\n            REPORT_DRAW(self->utf8_decoder.output.storage, self->utf8_decoder.output.pos);\n            screen_draw_text(self->screen, self->utf8_decoder.output.storage, self->utf8_decoder.output.pos);\n        }\n        if (sentinel_found) { SET_STATE(ESC); break; }\n    } while (self->read.pos < self->read.sz);\n}\n// }}}\n\n// Esc mode {{{\n#define IS_ESCAPED_CHAR \\\n        case '%': \\\n        case '(': \\\n        case ')': \\\n        case '*': \\\n        case '+': \\\n        case '-': \\\n        case '.': \\\n        case '/': \\\n        case ' ': \\\n        case '#'\n\n\nstatic void\nscreen_nel(Screen *screen) { screen_carriage_return(screen); screen_linefeed(screen); }\n\nstatic bool\nconsume_esc(PS *self) {\n#define CALL_ED(name) REPORT_COMMAND(name); name(self->screen); SET_STATE(NORMAL);\n#define CALL_ED1(name, ch) REPORT_COMMAND(name, ch); name(self->screen, ch); SET_STATE(NORMAL);\n#define CALL_ED2(name, a, b) REPORT_COMMAND(name, a, b); name(self->screen, a, b); SET_STATE(NORMAL);\n    const uint8_t ch = self->buf[self->read.pos++];\n    const bool is_first_char = self->read.pos - self->read.consumed == 1;\n    if (is_first_char) {\n        switch(ch) {\n            case ESC_DCS: SET_STATE(DCS); break;\n            case ESC_OSC: SET_STATE(OSC); break;\n            case ESC_CSI: SET_STATE(CSI); reset_csi(&self->csi); break;\n            case ESC_APC: SET_STATE(APC); break;\n            case ESC_SOS: SET_STATE(SOS); break;\n            case ESC_PM: SET_STATE(PM); break;\n            IS_ESCAPED_CHAR:\n                return false;\n            case ESC_RIS:\n                CALL_ED(screen_reset); break;\n            case ESC_IND:\n                CALL_ED(screen_index); break;\n            case ESC_NEL:\n                CALL_ED(screen_nel); break;\n            case ESC_RI:\n                CALL_ED(screen_reverse_index); break;\n            case ESC_HTS:\n                CALL_ED(screen_set_tab_stop); break;\n            case ESC_DECSC:\n                CALL_ED(screen_save_cursor); break;\n            case ESC_DECRC:\n                CALL_ED(screen_restore_cursor); break;\n            case ESC_DECKPNM:\n                CALL_ED(screen_normal_keypad_mode); break;\n            case ESC_DECKPAM:\n                CALL_ED(screen_alternate_keypad_mode); break;\n            default:\n                REPORT_ERROR(\"%s0x%x\", \"Unknown char after ESC: \", ch);\n                SET_STATE(NORMAL); break;\n        }\n        return true;\n    } else {\n        const uint8_t prev_ch = self->buf[self->read.pos-2];\n        SET_STATE(NORMAL);\n        switch(prev_ch) {\n            case '%':\n                switch(ch) {\n                    case '@':\n                        REPORT_ERROR(\"Ignoring attempt to switch to non-utf8 encoding\");\n                        break;\n                    case 'G':\n                        REPORT_ERROR(\"Ignoring attempt to switch to utf8 encoding as we are always utf-8\");\n                        break;\n                    default:\n                        REPORT_ERROR(\"Unhandled Esc %% code: 0x%x\", ch);  break;\n                }\n                break;\n            case '#':\n                if (ch == '8') { CALL_ED(screen_align); }\n                else { REPORT_ERROR(\"Unhandled Esc # code: 0x%x\", ch); }\n                break;\n            case '(':\n            case ')':\n                switch(ch) {\n                    case 'A':\n                    case 'B':\n                    case '0':\n                    case 'U':\n                    case 'V':\n                        CALL_ED2(screen_designate_charset, prev_ch - '(', ch); break;\n                    default:\n                        REPORT_ERROR(\"Unknown charset: 0x%x\", ch); break;\n                }\n                break;\n            case ' ':\n                switch(ch) {\n                    case 'F':\n                    case 'G':\n                        REPORT_ERROR(\"Ignoring attempt to turn on/off C1 controls as we only support C0 controls\"); break;\n                    default:\n                        REPORT_ERROR(\"Unhandled ESC SP escape code: 0x%x\", ch); break;\n                }\n                break;\n            default:\n                REPORT_ERROR(\"Unhandled charset related escape code: 0x%x 0x%x\", prev_ch, ch); break;\n        }\n        return true;\n    }\n#undef CALL_ED\n#undef CALL_ED1\n} // }}}\n\n// ST terminator {{{\nstatic bool\nfind_st_terminator(PS *self, size_t *end_pos) {\n    const size_t sz = self->read.sz - self->read.pos;\n    const uint8_t *q = find_either_of_two_bytes(self->buf + self->read.pos, sz, BEL, ESC_ST);\n    if (q == NULL) {\n        self->read.pos += sz;\n        return false;\n    }\n    switch(*q) {\n        case ESC_ST:\n            if (q > self->buf && *(q-1) == ESC) {\n                *end_pos = q - 1 - self->buf;\n                self->read.pos = *end_pos + 2;\n                return true;\n            }\n            self->read.pos = (q - self->buf) + 1;\n            break;\n        case BEL:\n            *end_pos = q - self->buf;\n            self->read.pos = *end_pos + 1;\n            return true;\n    }\n    return false;\n}\n// }}}\n\n// OSC {{{\n\n#include \"parse-multicell-command.h\"\n\nstatic bool\nis_osc_52(PS *self) {\n    return memcmp(self->buf + self->read.consumed, \"52;\", 3) == 0;\n}\n\nstatic void\ncontinue_osc_52(PS *self) {\n    self->read.pos -= 4;\n    self->read.consumed = self->read.pos;\n    self->buf[self->read.pos++] = '5'; self->buf[self->read.pos++] = '2';\n    self->buf[self->read.pos++] = ';'; self->buf[self->read.pos++] = ';';\n}\n\n\nstatic bool\naccumulate_st_terminated_esc_code(PS *self, void(dispatch)(PS*, uint8_t*, size_t, bool)) {\n    size_t pos;\n    if (find_st_terminator(self, &pos)) {\n        // technically we should check MAX_ESCAPE_CODE_LENGTH here but lets be generous in what we accept since  we\n        // have a full escape code\n        uint8_t *buf = self->buf + self->read.consumed;\n        size_t sz = pos - self->read.consumed;\n        buf[sz] = 0;  // ensure null termination, this is anyway an ST termination char\n        dispatch(self, buf, sz, false);\n        return true;\n    }\n    if (UNLIKELY((pos=self->read.pos - self->read.consumed) > MAX_ESCAPE_CODE_LENGTH)) {\n        if (self->vte_state == VTE_OSC && is_osc_52(self)) {\n            // null terminate\n            self->read.pos--;\n            uint8_t before = self->buf[self->read.pos];\n            self->buf[self->read.pos] = 0;\n            // send partial OSC 52\n            dispatch(self, self->buf + self->read.consumed, self->read.pos - self->read.consumed, true);\n            // continue OSC 52\n            self->buf[self->read.pos] = before;\n            continue_osc_52(self);\n            return accumulate_st_terminated_esc_code(self, dispatch);\n        }\n        REPORT_ERROR(\"%s escape code too long (%zu bytes), ignoring it\", vte_state_name(self->vte_state), pos);\n        return true;\n    }\n    return false;\n}\n\nstatic bool\nparse_osc_8(char *buf, char **id, char **url) {\n    // the spec says only ASCII printable chars are allowed in OSC 8\n    char *boundary = strstr(buf, \";\");\n    if (boundary == NULL) return false;\n    *boundary = 0;\n    if (*(boundary + 1)) *url = boundary + 1;\n    char *save = NULL, *token = strtok_r(buf, \":\", &save);\n    while (token != NULL) {\n        size_t len = strlen(token);\n        if (len > 3 && token[0] == 'i' && token[1] == 'd' && token[2] == '=' && token[3]) {\n            *id = token + 3;\n            break;\n        }\n        token = strtok_r(NULL, \":\", &save);\n    }\n    return true;\n}\n\nstatic void\ndispatch_hyperlink(PS *self, char *buf) {\n    char *id = NULL, *url = NULL;\n    if (parse_osc_8(buf, &id, &url)) {\n        REPORT_HYPERLINK(id, url);\n        set_active_hyperlink(self->screen, id, url);\n    } else {\n        REPORT_ERROR(\"Ignoring malformed OSC 8 code\");\n    }\n}\n\n\nstatic void\ndispatch_osc(PS *self, uint8_t *buf, size_t limit, bool is_extended_osc) {\n#define DISPATCH_OSC_WITH_CODE(name) REPORT_OSC2(name, code, mv); name(self->screen, code, mv);\n#define DISPATCH_OSC(name) REPORT_OSC(name, mv); name(self->screen, mv);\n#define START_DISPATCH {\\\n    RAII_PyObject(mv, PyMemoryView_FromMemory((char*)buf + i, limit - i, PyBUF_READ)); \\\n    if (mv) {\n#define END_DISPATCH_WITHOUT_BREAK }; PyErr_Clear(); }\n#define END_DISPATCH }; PyErr_Clear(); break; }\n\n    int64_t accumulator = 0;\n    int code=0;\n    unsigned int i;\n    for (i = 0; i < MIN(limit, 5u); i++) {\n        int64_t num = buf[i] - '0';\n        if (num < 0 || num > 9) break;\n        accumulator += num * digit_multipliers[i];\n    }\n    if (i > 0) {\n        code = accumulator / digit_multipliers[i - 1];\n        if (i < limit && buf[i] == ';') i++;\n    }\n\n    switch(code) {\n        case 0:\n            START_DISPATCH\n            DISPATCH_OSC(set_title);\n            DISPATCH_OSC(set_icon);\n            END_DISPATCH\n        case 1:\n            START_DISPATCH\n            DISPATCH_OSC(set_icon);\n            END_DISPATCH\n        case 2:\n            START_DISPATCH\n            DISPATCH_OSC(set_title);\n            END_DISPATCH\n        case 5: case 105: REPORT_ERROR(\"Ignoring OSC 5/105, used by XTerm to change special colors used for rendering bold/italic/underline\"); break;\n        case 6: case 106: {  // report only once as this is used by benchmark kitten causing log spam\n            static bool reported = false;\n            if (!reported) {\n                reported = true;\n                REPORT_ERROR(\"Ignoring OSC 6/106, used by XTerm to enable/disable special colors used for rendering bold/italic/underline\");\n            }\n        } break;\n        case 4:\n        case 104:\n            START_DISPATCH\n            DISPATCH_OSC_WITH_CODE(set_color_table_color);\n            END_DISPATCH\n        case 7:\n#ifdef DUMP_COMMANDS\n            START_DISPATCH\n            REPORT_OSC2(process_cwd_notification, code, mv);\n            END_DISPATCH_WITHOUT_BREAK\n#endif\n            process_cwd_notification(self->screen, code, (char*)buf + i, limit-i);\n            break;\n        case 8:\n            dispatch_hyperlink(self, (char*)buf + i);\n            break;\n        case 9:\n        case 99:\n        case 777:\n        case 1337:\n            START_DISPATCH\n            DISPATCH_OSC_WITH_CODE(desktop_notify)\n            END_DISPATCH\n        case 13: case 14: case 15: case 16: case 18:\n            REPORT_ERROR(\"Ignoring OSC 13,14,15,16 and 18 used for pointer and Textronic colors by XTerm\"); break;\n            break;\n        case 10:\n        case 11:\n        case 12:\n        case 17:\n        case 19:\n        case 22:\n        case 110:\n        case 111:\n        case 112:\n        case 117:\n        case 119:\n            START_DISPATCH\n            DISPATCH_OSC_WITH_CODE(set_dynamic_color);\n            END_DISPATCH\n        case 21:\n            START_DISPATCH\n            DISPATCH_OSC_WITH_CODE(color_control);\n            END_DISPATCH\n        case 52: case 5522:\n            START_DISPATCH\n            if (is_extended_osc && code == 52) code = -52;\n            DISPATCH_OSC_WITH_CODE(clipboard_control);\n            END_DISPATCH\n        case 46: REPORT_ERROR(\"Ignoring OSC 46 used for file logging in XTerm\"); break;\n        case 50: REPORT_ERROR(\"Ignoring OSC 50 used for font changing in XTerm\"); break;\n        case 51: REPORT_ERROR(\"Ignoring OSC 51 used by emacs shell\"); break;\n        case 60: case 61: REPORT_ERROR(\"Ignoring OSC 60/61 used for query control in XTerm\"); break;\n        case TEXT_SIZE_CODE:\n            parse_multicell_code(self, buf + i, limit - i);\n            break;\n        case 133:\n#ifdef DUMP_COMMANDS\n            START_DISPATCH\n            REPORT_OSC2(shell_prompt_marking, code, mv);\n            END_DISPATCH_WITHOUT_BREAK\n#endif\n            if (limit > i) {\n                buf[limit] = 0; // safe to do as we have 8 extra bytes after PARSER_BUF_SZ\n                shell_prompt_marking(self->screen, (char*)buf + i);\n            }\n            break;\n        case FILE_TRANSFER_CODE:\n            START_DISPATCH\n            DISPATCH_OSC(file_transmission);\n            END_DISPATCH\n        case 30001:\n            REPORT_COMMAND(screen_push_dynamic_colors);\n            screen_push_colors(self->screen, 0);\n            break;\n        case 30101:\n            REPORT_COMMAND(screen_pop_dynamic_colors);\n            screen_pop_colors(self->screen, 0);\n            break;\n        case 440: REPORT_ERROR(\"Ignoring OSC 440 used for audio by mintty\"); break;\n        case 633: REPORT_ERROR(\"Ignoring OSC 633, use by Windows Terminal for VSCode actions\"); break;\n        case 666: REPORT_ERROR(\"Ignoring OSC 666, typically used by VTE terminals for shell integration\"); break;\n        case 697: REPORT_ERROR(\"Ignoring OSC 697, typically used by Fig for shell integration\"); break;\n        case 701: REPORT_ERROR(\"Ignoring OSC 701, used by mintty for locale\"); break;\n        case 3008:\n            START_DISPATCH\n            DISPATCH_OSC(osc_context);\n            END_DISPATCH\n        case 7704: REPORT_ERROR(\"Ignoring OSC 7704, used by mintty for ANSI colors\"); break;\n        case 7750: REPORT_ERROR(\"Ignoring OSC 7750, used by mintty for Emoji style\"); break;\n        case 7770: REPORT_ERROR(\"Ignoring OSC 7770, used by mintty for font size\"); break;\n        case 7721: REPORT_ERROR(\"Ignoring OSC 7721, used by mintty for copy window title\"); break;\n        case 7771: REPORT_ERROR(\"Ignoring OSC 7771, used by mintty for glyph coverage\"); break;\n        case 7777: REPORT_ERROR(\"Ignoring OSC 7777, used by mintty for window size\"); break;\n        case 77119: REPORT_ERROR(\"Ignoring OSC 7777, used by mintty for wide chars\"); break;\n        case 9001: REPORT_ERROR(\"Ignoring OSC 9001, used by windows terminal\"); break;\n        default:\n            REPORT_UKNOWN_ESCAPE_CODE(\"OSC\", buf);\n            break;\n    }\n#undef DISPATCH_OSC\n#undef DISPATCH_OSC_WITH_CODE\n#undef START_DISPATCH\n#undef END_DISPATCH\n}\n\n// }}}\n\n// DCS {{{\n\nstatic bool\nstartswith(const uint8_t *string, ssize_t sz, const char *prefix, ssize_t l) {\n    if (sz < l) return false;\n    for (ssize_t i = 0; i < l; i++) {\n        if (string[i] != (unsigned char)prefix[i]) return false;\n    }\n    return true;\n}\n\nstatic bool\nparse_kitty_dcs(PS *self, uint8_t *buf, size_t bufsz) {\n#define starts_with(x) startswith(buf, bufsz, x, literal_strlen(x))\n#define inc(x) buf += literal_strlen(x); bufsz -= literal_strlen(x)\n#define dispatch(prefix, func, delta) {\\\n    if (starts_with(prefix)) {\\\n        inc(prefix); buf -= delta; bufsz += delta; \\\n        PyObject *cmd = PyMemoryView_FromMemory((char*)buf, bufsz, PyBUF_READ); \\\n        if (cmd) { \\\n            REPORT_OSC(func, cmd); \\\n            screen_handle_kitty_dcs(self->screen, #func, cmd); \\\n            Py_DECREF(cmd); \\\n        } else PyErr_Clear(); \\\n        return true; \\\n    }}\n    if (!starts_with(\"kitty-\")) return false;\n    inc(\"kitty-\");\n\n    dispatch(\"cmd{\", handle_remote_cmd, 1);\n    dispatch(\"overlay-ready|\", handle_overlay_ready, 0)\n    dispatch(\"kitten-result|\", handle_kitten_result, 0)\n    dispatch(\"print|\", handle_remote_print, 0)\n    dispatch(\"echo|\", handle_remote_echo, 0)\n    dispatch(\"ssh|\", handle_remote_ssh, 0)\n    dispatch(\"ask|\", handle_remote_askpass, 0)\n    dispatch(\"clone|\", handle_remote_clone, 0)\n    dispatch(\"edit|\", handle_remote_edit, 0)\n    dispatch(\"restore-cursor-appearance|\", handle_restore_cursor_appearance, 0)\n\n    return false;\n#undef dispatch\n#undef starts_with\n#undef inc\n}\n\nstatic void\ndispatch_dcs(PS *self, uint8_t *buf, size_t bufsz, bool is_extended UNUSED) {\n    if (bufsz < 2) return;\n    switch (buf[0]) {\n        case '+':\n        case '$':\n            if (buf[1] == 'q') {\n                PyObject *mv = PyMemoryView_FromMemory((char*)buf + 2, bufsz-2, PyBUF_READ);\n                if (mv) {\n                    REPORT_OSC2(screen_request_capabilities, (char)buf[0], mv);\n                    Py_DECREF(mv);\n                } else PyErr_Clear();\n                screen_request_capabilities(self->screen, (char)buf[0], (char*)buf + 2);\n            } else {\n                REPORT_UKNOWN_ESCAPE_CODE(\"DCS\", buf);\n            }\n            break;\n        case '=':\n            if (bufsz > 2 && (buf[1] == '1' || buf[1] == '2') && buf[2] == 's') {\n                if (buf[1] == '1') {\n                    REPORT_COMMAND(screen_start_pending_mode)\n                    if (!screen_pause_rendering(self->screen, true, 0)) {\n                        REPORT_ERROR(\"Pending mode start requested while already in pending mode. This is most likely an application error.\");\n                    }\n                } else {\n                    REPORT_COMMAND(screen_stop_pending_mode);\n                    if (!screen_pause_rendering(self->screen, false, 0)) {\n                        REPORT_ERROR(\"Pending mode stop command issued while not in pending mode, this can\"\n                            \" be either a bug in the terminal application or caused by a timeout with no data\"\n                            \" received for too long or by too much data in pending mode\");\n                    }\n                }\n            } else {\n                REPORT_UKNOWN_ESCAPE_CODE(\"DCS\", buf);\n            } break;\n        case '@':\n            if (!parse_kitty_dcs(self, buf + 1, bufsz-1)) REPORT_UKNOWN_ESCAPE_CODE(\"DCS\", buf);\n            break;\n        default:\n            REPORT_UKNOWN_ESCAPE_CODE(\"DCS\", buf);\n            break;\n    }\n}\n\n// }}}\n\n// CSI {{{\n\n#define CSI_SECONDARY \\\n        ' ': \\\n        case '!': \\\n        case '\"': \\\n        case '#': \\\n        case '$': \\\n        case '%': \\\n        case '&': \\\n        case '\\'': \\\n        case '(': \\\n        case ')': \\\n        case '*': \\\n        case '+': \\\n        case ',': \\\n        case '-': \\\n        case '.': \\\n        case '/'\n\n#define CSI_TRAILER \\\n        '@': \\\nSTART_ALLOW_CASE_RANGE \\\n        case 'a' ... 'z': \\\n        case 'A' ... 'Z': \\\nEND_ALLOW_CASE_RANGE \\\n        case '`': \\\n        case '{': \\\n        case '|': \\\n        case '}': \\\n        case '~'\n\n#define CSI_NORMAL_MODE_EMBEDDINGS \\\n        BEL: \\\n        case BS: \\\n        case HT: \\\n        case LF: \\\n        case VT: \\\n        case FF: \\\n        case CR: \\\n        case SO: \\\n        case SI\n\nstatic const char*\ncsi_letter(unsigned code) {\n    static char buf[8];\n    if (33 <= code && code <= 126) snprintf(buf, sizeof(buf), \"%c\", code);\n    else snprintf(buf, sizeof(buf), \"0x%x\", code);\n    return buf;\n}\n\nstatic bool\ncommit_csi_param(PS *self UNUSED, ParsedCSI *csi) {\n    if (!csi->num_digits) return true;\n    if (csi->num_params >= MAX_CSI_PARAMS) {\n        REPORT_ERROR(\"CSI escape code has too many parameters, ignoring it\");\n        return false;\n    }\n    csi->params[csi->num_params++] = csi->mult * (csi->accumulator / digit_multipliers[csi->num_digits - 1]);\n    csi->num_digits = 0; csi->mult = 1; csi->accumulator = 0;\n    return true;\n}\n\nstatic void\ncsi_add_digit(ParsedCSI *csi, uint8_t ch) {\n    if (UNLIKELY(csi->num_digits >= arraysz(digit_multipliers))) return;\n    csi->accumulator += (ch - '0') * digit_multipliers[csi->num_digits++];\n}\n\nstatic bool\ncsi_parse_loop(PS *self, ParsedCSI *csi, const uint8_t *buf, size_t *pos, const size_t sz, const size_t start) {\n    while (*pos < sz) {\n        const uint8_t ch = buf[*pos]; *pos += 1;\n        switch(csi->state) {\n            case CSI_START:\n                switch (ch) {\n                    case CSI_NORMAL_MODE_EMBEDDINGS:\n                        dispatch_single_byte_control(self, ch); break;\n                    case ';':\n                        csi->params[csi->num_params++] = 0;\n                        csi->state = CSI_BODY;\n                        break;\n                    case DIGIT:\n                        csi_add_digit(csi, ch);\n                        csi->state = CSI_BODY;\n                        break;\n                    case '?':\n                    case '>':\n                    case '<':\n                    case '=':\n                        csi->state = CSI_BODY;\n                        csi->primary = ch;\n                        break;\n                    case CSI_SECONDARY:\n                        if (ch == '-') {\n                            csi->mult = -1;\n                            csi->num_digits++;\n                            csi->state = CSI_BODY;\n                        } else {\n                            csi->secondary = ch;\n                            csi->state = CSI_POST_SECONDARY;\n                        }\n                        break;\n                    case CSI_TRAILER:\n                        csi->is_valid = true;\n                        csi->trailer = ch;\n                        return true;\n                    default:\n                        REPORT_ERROR(\"Invalid character in CSI: %s (0x%x), ignoring the sequence\", csi_letter(ch), ch);\n                        return true;\n                }\n                break;\n            case CSI_POST_SECONDARY:\n                switch (ch) {\n                    case CSI_NORMAL_MODE_EMBEDDINGS:\n                        dispatch_single_byte_control(self, ch); break;\n                    case CSI_TRAILER:\n                        csi->is_valid = true;\n                        csi->trailer = ch;\n                        break;\n                    default:\n                        REPORT_ERROR(\"Invalid character in CSI: %s (0x%x), ignoring the sequence\", csi_letter(ch), ch);\n                        break;\n                }\n                return true;\n            case CSI_BODY:\n                switch(ch) {\n                    case CSI_NORMAL_MODE_EMBEDDINGS:\n                        dispatch_single_byte_control(self, ch); break;\n                    case CSI_SECONDARY:\n                        if (ch == '-' && csi->num_digits == 0) {\n                            csi->mult = -1; csi->num_digits = 1;\n                        } else {\n                            if (!commit_csi_param(self, csi)) return true;\n                            csi->secondary = ch;\n                            csi->state = CSI_POST_SECONDARY;\n                        }\n                        break;\n                    case CSI_TRAILER:\n                        if (csi->num_digits == 1 && csi->secondary == 0 && csi->mult == -1) {\n                            csi->num_digits = 0; csi->secondary = '-';\n                        }\n                        if (!commit_csi_param(self, csi)) return true;\n                        csi->is_valid = true;\n                        csi->trailer = ch;\n                        return true;\n                    case ':':\n                        if (!commit_csi_param(self, csi)) return true;\n                        csi->is_sub_param[csi->num_params] = true;\n                        break;\n                    case ';':\n                        if (!csi->num_digits) csi->num_digits++;  // Empty means zero\n                        if (!commit_csi_param(self, csi)) return true;\n                        csi->is_sub_param[csi->num_params] = false;\n                        break;\n                    case DIGIT:\n                        csi_add_digit(csi, ch);\n                        break;\n                    default:\n                        REPORT_ERROR(\"Invalid character in CSI: %s (0x%x), ignoring the sequence\", csi_letter(ch), ch);\n                        return true;\n                }\n                break;\n        }\n    }\n    if (UNLIKELY(*pos - start > MAX_ESCAPE_CODE_LENGTH)) {\n        REPORT_ERROR(\"CSI escape too long ignoring and truncating\");\n        return true;\n    }\n    return false;\n#undef COMMIT_PARAM\n}\n\nstatic bool\nconsume_csi(PS *self) {\n    return csi_parse_loop(self, &self->csi, self->buf, &self->read.pos, self->read.sz, self->read.consumed);\n}\n\nstatic void\n_parse_multi_cursors(PS *self, ParsedCSI *csi) {\n    switch(csi->num_params) {\n    case 0:\n        REPORT_COMMAND(\"screen_multi_cursor\");\n        screen_multi_cursor(self->screen, 0, NULL, 0);\n        break;\n    case 1:\n        REPORT_PARAMS_WITH_FIRST(\"screen_multi_cursor\", csi->params[0], csi->params, 0);\n        screen_multi_cursor(self->screen, csi->params[0], csi->params, 0);\n        break;\n    default: {\n    unsigned pos = 1, first_param = pos;\n    for (; pos < csi->num_params; pos++) {\n        if (pos > first_param) {\n            if (!csi->is_sub_param[pos]) {\n                REPORT_PARAMS_WITH_FIRST(\"screen_multi_cursor\", csi->params[0], csi->params + first_param, pos - first_param);\n                screen_multi_cursor(self->screen, csi->params[0], csi->params + first_param, pos - first_param);\n                first_param = pos;\n            }\n        }\n    }\n    if (pos > first_param) {\n        REPORT_PARAMS_WITH_FIRST(\"screen_multi_cursor\", csi->params[0], csi->params + first_param, pos - first_param);\n        screen_multi_cursor(self->screen, csi->params[0], csi->params + first_param, pos - first_param);\n    }}}\n}\n\n\nstatic unsigned int\nparse_region(const ParsedCSI *csi, Region *r) {\n    switch(csi->num_params) {\n        case 0:\n            return 0;\n        case 1:\n            r->top = csi->params[0];\n            return 1;\n        case 2:\n            r->top = csi->params[0]; r->left = csi->params[1];\n            return 2;\n        case 3:\n            r->top = csi->params[0]; r->left = csi->params[1]; r->bottom = csi->params[2];\n            return 3;\n        default:\n            r->top = csi->params[0]; r->left = csi->params[1]; r->bottom = csi->params[2]; r->right = csi->params[3];\n            return 4;\n    }\n}\n\nstatic bool\n_parse_sgr(PS *self, ParsedCSI *csi) {\n#define SEND_SGR if (num_params) { \\\n    REPORT_PARAMS(report_name, csi->params + first_param, num_params, state != NORMAL, region); \\\n    select_graphic_rendition(screen, csi->params + first_param, num_params, state != NORMAL, region); \\\n    state = NORMAL; first_param += num_params; num_params = 0; \\\n}\n    Screen *screen = self->screen;\n    size_t pos = 0, first_param, num_params = 0;\n    Region r = {0}, *region = NULL;\n    const char *report_name = \"select_graphic_rendition\";\n    if (csi->trailer == 'r') {  // DECCARA\n        region = &r;\n        if (csi->num_params == 0) {\n            for (; csi->num_params < 5; csi->num_params++) csi->params[csi->num_params] = 0;\n        }\n        pos = parse_region(csi, region);\n        report_name = \"deccara\";\n        (void)report_name;\n    } else if (csi->num_params == 0) {\n        csi->params[0] = 0;\n        csi->num_params++;\n    }\n    enum State { NORMAL, SUB_PARAMS, COLOR, COLOR1, COLOR3 };\n    enum State state = NORMAL;\n\n    for (first_param = pos; pos < csi->num_params; pos++) {\n        switch (state) {\n            case NORMAL:\n                if (csi->is_sub_param[pos]) {\n                    if (num_params == 0 || pos == 0)  {\n                        REPORT_ERROR(\"SGR escape code has an unexpected sub-parameter ignoring the full code\");\n                        return false;\n                    }\n                    num_params--;\n                    SEND_SGR;\n                    state = SUB_PARAMS;\n                    first_param = pos - 1;\n                    num_params = 1;\n                }\n                switch(csi->params[pos]) {\n                    case 38: case 48: case DECORATION_FG_CODE:\n                        SEND_SGR;\n                        state = COLOR;\n                        first_param = pos;\n                        num_params = 1;\n                        break;\n                    default:\n                        num_params++;\n                        break;\n                } break;\n            case SUB_PARAMS:\n                switch(csi->is_sub_param[pos]) {\n                    case true:\n                        num_params++; break;\n                    case false:\n                        SEND_SGR;\n                        pos--;\n                        break;\n                } break;\n            case COLOR:\n                switch(csi->params[pos]) {\n                    case 2:\n                        state = csi->is_sub_param[pos] ? SUB_PARAMS : COLOR3;\n                        num_params++;\n                        break;\n                    case 5:\n                        state = csi->is_sub_param[pos] ? SUB_PARAMS : COLOR1;\n                        num_params++;\n                        break;\n                    default:\n                        REPORT_ERROR(\"SGR escape code has unknown color type: %d ignoring the full code\", csi->params[pos]);\n                        return false;\n                } break;\n            case COLOR1:\n                num_params++;\n                SEND_SGR;\n                break;\n            case COLOR3:\n                num_params++;\n                if (num_params >= 5) { SEND_SGR; }\n                break;\n        }\n    }\n    SEND_SGR;\n    return true;\n#undef SEND_SGR\n}\n\n#ifndef DUMP_COMMANDS\nbool\nparse_sgr(Screen *screen, const uint8_t *buf, unsigned int num, const char *report_name UNUSED, bool is_deccara) {\n    ParsedCSI csi = {.mult=1};\n    size_t pos = 0;\n    RAII_ALLOC(uint8_t, _buf, malloc(num + 3));\n    if (!_buf) return false;\n    memcpy(_buf, buf, num);\n    if (is_deccara) {\n        _buf[num++] = '$'; _buf[num++] = 'r';\n    } else {\n        _buf[num++] = 'm';\n    }\n    _buf[num] = 0;\n    PS *state = (PS*)screen->vt_parser->state;\n    state->screen = screen;\n    if (!csi_parse_loop(state, &csi, _buf, &pos, num, 0)) return false;\n    return _parse_sgr(state, &csi);\n}\n#endif\n\nstatic void\nscreen_cursor_up2(Screen *s, unsigned int count) { screen_cursor_up(s, count, false, -1); }\nstatic void\nscreen_cursor_back1(Screen *s, unsigned int count) { screen_cursor_move(s, count, -1, false); }\nstatic void\nscreen_tabn(Screen *s, unsigned int count) { for (index_type i=0; i < MAX(1u, count); i++) screen_tab(s); }\n\nstatic const char*\nrepr_csi_params(int *params, unsigned int num_params) {\n    if (!num_params) return \"\";\n    static char buf[256];\n    unsigned int pos = 0, i = 0;\n    while (pos < 200 && i++ < num_params && sizeof(buf) > pos + 1) {\n        const char *fmt = i < num_params ? \"%i, \" : \"%i\";\n        int ret = snprintf(buf + pos, sizeof(buf) - pos - 1, fmt, params[i-1]);\n        if (ret < 0) return \"An error occurred formatting the params array\";\n        pos += ret;\n    }\n    buf[pos] = 0;\n    return buf;\n}\n\nstatic void\nhandle_mode(PS *self) {\n    bool is_shifted = self->csi.primary == '?';\n    int shift = is_shifted ? 5 : 0;\n    for (unsigned i = 0; i < self->csi.num_params; i++) {\n        int p = self->csi.params[i];\n        if (p >= 0) {\n            unsigned int sp = p << shift;\n            switch (self->csi.trailer) {\n                case SM:\n                    screen_set_mode(self->screen, sp);\n                    REPORT_COMMAND(screen_set_mode, p, is_shifted);\n                    break;\n                case RM:\n                    screen_reset_mode(self->screen, sp);\n                    REPORT_COMMAND(screen_reset_mode, p, is_shifted);\n                    break;\n                case 's':\n                    screen_save_mode(self->screen, sp);\n                    REPORT_COMMAND(screen_save_mode, p, is_shifted);\n                    break;\n                case 'r':\n                    screen_restore_mode(self->screen, sp);\n                    REPORT_COMMAND(screen_restore_mode, p, is_shifted);\n                    break;\n            }\n        }\n    }\n}\n\nstatic void\ndispatch_csi(PS *self) {\n#define num_params self->csi.num_params\n#define code self->csi.trailer\n#define params self->csi.params\n#define start_modifier self->csi.primary\n#define end_modifier self->csi.secondary\n\n#define AT_MOST_ONE_PARAMETER { \\\n    if (num_params > 1) { \\\n        REPORT_ERROR(\"CSI code %s has %u > 1 parameters\", csi_letter(code), num_params); \\\n        break; \\\n    } \\\n}\n#define NON_NEGATIVE_PARAM(x) { \\\n    if (x < 0) { \\\n        REPORT_ERROR(\"CSI code %s is not allowed to have negative parameter (%d)\", csi_letter(code), x); \\\n        break; \\\n    } \\\n}\n\n#define CALL_CSI_HANDLER1(name, defval) \\\n    AT_MOST_ONE_PARAMETER; \\\n    p1 = num_params > 0 ? params[0] : defval; \\\n    NON_NEGATIVE_PARAM(p1); \\\n    REPORT_COMMAND(name, p1); \\\n    name(self->screen, p1); \\\n    break;\n\n#define CALL_CSI_HANDLER1P(name, defval, qch) \\\n    AT_MOST_ONE_PARAMETER; \\\n    p1 = num_params > 0 ? params[0] : defval; \\\n    NON_NEGATIVE_PARAM(p1); \\\n    private = start_modifier == qch; \\\n    REPORT_COMMAND(name, p1, private); \\\n    name(self->screen, p1, private); \\\n    break;\n\n#define CALL_CSI_HANDLER1S(name, defval) \\\n    AT_MOST_ONE_PARAMETER; \\\n    p1 = num_params > 0 ? params[0] : defval; \\\n    NON_NEGATIVE_PARAM(p1); \\\n    REPORT_COMMAND(name, p1, start_modifier); \\\n    name(self->screen, p1, start_modifier); \\\n    break;\n\n#define CALL_CSI_HANDLER1M(name, defval) \\\n    AT_MOST_ONE_PARAMETER; \\\n    p1 = num_params > 0 ? params[0] : defval; \\\n    NON_NEGATIVE_PARAM(p1); \\\n    REPORT_COMMAND(name, p1, end_modifier); \\\n    name(self->screen, p1, end_modifier); \\\n    break;\n\n#define CALL_CSI_HANDLER2(name, defval1, defval2) \\\n    if (num_params > 2) { \\\n        REPORT_ERROR(\"CSI code %s has %u > 2 parameters\", csi_letter(code), num_params); \\\n        break; \\\n    } \\\n    p1 = num_params > 0 ? params[0] : defval1; \\\n    p2 = num_params > 1 ? params[1] : defval2; \\\n    NON_NEGATIVE_PARAM(p1); \\\n    NON_NEGATIVE_PARAM(p2); \\\n    REPORT_COMMAND(name, p1, p2); \\\n    name(self->screen, p1, p2); \\\n    break;\n\n#define NO_MODIFIERS(modifier, special, special_msg) { \\\n    if (self->csi.primary || self->csi.secondary) { \\\n        if (special && modifier == special) { REPORT_ERROR(special_msg); } \\\n        else { REPORT_ERROR(\"CSI code %s has unsupported start modifier: %s or end modifier: %s\", csi_letter(self->csi.trailer), csi_letter(self->csi.primary), csi_letter(self->csi.secondary));} \\\n        break; \\\n    } \\\n}\n\n    int p1, p2; bool private;\n\n    switch(self->csi.trailer) {\n        case ICH:\n            NO_MODIFIERS(self->csi.secondary, ' ', \"Shift left escape code not implemented\");\n            CALL_CSI_HANDLER1(screen_insert_characters, 1);\n        case REP:\n            CALL_CSI_HANDLER1(screen_repeat_character, 1);\n        case CUU:\n            NO_MODIFIERS(end_modifier, ' ', \"Shift right escape code not implemented\");\n            CALL_CSI_HANDLER1(screen_cursor_up2, 1);\n        case CUD:\n        case VPR:\n            CALL_CSI_HANDLER1(screen_cursor_down, 1);\n        case CUF:\n        case HPR:\n            CALL_CSI_HANDLER1(screen_cursor_forward, 1);\n        case CUB:\n            CALL_CSI_HANDLER1(screen_cursor_back1, 1);\n        case CNL:\n            CALL_CSI_HANDLER1(screen_cursor_down1, 1);\n        case CPL:\n            CALL_CSI_HANDLER1(screen_cursor_up1, 1);\n        case CHA:\n        case HPA:\n            CALL_CSI_HANDLER1(screen_cursor_to_column, 1);\n        case VPA:\n            CALL_CSI_HANDLER1(screen_cursor_to_line, 1);\n        case CBT:\n            CALL_CSI_HANDLER1(screen_backtab, 1);\n        case CHT:\n            CALL_CSI_HANDLER1(screen_tabn, 1);\n        case CUP:\n        case HVP:\n            CALL_CSI_HANDLER2(screen_cursor_position, 1, 1);\n        case ED:\n            CALL_CSI_HANDLER1P(screen_erase_in_display, 0, '?');\n        case EL:\n            CALL_CSI_HANDLER1P(screen_erase_in_line, 0, '?');\n        case IL:\n            CALL_CSI_HANDLER1(screen_insert_lines, 1);\n        case DL:\n            CALL_CSI_HANDLER1(screen_delete_lines, 1);\n        case DCH:\n            if (end_modifier == '#' && !start_modifier) {\n                CALL_CSI_HANDLER1(screen_push_colors, 0);\n            } else {\n                CALL_CSI_HANDLER1(screen_delete_characters, 1);\n            }\n        case 'Q':\n            if (end_modifier == '#' && !start_modifier) { CALL_CSI_HANDLER1(screen_pop_colors, 0); }\n            REPORT_ERROR(\"Unknown CSI Q sequence with start and end modifiers: '%c' '%c' and %u parameters\", start_modifier, end_modifier, num_params);\n            break;\n        case 'R':\n            if (end_modifier == '#' && !start_modifier) {\n                REPORT_COMMAND(screen_report_color_stack);\n                screen_report_color_stack(self->screen);\n                break;\n            }\n            REPORT_ERROR(\"Unknown CSI R sequence with start and end modifiers: '%c' '%c' and %u parameters\", start_modifier, end_modifier, num_params);\n            break;\n        case ECH:\n            CALL_CSI_HANDLER1(screen_erase_characters, 1);\n        case DA:\n            CALL_CSI_HANDLER1S(report_device_attributes, 0);\n        case TBC:\n            CALL_CSI_HANDLER1(screen_clear_tab_stop, 0);\n        case SM:\n            handle_mode(self); break;\n        case RM:\n            handle_mode(self); break;\n        case DSR:\n            CALL_CSI_HANDLER1P(report_device_status, 0, '?');\n        case 's':\n            if (!start_modifier && !end_modifier && !num_params) {\n                REPORT_COMMAND(screen_save_cursor);\n                screen_save_cursor(self->screen);\n                break;\n            } else if (start_modifier == '?' && !end_modifier) {\n                if (!num_params) {\n                    REPORT_COMMAND(screen_save_modes);\n                    screen_save_modes(self->screen);\n                } else handle_mode(self);\n                break;\n            }\n            REPORT_ERROR(\"Unknown CSI s sequence with start and end modifiers: '%c' '%c' and %u parameters\", start_modifier, end_modifier, num_params);\n            break;\n        case 't':\n            if (!num_params) {\n                REPORT_ERROR(\"Unknown CSI t sequence with start and end modifiers: '%c' '%c' and no parameters\", start_modifier, end_modifier);\n                break;\n            }\n            if (start_modifier || end_modifier) {\n                REPORT_ERROR(\"Unknown CSI t sequence with start and end modifiers: '%c' '%c', %u parameters and first parameter: %d\", start_modifier, end_modifier, num_params, params[0]);\n                break;\n            }\n            switch(params[0]) {\n                case 4:\n                case 8:\n                    REPORT_ERROR(\"Escape codes to resize text area are not supported\");\n                    break;\n                case 14:\n                case 16:\n                case 18:\n                    CALL_CSI_HANDLER2(screen_report_size, 0, 0);\n                    break;\n                case 22:\n                case 23:\n                    if (num_params == 3 && !params[2]) num_params = 2; // ignore extra 0, generated by weechat or ncurses\n                    CALL_CSI_HANDLER2(screen_manipulate_title_stack, 22, 0);\n                    break;\n                default:\n                    REPORT_ERROR(\"Unknown CSI t window manipulation sequence with %u parameters and first parameter: %d\", num_params, params[0]);\n                    break;\n            }\n            break;\n        case 'u':\n            if (!start_modifier && !end_modifier && !num_params) {\n                REPORT_COMMAND(screen_restore_cursor);\n                screen_restore_cursor(self->screen);\n                break;\n            }\n            if (!end_modifier && start_modifier == '?') {\n                REPORT_COMMAND(screen_report_key_encoding_flags);\n                screen_report_key_encoding_flags(self->screen);\n                break;\n            }\n            if (!end_modifier && start_modifier == '=') {\n                CALL_CSI_HANDLER2(screen_set_key_encoding_flags, 0, 1);\n                break;\n            }\n            if (!end_modifier && start_modifier == '>') {\n                CALL_CSI_HANDLER1(screen_push_key_encoding_flags, 0);\n                break;\n            }\n            if (!end_modifier && start_modifier == '<') {\n                CALL_CSI_HANDLER1(screen_pop_key_encoding_flags, 1);\n                break;\n            }\n            REPORT_ERROR(\"Unknown CSI u sequence with start and end modifiers: '%c' '%c' and %u parameters\", start_modifier, end_modifier, num_params);\n            break;\n        case 'r':\n            if (!start_modifier && !end_modifier) {\n                // DECSTBM\n                CALL_CSI_HANDLER2(screen_set_margins, 0, 0);\n            } else if (start_modifier == '?' && !end_modifier) {\n                if (!num_params) {\n                    REPORT_COMMAND(screen_restore_modes);\n                    screen_restore_modes(self->screen);\n                } else handle_mode(self);\n                break;\n            } else if (!start_modifier && end_modifier == '$') {\n                _parse_sgr(self, &self->csi);\n                break;\n            }\n            REPORT_ERROR(\"Unknown CSI r sequence with start and end modifiers: '%c' '%c' and %u parameters\", start_modifier, end_modifier, num_params);\n            break;\n        case 'x':\n            if (!start_modifier && end_modifier == '*') {\n                CALL_CSI_HANDLER1(screen_decsace, 0);\n            }\n            REPORT_ERROR(\"Unknown CSI x sequence with start and end modifiers: '%c' '%c'\", start_modifier, end_modifier);\n            break;\n        case DECSCUSR:\n            if (end_modifier == ' ') {\n                if (!start_modifier) { CALL_CSI_HANDLER1M(screen_set_cursor, 1); }\n                if (start_modifier == '>') {\n                    _parse_multi_cursors(self, &self->csi);\n                    break;\n                }\n            } else if (end_modifier == 0 && start_modifier == '>') {\n                CALL_CSI_HANDLER1(screen_xtversion, 0);\n            }\n            REPORT_ERROR(\"Unknown CSI q sequence with start and end modifiers: '%c' '%c'\", start_modifier, end_modifier);\n            break;\n        case SU:\n            NO_MODIFIERS(end_modifier, ' ', \"Select presentation directions escape code not implemented\");\n            CALL_CSI_HANDLER1(screen_scroll, 1);\n        case SD:\n            if (!start_modifier && end_modifier == '+') {\n                CALL_CSI_HANDLER1(screen_reverse_scroll_and_fill_from_scrollback, 1);\n            } else {\n                NO_MODIFIERS(start_modifier, 0, \"\");\n                CALL_CSI_HANDLER1(screen_reverse_scroll, 1);\n            }\n            break;\n        case DECSTR:\n            if (end_modifier == '$') {\n                // DECRQM\n                CALL_CSI_HANDLER1P(report_mode_status, 0, '?');\n            } else {\n                REPORT_ERROR(\"Unknown DECSTR CSI sequence with start and end modifiers: '%c' '%c'\", start_modifier, end_modifier);\n            }\n            break;\n        case 'm':\n            if (!start_modifier && !end_modifier) {\n                _parse_sgr(self, &self->csi);\n                break;\n            }\n            if (start_modifier == '>' && !end_modifier) {\n                CALL_CSI_HANDLER2(screen_modify_other_keys, 0, INT_MAX);\n                break;\n            }\n            /* fallthrough */\n        default:\n            REPORT_ERROR(\"Unknown CSI code: '%c' with start_modifier: '%c' and end_modifier: '%c' and parameters: '%s'\", code, start_modifier, end_modifier, repr_csi_params(params, num_params));\n    }\n#undef num_params\n#undef code\n#undef params\n#undef start_modifier\n#undef end_modifier\n}\n\n// }}}\n\n// APC mode {{{\n\n#include \"parse-graphics-command.h\"\n\nstatic void\ndispatch_apc(PS *self, uint8_t *buf, size_t bufsz, bool is_extended UNUSED) {\n    if (bufsz < 2) return;\n    switch(buf[0]) {\n        case 'G':\n            parse_graphics_code(self, buf, bufsz);\n            break;\n        default:\n            REPORT_ERROR(\"Unrecognized APC code: 0x%x\", buf[0]);\n            break;\n    }\n}\n\n// }}}\n\n// PM mode {{{\nstatic void\ndispatch_pm(PS *self UNUSED, uint8_t *buf, size_t bufsz, bool is_extended UNUSED) {\n    if (bufsz < 2) return;\n    switch(buf[0]) {\n        default:\n            REPORT_ERROR(\"Unrecognized PM code: 0x%x\", buf[0]);\n            break;\n    }\n}\n\n\n// }}}\n\n// SOS mode {{{\nstatic void\ndispatch_sos(PS *self UNUSED, uint8_t *buf, size_t bufsz, bool is_extended UNUSED) {\n    if (bufsz < 2) return;\n    switch(buf[0]) {\n        default:\n            REPORT_ERROR(\"Unrecognized SOS code: 0x%x\", buf[0]);\n            break;\n    }\n}\n\n\n// }}}\n\n// Parse loop {{{\nstatic void\nconsume_input(PS *self, PyObject *dump_callback UNUSED, id_type window_id UNUSED) {\n#define consume(x) if (accumulate_st_terminated_esc_code(self, dispatch_##x)) { self->read.consumed = self->read.pos; SET_STATE(NORMAL); } break;\n\n#ifdef DUMP_COMMANDS\n    PyObject *dumped_bytes = PyBytes_FromStringAndSize((const char*)self->buf + self->read.pos, self->read.sz - self->read.pos);\n    size_t pre_consume_pos = self->read.pos;\n#endif\n\n    switch (self->vte_state) {\n        case VTE_NORMAL:\n            consume_normal(self); self->read.consumed = self->read.pos; break;\n        case VTE_ESC:\n            if (consume_esc(self)) { self->read.consumed = self->read.pos; }\n            break;\n        case VTE_CSI:\n            if (consume_csi(self)) { self->read.consumed = self->read.pos; if (self->csi.is_valid) dispatch_csi(self); SET_STATE(NORMAL); }\n            break;\n        case VTE_OSC:\n            consume(osc);\n        case VTE_APC:\n            consume(apc);\n        case VTE_PM:\n            consume(pm);\n        case VTE_DCS:\n            consume(dcs);\n        case VTE_SOS:\n            consume(sos);\n    }\n\n#ifdef DUMP_COMMANDS\n    if (dumped_bytes && dump_callback && self->read.pos > pre_consume_pos) {\n        if (_PyBytes_Resize(&dumped_bytes, self->read.pos - pre_consume_pos) == 0) {\n            PyObject *ret = PyObject_CallFunction(dump_callback, \"KsO\", window_id, \"bytes\", dumped_bytes);\n            Py_DECREF(dumped_bytes);\n            if (ret) { Py_DECREF(ret); } else { PyErr_Clear(); }\n        }\n    }\n#endif\n\n#undef consume\n}\n\n// }}}\n\n// API {{{\n\n#define with_lock pthread_mutex_lock(&self->lock);\n#define end_with_lock pthread_mutex_unlock(&self->lock);\n\nstatic void\nrun_worker(void *p, ParseData *pd, bool flush) {\n    Screen *screen = (Screen*)p;\n    PS *self = (PS*)screen->vt_parser->state;\n    screen->parsing_at = pd->now;\n    with_lock {\n        self->read.sz += self->write.pending; self->write.pending = 0;\n        pd->has_pending_input = self->read.pos < self->read.sz;\n        if (pd->has_pending_input) {\n            pd->time_since_new_input = pd->now - self->new_input_at;\n            if (flush || pd->time_since_new_input >= OPT(input_delay) || self->read.sz + 16 * 1024 > BUF_SZ) {\n                pd->input_read = true;\n                self->dump_callback = pd->dump_callback; self->now = pd->now;\n                self->screen = screen;\n                self->read.consumed = 0;\n                do {\n                    end_with_lock; {\n                        consume_input(self, pd->dump_callback, screen->window_id);\n                    } with_lock;\n                    self->read.sz += self->write.pending; self->write.pending = 0;\n                } while (self->read.pos < self->read.sz);\n                self->new_input_at = 0;\n                if (self->read.consumed) {\n                    pd->write_space_created = self->read.sz >= BUF_SZ;\n                    self->read.pos -= MIN(self->read.pos, self->read.consumed);\n                    self->read.sz -= MIN(self->read.sz, self->read.consumed);\n                    if (self->read.sz) memmove(self->buf, self->buf + self->read.consumed, self->read.sz);\n                }\n            }\n        }\n    } end_with_lock;\n}\n\n#ifndef DUMP_COMMANDS\n\nuint8_t*\nvt_parser_create_write_buffer(Parser *p, size_t *sz) {\n    PS *self = (PS*)p->state;\n    uint8_t *ans;\n    with_lock {\n        if (self->write.sz) fatal(\"vt_parser_create_write_buffer() called with an already existing write buffer\");\n        self->write.offset = self->read.sz + self->write.pending;\n        *sz = BUF_SZ - self->write.offset;\n        self->write.sz = *sz;\n        ans = self->buf + self->write.offset;\n    } end_with_lock;\n    return ans;\n}\n\nvoid\nvt_parser_commit_write(Parser *p, size_t sz) {\n    PS *self = (PS*)p->state;\n    with_lock {\n        size_t off = self->read.sz + self->write.pending;\n        if (self->new_input_at == 0) self->new_input_at = monotonic();\n        if (self->write.offset > off) memmove(self->buf + off, self->buf + self->write.offset, sz);\n        self->write.pending += sz;\n        self->write.sz = 0;\n    } end_with_lock;\n}\n\nbool\nvt_parser_has_space_for_input(const Parser *p) {\n    PS *self = (PS*)p->state;\n    bool ans;\n    with_lock {\n        ans = self->read.sz + self->write.pending < BUF_SZ;\n    } end_with_lock;\n    return ans;\n}\n#endif\n\n// }}}\n\n// Boilerplate {{{\n\n#ifdef DUMP_COMMANDS\nvoid\nparse_worker_dump(void *p, ParseData *pd, bool flush) { run_worker(p, pd, flush); }\n#else\nvoid\nparse_worker(void *p, ParseData *pd, bool flush) { run_worker(p, pd, flush); }\n#endif\n\n#ifndef DUMP_COMMANDS\nstatic PyObject*\nnew_vtparser_object(PyTypeObject *type UNUSED, PyObject *args, PyObject UNUSED *kwds) {\n    id_type window_id=0;\n    if (!PyArg_ParseTuple(args, \"|K\", &window_id)) return NULL;\n    return (PyObject*) alloc_vt_parser(window_id);\n}\n\nvoid\nfree_vt_parser(Parser* self) {\n    if (self->state) {\n        PS *s = (PS*)self->state;\n        utf8_decoder_free(&s->utf8_decoder);\n        pthread_mutex_destroy(&s->lock);\n        free(self->state); self->state = NULL;\n    }\n    Py_TYPE(self)->tp_free((PyObject*)self);\n}\n\nstatic void\nreset(PS *self) {\n    SET_STATE(NORMAL);\n    reset_csi(&self->csi);\n    utf8_decoder_reset(&self->utf8_decoder);\n}\n\nvoid\nreset_vt_parser(Parser *self) {\n    reset((PS*)self->state);\n}\n\nextern PyTypeObject Screen_Type;\n\nstatic PyObject*\ncurrent_state(Parser *self, PyObject *closure UNUSED) {\n    PS *state = (PS*)self->state;\n    return PyUnicode_FromString(vte_state_name(state->vte_state));\n}\n\nstatic PyGetSetDef getsetters[] = {\n    {\"vte_state\", (getter)current_state, NULL, \"The VTE parser state\", NULL},\n    {NULL}  /* Sentinel */\n};\n\n\nstatic PyMethodDef methods[] = {\n    {NULL},\n};\n\nPyTypeObject Parser_Type = {\n    PyVarObject_HEAD_INIT(NULL, 0)\n    .tp_name = \"fast_data_types.Parser\",\n    .tp_basicsize = sizeof(Parser),\n    .tp_dealloc = (destructor)free_vt_parser,\n    .tp_flags = Py_TPFLAGS_DEFAULT,\n    .tp_doc = \"VT Escape code parser\",\n    .tp_methods = methods,\n    .tp_getset = getsetters,\n    .tp_new = new_vtparser_object,\n};\n\nParser*\nalloc_vt_parser(id_type window_id) {\n    Parser *self = (Parser*)Parser_Type.tp_alloc(&Parser_Type, 1);\n    if (self != NULL) {\n        int ret;\n        if ((ret = posix_memalign((void**)&self->state, BUF_EXTRA, sizeof(PS))) != 0) {\n            Py_CLEAR(self);\n            PyErr_Format(PyExc_RuntimeError, \"Failed to call posix_memalign: %s\", strerror(ret));\n            return NULL;\n        }\n        memset(self->state, 0, sizeof(PS));\n        PS *state = (PS*)self->state;\n        if ((intptr_t)state->buf % BUF_EXTRA != 0) {\n            Py_CLEAR(self); PyErr_SetString(PyExc_TypeError, \"PS->buf is not aligned\");\n            return NULL;\n        }\n        if ((ret = pthread_mutex_init(&state->lock, NULL)) != 0) {\n            Py_CLEAR(self); PyErr_Format(PyExc_RuntimeError, \"Failed to create Parser lock mutex: %s\", strerror(ret));\n            return NULL;\n        }\n        state->window_id = window_id;\n        utf8_decoder_reset(&state->utf8_decoder);\n        reset_csi(&state->csi);\n    }\n    return self;\n}\n\n#undef EXTRA_INIT\n#define EXTRA_INIT \\\n    if (0 != PyModule_AddIntConstant(module, \"VT_PARSER_BUFFER_SIZE\", BUF_SZ)) return 0; \\\n    if (0 != PyModule_AddIntConstant(module, \"VT_PARSER_MAX_ESCAPE_CODE_SIZE\", MAX_ESCAPE_CODE_LENGTH)) return 0; \\\n    if (!init_simd(module)) return 0; \\\n\nINIT_TYPE(Parser)\n\n#endif\n// }}}\n"
  },
  {
    "path": "kitty/vt-parser.h",
    "content": "/*\n * Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"data-types.h\"\n\ntypedef struct { int x; } PARSER_STATE_HANDLE;\n\ntypedef struct Parser {\n    PyObject_HEAD\n\n    PARSER_STATE_HANDLE *state;\n} Parser;\n\ntypedef struct ParseData {\n    PyObject *dump_callback;\n    monotonic_t now;\n\n    bool input_read, write_space_created, has_pending_input;\n    monotonic_t time_since_new_input;\n} ParseData;\n\n// The must only be called on the main thread\nParser* alloc_vt_parser(id_type window_id);\nvoid free_vt_parser(Parser*);\nvoid reset_vt_parser(Parser*);\n\n\n// The following are thread safe, using an internal lock\nuint8_t* vt_parser_create_write_buffer(Parser*, size_t*);\nvoid vt_parser_commit_write(Parser*, size_t);\nbool vt_parser_has_space_for_input(const Parser*);\nvoid parse_worker(void *p, ParseData *data, bool flush);\nvoid parse_worker_dump(void *p, ParseData *data, bool flush);\n"
  },
  {
    "path": "kitty/wcswidth.c",
    "content": "/*\n * wcswidth.c\n * Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#include \"char-props.h\"\n#include \"wcswidth.h\"\n\nvoid\ninitialize_wcs_state(WCSState *state) {\n    zero_at_ptr(state);\n}\n\nint\nwcswidth_step(WCSState *state, const char_type ch) {\n    int ans = 0;\n    switch (state->parser_state) {\n        case IN_CSI: {\n            state->prev_width = 0;\n            if (0x40 <= ch && ch <= 0x7e) { state->parser_state = NORMAL; state->can_combine = false; }\n        } break;\n        case IN_ST_TERMINATED: {\n            state->prev_width = 0;\n            if (ch == '\\a' || (ch == '\\\\' && state->prev_ch == 0x1b)) { state->parser_state = NORMAL; state->can_combine = false; }\n        } break;\n\n        case NORMAL: {\n            CharProps cp = char_props_for(ch);\n            state->seg = grapheme_segmentation_step(state->seg, cp);\n            if (state->seg.add_to_current_cell && state->can_combine) {\n                switch(ch) {\n                    case 0xfe0f:\n                        if (char_props_for(state->prev_ch).is_emoji_presentation_base && state->prev_width == 1) {\n                            ans = 1; state->prev_width = 2;\n                        } else state->prev_width = 0;\n                        break;\n                    case 0xfe0e:\n                        if (char_props_for(state->prev_ch).is_emoji_presentation_base && state->prev_width == 2) {\n                            ans = -1; state->prev_width = 1;\n                        } else state->prev_width = 0;\n                        break;\n                }\n                break;\n            }\n            int width = wcwidth_std(cp);\n            switch (width) {\n                case -1: case 0:\n                    state->prev_width = 0;\n                    if (ch == 0x1b) state->parser_state = IN_ESC;\n                    break;\n                case 2:\n                    state->prev_width = 2; break;\n                default:\n                    state->prev_width = 1; break;\n            }\n            ans = state->prev_width;\n            state->can_combine = true;\n        } break;  // case NORMAL\n\n        case IN_ESC:\n            switch (ch) {\n                case '[':\n                    state->parser_state = IN_CSI; break;\n                case 'P':\n                case ']':\n                case 'X':\n                case '^':\n                case '_':\n                    state->parser_state = IN_ST_TERMINATED; break;\n                case 'D':\n                case 'E':\n                case 'H':\n                case 'M':\n                case 'N':\n                case 'O':\n                case 'Z':\n                case '6':\n                case '7':\n                case '8':\n                case '9':\n                case '=':\n                case '>':\n                case 'F':\n                case 'c':\n                case 'l':\n                case 'm':\n                case 'n':\n                case 'o':\n                case '|':\n                case '}':\n                case '~':\n                    break;\n                default:\n                    zero_at_ptr(state);\n                    return wcswidth_step(state, ch);\n            } break;\n    }\n    state->prev_ch = ch;\n    return ans;\n}\n\nsize_t\nwcswidth_string(const char_type *s) {\n    WCSState state;\n    initialize_wcs_state(&state);\n    size_t ans = 0;\n    while (*s) ans += wcswidth_step(&state, *(s++));\n    return ans;\n}\n\nPyObject *\nwcswidth_std(PyObject UNUSED *self, PyObject *str) {\n    if (PyUnicode_READY(str) != 0) return NULL;\n    int kind = PyUnicode_KIND(str);\n    void *data = PyUnicode_DATA(str);\n    Py_ssize_t len = PyUnicode_GET_LENGTH(str), i;\n    WCSState state;\n    initialize_wcs_state(&state);\n    size_t ans = 0;\n    for (i = 0; i < len; i++) {\n        char_type ch = PyUnicode_READ(kind, data, i);\n        ans += wcswidth_step(&state, ch);\n    }\n    return PyLong_FromSize_t(ans);\n}\n"
  },
  {
    "path": "kitty/wcswidth.h",
    "content": "/*\n * Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include \"char-props.h\"\n\ntypedef enum {NORMAL, IN_ESC, IN_CSI, IN_ST_TERMINATED} WCSParserState;\n\ntypedef struct {\n    char_type prev_ch;\n    int prev_width;\n    WCSParserState parser_state;\n    bool can_combine;\n    GraphemeSegmentationResult seg;\n} WCSState;\n\n\nvoid initialize_wcs_state(WCSState *state);\nint wcswidth_step(WCSState *state, const char_type ch);\nPyObject * wcswidth_std(PyObject UNUSED *self, PyObject *str);\nsize_t wcswidth_string(const char_type *s);\n"
  },
  {
    "path": "kitty/window.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport os\nimport re\nimport sys\nimport weakref\nfrom collections import deque\nfrom collections.abc import Callable, Generator, Iterable, Sequence\nfrom contextlib import contextmanager, suppress\nfrom enum import Enum, IntEnum, auto\nfrom functools import lru_cache, partial\nfrom gettext import gettext as _\nfrom itertools import chain\nfrom re import Pattern\nfrom time import time_ns\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Deque,\n    Iterator,\n    Literal,\n    NamedTuple,\n    Optional,\n    Union,\n)\n\nfrom .child import ProcessDesc\nfrom .cli_stub import CLIOptions, SaveAsSessionOptions\nfrom .clipboard import ClipboardRequestManager, set_clipboard_string\nfrom .constants import (\n    appname,\n    clear_handled_signals,\n    config_dir,\n    kitten_exe,\n    unserialize_launch_flag,\n    wakeup_io_loop,\n)\nfrom .fast_data_types import (\n    CURSOR_BEAM,\n    CURSOR_BLOCK,\n    CURSOR_UNDERLINE,\n    ESC_CSI,\n    ESC_DCS,\n    ESC_OSC,\n    GLFW_MOD_CONTROL,\n    GLFW_PRESS,\n    GLFW_RELEASE,\n    GLFW_REPEAT,\n    NO_CURSOR_SHAPE,\n    SCROLL_FULL,\n    SCROLL_LINE,\n    SCROLL_PAGE,\n    Color,\n    ColorProfile,\n    KeyEvent,\n    Screen,\n    add_timer,\n    add_window,\n    base64_decode,\n    buffer_keys_in_window,\n    cell_size_for_window,\n    click_mouse_cmd_output,\n    click_mouse_url,\n    current_focused_os_window_id,\n    encode_key_for_tty,\n    get_boss,\n    get_click_interval,\n    get_mouse_data_for_window,\n    get_options,\n    get_window_logo_settings_if_not_default,\n    is_css_pointer_name_valid,\n    is_modifier_key,\n    last_focused_os_window_id,\n    mark_os_window_dirty,\n    monotonic,\n    mouse_selection,\n    move_cursor_to_mouse_if_in_prompt,\n    pointer_name_to_css_name,\n    pt_to_px,\n    replace_c0_codes_except_nl_space_tab,\n    set_redirect_keys_to_overlay,\n    set_window_logo,\n    set_window_padding,\n    set_window_render_data,\n    set_window_title_bar_render_data,\n    update_ime_position_for_window,\n    update_pointer_shape,\n    update_window_title,\n    update_window_visibility,\n    wakeup_main_loop,\n)\nfrom .keys import keyboard_mode_name, mod_mask\nfrom .notifications import NotificationManager\nfrom .options.types import Options\nfrom .progress import Progress\nfrom .rgb import to_color\nfrom .terminfo import get_capabilities\nfrom .types import MouseEvent, NeighborsMap, OverlayType, WindowGeometry, ac, run_once\nfrom .typing_compat import BossType, ChildType, EdgeLiteral, TabType, TypedDict\nfrom .utils import (\n    color_as_int,\n    docs_url,\n    key_val_matcher,\n    kitty_ansi_sanitizer_pat,\n    log_error,\n    open_cmd,\n    open_url,\n    parse_uri_list,\n    path_from_osc7_url,\n    resolve_custom_file,\n    resolved_shell,\n    sanitize_control_codes,\n    sanitize_for_bracketed_paste,\n    sanitize_title,\n    sanitize_url_for_display_to_user,\n    shlex_split,\n)\n\nMatchPatternType = Union[Pattern[str], tuple[Pattern[str], Optional[Pattern[str]]]]\n\n\nif TYPE_CHECKING:\n    from kittens.tui.handler import OpenUrlHandler\n\n    from .fast_data_types import MousePosition\n    from .file_transmission import FileTransmission\n    from .notifications import OnlyWhen\n\n\nclass CwdRequestType(Enum):\n    current = auto()\n    last_reported = auto()\n    oldest = auto()\n    root = auto()\n\n\nclass CwdRequest:\n\n    def __init__(self, window: Optional['Window'] = None, request_type: CwdRequestType = CwdRequestType.current) -> None:\n        self.window_id = -1 if window is None else window.id\n        self.request_type = request_type\n\n    def __bool__(self) -> bool:\n        return self.window_id > -1\n\n    @property\n    def window(self) -> Optional['Window']:\n        return get_boss().window_id_map.get(self.window_id)\n\n    @property\n    def cwd_of_child(self) -> str:\n        window = self.window\n        if not window:\n            return ''\n        reported_cwd = path_from_osc7_url(window.screen.last_reported_cwd) if window.screen.last_reported_cwd else ''\n        if reported_cwd and not window.child_is_remote and (self.request_type is CwdRequestType.last_reported or window.at_prompt):\n            return reported_cwd\n        if self.request_type is CwdRequestType.root:\n            return window.get_cwd_of_root_child() or ''\n        return window.get_cwd_of_child(oldest=self.request_type is CwdRequestType.oldest) or ''\n\n    def modify_argv_for_launch_with_cwd(self, argv: list[str], env: dict[str, str] | None=None, hold_after_ssh: bool = False) -> str:\n        window = self.window\n        if not window:\n            return ''\n        reported_cwd = path_from_osc7_url(window.screen.last_reported_cwd) if window.screen.last_reported_cwd else ''\n        if reported_cwd and (self.request_type is not CwdRequestType.root or window.root_in_foreground_processes):\n            ssh_kitten_cmdline = window.ssh_kitten_cmdline()\n            if ssh_kitten_cmdline:\n                run_shell = argv[0] == resolved_shell(get_options())[0]\n                server_args = [] if run_shell else list(argv)\n                from kittens.ssh.utils import set_cwd_in_cmdline, set_env_in_cmdline, set_server_args_in_cmdline\n                if ssh_kitten_cmdline and ssh_kitten_cmdline[0] == 'kitten':\n                    ssh_kitten_cmdline[0] = kitten_exe()\n                argv[:] = ssh_kitten_cmdline\n                set_cwd_in_cmdline(reported_cwd, argv)\n                set_server_args_in_cmdline(server_args, argv, allocate_tty=not run_shell)\n                if hold_after_ssh:\n                    argv[:0] = [kitten_exe(), \"run-shell\"]\n                if env is not None:\n                    # Assume env is coming from a local process so drop env\n                    # vars that can cause issues when set on the remote host\n                    if env.get('KITTY_KITTEN_RUN_MODULE') == 'ssh_askpass':\n                        for k in ('KITTY_KITTEN_RUN_MODULE', 'SSH_ASKPASS', 'SSH_ASKPASS_REQUIRE'):\n                            env.pop(k, None)\n                    for k in (\n                        'HOME', 'USER', 'TEMP', 'TMP', 'TMPDIR', 'PATH', 'PWD', 'OLDPWD', 'KITTY_INSTALLATION_DIR',\n                        'HOSTNAME', 'SSH_AUTH_SOCK', 'SSH_AGENT_PID', 'KITTY_STDIO_FORWARDED',\n                        'KITTY_PUBLIC_KEY', 'TERMINFO', 'XDG_RUNTIME_DIR', 'XDG_VTNR',\n                        'XDG_DATA_DIRS', 'XAUTHORITY', 'EDITOR', 'VISUAL',\n                    ):\n                        env.pop(k, None)\n                    set_env_in_cmdline(env, argv, clone=False)\n                return ''\n            if not window.child_is_remote and (self.request_type is CwdRequestType.last_reported or window.at_prompt):\n                return reported_cwd\n        return window.get_cwd_of_child(oldest=self.request_type is CwdRequestType.oldest) or ''\n\n\ndef process_title_from_child(title: memoryview, is_base64: bool, default_title: str) -> str:\n    if is_base64:\n        try:\n            stitle = base64_decode(title).decode('utf-8', 'replace')\n        except Exception:\n            stitle = 'undecodeable title'\n    else:\n        stitle = str(title, 'utf-8', 'replace')\n    return sanitize_title(stitle or default_title)\n\n\n@lru_cache(maxsize=64)\ndef compile_match_query(exp: str, is_simple: bool = True) -> MatchPatternType:\n    if is_simple:\n        pat: MatchPatternType = re.compile(exp)\n    else:\n        kp, vp = exp.partition('=')[::2]\n        if vp:\n            pat = re.compile(kp), re.compile(vp)\n        else:\n            pat = re.compile(kp), None\n    return pat\n\n\ndef decode_cmdline(x: str) -> str:\n    ctype, sep, val = x.partition('=')\n    if ctype == 'cmdline':\n        return next(shlex_split(val, True))\n    elif ctype == 'cmdline_url':\n        from urllib.parse import unquote\n        return unquote(val)\n    return ''\n\n\nclass WindowDict(TypedDict):\n    id: int\n    is_focused: bool\n    is_active: bool\n    title: str\n    title_overridden: bool\n    pid: int | None\n    cwd: str\n    cmdline: list[str]\n    last_reported_cmdline: str\n    last_cmd_exit_status: int\n    env: dict[str, str]\n    foreground_processes: list[ProcessDesc]\n    is_self: bool\n    lines: int\n    columns: int\n    user_vars: dict[str, str]\n    at_prompt: bool\n    created_at: int\n    in_alternate_screen: bool\n    neighbors: NeighborsMap\n\n\nclass PipeData(TypedDict):\n    input_line_number: int\n    scrolled_by: int\n    cursor_x: int\n    cursor_y: int\n    lines: int\n    columns: int\n    text: str\n\n\nclass ClipboardPending(NamedTuple):\n    where: str\n    data: str\n    truncated: bool = False\n\n\nclass DynamicColor(IntEnum):\n    default_fg, default_bg, cursor_color, highlight_fg, highlight_bg = range(1, 6)\n\n\nclass CommandOutput(IntEnum):\n    last_run, first_on_screen, last_visited, last_non_empty = 0, 1, 2, 3\n\n\nDYNAMIC_COLOR_CODES = {\n    10: DynamicColor.default_fg,\n    11: DynamicColor.default_bg,\n    12: DynamicColor.cursor_color,\n    17: DynamicColor.highlight_bg,\n    19: DynamicColor.highlight_fg,\n}\nDYNAMIC_COLOR_CODES.update({k+100: v for k, v in DYNAMIC_COLOR_CODES.items()})\n\n\nclass Watcher:\n\n    def __call__(self, boss: BossType, window: 'Window', data: dict[str, Any]) -> None:\n        pass\n\n\nclass Watchers:\n\n    on_resize: list[Watcher]\n    on_close: list[Watcher]\n    on_focus_change: list[Watcher]\n    on_set_user_var: list[Watcher]\n    on_title_change: list[Watcher]\n    on_cmd_startstop: list[Watcher]\n    on_color_scheme_preference_change: list[Watcher]\n    on_tab_bar_dirty: list[Watcher]\n    on_quit: list[Watcher]\n\n    def __init__(self) -> None:\n        self.on_resize = []\n        self.on_close = []\n        self.on_focus_change = []\n        self.on_set_user_var = []\n        self.on_title_change = []\n        self.on_cmd_startstop = []\n        self.on_color_scheme_preference_change = []\n        self.on_tab_bar_dirty = []\n        self.on_quit = []\n\n    def add(self, others: 'Watchers') -> None:\n        def merge(base: list[Watcher], other: list[Watcher]) -> None:\n            for x in other:\n                if x not in base:\n                    base.append(x)\n        merge(self.on_resize, others.on_resize)\n        merge(self.on_close, others.on_close)\n        merge(self.on_focus_change, others.on_focus_change)\n        merge(self.on_set_user_var, others.on_set_user_var)\n        merge(self.on_title_change, others.on_title_change)\n        merge(self.on_cmd_startstop, others.on_cmd_startstop)\n        merge(self.on_color_scheme_preference_change, others.on_color_scheme_preference_change)\n        merge(self.on_tab_bar_dirty, others.on_tab_bar_dirty)\n        merge(self.on_quit, others.on_quit)\n\n    def clear(self) -> None:\n        del self.on_close[:], self.on_resize[:], self.on_focus_change[:]\n        del self.on_set_user_var[:], self.on_title_change[:], self.on_cmd_startstop[:]\n        del self.on_color_scheme_preference_change[:]\n        del self.on_tab_bar_dirty[:]\n        del self.on_quit[:]\n\n    def copy(self) -> 'Watchers':\n        ans = Watchers()\n        ans.on_close = self.on_close[:]\n        ans.on_resize = self.on_resize[:]\n        ans.on_focus_change = self.on_focus_change[:]\n        ans.on_set_user_var = self.on_set_user_var[:]\n        ans.on_title_change = self.on_title_change[:]\n        ans.on_cmd_startstop = self.on_cmd_startstop[:]\n        ans.on_color_scheme_preference_change = self.on_color_scheme_preference_change[:]\n        ans.on_tab_bar_dirty = self.on_tab_bar_dirty[:]\n        ans.on_quit = self.on_quit[:]\n        return ans\n\n    @property\n    def has_watchers(self) -> bool:\n        return bool(self.on_close or self.on_resize or self.on_focus_change or self.on_color_scheme_preference_change\n                    or self.on_set_user_var or self.on_title_change or self.on_cmd_startstop or self.on_tab_bar_dirty\n                    or self.on_quit)\n\n\ndef call_watchers(windowref: Callable[[], Optional['Window']], which: str, data: dict[str, Any]) -> None:\n\n    def callback(timer_id: int | None) -> None:\n        w = windowref()\n        if w is not None:\n            watchers: list[Watcher] = getattr(w.watchers, which)\n            w.call_watchers(watchers, data)\n\n    add_timer(callback, 0, False)\n\n\nclass WindowCreationSpec(NamedTuple):\n    use_shell: bool = True\n    cmd: list[str] | None = None\n    has_stdin: bool = False\n    override_title: str | None = None\n    cwd_from: CwdRequest | None = None\n    cwd: str | None = None\n    overlay_for: int | None = None\n    env: tuple[tuple[str, str], ...] | None = None\n    location: str | None = None\n    copy_colors_from: int | None = None\n    colors: tuple[str, ...] = ()\n    allow_remote_control: bool = False\n    marker: str | None = None\n    watchers: tuple[str, ...] = ()\n    overlay_behind: bool = False\n    is_clone_launch: str = ''\n    remote_control_passwords: dict[str, Sequence[str]] | None = None\n    hold: bool = False\n    bias: float | None = None\n    hold_after_ssh: bool = False\n    spacing: tuple[str, ...] = ()\n    user_vars: tuple[tuple[str, str], ...] = ()\n\n\ndef pagerhist(screen: Screen, as_ansi: bool = False, add_wrap_markers: bool = True, upto_output_start: bool = False) -> str:\n    pht = screen.historybuf.pagerhist_as_text(upto_output_start)\n    if pht and (not as_ansi or not add_wrap_markers):\n        sanitizer = text_sanitizer(as_ansi, add_wrap_markers)\n        pht = sanitizer(pht)\n    return pht\n\n\ndef as_text(\n    screen: Screen,\n    as_ansi: bool = False,\n    add_history: bool = False,\n    add_wrap_markers: bool = False,\n    alternate_screen: bool = False,\n    add_cursor: bool = False\n) -> str:\n    lines: list[str] = []\n    add_history = add_history and not (screen.is_using_alternate_linebuf() ^ alternate_screen)\n    if alternate_screen:\n        f = screen.as_text_alternate\n    else:\n        f = screen.as_text_non_visual if add_history else screen.as_text\n    f(lines.append, as_ansi, add_wrap_markers)\n    ctext = ''\n    if add_cursor:\n        ctext += '\\x1b[?25' + ('h' if screen.cursor_visible else 'l')\n        ctext += f'\\x1b[{screen.cursor.y + 1};{screen.cursor.x + 1}H'\n        shape = screen.cursor.shape\n        if shape == NO_CURSOR_SHAPE:\n            ctext += '\\x1b[?12' + ('h' if screen.cursor.blink else 'l')\n        else:\n            code = {CURSOR_BLOCK: 1, CURSOR_UNDERLINE: 3, CURSOR_BEAM: 5}[shape]\n            if not screen.cursor.blink:\n                code += 1\n            ctext += f'\\x1b[{code} q'\n\n    if add_history:\n        pht = pagerhist(screen, as_ansi, add_wrap_markers)\n        h: list[str] = [pht] if pht else []\n        screen.as_text_for_history_buf(h.append, as_ansi, add_wrap_markers)\n        if h:\n            if as_ansi:\n                h[-1] += '\\x1b[m'\n        ans = ''.join(chain(h, lines))\n        if ctext:\n            ans += ctext\n        return ans\n    ans = ''.join(lines)\n    if ctext:\n        ans += ctext\n    return ans\n\n\n\n@run_once\ndef load_paste_filter() -> Callable[[str], str]:\n    import runpy\n    import traceback\n    try:\n        m = runpy.run_path(os.path.join(config_dir, 'paste-actions.py'))\n        func: Callable[[str], str] = m['filter_paste']\n    except Exception as e:\n        if not isinstance(e, FileNotFoundError):\n            traceback.print_exc()\n            log_error(f'Failed to load paste filter function with error: {e}')\n\n        def func(text: str) -> str:\n            return text\n    return func\n\n\ndef text_sanitizer(as_ansi: bool, add_wrap_markers: bool) -> Callable[[str], str]:\n    pat = kitty_ansi_sanitizer_pat()\n    ansi, wrap_markers = not as_ansi, not add_wrap_markers\n\n    def remove_wrap_markers(line: str) -> str:\n        return line.replace('\\r', '')\n\n    def remove_sgr(line: str) -> str:\n        return str(pat.sub('', line))\n\n    def remove_both(line: str) -> str:\n        return str(pat.sub('', line.replace('\\r', '')))\n\n    if ansi:\n        return remove_both if wrap_markers else remove_sgr\n    return remove_wrap_markers\n\n\ndef cmd_output(screen: Screen, which: CommandOutput = CommandOutput.last_run, as_ansi: bool = False, add_wrap_markers: bool = False) -> str:\n    lines: list[str] = []\n    search_in_pager_hist = screen.cmd_output(which, lines.append, as_ansi, add_wrap_markers)\n    if search_in_pager_hist:\n        pht = pagerhist(screen, as_ansi, add_wrap_markers, True)\n        if pht:\n            lines.insert(0, pht)\n    for i in range(min(len(lines), 3)):\n        x = lines[i]\n        if x.startswith('\\x1b]133;C'):\n            lines[i] = x.partition('\\\\')[-1]\n    return ''.join(lines)\n\n\ndef process_remote_print(msg: memoryview) -> str:\n    return replace_c0_codes_except_nl_space_tab(base64_decode(msg)).decode('utf-8', 'replace')\n\n\ndef transparent_background_color_control(cp: ColorProfile, responses: dict[str, str], index: int, key: str, sep: str, val: str) -> None:\n    if sep == '=':\n        if val == '?':\n            if index > 8:\n                responses[key] = '?'\n            else:\n                c = cp.get_transparent_background_color(index - 1)\n                if c is None:\n                    responses[key] = ''\n                else:\n                    opacity = max(0, min(c.alpha / 255.0, 1))\n                    responses[key] = f'rgb:{c.red:02x}/{c.green:02x}/{c.blue:02x}@{opacity:.4f}'\n        elif index <= 8:\n            col, _, o = val.partition('@')\n            try:\n                opacity = float(o)\n            except Exception:\n                opacity = -1.0\n            c = to_color(col)\n            if c is not None:\n                cp.set_transparent_background_color(index - 1, c, opacity)\n    elif index <= 8:\n        cp.set_transparent_background_color(index - 1)\n\n\ndef color_control(cp: ColorProfile, code: int, value: str | bytes | memoryview = '') -> str:\n    if isinstance(value, (bytes, memoryview)):\n        value = str(value, 'utf-8', 'replace')\n    responses: dict[str, str] = {}\n    for rec in value.split(';'):\n        key, sep, val = rec.partition('=')\n        if key.startswith('transparent_background_color'):\n            index = int(key[len('transparent_background_color'):])\n            transparent_background_color_control(cp, responses, index, key, sep, val)\n            continue\n        attr = {\n            'foreground': 'default_fg', 'background': 'default_bg',\n            'selection_background': 'highlight_bg', 'selection_foreground': 'highlight_fg',\n            'cursor': 'cursor_color', 'cursor_text': 'cursor_text_color',\n            'visual_bell': 'visual_bell_color',\n        }.get(key, '')\n        colnum = -1\n        with suppress(Exception):\n            colnum = int(key)\n\n        def serialize_color(c: Color | None) -> str:\n            return '' if c is None else f'rgb:{c.red:02x}/{c.green:02x}/{c.blue:02x}'\n\n        if sep == '=':\n            if val == '?':\n                if attr:\n                    c = getattr(cp, attr)\n                    responses[key] = serialize_color(c)\n                else:\n                    if 0 <= colnum <= 255:\n                        c = cp.as_color((colnum << 8) | 1)\n                        responses[key] = serialize_color(c)\n                    else:\n                        responses[key] = '?'\n            else:\n                if attr:\n                    if val:\n                        val = val.partition('@')[0]\n                        col = to_color(val)\n                        if col is not None:\n                            setattr(cp, attr, col)\n                    else:\n                        with suppress(TypeError):\n                            setattr(cp, attr, None)\n                else:\n                    if 0 <= colnum <= 255:\n                        val = val.partition('@')[0]\n                        col = to_color(val)\n                        if col is not None:\n                            cp.set_color(colnum, color_as_int(col))\n        else:\n            if attr:\n                delattr(cp, attr)\n            else:\n                if 0 <= colnum <= 255:\n                    cp.set_color(colnum, get_options().color_table[colnum])\n    if responses:\n        payload = ';'.join(f'{k}={v}' for k, v in responses.items())\n        return f'{code};{payload}'\n    return ''\n\n\ndef da1(opts: Options) -> str:\n    ans = '?62;'\n    if 'write-clipboard' in opts.clipboard_control:\n        # see https://github.com/contour-terminal/vt-extensions/blob/master/clipboard-extension.md\n        ans += '52;'\n    return ans + 'c'\n\n\nclass EdgeWidths:\n    left: float | None\n    top: float | None\n    right: float | None\n    bottom: float | None\n\n    def __init__(self, serialized: dict[str, float | None] | None = None):\n        if serialized is not None:\n            self.left = serialized['left']\n            self.right = serialized['right']\n            self.top = serialized['top']\n            self.bottom = serialized['bottom']\n        else:\n            self.left = self.top = self.right = self.bottom = None\n\n    def serialize(self) -> dict[str, float | None]:\n        return {'left': self.left, 'right': self.right, 'top': self.top, 'bottom': self.bottom}\n\n    def copy(self) -> 'EdgeWidths':\n        return EdgeWidths(self.serialize())\n\n    def as_launch_args(self, prefix: str = 'padding') -> Iterator[str]:\n        if self.left is not None:\n            yield f'--spacing={prefix}-left={self.left}'\n        if self.right is not None:\n            yield f'--spacing={prefix}-left={self.right}'\n        if self.top is not None:\n            yield f'--spacing={prefix}-left={self.top}'\n        if self.bottom is not None:\n            yield f'--spacing={prefix}-left={self.bottom}'\n\n\nclass GlobalWatchers:\n\n    def __init__(self) -> None:\n        self.options_spec: dict[str, str] | None = None\n        self.ans = Watchers()\n        self.extra = ''\n\n    def __call__(self) -> Watchers:\n        spec = get_options().watcher\n        if spec == self.options_spec:\n            return self.ans\n        from .launch import load_watch_modules\n        if self.extra:\n            spec = spec.copy()\n            spec[self.extra] = self.extra\n        self.ans = load_watch_modules(spec.keys()) or self.ans\n        self.options_spec = spec.copy()\n        return self.ans\n\n    def set_extra(self, extra: str) -> None:\n        self.extra = extra\n\n\nglobal_watchers = GlobalWatchers()\n\n\nclass Window:\n\n    window_custom_type: str = ''\n    overlay_type = OverlayType.transient\n    initial_ignore_focus_changes: bool = False\n    initial_ignore_focus_changes_context_manager_in_operation: bool = False\n    creation_spec: WindowCreationSpec | None = None\n    created_in_session_name: str = ''\n    serialized_id: int = 0\n    show_title_bar: bool = False  # must be set before calling set_geometry\n\n    @classmethod\n    @contextmanager\n    def set_ignore_focus_changes_for_new_windows(cls, value: bool = True) -> Generator[None, None, None]:\n        if cls.initial_ignore_focus_changes_context_manager_in_operation:\n            yield\n        else:\n            orig, cls.initial_ignore_focus_changes = cls.initial_ignore_focus_changes, value\n            cls.initial_ignore_focus_changes_context_manager_in_operation = True\n            try:\n                yield\n            finally:\n                cls.initial_ignore_focus_changes = orig\n                cls.initial_ignore_focus_changes_context_manager_in_operation = False\n\n    def __init__(\n        self,\n        tab: TabType,\n        child: ChildType,\n        args: CLIOptions,\n        override_title: str | None = None,\n        copy_colors_from: Optional['Window'] = None,\n        watchers: Watchers | None = None,\n        allow_remote_control: bool = False,\n        remote_control_passwords: dict[str, Sequence[str]] | None = None,\n    ):\n        if watchers:\n            self.watchers = watchers\n            self.watchers.add(global_watchers())\n        else:\n            self.watchers = global_watchers().copy()\n        self.keys_redirected_till_ready_from: int = 0\n        self.last_focused_at = 0.\n        self.is_focused: bool = False\n        self.progress = Progress()\n        self.clear_progress_timer: int = 0\n        self.last_resized_at = 0.\n        self.started_at = monotonic()\n        self.created_at = time_ns()\n        self.current_remote_data: list[str] = []\n        self.current_mouse_event_button = 0\n        self.current_clipboard_read_ask: bool | None = None\n        self.last_cmd_output_start_time = 0.\n        self.last_cmd_end_notification: tuple[int, 'OnlyWhen'] | None = None\n        self.open_url_handler: 'OpenUrlHandler' = None\n        self.last_cmd_cmdline = ''\n        self.last_cmd_exit_status = 0\n        self.actions_on_close: list[Callable[['Window'], None]] = []\n        self.actions_on_focus_change: list[Callable[['Window', bool], None]] = []\n        self.actions_on_removal: list[Callable[['Window'], None]] = []\n        self.current_marker_spec: tuple[str, str | tuple[tuple[int, str], ...]] | None = None\n        self.kitten_result_processors: list[Callable[['Window', Any], None]] = []\n        self.child_is_launched = False\n        self.last_reported_pty_size = (-1, -1, -1, -1)\n        self._pause_resize_notifications_to_child: tuple[int, int, int, int] | None = None\n        self.needs_attention = False\n        self.ignore_focus_changes = self.initial_ignore_focus_changes\n        self.override_title = override_title\n        self.default_title = os.path.basename(child.argv[0] or appname)\n        self.child_title = self.default_title\n        self.title_stack: Deque[str] = deque(maxlen=10)\n        self.user_vars: dict[str, str] = {}\n        self.id: int = add_window(tab.os_window_id, tab.id, self.title)\n        if not self.id:\n            raise Exception(f'No tab with id: {tab.id} in OS Window: {tab.os_window_id} was found, or the window counter wrapped')\n        self.clipboard_request_manager = ClipboardRequestManager(self.id)\n        self.margin = EdgeWidths()\n        self.padding = EdgeWidths()\n        self.kitten_result: dict[str, Any] | None = None\n        self.tab_id = tab.id\n        self.os_window_id = tab.os_window_id\n        self.tabref: Callable[[], TabType | None] = weakref.ref(tab)\n        self.destroyed = False\n        self.geometry: WindowGeometry = WindowGeometry(0, 0, 0, 0, 0, 0)\n        self._title_bar_screen: Any = None\n        self.needs_layout = True\n        self.is_visible_in_layout: bool = True\n        self.child = child\n        cell_width, cell_height = cell_size_for_window(self.os_window_id)\n        opts = get_options()\n        self.screen: Screen = Screen(self, 24, 80, opts.scrollback_lines, cell_width, cell_height, self.id)\n        if copy_colors_from is not None:\n            self.screen.copy_colors_from(copy_colors_from.screen)\n        self.remote_control_passwords = remote_control_passwords\n        self.allow_remote_control = allow_remote_control\n\n    def remote_control_allowed(self, pcmd: dict[str, Any], extra_data: dict[str, Any]) -> bool:\n        if not self.allow_remote_control:\n            return False\n        from .remote_control import remote_control_allowed\n        return remote_control_allowed(pcmd, self.remote_control_passwords, self, extra_data)\n\n    @property\n    def file_transmission_control(self) -> 'FileTransmission':\n        ans: Optional['FileTransmission'] = getattr(self, '_file_transmission', None)\n        if ans is None:\n            from .file_transmission import FileTransmission\n            ans = self._file_transmission = FileTransmission(self.id)\n        return ans\n\n    def on_dpi_change(self, font_sz: float) -> None:\n        self.update_effective_padding()\n\n    def change_tab(self, tab: TabType) -> None:\n        self.tab_id = tab.id\n        self.os_window_id = tab.os_window_id\n        self.tabref = weakref.ref(tab)\n\n    def effective_margin(self, edge: EdgeLiteral) -> int:\n        q = getattr(self.margin, edge)\n        if q is not None:\n            return pt_to_px(q, self.os_window_id)\n        opts = get_options()\n        tab = self.tabref()\n        is_single_window = tab is not None and tab.has_single_window_visible()\n        if is_single_window:\n            q = getattr(opts.single_window_margin_width, edge)\n            if q > -0.1:\n                return pt_to_px(q, self.os_window_id)\n        q = getattr(opts.window_margin_width, edge)\n        return pt_to_px(q, self.os_window_id)\n\n    def effective_padding(self, edge: EdgeLiteral) -> int:\n        q = getattr(self.padding, edge)\n        if q is not None:\n            return pt_to_px(q, self.os_window_id)\n        opts = get_options()\n        tab = self.tabref()\n        is_single_window = tab is not None and tab.has_single_window_visible()\n        if is_single_window:\n            q = getattr(opts.single_window_padding_width, edge)\n            if q > -0.1:\n                return pt_to_px(q, self.os_window_id)\n        q = getattr(opts.window_padding_width, edge)\n        return pt_to_px(q, self.os_window_id)\n\n    def update_effective_padding(self) -> None:\n        set_window_padding(\n            self.os_window_id, self.tab_id, self.id,\n            self.effective_padding('left'), self.effective_padding('top'),\n            self.effective_padding('right'), self.effective_padding('bottom'))\n\n    def patch_edge_width(self, which: str, edge: EdgeLiteral, val: float | None) -> None:\n        q = self.padding if which == 'padding' else self.margin\n        setattr(q, edge, val)\n        if q is self.padding:\n            self.update_effective_padding()\n\n    def effective_border(self) -> int:\n        val, unit = get_options().window_border_width\n        if unit == 'pt':\n            val = max(1 if val > 0 else 0, pt_to_px(val, self.os_window_id))\n        else:\n            val = round(val)\n        return int(val)\n\n    def apply_options(self, is_active: bool) -> None:\n        self.update_effective_padding()\n        self.screen.color_profile.reload_from_opts()\n\n    @property\n    def title(self) -> str:\n        return self.override_title or self.child_title\n\n    def __repr__(self) -> str:\n        return f'Window(title={self.title}, id={self.id})'\n\n    @property\n    def overlay_parent(self) -> Optional['Window']:\n        tab = self.tabref()\n        if tab is None:\n            return None\n        return tab.overlay_parent(self)\n\n    @property\n    def current_colors(self) -> dict[str, int | None | tuple[tuple[Color, float], ...]]:\n        return self.screen.color_profile.as_dict()\n\n    @property\n    def at_prompt(self) -> bool:\n        return self.screen.cursor_at_prompt()\n\n    @property\n    def has_running_program(self) -> bool:\n        return not self.at_prompt\n\n    def matches(self, field: str, pat: MatchPatternType, active_session: str, most_recent_session: str) -> bool:\n        if isinstance(pat, tuple):\n            if field == 'env':\n                return key_val_matcher(self.child.environ.items(), *pat)\n            if field == 'var':\n                return key_val_matcher(self.user_vars.items(), *pat)\n            return False\n\n        if field in ('id', 'window_id'):\n            return pat.pattern == str(self.id)\n        if field == 'pid':\n            return pat.pattern == str(self.child.pid)\n        if field == 'title':\n            return pat.search(self.override_title or self.title) is not None\n        if field in 'cwd':\n            return pat.search(self.child.current_cwd or self.child.cwd) is not None\n        if field == 'cmdline':\n            for x in self.child.cmdline:\n                if pat.search(x) is not None:\n                    return True\n            return False\n        if field == 'session':\n            match pat.pattern:\n                case '.':\n                    return self.created_in_session_name == active_session\n                case '~':\n                    return self.created_in_session_name == active_session or self.created_in_session_name == most_recent_session\n\n            return pat.search(self.created_in_session_name) is not None\n        return False\n\n    def matches_query(\n        self, field: str, query: str, active_tab: TabType | None = None,\n        self_window: Optional['Window'] = None, active_session: str = '',\n        most_recent_session: str = '',\n    ) -> bool:\n        if field in ('num', 'recent'):\n            if active_tab is not None:\n                try:\n                    q = int(query)\n                except Exception:\n                    return False\n                with suppress(Exception):\n                    if field == 'num':\n                        return active_tab.get_nth_window(q) is self\n                    return active_tab.nth_active_window_id(q) == self.id\n            return False\n        if field == 'state':\n            if query == 'active':\n                tab = self.tabref()\n                return tab is not None and tab.active_window is self\n            if query == 'focused':\n                return active_tab is not None and self is active_tab.active_window and last_focused_os_window_id() == self.os_window_id\n            if query == 'needs_attention':\n                return self.needs_attention\n            if query == 'parent_active':\n                tab = self.tabref()\n                if tab is not None:\n                    tm = tab.tab_manager_ref()\n                    return tm is not None and tm.active_tab is tab\n                return False\n            if query == 'parent_focused':\n                return active_tab is not None and self.tabref() is active_tab and last_focused_os_window_id() == self.os_window_id\n            if query == 'focused_os_window':\n                return last_focused_os_window_id() == self.os_window_id\n            if query == 'self':\n                return self is self_window\n            if query == 'overlay_parent':\n                return self_window is not None and self is self_window.overlay_parent\n            return False\n        if field == 'neighbor':\n            t = get_boss().active_tab\n            if t is None:\n                return False\n            gid: int | None = None\n            if query == 'left':\n                gid = t.neighboring_group_id(\"left\")\n            elif query == 'right':\n                gid = t.neighboring_group_id(\"right\")\n            elif query == 'top':\n                gid = t.neighboring_group_id(\"top\")\n            elif query == 'bottom':\n                gid = t.neighboring_group_id(\"bottom\")\n            return gid is not None and t.windows.active_window_in_group_id(gid) is self\n\n        pat = compile_match_query(query, field not in ('env', 'var'))\n        return self.matches(field, pat, active_session, most_recent_session)\n\n    def set_visible_in_layout(self, val: bool) -> None:\n        val = bool(val)\n        if val is not self.is_visible_in_layout:\n            self.is_visible_in_layout = val\n            update_window_visibility(self.os_window_id, self.tab_id, self.id, val)\n            if val:\n                self.refresh()\n\n    def refresh(self, reload_all_gpu_data: bool = False) -> None:\n        self.screen.mark_as_dirty()\n        if reload_all_gpu_data:\n            self.screen.reload_all_gpu_data()\n        wakeup_io_loop()\n        wakeup_main_loop()\n\n    def pause_resize_notifications_to_child(self, pause: bool = True) -> None:\n        if pause:\n            if self._pause_resize_notifications_to_child is None:\n                self._pause_resize_notifications_to_child = -1, -1, -1, -1\n        else:\n            p, self._pause_resize_notifications_to_child = self._pause_resize_notifications_to_child, None\n            if p and p[0] > 0:\n                if self.resize_child(p):\n                    update_ime_position_for_window(self.id, True)\n\n    def resize_child(self, current_pty_size: tuple[int, int, int, int]) -> bool:\n        boss = get_boss()\n        boss.child_monitor.resize_pty(self.id, *current_pty_size)\n        self.last_resized_at = monotonic()\n        self.last_reported_pty_size = current_pty_size\n        self.notify_child_of_resize()\n        update_ime_position = False\n        if not self.child_is_launched:\n            self.child.mark_terminal_ready()\n            self.child_is_launched = True\n            update_ime_position = True\n            if boss.args.debug_rendering:\n                now = monotonic()\n                print(f'[{now:.3f}] Child launched', file=sys.stderr)\n        elif boss.args.debug_rendering:\n            print(f'[{monotonic():.3f}] SIGWINCH sent to child in window: {self.id} with size: {current_pty_size}', file=sys.stderr)\n        return update_ime_position\n\n    def set_geometry(self, new_geometry: WindowGeometry) -> None:\n        if self.destroyed:\n            return\n        # Determine if we need a title bar and compute adjusted dimensions\n        opts = get_options()\n        position = opts.window_title_bar\n        show_tb = self.show_title_bar and new_geometry.ynum > 1\n\n        if show_tb:\n            render_ynum = new_geometry.ynum - 1\n            cell_width, cell_height = cell_size_for_window(self.os_window_id)\n            if position == 'top':\n                render_top = new_geometry.top + cell_height\n                render_bottom = new_geometry.bottom\n                tb_top = new_geometry.top\n                tb_bottom = new_geometry.top + cell_height\n            else:\n                render_top = new_geometry.top\n                render_bottom = new_geometry.bottom - cell_height\n                tb_top = new_geometry.bottom - cell_height\n                tb_bottom = new_geometry.bottom\n        else:\n            render_ynum = new_geometry.ynum\n            render_top = new_geometry.top\n            render_bottom = new_geometry.bottom\n\n        if self.needs_layout or new_geometry.xnum != self.screen.columns or render_ynum != self.screen.lines:\n            self.screen.resize(max(0, render_ynum), max(0, new_geometry.xnum))\n            self.needs_layout = False\n            call_watchers(weakref.ref(self), 'on_resize', {'old_geometry': self.geometry, 'new_geometry': new_geometry})\n        current_pty_size = (\n            self.screen.lines, self.screen.columns,\n            max(0, new_geometry.right - new_geometry.left), max(0, render_bottom - render_top))\n        update_ime_position = False\n        if current_pty_size != self.last_reported_pty_size:\n            if self._pause_resize_notifications_to_child is None:\n                update_ime_position = self.resize_child(current_pty_size)\n            else:\n                self._pause_resize_notifications_to_child = current_pty_size\n        else:\n            mark_os_window_dirty(self.os_window_id)\n\n        # Store original geometry for borders/padding calculations\n        self.geometry = g = new_geometry\n        # Set C-side render data with adjusted top/bottom for content area\n        set_window_render_data(self.os_window_id, self.tab_id, self.id, self.screen,\n                             g.left, render_top, g.right, render_bottom,\n                             g.spaces.left, g.spaces.top, g.spaces.right, g.spaces.bottom)\n        self.update_effective_padding()\n\n        # Handle title bar screen\n        if show_tb:\n            if self._title_bar_screen is None:\n                from .window_title_bar import WindowTitleBarScreen\n                self._title_bar_screen = WindowTitleBarScreen(self.os_window_id, cell_width, cell_height)\n            tb_geom = WindowGeometry(\n                left=g.left, top=tb_top, right=g.right, bottom=tb_bottom,\n                xnum=0, ynum=1,\n            )\n            self._title_bar_screen.layout(tb_geom)\n            set_window_title_bar_render_data(\n                self.os_window_id, self.tab_id, self.id, self._title_bar_screen.screen,\n                tb_geom.left, tb_geom.top, tb_geom.right, tb_geom.bottom,\n            )\n        elif self._title_bar_screen is not None:\n            # Clear title bar render data\n            set_window_title_bar_render_data(\n                self.os_window_id, self.tab_id, self.id, self._title_bar_screen.screen,\n                0, 0, 0, 0,\n            )\n            self._title_bar_screen = None\n\n        if update_ime_position:\n            update_ime_position_for_window(self.id, True)\n\n    def update_title_bar(self, is_active: bool = False) -> None:\n        if (pts := self._title_bar_screen) is None:\n            return\n        from .progress import ProgressState\n        from .window_title_bar import WindowTitleData\n\n        progress_percent = ''\n        if self.progress.state is not ProgressState.unset:\n            if self.progress.state is ProgressState.indeterminate:\n                progress_percent = '[…] '\n            elif self.progress.percent > 0:\n                progress_percent = f'[{self.progress.percent}%] '\n\n        has_activity = self.has_activity_since_last_focus\n\n        data = WindowTitleData(\n            title=self.title or '',\n            is_active=is_active,\n            window_id=self.id,\n            tab_id=self.tab_id,\n            needs_attention=self.needs_attention,\n            has_activity_since_last_focus=has_activity,\n        )\n        # If template evaluates to empty string, zero title bar geometry to hide it\n        if pts.render(data, progress_percent):\n            g = pts.geometry\n            set_window_title_bar_render_data(\n                self.os_window_id, self.tab_id, self.id, pts.screen,\n                g.left, g.top, g.right, g.bottom,\n            )\n        else:\n            set_window_title_bar_render_data(\n                self.os_window_id, self.tab_id, self.id, pts.screen,\n                0, 0, 0, 0,\n            )\n\n    def close(self) -> None:\n        get_boss().mark_window_for_close(self)\n\n    @ac('misc', '''\n        Send the specified text to the active window\n\n        See :sc:`send_text <send_text>` for details.\n        ''')\n    def send_text(self, *args: str) -> bool:\n        mode = keyboard_mode_name(self.screen)\n        required_mode_, text = args[-2:]\n        required_mode = frozenset(required_mode_.split(','))\n        if not required_mode & {mode, 'all'}:\n            return True\n        if not text:\n            return True\n        self.write_to_child(text)\n        return False\n\n    @ac(\n        'misc', '''\n        Send the specified keys to the active window.\n        Note that the key will be sent only if the current keyboard mode of the program running in the terminal supports it.\n        Both key press and key release are sent. First presses for all specified keys and then releases in reverse order.\n        To send a pattern of press and release for multiple keys use the :ac:`combine` action. For example::\n\n            map f1 send_key ctrl+x alt+y\n            map f1 combine : send_key ctrl+x : send_key alt+y\n    ''')\n    def send_key(self, *args: str) -> bool:\n        from .options.utils import parse_shortcut\n        km = get_options().kitty_mod\n        passthrough = True\n        events = []\n        prev = ''\n        for human_key in args:\n            sk = parse_shortcut(human_key)\n            if sk.is_native:\n                raise ValueError(f'Native key codes not allowed in send_key: {human_key}')\n            sk = sk.resolve_kitty_mod(km)\n            events.append(KeyEvent(key=sk.key, mods=sk.mods, action=GLFW_REPEAT if human_key == prev else GLFW_PRESS))\n            prev = human_key\n        scroll_needed = False\n        for ev in events + [KeyEvent(key=x.key, mods=x.mods, action=GLFW_RELEASE) for x in reversed(events)]:\n            enc = self.encoded_key(ev)\n            if enc:\n                self.write_to_child(enc)\n                if ev.action != GLFW_RELEASE and not is_modifier_key(ev.key):\n                    scroll_needed = True\n                passthrough = False\n        if scroll_needed:\n            self.scroll_end()\n        return passthrough\n\n    def send_key_sequence(self, *keys: KeyEvent, synthesize_release_events: bool = True) -> None:\n        for key in keys:\n            enc = self.encoded_key(key)\n            if enc:\n                self.write_to_child(enc)\n            if synthesize_release_events and key.action != GLFW_RELEASE:\n                rkey = KeyEvent(key=key.key, mods=key.mods, action=GLFW_RELEASE)\n                enc = self.encoded_key(rkey)\n                if enc:\n                    self.write_to_child(enc)\n\n    @ac('debug', 'Show a dump of the current lines in the scrollback + screen with their line attributes')\n    def dump_lines_with_attrs(self, which_screen: Literal['main', 'alternate', 'current'] = 'current') -> None:\n        strings: list[str] = []\n        ws = 0 if which_screen == 'main' else (1 if which_screen == 'alternate' else -1)\n        self.screen.dump_lines_with_attrs(strings.append, ws)\n        text = ''.join(strings)\n        get_boss().display_scrollback(self, text, title='Dump of lines', report_cursor=False)\n\n    def write_to_child(self, data: str | bytes | memoryview) -> None:\n        if data:\n            if isinstance(data, str):\n                data = data.encode('utf-8')\n            if get_boss().child_monitor.needs_write(self.id, data) is not True:\n                log_error(f'Failed to write to child {self.id} as it does not exist')\n\n    def title_updated(self) -> None:\n        update_window_title(self.os_window_id, self.tab_id, self.id, self.title)\n        t = self.tabref()\n        if t is not None:\n            t.title_changed(self)\n\n    def set_title(self, title: str | None) -> None:\n        if title:\n            title = sanitize_title(title)\n        self.override_title = title or None\n        self.call_watchers(self.watchers.on_title_change, {'title': self.title, 'from_child': False})\n        self.title_updated()\n\n    @ac(\n        'win', '''\n        Change the title of the active window interactively, by typing in the new title.\n        If you specify an argument to this action then that is used as the title instead of asking for it.\n        Use the empty string (\"\") to reset the title to default. Use a space (\" \") to indicate that the\n        prompt should not be pre-filled. For example::\n\n            # interactive usage\n            map f1 set_window_title\n            # set a specific title\n            map f2 set_window_title some title\n            # reset to default\n            map f3 set_window_title \"\"\n            # interactive usage without prefilled prompt\n            map f3 set_window_title \" \"\n        '''\n    )\n    def set_window_title(self, title: str | None = None) -> None:\n        if title is not None and title not in ('\" \"', \"' '\"):\n            if title in ('\"\"', \"''\"):\n                title = ''\n            self.set_title(title)\n            return\n        prefilled = self.title\n        if title in ('\" \"', \"' '\"):\n            prefilled = ''\n        get_boss().get_line(\n            _('Enter the new title for this window below. An empty title will cause the default title to be used.'),\n            self.set_title, window=self, initial_value=prefilled, window_title=_('Rename window'))\n\n    def set_user_var(self, key: str, val: str | bytes | None) -> None:\n        key = sanitize_control_codes(key).replace('\\n', ' ')\n        self.user_vars.pop(key, None)  # ensure key will be newest in user_vars even if already present\n        if len(self.user_vars) > 64:  # dont store too many user vars\n            oldest_key = next(iter(self.user_vars))\n            self.user_vars.pop(oldest_key)\n        if val is not None:\n            if isinstance(val, bytes):\n                val = val.decode('utf-8', 'replace')\n            self.user_vars[key] = val = sanitize_control_codes(val).replace('\\n', ' ')\n            self.call_watchers(self.watchers.on_set_user_var, {'key': key, 'value': val})\n        else:\n            self.call_watchers(self.watchers.on_set_user_var, {'key': key, 'value': None})\n\n    # screen callbacks {{{\n\n    def osc_1337(self, raw_data: str) -> None:\n        for record in raw_data.split(';'):\n            key, _, val = record.partition('=')\n            if key == 'SetUserVar':\n                ukey, has_equal, uval = val.partition('=')\n                self.set_user_var(ukey, (base64_decode(uval) if uval else b'') if has_equal == '=' else None)\n\n    def desktop_notify(self, osc_code: int, raw_datab: memoryview) -> None:\n        raw_data = str(raw_datab, 'utf-8', 'replace')\n        if osc_code == 1337:\n            self.osc_1337(raw_data)\n        if osc_code == 777:\n            if not raw_data.startswith('notify;'):\n                log_error(f'Ignoring unknown OSC 777: {raw_data}')\n                return  # unknown OSC 777\n            raw_data = raw_data[len('notify;'):]\n        if osc_code == 9 and raw_data.startswith('4;'):\n            # This is probably the ConEmu \"progress reporting\" conflicting\n            # implementation which sadly some thoughtless people have\n            # implemented in unix CLI programs.\n            # See for example: https://github.com/kovidgoyal/kitty/issues/8011\n            try:\n                parts = tuple(map(int, raw_data.split(';')))[1:]\n            except Exception:\n                log_error(f'Ignoring malformed OSC 9;4 progress report: {raw_data!r}')\n                return\n            self.progress.update(*parts[:2])\n            if (tab := self.tabref()) is not None:\n                tab.update_progress()\n            self.clear_progress_if_needed()\n            return\n        get_boss().notification_manager.handle_notification_cmd(self.id, osc_code, raw_data)\n\n    def clear_progress_if_needed(self, timer_id: int | None = None) -> None:\n        # Clear stuck or completed progress\n        if timer_id is not None:  # this is a timer callback\n            self.clear_progress_timer = 0\n        if self.progress.clear_progress():\n            if (tab := self.tabref()) is not None:\n                tab.update_progress()\n        else:\n            if not self.clear_progress_timer:\n                self.clear_progress_timer = add_timer(self.clear_progress_if_needed, 1.0, False)\n\n    def on_mouse_event(self, event: dict[str, Any]) -> bool:\n        event['mods'] = event.get('mods', 0) & mod_mask\n        ev = MouseEvent(**event)\n        self.current_mouse_event_button = ev.button\n        action = get_options().mousemap.get(ev)\n        if action is None:\n            return False\n        return get_boss().combine(action, window_for_dispatch=self, dispatch_type='MouseEvent')\n\n    def open_url(self, url: str, hyperlink_id: int, cwd: str | None = None) -> None:\n        boss = get_boss()\n        try:\n            if self.open_url_handler and self.open_url_handler(boss, self, url, hyperlink_id, cwd or ''):\n                return\n        except Exception:\n            import traceback\n            traceback.print_exc()\n        opts = get_options()\n        if hyperlink_id:\n            if not opts.allow_hyperlinks:\n                return\n            from urllib.parse import unquote, urlparse, urlunparse\n            try:\n                purl = urlparse(url)\n            except Exception:\n                return\n            if (not purl.scheme or purl.scheme == 'file'):\n                if purl.netloc:\n                    from .utils import get_hostname\n                    hostname = get_hostname()\n                    remote_hostname = purl.netloc.partition(':')[0]\n                    if remote_hostname and remote_hostname != hostname and remote_hostname != 'localhost':\n                        self.handle_remote_file(purl.netloc, unquote(purl.path))\n                        return\n                    url = urlunparse(purl._replace(netloc=''))\n            if opts.allow_hyperlinks & 0b10:\n                from kittens.tui.operations import styled\n                boss.choose(\n                    'What would you like to do with this URL:\\n' + styled(sanitize_url_for_display_to_user(url), fg='yellow'),\n                    partial(self.hyperlink_open_confirmed, url, cwd),\n                    'o:Open', 'c:Copy to clipboard', 'n;red:Nothing', default='o',\n                    window=self, title=_('Hyperlink activated'),\n                )\n                return\n        boss.open_url(url, cwd=cwd)\n\n    def hyperlink_open_confirmed(self, url: str, cwd: str | None, q: str) -> None:\n        if q == 'o':\n            get_boss().open_url(url, cwd=cwd)\n        elif q == 'c':\n            set_clipboard_string(url)\n\n    def handle_remote_file(self, netloc: str, remote_path: str) -> None:\n        from kittens.remote_file.main import is_ssh_kitten_sentinel\n        from kittens.ssh.utils import get_connection_data\n\n        from .utils import SSHConnectionData\n        args = self.ssh_kitten_cmdline()\n        conn_data: None | list[str] | SSHConnectionData = None\n        if args:\n            ssh_cmdline = sorted(self.child.foreground_processes, key=lambda p: p['pid'])[-1]['cmdline'] or ['']\n            if 'ControlPath=' in ' '.join(ssh_cmdline):\n                idx = ssh_cmdline.index('--')\n                conn_data = [is_ssh_kitten_sentinel] + list(ssh_cmdline[:idx + 2])\n        if conn_data is None:\n            args = self.child.foreground_cmdline\n            conn_data = get_connection_data(args, self.child.foreground_cwd or self.child.current_cwd or '')\n            if conn_data is None:\n                get_boss().show_error('Could not handle remote file', f'No SSH connection data found in: {args}')\n                return\n        get_boss().run_kitten(\n            'remote_file', '--hostname', netloc.partition(':')[0], '--path', remote_path,\n            '--ssh-connection-data', json.dumps(conn_data)\n        )\n\n    def send_signal_for_key(self, key_num: bytes) -> bool:\n        try:\n            return self.child.send_signal_for_key(key_num)\n        except OSError as err:\n            log_error(f'Failed to send signal for key to child with err: {err}')\n            return False\n\n    def focus_changed(self, focused: bool) -> None:\n        if self.destroyed or self.ignore_focus_changes or self.is_focused == focused:\n            return\n        self.is_focused = focused\n        call_watchers(weakref.ref(self), 'on_focus_change', {'focused': focused})\n        for c in self.actions_on_focus_change:\n            try:\n                c(self, focused)\n            except Exception:\n                import traceback\n                traceback.print_exc()\n        self.screen.focus_changed(focused)\n        if focused:\n            self.last_focused_at = monotonic()\n            update_ime_position_for_window(self.id, False, 1)\n            changed = self.needs_attention\n            self.needs_attention = False\n            if changed:\n                tab = self.tabref()\n                if tab is not None:\n                    tab.relayout_borders()\n            if self.last_cmd_end_notification is not None:\n                from .notifications import OnlyWhen\n                opts = get_options()\n                if self.last_cmd_end_notification[1] in (OnlyWhen.unfocused, OnlyWhen.invisible) and 'focus' in opts.notify_on_cmd_finish.clear_on:\n                    get_boss().notification_manager.close_notification(self.last_cmd_end_notification[0])\n                    self.last_cmd_end_notification = None\n        elif self.os_window_id == current_focused_os_window_id():\n            # Cancel IME composition after loses focus\n            update_ime_position_for_window(self.id, False, -1)\n\n    def title_changed(self, new_title: memoryview | None, is_base64: bool = False) -> None:\n        self.child_title = process_title_from_child(new_title or memoryview(b''), is_base64, self.default_title)\n        self.call_watchers(self.watchers.on_title_change, {'title': self.child_title, 'from_child': True})\n        if self.override_title is None:\n            self.title_updated()\n\n    def osc_context(self, ctx_data: memoryview) -> None:\n        pass  # this is systemd's useless OSC 3008 context protocol https://systemd.io/OSC_CONTEXT/\n\n    def icon_changed(self, new_icon: memoryview) -> None:\n        pass  # TODO: Implement this\n\n    @property\n    def is_active(self) -> bool:\n        return get_boss().active_window is self\n\n    @property\n    def has_activity_since_last_focus(self) -> bool:\n        return self.screen.has_activity_since_last_focus()\n\n    def on_activity_since_last_focus(self) -> bool:\n        if get_options().tab_activity_symbol and (monotonic() - self.last_resized_at) > 0.5:\n            # Ignore activity soon after a resize as the child program is probably redrawing the screen\n            get_boss().on_activity_since_last_focus(self)\n            return True\n        return False\n\n    def on_da1(self) -> None:\n        self.screen.send_escape_code_to_child(ESC_CSI, da1(get_options()))\n\n    def on_bell(self) -> None:\n        cb = get_options().command_on_bell\n        if cb and cb != ['none']:\n            import shlex\n            import subprocess\n            env = self.child.foreground_environ\n            env['KITTY_CHILD_CMDLINE'] = ' '.join(map(shlex.quote, self.child.cmdline))\n            subprocess.Popen(cb, env=env, cwd=self.child.foreground_cwd, preexec_fn=clear_handled_signals)\n        if not self.is_active:\n            changed = not self.needs_attention\n            self.needs_attention = True\n            tab = self.tabref()\n            if tab is not None:\n                if changed:\n                    tab.relayout_borders()\n                tab.on_bell(self)\n\n    def color_profile_popped(self, bg_changed: bool) -> None:\n        if bg_changed:\n            get_boss().default_bg_changed_for(self.id, via_escape_code=True)\n\n    def report_color(self, code: str, col: Color) -> None:\n        r, g, b = col.red, col.green, col.blue\n        r |= r << 8\n        g |= g << 8\n        b |= b << 8\n        self.screen.send_escape_code_to_child(ESC_OSC, f'{code};rgb:{r:04x}/{g:04x}/{b:04x}')\n\n    def on_reset(self) -> None:\n        pass\n\n    def notify_child_of_resize(self) -> None:\n        pty_size = self.last_reported_pty_size\n        if pty_size[0] > -1 and self.screen.in_band_resize_notification:\n            self.screen.send_escape_code_to_child(ESC_CSI, f'48;{pty_size[0]};{pty_size[1]};{pty_size[3]};{pty_size[2]}t')\n\n    def color_control(self, code: int, value: str | bytes | memoryview = '') -> None:\n        response = color_control(self.screen.color_profile, code, value)\n        if response:\n            self.screen.send_escape_code_to_child(ESC_OSC, response)\n\n    def set_dynamic_color(self, code: int, value: str | bytes | memoryview = '') -> None:\n        if isinstance(value, (bytes, memoryview)):\n            value = str(value, 'utf-8', 'replace')\n        if code == 22:\n            ret = set_pointer_shape(self.screen, value, self.os_window_id)\n            if ret:\n                self.screen.send_escape_code_to_child(ESC_OSC, '22;' + ret)\n            return\n\n        dirtied = default_bg_changed = False\n        def change(which: DynamicColor, val: str) -> None:\n            nonlocal dirtied, default_bg_changed\n            dirtied = True\n            if which.name == 'default_bg':\n                default_bg_changed = True\n            v = to_color(val) if val else None\n            if v is None:\n                delattr(self.screen.color_profile, which.name)\n            else:\n                setattr(self.screen.color_profile, which.name, v)\n\n        for val in value.split(';'):\n            w = DYNAMIC_COLOR_CODES.get(code)\n            if w is not None:\n                if val == '?':\n                    col = getattr(self.screen.color_profile, w.name) or Color()\n                    self.report_color(str(code), col)\n                else:\n                    q = '' if code >= 100 else val\n                    change(w, q)\n            code += 1\n        if dirtied:\n            self.screen.mark_as_dirty()\n        if default_bg_changed:\n            get_boss().default_bg_changed_for(self.id, via_escape_code=True)\n\n    @property\n    def is_dark(self) -> bool:\n        return self.screen.color_profile.default_bg.is_dark\n\n    def on_color_scheme_preference_change(self, via_escape_code: bool = False) -> None:\n        if not via_escape_code:\n            self.report_color_scheme_preference_if_wanted()\n        self.call_watchers(self.watchers.on_color_scheme_preference_change, {\n            'is_dark': self.is_dark, 'via_escape_code': via_escape_code\n        })\n\n    def report_color_scheme_preference_if_wanted(self) -> None:\n        if self.screen.color_preference_notification:\n            self.report_color_scheme_preference()\n\n    def report_color_scheme_preference(self) -> None:\n        n = 1 if self.is_dark else 2\n        self.screen.send_escape_code_to_child(ESC_CSI, f'?997;{n}n')\n\n    def set_color_table_color(self, code: int, bvalue: memoryview | None = None) -> None:\n        value = str(bvalue or b'', 'utf-8', 'replace')\n        cp = self.screen.color_profile\n\n        def parse_color_set(raw: str) -> Generator[tuple[int, int | None], None, None]:\n            parts = raw.split(';')\n            lp = len(parts)\n            if lp % 2 != 0:\n                return\n            for c_, spec in [parts[i:i + 2] for i in range(0, len(parts), 2)]:\n                try:\n                    c = int(c_)\n                    if c < 0 or c > 255:\n                        continue\n                    if spec == '?':\n                        yield c, None\n                    else:\n                        q = to_color(spec)\n                        if q is not None:\n                            yield c, color_as_int(q)\n                except Exception:\n                    continue\n\n        if code == 4:\n            changed = False\n            for c, val in parse_color_set(value):\n                if val is None:  # color query\n                    qc = self.screen.color_profile.as_color((c << 8) | 1)\n                    assert qc is not None\n                    self.report_color(f'4;{c}', qc)\n                else:\n                    changed = True\n                    cp.set_color(c, val)\n            if changed:\n                self.refresh()\n        elif code == 104:\n            if not value.strip():\n                cp.reset_color_table()\n            else:\n                for x in value.split(';'):\n                    try:\n                        y = int(x)\n                    except Exception:\n                        continue\n                    if 0 <= y <= 255:\n                        cp.reset_color(y)\n            self.refresh()\n\n    def request_capabilities(self, q: str) -> None:\n        for result in get_capabilities(q, get_options(), self.id, self.os_window_id):\n            self.screen.send_escape_code_to_child(ESC_DCS, result)\n\n    def handle_remote_cmd(self, cmd: memoryview) -> None:\n        get_boss().handle_remote_cmd(cmd, self)\n\n    def handle_remote_echo(self, msg: memoryview) -> None:\n        data = base64_decode(msg)\n        # ensure we are not writing any control char back as this can lead to command injection on shell prompts\n        # Any bytes outside the printable ASCII range are removed.\n        data = re.sub(rb'[^ -~]', b'', data)\n        self.write_to_child(data)\n\n    def handle_remote_ssh(self, msg: memoryview) -> None:\n        from kittens.ssh.utils import get_ssh_data\n        for line in get_ssh_data(msg, f'{os.getpid()}-{self.id}'):\n            self.write_to_child(line)\n\n    def handle_kitten_result(self, msg: memoryview) -> None:\n        import base64\n        self.kitten_result = json.loads(base64.b85decode(msg))\n        for processor in self.kitten_result_processors:\n            try:\n                processor(self, self.kitten_result)\n            except Exception:\n                import traceback\n                traceback.print_exc()\n\n    def add_kitten_result_processor(self, callback: Callable[['Window', Any], None]) -> None:\n        self.kitten_result_processors.append(callback)\n\n    def handle_overlay_ready(self, msg: memoryview) -> None:\n        tab = self.tabref()\n        if tab is not None:\n            tab.move_window_to_top_of_group(self)\n        if self.keys_redirected_till_ready_from:\n            set_redirect_keys_to_overlay(self.os_window_id, self.tab_id, self.keys_redirected_till_ready_from, 0)\n            buffer_keys_in_window(self.os_window_id, self.tab_id, self.id, False)\n            self.keys_redirected_till_ready_from = 0\n\n    def append_remote_data(self, msgb: memoryview) -> str:\n        if not msgb:\n            cdata = ''.join(self.current_remote_data)\n            self.current_remote_data = []\n            return cdata\n        msg = str(msgb, 'utf-8', 'replace')\n        num, rest = msg.split(':', 1)\n        max_size = get_options().clipboard_max_size * 1024 * 1024\n        if num == '0' or sum(map(len, self.current_remote_data)) > max_size:\n            self.current_remote_data = []\n        self.current_remote_data.append(rest)\n        return ''\n\n    def handle_remote_edit(self, msg: memoryview) -> None:\n        cdata = self.append_remote_data(msg)\n        if cdata:\n            from .launch import remote_edit\n            remote_edit(cdata, self)\n\n    def handle_remote_clone(self, msg: memoryview) -> None:\n        cdata = self.append_remote_data(msg)\n        if cdata:\n            ac = get_options().allow_cloning\n            if ac == 'ask':\n                get_boss().confirm(_(\n                    'A program running in this window wants to clone it into another window.'\n                    ' Allow it do so, once?'),\n                    partial(self.handle_remote_clone_confirmation, cdata), window=self,\n                    title=_('Allow cloning of window?'),\n                )\n            elif ac in ('yes', 'y', 'true'):\n                self.handle_remote_clone_confirmation(cdata, True)\n\n    def handle_remote_clone_confirmation(self, cdata: str, confirmed: bool) -> None:\n        if confirmed:\n            from .launch import clone_and_launch\n            clone_and_launch(cdata, self)\n\n    def handle_remote_askpass(self, msgb: memoryview) -> None:\n        from .shm import SharedMemory\n        msg = str(msgb, 'utf-8')\n        with SharedMemory(name=msg, readonly=True) as shm:\n            shm.seek(1)\n            data = json.loads(shm.read_data_with_size())\n\n        def callback(ans: Any) -> None:\n            data = json.dumps(ans)\n            with SharedMemory(name=msg) as shm:\n                shm.seek(1)\n                shm.write_data_with_size(data)\n                shm.flush()\n                shm.seek(0)\n                shm.write(b'\\x01')\n\n        message: str = data['message']\n        if data['type'] == 'confirm':\n            get_boss().confirm(\n                message, callback, window=self, confirm_on_cancel=bool(data.get('confirm_on_cancel')),\n                confirm_on_accept=bool(data.get('confirm_on_accept', True)))\n        elif data['type'] == 'choose':\n            get_boss().choose(\n                message, callback, *data['choices'], window=self, default=data.get('default', ''))\n        elif data['type'] == 'get_line':\n            get_boss().get_line(\n                message, callback, window=self, is_password=bool(data.get('is_password')), prompt=data.get('prompt', '> '))\n        else:\n            log_error(f'Ignoring ask request with unknown type: {data[\"type\"]}')\n\n    def handle_remote_print(self, msg: memoryview) -> None:\n        text = process_remote_print(msg)\n        print(text, end='', flush=True)\n\n    def handle_restore_cursor_appearance(self, msg: memoryview | None = None) -> None:\n        opts = get_options()\n        self.screen.cursor.blink = opts.cursor_blink_interval[0] != 0\n        self.screen.cursor.shape = opts.cursor_shape\n        self.screen.cursor_visible = True\n        delattr(self.screen.color_profile, 'cursor_color')\n\n    def send_cmd_response(self, response: Any) -> None:\n        self.screen.send_escape_code_to_child(ESC_DCS, '@kitty-cmd' + json.dumps(response))\n\n    def file_transmission(self, data: memoryview) -> None:\n        self.file_transmission_control.handle_serialized_command(data)\n\n    def clipboard_control(self, data: memoryview, is_partial: bool | None = False) -> None:\n        if is_partial is None:\n            self.clipboard_request_manager.parse_osc_5522(data)\n        else:\n            self.clipboard_request_manager.parse_osc_52(data, is_partial)\n\n    def manipulate_title_stack(self, pop: bool, title: str, icon: Any) -> None:\n        if title:\n            if pop:\n                if self.title_stack:\n                    self.child_title = self.title_stack.pop()\n                    self.call_watchers(self.watchers.on_title_change, {'title': self.child_title, 'from_child': True})\n                    self.title_updated()\n            else:\n                if self.child_title:\n                    self.title_stack.append(self.child_title)\n\n    def handle_cmd_end(self, exit_status: str = '') -> None:\n        if self.last_cmd_output_start_time == 0.:\n            return\n        try:\n            self.last_cmd_exit_status = int(exit_status)\n        except Exception:\n            self.last_cmd_exit_status = 0\n        end_time = monotonic()\n        last_cmd_output_duration = end_time - self.last_cmd_output_start_time\n        self.last_cmd_output_start_time = 0.\n\n        self.call_watchers(self.watchers.on_cmd_startstop, {\n            \"is_start\": False, \"time\": end_time, 'cmdline': self.last_cmd_cmdline, 'exit_status': self.last_cmd_exit_status})\n\n        opts = get_options()\n        when, duration, action, notify_cmdline, _ = opts.notify_on_cmd_finish\n\n        if last_cmd_output_duration >= duration and when != 'never':\n            from .notifications import OnlyWhen\n            nm = get_boss().notification_manager\n            cmd = nm.create_notification_cmd()\n            cmd.title = 'kitty'\n            s = self.last_cmd_cmdline.replace('\\\\\\n', ' ')\n            cmd.body = f'Command {s} finished with status: {exit_status}.\\nClick to focus.'\n            cmd.only_when = OnlyWhen(when)\n            if not nm.is_notification_allowed(cmd, self.id):\n                return\n\n            def notify(window: Window, opts: Options, nm: NotificationManager) -> None:\n                if window.last_cmd_end_notification is not None:\n                    if 'next' in opts.notify_on_cmd_finish.clear_on:\n                        nm.close_notification(window.last_cmd_end_notification[0])\n                    window.last_cmd_end_notification = None\n                notification_id = nm.notify_with_command(cmd, window.id)\n                if notification_id is not None:\n                    window.last_cmd_end_notification = notification_id, cmd.only_when\n\n            if action == 'notify':\n                notify(self, opts, nm)\n            elif action == 'bell':\n                self.screen.bell()\n            elif action == 'notify-bell':\n                notify(self, opts, nm)\n                self.screen.bell()\n            elif action == 'command':\n                open_cmd([x.replace('%c', self.last_cmd_cmdline).replace('%s', exit_status) for x in notify_cmdline])\n            else:\n                raise ValueError(f'Unknown action in option `notify_on_cmd_finish`: {action}')\n\n    def cmd_output_marking(self, is_start: bool | None, cmdline: str = '') -> None:\n        if is_start:\n            start_time = monotonic()\n            self.last_cmd_output_start_time = start_time\n            cmdline = decode_cmdline(cmdline) if cmdline else ''\n            self.last_cmd_cmdline = cmdline\n            self.call_watchers(self.watchers.on_cmd_startstop, {\"is_start\": True, \"time\": start_time, 'cmdline': cmdline, 'exit_status': 0})\n        else:\n            self.handle_cmd_end(cmdline)\n    # }}}\n\n    # mouse actions {{{\n    @ac('mouse', '''\n        Handle a mouse click\n\n        Try to perform the specified actions one after the other till one of them is successful.\n        Supported actions are::\n\n            selection - check for a selection and if one exists abort processing\n            link - if a link exists under the mouse, click it\n            prompt - if the mouse click happens at a shell prompt move the cursor to the mouse location\n\n        For examples, see :ref:`conf-kitty-mouse.mousemap`\n        ''')\n    def mouse_handle_click(self, *actions: str) -> None:\n        for a in actions:\n            if a == 'selection':\n                if self.screen.has_selection():\n                    break\n            if a == 'link':\n                if click_mouse_url(self.os_window_id, self.tab_id, self.id):\n                    break\n            if a == 'prompt':\n                # Do not send move cursor events too soon after the window is\n                # focused, this is because there are people that click on\n                # windows and start typing immediately and the cursor event\n                # can interfere with that. See https://github.com/kovidgoyal/kitty/issues/4128\n                if monotonic() - self.last_focused_at < 1.5 * get_click_interval():\n                    return\n                if move_cursor_to_mouse_if_in_prompt(self.os_window_id, self.tab_id, self.id):\n                    self.screen.ignore_bells_for(1)\n                    break\n\n    @ac('mouse', 'Click the URL under the mouse')\n    def mouse_click_url(self) -> None:\n        self.mouse_handle_click('link')\n\n    @ac('mouse', 'Click the URL under the mouse only if the screen has no selection')\n    def mouse_click_url_or_select(self) -> None:\n        self.mouse_handle_click('selection', 'link')\n\n    @ac('mouse', '''\n        Manipulate the selection based on the current mouse position\n\n        For examples, see :ref:`conf-kitty-mouse.mousemap`\n        ''')\n    def mouse_selection(self, code: int) -> None:\n        mouse_selection(self.os_window_id, self.tab_id, self.id, code, self.current_mouse_event_button)\n\n    @ac('mouse', 'Paste the current primary selection')\n    def paste_selection(self) -> None:\n        txt = get_boss().current_primary_selection()\n        if txt:\n            self.paste_with_actions(txt)\n\n    @ac('mouse', 'Paste the current primary selection or the clipboard if no selection is present')\n    def paste_selection_or_clipboard(self) -> None:\n        txt = get_boss().current_primary_selection_or_clipboard()\n        if txt:\n            self.paste_with_actions(txt)\n\n    @ac('mouse', '''\n        Select clicked command output\n\n        Requires :ref:`shell_integration` to work\n        ''')\n    def mouse_select_command_output(self) -> None:\n        click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id, True)\n\n    @ac('mouse', '''\n        Show clicked command output in a pager like less\n\n        Requires :ref:`shell_integration` to work\n        ''')\n    def mouse_show_command_output(self) -> None:\n        if click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id, False):\n            self.show_cmd_output(CommandOutput.last_visited, 'Clicked command output')\n    # }}}\n\n    def text_for_selection(self, as_ansi: bool = False) -> str:\n        sts = get_options().strip_trailing_spaces\n        strip_trailing_spaces = sts == 'always' or (sts == 'smart' and not self.screen.is_rectangle_select())\n        lines = self.screen.text_for_selection(as_ansi, strip_trailing_spaces)\n        return ''.join(lines)\n\n    def has_selection(self) -> bool:\n        return self.screen.has_selection()\n\n    def call_watchers(self, which: Iterable[Watcher], data: dict[str, Any]) -> None:\n        boss = get_boss()\n        for w in which:\n            try:\n                w(boss, self, data)\n            except Exception:\n                import traceback\n                traceback.print_exc()\n\n    def destroy(self) -> None:\n        self.call_watchers(self.watchers.on_close, {})\n        self.destroyed = True\n        self.clipboard_request_manager.close()\n        del self.kitten_result_processors\n        if hasattr(self, 'screen'):\n            if self.is_active and self.os_window_id == current_focused_os_window_id():\n                # Cancel IME composition when window is destroyed\n                update_ime_position_for_window(self.id, False, -1)\n            # Remove cycles so that screen is de-allocated immediately\n            self.screen.reset_callbacks()\n            del self.screen\n\n    def as_text(\n        self,\n        as_ansi: bool = False,\n        add_history: bool = False,\n        add_wrap_markers: bool = False,\n        alternate_screen: bool = False,\n        add_cursor: bool = False\n    ) -> str:\n        return as_text(self.screen, as_ansi, add_history, add_wrap_markers, alternate_screen, add_cursor)\n\n    def cmd_output(self, which: CommandOutput = CommandOutput.last_run, as_ansi: bool = False, add_wrap_markers: bool = False) -> str:\n        return cmd_output(self.screen, which, as_ansi, add_wrap_markers)\n\n    def get_cwd_of_child(self, oldest: bool = False) -> str | None:\n        return self.child.get_foreground_cwd(oldest) or self.child.current_cwd\n\n    def get_cwd_of_root_child(self) -> str | None:\n        return self.child.current_cwd\n\n    def get_exe_of_child(self, oldest: bool = False) -> str:\n        return self.child.get_foreground_exe(oldest) or self.child.argv[0]\n\n    @property\n    def cwd_of_child(self) -> str | None:\n        return self.get_cwd_of_child()\n\n    @property\n    def root_in_foreground_processes(self) -> bool:\n        q = self.child.pid\n        for p in self.child.foreground_processes:\n            if p['pid'] == q:\n                return True\n        return False\n\n    @property\n    def child_is_remote(self) -> bool:\n        for p in self.child.foreground_processes:\n            q = list(p['cmdline'] or ())\n            if q and os.path.basename(q[0]).lower() == 'ssh':\n                return True\n        return False\n\n    def ssh_kitten_cmdline_with_pid(self) -> tuple[int, list[str]]:\n        from kittens.ssh.utils import is_kitten_cmdline\n        for p in self.child.foreground_processes:\n            q = list(p['cmdline'] or ())\n            if len(q) > 3 and os.path.basename(q[0]) == 'kitten' and q[1] == 'run-shell':\n                q = q[2:]  # --hold-after-ssh causes kitten run-shell wrapper to be added\n            if is_kitten_cmdline(q):\n                return p['pid'], q\n        return -1, []\n\n    def ssh_kitten_cmdline(self) -> list[str]:\n        return self.ssh_kitten_cmdline_with_pid()[1]\n\n    def pipe_data(self, text: str, has_wrap_markers: bool = False) -> PipeData:\n        text = text or ''\n        if has_wrap_markers:\n            text = text.replace('\\r\\n', '\\n').replace('\\r', '\\n')\n        lines = text.count('\\n')\n        input_line_number = (lines - (self.screen.lines - 1) - self.screen.scrolled_by)\n        return {\n            'input_line_number': input_line_number,\n            'scrolled_by': self.screen.scrolled_by,\n            'cursor_x': self.screen.cursor.x + 1,\n            'cursor_y': self.screen.cursor.y + 1,\n            'lines': self.screen.lines,\n            'columns': self.screen.columns,\n            'text': text\n        }\n\n    def set_logo(self, path: str, position: str = '', alpha: float = -1, png_data: bytes = b'') -> None:\n        path = resolve_custom_file(path) if path else ''\n        set_window_logo(self.os_window_id, self.tab_id, self.id, path, position or '', alpha, png_data)\n\n    def send_paste_event(self, is_primary_selection: bool = False) -> bool:\n        if not self.screen.paste_events:\n            return False\n        self.clipboard_request_manager.send_paste_event(is_primary_selection)\n        return True\n\n    def paste_with_actions(self, text: str, from_drop: bool = False, is_uri_list: bool = False) -> None:\n        if self.destroyed or not text:\n            return\n        opts = get_options()\n        if 'filter' in opts.paste_actions:\n            text = load_paste_filter()(text)\n            if not text:\n                return\n        if 'quote-urls-at-prompt' in opts.paste_actions and self.at_prompt:\n            if is_uri_list:\n                import shlex\n                urls = text.splitlines(keepends=False)\n                text = ' '.join(map(shlex.quote, urls))\n            else:\n                prefixes = '|'.join(opts.url_prefixes)\n                m = re.match(f'({prefixes}):(.+)', text)\n                if m is not None:\n                    scheme, rest = m.group(1), m.group(2)\n                    if rest.startswith('//') or scheme in ('mailto', 'irc'):\n                        import shlex\n                        text = shlex.quote(text)\n        if 'replace-dangerous-control-codes' in opts.paste_actions:\n            text = replace_c0_codes_except_nl_space_tab(text)\n        if 'replace-newline' in opts.paste_actions and 'confirm' not in opts.paste_actions:\n            text = text.replace('\\n', '\\x1bE')\n        btext = text.encode('utf-8')\n        which = 'drop' if from_drop else 'paste'\n        if 'confirm' in opts.paste_actions:\n            sanitized = replace_c0_codes_except_nl_space_tab(btext)\n            replaced_c0_control_codes = sanitized != btext\n            if 'replace-newline' in opts.paste_actions:\n                sanitized = sanitized.replace(b'\\n', b'\\x1bE')\n            replaced_newlines = False\n            if not self.screen.in_bracketed_paste_mode:\n                # \\n is converted to \\r and \\r is interpreted as the enter key\n                # by legacy programs that dont support the full kitty keyboard protocol,\n                # which in the case of shells can lead to command execution, so\n                # replace with <ESC>E (NEL) which has the newline visual effect \\r\\n but\n                # isnt interpreted as Enter.\n                t = sanitized.replace(b'\\n', b'\\x1bE')\n                replaced_newlines = t != sanitized\n                sanitized = t\n            if replaced_c0_control_codes or replaced_newlines:\n                msg = _(\n                    'The text to be {0} contains terminal control codes.\\n\\nIf the terminal program you are {1}'\n                    ' into does not properly sanitize text, this can lead to'\n                    ' \\x1b[31mcode execution vulnerabilities\\x1b[39m.\\n\\nHow would you like to proceed?'\n                ).format('dropped' if from_drop else 'pasted', 'dropping' if from_drop else 'pasting')\n                get_boss().choose(\n                    msg, partial(self.handle_dangerous_paste_confirmation, btext, sanitized),\n                    's;green:Sanitize and ' + which, f'a;red:{which.capitalize()} anyway', 'c;yellow:Cancel',\n                    window=self, default='s', title=_('Allow {}?').format(which),\n                )\n                return\n        if 'confirm-if-large' in opts.paste_actions:\n            msg = ''\n            if len(btext) > 16 * 1024:\n                msg = _('{1} very large amounts of text ({0} bytes) can be slow.').format(\n                    len(btext), 'Dropping' if from_drop else 'Pasting')\n                get_boss().confirm(msg + _(' Are you sure?'), partial(self.handle_large_paste_confirmation, btext),\n                                   window=self, title=_('Allow large {}?').format('drop' if from_drop else 'paste'))\n                return\n        self.paste_text(btext)\n\n    def handle_dangerous_paste_confirmation(self, unsanitized: bytes, sanitized: bytes, choice: str) -> None:\n        if choice == 's':\n            self.paste_text(sanitized)\n        elif choice == 'a':\n            self.paste_text(unsanitized)\n\n    def handle_large_paste_confirmation(self, btext: bytes, confirmed: bool) -> None:\n        if confirmed:\n            self.paste_text(btext)\n\n    def paste_bytes(self, text: str | bytes) -> None:\n        # paste raw bytes without any processing\n        if isinstance(text, str):\n            text = text.encode('utf-8')\n        self.screen.paste_bytes(text)\n\n    def paste_text(self, text: str | bytes) -> None:\n        if text and not self.destroyed:\n            if isinstance(text, str):\n                text = text.encode('utf-8')\n            if self.screen.in_bracketed_paste_mode:\n                text = sanitize_for_bracketed_paste(text)\n            else:\n                # Workaround for broken editors like nano that cannot handle\n                # newlines in pasted text see https://github.com/kovidgoyal/kitty/issues/994\n                text = text.replace(b'\\r\\n', b'\\n').replace(b'\\n', b'\\r')\n            self.screen.paste(text)\n\n    def clear_screen(self, reset: bool = False, scrollback: bool = False) -> None:\n        self.screen.cursor.x = self.screen.cursor.y = 0\n        if reset:\n            self.screen.reset()\n            self.child.reset_termios_state()\n        else:\n            self.screen.erase_in_display(3 if scrollback else 2, False)\n\n    def current_mouse_position(self) -> Optional['MousePosition']:\n        ' Return the last position at which a mouse event was received by this window '\n        return get_mouse_data_for_window(self.os_window_id, self.tab_id, self.id)\n\n    def on_drop(self, drop: dict[str, bytes]) -> None:\n        text = ''\n        is_uri_list = False\n        if uri_list := drop.pop('text/uri-list', b''):\n            urls = parse_uri_list(uri_list.decode('utf-8', 'replace'))\n            text = '\\n'.join(urls)\n            is_uri_list = True\n        elif tp := drop.pop('text/plain', b''):\n            text = tp.decode('utf-8', 'replace')\n        elif tp := drop.pop('text/plain;charset=utf-8', b''):\n            text = tp.decode('utf-8', 'replace')\n        if text:\n            self.paste_with_actions(text, from_drop=True, is_uri_list=is_uri_list)\n\n\n    # Serialization {{{\n    def as_dict(\n        self, is_focused: bool = False, is_self: bool = False, is_active: bool = False,\n        neighbors_map: NeighborsMap | None = None,\n    ) -> WindowDict:\n        if neighbors_map is None:\n            neighbors_map = {}\n        return {\n            'id': self.id,\n            'is_focused': is_focused,\n            'is_active': is_active,\n            'title': self.title,\n            'title_overridden': self.override_title is not None,\n            'pid': self.child.pid,\n            'cwd': self.child.current_cwd or self.child.cwd,\n            'cmdline': self.child.cmdline,\n            'last_reported_cmdline': self.last_cmd_cmdline,\n            'last_cmd_exit_status': self.last_cmd_exit_status,\n            'env': self.child.environ or self.child.final_env,\n            'foreground_processes': self.child.foreground_processes,\n            'is_self': is_self,\n            'at_prompt': self.at_prompt,\n            'lines': self.screen.lines,\n            'columns': self.screen.columns,\n            'user_vars': self.user_vars,\n            'created_at': self.created_at,\n            'in_alternate_screen': self.screen.is_using_alternate_linebuf(),\n            'neighbors': neighbors_map,\n        }\n\n    def serialize_state(self) -> dict[str, Any]:\n        ans = {\n            'version': 1,\n            'id': self.id,\n            'child_title': self.child_title,\n            'override_title': self.override_title,\n            'default_title': self.default_title,\n            'title_stack': list(self.title_stack),\n            'allow_remote_control': self.allow_remote_control,\n            'remote_control_passwords': self.remote_control_passwords,\n            'cwd': self.child.current_cwd or self.child.cwd,\n            'env': self.child.environ,\n            'cmdline': self.child.cmdline,\n            'last_reported_cmdline': self.last_cmd_cmdline,\n            'last_cmd_exit_status': self.last_cmd_exit_status,\n            'margin': self.margin.serialize(),\n            'user_vars': self.user_vars,\n            'padding': self.padding.serialize(),\n        }\n        if self.window_custom_type:\n            ans['window_custom_type'] = self.window_custom_type\n        if self.overlay_type is not OverlayType.transient:\n            ans['overlay_type'] = self.overlay_type.value\n        if self.user_vars:\n            ans['user_vars'] = self.user_vars\n        return ans\n\n    @property\n    def cwd_for_serialization(self) -> str:\n        cwd = self.get_cwd_of_child(oldest=False) or self.get_cwd_of_child(oldest=True) or self.child.cwd\n        if self.screen.last_reported_cwd and self.at_prompt and not self.child_is_remote:\n            cwd = path_from_osc7_url(self.screen.last_reported_cwd) or cwd\n        return cwd\n\n    def as_launch_command(self, ser_opts: SaveAsSessionOptions, cwd: str, is_overlay: bool = False) -> list[str]:\n        ' Return a launch command that can be used to serialize this window. Empty list indicates not serializable. '\n        if self.actions_on_close or self.actions_on_focus_change or self.actions_on_removal:\n            # such windows are typically UI kittens. The actions are not\n            # serializable anyway, so skip.\n            return []\n        ans = ['launch']\n        if cwd:\n            ans.append(f'--cwd={cwd}')\n        if self.allow_remote_control:\n            ans.append('--allow-remote-control')\n        if self.remote_control_passwords:\n            import shlex\n            for pw, rcp_items in self.remote_control_passwords.items():\n                ans.append(f'--remote-control-password={shlex.join((pw,) + tuple(rcp_items))}')\n        if self.creation_spec:\n            if self.creation_spec.env:\n                for k, v in self.creation_spec.env:\n                    if k not in ('KITTY_PIPE_DATA',):\n                        ans.append(f'--env={k}={v}')\n            for cs in self.creation_spec.colors:\n                ans.append(f'--color={cs}')\n            for wr in self.creation_spec.watchers:\n                ans.append(f'--watcher={wr}')\n            if self.creation_spec.hold:\n                ans.append('--hold')\n            if self.creation_spec.hold_after_ssh:\n                ans.append('--hold-after-ssh')\n        ans.extend(f'--var={k}={v}' for k, v in self.user_vars.items())\n        ans.extend(self.padding.as_launch_args())\n        ans.extend(self.margin.as_launch_args('margin'))\n        if self.override_title:\n            ans.append(f'--title={self.override_title}')\n        wl = get_window_logo_settings_if_not_default(self.os_window_id, self.tab_id, self.id)\n        if wl is not None:\n            logo_path, logo_alpha, logo_pos = wl\n            ans.extend((f'--logo={logo_path}', f'--logo-alpha={logo_alpha}'))\n            xpos = ypos = ''\n            if logo_pos[0] == logo_pos[2] != 0.5:\n                xpos = 'right' if logo_pos[0] else 'left'\n            if logo_pos[1] == logo_pos[3] != 0.5:\n                ypos = 'bottom' if logo_pos[1] else 'top'\n            lpos = 'center'\n            if xpos or ypos:\n                lpos = (f'{ypos}-{xpos}' if ypos else xpos) if xpos else ypos\n            ans.append(f'--logo-position={lpos}')\n\n        if is_overlay:\n            t = 'overlay-main' if self.overlay_type is OverlayType.main else 'overlay'\n            ans.append(f'--type={t}')\n\n        from kittens.ssh.utils import is_kitten_cmdline as is_ssh_kitten_cmdline\n        from kittens.ssh.utils import remove_env_var_from_cmdline, set_cwd_in_cmdline, set_single_env_var_in_cmdline\n        cmd: list[str] = []\n        if self.creation_spec and self.creation_spec.cmd:\n            if self.creation_spec.cmd != resolved_shell(get_options()):\n                cmd = self.creation_spec.cmd\n                if is_ssh_kitten_cmdline(cmd):\n                    if self.at_prompt:\n                        if self.screen.last_reported_cwd:\n                            set_cwd_in_cmdline(path_from_osc7_url(self.screen.last_reported_cwd), cmd)\n        unserialize_data: dict[str, int | list[str] | str] = {'id': self.id}\n        if not cmd and ser_opts.use_foreground_process:\n            def make_exe_absolute(cmd: list[str], pid: int) -> None:\n                if cmd and not os.path.isabs(cmd[0]):\n                    with suppress(Exception):\n                        from .child import abspath_of_exe\n                        cmd[0] = abspath_of_exe(pid)\n            kssh_cmdline = self.ssh_kitten_cmdline()\n            if kssh_cmdline:\n                remove_env_var_from_cmdline('KITTY_SI_RUN_COMMAND_AT_STARTUP', kssh_cmdline)\n                if self.at_prompt:\n                    if self.screen.last_reported_cwd:\n                        set_cwd_in_cmdline(path_from_osc7_url(self.screen.last_reported_cwd), kssh_cmdline)\n                else:\n                    if self.last_cmd_cmdline:\n                        set_single_env_var_in_cmdline('KITTY_SI_RUN_COMMAND_AT_STARTUP', self.last_cmd_cmdline, kssh_cmdline)\n                unserialize_data['cmd_at_shell_startup'] = kssh_cmdline\n            elif not self.at_prompt:\n                if self.last_cmd_cmdline:\n                    unserialize_data['cmd_at_shell_startup'] = self.last_cmd_cmdline\n                elif self.child.pid != (pid := self.child.pid_for_cwd) and pid is not None:\n                    # we have a shell running some command\n                    with suppress(Exception):\n                        fcmd = self.child.cmdline_of_pid(pid)\n                        if fcmd:\n                            make_exe_absolute(fcmd, pid)\n                            unserialize_data['cmd_at_shell_startup'] = fcmd\n        ans.insert(1, unserialize_launch_flag + json.dumps(unserialize_data))\n        ans.extend(cmd)\n        return ans\n    # }}}\n\n    # actions {{{\n\n    @ac('sc', 'Show scrollback in a pager like less')\n    def show_scrollback(self) -> Optional['Window']:\n        text = self.as_text(as_ansi=True, add_history=True, add_wrap_markers=True)\n        data = self.pipe_data(text, has_wrap_markers=True)\n        cursor_on_screen = self.screen.scrolled_by < self.screen.lines - self.screen.cursor.y\n        return get_boss().display_scrollback(self, data['text'], data['input_line_number'], report_cursor=cursor_on_screen)\n\n    @ac('sc', '''\n        Search scrollback in a pager like less. If there is selected text, it is automatically searched for.\n        Note that this assumes that pressing the / key triggers search mode in the page configured as the\n        scrollback pager.\n    ''')\n    def search_scrollback(self) -> None:\n        text = self.text_for_selection()\n        w = self.show_scrollback()\n        if w is not None:\n            w.send_key('/')\n            if text:\n                btext = text.encode()\n                sanitized = replace_c0_codes_except_nl_space_tab(btext)\n                if not w.screen.in_bracketed_paste_mode:\n                    sanitized = sanitized.replace(b'\\n', b'\\x1bE')\n                w.screen.paste_bytes(sanitized)\n                w.send_key('enter')\n\n    def show_cmd_output(self, which: CommandOutput, title: str = 'Command output', as_ansi: bool = True, add_wrap_markers: bool = True) -> None:\n        text = self.cmd_output(which, as_ansi=as_ansi, add_wrap_markers=add_wrap_markers)\n        text = text.replace('\\r\\n', '\\n').replace('\\r', '\\n')\n        get_boss().display_scrollback(self, text, title=title, report_cursor=False)\n\n    @ac('sc', '''\n        Show output from the first shell command on screen in a pager like less\n\n        Requires :ref:`shell_integration` to work\n        ''')\n    def show_first_command_output_on_screen(self) -> None:\n        self.show_cmd_output(CommandOutput.first_on_screen, 'First command output on screen')\n\n    @ac('sc', '''\n        Show output from the last shell command in a pager like less\n\n        Requires :ref:`shell_integration` to work\n        ''')\n    def show_last_command_output(self) -> None:\n        self.show_cmd_output(CommandOutput.last_run, 'Last command output')\n\n    @ac('sc', '''\n        Show the first command output below the last scrolled position via scroll_to_prompt\n        or the last mouse clicked command output in a pager like less\n\n        Requires :ref:`shell_integration` to work\n        ''')\n    def show_last_visited_command_output(self) -> None:\n        self.show_cmd_output(CommandOutput.last_visited, 'Last visited command output')\n\n    @ac('sc', '''\n        Show the last non-empty output from a shell command in a pager like less\n\n        Requires :ref:`shell_integration` to work\n        ''')\n    def show_last_non_empty_command_output(self) -> None:\n        self.show_cmd_output(CommandOutput.last_non_empty, 'Last non-empty command output')\n\n    @ac('cp', '''\n        Copy the last non-empty output from a shell command to the clipboard\n\n        Requires :ref:`shell_integration` to work\n        ''')\n    def copy_last_command_output(self) -> None:\n        text = self.cmd_output(CommandOutput.last_non_empty, as_ansi=False, add_wrap_markers=False)\n        if text:\n            set_clipboard_string(text)\n\n    @ac('cp', 'Paste the specified text into the current window. ANSI C escapes are decoded.')\n    def paste(self, text: str) -> None:\n        self.paste_with_actions(text)\n\n    @ac('cp', 'Copy the selected text from the active window to the clipboard')\n    def copy_to_clipboard(self) -> None:\n        text = self.text_for_selection()\n        if text:\n            set_clipboard_string(text)\n\n    @ac('cp', 'Copy the selected text from the active window to the clipboard with ANSI formatting codes')\n    def copy_ansi_to_clipboard(self) -> None:\n        text = self.text_for_selection(as_ansi=True)\n        if text:\n            set_clipboard_string(text)\n\n    def encoded_key(self, key_event: KeyEvent) -> bytes:\n        return encode_key_for_tty(\n            key=key_event.key, shifted_key=key_event.shifted_key, alternate_key=key_event.alternate_key,\n            mods=key_event.mods, action=key_event.action, text=key_event.text,\n            key_encoding_flags=self.screen.current_key_encoding_flags(),\n            cursor_key_mode=self.screen.cursor_key_mode,\n        ).encode('ascii')\n\n    @ac('cp', 'Copy the selected text from the active window to the clipboard, if no selection, send SIGINT (aka :kbd:`ctrl+c`)')\n    def copy_or_interrupt(self) -> None:\n        text = self.text_for_selection()\n        if text:\n            set_clipboard_string(text)\n        else:\n            self.scroll_end()\n            self.write_to_child(self.encoded_key(KeyEvent(key=ord('c'), mods=GLFW_MOD_CONTROL)))\n\n    @ac('cp', 'Copy the selected text from the active window to the clipboard, if no selection,'\n        ' pass the key through to the application running in the terminal.')\n    def copy_or_noop(self) -> bool:\n        text = self.text_for_selection()\n        if text:\n            set_clipboard_string(text)\n            return False\n        return True\n\n    @ac('cp', 'Copy the selected text from the active window to the clipboard and clear selection, if no selection, send SIGINT (aka :kbd:`ctrl+c`)')\n    def copy_and_clear_or_interrupt(self) -> None:\n        self.copy_or_interrupt()\n        self.screen.clear_selection()\n\n    @ac('cp', 'Copy the selected text from the active window to the clipboard,'\n        ' if no selection, copy the last command output (requires shell integration to work)')\n    def copy_selection_or_last_command_output(self) -> None:\n        if (text := self.text_for_selection() or self.cmd_output(CommandOutput.last_non_empty, as_ansi=False, add_wrap_markers=False)):\n            set_clipboard_string(text)\n\n    @ac('cp', 'Pass the selected text from the active window to the specified program')\n    def pass_selection_to_program(self, *args: str) -> None:\n        cwd = self.cwd_of_child\n        text = self.text_for_selection()\n        if text:\n            if args:\n                open_cmd(args, text, cwd=cwd)\n            else:\n                open_url(text, cwd=cwd)\n\n    @ac('cp', 'Clear the current selection')\n    def clear_selection(self) -> None:\n        self.screen.clear_selection()\n\n    def scroll_fractional_lines(self, amt: float) -> bool | None:\n        ' Scroll fractionally, negative values are up and positive values are down '\n        if self.screen.is_main_linebuf():\n            self.screen.fractional_scroll(amt)\n            return None\n        return True\n\n    @ac('sc', 'Scroll up by one line when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.')\n    def scroll_line_up(self) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll(SCROLL_LINE, True)\n            return None\n        return True\n\n    @ac('sc', 'Scroll down by one line when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.')\n    def scroll_line_down(self) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll(SCROLL_LINE, False)\n            return None\n        return True\n\n    @ac('sc', 'Scroll up by one page when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.')\n    def scroll_page_up(self) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll(SCROLL_PAGE, True)\n            return None\n        return True\n\n    @ac('sc', 'Scroll down by one page when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.')\n    def scroll_page_down(self) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll(SCROLL_PAGE, False)\n            return None\n        return True\n\n    @ac('sc', 'Scroll to the top of the scrollback buffer when in main screen')\n    def scroll_home(self) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll(SCROLL_FULL, True)\n            return None\n        return True\n\n    @ac('sc', 'Scroll to the bottom of the scrollback buffer when in main screen')\n    def scroll_end(self) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll(SCROLL_FULL, False)\n            return None\n        return True\n\n    @ac('sc', '''\n        Scroll to the previous/next shell command prompt\n        Allows easy jumping from one command to the next. Requires working\n        :ref:`shell_integration`. Takes two optional numbers as arguments:\n\n        The first is the number of prompts to jump; negative values jump up and\n        positive values jump down. A value of zero will jump to the last prompt\n        visited by this action. Defaults to -1\n\n        The second is the number of lines to show above the prompt that was\n        jumped to. This is somewhat like `less`'s `--jump-target` option or\n        vim's `scrolloff` setting. Defaults to 0.\n\n        For example::\n\n            map ctrl+p scroll_to_prompt -1 3  # jump to previous, showing 3 lines prior\n            map ctrl+n scroll_to_prompt 1     # jump to next\n            map ctrl+o scroll_to_prompt 0     # jump to last visited\n        ''')\n    def scroll_to_prompt(self, num_of_prompts: int = -1, scroll_offset: int = 0) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll_to_prompt(num_of_prompts, scroll_offset)\n            return None\n        return True\n\n    @ac('sc', 'Scroll prompt to the top of the screen, filling screen with empty lines, when in main screen.'\n        ' To avoid putting the lines above the prompt into the scrollback use scroll_prompt_to_top y')\n    def scroll_prompt_to_top(self, clear_scrollback: bool = False) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll_until_cursor_prompt(not clear_scrollback)\n            if self.screen.scrolled_by > 0:\n                self.scroll_end()\n            return None\n        return True\n\n    @ac('sc', 'Scroll prompt to the bottom of the screen, filling in extra lines from the scrollback buffer, when in main screen')\n    def scroll_prompt_to_bottom(self) -> bool | None:\n        if self.screen.is_main_linebuf():\n            self.screen.scroll_prompt_to_bottom()\n            return None\n        return True\n\n    @ac('mk', 'Toggle the current marker on/off')\n    def toggle_marker(self, ftype: str, spec: str | tuple[tuple[int, str], ...], flags: int) -> None:\n        from .marks import marker_from_spec\n        key = ftype, spec\n        if key == self.current_marker_spec:\n            self.remove_marker()\n            return\n        self.screen.set_marker(marker_from_spec(ftype, spec, flags))\n        self.current_marker_spec = key\n\n    def set_marker(self, spec: str | Sequence[str]) -> None:\n        from .marks import marker_from_spec\n        from .options.utils import parse_marker_spec, toggle_marker\n        if isinstance(spec, str):\n            func, (ftype, spec_, flags) = toggle_marker('toggle_marker', spec)\n        else:\n            ftype, spec_, flags = parse_marker_spec(spec[0], spec[1:])\n        key = ftype, spec_\n        self.screen.set_marker(marker_from_spec(ftype, spec_, flags))\n        self.current_marker_spec = key\n\n    @ac('mk', 'Remove a previously created marker')\n    def remove_marker(self) -> None:\n        if self.current_marker_spec is not None:\n            self.screen.set_marker()\n            self.current_marker_spec = None\n\n    @ac('mk', 'Scroll to the next or previous mark of the specified type')\n    def scroll_to_mark(self, prev: bool = True, mark: int = 0) -> None:\n        self.screen.scroll_to_next_mark(mark, prev)\n\n    @ac('misc', '''\n        Send the specified SIGNAL to the foreground process in the active window\n\n        For example::\n\n            map f1 signal_child SIGTERM\n        ''')\n    def signal_child(self, *signals: int) -> None:\n        pid = self.child.pid_for_cwd\n        if pid is not None:\n            for sig in signals:\n                os.kill(pid, sig)\n\n    @ac('misc', '''\n    Display the specified kitty documentation, preferring a local copy, if found.\n\n    For example::\n\n        # show the config docs\n        map f1 show_kitty_doc conf\n        # show the ssh kitten docs\n        map f1 show_kitty_doc kittens/ssh\n    ''')\n    def show_kitty_doc(self, which: str = '') -> None:\n        url = docs_url(which)\n        get_boss().open_url(url)\n    # }}}\n\n\ndef set_pointer_shape(screen: Screen, value: str, os_window_id: int = 0) -> str:\n    op, ret = '=', ''\n    if value and value[0] in '><=?':\n        op = value[0]\n        value = value[1:]\n    if op in '=>':\n        for v in value.split(','):\n            if v or op == '=':\n                screen.change_pointer_shape(op, v)\n        if os_window_id and current_focused_os_window_id() == os_window_id:\n            update_pointer_shape(os_window_id)\n    elif op == '<':\n        screen.change_pointer_shape('<', '')\n        if os_window_id and current_focused_os_window_id() == os_window_id:\n            update_pointer_shape(os_window_id)\n    elif op == '?':\n        ans = []\n        for q in value.split(','):\n            if is_css_pointer_name_valid(q):\n                ans.append('1')\n            else:\n                if q == '__default__':\n                    ans.append(pointer_name_to_css_name(get_options().default_pointer_shape))\n                elif q == '__grabbed__':\n                    ans.append(pointer_name_to_css_name(get_options().pointer_shape_when_grabbed))\n                elif q == '__current__':\n                    ans.append(screen.current_pointer_shape())\n                else:\n                    ans.append('0')\n        ret = ','.join(ans)\n    return ret\n"
  },
  {
    "path": "kitty/window_list.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport weakref\nfrom collections import deque\nfrom collections.abc import Iterator\nfrom contextlib import suppress\nfrom itertools import count\nfrom typing import Any, Deque, Sequence, Union\n\nfrom .fast_data_types import Color, get_options\nfrom .types import OverlayType, WindowGeometry\nfrom .typing_compat import EdgeLiteral, TabType, WindowType\n\nWindowOrId = Union[WindowType, int]\ngroup_id_counter = count(start=1)\n\n\ndef reset_group_id_counter() -> None:\n    global group_id_counter\n    group_id_counter = count(start=1)\n\n\ndef wrap_increment(val: int, num: int, delta: int) -> int:\n    mult = -1 if delta < 0 else 1\n    delta = mult * (abs(delta) % num)\n    return (val + num + delta) % num\n\n\nclass WindowGroup:\n\n    def __init__(self) -> None:\n        self.windows: list[WindowType] = []\n        self.id = next(group_id_counter)\n\n    def __repr__(self) -> str:\n        return f'WindowGroup(id={self.id}, windows={\", \".join(str(w.id) for w in self.windows)})'\n\n    def __len__(self) -> int:\n        return len(self.windows)\n\n    def __bool__(self) -> bool:\n        return bool(self.windows)\n\n    def __iter__(self) -> Iterator[WindowType]:\n        return iter(self.windows)\n\n    def __contains__(self, window: WindowType) -> bool:\n        for w in self.windows:\n            if w is window:\n                return True\n        return False\n\n    def has_window_id(self, wid: int) -> bool:\n        for w in self.windows:\n            if w.id == wid:\n                return True\n        return False\n\n    @property\n    def needs_attention(self) -> bool:\n        for w in self.windows:\n            if w.needs_attention:\n                return True\n        return False\n\n    @property\n    def main_window_id(self) -> int:\n        for w in reversed(self.windows):\n            if w.overlay_type is OverlayType.main:\n                return w.id\n        return self.windows[0].id if self.windows else 0\n\n    @property\n    def active_window_id(self) -> int:\n        return self.windows[-1].id if self.windows else 0\n\n    def add_window(self, window: WindowType, head_of_group: bool = False) -> None:\n        if head_of_group:\n            self.windows.insert(0, window)\n        else:\n            self.windows.append(window)\n\n    def move_window_to_top_of_group(self, window: WindowType) -> bool:\n        try:\n            idx = self.windows.index(window)\n        except ValueError:\n            return False\n        if idx == len(self.windows) - 1:\n            return False\n        del self.windows[idx]\n        self.windows.append(window)\n        return True\n\n    def remove_window(self, window: WindowType) -> None:\n        with suppress(ValueError):\n            self.windows.remove(window)\n\n    def serialize_state(self) -> dict[str, Any]:\n        return {\n            'id': self.id,\n            'windows': tuple(w.serialize_state() for w in self.windows),\n        }\n\n    def serialize_layout_state(self) -> dict[str, Any]:\n        return {\n            'id': self.id,\n            'window_ids': tuple(w.id for w in self.windows),\n        }\n\n    def unserialize_layout_state(self, window_ids: Sequence[int]) -> None:\n        order_map = {wid: i for i, wid in enumerate(window_ids)}\n        def sort_key(w: WindowType) -> int:\n            return order_map.get(w.id, -1)\n        self.windows.sort(key=sort_key)\n\n    def as_simple_dict(self) -> dict[str, Any]:\n        return {\n            'id': self.id,\n            'windows': [w.id for w in self.windows],\n        }\n\n    def decoration(self, which: EdgeLiteral, border_mult: int = 1, is_single_window: bool = False) -> int:\n        if not self.windows:\n            return 0\n        w = self.windows[0]\n        return w.effective_margin(which) + w.effective_border() * border_mult + w.effective_padding(which)\n\n    def effective_padding(self, which: EdgeLiteral) -> int:\n        if not self.windows:\n            return 0\n        w = self.windows[0]\n        return w.effective_padding(which)\n\n    def effective_border(self) -> int:\n        if not self.windows:\n            return 0\n        w = self.windows[0]\n        return w.effective_border()\n\n    def set_geometry(self, geom: WindowGeometry) -> None:\n        for w in self.windows:\n            w.set_geometry(geom)\n\n    @property\n    def default_bg(self) -> Color:\n        if self.windows:\n            w = self.windows[-1]\n            return w.screen.color_profile.default_bg or get_options().background\n        return get_options().background\n\n    @property\n    def geometry(self) -> WindowGeometry | None:\n        if self.windows:\n            w = self.windows[-1]\n            return w.geometry\n        return None\n\n    @property\n    def is_visible_in_layout(self) -> bool:\n        if self.windows:\n            w = self.windows[-1]\n            return w.is_visible_in_layout\n        return False\n\n\nclass WindowList:\n\n    def __init__(self, tab: TabType) -> None:\n        self.all_windows: list[WindowType] = []\n        self.id_map: dict[int, WindowType] = {}\n        self.groups: list[WindowGroup] = []\n        self._active_group_idx: int = -1\n        self.active_group_history: Deque[int] = deque((), 64)\n        self.tabref = weakref.ref(tab)\n\n    def __len__(self) -> int:\n        return len(self.all_windows)\n\n    def __bool__(self) -> bool:\n        return bool(self.all_windows)\n\n    def __iter__(self) -> Iterator[WindowType]:\n        return iter(self.all_windows)\n\n    def __contains__(self, window: WindowOrId) -> bool:\n        q = window if isinstance(window, int) else window.id\n        return q in self.id_map\n\n    def serialize_state(self) -> dict[str, Any]:\n        return {\n            'active_group_idx': self.active_group_idx,\n            'active_group_history': list(self.active_group_history),\n            'window_groups': [g.serialize_state() for g in self.groups]\n        }\n\n    def serialize_layout_state(self) -> dict[str, Any]:\n        return {\n            'active_group_idx': self.active_group_idx,\n            'active_group_history': list(self.active_group_history),\n            'window_groups': [g.serialize_layout_state() for g in self.groups]\n        }\n\n    def unserialize_layout_state(self, state: dict[str, Any], window_id_map: dict[int, int]) -> dict[int, int] | None:\n        if set(window_id_map.values()) != set(self.id_map):\n            # some window in this collection does not correspond to a\n            # serialized window\n            return None\n        ans = {}\n        gmap = {g.id: g for g in self.groups}\n        present_wids_map = {g.id: {w.id for w in g} for g in self.groups}\n\n        def unmapped_group_having_subset_of_windows(wids: Sequence[int]) -> Iterator[WindowGroup]:\n            mapped_wids = set()\n            for wid in wids:\n                new_wid = window_id_map.get(wid)\n                if new_wid is not None:\n                    mapped_wids.add(new_wid)\n            for gid in tuple(gmap):\n                present_wids = present_wids_map[gid]\n                if present_wids.issubset(mapped_wids):\n                    yield gmap.pop(gid)\n                    break\n\n        for wg in state['window_groups']:\n            old_group_id = wg['id']\n            for group in unmapped_group_having_subset_of_windows(wg['window_ids']):\n                ans[old_group_id] = group.id\n        # check that all the groups present were also in the serialized state.\n        # there could have been extra windows/groups in the serialized state,\n        # we ignore them.\n        if len(ans) != len(self.groups):\n            return None\n        gmap = {g.id: g for g in self.groups}\n        groups = []\n        for wg in state['window_groups']:\n            old_group_id = wg['id']\n            if new_group_id := ans.get(old_group_id):\n                groups.append((g := gmap[new_group_id]))\n                new_window_ids = []\n                for old_window_id in wg['window_ids']:\n                    if new_window_id := window_id_map.get(old_window_id):\n                        new_window_ids.append(new_window_id)\n                g.unserialize_layout_state(new_window_ids)\n        self.groups = groups\n        history = []\n        for old_wid in state['active_group_history']:\n            if new_wid := window_id_map.get(old_wid):\n                history.append(new_wid)\n        self.active_group_history = deque(history, 64)\n        return ans\n\n    @property\n    def active_group_idx(self) -> int:\n        return self._active_group_idx\n\n    @property\n    def active_window_history(self) -> list[int]:\n        ans = []\n        seen = set()\n        gid_map = {g.id: g for g in self.groups}\n        for gid in self.active_group_history:\n            g = gid_map.get(gid)\n            if g is not None:\n                w = g.active_window_id\n                if w > 0 and w not in seen:\n                    seen.add(w)\n                    ans.append(w)\n        return ans\n\n    def notify_on_active_window_change(self, old_active_window: WindowType | None, new_active_window: WindowType | None) -> None:\n        if old_active_window is not None:\n            old_active_window.focus_changed(False)\n        if new_active_window is not None:\n            new_active_window.focus_changed(True)\n        tab = self.tabref()\n        if tab is not None:\n            tab.active_window_changed()\n\n    def set_active_group_idx(self, i: int, notify: bool = True) -> bool:\n        changed = False\n        if i != self._active_group_idx and 0 <= i < len(self.groups):\n            old_active_window = self.active_window\n            g = self.active_group\n            if g is not None:\n                with suppress(ValueError):\n                    self.active_group_history.remove(g.id)\n                self.active_group_history.append(g.id)\n            self._active_group_idx = i\n            new_active_window = self.active_window\n            if old_active_window is not new_active_window:\n                if notify:\n                    self.notify_on_active_window_change(old_active_window, new_active_window)\n                changed = True\n        return changed\n\n    def set_active_group(self, group_id: int) -> bool:\n        for i, gr in enumerate(self.groups):\n            if gr.id == group_id:\n                return self.set_active_group_idx(i)\n        return False\n\n    def change_tab(self, tab: TabType) -> None:\n        self.tabref = weakref.ref(tab)\n\n    def iter_windows_with_visibility(self) -> Iterator[tuple[WindowType, bool]]:\n        for g in self.groups:\n            aw = g.active_window_id\n            for window in g:\n                yield window, window.id == aw\n\n    def iter_all_layoutable_groups(self, only_visible: bool = False) -> Iterator[WindowGroup]:\n        return iter(g for g in self.groups if g.is_visible_in_layout) if only_visible else iter(self.groups)\n\n    def iter_windows_with_number(self, only_visible: bool = True) -> Iterator[tuple[int, WindowType]]:\n        for i, g in enumerate(self.groups):\n            if not only_visible or g.is_visible_in_layout:\n                aw = g.active_window_id\n                for window in g:\n                    if window.id == aw:\n                        yield i, window\n                        break\n\n    def make_previous_group_active(self, which: int = 1, notify: bool = True) -> None:\n        which = max(1, which)\n        gid_map = {g.id: i for i, g in enumerate(self.groups)}\n        num = len(self.active_group_history)\n        for i in range(num):\n            idx = num - i - 1\n            gid = self.active_group_history[idx]\n            x = gid_map.get(gid)\n            if x is not None:\n                which -= 1\n                if which < 1:\n                    self.set_active_group_idx(x, notify=notify)\n                    return\n        self.set_active_group_idx(len(self.groups) - 1, notify=notify)\n\n    @property\n    def num_groups(self) -> int:\n        return len(self.groups)\n\n    def window_for_id(self, x: int) -> WindowType | None:\n        return self.id_map.get(x)\n\n    def group_for_window(self, x: WindowOrId) -> WindowGroup | None:\n        q = self.id_map[x] if isinstance(x, int) else x\n        for g in self.groups:\n            if q in g:\n                return g\n        return None\n\n    def group_for_id(self, gid: int) -> WindowGroup | None:\n        for g in self.groups:\n            if g.id == gid:\n                return g\n        return None\n\n    def group_idx_for_window(self, x: WindowOrId) -> int | None:\n        q = self.id_map[x] if isinstance(x, int) else x\n        for i, g in enumerate(self.groups):\n            if q in g:\n                return i\n        return None\n\n    def move_window_to_top_of_group(self, window: WindowType) -> bool:\n        g = self.group_for_window(window)\n        if g is None:\n            return False\n        before = self.active_window\n        if not g.move_window_to_top_of_group(window):\n            return False\n        after = self.active_window\n        changed = before is not after\n        if changed:\n            self.notify_on_active_window_change(before, after)\n        return changed\n\n    def windows_in_group_of(self, x: WindowOrId) -> Iterator[WindowType]:\n        g = self.group_for_window(x)\n        if g is not None:\n            return iter(g)\n        return iter(())\n\n    @property\n    def active_group(self) -> WindowGroup | None:\n        with suppress(Exception):\n            return self.groups[self.active_group_idx]\n        return None\n\n    @property\n    def active_window(self) -> WindowType | None:\n        with suppress(Exception):\n            return self.id_map[self.groups[self.active_group_idx].active_window_id]\n        return None\n\n    @property\n    def active_group_main(self) -> WindowType | None:\n        with suppress(Exception):\n            return self.id_map[self.groups[self.active_group_idx].main_window_id]\n        return None\n\n    def set_active_window_group_for(self, x: WindowOrId, for_keep_focus: WindowType | None = None) -> None:\n        try:\n            q = self.id_map[x] if isinstance(x, int) else x\n        except KeyError:\n            return\n        for i, group in enumerate(self.groups):\n            if q in group:\n                self.set_active_group_idx(i)\n                h = self.active_group_history\n                if for_keep_focus and len(h) > 2 and h[-2] == for_keep_focus.id and h[-1] != for_keep_focus.id:\n                    h.pop()\n                    h.pop()\n                break\n\n    def add_window(\n        self,\n        window: WindowType,\n        group_of: WindowOrId | None = None,\n        next_to: WindowOrId | None = None,\n        before: bool = False,\n        make_active: bool = True,\n        head_of_group: bool = False,\n    ) -> WindowGroup:\n        self.all_windows.append(window)\n        self.id_map[window.id] = window\n        target_group: WindowGroup | None = None\n\n        if group_of is not None:\n            target_group = self.group_for_window(group_of)\n        if target_group is None and next_to is not None:\n            q = self.id_map[next_to] if isinstance(next_to, int) else next_to\n            pos = -1\n            for i, g in enumerate(self.groups):\n                if q in g:\n                    pos = i\n                    break\n            if pos > -1:\n                target_group = WindowGroup()\n                self.groups.insert(pos + (0 if before else 1), target_group)\n        if target_group is None:\n            target_group = WindowGroup()\n            if before:\n                self.groups.insert(0, target_group)\n            else:\n                self.groups.append(target_group)\n\n        old_active_window = self.active_window\n        target_group.add_window(window, head_of_group=head_of_group)\n        if make_active:\n            for i, g in enumerate(self.groups):\n                if g is target_group:\n                    self.set_active_group_idx(i, notify=False)\n                    break\n        new_active_window = self.active_window\n        if new_active_window is not old_active_window:\n            self.notify_on_active_window_change(old_active_window, new_active_window)\n        return target_group\n\n    def remove_window(self, x: WindowOrId) -> None:\n        old_active_window = self.active_window\n        q = self.id_map[x] if isinstance(x, int) else x\n        try:\n            self.all_windows.remove(q)\n        except ValueError:\n            pass\n        self.id_map.pop(q.id, None)\n        for i, g in enumerate(tuple(self.groups)):\n            g.remove_window(q)\n            if not g:\n                del self.groups[i]\n                if self.groups:\n                    if self.active_group_idx == i:\n                        self.make_previous_group_active(notify=False)\n                    elif self.active_group_idx >= len(self.groups):\n                        self._active_group_idx -= 1\n                else:\n                    self._active_group_idx = -1\n                break\n        new_active_window = self.active_window\n        if old_active_window is not new_active_window:\n            self.notify_on_active_window_change(old_active_window, new_active_window)\n\n    def active_window_in_nth_group(self, n: int, clamp: bool = False) -> WindowType | None:\n        if clamp:\n            n = max(0, min(n, self.num_groups - 1))\n        if 0 <= n < self.num_groups:\n            return self.id_map.get(self.groups[n].active_window_id)\n        return None\n\n    def active_window_in_group_id(self, group_id: int) -> WindowType | None:\n        for g in self.groups:\n            if g.id == group_id:\n                return self.id_map.get(g.active_window_id)\n        return None\n\n    def activate_next_window_group(self, delta: int) -> None:\n        self.set_active_group_idx(wrap_increment(self.active_group_idx, self.num_groups, delta))\n\n    def move_window_group(self, by: int | None = None, to_group: int | None = None) -> bool:\n        if self.active_group_idx < 0 or not self.groups:\n            return False\n        target = -1\n        if by is not None:\n            target = wrap_increment(self.active_group_idx, self.num_groups, by)\n        if to_group is not None:\n            for i, group in enumerate(self.groups):\n                if group.id == to_group:\n                    target = i\n                    break\n        if target > -1:\n            if target == self.active_group_idx:\n                return False\n            self.groups[self.active_group_idx], self.groups[target] = self.groups[target], self.groups[self.active_group_idx]\n            self.set_active_group_idx(target)\n            return True\n        return False\n\n    def compute_needs_borders_map(self, draw_active_borders: bool) -> dict[int, bool]:\n        ag = self.active_group\n        return {gr.id: ((gr is ag and draw_active_borders) or gr.needs_attention) for gr in self.groups}\n\n    @property\n    def has_more_than_one_visible_group(self) -> bool:\n        ans = 0\n        for gr in self.groups:\n            if gr.is_visible_in_layout:\n                ans += 1\n                if ans > 1:\n                    return True\n        return False\n"
  },
  {
    "path": "kitty/window_logo.c",
    "content": "/*\n * window_logo.c\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n\n#include \"state.h\"\n#include \"window_logo.h\"\n#include <sys/mman.h>\n\ntypedef struct WindowLogoItem {\n    WindowLogo wl;\n    unsigned int refcnt;\n    char *path;\n    window_logo_id_t id;\n} WindowLogoItem;\n\n#define NAME hash_by_id\n#define KEY_TY window_logo_id_t\n#define VAL_TY WindowLogoItem*\n#include \"kitty-verstable.h\"\n#define id_for_loop(table) vt_create_for_loop(hash_by_id_itr, itr, &(table)->by_id)\n\n#define NAME hash_by_path\n#define KEY_TY const char*\n#define VAL_TY WindowLogoItem*\n#include \"kitty-verstable.h\"\n\n\nstruct WindowLogoTable {\n    hash_by_id by_id;\n    hash_by_path by_path;\n};\n\nstatic void\nfree_window_logo_bitmap(WindowLogo *wl) {\n    if (!wl->bitmap) return;\n    if (wl->mmap_size) {\n        if (munmap(wl->bitmap, wl->mmap_size) != 0) log_error(\"Failed to unmap window logo bitmap with error: %s\", strerror(errno));\n    } else free(wl->bitmap);\n    wl->bitmap = NULL; wl->mmap_size = 0;\n}\n\nstatic void\nfree_window_logo(WindowLogoItem **itemref) {\n    WindowLogoItem *item = *itemref;\n    free(item->path);\n    free_window_logo_bitmap(&item->wl);\n    if (item->wl.texture_id) free_texture(&item->wl.texture_id);\n    free(item); itemref = NULL;\n}\n\nstatic void\nsend_logo_to_gpu(WindowLogo *s) {\n    size_t off = s->mmap_size ? s->mmap_size - ((size_t)4) * s->width * s->height : 0;\n    send_image_to_gpu(&s->texture_id, s->bitmap + off, s->width, s->height, false, true, true, REPEAT_CLAMP);\n    free_window_logo_bitmap(s);\n}\n\n\nvoid\nset_on_gpu_state(WindowLogo *s, bool on_gpu) {\n    if (s->load_from_disk_ok) {\n        if (on_gpu) { if (!s->texture_id) send_logo_to_gpu(s); }\n        else if (s->texture_id) free_texture(&s->texture_id);\n    }\n}\n\nconst char*\nwindow_logo_path_for_id(WindowLogoTable *head, window_logo_id_t id) {\n    vt_create_for_loop(hash_by_path_itr, itr, &head->by_path) {\n        if (itr.data->val->id == id) return itr.data->key;\n    }\n    return NULL;\n}\n\nwindow_logo_id_t\nfind_or_create_window_logo(WindowLogoTable *head, const char *path, void *png_data, size_t png_data_size) {\n    hash_by_path_itr n = vt_get(&head->by_path, path);\n    if (!vt_is_end(n)) { n.data->val->refcnt++; return n.data->val->id; }\n    WindowLogoItem *s = calloc(1, sizeof *s);\n    if (!s) { PyErr_NoMemory(); return 0; }\n    s->path = strdup(path);\n    if (!s->path) { free(s); PyErr_NoMemory(); return 0; }\n    size_t size;\n    bool ok = false;\n    if (png_data == NULL || !png_data_size) {\n        ok = image_path_to_bitmap(path, &s->wl.bitmap, &s->wl.width, &s->wl.height, &s->wl.mmap_size);\n    } else {\n        ok = png_from_data(png_data, png_data_size, path, &s->wl.bitmap, &s->wl.width, &s->wl.height, &size);\n    }\n    if (ok) s->wl.load_from_disk_ok = true;\n    s->refcnt++;\n    static window_logo_id_t idc = 0;\n    s->id = ++idc;\n    if (vt_is_end(vt_insert(&head->by_path, s->path, s))) { free_window_logo(&s); PyErr_NoMemory(); return 0; }\n    if (vt_is_end(vt_insert(&head->by_id, s->id, s))) { vt_erase(&head->by_path, s->path); free_window_logo(&s); PyErr_NoMemory(); return 0; }\n    return s->id;\n}\n\nWindowLogo*\nfind_window_logo(WindowLogoTable *table, window_logo_id_t id) {\n    hash_by_id_itr n = vt_get(&table->by_id, id);\n    if (vt_is_end(n)) return NULL;\n    return &n.data->val->wl;\n}\n\nvoid\ndecref_window_logo(WindowLogoTable *table, window_logo_id_t id) {\n    hash_by_id_itr n = vt_get(&table->by_id, id);\n    if (!vt_is_end(n)) {\n        WindowLogoItem *s = n.data->val;\n        if (s->refcnt < 2) {\n            vt_erase(&table->by_id, s->id); vt_erase(&table->by_path, s->path);\n            free_window_logo(&s);\n        }\n        else s->refcnt--;\n    }\n}\n\nWindowLogoTable*\nalloc_window_logo_table(void) {\n    WindowLogoTable *ans = calloc(1, sizeof(WindowLogoTable));\n    if (ans) { vt_init(&ans->by_path); vt_init(&ans->by_id); }\n    return ans;\n}\n\nvoid\nfree_window_logo_table(WindowLogoTable **table) {\n    id_for_loop(*table) free_window_logo(&itr.data->val);\n    vt_cleanup(&(*table)->by_id); vt_cleanup(&(*table)->by_path);\n    free(*table); *table = NULL;\n}\n"
  },
  {
    "path": "kitty/window_logo.h",
    "content": "/*\n * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>\n *\n * Distributed under terms of the GPL3 license.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>\n\ntypedef unsigned int window_logo_id_t;\n\ntypedef struct WindowLogo {\n    unsigned int height, width;\n    bool load_from_disk_ok;\n    uint32_t texture_id;\n    uint8_t* bitmap;\n    size_t mmap_size;\n} WindowLogo;\n\ntypedef struct WindowLogoTable WindowLogoTable;\n\nwindow_logo_id_t\nfind_or_create_window_logo(WindowLogoTable *table, const char *path, void *png_data, size_t png_data_size);\n\nWindowLogo*\nfind_window_logo(WindowLogoTable *table, window_logo_id_t id);\n\nconst char*\nwindow_logo_path_for_id(WindowLogoTable *head, window_logo_id_t id);\n\nvoid\ndecref_window_logo(WindowLogoTable *table, window_logo_id_t id);\n\nvoid\nset_on_gpu_state(WindowLogo *logo, bool on_gpu);\n\nWindowLogoTable*\nalloc_window_logo_table(void);\n\nvoid\nfree_window_logo_table(WindowLogoTable **table);\n"
  },
  {
    "path": "kitty/window_title_bar.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2024, kitty contributors\n\nimport os\nfrom functools import lru_cache\nfrom typing import Any, NamedTuple\n\nfrom .constants import config_dir\nfrom .fast_data_types import (\n    DECAWM,\n    Screen,\n    get_options,\n)\nfrom .rgb import color_as_sgr, color_from_int, to_color\nfrom .tab_bar import draw_attributed_string, safe_builtins\nfrom .types import WindowGeometry, run_once\nfrom .utils import color_as_int, log_error\n\n\n@lru_cache\ndef _report_template_failure(template: str, e: str) -> None:\n    log_error(f'Invalid window title template: \"{template}\" with error: {e}')\n\n\n@lru_cache\ndef _compile_template(template: str) -> Any:\n    try:\n        return compile('f\"\"\"' + template + '\"\"\"', '<window_title_template>', 'eval')\n    except Exception as e:\n        _report_template_failure(template, str(e))\n\n\ndef _resolve_color(opt_val: Any, fallback_val: Any) -> Any:\n    if opt_val is None:\n        return fallback_val\n    return opt_val\n\n\nclass WindowTitleColorFormatter:\n    is_active: bool = False\n\n    def __init__(self, which: str):\n        self.which = which\n\n    def __getattr__(self, name: str) -> str:\n        q = name\n        if q == 'default':\n            ans = '9'\n        elif q == 'window':\n            opts = get_options()\n            if self.is_active:\n                fg_color = _resolve_color(opts.window_title_bar_active_foreground, opts.active_tab_foreground)\n                bg_color = _resolve_color(opts.window_title_bar_active_background, opts.active_tab_background)\n                col = color_from_int(color_as_int(fg_color if self.which == '3' else bg_color))\n            else:\n                fg_color = _resolve_color(opts.window_title_bar_inactive_foreground, opts.inactive_tab_foreground)\n                bg_color = _resolve_color(opts.window_title_bar_inactive_background, opts.inactive_tab_background)\n                col = color_from_int(color_as_int(fg_color if self.which == '3' else bg_color))\n            ans = f'8{color_as_sgr(col)}'\n        elif q.startswith('color'):\n            ans = f'8:5:{int(q[5:])}'\n        else:\n            if name.startswith('_'):\n                q = f'#{name[1:]}'\n            c = to_color(q)\n            if c is None:\n                raise AttributeError(f'{name} is not a valid color')\n            ans = f'8{color_as_sgr(c)}'\n        return f'\\x1b[{self.which}{ans}m'\n\n\nclass WindowTitleFormatter:\n    reset = '\\x1b[0m'\n    fg = WindowTitleColorFormatter('3')\n    bg = WindowTitleColorFormatter('4')\n    bold = '\\x1b[1m'\n    nobold = '\\x1b[22m'\n    italic = '\\x1b[3m'\n    noitalic = '\\x1b[23m'\n\n\nclass WindowTitleData(NamedTuple):\n    title: str\n    is_active: bool\n    window_id: int\n    tab_id: int\n    needs_attention: bool = False\n    has_activity_since_last_focus: bool = False\n\n\n@run_once\ndef load_custom_window_title_bar_module() -> dict[str, Any]:\n    import runpy\n    import traceback\n    try:\n        return runpy.run_path(os.path.join(config_dir, 'window_title_bar.py'))\n    except FileNotFoundError:\n        return {}\n    except Exception as e:\n        traceback.print_exc()\n        log_error(f'Failed to load custom window_title_bar.py module with error: {e}')\n        return {}\n\n\ndef _get_custom_draw_result(data: WindowTitleData) -> str | None:\n    m = load_custom_window_title_bar_module()\n    func = m.get('draw_window_title')\n    if func is None:\n        return None\n    try:\n        return str(func(data))\n    except Exception as e:\n        log_error(f'Custom draw_window_title function failed with error: {e}')\n        return None\n\n\ndef clear_caches() -> None:\n    load_custom_window_title_bar_module.clear_cached()\n\n\nclass WindowTitleBarScreen:\n    def __init__(self, os_window_id: int, cell_width: int, cell_height: int):\n        self.os_window_id = os_window_id\n        self.cell_width = cell_width\n        self.screen = Screen(None, 1, 10, 0, cell_width, cell_height)\n        self.screen.reset_mode(DECAWM)\n\n    def layout(self, geometry: WindowGeometry) -> None:\n        ncells = max(4, (geometry.right - geometry.left) // self.cell_width)\n        self.screen.resize(1, ncells)\n        self.geometry = geometry\n\n    def render(self, data: WindowTitleData, progress_percent: str) -> str:\n        opts = get_options()\n        s = self.screen\n        s.cursor.x = 0\n\n        is_active = data.is_active\n        if is_active:\n            fg_color = _resolve_color(opts.window_title_bar_active_foreground, opts.active_tab_foreground)\n            bg_color = _resolve_color(opts.window_title_bar_active_background, opts.active_tab_background)\n        else:\n            fg_color = _resolve_color(opts.window_title_bar_inactive_foreground, opts.inactive_tab_foreground)\n            bg_color = _resolve_color(opts.window_title_bar_inactive_background, opts.inactive_tab_background)\n\n        s.color_profile.default_fg = fg_color\n        s.color_profile.default_bg = bg_color\n        fg = (color_as_int(fg_color) << 8) | 2\n        bg = (color_as_int(bg_color) << 8) | 2\n\n        s.cursor.fg = fg\n        s.cursor.bg = bg\n\n        template = opts.window_title_template\n        if is_active and opts.active_window_title_template and opts.active_window_title_template != 'none':\n            template = opts.active_window_title_template\n\n        WindowTitleColorFormatter.is_active = is_active\n\n        bell_symbol = opts.bell_on_tab if data.needs_attention else ''\n        activity_symbol = opts.tab_activity_symbol if data.has_activity_since_last_focus else ''\n\n        custom_result = _get_custom_draw_result(data)\n\n        eval_locals = {\n            'title': data.title,\n            'is_active': is_active,\n            'fmt': WindowTitleFormatter,\n            'bell_symbol': bell_symbol,\n            'activity_symbol': activity_symbol,\n            'progress_percent': progress_percent,\n            'custom': custom_result or '',\n        }\n        try:\n            title = eval(_compile_template(template), {'__builtins__': safe_builtins}, eval_locals)\n        except Exception as e:\n            _report_template_failure(template, str(e))\n            title = data.title\n\n        align = opts.window_title_bar_align\n        s.erase_in_line(2, False)\n        draw_attributed_string((title_str := str(title)), s)\n        title_len = s.cursor.x\n        if align != 'left' and (pad := max(0, (s.columns - title_len) // (2 if align == 'center' else 1))):\n            s.cursor.x = 0\n            s.insert_characters(pad)\n            s.cursor.x = 0\n            s.erase_characters(pad)\n        return title_str\n"
  },
  {
    "path": "kitty/xdg.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport re\nfrom contextlib import suppress\n\nfrom kitty.types import run_once\n\n\n@run_once\ndef xdg_data_dirs() -> tuple[str, ...]:\n    return tuple(os.environ.get('XDG_DATA_DIRS', '/usr/local/share/:/usr/share/').split(os.pathsep))\n\n@run_once\ndef xdg_data_home() -> str:\n    return os.environ.get('XDG_DATA_HOME', os.path.expanduser('~/.local/share/'))\n\n\n@run_once\ndef icon_dirs() -> list[str]:\n    ans = []\n    def a(x: str) -> None:\n        if os.path.isdir(x):\n            ans.append(x)\n\n    a(os.path.join(xdg_data_home(), 'icons'))\n    a(os.path.expanduser('~/.icons'))\n    for x in xdg_data_dirs():\n        a(os.path.join(x, 'icons'))\n    return ans\n\n\nclass XDGIconCache:\n\n    def __init__(self) -> None:\n        self.existing_icon_names: set[str] = set()\n        self.scanned = False\n\n    def find_inherited_themes(self, basedir: str, seen_indexes: set[str], themes_to_search: set[str]) -> bool:\n        if basedir not in seen_indexes:\n            seen_indexes.add(basedir)\n            with suppress(OSError), open(os.path.join(basedir, 'index.theme')) as f:\n                raw = f.read()\n                if m := re.search(r'^Inherits\\s*=\\s*(.+?)$', raw, re.MULTILINE):\n                    for x in m.group(1).split(','):\n                        themes_to_search.add(x.strip())\n                return True\n        return False\n\n    def scan(self) -> None:\n        themes_to_search: set[str] = set()\n        self.scanned = True\n        seen_indexes: set[str] = set()\n        for icdir in icon_dirs():\n            if self.find_inherited_themes(os.path.join(icdir, 'default'), seen_indexes, themes_to_search):\n                break\n        themes_to_search.add('hicolor')\n        while True:\n            before = len(themes_to_search)\n            for icdir in icon_dirs():\n                for theme in tuple(themes_to_search):\n                    self.find_inherited_themes(os.path.join(icdir, theme), seen_indexes, themes_to_search)\n            if len(themes_to_search) == before:\n                break\n        for icdir in icon_dirs():\n            for theme in themes_to_search:\n                self.scan_theme_dir(os.path.join(icdir, theme))\n        self.scan_theme_dir('/usr/share/pixmaps')\n\n    def scan_theme_dir(self, base: str) -> None:\n        with suppress(OSError):\n            for (dirpath, dirnames, filenames) in os.walk(base):\n                for q in filenames:\n                    icon_name, sep, ext = q.lower().rpartition('.')\n                    if sep == '.' and ext in ('svg', 'png', 'xpm'):\n                        self.existing_icon_names.add(icon_name)\n\n    def icon_exists(self, name: str) -> bool:\n        if not self.scanned:\n            self.scan()\n        return name.lower() in self.existing_icon_names\n\n\nxdg_icon_cache = XDGIconCache()\nicon_exists = xdg_icon_cache.icon_exists\n\n\nclass AppIconCache:\n    def __init__(self) -> None:\n        self.scanned = False\n        self.lcase_app_name_to_path: dict[str, str] = {}\n        self.lcase_full_name_to_path: dict[str, str] = {}\n        self.icon_name_cache: dict[str, str] = {}\n\n    def scan(self) -> None:\n        self.scanned = True\n        for d in xdg_data_dirs():\n            d = os.path.join(d, 'applications')\n            with suppress(OSError):\n                for (dirpath, dirnames, filenames) in os.walk(d):\n                    for fname in filenames:\n                        if fname.endswith('.desktop'):\n                            path = os.path.join(dirpath, fname)\n                            self.process_desktop_file(path, os.path.relpath(path, d))\n\n    def process_desktop_file(self, path: str, relpath: str) -> None:\n        # file_id = relpath.replace('/', '-')\n        bname = os.path.basename(relpath)\n        parts = bname.split('.')[:-1]\n        appname = parts[-1]\n        self.lcase_app_name_to_path[appname.lower()] = path\n        self.lcase_full_name_to_path['.'.join(parts).lower()] = path\n\n    def icon_for_appname(self, appname: str) -> str:\n        if not self.scanned:\n            self.scan()\n        q = appname.lower()\n        if not appname or q in ('kitty', 'kitten', 'kitten-notify'):\n            return ''\n        path = self.lcase_full_name_to_path.get(q) or self.lcase_app_name_to_path.get(q)\n        if not path:\n            return ''\n        ans = self.icon_name_cache.get(path)\n        if ans is None:\n            try:\n                ans = self.icon_name_cache[path] = self.icon_name_from_desktop_file(path)\n            except OSError:\n                ans = self.icon_name_cache[path] = ''\n\n        return ans\n\n    def icon_name_from_desktop_file(self, path: str) -> str:\n        with open(path) as f:\n            raw = f.read()\n        if m := re.search(r'^Icon\\s*=\\s*(.+?)\\s*?$', raw, re.MULTILINE):\n            return m.group(1)\n        return ''\n\n\napp_icon_cache = AppIconCache()\nicon_for_appname = app_icon_cache.icon_for_appname\n"
  },
  {
    "path": "kitty_tests/GraphemeBreakTest.json",
    "content": "[\n  {\n    \"data\": [\n      \"\\r\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\\n\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"्\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"्̈\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̀\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"‌\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"‍\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"ः\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\",\n      \"̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"्\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"्̈\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̀\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"‌\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"‍\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"ः\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\n\",\n      \"̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"्\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"्̈\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̀\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"‌\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"‍\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"ः\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\u0000\",\n      \"̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"््\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"््̈\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̀\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्‌\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्‍\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्ः\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̀\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"्̀̈\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̀\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀‌\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀‍\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀ः\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"̀̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌्\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌्̈\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̀\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌‌\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌‍\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌ः\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‌̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍्\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍्̈\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̀\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍‌\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍‍\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍ः\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"‍̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦्\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦्̈\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̀\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦‌\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦‍\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦🇦\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦ः\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝्\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝्̈\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̀\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝‌\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝‍\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝🇦\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝۝\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝ः\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝가\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝각\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝क\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝©\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝ \"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝͸\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"۝̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः्\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः्̈\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̀\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः‌\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः‍\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ःः\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ः̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ्\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ्̈\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀ̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ्\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ्̈\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᅠ̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ्\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ्̈\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᆨ̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가्\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가्̈\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가ः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"가̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각्\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각्̈\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각ः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क्\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क्̈\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̀\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क‌\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क‍\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"कः\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©्\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©्̈\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̀\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©‌\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©‍\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©ः\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"©̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ्\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ्̈\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̀\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ‌\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ‍\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ः\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"\\r\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"\\n\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"\\u0000\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸्\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸्̈\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̀\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈̀\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸‌\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈‌\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸‍\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈‍\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"🇦\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"۝\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸ः\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈ः\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"ᅠ\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"ᆨ\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"가\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"각\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"क\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"©\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"͸̈\",\n      \"͸\"\n    ],\n    \"comment\": \"÷ [0.2] <reserved-0378> (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] <reserved-0378> (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"\\r\\n\",\n      \"a\",\n      \"\\n\",\n      \"̈\"\n    ],\n    \"comment\": \"÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [4.0] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ä\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" ‍\",\n      \"ن\"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] ARABIC LETTER NOON (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ن‍\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] ARABIC LETTER NOON (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] SPACE (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᄀᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"각ᆨ\",\n      \"ᄀ\"\n    ],\n    \"comment\": \"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🇦🇧\",\n      \"🇨\",\n      \"b\"\n    ],\n    \"comment\": \"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a\",\n      \"🇦🇧\",\n      \"🇨\",\n      \"b\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a\",\n      \"🇦🇧‍\",\n      \"🇨\",\n      \"b\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a\",\n      \"🇦‍\",\n      \"🇧🇨\",\n      \"b\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a\",\n      \"🇦🇧\",\n      \"🇨🇩\",\n      \"b\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER D (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a‍\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ä\",\n      \"b\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"aः\",\n      \"b\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a\",\n      \"؀b\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) × [9.2] LATIN SMALL LETTER B (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"👶🏿\",\n      \"👶\"\n    ],\n    \"comment\": \"÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a🏿\",\n      \"👶\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a🏿\",\n      \"👶‍🛑\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BABY (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"👶🏿̈‍👶🏿\"\n    ],\n    \"comment\": \"÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"🛑‍🛑\"\n    ],\n    \"comment\": \"÷ [0.2] OCTAGONAL SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a‍\",\n      \"🛑\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"✁‍\",\n      \"✁\"\n    ],\n    \"comment\": \"÷ [0.2] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a‍\",\n      \"✁\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] UPPER BLADE SCISSORS (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क\",\n      \"त\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क्त\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क््त\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क्‍त\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क़‍्त\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctExtendermConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क़्‍त\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctExtendermConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क्त्य\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER YA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क्\",\n      \"a\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"a्\",\n      \"त\"\n    ],\n    \"comment\": \"÷ [0.2] LATIN SMALL LETTER A (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"?्\",\n      \"त\"\n    ],\n    \"comment\": \"÷ [0.2] QUESTION MARK (XXmLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"क््त\"\n    ],\n    \"comment\": \"÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"સૻ્સૻ\"\n    ],\n    \"comment\": \"÷ [0.2] GUJARATI LETTER SA (LinkingConsonant) × [9.0] GUJARATI SIGN SHADDA (Extend_ConjunctExtendermConjunctLinker) × [9.0] GUJARATI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] GUJARATI LETTER SA (LinkingConsonant) × [9.0] GUJARATI SIGN SHADDA (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"မ္ဘ\",\n      \"ာ့\"\n    ],\n    \"comment\": \"÷ [0.2] MYANMAR LETTER MA (LinkingConsonant) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER BHA (LinkingConsonant) ÷ [999.0] MYANMAR VOWEL SIGN AA (XXmLinkingConsonantmExtPict) × [9.0] MYANMAR SIGN DOT BELOW (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"င်္ထ္ထ\"\n    ],\n    \"comment\": \"÷ [0.2] MYANMAR LETTER NGA (LinkingConsonant) × [9.0] MYANMAR SIGN ASAT (Extend_ConjunctExtendermConjunctLinker) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER THA (LinkingConsonant) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER THA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᬒᬁ\",\n      \"ᬲ᭄ᬯ\",\n      \"ᬲ᭄ᬢ᭄ᬬ\",\n      \"ᬲ᭄ᬢᬸ\"\n    ],\n    \"comment\": \"÷ [0.2] BALINESE LETTER OKARA TEDUNG (XXmLinkingConsonantmExtPict) × [9.0] BALINESE SIGN ULU CANDRA (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER WA (LinkingConsonant) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER TA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER YA (LinkingConsonant) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER TA (LinkingConsonant) × [9.0] BALINESE VOWEL SIGN SUKU (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ស្ត្រី\"\n    ],\n    \"comment\": \"÷ [0.2] KHMER LETTER SA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER LETTER TA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER LETTER RO (LinkingConsonant) × [9.0] KHMER VOWEL SIGN II (Extend_ConjunctExtendermConjunctLinker) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᬦ\",\n      \"ᬗ᭄ᬓ\"\n    ],\n    \"comment\": \"÷ [0.2] BALINESE LETTER NA (LinkingConsonant) ÷ [999.0] BALINESE LETTER NGA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER KA (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ᬧ\",\n      \"ᬓ᭄ᬋ\",\n      \"ᬋᬄ\"\n    ],\n    \"comment\": \"÷ [0.2] BALINESE LETTER PA (LinkingConsonant) ÷ [999.0] BALINESE LETTER KA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER RA REPA (LinkingConsonant) ÷ [999.0] BALINESE LETTER RA REPA (LinkingConsonant) × [9.1] BALINESE SIGN BISAH (SpacingMark) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ផ្ឯ\",\n      \"ម\"\n    ],\n    \"comment\": \"÷ [0.2] KHMER LETTER PHA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER INDEPENDENT VOWEL QE (LinkingConsonant) ÷ [999.0] KHMER LETTER MO (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"ហ្ឫ\",\n      \"ទ័\",\n      \"យ\"\n    ],\n    \"comment\": \"÷ [0.2] KHMER LETTER HA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER INDEPENDENT VOWEL RY (LinkingConsonant) ÷ [999.0] KHMER LETTER TO (LinkingConsonant) × [9.0] KHMER SIGN SAMYOK SANNYA (Extend_ConjunctExtendermConjunctLinker) ÷ [999.0] KHMER LETTER YO (LinkingConsonant) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \" \",\n      \"­\",\n      \" \"\n    ],\n    \"comment\": \"÷ [0.2] SPACE (Other) ÷ [0.4] SOFT HYPHEN ÷ [999.0] SPACE (Other) ÷ [0.3]\"\n  },\n  {\n    \"data\": [\n      \"👨‍👩‍👧‍👦\"\n    ],\n    \"comment\": \"÷ [0.2] MAN × [9.0] ZERO WIDTH JOINER × [11.0] WOMAN × [9.0] ZERO WIDTH JOINER × [11.0] GIRL × [9.0] ZERO WIDTH JOINER × [11.0] BOY ÷ [0.3]\"\n  }\n]"
  },
  {
    "path": "kitty_tests/__init__.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport fcntl\nimport io\nimport os\nimport select\nimport shlex\nimport shutil\nimport signal\nimport struct\nimport sys\nimport termios\nimport time\nfrom contextlib import contextmanager, suppress\nfrom functools import wraps\nfrom pty import CHILD, STDIN_FILENO, STDOUT_FILENO, fork\nfrom unittest import TestCase\n\nfrom kitty.config import finalize_keys, finalize_mouse_mappings\nfrom kitty.fast_data_types import TEXT_SIZE_CODE, Cursor, HistoryBuf, LineBuf, Screen, get_options, monotonic, set_options\nfrom kitty.options.parse import merge_result_dicts\nfrom kitty.options.types import Options, defaults\nfrom kitty.rgb import to_color\nfrom kitty.types import MouseEvent\nfrom kitty.utils import read_screen_size\nfrom kitty.window import da1, decode_cmdline, process_remote_print, process_title_from_child\n\n\ndef parse_bytes(screen, data, dump_callback=None):\n    data = memoryview(data)\n    while data:\n        dest = screen.test_create_write_buffer()\n        s = screen.test_commit_write_buffer(data, dest)\n        data = data[s:]\n        screen.test_parse_written_data(dump_callback)\n\n\ndef draw_multicell(\n    screen: Screen, text: str, width: int = 0, scale: int = 1, subscale_n: int = 0, subscale_d: int = 0, vertical_align: int = 0, horizontal_align: int = 0\n    ) -> None:\n    cmd = f'\\x1b]{TEXT_SIZE_CODE};w={width}:s={scale}:n={subscale_n}:d={subscale_d}:v={vertical_align}:h={horizontal_align};{text}\\a'\n    parse_bytes(screen, cmd.encode())\n\n\nclass Callbacks:\n\n    def __init__(self, pty=None) -> None:\n        self.clear()\n        self.pty = pty\n        self.ftc = None\n        self.set_pointer_shape = lambda data: None\n        self.last_cmd_at = 0\n        self.last_cmd_cmdline = ''\n        self.last_cmd_exit_status = sys.maxsize\n\n    def write(self, data) -> None:\n        self.wtcbuf += bytes(data)\n\n    def notify_child_of_resize(self):\n        self.num_of_resize_events += 1\n\n    def on_reset(self) -> None:\n        if self.pty is not None:\n            self.pty.reset_termios_state()\n\n    def color_control(self, code, data) -> None:\n        from kitty.window import color_control\n        response = color_control(self.color_profile, code, data)\n        if response:\n            def p(x):\n                if '@' in x:\n                    return (to_color(x.partition('@')[0]), int(255 * float(x.partition('@')[2])))\n                ans = to_color(x)\n                if ans is None:\n                    ans = x\n                return ans\n            parts = {x.partition('=')[0]:p(x.partition('=')[2]) for x in response.split(';')[1:]}\n            self.color_control_responses.append(parts)\n\n    def title_changed(self, data, is_base64=False) -> None:\n        self.titlebuf.append(process_title_from_child(data, is_base64, ''))\n\n    def osc_context(self, data):\n        pass\n\n    def icon_changed(self, data) -> None:\n        self.iconbuf += str(data, 'utf-8')\n\n    def set_dynamic_color(self, code, data='') -> None:\n        if code == 22:\n            self.set_pointer_shape(data)\n        else:\n            self.colorbuf += str(data or b'', 'utf-8')\n\n    def set_color_table_color(self, code, data='') -> None:\n        self.ctbuf += ''\n\n    def color_profile_popped(self, x) -> None:\n        pass\n\n    def cmd_output_marking(self, is_start: bool | None, data: str = '') -> None:\n        if is_start:\n            self.last_cmd_at = monotonic()\n            self.last_cmd_cmdline = decode_cmdline(data) if data else data\n        else:\n            if self.last_cmd_at != 0:\n                self.last_cmd_at = 0\n                with suppress(Exception):\n                    self.last_cmd_exit_status = int(data)\n\n    def request_capabilities(self, q) -> None:\n        from kitty.terminfo import get_capabilities\n        for c in get_capabilities(q, None):\n            self.write(c.encode('ascii'))\n\n    def desktop_notify(self, osc_code: int, raw_data: memoryview) -> None:\n        self.notifications.append((osc_code, str(raw_data, 'utf-8')))\n\n    def open_url(self, url: str, hyperlink_id: int) -> None:\n        self.open_urls.append((url, hyperlink_id))\n\n    def clipboard_control(self, data: memoryview, is_partial: bool = False) -> None:\n        self.cc_buf.append((str(data, 'utf-8'), is_partial))\n\n    def clear(self) -> None:\n        self.wtcbuf = b''\n        self.iconbuf = self.colorbuf = self.ctbuf = ''\n        self.titlebuf = []\n        self.printbuf = []\n        self.color_control_responses = []\n        self.notifications = []\n        self.open_urls = []\n        self.cc_buf = []\n        self.bell_count = 0\n        self.clone_cmds = []\n        self.current_clone_data = ''\n        self.last_cmd_exit_status = sys.maxsize\n        self.last_cmd_cmdline = ''\n        self.last_cmd_at = 0\n        self.num_of_resize_events = 0\n        self.da1 = []\n\n    def on_bell(self) -> None:\n        self.bell_count += 1\n\n    def on_da1(self) -> None:\n        payload = da1(get_options())\n        self.da1.append(payload)\n        if self.pty and self.pty.needs_da1:\n            self.pty.send_da1_response(payload)\n\n    def on_activity_since_last_focus(self) -> None:\n        pass\n\n    def on_mouse_event(self, event):\n        ev = MouseEvent(**event)\n        opts = get_options()\n        action_def = opts.mousemap.get(ev)\n        if not action_def:\n            return False\n        self.current_mouse_button = ev.button\n        for action in opts.alias_map.resolve_aliases(action_def, 'mouse_map'):\n            getattr(self, action.func)(*action.args)\n        self.current_mouse_button = 0\n        return True\n\n    def handle_remote_print(self, msg):\n        text = process_remote_print(msg)\n        self.printbuf.append(text)\n\n    def handle_remote_cmd(self, msg):\n        pass\n\n    def handle_remote_clone(self, msg):\n        msg = str(msg, 'utf-8')\n        if not msg:\n            if self.current_clone_data:\n                cdata, self.current_clone_data = self.current_clone_data, ''\n                from kitty.launch import CloneCmd\n                self.clone_cmds.append(CloneCmd(cdata))\n            self.current_clone_data = ''\n            return\n        num, rest = msg.split(':', 1)\n        if num == '0' or len(self.current_clone_data) > 1024 * 1024:\n            self.current_clone_data = ''\n        self.current_clone_data += rest\n\n    def handle_remote_ssh(self, msg):\n        from kittens.ssh.utils import get_ssh_data\n        if self.pty:\n            for line in get_ssh_data(msg, \"testing\"):\n                self.pty.write_to_child(line)\n\n    def handle_remote_echo(self, msg):\n        from base64 import standard_b64decode\n        if self.pty:\n            data = standard_b64decode(msg)\n            self.pty.write_to_child(data)\n\n    def file_transmission(self, data):\n        if self.ftc:\n            self.ftc.handle_serialized_command(data)\n\n\ndef filled_line_buf(ynum=5, xnum=5, cursor=Cursor()):\n    ans = LineBuf(ynum, xnum)\n    cursor.x = 0\n    for i in range(ynum):\n        t = (f'{i}') * xnum\n        ans.line(i).set_text(t, 0, xnum, cursor)\n    return ans\n\n\ndef filled_cursor():\n    ans = Cursor()\n    ans.bold = ans.italic = ans.reverse = ans.strikethrough = ans.dim = True\n    ans.fg = 0x101\n    ans.bg = 0x201\n    ans.decoration_fg = 0x301\n    return ans\n\n\ndef filled_history_buf(ynum=5, xnum=5, cursor=Cursor()):\n    lb = filled_line_buf(ynum, xnum, cursor)\n    ans = HistoryBuf(ynum, xnum)\n    for i in range(ynum):\n        ans.push(lb.line(i))\n    return ans\n\n\nis_ci = os.environ.get('CI') == 'true'\nmax_attempts = 4 if is_ci else 2\nsleep_duration = 4 if is_ci else 2\n\n\ndef retry_on_failure(max_attempts=max_attempts, sleep_duration=sleep_duration):\n    def decorator(func):\n        @wraps(func)\n        def wrapper(*args, **kwargs):\n            for attempt in range(max_attempts):\n                try:\n                    return func(*args, **kwargs)\n                except Exception:\n                    if attempt < max_attempts - 1: # Don't sleep on the last attempt\n                        time.sleep(sleep_duration)\n                        print(f'{func.__name__} failed, retrying in {sleep_duration} seconds', file=sys.stderr)\n                    else:\n                        raise # Re-raise the last exception\n        return wrapper\n    return decorator\n\n\nclass BaseTest(TestCase):\n\n    ae = TestCase.assertEqual\n    maxDiff = 2048\n    is_ci = is_ci\n\n    def rmtree_ignoring_errors(self, tdir):\n        try:\n            shutil.rmtree(tdir)\n        except FileNotFoundError as err:\n            print('Failed to delete the directory:', tdir, 'with error:', err, file=sys.stderr)\n\n    def tearDown(self):\n        set_options(None)\n\n    def set_options(self, options=None):\n        final_options = {'scrollback_pager_history_size': 1024, 'click_interval': 0.5}\n        if options:\n            final_options.update(options)\n        options = Options(merge_result_dicts(defaults._asdict(), final_options))\n        finalize_keys(options, {})\n        finalize_mouse_mappings(options, {})\n        set_options(options)\n        return options\n\n    def cmd_to_run_python_code(self, code):\n        from kitty.constants import kitty_exe\n        return [kitty_exe(), '+runpy', code]\n\n    def create_screen(self, cols=5, lines=5, scrollback=5, cell_width=10, cell_height=20, options=None):\n        self.set_options(options)\n        c = Callbacks()\n        s = Screen(c, lines, cols, scrollback, cell_width, cell_height, 0, c)\n        c.color_profile = s.color_profile\n        return s\n\n    def create_pty(\n            self, argv=None, cols=80, lines=100, scrollback=100, cell_width=10, cell_height=20,\n            options=None, cwd=None, env=None, stdin_fd=None, stdout_fd=None, needs_da1=False,\n    ):\n        self.set_options(options)\n        return PTY(\n            argv, lines, cols, scrollback, cell_width, cell_height, cwd, env, stdin_fd=stdin_fd, stdout_fd=stdout_fd,\n            needs_da1=needs_da1,\n        )\n\n    def assertEqualAttributes(self, c1, c2):\n        x1, y1, c1.x, c1.y = c1.x, c1.y, 0, 0\n        x2, y2, c2.x, c2.y = c2.x, c2.y, 0, 0\n        try:\n            self.assertEqual(c1, c2)\n        finally:\n            c1.x, c1.y, c2.x, c2.y = x1, y1, x2, y2\n\n\ndebug_stdout = debug_stderr = -1\n\n\n@contextmanager\ndef forwardable_stdio():\n    global debug_stderr, debug_stdout\n    debug_stdout = fd = os.dup(sys.stdout.fileno())\n    os.set_inheritable(fd, True)\n    debug_stderr = fd = os.dup(sys.stderr.fileno())\n    os.set_inheritable(fd, True)\n    try:\n        yield\n    finally:\n        os.close(debug_stderr)\n        os.close(debug_stdout)\n        debug_stderr = debug_stdout = -1\n\n\nclass PTY:\n\n    def __init__(\n        self, argv=None, rows=25, columns=80, scrollback=100, cell_width=10, cell_height=20,\n        cwd=None, env=None, stdin_fd=None, stdout_fd=None, needs_da1=False,\n    ):\n        self.is_child = False\n        if isinstance(argv, str):\n            argv = shlex.split(argv)\n        self.write_buf = b''\n        if argv is None:\n            from kitty.child import openpty\n            self.master_fd, self.slave_fd = openpty()\n            self.child_pid = 0\n            self.initial_termios_state = termios.tcgetattr(self.master_fd)\n        else:\n            self.child_pid, self.master_fd = fork()\n            self.is_child = self.child_pid == CHILD\n        self.child_waited_for = False\n        if self.is_child:\n            while read_screen_size().width != columns * cell_width:\n                time.sleep(0.01)\n            if cwd:\n                os.chdir(cwd)\n            if stdin_fd is not None:\n                os.dup2(stdin_fd, STDIN_FILENO)\n                os.close(stdin_fd)\n            if stdout_fd is not None:\n                os.dup2(stdout_fd, STDOUT_FILENO)\n                os.close(stdout_fd)\n            signal.pthread_sigmask(signal.SIG_SETMASK, ())\n            env = os.environ if env is None else env\n            if debug_stdout > -1:\n                env['KITTY_STDIO_FORWARDED'] = str(debug_stdout)\n            os.execvpe(argv[0], argv, env)\n        if stdin_fd is not None:\n            os.close(stdin_fd)\n        if stdout_fd is not None:\n            os.close(stdout_fd)\n        os.set_blocking(self.master_fd, False)\n        self.cell_width = cell_width\n        self.cell_height = cell_height\n        self.set_window_size(rows=rows, columns=columns)\n        self.needs_da1 = needs_da1\n        self.callbacks = Callbacks(self)\n        self.screen = Screen(self.callbacks, rows, columns, scrollback, cell_width, cell_height, 0, self.callbacks)\n        self.received_bytes = b''\n\n    def reset_termios_state(self):\n        if s := getattr(self, 'initial_termios_state', None):\n            termios.tcsetattr(self.master_fd, termios.TCSANOW, s)\n\n    def turn_off_echo(self):\n        s = termios.tcgetattr(self.master_fd)\n        s[3] &= ~termios.ECHO\n        termios.tcsetattr(self.master_fd, termios.TCSANOW, s)\n\n    def is_echo_on(self):\n        s = termios.tcgetattr(self.master_fd)\n        return True if s[3] & termios.ECHO else False\n\n    def __del__(self):\n        if not self.is_child:\n            if hasattr(self, 'master_fd'):\n                os.close(self.master_fd)\n                del self.master_fd\n            if hasattr(self, 'slave_fd'):\n                os.close(self.slave_fd)\n                del self.slave_fd\n            if self.child_pid > 0 and not self.child_waited_for:\n                os.waitpid(self.child_pid, 0)\n                self.child_waited_for = True\n\n    def write_to_child(self, data, flush=False):\n        if isinstance(data, str):\n            data = data.encode('utf-8')\n        self.write_buf += data\n        if flush:\n            self.process_input_from_child(0)\n\n    def send_da1_response(self, data: str) -> None:\n        self.write_to_child('\\x1b[' + data, flush=False) # ]]]]]]\n\n    def send_cmd_to_child(self, cmd, flush=False):\n        self.callbacks.last_cmd_exit_status = sys.maxsize\n        self.last_cmd = cmd\n        self.write_to_child(cmd + '\\r', flush=flush)\n\n    def process_input_from_child(self, timeout=10):\n        rd, wd, _ = select.select([self.master_fd], [self.master_fd] if self.write_buf else [], [], max(0, timeout))\n        if wd:\n            n = os.write(self.master_fd, self.write_buf)\n            self.write_buf = self.write_buf[n:]\n\n        bytes_read = 0\n        if rd:\n            data = os.read(self.master_fd, io.DEFAULT_BUFFER_SIZE)\n            bytes_read += len(data)\n            self.received_bytes += data\n            parse_bytes(self.screen, data)\n        return bytes_read\n\n    def wait_till(self, q, timeout=10, timeout_msg=None):\n        end_time = time.monotonic() + timeout\n        while not q() and time.monotonic() <= end_time:\n            try:\n                self.process_input_from_child(timeout=end_time - time.monotonic())\n            except OSError as e:\n                if not q():\n                    raise Exception(f'Failed to read from pty with error: {e}. {self.screen_contents_for_error()}') from e\n                return\n        if not q():\n            msg = 'The condition was not met'\n            if timeout_msg is not None:\n                msg = timeout_msg()\n            raise TimeoutError(f'Timed out after {timeout} seconds: {msg}. {self.screen_contents_for_error()}')\n\n    def wait_till_child_exits(self, timeout=30 if BaseTest.is_ci else 10, require_exit_code=None):\n        end_time = time.monotonic() + timeout\n        while time.monotonic() <= end_time:\n            si_pid, status = os.waitpid(self.child_pid, os.WNOHANG)\n            if si_pid == self.child_pid and os.WIFEXITED(status):\n                ec = os.waitstatus_to_exitcode(status) if hasattr(os, 'waitstatus_to_exitcode') else require_exit_code\n                self.child_waited_for = True\n                if require_exit_code is not None and ec != require_exit_code:\n                    raise AssertionError(\n                        f'Child exited with exit status: {status} code: {ec} != {require_exit_code}.'\n                        f' {self.screen_contents_for_error()}')\n                return status\n            with suppress(OSError):\n                self.process_input_from_child(timeout=0.02)\n        raise AssertionError(f'Child did not exit in {timeout} seconds. {self.screen_contents_for_error()}')\n\n    def set_window_size(self, rows=25, columns=80, send_signal=True):\n        if hasattr(self, 'screen'):\n            self.screen.resize(rows, columns)\n        if send_signal:\n            x_pixels = columns * self.cell_width\n            y_pixels = rows * self.cell_height\n            s = struct.pack('HHHH', rows, columns, x_pixels, y_pixels)\n            fcntl.ioctl(self.master_fd, termios.TIOCSWINSZ, s)\n\n    def screen_contents_for_error(self):\n        from kitty.window import as_text\n        ans = as_text(self.screen, add_history=True, as_ansi=False)\n        return f'Screen contents as repr:\\n{ans!r}\\nScreen contents:\\n{ans.rstrip()}'\n\n    def screen_contents(self):\n        lines = []\n        for i in range(self.screen.lines):\n            x = str(self.screen.line(i))\n            if x:\n                lines.append(x)\n        return '\\n'.join(lines)\n\n    def last_cmd_output(self, as_ansi=False, add_wrap_markers=False):\n        from kitty.window import cmd_output\n        return cmd_output(self.screen, as_ansi=as_ansi, add_wrap_markers=add_wrap_markers)\n"
  },
  {
    "path": "kitty_tests/atexit.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport select\nimport shutil\nimport signal\nimport subprocess\nimport tempfile\n\nfrom kitty.constants import kitten_exe, kitty_exe\nfrom kitty.shm import SharedMemory\n\nfrom . import BaseTest\n\n\nclass Atexit(BaseTest):\n\n    def setUp(self):\n        self.tdir = tempfile.mkdtemp()\n\n    def tearDown(self):\n        shutil.rmtree(self.tdir)\n\n    def test_go_atexit(self):\n        cp = subprocess.run([kitten_exe(), '__atexit__', 'test'], cwd=self.tdir)\n        self.ae(cp.returncode, 0)\n        self.assertFalse(os.listdir(self.tdir))\n\n    def test_atexit(self):\n\n        def r(action='close'):\n            p = subprocess.Popen([kitty_exe(), '+runpy', f'''\\\nimport subprocess\np = subprocess.Popen(['{kitten_exe()}', '__atexit__'])\nprint(p.pid, flush=True)\nraise SystemExit(p.wait())\n'''], stdin=subprocess.PIPE, stdout=subprocess.PIPE)\n            readers = [p.stdout.fileno()]\n            def read():\n                r, _, _ = select.select(readers, [], [], 10)\n                if not r:\n                    raise TimeoutError('Timed out waiting for read from child')\n                return p.stdout.readline().rstrip().decode()\n            atexit_pid = int(read())\n            for i in range(2):\n                with open(os.path.join(self.tdir, str(i)), 'w') as f:\n                    p.stdin.write(f'unlink {f.name}\\n'.encode())\n                    p.stdin.flush()\n                select.select(readers, [], [], 10)\n                self.ae(read(), str(i+1))\n            sdir = os.path.join(self.tdir, 'd')\n            os.mkdir(sdir)\n            p.stdin.write(f'rmtree {sdir}\\n'.encode())\n            p.stdin.flush()\n            open(os.path.join(sdir, 'f'), 'w').close()\n            select.select(readers, [], [], 10)\n            self.ae(read(), str(i+2))\n            shm = SharedMemory(size=64)\n            shm.write(b'1' * 64)\n            shm.flush()\n            p.stdin.write(f'shm_unlink {shm.name}\\n'.encode())\n            p.stdin.flush()\n            self.ae(read(), str(i+3))\n\n            self.assertTrue(os.listdir(self.tdir))\n            shm2 = SharedMemory(shm.name)\n            self.ae(shm2.read()[:64], b'1' * 64)\n\n            # Ensure child is ignoring signals\n            os.kill(atexit_pid, signal.SIGINT)\n            os.kill(atexit_pid, signal.SIGTERM)\n            if action == 'close':\n                p.stdin.close()\n            elif action == 'terminate':\n                p.terminate()\n            else:\n                p.kill()\n            p.wait(10)\n            if action != 'close':\n                p.stdin.close()\n            select.select(readers, [], [], 10)\n            self.assertFalse(read())\n            p.stdout.close()\n            self.assertFalse(os.listdir(self.tdir))\n            try:\n                os.waitpid(atexit_pid, 0)\n            except ChildProcessError:\n                pass\n            self.assertRaises(FileNotFoundError, lambda: SharedMemory(shm.name))\n\n        r('close')\n        r('terminate')\n        r('kill')\n"
  },
  {
    "path": "kitty_tests/check_build.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport json\nimport os\nimport shutil\nimport stat\nimport subprocess\nimport sys\nimport tempfile\nimport textwrap\nimport unittest\nfrom functools import partial\n\nfrom . import BaseTest\n\n\nclass TestBuild(BaseTest):\n\n    def test_exe(self) -> None:\n        from kitty.constants import kitten_exe, kitty_exe, str_version\n        exe = kitty_exe()\n        self.assertTrue(os.access(exe, os.X_OK))\n        self.assertTrue(os.path.isfile(exe))\n        self.assertIn('kitty', os.path.basename(exe))\n        exe = kitten_exe()\n        self.assertTrue(os.access(exe, os.X_OK))\n        self.assertTrue(os.path.isfile(exe))\n        self.assertIn(str_version, subprocess.check_output([exe, '--version']).decode())\n\n    def test_loading_extensions(self) -> None:\n        import kitty.fast_data_types as fdt\n        from kittens.transfer import rsync\n        del fdt, rsync\n\n    def test_loading_shaders(self) -> None:\n        from kitty.shaders import Program\n        for name in 'cell border bgimage tint graphics'.split():\n            Program(name)\n\n    def test_macos_dictation_forwarding(self) -> None:\n        from kitty.constants import glfw_path, is_macos\n        if not is_macos or not shutil.which('clang'):\n                self.skipTest('Dictation smoke test is macOS only and requires clang')\n        cocoa_module = glfw_path('cocoa')\n        probe = textwrap.dedent('''\\\n            #import <AppKit/AppKit.h>\n            #import <dlfcn.h>\n            #import <objc/runtime.h>\n            #import <objc/message.h>\n\n            static int start_calls = 0;\n            static int stop_calls = 0;\n            static id last_sender = nil;\n\n            static void fake_start_dictation(id self, SEL _cmd, id sender) {\n                (void)self; (void)_cmd;\n                start_calls++;\n                last_sender = sender;\n            }\n\n            static void fake_stop_dictation(id self, SEL _cmd, id sender) {\n                (void)self; (void)_cmd;\n                stop_calls++;\n                last_sender = sender;\n            }\n\n            static void require_true(BOOL condition, const char *message) {\n                if (!condition) {\n                    fprintf(stderr, \"FAIL: %s\\\\n\", message);\n                    exit(1);\n                }\n            }\n\n            int main(void) {\n                @autoreleasepool {\n                    [NSApplication sharedApplication];\n                    void *handle = dlopen(@@COCOA_MODULE@@, RTLD_NOW | RTLD_GLOBAL);\n                    require_true(handle != NULL, dlerror());\n\n                    SEL start = NSSelectorFromString(@\"startDictation:\");\n                    SEL stop = NSSelectorFromString(@\"stopDictation:\");\n                    Method start_method = class_getInstanceMethod([NSApplication class], start);\n                    Method stop_method = class_getInstanceMethod([NSApplication class], stop);\n                    require_true(start_method != NULL, \"NSApplication startDictation: missing\");\n                    require_true(stop_method != NULL, \"NSApplication stopDictation: missing\");\n                    method_setImplementation(start_method, (IMP)fake_start_dictation);\n                    method_setImplementation(stop_method, (IMP)fake_stop_dictation);\n\n                    Class view_cls = NSClassFromString(@\"GLFWContentView\");\n                    Class context_cls = NSClassFromString(@\"GLFWTextInputContext\");\n                    require_true(view_cls != Nil, \"GLFWContentView class not loaded\");\n                    require_true(context_cls != Nil, \"GLFWTextInputContext class not loaded\");\n\n                    SEL init_with_glfw_window = NSSelectorFromString(@\"initWithGlfwWindow:\");\n                    id view = ((id (*)(id, SEL, void *)) objc_msgSend)([view_cls alloc], init_with_glfw_window, NULL);\n                    require_true(view != nil, \"GLFWContentView initWithGlfwWindow: failed\");\n                    require_true([view respondsToSelector:start], \"GLFWContentView does not expose startDictation:\");\n                    require_true([view respondsToSelector:stop], \"GLFWContentView does not expose stopDictation:\");\n\n            #pragma clang diagnostic push\n            #pragma clang diagnostic ignored \"-Warc-performSelector-leaks\"\n                    [view performSelector:start withObject:@\"menu sender\"];\n            #pragma clang diagnostic pop\n                    require_true(start_calls == 1, \"startDictation: action was not forwarded to NSApplication\");\n                    require_true([(id)last_sender isEqual:@\"menu sender\"], \"startDictation: forwarded wrong sender\");\n\n                    [view doCommandBySelector:start];\n                    require_true(start_calls == 2, \"doCommandBySelector:startDictation: was swallowed\");\n                    require_true(last_sender == view, \"doCommandBySelector:startDictation: should forward self as sender\");\n\n                    id context = [view inputContext];\n                    require_true(context != nil, \"GLFWContentView inputContext missing\");\n                    require_true([context isKindOfClass:context_cls], \"GLFWContentView inputContext has wrong class\");\n                    [context doCommandBySelector:stop];\n                    require_true(stop_calls == 1, \"GLFWTextInputContext did not forward stopDictation:\");\n                    require_true(last_sender == nil, \"GLFWTextInputContext should forward nil sender\");\n\n                    printf(\"dictation forwarding probe passed\\\\n\");\n                }\n                return 0;\n            }\n        ''').replace('@@COCOA_MODULE@@', json.dumps(cocoa_module))\n        with tempfile.TemporaryDirectory() as tdir:\n            src = os.path.join(tdir, 'dictation_probe.m')\n            exe = os.path.join(tdir, 'dictation_probe')\n            with open(src, 'w') as f:\n                f.write(probe)\n            cp = subprocess.run(\n                ['clang', '-framework', 'AppKit', src, '-o', exe],\n                stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True\n            )\n            self.assertEqual(cp.returncode, 0, cp.stdout)\n            cp = subprocess.run([exe], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)\n            self.assertEqual(cp.returncode, 0, cp.stdout)\n            self.assertIn('dictation forwarding probe passed', cp.stdout)\n\n    def test_glfw_modules(self) -> None:\n        from kitty.constants import glfw_path, is_macos\n        linux_backends = ['x11']\n        if not self.is_ci:\n            linux_backends.append('wayland')\n        modules = ['cocoa'] if is_macos else linux_backends\n        for name in modules:\n            path = glfw_path(name)\n            self.assertTrue(os.path.isfile(path), f'{path} is not a file')\n            self.assertTrue(os.access(path, os.X_OK), f'{path} is not executable')\n\n    def test_all_kitten_names(self) -> None:\n        from kittens.runner import all_kitten_names\n        names = all_kitten_names()\n        self.assertIn('diff', names)\n        self.assertIn('hints', names)\n        self.assertGreater(len(names), 8)\n\n    def test_filesystem_locations(self) -> None:\n        from kitty.constants import fonts_dir, local_docs, logo_png_file, shell_integration_dir, terminfo_dir\n        zsh = os.path.join(shell_integration_dir, 'zsh')\n        self.assertTrue(os.path.isdir(terminfo_dir), f'Terminfo dir: {terminfo_dir}')\n        self.assertTrue(os.path.exists(logo_png_file), f'Logo file: {logo_png_file}')\n        self.assertTrue(os.path.exists(zsh), f'Shell integration: {zsh}')\n        nsfm = os.path.join(fonts_dir, 'SymbolsNerdFontMono-Regular.ttf')\n        self.assertTrue(os.path.exists(nsfm), f'Logo file: {nsfm}')\n\n        def is_executable(x):\n            mode = os.stat(x).st_mode\n            q = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH\n            return mode & q == q\n\n        for x in ('kitty', 'kitten'):\n            x = os.path.join(shell_integration_dir, 'ssh', x)\n            self.assertTrue(is_executable(x), f'{x} is not executable')\n        if getattr(sys, 'frozen', False):\n            self.assertTrue(os.path.isdir(local_docs()), f'Local docs: {local_docs()}')\n\n    def test_ca_certificates(self):\n        import ssl\n        if not getattr(sys, 'frozen', False):\n            self.skipTest('CA certificates are only tested on frozen builds')\n        c = ssl.create_default_context()\n        self.assertGreater(c.cert_store_stats()['x509_ca'], 2)\n\n    def test_docs_url(self):\n        from kitty.constants import website_url\n        from kitty.utils import docs_url\n\n        def run_tests(p, base, suffix='.html'):\n            def t(x, e):\n                self.ae(p(x), base + e)\n            t('', 'index.html' if suffix == '.html' else '')\n            t('conf', f'conf{suffix}')\n            t('kittens/ssh#frag', f'kittens/ssh{suffix}#frag')\n            t('#ref=confloc', f'conf{suffix}#confloc')\n            t('#ref=conf-kitty-fonts', f'conf{suffix}#conf-kitty-fonts')\n            t('#ref=conf-kitten-ssh-xxx', f'kittens/ssh{suffix}#conf-kitten-ssh-xxx')\n            t('#ref=at_close_tab', f'remote-control{suffix}#at-close-tab')\n            t('#ref=at-close-tab', f'remote-control{suffix}#at-close-tab')\n            t('#ref=action-copy', f'actions{suffix}#copy')\n            t('#ref=doc-/marks', f'marks{suffix}')\n\n        run_tests(partial(docs_url, local_docs_root='/docs'), 'file:///docs/')\n        w = website_url()\n        run_tests(partial(docs_url, local_docs_root=None), w, '/')\n        self.ae(docs_url('#ref=issues-123'), 'https://github.com/kovidgoyal/kitty/issues/123')\n\n    def test_launcher_ensures_stdio(self):\n        import subprocess\n\n        from kitty.constants import kitty_exe\n        exe = kitty_exe()\n        cp = subprocess.run([exe, '+runpy', f'''\\\nimport os, sys\nif sys.stdin:\n    os.close(sys.stdin.fileno())\nif sys.stdout:\n    os.close(sys.stdout.fileno())\nif sys.stderr:\n    os.close(sys.stderr.fileno())\nos.execlp({exe!r}, 'kitty', '+runpy', 'import sys; raise SystemExit(1 if sys.stdout is None or sys.stdin is None or sys.stderr is None else 0)')\n'''])\n        self.assertEqual(cp.returncode, 0)\n\n\ndef main() -> None:\n    tests = unittest.defaultTestLoader.loadTestsFromTestCase(TestBuild)\n    r = unittest.TextTestRunner(verbosity=4)\n    result = r.run(tests)\n    if result.errors or result.failures:\n        raise SystemExit(1)\n"
  },
  {
    "path": "kitty_tests/clipboard.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom base64 import standard_b64decode, standard_b64encode\n\nfrom kitty.clipboard import WriteRequest\nfrom kitty.fast_data_types import StreamingBase64Decoder\n\nfrom . import BaseTest\n\n\nclass TestClipboard(BaseTest):\n\n    def test_clipboard_write_request(self):\n        def t(data, expected):\n            wr = WriteRequest(max_size=64)\n            wr.add_base64_data(data)\n            self.ae(wr.data_for(), expected)\n        t('dGl0bGU=', b'title')\n        t('dGl0bGU', b'title')\n        t('dGl0bG', b'titl')\n        t('dGl0bG==', b'titl')\n        t('dGl0b', b'tit')\n        t('bGlnaHQgd29yaw', b'light work')\n        t('bGlnaHQgd29yaw==', b'light work')\n        wr = WriteRequest(max_size=64)\n        wr.add_base64_data('bGlnaHQgd29')\n        for x in b'y', b'a', b'y', b'4', b'=':\n            wr.add_base64_data(x)\n        self.ae(wr.data_for(), b'light work.')\n        wr = WriteRequest(max_size=64)\n        for x in 'bGlnaHQgd29y':\n            wr.add_base64_data(x)\n        self.ae(wr.data_for(), b'light wor')\n\n    def test_base64_streaming_decoder(self):\n        d = StreamingBase64Decoder()\n        c = standard_b64encode(b'abcdef')\n        self.ae(b'abcdef', d.decode(c))\n        self.assertFalse(d.needs_more_data())\n        a = d.decode(c[:4])\n        self.assertFalse(d.needs_more_data())\n        self.ae(b'abcdef', a + d.decode(c[4:]))\n        self.assertFalse(d.needs_more_data())\n        a = d.decode(c[:1])\n        self.assertTrue(d.needs_more_data())\n        self.ae(b'abcdef', a + d.decode(c[1:4]) + d.decode(c[4:]))\n        self.assertFalse(d.needs_more_data())\n        c = standard_b64encode(b'abcd')\n        self.ae(b'abcd', d.decode(c[:2]) + d.decode(c[2:]))\n        c1 = standard_b64encode(b'1' * 4096)\n        c2 = standard_b64encode(b'2' * 4096)\n        self.ae(standard_b64decode(c1) + standard_b64decode(c2), d.decode(c1) + d.decode(c2))\n        self.assertFalse(d.needs_more_data())\n"
  },
  {
    "path": "kitty_tests/command_palette.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom . import BaseTest\n\n\nclass TestCommandPalette(BaseTest):\n\n    def test_collect_keys_data(self):\n        from kittens.command_palette.main import collect_keys_data\n        from kitty.actions import groups\n        opts = self.set_options()\n        data = collect_keys_data(opts)\n        self.assertIn('modes', data)\n        self.assertIn('mouse', data)\n        self.assertIn('', data['modes'], 'Default keyboard mode should be present')\n        default_mode = data['modes']['']\n        # Should have at least some categories\n        self.assertTrue(len(default_mode) > 0, 'Should have at least one category')\n        # All category names should be from the known groups\n        known_titles = set(groups.values())\n        for cat_name in default_mode:\n            self.assertIn(cat_name, known_titles, f'Unknown category: {cat_name}')\n        # Each category should have bindings with required fields\n        for cat_name, bindings in default_mode.items():\n            self.assertIsInstance(bindings, list)\n            for b in bindings:\n                self.assertIn('key', b)\n                self.assertIn('action', b)\n                self.assertIn('action_display', b)\n                self.assertIn('definition', b)\n                self.assertIn('help', b)\n                self.assertIn('long_help', b)\n                self.assertIsInstance(b['key'], str)\n                self.assertIsInstance(b['action'], str)\n                # key may be empty for unmapped actions; action must always be non-empty\n                self.assertTrue(len(b['action']) > 0)\n        # Mouse mappings\n        self.assertIsInstance(data['mouse'], list)\n        for b in data['mouse']:\n            self.assertIn('key', b)\n            self.assertIn('action', b)\n            self.assertIn('action_display', b)\n\n    def test_collect_keys_categories_ordered(self):\n        from kittens.command_palette.main import collect_keys_data\n        from kitty.actions import groups\n        opts = self.set_options()\n        data = collect_keys_data(opts)\n        default_mode = data['modes']['']\n        cat_names = list(default_mode.keys())\n        group_titles = list(groups.values())\n        # Categories should appear in the same order as defined in groups\n        indices = []\n        for cat in cat_names:\n            if cat in group_titles:\n                indices.append(group_titles.index(cat))\n        self.ae(indices, sorted(indices), 'Categories should be ordered according to groups dict')\n\n    def test_collect_keys_bindings_sorted(self):\n        from kittens.command_palette.main import collect_keys_data\n        opts = self.set_options()\n        data = collect_keys_data(opts)\n        # Within each category, mapped entries (non-empty key) come first sorted by key,\n        # then unmapped entries (empty key) sorted by action name.\n        for cat_name, bindings in data['modes'][''].items():\n            seen_unmapped = False\n            for b in bindings:\n                if b['key'] == '':\n                    seen_unmapped = True\n                elif seen_unmapped:\n                    self.fail(\n                        f'In category {cat_name!r}, mapped binding {b!r} follows an unmapped one'\n                    )\n\n    def test_collect_keys_has_help_text(self):\n        from kittens.command_palette.main import collect_keys_data\n        opts = self.set_options()\n        data = collect_keys_data(opts)\n        # At least some bindings should have help text\n        has_help = False\n        for cat_name, bindings in data['modes'][''].items():\n            for b in bindings:\n                if b['help']:\n                    has_help = True\n                    break\n            if has_help:\n                break\n        self.assertTrue(has_help, 'At least some bindings should have help text')\n\n    def test_ordering_arrays_present(self):\n        from kittens.command_palette.main import collect_keys_data\n        opts = self.set_options()\n        data = collect_keys_data(opts)\n        # mode_order should list all modes\n        self.assertIn('mode_order', data)\n        self.assertIsInstance(data['mode_order'], list)\n        self.ae(set(data['mode_order']), set(data['modes'].keys()))\n        # category_order should list categories for each mode\n        self.assertIn('category_order', data)\n        self.assertIsInstance(data['category_order'], dict)\n        for mode_name in data['modes']:\n            self.assertIn(mode_name, data['category_order'])\n            self.ae(\n                set(data['category_order'][mode_name]),\n                set(data['modes'][mode_name].keys()),\n                f'category_order for mode {mode_name!r} should match modes keys',\n            )\n\n    def test_always_includes_unmapped_actions(self):\n        from kittens.command_palette.main import collect_keys_data\n        opts = self.set_options()\n        data = collect_keys_data(opts)\n        # Unmapped actions (empty key) are always included\n        found_unmapped = False\n        for cats in data['modes'].values():\n            for bindings in cats.values():\n                for b in bindings:\n                    if b['key'] == '':\n                        found_unmapped = True\n                        # Unmapped actions must still have action and definition\n                        self.assertTrue(len(b['action']) > 0)\n                        self.assertTrue(len(b['definition']) > 0)\n                        break\n        self.assertTrue(found_unmapped, 'Expected at least one unmapped action to always be present')\n\n    def test_unmapped_actions_sorted_order(self):\n        from kittens.command_palette.main import collect_keys_data\n        opts = self.set_options()\n        data = collect_keys_data(opts)\n        # In each category, mapped bindings (non-empty key) should come before unmapped ones\n        for cat_name, bindings in data['modes'].get('', {}).items():\n            seen_unmapped = False\n            for b in bindings:\n                if b['key'] == '':\n                    seen_unmapped = True\n                elif seen_unmapped:\n                    self.fail(\n                        f'In category {cat_name!r}, mapped binding {b!r} follows an unmapped one'\n                    )\n"
  },
  {
    "path": "kitty_tests/completion.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport json\nimport os\nimport shlex\nimport subprocess\nimport tempfile\n\nfrom kitty.constants import kitten_exe as kitten\n\nfrom . import BaseTest\n\n\nclass TestCompletion(BaseTest):\n\n    def test_completion(self):\n        with tempfile.TemporaryDirectory() as tdir:\n            completion(self, tdir)\n\n\ndef get_all_words(result):\n    all_words = set()\n    for group in result.get('groups', ()):\n        for m in group['matches']:\n            all_words.add(m['word'])\n    return all_words\n\n\ndef has_words(*words):\n    def t(self, result):\n        q = set(words)\n        missing = q - get_all_words(result)\n        self.assertFalse(missing, f'Words missing. Command line: {self.current_cmd!r}')\n    return t\n\n\ndef does_not_have_words(*words):\n    def t(self, result):\n        q = set(words)\n        all_words = get_all_words(result)\n        self.assertFalse(q & all_words, f'Words unexpectedly present. Command line: {self.current_cmd!r}')\n    return t\n\n\ndef all_words(*words):\n    def t(self, result):\n        expected = set(words)\n        actual = get_all_words(result)\n        self.assertEqual(expected, actual, f'Command line: {self.current_cmd!r}')\n    return t\n\n\ndef is_delegate(num_to_remove: int = 0, command: str = ''):\n    q = {}\n    if num_to_remove:\n        q['num_to_remove'] = num_to_remove\n    if command:\n        q['command'] = command\n\n    def t(self, result):\n        d = result['delegate']\n        self.assertEqual(d, q, f'Command line: {self.current_cmd!r}')\n    return t\n\n\ndef completion(self: TestCompletion, tdir: str):\n    all_cmds = []\n    all_argv = []\n    all_tests = []\n\n    def add(cmdline: str, *tests):\n        all_cmds.append(cmdline)\n        new_word = cmdline.endswith(' ')\n        if new_word:\n            cmdline = cmdline[:-1]\n        all_argv.append(shlex.split(cmdline))\n        if new_word:\n            all_argv[-1].append('')\n        all_tests.append(tests)\n\n    def run_tool():\n        env = os.environ.copy()\n        env['PATH'] = os.path.join(tdir, 'bin')\n        env['HOME'] = os.path.join(tdir, 'sub')\n        env['KITTY_CONFIG_DIRECTORY'] = os.path.join(tdir, 'sub')\n        cp = subprocess.run(\n            [kitten(), '__complete__', 'json'],\n            check=True, stdout=subprocess.PIPE, cwd=tdir, input=json.dumps(all_argv).encode(), env=env\n        )\n        self.assertEqual(cp.returncode, 0, f'kitten __complete__ failed with exit code: {cp.returncode}')\n        return json.loads(cp.stdout)\n\n    add('kitty ', has_words('@', '+', '+open'))\n    add('kitty @ l', has_words('ls', 'last-used-layout', 'launch'))\n\n    def make_file(path, mode=None):\n        with open(os.path.join(tdir, path), mode='x') as f:\n            if mode is not None:\n                os.chmod(f.fileno(), mode)\n\n    os.mkdir(os.path.join(tdir, 'bin'))\n    os.mkdir(os.path.join(tdir, 'sub'))\n    make_file('bin/exe1', 0o700)\n    make_file('bin/exe-not1')\n    make_file('exe2', 0o700)\n    make_file('exe-not2.jpeg')\n    make_file('sub/exe3', 0o700)\n    make_file('sub/exe-not3.png')\n\n    add('kitty x', all_words())\n    add('kitty e', all_words('exe1', 'exe2'))\n    add('kitty ./', all_words('./bin/', './sub/', './exe2'))\n    add('kitty ./e', all_words('./exe2'))\n    add('kitty ./s', all_words('./sub/'))\n    add('kitty ~', all_words('~/exe3'))\n    add('kitty ~/', all_words('~/exe3'))\n    add('kitty ~/e', all_words('~/exe3'))\n\n    add('kitty @ goto-layout ', has_words('tall', 'fat'))\n    add('kitty @ goto-layout spli', all_words('splits'))\n    add('kitty @ goto-layout f f', all_words())\n    add('kitty @ set-window-logo ', all_words('exe-not2.jpeg', 'sub/'))\n    add('kitty @ set-window-logo e', all_words('exe-not2.jpeg'))\n    add('kitty @ set-window-logo e e', all_words())\n    add('kitty +ope', has_words('+open'))\n    add('kitty +open -', has_words('-1', '-T'))\n\n    add('kitty -', has_words('-c', '-1', '--'), does_not_have_words('--config', '--single-instance'))\n    add('kitty -c', all_words('-c'))\n    add('kitty --', has_words('--config', '--single-instance', '--'))\n    add('kitty --s', has_words('--session', '--start-as'))\n    add('kitty --start-as', all_words('--start-as'))\n    add('kitty --start-as ', all_words('minimized', 'maximized', 'fullscreen', 'normal', 'hidden'))\n    add('kitty -1 ', does_not_have_words('@ls', '@'))\n    add('kitty --directory ', all_words('bin/', 'sub/'))\n    add('kitty -1d ', all_words('exe1'))\n    add('kitty -1d', all_words('-1d'))\n    add('kitty -o a', has_words('allow_remote_control='))\n    add('kitty --listen-on ', all_words('unix:', 'tcp:'))\n    add('kitty --listen-on unix:b', all_words('unix:bin/'))\n    add('kitty --directory=', all_words('--directory=bin/', '--directory=sub/'))\n    add('kitty --start-as=m', all_words('--start-as=minimized', '--start-as=maximized'))\n    add('kitty @launch --ty', has_words('--type'))\n    add('kitty @launch --type ', has_words('window', 'background', 'overlay'))\n    add('kitty @launch --cwd ', has_words('current', 'oldest', 'last_reported'))\n    add('kitty @launch --logo ', all_words('exe-not3.png'))\n    add('kitty @launch --logo ~', all_words('~/exe-not3.png'))\n    add('kitten ', has_words('@'))\n    add('kitten ', does_not_have_words('__complete__'))\n    add('kitten @launch --ty', has_words('--type'))\n\n    add('kitty + ', has_words('launch', 'kitten'))\n    add('kitty + kitten ', has_words('icat', 'diff'))\n    add('kitty +kitten icat ', has_words('sub/', 'exe-not2.jpeg'))\n    add('kitty + kitten icat --pr', has_words('--print-window-size'))\n    add('kitty + kitten diff ', has_words('exe-not2.jpeg'))\n    add('kitty + kitten themes --', has_words('--cache-age'))\n    add('kitty + kitten themes D', has_words('Default'))\n    add('kitty + kitten hyperlinked_grep ', is_delegate(3, 'rg'))\n    add('kitty +kitten hyperlinked_grep ', is_delegate(2, 'rg'))\n\n    add('clone-in-kitty --ty', has_words('--type'))\n\n    add('kitty bash ', is_delegate(1, 'bash'))\n    add('kitty -1 bash ', is_delegate(2, 'bash'))\n    add('kitty -1 bash --n', is_delegate(2, 'bash'))\n    add('kitty @launch --type tab bash --n', is_delegate(4, 'bash'))\n    add('kitty +kitten hyperlinked_grep --s', is_delegate(2, 'rg'))\n    add('kitty @launch e', all_words('exe1', 'exe2'))\n\n    for cmd, tests, result in zip(all_cmds, all_tests, run_tool()):\n        self.current_cmd = cmd\n        for test in tests:\n            test(self, result)\n"
  },
  {
    "path": "kitty_tests/crypto.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\n\nfrom . import BaseTest\n\n\ndef is_rlimit_memlock_too_low() -> bool:\n    ''' On supported systems, return true if the MEMLOCK limit is too low to\n    run the crypto test. '''\n    try:\n        import resource\n    except ModuleNotFoundError:\n        return False\n\n    memlock_limit, _ = resource.getrlimit(resource.RLIMIT_MEMLOCK)\n    pagesize = resource.getpagesize()\n    return memlock_limit <= pagesize\n\n\nclass TestCrypto(BaseTest):\n\n    def test_elliptic_curve_data_exchange(self):\n        if is_rlimit_memlock_too_low():\n            self.skipTest('RLIMIT_MEMLOCK is too low')\n        from kitty.fast_data_types import AES256GCMDecrypt, AES256GCMEncrypt, CryptoError, EllipticCurveKey\n        alice = EllipticCurveKey()\n        bob = EllipticCurveKey()\n        alice_secret = alice.derive_secret(bob.public)\n        bob_secret = bob.derive_secret(alice.public)\n        self.assertEqual(len(alice_secret), 32)\n        self.assertEqual(len(bob_secret), 32)\n        self.assertEqual(alice_secret, bob_secret)\n\n        auth_data = os.urandom(213)\n        plaintext = os.urandom(1011)\n        e = AES256GCMEncrypt(alice_secret)\n        e.add_authenticated_but_unencrypted_data(auth_data)\n        ciphertext = e.add_data_to_be_encrypted(plaintext, True)\n\n        d = AES256GCMDecrypt(bob_secret, e.iv, e.tag)\n        d.add_data_to_be_authenticated_but_not_decrypted(auth_data)\n        q = d.add_data_to_be_decrypted(ciphertext, True)\n        self.ae(q, plaintext)\n\n        def corrupt_data(data):\n            b = bytearray(data)\n            b[0] = (b[0] + 13) % 256\n            return bytes(b)\n\n        d = AES256GCMDecrypt(bob_secret, e.iv, corrupt_data(e.tag))\n        d.add_data_to_be_authenticated_but_not_decrypted(auth_data)\n        self.assertRaises(CryptoError, d.add_data_to_be_decrypted, ciphertext, True)\n\n        d = AES256GCMDecrypt(bob_secret, e.iv, e.tag)\n        d.add_data_to_be_authenticated_but_not_decrypted(corrupt_data(auth_data))\n        self.assertRaises(CryptoError, d.add_data_to_be_decrypted, ciphertext, True)\n\n        d = AES256GCMDecrypt(bob_secret, e.iv, e.tag)\n        d.add_data_to_be_authenticated_but_not_decrypted(auth_data)\n        self.assertRaises(CryptoError, d.add_data_to_be_decrypted, corrupt_data(ciphertext), True)\n"
  },
  {
    "path": "kitty_tests/datatypes.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport json\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\n\nfrom kitty.constants import is_macos, kitty_exe, read_kitty_resource\nfrom kitty.fast_data_types import (\n    Color,\n    HistoryBuf,\n    LineBuf,\n    abspath,\n    char_props_for,\n    expand_ansi_c_escapes,\n    expanduser,\n    get_config_dir,\n    makedirs,\n    parse_input_from_terminal,\n    read_file,\n    replace_c0_codes_except_nl_space_tab,\n    split_into_graphemes,\n    strip_csi,\n    truncate_point_for_length,\n    wcswidth,\n    wcwidth,\n)\nfrom kitty.fast_data_types import Cursor as C\nfrom kitty.rgb import to_color\nfrom kitty.utils import is_ok_to_read_image_file, is_path_in_temp_dir, sanitize_title, sanitize_url_for_display_to_user, shlex_split, shlex_split_with_positions\n\nfrom . import BaseTest, filled_cursor, filled_history_buf, filled_line_buf\n\n\ndef create_lbuf(*lines):\n    maxw = max(map(len, lines))\n    ans = LineBuf(len(lines), maxw)\n    for i, l0 in enumerate(lines):\n        ans.line(i).set_text(l0, 0, len(l0), C())\n        if i > 0:\n            ans.set_continued(i, len(lines[i-1]) == maxw)\n    return ans\n\n\nclass TestDataTypes(BaseTest):\n\n\n    def test_replace_c0_codes(self):\n        def t(x: str, expected: str):\n            q = replace_c0_codes_except_nl_space_tab(x)\n            self.ae(expected, q)\n            q = replace_c0_codes_except_nl_space_tab(x.encode('utf-8'))\n            self.ae(expected.encode('utf-8'), q)\n        t('abc', 'abc')\n        t('a\\0\\x01b\\x03\\x04\\t\\rc', 'a\\u2400\\u2401b\\u2403\\u2404\\t\\u240dc')\n        t('a\\0\\x01😸\\x03\\x04\\t\\rc', 'a\\u2400\\u2401😸\\u2403\\u2404\\t\\u240dc')\n        t('a\\nb\\tc d', 'a\\nb\\tc d')\n\n    def test_to_color(self):\n        for x in 'xxx #12 #1234 rgb:a/b'.split():\n            self.assertIsNone(to_color(x))\n\n        def c(spec, r=0, g=0, b=0, a=0):\n            c = to_color(spec)\n            self.ae(Color(r, g, b, a), c, spec)\n            self.ae(Color(r, green=g, alpha=a, blue=b), c, spec)\n\n        c('#eee # comment', 0xee, 0xee, 0xee)\n        c('#234567', 0x23, 0x45, 0x67)\n        c('#abCabcdef', 0xab, 0xab, 0xde)\n        c('rgb:e/e/e # comment', 0xee, 0xee, 0xee)\n        c('rgB:23/45/67', 0x23, 0x45, 0x67)\n        c('rgb:abc/abc/def', 0xab, 0xab, 0xde)\n        c('rEd', 0xff, 0, 0)\n        c('aLice blUe # comment', 240, 248, 255)\n        c('oklch(1,0,0)', 255, 255, 255)\n        c('oklch(0,0,0)', 0, 0, 0)\n        c('oklch(0.5,0.1,180)', 0, 117, 101)\n        c('oklcH(0.7 0.15 140) # comment', 0x68, 0xb4, 0x57)\n        c('oklch(0.9 0.05 265)', 0xce, 0xde, 0xff)\n        c('lAb(70 50 -30)', 0xea, 0x88, 0xe2)\n        c('lab(50,0,0)', 199, 199, 199)\n        c('lab(100,0,0)', 255, 255, 255)\n        c('lab(0,0,0)', 0, 0, 0)\n\n        self.ae(int(Color(1, 2, 3)), 0x10203)\n        base = Color(12, 12, 12)\n        a = Color(23, 23, 23)\n        b = Color(100, 100, 100)\n        self.assertLess(base.contrast(a), base.contrast(b))\n        self.ae(Color(1, 2, 3).as_sgr, ':2:1:2:3')\n        self.ae(Color(1, 2, 3).as_sharp, '#010203')\n        self.ae(Color(1, 2, 3, 4).as_sharp, '#04010203')\n        self.ae(Color(1, 2, 3, 4).rgb, 0x10203)\n\n    def test_oklch_gamut_mapping(self):\n        \"\"\"Test OKLCH color format with CSS Color 4 gamut mapping\"\"\"\n        def c(spec, r=0, g=0, b=0):\n            color = to_color(spec)\n            self.assertIsNotNone(color, f'Failed to parse: {spec}')\n            self.ae(Color(r, g, b), color, spec)\n\n        def in_range(spec):\n            \"\"\"Verify color values are in valid 0-255 range\"\"\"\n            color = to_color(spec)\n            self.assertIsNotNone(color, f'Failed to parse: {spec}')\n            self.assertTrue(0 <= color.red <= 255, f'Red out of range: {color.red}')\n            self.assertTrue(0 <= color.green <= 255, f'Green out of range: {color.green}')\n            self.assertTrue(0 <= color.blue <= 255, f'Blue out of range: {color.blue}')\n            return color\n\n        # In-gamut colors should parse unchanged\n        c('oklch(0.5 0.1 180)', 0x00, 0x75, 0x65)  # Mid-tone cyan with moderate chroma\n\n        # Out-of-gamut colors should be mapped to sRGB gamut\n        # High chroma red - should be mapped but remain reddish\n        color = in_range('oklch(0.7 0.35 25)')\n        self.assertGreater(color.red, 200, 'High chroma red should have high red component')\n        self.assertLess(color.green, 100, 'High chroma red should have low green component')\n\n        # Edge cases\n        c('oklch(0 0 0)', 0x00, 0x00, 0x00)  # Pure black\n        c('oklch(1 0 0)', 0xff, 0xff, 0xff)  # Pure white\n\n        # Achromatic colors (zero chroma)\n        c('oklch(0.5 0 180)', 0xbc, 0xbc, 0xbc)  # Mid gray, hue irrelevant\n        c('oklch(0.25 0 90)', 0x89, 0x89, 0x89)  # Dark gray\n\n        # Test various hues with moderate chroma\n        in_range('oklch(0.6 0.15 0)')    # Red hue\n        in_range('oklch(0.6 0.15 60)')   # Yellow hue\n        in_range('oklch(0.6 0.15 120)')  # Green hue\n        in_range('oklch(0.6 0.15 180)')  # Cyan hue\n        in_range('oklch(0.6 0.15 240)')  # Blue hue\n        in_range('oklch(0.6 0.15 300)')  # Magenta hue\n\n        # Test with different comma/space separators\n        c('oklch(0.5, 0.1, 180)', 0x00, 0x75, 0x65)\n        c('oklch(0.5,0.1,180)', 0x00, 0x75, 0x65)\n\n        # Test percentage lightness\n        color = to_color('oklch(50% 0.1 180)')\n        self.assertIsNotNone(color)\n\n        # Very high chroma should trigger gamut mapping\n        # These should all succeed and return valid RGB values\n        in_range('oklch(0.5 0.5 0)')\n        in_range('oklch(0.5 0.5 180)')\n        in_range('oklch(0.9 0.3 120)')\n\n    def test_inline_comments(self):\n        \"\"\"Test inline comments in color values\"\"\"\n        def c(spec, r=0, g=0, b=0):\n            color = to_color(spec)\n            self.assertIsNotNone(color, f'Failed to parse: {spec}')\n            self.ae(Color(r, g, b), color, spec)\n\n        # OKLCH with inline comment\n        c('oklch(0.5 0.1 180) # Cyan color', 0x00, 0x75, 0x65)\n        c('oklch(0.7 0.15 140) # Green', 0x68, 0xb4, 0x57)\n\n        # Hex colors with inline comments\n        c('#ff0000 # Red', 0xff, 0x00, 0x00)\n        c('#00ff00 # Green', 0x00, 0xff, 0x00)\n        c('#0000ff # Blue', 0x00, 0x00, 0xff)\n\n        # LAB colors with inline comments\n        c('lab(70 50 -30) # Purple-ish', 0xea, 0x88, 0xe2)\n\n        # RGB with inline comments\n        c('rgb:ff/00/00 # RGB Red', 0xff, 0x00, 0x00)\n\n        # Named color should not be affected by text after it\n        # (not a comment, just ignored)\n        c('red', 0xff, 0x00, 0x00)\n\n    def test_lab_parsing(self):\n        \"\"\"Test CIE LAB color format parsing\"\"\"\n        def c(spec, r=0, g=0, b=0):\n            color = to_color(spec)\n            self.assertIsNotNone(color, f'Failed to parse: {spec}')\n            self.ae(color.red, r)\n            self.ae(color.green, g)\n            self.ae(color.blue, b)\n            self.ae(Color(r, g, b), color, spec)\n\n        # LAB basic colors\n        c('lab(0 0 0)', 0x00, 0x00, 0x00)      # LAB black\n        c('lab(100 0 0)', 0xff, 0xff, 0xff)    # LAB white\n        c('lab(50 0 0)', 0xc7, 0xc7, 0xc7)     # LAB mid-gray\n\n        # LAB with color components\n        c('lab(70 50 -30)', 0xea, 0x88, 0xe2)  # Purple-ish\n        color = to_color('lab(50 50 50)')      # Orange/red-ish (positive a and b)\n        self.assertIsNotNone(color)\n        self.assertGreater(color.red, 0xc0)    # Should have high red\n        self.assertLess(color.blue, 0x50)      # Should have low blue\n\n        # LAB with different separators\n        color = to_color('lab(70, 50, -30)')\n        self.assertIsNotNone(color)\n        color = to_color('lab(70,50,-30)')\n        self.assertIsNotNone(color)\n\n        # LAB with negative values (valid for a and b channels)\n        color = to_color('lab(50 -50 -50)')\n        self.assertIsNotNone(color)\n        color = to_color('lab(50 -50 50)')\n        self.assertIsNotNone(color)\n\n    def test_color_format_errors(self):\n        \"\"\"Test error handling for invalid color formats\"\"\"\n        # Invalid OKLCH\n        self.assertIsNone(to_color('oklch()'))\n        self.assertIsNone(to_color('oklch(0.5)'))\n        self.assertIsNone(to_color('oklch(0.5 0.1)'))\n        self.assertIsNone(to_color('oklch(a b c)'))\n\n        # Invalid LAB\n        self.assertIsNone(to_color('lab()'))\n        self.assertIsNone(to_color('lab(50)'))\n        self.assertIsNone(to_color('lab(50 0)'))\n        self.assertIsNone(to_color('lab(a b c)'))\n\n        # Invalid color() function\n        self.assertIsNone(to_color('color()'))\n        self.assertIsNone(to_color('color(unknown 1 0 0)'))\n\n        # Empty and whitespace\n        self.assertIsNone(to_color(''))\n        self.assertIsNone(to_color('   '))\n\n        # Malformed hex\n        self.assertIsNone(to_color('#'))\n        self.assertIsNone(to_color('#12'))\n        self.assertIsNone(to_color('#1234'))\n\n        # Malformed rgb\n        self.assertIsNone(to_color('rgb:'))\n        self.assertIsNone(to_color('rgb:a/b'))\n\n    def test_linebuf(self):\n        old = filled_line_buf(2, 3, filled_cursor())\n        new = LineBuf(1, 3)\n        new.copy_old(old)\n        self.ae(new.line(0), old.line(1))\n        new.clear()\n        self.ae(str(new.line(0)), '')\n        old.set_attribute('reverse', False)\n        for y in range(old.ynum):\n            for x in range(old.xnum):\n                l0 = old.line(y)\n                c = l0.cursor_from(x)\n                self.assertFalse(c.reverse)\n                self.assertTrue(c.bold)\n        self.assertFalse(old.is_continued(0))\n        old.set_continued(1, True)\n        self.assertTrue(old.is_continued(1))\n        self.assertFalse(old.is_continued(0))\n\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb2 = LineBuf(5, 5)\n        lb2.copy_old(lb)\n        lb.index(0, 4)\n        for i in range(0, 4):\n            self.ae(lb.line(i), lb2.line(i + 1))\n        self.ae(lb.line(4), lb2.line(0))\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.index(1, 3)\n        self.ae(lb.line(0), lb2.line(0))\n        self.ae(lb.line(1), lb2.line(2))\n        self.ae(lb.line(2), lb2.line(3))\n        self.ae(lb.line(3), lb2.line(1))\n        self.ae(lb.line(4), lb2.line(4))\n        self.ae(lb.create_line_copy(1), lb2.line(2))\n        l2 = lb.create_line_copy(2)\n        lb.copy_line_to(1, l2)\n        self.ae(l2, lb2.line(2))\n        lb.clear_line(0)\n        self.ae(lb.line(0), LineBuf(1, lb.xnum).create_line_copy(0))\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.reverse_index(0, 4)\n        self.ae(lb.line(0), lb2.line(4))\n        for i in range(1, 5):\n            self.ae(lb.line(i), lb2.line(i - 1))\n\n        lb = filled_line_buf(5, 5, filled_cursor())\n        clb = filled_line_buf(5, 5, filled_cursor())\n        lb2 = LineBuf(1, 5)\n        lb.insert_lines(2, 1, lb.ynum - 1)\n        self.ae(lb.line(0), clb.line(0))\n        self.ae(lb.line(3), clb.line(1))\n        self.ae(lb.line(4), clb.line(2))\n        self.ae(lb.line(1), lb2.line(0))\n        self.ae(lb.line(2), lb2.line(0))\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.insert_lines(10, 0, lb.ynum - 1)\n        for i in range(lb.ynum):\n            self.ae(lb.line(i), lb2.line(0))\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.insert_lines(10, 1, lb.ynum - 1)\n        self.ae(lb.line(0), clb.line(0))\n        for i in range(1, lb.ynum):\n            self.ae(lb.line(i), lb2.line(0))\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.insert_lines(1, 1, 3)\n        self.ae(lb.line(0), clb.line(0))\n        self.ae(lb.line(1), lb2.line(0))\n        self.ae(lb.line(2), clb.line(1))\n        self.ae(lb.line(3), clb.line(2))\n        self.ae(lb.line(4), clb.line(4))\n\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.delete_lines(2, 1, lb.ynum - 1)\n        self.ae(lb.line(0), clb.line(0))\n        self.ae(lb.line(1), clb.line(3))\n        self.ae(lb.line(2), clb.line(4))\n        self.ae(lb.line(3), lb2.line(0))\n        self.ae(lb.line(4), lb2.line(0))\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.delete_lines(10, 0, lb.ynum - 1)\n        for i in range(lb.ynum):\n            self.ae(lb.line(i), lb2.line(0))\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.delete_lines(10, 1, lb.ynum - 1)\n        self.ae(lb.line(0), clb.line(0))\n        for i in range(1, lb.ynum):\n            self.ae(lb.line(i), lb2.line(0))\n        lb = filled_line_buf(5, 5, filled_cursor())\n        lb.delete_lines(1, 1, 3)\n        self.ae(lb.line(0), clb.line(0))\n        self.ae(lb.line(1), clb.line(2))\n        self.ae(lb.line(2), clb.line(3))\n        self.ae(lb.line(3), lb2.line(0))\n        self.ae(lb.line(4), clb.line(4))\n\n        lb = filled_line_buf(5, 5, filled_cursor())\n        l0 = lb.line(0)\n        l0.add_combining_char(1, '\\u0300')\n        l0.clear_text(1, 2)\n        self.ae(str(l0), '0  00')\n        self.assertEqualAttributes(l0.cursor_from(1), l0.cursor_from(0))\n\n        lb = filled_line_buf(10, 10, filled_cursor())\n        lb.clear()\n        lb2 = LineBuf(lb.ynum, lb.ynum)\n        for i in range(lb.ynum):\n            self.ae(lb.line(i), lb2.line(i))\n\n    def test_line(self):\n        lb = LineBuf(2, 3)\n        for y in range(lb.ynum):\n            line = lb.line(y)\n            self.ae(str(line), '')\n            for x in range(lb.xnum):\n                self.ae(line[x], '\\0')\n        with self.assertRaises(IndexError):\n            lb.line(lb.ynum)\n        with self.assertRaises(IndexError):\n            lb.line(0)[lb.xnum]\n        l0 = lb.line(0)\n        l0.set_text(' ', 0, len(' '), C())\n        l0.add_combining_char(0, '\\u0300')\n        self.ae(l0[0], ' \\u0300')\n        l0.add_combining_char(0, '\\U000e0100')\n        self.ae(l0[0], ' \\u0300\\U000e0100')\n        l0.add_combining_char(0, '\\u0302')\n        self.ae(l0[0], ' \\u0300\\U000e0100\\u0302')\n        l0.add_combining_char(0, '\\u0301')\n        self.ae(l0[0], ' \\u0300\\U000e0100\\u0302\\u0301')\n        self.ae(l0[1], '\\0')\n        self.ae(str(l0), ' \\u0300\\U000e0100\\u0302\\u0301')\n        t = 'Testing with simple text'\n        lb = LineBuf(2, len(t))\n        l0 = lb.line(0)\n        l0.set_text(t, 0, len(t), C())\n        self.ae(str(l0), t)\n        l0.set_text('a', 0, 1, C())\n        self.assertEqual(str(l0), 'a' + t[1:])\n\n        c = C(3, 5)\n        c.bold = c.italic = c.reverse = c.strikethrough = c.dim = True\n        c.fg = c.bg = c.decoration_fg = 0x0101\n        self.ae(c, c)\n        c2, c3 = c.copy(), c.copy()\n        self.ae(repr(c), repr(c2))\n        self.ae(c, c2)\n        c2.bold = False\n        self.assertNotEqual(c, c2)\n        l0.set_text(t, 0, len(t), C())\n        l0.apply_cursor(c2, 3)\n        self.assertEqualAttributes(c2, l0.cursor_from(3))\n        l0.apply_cursor(c2, 0, len(l0))\n        for i in range(len(l0)):\n            self.assertEqualAttributes(c2, l0.cursor_from(i))\n        l0.apply_cursor(c3, 0)\n        self.assertEqualAttributes(c3, l0.cursor_from(0))\n        l0.copy_char(0, l0, 1)\n        self.assertEqualAttributes(c3, l0.cursor_from(1))\n\n        t = '0123456789'\n        lb = LineBuf(1, len(t))\n        l3 = lb.line(0)\n        l3.set_text(t, 0, len(t), C())\n        self.ae(t, str(l3))\n\n        l3.set_text(t, 0, len(t), C())\n        q = C()\n        q.bold = q.italic = q.reverse = q.strikethrough = c.dim = True\n        q.decoration = 2\n        c = C()\n        c.x = 3\n        l3.set_text('axyb', 1, 2, c)\n        self.ae(str(l3), '012xy56789')\n        l3.set_char(0, 'x', 1, q)\n        self.assertEqualAttributes(l3.cursor_from(0), q)\n\n    def test_url_at(self):\n        self.set_options()\n\n        def create(t):\n            lb = create.lb = LineBuf(1, len(t))\n            lf = lb.line(0)\n            lf.set_text(t, 0, len(t), C())\n            return lf\n\n        l0 = create('file:///etc/test')\n        self.ae(l0.url_start_at(0), 0)\n\n        for trail in '.,\\\\}]>':\n            lx = create(\"http://xyz.com\" + trail)\n            self.ae(lx.url_end_at(0), len(lx) - 2)\n        for trail in ')':\n            turl = \"http://xyz.com\" + trail\n            lx = create(turl)\n            self.ae(len(lx) - 1, lx.url_end_at(0), repr(turl))\n        l0 = create(\"ftp://abc/\")\n        self.ae(l0.url_end_at(0), len(l0) - 1)\n        l2 = create(\"http://-abcd] \")\n        self.ae(l2.url_end_at(0), len(l2) - 3)\n        l3 = create(\"http://ab.de           \")\n        self.ae(l3.url_start_at(4), 0)\n        self.ae(l3.url_start_at(5), 0)\n\n        def lspace_test(n, scheme='http'):\n            lf = create(' ' * n + scheme + '://acme.com')\n            for i in range(0, n):\n                self.ae(lf.url_start_at(i), len(lf))\n            for i in range(n, len(lf)):\n                self.ae(lf.url_start_at(i), n)\n        for i in range(7):\n            for scheme in 'http https ftp file'.split():\n                lspace_test(i, scheme)\n        l3 = create('b https://testing.me a')\n        for s in (0, 1, len(l3) - 1, len(l3) - 2):\n            self.ae(l3.url_start_at(s), len(l3), 'failed with start at: %d' % s)\n        for s in range(2, len(l3) - 2):\n            self.ae(l3.url_start_at(s), 2, 'failed with start at: %d (%s)' % (s, str(l3)[s:]))\n\n        def no_url(t):\n            lf = create(t)\n            for s in range(len(lf)):\n                self.ae(lf.url_start_at(s), len(lf))\n        no_url('https:// testing.me a')\n        no_url('h ttp://acme.com')\n        no_url('http: //acme.com')\n        no_url('http:/ /acme.com')\n\n        l4 = create(' xxxxxtekljhgdkjgd')\n        self.ae(l4.url_end_at(0), 0)\n\n        for trail in '/-&':\n            l4 = create('http://a.b?q=1' + trail)\n            self.ae(l4.url_end_at(1), len(l4) - 1)\n\n        l4 = create('http://a.b.')\n        self.ae(l4.url_end_at(0), len(l4) - 2)\n        self.ae(l4.url_end_at(0, 0, True), len(l4) - 1)\n\n    def rewrap(self, lb, lines, columns):\n        return lb.rewrap(lines, columns)\n\n    def test_rewrap_simple(self):\n        ' Same width buffers '\n        lb = filled_line_buf(5, 5)\n        lb2 = LineBuf(lb.ynum, lb.xnum)\n        lb2 = self.rewrap(lb, lb.ynum, lb.xnum)[0]\n        for i in range(lb.ynum):\n            self.ae(lb2.line(i), lb.line(i))\n        lb2, _, cy = self.rewrap(lb, 8, 5)\n        self.ae(cy, 5)\n        for i in range(lb.ynum):\n            self.ae(lb2.line(i), lb.line(i), i)\n        empty = LineBuf(1, lb2.xnum)\n        for i in range(lb.ynum, lb2.ynum):\n            self.ae(str(lb2.line(i)), str(empty.line(0)))\n        lb2 = LineBuf(3, 5)\n        lb2, _, cy = self.rewrap(lb, 3, 5)\n        self.ae(cy, 3)\n        for i in range(lb2.ynum):\n            self.ae(lb2.line(i), lb.line(i + 2))\n        self.assertFalse(lb.dirty_lines())\n        self.ae(lb2.dirty_lines(), list(range(lb2.ynum)))\n\n    def line_comparison(self, buf, *lines):\n        for i, l0 in enumerate(lines):\n            l2 = buf.line(i)\n            self.ae(l0, str(l2))\n\n    def line_comparison_rewrap(self, lb, *lines):\n        lb2 = self.rewrap(lb, len(lines), max(map(len, lines)))[0]\n        self.line_comparison(lb2, *lines)\n        return lb2\n\n    def assertContinued(self, lb, *vals):\n        self.ae(list(vals), [lb.is_continued(i) for i in range(len(vals))])\n\n    def test_rewrap_wider(self):\n        ' New buffer wider '\n        lb = create_lbuf('0123 ', '56789')\n        lb2 = self.line_comparison_rewrap(lb, '0123 5', '6789', '')\n        self.assertContinued(lb2, False, True)\n        self.ae(lb2.dirty_lines(), [0, 1])\n\n        lb = create_lbuf('12', 'abc')\n        lb2 = self.line_comparison_rewrap(lb, '12', 'abc')\n        self.assertContinued(lb2, False, False)\n\n    def test_rewrap_narrower(self):\n        ' New buffer narrower '\n        lb = create_lbuf('123', 'abcde')\n        lb2 = self.line_comparison_rewrap(lb, '123', 'abc', 'de')\n        self.assertContinued(lb2, False, False, True)\n        lb = create_lbuf('123  ', 'abcde')\n        lb2 = self.line_comparison_rewrap(lb, '123', '  a', 'bcd', 'e')\n        self.assertContinued(lb2, False, True, True, True)\n\n    def test_utils(self):\n        def w(x):\n            return wcwidth(ord(x))\n        self.ae(wcswidth('\\x9c'), 0)\n        self.ae(wcswidth('a\\033[2mb'), 2)\n        self.ae(wcswidth('\\033a\\033[2mb'), 2)\n        self.ae(wcswidth('a\\033]8;id=moo;https://foo\\033\\\\a'), 2)\n        self.ae(wcswidth('a\\033x'), 2)\n        self.ae(tuple(map(w, 'a1\\0コニチ ✔')), (1, 1, 0, 2, 2, 2, 1, 1))\n        self.ae(wcswidth('\\u2716\\u2716\\ufe0f\\U0001f337'), 5)\n        self.ae(wcswidth('\\u25b6\\ufe0f'), 2)\n        self.ae(wcswidth('\\U0001f610\\ufe0e'), 1)\n        self.ae(wcswidth('\\U0001f1e6a'), 3)\n        self.ae(wcswidth('\\U0001F1E6a\\U0001F1E8a'), 6)\n        self.ae(wcswidth('\\U0001F1E6\\U0001F1E8a'), 3)\n        self.ae(wcswidth('\\U0001F1E6\\U0001F1E8\\U0001F1E6'), 4)\n        self.ae(wcswidth('a\\u00adb'), 2)\n        # Regional indicator symbols (unicode flags) are defined as having\n        # Emoji_Presentation so must have width 2 but combined must have\n        # width 2 not 4\n        self.ae(tuple(map(w, '\\U0001f1ee\\U0001f1f3')), (2, 2))\n        self.ae(wcswidth('\\U0001f1ee\\U0001f1f3'), 2)\n        tpl = truncate_point_for_length\n        self.ae(tpl('abc', 4), 3)\n        self.ae(tpl('abc', 2), 2)\n        self.ae(tpl('abc', 0), 0)\n        self.ae(tpl('a\\U0001f337', 2), 1)\n        self.ae(tpl('a\\U0001f337', 3), 2)\n        self.ae(tpl('a\\U0001f337b', 4), 3)\n        self.ae(tpl('a\\x1b[31mbc', 2), 7)\n\n        self.ae(sanitize_title('a\\0\\01 \\t\\n\\f\\rb'), 'a b')\n\n        def tp(*data, leftover='', text='', csi='', apc='', ibp=False):\n            text_r, csi_r, apc_r, rest = [], [], [], []\n            left = ''\n            in_bp = ibp\n\n            def on_csi(x):\n                nonlocal in_bp\n                if x == '200~':\n                    in_bp = True\n                elif x == '201~':\n                    in_bp = False\n                csi_r.append(x)\n\n            for d in data:\n                left = parse_input_from_terminal(text_r.append, rest.append, on_csi, rest.append, rest.append, apc_r.append, left + d, in_bp)\n            self.ae(left, leftover)\n            self.ae(text, ' '.join(text_r))\n            self.ae(csi, ' '.join(csi_r))\n            self.ae(apc, ' '.join(apc_r))\n            self.assertFalse(rest)\n\n        tp('a\\033[200~\\033[32mxy\\033[201~\\033[33ma', text='a \\033[32m xy a', csi='200~ 201~ 33m')\n        tp('abc', text='abc')\n        tp('a\\033[38:5:12:32mb', text='a b', csi='38:5:12:32m')\n        tp('a\\033_x,;(\\033\\\\b', text='a b', apc='x,;(')\n        tp('a\\033', '[', 'mb', text='a b', csi='m')\n        tp('a\\033[', 'mb', text='a b', csi='m')\n        tp('a\\033', '_', 'x\\033', '\\\\b', text='a b', apc='x')\n        tp('a\\033_', 'x', '\\033', '\\\\', 'b', text='a b', apc='x')\n\n        for prefix in ('/tmp', tempfile.gettempdir()):\n            for path in ('a.png', 'x/b.jpg', 'y/../c.jpg'):\n                self.assertTrue(is_path_in_temp_dir(os.path.join(prefix, path)))\n        for path in ('/home/xy/d.png', '/tmp/../home/x.jpg'):\n            self.assertFalse(is_path_in_temp_dir(os.path.join(path)))\n        for path in ('/proc/self/cmdline', os.devnull):\n            if os.path.exists(path):\n                with open(path) as pf:\n                    self.assertFalse(is_ok_to_read_image_file(path, pf.fileno()), path)\n        fifo = os.path.join(tempfile.gettempdir(), 'test-kitty-fifo')\n        os.mkfifo(fifo)\n        fifo_fd = os.open(fifo, os.O_RDONLY | os.O_NONBLOCK)\n        try:\n            self.assertFalse(is_ok_to_read_image_file(fifo, fifo_fd), fifo)\n        finally:\n            os.close(fifo_fd)\n            os.remove(fifo)\n        if os.path.isdir('/dev/shm'):\n            with tempfile.NamedTemporaryFile(dir='/dev/shm') as tf:\n                self.assertTrue(is_ok_to_read_image_file(tf.name, tf.fileno()), fifo)\n        self.ae(sanitize_url_for_display_to_user(\n            'h://a\\u0430b.com/El%20Ni%C3%B1o/'), 'h://xn--ab-7kc.com/El Niño/')\n        for x in ('~', '~/', '', '~root', '~root/~', '/~', '/a/b/', '~xx/a', '~~'):\n           self.assertEqual(os.path.expanduser(x), expanduser(x), x)\n        for x in (\n            '/', '', '/a', '/ab', '/ab/', '/ab/c', 'a', 'ab', 'ab/', 'ab///c', 'ab/././..', '.', '..', '../', './', '../..', '../.',\n            '/a/../..', '/a/../../', '/a/..', '/ab/../../../cd/.', '///',\n        ):\n           self.assertEqual(os.path.abspath(x), abspath(x), repr(x))\n        self.assertEqual('/', abspath('//'))\n        with tempfile.TemporaryDirectory() as tdir:\n            for x, ex in {\n                'a': None, 'a/b/c': None, 'a/..': None, 'a/../a': None,\n                'a/f': NotADirectoryError, 'a/f/d': NotADirectoryError, 'a/b/c/f/g': NotADirectoryError,\n            }.items():\n                q = os.path.join(tdir, x)\n                if ex is None:\n                    makedirs(q)\n                    open(os.path.join(q, 'f'), 'wb').close()\n                else:\n                    with self.assertRaises(ex, msg=x):\n                        makedirs(q)\n        saved = {x: os.environ.get(x) for x in 'KITTY_CONFIG_DIRECTORY XDG_CONFIG_DIRS XDG_CONFIG_HOME'.split()}\n        try:\n            dot_config = os.path.expanduser('~/.config')\n            if os.path.exists(dot_config):\n                shutil.rmtree(dot_config)\n            with tempfile.TemporaryDirectory() as tdir:\n                with open(tdir + '/macos-launch-services-cmdline', 'w') as f:\n                    print('kitty +runpy \"import sys; print(sys.argv[-1])\"', file=f)\n                    print('next-line', file=f)\n                    print()\n                if is_macos:\n                    env = os.environ.copy()\n                    env['KITTY_CONFIG_DIRECTORY'] = tdir\n                    env['KITTY_LAUNCHED_BY_LAUNCH_SERVICES'] = '1'\n                    cp = subprocess.run([kitty_exe(), '+runpy', 'import json, sys; print(json.dumps(sys.argv))'], env=env, stdout=subprocess.PIPE)\n                    actual = cp.stdout.strip().decode()\n                    if cp.returncode != 0:\n                        print(actual)\n                        raise AssertionError(f'kitty +runpy failed with return code: {cp.returncode}')\n                    self.ae('next-line', actual)\n                os.makedirs(tdir + '/good/kitty')\n                open(tdir + '/good/kitty/kitty.conf', 'w').close()\n                data = os.urandom(32879)\n                with open(tdir + '/f', 'wb') as f:\n                    f.write(data)\n                self.ae(data, read_file(f.name))\n                for x in (\n                    (f'KITTY_CONFIG_DIRECTORY={tdir}', f'{tdir}'),\n                    (f'XDG_CONFIG_HOME={tdir}/good', f'{tdir}/good/kitty'),\n                    (f'XDG_CONFIG_DIRS={tdir}:{tdir}/good', f'{tdir}/good/kitty'),\n                    (f'XDG_CONFIG_DIRS={tdir}:{tdir}/bad:{tdir}/f', f'{dot_config}/kitty'),\n                    (f'{dot_config}/kitty',),\n                ):\n                    for k in saved:\n                        os.environ.pop(k, None)\n                    for e in x[:-1]:\n                        k, v = e.partition('=')[::2]\n                        os.environ[k] = v\n                    self.assertEqual(x[-1], get_config_dir(), str(x))\n        finally:\n            if os.path.exists(dot_config):\n                shutil.rmtree(dot_config)\n            for k in saved:\n                os.environ.pop(k, None)\n                if saved[k] is not None:\n                    os.environ[k] = saved[k]\n\n    def test_historybuf(self):\n        lb = filled_line_buf()\n        hb = HistoryBuf(5, 5)\n        hb.push(lb.line(1))\n        hb.push(lb.line(2))\n        self.ae(hb.count, 2)\n        self.ae(hb.line(0), lb.line(2))\n        self.ae(hb.line(1), lb.line(1))\n        hb = filled_history_buf()\n        self.ae(str(hb.line(0)), '4' * hb.xnum)\n        self.ae(str(hb.line(4)), '0' * hb.xnum)\n        hb.push(lb.line(2))\n        self.ae(str(hb.line(0)), '2' * hb.xnum)\n        self.ae(str(hb.line(4)), '1' * hb.xnum)\n        hb = large_hb = HistoryBuf(3000, 5)\n        c = filled_cursor()\n        for i in range(3000):\n            line = lb.line(1)\n            t = str(i).ljust(5)\n            line.set_text(t, 0, 5, c)\n            hb.push(line)\n        for i in range(3000):\n            self.ae(str(hb.line(i)).rstrip(), str(3000 - 1 - i))\n\n        # rewrap\n        def as_ansi(hb):\n            lines = []\n            hb.as_ansi(lines.append)\n            return ''.join(lines)\n        hb = filled_history_buf(5, 5)\n        for i in range(hb.ynum):\n            hb.line(i).set_wrapped_flag(True)\n        before = as_ansi(hb)\n        hb2 = hb.rewrap(10)\n        self.ae(before, as_ansi(hb2).rstrip())\n\n        hb = filled_history_buf(5, 5)\n        hb2 = hb.rewrap(hb.xnum)\n        for i in range(hb.ynum):\n            self.ae(hb2.line(i), hb.line(i))\n        hb = filled_history_buf(5, 5)\n        hb2 = hb.rewrap(hb.xnum * 2)\n        hb3 = HistoryBuf(hb.ynum, hb.xnum)\n        hb3 = hb2.rewrap(hb.xnum)\n        for i in range(hb.ynum):\n            self.ae(hb.line(i), hb3.line(i))\n\n        hb2 = HistoryBuf(hb.ynum, hb.xnum)\n        hb2 = large_hb.rewrap(hb.xnum)\n        hb2.rewrap(large_hb.xnum)\n\n    def test_ansi_repr(self):\n        lb = filled_line_buf()\n        l0 = lb.line(0)\n        self.ae(l0.as_ansi(), '00000')\n        a = []\n        lb.as_ansi(a.append)\n        self.ae(a, [str(lb.line(i)) + '\\n' for i in range(lb.ynum)])\n        l2 = lb.line(0)\n        c = C()\n        c.bold = c.italic = c.reverse = c.strikethrough = c.dim = True\n        c.fg = (4 << 8) | 1\n        c.bg = (1 << 24) | (2 << 16) | (3 << 8) | 2\n        c.decoration_fg = (5 << 8) | 1\n        l2.set_text('1', 0, 1, c)\n        self.ae(str(l2), '10000')\n        self.ae(l2.as_ansi(), '\\x1b[1;2;3;7;9;34;48:2:1:2:3;58:5:5m' '1' '\\x1b[22;23;27;29;39;49;59m' '0000')  # ]]\n        lb = filled_line_buf()\n        for i in range(1, lb.ynum + 1):\n            lb.set_continued(i, True)\n        a = []\n        lb.as_ansi(a.append)\n        self.ae(a, [str(lb.line(i)) for i in range(lb.ynum)])\n        hb = filled_history_buf(5, 5)\n        a = []\n        hb.as_ansi(a.append)\n        self.ae(a, [str(hb.line(i)) + '\\n' for i in range(hb.count - 1, -1, -1)])\n\n    def test_strip_csi(self):\n        def q(x, y=''):\n            self.ae(y or x, strip_csi(x))\n        q('test')\n        q('a\\x1bbc', 'abc')\n        q('a\\x1b[bc', 'ac')\n        q('a\\x1b[12;34:43mbc', 'abc')\n        q('a\\x1b[12;34:43\\U0001f638', 'a\\U0001f638')\n\n    def test_single_key(self):\n        from kitty.fast_data_types import GLFW_MOD_KITTY, GLFW_MOD_SHIFT, SingleKey\n        for m in (GLFW_MOD_KITTY, GLFW_MOD_SHIFT):\n            s = SingleKey(mods=m)\n            self.ae(s.mods, m)\n        self.ae(tuple(iter(SingleKey())), (0, False, 0))\n        self.ae(tuple(SingleKey(key=sys.maxunicode, mods=GLFW_MOD_SHIFT, is_native=True)), (GLFW_MOD_SHIFT, True, sys.maxunicode))\n        self.ae(repr(SingleKey()), 'SingleKey()')\n        self.ae(repr(SingleKey(key=23, mods=2, is_native=True)), 'SingleKey(mods=2, is_native=True, key=23)')\n        self.ae(repr(SingleKey(key=23, mods=2)), 'SingleKey(mods=2, key=23)')\n        self.ae(repr(SingleKey(key=23)), 'SingleKey(key=23)')\n        self.ae(repr(SingleKey(key=0x1008ff57)), 'SingleKey(key=269025111)')\n        self.ae(repr(SingleKey(key=23)._replace(mods=2)), 'SingleKey(mods=2, key=23)')\n        self.ae(repr(SingleKey(key=23)._replace(key=-1, mods=GLFW_MOD_KITTY)), f'SingleKey(mods={GLFW_MOD_KITTY})')\n        self.assertEqual(SingleKey(key=1), SingleKey(key=1))\n        self.assertEqual(hash(SingleKey(key=1)), hash(SingleKey(key=1)))\n        self.assertNotEqual(hash(SingleKey(key=1, mods=2)), hash(SingleKey(key=1)))\n        self.assertNotEqual(SingleKey(key=1, mods=2), SingleKey(key=1))\n\n    def test_notify_identifier_sanitization(self):\n        from kitty.notifications import sanitize_identifier_pat\n        self.ae(sanitize_identifier_pat().sub('', '\\x1b\\nabc\\n[*'), 'abc')\n\n    def test_bracketed_paste_sanitizer(self):\n        from kitty.utils import sanitize_for_bracketed_paste\n        for x in ('\\x1b[201~ab\\x9b201~cd', '\\x1b[201\\x1b[201~~ab'):  # ]]]\n            q = sanitize_for_bracketed_paste(x.encode('utf-8'))\n            self.assertNotIn(b'\\x1b[201~', q)\n            self.assertNotIn('\\x9b201~'.encode(), q)\n            self.assertIn(b'ab', q)\n\n    def test_expand_ansi_c_escapes(self):\n        for src, expected in {\n            'abc': 'abc',\n            r'a\\ab': 'a\\ab',\n            r'a\\eb': 'a\\x1bb',\n            r'a\\r\\nb': 'a\\r\\nb',\n            r'a\\c b': 'a\\0b',\n            r'a\\c': 'a\\\\c',\n            r'a\\x1bb': 'a\\x1bb',\n            r'a\\x1b': 'a\\x1b',\n            r'a\\x1': 'a\\x01',\n            r'a\\x1g': 'a\\x01g',\n            r'a\\z\\\"': 'a\\\\z\"',\n            r'a\\123b': 'a\\123b',\n            r'a\\128b': 'a\\0128b',\n            r'a\\u1234e': 'a\\u1234e',\n            r'a\\U1f1eez': 'a\\U0001f1eez',\n            r'a\\x1\\\\':    \"a\\x01\\\\\",\n        }.items():\n            actual = expand_ansi_c_escapes(src)\n            self.ae(expected, actual)\n\n    def test_shlex_split(self):\n        for bad in (\n            'abc\\\\', '\\\\', \"'abc\", \"'\", '\"', 'asd' + '\\\\', r'\"a\\\"', '\"a\\\\',\n        ):\n            with self.assertRaises(ValueError, msg=f'Failed to raise exception for {bad!r}'):\n                tuple(shlex_split_with_positions(bad))\n            with self.assertRaises(ValueError, msg=f'Failed to raise exception for {bad!r}'):\n                tuple(shlex_split(bad))\n\n        for q, expected in {\n            'a\"\"': ((0, 'a'),),\n            'a\"\"b': ((0, 'ab'),),\n            '-1 \"\" 2': ((0, '-1'), (3, ''), (6, '2')),\n            \"-1 '' 2\": ((0, '-1'), (3, ''), (6, '2')),\n            'a \"\"': ((0, 'a'), (2, '')),\n            '\"\"': ((0, ''),),\n            '\"ab\"': ((0, 'ab'),),\n            r'x \"ab\"y \\m': ((0, 'x'), (2, 'aby'), (8, 'm')),\n            r'''x'y\"\\z'1''': ((0, 'xy\"\\\\z1'),),\n            r'\\abc\\ d': ((0, 'abc d'),),\n            '': ((0, ''),), '   ': ((0, ''),), ' \\tabc\\n\\t\\r ': ((2, 'abc'),),\n            \"$'ab'\": ((0, '$ab'),),\n            '😀': ((0, '😀'),),\n            '\"a😀\"': ((0, 'a😀'),),\n            '😀 a': ((0, '😀'), (2, 'a')),\n            ' \\t😀a': ((2, '😀a'),),\n        }.items():\n            ex = tuple(x[1] for x in expected)\n            actual = tuple(shlex_split(q))\n            self.ae(ex, actual, f'Failed for text: {q!r}')\n            actual = tuple(shlex_split_with_positions(q))\n            self.ae(expected, actual, f'Failed for text: {q!r}')\n\n        for q, expected in {\n            \"$'ab'\": ((0, 'ab'),),\n            \"1$'ab'\": ((0, '1ab'),),\n            '''\"1$'ab'\"''': ((0, \"1$'ab'\"),),\n            r\"$'a\\123b'\": ((0, 'a\\123b'),),\n            r\"$'a\\1b'\": ((0, 'a\\001b'),),\n            r\"$'a\\12b'\": ((0, 'a\\012b'),),\n            r\"$'a\\db'\": ((0, 'adb'),),\n            r\"$'a\\x1bb'\": ((0, 'a\\x1bb'),),\n            r\"$'\\u123z'\": ((0, '\\u0123z'),),\n            r\"$'\\U0001F1E8'\": ((0, '\\U0001F1E8'),),\n            r\"$'\\U1F1E8'\": ((0, '\\U0001F1E8'),),\n            r\"$'a\\U1F1E8'b\": ((0, 'a\\U0001F1E8b'),),\n        }.items():\n            actual = tuple(shlex_split_with_positions(q, True))\n            self.ae(expected, actual, f'Failed for text: {q!r}')\n            actual = tuple(shlex_split(q, True))\n            ex = tuple(x[1] for x in expected)\n            self.ae(ex, actual, f'Failed for text: {q!r}')\n\n    def test_split_into_graphemes(self):\n        self.assertEqual(char_props_for('\\ue000')['category'], 'Co')\n        self.ae(split_into_graphemes('ab'), ['a', 'b'])\n        s = self.create_screen(cols=12)\n        excluded_chars = set(range(32))\n\n        def is_excluded(text):\n            return bool(set(map(ord, text)) & excluded_chars)\n\n        def adapt_cell_text(cells):\n            for cell in cells:\n                gp = split_into_graphemes(cell)\n                if len(gp) == 1:\n                    yield cell\n                else:\n                    for i, g in enumerate(gp[:-1]):\n                        if wcswidth(gp[i+1][0]) != 0:\n                            raise AssertionError(\n                                f'cell {cell!r} contains grapheme break point at non zero width character for Test #{i}: {test[\"comment\"]}')\n                    yield from gp\n\n        for i, test in enumerate(json.loads(read_kitty_resource('GraphemeBreakTest.json', __name__.rpartition('.')[0]))):\n            expected = test['data']\n            text = ''.join(expected)\n            actual = split_into_graphemes(text)\n            self.ae(expected, actual, f'Test #{i} failed: {test[\"comment\"]}')\n            if is_excluded(text):\n                continue\n            s.carriage_return(), s.erase_in_line()\n            s.draw(' ' + text)\n            actual = []\n            for x in range(s.cursor.x):\n                cell = s.cpu_cells(0, x)\n                if cell['x'] > 0:\n                    continue\n                ct = cell['text']\n                if x == 0:\n                    ct = ct[1:]\n                if ct:\n                    actual.append(ct)\n            self.ae(expected, list(adapt_cell_text(actual)), f'Test #{i} failed: {test[\"comment\"]}')\n        s.reset()\n        s.draw('a' * s.columns)\n        s.draw('\\u0306')\n        self.ae(str(s.line(0)), 'a' * s.columns + '\\u0306')\n        s.reset()\n        s.draw('\\0')\n        self.ae(str(s.line(0)), '')\n"
  },
  {
    "path": "kitty_tests/file_transmission.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport shutil\nimport stat\nimport tempfile\nfrom collections import namedtuple\nfrom contextlib import contextmanager\nfrom pathlib import Path\n\nfrom kittens.transfer.rsync import Differ, Hasher, Patcher, parse_ftc\nfrom kittens.transfer.utils import set_paths\nfrom kitty.constants import kitten_exe\nfrom kitty.file_transmission import Action, Compression, FileTransmissionCommand, FileType, TransmissionType, ZlibDecompressor\nfrom kitty.file_transmission import TestFileTransmission as FileTransmission\n\nfrom . import PTY, BaseTest\n\n\ndef response(id='test', msg='', file_id='', name='', action='status', status='', size=-1):\n    ans = {'action': 'status'}\n    if id:\n        ans['id'] = id\n    if file_id:\n        ans['file_id'] = file_id\n    if name:\n        ans['name'] = name\n    if status:\n        ans['status'] = status\n    if size > -1:\n        ans['size'] = size\n    return ans\n\n\ndef names_in(path):\n    for dirpath, dirnames, filenames in os.walk(path):\n        for d in dirnames + filenames:\n            yield os.path.relpath(os.path.join(dirpath, d), path)\n\n\ndef serialized_cmd(**fields) -> str:\n    if 'id' not in fields:\n        fields['id'] = 'test'\n    for k, A in (('action', Action), ('ftype', FileType), ('ttype', TransmissionType), ('compression', Compression)):\n        if k in fields:\n            fields[k] = A[fields[k]]\n    if isinstance(fields.get('data'), str):\n        fields['data'] = fields['data'].encode('utf-8')\n    ans = FileTransmissionCommand(**fields)\n    return ans.serialize()\n\n\ndef generate_data(block_size, num_blocks, *extra) -> bytes:\n    extra = ''.join(extra)\n    b = b'_' * (block_size * num_blocks) + extra.encode()\n    ans = bytearray(b)\n    for i in range(num_blocks):\n        offset = i * block_size\n        p = str(i).encode()\n        ans[offset:offset+len(p)] = p\n    return bytes(ans)\n\n\ndef patch_data(data, *patches):\n    total_patch_size = 0\n    ans = bytearray(data)\n    for patch in patches:\n        o, sep, r = patch.partition(':')\n        r = r.encode()\n        total_patch_size += len(r)\n        offset = int(o)\n        ans[offset:offset+len(r)] = r\n    return bytes(ans), len(patches), total_patch_size\n\n\ndef run_roundtrip_test(self: 'TestFileTransmission', src_data, changed, num_of_patches, total_patch_size):\n    buf = memoryview(bytearray(30))\n    signature = bytearray(0)\n    p = Patcher(len(changed))\n    n = p.signature_header(buf)\n    signature.extend(buf[:n])\n    src = memoryview(changed)\n    bs = p.block_size\n    while src:\n        n = p.sign_block(src[:bs], buf)\n        signature.extend(buf[:n])\n        src = src[bs:]\n    d = Differ()\n    src = memoryview(signature)\n    while src:\n        d.add_signature_data(src[:13])\n        src = src[13:]\n    d.finish_signature_data()\n    del src, signature\n    src = memoryview(src_data)\n    delta = bytearray(0)\n    def read_into(b):\n        nonlocal src\n        n = min(len(b), len(src))\n        if n > 0:\n            b[:n] = src[:n]\n            src = src[n:]\n        return n\n    def write_delta(b):\n        delta.extend(b)\n    while d.next_op(read_into, write_delta):\n        pass\n    delta = memoryview(delta)\n    del src\n\n    def read_at(pos, output) -> int:\n        b = changed[pos:]\n        amt = min(len(output), len(b))\n        output[:amt] = b[:amt]\n        return amt\n\n    output = bytearray(0)\n\n    def write_changes(b):\n        output.extend(b)\n\n    def debug_msg():\n        return f'\\n\\nsrc:\\n{src_data.decode()}\\nchanged:\\n{changed.decode()}\\noutput:\\n{output.decode()}'\n    try:\n        while delta:\n            p.apply_delta_data(delta[:11], read_at, write_changes)\n            delta = delta[11:]\n        p.finish_delta_data()\n    except Exception as err:\n        self.fail(f'{err}\\n{debug_msg()}')\n    self.assertEqual(src_data, bytes(output), debug_msg())\n    limit = 2 * (p.block_size * num_of_patches)\n    if limit > -1:\n        self.assertLessEqual(\n            p.total_data_in_delta, limit, f'Unexpectedly poor delta performance: {total_patch_size=} {p.total_data_in_delta=} {limit=}')\n\n\ndef test_rsync_roundtrip(self: 'TestFileTransmission') -> None:\n    block_size = 16\n    src_data = generate_data(block_size, 16)\n    changed, num_of_patches, total_patch_size = patch_data(src_data, \"3:patch1\", \"16:patch2\", \"130:ptch3\", \"176:patch4\", \"222:XXYY\")\n\n    run_roundtrip_test(self, src_data, src_data[block_size:], 1, block_size)\n    run_roundtrip_test(self, src_data, changed, num_of_patches, total_patch_size)\n    run_roundtrip_test(self, src_data, b'', -1, 0)\n    run_roundtrip_test(self, src_data, src_data, 0, 0)\n    run_roundtrip_test(self, src_data, changed[:len(changed)-3], num_of_patches, total_patch_size)\n    run_roundtrip_test(self, src_data, changed[:37] + changed[81:], num_of_patches, total_patch_size)\n\n    block_size = 13\n    src_data = generate_data(block_size, 17, \"trailer\")\n    changed, num_of_patches, total_patch_size = patch_data(src_data, \"0:patch1\", \"19:patch2\")\n    run_roundtrip_test(self, src_data, changed, num_of_patches, total_patch_size)\n    run_roundtrip_test(self, src_data, changed[:len(changed)-3], num_of_patches, total_patch_size)\n    run_roundtrip_test(self, src_data, changed + b\"xyz...\", num_of_patches, total_patch_size)\n\n\nclass PtyFileTransmission(FileTransmission):\n\n    def __init__(self, pty, allow=True):\n        self.pty = pty\n        super().__init__(allow=allow)\n        self.pty.callbacks.ftc = self\n\n    def write_ftc_to_child(self, payload: FileTransmissionCommand, appendleft: bool = False, use_pending: bool = True) -> bool:\n        # print('to kitten:', payload)\n        self.pty.write_to_child('\\x1b]' + payload.serialize(prefix_with_osc_code=True) + '\\x1b\\\\', flush=False)\n        return True\n\n\nclass TransferPTY(PTY):\n\n    def __init__(self, cmd, cwd, allow=True, env=None):\n        super().__init__(cmd, cwd=cwd, env=env, rows=200, columns=120)\n        self.fc = PtyFileTransmission(self, allow=allow)\n\n\nclass TestFileTransmission(BaseTest):\n\n    def setUp(self):\n        self.direction_receive = False\n        self.kitty_home = self.kitty_cwd = self.kitten_home = self.kitten_cwd = ''\n        super().setUp()\n        self.tdir = os.path.realpath(tempfile.mkdtemp())\n        self.responses = []\n        self.orig_home = os.environ.get('HOME')\n\n    def tearDown(self):\n        self.rmtree_ignoring_errors(self.tdir)\n        self.responses = []\n        if self.orig_home is None:\n            os.environ.pop('HOME', None)\n        else:\n            os.environ['HOME'] = self.orig_home\n        super().tearDown()\n\n    def clean_tdir(self):\n        for x in os.listdir(self.tdir):\n            x = os.path.join(self.tdir, x)\n            if os.path.isdir(x):\n                shutil.rmtree(x)\n            else:\n                os.remove(x)\n        self.responses = []\n\n    def cr(self, a, b):\n        def f(r):\n            r.pop('size', None)\n            return r\n        a = tuple(f(r) for r in a if r.get('status') != 'PROGRESS')\n        b = tuple(f(r) for r in b if r.get('status') != 'PROGRESS')\n        self.ae(a, b)\n\n    def assertResponses(self, ft, limit=1024, **kw):\n        self.responses.append(response(**kw))\n        self.cr(ft.test_responses[:limit], self.responses[:limit])\n\n    def assertPathEqual(self, a, b):\n        a = os.path.abspath(os.path.realpath(a))\n        b = os.path.abspath(os.path.realpath(b))\n        self.ae(a, b)\n\n    def test_rsync_roundtrip(self):\n        test_rsync_roundtrip(self)\n\n    def test_file_get(self):\n        # send refusal\n        for quiet in (0, 1, 2):\n            ft = FileTransmission(allow=False)\n            ft.handle_serialized_command(serialized_cmd(action='receive', id='x', quiet=quiet))\n            self.cr(ft.test_responses, [] if quiet == 2 else [response(id='x', status='EPERM:User refused the transfer')])\n            self.assertFalse(ft.active_sends)\n        # reading metadata for specs\n        cwd = os.path.join(self.tdir, 'cwd')\n        home = os.path.join(self.tdir, 'home')\n        os.mkdir(cwd), os.mkdir(home)\n        with set_paths(cwd=cwd, home=home):\n            ft = FileTransmission()\n            self.responses = []\n            ft.handle_serialized_command(serialized_cmd(action='receive', size=1))\n            self.assertResponses(ft, status='OK')\n            ft.handle_serialized_command(serialized_cmd(action='file', file_id='missing', name='XXX'))\n            self.responses.append(response(status='ENOENT:Failed to read spec', file_id='missing'))\n            self.assertResponses(ft, status='OK', name=home)\n            ft = FileTransmission()\n            self.responses = []\n            ft.handle_serialized_command(serialized_cmd(action='receive', size=2))\n            self.assertResponses(ft, status='OK')\n            with open(os.path.join(home, 'a'), 'w') as f:\n                f.write('a')\n            os.mkdir(f.name + 'd')\n            with open(os.path.join(f.name + 'd', 'b'), 'w') as f2:\n                f2.write('bbb')\n            os.symlink(f.name, f.name + 'd/s')\n            os.link(f.name, f.name + 'd/h')\n            os.symlink('XXX', f.name + 'd/q')\n            ft.handle_serialized_command(serialized_cmd(action='file', file_id='a', name='a'))\n            ft.handle_serialized_command(serialized_cmd(action='file', file_id='b', name='ad'))\n            files = {r['name']: r for r in ft.test_responses if r['action'] == 'file'}\n            self.ae(len(files), 6)\n            q = files[f.name]\n            tgt = q['status'].encode('ascii')\n            self.ae(q['size'], 1), self.assertNotIn('ftype', q)\n            q = files[f.name + 'd']\n            self.ae(q['ftype'], 'directory')\n            q = files[f.name + 'd/b']\n            self.ae(q['size'], 3)\n            q = files[f.name + 'd/s']\n            self.ae(q['ftype'], 'symlink')\n            self.ae(q['data'], tgt)\n            q = files[f.name + 'd/h']\n            self.ae(q['ftype'], 'link')\n            self.ae(q['data'], tgt)\n            q = files[f.name + 'd/q']\n            self.ae(q['ftype'], 'symlink')\n            self.assertNotIn('data', q)\n        base = os.path.join(self.tdir, 'base')\n        os.mkdir(base)\n        src = os.path.join(base, 'src.bin')\n        data = os.urandom(16 * 1024)\n        with open(src, 'wb') as f:\n            f.write(data)\n        sl = os.path.join(base, 'src.link')\n        os.symlink(src, sl)\n        for compress in ('none', 'zlib'):\n            ft = FileTransmission()\n            self.responses = []\n            ft.handle_serialized_command(serialized_cmd(action='receive', size=1))\n            self.assertResponses(ft, status='OK')\n            ft.handle_serialized_command(serialized_cmd(action='file', file_id='src', name=src))\n            ft.active_sends['test'].metadata_sent = True\n            ft.test_responses = []\n            ft.handle_serialized_command(serialized_cmd(action='file', file_id='src', name=src, compression=compress))\n            received = b''.join(x['data'] for x in ft.test_responses)\n            if compress == 'zlib':\n                received = ZlibDecompressor()(received, True)\n            self.ae(data, received)\n            ft.test_responses = []\n            ft.handle_serialized_command(serialized_cmd(action='file', file_id='sl', name=sl, compression=compress))\n            received = b''.join(x['data'] for x in ft.test_responses)\n            self.ae(received.decode('utf-8'), src)\n\n    def test_parse_ftc(self):\n        def t(raw, *expected):\n            a = []\n\n            def c(k, v):\n                a.append(str(k, 'utf-8'))\n                a.append(str(v, 'utf-8'))\n\n            parse_ftc(raw, c)\n            self.ae(tuple(a), expected)\n\n        t('a=b', 'a', 'b')\n        t('a=b;', 'a', 'b')\n        t('a1=b1;c=d;;', 'a1', 'b1', 'c', 'd')\n        t('a1=b1;c=d;;e', 'a1', 'b1', 'c', 'd')\n        t('a1=b1;c=d;;;1=1', 'a1', 'b1', 'c', 'd', '1', '1')\n\n    def test_rsync_hashers(self):\n        h = Hasher(\"xxh3-64\")\n        h.update(b'abcd')\n        self.assertEqual(h.hexdigest(), '6497a96f53a89890')\n        self.assertEqual(h.digest64(), 7248448420886124688)\n        h128 = Hasher(\"xxh3-128\")\n        h128.update(b'abcd')\n        self.assertEqual(h128.hexdigest(), '8d6b60383dfa90c21be79eecd1b1353d')\n\n    @contextmanager\n    def run_kitten(self, cmd, home_dir='', allow=True, cwd=''):\n        cwd = cwd or self.kitten_cwd or self.tdir\n        cmd = [kitten_exe(), 'transfer'] + (['--direction=receive'] if self.direction_receive else []) + cmd\n        env = {'PWD': cwd}\n        env['HOME'] = home_dir or self.kitten_home or self.tdir\n        with set_paths(home=self.kitty_home, cwd=self.kitty_cwd):\n            pty = TransferPTY(cmd, cwd=cwd, allow=allow, env=env)\n            i = 10\n            while i > 0 and not pty.screen_contents().strip():\n                pty.process_input_from_child()\n                i -= 1\n            yield pty\n\n    def basic_transfer_tests(self):\n        src = os.path.join(self.tdir, 'src')\n        self.src_data = os.urandom(11113)\n        with open(src, 'wb') as s:\n            s.write(self.src_data)\n        dest = os.path.join(self.tdir, 'dest')\n        with self.run_kitten([src, dest], allow=False) as pty:\n            pty.wait_till_child_exits(require_exit_code=1)\n        self.assertFalse(os.path.exists(dest))\n\n        def single_file(*cmd):\n            with self.run_kitten(list(cmd) + [src, dest]) as pty:\n                pty.wait_till_child_exits(require_exit_code=0)\n            with open(dest, 'rb') as f:\n                self.assertEqual(self.src_data, f.read())\n\n        single_file()\n        single_file()\n        single_file('--transmit-deltas')\n        with open(dest, 'wb') as d:\n            d.write(os.urandom(1023))\n        single_file('--transmit-deltas')\n        os.remove(dest)\n        single_file('--transmit-deltas')\n        single_file('--compress=never')\n        single_file('--compress=always')\n        single_file('--transmit-deltas', '--compress=never')\n\n        def multiple_files(*cmd):\n            src = os.path.join(self.tdir, 'msrc')\n            dest = os.path.join(self.tdir, 'mdest')\n            if os.path.exists(src):\n                shutil.rmtree(src)\n            os.mkdir(src)\n            os.makedirs(dest, exist_ok=True)\n\n            expected = {}\n            Entry = namedtuple('Entry', 'relpath mtime mode nlink')\n\n            def entry(path, base=src):\n                st = os.stat(path, follow_symlinks=False)\n                mtime = st.st_mtime_ns\n                if stat.S_ISDIR(st.st_mode):\n                    mtime = 0  # mtime is flaky for dirs on CI even empty ones\n                return Entry(os.path.relpath(path, base), mtime, oct(st.st_mode), st.st_nlink)\n\n            def se(path):\n                e = entry(path)\n                expected[e.relpath] = e\n\n            b = Path(src)\n            with open(b / 'simple', 'wb') as f:\n                f.write(os.urandom(1317))\n                os.fchmod(f.fileno(), 0o766)\n            os.link(f.name, b / 'hardlink')\n            os.utime(f.name, (1.3, 1.3))\n            se(f.name)\n            se(str(b/'hardlink'))\n            os.mkdir(b / 'empty')\n            se(str(b/'empty'))\n            s = b / 'sub'\n            os.mkdir(s)\n            with open(s / 'reg', 'wb') as f:\n                f.write(os.urandom(113))\n            os.utime(f.name, (1171.3, 1171.3))\n            se(f.name)\n            se(str(s))\n            os.symlink('/', b/'abssym')\n            os.utime(b/'abssym', (1234.5, 1234.5), follow_symlinks=False)\n            se(b/'abssym')\n            os.symlink('sub/reg', b/'sym')\n            os.utime(b/'sym', (6789.1, 6789.1), follow_symlinks=False)\n            se(b/'sym')\n\n            with self.run_kitten(list(cmd) + [src, dest]) as pty:\n                pty.wait_till_child_exits(require_exit_code=0)\n\n            actual = {}\n            def de(path):\n                e = entry(path, os.path.join(dest, os.path.basename(src)))\n                if e.relpath != '.':\n                    actual[e.relpath] = e\n\n            for dirpath, dirnames, filenames in os.walk(dest):\n                for x in dirnames:\n                    de(os.path.join(dirpath, x))\n                for x in filenames:\n                    de(os.path.join(dirpath, x))\n\n            self.assertEqual(expected, actual)\n\n            for key, e in expected.items():\n                ex = os.path.join(src, key)\n                ax = os.path.join(dest, os.path.basename(src), key)\n                if os.path.islink(ex):\n                    self.ae(os.readlink(ex), os.readlink(ax))\n                elif os.path.isfile(ex):\n                    with open(ex, 'rb') as ef, open(ax, 'rb') as af:\n                        self.assertEqual(ef.read(), af.read())\n\n        multiple_files()\n        multiple_files('--compress=always')\n        self.clean_tdir()\n        multiple_files('--transmit-deltas')\n        multiple_files('--transmit-deltas')\n\n    def setup_dirs(self):\n        self.clean_tdir()\n        self.kitty_home = os.path.join(self.tdir, 'kitty-home')\n        self.kitty_cwd = os.path.join(self.tdir, 'kitty-cwd')\n        self.kitten_home = os.path.join(self.tdir, 'kitten-home')\n        self.kitten_cwd = os.path.join(self.tdir, 'kitten-cwd')\n        tuple(map(os.mkdir, (self.kitty_home, self.kitty_cwd, self.kitten_home, self.kitten_cwd)))\n\n    def create_src(self, base):\n        src = os.path.join(base, 'src')\n        with open(src, 'wb') as s:\n            s.write(self.src_data)\n        return src\n\n    def mirror_test(self, src, dest, prefix=''):\n        self.create_src(src)\n        os.symlink('/', os.path.join(src, 'sym'))\n        os.mkdir(os.path.join(src, 'sub'))\n        os.link(os.path.join(src, 'src'), os.path.join(src, 'sub', 'hardlink'))\n        with self.run_kitten(['--mode=mirror', f'{prefix}src', f'{prefix}sym', f'{prefix}sub']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(dest, 'src'))\n        os.remove(os.path.join(dest, 'sym'))\n        shutil.rmtree(os.path.join(dest, 'sub'))\n\n    def test_transfer_receive(self):\n        self.direction_receive = True\n        self.basic_transfer_tests()\n\n        self.setup_dirs()\n        self.create_src(self.kitty_home)\n\n        # dir expansion with single transfer\n        with self.run_kitten(['~/src', '~/src']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(self.kitten_home, 'src'))\n\n        with self.run_kitten(['src', 'src']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(self.kitten_cwd, 'src'))\n\n        # dir expansion with multiple transfers\n        os.symlink('/', os.path.join(self.kitty_home, 'sym'))\n        with self.run_kitten(['~/src', '~/sym', '~']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(self.kitten_home, 'src'))\n        os.remove(os.path.join(self.kitten_home, 'sym'))\n\n        with self.run_kitten(['src', 'sym', '.']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(self.kitten_cwd, 'src'))\n        os.remove(os.path.join(self.kitten_cwd, 'sym'))\n\n        # mirroring\n        self.setup_dirs()\n        self.mirror_test(self.kitty_home, self.kitten_home)\n\n    def test_transfer_send(self):\n        self.basic_transfer_tests()\n        src = os.path.join(self.tdir, 'src')\n        with open(src, 'wb') as s:\n            s.write(self.src_data)\n\n        self.setup_dirs()\n        self.create_src(self.kitten_home)\n\n        # dir expansion with single transfer\n        with self.run_kitten(['~/src', '~/src']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(self.kitty_home, 'src'))\n\n        self.create_src(self.kitten_cwd)\n        with self.run_kitten(['src', 'src']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(self.kitty_home, 'src'))\n\n        # dir expansion with multiple transfers\n        os.symlink('/', os.path.join(self.kitten_home, 'sym'))\n        with self.run_kitten(['~/src', '~/sym', '~']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(self.kitty_home, 'src'))\n        os.remove(os.path.join(self.kitty_home, 'sym'))\n\n        os.symlink('/', os.path.join(self.kitten_cwd, 'sym'))\n        with self.run_kitten(['src', 'sym', '.']) as pty:\n            pty.wait_till_child_exits(require_exit_code=0)\n        os.remove(os.path.join(self.kitty_home, 'src'))\n        os.remove(os.path.join(self.kitty_home, 'sym'))\n\n        # mirroring\n        self.setup_dirs()\n        self.mirror_test(self.kitten_home, self.kitty_home, prefix='~/')\n"
  },
  {
    "path": "kitty_tests/fonts.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport array\nimport os\nimport tempfile\nimport unittest\nfrom collections.abc import Iterable\nfrom functools import lru_cache, partial\nfrom itertools import repeat\nfrom math import ceil\n\nfrom kitty.constants import is_macos, read_kitty_resource\nfrom kitty.fast_data_types import (\n    DECAWM,\n    ParsedFontFeature,\n    get_fallback_font,\n    set_allow_use_of_box_fonts,\n    sprite_idx_to_pos,\n    sprite_map_set_layout,\n    sprite_map_set_limits,\n    test_render_line,\n    test_sprite_position_increment,\n    wcwidth,\n)\nfrom kitty.fonts import family_name_to_key\nfrom kitty.fonts.common import FontSpec, all_fonts_map, face_from_descriptor, get_font_files, get_named_style, spec_for_face\nfrom kitty.fonts.render import coalesce_symbol_maps, create_face, render_string, setup_for_testing, shape_string\nfrom kitty.options.types import Options\n\nfrom . import BaseTest, draw_multicell\n\n\ndef parse_font_spec(spec):\n    return FontSpec.from_setting(spec)\n\n\n@lru_cache(maxsize=64)\ndef testing_font_data(name):\n    return read_kitty_resource(name, __name__.rpartition('.')[0])\n\n\nclass Selection(BaseTest):\n\n    def test_font_selection(self):\n        self.set_options({'font_features': {'LiberationMono': (ParsedFontFeature('-dlig'),)}})\n        opts = Options()\n        fonts_map = all_fonts_map(True)\n        names = set(fonts_map['family_map']) | set(fonts_map['variable_map'])\n        del fonts_map\n\n        def s(family: str, *expected: str, alternate=None) -> None:\n            opts.font_family = parse_font_spec(family)\n            ff = get_font_files(opts)\n            actual = tuple(face_from_descriptor(ff[x]).postscript_name() for x in ('medium', 'bold', 'italic', 'bi'))  # type: ignore\n            del ff\n            for x in actual:\n                if '/' in x:  # Old FreeType failed to generate postscript name for a variable font probably\n                    return\n            with self.subTest(spec=family):\n                try:\n                    self.ae(expected, actual)\n                except AssertionError:\n                    if alternate:\n                        self.ae(alternate, actual)\n                    else:\n                        raise\n\n        def both(family: str, *expected: str, alternate=None) -> None:\n            for family in (family, f'family=\"{family}\"'):\n                s(family, *expected, alternate=alternate)\n\n        def has(family, allow_missing_in_ci=False):\n            ans = family_name_to_key(family) in names\n            if self.is_ci and not allow_missing_in_ci and not ans:\n                raise AssertionError(f'The family: {family} is not available')\n            return ans\n\n        def t(family, psprefix, bold='Bold', italic='Italic', bi='', reg='Regular', allow_missing_in_ci=False, alternate=None):\n            if has(family, allow_missing_in_ci=allow_missing_in_ci):\n                bi = bi or bold + italic\n                if reg:\n                    reg = '-' + reg\n                both(family, f'{psprefix}{reg}', f'{psprefix}-{bold}', f'{psprefix}-{italic}', f'{psprefix}-{bi}', alternate=alternate)\n\n        t('Source Code Pro', 'SourceCodePro', 'Semibold', 'It')\n        t('sourcecodeVf', 'SourceCodeVF', 'Semibold')\n\n        # The Arch ttf-fira-code package excludes the variable fonts for some reason\n        t('fira code', 'FiraCodeRoman', 'SemiBold', 'Regular', 'SemiBold', alternate=(\n            'FiraCode-Regular', 'FiraCode-SemiBold', 'FiraCode-Retina', 'FiraCode-SemiBold'))\n        t('hack', 'Hack')\n        # some ubuntu systems (such as the build VM) have only the regular and\n        # bold faces of DejaVu Sans Mono installed.\n        # t('DejaVu Sans Mono', 'DejaVuSansMono', reg='', italic='Oblique')\n        t('ubuntu mono', 'UbuntuMono')\n        t('liberation mono', 'LiberationMono', reg='')\n        t('ibm plex mono', 'IBMPlexMono', 'SmBld', reg='')\n        t('iosevka fixed', 'Iosevka-Fixed', 'Semibold', reg='', bi='Semibold-Italic', allow_missing_in_ci=True)\n        t('iosevka term', 'Iosevka-Term', 'Semibold', reg='', bi='Semibold-Italic', allow_missing_in_ci=True)\n        t('fantasque sans mono', 'FantasqueSansMono')\n        t('jetbrains mono', 'JetBrainsMono', 'SemiBold')\n        t('consolas', 'Consolas', reg='', allow_missing_in_ci=True)\n        if has('cascadia code'):\n            if is_macos:\n                both('cascadia code', 'CascadiaCode-Regular', 'CascadiaCode-Regular_SemiBold', 'CascadiaCode-Italic', 'CascadiaCode-Italic_SemiBold-Italic')\n            else:\n                both('cascadia code', 'CascadiaCodeRoman-Regular', 'CascadiaCodeRoman-SemiBold', 'CascadiaCode-Italic', 'CascadiaCode-SemiBoldItalic')\n        if has('cascadia mono'):\n            if is_macos:\n                both('cascadia mono', 'CascadiaMono-Regular', 'CascadiaMono-Regular_SemiBold', 'CascadiaMono-Italic', 'CascadiaMono-Italic_SemiBold-Italic')\n            else:\n                both('cascadia mono', 'CascadiaMonoRoman-Regular', 'CascadiaMonoRoman-SemiBold', 'CascadiaMono-Italic', 'CascadiaMono-SemiBoldItalic')\n        if has('operator mono', allow_missing_in_ci=True):\n            both('operator mono', 'OperatorMono-Medium', 'OperatorMono-Bold', 'OperatorMono-MediumItalic', 'OperatorMono-BoldItalic')\n\n        # Test variable font selection\n\n        if has('SourceCodeVF'):\n            opts = Options()\n            opts.font_family = parse_font_spec('family=\"SourceCodeVF\" variable_name=\"SourceCodeUpright\" style=\"Bold\"')\n            ff = get_font_files(opts)\n            face = face_from_descriptor(ff['medium'])\n            self.ae(get_named_style(face)['name'], 'Bold')\n            face = face_from_descriptor(ff['italic'])\n            self.ae(get_named_style(face)['name'], 'Bold Italic')\n            face = face_from_descriptor(ff['bold'])\n            self.ae(get_named_style(face)['name'], 'Black')\n            face = face_from_descriptor(ff['bi'])\n            self.ae(get_named_style(face)['name'], 'Black Italic')\n            opts.font_family = parse_font_spec('family=SourceCodeVF variable_name=SourceCodeUpright wght=470')\n            opts.italic_font = parse_font_spec('family=SourceCodeVF variable_name=SourceCodeItalic style=Black')\n            ff = get_font_files(opts)\n            self.assertFalse(get_named_style(ff['medium']))\n            self.ae(get_named_style(ff['italic'])['name'], 'Black Italic')\n        if has('cascadia code'):\n            opts = Options()\n            opts.font_family = parse_font_spec('family=\"cascadia code\"')\n            opts.italic_font = parse_font_spec('family=\"cascadia code\" variable_name= style=\"Light Italic\"')\n            ff = get_font_files(opts)\n\n            def t(x, **kw):\n                if 'spec' in kw:\n                    fs = FontSpec.from_setting('family=\"Cascadia Code\" ' + kw['spec'])._replace(created_from_string='')\n                else:\n                    kw['family'] = 'Cascadia Code'\n                    fs = FontSpec(**kw)\n                face = face_from_descriptor(ff[x])\n                self.ae(fs.as_setting, spec_for_face('Cascadia Code', face).as_setting)\n\n            t('medium', variable_name='CascadiaCodeRoman', style='Regular')\n            t('italic', variable_name='', style='Light Italic')\n\n            opts = Options()\n            opts.font_family = parse_font_spec('family=\"cascadia code\" variable_name=CascadiaCodeRoman wght=455')\n            opts.italic_font = parse_font_spec('family=\"cascadia code\" variable_name= wght=405')\n            opts.bold_font = parse_font_spec('family=\"cascadia code\" variable_name=CascadiaCodeRoman wght=603')\n            ff = get_font_files(opts)\n            t('medium', spec='variable_name=CascadiaCodeRoman wght=455')\n            t('italic', spec='variable_name= wght=405')\n            t('bold', spec='variable_name=CascadiaCodeRoman wght=603')\n            t('bi', spec='variable_name= wght=603')\n\n        # Test font features\n        if has('liberation mono'):\n            opts = Options()\n            opts.font_family = parse_font_spec('family=\"liberation mono\"')\n            ff = get_font_files(opts)\n            self.ae(face_from_descriptor(ff['medium']).applied_features(), {'dlig': '-dlig'})\n            self.ae(face_from_descriptor(ff['bold']).applied_features(), {})\n            opts.font_family = parse_font_spec('family=\"liberation mono\" features=\"dlig test=3\"')\n            ff = get_font_files(opts)\n            self.ae(face_from_descriptor(ff['medium']).applied_features(), {'dlig': 'dlig', 'test': 'test=3'})\n            self.ae(face_from_descriptor(ff['bold']).applied_features(), {'dlig': 'dlig', 'test': 'test=3'})\n\ndef block_helpers(s, sprites, cell_width, cell_height):\n    mr = {}\n    actual = b''\n    block_size = cell_width * cell_height * 4\n\n    def full_block():\n        return b'\\xff' * block_size\n\n    def empty_block():\n        return b'\\0' * block_size\n\n    def half_block(first=b'\\xff', second=b'\\0', swap=False):\n        frac = 0.5\n        height = ceil(frac * cell_height)\n        rest = cell_height - height\n        if swap:\n            height, rest = rest, height\n            first, second = second, first\n        return (first * (height * cell_width * 4)) + (second * rest * cell_width * 4)\n\n    def quarter_block():\n        frac = 0.5\n        height = ceil(frac * cell_height)\n        width = ceil(frac * cell_width)\n        ans = array.array('I', b'\\0' * block_size)\n        for y in range(height):\n            pos = cell_width * y\n            for x in range(width):\n                ans[pos + x] = 0xffffffff\n        return ans.tobytes()\n\n    def upper_half_block():\n        return half_block()\n\n    def lower_half_block():\n        return half_block(swap=True)\n\n    def block_as_str(a):\n        pixels = array.array('I', a)\n        def row(y):\n            pos = y * cell_width\n            return ' '.join(f'{int(pixels[pos + x] != 0)}' for x in range(cell_width))\n        return '\\n'.join(row(y) for y in range(cell_height))\n\n    def assert_blocks(a, b, msg=''):\n        nonlocal mr, actual, full_block, half_block, quarter_block, block_test, empty_block\n        if a != b:\n            del mr, actual, full_block, half_block, quarter_block, block_test, empty_block\n            msg = msg or 'block not equal'\n            if len(a) != len(b):\n                assert_blocks.__msg = msg + f' block lengths not equal: {len(a)/4} != {len(b)/4}'\n            else:\n                assert_blocks.__msg = msg + '\\n' + block_as_str(a) + '\\n\\n' + block_as_str(b)\n            del a, b\n            raise AssertionError(assert_blocks.__msg)\n\n    def multiline_render(text, scale=1, width=1, **kw):\n        s.reset()\n        draw_multicell(s, text, scale=scale, width=width, **kw)\n        ans = []\n        for y in range(scale):\n            line = s.line(y)\n            test_render_line(line)\n            for x in range(width * scale):\n                ans.append(sprites[sprite_idx_to_pos(line.sprite_at(x), setup_for_testing.xnum, setup_for_testing.ynum)])\n        return ans\n\n    def block_test(*expected, **kw):\n        nonlocal mr, actual\n        mr = multiline_render(kw.pop('text', '█'), **kw)\n        try:\n            z = zip(expected, mr, strict=True)\n        except TypeError:\n            z = zip(expected, mr)\n        for i, (expected, actual) in enumerate(z):\n            assert_blocks(expected(), actual, f'Block {i} expected != actual')\n\n\n    return full_block, empty_block, upper_half_block, lower_half_block, quarter_block, block_as_str, block_test\n\n\nclass FontBaseTest(BaseTest):\n\n    font_size = 16.0\n    dpi = 72.\n    font_name = 'FiraCode-Medium.otf'\n\n    def path_for_font(self, name):\n        if name not in self.font_path_cache:\n            with open(os.path.join(self.tdir, name), 'wb') as f:\n                self.font_path_cache[name] = f.name\n                f.write(testing_font_data(name))\n        return self.font_path_cache[name]\n\n    def setUp(self):\n        super().setUp()\n        self.font_path_cache = {}\n        self.tdir = tempfile.mkdtemp()\n        self.addCleanup(self.rmtree_ignoring_errors, self.tdir)\n        path = self.path_for_font(self.font_name) if self.font_name else ''\n        tc = setup_for_testing(size=self.font_size, dpi=self.dpi, main_face_path=path)\n        self.sprites, self.cell_width, self.cell_height = tc.__enter__()\n        self.addCleanup(tc.__exit__)\n        self.assertEqual([k[0] for k in self.sprites], list(range(11)))\n\n    def tearDown(self):\n        del self.sprites, self.cell_width, self.cell_height\n        self.font_path_cache = {}\n        super().tearDown()\n\n\n\nclass Rendering(FontBaseTest):\n\n    def test_sprite_map(self):\n        sprite_map_set_limits(10, 3)\n        sprite_map_set_layout(5, 4)  # 4 because of underline_exclusion row\n        self.ae(test_sprite_position_increment(), (0, 0, 0))\n        self.ae(test_sprite_position_increment(), (1, 0, 0))\n        self.ae(test_sprite_position_increment(), (0, 1, 0))\n        self.ae(test_sprite_position_increment(), (1, 1, 0))\n        self.ae(test_sprite_position_increment(), (0, 0, 1))\n        self.ae(test_sprite_position_increment(), (1, 0, 1))\n        self.ae(test_sprite_position_increment(), (0, 1, 1))\n        self.ae(test_sprite_position_increment(), (1, 1, 1))\n        self.ae(test_sprite_position_increment(), (0, 0, 2))\n        self.ae(test_sprite_position_increment(), (1, 0, 2))\n\n    def test_box_drawing(self):\n        s = self.create_screen(cols=len(box_chars) + 1, lines=1, scrollback=0)\n        prerendered = len(self.sprites)\n        s.draw(''.join(box_chars))\n        line = s.line(0)\n        test_render_line(line)\n        self.assertEqual(len(self.sprites) - prerendered, len(box_chars))\n\n    def test_scaled_box_drawing(self):\n        self.scaled_drawing_test()\n\n    def test_scaled_font_drawing(self):\n        set_allow_use_of_box_fonts(False)\n        try:\n            self.scaled_drawing_test()\n        finally:\n            set_allow_use_of_box_fonts(True)\n\n    def scaled_drawing_test(self):\n        s = self.create_screen(cols=8, lines=8, scrollback=0)\n        full_block, empty_block, upper_half_block, lower_half_block, quarter_block, block_as_str, block_test = block_helpers(\n                s, self.sprites, self.cell_width, self.cell_height)\n        block_test(full_block)\n        block_test(full_block, full_block, full_block, full_block, scale=2)\n        block_test(full_block, empty_block, empty_block, empty_block, scale=2, subscale_n=1, subscale_d=2)\n        block_test(empty_block, full_block, empty_block, empty_block, scale=2, subscale_n=1, subscale_d=2, horizontal_align=1)\n        block_test(full_block, full_block, empty_block, empty_block, scale=2, subscale_n=1, subscale_d=2, text='██')\n        block_test(empty_block, empty_block, full_block, empty_block, scale=2, subscale_n=1, subscale_d=2, vertical_align=1)\n        block_test(quarter_block, scale=1, subscale_n=1, subscale_d=2)\n        block_test(upper_half_block, scale=1, subscale_n=1, subscale_d=2, text='██')\n        block_test(lower_half_block, scale=1, subscale_n=1, subscale_d=2, text='██', vertical_align=1)\n\n    def test_font_rendering(self):\n        render_string('ab\\u0347\\u0305你好|\\U0001F601|\\U0001F64f|\\U0001F63a|')\n        text = 'He\\u0347\\u0305llo\\u0341, w\\u0302or\\u0306l\\u0354d!'\n        # macOS has no fonts capable of rendering combining chars\n        if is_macos:\n            text = text.encode('ascii', 'ignore').decode('ascii')\n        cells = render_string(text)[-1]\n        self.ae(len(cells), len(text.encode('ascii', 'ignore')))\n        text = '你好,世界'\n        sz = sum(map(lambda x: wcwidth(ord(x)), text))\n        cells = render_string(text)[-1]\n        self.ae(len(cells), sz)\n\n    @unittest.skipIf(is_macos, 'COLRv1 is only supported on Linux')\n    def test_rendering_colrv1(self):\n        f = create_face(self.path_for_font('twemoji_smiley-cff2_colr_1.otf'))\n        f.set_size(64, 96, 96)\n        for char in '😁😇😈':\n            _, w, h = f.render_codepoint(ord(char))\n            self.assertGreater(w, 64)\n            self.assertGreater(h, 64)\n\n    def test_shaping(self):\n\n        def ss(text, font=None):\n            path = self.path_for_font(font) if font else None\n            return shape_string(text, path=path)\n\n        def groups(text, font=None):\n            return [x[:2] for x in ss(text, font)]\n\n        for font in ('FiraCode-Medium.otf', 'CascadiaCode-Regular.otf', 'iosevka-regular.ttf'):\n            g = partial(groups, font=font)\n            self.ae(g('abcd'), [(1, 1) for i in range(4)])\n            self.ae(g('A===B!=C'), [(1, 1), (3, 3), (1, 1), (2, 2), (1, 1)])\n            self.ae(g('A=>>B!=C'), [(1, 1), (3, 3), (1, 1), (2, 2), (1, 1)])\n            if 'iosevka' in font:\n                self.ae(g('--->'), [(4, 4)])\n                self.ae(g('-' * 12 + '>'), [(13, 13)])\n                self.ae(g('<~~~'), [(4, 4)])\n                self.ae(g('a<~~~b'), [(1, 1), (4, 4), (1, 1)])\n            else:\n                self.ae(g('----'), [(4, 4)])\n                self.ae(g('F--a--'), [(1, 1), (2, 2), (1, 1), (2, 2)])\n                self.ae(g('===--<>=='), [(3, 3), (2, 2), (2, 2), (2, 2)])\n                self.ae(g('==!=<>==<><><>'), [(4, 4), (2, 2), (2, 2), (2, 2), (2, 2), (2, 2)])\n                self.ae(g('-' * 18), [(18, 18)])\n            self.ae(g('a>\\u2060<b'), [(1, 1), (1, 2), (1, 1), (1, 1)])\n        colon_glyph = ss('9:30', font='FiraCode-Medium.otf')[1][2]\n        self.assertNotEqual(colon_glyph, ss(':', font='FiraCode-Medium.otf')[0][2])\n        self.ae(colon_glyph, 1031)\n        self.ae(groups('9:30', font='FiraCode-Medium.otf'), [(1, 1), (1, 1), (1, 1), (1, 1)])\n\n        self.ae(groups('|\\U0001F601|\\U0001F64f|\\U0001F63a|'), [(1, 1), (2, 1), (1, 1), (2, 1), (1, 1), (2, 1), (1, 1)])\n        self.ae(groups('He\\u0347\\u0305llo\\u0337,', font='LiberationMono-Regular.ttf'),\n                [(1, 1), (1, 3), (1, 1), (1, 1), (1, 2), (1, 1)])\n\n        self.ae(groups('i\\u0332\\u0308', font='LiberationMono-Regular.ttf'), [(1, 2)])\n        self.ae(groups('u\\u0332 u\\u0332\\u0301', font='LiberationMono-Regular.ttf'), [(1, 2), (1, 1), (1, 2)])\n\n    def test_emoji_presentation(self):\n        s = self.create_screen()\n        s.draw('\\u2716\\u2716\\ufe0f')\n        self.ae((s.cursor.x, s.cursor.y), (3, 0))\n        s.draw('\\u2716\\u2716')\n        self.ae((s.cursor.x, s.cursor.y), (5, 0))\n        s.draw('\\ufe0f')\n        self.ae((s.cursor.x, s.cursor.y), (2, 1))\n        self.ae(str(s.line(0)), '\\u2716\\u2716\\ufe0f\\u2716')\n        self.ae(str(s.line(1)), '\\u2716\\ufe0f')\n        s.draw('\\u2716' * 3)\n        self.ae((s.cursor.x, s.cursor.y), (5, 1))\n        self.ae(str(s.line(1)), '\\u2716\\ufe0f\\u2716\\u2716\\u2716')\n        self.ae((s.cursor.x, s.cursor.y), (5, 1))\n        s.reset_mode(DECAWM)\n        s.draw('\\ufe0f')\n        s.set_mode(DECAWM)\n        self.ae((s.cursor.x, s.cursor.y), (5, 1))\n        self.ae(str(s.line(1)), '\\u2716\\ufe0f\\u2716\\u2716\\ufe0f')\n        s.cursor.y = s.lines - 1\n        s.draw('\\u2716' * s.columns)\n        self.ae((s.cursor.x, s.cursor.y), (5, 4))\n        s.draw('\\ufe0f')\n        self.ae((s.cursor.x, s.cursor.y), (2, 4))\n        self.ae(str(s.line(s.cursor.y)), '\\u2716\\ufe0f')\n\n    @unittest.skipUnless(is_macos, 'Only macOS has a Last Resort font')\n    def test_fallback_font_not_last_resort(self):\n        # Ensure that the LastResort font is not reported as a fallback font on\n        # macOS. See https://github.com/kovidgoyal/kitty/issues/799\n        with self.assertRaises(ValueError, msg='No fallback font found'):\n            get_fallback_font('\\U0010FFFF', False, False)\n\n    def test_coalesce_symbol_maps(self):\n        q = {(2, 3): 'a', (4, 6): 'b', (5, 5): 'b', (7, 7): 'b', (9, 9): 'b', (1, 1): 'a'}\n        self.ae(coalesce_symbol_maps(q), {(1, 3): 'a', (4, 7): 'b', (9, 9): 'b'})\n        q = {(1, 4): 'a', (2, 3): 'b'}\n        self.ae(coalesce_symbol_maps(q), {(1, 1): 'a', (2, 3): 'b', (4, 4): 'a'})\n        q = {(2, 3): 'b', (1, 4): 'a'}\n        self.ae(coalesce_symbol_maps(q), {(1, 4): 'a'})\n        q = {(1, 4): 'a', (2, 5): 'b'}\n        self.ae(coalesce_symbol_maps(q), {(1, 1): 'a', (2, 5): 'b'})\n        q = {(2, 5): 'b', (1, 4): 'a'}\n        self.ae(coalesce_symbol_maps(q), {(1, 4): 'a', (5, 5): 'b'})\n        q = {(1, 4): 'a', (2, 5): 'a'}\n        self.ae(coalesce_symbol_maps(q), {(1, 5): 'a'})\n        q = {(1, 4): 'a', (4, 5): 'b'}\n        self.ae(coalesce_symbol_maps(q), {(1, 3): 'a', (4, 5): 'b'})\n        q = {(4, 5): 'b', (1, 4): 'a'}\n        self.ae(coalesce_symbol_maps(q), {(1, 4): 'a', (5, 5): 'b'})\n        q = {(0, 30): 'a', (10, 10): 'b', (11, 11): 'b', (2, 2): 'c', (1, 1): 'c'}\n        self.ae(coalesce_symbol_maps(q), {\n            (0, 0): 'a', (1, 2): 'c', (3, 9): 'a', (10, 11): 'b', (12, 30): 'a'})\n\ndef test_chars(chars: str = '╌', sz: int = 128) -> None:\n    # kitty +runpy \"from kitty.fonts.box_drawing import test_chars; test_chars('XXX')\"\n    from kitty.fast_data_types import concat_cells, render_box_char, set_send_sprite_to_gpu\n    from kitty.fonts.render import display_bitmap, setup_for_testing\n    if not chars:\n        import sys\n        chars = sys.argv[-1]\n\n    def as_ord(x: str) -> int:\n        if x.lower().startswith('u+'):\n            return int(x[2:], 16)\n        return ord(x)\n\n    if '...' in chars:\n        start, end = chars.partition('...')[::2]\n        chars = ''.join(map(chr, range(as_ord(start), as_ord(end)+1)))\n\n    with setup_for_testing('monospace', sz) as (_, width, height):\n        try:\n            for ch in chars:\n                nb = render_box_char(as_ord(ch), width, height)\n                rgb_data = concat_cells(width, height, False, (nb,))\n                display_bitmap(rgb_data, width, height)\n                print()\n        finally:\n            set_send_sprite_to_gpu(None)\n\n\ndef test_drawing(sz: int = 48, family: str = 'monospace', start: int = 0x2500, num_rows: int = 10, num_cols: int = 16) -> None:\n    from kitty.fast_data_types import concat_cells, render_box_char, set_send_sprite_to_gpu\n\n    from .render import display_bitmap, setup_for_testing\n\n    with setup_for_testing(family, sz) as (_, width, height):\n        space = bytearray(width * height)\n\n        def join_cells(cells: Iterable[bytes]) -> bytes:\n            cells = tuple(bytes(x) for x in cells)\n            return concat_cells(width, height, False, cells)\n\n        def render_chr(ch: str) -> bytearray:\n            if ch in box_chars:\n                return bytearray(render_box_char(ord(ch), width, height))\n            return space\n\n        pos = start\n        rows = []\n        space_row = join_cells(repeat(space, 32))\n\n        try:\n            for r in range(num_rows):\n                row = []\n                for i in range(num_cols):\n                    row.append(render_chr(chr(pos)))\n                    row.append(space)\n                    pos += 1\n                rows.append(join_cells(row))\n                rows.append(space_row)\n            rgb_data = b''.join(rows)\n            width *= 32\n            height *= len(rows)\n            assert len(rgb_data) == width * height * 4, f'{len(rgb_data)} != {width * height * 4}'\n            display_bitmap(rgb_data, width, height)\n        finally:\n            set_send_sprite_to_gpu(None)\n\nbox_chars = {  # {{{\n'─',\n '━',\n '│',\n '┃',\n '┄',\n '┅',\n '┆',\n '┇',\n '┈',\n '┉',\n '┊',\n '┋',\n '┌',\n '┍',\n '┎',\n '┏',\n '┐',\n '┑',\n '┒',\n '┓',\n '└',\n '┕',\n '┖',\n '┗',\n '┘',\n '┙',\n '┚',\n '┛',\n '├',\n '┝',\n '┞',\n '┟',\n '┠',\n '┡',\n '┢',\n '┣',\n '┤',\n '┥',\n '┦',\n '┧',\n '┨',\n '┩',\n '┪',\n '┫',\n '┬',\n '┭',\n '┮',\n '┯',\n '┰',\n '┱',\n '┲',\n '┳',\n '┴',\n '┵',\n '┶',\n '┷',\n '┸',\n '┹',\n '┺',\n '┻',\n '┼',\n '┽',\n '┾',\n '┿',\n '╀',\n '╁',\n '╂',\n '╃',\n '╄',\n '╅',\n '╆',\n '╇',\n '╈',\n '╉',\n '╊',\n '╋',\n '╌',\n '╍',\n '╎',\n '╏',\n '═',\n '║',\n '╒',\n '╓',\n '╔',\n '╕',\n '╖',\n '╗',\n '╘',\n '╙',\n '╚',\n '╛',\n '╜',\n '╝',\n '╞',\n '╟',\n '╠',\n '╡',\n '╢',\n '╣',\n '╤',\n '╥',\n '╦',\n '╧',\n '╨',\n '╩',\n '╪',\n '╫',\n '╬',\n '╭',\n '╮',\n '╯',\n '╰',\n '╱',\n '╲',\n '╳',\n '╴',\n '╵',\n '╶',\n '╷',\n '╸',\n '╹',\n '╺',\n '╻',\n '╼',\n '╽',\n '╾',\n '╿',\n '▀',\n '▁',\n '▂',\n '▃',\n '▄',\n '▅',\n '▆',\n '▇',\n '█',\n '▉',\n '▊',\n '▋',\n '▌',\n '▍',\n '▎',\n '▏',\n '▐',\n '░',\n '▒',\n '▓',\n '▔',\n '▕',\n '▖',\n '▗',\n '▘',\n '▙',\n '▚',\n '▛',\n '▜',\n '▝',\n '▞',\n '▟',\n '◉',\n '○',\n '●',\n '◖',\n '◗',\n '◜',\n '◝',\n '◞',\n '◟',\n '◠',\n '◡',\n '◢',\n '◣',\n '◤',\n '◥',\n '⠀',\n '⠁',\n '⠂',\n '⠃',\n '⠄',\n '⠅',\n '⠆',\n '⠇',\n '⠈',\n '⠉',\n '⠊',\n '⠋',\n '⠌',\n '⠍',\n '⠎',\n '⠏',\n '⠐',\n '⠑',\n '⠒',\n '⠓',\n '⠔',\n '⠕',\n '⠖',\n '⠗',\n '⠘',\n '⠙',\n '⠚',\n '⠛',\n '⠜',\n '⠝',\n '⠞',\n '⠟',\n '⠠',\n '⠡',\n '⠢',\n '⠣',\n '⠤',\n '⠥',\n '⠦',\n '⠧',\n '⠨',\n '⠩',\n '⠪',\n '⠫',\n '⠬',\n '⠭',\n '⠮',\n '⠯',\n '⠰',\n '⠱',\n '⠲',\n '⠳',\n '⠴',\n '⠵',\n '⠶',\n '⠷',\n '⠸',\n '⠹',\n '⠺',\n '⠻',\n '⠼',\n '⠽',\n '⠾',\n '⠿',\n '⡀',\n '⡁',\n '⡂',\n '⡃',\n '⡄',\n '⡅',\n '⡆',\n '⡇',\n '⡈',\n '⡉',\n '⡊',\n '⡋',\n '⡌',\n '⡍',\n '⡎',\n '⡏',\n '⡐',\n '⡑',\n '⡒',\n '⡓',\n '⡔',\n '⡕',\n '⡖',\n '⡗',\n '⡘',\n '⡙',\n '⡚',\n '⡛',\n '⡜',\n '⡝',\n '⡞',\n '⡟',\n '⡠',\n '⡡',\n '⡢',\n '⡣',\n '⡤',\n '⡥',\n '⡦',\n '⡧',\n '⡨',\n '⡩',\n '⡪',\n '⡫',\n '⡬',\n '⡭',\n '⡮',\n '⡯',\n '⡰',\n '⡱',\n '⡲',\n '⡳',\n '⡴',\n '⡵',\n '⡶',\n '⡷',\n '⡸',\n '⡹',\n '⡺',\n '⡻',\n '⡼',\n '⡽',\n '⡾',\n '⡿',\n '⢀',\n '⢁',\n '⢂',\n '⢃',\n '⢄',\n '⢅',\n '⢆',\n '⢇',\n '⢈',\n '⢉',\n '⢊',\n '⢋',\n '⢌',\n '⢍',\n '⢎',\n '⢏',\n '⢐',\n '⢑',\n '⢒',\n '⢓',\n '⢔',\n '⢕',\n '⢖',\n '⢗',\n '⢘',\n '⢙',\n '⢚',\n '⢛',\n '⢜',\n '⢝',\n '⢞',\n '⢟',\n '⢠',\n '⢡',\n '⢢',\n '⢣',\n '⢤',\n '⢥',\n '⢦',\n '⢧',\n '⢨',\n '⢩',\n '⢪',\n '⢫',\n '⢬',\n '⢭',\n '⢮',\n '⢯',\n '⢰',\n '⢱',\n '⢲',\n '⢳',\n '⢴',\n '⢵',\n '⢶',\n '⢷',\n '⢸',\n '⢹',\n '⢺',\n '⢻',\n '⢼',\n '⢽',\n '⢾',\n '⢿',\n '⣀',\n '⣁',\n '⣂',\n '⣃',\n '⣄',\n '⣅',\n '⣆',\n '⣇',\n '⣈',\n '⣉',\n '⣊',\n '⣋',\n '⣌',\n '⣍',\n '⣎',\n '⣏',\n '⣐',\n '⣑',\n '⣒',\n '⣓',\n '⣔',\n '⣕',\n '⣖',\n '⣗',\n '⣘',\n '⣙',\n '⣚',\n '⣛',\n '⣜',\n '⣝',\n '⣞',\n '⣟',\n '⣠',\n '⣡',\n '⣢',\n '⣣',\n '⣤',\n '⣥',\n '⣦',\n '⣧',\n '⣨',\n '⣩',\n '⣪',\n '⣫',\n '⣬',\n '⣭',\n '⣮',\n '⣯',\n '⣰',\n '⣱',\n '⣲',\n '⣳',\n '⣴',\n '⣵',\n '⣶',\n '⣷',\n '⣸',\n '⣹',\n '⣺',\n '⣻',\n '⣼',\n '⣽',\n '⣾',\n '⣿',\n '\\ue0b0',\n '\\ue0b1',\n '\\ue0b2',\n '\\ue0b3',\n '\\ue0b4',\n '\\ue0b5',\n '\\ue0b6',\n '\\ue0b7',\n '\\ue0b8',\n '\\ue0b9',\n '\\ue0ba',\n '\\ue0bb',\n '\\ue0bc',\n '\\ue0bd',\n '\\ue0be',\n '\\ue0bf',\n '\\ue0d6',\n '\\ue0d7',\n '\\uee00',\n '\\uee01',\n '\\uee02',\n '\\uee03',\n '\\uee04',\n '\\uee05',\n '\\uee06',\n '\\uee07',\n '\\uee08',\n '\\uee09',\n '\\uee0a',\n '\\uee0b',\n '\\uf5d0',\n '\\uf5d1',\n '\\uf5d2',\n '\\uf5d3',\n '\\uf5d4',\n '\\uf5d5',\n '\\uf5d6',\n '\\uf5d7',\n '\\uf5d8',\n '\\uf5d9',\n '\\uf5da',\n '\\uf5db',\n '\\uf5dc',\n '\\uf5dd',\n '\\uf5de',\n '\\uf5df',\n '\\uf5e0',\n '\\uf5e1',\n '\\uf5e2',\n '\\uf5e3',\n '\\uf5e4',\n '\\uf5e5',\n '\\uf5e6',\n '\\uf5e7',\n '\\uf5e8',\n '\\uf5e9',\n '\\uf5ea',\n '\\uf5eb',\n '\\uf5ec',\n '\\uf5ed',\n '\\uf5ee',\n '\\uf5ef',\n '\\uf5f0',\n '\\uf5f1',\n '\\uf5f2',\n '\\uf5f3',\n '\\uf5f4',\n '\\uf5f5',\n '\\uf5f6',\n '\\uf5f7',\n '\\uf5f8',\n '\\uf5f9',\n '\\uf5fa',\n '\\uf5fb',\n '\\uf5fc',\n '\\uf5fd',\n '\\uf5fe',\n '\\uf5ff',\n '\\uf600',\n '\\uf601',\n '\\uf602',\n '\\uf603',\n '\\uf604',\n '\\uf605',\n '\\uf606',\n '\\uf607',\n '\\uf608',\n '\\uf609',\n '\\uf60a',\n '\\uf60b',\n '\\uf60c',\n '\\uf60d',\n '🬀',\n '🬁',\n '🬂',\n '🬃',\n '🬄',\n '🬅',\n '🬆',\n '🬇',\n '🬈',\n '🬉',\n '🬊',\n '🬋',\n '🬌',\n '🬍',\n '🬎',\n '🬏',\n '🬐',\n '🬑',\n '🬒',\n '🬓',\n '🬔',\n '🬕',\n '🬖',\n '🬗',\n '🬘',\n '🬙',\n '🬚',\n '🬛',\n '🬜',\n '🬝',\n '🬞',\n '🬟',\n '🬠',\n '🬡',\n '🬢',\n '🬣',\n '🬤',\n '🬥',\n '🬦',\n '🬧',\n '🬨',\n '🬩',\n '🬪',\n '🬫',\n '🬬',\n '🬭',\n '🬮',\n '🬯',\n '🬰',\n '🬱',\n '🬲',\n '🬳',\n '🬴',\n '🬵',\n '🬶',\n '🬷',\n '🬸',\n '🬹',\n '🬺',\n '🬻',\n '🬼',\n '🬽',\n '🬾',\n '🬿',\n '🭀',\n '🭁',\n '🭂',\n '🭃',\n '🭄',\n '🭅',\n '🭆',\n '🭇',\n '🭈',\n '🭉',\n '🭊',\n '🭋',\n '🭌',\n '🭍',\n '🭎',\n '🭏',\n '🭐',\n '🭑',\n '🭒',\n '🭓',\n '🭔',\n '🭕',\n '🭖',\n '🭗',\n '🭘',\n '🭙',\n '🭚',\n '🭛',\n '🭜',\n '🭝',\n '🭞',\n '🭟',\n '🭠',\n '🭡',\n '🭢',\n '🭣',\n '🭤',\n '🭥',\n '🭦',\n '🭧',\n '🭨',\n '🭩',\n '🭪',\n '🭫',\n '🭬',\n '🭭',\n '🭮',\n '🭯',\n '🭰',\n '🭱',\n '🭲',\n '🭳',\n '🭴',\n '🭵',\n '🭶',\n '🭷',\n '🭸',\n '🭹',\n '🭺',\n '🭻',\n '🭼',\n '🭽',\n '🭾',\n '🭿',\n '🮀',\n '🮁',\n '🮂',\n '🮃',\n '🮄',\n '🮅',\n '🮆',\n '🮇',\n '🮈',\n '🮉',\n '🮊',\n '🮋',\n '🮌',\n '🮍',\n '🮎',\n '🮏',\n '🮐',\n '🮑',\n '🮒',\n '\\U0001fb93',\n '🮔',\n '🮕',\n '🮖',\n '🮗',\n '🮘',\n '🮙',\n '🮚',\n '🮛',\n '🮜',\n '🮝',\n '🮞',\n '🮟',\n '🮠',\n '🮡',\n '🮢',\n '🮣',\n '🮤',\n '🮥',\n '🮦',\n '🮧',\n '🮨',\n '🮩',\n '🮪',\n '🮫',\n '🮬',\n '🮭',\n '🮮',\n '\\U0001fbe6', '\\U0001fbe7',\n }  # }}}\nfor ch in range(0x1cd00, 0x1cde5+1):  # octants\n    box_chars.add(chr(ch))\n"
  },
  {
    "path": "kitty_tests/glfw.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport sys\nimport unittest\n\nfrom . import BaseTest\n\n_plat = sys.platform.lower()\nis_macos = 'darwin' in _plat\n\n\nclass TestGLFW(BaseTest):\n\n    def test_os_window_size_calculation(self):\n        from kitty.utils import get_new_os_window_size\n\n        def t(w, h, width=0, height=0, unit='cells', incremental=False):\n            self.ae((w, h), get_new_os_window_size(metrics, width, height, unit, incremental, has_window_scaling))\n\n        with self.subTest(has_window_scaling=False):\n            has_window_scaling = False\n            metrics = {\n                'width': 200, 'height': 100,\n                'framebuffer_width': 200, 'framebuffer_height': 100,\n                'xscale': 2.0, 'yscale': 2.0, 'xdpi': 192.0, 'ydpi': 192.0,\n                'cell_width': 8, 'cell_height': 16\n            }\n            t(80 * metrics['cell_width'], 100, 80)\n            t(80 * metrics['cell_width'] + metrics['width'], 100, 80, incremental=True)\n            t(1217, 100, 1217, unit='pixels')\n            t(1217 + metrics['width'], 100, 1217, unit='pixels', incremental=True)\n\n        with self.subTest(has_window_scaling=True):\n            has_window_scaling = True\n            metrics['framebuffer_width'] = metrics['width'] * 2\n            metrics['framebuffer_height'] = metrics['height'] * 2\n            t(80 * metrics['cell_width'] / metrics['xscale'], 100, 80)\n            t(80 * metrics['cell_width'] / metrics['xscale'] + metrics['width'], 100, 80, incremental=True)\n            t(1217, 100, 1217, unit='pixels')\n            t(1217 + metrics['width'], 100, 1217, unit='pixels', incremental=True)\n\n    @unittest.skipIf(is_macos, 'Skipping test on macOS because glfw-cocoa.so is not built with backend_utils')\n    def test_utf_8_strndup(self):\n        import ctypes\n\n        from kitty.constants import glfw_path\n\n        backend_utils = glfw_path('x11')\n        lib = ctypes.CDLL(backend_utils)\n        libc = ctypes.CDLL(None)\n\n        class allocated_c_char_p(ctypes.c_char_p):\n            def __del__(self):\n                libc.free(self)\n\n        utf_8_strndup = lib.utf_8_strndup\n        utf_8_strndup.restype = allocated_c_char_p\n        utf_8_strndup.argtypes = (ctypes.c_char_p, ctypes.c_size_t)\n\n        def test(string):\n            string_bytes = bytes(string, 'utf-8')\n            prev_part_bytes = b''\n            prev_length_bytes = -1\n            for length in range(len(string) + 1):\n                part = string[:length]\n                part_bytes = bytes(part, 'utf-8')\n                length_bytes = len(part_bytes)\n                for length_bytes_2 in range(prev_length_bytes + 1, length_bytes):\n                    self.ae(utf_8_strndup(string_bytes, length_bytes_2).value, prev_part_bytes)\n                self.ae(utf_8_strndup(string_bytes, length_bytes).value, part_bytes)\n                prev_part_bytes = part_bytes\n                prev_length_bytes = length_bytes\n            self.ae(utf_8_strndup(string_bytes, len(string_bytes) + 1).value, string_bytes)  # Try to go one character after the end of the string\n\n        self.ae(utf_8_strndup(None, 2).value, None)\n        self.ae(utf_8_strndup(b'', 2).value, b'')\n\n        test('ö')\n        test('>a<')\n        test('>ä<')\n        test('>ế<')\n        test('>𐍈<')\n        test('∮ E⋅da = Q,  n → ∞, 𐍈∑ f(i) = ∏ g(i)')\n        self.ae(utf_8_strndup(b'\\xf0\\x9f\\x98\\xb8', 3).value, b'')\n        self.ae(utf_8_strndup(b'\\xc3\\xb6\\xf0\\x9f\\x98\\xb8', 4).value, b'\\xc3\\xb6')\n"
  },
  {
    "path": "kitty_tests/gr.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport sys\nimport zlib\nfrom base64 import standard_b64encode\nfrom contextlib import suppress\n\nwrite = getattr(sys.stdout, 'buffer', sys.stdout).write\n\n\ndef clear_screen():\n    write(b'\\033[2J')\n\n\ndef move_cursor(x, y):\n    write(f'\\033[{y};{x}H'.encode('ascii'))\n\n\ndef write_gr_cmd(cmd, payload):\n    cmd = ','.join(f'{k}={v}' for k, v in cmd.items())\n    w = write\n    w(b'\\033_G'), w(cmd.encode('ascii')), w(b';'), w(payload), w(b'\\033\\\\')\n    sys.stdout.flush()\n\n\ndef display(data, width, height, x, y, z, ncols=0, nrows=0):\n    move_cursor(x, y)\n    cmd = {'a': 'T', 's': width, 'v': height, 'c': ncols, 'r': nrows, 'S': len(data), 'z': z}\n    data = zlib.compress(data)\n    cmd['o'] = 'z'\n    data = standard_b64encode(data)\n    while data:\n        chunk, data = data[:4096], data[4096:]\n        m = 1 if data else 0\n        cmd['m'] = m\n        write_gr_cmd(cmd, chunk)\n        cmd.clear()\n\n\ndef display_png_file(path):\n    cmd = {'a': 'T', 't': 'f', 'f': '100'}\n    path = os.path.abspath(path)\n    if not isinstance(path, bytes):\n        path = path.encode(sys.getfilesystemencoding() or 'utf-8')\n    data = standard_b64encode(path)\n    write_gr_cmd(cmd, data)\n\n\ndef main():\n    from kitty.constants import logo_png_file\n    photo = sys.argv[-1]\n    if not photo.lower().endswith('.png'):\n        raise SystemExit('Must specify a PNG file to display')\n    clear_screen()\n    display(b'\\xdd\\xdd\\xdd\\xff', 1, 1, 0, 0, -10, 40, 20)\n    with open(logo_png_file.replace('.png', '.rgba'), 'rb') as f:\n        display(f.read(), 256, 256, 0, 5, -9)\n    display(b'\\0\\0\\0\\xaa', 1, 1, 0, 7, -8, 40, 3)\n    move_cursor(5, 8)\n    print('kitty is \\033[3m\\033[32mawesome\\033[m!')\n    move_cursor(0, 21)\n    print('Photo...')\n    display_png_file(photo)\n    with suppress(EOFError, KeyboardInterrupt):\n        input()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "kitty_tests/graphics.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport random\nimport tempfile\nimport time\nimport unittest\nimport zlib\nfrom contextlib import suppress\nfrom dataclasses import dataclass\nfrom io import BytesIO\n\nfrom kitty.fast_data_types import base64_decode, base64_encode, has_avx2, has_sse4_2, load_png_data, shm_unlink, shm_write, test_xor64\n\nfrom . import BaseTest, parse_bytes\n\ntry:\n    from PIL import Image\nexcept ImportError:\n    Image = None\n\n\ndef send_command(screen, cmd, payload=b''):\n    cmd = '\\033_G' + cmd\n    if payload:\n        if isinstance(payload, str):\n            payload = payload.encode('utf-8')\n        payload = base64_encode(payload).decode('ascii')\n        cmd += ';' + payload\n    cmd += '\\033\\\\'\n    c = screen.callbacks\n    c.clear()\n    parse_bytes(screen, cmd.encode('ascii'))\n    return c.wtcbuf\n\n\ndef parse_response(res):\n    if not res:\n        return\n    return res.decode('ascii').partition(';')[2].partition('\\033')[0]\n\n\ndef parse_response_with_ids(res):\n    if not res:\n        return\n    a, b = res.decode('ascii').split(';', 1)\n    code = b.partition('\\033')[0].split(':', 1)[0]\n    a = a.split('G', 1)[1]\n    return code, a\n\n\n@dataclass(frozen=True)\nclass Response:\n    code: str = 'OK'\n    msg: str = ''\n    image_id: int = 0\n    image_number: int = 0\n    frame_number: int = 0\n\n\ndef parse_full_response(res):\n    if not res:\n        return\n    a, b = res.decode('ascii').split(';', 1)\n    code = b.partition('\\033')[0].split(':', 1)\n    if len(code) == 1:\n        code = code[0]\n        msg = ''\n    else:\n        code, msg = code\n    a = a.split('G', 1)[1]\n    ans = {'code': code, 'msg': msg}\n    for x in a.split(','):\n        k, _, v = x.partition('=')\n        ans[{'i': 'image_id', 'I': 'image_number', 'r': 'frame_number'}[k]] = int(v)\n    return Response(**ans)\n\n\nall_bytes = bytes(bytearray(range(256)))\n\n\ndef byte_block(sz):\n    d, m = divmod(sz, len(all_bytes))\n    return (all_bytes * d) + all_bytes[:m]\n\n\ndef load_helpers(self):\n    s = self.create_screen()\n    g = s.grman\n\n    def pl(payload, **kw):\n        kw.setdefault('i', 1)\n        cmd = ','.join(f'{k}={v}' for k, v in kw.items())\n        res = send_command(s, cmd, payload)\n        return parse_response(res)\n\n    def sl(payload, **kw):\n        if isinstance(payload, str):\n            payload = payload.encode('utf-8')\n        data = kw.pop('expecting_data', payload)\n        cid = kw.setdefault('i', 1)\n        self.ae('OK', pl(payload, **kw))\n        img = g.image_for_client_id(cid)\n        self.assertIsNotNone(img, f'No image with id {cid} found')\n        self.ae(img['client_id'], cid)\n        self.ae(img['data'], data)\n        if 's' in kw:\n            self.ae((kw['s'], kw['v']), (img['width'], img['height']))\n        self.ae(img['is_4byte_aligned'], kw.get('f') != 24)\n        return img\n\n    return s, g, pl, sl\n\n\ndef put_helpers(self, cw, ch, cols=10, lines=5):\n    iid = 0\n\n    def create_screen():\n        s = self.create_screen(cols, lines, cell_width=cw, cell_height=ch)\n        return s, 2 / s.columns, 2 / s.lines\n\n    def put_cmd(\n        z=0, num_cols=0, num_lines=0, x_off=0, y_off=0, width=0, height=0, cell_x_off=0,\n        cell_y_off=0, placement_id=0, cursor_movement=0, unicode_placeholder=0, parent_id=0,\n        parent_placement_id=0, offset_from_parent_x=0, offset_from_parent_y=0,\n    ):\n        return (\n            f'z={z},c={num_cols},r={num_lines},x={x_off},y={y_off},w={width},h={height},'\n            f'X={cell_x_off},Y={cell_y_off},p={placement_id},C={cursor_movement},'\n            f'U={unicode_placeholder},P={parent_id},Q={parent_placement_id},'\n            f'H={offset_from_parent_x},V={offset_from_parent_y}'\n        )\n\n    def put_image(screen, w, h, **kw):\n        nonlocal iid\n        iid += 1\n        imgid = kw.pop('id', None) or iid\n        no_id = kw.pop('no_id', False)\n        a = kw.pop('a', 'T')\n        if no_id:\n            cmd = f'a={a},f=24,s=%d,v=%d,%s' % (w, h, put_cmd(**kw))\n        else:\n            cmd = f'a={a},f=24,i=%d,s=%d,v=%d,%s' % (imgid, w, h, put_cmd(**kw))\n        data = b'x' * w * h * 3\n        res = send_command(screen, cmd, data)\n        return imgid, parse_response(res)\n\n    def put_ref(screen, **kw):\n        imgid = kw.pop('id', None) or iid\n        cmd = 'a=p,i=%d,%s' % (imgid, put_cmd(**kw))\n        return imgid, parse_response_with_ids(send_command(screen, cmd))\n\n    def layers(screen, scrolled_by=0, xstart=-1, ystart=1):\n        return screen.grman.update_layers(scrolled_by, xstart, ystart, dx, dy, screen.columns, screen.lines, cw, ch)\n\n    def rect_eq(r, left, top, right, bottom):\n        for side in 'left top right bottom'.split():\n            a, b = r[side], locals()[side]\n            if abs(a - b) > 0.0001:\n                self.ae(a, b, 'the %s side is not equal' % side)\n\n    s, dx, dy = create_screen()\n    return s, dx, dy, put_image, put_ref, layers, rect_eq\n\n\ndef make_send_command(screen):\n    def li(payload='abcdefghijkl'*3, s=4, v=3, f=24, a='f', i=1, **kw):\n        if s:\n            kw['s'] = s\n        if v:\n            kw['v'] = v\n        if f:\n            kw['f'] = f\n        if i:\n            kw['i'] = i\n        kw['a'] = a\n        cmd = ','.join(f'{k}={v}' for k, v in kw.items())\n        res = send_command(screen, cmd, payload)\n        return parse_full_response(res)\n    return li\n\n\nclass TestGraphics(BaseTest):\n\n    def test_xor_data(self):\n        base_data = b'\\x01' * 64\n        key = b'\\x02' * 64\n        sizes = []\n        if has_sse4_2:\n            sizes.append(2)\n        if has_avx2:\n            sizes.append(3)\n        sizes.append(0)\n\n        def t(key, data, align_offset=0):\n            expected = test_xor64(key, data, 1, 0)\n            for which_function in sizes:\n                actual = test_xor64(key, data, which_function, align_offset)\n                self.ae(expected, actual, f'{align_offset=} {len(data)=}')\n\n        t(key, b'')\n\n        for base in (b'abc', base_data):\n            for extra in range(len(base_data)):\n                for align_offset in range(64):\n                    data = base + base_data[:extra]\n                    t(key, data, align_offset)\n\n    def test_disk_cache(self):\n        s = self.create_screen()\n        dc = s.grman.disk_cache\n        dc.small_hole_threshold = 0\n        data = {}\n\n        def key_as_bytes(key):\n            if isinstance(key, int):\n                key = str(key)\n            if isinstance(key, str):\n                key = key.encode('utf-8')\n            return bytes(key)\n\n        def add(key, val):\n            bkey = key_as_bytes(key)\n            data[key] = key_as_bytes(val)\n            dc.add(bkey, data[key])\n\n        def remove(key):\n            bkey = key_as_bytes(key)\n            data.pop(key, None)\n            return dc.remove(bkey)\n\n        def check_data():\n            for key, val in data.items():\n                self.ae(dc.get(key_as_bytes(key)), val)\n\n        def reset(small_hole_threshold=0, defrag_factor=2):\n            nonlocal dc, data, s\n            s = self.create_screen()\n            dc = s.grman.disk_cache\n            dc.small_hole_threshold = small_hole_threshold\n            dc.defrag_factor = defrag_factor\n            data = {}\n\n        holes_to_create = 2, 4, 6, 8\n        for i in range(25):\n            self.assertIsNone(add(i, f'{i}' * i))\n            if i <= max(holes_to_create):\n                # We wait here to ensure data is written in order, otherwise the\n                # holes test below can fail\n                self.assertTrue(dc.wait_for_write())\n\n        self.assertEqual(dc.total_size, sum(map(len, data.values())))\n        self.assertTrue(dc.wait_for_write())\n        check_data()\n        sz = dc.end_of_data_offset()\n        self.assertEqual(sz, sum(map(len, data.values())))\n        self.assertFalse(dc.holes())\n        holes = set()\n        for x in holes_to_create:\n            remove(x)\n            holes.add(x)\n            check_data()\n            self.assertRaises(KeyError, dc.get, key_as_bytes(x))\n            self.assertEqual(sz, dc.end_of_data_offset())\n            self.assertEqual(holes, {x[1] for x in dc.holes()})\n        self.assertEqual(sz, dc.end_of_data_offset())\n        # fill holes largest first to ensure small one doesn't go into large accidentally causing fragmentation\n        for i, x in enumerate(sorted(holes, reverse=True)):\n            x = 'ABCDEFGH'[i] * x\n            add(x, x)\n            self.assertTrue(dc.wait_for_write())\n            check_data()\n            holes.discard(len(x))\n            self.assertEqual(holes, {x[1] for x in dc.holes()})\n            self.assertEqual(sz, dc.end_of_data_offset(), f'Disk cache has unexpectedly grown from {sz} to {dc.end_of_data_offset()} with data: {x!r}')\n        check_data()\n        dc.clear()\n        self.assertEqual(dc.end_of_data_offset(), 0)\n\n        data.clear()\n        for i in range(25):\n            self.assertIsNone(add(i, f'{i}' * i))\n        dc.wait_for_write()\n        check_data()\n\n        before = dc.end_of_data_offset()\n        while dc.total_size > before // 3:\n            key = random.choice(tuple(data))\n            self.assertTrue(remove(key))\n        check_data()\n        add('trigger defrag', 'XXX')\n        dc.wait_for_write()\n        self.assertLess(dc.end_of_data_offset(), before)\n        check_data()\n        dc.clear()\n\n        st = time.monotonic()\n        while dc.end_of_data_offset() and time.monotonic() - st < 20:\n            time.sleep(0.01)\n        self.assertEqual(dc.end_of_data_offset(), 0)\n        for frame in range(32):\n            add(f'1:{frame}', f'{frame:02d}' * 8)\n        dc.wait_for_write()\n        self.assertEqual(dc.end_of_data_offset(), 32 * 16)\n        self.assertEqual(dc.num_cached_in_ram(), 0)\n        num_in_ram = 0\n        for frame in range(32):\n            dc.get(key_as_bytes(f'1:{frame}'))\n        self.assertEqual(dc.num_cached_in_ram(), num_in_ram)\n        for frame in range(32):\n            dc.get(key_as_bytes(f'1:{frame}'), True)\n            num_in_ram += 1\n        self.assertEqual(dc.num_cached_in_ram(), num_in_ram)\n\n        def clear_predicate(key):\n            return key.startswith(b'1:')\n\n        dc.remove_from_ram(clear_predicate)\n        self.assertEqual(dc.num_cached_in_ram(), 0)\n\n        reset(small_hole_threshold=512, defrag_factor=20)\n        self.assertIsNone(add(1, '1' * 1024))\n        self.assertIsNone(add(2, '2' * 1024))\n        dc.wait_for_write()\n        sz = dc.end_of_data_offset()\n        remove(1)\n        self.ae(sz, dc.end_of_data_offset())\n        self.ae({x[1] for x in dc.holes()}, {1024})\n        self.assertIsNone(add(3, '3' * 800))\n        dc.wait_for_write()\n        self.assertFalse(dc.holes())\n        self.ae(sz, dc.end_of_data_offset())\n        self.assertIsNone(add(4, '4' * 100))\n        sz += 100\n        dc.wait_for_write()\n        self.ae(sz, dc.end_of_data_offset())\n        check_data()\n        self.assertFalse(dc.holes())\n        remove(4)\n        self.assertFalse(dc.holes())\n        self.assertIsNone(add(5, '5' * 10))\n        sz += 10\n        dc.wait_for_write()\n        self.ae(sz, dc.end_of_data_offset())\n\n        # test hole coalescing\n        reset(defrag_factor=20)\n        for i in range(1, 6):\n            self.assertIsNone(add(i, str(i)*i))\n            dc.wait_for_write()\n        remove(2)\n        remove(4)\n        self.assertEqual(dc.holes(), {(1, 2), (6, 4)})\n        remove(3)\n        self.assertEqual(dc.holes(), {(1, 9)})\n\n    def test_suppressing_gr_command_responses(self):\n        s, g, pl, sl = load_helpers(self)\n        self.ae(pl('abcd', s=10, v=10, q=1), 'ENODATA:Insufficient image data: 4 < 400')\n        self.ae(pl('abcd', s=10, v=10, q=2), None)\n        self.assertIsNone(pl('abcd', s=1, v=1, a='q', q=1))\n        # Test chunked load\n        self.assertIsNone(pl('abcd', s=2, v=2, m=1, q=1))\n        self.assertIsNone(pl('efgh', m=1))\n        self.assertIsNone(pl('ijkl', m=1))\n        self.assertIsNone(pl('mnop', m=0))\n\n        # errors\n        self.assertIsNone(pl('abcd', s=2, v=2, m=1, q=1))\n        self.ae(pl('mnop', m=0), 'ENODATA:Insufficient image data: 8 < 16')\n        self.assertIsNone(pl('abcd', s=2, v=2, m=1, q=2))\n        self.assertIsNone(pl('mnop', m=0))\n\n        # frames\n        s = self.create_screen()\n        li = make_send_command(s)\n        self.assertEqual(li().code, 'ENOENT')\n        self.assertIsNone(li(q=2))\n        self.assertIsNone(li(a='t', q=1))\n        self.assertIsNone(li(payload='2' * 12, z=77, m=1, q=1))\n        self.assertIsNone(li(payload='2' * 12, m=1))\n        self.assertIsNone(li(payload='2' * 12))\n        self.assertIsNone(li(payload='2' * 12, z=77, m=1, q=1))\n        self.ae(li(payload='2' * 12).code, 'ENODATA')\n        self.assertIsNone(li(payload='2' * 12, z=77, m=1, q=2))\n        self.assertIsNone(li(payload='2' * 12))\n\n    def test_load_images(self):\n        s, g, pl, sl = load_helpers(self)\n        self.assertEqual(g.disk_cache.total_size, 0)\n\n        # Test load query\n        self.ae(pl('abcd', s=1, v=1, a='q'), 'OK')\n        self.ae(g.image_count, 0)\n\n        # Test simple load\n        for f in 32, 24:\n            p = 'abc' + ('d' if f == 32 else '')\n            img = sl(p, s=1, v=1, f=f)\n            self.ae(bool(img['is_4byte_aligned']), f == 32)\n\n        # Test chunked load\n        self.assertIsNone(pl('abcd', s=2, v=2, m=1))\n        self.assertIsNone(pl('efgh', m=1))\n        self.assertIsNone(pl('ijkl', m=1))\n        self.ae(pl('mnop', m=0), 'OK')\n        img = g.image_for_client_id(1)\n        self.ae(img['data'], b'abcdefghijklmnop')\n\n        # Test interrupted and retried chunked load\n        self.assertIsNone(pl('abcd', s=2, v=2, m=1))\n        self.assertIsNone(pl('efgh', m=1))\n        send_command(s, 'a=d')  # delete command should clear partial transfer\n        self.assertIsNone(pl('abcd', s=2, v=2, m=1))\n        self.assertIsNone(pl('efgh', m=1))\n        self.assertIsNone(pl('ijkl', m=1))\n        self.ae(pl('1234', m=0), 'OK')\n        img = g.image_for_client_id(1)\n        self.ae(img['data'], b'abcdefghijkl1234')\n\n        random_data = byte_block(32 * 1024)\n        sl(\n            random_data,\n            s=1024,\n            v=8,\n            expecting_data=random_data\n        )\n\n        # Test compression\n        compressed_random_data = zlib.compress(random_data)\n        sl(\n            compressed_random_data,\n            s=1024,\n            v=8,\n            o='z',\n            expecting_data=random_data\n        )\n\n        # Test chunked + compressed\n        b = len(compressed_random_data) // 2\n        self.assertIsNone(pl(compressed_random_data[:b], s=1024, v=8, o='z', m=1))\n        self.ae(pl(compressed_random_data[b:], m=0), 'OK')\n        img = g.image_for_client_id(1)\n        self.ae(img['data'], random_data)\n\n        # Test loading from file\n        def load_temp(prefix='tty-graphics-protocol-'):\n            f = tempfile.NamedTemporaryFile(prefix=prefix)\n            f.write(random_data), f.flush()\n            sl(f.name, s=1024, v=8, t='f', expecting_data=random_data)\n            self.assertTrue(os.path.exists(f.name))\n            f.seek(0), f.truncate(), f.write(compressed_random_data), f.flush()\n            sl(f.name, s=1024, v=8, t='t', o='z', expecting_data=random_data)\n            return f\n\n        f = load_temp()\n        self.assertFalse(os.path.exists(f.name), f'Temp file at {f.name} was not deleted')\n        with suppress(FileNotFoundError):\n            f.close()\n        f = load_temp('')\n        self.assertTrue(os.path.exists(f.name), f'Temp file at {f.name} was deleted')\n        f.close()\n\n        # Test loading from POSIX SHM\n        name = '/kitty-test-shm'\n        shm_write(name, random_data)\n        sl(name, s=1024, v=8, t='s', expecting_data=random_data)\n        self.assertRaises(\n            FileNotFoundError, shm_unlink, name\n        )  # check that file was deleted\n        s.reset()\n        self.assertEqual(g.disk_cache.total_size, 0)\n\n    @unittest.skipIf(Image is None, 'PIL not available, skipping PNG tests')\n    def test_load_png(self):\n        s, g, pl, sl = load_helpers(self)\n        w, h = 5, 3\n        rgba_data = byte_block(w * h * 4)\n        img = Image.frombytes('RGBA', (w, h), rgba_data)\n        rgb_data = img.convert('RGB').convert('RGBA').tobytes()\n        self.assertEqual(g.disk_cache.total_size, 0)\n\n        def png(mode='RGBA'):\n            buf = BytesIO()\n            i = img\n            if mode != i.mode:\n                i = img.convert(mode)\n            i.save(buf, 'PNG')\n            return buf.getvalue()\n\n        for mode in 'RGBA RGB'.split():\n            data = png(mode)\n            sl(data, f=100, expecting_data=rgb_data if mode == 'RGB' else rgba_data)\n\n        for m in 'LP':\n            img = img.convert(m)\n            rgba_data = img.convert('RGBA').tobytes()\n            data = png(m)\n        sl(data, f=100, expecting_data=rgba_data)\n\n        self.ae(pl(b'a' * 20, f=100, S=20).partition(':')[0], 'EBADPNG')\n        s.reset()\n        self.assertEqual(g.disk_cache.total_size, 0)\n\n    def test_load_png_simple(self):\n        # 1x1 transparent PNG\n        png_data = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==')\n        expected = b'\\x00\\xff\\xff\\x7f'\n        self.ae(load_png_data(png_data), (expected, 1, 1))\n        s, g, pl, sl = load_helpers(self)\n        sl(png_data, f=100, expecting_data=expected)\n        # test error handling for loading bad png data\n        self.assertRaisesRegex(ValueError, '[EBADPNG]', load_png_data, b'dsfsdfsfsfd')\n\n    def test_gr_operations_with_numbers(self):\n        s = self.create_screen()\n        g = s.grman\n        self.assertEqual(g.disk_cache.total_size, 0)\n\n        def li(payload, **kw):\n            cmd = ','.join(f'{k}={v}' for k, v in kw.items())\n            res = send_command(s, cmd, payload)\n            return parse_response_with_ids(res)\n\n        code, ids = li('abc', s=1, v=1, f=24, I=1, i=3)\n        self.ae(code, 'EINVAL')\n\n        code, ids = li('abc', s=1, v=1, f=24, I=1)\n        self.ae((code, ids), ('OK', 'i=1,I=1'))\n        img = g.image_for_client_number(1)\n        self.ae(img['client_number'], 1)\n        self.ae(img['client_id'], 1)\n        code, ids = li('abc', s=1, v=1, f=24, I=1)\n        self.ae((code, ids), ('OK', 'i=2,I=1'))\n        img = g.image_for_client_number(1)\n        self.ae(img['client_number'], 1)\n        self.ae(img['client_id'], 2)\n        code, ids = li('abc', s=1, v=1, f=24, I=1)\n        self.ae((code, ids), ('OK', 'i=3,I=1'))\n        code, ids = li('abc', s=1, v=1, f=24, i=5)\n        self.ae((code, ids), ('OK', 'i=5'))\n        code, ids = li('abc', s=1, v=1, f=24, I=3)\n        self.ae((code, ids), ('OK', 'i=4,I=3'))\n\n        # Test chunked load with number\n        self.assertIsNone(li('abcd', s=2, v=2, m=1, I=93))\n        self.assertIsNone(li('efgh', m=1))\n        self.assertIsNone(li('ijkx', m=1))\n        self.ae(li('mnop', m=0), ('OK', 'i=6,I=93'))\n        img = g.image_for_client_number(93)\n        self.ae(img['data'], b'abcdefghijkxmnop')\n        self.ae(img['client_id'], 6)\n\n        # test put with number\n        def put(**kw):\n            cmd = ','.join(f'{k}={v}' for k, v in kw.items())\n            cmd = 'a=p,' + cmd\n            return parse_response_with_ids(send_command(s, cmd))\n\n        code, idstr = put(c=2, r=2, I=93)\n        self.ae((code, idstr), ('OK', 'i=6,I=93'))\n        code, idstr = put(c=2, r=2, I=94)\n        self.ae(code, 'ENOENT')\n\n        # test delete with number\n        def delete(ac='N', **kw):\n            cmd = 'a=d'\n            if ac:\n                cmd += f',d={ac}'\n            if kw:\n                cmd += ',' + ','.join(f'{k}={v}' for k, v in kw.items())\n            send_command(s, cmd)\n\n        count = s.grman.image_count\n        put(i=1), put(i=2), put(i=3), put(i=4), put(i=5)\n        delete(I=94)\n        self.ae(s.grman.image_count, count)\n        delete(I=93)\n        self.ae(s.grman.image_count, count - 1)\n        delete(I=1)\n        self.ae(s.grman.image_count, count - 2)\n        cn = 1117\n        li('abc', s=1, v=1, f=24, I=cn)\n        first_id = g.image_for_client_number(cn)['internal_id']\n        li('abc', s=1, v=1, f=24, I=cn)\n        second_id = g.image_for_client_number(cn)['internal_id']\n        self.assertNotEqual(first_id, second_id)\n        count = s.grman.image_count\n        delete(I=cn)\n        self.ae(g.image_for_client_number(cn)['internal_id'], first_id)\n        self.ae(s.grman.image_count, count - 1)\n        s.reset()\n        self.assertEqual(g.disk_cache.total_size, 0)\n\n    def test_image_put(self):\n        cw, ch = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n        self.ae(put_image(s, cw, ch)[1], 'OK')\n        l0 = layers(s)\n        self.ae(len(l0), 1)\n        rect_eq(l0[0]['src_rect'], 0, 0, 1, 1)\n        rect_eq(l0[0]['dest_rect'], -1, 1, -1 + dx, 1 - dy)\n        self.ae(l0[0]['group_count'], 1)\n        self.ae(s.cursor.x, 1), self.ae(s.cursor.y, 0)\n        src_width, src_height = 3, 5\n        iid, (code, idstr) = put_ref(s, num_cols=s.columns, num_lines=1, x_off=2, y_off=1, width=src_width, height=src_height,\n                                     cell_x_off=3, cell_y_off=1, z=-1, placement_id=17)\n        self.ae(idstr, f'i={iid},p=17')\n        l2 = layers(s)\n        self.ae(len(l2), 2)\n        self.ae(l2[1], l0[0])\n        rect_eq(l2[0]['src_rect'], 2 / 10, 1 / 20, (2 + 3) / 10, (1 + 5)/20)\n        self.ae(l2[0]['group_count'], 2)\n        left, top = -1 + dx + 3 * dx / cw, 1 - 1 * dy / ch\n        right = -1 + (1 + s.columns) * dx\n        bottom = 1 - dy\n        rect_eq(l2[0]['dest_rect'], left, top, right, bottom)\n        self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1)\n        self.ae(put_image(s, 10, 20, cursor_movement=1)[1], 'OK')\n        self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1)\n        s.reset()\n        self.assertEqual(s.grman.disk_cache.total_size, 0)\n        self.ae(put_image(s, 2*cw, 2*ch, num_cols=3)[1], 'OK')\n        self.ae((s.cursor.x, s.cursor.y), (3, 2))\n        rect_eq(layers(s)[0]['dest_rect'], -1, 1, -1 + 3 * dx, 1 - 3*dy)\n\n    def test_image_layer_grouping(self):\n        cw, ch = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n\n        def group_counts():\n            return tuple(x['group_count'] for x in layers(s))\n\n        self.ae(put_image(s, 10, 20, id=1)[1], 'OK')\n        self.ae(group_counts(), (1,))\n        put_ref(s, id=1, num_cols=2, num_lines=1, placement_id=2)\n        put_ref(s, id=1, num_cols=2, num_lines=1, placement_id=3, z=-2)\n        put_ref(s, id=1, num_cols=2, num_lines=1, placement_id=4, z=-2)\n        self.ae(group_counts(), (4, 3, 2, 1))\n        self.ae(put_image(s, 8, 16, id=2, z=-1)[1], 'OK')\n        self.ae(group_counts(), (2, 1, 1, 2, 1))\n\n    def test_image_parents(self):\n        cw, ch = 10, 20\n        iw, ih = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n\n        def positions():\n            ans = {}\n            def x(x):\n                return round(((x + 1)/2) * s.columns)\n            def y(y):\n                return int(((-y + 1)/2) * s.lines)\n\n            for i in layers(s):\n                d = i['dest_rect']\n                ans[(i['image_id'], i['ref_id'])] = {'x': x(d['left']), 'y': y(d['top'])}\n            return ans\n\n        def p(x, y=0):\n            return {'x':x, 'y': y}\n\n        self.ae(put_image(s, iw, ih, id=1)[1], 'OK')\n        self.ae(put_ref(s, id=1, placement_id=1), (1, ('OK', 'i=1,p=1')))\n        pos = {(1, 1): p(0), (1, 2): p(1)}\n        self.ae(positions(), pos)\n        # check that adding a reference to a non-existent parent fails\n        self.ae(put_ref(s, id=1, placement_id=33, parent_id=1, parent_placement_id=2), (1, ('ENOPARENT', 'i=1,p=33')))\n        self.ae(put_ref(s, id=1, placement_id=33, parent_id=33), (1, ('ENOPARENT', 'i=1,p=33')))\n        # check that we cannot add a reference that is its own parent\n        self.ae(put_ref(s, id=1, placement_id=1, parent_id=1, parent_placement_id=1), (1, ('EINVAL', 'i=1,p=1')))\n\n        self.ae(put_image(s, iw, ih, id=2)[1], 'OK')\n        pos[(2,1)] = p(2)\n        self.ae(positions(), pos)\n        # Add two children to the first placement of img2\n        before = s.cursor.x, s.cursor.y\n        self.ae(put_ref(s, id=1, placement_id=2, parent_id=2, offset_from_parent_y=3), (1, ('OK', 'i=1,p=2')))\n        self.ae(before, (s.cursor.x, s.cursor.y), 'Cursor must not move for child image')\n        pos[(1,3)] = p(2, 3)\n        self.ae(positions(), pos)\n        self.ae(put_ref(s, id=2, placement_id=3, parent_id=2, offset_from_parent_y=4), (2, ('OK', 'i=2,p=3')))\n        pos[(2,2)] = p(2, 4)\n        self.ae(positions(), pos)\n        # Add a grand child to the second child of img2\n        self.ae(put_ref(s, id=2, placement_id=4, parent_id=2, parent_placement_id=3, offset_from_parent_x=-1), (2, ('OK', 'i=2,p=4')))\n        pos[(2,3)] = p(pos[(2,2)]['x']-1, pos[(2,2)]['y'])\n        self.ae(positions(), pos)\n        # Check that creating a cycle is prevented\n        self.ae(put_ref(s, id=2, placement_id=3, parent_id=2, parent_placement_id=4), (2, ('ECYCLE', 'i=2,p=3')))\n        self.ae(positions(), pos)\n        # Check that depth is limited\n        for i in range(5, 12):\n            q = put_ref(s, id=2, placement_id=i, parent_id=2, parent_placement_id=i-1, offset_from_parent_x=-1)[1][0]\n            if q == 'ETOODEEP':\n                break\n            self.ae(q, 'OK')\n        else:\n            self.assertTrue(False, 'Failed to limit reference chain depth')\n        # Check that deleting a parent removes all descendants\n        send_command(s, 'a=d,d=i,i=2,p=3')\n        pos.pop((2,3)), pos.pop((2,2))\n        self.ae(positions(), pos)\n        # Check that deleting a parent deletes all descendants and also removes\n        # images with no remaining placements\n        self.ae(put_ref(s, id=2, placement_id=3, parent_id=2, offset_from_parent_y=4), (2, ('OK', 'i=2,p=3')))\n        pos[(2,11)] = p(2, 4)\n        self.ae(positions(), pos)\n        self.ae(put_image(s, iw, ih, id=3, placement_id=97, parent_id=2, parent_placement_id=3)[1], 'OK')\n        pos[(3,1)] = p(2, 4)\n        self.ae(positions(), pos)\n        send_command(s, 'a=d,d=i,i=2')\n        pos.pop((3,1)), pos.pop((2,11)), pos.pop((2,1)), pos.pop((1,3))\n        self.ae(positions(), pos)\n        # Check that virtual placements that try to be relative are rejected\n        self.ae(put_ref(s, id=1, placement_id=11, parent_id=1, unicode_placeholder=1), (1, ('EINVAL', 'i=1,p=11')))\n        # Check creation of children of a unicode placeholder based image\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n        put_image(s, 20, 20, num_cols=4, num_lines=2, unicode_placeholder=1, id=42)\n        s.update_only_line_graphics_data()\n        self.assertFalse(positions())  # the reference is virtual\n        self.ae(put_ref(s, id=42, placement_id=11, parent_id=42, offset_from_parent_y=2, offset_from_parent_x=1), (42, ('OK', 'i=42,p=11')))\n        self.assertFalse(positions())  # the reference is virtual without any cell images so the child is invisible\n        s.apply_sgr(\"38;5;42\")\n        # These two characters will become one 2x1 ref.\n        s.cursor.x = s.cursor.y = 1\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\U0010EEEE\\u0305\\u030D\")\n        s.cursor.x = s.cursor.y = 0\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\U0010EEEE\\u0305\\u030D\")\n        s.update_only_line_graphics_data()\n        pos = {(1, 2): p(1, 2), (1, 3): p(0), (1, 4): p(1)}\n        self.ae(positions(), pos)\n        s.cursor.x = s.cursor.y = 0\n        s.erase_in_display(0, False)\n        s.update_only_line_graphics_data()\n        self.assertFalse(positions())  # the reference is virtual without any cell images so the child is invisible\n        s.cursor.x = s.cursor.y = 2\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\U0010EEEE\\u0305\\u030D\")\n        s.update_only_line_graphics_data()\n        self.ae(positions(), {(1, 5): {'x': 2, 'y': 2}, (1, 2): {'x': 3, 'y': 4}})\n\n    def test_unicode_placeholders(self):\n        # This test tests basic image placement using using unicode placeholders\n        cw, ch = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n        # Upload two images.\n        put_image(s, 20, 20, num_cols=4, num_lines=2, unicode_placeholder=1, id=42)\n        put_image(s, 10, 20, num_cols=4, num_lines=2, unicode_placeholder=1, id=(42<<16) + (43<<8) + 44)\n        # The references are virtual, so no visible refs yet.\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 0)\n        # A reminder of row/column diacritics meaning (assuming 0-based):\n        # \\u0305 -> 0\n        # \\u030D -> 1\n        # \\u030E -> 2\n        # \\u0310 -> 3\n        # Now print the placeholders for the first image.\n        # Encode the id as an 8-bit color.\n        s.apply_sgr(\"38;5;42\")\n        # These two characters will become one 2x1 ref.\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\U0010EEEE\\u0305\\u030D\")\n        # These two characters will be two separate refs (not contiguous).\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\U0010EEEE\\u0305\\u030E\")\n        s.cursor_move(4)\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 3)\n        self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.5, 'bottom': 0.5})\n        self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.25, 'bottom': 0.5})\n        self.ae(refs[2]['src_rect'], {'left': 0.5, 'top': 0.0, 'right': 0.75, 'bottom': 0.5})\n        # Erase the line.\n        s.erase_in_line(2)\n        # There must be 0 refs after the line is erased.\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 0)\n        # Now test encoding IDs with the 24-bit color.\n        # The first image, 1x1\n        s.apply_sgr(\"38;2;0;0;42\")\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\")\n        # The second image, 2x1\n        s.apply_sgr(\"38;2;42;43;44\")\n        s.draw(\"\\U0010EEEE\\u0305\\u030D\\U0010EEEE\\u0305\\u030E\")\n        s.cursor_move(2)\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 2)\n        self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.25, 'bottom': 0.5})\n        # The second ref spans the whole widths of the second image because it's\n        # fit to height and centered in a 4x2 box (specified in put_image).\n        self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.5})\n        # Erase the line.\n        s.erase_in_line(2)\n        # Now test implicit column numbers.\n        # We will mix implicit and explicit column/row specifications, but they\n        # will be combine into just two references.\n        s.apply_sgr(\"38;5;42\")\n        # full row 0 of the first image\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\U0010EEEE\\u0305\\U0010EEEE\\U0010EEEE\\u0305\")\n        # full row 1 of the first image\n        s.draw(\"\\U0010EEEE\\u030D\\U0010EEEE\\U0010EEEE\\U0010EEEE\\u030D\\u0310\")\n        s.cursor_move(8)\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 2)\n        self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.5})\n        self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.5, 'right': 1.0, 'bottom': 1.0})\n        # Now reset the screen, the images should be erased.\n        s.reset()\n        refs = layers(s)\n        self.ae(len(refs), 0)\n\n    def test_unicode_placeholders_3rd_combining_char(self):\n        # This test tests that we can use the 3rd diacritic for the most\n        # significant byte\n        cw, ch = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n        # Upload two images.\n        put_image(s, 20, 20, num_cols=4, num_lines=2, unicode_placeholder=1, id=42)\n        put_image(s, 20, 10, num_cols=4, num_lines=1, unicode_placeholder=1, id=(42 << 24) + 43)\n        # This one will have id=43, which does not exist.\n        s.apply_sgr(\"38;2;0;0;43\")\n        s.draw(\"\\U0010EEEE\\u0305\\U0010EEEE\\U0010EEEE\\U0010EEEE\")\n        s.cursor_move(4)\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 0)\n        s.erase_in_line(2)\n        # This one will have id=42. We explicitly specify that the most\n        # significant byte is 0 (third \\u305). Specifying the zero byte like\n        # this is not necessary but is correct.\n        s.apply_sgr(\"38;2;0;0;42\")\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\u0305\\U0010EEEE\\u0305\\u030D\\u0305\")\n        # This is the second image.\n        # \\u059C -> 42\n        s.apply_sgr(\"38;2;0;0;43\")\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\u059C\\U0010EEEE\\u0305\\u030D\\u059C\")\n        # Check that we can continue by using implicit row/column specification.\n        s.draw(\"\\U0010EEEE\\u0305\\U0010EEEE\")\n        s.cursor_move(6)\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 2)\n        self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.5, 'bottom': 0.5})\n        self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})\n        s.erase_in_line(2)\n        # Now test the 8-bit color mode. Using the third diacritic, we can\n        # specify 16 bits: the most significant byte and the least significant\n        # byte.\n        s.apply_sgr(\"38;5;42\")\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\u0305\\U0010EEEE\")\n        s.apply_sgr(\"38;5;43\")\n        s.draw(\"\\U0010EEEE\\u0305\\u0305\\u059C\\U0010EEEE\\U0010EEEE\\u0305\\U0010EEEE\")\n        s.cursor_move(6)\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 2)\n        self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 0.5, 'bottom': 0.5})\n        self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})\n\n    def test_unicode_placeholders_multiple_placements(self):\n        # Here we test placement specification via underline color.\n        cw, ch = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n        put_image(s, 20, 20, num_cols=1, num_lines=1, placement_id=1, unicode_placeholder=1, id=42)\n        put_ref(s, id=42, num_cols=2, num_lines=1, placement_id=22, unicode_placeholder=1)\n        put_ref(s, id=42, num_cols=4, num_lines=2, placement_id=44, unicode_placeholder=1)\n        # The references are virtual, so no visible refs yet.\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 0)\n        # Draw the first row of each placement.\n        s.apply_sgr(\"38;5;42\")\n        s.apply_sgr(\"58;5;1\")\n        s.draw(\"\\U0010EEEE\\u0305\")\n        s.apply_sgr(\"58;5;22\")\n        s.draw(\"\\U0010EEEE\\u0305\\U0010EEEE\\u0305\")\n        s.apply_sgr(\"58;5;44\")\n        s.draw(\"\\U0010EEEE\\u0305\\U0010EEEE\\u0305\\U0010EEEE\\u0305\\U0010EEEE\\u0305\")\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        self.ae(len(refs), 3)\n        self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.5})\n        self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})\n        self.ae(refs[2]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.5})\n\n    def test_unicode_placeholders_scroll(self):\n        # Here we test scrolling of a region. We'll draw an image spanning 8\n        # rows and then scroll only the middle part of this image. Each\n        # reference corresponds to one row.\n        cw, ch = 5, 10\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch, lines=8)\n        put_image(s, 5, 80, num_cols=1, num_lines=8, unicode_placeholder=1, id=42)\n        s.apply_sgr(\"38;5;42\")\n        s.cursor_position(1, 0)\n        s.draw(\"\\U0010EEEE\\u0305\\n\")\n        s.cursor_position(2, 0)\n        s.draw(\"\\U0010EEEE\\u030D\\n\")\n        s.cursor_position(3, 0)\n        s.draw(\"\\U0010EEEE\\u030E\\n\")\n        s.cursor_position(4, 0)\n        s.draw(\"\\U0010EEEE\\u0310\\n\")\n        s.cursor_position(5, 0)\n        s.draw(\"\\U0010EEEE\\u0312\\n\")\n        s.cursor_position(6, 0)\n        s.draw(\"\\U0010EEEE\\u033D\\n\")\n        s.cursor_position(7, 0)\n        s.draw(\"\\U0010EEEE\\u033E\\n\")\n        s.cursor_position(8, 0)\n        s.draw(\"\\U0010EEEE\\u033F\")\n        # Each line will contain a part of the image.\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        refs = sorted(refs, key=lambda r: r['src_rect']['top'])\n        self.ae(len(refs), 8)\n        for i in range(8):\n            self.ae(refs[i]['src_rect'], {'left': 0.0, 'top': 0.125*i, 'right': 1.0, 'bottom': 0.125*(i + 1)})\n            self.ae(refs[i]['dest_rect']['top'], 1 - 0.25*i)\n        # Now set margins to lines 3 and 6.\n        s.set_margins(3, 6)  # 1-based indexing\n        # Scroll two lines down (i.e. move lines 3..6 up).\n        # Lines 3 and 4 will be erased.\n        s.cursor_position(6, 0)\n        s.index()\n        s.index()\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        refs = sorted(refs, key=lambda r: r['src_rect']['top'])\n        self.ae(len(refs), 6)\n        # Lines 1 and 2 are outside of the region, not scrolled.\n        self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.125})\n        self.ae(refs[0]['dest_rect']['top'], 1.0)\n        self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.125*1, 'right': 1.0, 'bottom': 0.125*2})\n        self.ae(refs[1]['dest_rect']['top'], 1.0 - 0.25*1)\n        # Lines 3 and 4 are erased.\n        # Lines 5 and 6 are now higher.\n        self.ae(refs[2]['src_rect'], {'left': 0.0, 'top': 0.125*4, 'right': 1.0, 'bottom': 0.125*5})\n        self.ae(refs[2]['dest_rect']['top'], 1.0 - 0.25*2)\n        self.ae(refs[3]['src_rect'], {'left': 0.0, 'top': 0.125*5, 'right': 1.0, 'bottom': 0.125*6})\n        self.ae(refs[3]['dest_rect']['top'], 1.0 - 0.25*3)\n        # Lines 7 and 8 are outside of the region.\n        self.ae(refs[4]['src_rect'], {'left': 0.0, 'top': 0.125*6, 'right': 1.0, 'bottom': 0.125*7})\n        self.ae(refs[4]['dest_rect']['top'], 1.0 - 0.25*6)\n        self.ae(refs[5]['src_rect'], {'left': 0.0, 'top': 0.125*7, 'right': 1.0, 'bottom': 0.125*8})\n        self.ae(refs[5]['dest_rect']['top'], 1.0 - 0.25*7)\n        # Now scroll three lines up (i.e. move lines 5..6 down).\n        # Line 6 will be erased.\n        s.cursor_position(3, 0)\n        s.reverse_index()\n        s.reverse_index()\n        s.reverse_index()\n        s.update_only_line_graphics_data()\n        refs = layers(s)\n        refs = sorted(refs, key=lambda r: r['src_rect']['top'])\n        self.ae(len(refs), 5)\n        # Lines 1 and 2 are outside of the region, not scrolled.\n        self.ae(refs[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.125})\n        self.ae(refs[0]['dest_rect']['top'], 1.0)\n        self.ae(refs[1]['src_rect'], {'left': 0.0, 'top': 0.125*1, 'right': 1.0, 'bottom': 0.125*2})\n        self.ae(refs[1]['dest_rect']['top'], 1.0 - 0.25*1)\n        # Lines 3, 4 and 6 are erased.\n        # Line 5 is now lower.\n        self.ae(refs[2]['src_rect'], {'left': 0.0, 'top': 0.125*4, 'right': 1.0, 'bottom': 0.125*5})\n        self.ae(refs[2]['dest_rect']['top'], 1.0 - 0.25*5)\n        # Lines 7 and 8 are outside of the region.\n        self.ae(refs[3]['src_rect'], {'left': 0.0, 'top': 0.125*6, 'right': 1.0, 'bottom': 0.125*7})\n        self.ae(refs[3]['dest_rect']['top'], 1.0 - 0.25*6)\n        self.ae(refs[4]['src_rect'], {'left': 0.0, 'top': 0.125*7, 'right': 1.0, 'bottom': 0.125*8})\n        self.ae(refs[4]['dest_rect']['top'], 1.0 - 0.25*7)\n\n    def test_gr_scroll(self):\n        cw, ch = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n        put_image(s, 10, 20, no_id=True)  # a one cell image at (0, 0)\n        self.ae(len(layers(s)), 1)\n        for i in range(s.lines):\n            s.index()\n        self.ae(len(layers(s)), 0), self.ae(s.grman.image_count, 1)\n        for i in range(s.historybuf.ynum - 1):\n            s.index()\n            self.ae(len(layers(s)), 0), self.ae(s.grman.image_count, 1)\n        s.index()\n        self.ae(s.grman.image_count, 0)\n\n        # Now test with margins\n        s.reset()\n        # Test images outside page area untouched\n        put_image(s, cw, ch)  # a one cell image at (0, 0)\n        for i in range(s.lines - 1):\n            s.index()\n        put_image(s, cw, ch)  # a one cell image at (0, bottom)\n        s.set_margins(2, 4)  # 1-based indexing\n        self.ae(s.grman.image_count, 2)\n        for i in range(s.lines + s.historybuf.ynum):\n            s.index()\n            self.ae(s.grman.image_count, 2)\n        for i in range(s.lines):  # ensure cursor is at top margin\n            s.reverse_index()\n        # Test clipped scrolling during index\n        put_image(s, cw, 2*ch, z=-1, no_id=True)  # 1x2 cell image\n        self.ae(s.grman.image_count, 3)\n        self.ae(layers(s)[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})\n        s.index(), s.index()\n        l0 = layers(s)\n        self.ae(len(l0), 3)\n        self.ae(layers(s)[0]['src_rect'],  {'left': 0.0, 'top': 0.5, 'right': 1.0, 'bottom': 1.0})\n        s.index()\n        self.ae(s.grman.image_count, 2)\n        # Test clipped scrolling during reverse_index\n        for i in range(s.lines):\n            s.reverse_index()\n        put_image(s, cw, 2*ch, z=-1, no_id=True)  # 1x2 cell image\n        self.ae(s.grman.image_count, 3)\n        self.ae(layers(s)[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})\n        while s.cursor.y != 1:\n            s.reverse_index()\n        s.reverse_index(), s.reverse_index()\n        self.ae(layers(s)[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.5})\n        s.reverse_index()\n        self.ae(s.grman.image_count, 2)\n        s.reset()\n        self.assertEqual(s.grman.disk_cache.total_size, 0)\n\n    def test_gr_reset(self):\n        cw, ch = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n        put_image(s, cw, ch)  # a one cell image at (0, 0)\n        self.ae(len(layers(s)), 1)\n        s.reset()\n        self.ae(s.grman.image_count, 0)\n        put_image(s, cw, ch)  # a one cell image at (0, 0)\n        self.ae(s.grman.image_count, 1)\n        for i in range(s.lines):\n            s.index()\n        s.reset()\n        self.ae(s.grman.image_count, 1)\n\n    def test_gr_delete(self):\n        cw, ch = 10, 20\n        s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)\n\n        def delete(ac=None, **kw):\n            cmd = 'a=d'\n            if ac:\n                cmd += f',d={ac}'\n            if kw:\n                cmd += ',' + ','.join(f'{k}={v}' for k, v in kw.items())\n            send_command(s, cmd)\n\n        iid = put_image(s, cw, ch, a='t')[0]\n        self.ae(s.grman.image_count, 1)\n        delete('I', i=iid)\n        self.ae(s.grman.image_count, 0)\n        iid1 = put_image(s, cw, ch, a='t')[0]\n        iid2 = put_image(s, cw, ch, a='t')[0]\n        self.ae(s.grman.image_count, 2)\n        delete('R', x=iid1, y=iid2)\n        self.ae(s.grman.image_count, 0)\n\n        put_image(s, cw, ch)\n        delete()\n        self.ae(s.grman.image_count, 1)\n        self.ae(len(layers(s)), 0)\n        delete('A')\n        self.ae(s.grman.image_count, 1)\n        s.reset()\n        self.ae(s.grman.image_count, 0)\n        put_image(s, cw, ch)\n        self.ae(s.grman.image_count, 1)\n        delete('A')\n        self.ae(s.grman.image_count, 0)\n        self.assertEqual(s.grman.disk_cache.total_size, 0)\n        iid = put_image(s, cw, ch)[0]\n        delete('I', i=iid, p=7)\n        self.ae(s.grman.image_count, 1)\n        delete('I', i=iid)\n        self.ae(s.grman.image_count, 0)\n        self.assertEqual(s.grman.disk_cache.total_size, 0)\n        iid = put_image(s, cw, ch, placement_id=9)[0]\n        delete('I', i=iid, p=9)\n        self.ae(s.grman.image_count, 0)\n        self.assertEqual(s.grman.disk_cache.total_size, 0)\n        s.reset()\n        put_image(s, cw, ch)\n        put_image(s, cw, ch)\n        delete('C')\n        self.ae(s.grman.image_count, 2)\n        s.cursor_position(1, 1)\n        delete('C')\n        self.ae(s.grman.image_count, 1)\n        delete('P', x=2, y=1)\n        self.ae(s.grman.image_count, 0)\n        self.assertEqual(s.grman.disk_cache.total_size, 0)\n        put_image(s, cw, ch, z=9)\n        delete('Z', z=9)\n        self.ae(s.grman.image_count, 0)\n        put_image(s, cw, ch, id=1)\n        put_image(s, cw, ch, id=2)\n        put_image(s, cw, ch, id=3)\n        delete('R', y=2)\n        self.ae(s.grman.image_count, 1)\n        delete('R', x=3, y=3)\n        self.ae(s.grman.image_count, 0)\n        self.assertEqual(s.grman.disk_cache.total_size, 0)\n\n        # test put + delete + put\n        iid = 999999\n        self.ae(put_image(s, cw, ch, id=iid), (iid, 'OK'))\n        self.ae(put_ref(s, id=iid), (iid, ('OK', f'i={iid}')))\n        delete('i', i=iid)\n        self.ae(s.grman.image_count, 1)\n        self.ae(put_ref(s, id=iid), (iid, ('OK', f'i={iid}')))\n        delete('I', i=iid)\n        self.ae(put_ref(s, id=iid), (iid, ('ENOENT', f'i={iid}')))\n        self.ae(s.grman.image_count, 0)\n        self.assertEqual(s.grman.disk_cache.total_size, 0)\n\n        # test delete but not free\n        s.reset()\n        iid = 9999999\n        self.ae(put_image(s, cw, ch, id=iid), (iid, 'OK'))\n        self.ae(put_ref(s, id=iid), (iid, ('OK', f'i={iid}')))\n        self.ae(put_image(s, cw, ch, id=iid+1), (iid+1, 'OK'))\n        self.ae(put_ref(s, id=iid+1), (iid+1, ('OK', f'i={iid+1}')))\n        delete('i', i=iid)\n        self.ae(s.grman.image_count, 2)\n        delete('I', i=iid+1)\n        self.ae(s.grman.image_count, 1)\n\n    def test_animation_frame_loading(self):\n        s = self.create_screen()\n        g = s.grman\n        li = make_send_command(s)\n\n        def t(code='OK', image_id=1, frame_number=2, **kw):\n            res = li(**kw)\n            if code is not None:\n                self.assertEqual(code, res.code, f'{code} != {res.code}: {res.msg}')\n            if image_id is not None:\n                self.assertEqual(image_id, res.image_id)\n            if frame_number is not None:\n                self.assertEqual(frame_number, res.frame_number)\n\n        # test error on send frame for non-existent image\n        self.assertEqual(li().code, 'ENOENT')\n\n        # create image\n        self.assertEqual(li(a='t').code, 'OK')\n        self.assertEqual(g.disk_cache.total_size, 36)\n\n        # simple new frame (width=4, height=3)\n        self.assertIsNone(li(payload='2' * 12, z=77, m=1))\n        self.assertIsNone(li(payload='2' * 12, z=77, m=1))\n        t(payload='2' * 12, z=77)\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], ({'gap': 77, 'id': 2, 'data': b'2' * 36},))\n        # test editing a frame\n        t(payload='3' * 36, r=2)\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], ({'gap': 77, 'id': 2, 'data': b'3' * 36},))\n        # test editing part of a frame\n        t(payload='4' * 12, r=2, s=2, v=2)\n        img = g.image_for_client_id(1)\n\n        def expand(*rows):\n            ans = []\n            for r in rows:\n                ans.append(''.join(x * 3 for x in str(r)))\n            return ''.join(ans).encode('ascii')\n\n        self.assertEqual(img['extra_frames'], ({'gap': 77, 'id': 2, 'data': expand(4433, 4433, 3333)},))\n        t(payload='5' * 12, r=2, s=2, v=2, x=1, y=1)\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], ({'gap': 77, 'id': 2, 'data': expand(4433, 4553, 3553)},))\n        t(payload='3' * 36, r=2)\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], ({'gap': 77, 'id': 2, 'data': b'3' * 36},))\n        # test loading from previous frame\n        t(payload='4' * 12, c=2, s=2, v=2, z=101, frame_number=3)\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], (\n            {'gap': 77, 'id': 2, 'data': b'3' * 36},\n            {'gap': 101, 'id': 3, 'data': b'444444333333444444333333333333333333'},\n        ))\n        # test changing gaps\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['root_frame_gap'], 0)\n        self.assertIsNone(li(a='a', i=1, r=1, z=13))\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['root_frame_gap'], 13)\n        self.assertIsNone(li(a='a', i=1, r=2, z=43))\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'][0]['gap'], 43)\n        # test changing current frame\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['current_frame_index'], 0)\n        self.assertIsNone(li(a='a', i=1, c=2))\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['current_frame_index'], 1)\n\n        # test delete of frames\n        t(payload='5' * 36, frame_number=4)\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], (\n            {'gap': 43, 'id': 2, 'data': b'3' * 36},\n            {'gap': 101, 'id': 3, 'data': b'444444333333444444333333333333333333'},\n            {'gap': 40, 'id': 4, 'data': b'5' * 36},\n        ))\n        self.assertEqual(img['current_frame_index'], 1)\n        self.assertIsNone(li(a='d', d='f', i=1, r=1))\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['current_frame_index'], 0)\n        self.assertEqual(img['data'], b'3' * 36)\n        self.assertEqual(img['extra_frames'], (\n            {'gap': 101, 'id': 3, 'data': b'444444333333444444333333333333333333'},\n            {'gap': 40, 'id': 4, 'data': b'5' * 36},\n        ))\n        self.assertIsNone(li(a='a', i=1, c=3))\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['current_frame_index'], 2)\n        self.assertIsNone(li(a='d', d='f', i=1, r=2))\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['current_frame_index'], 1)\n        self.assertEqual(img['data'], b'3' * 36)\n        self.assertEqual(img['extra_frames'], (\n            {'gap': 40, 'id': 4, 'data': b'5' * 36},\n        ))\n        self.assertIsNone(li(a='d', d='f', i=1))\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['current_frame_index'], 0)\n        self.assertEqual(img['data'], b'5' * 36)\n        self.assertFalse(img['extra_frames'])\n        self.assertIsNone(li(a='d', d='f', i=1))\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['data'], b'5' * 36)\n        self.ae(g.image_count, 1)\n        self.assertIsNone(li(a='d', d='F', i=1))\n        self.ae(g.image_count, 0)\n        self.assertEqual(g.disk_cache.total_size, 0)\n\n        # test frame composition\n        self.assertEqual(li(a='t').code, 'OK')\n        self.assertEqual(g.disk_cache.total_size, 36)\n        t(payload='2' * 36)\n        t(payload='3' * 36, frame_number=3)\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], (\n            {'gap': 40, 'id': 2, 'data': b'2' * 36},\n            {'gap': 40, 'id': 3, 'data': b'3' * 36},\n        ))\n        self.assertEqual(li(a='c', i=11).code, 'ENOENT')\n        self.assertEqual(li(a='c', i=1, r=1, c=2).code, 'OK')\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], (\n            {'gap': 40, 'id': 2, 'data': b'abcdefghijkl'*3},\n            {'gap': 40, 'id': 3, 'data': b'3' * 36},\n        ))\n        self.assertEqual(li(a='c', i=1, r=2, c=3, w=1, h=2, x=1, y=1).code, 'OK')\n        img = g.image_for_client_id(1)\n        self.assertEqual(img['extra_frames'], (\n            {'gap': 40, 'id': 2, 'data': b'abcdefghijkl'*3},\n            {'gap': 40, 'id': 3, 'data': b'3' * 12 + (b'333abc' + b'3' * 6) * 2},\n        ))\n\n    def test_graphics_quota_enforcement(self):\n        s = self.create_screen()\n        g = s.grman\n        g.storage_limit = 36*2\n        li = make_send_command(s)\n        # test quota for simple images\n        self.assertEqual(li(a='T').code, 'OK')\n        self.assertEqual(li(a='T', i=2).code, 'OK')\n        self.assertEqual(g.disk_cache.total_size, g.storage_limit)\n        self.assertEqual(g.image_count, 2)\n        self.assertEqual(li(a='T', i=3).code, 'OK')\n        self.assertEqual(g.disk_cache.total_size, g.storage_limit)\n        self.assertEqual(g.image_count, 2)\n        # test quota for frames\n        for i in range(8):\n            self.assertEqual(li(payload=f'{i}' * 36, i=2).code, 'OK')\n        self.assertEqual(li(payload='x' * 36, i=2).code, 'ENOSPC')\n        # test editing should not trigger quota\n        self.assertEqual(li(payload='4' * 12, r=2, s=2, v=2, i=2).code, 'OK')\n\n        s.reset()\n        self.ae(g.image_count, 0)\n        self.assertEqual(g.disk_cache.total_size, 0)\n\n    @unittest.skipIf(Image is None, 'PIL not available, skipping PNG tests')\n    def test_cached_rgba_conversion(self):\n        from kitty.render_cache import ImageRenderCacheForTesting\n        w, h = 5, 3\n        rgba_data = byte_block(w * h * 4)\n        img = Image.frombytes('RGBA', (w, h), rgba_data)\n        buf = BytesIO()\n        img.save(buf, 'PNG')\n        png_data = buf.getvalue()\n        with tempfile.TemporaryDirectory() as cache_path:\n            irc = ImageRenderCacheForTesting(cache_path)\n            srcs, outputs = [], []\n            for i in range(2 * irc.max_entries):\n                with open(os.path.join(cache_path, f'{i}.png'), 'wb') as f:\n                    f.write(png_data)\n                srcs.append(f.name)\n                outputs.append(irc.render(f.name))\n                entries = list(irc.entries())\n                self.assertLessEqual(len(entries), irc.max_entries)\n            self.ae(irc.num_of_renders, len(outputs))\n            remaining_outputs = outputs[-irc.max_entries:]\n            for x in remaining_outputs:\n                self.assertTrue(os.path.exists(x))\n            for x in outputs[:-irc.max_entries]:\n                self.assertFalse(os.path.exists(x))\n            self.assertLess(os.path.getmtime(remaining_outputs[0]), os.path.getmtime(remaining_outputs[1]))\n            remaining_srcs = srcs[-irc.max_entries:]\n            self.ae(irc.render(remaining_srcs[0]), remaining_outputs[0])\n            self.ae(irc.num_of_renders, len(outputs))\n            self.assertGreater(os.path.getmtime(remaining_outputs[0]), os.path.getmtime(remaining_outputs[1]))\n\n            width, height, fd = irc(remaining_srcs[-1])\n            with open(fd, 'rb') as f:\n                self.ae((width, height), (w, h))\n                f.seek(8)\n                self.ae(rgba_data, f.read())\n"
  },
  {
    "path": "kitty_tests/keys.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom functools import partial\n\nimport kitty.fast_data_types as defines\nfrom kitty.key_encoding import EventType, KeyEvent, decode_key_event, encode_key_event\nfrom kitty.keys import Mappings\n\nfrom . import BaseTest\n\n\nclass TestKeys(BaseTest):\n\n    def test_encode_key_event(self):\n        enc = defines.encode_key_for_tty\n        ae = self.assertEqual\n        shift, alt, ctrl, super, hyper, meta = defines.GLFW_MOD_SHIFT, defines.GLFW_MOD_ALT, defines.GLFW_MOD_CONTROL, defines.GLFW_MOD_SUPER, defines.GLFW_MOD_HYPER, defines.GLFW_MOD_META  # noqa\n        num_lock, caps_lock = defines.GLFW_MOD_NUM_LOCK, defines.GLFW_MOD_CAPS_LOCK\n        press, repeat, release = defines.GLFW_PRESS, defines.GLFW_REPEAT, defines.GLFW_RELEASE  # noqa\n\n        def csi(mods=0, num=1, action=1, shifted_key=0, alternate_key=0, text=None, trailer='u'):\n            ans = '\\033['\n            if isinstance(num, str):\n                num = ord(num)\n            if num != 1 or mods or shifted_key or alternate_key or text:\n                ans += f'{num}'\n            if shifted_key or alternate_key:\n                if isinstance(shifted_key, str):\n                    shifted_key = ord(shifted_key)\n                ans += ':' + (f'{shifted_key}' if shifted_key else '')\n                if alternate_key:\n                    if isinstance(alternate_key, str):\n                        alternate_key = ord(alternate_key)\n                    ans += f':{alternate_key}'\n            if mods or action > 1 or text:\n                m = 0\n                if mods & shift:\n                    m |= 1\n                if mods & alt:\n                    m |= 2\n                if mods & ctrl:\n                    m |= 4\n                if mods & super:\n                    m |= 8\n                if mods & hyper:\n                    m |= 16\n                if mods & meta:\n                    m |= 32\n                if action > 1 or m:\n                    ans += f';{m+1}'\n                    if action > 1:\n                        ans += f':{action}'\n                elif text:\n                    ans += ';'\n            if text:\n                ans += ';' + ':'.join(map(str, map(ord, text)))\n            return ans + trailer\n\n        def mods_test(key, plain=None, shift=None, ctrl=None, alt=None, calt=None, cshift=None, ashift=None, csi_num=None, trailer='u'):\n            c = partial(csi, num=csi_num or key, trailer=trailer)\n            e = partial(enc, key=key)\n\n            def a(a, b):\n                ae(a, b, f\"{a.encode('ascii')} != {b.encode('ascii')}\")\n\n            def w(a, b):\n                return c(b) if a is None else a\n\n            a(e(), plain or c())\n            a(e(mods=defines.GLFW_MOD_SHIFT), w(shift, defines.GLFW_MOD_SHIFT))\n            a(e(mods=defines.GLFW_MOD_CONTROL), w(ctrl, defines.GLFW_MOD_CONTROL))\n            a(e(mods=defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL), w(calt, defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL))\n            a(e(mods=defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL), w(cshift, defines.GLFW_MOD_CONTROL | defines.GLFW_MOD_SHIFT))\n            a(e(mods=defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT), w(ashift, defines.GLFW_MOD_ALT | defines.GLFW_MOD_SHIFT))\n\n        def mkp(name, *a, **kw):\n            for x in (f'GLFW_FKEY_{name}', f'GLFW_FKEY_KP_{name}'):\n                k = getattr(defines, x)\n                mods_test(k, *a, **kw)\n\n        mkp('ENTER', '\\x0d', alt='\\033\\x0d', ctrl='\\x0d', shift='\\x0d', ashift='\\033\\x0d', calt='\\033\\x0d', cshift='\\x0d')\n        mods_test(defines.GLFW_FKEY_ESCAPE, '\\x1b', alt='\\033\\033', ctrl='\\x1b', shift='\\x1b', calt='\\x1b\\x1b', cshift='\\x1b', ashift='\\x1b\\x1b')\n        mods_test(defines.GLFW_FKEY_BACKSPACE, '\\x7f', alt='\\033\\x7f', ctrl='\\x08', shift='\\x7f', ashift='\\033\\x7f', cshift='\\x08', calt='\\x1b\\x08')\n        mods_test(defines.GLFW_FKEY_TAB, '\\t', alt='\\033\\t', shift='\\x1b[Z', ctrl='\\t', ashift='\\x1b\\x1b[Z', cshift='\\x1b[Z', calt='\\x1b\\t')\n        mkp('INSERT', csi_num=2, trailer='~')\n        mkp('DELETE', csi_num=3, trailer='~')\n        mkp('PAGE_UP', csi_num=5, trailer='~')\n        mkp('PAGE_DOWN', csi_num=6, trailer='~')\n        mkp('HOME', csi_num=1, trailer='H')\n        mkp('END', csi_num=1, trailer='F')\n        mods_test(defines.GLFW_FKEY_F1, '\\x1bOP', csi_num=1, trailer='P')\n        mods_test(defines.GLFW_FKEY_F2, '\\x1bOQ', csi_num=1, trailer='Q')\n        mods_test(defines.GLFW_FKEY_F3, '\\x1bOR', csi_num=13, trailer='~')\n        mods_test(defines.GLFW_FKEY_F4, '\\x1bOS', csi_num=1, trailer='S')\n        mods_test(defines.GLFW_FKEY_F5, csi_num=15, trailer='~')\n        mods_test(defines.GLFW_FKEY_F6, csi_num=17, trailer='~')\n        mods_test(defines.GLFW_FKEY_F7, csi_num=18, trailer='~')\n        mods_test(defines.GLFW_FKEY_F8, csi_num=19, trailer='~')\n        mods_test(defines.GLFW_FKEY_F9, csi_num=20, trailer='~')\n        mods_test(defines.GLFW_FKEY_F10, csi_num=21, trailer='~')\n        mods_test(defines.GLFW_FKEY_F11, csi_num=23, trailer='~')\n        mods_test(defines.GLFW_FKEY_F12, csi_num=24, trailer='~')\n        mkp('UP', csi_num=1, trailer='A')\n        mkp('DOWN', csi_num=1, trailer='B')\n        mkp('RIGHT', csi_num=1, trailer='C')\n        mkp('LEFT', csi_num=1, trailer='D')\n\n        # legacy key tests {{{\n        # start legacy letter tests (auto generated by gen-key-constants.py do not edit)\n        ae(enc(ord('`'), shifted_key=ord('~')), '`')\n        ae(enc(ord('`'), shifted_key=ord('~'), mods=shift), '~')\n        ae(enc(ord('`'), shifted_key=ord('~'), mods=alt), \"\\x1b\" + '`')\n        ae(enc(ord('`'), shifted_key=ord('~'), mods=shift | alt), \"\\x1b\" + '~')\n        ae(enc(ord('`'), shifted_key=ord('~'), mods=ctrl), '`')\n        ae(enc(ord('`'), shifted_key=ord('~'), mods=ctrl | alt), \"\\x1b\" + '`')\n        ae(enc(ord('1'), shifted_key=ord('!')), '1')\n        ae(enc(ord('1'), shifted_key=ord('!'), mods=shift), '!')\n        ae(enc(ord('1'), shifted_key=ord('!'), mods=alt), \"\\x1b\" + '1')\n        ae(enc(ord('1'), shifted_key=ord('!'), mods=shift | alt), \"\\x1b\" + '!')\n        ae(enc(ord('1'), shifted_key=ord('!'), mods=ctrl), '1')\n        ae(enc(ord('1'), shifted_key=ord('!'), mods=ctrl | alt), \"\\x1b\" + '1')\n        ae(enc(ord('2'), shifted_key=ord('@')), '2')\n        ae(enc(ord('2'), shifted_key=ord('@'), mods=shift), '@')\n        ae(enc(ord('2'), shifted_key=ord('@'), mods=alt), \"\\x1b\" + '2')\n        ae(enc(ord('2'), shifted_key=ord('@'), mods=shift | alt), \"\\x1b\" + '@')\n        ae(enc(ord('2'), shifted_key=ord('@'), mods=ctrl), '\\x00')\n        ae(enc(ord('2'), shifted_key=ord('@'), mods=ctrl | alt), \"\\x1b\" + '\\x00')\n        ae(enc(ord('3'), shifted_key=ord('#')), '3')\n        ae(enc(ord('3'), shifted_key=ord('#'), mods=shift), '#')\n        ae(enc(ord('3'), shifted_key=ord('#'), mods=alt), \"\\x1b\" + '3')\n        ae(enc(ord('3'), shifted_key=ord('#'), mods=shift | alt), \"\\x1b\" + '#')\n        ae(enc(ord('3'), shifted_key=ord('#'), mods=ctrl), '\\x1b')\n        ae(enc(ord('3'), shifted_key=ord('#'), mods=ctrl | alt), \"\\x1b\" + '\\x1b')\n        ae(enc(ord('4'), shifted_key=ord('$')), '4')\n        ae(enc(ord('4'), shifted_key=ord('$'), mods=shift), '$')\n        ae(enc(ord('4'), shifted_key=ord('$'), mods=alt), \"\\x1b\" + '4')\n        ae(enc(ord('4'), shifted_key=ord('$'), mods=shift | alt), \"\\x1b\" + '$')\n        ae(enc(ord('4'), shifted_key=ord('$'), mods=ctrl), '\\x1c')\n        ae(enc(ord('4'), shifted_key=ord('$'), mods=ctrl | alt), \"\\x1b\" + '\\x1c')\n        ae(enc(ord('5'), shifted_key=ord('%')), '5')\n        ae(enc(ord('5'), shifted_key=ord('%'), mods=shift), '%')\n        ae(enc(ord('5'), shifted_key=ord('%'), mods=alt), \"\\x1b\" + '5')\n        ae(enc(ord('5'), shifted_key=ord('%'), mods=shift | alt), \"\\x1b\" + '%')\n        ae(enc(ord('5'), shifted_key=ord('%'), mods=ctrl), '\\x1d')\n        ae(enc(ord('5'), shifted_key=ord('%'), mods=ctrl | alt), \"\\x1b\" + '\\x1d')\n        ae(enc(ord('6'), shifted_key=ord('^')), '6')\n        ae(enc(ord('6'), shifted_key=ord('^'), mods=shift), '^')\n        ae(enc(ord('6'), shifted_key=ord('^'), mods=alt), \"\\x1b\" + '6')\n        ae(enc(ord('6'), shifted_key=ord('^'), mods=shift | alt), \"\\x1b\" + '^')\n        ae(enc(ord('6'), shifted_key=ord('^'), mods=ctrl), '\\x1e')\n        ae(enc(ord('6'), shifted_key=ord('^'), mods=ctrl | alt), \"\\x1b\" + '\\x1e')\n        ae(enc(ord('7'), shifted_key=ord('&')), '7')\n        ae(enc(ord('7'), shifted_key=ord('&'), mods=shift), '&')\n        ae(enc(ord('7'), shifted_key=ord('&'), mods=alt), \"\\x1b\" + '7')\n        ae(enc(ord('7'), shifted_key=ord('&'), mods=shift | alt), \"\\x1b\" + '&')\n        ae(enc(ord('7'), shifted_key=ord('&'), mods=ctrl), '\\x1f')\n        ae(enc(ord('7'), shifted_key=ord('&'), mods=ctrl | alt), \"\\x1b\" + '\\x1f')\n        ae(enc(ord('8'), shifted_key=ord('*')), '8')\n        ae(enc(ord('8'), shifted_key=ord('*'), mods=shift), '*')\n        ae(enc(ord('8'), shifted_key=ord('*'), mods=alt), \"\\x1b\" + '8')\n        ae(enc(ord('8'), shifted_key=ord('*'), mods=shift | alt), \"\\x1b\" + '*')\n        ae(enc(ord('8'), shifted_key=ord('*'), mods=ctrl), '\\x7f')\n        ae(enc(ord('8'), shifted_key=ord('*'), mods=ctrl | alt), \"\\x1b\" + '\\x7f')\n        ae(enc(ord('9'), shifted_key=ord('(')), '9')\n        ae(enc(ord('9'), shifted_key=ord('('), mods=shift), '(')\n        ae(enc(ord('9'), shifted_key=ord('('), mods=alt), \"\\x1b\" + '9')\n        ae(enc(ord('9'), shifted_key=ord('('), mods=shift | alt), \"\\x1b\" + '(')\n        ae(enc(ord('9'), shifted_key=ord('('), mods=ctrl), '9')\n        ae(enc(ord('9'), shifted_key=ord('('), mods=ctrl | alt), \"\\x1b\" + '9')\n        ae(enc(ord('0'), shifted_key=ord(')')), '0')\n        ae(enc(ord('0'), shifted_key=ord(')'), mods=shift), ')')\n        ae(enc(ord('0'), shifted_key=ord(')'), mods=alt), \"\\x1b\" + '0')\n        ae(enc(ord('0'), shifted_key=ord(')'), mods=shift | alt), \"\\x1b\" + ')')\n        ae(enc(ord('0'), shifted_key=ord(')'), mods=ctrl), '0')\n        ae(enc(ord('0'), shifted_key=ord(')'), mods=ctrl | alt), \"\\x1b\" + '0')\n        ae(enc(ord('-'), shifted_key=ord('_')), '-')\n        ae(enc(ord('-'), shifted_key=ord('_'), mods=shift), '_')\n        ae(enc(ord('-'), shifted_key=ord('_'), mods=alt), \"\\x1b\" + '-')\n        ae(enc(ord('-'), shifted_key=ord('_'), mods=shift | alt), \"\\x1b\" + '_')\n        ae(enc(ord('-'), shifted_key=ord('_'), mods=ctrl), '-')\n        ae(enc(ord('-'), shifted_key=ord('_'), mods=ctrl | alt), \"\\x1b\" + '-')\n        ae(enc(ord('='), shifted_key=ord('+')), '=')\n        ae(enc(ord('='), shifted_key=ord('+'), mods=shift), '+')\n        ae(enc(ord('='), shifted_key=ord('+'), mods=alt), \"\\x1b\" + '=')\n        ae(enc(ord('='), shifted_key=ord('+'), mods=shift | alt), \"\\x1b\" + '+')\n        ae(enc(ord('='), shifted_key=ord('+'), mods=ctrl), '=')\n        ae(enc(ord('='), shifted_key=ord('+'), mods=ctrl | alt), \"\\x1b\" + '=')\n        ae(enc(ord('['), shifted_key=ord('{')), '[')\n        ae(enc(ord('['), shifted_key=ord('{'), mods=shift), '{')\n        ae(enc(ord('['), shifted_key=ord('{'), mods=alt), \"\\x1b\" + '[')\n        ae(enc(ord('['), shifted_key=ord('{'), mods=shift | alt), \"\\x1b\" + '{')\n        ae(enc(ord('['), shifted_key=ord('{'), mods=ctrl), '\\x1b')\n        ae(enc(ord('['), shifted_key=ord('{'), mods=ctrl | alt), \"\\x1b\" + '\\x1b')\n        ae(enc(ord(']'), shifted_key=ord('}')), ']')\n        ae(enc(ord(']'), shifted_key=ord('}'), mods=shift), '}')\n        ae(enc(ord(']'), shifted_key=ord('}'), mods=alt), \"\\x1b\" + ']')\n        ae(enc(ord(']'), shifted_key=ord('}'), mods=shift | alt), \"\\x1b\" + '}')\n        ae(enc(ord(']'), shifted_key=ord('}'), mods=ctrl), '\\x1d')\n        ae(enc(ord(']'), shifted_key=ord('}'), mods=ctrl | alt), \"\\x1b\" + '\\x1d')\n        ae(enc(ord('\\\\'), shifted_key=ord('|')), '\\\\')\n        ae(enc(ord('\\\\'), shifted_key=ord('|'), mods=shift), '|')\n        ae(enc(ord('\\\\'), shifted_key=ord('|'), mods=alt), \"\\x1b\" + '\\\\')\n        ae(enc(ord('\\\\'), shifted_key=ord('|'), mods=shift | alt), \"\\x1b\" + '|')\n        ae(enc(ord('\\\\'), shifted_key=ord('|'), mods=ctrl), '\\x1c')\n        ae(enc(ord('\\\\'), shifted_key=ord('|'), mods=ctrl | alt), \"\\x1b\" + '\\x1c')\n        ae(enc(ord(';'), shifted_key=ord(':')), ';')\n        ae(enc(ord(';'), shifted_key=ord(':'), mods=shift), ':')\n        ae(enc(ord(';'), shifted_key=ord(':'), mods=alt), \"\\x1b\" + ';')\n        ae(enc(ord(';'), shifted_key=ord(':'), mods=shift | alt), \"\\x1b\" + ':')\n        ae(enc(ord(';'), shifted_key=ord(':'), mods=ctrl), ';')\n        ae(enc(ord(';'), shifted_key=ord(':'), mods=ctrl | alt), \"\\x1b\" + ';')\n        ae(enc(ord(\"'\"), shifted_key=ord('\"')), \"'\")\n        ae(enc(ord(\"'\"), shifted_key=ord('\"'), mods=shift), '\"')\n        ae(enc(ord(\"'\"), shifted_key=ord('\"'), mods=alt), \"\\x1b\" + \"'\")\n        ae(enc(ord(\"'\"), shifted_key=ord('\"'), mods=shift | alt), \"\\x1b\" + '\"')\n        ae(enc(ord(\"'\"), shifted_key=ord('\"'), mods=ctrl), \"'\")\n        ae(enc(ord(\"'\"), shifted_key=ord('\"'), mods=ctrl | alt), \"\\x1b\" + \"'\")\n        ae(enc(ord(','), shifted_key=ord('<')), ',')\n        ae(enc(ord(','), shifted_key=ord('<'), mods=shift), '<')\n        ae(enc(ord(','), shifted_key=ord('<'), mods=alt), \"\\x1b\" + ',')\n        ae(enc(ord(','), shifted_key=ord('<'), mods=shift | alt), \"\\x1b\" + '<')\n        ae(enc(ord(','), shifted_key=ord('<'), mods=ctrl), ',')\n        ae(enc(ord(','), shifted_key=ord('<'), mods=ctrl | alt), \"\\x1b\" + ',')\n        ae(enc(ord('.'), shifted_key=ord('>')), '.')\n        ae(enc(ord('.'), shifted_key=ord('>'), mods=shift), '>')\n        ae(enc(ord('.'), shifted_key=ord('>'), mods=alt), \"\\x1b\" + '.')\n        ae(enc(ord('.'), shifted_key=ord('>'), mods=shift | alt), \"\\x1b\" + '>')\n        ae(enc(ord('.'), shifted_key=ord('>'), mods=ctrl), '.')\n        ae(enc(ord('.'), shifted_key=ord('>'), mods=ctrl | alt), \"\\x1b\" + '.')\n        ae(enc(ord('/'), shifted_key=ord('?')), '/')\n        ae(enc(ord('/'), shifted_key=ord('?'), mods=shift), '?')\n        ae(enc(ord('/'), shifted_key=ord('?'), mods=alt), \"\\x1b\" + '/')\n        ae(enc(ord('/'), shifted_key=ord('?'), mods=shift | alt), \"\\x1b\" + '?')\n        ae(enc(ord('/'), shifted_key=ord('?'), mods=ctrl), '\\x1f')\n        ae(enc(ord('/'), shifted_key=ord('?'), mods=ctrl | alt), \"\\x1b\" + '\\x1f')\n        ae(enc(ord('a'), shifted_key=ord('A')), 'a')\n        ae(enc(ord('a'), shifted_key=ord('A'), mods=shift), 'A')\n        ae(enc(ord('a'), shifted_key=ord('A'), mods=alt), \"\\x1b\" + 'a')\n        ae(enc(ord('a'), shifted_key=ord('A'), mods=shift | alt), \"\\x1b\" + 'A')\n        ae(enc(ord('a'), shifted_key=ord('A'), mods=ctrl), '\\x01')\n        ae(enc(ord('a'), shifted_key=ord('A'), mods=ctrl | alt), \"\\x1b\" + '\\x01')\n        ae(enc(ord('b'), shifted_key=ord('B')), 'b')\n        ae(enc(ord('b'), shifted_key=ord('B'), mods=shift), 'B')\n        ae(enc(ord('b'), shifted_key=ord('B'), mods=alt), \"\\x1b\" + 'b')\n        ae(enc(ord('b'), shifted_key=ord('B'), mods=shift | alt), \"\\x1b\" + 'B')\n        ae(enc(ord('b'), shifted_key=ord('B'), mods=ctrl), '\\x02')\n        ae(enc(ord('b'), shifted_key=ord('B'), mods=ctrl | alt), \"\\x1b\" + '\\x02')\n        ae(enc(ord('c'), shifted_key=ord('C')), 'c')\n        ae(enc(ord('c'), shifted_key=ord('C'), mods=shift), 'C')\n        ae(enc(ord('c'), shifted_key=ord('C'), mods=alt), \"\\x1b\" + 'c')\n        ae(enc(ord('c'), shifted_key=ord('C'), mods=shift | alt), \"\\x1b\" + 'C')\n        ae(enc(ord('c'), shifted_key=ord('C'), mods=ctrl), '\\x03')\n        ae(enc(ord('c'), shifted_key=ord('C'), mods=ctrl | alt), \"\\x1b\" + '\\x03')\n        ae(enc(ord('d'), shifted_key=ord('D')), 'd')\n        ae(enc(ord('d'), shifted_key=ord('D'), mods=shift), 'D')\n        ae(enc(ord('d'), shifted_key=ord('D'), mods=alt), \"\\x1b\" + 'd')\n        ae(enc(ord('d'), shifted_key=ord('D'), mods=shift | alt), \"\\x1b\" + 'D')\n        ae(enc(ord('d'), shifted_key=ord('D'), mods=ctrl), '\\x04')\n        ae(enc(ord('d'), shifted_key=ord('D'), mods=ctrl | alt), \"\\x1b\" + '\\x04')\n        ae(enc(ord('e'), shifted_key=ord('E')), 'e')\n        ae(enc(ord('e'), shifted_key=ord('E'), mods=shift), 'E')\n        ae(enc(ord('e'), shifted_key=ord('E'), mods=alt), \"\\x1b\" + 'e')\n        ae(enc(ord('e'), shifted_key=ord('E'), mods=shift | alt), \"\\x1b\" + 'E')\n        ae(enc(ord('e'), shifted_key=ord('E'), mods=ctrl), '\\x05')\n        ae(enc(ord('e'), shifted_key=ord('E'), mods=ctrl | alt), \"\\x1b\" + '\\x05')\n        ae(enc(ord('f'), shifted_key=ord('F')), 'f')\n        ae(enc(ord('f'), shifted_key=ord('F'), mods=shift), 'F')\n        ae(enc(ord('f'), shifted_key=ord('F'), mods=alt), \"\\x1b\" + 'f')\n        ae(enc(ord('f'), shifted_key=ord('F'), mods=shift | alt), \"\\x1b\" + 'F')\n        ae(enc(ord('f'), shifted_key=ord('F'), mods=ctrl), '\\x06')\n        ae(enc(ord('f'), shifted_key=ord('F'), mods=ctrl | alt), \"\\x1b\" + '\\x06')\n        ae(enc(ord('g'), shifted_key=ord('G')), 'g')\n        ae(enc(ord('g'), shifted_key=ord('G'), mods=shift), 'G')\n        ae(enc(ord('g'), shifted_key=ord('G'), mods=alt), \"\\x1b\" + 'g')\n        ae(enc(ord('g'), shifted_key=ord('G'), mods=shift | alt), \"\\x1b\" + 'G')\n        ae(enc(ord('g'), shifted_key=ord('G'), mods=ctrl), '\\x07')\n        ae(enc(ord('g'), shifted_key=ord('G'), mods=ctrl | alt), \"\\x1b\" + '\\x07')\n        ae(enc(ord('h'), shifted_key=ord('H')), 'h')\n        ae(enc(ord('h'), shifted_key=ord('H'), mods=shift), 'H')\n        ae(enc(ord('h'), shifted_key=ord('H'), mods=alt), \"\\x1b\" + 'h')\n        ae(enc(ord('h'), shifted_key=ord('H'), mods=shift | alt), \"\\x1b\" + 'H')\n        ae(enc(ord('h'), shifted_key=ord('H'), mods=ctrl), '\\x08')\n        ae(enc(ord('h'), shifted_key=ord('H'), mods=ctrl | alt), \"\\x1b\" + '\\x08')\n        ae(enc(ord('i'), shifted_key=ord('I')), 'i')\n        ae(enc(ord('i'), shifted_key=ord('I'), mods=shift), 'I')\n        ae(enc(ord('i'), shifted_key=ord('I'), mods=alt), \"\\x1b\" + 'i')\n        ae(enc(ord('i'), shifted_key=ord('I'), mods=shift | alt), \"\\x1b\" + 'I')\n        ae(enc(ord('i'), shifted_key=ord('I'), mods=ctrl), '\\t')\n        ae(enc(ord('i'), shifted_key=ord('I'), mods=ctrl | alt), \"\\x1b\" + '\\t')\n        ae(enc(ord('j'), shifted_key=ord('J')), 'j')\n        ae(enc(ord('j'), shifted_key=ord('J'), mods=shift), 'J')\n        ae(enc(ord('j'), shifted_key=ord('J'), mods=alt), \"\\x1b\" + 'j')\n        ae(enc(ord('j'), shifted_key=ord('J'), mods=shift | alt), \"\\x1b\" + 'J')\n        ae(enc(ord('j'), shifted_key=ord('J'), mods=ctrl), '\\n')\n        ae(enc(ord('j'), shifted_key=ord('J'), mods=ctrl | alt), \"\\x1b\" + '\\n')\n        ae(enc(ord('k'), shifted_key=ord('K')), 'k')\n        ae(enc(ord('k'), shifted_key=ord('K'), mods=shift), 'K')\n        ae(enc(ord('k'), shifted_key=ord('K'), mods=alt), \"\\x1b\" + 'k')\n        ae(enc(ord('k'), shifted_key=ord('K'), mods=shift | alt), \"\\x1b\" + 'K')\n        ae(enc(ord('k'), shifted_key=ord('K'), mods=ctrl), '\\x0b')\n        ae(enc(ord('k'), shifted_key=ord('K'), mods=ctrl | alt), \"\\x1b\" + '\\x0b')\n        ae(enc(ord('l'), shifted_key=ord('L')), 'l')\n        ae(enc(ord('l'), shifted_key=ord('L'), mods=shift), 'L')\n        ae(enc(ord('l'), shifted_key=ord('L'), mods=alt), \"\\x1b\" + 'l')\n        ae(enc(ord('l'), shifted_key=ord('L'), mods=shift | alt), \"\\x1b\" + 'L')\n        ae(enc(ord('l'), shifted_key=ord('L'), mods=ctrl), '\\x0c')\n        ae(enc(ord('l'), shifted_key=ord('L'), mods=ctrl | alt), \"\\x1b\" + '\\x0c')\n        ae(enc(ord('m'), shifted_key=ord('M')), 'm')\n        ae(enc(ord('m'), shifted_key=ord('M'), mods=shift), 'M')\n        ae(enc(ord('m'), shifted_key=ord('M'), mods=alt), \"\\x1b\" + 'm')\n        ae(enc(ord('m'), shifted_key=ord('M'), mods=shift | alt), \"\\x1b\" + 'M')\n        ae(enc(ord('m'), shifted_key=ord('M'), mods=ctrl), '\\r')\n        ae(enc(ord('m'), shifted_key=ord('M'), mods=ctrl | alt), \"\\x1b\" + '\\r')\n        ae(enc(ord('n'), shifted_key=ord('N')), 'n')\n        ae(enc(ord('n'), shifted_key=ord('N'), mods=shift), 'N')\n        ae(enc(ord('n'), shifted_key=ord('N'), mods=alt), \"\\x1b\" + 'n')\n        ae(enc(ord('n'), shifted_key=ord('N'), mods=shift | alt), \"\\x1b\" + 'N')\n        ae(enc(ord('n'), shifted_key=ord('N'), mods=ctrl), '\\x0e')\n        ae(enc(ord('n'), shifted_key=ord('N'), mods=ctrl | alt), \"\\x1b\" + '\\x0e')\n        ae(enc(ord('o'), shifted_key=ord('O')), 'o')\n        ae(enc(ord('o'), shifted_key=ord('O'), mods=shift), 'O')\n        ae(enc(ord('o'), shifted_key=ord('O'), mods=alt), \"\\x1b\" + 'o')\n        ae(enc(ord('o'), shifted_key=ord('O'), mods=shift | alt), \"\\x1b\" + 'O')\n        ae(enc(ord('o'), shifted_key=ord('O'), mods=ctrl), '\\x0f')\n        ae(enc(ord('o'), shifted_key=ord('O'), mods=ctrl | alt), \"\\x1b\" + '\\x0f')\n        ae(enc(ord('p'), shifted_key=ord('P')), 'p')\n        ae(enc(ord('p'), shifted_key=ord('P'), mods=shift), 'P')\n        ae(enc(ord('p'), shifted_key=ord('P'), mods=alt), \"\\x1b\" + 'p')\n        ae(enc(ord('p'), shifted_key=ord('P'), mods=shift | alt), \"\\x1b\" + 'P')\n        ae(enc(ord('p'), shifted_key=ord('P'), mods=ctrl), '\\x10')\n        ae(enc(ord('p'), shifted_key=ord('P'), mods=ctrl | alt), \"\\x1b\" + '\\x10')\n        ae(enc(ord('q'), shifted_key=ord('Q')), 'q')\n        ae(enc(ord('q'), shifted_key=ord('Q'), mods=shift), 'Q')\n        ae(enc(ord('q'), shifted_key=ord('Q'), mods=alt), \"\\x1b\" + 'q')\n        ae(enc(ord('q'), shifted_key=ord('Q'), mods=shift | alt), \"\\x1b\" + 'Q')\n        ae(enc(ord('q'), shifted_key=ord('Q'), mods=ctrl), '\\x11')\n        ae(enc(ord('q'), shifted_key=ord('Q'), mods=ctrl | alt), \"\\x1b\" + '\\x11')\n        ae(enc(ord('r'), shifted_key=ord('R')), 'r')\n        ae(enc(ord('r'), shifted_key=ord('R'), mods=shift), 'R')\n        ae(enc(ord('r'), shifted_key=ord('R'), mods=alt), \"\\x1b\" + 'r')\n        ae(enc(ord('r'), shifted_key=ord('R'), mods=shift | alt), \"\\x1b\" + 'R')\n        ae(enc(ord('r'), shifted_key=ord('R'), mods=ctrl), '\\x12')\n        ae(enc(ord('r'), shifted_key=ord('R'), mods=ctrl | alt), \"\\x1b\" + '\\x12')\n        ae(enc(ord('s'), shifted_key=ord('S')), 's')\n        ae(enc(ord('s'), shifted_key=ord('S'), mods=shift), 'S')\n        ae(enc(ord('s'), shifted_key=ord('S'), mods=alt), \"\\x1b\" + 's')\n        ae(enc(ord('s'), shifted_key=ord('S'), mods=shift | alt), \"\\x1b\" + 'S')\n        ae(enc(ord('s'), shifted_key=ord('S'), mods=ctrl), '\\x13')\n        ae(enc(ord('s'), shifted_key=ord('S'), mods=ctrl | alt), \"\\x1b\" + '\\x13')\n        ae(enc(ord('t'), shifted_key=ord('T')), 't')\n        ae(enc(ord('t'), shifted_key=ord('T'), mods=shift), 'T')\n        ae(enc(ord('t'), shifted_key=ord('T'), mods=alt), \"\\x1b\" + 't')\n        ae(enc(ord('t'), shifted_key=ord('T'), mods=shift | alt), \"\\x1b\" + 'T')\n        ae(enc(ord('t'), shifted_key=ord('T'), mods=ctrl), '\\x14')\n        ae(enc(ord('t'), shifted_key=ord('T'), mods=ctrl | alt), \"\\x1b\" + '\\x14')\n        ae(enc(ord('u'), shifted_key=ord('U')), 'u')\n        ae(enc(ord('u'), shifted_key=ord('U'), mods=shift), 'U')\n        ae(enc(ord('u'), shifted_key=ord('U'), mods=alt), \"\\x1b\" + 'u')\n        ae(enc(ord('u'), shifted_key=ord('U'), mods=shift | alt), \"\\x1b\" + 'U')\n        ae(enc(ord('u'), shifted_key=ord('U'), mods=ctrl), '\\x15')\n        ae(enc(ord('u'), shifted_key=ord('U'), mods=ctrl | alt), \"\\x1b\" + '\\x15')\n        ae(enc(ord('v'), shifted_key=ord('V')), 'v')\n        ae(enc(ord('v'), shifted_key=ord('V'), mods=shift), 'V')\n        ae(enc(ord('v'), shifted_key=ord('V'), mods=alt), \"\\x1b\" + 'v')\n        ae(enc(ord('v'), shifted_key=ord('V'), mods=shift | alt), \"\\x1b\" + 'V')\n        ae(enc(ord('v'), shifted_key=ord('V'), mods=ctrl), '\\x16')\n        ae(enc(ord('v'), shifted_key=ord('V'), mods=ctrl | alt), \"\\x1b\" + '\\x16')\n        ae(enc(ord('w'), shifted_key=ord('W')), 'w')\n        ae(enc(ord('w'), shifted_key=ord('W'), mods=shift), 'W')\n        ae(enc(ord('w'), shifted_key=ord('W'), mods=alt), \"\\x1b\" + 'w')\n        ae(enc(ord('w'), shifted_key=ord('W'), mods=shift | alt), \"\\x1b\" + 'W')\n        ae(enc(ord('w'), shifted_key=ord('W'), mods=ctrl), '\\x17')\n        ae(enc(ord('w'), shifted_key=ord('W'), mods=ctrl | alt), \"\\x1b\" + '\\x17')\n        ae(enc(ord('x'), shifted_key=ord('X')), 'x')\n        ae(enc(ord('x'), shifted_key=ord('X'), mods=shift), 'X')\n        ae(enc(ord('x'), shifted_key=ord('X'), mods=alt), \"\\x1b\" + 'x')\n        ae(enc(ord('x'), shifted_key=ord('X'), mods=shift | alt), \"\\x1b\" + 'X')\n        ae(enc(ord('x'), shifted_key=ord('X'), mods=ctrl), '\\x18')\n        ae(enc(ord('x'), shifted_key=ord('X'), mods=ctrl | alt), \"\\x1b\" + '\\x18')\n        ae(enc(ord('y'), shifted_key=ord('Y')), 'y')\n        ae(enc(ord('y'), shifted_key=ord('Y'), mods=shift), 'Y')\n        ae(enc(ord('y'), shifted_key=ord('Y'), mods=alt), \"\\x1b\" + 'y')\n        ae(enc(ord('y'), shifted_key=ord('Y'), mods=shift | alt), \"\\x1b\" + 'Y')\n        ae(enc(ord('y'), shifted_key=ord('Y'), mods=ctrl), '\\x19')\n        ae(enc(ord('y'), shifted_key=ord('Y'), mods=ctrl | alt), \"\\x1b\" + '\\x19')\n        ae(enc(ord('z'), shifted_key=ord('Z')), 'z')\n        ae(enc(ord('z'), shifted_key=ord('Z'), mods=shift), 'Z')\n        ae(enc(ord('z'), shifted_key=ord('Z'), mods=alt), \"\\x1b\" + 'z')\n        ae(enc(ord('z'), shifted_key=ord('Z'), mods=shift | alt), \"\\x1b\" + 'Z')\n        ae(enc(ord('z'), shifted_key=ord('Z'), mods=ctrl), '\\x1a')\n        ae(enc(ord('z'), shifted_key=ord('Z'), mods=ctrl | alt), \"\\x1b\" + '\\x1a')\n# end legacy letter tests\n        # }}}\n\n        ae(enc(key=ord(':'), shifted_key=ord('/'), mods=shift | alt), '\\x1b/')\n        for key in '~!@#$%^&*()_+{}|:\"<>?':\n            ae(enc(key=ord(key), mods=alt), '\\x1b' + key)\n        ae(enc(key=ord(' ')), ' ')\n        ae(enc(key=ord(' '), mods=ctrl | num_lock | caps_lock), '\\0')\n        ae(enc(key=ord(' '), mods=ctrl), '\\0')\n        ae(enc(key=ord(' '), mods=alt), '\\x1b ')\n        ae(enc(key=ord(' '), mods=shift), ' ')\n        ae(enc(key=ord(' '), mods=ctrl | alt), '\\x1b\\0')\n        ae(enc(key=ord(' '), mods=ctrl | shift), '\\0')\n        ae(enc(key=ord(' '), mods=alt | shift), '\\x1b ')\n        ae(enc(key=ord('i'), mods=ctrl | shift), csi(ctrl | shift, ord('i')))\n        ae(enc(key=defines.GLFW_FKEY_LEFT_SHIFT), '')\n        ae(enc(key=defines.GLFW_FKEY_CAPS_LOCK), '')\n\n        q = partial(enc, key=ord('a'))\n        ae(q(), 'a')\n        ae(q(text='a'), 'a')\n        ae(q(action=repeat), 'a')\n        ae(q(action=release), '')\n\n        # test disambiguate\n        dq = partial(enc, key_encoding_flags=0b1)\n        ae(dq(ord('a')), 'a')\n        ae(dq(defines.GLFW_FKEY_ESCAPE), csi(num=27))\n        ae(dq(defines.GLFW_FKEY_ENTER), '\\r')\n        ae(dq(defines.GLFW_FKEY_ENTER, mods=shift), csi(shift, 13))\n        ae(dq(defines.GLFW_FKEY_TAB), '\\t')\n        ae(dq(defines.GLFW_FKEY_BACKSPACE), '\\x7f')\n        ae(dq(defines.GLFW_FKEY_TAB, mods=shift), csi(shift, 9))\n        for mods in (ctrl, alt, ctrl | shift, alt | shift):\n            ae(dq(ord('a'), mods=mods), csi(mods, ord('a')))\n        ae(dq(ord(' '), mods=ctrl), csi(ctrl, ord(' ')))\n        for k in (defines.GLFW_FKEY_KP_PAGE_UP, defines.GLFW_FKEY_KP_0):\n            ae(dq(k), csi(num=k))\n            ae(dq(k, mods=ctrl), csi(ctrl, num=k))\n        ae(dq(defines.GLFW_FKEY_UP), '\\x1b[A')\n        ae(dq(defines.GLFW_FKEY_UP, mods=ctrl), csi(ctrl, 1, trailer='A'))\n\n        # test event type reporting\n        tq = partial(enc, key_encoding_flags=0b10)\n        ae(tq(ord('a')), 'a')\n        ae(tq(ord('a'), action=defines.GLFW_REPEAT), csi(num='a', action=2))\n        ae(tq(ord('a'), action=defines.GLFW_RELEASE), csi(num='a', action=3))\n        ae(tq(ord('a'), action=defines.GLFW_RELEASE, mods=shift), csi(shift, num='a', action=3))\n        tq = partial(enc, key_encoding_flags=0b11)\n        ae(tq(defines.GLFW_FKEY_BACKSPACE), '\\x7f')\n        ae(tq(defines.GLFW_FKEY_BACKSPACE, action=release), '')\n        tq = partial(enc, key_encoding_flags=0b11, mods=num_lock|caps_lock)\n        ae(tq(defines.GLFW_FKEY_ENTER), '\\r')\n        ae(tq(defines.GLFW_FKEY_ENTER, action=release), '')\n\n        # test alternate key reporting\n        aq = partial(enc, key_encoding_flags=0b100)\n        ae(aq(ord('a')), 'a')\n        ae(aq(ord('a'), shifted_key=ord('A')), 'a')\n        ae(aq(ord('a'), mods=shift, shifted_key=ord('A')), csi(shift, 'a', shifted_key='A'))\n        ae(aq(ord('a'), alternate_key=ord('A')), csi(num='a', alternate_key='A'))\n        ae(aq(ord('a'), mods=shift, shifted_key=ord('A'), alternate_key=ord('b')), csi(shift, 'a', shifted_key='A', alternate_key='b'))\n\n        # test report all keys\n        kq = partial(enc, key_encoding_flags=0b1000)\n        ae(kq(ord('a')), csi(num='a'))\n        ae(kq(ord('a'), action=defines.GLFW_REPEAT), csi(num='a'))\n        ae(kq(ord('a'), mods=ctrl), csi(ctrl, num='a'))\n        ae(kq(defines.GLFW_FKEY_UP), '\\x1b[A')\n        ae(kq(defines.GLFW_FKEY_LEFT_SHIFT), csi(num=defines.GLFW_FKEY_LEFT_SHIFT))\n        ae(kq(defines.GLFW_FKEY_ENTER), '\\x1b[13u')\n        ae(kq(defines.GLFW_FKEY_ENTER, mods=ctrl), '\\x1b[13;5u')\n        ae(kq(defines.GLFW_FKEY_TAB), '\\x1b[9u')\n        ae(kq(defines.GLFW_FKEY_BACKSPACE), '\\x1b[127u')\n\n        # test embed text\n        eq = partial(enc, key_encoding_flags=0b11000)\n        ae(eq(ord('a'), text='a'), csi(num='a', text='a'))\n        ae(eq(ord('a'), mods=shift, text='A'), csi(shift, num='a', text='A'))\n        ae(eq(ord('a'), mods=shift, text='AB'), csi(shift, num='a', text='AB'))\n\n        # test roundtripping via KeyEvent\n        for mods in range(64):\n            for action in EventType:\n                for key in ('ENTER', 'a', 'TAB', 'F3'):\n                    for shifted_key in ('', 'X'):\n                        for alternate_key in ('', 'Y'):\n                            for text in ('', 'moose'):\n                                ev = KeyEvent(\n                                    type=action, mods=mods, key=key, text=text, shifted_key=shifted_key, alternate_key=alternate_key,\n                                    shift=bool(mods & 1), alt=bool(mods & 2), ctrl=bool(mods & 4), super=bool(mods & 8),\n                                    hyper=bool(mods & 16), meta=bool(mods & 32)\n                                )\n                                ec = encode_key_event(ev)\n                                q = decode_key_event(ec[2:-1], ec[-1])\n                                self.ae(ev, q)\n\n    def test_encode_mouse_event(self):\n        NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL = range(4)\n        L, M, R = 1, 2, 3\n        protocol = SGR_PROTOCOL\n\n        def enc(button=L, action=defines.PRESS, mods=0, x=1, y=1):\n            return defines.test_encode_mouse(x, y, protocol, button, action, mods)\n\n        self.ae(enc(), '<0;1;1M')\n        self.ae(enc(action=defines.RELEASE), '<0;1;1m')\n        self.ae(enc(action=defines.MOVE, button=0), '<35;1;1M')\n        self.ae(enc(action=defines.DRAG), '<32;1;1M')\n\n        self.ae(enc(R), '<2;1;1M')\n        self.ae(enc(R, action=defines.RELEASE), '<2;1;1m')\n        self.ae(enc(R, action=defines.DRAG), '<34;1;1M')\n\n        self.ae(enc(M), '<1;1;1M')\n        self.ae(enc(M, action=defines.RELEASE), '<1;1;1m')\n        self.ae(enc(M, action=defines.DRAG), '<33;1;1M')\n\n        self.ae(enc(x=1234, y=5678), '<0;1234;5678M')\n        self.ae(enc(mods=defines.GLFW_MOD_SHIFT), '<4;1;1M')\n        self.ae(enc(mods=defines.GLFW_MOD_ALT), '<8;1;1M')\n        self.ae(enc(mods=defines.GLFW_MOD_CONTROL), '<16;1;1M')\n\n    def test_mapping(self):\n        from kitty.config import load_config\n        from kitty.options.utils import parse_shortcut\n        af = self.assertFalse\n\n        class Window:\n            def __init__(self, id=1):\n                self.key_seqs = []\n                self.id = id\n\n            def send_key_sequence(self, *s):\n                self.key_seqs.extend(s)\n\n        class TM(Mappings):\n\n            def __init__(self, *lines, active_window = Window()):\n                self.active_window = active_window\n                self.windows = [active_window]\n                bad_lines = []\n                self.options = load_config(overrides=lines, accumulate_bad_lines=bad_lines)\n                af(bad_lines)\n                self.ignore_os_keyboard_processing = False\n                super().__init__()\n\n            def get_active_window(self):\n                return self.active_window\n\n            def match_windows(self, expr: str):\n                for w in self.windows:\n                    if str(w.id) == expr:\n                        yield w\n\n            def show_error(self, title: str, msg: str) -> None:\n                pass\n\n            def ring_bell(self) -> None:\n                pass\n\n            def debug_print(self, *args, end: str = '\\n') -> None:\n                pass\n\n            def combine(self, action_definition: str) -> bool:\n                self.actions.append(action_definition)\n                if action_definition.startswith('push_keyboard_mode '):\n                    self.push_keyboard_mode(action_definition.partition(' ')[2])\n                elif action_definition == 'pop_keyboard_mode':\n                    self.pop_keyboard_mode()\n                return bool(action_definition)\n\n            def set_ignore_os_keyboard_processing(self, on: bool) -> None:\n                self.ignore_os_keyboard_processing = on\n\n            def set_cocoa_global_shortcuts(self, opts):\n                return {}\n\n            def get_options(self):\n                return self.options\n\n            def __call__(self, *keys: str):\n                self.actions = []\n                self.active_window.key_seqs = []\n                consumed = []\n                for key in keys:\n                    sk = parse_shortcut(key)\n                    ev = defines.KeyEvent(sk.key, 0, 0, sk.mods)\n                    consumed.append(self.dispatch_possible_special_key(ev))\n                return consumed\n\n        tm = TM('map ctrl+a new_window_with_cwd')\n        self.ae(tm('ctrl+a'), [True])\n        self.ae(tm.actions, ['new_window_with_cwd'])\n\n        tm = TM('map ctrl+f>2 set_font_size 20')\n        self.ae(tm('ctrl+f', '2'), [True, True])\n        self.ae(tm.actions, ['set_font_size 20'])\n        af(tm.active_window.key_seqs)\n        # unmatched multi key mapping should send all keys to child\n        self.ae(tm('ctrl+f', '1'), [True, False])\n        af(tm.actions)\n        self.ae(len(tm.active_window.key_seqs), 1)  # ctrl+f should have been sent to the window\n        # multi-key mapping that is unmapped should send all keys to child\n        tm = TM('map kitty_mod+p>f')\n        self.ae(tm('ctrl+shift+p', 'f'), [True, False])\n        self.ae(len(tm.active_window.key_seqs), 1)\n\n        # unmap\n        tm = TM('map kitty_mod+enter')\n        self.ae(tm('ctrl+shift+enter'), [False])\n\n        # single key mapping overrides previous all multi-key mappings with same prefix\n        tm = TM('map kitty_mod+p new_window')\n        self.ae(tm('ctrl+shift+p', 'f'), [True, False])\n        self.ae(tm.actions, ['new_window'])\n        # multi-key mapping overrides previous single key mapping with same prefix\n        tm = TM('map kitty_mod+s>p new_window')\n        self.ae(tm('ctrl+shift+s', 'p'), [True, True])\n        self.ae(tm.actions, ['new_window'])\n        # mix of single and multi-key mappings with same prefix\n        tm = TM('map alt+p>1 multi1', 'map alt+p single1', 'map alt+p>2 multi2')\n        self.ae(tm('alt+p', '2'), [True, True])\n        self.ae(tm.actions, ['multi2'])\n        self.ae(tm('alt+p', '1'), [True, False])\n        af(tm.actions)\n        self.ae(len(tm.active_window.key_seqs), 1)\n\n        # a single multi-key mapping should not prematurely match\n        tm = TM('map alt+1>2>3 new_window')\n        self.ae(tm('alt+1', '2'), [True, True])\n        af(tm.actions)\n        tm = TM('map alt+1>2>3 new_window')\n        self.ae(tm('alt+1', '2', '3'), [True, True, True])\n        self.ae(tm.actions, ['new_window'])\n\n        # changing a multi key mapping\n        tm = TM('map kitty_mod+p>f new_window')\n        self.ae(tm('ctrl+shift+p', 'f'), [True, True])\n        self.ae(tm.actions, ['new_window'])\n\n        # different behavior with focus selection\n        tm = TM('map --when-focus-on 2 kitty_mod+t')\n        tm.windows.append(Window(2))\n        self.ae(tm('ctrl+shift+t'), [True])\n        tm.active_window = tm.windows[1]\n        self.ae(tm('ctrl+shift+t'), [False])\n\n        # modal mappings\n        tm = TM('map --new-mode mw --on-unknown end kitty_mod+f7', 'map --mode mw left neighboring_window left', 'map --mode mw right neighboring_window right')\n        self.ae(tm('ctrl+shift+f7'), [True])\n        self.ae(tm.actions, ['push_keyboard_mode mw'])\n        self.ae(tm('right'), [True])\n        self.ae(tm.actions, ['neighboring_window right'])\n        self.ae(tm('left'), [True])\n        self.ae(tm.actions, ['neighboring_window left'])\n        self.ae(tm('x'), [True])\n        af(tm.keyboard_mode_stack)\n\n        # modal mapping with --on-action=end must restore OS keyboard processing\n        tm = TM('map --new-mode mw --on-action end m', 'map --mode mw a new_window')\n        self.ae(tm('m', 'a'), [True, True])\n        self.ae(tm.actions, ['push_keyboard_mode mw', 'new_window'])\n        af(tm.ignore_os_keyboard_processing)\n"
  },
  {
    "path": "kitty_tests/layout.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom kitty.config import defaults\nfrom kitty.fast_data_types import Region\nfrom kitty.layout.base import lgd\nfrom kitty.layout.interface import Grid, Horizontal, Splits, Stack, Tall\nfrom kitty.layout.splits import Pair\nfrom kitty.types import WindowGeometry\nfrom kitty.window import EdgeWidths\nfrom kitty.window_list import WindowList, reset_group_id_counter\n\nfrom . import BaseTest\n\n\nclass Window:\n\n    def __init__(self, win_id, overlay_for=None, overlay_window_id=None):\n        self.id = win_id\n        self.overlay_for = overlay_for\n        self.overlay_window_id = overlay_window_id\n        self.is_visible_in_layout = True\n        self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)\n        self.padding = EdgeWidths()\n        self.margin = EdgeWidths()\n        self.focused = False\n\n    def focus_changed(self, focused):\n        self.focused = focused\n\n    def effective_border(self):\n        return 1\n\n    def effective_padding(self, edge):\n        return 1\n\n    def effective_margin(self, edge):\n        return 1\n\n    def set_visible_in_layout(self, val):\n        self.is_visible_in_layout = bool(val)\n\n    def set_geometry(self, geometry):\n        self.geometry = geometry\n\n\ndef create_layout(cls, opts=None, border_width=2):\n    if opts is None:\n        opts = defaults\n    ans = cls(1, 1)\n    ans.set_active_window_in_os_window = lambda idx: None\n    ans.swap_windows_in_os_window = lambda a, b: None\n    orig = ans._set_dimensions\n    def set_dimensions(all_windows):\n        orig(all_windows)\n        # we need a non-zero width and height for central\n        lgd.central = Region((0, 0, 0, 0, 1, 1))\n    ans._set_dimensions = set_dimensions\n    return ans\n\n\nclass Tab:\n\n    def active_window_changed(self):\n        self.current_layout.update_visibility(self.windows)\n\n\ndef create_windows(layout, num=5):\n    t = Tab()\n    t.current_layout = layout\n    t.windows = ans = WindowList(t)\n    ans.tab_mem = t\n    reset_group_id_counter()\n    for i in range(num):\n        ans.add_window(Window(i + 1))\n    ans.set_active_group_idx(0)\n    return ans\n\n\ndef utils(self, q, windows):\n    def ids():\n        return [w.id for w in windows.groups]\n\n    def visible_ids():\n        return {gr.id for gr in windows.groups if gr.is_visible_in_layout}\n\n    def expect_ids(*a):\n        self.assertEqual(tuple(ids()), a)\n\n    def check_visible():\n        if q.only_active_window_visible:\n            self.ae(visible_ids(), {windows.active_group.id})\n        else:\n            self.ae(visible_ids(), {gr.id for gr in windows.groups})\n    return ids, visible_ids, expect_ids, check_visible\n\n\nclass TestLayout(BaseTest):\n\n    def setUp(self):\n        super().setUp()\n        self.set_options()\n\n    def do_ops_test(self, q):\n        windows = create_windows(q)\n        ids, visible_ids, expect_ids, check_visible = utils(self, q, windows)\n        # Test layout\n        q(windows)\n        self.ae(windows.active_group_idx, 0)\n        expect_ids(*range(1, len(windows)+1))\n        check_visible()\n\n        # Test nth_window\n        for i in range(windows.num_groups):\n            q.activate_nth_window(windows, i)\n            self.ae(windows.active_group_idx, i)\n            expect_ids(*range(1, len(windows)+1))\n            check_visible()\n\n        # Test next_window\n        for i in range(2 * windows.num_groups):\n            expected = (windows.active_group_idx + 1) % windows.num_groups\n            q.next_window(windows)\n            self.ae(windows.active_group_idx, expected)\n            expect_ids(*range(1, len(windows)+1))\n            check_visible()\n\n        # Test move_window\n        windows.set_active_group_idx(0)\n        expect_ids(1, 2, 3, 4, 5)\n        q.move_window(windows, 3)\n        self.ae(windows.active_group_idx, 3)\n        expect_ids(4, 2, 3, 1, 5)\n        check_visible()\n        windows.set_active_group_idx(0)\n        q.move_window(windows, 3)\n        expect_ids(*range(1, len(windows)+1))\n        check_visible()\n\n        # Test add_window\n        windows.set_active_group_idx(4)\n        q.add_window(windows, Window(6))\n        self.ae(windows.num_groups, 6)\n        self.ae(windows.active_group_idx, 5)\n        expect_ids(*range(1, windows.num_groups+1))\n        check_visible()\n\n        # Test remove_window\n        prev_window = windows.active_window\n        windows.set_active_group_idx(3)\n        self.ae(windows.active_group_idx, 3)\n        windows.remove_window(windows.active_window)\n        self.ae(windows.active_window, prev_window)\n        check_visible()\n        expect_ids(1, 2, 3, 5, 6)\n\n        windows.set_active_group_idx(0)\n        to_remove = windows.active_window\n        windows.set_active_group_idx(3)\n        windows.remove_window(to_remove)\n        self.ae(windows.active_group_idx, 3)\n        check_visible()\n        expect_ids(2, 3, 5, 6)\n\n        # Test set_active_window\n        for i in range(windows.num_groups):\n            windows.set_active_group_idx(i)\n            self.ae(i, windows.active_group_idx)\n            check_visible()\n\n        # Test\n\n    def do_overlay_test(self, q):\n        windows = create_windows(q)\n        ids, visible_ids, expect_ids, check_visible = utils(self, q, windows)\n\n        # Test add_window\n        w = Window(len(windows) + 1)\n        before = windows.active_group_idx\n        overlaid_group = before\n        overlay_window_id = w.id\n        windows.add_window(w, group_of=windows.active_window)\n        self.ae(before, windows.active_group_idx)\n        self.ae(w, windows.active_window)\n        expect_ids(1, 2, 3, 4, 5)\n        check_visible()\n\n        # Test layout\n        q(windows)\n        expect_ids(1, 2, 3, 4, 5)\n        check_visible()\n        w = Window(len(windows) + 1)\n        windows.add_window(w)\n        expect_ids(1, 2, 3, 4, 5, 6)\n        self.ae(windows.active_group_idx, windows.num_groups - 1)\n\n        # Test nth_window\n        for i in range(windows.num_groups):\n            q.activate_nth_window(windows, i)\n            self.ae(windows.active_group_idx, i)\n            if i == overlaid_group:\n                self.ae(windows.active_window.id, overlay_window_id)\n            expect_ids(1, 2, 3, 4, 5, 6)\n            check_visible()\n\n        # Test next_window\n        for i in range(windows.num_groups):\n            expected = (windows.active_group_idx + 1) % windows.num_groups\n            q.next_window(windows)\n            self.ae(windows.active_group_idx, expected)\n            expect_ids(1, 2, 3, 4, 5, 6)\n            check_visible()\n\n        # Test move_window\n        windows.set_active_group_idx(overlaid_group)\n        expect_ids(1, 2, 3, 4, 5, 6)\n        q.move_window(windows, 3)\n        self.ae(windows.active_group_idx, 3)\n        self.ae(windows.active_window.id, overlay_window_id)\n        expect_ids(4, 2, 3, 1, 5, 6)\n        check_visible()\n        windows.set_active_group_idx(0)\n        q.move_window(windows, 3)\n        expect_ids(1, 2, 3, 4, 5, 6)\n        check_visible()\n\n        # Test set_active_window\n        for i in range(windows.num_groups):\n            windows.set_active_group_idx(i)\n            self.ae(i, windows.active_group_idx)\n            if i == overlaid_group:\n                self.ae(windows.active_window.id, overlay_window_id)\n            check_visible()\n\n        # Test remove_window\n        expect_ids(1, 2, 3, 4, 5, 6)\n        windows.set_active_group_idx(overlaid_group)\n        windows.remove_window(overlay_window_id)\n        self.ae(windows.active_group_idx, overlaid_group)\n        self.ae(windows.active_window.id, 1)\n        expect_ids(1, 2, 3, 4, 5, 6)\n        check_visible()\n\n    def test_layout_operations(self):\n        for layout_class in (Stack, Horizontal, Tall, Grid):\n            q = create_layout(layout_class)\n            self.do_ops_test(q)\n\n    def test_overlay_layout_operations(self):\n        for layout_class in (Stack, Horizontal, Tall, Grid):\n            q = create_layout(layout_class)\n            self.do_overlay_test(q)\n\n    def test_splits(self):\n        q = create_layout(Splits)\n        all_windows = create_windows(q, num=0)\n        q.add_window(all_windows, Window(1))\n        self.ae(all_windows.active_group_idx, 0)\n        q.add_window(all_windows, Window(2), location='vsplit')\n        self.ae(all_windows.active_group_idx, 1)\n        q(all_windows)\n        self.ae(q.pairs_root.pair_for_window(2).horizontal, True)\n        q.add_window(all_windows, Window(3), location='hsplit')\n        self.ae(q.pairs_root.pair_for_window(2).horizontal, False)\n        q.add_window(all_windows, Window(4), location='vsplit')\n        windows = list(all_windows)\n        windows[0].set_geometry(WindowGeometry(0, 0, 10, 20, 0, 0))\n        windows[1].set_geometry(WindowGeometry(11, 0, 20, 10, 0, 0))\n        windows[2].set_geometry(WindowGeometry(11, 11, 15, 20, 0, 0))\n        windows[3].set_geometry(WindowGeometry(16, 11, 20, 20, 0, 0))\n        self.ae(q.neighbors_for_window(windows[0], all_windows), {'right': [2, 3]})\n        self.ae(q.neighbors_for_window(windows[1], all_windows), {'left': [1], 'bottom': [3, 4]})\n        self.ae(q.neighbors_for_window(windows[2], all_windows), {'left': [1], 'right': [4], 'top': [2]})\n        self.ae(q.neighbors_for_window(windows[3], all_windows), {'left': [3], 'top': [2]})\n\n    def test_splits_maximize(self):\n        q = create_layout(Splits)\n        all_windows = create_windows(q, num=0)\n        w1 = Window(1)\n        q.add_window(all_windows, w1)\n        w2 = Window(2)\n        q.add_window(all_windows, w2, location='vsplit')\n        w3 = Window(3)\n        q.add_window(all_windows, w3, location='hsplit')\n        # Layout: w1 | (w2 above w3) — horizontal split at root, vertical split on right\n        root = q.pairs_root\n        # root is horizontal, containing w1 and [w2/w3 vertical pair]\n        self.ae(root.horizontal, True)\n        right_pair = root.two if isinstance(root.two, Pair) else root.one\n        self.assertIsInstance(right_pair, Pair)\n\n        # Focus window 3 (bottom-right)\n        all_windows.set_active_group_idx(all_windows.groups.index(all_windows.group_for_window(w3)))\n\n        # Save original biases\n        root_bias_before = root.bias\n        right_pair_bias_before = right_pair.bias\n\n        # maximize vertical (fill full height) — affects vertical (horizontal==False) pairs\n        result = q.layout_action('maximize', ('vertical',), all_windows)\n        self.assertTrue(result)\n        # right_pair is vertical (horizontal==False) so its bias should be 0.0 (w3 is in 'two')\n        self.ae(right_pair.bias, 0.0)\n        # root is horizontal so its bias should be unchanged\n        self.ae(root.bias, root_bias_before)\n        # _maximized_biases should track w3's vertical maximize\n        self.assertIn((all_windows.active_group.id, False), q._maximized_biases)\n\n        # Toggle back\n        result = q.layout_action('maximize', ('vertical',), all_windows)\n        self.assertTrue(result)\n        self.ae(right_pair.bias, right_pair_bias_before)\n        self.ae(getattr(q, '_maximized_biases', {}), {})\n\n        # maximize horizontal (fill full width) — affects horizontal pairs\n        result = q.layout_action('maximize', ('horizontal',), all_windows)\n        self.assertTrue(result)\n        # root is horizontal, w3 is under root.two (right side), so bias should be 0.0\n        self.ae(root.bias, 0.0)\n        # right_pair is vertical, so unchanged\n        self.ae(right_pair.bias, right_pair_bias_before)\n\n        # Toggle back\n        result = q.layout_action('maximize', ('horizontal',), all_windows)\n        self.assertTrue(result)\n        self.ae(root.bias, root_bias_before)\n"
  },
  {
    "path": "kitty_tests/main.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport importlib\nimport os\nimport re\nimport shutil\nimport subprocess\nimport sys\nimport time\nimport unittest\nfrom collections.abc import Callable, Generator, Iterator, Sequence\nfrom contextlib import contextmanager\nfrom functools import lru_cache\nfrom tempfile import TemporaryDirectory, mkdtemp\nfrom threading import Thread\nfrom typing import (\n    Any,\n    NoReturn,\n    Optional,\n)\n\nfrom . import BaseTest\n\n\ndef contents(package: str) -> Iterator[str]:\n    try:\n        if sys.version_info[:2] < (3, 10):\n            raise ImportError(\"importlib.resources.files() doesn't work with frozen builds on python 3.9\")\n        from importlib.resources import files\n    except ImportError:\n        from importlib.resources import contents\n        return iter(contents(package))\n    return (path.name for path in files(package).iterdir())\n\n\ndef itertests(suite: unittest.TestSuite) -> Generator[unittest.TestCase, None, None]:\n    stack = [suite]\n    while stack:\n        suite = stack.pop()\n        for test in suite:\n            if isinstance(test, unittest.TestSuite):\n                stack.append(test)\n                continue\n            if test.__class__.__name__ == 'ModuleImportFailure':\n                raise Exception('Failed to import a test module: %s' % test)\n            yield test\n\n\ndef find_all_tests(package: str = '', excludes: Sequence[str] = ('main', 'gr')) -> unittest.TestSuite:\n    suits = []\n    if not package:\n        package = __name__.rpartition('.')[0] if '.' in __name__ else 'kitty_tests'\n    for x in contents(package):\n        name, ext = os.path.splitext(x)\n        if ext in ('.py', '.pyc') and name not in excludes:\n            m = importlib.import_module(package + '.' + x.partition('.')[0])\n            suits.append(unittest.defaultTestLoader.loadTestsFromModule(m))\n    return unittest.TestSuite(suits)\n\n\ndef filter_tests(suite: unittest.TestSuite, test_ok: Callable[[unittest.TestCase], bool]) -> unittest.TestSuite:\n    ans = unittest.TestSuite()\n    added: set[unittest.TestCase] = set()\n    for test in itertests(suite):\n        if test_ok(test) and test not in added:\n            ans.addTest(test)\n            added.add(test)\n    return ans\n\n\ndef filter_tests_by_name(suite: unittest.TestSuite, *names: str) -> unittest.TestSuite:\n    names_ = {x if x.startswith('test_') else 'test_' + x for x in names}\n\n    def q(test: unittest.TestCase) -> bool:\n        return test._testMethodName in names_\n\n    return filter_tests(suite, q)\n\n\ndef filter_tests_by_module(suite: unittest.TestSuite, *names: str) -> unittest.TestSuite:\n    names_ = frozenset(names)\n\n    def q(test: unittest.TestCase) -> bool:\n        m = test.__class__.__module__.rpartition('.')[-1]\n        return m in names_\n\n    return filter_tests(suite, q)\n\n\n@lru_cache\ndef python_for_type_check() -> str:\n    return shutil.which('python') or shutil.which('python3') or 'python'\n\n\ndef type_check() -> NoReturn:\n    from kitty.cli_stub import generate_stub  # type:ignore\n\n    generate_stub()\n    from kittens.tui.operations_stub import generate_stub  # type: ignore\n\n    generate_stub()\n    py = python_for_type_check()\n    os.execlp(py, py, '-m', 'mypy', '--pretty')\n\n\ndef run_cli(suite: unittest.TestSuite, verbosity: int = 4) -> bool:\n    r = unittest.TextTestRunner\n    r.resultclass = unittest.TextTestResult\n    runner = r(verbosity=verbosity)\n    runner.tb_locals = True  # type: ignore\n    from . import forwardable_stdio\n    with forwardable_stdio():\n        result = runner.run(suite)\n    sys.stdout.flush()\n    sys.stderr.flush()\n    return result.wasSuccessful()\n\n\ndef find_testable_go_packages() -> tuple[set[str], dict[str, list[str]]]:\n    test_functions: dict[str, list[str]] = {}\n    ans = set()\n    base = os.getcwd()\n    pat = re.compile(r'^func Test([A-Z]\\w+)', re.MULTILINE)\n    for (dirpath, dirnames, filenames) in os.walk(base):\n        if 'b' in dirnames and os.path.basename(dirpath) == 'bypy':\n            dirnames.remove('b')\n        for f in filenames:\n            if f.endswith('_test.go'):\n                q = os.path.relpath(dirpath, base)\n                ans.add(q)\n                with open(os.path.join(dirpath, f)) as s:\n                    raw = s.read()\n                for m in pat.finditer(raw):\n                    test_functions.setdefault(m.group(1), []).append(q)\n    return ans, test_functions\n\n\n@lru_cache\ndef go_exe() -> str:\n    return shutil.which('go') or ''\n\n\nclass GoProc(Thread):\n\n    def __init__(self, cmd: list[str]):\n        super().__init__(name='GoProc')\n        from kitty.constants import kitty_exe\n        env = os.environ.copy()\n        env['KITTY_PATH_TO_KITTY_EXE'] = kitty_exe()\n        self.stdout = b''\n        self.start_time = time.monotonic()\n        self.tdir = mkdtemp(prefix='kitty-go-tests-')\n        env['HOME'] = self.tdir\n        if not env.get('GOCACHE') and (gop := os.path.expanduser('~/.cache/go-build')) and os.path.isdir(gop):\n            env['GOCACHE'] = gop\n        if not env.get('GOMODCACHE') and (gop := os.path.expanduser('~/go/pkg/mod')) and os.path.isdir(gop):\n            env['GOMODCACHE'] = gop\n        env['XDG_CONFIG_HOME'] = self.tdir + '/conf'\n        os.mkdir(env['XDG_CONFIG_HOME'])\n        env['XDG_CACHE_HOME'] = self.tdir + '/cache'\n        os.mkdir(env['XDG_CACHE_HOME'])\n        self.proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)\n        self.start()\n\n    @property\n    def runtime(self):\n        return self.end_time - self.start_time\n\n    @property\n    def returncode(self):\n        return self.proc.returncode\n\n    def run(self) -> None:\n        try:\n            self.stdout, _ = self.proc.communicate()\n            self.proc.stdout.close()\n        finally:\n            shutil.rmtree(self.tdir)\n\n    def wait(self, timeout=None) -> None:\n        try:\n            self.join(timeout)\n        except KeyboardInterrupt:\n            self.proc.terminate()\n            if self.proc.wait(0.1) is None:\n                self.proc.kill()\n        self.join()\n        self.end_time = time.monotonic()\n        return self.stdout.decode('utf-8', 'replace'), self.proc.returncode\n\n\ndef run_go(packages: set[str], names: str) -> GoProc:\n    go = go_exe()\n    go_pkg_args = [f'github.com/kovidgoyal/kitty/{x}' for x in packages]\n    cmd = [go, 'test', '--tags', 'testing', '-v']\n    for name in names:\n        cmd.extend(('-run', name))\n    cmd += go_pkg_args\n    return GoProc(cmd)\n\n\n\ndef reduce_go_pkgs(module: str, names: Sequence[str]) -> set[str]:\n    if not go_exe():\n        raise SystemExit('go executable not found, current path: ' + repr(os.environ.get('PATH', '')))\n    go_packages, go_functions = find_testable_go_packages()\n    if module:\n        go_packages &= {module}\n    if names:\n        pkgs = set()\n        for name in names:\n            pkgs |= set(go_functions.get(name, []))\n        go_packages &= pkgs\n    return go_packages\n\n\ndef run_python_tests(args: Any, go_proc: 'Optional[GoProc]' = None) -> None:\n    tests = find_all_tests()\n\n    def print_go() -> None:\n        stdout, rc = go_proc.wait()\n        if go_proc.returncode == 0 and tests._tests:\n            print(f'All Go tests succeeded, ran in {go_proc.runtime:.1f} seconds', flush=True)\n        else:\n            print(stdout, end='', flush=True)\n        return rc\n\n    if args.module:\n        tests = filter_tests_by_module(tests, args.module)\n        if not tests._tests:\n            if go_proc:\n                raise SystemExit(print_go())\n            raise SystemExit('No test module named %s found' % args.module)\n\n    if args.name:\n        tests = filter_tests_by_name(tests, *args.name)\n        if not tests._tests and not go_proc:\n            raise SystemExit('No test named %s found' % args.name)\n    if tests._tests:\n        python_tests_ok = run_cli(tests, args.verbosity)\n    else:\n        python_tests_ok = True\n    exit_code = 0 if python_tests_ok else 1\n    if go_proc:\n        print_go()\n        if exit_code == 0:\n            exit_code = go_proc.returncode\n    if exit_code != 0:\n        print(\"\\x1b[31mError\\x1b[39m: Some tests failed!\")\n    raise SystemExit(exit_code)\n\n\ndef run_tests(report_env: bool = False) -> None:\n    report_env = report_env or BaseTest.is_ci\n    import argparse\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        'name',\n        nargs='*',\n        default=[],\n        help='The name of the test to run, for e.g. linebuf corresponds to test_linebuf. Can be specified multiple times.'\n        ' For go tests Something corresponds to TestSometing.',\n    )\n    parser.add_argument('--verbosity', default=4, type=int, help='Test verbosity')\n    parser.add_argument(\n        '--module',\n        default='',\n        help='Name of a test module to restrict to. For example: ssh.' ' For Go tests this is the name of a package, for example: tools/cli',\n    )\n    args = parser.parse_args()\n    if args.name and args.name[0] in ('type-check', 'type_check', 'mypy'):\n        type_check()\n    go_pkgs = reduce_go_pkgs(args.module, args.name)\n    os.environ['ASAN_OPTIONS'] = 'detect_leaks=0'  # ensure subprocesses dont fail because of leak detection\n    if go_pkgs:\n        go_proc: 'Optional[GoProc]' = run_go(go_pkgs, args.name)\n    else:\n        go_proc = None\n    with env_for_python_tests(report_env):\n        if go_pkgs:\n            if report_env:\n                print('Go executable:', go_exe())\n            print('Go packages being tested:', ' '.join(go_pkgs))\n        sys.stdout.flush()\n        run_python_tests(args, go_proc)\n\n\n@contextmanager\ndef env_vars(**kw: str) -> Iterator[None]:\n    originals = {k: os.environ.get(k) for k in kw}\n    os.environ.update(kw)\n    try:\n        yield\n    finally:\n        for k, v in originals.items():\n            if v is None:\n                os.environ.pop(k, None)\n            else:\n                os.environ[k] = v\n\n\n@contextmanager\ndef env_for_python_tests(report_env: bool = False) -> Iterator[None]:\n    gohome = os.path.expanduser('~/go')\n    current_home = os.path.expanduser('~') + os.sep\n    paths = os.environ.get('PATH', '/usr/local/sbin:/usr/local/bin:/usr/bin').split(os.pathsep)\n    path = os.pathsep.join(x for x in paths if not x.startswith(current_home))\n    launcher_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty', 'launcher')\n    path = f'{launcher_dir}{os.pathsep}{path}'\n    python_for_type_check()\n    print('Running under CI:', BaseTest.is_ci)\n    if report_env:\n        print('Using PATH in test environment:', path)\n        print('Python:', python_for_type_check())\n        from kitty.fast_data_types import has_avx2, has_sse4_2\n        print(f'Intrinsics: {has_avx2=} {has_sse4_2=}')\n    # we need fonts installed in the user home directory as well, so initialize\n    # fontconfig before nuking $HOME and friends\n    from kitty.fonts.common import all_fonts_map\n    all_fonts_map(True)\n\n    with TemporaryDirectory() as tdir, env_vars(\n        HOME=tdir,\n        KT_ORIGINAL_HOME=os.path.expanduser('~'),\n        USERPROFILE=tdir,\n        PATH=path,\n        TERM='xterm-kitty',\n        XDG_CONFIG_HOME=os.path.join(tdir, '.config'),\n        XDG_CONFIG_DIRS=os.path.join(tdir, '.config'),\n        XDG_DATA_DIRS=os.path.join(tdir, '.local', 'xdg'),\n        XDG_CACHE_HOME=os.path.join(tdir, '.cache'),\n        XDG_RUNTIME_DIR=os.path.join(tdir, '.cache', 'run'),\n        PYTHONWARNINGS='error',\n    ):\n        if os.path.isdir(gohome):\n            os.symlink(gohome, os.path.join(tdir, os.path.basename(gohome)))\n        yield\n\n\ndef main() -> None:\n    import warnings\n\n    warnings.simplefilter('error')\n    run_tests()\n"
  },
  {
    "path": "kitty_tests/mouse.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom functools import partial\n\nfrom kitty.fast_data_types import (\n    GLFW_MOD_ALT,\n    GLFW_MOD_CONTROL,\n    GLFW_MOUSE_BUTTON_LEFT,\n    GLFW_MOUSE_BUTTON_RIGHT,\n    create_mock_window,\n    mock_mouse_selection,\n    send_mock_mouse_event_to_window,\n)\n\nfrom . import BaseTest\n\n\ndef send_mouse_event(\n    window,\n    button=-1,\n    modifiers=0,\n    is_release=False,\n    x=0.0,\n    y=0,\n    clear_click_queue=False,\n):\n    ix = int(x)\n    in_left_half_of_cell = x - ix < 0.5\n    send_mock_mouse_event_to_window(\n        window, button, modifiers, is_release, ix, y, clear_click_queue, in_left_half_of_cell\n    )\n\n\nclass TestMouse(BaseTest):\n\n    def test_mouse_selection(self):\n        s = self.create_screen(\n            options=dict(\n                rectangle_select_modifiers=GLFW_MOD_ALT | GLFW_MOD_CONTROL\n            )\n        )\n        w = create_mock_window(s)\n        ev = partial(send_mouse_event, w)\n\n        def mouse_selection(code: int) -> None:\n            mock_mouse_selection(w, s.callbacks.current_mouse_button, code)\n\n        s.callbacks.mouse_selection = mouse_selection\n\n        def sel():\n            return ''.join(s.text_for_selection())\n\n        def init():\n            s.reset()\n            s.draw('pqrst')\n            s.draw('uvwxy')\n            s.draw('ABCDE')\n            s.draw('FGHIJ')\n            s.draw('KLMNO')\n            s.draw('12345')\n            s.draw('67890')\n            s.draw('abcde')\n            s.draw('fghij')\n            s.draw('klmno')\n\n        def press(x=0, y=0, modifiers=0, button=GLFW_MOUSE_BUTTON_LEFT):\n            ev(button, x=x, y=y, modifiers=modifiers)\n\n        def release(x=0, y=0, button=GLFW_MOUSE_BUTTON_LEFT):\n            ev(\n                button,\n                x=x,\n                y=y,\n                is_release=True,\n                clear_click_queue=True\n            )\n\n        def move(x=0, y=0, button=-1, q=None):\n            ev(x=x, y=y, button=button)\n            if q is not None:\n                sl = sel()\n                from kitty.window import as_text\n                self.ae(sl, q, f'{sl!r} != {q!r} after movement to x={x} y={y}. Screen contents: {as_text(s)!r}')\n\n        def multi_click(x=0, y=0, count=2):\n            clear_click_queue = True\n            while count > 0:\n                count -= 1\n                ev(GLFW_MOUSE_BUTTON_LEFT, x=x, y=y, clear_click_queue=clear_click_queue)\n                clear_click_queue = False\n\n        def scroll(x=0, y=0, up=True):\n            move(x=x, y=y, button=-2 if up else -3)\n\n        # Single line click, move, release test\n        init()\n        press()\n        move(x=3.6, q='1234')\n        release(x=3.6)\n        self.ae(sel(), '1234')\n        press(x=4), release(x=0.6)\n        self.ae(sel(), '234')\n\n        # multi line movement\n        init()\n        press(x=2, y=2)\n        move(x=2, y=1, q='890ab')\n        move(x=2.6, y=1, q='90ab')\n        move(y=1, q='67890ab')\n        move(x=4, y=1, q='0ab')\n        move(x=4.6, y=1, q='ab')\n        move(q='1234567890ab')\n        move(x=2, y=3, q='cdefg')\n        move(y=3, q='cde')\n        move(x=0.6, y=3, q='cdef')\n        move(x=2.6, y=3, q='cdefgh')\n        move(x=4.6, y=3, q='cdefghij')\n\n        # Single cell select\n        init()\n        press(), release(1)\n        self.ae(sel(), '1')\n        press(3), release(2)\n        self.ae(sel(), '3')\n\n        # Multi-line click release\n        init()\n        press(1, 1), release(3.6, 2)\n        self.ae(sel(), '7890abcd')\n        press(1.6, 1), release(3, 2)\n        self.ae(sel(), '890abc')\n        press(3.6, 4), release(2, 2)\n        self.ae(sel(), 'cdefghijklmn')\n        press(3, 4), release(2.6, 2)\n        self.ae(sel(), 'defghijklm')\n\n        # Word select with drag\n        s.reset()\n        s.draw('ab cd')\n        s.draw(' f gh')\n        s.draw(' stuv')\n        s.draw('X Y')\n        multi_click(x=1.4)\n        self.ae(sel(), 'ab')\n        move(2.6)\n        self.ae(sel(), 'ab ')\n        move(3.6)\n        self.ae(sel(), 'ab cd')\n        move(2.6)\n        self.ae(sel(), 'ab ')\n        release(3.6, 1)\n        self.ae(sel(), 'ab cd f gh')\n        multi_click(x=1, y=2)\n        self.ae(sel(), 'stuvX')\n        release()\n        multi_click(x=3.6)\n        self.ae(sel(), 'cd')\n        move(0.2)\n        release()\n        self.ae(sel(), 'ab cd')\n        multi_click(x=4.4)\n        self.ae(sel(), 'cd')\n        move(x=4.4, y=1)\n        self.ae(sel(), 'cd f gh')\n        move(x=4.4, y=0)\n        self.ae(sel(), 'cd')\n        release()\n        multi_click(x=4.4, y=1)\n        self.ae(sel(), 'gh')\n        move(x=4.4, y=0)\n        self.ae(sel(), 'cd f gh')\n        move(x=4.4, y=1)\n        self.ae(sel(), 'gh')\n        release()\n        multi_click(x=4.4)\n        self.ae(sel(), 'cd')\n        move()\n        self.ae(sel(), 'ab cd')\n        move(x=1, y=1)\n        self.ae(sel(), 'ab cd f')\n        move()\n        self.ae(sel(), 'ab cd')\n        release()\n        multi_click(x=1.4)\n        self.ae(sel(), 'ab')\n        move(x=4.4)\n        self.ae(sel(), 'ab cd')\n        move(x=4.4, y=1)\n        self.ae(sel(), 'ab cd f gh')\n        move(x=4.4)\n        self.ae(sel(), 'ab cd')\n\n        # Line select with drag\n        s.reset()\n        s.draw('1 2 3')\n        s.linefeed(), s.carriage_return()\n        s.draw('4 5 6')\n        s.linefeed(), s.carriage_return()\n        s.draw('7 8 9X')\n        multi_click(x=1, count=3)\n        self.ae(sel(), str(s.line(0)))\n        move(y=1)\n        self.ae(sel(), '1 2 3\\n4 5 6')\n        move(y=2)\n        self.ae(sel(), '1 2 3\\n4 5 6\\n7 8 9X')\n        move(y=1)\n        self.ae(sel(), '1 2 3\\n4 5 6')\n        move()\n        self.ae(sel(), str(s.line(0)))\n        release()\n        multi_click(y=1, count=3)\n        self.ae(sel(), '4 5 6')\n        move(y=0)\n        self.ae(sel(), '1 2 3\\n4 5 6')\n        move(y=1)\n        self.ae(sel(), '4 5 6')\n        move(y=2)\n        self.ae(sel(), '4 5 6\\n7 8 9X')\n        release()\n        s.reset()\n        s.draw(' 123')\n        s.linefeed(), s.carriage_return()\n        s.draw(' 456')\n        s.linefeed(), s.carriage_return()\n        multi_click(x=1, count=3)\n        self.ae(sel(), '123')\n        move(x=2, y=1)\n        self.ae(sel(), '123\\n 456')\n        release()\n        press(x=2, y=1, button=GLFW_MOUSE_BUTTON_RIGHT)\n        release(x=2, y=1, button=GLFW_MOUSE_BUTTON_RIGHT)\n        self.ae(sel(), '123\\n 456')\n        press(button=GLFW_MOUSE_BUTTON_RIGHT)\n        self.ae(sel(), ' 123\\n 456')\n        release(button=GLFW_MOUSE_BUTTON_RIGHT)\n\n        # line select for wrapped lines in scrollback\n        s.reset()\n        s.draw('ABCDE12345')\n        s.linefeed(), s.carriage_return()\n        s.draw(('X' * s.columns) * (s.lines-1))\n        multi_click(x=1, count=3)\n        self.ae(sel(), 'ABCDE12345')\n        s.reset()\n        s.draw('ABCDE12345')\n        s.linefeed(), s.carriage_return()\n        s.draw('678')\n        s.linefeed(), s.carriage_return()\n        s.draw(('X' * s.columns) * (s.lines-2))\n        multi_click(x=1, y=1, count=3)\n        self.ae(sel(), '678')\n        press(x=2, button=GLFW_MOUSE_BUTTON_RIGHT)\n        release(x=2, button=GLFW_MOUSE_BUTTON_RIGHT)\n        self.ae(sel(), 'ABCDE12345\\n678')\n\n        # Rectangle select\n        init()\n        press(x=1, y=1, modifiers=GLFW_MOD_ALT | GLFW_MOD_CONTROL)\n        move(x=3.6, y=3)\n        self.ae(sel(), '789bcdghi')\n        release(x=3, y=3)\n        self.ae(sel(), '78bcgh')\n        press(x=3.6, y=1, modifiers=GLFW_MOD_ALT | GLFW_MOD_CONTROL)\n        self.ae(sel(), '')\n        move(x=1, y=3)\n        self.ae(sel(), '789bcdghi')\n        release(x=1.6)\n        self.ae(sel(), '3489')\n\n        # scrolling\n        init()\n        press(x=1.6)\n        scroll(x=1)\n        self.ae(sel(), 'LMNO12')\n        scroll(x=1)\n        self.ae(sel(), 'GHIJKLMNO12')\n        scroll(x=1, up=False)\n        self.ae(sel(), 'LMNO12')\n        scroll(x=2.6, up=False)\n        self.ae(sel(), '3')\n        release()\n        # fractional scrolling\n        init()\n        s.fractional_scroll(-0.5)\n        press()\n        move(x=3.6, q='1234')\n        release(x=3.6)\n        self.ae(sel(), '1234')\n\n        # extending selections\n        init()\n        press()\n        move(x=3.6, q='1234')\n        release(x=3.6)\n        self.ae(sel(), '1234')\n        press(x=1, y=1, button=GLFW_MOUSE_BUTTON_RIGHT)\n        self.ae(sel(), '123456')\n        move(x=2, y=1, q='1234567')\n        release(x=3, y=1, button=GLFW_MOUSE_BUTTON_RIGHT)\n        self.ae(sel(), '12345678')\n        init()\n        press(y=2)\n        move(x=3.6, y=2, q='abcd')\n        press(x=3, y=0, button=GLFW_MOUSE_BUTTON_RIGHT)\n        self.ae(sel(), '4567890abcd')\n\n        # blank line select\n        s.reset()\n        s.draw('abcde')\n        s.linefeed(), s.carriage_return()\n        s.linefeed(), s.carriage_return()\n        s.draw('12345')\n        press(x=0, y=0)\n        move(x=2, y=2, q='abcde\\n\\n12')\n"
  },
  {
    "path": "kitty_tests/multicell.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom functools import partial\n\nfrom kitty.fast_data_types import EXTEND_CELL, TEXT_SIZE_CODE, test_ch_and_idx, wcswidth\n\nfrom . import BaseTest, parse_bytes\nfrom . import draw_multicell as multicell\n\n\nclass TestMulticell(BaseTest):\n\n    def test_multicell(self):\n        test_multicell(self)\n\n\ndef test_multicell(self: TestMulticell) -> None:\n    from kitty.tab_bar import as_rgb\n    from kitty.window import as_text\n\n    def as_ansi(add_history=False):\n        return as_text(s, as_ansi=True, add_history=add_history)\n\n    def ac(x_, y_, **assertions):  # assert cell\n        cell = s.cpu_cells(y_, x_)\n        msg = f'Assertion failed for cell at ({x_}, {y_})\\n{cell}\\n'\n        failures = []\n        def ae(key):\n            if key not in assertions:\n                return\n            if key in cell:\n                val = cell[key]\n            else:\n                mcd = cell['mcd']\n                if mcd is None:\n                    raise AssertionError(f'{msg}Unexpectedly not a multicell')\n                val = mcd[key]\n            if assertions[key] != val:\n                failures.append(f'{key}: (expected) {assertions[key]!r} != {val!r}')\n\n        self.ae(test_ch_and_idx(0), (0, 0, 0))\n        self.ae(test_ch_and_idx(1), (0, 1, 1))\n        self.ae(test_ch_and_idx(0x80000000), (1, 0, 0x80000000))\n        self.ae(test_ch_and_idx(0x80000001), (1, 1, 0x80000001))\n        self.ae(test_ch_and_idx((1, 0)), (1, 0, 0x80000000))\n        self.ae(test_ch_and_idx((1, 3)), (1, 3, 0x80000003))\n\n        ae('x')\n        ae('y')\n        ae('width')\n        ae('scale')\n        ae('subscale_n')\n        ae('subscale_d')\n        ae('vertical_align')\n        ae('horizontal_align')\n        ae('text')\n        ae('natural_width')\n        ae('next_char_was_wrapped')\n        if failures:\n            raise AssertionError(msg + '\\n' + '\\n'.join(failures))\n\n        if 'cursor' in assertions:\n            self.ae(assertions['cursor'], (s.cursor.x, s.cursor.y), msg)\n\n        if 'is_multicell' in assertions:\n            q = cell['mcd']\n            if assertions['is_multicell']:\n                if q is None:\n                    raise AssertionError(f'{msg}Unexpectedly not a multicell')\n            else:\n                if q is not None:\n                    raise AssertionError(f'{msg}Unexpectedly is a multicell')\n\n    def count_multicells(with_text=''):\n        ans = 0\n        for y in range(s.lines):\n            for x in range(s.columns):\n                c = s.cpu_cells(y, x)\n                if c['mcd'] is not None and (not with_text or c['text'] == with_text):\n                    ans += 1\n        return ans\n\n    def line_text(y):\n        def ct(c):\n            if c['text']:\n                return c['text']\n            if c['mcd']:\n                return '_'\n            return '\\0'\n        return ''.join(ct(c) for c in s.cpu_cells(y))\n\n    def assert_line(text, y=None):\n        if y is None:\n            y = s.cursor.y\n        self.ae(text, line_text(y))\n\n    def assert_cursor_at(x, y):\n        self.ae((s.cursor.x, s.cursor.y), (x, y))\n\n    s = self.create_screen(cols=8, lines=4)\n    s.draw('飛青進服三上')\n    s.resize(s.lines, 5)\n    self.ae('飛青', str(s.line(0)))\n\n    s = self.create_screen(cols=6, lines=6)\n\n    # Test basic multicell drawing\n    s.reset()\n    ac(0, 0, is_multicell=False)\n    multicell(s, 'a')\n    ac(0, 0, is_multicell=True, width=1, scale=1, subscale_n=0, x=0, y=0, text='a', natural_width=True, cursor=(1, 0))\n    ac(0, 1, is_multicell=False), ac(1, 0, is_multicell=False), ac(1, 1, is_multicell=False)\n    s.draw('莊')\n    ac(0, 0, is_multicell=True, width=1, scale=1, subscale_n=0, x=0, y=0, text='a', natural_width=True)\n    ac(1, 0, is_multicell=True, width=2, scale=1, subscale_n=0, x=0, y=0, text='莊', natural_width=True, cursor=(3, 0))\n    ac(2, 0, is_multicell=True, width=2, scale=1, subscale_n=0, x=1, y=0, text='', natural_width=True)\n    for x in range(s.columns):\n        ac(x, 1, is_multicell=False)\n    s.cursor.x = 0\n    multicell(s, 'a', width=2, scale=2, subscale_n=3)\n    ac(0, 0, is_multicell=True, width=2, scale=2, subscale_n=3, x=0, y=0, text='a', natural_width=False, cursor=(4, 0))\n    for x in range(1, 4):\n        ac(x, 0, is_multicell=True, width=2, scale=2, subscale_n=3, x=x, y=0, text='', natural_width=False)\n    for x in range(0, 4):\n        ac(x, 1, is_multicell=True, width=2, scale=2, subscale_n=3, x=x, y=1, text='', natural_width=False)\n    def comb(x, y):\n        s.reset()\n        multicell(s, 'a', scale=2)\n        s.cursor.x, s.cursor.y = x, y\n        s.draw('\\u0301')\n        assert_cursor_at(x, y)\n        ac(0, 0, text='a' if y else 'a\\u0301', is_multicell=True)\n    for y in range(2):\n        for x in range(1, 3):\n            comb(x, y)\n    comb(0, 1)\n    s.reset()\n    multicell(s, 'aüa', scale=2)\n    self.ae(s.cursor.x, 6)\n    s = self.create_screen(cols=7 * 7, lines=7)\n    multicell(s, 'a', scale=7, width=7)\n    for y in range(s.lines):\n        for x in range(s.columns):\n            ac(x, y, is_multicell=True, x=x, y=y)\n\n    # Test zero width roundtripping\n    for preserved in '\\xad\\u200b\\u2060':\n        s.reset()\n        multicell(s, f'|{preserved}|', scale=2)\n        assert_cursor_at(4, 0)\n        ac(0, 0, text=f'|{preserved}')\n\n    # Test wrapping\n    s = self.create_screen(cols=6, lines=6)\n    s.draw('x' * (s.columns - 1))\n    multicell(s, 'a', scale=2)\n    ac(s.columns - 1, 0, is_multicell=False, text='', next_char_was_wrapped=True)\n    s.reset()\n    multicell(s, 'a', scale=2)\n    s.draw('x' * s.columns)\n    ac(s.cursor.x-1, s.cursor.y, is_multicell=False, text='x', next_char_was_wrapped=False)\n    ac(0, 0, is_multicell=True, text='a')\n    ac(0, 1, is_multicell=True, text='', y=1)\n\n    # Test draw with cursor in a multicell\n    s.reset()\n    multicell(s, '12', scale=2)\n    s.draw('\\rx')\n    ac(0, 0, is_multicell=False, text='x')\n    ac(1, 0, is_multicell=False, text='')\n    ac(0, 1, is_multicell=False, text='')\n    ac(1, 1, is_multicell=False, text='')\n    ac(2, 0, is_multicell=True, text='2')\n    s.reset()\n    s.draw('莊')\n    s.cursor.x -= 1\n    s.draw('a'), ac(0, 0, is_multicell=False), ac(1, 0, is_multicell=False)\n    s.reset()\n    s.draw('莊')\n    s.cursor.x = 0\n    s.draw('a'), ac(0, 0, is_multicell=False), ac(1, 0, is_multicell=False)\n    s.reset()\n    multicell(s, 'a', width=2, scale=2, subscale_n=3, subscale_d=4)\n    s.cursor.x, s.cursor.y = 1, 1\n    s.draw('b')\n    self.ae(8, count_multicells())\n    assert_cursor_at(5, 1)\n    self.assertIn('b', str(s.linebuf))\n    s.reset()\n    s.cursor.x = 1\n    s.draw('莊')\n    s.cursor.x = 0\n    s.draw('莊')\n    ac(2, 0, is_multicell=False, text=' ')\n    s.reset()\n    multicell(s, 'a', scale=2)\n    s.cursor.x += 1\n    multicell(s, 'b', scale=2)\n    s.draw('莊')\n    assert_cursor_at(2, 2)\n    self.assertIn('莊', str(s.linebuf))\n    s.reset()\n    multicell(s, 'a', scale=2)\n    s.cursor.x += 1\n    multicell(s, 'b', scale=2)\n    assert_cursor_at(5, 0)\n    s.draw('\\u2716\\ufe0f')\n    assert_cursor_at(2, 2)\n    s.reset()\n    s.draw('莊')\n    s.cursor.x = 0\n    s.draw('b')\n    self.ae(str(s.line(0)), 'b')\n    s.reset()\n    s.draw('莊')\n    s.cursor.x = 1\n    s.draw('b')\n    self.ae(str(s.line(0)), ' b')\n\n    # Test multicell with cursor in a multicell\n    def big_a(x, y=0, spaces=False, skip=False):\n        s.reset()\n        s.cursor.x, s.cursor.y = 1, 1\n        multicell(s, 'a', scale=4)\n        ac(1, 1, x=0, y=0, text='a', scale=4, width=1)\n        s.cursor.x, s.cursor.y = x, y\n        multicell(s, 'b', scale=2)\n        if skip:\n            self.ae(20, count_multicells())\n            assert_cursor_at(2, 4)\n            self.assertIn('a', str(s.linebuf))\n        else:\n            ac(x, y, text='b')\n            self.ae(4, count_multicells())\n            for x_ in range(1, 5):\n                ac(x_, 4, text=' ' if spaces else '')\n    for y in (0, 1):\n        big_a(0, y), big_a(1, y), big_a(2, y, spaces=True)\n    big_a(2, 2, skip=True), big_a(5, 1, skip=True)\n\n    # Test multicell with combining and flag codepoints and default width\n    def seq(text, *expected):\n        s.reset()\n        multicell(s, text)\n        i = iter(expected)\n        for x in range(s.cursor.x):\n            cell = s.cpu_cells(0, x)\n            if cell['x'] == 0:\n                q = next(i)\n                ac(x, 0, text=q, width=wcswidth(q))\n    seq('ab', 'a', 'b')\n    flag = '\\U0001f1ee\\U0001f1f3'\n    seq(flag + 'CD', flag, 'C', 'D')\n    seq('àn̂X', 'à', 'n̂', 'X')\n    seq('\\U0001f1eea', '\\U0001f1ee', 'a')\n    del flag, seq\n\n    # Test insert chars with multicell (aka right shift)\n    s.reset()\n    s.draw('a')\n    s.cursor.x = 0\n    s.insert_characters(1)\n    assert_line('\\0a\\0\\0\\0\\0')\n    s.reset()\n    multicell(s, 'a', width=2)\n    s.cursor.x = 0\n    s.insert_characters(1)\n    assert_line('\\0a_\\0\\0\\0')\n    s.reset()\n    multicell(s, 'a', width=2)\n    s.cursor.x = 0\n    s.insert_characters(2)\n    assert_line('\\0\\0a_\\0\\0')\n    s.reset()\n    multicell(s, 'a', width=2)\n    s.cursor.x = 1\n    s.insert_characters(1)\n    assert_line('\\0\\0\\0\\0\\0\\0')\n    s.reset()\n    s.cursor.x = 3\n    multicell(s, 'a', width=2)\n    s.cursor.x = 0\n    s.insert_characters(1)\n    assert_line('\\0\\0\\0\\0a_')\n    s.reset()\n    s.cursor.x = s.columns - 2\n    multicell(s, 'a', width=2)\n    s.cursor.x = 0\n    s.insert_characters(1)\n    assert_line('\\0\\0\\0\\0\\0\\0')\n    # multiline\n    s.reset()\n    s.draw('a')\n    multicell(s, 'b', scale=2)\n    assert_line('ab_\\0\\0\\0')\n    assert_line('\\0__\\0\\0\\0', 1)\n    s.cursor.x, s.cursor.y = 0, 0\n    s.insert_characters(1)\n    assert_line('\\0a\\0\\0\\0\\0')\n    assert_line('\\0\\0\\0\\0\\0\\0', 1)\n    s.reset()\n    multicell(s, 'a', scale=2)\n    s.cursor.x = 3\n    s.insert_characters(1)\n    assert_line('a_\\0\\0\\0\\0')\n    assert_line('__\\0\\0\\0\\0', 1)\n\n    # Test delete chars with multicell (aka left shift)\n    s.reset()\n    s.draw(' 允许')\n    s.cursor.x = 0\n    s.delete_characters(1)\n    for x in range(4):\n        ac(x, 0, width=2)\n    s.reset()\n    multicell(s, 'a', width=2)\n    s.cursor.x = 0\n    s.delete_characters(1)\n    assert_line('\\0\\0\\0\\0\\0\\0')\n    s.reset()\n    multicell(s, 'a', width=2)\n    s.cursor.x = 1\n    s.delete_characters(1)\n    assert_line('\\0\\0\\0\\0\\0\\0')\n    s.reset()\n    s.draw('ab')\n    multicell(s, 'a', width=2)\n    s.cursor.x = 0\n    s.delete_characters(2)\n    assert_line('a_\\0\\0\\0\\0')\n    s.reset()\n    s.draw('a'), multicell(s, 'b', width=2), s.draw('c')\n    s.cursor.x = 0\n    s.delete_characters(1)\n    assert_line('b_c\\0\\0\\0')\n    s.reset()\n    s.draw('a'), multicell(s, 'b', width=2), s.draw('c')\n    s.cursor.x = 0\n    s.delete_characters(1)\n    assert_line('b_c\\0\\0\\0')\n    s.reset(), s.draw('a'), multicell(s, 'b', width=2), s.draw('c')\n    s.cursor.x = 0\n    s.delete_characters(2)\n    assert_line('\\0c\\0\\0\\0\\0')\n    s.reset(), s.draw('a'), multicell(s, 'b', width=2), s.draw('c')\n    s.cursor.x = 1\n    s.delete_characters(1)\n    assert_line('a\\0c\\0\\0\\0')\n    s.reset(), s.draw('a'), multicell(s, 'b', width=2), s.draw('c')\n    s.cursor.x = 2\n    s.delete_characters(1)\n    assert_line('a\\0c\\0\\0\\0')\n    # multiline\n    s.reset()\n    s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n    assert_line('ab_c\\0\\0')\n    assert_line('\\0__\\0\\0\\0', 1)\n    s.cursor.x, s.cursor.y = 0, 0\n    s.delete_characters(1)\n    assert_line('\\0\\0c\\0\\0\\0')\n    assert_line('\\0\\0\\0\\0\\0\\0', 1)\n    s.reset()\n    multicell(s, 'a', scale=2)\n    s.cursor.x = 3\n    s.delete_characters(1)\n    assert_line('a_\\0\\0\\0\\0')\n    assert_line('__\\0\\0\\0\\0', 1)\n\n    # Erase characters (aka replace with null)\n    s.reset()\n    s.cursor.x = 1\n    s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n    s.cursor.x = 0\n    s.erase_characters(1)\n    assert_line('\\0ab_c\\0')\n    s.erase_characters(2)\n    assert_line('\\0\\0b_c\\0')\n    assert_line('\\0\\0__\\0\\0', 1)\n    s.erase_characters(3)\n    assert_line('\\0\\0\\0\\0c\\0')\n    assert_line('\\0\\0\\0\\0\\0\\0', 1)\n\n    # Erase in line\n    for x in (1, 2):\n        s.reset()\n        s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n        s.cursor.x = x\n        s.erase_in_line(0)\n        assert_line('a\\0\\0\\0\\0\\0')\n        s.reset()\n        s.draw('a'), multicell(s, 'b', width=2), s.draw('c')\n        s.cursor.x = x\n        s.erase_in_line(1)\n        assert_line('\\0\\0\\0c\\0\\0')\n    s.reset()\n    s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n    s.erase_in_line(2)\n    for y in (0, 1):\n        assert_line('\\0\\0\\0\\0\\0\\0', y)\n    s.reset()\n    s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n    s.cursor.y = 1\n    s.erase_in_line(2)\n    assert_line('a\\0\\0c\\0\\0', 0)\n    assert_line('\\0\\0\\0\\0\\0\\0', 1)\n\n    # Clear scrollback\n    s.reset()\n    s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n    for i in range(s.lines):\n        s.index()\n    s.cursor.y = 0\n    assert_line('\\0__\\0\\0\\0')\n    s.clear_scrollback()\n    assert_line('\\0\\0\\0\\0\\0\\0')\n\n    # Erase in display\n    for x in (1, 2):\n        s.reset(), s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n        s.cursor.x = x\n        s.erase_in_display(0)\n        assert_line('a\\0\\0\\0\\0\\0')\n    s.reset(), s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n    s.cursor.x, s.cursor.y = 2, 1\n    s.erase_in_display(0)\n    assert_line('a\\0\\0c\\0\\0', 0)\n    assert_line('\\0\\0\\0\\0\\0\\0', 1)\n    s.reset(), s.draw('a'), multicell(s, 'b', scale=2), s.draw('c')\n    for i in range(s.lines):\n        s.index()\n    s.erase_in_display(22)\n    assert_line('ab_c\\0\\0', -2)\n    assert_line('\\0__\\0\\0\\0', -1)\n    self.ae(s.historybuf.line(1).as_ansi(), f'a\\x1b]{TEXT_SIZE_CODE};s=2;b\\x07c')\n    self.ae(s.historybuf.line(0).as_ansi(), ' ')\n\n    # Insert lines\n    s.reset()\n    multicell(s, 'a', scale=2)\n    s.cursor.x, s.cursor.y = 0, s.lines - 2\n    multicell(s, 'b', scale=2)\n    s.cursor.x, s.cursor.y = 0, 1\n    s.insert_lines(1)\n    for y in range(s.lines):\n        assert_line('\\0' * s.columns, y)\n    s.reset()\n    multicell(s, 'a', scale=2)\n    s.insert_lines(2)\n    assert_line('\\0' * s.columns, 0)\n    assert_line('a_\\0\\0\\0\\0', 2)\n\n    # Delete lines\n    s.reset()\n    multicell(s, 'a', scale=2)\n    s.cursor.y = 1\n    multicell(s, 'b', scale=2)\n    s.delete_lines(1)\n    for y in range(s.lines):\n        assert_line('\\0' * s.columns, y)\n\n    # ansi output\n    def ta(expected):\n        actual = as_ansi().rstrip()[3:]\n        self.ae(expected, actual)\n        s.reset()\n        parse_bytes(s, actual.encode())\n        actual2 = as_ansi().rstrip()[3:]\n        self.ae(expected, actual2)\n        s.reset()\n\n    s.reset()\n    multicell(s, 'a', width=2, scale=3, subscale_n=1, subscale_d=2, vertical_align=3, horizontal_align=3)\n    ta(f'\\x1b]{TEXT_SIZE_CODE};w=2:s=3:n=1:d=2:v=3:h=3;a\\x07')\n    s.draw('a')\n    multicell(s, 'b', width=2)\n    s.draw('c')\n    ta(f'a\\x1b]{TEXT_SIZE_CODE};w=2;b\\x07c')\n    multicell(s, 'a')\n    s.cursor.fg = as_rgb(0xffffff)\n    multicell(s, 'b')\n    ta('a\\x1b[38:2:255:255:255mb')\n    multicell(s, 'a', scale=2)\n    multicell(s, 'b', scale=2)\n    ta(f'\\x1b]{TEXT_SIZE_CODE};s=2;ab\\x07')\n    multicell(s, '😀a', scale=2)\n    ta(f'\\x1b]{TEXT_SIZE_CODE};s=2;😀a\\x07')\n    multicell(s, '😀', scale=2)\n    multicell(s, 'b', width=1, scale=2)\n    ta(f'\\x1b]{TEXT_SIZE_CODE};s=2;😀\\x07\\x1b]{TEXT_SIZE_CODE};w=1:s=2;b\\x07')\n    multicell(s, 'a', scale=2)\n    s.cursor.fg = as_rgb(0xffffff)\n    multicell(s, 'b', scale=2)\n    ta(f'\\x1b]{TEXT_SIZE_CODE};s=2;a\\x07\\x1b[38:2:255:255:255m\\x1b]{TEXT_SIZE_CODE};s=2;b\\x07\\n\\x1b[m\\x1b[38:2:255:255:255m')\n    multicell(s, 'a', scale=3)\n    multicell(s, 'b', scale=2)\n    ta(f'\\x1b]{TEXT_SIZE_CODE};s=3;a\\x07\\x1b]{TEXT_SIZE_CODE};s=2;b\\x07')\n\n    # rewrap with multicells\n    s = self.create_screen(cols=6, lines=6, scrollback=20)\n    o = s.lines, s.columns\n    def reset():\n        s.resize(*o)\n        s.reset()\n        s.clear_scrollback()\n\n    def mc(x=None, y=None):\n        if x is not None:\n            s.cursor.x = x\n        if y is not None:\n            s.cursor.y = y\n\n    reset()\n    multicell(s, 'a', scale=2)\n    before = as_ansi()\n    s.resize(s.lines + 1, s.columns)\n    self.ae(before.rstrip(), as_ansi().rstrip())\n\n    reset()\n    s.draw('a' * (s.columns - 2) + '😛' + 'bb')\n    mc(4, 0)\n    s.resize(s.lines, s.columns-1)\n    self.ae('\\x1b[maaaa\\x1b[m😛bb', as_ansi().rstrip())\n    assert_cursor_at(0, 1)\n    reset()\n    s.draw('a' * (s.columns - 2) + '😛' + 'bb')\n    mc(0, 1)\n    s.resize(s.lines, s.columns-2)\n    assert_cursor_at(2, 1)\n    self.ae('\\x1b[maaaa\\x1b[m😛bb', as_ansi().rstrip())\n    reset()\n    s.draw('a' * (s.columns - 2) + '😛' + 'bb')\n    mc(5, 0)\n    s.resize(s.lines, s.columns-3)\n    self.ae('\\x1b[maaa\\x1b[ma😛\\x1b[mbb', as_ansi().rstrip()) # ]]]]]]]\n    assert_cursor_at(2, 1)\n\n    def resize(lines, cols, cursorx=None, cursory=None):\n        mc(cursorx, cursory)\n        before = s.cursor.x, s.cursor.y\n        cell = s.cpu_cells(s.cursor.y, s.cursor.x)\n        cell.pop('next_char_was_wrapped')\n        s.resize(lines, cols)\n        ncell = s.cpu_cells(s.cursor.y, s.cursor.x)\n        ncell.pop('next_char_was_wrapped')\n        self.ae(cell, ncell, f'Cursor moved from: {before} to {(s.cursor.x, s.cursor.y)}')\n\n    reset()\n    multicell(s, 'a', scale=3), s.draw('b'*(s.columns-3))\n    resize(s.lines, s.columns-1, 5, 0)\n    self.ae(f'\\x1b[m\\x1b]{TEXT_SIZE_CODE};s=3;a\\x07bb\\x1b[mb', as_ansi().rstrip())  # ]]\n    ac(0, 0, is_multicell=True)\n    ac(0, 1, is_multicell=True)\n    ac(3, 1, is_multicell=False, text='b')\n    reset()\n    s.draw('X'), multicell(s, 'a', scale=3), s.draw('12345')\n    resize(s.lines, s.columns-1, 4, 0)\n    self.ae(f'\\x1b[mX\\x1b]{TEXT_SIZE_CODE};s=3;a\\x071\\x1b[m23\\x1b[m45', as_ansi().rstrip())  # ]]\n    for y in (0, 1):\n        ac(0, y, is_multicell=False), ac(1, y, is_multicell=True), ac(3, y, is_multicell=True)\n    ac(0, 1, is_multicell=False, text='2'), ac(4, 1, is_multicell=False, text='3')\n\n    reset()\n    s.draw('a'*(s.columns - 2)), s.draw('😛'), s.linefeed(), s.carriage_return(), s.draw('123')\n    resize(s.lines, s.columns-1, 5, 0)\n    self.ae('\\x1b[maaaa\\x1b[m😛\\n\\x1b[m123', as_ansi().rstrip()) # ]]]]]]]\n\n    reset()\n    s.draw('a'*(s.columns - 1)), s.draw('😛'), s.draw('bcd')\n    resize(s.lines, s.columns + 1, 0, 1)\n    self.ae('\\x1b[maaaaa😛\\x1b[mbcd', as_ansi().rstrip()) # ]]]]]]]\n\n    reset()\n    s.draw('a'*s.columns), s.draw('😛'), s.draw('bcd')\n    resize(s.lines, s.columns + 1, 0, 1)\n    self.ae('\\x1b[maaaaaa\\x1b[m😛bcd', as_ansi().rstrip()) # ]]]]]]]\n    ac(s.columns-1, 0, next_char_was_wrapped=True)\n    s.resize(s.lines, s.columns + 1)\n    self.ae('\\x1b[maaaaaa😛\\x1b[mbcd', as_ansi().rstrip()) # ]]]]]]]\n\n    reset()\n    s.draw('a'*(s.columns - 1)), multicell(s, 'X', scale=2), s.draw('bcd')\n    resize(s.lines, s.columns + 1, 0, 2)\n    self.ae(f'\\x1b[maaaaa\\x1b]{TEXT_SIZE_CODE};s=2;X\\x07\\x1b[mbcd', as_ansi().rstrip()) # ]]]]]]]\n    for y in (0, 1):\n        for x in (1, 2):\n            ac(s.columns-x, y, is_multicell=True)\n        for x in (0, 1):\n            ac(x, y, is_multicell=False)\n    reset()\n    s.draw('a'*(s.columns - 1)), multicell(s, 'X', scale=2), s.draw('bcd1234!')\n    s.resize(s.lines, s.columns + 2)\n    self.ae(f'\\x1b[maaaaa\\x1b]{TEXT_SIZE_CODE};s=2;X\\x07b\\x1b[mcd1234\\x1b[m!', as_ansi().rstrip()) # ]]]]]]]\n    for y in (0, 1):\n        for x in (1, 2):\n            ac(s.columns-x -1, y, is_multicell=True)\n        for x in (0, 1):\n            ac(x, y, is_multicell=False)\n\n    reset()\n    multicell(s, 'X', scale=4), s.draw('abc')\n    resize(3, 3, 5, 0)\n    self.ae('\\x1b[mabc', as_ansi().rstrip()) # ]]]]]]]\n    reset()\n    multicell(s, 'X', width=4), s.draw('abc')\n    resize(3, 3, 4, 0)\n    self.ae('\\x1b[mabc', as_ansi().rstrip()) # ]]]]]]]\n    reset()\n    s.draw('1'), multicell(s, 'X', width=4), s.draw('abc')\n    resize(3, 3, 5, 0)\n    self.ae('\\x1b[m1ab\\x1b[mc', as_ansi().rstrip()) # ]]]]]]]\n\n    reset()\n    suffix = '112233445555556666667'\n    multicell(s, 'X', scale=4), s.draw(suffix)\n    self.ae(str(s.historybuf), 'X11')  # X is split between the buffers\n    resize(6, s.columns+1, 0, 5)\n    self.ae(str(s.historybuf), 'X112')\n    self.ae(str(s.linebuf.line(0)), '233')\n    for y in range(3):\n        for x in range(4):\n            ac(x, y, is_multicell=True, x=x, y=y+1)\n    reset()\n    multicell(s, 'X', scale=4), s.draw(suffix)\n    resize(6, s.columns-1, 0, 5)\n    self.ae(f'X{suffix}', as_text(s, add_history=True))\n    self.ae(str(s.historybuf), '1\\nX1')\n    self.ae(str(s.linebuf.line(0)), '2')\n    for y in range(-2, 2):\n        for x in range(4):\n            ac(x, y, is_multicell=True, x=x, y=y+2, text='X' if (x, y) == (0, -2) else '')\n\n    reset()\n    multicell(s, 'AB', scale=2), s.draw('11223333334444445555556666667')\n    self.ae(str(s.historybuf), 'AB11')  # AB is split between the buffers\n    resize(6, s.columns+1, 0, 5)\n    self.ae(str(s.historybuf), 'AB112')\n    self.ae(str(s.linebuf.line(0)), '233')\n    for x in range(2):\n        ac(x, -1, is_multicell=True, x=x, y=0, text='' if x else 'A')\n        ac(x, 0, is_multicell=True, x=x, y=1, text='')\n    for x in range(2, 4):\n        ac(x, -1, is_multicell=True, x=x-2, y=0, text='' if x > 2 else 'B')\n        ac(x, 0, is_multicell=True, x=x-2, y=1, text='')\n\n    # resize without rewrap (alt screen)\n    s = self.create_screen(lines=6, cols=6)\n    s.toggle_alt_screen()\n\n    def reset_alt():\n        s.reset()\n        s.resize(6, 6)\n        s.toggle_alt_screen()\n    multicell(s, 'XY', scale=3)\n    s.resize(6, 5)\n    for y in range(3):\n        for x in range(3):\n            ac(x, y, is_multicell=True, text='X' if x == 0 and y == 0 else '')\n        for x in range(3, 5):\n            ac(x, y, is_multicell=False, text='')\n    reset_alt()\n    multicell(s, 'XY', scale=3)\n    s.resize(2, 6)\n    for y in range(s.lines):\n        for x in range(s.columns):\n            ac(x, y, is_multicell=False, text='')\n\n    # selections\n    s = self.create_screen(lines=5, cols=8)\n\n    def p(x=0, y=0, in_left_half_of_cell=True):\n        return (x, y, in_left_half_of_cell)\n\n    def ss(start, end, rectangle_select=False, extend_mode=EXTEND_CELL):\n        s.start_selection(start[0], start[1], rectangle_select, extend_mode, start[2])\n        s.update_selection(end[0], end[1], end[2])\n\n    def asl(*ranges, bp=1):\n        actual = s.current_selections()\n        def as_lists(x):\n            a = []\n            for y in range(s.lines):\n                a.append(x[y*s.columns: (y+1)*s.columns ])\n            return a\n\n        expected = bytearray(s.lines * s.columns)\n        for (y, x1, x2) in ranges:\n            pos = y * s.columns\n            for x in range(x1, x2 + 1):\n                expected[pos + x] = bp\n        for i, (e, a) in enumerate(zip(as_lists(bytes(expected)), as_lists(actual)[1:])):\n            self.ae(e, a, f'Row: {i}')\n\n    def ast(*expected, strip_trailing_whitespace=False, as_ansi=False):\n        actual = s.text_for_selection(as_ansi, strip_trailing_whitespace)\n        self.ae(expected, actual)\n\n    def asa(*expected, strip_trailing_whitespace=False):\n        ast(*expected, as_ansi=True, strip_trailing_whitespace=strip_trailing_whitespace)\n\n    s.reset()\n    s.draw('a'), multicell(s, 'b', width=2), s.draw('c')\n    ss(p(), p(x=1, in_left_half_of_cell=False))\n    asl((0, 0, 2))\n    ast('ab')\n    asa(f'a\\x1b]{TEXT_SIZE_CODE};w=2;b\\x07', '\\x1b[m')\n    ss(p(x=2), p(x=3, in_left_half_of_cell=False))\n    asl((0, 1, 3))\n    ast('bc')\n    asa(f'\\x1b]{TEXT_SIZE_CODE};w=2;b\\x07c', '\\x1b[m')\n\n    s.reset()\n    s.draw('a'), multicell(s, 'b', scale=2), s.draw('c'), multicell(s, 'd', scale=2)\n    ss(p(), p(x=4, in_left_half_of_cell=False))\n    asl((0, 0, 5), (1, 1, 2), (1, 4, 5))\n    ast('abcd')\n    asa(f'a\\x1b]{TEXT_SIZE_CODE};s=2;b\\x07c\\x1b]{TEXT_SIZE_CODE};s=2;d\\x07', '\\x1b[m')\n    ss(p(y=1, x=1), p(y=1, x=1, in_left_half_of_cell=False))\n    asl((0, 1, 2), (1, 1, 2))\n    ast('b')\n    asa(f'\\x1b]{TEXT_SIZE_CODE};s=2;b\\x07', '\\x1b[m')\n    ss(p(y=1, x=0), p(y=1, x=1, in_left_half_of_cell=False))  # empty leading cell before multiline on y=1\n    asl((0, 1, 2), (1, 0, 2))\n    ast('b')\n    asa(f'\\x1b]{TEXT_SIZE_CODE};s=2;b\\x07', '\\x1b[m')\n\n    s.reset()\n    multicell(s, 'X', scale=2), s.draw('123456abcd')\n    for x in (0, 1, 2):\n        ss(p(x=x), p(x=3, y=1, in_left_half_of_cell=False))\n        asl((0, 0, 7), (1, 0, 3))\n        ast('X123456', 'ab')\n        asa(f'\\x1b]{TEXT_SIZE_CODE};s=2;X\\x07123456', 'ab', '\\x1b[m')\n    ss(p(y=1), p(y=1, x=3, in_left_half_of_cell=False))\n    asl((0, 0, 1), (1, 0, 3))\n    ast('X', 'ab')\n    asa(f'\\x1b]{TEXT_SIZE_CODE};s=2;X\\x07', 'ab', '\\x1b[m')\n\n    s = self.create_screen(lines=5, cols=24)\n\n    s.reset()\n    multicell(s, 'ab cd ef', scale=2)\n    ss(p(6, 1), p(9, 0, in_left_half_of_cell=False))\n    ast('cd')\n    asa(f'\\x1b]{TEXT_SIZE_CODE};s=2;cd\\x07', '\\x1b[m')\n\n    s.reset()\n    multicell(s, 'ab', scale=2), s.draw('  '), multicell(s, 'cd', scale=2), s.draw('  '), multicell(s, 'ef', scale=2)\n    ss(p(6, 1), p(9, 0, in_left_half_of_cell=False))\n    ast('cd')\n\n    # Hyperlinks\n    s = self.create_screen(lines=5, cols=8)\n    asu = partial(asl, bp=2)\n    def set_link(url=None, id=None):\n        parse_bytes(s, '\\x1b]8;id={};{}\\x1b\\\\'.format(id or '', url or '').encode('utf-8'))\n\n    s.reset()\n    set_link('url-a', 'a')\n    multicell(s, 'ab', scale=2)\n    for y in (0, 1):\n        self.ae(s.line(y).hyperlink_ids(), (1, 1, 1, 1, 0, 0, 0, 0))\n    for y in (0, 1):\n        for x in (0, 3):\n            self.ae('url-a', s.hyperlink_at(x, y))\n    asu((0, 0, 3), (1, 0, 3))\n    self.ae(s.current_url_text(), 'ab')\n\n    # URL detection\n    s = self.create_screen(cols=60)\n\n    s.reset()\n    url = 'http://moo.com'\n    multicell(s, url, scale=2)\n    s.detect_url(0, 0)\n    self.ae(s.current_url_text(), url)\n    asu((0, 0, len(url)*2 - 1), (1, 0, len(url)*2 - 1))\n    # More tests for URL detection are in screen.py in detect_url()\n"
  },
  {
    "path": "kitty_tests/notifications.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport re\nimport tempfile\nfrom base64 import standard_b64encode\n\nfrom kitty.notifications import Channel, DesktopIntegration, IconDataCache, NotificationManager, UIState, Urgency\n\nfrom . import BaseTest\n\n\ndef n(\n    title='title', body='', urgency=Urgency.Normal, desktop_notification_id=1, icon_names=(), icon_path='',\n    application_name='', notification_types=(), timeout=-1, sound='system',\n):\n    return {\n        'title': title, 'body': body, 'urgency': urgency, 'id': desktop_notification_id, 'icon_names': icon_names, 'icon_path': icon_path,\n        'application_name': application_name, 'notification_types': notification_types, 'timeout': timeout, 'sound': sound,\n    }\n\n\nclass DesktopIntegration(DesktopIntegration):\n    def initialize(self):\n        self.reset()\n\n    def reset(self):\n        self.notifications = []\n        self.close_events = []\n        self.new_version_activated = False\n        self.close_succeeds = True\n        self.counter = 0\n\n    def query_live_notifications(self, channel_id, client_id):\n        ids = [n['id'] for n in self.notifications]\n        self.notification_manager.send_live_response(channel_id, client_id, tuple(ids))\n\n    def on_new_version_notification_activation(self, cmd, which) -> None:\n        self.new_version_activated = which + 1\n\n    def close_notification(self, desktop_notification_id: int) -> bool:\n        self.close_events.append(desktop_notification_id)\n        if self.close_succeeds:\n            self.notification_manager.notification_closed(desktop_notification_id)\n        return self.close_succeeds\n\n    def notify(self, cmd, existing_desktop_notification_id) -> int:\n        if existing_desktop_notification_id:\n            did = existing_desktop_notification_id\n        else:\n            self.counter += 1\n            did = self.counter\n        title, body, urgency = cmd.title, cmd.body, (Urgency.Normal if cmd.urgency is None else cmd.urgency)\n        ans = n(title, body, urgency, did, cmd.icon_names, os.path.basename(cmd.icon_path), cmd.application_name,\n                cmd.notification_types, timeout=cmd.timeout, sound=cmd.sound_name)\n        self.notifications.append(ans)\n        return self.counter\n\n\nclass Channel(Channel):\n\n    focused = visible = True\n\n    def __init__(self, *a):\n        super().__init__(*a)\n        self.reset()\n\n    def reset(self):\n        self.responses = []\n        self.focus_events = []\n\n    def ui_state(self, channel_id):\n        return UIState(self.focused, self.visible)\n\n    def focus(self, channel_id: int, activation_token: str) -> None:\n        self.focus_events.append(activation_token)\n\n    def send(self, channel_id: int, osc_escape_code: str) -> bool:\n        self.responses.append(osc_escape_code)\n\n\nclass NotificationManager(NotificationManager):\n\n    @property\n    def filter_rules(self):\n        yield from ('title:filterme',)\n\n\ndef do_test(self: 'TestNotifications', tdir: str) -> None:\n    di = DesktopIntegration(None)\n    ch = Channel()\n    nm = NotificationManager(di, ch, lambda *a, **kw: None, base_cache_dir=tdir)\n    di.notification_manager = nm\n\n    def reset():\n        di.reset()\n        ch.reset()\n        nm.reset()\n\n    def h(raw_data, osc_code=99, channel_id=1):\n        nm.handle_notification_cmd(channel_id, osc_code, raw_data)\n\n    def activate(which=0, button=0):\n        n = di.notifications[which]\n        nm.notification_activated(n['id'], button)\n\n    def close(which=0):\n        n = di.notifications[which]\n        di.close_notification(n['id'])\n\n    def assert_events(focus=True, close=0, report='', close_response='', live=''):\n        self.ae(ch.focus_events, [''] if focus else [])\n        if report:\n            self.assertIn(f'99;i={report};', ch.responses)\n        else:\n            for r in ch.responses:\n                m = re.match(r'99;i=[a-z0-9]+;', r)\n                self.assertIsNone(m, f'Unexpectedly found report response: {r}')\n        if close_response:\n            self.assertIn(f'99;i={close_response}:p=close;', ch.responses)\n        else:\n            for r in ch.responses:\n                m = re.match(r'99;i=[a-z0-9]+:p=close;', r)\n                self.assertIsNone(m, f'Unexpectedly found close response: {r}')\n        if live:\n            self.assertIn(f'99;i=live:p=alive;{live}', ch.responses)\n        else:\n            for r in ch.responses:\n                m = re.match(r'99;i=[a-z0-9]+:p=alive;', r)\n                self.assertIsNone(m, f'Unexpectedly found alive response: {r}')\n        self.ae(di.close_events, [close] if close else [])\n\n    h('test it', osc_code=9)\n    self.ae(di.notifications, [n(title='test it')])\n    activate()\n    assert_events()\n    reset()\n\n    h('d=0:u=2:i=x;title')\n    h('d=1:i=x:p=body;body')\n    self.ae(di.notifications, [n(body='body', urgency=Urgency.Critical)])\n    activate()\n    assert_events()\n    reset()\n\n    h('i=x:p=body:a=-focus;body')\n    self.ae(di.notifications, [n(title='body')])\n    activate()\n    assert_events(focus=False)\n    reset()\n\n    nm.send_new_version_notification('moose')\n    self.ae(di.notifications, [n('kitty update available!', 'kitty version moose released')])\n    activate()\n    self.assertTrue(di.new_version_activated)\n    reset()\n\n    h('i=x:e=1;' + standard_b64encode(b'title').decode('ascii'))\n    self.ae(di.notifications, [n()])\n    activate()\n    assert_events()\n    reset()\n\n    h('e=1;' + standard_b64encode(b'title').decode('ascii'))\n    self.ae(di.notifications, [n()])\n    activate()\n    assert_events()\n    reset()\n\n    h('d=0:i=x:a=-report;title')\n    h('d=1:i=x:a=report;body')\n    self.ae(di.notifications, [n(title='titlebody')])\n    activate()\n    assert_events(report='x')\n    reset()\n\n    h('a=report;title')\n    self.ae(di.notifications, [n()])\n    activate()\n    assert_events(report='0')\n    reset()\n\n    h('d=0:i=y;title')\n    h('d=1:i=y:p=xxx;title')\n    self.ae(di.notifications, [n()])\n    reset()\n\n    # test filtering\n    h(';title')\n    h(';filterme please')\n    self.ae(di.notifications, [n()])\n    reset()\n\n    # test closing interactions with reporting and activation\n    h('i=c;title')\n    self.ae(di.notifications, [n()])\n    close()\n    assert_events(focus=False, close=True)\n    reset()\n    h('i=c:c=1;title')\n    self.ae(di.notifications, [n()])\n    h('i=c:p=close')\n    self.ae(di.notifications, [n()])\n    assert_events(focus=False, close=True, close_response='c')\n    reset()\n    h('i=c:c=1;title')\n    h('i=c:p=close')\n    self.ae(di.notifications, [n()])\n    assert_events(focus=False, close=True, close_response='c')\n    reset()\n    h('i=c;title')\n    activate()\n    close()\n    h('i=c:p=close')\n    self.ae(di.notifications, [n()])\n    assert_events(focus=True, close=True)\n    reset()\n    h('i=c:a=report:c=1;title')\n    activate()\n    h('i=c:p=close')\n    self.ae(di.notifications, [n()])\n    assert_events(focus=True, report='c', close=True, close_response='c')\n    reset()\n\n    h('i=a[;title')\n    h('i=b;title')\n    h('i=live:p=alive;')\n    assert_events(focus=False, live='a,b')\n    reset()\n\n    h(';title')\n    self.ae(di.notifications, [n()])\n    activate()\n    assert_events()\n    reset()\n\n    # test sounds\n    def enc(x):\n      return standard_b64encode(x.encode()).decode()\n\n    h(f's={enc(\"silent\")};title')\n    self.ae(di.notifications, [n(sound='silent')])\n    h(f's={enc(\"custom\")};title')\n    self.ae(di.notifications[-1], n(desktop_notification_id=2, sound='custom'))\n    reset()\n\n    # Test querying\n    h('i=xyz:p=?')\n    self.assertFalse(di.notifications)\n    qr = 'a=focus,report:o=always,unfocused,invisible:u=0,1,2:p=title,body,?,close,icon,alive,buttons:c=1:w=1:s=system,silent,error,info,question,warn,warning'\n    self.ae(ch.responses, [f'99;i=xyz:p=?;{qr}'])\n    reset()\n    h('p=?')\n    self.assertFalse(di.notifications)\n    self.ae(ch.responses, [f'99;i=0:p=?;{qr}'])\n\n    # Test MIME streaming\n    for padding in (True, False):\n        for extra in ('a', 'ab', 'abc', 'abcd'):\n            text = 'some reasonably long text to test MIME streaming with: '\n            encoded = standard_b64encode(text.encode()).decode()\n            if not padding:\n                encoded = encoded.rstrip('=')\n            for t in encoded:\n                h(f'i=s:e=1:d=0;{t}')\n            h(f'i=s:e=1:d=0:p=body;{encoded[:13]}')\n            h(f'i=s:e=1:d=0:p=body;{encoded[13:]}')\n            h('i=s')\n            self.ae(di.notifications, [n(text, text)])\n            reset()\n\n    # Test application name and notification type\n    def e(x):\n        return standard_b64encode(x.encode()).decode()\n\n    h(f'i=t:d=0:f={e(\"app\")}:t={e(\"1\")};title')\n    h(f'i=t:t={e(\"test\")}')\n    self.ae(di.notifications, [n(application_name='app', notification_types=('1', 'test',))])\n    reset()\n\n    # Test timeout\n    h('w=3;title')\n    self.ae(di.notifications, [n(timeout=3)])\n    reset()\n\n    # Test Disk Cache\n    dc = IconDataCache(base_cache_dir=tdir, max_cache_size=4)\n    cache_dir = dc._ensure_state()\n    for i in range(5):\n        dc.add_icon(str(i), str(i).encode())\n    self.ae(set(dc.keys()), set(map(str, range(1, 5))))\n    del dc\n    self.assertFalse(os.path.exists(cache_dir))\n\n    # Test icons\n    def send_with_icon(data='', n='', g=''):\n        m = ''\n        if n:\n            for x in n.split(','):\n                m += f'n={standard_b64encode(x.encode()).decode()}:'\n        if g:\n            m += f'g=({g}:'\n        h(f'i=9:d=0:{m};title')\n        h(f'i=9:p=icon;{data}')\n\n    dc = nm.icon_data_cache\n    send_with_icon(n='mycon,ic2')\n    self.ae(di.notifications, [n(icon_names=('mycon', 'ic2'))])\n    reset()\n    send_with_icon(g='gid')\n    self.ae(di.notifications, [n()])\n    reset()\n    send_with_icon(g='gid', data='1')\n    self.ae(di.notifications, [n(icon_path=dc.hash(b'1'))])\n    send_with_icon(g='gid', n='moose')\n    self.ae(di.notifications[-1], n(icon_names=('moose',), icon_path=dc.hash(b'1')))\n    send_with_icon(g='gid2', data='2')\n    self.ae(di.notifications[-1], n(icon_path=dc.hash(b'2')))\n    send_with_icon(data='3')\n    self.ae(di.notifications[-1], n(icon_path=dc.hash(b'3')))\n    reset()\n\n\nclass TestNotifications(BaseTest):\n\n    def test_desktop_notify(self):\n        with tempfile.TemporaryDirectory() as tdir:\n            do_test(self, tdir)\n\n"
  },
  {
    "path": "kitty_tests/open_actions.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nfrom contextlib import contextmanager\n\nfrom kitty.utils import get_editor\n\nfrom . import BaseTest\n\n\n@contextmanager\ndef patch_env(**kw):\n    orig = os.environ.copy()\n    for k, v in kw.items():\n        if v is None:\n            os.environ.pop(k, None)\n        else:\n            os.environ[k] = v\n    yield\n    os.environ.clear()\n    os.environ.update(orig)\n\n\nclass TestOpenActions(BaseTest):\n\n    def test_parsing_of_open_actions(self):\n        from kitty.open_actions import KeyAction, actions_for_url\n        self.set_options()\n        spec = '''\nprotocol file\nmime text/*\nfragment_matches .\nAcTion launch $EDITOR $FILE_PATH $FRAGMENT\naction\n\nprotocol file\nmime text/*\naction ignored\n\next py,txt\naction one\naction two\n'''\n\n        def actions(url):\n            with patch_env(FILE_PATH='notgood'):\n                return tuple(actions_for_url(url, spec))\n\n        def single(url, func, *args):\n            acts = actions(url)\n            self.ae(len(acts), 1)\n            self.ae(acts[0].func, func)\n            self.ae(acts[0].args, args)\n\n        single('file://hostname/tmp/moo.txt#23', 'launch', *get_editor(), '/tmp/moo.txt', '23')\n        single('some thing.txt', 'ignored')\n        self.ae(actions('x:///a.txt'), (KeyAction('one', ()), KeyAction('two', ())))\n"
  },
  {
    "path": "kitty_tests/options.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport shutil\nimport subprocess\nimport tempfile\n\nfrom kitty.constants import kitty_exe\nfrom kitty.fast_data_types import Color, test_cursor_blink_easing_function\nfrom kitty.options.utils import DELETE_ENV_VAR, EasingFunction, to_color\nfrom kitty.utils import log_error, shlex_split\n\nfrom . import BaseTest\n\n\nclass TestConfParsing(BaseTest):\n\n    def setUp(self):\n        super().setUp()\n        self.error_messages = []\n        log_error.redirect = self.error_messages.append\n        self.tdir = tempfile.mkdtemp()\n\n    def tearDown(self):\n        del log_error.redirect\n        shutil.rmtree(self.tdir)\n        super().tearDown()\n\n    def test_conf_parsing(self):\n        conf_parsing(self)\n\n    def test_launcher(self):\n        launcher(self)\n\n    def test_cli_parsing(self):\n        cli_parsing(self)\n\n\ndef cli_parsing(self):\n    from kitty.cli import CLIOptions, Options, parse_cmdline, parse_option_spec\n    seq, disabled = parse_option_spec('''\\\n--simple-string -s\na simple string\n\n\n--list -l\ntype=list\na list\n\n\n--choice -c\nchoices=a,b,c\nsome choices\n\n\n--bool-set -1\ntype=bool-set\nset a bool\n\n\n--bool-reset -0\ntype=bool-reset\nunset a bool\n\n\n--int -i\ntype=int\na integer\n\n\n--float -f\ntype=float\na float\n\n\n--version -v\ntype=bool-set\nversion\n''')\n    oc = Options(seq, usage='xxx', message='yyy', appname='test')\n    oc.do_print = False\n\n    def t(args, leftover=(), fails=False, version_called=False, help_called=False, **expected):\n        oc.help_called = oc.version_called = False\n        args = list(shlex_split(args))\n        ans = CLIOptions()\n        if fails:\n            if isinstance(fails, str):\n                with self.assertRaisesRegex(SystemExit, fails):\n                    parse_cmdline(oc, disabled, ans, args=args)\n            else:\n                with self.assertRaises(SystemExit):\n                    parse_cmdline(oc, disabled, ans, args=args)\n        else:\n            actual_leftover = parse_cmdline(oc, disabled, ans, args=args)\n            self.assertEqual(tuple(leftover), tuple(actual_leftover), f'{args}\\n{ans}')\n            for dest, defval in oc.values_map.items():\n                val = expected.get(dest, defval)\n                self.assertEqual(val, getattr(ans, dest, BaseTest), f'Failed to parse {dest} correctly for: {args}\\n{ans}')\n        self.assertEqual(version_called, oc.version_called, f'Failed to call version for: {args}\\n{ans}')\n        self.assertEqual(help_called, oc.help_called, f'Failed to call help for: {args}\\n{ans}')\n\n    t('-1', bool_set=True)\n    t('-01', bool_reset=False, bool_set=True)\n    t('-01=y', bool_reset=False, bool_set=True)\n    t('-01=n', bool_reset=False, bool_set=False)\n    t('--simple-string xx moo -1', leftover=['moo', '-1'], simple_string='xx')\n    t('--si -0 -- -1', leftover=['-1'], simple_string='-0')\n    t('--simple-string=-0 -- -1', leftover=['-1'], simple_string='-0')\n    t('--simple-string=--help -- -1', leftover=['-1'], simple_string='--help')\n    t('--simple-string --help -- -1', leftover=['-1'], simple_string='--help')\n    t('-1l=a --list=b -c b --list c', bool_set=True, choice='b', list=list('abc'))\n    t('-1s= -l \"\" --list= x', leftover=['x'], bool_set=True, simple_string='', list=['', ''])\n    t('--choice moo', fails=r'moo')\n    t('--bool', fails='mbiguous')\n    t('-1c moo', fails=r'moo')\n    t('-10c=moo', fails=r'moo')\n    t('-1 -h', fails=True, help_called=True)\n    t('-1 --help', fails=True, help_called=True)\n    t('-1 -0v', fails=True, version_called=True)\n    t('-1 -v0', fails=True, version_called=True)\n    t('-1 --version', fails=True, version_called=True)\n    t('-f=3.142 --int 17', float=3.142, int=17)\n\n\ndef launcher(self):\n    kexe = kitty_exe()\n    cfgdir = None\n    def get_report(cmdline: str, launch_services= False):\n        nonlocal cfgdir\n        args = list(shlex_split(cmdline))\n        env = dict(os.environ)\n        if launch_services:\n            env['KITTY_LAUNCHED_BY_LAUNCH_SERVICES'] = '1'\n        cp = subprocess.run([kexe, \"+testing-launcher-code\"] + args, env=env, stdout=subprocess.PIPE)\n        self.assertEqual(cp.returncode, 0)\n        ans = {}\n        for line in cp.stdout.decode().split('\\n'):\n            if not line:\n                continue\n            try:\n                key, val = line.split(':')\n            except ValueError:\n                raise AssertionError(f'Unexpected output from launcher: {line!r}\\n{cp.stdout.decode()}')\n            if '\\x1e' in val:\n                val = [x for x in val.split('\\x1e') if x]\n            else:\n                val = val.strip()\n            ans[key] = val\n            if key == 'config_dir':\n                cfgdir = val\n        return ans, cp.stdout.decode().replace('\\x1e', ' ')\n    def test(cmdline, assertions):\n        r, output = get_report(cmdline, launch_services=assertions.get('launched_by_launch_services', '0') != '0')\n        for key, expected in assertions.items():\n            self.assertEqual(expected, r.get(key), f'Failed for {key} with command line: {cmdline}\\nOutput:\\n{output}')\n        return output\n\n    def t(cmdline, **assertions):\n        assertions['is_quick_access_terminal'] = '0'\n        if cfgdir:\n            assertions['config_dir'] = cfgdir\n        assertions.setdefault('launched_by_launch_services', '0')\n        test(cmdline, assertions)\n\n    def si(cmdline, **assertions):\n        assertions['single_instance'] = '1'\n        test(cmdline, assertions)\n\n    def dt(cmdline, **assertions):\n        assertions['detach'] = 'true'\n        test(cmdline, assertions)\n\n    def k(cmdline):\n        assertions = {}\n        assertions['argv'] = ['kitten'] + cmdline.split()\n        for prefix in ('+kitten', '+ kitten'):\n            output = test(prefix + ' ' + cmdline, assertions)\n            self.assertIn('kitten_exe:', output)\n\n    def pn(cmdline, **assertions):\n        ig = assertions.get('instance_group')\n        assertions['instance_group'] = f'panel-{ig}' if ig else 'panel'\n        assertions['single_instance'] = '1'\n        assertions['session'] = ''\n        test(cmdline, assertions)\n\n    t('', original_argv=[kexe], argv=[])\n    t('--title=xxx --start-as maximized -c=a -c b cat', title='xxx', start_as='maximized', config=['a', 'b'], original_argv=[\n        kexe, '--title=xxx', '--start-as', 'maximized', '-c=a', '-c', 'b', 'cat'], argv=['cat'])\n    k('icat abc xyz')\n    t('+kitten unwrapped xyz', argv=['+kitten', 'unwrapped', 'xyz'])\n    t('+ kitten unwrapped xyz', original_argv=[kexe, '+', 'kitten', 'unwrapped', 'xyz'])\n    si('--single-instance --instance-group=g -T 3', argv=[kexe, '--single-instance', '--instance-group=g', '-T', '3'])\n    t('+open --help', argv=['+open', '--help'])\n    t('+open -1 --help', argv=['+open', '-1', '--help'])\n    si('+open -1 moose', argv=[kexe, '+open', '-1', 'moose'], open_urls=['moose'])\n    si('+open -1 --instance-group=g x y', instance_group='g', open_urls=['x', 'y'])\n    dt('--detach --session=moose --detached-log=xyz', detached_log='xyz', session='moose')\n    pn('+kitten panel -1 --edge=left', edge='left')\n\n\ndef conf_parsing(self):\n    from kitty.config import defaults, load_config\n    from kitty.constants import is_macos\n    from kitty.fonts import FontModification, ModificationType, ModificationUnit, ModificationValue\n    from kitty.options.utils import to_modifiers\n    bad_lines = []\n\n    def p(*lines, bad_line_num=0, num_err=None):\n        del bad_lines[:]\n        del self.error_messages[:]\n        ans = load_config(overrides=lines, accumulate_bad_lines=bad_lines)\n        if bad_line_num:\n            self.ae(len(bad_lines), bad_line_num)\n        else:\n            self.assertFalse(bad_lines)\n        if num_err is not None:\n            self.ae(len(self.error_messages), num_err, '\\n'.join(self.error_messages))\n        return ans\n\n    def keys_for_func(opts, name):\n        for key, defns in opts.keyboard_modes[''].keymap.items():\n            for action in opts.alias_map.resolve_aliases(defns[0].definition):\n                if action.func == name:\n                    yield key\n\n    opts = p('font_size 11.37', 'clear_all_shortcuts y', 'color23 red')\n    self.ae(opts.font_size, 11.37)\n    self.ae(opts.mouse_hide_wait[0], 0 if is_macos else 3)\n    self.ae(opts.mouse_hide_wait[1], 0)\n    self.ae(opts.mouse_hide_wait[2], 40)\n    self.ae(opts.mouse_hide_wait[3], True)\n    self.ae(opts.color23, Color(255, 0, 0))\n    self.assertFalse(opts.keyboard_modes[''].keymap)\n    opts = p(\"url_excluded_characters '''\")\n    self.ae(opts.url_excluded_characters, \"'''\")\n    opts = p(\"url_excluded_characters abc'\")\n    self.ae(opts.url_excluded_characters, \"abc'\")\n    opts = p('clear_all_shortcuts y', 'map f1 next_window')\n    self.ae(len(opts.keyboard_modes[''].keymap), 1)\n    opts = p('clear_all_mouse_actions y', 'mouse_map left click ungrabbed mouse_click_url_or_select')\n    self.ae(len(opts.mousemap), 1)\n    opts = p('strip_trailing_spaces always')\n    self.ae(opts.strip_trailing_spaces, 'always')\n    self.assertFalse(bad_lines)\n    opts = p('pointer_shape_when_grabbed XXX', bad_line_num=1)\n    self.ae(opts.pointer_shape_when_grabbed, defaults.pointer_shape_when_grabbed)\n    opts = p('modify_font underline_position -2', 'modify_font underline_thickness 150%', 'modify_font size Test -1px')\n    self.ae(opts.modify_font, {\n        'underline_position': FontModification(ModificationType.underline_position, ModificationValue(-2., ModificationUnit.pt)),\n        'underline_thickness': FontModification(ModificationType.underline_thickness, ModificationValue(150, ModificationUnit.percent)),\n        'size:Test': FontModification(ModificationType.size, ModificationValue(-1., ModificationUnit.pixel), 'Test'),\n    })\n\n    # test the aliasing options\n    opts = p('env A=1', 'env B=x$A', 'env C=', 'env D', 'clear_all_shortcuts y', 'kitten_alias a b --moo', 'map f1 kitten a arg')\n    self.ae(opts.env, {'A': '1', 'B': 'x1', 'C': '', 'D': DELETE_ENV_VAR})\n\n    def ac(which=0):\n        ka = tuple(opts.keyboard_modes[''].keymap.values())[0][0]\n        acs = opts.alias_map.resolve_aliases(ka.definition)\n        return acs[which]\n\n    ka = ac()\n    self.ae(ka.func, 'kitten')\n    self.ae(ka.args, ('b', '--moo', 'arg'))\n\n    opts = p('clear_all_shortcuts y', 'kitten_alias hints hints --hi', 'map f1 kitten hints XXX')\n    ka = ac()\n    self.ae(ka.func, 'kitten')\n    self.ae(ka.args, ('hints', '--hi', 'XXX'))\n\n    opts = p('clear_all_shortcuts y', 'action_alias la launch --moo', 'map f1 la XXX')\n    ka = ac()\n    self.ae(ka.func, 'launch')\n    self.ae(ka.args, ('--moo', 'XXX'))\n\n    opts = p('clear_all_shortcuts y', 'action_alias one launch --moo', 'action_alias two one recursive', 'map f1 two XXX')\n    ka = ac()\n    self.ae(ka.func, 'launch')\n    self.ae(ka.args, ('--moo', 'recursive', 'XXX'))\n\n    opts = p('clear_all_shortcuts y', 'action_alias launch two 1', 'action_alias two launch 2', 'map f1 launch 3')\n    ka = ac()\n    self.ae(ka.func, 'launch')\n    self.ae(ka.args, ('2', '1', '3'))\n\n    opts = p('clear_all_shortcuts y', 'action_alias launch launch --moo', 'map f1 launch XXX')\n    ka = ac()\n    self.ae(ka.func, 'launch')\n    self.ae(ka.args, ('--moo', 'XXX'))\n\n    opts = p('clear_all_shortcuts y', 'action_alias cfs change_font_size current', 'map f1 cfs +2')\n    ka = ac()\n    self.ae(ka.func, 'change_font_size')\n    self.ae(ka.args, (False, '+', 2.0))\n\n    opts = p('clear_all_shortcuts y', 'action_alias la launch --moo', 'map f1 combine : new_window : la ')\n    self.ae((ac().func, ac(1).func), ('new_window', 'launch'))\n\n    opts = p('clear_all_shortcuts y', 'action_alias cc combine : new_window : launch --moo', 'map f1 cc XXX')\n    self.ae((ac().func, ac(1).func), ('new_window', 'launch'))\n    self.ae(ac(1).args, ('--moo', 'XXX'))\n\n    opts = p('clear_all_shortcuts y', 'action_alias ss kitten \"space 1\"', 'map f1 ss \"space 2\"')\n    self.ae(ac().args, ('space 1', 'space 2'))\n\n    opts = p('kitty_mod alt')\n    self.ae(opts.kitty_mod, to_modifiers('alt'))\n    self.ae(next(keys_for_func(opts, 'next_layout')).mods, opts.kitty_mod)\n\n    # deprecation handling\n    opts = p('clear_all_shortcuts y', 'send_text all f1 hello')\n    self.ae(len(opts.keyboard_modes[''].keymap), 1)\n    opts = p('macos_hide_titlebar y' if is_macos else 'x11_hide_window_decorations y')\n    self.assertTrue(opts.hide_window_decorations)\n    self.ae(len(self.error_messages), 1)\n\n    # line breaks\n    opts = p(\"    font\",\n                \" \\t  \\t    \\\\_size\",\n                \"    \\\\ 12\",\n                \"\\\\.35\",\n                \"col\",\n                \"\\\\o\",\n                \"\\t \\t\\\\r\",\n                \"\\\\25\",\n                \" \\\\ blue\")\n    self.ae(opts.font_size, 12.35)\n    self.ae(opts.color25, Color(0, 0, 255))\n\n    # cursor_blink_interval\n    def cb(src, interval=-1, first=EasingFunction(), second=EasingFunction()):\n        opts = p('cursor_blink_interval ' + src)\n        self.ae((float(interval), first, second), (float(opts.cursor_blink_interval[0]), opts.cursor_blink_interval[1], opts.cursor_blink_interval[2]))\n    cb('3', 3)\n    cb('-2.3', -2.3)\n    cb('linear', first=EasingFunction('cubic-bezier', cubic_bezier_points=(0, 0.0, 1.0, 1.0)))\n    cb('linear 19', 19, EasingFunction('cubic-bezier', cubic_bezier_points=(0, 0.0, 1.0, 1.0)))\n    cb('ease-in-out cubic-bezier(0.1, 0.2, 0.3, 0.4) 11', 11,\n        EasingFunction('cubic-bezier', cubic_bezier_points=(0.42, 0, 0.58, 1)),\n        EasingFunction('cubic-bezier', cubic_bezier_points=(0.1, 0.2, 0.3, 0.4))\n    )\n    cb('step-start', first=EasingFunction('steps', num_steps=1, jump_type='start'))\n    cb('steps(7, jump-none)', first=EasingFunction('steps', num_steps=7, jump_type='none'))\n    cb('linear(0, 0.25, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.5, 1.0), linear_y=(0, 0.25, 1.0)))\n    cb('linear(0, 0.25 75%, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.75, 1.0), linear_y=(0, 0.25, 1.0)))\n    cb('linear(0, 0.25 25% 75%, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.25, 0.75, 1.0), linear_y=(0, 0.25, 0.25, 1.0)))\n\n    # test that easing functions give expected values\n    def ef(spec, tests, only_single=True, duration=0.5, accuracy=0):\n        cfv = p('cursor_blink_interval ' + spec).cursor_blink_interval\n        self.set_options({'cursor_blink_interval': cfv})\n        for t, expected in tests.items():\n            actual = test_cursor_blink_easing_function(t, only_single, duration)\n            if abs(actual - expected) > accuracy:\n                self.ae(expected, actual, f'Failed for {spec=} with {t=}: {expected} != {actual}')\n\n    ef('linear', {0:1, 0.25: 0.5, 0.5: 0, 0.75: 0.5, 1: 1}, only_single=False)\n\n    ef('linear(0, 0.25 25% 75%, 1)', {0: 0, 0.25: 0.25, 0.3: 0.25, 0.75: 0.25, 1:1})\n    linear_vals = {0: 0, 1: 1, 0.1234: 0.1234, 0.6453: 0.6453}\n    for spec in ('linear', 'linear(0, 1)', 'cubic-bezier(0, 0, 1, 1)', 'cubic-bezier(0.2, 0.2, 0.7, 0.7)'):\n        ef(spec, linear_vals)\n    # test an almost linear function to test cubic bezier implementation\n    ef('cubic-bezier(0.2, 0.2, 0.7, 0.71)', linear_vals, accuracy=0.01)\n    ef('cubic-bezier(0.23, 0.2, 0.7, 0.71)', linear_vals, accuracy=0.01)\n\n    ef('steps(5)', {0: 0, 0.1: 0, 0.3: 0.2, 0.9:0.8})\n    ef('steps(5, start)', {0: 0.2, 0.1: 0.2, 0.3: 0.4, 0.9:1})\n    ef('steps(4, jump-both)', {0: 0.2, 0.1: 0.2, 0.3: 0.4, 0.9:1})\n    ef('steps(6, jump-none)', {0: 0, 0.1: 0.0, 0.3: 0.2, 0.9:1})\n\n    # test various include modes\n    base = os.path.join(self.tdir, 'glob')\n    os.mkdir(base)\n    with open(os.path.join(base, 'fg'), 'w') as f:\n        print('foreground red', file=f)\n    opts = p(f'include {f.name}', num_err=0)\n    self.ae(opts.foreground, to_color('red'))\n    with open(os.path.join(self.tdir, 'bg'), 'w') as f:\n        print('background white', file=f)\n        print('globinclude glob/*', file=f)\n        print('envinclude ENVINCLUDE', file=f)\n        print('geninclude g.py', file=f)\n        print('geninclude g', file=f)\n    with open(os.path.join(self.tdir, 'g.py'), 'w') as g:\n        print('print(\"background_opacity .77\")', file=g)\n        print('print(\"background_blur 77\")', file=g)\n    with open(os.path.join(self.tdir, 'g'), 'w') as g:\n        print('#!/bin/sh', file=g)\n        print('echo background_image_linear y', file=g)\n        print('echo background_image_layout clamped', file=g)\n        os.chmod(g.fileno(), 0o700)\n    os.environ['ENVINCLUDE'] = 'cursor yellow'\n    opts = p(f'include {f.name}', num_err=0)\n    os.environ.pop('ENVINCLUDE')\n    self.ae(opts.foreground, to_color('red'))\n    self.ae(opts.background, to_color('white'))\n    self.ae(opts.cursor, to_color('yellow'))\n    self.ae(opts.background_opacity, .77)\n    self.ae(opts.background_blur, 77)\n    self.ae(opts.background_image_linear, True)\n    self.ae(opts.background_image_layout, 'clamped')\n    with open(os.path.join(self.tdir, 'a'), 'w') as a:\n        print('background red', file=a)\n        print('include b', file=a)\n    with open(os.path.join(self.tdir, 'b'), 'w') as a:\n        print('foreground red', file=a)\n        print('include a', file=a)\n    opts = p(f'include {a.name}', num_err=0, bad_line_num=1)\n    self.ae(opts.foreground, to_color('red'))\n    self.ae(opts.background, to_color('red'))\n"
  },
  {
    "path": "kitty_tests/panels.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport shlex\nimport subprocess\n\n\ndef r(msg: str, cmdline: str) -> None:\n    try:\n        q = input('Test ' + msg + '? (y/n): ').lower()\n        if q in ('y', 'yes'):\n            try:\n                subprocess.run(['kitten'] + shlex.split(cmdline))\n            except KeyboardInterrupt:\n                pass\n    except KeyboardInterrupt:\n        raise SystemExit(1)\n\nif __name__ == '__main__':\n    r('top panel check transpareny, no input focus, margins and struts',\n    'panel -o background_opacity=0.2 --edge=top --lines=2 --margin-left=50 --margin-right=100')\n\n    r('bottom panel, check struts', 'panel -o background_opacity=0.2 --edge=bottom --lines=2 --margin-left=100 --margin-right=50')\n\n    r('left panel, check struts', 'panel -o background_opacity=0.2 --edge=left --columns=2 --margin-top=50 --margin-bottom=100')\n\n    r('right panel, check struts', 'panel -o background_opacity=0.2 --edge=right --columns=2 --margin-top=50 --margin-bottom=100')\n\n    r('background, check transparency and margins and no input focus',\n    'panel -o background_opacity=0.2 --edge=background --margin-top=50 --margin-bottom=50 --margin-left=100 --margin-right=100')\n\n    r('quake, check transparency and focus on show/re-show', 'quick-access-terminal')\n"
  },
  {
    "path": "kitty_tests/parser.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom binascii import hexlify\nfrom functools import partial\n\nfrom kitty.fast_data_types import (\n    CURSOR_BLOCK,\n    VT_PARSER_BUFFER_SIZE,\n    base64_decode,\n    base64_encode,\n    has_avx2,\n    has_sse4_2,\n    test_find_either_of_two_bytes,\n    test_utf8_decode_to_sentinel,\n)\n\nfrom . import BaseTest, parse_bytes\n\n\ndef cnv(x):\n    if isinstance(x, memoryview):\n        x = str(x, 'utf-8')\n    return x\n\n\nclass CmdDump(list):\n\n    def __call__(self, window_id, *a):\n        if a and a[0] == 'bytes':\n            return\n        if a and a[0] == 'error':\n            a = a[1:]\n        self.append(tuple(map(cnv, a)))\n\n    def get_result(self):\n        current = ''\n        q = []\n        for args in self:\n            if args[0] == 'draw':\n                current += args[1]\n            else:\n                if current:\n                    q.append(('draw', current))\n                    current = ''\n                q.append(args)\n        if current:\n            q.append(('draw', current))\n        return tuple(q)\n\n\nclass TestParser(BaseTest):\n\n    def create_write_buffer(self, screen):\n        return screen.test_create_write_buffer()\n\n    def write_bytes(self, screen, write_buf, data):\n        if isinstance(data, str):\n            data = data.encode('utf-8')\n        s = screen.test_commit_write_buffer(data, write_buf)\n        return data[s:]\n\n    def parse_written_data(self, screen, *cmds):\n        cd = CmdDump()\n        screen.test_parse_written_data(cd)\n        cmds = tuple(('draw', x) if isinstance(x, str) else tuple(map(cnv, x)) for x in cmds)\n        self.ae(cmds, cd.get_result())\n\n    def parse_bytes_dump(self, s, x, *cmds):\n        cd = CmdDump()\n        if isinstance(x, str):\n            x = x.encode('utf-8')\n        cmds = tuple(('draw', x) if isinstance(x, str) else tuple(map(cnv, x)) for x in cmds)\n        parse_bytes(s, x, cd)\n        self.ae(cmds, cd.get_result())\n\n    def test_charsets(self):\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        pb(b'\\xc3')\n        pb(b'\\xa1', ('draw', b'\\xc3\\xa1'.decode('utf-8')))\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        pb('\\033)0\\x0e/_', ('screen_designate_charset', 1, ord('0')), ('screen_change_charset', 1), '/_')\n        self.ae(str(s.line(0)), '/\\xa0')\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        pb('\\033(0/_', ('screen_designate_charset', 0, ord('0')), '/_')\n        self.ae(str(s.line(0)), '/\\xa0')\n\n    def test_parser_threading(self):\n        s = self.create_screen()\n\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), 'a\\x1b]2;some title'))\n        b = self.create_write_buffer(s)\n        self.parse_written_data(s, 'a')\n        self.assertFalse(self.write_bytes(s, b, ' full\\x1b\\\\'))\n        self.parse_written_data(s, ('set_title', 'some title full'))\n\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), 'a\\x1b]'))\n        b = self.create_write_buffer(s)\n        self.parse_written_data(s, 'a')\n        self.assertFalse(self.write_bytes(s, b, '2;title\\x1b\\\\'))\n        self.parse_written_data(s, ('set_title', 'title'))\n\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), 'a\\x1b'))\n        b = self.create_write_buffer(s)\n        self.parse_written_data(s, 'a')\n        self.assertFalse(self.write_bytes(s, b, ']2;title\\x1b\\\\'))\n        self.parse_written_data(s, ('set_title', 'title'))\n\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), 'a\\x1b]2;some title\\x1b'))\n        b = self.create_write_buffer(s)\n        self.parse_written_data(s, 'a')\n        self.assertFalse(self.write_bytes(s, b, '\\\\b'))\n        self.parse_written_data(s, ('set_title', 'some title'), 'b')\n\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), '1\\x1b'))\n        self.parse_written_data(s, '1')\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), 'E2'))\n        self.parse_written_data(s, ('screen_nel',), ('draw', '2'))\n\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), '1\\x1b[2'))\n        self.parse_written_data(s, '1')\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), '3mx'))\n        self.parse_written_data(s, ('select_graphic_rendition', '23'), 'x')\n\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), '1\\x1b'))\n        self.parse_written_data(s, '1')\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), '[23mx'))\n        self.parse_written_data(s, ('select_graphic_rendition', '23'), 'x')\n\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), '1\\x1b['))\n        self.parse_written_data(s, '1')\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), '23mx'))\n        self.parse_written_data(s, ('select_graphic_rendition', '23'), 'x')\n\n        # test full write\n        sz = VT_PARSER_BUFFER_SIZE // 3 + 7\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), b'a' * sz))\n        self.assertFalse(self.write_bytes(s, self.create_write_buffer(s), b'b' * sz))\n        left = self.write_bytes(s, self.create_write_buffer(s), b'c' * sz)\n        self.assertTrue(len(left), 3 * sz - VT_PARSER_BUFFER_SIZE)\n        self.assertFalse(self.create_write_buffer(s))\n        s.test_parse_written_data()\n        b = self.create_write_buffer(s)\n        self.assertTrue(b)\n        self.write_bytes(s, b, b'')\n\n    def test_base64(self):\n        for src, expected in {\n            'bGlnaHQgdw==': 'light w',\n            'bGlnaHQgd28=': 'light wo',\n            'bGlnaHQgd29y': 'light wor',\n        }.items():\n            self.ae(base64_decode(src.encode()), expected.encode(), f'Decoding of {src} failed')\n            self.ae(base64_decode(src.replace('=', '').encode()), expected.encode(), f'Decoding of {src} failed')\n            self.ae(base64_encode(expected.encode()), src.replace('=', '').encode(), f'Encoding of {expected} failed')\n\n    def test_simple_parsing(self):\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n\n        pb('12', '12')\n        self.ae(str(s.line(0)), '12')\n        self.ae(s.cursor.x, 2)\n        pb('3456', '3456')\n        self.ae(str(s.line(0)), '12345')\n        self.ae(str(s.line(1)), '6')\n        pb(b'\\n123\\n\\r45', ('screen_linefeed',), '123', ('screen_linefeed',), ('screen_carriage_return',), '45')\n        self.ae(str(s.line(1)), '6')\n        self.ae(str(s.line(2)), ' 123')\n        self.ae(str(s.line(3)), '45')\n        pb(b'\\rabcde', ('screen_carriage_return',), 'abcde')\n        self.ae(str(s.line(3)), 'abcde')\n        pb('\\rßxyz1', ('screen_carriage_return',), 'ßxyz1')\n        self.ae(str(s.line(3)), 'ßxyz1')\n        pb('ニチ ', 'ニチ ')\n        self.ae(str(s.line(4)), 'ニチ ')\n        s.reset()\n        self.assertFalse(str(s.line(1)) + str(s.line(2)) + str(s.line(3)))\n        c1_controls = '\\x84\\x85\\x88\\x8d\\x8e\\x8f\\x90\\x96\\x97\\x98\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f'\n        pb(c1_controls, c1_controls)\n        self.assertFalse(str(s.line(1)) + str(s.line(2)) + str(s.line(3)))\n        pb('😀'.encode()[:-1])\n        pb('\\x1b\\x1b%a', '\\ufffd', ('Unknown char after ESC: 0x1b',), ('draw', '%a'))\n\n    def test_utf8_simd_decode(self):\n        def unsupported(which):\n            return (which == 2 and not has_sse4_2) or (which == 3 and not has_avx2)\n\n        def reset_state():\n            test_utf8_decode_to_sentinel(b'', -1)\n\n        def asbytes(x):\n            if isinstance(x, str):\n                x = x.encode()\n            return x\n\n        def t(*a, which=2):\n            if unsupported(which):\n                return\n\n            def parse_parts(which):\n                total_consumed = 0\n                esc_found = False\n                parts = []\n                for x in a:\n                    found_sentinel, x, num_consumed = test_utf8_decode_to_sentinel(asbytes(x), which)\n                    total_consumed += num_consumed\n                    if found_sentinel:\n                        esc_found = found_sentinel\n                    parts.append(x)\n                return esc_found, ''.join(parts), total_consumed\n\n            reset_state()\n            expected = parse_parts(1)\n            reset_state()\n            actual = parse_parts(which)\n            self.ae(expected, actual, msg=f'Failed for {a} with {which=}\\n{expected!r} !=\\n{actual!r}')\n            return actual\n\n        def double_test(x):\n            for which in (2, 3):\n                t(x, which=which)\n            t(x*2, which=3)\n            reset_state()\n\n        # incomplete trailer at end of vector\n        t(\"a\"*10 + \"😸😸\" + \"b\"*15)\n\n        x = double_test\n        x('2:α3')\n        x('2:α\\x1b3')\n        x('2:α3:≤4:😸|')\n        x('abcd1234efgh5678')\n        x('abc\\x1bd1234efgh5678')\n        x('abcd1234efgh5678ijklABCDmnopEFGH')\n\n        for which in (2, 3):\n            x = partial(t, which=which)\n            x('abcdef', 'ghijk')\n            x('2:α3', ':≤4:😸|')\n            # trailing incomplete sequence\n            for prefix in (b'abcd', '😸'.encode()):\n                for suffix in (b'1234', '😸'.encode()):\n                    x(prefix + b'\\xf0\\x9f', b'\\x98\\xb8' + suffix)\n                    x(prefix + b'\\xf0\\x9f\\x9b', b'\\xb8' + suffix)\n                    x(prefix + b'\\xf0', b'\\x9f\\x98\\xb8' + suffix)\n                    x(prefix + b'\\xc3', b'\\xa4' + suffix)\n                    x(prefix + b'\\xe2', b'\\x89\\xa4' + suffix)\n                    x(prefix + b'\\xe2\\x89', b'\\xa4' + suffix)\n\n        def test_expected(src, expected, which=2):\n            if unsupported(which):\n                return\n            reset_state()\n            _, actual, _ = t(b'filler' + asbytes(src), which=which)\n            expected = 'filler' + expected\n            self.ae(expected, actual, f'Failed for: {src!r} with {which=}')\n\n        for which in (1, 2, 3):\n            pb = partial(test_expected, which=which)\n            pb('ニチ', 'ニチ')\n            pb('\\x84\\x85', '\\x84\\x85')\n            pb('\\x84\\x85', '\\x84\\x85')\n            pb('\\uf4df', '\\uf4df')\n            pb('\\uffff', '\\uffff')\n            pb('\\0', '\\0')\n            pb(chr(0x10ffff), chr(0x10ffff))\n            # Kitty's UTF-8 decoding uses `U+FFFD substitution of maximal subparts\n            # <https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G66453>`_,\n            # same as in the WHATWG Encoding Standard.\n            # This means that ill-formed sequences may be replaced by multiple\n            # U+FFFD REPLACEMENT CHARACTERs.\n            pb(b'abcd\\xf51234', 'abcd\\ufffd1234')  # bytes > 0xf4\n            pb(b'abcd\\xff1234', 'abcd\\ufffd1234')  # bytes > 0xf4\n            pb(b'\"\\xbf\"', '\"\\ufffd\"')\n            pb(b'\"\\x80\"', '\"\\ufffd\"')\n            pb(b'\"\\x80\\xbf\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\x80\\xbf\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xc0 \"', '\"\\ufffd \"')\n            pb(b'\"\\xfe\"', '\"\\ufffd\"')\n            pb(b'\"\\xff\"', '\"\\ufffd\"')\n            pb(b'\"\\xff\\xfe\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xfe\\xfe\\xff\\xff\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xef\\xbf\"', '\"\\ufffd\"')\n            pb(b'\"\\xe0\\xa0\"', '\"\\ufffd\"')\n            pb(b'\"\\xf0\\x9f\\x98\"', '\"\\ufffd\"')\n            pb(b'\"\\xef\\x93\\x94\\x95\"', '\"\\uf4d4\\ufffd\"')\n\n            # Lone continuation bytes with no leading starts\n            pb(b'\"\\xbf\"', '\"\\ufffd\"')\n            pb(b'\"\\x80\"', '\"\\ufffd\"')\n\n            # Multiple lone continuation bytes\n            pb(b'\"\\x80\\xbf\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\x80\\xbf\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n\n            # Lone starter byte of 2-byte sequence\n            pb(b'\"\\xc0 \"', '\"\\ufffd \"')\n\n            # Single never-valid bytes\n            pb(b'\"\\xfe\"', '\"\\ufffd\"')\n            pb(b'\"\\xff\"', '\"\\ufffd\"')\n\n            # Multiple never-valid bytes\n            pb(b'\"\\xff\\xfe\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xfe\\xfe\\xff\\xff\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n\n            # Truncated 2-byte sequence (only 1 byte)\n            pb(b'\"\\xc2\"', '\"\\ufffd\"')\n\n            # Truncated 3-byte sequences (only 2 bytes)\n            pb(b'\"\\xef\\xbf\"', '\"\\ufffd\"')\n            pb(b'\"\\xe0\\xa0\"', '\"\\ufffd\"')\n\n            # Truncated 4-byte sequence (only 2 or 3 bytes)\n            pb(b'\"\\xf0\\x9f\"', '\"\\ufffd\"')\n            pb(b'\"\\xf0\\x9f\\x98\"', '\"\\ufffd\"')\n\n            # Bad continuation byte (restored as ASCII)\n            pb(b'\"\\xe1\\x28\\xa1\"', '\"\\ufffd(\\ufffd\"')  # )\n\n            # Overlong 2-byte sequence for U+0000 (should be `0x00`)\n            pb(b'\"\\xc0\\x80\"', '\"\\ufffd\\ufffd\"')\n\n            # Overlong 3-byte sequence for U+0000 (violates boundary)\n            pb(b'\"\\xe0\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n\n            # Overlong 4-byte sequence for U+0000 (violates boundary)\n            pb(b'\"\\xf0\\x80\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n\n            # High surrogate code point\n            pb(b'\"\\xed\\xa0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n\n            # Low surrogate code point\n            pb(b'\"\\xed\\xb0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n\n            # Too large starter byte\n            pb(b'\"\\xff\\x80\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n\n            # The following boundary cases come from the table of well-formed UTF-8 byte sequences\n            # <https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G27506>`_.\n            # For continuation bytes, both 0xC0 and 0xC2 are tested as values that exceed the valid maximum.\n            # This is because 0xC0 is an invalid starter byte, but 0xC2 is also a starter byte for 2-byte sequences.\n            # simd-string-impl.h prefers classifying bytes as starter bytes when possible (e.g., in \"\\xf0\\x90\\xc2\\x80\").\n            # The tests need to check that simd-string-impl.h correctly detects\n            # starter bytes that are actually invalid continution bytes, like 0xC2.\n\n            # Boundary cases: 2-byte sequences\n            pb(b'\"\\xc1\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xc1\\x80\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xc1\\xbf\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xc1\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xc1\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xc2\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xc2\\x80\"', '\"\\x80\"')\n            pb(b'\"\\xc2\\xbf\"', '\"\\xbf\"')\n            pb(b'\"\\xc2\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xc2\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xdf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xdf\\x80\"', '\"\\u07c0\"')\n            pb(b'\"\\xdf\\xbf\"', '\"\\u07ff\"')\n            pb(b'\"\\xdf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xdf\\xc2\"', '\"\\ufffd\\ufffd\"')\n\n            # Boundary cases: 3-byte sequences starting with 0xE0\n            pb(b'\"\\xe0\\x9f\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xe0\\xa0\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xe0\\xa0\\x80\"', '\"\\u0800\"')\n            pb(b'\"\\xe0\\xa0\\xbf\"', '\"\\u083f\"')\n            pb(b'\"\\xe0\\xa0\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xe0\\xa0\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xe0\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xe0\\xbf\\x80\"', '\"\\u0fc0\"')\n            pb(b'\"\\xe0\\xbf\\xbf\"', '\"\\u0fff\"')\n            pb(b'\"\\xe0\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xe0\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xe0\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xe0\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n\n            # Boundary cases: 3-byte sequences starting with 0xE1..0xEC\n            pb(b'\"\\xe1\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xe1\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xe1\\x80\\x80\"', '\"\\u1000\"')\n            pb(b'\"\\xe1\\x80\\xbf\"', '\"\\u103f\"')\n            pb(b'\"\\xe1\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xe1\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xe1\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xe1\\xbf\\x80\"', '\"\\u1fc0\"')\n            pb(b'\"\\xe1\\xbf\\xbf\"', '\"\\u1fff\"')\n            pb(b'\"\\xe1\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xe1\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xe1\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xe1\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xec\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xec\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xec\\x80\\x80\"', '\"\\uc000\"')\n            pb(b'\"\\xec\\x80\\xbf\"', '\"\\uc03f\"')\n            pb(b'\"\\xec\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xec\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xec\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xec\\xbf\\x80\"', '\"\\ucfc0\"')\n            pb(b'\"\\xec\\xbf\\xbf\"', '\"\\ucfff\"')\n            pb(b'\"\\xec\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xec\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xec\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xec\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n\n            # Boundary cases: 3-byte sequences starting with 0xED\n            pb(b'\"\\xed\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xed\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xed\\x80\\x80\"', '\"\\ud000\"')\n            pb(b'\"\\xed\\x80\\xbf\"', '\"\\ud03f\"')\n            pb(b'\"\\xed\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xed\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xed\\x9f\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xed\\x9f\\x80\"', '\"\\ud7c0\"')\n            pb(b'\"\\xed\\x9f\\xbf\"', '\"\\ud7ff\"')\n            pb(b'\"\\xed\\x9f\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xed\\x9f\\xc2\"', '\"\\ufffd\\ufffd\"')\n\n            # Boundary cases: 3-byte sequences starting with 0xEE..0xEF\n            pb(b'\"\\xed\\xa0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xee\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xee\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xee\\x80\\x80\"', '\"\\ue000\"')\n            pb(b'\"\\xee\\x80\\xbf\"', '\"\\ue03f\"')\n            pb(b'\"\\xee\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xee\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xee\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xee\\xbf\\x80\"', '\"\\uefc0\"')\n            pb(b'\"\\xee\\xbf\\xbf\"', '\"\\uefff\"')\n            pb(b'\"\\xee\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xee\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xee\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xee\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xef\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xef\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xef\\x80\\x80\"', '\"\\uf000\"')\n            pb(b'\"\\xef\\x80\\xbf\"', '\"\\uf03f\"')\n            pb(b'\"\\xef\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xef\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xef\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xef\\xbf\\x80\"', '\"\\uffc0\"')\n            pb(b'\"\\xef\\xbf\\xbf\"', '\"\\uffff\"')\n            pb(b'\"\\xef\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xef\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xef\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xef\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n\n            # Boundary cases: 4-byte sequences starting with 0xF0\n            pb(b'\"\\xf0\\x8f\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf0\\x90\\x80\\x80\"', '\"\\U00010000\"')\n            pb(b'\"\\xf0\\x90\\x80\\xbf\"', '\"\\U0001003f\"')\n            pb(b'\"\\xf0\\x90\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf0\\x90\\xbf\\x80\"', '\"\\U00010fc0\"')\n            pb(b'\"\\xf0\\x90\\xbf\\xbf\"', '\"\\U00010fff\"')\n            pb(b'\"\\xf0\\x90\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\xc0\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf0\\x90\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\xc0\\xbf\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\xc0\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\xc0\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\xc2\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf0\\x90\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xf0\\x90\\xc2\\xbf\"', '\"\\ufffd\\xbf\"')\n            pb(b'\"\\xf0\\x90\\xc2\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\x90\\xc2\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf0\\xbf\\x80\\x80\"', '\"\\U0003f000\"')\n            pb(b'\"\\xf0\\xbf\\x80\\xbf\"', '\"\\U0003f03f\"')\n            pb(b'\"\\xf0\\xbf\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf0\\xbf\\xbf\\x80\"', '\"\\U0003ffc0\"')\n            pb(b'\"\\xf0\\xbf\\xbf\\xbf\"', '\"\\U0003ffff\"')\n            pb(b'\"\\xf0\\xbf\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\xc0\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf0\\xbf\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\xc0\\xbf\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\xc0\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\xc0\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\xc2\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf0\\xbf\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xf0\\xbf\\xc2\\xbf\"', '\"\\ufffd\\xbf\"')\n            pb(b'\"\\xf0\\xbf\\xc2\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xbf\\xc2\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf0\\xc0\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n\n            # Boundary cases: 4-byte sequences starting with 0xF1..0xF3\n            pb(b'\"\\xf1\\x7f\\x80\\x80\"', '\"\\ufffd\\x7f\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf1\\x80\\x80\\x80\"', '\"\\U00040000\"')\n            pb(b'\"\\xf1\\x80\\x80\\xbf\"', '\"\\U0004003f\"')\n            pb(b'\"\\xf1\\x80\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf1\\x80\\xbf\\x80\"', '\"\\U00040fc0\"')\n            pb(b'\"\\xf1\\x80\\xbf\\xbf\"', '\"\\U00040fff\"')\n            pb(b'\"\\xf1\\x80\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\xc0\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf1\\x80\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\xc0\\xbf\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\xc0\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\x80\\xc2\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf1\\x80\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xf1\\x80\\xc2\\xbf\"', '\"\\ufffd\\xbf\"')\n            pb(b'\"\\xf1\\x80\\xc2\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf1\\xbf\\x80\\x80\"', '\"\\U0007f000\"')\n            pb(b'\"\\xf1\\xbf\\x80\\xbf\"', '\"\\U0007f03f\"')\n            pb(b'\"\\xf1\\xbf\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf1\\xbf\\xbf\\x80\"', '\"\\U0007ffc0\"')\n            pb(b'\"\\xf1\\xbf\\xbf\\xbf\"', '\"\\U0007ffff\"')\n            pb(b'\"\\xf1\\xbf\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\xc0\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf1\\xbf\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\xc0\\xbf\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\xc0\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\xc0\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\xc2\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf1\\xbf\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xf1\\xbf\\xc2\\xbf\"', '\"\\ufffd\\xbf\"')\n            pb(b'\"\\xf1\\xbf\\xc2\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xbf\\xc2\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xc0\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf1\\xc2\\x80\\x80\"', '\"\\ufffd\\x80\\ufffd\"')\n            pb(b'\"\\xf3\\x7f\\x80\\x80\"', '\"\\ufffd\\x7f\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf3\\x80\\x80\\x80\"', '\"\\U000c0000\"')\n            pb(b'\"\\xf3\\x80\\x80\\xbf\"', '\"\\U000c003f\"')\n            pb(b'\"\\xf3\\x80\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf3\\x80\\xbf\\x80\"', '\"\\U000c0fc0\"')\n            pb(b'\"\\xf3\\x80\\xbf\\xbf\"', '\"\\U000c0fff\"')\n            pb(b'\"\\xf3\\x80\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\xc0\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf3\\x80\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\xc0\\xbf\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\xc0\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\xc0\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\xc2\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf3\\x80\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xf3\\x80\\xc2\\xbf\"', '\"\\ufffd\\xbf\"')\n            pb(b'\"\\xf3\\x80\\xc2\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\x80\\xc2\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf3\\xbf\\x80\\x80\"', '\"\\U000ff000\"')\n            pb(b'\"\\xf3\\xbf\\x80\\xbf\"', '\"\\U000ff03f\"')\n            pb(b'\"\\xf3\\xbf\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf3\\xbf\\xbf\\x80\"', '\"\\U000fffc0\"')\n            pb(b'\"\\xf3\\xbf\\xbf\\xbf\"', '\"\\U000fffff\"')\n            pb(b'\"\\xf3\\xbf\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\xc0\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf3\\xbf\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\xc0\\xbf\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\xc0\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\xc0\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\xc2\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf3\\xbf\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xf3\\xbf\\xc2\\xbf\"', '\"\\ufffd\\xbf\"')\n            pb(b'\"\\xf3\\xbf\\xc2\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xbf\\xc2\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xc0\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf3\\xc2\\x80\\x80\"', '\"\\ufffd\\x80\\ufffd\"')\n\n            # Boundary cases: 4-byte sequences starting with 0xF4\n            pb(b'\"\\xf4\\x7f\\x80\\x80\"', '\"\\ufffd\\x7f\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf4\\x80\\x80\\x80\"', '\"\\U00100000\"')\n            pb(b'\"\\xf4\\x80\\x80\\xbf\"', '\"\\U0010003f\"')\n            pb(b'\"\\xf4\\x80\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf4\\x80\\xbf\\x80\"', '\"\\U00100fc0\"')\n            pb(b'\"\\xf4\\x80\\xbf\\xbf\"', '\"\\U00100fff\"')\n            pb(b'\"\\xf4\\x80\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\xc0\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf4\\x80\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\xc0\\xbf\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\xc0\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\xc0\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\xc2\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf4\\x80\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xf4\\x80\\xc2\\xbf\"', '\"\\ufffd\\xbf\"')\n            pb(b'\"\\xf4\\x80\\xc2\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x80\\xc2\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\x7f\\x80\"', '\"\\ufffd\\x7f\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\x80\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf4\\x8f\\x80\\x80\"', '\"\\U0010f000\"')\n            pb(b'\"\\xf4\\x8f\\x80\\xbf\"', '\"\\U0010f03f\"')\n            pb(b'\"\\xf4\\x8f\\x80\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\x80\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\xbf\\x7f\"', '\"\\ufffd\\x7f\"')\n            pb(b'\"\\xf4\\x8f\\xbf\\x80\"', '\"\\U0010ffc0\"')\n            pb(b'\"\\xf4\\x8f\\xbf\\xbf\"', '\"\\U0010ffff\"')\n            pb(b'\"\\xf4\\x8f\\xbf\\xc0\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\xbf\\xc2\"', '\"\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\xc0\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf4\\x8f\\xc0\\x80\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\xc0\\xbf\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\xc0\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\xc0\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\xc2\\x7f\"', '\"\\ufffd\\ufffd\\x7f\"')\n            pb(b'\"\\xf4\\x8f\\xc2\\x80\"', '\"\\ufffd\\x80\"')\n            pb(b'\"\\xf4\\x8f\\xc2\\xbf\"', '\"\\ufffd\\xbf\"')\n            pb(b'\"\\xf4\\x8f\\xc2\\xc0\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x8f\\xc2\\xc2\"', '\"\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf4\\x90\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n            pb(b'\"\\xf5\\x80\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n\n            # Boundary case: too large codepoint (> U+10FFFF)\n            pb(b'\"\\xf5\\x80\\x80\\x80\"', '\"\\ufffd\\ufffd\\ufffd\\ufffd\"')\n\n\n    def test_find_either_of_two_bytes(self):\n        sizes = []\n        if has_sse4_2:\n            sizes.append(2)\n        if has_avx2:\n            sizes.append(3)\n        sizes.append(0)\n\n        def test(buf, a, b, align_offset=0):\n            a_, b_ = ord(a), ord(b)\n            expected = test_find_either_of_two_bytes(buf, a_, b_, 1, 0)\n            for sz in sizes:\n                actual = test_find_either_of_two_bytes(buf, a_, b_, sz, align_offset)\n                self.ae(expected, actual, f'Failed for: {buf!r} {a=} {b=} at {sz=} and {align_offset=}')\n\n        q = 'abc'\n        for off in range(32):\n            test(q, '<', '>', off)\n            test(q, ' ', 'b', off)\n            test(q, '<', 'a', off)\n            test(q, '<', 'b', off)\n            test(q, 'c', '>', off)\n\n        def tests(buf, a, b):\n            for sz in (0, 16, 32, 64, 79):\n                buf = (' ' * sz) + buf\n                for align_offset in range(32):\n                    test(buf, a, b, align_offset)\n        tests(\"\", '<', '>')\n        tests(\"a\", '\\0', '\\0')\n        tests(\"a\", '<', '>')\n        tests(\"dsdfsfa\", '1', 'a')\n        tests(\"xa\", 'a', 'a')\n        tests(\"bbb\", 'a', '1')\n        tests(\"bba\", 'a', '<')\n        tests(\"baa\", '>', 'a')\n\n    def test_esc_codes(self):\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        pb('12\\033Da', '12', ('screen_index',), 'a')\n        self.ae(str(s.line(0)), '12')\n        self.ae(str(s.line(1)), '  a')\n        pb('\\033xa', ('Unknown char after ESC: 0x%x' % ord('x'),), 'a')\n        pb('\\033c123', ('screen_reset', ), '123')\n        self.ae(str(s.line(0)), '123')\n        pb('\\033.\\033a', ('Unhandled charset related escape code: 0x2e 0x1b',), 'a')\n\n    def test_csi_codes(self):\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        pb('abcde', 'abcde')\n        s.cursor_move(5)\n        pb('x\\033[2@y', 'x', ('screen_insert_characters', 2), 'y')\n        self.ae(str(s.line(0)), 'xy bc')\n        pb('x\\033[2;7@y', 'x', ('CSI code @ has 2 > 1 parameters',), 'y')\n        pb('x\\033[2;-7@y', 'x', ('CSI code @ has 2 > 1 parameters',), 'y')\n        pb('x\\033[-0001234567890@y', 'x', ('CSI code @ is not allowed to have negative parameter (-1234567890)',), 'y')\n        pb('x\\033[2-3@y', 'x', ('Invalid character in CSI: 3 (0x33), ignoring the sequence',), '@y')\n        pb('x\\033[@y', 'x', ('screen_insert_characters', 1), 'y')\n        pb('x\\033[345@y', 'x', ('screen_insert_characters', 345), 'y')\n        pb('x\\033[345;@y', 'x', ('screen_insert_characters', 345), 'y')\n        pb('\\033[H', ('screen_cursor_position', 1, 1))\n        self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 0)\n        pb('\\033[4H', ('screen_cursor_position', 4, 1))\n        pb('\\033[4;0H', ('screen_cursor_position', 4, 0))\n        pb('\\033[3;2H', ('screen_cursor_position', 3, 2))\n        pb('\\033[3;2;H', ('screen_cursor_position', 3, 2))\n        pb('\\033[00000000003;0000000000000002H', ('screen_cursor_position', 3, 2))\n        self.ae(s.cursor.x, 1), self.ae(s.cursor.y, 2)\n        pb('\\033[0001234567890H', ('screen_cursor_position', 1234567890, 1))\n        pb('\\033[J', ('screen_erase_in_display', 0, 0))\n        pb('\\033[?J', ('screen_erase_in_display', 0, 1))\n        pb('\\033[?2J', ('screen_erase_in_display', 2, 1))\n        pb('\\033[h')\n        pb('\\033[20;4h', ('screen_set_mode', 20, 0), ('screen_set_mode', 4, 0))\n        pb('\\033[?1000;1004h', ('screen_set_mode', 1000, 1), ('screen_set_mode', 1004, 1))\n        pb('\\033[20;4;20l', ('screen_reset_mode', 20, 0), ('screen_reset_mode', 4, 0), ('screen_reset_mode', 20, 0))\n        pb('\\033[=c', ('report_device_attributes', 0, 61))\n        s.reset()\n\n        def sgr(*params):\n            return (('select_graphic_rendition', f'{x}') for x in params)\n\n        pb('\\033[1;2;3;4;7;9;34;44m', *sgr('1;2;3;4;7;9;34;44'))\n        for attr in 'bold italic reverse strikethrough dim'.split():\n            self.assertTrue(getattr(s.cursor, attr), attr)\n        self.ae(s.cursor.decoration, 1)\n        self.ae(s.cursor.fg, 4 << 8 | 1)\n        self.ae(s.cursor.bg, 4 << 8 | 1)\n        pb('\\033[38;5;1;48;5;7m', ('select_graphic_rendition', '38:5:1'), ('select_graphic_rendition', '48:5:7'))\n        self.ae(s.cursor.fg, 1 << 8 | 1)\n        self.ae(s.cursor.bg, 7 << 8 | 1)\n        pb('\\033[38;2;1;2;3;48;2;7;8;9m', ('select_graphic_rendition', '38:2:1:2:3'), ('select_graphic_rendition', '48:2:7:8:9'))\n        self.ae(s.cursor.fg, 1 << 24 | 2 << 16 | 3 << 8 | 2)\n        self.ae(s.cursor.bg, 7 << 24 | 8 << 16 | 9 << 8 | 2)\n        pb('\\033[0;2m', *sgr('0;2'))\n        pb('\\033[;2m', *sgr('0;2'))\n        pb('\\033[m', *sgr('0'))\n        pb('\\033[1;;2m', *sgr('1;0;2'))\n        pb('\\033[38;5;1m', ('select_graphic_rendition', '38:5:1'))\n        pb('\\033[58;2;1;2;3m', ('select_graphic_rendition', '58:2:1:2:3'))\n        pb('\\033[38;2;1;2;3m', ('select_graphic_rendition', '38:2:1:2:3'))\n        pb('\\033[1001:2:1:2:3m', ('select_graphic_rendition', '1001:2:1:2:3'))\n        pb('\\033[38:2:1:2:3;48:5:9;58;5;7m', (\n            'select_graphic_rendition', '38:2:1:2:3'), ('select_graphic_rendition', '48:5:9'), ('select_graphic_rendition', '58:5:7'))\n        s.reset()\n        pb('\\033[1;2;3;4:5;7;9;34;44m', *sgr('1;2;3', '4:5', '7;9;34;44'))\n        for attr in 'bold italic reverse strikethrough dim'.split():\n            self.assertTrue(getattr(s.cursor, attr), attr)\n        self.ae(s.cursor.decoration, 5)\n        c = s.callbacks\n        pb('\\033[5n', ('report_device_status', 5, 0))\n        self.ae(c.wtcbuf, b'\\033[0n')\n        c.clear()\n        pb('\\033[6n', ('report_device_status', 6, 0))\n        self.ae(c.wtcbuf, b'\\033[1;1R')\n        pb('12345', '12345')\n        c.clear()\n        pb('\\033[6n', ('report_device_status', 6, 0))\n        self.ae(c.wtcbuf, b'\\033[2;1R')\n        c.clear()\n        s.cursor_key_mode = True\n        pb('\\033[?1$p', ('report_mode_status', 1, 1))\n        self.ae(c.wtcbuf, b'\\033[?1;1$y')\n        pb('\\033[?1l', ('screen_reset_mode', 1, 1))\n        self.assertFalse(s.cursor_key_mode)\n        c.clear()\n        pb('\\033[?1$p', ('report_mode_status', 1, 1))\n        self.ae(c.wtcbuf, b'\\033[?1;2$y')\n        pb('\\033[2;4r', ('screen_set_margins', 2, 4))\n        c.clear()\n        pb('\\033[14t', ('screen_report_size', 14, 0))\n        pb('\\033[14;2t', ('screen_report_size', 14, 2))\n        self.ae(c.wtcbuf, b'\\033[4;100;50t\\033[4;100;50t')\n        self.ae(s.margin_top, 1), self.ae(s.margin_bottom, 3)\n        pb('\\033[r', ('screen_set_margins', 0, 0))\n        self.ae(s.margin_top, 0), self.ae(s.margin_bottom, 4)\n        pb('\\033[1 q', ('screen_set_cursor', 1, ord(' ')))\n        self.assertTrue(s.cursor.blink)\n        self.ae(s.cursor.shape, CURSOR_BLOCK)\n\n        s.reset()\n        pb('\\033[3 @', ('Shift left escape code not implemented',))\n        pb('\\033[3 A', ('Shift right escape code not implemented',))\n        pb('\\033[3;4 S', ('Select presentation directions escape code not implemented',))\n        pb('\\033[1T', ('screen_reverse_scroll', 1))\n        pb('\\033[T', ('screen_reverse_scroll', 1))\n        pb('\\033[+T', ('screen_reverse_scroll_and_fill_from_scrollback', 1))\n\n        c.clear()\n        pb('\\033[?2026$p', ('report_mode_status', 2026, 1))\n        self.ae(c.wtcbuf, b'\\x1b[?2026;2$y')\n        c.clear()\n        pb('\\033[?2026h', ('screen_set_mode', 2026, 1))\n        pb('\\033[?2026$p', ('report_mode_status', 2026, 1))\n        self.ae(c.wtcbuf, b'\\x1b[?2026;1$y')\n        pb('\\033[?2026l', ('screen_reset_mode', 2026, 1))\n        c.clear()\n        pb('\\033[?2026$p', ('report_mode_status', 2026, 1))\n        self.ae(c.wtcbuf, b'\\x1b[?2026;2$y')\n\n    def test_csi_code_rep(self):\n        s = self.create_screen(8)\n        pb = partial(self.parse_bytes_dump, s)\n        pb('\\033[1b', ('screen_repeat_character', 1))\n        self.ae(str(s.line(0)), '')\n        pb('x\\033[7b', 'x', ('screen_repeat_character', 7))\n        self.ae(str(s.line(0)), 'xxxxxxxx')\n        pb('\\033[1;3H', ('screen_cursor_position', 1, 3))\n        pb('\\033[byz\\033[b', ('screen_repeat_character', 1), 'yz', ('screen_repeat_character', 1))\n        # repeat 'x' at 3, then 'yz' at 4-5, then repeat 'z' at 6\n        self.ae(str(s.line(0)), 'xxxyzzxx')\n        s.reset()\n        pb(' \\033[3b', ' ', ('screen_repeat_character', 3))\n        self.ae(str(s.line(0)), '    ')\n        s.reset()\n        pb('\\t\\033[b', ('screen_tab',), ('screen_repeat_character', 1))\n        self.ae(str(s.line(0)), '\\t')\n        s.reset()\n        b']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'\n\n    def test_osc_codes(self):\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        c = s.callbacks\n        pb('a\\033]2;x\\\\ryz\\033\\\\bcde', 'a', ('set_title', 'x\\\\ryz'), 'bcde')\n        self.ae(str(s.line(0)), 'abcde')\n        self.ae(c.titlebuf, ['x\\\\ryz'])\n        c.clear()\n        pb('\\033]\\x07', ('set_title', ''), ('set_icon', ''))\n        self.ae(c.titlebuf, ['']), self.ae(c.iconbuf, '')\n        pb('1\\033]ab\\x072', '1', ('set_title', 'ab'), ('set_icon', 'ab'), '2')\n        self.ae(c.titlebuf, ['', 'ab']), self.ae(c.iconbuf, 'ab')\n        c.clear()\n        pb('\\033]2;;;;\\x07', ('set_title', ';;;'))\n        self.ae(c.titlebuf, [';;;'])\n        c.clear()\n        pb('\\033]2;\\x07', ('set_title', ''))\n        self.ae(c.titlebuf, [''])\n        pb('\\033]110\\x07', ('set_dynamic_color', 110, ''))\n        self.ae(c.colorbuf, '')\n        c.clear()\n        pb('\\033]9;\\x07', ('desktop_notify', 9, ''))\n        pb('\\033]9;test it with a nice long string\\x07', ('desktop_notify', 9, 'test it with a nice long string'))\n        pb('\\033]99;moo=foo;test it\\x07', ('desktop_notify', 99, 'moo=foo;test it'))\n        self.ae(c.notifications, [(9, ''), (9, 'test it with a nice long string'), (99, 'moo=foo;test it')])\n        c.clear()\n        pb('\\033]8;;\\x07', ('set_active_hyperlink', None, None))\n        pb('\\033]8moo\\x07', ('Ignoring malformed OSC 8 code',))\n        pb('\\033]8;moo\\x07', ('Ignoring malformed OSC 8 code',))\n        pb('\\033]8;id=xyz;\\x07', ('set_active_hyperlink', 'xyz', None))\n        pb('\\033]8;moo:x=z:id=xyz:id=abc;http://yay;.com\\x07', ('set_active_hyperlink', 'xyz', 'http://yay;.com'))\n        c.clear()\n        payload = '1' * 1024\n        pb(f'\\033]52;p;{payload}\\x07', ('clipboard_control', 52, f'p;{payload}'))\n        c.clear()\n        pb('\\033]52;p;xyz\\x07', ('clipboard_control', 52, 'p;xyz'))\n        c.clear()\n        pb('\\033]22;?__current__\\x07', ('set_dynamic_color', 22, '?__current__'))\n\n    def test_dcs_codes(self):\n        s = self.create_screen()\n        c = s.callbacks\n        pb = partial(self.parse_bytes_dump, s)\n        q = hexlify(b'kind').decode('ascii')\n        pb(f'a\\033P+q{q}\\033\\\\bcde', 'a', ('screen_request_capabilities', 43, q), 'bcde')\n        self.ae(str(s.line(0)), 'abcde')\n        self.ae(c.wtcbuf, '1+r{}={}'.format(q, '1b5b313b3242').encode('ascii'))\n        c.clear()\n        pb('\\033P$q q\\033\\\\', ('screen_request_capabilities', ord('$'), ' q'))\n        self.ae(c.wtcbuf, b'\\033P1$r1 q\\033\\\\')\n        c.clear()\n        pb('\\033P$qm\\033\\\\', ('screen_request_capabilities', ord('$'), 'm'))\n        self.ae(c.wtcbuf, b'\\033P1$rm\\033\\\\')\n        for sgr in '0;34;102;1;2;3;4 0;38:5:200;58:2:10:11:12'.split():\n            expected = set(sgr.split(';'))\n            c.clear()\n            parse_bytes(s, f'\\033[{sgr}m\\033P$qm\\033\\\\'.encode('ascii'))\n            r = c.wtcbuf.decode('ascii').partition('r')[2].partition('m')[0]\n            self.ae(expected, set(r.split(';')))\n        c.clear()\n        pb('\\033P$qr\\033\\\\', ('screen_request_capabilities', ord('$'), 'r'))\n        self.ae(c.wtcbuf, f'\\033P1$r{s.margin_top + 1};{s.margin_bottom + 1}r\\033\\\\'.encode('ascii'))\n        pb('\\033P@kitty-cmd{abc\\033\\\\', ('handle_remote_cmd', '{abc'))\n        p = base64_encode('abcd').decode()\n        pb(f'\\033P@kitty-print|{p}\\033\\\\', ('handle_remote_print', p))\n        self.ae(['abcd'], s.callbacks.printbuf)\n\n        c.clear()\n        pb('\\033[?2026$p', ('report_mode_status', 2026, 1))\n        self.ae(c.wtcbuf, b'\\x1b[?2026;2$y')\n        pb('\\033P=1s\\033\\\\', ('screen_start_pending_mode',))\n        c.clear()\n        pb('\\033[?2026$p', ('report_mode_status', 2026, 1))\n        self.ae(c.wtcbuf, b'\\x1b[?2026;1$y')\n        pb('\\033P=2s\\033\\\\', ('screen_stop_pending_mode',))\n        c.clear()\n        pb('\\033[?2026$p', ('report_mode_status', 2026, 1))\n        self.ae(c.wtcbuf, b'\\x1b[?2026;2$y')\n\n\n    def test_oth_codes(self):\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        pb('a\\033_+\\\\+\\033\\\\bcde', ('draw', 'a'), ('Unrecognized APC code: 0x2b',), ('draw', 'bcde'))\n        pb('a\\033^+\\\\+\\033\\\\bcde', ('draw', 'a'), ('Unrecognized PM code: 0x2b',), ('draw', 'bcde'))\n        pb('a\\033X+\\\\+\\033\\\\bcde', ('draw', 'a'), ('Unrecognized SOS code: 0x2b',), ('draw', 'bcde'))\n\n    def test_graphics_command(self):\n        from base64 import standard_b64encode\n\n        def enc(x):\n            return standard_b64encode(x.encode('utf-8') if isinstance(x, str) else x).decode('ascii')\n\n        def c(**k):\n            for p, v in tuple(k.items()):\n                if isinstance(v, str) and p != 'payload':\n                    k[p] = v.encode('ascii')\n            for f in 'action delete_action transmission_type compressed'.split():\n                k.setdefault(f, b'\\0')\n            for f in ('format more id data_sz data_offset width height x_offset y_offset data_height data_width cursor_movement'\n                      ' num_cells num_lines cell_x_offset cell_y_offset z_index placement_id image_number quiet unicode_placement'\n                      ' parent_id parent_placement_id offset_from_parent_x offset_from_parent_y'\n            ).split():\n                k.setdefault(f, 0)\n            p = k.pop('payload', '')\n            k[''] = p\n            return ('graphics_command', k)\n\n        def t(cmd, **kw):\n            pb('\\033_G{};{}\\033\\\\'.format(cmd, enc(kw.get('payload', ''))), c(**kw))\n\n        def e(cmd, err):\n            pb(f'\\033_G{cmd}\\033\\\\', (err,))\n\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        uint32_max = 2**32 - 1\n        t('i=%d' % uint32_max, id=uint32_max)\n        t('i=3,p=4', id=3, placement_id=4)\n        e('i=%d' % (uint32_max + 1), 'Malformed GraphicsCommand control block, number is too large')\n        pb('\\033_Gi=12\\033\\\\', c(id=12))\n        t('a=t,t=d,s=100,z=-9', payload='X', action='t', transmission_type='d', data_width=100, z_index=-9)\n        t('a=t,t=d,s=100,z=9', payload='payload', action='t', transmission_type='d', data_width=100, z_index=9)\n        t('a=t,t=d,s=100,z=9,q=2', action='t', transmission_type='d', data_width=100, z_index=9, quiet=2)\n        e(',s=1', 'Malformed GraphicsCommand control block, invalid key character: 0x2c')\n        e('W=1', 'Malformed GraphicsCommand control block, invalid key character: 0x57')\n        e('1=1', 'Malformed GraphicsCommand control block, invalid key character: 0x31')\n        e('a=t,,w=2', 'Malformed GraphicsCommand control block, invalid key character: 0x2c')\n        e('s', 'Malformed GraphicsCommand control block, no = after key')\n        e('s=', 'Malformed GraphicsCommand control block, expecting an integer value')\n        e('s==', 'Malformed GraphicsCommand control block, expecting an integer value for key: s')\n        e('s=1=', 'Malformed GraphicsCommand control block, expecting a , or semi-colon after a value, found: 0x3d')\n\n    def test_deccara(self):\n        s = self.create_screen()\n        pb = partial(self.parse_bytes_dump, s)\n        pb('\\033[$r', ('deccara', '0;0;0;0;0'))\n        pb('\\033[;;;;4:3;38:5:10;48:2:1:2:3;1$r',\n           ('deccara', '0;0;0;0;4:3'), ('deccara', '0;0;0;0;38:5:10'), ('deccara', '0;0;0;0;48:2:1:2:3'), ('deccara', '0;0;0;0;1'))\n        for y in range(s.lines):\n            line = s.line(y)\n            for x in range(s.columns):\n                c = line.cursor_from(x)\n                self.ae(c.bold, True)\n                self.ae(c.italic, False)\n                self.ae(c.decoration, 3)\n                self.ae(c.fg, (10 << 8) | 1)\n                self.ae(c.bg, (1 << 24 | 2 << 16 | 3 << 8 | 2))\n        self.ae(s.line(0).cursor_from(0).bold, True)\n        pb('\\033[1;2;2;3;22;39$r', ('deccara', '1;2;2;3;22;39'))\n        self.ae(s.line(0).cursor_from(0).bold, True)\n        line = s.line(0)\n        for x in range(1, s.columns):\n            c = line.cursor_from(x)\n            self.ae(c.bold, False)\n            self.ae(c.fg, 0)\n        line = s.line(1)\n        for x in range(0, 3):\n            c = line.cursor_from(x)\n            self.ae(c.bold, False)\n        self.ae(line.cursor_from(3).bold, True)\n        pb('\\033[2*x\\033[3;2;4;3;34$r\\033[*x', ('screen_decsace', 2), ('deccara', '3;2;4;3;34'), ('screen_decsace', 0))\n        for y in range(2, 4):\n            line = s.line(y)\n            for x in range(s.columns):\n                self.ae(line.cursor_from(x).fg, (10 << 8 | 1) if x < 1 or x > 2 else (4 << 8) | 1)\n"
  },
  {
    "path": "kitty_tests/screen.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom kitty.config import defaults\nfrom kitty.fast_data_types import DECAWM, DECCOLM, DECOM, IRM, VT_PARSER_BUFFER_SIZE, Color, ColorProfile, Cursor\nfrom kitty.marks import marker_from_function, marker_from_regex, marker_from_text\nfrom kitty.window import pagerhist\n\nfrom . import BaseTest, draw_multicell, parse_bytes\n\n\nclass TestScreen(BaseTest):\n\n    def test_draw_fast(self):\n        s = self.create_screen()\n\n        # Test in line-wrap, non-insert mode\n        s.draw('a' * 5)\n        self.ae(str(s.line(0)), 'a' * 5)\n        self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)\n        s.draw('b' * 7)\n        self.assertTrue(s.linebuf.is_continued(1))\n        self.assertTrue(s.linebuf.is_continued(2))\n        self.ae(str(s.line(0)), 'a' * 5)\n        self.ae(str(s.line(1)), 'b' * 5)\n        self.ae(str(s.line(2)), 'b' * 2)\n        self.ae(s.cursor.x, 2), self.ae(s.cursor.y, 2)\n        s.draw('c' * 15)\n        self.ae(str(s.line(0)), 'b' * 5)\n        self.ae(str(s.line(1)), 'bbccc')\n\n        # Now test without line-wrap\n        s.reset(), s.reset_dirty()\n        s.reset_mode(DECAWM)\n        s.draw('0123456789')\n        self.ae(str(s.line(0)), '01239')\n        self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)\n        s.draw('ab')\n        self.ae(str(s.line(0)), '0123b')\n        self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)\n\n        # Now test in insert mode\n        s.reset(), s.reset_dirty()\n        s.set_mode(IRM)\n        s.draw('12345' * 5)\n        s.cursor_move(5)\n        self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 4)\n        s.reset_dirty()\n        s.draw('ab')\n        self.ae(str(s.line(4)), 'ab123')\n        self.ae((s.cursor.x, s.cursor.y), (2, 4))\n\n    def test_draw_char(self):\n        # Test in line-wrap, non-insert mode\n        s = self.create_screen()\n        s.draw('ココx')\n        self.ae(str(s.line(0)), 'ココx')\n        self.ae(tuple(map(s.line(0).width, range(5))), (2, 0, 2, 0, 1))\n        self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)\n        s.draw('ニチハ')\n        self.ae(str(s.line(0)), 'ココx')\n        self.ae(str(s.line(1)), 'ニチ')\n        self.ae(str(s.line(2)), 'ハ')\n        self.ae(s.cursor.x, 2), self.ae(s.cursor.y, 2)\n        s.draw('Ƶ̧\\u0308')\n        self.ae(str(s.line(2)), 'ハƵ̧\\u0308')\n        self.ae(s.cursor.x, 3), self.ae(s.cursor.y, 2)\n        s.draw('xy'), s.draw('\\u0306')\n        self.ae(str(s.line(2)), 'ハƵ̧\\u0308xy\\u0306')\n        self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 2)\n        s.draw('c' * 15)\n        self.ae(str(s.line(0)), 'ニチ')\n        s.reset()\n        qt = 'a' * s.columns + '\\u0306'\n        s.draw(qt)\n        self.ae(str(s.line(0)), qt)\n        s.reset()\n        s.draw(qt[:-1]), s.draw(qt[-1])\n        self.ae(str(s.line(0)), qt)\n        s.reset()\n        s.draw(qt[:-1]), s.linefeed(), s.carriage_return(), s.draw(qt[-1])\n        self.ae(str(s.line(0)), qt[:-1])\n\n        # Now test without line-wrap\n        s.reset(), s.reset_dirty()\n        s.reset_mode(DECAWM)\n        s.draw('0\\u030612345\\u03066789\\u0306')\n        self.ae(str(s.line(0)), '0\\u03061239\\u0306')\n        self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)\n        s.draw('ab\\u0306')\n        self.ae(str(s.line(0)), '0\\u0306123b\\u0306')\n        self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)\n\n        # Now test in insert mode\n        s.reset(), s.reset_dirty()\n        text = '1\\u03062345'\n        s.set_mode(IRM)\n        s.draw(text * 5)\n        self.ae(str(s.line(0)), text)\n        s.cursor_move(5)\n        self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 4)\n        s.reset_dirty()\n        s.draw('a\\u0306b')\n        self.ae(str(s.line(4)), 'a\\u0306b1\\u030623')\n        self.ae((s.cursor.x, s.cursor.y), (2, 4))\n\n        # Test drawing of tabs\n        s = self.create_screen(cols=32)\n        txt = 'a\\tb'\n        s.draw(txt)\n        ln = s.line(0)\n        self.ae(txt, ln.as_ansi())\n\n    def test_rep(self):\n        s = self.create_screen()\n        s.draw('a')\n        parse_bytes(s, b'\\x1b[b')\n        self.ae(str(s.line(0)), 'aa')\n        parse_bytes(s, b'\\x1b[3b')\n        self.ae(str(s.line(0)), 'a'*5)\n        s.draw(' ')\n        parse_bytes(s, b'\\x1b[3b')\n        self.ae(str(s.line(1)), ' '*4)\n\n    def test_emoji_skin_tone_modifiers(self):\n        s = self.create_screen()\n        q = chr(0x1f469) + chr(0x1f3fd)\n        s.draw(q)\n        self.ae(str(s.line(0)), q)\n        self.ae(s.cursor.x, 2)\n\n    def test_regional_indicators(self):\n        s = self.create_screen()\n        flag = '\\U0001f1ee\\U0001f1f3'\n        s.draw(flag)\n        self.ae(str(s.line(0)), flag)\n        self.ae(s.cursor.x, 2)\n        s = self.create_screen()\n        s.draw('a'), s.draw(flag[0]), s.draw('b')\n        self.ae(str(s.line(0)), 'a' + flag[0] + 'b')\n        self.ae(s.cursor.x, 4)\n\n    def test_zwj(self):\n        s = self.create_screen(cols=20)\n        q = '\\U0001f468\\u200d\\U0001f469\\u200d\\U0001f467\\u200d\\U0001f466'\n        s.draw(q)\n        self.ae(q, str(s.line(0)))\n        self.ae(s.cursor.x, 2)\n        for x in '\\u200b\\u200c\\u200d':\n            s.reset()\n            q = f'X{x}Y'\n            s.draw(q)\n            self.ae(q, str(s.line(0)))\n            self.ae(s.cursor.x, 2)\n\n    def test_char_manipulation(self):\n        s = self.create_screen()\n\n        def init():\n            s.reset(), s.reset_dirty()\n            s.draw('abcde')\n            s.cursor.bold = True\n            s.cursor_move(4)\n            s.reset_dirty()\n            self.ae(s.cursor.x, 1)\n\n        init()\n        s.insert_characters(2)\n        self.ae(str(s.line(0)), 'a  bc')\n        self.assertTrue(s.line(0).cursor_from(1).bold)\n        s.cursor_move(1)\n        s.insert_characters(20)\n        self.ae(str(s.line(0)), '')\n        s.draw('xココ')\n        s.cursor_move(5)\n        s.reset_dirty()\n        s.insert_characters(1)\n        self.ae(str(s.line(0)), ' xコ')\n        c = Cursor()\n        c.italic = True\n        s.line(0).apply_cursor(c, 0, 5)\n        self.ae(s.line(0).width(2), 2)\n        self.assertTrue(s.line(0).cursor_from(2).italic)\n        self.assertFalse(s.line(0).cursor_from(2).bold)\n\n        init()\n        s.delete_characters(2)\n        self.ae(str(s.line(0)), 'ade')\n        self.assertTrue(s.line(0).cursor_from(4).bold)\n        self.assertFalse(s.line(0).cursor_from(2).bold)\n        s = self.create_screen()\n        s.set_margins(1, 2)\n        s.cursor.y = 3\n        s.draw('abcde')\n        s.cursor.x = 0\n        s.delete_characters(2)\n        self.ae('cde', str(s.line(s.cursor.y)))\n\n        init()\n        s.erase_characters(2)\n        self.ae(str(s.line(0)), 'a  de')\n        self.assertTrue(s.line(0).cursor_from(1).bold)\n        self.assertFalse(s.line(0).cursor_from(4).bold)\n        s.erase_characters(20)\n        self.ae(str(s.line(0)), 'a')\n\n        init()\n        s.erase_in_line()\n        self.ae(str(s.line(0)), 'a')\n        self.assertTrue(s.line(0).cursor_from(1).bold)\n        self.assertFalse(s.line(0).cursor_from(0).bold)\n        init()\n        s.erase_in_line(1)\n        self.ae(str(s.line(0)), '  cde')\n        init()\n        s.erase_in_line(2)\n        self.ae(str(s.line(0)), '')\n        init()\n        s.erase_in_line(2, True)\n        self.ae((False, False, False, False, False), tuple(map(lambda i: s.line(0).cursor_from(i).bold, range(5))))\n\n    def test_erase_in_screen(self):\n        s = self.create_screen()\n\n        def init():\n            s.reset()\n            s.draw('12345' * 5)\n            s.reset_dirty()\n            s.cursor.x, s.cursor.y = 2, 1\n            s.cursor.bold = True\n            self.ae(continuations(s), (True, True, True, True, False))\n\n        def all_lines(s):\n            return tuple(str(s.line(i)) for i in range(s.lines))\n\n        def continuations(s):\n            return tuple(s.line(i).last_char_has_wrapped_flag() for i in range(s.lines))\n\n        init()\n        s.erase_in_display(0)\n        self.ae(all_lines(s), ('12345', '12', '', '', ''))\n        self.ae(continuations(s), (True, False, False, False, False))\n\n        init()\n        s.erase_in_display(1)\n        self.ae(all_lines(s), ('', '   45', '12345', '12345', '12345'))\n        self.ae(continuations(s), (False, True, True, True, False))\n\n        init()\n        s.erase_in_display(2)\n        self.ae(all_lines(s), ('', '', '', '', ''))\n        self.assertTrue(s.line(0).cursor_from(1).bold)\n        self.ae(continuations(s), (False, False, False, False, False))\n\n        init()\n        s.erase_in_display(2, True)\n        self.ae(all_lines(s), ('', '', '', '', ''))\n        self.ae(continuations(s), (False, False, False, False, False))\n        self.assertFalse(s.line(0).cursor_from(1).bold)\n\n    def test_cursor_movement(self):\n        s = self.create_screen()\n        s.draw('12345' * 5)\n        s.reset_dirty()\n        s.cursor_up(2)\n        self.ae((s.cursor.x, s.cursor.y), (4, 2))\n        s.cursor_up1()\n        self.ae((s.cursor.x, s.cursor.y), (0, 1))\n        s.cursor_forward(3)\n        self.ae((s.cursor.x, s.cursor.y), (3, 1))\n        s.cursor_move()\n        self.ae((s.cursor.x, s.cursor.y), (2, 1))\n        s.cursor_down()\n        self.ae((s.cursor.x, s.cursor.y), (2, 2))\n        s.cursor_down1(5)\n        self.ae((s.cursor.x, s.cursor.y), (0, 4))\n\n        s = self.create_screen()\n        s.draw('12345' * 5)\n        s.index()\n        self.ae(str(s.line(4)), '')\n        for i in range(4):\n            self.ae(str(s.line(i)), '12345')\n        s.draw('12345' * 5)\n        s.cursor_up(5)\n        s.reverse_index()\n        self.ae(str(s.line(0)), '')\n        for i in range(1, 5):\n            self.ae(str(s.line(i)), '12345')\n\n    def test_backspace_wide_characters(self):\n        s = self.create_screen()\n        s.draw('⛅')\n        self.ae(s.cursor.x, 2)\n        s.backspace()\n        s.draw(' ')\n        s.backspace()\n        self.ae(s.cursor.x, 1)\n\n    def test_resize(self):\n        from kitty.window import as_text\n        def at():\n            return as_text(s, add_history=True)\n        def ac():\n            return s.line(s.cursor.y)[s.cursor.x]\n\n        # test that a wrapped line split by the history buffer is re-stitched\n        s = self.create_screen(cols=4, lines=4, scrollback=4)\n        text = ''\n        for i in range(s.lines + 1):\n            if i == 2:\n                text += 'abcd'\n            else:\n                text += str(i + 1) * s.columns\n        s.draw(text)\n        self.assertTrue(s.historybuf.endswith_wrap())\n        s.cursor.x, s.cursor.y = 1, 1\n        self.ae(ac(), 'b')\n        s.resize(s.lines, s.columns + 2)\n        self.assertTrue(s.historybuf.endswith_wrap())\n        self.ae(str(s.historybuf), '111122')\n        self.ae(at(), text + '\\n')\n        self.ae((s.cursor.x, s.cursor.y), (3, 0))\n        self.ae(ac(), 'b')\n        s = self.create_screen(cols=4, lines=4, scrollback=4)\n        s.draw('1111222'), s.linefeed(), s.carriage_return()\n        s.draw('333344445555')\n        s.resize(s.lines, s.columns + 2)\n        self.ae(str(s.historybuf), '111122')\n        self.ae(str(s.line(0)), '2')\n        self.ae(at(), '1111222\\n333344445555\\n')\n        s = self.create_screen(cols=4, lines=4, scrollback=4)\n        s.draw('1111😸2'), s.linefeed(), s.carriage_return()\n        s.index(), s.index()\n        s.resize(s.lines, s.columns + 1)\n        self.ae(str(s.historybuf), '1111')\n        self.assertTrue(s.historybuf.endswith_wrap())\n        self.ae(at(), '1111😸2\\n\\n\\n')\n        s = self.create_screen(cols=4, lines=4, scrollback=4)\n        s.draw(text)\n        s.cursor.x, s.cursor.y = 1, 1\n        self.ae(ac(), 'b')\n        s.resize(s.lines, s.columns * 2)\n        self.ae(ac(), 'b')\n        self.ae(str(s.historybuf), '11112222')\n        self.ae(at(), text + '\\n\\n')\n        self.ae((s.cursor.x, s.cursor.y), (1, 0))\n\n        # test that trailing blank line is preserved on resize\n        s = self.create_screen(cols=5, lines=5, scrollback=15)\n        for i in range(3):\n            s.draw(f'oo{i}'), s.index(), s.carriage_return()\n        s.draw('$ pp'), s.index(), s.carriage_return()\n        s.resize(s.lines, 2)\n        self.assertFalse(str(s.line(s.cursor.y)))\n        self.assertFalse(s.cursor.x)\n        # test that only happens when last line is not continued\n        s = self.create_screen(cols=5, lines=5, scrollback=15)\n        for i in range(3):\n            s.draw(f'oo{i}'), s.index(), s.carriage_return()\n        s.draw('p' * (s.columns + 2)), s.carriage_return()\n        s.resize(s.lines, 2)\n        self.assertTrue(str(s.line(s.cursor.y)))\n        s = self.create_screen(cols=5, lines=5, scrollback=15)\n        s.draw('12345'), s.carriage_return(), s.index()\n        s.resize(s.lines, s.columns - 1)\n        self.ae(('1234', '5', ''), tuple(str(s.line(i)) for i in range(s.cursor.y+1)))\n\n        s = self.create_screen(scrollback=6)\n        s.draw(''.join([str(i) * s.columns for i in range(s.lines)]))\n        s.resize(3, 10)\n        self.ae(str(s.line(0)), '0'*5 + '1'*5)\n        self.ae(str(s.line(1)), '2'*5 + '3'*5)\n        self.ae(str(s.line(2)), '4'*5)\n        s.resize(5, 1)\n        self.ae(str(s.line(0)), '4')\n        self.ae(str(s.historybuf), '3\\n3\\n3\\n3\\n3\\n2')\n        s = self.create_screen(scrollback=20)\n        s.draw(''.join(str(i) * s.columns for i in range(s.lines*2)))\n        self.ae(str(s.linebuf), '55555\\n66666\\n77777\\n88888\\n99999')\n        before = at()\n        s.resize(5, 2)\n        self.ae(before, at())\n        self.ae(str(s.linebuf), '88\\n88\\n89\\n99\\n99')\n        s = self.create_screen()\n        s.draw('a' * s.columns)\n        s.linefeed(), s.carriage_return()\n        s.draw('bb')\n        s.resize(s.lines, s.columns - 2)\n        self.ae(str(s.linebuf), 'aaa\\naa\\nbb\\n\\n')\n        s.cursor.y = s.cursor.x = 0\n        s.draw('x' * len(str(s.line(0))))\n        s.linefeed(), s.carriage_return()\n        s.draw('x' * len(str(s.line(1))))\n        s.resize(s.lines, s.columns + 4)\n        self.ae(str(s.linebuf), 'xxx\\nxx\\nbb\\n\\n')\n        s = self.create_screen()\n        c = s.callbacks\n        parse_bytes(s, b'\\x1b[?2048$p')  # ]\n        self.ae(c.wtcbuf, b'\\x1b[?2048;2$y')  # ]\n        c.clear()\n        parse_bytes(s, b'\\x1b[?2048h\\x1b[?2048$p')  # ]]\n        self.ae(c.wtcbuf, b'\\x1b[?2048;1$y')  # ]\n        self.ae(c.num_of_resize_events, 1)\n        parse_bytes(s, b'\\x1b[?2048h')  # ]\n        self.ae(c.num_of_resize_events, 2)\n\n    def test_da1(self):\n        s = self.create_screen()\n        parse_bytes(s, b'\\x1b[c\\x1b[0c')  # ]]\n        self.ae(s.callbacks.da1, ['?62;52;c', '?62;52;c'])  # ]]\n        s.callbacks.clear()\n        self.create_screen(options={'clipboard_control': 'read-clipboard'})\n        parse_bytes(s, b'\\x1b[c')  # ]]\n        self.ae(s.callbacks.da1, ['?62;c'])  # ]]\n\n    def test_cursor_after_resize(self):\n\n        def draw(text, end_line=True):\n            s.draw(text)\n            if end_line:\n                s.linefeed(), s.carriage_return()\n\n        s = self.create_screen()\n        draw('123'), draw('123')\n        y_before = s.cursor.y\n        s.resize(s.lines, s.columns-1)\n        self.ae(y_before, s.cursor.y)\n\n        s = self.create_screen(cols=5, lines=8)\n        draw('one')\n        draw('two three four five |||', end_line=False)\n        s.resize(s.lines + 2, s.columns + 2)\n        y = s.cursor.y\n        self.assertIn('|', str(s.line(y)))\n\n        s = self.create_screen()\n        draw('a')\n        x_before = s.cursor.x\n        s.resize(s.lines - 1, s.columns)\n        self.ae(x_before, s.cursor.x)\n\n        s = self.create_screen()\n        s.draw('abc')\n        b = s.cursor.x\n        s.resize(7, s.columns)\n        self.assertEqual(s.cursor.x, b)\n        s.cursor.x = 0\n        s.resize(5, s.columns)\n        self.assertEqual(s.cursor.x, 0)\n\n    def test_scrollback_fill_after_resize(self):\n        def prepare_screen(content=()):\n            ans = self.create_screen(options={'scrollback_fill_enlarged_window': True})\n            for line in content:\n                ans.draw(line)\n                ans.linefeed()\n                ans.carriage_return()\n            return ans\n\n        def assert_lines(*lines):\n            return self.ae(lines, tuple(str(s.line(i)) for i in range(s.lines)))\n\n        # test the reverse scroll function\n        s = prepare_screen(map(str, range(6)))\n        assert_lines('2', '3', '4', '5', '')\n        s.reverse_scroll(2, True)\n        assert_lines('0', '1', '2', '3', '4')\n\n        # Height increased, width unchanged → pull down lines to fill new space at the top\n        s = prepare_screen(map(str, range(6)))\n        assert_lines('2', '3', '4', '5', '')\n        dist_from_bottom = s.lines - s.cursor.y\n        s.resize(7, s.columns)\n        assert_lines('0', '1', '2', '3', '4', '5', '')\n        self.ae(dist_from_bottom, s.lines - s.cursor.y)\n\n        # Height increased, width increased → rewrap, pull down\n        s = prepare_screen(['0', '1', '2', '3' * 15])\n        assert_lines('2', '33333', '33333', '33333', '')\n        s.resize(7, 12)\n        assert_lines('0', '1', '2', '333333333333', '333', '', '')\n\n        # Height increased, width decreased → rewrap, pull down if possible\n        s = prepare_screen(['0', '1', '2', '3' * 5])\n        assert_lines('0', '1', '2', '33333', '')\n        s.resize(6, 4)\n        assert_lines('0', '1', '2', '3333', '3', '')\n\n        # Height unchanged, width increased → rewrap, pull down if possible\n        s = prepare_screen(['0', '1', '2', '3' * 15])\n        assert_lines('2', '33333', '33333', '33333', '')\n        s.resize(s.lines, 12)\n        assert_lines('1', '2', '333333333333', '333', '')\n\n        # Height decreased, width increased → rewrap, pull down if possible\n        s = prepare_screen(['0', '1', '2', '3' * 15])\n        assert_lines('2', '33333', '33333', '33333', '')\n        s.resize(4, 12)\n        assert_lines('2', '333333333333', '333', '')\n\n        # Height increased with large continued text\n        s = self.create_screen(options={'scrollback_fill_enlarged_window': True})\n        s.draw(('x' * (s.columns * s.lines * 2)) + 'abcde')\n        s.carriage_return(), s.linefeed()\n        s.draw('>')\n        assert_lines('xxxxx', 'xxxxx', 'xxxxx', 'abcde', '>')\n        s.resize(s.lines + 2, s.columns)\n        assert_lines('xxxxx', 'xxxxx', 'xxxxx', 'xxxxx', 'xxxxx', 'abcde', '>')\n\n    def test_tab_stops(self):\n        # Taken from vttest/main.c\n        s = self.create_screen(cols=80, lines=2)\n        s.cursor_position(1, 1)\n        s.clear_tab_stop(3)\n        for col in range(1, s.columns - 1, 3):\n            s.cursor_forward(3)\n            s.set_tab_stop()\n        s.cursor_position(1, 4)\n        for col in range(4, s.columns - 1, 6):\n            s.clear_tab_stop(0)\n            s.cursor_forward(6)\n        s.cursor_position(1, 7)\n        s.clear_tab_stop(2)\n        s.cursor_position(1, 1)\n        for col in range(1, s.columns - 1, 6):\n            s.tab()\n            s.draw('*')\n        s.cursor_position(2, 2)\n        self.ae(str(s.line(0)), '\\t*'*13)\n        s = self.create_screen(cols=4, lines=2)\n        s.draw('aaaX\\tbbbb')\n        self.ae(str(s.line(0)) + str(s.line(1)), 'aaaXbbbb')\n\n    def test_backspace(self):\n        s = self.create_screen()\n        q = 'a'*s.columns\n        def backspace(use_bs=True):\n            if use_bs:  # this is how the kernel implements backspace\n                s.draw('\\x08 \\x08')\n            else:\n                s.cursor_move(1)\n                s.draw(' ')\n                s.cursor_move(1)\n        for use_bs in (True, False):\n            s.reset()\n            s.draw(q)\n            s.draw('b')\n            backspace(use_bs)\n            self.ae(str(s.line(0)), q)\n            self.ae(str(s.line(1)), ' ')\n            self.ae(s.cursor.x, 0)\n            backspace(use_bs)\n            self.ae(str(s.line(0)), q[:-1] + ' ')\n            self.ae(str(s.line(1)), ' ')\n        # Test that CUB does not move cursor onto previous line\n        s.reset()\n        s.draw('a'*s.columns + 'b')\n        self.ae((s.cursor.x, s.cursor.y), (1, 1))\n        parse_bytes(s, b'\\x1b[100D')\n        self.ae((s.cursor.x, s.cursor.y), (0, 1))\n\n    def test_margins(self):\n        # Taken from vttest/main.c\n        s = self.create_screen(cols=80, lines=24)\n\n        def nl():\n            s.carriage_return(), s.linefeed()\n\n        for deccolm in (False, True):\n            if deccolm:\n                s.resize(24, 132)\n                s.set_mode(DECCOLM)\n            else:\n                s.reset_mode(DECCOLM)\n            region = s.lines - 6\n            s.set_margins(3, region + 3)\n            s.set_mode(DECOM)\n            for i in range(26):\n                ch = chr(ord('A') + i)\n                which = i % 4\n                if which == 0:\n                    s.cursor_position(region + 1, 1), s.draw(ch)\n                    s.cursor_position(region + 1, s.columns), s.draw(ch.lower())\n                    nl()\n                elif which == 1:\n                    # Simple wrapping\n                    s.cursor_position(region, s.columns), s.draw(chr(ord('A') + i - 1).lower() + ch)\n                    # Backspace at right margin\n                    s.cursor_position(region + 1, s.columns), s.draw(ch), s.backspace(), s.draw(ch.lower())\n                    nl()\n                elif which == 2:\n                    # Tab to right margin\n                    s.cursor_position(region + 1, s.columns), s.draw(ch), s.backspace(), s.backspace(), s.tab(), s.tab(), s.draw(ch.lower())\n                    s.cursor_position(region + 1, 2), s.backspace(), s.draw(ch), nl()\n                else:\n                    s.cursor_position(region + 1, 1), nl()\n                    s.cursor_position(region, 1), s.draw(ch)\n                    s.cursor_position(region, s.columns), s.draw(ch.lower())\n            for ln in range(2, region + 2):\n                c = chr(ord('I') + ln - 2)\n                before = '\\t' if ln % 4 == 0 else ' '\n                self.ae(c + ' ' * (s.columns - 3) + before + c.lower(), str(s.line(ln)))\n            s.reset_mode(DECOM)\n        # Test that moving cursor outside the margins works as expected\n        s = self.create_screen(10, 10)\n        s.set_margins(4, 6)\n        s.cursor_position(0, 0)\n        self.ae(s.cursor.y, 0)\n        nl()\n        self.ae(s.cursor.y, 1)\n        s.cursor.y = s.lines - 1\n        self.ae(s.cursor.y, 9)\n        s.reverse_index()\n        self.ae(s.cursor.y, 8)\n\n    def test_sgr(self):\n        s = self.create_screen()\n        s.select_graphic_rendition(0, 1, 5, 37, 42)\n        s.draw('a')\n        c = s.line(0).cursor_from(0)\n        self.assertTrue(c.bold)\n        self.assertTrue(c.blink)\n        self.ae(c.bg, (2 << 8) | 1)\n        self.ae('\\x1b[22;1;5;37;42ma', s.line(0).as_ansi())\n        s.cursor_position(2, 1)\n        s.select_graphic_rendition(0, 35)\n        s.draw('b')\n        c = s.line(1).cursor_from(0)\n        self.ae(c.fg, (5 << 8) | 1)\n        self.ae(c.bg, 0)\n        s.cursor_position(2, 2)\n        s.select_graphic_rendition(38, 2, 99, 1, 2, 3)\n        s.draw('c')\n        c = s.line(1).cursor_from(1)\n        self.ae(c.fg, (1 << 24) | (2 << 16) | (3 << 8) | 2)\n\n    def test_cursor_hidden(self):\n        s = self.create_screen()\n        s.toggle_alt_screen()\n        s.cursor_visible = False\n        s.toggle_alt_screen()\n        self.assertFalse(s.cursor_visible)\n\n    def test_dirty_lines(self):\n        s = self.create_screen()\n        self.assertFalse(s.linebuf.dirty_lines())\n        s.draw('a' * (s.columns * 2))\n        self.ae(s.linebuf.dirty_lines(), [0, 1])\n        self.assertFalse(s.historybuf.dirty_lines())\n        while not s.historybuf.count:\n            s.draw('a' * (s.columns * 2))\n        self.ae(s.historybuf.dirty_lines(), list(range(s.historybuf.count)))\n        self.ae(s.linebuf.dirty_lines(), list(range(s.lines)))\n        s.cursor.x, s.cursor.y = 0, 1\n        s.insert_lines(2)\n        self.ae(s.linebuf.dirty_lines(), [0, 3, 4])\n        s.draw('a' * (s.columns * s.lines))\n        self.ae(s.linebuf.dirty_lines(), list(range(s.lines)))\n        s.cursor.x, s.cursor.y = 0, 1\n        s.delete_lines(2)\n        self.ae(s.linebuf.dirty_lines(), [0, 1, 2])\n\n        s = self.create_screen()\n        self.assertFalse(s.linebuf.dirty_lines())\n        s.erase_in_line(0, False)\n        self.ae(s.linebuf.dirty_lines(), [0])\n        s.index(), s.index()\n        s.erase_in_display(0, False)\n        self.ae(s.linebuf.dirty_lines(), [0, 2, 3, 4])\n\n        s = self.create_screen()\n        self.assertFalse(s.linebuf.dirty_lines())\n        s.insert_characters(2)\n        self.ae(s.linebuf.dirty_lines(), [0])\n        s.cursor.y = 1\n        s.delete_characters(2)\n        self.ae(s.linebuf.dirty_lines(), [0, 1])\n        s.cursor.y = 2\n        s.erase_characters(2)\n        self.ae(s.linebuf.dirty_lines(), [0, 1, 2])\n\n    def test_selection_as_text(self):\n        s = self.create_screen()\n        for i in range(2 * s.lines):\n            if i != 0:\n                s.carriage_return(), s.linefeed()\n            s.draw(str(i) * s.columns)\n        s.start_selection(0, 0)\n        s.update_selection(4, 4)\n\n        def ts(*args):\n            return ''.join(s.text_for_selection(*args))\n\n        expected = ''.join(('55555', '\\n66666', '\\n77777', '\\n88888', '\\n99999'))\n        self.ae(ts(), expected)\n        self.ae(ts(True), expected)\n        s.scroll(2, True)\n        self.ae(ts(), expected)\n        self.ae(ts(True), expected)\n        s.reset()\n        s.draw('ab   cd')\n        s.start_selection(0, 0)\n        s.update_selection(1, 3)\n        self.ae(ts(), ''.join(('ab   ', 'cd')))\n        self.ae(ts(False, True), ''.join(('ab', 'cd')))\n        s.reset()\n        s.draw('ab        cd')\n        s.start_selection(0, 0)\n        s.update_selection(3, 4)\n        self.ae(s.text_for_selection(), ('ab   ', '     ', 'cd'))\n        self.ae(s.text_for_selection(False, True), ('ab', '\\n', 'cd'))\n        s.reset()\n        s.draw('a')\n        s.select_graphic_rendition(32)\n        s.draw('b')\n        s.select_graphic_rendition(39)\n        s.draw('c  xy')\n        s.start_selection(0, 0)\n        s.update_selection(1, 3)\n        self.ae(s.text_for_selection(), ('abc  ', 'xy'))\n        self.ae(s.text_for_selection(True), ('a\\x1b[32mb\\x1b[39mc  ', 'xy', '\\x1b[m'))\n        self.ae(s.text_for_selection(True, True), ('a\\x1b[32mb\\x1b[39mc', 'xy', '\\x1b[m'))\n        # ]]]]]]]]]]]]]]]]]]]]\n        s.reset()\n        s.draw('a'), s.carriage_return(), s.linefeed(), s.linefeed(), s.draw('b')\n        s.start_selection(0, 0)\n        s.update_selection(4, 4)\n        self.ae(''.join(s.text_for_selection()), 'a\\n\\nb')\n        self.ae(''.join(s.text_for_selection(True)), 'a\\n\\nb')\n\n    def test_soft_hyphen(self):\n        s = self.create_screen()\n        s.draw('a\\u00adb')\n        self.ae(s.cursor.x, 2)\n        s.start_selection(0, 0)\n        s.update_selection(2, 0)\n        self.ae(s.text_for_selection(), ('a\\u00adb',))\n\n    def test_variation_selectors(self):\n        s = self.create_screen(cols=3)\n        q = '*\\ufe0f'\n        s.draw(q*(s.columns+1))\n        self.ae(str(s.line(0)), q*(s.columns//2))\n        s = self.create_screen(cols=8)\n        def widths(text, *widths):\n            s.reset()\n            s.draw(text)\n            def w(x):\n                c = s.cpu_cells(0, x)\n                return (c['mcd'] or {'width': 1})['width']\n            actual = tuple(w(x) for x in range(len(widths)))\n            self.ae(widths, actual)\n        widths('\\u4e00\\u4e00\\u26ab\\ufe0e', 2, 2, 2, 2, 1)\n\n        s = self.create_screen()\n        def tt(text_to_draw):\n            s.reset()\n            s.draw(text_to_draw)\n            self.ae(str(s.line(0)), text_to_draw)\n        tt('abc\\U0001f44d\\ufe0ed')\n\n        def t(*a):\n            s.reset()\n            for i in range(0, len(a), 2):\n                char, x = a[i], a[i+1]\n                s.draw(char)\n                self.ae(s.cursor.x, x, f'after char: {char!r}')\n        # already wide + VS15\n        t('\\U0001f610', 2, '\\ufe0e', 1, '\\ufe0e', 1)\n        t('\\U0001f610\\ufe0e', 1, '\\ufe0e', 1)\n        # narrow + VS16\n        t('\\u25b6', 1, '\\ufe0f', 2)\n        t('\\u25b6\\ufe0f', 2)\n        # wide + VS16\n        t('\\u26d4\\ufe0f', 2, '\\ufe0f', 2)\n        t('\\u26d4', 2, '\\ufe0f', 2)\n        # narrow + VS15\n        t('\\u25b6', 1, '\\ufe0e', 1)\n        t('\\u25b6\\ufe0e', 1)\n        # narrow + VS16 + VS15\n        t('\\u25b6', 1, '\\ufe0f', 2, '\\ufe0e', 2)\n        # wide + VS15 + VS16\n        t('\\U0001f610', 2, '\\ufe0e', 1, '\\ufe0f', 1)\n        # large numbers of combining chars\n        s.reset()\n        s.draw(\"\\N{HEAVY EXCLAMATION MARK SYMBOL}\" + 4500 * \"\\N{VARIATION SELECTOR-16}\")\n\n    def test_writing_with_cursor_on_trailer_of_wide_character(self):\n        s = self.create_screen()\n        def r(x, pos, expected):\n            s.reset()\n            s.draw('😸')\n            self.ae(s.cursor.x, 2)\n            s.cursor.x = 1\n            s.draw(x)\n            self.ae(s.cursor.x, pos)\n            self.ae(str(s.line(0)), expected)\n\n        r('a', 2, ' a')\n        r('😸', 3, ' 😸')\n        r('\\u0304', 1, '😸\\u0304')\n        r('\\r', 0, '😸')\n\n\n    def test_serialize(self):\n        from kitty.window import as_text\n        sgr0 = '\\x1b[m'  # ]\n        s = self.create_screen()\n        parse_bytes(s, b'\\x1b[1;91m')  # ]\n        s.draw('X')\n        parse_bytes(s, b'\\x1b[0m\\x1b[2m')  # ]]\n        s.draw('Y')\n        self.ae(as_text(s, True), f'{sgr0}\\x1b[22;1;91mX\\x1b[22;2;39mY\\n\\n\\n\\n')  # ]]\n\n        s.reset()\n        s.draw('ab' * s.columns)\n        s.carriage_return(), s.linefeed()\n        s.draw('c')\n\n        self.ae(as_text(s), 'ababababab\\nc\\n\\n')\n        self.ae(as_text(s, True), f'{sgr0}ababa{sgr0}babab\\n{sgr0}c\\n\\n')\n\n        s = self.create_screen(cols=2, lines=2, scrollback=2)\n        for i in range(1, 7):\n            s.select_graphic_rendition(30 + i)\n            s.draw(f'{i}' * s.columns)\n        self.ae(as_text(s, True, True), f'{sgr0}\\x1b[31m11{sgr0}\\x1b[32m22{sgr0}\\x1b[33m33{sgr0}\\x1b[34m44{sgr0}{sgr0}\\x1b[35m55{sgr0}\\x1b[36m66')\n        # ]]]]]]]]]]]]]]]]]]]]]\n\n        def hl(url='', id=''):\n            return '\\x1b]8;{};{}\\x1b\\\\'.format(f'id={id}' if id else '', url or '')\n\n        def set_link(url=None, id=None):\n            parse_bytes(s, hl(url, id).encode('utf-8'))\n\n        s = self.create_screen()\n        set_link('moo')\n        s.draw('X')\n        set_link()\n        self.ae(as_text(s, True), f'{sgr0}{hl(\"moo\")}X{hl()}\\n\\n\\n\\n')\n        s.reset()\n        set_link('moo')\n        s.draw('X'*s.columns)\n        set_link()\n        self.ae(as_text(s, True), f'{sgr0}{hl(\"moo\")}XXXXX\\n{sgr0}{hl()}\\n\\n\\n')\n\n        s.reset()\n        s.draw('a')\n        set_link('moo', 'foo')\n        s.draw('bcdef')\n        self.ae(as_text(s, True), f'{sgr0}a{hl(\"moo\", \"foo\")}bcde{sgr0}f{hl()}\\n\\n\\n')\n        set_link()\n        s.draw('gh')\n        self.ae(as_text(s, True), f'{sgr0}a{hl(\"moo\", \"foo\")}bcde{sgr0}f{hl()}gh\\n\\n\\n')\n        s.reset()\n        s.draw('a')\n        set_link('moo')\n        s.draw('bcdef')\n        self.ae(as_text(s, True), f'{sgr0}a{hl(\"moo\")}bcde{sgr0}f{hl()}\\n\\n\\n')\n\n    def test_wrapping_serialization(self):\n        from kitty.window import as_text\n        s = self.create_screen(cols=2, lines=2, scrollback=2, options={'scrollback_pager_history_size': 128})\n        s.draw('ū̀abbccddeefū̀')\n        self.ae(as_text(s, add_history=True), 'ū̀abbccddeefū̀')\n        self.assertNotIn('\\n', as_text(s, add_history=True, as_ansi=True))\n\n        s = self.create_screen(cols=2, lines=2, scrollback=2, options={'scrollback_pager_history_size': 128})\n        s.draw('1'), s.carriage_return(), s.linefeed()\n        s.draw('2'), s.carriage_return(), s.linefeed()\n        s.draw('3'), s.carriage_return(), s.linefeed()\n        s.draw('4'), s.carriage_return(), s.linefeed()\n        s.draw('5'), s.carriage_return(), s.linefeed()\n        s.draw('6'), s.carriage_return(), s.linefeed()\n        s.draw('7')\n        self.ae(as_text(s, add_history=True), '1\\n2\\n3\\n4\\n5\\n6\\n7')\n\n        s = self.create_screen(cols=2, lines=2, scrollback=2, options={'scrollback_pager_history_size': 128})\n        s.draw('aabb')\n        s.cursor.y = 0\n        s.carriage_return(), s.linefeed()\n        self.ae(as_text(s, add_history=True), 'aabb')\n\n        s = self.create_screen(cols=2, lines=2, scrollback=2, options={'scrollback_pager_history_size': 128})\n        s.draw('a'), s.carriage_return(), s.linefeed()\n        s.cursor.y = 0\n        s.draw('aabb')\n        self.ae(as_text(s), 'aabb')\n        s = self.create_screen(cols=2, lines=2, scrollback=2, options={'scrollback_pager_history_size': 128})\n        s.draw('a😀')\n        self.ae(as_text(s), 'a😀')\n\n    def test_pagerhist(self):\n        hsz = 8\n        s = self.create_screen(cols=2, lines=2, scrollback=2, options={'scrollback_pager_history_size': hsz})\n\n        def contents():\n            return s.historybuf.pagerhist_as_text()\n\n        def line(i):\n            q.append('\\x1b[m' + f'{i}' * s.columns + '\\r')\n\n        def w(x):\n            s.historybuf.pagerhist_write(x)\n\n        def test():\n            expected = ''.join(q)\n            maxlen = hsz\n            extra = len(expected) - maxlen\n            if extra > 0:\n                expected = expected[extra:]\n            got = contents()\n            self.ae(got, expected)\n\n        q = []\n        for i in range(4):\n            s.draw(f'{i}' * s.columns)\n        self.ae(contents(), '')\n        s.draw('4' * s.columns), line(0), test()\n        s.draw('5' * s.columns), line(1), test()\n        s.draw('6' * s.columns), line(2), test()\n        s.draw('7' * s.columns), line(3), test()\n        s.draw('8' * s.columns), line(4), test()\n        s.draw('9' * s.columns), line(5), test()\n\n        s = self.create_screen(options={'scrollback_pager_history_size': 2048})\n        text = '\\x1b[msoft\\r\\x1b[mbreak\\nnext😼cat'\n        w(text)\n        self.ae(contents(), text)\n        s.historybuf.pagerhist_rewrap(2)\n        self.ae(contents(), '\\x1b[mso\\rft\\x1b[m\\rbr\\rea\\rk\\nne\\rxt\\r😼\\rca\\rt')\n\n        s = self.create_screen(options={'scrollback_pager_history_size': 8})\n        w('😼')\n        self.ae(contents(), '😼')\n        w('abcd')\n        self.ae(contents(), '😼abcd')\n        w('e')\n        self.ae(contents(), 'abcde')\n\n    def test_user_marking(self):\n\n        def cells(*a, y=0, mark=3):\n            return [(x, y, mark) for x in a]\n\n        s = self.create_screen()\n        s.draw('abaa')\n        s.carriage_return(), s.linefeed()\n        s.draw('xyxyx')\n        s.set_marker(marker_from_regex('a', 3))\n        self.ae(s.marked_cells(), cells(0, 2, 3))\n        s.set_marker()\n        self.ae(s.marked_cells(), [])\n\n        def mark_x(text):\n            col = 0\n            for i, c in enumerate(text):\n                if c == 'x':\n                    col += 1\n                    yield i, i, col\n\n        s.set_marker(marker_from_function(mark_x))\n        self.ae(s.marked_cells(), [(0, 1, 1), (2, 1, 2), (4, 1, 3)])\n        s = self.create_screen(lines=5, scrollback=10)\n        for i in range(15):\n            s.draw(str(i))\n            if i != 14:\n                s.carriage_return(), s.linefeed()\n        s.set_marker(marker_from_regex(r'\\d+', 3))\n        for i in range(10):\n            self.assertTrue(s.scroll_to_next_mark())\n            self.ae(s.scrolled_by, i + 1)\n        self.ae(s.scrolled_by, 10)\n        for i in range(10):\n            self.assertTrue(s.scroll_to_next_mark(0, False))\n            self.ae(s.scrolled_by, 10 - i - 1)\n        self.ae(s.scrolled_by, 0)\n\n        s = self.create_screen()\n        s.draw('🐈ab')\n        s.set_marker(marker_from_regex('🐈', 3))\n        self.ae(s.marked_cells(), cells(0, 1))\n        s.set_marker(marker_from_regex('🐈a', 3))\n        self.ae(s.marked_cells(), cells(0, 1, 2))\n        s.set_marker(marker_from_regex('a', 3))\n        self.ae(s.marked_cells(), cells(2))\n        s = self.create_screen(cols=20)\n        s.tab()\n        s.draw('ab')\n        s.set_marker(marker_from_regex('a', 3))\n        self.ae(s.marked_cells(), cells(8))\n        s.set_marker(marker_from_regex('\\t', 3))\n        self.ae(s.marked_cells(), cells(*range(8)))\n        s = self.create_screen()\n        s.cursor.x = 2\n        s.draw('x')\n        s.cursor.x += 1\n        s.draw('x')\n        s.set_marker(marker_from_function(mark_x))\n        self.ae(s.marked_cells(), [(2, 0, 1), (4, 0, 2)])\n        # Test CJK/wide characters not at position 0 (issue #9705)\n        s = self.create_screen(cols=20)\n        s.draw('テスト世界')\n        s.set_marker(marker_from_regex('テ', 3))\n        self.ae(s.marked_cells(), cells(0, 1))\n        s.set_marker(marker_from_regex('世', 3))\n        self.ae(s.marked_cells(), cells(6, 7))\n        s.set_marker(marker_from_text('世界', 3))\n        self.ae(s.marked_cells(), cells(6, 7, 8, 9))\n        s = self.create_screen(cols=20)\n        s.draw('ABテCD世EF')\n        s.set_marker(marker_from_regex('テ', 3))\n        self.ae(s.marked_cells(), cells(2, 3))\n        s.set_marker(marker_from_regex('世', 3))\n        self.ae(s.marked_cells(), cells(6, 7))\n\n    def test_hyperlinks(self):\n        s = self.create_screen()\n        self.ae(s.line(0).hyperlink_ids(), tuple(0 for x in range(s.columns)))\n\n        def set_link(url=None, id=None):\n            parse_bytes(s, '\\x1b]8;id={};{}\\x1b\\\\'.format(id or '', url or '').encode('utf-8'))\n\n        set_link('wide-chars', 'XX')\n        self.ae(s.line(0).hyperlink_ids(), tuple(0 for x in range(s.columns)))\n        s.draw('状')\n        self.ae(s.line(0).hyperlink_ids(), (1, 1) + tuple(0 for x in range(s.columns - 2)))\n        set_link()\n\n        s = self.create_screen()\n        set_link('url-a', 'a')\n        self.ae(s.line(0).hyperlink_ids(), tuple(0 for x in range(s.columns)))\n        s.draw('a')\n        self.ae(s.line(0).hyperlink_ids(), (1,) + tuple(0 for x in range(s.columns - 1)))\n        s.draw('bc')\n        self.ae(s.line(0).hyperlink_ids(), (1, 1, 1, 0, 0))\n        set_link()\n        s.draw('d')\n        self.ae(s.line(0).hyperlink_ids(), (1, 1, 1, 0, 0))\n        set_link('url-a', 'a')\n        s.draw('efg')\n        self.ae(s.line(0).hyperlink_ids(), (1, 1, 1, 0, 1))\n        self.ae(s.line(1).hyperlink_ids(), (1, 1, 0, 0, 0))\n        set_link('url-b')\n        s.draw('hij')\n        self.ae(s.line(1).hyperlink_ids(), (1, 1, 2, 2, 2))\n        set_link()\n        self.ae({('a:url-a', 1), (':url-b', 2)}, s.hyperlinks_as_set())\n        s.garbage_collect_hyperlink_pool()\n        self.ae({('a:url-a', 1), (':url-b', 2)}, s.hyperlinks_as_set())\n        for i in range(s.lines + 2):\n            s.linefeed()\n        s.garbage_collect_hyperlink_pool()\n        self.ae({('a:url-a', 1), (':url-b', 2)}, s.hyperlinks_as_set())\n        for i in range(s.lines * 2):\n            s.linefeed()\n        s.garbage_collect_hyperlink_pool()\n        self.assertFalse(s.hyperlinks_as_set())\n        set_link('url-a', 'x')\n        s.draw('a')\n        set_link('url-a', 'y')\n        s.draw('a')\n        set_link()\n        self.ae({('x:url-a', 1), ('y:url-a', 2)}, s.hyperlinks_as_set())\n\n        s = self.create_screen()\n        set_link('u' * 2048)\n        s.draw('a')\n        self.ae({(':' + 'u' * 2045, 1)}, s.hyperlinks_as_set())\n        s = self.create_screen()\n        set_link('u' * 2048, 'i' * 300)\n        s.draw('a')\n        self.ae({('i'*256 + ':' + 'u' * (2045 - 256), 1)}, s.hyperlinks_as_set())\n\n        s = self.create_screen()\n        set_link('1'), s.draw('1')\n        set_link('2'), s.draw('2')\n        set_link('3'), s.draw('3')\n        s.cursor.x = 1\n        set_link(), s.draw('X')\n        self.ae(s.line(0).hyperlink_ids(), (1, 0, 3, 0, 0))\n        self.ae({(':1', 1), (':2', 2), (':3', 3)}, s.hyperlinks_as_set())\n        s.garbage_collect_hyperlink_pool()\n        self.ae({(':1', 1), (':3', 2)}, s.hyperlinks_as_set())\n        set_link('3'), s.draw('3')\n        self.ae({(':1', 1), (':3', 2)}, s.hyperlinks_as_set())\n        set_link('4'), s.draw('4')\n        self.ae({(':1', 1), (':3', 2), (':4', 3)}, s.hyperlinks_as_set())\n\n        s = self.create_screen()\n        set_link('1'), s.draw('1')\n        set_link('2'), s.draw('2')\n        set_link('1'), s.draw('1')\n        self.ae({(':2', 2), (':1', 1)}, s.hyperlinks_as_set())\n\n        s = self.create_screen()\n        set_link('1'), s.draw('12'), set_link(), s.draw('X'), set_link('1'), s.draw('3')\n        s.linefeed(), s.carriage_return()\n        s.draw('abc')\n        s.linefeed(), s.carriage_return()\n        set_link(), s.draw('Z ')\n        set_link('1'), s.draw('xyz')\n        s.linefeed(), s.carriage_return()\n        set_link('2'), s.draw('Z Z')\n        self.assertIsNone(s.current_url_text())\n        self.assertIsNone(s.hyperlink_at(0, 4))\n        self.assertIsNone(s.current_url_text())\n        self.ae(s.hyperlink_at(0, 0), '1')\n        self.ae(s.current_url_text(), '123abcxyz')\n        self.ae('1', s.hyperlink_at(3, 2))\n        self.ae(s.current_url_text(), '123abcxyz')\n        self.ae('2', s.hyperlink_at(1, 3))\n        self.ae(s.current_url_text(), 'Z Z')\n\n    def test_bottom_margin(self):\n        s = self.create_screen(cols=80, lines=6, scrollback=4)\n        s.set_margins(0, 5)\n        for i in range(8):\n            s.draw(str(i))\n            s.linefeed()\n            s.carriage_return()\n\n        self.ae(str(s.linebuf), '4\\n5\\n6\\n7\\n\\n')\n        self.ae(str(s.historybuf), '3\\n2\\n1\\n0')\n\n    def test_top_margin(self):\n        s = self.create_screen(cols=80, lines=6, scrollback=4)\n        s.set_margins(2, 6)\n        for i in range(8):\n            s.draw(str(i))\n            s.linefeed()\n            s.carriage_return()\n\n        self.ae(str(s.linebuf), '0\\n4\\n5\\n6\\n7\\n')\n        self.ae(str(s.historybuf), '')\n\n    def test_top_and_bottom_margin(self):\n        s = self.create_screen(cols=80, lines=6, scrollback=4)\n        s.set_margins(2, 5)\n        for i in range(8):\n            s.draw(str(i))\n            s.linefeed()\n            s.carriage_return()\n\n        self.ae(str(s.linebuf), '0\\n5\\n6\\n7\\n\\n')\n        self.ae(str(s.historybuf), '')\n\n    def test_osc_52(self):\n        s = self.create_screen()\n        c = s.callbacks\n\n        def send(what: str):\n            return parse_bytes(s, f'\\033]52;p;{what}\\a'.encode('ascii'))\n\n        def t(q, *expected):\n            c.clear()\n            send(q)\n            del q\n            t.ex = list(expected)\n            del expected\n            try:\n                self.ae(tuple(map(len, c.cc_buf)), tuple(map(len, t.ex)))\n                self.ae(c.cc_buf, t.ex)\n            finally:\n                del t.ex\n\n        t('XYZ', ('p;XYZ', False))\n        t('a' * VT_PARSER_BUFFER_SIZE, ('p;' + 'a' * (VT_PARSER_BUFFER_SIZE - 8), True), (';' + 'a' * 8, False))\n        t('', ('p;', False))\n        t('!', ('p;!', False))\n\n    def test_key_encoding_flags_stack(self):\n        s = self.create_screen()\n        c = s.callbacks\n\n        def w(code, p1='', p2=''):\n            p = f'{p1}'\n            if p2:\n                p += f';{p2}'\n            return parse_bytes(s, f'\\033[{code}{p}u'.encode('ascii'))\n\n        def ac(flags):\n            parse_bytes(s, b'\\033[?u')\n            self.ae(c.wtcbuf, f'\\033[?{flags}u'.encode('ascii'))\n            c.clear()\n\n        ac(0)\n        w('=', 0b1001)\n        ac(0b1001)\n        w('=', 0b0011, 2)\n        ac(0b1011)\n        w('=', 0b0110, 3)\n        ac(0b1001)\n        s.reset()\n        ac(0)\n\n        w('>', 0b0011)\n        ac(0b0011)\n        w('=', 0b1111)\n        ac(0b1111)\n        w('>', 0b10)\n        ac(0b10)\n        w('<')\n        ac(0b1111)\n        for i in range(10):\n            w('<')\n            ac(0)\n        s.reset()\n\n        for i in range(1, 16):\n            w('>', i)\n        ac(15)\n        w('<'), ac(14), w('<'), ac(13)\n\n    def test_color_stack(self):\n        s = self.create_screen()\n        c = s.callbacks\n\n        def w(code):\n            return parse_bytes(s, ('\\033[' + code).encode('ascii'))\n\n        def ac(idx, count):\n            self.ae(c.wtcbuf, f'\\033[{idx};{count}#Q'.encode('ascii'))\n            c.clear()\n        # ]]]]]]]]]]]]]]]]}}}}}}}}}}}}}}}}))))))))))))))))))))))\n\n        w('#R')\n        ac(0, 0)\n\n        w('#P')\n        w('#R')\n        ac(0, 1)\n        w('10#P')\n        w('#R')\n        ac(0, 1)\n        w('#Q')\n        w('#R')\n        ac(0, 0)\n        for i in range(20):\n            w('#P')\n        w('#R')\n        ac(9, 10)\n\n    def test_detect_url(self):\n        detect_url(self)\n        detect_url(self, scale=2)\n\n    def test_prompt_marking(self):\n        # ]]]]]]]]]]]]]]]]}}}}}}}}}}}}}}}}))))))))))))))))))))))\n\n        def mark_prompt():\n            parse_bytes(s, b'\\033]133;A\\007')\n\n        def mark_output():\n            parse_bytes(s, b'\\033]133;C\\007')\n\n        def draw_prompt(x):\n            mark_prompt(), s.draw(f'$ {x}'), s.carriage_return(), s.index()\n\n        def draw_output(n, x='', m=True):\n            if m:\n                mark_output()\n            for i in range(n):\n                s.draw(f'{i}{x}'), s.index(), s.carriage_return()\n\n        from kitty.window import as_text\n        def at():\n            return as_text(s, add_history=True)\n\n        s = self.create_screen(cols=5, lines=5, scrollback=15)\n        draw_output(3, 'oo')\n        draw_prompt('pp')\n        mark_output()\n        s.toggle_alt_screen()\n        s.resize(s.lines, 2)\n        s.toggle_alt_screen()\n        self.assertFalse(str(s.line(s.cursor.y)))\n\n        s = self.create_screen(scrollback=10)\n        draw_output(1, 'start')\n        for i in range(4):\n            draw_prompt(str(i) + 'ab')\n            draw_output(1, 'ut')\n        self.ae(s.scrolled_by, 0)\n        self.ae(str(s.visual_line(0)), '$ 2ab')\n        self.assertFalse(s.scroll_to_prompt(-3))\n        self.assertTrue(s.scroll_to_prompt())\n        self.ae(str(s.visual_line(0)), '$ 1ab')\n        self.assertTrue(s.scroll_to_prompt())\n        self.ae(str(s.visual_line(0)), '$ 0ab')\n        self.assertFalse(s.scroll_to_prompt())\n        self.assertFalse(s.scroll_to_prompt(4))\n        self.assertTrue(s.scroll_to_prompt(1))\n        self.ae(str(s.visual_line(0)), '$ 1ab')\n        self.assertTrue(s.scroll_to_prompt(1))\n        self.ae(str(s.visual_line(0)), '$ 2ab')\n        self.assertFalse(s.scroll_to_prompt(1))\n        # wrap prompts\n        s.resize(s.lines + 1, s.columns - 2)\n        self.ae(s.scrolled_by, 0)\n        self.ae(str(s.visual_line(0)), 'ab')\n        self.assertFalse(s.scroll_to_prompt(-4))\n        self.assertTrue(s.scroll_to_prompt())\n        self.ae(str(s.visual_line(0)), '$ 2')\n        self.assertTrue(s.scroll_to_prompt())\n        self.ae(str(s.visual_line(0)), '$ 1')\n        self.assertTrue(s.scroll_to_prompt())\n        self.ae(str(s.visual_line(0)), '$ 0')\n        self.assertFalse(s.scroll_to_prompt())\n        self.assertFalse(s.scroll_to_prompt(4))\n        self.assertTrue(s.scroll_to_prompt(1))\n        self.ae(str(s.visual_line(0)), '$ 1')\n        self.assertTrue(s.scroll_to_prompt(1))\n        self.ae(str(s.visual_line(0)), '$ 2')\n        self.assertTrue(s.scroll_to_prompt(1))\n        self.ae(str(s.visual_line(0)), 'ab')\n        self.assertFalse(s.scroll_to_prompt(1))\n\n        s = self.create_screen()\n        mark_prompt(), s.draw('$ 0')\n        s.carriage_return(), s.index()\n        mark_prompt(), s.draw('$ 1')\n        for i in range(s.lines):\n            s.carriage_return(), s.index()\n            s.draw(str(i))\n        self.assertTrue(s.scroll_to_prompt())\n        self.ae(str(s.visual_line(0)), '$ 1')\n\n        def lco(as_ansi=False, which=0):\n            a = []\n            if s.cmd_output(which, a.append, as_ansi):\n                pht = pagerhist(s, as_ansi=as_ansi, upto_output_start=True)\n                if pht:\n                    a.insert(0, pht)\n            return ''.join(a)\n\n        def fco():\n            a = []\n            s.cmd_output(1, a.append)\n            return ''.join(a)\n\n        def lvco():\n            a = []\n            s.cmd_output(2, a.append)\n            return ''.join(a)\n\n        s = self.create_screen(cols=5, lines=5, scrollback=15)\n        draw_prompt('P' * s.columns)\n        draw_output(s.lines + 1, 'a')  # ensure prompt is in scrollback\n        draw_prompt('Q' * s.columns)\n        draw_output(s.lines + 1, 'b')  # ensure prompt is in scrollback\n        draw_prompt('R' * s.columns)\n        self.ae(lco(), '0b\\n1b\\n2b\\n3b\\n4b\\n5b')\n\n        s = self.create_screen()\n        s.draw('abcd'), s.index(), s.carriage_return()\n        s.draw('12'), s.index(), s.carriage_return()\n        self.ae(fco(), '')\n        self.ae(lco(), 'abcd\\n12\\n')\n        s = self.create_screen()\n        mark_prompt(), s.draw('$ 0')\n        s.carriage_return(), s.index()\n        mark_output()\n        s.draw('abcd'), s.index(), s.carriage_return()\n        s.draw('12'), s.index(), s.carriage_return()\n        mark_prompt(), s.draw('$ 1')\n        self.ae(fco(), 'abcd\\n12')\n        self.ae(lco(), 'abcd\\n12')\n        self.ae(lco(as_ansi=True), '\\x1b[m\\x1b]133;C\\x1b\\\\abcd\\n\\x1b[m12')  # ]]]\n\n        s = self.create_screen(cols=5, lines=5, scrollback=15)\n        draw_output(1, 'start', False)\n        draw_prompt('0'), draw_output(3)\n        draw_prompt('1')\n        draw_prompt('2'), draw_output(2, 'x')\n\n        # last cmd output\n        # test: find upwards\n        self.ae(s.scrolled_by, 0)\n        self.ae(lco(), '0x\\n1x\\n')\n        # get output after scroll up\n        s.scroll_to_prompt()\n        self.ae(s.scrolled_by, 4)\n        self.ae(str(s.visual_line(0)), '$ 0')\n        self.ae(lco(), '0x\\n1x\\n')\n\n        # first cmd output on screen\n        # test: find around\n        self.ae(fco(), '0\\n1\\n2')\n        s.scroll(2, False)\n        self.ae(s.scrolled_by, 2)\n        self.ae(str(s.visual_line(0)), '1')\n        self.ae(fco(), '0x\\n1x\\n')\n        # test: find downwards\n        s.scroll(2, False)\n        self.ae(str(s.visual_line(0)), '$ 1')\n        self.ae(fco(), '0x\\n1x\\n')\n        # test: obscure prompt in scrollback\n        s.resize(3, 5)\n        self.ae(str(s.visual_line(0)), '0x')\n        # without succeeding prompt\n        self.ae(fco(), '0x\\n1x\\n')\n        s.resize(4, 5)\n        draw_prompt('3')\n        # with succeeding prompt\n        self.ae(fco(), '0x\\n1x')\n\n        # resize\n        # get last cmd output with continued output mark\n        draw_output(1, 'long_line'), draw_output(2, 'l', False)\n        s.resize(4, 5)\n        s.scroll_to_prompt(-4)\n        self.ae(str(s.visual_line(0)), '$ 0')\n        self.ae(lco(), '0long_line\\n0l\\n1l\\n')\n\n        # last visited cmd output\n        self.ae(lvco(), '0\\n1\\n2')\n        s.scroll_to_prompt(1)\n        self.ae(lvco(), '0x\\n1x')\n        # test: obscure prompt\n        s.scroll(2, False)\n        s.set_last_visited_prompt()\n        self.ae(lvco(), '0x\\n1x')\n        # test: prompts without output\n        s.scroll(s.scrolled_by, False)\n        s.resize(5, s.columns + 5)\n        draw_prompt('4')\n        s.set_last_visited_prompt(2)\n        self.ae(lvco(), '')\n        draw_prompt('wrapcmd')\n        self.ae(lvco(), '')\n        draw_output(1, 'wrapout'), draw_output(1, 'y', False)\n        s.set_last_visited_prompt(0)\n        self.ae(lvco(), '0wrapout\\n0y\\n')\n        # wrap long prompt with long output\n        s.resize(5, s.columns - 5)\n        # test: set last visited to previous empty prompt\n        s.scroll_to_prompt(-2)\n        self.ae(str(s.visual_line(0)), '$ 4')\n        self.ae(lvco(), '0wrapout\\n0y\\n')\n        draw_prompt('end')\n        s.scroll_to_prompt(-1)\n        self.ae(lvco(), '0wrapout\\n0y')\n        s.scroll_to_prompt(1)\n        self.ae(lvco(), '0wrapout\\n0y')\n        # test: set last visited to continued line of long prompt\n        s.set_last_visited_prompt(1)\n        self.ae(lvco(), '0wrapout\\n0y')\n        # test: set last visited to continued line of output\n        s.set_last_visited_prompt(3)\n        self.ae(lvco(), '0wrapout\\n0y')\n\n        # test: losing markers past scrollback\n        s = self.create_screen(lines=10, scrollback=0)\n        draw_prompt('a' * (s.columns * 3))\n        draw_output(1, 'v' * (s.columns * 2)), draw_output(1, 'w', False)\n        draw_prompt('b')\n        draw_output(1, 'x')\n        # remove prompt start above, set last visited to within prompt\n        s.clear_scrollback()\n        s.set_last_visited_prompt(0)\n        self.ae(lvco(), '0vvvvvvvvvv\\n0w')\n        # remove output start above, set last visited to within output\n        draw_output(3, 'y', False), draw_output(1, 'z', False)\n        s.clear_scrollback()\n        s.set_last_visited_prompt(0)\n        self.ae(lvco(), 'vvvvvv\\n0w')\n        draw_output(1, 'end', False)\n        s.clear_scrollback()\n        s.set_last_visited_prompt(0)\n        self.ae(lvco(), 'v\\n0w')\n        # clear last visited line without setting new one\n        draw_output(1, 'end', False)\n        s.clear_scrollback()\n        self.ae(lvco(), '')\n\n        # test that post rewrap prompt lines have correct attributes\n        s = self.create_screen(cols=5, lines=5, scrollback=15)\n        draw_prompt('P' * (s.columns - 2))\n        draw_output(s.lines + 1, 'a')  # ensure prompt is in scrollback\n        draw_prompt('Q' * (s.columns - 2))\n        self.ae(str(s.visual_line(0)), '3a')\n        s.scroll_to_prompt()\n        self.ae(str(s.visual_line(0)), '$ PPP')\n        s.scroll_to_prompt(1)\n        self.ae(str(s.visual_line(0)), '3a')\n        s.resize(s.lines, s.columns - 2)\n        s.scroll_to_prompt()\n        self.ae(str(s.visual_line(0)), '$ P')\n\n        # last command output without line break\n        s = self.create_screen(cols=10, lines=3)\n        draw_prompt('p1')\n        mark_output(), s.draw('running')\n        self.ae(lco(), 'running')\n        s.index(), s.carriage_return()\n        self.ae(lco(), 'running\\n')\n\n        # last command output from pager history\n        s = self.create_screen()\n        draw_prompt('p1')\n        draw_output(30)\n        self.ae(tuple(map(int, lco().split())), tuple(range(0, 30)))\n\n        # last non empty command output\n        s = self.create_screen()\n        draw_prompt('a'), draw_output(2, 'a')\n        draw_prompt('b'), mark_output()\n        self.ae(lco(), '')\n        self.ae(lco(which=3), '0a\\n1a')\n        s.draw('running'), s.index(),  s.carriage_return()\n        self.ae(lco(which=3), 'running\\n')\n        s = self.create_screen()\n        draw_prompt('p1')\n        draw_output(30)\n        self.ae(tuple(map(int, lco(which=3).split())), tuple(range(0, 30)))\n        s = self.create_screen()\n        draw_prompt('p1')\n        draw_output(2, 'a')\n        draw_prompt('p1')\n        draw_prompt('p1')\n        self.ae(lco(which=3), '0a\\n1a')\n\n        # erase last command\n        s = self.create_screen(cols=10, lines=10, scrollback=15)\n        s.draw('before\\r\\n')\n        draw_prompt('p1'), draw_output(2), mark_prompt(), s.draw('partial')\n        self.ae('before\\n$ p1\\n0\\n1\\npartial', at().rstrip())\n        self.ae(lco(), '0\\n1')\n        x = s.cursor.x\n        s.erase_last_command()\n        self.ae('before\\npartial', at().rstrip())\n        self.ae((x, 1), (s.cursor.x, s.cursor.y))\n        s.reset()\n        s.draw('before\\r\\n')\n        draw_prompt('p1'), draw_output(2), mark_prompt(), s.draw('partial')\n        x = s.cursor.x\n        s.erase_last_command(False)\n        self.ae('before\\n$ p1\\npartial', at().rstrip())\n        for scroll in (8, 9, 10):\n            s.reset()\n            s.draw('before'), s.carriage_return(), s.linefeed()\n            draw_prompt('p1'), draw_output(scroll), mark_prompt(), s.draw('partial')\n            s.erase_last_command()\n            self.ae('before\\npartial', at().rstrip())\n        s.reset()\n        draw_multicell(s, 'A', scale=2), s.draw('a'), draw_multicell(s, 'B', scale=2), s.draw('b\\r\\n')\n        draw_prompt('p1'), draw_output(9), mark_prompt(), s.draw('partial')\n        s.erase_last_command()\n        self.ae(at().rstrip(), '  a  b\\npartial')\n\n    def test_pointer_shapes(self):\n        from kitty.window import set_pointer_shape\n        s = self.create_screen()\n        c = s.callbacks\n        response = ''\n\n        def cb(data):\n            nonlocal response\n            response = set_pointer_shape(s, str(data, 'utf-8'))\n        c.set_pointer_shape = cb\n\n        def send(a):\n            nonlocal response\n            response = ''\n            parse_bytes(s, f'\\x1b]22;{a}\\x1b\\\\'.encode())\n            return response\n\n        self.ae(send('?__current__'), '0')\n        self.ae(send('?__default__,__grabbed__,default,ne-resize,crosshair,XXX'), 'text,default,1,1,1,0')\n\n        def t(q, e=None):\n            self.ae(send(q), '')\n            self.ae(send('?__current__'), e)\n\n        t('default', 'default')\n        s.reset()\n        self.ae(send('?__current__'), '0')\n        t('=crosshair', 'crosshair')\n        t('<', '0')\n        t('=crosshair', 'crosshair')\n        t('', '0')\n        t('>help', 'help')\n        t('>wait', 'wait')\n        t('<', 'help')\n        t('<', '0')\n        t('default,help', 'help')\n        t('<', '0')\n        t('>default,help', 'help')\n        t('<', 'default')\n        t('<', '0')\n        t('=left_ptr', 'default')\n        t('=fleur', 'move')\n\n    def test_color_profile(self):\n        c = ColorProfile(defaults)\n        for i in range(8):\n            col = getattr(defaults, f'color{i}')\n            self.ae(c.as_color(i << 8 | 1), col)\n        self.ae(c.as_color(255 << 8 | 1), Color(0xee, 0xee, 0xee))\n        s = self.create_screen()\n        s.color_profile.reload_from_opts(defaults)\n        def q(send, expected=None):\n            s.callbacks.clear()\n            parse_bytes(s, b'\\x1b]21;' + ';'.join(f'{k}={v}' for k, v in send.items()).encode() + b'\\a')\n            self.ae(s.callbacks.color_control_responses, [expected] if expected else [])\n        q({k: '?' for k in 'background foreground 213 unknown'.split()}, {\n            'background': defaults.background, 'foreground': defaults.foreground, '213': defaults.color213, 'unknown': '?'})\n        q({'background':'aquamarine'})\n        q({'background':'?', 'selection_background': '?'}, {\n            'background': Color.parse_color('Aquamarine'), 'selection_background': s.color_profile.highlight_bg})\n        q({'selection_background': ''})\n        self.assertIsNone(s.color_profile.highlight_bg)\n        q({'selection_background': '?'}, {'selection_background': ''})\n        s.color_profile.reload_from_opts(defaults)\n        q({'transparent_background_color9': '?'}, {'transparent_background_color9': '?'})\n        q({'transparent_background_color2': '?'}, {'transparent_background_color2': ''})\n        q({'transparent_background_color2': 'red@0.5'})\n        q({'transparent_background_color2': '?'}, {'transparent_background_color2': (Color(255, 0, 0), 126)})\n        q({'transparent_background_color2': '#ffffff@-1'})\n        q({'transparent_background_color2': '?'}, {'transparent_background_color2': (Color(255, 255, 255), 255)})\n\n    def test_multi_cursors(self):\n        s = self.create_screen()\n        c = s.callbacks\n        # Test detection\n        def ec(payload=''):\n            return f'\\x1b[>{payload} q'.encode()  # ]\n        parse_bytes(s, ec())\n        self.ae(c.wtcbuf, ec('1;2;3;29;30;40;100;101'))\n\n        def current() -> dict[int, tuple[int, int]]:\n            ans = {}\n            c.clear()\n            parse_bytes(s, ec('100'))\n            for entry in c.wtcbuf[6:-2].decode().split(';'):\n                if entry:\n                    which, _, y, x = map(int, entry.split(':'))\n                    ans.setdefault(which, set()).add((x-1, y-1))\n            return ans\n        self.ae({}, current())\n\n        def a(which: int, *positions: tuple[int, int], region=None) -> dict[int, tuple[int, int]]:\n            if positions:\n                buf = [f'\\x1b[>{which};']  # ]\n                buf.extend(f'2:{y+1}:{x+1};' for x, y in positions)\n                parse_bytes(s, ''.join(buf).encode() + b' q')\n            if region:\n                if region is True:\n                    parse_bytes(s, ec(f'{which};4'))\n                else:\n                    left, top, right, bottom = region\n                    parse_bytes(s, ec(f'{which};4:{top+1}:{left+1}:{bottom+1}:{right+1}'))\n            return current()\n\n        self.ae(a(1, region=True), {1:{(x, y) for x in range(s.columns) for y in range(s.lines)}})\n        self.ae(a(0, region=True), {})\n        self.ae(a(29, region=(1, 2, 2, 3)), {29: {(1, 2), (2, 2), (1, 3), (2, 3)}})\n        self.ae(a(2, (1, 2), (1, 3)), {29: {(2, 3), (2, 2)}, 2: {(1, 2), (1, 3)}})\n        self.ae(a(0, (1, 2), (2, 3)), {29: {(2, 2)}, 2: {(1, 3)}})\n        self.ae(a(0, region=True), {})\n        s.cursor.x, s.cursor.y = 1, 2\n        parse_bytes(s, ec('3;0'))\n        self.ae(current(), {3: {(1, 2)}})\n        parse_bytes(s, ec('3;2:3'))\n        self.ae(current(), {3: {(1, 2)}})\n        parse_bytes(s, ec('0;4:3:1:4'))\n        self.ae(current(), {})\n\n        def sc(op, r=0, g=0, b=0, slot=40):\n            parse_bytes(s, ec(f'{slot};{op}:{r}:{g}:{b}'))\n            c.clear()\n            parse_bytes(s, ec('101'))\n            for x in c.wtcbuf[3:-2].decode().split(';')[1:]:\n                parts = x.split(':')\n                if int(parts[0]) == slot:\n                    if op < 2:\n                        self.ae(op, int(parts[1]))\n                    elif op == 2:\n                        self.ae((op, r, g, b), tuple(map(int, parts[1:])))\n                    else:\n                        self.ae((op, r), tuple(map(int, parts[1:])))\n                    break\n        for slot in (40, 30):\n            sc(0, slot=slot)\n            sc(1, slot=slot)\n            sc(2, 1, 2, 3, slot=slot)\n            sc(5, 13, slot=slot)\n\n\ndef detect_url(self, scale=1):\n    s = self.create_screen(cols=30 * scale)\n\n    def ae(expected, x=3, y=0):\n        s.detect_url(x * scale, y * scale)\n        url = ''.join(s.text_for_marked_url())\n        self.assertEqual(expected, url)\n\n    def t(url, x=0, y=0, before='', after='', expected=''):\n        s.reset()\n        s.cursor.x = x\n        s.cursor.y = y\n        text = before + url + after\n        if scale == 1:\n            s.draw(text)\n        else:\n            draw_multicell(s, text, scale=scale)\n        ae(expected or url, x=x + 1 + len(before), y=y)\n\n\n    t('http://moo.com')\n    t('http://moo.com/something?else=+&what-')\n    t('http://moo.com#fragme')\n    for (st, e) in '() {} [] <>'.split():\n        t('http://moo.com', before=st, after=e)\n    for trailer in ')-=':\n        t('http://moo.com' + trailer)\n    for trailer in '{}([<>':   # )]>\n        t('http://moo.com', after=trailer)\n    if scale == 1:\n        t('http://moo.com', x=s.columns - 9)\n    t('https://wraps-by-one-char.com', before='[', after=']')\n    t('http://[::1]:8080')\n    t('https://wr[aps-by-one-ch]ar.com')\n    t('http://[::1]:8080/x', after='[')  # ]\n    t('http://[::1]:8080/x]y34', expected='http://[::1]:8080/x')\n    t('https://wraps-by-one-char.com[]/x', after='[')  # ]\n    t('https://t.me/#1_*_*_*_*')\n"
  },
  {
    "path": "kitty_tests/search_query_parser.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom . import BaseTest\n\n\nclass TestSQP(BaseTest):\n\n    def test_search_query_parser(self):\n        from kitty.search_query_parser import ParseException, search\n        locations = 'id'\n        universal_set = {1, 2, 3, 4, 5}\n\n        def get_matches(location, query, candidates):\n            return {x for x in candidates if query == str(x)}\n\n        def t(q, expected=set()):\n            actual = search(q, locations, universal_set, get_matches)\n            self.ae(actual, expected)\n\n        t('id:1', {1})\n        t('id:\"1\"', {1})\n        t('id:1 and id:1', {1})\n        t('id:1 or id:2', {1, 2})\n        t('id:1 and id:2')\n        t('not id:1', universal_set - {1})\n        t('(id:1 or id:2) and id:1', {1})\n        self.assertRaises(ParseException, t, '1')\n        self.assertRaises(ParseException, t, '\"id:1\"')\n"
  },
  {
    "path": "kitty_tests/shell_integration.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport errno\nimport os\nimport shlex\nimport shutil\nimport subprocess\nimport tempfile\nimport unittest\nfrom contextlib import contextmanager\nfrom functools import lru_cache, partial\n\nfrom kitty.bash import decode_ansi_c_quoted_string\nfrom kitty.constants import is_macos, kitten_exe, kitty_base_dir, shell_integration_dir, terminfo_dir\nfrom kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE\nfrom kitty.shell_integration import setup_bash_env, setup_fish_env, setup_zsh_env\n\nfrom . import BaseTest\n\n\n@lru_cache\ndef bash_ok():\n    v = shutil.which('bash')\n    if not v:\n        return False\n    o = subprocess.check_output([v, '-c', 'echo \"${BASH_VERSINFO[0]}\\n${BASH_VERSINFO[4]}\"']).decode('utf-8').splitlines()\n    if not o:\n        return False\n    major_ver, relstatus = o[0], o[-1]\n    return int(major_ver) >= 5 and relstatus == 'release'\n\n\ndef basic_shell_env(home_dir):\n    ans = {\n        'PATH': os.environ.get('PATH', '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'),\n        'HOME': home_dir,\n        'TERM': 'xterm-kitty',\n        'TERMINFO': terminfo_dir,\n        'KITTY_SHELL_INTEGRATION': 'enabled',\n        'KITTY_INSTALLATION_DIR': kitty_base_dir,\n        'BASH_SILENCE_DEPRECATION_WARNING': '1',\n        'PYTHONDONTWRITEBYTECODE': '1',\n        'WEZTERM_SHELL_SKIP_ALL': '1',  # dont fail if WezTerm's system wide, default on (why?) shell integration is installed\n    }\n    for x in ('USER', 'LANG'):\n        if os.environ.get(x):\n            ans[x] = os.environ[x]\n    return ans\n\n\ndef safe_env_for_running_shell(argv, home_dir, rc='', shell='zsh', with_kitten=False):\n    ans = basic_shell_env(home_dir)\n    if shell == 'zsh':\n        argv.insert(1, '--noglobalrcs')\n        with open(os.path.join(home_dir, '.zshrc'), 'w') as f:\n            print(rc + '\\nZLE_RPROMPT_INDENT=0', file=f)\n        setup_zsh_env(ans, argv)\n    elif shell == 'fish':\n        conf_dir = os.path.join(home_dir, '.config', 'fish')\n        os.makedirs(conf_dir, exist_ok=True)\n        # Avoid generating unneeded completion scripts\n        os.makedirs(os.path.join(home_dir, '.local', 'share', 'fish', 'generated_completions'), exist_ok=True)\n        with open(os.path.join(conf_dir, 'config.fish'), 'w') as f:\n            print(rc + '\\n', file=f)\n        setup_fish_env(ans, argv)\n    elif shell == 'bash':\n        bashrc = os.path.join(home_dir, '.bashrc')\n        if with_kitten:\n            ans['KITTY_RUNNING_BASH_INTEGRATION_TEST'] = bashrc\n        else:\n            setup_bash_env(ans, argv)\n            ans['KITTY_BASH_INJECT'] += ' posix'\n            ans['KITTY_BASH_POSIX_ENV'] = bashrc\n        with open(bashrc, 'w') as f:\n            # ensure LINES and COLUMNS are kept up to date\n            print('shopt -s checkwinsize', file=f)\n            if rc:\n                print(rc, file=f)\n    return ans\n\n\nclass ShellIntegration(BaseTest):\n\n    with_kitten = False\n\n    @contextmanager\n    def run_shell(self, shell='zsh', rc='', cmd='', setup_env=None, extra_env=None):\n        home_dir = self.home_dir = os.path.realpath(tempfile.mkdtemp())\n        needs_da1 = os.path.basename(shell) == 'fish'\n        cmd = cmd or shell\n        cmd = shlex.split(cmd.format(**locals()))\n        env = (setup_env or safe_env_for_running_shell)(cmd, home_dir, rc=rc, shell=shell, with_kitten=self.with_kitten)\n        env['KITTY_RUNNING_SHELL_INTEGRATION_TEST'] = '1'\n        if extra_env:\n            env.update(extra_env)\n        try:\n            if self.with_kitten:\n                cmd = [kitten_exe(), 'run-shell', '--shell', shlex.join(cmd)]\n            pty = self.create_pty(cmd, cwd=home_dir, env=env, cols=180, needs_da1=needs_da1)\n            i = 10\n            while i > 0 and not pty.screen_contents().strip():\n                pty.process_input_from_child()\n                i -= 1\n            yield pty\n        finally:\n            while os.path.exists(home_dir):\n                try:\n                    shutil.rmtree(home_dir)\n                except OSError as e:\n                    # As of fish 4 fish runs a background daemon generating\n                    # completions.\n                    if e.errno == errno.ENOTEMPTY:\n                        continue\n                    raise\n\n    @unittest.skipUnless(shutil.which('zsh'), 'zsh not installed')\n    def test_zsh_integration(self):\n        ps1, rps1 = 'left>', '<right'\n        with self.run_shell(\n            rc=f'''\nPS1=\"{ps1}\"\nRPS1=\"{rps1}\"\n''') as pty:\n            q = ps1 + ' ' * (pty.screen.columns - len(ps1) - len(rps1)) + rps1\n            try:\n                pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            except TimeoutError as e:\n                raise AssertionError(f'Cursor was not changed to beam. Screen contents: {repr(pty.screen_contents())}') from e\n            pty.wait_till(lambda: pty.screen_contents() == q)\n            self.ae(pty.callbacks.titlebuf[-1], '~')\n            pty.callbacks.clear()\n            pty.send_cmd_to_child('mkdir test && ls -a')\n            self.assert_command(pty)\n            pty.wait_till(lambda: pty.screen_contents().count(rps1) == 2)\n            self.ae(pty.callbacks.titlebuf[-2:], ['mkdir test && ls -a', '~'])\n            q = '\\n'.join(str(pty.screen.line(i)) for i in range(1, pty.screen.cursor.y))\n            self.ae(pty.last_cmd_output(), q)\n            # shrink the screen\n            pty.write_to_child(r'echo $COLUMNS')\n            pty.set_window_size(rows=20, columns=40)\n            q = ps1 + 'echo $COLUMNS' + ' ' * (40 - len(ps1) - len(rps1) - len('echo $COLUMNS')) + rps1\n            pty.process_input_from_child()\n\n            def redrawn():\n                q = pty.screen_contents()\n                return '$COLUMNS' in q and q.count(rps1) == 2 and q.count(ps1) == 2\n\n            pty.wait_till(redrawn)\n            self.ae(q, str(pty.screen.line(pty.screen.cursor.y)))\n            pty.write_to_child('\\r')\n            self.assert_command(pty, 'echo $COLUMNS')\n            pty.wait_till(lambda: pty.screen_contents().count(rps1) == 3)\n            self.ae('40', str(pty.screen.line(pty.screen.cursor.y - 1)))\n            self.ae(q, str(pty.screen.line(pty.screen.cursor.y - 2)))\n            pty.send_cmd_to_child('clear')\n            self.assert_command(pty)\n            q = ps1 + ' ' * (pty.screen.columns - len(ps1) - len(rps1)) + rps1\n            pty.wait_till(lambda: pty.screen_contents() == q)\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            pty.send_cmd_to_child('cat')\n            pty.wait_till(lambda: pty.screen.cursor.shape == 0)\n            pty.write_to_child('\\x04')\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            self.assert_command(pty)\n            # Check escaping of inputs\n            pty.send_cmd_to_child(\"-f-this-command-must-not-exist\")\n            self.assert_command(pty, exit_status=127)\n        with self.run_shell(rc=f'''PS1=\"{ps1}\"''') as pty:\n            pty.callbacks.clear()\n            pty.send_cmd_to_child('printf \"%s\\x16\\a%s\" \"a\" \"b\"')\n            pty.wait_till(lambda: 'ab' in pty.screen_contents())\n            self.assertTrue(pty.screen.last_reported_cwd.decode().endswith(self.home_dir))\n            self.assertIn('%s^G%s', pty.screen_contents())\n            q = os.path.join(self.home_dir, 'testing-cwd-notification-🐱')\n            os.mkdir(q)\n            pty.send_cmd_to_child(f'cd {q}')\n            pty.wait_till(lambda: pty.screen.last_reported_cwd.decode().endswith(q))\n            if not is_macos:  # Fails on older macOS like the one used to build kitty binary because of unicode encoding issues\n                self.assert_command(pty)\n        with self.run_shell(rc=f'''PS1=\"{ps1}\"\\nexport ES=\"a\\n b c\\nd\"''') as pty:\n            pty.callbacks.clear()\n            pty.send_cmd_to_child('clone-in-kitty')\n            pty.wait_till(lambda: len(pty.callbacks.clone_cmds) == 1)\n            self.assert_command(pty)\n            env = pty.callbacks.clone_cmds[0].env\n            self.ae(env.get('ES'), 'a\\n b c\\nd')\n        with self.run_shell(rc='PS1=XXX', extra_env={'KITTY_SI_RUN_COMMAND_AT_STARTUP': 'echo pre-start'}) as pty:\n            pty.wait_till(lambda: 'XXX' in pty.screen_contents())\n            self.assertIn('pre-start', pty.screen_contents())\n            self.assertTrue(pty.screen_contents().startswith('pre-start'))\n\n    @unittest.skipUnless(shutil.which('fish'), 'fish not installed')\n    def test_fish_integration(self):\n        fish_prompt, right_prompt = 'left>', '<right'\n        completions_dir = os.path.join(kitty_base_dir, 'shell-integration', 'fish', 'vendor_completions.d')\n        with self.run_shell(\n            shell='fish',\n            extra_env={'KITTY_SI_RUN_COMMAND_AT_STARTUP': 'echo XXX'},\n            rc=f'''\nset -g fish_greeting\nfunction fish_prompt; echo -n \"{fish_prompt}\"; end\nfunction fish_right_prompt; echo -n \"{right_prompt}\"; end\nfunction _test_comp_path; contains \"{completions_dir}\" $fish_complete_path; and echo ok; end\nfunction _set_key; set -g fish_key_bindings fish_$argv[1]_key_bindings; end\nfunction _set_status_prompt; function fish_prompt; echo -n \"$pipestatus $status {fish_prompt}\"; end; end\n''') as pty:\n            q = 'XXX\\n' + fish_prompt + ' ' * (pty.screen.columns - len(fish_prompt) - len(right_prompt)) + right_prompt\n            pty.wait_till(lambda: pty.screen_contents().count(right_prompt) == 1)\n            self.ae(pty.screen_contents(), q)\n\n            # shell integration dir must not be in XDG_DATA_DIRS\n            cmd = f'string match -q -- \"*{shell_integration_dir}*\" \"$XDG_DATA_DIRS\" || echo \"XDD_OK\"'\n            pty.send_cmd_to_child(cmd)\n            pty.wait_till(lambda: 'XDD_OK' in pty.screen_contents())\n            # self.assert_command(pty, cmd)\n\n            # CWD reporting\n            self.assertTrue(pty.screen.last_reported_cwd.decode().endswith(self.home_dir))\n            q = os.path.join(self.home_dir, 'testing-cwd-notification-🐱')\n            os.mkdir(q)\n            pty.send_cmd_to_child(f'cd {q}')\n            # self.assert_command(pty)\n            pty.wait_till(lambda: pty.screen.last_reported_cwd.decode().endswith(q))\n            pty.send_cmd_to_child('cd -')\n            pty.wait_till(lambda: pty.screen.last_reported_cwd.decode().endswith(self.home_dir))\n\n            # completion and prompt marking\n            pty.wait_till(lambda: 'cd -' not in pty.screen_contents().splitlines()[-1])\n            pty.send_cmd_to_child('clear')\n            pty.wait_till(lambda: pty.screen_contents().count(right_prompt) == 1)\n            pty.send_cmd_to_child('_test_comp_path')\n            # self.assert_command(pty)\n            pty.wait_till(lambda: pty.screen_contents().count(right_prompt) == 2)\n            q = '\\n'.join(str(pty.screen.line(i)) for i in range(1, pty.screen.cursor.y))\n            self.ae(q, 'ok')\n            self.ae(pty.last_cmd_output(), q)\n\n            # resize and redraw (fish_handle_reflow)\n            pty.write_to_child(r'echo $COLUMNS')\n            pty.set_window_size(rows=20, columns=40)\n            q = fish_prompt + 'echo $COLUMNS' + ' ' * (40 - len(fish_prompt) - len(right_prompt) - len('echo $COLUMNS')) + right_prompt\n            pty.process_input_from_child()\n\n            def redrawn():\n                q = pty.screen_contents()\n                return '$COLUMNS' in q and q.count(right_prompt) == 2 and q.count(fish_prompt) == 2\n\n            pty.wait_till(redrawn)\n            self.ae(q, str(pty.screen.line(pty.screen.cursor.y)))\n            pty.write_to_child('\\r')\n            pty.wait_till(lambda: pty.screen_contents().count(right_prompt) == 3)\n            # self.assert_command(pty, 'echo $COLUMNS')\n            self.ae('40', str(pty.screen.line(pty.screen.cursor.y - 1)))\n            self.ae(q, str(pty.screen.line(pty.screen.cursor.y - 2)))\n\n            # cursor shapes\n            pty.send_cmd_to_child('clear')\n            q = fish_prompt + ' ' * (pty.screen.columns - len(fish_prompt) - len(right_prompt)) + right_prompt\n            pty.wait_till(lambda: pty.screen_contents() == q)\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            pty.send_cmd_to_child('echo; cat')\n            pty.wait_till(lambda: pty.screen.cursor.shape == 0 and pty.screen.cursor.y > 1)\n            pty.write_to_child('\\x04')\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            pty.send_cmd_to_child('_set_key vi')\n            pty.wait_till(lambda: pty.screen_contents().count(right_prompt) == 3)\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            pty.write_to_child('\\x1b')\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BLOCK)\n            pty.write_to_child('r')\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_UNDERLINE)\n            pty.write_to_child('\\x1b')\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BLOCK)\n            pty.write_to_child('i')\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            pty.send_cmd_to_child('_set_key default')\n            # self.assert_command(pty)\n            pty.wait_till(lambda: pty.screen_contents().count(right_prompt) == 4)\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n\n            pty.send_cmd_to_child('exit')\n\n    def assert_command(self, pty, cmd='', exit_status=0):\n        cmd = cmd or pty.last_cmd\n        pty.wait_till(lambda: pty.callbacks.last_cmd_exit_status == exit_status, timeout_msg=lambda: f'{pty.callbacks.last_cmd_exit_status=} != {exit_status}')\n        pty.wait_till(lambda: pty.callbacks.last_cmd_cmdline == cmd, timeout_msg=lambda: f'{pty.callbacks.last_cmd_cmdline=!r} != {cmd!r}')\n\n    @unittest.skipUnless(bash_ok(), 'bash not installed, too old, or debug build')\n    def test_bash_integration(self):\n        ps1 = 'prompt> '\n        with self.run_shell(\n            shell='bash', rc=f'''\nPS1=\"{ps1}\"\n''') as pty:\n            try:\n                pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            except TimeoutError as e:\n                raise AssertionError(f'Cursor was not changed to beam. Screen contents: {repr(pty.screen_contents())}') from e\n            pty.wait_till(lambda: pty.screen_contents().count(ps1) == 1)\n            self.ae(pty.screen_contents(), ps1)\n            pty.wait_till(lambda: pty.callbacks.titlebuf[-1:] == ['~'])\n            self.ae(pty.callbacks.titlebuf[-1], '~')\n            pty.callbacks.clear()\n            cmd = 'mkdir test && ls -a'\n            pty.send_cmd_to_child(cmd)\n            pty.wait_till(lambda: pty.callbacks.titlebuf[-2:] == [cmd, '~'])\n            pty.wait_till(lambda: pty.screen_contents().count(ps1) == 2)\n            self.assert_command(pty, cmd)\n            q = '\\n'.join(str(pty.screen.line(i)) for i in range(1, pty.screen.cursor.y))\n            self.ae(pty.last_cmd_output(), q)\n            # shrink the screen\n            pty.write_to_child(r'echo $COLUMNS')\n            pty.set_window_size(rows=20, columns=40)\n            pty.process_input_from_child()\n\n            def redrawn():\n                q = pty.screen_contents()\n                return '$COLUMNS' in q and q.count(ps1) == 2\n\n            pty.wait_till(redrawn)\n            self.ae(ps1 + 'echo $COLUMNS', str(pty.screen.line(pty.screen.cursor.y)))\n            pty.write_to_child('\\r')\n            pty.wait_till(lambda: pty.screen_contents().count(ps1) == 3)\n            self.ae('40', str(pty.screen.line(pty.screen.cursor.y - 1)))\n            self.ae(ps1 + 'echo $COLUMNS', str(pty.screen.line(pty.screen.cursor.y - 2)))\n            pty.send_cmd_to_child('clear')\n            pty.wait_till(lambda: pty.screen_contents() == ps1)\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            pty.send_cmd_to_child('cat')\n            pty.wait_till(lambda: pty.screen.cursor.shape == 0)\n            pty.write_to_child('\\x04')\n            pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM)\n            pty.write_to_child('\\x04')\n            pty.send_cmd_to_child('clear')\n            pty.wait_till(lambda: pty.callbacks.titlebuf)\n        with self.run_shell(shell='bash', rc=f'''PS1=\"{ps1}\"\\ndeclare LOCAL_KSI_VAR=1''') as pty:\n            pty.callbacks.clear()\n            pty.send_cmd_to_child('declare')\n            pty.wait_till(lambda: 'LOCAL_KSI_VAR' in pty.screen_contents())\n            self.assert_command(pty, 'declare')\n        with self.run_shell(shell='bash', rc=f'''PS1=\"{ps1}\"''') as pty:\n            pty.callbacks.clear()\n            pty.send_cmd_to_child('printf \"%s\\x16\\a%s\" \"a\" \"b\"')\n            pty.wait_till(lambda: pty.screen_contents().count(ps1) == 2)\n            self.ae(pty.screen_contents(), f'{ps1}printf \"%s^G%s\" \"a\" \"b\"\\nab{ps1}')\n            self.assertTrue(pty.screen.last_reported_cwd.decode().endswith(self.home_dir))\n            pty.send_cmd_to_child('echo $HISTFILE')\n            pty.wait_till(lambda: '.bash_history' in pty.screen_contents().replace('\\n', ''))\n            q = os.path.join(self.home_dir, 'testing-cwd-notification-🐱')\n            os.mkdir(q)\n            pty.send_cmd_to_child(f'cd {q}')\n            pty.wait_till(lambda: pty.screen.last_reported_cwd.decode().endswith(q))\n\n        for ps1 in ('line1\\\\nline\\\\2\\\\prompt> ', 'line1\\nprompt> ', 'line1\\\\nprompt> ',):\n            with self.subTest(ps1=ps1), self.run_shell(\n                shell='bash', rc=f'''\n    PS1=\"{ps1}\"\n    ''') as pty:\n                ps1 = ps1.replace('\\\\n', '\\n')\n                pty.wait_till(lambda: pty.screen_contents().count(ps1) == 1)\n                pty.send_cmd_to_child('echo test')\n                pty.wait_till(lambda: pty.screen_contents().count(ps1) == 2)\n                self.ae(pty.screen_contents(), f'{ps1}echo test\\ntest\\n{ps1}')\n                pty.write_to_child(r'echo $COLUMNS')\n                pty.set_window_size(rows=20, columns=40)\n                pty.process_input_from_child()\n                pty.wait_till(redrawn)\n                self.ae(ps1.splitlines()[-1] + 'echo $COLUMNS', str(pty.screen.line(pty.screen.cursor.y)))\n                pty.write_to_child('\\r')\n                pty.wait_till(lambda: pty.screen_contents().count(ps1) == 3)\n                self.ae('40', str(pty.screen.line(pty.screen.cursor.y - len(ps1.splitlines()))))\n                self.ae(ps1.splitlines()[-1] + 'echo $COLUMNS', str(pty.screen.line(pty.screen.cursor.y - 1 - len(ps1.splitlines()))))\n                self.assert_command(pty, 'echo $COLUMNS')\n\n        with self.run_shell(shell='bash', rc='PS1=XXX', extra_env={'KITTY_SI_RUN_COMMAND_AT_STARTUP': 'echo pre-start'}) as pty:\n            pty.wait_till(lambda: 'XXX' in pty.screen_contents())\n            self.assertIn('pre-start', pty.screen_contents())\n            self.assertTrue(pty.screen_contents().startswith('pre-start'))\n        # test startup file sourcing\n\n        def setup_env(excluded, argv, home_dir, rc='', shell='bash', with_kitten=self.with_kitten):\n            ans = basic_shell_env(home_dir)\n            if not with_kitten:\n                setup_bash_env(ans, argv)\n            for x in {'profile', 'bash.bashrc', '.bash_profile', '.bash_login', '.profile', '.bashrc', 'rcfile'} - excluded:\n                with open(os.path.join(home_dir, x), 'w') as f:\n                    if x == '.bashrc' and rc:\n                        print(rc, file=f)\n                    else:\n                        print(f'echo [{x}]', file=f)\n            ans['KITTY_BASH_ETC_LOCATION'] = home_dir\n            ans['PS1'] = 'PROMPT $ '\n            return ans\n\n        def run_test(argv, *expected, excluded=(), rc='', wait_string='PROMPT $', assert_not_in=False):\n            with self.subTest(argv=argv), self.run_shell(shell='bash', setup_env=partial(setup_env, set(excluded)), cmd=argv, rc=rc) as pty:\n                pty.wait_till(lambda: wait_string in pty.screen_contents())\n                q = pty.screen_contents()\n                for x in expected:\n                    if assert_not_in:\n                        self.assertNotIn(f'[{x}]', q)\n                    else:\n                        self.assertIn(f'[{x}]', q)\n\n        run_test('bash', 'bash.bashrc', '.bashrc')\n        run_test('bash --rcfile rcfile', 'bash.bashrc', 'rcfile')\n        run_test('bash --init-file rcfile', 'bash.bashrc', 'rcfile')\n        run_test('bash --norc')\n        run_test('bash -l', 'profile', '.bash_profile')\n        run_test('bash --noprofile -l')\n        run_test('bash -l', 'profile', '.bash_login', excluded=('.bash_profile',))\n        run_test('bash -l', 'profile', '.profile', excluded=('.bash_profile', '.bash_login'))\n\n        # test argument parsing and non-interactive shell\n        run_test('bash -s arg1 --rcfile rcfile', 'rcfile', rc='echo ok;read', wait_string='ok', assert_not_in=True)\n        run_test('bash +O login_shell -ic \"echo ok;read\"', 'bash.bashrc', excluded=('.bash_profile'), wait_string='ok', assert_not_in=True)\n        run_test('bash -l .bashrc', 'profile', rc='echo ok;read', wait_string='ok', assert_not_in=True)\n        run_test('bash -il -- .bashrc', 'profile', rc='echo ok;read', wait_string='ok')\n\n        with self.run_shell(shell='bash', setup_env=partial(setup_env, set()), cmd='bash',\n                            rc=f'''PS1=\"{ps1}\"\\nexport ES=$'a\\n `b` c\\n$d'\\nexport ES2=\"XXX\" ''') as pty:\n            pty.callbacks.clear()\n            pty.send_cmd_to_child('clone-in-kitty')\n            pty.wait_till(lambda: len(pty.callbacks.clone_cmds) == 1)\n            env = pty.callbacks.clone_cmds[0].env\n            self.ae(env.get('ES'), 'a\\n `b` c\\n$d', f'Screen contents: {pty.screen_contents()!r}')\n            self.ae(env.get('ES2'), 'XXX', f'Screen contents: {pty.screen_contents()!r}')\n\n        for q, e in {\n            'a': 'a',\n            r'a\\ab': 'a\\ab',\n            r'a\\x7z': 'a\\x07z',\n            r'a\\7b': 'a\\007b',\n            r'a\\U1f345x': 'a🍅x',\n            r'a\\c b': 'a\\0b',\n        }.items():\n            self.ae(decode_ansi_c_quoted_string(f\"$'{q}'\"), e, f'Failed to decode: {q!r}')\n\n\nclass ShellIntegrationWithKitten(ShellIntegration):\n    with_kitten = True\n"
  },
  {
    "path": "kitty_tests/shm.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport os\nimport subprocess\n\nfrom kitty.constants import kitten_exe\nfrom kitty.fast_data_types import shm_unlink\nfrom kitty.shm import SharedMemory\n\nfrom . import BaseTest\n\n\nclass SHMTest(BaseTest):\n\n    def test_shm_with_kitten(self):\n        data = os.urandom(333)\n        with SharedMemory(size=363) as shm:\n            shm.write_data_with_size(data)\n            cp = subprocess.run([kitten_exe(), '__pytest__', 'shm', 'read', shm.name], stdout=subprocess.PIPE)\n            self.assertEqual(cp.returncode, 0)\n            self.assertEqual(cp.stdout, data)\n            self.assertRaises(FileNotFoundError, shm_unlink, shm.name)\n        cp = subprocess.run([kitten_exe(), '__pytest__', 'shm', 'write'], input=data, stdout=subprocess.PIPE)\n        self.assertEqual(cp.returncode, 0)\n        name = cp.stdout.decode().strip()\n        with SharedMemory(name=name, unlink_on_exit=True) as shm:\n            q = shm.read_data_with_size()\n            self.assertEqual(data, q)\n"
  },
  {
    "path": "kitty_tests/ssh.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport glob\nimport json\nimport os\nimport shutil\nimport subprocess\nimport tempfile\nfrom contextlib import suppress\nfrom functools import lru_cache\n\nfrom kittens.ssh.utils import get_connection_data\nfrom kitty.constants import is_macos, kitten_exe, runtime_dir\nfrom kitty.fast_data_types import CURSOR_BEAM, shm_unlink\nfrom kitty.utils import SSHConnectionData\n\nfrom . import BaseTest, retry_on_failure\nfrom .shell_integration import bash_ok, basic_shell_env\n\n\ndef files_in(path):\n    for record in os.walk(path):\n        for f in record[-1]:\n            yield os.path.relpath(os.path.join(record[0], f), path)\n\n\nclass SSHKitten(BaseTest):\n\n    @retry_on_failure()\n    def test_basic_pty_operations(self):\n        pty = self.create_pty('echo hello')\n        pty.process_input_from_child()\n        self.ae(pty.screen_contents(), 'hello')\n        pty = self.create_pty(self.cmd_to_run_python_code('''\\\nimport array, fcntl, sys, termios\nbuf = array.array('H', [0, 0, 0, 0])\nfcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)\nprint(' '.join(map(str, buf)))'''), lines=13, cols=77)\n        pty.process_input_from_child()\n        self.ae(pty.screen_contents(), '13 77 770 260')\n\n    @retry_on_failure()\n    def test_ssh_connection_data(self):\n        def t(cmdline, binary='ssh', host='main', port=None, identity_file='', extra_args=()):\n            if identity_file:\n                identity_file = os.path.abspath(identity_file)\n            en = {f'{x[0]}' for x in extra_args}\n            q = get_connection_data(cmdline.split(), extra_args=en)\n            self.ae(q, SSHConnectionData(binary, host, port, identity_file, extra_args))\n\n        t('ssh main')\n        t('ssh un@ip -i ident -p34', host='un@ip', port=34, identity_file='ident')\n        t('ssh un@ip -iident -p34', host='un@ip', port=34, identity_file='ident')\n        t('ssh -p 33 main', port=33)\n        t('ssh -p 34 ssh://un@ip:33/', host='un@ip', port=34)\n        t('ssh --kitten=one -p 12 --kitten two -ix main', identity_file='x', port=12, extra_args=(('--kitten', 'one'), ('--kitten', 'two')))\n        self.assertTrue(runtime_dir())\n\n    @property\n    @lru_cache\n    def all_possible_sh(self):\n        python = 'python3' if shutil.which('python3') else 'python'\n        return tuple(filter(shutil.which, ('dash', 'zsh', 'bash', 'posh', 'sh', python)))\n\n    @retry_on_failure()\n    def test_ssh_copy(self):\n        simple_data = 'rkjlhfwf9whoaa'\n\n        def touch(p):\n            with open(os.path.join(local_home, p), 'w') as f:\n                f.write(simple_data)\n\n        for sh in self.all_possible_sh:\n            with tempfile.TemporaryDirectory() as remote_home, tempfile.TemporaryDirectory() as local_home:\n                tuple(map(touch, 'simple-file g.1 g.2'.split()))\n                os.makedirs(f'{local_home}/d1/d2/d3')\n                touch('d1/d2/x')\n                touch('d1/d2/w.exclude')\n                os.mkdir(f'{local_home}/d1/r')\n                touch('d1/r/noooo')\n                os.symlink('d2/x', f'{local_home}/d1/y')\n                os.symlink('simple-file', f'{local_home}/s1')\n                os.symlink('simple-file', f'{local_home}/s2')\n\n                conf = '''\\\ncopy simple-file\ncopy s1\ncopy --symlink-strategy=keep-path s2\ncopy --dest=a/sfa simple-file\ncopy --glob g.*\ncopy --exclude **/w.* --exclude **/r d1\n'''\n                self.check_bootstrap(\n                    sh, remote_home, test_script='env; exit 0', SHELL_INTEGRATION_VALUE='', conf=conf, home=local_home,\n                )\n                tname = '.terminfo'\n                if os.path.exists('/usr/share/misc/terminfo.cdb'):\n                    tname += '.cdb'\n                self.assertTrue(os.path.lexists(f'{remote_home}/{tname}/78'))\n                self.assertTrue(os.path.exists(f'{remote_home}/{tname}/78/xterm-kitty'))\n                self.assertTrue(os.path.exists(f'{remote_home}/{tname}/x/xterm-kitty'))\n                for w in ('simple-file', 'a/sfa', 's2'):\n                    with open(os.path.join(remote_home, w)) as f:\n                        self.ae(f.read(), simple_data)\n                    self.assertFalse(os.path.islink(f.name))\n                self.assertTrue(os.path.lexists(f'{remote_home}/d1/y'))\n                self.assertTrue(os.path.exists(f'{remote_home}/d1/y'))\n                self.ae(os.readlink(f'{remote_home}/d1/y'), 'd2/x')\n                self.ae(os.readlink(f'{remote_home}/s1'), 'simple-file')\n                contents = set(files_in(remote_home))\n                contents.discard('.zshrc')  # added by check_bootstrap()\n                # depending on platform one of these is a symlink and hence\n                # isn't in contents\n                contents.discard(f'{tname}/x/xterm-kitty')\n                contents.discard(f'{tname}/78/xterm-kitty')\n                self.ae(contents, {\n                    'g.1', 'g.2', f'{tname}/kitty.terminfo', 'simple-file', 'd1/d2/x', 'd1/y', 'a/sfa', 's1', 's2',\n                    '.local/share/kitty-ssh-kitten/kitty/version', '.local/share/kitty-ssh-kitten/kitty/bin/kitty',\n                    '.local/share/kitty-ssh-kitten/kitty/bin/kitten'\n                })\n                self.ae(len(glob.glob(f'{remote_home}/{tname}/*/xterm-kitty')), 2)\n\n    @retry_on_failure()\n    def test_ssh_env_vars(self):\n        tset = '$A-$(echo no)-`echo no2` !Q5 \"something else\"'\n        for sh in self.all_possible_sh:\n            with tempfile.TemporaryDirectory() as tdir:\n                os.mkdir(os.path.join(tdir, 'cwd'))\n                conf = f'''\ncwd $HOME/cwd\nenv A=AAA\nenv TSET={tset}\nenv COLORTERM\n'''\n                pty = self.check_bootstrap(\n                    sh, tdir, test_script='env; pwd; exit 0', SHELL_INTEGRATION_VALUE='', conf=conf\n                )\n                pty.wait_till(lambda: 'TSET={}'.format(tset.replace('$A', 'AAA')) in pty.screen_contents())\n                self.assertNotIn('COLORTERM', pty.screen_contents())\n                pty.wait_till(lambda: '/cwd' in pty.screen_contents())\n                self.assertTrue(pty.is_echo_on())\n\n    @retry_on_failure()\n    def test_ssh_bootstrap_with_different_launchers(self):\n        for launcher in self.all_possible_sh:\n            if 'python' in launcher:\n                continue\n            for sh in self.all_possible_sh:\n                if sh == 'sh' or 'python' in sh:\n                    q = shutil.which(launcher)\n                    if q:\n                        with tempfile.TemporaryDirectory() as tdir:\n                            self.check_bootstrap(sh, tdir, test_script='env; exit 0', SHELL_INTEGRATION_VALUE='', launcher=q)\n\n    @retry_on_failure()\n    def test_ssh_leading_data(self):\n        script = 'echo \"ld:$leading_data\"; exit 0'\n        for sh in self.all_possible_sh:\n            if 'python' in sh:\n                script = 'print(\"ld:\" + leading_data.decode(\"ascii\")); raise SystemExit(0);'\n            with tempfile.TemporaryDirectory() as tdir:\n                pty = self.check_bootstrap(\n                    sh, tdir, test_script=script,\n                    SHELL_INTEGRATION_VALUE='', pre_data='before_tarfile')\n                self.ae(pty.screen_contents(), 'UNTAR_DONE\\nld:before_tarfile')\n\n    @retry_on_failure()\n    def test_ssh_login_shell_detection(self):\n        methods = []\n        if shutil.which('python') or shutil.which('python3') or shutil.which('python2'):\n            methods.append('using_python')\n        if is_macos:\n            methods += ['using_id']\n        else:\n            if shutil.which('getent'):\n                methods.append('using_getent')\n            if os.access('/etc/passwd', os.R_OK):\n                methods.append('using_passwd')\n        self.assertTrue(methods)\n        import pwd\n        try:\n            expected_login_shell = pwd.getpwuid(os.geteuid()).pw_shell\n        except KeyError:\n            self.skipTest('Skipping login shell detection as getpwuid() failed to read login shell')\n        if os.path.basename(expected_login_shell) == 'nologin':\n            self.skipTest('Skipping login shell detection as login shell is set to nologin')\n        for m in methods:\n            for sh in self.all_possible_sh:\n                if 'python' in sh:\n                    continue\n                with tempfile.TemporaryDirectory() as tdir:\n                    pty = self.check_bootstrap(sh, tdir, test_script=f'{m}; echo \"$login_shell\"; exit 0', SHELL_INTEGRATION_VALUE='')\n                    self.assertIn(expected_login_shell, pty.screen_contents())\n\n    @retry_on_failure()\n    def test_ssh_shell_integration(self):\n        ok_login_shell = ''\n        for sh in self.all_possible_sh:\n            for login_shell in {'fish', 'zsh', 'bash'} & set(self.all_possible_sh):\n                if login_shell == 'bash' and not bash_ok():\n                    continue\n                ok_login_shell = login_shell\n                with tempfile.TemporaryDirectory() as tdir:\n                    pty = self.check_bootstrap(sh, tdir, login_shell)\n                    if login_shell == 'bash':\n                        pty.send_cmd_to_child('echo $HISTFILE')\n                        pty.wait_till(lambda: '/.bash_history' in pty.screen_contents())\n                    elif login_shell == 'zsh':\n                        pty.send_cmd_to_child('echo \"login_shell=$ZSH_NAME\"')\n                        pty.wait_till(lambda: 'login_shell=zsh' in pty.screen_contents())\n                    self.assertIn(b'\\x1b]133;', pty.received_bytes)\n        # check that turning off shell integration works\n        if ok_login_shell in ('bash', 'zsh'):\n            for val in ('', 'no-rc', 'enabled no-rc'):\n                for sh in self.all_possible_sh:\n                    with tempfile.TemporaryDirectory() as tdir:\n                        pty = self.check_bootstrap(sh, tdir, ok_login_shell, val)\n                        num_lines = len(pty.screen_contents().splitlines())\n                        pty.send_cmd_to_child('echo \"$TERM=fruity\"')\n                        pty.wait_till(lambda: 'kitty=fruity' in pty.screen_contents(), timeout=30)\n                        pty.wait_till(lambda: len(pty.screen_contents().splitlines()) >= num_lines + 2)\n                        self.assertEqual(pty.screen.cursor.shape, 0)\n                        self.assertNotIn(b'\\x1b]133;', pty.received_bytes)\n\n    def check_bootstrap(self, sh, home_dir, login_shell='', SHELL_INTEGRATION_VALUE='enabled', test_script='', pre_data='', conf='', launcher='sh', home=''):\n        if login_shell:\n            conf += f'\\nlogin_shell {login_shell}'\n        if 'python' in sh:\n            if test_script.startswith('env;'):\n                test_script = f'os.execlp(\"sh\", \"sh\", \"-c\", {test_script!r})'\n            test_script = f'print(\"UNTAR_DONE\", flush=True); {test_script}'\n        else:\n            test_script = f'echo \"UNTAR_DONE\"; {test_script}'\n        conf += '\\nshell_integration ' + (SHELL_INTEGRATION_VALUE or 'disabled')\n        conf += '\\ninterpreter ' + sh\n        env = os.environ.copy()\n        if home:\n            env['HOME'] = home\n        cp = subprocess.run([kitten_exe(), '__pytest__', 'ssh', test_script], env=env, stdout=subprocess.PIPE, input=conf.encode('utf-8'))\n        self.assertEqual(cp.returncode, 0)\n        self.rdata = json.loads(cp.stdout)\n        del cp\n        try:\n            env = basic_shell_env(home_dir)\n            # Avoid generating unneeded completion scripts\n            os.makedirs(os.path.join(home_dir, '.local', 'share', 'fish', 'generated_completions'), exist_ok=True)\n            # prevent newuser-install from running\n            open(os.path.join(home_dir, '.zshrc'), 'w').close()\n            pty = self.create_pty([launcher, '-c', ' '.join(self.rdata['cmd'])], cwd=home_dir, env=env)\n            pty.turn_off_echo()\n            if pre_data:\n                pty.write_buf = pre_data.encode('utf-8')\n\n            def check_untar_or_fail():\n                q = pty.screen_contents()\n                return 'UNTAR_DONE' in q\n            pty.wait_till(check_untar_or_fail, timeout=60)\n            self.assertTrue(os.path.exists(os.path.join(home_dir, '.terminfo/kitty.terminfo')))\n            if SHELL_INTEGRATION_VALUE != 'enabled':\n                pty.wait_till(lambda: len(pty.screen_contents().splitlines()) > 1, timeout=60)\n                self.assertEqual(pty.screen.cursor.shape, 0)\n            else:\n                pty.wait_till(lambda: pty.screen.cursor.shape == CURSOR_BEAM, timeout=60)\n            return pty\n        finally:\n            with suppress(FileNotFoundError):\n                shm_unlink(self.rdata['shm_name'])\n"
  },
  {
    "path": "kitty_tests/tui.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nfrom . import BaseTest\n\n\nclass TestTUI(BaseTest):\n\n    def test_line_edit(self):\n        from kittens.tui.line_edit import LineEdit\n        le = LineEdit()\n        le.on_text('abcd', False)\n        self.ae(le.cursor_pos, 4)\n        for i in range(5):\n            self.assertTrue(le.left()) if i < 4 else self.assertFalse(le.left())\n            self.ae(le.cursor_pos, max(0, 3 - i))\n        self.ae(le.pending_bell, True)\n        le.clear()\n        le.on_text('abcd', False), le.home()\n        self.ae(le.cursor_pos, 0)\n        for i in range(5):\n            self.assertTrue(le.right()) if i < 4 else self.assertFalse(le.right())\n            self.ae(le.cursor_pos, min(4, i + 1))\n        self.ae(le.pending_bell, True)\n        le.clear()\n        le.on_text('abcd', False)\n        self.ae(le.current_input, 'abcd')\n        self.ae(le.cursor_pos, 4)\n        self.ae(le.split_at_cursor(), ('abcd', ''))\n        le.backspace()\n        self.ae(le.current_input, 'abc')\n        self.ae(le.cursor_pos, 3)\n        self.assertFalse(le.pending_bell)\n        le.backspace(num=2)\n        self.ae(le.current_input, 'a')\n        self.ae(le.cursor_pos, 1)\n        self.assertFalse(le.pending_bell)\n        le.backspace(num=2)\n        self.ae(le.current_input, '')\n        self.ae(le.cursor_pos, 0)\n        le.backspace()\n        self.assertTrue(le.pending_bell)\n\n    def test_multiprocessing_spawn(self):\n        from kitty.multiprocessing import test_spawn\n        test_spawn()\n"
  },
  {
    "path": "kitty_tests/utmp.py",
    "content": "import subprocess\n\nfrom kitty.fast_data_types import num_users\n\nfrom . import BaseTest\n\n\nclass UTMPTest(BaseTest):\n\n    def test_num_users(self):\n        # who is the control\n        try:\n            expected = subprocess.check_output(['who']).decode('utf-8').count('\\n')\n        except FileNotFoundError:\n            self.skipTest('No who executable cannot verify num_users')\n        else:\n            self.ae(num_users(), expected)\n"
  },
  {
    "path": "logo/attribution.txt",
    "content": "Attribution\n###########\n\n- Name: macOS Big Sur Icon Template\n- Author: Václav Vančura\n- Source: https://www.figma.com/community/file/857303226040719059\n- License: https://creativecommons.org/licenses/by/4.0/\n- Modification: Replaced the template logo with kitty's\n"
  },
  {
    "path": "logo/make.py",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport os\nimport shutil\nimport subprocess\n\nbase = os.path.dirname(os.path.abspath(__file__))\nunframed_src = os.path.join(base, 'kitty.svg')\nframed_src = os.path.join(base, 'kitty-framed.svg')\n\n\ndef abspath(x):\n    return os.path.join(base, x)\n\n\ndef run(*args):\n    try:\n        subprocess.check_call(args)\n    except OSError:\n        raise SystemExit(f'You are missing the {args[0]} program needed to generate the kitty logo')\n\n\ndef render(output, sz=256, src=unframed_src):\n    print(f'Rendering {os.path.basename(src)} at {sz}x{sz}...')\n    run('rsvg-convert', '-w', str(sz), '-h', str(sz), '-o', output, src)\n    run('optipng', '-quiet', '-o7', '-strip', 'all', output)\n\n\ndef main():\n    render(abspath('kitty.png'))\n    render(abspath('kitty-128.png'), sz=128)\n    iconset = abspath('kitty.iconset')\n    if os.path.exists(iconset):\n        shutil.rmtree(iconset)\n    os.mkdir(iconset)\n    os.chdir(iconset)\n    for sz in (16, 32, 64, 128, 256, 512, 1024):\n        iname = os.path.join(iconset, 'icon_{0}x{0}.png'.format(sz))\n        iname2x = 'icon_{0}x{0}@2x.png'.format(sz // 2)\n        render(iname, sz, src=framed_src)\n        if sz > 16 and sz != 128:\n            shutil.copy2(iname, iname2x)\n        if sz in (64, 1024):\n            os.remove(iname)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "publish.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport argparse\nimport base64\nimport contextlib\nimport datetime\nimport glob\nimport io\nimport json\nimport mimetypes\nimport os\nimport pprint\nimport re\nimport shlex\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nimport time\nfrom contextlib import contextmanager, suppress\nfrom http.client import HTTPResponse, HTTPSConnection\nfrom typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union\nfrom urllib.parse import urlencode, urlparse\n\nos.chdir(os.path.dirname(os.path.abspath(__file__)))\ndocs_dir = os.path.abspath('docs')\npublish_dir = os.path.abspath(os.path.join('..', 'kovidgoyal.github.io', 'kitty'))\nbuilding_nightly = False\nwith open('kitty/constants.py') as f:\n    raw = f.read()\nnv = re.search(r'^version: Version\\s+=\\s+Version\\((\\d+), (\\d+), (\\d+)\\)', raw, flags=re.MULTILINE)\nif nv is not None:\n    version = f'{nv.group(1)}.{nv.group(2)}.{nv.group(3)}'\nap = re.search(r\"^appname: str\\s+=\\s+'([^']+)'\", raw, flags=re.MULTILINE)\nif ap is not None:\n    appname = ap.group(1)\n\nALL_ACTIONS = 'local_build man html build tag sdist sbom upload website'.split()\nNIGHTLY_ACTIONS = 'local_build man html build sdist sbom upload_nightly'.split()\n\n\ndef echo_cmd(cmd: Iterable[str]) -> None:\n    isatty = sys.stdout.isatty()\n    end = '\\n'\n    if isatty:\n        end = f'\\x1b[m{end}'\n        print('\\x1b[32m', end='')  # ]]]]]\n    print(shlex.join(cmd), end=end, flush=True)\n\n\ndef call(*cmd: str, cwd: Optional[str] = None, echo: bool = False, timeout: float | None = None) -> None:\n    if len(cmd) == 1:\n        q = shlex.split(cmd[0])\n    else:\n        q = list(cmd)\n    if echo:\n        echo_cmd(cmd)\n    p = subprocess.Popen(q, cwd=cwd)\n    try:\n        ret = p.wait(timeout)\n    except subprocess.TimeoutExpired:\n        p.terminate()\n        try:\n            p.wait(1)\n        except subprocess.TimeoutExpired:\n            p.kill()\n            p.wait()\n        raise\n    if ret != 0:\n        raise SystemExit(ret)\n\n\ndef run_local_build(args: Any) -> None:\n    call('make debug')\n\n\ndef run_build(args: Any) -> None:\n    import runpy\n\n    m = runpy.run_path('./setup.py', run_name='__publish__')\n    vcs_rev: str = m['get_vcs_rev']()\n\n    def run_with_retry(cmd: str, timeout: float | None = 20 * 60, retry_cmd: str = '') -> None:\n        try:\n            call(cmd, echo=True, timeout=timeout)\n        except (SystemExit, Exception):\n            if not (building_nightly and retry_cmd):\n                raise\n            print('Build failed, retrying in a minute...', file=sys.stderr)\n            call(retry_cmd)\n            time.sleep(60)\n            call(cmd, echo=True, timeout=timeout)\n\n    for x, retry_cmd in {'64': '', 'arm64': 'pkill -9 -f -e qemu-aarch64-static'}.items():\n        prefix = f'python ../bypy linux --arch {x} '\n        run_with_retry(prefix + f'program --non-interactive --extra-program-data \"{vcs_rev}\"', retry_cmd=retry_cmd)\n    run_with_retry(\n        f'python ../bypy macos program --sign-installers --notarize --non-interactive --extra-program-data \"{vcs_rev}\"',\n        retry_cmd='python ../bypy macos shutdown'\n    )\n    call('python ../bypy macos shutdown', echo=True)\n    call('make debug')\n    call('./setup.py build-static-binaries')\n\n\ndef run_tag(args: Any) -> None:\n    call('git push')\n    call('git tag -s v{0} -m version-{0}'.format(version))\n    call(f'git push origin v{version}')\n\n\ndef run_man(args: Any) -> None:\n    call('make FAIL_WARN=1 man', cwd=docs_dir)\n\n\ndef run_html(args: Any) -> None:\n    # Force a fresh build otherwise the search index is not correct\n    with suppress(FileNotFoundError):\n        shutil.rmtree(os.path.join(docs_dir, '_build', 'dirhtml'))\n    call('make FAIL_WARN=1 \"OPTS=-D analytics_id=G-XTJK3R7GF2\" dirhtml', cwd=docs_dir)\n    add_old_redirects('docs/_build/dirhtml')\n\n    with suppress(FileNotFoundError):\n        shutil.rmtree(os.path.join(docs_dir, '_build', 'html'))\n    call('make FAIL_WARN=1 \"OPTS=-D analytics_id=G-XTJK3R7GF2\" html', cwd=docs_dir)\n\n\ndef generate_redirect_html(link_name: str, bname: str) -> None:\n    with open(link_name, 'w') as f:\n        f.write(f'''\n<html>\n<head>\n<title>Redirecting...</title>\n<link rel=\"canonical\" href=\"{bname}/\" />\n<noscript>\n<meta http-equiv=\"refresh\" content=\"0;url={bname}/\" />\n</noscript>\n<script type=\"text/javascript\">\nwindow.location.replace('./{bname}/' + window.location.hash);\n</script>\n</head>\n<body>\n<p>Redirecting, please wait...</p>\n</body>\n</html>\n''')\n\n\ndef add_old_redirects(loc: str) -> None:\n    for dirpath, dirnames, filenames in os.walk(loc):\n        if dirpath != loc:\n            for fname in filenames:\n                if fname == 'index.html':\n                    bname = os.path.basename(dirpath)\n                    base = os.path.dirname(dirpath)\n                    link_name = os.path.join(base, f'{bname}.html') if base else f'{bname}.html'\n                    generate_redirect_html(link_name, bname)\n\n    old_unicode_input_path = os.path.join(loc, 'kittens', 'unicode-input')\n    os.makedirs(old_unicode_input_path, exist_ok=True)\n    generate_redirect_html(os.path.join(old_unicode_input_path, 'index.html'), '../unicode_input')\n    generate_redirect_html(f'{old_unicode_input_path}.html', 'unicode_input')\n\n\ndef run_docs(args: Any) -> None:\n    subprocess.check_call(['make', 'docs'])\n\n\ndef run_website(args: Any) -> None:\n    if os.path.exists(publish_dir):\n        shutil.rmtree(publish_dir)\n    shutil.copytree(os.path.join(docs_dir, '_build', 'dirhtml'), publish_dir, symlinks=True)\n    with open(os.path.join(publish_dir, 'current-version.txt'), 'w') as f:\n        f.write(version)\n    shutil.copy2(os.path.join(docs_dir, 'installer.sh'), publish_dir)\n    os.chdir(os.path.dirname(publish_dir))\n    subprocess.check_call(['optipng', '-o7'] + glob.glob('kitty/_images/social_previews/*.png'))\n    subprocess.check_call(['git', 'add', 'kitty'])\n    subprocess.check_call(['git', 'commit', '-m', 'kitty website updates'])\n    subprocess.check_call(['git', 'push'])\n\n\ndef sign_file(path: str) -> None:\n    dest = f'{path}.sig'\n    with suppress(FileNotFoundError):\n        os.remove(dest)\n    subprocess.check_call([\n        os.environ['PENV'] + '/gpg-as-kovid', '--output', f'{path}.sig',\n        '--detach-sig', path\n    ])\n\n\ndef run_sdist(args: Any) -> None:\n    with tempfile.TemporaryDirectory() as tdir:\n        base = os.path.join(tdir, f'kitty-{version}')\n        os.mkdir(base)\n        subprocess.check_call(f'git archive HEAD | tar -x -C {base}', shell=True)\n        dest = os.path.join(base, 'docs', '_build')\n        os.mkdir(dest)\n        for x in 'html man'.split():\n            shutil.copytree(os.path.join(docs_dir, '_build', x), os.path.join(dest, x))\n        dest = os.path.abspath(os.path.join('build', f'kitty-{version}.tar'))\n        subprocess.check_call(['tar', '-cf', dest, os.path.basename(base)], cwd=tdir)\n        with suppress(FileNotFoundError):\n            os.remove(f'{dest}.xz')\n        subprocess.check_call(['xz', '-9', dest])\n        sign_file(f'{dest}.xz')\n\n\ndef run_sbom(args: Any) -> None:\n    call(f'python ../bypy sbom --output build/kitty-{version}.tar.xz.spdx.json --url https://sw.kovidgoyal.net/kitty/binary kovidgoyal/{appname} {version}')\n\n\nclass ReadFileWithProgressReporting(io.FileIO):  # {{{\n    def __init__(self, path: str):\n        super().__init__(path, 'rb')\n        self.seek(0, os.SEEK_END)\n        self._total = self.tell()\n        self.seek(0)\n        self.start_time = time.monotonic()\n        print('Starting upload of:', os.path.basename(path), 'size:', self._total)\n\n    def __len__(self) -> int:\n        return self._total\n\n    def read(self, size: Optional[int] = -1) -> bytes:\n        data = io.FileIO.read(self, size)\n        if data:\n            self.report_progress(len(data))\n        return data\n\n    def report_progress(self, size: int) -> None:\n        def write(*args: str) -> None:\n            print(*args, end='')\n\n        frac = int(self.tell() * 100 / self._total)\n        mb_pos = self.tell() / float(1024**2)\n        mb_tot = self._total / float(1024**2)\n        kb_pos = self.tell() / 1024.0\n        kb_rate = kb_pos / (time.monotonic() - self.start_time)\n        bit_rate = kb_rate * 1024\n        eta = int((self._total - self.tell()) / bit_rate) + 1\n        eta_m, eta_s = divmod(eta, 60)\n        if sys.stdout.isatty():\n            write(\n                f'\\r\\033[K\\033[?7h {frac}% {mb_pos:.1f}/{mb_tot:.1f}MB {kb_rate:.1f} KB/sec {eta_m} minutes, {eta_s} seconds left\\033[?7l')\n        if self.tell() >= self._total:\n            t = int(time.monotonic() - self.start_time) + 1\n            print(f'\\nUpload took {t//60} minutes and {t%60} seconds at {kb_rate:.1f} KB/sec')\n        sys.stdout.flush()\n\n\n# }}}\n\n\nclass GitHub:  # {{{\n\n    API = 'https://api.github.com'\n\n    def __init__(\n        self,\n        files: Dict[str, str],\n        reponame: str,\n        version: str,\n        username: str,\n        password: str,\n        replace: bool = False\n    ):\n        self.files, self.reponame, self.version, self.username, self.password, self.replace = (\n            files, reponame, version, username, password, replace)\n        self.current_tag_name = self.version if self.version == 'nightly' else f'v{self.version}'\n        self.is_nightly = self.current_tag_name == 'nightly'\n        self.auth = 'Basic ' + base64.standard_b64encode(f'{self.username}:{self.password}'.encode()).decode()\n        self.url_base = f'{self.API}/repos/{self.username}/{self.reponame}/releases'\n\n    def info(self, *args: Any) -> None:\n        print(*args, flush=True)\n\n    def error(self, *args: Any) -> None:\n        print(*args, flush=True, file=sys.stderr)\n\n    def make_request(\n        self, url: str, data: Optional[Dict[str, Any]] = None, method:str = 'GET',\n        upload_data: Optional[ReadFileWithProgressReporting] = None,\n        params: Optional[Dict[str, str]] = None,\n    ) -> HTTPSConnection:\n        headers={\n            'Authorization': self.auth,\n            'Accept': 'application/vnd.github+json',\n            'User-Agent': 'kitty',\n            'X-GitHub-Api-Version': '2022-11-28',\n        }\n        if params:\n            url += '?' + urlencode(params)\n        rdata: Optional[Union[bytes, io.FileIO]] = None\n        if data is not None:\n            rdata = json.dumps(data).encode('utf-8')\n            headers['Content-Type'] = 'application/json'\n            headers['Content-Length'] = str(len(rdata))\n        elif upload_data is not None:\n            rdata = upload_data\n            mime_type = mimetypes.guess_type(os.path.basename(str(upload_data.name)))[0] or 'application/octet-stream'\n            headers['Content-Type'] = mime_type\n            headers['Content-Length'] = str(upload_data._total)\n        purl = urlparse(url)\n        conn = HTTPSConnection(purl.netloc, timeout=60)\n        conn.request(method, url, body=rdata, headers=headers)\n        return conn\n\n    def make_request_with_retries(\n        self, url: str, data: Optional[Dict[str, str]] = None, method:str = 'GET',\n        num_tries: int = 2, sleep_between_tries: float = 15,\n        success_codes: Tuple[int, ...] = (200,),\n        failure_msg: str = 'Request failed',\n        return_data: bool = False,\n        upload_path: str = '',\n        params: Optional[Dict[str, str]] = None,\n        failure_callback: Callable[[HTTPResponse], None] = lambda r: None,\n    ) -> Any:\n        for i in range(num_tries):\n            is_last_try = i == num_tries - 1\n            try:\n                if upload_path:\n                    conn = self.make_request(url, method='POST', upload_data=ReadFileWithProgressReporting(upload_path), params=params)\n                else:\n                    conn = self.make_request(url, data, method, params=params)\n                with contextlib.closing(conn):\n                    r = conn.getresponse()\n                    if r.status in success_codes:\n                        return json.loads(r.read()) if return_data else None\n                    if is_last_try:\n                        self.fail(r, failure_msg)\n                    else:\n                        self.print_failed_response_details(r, failure_msg)\n                        failure_callback(r)\n            except Exception as e:\n                self.error(failure_msg, 'with error:', e)\n            self.error(f'Retrying after {sleep_between_tries} seconds')\n            if is_last_try:\n                break\n            time.sleep(sleep_between_tries)\n        raise SystemExit('All retries failed, giving up')\n\n    def patch(self, url: str, fail_msg: str, **data: str) -> None:\n        self.make_request_with_retries(url, data, method='PATCH', failure_msg=fail_msg)\n\n    def update_nightly_description(self, release_id: int) -> None:\n        url = f'{self.url_base}/{release_id}'\n        now = str(datetime.datetime.now(datetime.timezone.utc)).split('.')[0] + ' UTC'\n        commit = subprocess.check_output(['git', 'rev-parse', '--verify', '--end-of-options', 'master^{commit}']).decode('utf-8').strip()\n        self.patch(\n            url, 'Failed to update nightly release description',\n            body=f'Nightly release, generated on: {now} from commit: {commit}.'\n            ' For how to install nightly builds, see: https://sw.kovidgoyal.net/kitty/binary/#customizing-the-installation'\n        )\n\n    def __call__(self) -> None:\n        # See https://docs.github.com/en/rest/releases/assets#upload-a-release-asset\n        release = self.create_release()\n        upload_url = release['upload_url'].partition('{')[0]\n        all_assest_for_release = self.existing_assets_for_release(release)\n        assets_by_fname = {a['name']:a for a in all_assest_for_release}\n\n        def delete_asset(asset: Dict[str, Any], allow_not_found: bool = True) -> None:\n            success_codes = [204]\n            if allow_not_found:\n                success_codes.append(404)\n            self.make_request_with_retries(\n                asset['url'], method='DELETE', num_tries=5, sleep_between_tries=2, success_codes=tuple(success_codes),\n                failure_msg='Failed to delete asset from GitHub')\n\n        def upload_with_retries(path: str, desc: str, num_tries: int = 8, sleep_time: float = 60.0) -> None:\n            fname = os.path.basename(path)\n            if self.is_nightly:\n                fname = fname.replace(version, 'nightly')\n            if fname in assets_by_fname:\n                self.info(f'Deleting {fname} from GitHub with id: {assets_by_fname[fname][\"id\"]}')\n                delete_asset(assets_by_fname.pop(fname))\n            params = {'name': fname, 'label': desc}\n\n            self.make_request_with_retries(\n                upload_url, upload_path=path, params=params, num_tries=num_tries, sleep_between_tries=sleep_time,\n                failure_msg=f'Failed to upload file: {fname}', success_codes=(201,),\n            )\n\n        if self.is_nightly:\n            for fname in tuple(assets_by_fname):\n                self.info(f'Deleting {fname} from GitHub with id: {assets_by_fname[fname][\"id\"]}')\n                delete_asset(assets_by_fname.pop(fname))\n        for path, desc in self.files.items():\n            self.info('')\n            upload_with_retries(path, desc)\n        if self.is_nightly:\n            self.update_nightly_description(release['id'])\n\n    def print_failed_response_details(self, r: HTTPResponse, msg: str) -> None:\n        self.error(msg, f'\\nStatus Code: {r.status} {r.reason}')\n        try:\n            jr = json.loads(r.read())\n        except Exception:\n            pass\n        else:\n            self.error('JSON from response:')\n            pprint.pprint(jr, stream=sys.stderr)\n\n    def fail(self, r: HTTPResponse, msg: str) -> None:\n        self.print_failed_response_details(r, msg)\n        raise SystemExit(1)\n\n    def existing_assets_for_release(self, release: Dict[str, Any]) -> List[Dict[str, Any]]:\n        if 'assets' in release:\n            d: List[Dict[str, Any]] = release['assets']\n        else:\n            d = self.make_request_with_retries(\n                release['assets_url'], params={'per_page': '64'}, failure_msg='Failed to get assets for release', return_data=True)\n        return d\n\n    def create_release(self) -> Dict[str, Any]:\n        ' Create a release on GitHub or if it already exists, return the existing release '\n        # Check for existing release\n        url = f'{self.url_base}/tags/{self.current_tag_name}'\n        with contextlib.closing(self.make_request(url)) as conn:\n            r = conn.getresponse()\n            if r.status == 200:\n                return {str(k): v for k, v in json.loads(r.read()).items()}\n        if self.is_nightly:\n            self.fail(r, 'No existing nightly release found on GitHub')\n        data = {\n            'tag_name': self.current_tag_name,\n            'target_commitish': 'master',\n            'name': f'version {self.version}',\n            'body': f'Release version {self.version}.'\n            ' For changelog, see https://sw.kovidgoyal.net/kitty/changelog/#detailed-list-of-changes'\n            ' GPG key used for signing tarballs is: https://calibre-ebook.com/signatures/kovid.gpg',\n            'draft': False,\n            'prerelease': False\n        }\n        with contextlib.closing(self.make_request(self.url_base, method='POST', data=data)) as conn:\n            r = conn.getresponse()\n            if r.status != 201:\n                self.fail(r, f'Failed to create release for version: {self.version}')\n            return {str(k): v for k, v in json.loads(r.read()).items()}\n# }}}\n\n\ndef get_github_data() -> Dict[str, str]:\n    with open(os.environ['PENV'] + '/github-token') as f:\n        un, pw = f.read().strip().split(':')\n    return {'username': un, 'password': pw}\n\n\ndef files_for_upload() -> Dict[str, str]:\n    files = {}\n    signatures = {}\n    for f, desc in {\n        'macos/dist/kitty-{}.dmg': 'macOS dmg',\n        'linux/64/dist/kitty-{}-x86_64.txz': 'Linux amd64 binary bundle',\n        'linux/arm64/dist/kitty-{}-arm64.txz': 'Linux arm64 binary bundle',\n    }.items():\n        path = os.path.join('bypy', 'b', f.format(version))\n        if not os.path.exists(path):\n            raise SystemExit(f'The installer {path} does not exist')\n        files[path] = desc\n        signatures[path] = f'GPG signature for {desc}'\n    b = len(files)\n    for path in glob.glob('build/static/kitten-*'):\n        if path.endswith('.sig'):\n            continue\n        path = os.path.abspath(path)\n        exe_name = os.path.basename(path)\n        files[path] = f'Static {exe_name} executable'\n        signatures[path] = f'GPG signature for static {exe_name} executable'\n    if len(files) == b:\n        raise SystemExit('No static binaries found')\n\n    files[f'build/kitty-{version}.tar.xz'] = 'Source code'\n    files[f'build/kitty-{version}.tar.xz.sig'] = 'Source code GPG signature'\n    files[f'build/kitty-{version}.tar.xz.spdx.json'] = 'SBOM for kitty (all builds)'\n    for path, desc in signatures.items():\n        sign_file(path)\n        files[f'{path}.sig'] = desc\n    for f in files:\n        if not os.path.exists(f):\n            raise SystemExit(f'The release artifact {f} does not exist')\n    return files\n\n\ndef run_upload(args: Any) -> None:\n    gd = get_github_data()\n    files = files_for_upload()\n    gh = GitHub(files, appname, version, gd['username'], gd['password'])\n    gh()\n\n\ndef run_upload_nightly(args: Any) -> None:\n    subprocess.check_call(['git', 'tag', '-f', 'nightly'])\n    subprocess.check_call(['git', 'push', 'origin', 'nightly', '-f'])\n    gd = get_github_data()\n    files = files_for_upload()\n    gh = GitHub(files, appname, 'nightly', gd['username'], gd['password'])\n    gh()\n\n\ndef current_branch() -> str:\n    return subprocess.check_output(['git', 'symbolic-ref', '--short', 'HEAD']).decode('utf-8').strip()\n\n\ndef require_git_master(branch: str = 'master') -> None:\n    if current_branch() != branch:\n        raise SystemExit(f'You must be in the {branch} git branch')\n\n\ndef safe_read(path: str) -> str:\n    with suppress(FileNotFoundError):\n        with open(path) as f:\n            return f.read()\n    return ''\n\n\ndef remove_pycache_only_folders() -> None:\n    folders_to_remove = []\n    for dirpath, folders, files in os.walk('.'):\n        if not files and folders == ['__pycache__']:\n            folders_to_remove.append(dirpath)\n    for x in folders_to_remove:\n        shutil.rmtree(x)\n\n\n@contextmanager\ndef change_to_git_master() -> Generator[None, None, None]:\n    stash_ref_before = safe_read('.git/refs/stash')\n    subprocess.check_call(['git', 'stash', '-u'])\n    try:\n        branch_before = current_branch()\n        if branch_before != 'master':\n            subprocess.check_call(['git', 'switch', 'master'])\n            remove_pycache_only_folders()\n            subprocess.check_call(['make', 'clean', 'debug'])\n        try:\n            yield\n        finally:\n            if branch_before != 'master':\n                subprocess.check_call(['git', 'switch', branch_before])\n                subprocess.check_call(['make', 'clean', 'debug'])\n    finally:\n        if stash_ref_before != safe_read('.git/refs/stash'):\n            subprocess.check_call(['git', 'stash', 'pop'])\n\n\ndef require_penv() -> None:\n    if 'PENV' not in os.environ:\n        raise SystemExit('The PENV env var is not present, required for uploading releases')\n\n\ndef exec_actions(actions: Iterable[str], args: Any) -> None:\n    for action in actions:\n        print('Running', action)\n        cwd = os.getcwd()\n        globals()[f'run_{action}'](args)\n        os.chdir(cwd)\n\n\ndef main() -> None:\n    global building_nightly\n    parser = argparse.ArgumentParser(description='Publish kitty')\n    parser.add_argument(\n        '--only',\n        default=False,\n        action='store_true',\n        help='Only run the specified action, by default the specified action and all sub-sequent actions are run')\n    parser.add_argument(\n        '--nightly',\n        default=False,\n        action='store_true',\n        help='Upload a nightly release, ignores all other arguments')\n    parser.add_argument(\n        'action',\n        default='all',\n        nargs='?',\n        choices=list(ALL_ACTIONS) + ['all', 'upload_nightly'],\n        help='The action to start with')\n    args = parser.parse_args()\n    require_penv()\n    if args.nightly:\n        with change_to_git_master():\n            building_nightly = True\n            exec_actions(NIGHTLY_ACTIONS, args)\n            subprocess.run(['make', 'clean', 'debug'])\n        return\n    require_git_master()\n    if args.action == 'all':\n        actions = list(ALL_ACTIONS)\n    elif args.action == 'upload_nightly':\n        actions = ['upload_nightly']\n    else:\n        idx = ALL_ACTIONS.index(args.action)\n        actions = ALL_ACTIONS[idx:]\n    if args.only:\n        del actions[1:]\n    else:\n        try:\n            ans = input(f'Publish version \\033[91m{version}\\033[m (y/n): ')\n        except KeyboardInterrupt:\n            ans = 'n'\n        if ans.lower() != 'y':\n            return\n    if actions == ['website']:\n        actions.insert(0, 'html')\n    exec_actions(actions, args)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[project]\nrequires-python = \">=3.10\"\n\n[build-system]\nrequires = [\n    # Needed for installing pure python packages at build time\n    \"installer == 0.7.0\",\n    # Needed for some weird reason for glib\n    \"packaging == 23.1\",\n]\n\n[tool.mypy]\nfiles = 'kitty,kittens,glfw,*.py,docs/conf.py,gen'\nno_implicit_optional = true\nsqlite_cache = true\ncache_fine_grained = true\nwarn_redundant_casts = true\nwarn_unused_ignores = true\nwarn_return_any = true\nwarn_unreachable = true\nwarn_unused_configs = true\ncheck_untyped_defs = true\ndisallow_untyped_defs = true\ndisallow_untyped_decorators = true\ndisallow_untyped_calls = true\ndisallow_incomplete_defs = true\nstrict = true\nstrict_bytes = true\nno_implicit_reexport = true\n\n[tool.pylsp-mypy]\nenabled = true\ndmypy = true\nexclude = ['kitty_tests/*']\nreport_progress = true\n\n[tool.ruff]\nline-length = 160\n\n[tool.ruff.lint]\nselect = ['E', 'F', 'I', 'RUF100']\n\n[tool.ruff.lint.per-file-ignores]\n\"kitty/options/types.py\" = [\"E501\"]\n\"kitty/options/parse.py\" = [\"E501\"]\n\n[tool.ruff.lint.isort]\ndetect-same-package = true\n\n[tool.ruff.format]\nquote-style = 'single'\n"
  },
  {
    "path": "rsync-and-build.sh",
    "content": "#!/bin/sh\n\n# To be used via a script such as\n: <<'COMMENT'\nexport RSYNC_PASSWORD=password\nexport BUILDBOT=rsync://useranme@server/path/to/this/directory\nmkdir -p ~/kitty-src\ncd ~/kitty-src || exit 1\n\nscript=rsync-and-build.sh\nif [[ -e \"$script\" ]]; then\n    . \"./$script\"\nelse\n    rsync -a --include \"$script\" --exclude '*' \"$BUILDBOT\" . && source \"$script\"\nfi\nCOMMENT\n\nrsync --info=progress2 -a -zz --delete --force --exclude /bypy/b --exclude '*_generated.*' --exclude '*_generated_test.*' --exclude '/docs/_build' --include '/.github' --exclude '/.*' --exclude '/dependencies' --exclude '/tags' --exclude '__pycache__' --exclude '/kitty/launcher/kitt*' --exclude '/build' --exclude '/dist' --exclude '*.swp' --exclude '*.swo' --exclude '*.so' --exclude '*.dylib' --exclude '*.dSYM' \"$BUILDBOT\" . && exec ./dev.sh build \"$@\"\n"
  },
  {
    "path": "session.vim",
    "content": "\" Scan the following dirs recursively for tags\nlet g:project_tags_dirs = ['kitty', 'kittens', 'tools']\nset wildignore+==template.py\nset wildignore+=tags\nset expandtab\nset tabstop=4\nset shiftwidth=4\nset softtabstop=0\nset smarttab\npython3 <<endpython\nimport sys\nsys.path.insert(0, os.path.abspath('.'))\nimport kitty\nendpython\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport argparse\nimport glob\nimport json\nimport os\nimport platform\nimport re\nimport runpy\nimport shlex\nimport shutil\nimport struct\nimport subprocess\nimport sys\nimport sysconfig\nimport tempfile\nimport textwrap\nimport time\nfrom contextlib import suppress\nfrom functools import lru_cache, partial\nfrom pathlib import Path\nfrom typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, Sequence, Set, Tuple, Union, cast\n\nfrom glfw import glfw\nfrom glfw.glfw import ISA, BinaryArch, Command, CompileKey, CompilerType\n\nsrc_base = os.path.dirname(os.path.abspath(__file__))\nsetattr(sys, 'running_from_setup', True)\n\ndef check_version_info() -> None:\n    with open(os.path.join(src_base, 'pyproject.toml')) as f:\n        raw = f.read()\n    m = re.search(r'''^requires-python\\s*=\\s*['\"](.+?)['\"]''', raw, flags=re.MULTILINE)\n    assert m is not None\n    minver = m.group(1)\n    match = re.match(r'(>=?)(\\d+)\\.(\\d+)', minver)\n    assert match is not None\n    q = int(match.group(2)), int(match.group(3))\n    if match.group(1) == '>=':\n        is_ok = sys.version_info >= q\n    else:\n        is_ok = sys.version_info > q\n    if not is_ok:\n        exit(f'kitty requires Python {minver}. Current Python version: {\".\".join(map(str, sys.version_info[:3]))}')\n\n\ncheck_version_info()\nverbose = False\nbuild_dir = 'build'\nconstants = os.path.join('kitty', 'constants.py')\nwith open(constants, 'rb') as f:\n    constants = f.read().decode('utf-8')\nappname = re.search(r\"^appname: str = '([^']+)'\", constants, re.MULTILINE).group(1)  # type: ignore\nversion = tuple(\n    map(\n        int,\n        re.search(  # type: ignore\n            r\"^version: Version = Version\\((\\d+), (\\d+), (\\d+)\\)\", constants, re.MULTILINE\n        ).group(1, 2, 3)\n    )\n)\n_plat = sys.platform.lower()\nis_macos = 'darwin' in _plat\nis_openbsd = 'openbsd' in _plat\nis_freebsd = 'freebsd' in _plat\nis_netbsd = 'netbsd' in _plat\nis_dragonflybsd = 'dragonfly' in _plat\nis_bsd = is_freebsd or is_netbsd or is_dragonflybsd or is_openbsd\nis_windows = sys.platform == 'win32'\nis_arm = platform.processor() == 'arm' or platform.machine() in ('arm64', 'aarch64')\nc_std = '' if is_openbsd else '-std=c11'\nEnv = glfw.Env\nenv = Env()\nPKGCONFIG = os.environ.get('PKGCONFIG_EXE', 'pkg-config')\nlink_targets: List[str] = []\nmacos_universal_arches = ('arm64', 'x86_64') if is_arm else ('x86_64', 'arm64')\n\n\ndef LinkKey(output: str) -> CompileKey:\n    return CompileKey('', output)\n\n\nclass CompilationDatabase:\n\n    def __init__(self, incremental: bool = False):\n        self.incremental = incremental\n        self.compile_commands: List[Command] = []\n        self.link_commands: List[Command] = []\n        self.post_link_commands: List[Command] = []\n\n    def add_command(\n        self,\n        desc: str,\n        cmd: List[str],\n        is_newer_func: Callable[[], bool],\n        key: Optional[CompileKey] = None,\n        on_success: Optional[Callable[[], None]] = None,\n        keyfile: Optional[str] = None,\n        is_post_link: bool = False,\n    ) -> None:\n        def no_op() -> None:\n            pass\n\n        if is_post_link:\n            queue = self.post_link_commands\n        else:\n            queue = self.link_commands if keyfile is None else self.compile_commands\n        queue.append(Command(desc, cmd, is_newer_func, on_success or no_op, key, keyfile))\n\n    def build_all(self) -> None:\n\n        def sort_key(compile_cmd: Command) -> int:\n            if compile_cmd.keyfile:\n                return os.path.getsize(compile_cmd.keyfile)\n            return 0\n\n        items = []\n        for compile_cmd in self.compile_commands:\n            if not self.incremental or self.cmd_changed(compile_cmd) or compile_cmd.is_newer_func():\n                items.append(compile_cmd)\n        items.sort(key=sort_key, reverse=True)\n        parallel_run(items)\n\n        items = []\n        for compile_cmd in self.link_commands:\n            if not self.incremental or compile_cmd.is_newer_func():\n                items.append(compile_cmd)\n        parallel_run(items)\n\n        items = []\n        for compile_cmd in self.post_link_commands:\n            if not self.incremental or compile_cmd.is_newer_func():\n                items.append(compile_cmd)\n        parallel_run(items)\n\n    def cmd_changed(self, compile_cmd: Command) -> bool:\n        key, cmd = compile_cmd.key, compile_cmd.cmd\n        dkey = self.db.get(key)\n        if dkey != cmd:\n            return True\n        return False\n\n    def __enter__(self) -> 'CompilationDatabase':\n        self.all_keys: Set[CompileKey] = set()\n        self.dbpath = os.path.abspath(os.path.join(build_dir, 'compile_commands.json'))\n        self.linkdbpath = os.path.join(os.path.dirname(self.dbpath), 'link_commands.json')\n        try:\n            with open(self.dbpath) as f:\n                compilation_database = json.load(f)\n        except FileNotFoundError:\n            compilation_database = []\n        try:\n            with open(self.linkdbpath) as f:\n                link_database = json.load(f)\n        except FileNotFoundError:\n            link_database = []\n        compilation_database = {\n            CompileKey(k['file'], k['output']): k['arguments'] for k in compilation_database\n        }\n        self.db = compilation_database\n        self.linkdb = {tuple(k['output']): k['arguments'] for k in link_database}\n        return self\n\n    def __exit__(self, *a: object) -> None:\n        cdb = self.db\n        for key in set(cdb) - self.all_keys:\n            del cdb[key]\n        compilation_database = [\n            {'file': c.key.src, 'arguments': c.cmd, 'directory': src_base, 'output': c.key.dest} for c in self.compile_commands if c.key is not None\n        ]\n        with suppress(FileNotFoundError):\n            with open(self.dbpath, 'w') as f:\n                json.dump(compilation_database, f, indent=2, sort_keys=True)\n            with open(self.linkdbpath, 'w') as f:\n                json.dump([{'output': c.key, 'arguments': c.cmd, 'directory': src_base} for c in self.link_commands], f, indent=2, sort_keys=True)\n\n\n\nclass Options:\n    action: str = 'build'\n    debug: bool = False\n    verbose: int = 0\n    sanitize: bool = False\n    prefix: str = './linux-package'\n    dir_for_static_binaries: str = 'build/static'\n    skip_code_generation: bool = False\n    skip_building_kitten: bool = False\n    clean_for_cross_compile: bool = False\n    python_compiler_flags: str = ''\n    python_linker_flags: str = ''\n    incremental: bool = True\n    build_dsym: bool = False\n    ignore_compiler_warnings: bool = False\n    profile: bool = False\n    libdir_name: str = 'lib'\n    extra_logging: List[str] = []\n    extra_include_dirs: List[str] = []\n    extra_library_dirs: List[str] = []\n    link_time_optimization: bool = 'KITTY_NO_LTO' not in os.environ\n    update_check_interval: float = 24.0\n    shell_integration: str = 'enabled'\n    egl_library: Optional[str] = os.getenv('KITTY_EGL_LIBRARY')\n    startup_notification_library: Optional[str] = os.getenv('KITTY_STARTUP_NOTIFICATION_LIBRARY')\n    canberra_library: Optional[str] = os.getenv('KITTY_CANBERRA_LIBRARY')\n    systemd_library: Optional[str] = os.getenv('KITTY_SYSTEMD_LIBRARY')\n    fontconfig_library: Optional[str] = os.getenv('KITTY_FONTCONFIG_LIBRARY')\n    building_arch: str = ''\n\n    # Extras\n    compilation_database: CompilationDatabase = CompilationDatabase()\n    vcs_rev: str = ''\n\ndef emphasis(text: str) -> str:\n    if sys.stdout.isatty():\n        text = f'\\033[32m{text}\\033[39m'\n    return text\n\n\ndef error(text: str) -> str:\n    if sys.stdout.isatty():\n        text = f'\\033[91m{text}\\033[39m'\n    return text\n\n\ndef pkg_config(pkg: str, *args: str, extra_pc_dir: str = '', fatal: bool = True) -> List[str]:\n    env = os.environ.copy()\n    if extra_pc_dir:\n        pp = env.get('PKG_CONFIG_PATH', '')\n        if pp:\n            pp += os.pathsep\n        env['PKG_CONFIG_PATH'] = f'{pp}{extra_pc_dir}'\n    cmd = [PKGCONFIG, pkg] + list(args)\n    try:\n        return list(\n            filter(\n                None,\n                shlex.split(\n                    subprocess.check_output(cmd, env=env, stderr=None if fatal else subprocess.DEVNULL).decode('utf-8')\n                )\n            )\n        )\n    except FileNotFoundError:\n        if is_windows:\n            raise SystemExit(\n                f'The command {error(PKGCONFIG)} was not found. You might need to install MSYS2 and its'\n                ' mingw-w64-x86_64-pkg-config package, or use WSL.')\n        raise\n    except subprocess.CalledProcessError:\n        if fatal:\n            raise SystemExit(f'The package {error(pkg)} was not found on your system')\n        raise\n\n\ndef pkg_version(package: str) -> Tuple[int, int]:\n    ver = subprocess.check_output([\n        PKGCONFIG, package, '--modversion']).decode('utf-8').strip()\n    m = re.match(r'(\\d+).(\\d+)', ver)\n    if m is not None:\n        qmajor, qminor = map(int, m.groups())\n        return qmajor, qminor\n    return -1, -1\n\n\ndef libcrypto_flags() -> Tuple[List[str], List[str]]:\n    # Apple use their special snowflake TLS libraries and additionally\n    # have an ancient broken system OpenSSL, so we need to check for one\n    # installed by all the various macOS package managers.\n    extra_pc_dir = ''\n\n    try:\n        cflags = pkg_config('libcrypto', '--cflags-only-I', fatal=False)\n    except subprocess.CalledProcessError:\n        if is_macos:\n            import ssl\n            v = ssl.OPENSSL_VERSION_INFO\n            pats = f'{v[0]}.{v[1]}', f'{v[0]}'\n            for pat in pats:\n                q = f'opt/openssl@{pat}/lib/pkgconfig'\n                openssl_dirs = glob.glob(f'/opt/homebrew/{q}') + glob.glob(f'/usr/local/{q}')\n                if openssl_dirs:\n                    break\n            else:\n                raise SystemExit(f'Failed to find OpenSSL version {v[0]}.{v[1]} on your system')\n            extra_pc_dir = os.pathsep.join(openssl_dirs)\n        cflags = pkg_config('libcrypto', '--cflags-only-I', extra_pc_dir=extra_pc_dir)\n    ldflags = pkg_config('libcrypto', '--libs', extra_pc_dir=extra_pc_dir)\n    # Workaround bug in homebrew openssl package. This bug appears in CI only\n    if is_macos and ldflags and 'homebrew/Cellar' in ldflags[0] and not ldflags[0].endswith('/lib'):\n        ldflags.insert(0, ldflags[0] + '/lib')\n    return cflags, ldflags\n\n\n@lru_cache(maxsize=2)\ndef xxhash_flags() -> tuple[list[str], list[str]]:\n    return pkg_config('libxxhash', '--cflags-only-I'), pkg_config('libxxhash', '--libs')\n\n\n\ndef at_least_version(package: str, major: int, minor: int = 0) -> None:\n    q = f'{major}.{minor}'\n    if subprocess.run([PKGCONFIG, package, f'--atleast-version={q}']\n                      ).returncode != 0:\n        qmajor = qminor = 0\n        try:\n            ver = subprocess.check_output([PKGCONFIG, package, '--modversion']\n                                          ).decode('utf-8').strip()\n            m = re.match(r'(\\d+).(\\d+)', ver)\n            if m is not None:\n                qmajor, qminor = map(int, m.groups())\n        except Exception:\n            ver = 'not found'\n        if qmajor < major or (qmajor == major and qminor < minor):\n            raise SystemExit(f'{error(package)} >= {major}.{minor} is required, found version: {ver}')\n\n\ndef cc_version() -> Tuple[List[str], Tuple[int, int]]:\n    if 'CC' in os.environ:\n        q = os.environ['CC']\n    else:\n        if is_windows:\n            if shutil.which('cl.exe'):\n                q = 'cl.exe'\n            elif shutil.which('gcc'):\n                q = 'gcc'\n            elif shutil.which('clang'):\n                q = 'clang'\n            else:\n                raise SystemExit('No C compiler found. On Windows, install Visual Studio (MSVC) or MinGW-w64 (gcc/clang).')\n        elif is_macos:\n            q = 'clang'\n        else:\n            if shutil.which('gcc'):\n                q = 'gcc'\n            elif shutil.which('clang'):\n                q = 'clang'\n            else:\n                q = 'cc'\n    cc = shlex.split(q)\n    if is_windows and cc[0].lower() == 'cl.exe':\n        raw = subprocess.check_output(cc + ['/?']).decode()\n        if m := re.search(r'Compiler Version ([\\d\\.]+)', raw):\n            parts = tuple(map(int, m.group(1).split('.')))\n            return cc, (parts[0], parts[1])\n    raw = subprocess.check_output(cc + ['-dumpversion']).decode('utf-8')\n    ver_ = raw.strip().split('.')[:2]\n    try:\n        if len(ver_) == 1:\n            ver = int(ver_[0]), 0\n        else:\n            ver = int(ver_[0]), int(ver_[1])\n    except Exception:\n        ver = (0, 0)\n    return cc, ver\n\n\ndef get_python_include_paths() -> List[str]:\n    ans = []\n    for name in sysconfig.get_path_names():\n        if 'include' in name:\n            ans.append(name)\n\n    def gp(x: str) -> Optional[str]:\n        return sysconfig.get_path(x)\n\n    return sorted(frozenset(filter(None, map(gp, sorted(ans)))))\n\n\ndef get_python_flags(args: Options, cflags: List[str], for_main_executable: bool = False) -> List[str]:\n    if args.python_compiler_flags:\n        cflags.extend(shlex.split(args.python_compiler_flags))\n    else:\n        cflags.extend(f'-I{x}' for x in get_python_include_paths())\n    if args.python_linker_flags:\n        return shlex.split(args.python_linker_flags)\n    libs: List[str] = []\n    libs += (sysconfig.get_config_var('LIBS') or '').split()\n    libs += (sysconfig.get_config_var('SYSLIBS') or '').split()\n    fw = sysconfig.get_config_var('PYTHONFRAMEWORK')\n    if fw:\n        for var in 'data include stdlib'.split():\n            val = sysconfig.get_path(var)\n            if val and f'/{fw}.framework' in val:\n                fdir = val[:val.index(f'/{fw}.framework')]\n                if os.path.isdir(\n                    os.path.join(fdir, f'{fw}.framework')\n                ):\n                    framework_dir = fdir\n                    break\n        else:\n            raise SystemExit('Failed to find Python framework')\n        ldlib = sysconfig.get_config_var('LDLIBRARY')\n        if ldlib:\n            libs.append(os.path.join(framework_dir, ldlib))\n    else:\n        ldlib = sysconfig.get_config_var('LIBDIR')\n        if ldlib:\n            libs += [f'-L{ldlib}']\n        ldlib = sysconfig.get_config_var('VERSION')\n        if ldlib:\n            libs += [f'-lpython{ldlib}{sys.abiflags}']\n        lval = sysconfig.get_config_var('LINKFORSHARED') or ''\n        if not for_main_executable:\n            # Python sets the stack size on macOS which is not allowed unless\n            # compiling an executable https://github.com/kovidgoyal/kitty/issues/289\n            lval = re.sub(r'-Wl,-stack_size,\\d+', '', lval)\n        libs += list(filter(None, lval.split()))\n    return libs\n\n\ndef get_sanitize_args(cc: List[str], ccver: Tuple[int, int]) -> List[str]:\n    return ['-fsanitize=address,undefined', '-fno-omit-frame-pointer']\n\n\ndef get_binary_arch(path: str) -> BinaryArch:\n    with open(path, 'rb') as f:\n        sig = f.read(64)\n    if sig.startswith(b'\\x7fELF'):  # ELF\n        bits = {1: 32, 2: 64}[sig[4]]\n        endian = {1: '<', 2: '>'}[sig[5]]\n        machine, = struct.unpack_from(endian + 'H', sig, 0x12)\n        isa = {i.value:i for i in ISA}.get(machine, ISA.Other)\n    elif sig[:4] in (b'\\xcf\\xfa\\xed\\xfe', b'\\xce\\xfa\\xed\\xfe'): # Mach-O\n        s, cpu_type, = struct.unpack_from('<II', sig, 0)\n        bits = {0xfeedface: 32, 0xfeedfacf: 64}[s]\n        cpu_type &= 0xff\n        isa = {0x7: ISA.AMD64, 0xc: ISA.ARM64}[cpu_type]\n    else:\n        raise SystemExit(f'Unknown binary format with signature: {sig[:4]!r}')\n    return BinaryArch(bits=bits, isa=isa)\n\n\ndef test_compile(\n    cc: List[str], *cflags: str,\n    src: str = '',\n    source_ext: str = 'c',\n    link_also: bool = True,\n    show_stderr: bool = False,\n    libraries: Iterable[str] = (),\n    ldflags: Iterable[str] = (),\n    get_output_arch: bool = False,\n) -> Union[bool, BinaryArch]:\n    src = src or 'int main(void) { return 0; }'\n    with tempfile.TemporaryDirectory(prefix='kitty-test-compile-') as tdir:\n        with open(os.path.join(tdir, f'source.{source_ext}'), 'w', encoding='utf-8') as srcf:\n            print(src, file=srcf)\n        output = os.path.join(tdir, 'source.output')\n        ret = subprocess.Popen(\n            cc + ['-Werror=implicit-function-declaration'] + list(cflags) + ([] if link_also else ['-c']) +\n            ['-o', output, srcf.name] +\n            [f'-l{x}' for x in libraries] + list(ldflags),\n            stdout=subprocess.DEVNULL, stdin=subprocess.DEVNULL,\n            stderr=None if show_stderr else subprocess.DEVNULL\n        ).wait()\n        if get_output_arch:\n            if ret != 0:\n                raise SystemExit(f'Failed to determine target architecture compiling test program failed with exit code: {ret}')\n            return get_binary_arch(output)\n        return ret == 0\n\n\ndef first_successful_compile(cc: List[str], *cflags: str, src: str = '', source_ext: str = 'c') -> str:\n    for x in cflags:\n        if test_compile(cc, *shlex.split(x), src=src, source_ext=source_ext):\n            return x\n    return ''\n\n\ndef set_arches(flags: List[str], *arches: str) -> None:\n    while True:\n        try:\n            idx = flags.index('-arch')\n        except ValueError:\n            break\n        del flags[idx]\n        del flags[idx]\n    for arch in arches:\n        flags.extend(('-arch', arch))\n\n\ndef init_env(\n    debug: bool = False,\n    sanitize: bool = False,\n    native_optimizations: bool = True,\n    link_time_optimization: bool = True,\n    profile: bool = False,\n    egl_library: Optional[str] = None,\n    startup_notification_library: Optional[str] = None,\n    canberra_library: Optional[str] = None,\n    systemd_library: Optional[str] = None,\n    fontconfig_library: Optional[str] = None,\n    extra_logging: Iterable[str] = (),\n    extra_include_dirs: Iterable[str] = (),\n    ignore_compiler_warnings: bool = False,\n    building_arch: str = '',\n    extra_library_dirs: Iterable[str] = (),\n    verbose: bool = True,\n    vcs_rev: str = '',\n) -> Env:\n    native_optimizations = native_optimizations and not sanitize\n    cc, ccver = cc_version()\n    if verbose:\n        print('CC:', cc, ccver)\n    stack_protector = first_successful_compile(cc, '-fstack-protector-strong', '-fstack-protector')\n    missing_braces = ''\n    if ccver < (5, 2):\n        missing_braces = '-Wno-missing-braces'\n    df = '-g3'\n    float_conversion = ''\n    if ccver >= (5, 0):\n        df += ' -Og'\n        float_conversion = '-Wfloat-conversion'\n    fortify_source = '' if sanitize and is_macos else '-D_FORTIFY_SOURCE=2'\n    optimize = df if debug or sanitize else '-O3'\n    sanitize_args = get_sanitize_args(cc, ccver) if sanitize else []\n    cppflags_ = os.environ.get(\n        'OVERRIDE_CPPFLAGS', '-D{}DEBUG'.format('' if debug else 'N'),\n    )\n    cppflags = shlex.split(cppflags_)\n    for el in extra_logging:\n        cppflags.append('-DDEBUG_{}'.format(el.upper().replace('-', '_')))\n    has_copy_file_range = test_compile(cc, src='#define _GNU_SOURCE 1\\n#include <unistd.h>\\nint main() { copy_file_range(1, NULL, 2, NULL, 0, 0); return 0; }')\n    werror = '' if ignore_compiler_warnings else '-pedantic-errors -Werror'\n    sanitize_flag = ' '.join(sanitize_args)\n    env_cflags = shlex.split(os.environ.get('CFLAGS', ''))\n    env_cppflags = shlex.split(os.environ.get('CPPFLAGS', ''))\n    env_ldflags = shlex.split(os.environ.get('LDFLAGS', ''))\n    # Newer clang does not use -fno-plt leading to an error\n    no_plt = '-fno-plt' if test_compile(cc, '-fno-plt', '-Werror') else ''\n\n    cflags_ = os.environ.get(\n        'OVERRIDE_CFLAGS', (\n            f'-Wextra {float_conversion} -Wno-missing-field-initializers -Wall -Wstrict-prototypes {c_std}'\n            f' {werror} {optimize} {sanitize_flag} -fwrapv {stack_protector} {missing_braces}'\n            f' -pipe -fvisibility=hidden {no_plt}'\n        )\n    )\n    cflags = shlex.split(cflags_) + shlex.split(\n        sysconfig.get_config_var('CCSHARED') or ''\n    )\n    ldflags_ = os.environ.get(\n        'OVERRIDE_LDFLAGS',\n        '-Wall ' + ' '.join(sanitize_args) + ('' if debug else ' -O3')\n    )\n    ldflags = shlex.split(ldflags_)\n    ldflags.append('-shared')\n    cppflags += env_cppflags\n    cflags += env_cflags\n    if fortify_source:\n        for x in cflags:\n            if '_FORTIFY_SOURCE' in x:\n                break\n        else:\n            cflags.append(fortify_source)\n    ldflags += env_ldflags\n    if not debug and not sanitize and not is_openbsd and link_time_optimization:\n        # See https://github.com/google/sanitizers/issues/647\n        cflags.append('-flto')\n        ldflags.append('-flto')\n\n    if debug:\n        cflags.append('-DKITTY_DEBUG_BUILD')\n\n    if profile:\n        cppflags.append('-DWITH_PROFILER')\n        cflags.append('-g3')\n        ldflags.append('-lprofiler')\n\n    if debug or profile:\n        cflags.append('-fno-omit-frame-pointer')\n\n    library_paths: Dict[str, List[str]] = {}\n\n    def add_lpath(which: str, name: str, val: Optional[str]) -> None:\n        if val:\n            if '\"' in val:\n                raise SystemExit(f'Cannot have quotes in library paths: {val}')\n            library_paths.setdefault(which, []).append(f'{name}=\"{val}\"')\n\n    add_lpath('glfw/egl_context.c', '_GLFW_EGL_LIBRARY', egl_library)\n    add_lpath('kitty/desktop.c', '_KITTY_STARTUP_NOTIFICATION_LIBRARY', startup_notification_library)\n    add_lpath('kitty/desktop.c', '_KITTY_CANBERRA_LIBRARY', canberra_library)\n    add_lpath('kitty/systemd.c', '_KITTY_SYSTEMD_LIBRARY', systemd_library)\n    add_lpath('kitty/fontconfig.c', '_KITTY_FONTCONFIG_LIBRARY', fontconfig_library)\n\n    for path in extra_include_dirs:\n        cflags.append(f'-I{path}')\n\n    ldpaths = []\n    for path in extra_library_dirs:\n        ldpaths.append(f'-L{path}')\n\n    if os.environ.get(\"DEVELOP_ROOT\"):\n        cflags.insert(0, f'-I{os.environ[\"DEVELOP_ROOT\"]}/include')\n        ldpaths.insert(0, f'-L{os.environ[\"DEVELOP_ROOT\"]}/lib')\n\n    if building_arch:\n        set_arches(cflags, building_arch)\n        set_arches(ldflags, building_arch)\n    ba = test_compile(cc, *(cppflags + cflags), ldflags=ldflags, get_output_arch=True)\n    assert isinstance(ba, BinaryArch)\n    if ba.isa not in (ISA.AMD64, ISA.X86, ISA.ARM64):\n        cppflags.append('-DKITTY_NO_SIMD')\n\n    control_flow_protection = ''\n    if ba.isa == ISA.AMD64:\n        control_flow_protection = '-fcf-protection=full' if ccver >= (9, 0) else ''\n    elif ba.isa == ISA.ARM64:\n        # Using -mbranch-protection=standard causes crashes on Linux ARM, reported\n        # in https://github.com/kovidgoyal/kitty/issues/6845#issuecomment-1835886938\n        if is_macos:\n            control_flow_protection = '-mbranch-protection=standard'\n\n    if control_flow_protection:\n        cflags.append(control_flow_protection)\n\n    if native_optimizations and ba.isa in (ISA.AMD64, ISA.X86):\n        cflags.extend('-march=native -mtune=native'.split())\n\n    ans = Env(\n        cc, cppflags, cflags, ldflags, library_paths, binary_arch=ba, native_optimizations=native_optimizations,\n        ccver=ccver, ldpaths=ldpaths, vcs_rev=vcs_rev,\n    )\n    ans.has_copy_file_range = bool(has_copy_file_range)\n    if ans.compiler_type is CompilerType.gcc:\n        cflags.append('-Wno-packed-bitfield-compat')\n    if verbose:\n        print(ans.cc_version_string.strip())\n        print('Detected:', ans.compiler_type)\n    return ans\n\n\ndef kitty_env(args: Options) -> Env:\n    ans = env.copy()\n    cflags = ans.cflags\n    cflags.append('-pthread')\n    cppflags = ans.cppflags\n    # We add 4000 to the primary version because vim turns on SGR mouse mode\n    # automatically if this version is high enough\n    ans.primary_version = version[0] + 4000\n    ans.secondary_version = version[1]\n    ans.xt_version = '.'.join(map(str, version))\n\n    xxhash = xxhash_flags()\n    at_least_version('harfbuzz', 1, 5)\n    cflags.extend(pkg_config('libpng', '--cflags-only-I'))\n    cflags.extend(pkg_config('lcms2', '--cflags-only-I'))\n    cflags.extend(xxhash[0])\n    # simde doesnt come with pkg-config files but some Linux distros add\n    # them and on macOS when building with homebrew it is required\n    with suppress(SystemExit, subprocess.CalledProcessError):\n        cflags.extend(pkg_config('simde', '--cflags-only-I', fatal=False))\n    libcrypto_cflags, libcrypto_ldflags = libcrypto_flags()\n    cflags.extend(libcrypto_cflags)\n    if is_macos:\n        platform_libs = [\n            '-framework', 'Carbon', '-framework', 'CoreText', '-framework', 'CoreGraphics',\n            '-framework', 'AudioToolbox',\n        ]\n        test_program_src = '''#include <UserNotifications/UserNotifications.h>\n        int main(void) { return 0; }\\n'''\n        user_notifications_framework = first_successful_compile(\n            ans.cc, '-framework UserNotifications', src=test_program_src, source_ext='m')\n        if user_notifications_framework:\n            platform_libs.extend(shlex.split(user_notifications_framework))\n        else:\n            raise SystemExit('UserNotifications framework missing')\n        # Apple deprecated OpenGL in Mojave (10.14) silence the endless\n        # warnings about it\n        cppflags.append('-DGL_SILENCE_DEPRECATION')\n    else:\n        cflags.extend(pkg_config('cairo-fc', '--cflags-only-I'))\n        platform_libs = []\n        platform_libs.extend(pkg_config('cairo-fc', '--libs'))\n    cflags.extend(pkg_config('harfbuzz', '--cflags-only-I'))\n    platform_libs.extend(pkg_config('harfbuzz', '--libs'))\n    pylib = get_python_flags(args, cflags)\n    gl_libs = ['-framework', 'OpenGL'] if is_macos else pkg_config('gl', '--libs')\n    libpng = pkg_config('libpng', '--libs')\n    lcms2 = pkg_config('lcms2', '--libs')\n    ans.ldpaths += pylib + platform_libs + gl_libs + libpng + lcms2 + libcrypto_ldflags + xxhash[1]\n    if is_macos:\n        ans.ldpaths.extend('-framework Cocoa'.split())\n    elif not is_openbsd:\n        ans.ldpaths += ['-lrt']\n        if '-ldl' not in ans.ldpaths:\n            ans.ldpaths.append('-ldl')\n    if '-lz' not in ans.ldpaths:\n        ans.ldpaths.append('-lz')\n\n    return ans\n\n\ndef define(x: str) -> str:\n    return f'-D{x}'\n\n\ndef run_tool(cmd: Union[str, List[str]], desc: Optional[str] = None) -> None:\n    if verbose:\n        desc = None\n\n    if is_windows:\n        # On Windows, it's generally safer to pass a single string to Popen with shell=True\n        # for commands that might involve shell built-ins or complex paths.\n        if isinstance(cmd, list):\n            wcmd_to_execute = shlex.join(cmd)\n        else:\n            wcmd_to_execute = cmd\n        print(desc or wcmd_to_execute)\n        p = subprocess.Popen(wcmd_to_execute, shell=True)\n    else:\n        # On Unix-like systems, passing a list is generally preferred for security and clarity.\n        if isinstance(cmd, str):\n            cmd_to_execute = shlex.split(cmd) # Split the string into a list of arguments\n        else:\n            cmd_to_execute = cmd\n        print(desc or ' '.join(cmd_to_execute))\n        p = subprocess.Popen(cmd_to_execute)\n\n    ret = p.wait()\n    if ret != 0:\n        if desc:\n            print(wcmd_to_execute if is_windows else cmd_to_execute) # Print the actual command that was executed\n        raise SystemExit(ret)\n\n\n@lru_cache\ndef get_vcs_rev() -> str:\n    ans = ''\n    git_exe = shutil.which('git') or 'git'\n    if os.path.exists('.git'):\n        try:\n            rev = subprocess.check_output([git_exe, 'rev-parse', 'HEAD']).decode('utf-8')\n            ans = rev.strip()\n        except (subprocess.CalledProcessError, FileNotFoundError):\n            # Fallback for older git versions or other issues\n            try:\n                with open(os.path.join('.git', 'HEAD')) as f:\n                    head_content = f.read().strip()\n                if head_content.startswith('ref:'):\n                    ref_path = head_content[5:].strip()\n                    with open(os.path.join('.git', ref_path)) as f:\n                        ans = f.read().strip()\n                else:\n                    ans = head_content\n            except Exception as e:\n                print(error(f'Warning: Failed to get git revision from .git directory: {e}'), file=sys.stderr)\n    return ans\n\n\n@lru_cache\ndef base64_defines(isa: ISA) -> List[str]:\n    defs = {\n        'HAVE_AVX512': 0,\n        'HAVE_AVX2': 0,\n        'HAVE_NEON32': 0,\n        'HAVE_NEON64': 0,\n        'HAVE_SSSE3': 0,\n        'HAVE_SSE41': 0,\n        'HAVE_SSE42': 0,\n        'HAVE_AVX': 0,\n    }\n    if isa == ISA.ARM64:\n        defs['HAVE_NEON64'] = 1\n    elif isa == ISA.AMD64:\n        defs['HAVE_AVX2'] = 1\n        defs['HAVE_AVX'] = 1\n        defs['HAVE_SSE42'] = 1\n        defs['HAVE_SSE41'] = 1\n        defs['HAVE_SSE3'] = 1\n    elif isa == ISA.X86:\n        defs['HAVE_SSE42'] = 1\n        defs['HAVE_SSE41'] = 1\n        defs['HAVE_SSE3'] = 1\n    return [f'{k}={v}' for k, v in defs.items()]\n\n\ndef get_source_specific_defines(env: Env, src: str) -> Tuple[str, List[str], Optional[List[str]]]:\n    if src == 'kitty/vt-parser-dump.c':\n        return 'kitty/vt-parser.c', [], ['DUMP_COMMANDS']\n    if src == 'kitty/data-types.c':\n        if not env.vcs_rev:\n            env.vcs_rev = get_vcs_rev()\n        return src, [], [f'KITTY_VCS_REV=\"{env.vcs_rev}\"', f'WRAPPED_KITTENS=\"{wrapped_kittens()}\"']\n    if src.startswith('3rdparty/base64/'):\n        return src, ['3rdparty/base64',], base64_defines(env.binary_arch.isa)\n    if src == 'kitty/screen.c':\n        return src, [], [f'PRIMARY_VERSION={env.primary_version}', f'SECONDARY_VERSION={env.secondary_version}', f'XT_VERSION=\"{env.xt_version}\"']\n    if src == 'kitty/fast-file-copy.c':\n        return src, [], (['HAS_COPY_FILE_RANGE'] if env.has_copy_file_range else None)\n    try:\n        return src, [], env.library_paths[src]\n    except KeyError:\n        return src, [], None\n\n\ndef get_source_specific_cflags(env: Env, src: str) -> List[str]:\n    ans = list(env.cflags)\n    # SIMD specific flags\n    if src in ('kitty/simd-string-128.c', 'kitty/simd-string-256.c'):\n        # simde recommends these are used for best performance\n        ans.extend(('-fopenmp-simd', '-DSIMDE_ENABLE_OPENMP'))\n        if env.binary_arch.isa in (ISA.AMD64, ISA.X86):\n            ans.append('-msse4.2' if '128' in src else '-mavx2')\n            if '256' in src:\n                # We have manual vzeroupper so prevent compiler from emitting it causing duplicates\n                if env.compiler_type is CompilerType.clang:\n                    ans.append('-mllvm')\n                    ans.append('-x86-use-vzeroupper=0')\n                else:\n                    ans.append('-mno-vzeroupper')\n    elif src.startswith('3rdparty/base64/lib/arch/'):\n        if env.binary_arch.isa in (ISA.AMD64, ISA.X86):\n            q = src.split(os.path.sep)\n            if 'sse3' in q:\n                ans.append('-msse3')\n            elif 'sse41' in q:\n                ans.append('-msse4.1')\n            elif 'sse42' in q:\n                ans.append('-msse4.2')\n            elif 'avx' in q:\n                ans.append('-mavx')\n            elif 'avx2' in q:\n                ans.append('-mavx2')\n    return ans\n\n\ndef newer(dest: str, *sources: str) -> bool:\n    try:\n        dtime = os.path.getmtime(dest)\n    except OSError:\n        return True\n    for s in sources:\n        with suppress(FileNotFoundError):\n            if os.path.getmtime(s) >= dtime:\n                return True\n    return False\n\n\ndef dependecies_for(src: str, obj: str, all_headers: Iterable[str]) -> Iterable[str]:\n    dep_file = obj.rpartition('.')[0] + '.d'\n    try:\n        with open(dep_file) as f:\n            deps = f.read()\n    except FileNotFoundError:\n        yield src\n        yield from iter(all_headers)\n    else:\n        RE_INC = re.compile(\n            r'^(?P<target>.+?):\\s+(?P<deps>.+?)$', re.MULTILINE\n        )\n        SPACE_TOK = '\\x1B'\n\n        text = deps.replace('\\\\\\n', ' ').replace('\\\\ ', SPACE_TOK)\n        for match in RE_INC.finditer(text):\n            files = (\n                f.replace(SPACE_TOK, ' ') for f in match.group('deps').split()\n            )\n            for path in files:\n                path = os.path.abspath(path)\n                if path.startswith(src_base):\n                    yield path\n\n\ndef parallel_run(items: List[Command]) -> None:\n    try:\n        num_workers = max(2, os.cpu_count() or 1)\n    except Exception:\n        num_workers = 2\n    items = list(reversed(items))\n    workers: Dict[int, Tuple[Optional[Command], Optional['subprocess.Popen[bytes]']]] = {}\n    failed = None\n    num, total = 0, len(items)\n\n    def wait() -> None:\n        nonlocal failed\n        if not workers:\n            return\n        pid, s = os.wait()\n        compile_cmd, w = workers.pop(pid, (None, None))\n        if compile_cmd is None:\n            return\n        if ((s & 0xff) != 0 or ((s >> 8) & 0xff) != 0):\n            if failed is None:\n                failed = compile_cmd\n        elif compile_cmd.on_success is not None:\n            compile_cmd.on_success()\n\n    printed = False\n    isatty = sys.stdout.isatty()\n    while items and failed is None:\n        while len(workers) < num_workers and items:\n            compile_cmd = items.pop()\n            num += 1\n            if verbose:\n                print(' '.join(compile_cmd.cmd))\n            elif isatty:\n                print(f'\\r\\x1b[K[{num}/{total}] {compile_cmd.desc}', end='')  # ]]\n            else:\n                print(f'[{num}/{total}] {compile_cmd.desc}', flush=True)\n            printed = True\n            w = subprocess.Popen(compile_cmd.cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)\n            workers[w.pid] = compile_cmd, w\n        wait()\n    while len(workers):\n        wait()\n    if not verbose and printed:\n        print(' done')\n    if failed:\n        print(failed.desc)\n        run_tool(list(failed.cmd))\n\n\ndef add_builtin_fonts(args: Options) -> None:\n    fonts_dir = os.path.join(src_base, 'fonts')\n    os.makedirs(fonts_dir, exist_ok=True)\n\n    for psname, (filename, human_name) in {\n        'SymbolsNFM': ('SymbolsNerdFontMono-Regular.ttf', 'Symbols NERD Font Mono')\n    }.items():\n        dest = os.path.join(fonts_dir, filename)\n        if os.path.exists(dest):\n            continue\n        font_file = ''\n        if is_macos:\n            candidates = (\n                os.path.expanduser('~/Library/Fonts'), '/Library/Fonts', '/System/Library/Fonts', '/Network/Library/Fonts')\n            for candidate in candidates:\n                q = os.path.join(candidate, filename)\n                if os.path.exists(q):\n                    font_file = q\n                    break\n            else:\n                for candidate in candidates:\n                    for root, _, files in os.walk(candidate):\n                        if filename in files:\n                            font_file = os.path.join(root, filename)\n                            break\n                    if font_file:\n                        break\n        elif is_windows:\n            for candidate in (\n                    os.path.expandvars(r'%userprofile%\\AppData\\Local\\Microsoft\\Windows\\Fonts'),\n                    os.path.expandvars(r'%windir%\\Fonts'),\n            ):\n                q = os.path.join(candidate, filename)\n                if os.path.exists(q):\n                    font_file = q\n                    break\n        else:\n            lines = subprocess.check_output([\n                'fc-list', '--format', '%{file}\\n%{postscriptname}', f':postscriptname={psname}']).decode().splitlines()\n            if len(lines) != 2:\n                raise SystemExit(f'fc-match returned unexpected output: {lines}')\n            if lines[1] != psname:\n                raise SystemExit(f'The font {human_name!r} was not found on your system, please install it')\n            font_file = lines[0]\n        if not font_file:\n            raise SystemExit(f'The font {human_name!r} was not found on your system, please install it')\n        print(f'Copying {human_name!r} from {font_file}')\n        shutil.copy(font_file, dest)\n        os.chmod(dest, 0o644)\n\n\ndef compile_c_extension(\n    kenv: Env,\n    module: str,\n    compilation_database: CompilationDatabase,\n    sources: List[str],\n    headers: List[str],\n    desc_prefix: str = '',\n    build_dsym: bool = False,\n) -> None:\n    prefix = os.path.basename(module)\n    objects = [\n        os.path.join(build_dir, f'{prefix}-{src.replace(\"/\", \"-\")}.o')\n        for src in sources\n    ]\n\n    for original_src, dest in zip(sources, objects):\n        src = original_src\n        cppflags = kenv.cppflags[:]\n        src, include_paths, defines = get_source_specific_defines(kenv, src)\n        if defines is not None:\n            cppflags.extend(map(define, defines))\n        cflags = get_source_specific_cflags(kenv, src)\n        cmd = kenv.cc + ['-MMD'] + cppflags + [f'-I{x}' for x in include_paths] + cflags\n        cmd += ['-c', src] + ['-o', dest]\n        key = CompileKey(original_src, os.path.basename(dest))\n        desc = f'Compiling {emphasis(desc_prefix + src)} ...'\n        compilation_database.add_command(desc, cmd, partial(newer, dest, *dependecies_for(src, dest, headers)), key=key, keyfile=src)\n    dest = os.path.join(build_dir, f'{module}.so')\n    real_dest = f'{module}.so'\n    link_targets.append(os.path.abspath(real_dest))\n    os.makedirs(os.path.dirname(dest), exist_ok=True)\n    desc = f'Linking {emphasis(desc_prefix + module)} ...'\n    # Old versions of clang don't like -pthread being passed to the linker\n    # Don't treat linker warnings as errors (linker generates spurious\n    # warnings on some old systems)\n    unsafe = {'-pthread', '-Werror', '-pedantic-errors'}\n    linker_cflags = list(filter(lambda x: x not in unsafe, kenv.cflags))\n    cmd = kenv.cc + linker_cflags + kenv.ldflags + objects + kenv.ldpaths + ['-o', dest]\n\n    def on_success() -> None:\n        os.rename(dest, real_dest)\n\n    compilation_database.add_command(desc, cmd, partial(newer, real_dest, *objects), on_success=on_success, key=LinkKey(f'{module}.so'))\n    if is_macos and build_dsym:\n        real_dest = os.path.abspath(real_dest)\n        desc = f'Linking dSYM {emphasis(desc_prefix + module)} ...'\n        dsym = f'{real_dest}.dSYM/Contents/Resources/DWARF/{os.path.basename(real_dest)}'\n        compilation_database.add_command(desc, ['dsymutil', real_dest], partial(newer, dsym, real_dest), key=LinkKey(dsym), is_post_link=True)\n\n\ndef find_c_files() -> Tuple[List[str], List[str]]:\n    ans, headers = [], []\n    d = 'kitty'\n    exclude = {\n        'fontconfig.c', 'freetype.c', 'desktop.c', 'freetype_render_ui_text.c'\n    } if is_macos else {\n        'core_text.m', 'cocoa_window.m', 'macos_process_info.c'\n    }\n    for x in sorted(os.listdir(d)):\n        ext = os.path.splitext(x)[1]\n        if ext in ('.c', '.m') and os.path.basename(x) not in exclude:\n            ans.append(os.path.join('kitty', x))\n        elif ext == '.h':\n            headers.append(os.path.join('kitty', x))\n    ans.append('kitty/vt-parser-dump.c')\n\n    # ringbuf\n    ans.append('3rdparty/ringbuf/ringbuf.c')\n    # base64\n    ans.extend(glob.glob('3rdparty/base64/lib/arch/*/codec.c'))\n    ans.append('3rdparty/base64/lib/tables/tables.c')\n    ans.append('3rdparty/base64/lib/codec_choose.c')\n    ans.append('3rdparty/base64/lib/lib.c')\n    return ans, headers\n\n\ndef compile_glfw(compilation_database: CompilationDatabase, build_dsym: bool = False) -> None:\n    modules = 'cocoa' if is_macos else 'x11 wayland'\n    for module in modules.split():\n        try:\n            genv = glfw.init_env(env, pkg_config, pkg_version, at_least_version, test_compile, module)\n        except SystemExit as err:\n            if module != 'wayland':\n                raise\n            print(err, file=sys.stderr)\n            print(error('Disabling building of wayland backend'), file=sys.stderr)\n            continue\n        sources = [os.path.join('glfw', x) for x in genv.sources]\n        all_headers = [os.path.join('glfw', x) for x in genv.all_headers]\n        if module == 'wayland':\n            try:\n                glfw.build_wayland_protocols(genv, parallel_run, emphasis, newer, 'glfw')\n            except SystemExit as err:\n                print(err, file=sys.stderr)\n                print(error('Disabling building of wayland backend'), file=sys.stderr)\n                continue\n        compile_c_extension(\n            genv, f'kitty/glfw-{module}', compilation_database,\n            sources, all_headers, desc_prefix=f'[{module}] ', build_dsym=build_dsym)\n\n\ndef kittens_env(args: Options) -> Env:\n    kenv = env.copy()\n    cflags = kenv.cflags\n    cflags.append('-pthread')\n    cflags.append('-Ikitty')\n    pylib = get_python_flags(args, cflags)\n    kenv.ldpaths += pylib\n    return kenv\n\n\ndef compile_kittens(args: Options) -> None:\n    kenv = kittens_env(args)\n\n    def list_files(q: str) -> List[str]:\n        return sorted(glob.glob(q))\n\n    def files(\n            kitten: str,\n            output: str,\n            extra_headers: Sequence[str] = (),\n            extra_sources: Sequence[str] = (),\n            filter_sources: Optional[Callable[[str], bool]] = None,\n            includes: Sequence[str] = (), libraries: Sequence[str] = (),\n    ) -> Tuple[str, List[str], List[str], str, Sequence[str], Sequence[str]]:\n        sources = list(filter(filter_sources, list(extra_sources) + list_files(os.path.join('kittens', kitten, '*.c'))))\n        headers = list_files(os.path.join('kittens', kitten, '*.h')) + list(extra_headers)\n        return kitten, sources, headers, f'kittens/{kitten}/{output}', includes, libraries\n\n    xxhash = xxhash_flags()\n    for kitten, sources, all_headers, dest, includes, libraries in (\n        files('transfer', 'rsync', libraries=xxhash[1], includes=xxhash[0]),\n    ):\n        final_env = kenv.copy()\n        final_env.cflags.extend(includes)\n        final_env.ldpaths[:0] = list(libraries)\n        compile_c_extension(\n            final_env, dest, args.compilation_database, sources, all_headers + ['kitty/data-types.h'], build_dsym=args.build_dsym)\n\n\ndef init_env_from_args(args: Options, native_optimizations: bool = False) -> None:\n    global env\n    env = init_env(\n        args.debug, args.sanitize, native_optimizations, args.link_time_optimization, args.profile,\n        args.egl_library, args.startup_notification_library, args.canberra_library, args.systemd_library, args.fontconfig_library,\n        args.extra_logging, args.extra_include_dirs, args.ignore_compiler_warnings,\n        args.building_arch, args.extra_library_dirs, verbose=args.verbose > 0, vcs_rev=args.vcs_rev,\n    )\n\n\n@lru_cache\ndef extract_rst_targets() -> Dict[str, Dict[str, str]]:\n    m = runpy.run_path('docs/extract-rst-targets.py')\n    return cast(Dict[str, Dict[str, str]], m['main']())\n\n\ndef update_if_changed(path: str, text: str) -> None:\n    q = ''\n    with suppress(FileNotFoundError), open(path) as f:\n        q = f.read()\n    if q != text:\n        with open(path, 'w') as f:\n            f.write(text)\n\n\ndef build_ref_map(skip_generation: bool = False) -> str:\n    dest = 'kitty/docs_ref_map_generated.h'\n    if not skip_generation:\n        d = extract_rst_targets()\n        h = 'static const char docs_ref_map[] = {\\n' + textwrap.fill(', '.join(map(str, bytearray(json.dumps(d, sort_keys=True).encode('utf-8'))))) + '\\n};\\n'\n        update_if_changed(dest, h)\n    return dest\n\n\ndef build_cli_parser_specs(skip_generation: bool = False) -> str:\n    dest = 'kitty/launcher/cli-parser-data_generated.h'\n    if not skip_generation:\n        m = runpy.run_path('kitty/simple_cli_definitions.py', {'appname': appname})\n        h = '\\n'.join(m['generate_c_parsers']())\n        update_if_changed(dest, h)\n    return dest\n\n\ndef build_uniforms_header(skip_generation: bool = False) -> str:\n    dest = 'kitty/uniforms_generated.h'\n    if skip_generation:\n        return dest\n    lines: list[str] = []\n    a = lines.append\n    uniform_names: Dict[str, Tuple[str, ...]] = {}\n    class_names = {}\n    function_names = {}\n\n    def find_uniform_names(raw: str) -> Iterator[str]:\n        for m in re.finditer(r'^uniform\\s+\\S+\\s+(.+?);', raw, flags=re.MULTILINE):\n            for x in m.group(1).split(','):\n                yield x.strip().partition('[')[0]\n\n    for x in sorted(glob.glob('kitty/*.glsl')):\n        name = os.path.basename(x).partition('.')[0]\n        name, sep, shader_type = name.rpartition('_')\n        if not sep or shader_type not in ('fragment', 'vertex'):\n            continue\n        class_names[name] = f'{name.capitalize()}Uniforms'\n        function_names[name] = f'get_uniform_locations_{name}'\n        with open(x) as f:\n            raw = f.read()\n        uniform_names[name] = uniform_names.setdefault(name, ()) + tuple(find_uniform_names(raw))\n    for name in sorted(class_names):\n        class_name, function_name, uniforms = class_names[name], function_names[name], uniform_names[name]\n        a(f'typedef struct {class_name} ''{')\n        for n in uniforms:\n            a(f'    int {n};')\n        a('}'f' {class_name};')\n        a('')\n        a(f'static inline void\\n{function_name}(int program, {class_name} *ans) ''{')\n        for n in uniforms:\n            a(f'    ans->{n} = get_uniform_location(program, \"{n}\");')\n        a('}')\n        a('')\n    src = '\\n'.join(lines)\n    try:\n        with open(dest) as f:\n            current = f.read()\n    except FileNotFoundError:\n        current = ''\n    if src != current:\n        with open(dest, 'w') as f:\n            f.write(src)\n    return dest\n\n\n@lru_cache\ndef wrapped_kittens() -> str:\n    with open('shell-integration/ssh/kitty') as f:\n        for line in f:\n            if line.startswith('    wrapped_kittens=\"'):\n                val = line.strip().partition('\"')[2][:-1]\n                return ' '.join(sorted(filter(None, val.split())))\n    raise Exception('Failed to read wrapped kittens from kitty wrapper script')\n\n\ndef build(args: Options, native_optimizations: bool = True, call_init: bool = True) -> None:\n    if call_init:\n        init_env_from_args(args, native_optimizations)\n\n    sources, headers = find_c_files()\n    headers.append(build_ref_map(args.skip_code_generation))\n    headers.append(build_cli_parser_specs(args.skip_code_generation))\n    headers.append(build_uniforms_header(args.skip_code_generation))\n    compile_c_extension(\n        kitty_env(args), 'kitty/fast_data_types', args.compilation_database, sources, headers,\n        build_dsym=args.build_dsym,\n    )\n    compile_glfw(args.compilation_database, args.build_dsym)\n    compile_kittens(args)\n    add_builtin_fonts(args)\n\n\ndef safe_makedirs(path: str) -> None:\n    os.makedirs(path, exist_ok=True)\n\n\ndef update_go_generated_files(args: Options, kitty_exe: str) -> None:\n    if args.skip_code_generation:\n        print('Skipping generation of Go files due to command line option', flush=True)\n        return\n    # update all the various auto-generated go files, if needed\n    if args.verbose:\n        print('Updating Go generated files...', flush=True)\n\n    env = os.environ.copy()\n    env['ASAN_OPTIONS'] = 'detect_leaks=0'\n    cp = subprocess.run([kitty_exe, '+launch', os.path.join(src_base, 'gen/go_code.py')], stdout=subprocess.DEVNULL, env=env)\n    if cp.returncode != 0:\n        if os.environ.get('CI') == 'true' and cp.returncode < 0 and shutil.which('coredumpctl'):\n            subprocess.run(['sh', '-c', 'echo bt | coredumpctl debug'])\n        raise SystemExit(f'Generating go code failed with exit code: {cp.returncode}')\n\n\ndef parse_go_version(x: str) -> Tuple[int, int, int]:\n    def safe_int(x: str) -> int:\n        with suppress(ValueError):\n            return int(x)\n        return int(re.split(r'[-a-zA-Z]', x)[0])\n    ans = list(map(safe_int, x.split('.')))\n    while len(ans) < 3:\n        ans.append(0)\n    return ans[0], ans[1], ans[2]\n\n\n@lru_cache(2)\ndef go_cmd() -> list[str]:\n    go = shutil.which('go')\n    if go:\n        return [go]\n    return []\n\n\ndef build_static_kittens(\n    args: Options, launcher_dir: str, destination_dir: str = '', for_freeze: bool = False,\n    for_platform: Optional[Tuple[str, str]] = None\n) -> str:\n    sys.stdout.flush()\n    sys.stderr.flush()\n    go = go_cmd()\n    if not go:\n        raise SystemExit('The go tool was not found on this system. Install Go')\n    required_go_version = subprocess.check_output(go + 'list -f {{.GoVersion}} -m'.split(), env=dict(os.environ, GO111MODULE=\"on\")).decode().strip()\n    go_version_raw = subprocess.check_output(go + ['version']).decode().strip().split()\n    if go_version_raw[2] != \"devel\":\n        current_go_version = go_version_raw[2][2:]\n    else:\n        current_go_version = go_version_raw[3][2:]\n    if parse_go_version(required_go_version) > parse_go_version(current_go_version):\n        raise SystemExit(f'The version of go on this system ({current_go_version}) is too old. go >= {required_go_version} is needed')\n    if not for_platform:\n        update_go_generated_files(args, os.path.join(launcher_dir, appname))\n    if args.skip_building_kitten:\n        print('Skipping building of the kitten binary because of a command line option. Build is incomplete', file=sys.stderr)\n        return ''\n    cmd = go + ['build', '-v']\n    vcs_rev = args.vcs_rev or get_vcs_rev()\n    ld_flags: List[str] = []\n    with open('go.mod') as f:\n        m = re.search(r'^module\\s+(\\S+)', f.read(), flags=re.M)\n        assert m is not None\n        modpath = m.group(1).strip()\n    binary_data_flags = [f\"-X {modpath}.VCSRevision={vcs_rev}\"]\n    if for_freeze:\n        binary_data_flags.append(f\"-X {modpath}.IsFrozenBuild=true\")\n    if for_platform:\n        binary_data_flags.append(f\"-X {modpath}.IsStandaloneBuild=true\")\n    if not args.debug:\n        ld_flags.append('-s')\n        ld_flags.append('-w')\n    cmd += ['-ldflags', ' '.join(binary_data_flags + ld_flags)]\n    dest = os.path.join(destination_dir or launcher_dir, 'kitten')\n    if for_platform:\n        dest += f'-{for_platform[0]}-{for_platform[1]}'\n    src = os.path.abspath('tools/cmd')\n\n    def run_one(dest: str) -> None:\n        c = cmd + ['-o', dest, src]\n        if args.verbose:\n            print(shlex.join(c))\n        e = os.environ.copy()\n        # https://github.com/kovidgoyal/kitty/issues/6051#issuecomment-1441369828\n        e.pop('PWD', None)\n        if for_platform:\n            e['CGO_ENABLED'] = '0'\n            e['GOOS'] = for_platform[0]\n            e['GOARCH'] = for_platform[1]\n        elif args.building_arch:\n            e['GOARCH'] = {'x86_64': 'amd64', 'arm64': 'arm64'}[args.building_arch]\n        cp = subprocess.run(c, env=e)\n        if cp.returncode != 0:\n            raise SystemExit(cp.returncode)\n\n    if is_macos and for_freeze and not for_platform:\n        adests = []\n        for arch in macos_universal_arches:\n            args.building_arch = arch\n            adest = dest + '-' + arch\n            adests.append(adest)\n            run_one(adest)\n        lipo({dest: adests})\n    else:\n        run_one(dest)\n    return dest\n\n\ndef build_static_binaries(args: Options, launcher_dir: str) -> None:\n    arches = 'amd64', 'arm64'\n    for os_, arches_ in {\n        'darwin': arches, 'linux': arches + ('arm', '386'), 'freebsd': arches, 'netbsd': arches, 'openbsd': arches,\n        'dragonfly': ('amd64',),\n    }.items():\n        for arch in arches_:\n            print('Cross compiling static kitten for:', os_, arch)\n            build_static_kittens(args, launcher_dir, args.dir_for_static_binaries, for_platform=(os_, arch))\n\n\ndef read_bool_options(path: str = 'kitty/cli.py') -> Tuple[str, ...]:\n    with open(os.path.join(src_base, path)) as f:\n        raw = f.read()\n    m = re.search(r\"^\\s*OPTIONS = r?'''(.+?)'''\", raw, flags=re.MULTILINE | re.DOTALL)\n    assert m is not None\n    ans: List[str] = []\n    in_option: List[str] = []\n    prev_line_was_blank = False\n    for line in m.group(1).splitlines():\n        if in_option:\n            is_blank = not line.strip()\n            if is_blank:\n                if prev_line_was_blank:\n                    in_option = []\n            prev_line_was_blank = is_blank\n            if line.startswith('type=bool-'):\n                ans.extend(x.lstrip('-') for x in in_option)\n        else:\n            if line.startswith('-'):\n                in_option = line.strip().split()\n    return tuple(ans)\n\n\ndef build_launcher(args: Options, launcher_dir: str = '.', bundle_type: str = 'source') -> str:\n    werror = '' if args.ignore_compiler_warnings else '-pedantic-errors -Werror'\n    cflags = f'-Wall {werror} -fpie {c_std}'.strip().split()\n    cppflags = [define(f'WRAPPED_KITTENS=\" {wrapped_kittens()} \"')]\n    ldflags = shlex.split(os.environ.get('LDFLAGS', ''))\n    xxhash = xxhash_flags()\n    cppflags.extend(xxhash[0])\n    libs: list[str] = xxhash[1]\n    if args.profile or args.sanitize:\n        cflags.append('-g3')\n        if args.sanitize:\n            sanitize_args = get_sanitize_args(env.cc, env.ccver)\n            cflags.extend(sanitize_args)\n            ldflags.extend(sanitize_args)\n            libs += ['-lasan'] if not is_macos and env.compiler_type is not CompilerType.clang else []\n        if args.profile:\n            libs.append('-lprofiler')\n    else:\n        cflags.append('-g3' if args.debug else '-O3')\n    if bundle_type.endswith('-freeze'):\n        cppflags.append('-DFOR_BUNDLE')\n        cppflags.append(f'-DPYVER=\"{sysconfig.get_python_version()}\"')\n        cppflags.append(f'-DKITTY_LIB_DIR_NAME=\"{args.libdir_name}\"')\n    elif bundle_type == 'source':\n        cppflags.append('-DFROM_SOURCE')\n    elif bundle_type == 'develop':\n        cppflags.append('-DFROM_SOURCE')\n        ph = os.path.relpath(os.environ[\"DEVELOP_ROOT\"], '.')\n        cppflags.append(f'-DSET_PYTHON_HOME=\"{ph}\"')\n        if not is_macos:\n            ldflags += ['-Wl,--disable-new-dtags', f'-Wl,-rpath,$ORIGIN/../../{ph}/lib']\n    if bundle_type.startswith('macos-'):\n        klp = '../Resources/kitty'\n    elif bundle_type.startswith('linux-'):\n        klp = '../{}/kitty'.format(args.libdir_name.strip('/'))\n    elif bundle_type == 'source':\n        klp = os.path.relpath('.', launcher_dir)\n    elif bundle_type == 'develop':\n        # make the kitty executable relocatable\n        klp = src_base\n    else:\n        raise SystemExit(f'Unknown bundle type: {bundle_type}')\n    cppflags.append(f'-DKITTY_LIB_PATH=\"{klp}\"')\n    pylib = get_python_flags(args, cflags, for_main_executable=True)\n    cppflags += shlex.split(os.environ.get('CPPFLAGS', ''))\n    cflags += shlex.split(os.environ.get('CFLAGS', ''))\n    for path in args.extra_include_dirs:\n        cflags.append(f'-I{path}')\n    if args.building_arch:\n        set_arches(cflags, args.building_arch)\n        set_arches(ldflags, args.building_arch)\n    if bundle_type == 'linux-freeze':\n        # --disable-new-dtags prevents -rpath from generating RUNPATH instead of\n        # RPATH entries in the launcher. The ld dynamic linker does not search\n        # RUNPATH locations for transitive dependencies, unlike RPATH.\n        ldflags += ['-Wl,--disable-new-dtags', '-Wl,-rpath,$ORIGIN/../lib']\n    os.makedirs(launcher_dir, exist_ok=True)\n    os.makedirs(build_dir, exist_ok=True)\n    objects = []\n    headers = glob.glob('kitty/launcher/*.h')\n    cppflags.append('-DKITTY_VERSION=\"' + '.'.join(map(str, version)) + '\"')\n    for src in ('kitty/launcher/main.c', 'kitty/launcher/single-instance.c', 'kitty/launcher/cmdline.c'):\n        obj = os.path.join(build_dir, src.replace('/', '-').replace('.c', '.o'))\n        objects.append(obj)\n        cmd = env.cc + cppflags + cflags + ['-c', src, '-o', obj]\n        key = CompileKey(src, os.path.basename(obj))\n        args.compilation_database.add_command(\n            f'Compiling {emphasis(src)} ...', cmd, partial(newer, obj, src, *dependecies_for(src, obj, headers)), key=key, keyfile=src)\n    dest = kitty_exe = os.path.join(launcher_dir, 'kitty')\n    link_targets.append(os.path.abspath(dest))\n    desc = f'Linking {emphasis(\"launcher\")} ...'\n    cmd = env.cc + ldflags + objects + libs + pylib + ['-o', dest]\n    args.compilation_database.add_command(desc, cmd, partial(newer, dest, *objects), key=LinkKey('kitty'))\n    if args.build_dsym and is_macos:\n        desc = f'Linking dSYM {emphasis(\"launcher\")} ...'\n        dsym = f'{dest}.dSYM/Contents/Resources/DWARF/{os.path.basename(dest)}'\n        args.compilation_database.add_command(desc, ['dsymutil', dest], partial(newer, dsym, dest), key=LinkKey(dsym), is_post_link=True)\n    args.compilation_database.build_all()\n    return kitty_exe\n\n\n# Packaging {{{\ndef copy_man_pages(ddir: str) -> None:\n    mandir = os.path.join(ddir, 'share', 'man')\n    safe_makedirs(mandir)\n    man_levels = '15'\n    with suppress(FileNotFoundError):\n        for x in man_levels:\n            shutil.rmtree(os.path.join(mandir, f'man{x}'))\n    src = 'docs/_build/man'\n    if not os.path.exists(src):\n        raise SystemExit('''\\\nThe kitty man pages are missing. If you are building from git then run:\nmake && make docs\n(needs the sphinx documentation system to be installed)\n''')\n    for x in man_levels:\n        os.makedirs(os.path.join(mandir, f'man{x}'))\n        for y in glob.glob(os.path.join(src, f'*.{x}')):\n            shutil.copy2(y, os.path.join(mandir, f'man{x}'))\n\n\ndef copy_html_docs(ddir: str) -> None:\n    htmldir = os.path.join(ddir, 'share', 'doc', appname, 'html')\n    safe_makedirs(os.path.dirname(htmldir))\n    with suppress(FileNotFoundError):\n        shutil.rmtree(htmldir)\n    src = 'docs/_build/html'\n    if not os.path.exists(src):\n        raise SystemExit('''\\\nThe kitty html docs are missing. If you are building from git then run:\nmake && make docs\n(needs the sphinx documentation system to be installed)\n''')\n    shutil.copytree(src, htmldir)\n\n\ndef compile_python(base_path: str) -> None:\n    import compileall\n    import py_compile\n    for root, dirs, files in os.walk(base_path):\n        for f in files:\n            if f.rpartition('.')[-1] in ('pyc', 'pyo'):\n                os.remove(os.path.join(root, f))\n\n    exclude = re.compile('.*/shell-integration/ssh/bootstrap.py')\n    compileall.compile_dir(\n        base_path, rx=exclude, force=True, optimize=(0, 1, 2), quiet=1, workers=0,  # type: ignore\n        invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH, ddir='')\n\n\ndef create_linux_bundle_gunk(ddir: str, args: Options) -> None:\n    libdir_name = args.libdir_name\n    base = Path(ddir)\n    in_src_launcher = base / (f'{libdir_name}/kitty/kitty/launcher/kitty')\n    launcher = base / 'bin/kitty'\n    skip_docs = False\n    if not os.path.exists('docs/_build/html'):\n        kitten_exe = os.path.join(os.path.dirname(str(launcher)), 'kitten')\n        if os.path.exists(kitten_exe):\n            os.environ['KITTEN_EXE_FOR_DOCS'] = kitten_exe\n            make = 'gmake' if is_freebsd else 'make'\n            run_tool([make, 'docs'])\n        else:\n            if args.skip_building_kitten:\n                skip_docs = True\n                print('WARNING: You have chosen to skip building kitten.'\n                      ' This means docs could not be generated and will not be included in the linux package.'\n                      ' You should build kitten and then re-run this build.', file=sys.stderr)\n            else:\n                raise SystemExit(f'kitten binary not found at: {kitten_exe}')\n    if not skip_docs:\n        copy_man_pages(ddir)\n        copy_html_docs(ddir)\n    for (icdir, ext) in {'256x256': 'png', 'scalable': 'svg'}.items():\n        icdir = os.path.join(ddir, 'share', 'icons', 'hicolor', icdir, 'apps')\n        safe_makedirs(icdir)\n        shutil.copy2(f'logo/kitty.{ext}', icdir)\n    deskdir = os.path.join(ddir, 'share', 'applications')\n    safe_makedirs(deskdir)\n    with open(os.path.join(deskdir, 'kitty.desktop'), 'w') as f:\n        f.write(\n            '''\\\n[Desktop Entry]\nVersion=1.0\nType=Application\nName=kitty\nGenericName=Terminal emulator\nComment=Fast, feature-rich, GPU based terminal\nTryExec=kitty\nStartupNotify=true\nExec=kitty\nIcon=kitty\nCategories=System;TerminalEmulator;\nX-TerminalArgExec=--\nX-TerminalArgTitle=--title\nX-TerminalArgAppId=--class\nX-TerminalArgDir=--working-directory\nX-TerminalArgHold=--hold\n''')\n    with open(os.path.join(deskdir, 'kitty-open.desktop'), 'w') as f:\n        f.write(\n            '''\\\n[Desktop Entry]\nVersion=1.0\nType=Application\nName=kitty URL Launcher\nGenericName=Terminal emulator\nComment=Open URLs with kitty\nStartupNotify=true\nTryExec=kitty\nExec=kitty +open %U\nIcon=kitty\nCategories=System;TerminalEmulator;\nNoDisplay=true\nMimeType=image/*;application/x-sh;application/x-shellscript;inode/directory;text/*;x-scheme-handler/kitty;x-scheme-handler/ssh;\n''')\n\n    if os.path.exists(in_src_launcher):\n        os.remove(in_src_launcher)\n    os.makedirs(os.path.dirname(in_src_launcher), exist_ok=True)\n    os.symlink(os.path.relpath(launcher, os.path.dirname(in_src_launcher)), in_src_launcher)\n\n\ndef macos_info_plist(for_quake: str = '') -> bytes:\n    import plistlib\n    VERSION = '.'.join(map(str, version))\n\n    def access(what: str, verb: str = 'would like to access') -> str:\n        return f'A program running inside kitty {verb} {what}'\n\n    docs = [] if for_quake else [\n        {\n            'CFBundleTypeName': 'Terminal scripts',\n            'CFBundleTypeExtensions': ['command', 'sh', 'zsh', 'bash', 'fish', 'tool'],\n            'CFBundleTypeIconFile': f'{appname}.icns',\n            'CFBundleTypeRole': 'Editor',\n        },\n        {\n            'CFBundleTypeName': 'Folders',\n            'LSItemContentTypes': ['public.directory'],\n            'CFBundleTypeRole': 'Editor',\n            'LSHandlerRank': 'Alternate',\n        },\n        {\n            'LSItemContentTypes': ['public.unix-executable'],\n            'CFBundleTypeRole': 'Shell',\n        },\n        {\n            'CFBundleTypeName': 'Text files',\n            'LSItemContentTypes': ['public.text'],\n            'CFBundleTypeRole': 'Editor',\n            'LSHandlerRank': 'Alternate',\n        },\n        {\n            'CFBundleTypeName': 'Image files',\n            'LSItemContentTypes': ['public.image'],\n            'CFBundleTypeRole': 'Viewer',\n            'LSHandlerRank': 'Alternate',\n        },\n        # Allows dragging arbitrary files to kitty Dock icon, and list kitty in the Open With context menu.\n        {\n            'CFBundleTypeName': 'All files',\n            'LSItemContentTypes': ['public.archive', 'public.content', 'public.data'],\n            'CFBundleTypeRole': 'Editor',\n            'LSHandlerRank': 'Alternate',\n        },\n    ]\n\n    url_schemes = [] if for_quake else [\n        {\n            'CFBundleURLName': 'File URL',\n            'CFBundleURLSchemes': ['file'],\n        },\n        {\n            'CFBundleURLName': 'FTP URL',\n            'CFBundleURLSchemes': ['ftp', 'ftps'],\n        },\n        {\n            'CFBundleURLName': 'Gemini URL',\n            'CFBundleURLSchemes': ['gemini'],\n        },\n        {\n            'CFBundleURLName': 'Git URL',\n            'CFBundleURLSchemes': ['git'],\n        },\n        {\n            'CFBundleURLName': 'Gopher URL',\n            'CFBundleURLSchemes': ['gopher'],\n        },\n        {\n            'CFBundleURLName': 'HTTP URL',\n            'CFBundleURLSchemes': ['http', 'https'],\n        },\n        {\n            'CFBundleURLName': 'IRC URL',\n            'CFBundleURLSchemes': ['irc', 'irc6', 'ircs'],\n        },\n        {\n            'CFBundleURLName': 'kitty URL',\n            'CFBundleURLSchemes': ['kitty'],\n            'LSHandlerRank': 'Owner',\n            'LSIsAppleDefaultForScheme': True,\n        },\n        {\n            'CFBundleURLName': 'Mail Address URL',\n            'CFBundleURLSchemes': ['mailto'],\n        },\n        {\n            'CFBundleURLName': 'News URL',\n            'CFBundleURLSchemes': ['news', 'nntp'],\n        },\n        {\n            'CFBundleURLName': 'SSH and SFTP URL',\n            'CFBundleURLSchemes': ['ssh', 'sftp'],\n        },\n        {\n            'CFBundleURLName': 'Telnet URL',\n            'CFBundleURLSchemes': ['telnet'],\n        },\n    ]\n\n    services = [\n        {\n            'NSMenuItem': {'default': for_quake},\n            'NSMessage': 'quickAccessTerminal',\n            'NSRequiredContext': {'NSServiceCategory': 'None'},\n        },\n    ] if for_quake else [\n        {\n            'NSMenuItem': {'default': f'New {appname} Tab Here'},\n            'NSMessage': 'openTab',\n            'NSRequiredContext': {'NSTextContent': 'FilePath'},\n            'NSSendTypes': ['NSFilenamesPboardType', 'public.plain-text'],\n        },\n        {\n            'NSMenuItem': {'default': f'New {appname} Window Here'},\n            'NSMessage': 'openOSWindow',\n            'NSRequiredContext': {'NSTextContent': 'FilePath'},\n            'NSSendTypes': ['NSFilenamesPboardType', 'public.plain-text'],\n        },\n        {\n            'NSMenuItem': {'default': f'Open with {appname}'},\n            'NSMessage': 'openFileURLs',\n            'NSRequiredContext': {'NSTextContent': 'FilePath'},\n            'NSSendTypes': ['NSFilenamesPboardType', 'public.plain-text'],\n        },\n    ]\n\n    pl = dict(\n        # Naming\n        CFBundleName=f'{appname}-quick-access' if for_quake else appname,\n        CFBundleDisplayName=f'{appname}-quick-access' if for_quake else appname,\n        # Identification\n        CFBundleIdentifier=f'net.kovidgoyal.{appname}' + ('-quick-access' if for_quake else ''),\n        # Bundle Version Info\n        CFBundleVersion=VERSION,\n        CFBundleShortVersionString=VERSION,\n        CFBundleInfoDictionaryVersion='6.0',\n        NSHumanReadableCopyright=time.strftime('Copyright %Y, Kovid Goyal'),\n        CFBundleGetInfoString='kitty - The fast, feature-rich, GPU based terminal emulator. https://sw.kovidgoyal.net/kitty/',\n        # Operating System Version\n        LSMinimumSystemVersion='12.0.0',\n        # Categorization\n        CFBundlePackageType='APPL',\n        CFBundleSignature='????',\n        LSApplicationCategoryType='public.app-category.utilities',\n        # App Execution\n        CFBundleExecutable=quake_name if for_quake else appname,\n        LSEnvironment={'KITTY_LAUNCHED_BY_LAUNCH_SERVICES': '1'},\n        LSRequiresNativeExecution=True,\n        NSSupportsSuddenTermination=False,\n        # Localization\n        # see https://github.com/kovidgoyal/kitty/issues/1233\n        CFBundleDevelopmentRegion='English',\n        CFBundleAllowMixedLocalizations=True,\n        TICapsLockLanguageSwitchCapable=True,\n        # User Interface and Graphics\n        CFBundleIconFile=f'{appname}.icns',\n        NSHighResolutionCapable=True,\n        NSSupportsAutomaticGraphicsSwitching=True,\n        # Needed for dark mode in Mojave when linking against older SDKs\n        NSRequiresAquaSystemAppearance='NO',\n        # Document and URL Types\n        CFBundleDocumentTypes=docs,\n        CFBundleURLTypes=url_schemes,\n        # Services\n        NSServices=services,\n        # Calendar and Reminders\n        NSCalendarsUsageDescription=access('your calendar data.'),\n        NSRemindersUsageDescription=access('your reminders.'),\n        # Camera and Microphone\n        NSCameraUsageDescription=access('the camera.'),\n        NSMicrophoneUsageDescription=access('the microphone.'),\n        # Contacts\n        NSContactsUsageDescription=access('your contacts.'),\n        # Location\n        NSLocationUsageDescription=access('your location information.'),\n        NSLocationTemporaryUsageDescriptionDictionary=access('your location temporarily.'),\n        # Motion\n        NSMotionUsageDescription=access('motion data.'),\n        # Networking\n        NSLocalNetworkUsageDescription=access('local network.'),\n        # Photos\n        NSPhotoLibraryUsageDescription=access('your photo library.'),\n        # Scripting\n        NSAppleScriptEnabled=False,\n        # Security\n        NSAppleEventsUsageDescription=access('AppleScript.'),\n        NSSystemAdministrationUsageDescription=access('elevated privileges.', 'requires'),\n        NSBluetoothAlwaysUsageDescription=access('Bluetooth.'),\n        # Speech\n        NSSpeechRecognitionUsageDescription=access('speech recognition.'),\n    )\n    if for_quake:\n        # exclude from dock and menubar\n        pl['LSBackgroundOnly'] = True\n    return plistlib.dumps(pl)\n\n\ndef create_macos_app_icon(where: str = 'Resources') -> None:\n    iconset_dir = os.path.abspath(os.path.join('logo', f'{appname}.iconset'))\n    icns_dir = os.path.join(where, f'{appname}.icns')\n    try:\n        subprocess.check_call([\n            'iconutil', '-c', 'icns', iconset_dir, '-o', icns_dir\n        ])\n    except FileNotFoundError:\n        print(f'{error(\"iconutil not found\")}, using png2icns (without retina support) to convert the logo', file=sys.stderr)\n        subprocess.check_call([\n            'png2icns', icns_dir\n        ] + [os.path.join(iconset_dir, logo) for logo in [\n            # png2icns does not support retina icons, so only pass the non-retina icons\n            'icon_16x16.png',\n            'icon_32x32.png',\n            'icon_128x128.png',\n            'icon_256x256.png',\n            'icon_512x512.png',\n        ]])\n\n\nquake_name = f'{appname}-quick-access'\n\n\ndef create_quick_access_bundle(kapp: str, quake_desc: str = 'Quick access to kitty') -> None:\n    qapp = os.path.join(kapp, 'Contents', f'{quake_name}.app')\n    base_exe_dir = os.path.join(kapp, 'Contents/MacOS')\n    if os.path.exists(qapp):\n        shutil.rmtree(qapp)\n    bin_dir = os.path.join(qapp, 'Contents/MacOS')\n    os.makedirs(bin_dir)\n    with open(os.path.join(qapp, 'Contents/Info.plist'), 'wb') as f:\n        f.write(macos_info_plist(quake_desc))\n    for exe in os.listdir(base_exe_dir):\n        os.symlink(f'../../../MacOS/{exe}', os.path.join(bin_dir, exe))\n    base_exe = os.path.join(base_exe_dir, 'kitty')\n    if os.path.exists(base_exe):  # during freeze launcher is built after bundle is created\n        shutil.copy2(base_exe, os.path.join(bin_dir, quake_name))\n    for x in ('Frameworks', 'Resources'):\n        os.symlink(f'../../{x}', os.path.join(qapp, 'Contents', x))\n\n\ndef create_minimal_macos_bundle(args: Options, launcher_dir: str, relocate: bool = False) -> None:\n    kapp = os.path.join(launcher_dir, 'kitty.app')\n    if os.path.exists(kapp):\n        shutil.rmtree(kapp)\n    bin_dir = os.path.join(kapp, 'Contents/MacOS')\n    resources_dir = os.path.join(kapp, 'Contents/Resources')\n    os.makedirs(resources_dir)\n    os.makedirs(bin_dir)\n    with open(os.path.join(kapp, 'Contents/Info.plist'), 'wb') as f:\n        f.write(macos_info_plist())\n    if relocate:\n        shutil.copy2(os.path.join(launcher_dir, \"kitty\"), bin_dir)\n        shutil.copy2(os.path.join(launcher_dir, \"kitten\"), bin_dir)\n    else:\n        build_launcher(args, bin_dir)\n        build_static_kittens(args, launcher_dir=bin_dir)\n        kitty_exe = os.path.join(launcher_dir, appname)\n        with suppress(FileNotFoundError):\n            os.remove(kitty_exe)\n        os.symlink(os.path.join(os.path.relpath(bin_dir, launcher_dir), appname), kitty_exe)\n    create_macos_app_icon(resources_dir)\n    create_quick_access_bundle(kapp, 'Quick access to kitty built from source')\n\n\ndef create_macos_bundle_gunk(dest: str, for_freeze: bool, args: Options) -> str:\n    ddir = Path(dest)\n    os.mkdir(ddir / 'Contents')\n    with open(ddir / 'Contents/Info.plist', 'wb') as fp:\n        fp.write(macos_info_plist())\n    copy_man_pages(str(ddir))\n    copy_html_docs(str(ddir))\n    os.rename(ddir / 'share', ddir / 'Contents/Resources')\n    os.rename(ddir / 'bin', ddir / 'Contents/MacOS')\n    os.rename(ddir / 'lib', ddir / 'Contents/Frameworks')\n    os.rename(ddir / 'Contents/Frameworks/kitty', ddir / 'Contents/Resources/kitty')\n    kitty_exe = ddir / 'Contents/MacOS/kitty'\n    in_src_launcher = ddir / 'Contents/Resources/kitty/kitty/launcher/kitty'\n    if os.path.exists(in_src_launcher):\n        os.remove(in_src_launcher)\n    os.makedirs(os.path.dirname(in_src_launcher), exist_ok=True)\n    os.symlink(os.path.relpath(kitty_exe, os.path.dirname(in_src_launcher)), in_src_launcher)\n    create_macos_app_icon(os.path.join(ddir, 'Contents', 'Resources'))\n    if not for_freeze:\n        kitten_exe = build_static_kittens(args, launcher_dir=os.path.dirname(kitty_exe))\n        if not kitten_exe:\n            raise SystemExit('kitten not built cannot create macOS bundle')\n        os.symlink(os.path.relpath(kitten_exe, os.path.dirname(in_src_launcher)),\n                   os.path.join(os.path.dirname(in_src_launcher), os.path.basename(kitten_exe)))\n    create_quick_access_bundle(dest)\n    return str(kitty_exe)\n\n\ndef package(args: Options, bundle_type: str, do_build_all: bool = True) -> None:\n    ddir = args.prefix\n    for_freeze = bundle_type.endswith('-freeze')\n    if bundle_type == 'linux-freeze':\n        args.libdir_name = 'lib'\n    libdir = os.path.join(ddir, args.libdir_name.strip('/'), 'kitty')\n    if os.path.exists(libdir):\n        shutil.rmtree(libdir)\n    launcher_dir = os.path.join(ddir, 'bin')\n    safe_makedirs(launcher_dir)\n    if for_freeze:  # freeze launcher is built separately\n        if do_build_all:\n            args.compilation_database.build_all()\n    else:\n        build_launcher(args, launcher_dir, bundle_type)\n    os.makedirs(os.path.join(libdir, 'logo'))\n    build_terminfo = runpy.run_path('build-terminfo', run_name='import_build')\n    for x in (libdir, os.path.join(ddir, 'share')):\n        odir = os.path.join(x, 'terminfo')\n        safe_makedirs(odir)\n        build_terminfo['compile_terminfo'](odir)\n    shutil.copy2('terminfo/kitty.terminfo', os.path.join(libdir, 'terminfo'))\n    shutil.copy2('terminfo/kitty.termcap', os.path.join(libdir, 'terminfo'))\n    shutil.copy2('__main__.py', libdir)\n    shutil.copy2('logo/kitty-128.png', os.path.join(libdir, 'logo'))\n    shutil.copy2('logo/kitty.png', os.path.join(libdir, 'logo'))\n    shutil.copy2('logo/beam-cursor.png', os.path.join(libdir, 'logo'))\n    shutil.copy2('logo/beam-cursor@2x.png', os.path.join(libdir, 'logo'))\n    shutil.copytree('shell-integration', os.path.join(libdir, 'shell-integration'), dirs_exist_ok=True)\n    shutil.copytree('fonts', os.path.join(libdir, 'fonts'), dirs_exist_ok=True)\n    allowed_extensions = frozenset('py glsl so'.split())\n\n    def src_ignore(parent: str, entries: Iterable[str]) -> List[str]:\n        return [\n            x for x in entries\n            if '.' in x and x.rpartition('.')[2] not in\n            allowed_extensions\n        ]\n\n    shutil.copytree('kitty', os.path.join(libdir, 'kitty'), ignore=src_ignore)\n    shutil.copytree('kittens', os.path.join(libdir, 'kittens'), ignore=src_ignore)\n    if for_freeze:\n        shutil.copytree('kitty_tests', os.path.join(libdir, 'kitty_tests'))\n\n    def repl(name: str, raw: str, defval: Union[str, float, FrozenSet[str]], val: Union[str, float, FrozenSet[str]]) -> str:\n        if defval == val:\n            return raw\n        tname = type(defval).__name__\n        if tname == 'frozenset':\n            tname = 'frozenset[str]'\n        prefix = f'{name}: {tname} ='\n        nraw = raw.replace(f'{prefix} {defval!r}', f'{prefix} {val!r}', 1)\n        if nraw == raw:\n            raise SystemExit(f'Failed to change the value of {name}')\n        return nraw\n\n    with open(os.path.join(libdir, 'kitty/options/types.py'), 'r+', encoding='utf-8') as f:\n        oraw = raw = f.read()\n        raw = repl('update_check_interval', raw, Options.update_check_interval, args.update_check_interval)\n        raw = repl('shell_integration', raw, frozenset(Options.shell_integration.split()), frozenset(args.shell_integration.split()))\n        if raw != oraw:\n            f.seek(0), f.truncate(), f.write(raw)\n\n    compile_python(libdir)\n\n    def should_be_executable(path: str) -> bool:\n        if path.endswith('.so'):\n            return True\n        q = path.split(os.sep)[-2:]\n        if len(q) == 2 and q[0] == 'ssh' and q[1] in ('kitty', 'kitten'):\n            return True\n        return False\n\n    for root, dirs, files in os.walk(libdir):\n        for f_ in files:\n            path = os.path.join(root, f_)\n            os.chmod(path, 0o755 if should_be_executable(path) else 0o644)\n    if not for_freeze and not bundle_type.startswith('macos-'):\n        build_static_kittens(args, launcher_dir=launcher_dir)\n    if not is_macos and not is_windows:\n        create_linux_bundle_gunk(ddir, args)\n\n    if bundle_type.startswith('macos-'):\n        create_macos_bundle_gunk(ddir, for_freeze, args)\n# }}}\n\n\ndef clean_launcher_dir(launcher_dir: str) -> None:\n    for x in glob.glob(os.path.join(launcher_dir, 'kitt*')):\n        if os.path.isdir(x):\n            shutil.rmtree(x)\n        else:\n            os.remove(x)\n\n\ndef clean(for_cross_compile: bool = False) -> None:\n\n    def safe_remove(*entries: str) -> None:\n        for x in entries:\n            if os.path.exists(x):\n                if os.path.isdir(x):\n                    shutil.rmtree(x)\n                else:\n                    os.unlink(x)\n\n    safe_remove(\n        'build', 'compile_commands.json', 'link_commands.json',\n        'linux-package', 'kitty.app', 'asan-launcher',\n        'kitty-profile')  # no fonts as that is not generated by build\n    if not for_cross_compile:\n        safe_remove('docs/generated')\n    clean_launcher_dir('kitty/launcher')\n\n    def excluded(root: str, d: str) -> bool:\n        q = os.path.relpath(os.path.join(root, d), src_base).replace(os.sep, '/')\n        return q in ('.git', 'bypy/b', 'dependencies')\n\n    def is_generated(f: str) -> bool:\n        e = f.endswith\n        return (\n            e('_generated.h') or e('_generated.go') or e('_generated.bin') or\n            e('_generated.s') or e('_generated_test.s') or e('_generated_test.go')\n        )\n\n    for root, dirs, files in os.walk(src_base, topdown=True):\n        dirs[:] = [d for d in dirs if not excluded(root, d)]\n        remove_dirs = {d for d in dirs if d == '__pycache__' or d.endswith('.dSYM')}\n        for d in remove_dirs:\n            shutil.rmtree(os.path.join(root, d))\n            dirs.remove(d)\n        for f in files:\n            ext = f.rpartition('.')[-1]\n            if ext in ('so', 'pyc', 'pyo', 'pyd', 'dylib') or (not for_cross_compile and is_generated(f)):\n                os.unlink(os.path.join(root, f))\n    for x in glob.glob('glfw/wayland-*-protocol.[ch]'):\n        os.unlink(x)\n    for x in glob.glob('kittens/*'):\n        if os.path.isdir(x) and not os.path.exists(os.path.join(x, '__init__.py')):\n            shutil.rmtree(x)\n    if go := go_cmd():\n        subprocess.check_call(go + ['clean', '-cache', '-testcache', '-modcache', '-fuzzcache'])\n\n\ndef option_parser() -> argparse.ArgumentParser:  # {{{\n    p = argparse.ArgumentParser()\n    p.add_argument(\n        'action',\n        nargs='?',\n        default=Options.action,\n        choices=('build',\n                 'test',\n                 'develop',\n                 'linux-package',\n                 'kitty.app',\n                 'linux-freeze',\n                 'macos-freeze',\n                 'build-launcher',\n                 'build-frozen-launcher',\n                 'build-frozen-tools',\n                 'clean',\n                 'export-ci-bundles',\n                 'build-dep',\n                 'build-static-binaries',\n                 ),\n        help='Action to perform (default is build)'\n    )\n    p.add_argument(\n        '--debug',\n        default=Options.debug,\n        action='store_true',\n        help='Build extension modules with debugging symbols'\n    )\n    p.add_argument(\n        '-v', '--verbose',\n        default=Options.verbose,\n        action='count',\n        help='Be verbose'\n    )\n    p.add_argument(\n        '--sanitize',\n        default=Options.sanitize,\n        action='store_true',\n        help='Turn on sanitization to detect memory access errors and undefined behavior. This is a big performance hit.'\n    )\n    p.add_argument(\n        '--prefix',\n        default=Options.prefix,\n        help='Where to create the linux package'\n    )\n    p.add_argument(\n        '--dir-for-static-binaries',\n        default=Options.dir_for_static_binaries,\n        help='Where to create the static kitten binary'\n    )\n    p.add_argument(\n        '--skip-code-generation',\n        default=Options.skip_code_generation,\n        action='store_true',\n        help='Do not create the *_generated.* source files. This is useful if they'\n        ' have already been generated by a previous build, for example during a two-stage cross compilation.'\n    )\n    p.add_argument(\n        '--skip-building-kitten',\n        default=Options.skip_building_kitten,\n        action='store_true',\n        help='Do not build the kitten binary. Useful if you want to build it separately.'\n    )\n    p.add_argument(\n        '--clean-for-cross-compile',\n        default=Options.clean_for_cross_compile,\n        action='store_true',\n        help='Do not clean generated Go source files. Useful for cross-compilation.'\n    )\n    p.add_argument(\n        '--python-compiler-flags', default=Options.python_compiler_flags,\n        help='Compiler flags for compiling against Python. Typically include directives. If not set'\n        ' the Python used to run setup.py is queried for these.'\n    )\n    p.add_argument(\n        '--python-linker-flags', default=Options.python_linker_flags,\n        help='Linker flags for linking against Python. Typically dynamic library names and search paths directives. If not set'\n        ' the Python used to run setup.py is queried for these.'\n    )\n    p.add_argument(\n        '--full',\n        dest='incremental',\n        default=Options.incremental,\n        action='store_false',\n        help='Do a full build, even for unchanged files'\n    )\n    p.add_argument(\n        '--profile',\n        default=Options.profile,\n        action='store_true',\n        help='Use the -pg compile flag to add profiling information'\n    )\n    p.add_argument(\n        '--libdir-name',\n        default=Options.libdir_name,\n        help='The name of the directory inside --prefix in which to store compiled files. Defaults to \"lib\"'\n    )\n    p.add_argument(\n        '--vcs-rev', default='',\n        help='The VCS revision to embed in the binary. The default is to read it from the .git directory when present.'\n    )\n    p.add_argument(\n        '--extra-logging',\n        action='append',\n        default=Options.extra_logging,\n        choices=('event-loop',),\n        help='Turn on extra logging for debugging in this build. Can be specified multiple times, to turn'\n        ' on different types of logging.'\n    )\n    p.add_argument(\n        '--extra-include-dirs', '-I',\n        action='append',\n        default=Options.extra_include_dirs,\n        help='Extra include directories to use while compiling'\n    )\n    p.add_argument(\n        '--extra-library-dirs', '-L',\n        action='append',\n        default=Options.extra_library_dirs,\n        help='Extra library directories to use while linking'\n    )\n    p.add_argument(\n        '--update-check-interval',\n        type=float,\n        default=Options.update_check_interval,\n        help='When building a package, the default value for the update_check_interval setting will'\n        ' be set to this number. Use zero to disable update checking.'\n    )\n    p.add_argument(\n        '--shell-integration',\n        type=str,\n        default=Options.shell_integration,\n        help='When building a package, the default value for the shell_integration setting will'\n        ' be set to this. Use \"enabled no-rc\" if you intend to install the shell integration scripts system wide.'\n    )\n    p.add_argument(\n        '--egl-library',\n        type=str,\n        default=Options.egl_library,\n        help='The filename argument passed to dlopen for libEGL.'\n        ' This can be used to change the name of the loaded library or specify an absolute path.'\n    )\n    p.add_argument(\n        '--startup-notification-library',\n        type=str,\n        default=Options.startup_notification_library,\n        help='The filename argument passed to dlopen for libstartup-notification-1.'\n        ' This can be used to change the name of the loaded library or specify an absolute path.'\n    )\n    p.add_argument(\n        '--canberra-library',\n        type=str,\n        default=Options.canberra_library,\n        help='The filename argument passed to dlopen for libcanberra.'\n        ' This can be used to change the name of the loaded library or specify an absolute path.'\n    )\n    p.add_argument(\n        '--systemd-library',\n        type=str,\n        default=Options.systemd_library,\n        help='The filename argument passed to dlopen for libsystemd.'\n        ' This can be used to change the name of the loaded library or specify an absolute path.'\n    )\n    p.add_argument(\n        '--fontconfig-library',\n        type=str,\n        default=Options.fontconfig_library,\n        help='The filename argument passed to dlopen for libfontconfig.'\n        ' This can be used to change the name of the loaded library or specify an absolute path.'\n    )\n    p.add_argument(\n        '--disable-link-time-optimization',\n        dest='link_time_optimization',\n        default=Options.link_time_optimization,\n        action='store_false',\n        help='Turn off Link Time Optimization (LTO).'\n    )\n    p.add_argument(\n        '--ignore-compiler-warnings',\n        default=Options.ignore_compiler_warnings, action='store_true',\n        help='Ignore any warnings from the compiler while building'\n    )\n    p.add_argument(\n        '--build-dSYM', dest='build_dsym',\n        default=Options.build_dsym, action='store_true',\n        help='Build the dSYM bundle on macOS, ignored on other platforms'\n    )\n    return p\n# }}}\n\n\ndef build_dep() -> None:\n    class Options:\n        platform: str = 'all'\n        deps: List[str] = []\n\n    p = argparse.ArgumentParser(prog=f'{sys.argv[0]} build-dep', description='Build dependencies for the kitty binary packages')\n    p.add_argument(\n        '--platform',\n        default=Options.platform,\n        choices='all macos linux linux-arm64 linux-64'.split(),\n        help='Platforms to build the dep for'\n    )\n    p.add_argument(\n        'deps',\n        nargs='*',\n        default=Options.deps,\n        help='Names of the dependencies, if none provided, build all'\n    )\n    args = p.parse_args(sys.argv[2:], namespace=Options())\n    linux_platforms = [\n        ['linux', '--arch=64'],\n        ['linux', '--arch=arm64'],\n    ]\n    if args.platform == 'all':\n        platforms = linux_platforms + [['macos']]\n    elif args.platform == 'linux':\n        platforms = linux_platforms\n    elif args.platform == 'macos':\n        platforms = [['macos']]\n    elif '-' in args.platform:\n        parts = args.platform.split('-')\n        platforms = [[parts[0], f'--arch={parts[1]}']]\n    else:\n        raise SystemExit(f'Unknown platform: {args.platform}')\n    base = [sys.executable, '../bypy']\n    for pf in platforms:\n        cmd = base + pf + ['dependencies'] + args.deps\n        run_tool(cmd)\n\n\ndef lipo(target_map: Dict[str, List[str]]) -> None:\n    print(f'Using lipo to generate {len(target_map)} universal binaries...')\n    for dest, inputs in target_map.items():\n        cmd = ['lipo', '-create', '-output', dest] + inputs\n        subprocess.check_call(cmd)\n        for x in inputs:\n            os.remove(x)\n\n\ndef macos_freeze(args: Options, launcher_dir: str, only_frozen_launcher: bool = False) -> None:\n    global build_dir\n    # Need to build a universal binary in two stages\n    orig_build_dir = build_dir\n    link_target_map: Dict[str, List[str]] = {}\n    bundle_type = 'macos-freeze'\n    for arch in macos_universal_arches:\n        args.building_arch = arch\n        build_dir = os.path.join(orig_build_dir, arch)\n        os.makedirs(build_dir, exist_ok=True)\n        print('Building for arch:', arch, 'in', build_dir)\n        if arch is not macos_universal_arches[0]:\n            args.skip_code_generation = True  # cant run kitty as its not a native arch\n        link_targets.clear()\n        with CompilationDatabase() as cdb:\n            args.compilation_database = cdb\n            init_env_from_args(args, native_optimizations=False)\n            if only_frozen_launcher:\n                kitty_exe_path = build_launcher(args, launcher_dir=launcher_dir, bundle_type=bundle_type)\n            else:\n                build_launcher(args, launcher_dir=launcher_dir)\n                build(args, native_optimizations=False, call_init=False)\n            cdb.build_all()\n        for x in link_targets:\n            arch_specific = x + '-' + arch\n            link_target_map.setdefault(x, []).append(arch_specific)\n            os.rename(x, arch_specific)\n    build_dir = orig_build_dir\n    lipo(link_target_map)\n    if only_frozen_launcher:\n        if is_macos:\n            shutil.copy2(kitty_exe_path, os.path.dirname(kitty_exe_path) + f'/../Contents/{quake_name}.app/Contents/MacOS/{quake_name}')\n    else:\n        package(args, bundle_type=bundle_type, do_build_all=False)\n\n\ndef do_build(args: Options) -> None:\n    launcher_dir = 'kitty/launcher'\n\n    if args.action == 'test':\n        texe = os.path.abspath(os.path.join(launcher_dir, 'kitty'))\n        os.execl(texe, texe, '+launch', 'test.py')\n    if args.action == 'clean':\n        clean(for_cross_compile=args.clean_for_cross_compile)\n        return\n    if args.action == 'macos-freeze':\n        return macos_freeze(args, launcher_dir)\n    if args.action == 'build-frozen-launcher' and is_macos:\n        launcher_dir=os.path.join(args.prefix, 'bin')\n        return macos_freeze(args, launcher_dir, only_frozen_launcher=True)\n\n    with CompilationDatabase(args.incremental) as cdb:\n        args.compilation_database = cdb\n        if args.action == 'build':\n            build(args)\n            if is_macos:\n                create_minimal_macos_bundle(args, launcher_dir)\n            else:\n                build_launcher(args, launcher_dir=launcher_dir)\n                build_static_kittens(args, launcher_dir=launcher_dir)\n        elif args.action == 'develop':\n            build(args)\n            build_launcher(args, launcher_dir=launcher_dir, bundle_type='develop')\n            build_static_kittens(args, launcher_dir=launcher_dir)\n            if is_macos:\n                create_minimal_macos_bundle(args, launcher_dir, relocate=True)\n        elif args.action == 'build-launcher':\n            init_env_from_args(args, False)\n            build_launcher(args, launcher_dir=launcher_dir)\n            build_static_kittens(args, launcher_dir=launcher_dir)\n        elif args.action == 'build-frozen-launcher':\n            init_env_from_args(args, False)\n            bundle_type = ('macos' if is_macos else 'linux') + '-freeze'\n            build_launcher(args, launcher_dir=os.path.join(args.prefix, 'bin'), bundle_type=bundle_type)\n        elif args.action == 'build-frozen-tools':\n            build_static_kittens(args, launcher_dir=args.prefix, for_freeze=True)\n        elif args.action == 'linux-package':\n            build(args, native_optimizations=False)\n            package(args, bundle_type='linux-package')\n        elif args.action == 'linux-freeze':\n            build(args, native_optimizations=False)\n            package(args, bundle_type='linux-freeze')\n        elif args.action == 'kitty.app':\n            args.prefix = 'kitty.app'\n            if os.path.exists(args.prefix):\n                shutil.rmtree(args.prefix)\n            build(args)\n            package(args, bundle_type='macos-package')\n            print('kitty.app successfully built!')\n        elif args.action == 'export-ci-bundles':\n            cmd = [sys.executable, '../bypy', 'export', 'download.calibre-ebook.com:/srv/download/ci/kitty']\n            subprocess.check_call(cmd + ['linux'])\n            subprocess.check_call(cmd + ['macos'])\n        elif args.action == 'build-static-binaries':\n            build_static_binaries(args, launcher_dir)\n\n\ndef main() -> None:\n    global verbose, build_dir\n    if len(sys.argv) > 1 and sys.argv[1] == 'build-dep':\n        return build_dep()\n    args = option_parser().parse_args(namespace=Options())\n    verbose = args.verbose > 0\n    args.prefix = os.path.abspath(args.prefix)\n    os.chdir(src_base)\n    os.makedirs(build_dir, exist_ok=True)\n    do_build(args)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "shell-integration/bash/kitty.bash",
    "content": "#!/bin/bash\n\nif [[ \"$-\" != *i* ]] ; then builtin return; fi  # check in interactive mode\nif [[ -z \"$KITTY_SHELL_INTEGRATION\" ]]; then builtin return; fi\n\n# Load the normal bash startup files\nif [[ -n \"$KITTY_BASH_INJECT\" ]]; then\n    builtin declare kitty_bash_inject=\"$KITTY_BASH_INJECT\"\n    builtin declare ksi_val=\"$KITTY_SHELL_INTEGRATION\"\n    builtin unset KITTY_SHELL_INTEGRATION  # ensure manual sourcing of this file in bashrc does not have any effect\n    builtin unset KITTY_BASH_INJECT ENV\n    if [[ -z \"$HOME\" ]]; then HOME=~; fi\n    if [[ -z \"$KITTY_BASH_ETC_LOCATION\" ]]; then KITTY_BASH_ETC_LOCATION=\"/etc\"; fi\n\n    _ksi_sourceable() {\n        [[ -f \"$1\" && -r \"$1\" ]] && builtin return 0; builtin return 1;\n    }\n\n    if [[ -n \"$ksi_val\" && \"$ksi_val\" != *no-sudo* && -n \"$TERMINFO\" && ! ( -r \"/usr/share/terminfo/x/xterm-kitty\" || -r \"/usr/share/terminfo/78/xterm-kitty\" ) ]]; then\n        # this must be done before sourcing user bashrc otherwise aliasing of sudo does not work\n        sudo() {\n            # Ensure terminfo is available in sudo\n            builtin local is_sudoedit=\"n\"\n            for arg; do\n                if [[ \"$arg\" == \"-e\" || $arg == \"--edit\" ]]; then\n                    is_sudoedit=\"y\"\n                    builtin break;\n                fi\n                [[ \"$arg\" != -* && \"$arg\" != *=* ]] && builtin break  # command found\n            done\n            if [[ \"$is_sudoedit\" == \"y\" ]]; then\n                builtin command sudo \"$@\";\n            else\n                builtin command sudo TERMINFO=\"$TERMINFO\" \"$@\";\n            fi\n        }\n    fi\n\n    if [[ \"$kitty_bash_inject\" == *\"posix\"* ]]; then\n        _ksi_sourceable \"$KITTY_BASH_POSIX_ENV\" && {\n            builtin source \"$KITTY_BASH_POSIX_ENV\"\n            builtin export ENV=\"$KITTY_BASH_POSIX_ENV\"\n        }\n    else\n        builtin set +o posix\n        builtin shopt -u inherit_errexit 2>/dev/null  # resetting posix does not clear this\n        if [[ -n \"$KITTY_BASH_UNEXPORT_HISTFILE\" ]]; then\n            builtin export -n HISTFILE\n            builtin unset KITTY_BASH_UNEXPORT_HISTFILE\n        fi\n\n        # See run_startup_files() in shell.c in the Bash source code\n        if builtin shopt -q login_shell; then\n            if [[ \"$kitty_bash_inject\" != *\"no-profile\"* ]]; then\n                _ksi_sourceable \"$KITTY_BASH_ETC_LOCATION/profile\" && builtin source \"$KITTY_BASH_ETC_LOCATION/profile\"\n                for _ksi_i in \"$HOME/.bash_profile\" \"$HOME/.bash_login\" \"$HOME/.profile\"; do\n                    _ksi_sourceable \"$_ksi_i\" && { builtin source \"$_ksi_i\"; break; }\n                done\n            fi\n        else\n            if [[ \"$kitty_bash_inject\" != *\"no-rc\"* ]]; then\n                # Linux distros build bash with -DSYS_BASHRC. Unfortunately, there is\n                # no way to probe bash for it and different distros use different files\n                # Arch, Debian, Ubuntu use /etc/bash.bashrc\n                # Fedora uses /etc/bashrc sourced from ~/.bashrc instead of SYS_BASHRC\n                # Void Linux uses /etc/bash/bashrc\n                for _ksi_i in \"$KITTY_BASH_ETC_LOCATION/bash.bashrc\" \"$KITTY_BASH_ETC_LOCATION/bash/bashrc\" ; do\n                    _ksi_sourceable \"$_ksi_i\" && { builtin source \"$_ksi_i\"; break; }\n                done\n                if [[ -z \"$KITTY_BASH_RCFILE\" ]]; then KITTY_BASH_RCFILE=\"$HOME/.bashrc\"; fi\n                _ksi_sourceable \"$KITTY_BASH_RCFILE\" && builtin source \"$KITTY_BASH_RCFILE\"\n            fi\n        fi\n    fi\n    builtin unset KITTY_BASH_RCFILE KITTY_BASH_POSIX_ENV KITTY_BASH_ETC_LOCATION\n    builtin unset -f _ksi_sourceable\n    builtin export KITTY_SHELL_INTEGRATION=\"$ksi_val\"\n    builtin unset _ksi_i ksi_val kitty_bash_inject\nfi\n\n\nif [ \"${BASH_VERSINFO:-0}\" -lt 4 ]; then\n    builtin unset KITTY_SHELL_INTEGRATION\n    builtin printf \"%s\\n\" \"Bash version ${BASH_VERSION} too old, kitty shell integration disabled\" > /dev/stderr\n    builtin return\nfi\n\nif [ -v \"_ksi_prompt[sourced]\" ]; then\n    # we have already run\n    builtin unset KITTY_SHELL_INTEGRATION\n    builtin return\nfi\n\n# this is defined outside _ksi_main to make it global without using declare -g\n# which is not available on older bash\nbuiltin declare -A _ksi_prompt\n_ksi_prompt=(\n    [cursor]='y' [title]='y' [mark]='y' [complete]='y' [cwd]='y' [sudo]='y' [ps0]='' [ps0_suffix]='' [ps1]='' [ps1_suffix]='' [ps2]=''\n    [hostname_prefix]='' [sourced]='y' [last_reported_cwd]=''\n)\n\n_ksi_main() {\n    builtin local ifs=\"$IFS\" i\n    IFS=\" \"\n    for i in ${KITTY_SHELL_INTEGRATION[@]}; do\n        case \"$i\" in\n            \"no-cursor\") _ksi_prompt[cursor]='n';;\n            \"no-title\") _ksi_prompt[title]='n';;\n            \"no-prompt-mark\") _ksi_prompt[mark]='n';;\n            \"no-complete\") _ksi_prompt[complete]='n';;\n            \"no-cwd\") _ksi_prompt[cwd]='n';;\n            \"no-sudo\") _ksi_prompt[sudo]='n';;\n        esac\n    done\n    IFS=\"$ifs\"\n\n    builtin unset KITTY_SHELL_INTEGRATION\n    if [[ -n \"$SSH_KITTEN_KITTY_DIR\" ]]; then\n        if [[ ! \"$PATH\" =~ (^|:)${SSH_KITTEN_KITTY_DIR}(:|$) ]] && [[ -z \"$(builtin command -v kitten)\" ]]; then\n            builtin export PATH=\"${PATH}:${SSH_KITTEN_KITTY_DIR}\"\n        fi\n        builtin unset SSH_KITTEN_KITTY_DIR\n    fi\n    builtin local krcs=\"$KITTY_SI_RUN_COMMAND_AT_STARTUP\"\n    builtin unset KITTY_SI_RUN_COMMAND_AT_STARTUP\n\n    _ksi_debug_print() {\n        # print a line to STDERR of parent kitty process\n        builtin local b\n        b=$(builtin command base64 <<< \"${@}\")\n        builtin printf \"\\eP@kitty-print|%s\\e\\\\\" \"${b//[[:space:]]}}\"\n    }\n\n    _ksi_set_mark() {\n        _ksi_prompt[\"${1}_mark\"]=\"\\[\\e]133;k;${1}_kitty\\a\\]\"\n    }\n\n    _ksi_set_mark start\n    _ksi_set_mark end\n    _ksi_set_mark start_secondary\n    _ksi_set_mark end_secondary\n    _ksi_set_mark start_suffix\n    _ksi_set_mark end_suffix\n    builtin unset -f _ksi_set_mark\n    _ksi_prompt[secondary_prompt]=\"\\n${_ksi_prompt[start_secondary_mark]}\\[\\e]133;A;k=s\\a\\]${_ksi_prompt[end_secondary_mark]}\"\n\n    _ksi_prompt_command() {\n        # we first remove any previously added kitty code from the prompt variables and then add\n        # it back, to ensure we have only a single instance\n        if [[ -n \"${_ksi_prompt[ps0]}\" ]]; then\n            PS0=${PS0//\\\\\\[\\\\e\\]133;k;start_kitty\\\\a\\\\\\]*end_kitty\\\\a\\\\\\]}\n            PS0=\"${_ksi_prompt[ps0]}$PS0\"\n        fi\n        if [[ -n \"${_ksi_prompt[ps0_suffix]}\" ]]; then\n            PS0=${PS0//\\\\\\[\\\\e\\]133;k;start_suffix_kitty\\\\a\\\\\\]*end_suffix_kitty\\\\a\\\\\\]}\n            PS0=\"${PS0}${_ksi_prompt[ps0_suffix]}\"\n        fi\n        # restore PS1 to its pristine state without our additions\n        if [[ -n \"${_ksi_prompt[ps1]}\" ]]; then\n            PS1=${PS1//\\\\\\[\\\\e\\]133;k;start_kitty\\\\a\\\\\\]*end_kitty\\\\a\\\\\\]}\n            PS1=${PS1//\\\\\\[\\\\e\\]133;k;start_secondary_kitty\\\\a\\\\\\]*end_secondary_kitty\\\\a\\\\\\]}\n        fi\n        if [[ -n \"${_ksi_prompt[ps1_suffix]}\" ]]; then\n            PS1=${PS1//\\\\\\[\\\\e\\]133;k;start_suffix_kitty\\\\a\\\\\\]*end_suffix_kitty\\\\a\\\\\\]}\n        fi\n        if [[ -n \"${_ksi_prompt[ps1]}\" ]]; then\n            if [[ \"${_ksi_prompt[mark]}\" == \"y\" && ( \"${PS1}\" == *\"\\n\"* || \"${PS1}\" == *$'\\n'* ) ]]; then\n                builtin local oldval\n                oldval=$(builtin shopt -p extglob)\n                builtin shopt -s extglob\n                # bash does not redraw the leading lines in a multiline prompt so\n                # mark the last line as a secondary prompt. Otherwise on resize the\n                # lines before the last line will be erased by kitty.\n                # the first part removes everything from the last \\n onwards\n                # the second part appends a newline with the secondary marking\n                # the third part appends everything after the last newline\n                PS1=${PS1%@('\\n'|$'\\n')*}${_ksi_prompt[secondary_prompt]}${PS1##*@('\\n'|$'\\n')}\n                builtin eval \"$oldval\"\n            fi\n            PS1=\"${_ksi_prompt[ps1]}$PS1\"\n        fi\n        if [[ -n \"${_ksi_prompt[ps1_suffix]}\" ]]; then\n            PS1=\"${PS1}${_ksi_prompt[ps1_suffix]}\"\n        fi\n        if [[ -n \"${_ksi_prompt[ps2]}\" ]]; then\n            PS2=${PS2//\\\\\\[\\\\e\\]133;k;start_kitty\\\\a\\\\\\]*end_kitty\\\\a\\\\\\]}\n            PS2=\"${_ksi_prompt[ps2]}$PS2\"\n        fi\n\n        if [[ \"${_ksi_prompt[cwd]}\" == \"y\" ]]; then\n            # unfortunately bash provides no hooks to detect cwd changes\n            # in particular this means cwd reporting will not happen for a\n            # command like cd /test && cat. PS0 is evaluated before cd is run.\n            if [[ \"${_ksi_prompt[last_reported_cwd]}\" != \"$PWD\" ]]; then\n                _ksi_prompt[last_reported_cwd]=\"$PWD\"\n                builtin printf \"\\e]7;kitty-shell-cwd://%s%s\\a\" \"$HOSTNAME\" \"$PWD\"\n            fi\n        fi\n    }\n\n    if [[ \"${_ksi_prompt[cursor]}\" == \"y\" ]]; then\n        _ksi_prompt[ps1_suffix]+=\"\\[\\e[5 q\\]\"  # blinking bar cursor\n        _ksi_prompt[ps0_suffix]+=\"\\[\\e[0 q\\]\"  # blinking default cursor\n    fi\n\n    if [[ \"${_ksi_prompt[title]}\" == \"y\" ||  \"${_ksi_prompt[mark]}\" ]]; then\n        _ksi_get_current_command() {\n            builtin local last_cmd\n            last_cmd=$(HISTTIMEFORMAT= builtin history 1)\n            last_cmd=\"${last_cmd#*[[:digit:]]*[[:space:]]}\"  # remove leading history number\n            last_cmd=\"${last_cmd#\"${last_cmd%%[![:space:]]*}\"}\"  # remove remaining leading whitespace\n            if [[ \"${_ksi_prompt[title]}\" == \"y\" ]]; then\n                builtin printf \"\\e]2;%s%s\\a\" \"${_ksi_prompt[hostname_prefix]@P}\" \"${last_cmd//[[:cntrl:]]}\" # removes any control characters\n            fi\n            if [[ \"${_ksi_prompt[mark]}\" == \"y\" ]]; then\n                builtin printf \"\\e]133;C;cmdline=%q\\a\" \"$last_cmd\"\n            fi\n        }\n        _ksi_prompt[ps0]+='$(_ksi_get_current_command > /dev/tty)';\n    fi\n\n    if [[ \"${_ksi_prompt[title]}\" == \"y\" ]]; then\n        if [[ -z \"$KITTY_PID\" ]]; then\n            if [[ -n \"$SSH_TTY\" || -n \"$SSH2_TTY$KITTY_WINDOW_ID\" ]]; then\n                # connected to most SSH servers\n                # or use ssh kitten to connected to some SSH servers that do not set SSH_TTY\n                _ksi_prompt[hostname_prefix]=\"\\h: \"\n            elif [[ -n \"$(builtin command -v who)\" && \"$(builtin command who -m 2> /dev/null)\" =~ \"\\([a-fA-F.:0-9]+\\)$\" ]]; then\n                # the shell integration script is installed manually on the remote system\n                # the environment variables are cleared after sudo\n                # OpenSSH's sshd creates entries in utmp for every login so use those\n                _ksi_prompt[hostname_prefix]=\"\\h: \"\n            fi\n        fi\n        # see https://www.gnu.org/software/bash/manual/html_node/Controlling-the-Prompt.html#Controlling-the-Prompt\n        # we use suffix here because some distros add title setting to their bashrc files by default\n        _ksi_prompt[ps1_suffix]+=\"\\[\\e]2;${_ksi_prompt[hostname_prefix]}\\w\\a\\]\"\n        if [[ \"$HISTCONTROL\" == *\"ignoreboth\"* ]] || [[ \"$HISTCONTROL\" == *\"ignorespace\"* ]]; then\n            _ksi_debug_print \"ignoreboth or ignorespace present in bash HISTCONTROL setting, showing running command will not be robust\"\n        fi\n    fi\n\n    if [[ \"${_ksi_prompt[mark]}\" == \"y\" ]]; then\n        # this can result in multiple D prompt marks or ones that dont\n        # correspond to a cmd but kitty handles this gracefully, only\n        # taking into account the first D after a C.\n        _ksi_prompt[ps1]+=\"\\[\\e]133;D;\\$?\\a\\e]133;A\\a\\]\"\n        _ksi_prompt[ps2]+=\"\\[\\e]133;A;k=s\\a\\]\"\n    fi\n\n    builtin alias edit-in-kitty=\"kitten edit-in-kitty\"\n\n\n    if [[ \"${_ksi_prompt[complete]}\" == \"y\" ]]; then\n        _ksi_completions() {\n            builtin local src\n            builtin local limit\n            # Send all words up to the word the cursor is currently on\n            builtin let limit=1+$COMP_CWORD\n            src=$(builtin printf \"%s\\n\" \"${COMP_WORDS[@]:0:$limit}\" | builtin command kitten __complete__ bash)\n            if [[ $? == 0 ]]; then\n                builtin eval \"${src}\"\n            fi\n        }\n        builtin complete -F _ksi_completions kitty\n        builtin complete -F _ksi_completions edit-in-kitty\n        builtin complete -F _ksi_completions clone-in-kitty\n        builtin complete -F _ksi_completions kitten\n    fi\n\n    # wrap our prompt additions in markers we can use to remove them using\n    # bash's anemic pattern substitution\n    if [[ -n \"${_ksi_prompt[ps0]}\" ]]; then\n        _ksi_prompt[ps0]=\"${_ksi_prompt[start_mark]}${_ksi_prompt[ps0]}${_ksi_prompt[end_mark]}\"\n    fi\n    if [[ -n \"${_ksi_prompt[ps0_suffix]}\" ]]; then\n        _ksi_prompt[ps0_suffix]=\"${_ksi_prompt[start_suffix_mark]}${_ksi_prompt[ps0_suffix]}${_ksi_prompt[end_suffix_mark]}\"\n    fi\n    if [[ -n \"${_ksi_prompt[ps1]}\" ]]; then\n        _ksi_prompt[ps1]=\"${_ksi_prompt[start_mark]}${_ksi_prompt[ps1]}${_ksi_prompt[end_mark]}\"\n    fi\n    if [[ -n \"${_ksi_prompt[ps1_suffix]}\" ]]; then\n        _ksi_prompt[ps1_suffix]=\"${_ksi_prompt[start_suffix_mark]}${_ksi_prompt[ps1_suffix]}${_ksi_prompt[end_suffix_mark]}\"\n    fi\n    if [[ -n \"${_ksi_prompt[ps2]}\" ]]; then\n        _ksi_prompt[ps2]=\"${_ksi_prompt[start_mark]}${_ksi_prompt[ps2]}${_ksi_prompt[end_mark]}\"\n    fi\n    # BASH aborts the entire script when doing unset with failglob set, somebody should report this upstream\n    builtin local oldval\n    oldval=$(builtin shopt -p failglob)\n    builtin shopt -u failglob\n    builtin unset _ksi_prompt[start_mark] _ksi_prompt[end_mark] _ksi_prompt[start_suffix_mark] _ksi_prompt[end_suffix_mark] _ksi_prompt[start_secondary_mark] _ksi_prompt[end_secondary_mark]\n    builtin eval \"$oldval\"\n\n    # install our prompt command, using an array if it is unset or already an array,\n    # otherwise append a string. We check if _ksi_prompt_command exists as some shell\n    # scripts stupidly export PROMPT_COMMAND making it inherited by all programs launched\n    # from the shell\n    builtin local pc\n    pc='builtin declare -F _ksi_prompt_command > /dev/null 2> /dev/null && _ksi_prompt_command'\n    if [[ -z \"${PROMPT_COMMAND[*]}\" ]]; then\n        PROMPT_COMMAND=([0]=\"$pc\")\n    elif [[ $(builtin declare -p PROMPT_COMMAND 2> /dev/null) =~ 'declare -a PROMPT_COMMAND' ]]; then\n        PROMPT_COMMAND+=(\"$pc\")\n    else\n        builtin local oldval\n        oldval=$(builtin shopt -p extglob)\n        builtin shopt -s extglob\n        PROMPT_COMMAND=\"${PROMPT_COMMAND%%+([[:space:]])}\"\n        PROMPT_COMMAND=\"${PROMPT_COMMAND%%+(;)}\"\n        builtin eval \"$oldval\"\n        PROMPT_COMMAND+=\"; $pc\"\n    fi\n    if [ -n \"${KITTY_IS_CLONE_LAUNCH}\" ]; then\n        builtin local orig_conda_env=\"$CONDA_DEFAULT_ENV\"\n        builtin eval \"${KITTY_IS_CLONE_LAUNCH}\"\n        builtin hash -r 2> /dev/null 1> /dev/null\n        builtin local venv=\"${VIRTUAL_ENV}/bin/activate\"\n        builtin local sourced=\"\"\n        _ksi_s_is_ok() {\n            [[ -z \"$sourced\" && \"$KITTY_CLONE_SOURCE_STRATEGIES\" == *\",$1,\"* ]] && builtin return 0\n            builtin return 1\n        }\n\n        if _ksi_s_is_ok \"venv\" && [ -n \"${VIRTUAL_ENV}\" -a -r \"$venv\" ]; then\n            sourced=\"y\"\n            builtin unset VIRTUAL_ENV\n            builtin source \"$venv\"\n        fi; if _ksi_s_is_ok \"conda\" && [ -n \"${CONDA_DEFAULT_ENV}\" ] && builtin command -v conda >/dev/null 2>/dev/null && [ \"${CONDA_DEFAULT_ENV}\" != \"$orig_conda_env\" ]; then\n            sourced=\"y\"\n            conda activate \"${CONDA_DEFAULT_ENV}\"\n        fi; if _ksi_s_is_ok \"env_var\" && [[ -n \"${KITTY_CLONE_SOURCE_CODE}\" ]]; then\n            sourced=\"y\"\n            builtin eval \"${KITTY_CLONE_SOURCE_CODE}\"\n        fi; if _ksi_s_is_ok \"path\" && [[ -r \"${KITTY_CLONE_SOURCE_PATH}\" ]]; then\n            sourced=\"y\"\n            builtin source \"${KITTY_CLONE_SOURCE_PATH}\"\n        fi\n        builtin unset -f _ksi_s_is_ok\n        # Ensure PATH has no duplicate entries\n        if [ -n \"$PATH\" ]; then\n            builtin local old_PATH=$PATH:; PATH=\n            while [ -n \"$old_PATH\" ]; do\n                builtin local x\n                x=${old_PATH%%:*}\n                case $PATH: in\n                    *:\"$x\":*) ;;\n                    *) PATH=$PATH:$x;;\n                esac\n                old_PATH=${old_PATH#*:}\n            done\n            PATH=${PATH#:}\n        fi\n    fi\n    builtin unset KITTY_IS_CLONE_LAUNCH KITTY_CLONE_SOURCE_STRATEGIES\n    if [[ -n \"$krcs\" ]]; then builtin\n        builtin printf \"\\e]2;%s\\a\" \"${krcs//[[:cntrl:]]}\" # removes any control characters\n        eval \"$krcs\";\n    fi\n}\n_ksi_main\nbuiltin unset -f _ksi_main\n\ncase :$SHELLOPTS: in\n  *:posix:*) ;;\n  *)\n\n_ksi_transmit_data() {\n    builtin local data\n    data=\"${1//[[:space:]]}\"\n    builtin local pos=0\n    builtin local chunk_num=0\n    while [ $pos -lt ${#data} ]; do\n        builtin local chunk=\"${data:$pos:2048}\"\n        pos=$(($pos+2048))\n        builtin printf '\\eP@kitty-%s|%s:%s\\e\\\\' \"${2}\" \"${chunk_num}\" \"${chunk}\"\n        chunk_num=$(($chunk_num+1))\n    done\n    # save history so it is available in new shell\n    [ \"$3\" = \"save_history\" ] && builtin history -a\n    builtin printf '\\eP@kitty-%s|\\e\\\\' \"${2}\"\n}\n\nclone-in-kitty() {\n    builtin local bv=\"${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}.${BASH_VERSINFO[2]}\"\n    builtin local data=\"shell=bash,pid=$$,bash_version=$bv,cwd=$(builtin printf \"%s\" \"$PWD\" | builtin command base64),envfmt=bash,env=$(builtin export | builtin command base64)\"\n    while :; do\n        case \"$1\" in\n            \"\") break;;\n            -h|--help)\n                builtin printf \"%s\\n\\n%s\\n\" \"Clone the current bash session into a new kitty window.\" \"For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#clone-shell\"\n                builtin return\n                ;;\n            *) data=\"$data,a=$(builtin printf \"%s\" \"$1\" | builtin command base64)\";;\n        esac\n        shift\n    done\n    _ksi_transmit_data \"$data\" \"clone\" \"save_history\"\n}\n\n      ;;\nesac\n\n"
  },
  {
    "path": "shell-integration/fish/vendor_completions.d/clone-in-kitty.fish",
    "content": "function __ksi_completions\n    set --local ct (commandline --current-token)\n    set --local tokens (commandline --tokenize --cut-at-cursor --current-process)\n    printf \"%s\\n\" $tokens $ct | command kitten __complete__ fish | source -\nend\n\ncomplete -f -c clone-in-kitty -a \"(__ksi_completions)\"\n"
  },
  {
    "path": "shell-integration/fish/vendor_completions.d/kitten.fish",
    "content": "function __ksi_completions\n    set --local ct (commandline --current-token)\n    set --local tokens (commandline --tokenize --cut-at-cursor --current-process)\n    printf \"%s\\n\" $tokens $ct | command kitten __complete__ fish | source -\nend\n\ncomplete -f -c kitten -a \"(__ksi_completions)\"\n"
  },
  {
    "path": "shell-integration/fish/vendor_completions.d/kitty.fish",
    "content": "function __ksi_completions\n    set --local ct (commandline --current-token)\n    set --local tokens (commandline --tokenize --cut-at-cursor --current-process)\n    printf \"%s\\n\" $tokens $ct | command kitten __complete__ fish | source -\nend\n\ncomplete -f -c kitty -a \"(__ksi_completions)\"\n"
  },
  {
    "path": "shell-integration/fish/vendor_conf.d/kitty-shell-integration.fish",
    "content": "#!/bin/fish\n\n# To use fish's autoloading feature, kitty prepends the vendored integration script directory to XDG_DATA_DIRS.\n# The original paths needs to be restored here to not affect other programs.\n# In particular, if the original XDG_DATA_DIRS does not exist, it needs to be removed.\nif set -q KITTY_FISH_XDG_DATA_DIR\n    if set -q XDG_DATA_DIRS\n        set --global --export --path XDG_DATA_DIRS \"$XDG_DATA_DIRS\"\n        if set --local index (contains --index \"$KITTY_FISH_XDG_DATA_DIR\" $XDG_DATA_DIRS)\n            set --erase --global XDG_DATA_DIRS[$index]\n            test -n \"$XDG_DATA_DIRS\" || set --erase --global XDG_DATA_DIRS\n        end\n        if set -q XDG_DATA_DIRS\n            set --global --export --unpath XDG_DATA_DIRS \"$XDG_DATA_DIRS\"\n        end\n    end\n    set --erase KITTY_FISH_XDG_DATA_DIR\nend\n\nstatus is-interactive || exit 0\nnot functions -q __ksi_schedule || exit 0\n# Check fish version 3.3.0+ efficiently and fallback to check the minimum working version 3.2.0, exit on outdated versions.\n# \"Warning: Update fish to version 3.3.0+ to enable kitty shell integration.\\n\"\nset -q fish_killring || set -q status_generation || string match -qnv \"3.1.*\" \"$version\"\nor echo -en \\eP@kitty-print\\|V2FybmluZzogVXBkYXRlIGZpc2ggdG8gdmVyc2lvbiAzLjMuMCsgdG8gZW5hYmxlIGtpdHR5IHNoZWxsIGludGVncmF0aW9uLgo=\\e\\\\ && exit 0 || exit 0\n\n\nif test -n \"$KITTY_SI_RUN_COMMAND_AT_STARTUP\"\n    printf '\\e]2;%s\\a' (string replace -ra '[\\x00-\\x1F\\x7F]' '' -- \"$KITTY_SI_RUN_COMMAND_AT_STARTUP\")\n    set --local _krcs \"$KITTY_SI_RUN_COMMAND_AT_STARTUP\"\n    set --erase KITTY_SI_RUN_COMMAND_AT_STARTUP\n    eval \"$_krcs\"\nend\n\nfunction __ksi_schedule --on-event fish_prompt -d \"Setup kitty integration after other scripts have run, we hope\"\n    functions --erase __ksi_schedule\n    test -n \"$KITTY_SHELL_INTEGRATION\" || return 0\n    set --local _ksi (string split \" \" -- \"$KITTY_SHELL_INTEGRATION\")\n    set --erase KITTY_SHELL_INTEGRATION\n    if test -n \"$SSH_KITTEN_KITTY_DIR\"\n        if not contains -- \"$SSH_KITTEN_KITTY_DIR\" \"$PATH\"\n            if not type kitten 2> /dev/null > /dev/null\n                set -gx PATH \"$PATH\" \"$SSH_KITTEN_KITTY_DIR\"\n            end\n        end\n        set --erase SSH_KITTEN_KITTY_DIR\n    end\n    # Enable cursor shape changes for default mode and vi mode\n    if not contains \"no-cursor\" $_ksi\n        function __ksi_set_cursor --on-variable fish_key_bindings -d \"Set the cursor shape for different modes when switching key bindings\"\n            if test \"$fish_key_bindings\" = fish_default_key_bindings\n                function __ksi_bar_cursor --on-event fish_prompt -d \"Set cursor shape to blinking bar on prompt\"\n                    echo -en \"\\e[5 q\"\n                end\n                # Change the cursor shape on first run\n                set -q argv[1]\n                and __ksi_bar_cursor\n            else\n                functions --erase __ksi_bar_cursor\n                contains \"$fish_key_bindings\" fish_vi_key_bindings fish_hybrid_key_bindings\n                and __ksi_set_vi_cursor\n            end\n        end\n\n        function __ksi_set_vi_cursor -d \"Set the vi mode cursor shapes\"\n            # Set the vi mode cursor shapes only when none of them are configured\n            set --local vi_modes fish_cursor_{default,insert,replace_one,visual}\n            set -q $vi_modes\n            test \"$status\" -eq 4 || return\n\n            set --local vi_cursor_shapes block line underscore block\n            for i in 1 2 3 4\n                set --global $vi_modes[$i] $vi_cursor_shapes[$i] blink\n            end\n\n            # Change the cursor shape for current mode\n            test \"$fish_bind_mode\" = \"insert\" && echo -en \"\\e[5 q\" || echo -en \"\\e[1 q\"\n        end\n\n        function __ksi_default_cursor --on-event fish_preexec -d \"Set cursor shape to blinking default shape before executing command\"\n            echo -en \"\\e[0 q\"\n        end\n\n        __ksi_set_cursor init\n    end\n\n    # Enable prompt marking with OSC 133\n    if not contains \"no-prompt-mark\" $_ksi and not set -q __ksi_prompt_state\n        set --local suffix ''\n        if bind --function-names | string match -q forward-char-passive\n            set suffix '-passive'\n        end\n        # fish 3.8 emits prompt markers, so we don't need to. It also is the\n        # first version to define forward-char-passive so use that as a test to detect it\n        if test \"$suffix\" = \"\"\n            function __ksi_mark_prompt_start --on-event fish_prompt --on-event fish_cancel --on-event fish_posterror\n                test \"$__ksi_prompt_state\" != prompt-start\n                and echo -en \"\\e]133;D\\a\"\n                set --global __ksi_prompt_state prompt-start\n                echo -en \"\\e]133;A;special_key=1\\a\"\n            end\n            __ksi_mark_prompt_start\n\n            function __ksi_mark_output_start --on-event fish_preexec\n                set --global __ksi_prompt_state pre-exec\n                printf '\\e]133;C;cmdline_url=%s\\a' (string escape --style=url -- \"$argv\")\n            end\n\n            function __ksi_mark_output_end --on-event fish_postexec\n                set --global __ksi_prompt_state post-exec\n                echo -en \"\\e]133;D;$status\\a\"\n            end\n\n            # With prompt marking, kitty clears the current prompt on resize,\n            # so we need fish to redraw it.\n            set --global fish_handle_reflow 1\n        end\n\n        # Binding for special key to move cursor on mouse click without triggering any\n        # autocompletion or other effects\n        for mode in (bind --list-modes | string match -v paste)  # bind in all modes except paste\n            bind --preset -M \"$mode\" \\e\\[1u \"forward-char$suffix\"\n            bind --preset -M \"$mode\" \\e\\[1\\;1u \"backward-char$suffix\"\n        end\n    end\n\n    # Enable CWD reporting\n    if not contains \"no-cwd\" $_ksi\n        # This function name is from fish and will override the builtin one, which is enabled by default for kitty in fish 3.5.0+.\n        # We provide this to ensure that fish 3.2.0 and above will work.\n        # https://github.com/fish-shell/fish-shell/blob/3.2.0/share/functions/__fish_config_interactive.fish#L275\n        # An executed program could change cwd and report the changed cwd, so also report cwd at each new prompt\n        function __update_cwd_osc --on-variable PWD --on-event fish_prompt -d \"Report PWD changes to kitty\"\n            status is-command-substitution\n            or echo -en \"\\e]7;kitty-shell-cwd://$hostname$PWD\\a\"\n        end\n        __update_cwd_osc\n    end\n\n    # Note that neither alias nor function is recursive in fish so if the user defines an alias/function\n    # for sudo it will be clobbered by us, so only install this if sudo is not already function\n    if not contains \"no-sudo\" $_ksi\n        and test -n \"$TERMINFO\" -a \"file\" = (type -t sudo 2> /dev/null || echo \"x\")\n        and not test -r \"/usr/share/terminfo/x/xterm-kitty\" -o -r \"/usr/share/terminfo/78/xterm-kitty\"\n        # Ensure terminfo is available in sudo\n        function sudo\n            set --local is_sudoedit \"n\"\n            for arg in $argv\n                if string match -q -- \"-e\" \"$arg\" or string match -q -- \"--edit\" \"$arg\"\n                    set is_sudoedit \"y\"\n                    break\n                end\n                if not string match -r -q -- \"^-\" \"$arg\" and not string match -r -q -- \"=\" \"$arg\"\n                    break  # reached the command\n                end\n            end\n            if string match -q -- \"$is_sudoedit\" \"y\"\n                command sudo $argv\n            else\n                command sudo TERMINFO=\"$TERMINFO\" $argv\n            end\n        end\n    end\n\n    # Handle clone launches\n    if test -n \"$KITTY_IS_CLONE_LAUNCH\"\n        set --local orig_conda_env \"$CONDA_DEFAULT_ENV\"\n        eval \"$KITTY_IS_CLONE_LAUNCH\"\n        set --local venv \"$VIRTUAL_ENV/bin/activate.fish\"\n        set --global _ksi_sourced\n        function _ksi_s_is_ok\n            test -z \"$_ksi_sourced\"\n            and string match -q -- \"*,$argv[1],*\" \"$KITTY_CLONE_SOURCE_STRATEGIES\"\n            and return 0\n            return 1\n        end\n        if _ksi_s_is_ok \"venv\"\n            and test -n \"$VIRTUAL_ENV\" -a -r \"$venv\"\n            set _ksi_sourced \"y\"\n            set --erase VIRTUAL_ENV _OLD_FISH_PROMPT_OVERRIDE  # activate.fish stupidly exports _OLD_FISH_PROMPT_OVERRIDE\n            source \"$venv\"\n        end\n        if _ksi_s_is_ok \"conda\"\n            and test -n \"$CONDA_DEFAULT_ENV\" -a \"$CONDA_DEFAULT_ENV\" != \"$orig_conda_env\"\n            and functions -q conda\n            set _ksi_sourced \"y\"\n            conda activate \"$CONDA_DEFAULT_ENV\"\n        end\n        if _ksi_s_is_ok \"env_var\"\n            and test -n \"$KITTY_CLONE_SOURCE_CODE\"\n            set _ksi_sourced \"y\"\n            eval \"$KITTY_CLONE_SOURCE_CODE\"\n        end\n        if _ksi_s_is_ok \"path\"\n            and test -r \"$KITTY_CLONE_SOURCE_PATH\"\n            set _ksi_sourced \"y\"\n            source \"$KITTY_CLONE_SOURCE_PATH\"\n        end\n        set --erase KITTY_IS_CLONE_LAUNCH KITTY_CLONE_SOURCE_STRATEGIES _ksi_sourced\n        functions --erase _ksi_s_is_ok\n\n        # Ensure PATH has no duplicate entries\n        set --local --path new_path\n        for p in $PATH\n            contains -- \"$p\" $new_path\n            or set --append new_path \"$p\"\n        end\n        test (count $new_path) -eq (count $PATH)\n        or set --global --export --path PATH $new_path\n    end\nend\n\nfunction edit-in-kitty --wraps \"kitten edit-in-kitty\" -d \"Edit the specified file in a kitty overlay window with your locally installed editor\"\n    kitten edit-in-kitty $argv\nend\n\nfunction __ksi_transmit_data -d \"Transmit data to kitty using chunked DCS escapes\"\n    set --local data (string replace --regex --all -- \"\\s\" \"\" \"$argv[1]\")\n    set --local data_len (string length -- \"$data\")\n    set --local pos 1\n    set --local chunk_num 0\n    while test \"$pos\" -le $data_len\n        printf \\eP@kitty-%s\\|%s:%s\\e\\\\ \"$argv[2]\" \"$chunk_num\" (string sub --start $pos --length 2048 -- \"$data\")\n        set pos (math $pos + 2048)\n        set chunk_num (math $chunk_num + 1)\n    end\n    printf \\eP@kitty-%s\\|\\e\\\\ \"$argv[2]\"\nend\n\nfunction clone-in-kitty -d \"Clone the current fish session into a new kitty window\"\n    set --local data\n    for a in $argv\n        if contains -- \"$a\" -h --help\n            echo \"Clone the current fish session into a new kitty window.\"\n            echo\n            echo \"For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#clone-shell\"\n            return\n        end\n        set --local ea (printf \"%s\" \"$a\" | base64)\n        set --append data \"a=$ea\"\n    end\n    set --local envs\n    for e in (set --export --names)\n        set --append envs \"$e=$$e\"\n    end\n    set --local b64_envs (string join0 -- $envs | base64)\n    set --local b64_cwd (printf \"%s\" \"$PWD\" | base64)\n    set --prepend data \"shell=fish\" \"pid=$fish_pid\" \"cwd=$b64_cwd\" \"env=$b64_envs\"\n    __ksi_transmit_data (string join \",\" -- $data) \"clone\"\nend\n"
  },
  {
    "path": "shell-integration/ssh/bootstrap-utils.sh",
    "content": "#!/bin/sh\n#\n# bootstrap-utils.sh\n# Copyright (C) 2022 Kovid Goyal <kovid at kovidgoyal.net>\n#\n# Distributed under terms of the MIT license.\n#\n\nmv_files_and_dirs() {\n    cwd=\"$PWD\"\n    cd \"$1\"\n    command find . -type d -exec mkdir -p \"$2/{}\" \";\"\n    command find . -type l -exec sh -c \"tgt=\\$(command readlink -n \\\"{}\\\"); command ln -snf \\\"\\$tgt\\\" \\\"$2/{}\\\"; command rm -f \\\"{}\\\"\" \";\"\n    command find . -type f -exec mv \"{}\" \"$2/{}\" \";\"\n    cd \"$cwd\"\n}\n\ncompile_terminfo() {\n    tname=\".terminfo\"\n    # Ensure the 78 dir is present\n    if [ ! -f \"$1/$tname/78/xterm-kitty\" ]; then\n        command mkdir -p \"$1/$tname/78\"\n        command ln -sf \"../x/xterm-kitty\" \"$1/$tname/78/xterm-kitty\"\n    fi\n\n    if [ -e \"/usr/share/misc/terminfo.cdb\" ]; then\n        # NetBSD requires this file, see https://github.com/kovidgoyal/kitty/issues/4622\n        # Also compile terminfo using tic installed via pkgsrc,\n        # so that programs that depend on the new version of ncurses automatically fall back to this one.\n        if [ -x \"/usr/pkg/bin/tic\" ]; then\n            /usr/pkg/bin/tic -x -o \"$1/$tname\" \"$1/.terminfo/kitty.terminfo\" 2>/dev/null\n        fi\n        if [ ! -e \"$1/$tname/x/xterm-kitty\" ]; then\n            command ln -sf \"../../.terminfo.cdb\" \"$1/$tname/x/xterm-kitty\"\n        fi\n        tname=\".terminfo.cdb\"\n    fi\n\n    # export TERMINFO\n    export TERMINFO=\"$HOME/$tname\"\n\n    # compile terminfo for this system\n    if [ -x \"$(command -v tic)\" ]; then\n        tic_out=$(command tic -x -o \"$1/$tname\" \"$1/.terminfo/kitty.terminfo\" 2>&1)\n        [ $? = 0 ] || die \"Failed to compile terminfo with err: $tic_out\"\n    fi\n}\n\nparse_passwd_record() {\n    printf \"%s\" \"$(command grep -o '[^:]*$')\"\n}\n\nlogin_shell_is_ok() {\n    [ -n \"$1\" ] && login_shell=$(echo $1 | parse_passwd_record)\n    [ -n \"$login_shell\" -a -x \"$login_shell\" ] && return 0\n    return 1\n}\n\nusing_getent() {\n    cmd=$(command -v getent) && [ -n \"$cmd\" ] && output=$(command \"$cmd\" passwd \"$USER\" 2>/dev/null) \\\n    && login_shell_is_ok \"$output\"\n}\n\nusing_id() {\n    cmd=$(command -v id) && [ -n \"$cmd\" ] && output=$(command \"$cmd\" -P \"$USER\" 2>/dev/null) \\\n    && login_shell_is_ok \"$output\"\n}\n\nusing_python() {\n    detect_python && output=$(command \"$python\" -c \"import pwd, os; print(pwd.getpwuid(os.geteuid()).pw_shell)\" 2>/dev/null) \\\n    && login_shell=\"$output\" && login_shell_is_ok\n}\n\nusing_perl() {\n    detect_perl && output=$(command \"$perl\" -e 'my $shell = (getpwuid($<))[8]; print $shell' 2>/dev/null) \\\n    && login_shell=\"$output\" && login_shell_is_ok\n}\n\nusing_passwd() {\n    [ -f \"/etc/passwd\" -a -r \"/etc/passwd\" ] && output=$(command grep \"^$USER:\" /etc/passwd 2>/dev/null) \\\n    && login_shell_is_ok \"$output\"\n}\n\nusing_shell_env() {\n    [ -n \"$SHELL\" ] && login_shell=\"$SHELL\" && login_shell_is_ok\n}\n\nexecute_with_python() {\n    if detect_python; then\n        exec \"$python\" \"-c\" \"import os; os.execlp('$login_shell', '-' '$shell_name')\"\n    fi\n    return 1\n}\n\nexecute_with_perl() {\n    if detect_perl; then\n        exec \"$perl\" \"-e\" \"exec {'$login_shell'} '-$shell_name'\"\n    fi\n    return 1\n}\n\nexec_zsh_with_integration() {\n    zdotdir=\"$ZDOTDIR\"\n    if [ -z \"$zdotdir\" ]; then\n        zdotdir=~\n    else\n        export KITTY_ORIG_ZDOTDIR=\"$zdotdir\"\n    fi\n    # dont prevent zsh-newuser-install from running\n    if [ -f \"$zdotdir/.zshrc\" -o -f \"$zdotdir/.zshenv\" -o -f \"$zdotdir/.zprofile\" -o -f \"$zdotdir/.zlogin\" ]; then\n        export ZDOTDIR=\"$shell_integration_dir/zsh\"\n        exec \"$login_shell\" \"-l\"\n    fi\n    # ensure this is not propagated\n    unset KITTY_ORIG_ZDOTDIR\n}\n\nexec_fish_with_integration() {\n    if [ -z \"$XDG_DATA_DIRS\" ]; then\n        export XDG_DATA_DIRS=\"$shell_integration_dir\"\n    else\n        export XDG_DATA_DIRS=\"$shell_integration_dir:$XDG_DATA_DIRS\"\n    fi\n    export KITTY_FISH_XDG_DATA_DIR=\"$shell_integration_dir\"\n    exec \"$login_shell\" \"-l\"\n}\n\nexec_bash_with_integration() {\n    export ENV=\"$shell_integration_dir/bash/kitty.bash\"\n    export KITTY_BASH_INJECT=\"1\"\n    if [ -z \"$HISTFILE\" ]; then\n        export HISTFILE=\"$HOME/.bash_history\"\n        export KITTY_BASH_UNEXPORT_HISTFILE=\"1\"\n    fi\n    exec \"$login_shell\" \"--login\" \"--posix\"\n}\n\nexec_with_shell_integration() {\n    [ -z \"$shell_integration_dir\" ] && return\n    case \"$shell_name\" in\n        \"zsh\")\n            exec_zsh_with_integration\n            ;;\n        \"fish\")\n            exec_fish_with_integration\n            ;;\n        \"bash\")\n            exec_bash_with_integration\n            ;;\n    esac\n}\n\nexecute_sh_with_posix_env() {\n    # only for sh as that is likely to be POSIX compliant\n    [ \"$shell_name\" = \"sh\" ] || return\n    # sh supports -l so use that\n    command \"$login_shell\" -l -c \":\" > /dev/null 2> /dev/null && return\n    [ -z \"$shell_integration_dir\" ] && die \"Could not read data over tty ssh kitten cannot function\"\n    sh_dir=\"$shell_integration_dir/sh\"\n    command mkdir -p \"$sh_dir\" || die \"Creating directory $sh_dir failed\"\n    sh_script=\"$sh_dir/login_shell_env.sh\"\n    # Source /etc/profile, ~/.profile, and then check and source ENV\n    printf \"%s\" '\nif [ -n \"$KITTY_SH_INJECT\" ]; then\n    unset ENV; unset KITTY_SH_INJECT\n    _ksi_safe_source() { [ -f \"$1\" -a -r \"$1\" ] || return 1; . \"$1\"; return 0; }\n    [ -n \"$KITTY_SH_POSIX_ENV\" ] && export ENV=\"$KITTY_SH_POSIX_ENV\"\n    unset KITTY_SH_POSIX_ENV\n    _ksi_safe_source \"/etc/profile\"; _ksi_safe_source \"${HOME-}/.profile\"\n    [ -n \"$ENV\" ] && _ksi_safe_source \"$ENV\"\n    unset -f _ksi_safe_source\nfi' > \"$sh_script\"\n    export KITTY_SH_INJECT=\"1\"\n    [ -n \"$ENV\" ] && export KITTY_SH_POSIX_ENV=\"$ENV\"\n    export ENV=\"$sh_script\"\n    exec \"$login_shell\"\n}\n\ninstall_kitty_bootstrap() {\n    kitty_dir=\"$data_dir/kitty/bin\"\n    export SSH_KITTEN_KITTY_DIR=\"$kitty_dir\"\n    kitty_exists=\"n\"\n    command -v kitty 2> /dev/null > /dev/null && kitty_exists=\"y\"\n    if [ \"$kitty_remote\" = \"yes\" -o \"$kitty_remote-$kitty_exists\" = \"if-needed-n\" ]; then\n        if [ \"$kitty_exists\" = \"y\" ]; then\n            export PATH=\"$kitty_dir:$PATH\"\n        else\n            export PATH=\"$PATH:$kitty_dir\"\n        fi\n    fi\n}\n\nprepare_for_exec() {\n    if [ -n \"$leading_data\" ]; then\n        # clear current line as it might have things echoed on it from leading_data\n        # because we only turn off echo in this script whereas the leading bytes could\n        # have been sent before the script had a chance to run\n        printf \"\\r\\033[K\" > /dev/tty\n    fi\n    [ -f \"$HOME/.terminfo/kitty.terminfo\" ] || die \"Incomplete extraction of ssh data\"\n    install_kitty_bootstrap\n\n    [ -n \"$login_shell\" ] || using_getent || using_id || using_python || using_perl || using_passwd || using_shell_env || login_shell=\"sh\"\n    case \"$login_shell\" in\n        /*) ;;\n        *)\n            if ! command -v \"$login_shell\" > /dev/null 2> /dev/null; then\n                for i in /opt/homebrew/bin /opt/homebrew/sbin /opt/local/bin /opt/local/sbin /usr/local/bin /usr/bin /bin /usr/sbin /sbin\n                do\n                    if [ -x \"$i/$login_shell\" ]; then\n                        login_shell=\"$i/$login_shell\"\n                        break\n                    fi\n                done\n            fi\n            ;;\n    esac\n    shell_name=$(command basename $login_shell)\n    [ -n \"$login_cwd\" ] && cd \"$login_cwd\"\n}\n\nexec_login_shell() {\n    case \"$KITTY_SHELL_INTEGRATION\" in\n        (\"\")\n            # only blanks or unset\n            unset KITTY_SHELL_INTEGRATION\n            ;;\n        (*)\n            # not blank\n            printf \"%s\" \"$KITTY_SHELL_INTEGRATION\" | command grep -q '\\bno-rc\\b' || exec_with_shell_integration\n            # either no-rc or exec failed\n            unset KITTY_SHELL_INTEGRATION\n            ;;\n    esac\n\n    # We need to pass the first argument to the executed program with a leading -\n    # to make sure the shell executes as a login shell. Note that not all shells\n    # support exec -a so we use the below to try to detect such shells\n    [ \"$(exec -a echo echo OK 2> /dev/null)\" = \"OK\" ] && exec -a \"-$shell_name\" \"$login_shell\"\n    execute_with_python\n    execute_with_perl\n    execute_sh_with_posix_env\n    exec \"$login_shell\" \"-l\"\n    if [ -e /dev/stderr ]; then\n        printf \"%s\\n\" \"Could not execute the shell $login_shell as a login shell\" > /dev/stderr\n    elif [ -e /dev/fd/2 ]; then\n        printf \"%s\\n\" \"Could not execute the shell $login_shell as a login shell\" > /dev/fd/2\n    else\n        printf \"%s\\n\" \"Could not execute the shell $login_shell as a login shell\"\n    fi\n    exec \"$login_shell\"\n}\n"
  },
  {
    "path": "shell-integration/ssh/bootstrap.py",
    "content": "#!/usr/bin/env python\n# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>\n\n\nimport base64\nimport contextlib\nimport errno\nimport io\nimport json\nimport os\nimport pwd\nimport shutil\nimport subprocess\nimport sys\nimport tarfile\nimport tempfile\nimport termios\n\ntty_file_obj = None\necho_on = int('ECHO_ON')\ndata_dir = shell_integration_dir = ''\nrequest_data = int('REQUEST_DATA')\nleading_data = b''\nlogin_shell = os.environ.get('SHELL') or '/bin/sh'\ntry:\n    login_shell = pwd.getpwuid(os.geteuid()).pw_shell\nexcept KeyError:\n    pass\nexport_home_cmd = b'EXPORT_HOME_CMD'\nif export_home_cmd:\n    HOME = base64.standard_b64decode(export_home_cmd).decode('utf-8')\n    os.chdir(HOME)\nelse:\n    HOME = os.path.expanduser('~')\n\n\ndef set_echo(fd, on=False):\n    if fd < 0:\n        fd = sys.stdin.fileno()\n    old = termios.tcgetattr(fd)\n    new = termios.tcgetattr(fd)\n    if on:\n        new[3] |= termios.ECHO\n    else:\n        new[3] &= ~termios.ECHO\n    termios.tcsetattr(fd, termios.TCSANOW, new)\n    return fd, old\n\n\ndef cleanup():\n    global tty_file_obj\n    if tty_file_obj is not None:\n        if echo_on:\n            set_echo(tty_file_obj.fileno(), True)\n        tty_file_obj.close()\n        tty_file_obj = None\n\n\ndef write_all(fd, data):\n    if isinstance(data, str):\n        data = data.encode('utf-8')\n    data = memoryview(data)\n    while data:\n        try:\n            n = os.write(fd, data)\n        except BlockingIOError:\n            continue\n        if not n:\n            break\n        data = data[n:]\n\n\ndef dcs_to_kitty(payload, type='ssh'):\n    if isinstance(payload, str):\n        payload = payload.encode('utf-8')\n    payload = base64.standard_b64encode(payload)\n    return b'\\033P@kitty-' + type.encode('ascii') + b'|' + payload + b'\\033\\\\'\n\n\ndef send_data_request():\n    write_all(tty_file_obj.fileno(), dcs_to_kitty('id=REQUEST_ID:pwfile=PASSWORD_FILENAME:pw=DATA_PASSWORD'))\n\n\ndef debug(msg):\n    data = dcs_to_kitty('debug: {}'.format(msg), 'print')\n    if tty_file_obj is None:\n        with open(os.ctermid(), 'wb') as fl:\n            write_all(fl.fileno(), data)\n    else:\n        write_all(tty_file_obj.fileno(), data)\n\n\ndef apply_env_vars(raw):\n    global login_shell\n\n    def process_defn(defn):\n        parts = json.loads(defn)\n        if len(parts) == 1:\n            key, val = parts[0], ''\n        else:\n            key, val, literal_quote = parts\n            if not literal_quote:\n                val = os.path.expandvars(val)\n        os.environ[key] = val\n\n    for line in raw.splitlines():\n        val = line.split(' ', 1)[-1]\n        if line.startswith('export '):\n            process_defn(val)\n        elif line.startswith('unset '):\n            os.environ.pop(json.loads(val)[0], None)\n    login_shell = os.environ.pop('KITTY_LOGIN_SHELL', login_shell)\n\n\ndef move(src, base_dest):\n    for x in os.listdir(src):\n        path = os.path.join(src, x)\n        dest = os.path.join(base_dest, x)\n        if os.path.islink(path):\n            try:\n                os.unlink(dest)\n            except EnvironmentError:\n                pass\n            os.symlink(os.readlink(path), dest)\n        elif os.path.isdir(path):\n            if not os.path.exists(dest):\n                os.makedirs(dest)\n            move(path, dest)\n        else:\n            shutil.move(path, dest)\n\n\ndef compile_terminfo(base):\n    try:\n        tic = shutil.which('tic')\n    except AttributeError:\n        # python2\n        for x in os.environ.get('PATH', '').split(os.pathsep):\n            q = os.path.join(x, 'tic')\n            if os.access(q, os.X_OK) and os.path.isfile(q):\n                tic = q\n                break\n        else:\n            tic = ''\n    if not tic:\n        return\n    tname = '.terminfo'\n    q = os.path.join(base, tname, '78', 'xterm-kitty')\n    if not os.path.exists(q):\n        try:\n            os.makedirs(os.path.dirname(q))\n        except EnvironmentError as e:\n            if e.errno != errno.EEXIST:\n                raise\n        os.symlink('../x/xterm-kitty', q)\n    if os.path.exists('/usr/share/misc/terminfo.cdb'):\n        # NetBSD requires this\n        os.symlink('../../.terminfo.cdb', os.path.join(base, tname, 'x', 'xterm-kitty'))\n        tname += '.cdb'\n    os.environ['TERMINFO'] = os.path.join(HOME, tname)\n    p = subprocess.Popen(\n        [tic, '-x', '-o', os.path.join(base, tname), os.path.join(base, '.terminfo', 'kitty.terminfo')],\n        stdout=subprocess.PIPE, stderr=subprocess.STDOUT\n    )\n    output = p.stdout.read()\n    rc = p.wait()\n    if rc != 0:\n        getattr(sys.stderr, 'buffer', sys.stderr).write(output)\n        raise SystemExit('Failed to compile the terminfo database')\n\n\ndef iter_base64_data(f):\n    global leading_data\n    started = 0\n    while True:\n        line = f.readline().rstrip()\n        if started == 0:\n            if line == b'KITTY_DATA_START':\n                started = 1\n            else:\n                leading_data += line\n        elif started == 1:\n            if line == b'OK':\n                started = 2\n            else:\n                raise SystemExit(line.decode('utf-8', 'replace').rstrip())\n        else:\n            if line == b'KITTY_DATA_END':\n                break\n            yield line\n\n\n@contextlib.contextmanager\ndef temporary_directory(dir, prefix):\n    # tempfile.TemporaryDirectory not available in python2\n    tdir = tempfile.mkdtemp(dir=dir, prefix=prefix)\n    try:\n        yield tdir\n    finally:\n        shutil.rmtree(tdir)\n\n\ndef get_data():\n    global data_dir, shell_integration_dir, leading_data\n    data = []\n    data = b''.join(iter_base64_data(tty_file_obj))\n    if leading_data:\n        # clear current line as it might have things echoed on it from leading_data\n        # because we only turn off echo in this script whereas the leading bytes could\n        # have been sent before the script had a chance to run\n        sys.stdout.write('\\r\\033[K')\n    data = base64.standard_b64decode(data)\n    with temporary_directory(dir=HOME, prefix='.kitty-ssh-kitten-untar-') as tdir, tarfile.open(fileobj=io.BytesIO(data)) as tf:\n        try:\n            # We have to use fully_trusted as otherwise it refuses to extract,\n            # for example, symlinks that point to absolute paths outside\n            # tdir.\n            tf.extractall(tdir, filter='fully_trusted')\n        except TypeError:\n            tf.extractall(tdir)\n        with open(tdir + '/data.sh') as f:\n            env_vars = f.read()\n        apply_env_vars(env_vars)\n        data_dir = os.environ.pop('KITTY_SSH_KITTEN_DATA_DIR')\n        if not os.path.isabs(data_dir):\n            data_dir = os.path.join(HOME, data_dir)\n        data_dir = os.path.abspath(data_dir)\n        shell_integration_dir = os.path.join(data_dir, 'shell-integration')\n        compile_terminfo(tdir + '/home')\n        move(tdir + '/home', HOME)\n        if os.path.exists(tdir + '/root'):\n            move(tdir + '/root', '/')\n\n\ndef exec_with_better_error(*a):\n    try:\n        os.execlp(*a)\n    except OSError as err:\n        if err.errno == errno.ENOENT:\n            raise SystemExit('The program: \"' + a[0] + '\" was not found')\n        raise\n\n\ndef exec_zsh_with_integration():\n    zdotdir = os.environ.get('ZDOTDIR') or ''\n    if not zdotdir:\n        zdotdir = HOME\n        os.environ.pop('KITTY_ORIG_ZDOTDIR', None)  # ensure this is not propagated\n    else:\n        os.environ['KITTY_ORIG_ZDOTDIR'] = zdotdir\n    # dont prevent zsh-newuser-install from running\n    for q in ('.zshrc', '.zshenv', '.zprofile', '.zlogin'):\n        if os.path.exists(os.path.join(zdotdir, q)):\n            os.environ['ZDOTDIR'] = shell_integration_dir + '/zsh'\n            exec_with_better_error(login_shell, os.path.basename(login_shell), '-l')\n    os.environ.pop('KITTY_ORIG_ZDOTDIR', None)  # ensure this is not propagated\n\n\ndef exec_fish_with_integration():\n    if not os.environ.get('XDG_DATA_DIRS'):\n        os.environ['XDG_DATA_DIRS'] = shell_integration_dir\n    else:\n        os.environ['XDG_DATA_DIRS'] = shell_integration_dir + ':' + os.environ['XDG_DATA_DIRS']\n    os.environ['KITTY_FISH_XDG_DATA_DIR'] = shell_integration_dir\n    exec_with_better_error(login_shell, os.path.basename(login_shell), '-l')\n\n\ndef exec_bash_with_integration():\n    os.environ['ENV'] = os.path.join(shell_integration_dir, 'bash', 'kitty.bash')\n    os.environ['KITTY_BASH_INJECT'] = '1'\n    if not os.environ.get('HISTFILE'):\n        os.environ['HISTFILE'] = os.path.join(HOME, '.bash_history')\n        os.environ['KITTY_BASH_UNEXPORT_HISTFILE'] = '1'\n    exec_with_better_error(login_shell, os.path.basename('login_shell'), '--posix')\n\n\ndef exec_with_shell_integration():\n    shell_name = os.path.basename(login_shell).lower()\n    if shell_name == 'zsh':\n        exec_zsh_with_integration()\n    if shell_name == 'fish':\n        exec_fish_with_integration()\n    if shell_name == 'bash':\n        exec_bash_with_integration()\n\n\ndef install_kitty_bootstrap():\n    kitty_remote = os.environ.pop('KITTY_REMOTE', '')\n    kitty_exists = shutil.which('kitty')\n    kitty_dir = os.path.join(data_dir, 'kitty', 'bin')\n    os.environ['SSH_KITTEN_KITTY_DIR'] = kitty_dir\n    if kitty_remote == 'yes' or (kitty_remote == 'if-needed' and not kitty_exists):\n        if kitty_exists:\n            os.environ['PATH'] = kitty_dir + os.pathsep + os.environ['PATH']\n        else:\n            os.environ['PATH'] = os.environ['PATH'] + os.pathsep + kitty_dir\n\n\ndef main():\n    global tty_file_obj, login_shell\n    # the value of O_CLOEXEC below is on macOS which is most likely to not have\n    # os.O_CLOEXEC being still stuck with python2\n    tty_file_obj = os.fdopen(os.open(os.ctermid(), os.O_RDWR | getattr(os, 'O_CLOEXEC', 16777216)), 'rb')\n    try:\n        if request_data:\n            set_echo(tty_file_obj.fileno(), on=False)\n            send_data_request()\n        get_data()\n    finally:\n        cleanup()\n    cwd = os.environ.pop('KITTY_LOGIN_CWD', '')\n    install_kitty_bootstrap()\n    if cwd:\n        try:\n            os.chdir(cwd)\n        except Exception as err:\n            print(f'Failed to change working directory to: {cwd} with error: {err}', file=sys.stderr)\n    ksi = frozenset(filter(None, os.environ.get('KITTY_SHELL_INTEGRATION', '').split()))\n    exec_cmd = b'EXEC_CMD'\n    if exec_cmd:\n        os.environ.pop('KITTY_SHELL_INTEGRATION', None)\n        cmd = base64.standard_b64decode(exec_cmd).decode('utf-8')\n        exec_with_better_error(login_shell, os.path.basename(login_shell), '-c', cmd)\n    TEST_SCRIPT  # noqa\n    if ksi and 'no-rc' not in ksi:\n        exec_with_shell_integration()\n    os.environ.pop('KITTY_SHELL_INTEGRATION', None)\n    exec_with_better_error(login_shell, '-' + os.path.basename(login_shell))\n\n\nmain()\n"
  },
  {
    "path": "shell-integration/ssh/bootstrap.sh",
    "content": "#!/bin/sh\n# Copyright (C) 2022 Kovid Goyal <kovid at kovidgoyal.net>\n# Distributed under terms of the GPLv3 license.\n\n{ \\unalias command; \\unset -f command; } >/dev/null 2>&1\ntdir=\"\"\nshell_integration_dir=\"\"\necho_on=\"ECHO_ON\"\n\ncleanup_on_bootstrap_exit() {\n    [ \"$echo_on\" = \"1\" ] && command stty \"echo\" 2> /dev/null < /dev/tty\n    echo_on=\"0\"\n    [ -n \"$tdir\" ] && command rm -rf \"$tdir\"\n    tdir=\"\"\n}\n\ndie() {\n    if [ -e /dev/stderr ]; then\n        printf \"\\033[31m%s\\033[m\\n\\r\" \"$*\" > /dev/stderr;\n    elif [ -e /dev/fd/2 ]; then\n        printf \"\\033[31m%s\\033[m\\n\\r\" \"$*\" > /dev/fd/2;\n    else\n        printf \"\\033[31m%s\\033[m\\n\\r\" \"$*\";\n    fi\n    cleanup_on_bootstrap_exit;\n    exit 1;\n}\n\npython_detected=\"0\"\ndetect_python() {\n    if [ python_detected = \"1\" ]; then\n        [ -n \"$python\" ] && return 0\n        return 1\n    fi\n    python_detected=\"1\"\n    python=$(command -v python3)\n    [ -z \"$python\" ] && python=$(command -v python2)\n    [ -z \"$python\" ] && python=$(command -v python)\n    if [ -z \"$python\" -o ! -x \"$python\" ]; then python=\"\"; return 1; fi\n    return 0\n}\n\nperl_detected=\"0\"\ndetect_perl() {\n    if [ perl_detected = \"1\" ]; then\n        [ -n \"$perl\" ] && return 0\n        return 1\n    fi\n    perl_detected=\"1\"\n    perl=$(command -v perl)\n    if [ -z \"$perl\" -o ! -x \"$perl\" ]; then perl=\"\"; return 1; fi\n    return 0\n}\n\nif command -v base64 > /dev/null 2> /dev/null; then\n    base64_encode() { command base64 | command tr -d \\\\n\\\\r; }\n    base64_decode() { command base64 -d; }\nelif command -v openssl > /dev/null 2> /dev/null; then\n    base64_encode() { command openssl enc -A -base64; }\n    base64_decode() { command openssl enc -A -d -base64; }\nelif command -v b64encode > /dev/null 2> /dev/null; then\n    base64_encode() { command b64encode - | command sed '1d;$d' | command tr -d \\\\n\\\\r; }\n    base64_decode() { command fold -w 76 | command b64decode -r; }\nelif detect_python; then\n    pybase64() { command \"$python\" -c \"import sys, base64; getattr(sys.stdout, 'buffer', sys.stdout).write(base64.standard_b64$1(getattr(sys.stdin, 'buffer', sys.stdin).read()))\"; }\n    base64_encode() { pybase64 \"encode\"; }\n    base64_decode() { pybase64 \"decode\"; }\nelif detect_perl; then\n    base64_encode() { command \"$perl\" -MMIME::Base64 -0777 -ne 'print encode_base64($_)'; }\n    base64_decode() { command \"$perl\" -MMIME::Base64 -ne 'print decode_base64($_)'; }\nelse\n    die \"base64 executable not present on remote host, ssh kitten cannot function.\"\nfi\n\ndcs_to_kitty() { printf \"\\033P@kitty-$1|%s\\033\\134\" \"$(printf \"%s\" \"$2\" | base64_encode)\" > /dev/tty; }\ndebug() { dcs_to_kitty \"print\" \"debug: $1\"; }\n\n# If $HOME is configured set it here\nEXPORT_HOME_CMD\n# ensure $HOME is set\n[ -z \"$HOME\" ] && HOME=~\n# ensure $USER is set\n[ -z \"$USER\" ] && USER=\"$LOGNAME\"\n[ -z \"$USER\" ] && USER=\"$(command whoami 2> /dev/null)\"\n\nleading_data=\"\"\nlogin_shell=\"\"\nlogin_cwd=\"\"\n\nrequest_data=\"REQUEST_DATA\"\ntrap \"cleanup_on_bootstrap_exit\" EXIT\n[ \"$request_data\" = \"1\" ] && {\n    command stty \"-echo\" < /dev/tty\n    dcs_to_kitty \"ssh\" \"id=\"REQUEST_ID\":pwfile=\"PASSWORD_FILENAME\":pw=\"DATA_PASSWORD\"\"\n}\n\nread_base64_from_tty() {\n    while IFS= read -r line; do\n        [ \"$line\" = \"KITTY_DATA_END\" ] && return 0\n        printf \"%s\" \"$line\"\n    done\n}\n\nuntar_and_read_env() {\n    # extract the tar file atomically, in the sense that any file from the\n    # tarfile is only put into place after it has been fully written to disk\n    command -v tar > /dev/null 2> /dev/null || die \"tar is not available on this server. The ssh kitten requires tar.\"\n    tdir=$(command mktemp -d \"$HOME/.kitty-ssh-kitten-untar-XXXXXXXXXXXX\")\n    [ $? = 0 ] || die \"Creating temp directory failed\"\n    # suppress STDERR for tar as tar prints various warnings if for instance, timestamps are in the future\n    old_umask=$(umask)\n    umask 000\n    read_base64_from_tty | base64_decode | command tar \"xpzf\" \"-\" \"-C\" \"$tdir\" 2> /dev/null\n    umask \"$old_umask\"\n    . \"$tdir/bootstrap-utils.sh\"\n    . \"$tdir/data.sh\"\n    [ -z \"$KITTY_SSH_KITTEN_DATA_DIR\" ] && die \"Failed to read SSH data from tty\"\n    case \"$KITTY_SSH_KITTEN_DATA_DIR\" in\n        /*) data_dir=\"$KITTY_SSH_KITTEN_DATA_DIR\" ;;\n        *) data_dir=\"$HOME/$KITTY_SSH_KITTEN_DATA_DIR\"\n    esac\n    shell_integration_dir=\"$data_dir/shell-integration\"\n    unset KITTY_SSH_KITTEN_DATA_DIR\n    login_shell=\"$KITTY_LOGIN_SHELL\"\n    unset KITTY_LOGIN_SHELL\n    login_cwd=\"$KITTY_LOGIN_CWD\"\n    unset KITTY_LOGIN_CWD\n    kitty_remote=\"$KITTY_REMOTE\"\n    unset KITTY_REMOTE\n    compile_terminfo \"$tdir/home\"\n    mv_files_and_dirs \"$tdir/home\" \"$HOME\"\n    [ -e \"$tdir/root\" ] && mv_files_and_dirs \"$tdir/root\" \"\"\n    command rm -rf \"$tdir\"\n    tdir=\"\"\n}\n\nget_data() {\n    started=\"n\"\n    while IFS= read -r line; do\n        if [ \"$started\" = \"y\" ]; then\n            [ \"$line\" = \"OK\" ] && break\n            die \"$line\"\n        else\n            if [ \"$line\" = \"KITTY_DATA_START\" ]; then\n                started=\"y\"\n            else\n                leading_data=\"$leading_data$line\"\n            fi\n        fi\n    done\n    untar_and_read_env\n}\n\n# ask for the SSH data\nget_data\ncleanup_on_bootstrap_exit\nprepare_for_exec\n# If a command was passed to SSH execute it here\nEXEC_CMD\n\n# Used in the tests\nTEST_SCRIPT\n\nexec_login_shell\n"
  },
  {
    "path": "shell-integration/ssh/kitten",
    "content": "#!/bin/sh\n# Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n#\n# Distributed under terms of the GPLv3 license.\n\n{ \\unalias command; \\unset -f command; } >/dev/null 2>&1\n\n\ndie() {\n    if [ -e /dev/stderr ]; then\n        printf \"\\033[31m%s\\033[m\\n\\r\" \"$*\" > /dev/stderr;\n    elif [ -e /dev/fd/2 ]; then\n        printf \"\\033[31m%s\\033[m\\n\\r\" \"$*\" > /dev/fd/2;\n    else\n        printf \"\\033[31m%s\\033[m\\n\\r\" \"$*\";\n    fi\n    exit 1;\n}\n\nexec_kitty() {\n    [ -n \"$kitty_exe\" ] && exec \"$kitty_exe\" \"$@\"\n    die \"Failed to execute kitty\"\n}\n\nscript_path=\"$(command readlink -f \"$0\" 2> /dev/null)\"\n[ $? = 0 ] || script_path=\"$0\"\nscript_dir=\"$(command dirname \"$script_path\")\"\ninstall_dir=\"$(command dirname \"$script_dir\")/install-tool\"\nremote_kitty_version_file=\"$script_dir/../version\"\nlocal_kitty_version_file=\"$install_dir/installed-kitten-version\"\nkitty_exe=\"$install_dir/kitten\"\nlocal_kitty_version=\"\"\n\n[ -f \"$kitty_exe\" -a -x \"$kitty_exe\" ] && exec_kitty \"$@\"\n\n# Use kitten from the downloaded kitty installation, if available.\nembed_exe=\"$(command dirname \"$script_dir\")/install/bin/kitten\"\n[ -f \"$embed_exe\" -a -x \"$embed_exe\" ] && {\n    kitty_exe=\"$embed_exe\"\n    exec_kitty \"$@\"\n}\n\ncase \"$(command uname)\" in\n    'Linux') OS=\"linux\";;\n    'Darwin') OS=\"darwin\";;\n    'FreeBSD') OS=\"freebsd\";;\n    'NetBSD') OS=\"netbsd\";;\n    'OpenBSD') OS=\"openbsd\";;\n    'DragonFlyBSD') OS=\"dragonfly\";;\n    *) die \"kitten pre-built binaries are not available for the $(command uname) operating system\";;\nesac\n\nif command -v curl 2> /dev/null > /dev/null; then\n    fetch() {\n        command curl -fL \"$1\"\n    }\n    fetch_quiet() {\n        command curl -fsSL \"$1\"\n    }\nelif command -v wget 2> /dev/null > /dev/null; then\n    fetch() {\n        command wget -O- \"$1\"\n    }\n    fetch_quiet() {\n        command wget --quiet -O- \"$1\"\n    }\nelse\n    die \"Neither curl nor wget available, cannot download kitten\"\nfi\n\ncase \"$(command uname -m)\" in\n    amd64|x86_64) arch=\"amd64\";;\n    aarch64*) arch=\"arm64\";;\n    armv8*) arch=\"arm64\";;\n    arm64) arch=\"arm64\";;\n    arm|armv7l) arch=\"arm\";;\n    i386) arch=\"386\";;\n    i686) arch=\"386\";;\n    *) die \"Unknown CPU architecture $(command uname -m)\";;\nesac\n\nurl=\"https://github.com/kovidgoyal/kitty/releases/latest/download/kitten-$OS-$arch\"\n\nprintf \"\\033[33mkitten needs to be installed\\033[m\\n\\n\"\ncommand mkdir -p \"$install_dir\"\nprintf \"Downloading kitten from: \\033[32m%s\\033[m\\n\\n\" \"$url\"\ndownload_dest=\"$(command mktemp \"$kitty_exe.XXXXXXXXXX\")\"\nfetch \"$url\" > \"$download_dest\" || {\n    command rm -f \"$download_dest\"\n    die \"Failed to download kitten\"\n}\ncommand chmod 755 \"$download_dest\"\ncommand mv \"$download_dest\" \"$kitty_exe\"\ncommand \"$kitty_exe\" --version | cut -d\" \" -f2 > \"$local_kitty_version_file\"\nexec_kitty \"$@\"\n"
  },
  {
    "path": "shell-integration/ssh/kitty",
    "content": "#!/bin/sh\n# Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>\n#\n# Distributed under terms of the GPLv3 license.\n\n{ \\unalias command; \\unset -f command; } >/dev/null 2>&1\n\n\ndie() { printf \"\\033[31m%s\\033[m\\n\\r\" \"$*\" > /dev/stderr; exit 1; }\n\ndelete_lock_dir() {\n    trap '' EXIT INT QUIT TERM\n    [ -n \"$lock_dir\" ] && {\n        command rm -rf \"$lock_dir\"\n        lock_dir=\"\"\n    }\n}\n\nexec_kitty() {\n    delete_lock_dir\n    [ -n \"$kitty_exe\" ] && exec \"$kitty_exe\" \"$@\"\n    die \"Failed to execute kitty\"\n}\n\n\nis_wrapped_kitten() {\n    wrapped_kittens=\"clipboard icat hyperlinked_grep ask hints unicode_input ssh themes diff show_key transfer query_terminal choose-files command-palette\"\n    [ -n \"$1\" ] && {\n        case \" $wrapped_kittens \" in\n            *\" $1 \"*) printf \"%s\" \"$1\" ;;\n        esac\n    }\n}\n\ntest \"(\" \"$1\" = \"+kitten\" -a -n \"$(is_wrapped_kitten \"$2\")\" \")\" -o \"(\" \"$1\" = \"+\" -a \"$2\" = \"kitten\" -a \"$(is_wrapped_kitten \"$3\")\" \")\" && {\n    if [ \"$1\" = \"+kitten\" ]; then shift \"1\"; else shift \"2\"; fi\n    exec kitten \"$@\"\n}\n\nlock_dir=\"\"\nscript_path=\"$(command readlink -f \"$0\" 2> /dev/null)\"\n[ $? = 0 ] || script_path=\"$0\"\nscript_dir=\"$(command dirname \"$script_path\")\"\ninstall_dir=\"$(command dirname \"$script_dir\")/install\"\nremote_kitty_version_file=\"$script_dir/../version\"\nlocal_kitty_version_file=\"$install_dir/installed-kitty-version\"\nkitty_exe=\"$install_dir/bin/kitty\"\nlocal_kitty_version=\"\"\n\n[ -f \"$kitty_exe\" -a -x \"$kitty_exe\" -a \"$1\" != \"+update-kitty\" ] && exec_kitty \"$@\"\n\ncase \"$(command uname)\" in\n    'Linux') OS=\"linux\";;\n    'Darwin') OS=\"macos\";;\n    *) die \"kitty pre-built binaries are not available for the $(command uname) operating system\";;\nesac\n\nif command -v curl 2> /dev/null > /dev/null; then\n    fetch() {\n        command curl -fL \"$1\"\n    }\n    fetch_quiet() {\n        command curl -fsSL \"$1\"\n    }\nelif command -v wget 2> /dev/null > /dev/null; then\n    fetch() {\n        command wget -O- \"$1\"\n    }\n    fetch_quiet() {\n        command wget --quiet -O- \"$1\"\n    }\nelse\n    die \"Neither curl nor wget available, cannot download kitty\"\nfi\n\nif [ \"$OS\" = \"linux\" ]; then\n    case \"$(command uname -m)\" in\n        amd64|x86_64) arch=\"x86_64\";;\n        aarch64*) arch=\"arm64\";;\n        armv8*) arch=\"arm64\";;\n        arm64) arch=\"arm64\";;\n        i386) arch=\"i686\";;\n        i686) arch=\"i686\";;\n        *) die \"Unknown CPU architecture $(command uname -m)\";;\n    esac\nfi\n\nrelease_version=$(fetch_quiet \"https://sw.kovidgoyal.net/kitty/current-version.txt\")\n[ $? -ne 0 -o -z \"$release_version\" ] && {\n    [ -n \"$local_kitty_version\" ] && exec_kitty \"$@\"\n    die \"Could not get kitty latest release version\"\n}\n\nif [ \"$OS\" = \"linux\" ]; then\n    url=\"https://github.com/kovidgoyal/kitty/releases/download/v$release_version/kitty-$release_version-$arch.txz\"\nelse\n    url=\"https://github.com/kovidgoyal/kitty/releases/download/v$release_version/kitty-$release_version.dmg\"\nfi\n\nlock_dir=\"$script_dir/kitty-install-lock\"\nif ! command mkdir \"$lock_dir\" 2> /dev/null; then\n    ed=\"$lock_dir\"\n    lock_dir=\"\";\n    die \"Failed to create lock dir another instance of the kitty bootstrap script is running. If you are sure that is not the case delete: $ed\";\nfi\ntrap 'delete_lock_dir' EXIT INT QUIT TERM\n\nprintf \"\\033[33mkitty needs to be installed\\033[m\\n\\n\"\ncommand rm -rf \"$install_dir\"\ncommand mkdir -p \"$install_dir\"\nprintf \"Downloading kitty from: \\033[32m%s\\033[m\\n\\n\" \"$url\"\n\nif [ \"$OS\" = \"linux\" ]; then\n    old_umask=$(umask)\n    umask 000\n    fetch \"$url\" | command tar -C \"$install_dir\" -xJof -\n    umask \"$old_umask\"\n    [ $? = 0 ] || die \"Failed to download and install kitty\"\nelse\n    tdir=$(command mktemp -d \"$install_dir/tmp-for-dmg-XXXXXXXXXXXX\")\n    [ $? = 0 ] || die \"Creating temp directory failed\"\n    fetch \"$url\" > \"$tdir/kitty.dmg\"\n    command mkdir \"$tdir/mp\"\n    command hdiutil attach \"$tdir/kitty.dmg\" \"-mountpoint\" \"$tdir/mp\" || die \"Failed to mount kitty.dmg\"\n    command ditto -v \"$tdir/mp/kitty.app\" \"$install_dir/kitty.app\"\n    rc=\"$?\"\n    command hdiutil detach \"$tdir/mp\"\n    command rm -rf \"$tdir\"\n    [ \"$rc\" != \"0\" ] && die \"Failed to copy kitty.app from mounted dmg\"\n    command mkdir \"$install_dir/bin\"\n    command ln -sf \"$install_dir/kitty.app/Contents/MacOS/kitty\" \"$install_dir/bin/kitty\"\nfi\ncommand \"$kitty_exe\" +runpy \"from kitty.constants import str_version; print(end=str_version)\" > \"$local_kitty_version_file\"\nexec_kitty \"$@\"\n"
  },
  {
    "path": "shell-integration/zsh/.zshenv",
    "content": "# This file can get sourced with aliases enabled. To avoid alias expansion\n# we quote everything that can be quoted. Some aliases will still break us\n# though.\n\n# Don't use [[ -v ... ]] because it doesn't work in zsh < 5.4.\nif [[ -n \"${KITTY_ORIG_ZDOTDIR+X}\" ]]; then\n    # Normally ZDOTDIR shouldn't be exported but it was in the environment\n    # of kitty, so we export it.\n    'builtin' 'export' ZDOTDIR=\"$KITTY_ORIG_ZDOTDIR\"\n    'builtin' 'unset' 'KITTY_ORIG_ZDOTDIR'\nelse\n    'builtin' 'unset' 'ZDOTDIR'\nfi\n\n# Use try-always to have the right error code.\n{\n    # Zsh treats empty $ZDOTDIR as if it was \"/\". We do the same.\n    #\n    # Source the user's zshenv before sourcing kitty.zsh because the former\n    # might set fpath and other things without which kitty.zsh won't work.\n    #\n    # Use typeset in case we are in a function with warn_create_global in\n    # effect. Unlikely but better safe than sorry.\n    'builtin' 'typeset' _ksi_file=${ZDOTDIR-~}\"/.zshenv\"\n    # Zsh ignores unreadable rc files. We do the same.\n    # Zsh ignores rc files that are directories, and so does source.\n    [[ ! -r \"$_ksi_file\" ]] || 'builtin' 'source' '--' \"$_ksi_file\"\n} always {\n    if [[ -o 'interactive' && -n \"${KITTY_SHELL_INTEGRATION-}\" ]]; then\n        'builtin' 'autoload' '--' 'is-at-least'\n        'is-at-least' \"5.1\" || {\n            builtin echo \"ZSH ${ZSH_VERSION} is too old for kitty shell integration\" > /dev/stderr\n            return\n        }\n        # ${(%):-%x} is the path to the current file.\n        # On top of it we add :A:h to get the directory.\n        'builtin' 'typeset' _ksi_file=\"${${(%):-%x}:A:h}\"/kitty-integration\n        if [[ -r \"$_ksi_file\" ]]; then\n            'builtin' 'autoload' '-Uz' '--' \"$_ksi_file\"\n            \"${_ksi_file:t}\"\n            'builtin' 'unfunction' '--' \"${_ksi_file:t}\"\n        fi\n    fi\n    'builtin' 'unset' '_ksi_file'\n}\n"
  },
  {
    "path": "shell-integration/zsh/completions/_kitty",
    "content": "#compdef kitty\n\n(( ${+commands[kitten]} )) || builtin return\nbuiltin local src cmd=${(F)words:0:$CURRENT}\n# Send all words up to the word the cursor is currently on.\nsrc=$(builtin command kitten __complete__ zsh \"_matcher=$_matcher\" <<<$cmd) || builtin return\nbuiltin eval \"$src\"\n"
  },
  {
    "path": "shell-integration/zsh/kitty-integration",
    "content": "#!/bin/zsh\n#\n# Enables integration between zsh and kitty based on KITTY_SHELL_INTEGRATION.\n# The latter is set by kitty based on kitty.conf.\n#\n# This is an autoloadable function. It's invoked automatically in shells\n# directly spawned by kitty but not in any other shells. For example, running\n# `exec zsh`, `sudo -E zsh`, `tmux`, or plain `zsh` will create a shell where\n# kitty-integration won't automatically run. Zsh users who want integration with\n# kitty in all shells should add the following lines to their .zshrc:\n#\n#   if [[ -n \"$KITTY_INSTALLATION_DIR\" ]]; then\n#     export KITTY_SHELL_INTEGRATION=\"enabled\"\n#     autoload -Uz -- \"$KITTY_INSTALLATION_DIR\"/shell-integration/zsh/kitty-integration\n#     kitty-integration\n#     unfunction kitty-integration\n#   fi\n#\n# Implementation note: We can assume that alias expansion is disabled in this\n# file, so no need to quote defensively. We still have to defensively prefix all\n# builtins with `builtin` to avoid accidentally invoking user-defined functions.\n# We avoid `function` reserved word as an additional defensive measure.\n\nbuiltin emulate -L zsh -o no_warn_create_global -o no_aliases\n\n[[ -o interactive ]]                || builtin return 0  # non-interactive shell\n[[ -n \"$KITTY_SHELL_INTEGRATION\" ]] || builtin return 0  # integration disabled\n(( ! $+_ksi_state ))                || builtin return 0  # already initialized\n\n# 0: no OSC 133 [AC] marks have been written yet.\n# 1: the last written OSC 133 C has not been closed with D yet.\n# 2: none of the above.\nbuiltin typeset -gi _ksi_state\n\n# Attempt to create a writable file descriptor to the TTY so that we can print\n# to the TTY later even when STDOUT is redirected. This code is fairly subtle.\n#\n# - It's tempting to do `[[ -t 1 ]] && exec {_ksi_state}>&1` but we cannot do this\n#   because it'll create a file descriptor >= 10 without O_CLOEXEC. This file\n#   descriptor will leak to child processes.\n# - If we do `exec {3}>&1`, the file descriptor won't leak to the child processes\n#   but it'll still leak if the current process is replaced with another. In\n#   addition, it'll break user code that relies on fd 3 being available.\n# - Zsh doesn't expose dup3, which would have allowed us to copy STDOUT with\n#   O_CLOEXEC. The only way to create a file descriptor with O_CLOEXEC is via\n#   sysopen.\n# - `zmodload zsh/system` and `sysopen -o cloexec -wu _ksi_fd -- /dev/tty` can\n#   fail with an error message to STDERR (the latter can happen even if /dev/tty\n#   is writable), hence the redirection of STDERR. We do it for the whole block\n#   for performance reasons (redirections are slow).\n# - We must open the file descriptor right here rather than in _ksi_deferred_init\n#   because there are broken zsh plugins out there that run `exec {fd}< <(cmd)`\n#   and then close the file descriptor more than once while suppressing errors.\n#   This could end up closing our file descriptor if we opened it in\n#   _ksi_deferred_init.\ntypeset -gi _ksi_fd\n{\n    builtin zmodload zsh/system && (( $+builtins[sysopen] )) && {\n        { [[ -w     $TTY ]] && builtin sysopen -o cloexec -wu _ksi_fd --     $TTY } ||\n        { [[ -w /dev/tty ]] && builtin sysopen -o cloexec -wu _ksi_fd -- /dev/tty }\n    }\n} 2>/dev/null || (( _ksi_fd = 1 ))\n\n# Asks kitty to print $@ to its STDERR. This is for debugging.\n_ksi_debug_print() {\n    builtin local data\n    data=$(builtin command base64 <<<\"${(j: :)@}\") || builtin return\n    # Removing all spaces rather than just \\n allows this code to\n    # work on broken systems where base64 outputs \\r\\n.\n    builtin print -nu \"$_ksi_fd\" '\\eP@kitty-print|'\"${data//[[:space:]]}\"'\\e\\\\'\n}\n\n# We defer initialization until precmd for several reasons:\n#\n# - Oh My Zsh and many other configs remove zle-line-init and\n#   zle-line-finish hooks when they initialize.\n# - By deferring initialization we allow user rc files to opt out from some\n#   parts of integration. For example, if a zshrc theme prints OSC 133\n#   marks, it can append \" no-prompt-mark\" to KITTY_SHELL_INTEGRATION during\n#   initialization to avoid redundant marks from our code.\nbuiltin typeset -ag precmd_functions\nprecmd_functions+=(_ksi_deferred_init)\n\n_ksi_deferred_init() {\n    builtin emulate -L zsh -o no_warn_create_global -o no_aliases\n\n    # Recognized options: no-cursor, no-title, no-prompt-mark, no-complete, no-cwd, no-sudo.\n    builtin local -a opt\n    opt=(${(s: :)KITTY_SHELL_INTEGRATION})\n    builtin unset KITTY_SHELL_INTEGRATION\n    builtin local krcs=\"$KITTY_SI_RUN_COMMAND_AT_STARTUP\"\n    builtin unset KITTY_SI_RUN_COMMAND_AT_STARTUP\n\n    if [[ -n \"$SSH_KITTEN_KITTY_DIR\" ]]; then\n        if [[ ! \"$PATH\" =~ (^|:)${SSH_KITTEN_KITTY_DIR}(:|$) ]] && [[ -z \"$(builtin command -v kitten)\" ]]; then\n            builtin export PATH=\"${PATH}:${SSH_KITTEN_KITTY_DIR}\"\n        fi\n        builtin unset SSH_KITTEN_KITTY_DIR\n    fi\n\n    # The directory where kitty-integration is located: /.../shell-integration/zsh.\n    builtin local self_dir=\"${functions_source[_ksi_deferred_init]:A:h}\"\n    # The directory with _kitty. We store it in a directory of its own rather than\n    # in $self_dir because we are adding it to fpath and we don't want any other\n    # files to be accidentally autoloadable.\n    builtin local comp_dir=\"$self_dir/completions\"\n\n    # Enable completions for `kitty` command.\n    if (( ! opt[(Ie)no-complete] )) && [[ -r $comp_dir/_kitty ]]; then\n        if (( $+functions[compdef] )); then\n            # If compdef is defined, then either compinit has already run or it's\n            # a shim that records all calls for the purpose of replaying them after\n            # compinit. Either way we clobber the existing completion for kitty and\n            # install our own.\n            builtin unset \"functions[_kitty]\"\n            builtin autoload -Uz -- $comp_dir/_kitty\n            compdef _kitty kitty\n            compdef _kitty clone-in-kitty\n            compdef _kitty kitten\n        fi\n\n        # If compdef is not set, compinit has not run yet. In this case we must\n        # add our completions directory to fpath so that _kitty gets picked up by\n        # compinit.\n        #\n        # We extend fpath even if compinit has run because it might run again.\n        # Without our completions directory in fpath compinit would our _comp\n        # mapping.\n        builtin typeset -ga fpath\n        fpath=($comp_dir ${fpath:#$comp_dir})\n    fi\n\n    # Enable semantic markup with OSC 133.\n    if (( ! opt[(Ie)no-prompt-mark] )); then\n        _ksi_precmd() {\n            builtin local -i cmd_status=$?\n            builtin emulate -L zsh -o no_warn_create_global -o no_aliases\n\n            # Don't write OSC 133 D when our precmd handler is invoked from zle.\n            # Some plugins do that to update prompt on cd.\n            if ! builtin zle; then\n                # This code works incorrectly in the presence of a precmd or chpwd\n                # hook that prints. For example, sindresorhus/pure prints an empty\n                # line on precmd and marlonrichert/zsh-snap prints $PWD on chpwd.\n                # We'll end up writing our OSC 133 D mark too late.\n                #\n                # Another failure mode is when the output of a command doesn't end\n                # with LF and prompst_sp is set (it is by default). In this case\n                # we'll incorrectly state that '%' from prompt_sp is a part of the\n                # command's output.\n                if (( _ksi_state == 1 )); then\n                    # The last written OSC 133 C has not been closed with D yet.\n                    # Close it and supply status.\n                    builtin print -nu $_ksi_fd '\\e]133;D;'$cmd_status'\\a'\n                    (( _ksi_state = 2 ))\n                elif (( _ksi_state == 2 )); then\n                    # There might be an unclosed OSC 133 C. Close that.\n                    builtin print -nu $_ksi_fd '\\e]133;D\\a'\n                fi\n            fi\n\n            builtin local mark1=$'%{\\e]133;A\\a%}'\n            if [[ -o prompt_percent ]]; then\n                builtin typeset -g precmd_functions\n                if [[ ${precmd_functions[-1]} == _ksi_precmd ]]; then\n                    # This is the best case for us: we can add our marks to PS1 and\n                    # PS2. This way our marks will be printed whenever zsh\n                    # redisplays prompt: on reset-prompt, on SIGWINCH, and on\n                    # SIGCHLD if notify is set. Themes that update prompt\n                    # asynchronously from a `zle -F` handler might still remove our\n                    # marks. Oh well.\n                    builtin local mark2=$'%{\\e]133;A;k=s\\a%}'\n                    # Add marks conditionally to avoid a situation where we have\n                    # several marks in place. These conditions can have false\n                    # positives and false negatives though.\n                    #\n                    # - False positive (with prompt_percent): PS1=\"%(?.$mark1.)\"\n                    # - False negative (with prompt_subst):   PS1='$mark1'\n                    [[ $PS1 == *$mark1* ]] || PS1=${mark1}${PS1}\n                    # PS2 mark is needed when clearing the prompt on resize\n                    [[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2}\n                    (( _ksi_state = 2 ))\n                else\n                    # If our precmd hook is not the last, we cannot rely on prompt\n                    # changes to stick, so we don't even try. At least we can move\n                    # our hook to the end to have better luck next time. If there is\n                    # another piece of code that wants to take this privileged\n                    # position, this won't work well. We'll break them as much as\n                    # they are breaking us.\n                    precmd_functions=(${precmd_functions:#_ksi_precmd} _ksi_precmd)\n                    # Plugins that invoke precmd hooks from zle do that before zle\n                    # is trashed. This means that the cursor is in the middle of\n                    # BUFFER and we cannot print our mark there. Prompt might\n                    # already have a mark, so the following reset-prompt will write\n                    # it. If it doesn't, there is nothing we can do.\n                    if ! builtin zle; then\n                        builtin print -rnu $_ksi_fd -- $mark1[3,-3]\n                        (( _ksi_state = 2 ))\n                    fi\n                fi\n            elif ! builtin zle; then\n                # Without prompt_percent we cannot patch prompt. Just print the\n                # mark, except when we are invoked from zle. In the latter case we\n                # cannot do anything.\n                builtin print -rnu $_ksi_fd -- $mark1[3,-3]\n                (( _ksi_state = 2 ))\n            fi\n        }\n\n        _ksi_preexec() {\n            builtin emulate -L zsh -o no_warn_create_global -o no_aliases\n\n            # This can potentially break user prompt. Oh well. The robustness of\n            # this code can be improved in the case prompt_subst is set because\n            # it'll allow us distinguish (not perfectly but close enough) between\n            # our own prompt, user prompt, and our own prompt with user additions on\n            # top. We cannot force prompt_subst on the user though, so we would\n            # still need this code for the no_prompt_subst case.\n            PS1=${PS1//$'%{\\e]133;A\\a%}'}\n            PS2=${PS2//$'%{\\e]133;A;k=s\\a%}'}\n\n            # This will work incorrectly in the presence of a preexec hook that\n            # prints. For example, if MichaelAquilina/zsh-you-should-use installs\n            # its preexec hook before us, we'll incorrectly mark its output as\n            # belonging to the command (as if the user typed it into zle) rather\n            # than command output.\n            builtin print -nu \"$_ksi_fd\" -f '\\e]133;C;cmdline=%q\\a' -- \"$1\"\n            (( _ksi_state = 1 ))\n        }\n\n        # the following two lines are commented out as currently kitty doesn't use B prompt marking\n        # and hooking zle widgets in ZSH is a total minefield, see https://github.com/kovidgoyal/kitty/issues/4428\n        # so we can at least tell users to use no-cursor and with that avoid hooking ZLE widgets at all\n        # functions[_ksi_zle_line_init]+='\n        #     builtin print -nu \"$_ksi_fd\" \"\\\\e]133;B\\\\a\"'\n    fi\n\n    # Enable reporting current working dir to terminal\n    if (( ! opt[(Ie)no-cwd] )); then\n        _ksi_report_pwd() { builtin print -nu $_ksi_fd '\\e]7;kitty-shell-cwd://'\"$HOST\"\"$PWD\"'\\a'; }\n        chpwd_functions=(${chpwd_functions[@]} \"_ksi_report_pwd\")\n        # An executed program could change cwd and report the changed cwd, so also report cwd at each new prompt\n        # as in this case chpwd_functions is insufficient. chpwd_functions is still needed for things like: cd x && something\n        functions[_ksi_precmd]+=\"\n            _ksi_report_pwd\"\n        _ksi_report_pwd\n    fi\n\n    # Enable terminal title changes.\n    if (( ! opt[(Ie)no-title] )); then\n        # We don't use `print -P` because it depends on prompt options, which\n        # we don't control and cannot change.\n        #\n        # We use (V) in preexec to convert control characters to something visible\n        # (LF becomes \\n, etc.). This isn't necessary in precmd because (%) does it\n        # for us.\n        builtin local is_ssh_session=\"n\"\n        if [[ -n \"$KITTY_PID\" ]]; then\n            # kitty running locally\n        elif [[ -n \"$SSH_TTY\" || -n \"$SSH2_TTY$KITTY_WINDOW_ID\" ]]; then\n            # connected to most SSH servers\n            # or use ssh kitten to connected to some SSH servers that do not set SSH_TTY\n            is_ssh_session=\"y\"\n        elif [[ -n \"$(builtin command -v who)\" ]]; then\n            # the shell integration script is installed manually on the remote system\n            # the environment variables are cleared after sudo\n            # OpenSSH's sshd creates entries in utmp for every login so use those\n            [[ \"$(builtin command who -m 2> /dev/null)\" =~ \"\\([a-fA-F.:0-9]+\\)$\" ]] && is_ssh_session=\"y\"\n        fi\n\n        if [[ \"$is_ssh_session\" == \"y\" ]]; then\n            functions[_ksi_precmd]+=\"\n                builtin print -rnu $_ksi_fd \\$'\\\\e]2;'\\\"\\${(V)\\${HOST-}/%.*/}: \\${(%):-%(4~|…/%3~|%~)}\\\"\\$'\\\\a'\"\n            functions[_ksi_preexec]+=\"\n                builtin print -rnu $_ksi_fd \\$'\\\\e]2;'\\\"\\${(V)\\${HOST-}/%.*/}: \\${(V)1}\\\"\\$'\\\\a'\"\n        else\n            functions[_ksi_precmd]+=\"\n                builtin print -rnu $_ksi_fd \\$'\\\\e]2;'\\\"\\${(%):-%(4~|…/%3~|%~)}\\\"\\$'\\\\a'\"\n            functions[_ksi_preexec]+=\"\n                builtin print -rnu $_ksi_fd \\$'\\\\e]2;'\\\"\\${(V)1}\\\"\\$'\\\\a'\"\n        fi\n    fi\n\n    # Enable cursor shape changes depending on the current keymap.\n    if (( ! opt[(Ie)no-cursor] )); then\n        # This implementation leaks blinking block cursor into external commands\n        # executed from zle. For example, users of fzf-based widgets may find\n        # themselves with a blinking block cursor within fzf.\n        _ksi_zle_line_init _ksi_zle_line_finish _ksi_zle_keymap_select() {\n            case ${KEYMAP-} in\n                # Blinking block cursor.\n                vicmd|visual) builtin print -nu \"$_ksi_fd\" '\\e[1 q';;\n                # Blinking bar cursor.\n                *)            builtin print -nu \"$_ksi_fd\" '\\e[5 q';;\n            esac\n        }\n        # Restore the blinking default shape before executing an external command\n        functions[_ksi_preexec]+=\"\n            builtin print -rnu $_ksi_fd \\$'\\\\e[0 q'\"\n    fi\n\n\n    # Some zsh users manually run `source ~/.zshrc` in order to apply rc file\n    # changes to the current shell. This is a terrible practice that breaks many\n    # things, including our shell integration. For example, Oh My Zsh and Prezto\n    # (both very popular among zsh users) will remove zle-line-init and\n    # zle-line-finish hooks if .zshrc is manually sourced. Prezto will also remove\n    # zle-keymap-select.\n    #\n    # Another common (and much more robust) way to apply rc file changes to the\n    # current shell is `exec zsh`. This will remove our integration from the shell\n    # unless it's explicitly invoked from .zshrc. This is not an issue with\n    # `exec zsh` but rather with our implementation of automatic shell integration.\n\n    # In the ideal world we would use add-zle-hook-widget to hook zle-line-init\n    # and similar widget. This breaks user configs though, so we have do this\n    # horrible thing instead.\n    builtin local hook func widget orig_widget flag\n    for hook in line-init line-finish keymap-select; do\n        func=_ksi_zle_${hook/-/_}\n        (( $+functions[$func] )) || builtin continue\n        widget=zle-$hook\n        if [[ $widgets[$widget] == user:azhw:* &&\n              $+functions[add-zle-hook-widget] -eq 1 ]]; then\n            # If the widget is already hooked by add-zle-hook-widget at the top\n            # level, add our hook at the end. We MUST do it this way. We cannot\n            # just wrap the widget ourselves in this case because it would\n            # trigger bugs in add-zle-hook-widget.\n            add-zle-hook-widget $hook $func\n        else\n            if (( $+widgets[$widget] )); then\n                # There is a widget but it's not from add-zle-hook-widget. We\n                # can rename the original widget, install our own and invoke\n                # the original when we are called.\n                #\n                # Note: The leading dot is to work around bugs in\n                # zsh-syntax-highlighting.\n                orig_widget=._ksi_orig_$widget\n                builtin zle -A $widget $orig_widget\n                if [[ $widgets[$widget] == user:* ]]; then\n                    # No -w here to preserve $WIDGET within the original widget.\n                    flag=\n                else\n                    flag=w\n                fi\n                functions[$func]+=\"\n                    builtin zle $orig_widget -N$flag -- \\\"\\$@\\\"\"\n            fi\n            builtin zle -N $widget $func\n        fi\n    done\n\n    # run startup command\n    if [[ -n \"$krcs\" ]]; then\n        builtin print -nu \"$_ksi_fd\" -f '\\e]2;%s\\e\\\\' -- \"${(V)krcs}\"\n        builtin eval \"$krcs\"\n    fi\n\n    if (( $+functions[_ksi_preexec] )); then\n        builtin typeset -ag preexec_functions\n        preexec_functions+=(_ksi_preexec)\n    fi\n\n    builtin typeset -ag precmd_functions\n    if (( $+functions[_ksi_precmd] )); then\n        precmd_functions=(${precmd_functions:/_ksi_deferred_init/_ksi_precmd})\n        _ksi_precmd\n    else\n        precmd_functions=(${precmd_functions:#_ksi_deferred_init})\n    fi\n\n    if [ -n \"${KITTY_IS_CLONE_LAUNCH}\" ]; then\n        builtin local orig_conda_env=\"$CONDA_DEFAULT_ENV\"\n        builtin eval \"${KITTY_IS_CLONE_LAUNCH}\"\n        builtin hash -r 2> /dev/null 1> /dev/null\n        builtin local venv=\"${VIRTUAL_ENV}/bin/activate\"\n        builtin local sourced=\"\"\n        _ksi_s_is_ok() {\n            [[ -z \"$sourced\" && \"$KITTY_CLONE_SOURCE_STRATEGIES\" == *\",$1,\"* ]] && builtin return 0\n            builtin return 1\n        }\n\n        if _ksi_s_is_ok \"venv\" && [[ -n \"${VIRTUAL_ENV}\" && -r \"$venv\" ]]; then\n            sourced=\"y\"\n            builtin unset VIRTUAL_ENV\n            builtin source \"$venv\"\n        fi; if _ksi_s_is_ok \"conda\" && [[ -n \"${CONDA_DEFAULT_ENV}\" && (( $+commands[conda] )) && \"${CONDA_DEFAULT_ENV}\" != \"$orig_conda_env\" ]]; then\n            sourced=\"y\"\n            conda activate \"${CONDA_DEFAULT_ENV}\"\n        fi; if _ksi_s_is_ok \"env_var\" && [[ -n \"${KITTY_CLONE_SOURCE_CODE}\" ]]; then\n            sourced=\"y\"\n            builtin eval \"${KITTY_CLONE_SOURCE_CODE}\"\n        fi; if _ksi_s_is_ok \"path\" && [[ -r \"${KITTY_CLONE_SOURCE_PATH}\" ]]; then\n            sourced=\"y\"\n            builtin source \"${KITTY_CLONE_SOURCE_PATH}\"\n        fi\n        builtin unfunction _ksi_s_is_ok\n        # Ensure PATH has no duplicate entries\n        builtin typeset -gxU PATH=\"$PATH\"\n    fi\n    builtin unset KITTY_IS_CLONE_LAUNCH KITTY_CLONE_SOURCE_STRATEGIES\n\n    builtin alias edit-in-kitty=\"kitten edit-in-kitty\"\n\n    if (( ! opt[(Ie)no-sudo] )) ; then\n        if [[ -n \"$TERMINFO\" && ! ( -r \"/usr/share/terminfo/x/xterm-kitty\" || -r \"/usr/share/terminfo/78/xterm-kitty\" ) ]]; then\n            sudo() {\n                # Ensure terminfo is available in sudo\n                builtin local is_sudoedit=\"n\"\n                for arg; do\n                    if [[ \"$arg\" == \"-e\" || $arg == \"--edit\" ]]; then\n                        is_sudoedit=\"y\"\n                        builtin break;\n                    fi\n                    [[ \"$arg\" != -* && \"$arg\" != *=* ]] && builtin break  # command found\n                done\n                if [[ \"$is_sudoedit\" == \"y\" ]]; then\n                    builtin command sudo \"$@\";\n                else\n                    builtin command sudo TERMINFO=\"$TERMINFO\" \"$@\";\n                fi\n            }\n        fi\n    fi\n\n    # Map alt+left/right to move by word if not already mapped. This is expected behavior on macOS and I am tired\n    # of answering questions about it.\n    [[ $(builtin bindkey \"^[[1;3C\") == *\" undefined-key\" ]] && builtin bindkey \"^[[1;3C\" \"forward-word\"\n    [[ $(builtin bindkey \"^[[1;3D\") == *\" undefined-key\" ]] && builtin bindkey \"^[[1;3D\" \"backward-word\"\n\n    # Unfunction _ksi_deferred_init to save memory. Don't unfunction\n    # kitty-integration though because decent public functions aren't supposed to\n    # to unfunction themselves when invoked. Unfunctioning is done by calling code.\n    builtin unfunction _ksi_deferred_init\n\n}\n\n_ksi_transmit_data() {\n    builtin local data=\"${1//[[:space:]]}\"\n    builtin local pos=0\n    builtin local chunk_num=0\n    while [ $pos -lt ${#data} ]; do\n        builtin local chunk=\"${data:$pos:2048}\"\n        pos=$(($pos+2048))\n        builtin print -nu \"$_ksi_fd\" -f '\\eP@kitty-%s|%s:%s\\e\\\\' \"${2}\" \"${chunk_num}\" \"${chunk}\"\n        chunk_num=$(($chunk_num+1))\n    done\n    # save history so it is available in new shell\n    [ \"$3\" = \"save_history\" ] && builtin fc -AI\n    builtin print -nu \"$_ksi_fd\" -f '\\eP@kitty-%s|\\e\\\\' \"${2}\"\n}\n\nclone-in-kitty() {\n    builtin local data=\"shell=zsh,pid=$$,cwd=$(builtin printf \"%s\" \"$PWD\" | builtin command base64)\"\n    while :; do\n        case \"$1\" in\n            \"\") break;;\n            -h|--help)\n                builtin printf \"%s\\n\\n%s\\n\" \"Clone the current zsh session into a new kitty window.\" \"For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#clone-shell\"\n                builtin return\n                ;;\n            *) data=\"$data,a=$(builtin printf \"%s\" \"$1\" | builtin command base64)\";;\n        esac\n        shift\n    done\n    builtin local env\n    builtin local env_vars\n    builtin local varname\n    env_vars=(${(f)\"$(builtin export)\"})\n    for i in $env_vars; do\n        varname=\"${i%%=*}\"\n        env=\"${env}$(builtin printf \"%s=%s\\0\" \"$varname\" \"${(P)varname}\")\"\n    done\n    data=\"$data,env=$(builtin printf \"%s\" \"$env\" | builtin command base64)\"\n    _ksi_transmit_data \"$data\" \"clone\" \"save_history\"\n}\n"
  },
  {
    "path": "shell-integration/zsh/kitty.zsh",
    "content": "#!/bin/zsh\n#\n# This file can get sourced with aliases enabled. Moreover, it be sourced from\n# zshrc, so the chance of having some aliases already defined is high. To avoid\n# alias expansion we quote everything that can be quoted. Some aliases will\n# still break us. For example:\n#\n#   alias -g -- -r='$RANDOM'\n#\n# For this reason users are discouraged from sourcing kitty.zsh in favor of\n# invoking kitty-integration directly.\n\n# ${(%):-%x} is the path to the current file.\n# On top of it we add :A:h to get the directory.\n'builtin' 'typeset' _ksi_file=\"${${(%):-%x}:A:h}\"/kitty-integration\nif [[ -r \"$_ksi_file\" ]]; then\n    'builtin' 'autoload' '-Uz' '--' \"$_ksi_file\"\n    \"${_ksi_file:t}\"\n    'builtin' 'unfunction' '--' \"${_ksi_file:t}\"\nfi\n'builtin' 'unset' '_ksi_file'\n"
  },
  {
    "path": "shell.nix",
    "content": "{pkgs ? import <nixpkgs> {}}:\nwith pkgs; let\n  inherit (lib) optional optionals;\n  inherit (xorg) libX11 libXrandr libXinerama libXcursor libXi libXext;\n  inherit (darwin.apple_sdk.frameworks) Cocoa CoreGraphics Foundation IOKit Kernel OpenGL UniformTypeIdentifiers;\n  harfbuzzWithCoreText = harfbuzz.override {withCoreText = stdenv.isDarwin;};\nin\n  with python3Packages;\n    mkShell rec {\n      buildInputs =\n        [\n          harfbuzzWithCoreText\n          ncurses\n          lcms2\n          xxHash\n          simde\n          go_1_24\n          matplotlib\n        ]\n        ++ optionals stdenv.isDarwin [\n          Cocoa\n          CoreGraphics\n          Foundation\n          IOKit\n          Kernel\n          OpenGL\n          UniformTypeIdentifiers\n          libpng\n          zlib\n        ]\n        ++ lib.optionals (stdenv.isDarwin && (builtins.hasAttr \"UserNotifications\" darwin.apple_sdk.frameworks)) [\n          darwin.apple_sdk.frameworks.UserNotifications\n        ]\n        ++ optionals stdenv.isLinux [\n          fontconfig\n          libunistring\n          libcanberra\n          libX11\n          libXrandr\n          libXinerama\n          libXcursor\n          libxkbcommon\n          libXi\n          libXext\n          wayland-protocols\n          wayland\n          openssl\n          dbus\n          cairo #\n        ]\n        ++ lib.optionals stdenv.hostPlatform.isLinux [\n          wayland-scanner\n        ]\n        ++ checkInputs;\n\n      nativeBuildInputs =\n        [\n          ncurses\n          pkg-config\n          sphinx\n          furo\n          sphinx-copybutton\n          sphinxext-opengraph\n          sphinx-inline-tabs\n        ]\n        ++ optionals stdenv.isDarwin [\n          imagemagick\n          libicns # For the png2icns tool.\n        ];\n\n      propagatedBuildInputs = optional stdenv.isLinux libGL;\n\n      checkInputs = [\n        pillow\n      ];\n\n      # Causes build failure due to warning when using Clang\n      hardeningDisable = [\"strictoverflow\"];\n\n      shellHook =\n        if stdenv.isDarwin\n        then ''\n          export KITTY_NO_LTO=\n          # Add fonts by hand\n\n          if [ ! -e ./fonts/SymbolsNerdFontMono-Regular.ttf ]; then\n            mkdir -p ./fonts/\n            cp \"${nerd-fonts.symbols-only}/share/fonts/truetype/NerdFonts/Symbols/SymbolsNerdFontMono-Regular.ttf\" ./fonts/\n          fi\n        ''\n        else ''\n          export KITTY_EGL_LIBRARY='${lib.getLib libGL}/lib/libEGL.so.1'\n          export KITTY_STARTUP_NOTIFICATION_LIBRARY='${libstartup_notification}/lib/libstartup-notification-1.so'\n          export KITTY_CANBERRA_LIBRARY='${libcanberra}/lib/libcanberra.so'\n          export KITTY_FONTCONFIG_LIBRARY='${fontconfig.lib}/lib/libfontconfig.so'\n\n          # Add fonts by hand\n          if [ ! -e ./fonts/SymbolsNerdFontMono-Regular.ttf ]; then\n            mkdir -p ./fonts/\n            cp \"${nerd-fonts.symbols-only}/share/fonts/truetype/NerdFonts/Symbols/SymbolsNerdFontMono-Regular.ttf\" ./fonts/\n          fi\n        '';\n    }\n"
  },
  {
    "path": "staticcheck.conf",
    "content": "checks = [\"all\", \"-ST10006\", \"-ST1012\"]\n"
  },
  {
    "path": "terminfo/kitty.termcap",
    "content": "xterm-kitty|KovIdTTY:5i:NP:am:bw:cc:hs:km:mi:ms:xn:Co#256:co#80:it#8:li#24:pa#32767:#2=\\E[1;2H:#3=\\E[2;2~:#4=\\E[1;2D:%c=\\E[6;2~:%e=\\E[5;2~:%i=\\E[1;2C:&9=\\E[1;2E:*4=\\E[3;2~:*7=\\E[1;2F:@1=\\EOE:@7=\\EOF:AB=\\E[48;5;%dm:AF=\\E[38;5;%dm:AL=\\E[%dL:DC=\\E[%dP:DL=\\E[%dM:DO=\\E[%dB:F1=\\E[23~:F2=\\E[24~:F3=\\E[1;2P:F4=\\E[1;2Q:F5=\\E[13;2~:F6=\\E[1;2S:F7=\\E[15;2~:F8=\\E[17;2~:F9=\\E[18;2~:FA=\\E[19;2~:FB=\\E[20;2~:FC=\\E[21;2~:FD=\\E[23;2~:FE=\\E[24;2~:FF=\\E[1;5P:FG=\\E[1;5Q:FH=\\E[13;5~:FI=\\E[1;5S:FJ=\\E[15;5~:FK=\\E[17;5~:FL=\\E[18;5~:FM=\\E[19;5~:FN=\\E[20;5~:FO=\\E[21;5~:FP=\\E[23;5~:FQ=\\E[24;5~:FR=\\E[1;6P:FS=\\E[1;6Q:FT=\\E[13;6~:FU=\\E[1;6S:FV=\\E[15;6~:FW=\\E[17;6~:FX=\\E[18;6~:FY=\\E[19;6~:FZ=\\E[20;6~:Fa=\\E[21;6~:Fb=\\E[23;6~:Fc=\\E[24;6~:Fd=\\E[1;3P:Fe=\\E[1;3Q:Ff=\\E[13;3~:Fg=\\E[1;3S:Fh=\\E[15;3~:Fi=\\E[17;3~:Fj=\\E[18;3~:Fk=\\E[19;3~:Fl=\\E[20;3~:Fm=\\E[21;3~:Fn=\\E[23;3~:Fo=\\E[24;3~:Fp=\\E[1;4P:Fq=\\E[1;4Q:Fr=\\E[13;4~:IC=\\E[%d@:..Ic=\\E]4;%p1%d;rgb\\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\\E\\\\:Km=\\E[M:LE=\\E[%dD:RA=\\E[?7l:RI=\\E[%dC:SA=\\E[?7h:SF=\\E[%dS:SR=\\E[%dT:UP=\\E[%dA:ZH=\\E[3m:ZR=\\E[23m:ac=++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~:ae=\\E(B:al=\\E[L:as=\\E(0:bl=^G:bt=\\E[Z:cb=\\E[1K:cd=\\E[J:ce=\\E[K:ch=\\E[%i%dG:cl=\\E[H\\E[2J:cm=\\E[%i%d;%dH:cr=\\r:cs=\\E[%i%d;%dr:ct=\\E[3g:cv=\\E[%i%dd:dc=\\E[P:dl=\\E[M:do=\\n:ds=\\E]2;\\E\\\\:ec=\\E[%dX:ei=\\E[4l:fs=^G:ho=\\E[H:im=\\E[4h:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:k5=\\E[15~:k6=\\E[17~:k7=\\E[18~:k8=\\E[19~:k9=\\E[20~:k;=\\E[21~:kB=\\E[Z:kD=\\E[3~:kF=\\E[1;2B:kI=\\E[2~:kN=\\E[6~:kP=\\E[5~:kR=\\E[1;2A:kb=\\177:kd=\\EOB:ke=\\E[?1l:kh=\\EOH:kl=\\EOD:kr=\\EOC:ks=\\E[?1h:ku=\\EOA:le=^H:mb=\\E[5m:md=\\E[1m:me=\\E[0m:mh=\\E[2m:mr=\\E[7m:nd=\\E[C:oc=\\E]104\\007:op=\\E[39;49m:r1=\\E]\\E\\\\\\Ec:rc=\\E8:..rp=%p1%c\\E[%p2%{1}%-%db:..sa=%?%p9%t\\E(0%e\\E(B%;\\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;%?%p5%t;2%;m:sc=\\E7:se=\\E[27m:sf=\\n:so=\\E[7m:sr=\\EM:st=\\EH:ta=^I:te=\\E[?1049l:ti=\\E[?1049h:ts=\\E]2;:u6=\\E[%i%d;%dR:u7=\\E[6n:..u8=\\E[?%[;0123456789]c:u9=\\E[c:ue=\\E[24m:up=\\E[A:us=\\E[4m:vb=\\E[?5h\\E[?5l:ve=\\E[?12h\\E[?25h:vi=\\E[?25l:vs=\\E[?12;25h:"
  },
  {
    "path": "terminfo/kitty.terminfo",
    "content": "xterm-kitty|KovIdTTY,\n\tSu,\n\tTc,\n\tXF,\n\tam,\n\tbw,\n\tccc,\n\tfullkbd,\n\ths,\n\tkm,\n\tmc5i,\n\tmir,\n\tmsgr,\n\tnpc,\n\txenl,\n\tcolors#256,\n\tcols#80,\n\tit#8,\n\tlines#24,\n\tpairs#32767,\n\tBD=\\E[?2004l,\n\tBE=\\E[?2004h,\n\tCr=\\E]112\\007,\n\tCs=\\E]12;%p1%s\\007,\n\tMs=\\E]52;%p1%s;%p2%s\\E\\\\,\n\tPE=\\E[201~,\n\tPS=\\E[200~,\n\tRV=\\E[>c,\n\tSe=\\E[2\\sq,\n\tSetulc=\\E[58:2:%p1%{65536}%/%d:%p1%{256}%/%{255}%&%d:%p1%{255}%&%d%;m,\n\tSmulx=\\E[4:%p1%dm,\n\tSs=\\E[%p1%d\\sq,\n\tSync=\\EP=%p1%ds\\E\\\\,\n\tXR=\\E[>0q,\n\tacsc=++\\,\\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,\n\tbel=^G,\n\tblink=\\E[5m,\n\tbold=\\E[1m,\n\tcbt=\\E[Z,\n\tcivis=\\E[?25l,\n\tclear=\\E[H\\E[2J,\n\tcnorm=\\E[?12h\\E[?25h,\n\tcr=^M,\n\tcsr=\\E[%i%p1%d;%p2%dr,\n\tcub=\\E[%p1%dD,\n\tcub1=^H,\n\tcud=\\E[%p1%dB,\n\tcud1=^J,\n\tcuf=\\E[%p1%dC,\n\tcuf1=\\E[C,\n\tcup=\\E[%i%p1%d;%p2%dH,\n\tcuu=\\E[%p1%dA,\n\tcuu1=\\E[A,\n\tcvvis=\\E[?12;25h,\n\tdch=\\E[%p1%dP,\n\tdch1=\\E[P,\n\tdim=\\E[2m,\n\tdl=\\E[%p1%dM,\n\tdl1=\\E[M,\n\tdsl=\\E]2;\\E\\\\,\n\tech=\\E[%p1%dX,\n\ted=\\E[J,\n\tel=\\E[K,\n\tel1=\\E[1K,\n\tfd=\\E[?1004l,\n\tfe=\\E[?1004h,\n\tflash=\\E[?5h$<100/>\\E[?5l,\n\tfsl=^G,\n\thome=\\E[H,\n\thpa=\\E[%i%p1%dG,\n\tht=^I,\n\thts=\\EH,\n\tich=\\E[%p1%d@,\n\til=\\E[%p1%dL,\n\til1=\\E[L,\n\tind=^J,\n\tindn=\\E[%p1%dS,\n\tinitc=\\E]4;%p1%d;rgb\\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\\E\\\\,\n\tkBEG=\\E[1;2E,\n\tkBEG3=\\E[1;3E,\n\tkBEG4=\\E[1;4E,\n\tkBEG5=\\E[1;5E,\n\tkBEG6=\\E[1;6E,\n\tkBEG7=\\E[1;7E,\n\tkDC=\\E[3;2~,\n\tkDC3=\\E[3;3~,\n\tkDC4=\\E[3;4~,\n\tkDC5=\\E[3;5~,\n\tkDC6=\\E[3;6~,\n\tkDC7=\\E[3;7~,\n\tkDN=\\E[1;2B,\n\tkDN3=\\E[1;3B,\n\tkDN4=\\E[1;4B,\n\tkDN5=\\E[1;5B,\n\tkDN6=\\E[1;6B,\n\tkDN7=\\E[1;7B,\n\tkEND=\\E[1;2F,\n\tkEND3=\\E[1;3F,\n\tkEND4=\\E[1;4F,\n\tkEND5=\\E[1;5F,\n\tkEND6=\\E[1;6F,\n\tkEND7=\\E[1;7F,\n\tkHOM=\\E[1;2H,\n\tkHOM3=\\E[1;3H,\n\tkHOM4=\\E[1;4H,\n\tkHOM5=\\E[1;5H,\n\tkHOM6=\\E[1;6H,\n\tkHOM7=\\E[1;7H,\n\tkIC=\\E[2;2~,\n\tkIC3=\\E[2;3~,\n\tkIC4=\\E[2;4~,\n\tkIC5=\\E[2;5~,\n\tkIC6=\\E[2;6~,\n\tkIC7=\\E[2;7~,\n\tkLFT=\\E[1;2D,\n\tkLFT3=\\E[1;3D,\n\tkLFT4=\\E[1;4D,\n\tkLFT5=\\E[1;5D,\n\tkLFT6=\\E[1;6D,\n\tkLFT7=\\E[1;7D,\n\tkNXT=\\E[6;2~,\n\tkNXT3=\\E[6;3~,\n\tkNXT4=\\E[6;4~,\n\tkNXT5=\\E[6;5~,\n\tkNXT6=\\E[6;6~,\n\tkNXT7=\\E[6;7~,\n\tkPRV=\\E[5;2~,\n\tkPRV3=\\E[5;3~,\n\tkPRV4=\\E[5;4~,\n\tkPRV5=\\E[5;5~,\n\tkPRV6=\\E[5;6~,\n\tkPRV7=\\E[5;7~,\n\tkRIT=\\E[1;2C,\n\tkRIT3=\\E[1;3C,\n\tkRIT4=\\E[1;4C,\n\tkRIT5=\\E[1;5C,\n\tkRIT6=\\E[1;6C,\n\tkRIT7=\\E[1;7C,\n\tkUP=\\E[1;2A,\n\tkUP3=\\E[1;3A,\n\tkUP4=\\E[1;4A,\n\tkUP5=\\E[1;5A,\n\tkUP6=\\E[1;6A,\n\tkUP7=\\E[1;7A,\n\tkbeg=\\EOE,\n\tkbs=\\177,\n\tkcbt=\\E[Z,\n\tkcub1=\\EOD,\n\tkcud1=\\EOB,\n\tkcuf1=\\EOC,\n\tkcuu1=\\EOA,\n\tkdch1=\\E[3~,\n\tkend=\\EOF,\n\tkf1=\\EOP,\n\tkf10=\\E[21~,\n\tkf11=\\E[23~,\n\tkf12=\\E[24~,\n\tkf13=\\E[1;2P,\n\tkf14=\\E[1;2Q,\n\tkf15=\\E[13;2~,\n\tkf16=\\E[1;2S,\n\tkf17=\\E[15;2~,\n\tkf18=\\E[17;2~,\n\tkf19=\\E[18;2~,\n\tkf2=\\EOQ,\n\tkf20=\\E[19;2~,\n\tkf21=\\E[20;2~,\n\tkf22=\\E[21;2~,\n\tkf23=\\E[23;2~,\n\tkf24=\\E[24;2~,\n\tkf25=\\E[1;5P,\n\tkf26=\\E[1;5Q,\n\tkf27=\\E[13;5~,\n\tkf28=\\E[1;5S,\n\tkf29=\\E[15;5~,\n\tkf3=\\EOR,\n\tkf30=\\E[17;5~,\n\tkf31=\\E[18;5~,\n\tkf32=\\E[19;5~,\n\tkf33=\\E[20;5~,\n\tkf34=\\E[21;5~,\n\tkf35=\\E[23;5~,\n\tkf36=\\E[24;5~,\n\tkf37=\\E[1;6P,\n\tkf38=\\E[1;6Q,\n\tkf39=\\E[13;6~,\n\tkf4=\\EOS,\n\tkf40=\\E[1;6S,\n\tkf41=\\E[15;6~,\n\tkf42=\\E[17;6~,\n\tkf43=\\E[18;6~,\n\tkf44=\\E[19;6~,\n\tkf45=\\E[20;6~,\n\tkf46=\\E[21;6~,\n\tkf47=\\E[23;6~,\n\tkf48=\\E[24;6~,\n\tkf49=\\E[1;3P,\n\tkf5=\\E[15~,\n\tkf50=\\E[1;3Q,\n\tkf51=\\E[13;3~,\n\tkf52=\\E[1;3S,\n\tkf53=\\E[15;3~,\n\tkf54=\\E[17;3~,\n\tkf55=\\E[18;3~,\n\tkf56=\\E[19;3~,\n\tkf57=\\E[20;3~,\n\tkf58=\\E[21;3~,\n\tkf59=\\E[23;3~,\n\tkf6=\\E[17~,\n\tkf60=\\E[24;3~,\n\tkf61=\\E[1;4P,\n\tkf62=\\E[1;4Q,\n\tkf63=\\E[13;4~,\n\tkf7=\\E[18~,\n\tkf8=\\E[19~,\n\tkf9=\\E[20~,\n\tkhome=\\EOH,\n\tkich1=\\E[2~,\n\tkind=\\E[1;2B,\n\tkmous=\\E[M,\n\tknp=\\E[6~,\n\tkpp=\\E[5~,\n\tkri=\\E[1;2A,\n\tkxIN=\\E[I,\n\tkxOUT=\\E[O,\n\toc=\\E]104\\007,\n\top=\\E[39;49m,\n\trc=\\E8,\n\trep=%p1%c\\E[%p2%{1}%-%db,\n\trev=\\E[7m,\n\tri=\\EM,\n\trin=\\E[%p1%dT,\n\tritm=\\E[23m,\n\trmacs=\\E(B,\n\trmam=\\E[?7l,\n\trmcup=\\E[?1049l,\n\trmir=\\E[4l,\n\trmkx=\\E[?1l,\n\trmso=\\E[27m,\n\trmul=\\E[24m,\n\trmxx=\\E[29m,\n\trs1=\\E]\\E\\\\\\Ec,\n\tsc=\\E7,\n\tsetab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,\n\tsetaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,\n\tsetrgbb=\\E[48:2:%p1%d:%p2%d:%p3%dm,\n\tsetrgbf=\\E[38:2:%p1%d:%p2%d:%p3%dm,\n\tsgr=%?%p9%t\\E(0%e\\E(B%;\\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;%?%p5%t;2%;m,\n\tsgr0=\\E(B\\E[m,\n\tsitm=\\E[3m,\n\tsmacs=\\E(0,\n\tsmam=\\E[?7h,\n\tsmcup=\\E[?1049h,\n\tsmir=\\E[4h,\n\tsmkx=\\E[?1h,\n\tsmso=\\E[7m,\n\tsmul=\\E[4m,\n\tsmxx=\\E[9m,\n\ttbc=\\E[3g,\n\ttsl=\\E]2;,\n\tu6=\\E[%i%d;%dR,\n\tu7=\\E[6n,\n\tu8=\\E[?%[;0123456789]c,\n\tu9=\\E[c,\n\tvpa=\\E[%i%p1%dd,\n"
  },
  {
    "path": "test.py",
    "content": "#!./kitty/launcher/kitty +launch\n# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport importlib\n\n\ndef main() -> None:\n    m = importlib.import_module('kitty_tests.main')\n    getattr(m, 'main')()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/README.rst",
    "content": "This folder contains library and utility code for the various \"kittens\". Small\nterminal programs compiled statically for doing things like kitty remote\ncontrol, icat etc. These are often re-implementations of earlier kittens that\nwere written in Python.\n"
  },
  {
    "path": "tools/cli/bash.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc bash_completion_script(commands []string) (string, error) {\n\treturn `_ksi_completions() {\n    builtin local src\n    builtin local limit\n    # Send all words up to the word the cursor is currently on\n    builtin let limit=1+$COMP_CWORD\n    src=$(builtin printf \"%s\\n\" \"${COMP_WORDS[@]:0:$limit}\" | builtin command kitten __complete__ bash)\n    if [[ $? == 0 ]]; then\n        builtin eval \"${src}\"\n    fi\n}\n\nbuiltin complete -F _ksi_completions kitty\nbuiltin complete -F _ksi_completions edit-in-kitty\nbuiltin complete -F _ksi_completions clone-in-kitty\nbuiltin complete -F _ksi_completions kitten\n`, nil\n}\n\nfunc bash_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {\n\toutput := strings.Builder{}\n\tf := func(format string, args ...any) { fmt.Fprintf(&output, format+\"\\n\", args...) }\n\tn := completions[0].Delegate.NumToRemove\n\tif n > 0 {\n\t\tf(\"if builtin declare -F _command_offset >/dev/null; then\")\n\t\tf(\"  compopt +o nospace\")\n\t\tf(\"  COMP_WORDS[%d]=%s\", n, utils.QuoteStringForSH(completions[0].Delegate.Command))\n\t\tf(\"  _command_offset %d\", n)\n\t\tf(\"fi\")\n\t} else {\n\t\tfor _, mg := range completions[0].Groups {\n\t\t\tmg.remove_common_prefix()\n\t\t\tif mg.NoTrailingSpace {\n\t\t\t\tf(\"compopt -o nospace\")\n\t\t\t} else {\n\t\t\t\tf(\"compopt +o nospace\")\n\t\t\t}\n\t\t\tif mg.IsFiles {\n\t\t\t\tf(\"compopt -o filenames\")\n\t\t\t\tfor _, m := range mg.Matches {\n\t\t\t\t\tif strings.HasSuffix(m.Word, utils.Sep) {\n\t\t\t\t\t\tm.Word = m.Word[:len(m.Word)-1]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tf(\"compopt +o filenames\")\n\t\t\t}\n\t\t\tfor _, m := range mg.Matches {\n\t\t\t\tf(\"COMPREPLY+=(%s)\", utils.QuoteStringForSH(m.Word))\n\t\t\t}\n\t\t}\n\t}\n\t// debugf(\"%#v\", output.String())\n\treturn []byte(output.String()), nil\n}\n\nfunc bash_init_completions(completions *Completions) {\n\tcompletions.split_on_equals = true\n}\n\nfunc init() {\n\tcompletion_scripts[\"bash\"] = bash_completion_script\n\tinput_parsers[\"bash\"] = shell_input_parser\n\toutput_serializers[\"bash\"] = bash_output_serializer\n\tinit_completions[\"bash\"] = bash_init_completions\n}\n"
  },
  {
    "path": "tools/cli/command.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype RunFunc = func(cmd *Command, args []string) (int, error)\n\ntype Command struct {\n\tName, Group                       string\n\tUsage, ShortDescription, HelpText string\n\tHidden                            bool\n\n\t// Number of non-option arguments after which to stop parsing options. 0 means no options after the first non-option arg.\n\tAllowOptionsAfterArgs int\n\t// If true does not fail if the first non-option arg is not a sub-command\n\tSubCommandIsOptional bool\n\t// If true subcommands are ignored unless they are the first non-option argument\n\tSubCommandMustBeFirst bool\n\t// The entry point for this command\n\tRun RunFunc\n\t// The completer for args\n\tArgCompleter CompletionFunc\n\t// Stop completion processing at this arg num\n\tStopCompletingAtArg int\n\t// Consider all args as non-options args when parsing for completion\n\tOnlyArgsAllowed bool\n\t// Pass through all args, useful for wrapper commands\n\tIgnoreAllArgs bool\n\t// Specialised arg parsing\n\tParseArgsForCompletion func(cmd *Command, args []string, completions *Completions)\n\t// Callback that is called on error\n\tCallbackOnError func(cmd *Command, err error, during_parsing bool, exit_code int) (final_exit_code int)\n\n\tSubCommandGroups []*CommandGroup\n\tOptionGroups     []*OptionGroup\n\tParent           *Command\n\n\tArgs []string\n\n\toption_map      map[string]*Option\n\tIndexOfFirstArg int\n}\n\nfunc (self *Command) Clone(parent *Command) *Command {\n\tans := *self\n\tans.Args = make([]string, 0, 8)\n\tans.Parent = parent\n\tans.SubCommandGroups = make([]*CommandGroup, len(self.SubCommandGroups))\n\tans.OptionGroups = make([]*OptionGroup, len(self.OptionGroups))\n\tans.option_map = nil\n\n\tfor i, o := range self.OptionGroups {\n\t\tans.OptionGroups[i] = o.Clone(&ans)\n\t}\n\tfor i, g := range self.SubCommandGroups {\n\t\tans.SubCommandGroups[i] = g.Clone(&ans)\n\t}\n\treturn &ans\n}\n\nfunc (self *Command) AddClone(group string, src *Command) *Command {\n\tc := src.Clone(self)\n\tg := self.AddSubCommandGroup(group)\n\tc.Group = g.Title\n\tg.SubCommands = append(g.SubCommands, c)\n\treturn c\n}\n\nfunc init_cmd(c *Command) {\n\tc.SubCommandGroups = make([]*CommandGroup, 0, 8)\n\tc.OptionGroups = make([]*OptionGroup, 0, 8)\n\tc.Args = make([]string, 0, 8)\n\tc.option_map = nil\n}\n\nfunc NewRootCommand() *Command {\n\tans := Command{\n\t\tName: filepath.Base(os.Args[0]),\n\t}\n\tinit_cmd(&ans)\n\treturn &ans\n}\n\nfunc (self *Command) AddSubCommandGroup(title string) *CommandGroup {\n\tfor _, g := range self.SubCommandGroups {\n\t\tif g.Title == title {\n\t\t\treturn g\n\t\t}\n\t}\n\tans := CommandGroup{Title: title, SubCommands: make([]*Command, 0, 8)}\n\tself.SubCommandGroups = append(self.SubCommandGroups, &ans)\n\treturn &ans\n}\n\nfunc (self *Command) AddSubCommand(ans *Command) *Command {\n\tg := self.AddSubCommandGroup(ans.Group)\n\tg.SubCommands = append(g.SubCommands, ans)\n\tinit_cmd(ans)\n\tans.Parent = self\n\treturn ans\n}\n\nfunc (self *Command) Validate() error {\n\tseen_sc := make(map[string]bool)\n\tfor _, g := range self.SubCommandGroups {\n\t\tfor _, sc := range g.SubCommands {\n\t\t\tif seen_sc[sc.Name] {\n\t\t\t\treturn &ParseError{Message: fmt.Sprintf(\"The sub-command :yellow:`%s` occurs twice inside %s\", sc.Name, self.Name)}\n\t\t\t}\n\t\t\tseen_sc[sc.Name] = true\n\t\t\terr := sc.Validate()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tseen_flags := make(map[string]bool)\n\n\tself.option_map = make(map[string]*Option, 128)\n\tvalidate_options := func(opt *Option) error {\n\t\tif self.option_map[opt.Name] != nil {\n\t\t\treturn &ParseError{Message: fmt.Sprintf(\"The option :yellow:`%s` occurs twice inside %s\", opt.Name, self.Name)}\n\t\t}\n\t\tfor _, a := range opt.Aliases {\n\t\t\tq := a.String()\n\t\t\tif seen_flags[q] {\n\t\t\t\treturn &ParseError{Message: fmt.Sprintf(\"The option :yellow:`%s` occurs twice inside %s\", q, self.Name)}\n\t\t\t}\n\t\t\tseen_flags[q] = true\n\t\t}\n\t\tself.option_map[opt.Name] = opt\n\t\treturn nil\n\t}\n\terr := self.VisitAllOptions(validate_options)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif self.option_map[\"Help\"] == nil {\n\t\tif seen_flags[\"-h\"] || seen_flags[\"--help\"] {\n\t\t\treturn &ParseError{Message: fmt.Sprintf(\"The --help or -h flags are assigned to an option other than Help in %s\", self.Name)}\n\t\t}\n\t\tself.option_map[\"Help\"] = self.Add(OptionSpec{Name: \"--help -h\", Type: \"bool-set\", Help: \"Show help for this command\"})\n\t}\n\n\tif self.Parent == nil && self.option_map[\"Version\"] == nil {\n\t\tif seen_flags[\"--version\"] {\n\t\t\treturn &ParseError{Message: fmt.Sprintf(\"The --version flag is assigned to an option other than Version in %s\", self.Name)}\n\t\t}\n\t\tself.option_map[\"Version\"] = self.Add(OptionSpec{Name: \"--version\", Type: \"bool-set\", Help: \"Show version\"})\n\t}\n\n\treturn nil\n}\n\nfunc (self *Command) Root() *Command {\n\tp := self\n\tfor p.Parent != nil {\n\t\tp = p.Parent\n\t}\n\treturn p\n}\n\nfunc (self *Command) CommandStringForUsage() string {\n\tnames := make([]string, 0, 8)\n\tp := self\n\tfor p != nil {\n\t\tif p.Name != \"\" {\n\t\t\tnames = append(names, p.Name)\n\t\t}\n\t\tp = p.Parent\n\t}\n\treturn strings.Join(utils.Reverse(names), \" \")\n}\n\nfunc (self *Command) ParseArgs(args []string) (*Command, error) {\n\tfor ; self.Parent != nil; self = self.Parent {\n\t}\n\terr := self.Validate()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif args == nil {\n\t\targs = os.Args\n\t}\n\tif len(args) < 1 {\n\t\treturn nil, &ParseError{Message: \"At least one arg must be supplied\"}\n\t}\n\tctx := Context{SeenCommands: make([]*Command, 0, 4)}\n\terr = self.parse_args(&ctx, args[1:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn ctx.SeenCommands[len(ctx.SeenCommands)-1], nil\n}\n\nfunc (self *Command) ResetAfterParseArgs() {\n\tfor _, g := range self.SubCommandGroups {\n\t\tfor _, sc := range g.SubCommands {\n\t\t\tsc.ResetAfterParseArgs()\n\t\t}\n\t}\n\n\tfor _, g := range self.OptionGroups {\n\t\tfor _, o := range g.Options {\n\t\t\to.reset()\n\t\t}\n\t}\n\tself.option_map = nil\n\tself.IndexOfFirstArg = 0\n\tself.Args = make([]string, 0, 8)\n}\n\nfunc (self *Command) HasSubCommands() bool {\n\tfor _, g := range self.SubCommandGroups {\n\t\tif len(g.SubCommands) > 0 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *Command) HasVisibleSubCommands() bool {\n\tfor _, g := range self.SubCommandGroups {\n\t\tif g.HasVisibleSubCommands() {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *Command) VisitAllOptions(callback func(*Option) error) error {\n\tdepth := 0\n\titer_opts := func(cmd *Command) error {\n\t\tfor _, g := range cmd.OptionGroups {\n\t\t\tfor _, o := range g.Options {\n\t\t\t\tif o.Depth >= depth {\n\t\t\t\t\terr := callback(o)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tfor p := self; p != nil; p = p.Parent {\n\t\terr := iter_opts(p)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdepth++\n\t}\n\treturn nil\n}\n\nfunc (self *Command) AllOptions() []*Option {\n\tans := make([]*Option, 0, 64)\n\t_ = self.VisitAllOptions(func(o *Option) error { ans = append(ans, o); return nil })\n\treturn ans\n}\n\nfunc (self *Command) GetVisibleOptions() ([]string, map[string][]*Option) {\n\tgroup_titles := make([]string, 0, len(self.OptionGroups))\n\tgmap := make(map[string][]*Option)\n\n\tadd_options := func(group_title string, opts []*Option) {\n\t\tif len(opts) == 0 {\n\t\t\treturn\n\t\t}\n\t\tx := gmap[group_title]\n\t\tif x == nil {\n\t\t\tgroup_titles = append(group_titles, group_title)\n\t\t\tgmap[group_title] = opts\n\t\t} else {\n\t\t\tgmap[group_title] = append(x, opts...)\n\t\t}\n\t}\n\n\tdepth := 0\n\tprocess_cmd := func(cmd *Command) {\n\t\tfor _, g := range cmd.OptionGroups {\n\t\t\tgopts := make([]*Option, 0, len(g.Options))\n\t\t\tfor _, o := range g.Options {\n\t\t\t\tif !o.Hidden && o.Depth >= depth {\n\t\t\t\t\tgopts = append(gopts, o)\n\t\t\t\t}\n\t\t\t}\n\t\t\tadd_options(g.Title, gopts)\n\t\t}\n\t}\n\tfor p := self; p != nil; p = p.Parent {\n\t\tprocess_cmd(p)\n\t\tdepth++\n\t}\n\treturn group_titles, gmap\n}\n\nfunc sort_levenshtein_matches(q string, matches []string) {\n\tutils.StableSort(matches, func(a, b string) int {\n\t\tla, lb := utils.LevenshteinDistance(a, q, true), utils.LevenshteinDistance(b, q, true)\n\t\tif la != lb {\n\t\t\treturn la - lb\n\t\t}\n\t\treturn strings.Compare(a, b)\n\t})\n\n}\n\nfunc (self *Command) SuggestionsForCommand(name string, max_distance int /* good default is 2 */) []string {\n\tans := make([]string, 0, 8)\n\tq := strings.ToLower(name)\n\tfor _, g := range self.SubCommandGroups {\n\t\tfor _, sc := range g.SubCommands {\n\t\t\tif utils.LevenshteinDistance(sc.Name, q, true) <= max_distance {\n\t\t\t\tans = append(ans, sc.Name)\n\t\t\t}\n\t\t}\n\t}\n\tsort_levenshtein_matches(q, ans)\n\treturn ans\n}\n\nfunc (self *Command) SuggestionsForOption(name_with_hyphens string, max_distance int /* good default is 2 */) []string {\n\tans := make([]string, 0, 8)\n\tq := strings.ToLower(name_with_hyphens)\n\t_ = self.VisitAllOptions(func(opt *Option) error {\n\t\tfor _, a := range opt.Aliases {\n\t\t\tas := a.String()\n\t\t\tif utils.LevenshteinDistance(as, q, true) <= max_distance {\n\t\t\t\tans = append(ans, as)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tsort_levenshtein_matches(q, ans)\n\treturn ans\n}\n\nfunc (self *Command) FindSubCommand(name string) *Command {\n\tfor _, g := range self.SubCommandGroups {\n\t\tc := g.FindSubCommand(name)\n\t\tif c != nil {\n\t\t\treturn c\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *Command) FindSubCommands(prefix string) []*Command {\n\tc := self.FindSubCommand(prefix)\n\tif c != nil {\n\t\treturn []*Command{c}\n\t}\n\tans := make([]*Command, 0, 4)\n\tfor _, g := range self.SubCommandGroups {\n\t\tans = g.FindSubCommands(prefix, ans)\n\t}\n\treturn ans\n}\n\nfunc (self *Command) AddOptionGroup(title string) *OptionGroup {\n\tfor _, g := range self.OptionGroups {\n\t\tif g.Title == title {\n\t\t\treturn g\n\t\t}\n\t}\n\tans := OptionGroup{Title: title, Options: make([]*Option, 0, 8)}\n\tself.OptionGroups = append(self.OptionGroups, &ans)\n\treturn &ans\n}\n\nfunc (self *Command) AddOptionToGroupFromString(group string, items ...string) *Option {\n\tans, err := self.AddOptionGroup(group).AddOptionFromString(self, items...)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn ans\n\n}\n\nfunc (self *Command) AddToGroup(group string, s OptionSpec) *Option {\n\tans, err := self.AddOptionGroup(group).AddOption(self, s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn ans\n}\n\nfunc (self *Command) AddOptionFromString(items ...string) *Option {\n\treturn self.AddOptionToGroupFromString(\"\", items...)\n}\n\nfunc (self *Command) Add(s OptionSpec) *Option {\n\treturn self.AddToGroup(\"\", s)\n}\n\nfunc (self *Command) FindOptions(name_with_hyphens string) []*Option {\n\tans := make([]*Option, 0, 4)\n\tfor _, g := range self.OptionGroups {\n\t\tx := g.FindOptions(name_with_hyphens)\n\t\tif x != nil {\n\t\t\tans = append(ans, x...)\n\t\t}\n\t}\n\tdepth := 0\n\tfor p := self.Parent; p != nil; p = p.Parent {\n\t\tdepth++\n\t\tfor _, po := range p.FindOptions(name_with_hyphens) {\n\t\t\tif po.Depth >= depth {\n\t\t\t\tans = append(ans, po)\n\t\t\t}\n\t\t}\n\t}\n\treturn ans\n\n}\n\nfunc (self *Command) FindOption(name_with_hyphens string) *Option {\n\tfor _, g := range self.OptionGroups {\n\t\tq := g.FindOption(name_with_hyphens)\n\t\tif q != nil {\n\t\t\treturn q\n\t\t}\n\t}\n\tdepth := 0\n\tfor p := self.Parent; p != nil; p = p.Parent {\n\t\tdepth++\n\t\tq := p.FindOption(name_with_hyphens)\n\t\tif q != nil && q.Depth >= depth {\n\t\t\treturn q\n\t\t}\n\t}\n\treturn nil\n}\n\ntype Context struct {\n\tSeenCommands []*Command\n}\n\nfunc GetOptionValue[T any](self *Command, name string) (ans T, err error) {\n\topt := self.option_map[name]\n\tif opt == nil {\n\t\terr = fmt.Errorf(\"No option with the name: %s\", name)\n\t\treturn\n\t}\n\tans, ok := opt.parsed_value().(T)\n\tif !ok {\n\t\terr = fmt.Errorf(\"The option %s is not of the correct type\", name)\n\t}\n\treturn\n}\n\nfunc (self *Command) OptionsSeenOnCommandLine() map[string]bool {\n\tans := make(map[string]bool)\n\tfor name, opt := range self.option_map {\n\t\tans[name] = opt != nil && len(opt.values_from_cmdline) > 0\n\t}\n\treturn ans\n}\n\nfunc (self *Command) GetOptionValues(pointer_to_options_struct any) error {\n\tval := reflect.ValueOf(pointer_to_options_struct).Elem()\n\tif val.Kind() != reflect.Struct {\n\t\treturn fmt.Errorf(\"Need a pointer to a struct to set option values on\")\n\t}\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tf := val.Field(i)\n\t\tfield_name := val.Type().Field(i).Name\n\t\tif utils.Capitalize(field_name) != field_name || !f.CanSet() {\n\t\t\tcontinue\n\t\t}\n\t\topt := self.option_map[field_name]\n\t\tif opt == nil {\n\t\t\treturn fmt.Errorf(\"No option with the name: %s\", field_name)\n\t\t}\n\t\tswitch opt.OptionType {\n\t\tcase IntegerOption, CountOption:\n\t\t\tif f.Kind() != reflect.Int {\n\t\t\t\treturn fmt.Errorf(\"The field: %s must be an integer\", field_name)\n\t\t\t}\n\t\t\tv := int64(opt.parsed_value().(int))\n\t\t\tif f.OverflowInt(v) {\n\t\t\t\treturn fmt.Errorf(\"The value: %d is too large for the integer type used for the option: %s\", v, field_name)\n\t\t\t}\n\t\t\tf.SetInt(v)\n\t\tcase FloatOption:\n\t\t\tif f.Kind() != reflect.Float64 {\n\t\t\t\treturn fmt.Errorf(\"The field: %s must be a float64\", field_name)\n\t\t\t}\n\t\t\tv := opt.parsed_value().(float64)\n\t\t\tif f.OverflowFloat(v) {\n\t\t\t\treturn fmt.Errorf(\"The value: %f is too large for the integer type used for the option: %s\", v, field_name)\n\t\t\t}\n\t\t\tf.SetFloat(v)\n\t\tcase BoolOption:\n\t\t\tif f.Kind() != reflect.Bool {\n\t\t\t\treturn fmt.Errorf(\"The field: %s must be a boolean\", field_name)\n\t\t\t}\n\t\t\tv := opt.parsed_value().(bool)\n\t\t\tf.SetBool(v)\n\t\tcase StringOption:\n\t\t\tif opt.IsList {\n\t\t\t\tif !is_string_slice(f) {\n\t\t\t\t\treturn fmt.Errorf(\"The field: %s must be a []string\", field_name)\n\t\t\t\t}\n\t\t\t\tv := opt.parsed_value().([]string)\n\t\t\t\tf.Set(reflect.ValueOf(v))\n\t\t\t} else {\n\t\t\t\tif f.Kind() != reflect.String {\n\t\t\t\t\treturn fmt.Errorf(\"The field: %s must be a string\", field_name)\n\t\t\t\t}\n\t\t\t\tv := opt.parsed_value().(string)\n\t\t\t\tf.SetString(v)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *Command) ExecArgs(args []string) (exit_code int) {\n\troot := self\n\tfor root.Parent != nil {\n\t\troot = root.Parent\n\t}\n\tcmd, err := root.ParseArgs(args)\n\tif err != nil {\n\t\tif self.CallbackOnError != nil {\n\t\t\treturn self.CallbackOnError(cmd, err, true, 1)\n\t\t}\n\t\tShowError(err)\n\t\treturn 1\n\t}\n\thelp_opt := cmd.option_map[\"Help\"]\n\tversion_opt := root.option_map[\"Version\"]\n\tif help_opt != nil && help_opt.parsed_value().(bool) {\n\t\tcmd.ShowHelp()\n\t\treturn\n\t} else if version_opt != nil && version_opt.parsed_value().(bool) {\n\t\troot.ShowVersion()\n\t\treturn\n\t} else if cmd.Run != nil {\n\t\texit_code, err = cmd.Run(cmd, cmd.Args)\n\t\tif err != nil {\n\t\t\tif exit_code == 0 {\n\t\t\t\texit_code = 1\n\t\t\t}\n\t\t\tif self.CallbackOnError != nil {\n\t\t\t\treturn self.CallbackOnError(cmd, err, false, exit_code)\n\t\t\t}\n\t\t\tShowError(err)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Command) GetCompletions(argv []string, init_completions func(*Completions)) *Completions {\n\tans := NewCompletions()\n\tif init_completions != nil {\n\t\tinit_completions(ans)\n\t}\n\tif len(argv) > 0 {\n\t\texe := argv[0]\n\t\texe = filepath.Base(exe) // zsh completion script passes full path to exe when using aliases\n\t\tcmd := self.FindSubCommand(exe)\n\t\tif cmd != nil {\n\t\t\tif cmd.ParseArgsForCompletion != nil {\n\t\t\t\tcmd.ParseArgsForCompletion(cmd, argv[1:], ans)\n\t\t\t} else {\n\t\t\t\tcompletion_parse_args(cmd, argv[1:], ans)\n\t\t\t}\n\t\t}\n\t}\n\tnon_empty_groups := make([]*MatchGroup, 0, len(ans.Groups))\n\tfor _, gr := range ans.Groups {\n\t\tif len(gr.Matches) > 0 {\n\t\t\tnon_empty_groups = append(non_empty_groups, gr)\n\t\t}\n\t}\n\tans.Groups = non_empty_groups\n\treturn ans\n}\n"
  },
  {
    "path": "tools/cli/completion-main.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc json_input_parser(data []byte, shell_state map[string]string) ([][]string, error) {\n\tans := make([][]string, 0, 32)\n\terr := json.Unmarshal(data, &ans)\n\treturn ans, err\n}\n\nfunc json_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {\n\treturn json.Marshal(completions)\n}\n\ntype completion_script_func func(commands []string) (string, error)\ntype parser_func func(data []byte, shell_state map[string]string) ([][]string, error)\ntype serializer_func func(completions []*Completions, shell_state map[string]string) ([]byte, error)\n\nvar completion_scripts = make(map[string]completion_script_func, 4)\nvar input_parsers = make(map[string]parser_func, 4)\nvar output_serializers = make(map[string]serializer_func, 4)\nvar init_completions = make(map[string]func(*Completions), 4)\n\nfunc init() {\n\tinput_parsers[\"json\"] = json_input_parser\n\toutput_serializers[\"json\"] = json_output_serializer\n}\n\nvar registered_exes []func(root *Command)\n\nfunc RegisterExeForCompletion(x func(root *Command)) {\n\tif registered_exes == nil {\n\t\tregistered_exes = make([]func(root *Command), 0, 4)\n\t}\n\tregistered_exes = append(registered_exes, x)\n}\n\nfunc GenerateCompletions(args []string) error {\n\toutput_type := \"json\"\n\tif len(args) > 0 {\n\t\toutput_type = args[0]\n\t\targs = args[1:]\n\t}\n\tn := max(1, len(args))\n\tif output_type == \"setup\" {\n\t\tif len(args) == 0 {\n\t\t\treturn fmt.Errorf(\"The shell must be specified\")\n\t\t}\n\t\tshell_name := args[0]\n\t\targs = args[1:]\n\t\tcompletion_script := completion_scripts[shell_name]\n\t\tif completion_script == nil {\n\t\t\treturn fmt.Errorf(\"Unsupported shell: %s\", shell_name)\n\t\t}\n\t\toutput, err := completion_script(args)\n\t\tif err == nil {\n\t\t\t_, err = os.Stdout.WriteString(output)\n\t\t}\n\t\treturn err\n\t}\n\tshell_state := make(map[string]string, n)\n\tfor _, arg := range args {\n\t\tk, v, found := strings.Cut(arg, \"=\")\n\t\tif !found {\n\t\t\treturn fmt.Errorf(\"Invalid shell state specification: %s\", arg)\n\t\t}\n\t\tshell_state[k] = v\n\t}\n\tinput_parser := input_parsers[output_type]\n\toutput_serializer := output_serializers[output_type]\n\tif input_parser == nil || output_serializer == nil {\n\t\treturn fmt.Errorf(\"Unknown output type: %s\", output_type)\n\t}\n\tdata, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// debugf(\"%#v\", string(data))\n\tall_argv, err := input_parser(data, shell_state)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar root = NewRootCommand()\n\tfor _, re := range registered_exes {\n\t\tre(root)\n\t}\n\n\terr = root.Validate()\n\tif err != nil {\n\t\treturn err\n\t}\n\tall_completions := make([]*Completions, 0, 1)\n\tfor _, argv := range all_argv {\n\t\tall_completions = append(all_completions, root.GetCompletions(argv, init_completions[output_type]))\n\t\troot.ResetAfterParseArgs()\n\t}\n\toutput, err := output_serializer(all_completions, shell_state)\n\tif err == nil {\n\t\t_, err = os.Stdout.Write(output)\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "tools/cli/completion-parse-args.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\nvar _ = os.Getenv\n\nfunc (self *Completions) add_options_group(options []*Option, word string) {\n\tgroup := self.AddMatchGroup(\"Options\")\n\tif strings.HasPrefix(word, \"--\") {\n\t\tif word == \"--\" {\n\t\t\tgroup.Matches = append(group.Matches, &Match{Word: \"--\", Description: \"End of options\"})\n\t\t}\n\t\tfor _, opt := range options {\n\t\t\tfor _, q := range opt.Aliases {\n\t\t\t\tif strings.HasPrefix(q.String(), word) {\n\t\t\t\t\tgroup.Matches = append(group.Matches, &Match{Word: q.String(), Description: opt.Help})\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif word == \"-\" {\n\t\t\tgroup.Matches = append(group.Matches, &Match{Word: \"--\", Description: \"End of options\"})\n\t\t\tfor _, opt := range options {\n\t\t\t\thas_single_letter_alias := false\n\t\t\t\tfor _, q := range opt.Aliases {\n\t\t\t\t\tif q.IsShort {\n\t\t\t\t\t\tgroup.AddMatch(\"-\"+q.NameWithoutHyphens, opt.Help)\n\t\t\t\t\t\thas_single_letter_alias = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !has_single_letter_alias {\n\t\t\t\t\tfor _, q := range opt.Aliases {\n\t\t\t\t\t\tif !q.IsShort {\n\t\t\t\t\t\t\tgroup.AddMatch(q.String(), opt.Help)\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\trunes := []rune(word)\n\t\t\tlast_letter := string(runes[len(runes)-1])\n\t\t\tfor _, opt := range options {\n\t\t\t\tfor _, q := range opt.Aliases {\n\t\t\t\t\tif q.IsShort && q.NameWithoutHyphens == last_letter {\n\t\t\t\t\t\tgroup.AddMatch(word, opt.Help)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (self *Command) sub_command_allowed_at(completions *Completions, arg_num int) bool {\n\tif self.SubCommandMustBeFirst {\n\t\treturn arg_num == 1 && completions.CurrentWordIdxInParent == 1\n\t}\n\treturn arg_num == 1\n}\n\nfunc complete_word(word string, completions *Completions, only_args_allowed bool, expecting_arg_for *Option, arg_num int) {\n\tcmd := completions.CurrentCmd\n\tif expecting_arg_for != nil {\n\t\tif expecting_arg_for.Completer != nil {\n\t\t\texpecting_arg_for.Completer(completions, word, arg_num)\n\t\t}\n\t\treturn\n\t}\n\tif !only_args_allowed && strings.HasPrefix(word, \"-\") {\n\t\tif strings.HasPrefix(word, \"--\") && strings.Contains(word, \"=\") {\n\t\t\tidx := strings.Index(word, \"=\")\n\t\t\toption := cmd.FindOption(word[:idx])\n\t\t\tif option != nil {\n\t\t\t\tif option.Completer != nil {\n\t\t\t\t\toption.Completer(completions, word[idx+1:], arg_num)\n\t\t\t\t\tcompletions.AddPrefixToAllMatches(word[:idx+1])\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tcompletions.add_options_group(cmd.AllOptions(), word)\n\t\t}\n\t\treturn\n\t}\n\tif cmd.HasVisibleSubCommands() && cmd.sub_command_allowed_at(completions, arg_num) {\n\t\tfor _, cg := range cmd.SubCommandGroups {\n\t\t\tgroup := completions.AddMatchGroup(cg.Title)\n\t\t\tif group.Title == \"\" {\n\t\t\t\tgroup.Title = \"Sub-commands\"\n\t\t\t}\n\t\t\tfor _, sc := range cg.SubCommands {\n\t\t\t\tif !sc.Hidden && strings.HasPrefix(sc.Name, word) {\n\t\t\t\t\tt := sc.ShortDescription\n\t\t\t\t\tif t == \"\" {\n\t\t\t\t\t\tt = sc.HelpText\n\t\t\t\t\t}\n\t\t\t\t\tgroup.AddMatch(sc.Name, t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif cmd.SubCommandIsOptional && cmd.ArgCompleter != nil {\n\t\t\tcmd.ArgCompleter(completions, word, arg_num)\n\t\t}\n\t\treturn\n\t}\n\n\tif cmd.ArgCompleter != nil {\n\t\tcmd.ArgCompleter(completions, word, arg_num)\n\t}\n}\n\nfunc completion_parse_args(cmd *Command, words []string, completions *Completions) {\n\tcompletions.CurrentCmd = cmd\n\tif len(words) == 0 {\n\t\tcomplete_word(\"\", completions, false, nil, 0)\n\t\treturn\n\t}\n\tcompletions.AllWords = words\n\n\tvar expecting_arg_for *Option\n\tonly_args_allowed := false\n\targ_num := 0\n\n\tfor i, word := range words {\n\t\tcmd = completions.CurrentCmd\n\t\tcompletions.CurrentWordIdx = i\n\t\tcompletions.CurrentWordIdxInParent++\n\t\tis_last_word := i == len(words)-1\n\t\tis_option_equal := completions.split_on_equals && word == \"=\" && expecting_arg_for != nil\n\t\tif only_args_allowed || (expecting_arg_for == nil && !strings.HasPrefix(word, \"-\")) {\n\t\t\tif !is_option_equal {\n\t\t\t\targ_num++\n\t\t\t}\n\t\t\tif arg_num == 1 {\n\t\t\t\tcmd.IndexOfFirstArg = completions.CurrentWordIdx\n\t\t\t}\n\t\t}\n\t\tif is_last_word {\n\t\t\tif is_option_equal {\n\t\t\t\tword = \"\"\n\t\t\t}\n\t\t\tcomplete_word(word, completions, only_args_allowed, expecting_arg_for, arg_num)\n\t\t} else {\n\t\t\tif expecting_arg_for != nil {\n\t\t\t\tif is_option_equal {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\texpecting_arg_for = nil\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif word == \"--\" {\n\t\t\t\tonly_args_allowed = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !only_args_allowed && strings.HasPrefix(word, \"-\") {\n\t\t\t\tif !strings.Contains(word, \"=\") {\n\t\t\t\t\toption := cmd.FindOption(word)\n\t\t\t\t\tif option != nil && option.needs_argument() {\n\t\t\t\t\t\texpecting_arg_for = option\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif cmd.HasVisibleSubCommands() && cmd.sub_command_allowed_at(completions, arg_num) {\n\t\t\t\tsc := cmd.FindSubCommand(word)\n\t\t\t\tif sc == nil {\n\t\t\t\t\tonly_args_allowed = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcompletions.CurrentCmd = sc\n\t\t\t\tcmd = sc\n\t\t\t\targ_num = 0\n\t\t\t\tcompletions.CurrentWordIdxInParent = 0\n\t\t\t\tonly_args_allowed = cmd.OnlyArgsAllowed\n\t\t\t\tif cmd.ParseArgsForCompletion != nil {\n\t\t\t\t\tcmd.ParseArgsForCompletion(cmd, words[i+1:], completions)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else if cmd.StopCompletingAtArg > 0 && arg_num >= cmd.StopCompletingAtArg {\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\tonly_args_allowed = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/cli/completion.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype Match struct {\n\tWord        string `json:\"word,omitempty\"`\n\tDescription string `json:\"description,omitempty\"`\n}\n\ntype MatchGroup struct {\n\tTitle           string   `json:\"title,omitempty\"`\n\tNoTrailingSpace bool     `json:\"no_trailing_space,omitempty\"`\n\tIsFiles         bool     `json:\"is_files,omitempty\"`\n\tMatches         []*Match `json:\"matches,omitempty\"`\n}\n\nfunc (self *MatchGroup) remove_common_prefix() string {\n\tif self.IsFiles {\n\t\tif len(self.Matches) > 1 {\n\t\t\tlcp := self.longest_common_prefix()\n\t\t\tif strings.Contains(lcp, utils.Sep) {\n\t\t\t\tlcp = strings.TrimRight(filepath.Dir(lcp), utils.Sep) + utils.Sep\n\t\t\t\tself.remove_prefix_from_all_matches(lcp)\n\t\t\t\treturn lcp\n\t\t\t}\n\t\t}\n\t} else if len(self.Matches) > 1 && strings.HasPrefix(self.Matches[0].Word, \"--\") && strings.Contains(self.Matches[0].Word, \"=\") {\n\t\tlcp, _, _ := strings.Cut(self.longest_common_prefix(), \"=\")\n\t\tlcp += \"=\"\n\t\tif len(lcp) > 3 {\n\t\t\tself.remove_prefix_from_all_matches(lcp)\n\t\t\treturn lcp\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc (self *MatchGroup) AddMatch(word string, description ...string) *Match {\n\tans := Match{Word: word, Description: strings.Join(description, \" \")}\n\tself.Matches = append(self.Matches, &ans)\n\treturn &ans\n}\n\nfunc (self *MatchGroup) AddPrefixToAllMatches(prefix string) {\n\tfor _, m := range self.Matches {\n\t\tm.Word = prefix + m.Word\n\t}\n}\n\nfunc (self *MatchGroup) remove_prefix_from_all_matches(prefix string) {\n\tfor _, m := range self.Matches {\n\t\tm.Word = m.Word[len(prefix):]\n\t}\n}\n\nfunc (self *MatchGroup) has_descriptions() bool {\n\tfor _, m := range self.Matches {\n\t\tif m.Description != \"\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *MatchGroup) max_visual_word_length(limit int) int {\n\tans := 0\n\tfor _, m := range self.Matches {\n\t\tif q := wcswidth.Stringwidth(m.Word); q > ans {\n\t\t\tans = q\n\t\t\tif ans > limit {\n\t\t\t\treturn limit\n\t\t\t}\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc (self *MatchGroup) longest_common_prefix() string {\n\tlimit := len(self.Matches)\n\ti := 0\n\treturn utils.LongestCommon(func() (string, bool) {\n\t\tif i < limit {\n\t\t\ti++\n\t\t\treturn self.Matches[i-1].Word, false\n\t\t}\n\t\treturn \"\", true\n\t}, true)\n}\n\ntype Delegate struct {\n\tNumToRemove int    `json:\"num_to_remove,omitempty\"`\n\tCommand     string `json:\"command,omitempty\"`\n}\n\ntype Completions struct {\n\tGroups   []*MatchGroup `json:\"groups,omitempty\"`\n\tDelegate Delegate      `json:\"delegate\"`\n\n\tCurrentCmd             *Command `json:\"-\"`\n\tAllWords               []string `json:\"-\"` // all words passed to parse_args()\n\tCurrentWordIdx         int      `json:\"-\"` // index of current word in all_words\n\tCurrentWordIdxInParent int      `json:\"-\"` // index of current word in parents command line 1 for first word after parent\n\n\tsplit_on_equals bool // true if the cmdline is split on = (BASH does this because readline does this)\n}\n\nfunc NewCompletions() *Completions {\n\treturn &Completions{Groups: make([]*MatchGroup, 0, 4)}\n}\n\nfunc (self *Completions) AddPrefixToAllMatches(prefix string) {\n\tfor _, mg := range self.Groups {\n\t\tmg.AddPrefixToAllMatches(prefix)\n\t}\n}\n\nfunc (self *Completions) MergeMatchGroup(mg *MatchGroup) {\n\tif len(mg.Matches) == 0 {\n\t\treturn\n\t}\n\tvar dest *MatchGroup\n\tfor _, q := range self.Groups {\n\t\tif q.Title == mg.Title {\n\t\t\tdest = q\n\t\t\tbreak\n\t\t}\n\t}\n\tif dest == nil {\n\t\tdest = self.AddMatchGroup(mg.Title)\n\t\tdest.NoTrailingSpace = mg.NoTrailingSpace\n\t\tdest.IsFiles = mg.IsFiles\n\t}\n\tseen := utils.NewSet[string](64)\n\tfor _, q := range self.Groups {\n\t\tfor _, m := range q.Matches {\n\t\t\tseen.Add(m.Word)\n\t\t}\n\t}\n\tfor _, m := range mg.Matches {\n\t\tif !seen.Has(m.Word) {\n\t\t\tseen.Add(m.Word)\n\t\t\tdest.Matches = append(dest.Matches, m)\n\t\t}\n\t}\n}\n\nfunc (self *Completions) AddMatchGroup(title string) *MatchGroup {\n\tfor _, q := range self.Groups {\n\t\tif q.Title == title {\n\t\t\treturn q\n\t\t}\n\t}\n\tans := MatchGroup{Title: title, Matches: make([]*Match, 0, 8)}\n\tself.Groups = append(self.Groups, &ans)\n\treturn &ans\n}\n\ntype CompletionFunc = func(completions *Completions, word string, arg_num int)\n\nfunc NamesCompleter(title string, names ...string) CompletionFunc {\n\treturn func(completions *Completions, word string, arg_num int) {\n\t\tmg := completions.AddMatchGroup(title)\n\t\tfor _, q := range names {\n\t\t\tif strings.HasPrefix(q, word) {\n\t\t\t\tmg.AddMatch(q)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc ChainCompleters(completers ...CompletionFunc) CompletionFunc {\n\treturn func(completions *Completions, word string, arg_num int) {\n\t\tfor _, f := range completers {\n\t\t\tf(completions, word, arg_num)\n\t\t}\n\t}\n}\n\nfunc CompletionForWrapper(wrapped_cmd string) func(completions *Completions, word string, arg_num int) {\n\treturn func(completions *Completions, word string, arg_num int) {\n\t\tcompletions.Delegate.NumToRemove = completions.CurrentWordIdx\n\t\tcompletions.Delegate.Command = wrapped_cmd\n\t}\n}\n"
  },
  {
    "path": "tools/cli/files.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"mime\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc absolutize_path(path string) string {\n\tpath = utils.Expanduser(path)\n\tq, err := filepath.Abs(path)\n\tif err == nil {\n\t\tpath = q\n\t}\n\treturn path\n}\n\ntype FileEntry struct {\n\tName, CompletionCandidate, Abspath string\n\tMode                               os.FileMode\n\tIsDir, IsSymlink, IsEmptyDir       bool\n}\n\nfunc CompleteFiles(prefix string, callback func(*FileEntry), cwd string) error {\n\tif cwd == \"\" {\n\t\tvar err error\n\t\tcwd, err = os.Getwd()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tlocation := absolutize_path(prefix)\n\tbase_dir := \"\"\n\tjoinable_prefix := \"\"\n\tswitch prefix {\n\tcase \".\":\n\t\tbase_dir = \".\"\n\t\tjoinable_prefix = \"\"\n\tcase \"./\":\n\t\tbase_dir = \".\"\n\t\tjoinable_prefix = \"./\"\n\tcase \"/\":\n\t\tbase_dir = \"/\"\n\t\tjoinable_prefix = \"/\"\n\tcase \"~\":\n\t\tbase_dir = location\n\t\tjoinable_prefix = \"~/\"\n\tcase \"\":\n\t\tbase_dir = cwd\n\t\tjoinable_prefix = \"\"\n\tdefault:\n\t\tif strings.HasSuffix(prefix, utils.Sep) {\n\t\t\tbase_dir = location\n\t\t\tjoinable_prefix = prefix\n\t\t} else {\n\t\t\tidx := strings.LastIndex(prefix, utils.Sep)\n\t\t\tif idx > 0 {\n\t\t\t\tjoinable_prefix = prefix[:idx+1]\n\t\t\t\tbase_dir = filepath.Dir(location)\n\t\t\t}\n\t\t}\n\t}\n\tif base_dir == \"\" {\n\t\tbase_dir = cwd\n\t}\n\tif !strings.HasPrefix(base_dir, \"~\") && !filepath.IsAbs(base_dir) {\n\t\tbase_dir = filepath.Join(cwd, base_dir)\n\t}\n\t// fmt.Printf(\"prefix=%#v base_dir=%#v joinable_prefix=%#v\\n\", prefix, base_dir, joinable_prefix)\n\tentries, err := os.ReadDir(base_dir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, entry := range entries {\n\t\tq := joinable_prefix + entry.Name()\n\t\tif !strings.HasPrefix(q, prefix) {\n\t\t\tcontinue\n\t\t}\n\t\tabspath := filepath.Join(base_dir, entry.Name())\n\t\tdir_to_check := \"\"\n\t\tdata := FileEntry{\n\t\t\tName: entry.Name(), Abspath: abspath, Mode: entry.Type(), IsDir: entry.IsDir(),\n\t\t\tIsSymlink: entry.Type()&os.ModeSymlink == os.ModeSymlink, CompletionCandidate: q}\n\t\tif data.IsSymlink {\n\t\t\ttarget, err := filepath.EvalSymlinks(abspath)\n\t\t\tif err == nil && target != base_dir {\n\t\t\t\ttd, err := os.Stat(target)\n\t\t\t\tif err == nil && td.IsDir() {\n\t\t\t\t\tdir_to_check = target\n\t\t\t\t\tdata.IsDir = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif dir_to_check != \"\" {\n\t\t\tsubentries, err := os.ReadDir(dir_to_check)\n\t\t\tdata.IsEmptyDir = err != nil || len(subentries) == 0\n\t\t}\n\t\tif data.IsDir {\n\t\t\tdata.CompletionCandidate += utils.Sep\n\t\t}\n\t\tcallback(&data)\n\t}\n\treturn nil\n}\n\nfunc CompleteExecutablesInPath(prefix string, paths ...string) []string {\n\tans := make([]string, 0, 1024)\n\tif len(paths) == 0 {\n\t\tpaths = filepath.SplitList(os.Getenv(\"PATH\"))\n\t}\n\tfor _, dir := range paths {\n\t\tentries, err := os.ReadDir(dir)\n\t\tif err == nil {\n\t\t\tfor _, e := range entries {\n\t\t\t\tif strings.HasPrefix(e.Name(), prefix) && !e.IsDir() && unix.Access(filepath.Join(dir, e.Name()), unix.X_OK) == nil {\n\t\t\t\t\tans = append(ans, e.Name())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc is_dir_or_symlink_to_dir(entry os.DirEntry, path string) bool {\n\tif entry.IsDir() {\n\t\treturn true\n\t}\n\tif entry.Type()&os.ModeSymlink == os.ModeSymlink {\n\t\tp, err := filepath.EvalSymlinks(path)\n\t\tif err == nil {\n\t\t\ts, err := os.Stat(p)\n\t\t\tif err == nil && s.IsDir() {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc fname_based_completer(prefix, cwd string, is_match func(string) bool) []string {\n\tans := make([]string, 0, 1024)\n\t_ = CompleteFiles(prefix, func(entry *FileEntry) {\n\t\tif entry.IsDir && !entry.IsEmptyDir {\n\t\t\tentries, err := os.ReadDir(entry.Abspath)\n\t\t\tif err == nil {\n\t\t\t\tfor _, e := range entries {\n\t\t\t\t\tif is_match(e.Name()) || is_dir_or_symlink_to_dir(e, filepath.Join(entry.Abspath, e.Name())) {\n\t\t\t\t\t\tans = append(ans, entry.CompletionCandidate)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tq := strings.ToLower(entry.Name)\n\t\tif is_match(q) {\n\t\t\tans = append(ans, entry.CompletionCandidate)\n\t\t}\n\t}, cwd)\n\treturn ans\n\n}\n\nfunc complete_by_fnmatch(prefix, cwd string, patterns []string) []string {\n\treturn fname_based_completer(prefix, cwd, func(name string) bool {\n\t\tfor _, pat := range patterns {\n\t\t\tmatched, err := filepath.Match(pat, name)\n\t\t\tif err == nil && matched {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\nfunc complete_by_mimepat(prefix, cwd string, patterns []string) []string {\n\tall_allowed := slices.Contains(patterns, \"*\")\n\treturn fname_based_completer(prefix, cwd, func(name string) bool {\n\t\tif all_allowed {\n\t\t\treturn true\n\t\t}\n\t\tidx := strings.Index(name, \".\")\n\t\tif idx < 1 {\n\t\t\treturn false\n\t\t}\n\t\text := name[idx:]\n\t\tmt := mime.TypeByExtension(ext)\n\t\tif mt == \"\" {\n\t\t\text = filepath.Ext(name)\n\t\t\tmt = mime.TypeByExtension(ext)\n\t\t}\n\t\tif mt == \"\" {\n\t\t\treturn false\n\t\t}\n\t\tfor _, pat := range patterns {\n\t\t\tmatched, err := filepath.Match(pat, mt)\n\t\t\tif err == nil && matched {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\ntype relative_to int\n\nconst (\n\tCWD relative_to = iota\n\tCONFIG\n)\n\nfunc get_cwd_for_completion(relative_to relative_to) string {\n\tswitch relative_to {\n\tcase CONFIG:\n\t\treturn utils.ConfigDir()\n\t}\n\treturn \"\"\n}\n\nfunc make_completer(title string, relative_to relative_to, patterns []string, f func(string, string, []string) []string) CompletionFunc {\n\tlpats := make([]string, 0, len(patterns))\n\tfor _, p := range patterns {\n\t\tlpats = append(lpats, strings.ToLower(p))\n\t}\n\tcwd := get_cwd_for_completion(relative_to)\n\n\treturn func(completions *Completions, word string, arg_num int) {\n\t\tq := f(word, cwd, lpats)\n\t\tif len(q) > 0 {\n\t\t\tdirs, files := make([]string, 0, len(q)), make([]string, 0, len(q))\n\t\t\tfor _, x := range q {\n\t\t\t\tif strings.HasSuffix(x, \"/\") {\n\t\t\t\t\tdirs = append(dirs, x)\n\t\t\t\t} else {\n\t\t\t\t\tfiles = append(files, x)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(dirs) > 0 {\n\t\t\t\tmg := completions.AddMatchGroup(\"Directories\")\n\t\t\t\tmg.IsFiles = true\n\t\t\t\tmg.NoTrailingSpace = true\n\t\t\t\tfor _, c := range dirs {\n\t\t\t\t\tmg.AddMatch(c)\n\t\t\t\t}\n\t\t\t}\n\t\t\tmg := completions.AddMatchGroup(title)\n\t\t\tmg.IsFiles = true\n\t\t\tfor _, c := range files {\n\t\t\t\tmg.AddMatch(c)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc CompleteExecutableFirstArg(completions *Completions, word string, arg_num int) {\n\tif arg_num > 1 {\n\t\tcompletions.Delegate.NumToRemove = completions.CurrentCmd.IndexOfFirstArg + 1 // +1 because the first word is not present in all_words\n\t\tcompletions.Delegate.Command = completions.AllWords[completions.CurrentCmd.IndexOfFirstArg]\n\t\treturn\n\t}\n\texes := CompleteExecutablesInPath(word)\n\tif len(exes) > 0 {\n\t\tmg := completions.AddMatchGroup(\"Executables in PATH\")\n\t\tfor _, exe := range exes {\n\t\t\tmg.AddMatch(exe)\n\t\t}\n\t}\n\n\tif len(word) > 0 {\n\t\tmg := completions.AddMatchGroup(\"Executables\")\n\t\tmg.IsFiles = true\n\n\t\t_ = CompleteFiles(word, func(entry *FileEntry) {\n\t\t\tif entry.IsDir && !entry.IsEmptyDir {\n\t\t\t\t// only allow directories that have sub-dirs or executable files in them\n\t\t\t\tentries, err := os.ReadDir(entry.Abspath)\n\t\t\t\tif err == nil {\n\t\t\t\t\tfor _, x := range entries {\n\t\t\t\t\t\tif x.IsDir() || unix.Access(filepath.Join(entry.Abspath, x.Name()), unix.X_OK) == nil {\n\t\t\t\t\t\t\tmg.AddMatch(entry.CompletionCandidate)\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if unix.Access(entry.Abspath, unix.X_OK) == nil {\n\t\t\t\tmg.AddMatch(entry.CompletionCandidate)\n\t\t\t}\n\t\t}, \"\")\n\t}\n}\n\nfunc FnmatchCompleter(title string, relative_to relative_to, patterns ...string) CompletionFunc {\n\treturn make_completer(title, relative_to, patterns, complete_by_fnmatch)\n}\n\nfunc MimepatCompleter(title string, relative_to relative_to, patterns ...string) CompletionFunc {\n\treturn make_completer(title, relative_to, patterns, complete_by_mimepat)\n}\n\nfunc DirectoryCompleter(title string, relative_to relative_to) CompletionFunc {\n\tif title == \"\" {\n\t\ttitle = \"Directories\"\n\t}\n\tcwd := get_cwd_for_completion(relative_to)\n\n\treturn func(completions *Completions, word string, arg_num int) {\n\t\tmg := completions.AddMatchGroup(title)\n\t\tmg.NoTrailingSpace = true\n\t\tmg.IsFiles = true\n\t\t_ = CompleteFiles(word, func(entry *FileEntry) {\n\t\t\tif entry.Mode.IsDir() {\n\t\t\t\tmg.AddMatch(entry.CompletionCandidate)\n\t\t\t}\n\t\t}, cwd)\n\t}\n}\n"
  },
  {
    "path": "tools/cli/files_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestCompleteFiles(t *testing.T) {\n\ttdir := t.TempDir()\n\tcwd, _ := os.Getwd()\n\tif cwd != \"\" {\n\t\tdefer os.Chdir(cwd)\n\t}\n\tos.Chdir(tdir)\n\n\tcreate := func(parts ...string) {\n\t\tf, _ := os.Create(filepath.Join(tdir, filepath.Join(parts...)))\n\t\tf.Close()\n\t}\n\tcreate(\"one.txt\")\n\tcreate(\"two.txt\")\n\tos.Mkdir(filepath.Join(tdir, \"odir\"), 0700)\n\tcreate(\"odir\", \"three.txt\")\n\tcreate(\"odir\", \"four.txt\")\n\n\ttest_candidates := func(prefix string, expected ...string) {\n\t\tif expected == nil {\n\t\t\texpected = make([]string, 0)\n\t\t}\n\t\tsort.Strings(expected)\n\t\tactual := make([]string, 0, len(expected))\n\t\tCompleteFiles(prefix, func(entry *FileEntry) {\n\t\t\tactual = append(actual, entry.CompletionCandidate)\n\t\t\tif _, err := os.Stat(entry.Abspath); err != nil {\n\t\t\t\tt.Fatalf(\"Abspath does not exist: %#v\", entry.Abspath)\n\t\t\t}\n\t\t}, \"\")\n\t\tsort.Strings(actual)\n\t\tif !reflect.DeepEqual(expected, actual) {\n\t\t\tt.Fatalf(\"Did not get expected completion candidates for prefix: %#v\\nExpected: %#v\\nActual:   %#v\", prefix, expected, actual)\n\t\t}\n\t}\n\n\ttest_abs_candidates := func(prefix string, expected ...string) {\n\t\te := make([]string, len(expected))\n\t\tfor i, x := range expected {\n\t\t\tif filepath.IsAbs(x) {\n\t\t\t\te[i] = x\n\t\t\t} else {\n\t\t\t\te[i] = filepath.Join(tdir, x)\n\t\t\t\tif strings.HasSuffix(x, utils.Sep) {\n\t\t\t\t\te[i] += utils.Sep\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttest_candidates(prefix, e...)\n\t}\n\n\ttest_cwd_prefix := func(prefix string, expected ...string) {\n\t\te := make([]string, len(expected))\n\t\tfor i, x := range expected {\n\t\t\te[i] = \"./\" + x\n\t\t}\n\t\ttest_candidates(\"./\"+prefix, e...)\n\t}\n\n\ttest_cwd_prefix(\"\", \"one.txt\", \"two.txt\", \"odir/\")\n\ttest_cwd_prefix(\"t\", \"two.txt\")\n\ttest_cwd_prefix(\"x\")\n\n\ttest_abs_candidates(tdir+utils.Sep, \"one.txt\", \"two.txt\", \"odir/\")\n\ttest_abs_candidates(filepath.Join(tdir, \"o\"), \"one.txt\", \"odir/\")\n\n\ttest_candidates(\"\", \"one.txt\", \"two.txt\", \"odir/\")\n\ttest_candidates(\"t\", \"two.txt\")\n\ttest_candidates(\"o\", \"one.txt\", \"odir/\")\n\ttest_candidates(\"odir\", \"odir/\")\n\ttest_candidates(\"odir/\", \"odir/three.txt\", \"odir/four.txt\")\n\ttest_candidates(\"odir/f\", \"odir/four.txt\")\n\ttest_candidates(\"x\")\n\n}\n\nfunc TestCompleteExecutables(t *testing.T) {\n\ttdir := t.TempDir()\n\tcreate := func(base string, name string, mode os.FileMode) {\n\t\tf, _ := os.OpenFile(filepath.Join(tdir, base, name), os.O_CREATE, mode)\n\t\tf.Close()\n\t}\n\tos.Mkdir(filepath.Join(tdir, \"one\"), 0700)\n\tos.Mkdir(filepath.Join(tdir, \"two\"), 0700)\n\n\tcreate(\"\", \"not-in-path\", 0700)\n\tcreate(\"one\", \"one-exec\", 0700)\n\tcreate(\"one\", \"one-not-exec\", 0600)\n\tcreate(\"two\", \"two-exec\", 0700)\n\tos.Symlink(filepath.Join(tdir, \"two\", \"two-exec\"), filepath.Join(tdir, \"one\", \"s\"))\n\tos.Symlink(filepath.Join(tdir, \"one\", \"one-not-exec\"), filepath.Join(tdir, \"one\", \"n\"))\n\n\tt.Setenv(\"PATH\", strings.Join([]string{filepath.Join(tdir, \"one\"), filepath.Join(tdir, \"two\")}, string(os.PathListSeparator)))\n\ttest_candidates := func(prefix string, expected ...string) {\n\t\tif expected == nil {\n\t\t\texpected = make([]string, 0)\n\t\t}\n\t\tactual := CompleteExecutablesInPath(prefix)\n\t\tsort.Strings(expected)\n\t\tsort.Strings(actual)\n\t\tif !reflect.DeepEqual(expected, actual) {\n\t\t\tt.Fatalf(\"Did not get expected completion candidates for prefix: %#v\\nExpected: %#v\\nActual:   %#v\", prefix, expected, actual)\n\t\t}\n\t}\n\ttest_candidates(\"\", \"one-exec\", \"two-exec\", \"s\")\n\ttest_candidates(\"o\", \"one-exec\")\n\ttest_candidates(\"x\")\n}\n"
  },
  {
    "path": "tools/cli/fish.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc fish_completion_script(commands []string) (string, error) {\n\t// One command in fish requires one completion script.\n\t// Usage: kitten __complete__ setup fish [kitty|kitten|clone-in-kitty]\n\tall_commands := map[string]bool{\n\t\t\"kitty\":          true,\n\t\t\"clone-in-kitty\": true,\n\t\t\"kitten\":         true,\n\t}\n\tif len(commands) == 0 {\n\t\tcommands = append(commands, utils.Keys(all_commands)...)\n\t}\n\tscript := strings.Builder{}\n\tscript.WriteString(`function __ksi_completions\n    set --local ct (commandline --current-token)\n    set --local tokens (commandline --tokenize --cut-at-cursor --current-process)\n    printf \"%s\\n\" $tokens $ct | command kitten __complete__ fish | source -\nend\n\n`)\n\tslices.Sort(commands)\n\tfor _, cmd := range commands {\n\t\tif all_commands[cmd] {\n\t\t\tfmt.Fprintf(&script, \"complete -f -c %s -a \\\"(__ksi_completions)\\\"\\n\", cmd)\n\t\t} else if strings.Contains(cmd, \"=\") {\n\t\t\t// Reserved for `setup SHELL [KEY=VALUE ...]`, not used now.\n\t\t\tcontinue\n\t\t} else {\n\t\t\treturn \"\", fmt.Errorf(\"No fish completion script for command: %s\", cmd)\n\t\t}\n\t}\n\treturn script.String(), nil\n}\n\nfunc fish_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {\n\toutput := strings.Builder{}\n\tf := func(format string, args ...any) { fmt.Fprintf(&output, format+\"\\n\", args...) }\n\tn := completions[0].Delegate.NumToRemove\n\tfm := markup.New(false) // fish freaks out if there are escape codes in the description strings\n\tlegacy_completion := shell_state[\"_legacy_completion\"]\n\tif legacy_completion == \"fish2\" {\n\t\tfor _, mg := range completions[0].Groups {\n\t\t\tfor _, m := range mg.Matches {\n\t\t\t\tf(\"%s\", strings.ReplaceAll(m.Word+\"\\t\"+fm.Prettify(m.Description), \"\\n\", \" \"))\n\t\t\t}\n\t\t}\n\t} else if n > 0 {\n\t\twords := make([]string, len(completions[0].AllWords)-n+1)\n\t\twords[0] = completions[0].Delegate.Command\n\t\tcopy(words[1:], completions[0].AllWords[n:])\n\t\tfor i, w := range words {\n\t\t\twords[i] = fmt.Sprintf(\"(string escape -- %s)\", utils.QuoteStringForFish(w))\n\t\t}\n\t\tcmdline := strings.Join(words, \" \")\n\t\tf(\"set __ksi_cmdline \" + cmdline)\n\t\tf(\"complete -C \\\"$__ksi_cmdline\\\"\")\n\t\tf(\"set --erase __ksi_cmdline\")\n\t} else {\n\t\tfor _, mg := range completions[0].Groups {\n\t\t\tfor _, m := range mg.Matches {\n\t\t\t\tf(\"echo -- %s\", utils.QuoteStringForFish(m.Word+\"\\t\"+fm.Prettify(m.Description)))\n\t\t\t}\n\t\t}\n\t}\n\t// debugf(\"%#v\", output.String())\n\treturn []byte(output.String()), nil\n}\n\nfunc init() {\n\tcompletion_scripts[\"fish\"] = fish_completion_script\n\tinput_parsers[\"fish\"] = shell_input_parser\n\toutput_serializers[\"fish\"] = fish_output_serializer\n}\n"
  },
  {
    "path": "tools/cli/group.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\ntype CommandGroup struct {\n\tSubCommands []*Command\n\tTitle       string\n}\n\nfunc (self *CommandGroup) HasVisibleSubCommands() bool {\n\tfor _, c := range self.SubCommands {\n\t\tif !c.Hidden {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *CommandGroup) Clone(parent *Command) *CommandGroup {\n\tans := CommandGroup{Title: self.Title, SubCommands: make([]*Command, len(self.SubCommands))}\n\tfor i, o := range self.SubCommands {\n\t\tans.SubCommands[i] = o.Clone(parent)\n\t}\n\treturn &ans\n}\n\nfunc (self *CommandGroup) AddSubCommand(parent *Command, name string) *Command {\n\tans := NewRootCommand()\n\tans.Parent = parent\n\tans.Name = name\n\tself.SubCommands = append(self.SubCommands, ans)\n\treturn ans\n}\n\nfunc (self *CommandGroup) FindSubCommand(name string) *Command {\n\tfor _, c := range self.SubCommands {\n\t\tif c.Name == name {\n\t\t\treturn c\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *CommandGroup) FindSubCommands(prefix string, matches []*Command) []*Command {\n\tfor _, c := range self.SubCommands {\n\t\tif strings.HasPrefix(c.Name, prefix) {\n\t\t\tmatches = append(matches, c)\n\t\t}\n\t}\n\treturn matches\n}\n\ntype OptionGroup struct {\n\tOptions []*Option\n\tTitle   string\n}\n\nfunc (self *OptionGroup) Clone(parent *Command) *OptionGroup {\n\tans := OptionGroup{Title: self.Title, Options: make([]*Option, len(self.Options))}\n\tfor i, o := range self.Options {\n\t\tc := *o\n\t\tc.init_option()\n\t\tc.Parent = parent\n\t\tans.Options[i] = &c\n\t}\n\treturn &ans\n}\n\nfunc (self *OptionGroup) AddOption(parent *Command, spec OptionSpec) (*Option, error) {\n\tans, err := option_from_spec(spec)\n\tif err == nil {\n\t\tans.Parent = parent\n\t\tself.Options = append(self.Options, ans)\n\t}\n\treturn ans, err\n}\n\nfunc (self *OptionGroup) AddOptionFromString(parent *Command, items ...string) (*Option, error) {\n\tans, err := OptionFromString(items...)\n\tif err == nil {\n\t\tans.Parent = parent\n\t\tself.Options = append(self.Options, ans)\n\t}\n\treturn ans, err\n}\n\nfunc (self *OptionGroup) FindOptions(prefix_with_hyphens string) []*Option {\n\tis_short := !strings.HasPrefix(prefix_with_hyphens, \"--\")\n\toption_name := NormalizeOptionName(prefix_with_hyphens)\n\tans := make([]*Option, 0, 4)\n\tfor _, q := range self.Options {\n\t\tif q.MatchingAlias(option_name, is_short) != \"\" {\n\t\t\tans = append(ans, q)\n\t\t}\n\t}\n\treturn ans\n\n}\n\nfunc (self *OptionGroup) FindOption(name_with_hyphens string) *Option {\n\tis_short := !strings.HasPrefix(name_with_hyphens, \"--\")\n\toption_name := NormalizeOptionName(name_with_hyphens)\n\tfor _, q := range self.Options {\n\t\tif q.HasAlias(option_name, is_short) {\n\t\t\treturn q\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tools/cli/help.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n)\n\nvar _ = fmt.Print\n\nfunc ShowError(err error) {\n\tformatter := markup.New(tty.IsTerminal(os.Stderr.Fd()))\n\tmsg := formatter.Prettify(err.Error())\n\tfmt.Fprintln(os.Stderr, formatter.Err(\"Error\")+\":\", msg)\n}\n\nfunc (self *Command) version_string(formatter *markup.Context) string {\n\treturn fmt.Sprintln(formatter.Italic(self.CommandStringForUsage()), formatter.Opt(kitty.VersionString), \"created by\", formatter.Title(\"Kovid Goyal\"))\n}\n\nfunc (self *Command) ShowVersion() {\n\tformatter := markup.New(tty.IsTerminal(os.Stdout.Fd()))\n\tfmt.Fprint(os.Stdout, self.version_string(formatter))\n}\n\nfunc format_with_indent(output io.Writer, text string, indent string, screen_width int) {\n\tindented := style.WrapText(text, screen_width, style.WrapOptions{Indent: indent, Ignore_lines_containing: \"#placeholder_for_formatting#\", Trim_whitespace: true})\n\tio.WriteString(output, indented)\n\tio.WriteString(output, \"\\n\")\n}\n\nfunc (self *Command) FormatSubCommands(output io.Writer, formatter *markup.Context, screen_width int) {\n\tfor _, g := range self.SubCommandGroups {\n\t\tif !g.HasVisibleSubCommands() {\n\t\t\tcontinue\n\t\t}\n\t\ttitle := g.Title\n\t\tif title == \"\" {\n\t\t\ttitle = \"Commands\"\n\t\t}\n\t\tfmt.Fprintln(output)\n\t\tfmt.Fprintln(output, formatter.Title(title)+\":\")\n\t\tfor _, c := range g.SubCommands {\n\t\t\tif c.Hidden {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Fprintln(output, \"  \", formatter.Opt(c.Name))\n\t\t\tformat_with_indent(output, formatter.Prettify(c.ShortDescription), \"    \", screen_width)\n\t\t}\n\t}\n\n}\n\nfunc (self *Option) FormatOptionForMan(output io.Writer) {\n\tfmt.Fprintln(output, \".TP\")\n\tfmt.Fprint(output, \".BI \\\"\")\n\tfor i, a := range self.Aliases {\n\t\tfmt.Fprint(output, a.String())\n\t\tif i != len(self.Aliases)-1 {\n\t\t\tfmt.Fprint(output, \", \")\n\t\t}\n\t}\n\tfmt.Fprint(output, \"\\\" \")\n\tdefval := self.Default\n\tswitch self.OptionType {\n\tcase CountOption:\n\t\tdefval = \"\"\n\tcase BoolOption:\n\t\tdefval = utils.IfElse(self.Default == \"true\", \"yes\", \"no\")\n\t}\n\n\tif defval != \"\" {\n\t\tfmt.Fprintf(output, \"\\\" [=%s]\\\"\", escape_text_for_man(defval))\n\t}\n\tfmt.Fprintln(output)\n\tfmt.Fprintln(output, escape_help_for_man(self.Help))\n\tif self.Choices != nil {\n\t\tfmt.Fprintln(output)\n\t\tfmt.Fprintln(output, \"Choices: \"+strings.Join(self.Choices, \", \"))\n\t}\n}\n\nfunc (self *Option) FormatOption(output io.Writer, formatter *markup.Context, screen_width int) {\n\tfmt.Fprint(output, \"  \")\n\tfor i, a := range self.Aliases {\n\t\tfmt.Fprint(output, formatter.Opt(a.String()))\n\t\tif i != len(self.Aliases)-1 {\n\t\t\tfmt.Fprint(output, \", \")\n\t\t}\n\t}\n\tdefval := self.Default\n\tswitch self.OptionType {\n\tcase CountOption:\n\t\tdefval = \"\"\n\tcase BoolOption:\n\t\tyn := utils.IfElse(self.Default == \"true\", \"yes\", \"no\")\n\t\tfmt.Fprintf(output, \" %s\", formatter.Italic(\"[=\"+yn+\"]\"))\n\t\tdefval = \"\"\n\t}\n\tif defval != \"\" {\n\t\tfmt.Fprintf(output, \" [=%s]\", formatter.Italic(defval))\n\t}\n\tfmt.Fprintln(output)\n\tformat_with_indent(output, formatter.Prettify(prepare_help_text_for_display(self.Help)), \"    \", screen_width)\n\tif self.Choices != nil {\n\t\tformat_with_indent(output, \"Choices: \"+strings.Join(self.Choices, \", \"), \"    \", screen_width)\n\t}\n}\n\nfunc (self *Command) ShowHelp() {\n\tself.ShowHelpWithCommandString(strings.TrimSpace(self.CommandStringForUsage()))\n}\n\nfunc ShowHelpInPager(text string) {\n\tpager := exec.Command(kitty.DefaultPager[0], kitty.DefaultPager[1:]...)\n\tpager.Stdin = strings.NewReader(text)\n\tpager.Stdout = os.Stdout\n\tpager.Stderr = os.Stderr\n\t_ = pager.Run()\n}\n\nfunc getDeterministicTimestamp() time.Time {\n\tif epoch, err := strconv.ParseInt(os.Getenv(\"SOURCE_DATE_EPOCH\"), 10, 64); err == nil {\n\t\treturn time.Unix(epoch, 0).UTC()\n\t}\n\treturn time.Now()\n}\n\nfunc (self *Command) GenerateManPages(level int, recurse bool) (err error) {\n\tvar names []string\n\tp := self\n\tfor p != nil {\n\t\tnames = append(names, p.Name)\n\t\tp = p.Parent\n\t}\n\tslices.Reverse(names)\n\tname := strings.Join(names, \"-\")\n\toutf, err := os.Create(fmt.Sprintf(\"%s.%d\", name, level))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer outf.Close()\n\tfmt.Fprintf(outf, `.TH \"%s\" \"1\" \"%s\" \"%s\" \"%s\"`, name, getDeterministicTimestamp().Format(\"Jan 02, 2006\"), kitty.VersionString, \"kitten Manual\")\n\tfmt.Fprintln(outf)\n\tfmt.Fprintln(outf, \".SH Name\")\n\tfmt.Fprintln(outf, name, \"\\\\-\", escape_text_for_man(self.ShortDescription))\n\tfmt.Fprintln(outf, \".SH Usage\")\n\tfmt.Fprintln(outf, \".SY\", `\"`+self.CommandStringForUsage()+` `+self.Usage+`\"`)\n\tfmt.Fprintln(outf, \".YS\")\n\tif self.HelpText != \"\" {\n\t\tfmt.Fprintln(outf, \".SH Description\")\n\t\tfmt.Fprintln(outf, escape_help_for_man(self.HelpText))\n\t}\n\n\tif self.HasVisibleSubCommands() {\n\t\tfor _, g := range self.SubCommandGroups {\n\t\t\tif !g.HasVisibleSubCommands() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttitle := g.Title\n\t\t\tif title == \"\" {\n\t\t\t\ttitle = \"Commands\"\n\t\t\t}\n\t\t\tfmt.Fprintln(outf, \".SH\", title)\n\n\t\t\tfor _, c := range utils.Sort(g.SubCommands, func(a, b *Command) int { return strings.Compare(a.Name, b.Name) }) {\n\t\t\t\tif c.Hidden {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif recurse {\n\t\t\t\t\tif err = c.GenerateManPages(level, recurse); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfmt.Fprintln(outf, \".TP\", \"2\")\n\t\t\t\tfmt.Fprintln(outf, c.Name)\n\t\t\t\tfmt.Fprintln(outf, escape_text_for_man(c.ShortDescription)+\".\", \"See: \")\n\t\t\t\tfmt.Fprintf(outf, \".MR %s %d\\n\", name+\"-\"+c.Name, level)\n\t\t\t}\n\t\t}\n\t\tfmt.Fprintln(outf, \".PP\")\n\t\tfmt.Fprintln(outf, \"Get help for an individual command by running:\")\n\t\tfmt.Fprintln(outf, \".SY\", self.CommandStringForUsage())\n\t\tfmt.Fprintln(outf, \"command\", \"-h\")\n\t\tfmt.Fprintln(outf, \".YS\")\n\t}\n\n\tgroup_titles, gmap := self.GetVisibleOptions()\n\tif len(group_titles) > 0 {\n\t\tfor _, title := range group_titles {\n\t\t\tptitle := title\n\t\t\tif title == \"\" {\n\t\t\t\tptitle = \"Options\"\n\t\t\t}\n\t\t\tfmt.Fprintln(outf, \".SH\", ptitle)\n\t\t\tfor _, opt := range gmap[title] {\n\t\t\t\topt.FormatOptionForMan(outf)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Command) ShowHelpWithCommandString(cs string) {\n\tformatter := markup.New(tty.IsTerminal(os.Stdout.Fd()))\n\tscreen_width := 80\n\tif formatter.EscapeCodesAllowed() {\n\t\tvar sz *unix.Winsize\n\t\tvar tty_size_err error\n\t\tfor {\n\t\t\tsz, tty_size_err = unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ)\n\t\t\tif tty_size_err != unix.EINTR {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif tty_size_err == nil && sz.Col < 80 {\n\t\t\tscreen_width = int(sz.Col)\n\t\t}\n\t}\n\tvar output strings.Builder\n\n\tfmt.Fprintln(&output, formatter.Title(\"Usage\")+\":\", formatter.Exe(cs), strings.TrimSpace(formatter.Prettify(self.Usage)))\n\tfmt.Fprintln(&output)\n\tif self.HelpText != \"\" {\n\t\tformat_with_indent(&output, formatter.Prettify(prepare_help_text_for_display(self.HelpText)), \"\", screen_width)\n\t} else if self.ShortDescription != \"\" {\n\t\tformat_with_indent(&output, formatter.Prettify(self.ShortDescription), \"\", screen_width)\n\t}\n\n\tif self.HasVisibleSubCommands() {\n\t\tself.FormatSubCommands(&output, formatter, screen_width)\n\t\tfmt.Fprintln(&output)\n\t\tformat_with_indent(&output, \"Get help for an individual command by running:\", \"\", screen_width)\n\t\tfmt.Fprintln(&output, \"   \", strings.TrimSpace(self.CommandStringForUsage()), formatter.Italic(\"command\"), \"-h\")\n\t}\n\n\tgroup_titles, gmap := self.GetVisibleOptions()\n\tif len(group_titles) > 0 {\n\t\tfmt.Fprintln(&output)\n\t\tfor _, title := range group_titles {\n\t\t\tptitle := title\n\t\t\tif title == \"\" {\n\t\t\t\tptitle = \"Options\"\n\t\t\t}\n\t\t\tfmt.Fprintln(&output, formatter.Title(ptitle)+\":\")\n\t\t\tfor _, opt := range gmap[title] {\n\t\t\t\topt.FormatOption(&output, formatter, screen_width)\n\t\t\t\tfmt.Fprintln(&output)\n\t\t\t}\n\t\t}\n\t}\n\toutput.WriteString(self.version_string(formatter))\n\toutput_text := output.String()\n\t// fmt.Printf(\"%#v\\n\", output_text)\n\tif formatter.EscapeCodesAllowed() {\n\t\tShowHelpInPager(output_text)\n\t} else {\n\t\tos.Stdout.WriteString(output_text)\n\t}\n}\n"
  },
  {
    "path": "tools/cli/markup/prettify.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage markup\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n)\n\nvar _ = fmt.Print\n\ntype Context struct {\n\tfmt_ctx style.Context\n\n\tCyan, Green, Blue, Magenta, Red, BrightRed, Yellow, Italic, Bold, Dim, Title, Exe, Opt, Emph, Err, Code func(args ...any) string\n\tUrl                                                                                                     func(string, string) string\n}\n\nvar (\n\tfmt_ctx = style.Context{}\n)\n\nfunc New(allow_escape_codes bool) *Context {\n\tans := Context{}\n\tans.fmt_ctx.AllowEscapeCodes = allow_escape_codes\n\tfmt_ctx := &ans.fmt_ctx\n\n\tans.Cyan = fmt_ctx.SprintFunc(\"fg=bright-cyan\")\n\tans.Red = fmt_ctx.SprintFunc(\"fg=red\")\n\tans.Magenta = fmt_ctx.SprintFunc(\"fg=magenta\")\n\tans.Green = fmt_ctx.SprintFunc(\"fg=green\")\n\tans.Blue = fmt_ctx.SprintFunc(\"fg=blue\")\n\tans.BrightRed = fmt_ctx.SprintFunc(\"fg=bright-red\")\n\tans.Yellow = fmt_ctx.SprintFunc(\"fg=bright-yellow\")\n\tans.Italic = fmt_ctx.SprintFunc(\"italic\")\n\tans.Bold = fmt_ctx.SprintFunc(\"bold\")\n\tans.Dim = fmt_ctx.SprintFunc(\"dim\")\n\tans.Title = fmt_ctx.SprintFunc(\"bold fg=blue\")\n\tans.Exe = fmt_ctx.SprintFunc(\"bold fg=bright-yellow\")\n\tans.Opt = ans.Green\n\tans.Emph = ans.BrightRed\n\tans.Err = fmt_ctx.SprintFunc(\"bold fg=bright-red\")\n\tans.Code = ans.Cyan\n\tans.Url = fmt_ctx.UrlFunc(\"u=curly uc=cyan\")\n\n\treturn &ans\n}\n\nfunc Remove_backslash_escapes(text string) string {\n\t// see https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#escaping-mechanism\n\tout := strings.Builder{}\n\tprev_was_slash := false\n\tout.Grow(len(text))\n\tfor _, ch := range text {\n\t\tif prev_was_slash {\n\t\t\tif !unicode.IsSpace(ch) {\n\t\t\t\tout.WriteRune(ch)\n\t\t\t}\n\t\t\tprev_was_slash = false\n\t\t} else {\n\t\t\tif ch == '\\\\' {\n\t\t\t\tprev_was_slash = true\n\t\t\t} else {\n\t\t\t\tout.WriteRune(ch)\n\t\t\t}\n\t\t}\n\t}\n\treturn out.String()\n}\n\nfunc ReplaceAllRSTRoles(str string, repl func(Rst_format_match) string) string {\n\tvar m Rst_format_match\n\trf := func(full_match string, groupdict map[string]utils.SubMatch) string {\n\t\tm.Payload = groupdict[\"payload\"].Text\n\t\tm.Role = groupdict[\"role\"].Text\n\t\treturn repl(m)\n\t}\n\treturn utils.ReplaceAll(utils.MustCompile(\":(?P<role>[a-z]+):(?:(?:`(?P<payload>[^`]+)`)|(?:'(?P<payload>[^']+)'))\"), str, rf)\n}\n\nfunc (self *Context) hyperlink_for_url(url string, text string) string {\n\treturn self.Url(url, text)\n}\n\nfunc (self *Context) hyperlink_for_path(path string, text string) string {\n\tif !fmt_ctx.AllowEscapeCodes {\n\t\treturn text\n\t}\n\tpath = strings.ReplaceAll(utils.Abspath(path), string(os.PathSeparator), \"/\")\n\tfi, err := os.Stat(path)\n\tif err == nil && fi.IsDir() {\n\t\tpath = strings.TrimSuffix(path, \"/\") + \"/\"\n\t}\n\thost := utils.Hostname()\n\turl := \"file://\" + host + path\n\treturn self.hyperlink_for_url(url, text)\n}\n\nfunc Text_and_target(x string) (text string, target string) {\n\tparts := strings.SplitN(x, \"<\", 2)\n\ttext = strings.TrimSpace(parts[0])\n\ttarget = strings.TrimRight(parts[len(parts)-1], \">\")\n\treturn\n}\n\ntype Rst_format_match struct {\n\tRole, Payload string\n}\n\nfunc (self *Context) link(x string) string {\n\ttext, url := Text_and_target(x)\n\treturn self.hyperlink_for_url(url, text)\n}\n\nfunc (self *Context) ref_hyperlink(x string, prefix string) string {\n\ttext, target := Text_and_target(x)\n\turl := \"kitty+doc://\" + utils.Hostname() + \"/#ref=\" + prefix + target\n\ttext = ReplaceAllRSTRoles(text, func(group Rst_format_match) string {\n\t\treturn group.Payload\n\t})\n\treturn self.hyperlink_for_url(url, text)\n}\n\nfunc (self *Context) Prettify(text string) string {\n\treturn ReplaceAllRSTRoles(text, func(group Rst_format_match) string {\n\t\tval := group.Payload\n\t\tswitch group.Role {\n\t\tcase \"file\":\n\t\t\tif val == \"kitty.conf\" && self.fmt_ctx.AllowEscapeCodes {\n\t\t\t\tpath := filepath.Join(utils.ConfigDir(), val)\n\t\t\t\tval = self.hyperlink_for_path(path, val)\n\t\t\t}\n\t\t\treturn self.Italic(val)\n\t\tcase \"env\", \"envvar\":\n\t\t\treturn self.ref_hyperlink(val, \"envvar-\")\n\t\tcase \"doc\":\n\t\t\ttext, target := Text_and_target(val)\n\t\t\tno_title := text == target\n\t\t\ttarget = strings.Trim(target, \"/\")\n\t\t\tif title, ok := kitty.DocTitleMap[target]; ok && no_title {\n\t\t\t\tval = title + \" <\" + target + \">\"\n\t\t\t} else {\n\t\t\t\tval = text + \" <\" + target + \">\"\n\t\t\t}\n\t\t\treturn self.ref_hyperlink(val, \"doc-\")\n\t\tcase \"iss\":\n\t\t\treturn self.ref_hyperlink(val, \"issues-\")\n\t\tcase \"pull\":\n\t\t\treturn self.ref_hyperlink(val, \"pull-\")\n\t\tcase \"disc\":\n\t\t\treturn self.ref_hyperlink(val, \"discussions-\")\n\t\tcase \"ref\":\n\t\t\treturn self.ref_hyperlink(val, \"\")\n\t\tcase \"ac\":\n\t\t\treturn self.ref_hyperlink(val, \"action-\")\n\t\tcase \"term\":\n\t\t\treturn self.ref_hyperlink(val, \"term-\")\n\t\tcase \"code\":\n\t\t\treturn self.Code(Remove_backslash_escapes(val))\n\t\tcase \"link\":\n\t\t\treturn self.link(val)\n\t\tcase \"option\":\n\t\t\tidx := strings.LastIndex(val, \"--\")\n\t\t\tif idx < 0 {\n\t\t\t\tidx = strings.Index(val, \"-\")\n\t\t\t}\n\t\t\tif idx > -1 {\n\t\t\t\tval = strings.TrimSuffix(val[idx:], \">\")\n\t\t\t}\n\t\t\treturn self.Bold(val)\n\t\tcase \"opt\":\n\t\t\treturn self.Bold(val)\n\t\tcase \"yellow\":\n\t\t\treturn self.Yellow(val)\n\t\tcase \"blue\":\n\t\t\treturn self.Blue(val)\n\t\tcase \"green\":\n\t\t\treturn self.Green(val)\n\t\tcase \"cyan\":\n\t\t\treturn self.Cyan(val)\n\t\tcase \"magenta\":\n\t\t\treturn self.Magenta(val)\n\t\tcase \"emph\":\n\t\t\treturn self.Italic(val)\n\t\tdefault:\n\t\t\treturn val\n\t\t}\n\n\t})\n}\n\nfunc (self *Context) SetAllowEscapeCodes(allow_escape_codes bool) {\n\tself.fmt_ctx.AllowEscapeCodes = allow_escape_codes\n}\n\nfunc (self *Context) EscapeCodesAllowed() bool {\n\treturn self.fmt_ctx.AllowEscapeCodes\n}\n"
  },
  {
    "path": "tools/cli/option-from-string.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n)\n\nvar _ = fmt.Print\n\nfunc camel_case_dest(x string) string {\n\tx = strings.ReplaceAll(strings.ReplaceAll(x, \"-\", \"_\"), \",\", \"\")\n\tparts := strings.Split(x, \"_\")\n\tfor i, p := range parts {\n\t\tparts[i] = utils.Capitalize(p)\n\t}\n\treturn strings.Join(parts, \"\")\n}\n\n/*\nCreate an [Option] from a string. Syntax is:\n\n\t--option-name, --option-alias, -s\n\ttype: string\n\tdest: destination\n\tchoices: choice1, choice2, choice 3\n\tdepth: 0\n\tdefault: something\n\tHelp text on multiple lines. Indented lines are preserved as indented blocks. Blank lines\n\tare preserved as blank lines. #placeholder_for_formatting# is replaced by the empty string.\n\t.. code:: blocks are handled specially. Lines in them starting with \"$ \" have the $ colored\n\tto indicate a prompt.\n\nAvailable types are: string, str, list, int, float, count, bool-set, bool-reset, choices\nThe default dest is the first --option-name which must be a long option. The destination is automatically CamelCased from snake_case.\nIf choices are specified type is set to choices automatically.\nIf depth is negative option is added to all subcommands. If depth is positive option is added to sub-commands upto\nthe specified depth.\nSet the help text to \"!\" to have an option hidden.\n*/\nfunc OptionFromString(entries ...string) (*Option, error) {\n\treturn option_from_string(map[string]string{}, entries...)\n}\n\nfunc is_string_slice(f reflect.Value) bool {\n\tif f.Kind() != reflect.Slice {\n\t\treturn false\n\t}\n\treturn f.Type().Elem().Kind() == reflect.String\n}\n\nfunc OptionsFromStruct(pointer_to_options_struct any) ([]*Option, error) {\n\tval := reflect.ValueOf(pointer_to_options_struct).Elem()\n\tif val.Kind() != reflect.Struct {\n\t\treturn nil, fmt.Errorf(\"Need a pointer to a struct to set option values on\")\n\t}\n\tans := make([]*Option, 0, val.NumField())\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tf := val.Field(i)\n\t\tfield_name := val.Type().Field(i).Name\n\t\ttag := val.Type().Field(i).Tag\n\t\tif utils.Capitalize(field_name) != field_name || !f.CanSet() {\n\t\t\tcontinue\n\t\t}\n\t\ttyp := \"str\"\n\t\tswitch f.Kind() {\n\t\tcase reflect.Slice:\n\t\t\tif !is_string_slice(f) {\n\t\t\t\treturn nil, fmt.Errorf(\"The field %s is not a slice of strings\", field_name)\n\t\t\t}\n\t\t\ttyp = \"list\"\n\t\tcase reflect.Int:\n\t\t\ttyp = \"int\"\n\t\tcase reflect.Float64:\n\t\t\ttyp = \"float\"\n\t\tcase reflect.Bool:\n\t\t\ttyp = \"bool-set\"\n\t\t}\n\t\toverrides := map[string]string{\"dest\": field_name, \"type\": typ}\n\t\topt, err := option_from_string(overrides, string(tag))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif opt.OptionType == CountOption && f.Kind() != reflect.Int {\n\t\t\treturn nil, fmt.Errorf(\"The field %s is of count type but in the options struct it does not have type int\", field_name)\n\t\t}\n\t\tans = append(ans, opt)\n\t}\n\n\treturn ans, nil\n}\n\ntype multi_scan struct {\n\tentries []string\n}\n\nvar mpat *regexp.Regexp\n\nfunc (self *Option) init_option() {\n\tself.values_from_cmdline = make([]string, 0, 1)\n\tself.parsed_values_from_cmdline = make([]any, 0, 1)\n}\nfunc option_from_spec(spec OptionSpec) (*Option, error) {\n\tans := Option{\n\t\tHelp: spec.Help,\n\t}\n\tans.init_option()\n\tparts := strings.Split(spec.Name, \" \")\n\tans.Name = camel_case_dest(parts[0])\n\tans.Aliases = make([]Alias, len(parts))\n\tfor i, x := range parts {\n\t\tans.Aliases[i] = Alias{NameWithoutHyphens: strings.TrimLeft(x, \"-\"), IsShort: !strings.HasPrefix(x, \"--\")}\n\t}\n\tif spec.Dest != \"\" {\n\t\tans.Name = spec.Dest\n\t}\n\tans.Depth = spec.Depth\n\tif spec.Choices != \"\" {\n\t\tparts := strings.Split(spec.Choices, \",\")\n\t\tif len(parts) == 1 {\n\t\t\tparts = strings.Split(spec.Choices, \" \")\n\t\t} else {\n\t\t\tfor i, x := range parts {\n\t\t\t\tparts[i] = strings.TrimSpace(x)\n\t\t\t}\n\t\t}\n\t\tans.Choices = parts\n\t\tans.OptionType = StringOption\n\t\tif ans.Default == \"\" {\n\t\t\tans.Default = parts[0]\n\t\t}\n\t} else {\n\t\tswitch spec.Type {\n\t\tcase \"choice\", \"choices\":\n\t\t\tans.OptionType = StringOption\n\t\tcase \"int\":\n\t\t\tans.OptionType = IntegerOption\n\t\t\tans.Default = \"0\"\n\t\tcase \"float\":\n\t\t\tans.OptionType = FloatOption\n\t\t\tans.Default = \"0\"\n\t\tcase \"count\":\n\t\t\tans.OptionType = CountOption\n\t\t\tans.Default = \"0\"\n\t\tcase \"bool-set\":\n\t\t\tans.OptionType = BoolOption\n\t\t\tans.Default = \"false\"\n\t\tcase \"bool-reset\":\n\t\t\tans.OptionType = BoolOption\n\t\t\tans.Default = \"true\"\n\t\t\tfor _, a := range ans.Aliases {\n\t\t\t\ta.IsUnset = true\n\t\t\t}\n\t\tcase \"list\":\n\t\t\tans.IsList = true\n\t\t\tfallthrough\n\t\tcase \"str\", \"string\", \"\":\n\t\t\tans.OptionType = StringOption\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"Unknown option type: %s\", spec.Type)\n\t\t}\n\t}\n\tif spec.Default != \"\" {\n\t\tans.Default = spec.Default\n\t}\n\tans.Help = spec.Help\n\tans.Hidden = spec.Help == \"!\"\n\tpval, err := ans.parse_value(ans.Default)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tans.parsed_default = pval\n\tif ans.IsList {\n\t\tif ans.Default == \"\" {\n\t\t\tans.parsed_default = nil\n\t\t} else if ans.parsed_default, err = shlex.Split(ans.Default); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tans.Completer = spec.Completer\n\tif ans.Aliases == nil || len(ans.Aliases) == 0 {\n\t\treturn nil, fmt.Errorf(\"No --aliases specified for option\")\n\t}\n\tif ans.Name == \"\" {\n\t\treturn nil, fmt.Errorf(\"No dest specified for option\")\n\t}\n\treturn &ans, nil\n}\n\nfunc indent_of_line(x string) int {\n\treturn len(x) - len(strings.TrimLeft(x, \" \\n\\t\\v\\f\"))\n}\n\nfunc escape_text_for_man(raw string) string {\n\titalic := func(x string) string {\n\t\treturn \"\\n.I \" + x\n\t}\n\tbold := func(x string) string {\n\t\treturn \"\\n.B \" + x\n\t}\n\ttext_without_target := func(val string) string {\n\t\ttext, target := markup.Text_and_target(val)\n\t\tno_title := text == target\n\t\tif no_title {\n\t\t\treturn val\n\t\t}\n\t\treturn text\n\t}\n\tref_hyperlink := func(val, prefix string) string {\n\t\treturn text_without_target(val)\n\t}\n\n\traw = markup.ReplaceAllRSTRoles(raw, func(group markup.Rst_format_match) string {\n\t\tval := group.Payload\n\t\tswitch group.Role {\n\t\tcase \"file\":\n\t\t\treturn italic(val)\n\t\tcase \"env\", \"envvar\":\n\t\t\treturn bold(val)\n\t\tcase \"doc\":\n\t\t\treturn text_without_target(val)\n\t\tcase \"iss\":\n\t\t\treturn \"Issue #\" + val\n\t\tcase \"pull\":\n\t\t\treturn \"PR #\" + val\n\t\tcase \"disc\":\n\t\t\treturn \"Discussion #\" + val\n\t\tcase \"ref\":\n\t\t\treturn ref_hyperlink(val, \"\")\n\t\tcase \"ac\":\n\t\t\treturn ref_hyperlink(val, \"action-\")\n\t\tcase \"term\":\n\t\t\treturn ref_hyperlink(val, \"term-\")\n\t\tcase \"code\":\n\t\t\treturn markup.Remove_backslash_escapes(val)\n\t\tcase \"link\":\n\t\t\treturn text_without_target(val)\n\t\tcase \"option\":\n\t\t\tidx := strings.LastIndex(val, \"--\")\n\t\t\tif idx < 0 {\n\t\t\t\tidx = strings.Index(val, \"-\")\n\t\t\t}\n\t\t\tif idx > -1 {\n\t\t\t\tval = strings.TrimSuffix(val[idx:], \">\")\n\t\t\t}\n\t\t\treturn bold(val)\n\t\tcase \"opt\":\n\t\t\treturn bold(val)\n\t\tcase \"yellow\":\n\t\t\treturn val\n\t\tcase \"blue\":\n\t\t\treturn val\n\t\tcase \"green\":\n\t\t\treturn val\n\t\tcase \"cyan\":\n\t\t\treturn val\n\t\tcase \"magenta\":\n\t\t\treturn val\n\t\tcase \"emph\":\n\t\t\treturn val\n\t\tdefault:\n\t\t\treturn val\n\t\t}\n\t})\n\tsb := strings.Builder{}\n\tsb.Grow(2 * len(raw))\n\treplacements := map[rune]string{\n\t\t'\"': `\\[dq]`, '\\'': `\\[aq]`, '-': `\\-`, '\\\\': `\\e`, '^': `\\(ha`, '`': `\\(ga`, '~': `\\(ti`,\n\t}\n\tfor _, ch := range raw {\n\t\tif rep, found := replacements[ch]; found {\n\t\t\tsb.WriteString(rep)\n\t\t} else {\n\t\t\tsb.WriteRune(ch)\n\t\t}\n\t}\n\treturn sb.String()\n}\n\nfunc escape_help_for_man(raw string) string {\n\thelp := strings.Builder{}\n\thelp.Grow(len(raw) + 256)\n\tprev_indent := 0\n\tin_code_block := false\n\tlines := utils.Splitlines(raw)\n\n\thandle_non_empty_line := func(i int, line string) int {\n\t\tif strings.TrimSpace(line) == \"#placeholder_for_formatting#\" {\n\t\t\treturn i + 1\n\t\t}\n\t\tif strings.HasPrefix(line, \".. code::\") {\n\t\t\tin_code_block = true\n\t\t\treturn i + 1\n\t\t}\n\t\tcurrent_indent := indent_of_line(line)\n\t\tif current_indent > 1 {\n\t\t\tif prev_indent == 0 {\n\t\t\t\thelp.WriteString(\"\\n\")\n\t\t\t} else {\n\t\t\t\tline = strings.TrimSpace(line)\n\t\t\t}\n\t\t}\n\t\tprev_indent = current_indent\n\t\tif help.Len() > 0 && !strings.HasSuffix(help.String(), \"\\n\") {\n\t\t\thelp.WriteString(\" \")\n\t\t}\n\t\thelp.WriteString(line)\n\t\treturn i\n\t}\n\n\thandle_empty_line := func(i int, line string) int {\n\t\tprev_indent = 0\n\t\thelp.WriteString(\"\\n\")\n\t\tif !strings.HasSuffix(help.String(), \"::\") {\n\t\t\thelp.WriteString(\"\\n\")\n\t\t}\n\t\treturn i\n\t}\n\n\thandle_code_block_line := func(i int, line string) int {\n\t\tif line == \"\" {\n\t\t\thelp.WriteString(\"\\n\")\n\t\t\treturn i\n\t\t}\n\t\tcurrent_indent := indent_of_line(line)\n\t\tif current_indent == 0 {\n\t\t\tin_code_block = false\n\t\t\treturn handle_non_empty_line(i, line)\n\t\t}\n\t\tline = line[4:]\n\t\tis_prompt := strings.HasPrefix(line, \"$ \")\n\t\tif is_prompt {\n\t\t\thelp.WriteString(\":yellow:`$ `\")\n\t\t\tline = line[2:]\n\t\t}\n\t\thelp.WriteString(line)\n\t\thelp.WriteString(\"\\n\")\n\t\treturn i\n\t}\n\n\tfor i := 0; i < len(lines); i++ {\n\t\tline := lines[i]\n\t\tif in_code_block {\n\t\t\ti = handle_code_block_line(i, line)\n\t\t\tcontinue\n\t\t}\n\t\tif line != \"\" {\n\t\t\ti = handle_non_empty_line(i, line)\n\t\t} else {\n\t\t\ti = handle_empty_line(i, line)\n\t\t}\n\t}\n\treturn escape_text_for_man(help.String())\n}\n\nfunc prepare_help_text_for_display(raw string) string {\n\thelp := strings.Builder{}\n\thelp.Grow(len(raw) + 256)\n\tprev_indent := 0\n\tin_code_block := false\n\tlines := utils.Splitlines(raw)\n\n\thandle_non_empty_line := func(i int, line string) int {\n\t\tif strings.HasPrefix(line, \".. code::\") {\n\t\t\tin_code_block = true\n\t\t\treturn i + 1\n\t\t}\n\t\tcurrent_indent := indent_of_line(line)\n\t\tif current_indent > 1 {\n\t\t\tif prev_indent == 0 {\n\t\t\t\thelp.WriteString(\"\\n\")\n\t\t\t} else {\n\t\t\t\tline = strings.TrimSpace(line)\n\t\t\t}\n\t\t}\n\t\tprev_indent = current_indent\n\t\tif help.Len() > 0 && !strings.HasSuffix(help.String(), \"\\n\") {\n\t\t\thelp.WriteString(\" \")\n\t\t}\n\t\thelp.WriteString(line)\n\t\treturn i\n\t}\n\n\thandle_empty_line := func(i int, line string) int {\n\t\tprev_indent = 0\n\t\thelp.WriteString(\"\\n\")\n\t\tif !strings.HasSuffix(help.String(), \"::\") {\n\t\t\thelp.WriteString(\"\\n\")\n\t\t}\n\t\treturn i\n\t}\n\n\thandle_code_block_line := func(i int, line string) int {\n\t\tif line == \"\" {\n\t\t\thelp.WriteString(\"\\n\")\n\t\t\treturn i\n\t\t}\n\t\tcurrent_indent := indent_of_line(line)\n\t\tif current_indent == 0 {\n\t\t\tin_code_block = false\n\t\t\treturn handle_non_empty_line(i, line)\n\t\t}\n\t\tline = line[4:]\n\t\tis_prompt := strings.HasPrefix(line, \"$ \")\n\t\tif is_prompt {\n\t\t\thelp.WriteString(\":yellow:`$ `\")\n\t\t\tline = line[2:]\n\t\t}\n\t\thelp.WriteString(line)\n\t\thelp.WriteString(\"\\n\")\n\t\treturn i\n\t}\n\n\tfor i := 0; i < len(lines); i++ {\n\t\tline := lines[i]\n\t\tif in_code_block {\n\t\t\ti = handle_code_block_line(i, line)\n\t\t\tcontinue\n\t\t}\n\t\tif line != \"\" {\n\t\t\ti = handle_non_empty_line(i, line)\n\t\t} else {\n\t\t\ti = handle_empty_line(i, line)\n\t\t}\n\t}\n\treturn help.String()\n}\n\nfunc option_from_string(overrides map[string]string, entries ...string) (*Option, error) {\n\tif mpat == nil {\n\t\tmpat = regexp.MustCompile(\"^([a-z]+)=(.+)\")\n\t}\n\tspec := OptionSpec{}\n\tscanner := utils.NewScanLines(entries...)\n\tin_help := false\n\thelp := strings.Builder{}\n\thelp.Grow(2048)\n\n\tif dq, found := overrides[\"type\"]; found {\n\t\tspec.Type = dq\n\t}\n\tif dq, found := overrides[\"dest\"]; found {\n\t\tspec.Dest = dq\n\t}\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tif spec.Name == \"\" {\n\t\t\tif strings.HasPrefix(line, \"--\") {\n\t\t\t\tspec.Name = line\n\t\t\t}\n\t\t} else if in_help {\n\t\t\tspec.Help += line + \"\\n\"\n\t\t} else {\n\t\t\tline = strings.TrimSpace(line)\n\t\t\tmatches := mpat.FindStringSubmatch(line)\n\t\t\tif matches == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tk, v := matches[1], matches[2]\n\t\t\tswitch k {\n\t\t\tcase \"choices\":\n\t\t\t\tspec.Choices = v\n\t\t\tcase \"default\":\n\t\t\t\tif overrides[\"default\"] == \"\" {\n\t\t\t\t\tspec.Default = v\n\t\t\t\t}\n\t\t\tcase \"dest\":\n\t\t\t\tif overrides[\"dest\"] == \"\" {\n\t\t\t\t\tspec.Dest = v\n\t\t\t\t}\n\t\t\tcase \"depth\":\n\t\t\t\tdepth, err := strconv.ParseInt(v, 0, 0)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tspec.Depth = int(depth)\n\t\t\tcase \"condition\", \"completion\":\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"Unknown option metadata key: %s\", k)\n\t\t\tcase \"type\":\n\t\t\t\tspec.Type = v\n\t\t\t}\n\t\t}\n\t}\n\treturn option_from_spec(spec)\n}\n"
  },
  {
    "path": "tools/cli/option.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\ntype OptionType int\n\nconst (\n\tStringOption OptionType = iota\n\tIntegerOption\n\tFloatOption\n\tBoolOption\n\tCountOption\n)\n\ntype Alias struct {\n\tNameWithoutHyphens string\n\tIsShort            bool\n\tIsUnset            bool\n}\n\nfunc (self *Alias) String() string {\n\tif self.IsShort {\n\t\treturn \"-\" + self.NameWithoutHyphens\n\t}\n\treturn \"--\" + self.NameWithoutHyphens\n}\n\ntype OptionSpec struct {\n\tName      string\n\tType      string\n\tDest      string\n\tChoices   string\n\tDepth     int\n\tDefault   string\n\tHelp      string\n\tCompleter CompletionFunc\n}\n\ntype Option struct {\n\tName       string\n\tAliases    []Alias\n\tChoices    []string\n\tDefault    string\n\tOptionType OptionType\n\tHidden     bool\n\tDepth      int\n\tHelp       string\n\tIsList     bool\n\tParent     *Command\n\tCompleter  CompletionFunc\n\n\tvalues_from_cmdline        []string\n\tparsed_values_from_cmdline []any\n\tparsed_default             any\n\tseen_option                string\n}\n\nfunc (self *Option) reset() {\n\tself.values_from_cmdline = self.values_from_cmdline[:0]\n\tself.parsed_values_from_cmdline = self.parsed_values_from_cmdline[:0]\n\tself.seen_option = \"\"\n}\n\nfunc (self *Option) needs_argument() bool {\n\treturn self.OptionType != BoolOption && self.OptionType != CountOption\n}\n\nfunc (self *Option) MatchingAlias(prefix_without_hyphens string, is_short bool) string {\n\tfor _, a := range self.Aliases {\n\t\tif a.IsShort == is_short && strings.HasPrefix(a.NameWithoutHyphens, prefix_without_hyphens) {\n\t\t\treturn a.String()\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc (self *Option) HasAlias(name_without_hyphens string, is_short bool) bool {\n\tfor _, a := range self.Aliases {\n\t\tif a.IsShort == is_short && a.NameWithoutHyphens == name_without_hyphens {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\ntype ParseError struct {\n\tOption  *Option\n\tMessage string\n}\n\nfunc (self *ParseError) Error() string { return self.Message }\n\nfunc NormalizeOptionName(name string) string {\n\treturn strings.ReplaceAll(strings.TrimLeft(name, \"-\"), \"_\", \"-\")\n}\n\nfunc (self *Option) parsed_value() any {\n\tif len(self.values_from_cmdline) == 0 {\n\t\tif self.IsList {\n\t\t\tif self.parsed_default == nil {\n\t\t\t\treturn []string{}\n\t\t\t}\n\t\t}\n\t\treturn self.parsed_default\n\t}\n\tswitch self.OptionType {\n\tcase CountOption:\n\t\treturn len(self.parsed_values_from_cmdline)\n\tcase StringOption:\n\t\tif self.IsList {\n\t\t\tans := make([]string, 0, len(self.parsed_values_from_cmdline)+2)\n\t\t\tif self.parsed_default != nil {\n\t\t\t\tans = append(ans, self.parsed_default.([]string)...)\n\t\t\t}\n\t\t\tfor _, x := range self.parsed_values_from_cmdline {\n\t\t\t\tans = append(ans, x.(string))\n\t\t\t}\n\t\t\treturn ans\n\t\t}\n\t\tfallthrough\n\tdefault:\n\t\treturn self.parsed_values_from_cmdline[len(self.parsed_values_from_cmdline)-1]\n\t}\n}\n\nfunc (self *Option) parse_value(val string) (any, error) {\n\tswitch self.OptionType {\n\tcase BoolOption:\n\t\tswitch val {\n\t\tcase \"y\", \"yes\", \"true\":\n\t\t\treturn true, nil\n\t\tcase \"n\", \"no\", \"false\":\n\t\t\treturn false, nil\n\t\tdefault:\n\t\t\treturn nil, &ParseError{Option: self, Message: fmt.Sprintf(\":yellow:`%s` is not a valid value for :bold:`%s`.\", val, self.seen_option)}\n\t\t}\n\tcase StringOption:\n\t\treturn val, nil\n\tcase IntegerOption, CountOption:\n\t\tpval, err := strconv.ParseInt(val, 0, 0)\n\t\tif err != nil {\n\t\t\treturn nil, &ParseError{Option: self, Message: fmt.Sprintf(\n\t\t\t\t\":yellow:`%s` is not a valid number for :bold:`%s`. Only integers in decimal, hexadecimal, binary or octal notation are accepted.\", val, self.seen_option)}\n\t\t}\n\t\treturn int(pval), nil\n\tcase FloatOption:\n\t\tpval, err := strconv.ParseFloat(val, 64)\n\t\tif err != nil {\n\t\t\treturn nil, &ParseError{Option: self, Message: fmt.Sprintf(\n\t\t\t\t\":yellow:`%s` is not a valid number for :bold:`%s`. Only floats in decimal and hexadecimal notation are accepted.\", val, self.seen_option)}\n\t\t}\n\t\treturn pval, nil\n\tdefault:\n\t\treturn nil, &ParseError{Option: self, Message: fmt.Sprintf(\"Unknown option type for %s\", self.Name)}\n\t}\n}\n\nfunc (self *Option) add_value(val string) error {\n\tname_without_hyphens := NormalizeOptionName(self.seen_option)\n\tswitch self.OptionType {\n\tcase BoolOption:\n\t\tif val == \"\" {\n\t\t\tfor _, x := range self.Aliases {\n\t\t\t\tif x.NameWithoutHyphens == name_without_hyphens {\n\t\t\t\t\tif x.IsUnset {\n\t\t\t\t\t\tself.values_from_cmdline = append(self.values_from_cmdline, \"false\")\n\t\t\t\t\t\tself.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, false)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tself.values_from_cmdline = append(self.values_from_cmdline, \"true\")\n\t\t\t\t\t\tself.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, true)\n\t\t\t\t\t}\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tswitch val {\n\t\t\tcase \"y\", \"yes\", \"true\":\n\t\t\t\tself.values_from_cmdline = append(self.values_from_cmdline, \"true\")\n\t\t\t\tself.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, true)\n\t\t\tcase \"n\", \"no\", \"false\":\n\t\t\t\tself.values_from_cmdline = append(self.values_from_cmdline, \"false\")\n\t\t\t\tself.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, false)\n\t\t\tdefault:\n\t\t\t\treturn &ParseError{Option: self, Message: fmt.Sprintf(\":yellow:`%s` is not a valid value for :bold:`%s`. Valid values: %s\",\n\t\t\t\t\tval, self.seen_option, \"y, yes, true, n, no and false\",\n\t\t\t\t)}\n\n\t\t\t}\n\t\t}\n\tcase StringOption:\n\t\tif self.Choices != nil && !slices.Contains(self.Choices, val) {\n\t\t\treturn &ParseError{Option: self, Message: fmt.Sprintf(\":yellow:`%s` is not a valid value for :bold:`%s`. Valid values: %s\",\n\t\t\t\tval, self.seen_option, strings.Join(self.Choices, \", \"),\n\t\t\t)}\n\t\t}\n\t\tself.values_from_cmdline = append(self.values_from_cmdline, val)\n\t\tself.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, val)\n\tcase IntegerOption, FloatOption:\n\t\tpval, err := self.parse_value(val)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tself.values_from_cmdline = append(self.values_from_cmdline, val)\n\t\tself.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, pval)\n\tcase CountOption:\n\t\tself.values_from_cmdline = append(self.values_from_cmdline, val)\n\t\tself.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, 1)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tools/cli/parse-args.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\nfunc (self *Command) parse_args(ctx *Context, args []string) error {\n\targs_to_parse := make([]string, len(args))\n\tcopy(args_to_parse, args)\n\tctx.SeenCommands = append(ctx.SeenCommands, self)\n\tif self.IgnoreAllArgs {\n\t\tself.Args = args\n\t\treturn nil\n\t}\n\n\tvar expecting_arg_for *Option\n\toptions_allowed := true\n\n\tconsume_arg := func() string { ans := args_to_parse[0]; args_to_parse = args_to_parse[1:]; return ans }\n\n\thandle_option := func(opt_str string, has_val bool, opt_val string, val_not_allowed bool) error {\n\t\tpossible_options := self.FindOptions(opt_str)\n\t\tvar opt *Option\n\t\tif len(possible_options) == 1 {\n\t\t\topt = possible_options[0]\n\t\t\topt_str = opt.MatchingAlias(NormalizeOptionName(opt_str), !strings.HasPrefix(opt_str, \"--\"))\n\t\t} else if len(possible_options) == 0 {\n\t\t\tpossibles := self.SuggestionsForOption(opt_str, 2)\n\t\t\tif len(possibles) > 0 {\n\t\t\t\treturn &ParseError{Message: fmt.Sprintf(\"Unknown option: :yellow:`%s`. Did you mean:\\n\\t%s\", opt_str, strings.Join(possibles, \"\\n\\t\"))}\n\t\t\t}\n\t\t\treturn &ParseError{Message: fmt.Sprintf(\"Unknown option: :yellow:`%s`\", opt_str)}\n\t\t} else {\n\t\t\tambi := make([]string, len(possible_options))\n\t\t\tfor i, o := range possible_options {\n\t\t\t\tambi[i] = o.MatchingAlias(NormalizeOptionName(opt_str), !strings.HasPrefix(opt_str, \"--\"))\n\t\t\t\tif ambi[i] == opt_str {\n\t\t\t\t\topt = o\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif opt == nil {\n\t\t\t\treturn &ParseError{Message: fmt.Sprintf(\"Ambiguous option: :yellow:`%s` could be any of: %s\", opt_str, strings.Join(ambi, \", \"))}\n\t\t\t}\n\t\t}\n\t\topt.seen_option = opt_str\n\t\tneeds_arg := opt.needs_argument()\n\t\tif needs_arg && val_not_allowed {\n\t\t\treturn &ParseError{Message: fmt.Sprintf(\"The option: :yellow:`%s` must be followed by a value not another option\", opt_str)}\n\t\t}\n\t\tif has_val {\n\t\t\tif !needs_arg {\n\t\t\t\tif opt.OptionType == BoolOption {\n\t\t\t\t\tif err := opt.add_value(opt_val); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn &ParseError{Message: fmt.Sprintf(\"The option: :yellow:`%s` does not take values\", opt_str)}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn opt.add_value(opt_val)\n\t\t} else if needs_arg {\n\t\t\texpecting_arg_for = opt\n\t\t} else {\n\t\t\topt.add_value(\"\")\n\t\t}\n\t\treturn nil\n\t}\n\n\tfor len(args_to_parse) > 0 {\n\t\targ := consume_arg()\n\n\t\tif expecting_arg_for == nil {\n\t\t\tif options_allowed && strings.HasPrefix(arg, \"-\") && arg != \"-\" {\n\t\t\t\t// handle option arg\n\t\t\t\tif arg == \"--\" {\n\t\t\t\t\toptions_allowed = false\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\topt_str, opt_val, has_val := strings.Cut(arg, \"=\")\n\t\t\t\tif strings.HasPrefix(opt_str, \"--\") {\n\t\t\t\t\terr := handle_option(opt_str, has_val, opt_val, false)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\trunes := []rune(opt_str[1:])\n\t\t\t\t\tfor i, sl := range runes {\n\t\t\t\t\t\tvar err error\n\t\t\t\t\t\tif i == len(runes)-1 {\n\t\t\t\t\t\t\terr = handle_option(\"-\"+string(sl), has_val, opt_val, false)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\terr = handle_option(\"-\"+string(sl), false, \"\", true)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// handle non option arg\n\t\t\t\tif self.AllowOptionsAfterArgs <= len(self.Args) {\n\t\t\t\t\toptions_allowed = false\n\t\t\t\t}\n\t\t\t\tif self.HasSubCommands() {\n\t\t\t\t\tpossible_cmds := self.FindSubCommands(arg)\n\t\t\t\t\tif len(possible_cmds) == 1 {\n\t\t\t\t\t\treturn possible_cmds[0].parse_args(ctx, args_to_parse)\n\t\t\t\t\t}\n\t\t\t\t\tif !self.SubCommandIsOptional {\n\t\t\t\t\t\tif len(possible_cmds) == 0 {\n\t\t\t\t\t\t\tpossibles := self.SuggestionsForCommand(arg, 2)\n\t\t\t\t\t\t\tif len(possibles) > 0 {\n\t\t\t\t\t\t\t\treturn &ParseError{Message: fmt.Sprintf(\"Unknown subcommand: :yellow:`%s`. Did you mean:\\n\\t%s\", arg, strings.Join(possibles, \"\\n\\t\"))}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn &ParseError{Message: fmt.Sprintf(\":yellow:`%s` is not a known subcommand for :emph:`%s`. Use --help to get a list of valid subcommands.\", arg, self.Name)}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcn := make([]string, len(possible_cmds))\n\t\t\t\t\t\tfor i, x := range possible_cmds {\n\t\t\t\t\t\t\tcn[i] = x.Name\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn &ParseError{Message: fmt.Sprintf(\n\t\t\t\t\t\t\t\":yellow:`%s` is not a known subcommand for :emph:`%s`. Did you mean:\\n\\t%s\", arg, self.Name, strings.Join(cn, \"\\n\\t\"))}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tself.Args = append(self.Args, arg)\n\t\t\t}\n\t\t} else {\n\t\t\t// handle option value\n\t\t\terr := expecting_arg_for.add_value(arg)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\texpecting_arg_for = nil\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tools/cli/types_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\ntype empty_options struct {\n}\n\ntype base_options struct {\n\tFromParent int\n}\n\ntype options struct {\n\tFromParent      int\n\tSimpleString    string\n\tChoices         string\n\tSetMe           bool\n\tInt             int\n\tFloat           float64\n\tList            []string\n\tListWithDefault []string\n}\n\nfunc TestCLIParsing(t *testing.T) {\n\n\trt := func(expected_cmd *Command, cmdline string, expected_options any, expected_args ...string) {\n\t\tif opts, ok := expected_options.(*options); ok && opts.ListWithDefault == nil {\n\t\t\topts.ListWithDefault = []string{`1`, `2`}\n\t\t}\n\t\tcp, err := shlex.Split(cmdline)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tcmd, err := expected_cmd.ParseArgs(cp)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif s, ok := expected_options.(*options); ok {\n\t\t\tif s.Choices == \"\" {\n\t\t\t\ts.Choices = \"a\"\n\t\t\t}\n\t\t\tif s.List == nil {\n\t\t\t\ts.List = make([]string, 0)\n\t\t\t}\n\t\t}\n\t\tactual_options := reflect.New(reflect.TypeOf(expected_options).Elem()).Interface()\n\t\terr = cmd.GetOptionValues(actual_options)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif diff := cmp.Diff(expected_options, actual_options); diff != \"\" {\n\t\t\tt.Fatalf(\"Option values incorrect (expected != actual):\\nCommand line: %s\\n%s\", cmdline, diff)\n\t\t}\n\t\tif expected_args == nil {\n\t\t\texpected_args = []string{}\n\t\t}\n\t\tif diff := cmp.Diff(expected_args, cmd.Args); diff != \"\" {\n\t\t\tt.Fatalf(\"Argument values incorrect (expected != actual):\\nCommand line: %s\\n%s\", cmdline, diff)\n\t\t}\n\t\tcmd.Root().ResetAfterParseArgs()\n\t}\n\n\troot := NewRootCommand()\n\troot.Add(OptionSpec{Name: \"--from-parent -p\", Type: \"count\", Depth: 1})\n\tchild1 := root.AddSubCommand(&Command{Name: \"child1\"})\n\tchild1.Add(OptionSpec{Name: \"--choices\", Choices: \"a b c\"})\n\tchild1.Add(OptionSpec{Name: \"--simple-string -s\"})\n\tchild1.Add(OptionSpec{Name: \"--set-me -b\", Type: \"bool-set\"})\n\tchild1.Add(OptionSpec{Name: \"--int -i\", Type: \"int\"})\n\tchild1.Add(OptionSpec{Name: \"--float\", Type: \"float\"})\n\tchild1.Add(OptionSpec{Name: \"--list\", Type: \"list\"})\n\tchild1.Add(OptionSpec{Name: \"--list-with-default -L\", Type: \"list\", Default: \"1 2\"})\n\tchild1.SubCommandIsOptional = true\n\tgc1 := child1.AddSubCommand(&Command{Name: \"gc1\"})\n\n\trt(\n\t\tchild1, \"test --from-parent child1 -ps ss --choices b --from-parent one two\",\n\t\t&options{SimpleString: \"ss\", Choices: \"b\", FromParent: 3},\n\t\t\"one\", \"two\",\n\t)\n\trt(child1, \"test child1\", &options{})\n\trt(child1, \"test child1 --set-me one\", &options{SetMe: true}, \"one\")\n\trt(child1, \"test child1 --set-me=y one\", &options{SetMe: true}, \"one\")\n\trt(child1, \"test child1 --set-me=n one\", &options{SetMe: false}, \"one\")\n\trt(child1, \"test child1 --set-me=n --set-me one\", &options{SetMe: true}, \"one\")\n\trt(child1, \"test child1 --set-me=y -b=n one\", &options{SetMe: false}, \"one\")\n\trt(child1, \"test child1 --set-me --simple-string=foo one\", &options{SimpleString: \"foo\", SetMe: true}, \"one\")\n\trt(child1, \"test child1 --set-me --simp=foo one\", &options{SimpleString: \"foo\", SetMe: true}, \"one\")\n\trt(child1, \"test child1 --set-me --simple-string= one\", &options{SetMe: true}, \"one\")\n\trt(child1, \"test child1 --int -3 --simple-s -s --float=3.3\", &options{SimpleString: \"-s\", Int: -3, Float: 3.3})\n\trt(child1, \"test child1 -bi=3 --float=3.3\", &options{SetMe: true, Int: 3, Float: 3.3})\n\trt(child1, \"test child1 --list -3 -p --list one\", &options{FromParent: 1, List: []string{\"-3\", \"one\"}})\n\trt(child1, \"test child1 -L 3 -L 4\", &options{ListWithDefault: []string{`1`, `2`, `3`, `4`}})\n\trt(gc1, \"test -p child1 -p gc1 xxx\", &empty_options{}, \"xxx\")\n\n\t_, err := child1.ParseArgs(strings.Split(\"test child1 --choices x\", \" \"))\n\tif err == nil {\n\t\tt.Fatalf(\"Invalid choice not caught\")\n\t}\n\troot.ResetAfterParseArgs()\n\tgc1.ParseArgs(strings.Split(\"test child1 -p gc1 xxx\", \" \"))\n\terr = gc1.GetOptionValues(&base_options{})\n\tif err == nil {\n\t\tt.Fatalf(\"Invalid choice not caught\")\n\t}\n}\n"
  },
  {
    "path": "tools/cli/wcswidth_kitten.go",
    "content": "package cli\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype cpos struct {\n\tx, y int\n}\n\nfunc cpos_from_report(csi string) (ans cpos, err error) {\n\tbefore, after, found := strings.Cut(csi, \";\")\n\tif !found {\n\t\treturn ans, fmt.Errorf(\"Malformed Cursor Position Report from terminal with no ;\")\n\t}\n\tif ans.y, err = strconv.Atoi(before); err != nil {\n\t\treturn ans, fmt.Errorf(\"Malformed Cursor Position Report from terminal: %s\", csi)\n\t}\n\tif ans.x, err = strconv.Atoi(after); err != nil {\n\t\treturn ans, fmt.Errorf(\"Malformed Cursor Position Report from terminal: %s\", csi)\n\t}\n\t// convert 1-based indexing to zero based indexing\n\tans.x--\n\tans.y--\n\treturn\n}\n\ntype test_struct struct {\n\tdescription               string\n\tnum                       int\n\texpected_cursor_positions []int\n\tactual_cursor_positions   []cpos\n\tpayload                   string\n\ttester                    func(actual_cursor_positions []cpos, screen_width int) string\n\tpayload_gen               func(width_in_cells int) string\n}\n\nconst cursor_position_report = \"\\x1b[6n\"\nconst reset_line = \"\\r\\x1b[K\"\n\nfunc wrap_gen(width_in_cells int) string {\n\treturn strings.Repeat(\" \", width_in_cells-2) + \"\\U0001f1ee\" + cursor_position_report + \"\\U0001f1f3\" + cursor_position_report\n}\n\nfunc wrap_tester(actual_cursor_positions []cpos, screen_width int) string {\n\tif actual_cursor_positions[0].x != actual_cursor_positions[1].x {\n\t\treturn fmt.Sprintf(\"The cursor moved after adding a combining char from: %v -> %v\", actual_cursor_positions[0], actual_cursor_positions[1])\n\t}\n\treturn \"\"\n}\n\nfunc run_tests(tests []*test_struct) (err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnum_reports := 0\n\texpected_num_reports := 0\n\tgen_payload := func(screen_width int) string {\n\t\tbuf := strings.Builder{}\n\t\tbuf.WriteString(loop.PENDING_UPDATE.EscapeCodeToSet())\n\t\tfor _, t := range tests {\n\t\t\tpayload := t.payload\n\t\t\tif t.payload_gen != nil {\n\t\t\t\tpayload = t.payload_gen(screen_width)\n\t\t\t}\n\t\t\texpected_num_reports += strings.Count(payload, cursor_position_report)\n\t\t\tbuf.WriteString(payload)\n\t\t\tbuf.WriteString(reset_line)\n\t\t\tif buf.Len() > 512*1024 {\n\t\t\t\tbuf.WriteString(loop.PENDING_UPDATE.EscapeCodeToReset())\n\t\t\t\tbuf.WriteString(loop.PENDING_UPDATE.EscapeCodeToSet())\n\t\t\t}\n\t\t}\n\t\tbuf.WriteString(loop.PENDING_UPDATE.EscapeCodeToReset())\n\t\tbuf.WriteString(\"\\x1b[c\")\n\t\treturn buf.String()\n\t}\n\n\tprint_para := func(text string) {\n\t\tsz, _ := lp.ScreenSize()\n\t\tfor _, line := range style.WrapTextAsLines(text, int(sz.WidthCells), style.WrapOptions{Trim_whitespace: true}) {\n\t\t\tlp.Println(line)\n\t\t}\n\t\tlp.Println()\n\t}\n\tscreen_width := 80\n\tlp.OnInitialize = func() (string, error) {\n\t\tsz, _ := lp.ScreenSize()\n\t\tscreen_width = int(sz.WidthCells)\n\t\tprint_para(\"These tests work by sending text to the terminal and then querying it for its cursor position. Every test is thus different strings sent to the terminal along with a list of expected cursor positions after each string. A failure means the actual cursor position was different from the expected one. A failure where the first expected cursor position is correct but subsequent ones are not, means that the complete string was rendered at the correct width but individual graphemes from the string were not.\")\n\t\tprint_para(\"The individual test descriptions use the character ÷ to indicate a position where a break is expected to occur and the character × to indicate a position where no break should happen. \")\n\t\tlp.Printf(\"Running %d tests, please wait...\\n\", len(tests))\n\t\tlp.SaveCursorPosition()\n\t\tlp.SetCursorVisible(false)\n\n\t\tlp.QueueWriteString(gen_payload(screen_width))\n\t\treturn \"\", err\n\t}\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorVisible(true)\n\t\tlp.RestoreCursorPosition()\n\t\tlp.ClearToEndOfScreen()\n\t\treturn \"\"\n\t}\n\tcurrent_test_idx := 0\n\tlp.OnEscapeCode = func(typ loop.EscapeCodeType, data []byte) error {\n\t\tif typ == loop.CSI {\n\t\t\tswitch data[len(data)-1] {\n\t\t\tcase 'c':\n\t\t\t\tlp.Quit(0)\n\t\t\tcase 'R':\n\t\t\t\tif idx := bytes.IndexByte(data, ';'); idx > -1 {\n\t\t\t\t\tif cpos, err := cpos_from_report(utils.UnsafeBytesToString(data[:len(data)-1])); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnum_reports++\n\t\t\t\t\t\tt := tests[current_test_idx]\n\t\t\t\t\t\tif len(t.actual_cursor_positions) >= len(t.expected_cursor_positions) && current_test_idx+1 < len(tests) {\n\t\t\t\t\t\t\tcurrent_test_idx += 1\n\t\t\t\t\t\t\tt = tests[current_test_idx]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tt.actual_cursor_positions = append(t.actual_cursor_positions, cpos)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tif err = lp.Run(); err != nil {\n\t\treturn err\n\t}\n\tif num_reports != expected_num_reports {\n\t\treturn fmt.Errorf(\"Terminal did not report the cursor position as many times as expected. %d != %d\", expected_num_reports, num_reports)\n\t}\n\treturn show_results(tests, screen_width)\n}\n\nfunc show_results(tests []*test_struct, screen_width int) (err error) {\n\tnum_failures := 0\n\tfor _, t := range tests {\n\t\tdiff := \"\"\n\t\tif t.tester == nil {\n\t\t\tac := make([]int, len(t.actual_cursor_positions))\n\t\t\tfor i, cp := range t.actual_cursor_positions {\n\t\t\t\tac[i] = cp.x\n\t\t\t}\n\t\t\tdiff = cmp.Diff(t.expected_cursor_positions, ac)\n\t\t} else {\n\t\t\tdiff = t.tester(t.actual_cursor_positions, screen_width)\n\t\t}\n\t\tif diff != \"\" {\n\t\t\tfmt.Printf(\"\\x1b[31mTest number %d failed\\x1b[39m: %s\\n\", t.num, t.description)\n\t\t\tfmt.Println(diff)\n\t\t\tnum_failures++\n\t\t}\n\t}\n\tif num_failures > 0 {\n\t\terr = fmt.Errorf(\"%d out of %d tests failed.\", num_failures, len(tests))\n\t} else {\n\t\tfmt.Println(\"All tests passed!\")\n\t}\n\treturn\n}\n\nfunc has_control_chars(text string) bool {\n\tfor _, ch := range text {\n\t\tif ch < ' ' {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc create_tests(gb_tests []kitty.GraphemeBreakTest, width_in_cells int) (tests []*test_struct) {\n\tfor _, t := range gb_tests {\n\t\ttext := strings.Join(t.Data, \"\")\n\t\trt, _ := json.Marshal(text)\n\t\tdesc := fmt.Sprintf(\"Unicode GraphemeBreakTest: Text: %s Expected breaks:\\n%s\", string(rt), t.Comment)\n\t\tif has_control_chars(text) {\n\t\t\tcontinue\n\t\t}\n\t\tbuf := strings.Builder{}\n\t\tbuf.WriteString(\" \" + text + cursor_position_report + reset_line + \" \")\n\t\texpected_cursor_positions := []int{1 + wcswidth.Stringwidth(text)}\n\t\t// Now test cursor position after each individual grapheme\n\t\tpos := 0\n\t\tfor _, grapheme := range t.Data {\n\t\t\tbuf.WriteString(grapheme + cursor_position_report)\n\t\t\tpos += wcswidth.Stringwidth(grapheme)\n\t\t\texpected_cursor_positions = append(expected_cursor_positions, 1+pos)\n\t\t}\n\t\ttest := test_struct{num: len(tests) + 1, description: desc, payload: buf.String(), expected_cursor_positions: expected_cursor_positions}\n\t\ttests = append(tests, &test)\n\t}\n\ttest := test_struct{\n\t\tnum: len(tests) + 1, description: \"Check that combining characters are merged into last cell even when cursor is on the next line\",\n\t\tpayload_gen: wrap_gen, tester: wrap_tester}\n\ttests = append(tests, &test)\n\treturn\n}\n\nfunc main(allowed_tests *utils.Set[int]) (rc int, err error) {\n\tterm, err := tty.OpenControllingTerm()\n\tif err != nil {\n\t\treturn 1, fmt.Errorf(\"Could not open controlling terminal with error: %w\", err)\n\t}\n\tsz, err := term.GetSize()\n\tterm.Close()\n\tif err != nil {\n\t\treturn 1, fmt.Errorf(\"Could not get size of controlling terminal with error: %w\", err)\n\t}\n\twidth_in_cells := int(sz.Col)\n\tif gb_tests, err := kitty.LoadGraphemeBreakTests(); err == nil {\n\t\ttests := create_tests(gb_tests, width_in_cells)\n\t\tif allowed_tests.Len() > 0 {\n\t\t\ttemp := make([]*test_struct, 0, len(tests))\n\t\t\tfor _, t := range tests {\n\t\t\t\tif allowed_tests.Has(t.num) {\n\t\t\t\t\ttemp = append(temp, t)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttests = temp\n\t\t}\n\t\tif err = run_tests(tests); err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t} else {\n\t\treturn 1, err\n\t}\n\treturn\n}\n\nfunc WcswidthKittenEntryPoint(root *Command) {\n\troot.AddSubCommand(&Command{\n\t\tName:            \"__width_test__\",\n\t\tUsage:           \"[test number to run...]\",\n\t\tHelpText:        \"Test the terminal for compliance with the kitty text-sizing specification's splitting of text into cells. You can optionally specify specific test numbers to run.\",\n\t\tHidden:          true,\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *Command, args []string) (rc int, err error) {\n\t\t\tallowed_tests := utils.NewSet[int]()\n\t\t\tfor _, arg := range args {\n\t\t\t\tif x, err := strconv.Atoi(arg); err == nil {\n\t\t\t\t\tallowed_tests.Add(x)\n\t\t\t\t} else {\n\t\t\t\t\treturn 1, fmt.Errorf(\"%s is not a valid test number\", arg)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn main(allowed_tests)\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "tools/cli/zsh.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage cli\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\nfunc zsh_completion_script(commands []string) (string, error) {\n\treturn `#compdef kitty\n\n_kitty() {\n    (( ${+commands[kitten]} )) || builtin return\n    builtin local src cmd=${(F)words:0:$CURRENT}\n    # Send all words up to the word the cursor is currently on.\n    src=$(builtin command kitten __complete__ zsh \"_matcher=$_matcher\" <<<$cmd) || builtin return\n    builtin eval \"$src\"\n}\n\nif (( $+functions[compdef] )); then\n    compdef _kitty kitty\n    compdef _kitty clone-in-kitty\n    compdef _kitty kitten\nfi\n`, nil\n}\n\nfunc shell_input_parser(data []byte, shell_state map[string]string) ([][]string, error) {\n\traw := string(data)\n\tnew_word := strings.HasSuffix(raw, \"\\n\\n\")\n\traw = strings.TrimRight(raw, \"\\n \\t\")\n\tscanner := utils.NewLineScanner(raw)\n\twords := make([]string, 0, 32)\n\tfor scanner.Scan() {\n\t\twords = append(words, scanner.Text())\n\t}\n\tif new_word {\n\t\twords = append(words, \"\")\n\t}\n\treturn [][]string{words}, nil\n}\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc zsh_input_parser(data []byte, shell_state map[string]string) ([][]string, error) {\n\tmatcher := shell_state[\"_matcher\"]\n\tq := \"\"\n\tif matcher != \"\" {\n\t\tq = strings.Split(strings.ToLower(matcher), \":\")[0][:1]\n\t}\n\tif q != \"\" && strings.Contains(\"lrbe\", q) {\n\t\t// this is zsh anchor based matching\n\t\t// https://zsh.sourceforge.io/Doc/Release/Completion-Widgets.html#Completion-Matching-Control\n\t\t// can be specified with matcher-list and some systems do it by default,\n\t\t// for example, Debian, which adds the following to zshrc\n\t\t// zstyle ':completion:*' matcher-list '' 'm:{a-z}={A-Z}' 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=* l:|=*'\n\t\t// For some reason that I dont have the\n\t\t// time/interest to figure out, returning completion candidates for\n\t\t// these matcher types break completion, so just abort in this case.\n\t\treturn nil, fmt.Errorf(\"ZSH anchor based matching active, cannot complete. Turn it off by setting zstyle :completion: to something that does not use anchors in your ~/.zshrc\")\n\t}\n\treturn shell_input_parser(data, shell_state)\n}\n\nfunc (self *Match) FormatForCompletionList(max_word_len int, f *markup.Context, screen_width int) string {\n\tword := self.Word\n\tdesc := self.Description\n\tif desc == \"\" {\n\t\treturn word\n\t}\n\tword_len := wcswidth.Stringwidth(word)\n\tline, _, _ := strings.Cut(strings.TrimSpace(desc), \"\\n\")\n\tdesc = f.Prettify(line)\n\n\tmultiline := false\n\tmax_desc_len := screen_width - max_word_len - 3\n\tif word_len > max_word_len {\n\t\tmultiline = true\n\t} else {\n\t\tword += strings.Repeat(\" \", max_word_len-word_len)\n\t}\n\tif wcswidth.Stringwidth(desc) > max_desc_len {\n\t\tdesc = style.WrapTextAsLines(desc, max_desc_len-2, style.WrapOptions{})[0] + \"…\"\n\t}\n\tif multiline {\n\t\treturn word + \"\\n\" + strings.Repeat(\" \", max_word_len+2) + desc\n\t}\n\treturn word + \"  \" + desc\n}\n\nfunc serialize(completions *Completions, f *markup.Context, screen_width int) ([]byte, error) {\n\toutput := strings.Builder{}\n\tif completions.Delegate.NumToRemove > 0 {\n\t\tfor i := 0; i < completions.Delegate.NumToRemove; i++ {\n\t\t\tfmt.Fprintln(&output, \"shift words\")\n\t\t\tfmt.Fprintln(&output, \"(( CURRENT-- ))\")\n\t\t}\n\t\tservice := utils.QuoteStringForSH(completions.Delegate.Command)\n\t\tfmt.Fprintln(&output, \"words[1]=\"+service)\n\t\tfmt.Fprintln(&output, \"_normal -p\", service)\n\t} else {\n\t\tfor _, mg := range completions.Groups {\n\t\t\tcmd := strings.Builder{}\n\t\t\tescape_ourselves := mg.IsFiles // zsh quoting quotes a leading ~/ in filenames which is wrong\n\t\t\tcmd.WriteString(\"compadd -U \")\n\t\t\tif escape_ourselves {\n\t\t\t\tcmd.WriteString(\"-Q \")\n\t\t\t}\n\t\t\tcmd.WriteString(\"-J \")\n\t\t\tcmd.WriteString(utils.QuoteStringForSH(mg.Title))\n\t\t\tcmd.WriteString(\" -X \")\n\t\t\tcmd.WriteString(utils.QuoteStringForSH(\"%B\" + mg.Title + \"%b\"))\n\t\t\tif mg.NoTrailingSpace {\n\t\t\t\tcmd.WriteString(\" -S ''\")\n\t\t\t}\n\t\t\tif mg.IsFiles {\n\t\t\t\tcmd.WriteString(\" -f\")\n\t\t\t}\n\t\t\tlcp := mg.remove_common_prefix()\n\t\t\tif lcp != \"\" {\n\t\t\t\tcmd.WriteString(\" -p \")\n\t\t\t\tcmd.WriteString(utils.QuoteStringForSH(lcp))\n\t\t\t}\n\t\t\tif mg.has_descriptions() {\n\t\t\t\tfmt.Fprintln(&output, \"compdescriptions=(\")\n\t\t\t\tlimit := mg.max_visual_word_length(16)\n\t\t\t\tfor _, m := range mg.Matches {\n\t\t\t\t\tfmt.Fprintln(&output, utils.QuoteStringForSH(wcswidth.StripEscapeCodes(m.FormatForCompletionList(limit, f, screen_width))))\n\t\t\t\t}\n\t\t\t\tfmt.Fprintln(&output, \")\")\n\t\t\t\tcmd.WriteString(\" -l -d compdescriptions\")\n\t\t\t}\n\t\t\tcmd.WriteString(\" --\")\n\t\t\tfor _, m := range mg.Matches {\n\t\t\t\tcmd.WriteString(\" \")\n\t\t\t\tw := m.Word\n\t\t\t\tif escape_ourselves {\n\t\t\t\t\tw = utils.EscapeSHMetaCharacters(m.Word)\n\t\t\t\t}\n\t\t\t\tcmd.WriteString(utils.QuoteStringForSH(w))\n\t\t\t}\n\t\t\tfmt.Fprintln(&output, cmd.String(), \";\")\n\t\t}\n\t}\n\t// debugf(\"%#v\", output.String())\n\treturn []byte(output.String()), nil\n}\n\nfunc zsh_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {\n\tvar f *markup.Context\n\tscreen_width := 80\n\tctty, err := tty.OpenControllingTerm()\n\tif err == nil {\n\t\tsz, err := ctty.GetSize()\n\t\tctty.Close()\n\t\tif err == nil {\n\t\t\tscreen_width = int(sz.Col)\n\t\t}\n\t}\n\tf = markup.New(false) // ZSH freaks out if there are escape codes in the description strings\n\treturn serialize(completions[0], f, screen_width)\n}\n\nfunc init() {\n\tcompletion_scripts[\"zsh\"] = zsh_completion_script\n\tinput_parsers[\"zsh\"] = zsh_input_parser\n\toutput_serializers[\"zsh\"] = zsh_output_serializer\n}\n"
  },
  {
    "path": "tools/cmd/at/complete_actions.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc complete_actions(completions *cli.Completions, word string, arg_num int) {\n\tif arg_num < 2 {\n\t\tscanner := utils.NewLineScanner(KittyActionNames)\n\t\tmg := completions.AddMatchGroup(\"Actions\")\n\t\tfor scanner.Scan() {\n\t\t\tline := strings.TrimSpace(scanner.Text())\n\t\t\tif line != \"\" && strings.HasPrefix(line, word) {\n\t\t\t\tmg.AddMatch(line)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc complete_kitty_override(completions *cli.Completions, word string, arg_num int) {\n\tmg := completions.AddMatchGroup(\"Config directives\")\n\tmg.NoTrailingSpace = true\n\tscanner := utils.NewLineScanner(kitty.OptionNames)\n\tfor scanner.Scan() {\n\t\tline := strings.TrimSpace(scanner.Text())\n\t\tif strings.HasPrefix(line, word) {\n\t\t\tmg.AddMatch(line + \"=\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/cmd/at/env.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"strings\"\n)\n\nfunc parse_key_val_args(args []string) map[escaped_string]escaped_string {\n\tans := make(map[escaped_string]escaped_string, len(args))\n\tfor _, arg := range args {\n\t\tkey, value, found := strings.Cut(arg, \"=\")\n\t\tif found {\n\t\t\tans[escaped_string(key)] = escaped_string(value)\n\t\t} else {\n\t\t\tans[escaped_string(key+\"=\")] = \"\"\n\t\t}\n\t}\n\treturn ans\n}\n"
  },
  {
    "path": "tools/cmd/at/launch.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nvar _ = fmt.Print\n\nfunc copy_local_env(copy_env bool) []string {\n\tif copy_env {\n\t\treturn os.Environ()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tools/cmd/at/main.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf16\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/crypto\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/base85\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n)\n\nconst lowerhex = \"0123456789abcdef\"\n\nvar ProtocolVersion [3]int = [3]int{0, 26, 0}\n\ntype password struct {\n\tval    string\n\tis_set bool\n}\n\ntype GlobalOptions struct {\n\tto_network, to_address     string\n\tpassword                   password\n\tto_address_is_from_env_var bool\n\talready_setup              bool\n}\n\nvar global_options GlobalOptions\n\nfunc expand_ansi_c_escapes_in_args(args ...string) (escaped_string, error) {\n\tfor i, x := range args {\n\t\targs[i] = shlex.ExpandANSICEscapes(x)\n\t}\n\treturn escaped_string(strings.Join(args, \" \")), nil\n}\n\nfunc escape_list_of_strings(args []string) []escaped_string {\n\tans := make([]escaped_string, len(args))\n\tfor i, x := range args {\n\t\tans[i] = escaped_string(x)\n\t}\n\treturn ans\n}\n\nfunc set_payload_string_field(io_data *rc_io_data, field, data string) {\n\tpayload_interface := reflect.ValueOf(&io_data.rc.Payload).Elem()\n\tstruct_in_interface := reflect.New(payload_interface.Elem().Type()).Elem()\n\tstruct_in_interface.Set(payload_interface.Elem()) // copies the payload to struct_in_interface\n\tstruct_in_interface.FieldByName(field).SetString(data)\n\tpayload_interface.Set(struct_in_interface) // copies struct_in_interface back to payload\n}\n\nfunc get_pubkey(encoded_key string) (encryption_version string, pubkey []byte, err error) {\n\tif encoded_key == \"\" {\n\t\tencoded_key = os.Getenv(\"KITTY_PUBLIC_KEY\")\n\t\tif encoded_key == \"\" {\n\t\t\terr = fmt.Errorf(\"Password usage requested but KITTY_PUBLIC_KEY environment variable is not available\")\n\t\t\treturn\n\t\t}\n\t}\n\tencryption_version, encoded_key, found := strings.Cut(encoded_key, \":\")\n\tif !found {\n\t\terr = fmt.Errorf(\"KITTY_PUBLIC_KEY environment variable does not have a : in it\")\n\t\treturn\n\t}\n\tif encryption_version != kitty.RC_ENCRYPTION_PROTOCOL_VERSION {\n\t\terr = fmt.Errorf(\"KITTY_PUBLIC_KEY has unknown version, if you are running on a remote system, update kitty on this system\")\n\t\treturn\n\t}\n\tpubkey = make([]byte, base85.DecodedLen(len(encoded_key)))\n\tn, err := base85.Decode(pubkey, []byte(encoded_key))\n\tif err == nil {\n\t\tpubkey = pubkey[:n]\n\t}\n\treturn\n}\n\ntype escaped_string string\n\nfunc (s escaped_string) MarshalJSON() ([]byte, error) {\n\t// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON\n\t// we additionally escape all non-ascii chars so they can be safely transmitted inside an escape code\n\tsrc := utf16.Encode([]rune(s))\n\tbuf := make([]byte, 0, len(src)+128)\n\ta := func(x ...byte) {\n\t\tbuf = append(buf, x...)\n\t}\n\ta('\"')\n\tfor _, r := range src {\n\t\tif ' ' <= r && r <= 126 {\n\t\t\tif r == '\\\\' || r == '\"' {\n\t\t\t\tbuf = append(buf, '\\\\')\n\t\t\t}\n\t\t\tbuf = append(buf, byte(r))\n\t\t\tcontinue\n\t\t}\n\t\tswitch r {\n\t\tcase '\\n':\n\t\t\ta('\\\\', 'n')\n\t\tcase '\\t':\n\t\t\ta('\\\\', 't')\n\t\tcase '\\r':\n\t\t\ta('\\\\', 'r')\n\t\tcase '\\f':\n\t\t\ta('\\\\', 'f')\n\t\tcase '\\b':\n\t\t\ta('\\\\', 'b')\n\t\tdefault:\n\t\t\ta('\\\\', 'u')\n\t\t\tfor s := 12; s >= 0; s -= 4 {\n\t\t\t\ta(lowerhex[r>>uint(s)&0xF])\n\t\t\t}\n\t\t}\n\t}\n\ta('\"')\n\treturn buf, nil\n}\n\nfunc simple_serializer(rc *utils.RemoteControlCmd) (ans []byte, err error) {\n\treturn json.Marshal(rc)\n}\n\ntype serializer_func func(rc *utils.RemoteControlCmd) ([]byte, error)\n\nfunc create_serializer(password password, encoded_pubkey string, io_data *rc_io_data) (err error) {\n\tio_data.serializer = simple_serializer\n\tif password.is_set {\n\t\tencryption_version, pubkey, err := get_pubkey(encoded_pubkey)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tio_data.serializer = func(rc *utils.RemoteControlCmd) (ans []byte, err error) {\n\t\t\tec, err := crypto.Encrypt_cmd(rc, global_options.password.val, pubkey, encryption_version)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\treturn json.Marshal(ec)\n\t\t}\n\t\tif io_data.timeout < 120*time.Second {\n\t\t\tio_data.timeout = 120 * time.Second\n\t\t}\n\t}\n\treturn nil\n}\n\ntype ResponseData struct {\n\tas_str    string\n\tis_string bool\n}\n\nfunc (self *ResponseData) UnmarshalJSON(data []byte) error {\n\tif bytes.HasPrefix(data, []byte(\"\\\"\")) {\n\t\tself.is_string = true\n\t\treturn json.Unmarshal(data, &self.as_str)\n\t}\n\tif bytes.Equal(data, []byte(\"true\")) {\n\t\tself.as_str = \"True\"\n\t} else if bytes.Equal(data, []byte(\"false\")) {\n\t\tself.as_str = \"False\"\n\t} else {\n\t\tself.as_str = string(data)\n\t}\n\treturn nil\n}\n\ntype Response struct {\n\tOk        bool         `json:\"ok\"`\n\tData      ResponseData `json:\"data\"`\n\tError     string       `json:\"error,omitempty\"`\n\tTraceback string       `json:\"tb,omitempty\"`\n}\n\ntype rc_io_data struct {\n\tcmd                        *cli.Command\n\trc                         *utils.RemoteControlCmd\n\tserializer                 serializer_func\n\ton_key_event               func(lp *loop.Loop, ke *loop.KeyEvent) error\n\tstring_response_is_err     bool\n\thandle_response            func(data []byte) error\n\ttimeout                    time.Duration\n\tmultiple_payload_generator func(io_data *rc_io_data) (bool, error)\n\n\tchunks_done bool\n}\n\nfunc (self *rc_io_data) next_chunk() (chunk []byte, err error) {\n\tif self.chunks_done {\n\t\treturn make([]byte, 0), nil\n\t}\n\tif self.multiple_payload_generator != nil {\n\t\tis_last, err := self.multiple_payload_generator(self)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif is_last {\n\t\t\tself.chunks_done = true\n\t\t}\n\t\treturn self.serializer(self.rc)\n\t}\n\tself.chunks_done = true\n\treturn self.serializer(self.rc)\n}\n\nfunc get_response(do_io func(io_data *rc_io_data) ([]byte, error), io_data *rc_io_data) (ans *Response, err error) {\n\tserialized_response, err := do_io(io_data)\n\tif err != nil {\n\t\tif errors.Is(err, os.ErrDeadlineExceeded) && io_data.rc.Async != \"\" {\n\t\t\tio_data.rc.Payload = nil\n\t\t\tio_data.rc.CancelAsync = true\n\t\t\tio_data.multiple_payload_generator = nil\n\t\t\tio_data.rc.NoResponse = true\n\t\t\tio_data.chunks_done = false\n\t\t\t_, _ = do_io(io_data)\n\t\t\terr = fmt.Errorf(\"Timed out waiting for a response from kitty\")\n\t\t}\n\t\treturn nil, err\n\t}\n\tif len(serialized_response) == 0 {\n\t\tif io_data.rc.NoResponse {\n\t\t\tres := Response{Ok: true}\n\t\t\tans = &res\n\t\t\treturn\n\t\t}\n\t\terr = fmt.Errorf(\"Received empty response from kitty\")\n\t\treturn\n\t}\n\tvar response Response\n\terr = json.Unmarshal(serialized_response, &response)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"Invalid response received from kitty, unmarshalling error: %w\", err)\n\t\treturn\n\t}\n\tans = &response\n\treturn\n}\n\nvar running_shell = false\n\ntype exit_error struct {\n\texit_code int\n}\n\nfunc (m *exit_error) Error() string {\n\treturn fmt.Sprintf(\"Subprocess exit with code: %d\", m.exit_code)\n}\n\nfunc send_rc_command(io_data *rc_io_data) (err error) {\n\terr = setup_global_options(io_data.cmd)\n\tif err != nil {\n\t\treturn err\n\t}\n\twid, err := strconv.Atoi(os.Getenv(\"KITTY_WINDOW_ID\"))\n\tif err == nil && wid > 0 {\n\t\tio_data.rc.KittyWindowId = uint(wid)\n\t}\n\terr = create_serializer(global_options.password, \"\", io_data)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar response *Response\n\tresponse, err = get_response(utils.IfElse(global_options.to_network == \"\", do_tty_io, do_socket_io), io_data)\n\tif err != nil || response == nil {\n\t\treturn\n\t}\n\tif !response.Ok {\n\t\tif response.Traceback != \"\" {\n\t\t\tfmt.Fprintln(os.Stderr, response.Traceback)\n\t\t}\n\t\treturn fmt.Errorf(\"%s\", response.Error)\n\t}\n\tif io_data.handle_response != nil {\n\t\treturn io_data.handle_response(utils.UnsafeStringToBytes(response.Data.as_str))\n\t}\n\tif response.Data.is_string && io_data.string_response_is_err {\n\t\treturn fmt.Errorf(\"%s\", response.Data.as_str)\n\t}\n\tif response.Data.as_str != \"\" {\n\t\tfmt.Println(strings.TrimRight(response.Data.as_str, \"\\n \\t\"))\n\t}\n\treturn\n}\n\nfunc get_password(password string, password_file string, password_env string, use_password string) (ans password, err error) {\n\tif use_password == \"never\" {\n\t\treturn\n\t}\n\tif password != \"\" {\n\t\tans.is_set, ans.val = true, password\n\t}\n\tif !ans.is_set && password_file != \"\" {\n\t\tif password_file == \"-\" {\n\t\t\tif tty.IsTerminal(os.Stdin.Fd()) {\n\t\t\t\tp, err := tui.ReadPassword(\"Password: \", true)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn ans, err\n\t\t\t\t}\n\t\t\t\tans.is_set, ans.val = true, p\n\t\t\t} else {\n\t\t\t\tvar q []byte\n\t\t\t\tq, err = io.ReadAll(os.Stdin)\n\t\t\t\tif err == nil {\n\t\t\t\t\tans.is_set, ans.val = true, strings.TrimRight(string(q), \" \\n\\t\")\n\t\t\t\t}\n\t\t\t\tttyf, err := os.Open(tty.Ctermid())\n\t\t\t\tif err == nil {\n\t\t\t\t\terr = unix.Dup2(int(ttyf.Fd()), int(os.Stdin.Fd())) //nolint ineffassign err is returned indicating duping failed\n\t\t\t\t\tttyf.Close()\n\t\t\t\t}\n\t\t\t}\n\t\t} else if strings.HasPrefix(password_file, \"fd:\") {\n\t\t\tvar fd int\n\t\t\tif fd, err = strconv.Atoi(password_file[3:]); err == nil {\n\t\t\t\tf := os.NewFile(uintptr(fd), password_file)\n\t\t\t\tvar q []byte\n\t\t\t\tif q, err = io.ReadAll(f); err == nil {\n\t\t\t\t\tans.is_set = true\n\t\t\t\t\tans.val = string(q)\n\t\t\t\t}\n\t\t\t\tf.Close()\n\t\t\t}\n\t\t} else {\n\t\t\tvar q []byte\n\t\t\tif !filepath.IsAbs(password_file) {\n\t\t\t\tpassword_file = filepath.Join(utils.ConfigDir(), password_file)\n\t\t\t}\n\t\t\tq, err = os.ReadFile(password_file)\n\t\t\tif err == nil {\n\t\t\t\tans.is_set, ans.val = true, strings.TrimRight(string(q), \" \\n\\t\")\n\t\t\t} else {\n\t\t\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\t\t\terr = nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif !ans.is_set && password_env != \"\" {\n\t\tans.val, ans.is_set = os.LookupEnv(password_env)\n\t}\n\tif !ans.is_set && use_password == \"always\" {\n\t\tans.is_set = true\n\t\treturn ans, nil\n\t}\n\tif len(ans.val) > 1024 {\n\t\treturn ans, fmt.Errorf(\"Specified password is too long\")\n\t}\n\treturn ans, nil\n}\n\nvar all_commands []func(*cli.Command) *cli.Command = make([]func(*cli.Command) *cli.Command, 0, 64)\n\nfunc register_at_cmd(f func(*cli.Command) *cli.Command) {\n\tall_commands = append(all_commands, f)\n}\n\nfunc setup_global_options(cmd *cli.Command) (err error) {\n\tif global_options.already_setup {\n\t\treturn nil\n\t}\n\terr = cmd.GetOptionValues(&rc_global_opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif rc_global_opts.To == \"\" {\n\t\trc_global_opts.To = os.Getenv(\"KITTY_LISTEN_ON\")\n\t\tglobal_options.to_address_is_from_env_var = true\n\t}\n\tif rc_global_opts.To != \"\" {\n\t\tnetwork, address, err := utils.ParseSocketAddress(rc_global_opts.To)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tglobal_options.to_network = network\n\t\tglobal_options.to_address = address\n\t}\n\tq, err := get_password(rc_global_opts.Password, rc_global_opts.PasswordFile, rc_global_opts.PasswordEnv, rc_global_opts.UsePassword)\n\tglobal_options.password = q\n\tglobal_options.already_setup = true\n\treturn err\n\n}\n\nfunc EntryPoint(tool_root *cli.Command) *cli.Command {\n\tat_root_command := tool_root.AddSubCommand(&cli.Command{\n\t\tName:             \"@\",\n\t\tUsage:            \"[global options] [sub-command] [sub-command options] [sub-command args]\",\n\t\tShortDescription: \"Control kitty remotely\",\n\t\tHelpText:         \"Control kitty by sending it commands. Set the allow_remote_control option in :file:`kitty.conf` for this to work. When run without any sub-commands this will start an interactive shell to control kitty.\",\n\t\tRun:              shell_main,\n\t})\n\tadd_rc_global_opts(at_root_command)\n\n\tglobal_options_group := at_root_command.OptionGroups[0]\n\n\tfor _, reg_func := range all_commands {\n\t\tc := reg_func(at_root_command)\n\t\tclone := tool_root.AddClone(\"\", c)\n\t\tclone.Name = \"@\" + c.Name\n\t\tclone.Hidden = true\n\t\tclone.OptionGroups = append(clone.OptionGroups, global_options_group.Clone(clone))\n\t}\n\treturn at_root_command\n}\n"
  },
  {
    "path": "tools/cmd/at/main_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/crypto\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"testing\"\n)\n\nfunc TestEncodeJSON(t *testing.T) {\n\ttests := map[string]string{\n\t\t\"a b\\nc\\td\\a\": `a b\\nc\\td\\u0007`,\n\t\t\"•\":           `\\u2022`,\n\t\t`a\"b\\c`:       `a\\\"b\\\\c`,\n\t\t\"\\U0001f123\":  `\\ud83c\\udd23`,\n\t}\n\tvar s escaped_string\n\tfor x, expected := range tests {\n\t\ts = escaped_string(x)\n\t\texpected = `\"` + expected + `\"`\n\t\tactualb, _ := s.MarshalJSON()\n\t\tactual := string(actualb)\n\t\tif expected != actual {\n\t\t\tt.Fatalf(\"Failed for %#v\\n%#v != %#v\", x, expected, actual)\n\t\t}\n\t}\n\n}\n\nfunc TestCommandToJSON(t *testing.T) {\n\tpv := fmt.Sprint(ProtocolVersion[0], \",\", ProtocolVersion[1], \",\", ProtocolVersion[2])\n\trc, err := create_rc_ls([]string{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tmarshal := func(rc *utils.RemoteControlCmd) string {\n\t\tq, err := json.Marshal(rc)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\treturn string(q)\n\t}\n\n\ttest := func(rc *utils.RemoteControlCmd, rest string) {\n\t\tq := marshal(rc)\n\t\texpected := `{\"cmd\":\"` + rc.Cmd + `\",\"version\":[` + pv + `]`\n\t\texpected += rest + \"}\"\n\t\tif q != expected {\n\t\t\tt.Fatalf(\"expected != actual: %#v != %#v\", expected, q)\n\t\t}\n\t}\n\ttest(rc, \"\")\n}\n\nfunc TestRCSerialization(t *testing.T) {\n\tio_data := rc_io_data{}\n\terr := create_serializer(password{\"\", false}, \"\", &io_data)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar ver = [3]int{1, 2, 3}\n\trc := utils.RemoteControlCmd{\n\t\tCmd: \"test\", Version: ver,\n\t}\n\tsimple := func(expected string) {\n\t\tactual, err := io_data.serializer(&rc)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tas := string(actual)\n\t\tif as != expected {\n\t\t\tt.Fatalf(\"Incorrect serialization: %s != %s\", expected, as)\n\t\t}\n\t}\n\tsimple(string(`{\"cmd\":\"test\",\"version\":[1,2,3]}`))\n\tpubkey_b, _, err := crypto.KeyPair(\"1\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tpubkey, err := crypto.EncodePublicKey(pubkey_b, \"1\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\terr = create_serializer(password{\"tpw\", true}, pubkey, &io_data)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\traw, err := io_data.serializer(&rc)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar ec utils.EncryptedRemoteControlCmd\n\terr = json.Unmarshal([]byte(raw), &ec)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif ec.Version != ver {\n\t\tt.Fatal(\"Incorrect version in encrypted command: \", ec.Version)\n\t}\n}\n"
  },
  {
    "path": "tools/cmd/at/run.go",
    "content": "package at\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n)\n\nvar _ = fmt.Print\n\ntype run_response_data struct {\n\tStdout      string `json:\"stdout\"`\n\tStderr      string `json:\"stderr\"`\n\tExit_code   int    `json:\"exit_code\"`\n\tExit_status int    `json:\"exit_status\"`\n}\n\nfunc run_handle_response(data []byte) error {\n\tvar r run_response_data\n\tif err := json.Unmarshal(data, &r); err != nil {\n\t\treturn err\n\t}\n\tif stdout, err := base64.StdEncoding.DecodeString(r.Stdout); err == nil {\n\t\t_, _ = os.Stdout.Write(stdout)\n\t} else {\n\t\treturn err\n\t}\n\tif stderr, err := base64.StdEncoding.DecodeString(r.Stderr); err == nil {\n\t\t_, _ = os.Stderr.Write(stderr)\n\t} else {\n\t\treturn err\n\t}\n\tif r.Exit_code != 0 {\n\t\treturn &exit_error{r.Exit_code}\n\t}\n\treturn nil\n}\n\nfunc read_run_data(io_data *rc_io_data, args []string, payload *run_json_type) (func(io_data *rc_io_data) (bool, error), error) {\n\tis_first_call := true\n\tis_tty := tty.IsTerminal(os.Stdin.Fd())\n\tbuf := make([]byte, 4096)\n\tcmdline := make([]escaped_string, len(args))\n\tfor i, s := range args {\n\t\tcmdline[i] = escaped_string(s)\n\t}\n\tpayload.Cmdline = cmdline\n\tio_data.handle_response = run_handle_response\n\n\treturn func(io_data *rc_io_data) (bool, error) {\n\t\tif is_first_call {\n\t\t\tis_first_call = false\n\t\t} else {\n\t\t\tio_data.rc.Stream = false\n\t\t}\n\t\tbuf = buf[:cap(buf)]\n\t\tvar n int\n\t\tvar err error\n\t\tif is_tty {\n\t\t\tbuf = buf[:0]\n\t\t\terr = io.EOF\n\t\t} else {\n\t\t\tn, err = os.Stdin.Read(buf)\n\t\t\tif err != nil && err != io.EOF {\n\t\t\t\treturn false, err\n\t\t\t}\n\t\t\tbuf = buf[:n]\n\t\t}\n\t\tset_payload_data(io_data, base64.StdEncoding.EncodeToString(buf))\n\t\tif err == io.EOF {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, nil\n\t}, nil\n\n}\n"
  },
  {
    "path": "tools/cmd/at/scroll_window.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc parse_scroll_amount(amt string) ([]any, error) {\n\tvar ans = make([]any, 2)\n\tif amt == \"start\" || amt == \"end\" {\n\t\tans[0] = amt\n\t\tans[1] = nil\n\t} else {\n\t\tpages := strings.Contains(amt, \"p\")\n\t\tunscroll := strings.Contains(amt, \"u\")\n\t\tprompt := strings.Contains(amt, \"r\")\n\t\tvar mult float64 = 1\n\t\tif strings.HasSuffix(amt, \"-\") && !unscroll {\n\t\t\tmult = -1\n\t\t}\n\t\tq, err := strconv.ParseFloat(strings.TrimRight(amt, \"+-plur\"), 64)\n\t\tif err != nil {\n\t\t\treturn ans, err\n\t\t}\n\t\tif (unscroll || prompt) && q != float64(int(q)) {\n\t\t\treturn ans, fmt.Errorf(\"The number must be an integer\")\n\t\t}\n\t\tans[0] = q * mult\n\t\tif pages {\n\t\t\tans[1] = \"p\"\n\t\t} else if unscroll {\n\t\t\tans[1] = \"u\"\n\t\t} else if prompt {\n\t\t\tans[1] = \"r\"\n\t\t} else {\n\t\t\tans[1] = \"l\"\n\t\t}\n\t}\n\treturn ans, nil\n}\n"
  },
  {
    "path": "tools/cmd/at/send_text.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n)\n\nvar end_reading_from_stdin = errors.New(\"end reading from STDIN\")\nvar waiting_on_stdin = errors.New(\"wait for key events from STDIN\")\n\nfunc make_file_gen(f *os.File) func(*rc_io_data) (bool, error) {\n\tchunk := make([]byte, 2048)\n\tfile_gen := func(io_data *rc_io_data) (bool, error) {\n\t\tn, err := f.Read(chunk)\n\t\tif err != nil && !errors.Is(err, io.EOF) {\n\t\t\treturn false, err\n\t\t}\n\t\tset_payload_data(io_data, \"base64:\"+base64.StdEncoding.EncodeToString(chunk[:n]))\n\t\treturn n == 0 || errors.Is(err, io.EOF), nil\n\t}\n\treturn file_gen\n\n}\nfunc parse_send_text(io_data *rc_io_data, args []string) error {\n\tgenerators := make([]func(io_data *rc_io_data) (bool, error), 0, 1)\n\n\tif len(args) > 0 {\n\t\tfor i, arg := range args {\n\t\t\targs[i] = shlex.ExpandANSICEscapes(arg)\n\t\t}\n\t\ttext := strings.Join(args, \" \")\n\t\ttext_gen := func(io_data *rc_io_data) (bool, error) {\n\t\t\tlimit := min(len(text), 2048)\n\t\t\tset_payload_data(io_data, \"base64:\"+base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(text[:limit])))\n\t\t\ttext = text[limit:]\n\t\t\treturn len(text) == 0, nil\n\t\t}\n\t\tgenerators = append(generators, text_gen)\n\t}\n\n\tif options_send_text.FromFile != \"\" {\n\t\tf, err := os.Open(options_send_text.FromFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgenerators = append(generators, make_file_gen(f))\n\t}\n\n\tif options_send_text.Stdin {\n\t\tif tty.IsTerminal(os.Stdin.Fd()) {\n\t\t\tpending_key_events := make([]string, 0, 1)\n\n\t\t\tio_data.on_key_event = func(lp *loop.Loop, ke *loop.KeyEvent) error {\n\t\t\t\tke.Handled = true\n\t\t\t\tif ke.MatchesPressOrRepeat(\"ctrl+d\") {\n\t\t\t\t\treturn end_reading_from_stdin\n\t\t\t\t}\n\t\t\t\tbs := \"kitty-key:\" + base64.StdEncoding.EncodeToString([]byte(ke.AsCSI()))\n\t\t\t\tpending_key_events = append(pending_key_events, bs)\n\t\t\t\tif ke.Text != \"\" {\n\t\t\t\t\tlp.QueueWriteString(ke.Text)\n\t\t\t\t} else if ke.MatchesPressOrRepeat(\"backspace\") {\n\t\t\t\t\tlp.QueueWriteString(\"\\x08\\x1b[P\")\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tkey_gen := func(io_data *rc_io_data) (bool, error) {\n\t\t\t\tif len(pending_key_events) > 0 {\n\t\t\t\t\tpayload := io_data.rc.Payload.(send_text_json_type)\n\t\t\t\t\tpayload.Exclude_active = true\n\t\t\t\t\tio_data.rc.Payload = payload\n\t\t\t\t\tset_payload_data(io_data, pending_key_events[0])\n\t\t\t\t\tpending_key_events = pending_key_events[1:]\n\t\t\t\t\treturn false, nil\n\t\t\t\t}\n\t\t\t\treturn false, waiting_on_stdin\n\t\t\t}\n\t\t\tgenerators = append(generators, key_gen)\n\t\t} else {\n\t\t\tgenerators = append(generators, make_file_gen(os.Stdin))\n\t\t}\n\t}\n\n\tio_data.multiple_payload_generator = func(io_data *rc_io_data) (bool, error) {\n\t\tif len(generators) == 0 {\n\t\t\tset_payload_data(io_data, \"text:\")\n\t\t\treturn true, nil\n\t\t}\n\t\tfinished, err := generators[0](io_data)\n\t\tif finished {\n\t\t\tgenerators = generators[1:]\n\t\t\tfinished = len(generators) == 0\n\t\t}\n\t\treturn finished, err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "tools/cmd/at/set_background_opacity.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport \"strconv\"\n\nfunc parse_opacity(arg string) (float64, error) {\n\tans, err := strconv.ParseFloat(arg, 64)\n\tif err != nil {\n\t\treturn 0, nil\n\t}\n\treturn max(0, min(ans, 1)), nil\n}\n"
  },
  {
    "path": "tools/cmd/at/set_colors.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n)\n\nvar nullable_colors = map[string]bool{\n\t// generated by gen-config.py do not edit\n\t// NULLABLE_COLORS_START\n\t\"active_border_color\":                  true,\n\t\"cursor\":                               true,\n\t\"cursor_text_color\":                    true,\n\t\"cursor_trail_color\":                   true,\n\t\"selection_background\":                 true,\n\t\"selection_foreground\":                 true,\n\t\"tab_bar_background\":                   true,\n\t\"tab_bar_margin_color\":                 true,\n\t\"visual_bell_color\":                    true,\n\t\"window_title_bar_active_background\":   true,\n\t\"window_title_bar_active_foreground\":   true,\n\t\"window_title_bar_inactive_background\": true,\n\t\"window_title_bar_inactive_foreground\": true,\n\t// NULLABLE_COLORS_END\n}\n\nfunc set_color_in_color_map(key, val string, ans map[string]any, check_nullable, skip_nullable bool) error {\n\tif val == \"none\" {\n\t\tif check_nullable && !nullable_colors[key] {\n\t\t\tif skip_nullable {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"The color %s cannot be set to none\", key)\n\t\t}\n\t\tans[key] = nil\n\t} else if key == \"transparent_background_colors\" {\n\t\tans[key] = val\n\t} else {\n\t\tcol, err := style.ParseColor(val)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s is not a valid color\", val)\n\t\t}\n\t\tans[key] = col.AsRGB()\n\t}\n\treturn nil\n}\n\nfunc parse_colors_and_files(args []string) (map[string]any, error) {\n\tans := make(map[string]any, len(args))\n\tfor _, arg := range args {\n\t\tkey, val, found := strings.Cut(strings.ToLower(arg), \"=\")\n\t\tif found {\n\t\t\terr := set_color_in_color_map(key, val, ans, true, false)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t} else {\n\t\t\tpath := utils.Expanduser(arg)\n\t\t\tf, err := os.Open(path)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tdefer f.Close()\n\t\t\tscanner := bufio.NewScanner(f)\n\t\t\tfor scanner.Scan() {\n\t\t\t\tkey, val, found := strings.Cut(scanner.Text(), \" \")\n\t\t\t\tif found {\n\t\t\t\t\tset_color_in_color_map(strings.ToLower(key), strings.ToLower(strings.TrimSpace(val)), ans, true, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn ans, nil\n}\n"
  },
  {
    "path": "tools/cmd/at/set_font_size.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"bytes\"\n\t\"strconv\"\n)\n\nfunc parse_set_font_size(arg string, payload *set_font_size_json_type) error {\n\tif len(arg) > 0 && (bytes.IndexByte([]byte{'+', '-', '/', '*'}, arg[0]) > -1) {\n\t\tpayload.Increment_op = arg[:1]\n\t\targ = arg[1:]\n\t}\n\tval, err := strconv.ParseFloat(arg, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpayload.Size = val\n\treturn nil\n}\n"
  },
  {
    "path": "tools/cmd/at/set_spacing.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc parse_set_spacing(args []string) (map[string]any, error) {\n\tans := make(map[string]any, len(args))\n\tmapper := make(map[string][]string, 32)\n\ttypes := [2]string{\"margin\", \"padding\"}\n\tfor _, q := range types {\n\t\tmapper[q] = []string{q + \"-left\", q + \"-top\", q + \"-right\", q + \"-bottom\"}\n\t\tmapper[q+\"-h\"] = []string{q + \"-left\", q + \"-right\"}\n\t\tmapper[q+\"-v\"] = []string{q + \"-top\", q + \"-bottom\"}\n\t\tmapper[q+\"-left\"] = []string{q + \"-left\"}\n\t\tmapper[q+\"-right\"] = []string{q + \"-right\"}\n\t\tmapper[q+\"-top\"] = []string{q + \"-top\"}\n\t\tmapper[q+\"-bottom\"] = []string{q + \"-bottom\"}\n\t}\n\tfor _, arg := range args {\n\t\tk, v, found := strings.Cut(arg, \"=\")\n\t\tif !found {\n\t\t\treturn nil, fmt.Errorf(\"%s is not a valid setting\", arg)\n\t\t}\n\t\tk = strings.ToLower(k)\n\t\tv = strings.ToLower(v)\n\t\twhich, found := mapper[k]\n\t\tif !found {\n\t\t\treturn nil, fmt.Errorf(\"%s is not a valid edge specification\", k)\n\t\t}\n\t\tif v == \"default\" {\n\t\t\tfor _, q := range which {\n\t\t\t\tans[q] = nil\n\t\t\t}\n\t\t} else {\n\t\t\tval, err := strconv.ParseFloat(v, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%s is not a number\", v)\n\t\t\t}\n\t\t\tfor _, q := range which {\n\t\t\t\tans[q] = val\n\t\t\t}\n\t\t}\n\n\t}\n\treturn ans, nil\n}\n"
  },
  {
    "path": "tools/cmd/at/set_tab_color.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar valid_color_names = map[string]bool{\"active_fg\": true, \"active_bg\": true, \"inactive_fg\": true, \"inactive_bg\": true}\n\nfunc parse_tab_colors(args []string) (map[string]any, error) {\n\tans := make(map[string]any, len(args))\n\tfor _, arg := range args {\n\t\tkey, val, found := strings.Cut(strings.ToLower(arg), \"=\")\n\t\tif !found {\n\t\t\treturn nil, fmt.Errorf(\"%s is not a valid setting\", arg)\n\t\t}\n\t\tif !valid_color_names[key] {\n\t\t\treturn nil, fmt.Errorf(\"%s is not a valid color name\", key)\n\t\t}\n\t\terr := set_color_in_color_map(key, val, ans, false, false)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn ans, nil\n}\n"
  },
  {
    "path": "tools/cmd/at/set_window_logo.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"image\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nfunc set_payload_data(io_data *rc_io_data, data string) {\n\tset_payload_string_field(io_data, \"Data\", data)\n}\n\nfunc read_window_logo(io_data *rc_io_data, path string) (func(io_data *rc_io_data) (bool, error), error) {\n\tif strings.ToLower(path) == \"none\" {\n\t\tio_data.rc.Stream = false\n\t\treturn func(io_data *rc_io_data) (bool, error) {\n\t\t\tset_payload_data(io_data, \"-\")\n\t\t\treturn true, nil\n\t\t}, nil\n\t}\n\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar image_data_stream io.Reader\n\timage_data_stream = f\n\tconfig, format, ierr := image.DecodeConfig(f)\n\tif ierr != nil {\n\t\treturn nil, fmt.Errorf(\"%s is not a supported image format\", path)\n\t}\n\tf.Seek(0, 0)\n\n\tif format != \"png\" {\n\t\tf.Seek(0, 0)\n\t\timg, _, err := image.Decode(f)\n\t\tif err != nil {\n\t\t\tf.Close()\n\t\t}\n\t\tf.Close()\n\t\tb := bytes.Buffer{}\n\t\tb.Grow(config.Height * config.Width * 4)\n\t\terr = images.Encode(&b, img, \"image/png\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\timage_data_stream = &b\n\t}\n\tis_first_call := true\n\tbuf := make([]byte, 2048)\n\n\treturn func(io_data *rc_io_data) (bool, error) {\n\t\tif is_first_call {\n\t\t\tis_first_call = false\n\t\t} else {\n\t\t\tio_data.rc.Stream = false\n\t\t}\n\t\tbuf = buf[:cap(buf)]\n\t\tn, err := image_data_stream.Read(buf)\n\t\tif err != nil && err != io.EOF {\n\t\t\treturn false, err\n\t\t}\n\t\tbuf = buf[:n]\n\t\tset_payload_data(io_data, base64.StdEncoding.EncodeToString(buf))\n\t\tif err == io.EOF {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, nil\n\t}, nil\n}\n"
  },
  {
    "path": "tools/cmd/at/shell.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/readline\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n)\n\nvar _ = fmt.Print\n\nvar formatter *markup.Context\n\nconst prompt = \"🐱 \"\n\nvar ErrExec = errors.New(\"Execute command\")\n\nfunc shell_loop(rl *readline.Readline, kill_if_signaled bool) (int, error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\trl.ChangeLoopAndResetText(lp)\n\n\tlp.OnInitialize = func() (string, error) {\n\t\trl.Start()\n\t\treturn \"\", nil\n\t}\n\tlp.OnFinalize = func() string { rl.End(); return \"\" }\n\n\tlp.OnResumeFromStop = func() error {\n\t\trl.Start()\n\t\treturn nil\n\t}\n\n\tlp.OnResize = rl.OnResize\n\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\terr := rl.OnKeyEvent(event)\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tlp.Quit(0)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif err == readline.ErrAcceptInput {\n\t\t\t\tif strings.HasSuffix(rl.TextBeforeCursor(), \"\\\\\") && rl.CursorAtEndOfLine() {\n\t\t\t\t\trl.OnText(\"\\n\", false, false)\n\t\t\t\t\trl.Redraw()\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\trl.MoveCursorToEnd()\n\t\t\t\trl.Redraw()\n\t\t\t\tlp.ClearToEndOfScreen()\n\t\t\t\treturn ErrExec\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif event.Handled {\n\t\t\trl.Redraw()\n\t\t\treturn nil\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnText = func(text string, from_key_event, in_bracketed_paste bool) error {\n\t\terr := rl.OnText(text, from_key_event, in_bracketed_paste)\n\t\tif err == nil {\n\t\t\trl.Redraw()\n\t\t}\n\t\treturn err\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tif kill_if_signaled {\n\t\t\tlp.KillIfSignalled()\n\t\t\treturn 1, nil\n\t\t}\n\t\treturn 1, fmt.Errorf(\"Killed by signal: %s\", ds)\n\t}\n\treturn 0, nil\n}\n\nfunc show_basic_help() {\n\toutput := strings.Builder{}\n\tfmt.Fprintln(&output, \"Control kitty by sending it commands.\")\n\tfmt.Fprintln(&output)\n\tfmt.Fprintln(&output, formatter.Title(\"Commands\")+\":\")\n\tr := EntryPoint(cli.NewRootCommand())\n\tfor _, g := range r.SubCommandGroups {\n\t\tfor _, sc := range g.SubCommands {\n\t\t\tfmt.Fprintln(&output, \" \", formatter.Green(sc.Name))\n\t\t\tfmt.Fprintln(&output, \"   \", sc.ShortDescription)\n\t\t}\n\t}\n\tfmt.Fprintln(&output, \" \", formatter.Green(\"help\"))\n\tfmt.Fprintln(&output, \"   \", \"Show this help\")\n\tfmt.Fprintln(&output, \" \", formatter.Green(\"exit\"))\n\tfmt.Fprintln(&output, \"   \", \"Exit this shell\")\n\tcli.ShowHelpInPager(output.String())\n}\n\nfunc exec_command(at_root_command *cli.Command, rl *readline.Readline, cmdline string) bool {\n\tparsed_cmdline, err := shlex.Split(cmdline)\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"Could not parse cmdline:\", err)\n\t\treturn true\n\t}\n\tif len(parsed_cmdline) == 0 {\n\t\treturn true\n\t}\n\tcwd, _ := os.Getwd()\n\thi := readline.HistoryItem{Timestamp: time.Now(), Cmd: rl.AllText(), ExitCode: -1, Cwd: cwd}\n\tswitch parsed_cmdline[0] {\n\tcase \"exit\":\n\t\thi.ExitCode = 0\n\t\trl.AddHistoryItem(hi)\n\t\treturn false\n\tcase \"help\":\n\t\thi.ExitCode = 0\n\t\tdefer rl.AddHistoryItem(hi)\n\t\tif len(parsed_cmdline) == 1 {\n\t\t\tshow_basic_help()\n\t\t\treturn true\n\t\t}\n\t\tswitch parsed_cmdline[1] {\n\t\tcase \"exit\":\n\t\t\tfmt.Println(\"Exit this shell\")\n\t\tcase \"help\":\n\t\t\tfmt.Println(\"Show help\")\n\t\tdefault:\n\t\t\tsc := at_root_command.FindSubCommand(parsed_cmdline[1])\n\t\t\tif sc == nil {\n\t\t\t\thi.ExitCode = 1\n\t\t\t\tfmt.Fprintln(os.Stderr, \"No command named\", formatter.BrightRed(parsed_cmdline[1])+\". Type help for a list of commands\")\n\t\t\t} else {\n\t\t\t\tsc.ShowHelpWithCommandString(sc.Name)\n\t\t\t}\n\t\t}\n\t\treturn true\n\tdefault:\n\t\tif at_root_command.FindSubCommand(parsed_cmdline[0]) == nil {\n\t\t\thi.ExitCode = 1\n\t\t\tfmt.Fprintln(os.Stderr, \"No command named\", formatter.BrightRed(parsed_cmdline[0])+\". Type help for a list of commands\")\n\t\t\treturn true\n\t\t}\n\t\tcmdline := []string{\"kitten\", \"@\"}\n\t\tcmdline = append(cmdline, parsed_cmdline...)\n\t\troot := cli.NewRootCommand()\n\t\tEntryPoint(root)\n\t\thi.ExitCode = root.ExecArgs(cmdline)\n\t\thi.Duration = time.Now().Sub(hi.Timestamp)\n\t\trl.AddHistoryItem(hi)\n\t}\n\treturn true\n}\n\nfunc completions(before_cursor, after_cursor string) (ans *cli.Completions) {\n\tconst prefix = \"kitten @ \"\n\ttext := prefix + before_cursor\n\targv, position_of_last_arg := shlex.SplitForCompletion(text)\n\tif len(argv) == 0 || position_of_last_arg < len(prefix) {\n\t\treturn\n\t}\n\troot := cli.NewRootCommand()\n\tc := root.AddSubCommand(&cli.Command{Name: \"kitten\"})\n\tEntryPoint(c)\n\ta := c.FindSubCommand(\"@\")\n\n\tadd_sc := func(cmd, desc string) {\n\t\tvar x *cli.Command\n\t\tif x = a.FindSubCommand(cmd); x == nil {\n\t\t\tx = a.AddSubCommand(&cli.Command{Name: cmd})\n\t\t}\n\t\tx.ShortDescription = desc\n\t}\n\tadd_sc(\"help\", \"Show help\")\n\tadd_sc(\"exit\", \"Exit the kitty shell\")\n\troot.Validate()\n\tans = root.GetCompletions(argv, nil)\n\tans.CurrentWordIdx = position_of_last_arg - len(prefix)\n\treturn\n}\n\nfunc shell_main(cmd *cli.Command, args []string) (int, error) {\n\terr := setup_global_options(cmd)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\trunning_shell = true\n\tformatter = markup.New(true)\n\tfmt.Println(\"Welcome to the kitty shell!\")\n\tfmt.Println(\"Use\", formatter.Green(\"help\"), \"for assistance or\", formatter.Green(\"exit\"), \"to quit.\")\n\tif atwid := os.Getenv(\"KITTY_SHELL_ACTIVE_WINDOW_ID\"); atwid != \"\" {\n\t\tamsg := \"Previously active window id: \" + atwid\n\t\tos.Unsetenv(\"KITTY_SHELL_ACTIVE_WINDOW_ID\")\n\t\tif attid := os.Getenv(\"KITTY_SHELL_ACTIVE_TAB_ID\"); attid != \"\" {\n\t\t\tos.Unsetenv(\"KITTY_SHELL_ACTIVE_TAB_ID\")\n\t\t\tamsg += \" and tab id: \" + attid\n\t\t}\n\t\tfmt.Println(amsg)\n\t}\n\tvar rl *readline.Readline\n\tcombined_completer := func(before_cursor, after_cursor string) *cli.Completions {\n\t\tans := completions(before_cursor, after_cursor)\n\t\thistory := rl.HistoryCompleter(before_cursor, after_cursor)\n\t\tfor _, group := range history.Groups {\n\t\t\tans.MergeMatchGroup(group)\n\t\t}\n\t\treturn ans\n\t}\n\n\trl = readline.New(nil, readline.RlInit{Prompt: prompt, Completer: combined_completer, HistoryPath: filepath.Join(utils.CacheDir(), \"shell.history.json\")})\n\tdefer func() {\n\t\trl.Shutdown()\n\t}()\n\tfor {\n\t\trc, err := shell_loop(rl, true)\n\t\tif err != nil {\n\t\t\tif err == ErrExec {\n\t\t\t\tcmdline := rl.AllText()\n\t\t\t\tcmdline = strings.ReplaceAll(cmdline, \"\\\\\\n\", \"\")\n\t\t\t\tif !exec_command(cmd, rl, cmdline) {\n\t\t\t\t\treturn 0, nil\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\treturn rc, err\n\t}\n}\n"
  },
  {
    "path": "tools/cmd/at/socket_io.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nfunc write_all_to_conn(conn *net.Conn, data []byte) error {\n\tfor len(data) > 0 {\n\t\tn, err := (*conn).Write(data)\n\t\tif err != nil && errors.Is(err, io.ErrShortWrite) {\n\t\t\terr = nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdata = data[n:]\n\t}\n\treturn nil\n}\n\nfunc write_many_to_conn(conn *net.Conn, datums ...[]byte) error {\n\tfor len(datums) > 0 {\n\t\terr := write_all_to_conn(conn, datums[0])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdatums = datums[1:]\n\t}\n\treturn nil\n}\n\ntype response_reader struct {\n\tparser            wcswidth.EscapeCodeParser\n\tstorage           [utils.DEFAULT_IO_BUFFER_SIZE]byte\n\tpending_responses [][]byte\n}\n\nfunc (r *response_reader) read_response_from_conn(conn *net.Conn, timeout time.Duration) (serialized_response []byte, err error) {\n\tkeep_going := true\n\tif len(r.pending_responses) == 0 {\n\t\tr.parser.HandleDCS = func(data []byte) error {\n\t\t\tif bytes.HasPrefix(data, []byte(\"@kitty-cmd\")) {\n\t\t\t\tr.pending_responses = append(r.pending_responses, append([]byte{}, data[len(\"@kitty-cmd\"):]...))\n\t\t\t\tkeep_going = false\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tbuf := r.storage[:]\n\t\tfor keep_going {\n\t\t\tvar n int\n\t\t\t(*conn).SetDeadline(time.Now().Add(timeout))\n\t\t\tn, err = (*conn).Read(buf)\n\t\t\tif err != nil {\n\t\t\t\tkeep_going = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tr.parser.Parse(buf[:n])\n\t\t}\n\t}\n\tif len(r.pending_responses) > 0 {\n\t\tserialized_response = r.pending_responses[0]\n\t\tr.pending_responses = r.pending_responses[1:]\n\t}\n\treturn\n}\n\nconst cmd_escape_code_prefix = \"\\x1bP@kitty-cmd\"\nconst cmd_escape_code_suffix = \"\\x1b\\\\\"\n\nfunc run_stdin_echo_loop(conn *net.Conn, io_data *rc_io_data) (err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors)\n\tif err != nil {\n\t\treturn\n\t}\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tevent.Handled = true\n\t\terr = io_data.on_key_event(lp, event)\n\t\tif err != nil {\n\t\t\tif err == end_reading_from_stdin {\n\t\t\t\tlp.Quit(0)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tchunk, err := io_data.next_chunk()\n\t\tif err != nil {\n\t\t\tif err == waiting_on_stdin {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\terr = write_many_to_conn(conn, []byte(cmd_escape_code_prefix), chunk, []byte(cmd_escape_code_suffix))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\terr = lp.Run()\n\tif err == nil {\n\t\tlp.KillIfSignalled()\n\t}\n\treturn err\n}\n\nfunc simple_socket_io(conn *net.Conn, io_data *rc_io_data) (serialized_response []byte, err error) {\n\tr := response_reader{}\n\tr.pending_responses = make([][]byte, 0, 2) // we read at most two responses\n\tfirst_escape_code_sent := false\n\twants_streaming := io_data.rc.Stream\n\tfor {\n\t\tvar chunk []byte\n\t\tchunk, err = io_data.next_chunk()\n\t\tif err != nil {\n\t\t\tif err == waiting_on_stdin {\n\t\t\t\terr := run_stdin_echo_loop(conn, io_data)\n\t\t\t\treturn make([]byte, 0), err\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif len(chunk) == 0 {\n\t\t\tbreak\n\t\t}\n\t\terr = write_many_to_conn(conn, []byte(cmd_escape_code_prefix), chunk, []byte(cmd_escape_code_suffix))\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif !first_escape_code_sent {\n\t\t\tfirst_escape_code_sent = true\n\t\t\tif wants_streaming {\n\t\t\t\tvar streaming_response []byte\n\t\t\t\tstreaming_response, err = r.read_response_from_conn(conn, io_data.timeout)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !is_stream_response(streaming_response) {\n\t\t\t\t\terr = fmt.Errorf(\"Did not receive expected streaming response\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif io_data.rc.NoResponse {\n\t\treturn\n\t}\n\treturn r.read_response_from_conn(conn, io_data.timeout)\n}\n\nfunc do_socket_io(io_data *rc_io_data) (serialized_response []byte, err error) {\n\tvar conn net.Conn\n\tif global_options.to_network == \"fd\" {\n\t\tfd, _ := strconv.Atoi(global_options.to_address)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tf := os.NewFile(uintptr(fd), \"fd:\"+global_options.to_address)\n\t\tconn, err = net.FileConn(f)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Failed to open a socket for the remote control file descriptor: %d with error: %w\", fd, err)\n\t\t}\n\t\tdefer f.Close()\n\t} else {\n\t\tnetwork := utils.IfElse(global_options.to_network == \"ip\", \"tcp\", global_options.to_network)\n\t\tconn, err = net.Dial(network, global_options.to_address)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"Failed to connect to %s:%s with error: %w\", network, global_options.to_address, err)\n\t\t\treturn\n\t\t}\n\t}\n\tdefer conn.Close()\n\treturn simple_socket_io(&conn, io_data)\n}\n"
  },
  {
    "path": "tools/cmd/at/template.go",
    "content": "//go:build exclude\n\n// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\n// Code generated by gen-go-code.py; DO NOT EDIT.\n\npackage at\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\nvar _ = strings.Join\n\ntype options_CMD_NAME_type struct {\n\tOPTIONS_DECLARATION_CODE\n}\n\nvar options_CMD_NAME options_CMD_NAME_type\n\ntype CMD_NAME_json_type struct {\n\tJSON_DECLARATION_CODE\n}\n\nfunc create_payload_CMD_NAME(io_data *rc_io_data, cmd *cli.Command, args []string) (err error) {\n\tpayload := CMD_NAME_json_type{}\n\tJSON_INIT_CODE\n\tio_data.rc.Payload = payload\n\treturn\n}\n\nfunc create_rc_CMD_NAME(args []string) (*utils.RemoteControlCmd, error) {\n\trc := utils.RemoteControlCmd{\n\t\tCmd:        \"CLI_NAME\",\n\t\tVersion:    ProtocolVersion,\n\t\tNoResponse: NO_RESPONSE_BASE,\n\t\tStream:     STREAM_WANTED,\n\t}\n\tif rc.Stream {\n\t\tstream_id, err := utils.HumanRandomId(128)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\trc.StreamId = stream_id\n\t}\n\tif IS_ASYNC {\n\t\tasync_id, err := utils.HumanRandomId(128)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\trc.Async = async_id\n\t}\n\treturn &rc, nil\n}\n\nfunc run_CMD_NAME(cmd *cli.Command, args []string) (return_code int, err error) {\n\terr = cmd.GetOptionValues(&options_CMD_NAME)\n\tif err != nil {\n\t\treturn\n\t}\n\n\trc, err := create_rc_CMD_NAME(args)\n\tif err != nil {\n\t\treturn\n\t}\n\tnrv, err := cli.GetOptionValue[bool](cmd, \"NoResponse\")\n\tif err == nil {\n\t\trc.NoResponse = nrv\n\t}\n\tvar timeout float64 = WAIT_TIMEOUT\n\trt, err := cli.GetOptionValue[float64](cmd, \"ResponseTimeout\")\n\tif err == nil {\n\t\ttimeout = rt\n\t}\n\tio_data := rc_io_data{\n\t\tcmd:                    cmd,\n\t\trc:                     rc,\n\t\ttimeout:                time.Duration(timeout * float64(time.Second)),\n\t\tstring_response_is_err: STRING_RESPONSE_IS_ERROR,\n\t}\n\terr = create_payload_CMD_NAME(&io_data, cmd, args)\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = send_rc_command(&io_data)\n\tif ee, ok := err.(*exit_error); ok && !running_shell {\n\t\treturn ee.exit_code, nil\n\t}\n\treturn\n}\n\nfunc setup_CMD_NAME(parent *cli.Command) *cli.Command {\n\tans := parent.AddSubCommand(&cli.Command{\n\t\tName:             \"CLI_NAME\",\n\t\tUsage:            \"ARGSPEC\",\n\t\tShortDescription: \"SHORT_DESC\",\n\t\tHelpText:         \"LONG_DESC\",\n\t\tRun:              run_CMD_NAME,\n\t})\n\tADD_FLAGS_CODE\n\treturn ans\n}\n\nfunc init() {\n\tregister_at_cmd(setup_CMD_NAME)\n}\n"
  },
  {
    "path": "tools/cmd/at/tty_io.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage at\n\nimport (\n\t\"encoding/json\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\ntype stream_response struct {\n\tOk     bool `json:\"ok\"`\n\tStream bool `json:\"stream\"`\n}\n\nfunc is_stream_response(serialized_response []byte) bool {\n\tvar response stream_response\n\tif len(serialized_response) > 32 {\n\t\treturn false\n\t}\n\terr := json.Unmarshal(serialized_response, &response)\n\treturn err == nil && response.Stream\n}\n\nfunc do_chunked_io(io_data *rc_io_data) (serialized_response []byte, err error) {\n\tserialized_response = make([]byte, 0)\n\t// we cant do inbandresize notification as in the --no-response case the\n\t// command can cause a resize and the loop can quit before the notification\n\t// arrives, leading to the notification being sent to whatever is executed\n\t// after us. Similarly no focus tracking.\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoInBandResizeNotifications, loop.NoFocusTracking)\n\tif io_data.on_key_event != nil {\n\t\tlp.FullKeyboardProtocol()\n\t} else {\n\t\tlp.NoKeyboardStateChange()\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconst (\n\t\tBEFORE_FIRST_ESCAPE_CODE_SENT = iota\n\t\tWAITING_FOR_STREAMING_RESPONSE\n\t\tSENDING\n\t\tWAITING_FOR_RESPONSE\n\t)\n\tstate := BEFORE_FIRST_ESCAPE_CODE_SENT\n\tvar last_received_data_at time.Time\n\tvar check_for_timeout func(timer_id loop.IdType) error\n\twants_streaming := false\n\n\tcheck_for_timeout = func(timer_id loop.IdType) (err error) {\n\t\tif state != WAITING_FOR_RESPONSE && state != WAITING_FOR_STREAMING_RESPONSE {\n\t\t\treturn\n\t\t}\n\t\tif io_data.on_key_event != nil {\n\t\t\treturn\n\t\t}\n\t\ttime_since_last_received_data := time.Since(last_received_data_at)\n\t\tif time_since_last_received_data >= io_data.timeout {\n\t\t\treturn os.ErrDeadlineExceeded\n\t\t}\n\t\t_, err = lp.AddTimer(io_data.timeout-time_since_last_received_data, false, check_for_timeout)\n\t\treturn\n\t}\n\n\ttransition_to_read := func() {\n\t\tif state == WAITING_FOR_RESPONSE && io_data.rc.NoResponse {\n\t\t\tlp.Quit(0)\n\t\t}\n\t\tlast_received_data_at = time.Now()\n\t\t_, _ = lp.AddTimer(io_data.timeout, false, check_for_timeout)\n\t}\n\n\tlp.OnReceivedData = func(data []byte) error {\n\t\tlast_received_data_at = time.Now()\n\t\treturn nil\n\t}\n\n\tqueue_escape_code := func(data []byte) {\n\t\tlp.QueueWriteString(cmd_escape_code_prefix)\n\t\tlp.UnsafeQueueWriteBytes(data)\n\t\tlp.QueueWriteString(cmd_escape_code_suffix)\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tchunk, err := io_data.next_chunk()\n\t\twants_streaming = io_data.rc.Stream\n\t\tif err != nil {\n\t\t\tif err == waiting_on_stdin {\n\t\t\t\treturn \"\", nil\n\t\t\t}\n\t\t\treturn \"\", err\n\t\t}\n\t\tif len(chunk) == 0 {\n\t\t\tstate = WAITING_FOR_RESPONSE\n\t\t\ttransition_to_read()\n\t\t} else {\n\t\t\tqueue_escape_code(chunk)\n\t\t}\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnWriteComplete = func(completed_write_id loop.IdType, has_pending_writes bool) error {\n\t\tif state == WAITING_FOR_STREAMING_RESPONSE || state == WAITING_FOR_RESPONSE {\n\t\t\treturn nil\n\t\t}\n\t\tchunk, err := io_data.next_chunk()\n\t\tif err != nil {\n\t\t\tif err == waiting_on_stdin {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif len(chunk) == 0 {\n\t\t\tstate = utils.IfElse(state == BEFORE_FIRST_ESCAPE_CODE_SENT && wants_streaming, WAITING_FOR_STREAMING_RESPONSE, WAITING_FOR_RESPONSE)\n\t\t\ttransition_to_read()\n\t\t} else {\n\t\t\tqueue_escape_code(chunk)\n\t\t}\n\t\tif state == BEFORE_FIRST_ESCAPE_CODE_SENT {\n\t\t\tif wants_streaming {\n\t\t\t\tstate = WAITING_FOR_STREAMING_RESPONSE\n\t\t\t\ttransition_to_read()\n\t\t\t} else {\n\t\t\t\tstate = SENDING\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif io_data.on_key_event == nil {\n\t\t\treturn nil\n\t\t}\n\t\terr := io_data.on_key_event(lp, event)\n\t\tif err == end_reading_from_stdin {\n\t\t\tlp.Quit(0)\n\t\t\treturn nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tchunk, err := io_data.next_chunk()\n\t\tif err != nil {\n\t\t\tif err == waiting_on_stdin {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tqueue_escape_code(chunk)\n\t\treturn err\n\t}\n\n\tlp.OnRCResponse = func(raw []byte) error {\n\t\tif state == WAITING_FOR_STREAMING_RESPONSE && is_stream_response(raw) {\n\t\t\tstate = SENDING\n\t\t\treturn lp.OnWriteComplete(0, false)\n\t\t}\n\t\tserialized_response = raw\n\t\tlp.Quit(0)\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tif err == nil {\n\t\tlp.KillIfSignalled()\n\t}\n\treturn\n\n}\n\nfunc do_tty_io(io_data *rc_io_data) (serialized_response []byte, err error) {\n\treturn do_chunked_io(io_data)\n}\n"
  },
  {
    "path": "tools/cmd/atexit/main.go",
    "content": "package atexit\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc main() (rc int, err error) {\n\tsignal.Ignore()\n\tdone_channel := make(chan bool)\n\tlines := []string{}\n\n\tdefer os.Stdout.Close()\n\n\tgo func() {\n\t\tscanner := bufio.NewScanner(os.Stdin)\n\t\tfor scanner.Scan() {\n\t\t\tlines = append(lines, scanner.Text())\n\t\t\tfmt.Println(len(lines))\n\t\t}\n\t\tdone_channel <- true\n\t}()\n\n\t<-done_channel\n\trc = 0\n\tfor _, line := range lines {\n\t\tif action, rest, found := strings.Cut(line, \" \"); found {\n\t\t\tif !found {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch action {\n\t\t\tcase \"unlink\":\n\t\t\t\tif err := os.Remove(rest); err != nil && !os.IsNotExist(err) {\n\t\t\t\t\tfmt.Fprintln(os.Stderr, \"Failed to unlink:\", rest, \"with error:\", err)\n\t\t\t\t\trc = 1\n\t\t\t\t}\n\t\t\tcase \"shm_unlink\":\n\t\t\t\tif err := shm.ShmUnlink(rest); err != nil && !os.IsNotExist(err) {\n\t\t\t\t\tfmt.Fprintln(os.Stderr, \"Failed to shm_unlink:\", rest, \"with error:\", err)\n\t\t\t\t\trc = 1\n\t\t\t\t}\n\t\t\tcase \"rmtree\":\n\t\t\t\tif err := os.RemoveAll(rest); err != nil && !os.IsNotExist(err) {\n\t\t\t\t\tfmt.Fprintln(os.Stderr, \"Failed to rmtree:\", rest, \"with error:\", err)\n\t\t\t\t\trc = 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc do_test() (err error) {\n\tif err = os.WriteFile(\"file\", []byte(\"moose\"), 0o600); err != nil {\n\t\treturn\n\t}\n\tif err = utils.AtExitUnlink(\"file\"); err != nil {\n\t\treturn\n\t}\n\tif err = os.Mkdir(\"dir\", 0o700); err != nil {\n\t\treturn\n\t}\n\tif err = utils.AtExitRmtree(\"dir\"); err != nil {\n\t\treturn\n\t}\n\tif err = os.WriteFile(\"dir/sf\", []byte(\"cat\"), 0o600); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc EntryPoint(root *cli.Command) {\n\troot.AddSubCommand(&cli.Command{\n\t\tName:            \"__atexit__\",\n\t\tHidden:          true,\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\tif len(args) != 0 {\n\t\t\t\tif args[0] == \"test\" {\n\t\t\t\t\trc = 0\n\t\t\t\t\tif err = do_test(); err != nil {\n\t\t\t\t\t\trc = 1\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\treturn 1, fmt.Errorf(\"Usage: __atexit__\")\n\t\t\t}\n\t\t\treturn main()\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "tools/cmd/benchmark/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage benchmark\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math/rand/v2\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/graphics\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\ntype Options struct {\n\tRepetitions    int\n\tWithScrollback bool\n\tRender         bool\n}\n\nconst reset = \"\\x1b]\\x1b\\\\\\x1bc\"\nconst ascii_printable = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ  `~!@#$%^&*()_+-=[]{}\\\\|;:'\\\",<.>/?\"\nconst control_chars = \"\\n\\t\"\nconst chinese_lorem_ipsum = `\n旦海司有幼雞讀松鼻種比門真目怪少：扒裝虎怕您跑綠蝶黃，位香法士錯乙音造活羽詞坡村目園尺封鳥朋；法松夕點我冬停雪因科對只貓息加黃住蝶，明鴨乾春呢風乙時昔孝助？小紅女父故去。\n飯躲裝個哥害共買去隻把氣年，己你校跟飛百拉！快石牙飽知唱想土人吹象毛吉每浪四又連見、欠耍外豆雞秋鼻。住步帶。\n打六申幾麼：或皮又荷隻乙犬孝習秋還何氣；幾裏活打能花是入海乙山節會。種第共後陽沒喜姐三拍弟海肖，行知走亮包，他字幾，的木卜流旦乙左杯根毛。\n您皮買身苦八手牛目地止哥彩第合麻讀午。原朋河乾種果「才波久住這香松」兄主衣快他玉坐要羽和亭但小山吉也吃耳怕，也爪斗斥可害朋許波怎祖葉卜。\n行花兩耍許車丟學「示想百吃門高事」不耳見室九星枝買裝，枝十新央發旁品丁青給，科房火；事出出孝肉古：北裝愛升幸百東鼻到從會故北「可休笑物勿三游細斗」娘蛋占犬。我羊波雨跳風。\n牛大燈兆新七馬，叫這牙後戶耳、荷北吃穿停植身玩間告或西丟再呢，他禾七愛干寺服石安：他次唱息它坐屋父見這衣發現來，苗會開條弓世者吃英定豆哭；跳風掃叫美神。\n寸再了耍休壯植己，燈錯和，蝶幾欠雞定和愛，司紅後弓第樹會金拉快喝夕見往，半瓜日邊出讀雞苦歌許開；發火院爸乙；四帶亮錯鳥洋個讀。\n`\nconst misc_unicode = `\n‘’“”‹›«»‚„ 😀😛😇😈😉😍😎😮👍👎 —–§¶†‡©®™ →⇒•·°±−×÷¼½½¾\n…µ¢£€¿¡¨´¸ˆ˜ ÀÁÂÃÄÅÆÇÈÉÊË ÌÍÎÏÐÑÒÓÔÕÖØ ŒŠÙÚÛÜÝŸÞßàá âãäåæçèéêëìí\nîïðñòóôõöøœš ùúûüýÿþªºαΩ∞ ū̀n̂o᷵H̨a̠b̡͓̐c̡͓̐X̡͓̐\n`\n\nvar opts Options\n\nfunc benchmark_data(description string, data string, opts Options) (duration time.Duration, sent_data_size int, reps int, err error) {\n\tterm, err := tty.OpenControllingTerm(tty.SetRaw)\n\tif err != nil {\n\t\treturn 0, 0, 0, err\n\t}\n\tdefer term.RestoreAndClose()\n\twrite_with_retry := func(data string) (err error) {\n\t\treturn term.WriteAllString(data)\n\t}\n\tstate := loop.TerminalStateOptions{Alternate_screen: !opts.WithScrollback}\n\tif err = write_with_retry(state.SetStateEscapeCodes() + loop.DECTCEM.EscapeCodeToReset()); err != nil {\n\t\treturn\n\t}\n\tdefer func() { _ = write_with_retry(state.ResetStateEscapeCodes() + loop.DECTCEM.EscapeCodeToSet() + reset) }()\n\tconst count = 3\n\n\tconst clear_screen = \"\\x1b[m\\x1b[H\\x1b[2J\"\n\tdesc := clear_screen + \"Running: \" + description + \"\\r\\n\"\n\tconst pause_rendering = \"\\x1b[?2026h\"\n\tconst resume_rendering = \"\\x1b[?2026l\"\n\tif !opts.Render {\n\t\tif err = write_with_retry(desc + pause_rendering); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tstart := time.Now()\n\tend_of_loop_reset := desc\n\tif !opts.Render {\n\t\tend_of_loop_reset += resume_rendering + pause_rendering\n\t}\n\tfor reps < opts.Repetitions {\n\t\tif err = write_with_retry(data); err != nil {\n\t\t\treturn\n\t\t}\n\t\tsent_data_size += len(data)\n\t\treps += 1\n\t\tif err = write_with_retry(end_of_loop_reset); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tfinalize := clear_screen + \"Waiting for response indicating parsing finished\\r\\n\"\n\tif !opts.Render {\n\t\tfinalize += resume_rendering\n\t}\n\tfinalize += strings.Repeat(\"\\x1b[5n\", count)\n\tif err = write_with_retry(finalize); err != nil {\n\t\treturn\n\t}\n\tq := []byte(strings.Repeat(\"\\x1b[0n\", count))\n\tvar read_data []byte\n\tbuf := make([]byte, 8192)\n\tfor !bytes.Contains(read_data, q) {\n\t\tn, err := term.Read(buf)\n\t\tif err != nil {\n\t\t\tif (errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EINTR)) && n == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tread_data = append(read_data, buf[:n]...)\n\t}\n\tduration = time.Since(start)\n\treturn\n}\n\nfunc random_string_of_bytes(n int, alphabet string) string {\n\tb := make([]byte, n)\n\tal := len(alphabet)\n\tfor i := range n {\n\t\tb[i] = alphabet[rand.IntN(al)]\n\t}\n\treturn utils.UnsafeBytesToString(b)\n}\n\ntype result struct {\n\tdesc        string\n\tdata_sz     int\n\tduration    time.Duration\n\trepetitions int\n}\n\nfunc simple_ascii() (r result, err error) {\n\tconst desc = \"Only ASCII chars\"\n\tdata := random_string_of_bytes(1024*2048+13, ascii_printable+control_chars)\n\tduration, data_sz, reps, err := benchmark_data(desc, data, opts)\n\tif err != nil {\n\t\treturn result{}, err\n\t}\n\treturn result{desc, data_sz, duration, reps}, nil\n}\n\nfunc unicode() (r result, err error) {\n\tconst desc = \"Unicode chars\"\n\tdata := strings.Repeat(chinese_lorem_ipsum+misc_unicode+control_chars, 1024)\n\tduration, data_sz, reps, err := benchmark_data(desc, data, opts)\n\tif err != nil {\n\t\treturn result{}, err\n\t}\n\treturn result{desc, data_sz, duration, reps}, nil\n}\n\nfunc ascii_with_csi() (r result, err error) {\n\tconst sz = 1024*1024 + 17\n\tout := make([]byte, 0, sz+48)\n\tchunk := \"\"\n\tfor len(out) < sz {\n\t\tq := rand.IntN(100)\n\t\tswitch {\n\t\tcase (q < 10):\n\t\t\tchunk = random_string_of_bytes(rand.IntN(72)+1, ascii_printable+control_chars)\n\t\tcase (10 <= q && q < 30):\n\t\t\tchunk = \"\\x1b[m\\x1b[?1h\\x1b[H\"\n\t\tcase (30 <= q && q < 40):\n\t\t\tchunk = \"\\x1b[1;2;3;4:3;31m\"\n\t\tcase (40 <= q && q < 50):\n\t\t\tchunk = \"\\x1b[38:5:24;48:2:125:136:147m\"\n\t\tcase (50 <= q && q < 60):\n\t\t\tchunk = \"\\x1b[58;5;44;2m\"\n\t\tcase (60 <= q && q < 80):\n\t\t\tchunk = \"\\x1b[m\\x1b[10A\\x1b[3E\\x1b[2K\"\n\t\tcase (80 <= q && q < 100):\n\t\t\tchunk = \"\\x1b[39m\\x1b[10`a\\x1b[100b\\x1b[?1l\"\n\t\t}\n\t\tout = append(out, utils.UnsafeStringToBytes(chunk)...)\n\t}\n\tout = append(out, \"\\x1b[m\"...)\n\tconst desc = \"CSI codes with few chars\"\n\tduration, data_sz, reps, err := benchmark_data(desc, utils.UnsafeBytesToString(out), opts)\n\tif err != nil {\n\t\treturn result{}, err\n\t}\n\treturn result{desc, data_sz, duration, reps}, nil\n}\n\nfunc images() (r result, err error) {\n\tg := graphics.GraphicsCommand{}\n\tg.SetImageId(12345)\n\tg.SetQuiet(graphics.GRT_quiet_silent)\n\tg.SetAction(graphics.GRT_action_transmit)\n\tg.SetFormat(graphics.GRT_format_rgba)\n\tconst dim = 1024\n\tg.SetDataWidth(dim)\n\tg.SetDataHeight(dim)\n\tg.DisableCompression = true // dont want to measure the speed of zlib\n\tb := strings.Builder{}\n\tb.Grow(8 * dim * dim)\n\t_ = g.WriteWithPayloadTo(&b, make([]byte, 4*dim*dim))\n\tg.SetAction(graphics.GRT_action_delete)\n\tg.SetDelete(graphics.GRT_free_by_id)\n\t_ = g.WriteWithPayloadTo(&b, nil)\n\tdata := b.String()\n\tconst desc = \"Images\"\n\tduration, data_sz, reps, err := benchmark_data(desc, data, opts)\n\tif err != nil {\n\t\treturn result{}, err\n\t}\n\treturn result{desc, data_sz, duration, reps}, nil\n}\n\nfunc long_escape_codes() (r result, err error) {\n\tdata := random_string_of_bytes(8024, ascii_printable)\n\t// OSC 6 is document reporting or XTerm special color which kitty ignores after parsing\n\tdata = strings.Repeat(\"\\x1b]6;\"+data+\"\\x07\", 1024)\n\tconst desc = \"Long escape codes\"\n\tduration, data_sz, reps, err := benchmark_data(desc, data, opts)\n\tif err != nil {\n\t\treturn result{}, err\n\t}\n\treturn result{desc, data_sz, duration, reps}, nil\n}\n\nvar divs = []time.Duration{\n\ttime.Duration(1), time.Duration(10), time.Duration(100), time.Duration(1000)}\n\nfunc round(d time.Duration, digits int) time.Duration {\n\tswitch {\n\tcase d > time.Second:\n\t\td = d.Round(time.Second / divs[digits])\n\tcase d > time.Millisecond:\n\t\td = d.Round(time.Millisecond / divs[digits])\n\tcase d > time.Microsecond:\n\t\td = d.Round(time.Microsecond / divs[digits])\n\t}\n\treturn d\n}\n\nfunc present_result(r result, col_width int) {\n\trate := float64(r.data_sz) / r.duration.Seconds()\n\trate /= 1024. * 1024.\n\tf := fmt.Sprintf(\"%%-%ds\", col_width)\n\tfmt.Printf(\"  \"+f+\" : %-10v @ \\x1b[32m%-7.1f\\x1b[m MB/s\\n\", r.desc, round(r.duration, 2), rate)\n}\n\nfunc all_benchamrks() []string {\n\treturn []string{\n\t\t\"ascii\", \"unicode\", \"csi\", \"images\", \"long_escape_codes\",\n\t}\n}\n\nfunc main(args []string) (err error) {\n\tif len(args) == 0 {\n\t\targs = all_benchamrks()\n\t}\n\tvar results []result\n\tvar r result\n\t// First warm up the terminal by getting it to render all chars so that font rendering\n\t// time is not polluting the benchmarks.\n\tw := Options{Repetitions: 1}\n\tif _, _, _, err = benchmark_data(\"Warmup\", ascii_printable+control_chars+chinese_lorem_ipsum+misc_unicode, w); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(time.Second / 2)\n\n\tif slices.Index(args, \"ascii\") >= 0 {\n\t\tif r, err = simple_ascii(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresults = append(results, r)\n\t}\n\n\tif slices.Index(args, \"unicode\") >= 0 {\n\t\tif r, err = unicode(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresults = append(results, r)\n\t}\n\n\tif slices.Index(args, \"csi\") >= 0 {\n\t\tif r, err = ascii_with_csi(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresults = append(results, r)\n\t}\n\n\tif slices.Index(args, \"long_escape_codes\") >= 0 {\n\t\tif r, err = long_escape_codes(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresults = append(results, r)\n\t}\n\n\tif slices.Index(args, \"images\") >= 0 {\n\t\tif r, err = images(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresults = append(results, r)\n\t}\n\n\tfmt.Print(reset)\n\tfmt.Println(\n\t\t\"These results measure the time it takes the terminal to fully parse all the data sent to it.\")\n\tif opts.Render {\n\t\tfmt.Println(\"Note that not all data transmitted will be displayed as input parsing is typically asynchronous with rendering in high performance terminals.\")\n\t} else {\n\t\tfmt.Println(\"Note that \\x1b[31mrendering is suppressed\\x1b[m (if the terminal supports the synchronized output escape code) to better benchmark parser performance. Use the --render flag to enable rendering.\")\n\t}\n\tfmt.Println()\n\tfmt.Println(\"Results:\")\n\tmlen := 10\n\tfor _, r := range results {\n\t\tmlen = max(mlen, len(r.desc))\n\t}\n\tfor _, r := range results {\n\t\tpresent_result(r, mlen)\n\t}\n\treturn\n}\n\nfunc EntryPoint(root *cli.Command) {\n\tsc := root.AddSubCommand(&cli.Command{\n\t\tName:             \"__benchmark__\",\n\t\tShortDescription: \"Run various benchmarks\",\n\t\tHelpText:         \"To run only particular benchmarks, specify them on the command line from the set: \" + strings.Join(all_benchamrks(), \", \") + \". Benchmarking works by sending large amount of data to the TTY device and waiting for the terminal to process the data and respond to queries sent to it in the data. By default rendering is suppressed during benchmarking to focus on parser performance. Use the --render flag to enable it, but be aware that rendering in modern terminals is typically asynchronous so it wont be properly benchmarked by this kitten.\",\n\t\tUsage:            \"[options] [optional benchmark to run ...]\",\n\t\tHidden:           true,\n\t\tRun: func(cmd *cli.Command, args []string) (ret int, err error) {\n\t\t\tif err = cmd.GetOptionValues(&opts); err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\topts.Repetitions = max(1, opts.Repetitions)\n\t\t\tif err = main(args); err != nil {\n\t\t\t\tret = 1\n\t\t\t}\n\t\t\treturn\n\t\t},\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName:    \"--repetitions\",\n\t\tDefault: \"100\",\n\t\tType:    \"int\",\n\t\tHelp:    \"The number of repetitions of each benchmark\",\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName: \"--with-scrollback\",\n\t\tType: \"bool-set\",\n\t\tHelp: \"Use the main screen instead of the alt screen so speed of scrollback is also tested\",\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName: \"--render\",\n\t\tType: \"bool-set\",\n\t\tHelp: \"Allow rendering of the data sent during tests. Note that modern terminals render asynchronously, so timings do not generally reflect render performance.\",\n\t})\n\n}\n"
  },
  {
    "path": "tools/cmd/completion/kitty.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage completion\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\tkitty_constants \"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/themes\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc complete_kitty_override(completions *cli.Completions, word string, arg_num int) {\n\tmg := completions.AddMatchGroup(\"Config directives\")\n\tmg.NoTrailingSpace = true\n\tscanner := utils.NewLineScanner(kitty_constants.OptionNames)\n\tfor scanner.Scan() {\n\t\tline := strings.TrimSpace(scanner.Text())\n\t\tif strings.HasPrefix(line, word) {\n\t\t\tmg.AddMatch(line + \"=\")\n\t\t}\n\t}\n}\n\nfunc complete_kitty_listen_on(completions *cli.Completions, word string, arg_num int) {\n\tif !strings.Contains(word, \":\") {\n\t\tmg := completions.AddMatchGroup(\"Address family\")\n\t\tmg.NoTrailingSpace = true\n\t\tfor _, q := range []string{\"unix:\", \"tcp:\"} {\n\t\t\tif strings.HasPrefix(q, word) {\n\t\t\t\tmg.AddMatch(q)\n\t\t\t}\n\t\t}\n\t} else if strings.HasPrefix(word, \"unix:\") && !strings.HasPrefix(word, \"unix:@\") {\n\t\tcli.FnmatchCompleter(\"UNIX sockets\", cli.CWD, \"*\")(completions, word[len(\"unix:\"):], arg_num)\n\t\tcompletions.AddPrefixToAllMatches(\"unix:\")\n\t}\n}\n\nfunc complete_plus_launch(completions *cli.Completions, word string, arg_num int) {\n\tif arg_num == 1 {\n\t\tcli.FnmatchCompleter(\"Python scripts\", cli.CWD, \"*.py\")(completions, word, arg_num)\n\t\tif strings.HasPrefix(word, \":\") {\n\t\t\texes := cli.CompleteExecutablesInPath(word[1:])\n\t\t\tmg := completions.AddMatchGroup(\"Python scripts in PATH\")\n\t\t\tfor _, exe := range exes {\n\t\t\t\tmg.AddMatch(\":\" + exe)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tcli.FnmatchCompleter(\"Files\", cli.CWD, \"*\")(completions, word, arg_num)\n\t}\n}\n\nfunc complete_plus_runpy(completions *cli.Completions, word string, arg_num int) {\n\tif arg_num > 1 {\n\t\tcli.FnmatchCompleter(\"Files\", cli.CWD, \"*\")(completions, word, arg_num)\n\t}\n}\n\nfunc complete_plus_open(completions *cli.Completions, word string, arg_num int) {\n\tcli.FnmatchCompleter(\"Files\", cli.CWD, \"*\")(completions, word, arg_num)\n}\n\nfunc complete_themes(completions *cli.Completions, word string, arg_num int) {\n\tthemes.CompleteThemes(completions, word, arg_num)\n}\n\nfunc EntryPoint(tool_root *cli.Command) {\n\ttool_root.AddSubCommand(&cli.Command{\n\t\tName: \"__complete__\", Hidden: true,\n\t\tUsage:            \"output_type [shell state...]\",\n\t\tShortDescription: \"Generate completions for kitty commands\",\n\t\tHelpText:         \"Generate completion candidates for kitty commands. The command line is read from STDIN. output_type can be one of the supported shells: :code:`zsh`, :code:`fish`, :code:`bash`, or :code:`setup` for completion setup script following with the shell name, or :code:`json` for JSON output.\",\n\t\tRun: func(cmd *cli.Command, args []string) (ret int, err error) {\n\t\t\treturn ret, cli.GenerateCompletions(args)\n\t\t},\n\t})\n\n}\n"
  },
  {
    "path": "tools/cmd/edit_in_kitty/main.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage edit_in_kitty\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n)\n\nvar _ = fmt.Print\n\nfunc encode(x string) string {\n\treturn base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(x))\n}\n\ntype OnDataCallback = func(data_type string, data []byte) error\n\nfunc edit_loop(data_to_send string, kill_if_signaled bool, on_data OnDataCallback) (err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)\n\tif err != nil {\n\t\treturn\n\t}\n\tcurrent_text := strings.Builder{}\n\tdata := strings.Builder{}\n\tdata.Grow(4096)\n\tstarted := false\n\tcanceled := false\n\tupdate_type := \"\"\n\n\thandle_line := func(line string) error {\n\t\tif canceled {\n\t\t\treturn nil\n\t\t}\n\t\tif started {\n\t\t\tif update_type == \"\" {\n\t\t\t\tupdate_type = line\n\t\t\t} else {\n\t\t\t\tif line == \"KITTY_DATA_END\" {\n\t\t\t\t\tlp.QueueWriteString(update_type + \"\\r\\n\")\n\t\t\t\t\tif update_type == \"DONE\" {\n\t\t\t\t\t\tlp.Quit(0)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t\tb, err := base64.StdEncoding.DecodeString(data.String())\n\t\t\t\t\tdata.Reset()\n\t\t\t\t\tdata.Grow(4096)\n\t\t\t\t\tstarted = false\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\terr = on_data(update_type, b)\n\t\t\t\t\t}\n\t\t\t\t\tupdate_type = \"\"\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tdata.WriteString(line)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif line == \"KITTY_DATA_START\" {\n\t\t\t\tstarted = true\n\t\t\t\tupdate_type = \"\"\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tcheck_for_line := func() error {\n\t\tif canceled {\n\t\t\treturn nil\n\t\t}\n\t\ts := current_text.String()\n\t\tfor {\n\t\t\tidx := strings.Index(s, \"\\n\")\n\t\t\tif idx < 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\terr = handle_line(s[:idx])\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ts = s[idx+1:]\n\t\t}\n\t\tcurrent_text.Reset()\n\t\tcurrent_text.Grow(4096)\n\t\tif s != \"\" {\n\t\t\tcurrent_text.WriteString(s)\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tpos, chunk_num := 0, 0\n\t\tfor {\n\t\t\tlimit := min(pos+2048, len(data_to_send))\n\t\t\tif limit <= pos {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tlp.QueueWriteString(\"\\x1bP@kitty-edit|\" + strconv.Itoa(chunk_num) + \":\")\n\t\t\tlp.QueueWriteString(data_to_send[pos:limit])\n\t\t\tlp.QueueWriteString(\"\\x1b\\\\\")\n\t\t\tchunk_num++\n\t\t\tpos = limit\n\t\t}\n\t\tlp.QueueWriteString(\"\\x1bP@kitty-edit|\\x1b\\\\\")\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnText = func(text string, from_key_event bool, in_bracketed_paste bool) error {\n\t\tif !from_key_event {\n\t\t\tcurrent_text.WriteString(text)\n\t\t\terr = check_for_line()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tconst abort_msg = \"\\x1bP@kitty-edit|0:abort_signaled=interrupt\\x1b\\\\\\x1bP@kitty-edit|\\x1b\\\\\"\n\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") || event.MatchesPressOrRepeat(\"esc\") {\n\t\t\tevent.Handled = true\n\t\t\tcanceled = true\n\t\t\tlp.QueueWriteString(abort_msg)\n\t\t\tif !started {\n\t\t\t\treturn tui.Canceled\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn\n\t}\n\tif canceled {\n\t\treturn tui.Canceled\n\t}\n\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tfmt.Print(abort_msg)\n\t\tif kill_if_signaled {\n\t\t\tlp.KillIfSignalled()\n\t\t\treturn\n\t\t}\n\t\treturn &tui.KilledBySignal{Msg: fmt.Sprint(\"Killed by signal: \", ds), SignalName: ds}\n\t}\n\treturn\n}\n\nfunc edit_in_kitty(path string, opts *Options) (err error) {\n\tread_file, err := os.Open(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Failed to open %s for reading with error: %w\", path, err)\n\t}\n\tdefer read_file.Close()\n\tvar s unix.Stat_t\n\terr = unix.Fstat(int(read_file.Fd()), &s)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Failed to stat %s with error: %w\", path, err)\n\t}\n\tif s.Size > int64(opts.MaxFileSize)*1024*1024 {\n\t\treturn fmt.Errorf(\"File size %s is too large for performant editing\", humanize.Bytes(uint64(s.Size)))\n\t}\n\n\tfile_data, err := io.ReadAll(read_file)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Failed to read from %s with error: %w\", path, err)\n\t}\n\tread_file.Close()\n\tdata := strings.Builder{}\n\tdata.Grow(len(file_data) * 4)\n\n\tadd := func(key, val string) {\n\t\tif data.Len() > 0 {\n\t\t\tdata.WriteString(\",\")\n\t\t}\n\t\tdata.WriteString(key)\n\t\tdata.WriteString(\"=\")\n\t\tdata.WriteString(val)\n\t}\n\tadd_encoded := func(key, val string) { add(key, encode(val)) }\n\n\tif unix.Access(path, unix.R_OK|unix.W_OK) != nil {\n\t\treturn fmt.Errorf(\"%s is not readable and writeable\", path)\n\t}\n\tcwd, err := os.Getwd()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Failed to get the current working directory with error: %w\", err)\n\t}\n\tadd_encoded(\"cwd\", cwd)\n\tfor _, arg := range os.Args[2:] {\n\t\tadd_encoded(\"a\", arg)\n\t}\n\tadd(\"file_inode\", fmt.Sprintf(\"%d:%d:%d\", s.Dev, s.Ino, s.Mtim.Nano()))\n\tadd_encoded(\"file_data\", utils.UnsafeBytesToString(file_data))\n\tfmt.Println(\"Waiting for editing to be completed, press Esc to abort...\")\n\twrite_data := func(data_type string, rdata []byte) (err error) {\n\t\terr = utils.AtomicWriteFile(path, bytes.NewReader(rdata), fs.FileMode(s.Mode).Perm())\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"Failed to write data to %s with error: %w\", path, err)\n\t\t}\n\t\treturn\n\t}\n\terr = edit_loop(data.String(), true, write_data)\n\tif err != nil {\n\t\tif err == tui.Canceled {\n\t\t\treturn err\n\t\t}\n\t\treturn fmt.Errorf(\"Failed to receive edited file back from terminal with error: %w\", err)\n\t}\n\treturn\n}\n\ntype Options struct {\n\tMaxFileSize int\n}\n\nfunc EntryPoint(parent *cli.Command) *cli.Command {\n\tsc := parent.AddSubCommand(&cli.Command{\n\t\tName:             \"edit-in-kitty\",\n\t\tUsage:            \"[options] [+lnum] file-to-edit\",\n\t\tShortDescription: \"Edit a file in a kitty overlay window\",\n\t\tHelpText: \"Edit the specified file in a kitty overlay window. Works over SSH as well.\\n\\n\" +\n\t\t\t\"For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#edit-file\",\n\t\tRun: func(cmd *cli.Command, args []string) (ret int, err error) {\n\t\t\tif len(args) == 0 {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"Usage:\", cmd.Usage)\n\t\t\t\treturn 1, fmt.Errorf(\"No file to edit specified.\")\n\t\t\t}\n\n\t\t\tvar file_path string\n\t\t\tif len(args) == 1 {\n\t\t\t\tfile_path = args[0]\n\t\t\t} else if len(args) == 2 && strings.HasPrefix(args[0], \"+\") {\n\t\t\t\tvar lnum string\n\t\t\t\tlnum, file_path = args[0][1:], args[1]\n\t\t\t\tif _, err := strconv.Atoi(lnum); err != nil {\n\t\t\t\t\treturn 1, fmt.Errorf(\"Invalid line number %s\", lnum)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"Usage:\", cmd.Usage)\n\t\t\t\treturn 1, fmt.Errorf(\"Only one file to edit and optionally a line number must be specified\")\n\t\t\t}\n\n\t\t\tvar opts Options\n\t\t\terr = cmd.GetOptionValues(&opts)\n\t\t\tif err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\terr = edit_in_kitty(file_path, &opts)\n\t\t\treturn 0, err\n\t\t},\n\t})\n\tAddCloneSafeOpts(sc)\n\tsc.Add(cli.OptionSpec{\n\t\tName:    \"--max-file-size\",\n\t\tDefault: \"8\",\n\t\tType:    \"int\",\n\t\tHelp:    \"The maximum allowed size (in MB) of files to edit. Since the file data has to be base64 encoded and transmitted over the tty device, overly large files will not perform well.\",\n\t})\n\treturn sc\n}\n"
  },
  {
    "path": "tools/cmd/main.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/kittens/ssh\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/completion\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/tool\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc KittenMain(args ...string) int {\n\tdefer utils.WaitForAtexitWorkerToFinish()\n\tkrm := os.Getenv(\"KITTY_KITTEN_RUN_MODULE\")\n\tos.Unsetenv(\"KITTY_KITTEN_RUN_MODULE\")\n\tswitch krm {\n\tcase \"ssh_askpass\":\n\t\treturn ssh.RunSSHAskpass()\n\t}\n\troot := cli.NewRootCommand()\n\troot.ShortDescription = \"Fast, statically compiled implementations of various kittens (command line tools for use with kitty)\"\n\troot.HelpText = \"kitten serves as a launcher for running individual kittens. Each kitten can be run as :code:`kitten command`. The list of available kittens is given below.\"\n\troot.Usage = \"command [command options] [command args]\"\n\troot.Run = func(cmd *cli.Command, args []string) (int, error) {\n\t\tif len(args) == 0 {\n\t\t\tcmd.ShowHelp()\n\t\t\treturn 0, nil\n\t\t}\n\t\tif strings.HasSuffix(args[0], \".py\") {\n\t\t\texe := utils.KittyExe()\n\t\t\tif !filepath.IsAbs(exe) {\n\t\t\t\texe = utils.Which(exe)\n\t\t\t}\n\t\t\tif err := unix.Exec(exe, append([]string{filepath.Base(exe), \"+kitten\"}, args...), os.Environ()); err != nil {\n\t\t\t\treturn 1, fmt.Errorf(\"failed to run python kitten: %s as could not run kitty executable, with error: %w\", args[0], err)\n\t\t\t}\n\t\t}\n\t\treturn 1, fmt.Errorf(\":yellow:`%s` is not a known kitten. Use --help to get a list of known kittens.\", args[0])\n\t}\n\n\ttool.KittyToolEntryPoints(root)\n\tcompletion.EntryPoint(root)\n\n\troot.SubCommandIsOptional = true\n\treturn root.ExecArgs(args)\n}\n\nfunc main() {\n\tos.Exit(KittenMain())\n}\n"
  },
  {
    "path": "tools/cmd/mouse_demo/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage mouse_demo\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n)\n\nvar _ = fmt.Print\n\nfunc Run(args []string) (rc int, err error) {\n\tall_pointer_shapes := []loop.PointerShape{\n\t\t// start all pointer shapes (auto generated by gen-key-constants.py do not edit)\n\t\tloop.DEFAULT_POINTER,\n\t\tloop.TEXT_POINTER,\n\t\tloop.POINTER_POINTER,\n\t\tloop.HELP_POINTER,\n\t\tloop.WAIT_POINTER,\n\t\tloop.PROGRESS_POINTER,\n\t\tloop.CROSSHAIR_POINTER,\n\t\tloop.CELL_POINTER,\n\t\tloop.VERTICAL_TEXT_POINTER,\n\t\tloop.MOVE_POINTER,\n\t\tloop.E_RESIZE_POINTER,\n\t\tloop.NE_RESIZE_POINTER,\n\t\tloop.NW_RESIZE_POINTER,\n\t\tloop.N_RESIZE_POINTER,\n\t\tloop.SE_RESIZE_POINTER,\n\t\tloop.SW_RESIZE_POINTER,\n\t\tloop.S_RESIZE_POINTER,\n\t\tloop.W_RESIZE_POINTER,\n\t\tloop.EW_RESIZE_POINTER,\n\t\tloop.NS_RESIZE_POINTER,\n\t\tloop.NESW_RESIZE_POINTER,\n\t\tloop.NWSE_RESIZE_POINTER,\n\t\tloop.ZOOM_IN_POINTER,\n\t\tloop.ZOOM_OUT_POINTER,\n\t\tloop.ALIAS_POINTER,\n\t\tloop.COPY_POINTER,\n\t\tloop.NOT_ALLOWED_POINTER,\n\t\tloop.NO_DROP_POINTER,\n\t\tloop.GRAB_POINTER,\n\t\tloop.GRABBING_POINTER,\n\t\t// end all pointer shapes\n\t}\n\tall_pointer_shape_names := make([]string, len(all_pointer_shapes))\n\tcol_width := 0\n\tfor i, p := range all_pointer_shapes {\n\t\tall_pointer_shape_names[i] = p.String()\n\t\tcol_width = max(col_width, len(all_pointer_shape_names[i]))\n\t}\n\tcol_width += 1\n\n\tlp, err := loop.New()\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tlp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)\n\tvar current_mouse_event *loop.MouseEvent\n\n\tdraw_screen := func() {\n\t\tlp.StartAtomicUpdate()\n\t\tdefer lp.EndAtomicUpdate()\n\t\tlp.AllowLineWrapping(false)\n\t\tdefer lp.AllowLineWrapping(true)\n\t\tif current_mouse_event == nil {\n\t\t\tlp.ClearScreen()\n\t\t\tlp.Println(`Move the mouse or click to see mouse events`)\n\t\t\treturn\n\t\t}\n\t\tlp.ClearScreen()\n\t\tif current_mouse_event.Event_type == loop.MOUSE_LEAVE {\n\t\t\tlp.Println(\"Mouse has left the window\")\n\t\t\treturn\n\t\t}\n\t\tlp.Printf(\"Position: %d, %d (pixels)\\r\\n\", current_mouse_event.Pixel.X, current_mouse_event.Pixel.Y)\n\t\tlp.Printf(\"Cell    : %d, %d\\r\\n\", current_mouse_event.Cell.X, current_mouse_event.Cell.Y)\n\t\tlp.Printf(\"Type    : %s\\r\\n\", current_mouse_event.Event_type)\n\t\ty := 3\n\t\tif current_mouse_event.Buttons != loop.NO_MOUSE_BUTTON {\n\t\t\tlp.Println(current_mouse_event.Buttons.String())\n\t\t\ty += 1\n\t\t}\n\t\tif mods := current_mouse_event.Mods.String(); mods != \"\" {\n\t\t\tlp.Printf(\"Modifiers: %s\\r\\n\", mods)\n\t\t\ty += 1\n\t\t}\n\t\tlp.Println(\"Hover the mouse over the names below to see the shapes\")\n\t\ty += 1\n\n\t\tsw := 80\n\t\tsh := 24\n\t\tif s, err := lp.ScreenSize(); err == nil {\n\t\t\tsw = int(s.WidthCells)\n\t\t\tsh = int(s.HeightCells)\n\t\t}\n\n\t\tnum_cols := max(1, sw/col_width)\n\t\tpos := 0\n\t\tcolfmt := \"%-\" + strconv.Itoa(col_width) + \"s\"\n\t\tis_on_name := false\n\t\tvar ps loop.PointerShape\n\t\tfor y < sh && pos < len(all_pointer_shapes) {\n\t\t\tis_row := y == current_mouse_event.Cell.Y\n\t\t\tfor c := 0; c < num_cols && pos < len(all_pointer_shapes); c++ {\n\t\t\t\tname := all_pointer_shape_names[pos]\n\t\t\t\tis_hovered := false\n\t\t\t\tif is_row {\n\t\t\t\t\tstart_x := c * col_width\n\t\t\t\t\tx := current_mouse_event.Cell.X\n\t\t\t\t\tif x < start_x+len(name) && x >= start_x {\n\t\t\t\t\t\tis_on_name = true\n\t\t\t\t\t\tis_hovered = true\n\t\t\t\t\t\tps = all_pointer_shapes[pos]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif is_hovered {\n\t\t\t\t\tlp.QueueWriteString(\"\\x1b[31m\")\n\t\t\t\t}\n\t\t\t\tlp.Printf(colfmt, name)\n\t\t\t\tlp.QueueWriteString(\"\\x1b[m\")\n\t\t\t\tpos++\n\t\t\t}\n\t\t\ty += 1\n\t\t\tlp.Println()\n\t\t}\n\t\tlp.PopPointerShape()\n\t\tif is_on_name {\n\t\t\tlp.PushPointerShape(ps)\n\t\t}\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.SetWindowTitle(\"kitty mouse features demo\")\n\t\tlp.SetCursorVisible(false)\n\t\tdraw_screen()\n\t\treturn \"\", nil\n\t}\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorVisible(true)\n\t\treturn \"\"\n\t}\n\n\tlp.OnMouseEvent = func(ev *loop.MouseEvent) error {\n\t\tcurrent_mouse_event = ev\n\t\tdraw_screen()\n\t\treturn nil\n\t}\n\tlp.OnKeyEvent = func(ev *loop.KeyEvent) error {\n\t\tif ev.MatchesPressOrRepeat(\"esc\") || ev.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\t\tlp.Quit(0)\n\t\t}\n\t\treturn nil\n\t}\n\tlp.OnResize = func(old_size loop.ScreenSize, new_size loop.ScreenSize) error {\n\t\tdraw_screen()\n\t\treturn nil\n\t}\n\terr = lp.Run()\n\tif err != nil {\n\t\trc = 1\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tools/cmd/pytest/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage pytest\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/kitty/kittens/ssh\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n)\n\nvar _ = fmt.Print\n\nfunc test_integration_with_python(args []string) (rc int, err error) {\n\tswitch args[0] {\n\tdefault:\n\t\treturn 1, fmt.Errorf(\"Unknown test type: %s\", args[0])\n\tcase \"read\":\n\t\tdata, err := shm.ReadWithSizeAndUnlink(args[1])\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\t_, err = os.Stdout.Write(data)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\tcase \"write\":\n\t\tdata, err := io.ReadAll(os.Stdin)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tmmap, err := shm.CreateTemp(\"shmtest-\", uint64(len(data)+shm.NUM_BYTES_FOR_SIZE))\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tif err = shm.WriteWithSize(mmap, data, 0); err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tmmap.Close()\n\t\tfmt.Println(mmap.Name())\n\t}\n\treturn 0, nil\n}\n\nfunc shm_entry_point(root *cli.Command) {\n\troot.AddSubCommand(&cli.Command{\n\t\tName:            \"shm\",\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\treturn test_integration_with_python(args)\n\t\t},\n\t})\n\n}\nfunc EntryPoint(root *cli.Command) {\n\troot = root.AddSubCommand(&cli.Command{\n\t\tName:   \"__pytest__\",\n\t\tHidden: true,\n\t})\n\tshm_entry_point(root)\n\tssh.TestEntryPoint(root)\n}\n"
  },
  {
    "path": "tools/cmd/run_shell/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage run_shell\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/shell_integration\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\n\t\"golang.org/x/exp/slices\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\ntype Options struct {\n\tShell              string\n\tShellIntegration   string\n\tEnv                []string\n\tCwd                string\n\tInjectSelfOntoPath string\n}\n\nfunc inject_self_onto_path() {\n\tif exe, err := os.Executable(); err == nil {\n\t\tif exe_dir, err := filepath.Abs(exe); err == nil {\n\t\t\trealpath := func(x string) string {\n\t\t\t\tif ans, err := filepath.EvalSymlinks(x); err == nil {\n\t\t\t\t\treturn ans\n\t\t\t\t}\n\t\t\t\treturn x\n\t\t\t}\n\t\t\texe_dir = realpath(filepath.Dir(exe_dir))\n\t\t\tpath_items := strings.Split(os.Getenv(\"PATH\"), string(os.PathListSeparator))\n\t\t\trealpath_items := utils.Map(realpath, path_items)\n\t\t\tdone := false\n\t\t\tchanged := false\n\t\t\tis_executable_file := func(q string) bool {\n\t\t\t\tif unix.Access(q, unix.X_OK) != nil {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif s, err := os.Stat(q); err == nil && !s.IsDir() {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tfor i, x := range realpath_items {\n\t\t\t\tq := filepath.Join(x, filepath.Base(exe))\n\t\t\t\tif is_executable_file(q) {\n\t\t\t\t\t// some kitten already in path\n\t\t\t\t\tif utils.Samefile(q, exe) {\n\t\t\t\t\t\tdone = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tpath_items = slices.Insert(path_items, i, exe_dir)\n\t\t\t\t\tchanged, done = true, true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !done {\n\t\t\t\tpath_items = append(path_items, exe_dir)\n\t\t\t\tchanged = true\n\t\t\t}\n\t\t\tif changed {\n\t\t\t\tos.Setenv(\"PATH\", strings.Join(path_items, string(os.PathListSeparator)))\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc main(args []string, opts *Options) (rc int, err error) {\n\tif len(args) > 0 {\n\t\ttui.RunCommandRestoringTerminalToSaneStateAfter(args)\n\t}\n\tenv_before := os.Environ()\n\tchanged := false\n\tfor _, entry := range opts.Env {\n\t\tk, v, found := strings.Cut(entry, \"=\")\n\t\tif found {\n\t\t\tif err := os.Setenv(k, v); err != nil {\n\t\t\t\treturn 1, fmt.Errorf(\"Failed to set the env var %s with error: %w\", k, err)\n\t\t\t}\n\t\t} else {\n\t\t\tif err := os.Unsetenv(k); err != nil {\n\t\t\t\treturn 1, fmt.Errorf(\"Failed to unset the env var %s with error: %w\", k, err)\n\t\t\t}\n\t\t}\n\t\tchanged = true\n\t}\n\tif os.Getenv(\"TERM\") == \"\" {\n\t\tos.Setenv(\"TERM\", kitty.DefaultTermName)\n\t}\n\tif opts.InjectSelfOntoPath == \"always\" || (opts.InjectSelfOntoPath == \"unless-root\" && os.Geteuid() != 0) {\n\t\tinject_self_onto_path()\n\t}\n\tif term := os.Getenv(\"TERM\"); term == kitty.DefaultTermName && shell_integration.PathToTerminfoDb(term) == \"\" {\n\t\tif terminfo_dir, err := shell_integration.EnsureTerminfoFiles(); err == nil {\n\t\t\tos.Unsetenv(\"TERMINFO\")\n\t\t\texisting := os.Getenv(\"TERMINFO_DIRS\")\n\t\t\tif existing != \"\" {\n\t\t\t\texisting = string(os.PathListSeparator) + existing\n\t\t\t}\n\t\t\tos.Setenv(\"TERMINFO_DIRS\", terminfo_dir+existing)\n\t\t}\n\t}\n\terr = tui.RunShell(tui.ResolveShell(opts.Shell), tui.ResolveShellIntegration(opts.ShellIntegration), opts.Cwd)\n\tif changed {\n\t\tos.Clearenv()\n\t\tfor _, entry := range env_before {\n\t\t\tk, v, _ := strings.Cut(entry, \"=\")\n\t\t\tos.Setenv(k, v)\n\t\t}\n\t}\n\tif err != nil {\n\t\trc = 1\n\t}\n\treturn\n}\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc EntryPoint(root *cli.Command) *cli.Command {\n\tsc := root.AddSubCommand(&cli.Command{\n\t\tName:             \"run-shell\",\n\t\tUsage:            \"[options] [optional cmd to run before running the shell ...]\",\n\t\tShortDescription: \"Run the user's shell with shell integration enabled\",\n\t\tHelpText:         \"Run the users's configured shell. If the shell supports shell integration, enable it based on the user's configured shell_integration setting.\",\n\t\tRun: func(cmd *cli.Command, args []string) (ret int, err error) {\n\t\t\topts := &Options{}\n\t\t\terr = cmd.GetOptionValues(opts)\n\t\t\tif err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\treturn main(args, opts)\n\t\t},\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName: \"--shell-integration\",\n\t\tHelp: \"Specify a value for the :opt:`shell_integration` option, overriding the one from :file:`kitty.conf`.\",\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName:    \"--shell\",\n\t\tDefault: \".\",\n\t\tHelp:    \"Specify the shell command to run. The default value of :code:`.` will use the parent shell if recognized, falling back to the value of the :opt:`shell` option from :file:`kitty.conf`.\",\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName: \"--env\",\n\t\tHelp: \"Specify an env var to set before running the shell. Of the form KEY=VAL. Can be specified multiple times. If no = is present KEY is unset.\",\n\t\tType: \"list\",\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName: \"--cwd\",\n\t\tHelp: \"The working directory to use when executing the shell.\",\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName:    \"--inject-self-onto-path\",\n\t\tHelp:    \"Add the directory containing this kitten binary to PATH. Directory is added only if not already present.\",\n\t\tDefault: \"always\",\n\t\tChoices: \"always,never,unless-root\",\n\t})\n\n\treturn sc\n}\n"
  },
  {
    "path": "tools/cmd/show_error/main.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage show_error\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n)\n\nvar _ = fmt.Print\n\ntype Options struct {\n\tTitle string\n}\n\ntype Message struct {\n\tMsg       string `json:\"msg\"`\n\tTraceback string `json:\"tb\"`\n}\n\nfunc main(args []string, opts *Options) (rc int, err error) {\n\tif tty.IsTerminal(os.Stdin.Fd()) {\n\t\treturn 1, fmt.Errorf(\"Input data for this kitten must be piped as JSON to STDIN\")\n\t}\n\tdata, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tm := Message{}\n\terr = json.Unmarshal(data, &m)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tf := markup.New(true)\n\tif opts.Title != \"\" {\n\t\tfmt.Println(f.Err(opts.Title))\n\t\tfmt.Println(loop.EscapeCodeToSetWindowTitle(opts.Title))\n\t\tfmt.Println()\n\t}\n\tfmt.Println(m.Msg)\n\tshow_traceback := false\n\tif m.Traceback != \"\" {\n\t\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tlp.OnInitialize = func() (string, error) {\n\t\t\tlp.SetCursorVisible(false)\n\t\t\tlp.QueueWriteString(\"\\n\\r\\x1b[1;32mPress e to see detailed traceback or any other key to exit\\x1b[m\\r\\n\")\n\t\t\treturn \"\", nil\n\t\t}\n\t\tlp.OnFinalize = func() string {\n\t\t\tlp.SetCursorVisible(true)\n\t\t\treturn \"\"\n\t\t}\n\n\t\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\t\tif event.Type == loop.PRESS || event.Type == loop.REPEAT {\n\t\t\t\tif event.MatchesPressOrRepeat(\"e\") || event.MatchesPressOrRepeat(\"shift+e\") || event.MatchesPressOrRepeat(\"E\") {\n\t\t\t\t\tshow_traceback = true\n\t\t\t\t\tlp.Quit(0)\n\t\t\t\t} else {\n\t\t\t\t\tlp.Quit(1)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif event.MatchesPressOrRepeat(\"enter\") || event.MatchesPressOrRepeat(\"kp_enter\") || event.MatchesPressOrRepeat(\"esc\") || event.MatchesPressOrRepeat(\"ctrl+c\") || event.MatchesPressOrRepeat(\"ctrl+d\") {\n\t\t\t\tevent.Handled = true\n\t\t\t\tlp.Quit(0)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tlp.Run()\n\t\tif lp.ExitCode() == 1 {\n\t\t\treturn 0, nil\n\t\t}\n\t}\n\tif show_traceback {\n\t\tfmt.Println(m.Traceback)\n\t\tfmt.Println()\n\t}\n\ttui.HoldTillEnter(true)\n\treturn\n}\n\nfunc EntryPoint(root *cli.Command) *cli.Command {\n\tsc := root.AddSubCommand(&cli.Command{\n\t\tName:             \"__show_error__\",\n\t\tHidden:           true,\n\t\tUsage:            \"[options]\",\n\t\tShortDescription: \"Show an error message. Internal use.\",\n\t\tHelpText:         \"Show an error message. Used internally by kitty.\",\n\t\tRun: func(cmd *cli.Command, args []string) (ret int, err error) {\n\t\t\topts := &Options{}\n\t\t\terr = cmd.GetOptionValues(opts)\n\t\t\tif err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\treturn main(args, opts)\n\t\t},\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName:    \"--title\",\n\t\tDefault: \"ERROR\",\n\t\tHelp:    \"The title for the error message\",\n\t})\n\treturn sc\n}\n"
  },
  {
    "path": "tools/cmd/tool/confirm_and_run_shebang.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tool\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty/kittens/ask\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype ConfirmPolicy uint8\n\nconst (\n\tConfirmAlways = iota\n\tConfirmNever\n\tConfirmIfNeeded\n)\n\nfunc ask_for_permission(script_path string) (response string, err error) {\n\topts := &ask.Options{Type: \"choices\", Default: \"n\", Choices: []string{\"y;green:Yes\", \"n;red:No\", \"v;yellow:View\", \"e;magenta:Edit\"}}\n\n\tctx := markup.New(true)\n\topts.Message = ctx.Prettify(fmt.Sprintf(\n\t\t\"Attempting to execute the script: :yellow:`%s`\\nExecuting untrusted scripts can be dangerous. Proceed anyway?\", script_path))\n\tresponse, err = ask.GetChoices(opts)\n\treturn response, err\n}\n\nfunc ask_if_exe_allowed(exe_path string) (ok bool, err error) {\n\topts := &ask.Options{Type: \"yesno\", Default: \"n\"}\n\tctx := markup.New(true)\n\topts.Message = ctx.Prettify(fmt.Sprintf(\n\t\t\"Attempting to execute the program: :yellow:`%s`\\nExecuting untrusted programs can be dangerous. Proceed anyway?\", exe_path))\n\tresponse, err := ask.GetChoices(opts)\n\treturn response == \"y\", err\n}\n\nfunc permission_denied(script_path string) error {\n\treturn fmt.Errorf(\"Execution of %s was denied by user\", script_path)\n}\n\nfunc confirm_and_run_exe(args []string) (rc int, err error) {\n\texe := args[len(args)-1]\n\tok, err := ask_if_exe_allowed(exe)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tif ok {\n\t\texe = utils.FindExe(args[0])\n\t\tif exe == \"\" {\n\t\t\treturn 1, fmt.Errorf(\"Failed to find the script interpreter: %s\", args[0])\n\t\t}\n\t\tif err = unix.Exec(exe, []string{exe}, os.Environ()); err != nil {\n\t\t\trc = 1\n\t\t}\n\t} else {\n\t\treturn 1, permission_denied(exe)\n\t}\n\treturn\n}\n\nfunc confirm_and_run_shebang(args []string, confirm_policy ConfirmPolicy) (rc int, err error) {\n\tscript_path := args[len(args)-1]\n\tdo_confirm := true\n\tswitch confirm_policy {\n\tcase ConfirmNever:\n\t\tdo_confirm = false\n\tcase ConfirmAlways:\n\t\tdo_confirm = true\n\tcase ConfirmIfNeeded:\n\t\tdo_confirm = unix.Access(script_path, unix.X_OK) != nil\n\t}\n\tif do_confirm {\n\t\tresponse, err := ask_for_permission(script_path)\n\t\tif err != nil {\n\t\t\treturn 1, err\n\t\t}\n\t\tswitch response {\n\t\tdefault:\n\t\t\treturn 1, permission_denied(script_path)\n\t\tcase \"v\":\n\t\t\traw, err := os.ReadFile(script_path)\n\t\t\tif err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\tcli.ShowHelpInPager(utils.UnsafeBytesToString(raw))\n\t\t\t// The pager might have exited automatically if there is less than\n\t\t\t// one screen of text, so confirm manually, here, where output from\n\t\t\t// pager will still be visible.\n\t\t\tfmt.Print(\"Execute the script? (y/n): \")\n\t\t\tq, err := tty.ReadSingleByteFromTerminal()\n\t\t\tif err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\tif q != 'y' && q != 'Y' {\n\t\t\t\tfmt.Println()\n\t\t\t\treturn 1, permission_denied(script_path)\n\t\t\t}\n\t\t\tfmt.Print(\"\\x1b[H\\x1b[2J\") // clear screen\n\t\tcase \"e\":\n\t\t\texe, err := os.Executable()\n\t\t\tif err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\teditor := exec.Command(exe, \"edit-in-kitty\", script_path)\n\t\t\teditor.Stdin = os.Stdin\n\t\t\teditor.Stdout = os.Stdout\n\t\t\teditor.Stderr = os.Stderr\n\t\t\teditor.Run()\n\t\t\treturn confirm_and_run_shebang(args, ConfirmIfNeeded)\n\t\tcase \"y\":\n\t\t}\n\t}\n\texe := utils.FindExe(args[0])\n\tif exe == \"\" {\n\t\treturn 1, fmt.Errorf(\"Failed to find the script interpreter: %s\", args[0])\n\t}\n\terr = unix.Exec(exe, args, os.Environ())\n\tif err != nil {\n\t\trc = 1\n\t}\n\treturn\n}\n\nfunc run_shebang(args []string) (rc int, err error) {\n\tif len(args) < 3 {\n\t\treturn 1, fmt.Errorf(\"Usage: kitten __shebang__ confirm-exe path_to_script cmd...\")\n\t}\n\tvar confirm_policy ConfirmPolicy\n\tswitch args[0] {\n\tcase \"confirm-always\":\n\t\tconfirm_policy = ConfirmAlways\n\tcase \"confirm-never\":\n\t\tconfirm_policy = ConfirmNever\n\tcase \"confirm-if-needed\":\n\t\tconfirm_policy = ConfirmIfNeeded\n\tdefault:\n\t\treturn 1, fmt.Errorf(\"Unknown confirmation policy: %s\", args[1])\n\t}\n\tscript_path := args[1]\n\tcmd := args[2:]\n\tif len(cmd) == 1 && cmd[0] == \"__ext__\" {\n\t\text := filepath.Ext(script_path)\n\t\tif ext == \"\" || ext == \".\" {\n\t\t\treturn 1, fmt.Errorf(\"%s has no file extension so cannot be used in __ext__ mode\", script_path)\n\t\t}\n\t\tcmd = []string{ext[1:]}\n\t}\n\tf, err := os.Open(script_path)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\tscanner := bufio.NewScanner(f)\n\tfirst_line := \"\"\n\tif scanner.Scan() {\n\t\tfirst_line = scanner.Text()\n\t} else if err = scanner.Err(); err != nil {\n\t\tf.Close()\n\t\treturn 1, fmt.Errorf(\"Failed to read from %s with error: %w\", script_path, err)\n\t}\n\tf.Close()\n\tif strings.HasPrefix(first_line, \"#!\") {\n\t\tfirst_line = strings.TrimSpace(first_line[2:])\n\t\tswitch runtime.GOOS {\n\t\tcase \"darwin\":\n\t\t\tcmd = strings.Split(first_line, \" \")\n\t\tdefault:\n\t\t\tcmd = strings.SplitN(first_line, \" \", 2)\n\t\t}\n\t}\n\tcmd = append(cmd, script_path)\n\treturn confirm_and_run_shebang(cmd, confirm_policy)\n}\n"
  },
  {
    "path": "tools/cmd/tool/main.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tool\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kovidgoyal/kitty/kittens/ask\"\n\t\"github.com/kovidgoyal/kitty/kittens/choose_files\"\n\t\"github.com/kovidgoyal/kitty/kittens/choose_fonts\"\n\t\"github.com/kovidgoyal/kitty/kittens/clipboard\"\n\t\"github.com/kovidgoyal/kitty/kittens/command_palette\"\n\t\"github.com/kovidgoyal/kitty/kittens/desktop_ui\"\n\t\"github.com/kovidgoyal/kitty/kittens/diff\"\n\t\"github.com/kovidgoyal/kitty/kittens/hints\"\n\t\"github.com/kovidgoyal/kitty/kittens/hyperlinked_grep\"\n\t\"github.com/kovidgoyal/kitty/kittens/icat\"\n\t\"github.com/kovidgoyal/kitty/kittens/notify\"\n\t\"github.com/kovidgoyal/kitty/kittens/panel\"\n\t\"github.com/kovidgoyal/kitty/kittens/query_terminal\"\n\t\"github.com/kovidgoyal/kitty/kittens/quick_access_terminal\"\n\t\"github.com/kovidgoyal/kitty/kittens/show_key\"\n\t\"github.com/kovidgoyal/kitty/kittens/ssh\"\n\t\"github.com/kovidgoyal/kitty/kittens/themes\"\n\t\"github.com/kovidgoyal/kitty/kittens/transfer\"\n\t\"github.com/kovidgoyal/kitty/kittens/unicode_input\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/at\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/atexit\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/benchmark\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/edit_in_kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/mouse_demo\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/pytest\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/run_shell\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/show_error\"\n\t\"github.com/kovidgoyal/kitty/tools/cmd/update_self\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nvar _ = fmt.Print\n\nfunc KittyToolEntryPoints(root *cli.Command) {\n\troot.Add(cli.OptionSpec{\n\t\tName: \"--version\", Type: \"bool-set\", Help: \"The current kitten version.\"})\n\ttui.PrepareRootCmd(root)\n\t// @\n\tat.EntryPoint(root)\n\t// update-self\n\tupdate_self.EntryPoint(root)\n\t// edit-in-kitty\n\tedit_in_kitty.EntryPoint(root)\n\t// clipboard\n\tclipboard.EntryPoint(root)\n\t// icat\n\ticat.EntryPoint(root)\n\t// ssh\n\tssh.EntryPoint(root)\n\t// transfer\n\ttransfer.EntryPoint(root)\n\t// panel\n\tpanel.EntryPoint(root)\n\t// quick_access_terminal\n\tquick_access_terminal.EntryPoint(root)\n\t// unicode_input\n\tunicode_input.EntryPoint(root)\n\t// show_key\n\tshow_key.EntryPoint(root)\n\t// desktop_ui\n\tdesktop_ui.EntryPoint(root)\n\t// mouse_demo\n\troot.AddSubCommand(&cli.Command{\n\t\tName:             \"mouse-demo\",\n\t\tShortDescription: \"Demo the mouse handling kitty implements for terminal programs\",\n\t\tOnlyArgsAllowed:  true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\treturn mouse_demo.Run(args)\n\t\t},\n\t})\n\t// hyperlinked_grep\n\thyperlinked_grep.EntryPoint(root)\n\t// ask\n\task.EntryPoint(root)\n\t// hints\n\thints.EntryPoint(root)\n\t// diff\n\tdiff.EntryPoint(root)\n\t// notify\n\tnotify.EntryPoint(root)\n\t// themes\n\tthemes.EntryPoint(root)\n\tthemes.ParseEntryPoint(root)\n\t// run-shell\n\trun_shell.EntryPoint(root)\n\t// show_error\n\tshow_error.EntryPoint(root)\n\t// choose-fonts\n\tchoose_fonts.EntryPoint(root)\n\t// choose-files\n\tchoose_files.EntryPoint(root)\n\t// command-palette\n\tcommand_palette.EntryPoint(root)\n\t// query-terminal\n\tquery_terminal.EntryPoint(root)\n\t// __pytest__\n\tpytest.EntryPoint(root)\n\t// __hold_till_enter__\n\troot.AddSubCommand(&cli.Command{\n\t\tName:            \"__hold_till_enter__\",\n\t\tHidden:          true,\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\treturn tui.ExecAndHoldTillEnter(args)\n\t\t},\n\t})\n\t// __shebang__\n\troot.AddSubCommand(&cli.Command{\n\t\tName:            \"__shebang__\",\n\t\tHidden:          true,\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\treturn run_shebang(args)\n\t\t},\n\t})\n\t// __confirm_and_run_exe__\n\troot.AddSubCommand(&cli.Command{\n\t\tName:            \"__confirm_and_run_exe__\",\n\t\tHidden:          true,\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\treturn confirm_and_run_exe(args)\n\t\t},\n\t})\n\n\t// __convert_image__\n\timages.ConvertEntryPoint(root)\n\t// __atexit__\n\tatexit.EntryPoint(root)\n\t// __width_test__\n\tcli.WcswidthKittenEntryPoint(root)\n\t// __generate_man_pages__\n\troot.AddSubCommand(&cli.Command{\n\t\tName:            \"__generate_man_pages__\",\n\t\tHidden:          true,\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\tq := root\n\t\t\tif len(args) > 0 {\n\t\t\t\tfor _, scname := range args {\n\t\t\t\t\tsc := q.FindSubCommand(scname)\n\t\t\t\t\tif sc == nil {\n\t\t\t\t\t\treturn 1, fmt.Errorf(\"No sub command named: %s found\", scname)\n\t\t\t\t\t}\n\t\t\t\t\tif err = sc.GenerateManPages(1, true); err != nil {\n\t\t\t\t\t\treturn 1, err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err = q.GenerateManPages(1, false); err != nil {\n\t\t\t\t\trc = 1\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t},\n\t})\n\tbenchmark.EntryPoint(root)\n}\n"
  },
  {
    "path": "tools/cmd/update_self/main.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage update_self\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\ntype Options struct {\n\tFetchVersion string\n}\n\nfunc update_self(version string) (err error) {\n\texe := \"\"\n\texe, err = os.Executable()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Failed to determine path to kitten: %w\", err)\n\t}\n\texe, err = filepath.EvalSymlinks(exe)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif kitty.IsStandaloneBuild == \"\" {\n\t\treturn fmt.Errorf(\"This is not a standalone kitten executable. You must update all of kitty instead.\")\n\t}\n\trv := \"v\" + version\n\tif version == \"nightly\" {\n\t\trv = version\n\t}\n\turl_base := fmt.Sprintf(\"https://github.com/kovidgoyal/kitty/releases/download/%s\", rv)\n\tif version == \"latest\" {\n\t\turl_base = \"https://github.com/kovidgoyal/kitty/releases/latest/download\"\n\t}\n\turl := fmt.Sprintf(\"%s/kitten-%s-%s\", url_base, runtime.GOOS, runtime.GOARCH)\n\tdest, err := os.CreateTemp(filepath.Dir(exe), \"kitten.\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() { os.Remove(dest.Name()) }()\n\n\tif !tty.IsTerminal(os.Stdout.Fd()) {\n\t\tfmt.Println(\"Downloading:\", url)\n\t\terr = utils.DownloadToFile(exe, url, nil, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfmt.Println(\"Downloaded to:\", exe)\n\t} else {\n\t\terr = tui.DownloadFileWithProgress(exe, url, true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfmt.Print(\"Updated to: \")\n\treturn unix.Exec(exe, []string{\"kitten\", \"--version\"}, os.Environ())\n}\n\nfunc EntryPoint(root *cli.Command) *cli.Command {\n\tsc := root.AddSubCommand(&cli.Command{\n\t\tName:             \"update-self\",\n\t\tUsage:            \"[options]\",\n\t\tShortDescription: \"Update this kitten binary\",\n\t\tHelpText:         \"Update this kitten binary in place to the latest available version.\",\n\t\tRun: func(cmd *cli.Command, args []string) (ret int, err error) {\n\t\t\tif len(args) != 0 {\n\t\t\t\treturn 1, fmt.Errorf(\"No command line arguments are allowed\")\n\t\t\t}\n\t\t\topts := &Options{}\n\t\t\terr = cmd.GetOptionValues(opts)\n\t\t\tif err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\treturn 0, update_self(opts.FetchVersion)\n\t\t},\n\t})\n\tsc.Add(cli.OptionSpec{\n\t\tName:    \"--fetch-version\",\n\t\tDefault: \"latest\",\n\t\tHelp:    fmt.Sprintf(\"The version to fetch. The special words :code:`latest` and :code:`nightly` fetch the latest stable and nightly release respectively. Other values can be, for example: :code:`%s`.\", kitty.VersionString),\n\t})\n\treturn sc\n}\n"
  },
  {
    "path": "tools/config/api.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage config\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\n\t\"github.com/shirou/gopsutil/v4/process\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc StringToBool(x string) bool {\n\tx = strings.ToLower(x)\n\treturn x == \"y\" || x == \"yes\" || x == \"true\"\n}\n\ntype ConfigLine struct {\n\tSrc_file, Line string\n\tLine_number    int\n\tErr            error\n}\n\ntype ConfigParser struct {\n\tLineHandler     func(key, val string) error\n\tCommentsHandler func(line string) error\n\tSourceHandler   func(text, path string)\n\n\tbad_lines     []ConfigLine\n\tseen_includes map[string]bool\n\toverride_env  []string\n}\n\ntype Scanner interface {\n\tScan() bool\n\tText() string\n\tErr() error\n}\n\nfunc (self *ConfigParser) BadLines() []ConfigLine {\n\treturn self.bad_lines\n}\n\nvar key_pat = sync.OnceValue(func() *regexp.Regexp {\n\treturn regexp.MustCompile(`([a-zA-Z][a-zA-Z0-9_-]*)\\s+(.+)$`)\n})\n\nvar kitty_os = sync.OnceValue(func() string {\n\tswitch runtime.GOOS {\n\tcase \"linux\":\n\t\treturn \"linux\"\n\tcase \"freebsd\", \"netbsd\", \"openbsd\":\n\t\treturn \"bsd\"\n\tcase \"darwin\":\n\t\treturn \"macos\"\n\t}\n\treturn \"unknown\"\n})\n\nfunc geninclude(path string) (string, error) {\n\tcmd := exec.Command(path)\n\tcmd.Env = os.Environ()\n\tcmd.Env = append(cmd.Env, \"KITTY_OS=\"+kitty_os())\n\tif strings.HasSuffix(path, \".py\") && unix.Access(path, unix.X_OK) != nil {\n\t\tif utils.KittyExe() == \"\" || strings.HasPrefix(path, \":\") {\n\t\t\tcmd = exec.Command(\"python\", path)\n\t\t} else {\n\t\t\tcmd = exec.Command(utils.KittyExe(), \"+launch\", path)\n\t\t}\n\t}\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif err = cmd.Start(); err != nil {\n\t\treturn \"\", err\n\t}\n\tdata, err := io.ReadAll(stdout)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif err = cmd.Wait(); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn utils.UnsafeBytesToString(data), nil\n}\n\nfunc ExpandVars(x string) string {\n\treturn os.Expand(x, func(k string) string {\n\t\tif k == \"KITTY_OS\" {\n\t\t\treturn kitty_os()\n\t\t}\n\t\treturn os.Getenv(k)\n\t})\n}\n\nfunc (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes string, depth int) error {\n\tif self.seen_includes[name] { // avoid include loops\n\t\treturn nil\n\t}\n\tself.seen_includes[name] = true\n\n\trecurse := func(r io.Reader, nname, base_path_for_includes string) error {\n\t\tif depth > 32 {\n\t\t\treturn fmt.Errorf(\"Too many nested include directives while processing config file: %s\", name)\n\t\t}\n\t\tescanner := bufio.NewScanner(r)\n\t\treturn self.parse(escanner, nname, base_path_for_includes, depth+1)\n\t}\n\n\tmake_absolute := func(path string) (string, error) {\n\t\tif path == \"\" {\n\t\t\treturn \"\", fmt.Errorf(\"Empty include paths not allowed\")\n\t\t}\n\t\tif !filepath.IsAbs(path) {\n\t\t\tpath = filepath.Join(base_path_for_includes, path)\n\t\t}\n\t\treturn path, nil\n\t}\n\n\tlnum := 0\n\tnext_line_num := 0\n\tnext_line := \"\"\n\tvar line string\n\n\tadd_bad_line := func(err error) {\n\t\tself.bad_lines = append(self.bad_lines, ConfigLine{Src_file: name, Line: line, Line_number: lnum, Err: err})\n\t}\n\n\tfor {\n\t\tif next_line != \"\" {\n\t\t\tline = next_line\n\t\t} else {\n\t\t\tif scanner.Scan() {\n\t\t\t\tline = strings.TrimLeft(scanner.Text(), \" \\t\")\n\t\t\t\tnext_line_num++\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif line == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tlnum = next_line_num\n\t\tif scanner.Scan() {\n\t\t\tnext_line = strings.TrimLeft(scanner.Text(), \" \\t\")\n\t\t\tnext_line_num++\n\n\t\t\tfor strings.HasPrefix(next_line, `\\`) {\n\t\t\t\tline += next_line[1:]\n\t\t\t\tif scanner.Scan() {\n\t\t\t\t\tnext_line = strings.TrimLeft(scanner.Text(), \" \\t\")\n\t\t\t\t\tnext_line_num++\n\t\t\t\t} else {\n\t\t\t\t\tnext_line = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tnext_line = \"\"\n\t\t}\n\n\t\tif line[0] == '#' {\n\t\t\tif self.CommentsHandler != nil {\n\t\t\t\tif err := self.CommentsHandler(line); err != nil {\n\t\t\t\t\tadd_bad_line(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tm := key_pat().FindStringSubmatch(line)\n\t\tif len(m) < 3 {\n\t\t\tadd_bad_line(fmt.Errorf(\"Invalid config line: %#v\", line))\n\t\t\tcontinue\n\t\t}\n\t\tkey, val := m[1], m[2]\n\t\tfor i, ch := range line {\n\t\t\tif ch == ' ' || ch == '\\t' {\n\t\t\t\tkey = line[:i]\n\t\t\t\tval = strings.TrimSpace(line[i+1:])\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tswitch key {\n\t\tdefault:\n\t\t\tif err := self.LineHandler(key, val); err != nil {\n\t\t\t\tadd_bad_line(err)\n\t\t\t}\n\t\tcase \"include\", \"globinclude\", \"envinclude\", \"geninclude\":\n\t\t\tvar includes []string\n\t\t\tval = ExpandVars(val)\n\t\t\tswitch key {\n\t\t\tcase \"include\":\n\t\t\t\tif aval, err := make_absolute(val); err == nil {\n\t\t\t\t\tincludes = []string{aval}\n\t\t\t\t} else {\n\t\t\t\t\tadd_bad_line(err)\n\t\t\t\t}\n\t\t\tcase \"globinclude\":\n\t\t\t\taval, err := make_absolute(val)\n\t\t\t\tif err == nil {\n\t\t\t\t\tmatches, err := filepath.Glob(aval)\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tincludes = matches\n\t\t\t\t\t} else {\n\t\t\t\t\t\tadd_bad_line(err)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tadd_bad_line(err)\n\t\t\t\t}\n\t\t\tcase \"geninclude\":\n\t\t\t\tif aval, err := make_absolute(val); err == nil {\n\t\t\t\t\tif g, err := geninclude(aval); err == nil {\n\t\t\t\t\t\tif err := recurse(strings.NewReader(g), \"<gen: \"+val+\">\", base_path_for_includes); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tadd_bad_line(err)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tadd_bad_line(err)\n\t\t\t\t}\n\t\t\tcase \"envinclude\":\n\t\t\t\tenv := utils.IfElse(self.override_env == nil, os.Environ(), self.override_env)\n\t\t\t\tfor _, x := range env {\n\t\t\t\t\tkey, eval, _ := strings.Cut(x, \"=\")\n\t\t\t\t\tis_match, err := filepath.Match(val, key)\n\t\t\t\t\tif is_match && err == nil {\n\t\t\t\t\t\terr := recurse(strings.NewReader(eval), \"<env var: \"+key+\">\", base_path_for_includes)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(includes) > 0 {\n\t\t\t\tfor _, incpath := range includes {\n\t\t\t\t\tif raw, err := os.ReadFile(incpath); err == nil {\n\t\t\t\t\t\tif err := recurse(bytes.NewReader(raw), incpath, filepath.Dir(incpath)); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if !errors.Is(err, fs.ErrNotExist) {\n\t\t\t\t\t\tadd_bad_line(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *ConfigParser) ParseFiles(paths ...string) error {\n\tfor _, path := range paths {\n\t\tapath, err := filepath.Abs(path)\n\t\tif err == nil {\n\t\t\tpath = apath\n\t\t}\n\t\traw, err := os.ReadFile(path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tscanner := utils.NewLineScanner(utils.UnsafeBytesToString(raw))\n\t\tself.seen_includes = make(map[string]bool)\n\t\terr = self.parse(scanner, path, filepath.Dir(path), 0)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif self.SourceHandler != nil {\n\t\t\tself.SourceHandler(utils.UnsafeBytesToString(raw), path)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *ConfigParser) LoadConfig(name string, paths []string, overrides []string) (err error) {\n\tconst SYSTEM_CONF = \"/etc/xdg/kitty\"\n\tsystem_conf := filepath.Join(SYSTEM_CONF, name)\n\tadd_if_exists := func(q string) {\n\t\terr = self.ParseFiles(q)\n\t\tif err != nil && errors.Is(err, fs.ErrNotExist) {\n\t\t\terr = nil\n\t\t}\n\t}\n\tif add_if_exists(system_conf); err != nil {\n\t\treturn err\n\t}\n\tif len(paths) > 0 {\n\t\tfor _, path := range paths {\n\t\t\tif add_if_exists(path); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif add_if_exists(filepath.Join(utils.ConfigDirForName(name), name)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif len(overrides) > 0 {\n\t\terr = self.ParseOverrides(overrides...)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn\n}\n\ntype LinesScanner struct {\n\tlines []string\n}\n\nfunc (self *LinesScanner) Scan() bool {\n\treturn len(self.lines) > 0\n}\n\nfunc (self *LinesScanner) Text() string {\n\tans := self.lines[0]\n\tself.lines = self.lines[1:]\n\treturn ans\n}\n\nfunc (self *LinesScanner) Err() error {\n\treturn nil\n}\n\nfunc (self *ConfigParser) ParseOverrides(overrides ...string) error {\n\ts := LinesScanner{lines: utils.Map(func(x string) string {\n\t\treturn strings.Replace(x, \"=\", \" \", 1)\n\t}, overrides)}\n\tself.seen_includes = make(map[string]bool)\n\treturn self.parse(&s, \"<overrides>\", utils.ConfigDir(), 0)\n}\n\nfunc is_kitty_gui_cmdline(exe string, cmd ...string) bool {\n\tif len(cmd) == 0 {\n\t\treturn false\n\t}\n\tif filepath.Base(exe) != \"kitty\" {\n\t\treturn false\n\t}\n\tif len(cmd) == 1 {\n\t\treturn true\n\t}\n\ts := cmd[1][:1]\n\tswitch s {\n\tcase `@`:\n\t\treturn false\n\tcase `+`:\n\t\tif cmd[1] == `+` {\n\t\t\treturn len(cmd) > 2 && cmd[2] == `open`\n\t\t}\n\t\treturn cmd[1] == `+open`\n\t}\n\treturn true\n}\n\ntype Patcher struct {\n\tWrite_backup bool\n\tMode         fs.FileMode\n}\n\nfunc (self Patcher) Patch(path, sentinel, content string, settings_to_comment_out ...string) (updated bool, err error) {\n\tif self.Mode == 0 {\n\t\tself.Mode = 0o644\n\t}\n\tbackup_path := path\n\tif q, err := filepath.EvalSymlinks(path); err == nil {\n\t\tpath = q\n\t}\n\traw, err := os.ReadFile(path)\n\tif err != nil && !errors.Is(err, fs.ErrNotExist) {\n\t\treturn false, err\n\t}\n\tadd_at_top := \"\"\n\tbackup := true\n\tif raw == nil {\n\t\tcc := kitty.CommentedOutDefaultConfig\n\t\tif idx := strings.Index(cc, \"\\n\\n\"); idx > 0 {\n\t\t\tadd_at_top = cc[:idx+2]\n\t\t\traw = []byte(cc[idx+2:])\n\t\t\tbackup = false\n\t\t}\n\t}\n\tpat := utils.MustCompile(fmt.Sprintf(`(?m)^\\s*(%s)\\b`, strings.Join(settings_to_comment_out, \"|\")))\n\ttext := pat.ReplaceAllString(utils.UnsafeBytesToString(raw), `# $1`)\n\n\tpat = utils.MustCompile(fmt.Sprintf(`(?ms)^# BEGIN_%s.+?# END_%s`, sentinel, sentinel))\n\treplaced := false\n\taddition := fmt.Sprintf(\"# BEGIN_%s\\n%s\\n# END_%s\", sentinel, content, sentinel)\n\tntext := pat.ReplaceAllStringFunc(text, func(string) string {\n\t\treplaced = true\n\t\treturn addition\n\t})\n\tif !replaced {\n\t\tif add_at_top != \"\" {\n\t\t\tntext = add_at_top + addition\n\t\t\tif text != \"\" {\n\t\t\t\tntext += \"\\n\\n\" + text\n\t\t\t}\n\t\t} else {\n\t\t\tif text != \"\" {\n\t\t\t\ttext += \"\\n\\n\"\n\t\t\t}\n\t\t\tntext = text + addition\n\t\t}\n\t}\n\tnraw := utils.UnsafeStringToBytes(ntext)\n\tif !bytes.Equal(raw, nraw) {\n\t\tif len(raw) > 0 && self.Write_backup && backup {\n\t\t\t_ = os.WriteFile(backup_path+\".bak\", raw, self.Mode)\n\t\t}\n\n\t\treturn true, utils.AtomicUpdateFile(path, bytes.NewReader(nraw), self.Mode)\n\t}\n\treturn false, nil\n}\n\nfunc ReloadConfigInKitty(in_parent_only bool) error {\n\tif in_parent_only {\n\t\tif pid, err := strconv.ParseInt(os.Getenv(\"KITTY_PID\"), 10, 32); err == nil {\n\t\t\tif p, err := process.NewProcess(int32(pid)); err == nil {\n\t\t\t\tif exe, eerr := p.Exe(); eerr == nil {\n\t\t\t\t\tif c, err := p.CmdlineSlice(); err == nil && is_kitty_gui_cmdline(exe, c...) {\n\t\t\t\t\t\treturn p.SendSignal(unix.SIGUSR1)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\t// process.Processes() followed by filtering by getting the process\n\t// exe and cmdline is very slow on non-Linux systems as CGO is not allowed\n\t// which means getting exe works by calling lsof on every process. So instead do\n\t// initial filtering based on ps output.\n\tif ps_out, err := exec.Command(\"ps\", \"-x\", \"-o\", \"pid=,comm=\").Output(); err == nil {\n\t\tfor _, line := range utils.Splitlines(utils.UnsafeBytesToString(ps_out)) {\n\t\t\tline = strings.TrimSpace(line)\n\t\t\tif pid_string, argv0, found := strings.Cut(line, \" \"); found {\n\t\t\t\tif pid, err := strconv.ParseInt(strings.TrimSpace(pid_string), 10, 32); err == nil && strings.Contains(argv0, \"kitty\") {\n\t\t\t\t\tif p, err := process.NewProcess(int32(pid)); err == nil {\n\t\t\t\t\t\tif cmdline, err := p.CmdlineSlice(); err == nil {\n\t\t\t\t\t\t\tif exe, err := p.Exe(); err == nil && is_kitty_gui_cmdline(exe, cmdline...) {\n\t\t\t\t\t\t\t\t_ = p.SendSignal(unix.SIGUSR1)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nvar OverrideEffectiveConfigPath string\n\nfunc ReadKittyConfig(line_handler func(key, val string) error, override_effective_config_path ...string) error {\n\tkp := os.Getenv(\"KITTY_PID\")\n\tkitty_conf_path := \"\"\n\tif len(override_effective_config_path) > 0 {\n\t\tkitty_conf_path = override_effective_config_path[0]\n\t}\n\tif _, err := strconv.Atoi(kp); err == nil && kitty_conf_path == \"\" {\n\t\teffective_config_path := filepath.Join(utils.CacheDir(), \"effective-config\", kp)\n\t\tif unix.Access(effective_config_path, unix.R_OK) == nil {\n\t\t\tkitty_conf_path = effective_config_path\n\t\t}\n\t}\n\tif kitty_conf_path == \"\" {\n\t\tkitty_conf_path = filepath.Join(utils.ConfigDir(), \"kitty.conf\")\n\t}\n\tcp := ConfigParser{LineHandler: line_handler}\n\treturn cp.ParseFiles(kitty_conf_path)\n}\n"
  },
  {
    "path": "tools/config/api_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage config\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestConfigParsing(t *testing.T) {\n\ttdir := t.TempDir()\n\tconf_file := filepath.Join(tdir, \"a.conf\")\n\tif err := os.Mkdir(filepath.Join(tdir, \"sub\"), 0o700); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tw := func(path string, data []byte) {\n\t\tif err := os.WriteFile(path, data, 0o600); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\tw(filepath.Join(tdir, \"g.py\"), []byte(`\nprint('gpy 1')\nprint('gpy 2')\n`))\n\tw(conf_file, []byte(\n\t\t`error main\n# igno\n     \\re me\na\tone\n#: other\ninclude\n\\ sub/b.conf\nb x\ninclude non-exis\n\\tent\nglobin\n\\clude sub/c?.c\n   \\onf\nbadline\ngeninclude g.py\n`))\n\tw(filepath.Join(tdir, \"sub/b.conf\"), []byte(\"incb cool\\ninclude a.conf\"))\n\tw(filepath.Join(tdir, \"sub/c1.conf\"), []byte(\"inc1 cool\"))\n\tw(filepath.Join(tdir, \"sub/c2.conf\"), []byte(\"inc2 cool\\nenvinclude ENVINCLUDE\"))\n\tw(filepath.Join(tdir, \"sub/c.conf\"), []byte(\"inc notcool\\nerror sub\"))\n\n\tvar parsed_lines []string\n\tpl := func(key, val string) error {\n\t\tif key == \"error\" {\n\t\t\treturn fmt.Errorf(\"%s\", val)\n\t\t}\n\t\tparsed_lines = append(parsed_lines, key+\" \"+val)\n\t\treturn nil\n\t}\n\n\tp := ConfigParser{LineHandler: pl, override_env: []string{\"ENVINCLUDE=env cool\\ninclude c.conf\"}}\n\terr := p.ParseFiles(conf_file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err = p.ParseOverrides(\"over one\", \"over two\"); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdiff := cmp.Diff([]string{\"a one\", \"incb cool\", \"b x\", \"inc1 cool\", \"inc2 cool\", \"env cool\", \"inc notcool\", \"gpy 1\", \"gpy 2\", \"over one\", \"over two\"}, parsed_lines)\n\tif diff != \"\" {\n\t\tt.Fatalf(\"Unexpected parsed config values:\\n%s\", diff)\n\t}\n\tbad_lines := []string{}\n\tfor _, bl := range p.BadLines() {\n\t\tbad_lines = append(bad_lines, fmt.Sprintf(\"%s: %d\", filepath.Base(bl.Src_file), bl.Line_number))\n\t}\n\tdiff = cmp.Diff([]string{\"a.conf: 1\", \"c.conf: 2\", \"a.conf: 14\"}, bad_lines)\n\tif diff != \"\" {\n\t\tt.Fatalf(\"Unexpected bad lines:\\n%s\", diff)\n\t}\n}\n"
  },
  {
    "path": "tools/config/utils.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage config\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"unicode/utf8\"\n)\n\nvar _ = fmt.Print\n\nfunc ParseStrDict(val, record_sep, field_sep string) (map[string]string, error) {\n\tans := make(map[string]string)\n\tfor record := range strings.SplitSeq(val, record_sep) {\n\t\tkey, val, found := strings.Cut(record, field_sep)\n\t\tif found {\n\t\t\tans[key] = val\n\t\t}\n\t}\n\treturn ans, nil\n}\n\nfunc PositiveFloat(val string) (ans float64, err error) {\n\tans, err = strconv.ParseFloat(val, 64)\n\tif err == nil {\n\t\tans = max(0, ans)\n\t}\n\treturn\n}\n\nfunc UnitFloat(val string) (ans float64, err error) {\n\tans, err = strconv.ParseFloat(val, 64)\n\tif err == nil {\n\t\tans = max(0, min(ans, 1))\n\t}\n\treturn\n}\n\nfunc StringLiteral(val string) (string, error) {\n\tans := strings.Builder{}\n\tans.Grow(len(val))\n\tvar buf [8]rune\n\tbufcount := 0\n\tbuflimit := 0\n\tvar prefix rune\n\ttype State int\n\tconst (\n\t\tnormal State = iota\n\t\tbackslash\n\t\toctal\n\t\thex\n\t)\n\tvar state State\n\tdecode := func(base int) {\n\t\ttext := string(buf[:bufcount])\n\t\tif num, err := strconv.ParseUint(text, base, 32); err == nil && num <= utf8.MaxRune {\n\t\t\tans.WriteRune(rune(num))\n\t\t}\n\t\tstate = normal\n\t\tbufcount = 0\n\t\tbuflimit = 0\n\t\tprefix = 0\n\t}\n\n\twrite_invalid_buf := func() {\n\t\tans.WriteByte('\\\\')\n\t\tans.WriteRune(prefix)\n\t\tfor _, r := range buf[:bufcount] {\n\t\t\tans.WriteRune(r)\n\t\t}\n\t\tstate = normal\n\t\tbufcount = 0\n\t\tbuflimit = 0\n\t\tprefix = 0\n\t}\n\n\tvar dispatch_ch_recurse func(rune)\n\n\tdispatch_ch := func(ch rune) {\n\t\tswitch state {\n\t\tcase normal:\n\t\t\tswitch ch {\n\t\t\tcase '\\\\':\n\t\t\t\tstate = backslash\n\t\t\tdefault:\n\t\t\t\tans.WriteRune(ch)\n\t\t\t}\n\t\tcase octal:\n\t\t\tswitch ch {\n\t\t\tcase '0', '1', '2', '3', '4', '5', '6', '7':\n\t\t\t\tif bufcount >= buflimit {\n\t\t\t\t\tdecode(8)\n\t\t\t\t\tdispatch_ch_recurse(ch)\n\t\t\t\t} else {\n\t\t\t\t\tbuf[bufcount] = ch\n\t\t\t\t\tbufcount++\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tdecode(8)\n\t\t\t\tdispatch_ch_recurse(ch)\n\t\t\t}\n\t\tcase hex:\n\t\t\tswitch ch {\n\t\t\tcase '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F':\n\t\t\t\tbuf[bufcount] = ch\n\t\t\t\tbufcount++\n\t\t\t\tif bufcount >= buflimit {\n\t\t\t\t\tdecode(16)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\twrite_invalid_buf()\n\t\t\t\tdispatch_ch_recurse(ch)\n\t\t\t}\n\t\tcase backslash:\n\t\t\tswitch ch {\n\t\t\tcase '\\n':\n\t\t\tcase '\\\\':\n\t\t\t\tans.WriteRune('\\\\')\n\t\t\t\tstate = normal\n\t\t\tcase '\\'', '\"':\n\t\t\t\tans.WriteRune(ch)\n\t\t\t\tstate = normal\n\t\t\tcase 'a':\n\t\t\t\tans.WriteRune('\\a')\n\t\t\t\tstate = normal\n\t\t\tcase 'b':\n\t\t\t\tans.WriteRune('\\b')\n\t\t\t\tstate = normal\n\t\t\tcase 'f':\n\t\t\t\tans.WriteRune('\\f')\n\t\t\t\tstate = normal\n\t\t\tcase 'n':\n\t\t\t\tans.WriteRune('\\n')\n\t\t\t\tstate = normal\n\t\t\tcase 'r':\n\t\t\t\tans.WriteRune('\\r')\n\t\t\t\tstate = normal\n\t\t\tcase 't':\n\t\t\t\tans.WriteRune('\\t')\n\t\t\t\tstate = normal\n\t\t\tcase 'v':\n\t\t\t\tans.WriteRune('\\v')\n\t\t\t\tstate = normal\n\t\t\tcase '0', '1', '2', '3', '4', '5', '6', '7':\n\t\t\t\tbuf[0] = ch\n\t\t\t\tbufcount = 1\n\t\t\t\tbuflimit = 3\n\t\t\t\tstate = octal\n\t\t\tcase 'x':\n\t\t\t\tbufcount = 0\n\t\t\t\tbuflimit = 2\n\t\t\t\tstate = hex\n\t\t\t\tprefix = ch\n\t\t\tcase 'u':\n\t\t\t\tbufcount = 0\n\t\t\t\tbuflimit = 4\n\t\t\t\tstate = hex\n\t\t\t\tprefix = ch\n\t\t\tcase 'U':\n\t\t\t\tbufcount = 0\n\t\t\t\tbuflimit = 8\n\t\t\t\tstate = hex\n\t\t\t\tprefix = ch\n\t\t\tdefault:\n\t\t\t\tans.WriteByte('\\\\')\n\t\t\t\tans.WriteRune(ch)\n\t\t\t\tstate = normal\n\t\t\t}\n\t\t}\n\t}\n\tdispatch_ch_recurse = dispatch_ch\n\tfor _, ch := range val {\n\t\tdispatch_ch(ch)\n\t}\n\tswitch state {\n\tcase octal:\n\t\tdecode(8)\n\tcase hex:\n\t\twrite_invalid_buf()\n\tcase backslash:\n\t\tans.WriteRune('\\\\')\n\t}\n\treturn ans.String(), nil\n}\n\nvar ModMap = sync.OnceValue(func() map[string]string {\n\treturn map[string]string{\n\t\t\"shift\":     \"shift\",\n\t\t\"⇧\":         \"shift\",\n\t\t\"alt\":       \"alt\",\n\t\t\"option\":    \"alt\",\n\t\t\"opt\":       \"alt\",\n\t\t\"⌥\":         \"alt\",\n\t\t\"super\":     \"super\",\n\t\t\"command\":   \"super\",\n\t\t\"cmd\":       \"super\",\n\t\t\"⌘\":         \"super\",\n\t\t\"control\":   \"ctrl\",\n\t\t\"ctrl\":      \"ctrl\",\n\t\t\"⌃\":         \"ctrl\",\n\t\t\"hyper\":     \"hyper\",\n\t\t\"meta\":      \"meta\",\n\t\t\"num_lock\":  \"num_lock\",\n\t\t\"caps_lock\": \"caps_lock\",\n\t}\n})\n\nvar ShortcutSpecPat = sync.OnceValue(func() *regexp.Regexp {\n\treturn regexp.MustCompile(`([^+])>`)\n})\n\nfunc NormalizeShortcut(spec string) string {\n\tparts := strings.Split(strings.ToLower(spec), \"+\")\n\tkey := parts[len(parts)-1]\n\tif len(parts) == 1 {\n\t\treturn key\n\t}\n\tmods := parts[:len(parts)-1]\n\tmmap := ModMap()\n\tmods = utils.Map(func(x string) string {\n\t\tans := mmap[x]\n\t\tif ans == \"\" {\n\t\t\tans = x\n\t\t}\n\t\treturn ans\n\t}, mods)\n\tslices.Sort(mods)\n\treturn strings.Join(mods, \"+\") + \"+\" + key\n}\n\nfunc NormalizeShortcuts(spec string) []string {\n\tif strings.HasSuffix(spec, \"+\") {\n\t\tspec = spec[:len(spec)-1] + \"plus\"\n\t}\n\tspec = strings.ReplaceAll(spec, \"++\", \"+plus\")\n\tspec = ShortcutSpecPat().ReplaceAllString(spec, \"$1\\x00\")\n\treturn utils.Map(NormalizeShortcut, strings.Split(spec, \"\\x00\"))\n}\n\ntype KeyAction struct {\n\tNormalized_keys []string\n\tName            string\n\tArgs            string\n}\n\nfunc (self *KeyAction) String() string {\n\treturn fmt.Sprintf(\"map %#v %#v %#v\\n\", strings.Join(self.Normalized_keys, \">\"), self.Name, self.Args)\n}\n\nfunc ParseMap(val string) (*KeyAction, error) {\n\tspec, action, found := strings.Cut(val, \" \")\n\tif !found {\n\t\treturn nil, fmt.Errorf(\"No action specified for shortcut %s\", val)\n\t}\n\taction = strings.TrimSpace(action)\n\taction_name, action_args, _ := strings.Cut(action, \" \")\n\taction_args = strings.TrimSpace(action_args)\n\treturn &KeyAction{Name: action_name, Args: action_args, Normalized_keys: NormalizeShortcuts(spec)}, nil\n}\n\ntype ShortcutTracker struct {\n\tpartial_matches      []*KeyAction\n\tpartial_num_consumed int\n}\n\nfunc (self *ShortcutTracker) Match(ev *loop.KeyEvent, all_actions []*KeyAction) *KeyAction {\n\tif self.partial_num_consumed > 0 {\n\t\tev.Handled = true\n\t\tself.partial_matches = utils.Filter(self.partial_matches, func(ac *KeyAction) bool {\n\t\t\treturn self.partial_num_consumed < len(ac.Normalized_keys) && ev.MatchesPressOrRepeat(ac.Normalized_keys[self.partial_num_consumed])\n\t\t})\n\t\tif len(self.partial_matches) == 0 {\n\t\t\tself.partial_num_consumed = 0\n\t\t\treturn nil\n\t\t}\n\t} else {\n\t\tself.partial_matches = utils.Filter(all_actions, func(ac *KeyAction) bool {\n\t\t\treturn ev.MatchesPressOrRepeat(ac.Normalized_keys[0])\n\t\t})\n\t\tif len(self.partial_matches) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tev.Handled = true\n\t}\n\tself.partial_num_consumed++\n\tfor _, x := range self.partial_matches {\n\t\tif self.partial_num_consumed >= len(x.Normalized_keys) {\n\t\t\tself.partial_num_consumed = 0\n\t\t\treturn x\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc ResolveShortcuts(actions []*KeyAction) []*KeyAction {\n\taction_map := make(map[string]*KeyAction, len(actions))\n\tfor _, ac := range actions {\n\t\tkey := strings.Join(ac.Normalized_keys, \"\\x00\")\n\t\tif ac.Name == \"no_op\" || ac.Name == \"no-op\" {\n\t\t\tdelete(action_map, key)\n\t\t} else {\n\t\t\taction_map[key] = ac\n\t\t}\n\t}\n\treturn utils.Values(action_map)\n}\n"
  },
  {
    "path": "tools/config/utils_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage config\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestStringLiteralParsing(t *testing.T) {\n\tfor q, expected := range map[string]string{\n\t\t`abc`:                    `abc`,\n\t\t`a\\nb\\M`:                 \"a\\nb\\\\M\",\n\t\t`a\\x20\\x1\\u1234\\123\\12|`: \"a \\\\x1\\u1234\\123\\x0a|\",\n\t} {\n\t\tactual, err := StringLiteral(q)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif expected != actual {\n\t\t\tt.Fatalf(\"Failed with input: %#v\\n%#v != %#v\", q, expected, actual)\n\t\t}\n\t}\n}\n\nfunc TestNormalizeShortcuts(t *testing.T) {\n\tfor q, expected_ := range map[string]string{\n\t\t`a`:           `a`,\n\t\t`+`:           `plus`,\n\t\t`cmd+b>opt+>`: `super+b alt+>`,\n\t\t`cmd+>>opt+>`: `super+> alt+>`,\n\t} {\n\t\texpected := strings.Split(expected_, \" \")\n\t\tactual := NormalizeShortcuts(q)\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"failed with input: %#v\\n%s\", q, diff)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/crypto/crypto.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage crypto\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/base85\"\n)\n\nfunc curve25519_key_pair() (private_key []byte, public_key []byte, err error) {\n\tcurve := ecdh.X25519()\n\tprivkey, err := curve.GenerateKey(rand.Reader)\n\tif err == nil {\n\t\tpubkey := privkey.PublicKey()\n\t\treturn privkey.Bytes(), pubkey.Bytes(), nil\n\t}\n\treturn nil, nil, err\n}\n\nfunc curve25519_derive_shared_secret(private_key []byte, public_key []byte) (secret []byte, err error) {\n\tprkey, err := ecdh.X25519().NewPrivateKey(private_key)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Invalid X25519 private key: %w\", err)\n\t}\n\tpubkey, err := ecdh.X25519().NewPublicKey(public_key)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Invalid X25519 public key: %w\", err)\n\t}\n\tsecret, err = prkey.ECDH(pubkey)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"Failed to perform ECDH shared secret derivation: %w\", err)\n\t}\n\treturn\n}\n\nfunc b85_encode(data []byte) (encoded string) {\n\tencoded = base85.EncodeToString(data)\n\treturn\n}\n\nfunc b85_decode(data string) (decoded []byte, err error) {\n\tdecoded, err = base85.DecodeString(data)\n\treturn\n}\n\nfunc encrypt(plaintext []byte, alice_public_key []byte, encryption_protocol string) (iv []byte, tag []byte, ciphertext []byte, bob_public_key []byte, err error) {\n\tbob_private_key, bob_public_key, err := KeyPair(encryption_protocol)\n\tif err != nil {\n\t\treturn\n\t}\n\tshared_secret_raw, err := curve25519_derive_shared_secret(bob_private_key, alice_public_key)\n\tif err != nil {\n\t\treturn\n\t}\n\tshared_secret_hashed := sha256.Sum256(shared_secret_raw)\n\tshared_secret := shared_secret_hashed[:]\n\tblock, err := aes.NewCipher(shared_secret)\n\tif err != nil {\n\t\treturn\n\t}\n\taesgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn\n\t}\n\tiv = make([]byte, aesgcm.NonceSize())\n\t_, err = rand.Read(iv)\n\tif err != nil {\n\t\treturn\n\t}\n\toutput := aesgcm.Seal(nil, iv, plaintext, nil)\n\tciphertext = output[0 : len(output)-16]\n\ttag = output[len(output)-16:]\n\treturn\n}\n\nfunc KeyPair(encryption_protocol string) (private_key []byte, public_key []byte, err error) {\n\tswitch encryption_protocol {\n\tcase \"1\":\n\t\treturn curve25519_key_pair()\n\tdefault:\n\t\terr = fmt.Errorf(\"Unknown encryption protocol: %s\", encryption_protocol)\n\t\treturn\n\t}\n}\n\nfunc EncodePublicKey(pubkey []byte, encryption_protocol string) (ans string, err error) {\n\tswitch encryption_protocol {\n\tcase \"1\":\n\t\tans = fmt.Sprintf(\"1:%s\", b85_encode(pubkey))\n\tdefault:\n\t\terr = fmt.Errorf(\"Unknown encryption protocol: %s\", encryption_protocol)\n\t\treturn\n\t}\n\treturn\n}\n\nfunc DecodePublicKey(raw string) (encryption_protocol string, pubkey []byte, err error) {\n\tencryption_protocol, encoded_pubkey, found := strings.Cut(raw, \":\")\n\tif !found {\n\t\treturn \"\", nil, fmt.Errorf(\"Invalid encoded pubkey, no : in string\")\n\t}\n\tpubkey, err = b85_decode(encoded_pubkey)\n\treturn\n}\n\nfunc Encrypt_cmd(cmd *utils.RemoteControlCmd, password string, other_pubkey []byte, encryption_protocol string) (encrypted_cmd utils.EncryptedRemoteControlCmd, err error) {\n\tcmd.Password = password\n\tcmd.Timestamp = time.Now().UnixNano()\n\tplaintext, err := json.Marshal(cmd)\n\tif err != nil {\n\t\treturn\n\t}\n\tiv, tag, ciphertext, pubkey, err := encrypt(plaintext, other_pubkey, encryption_protocol)\n\tencrypted_cmd = utils.EncryptedRemoteControlCmd{\n\t\tVersion: cmd.Version, IV: b85_encode(iv), Tag: b85_encode(tag), Pubkey: b85_encode(pubkey), Encrypted: b85_encode(ciphertext)}\n\tif encryption_protocol != \"1\" {\n\t\tencrypted_cmd.EncProto = encryption_protocol\n\t}\n\treturn\n}\n\nfunc Encrypt_data(data []byte, other_pubkey []byte, encryption_protocol string) (ans []byte, err error) {\n\td := make([]byte, 0, uint64(len(data))+32)\n\td = fmt.Appendf(d, \"%s:\", strconv.FormatInt(time.Now().UnixNano(), 10))\n\td = append(d, data...)\n\tiv, tag, ciphertext, pubkey, err := encrypt(d, other_pubkey, encryption_protocol)\n\tif err != nil {\n\t\treturn\n\t}\n\tec := utils.EncryptedRemoteControlCmd{\n\t\tIV: b85_encode(iv), Tag: b85_encode(tag), Pubkey: b85_encode(pubkey), Encrypted: b85_encode(ciphertext)}\n\tif encryption_protocol != \"1\" {\n\t\tec.EncProto = encryption_protocol\n\t}\n\tans, err = json.Marshal(ec)\n\treturn\n}\n\n// }}}\n"
  },
  {
    "path": "tools/disk_cache/api.go",
    "content": "package disk_cache\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar _ = fmt.Print\n\ntype Entry struct {\n\tKey      string\n\tSize     int64\n\tLastUsed time.Time\n}\n\ntype Metadata struct {\n\tTotalSize     int64\n\tPathMap       map[string]string\n\tSortedEntries []*Entry\n}\n\ntype DiskCache struct {\n\tPath    string\n\tMaxSize int64\n\n\tlock_file               *os.File\n\tlock_mutex              sync.Mutex\n\tentries                 Metadata\n\tentry_map               map[string]*Entry\n\tentries_last_read_state *file_state\n\tentries_dirty           bool\n\tget_dir                 string\n\tread_count              int\n}\n\nfunc NewDiskCache(path string, max_size int64) (dc *DiskCache, err error) {\n\treturn new_disk_cache(path, max_size)\n}\n\nfunc (dc *DiskCache) ResultsDir() string {\n\treturn dc.get_dir\n}\n\nfunc (dc *DiskCache) Get(key string, items ...string) (map[string]string, error) {\n\tdc.lock()\n\tdefer dc.unlock()\n\treturn dc.get(key, items)\n}\n\nfunc (dc *DiskCache) GetPath(path string, items ...string) (string, map[string]string, error) {\n\tdc.lock()\n\tdefer dc.unlock()\n\tkey, err := key_for_path(path)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\tans, err := dc.get(key, items)\n\treturn key, ans, err\n}\n\nfunc (dc *DiskCache) Remove(key string) (err error) {\n\tdc.lock()\n\tdefer dc.unlock()\n\treturn dc.remove(key)\n}\n\nfunc (dc *DiskCache) Clear() (err error) {\n\tdc.lock()\n\tdefer dc.unlock()\n\treturn dc.clear()\n}\n\nfunc (dc *DiskCache) AddPath(path, key string, items map[string][]byte) (ans map[string]string, err error) {\n\tdc.lock()\n\tdefer dc.unlock()\n\tans, err = dc.add_path(path, key, items)\n\treturn\n}\n\nfunc (dc *DiskCache) Add(key string, items map[string][]byte) (ans map[string]string, err error) {\n\tdc.lock()\n\tdefer dc.unlock()\n\treturn dc.add(key, items)\n}\n"
  },
  {
    "path": "tools/disk_cache/implementation.go",
    "content": "package disk_cache\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"maps\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype file_state struct {\n\tSize    int64\n\tModTime time.Time\n\tInode   uint64\n}\n\nfunc (s file_state) String() string {\n\treturn fmt.Sprintf(\"fs{Size: %d, Inode: %d, ModTime: %s}\", s.Size, s.Inode, s.ModTime)\n}\n\nfunc (s *file_state) equal(o *file_state) bool {\n\treturn o != nil && s.Size == o.Size && s.ModTime.Equal(o.ModTime) && s.Inode == o.Inode\n}\n\nfunc get_file_state(fi fs.FileInfo) *file_state {\n\t// The Sys() method returns the underlying data source (can be nil).\n\t// For Unix-like systems, it's a *syscall.Stat_t.\n\tstat, ok := fi.Sys().(*syscall.Stat_t)\n\tif !ok {\n\t\t// For non-Unix systems, you might not have an inode.\n\t\t// In that case, you can fall back to using only size and mod time.\n\t\treturn &file_state{\n\t\t\tSize:    fi.Size(),\n\t\t\tModTime: fi.ModTime(),\n\t\t\tInode:   0, // Inode not available\n\t\t}\n\t}\n\treturn &file_state{\n\t\tSize:    fi.Size(),\n\t\tModTime: fi.ModTime(),\n\t\tInode:   stat.Ino,\n\t}\n}\n\nfunc get_file_state_from_path(path string) (*file_state, error) {\n\tif s, err := os.Stat(path); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn get_file_state(s), nil\n\t}\n}\n\nconst GET_DIR_PREFIX = \"getdir-\"\n\nfunc new_disk_cache(path string, max_size int64) (dc *DiskCache, err error) {\n\tif path, err = filepath.Abs(path); err != nil {\n\t\treturn\n\t}\n\tif err = os.MkdirAll(path, 0o700); err != nil {\n\t\treturn\n\t}\n\tans := &DiskCache{Path: path, MaxSize: max_size}\n\tans.lock()\n\tdefer ans.unlock()\n\tif err = ans.ensure_entries(); err != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\tif we := ans.write_entries_if_dirty(); we != nil && err == nil {\n\t\t\terr = we\n\t\t}\n\t}()\n\tif _, err := ans.prune(); err != nil {\n\t\treturn nil, err\n\t}\n\tif ans.get_dir, err = os.MkdirTemp(ans.Path, GET_DIR_PREFIX+\"*\"); err != nil {\n\t\treturn\n\t}\n\tif err = utils.AtExitRmtree(ans.get_dir); err != nil {\n\t\treturn\n\t}\n\treturn ans, nil\n}\n\nfunc key_for_path(path string) (key string, err error) {\n\tif path, err = filepath.EvalSymlinks(path); err != nil {\n\t\treturn\n\t}\n\tif path, err = filepath.Abs(path); err != nil {\n\t\treturn\n\t}\n\n\ts, err := os.Stat(path)\n\tif err != nil {\n\t\treturn\n\t}\n\tdata := fmt.Sprintf(\"%s\\x00%d\\x00%d\", path, s.Size(), s.ModTime().UnixNano())\n\tsum := sha256.Sum256(utils.UnsafeStringToBytes(data))\n\treturn hex.EncodeToString(sum[:]), nil\n}\n\nfunc (dc *DiskCache) lock() (err error) {\n\tdc.lock_mutex.Lock()\n\tdefer dc.lock_mutex.Unlock()\n\tif dc.lock_file != nil {\n\t\treturn\n\t}\n\tif dc.lock_file, err = os.OpenFile(filepath.Join(dc.Path, \"lockfile\"), os.O_RDWR|os.O_CREATE, 0o600); err != nil {\n\t\treturn\n\t}\n\treturn utils.LockFileExclusive(dc.lock_file)\n}\n\nfunc (dc *DiskCache) unlock() {\n\tdc.lock_mutex.Lock()\n\tdefer dc.lock_mutex.Unlock()\n\tif dc.lock_file != nil {\n\t\tutils.UnlockFile(dc.lock_file)\n\t\tdc.lock_file.Close()\n\t\tdc.lock_file = nil\n\t}\n}\n\nconst ENTRIES_NAME = \"entries.json\"\n\nfunc (dc *DiskCache) entries_path() string { return filepath.Join(dc.Path, ENTRIES_NAME) }\n\nfunc (dc *DiskCache) write_entries_if_dirty() (err error) {\n\tif !dc.entries_dirty {\n\t\treturn\n\t}\n\tpath := dc.entries_path()\n\tdefer func() {\n\t\tif err == nil {\n\t\t\tdc.entries_dirty = false\n\t\t\tif s, serr := get_file_state_from_path(path); serr == nil {\n\t\t\t\tdc.entries_last_read_state = s\n\t\t\t}\n\t\t}\n\t}()\n\tif d, err := json.Marshal(dc.entries); err != nil {\n\t\treturn err\n\t} else {\n\t\t// use a rename so that the inode number changes\n\t\t// dont bother with full utils.AtomicWriteFile() as it is slower\n\t\ttemp := path + \".temp\"\n\t\tremoved := false\n\t\tdefer func() {\n\t\t\tif !removed {\n\t\t\t\t_ = os.Remove(temp)\n\t\t\t\tremoved = true\n\t\t\t}\n\t\t}()\n\t\tif err = os.WriteFile(temp, d, 0o600); err == nil {\n\t\t\tif err = os.Rename(temp, path); err == nil {\n\t\t\t\tremoved = true\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}\n}\n\nfunc (e Entry) String() string {\n\treturn fmt.Sprintf(\"Entry{Key: %s, Size: %d, LastUsed: %s}\", e.Key, e.Size, e.LastUsed)\n}\n\nfunc (dc *DiskCache) entries_from_folders() (total_size int64, ans map[string]*Entry, sorted []*Entry, err error) {\n\tif entries, err := os.ReadDir(dc.Path); err != nil {\n\t\treturn 0, nil, nil, err\n\t} else {\n\t\tans = make(map[string]*Entry)\n\t\tvar total int64\n\t\tfor _, x := range entries {\n\t\t\tif x.IsDir() {\n\t\t\t\tif sub_entries, err := os.ReadDir(filepath.Join(dc.Path, x.Name())); err == nil && len(sub_entries) == 1 {\n\t\t\t\t\tkey := sub_entries[0].Name()\n\t\t\t\t\tpath := dc.folder_for_key(key)\n\t\t\t\t\tif file_entries, err := os.ReadDir(path); err == nil {\n\t\t\t\t\t\te := Entry{Key: key}\n\t\t\t\t\t\tfor _, f := range file_entries {\n\t\t\t\t\t\t\tif fi, err := f.Info(); err == nil {\n\t\t\t\t\t\t\t\te.Size += fi.Size()\n\t\t\t\t\t\t\t\tif fi.ModTime().After(e.LastUsed) {\n\t\t\t\t\t\t\t\t\te.LastUsed = fi.ModTime()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tans[key] = &e\n\t\t\t\t\t\ttotal += e.Size\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsorted = utils.Values(ans)\n\t\tslices.SortFunc(sorted, func(a, b *Entry) int {\n\t\t\treturn a.LastUsed.Compare(b.LastUsed)\n\t\t})\n\t\treturn total, ans, sorted, nil\n\t}\n}\n\nfunc (dc *DiskCache) rebuild_entries() error {\n\ttotal, ans, sorted, err := dc.entries_from_folders()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdc.entries = Metadata{TotalSize: total, SortedEntries: sorted, PathMap: make(map[string]string)}\n\tdc.entry_map = ans\n\tdc.entries_dirty = true\n\treturn dc.write_entries_if_dirty()\n}\n\nfunc (dc *DiskCache) ensure_entries() error {\n\tneeded := dc.entry_map == nil || dc.entries_last_read_state == nil\n\tpath := dc.entries_path()\n\tvar fstate *file_state\n\tif !needed {\n\t\tif s, err := get_file_state_from_path(path); err == nil {\n\t\t\tfstate = s\n\t\t\tif !s.equal(dc.entries_last_read_state) {\n\t\t\t\tneeded = true\n\t\t\t}\n\t\t}\n\t}\n\tif needed {\n\t\tif data, err := os.ReadFile(path); err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tdc.entry_map = make(map[string]*Entry)\n\t\t\t\tdc.entries = Metadata{SortedEntries: make([]*Entry, 0), PathMap: make(map[string]string)}\n\t\t\t} else {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tdc.read_count += 1\n\t\t\tdc.entries = Metadata{SortedEntries: make([]*Entry, 0), PathMap: make(map[string]string)}\n\t\t\tif err := json.Unmarshal(data, &dc.entries); err != nil {\n\t\t\t\t// corrupted data\n\t\t\t\tdc.rebuild_entries()\n\t\t\t} else {\n\t\t\t\tif fstate == nil {\n\t\t\t\t\tif s, err := get_file_state_from_path(path); err == nil {\n\t\t\t\t\t\tfstate = s\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdc.entries_last_read_state = fstate\n\t\t\t}\n\t\t\tdc.entry_map = make(map[string]*Entry)\n\t\t\tfor _, e := range dc.entries.SortedEntries {\n\t\t\t\tdc.entry_map[e.Key] = e\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (dc *DiskCache) folder_for_key(key string) (ans string) {\n\tif len(key) < 5 {\n\t\tans = filepath.Join(key, key)\n\t} else {\n\t\tans = filepath.Join(key[:4], key)\n\t}\n\treturn filepath.Join(dc.Path, ans)\n}\n\nfunc (dc *DiskCache) export_to_get_dir(key, path string) (string, error) {\n\tdest := filepath.Join(dc.get_dir, key+\"-\"+filepath.Base(path))\n\tif err := os.Link(path, dest); err != nil {\n\t\tos.Remove(dest)\n\t\tif err := os.Link(path, dest); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn dest, nil\n\n}\n\nfunc (dc *DiskCache) get(key string, items []string) (map[string]string, error) {\n\tif err := dc.ensure_entries(); err != nil {\n\t\treturn nil, err\n\t}\n\tbase := dc.folder_for_key(key)\n\tif len(items) == 0 {\n\t\tif entries, err := os.ReadDir(base); err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\treturn nil, err\n\t\t} else {\n\t\t\tfor _, e := range entries {\n\t\t\t\titems = append(items, e.Name())\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif s, err := os.Stat(base); err != nil || !s.IsDir() {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tans := make(map[string]string, len(items))\n\tfor _, x := range items {\n\t\tp := filepath.Join(base, x)\n\t\tif s, err := os.Stat(p); err != nil || s.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tdest, _ := dc.export_to_get_dir(key, p)\n\t\tif dest != \"\" {\n\t\t\tans[x] = dest\n\t\t}\n\t}\n\tif len(items) > 0 {\n\t\tdc.update_timestamp(key)\n\t}\n\treturn ans, dc.write_entries_if_dirty()\n}\n\nfunc (dc *DiskCache) clear() (err error) {\n\tif entries, err := os.ReadDir(dc.Path); err != nil {\n\t\treturn err\n\t} else {\n\t\tdefer func() {\n\t\t\tif we := dc.write_entries_if_dirty(); we != nil && err == nil {\n\t\t\t\terr = we\n\t\t\t}\n\t\t}()\n\t\tfor _, x := range entries {\n\t\t\tif x.IsDir() && !strings.HasPrefix(x.Name(), GET_DIR_PREFIX) {\n\t\t\t\t_ = os.RemoveAll(filepath.Join(dc.Path, x.Name()))\n\t\t\t}\n\t\t}\n\t\tdc.entries_dirty = true\n\t\tdc.entry_map = make(map[string]*Entry)\n\t\tdc.entries.SortedEntries = make([]*Entry, 0)\n\t\tdc.entries.TotalSize = 0\n\t}\n\treturn\n}\n\nfunc (dc *DiskCache) remove(key string) (err error) {\n\tif err = dc.ensure_entries(); err != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\tif we := dc.write_entries_if_dirty(); we != nil && err == nil {\n\t\t\terr = we\n\t\t}\n\t}()\n\tbase := dc.folder_for_key(key)\n\tif err = os.RemoveAll(base); err == nil {\n\t\tt := dc.entry_map[key]\n\t\tif t != nil {\n\t\t\tdelete(dc.entry_map, key)\n\t\t\tdc.entries.TotalSize = max(0, dc.entries.TotalSize-t.Size)\n\t\t\tdc.entries.SortedEntries = utils.Filter(dc.entries.SortedEntries, func(x *Entry) bool { return x.Key != key })\n\t\t\tdc.entries_dirty = true\n\t\t}\n\t}\n\treturn\n}\n\nfunc (dc *DiskCache) prune() (bool, error) {\n\tif dc.MaxSize < 1 || dc.entries.TotalSize <= dc.MaxSize {\n\t\treturn false, nil\n\t}\n\tfor dc.entries.TotalSize > dc.MaxSize && len(dc.entries.SortedEntries) > 0 {\n\t\tbase := dc.folder_for_key(dc.entries.SortedEntries[0].Key)\n\t\tif err := os.RemoveAll(base); err == nil {\n\t\t\tt := dc.entries.SortedEntries[0]\n\t\t\tdelete(dc.entry_map, t.Key)\n\t\t\tmaps.DeleteFunc(dc.entries.PathMap, func(path, key string) bool { return key == t.Key })\n\t\t\tdc.entries.TotalSize = max(0, dc.entries.TotalSize-t.Size)\n\t\t\tdc.entries.SortedEntries = dc.entries.SortedEntries[1:]\n\t\t\tdc.entries_dirty = true\n\t\t} else {\n\t\t\treturn false, err\n\t\t}\n\t}\n\treturn true, nil\n}\n\nfunc (dc *DiskCache) update_timestamp(key string) {\n\tt := dc.entry_map[key]\n\tt.LastUsed = time.Now()\n\tidx := slices.Index(dc.entries.SortedEntries, t)\n\tcopy(dc.entries.SortedEntries[idx:], dc.entries.SortedEntries[idx+1:])\n\tdc.entries.SortedEntries[len(dc.entries.SortedEntries)-1] = t\n\tdc.entries_dirty = true\n}\n\nfunc (dc *DiskCache) update_accounting(key string, changed int64) (err error) {\n\tt := dc.entry_map[key]\n\tif t == nil {\n\t\tt = &Entry{Key: key}\n\t\tdc.entry_map[key] = t\n\t\tdc.entries.SortedEntries = append(dc.entries.SortedEntries, t)\n\t}\n\told_size := t.Size\n\tt.Size += changed\n\tt.Size = max(0, t.Size)\n\tdc.entries.TotalSize += t.Size - old_size\n\tdc.entries_dirty = true\n\tdc.update_timestamp(key)\n\tdc.prune()\n\treturn dc.write_entries_if_dirty()\n}\n\nfunc (dc *DiskCache) keys() (ans []string, err error) {\n\tif err = dc.ensure_entries(); err != nil {\n\t\treturn\n\t}\n\tans = make([]string, len(dc.entries.SortedEntries))\n\tfor i, e := range dc.entries.SortedEntries {\n\t\tans[i] = e.Key\n\t}\n\treturn\n}\n\nfunc (dc *DiskCache) add_path(path, key string, items map[string][]byte) (ans map[string]string, err error) {\n\tif err = dc.ensure_entries(); err != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\tif we := dc.write_entries_if_dirty(); we != nil && err == nil {\n\t\t\terr = we\n\t\t}\n\t}()\n\n\tif existing := dc.entries.PathMap[path]; existing != \"\" && existing != key {\n\t\tdelete(dc.entries.PathMap, path)\n\t\tdc.entries_dirty = true\n\t\tif err = dc.remove(existing); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tdc.entries.PathMap[path] = key\n\tdc.entries_dirty = true\n\treturn dc.add(key, items)\n}\n\nfunc (dc *DiskCache) add(key string, items map[string][]byte) (ans map[string]string, err error) {\n\tif err = dc.ensure_entries(); err != nil {\n\t\treturn\n\t}\n\tbase := dc.folder_for_key(key)\n\tif err = os.MkdirAll(base, 0o700); err != nil {\n\t\treturn\n\t}\n\tvar changed int64\n\tdefer func() {\n\t\te := dc.update_accounting(key, changed)\n\t\tif err == nil {\n\t\t\terr = e\n\t\t}\n\t}()\n\tans = make(map[string]string, len(items))\n\tfor x, data := range items {\n\t\tp := filepath.Join(base, x)\n\t\tvar before int64\n\t\texists := false\n\t\tif s, err := os.Stat(p); err == nil {\n\t\t\tbefore = s.Size()\n\t\t\texists = true\n\t\t}\n\t\tif len(data) == 0 {\n\t\t\tif exists {\n\t\t\t\tif err = os.Remove(p); err != nil {\n\t\t\t\t\tif !os.IsNotExist(err) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\terr = nil\n\t\t\t\t}\n\t\t\t\tchanged -= before\n\t\t\t}\n\t\t} else {\n\t\t\t// unlink the file so that writing to it does not change a\n\t\t\t// previously linked copy created by get()\n\t\t\tif exists {\n\t\t\t\t_ = os.Remove(p)\n\t\t\t}\n\t\t\tif err = os.WriteFile(p, data, 0o700); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tchanged += int64(len(data)) - before\n\t\t\tif dest, err := dc.export_to_get_dir(key, p); err != nil {\n\t\t\t\treturn ans, err\n\t\t\t} else {\n\t\t\t\tans[x] = dest\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tools/disk_cache/implementation_test.go",
    "content": "package disk_cache\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc TestDiskCache(t *testing.T) {\n\ttdir := t.TempDir()\n\tdc, err := NewDiskCache(tdir, 64)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdc2, err := NewDiskCache(tdir, dc.MaxSize)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tensure_entries := func() {\n\t\tfor _, x := range []*DiskCache{dc, dc2} {\n\t\t\tif err = x.ensure_entries(); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\n\t}\n\tarc := func(counts ...int) {\n\t\tensure_entries()\n\t\tif diff := cmp.Diff(counts, []int{dc.read_count, dc2.read_count}); diff != \"\" {\n\t\t\tt.Fatalf(\"disk cache has unexpected read count\\n%s\\n%s\", diff, debug.Stack())\n\t\t}\n\t}\n\tadd := func(dc *DiskCache, key string, data map[string]string) {\n\t\td := make(map[string][]byte, len(data))\n\t\tfor k, v := range data {\n\t\t\td[k] = []byte(v)\n\t\t}\n\t\tif _, err := dc.Add(key, d); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tensure_entries()\n\t}\n\n\tm, err := dc.Get(\"missing\", \"one\", \"two\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(m) > 0 {\n\t\tt.Fatalf(\"Unexpected return from missing: %s\", m)\n\t}\n\n\tad := func(key string, expected map[string]string) {\n\t\tfor _, x := range []*DiskCache{dc, dc2} {\n\t\t\tactual, err := x.Get(key, utils.Keys(expected)...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tfor k, path := range actual {\n\t\t\t\td, err := os.ReadFile(path)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tactual[k] = string(d)\n\t\t\t}\n\t\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\t\tt.Fatalf(\"Data for %s not equal: %s\", key, diff)\n\t\t\t}\n\t\t\tensure_entries()\n\t\t}\n\t}\n\tak := func(keys ...string) {\n\t\tfor i, x := range []*DiskCache{dc, dc2} {\n\t\t\tkk, err := x.keys()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif diff := cmp.Diff(keys, kk); diff != \"\" {\n\t\t\t\tt.Fatalf(\"wrong keys in %d: %s\", i+1, diff)\n\t\t\t}\n\t\t}\n\t\tensure_entries()\n\t}\n\tadd(dc, \"k1\", map[string]string{\"1\": \"abcd\", \"2\": \"efgh\"})\n\tarc(0, 1)\n\tad(\"k1\", map[string]string{\"1\": \"abcd\", \"2\": \"efgh\"})\n\tarc(1, 2) // the two gets cause two updates\n\tadd(dc, \"k1\", map[string]string{\"3\": \"ijk\", \"4\": \"lmo\"})\n\tarc(1, 3) // dc.Add() causes re-read in dc2\n\tak(\"k1\")\n\tarc(1, 3)\n\tadd(dc2, \"k2\", map[string]string{\"1\": \"123456789\"})\n\tarc(2, 3) // dc2.Add() causes re-read in dc\n\tak(\"k1\", \"k2\")\n\tarc(2, 3)\n\tad(\"k1\", map[string]string{\"1\": \"abcd\", \"2\": \"efgh\", \"3\": \"ijk\"})\n\tif dc.entries.TotalSize != 14+9 {\n\t\tt.Fatalf(\"TotalSize: %d != %d\", dc.entries.TotalSize, 14+9)\n\t}\n\tarc(3, 4) // the two gets cause two updates\n\tak(\"k2\", \"k1\")\n\tdc.Get(\"k2\")\n\tarc(3, 5) // dc.Get() causes dc2 to read\n\tak(\"k1\", \"k2\")\n\tadd(dc, \"k3\", map[string]string{\"1\": strings.Repeat(\"a\", int(dc.MaxSize)-10)})\n\tarc(3, 6) // dc.Add() causes dc2 to read\n\tak(\"k2\", \"k3\")\n\t// check that creating a new disk cache prunes\n\t_, err = NewDiskCache(tdir, dc.MaxSize-8)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tak(\"k3\")\n\tarc(4, 7) // NewDiskCache()\n\n\t// test the path api\n\tpath := filepath.Join(tdir, \"source\")\n\tif err = os.WriteFile(path, []byte(\"abcdfjrof\"), 0o600); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tkey, _, err := dc.GetPath(path)\n\tif _, err = dc.AddPath(path, key, map[string][]byte{\"1\": []byte(\"1\")}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_, _, entries_before, _ := dc.entries_from_folders()\n\tif diff := cmp.Diff(1, len(dc.entries.PathMap)); diff != \"\" {\n\t\tt.Fatalf(\"unexpected pathmap count: %s\", diff)\n\t}\n\tif err = os.WriteFile(path, []byte(\"changed contents\"), 0o600); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif _, err = dc.AddPath(path, key, map[string][]byte{\"1\": []byte(\"2\")}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif diff := cmp.Diff(1, len(dc.entries.PathMap)); diff != \"\" {\n\t\tt.Fatalf(\"unexpected pathmap count: %s\", diff)\n\t}\n\t_, _, entries_after, _ := dc.entries_from_folders()\n\tif len(entries_before) != len(entries_after) {\n\t\tt.Fatalf(\"unexpected entries: %s\", entries_after)\n\t}\n\tarc(4, 8) // dc.AddPath() causes dc2 to read\n}\n"
  },
  {
    "path": "tools/fzf/algo.go",
    "content": "package fzf\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"golang.org/x/text/unicode/norm\"\n)\n\nvar _ = fmt.Print\n\n/*\n\nAlgorithm\n---------\n\nBased on code from fzf (MIT licensed):\nhttps://github.com/junegunn/fzf\n\nFuzzyMatch implements a modified version of Smith-Waterman algorithm to find\nthe optimal solution (highest score) according to the scoring criteria. Unlike\nthe original algorithm, omission or mismatch of a character in the pattern is\nnot allowed.\n\nScoring criteria\n----------------\n\n- We prefer matches at special positions, such as the start of a word, or\n  uppercase character in camelCase words.\n\n- That is, we prefer an occurrence of the pattern with more characters\n  matching at special positions, even if the total match length is longer.\n    e.g. \"fuzzyfinder\" vs. \"fuzzy-finder\" on \"ff\"\n                            ````````````\n- Also, if the first character in the pattern appears at one of the special\n  positions, the bonus point for the position is multiplied by a constant\n  as it is extremely likely that the first character in the typed pattern\n  has more significance than the rest.\n    e.g. \"fo-bar\" vs. \"foob-r\" on \"br\"\n          ``````\n- But since fzf is still a fuzzy finder, not an acronym finder, we should also\n  consider the total length of the matched substring. This is why we have the\n  gap penalty. The gap penalty increases as the length of the gap (distance\n  between the matching characters) increases, so the effect of the bonus is\n  eventually cancelled at some point.\n    e.g. \"fuzzyfinder\" vs. \"fuzzy-blurry-finder\" on \"ff\"\n          ```````````\n- Consequently, it is crucial to find the right balance between the bonus\n  and the gap penalty. The parameters were chosen that the bonus is cancelled\n  when the gap size increases beyond 8 characters.\n\n- The bonus mechanism can have the undesirable side effect where consecutive\n  matches are ranked lower than the ones with gaps.\n    e.g. \"foobar\" vs. \"foo-bar\" on \"foob\"\n                       ```````\n- To correct this anomaly, we also give extra bonus point to each character\n  in a consecutive matching chunk.\n    e.g. \"foobar\" vs. \"foo-bar\" on \"foob\"\n          ``````\n- The amount of consecutive bonus is primarily determined by the bonus of the\n  first character in the chunk.\n    e.g. \"foobar\" vs. \"out-of-bound\" on \"oob\"\n                       ````````````\n*/\n\nfunc try_skip(input *Chars, case_sensitive bool, b byte, from int) int {\n\tbyteArray := input.Bytes()[from:]\n\tidx := bytes.IndexByte(byteArray, b)\n\tif idx == 0 {\n\t\t// Can't skip any further\n\t\treturn from\n\t}\n\t// We may need to search for the uppercase letter again. We don't have to\n\t// consider normalization as we can be sure that this is an ASCII string.\n\tif !case_sensitive && b >= 'a' && b <= 'z' {\n\t\tif idx > 0 {\n\t\t\tbyteArray = byteArray[:idx]\n\t\t}\n\t\tuidx := bytes.IndexByte(byteArray, b-32)\n\t\tif uidx >= 0 {\n\t\t\tidx = uidx\n\t\t}\n\t}\n\tif idx < 0 {\n\t\treturn -1\n\t}\n\treturn from + idx\n}\n\nfunc ascii_fuzzy_index(input *Chars, pattern []rune, pattern_is_ascii bool, case_sensitive bool) (int, int) {\n\t// Can't determine\n\tif !input.Is_ASCII() {\n\t\treturn 0, input.Length()\n\t}\n\t// Can't match\n\tif !pattern_is_ascii {\n\t\treturn -1, -1\n\t}\n\n\tfirstIdx, idx, lastIdx := 0, 0, 0\n\tvar b byte\n\tfor pidx := range len(pattern) {\n\t\tb = byte(pattern[pidx])\n\t\tidx = try_skip(input, case_sensitive, b, idx)\n\t\tif idx < 0 {\n\t\t\treturn -1, -1\n\t\t}\n\t\tif pidx == 0 && idx > 0 {\n\t\t\t// Step back to find the right bonus point\n\t\t\tfirstIdx = idx - 1\n\t\t}\n\t\tlastIdx = idx\n\t\tidx++\n\t}\n\n\t// Find the last appearance of the last character of the pattern to limit the search scope\n\tbu := b\n\tif !case_sensitive && b >= 'a' && b <= 'z' {\n\t\tbu = b - 32\n\t}\n\tscope := input.Bytes()[lastIdx:]\n\tfor offset := len(scope) - 1; offset > 0; offset-- {\n\t\tif scope[offset] == b || scope[offset] == bu {\n\t\t\treturn firstIdx, lastIdx + offset + 1\n\t\t}\n\t}\n\treturn firstIdx, lastIdx + 1\n}\n\nfunc (m *FuzzyMatcher) charClassOfNonAscii(char rune) charClass {\n\tif unicode.IsLower(char) {\n\t\treturn charLower\n\t} else if unicode.IsUpper(char) {\n\t\treturn charUpper\n\t} else if unicode.IsNumber(char) {\n\t\treturn charNumber\n\t} else if unicode.IsLetter(char) {\n\t\treturn charLetter\n\t} else if unicode.IsSpace(char) {\n\t\treturn charWhite\n\t} else if strings.ContainsRune(m.delimiterChars, char) {\n\t\treturn charDelimiter\n\t}\n\treturn charNonWord\n}\n\n// Score the input against pattern. If !m.Case_sensitive pattern must be\n// lowercased already. pattern must be non-empty. When m.Ignore_accents\n// accents must already be removed from both pattern and input.\nfunc (m *FuzzyMatcher) score_one(input *Chars, pattern []rune, pattern_is_ascii bool, slab *slab) (ans Result) {\n\tM := len(pattern)\n\tN := input.Length()\n\tif M > N {\n\t\treturn\n\t}\n\n\t// Phase 1. Optimized search for ASCII string\n\tminIdx, maxIdx := ascii_fuzzy_index(input, pattern, pattern_is_ascii, m.Case_sensitive)\n\tif minIdx < 0 {\n\t\treturn\n\t}\n\t// fmt.Println(N, maxIdx, idx, maxIdx-idx, input.ToString())\n\tN = maxIdx - minIdx\n\n\tslab.reset()\n\n\tH0 := slab.alloc16(N)\n\tC0 := slab.alloc16(N)\n\t// Bonus point for each position\n\tB := slab.alloc16(N)\n\t// The first occurrence of each character in the pattern\n\tF := slab.alloc32(M)\n\t// Rune array\n\tT := slab.alloc32(N)\n\tinput.CopyRunes(T, minIdx)\n\n\t// Phase 2. Calculate bonus for each point\n\tmaxScore, maxScorePos := int16(0), 0\n\tpidx, lastIdx := 0, 0\n\tpchar0, pchar, prevH0, prevClass, inGap := pattern[0], pattern[0], int16(0), m.initialCharClass, false\n\tfor off, char := range T {\n\t\tvar class charClass\n\t\tif char <= unicode.MaxASCII {\n\t\t\tclass = m.asciiCharClasses[char]\n\t\t\tif !m.Case_sensitive && class == charUpper {\n\t\t\t\tchar += 32\n\t\t\t\tT[off] = char\n\t\t\t}\n\t\t} else {\n\t\t\tclass = m.charClassOfNonAscii(char)\n\t\t\tif !m.Case_sensitive && class == charUpper {\n\t\t\t\tchar = unicode.To(unicode.LowerCase, char)\n\t\t\t}\n\t\t\tT[off] = char\n\t\t}\n\n\t\tbonus := m.bonusMatrix[prevClass][class]\n\t\tB[off] = bonus\n\t\tprevClass = class\n\n\t\tif char == pchar {\n\t\t\tif pidx < M {\n\t\t\t\tF[pidx] = int32(off)\n\t\t\t\tpidx++\n\t\t\t\tpchar = pattern[min(pidx, M-1)]\n\t\t\t}\n\t\t\tlastIdx = off\n\t\t}\n\n\t\tif char == pchar0 {\n\t\t\tscore := scoreMatch + bonus*bonusFirstCharMultiplier\n\t\t\tH0[off] = score\n\t\t\tC0[off] = 1\n\t\t\tif M == 1 && (!m.Backwards && score > maxScore || m.Backwards && score >= maxScore) {\n\t\t\t\tmaxScore, maxScorePos = score, off\n\t\t\t\tif !m.Backwards && bonus >= bonusBoundary {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tinGap = false\n\t\t} else {\n\t\t\tif inGap {\n\t\t\t\tH0[off] = max(prevH0+scoreGapExtension, 0)\n\t\t\t} else {\n\t\t\t\tH0[off] = max(prevH0+scoreGapStart, 0)\n\t\t\t}\n\t\t\tC0[off] = 0\n\t\t\tinGap = true\n\t\t}\n\t\tprevH0 = H0[off]\n\t}\n\tif pidx != M {\n\t\treturn\n\t}\n\tif M == 1 {\n\t\tif m.Without_positions {\n\t\t\treturn Result{Score: uint(maxScore)}\n\t\t}\n\t\treturn Result{Score: uint(maxScore), Positions: []int{minIdx + maxScorePos}}\n\t}\n\n\t// Phase 3. Fill in score matrix (H)\n\t// Unlike the original algorithm, we do not allow omission.\n\tf0 := int(F[0])\n\twidth := lastIdx - f0 + 1\n\tH := slab.alloc16(width * M)\n\tcopy(H, H0[f0:lastIdx+1])\n\n\t// Possible length of consecutive chunk at each position.\n\tC := slab.alloc16(width * M)\n\tcopy(C, C0[f0:lastIdx+1])\n\n\tFsub := F[1:]\n\tPsub := pattern[1:][:len(Fsub)]\n\tfor off, f := range Fsub {\n\t\tf := int(f)\n\t\tpchar := Psub[off]\n\t\tpidx := off + 1\n\t\trow := pidx * width\n\t\tinGap := false\n\t\tTsub := T[f : lastIdx+1]\n\t\tBsub := B[f:][:len(Tsub)]\n\t\tCsub := C[row+f-f0:][:len(Tsub)]\n\t\tCdiag := C[row+f-f0-1-width:][:len(Tsub)]\n\t\tHsub := H[row+f-f0:][:len(Tsub)]\n\t\tHdiag := H[row+f-f0-1-width:][:len(Tsub)]\n\t\tHleft := H[row+f-f0-1:][:len(Tsub)]\n\t\tHleft[0] = 0\n\t\tfor off, char := range Tsub {\n\t\t\tcol := off + f\n\t\t\tvar s1, s2, consecutive int16\n\n\t\t\tif inGap {\n\t\t\t\ts2 = Hleft[off] + scoreGapExtension\n\t\t\t} else {\n\t\t\t\ts2 = Hleft[off] + scoreGapStart\n\t\t\t}\n\n\t\t\tif pchar == char {\n\t\t\t\ts1 = Hdiag[off] + scoreMatch\n\t\t\t\tb := Bsub[off]\n\t\t\t\tconsecutive = Cdiag[off] + 1\n\t\t\t\tif consecutive > 1 {\n\t\t\t\t\tfb := B[col-int(consecutive)+1]\n\t\t\t\t\t// Break consecutive chunk\n\t\t\t\t\tif b >= bonusBoundary && b > fb {\n\t\t\t\t\t\tconsecutive = 1\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb = max(b, max(bonusConsecutive, fb))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif s1+b < s2 {\n\t\t\t\t\ts1 += Bsub[off]\n\t\t\t\t\tconsecutive = 0\n\t\t\t\t} else {\n\t\t\t\t\ts1 += b\n\t\t\t\t}\n\t\t\t}\n\t\t\tCsub[off] = consecutive\n\n\t\t\tinGap = s1 < s2\n\t\t\tscore := max(max(s1, s2), 0)\n\t\t\tif pidx == M-1 && (!m.Backwards && score > maxScore || m.Backwards && score >= maxScore) {\n\t\t\t\tmaxScore, maxScorePos = score, col\n\t\t\t}\n\t\t\tHsub[off] = score\n\t\t}\n\t}\n\t// Phase 4. (Optional) Backtrace to find character positions\n\tvar pos []int\n\tj := f0\n\tif !m.Without_positions {\n\t\tpos = make([]int, 0, M)\n\t\ti := M - 1\n\t\tj = maxScorePos\n\t\tpreferMatch := true\n\t\tfor {\n\t\t\tI := i * width\n\t\t\tj0 := j - f0\n\t\t\ts := H[I+j0]\n\n\t\t\tvar s1, s2 int16\n\t\t\tif i > 0 && j >= int(F[i]) {\n\t\t\t\ts1 = H[I-width+j0-1]\n\t\t\t}\n\t\t\tif j > int(F[i]) {\n\t\t\t\ts2 = H[I+j0-1]\n\t\t\t}\n\n\t\t\tif s > s1 && (s > s2 || s == s2 && preferMatch) {\n\t\t\t\tpos = append(pos, j+minIdx)\n\t\t\t\tif i == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\ti--\n\t\t\t}\n\t\t\tpreferMatch = C[I+j0] > 1 || I+width+j0+1 < len(C) && C[I+width+j0+1] > 0\n\t\t\tj--\n\t\t}\n\t}\n\treturn Result{Score: uint(maxScore), Positions: pos}\n}\n\nfunc (m *FuzzyMatcher) score(items []string, pattern string, scoring_func func(string, []rune, bool, *slab, func(string) Chars) Result) (ans []Result, err error) {\n\tif pattern == \"\" || len(items) < 1 {\n\t\treturn make([]Result, len(items)), nil\n\t}\n\tas_chars := CharsFromString\n\tif m.Ignore_accents {\n\t\tpattern = string(CharsFromStringWithoutAccents(pattern).runes)\n\t\tas_chars = CharsFromStringWithoutAccents\n\t}\n\tpattern = norm.NFC.String(pattern)\n\tif !m.Case_sensitive {\n\t\tpattern = strings.ToLower(pattern)\n\t}\n\tpat := []rune(pattern)\n\tpattern_is_ascii := !slices.ContainsFunc(pat, func(r rune) bool { return r >= utf8.RuneSelf })\n\tans = make([]Result, len(items))\n\terr = parallel.Run_in_parallel_over_range(0, func(start, end int) {\n\t\ts := slab{}\n\t\tfor i := start; i < end; i++ {\n\t\t\tans[i] = scoring_func(items[i], pat, pattern_is_ascii, &s, as_chars)\n\t\t}\n\t}, 0, len(items))\n\treturn\n\n}\n"
  },
  {
    "path": "tools/fzf/algo_test.go",
    "content": "package fzf\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\tgcmp \"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc assertMatch(t *testing.T, m *FuzzyMatcher, item string, query string, start, end, score int) {\n\tr, err := m.Score([]string{item}, query)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif r[0].Score != uint(score) {\n\t\tt.Fatalf(\"Score of %#v in %#v is %d instead of %d\", query, item, r[0].Score, score)\n\t}\n\tif start > -1 && end > -1 {\n\t\tp := r[0].Positions\n\t\tsort.Ints(p)\n\t\tif len(p) < 1 {\n\t\t\tt.Fatalf(\"Got no positions for %#v in %#v\", query, item)\n\t\t}\n\t\tif p[0] != start {\n\t\t\tt.Fatalf(\"First char of %#v in %#v at %d instead of %d\", query, item, p[0], start)\n\t\t}\n\t\tif p[len(p)-1]+1 != end {\n\t\t\tt.Fatalf(\"Last char of %#v in %#v at %d instead of %d\", query, item, p[len(p)-1], end-1)\n\t\t}\n\t}\n}\n\nfunc TestFZFAlgo(t *testing.T) {\n\tfn := NewFuzzyMatcher(DEFAULT_SCHEME)\n\tfor _, forward := range []bool{true, false} {\n\t\tfn.Backwards = !forward\n\t\tfn.Case_sensitive = false\n\t\tassertMatch(t, fn, \"fooBarbaz1\", \"oBZ\", 2, 9,\n\t\t\tscoreMatch*3+bonusCamel123+scoreGapStart+scoreGapExtension*3)\n\t\tassertMatch(t, fn, \"foo bar baz\", \"fbb\", 0, 9,\n\t\t\tscoreMatch*3+int(fn.bonusBoundaryWhite)*bonusFirstCharMultiplier+\n\t\t\t\tint(fn.bonusBoundaryWhite)*2+2*scoreGapStart+4*scoreGapExtension)\n\t\tassertMatch(t, fn, \"/AutomatorDocument.icns\", \"rdoc\", 9, 13,\n\t\t\tscoreMatch*4+bonusCamel123+bonusConsecutive*2)\n\t\tassertMatch(t, fn, \"/man1/zshcompctl.1\", \"zshc\", 6, 10,\n\t\t\tscoreMatch*4+int(fn.bonusBoundaryDelimiter)*bonusFirstCharMultiplier+int(fn.bonusBoundaryDelimiter)*3)\n\t\tassertMatch(t, fn, \"/.oh-my-zsh/cache\", \"zshc\", 8, 13,\n\t\t\tscoreMatch*4+bonusBoundary*bonusFirstCharMultiplier+bonusBoundary*2+scoreGapStart+int(fn.bonusBoundaryDelimiter))\n\t\tassertMatch(t, fn, \"ab0123 456\", \"12356\", 3, 10,\n\t\t\tscoreMatch*5+bonusConsecutive*3+scoreGapStart+scoreGapExtension)\n\t\tassertMatch(t, fn, \"abc123 456\", \"12356\", 3, 10,\n\t\t\tscoreMatch*5+bonusCamel123*bonusFirstCharMultiplier+bonusCamel123*2+bonusConsecutive+scoreGapStart+scoreGapExtension)\n\t\tassertMatch(t, fn, \"foo/bar/baz\", \"fbb\", 0, 9,\n\t\t\tscoreMatch*3+int(fn.bonusBoundaryWhite)*bonusFirstCharMultiplier+\n\t\t\t\tint(fn.bonusBoundaryDelimiter)*2+2*scoreGapStart+4*scoreGapExtension)\n\t\tassertMatch(t, fn, \"fooBarBaz\", \"fbb\", 0, 7,\n\t\t\tscoreMatch*3+int(fn.bonusBoundaryWhite)*bonusFirstCharMultiplier+\n\t\t\t\tbonusCamel123*2+2*scoreGapStart+2*scoreGapExtension)\n\t\tassertMatch(t, fn, \"foo barbaz\", \"fbb\", 0, 8,\n\t\t\tscoreMatch*3+int(fn.bonusBoundaryWhite)*bonusFirstCharMultiplier+int(fn.bonusBoundaryWhite)+\n\t\t\t\tscoreGapStart*2+scoreGapExtension*3)\n\t\tassertMatch(t, fn, \"fooBar Baz\", \"foob\", 0, 4,\n\t\t\tscoreMatch*4+int(fn.bonusBoundaryWhite)*bonusFirstCharMultiplier+int(fn.bonusBoundaryWhite)*3)\n\t\tassertMatch(t, fn, \"xFoo-Bar Baz\", \"foo-b\", 1, 6,\n\t\t\tscoreMatch*5+bonusCamel123*bonusFirstCharMultiplier+bonusCamel123*2+\n\t\t\t\tbonusNonWord+bonusBoundary)\n\n\t\tfn.Case_sensitive = true\n\t\tassertMatch(t, fn, \"fooBarbaz\", \"oBz\", 2, 9,\n\t\t\tscoreMatch*3+bonusCamel123+scoreGapStart+scoreGapExtension*3)\n\t\tassertMatch(t, fn, \"Foo/Bar/Baz\", \"FBB\", 0, 9,\n\t\t\tscoreMatch*3+int(fn.bonusBoundaryWhite)*bonusFirstCharMultiplier+int(fn.bonusBoundaryDelimiter)*2+\n\t\t\t\tscoreGapStart*2+scoreGapExtension*4)\n\t\tassertMatch(t, fn, \"FooBarBaz\", \"FBB\", 0, 7,\n\t\t\tscoreMatch*3+int(fn.bonusBoundaryWhite)*bonusFirstCharMultiplier+bonusCamel123*2+\n\t\t\t\tscoreGapStart*2+scoreGapExtension*2)\n\t\tassertMatch(t, fn, \"FooBar Baz\", \"FooB\", 0, 4,\n\t\t\tscoreMatch*4+int(fn.bonusBoundaryWhite)*bonusFirstCharMultiplier+int(fn.bonusBoundaryWhite)*2+\n\t\t\t\tmax(bonusCamel123, int(fn.bonusBoundaryWhite)))\n\n\t\t// Consecutive bonus updated\n\t\tassertMatch(t, fn, \"foo-bar\", \"o-ba\", 2, 6, scoreMatch*4+bonusBoundary*3)\n\n\t\t// Non-match\n\t\tassertMatch(t, fn, \"fooBarbaz\", \"oBZ\", -1, -1, 0)\n\t\tassertMatch(t, fn, \"Foo Bar Baz\", \"fbb\", -1, -1, 0)\n\t\tassertMatch(t, fn, \"fooBarbaz\", \"fooBarbazz\", -1, -1, 0)\n\t}\n\n\tvar positions [][]int\n\tsort_by_score := false\n\tfn.Case_sensitive = false\n\n\tsimple := func(items, query string, expected ...string) {\n\t\tilist := utils.Splitlines(items)\n\t\tmatches, err := fn.Score(ilist, query)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif sort_by_score {\n\t\t\tslist := make([]int, len(matches))\n\t\t\tfor i := range len(slist) {\n\t\t\t\tslist[i] = i\n\t\t\t}\n\t\t\tutils.StableSort(slist, func(a, b int) int {\n\t\t\t\treturn cmp.Compare(matches[b].Score, matches[a].Score)\n\t\t\t})\n\t\t\tnlist, nmatches := make([]string, len(ilist)), make([]Result, len(matches))\n\t\t\tfor i, j := range slist {\n\t\t\t\tnlist[i] = ilist[j]\n\t\t\t\tnmatches[i] = matches[j]\n\t\t\t}\n\t\t\tilist = nlist\n\t\t\tmatches = nmatches\n\t\t}\n\t\tactual := make([]string, 0, len(matches))\n\t\tactual_positions := make([][]int, 0, len(matches))\n\t\tfor i, m := range matches {\n\t\t\tif m.Score > 0 {\n\t\t\t\tsort.Ints(m.Positions)\n\t\t\t\tactual = append(actual, ilist[i])\n\t\t\t\tactual_positions = append(actual_positions, m.Positions)\n\t\t\t}\n\t\t}\n\t\tif expected == nil {\n\t\t\texpected = []string{}\n\t\t}\n\n\t\tif diff := gcmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed for items: %#v\\nQuery: %#v\\nMatches: %#v\\n%s\", ilist, query, matches, diff)\n\t\t}\n\t\tif positions != nil {\n\t\t\tif diff := gcmp.Diff(positions, actual_positions); diff != \"\" {\n\t\t\t\tt.Fatalf(\"Failed positions for items: %v\\n%s\", ilist, diff)\n\t\t\t}\n\t\t\tpositions = nil\n\t\t}\n\t}\n\tsimple(\"test\\nxyz\", \"te\", \"test\")\n\tsimple(\"abc\\nxyz\", \"ba\")\n\tsimple(\"abc\\n123\", \"abc\", \"abc\")\n\tsimple(\"test\\nxyz\", \"Te\", \"test\")\n\tsimple(\"test\\nxyz\", \"XY\", \"xyz\")\n\tsimple(\"test\\nXYZ\", \"xy\", \"XYZ\")\n\tsimple(\"test\\nXYZ\", \"mn\")\n\n\tpositions = [][]int{{0, 2}, {0, 1}}\n\tsimple(\"abc\\nac\", \"ac\", \"abc\", \"ac\")\n\tpositions = [][]int{{0}}\n\tsimple(\"abc\\nv\", \"a\", \"abc\")\n\tpositions = [][]int{{1, 3}}\n\tsimple(\"汉a字b\\nxyz\", \"ab\", \"汉a字b\")\n\n\tsort_by_score = true\n\t// Match at start\n\tsimple(\"archer\\nelementary\", \"e\", \"elementary\", \"archer\")\n\t// Match at level factor\n\tsimple(\"xxxy\\nxx/y\", \"y\", \"xx/y\", \"xxxy\")\n\t// CamelCase\n\tsimple(\"xxxy\\nxxxY\", \"y\", \"xxxY\", \"xxxy\")\n\t// Distance\n\tsimple(\"abbc\\nabc\", \"ac\", \"abc\", \"abbc\")\n\t// Extreme chars\n\tsimple(\"xxa\\naxx\", \"a\", \"axx\", \"xxa\")\n\t// Highest score\n\tpositions = [][]int{{3}}\n\tsimple(\"xa/a\", \"a\", \"xa/a\")\n\n\tsort_by_score = false\n\titems := make([]string, 256)\n\n\tfor i := range items {\n\t\titems[i] = strconv.Itoa(i)\n\t}\n\texpected := make([]string, 0, len(items))\n\tfor _, x := range items {\n\t\tif strings.ContainsRune(x, rune('2')) {\n\t\t\texpected = append(expected, x)\n\t\t}\n\t}\n\tsimple(strings.Join(items, \"\\n\"), \"2\", expected...)\n}\n"
  },
  {
    "path": "tools/fzf/api.go",
    "content": "package fzf\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\nfunc NewFuzzyMatcher(scheme Scheme) (ans *FuzzyMatcher) {\n\treturn new_fuzzy_matcher(scheme)\n}\n\n// Score the specified items using runtime.NumCPU() go routines. This function\n// reports a panic in any worker go routine as a regular error.\nfunc (m *FuzzyMatcher) Score(items []string, pattern string) (ans []Result, err error) {\n\treturn m.score(items, pattern, func(item string, pat []rune, pattern_is_ascii bool, slab *slab, as_chars func(string) Chars) Result {\n\t\tc := as_chars(item)\n\t\treturn m.score_one(&c, pat, pattern_is_ascii, slab)\n\t})\n}\n\n// Clear the cache used ScoreWithCache(). Useful if you change some of the\n// settings used for scoring.\nfunc (m *FuzzyMatcher) ClearScoreCache() {\n\tm.cache_mutex.Lock()\n\tm.cache = make(map[string]Result)\n\tm.cache_mutex.Unlock()\n}\n\n// Same as Score, except that it uses a cache. Remember to call\n// ClearScoreCache() if you change any scoring settings on this FuzzyMatcher.\nfunc (m *FuzzyMatcher) ScoreWithCache(items []string, pattern string) (ans []Result, err error) {\n\tkey_prefix := pattern + \"\\x00\"\n\treturn m.score(items, pattern, func(item string, pat []rune, pattern_is_ascii bool, slab *slab, as_chars func(string) Chars) Result {\n\t\tkey := key_prefix + item\n\t\tm.cache_mutex.Lock()\n\t\tres, found := m.cache[key]\n\t\tm.cache_mutex.Unlock()\n\t\tif !found {\n\t\t\tc := as_chars(item)\n\t\t\tres = m.score_one(&c, pat, pattern_is_ascii, slab)\n\t\t\tm.cache_mutex.Lock()\n\t\t\tm.cache[key] = res\n\t\t\tm.cache_mutex.Unlock()\n\t\t}\n\t\treturn res\n\t})\n}\n"
  },
  {
    "path": "tools/fzf/types.go",
    "content": "package fzf\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\t\"unsafe\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"golang.org/x/text/unicode/norm\"\n)\n\nvar _ = fmt.Print\n\ntype Chars struct {\n\tbytes []byte\n\trunes []rune\n}\n\nfunc check_ascii(bytes []byte) (ascii_until int) {\n\tslen := len(bytes)\n\t// Process 8 bytes at a time\n\ti := 0\n\tfor ; i+8 <= slen; i += 8 {\n\t\tv := *(*uint64)(unsafe.Pointer(&bytes[i]))\n\t\t// If any byte has its high bit set, v & 0x8080808080808080 != 0\n\t\tif v&0x8080808080808080 != 0 {\n\t\t\t// At least one non-ASCII byte in this chunk, find which\n\t\t\tfor j := range 8 {\n\t\t\t\tif bytes[i+j]&utf8.RuneSelf != 0 {\n\t\t\t\t\treturn i + j\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Handle remaining bytes\n\tfor ; i < slen; i++ {\n\t\tif bytes[i]&utf8.RuneSelf != 0 {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc CharsFromString(text string) (ans Chars) {\n\tans.bytes = utils.UnsafeStringToBytes(text)\n\tascii_until := check_ascii(ans.bytes)\n\tif ascii_until > -1 {\n\t\trunes := []rune(norm.NFC.String(text[ascii_until:]))\n\t\tans.runes = make([]rune, ascii_until+len(runes))\n\t\tfor i := range ascii_until {\n\t\t\tans.runes[i] = rune(ans.bytes[i])\n\t\t}\n\t\tcopy(ans.runes[ascii_until:], runes)\n\t}\n\treturn\n}\n\nfunc CharsFromStringWithoutAccents(text string) (ans Chars) {\n\tans.bytes = utils.UnsafeStringToBytes(text)\n\tascii_until := check_ascii(ans.bytes)\n\tif ascii_until > -1 {\n\t\trunes := []rune(norm.NFD.String(text[ascii_until:]))\n\t\tans.runes = make([]rune, ascii_until, ascii_until+len(runes))\n\t\tfor i := range ascii_until {\n\t\t\tans.runes[i] = rune(ans.bytes[i])\n\t\t}\n\t\tfor _, r := range runes {\n\t\t\tif !unicode.Is(unicode.Mn, r) {\n\t\t\t\tans.runes = append(ans.runes, r)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (c *Chars) Bytes() []byte  { return c.bytes }\nfunc (c *Chars) Is_ASCII() bool { return c.runes == nil }\nfunc (c *Chars) Get(i int) rune {\n\tif c.runes != nil {\n\t\treturn c.runes[i]\n\t}\n\treturn rune(c.bytes[i])\n}\nfunc (c *Chars) Length() int {\n\tif c.runes != nil {\n\t\treturn len(c.runes)\n\t}\n\treturn len(c.bytes)\n}\n\nfunc (c *Chars) CopyRunes(dest []rune, from int) {\n\tif c.runes != nil {\n\t\tcopy(dest, c.runes[from:])\n\t\treturn\n\t}\n\tfor idx, b := range c.bytes[from:][:len(dest)] {\n\t\tdest[idx] = rune(b)\n\t}\n}\n\ntype charClass int\n\nconst (\n\tcharWhite charClass = iota\n\tcharNonWord\n\tcharDelimiter\n\tcharLower\n\tcharUpper\n\tcharLetter\n\tcharNumber\n)\n\nconst (\n\tscoreMatch        = 16\n\tscoreGapStart     = -3\n\tscoreGapExtension = -1\n\n\t// We prefer matches at the beginning of a word, but the bonus should not be\n\t// too great to prevent the longer acronym matches from always winning over\n\t// shorter fuzzy matches. The bonus point here was specifically chosen that\n\t// the bonus is cancelled when the gap between the acronyms grows over\n\t// 8 characters, which is approximately the average length of the words found\n\t// in web2 dictionary and my file system.\n\tbonusBoundary = scoreMatch / 2\n\n\t// Although bonus point for non-word characters is non-contextual, we need it\n\t// for computing bonus points for consecutive chunks starting with a non-word\n\t// character.\n\tbonusNonWord = scoreMatch / 2\n\n\t// Edge-triggered bonus for matches in camelCase words.\n\t// Compared to word-boundary case, they don't accompany single-character gaps\n\t// (e.g. FooBar vs. foo-bar), so we deduct bonus point accordingly.\n\tbonusCamel123 = bonusBoundary + scoreGapExtension\n\n\t// Minimum bonus point given to characters in consecutive chunks.\n\t// Note that bonus points for consecutive matches shouldn't have needed if we\n\t// used fixed match score as in the original algorithm.\n\tbonusConsecutive = -(scoreGapStart + scoreGapExtension)\n\n\t// The first character in the typed pattern usually has more significance\n\t// than the rest so it's important that it appears at special positions where\n\t// bonus points are given, e.g. \"to-go\" vs. \"ongoing\" on \"og\" or on \"ogo\".\n\t// The amount of the extra bonus should be limited so that the gap penalty is\n\t// still respected.\n\tbonusFirstCharMultiplier = 2\n)\n\nconst whiteChars = \" \\t\\n\\v\\f\\r\\x85\\xA0\"\n\ntype Result struct {\n\tScore     uint // A value of zero means did not match\n\tPositions []int\n}\n\ntype FuzzyMatcher struct {\n\tCase_sensitive, Ignore_accents, Backwards, Without_positions bool\n\n\t// Extra bonus for word boundary after whitespace character or beginning of the string\n\tbonusBoundaryWhite int16\n\n\t// Extra bonus for word boundary after slash, colon, semi-colon, and comma\n\tbonusBoundaryDelimiter int16\n\n\tinitialCharClass charClass\n\n\t// A minor optimization that can give 15%+ performance boost\n\tasciiCharClasses [unicode.MaxASCII + 1]charClass\n\n\t// A minor optimization that can give yet another 5% performance boost\n\tbonusMatrix [charNumber + 1][charNumber + 1]int16\n\n\tdelimiterChars string\n\n\tcache       map[string]Result\n\tcache_mutex sync.Mutex\n}\n\nfunc (m *FuzzyMatcher) bonusFor(prevClass charClass, class charClass) int16 {\n\tif class > charNonWord {\n\t\tswitch prevClass {\n\t\tcase charWhite:\n\t\t\t// Word boundary after whitespace\n\t\t\treturn m.bonusBoundaryWhite\n\t\tcase charDelimiter:\n\t\t\t// Word boundary after a delimiter character\n\t\t\treturn m.bonusBoundaryDelimiter\n\t\tcase charNonWord:\n\t\t\t// Word boundary\n\t\t\treturn bonusBoundary\n\t\t}\n\t}\n\n\tif prevClass == charLower && class == charUpper ||\n\t\tprevClass != charNumber && class == charNumber {\n\t\t// camelCase letter123\n\t\treturn bonusCamel123\n\t}\n\n\tswitch class {\n\tcase charNonWord, charDelimiter:\n\t\treturn bonusNonWord\n\tcase charWhite:\n\t\treturn m.bonusBoundaryWhite\n\t}\n\treturn 0\n}\n\ntype Scheme string\n\nconst (\n\tDEFAULT_SCHEME Scheme = \"default\"\n\tPATH_SCHEME    Scheme = \"path\"\n\tHISTORY_SCHEME Scheme = \"history\"\n)\n\nfunc new_fuzzy_matcher(scheme Scheme) (ans *FuzzyMatcher) {\n\tans = &FuzzyMatcher{\n\t\tbonusBoundaryWhite:     bonusBoundary + 2,\n\t\tbonusBoundaryDelimiter: bonusBoundary + 1,\n\t\tdelimiterChars:         \"/,:;|\",\n\t\tcache:                  make(map[string]Result),\n\t}\n\tswitch scheme {\n\tcase PATH_SCHEME:\n\t\tans.bonusBoundaryWhite = bonusBoundary\n\t\tans.initialCharClass = charDelimiter\n\t\tif os.PathSeparator == '/' {\n\t\t\tans.delimiterChars = \"/\"\n\t\t} else {\n\t\t\tans.delimiterChars = \"/\" + string(os.PathSeparator)\n\t\t}\n\tcase HISTORY_SCHEME:\n\t\tans.bonusBoundaryWhite = bonusBoundary\n\t\tans.bonusBoundaryDelimiter = bonusBoundary\n\t}\n\tfor i := 0; i <= unicode.MaxASCII; i++ {\n\t\tchar := rune(i)\n\t\tc := charNonWord\n\t\tif char >= 'a' && char <= 'z' {\n\t\t\tc = charLower\n\t\t} else if char >= 'A' && char <= 'Z' {\n\t\t\tc = charUpper\n\t\t} else if char >= '0' && char <= '9' {\n\t\t\tc = charNumber\n\t\t} else if strings.ContainsRune(whiteChars, char) {\n\t\t\tc = charWhite\n\t\t} else if strings.ContainsRune(ans.delimiterChars, char) {\n\t\t\tc = charDelimiter\n\t\t}\n\t\tans.asciiCharClasses[i] = c\n\t}\n\tfor i := 0; i <= int(charNumber); i++ {\n\t\tfor j := 0; j <= int(charNumber); j++ {\n\t\t\tans.bonusMatrix[i][j] = ans.bonusFor(charClass(i), charClass(j))\n\t\t}\n\t}\n\treturn\n}\n\ntype slab struct {\n\ti16                []int16\n\ti32                []int32\n\ti16_used, i32_used int\n}\n\nconst slab_initial_size = 8192\n\nfunc (s *slab) reset() {\n\tif s.i16 == nil {\n\t\ts.i16 = make([]int16, slab_initial_size)\n\t}\n\tif s.i32 == nil {\n\t\ts.i32 = make([]int32, slab_initial_size)\n\t}\n\ts.i16_used, s.i32_used = 0, 0\n}\n\nfunc (s *slab) alloc16(sz int) []int16 {\n\tif sz+s.i16_used < len(s.i16) {\n\t\ts.i16 = make([]int16, max(slab_initial_size, 2*(s.i16_used+sz)))\n\t\ts.i16_used = 0\n\t}\n\tpos := s.i16_used\n\ts.i16_used += sz\n\treturn s.i16[pos:s.i16_used]\n}\n\nfunc (s *slab) alloc32(sz int) []int32 {\n\tif sz+s.i32_used < len(s.i32) {\n\t\ts.i32 = make([]int32, max(slab_initial_size, 2*(s.i32_used+sz)))\n\t\ts.i32_used = 0\n\t}\n\tpos := s.i32_used\n\ts.i32_used += sz\n\treturn s.i32[pos:s.i32_used]\n}\n"
  },
  {
    "path": "tools/highlight/api.go",
    "content": "package highlight\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/alecthomas/chroma/v2\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nvar ErrNoLexer = errors.New(\"No lexer available for this format\")\n\ntype StyleResolveData interface {\n\tStyleName() string\n\tUseLightColors() bool\n\tSyntaxAliases() map[string]string\n\tTextForPath(string) (string, error)\n}\n\ntype Highlighter interface {\n\tHighlightFile(path string, srd StyleResolveData) (highlighted_string string, err error)\n\tSanitize(string) string\n}\n\nfunc NewHighlighter(sanitize func(string) string) Highlighter {\n\tif sanitize == nil {\n\t\tsanitize = func(text string) string { return utils.ReplaceControlCodes(text, \"    \", \"\\n\") }\n\t}\n\treturn &highlighter{sanitize: sanitize, tokens_map: make(map[string][]chroma.Token)}\n}\n"
  },
  {
    "path": "tools/highlight/impl.go",
    "content": "package highlight\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/alecthomas/chroma/v2\"\n\t\"github.com/alecthomas/chroma/v2/lexers\"\n\t\"github.com/alecthomas/chroma/v2/styles\"\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nvar default_style = sync.OnceValue(func() *chroma.Style {\n\t// Default style generated by python style.py default pygments.styles.default.DefaultStyle\n\t// with https://raw.githubusercontent.com/alecthomas/chroma/master/_tools/style.py\n\treturn styles.Register(chroma.MustNewStyle(\"default\", chroma.StyleEntries{\n\t\tchroma.TextWhitespace:        \"#bbbbbb\",\n\t\tchroma.Comment:               \"italic #3D7B7B\",\n\t\tchroma.CommentPreproc:        \"noitalic #9C6500\",\n\t\tchroma.Keyword:               \"bold #008000\",\n\t\tchroma.KeywordPseudo:         \"nobold\",\n\t\tchroma.KeywordType:           \"nobold #B00040\",\n\t\tchroma.Operator:              \"#666666\",\n\t\tchroma.OperatorWord:          \"bold #AA22FF\",\n\t\tchroma.NameBuiltin:           \"#008000\",\n\t\tchroma.NameFunction:          \"#0000FF\",\n\t\tchroma.NameClass:             \"bold #0000FF\",\n\t\tchroma.NameNamespace:         \"bold #0000FF\",\n\t\tchroma.NameException:         \"bold #CB3F38\",\n\t\tchroma.NameVariable:          \"#19177C\",\n\t\tchroma.NameConstant:          \"#880000\",\n\t\tchroma.NameLabel:             \"#767600\",\n\t\tchroma.NameEntity:            \"bold #717171\",\n\t\tchroma.NameAttribute:         \"#687822\",\n\t\tchroma.NameTag:               \"bold #008000\",\n\t\tchroma.NameDecorator:         \"#AA22FF\",\n\t\tchroma.LiteralString:         \"#BA2121\",\n\t\tchroma.LiteralStringDoc:      \"italic\",\n\t\tchroma.LiteralStringInterpol: \"bold #A45A77\",\n\t\tchroma.LiteralStringEscape:   \"bold #AA5D1F\",\n\t\tchroma.LiteralStringRegex:    \"#A45A77\",\n\t\tchroma.LiteralStringSymbol:   \"#19177C\",\n\t\tchroma.LiteralStringOther:    \"#008000\",\n\t\tchroma.LiteralNumber:         \"#666666\",\n\t\tchroma.GenericHeading:        \"bold #000080\",\n\t\tchroma.GenericSubheading:     \"bold #800080\",\n\t\tchroma.GenericDeleted:        \"#A00000\",\n\t\tchroma.GenericInserted:       \"#008400\",\n\t\tchroma.GenericError:          \"#E40000\",\n\t\tchroma.GenericEmph:           \"italic\",\n\t\tchroma.GenericStrong:         \"bold\",\n\t\tchroma.GenericPrompt:         \"bold #000080\",\n\t\tchroma.GenericOutput:         \"#717171\",\n\t\tchroma.GenericTraceback:      \"#04D\",\n\t\tchroma.Error:                 \"border:#FF0000\",\n\t\tchroma.Background:            \" bg:#f8f8f8\",\n\t}))\n})\n\n// Clear the background colour.\nfunc clear_background(style *chroma.Style) *chroma.Style {\n\tbuilder := style.Builder()\n\tbg := builder.Get(chroma.Background)\n\tbg.Background = 0\n\tbg.NoInherit = true\n\tbuilder.AddEntry(chroma.Background, bg)\n\tstyle, _ = builder.Build()\n\treturn style\n}\n\nfunc ansi_formatter(w io.Writer, style *chroma.Style, sanitize func(string) string, it chroma.Iterator) (err error) {\n\tconst SGR_PREFIX = \"\\033[\"\n\tconst SGR_SUFFIX = \"m\"\n\tstyle = clear_background(style)\n\tbefore, after := make([]byte, 0, 64), make([]byte, 0, 64)\n\tnl := []byte{'\\n'}\n\twrite_sgr := func(which []byte) (err error) {\n\t\tif len(which) > 1 {\n\t\t\tif _, err = w.Write(utils.UnsafeStringToBytes(SGR_PREFIX)); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err = w.Write(which[:len(which)-1]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err = w.Write(utils.UnsafeStringToBytes(SGR_SUFFIX)); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\twrite := func(text string) (err error) {\n\t\tif err = write_sgr(before); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif _, err = w.Write(utils.UnsafeStringToBytes(text)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = write_sgr(after); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn\n\t}\n\n\tfor token := it(); token != chroma.EOF; token = it() {\n\t\tentry := style.Get(token.Type)\n\t\tbefore, after = before[:0], after[:0]\n\t\tif !entry.IsZero() {\n\t\t\tif entry.Bold == chroma.Yes {\n\t\t\t\tbefore = append(before, '1', ';')\n\t\t\t\tafter = append(after, '2', '2', '1', ';')\n\t\t\t}\n\t\t\tif entry.Underline == chroma.Yes {\n\t\t\t\tbefore = append(before, '4', ';')\n\t\t\t\tafter = append(after, '2', '4', ';')\n\t\t\t}\n\t\t\tif entry.Italic == chroma.Yes {\n\t\t\t\tbefore = append(before, '3', ';')\n\t\t\t\tafter = append(after, '2', '3', ';')\n\t\t\t}\n\t\t\tif entry.Colour.IsSet() {\n\t\t\t\tbefore = append(before, fmt.Sprintf(\"38:2:%d:%d:%d;\", entry.Colour.Red(), entry.Colour.Green(), entry.Colour.Blue())...)\n\t\t\t\tafter = append(after, '3', '9', ';')\n\t\t\t}\n\t\t}\n\t\t// independently format each line in a multiline token, needed for the diff kitten highlighting to work, also\n\t\t// pagers like less reset SGR formatting at line boundaries\n\t\ttext := sanitize(token.Value)\n\t\tfor text != \"\" {\n\t\t\tidx := strings.IndexByte(text, '\\n')\n\t\t\tif idx < 0 {\n\t\t\t\tif err = write(text); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif err = write(text[:idx]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err = w.Write(nl); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ttext = text[idx+1:]\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc resolved_chroma_style(srd StyleResolveData) *chroma.Style {\n\tname := srd.StyleName()\n\tvar style *chroma.Style\n\tif name == \"default\" {\n\t\tstyle = default_style()\n\t} else {\n\t\tstyle = styles.Get(name)\n\t}\n\tif style == nil {\n\t\tif srd.UseLightColors() {\n\t\t\tstyle = default_style()\n\t\t} else {\n\t\t\tstyle = styles.Get(\"monokai\")\n\t\t\tif style == nil {\n\t\t\t\tstyle = styles.Get(\"github-dark\")\n\t\t\t}\n\t\t}\n\t\tif style == nil {\n\t\t\tstyle = styles.Fallback\n\t\t}\n\t}\n\treturn style\n}\n\ntype highlighter struct {\n\ttokens_map map[string][]chroma.Token\n\tlock       sync.Mutex\n\tsanitize   func(string) string\n}\n\nfunc (h *highlighter) Sanitize(x string) string { return h.sanitize(x) }\n\nfunc (h *highlighter) HighlightFile(path string, srd StyleResolveData) (highlighted_string string, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = parallel.Format_stacktrace_on_panic(r, 1)\n\t\t}\n\t}()\n\tfilename_for_detection := filepath.Base(path)\n\text := filepath.Ext(filename_for_detection)\n\tif ext != \"\" {\n\t\text = strings.ToLower(ext[1:])\n\t\tr := srd.SyntaxAliases()[ext]\n\t\tif r != \"\" {\n\t\t\tfilename_for_detection = \"file.\" + r\n\t\t}\n\t}\n\ttext, err := srd.TextForPath(path)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\th.lock.Lock()\n\ttokens := h.tokens_map[path]\n\th.lock.Unlock()\n\tif tokens == nil {\n\t\tlexer := lexers.Match(filename_for_detection)\n\t\tif lexer == nil {\n\t\t\tlexer = lexers.Analyse(text)\n\t\t}\n\t\tif lexer == nil {\n\t\t\treturn \"\", fmt.Errorf(\"Cannot highlight %#v: %w\", path, ErrNoLexer)\n\t\t}\n\t\tlexer = chroma.Coalesce(lexer)\n\t\titerator, err := lexer.Tokenise(nil, text)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\ttokens = iterator.Tokens()\n\t\th.lock.Lock()\n\t\th.tokens_map[path] = tokens\n\t\th.lock.Unlock()\n\t}\n\tw := strings.Builder{}\n\tw.Grow(len(text) * 2)\n\terr = ansi_formatter(&w, resolved_chroma_style(srd), h.sanitize, chroma.Literator(tokens...))\n\treturn w.String(), err\n}\n"
  },
  {
    "path": "tools/icons/file-types.go",
    "content": "package icons\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n)\n\nvar _ = fmt.Print\n\n// file types {{\nconst (\n\tAUDIO           rune = 0xf001  // \n\tBINARY          rune = 0xeae8  // \n\tBOOK            rune = 0xe28b  // \n\tCACHE           rune = 0xf49b  // \n\tCAD             rune = 0xf0eeb // 󰻫\n\tCALENDAR        rune = 0xeab0  // \n\tCLOCK           rune = 0xf43a  // \n\tCOMPRESSED      rune = 0xf410  // \n\tCONFIG          rune = 0xe615  // \n\tCSS3            rune = 0xe749  // \n\tDATABASE        rune = 0xf1c0  // \n\tDIFF            rune = 0xf440  // \n\tDISK_IMAGE      rune = 0xe271  // \n\tDOCKER          rune = 0xe650  // \n\tDOCUMENT        rune = 0xf1c2  // \n\tDOWNLOAD        rune = 0xf01da // 󰇚\n\tEDA_PCB         rune = 0xeabe  // \n\tEDA_SCH         rune = 0xf0b45 // 󰭅\n\tEMACS           rune = 0xe632  // \n\tESLINT          rune = 0xe655  // \n\tFILE            rune = 0xf15b  // \n\tFILE_3D         rune = 0xf01a7 // 󰆧\n\tFILE_OUTLINE    rune = 0xf016  // \n\tFOLDER          rune = 0xe5ff  // \n\tFOLDER_CONFIG   rune = 0xe5fc  // \n\tFOLDER_EXERCISM rune = 0xebe5  // \n\tFOLDER_GIT      rune = 0xe5fb  // \n\tFOLDER_GITHUB   rune = 0xe5fd  // \n\tFOLDER_HIDDEN   rune = 0xf179e // 󱞞\n\tFOLDER_KEY      rune = 0xf08ac // 󰢬\n\tFOLDER_NPM      rune = 0xe5fa  // \n\tFOLDER_OCAML    rune = 0xe67a  // \n\tFOLDER_OPEN     rune = 0xf115  // \n\tFONT            rune = 0xf031  // \n\tFREECAD         rune = 0xf336  // \n\tGIMP            rune = 0xf338  // \n\tGIST_SECRET     rune = 0xeafa  // \n\tGIT             rune = 0xf1d3  // \n\tGODOT           rune = 0xe65f  // \n\tGRADLE          rune = 0xe660  // \n\tGRAPH           rune = 0xf1049 // 󱁉\n\tGRAPHQL         rune = 0xe662  // \n\tGRUNT           rune = 0xe611  // \n\tGTK             rune = 0xf362  // \n\tGULP            rune = 0xe610  // \n\tHTML5           rune = 0xf13b  // \n\tIMAGE           rune = 0xf1c5  // \n\tINFO            rune = 0xf129  // \n\tINTELLIJ        rune = 0xe7b5  // \n\tJSON            rune = 0xe60b  // \n\tKDENLIVE        rune = 0xf33c  // \n\tKEY             rune = 0xeb11  // \n\tKEYPASS         rune = 0xf23e  // \n\tKICAD           rune = 0xf34c  // \n\tKRITA           rune = 0xf33d  // \n\tLANG_ARDUINO    rune = 0xf34b  // \n\tLANG_ASSEMBLY   rune = 0xe637  // \n\tLANG_C          rune = 0xe61e  // \n\tLANG_CPP        rune = 0xe61d  // \n\tLANG_CSHARP     rune = 0xf031b // 󰌛\n\tLANG_D          rune = 0xe7af  // \n\tLANG_ELIXIR     rune = 0xe62d  // \n\tLANG_FENNEL     rune = 0xe6af  // \n\tLANG_FORTRAN    rune = 0xf121a // 󱈚\n\tLANG_FSHARP     rune = 0xe7a7  // \n\tLANG_GLEAM      rune = 0xf09a5 // 󰦥\n\tLANG_GO         rune = 0xe65e  // \n\tLANG_GROOVY     rune = 0xe775  // \n\tLANG_HASKELL    rune = 0xe777  // \n\tLANG_HDL        rune = 0xf035b // 󰍛\n\tLANG_HOLYC      rune = 0xf00a2 // 󰂢\n\tLANG_JAVA       rune = 0xe256  // \n\tLANG_JAVASCRIPT rune = 0xe74e  // \n\tLANG_KOTLIN     rune = 0xe634  // \n\tLANG_LUA        rune = 0xe620  // \n\tLANG_NIM        rune = 0xe677  // \n\tLANG_OCAML      rune = 0xe67a  // \n\tLANG_PERL       rune = 0xe67e  // \n\tLANG_PHP        rune = 0xe73d  // \n\tLANG_PYTHON     rune = 0xe606  // \n\tLANG_R          rune = 0xe68a  // \n\tLANG_RUBY       rune = 0xe739  // \n\tLANG_RUBYRAILS  rune = 0xe73b  // \n\tLANG_RUST       rune = 0xe68b  // \n\tLANG_SASS       rune = 0xe603  // \n\tLANG_SCHEME     rune = 0xe6b1  // \n\tLANG_STYLUS     rune = 0xe600  // \n\tLANG_TEX        rune = 0xe69b  // \n\tLANG_TYPESCRIPT rune = 0xe628  // \n\tLANG_V          rune = 0xe6ac  // \n\tLIBRARY         rune = 0xeb9c  // \n\tLICENSE         rune = 0xf02d  // \n\tLOCK            rune = 0xf023  // \n\tLOG             rune = 0xf18d  // \n\tMAKE            rune = 0xe673  // \n\tMARKDOWN        rune = 0xf48a  // \n\tMUSTACHE        rune = 0xe60f  // \n\tNAMED_PIPE      rune = 0xf07e5 // 󰟥\n\tNODEJS          rune = 0xe718  // \n\tNOTEBOOK        rune = 0xe678  // \n\tNPM             rune = 0xe71e  // \n\tOS_ANDROID      rune = 0xe70e  // \n\tOS_APPLE        rune = 0xf179  // \n\tOS_LINUX        rune = 0xf17c  // \n\tOS_WINDOWS      rune = 0xf17a  // \n\tOS_WINDOWS_CMD  rune = 0xebc4  // \n\tPLAYLIST        rune = 0xf0cb9 // 󰲹\n\tPOWERSHELL      rune = 0xebc7  // \n\tPRIVATE_KEY     rune = 0xf0306 // 󰌆\n\tPUBLIC_KEY      rune = 0xf0dd6 // 󰷖\n\tQT              rune = 0xf375  // \n\tRAZOR           rune = 0xf1fa  // \n\tREACT           rune = 0xe7ba  // \n\tREADME          rune = 0xf00ba // 󰂺\n\tSHEET           rune = 0xf1c3  // \n\tSHELL           rune = 0xf1183 // 󱆃\n\tSHELL_CMD       rune = 0xf489  // \n\tSHIELD_CHECK    rune = 0xf0565 // 󰕥\n\tSHIELD_KEY      rune = 0xf0bc4 // 󰯄\n\tSHIELD_LOCK     rune = 0xf099d // 󰦝\n\tSIGNED_FILE     rune = 0xf19c3 // 󱧃\n\tSLIDE           rune = 0xf1c4  // \n\tSOCKET          rune = 0xf0427 // 󰐧\n\tSQLITE          rune = 0xe7c4  // \n\tSUBLIME         rune = 0xe7aa  // \n\tSUBTITLE        rune = 0xf0a16 // 󰨖\n\tSYMLINK         rune = 0xf481  // \n\tSYMLINK_TO_DIR  rune = 0xf482  // \n\tTERRAFORM       rune = 0xf1062 // 󱁢\n\tTEXT            rune = 0xf15c  // \n\tTMUX            rune = 0xebc8  // \n\tTOML            rune = 0xe6b2  // \n\tTRANSLATION     rune = 0xf05ca // 󰗊\n\tTYPST           rune = 0xf37f  // \n\tUNITY           rune = 0xe721  // \n\tVECTOR          rune = 0xf0559 // 󰕙\n\tVIDEO           rune = 0xf03d  // \n\tVIM             rune = 0xe7c5  // \n\tWRENCH          rune = 0xf0ad  // \n\tXML             rune = 0xf05c0 // 󰗀\n\tYAML            rune = 0xe6a8  // \n\tYARN            rune = 0xe6a7  // \n) // }}}\n\nvar DirectoryNameMap = sync.OnceValue(func() map[string]rune { // {{{\n\treturn map[string]rune{\n\t\t\".config\":       FOLDER_CONFIG,   // \n\t\t\".exercism\":     FOLDER_EXERCISM, // \n\t\t\".git\":          FOLDER_GIT,      // \n\t\t\".github\":       FOLDER_GITHUB,   // \n\t\t\".npm\":          FOLDER_NPM,      // \n\t\t\".opam\":         FOLDER_OCAML,    // \n\t\t\".ssh\":          FOLDER_KEY,      // 󰢬\n\t\t\".Trash\":        0xf1f8,          // \n\t\t\"cabal\":         LANG_HASKELL,    // \n\t\t\"config\":        FOLDER_CONFIG,   // \n\t\t\"Contacts\":      0xf024c,         // 󰉌\n\t\t\"cron.d\":        FOLDER_CONFIG,   // \n\t\t\"cron.daily\":    FOLDER_CONFIG,   // \n\t\t\"cron.hourly\":   FOLDER_CONFIG,   // \n\t\t\"cron.minutely\": FOLDER_CONFIG,   // \n\t\t\"cron.monthly\":  FOLDER_CONFIG,   // \n\t\t\"cron.weekly\":   FOLDER_CONFIG,   // \n\t\t\"Desktop\":       0xf108,          // \n\t\t\"Downloads\":     0xf024d,         // 󰉍\n\t\t\"etc\":           FOLDER_CONFIG,   // \n\t\t\"Favorites\":     0xf069d,         // 󰚝\n\t\t\"hidden\":        FOLDER_HIDDEN,   // 󱞞\n\t\t\"home\":          0xf10b5,         // 󱂵\n\t\t\"~\":             0xf10b5,         // 󱂵\n\t\t\"include\":       FOLDER_CONFIG,   // \n\t\t\"Mail\":          0xf01f0,         // 󰇰\n\t\t\"Movies\":        0xf0fce,         // 󰿎\n\t\t\"Music\":         0xf1359,         // 󱍙\n\t\t\"node_modules\":  FOLDER_NPM,      // \n\t\t\"npm_cache\":     FOLDER_NPM,      // \n\t\t\"pam.d\":         FOLDER_KEY,      // 󰢬\n\t\t\"Pictures\":      0xf024f,         // 󰉏\n\t\t\"ssh\":           FOLDER_KEY,      // 󰢬\n\t\t\"sudoers.d\":     FOLDER_KEY,      // 󰢬\n\t\t\"Videos\":        0xf03d,          // \n\t\t\"xbps.d\":        FOLDER_CONFIG,   // \n\t\t\"xorg.conf.d\":   FOLDER_CONFIG,   // \n\t}\n}) // }}}\n\nvar FileNameMap = sync.OnceValue(func() map[string]rune { // {{{\n\treturn map[string]rune{\n\n\t\t\"._DS_Store\":                 OS_APPLE,        // \n\t\t\".aliases\":                   SHELL,           // 󱆃\n\t\t\".atom\":                      0xe764,          // \n\t\t\".bash_aliases\":              SHELL,           // 󱆃\n\t\t\".bash_history\":              SHELL,           // 󱆃\n\t\t\".bash_logout\":               SHELL,           // 󱆃\n\t\t\".bash_profile\":              SHELL,           // 󱆃\n\t\t\".bashrc\":                    SHELL,           // 󱆃\n\t\t\".CFUserTextEncoding\":        OS_APPLE,        // \n\t\t\".clang-format\":              CONFIG,          // \n\t\t\".clang-tidy\":                CONFIG,          // \n\t\t\".codespellrc\":               0xf04c6,         // 󰓆\n\t\t\".condarc\":                   0xe715,          // \n\t\t\".cshrc\":                     SHELL,           // 󱆃\n\t\t\".DS_Store\":                  OS_APPLE,        // \n\t\t\".editorconfig\":              0xe652,          // \n\t\t\".emacs\":                     EMACS,           // \n\t\t\".envrc\":                     0xf462,          // \n\t\t\".eslintignore\":              ESLINT,          // \n\t\t\".eslintrc.cjs\":              ESLINT,          // \n\t\t\".eslintrc.js\":               ESLINT,          // \n\t\t\".eslintrc.json\":             ESLINT,          // \n\t\t\".eslintrc.yaml\":             ESLINT,          // \n\t\t\".eslintrc.yml\":              ESLINT,          // \n\t\t\".fennelrc\":                  LANG_FENNEL,     // \n\t\t\".gcloudignore\":              0xf11f6,         // 󱇶\n\t\t\".git-blame-ignore-revs\":     GIT,             // \n\t\t\".gitattributes\":             GIT,             // \n\t\t\".gitconfig\":                 GIT,             // \n\t\t\".gitignore\":                 GIT,             // \n\t\t\".gitignore_global\":          GIT,             // \n\t\t\".gitlab-ci.yml\":             0xf296,          // \n\t\t\".gitmodules\":                GIT,             // \n\t\t\".gtkrc-2.0\":                 GTK,             // \n\t\t\".gvimrc\":                    VIM,             // \n\t\t\".htaccess\":                  CONFIG,          // \n\t\t\".htpasswd\":                  CONFIG,          // \n\t\t\".idea\":                      INTELLIJ,        // \n\t\t\".ideavimrc\":                 VIM,             // \n\t\t\".inputrc\":                   CONFIG,          // \n\t\t\".kshrc\":                     SHELL,           // 󱆃\n\t\t\".login\":                     SHELL,           // 󱆃\n\t\t\".logout\":                    SHELL,           // 󱆃\n\t\t\".luacheckrc\":                CONFIG,          // \n\t\t\".luaurc\":                    CONFIG,          // \n\t\t\".mailmap\":                   GIT,             // \n\t\t\".nanorc\":                    0xe838,          // \n\t\t\".node_repl_history\":         NODEJS,          // \n\t\t\".npmignore\":                 NPM,             // \n\t\t\".npmrc\":                     NPM,             // \n\t\t\".nuxtrc\":                    0xf1106,         // 󱄆\n\t\t\".ocamlinit\":                 LANG_OCAML,      // \n\t\t\".parentlock\":                LOCK,            // \n\t\t\".pre-commit-config.yaml\":    0xf06e2,         // 󰛢\n\t\t\".prettierignore\":            0xe6b4,          // \n\t\t\".prettierrc\":                0xe6b4,          // \n\t\t\".profile\":                   SHELL,           // 󱆃\n\t\t\".pylintrc\":                  CONFIG,          // \n\t\t\".python_history\":            LANG_PYTHON,     // \n\t\t\".rustfmt.toml\":              LANG_RUST,       // \n\t\t\".rvm\":                       LANG_RUBY,       // \n\t\t\".rvmrc\":                     LANG_RUBY,       // \n\t\t\".SRCINFO\":                   0xf303,          // \n\t\t\".stowrc\":                    0xeef1,          // \n\t\t\".tcshrc\":                    SHELL,           // 󱆃\n\t\t\".viminfo\":                   VIM,             // \n\t\t\".vimrc\":                     VIM,             // \n\t\t\".Xauthority\":                CONFIG,          // \n\t\t\".xinitrc\":                   CONFIG,          // \n\t\t\".Xresources\":                CONFIG,          // \n\t\t\".yarnrc\":                    YARN,            // \n\t\t\".zlogin\":                    SHELL,           // 󱆃\n\t\t\".zlogout\":                   SHELL,           // 󱆃\n\t\t\".zprofile\":                  SHELL,           // 󱆃\n\t\t\".zsh_history\":               SHELL,           // 󱆃\n\t\t\".zsh_sessions\":              SHELL,           // 󱆃\n\t\t\".zshenv\":                    SHELL,           // 󱆃\n\t\t\".zshrc\":                     SHELL,           // 󱆃\n\t\t\"_gvimrc\":                    VIM,             // \n\t\t\"_vimrc\":                     VIM,             // \n\t\t\"a.out\":                      SHELL_CMD,       // \n\t\t\"authorized_keys\":            0xf08c0,         // 󰣀\n\t\t\"AUTHORS\":                    0xedca,          // \n\t\t\"AUTHORS.txt\":                0xedca,          // \n\t\t\"bashrc\":                     SHELL,           // 󱆃\n\t\t\"Brewfile\":                   0xf1116,         // 󱄖\n\t\t\"Brewfile.lock.json\":         0xf1116,         // 󱄖\n\t\t\"bspwmrc\":                    0xf355,          // \n\t\t\"build.gradle.kts\":           GRADLE,          // \n\t\t\"build.zig.zon\":              0xe6a9,          // \n\t\t\"bun.lockb\":                  0xe76f,          // \n\t\t\"cantorrc\":                   0xf373,          // \n\t\t\"Cargo.lock\":                 LANG_RUST,       // \n\t\t\"Cargo.toml\":                 LANG_RUST,       // \n\t\t\"CMakeLists.txt\":             0xe794,          // \n\t\t\"CODE_OF_CONDUCT\":            0xf4ae,          // \n\t\t\"CODE_OF_CONDUCT.md\":         0xf4ae,          // \n\t\t\"COMMIT_EDITMSG\":             GIT,             // \n\t\t\"compose.yaml\":               DOCKER,          // \n\t\t\"compose.yml\":                DOCKER,          // \n\t\t\"composer.json\":              LANG_PHP,        // \n\t\t\"composer.lock\":              LANG_PHP,        // \n\t\t\"config\":                     CONFIG,          // \n\t\t\"config.ru\":                  LANG_RUBY,       // \n\t\t\"config.status\":              CONFIG,          // \n\t\t\"configure\":                  WRENCH,          // \n\t\t\"configure.ac\":               CONFIG,          // \n\t\t\"configure.in\":               CONFIG,          // \n\t\t\"constraints.txt\":            LANG_PYTHON,     // \n\t\t\"COPYING\":                    LICENSE,         // \n\t\t\"COPYRIGHT\":                  LICENSE,         // \n\t\t\"crontab\":                    CONFIG,          // \n\t\t\"crypttab\":                   CONFIG,          // \n\t\t\"csh.cshrc\":                  SHELL,           // 󱆃\n\t\t\"csh.login\":                  SHELL,           // 󱆃\n\t\t\"csh.logout\":                 SHELL,           // 󱆃\n\t\t\"docker-compose.yaml\":        DOCKER,          // \n\t\t\"docker-compose.yml\":         DOCKER,          // \n\t\t\"Dockerfile\":                 DOCKER,          // \n\t\t\"dune\":                       LANG_OCAML,      // \n\t\t\"dune-project\":               WRENCH,          // \n\t\t\"Earthfile\":                  0xf0ac,          // \n\t\t\"environment\":                CONFIG,          // \n\t\t\"favicon.ico\":                0xe623,          // \n\t\t\"fennelrc\":                   LANG_FENNEL,     // \n\t\t\"flake.lock\":                 0xf313,          // \n\t\t\"fonts.conf\":                 FONT,            // \n\t\t\"fp-info-cache\":              KICAD,           // \n\t\t\"fp-lib-table\":               KICAD,           // \n\t\t\"FreeCAD.conf\":               FREECAD,         // \n\t\t\"Gemfile\":                    LANG_RUBY,       // \n\t\t\"Gemfile.lock\":               LANG_RUBY,       // \n\t\t\"GNUmakefile\":                MAKE,            // \n\t\t\"go.mod\":                     LANG_GO,         // \n\t\t\"go.sum\":                     LANG_GO,         // \n\t\t\"go.work\":                    LANG_GO,         // \n\t\t\"gradle\":                     GRADLE,          // \n\t\t\"gradle.properties\":          GRADLE,          // \n\t\t\"gradlew\":                    GRADLE,          // \n\t\t\"gradlew.bat\":                GRADLE,          // \n\t\t\"group\":                      LOCK,            // \n\t\t\"gruntfile.coffee\":           GRUNT,           // \n\t\t\"gruntfile.js\":               GRUNT,           // \n\t\t\"gruntfile.ls\":               GRUNT,           // \n\t\t\"gshadow\":                    LOCK,            // \n\t\t\"gtkrc\":                      GTK,             // \n\t\t\"gulpfile.coffee\":            GULP,            // \n\t\t\"gulpfile.js\":                GULP,            // \n\t\t\"gulpfile.ls\":                GULP,            // \n\t\t\"heroku.yml\":                 0xe77b,          // \n\t\t\"hostname\":                   CONFIG,          // \n\t\t\"hypridle.conf\":              0xf359,          // \n\t\t\"hyprland.conf\":              0xf359,          // \n\t\t\"hyprlock.conf\":              0xf359,          // \n\t\t\"hyprpaper.conf\":             0xf359,          // \n\t\t\"i3blocks.conf\":              0xf35a,          // \n\t\t\"i3status.conf\":              0xf35a,          // \n\t\t\"id_dsa\":                     PRIVATE_KEY,     // 󰌆\n\t\t\"id_ecdsa\":                   PRIVATE_KEY,     // 󰌆\n\t\t\"id_ecdsa_sk\":                PRIVATE_KEY,     // 󰌆\n\t\t\"id_ed25519\":                 PRIVATE_KEY,     // 󰌆\n\t\t\"id_ed25519_sk\":              PRIVATE_KEY,     // 󰌆\n\t\t\"id_rsa\":                     PRIVATE_KEY,     // 󰌆\n\t\t\"index.theme\":                0xee72,          // \n\t\t\"inputrc\":                    CONFIG,          // \n\t\t\"Jenkinsfile\":                0xe66e,          // \n\t\t\"jsconfig.json\":              LANG_JAVASCRIPT, // \n\t\t\"Justfile\":                   WRENCH,          // \n\t\t\"justfile\":                   WRENCH,          // \n\t\t\"kalgebrarc\":                 0xf373,          // \n\t\t\"kdeglobals\":                 0xf373,          // \n\t\t\"kdenlive-layoutsrc\":         KDENLIVE,        // \n\t\t\"kdenliverc\":                 KDENLIVE,        // \n\t\t\"kitty.conf\":                 '🐱',\n\t\t\"known_hosts\":                0xf08c0,         // 󰣀\n\t\t\"kritadisplayrc\":             KRITA,           // \n\t\t\"kritarc\":                    KRITA,           // \n\t\t\"LICENCE\":                    LICENSE,         // \n\t\t\"LICENCE.md\":                 LICENSE,         // \n\t\t\"LICENCE.txt\":                LICENSE,         // \n\t\t\"LICENSE\":                    LICENSE,         // \n\t\t\"LICENSE-APACHE\":             LICENSE,         // \n\t\t\"LICENSE-MIT\":                LICENSE,         // \n\t\t\"LICENSE.md\":                 LICENSE,         // \n\t\t\"LICENSE.txt\":                LICENSE,         // \n\t\t\"localized\":                  OS_APPLE,        // \n\t\t\"localtime\":                  CLOCK,           // \n\t\t\"lock\":                       LOCK,            // \n\t\t\"LOCK\":                       LOCK,            // \n\t\t\"log\":                        LOG,             // \n\t\t\"LOG\":                        LOG,             // \n\t\t\"lxde-rc.xml\":                0xf363,          // \n\t\t\"lxqt.conf\":                  0xf364,          // \n\t\t\"Makefile\":                   MAKE,            // \n\t\t\"makefile\":                   MAKE,            // \n\t\t\"Makefile.ac\":                MAKE,            // \n\t\t\"Makefile.am\":                MAKE,            // \n\t\t\"Makefile.in\":                MAKE,            // \n\t\t\"MANIFEST\":                   LANG_PYTHON,     // \n\t\t\"MANIFEST.in\":                LANG_PYTHON,     // \n\t\t\"mix.lock\":                   LANG_ELIXIR,     // \n\t\t\"mpv.conf\":                   0xf36e,          // \n\t\t\"npm-shrinkwrap.json\":        NPM,             // \n\t\t\"npmrc\":                      NPM,             // \n\t\t\"package-lock.json\":          NPM,             // \n\t\t\"package.json\":               NPM,             // \n\t\t\"passwd\":                     LOCK,            // \n\t\t\"php.ini\":                    LANG_PHP,        // \n\t\t\"PKGBUILD\":                   0xf303,          // \n\t\t\"platformio.ini\":             0xe682,          // \n\t\t\"pom.xml\":                    0xe674,          // \n\t\t\"Procfile\":                   0xe77b,          // \n\t\t\"profile\":                    SHELL,           // 󱆃\n\t\t\"PrusaSlicer.ini\":            0xf351,          // \n\t\t\"PrusaSlicerGcodeViewer.ini\": 0xf351,          // \n\t\t\"pyproject.toml\":             LANG_PYTHON,     // \n\t\t\"pyvenv.cfg\":                 LANG_PYTHON,     // \n\t\t\"qt5ct.conf\":                 QT,              // \n\t\t\"qt6ct.conf\":                 QT,              // \n\t\t\"QtProject.conf\":             QT,              // \n\t\t\"Rakefile\":                   LANG_RUBY,       // \n\t\t\"README\":                     README,          // 󰂺\n\t\t\"README.md\":                  README,          // 󰂺\n\t\t\"release.toml\":               LANG_RUST,       // \n\t\t\"renovate.json\":              0xf027c,         // 󰉼\n\t\t\"requirements.txt\":           LANG_PYTHON,     // \n\t\t\"robots.txt\":                 0xf06a9,         // 󰚩\n\t\t\"rubydoc\":                    LANG_RUBYRAILS,  // \n\t\t\"rvmrc\":                      LANG_RUBY,       // \n\t\t\"SECURITY\":                   0xf0483,         // 󰒃\n\t\t\"SECURITY.md\":                0xf0483,         // 󰒃\n\t\t\"settings.gradle.kts\":        GRADLE,          // \n\t\t\"shadow\":                     LOCK,            // \n\t\t\"shells\":                     CONFIG,          // \n\t\t\"sudoers\":                    LOCK,            // \n\t\t\"sxhkdrc\":                    CONFIG,          // \n\t\t\"sym-lib-table\":              KICAD,           // \n\t\t\"timezone\":                   CLOCK,           // \n\t\t\"tmux.conf\":                  TMUX,            // \n\t\t\"tmux.conf.local\":            TMUX,            // \n\t\t\"tsconfig.json\":              LANG_TYPESCRIPT, // \n\t\t\"Vagrantfile\":                0x2371,          // ⍱\n\t\t\"vlcrc\":                      0xf057c,         // 󰕼\n\t\t\"webpack.config.js\":          0xf072b,         // 󰜫\n\t\t\"weston.ini\":                 0xf367,          // \n\t\t\"xmobarrc\":                   0xf35e,          // \n\t\t\"xmobarrc.hs\":                0xf35e,          // \n\t\t\"xmonad.hs\":                  0xf35e,          // \n\t\t\"yarn.lock\":                  YARN,            // \n\t\t\"zlogin\":                     SHELL,           // 󱆃\n\t\t\"zlogout\":                    SHELL,           // 󱆃\n\t\t\"zprofile\":                   SHELL,           // 󱆃\n\t\t\"zshenv\":                     SHELL,           // 󱆃\n\t\t\"zshrc\":                      SHELL,           // 󱆃\n\t}\n}) // }}}\n\nvar ExtensionMap = sync.OnceValue(func() map[string]rune { // {{{\n\treturn map[string]rune{\n\t\t\"123dx\":            CAD,             // 󰻫\n\t\t\"3dm\":              CAD,             // 󰻫\n\t\t\"3g2\":              VIDEO,           // \n\t\t\"3gp\":              VIDEO,           // \n\t\t\"3gp2\":             VIDEO,           // \n\t\t\"3gpp\":             VIDEO,           // \n\t\t\"3gpp2\":            VIDEO,           // \n\t\t\"3mf\":              FILE_3D,         // 󰆧\n\t\t\"7z\":               COMPRESSED,      // \n\t\t\"a\":                OS_LINUX,        // \n\t\t\"aac\":              AUDIO,           // \n\t\t\"acf\":              0xf1b6,          // \n\t\t\"age\":              SHIELD_LOCK,     // 󰦝\n\t\t\"ai\":               0xe7b4,          // \n\t\t\"aif\":              AUDIO,           // \n\t\t\"aifc\":             AUDIO,           // \n\t\t\"aiff\":             AUDIO,           // \n\t\t\"alac\":             AUDIO,           // \n\t\t\"android\":          OS_ANDROID,      // \n\t\t\"ape\":              AUDIO,           // \n\t\t\"apk\":              OS_ANDROID,      // \n\t\t\"app\":              BINARY,          // \n\t\t\"apple\":            OS_APPLE,        // \n\t\t\"applescript\":      OS_APPLE,        // \n\t\t\"ar\":               COMPRESSED,      // \n\t\t\"arj\":              COMPRESSED,      // \n\t\t\"arw\":              IMAGE,           // \n\t\t\"asc\":              SHIELD_LOCK,     // 󰦝\n\t\t\"asm\":              LANG_ASSEMBLY,   // \n\t\t\"asp\":              0xf121,          // \n\t\t\"ass\":              SUBTITLE,        // 󰨖\n\t\t\"avi\":              VIDEO,           // \n\t\t\"avif\":             IMAGE,           // \n\t\t\"avro\":             JSON,            // \n\t\t\"awk\":              SHELL_CMD,       // \n\t\t\"bash\":             SHELL_CMD,       // \n\t\t\"bat\":              OS_WINDOWS_CMD,  // \n\t\t\"bats\":             SHELL_CMD,       // \n\t\t\"bdf\":              FONT,            // \n\t\t\"bib\":              LANG_TEX,        // \n\t\t\"bin\":              BINARY,          // \n\t\t\"blend\":            0xf00ab,         // 󰂫\n\t\t\"bmp\":              IMAGE,           // \n\t\t\"br\":               COMPRESSED,      // \n\t\t\"brd\":              EDA_PCB,         // \n\t\t\"brep\":             CAD,             // 󰻫\n\t\t\"bst\":              LANG_TEX,        // \n\t\t\"bundle\":           OS_APPLE,        // \n\t\t\"bz\":               COMPRESSED,      // \n\t\t\"bz2\":              COMPRESSED,      // \n\t\t\"bz3\":              COMPRESSED,      // \n\t\t\"c\":                LANG_C,          // \n\t\t\"c++\":              LANG_CPP,        // \n\t\t\"cab\":              OS_WINDOWS,      // \n\t\t\"cache\":            CACHE,           // \n\t\t\"cast\":             VIDEO,           // \n\t\t\"catpart\":          CAD,             // 󰻫\n\t\t\"catproduct\":       CAD,             // 󰻫\n\t\t\"cbr\":              IMAGE,           // \n\t\t\"cbz\":              IMAGE,           // \n\t\t\"cc\":               LANG_CPP,        // \n\t\t\"cert\":             GIST_SECRET,     // \n\t\t\"cfg\":              CONFIG,          // \n\t\t\"cjs\":              LANG_JAVASCRIPT, // \n\t\t\"class\":            LANG_JAVA,       // \n\t\t\"clj\":              0xe768,          // \n\t\t\"cljc\":             0xe768,          // \n\t\t\"cljs\":             0xe76a,          // \n\t\t\"cls\":              LANG_TEX,        // \n\t\t\"cmake\":            0xe794,          // \n\t\t\"cmd\":              OS_WINDOWS,      // \n\t\t\"coffee\":           0xf0f4,          // \n\t\t\"com\":              0xe629,          // \n\t\t\"conda\":            0xe715,          // \n\t\t\"conf\":             CONFIG,          // \n\t\t\"config\":           CONFIG,          // \n\t\t\"cow\":              0xf019a,         // 󰆚\n\t\t\"cp\":               LANG_CPP,        // \n\t\t\"cpio\":             COMPRESSED,      // \n\t\t\"cpp\":              LANG_CPP,        // \n\t\t\"cr\":               0xe62f,          // \n\t\t\"cr2\":              IMAGE,           // \n\t\t\"crdownload\":       DOWNLOAD,        // 󰇚\n\t\t\"crt\":              GIST_SECRET,     // \n\t\t\"cs\":               LANG_CSHARP,     // 󰌛\n\t\t\"csh\":              SHELL_CMD,       // \n\t\t\"cshtml\":           RAZOR,           // \n\t\t\"csproj\":           LANG_CSHARP,     // 󰌛\n\t\t\"css\":              CSS3,            // \n\t\t\"csv\":              SHEET,           // \n\t\t\"csx\":              LANG_CSHARP,     // 󰌛\n\t\t\"cts\":              LANG_TYPESCRIPT, // \n\t\t\"cu\":               0xe64b,          // \n\t\t\"cue\":              PLAYLIST,        // 󰲹\n\t\t\"cxx\":              LANG_CPP,        // \n\t\t\"d\":                LANG_D,          // \n\t\t\"dart\":             0xe798,          // \n\t\t\"db\":               DATABASE,        // \n\t\t\"db3\":              SQLITE,          // \n\t\t\"dconf\":            DATABASE,        // \n\t\t\"deb\":              0xe77d,          // \n\t\t\"desktop\":          0xebd1,          // \n\t\t\"di\":               LANG_D,          // \n\t\t\"diff\":             DIFF,            // \n\t\t\"djv\":              DOCUMENT,        // \n\t\t\"djvu\":             DOCUMENT,        // \n\t\t\"dll\":              LIBRARY,         // \n\t\t\"dmg\":              DISK_IMAGE,      // \n\t\t\"doc\":              DOCUMENT,        // \n\t\t\"dockerfile\":       DOCKER,          // \n\t\t\"dockerignore\":     DOCKER,          // \n\t\t\"docm\":             DOCUMENT,        // \n\t\t\"docx\":             DOCUMENT,        // \n\t\t\"dot\":              GRAPH,           // 󱁉\n\t\t\"download\":         DOWNLOAD,        // 󰇚\n\t\t\"drawio\":           0xebba,          // \n\t\t\"dump\":             DATABASE,        // \n\t\t\"dvi\":              IMAGE,           // \n\t\t\"dwg\":              CAD,             // 󰻫\n\t\t\"dxf\":              CAD,             // 󰻫\n\t\t\"dylib\":            OS_APPLE,        // \n\t\t\"ebook\":            BOOK,            // \n\t\t\"ebuild\":           0xf30d,          // \n\t\t\"editorconfig\":     0xe652,          // \n\t\t\"edn\":              0xe76a,          // \n\t\t\"eex\":              LANG_ELIXIR,     // \n\t\t\"ejs\":              0xe618,          // \n\t\t\"el\":               EMACS,           // \n\t\t\"elc\":              EMACS,           // \n\t\t\"elf\":              BINARY,          // \n\t\t\"elm\":              0xe62c,          // \n\t\t\"eml\":              0xf003,          // \n\t\t\"env\":              0xf462,          // \n\t\t\"eot\":              FONT,            // \n\t\t\"eps\":              VECTOR,          // 󰕙\n\t\t\"epub\":             BOOK,            // \n\t\t\"erb\":              LANG_RUBYRAILS,  // \n\t\t\"erl\":              0xe7b1,          // \n\t\t\"ex\":               LANG_ELIXIR,     // \n\t\t\"exe\":              OS_WINDOWS_CMD,  // \n\t\t\"exs\":              LANG_ELIXIR,     // \n\t\t\"f\":                LANG_FORTRAN,    // 󱈚\n\t\t\"f#\":               LANG_FSHARP,     // \n\t\t\"f3d\":              CAD,             // 󰻫\n\t\t\"f3z\":              CAD,             // 󰻫\n\t\t\"f90\":              LANG_FORTRAN,    // 󱈚\n\t\t\"fbx\":              FILE_3D,         // 󰆧\n\t\t\"fcbak\":            FREECAD,         // \n\t\t\"fcmacro\":          FREECAD,         // \n\t\t\"fcmat\":            FREECAD,         // \n\t\t\"fcparam\":          FREECAD,         // \n\t\t\"fcscript\":         FREECAD,         // \n\t\t\"fcstd\":            FREECAD,         // \n\t\t\"fcstd1\":           FREECAD,         // \n\t\t\"fctb\":             FREECAD,         // \n\t\t\"fctl\":             FREECAD,         // \n\t\t\"fdmdownload\":      DOWNLOAD,        // 󰇚\n\t\t\"fish\":             SHELL_CMD,       // \n\t\t\"flac\":             AUDIO,           // \n\t\t\"flc\":              FONT,            // \n\t\t\"flf\":              FONT,            // \n\t\t\"flv\":              VIDEO,           // \n\t\t\"fnl\":              LANG_FENNEL,     // \n\t\t\"fnt\":              FONT,            // \n\t\t\"fodg\":             0xf379,          // \n\t\t\"fodp\":             0xf37a,          // \n\t\t\"fods\":             0xf378,          // \n\t\t\"fodt\":             0xf37c,          // \n\t\t\"fon\":              FONT,            // \n\t\t\"font\":             FONT,            // \n\t\t\"for\":              LANG_FORTRAN,    // 󱈚\n\t\t\"fs\":               LANG_FSHARP,     // \n\t\t\"fsi\":              LANG_FSHARP,     // \n\t\t\"fsproj\":           LANG_FSHARP,     // \n\t\t\"fsscript\":         LANG_FSHARP,     // \n\t\t\"fsx\":              LANG_FSHARP,     // \n\t\t\"gba\":              0xf1393,         // 󱎓\n\t\t\"gbl\":              EDA_PCB,         // \n\t\t\"gbo\":              EDA_PCB,         // \n\t\t\"gbp\":              EDA_PCB,         // \n\t\t\"gbr\":              EDA_PCB,         // \n\t\t\"gbs\":              EDA_PCB,         // \n\t\t\"gcode\":            0xf0af4,         // 󰫴\n\t\t\"gd\":               GODOT,           // \n\t\t\"gdoc\":             DOCUMENT,        // \n\t\t\"gem\":              LANG_RUBY,       // \n\t\t\"gemfile\":          LANG_RUBY,       // \n\t\t\"gemspec\":          LANG_RUBY,       // \n\t\t\"gform\":            0xf298,          // \n\t\t\"gif\":              IMAGE,           // \n\t\t\"git\":              GIT,             // \n\t\t\"gleam\":            LANG_GLEAM,      // 󰦥\n\t\t\"gm1\":              EDA_PCB,         // \n\t\t\"gml\":              EDA_PCB,         // \n\t\t\"go\":               LANG_GO,         // \n\t\t\"godot\":            GODOT,           // \n\t\t\"gpg\":              SHIELD_LOCK,     // 󰦝\n\t\t\"gql\":              GRAPHQL,         // \n\t\t\"gradle\":           GRADLE,          // \n\t\t\"graphql\":          GRAPHQL,         // \n\t\t\"gresource\":        GTK,             // \n\t\t\"groovy\":           LANG_GROOVY,     // \n\t\t\"gsheet\":           SHEET,           // \n\t\t\"gslides\":          SLIDE,           // \n\t\t\"gtl\":              EDA_PCB,         // \n\t\t\"gto\":              EDA_PCB,         // \n\t\t\"gtp\":              EDA_PCB,         // \n\t\t\"gts\":              EDA_PCB,         // \n\t\t\"guardfile\":        LANG_RUBY,       // \n\t\t\"gv\":               GRAPH,           // 󱁉\n\t\t\"gvy\":              LANG_GROOVY,     // \n\t\t\"gz\":               COMPRESSED,      // \n\t\t\"h\":                LANG_C,          // \n\t\t\"h++\":              LANG_CPP,        // \n\t\t\"h264\":             VIDEO,           // \n\t\t\"haml\":             0xe664,          // \n\t\t\"hbs\":              MUSTACHE,        // \n\t\t\"hc\":               LANG_HOLYC,      // 󰂢\n\t\t\"heic\":             IMAGE,           // \n\t\t\"heics\":            VIDEO,           // \n\t\t\"heif\":             IMAGE,           // \n\t\t\"hex\":              0xf12a7,         // 󱊧\n\t\t\"hh\":               LANG_CPP,        // \n\t\t\"hi\":               BINARY,          // \n\t\t\"hpp\":              LANG_CPP,        // \n\t\t\"hrl\":              0xe7b1,          // \n\t\t\"hs\":               LANG_HASKELL,    // \n\t\t\"htm\":              HTML5,           // \n\t\t\"html\":             HTML5,           // \n\t\t\"hxx\":              LANG_CPP,        // \n\t\t\"iam\":              CAD,             // 󰻫\n\t\t\"ical\":             CALENDAR,        // \n\t\t\"icalendar\":        CALENDAR,        // \n\t\t\"ico\":              IMAGE,           // \n\t\t\"ics\":              CALENDAR,        // \n\t\t\"ifb\":              CALENDAR,        // \n\t\t\"ifc\":              CAD,             // 󰻫\n\t\t\"ige\":              CAD,             // 󰻫\n\t\t\"iges\":             CAD,             // 󰻫\n\t\t\"igs\":              CAD,             // 󰻫\n\t\t\"image\":            DISK_IMAGE,      // \n\t\t\"img\":              DISK_IMAGE,      // \n\t\t\"iml\":              INTELLIJ,        // \n\t\t\"info\":             INFO,            // \n\t\t\"ini\":              CONFIG,          // \n\t\t\"inl\":              LANG_C,          // \n\t\t\"ino\":              LANG_ARDUINO,    // \n\t\t\"ipt\":              CAD,             // 󰻫\n\t\t\"ipynb\":            NOTEBOOK,        // \n\t\t\"iso\":              DISK_IMAGE,      // \n\t\t\"j2c\":              IMAGE,           // \n\t\t\"j2k\":              IMAGE,           // \n\t\t\"jad\":              LANG_JAVA,       // \n\t\t\"jar\":              LANG_JAVA,       // \n\t\t\"java\":             LANG_JAVA,       // \n\t\t\"jfi\":              IMAGE,           // \n\t\t\"jfif\":             IMAGE,           // \n\t\t\"jif\":              IMAGE,           // \n\t\t\"jl\":               0xe624,          // \n\t\t\"jmd\":              MARKDOWN,        // \n\t\t\"jp2\":              IMAGE,           // \n\t\t\"jpe\":              IMAGE,           // \n\t\t\"jpeg\":             IMAGE,           // \n\t\t\"jpf\":              IMAGE,           // \n\t\t\"jpg\":              IMAGE,           // \n\t\t\"jpx\":              IMAGE,           // \n\t\t\"js\":               LANG_JAVASCRIPT, // \n\t\t\"json\":             JSON,            // \n\t\t\"json5\":            JSON,            // \n\t\t\"jsonc\":            JSON,            // \n\t\t\"jsx\":              REACT,           // \n\t\t\"jwmrc\":            0xf35b,          // \n\t\t\"jxl\":              IMAGE,           // \n\t\t\"kbx\":              SHIELD_KEY,      // 󰯄\n\t\t\"kdb\":              KEYPASS,         // \n\t\t\"kdbx\":             KEYPASS,         // \n\t\t\"kdenlive\":         KDENLIVE,        // \n\t\t\"kdenlivetitle\":    KDENLIVE,        // \n\t\t\"key\":              KEY,             // \n\t\t\"kicad_dru\":        KICAD,           // \n\t\t\"kicad_mod\":        KICAD,           // \n\t\t\"kicad_pcb\":        KICAD,           // \n\t\t\"kicad_prl\":        KICAD,           // \n\t\t\"kicad_pro\":        KICAD,           // \n\t\t\"kicad_sch\":        KICAD,           // \n\t\t\"kicad_sym\":        KICAD,           // \n\t\t\"kicad_wks\":        KICAD,           // \n\t\t\"ko\":               OS_LINUX,        // \n\t\t\"kpp\":              KRITA,           // \n\t\t\"kra\":              KRITA,           // \n\t\t\"krz\":              KRITA,           // \n\t\t\"ksh\":              SHELL_CMD,       // \n\t\t\"kt\":               LANG_KOTLIN,     // \n\t\t\"kts\":              LANG_KOTLIN,     // \n\t\t\"latex\":            LANG_TEX,        // \n\t\t\"lbr\":              LIBRARY,         // \n\t\t\"lck\":              LOCK,            // \n\t\t\"ldb\":              DATABASE,        // \n\t\t\"leex\":             LANG_ELIXIR,     // \n\t\t\"less\":             0xe758,          // \n\t\t\"lff\":              FONT,            // \n\t\t\"lhs\":              LANG_HASKELL,    // \n\t\t\"lib\":              LIBRARY,         // \n\t\t\"license\":          LICENSE,         // \n\t\t\"lisp\":             0xf0172,         // 󰅲\n\t\t\"localized\":        OS_APPLE,        // \n\t\t\"lock\":             LOCK,            // \n\t\t\"log\":              LOG,             // \n\t\t\"lpp\":              EDA_PCB,         // \n\t\t\"lrc\":              SUBTITLE,        // 󰨖\n\t\t\"ltx\":              LANG_TEX,        // \n\t\t\"lua\":              LANG_LUA,        // \n\t\t\"luac\":             LANG_LUA,        // \n\t\t\"luau\":             LANG_LUA,        // \n\t\t\"lz\":               COMPRESSED,      // \n\t\t\"lz4\":              COMPRESSED,      // \n\t\t\"lzh\":              COMPRESSED,      // \n\t\t\"lzma\":             COMPRESSED,      // \n\t\t\"lzo\":              COMPRESSED,      // \n\t\t\"m\":                LANG_C,          // \n\t\t\"m2ts\":             VIDEO,           // \n\t\t\"m2v\":              VIDEO,           // \n\t\t\"m3u\":              PLAYLIST,        // 󰲹\n\t\t\"m3u8\":             PLAYLIST,        // 󰲹\n\t\t\"m4a\":              AUDIO,           // \n\t\t\"m4v\":              VIDEO,           // \n\t\t\"magnet\":           0xf076,          // \n\t\t\"markdown\":         MARKDOWN,        // \n\t\t\"md\":               MARKDOWN,        // \n\t\t\"md5\":              SHIELD_CHECK,    // 󰕥\n\t\t\"mdb\":              DATABASE,        // \n\t\t\"mdx\":              MARKDOWN,        // \n\t\t\"mid\":              0xf08f2,         // 󰣲\n\t\t\"mjs\":              LANG_JAVASCRIPT, // \n\t\t\"mk\":               MAKE,            // \n\t\t\"mka\":              AUDIO,           // \n\t\t\"mkd\":              MARKDOWN,        // \n\t\t\"mkv\":              VIDEO,           // \n\t\t\"ml\":               LANG_OCAML,      // \n\t\t\"mli\":              LANG_OCAML,      // \n\t\t\"mll\":              LANG_OCAML,      // \n\t\t\"mly\":              LANG_OCAML,      // \n\t\t\"mm\":               LANG_CPP,        // \n\t\t\"mo\":               TRANSLATION,     // 󰗊\n\t\t\"mobi\":             BOOK,            // \n\t\t\"mov\":              VIDEO,           // \n\t\t\"mp2\":              AUDIO,           // \n\t\t\"mp3\":              AUDIO,           // \n\t\t\"mp4\":              VIDEO,           // \n\t\t\"mpeg\":             VIDEO,           // \n\t\t\"mpg\":              VIDEO,           // \n\t\t\"msf\":              0xf370,          // \n\t\t\"msi\":              OS_WINDOWS,      // \n\t\t\"mts\":              LANG_TYPESCRIPT, // \n\t\t\"mustache\":         MUSTACHE,        // \n\t\t\"nef\":              IMAGE,           // \n\t\t\"nfo\":              INFO,            // \n\t\t\"nim\":              LANG_NIM,        // \n\t\t\"nimble\":           LANG_NIM,        // \n\t\t\"nims\":             LANG_NIM,        // \n\t\t\"ninja\":            0xf0774,         // 󰝴\n\t\t\"nix\":              0xf313,          // \n\t\t\"node\":             NODEJS,          // \n\t\t\"norg\":             0xe847,          // \n\t\t\"nsp\":              0xF07E1,         // 󰟡\n\t\t\"nu\":               SHELL_CMD,       // \n\t\t\"o\":                BINARY,          // \n\t\t\"obj\":              FILE_3D,         // 󰆧\n\t\t\"odb\":              DATABASE,        // \n\t\t\"odf\":              0xf37b,          // \n\t\t\"odg\":              0xf379,          // \n\t\t\"odp\":              0xf37a,          // \n\t\t\"ods\":              0xf378,          // \n\t\t\"odt\":              0xf37c,          // \n\t\t\"ogg\":              AUDIO,           // \n\t\t\"ogm\":              VIDEO,           // \n\t\t\"ogv\":              VIDEO,           // \n\t\t\"opml\":             XML,             // 󰗀\n\t\t\"opus\":             AUDIO,           // \n\t\t\"orf\":              IMAGE,           // \n\t\t\"org\":              0xe633,          // \n\t\t\"otf\":              FONT,            // \n\t\t\"out\":              0xeb2c,          // \n\t\t\"p12\":              KEY,             // \n\t\t\"par\":              COMPRESSED,      // \n\t\t\"part\":             DOWNLOAD,        // 󰇚\n\t\t\"patch\":            DIFF,            // \n\t\t\"pbm\":              IMAGE,           // \n\t\t\"pcbdoc\":           EDA_PCB,         // \n\t\t\"pcm\":              AUDIO,           // \n\t\t\"pdf\":              0xf1c1,          // \n\t\t\"pem\":              KEY,             // \n\t\t\"pfx\":              KEY,             // \n\t\t\"pgm\":              IMAGE,           // \n\t\t\"phar\":             LANG_PHP,        // \n\t\t\"php\":              LANG_PHP,        // \n\t\t\"pkg\":              0xeb29,          // \n\t\t\"pl\":               LANG_PERL,       // \n\t\t\"plist\":            OS_APPLE,        // \n\t\t\"pls\":              PLAYLIST,        // 󰲹\n\t\t\"plx\":              LANG_PERL,       // \n\t\t\"ply\":              FILE_3D,         // 󰆧\n\t\t\"pm\":               LANG_PERL,       // \n\t\t\"png\":              IMAGE,           // \n\t\t\"pnm\":              IMAGE,           // \n\t\t\"po\":               TRANSLATION,     // 󰗊\n\t\t\"pod\":              LANG_PERL,       // \n\t\t\"pot\":              TRANSLATION,     // 󰗊\n\t\t\"pp\":               0xe631,          // \n\t\t\"ppm\":              IMAGE,           // \n\t\t\"pps\":              SLIDE,           // \n\t\t\"ppsx\":             SLIDE,           // \n\t\t\"ppt\":              SLIDE,           // \n\t\t\"pptx\":             SLIDE,           // \n\t\t\"prjpcb\":           EDA_PCB,         // \n\t\t\"procfile\":         LANG_RUBY,       // \n\t\t\"properties\":       JSON,            // \n\t\t\"prql\":             DATABASE,        // \n\t\t\"ps\":               VECTOR,          // 󰕙\n\t\t\"ps1\":              POWERSHELL,      // \n\t\t\"psb\":              0xe7b8,          // \n\t\t\"psd\":              0xe7b8,          // \n\t\t\"psd1\":             POWERSHELL,      // \n\t\t\"psf\":              FONT,            // \n\t\t\"psm\":              CAD,             // 󰻫\n\t\t\"psm1\":             POWERSHELL,      // \n\t\t\"pub\":              PUBLIC_KEY,      // 󰷖\n\t\t\"purs\":             0xe630,          // \n\t\t\"pxd\":              LANG_PYTHON,     // \n\t\t\"pxm\":              IMAGE,           // \n\t\t\"py\":               LANG_PYTHON,     // \n\t\t\"pyc\":              LANG_PYTHON,     // \n\t\t\"pyd\":              LANG_PYTHON,     // \n\t\t\"pyi\":              LANG_PYTHON,     // \n\t\t\"pyo\":              LANG_PYTHON,     // \n\t\t\"pyw\":              LANG_PYTHON,     // \n\t\t\"pyx\":              LANG_PYTHON,     // \n\t\t\"qcow\":             DISK_IMAGE,      // \n\t\t\"qcow2\":            DISK_IMAGE,      // \n\t\t\"qm\":               TRANSLATION,     // 󰗊\n\t\t\"qml\":              QT,              // \n\t\t\"qrc\":              QT,              // \n\t\t\"qss\":              QT,              // \n\t\t\"r\":                LANG_R,          // \n\t\t\"rake\":             LANG_RUBY,       // \n\t\t\"rakefile\":         LANG_RUBY,       // \n\t\t\"rar\":              COMPRESSED,      // \n\t\t\"raw\":              IMAGE,           // \n\t\t\"razor\":            RAZOR,           // \n\t\t\"rb\":               LANG_RUBY,       // \n\t\t\"rdata\":            LANG_R,          // \n\t\t\"rdb\":              0xe76d,          // \n\t\t\"rdoc\":             MARKDOWN,        // \n\t\t\"rds\":              LANG_R,          // \n\t\t\"readme\":           README,          // 󰂺\n\t\t\"rkt\":              LANG_SCHEME,     // \n\t\t\"rlib\":             LANG_RUST,       // \n\t\t\"rmd\":              MARKDOWN,        // \n\t\t\"rmeta\":            LANG_RUST,       // \n\t\t\"rpm\":              0xe7bb,          // \n\t\t\"rs\":               LANG_RUST,       // \n\t\t\"rspec\":            LANG_RUBY,       // \n\t\t\"rspec_parallel\":   LANG_RUBY,       // \n\t\t\"rspec_status\":     LANG_RUBY,       // \n\t\t\"rss\":              0xf09e,          // \n\t\t\"rst\":              TEXT,            // \n\t\t\"rtf\":              TEXT,            // \n\t\t\"ru\":               LANG_RUBY,       // \n\t\t\"rubydoc\":          LANG_RUBYRAILS,  // \n\t\t\"s\":                LANG_ASSEMBLY,   // \n\t\t\"s3db\":             SQLITE,          // \n\t\t\"sal\":              0xf147b,         // 󱑻\n\t\t\"sass\":             LANG_SASS,       // \n\t\t\"sbt\":              SUBTITLE,        // 󰨖\n\t\t\"scad\":             0xf34e,          // \n\t\t\"scala\":            0xe737,          // \n\t\t\"sch\":              EDA_SCH,         // 󰭅\n\t\t\"schdoc\":           EDA_SCH,         // 󰭅\n\t\t\"scm\":              LANG_SCHEME,     // \n\t\t\"scss\":             LANG_SASS,       // \n\t\t\"service\":          0xeba2,          // \n\t\t\"sf2\":              0xf0f70,         // 󰽰\n\t\t\"sfz\":              0xf0f70,         // 󰽰\n\t\t\"sh\":               SHELL_CMD,       // \n\t\t\"sha1\":             SHIELD_CHECK,    // 󰕥\n\t\t\"sha224\":           SHIELD_CHECK,    // 󰕥\n\t\t\"sha256\":           SHIELD_CHECK,    // 󰕥\n\t\t\"sha384\":           SHIELD_CHECK,    // 󰕥\n\t\t\"sha512\":           SHIELD_CHECK,    // 󰕥\n\t\t\"shell\":            SHELL_CMD,       // \n\t\t\"shtml\":            HTML5,           // \n\t\t\"sig\":              SIGNED_FILE,     // 󱧃\n\t\t\"signature\":        SIGNED_FILE,     // 󱧃\n\t\t\"skp\":              CAD,             // 󰻫\n\t\t\"sl3\":              SQLITE,          // \n\t\t\"sld\":              LANG_SCHEME,     // \n\t\t\"sldasm\":           CAD,             // 󰻫\n\t\t\"sldprt\":           CAD,             // 󰻫\n\t\t\"slim\":             LANG_RUBYRAILS,  // \n\t\t\"sln\":              0xe70c,          // \n\t\t\"slvs\":             CAD,             // 󰻫\n\t\t\"so\":               OS_LINUX,        // \n\t\t\"sql\":              DATABASE,        // \n\t\t\"sqlite\":           SQLITE,          // \n\t\t\"sqlite3\":          SQLITE,          // \n\t\t\"sr\":               0xf147b,         // 󱑻\n\t\t\"srt\":              SUBTITLE,        // 󰨖\n\t\t\"ss\":               LANG_SCHEME,     // \n\t\t\"ssa\":              SUBTITLE,        // 󰨖\n\t\t\"ste\":              CAD,             // 󰻫\n\t\t\"step\":             CAD,             // 󰻫\n\t\t\"stl\":              FILE_3D,         // 󰆧\n\t\t\"stp\":              CAD,             // 󰻫\n\t\t\"sty\":              LANG_TEX,        // \n\t\t\"styl\":             LANG_STYLUS,     // \n\t\t\"stylus\":           LANG_STYLUS,     // \n\t\t\"sub\":              SUBTITLE,        // 󰨖\n\t\t\"sublime-build\":    SUBLIME,         // \n\t\t\"sublime-keymap\":   SUBLIME,         // \n\t\t\"sublime-menu\":     SUBLIME,         // \n\t\t\"sublime-options\":  SUBLIME,         // \n\t\t\"sublime-package\":  SUBLIME,         // \n\t\t\"sublime-project\":  SUBLIME,         // \n\t\t\"sublime-session\":  SUBLIME,         // \n\t\t\"sublime-settings\": SUBLIME,         // \n\t\t\"sublime-snippet\":  SUBLIME,         // \n\t\t\"sublime-theme\":    SUBLIME,         // \n\t\t\"sv\":               LANG_HDL,        // 󰍛\n\t\t\"svelte\":           0xe697,          // \n\t\t\"svg\":              VECTOR,          // 󰕙\n\t\t\"svh\":              LANG_HDL,        // 󰍛\n\t\t\"swf\":              AUDIO,           // \n\t\t\"swift\":            0xe755,          // \n\t\t\"t\":                LANG_PERL,       // \n\t\t\"tape\":             0xF0A1B,         // 󰨛\n\t\t\"tar\":              COMPRESSED,      // \n\t\t\"taz\":              COMPRESSED,      // \n\t\t\"tbc\":              0xf06d3,         // 󰛓\n\t\t\"tbz\":              COMPRESSED,      // \n\t\t\"tbz2\":             COMPRESSED,      // \n\t\t\"tc\":               DISK_IMAGE,      // \n\t\t\"tcl\":              0xf06d3,         // 󰛓\n\t\t\"tex\":              LANG_TEX,        // \n\t\t\"tf\":               TERRAFORM,       // 󱁢\n\t\t\"tfstate\":          TERRAFORM,       // 󱁢\n\t\t\"tfvars\":           TERRAFORM,       // 󱁢\n\t\t\"tgz\":              COMPRESSED,      // \n\t\t\"tif\":              IMAGE,           // \n\t\t\"tiff\":             IMAGE,           // \n\t\t\"tlz\":              COMPRESSED,      // \n\t\t\"tml\":              CONFIG,          // \n\t\t\"tmux\":             TMUX,            // \n\t\t\"toml\":             TOML,            // \n\t\t\"torrent\":          0xe275,          // \n\t\t\"tres\":             GODOT,           // \n\t\t\"ts\":               LANG_TYPESCRIPT, // \n\t\t\"tscn\":             GODOT,           // \n\t\t\"tsv\":              SHEET,           // \n\t\t\"tsx\":              REACT,           // \n\t\t\"ttc\":              FONT,            // \n\t\t\"ttf\":              FONT,            // \n\t\t\"twig\":             0xe61c,          // \n\t\t\"txt\":              TEXT,            // \n\t\t\"txz\":              COMPRESSED,      // \n\t\t\"typ\":              TYPST,           // \n\t\t\"tz\":               COMPRESSED,      // \n\t\t\"tzo\":              COMPRESSED,      // \n\t\t\"ui\":               0xf2d0,          // \n\t\t\"unity\":            UNITY,           // \n\t\t\"unity3d\":          UNITY,           // \n\t\t\"v\":                LANG_V,          // \n\t\t\"vala\":             0xe8d1,          // \n\t\t\"vdi\":              DISK_IMAGE,      // \n\t\t\"vhd\":              DISK_IMAGE,      // \n\t\t\"vhdl\":             LANG_HDL,        // 󰍛\n\t\t\"vhs\":              0xF0A1B,         // 󰨛\n\t\t\"vi\":               0xe81e,          // \n\t\t\"video\":            VIDEO,           // \n\t\t\"vim\":              VIM,             // \n\t\t\"vmdk\":             DISK_IMAGE,      // \n\t\t\"vob\":              VIDEO,           // \n\t\t\"vsix\":             0xf0a1e,         // 󰨞\n\t\t\"vue\":              0xf0844,         // 󰡄\n\t\t\"war\":              LANG_JAVA,       // \n\t\t\"wav\":              AUDIO,           // \n\t\t\"webm\":             VIDEO,           // \n\t\t\"webmanifest\":      JSON,            // \n\t\t\"webp\":             IMAGE,           // \n\t\t\"whl\":              LANG_PYTHON,     // \n\t\t\"windows\":          OS_WINDOWS,      // \n\t\t\"wma\":              AUDIO,           // \n\t\t\"wmv\":              VIDEO,           // \n\t\t\"woff\":             FONT,            // \n\t\t\"woff2\":            FONT,            // \n\t\t\"wrl\":              FILE_3D,         // 󰆧\n\t\t\"wrz\":              FILE_3D,         // 󰆧\n\t\t\"wv\":               AUDIO,           // \n\t\t\"x_b\":              CAD,             // 󰻫\n\t\t\"x_t\":              CAD,             // 󰻫\n\t\t\"xaml\":             0xf0673,         // 󰙳\n\t\t\"xcf\":              GIMP,            // \n\t\t\"xci\":              0xF07E1,         // 󰟡\n\t\t\"xhtml\":            HTML5,           // \n\t\t\"xlr\":              SHEET,           // \n\t\t\"xls\":              SHEET,           // \n\t\t\"xlsm\":             SHEET,           // \n\t\t\"xlsx\":             SHEET,           // \n\t\t\"xml\":              XML,             // 󰗀\n\t\t\"xpi\":              0xeae6,          // \n\t\t\"xpm\":              IMAGE,           // \n\t\t\"xul\":              XML,             // 󰗀\n\t\t\"xz\":               COMPRESSED,      // \n\t\t\"yaml\":             YAML,            // \n\t\t\"yml\":              YAML,            // \n\t\t\"z\":                COMPRESSED,      // \n\t\t\"z64\":              0xf1393,         // 󱎓\n\t\t\"zig\":              0xe6a9,          // \n\t\t\"zip\":              COMPRESSED,      // \n\t\t\"zsh\":              SHELL_CMD,       // \n\t\t\"zsh-theme\":        SHELL,           // 󱆃\n\t\t\"zst\":              COMPRESSED,      // \n\t}\n}) // }}}\n\nfunc IconForPath(path string) string {\n\tbn := filepath.Base(path)\n\tif ans, found := FileNameMap()[bn]; found {\n\t\treturn string(ans)\n\t}\n\tif _, ext, found := strings.Cut(bn, \".\"); found {\n\t\tif ans, found := ExtensionMap()[strings.ToLower(ext)]; found {\n\t\t\treturn string(ans)\n\t\t}\n\t}\n\treturn string(FILE)\n}\n\nfunc IconForFileWithMode(path string, mode fs.FileMode, follow_symlinks bool) string {\n\tswitch mode & fs.ModeType {\n\tcase fs.ModeDir:\n\t\tbn := filepath.Base(path)\n\t\tif ans, found := DirectoryNameMap()[bn]; found {\n\t\t\treturn string(ans)\n\t\t}\n\t\treturn string(FOLDER)\n\tcase fs.ModeSymlink:\n\t\tif follow_symlinks {\n\t\t\tif dest, err := os.Readlink(path); err == nil {\n\t\t\t\tif st, err := os.Stat(dest); err == nil {\n\t\t\t\t\tif st.IsDir() {\n\t\t\t\t\t\treturn string(SYMLINK_TO_DIR)\n\t\t\t\t\t}\n\t\t\t\t\treturn IconForFileWithMode(dest, st.Mode(), follow_symlinks)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn string(SYMLINK)\n\tcase fs.ModeNamedPipe:\n\t\treturn string(NAMED_PIPE)\n\tcase fs.ModeSocket:\n\t\treturn string(SOCKET)\n\tdefault:\n\t\treturn IconForPath(path)\n\t}\n}\n"
  },
  {
    "path": "tools/ignorefiles/api.go",
    "content": "package ignorefiles\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"sync\"\n)\n\nvar _ = fmt.Print\n\ntype IgnoreFile interface {\n\tLen() int // number of rules\n\tLoadString(string) error\n\tLoadBytes([]byte) error\n\tLoadLines(...string) error\n\tLoadFile(io.Reader) error\n\tLoadPath(string) error\n\n\t// relpath is the path relative to the directory containing the ignorefile.\n\t// When the result is due to a rule matching, linenum_of_matching_rule is\n\t// >=0 and pattern is the textual representation of the rule. Otherwise\n\t// linenum_of_matching_rule is -1 and pattern is the empty string.\n\tIsIgnored(relpath string, ftype fs.FileMode) (is_ignored bool, linenum_of_matching_rule int, pattern string)\n}\n\nfunc NewGitignore() IgnoreFile { return &Gitignore{index_of_last_negated_rule: -1} }\n\n// The global gitignore from ~/.config/git/ignore\nvar GlobalGitignore = sync.OnceValue(func() IgnoreFile {\n\treturn get_global_gitignore()\n})\n"
  },
  {
    "path": "tools/ignorefiles/gitignore.go",
    "content": "package ignorefiles\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype GitPattern struct {\n\tline_number int\n\tonly_dirs   bool\n\tnegated     bool\n\tpattern     string\n\tparts       []string\n\tmatcher     func(path string) bool\n}\n\ntype Gitignore struct {\n\tpatterns                   []GitPattern\n\tindex_of_last_negated_rule int\n\tline_number_offset         int\n}\n\nfunc (g Gitignore) Len() int { return len(g.patterns) }\n\nfunc (g Gitignore) IsIgnored(relpath string, ftype os.FileMode) (is_ignored bool, linenum_of_matching_rule int, pattern string) {\n\tif os.PathSeparator != '/' {\n\t\trelpath = strings.ReplaceAll(relpath, string(os.PathSeparator), \"/\")\n\t}\n\tlinenum_of_matching_rule = -1\n\tfor i, pat := range g.patterns {\n\t\tif is_ignored {\n\t\t\tif i > g.index_of_last_negated_rule {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif pat.negated && pat.Match(relpath, ftype) {\n\t\t\t\tis_ignored = false\n\t\t\t\tlinenum_of_matching_rule = pat.line_number\n\t\t\t\tpattern = pat.pattern\n\t\t\t}\n\t\t} else {\n\t\t\tif !pat.negated && pat.Match(relpath, ftype) {\n\t\t\t\tis_ignored = true\n\t\t\t\tlinenum_of_matching_rule = pat.line_number\n\t\t\t\tpattern = pat.pattern\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (g *Gitignore) load_line(line string, line_number int) {\n\tif p, skipped_line := CompileGitIgnoreLine(line); !skipped_line {\n\t\tp.line_number = g.line_number_offset + line_number\n\t\tg.patterns = append(g.patterns, p)\n\t\tif p.negated {\n\t\t\tg.index_of_last_negated_rule = len(g.patterns) - 1\n\t\t}\n\t}\n}\n\nfunc (g *Gitignore) LoadLines(lines ...string) error {\n\tfor i, line := range lines {\n\t\tg.load_line(line, i)\n\t}\n\tg.line_number_offset += len(lines)\n\treturn nil\n}\n\nfunc (g *Gitignore) LoadString(text string) error {\n\ts := utils.NewLineScanner(text)\n\tlnum := 0\n\tfor s.Scan() {\n\t\tg.load_line(s.Text(), lnum)\n\t\tlnum++\n\t}\n\tg.line_number_offset += lnum\n\treturn nil\n}\n\nfunc (g *Gitignore) LoadBytes(text []byte) error {\n\treturn g.LoadString(string(text))\n}\n\nfunc (g *Gitignore) LoadPath(path string) error {\n\tif data, err := os.ReadFile(path); err == nil {\n\t\treturn g.LoadString(utils.UnsafeBytesToString(data))\n\t} else {\n\t\treturn err\n\t}\n}\n\nfunc (g *Gitignore) LoadFile(f io.Reader) error {\n\tif data, err := io.ReadAll(f); err == nil {\n\t\treturn g.LoadString(utils.UnsafeBytesToString(data))\n\t} else {\n\t\treturn err\n\t}\n}\n\nfunc (p GitPattern) Match(path string, ftype fs.FileMode) bool {\n\tif p.only_dirs && ftype&fs.ModeDir == 0 {\n\t\treturn false\n\t}\n\treturn p.matcher(path)\n}\n\nfunc anchored_single_match(path string, pattern string) bool {\n\tname, _, _ := strings.Cut(path, \"/\")\n\tmatches, err := filepath.Match(pattern, name)\n\treturn err == nil && matches\n}\n\nfunc unanchored_single_match(path string, pattern string) bool {\n\tfor path != \"\" {\n\t\tvar name string\n\t\tname, path, _ = strings.Cut(path, \"/\")\n\t\tmatches, err := filepath.Match(pattern, name)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tif matches {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc anchored_simple_match(path string, parts []string) bool {\n\tfor ; path != \"\" && len(parts) > 0; parts = parts[1:] {\n\t\tvar name string\n\t\tname, path, _ = strings.Cut(path, \"/\")\n\t\tif matches, err := filepath.Match(parts[0], name); err != nil || !matches {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn path == \"\" && len(parts) == 0\n}\n\nfunc anchored_full_match(path string, parts []string) bool {\n\tpos, last := 0, len(parts)-1\n\tfor pos <= last && path != \"\" {\n\t\tvar name string\n\t\tname, path, _ = strings.Cut(path, \"/\")\n\t\tswitch parts[pos] {\n\t\tcase \"**\":\n\t\t\tfor pos+1 < len(parts) && parts[pos+1] == \"**\" {\n\t\t\t\tpos++\n\t\t\t}\n\t\t\tif pos == last {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tpos++\n\t\t\tfor {\n\t\t\t\tmatches, err := filepath.Match(parts[pos], name)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif matches {\n\t\t\t\t\treturn anchored_full_match(path, parts[pos+1:])\n\t\t\t\t}\n\t\t\t\tif path == \"\" {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tname, path, _ = strings.Cut(path, \"/\")\n\t\t\t}\n\t\tdefault:\n\t\t\tif matches, err := filepath.Match(parts[pos], name); err != nil || !matches {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tpos++\n\t\t}\n\t}\n\treturn path == \"\" && pos > last\n}\n\n// Parse a line from a .gitignore file, see man gitignore for the syntax\nfunc CompileGitIgnoreLine(line string) (ans GitPattern, skipped_line bool) {\n\t// Strip comments\n\tif strings.HasPrefix(line, `#`) {\n\t\tskipped_line = true\n\t\treturn\n\t}\n\n\t// Trim OS-specific carriage returns.\n\tline = strings.TrimRight(line, \"\\r\")\n\n\t// Trim trailing spaces unless backslash escaped\n\tfor strings.HasSuffix(line, \" \") {\n\t\tif strings.HasSuffix(line, `\\ `) {\n\t\t\tline = line[:len(line)-2] + \" \"\n\t\t\tbreak\n\t\t}\n\t\tline = line[:len(line)-1]\n\t}\n\n\t// Empty lines are ignored\n\tif line == \"\" {\n\t\tskipped_line = true\n\t\treturn\n\t}\n\tans.pattern = line\n\n\t// Handle negated (accept) patterns\n\tif line[0] == '!' {\n\t\tline = line[1:]\n\t\tans.negated = true\n\t}\n\n\t// Handle leading slash used to escape leading # or !\n\tif line[0] == '\\\\' && len(line) > 1 && (line[1] == '#' || line[1] == '!') {\n\t\tline = line[1:]\n\t}\n\tif strings.HasSuffix(line, \"/\") {\n\t\tans.only_dirs = true\n\t\tline = strings.TrimRight(line, \"/\")\n\t\tif line == \"\" {\n\t\t\tskipped_line = true\n\t\t\treturn\n\t\t}\n\t}\n\tstarts_with_slash := strings.HasPrefix(line, \"/\")\n\tif starts_with_slash {\n\t\tline = strings.TrimLeft(line, \"/\")\n\t}\n\tans.parts = strings.Split(line, \"/\")\n\tif slices.Contains(ans.parts, \"\") {\n\t\tans.parts = slices.DeleteFunc(ans.parts, func(x string) bool { return x == \"\" })\n\t}\n\tif len(ans.parts) == 0 {\n\t\tskipped_line = true\n\t\treturn\n\t}\n\tif len(ans.parts) == 1 {\n\t\tpattern := ans.parts[0]\n\t\tif pattern == \"**\" {\n\t\t\tans.matcher = func(string) bool { return true }\n\t\t} else {\n\t\t\tif starts_with_slash {\n\t\t\t\tans.matcher = func(path string) bool { return anchored_single_match(path, pattern) }\n\t\t\t} else {\n\t\t\t\tans.matcher = func(path string) bool { return unanchored_single_match(path, pattern) }\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif slices.Contains(ans.parts, \"**\") {\n\t\t\tans.matcher = func(path string) bool { return anchored_full_match(path, ans.parts) }\n\t\t} else {\n\t\t\tans.matcher = func(path string) bool { return anchored_simple_match(path, ans.parts) }\n\t\t}\n\t}\n\treturn\n}\n\nfunc get_global_gitconfig_excludesfile() (ans string) {\n\treturn _get_global_gitconfig_excludesfile(utils.Expanduser(\"~/.gitconfig\"))\n}\n\nfunc _get_global_gitconfig_excludesfile(home_gitconfig string) (ans string) {\n\tcfhome := os.Getenv(\"XDG_CONFIG_HOME\")\n\tif cfhome == \"\" {\n\t\tcfhome = utils.Expanduser(\"~/.config\")\n\t}\n\tfor _, candidate := range []string{\"/etc/gitconfig\", filepath.Join(cfhome, \"git\", \"config\"), home_gitconfig} {\n\t\tif data, err := os.ReadFile(candidate); err == nil {\n\t\t\ts := utils.NewLineScanner(utils.UnsafeBytesToString(data))\n\t\t\tin_core := false\n\t\t\tfor s.Scan() {\n\t\t\t\tline := strings.TrimSpace(s.Text())\n\t\t\t\tif in_core {\n\t\t\t\t\tif strings.HasPrefix(line, \"[\") {\n\t\t\t\t\t\tin_core = line == \"[core]\"\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif k, rest, found := strings.Cut(line, \"=\"); found && strings.ToLower(strings.TrimSpace(k)) == `excludesfile` {\n\t\t\t\t\t\tans = strings.TrimSpace(rest)\n\t\t\t\t\t\tans = utils.Expanduser(ans)\n\t\t\t\t\t\tif !filepath.IsAbs(ans) {\n\t\t\t\t\t\t\tif a, err := filepath.Abs(ans); err != nil {\n\t\t\t\t\t\t\t\tans = a\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if strings.ToLower(line) == \"[core]\" {\n\t\t\t\t\tin_core = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif ans == \"\" {\n\t\tans = filepath.Join(cfhome, \"git\", \"ignore\")\n\t}\n\treturn\n}\n\nfunc get_global_gitignore() (ans IgnoreFile) {\n\texcludesfile := get_global_gitconfig_excludesfile()\n\tif data, err := os.ReadFile(excludesfile); err == nil {\n\t\tq := NewGitignore()\n\t\tif q.LoadString(utils.UnsafeBytesToString(data)) == nil {\n\t\t\tans = q\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tools/ignorefiles/gitignore_test.go",
    "content": "package ignorefiles\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc TestGitignore(t *testing.T) {\n\tfor line, expected := range map[string]struct {\n\t\tskipped, negated, only_dirs bool\n\t\tparts                       []string\n\t}{\n\t\t\"\":           {skipped: true},\n\t\t\" \":          {skipped: true},\n\t\t\"  \":         {skipped: true},\n\t\t\"/\":          {skipped: true},\n\t\t\"//\":         {skipped: true},\n\t\t\"# abc\":      {skipped: true},\n\t\t`\\!moose \\ `: {parts: []string{`!moose  `}},\n\t\t`\\#m\\oose  `: {parts: []string{`#m\\oose`}},\n\t} {\n\t\tp, skipped := CompileGitIgnoreLine(line)\n\t\tif skipped != expected.skipped {\n\t\t\tt.Fatalf(\"skipped: %v != %v for line: %s\", expected.skipped, skipped, line)\n\t\t}\n\t\tif !skipped {\n\t\t\tif p.negated != expected.negated {\n\t\t\t\tt.Fatalf(\"negated: %v != %v for line: %s\", expected.negated, p.negated, line)\n\t\t\t}\n\t\t\tif p.only_dirs != expected.only_dirs {\n\t\t\t\tt.Fatalf(\"only_dirs: %v != %v for line: %s\", expected.only_dirs, p.only_dirs, line)\n\t\t\t}\n\t\t\tif diff := cmp.Diff(expected.parts, p.parts); diff != \"\" {\n\t\t\t\tt.Fatalf(\"parts not equal for line: %s\\n%s\", line, diff)\n\t\t\t}\n\t\t}\n\t}\n\ttype ptest struct {\n\t\tpath     string\n\t\texpected bool\n\t}\n\tfor _, x := range []struct {\n\t\tline  string\n\t\ttests []ptest\n\t}{\n\t\t{\"foo\", []ptest{\n\t\t\t{\"foo\", true}, {\"x/foo\", true}, {\"foo/x\", true},\n\t\t}},\n\t\t{\"/foo\", []ptest{\n\t\t\t{\"foo\", true}, {\"x/foo\", false},\n\t\t}},\n\t\t{\"doc/frotz/\", []ptest{\n\t\t\t{\"doc/frotz/\", true}, {\"a/doc/frotz/\", false}, {\"doc/frotz\", false},\n\t\t}},\n\t\t{\"frotz/\", []ptest{\n\t\t\t{\"frotz/\", true}, {\"a/doc/frotz/\", true}, {\"doc/frotz\", false}, {\"frotz/\", true},\n\t\t}},\n\t\t{\"foo.*\", []ptest{\n\t\t\t{\"foo.txt\", true}, {\"foo\", false}, {\"a/foo.x\", true}, {\"foo.\", true},\n\t\t}},\n\n\t\t{\"**\", []ptest{\n\t\t\t{\"foo\", true}, {\"x/foo\", true}, {\"foo/x\", true},\n\t\t}},\n\n\t\t{\"**/foo\", []ptest{\n\t\t\t{\"foo\", true}, {\"x/foo\", true}, {\"foo/x\", false},\n\t\t}},\n\t\t{\"**/foo/bar\", []ptest{\n\t\t\t{\"foo\", false}, {\"x/foo\", false}, {\"foo/bar\", true}, {\"a/foo/bar\", true}, {\"foo/bar/a\", false},\n\t\t}},\n\t\t{\"foo/**\", []ptest{\n\t\t\t{\"foo\", false}, {\"x/foo\", false}, {\"foo/bar\", true}, {\"foo/bar/a\", true}, {\"foo/bar/a/\", true},\n\t\t}},\n\t\t{\"a/**/b\", []ptest{\n\t\t\t{\"a/b\", true}, {\"a/x/b\", true}, {\"a/x/y/b\", true}, {\"x/a/b\", false}, {\"a/b/x\", false},\n\t\t}},\n\t\t{\"a/**/b/**/c\", []ptest{\n\t\t\t{\"a/b/c\", true}, {\"a/x/b/c\", true}, {\"a/x/y/b/m/n/c\", true},\n\t\t}},\n\t} {\n\t\tp, skipped := CompileGitIgnoreLine(x.line)\n\t\tif skipped {\n\t\t\tt.Fatalf(\"Unexpectedly failed to compile: %#v\", x.line)\n\t\t}\n\t\tfor _, test := range x.tests {\n\t\t\tpath := strings.TrimRight(test.path, \"/\")\n\t\t\tftype := utils.IfElse(len(path) < len(test.path), fs.ModeDir, 0)\n\t\t\tif actual := p.Match(path, ftype); actual != test.expected {\n\t\t\t\tt.Fatalf(\"matched: %v != %v for pattern: %#v and path: %#v\", test.expected, actual, x.line, test.path)\n\t\t\t}\n\t\t}\n\t}\n\tfor text, tests := range map[string]map[string]bool{\n\t\t``: {\"foo\": false},\n\t\t`\n# exclude everything except directory foo/bar\n/*\n!/foo\n/foo/*\n!/foo/bar`: {\n\t\t\t\"a\": true, \"foo\": false, \"foo/x\": true, \"foo/bar\": false, \"foo/bar/\": false,\n\t\t},\n\t\t`\n**/foo\nbar `: {\n\t\t\t\"foo\": true, \"baz/foo\": true, \"bar\": true, \"baz/bar\": true, \"a\": false,\n\t\t},\n\t\t`/*.c`: {\"a.c\": true, \"b/a.c\": false},\n\t\t`\n**/external/**/*.json\n**/external/**/.*ignore\n**/external/foobar/*.css`: {\n\t\t\t\"external/foobar/angular.foo.css\": true, \"external/barfoo/.gitignore\": true, \"external/barfoo/.bower.json\": true,\n\t\t},\n\t\t\"abc/def\\r\\nxyz\": {\"abc/def\": true, \"a/xyz\": true},\n\t\t`/**/foo`:        {\"foo\": true, \"foo/\": true, \"a/b/foo\": true, \"fooo\": false, \"ofoo\": false},\n\t\t\"/.js\":           {\".js\": true, \".js/\": true, \".js/a\": true, \".jsa\": false},\n\t\t\"*.js\":           {\".js\": true, \".js/\": true, \".js/a\": true, \"a.js/a\": true, \"a.js/a.js\": true, \".jsa\": false, \"a.jsa\": false},\n\t\t\"foo/**/\":        {\"foo/\": false, \"foo\": false, \"foo/abc/\": true, \"foo/a/b/c/\": true, \"foo/a\": false},\n\t\t\"foo/**/*.bar\":   {\"foo/\": false, \"abc.bar\": false, \"foo/abc.bar\": true, \"foo/a.bar/\": true, \"foo/x/y/z.bar\": true},\n\t\t`\\#abc`:          {\"abc\": false, \"#abc\": true},\n\t\t\"abc\\n!abc/x\":    {\"abc\": true, \"abc/x\": false, \"abc/y\": true},\n\t\t`abc/*`:          {\"abc\": false, \"abc/\": false, \"abc/x\": true},\n\t} {\n\t\tp := NewGitignore()\n\t\tif err := p.LoadString(text); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tfor tpath, expected := range tests {\n\t\t\tpath := strings.TrimRight(tpath, \"/\")\n\t\t\tftype := utils.IfElse(len(path) < len(tpath), fs.ModeDir, 0)\n\t\t\tif actual, _, _ := p.IsIgnored(path, ftype); actual != expected {\n\t\t\t\tt.Fatalf(\"ignored: %v != %v for path: %#v and ignorefile:\\n%s\", expected, actual, tpath, text)\n\t\t\t}\n\t\t}\n\t}\n\ttdir := t.TempDir()\n\tgc := filepath.Join(tdir, \".gitconfig\")\n\tos.WriteFile(gc, []byte(`\n[core]\nsomething\n[else]\n...\n[core]\n...\n[core]\nexcludesfile = one\n[core]\nexcludesfile = ~/global-gitignore\n`), 0600)\n\tif ef := _get_global_gitconfig_excludesfile(gc); ef != utils.Expanduser(\"~/global-gitignore\") {\n\t\tt.Fatalf(\"global gitconfig excludes file incorrect: %s != %s\", utils.Expanduser(\"~/global-gitignore\"), ef)\n\t}\n}\n"
  },
  {
    "path": "tools/rsync/algorithm.go",
    "content": "// Algorithm found at: https://rsync.samba.org/tech_report/tech_report.html\n// Code in this file is inspired by: https://github.com/jbreiding/rsync-go\n//\n// Definitions\n//\n//\tSource: The final content.\n//\tTarget: The content to be made into final content.\n//\tSignature: The sequence of hashes used to identify the content.\npackage rsync\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"hash\"\n\t\"io\"\n\t\"slices\"\n\t\"strconv\"\n\n\t\"github.com/zeebo/xxh3\"\n)\n\n// If no BlockSize is specified in the rsync instance, this value is used.\nconst DefaultBlockSize = 1024 * 6\n\n// Internal constant used in rolling checksum.\nconst _M = 1 << 16\n\n// Operation Types.\ntype OpType byte // enum\n\nconst (\n\tOpBlock OpType = iota\n\tOpData\n\tOpHash\n\tOpBlockRange\n)\n\ntype xxh3_128 struct {\n\txxh3.Hasher\n}\n\nfunc (self *xxh3_128) Sum(b []byte) []byte {\n\ts := self.Sum128()\n\tpos := len(b)\n\tlimit := pos + 16\n\tif limit > cap(b) {\n\t\tvar x [16]byte\n\t\tb = append(b, x[:]...)\n\t} else {\n\t\tb = b[:limit]\n\t}\n\tbinary.BigEndian.PutUint64(b[pos:], s.Hi)\n\tbinary.BigEndian.PutUint64(b[pos+8:], s.Lo)\n\treturn b\n}\n\nfunc new_xxh3_64() hash.Hash64 {\n\tans := xxh3.New()\n\tans.Reset()\n\treturn ans\n}\n\nfunc new_xxh3_128() hash.Hash {\n\tans := new(xxh3_128)\n\tans.Reset()\n\treturn ans\n}\n\n// Instruction to mutate target to align to source.\ntype Operation struct {\n\tType          OpType\n\tBlockIndex    uint64\n\tBlockIndexEnd uint64\n\tData          []byte\n}\n\nfunc (self Operation) String() string {\n\tans := \"{\" + self.Type.String() + \" \"\n\tswitch self.Type {\n\tcase OpBlock:\n\t\tans += strconv.FormatUint(self.BlockIndex, 10)\n\tcase OpBlockRange:\n\t\tans += strconv.FormatUint(self.BlockIndex, 10) + \" to \" + strconv.FormatUint(self.BlockIndexEnd, 10)\n\tcase OpData:\n\t\tans += strconv.Itoa(len(self.Data))\n\tcase OpHash:\n\t\tans += hex.EncodeToString(self.Data)\n\t}\n\treturn ans + \"}\"\n}\n\nvar bin = binary.LittleEndian\n\nfunc (self Operation) SerializeSize() int {\n\tswitch self.Type {\n\tcase OpBlock:\n\t\treturn 9\n\tcase OpBlockRange:\n\t\treturn 13\n\tcase OpHash:\n\t\treturn 3 + len(self.Data)\n\tcase OpData:\n\t\treturn 5 + len(self.Data)\n\t}\n\treturn -1\n}\n\nfunc (self Operation) Serialize(ans []byte) {\n\tswitch self.Type {\n\tcase OpBlock:\n\t\tbin.PutUint64(ans[1:], self.BlockIndex)\n\tcase OpBlockRange:\n\t\tbin.PutUint64(ans[1:], self.BlockIndex)\n\t\tbin.PutUint32(ans[9:], uint32(self.BlockIndexEnd-self.BlockIndex))\n\tcase OpHash:\n\t\tbin.PutUint16(ans[1:], uint16(len(self.Data)))\n\t\tcopy(ans[3:], self.Data)\n\tcase OpData:\n\t\tbin.PutUint32(ans[1:], uint32(len(self.Data)))\n\t\tcopy(ans[5:], self.Data)\n\t}\n\tans[0] = byte(self.Type)\n}\n\nfunc (self *Operation) Unserialize(data []byte) (n int, err error) {\n\tif len(data) < 1 {\n\t\treturn -1, io.ErrShortBuffer\n\t}\n\tswitch OpType(data[0]) {\n\tcase OpBlock:\n\t\tn = 9\n\t\tif len(data) < n {\n\t\t\treturn -1, io.ErrShortBuffer\n\t\t}\n\t\tself.BlockIndex = bin.Uint64(data[1:])\n\t\tself.Data = nil\n\tcase OpBlockRange:\n\t\tn = 13\n\t\tif len(data) < n {\n\t\t\treturn -1, io.ErrShortBuffer\n\t\t}\n\t\tself.BlockIndex = bin.Uint64(data[1:])\n\t\tself.BlockIndexEnd = self.BlockIndex + uint64(bin.Uint32(data[9:]))\n\t\tself.Data = nil\n\tcase OpHash:\n\t\tn = 3\n\t\tif len(data) < n {\n\t\t\treturn -1, io.ErrShortBuffer\n\t\t}\n\t\tsz := int(bin.Uint16(data[1:]))\n\t\tn += sz\n\t\tif len(data) < n {\n\t\t\treturn -1, io.ErrShortBuffer\n\t\t}\n\t\tself.Data = data[3:n]\n\tcase OpData:\n\t\tn = 5\n\t\tif len(data) < n {\n\t\t\treturn -1, io.ErrShortBuffer\n\t\t}\n\t\tsz := int(bin.Uint32(data[1:]))\n\t\tn += sz\n\t\tif len(data) < n {\n\t\t\treturn -1, io.ErrShortBuffer\n\t\t}\n\t\tself.Data = data[5:n]\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"record has unknown operation type: %d\", data[0])\n\t}\n\tself.Type = OpType(data[0])\n\treturn\n}\n\n// Signature hash item generated from target.\ntype BlockHash struct {\n\tIndex      uint64\n\tWeakHash   uint32\n\tStrongHash uint64\n}\n\nconst BlockHashSize = 20\n\n// Put the serialization of this BlockHash to output\nfunc (self BlockHash) Serialize(output []byte) {\n\tbin.PutUint64(output, self.Index)\n\tbin.PutUint32(output[8:], self.WeakHash)\n\tbin.PutUint64(output[12:], self.StrongHash)\n}\n\nfunc (self *BlockHash) Unserialize(data []byte) (err error) {\n\tif len(data) < 20 {\n\t\treturn fmt.Errorf(\"record too small to be a BlockHash: %d < %d\", len(data), 20)\n\t}\n\tself.Index = bin.Uint64(data)\n\tself.WeakHash = bin.Uint32(data[8:])\n\tself.StrongHash = bin.Uint64(data[12:])\n\treturn\n}\n\n// Properties to use while working with the rsync algorithm.\n// A single rsync should not be used concurrently as it may contain\n// internal buffers and hash sums.\ntype rsync struct {\n\tBlockSize int\n\n\t// This must be non-nil before using any functions\n\thasher                  hash.Hash64\n\thasher_constructor      func() hash.Hash64\n\tchecksummer_constructor func() hash.Hash\n\tchecksummer             hash.Hash\n\tchecksum_done           bool\n\tbuffer                  []byte\n}\n\nfunc (r *rsync) SetHasher(c func() hash.Hash64) {\n\tr.hasher_constructor = c\n\tr.hasher = c()\n}\n\nfunc (r *rsync) SetChecksummer(c func() hash.Hash) {\n\tr.checksummer_constructor = c\n\tr.checksummer = c()\n}\n\n// If the target length is known the number of hashes in the\n// signature can be determined.\nfunc (r *rsync) BlockHashCount(targetLength int64) (count int64) {\n\tbs := int64(r.BlockSize)\n\tcount = targetLength / bs\n\tif targetLength%bs != 0 {\n\t\tcount++\n\t}\n\treturn\n}\n\ntype signature_iterator struct {\n\thasher hash.Hash64\n\tbuffer []byte\n\tsrc    io.Reader\n\trc     rolling_checksum\n\tindex  uint64\n}\n\n// ans is valid iff err == nil\nfunc (self *signature_iterator) next() (ans BlockHash, err error) {\n\tn, err := io.ReadAtLeast(self.src, self.buffer, cap(self.buffer))\n\tswitch err {\n\tcase io.ErrUnexpectedEOF, io.EOF, nil:\n\t\terr = nil\n\tdefault:\n\t\treturn\n\t}\n\tif n == 0 {\n\t\treturn ans, io.EOF\n\t}\n\tb := self.buffer[:n]\n\tself.hasher.Reset()\n\tself.hasher.Write(b)\n\tans = BlockHash{Index: self.index, WeakHash: self.rc.full(b), StrongHash: self.hasher.Sum64()}\n\tself.index++\n\treturn\n\n}\n\n// Calculate the signature of target.\nfunc (r *rsync) CreateSignatureIterator(target io.Reader) func() (BlockHash, error) {\n\treturn (&signature_iterator{\n\t\thasher: r.hasher_constructor(), buffer: make([]byte, r.BlockSize), src: target,\n\t}).next\n}\n\n// Apply the difference to the target.\nfunc (r *rsync) ApplyDelta(output io.Writer, target io.ReadSeeker, op Operation) error {\n\tvar err error\n\tvar n int\n\tvar block []byte\n\n\tr.set_buffer_to_size(r.BlockSize)\n\tbuffer := r.buffer\n\tif r.checksummer == nil {\n\t\tr.checksummer = r.checksummer_constructor()\n\t}\n\n\twrite := func(b []byte) (err error) {\n\t\tif _, err = r.checksummer.Write(b); err == nil {\n\t\t\t_, err = output.Write(b)\n\t\t}\n\t\treturn err\n\t}\n\twrite_block := func(op Operation) (err error) {\n\t\tif _, err = target.Seek(int64(r.BlockSize*int(op.BlockIndex)), io.SeekStart); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif n, err = io.ReadAtLeast(target, buffer, r.BlockSize); err != nil && err != io.ErrUnexpectedEOF {\n\t\t\treturn err\n\t\t}\n\t\tblock = buffer[:n]\n\t\treturn write(block)\n\t}\n\n\tswitch op.Type {\n\tcase OpBlockRange:\n\t\tfor i := op.BlockIndex; i <= op.BlockIndexEnd; i++ {\n\t\t\tif err = write_block(Operation{\n\t\t\t\tType:       OpBlock,\n\t\t\t\tBlockIndex: i,\n\t\t\t}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tcase OpBlock:\n\t\treturn write_block(op)\n\tcase OpData:\n\t\treturn write(op.Data)\n\tcase OpHash:\n\t\tactual := r.checksummer.Sum(nil)\n\t\tif !bytes.Equal(actual, op.Data) {\n\t\t\treturn fmt.Errorf(\"Failed to verify overall file checksum actual: %s != expected: %s. This usually happens if some data was corrupted in transit or one of the involved files was altered while the transfer was in progress.\", hex.EncodeToString(actual), hex.EncodeToString(op.Data))\n\t\t}\n\t\tr.checksum_done = true\n\t}\n\treturn nil\n}\n\nfunc (r *rsync) set_buffer_to_size(sz int) {\n\tif cap(r.buffer) < sz {\n\t\tr.buffer = make([]byte, sz)\n\t} else {\n\t\tr.buffer = r.buffer[:sz]\n\t}\n}\n\n// see https://rsync.samba.org/tech_report/node3.html\ntype rolling_checksum struct {\n\talpha, beta, val, l           uint32\n\tfirst_byte_of_previous_window uint32\n}\n\nfunc (self *rolling_checksum) full(data []byte) uint32 {\n\tvar alpha, beta uint32\n\tself.l = uint32(len(data)) // actually should be len(data) - 1 but the equations always use l+1\n\tfor i, b := range data {\n\t\talpha += uint32(b)\n\t\tbeta += (self.l - uint32(i)) * uint32(b)\n\t}\n\tself.first_byte_of_previous_window = uint32(data[0])\n\tself.alpha = alpha % _M\n\tself.beta = beta % _M\n\tself.val = self.alpha + _M*self.beta\n\treturn self.val\n}\n\nfunc (self *rolling_checksum) add_one_byte(first_byte, last_byte byte) {\n\tself.alpha = (self.alpha - self.first_byte_of_previous_window + uint32(last_byte)) % _M\n\tself.beta = (self.beta - (self.l)*self.first_byte_of_previous_window + self.alpha) % _M\n\tself.val = self.alpha + _M*self.beta\n\tself.first_byte_of_previous_window = uint32(first_byte)\n}\n\ntype diff struct {\n\tbuffer       []byte\n\top_write_buf [32]byte\n\t// A single β hash may correlate with many unique hashes.\n\thash_lookup map[uint32][]BlockHash\n\tsource      io.Reader\n\thasher      hash.Hash64\n\tchecksummer hash.Hash\n\toutput      io.Writer\n\n\twindow, data      struct{ pos, sz int }\n\tblock_size        int\n\tfinished, written bool\n\trc                rolling_checksum\n\n\tpending_op *Operation\n}\n\nfunc (self *diff) Next() (err error) {\n\treturn self.pump_till_op_written()\n}\n\nfunc (self *diff) hash(b []byte) uint64 {\n\tself.hasher.Reset()\n\tself.hasher.Write(b)\n\treturn self.hasher.Sum64()\n}\n\n// Combine OpBlock into OpBlockRange. To do this store the previous\n// non-data operation and determine if it can be extended.\nfunc (self *diff) send_pending() (err error) {\n\tif self.pending_op != nil {\n\t\terr = self.send_op(self.pending_op)\n\t\tself.pending_op = nil\n\t}\n\treturn\n}\n\nfunc (self *diff) enqueue(op Operation) (err error) {\n\tswitch op.Type {\n\tcase OpBlock:\n\t\tif self.pending_op != nil {\n\t\t\tswitch self.pending_op.Type {\n\t\t\tcase OpBlock:\n\t\t\t\tif self.pending_op.BlockIndex+1 == op.BlockIndex {\n\t\t\t\t\tself.pending_op = &Operation{\n\t\t\t\t\t\tType:          OpBlockRange,\n\t\t\t\t\t\tBlockIndex:    self.pending_op.BlockIndex,\n\t\t\t\t\t\tBlockIndexEnd: op.BlockIndex,\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase OpBlockRange:\n\t\t\t\tif self.pending_op.BlockIndexEnd+1 == op.BlockIndex {\n\t\t\t\t\tself.pending_op.BlockIndexEnd = op.BlockIndex\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err = self.send_pending(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tself.pending_op = &op\n\tcase OpHash:\n\t\tif err = self.send_pending(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = self.send_op(&op); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n\n}\n\nfunc (self *diff) send_op(op *Operation) error {\n\tb := self.op_write_buf[:op.SerializeSize()]\n\top.Serialize(b)\n\tself.written = true\n\t_, err := self.output.Write(b)\n\treturn err\n}\n\nfunc (self *diff) send_data() error {\n\tif self.data.sz > 0 {\n\t\tif err := self.send_pending(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tself.written = true\n\t\tdata := self.buffer[self.data.pos : self.data.pos+self.data.sz]\n\t\tvar buf [5]byte\n\t\tbin.PutUint32(buf[1:], uint32(len(data)))\n\t\tbuf[0] = byte(OpData)\n\t\tif _, err := self.output.Write(buf[:]); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif _, err := self.output.Write(data); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tself.data.pos += self.data.sz\n\t\tself.data.sz = 0\n\t}\n\treturn nil\n}\n\nfunc (self *diff) pump_till_op_written() error {\n\tself.written = false\n\tfor !self.finished && !self.written {\n\t\tif err := self.read_next(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif self.finished {\n\t\tif err := self.send_pending(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn io.EOF\n\t}\n\treturn nil\n}\n\nfunc (self *diff) ensure_idx_valid(idx int) (ok bool, err error) {\n\tif idx < len(self.buffer) {\n\t\treturn true, nil\n\t}\n\tif idx >= cap(self.buffer) {\n\t\t// need to wrap the buffer, so send off any data present behind the window\n\t\tif err = self.send_data(); err != nil {\n\t\t\treturn\n\t\t}\n\t\t// copy the window and any data present after it to the start of the buffer\n\t\tdistance_from_window_pos := idx - self.window.pos\n\t\tamt_to_copy := len(self.buffer) - self.window.pos\n\t\tcopy(self.buffer, self.buffer[self.window.pos:self.window.pos+amt_to_copy])\n\t\tself.buffer = self.buffer[:amt_to_copy]\n\t\tself.window.pos = 0\n\t\tself.data.pos = 0\n\t\treturn self.ensure_idx_valid(distance_from_window_pos)\n\t}\n\textra := idx - len(self.buffer) + 1\n\tvar n int\n\tn, err = io.ReadAtLeast(self.source, self.buffer[len(self.buffer):cap(self.buffer)], extra)\n\tblock := self.buffer[len(self.buffer):][:n]\n\tswitch err {\n\tcase nil:\n\t\tok = true\n\t\tself.buffer = self.buffer[:len(self.buffer)+n]\n\t\tself.checksummer.Write(block)\n\tcase io.ErrUnexpectedEOF, io.EOF:\n\t\terr = nil\n\t\tself.buffer = self.buffer[:len(self.buffer)+n]\n\t\tself.checksummer.Write(block)\n\t}\n\treturn\n}\n\nfunc (self *diff) finish_up() (err error) {\n\tif err = self.send_data(); err != nil {\n\t\treturn\n\t}\n\tself.data.pos = self.window.pos\n\tself.data.sz = len(self.buffer) - self.window.pos\n\tif err = self.send_data(); err != nil {\n\t\treturn\n\t}\n\tself.enqueue(Operation{Type: OpHash, Data: self.checksummer.Sum(nil)})\n\tself.finished = true\n\treturn\n}\n\n// See https://rsync.samba.org/tech_report/node4.html for the design of this algorithm\nfunc (self *diff) read_next() (err error) {\n\tif self.window.sz > 0 {\n\t\tif ok, err := self.ensure_idx_valid(self.window.pos + self.window.sz); !ok {\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn self.finish_up()\n\t\t}\n\t\tself.window.pos++\n\t\tself.data.sz++\n\t\tself.rc.add_one_byte(self.buffer[self.window.pos], self.buffer[self.window.pos+self.window.sz-1])\n\t} else {\n\t\tif ok, err := self.ensure_idx_valid(self.window.pos + self.block_size - 1); !ok {\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn self.finish_up()\n\t\t}\n\t\tself.window.sz = self.block_size\n\t\tself.rc.full(self.buffer[self.window.pos : self.window.pos+self.window.sz])\n\t}\n\tfound_hash := false\n\tvar block_index uint64\n\tif hh, ok := self.hash_lookup[self.rc.val]; ok {\n\t\tblock_index, found_hash = find_hash(hh, self.hash(self.buffer[self.window.pos:self.window.pos+self.window.sz]))\n\t}\n\tif found_hash {\n\t\tif err = self.send_data(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tself.enqueue(Operation{Type: OpBlock, BlockIndex: block_index})\n\t\tself.window.pos += self.window.sz\n\t\tself.data.pos = self.window.pos\n\t\tself.window.sz = 0\n\t}\n\treturn nil\n}\n\ntype OperationWriter struct {\n\tOperations     []Operation\n\texpecting_data bool\n}\n\nfunc (self *OperationWriter) Write(p []byte) (n int, err error) {\n\tif self.expecting_data {\n\t\tself.expecting_data = false\n\t\tself.Operations = append(self.Operations, Operation{Type: OpData, Data: slices.Clone(p)})\n\t} else {\n\t\tswitch OpType(p[0]) {\n\t\tcase OpData:\n\t\t\tself.expecting_data = true\n\t\tcase OpBlock, OpBlockRange, OpHash:\n\t\t\top := Operation{}\n\t\t\tif n, err = op.Unserialize(p); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t} else if n < len(p) {\n\t\t\t\treturn 0, io.ErrShortWrite\n\t\t\t}\n\t\t\tself.Operations = append(self.Operations, op)\n\t\tdefault:\n\t\t\treturn 0, fmt.Errorf(\"Unknown OpType: %d\", p[0])\n\t\t}\n\t}\n\treturn\n}\n\nfunc (r *rsync) CreateDelta(source io.Reader, signature []BlockHash) ([]Operation, error) {\n\tw := OperationWriter{}\n\tit := r.CreateDiff(source, signature, &w)\n\tfor {\n\t\tif err := it(); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\treturn w.Operations, nil\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\t}\n}\n\nconst DataSizeMultiple int = 8\n\nfunc (r *rsync) CreateDiff(source io.Reader, signature []BlockHash, output io.Writer) func() error {\n\tans := &diff{\n\t\tblock_size: r.BlockSize, buffer: make([]byte, 0, (r.BlockSize * DataSizeMultiple)),\n\t\thash_lookup: make(map[uint32][]BlockHash, len(signature)),\n\t\tsource:      source, hasher: r.hasher_constructor(),\n\t\tchecksummer: r.checksummer_constructor(), output: output,\n\t}\n\tfor _, h := range signature {\n\t\tkey := h.WeakHash\n\t\tans.hash_lookup[key] = append(ans.hash_lookup[key], h)\n\t}\n\n\treturn ans.Next\n}\n\n// Use a more unique way to identify a set of bytes.\nfunc (r *rsync) hash(v []byte) uint64 {\n\tr.hasher.Reset()\n\tr.hasher.Write(v)\n\treturn r.hasher.Sum64()\n}\n\nfunc (r *rsync) HashSize() int      { return r.hasher.Size() }\nfunc (r *rsync) HashBlockSize() int { return r.hasher.BlockSize() }\nfunc (r *rsync) HasHasher() bool    { return r.hasher != nil }\n\n// Searches for a given strong hash among all strong hashes in this bucket.\nfunc find_hash(hh []BlockHash, hv uint64) (uint64, bool) {\n\tfor _, block := range hh {\n\t\tif block.StrongHash == hv {\n\t\t\treturn block.Index, true\n\t\t}\n\t}\n\treturn 0, false\n}\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n"
  },
  {
    "path": "tools/rsync/api.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\n// First create a patcher with:\n// p = NewPatcher()\n// Create a signature for the file you want to update using\n// p.CreateSignatureIterator(file_to_update)\n// Now create a Differ with the created signature\n// d = NewDiffer()\n// d.AddSignatureData(signature_data_from_previous_step)\n// Now create a delta based on the signature and the reference file\n// d.CreateDelta(reference_file)\n// Finally, apply this delta using the patcher to produce a file identical to reference_file\n// based ont he delta data and file_to_update\n// p.StartDelta(output_file, file_to_update)\n// p.UpdateDelta(...)\n// p.FinishDelta()\npackage rsync\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nconst MaxBlockSize int = 1024 * 1024 // sqrt of 1TB\n\ntype StrongHashType uint16\ntype WeakHashType uint16\ntype ChecksumType uint16\n\nconst (\n\tXXH3 StrongHashType = iota\n)\nconst (\n\tXXH3128Sum ChecksumType = iota\n)\nconst (\n\tRsync WeakHashType = iota\n)\n\ntype GrowBufferFunction = func(slice []byte, sz int) []byte\n\ntype Api struct {\n\trsync     rsync\n\tsignature []BlockHash\n\n\tChecksum_type    ChecksumType\n\tStrong_hash_type StrongHashType\n\tWeak_hash_type   WeakHashType\n}\n\ntype Differ struct {\n\tApi\n\tunconsumed_signature_data []byte\n}\n\ntype Patcher struct {\n\tApi\n\tunconsumed_delta_data                        []byte\n\texpected_input_size_for_signature_generation int64\n\tdelta_output                                 io.Writer\n\tdelta_input                                  io.ReadSeeker\n\ttotal_data_in_delta                          int\n}\n\n// internal implementation {{{\nfunc (self *Api) read_signature_header(data []byte) (consumed int, err error) {\n\tif len(data) < 12 {\n\t\treturn -1, io.ErrShortBuffer\n\t}\n\tif version := bin.Uint16(data); version != 0 {\n\t\treturn consumed, fmt.Errorf(\"Invalid version in signature header: %d\", version)\n\t}\n\tswitch csum := ChecksumType(bin.Uint16(data[2:])); csum {\n\tcase XXH3128Sum:\n\t\tself.Checksum_type = XXH3128Sum\n\t\tself.rsync.SetChecksummer(new_xxh3_128)\n\tdefault:\n\t\treturn consumed, fmt.Errorf(\"Invalid checksum_type in signature header: %d\", csum)\n\t}\n\tswitch strong_hash := StrongHashType(bin.Uint16(data[4:])); strong_hash {\n\tcase XXH3:\n\t\tself.Strong_hash_type = strong_hash\n\t\tself.rsync.SetHasher(new_xxh3_64)\n\tdefault:\n\t\treturn consumed, fmt.Errorf(\"Invalid strong_hash in signature header: %d\", strong_hash)\n\t}\n\tswitch weak_hash := WeakHashType(bin.Uint16(data[6:])); weak_hash {\n\tcase Rsync:\n\t\tself.Weak_hash_type = weak_hash\n\tdefault:\n\t\treturn consumed, fmt.Errorf(\"Invalid weak_hash in signature header: %d\", weak_hash)\n\t}\n\tblock_size := int(bin.Uint32(data[8:]))\n\tconsumed = 12\n\tif block_size == 0 {\n\t\treturn consumed, fmt.Errorf(\"rsync signature header has zero block size\")\n\t}\n\tif block_size > MaxBlockSize {\n\t\treturn consumed, fmt.Errorf(\"rsync signature header has too large block size %d > %d\", block_size, MaxBlockSize)\n\t}\n\tself.rsync.BlockSize = block_size\n\tself.signature = make([]BlockHash, 0, 1024)\n\treturn\n}\n\nfunc (self *Api) read_signature_blocks(data []byte) (consumed int) {\n\tblock_hash_size := self.rsync.HashSize() + 12\n\tfor ; len(data) >= block_hash_size; data = data[block_hash_size:] {\n\t\tbl := BlockHash{}\n\t\tbl.Unserialize(data[:block_hash_size])\n\t\tself.signature = append(self.signature, bl)\n\t\tconsumed += block_hash_size\n\t}\n\treturn\n}\n\nfunc (self *Differ) FinishSignatureData() (err error) {\n\tif len(self.unconsumed_signature_data) > 0 {\n\t\treturn fmt.Errorf(\"There were %d leftover bytes in the signature data\", len(self.unconsumed_signature_data))\n\t}\n\tself.unconsumed_signature_data = nil\n\tif !self.rsync.HasHasher() {\n\t\treturn fmt.Errorf(\"No header was found in the signature data\")\n\t}\n\treturn\n}\n\nfunc (self *Patcher) update_delta(data []byte) (consumed int, err error) {\n\top := Operation{}\n\tfor len(data) > 0 {\n\t\tn, uerr := op.Unserialize(data)\n\t\tif uerr == nil {\n\t\t\tconsumed += n\n\t\t\tdata = data[n:]\n\t\t\tif err = self.rsync.ApplyDelta(self.delta_output, self.delta_input, op); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif op.Type == OpData {\n\t\t\t\tself.total_data_in_delta += len(op.Data)\n\t\t\t}\n\t\t} else {\n\t\t\tif n < 0 {\n\t\t\t\treturn consumed, nil\n\t\t\t}\n\t\t\treturn consumed, uerr\n\t\t}\n\t}\n\treturn\n}\n\n// }}}\n\n// Start applying serialized delta\nfunc (self *Patcher) StartDelta(delta_output io.Writer, delta_input io.ReadSeeker) {\n\tself.delta_output = delta_output\n\tself.delta_input = delta_input\n\tself.total_data_in_delta = 0\n\tself.unconsumed_delta_data = nil\n}\n\n// Apply a chunk of delta data\nfunc (self *Patcher) UpdateDelta(data []byte) (err error) {\n\tself.unconsumed_delta_data = append(self.unconsumed_delta_data, data...)\n\tconsumed, err := self.update_delta(self.unconsumed_delta_data)\n\tif err != nil {\n\t\treturn err\n\t}\n\tself.unconsumed_delta_data = utils.ShiftLeft(self.unconsumed_delta_data, consumed)\n\treturn\n}\n\n// Finish applying delta data\nfunc (self *Patcher) FinishDelta() (err error) {\n\tif err = self.UpdateDelta([]byte{}); err != nil {\n\t\treturn err\n\t}\n\tif len(self.unconsumed_delta_data) > 0 {\n\t\treturn fmt.Errorf(\"There are %d leftover bytes in the delta\", len(self.unconsumed_delta_data))\n\t}\n\tself.delta_input = nil\n\tself.delta_output = nil\n\tself.unconsumed_delta_data = nil\n\tif !self.rsync.checksum_done {\n\t\treturn fmt.Errorf(\"The checksum was not received at the end of the delta data\")\n\t}\n\treturn\n}\n\n// Create a signature for the data source in src.\nfunc (self *Patcher) CreateSignatureIterator(src io.Reader, output io.Writer) func() error {\n\tvar it func() (BlockHash, error)\n\tfinished := false\n\tvar b [BlockHashSize]byte\n\treturn func() error {\n\t\tif finished {\n\t\t\treturn io.EOF\n\t\t}\n\t\tif it == nil { // write signature header\n\t\t\tit = self.rsync.CreateSignatureIterator(src)\n\t\t\tbin.PutUint16(b[:], 0)\n\t\t\tbin.PutUint16(b[2:], uint16(self.Checksum_type))\n\t\t\tbin.PutUint16(b[4:], uint16(self.Strong_hash_type))\n\t\t\tbin.PutUint16(b[6:], uint16(self.Weak_hash_type))\n\t\t\tbin.PutUint32(b[8:], uint32(self.rsync.BlockSize))\n\t\t\tif _, err := output.Write(b[:12]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tbl, err := it()\n\t\tswitch err {\n\t\tcase io.EOF:\n\t\t\tfinished = true\n\t\t\treturn io.EOF\n\t\tcase nil:\n\t\t\tbl.Serialize(b[:BlockHashSize])\n\t\t\t_, err = output.Write(b[:BlockHashSize])\n\t\t\treturn err\n\t\tdefault:\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n// Create a serialized delta based on the previously loaded signature\nfunc (self *Differ) CreateDelta(src io.Reader, output io.Writer) func() error {\n\tif err := self.FinishSignatureData(); err != nil {\n\t\treturn func() error { return err }\n\t}\n\tif self.signature == nil {\n\t\treturn func() error {\n\t\t\treturn fmt.Errorf(\"Cannot call CreateDelta() before loading a signature\")\n\t\t}\n\t}\n\treturn self.rsync.CreateDiff(src, self.signature, output)\n}\n\nfunc (self *Differ) BlockSize() int {\n\treturn self.rsync.BlockSize\n}\n\n// Add more external signature data\nfunc (self *Differ) AddSignatureData(data []byte) (err error) {\n\tself.unconsumed_signature_data = append(self.unconsumed_signature_data, data...)\n\tif !self.rsync.HasHasher() {\n\t\tconsumed, err := self.read_signature_header(self.unconsumed_signature_data)\n\t\tif err != nil {\n\t\t\tif consumed < 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tself.unconsumed_signature_data = utils.ShiftLeft(self.unconsumed_signature_data, consumed)\n\t}\n\tconsumed := self.read_signature_blocks(self.unconsumed_signature_data)\n\tself.unconsumed_signature_data = utils.ShiftLeft(self.unconsumed_signature_data, consumed)\n\treturn nil\n}\n\n// Use to calculate a delta based on a supplied signature, via AddSignatureData\nfunc NewDiffer() *Differ {\n\treturn &Differ{}\n}\n\n// Use to create a signature and possibly apply a delta\nfunc NewPatcher(expected_input_size int64) (ans *Patcher) {\n\tbs := DefaultBlockSize\n\tsz := max(0, expected_input_size)\n\tif sz > 0 {\n\t\tbs = int(math.Round(math.Sqrt(float64(sz))))\n\t}\n\tans = &Patcher{}\n\tans.rsync.BlockSize = min(bs, MaxBlockSize)\n\tans.rsync.SetHasher(new_xxh3_64)\n\tans.rsync.SetChecksummer(new_xxh3_128)\n\n\tif ans.rsync.HashBlockSize() > 0 && ans.rsync.HashBlockSize() < ans.rsync.BlockSize {\n\t\tans.rsync.BlockSize = (ans.rsync.BlockSize / ans.rsync.HashBlockSize()) * ans.rsync.HashBlockSize()\n\t}\n\n\tans.expected_input_size_for_signature_generation = sz\n\treturn\n}\n"
  },
  {
    "path": "tools/rsync/api_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage rsync\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\nvar _ = cmp.Diff\n\nfunc run_roundtrip_test(t *testing.T, src_data, changed []byte, num_of_patches, total_patch_size int) {\n\tusing_serialization := false\n\tt.Helper()\n\tprefix_msg := func() string {\n\t\tq := utils.IfElse(using_serialization, \"with\", \"without\")\n\t\treturn fmt.Sprintf(\"Running %s serialization: src size: %d changed size: %d difference: %d\\n\",\n\t\t\tq, len(src_data), len(changed), len(changed)-len(src_data))\n\t}\n\n\ttest_equal := func(src_data, output []byte) {\n\t\tif !bytes.Equal(src_data, output) {\n\t\t\tfirst_diff := min(len(src_data), len(output))\n\t\t\tfor i := 0; i < first_diff; i++ {\n\t\t\t\tif src_data[i] != output[i] {\n\t\t\t\t\tfirst_diff = i\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tt.Fatalf(\"%sPatching failed: %d extra_bytes first different byte at: %d\\nsrc:\\n%s\\nchanged:\\n%s\\noutput:\\n%s\\n\",\n\t\t\t\tprefix_msg(), len(output)-len(src_data), first_diff, string(src_data), string(changed), string(output))\n\t\t}\n\t}\n\n\t// first try just the engine without serialization\n\tp := NewPatcher(int64(len(src_data)))\n\tsignature := make([]BlockHash, 0, 128)\n\ts_it := p.rsync.CreateSignatureIterator(bytes.NewReader(changed))\n\tfor {\n\t\ts, err := s_it()\n\t\tif err == nil {\n\t\t\tsignature = append(signature, s)\n\t\t} else if err == io.EOF {\n\t\t\tbreak\n\t\t} else {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\n\ttotal_data_in_delta := 0\n\tapply_delta := func(signature []BlockHash) []byte {\n\t\tdelta_ops, err := p.rsync.CreateDelta(bytes.NewReader(src_data), signature)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif delta_ops[len(delta_ops)-1].Type != OpHash {\n\t\t\tt.Fatalf(\"Last operation was not OpHash\")\n\t\t}\n\t\ttotal_data_in_delta = 0\n\t\toutputbuf := bytes.Buffer{}\n\t\tfor _, op := range delta_ops {\n\t\t\tif op.Type == OpData {\n\t\t\t\ttotal_data_in_delta += len(op.Data)\n\t\t\t}\n\t\t\tp.rsync.ApplyDelta(&outputbuf, bytes.NewReader(changed), op)\n\t\t}\n\t\treturn outputbuf.Bytes()\n\t}\n\ttest_equal(src_data, apply_delta(signature))\n\tlimit := 2 * (p.rsync.BlockSize * num_of_patches)\n\tif limit > -1 && total_data_in_delta > limit {\n\t\tt.Fatalf(\"%sUnexpectedly poor delta performance: total_patch_size: %d total_delta_size: %d limit: %d\", prefix_msg(), total_patch_size, total_data_in_delta, limit)\n\t}\n\n\t// Now try with serialization\n\tusing_serialization = true\n\tp = NewPatcher(int64(len(changed)))\n\tsignature_of_changed := bytes.Buffer{}\n\tss_it := p.CreateSignatureIterator(bytes.NewReader(changed), &signature_of_changed)\n\tvar err error\n\tfor {\n\t\terr = ss_it()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\td := NewDiffer()\n\tif err := d.AddSignatureData(signature_of_changed.Bytes()); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdb := bytes.Buffer{}\n\tit := d.CreateDelta(bytes.NewBuffer(src_data), &db)\n\tfor {\n\t\tif err := it(); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\tdeltabuf := db.Bytes()\n\toutputbuf := bytes.Buffer{}\n\tp.StartDelta(&outputbuf, bytes.NewReader(changed))\n\tfor len(deltabuf) > 0 {\n\t\tn := min(123, len(deltabuf))\n\t\tif err := p.UpdateDelta(deltabuf[:n]); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tdeltabuf = deltabuf[n:]\n\t}\n\tif err := p.FinishDelta(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttest_equal(src_data, outputbuf.Bytes())\n\tif limit > -1 && p.total_data_in_delta > limit {\n\t\tt.Fatalf(\"%sUnexpectedly poor delta performance: total_patch_size: %d total_delta_size: %d limit: %d\", prefix_msg(), total_patch_size, p.total_data_in_delta, limit)\n\t}\n}\n\nfunc generate_data(block_size, num_of_blocks int, extra ...string) []byte {\n\te := strings.Join(extra, \"\")\n\tans := make([]byte, num_of_blocks*block_size+len(e))\n\tutils.Memset(ans, '_')\n\tfor i := range num_of_blocks {\n\t\toffset := i * block_size\n\t\tcopy(ans[offset:], strconv.Itoa(i))\n\t}\n\tcopy(ans[num_of_blocks*block_size:], e)\n\treturn ans\n}\n\nfunc patch_data(data []byte, patches ...string) (num_of_patches, total_patch_size int) {\n\tnum_of_patches = len(patches)\n\tfor _, patch := range patches {\n\t\to, r, _ := strings.Cut(patch, \":\")\n\t\ttotal_patch_size += len(r)\n\t\tif offset, err := strconv.Atoi(o); err == nil {\n\t\t\tcopy(data[offset:], r)\n\t\t} else {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\treturn\n}\n\nfunc TestRsyncRoundtrip(t *testing.T) {\n\tblock_size := 16\n\tsrc_data := generate_data(block_size, 16)\n\tchanged := slices.Clone(src_data)\n\tnum_of_patches, total_patch_size := patch_data(changed, \"3:patch1\", \"16:patch2\", \"130:ptch3\", \"176:patch4\", \"222:XXYY\")\n\n\trun_roundtrip_test(t, src_data, src_data[block_size:], 1, block_size)\n\trun_roundtrip_test(t, src_data, changed, num_of_patches, total_patch_size)\n\trun_roundtrip_test(t, src_data, []byte{}, -1, 0)\n\trun_roundtrip_test(t, src_data, src_data, 0, 0)\n\trun_roundtrip_test(t, src_data, changed[:len(changed)-3], num_of_patches, total_patch_size)\n\trun_roundtrip_test(t, src_data, append(changed[:37], changed[81:]...), num_of_patches, total_patch_size)\n\n\tblock_size = 13\n\tsrc_data = generate_data(block_size, 17, \"trailer\")\n\tchanged = slices.Clone(src_data)\n\tnum_of_patches, total_patch_size = patch_data(changed, \"0:patch1\", \"19:patch2\")\n\trun_roundtrip_test(t, src_data, changed, num_of_patches, total_patch_size)\n\trun_roundtrip_test(t, src_data, changed[:len(changed)-3], num_of_patches, total_patch_size)\n\trun_roundtrip_test(t, src_data, append(changed, \"xyz...\"...), num_of_patches, total_patch_size)\n}\n\nfunc TestRsyncHashers(t *testing.T) {\n\th := new_xxh3_64()\n\th.Write([]byte(\"abcd\"))\n\tif diff := cmp.Diff(hex.EncodeToString(h.Sum(nil)), `6497a96f53a89890`); diff != \"\" {\n\t\tt.Fatalf(\"%s\", diff)\n\t}\n\tif diff := cmp.Diff(h.Sum64(), uint64(7248448420886124688)); diff != \"\" {\n\t\tt.Fatalf(\"%s\", diff)\n\t}\n\th2 := new_xxh3_128()\n\th2.Write([]byte(\"abcd\"))\n\tif diff := cmp.Diff(hex.EncodeToString(h2.Sum(nil)), `8d6b60383dfa90c21be79eecd1b1353d`); diff != \"\" {\n\t\tt.Fatalf(\"%s\", diff)\n\t}\n}\n"
  },
  {
    "path": "tools/simdstring/benchmark.sh",
    "content": "#!/bin/sh\n#\n# test.sh\n# Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n#\n# Distributed under terms of the MIT license.\n#\n\ngo run generate.go && go test -bench=.\n\n\n"
  },
  {
    "path": "tools/simdstring/benchmarks_test.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage simdstring\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc haystack(filler, needle byte, pos int) []byte {\n\tvar data []byte\n\tif pos > 0 {\n\t\tdata = append(bytes.Repeat([]byte{filler}, pos-1), needle)\n\t} else {\n\t\tdata = []byte{needle}\n\t}\n\treturn data\n}\n\nvar sizes = []int{6, 327, 9875, 1198673}\n\nfunc BenchmarkIndexByte(b *testing.B) {\n\tt := func(pos int, which string) {\n\t\tdata := haystack('a', 'q', pos)\n\t\tf := IndexByte\n\t\tswitch which {\n\t\tcase \"scalar\":\n\t\t\tf = index_byte_scalar\n\t\tcase \"stdlib\":\n\t\t\tf = bytes.IndexByte\n\t\t}\n\t\tb.Run(fmt.Sprintf(\"%s_sz=%d\", which, pos), func(b *testing.B) {\n\t\t\tfor b.Loop() {\n\t\t\t\tf(data, 'q')\n\t\t\t}\n\t\t})\n\t}\n\tfor _, pos := range sizes {\n\t\tt(pos, \"simdstring\")\n\t\tt(pos, \"scalar\")\n\t\tt(pos, \"stdlib\")\n\t}\n}\n\nfunc BenchmarkIndexByte2(b *testing.B) {\n\tt := func(pos int, which string) {\n\t\tdata := haystack('a', 'q', pos)\n\t\tf := IndexByte2\n\t\tswitch which {\n\t\tcase \"scalar\":\n\t\t\tf = index_byte2_scalar\n\t\t}\n\t\tb.Run(fmt.Sprintf(\"%s_sz=%d\", which, pos), func(b *testing.B) {\n\t\t\tfor b.Loop() {\n\t\t\t\tf(data, 'q', 'x')\n\t\t\t}\n\t\t})\n\t}\n\tfor _, pos := range sizes {\n\t\tt(pos, \"simdstring\")\n\t\tt(pos, \"scalar\")\n\t}\n}\n\nfunc BenchmarkNotIndexByte(b *testing.B) {\n\tt := func(pos int, which string) {\n\t\t// Fill with 'a' and place 'q' (a non-matching byte) at the target position\n\t\tdata := haystack('a', 'q', pos)\n\t\tf := NotIndexByte\n\t\tswitch which {\n\t\tcase \"scalar\":\n\t\t\tf = not_index_byte_scalar\n\t\t}\n\t\tb.Run(fmt.Sprintf(\"%s_sz=%d\", which, pos), func(b *testing.B) {\n\t\t\tfor b.Loop() {\n\t\t\t\tf(data, 'a')\n\t\t\t}\n\t\t})\n\t}\n\tfor _, pos := range sizes {\n\t\tt(pos, \"simdstring\")\n\t\tt(pos, \"scalar\")\n\t}\n}\n\nfunc BenchmarkNotIndexByte2(b *testing.B) {\n\tt := func(pos int, which string) {\n\t\t// Fill with 'a' and place 'q' (neither 'a' nor 'x') at the target position\n\t\tdata := haystack('a', 'q', pos)\n\t\tf := NotIndexByte2\n\t\tswitch which {\n\t\tcase \"scalar\":\n\t\t\tf = not_index_byte2_scalar\n\t\t}\n\t\tb.Run(fmt.Sprintf(\"%s_sz=%d\", which, pos), func(b *testing.B) {\n\t\t\tfor b.Loop() {\n\t\t\t\tf(data, 'a', 'x')\n\t\t\t}\n\t\t})\n\t}\n\tfor _, pos := range sizes {\n\t\tt(pos, \"simdstring\")\n\t\tt(pos, \"scalar\")\n\t}\n}\n"
  },
  {
    "path": "tools/simdstring/generate.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n//go:build ignore\n\n// See https://www.quasilyte.dev/blog/post/go-asm-complementary-reference/\n// for differences between AT&T and Go assembly\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/types\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unsafe\"\n)\n\nvar _ = fmt.Print\n\ntype Register struct {\n\tName       string\n\tSize       int\n\tRestricted bool\n}\n\nfunc (r Register) String() string            { return r.Name }\nfunc (r Register) ARMFullWidth() string      { return fmt.Sprintf(\"%s.B%d\", r, r.Size/8) }\nfunc (r Register) AddressInRegister() string { return fmt.Sprintf(\"(%s)\", r) }\n\ntype Arch string\n\nconst (\n\tX86   Arch = \"386\"\n\tAMD64 Arch = \"amd64\"\n\tARM64 Arch = \"arm64\"\n)\n\ntype ISA struct {\n\tBits                       int\n\tGoarch                     Arch\n\tRegisters                  []Register\n\tUsedRegisters              map[Register]bool\n\tSizes                      types.Sizes\n\tGeneralPurposeRegisterSize int\n\tHasSIMD                    bool\n}\n\nconst ByteSlice types.BasicKind = 100001\n\nfunc (isa *ISA) NativeAdd() string {\n\tif isa.Goarch == ARM64 {\n\t\treturn \"ADD\"\n\t}\n\tif isa.GeneralPurposeRegisterSize == 32 {\n\t\treturn \"ADDL\"\n\t}\n\treturn \"ADDQ\"\n}\n\nfunc (isa *ISA) NativeSubtract() string {\n\tif isa.Goarch == ARM64 {\n\t\treturn \"SUB\"\n\t}\n\tif isa.GeneralPurposeRegisterSize == 32 {\n\t\treturn \"SUBL\"\n\t}\n\treturn \"SUBQ\"\n}\n\nfunc (isa *ISA) add_regs(size int, names ...string) {\n\tfor _, r := range names {\n\t\tisa.Registers = append(isa.Registers, Register{r, size, false})\n\t}\n}\n\nfunc (ans *ISA) add_x86_regs() {\n\tans.add_regs(ans.GeneralPurposeRegisterSize, `AX`, `BX`, `DX`, `SI`, `DI`, `BP`)\n\tif ans.GeneralPurposeRegisterSize == 64 {\n\t\tans.add_regs(ans.GeneralPurposeRegisterSize, `R8`, `R9`, `R10`, `R11`, `R12`, `R13`, `R14`, `R15`)\n\t}\n\t// CX is used for shift and rotate instructions\n\tans.Registers = append(ans.Registers, Register{`CX`, ans.GeneralPurposeRegisterSize, true})\n\t// SP is the stack pointer used by the Go runtime\n\tans.Registers = append(ans.Registers, Register{`SP`, ans.GeneralPurposeRegisterSize, true})\n\tans.add_regs(128, `X0`, `X1`, `X2`, `X3`, `X4`, `X5`, `X6`, `X7`, `X8`, `X9`, `X10`, `X11`, `X12`, `X13`, `X14`, `X15`)\n\tif ans.Goarch == AMD64 {\n\t\tans.add_regs(256,\n\t\t\t`Y0`, `Y1`, `Y2`, `Y3`, `Y4`, `Y5`, `Y6`, `Y7`, `Y8`, `Y9`, `Y10`, `Y11`, `Y12`, `Y13`, `Y14`, `Y15`)\n\t}\n}\n\nfunc Createi386ISA(bits int) ISA {\n\tans := ISA{\n\t\tBits:                       bits,\n\t\tGeneralPurposeRegisterSize: 32,\n\t\tGoarch:                     X86,\n\t\tSizes:                      types.SizesFor(runtime.Compiler, string(X86)),\n\t\tHasSIMD:                    bits == 128,\n\t}\n\tans.add_x86_regs()\n\treturn ans\n}\n\nfunc CreateAMD64ISA(bits int) ISA {\n\tans := ISA{\n\t\tBits:                       bits,\n\t\tGeneralPurposeRegisterSize: 64,\n\t\tGoarch:                     AMD64,\n\t\tSizes:                      types.SizesFor(runtime.Compiler, string(AMD64)),\n\t\tHasSIMD:                    true,\n\t}\n\tans.add_x86_regs()\n\treturn ans\n}\n\nfunc CreateARM64ISA(bits int) ISA {\n\tans := ISA{\n\t\tBits:                       bits,\n\t\tGoarch:                     ARM64,\n\t\tGeneralPurposeRegisterSize: 64,\n\t\tSizes:                      types.SizesFor(runtime.Compiler, string(ARM64)),\n\t\tHasSIMD:                    bits == 128,\n\t}\n\tans.add_regs(ans.GeneralPurposeRegisterSize,\n\t\t`R0`, `R1`, `R2`, `R3`, `R4`, `R5`, `R6`, `R7`, `R8`, `R9`, `R10`, `R11`, `R12`, `R13`, `R14`, `R15`)\n\tans.add_regs(128,\n\t\t`V0`, `V1`, `V2`, `V3`, `V4`, `V5`, `V6`, `V7`, `V8`, `V9`, `V10`, `V11`, `V12`, `V13`, `V14`, `V15`,\n\t\t`V16`, `V17`, `V18`, `V19`, `V20`, `V21`, `V22`, `V23`, `V24`, `V25`, `V26`, `V27`, `V28`, `V29`, `V30`, `V31`,\n\t)\n\treturn ans\n}\n\nfunc AsVar(s types.BasicKind, name string) *types.Var {\n\tvar t types.Type\n\tswitch s {\n\tcase ByteSlice:\n\t\tt = types.NewSlice(types.Typ[types.Byte])\n\tdefault:\n\t\tt = types.Typ[s]\n\t}\n\treturn types.NewParam(0, nil, name, t)\n\n}\n\ntype FunctionParam struct {\n\tName string\n\tType types.BasicKind\n}\n\ntype Function struct {\n\tName            string\n\tDesc            string\n\tParams, Returns []FunctionParam\n\tUsedRegisters   map[Register]bool\n\n\tSize                        int\n\tISA                         ISA\n\tParamOffsets, ReturnOffsets []int\n\tInstructions                []string\n\tUsed256BitReg               bool\n}\n\nfunc (f *Function) Reg() Register {\n\tfor _, r := range f.ISA.Registers {\n\t\tif !r.Restricted && r.Size == f.ISA.GeneralPurposeRegisterSize && !f.UsedRegisters[r] {\n\t\t\tf.UsedRegisters[r] = true\n\t\t\treturn r\n\t\t}\n\t}\n\tb := []string{}\n\tfor _, r := range f.ISA.Registers {\n\t\tif !r.Restricted && r.Size == f.ISA.GeneralPurposeRegisterSize {\n\t\t\tb = append(b, r.Name)\n\t\t}\n\t}\n\tpanic(fmt.Sprint(\"No available general purpose registers, used registers: \", strings.Join(b, \", \")))\n}\n\nfunc (f *Function) RegForShifts() Register {\n\tif f.ISA.Goarch == ARM64 {\n\t\treturn f.Reg()\n\t}\n\tfor _, r := range f.ISA.Registers {\n\t\tif r.Name == \"CX\" {\n\t\t\tif f.UsedRegisters[r] {\n\t\t\t\tpanic(\"The register for shifts is already used\")\n\t\t\t}\n\t\t\treturn r\n\t\t}\n\t}\n\tpanic(\"No register for shifts found\")\n}\n\nfunc (f *Function) Vec(size ...int) Register {\n\tszq := f.ISA.Bits\n\tif len(size) > 0 {\n\t\tszq = size[0]\n\t}\n\tif f.ISA.Goarch == ARM64 {\n\t\tfor _, r := range f.ISA.Registers {\n\t\t\tif r.Size == szq && !r.Restricted && !f.UsedRegisters[r] {\n\t\t\t\tf.UsedRegisters[r] = true\n\t\t\t\tif r.Size > 128 {\n\t\t\t\t\tf.Used256BitReg = true\n\t\t\t\t}\n\t\t\t\treturn r\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// In Intels crazy architecture AVX registers and SSE registers are the same hardware register so changing\n\t\t// one can change the other. Sigh.\n\t\tused := make(map[uint32]bool, len(f.UsedRegisters))\n\t\tfor r, is_used := range f.UsedRegisters {\n\t\t\tif is_used && r.Size > f.ISA.GeneralPurposeRegisterSize {\n\t\t\t\tused[r.ARMId()] = true\n\t\t\t}\n\t\t}\n\t\tfor _, r := range f.ISA.Registers {\n\t\t\tif r.Size == szq && !r.Restricted && !used[r.ARMId()] {\n\t\t\t\tf.UsedRegisters[r] = true\n\t\t\t\tif r.Size > 128 {\n\t\t\t\t\tf.Used256BitReg = true\n\t\t\t\t}\n\t\t\t\treturn r\n\t\t\t}\n\t\t}\n\n\t}\n\tpanic(\"No available vector registers\")\n}\n\nfunc (f *Function) ReleaseReg(r ...Register) {\n\tfor _, x := range r {\n\t\tf.UsedRegisters[x] = false\n\t}\n}\n\nfunc (f *Function) instr(items ...any) {\n\tsarr := make([]string, len(items))\n\tfor i, val := range items {\n\t\tvar f string\n\t\tif i > 0 && i < len(items)-1 {\n\t\t\tf = \"%s,\"\n\t\t} else {\n\t\t\tf = \"%s\"\n\t\t}\n\t\tsarr[i] = fmt.Sprintf(f, val)\n\t}\n\tf.Instructions = append(f.Instructions, \"\\t\"+strings.Join(sarr, \" \"))\n}\n\nfunc (f *Function) MemLoadForBasicType(t types.BasicKind) string {\n\tif f.ISA.Goarch == ARM64 {\n\t\tswitch t {\n\t\tcase types.Uint8:\n\t\t\treturn \"MOVBU\"\n\t\tcase types.Int8:\n\t\t\treturn \"MOVB\"\n\t\tcase types.Uint16:\n\t\t\treturn \"MOVHU\"\n\t\tcase types.Int16:\n\t\t\treturn \"MOVH\"\n\t\tcase types.Uint32:\n\t\t\treturn \"MOVWU\"\n\t\tcase types.Int32:\n\t\t\treturn \"MOVW\"\n\t\tcase types.Uint64, types.Uintptr, ByteSlice, types.String, types.Uint, types.Int64, types.Int:\n\t\t\treturn `MOVD`\n\t\t}\n\t} else {\n\t\tif f.ISA.GeneralPurposeRegisterSize == 32 {\n\t\t\tswitch t {\n\t\t\tcase types.Uint8:\n\t\t\t\treturn \"MOVBLZX\"\n\t\t\tcase types.Int8:\n\t\t\t\treturn \"MOVBLSX\"\n\t\t\tcase types.Uint16:\n\t\t\t\treturn \"MOVWLZX\"\n\t\t\tcase types.Int16:\n\t\t\t\treturn \"MOVWLSX\"\n\t\t\tcase types.Uint32, types.Uintptr, types.Int32, ByteSlice, types.String, types.Int, types.Uint:\n\t\t\t\treturn \"MOVL\"\n\t\t\t}\n\t\t} else {\n\t\t\tswitch t {\n\t\t\tcase types.Uint8:\n\t\t\t\treturn \"MOVBQZX\"\n\t\t\tcase types.Int8:\n\t\t\t\treturn \"MOVBQSX\"\n\t\t\tcase types.Uint16:\n\t\t\t\treturn \"MOVWQZX\"\n\t\t\tcase types.Int16:\n\t\t\t\treturn \"MOVWQSX\"\n\t\t\tcase types.Uint32:\n\t\t\t\treturn \"MOVLQZX\"\n\t\t\tcase types.Int32:\n\t\t\t\treturn \"MOVLQSX\"\n\t\t\tcase types.Int64, types.Uint64, types.Uintptr, ByteSlice, types.String, types.Int, types.Uint:\n\t\t\t\treturn \"MOVQ\"\n\t\t\t}\n\t\t}\n\t}\n\tpanic(fmt.Sprint(\"Unknown type: \", t))\n}\n\nfunc (f *Function) LoadUnsignedBytesFromMemory(addr string, n int, dest Register) {\n\tdefer f.AddTrailingComment(dest, \"=\", n, \"byte(s) from the memory pointed to by\", addr)\n\tswitch n {\n\tcase 1:\n\t\tif dest.Size != f.ISA.GeneralPurposeRegisterSize {\n\t\t\tpanic(fmt.Sprintf(\"cannot load %d bytes into vector register\", n))\n\t\t}\n\t\tf.instr(f.MemLoadForBasicType(types.Byte), addr, dest)\n\tcase 2:\n\t\tif dest.Size != f.ISA.GeneralPurposeRegisterSize {\n\t\t\tpanic(fmt.Sprintf(\"cannot load %d bytes into vector register\", n))\n\t\t}\n\t\tf.instr(f.MemLoadForBasicType(types.Uint16), addr, dest)\n\tcase 4:\n\t\tif dest.Size != f.ISA.GeneralPurposeRegisterSize {\n\t\t\tpanic(fmt.Sprintf(\"cannot load %d bytes into vector register\", n))\n\t\t}\n\t\tf.instr(f.MemLoadForBasicType(types.Uint32), addr, dest)\n\tcase 8:\n\t\tif dest.Size != f.ISA.GeneralPurposeRegisterSize {\n\t\t\tpanic(fmt.Sprintf(\"cannot load %d bytes into vector register\", n))\n\t\t}\n\t\tif dest.Size*8 > f.ISA.GeneralPurposeRegisterSize {\n\t\t\tpanic(fmt.Sprintf(\"cannot load %d bytes into %d bit register\", n, dest.Size))\n\t\t}\n\t\tf.instr(f.MemLoadForBasicType(types.Uint64), addr, dest)\n\tdefault:\n\t\tif dest.Size*8 != f.ISA.GeneralPurposeRegisterSize {\n\t\t\tpanic(fmt.Sprintf(\"cannot load %d bytes into %d bit register\", n, dest.Size))\n\t\t}\n\t\tf.instr(f.MemLoadForBasicType(types.Uintptr), addr, dest)\n\t}\n}\n\nfunc (f *Function) LoadParam(p string) Register {\n\tr := f.Reg()\n\tfor i, q := range f.Params {\n\t\tif q.Name == p {\n\t\t\toffset := f.ParamOffsets[i]\n\t\t\tmov := f.MemLoadForBasicType(q.Type)\n\t\t\tf.instr(mov, fmt.Sprintf(\"%s+%d(FP)\", q.Name, offset), r)\n\t\t\tf.AddTrailingComment(\"load the function parameter\", p, \"into\", r)\n\t\t}\n\t}\n\treturn r\n}\n\nfunc (f *Function) set_return_value(offset int, q FunctionParam, val any) {\n\tmov := f.MemLoadForBasicType(q.Type)\n\tvr := val_repr_for_arithmetic(val)\n\tdefer f.AddTrailingComment(\"save the value:\", val, \"to the function return parameter:\", q.Name)\n\tif f.ISA.Goarch == ARM64 && strings.HasPrefix(vr, `$`) {\n\t\t// no way to store an immediate value into a memory address\n\t\ttemp := f.Reg()\n\t\tf.SetRegisterTo(temp, val)\n\t\tdefer f.ReleaseReg(temp)\n\t\tvr = temp.Name\n\t}\n\tf.instr(mov, vr, fmt.Sprintf(\"%s+%d(FP)\", q.Name, offset))\n}\n\nfunc (f *Function) SetReturnValue(p string, val any) {\n\tfor i, q := range f.Returns {\n\t\tif q.Name == p {\n\t\t\tf.set_return_value(f.ReturnOffsets[i], q, val)\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc (f *Function) CountTrailingZeros(r, ans Register) {\n\tif r.Size == f.ISA.GeneralPurposeRegisterSize {\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\tf.instr(\"RBIT\", r, r)\n\t\t\tf.AddTrailingComment(\"reverse the bits\")\n\t\t\tf.instr(\"CLZ\", r, ans)\n\t\t\tf.AddTrailingComment(ans, \"= number of leading zeros in\", r)\n\t\t} else {\n\t\t\tf.instr(\"BSFL\", ans, ans)\n\t\t\tf.AddTrailingComment(ans, \"= number of trailing zeros in\", r)\n\t\t}\n\t} else {\n\t\tpanic(\"cannot count trailing zeros in a vector register\")\n\t}\n}\n\nfunc (f *Function) Comment(x ...any) {\n\tf.Instructions = append(f.Instructions, space_join(\"\\t//\", x...))\n}\n\nfunc shrn8b_immediate4(a, b Register) uint32 {\n\treturn (0x0f0c84 << 8) | (a.ARMId()<<5 | b.ARMId())\n}\n\nfunc encode_cmgt16b(a, b, dest Register) (ans uint32) {\n\treturn 0x271<<21 | b.ARMId()<<16 | 0xd<<10 | a.ARMId()<<5 | dest.ARMId()\n}\n\nfunc encode_not16b(src, dest Register) uint32 {\n\t// NOT Vd.16B, Vn.16B (alias of MVN)\n\t// Encoding: 0 Q 1 01110 size 10000 00101 10 Rn Rd (Q=1, size=00 for .16B)\n\treturn 0x6E205800 | (src.ARMId() << 5) | dest.ARMId()\n}\n\nfunc (f *Function) MaskForCountDestructive(vec, ans Register) {\n\t// vec is clobbered by this function\n\tf.Comment(\"Count the number of bytes to the first 0xff byte and put the result in\", ans)\n\tif f.ISA.Goarch == ARM64 {\n\t\t// See https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon\n\t\tf.Comment(\"Go assembler doesn't support the shrn instruction, below we have: shrn.8b\", vec, vec, \"#4\")\n\t\tf.Comment(\"It is shifting right by four bits in every 16 bit word and truncating to 8 bits storing the result in the lower 64 bits of\", vec)\n\t\tf.instr(\"WORD\", fmt.Sprintf(\"$0x%x\", shrn8b_immediate4(vec, vec)))\n\t\tf.instr(\"FMOVD\", \"F\"+vec.Name[1:], ans)\n\t\tf.AddTrailingComment(\"Extract the lower 64 bits from\", vec, \"and put them into\", ans)\n\t} else {\n\t\tif f.ISA.Bits == 128 {\n\t\t\tf.instr(\"PMOVMSKB\", vec, ans)\n\t\t} else {\n\t\t\tf.instr(\"VPMOVMSKB\", vec, ans)\n\t\t}\n\t\tf.AddTrailingComment(ans, \"= mask of the highest bit in every byte in\", vec)\n\t}\n}\n\nfunc (f *Function) shift_self(right bool, self, amt any) {\n\top := \"\"\n\tif right {\n\t\top = \"SHRQ\"\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\top = \"LSR\"\n\t\t}\n\t} else {\n\t\top = \"SHLQ\"\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\top = \"LSL\"\n\t\t}\n\t}\n\tswitch v := amt.(type) {\n\tcase Register:\n\t\tif f.ISA.Goarch != ARM64 && v.Name != \"CX\" {\n\t\t\tpanic(\"On Intel only the CX register can be used for shifts\")\n\t\t}\n\t\tf.instr(op, v, self)\n\tdefault:\n\t\tf.instr(op, val_repr_for_arithmetic(v), self)\n\t}\n}\n\nfunc (f *Function) ShiftSelfRight(self, amt any) {\n\tf.shift_self(true, self, amt)\n}\n\nfunc (f *Function) ShiftSelfLeft(self, amt any) {\n\tf.shift_self(false, self, amt)\n}\n\nfunc (f *Function) ShiftMaskRightDestructive(mask, amt any) {\n\t// The amt register is clobbered by this function\n\tswitch n := amt.(type) {\n\tcase Register:\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\tf.Comment(\"The mask has 4 bits per byte, so multiply\", n, \"by 4\")\n\t\t\tf.ShiftSelfLeft(n, 2)\n\t\t}\n\t\tf.ShiftSelfRight(mask, n)\n\tcase int:\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\tn <<= 2\n\t\t}\n\t\tf.ShiftSelfRight(mask, n)\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"Cannot shift by: %s\", amt))\n\t}\n}\n\nfunc (f *Function) CountLeadingZeroBytesInMask(src, ans Register) {\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.CountTrailingZeros(src, ans)\n\t\tf.instr(\"UBFX\", \"$2\", ans, \"$30\", ans)\n\t\tf.AddTrailingComment(ans, \">>= 2 (divide by 4)\")\n\t} else {\n\t\tf.CountTrailingZeros(src, ans)\n\t}\n}\n\nfunc (f *Function) CountBytesToFirstMatchDestructive(vec, ans Register) {\n\t// vec is clobbered by this function\n\tf.Comment(\"Count the number of bytes to the first 0xff byte and put the result in\", ans)\n\tf.MaskForCountDestructive(vec, ans)\n\tf.CountLeadingZeroBytesInMask(ans, ans)\n\tf.BlankLine()\n}\n\nfunc (f *Function) LoadParamLen(p string) Register {\n\tr := f.Reg()\n\tfor i, q := range f.Params {\n\t\tif q.Name == p {\n\t\t\toffset := f.ParamOffsets[i]\n\t\t\tif q.Type == ByteSlice || q.Type == types.String {\n\t\t\t\toffset += int(f.ISA.Sizes.Sizeof(types.Typ[types.Uintptr]))\n\t\t\t}\n\t\t\tmov := f.MemLoadForBasicType(q.Type)\n\t\t\tf.instr(mov, fmt.Sprintf(\"%s_len+%d(FP)\", q.Name, offset), r)\n\t\t\tf.AddTrailingComment(\"load the length of the function parameter\", q.Name, \"into\", r)\n\t\t\tbreak\n\t\t}\n\t}\n\treturn r\n}\n\nfunc (f *Function) unaligned_move() string {\n\tswitch f.ISA.Goarch {\n\tcase X86, AMD64:\n\t\tif f.ISA.Bits == 128 {\n\t\t\treturn \"MOVOU\"\n\t\t}\n\t\treturn \"VMOVDQU\"\n\tdefault:\n\t\tpanic(\"Unknown arch: \" + string(f.ISA.Goarch))\n\t}\n}\n\nfunc (f *Function) aligned_move() string {\n\tswitch f.ISA.Goarch {\n\tcase X86, AMD64:\n\t\tif f.ISA.Bits == 128 {\n\t\t\treturn \"MOVOA\"\n\t\t}\n\t\treturn \"VMOVDQA\"\n\tdefault:\n\t\tpanic(\"Unknown arch: \" + string(f.ISA.Goarch))\n\t}\n}\n\nfunc (f *Function) LoadPointerUnaligned(register_containing_pointer_value Register, dest Register) {\n\taddr := register_containing_pointer_value.AddressInRegister()\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(`VLD1`, addr, \"[\"+dest.ARMFullWidth()+\"]\")\n\t} else {\n\t\tf.instr(f.unaligned_move(), addr, dest)\n\t}\n\tf.AddTrailingComment(\"load memory from the address in\", register_containing_pointer_value, \"to\", dest)\n}\n\nfunc (f *Function) LoadPointerAligned(register_containing_pointer_value Register, dest Register) {\n\taddr := register_containing_pointer_value.AddressInRegister()\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(`VLD1`, addr, \"[\"+dest.ARMFullWidth()+\"]\")\n\t} else {\n\t\tf.instr(f.aligned_move(), addr, dest)\n\t}\n\tf.AddTrailingComment(\"load memory from the address in\", register_containing_pointer_value, \"to\", dest)\n}\n\nfunc (f *Function) StoreUnalignedToPointer(vec, register_containing_pointer_value Register) {\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(`VST1`, \"[\"+vec.ARMFullWidth()+\"]\", fmt.Sprintf(\"(%s)\", register_containing_pointer_value))\n\t} else {\n\t\tf.instr(f.unaligned_move(), vec, fmt.Sprintf(\"(%s)\", register_containing_pointer_value))\n\t}\n\tf.AddTrailingComment(\"store the value of\", vec, \"in to the memory whose address is in:\", register_containing_pointer_value)\n}\n\nfunc (f *Function) test_if_zero(a Register) {\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(\"AND\", a, a, a)\n\t}\n\tswitch a.Size {\n\tcase 32:\n\t\tf.instr(\"TESTL\", a, a)\n\tcase 64:\n\t\tf.instr(\"TESTQ\", a, a)\n\tcase 128:\n\t\tf.instr(\"PTEST\", a, a)\n\tdefault:\n\t\tf.instr(\"VPTEST\", a, a)\n\t}\n\tf.AddTrailingComment(\"test if\", a, \"is zero\")\n\n}\n\nfunc (f *Function) JumpTo(label string) {\n\tf.instr(\"JMP\", label)\n\tf.AddTrailingComment(\"jump to:\", label)\n}\n\nfunc (f *Function) jump_on_zero_check(a Register, label string, on_zero bool) {\n\tif f.ISA.Goarch == ARM64 {\n\t\tif a.Size > f.ISA.GeneralPurposeRegisterSize {\n\t\t\ttemp := f.Vec()\n\t\t\tdefer f.ReleaseReg(temp)\n\t\t\tf.instr(\"VDUP\", a.Name+\".D[1]\", temp)\n\t\t\tf.AddTrailingComment(`duplicate the upper 64 bits of`, a, \"into the lower and upper 64 bits of\", temp)\n\t\t\tf.Or(a, temp, temp)\n\t\t\ta = f.Reg()\n\t\t\tdefer f.ReleaseReg(a)\n\t\t\tf.instr(\"FMOVD\", \"F\"+temp.Name[1:], a)\n\t\t\tf.AddTrailingComment(a, \"= lower 64bits of\", temp)\n\t\t}\n\t\tif on_zero {\n\t\t\tf.instr(\"CBZ\", a, label)\n\t\t} else {\n\t\t\tf.instr(\"CBNZ\", a, label)\n\t\t}\n\t} else {\n\t\tf.test_if_zero(a)\n\t\tif on_zero {\n\t\t\tf.instr(\"JZ\", label)\n\t\t} else {\n\t\t\tf.instr(\"JNZ\", label)\n\t\t}\n\t}\n}\n\nfunc (f *Function) JumpIfZero(a Register, label string) {\n\tf.jump_on_zero_check(a, label, true)\n\tf.AddTrailingComment(\"jump to:\", label, \"if\", a, \"is zero\")\n}\n\nfunc (f *Function) JumpIfNonZero(a Register, label string) {\n\tf.jump_on_zero_check(a, label, false)\n\tf.AddTrailingComment(\"jump to:\", label, \"if\", a, \"is non-zero\")\n}\n\nfunc (f *Function) compare(a, b Register) {\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(\"CMP\", b, a)\n\t} else {\n\t\tif a.Size == 32 {\n\t\t\tf.instr(\"CMPL\", a, b)\n\t\t} else {\n\t\t\tf.instr(\"CMPQ\", a, b)\n\t\t}\n\t}\n\tf.AddTrailingComment(\"compare\", a, \"to\", b)\n}\n\nfunc (f *Function) JumpIfLessThan(a, b Register, label string) {\n\tf.compare(a, b)\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(\"BLT\", label)\n\t} else {\n\t\tf.instr(\"JLT\", label)\n\t}\n\tf.AddTrailingComment(\"jump to:\", label, \"if\", a, \"<\", b)\n}\n\nfunc (f *Function) JumpIfLessThanOrEqual(a, b Register, label string) {\n\tf.compare(a, b)\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(\"BLE\", label)\n\t} else {\n\t\tf.instr(\"JLE\", label)\n\t}\n\tf.AddTrailingComment(\"jump to:\", label, \"if\", a, \"<=\", b)\n}\n\nfunc (f *Function) JumpIfEqual(a, b Register, label string) {\n\tf.compare(a, b)\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(\"BEQ\", label)\n\t} else {\n\t\tf.instr(\"JE\", label)\n\t}\n\tf.AddTrailingComment(\"jump to:\", label, \"if\", a, \"==\", b)\n}\n\nfunc (f *Function) Or(a, b, dest Register) {\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(\"VORR\", a.ARMFullWidth(), b.ARMFullWidth(), dest.ARMFullWidth())\n\t} else {\n\t\tif f.ISA.Bits == 128 {\n\t\t\tswitch dest.Name {\n\t\t\tcase b.Name:\n\t\t\t\tf.instr(\"POR\", a, b)\n\t\t\tcase a.Name:\n\t\t\t\tf.instr(\"POR\", b, a)\n\t\t\tdefault:\n\t\t\t\tf.CopyRegister(b, dest)\n\t\t\t\tf.instr(\"POR\", a, dest)\n\t\t\t}\n\t\t} else {\n\t\t\tf.instr(\"VPOR\", a, b, dest)\n\t\t}\n\t}\n\tf.AddTrailingComment(dest, \"=\", a, \"|\", b, \"(bitwise)\")\n}\n\nfunc (f *Function) NotSelf(r Register) {\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.Comment(\"Go assembler doesn't support the VMVN instruction, below we have: NOT\", r.ARMFullWidth()+\",\", r.ARMFullWidth())\n\t\tf.instr(\"WORD\", fmt.Sprintf(\"$0x%x\", encode_not16b(r, r)))\n\t\tf.AddTrailingComment(r, \"= ~\", r, \"(bitwise NOT)\")\n\t\treturn\n\t}\n\tall_ones := f.Vec(r.Size)\n\tdefer f.ReleaseReg(all_ones)\n\tf.AllOnesRegister(all_ones)\n\tif r.Size == 128 {\n\t\tf.instr(\"PXOR\", all_ones, r)\n\t} else {\n\t\tf.instr(\"VPXOR\", all_ones, r, r)\n\t}\n\tf.AddTrailingComment(r, \"= ~\", r, \"(bitwise NOT)\")\n}\n\nfunc (f *Function) And(a, b, dest Register) {\n\tif f.ISA.Goarch == ARM64 {\n\t\tf.instr(\"VAND\", a.ARMFullWidth(), b.ARMFullWidth(), dest.ARMFullWidth())\n\t} else {\n\t\tif f.ISA.Bits == 128 {\n\t\t\tswitch dest.Name {\n\t\t\tcase b.Name:\n\t\t\t\tf.instr(\"PAND\", a, b)\n\t\t\tcase a.Name:\n\t\t\t\tf.instr(\"PAND\", b, a)\n\t\t\tdefault:\n\t\t\t\tf.CopyRegister(b, dest)\n\t\t\t\tf.instr(\"PAND\", a, dest)\n\t\t\t}\n\t\t} else {\n\t\t\tf.instr(\"VPAND\", a, b, dest)\n\t\t}\n\t}\n\tf.AddTrailingComment(dest, \"=\", a, \"&\", b, \"(bitwise)\")\n}\n\nfunc (f *Function) ClearRegisterToZero(r Register) {\n\tdefer func() { f.AddTrailingComment(\"set\", r, \"to zero\") }()\n\tif f.ISA.Goarch == ARM64 {\n\t\tif r.Size == f.ISA.GeneralPurposeRegisterSize {\n\t\t\tf.instr(f.MemLoadForBasicType(types.Int32), val_repr_for_arithmetic(0), r)\n\t\t} else {\n\t\t\tf.instr(\"VMOVI\", \"$0\", r.ARMFullWidth())\n\t\t}\n\t\treturn\n\t}\n\tswitch r.Size {\n\tcase 128:\n\t\tf.instr(\"PXOR\", r, r)\n\tcase f.ISA.GeneralPurposeRegisterSize:\n\t\tif r.Size == 32 {\n\t\t\tf.instr(\"XORL\", r, r)\n\t\t} else {\n\t\t\tf.instr(\"XORQ\", r, r)\n\t\t}\n\tcase 256:\n\t\tf.instr(\"VPXOR\", r, r, r)\n\t}\n}\n\nfunc (f *Function) AllOnesRegister(r Register) {\n\tswitch r.Size {\n\tdefault:\n\t\tf.CmpEqEpi8(r, r, r)\n\tcase f.ISA.GeneralPurposeRegisterSize:\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\tf.instr(\"MOVD\", \"$-1\", r)\n\t\t} else {\n\t\t\tswitch r.Size {\n\t\t\tcase 32:\n\t\t\t\tf.instr(\"MOVL\", \"$0xFFFFFFFF\")\n\t\t\tcase 64:\n\t\t\t\tf.instr(\"MOVQ\", \"$0xFFFFFFFFFFFFFFFF\")\n\t\t\t}\n\t\t}\n\t\tf.AddTrailingComment(r, \"= all ones\")\n\t}\n}\n\nfunc (f *Function) CopyRegister(a, ans Register) {\n\tif a.Size != ans.Size {\n\t\tpanic(\"Can only copy registers of equal sizes\")\n\t}\n\tif a.Size > f.ISA.GeneralPurposeRegisterSize {\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\tf.instr(\"VDUP\", a.Name[1:]+\".D2\", ans.Name+\".D2\")\n\t\t} else {\n\t\t\tf.instr(f.aligned_move(), a, ans)\n\t\t}\n\t} else {\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\tf.instr(\"MOVD\", a, ans)\n\t\t} else {\n\t\t\tif a.Size == 32 {\n\t\t\t\tf.instr(\"MOVL\", a, ans)\n\t\t\t} else {\n\t\t\t\tf.instr(\"MOVQ\", a, ans)\n\t\t\t}\n\t\t}\n\t}\n\tf.AddTrailingComment(ans, \"=\", a)\n}\n\nfunc (f *Function) SetRegisterTo(self Register, val any) {\n\tswitch v := val.(type) {\n\tcase Register:\n\t\tf.CopyRegister(self, v)\n\tcase int:\n\t\tif self.Size != f.ISA.GeneralPurposeRegisterSize {\n\t\t\tpanic(\"TODO: Cannot yet set constant values in vector registers\")\n\t\t}\n\t\tswitch v {\n\t\tcase 0:\n\t\t\tf.ClearRegisterToZero(self)\n\t\tcase -1:\n\t\t\tf.AllOnesRegister(self)\n\t\tdefault:\n\t\t\tif f.ISA.Goarch == ARM64 {\n\t\t\t\tf.instr(\"MOVD\", val_repr_for_arithmetic(v), self)\n\t\t\t} else {\n\t\t\t\tf.instr(\"MOVL\", val_repr_for_arithmetic(v), self)\n\t\t\t}\n\t\t\tf.AddTrailingComment(self, \"= \", v)\n\t\t}\n\tcase string:\n\t\tf.instr(f.MemLoadForBasicType(types.Uintptr), v)\n\t\tf.AddTrailingComment(self, \"=\", self.Size/8, \"bytes at the address\", v)\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"cannot set register to value: %#v\", val))\n\t}\n}\n\nfunc (r Register) ARMId() uint32 {\n\tnum, err := strconv.ParseUint(r.Name[1:], 10, 32)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn uint32(num)\n}\n\nfunc (f *Function) cmp(a, b, ans Register, op, c_rep string) {\n\tif a.Size != b.Size || a.Size != ans.Size {\n\t\tpanic(\"Can only compare registers of equal sizes\")\n\t}\n\tif f.ISA.Goarch == ARM64 {\n\t\tif op == \"EQ\" {\n\t\t\tf.instr(\"VCMEQ\", a.ARMFullWidth(), b.ARMFullWidth(), ans.ARMFullWidth())\n\t\t} else {\n\t\t\tf.instr(\"WORD\", fmt.Sprintf(\"$0x%x\", encode_cmgt16b(a, b, ans)))\n\t\t}\n\t} else {\n\t\tfop := `PCMP` + op + \"B\"\n\t\tif f.ISA.Bits == 128 {\n\t\t\tif op == \"EQ\" {\n\t\t\t\tswitch ans.Name {\n\t\t\t\tcase a.Name:\n\t\t\t\t\tf.instr(fop, b, ans)\n\t\t\t\tcase b.Name:\n\t\t\t\t\tf.instr(fop, a, ans)\n\t\t\t\tdefault:\n\t\t\t\t\tf.CopyRegister(a, ans)\n\t\t\t\t\tf.instr(fop, b, ans)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// order matters, we want destination aka 2nd arg to be both a and ans\n\t\t\t\tswitch ans.Name {\n\t\t\t\tcase a.Name:\n\t\t\t\t\tf.instr(fop, b, a)\n\t\t\t\tcase b.Name:\n\t\t\t\t\tvec := f.Vec(a.Size)\n\t\t\t\t\tf.CopyRegister(a, vec)\n\t\t\t\t\tf.instr(fop, b, vec)\n\t\t\t\t\tf.CopyRegister(vec, b)\n\t\t\t\t\tf.ReleaseReg(vec)\n\t\t\t\tdefault:\n\t\t\t\t\tf.CopyRegister(a, ans)\n\t\t\t\t\tf.instr(fop, b, ans)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tf.instr(\"V\"+fop, b, a, ans)\n\t\t}\n\t}\n\tf.AddTrailingComment(ans, \"= 0xff on every byte where\", a.Name+\"[n]\", c_rep, b.Name+\"[n] and zero elsewhere\")\n}\n\nfunc (f *Function) CmpGtEpi8(a, b, ans Register) {\n\tf.cmp(a, b, ans, \"GT\", \">\")\n}\n\nfunc (f *Function) CmpLtEpi8(a, b, ans Register) {\n\tf.cmp(b, a, ans, \"GT\", \">\")\n}\n\nfunc (f *Function) CmpEqEpi8(a, b, ans Register) {\n\tf.cmp(a, b, ans, \"EQ\", \"==\")\n}\n\nfunc (f *Function) Set1Epi8(val any, vec Register) {\n\tif vec.Size != 128 && vec.Size != 256 {\n\t\tpanic(\"Set1Epi8 only works on vector registers\")\n\t}\n\tdo_shuffle_load := func(r Register) {\n\t\tf.instr(\"MOVL\", r, vec)\n\t\tshuffle_mask := f.Vec()\n\t\tf.ClearRegisterToZero(shuffle_mask)\n\t\tf.instr(\"PSHUFB\", shuffle_mask, vec)\n\t\tf.ReleaseReg(shuffle_mask)\n\t}\n\n\tswitch v := val.(type) {\n\tdefault:\n\t\tpanic(\"unknown type for set1_epi8\")\n\tcase int:\n\t\tswitch v {\n\t\tcase 0:\n\t\t\tf.ClearRegisterToZero(vec)\n\t\t\treturn\n\t\tcase -1:\n\t\t\tf.AllOnesRegister(vec)\n\t\t\treturn\n\t\t}\n\t\tr := f.Reg()\n\t\tdefer f.ReleaseReg(r)\n\t\tf.SetRegisterTo(r, v)\n\t\tf.Set1Epi8(r, vec)\n\tcase Register:\n\t\tf.Comment(\"Set all bytes of\", vec, \"to the lowest byte in\", v)\n\t\tif v.Size != f.ISA.GeneralPurposeRegisterSize {\n\t\t\tpanic(\"Can only set1_epi8 from a general purpose register\")\n\t\t}\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\tf.instr(\"VMOV\", v, vec.ARMFullWidth())\n\t\t} else {\n\t\t\tswitch vec.Size {\n\t\t\tcase 128:\n\t\t\t\tdo_shuffle_load(v)\n\t\t\tcase 256:\n\t\t\t\ttemp := f.Vec(128)\n\t\t\t\tdefer f.ReleaseReg(temp)\n\t\t\t\tf.instr(\"VMOVD\", v, temp)\n\t\t\t\tf.instr(\"VPBROADCASTB\", temp, vec)\n\t\t\t}\n\t\t}\n\t\tdefer f.Comment()\n\tcase string:\n\t\tf.Comment(\"Set all bytes of\", vec, \"to the first byte in\", v)\n\t\tif f.ISA.Goarch == ARM64 {\n\t\t\tr := f.LoadParam(v)\n\t\t\tf.instr(\"VMOV\", r, vec.ARMFullWidth())\n\t\t\tf.ReleaseReg(r)\n\t\t\treturn\n\t\t}\n\t\tswitch vec.Size {\n\t\tcase 128:\n\t\t\tr := f.LoadParam(v)\n\t\t\tdefer f.ReleaseReg(r)\n\t\t\tdo_shuffle_load(r)\n\t\tcase 256:\n\t\t\tf.instr(\"VPBROADCASTB\", f.ParamPos(v), vec)\n\t\t}\n\t\tdefer f.Comment()\n\t}\n}\n\nfunc (isa *ISA) structsize(vs []*types.Var) int64 {\n\tn := len(vs)\n\tif n == 0 {\n\t\treturn 0\n\t}\n\toffsets := isa.Sizes.Offsetsof(vs)\n\treturn offsets[n-1] + isa.Sizes.Sizeof(vs[n-1].Type())\n}\n\nfunc tuplevars(params []FunctionParam) []*types.Var {\n\tvars := make([]*types.Var, len(params))\n\tfor i, p := range params {\n\t\tvars[i] = AsVar(p.Type, p.Name)\n\t}\n\treturn vars\n}\n\nfunc NewFunction(isa ISA, name, description string, params, returns []FunctionParam) *Function {\n\tname = fmt.Sprintf(\"%s_%d\", name, isa.Bits)\n\tans := Function{Name: name, Desc: description, Params: params, Returns: returns, ISA: isa}\n\tvars := tuplevars(params)\n\tvars = append(vars, types.NewParam(0, nil, \"sentinel\", types.Typ[types.Uint64]))\n\toffsets := isa.Sizes.Offsetsof(vars)\n\tn := len(params)\n\tparamssize := int(offsets[n])\n\tans.ParamOffsets = make([]int, n)\n\tans.Size = paramssize\n\tfor i := range ans.ParamOffsets {\n\t\tans.ParamOffsets[i] = int(offsets[i])\n\t}\n\tif len(returns) > 0 {\n\t\tvars = tuplevars(returns)\n\t\toffsets = isa.Sizes.Offsetsof(vars)\n\t\tans.ReturnOffsets = make([]int, len(offsets))\n\t\tfor i, off := range offsets {\n\t\t\tans.ReturnOffsets[i] = paramssize + int(off)\n\t\t}\n\t\tans.Size += int(isa.structsize(vars))\n\t}\n\treturn &ans\n}\n\nfunc (s *Function) ParamPos(name string) string {\n\tfor n, i := range s.Params {\n\t\tif i.Name == name {\n\t\t\treturn fmt.Sprintf(\"%s+%d(FP)\", i.Name, s.ParamOffsets[n])\n\t\t}\n\t}\n\tpanic(fmt.Errorf(\"Unknown parameter: %s\", name))\n}\n\nfunc (s *Function) print_signature(w io.Writer) {\n\tfmt.Fprintf(w, \"func %s(\", s.Name)\n\tprint_p := func(p FunctionParam) {\n\t\tvar tname string\n\t\tif p.Type == ByteSlice {\n\t\t\ttname = \"[]byte\"\n\t\t} else {\n\t\t\ttname = types.Universe.Lookup(types.Typ[p.Type].String()).String()\n\t\t}\n\t\ttname, _ = strings.CutPrefix(tname, \"type \")\n\t\tfmt.Fprintf(w, \"%s %s\", p.Name, tname)\n\t}\n\tfor i, p := range s.Params {\n\t\tif i > 0 {\n\t\t\tfmt.Fprint(w, \", \")\n\t\t}\n\t\tprint_p(p)\n\t}\n\tfmt.Fprint(w, \")\")\n\tif len(s.Returns) == 0 {\n\t\treturn\n\t}\n\tfmt.Fprint(w, \" (\")\n\tfor i, p := range s.Returns {\n\t\tif i > 0 {\n\t\t\tfmt.Fprint(w, \", \")\n\t\t}\n\t\tprint_p(p)\n\t}\n\tfmt.Fprint(w, \")\")\n}\n\nfunc (s *Function) OutputStub(w io.Writer) {\n\tif s.Desc != \"\" {\n\t\tfmt.Fprintln(w, \"// \"+s.Desc)\n\t\tfmt.Fprintln(w, \"//\")\n\t}\n\tif s.ISA.HasSIMD {\n\t\tfmt.Fprintln(w, \"//go:noescape\")\n\t}\n\ts.print_signature(w)\n\tif s.ISA.HasSIMD {\n\t\tfmt.Fprintln(w)\n\t} else {\n\t\tfmt.Fprintln(w, \"{\")\n\t\tfmt.Fprintln(w, \"panic(\\\"No SIMD implementations for this function\\\")\")\n\t\tfmt.Fprintln(w, \"}\")\n\t}\n\tfmt.Fprintln(w)\n}\n\nfunc (s *Function) BlankLine() { s.Instructions = append(s.Instructions, \"\") }\n\nfunc (s *Function) Return() {\n\tif s.Used256BitReg {\n\t\ts.instr(\"VZEROUPPER\")\n\t\ts.AddTrailingComment(\"zero upper bits of AVX registers to avoid dependencies when switching between SSE and AVX code\")\n\t}\n\ts.instr(\"RET\")\n\ts.AddTrailingComment(\"return from function\")\n\ts.BlankLine()\n}\n\nfunc (s *Function) end_function() {\n\tamt := 16\n\tif s.Used256BitReg {\n\t\tamt = 32\n\t}\n\ts.instr(fmt.Sprintf(\"PCALIGN $%d\\n\", amt))\n\ts.Return()\n}\n\nfunc (s *Function) Label(name string) {\n\ts.Instructions = append(s.Instructions, name+\":\")\n\ts.AddTrailingComment(\"jump target\")\n}\n\nfunc space_join(prefix string, x ...any) string {\n\tb := strings.Builder{}\n\tif prefix != \"\" {\n\t\tb.WriteString(prefix)\n\t\tb.WriteByte(' ')\n\t}\n\tfor _, x := range x {\n\t\tb.WriteString(fmt.Sprint(x))\n\t\tb.WriteByte(' ')\n\t}\n\treturn b.String()\n\n}\n\nfunc (s *Function) AddTrailingComment(x ...any) {\n\ts.Instructions[len(s.Instructions)-1] += space_join(\" //\", x...)\n}\n\nfunc val_repr_for_arithmetic(val any) (ans string) {\n\tswitch v := val.(type) {\n\tcase int:\n\t\tif v < 0 {\n\t\t\treturn fmt.Sprintf(\"$%d\", v)\n\t\t}\n\t\treturn fmt.Sprintf(\"$0x%x\", v)\n\tcase string:\n\t\treturn val.(string)\n\tcase fmt.Stringer:\n\t\treturn val.(fmt.Stringer).String()\n\tdefault:\n\t\treturn fmt.Sprint(val)\n\t}\n}\n\nfunc (f *Function) AndSelf(self Register, val any) {\n\tswitch f.ISA.Goarch {\n\tcase ARM64:\n\t\tf.instr(\"AND\", val_repr_for_arithmetic(val), self)\n\tcase AMD64:\n\t\tf.instr(\"ANDQ\", val_repr_for_arithmetic(val), self)\n\tcase X86:\n\t\tf.instr(\"ANDL\", val_repr_for_arithmetic(val), self)\n\tdefault:\n\t\tpanic(\"Unknown architecture for AND\")\n\t}\n\tf.AddTrailingComment(self, \"&=\", val)\n}\n\nfunc (f *Function) NegateSelf(self Register) {\n\tif f.ISA.Goarch == \"ARM64\" {\n\t\tf.instr(\"NEG\", self, self)\n\t} else {\n\t\tf.instr(\"NEGQ\", self)\n\t}\n\tf.AddTrailingComment(self, \"*= -1\")\n}\n\nfunc (f *Function) AddToSelf(self Register, val any) {\n\tf.instr(f.ISA.NativeAdd(), val_repr_for_arithmetic(val), self) // pos += sizeof(vec)\n\tf.AddTrailingComment(self, \"+=\", val)\n}\n\nfunc (f *Function) SubtractFromSelf(self Register, val any) {\n\tf.instr(f.ISA.NativeSubtract(), val_repr_for_arithmetic(val), self) // pos += sizeof(vec)\n\tf.AddTrailingComment(self, \"-=\", val)\n}\n\nfunc (s *Function) SetRegisterToOffset(dest Register, base_register Register, constant_offset int, offset_register Register) {\n\tif s.ISA.Goarch == ARM64 {\n\t\ts.SetRegisterTo(dest, constant_offset)\n\t\ts.AddToSelf(dest, base_register)\n\t\ts.AddToSelf(dest, offset_register)\n\t} else {\n\t\taddr := fmt.Sprintf(\"%d(%s)(%s*1)\", constant_offset, base_register, offset_register)\n\t\ts.instr(s.ISA.LEA(), addr, dest)\n\t\ts.AddTrailingComment(dest, \"=\", base_register, \"+\", offset_register, \"+\", constant_offset)\n\t}\n}\n\nfunc (s *Function) OutputASM(w io.Writer) {\n\tif !s.ISA.HasSIMD {\n\t\treturn\n\t}\n\tfmt.Fprint(w, \"// \")\n\ts.print_signature(w)\n\tfmt.Fprintf(w, \"\\nTEXT ·%s(SB), NOSPLIT|TOPFRAME|NOFRAME, $0-%d\\n\", s.Name, s.Size)\n\n\thas_trailing_return := false\n\tfor _, i := range s.Instructions {\n\t\tif len(i) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(i, \"\\tRET \") {\n\t\t\thas_trailing_return = true\n\t\t} else {\n\t\t\thas_trailing_return = false\n\t\t}\n\t}\n\n\tif !has_trailing_return {\n\t\ts.Return()\n\t}\n\tfor _, i := range s.Instructions {\n\t\tfmt.Fprintln(w, i)\n\t}\n\tfmt.Fprintln(w)\n}\n\ntype State struct {\n\tISA                           ISA\n\tActiveFunction                *Function\n\tASMOutput, StubOutput         strings.Builder\n\tTestASMOutput, TestStubOutput strings.Builder\n}\n\nvar package_name = \"simdstring\"\n\nfunc NewState(isa ISA, build_tags ...string) *State {\n\tans := &State{ISA: isa}\n\tif len(build_tags) == 0 {\n\t\tbuild_tags = append(build_tags, string(isa.Goarch))\n\t}\n\n\tbuild_tag := func(w io.Writer, is_test bool) {\n\t\tfmt.Fprintf(w, \"//go:build %s\\n\", strings.Join(build_tags, \" \"))\n\t}\n\tasm := func(w io.Writer) {\n\t\tfmt.Fprintln(w, \"// Generated by generate.go do not edit\")\n\t\tfmt.Fprintln(w, \"// vim: ft=goasm\")\n\t\tbuild_tag(w, w == &ans.TestASMOutput)\n\t\tfmt.Fprintln(w, \"\\n#include \\\"go_asm.h\\\"\")\n\t\tfmt.Fprintln(w, \"#include \\\"textflag.h\\\"\")\n\t\tfmt.Fprintln(w)\n\t}\n\tasm(&ans.ASMOutput)\n\tasm(&ans.TestASMOutput)\n\n\tstub := func(w io.Writer) {\n\t\tfmt.Fprintln(w, \"// Generated by generate.go do not edit\")\n\t\tbuild_tag(w, w == &ans.TestStubOutput)\n\t\tfmt.Fprintln(w, \"\\npackage \"+package_name)\n\t\tfmt.Fprintln(w)\n\t}\n\tstub(&ans.StubOutput)\n\tstub(&ans.TestStubOutput)\n\treturn ans\n}\n\nfunc (s *State) OutputFunction() {\n\tif s.ActiveFunction == nil {\n\t\treturn\n\t}\n\tif strings.HasPrefix(s.ActiveFunction.Name, \"test_\") {\n\t\ts.ActiveFunction.OutputASM(&s.TestASMOutput)\n\t\ts.ActiveFunction.OutputStub(&s.TestStubOutput)\n\t} else {\n\t\ts.ActiveFunction.OutputASM(&s.ASMOutput)\n\t\ts.ActiveFunction.OutputStub(&s.StubOutput)\n\t}\n\ts.ActiveFunction = nil\n}\n\nfunc (s *State) NewFunction(name, description string, params, returns []FunctionParam) *Function {\n\ts.OutputFunction()\n\ts.ActiveFunction = NewFunction(s.ISA, name, description, params, returns)\n\ts.ActiveFunction.UsedRegisters = make(map[Register]bool)\n\treturn s.ActiveFunction\n}\n\nfunc (f *Function) load_vec_from_param(param string) Register {\n\tsrc := f.LoadParam(param)\n\tvec := f.Vec()\n\tf.LoadPointerUnaligned(src, vec)\n\tf.ReleaseReg(src)\n\treturn vec\n}\n\nfunc (f *Function) store_vec_in_param(vec Register, param string) {\n\tans := f.LoadParam(param)\n\tf.StoreUnalignedToPointer(vec, ans)\n\tf.ReleaseReg(ans)\n}\n\nfunc (s *State) test_load() {\n\tf := s.NewFunction(\"test_load_asm\", \"Test loading of vector register\", []FunctionParam{{\"src\", ByteSlice}, {\"ans\", ByteSlice}}, nil)\n\tif !s.ISA.HasSIMD {\n\t\treturn\n\t}\n\tvec := f.load_vec_from_param(\"src\")\n\tf.store_vec_in_param(vec, `ans`)\n}\n\nfunc (s *State) test_set1_epi8() {\n\tf := s.NewFunction(\"test_set1_epi8_asm\", \"Test broadcast of byte into vector\", []FunctionParam{{\"b\", types.Byte}, {\"ans\", ByteSlice}}, nil)\n\tif !s.ISA.HasSIMD {\n\t\treturn\n\t}\n\tvec := f.Vec()\n\tr := f.LoadParam(\"b\")\n\tq := f.Reg()\n\tf.SetRegisterTo(q, int(' '))\n\tf.JumpIfEqual(r, q, \"space\")\n\tf.SetRegisterTo(q, 11)\n\tf.JumpIfEqual(r, q, \"eleven\")\n\tf.Set1Epi8(\"b\", vec)\n\tf.store_vec_in_param(vec, `ans`)\n\tf.Return()\n\tf.Label(\"space\")\n\tf.Set1Epi8(int(' '), vec)\n\tf.store_vec_in_param(vec, `ans`)\n\tf.Return()\n\tf.Label(\"eleven\")\n\tf.Set1Epi8(-1, vec)\n\tf.store_vec_in_param(vec, `ans`)\n\tf.Return()\n\n}\n\nfunc (s *State) test_cmpeq_epi8() {\n\tf := s.NewFunction(\"test_cmpeq_epi8_asm\", \"Test byte comparison of two vectors\", []FunctionParam{{\"a\", ByteSlice}, {\"b\", ByteSlice}, {\"ans\", ByteSlice}}, nil)\n\tif !s.ISA.HasSIMD {\n\t\treturn\n\t}\n\ta := f.load_vec_from_param(\"a\")\n\tb := f.load_vec_from_param(\"b\")\n\tf.CmpEqEpi8(a, b, a)\n\tf.store_vec_in_param(a, \"ans\")\n}\n\nfunc (s *State) test_cmplt_epi8() {\n\tf := s.NewFunction(\n\t\t\"test_cmplt_epi8_asm\", \"Test byte comparison of two vectors\", []FunctionParam{{\"a\", ByteSlice}, {\"b\", ByteSlice}, {\"which\", types.Int}, {\"ans\", ByteSlice}}, nil)\n\tif !s.ISA.HasSIMD {\n\t\treturn\n\t}\n\twhich := f.LoadParam(\"which\")\n\ta := f.load_vec_from_param(\"a\")\n\tb := f.load_vec_from_param(\"b\")\n\tr := f.Reg()\n\tf.SetRegisterTo(r, 1)\n\tf.JumpIfEqual(which, r, \"one\")\n\tf.SetRegisterTo(r, 2)\n\tf.JumpIfEqual(which, r, \"two\")\n\tans := f.Vec()\n\tf.CmpLtEpi8(a, b, ans)\n\tf.store_vec_in_param(ans, \"ans\")\n\tf.Return()\n\tf.Label(\"one\")\n\tf.CmpLtEpi8(a, b, a)\n\tf.store_vec_in_param(a, \"ans\")\n\tf.Return()\n\tf.Label(\"two\")\n\tf.CmpLtEpi8(a, b, b)\n\tf.store_vec_in_param(b, \"ans\")\n\tf.Return()\n}\n\nfunc (s *State) test_or() {\n\tf := s.NewFunction(\"test_or_asm\", \"Test OR of two vectors\", []FunctionParam{{\"a\", ByteSlice}, {\"b\", ByteSlice}, {\"ans\", ByteSlice}}, nil)\n\tif !s.ISA.HasSIMD {\n\t\treturn\n\t}\n\ta := f.load_vec_from_param(\"a\")\n\tb := f.load_vec_from_param(\"b\")\n\tf.Or(a, b, a)\n\tf.store_vec_in_param(a, \"ans\")\n}\n\nfunc (s *State) test_jump_if_zero() {\n\tf := s.NewFunction(\"test_jump_if_zero_asm\", \"Test jump on zero register\", []FunctionParam{{\"a\", ByteSlice}}, []FunctionParam{{\"ans\", types.Int}})\n\tif !s.ISA.HasSIMD {\n\t\treturn\n\t}\n\ta := f.load_vec_from_param(\"a\")\n\tf.JumpIfZero(a, \"zero\")\n\tf.SetReturnValue(\"ans\", 1)\n\tf.Return()\n\tf.Label(\"zero\")\n\tf.SetReturnValue(\"ans\", 0)\n}\n\nfunc (s *State) test_count_to_match() {\n\tf := s.NewFunction(\"test_count_to_match_asm\", \"Test counting bytes to first match\", []FunctionParam{{\"a\", ByteSlice}, {\"b\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif !s.ISA.HasSIMD {\n\t\treturn\n\t}\n\ta := f.load_vec_from_param(\"a\")\n\tb := f.Vec()\n\tf.Set1Epi8(\"b\", b)\n\tf.CmpEqEpi8(a, b, b)\n\tf.JumpIfZero(b, \"fail\")\n\tres := f.Reg()\n\tf.CountBytesToFirstMatchDestructive(b, res)\n\tf.SetReturnValue(\"ans\", res)\n\tf.Return()\n\tf.Label(\"fail\")\n\tf.SetReturnValue(\"ans\", -1)\n}\n\nfunc (isa *ISA) LEA() string {\n\tif isa.GeneralPurposeRegisterSize == 32 {\n\t\treturn \"LEAL\"\n\t}\n\treturn \"LEAQ\"\n}\n\nfunc (s *State) index_func(f *Function, test_bytes func(bytes_to_test, test_ans Register)) {\n\tpos := f.Reg()\n\ttest_ans := f.Vec()\n\tbytes_to_test := f.Vec()\n\tdata_start := f.LoadParam(`data`)\n\tlimit := f.LoadParamLen(`data`)\n\tf.JumpIfZero(limit, \"fail\")\n\tf.AddToSelf(limit, data_start)\n\tmask := f.Reg()\n\n\tvecsz := f.ISA.Bits / 8\n\tf.CopyRegister(data_start, pos)\n\n\tfunc() {\n\t\tunaligned_bytes := f.RegForShifts()\n\t\tdefer f.ReleaseReg(unaligned_bytes)\n\t\tf.CopyRegister(data_start, unaligned_bytes)\n\t\tf.AndSelf(unaligned_bytes, vecsz-1)\n\t\tf.SubtractFromSelf(pos, unaligned_bytes)\n\t\tf.Comment(fmt.Sprintf(\"%s is now aligned to a %d byte boundary so loading from it is safe\", pos, vecsz))\n\t\tf.LoadPointerAligned(pos, bytes_to_test)\n\t\ttest_bytes(bytes_to_test, test_ans)\n\t\tf.MaskForCountDestructive(test_ans, mask)\n\t\tf.Comment(\"We need to shift out the possible extra bytes at the start of the string caused by the unaligned read\")\n\t\tf.ShiftMaskRightDestructive(mask, unaligned_bytes)\n\t\tf.JumpIfZero(mask, \"loop_start\")\n\t\tf.CopyRegister(data_start, pos)\n\t\tf.JumpTo(\"byte_found_in_mask\")\n\t}()\n\n\tf.Comment(\"Now loop over aligned blocks\")\n\tf.Label(\"loop_start\")\n\tf.AddToSelf(pos, vecsz)\n\tf.JumpIfLessThanOrEqual(limit, pos, \"fail\")\n\tf.LoadPointerAligned(pos, bytes_to_test)\n\ttest_bytes(bytes_to_test, test_ans)\n\tf.JumpIfNonZero(test_ans, \"byte_found_in_vec\")\n\tf.JumpTo(\"loop_start\")\n\n\tf.Label(\"byte_found_in_vec\")\n\tf.MaskForCountDestructive(test_ans, mask)\n\tf.Comment(\"Get the result from\", mask, \"and return it\")\n\tf.Label(\"byte_found_in_mask\")\n\tf.CountLeadingZeroBytesInMask(mask, mask)\n\tf.AddToSelf(mask, pos)\n\tf.JumpIfLessThanOrEqual(limit, mask, \"fail\")\n\tf.SubtractFromSelf(mask, data_start)\n\tf.SetReturnValue(\"ans\", mask)\n\tf.Return()\n\tf.Label(\"fail\")\n\tf.SetReturnValue(\"ans\", -1)\n\tf.Return()\n}\n\nfunc (s *State) indexbyte2_body(f *Function) {\n\tb1 := f.Vec()\n\tb2 := f.Vec()\n\tf.Set1Epi8(\"b1\", b1)\n\tf.Set1Epi8(\"b2\", b2)\n\ttest_bytes := func(bytes_to_test, test_ans Register) {\n\t\tf.CmpEqEpi8(bytes_to_test, b1, test_ans)\n\t\tf.CmpEqEpi8(bytes_to_test, b2, bytes_to_test)\n\t\tf.Or(test_ans, bytes_to_test, test_ans)\n\t}\n\ts.index_func(f, test_bytes)\n}\n\nfunc (s *State) indexbyte2() {\n\tf := s.NewFunction(\"index_byte2_asm\", \"Find the index of either of two bytes\", []FunctionParam{{\"data\", ByteSlice}, {\"b1\", types.Byte}, {\"b2\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.indexbyte2_body(f)\n\t}\n\tf = s.NewFunction(\"index_byte2_string_asm\", \"Find the index of either of two bytes\", []FunctionParam{{\"data\", types.String}, {\"b1\", types.Byte}, {\"b2\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.indexbyte2_body(f)\n\t}\n\n}\n\nfunc (s *State) indexbyte_body(f *Function) {\n\tb := f.Vec()\n\tf.Set1Epi8(\"b\", b)\n\ttest_bytes := func(bytes_to_test, test_ans Register) {\n\t\tf.CmpEqEpi8(bytes_to_test, b, test_ans)\n\t}\n\ts.index_func(f, test_bytes)\n}\n\nfunc (s *State) indexbyte() {\n\tf := s.NewFunction(\"index_byte_asm\", \"Find the index of a byte\", []FunctionParam{{\"data\", ByteSlice}, {\"b\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.indexbyte_body(f)\n\t}\n\tf = s.NewFunction(\"index_byte_string_asm\", \"Find the index of a byte\", []FunctionParam{{\"data\", types.String}, {\"b\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.indexbyte_body(f)\n\t}\n\n}\n\nfunc (s *State) indexc0_body(f *Function) {\n\tlower := f.Vec()\n\tupper := f.Vec()\n\tdel := f.Vec()\n\tf.Set1Epi8(-1, lower)\n\tf.Set1Epi8(int(' '), upper)\n\tf.Set1Epi8(0x7f, del)\n\n\ttest_bytes := func(bytes_to_test, test_ans Register) {\n\t\ttemp := f.Vec()\n\t\tdefer f.ReleaseReg(temp)\n\t\tf.CmpEqEpi8(bytes_to_test, del, test_ans)\n\t\tf.CmpLtEpi8(bytes_to_test, upper, temp)\n\t\tf.CmpGtEpi8(bytes_to_test, lower, bytes_to_test)\n\t\tf.And(temp, bytes_to_test, bytes_to_test)\n\t\tf.Or(test_ans, bytes_to_test, test_ans)\n\t}\n\ts.index_func(f, test_bytes)\n}\n\nfunc (s *State) indexc0() {\n\tf := s.NewFunction(\"index_c0_asm\", \"Find the index of a C0 control code\", []FunctionParam{{\"data\", ByteSlice}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.indexc0_body(f)\n\t}\n\tf = s.NewFunction(\"index_c0_string_asm\", \"Find the index of a C0 control code\", []FunctionParam{{\"data\", types.String}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.indexc0_body(f)\n\t}\n\n}\n\nfunc (s *State) not_index_byte_body(f *Function) {\n\tb := f.Vec()\n\tf.Set1Epi8(\"b\", b)\n\ttest_bytes := func(bytes_to_test, test_ans Register) {\n\t\tf.CmpEqEpi8(bytes_to_test, b, test_ans)\n\t\tf.NotSelf(test_ans)\n\t}\n\ts.index_func(f, test_bytes)\n}\n\nfunc (s *State) not_index_byte() {\n\tf := s.NewFunction(\"not_index_byte_asm\", \"Find the index of the first byte that is not b\", []FunctionParam{{\"data\", ByteSlice}, {\"b\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.not_index_byte_body(f)\n\t}\n\tf = s.NewFunction(\"not_index_byte_string_asm\", \"Find the index of the first byte that is not b\", []FunctionParam{{\"data\", types.String}, {\"b\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.not_index_byte_body(f)\n\t}\n\n}\n\nfunc (s *State) not_index_byte2_body(f *Function) {\n\tb1 := f.Vec()\n\tb2 := f.Vec()\n\tf.Set1Epi8(\"b1\", b1)\n\tf.Set1Epi8(\"b2\", b2)\n\ttest_bytes := func(bytes_to_test, test_ans Register) {\n\t\tf.CmpEqEpi8(bytes_to_test, b1, test_ans)\n\t\tf.CmpEqEpi8(bytes_to_test, b2, bytes_to_test)\n\t\tf.Or(test_ans, bytes_to_test, test_ans)\n\t\tf.NotSelf(test_ans)\n\t}\n\ts.index_func(f, test_bytes)\n}\n\nfunc (s *State) not_index_byte2() {\n\tf := s.NewFunction(\"not_index_byte2_asm\", \"Find the index of the first byte that is neither b1 nor b2\", []FunctionParam{{\"data\", ByteSlice}, {\"b1\", types.Byte}, {\"b2\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.not_index_byte2_body(f)\n\t}\n\tf = s.NewFunction(\"not_index_byte2_string_asm\", \"Find the index of the first byte that is neither b1 nor b2\", []FunctionParam{{\"data\", types.String}, {\"b1\", types.Byte}, {\"b2\", types.Byte}}, []FunctionParam{{\"ans\", types.Int}})\n\tif s.ISA.HasSIMD {\n\t\ts.not_index_byte2_body(f)\n\t}\n\n}\n\nfunc (s *State) Generate() {\n\ts.test_load()\n\ts.test_set1_epi8()\n\ts.test_cmpeq_epi8()\n\ts.test_cmplt_epi8()\n\ts.test_or()\n\ts.test_jump_if_zero()\n\ts.test_count_to_match()\n\n\ts.indexbyte2()\n\ts.indexc0()\n\ts.indexbyte()\n\ts.not_index_byte()\n\ts.not_index_byte2()\n\n\ts.OutputFunction()\n}\n\n// CLI {{{\nfunc exit(msg any) {\n\tfmt.Fprintf(os.Stderr, \"%s\\n\", msg)\n\tos.Exit(1)\n}\n\nfunc write_file(name, text string) {\n\tb := unsafe.Slice(unsafe.StringData(text), len(text))\n\tif existing, err := os.ReadFile(name); err == nil && bytes.Equal(existing, b) {\n\t\treturn\n\t}\n\tif err := os.WriteFile(name, b, 0664); err != nil {\n\t\texit(err)\n\t}\n}\n\nfunc do_one(s *State) {\n\ts.Generate()\n\n\tif s.ISA.HasSIMD {\n\t\twrite_file(fmt.Sprintf(\"asm_%d_%s_generated.s\", s.ISA.Bits, s.ISA.Goarch), s.ASMOutput.String())\n\t\twrite_file(fmt.Sprintf(\"asm_%d_%s_generated_test.s\", s.ISA.Bits, s.ISA.Goarch), s.TestASMOutput.String())\n\t}\n\twrite_file(fmt.Sprintf(\"asm_%d_%s_generated.go\", s.ISA.Bits, s.ISA.Goarch), s.StubOutput.String())\n\twrite_file(fmt.Sprintf(\"asm_%d_%s_generated_test.go\", s.ISA.Bits, s.ISA.Goarch), s.TestStubOutput.String())\n}\n\nfunc create_isa(arch Arch, bits int) ISA {\n\tswitch arch {\n\tcase AMD64:\n\t\treturn CreateAMD64ISA(bits)\n\tcase ARM64:\n\t\treturn CreateARM64ISA(bits)\n\t}\n\tpanic(\"Unknown ISA arch\")\n}\n\nfunc main() {\n\toutput_dir, err := os.Getwd()\n\tif err != nil {\n\t\texit(err)\n\t}\n\tif len(os.Args) > 1 {\n\t\tif output_dir, err = filepath.Abs(os.Args[len(os.Args)-1]); err != nil {\n\t\t\texit(err)\n\t\t}\n\t}\n\tif err = os.MkdirAll(output_dir, 0755); err != nil {\n\t\texit(err)\n\t}\n\tif err = os.Chdir(output_dir); err != nil {\n\t\texit(err)\n\t}\n\tpackage_name = filepath.Base(output_dir)\n\tsimd_arches := []Arch{AMD64, ARM64}\n\ta := make([]string, len(simd_arches))\n\tfor i, arch := range simd_arches {\n\t\ta[i] = string(arch)\n\t}\n\tno_simd_build_tag := fmt.Sprintf(\"!(%s)\", strings.Join(a, \"||\"))\n\n\tfor _, bits := range []int{128, 256} {\n\t\tfor _, arch := range simd_arches {\n\t\t\ts := NewState(create_isa(arch, bits))\n\t\t\tfmt.Fprintf(&s.StubOutput, \"const HasSIMD%dCode = %#v\\n\", bits, s.ISA.HasSIMD)\n\t\t\tdo_one(s)\n\t\t}\n\t\ts := NewState(CreateAMD64ISA(bits), no_simd_build_tag)\n\t\ts.ISA.HasSIMD = false\n\t\tfmt.Fprintf(&s.StubOutput, \"const HasSIMD%dCode = false\\n\", bits)\n\t\ts.Generate()\n\t\twrite_file(fmt.Sprintf(\"asm_other_%d_generated.go\", bits), s.StubOutput.String())\n\t\twrite_file(fmt.Sprintf(\"asm_other_%d_generated_test.go\", bits), s.TestStubOutput.String())\n\t}\n}\n\n// }}}\n"
  },
  {
    "path": "tools/simdstring/generate.sh",
    "content": "#!/bin/sh\n#\n# generate.sh\n# Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n#\n# Distributed under terms of the MIT license.\n#\ngo run generate.go && GOARCH=amd64 go vet ./... && GOARCH=arm64 go vet ./...\n"
  },
  {
    "path": "tools/simdstring/intrinsics.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage simdstring\n\nimport (\n\t\"runtime\"\n\n\t\"golang.org/x/sys/cpu\"\n)\n\nvar Have128bit = false\nvar Have256bit = false\nvar VectorSize = 1\n\n// Return the index at which b first occurs in data. If not found -1 is returned.\nvar IndexByte func(data []byte, b byte) int = index_byte_scalar\n\n// Return the index at which b first occurs in text. If not found -1 is returned.\nvar IndexByteString func(text string, b byte) int = index_byte_string_scalar\n\n// Return the index at which either a or b first occurs in data. If neither is\n// found -1 is returned.\nvar IndexByte2 func(data []byte, a, b byte) int = index_byte2_scalar\n\n// Return the index at which either a or b first occurs in text. If neither is\n// found -1 is returned.\nvar IndexByte2String func(text string, a, b byte) int = index_byte2_string_scalar\n\n// Return the index at which the first C0 byte is found or -1 when no such bytes are present.\nvar IndexC0 func(data []byte) int = index_c0_scalar\n\n// Return the index at which the first C0 byte is found or -1 when no such bytes are present.\nvar IndexC0String func(data string) int = index_c0_string_scalar\n\n// Return the index of the first byte in data that is not equal to b. If all bytes equal b, -1 is returned.\nvar NotIndexByte func(data []byte, b byte) int = not_index_byte_scalar\n\n// Return the index of the first byte in text that is not equal to b. If all bytes equal b, -1 is returned.\nvar NotIndexByteString func(text string, b byte) int = not_index_byte_string_scalar\n\n// Return the index of the first byte in data that is neither a nor b. If all bytes are a or b, -1 is returned.\nvar NotIndexByte2 func(data []byte, a, b byte) int = not_index_byte2_scalar\n\n// Return the index of the first byte in text that is neither a nor b. If all bytes are a or b, -1 is returned.\nvar NotIndexByte2String func(text string, a, b byte) int = not_index_byte2_string_scalar\n\nfunc init() {\n\tswitch runtime.GOARCH {\n\tcase \"amd64\":\n\t\tif cpu.Initialized {\n\t\t\tHave128bit = cpu.X86.HasSSE42 && HasSIMD128Code\n\t\t\tHave256bit = cpu.X86.HasAVX2 && HasSIMD256Code\n\t\t}\n\tcase \"arm64\":\n\t\tHave128bit = HasSIMD128Code\n\t\tHave256bit = HasSIMD256Code\n\t}\n\tif Have256bit {\n\t\tIndexByte = index_byte_asm_256\n\t\tIndexByteString = index_byte_string_asm_256\n\t\tIndexByte2 = index_byte2_asm_256\n\t\tIndexByte2String = index_byte2_string_asm_256\n\t\tIndexC0 = index_c0_asm_256\n\t\tIndexC0String = index_c0_string_asm_256\n\t\tNotIndexByte = not_index_byte_asm_256\n\t\tNotIndexByteString = not_index_byte_string_asm_256\n\t\tNotIndexByte2 = not_index_byte2_asm_256\n\t\tNotIndexByte2String = not_index_byte2_string_asm_256\n\t\tVectorSize = 32\n\t} else if Have128bit {\n\t\tIndexByte = index_byte_asm_128\n\t\tIndexByteString = index_byte_string_asm_128\n\t\tIndexByte2 = index_byte2_asm_128\n\t\tIndexByte2String = index_byte2_string_asm_128\n\t\tIndexC0 = index_c0_asm_128\n\t\tIndexC0String = index_c0_string_asm_128\n\t\tNotIndexByte = not_index_byte_asm_128\n\t\tNotIndexByteString = not_index_byte_string_asm_128\n\t\tNotIndexByte2 = not_index_byte2_asm_128\n\t\tNotIndexByte2String = not_index_byte2_string_asm_128\n\t\tVectorSize = 16\n\t}\n}\n"
  },
  {
    "path": "tools/simdstring/intrinsics_test.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage simdstring\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc test_load(src []byte) []byte {\n\tans := make([]byte, len(src))\n\tif len(src) == 16 {\n\t\ttest_load_asm_128(src, ans)\n\t} else {\n\t\ttest_load_asm_256(src, ans)\n\t}\n\treturn ans\n}\n\nfunc test_set1_epi8(b byte, sz int) []byte {\n\tans := make([]byte, sz)\n\tif sz == 16 {\n\t\ttest_set1_epi8_asm_128(b, ans)\n\t} else {\n\t\ttest_set1_epi8_asm_256(b, ans)\n\t}\n\treturn ans\n}\n\nfunc test_cmpeq_epi8(a, b []byte) []byte {\n\tans := make([]byte, len(a))\n\tif len(ans) == 16 {\n\t\ttest_cmpeq_epi8_asm_128(a, b, ans)\n\t} else {\n\t\ttest_cmpeq_epi8_asm_256(a, b, ans)\n\t}\n\treturn ans\n}\n\nfunc test_cmplt_epi8(t *testing.T, a, b []byte) []byte {\n\tans := make([]byte, len(a))\n\tvar prev []byte\n\tfor which := range 3 {\n\t\tif len(ans) == 16 {\n\t\t\ttest_cmplt_epi8_asm_128(a, b, which, ans)\n\t\t} else {\n\t\t\ttest_cmplt_epi8_asm_256(a, b, which, ans)\n\t\t}\n\t\tif prev != nil {\n\t\t\tif s := cmp.Diff(prev, ans); s != \"\" {\n\t\t\t\tt.Fatalf(\"cmplt returned different result for which=%d\\n%s\", which, s)\n\t\t\t}\n\t\t}\n\t\tprev = bytes.Clone(ans)\n\t}\n\treturn ans\n}\n\nfunc test_or(a, b []byte) []byte {\n\tans := make([]byte, len(a))\n\tif len(ans) == 16 {\n\t\ttest_or_asm_128(a, b, ans)\n\t} else {\n\t\ttest_or_asm_256(a, b, ans)\n\t}\n\treturn ans\n}\n\nfunc test_jump_if_zero(a []byte) int {\n\tif len(a) == 16 {\n\t\treturn test_jump_if_zero_asm_128(a)\n\t}\n\treturn test_jump_if_zero_asm_256(a)\n}\n\nfunc test_count_to_match(a []byte, b byte) int {\n\tif len(a) == 16 {\n\t\treturn test_count_to_match_asm_128(a, b)\n\t}\n\treturn test_count_to_match_asm_256(a, b)\n}\n\nfunc ordered_bytes(size int) []byte {\n\tans := make([]byte, size)\n\tfor i := range ans {\n\t\tans[i] = byte(i)\n\t}\n\treturn ans\n}\n\nfunc broadcast_byte(b byte, size int) []byte {\n\treturn bytes.Repeat([]byte{b}, size)\n}\n\nfunc get_sizes(t *testing.T) []int {\n\tsizes := []int{}\n\tif Have128bit {\n\t\tsizes = append(sizes, 16)\n\t}\n\tif Have256bit {\n\t\tsizes = append(sizes, 32)\n\t}\n\n\tif len(sizes) == 0 {\n\t\tt.Skip(\"skipping as no SIMD available at runtime\")\n\t}\n\treturn sizes\n}\n\nfunc addressof_data(b []byte) uintptr {\n\treturn uintptr(unsafe.Pointer(&b[0]))\n}\n\nfunc memset(ans []byte, val byte) {\n\tfor i := range ans {\n\t\tans[i] = val\n\t}\n}\n\nfunc aligned_slice(sz, alignment int) ([]byte, []byte) {\n\tans := make([]byte, sz+alignment+512)\n\ta := addressof_data(ans)\n\ta &= uintptr(alignment - 1)\n\textra := uintptr(alignment) - a\n\tmemset(ans, '<')\n\tmemset(ans[extra+uintptr(sz):], '>')\n\treturn ans[extra : extra+uintptr(sz)], ans\n}\n\nfunc TestSIMDStringOps(t *testing.T) {\n\tsizes := get_sizes(t)\n\ttest := func(haystack []byte, a, b byte, align_offset int) {\n\t\tvar actual int\n\t\tsh, _ := aligned_slice(len(haystack)+align_offset, 64)\n\t\tsh = sh[align_offset:]\n\t\tcopy(sh, haystack)\n\t\thaystack = sh\n\t\texpected := index_byte2_scalar(haystack, a, b)\n\n\t\tfor _, sz := range sizes {\n\t\t\tswitch sz {\n\t\t\tcase 16:\n\t\t\t\tactual = index_byte2_asm_128(haystack, a, b)\n\t\t\tcase 32:\n\t\t\t\tactual = index_byte2_asm_256(haystack, a, b)\n\t\t\t}\n\t\t\tif actual != expected {\n\t\t\t\tt.Fatalf(\"Failed to find '%c' or '%c' in: %#v at align: %d (expected: %d != actual: %d) at size: %d\",\n\t\t\t\t\ta, b, string(haystack), addressof_data(haystack)&uintptr(sz-1), expected, actual, sz)\n\t\t\t}\n\t\t}\n\n\t}\n\t// test alignment issues\n\tq := []byte(\"abc\")\n\tfor sz := range 32 {\n\t\ttest(q, '<', '>', sz)\n\t\ttest(q, ' ', 'b', sz)\n\t\ttest(q, '<', 'a', sz)\n\t\ttest(q, '<', 'b', sz)\n\t\ttest(q, 'c', '>', sz)\n\t}\n\n\ttests := func(h string, a, b byte) {\n\t\tfor _, sz := range []int{0, 16, 32, 64, 79} {\n\t\t\tq := strings.Repeat(\" \", sz) + h\n\t\t\tfor sz := range 32 {\n\t\t\t\ttest([]byte(q), a, b, sz)\n\t\t\t}\n\t\t}\n\t}\n\ttest(nil, '<', '>', 1)\n\ttest([]byte{}, '<', '>', 1)\n\n\ttests(\"\", '<', '>')\n\ttests(\"a\", 0, 0)\n\ttests(\"a\", '<', '>')\n\ttests(\"dsdfsfa\", '1', 'a')\n\ttests(\"xa\", 'a', 'a')\n\ttests(\"bbb\", 'a', '1')\n\ttests(\"bba\", 'a', '<')\n\ttests(\"baa\", '>', 'a')\n\n\tc0test := func(haystack []byte) {\n\t\tvar actual int\n\t\tsafe_haystack := append(bytes.Repeat([]byte{'<'}, 64), haystack...)\n\t\tsafe_haystack = append(safe_haystack, bytes.Repeat([]byte{'>'}, 64)...)\n\t\thaystack = safe_haystack[64 : 64+len(haystack)]\n\t\texpected := index_c0_scalar(haystack)\n\n\t\tfor _, sz := range sizes {\n\t\t\tswitch sz {\n\t\t\tcase 16:\n\t\t\t\tactual = index_c0_asm_128(haystack)\n\t\t\tcase 32:\n\t\t\t\tactual = index_c0_asm_256(haystack)\n\t\t\t}\n\t\t\tif actual != expected {\n\t\t\t\tt.Fatalf(\"C0 char index failed in: %#v (%d != %d) at size: %d\", string(haystack), expected, actual, sz)\n\t\t\t}\n\t\t}\n\n\t}\n\n\tc0tests := func(h string) {\n\t\tc0test([]byte(h))\n\t\tfor _, sz := range []int{16, 32, 64, 79} {\n\t\t\tq := strings.Repeat(\" \", sz) + h\n\t\t\tc0test([]byte(q))\n\t\t}\n\t}\n\n\tc0tests(\"a\\nfgdfgd\\r\")\n\tc0tests(\"\")\n\tc0tests(\"abcdef\")\n\tc0tests(\"afsgdfg\\x7f\")\n\tc0tests(\"afgd\\x1bfgd\\t\")\n\tc0tests(\"a\\x00\")\n\n\tindex_test := func(haystack []byte, needle byte) {\n\t\tvar actual int\n\t\texpected := index_byte_scalar(haystack, needle)\n\n\t\tfor _, sz := range sizes {\n\t\t\tswitch sz {\n\t\t\tcase 16:\n\t\t\t\tactual = index_byte_asm_128(haystack, needle)\n\t\t\tcase 32:\n\t\t\t\tactual = index_byte_asm_256(haystack, needle)\n\t\t\t}\n\t\t\tif actual != expected {\n\t\t\t\tt.Fatalf(\"index failed in: %#v (%d != %d) at size: %d with needle: %#v\", string(haystack), expected, actual, sz, needle)\n\t\t\t}\n\t\t}\n\t}\n\tindex_test([]byte(\"abc\"), 'x')\n\tindex_test([]byte(\"abc\"), 'b')\n\n\tnot_index_test := func(haystack []byte, needle byte) {\n\t\tvar actual int\n\t\texpected := not_index_byte_scalar(haystack, needle)\n\n\t\tfor _, sz := range sizes {\n\t\t\tswitch sz {\n\t\t\tcase 16:\n\t\t\t\tactual = not_index_byte_asm_128(haystack, needle)\n\t\t\tcase 32:\n\t\t\t\tactual = not_index_byte_asm_256(haystack, needle)\n\t\t\t}\n\t\t\tif actual != expected {\n\t\t\t\tt.Fatalf(\"not_index failed in: %#v (%d != %d) at size: %d with needle: %#v\", string(haystack), expected, actual, sz, needle)\n\t\t\t}\n\t\t}\n\t}\n\tnot_index_test(nil, 'a')\n\tnot_index_test([]byte{}, 'a')\n\tnot_index_test([]byte(\"aaa\"), 'a')\n\tnot_index_test([]byte(\"aaab\"), 'a')\n\tnot_index_test([]byte(\"baaa\"), 'a')\n\tnot_index_test([]byte(\"abc\"), 'a')\n\tfor _, sz := range []int{0, 16, 32, 64, 79} {\n\t\tq := strings.Repeat(\"a\", sz) + \"b\"\n\t\tnot_index_test([]byte(q), 'a')\n\t\tnot_index_test([]byte(q), 'b')\n\t\tnot_index_test([]byte(strings.Repeat(\"a\", sz)), 'a')\n\t}\n\n\tnot_index2_test := func(haystack []byte, a, b byte) {\n\t\tvar actual int\n\t\texpected := not_index_byte2_scalar(haystack, a, b)\n\n\t\tfor _, sz := range sizes {\n\t\t\tswitch sz {\n\t\t\tcase 16:\n\t\t\t\tactual = not_index_byte2_asm_128(haystack, a, b)\n\t\t\tcase 32:\n\t\t\t\tactual = not_index_byte2_asm_256(haystack, a, b)\n\t\t\t}\n\t\t\tif actual != expected {\n\t\t\t\tt.Fatalf(\"not_index2 failed in: %#v (%d != %d) at size: %d with needles: %#v %#v\", string(haystack), expected, actual, sz, a, b)\n\t\t\t}\n\t\t}\n\t}\n\tnot_index2_test(nil, 'a', 'b')\n\tnot_index2_test([]byte{}, 'a', 'b')\n\tnot_index2_test([]byte(\"aabb\"), 'a', 'b')\n\tnot_index2_test([]byte(\"aabbc\"), 'a', 'b')\n\tnot_index2_test([]byte(\"caabb\"), 'a', 'b')\n\tfor _, sz := range []int{0, 16, 32, 64, 79} {\n\t\tq := strings.Repeat(\"ab\", sz) + \"c\"\n\t\tnot_index2_test([]byte(q), 'a', 'b')\n\t\tnot_index2_test([]byte(strings.Repeat(\"ab\", sz)), 'a', 'b')\n\t\tfor align := range 32 {\n\t\t\tnot_index2_test([]byte(strings.Repeat(\" \", align)+q), 'a', 'b')\n\t\t}\n\t}\n\n}\n\nfunc TestIntrinsics(t *testing.T) {\n\tswitch runtime.GOARCH {\n\tcase \"amd64\":\n\t\tif !HasSIMD128Code {\n\t\t\tt.Fatal(\"SIMD 128bit code not built\")\n\t\t}\n\t\tif !HasSIMD256Code {\n\t\t\tt.Fatal(\"SIMD 256bit code not built\")\n\t\t}\n\tcase \"arm64\":\n\t\tif !HasSIMD128Code {\n\t\t\tt.Fatal(\"SIMD 128bit code not built\")\n\t\t}\n\t\tif !Have128bit {\n\t\t\tt.Fatal(\"SIMD 128bit support not available at runtime\")\n\t\t}\n\t}\n\tae := func(sz int, func_name string, a, b any) {\n\t\tif s := cmp.Diff(a, b); s != \"\" {\n\t\t\tt.Fatalf(\"%s failed with size: %d\\n%s\", func_name, sz, s)\n\t\t}\n\t}\n\ttests := []func(int){}\n\n\ttests = append(tests, func(sz int) {\n\t\ta := ordered_bytes(sz)\n\t\tae(sz, `load_test`, a, test_load(a))\n\t})\n\ttests = append(tests, func(sz int) {\n\t\tfor _, b := range []byte{1, 0b110111, 0xff, 0, ' '} {\n\t\t\tae(sz, `set1_epi8_test`, broadcast_byte(b, sz), test_set1_epi8(b, sz))\n\t\t}\n\t\tae(sz, `set1_epi8_test`, broadcast_byte(0xff, sz), test_set1_epi8(11, sz))\n\t})\n\ttests = append(tests, func(sz int) {\n\t\ta := ordered_bytes(sz)\n\t\tb := ordered_bytes(sz)\n\t\tans := test_cmpeq_epi8(a, b)\n\t\tae(sz, `cmpeq_epi8_test`, broadcast_byte(0xff, sz), ans)\n\n\t\tlt := func(a, b []byte) []byte {\n\t\t\tans := make([]byte, len(a))\n\t\t\tfor i := range ans {\n\t\t\t\tif int8(a[i]) < int8(b[i]) {\n\t\t\t\t\tans[i] = 0xff\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ans\n\t\t}\n\n\t\tae(sz, \"cmplt_epi8_test with equal vecs of non-negative numbers\", lt(a, b), test_cmplt_epi8(t, a, b))\n\t\ta = broadcast_byte(1, sz)\n\t\tb = broadcast_byte(2, sz)\n\t\tae(sz, \"cmplt_epi8_test with 1 and 2\", lt(a, b), test_cmplt_epi8(t, a, b))\n\t\tae(sz, \"cmplt_epi8_test with 2 and 1\", lt(b, a), test_cmplt_epi8(t, b, a))\n\t\ta = broadcast_byte(0xff, sz)\n\t\tb = broadcast_byte(0, sz)\n\t\tae(sz, \"cmplt_epi8_test with -1 and 0\", lt(a, b), test_cmplt_epi8(t, a, b))\n\t})\n\ttests = append(tests, func(sz int) {\n\t\ta := make([]byte, sz)\n\t\tb := make([]byte, sz)\n\t\tc := make([]byte, sz)\n\t\ta[0] = 0xff\n\t\tb[0] = 0xff\n\t\tb[1] = 0xff\n\t\ta[sz-1] = 1\n\t\tb[sz-1] = 2\n\t\tfor i := range c {\n\t\t\tc[i] = a[i] | b[i]\n\t\t}\n\t\tans := test_or(a, b)\n\t\tae(sz, `or_test`, c, ans)\n\t})\n\n\ttests = append(tests, func(sz int) {\n\t\ta := make([]byte, sz)\n\t\tif e := test_jump_if_zero(a); e != 0 {\n\t\t\tt.Fatalf(\"Did not detect zero register\")\n\t\t}\n\t\tfor i := range sz {\n\t\t\ta = make([]byte, sz)\n\t\t\ta[i] = 1\n\t\t\tif e := test_jump_if_zero(a); e != 1 {\n\t\t\t\tt.Fatalf(\"Did not detect non-zero register\")\n\t\t\t}\n\t\t}\n\t})\n\n\ttests = append(tests, func(sz int) {\n\t\ta := ordered_bytes(sz)\n\t\tif e := test_count_to_match(a, 77); e != -1 {\n\t\t\tt.Fatalf(\"Unexpectedly found byte at: %d\", e)\n\t\t}\n\t\tfor i := range sz {\n\t\t\tif e := test_count_to_match(a, byte(i)); e != i {\n\t\t\t\tt.Fatalf(\"Failed to find the byte: %d (%d != %d)\", i, i, e)\n\t\t\t}\n\t\t}\n\t\ta[7] = 0x34\n\t\tif e := test_count_to_match(a, 0x34); e != 7 {\n\t\t\tt.Fatalf(\"Failed to find the byte: %d (%d != %d)\", 0x34, 7, e)\n\t\t}\n\t})\n\n\tsizes := get_sizes(t)\n\tfor _, sz := range sizes {\n\t\tfor _, test := range tests {\n\t\t\ttest(sz)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "tools/simdstring/scalar.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage simdstring\n\nfunc index_byte_scalar(data []byte, b byte) int {\n\tfor i, ch := range data {\n\t\tif ch == b {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc index_byte_string_scalar(data string, b byte) int {\n\tfor i := 0; i < len(data); i++ {\n\t\tif data[i] == b {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc index_byte2_scalar(data []byte, a, b byte) int {\n\tfor i, ch := range data {\n\t\tswitch ch {\n\t\tcase a, b:\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc index_byte2_string_scalar(data string, a, b byte) int {\n\tfor i := 0; i < len(data); i++ {\n\t\tswitch data[i] {\n\t\tcase a, b:\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc index_c0_scalar(data []byte) int {\n\tfor i := range data {\n\t\tif data[i] == 0x7f || data[i] < ' ' {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc index_c0_string_scalar(data string) int {\n\tfor i := 0; i < len(data); i++ {\n\t\tif data[i] == 0x7f || data[i] < ' ' {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc not_index_byte_scalar(data []byte, b byte) int {\n\tfor i, ch := range data {\n\t\tif ch != b {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc not_index_byte_string_scalar(data string, b byte) int {\n\tfor i := 0; i < len(data); i++ {\n\t\tif data[i] != b {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc not_index_byte2_scalar(data []byte, a, b byte) int {\n\tfor i, ch := range data {\n\t\tif ch != a && ch != b {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc not_index_byte2_string_scalar(data string, a, b byte) int {\n\tfor i := 0; i < len(data); i++ {\n\t\tif data[i] != a && data[i] != b {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n"
  },
  {
    "path": "tools/simdstring/test.sh",
    "content": "#!/bin/sh\n#\n# test.sh\n# Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>\n#\n# Distributed under terms of the MIT license.\n#\n\necho -e \"\\x1b[32mtesting amd64\\x1b[m\" && go test -v &&\n    echo -e \"\\x1b[32mtesting arm64\\x1b[m\" && GOARCH=arm64 go test -v -exec qemu-aarch64-static\n\n\n"
  },
  {
    "path": "tools/themes/collection.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage themes\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"maps\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/subseq\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n)\n\nvar _ = fmt.Print\n\nvar AllColorSettingNames = map[string]bool{ // {{{\n\t// generated by gen-config.py do not edit\n\t// ALL_COLORS_START\n\t\"active_border_color\":                  true,\n\t\"active_tab_background\":                true,\n\t\"active_tab_foreground\":                true,\n\t\"background\":                           true,\n\t\"bell_border_color\":                    true,\n\t\"color0\":                               true,\n\t\"color1\":                               true,\n\t\"color10\":                              true,\n\t\"color100\":                             true,\n\t\"color101\":                             true,\n\t\"color102\":                             true,\n\t\"color103\":                             true,\n\t\"color104\":                             true,\n\t\"color105\":                             true,\n\t\"color106\":                             true,\n\t\"color107\":                             true,\n\t\"color108\":                             true,\n\t\"color109\":                             true,\n\t\"color11\":                              true,\n\t\"color110\":                             true,\n\t\"color111\":                             true,\n\t\"color112\":                             true,\n\t\"color113\":                             true,\n\t\"color114\":                             true,\n\t\"color115\":                             true,\n\t\"color116\":                             true,\n\t\"color117\":                             true,\n\t\"color118\":                             true,\n\t\"color119\":                             true,\n\t\"color12\":                              true,\n\t\"color120\":                             true,\n\t\"color121\":                             true,\n\t\"color122\":                             true,\n\t\"color123\":                             true,\n\t\"color124\":                             true,\n\t\"color125\":                             true,\n\t\"color126\":                             true,\n\t\"color127\":                             true,\n\t\"color128\":                             true,\n\t\"color129\":                             true,\n\t\"color13\":                              true,\n\t\"color130\":                             true,\n\t\"color131\":                             true,\n\t\"color132\":                             true,\n\t\"color133\":                             true,\n\t\"color134\":                             true,\n\t\"color135\":                             true,\n\t\"color136\":                             true,\n\t\"color137\":                             true,\n\t\"color138\":                             true,\n\t\"color139\":                             true,\n\t\"color14\":                              true,\n\t\"color140\":                             true,\n\t\"color141\":                             true,\n\t\"color142\":                             true,\n\t\"color143\":                             true,\n\t\"color144\":                             true,\n\t\"color145\":                             true,\n\t\"color146\":                             true,\n\t\"color147\":                             true,\n\t\"color148\":                             true,\n\t\"color149\":                             true,\n\t\"color15\":                              true,\n\t\"color150\":                             true,\n\t\"color151\":                             true,\n\t\"color152\":                             true,\n\t\"color153\":                             true,\n\t\"color154\":                             true,\n\t\"color155\":                             true,\n\t\"color156\":                             true,\n\t\"color157\":                             true,\n\t\"color158\":                             true,\n\t\"color159\":                             true,\n\t\"color16\":                              true,\n\t\"color160\":                             true,\n\t\"color161\":                             true,\n\t\"color162\":                             true,\n\t\"color163\":                             true,\n\t\"color164\":                             true,\n\t\"color165\":                             true,\n\t\"color166\":                             true,\n\t\"color167\":                             true,\n\t\"color168\":                             true,\n\t\"color169\":                             true,\n\t\"color17\":                              true,\n\t\"color170\":                             true,\n\t\"color171\":                             true,\n\t\"color172\":                             true,\n\t\"color173\":                             true,\n\t\"color174\":                             true,\n\t\"color175\":                             true,\n\t\"color176\":                             true,\n\t\"color177\":                             true,\n\t\"color178\":                             true,\n\t\"color179\":                             true,\n\t\"color18\":                              true,\n\t\"color180\":                             true,\n\t\"color181\":                             true,\n\t\"color182\":                             true,\n\t\"color183\":                             true,\n\t\"color184\":                             true,\n\t\"color185\":                             true,\n\t\"color186\":                             true,\n\t\"color187\":                             true,\n\t\"color188\":                             true,\n\t\"color189\":                             true,\n\t\"color19\":                              true,\n\t\"color190\":                             true,\n\t\"color191\":                             true,\n\t\"color192\":                             true,\n\t\"color193\":                             true,\n\t\"color194\":                             true,\n\t\"color195\":                             true,\n\t\"color196\":                             true,\n\t\"color197\":                             true,\n\t\"color198\":                             true,\n\t\"color199\":                             true,\n\t\"color2\":                               true,\n\t\"color20\":                              true,\n\t\"color200\":                             true,\n\t\"color201\":                             true,\n\t\"color202\":                             true,\n\t\"color203\":                             true,\n\t\"color204\":                             true,\n\t\"color205\":                             true,\n\t\"color206\":                             true,\n\t\"color207\":                             true,\n\t\"color208\":                             true,\n\t\"color209\":                             true,\n\t\"color21\":                              true,\n\t\"color210\":                             true,\n\t\"color211\":                             true,\n\t\"color212\":                             true,\n\t\"color213\":                             true,\n\t\"color214\":                             true,\n\t\"color215\":                             true,\n\t\"color216\":                             true,\n\t\"color217\":                             true,\n\t\"color218\":                             true,\n\t\"color219\":                             true,\n\t\"color22\":                              true,\n\t\"color220\":                             true,\n\t\"color221\":                             true,\n\t\"color222\":                             true,\n\t\"color223\":                             true,\n\t\"color224\":                             true,\n\t\"color225\":                             true,\n\t\"color226\":                             true,\n\t\"color227\":                             true,\n\t\"color228\":                             true,\n\t\"color229\":                             true,\n\t\"color23\":                              true,\n\t\"color230\":                             true,\n\t\"color231\":                             true,\n\t\"color232\":                             true,\n\t\"color233\":                             true,\n\t\"color234\":                             true,\n\t\"color235\":                             true,\n\t\"color236\":                             true,\n\t\"color237\":                             true,\n\t\"color238\":                             true,\n\t\"color239\":                             true,\n\t\"color24\":                              true,\n\t\"color240\":                             true,\n\t\"color241\":                             true,\n\t\"color242\":                             true,\n\t\"color243\":                             true,\n\t\"color244\":                             true,\n\t\"color245\":                             true,\n\t\"color246\":                             true,\n\t\"color247\":                             true,\n\t\"color248\":                             true,\n\t\"color249\":                             true,\n\t\"color25\":                              true,\n\t\"color250\":                             true,\n\t\"color251\":                             true,\n\t\"color252\":                             true,\n\t\"color253\":                             true,\n\t\"color254\":                             true,\n\t\"color255\":                             true,\n\t\"color26\":                              true,\n\t\"color27\":                              true,\n\t\"color28\":                              true,\n\t\"color29\":                              true,\n\t\"color3\":                               true,\n\t\"color30\":                              true,\n\t\"color31\":                              true,\n\t\"color32\":                              true,\n\t\"color33\":                              true,\n\t\"color34\":                              true,\n\t\"color35\":                              true,\n\t\"color36\":                              true,\n\t\"color37\":                              true,\n\t\"color38\":                              true,\n\t\"color39\":                              true,\n\t\"color4\":                               true,\n\t\"color40\":                              true,\n\t\"color41\":                              true,\n\t\"color42\":                              true,\n\t\"color43\":                              true,\n\t\"color44\":                              true,\n\t\"color45\":                              true,\n\t\"color46\":                              true,\n\t\"color47\":                              true,\n\t\"color48\":                              true,\n\t\"color49\":                              true,\n\t\"color5\":                               true,\n\t\"color50\":                              true,\n\t\"color51\":                              true,\n\t\"color52\":                              true,\n\t\"color53\":                              true,\n\t\"color54\":                              true,\n\t\"color55\":                              true,\n\t\"color56\":                              true,\n\t\"color57\":                              true,\n\t\"color58\":                              true,\n\t\"color59\":                              true,\n\t\"color6\":                               true,\n\t\"color60\":                              true,\n\t\"color61\":                              true,\n\t\"color62\":                              true,\n\t\"color63\":                              true,\n\t\"color64\":                              true,\n\t\"color65\":                              true,\n\t\"color66\":                              true,\n\t\"color67\":                              true,\n\t\"color68\":                              true,\n\t\"color69\":                              true,\n\t\"color7\":                               true,\n\t\"color70\":                              true,\n\t\"color71\":                              true,\n\t\"color72\":                              true,\n\t\"color73\":                              true,\n\t\"color74\":                              true,\n\t\"color75\":                              true,\n\t\"color76\":                              true,\n\t\"color77\":                              true,\n\t\"color78\":                              true,\n\t\"color79\":                              true,\n\t\"color8\":                               true,\n\t\"color80\":                              true,\n\t\"color81\":                              true,\n\t\"color82\":                              true,\n\t\"color83\":                              true,\n\t\"color84\":                              true,\n\t\"color85\":                              true,\n\t\"color86\":                              true,\n\t\"color87\":                              true,\n\t\"color88\":                              true,\n\t\"color89\":                              true,\n\t\"color9\":                               true,\n\t\"color90\":                              true,\n\t\"color91\":                              true,\n\t\"color92\":                              true,\n\t\"color93\":                              true,\n\t\"color94\":                              true,\n\t\"color95\":                              true,\n\t\"color96\":                              true,\n\t\"color97\":                              true,\n\t\"color98\":                              true,\n\t\"color99\":                              true,\n\t\"cursor\":                               true,\n\t\"cursor_text_color\":                    true,\n\t\"cursor_trail_color\":                   true,\n\t\"foreground\":                           true,\n\t\"inactive_border_color\":                true,\n\t\"inactive_tab_background\":              true,\n\t\"inactive_tab_foreground\":              true,\n\t\"macos_titlebar_color\":                 true,\n\t\"mark1_background\":                     true,\n\t\"mark1_foreground\":                     true,\n\t\"mark2_background\":                     true,\n\t\"mark2_foreground\":                     true,\n\t\"mark3_background\":                     true,\n\t\"mark3_foreground\":                     true,\n\t\"scrollbar_handle_color\":               true,\n\t\"scrollbar_track_color\":                true,\n\t\"selection_background\":                 true,\n\t\"selection_foreground\":                 true,\n\t\"tab_bar_background\":                   true,\n\t\"tab_bar_margin_color\":                 true,\n\t\"url_color\":                            true,\n\t\"visual_bell_color\":                    true,\n\t\"wayland_titlebar_color\":               true,\n\t\"window_title_bar_active_background\":   true,\n\t\"window_title_bar_active_foreground\":   true,\n\t\"window_title_bar_inactive_background\": true,\n\t\"window_title_bar_inactive_foreground\": true, // ALL_COLORS_END\n} // }}}\n\ntype JSONMetadata struct {\n\tEtag      string `json:\"etag\"`\n\tTimestamp string `json:\"timestamp\"`\n}\n\nvar ErrNoCacheFound = errors.New(\"No cache found and max cache age is negative\")\n\nfunc set_comment_in_zip_file(path string, comment string) error {\n\tsrc, err := zip.OpenReader(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer src.Close()\n\tbuf := bytes.Buffer{}\n\tdest := zip.NewWriter(&buf)\n\tif err = dest.SetComment(comment); err != nil {\n\t\treturn err\n\t}\n\tfor _, sf := range src.File {\n\t\terr = dest.Copy(sf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tdest.Close()\n\treturn utils.AtomicUpdateFile(path, bytes.NewReader(buf.Bytes()), 0o644)\n}\n\nfunc fetch_cached(name, url, cache_path string, max_cache_age time.Duration) (string, error) {\n\tcache_path = filepath.Join(cache_path, name+\".zip\")\n\tzf, err := zip.OpenReader(cache_path)\n\tif err != nil && !errors.Is(err, fs.ErrNotExist) {\n\t\treturn \"\", err\n\t}\n\n\tvar jm JSONMetadata\n\tif err == nil {\n\t\tdefer zf.Close()\n\t\tif err = json.Unmarshal(utils.UnsafeStringToBytes(zf.Comment), &jm); err == nil {\n\t\t\tif max_cache_age < 0 {\n\t\t\t\treturn cache_path, nil\n\t\t\t}\n\t\t\tcache_age, err := utils.ISO8601Parse(jm.Timestamp)\n\t\t\tif err == nil {\n\t\t\t\tif time.Now().Before(cache_age.Add(max_cache_age)) {\n\t\t\t\t\treturn cache_path, nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif max_cache_age < 0 {\n\t\treturn \"\", ErrNoCacheFound\n\t}\n\treq, err := http.NewRequest(http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif jm.Etag != \"\" {\n\t\treq.Header.Add(\"If-None-Match\", jm.Etag)\n\t}\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to download %s with error: %w\", url, err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tif resp.StatusCode == http.StatusNotModified {\n\t\t\tjm.Timestamp = utils.ISO8601Format(time.Now())\n\t\t\tcomment, _ := json.Marshal(jm)\n\t\t\terr = set_comment_in_zip_file(cache_path, utils.UnsafeBytesToString(comment))\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn cache_path, nil\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"Failed to download %s with HTTP error: %s\", url, resp.Status)\n\t}\n\tvar tf, tf2 *os.File\n\ttf, err = os.CreateTemp(filepath.Dir(cache_path), name+\".temp-*\")\n\tif err == nil {\n\t\ttf2, err = os.CreateTemp(filepath.Dir(cache_path), name+\".temp-*\")\n\t}\n\tdefer func() {\n\t\tif tf != nil {\n\t\t\ttf.Close()\n\t\t\tos.Remove(tf.Name())\n\t\t\ttf = nil\n\t\t}\n\t\tif tf2 != nil {\n\t\t\ttf2.Close()\n\t\t\tos.Remove(tf2.Name())\n\t\t\ttf2 = nil\n\t\t}\n\t}()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to create temp file in %s with error: %w\", filepath.Dir(cache_path), err)\n\t}\n\t_, err = io.Copy(tf, resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to download %s with error: %w\", url, err)\n\t}\n\tr, err := zip.OpenReader(tf.Name())\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to open downloaded zip file with error: %w\", err)\n\t}\n\tdefer r.Close()\n\tw := zip.NewWriter(tf2)\n\tjm.Etag = resp.Header.Get(\"ETag\")\n\tjm.Timestamp = utils.ISO8601Format(time.Now())\n\tcomment, _ := json.Marshal(jm)\n\tif err = w.SetComment(utils.UnsafeBytesToString(comment)); err != nil {\n\t\treturn \"\", err\n\t}\n\tfor _, file := range r.File {\n\t\terr = w.Copy(file)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"Failed to copy zip file from source to destination archive\")\n\t\t}\n\t}\n\terr = w.Close()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\ttf2.Close()\n\terr = os.Rename(tf2.Name(), cache_path)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to atomic rename temp file to %s with error: %w\", cache_path, err)\n\t}\n\ttf2 = nil\n\treturn cache_path, nil\n}\n\nfunc FetchCached(max_cache_age time.Duration) (string, error) {\n\treturn fetch_cached(\"kitty-themes\", \"https://codeload.github.com/kovidgoyal/kitty-themes/zip/master\", utils.CacheDir(), max_cache_age)\n}\n\ntype ThemeMetadata struct {\n\tName         string `json:\"name\"`\n\tFilepath     string `json:\"file\"`\n\tIs_dark      bool   `json:\"is_dark\"`\n\tNum_settings int    `json:\"num_settings\"`\n\tBlurb        string `json:\"blurb\"`\n\tLicense      string `json:\"license\"`\n\tUpstream     string `json:\"upstream\"`\n\tAuthor       string `json:\"author\"`\n}\n\nfunc ParseThemeMetadata(path string) (*ThemeMetadata, map[string]string, error) {\n\tvar in_metadata, in_blurb, finished_metadata bool\n\tans := ThemeMetadata{Is_dark: true} // the default background in kitty is dark\n\tsettings := map[string]string{}\n\n\tread_is_dark := func(key, val string) (err error) {\n\t\tsettings[key] = val\n\t\tif key == \"background\" {\n\t\t\tif val != \"\" {\n\t\t\t\tbg, err := style.ParseColor(val)\n\t\t\t\tif err == nil {\n\t\t\t\t\tans.Is_dark = utils.Max(bg.Red, bg.Green, bg.Blue) < 115\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\tread_metadata := func(line string) (err error) {\n\t\tis_block := strings.HasPrefix(line, \"## \")\n\t\tif in_metadata && !is_block {\n\t\t\tfinished_metadata = true\n\t\t}\n\t\tif finished_metadata {\n\t\t\treturn\n\t\t}\n\t\tif !in_metadata && is_block {\n\t\t\tin_metadata = true\n\t\t}\n\t\tif !in_metadata {\n\t\t\treturn\n\t\t}\n\t\tline = line[3:]\n\t\tif in_blurb {\n\t\t\tans.Blurb += \" \" + line\n\t\t\treturn\n\t\t}\n\t\tkey, val, found := strings.Cut(line, \":\")\n\t\tif !found {\n\t\t\treturn\n\t\t}\n\t\tkey = strings.TrimSpace(strings.ToLower(key))\n\t\tval = strings.TrimSpace(val)\n\t\tswitch key {\n\t\tcase \"name\":\n\t\t\tif val != \"The name of the theme (if not present, derived from filename)\" {\n\t\t\t\tans.Name = val\n\t\t\t}\n\t\tcase \"author\":\n\t\t\tans.Author = val\n\t\tcase \"upstream\":\n\t\t\tans.Upstream = val\n\t\tcase \"blurb\":\n\t\t\tans.Blurb = val\n\t\t\tin_blurb = true\n\t\tcase \"license\":\n\t\t\tans.License = val\n\t\t}\n\t\treturn\n\t}\n\tcp := config.ConfigParser{LineHandler: read_is_dark, CommentsHandler: read_metadata}\n\terr := cp.ParseFiles(path)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tans.Num_settings = len(settings)\n\treturn &ans, settings, nil\n}\n\ntype Theme struct {\n\tmetadata *ThemeMetadata\n\n\tcode                        string\n\tsettings                    map[string]string\n\tzip_reader                  *zip.File\n\tis_user_defined             bool\n\tpath_for_user_defined_theme string\n}\n\nfunc (self *Theme) Name() string        { return self.metadata.Name }\nfunc (self *Theme) Author() string      { return self.metadata.Author }\nfunc (self *Theme) Blurb() string       { return self.metadata.Blurb }\nfunc (self *Theme) IsDark() bool        { return self.metadata.Is_dark }\nfunc (self *Theme) IsUserDefined() bool { return self.is_user_defined }\n\nfunc (self *Theme) load_code() (string, error) {\n\tif self.zip_reader != nil {\n\t\tf, err := self.zip_reader.Open()\n\t\tself.zip_reader = nil\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tdefer f.Close()\n\t\tdata, err := io.ReadAll(f)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tself.code = utils.UnsafeBytesToString(data)\n\t}\n\tif self.is_user_defined && self.path_for_user_defined_theme != \"\" && self.code == \"\" {\n\t\traw, err := os.ReadFile(self.path_for_user_defined_theme)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tself.code = utils.UnsafeBytesToString(raw)\n\t}\n\treturn self.code, nil\n}\n\nfunc (self *Theme) Code() (string, error) {\n\treturn self.load_code()\n}\n\nfunc (self *Theme) SaveInDir(dirpath string) (err error) {\n\tpath := filepath.Join(dirpath, self.Name()+\".conf\")\n\tcode, err := self.Code()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn utils.AtomicUpdateFile(path, bytes.NewReader(utils.UnsafeStringToBytes(code)), 0o644)\n}\n\nfunc (self *Theme) SaveInFile(config_dir, config_file_name string) (err error) {\n\t_ = os.MkdirAll(config_dir, 0o755)\n\tpath := filepath.Join(config_dir, config_file_name)\n\tcode, err := self.Code()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn utils.AtomicUpdateFile(path, bytes.NewReader(utils.UnsafeStringToBytes(code)), 0o644)\n}\n\nfunc (self *Theme) SaveInConf(config_dir, reload_in, config_file_name string) (err error) {\n\tif err = self.SaveInFile(config_dir, `current-theme.conf`); err != nil {\n\t\treturn err\n\t}\n\tconfpath := config_file_name\n\tif !filepath.IsAbs(config_file_name) {\n\t\tconfpath = filepath.Join(config_dir, config_file_name)\n\t}\n\tpatcher := config.Patcher{Write_backup: true}\n\tif _, err = patcher.Patch(\n\t\tconfpath, \"KITTY_THEME\", fmt.Sprintf(\"# %s\\ninclude current-theme.conf\", self.metadata.Name),\n\t\tutils.Keys(AllColorSettingNames)...); err != nil {\n\t\treturn\n\t}\n\tswitch reload_in {\n\tcase \"parent\":\n\t\tconfig.ReloadConfigInKitty(true)\n\tcase \"all\":\n\t\tconfig.ReloadConfigInKitty(false)\n\t}\n\treturn\n}\n\nfunc (self *Theme) Settings() (map[string]string, error) {\n\tif self.zip_reader != nil {\n\t\tcode, err := self.load_code()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tself.settings = make(map[string]string, 64)\n\t\tscanner := utils.NewLineScanner(code)\n\t\tfor scanner.Scan() {\n\t\t\tline := strings.TrimSpace(scanner.Text())\n\t\t\tif line != \"\" && line[0] != '#' {\n\t\t\t\tkey, val, found := strings.Cut(line, \" \")\n\t\t\t\tif found {\n\t\t\t\t\tself.settings[key] = val\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn self.settings, nil\n}\n\nfunc (self *Theme) AsEscapeCodes() (string, error) {\n\tsettings, err := self.Settings()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn ColorSettingsAsEscapeCodes(settings), nil\n}\n\nfunc ColorSettingsAsEscapeCodes(settings map[string]string) string {\n\tw := strings.Builder{}\n\tw.Grow(4096)\n\n\tset_color := func(i int, sharp string) {\n\t\tw.WriteByte(';')\n\t\tw.WriteString(strconv.Itoa(i))\n\t\tw.WriteByte(';')\n\t\tw.WriteString(sharp)\n\t}\n\n\tset_default_color := func(name, defval string, num loop.DefaultColor) {\n\t\tw.WriteString(\"\\033]\")\n\t\tdefer func() { w.WriteString(\"\\033\\\\\") }()\n\t\tval, found := settings[name]\n\t\tif !found {\n\t\t\tval = defval\n\t\t}\n\t\tif val != \"\" {\n\t\t\trgba, err := style.ParseColor(val)\n\t\t\tif err == nil {\n\t\t\t\tw.WriteString(strconv.Itoa(int(num)))\n\t\t\t\tw.WriteByte(';')\n\t\t\t\tw.WriteString(rgba.AsRGBSharp())\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tw.WriteByte('1')\n\t\tw.WriteString(strconv.Itoa(int(num)))\n\t}\n\tset_default_color(\"foreground\", style.DefaultColors.Foreground, loop.FOREGROUND)\n\tset_default_color(\"background\", style.DefaultColors.Background, loop.BACKGROUND)\n\tset_default_color(\"cursor\", style.DefaultColors.Cursor, loop.CURSOR)\n\tset_default_color(\"selection_background\", style.DefaultColors.SelectionBg, loop.SELECTION_BG)\n\tset_default_color(\"selection_foreground\", style.DefaultColors.SelectionFg, loop.SELECTION_FG)\n\n\tw.WriteString(\"\\033]4\")\n\tfor i := range 256 {\n\t\tkey := \"color\" + strconv.Itoa(i)\n\t\tval := settings[key]\n\t\tif val != \"\" {\n\t\t\trgba, err := style.ParseColor(val)\n\t\t\tif err == nil {\n\t\t\t\tset_color(i, rgba.AsRGBSharp())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\trgba := style.RGBA{}\n\t\trgba.FromRGB(style.ColorTable[i])\n\t\tset_color(i, rgba.AsRGBSharp())\n\t}\n\tw.WriteString(\"\\033\\\\\")\n\treturn w.String()\n}\n\ntype Themes struct {\n\tname_map  map[string]*Theme\n\tindex_map []string\n}\n\nfunc (self *Themes) Copy() *Themes {\n\tans := &Themes{name_map: make(map[string]*Theme, len(self.name_map)), index_map: slices.Clone(self.index_map)}\n\tmaps.Copy(ans.name_map, self.name_map)\n\treturn ans\n}\n\nvar camel_case_pat = sync.OnceValue(func() *regexp.Regexp {\n\treturn regexp.MustCompile(`([a-z])([A-Z])`)\n})\n\nfunc ThemeNameFromFileName(fname string) string {\n\tfname = fname[:len(fname)-len(path.Ext(fname))]\n\tfname = strings.ReplaceAll(fname, \"_\", \" \")\n\tfname = camel_case_pat().ReplaceAllString(fname, \"$1 $2\")\n\treturn strings.Join(utils.Map(strings.Title, strings.Split(fname, \" \")), \" \")\n}\n\nfunc (self *Themes) Len() int { return len(self.name_map) }\nfunc (self *Themes) At(x int) *Theme {\n\tif x >= len(self.index_map) || x < 0 {\n\t\treturn nil\n\t}\n\treturn self.name_map[self.index_map[x]]\n}\nfunc (self *Themes) Names() []string { return self.index_map }\n\nfunc (self *Themes) create_index_map() {\n\tself.index_map = utils.Keys(self.name_map)\n\tself.index_map = utils.StableSortWithKey(self.index_map, strings.ToLower)\n}\n\nfunc (self *Themes) Filtered(is_ok func(*Theme) bool) *Themes {\n\tthemes := utils.Filter(utils.Values(self.name_map), is_ok)\n\tans := Themes{name_map: make(map[string]*Theme, len(themes))}\n\tfor _, theme := range themes {\n\t\tans.name_map[theme.metadata.Name] = theme\n\t}\n\tans.create_index_map()\n\treturn &ans\n}\n\nfunc (self *Themes) AddFromFile(path string) (*Theme, error) {\n\tm, conf, err := ParseThemeMetadata(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif m.Name == \"\" {\n\t\tm.Name = ThemeNameFromFileName(filepath.Base(path))\n\t}\n\tt := Theme{metadata: m, is_user_defined: true, settings: conf, path_for_user_defined_theme: path}\n\tself.name_map[m.Name] = &t\n\treturn &t, nil\n\n}\n\nfunc (self *Themes) add_from_dir(dirpath string) error {\n\tentries, err := os.ReadDir(dirpath)\n\tif err != nil {\n\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\terr = nil\n\t\t}\n\t\treturn err\n\t}\n\tfor _, e := range entries {\n\t\tif !e.IsDir() && strings.HasSuffix(e.Name(), \".conf\") {\n\t\t\tpath := filepath.Join(dirpath, e.Name())\n\t\t\t// ignore files if they are the STDOUT of the current processes\n\t\t\t// allows using kitten theme --dump-theme name > ~/.config/kitty/themes/name.conf\n\t\t\tif utils.Samefile(path, os.Stdout) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, err = self.AddFromFile(path); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *Themes) add_from_zip_file(zippath string) (io.Closer, error) {\n\tr, err := zip.OpenReader(zippath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tname_map := make(map[string]*zip.File, len(r.File))\n\tvar themes []*ThemeMetadata\n\ttheme_dir := \"\"\n\tfor _, file := range r.File {\n\t\tname_map[file.Name] = file\n\t\tif path.Base(file.Name) == \"themes.json\" {\n\t\t\ttheme_dir = path.Dir(file.Name)\n\t\t\tfr, err := file.Open()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Error while opening %s from the ZIP file: %w\", file.Name, err)\n\t\t\t}\n\t\t\tdefer fr.Close()\n\t\t\traw, err := io.ReadAll(fr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Error while reading %s from the ZIP file: %w\", file.Name, err)\n\t\t\t}\n\t\t\terr = json.Unmarshal(raw, &themes)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Error while decoding %s: %w\", file.Name, err)\n\t\t\t}\n\t\t}\n\t}\n\tif theme_dir == \"\" {\n\t\treturn nil, fmt.Errorf(\"No themes.json found in ZIP file\")\n\t}\n\tfor _, theme := range themes {\n\t\tkey := path.Join(theme_dir, theme.Filepath)\n\t\tf := name_map[key]\n\t\tif f != nil {\n\t\t\tt := Theme{metadata: theme, zip_reader: f}\n\t\t\tself.name_map[theme.Name] = &t\n\t\t}\n\t}\n\treturn r, nil\n}\n\nfunc (self *Themes) ThemeByName(name string) *Theme {\n\tans := self.name_map[name]\n\tif ans == nil {\n\t\tq := strings.ToLower(name)\n\t\tfor k, t := range self.name_map {\n\t\t\tif strings.ToLower(k) == q {\n\t\t\t\treturn t\n\t\t\t}\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc match(expression string, items []string) []*subseq.Match {\n\tmatches := subseq.ScoreItems(expression, items, subseq.Options{Level1: \" \"})\n\tmatches = utils.StableSort(matches, func(a, b *subseq.Match) int {\n\t\tif b.Score < a.Score {\n\t\t\treturn -1\n\t\t}\n\t\tif b.Score > a.Score {\n\t\t\treturn 1\n\t\t}\n\t\treturn 0\n\t})\n\treturn matches\n}\n\nconst (\n\tMARK_BEFORE = \"\\033[33m\"\n\tMARK_AFTER  = \"\\033[39m\"\n)\n\nfunc (self *Themes) ApplySearch(expression string, marks ...string) []string {\n\tmark_before, mark_after := MARK_BEFORE, MARK_AFTER\n\tif len(marks) == 2 {\n\t\tmark_before, mark_after = marks[0], marks[1]\n\t}\n\tresults := utils.Filter(match(expression, self.index_map), func(x *subseq.Match) bool { return x.Score > 0 })\n\tname_map := make(map[string]*Theme, len(results))\n\tfor _, m := range results {\n\t\tname_map[m.Text] = self.name_map[m.Text]\n\t}\n\tself.name_map = name_map\n\tself.index_map = self.index_map[:0]\n\tans := make([]string, 0, len(results))\n\tfor _, m := range results {\n\t\ttext := m.Text\n\t\tpositions := m.Positions\n\t\tfor i := len(positions) - 1; i >= 0; i-- {\n\t\t\tp := positions[i]\n\t\t\ttext = text[:p] + mark_before + text[p:p+1] + mark_after + text[p+1:]\n\t\t}\n\t\tans = append(ans, text)\n\t\tself.index_map = append(self.index_map, m.Text)\n\t}\n\treturn ans\n}\n\nfunc LoadThemes(cache_age time.Duration) (ans *Themes, closer io.Closer, err error) {\n\tzip_path, err := FetchCached(cache_age)\n\tans = &Themes{name_map: make(map[string]*Theme)}\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif closer, err = ans.add_from_zip_file(zip_path); err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif err = ans.add_from_dir(filepath.Join(utils.ConfigDir(), \"themes\")); err != nil {\n\t\treturn nil, nil, err\n\t}\n\tans.create_index_map()\n\treturn ans, closer, nil\n}\n\nfunc ThemeFromFile(path string) (*Theme, error) {\n\tans := &Themes{name_map: make(map[string]*Theme)}\n\treturn ans.AddFromFile(path)\n}\n\nfunc GetThemeNames(cache_age time.Duration) (ans []string, err error) {\n\tthemes, closer, err := LoadThemes(cache_age)\n\tif err != nil {\n\t\tif errors.Is(err, ErrNoCacheFound) {\n\t\t\treturn []string{\"Default\"}, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closer.Close()\n\tfor name := range themes.name_map {\n\t\tans = append(ans, name)\n\t}\n\treturn\n}\n\nfunc CompleteThemes(completions *cli.Completions, word string, arg_num int) {\n\tnames, err := GetThemeNames(-1)\n\tif err == nil {\n\t\tmg := completions.AddMatchGroup(\"Themes\")\n\t\tfor _, theme_name := range names {\n\t\t\ttheme_name = strings.TrimSpace(theme_name)\n\t\t\tif theme_name != \"\" && strings.HasPrefix(theme_name, word) {\n\t\t\t\tmg.AddMatch(theme_name)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/themes/collection_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage themes\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestThemeCollections(t *testing.T) {\n\tfor fname, expected := range map[string]string{\n\t\t\"moose\":    \"Moose\",\n\t\t\"mooseCat\": \"Moose Cat\",\n\t\t\"a_bC\":     \"A B C\",\n\t} {\n\t\tactual := ThemeNameFromFileName(fname)\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Unexpected theme name for %s:\\n%s\", fname, diff)\n\t\t}\n\t}\n\n\ttdir := t.TempDir()\n\n\tpt := func(expected ThemeMetadata, lines ...string) {\n\t\tif err := os.WriteFile(filepath.Join(tdir, \"temp.conf\"), []byte(strings.Join(lines, \"\\n\")), 0o600); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tactual, _, err := ParseThemeMetadata(filepath.Join(tdir, \"temp.conf\"))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif diff := cmp.Diff(&expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to parse:\\n%s\\n\\n%s\", strings.Join(lines, \"\\n\"), diff)\n\t\t}\n\t}\n\tpt(ThemeMetadata{Name: \"XYZ\", Blurb: \"a b\", Author: \"A\", Is_dark: true, Num_settings: 2},\n\t\t\"# some crap\", \" \", \"## \", \"## author: A\", \"## name: XYZ\", \"## blurb: a\", \"## b\", \"\", \"color red\", \"background black\", \"include inc.conf\")\n\tif err := os.WriteFile(filepath.Join(tdir, \"inc.conf\"), []byte(\"background white\"), 0o600); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tpt(ThemeMetadata{Name: \"XYZ\", Blurb: \"a b\", Author: \"A\", Num_settings: 2},\n\t\t\"# some crap\", \" \", \"## \", \"## author: A\", \"## name: XYZ\", \"## blurb: a\", \"## b\", \"\", \"color red\", \"background black\", \"include inc.conf\")\n\n\tbuf := bytes.Buffer{}\n\tzw := zip.NewWriter(&buf)\n\tfw, _ := zw.Create(\"x/themes.json\")\n\tif _, err := fw.Write([]byte(`[\n    {\n        \"author\": \"X Y\",\n        \"blurb\": \"A dark color scheme for the kitty terminal.\",\n        \"file\": \"themes/Alabaster_Dark.conf\",\n        \"is_dark\": true,\n        \"license\": \"MIT\",\n        \"name\": \"Alabaster Dark\",\n        \"num_settings\": 30,\n        \"upstream\": \"https://xxx.com\"\n    },\n\t{\n\t\t\"name\": \"Empty\", \"file\": \"empty.conf\"\n\t}\n\t]`)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfw, _ = zw.Create(\"x/empty.conf\")\n\tif _, err := fw.Write([]byte(\"empty\")); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfw, _ = zw.Create(\"x/themes/Alabaster_Dark.conf\")\n\tif _, err := fw.Write([]byte(\"alabaster\")); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tzw.Close()\n\n\treceived_etag := \"\"\n\trequest_count := 0\n\tsend_count := 0\n\tcheck_etag := true\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\trequest_count++\n\t\treceived_etag = r.Header.Get(\"If-None-Match\")\n\t\tif check_etag && received_etag == `\"xxx\"` {\n\t\t\tw.WriteHeader(http.StatusNotModified)\n\t\t\treturn\n\t\t}\n\t\tsend_count++\n\t\tw.Header().Add(\"ETag\", `\"xxx\"`)\n\t\tw.Write(buf.Bytes())\n\t}))\n\tdefer ts.Close()\n\n\tif _, err := fetch_cached(\"test\", ts.URL, tdir, 0); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tr, err := zip.OpenReader(filepath.Join(tdir, \"test.zip\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar jm JSONMetadata\n\terr = json.Unmarshal([]byte(r.Comment), &jm)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif jm.Etag != `\"xxx\"` {\n\t\tt.Fatalf(\"Unexpected ETag: %#v\", jm.Etag)\n\t}\n\t_, err = fetch_cached(\"test\", ts.URL, tdir, time.Hour)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif request_count != 1 {\n\t\tt.Fatalf(\"Cached zip file was not used: %d\", request_count)\n\t}\n\t_, err = fetch_cached(\"test\", ts.URL, tdir, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif request_count != 2 {\n\t\tt.Fatalf(\"Cached zip file was incorrectly used: %d\", request_count)\n\t}\n\tif received_etag != `\"xxx\"` {\n\t\tt.Fatalf(\"Got invalid ETag: %#v\", received_etag)\n\t}\n\tif send_count != 1 {\n\t\tt.Fatalf(\"Cached zip file was incorrectly re-downloaded: %d\", send_count)\n\t}\n\tcheck_etag = false\n\t_, err = fetch_cached(\"test\", ts.URL, tdir, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif send_count != 2 {\n\t\tt.Fatalf(\"Cached zip file was incorrectly not re-downloaded. %d\", send_count)\n\t}\n\tcoll := Themes{name_map: map[string]*Theme{}}\n\tcloser, err := coll.add_from_zip_file(filepath.Join(tdir, \"test.zip\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer closer.Close()\n\tif code, err := coll.ThemeByName(\"Empty\").load_code(); code != \"empty\" {\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tt.Fatal(\"failed to load code for empty theme\")\n\t}\n\tif code, err := coll.ThemeByName(\"Alabaster Dark\").load_code(); code != \"alabaster\" {\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tt.Fatal(\"failed to load code for alabaster theme\")\n\t}\n}\n"
  },
  {
    "path": "tools/tty/tty.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tty\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nconst (\n\tTCSANOW   = 0\n\tTCSADRAIN = 1\n\tTCSAFLUSH = 2\n)\n\ntype Term struct {\n\tos_file *os.File\n\tstates  []unix.Termios\n}\n\nfunc eintr_retry_noret(f func() error) error {\n\tfor {\n\t\tqerr := f()\n\t\tif qerr == unix.EINTR {\n\t\t\tcontinue\n\t\t}\n\t\treturn qerr\n\t}\n}\n\nfunc eintr_retry_intret(f func() (int, error)) (int, error) {\n\tfor {\n\t\tq, qerr := f()\n\t\tif qerr == unix.EINTR {\n\t\t\tcontinue\n\t\t}\n\t\treturn q, qerr\n\t}\n}\n\nfunc IsTerminal(fd uintptr) bool {\n\tvar t unix.Termios\n\terr := eintr_retry_noret(func() error { return Tcgetattr(int(fd), &t) })\n\treturn err == nil\n}\n\ntype TermiosOperation func(t *unix.Termios)\n\nfunc get_vmin_and_vtime(d time.Duration) (uint8, uint8) {\n\tif d > 0 {\n\t\t// VTIME is expressed in terms of deciseconds\n\t\tvtimeDeci := d.Milliseconds() / 100\n\t\t// ensure valid range\n\t\tvtime := uint8(clamp(vtimeDeci, 1, 0xff))\n\t\treturn 0, vtime\n\t}\n\t// block indefinitely until we receive at least 1 byte\n\treturn 1, 0\n}\n\nfunc SetReadTimeout(d time.Duration) TermiosOperation {\n\tvmin, vtime := get_vmin_and_vtime(d)\n\treturn func(t *unix.Termios) {\n\t\tt.Cc[unix.VMIN] = vmin\n\t\tt.Cc[unix.VTIME] = vtime\n\t}\n}\n\nvar SetBlockingRead TermiosOperation = SetReadTimeout(0)\n\nvar SetNoCanonical TermiosOperation = func(t *unix.Termios) {\n\tt.Lflag &^= unix.ICANON\n}\n\nvar SetRaw TermiosOperation = func(t *unix.Termios) {\n\t// This attempts to replicate the behaviour documented for cfmakeraw in\n\t// the termios(3) manpage, as Go doesn't wrap cfmakeraw probably because its not in POSIX\n\tt.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON\n\tt.Oflag &^= unix.OPOST\n\tt.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN\n\tt.Cflag &^= unix.CSIZE | unix.PARENB\n\tt.Cflag |= unix.CS8\n\tt.Cc[unix.VMIN] = 1\n\tt.Cc[unix.VTIME] = 0\n}\nvar SetNoEcho TermiosOperation = func(t *unix.Termios) {\n\tt.Lflag &^= unix.ECHO\n}\n\nvar SetReadPassword TermiosOperation = func(t *unix.Termios) {\n\tt.Lflag &^= unix.ECHO\n\tt.Lflag |= unix.ISIG\n\tt.Lflag &^= unix.ICANON\n\tt.Iflag |= unix.ICRNL\n\tt.Cc[unix.VMIN] = 1\n\tt.Cc[unix.VTIME] = 0\n}\n\nfunc WrapTerm(fd int, name string, operations ...TermiosOperation) (self *Term, err error) {\n\tif name == \"\" {\n\t\tname = fmt.Sprintf(\"<fd: %d>\", fd)\n\t}\n\tos_file := os.NewFile(uintptr(fd), name)\n\tif os_file == nil {\n\t\treturn nil, os.ErrInvalid\n\t}\n\tself = &Term{os_file: os_file}\n\terr = self.ApplyOperations(TCSANOW, operations...)\n\tif err != nil {\n\t\tself.Close()\n\t\tself = nil\n\t}\n\treturn\n}\n\nfunc OpenTerm(name string, operations ...TermiosOperation) (self *Term, err error) {\n\tfd, err := eintr_retry_intret(func() (int, error) {\n\t\treturn unix.Open(name, unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666)\n\t})\n\tif err != nil {\n\t\treturn nil, &os.PathError{Op: \"open\", Path: name, Err: err}\n\t}\n\tself, err = WrapTerm(fd, name, operations...)\n\treturn\n}\n\nfunc OpenControllingTerm(operations ...TermiosOperation) (self *Term, err error) {\n\treturn OpenTerm(Ctermid(), operations...)\n}\n\nfunc (self *Term) Fd() int {\n\tif self.os_file == nil {\n\t\treturn -1\n\t}\n\treturn int(self.os_file.Fd())\n}\n\nfunc (self *Term) Close() error {\n\tif self.os_file == nil {\n\t\treturn nil\n\t}\n\terr := eintr_retry_noret(func() error { return self.os_file.Close() })\n\tself.os_file = nil\n\treturn err\n}\n\nfunc (self *Term) WasEchoOnOriginally() bool {\n\tif len(self.states) > 0 {\n\t\treturn self.states[0].Lflag&unix.ECHO != 0\n\t}\n\treturn false\n}\n\nfunc (self *Term) Tcgetattr(ans *unix.Termios) error {\n\treturn eintr_retry_noret(func() error { return Tcgetattr(self.Fd(), ans) })\n}\n\nfunc (self *Term) Tcsetattr(when uintptr, ans *unix.Termios) error {\n\treturn eintr_retry_noret(func() error { return Tcsetattr(self.Fd(), when, ans) })\n}\n\nfunc (self *Term) set_termios_attrs(when uintptr, modify func(*unix.Termios)) (err error) {\n\tvar state unix.Termios\n\tif err = self.Tcgetattr(&state); err != nil {\n\t\treturn\n\t}\n\tnew_state := state\n\tmodify(&new_state)\n\tif err = self.Tcsetattr(when, &new_state); err == nil {\n\t\tself.states = append(self.states, state)\n\t}\n\treturn\n}\n\nfunc (self *Term) ApplyOperations(when uintptr, operations ...TermiosOperation) (err error) {\n\tif len(operations) == 0 {\n\t\treturn\n\t}\n\treturn self.set_termios_attrs(when, func(t *unix.Termios) {\n\t\tfor _, op := range operations {\n\t\t\top(t)\n\t\t}\n\t})\n}\n\nfunc (self *Term) PopStateWhen(when uintptr) (err error) {\n\tif len(self.states) == 0 {\n\t\treturn nil\n\t}\n\tidx := len(self.states) - 1\n\tif err = self.Tcsetattr(when, &self.states[idx]); err == nil {\n\t\tself.states = self.states[:idx]\n\t}\n\treturn\n}\n\nfunc (self *Term) PopState() error {\n\treturn self.PopStateWhen(TCSAFLUSH)\n}\n\nfunc (self *Term) RestoreWhen(when uintptr) (err error) {\n\tif len(self.states) == 0 {\n\t\treturn nil\n\t}\n\tself.states = self.states[:1]\n\treturn self.PopStateWhen(when)\n}\n\nfunc (self *Term) Restore() error {\n\treturn self.RestoreWhen(TCSAFLUSH)\n}\n\nfunc (self *Term) RestoreAndClose() error {\n\t_ = self.Restore()\n\treturn self.Close()\n}\n\nfunc (self *Term) Suspend() (resume func() error, err error) {\n\tvar state unix.Termios\n\terr = self.Tcgetattr(&state)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(self.states) > 0 {\n\t\terr := self.Tcsetattr(TCSANOW, &self.states[0])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn func() error { return self.Tcsetattr(TCSANOW, &state) }, nil\n\n}\n\nfunc (self *Term) SuspendAndRun(callback func() error) error {\n\tresume, err := self.Suspend()\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = callback()\n\tif rerr := resume(); rerr != nil {\n\t\terr = rerr\n\t}\n\treturn err\n}\n\nfunc clamp(v, lo, hi int64) int64 {\n\tif v < lo {\n\t\treturn lo\n\t}\n\tif v > hi {\n\t\treturn hi\n\t}\n\treturn v\n}\n\nfunc (self *Term) ReadWithTimeout(b []byte, d time.Duration) (n int, err error) {\n\tvar read, write, in_err unix.FdSet\n\tpselect := func() (int, error) {\n\t\tread.Zero()\n\t\twrite.Zero()\n\t\tin_err.Zero()\n\t\tread.Set(self.Fd())\n\t\treturn utils.Select(self.Fd()+1, &read, &write, &in_err, d)\n\t}\n\tnum_ready, err := pselect()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif num_ready == 0 {\n\t\terr = os.ErrDeadlineExceeded\n\t\treturn 0, err\n\t}\n\tfor {\n\t\tn, err = self.Read(b)\n\t\tif errors.Is(err, unix.EINTR) {\n\t\t\tcontinue\n\t\t}\n\t\treturn n, err\n\t}\n}\n\nfunc is_temporary_read_error(err error) bool {\n\treturn errors.Is(err, unix.EINTR) || errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EWOULDBLOCK)\n}\n\nfunc (self *Term) Read(b []byte) (n int, err error) {\n\tfor {\n\t\tn, err = self.os_file.Read(b)\n\t\t// On macOS we get EAGAIN if another thread is writing to the tty at the same time\n\t\tif err != nil && is_temporary_read_error(err) && n <= 0 {\n\t\t\tcontinue\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc (self *Term) Write(b []byte) (int, error) {\n\treturn self.os_file.Write(b)\n}\n\nfunc is_temporary_error(err error) bool {\n\treturn errors.Is(err, unix.EINTR) || errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EWOULDBLOCK) || errors.Is(err, io.ErrShortWrite)\n}\n\nfunc (self *Term) WriteAll(b []byte) error {\n\tfor len(b) > 0 {\n\t\tn, err := self.os_file.Write(b)\n\t\tif err != nil && !is_temporary_error(err) {\n\t\t\treturn err\n\t\t}\n\t\tb = b[n:]\n\t}\n\treturn nil\n}\n\nfunc (self *Term) WriteAllString(s string) error {\n\treturn self.WriteAll(utils.UnsafeStringToBytes(s))\n}\n\nfunc (self *Term) WriteString(b string) (int, error) {\n\treturn self.os_file.WriteString(b)\n}\n\nfunc (self *Term) DebugPrintln(a ...any) {\n\tmsg := fmt.Appendln(nil, a...)\n\tconst limit = 2048\n\tencoded := make([]byte, limit*2)\n\tfor i := 0; i < len(msg); i += limit {\n\t\tend := min(i+limit, len(msg))\n\t\tchunk := msg[i:end]\n\t\tencoded = encoded[:cap(encoded)]\n\t\tbase64.StdEncoding.Encode(encoded, chunk)\n\t\t_, _ = self.WriteString(\"\\x1bP@kitty-print|\")\n\t\t_, _ = self.Write(encoded)\n\t\t_, _ = self.WriteString(\"\\x1b\\\\\")\n\t}\n}\n\nfunc GetSize(fd int) (*unix.Winsize, error) {\n\tfor {\n\t\tsz, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)\n\t\tif err != unix.EINTR {\n\t\t\treturn sz, err\n\t\t}\n\t}\n}\n\nfunc (self *Term) GetSize() (*unix.Winsize, error) {\n\treturn GetSize(self.Fd())\n}\n\n// go doesn't have a wrapper for ctermid()\nfunc Ctermid() string { return \"/dev/tty\" }\n\nvar KittyStdout = sync.OnceValue(func() *os.File {\n\tif fds := os.Getenv(`KITTY_STDIO_FORWARDED`); fds != \"\" {\n\t\tif fd, err := strconv.Atoi(fds); err == nil && fd > -1 {\n\t\t\tif f := os.NewFile(uintptr(fd), \"<kitty_stdout>\"); f != nil {\n\t\t\t\treturn f\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n})\n\nfunc DebugPrintln(a ...any) {\n\tif f := KittyStdout(); f != nil {\n\t\tfmt.Fprintln(f, a...)\n\t\treturn\n\t}\n\tterm, err := OpenControllingTerm()\n\tif err == nil {\n\t\tdefer term.Close()\n\t\tterm.DebugPrintln(a...)\n\t}\n}\n\nfunc ReadSingleByteFromTerminal() (b byte, err error) {\n\tterm, err := OpenControllingTerm(SetBlockingRead, SetNoCanonical)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer term.Close()\n\tans := []byte{b}\n\tfor {\n\t\tn, err := term.Read(ans)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif n > 0 {\n\t\t\treturn ans[0], nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/tty/tty_bsd.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n//go:build darwin || freebsd || openbsd || netbsd || dragonfly\n// +build darwin freebsd openbsd netbsd dragonfly\n\npackage tty\n\nimport (\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc Tcgetattr(fd int, argp *unix.Termios) error {\n\treturn unix.IoctlSetTermios(fd, unix.TIOCGETA, argp)\n}\n\nfunc Tcsetattr(fd int, opt uintptr, argp *unix.Termios) error {\n\tswitch opt {\n\tcase TCSANOW:\n\t\topt = unix.TIOCSETA\n\tcase TCSADRAIN:\n\t\topt = unix.TIOCSETAW\n\tcase TCSAFLUSH:\n\t\topt = unix.TIOCSETAF\n\tdefault:\n\t\treturn unix.EINVAL\n\t}\n\treturn unix.IoctlSetTermios(fd, uint(opt), argp)\n}\n"
  },
  {
    "path": "tools/tty/tty_linux.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tty\n\nimport \"golang.org/x/sys/unix\"\n\nconst (\n\tTCSETS  = 0x5402\n\tTCSETSW = 0x5403\n\tTCSETSF = 0x5404\n\tTCFLSH  = 0x540B\n\tTCSBRK  = 0x5409\n\tTCSBRKP = 0x5425\n\n\tIXON    = 0x00000400\n\tIXANY   = 0x00000800\n\tIXOFF   = 0x00001000\n\tCRTSCTS = 0x80000000\n)\n\nfunc Tcgetattr(fd int, argp *unix.Termios) error {\n\treturn unix.IoctlSetTermios(fd, unix.TCGETS, argp)\n}\n\nfunc Tcsetattr(fd int, action uintptr, argp *unix.Termios) error {\n\tvar request uint\n\tswitch action {\n\tcase TCSANOW:\n\t\trequest = TCSETS\n\tcase TCSADRAIN:\n\t\trequest = TCSETSW\n\tcase TCSAFLUSH:\n\t\trequest = TCSETSF\n\tdefault:\n\t\treturn unix.EINVAL\n\t}\n\treturn unix.IoctlSetTermios(fd, request, argp)\n}\n"
  },
  {
    "path": "tools/tui/dcs_to_kitty.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc DCSToKitty(msgtype, payload string) (string, error) {\n\tdata := base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(payload))\n\tans := \"\\x1bP@kitty-\" + msgtype + \"|\" + data\n\ttmux := TmuxSocketAddress()\n\tif tmux != \"\" {\n\t\terr := TmuxAllowPassthrough()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tans = \"\\033Ptmux;\\033\" + ans + \"\\033\\033\\\\\\033\\\\\"\n\t} else {\n\t\tans += \"\\033\\\\\"\n\t}\n\treturn ans, nil\n}\n"
  },
  {
    "path": "tools/tui/download_with_progress.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/humanize\"\n)\n\nvar _ = fmt.Print\n\ntype dl_data struct {\n\tmutex               sync.Mutex\n\tcanceled_by_user    bool\n\terror_from_download error\n\tdone, total         uint64\n\tdownload_started    bool\n\tdownload_finished   bool\n\ttemp_file_path      string\n}\n\ntype render_data struct {\n\tdone, total  uint64\n\tscreen_width int\n\tspinner      *Spinner\n\tstarted_at   time.Time\n}\n\nfunc render_without_total(rd *render_data) string {\n\treturn fmt.Sprint(rd.spinner.Tick(), humanize.Bytes(rd.done), \" downloaded so far. Started %s\", humanize.Time(rd.started_at))\n}\n\nfunc format_time(d time.Duration) string {\n\td = d.Round(time.Second)\n\tans := \"\"\n\tif d.Hours() > 1 {\n\t\th := d / time.Hour\n\t\td -= h * time.Hour\n\t\tans += fmt.Sprintf(\"%02d:\", h)\n\t}\n\tm := d / time.Minute\n\td -= m * time.Minute\n\ts := d / time.Second\n\treturn fmt.Sprintf(\"%s%02d:%02d\", ans, m, s)\n}\n\nfunc render_progress(rd *render_data) string {\n\tif rd.total == 0 {\n\t\treturn render_without_total(rd)\n\t}\n\tnow := time.Now()\n\tduration := now.Sub(rd.started_at)\n\trate := float64(rd.done) / float64(duration)\n\tfrac := float64(rd.done) / float64(rd.total)\n\tbytes_left := rd.total - rd.done\n\ttime_left := time.Duration(float64(bytes_left) / rate)\n\tspeed := rate * float64(time.Second)\n\tbefore := rd.spinner.Tick()\n\tafter := fmt.Sprintf(\" %d%% %s/s %s\", int(frac*100), strings.ReplaceAll(humanize.Bytes(uint64(speed)), \" \", \"\"), format_time(time_left))\n\tavailable_width := rd.screen_width - len(\"T  100% 1000 MB/s 11:11:11\")\n\t// fmt.Println(\"\\r\\n\", frac, available_width)\n\tprogress_bar := \"\"\n\tif available_width > 10 {\n\t\tprogress_bar = \" \" + RenderProgressBar(frac, available_width)\n\t}\n\treturn before + progress_bar + after\n}\n\nfunc DownloadFileWithProgress(destpath, url string, kill_if_signaled bool) (err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)\n\tif err != nil {\n\t\treturn\n\t}\n\tdl_data := dl_data{}\n\trd := render_data{spinner: NewSpinner(\"dots\"), started_at: time.Now()}\n\n\tregister_temp_file_path := func(path string) {\n\t\tdl_data.mutex.Lock()\n\t\tdl_data.temp_file_path = path\n\t\tdl_data.mutex.Unlock()\n\t}\n\n\treport_progress := func(done, total uint64) error {\n\t\tdl_data.mutex.Lock()\n\t\tdl_data.done = done\n\t\tdl_data.total = total\n\t\tcanceled := dl_data.canceled_by_user\n\t\tdl_data.mutex.Unlock()\n\t\tif canceled {\n\t\t\treturn Canceled\n\t\t}\n\t\tlp.WakeupMainThread()\n\t\treturn nil\n\t}\n\n\tdo_download := func() {\n\t\tlp.RecoverFromPanicInGoRoutine()\n\t\tdl_data.mutex.Lock()\n\t\tdl_data.download_started = true\n\t\tdl_data.mutex.Unlock()\n\t\terr := utils.DownloadToFile(destpath, url, report_progress, register_temp_file_path)\n\t\tdl_data.mutex.Lock()\n\t\tdl_data.download_finished = true\n\t\tif err != Canceled && err != nil {\n\t\t\tdl_data.error_from_download = err\n\t\t}\n\t\tdl_data.mutex.Unlock()\n\t\tlp.WakeupMainThread()\n\t}\n\n\tredraw := func() {\n\t\tlp.StartAtomicUpdate()\n\t\tlp.AllowLineWrapping(false)\n\t\tdefer func() {\n\t\t\tlp.AllowLineWrapping(true)\n\t\t\tlp.EndAtomicUpdate()\n\t\t}()\n\t\tlp.QueueWriteString(\"\\r\")\n\t\tlp.ClearToEndOfLine()\n\t\tdl_data.mutex.Lock()\n\t\trd.done, rd.total = dl_data.done, dl_data.total\n\t\tdl_data.mutex.Unlock()\n\t\tif rd.done+rd.total == 0 {\n\t\t\tlp.QueueWriteString(\"Waiting for download to start...\")\n\t\t} else {\n\t\t\tsz, err := lp.ScreenSize()\n\t\t\tw := sz.WidthCells\n\t\t\tif err != nil {\n\t\t\t\tw = 80\n\t\t\t}\n\t\t\trd.screen_width = int(w)\n\t\t\tlp.QueueWriteString(render_progress(&rd))\n\t\t}\n\t}\n\n\ton_timer_tick := func(timer_id loop.IdType) error {\n\t\treturn lp.OnWakeup()\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tif _, err = lp.AddTimer(rd.spinner.interval, true, on_timer_tick); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tgo do_download()\n\t\tlp.QueueWriteString(\"Downloading: \" + url + \"\\r\\n\")\n\t\treturn \"\\r\\n\", nil\n\t}\n\n\tlp.OnResumeFromStop = func() error {\n\t\tredraw()\n\t\treturn nil\n\t}\n\tlp.OnResize = func(old_size, new_size loop.ScreenSize) error {\n\t\tredraw()\n\t\treturn nil\n\t}\n\tlp.OnWakeup = func() error {\n\t\tdl_data.mutex.Lock()\n\t\terr := dl_data.error_from_download\n\t\tfinished := dl_data.download_finished\n\t\tdl_data.mutex.Unlock()\n\t\tif err != nil {\n\t\t\treturn dl_data.error_from_download\n\t\t}\n\t\tif finished {\n\t\t\tlp.Quit(0)\n\t\t\treturn nil\n\t\t}\n\t\tredraw()\n\t\treturn nil\n\t}\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"ctrl+c\") || event.MatchesPressOrRepeat(\"esc\") {\n\t\t\tevent.Handled = true\n\t\t\tdl_data.mutex.Lock()\n\t\t\tdl_data.canceled_by_user = true\n\t\t\tdl_data.mutex.Unlock()\n\t\t\treturn Canceled\n\t\t}\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tdl_data.mutex.Lock()\n\tif dl_data.temp_file_path != \"\" && !dl_data.download_finished {\n\t\tos.Remove(dl_data.temp_file_path)\n\t}\n\tdl_data.mutex.Unlock()\n\tif err != nil {\n\t\treturn\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tif kill_if_signaled {\n\t\t\tlp.KillIfSignalled()\n\t\t\treturn\n\t\t}\n\t\treturn &KilledBySignal{Msg: fmt.Sprint(\"Killed by signal: \", ds), SignalName: ds}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "tools/tui/graphics/collection.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage graphics\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/kitty/tools/tui\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\nvar _ = fmt.Print\n\ntype Size struct{ Width, Height int }\n\ntype rendering struct {\n\timg      *images.ImageData\n\timage_id uint32\n}\n\ntype temp_resource struct {\n\tpath string\n\tmmap shm.MMap\n}\n\nfunc (self *temp_resource) remove() {\n\tif self.path != \"\" {\n\t\tos.Remove(self.path)\n\t\tself.path = \"\"\n\t}\n\tif self.mmap != nil {\n\t\t_ = self.mmap.Unlink()\n\t\tself.mmap = nil\n\t}\n}\n\ntype Image struct {\n\tsrc struct {\n\t\tpath   string\n\t\tdata   *images.ImageData\n\t\tsize   Size\n\t\tloaded bool\n\t}\n\trenderings map[Size]*rendering\n\terr        error\n}\n\nfunc NewImage() *Image {\n\treturn &Image{\n\t\trenderings: make(map[Size]*rendering),\n\t}\n}\n\ntype ImageCollection struct {\n\tShm_supported, Files_supported      atomic.Bool\n\tdetection_file_id, detection_shm_id uint32\n\ttemp_file_map                       map[uint32]*temp_resource\n\trunning_in_tmux                     bool\n\n\tmutex            sync.Mutex\n\timage_id_counter uint32\n\n\timages map[string]*Image\n}\n\nvar ErrNotFound = errors.New(\"not found\")\n\nfunc (self *ImageCollection) GetSizeIfAvailable(key string, page_size Size) (Size, error) {\n\tif !self.mutex.TryLock() {\n\t\treturn Size{}, ErrNotFound\n\t}\n\tdefer self.mutex.Unlock()\n\timg := self.images[key]\n\tif img == nil {\n\t\treturn Size{}, ErrNotFound\n\t}\n\tans := img.renderings[page_size]\n\tif ans == nil {\n\t\tif img.err != nil {\n\t\t\treturn Size{}, img.err\n\t\t}\n\t\treturn Size{}, ErrNotFound\n\t}\n\treturn Size{ans.img.Width, ans.img.Height}, img.err\n}\n\nfunc (self *ImageCollection) ResolutionOf(key string) Size {\n\tif !self.mutex.TryLock() {\n\t\treturn Size{-1, -1}\n\t}\n\tdefer self.mutex.Unlock()\n\ti := self.images[key]\n\tif i == nil {\n\t\treturn Size{-2, -2}\n\t}\n\treturn i.src.size\n}\n\nfunc (self *ImageCollection) AddPaths(paths ...string) {\n\tself.mutex.Lock()\n\tdefer self.mutex.Unlock()\n\tfor _, path := range paths {\n\t\tif self.images[path] == nil {\n\t\t\ti := NewImage()\n\t\t\ti.src.path = path\n\t\t\tself.images[path] = i\n\t\t}\n\t}\n}\n\nfunc (self *Image) ResizeForPageSize(width, height int) {\n\tsz := Size{width, height}\n\tif self.renderings[sz] != nil {\n\t\treturn\n\t}\n\tfinal_width, final_height := images.FitImage(self.src.size.Width, self.src.size.Height, width, height)\n\tif final_width == self.src.size.Width && final_height == self.src.data.Height {\n\t\tself.renderings[sz] = &rendering{img: self.src.data}\n\t\treturn\n\t}\n\tx_frac, y_frac := float64(final_width)/float64(self.src.size.Width), float64(final_height)/float64(self.src.size.Height)\n\tself.renderings[sz] = &rendering{img: self.src.data.Resize(x_frac, y_frac)}\n}\n\nfunc (self *ImageCollection) ResizeForPageSize(width, height int) {\n\tself.mutex.Lock()\n\tdefer self.mutex.Unlock()\n\n\tctx := images.Context{}\n\tkeys := utils.Keys(self.images)\n\tif err := ctx.SafeParallel(0, len(keys), func(nums <-chan int) {\n\t\tfor i := range nums {\n\t\t\timg := self.images[keys[i]]\n\t\t\tif img.src.loaded && img.err == nil {\n\t\t\t\timg.ResizeForPageSize(width, height)\n\t\t\t}\n\t\t}\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc (self *ImageCollection) DeleteAllVisiblePlacements(lp *loop.Loop) {\n\tg := self.new_graphics_command()\n\tg.SetAction(GRT_action_delete).SetDelete(GRT_delete_visible)\n\t_ = g.WriteWithPayloadToLoop(lp, nil)\n}\n\nfunc (self *ImageCollection) PlaceImageSubRect(lp *loop.Loop, key string, page_size Size, left, top, width, height int) {\n\tself.mutex.Lock()\n\tdefer self.mutex.Unlock()\n\timg := self.images[key]\n\tif img == nil {\n\t\treturn\n\t}\n\tr := img.renderings[page_size]\n\tif r == nil {\n\t\treturn\n\t}\n\tif r.image_id == 0 {\n\t\tself.transmit_rendering(lp, r)\n\t}\n\tif width < 0 {\n\t\twidth = r.img.Width\n\t}\n\tif height < 0 {\n\t\theight = r.img.Height\n\t}\n\twidth = utils.Max(0, utils.Min(r.img.Width-left, width))\n\theight = utils.Max(0, utils.Min(r.img.Height-top, height))\n\tgc := self.new_graphics_command()\n\tgc.SetAction(GRT_action_display).SetLeftEdge(uint64(left)).SetTopEdge(uint64(top)).SetWidth(uint64(width)).SetHeight(uint64(height))\n\tgc.SetImageId(r.image_id).SetPlacementId(1).SetCursorMovement(GRT_cursor_static)\n\t_ = gc.WriteWithPayloadToLoop(lp, nil)\n}\n\nfunc (self *ImageCollection) Initialize(lp *loop.Loop) {\n\ttmux := tui.TmuxSocketAddress()\n\tif tmux != \"\" && tui.TmuxAllowPassthrough() == nil {\n\t\tself.running_in_tmux = true\n\t}\n\tif !self.running_in_tmux {\n\t\tg := func(t GRT_t, payload string) uint32 {\n\t\t\tself.image_id_counter++\n\t\t\tg1 := self.new_graphics_command()\n\t\t\tg1.SetTransmission(t).SetAction(GRT_action_query).SetImageId(self.image_id_counter).SetDataWidth(1).SetDataHeight(1).SetFormat(\n\t\t\t\tGRT_format_rgb).SetDataSize(uint64(len(payload)))\n\t\t\t_ = g1.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(payload))\n\t\t\treturn self.image_id_counter\n\t\t}\n\t\ttf, err := images.CreateTempInRAM()\n\t\tif err == nil {\n\t\t\tif _, err = tf.Write([]byte{1, 2, 3}); err == nil {\n\t\t\t\tself.detection_file_id = g(GRT_transmission_tempfile, tf.Name())\n\t\t\t\tself.temp_file_map[self.detection_file_id] = &temp_resource{path: tf.Name()}\n\t\t\t}\n\t\t\ttf.Close()\n\t\t}\n\t\tsf, err := shm.CreateTemp(\"icat-\", 3)\n\t\tif err == nil {\n\t\t\tcopy(sf.Slice(), []byte{1, 2, 3})\n\t\t\tsf.Close()\n\t\t\tself.detection_shm_id = g(GRT_transmission_sharedmem, sf.Name())\n\t\t\tself.temp_file_map[self.detection_shm_id] = &temp_resource{mmap: sf}\n\t\t}\n\t}\n}\n\nfunc (self *ImageCollection) Finalize(lp *loop.Loop) {\n\tfor _, tr := range self.temp_file_map {\n\t\ttr.remove()\n\t}\n\tfor _, img := range self.images {\n\t\tfor _, r := range img.renderings {\n\t\t\tif r.image_id > 0 {\n\t\t\t\tg := self.new_graphics_command()\n\t\t\t\tg.SetAction(GRT_action_delete).SetDelete(GRT_free_by_id).SetImageId(r.image_id)\n\t\t\t\t_ = g.WriteWithPayloadToLoop(lp, nil)\n\t\t\t}\n\t\t}\n\t\timg.renderings = nil\n\t}\n\tself.images = nil\n}\n\nfunc (self *ImageCollection) mark_img_as_needing_transmission(id uint32) bool {\n\tself.mutex.Lock()\n\tdefer self.mutex.Unlock()\n\n\tfor _, img := range self.images {\n\t\tfor _, r := range img.renderings {\n\t\t\tif r.image_id == id {\n\t\t\t\tr.image_id = 0\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// Handle graphics response. Returns false if an image needs re-transmission because\n// the terminal replied with ENOENT for a placement\nfunc (self *ImageCollection) HandleGraphicsCommand(gc *GraphicsCommand) bool {\n\tswitch gc.ImageId() {\n\tcase self.detection_file_id:\n\t\tif gc.ResponseMessage() == \"OK\" {\n\t\t\tself.Files_supported.Store(true)\n\t\t} else {\n\t\t\tif tr := self.temp_file_map[gc.ImageId()]; tr != nil {\n\t\t\t\ttr.remove()\n\t\t\t}\n\t\t}\n\t\tdelete(self.temp_file_map, gc.ImageId())\n\t\tself.detection_file_id = 0\n\t\treturn true\n\tcase self.detection_shm_id:\n\t\tif gc.ResponseMessage() == \"OK\" {\n\t\t\tself.Shm_supported.Store(true)\n\t\t} else {\n\t\t\tif tr := self.temp_file_map[gc.ImageId()]; tr != nil {\n\t\t\t\ttr.remove()\n\t\t\t}\n\t\t}\n\t\tdelete(self.temp_file_map, gc.ImageId())\n\t\tself.detection_shm_id = 0\n\t\treturn true\n\t}\n\tif is_transmission_response := gc.PlacementId() == 0; is_transmission_response {\n\t\tif gc.ResponseMessage() != \"OK\" {\n\t\t\t// this should never happen but lets cleanup anyway\n\t\t\tif tr := self.temp_file_map[gc.ImageId()]; tr != nil {\n\t\t\t\ttr.remove()\n\t\t\t\tdelete(self.temp_file_map, gc.ImageId())\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tif gc.ResponseMessage() != \"OK\" && gc.PlacementId() != 0 {\n\t\tif self.mark_img_as_needing_transmission(gc.ImageId()) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (self *ImageCollection) LoadAll() {\n\tself.mutex.Lock()\n\tdefer self.mutex.Unlock()\n\tctx := images.Context{}\n\tall := utils.Values(self.images)\n\tif err := ctx.SafeParallel(0, len(self.images), func(nums <-chan int) {\n\t\tfor i := range nums {\n\t\t\timg := all[i]\n\t\t\tif !img.src.loaded {\n\t\t\t\timg.src.data, img.err = images.OpenImageFromPath(img.src.path)\n\t\t\t\tif img.err == nil {\n\t\t\t\t\timg.src.size.Width, img.src.size.Height = img.src.data.Width, img.src.data.Height\n\t\t\t\t}\n\t\t\t\timg.src.loaded = true\n\t\t\t}\n\t\t}\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc NewImageCollection(paths ...string) *ImageCollection {\n\titems := make(map[string]*Image, len(paths))\n\tfor _, path := range paths {\n\t\ti := NewImage()\n\t\ti.src.path = path\n\t\titems[path] = i\n\t}\n\treturn &ImageCollection{images: items, temp_file_map: make(map[uint32]*temp_resource)}\n}\n\nfunc (self *ImageCollection) new_graphics_command() *GraphicsCommand {\n\tgc := GraphicsCommand{}\n\tif self.running_in_tmux {\n\t\tgc.WrapPrefix = \"\\033Ptmux;\"\n\t\tgc.WrapSuffix = \"\\033\\\\\"\n\t\tgc.EncodeSerializedDataFunc = func(x string) string { return strings.ReplaceAll(x, \"\\033\", \"\\033\\033\") }\n\t}\n\treturn &gc\n}\n\nfunc transmit_by_escape_code(lp *loop.Loop, image_id uint32, temp_file_map map[uint32]*temp_resource, frame *images.ImageFrame, gc *GraphicsCommand) {\n\tatomic := lp.IsAtomicUpdateActive()\n\tlp.EndAtomicUpdate()\n\tgc.SetTransmission(GRT_transmission_direct)\n\t_, _, _, data := frame.Data()\n\t_ = gc.WriteWithPayloadToLoop(lp, data)\n\tif atomic {\n\t\tlp.StartAtomicUpdate()\n\t}\n}\n\nfunc transmit_by_shm(lp *loop.Loop, image_id uint32, temp_file_map map[uint32]*temp_resource, frame *images.ImageFrame, gc *GraphicsCommand) {\n\tmmap, err := frame.DataAsSHM(\"kdiff-img-*\")\n\tif err != nil {\n\t\ttransmit_by_escape_code(lp, image_id, temp_file_map, frame, gc)\n\t\treturn\n\t}\n\tmmap.Close()\n\ttemp_file_map[image_id] = &temp_resource{mmap: mmap}\n\tgc.SetTransmission(GRT_transmission_sharedmem)\n\t_ = gc.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(mmap.Name()))\n}\n\nfunc transmit_by_file(lp *loop.Loop, image_id uint32, temp_file_map map[uint32]*temp_resource, frame *images.ImageFrame, gc *GraphicsCommand) {\n\tf, err := images.CreateTempInRAM()\n\tif err != nil {\n\t\ttransmit_by_escape_code(lp, image_id, temp_file_map, frame, gc)\n\t\treturn\n\t}\n\tdefer f.Close()\n\ttemp_file_map[image_id] = &temp_resource{path: f.Name()}\n\t_, _, _, data := frame.Data()\n\t_, err = f.Write(data)\n\tif err != nil {\n\t\ttransmit_by_escape_code(lp, image_id, temp_file_map, frame, gc)\n\t\treturn\n\t}\n\tgc.SetTransmission(GRT_transmission_tempfile)\n\t_ = gc.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(f.Name()))\n}\n\nfunc (self *ImageCollection) transmit_rendering(lp *loop.Loop, r *rendering) {\n\tif r.image_id == 0 {\n\t\tself.image_id_counter++\n\t\tr.image_id = self.image_id_counter\n\t}\n\tis_animated := len(r.img.Frames) > 0\n\ttransmit := transmit_by_escape_code\n\tif self.Shm_supported.Load() {\n\t\ttransmit = transmit_by_shm\n\t} else if self.Files_supported.Load() {\n\t\ttransmit = transmit_by_file\n\t}\n\n\tframe_control_cmd := self.new_graphics_command()\n\tframe_control_cmd.SetAction(GRT_action_animate).SetImageId(r.image_id)\n\tfor frame_num, frame := range r.img.Frames {\n\t\tgc := self.new_graphics_command()\n\t\tgc.SetImageId(r.image_id)\n\t\tgc.SetDataWidth(uint64(frame.Width)).SetDataHeight(uint64(frame.Height))\n\t\tif frame.Is_opaque {\n\t\t\tgc.SetFormat(GRT_format_rgb)\n\t\t}\n\t\tswitch frame_num {\n\t\tcase 0:\n\t\t\tgc.SetAction(GRT_action_transmit)\n\t\t\tgc.SetCursorMovement(GRT_cursor_static)\n\t\tdefault:\n\t\t\tgc.SetAction(GRT_action_frame)\n\t\t\tgc.SetGap(frame.Delay_ms)\n\t\t\tif frame.Compose_onto > 0 {\n\t\t\t\tgc.SetOverlaidFrame(uint64(frame.Compose_onto))\n\t\t\t}\n\t\t\tif frame.Replace {\n\t\t\t\tgc.SetCompositionMode(Overwrite)\n\t\t\t}\n\t\t\tgc.SetLeftEdge(uint64(frame.Left)).SetTopEdge(uint64(frame.Top))\n\t\t}\n\t\ttransmit(lp, r.image_id, self.temp_file_map, frame, gc)\n\t\tif is_animated {\n\t\t\tswitch frame_num {\n\t\t\tcase 0:\n\t\t\t\t// set gap for the first frame and number of loops for the animation\n\t\t\t\tc := frame_control_cmd\n\t\t\t\tc.SetTargetFrame(uint64(frame.Number))\n\t\t\t\tc.SetGap(int32(frame.Delay_ms))\n\t\t\t\tc.SetNumberOfLoops(1)\n\t\t\t\t_ = c.WriteWithPayloadToLoop(lp, nil)\n\t\t\tcase 1:\n\t\t\t\tc := frame_control_cmd\n\t\t\t\tc.SetAnimationControl(2) // set animation to loading mode\n\t\t\t\t_ = c.WriteWithPayloadToLoop(lp, nil)\n\t\t\t}\n\t\t}\n\t}\n\tif is_animated {\n\t\tc := frame_control_cmd\n\t\tc.SetAnimationControl(3) // set animation to normal mode\n\t\t_ = c.WriteWithPayloadToLoop(lp, nil)\n\t}\n}\n"
  },
  {
    "path": "tools/tui/graphics/command.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage graphics\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\n// Enums {{{\ntype GRT_a int // enum\n\nconst (\n\tGRT_action_transmit             GRT_a = iota // t\n\tGRT_action_transmit_and_display              // T\n\tGRT_action_query                             // q\n\tGRT_action_display                           // p\n\tGRT_action_delete                            // d\n\tGRT_action_frame                             // f\n\tGRT_action_animate                           // a\n\tGRT_action_compose                           // c\n)\n\ntype GRT_q int // enum\n\nconst (\n\tGRT_quiet_noisy       GRT_q = iota // 0\n\tGRT_quiet_only_errors              // 1\n\tGRT_quiet_silent                   // 2\n)\n\ntype GRT_f int // enum\n\nconst (\n\tGRT_format_rgba GRT_f = iota // 32\n\tGRT_format_rgb               // 24\n\tGRT_format_png               // 100\n)\n\ntype GRT_t int // enum\n\nconst (\n\tGRT_transmission_direct    GRT_t = iota // d\n\tGRT_transmission_file                   // f\n\tGRT_transmission_tempfile               // t\n\tGRT_transmission_sharedmem              // s\n)\n\ntype GRT_o int // enum\n\nconst (\n\tGRT_compression_none GRT_o = iota //\n\tGRT_compression_zlib              // z\n)\n\ntype GRT_m int // enum\n\nconst (\n\tGRT_more_nomore GRT_m = iota // 0\n\tGRT_more_more                // 1\n)\n\ntype GRT_C int // enum\n\nconst (\n\tGRT_cursor_move   GRT_C = iota // 0\n\tGRT_cursor_static              // 1\n)\n\ntype GRT_U int // enum\n\nconst (\n\tGRT_no_unicode_placeholder     GRT_U = iota // 0\n\tGRT_create_unicode_placeholder              // 1\n)\n\ntype CompositionMode int // enum\n\nconst (\n\tAlphaBlend CompositionMode = iota // 0\n\tOverwrite                         // 1\n)\n\ntype GRT_d int // enum\n\nconst (\n\tGRT_delete_visible GRT_d = iota // a\n\tGRT_free_visible                // A\n\n\tGRT_delete_by_id // i\n\tGRT_free_by_id   // I\n\n\tGRT_delete_by_number // n\n\tGRT_free_by_number   // N\n\n\tGRT_delete_by_cursor // c\n\tGRT_free_by_cursor   // C\n\n\tGRT_delete_by_frame // f\n\tGRT_free_by_frame   // F\n\n\tGRT_delete_by_cell // p\n\tGRT_free_by_cell   // P\n\n\tGRT_delete_by_cell_zindex // q\n\tGRT_free_by_cell_zindex   // Q\n\n\tGRT_delete_by_range // r\n\tGRT_free_by_range   // R\n\n\tGRT_delete_by_column // x\n\tGRT_free_by_column   // X\n\n\tGRT_delete_by_row // y\n\tGRT_free_by_row   // Y\n\n\tGRT_delete_by_zindex // z\n\tGRT_free_by_zindex   // Z\n)\n\n// }}}\n\ntype GraphicsCommand struct {\n\ta GRT_a\n\tq GRT_q\n\tf GRT_f\n\tt GRT_t\n\to GRT_o\n\tm GRT_m\n\tC GRT_C\n\td GRT_d\n\tU GRT_U\n\n\ts, v, S, O, x, y, w, h, X, Y, c, r uint64\n\n\ti, I, p uint32\n\n\tz int32\n\n\tDisableCompression       bool\n\tWrapPrefix, WrapSuffix   string\n\tEncodeSerializedDataFunc func(string) string\n\n\tresponse_message string\n}\n\nfunc (self *GraphicsCommand) serialize_non_default_fields() (ans []string) {\n\tvar null GraphicsCommand\n\tans = make([]string, 0, 16)\n\n\twrite_key := func(k byte, val, defval any) {\n\t\tif val != defval {\n\t\t\tans = append(ans, fmt.Sprintf(\"%c=%v\", k, val))\n\t\t}\n\t}\n\n\twrite_key('a', self.a, null.a)\n\twrite_key('q', self.q, null.q)\n\twrite_key('f', self.f, null.f)\n\twrite_key('t', self.t, null.t)\n\twrite_key('o', self.o, null.o)\n\twrite_key('m', self.m, null.m)\n\twrite_key('C', self.C, null.C)\n\twrite_key('U', self.U, null.U)\n\twrite_key('d', self.d, null.d)\n\n\twrite_key('s', self.s, null.s)\n\twrite_key('v', self.v, null.v)\n\twrite_key('S', self.S, null.S)\n\twrite_key('O', self.O, null.O)\n\twrite_key('x', self.x, null.x)\n\twrite_key('y', self.y, null.y)\n\twrite_key('w', self.w, null.w)\n\twrite_key('h', self.h, null.h)\n\twrite_key('X', self.X, null.X)\n\twrite_key('Y', self.Y, null.Y)\n\twrite_key('c', self.c, null.c)\n\twrite_key('r', self.r, null.r)\n\n\twrite_key('i', self.i, null.i)\n\twrite_key('I', self.I, null.I)\n\twrite_key('p', self.p, null.p)\n\n\twrite_key('z', self.z, null.z)\n\treturn\n}\n\nfunc (self GraphicsCommand) String() string {\n\tans := \"GraphicsCommand(\" + strings.Join(self.serialize_non_default_fields(), \", \")\n\tif self.response_message != \"\" {\n\t\tans += fmt.Sprintf(\", response=%#v\", self.response_message)\n\t}\n\treturn ans + \")\"\n}\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc (self *GraphicsCommand) serialize_to(buf io.StringWriter, chunk string) (err error) {\n\tvar ws func(string)\n\tif self.EncodeSerializedDataFunc == nil {\n\t\tws = func(s string) {\n\t\t\t_, err = buf.WriteString(s)\n\t\t}\n\t} else {\n\t\tws = func(s string) {\n\t\t\t_, err = buf.WriteString(self.EncodeSerializedDataFunc(s))\n\t\t}\n\t}\n\tif self.WrapPrefix != \"\" {\n\t\t_, err = buf.WriteString(self.WrapPrefix)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif self.WrapSuffix != \"\" {\n\t\t\tdefer func() {\n\t\t\t\tif err == nil {\n\t\t\t\t\t_, err = buf.WriteString(self.WrapSuffix)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t}\n\tws(\"\\033_G\")\n\tif err == nil {\n\t\titems := self.serialize_non_default_fields()\n\t\tws(strings.Join(items, \",\"))\n\t\tif err == nil {\n\t\t\tif len(chunk) > 0 {\n\t\t\t\tws(\";\")\n\t\t\t\tif err == nil {\n\t\t\t\t\tws(chunk)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tws(\"\\033\\\\\")\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc compress_with_zlib(data []byte) []byte {\n\tvar b bytes.Buffer\n\tb.Grow(len(data) + 128)\n\tw := zlib.NewWriter(&b)\n\t_, _ = w.Write(data)\n\tw.Close()\n\treturn b.Bytes()\n}\n\nfunc (self *GraphicsCommand) AsAPC(payload []byte) string {\n\tbuf := strings.Builder{}\n\tbuf.Grow(1024)\n\t_ = self.WriteWithPayloadTo(&buf, payload)\n\treturn buf.String()\n}\n\nfunc (self *GraphicsCommand) WriteWithPayloadTo(o io.StringWriter, payload []byte) (err error) {\n\tconst compression_threshold = 2048\n\tif len(payload) == 0 {\n\t\treturn self.serialize_to(o, \"\")\n\t}\n\tif len(payload) <= compression_threshold {\n\t\treturn self.serialize_to(o, base64.RawStdEncoding.EncodeToString(payload))\n\t}\n\tgc := *self\n\tif !self.DisableCompression && self.Format() != GRT_format_png {\n\t\tcompressed := compress_with_zlib(payload)\n\t\tif len(compressed) < len(payload) {\n\t\t\tgc.SetCompression(GRT_compression_zlib)\n\t\t\tpayload = compressed\n\t\t}\n\t}\n\tconst chunk_size = 128 * 1024\n\tdata := base64.RawStdEncoding.EncodeToString(payload)\n\tfor len(data) > 0 && err == nil {\n\t\tchunk := data\n\t\tif len(data) > chunk_size {\n\t\t\tchunk = data[:chunk_size]\n\t\t\tdata = data[chunk_size:]\n\t\t} else {\n\t\t\tdata = \"\"\n\t\t}\n\t\tif len(data) > 0 {\n\t\t\tgc.m = GRT_more_more\n\t\t} else {\n\t\t\tgc.m = GRT_more_nomore\n\t\t}\n\t\terr = gc.serialize_to(o, chunk)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgc = GraphicsCommand{\n\t\t\tq: self.q, a: self.a, WrapPrefix: self.WrapPrefix, WrapSuffix: self.WrapSuffix,\n\t\t\tEncodeSerializedDataFunc: self.EncodeSerializedDataFunc}\n\t}\n\treturn\n}\n\ntype loop_io_writer struct {\n\tlp *loop.Loop\n}\n\nfunc (self *loop_io_writer) WriteString(data string) (n int, err error) {\n\tself.lp.QueueWriteString(data)\n\treturn\n}\n\nfunc (self *GraphicsCommand) WriteWithPayloadToLoop(lp *loop.Loop, payload []byte) (err error) {\n\tw := loop_io_writer{lp}\n\treturn self.WriteWithPayloadTo(&w, payload)\n}\n\nfunc set_val[T any](loc *T, parser func(string) (T, error), value string) (err error) {\n\tvar temp T\n\ttemp, err = parser(value)\n\tif err == nil {\n\t\t*loc = temp\n\t}\n\treturn err\n}\n\nfunc set_uval(loc *uint64, value string) (err error) {\n\ttemp, err := strconv.ParseUint(value, 10, 64)\n\tif err == nil {\n\t\t*loc = temp\n\t}\n\treturn err\n}\n\nfunc set_u32val(loc *uint32, value string) (err error) {\n\ttemp, err := strconv.ParseUint(value, 10, 32)\n\tif err == nil {\n\t\t*loc = uint32(temp)\n\t}\n\treturn err\n}\n\nfunc set_i32val(loc *int32, value string) (err error) {\n\ttemp, err := strconv.ParseInt(value, 10, 32)\n\tif err == nil {\n\t\t*loc = int32(temp)\n\t}\n\treturn err\n}\n\nfunc (self *GraphicsCommand) SetString(key byte, value string) (err error) {\n\tswitch key {\n\tcase 'a':\n\t\terr = set_val(&self.a, GRT_a_from_string, value)\n\tcase 'q':\n\t\terr = set_val(&self.q, GRT_q_from_string, value)\n\tcase 'f':\n\t\terr = set_val(&self.f, GRT_f_from_string, value)\n\tcase 't':\n\t\terr = set_val(&self.t, GRT_t_from_string, value)\n\tcase 'o':\n\t\terr = set_val(&self.o, GRT_o_from_string, value)\n\tcase 'm':\n\t\terr = set_val(&self.m, GRT_m_from_string, value)\n\tcase 'C':\n\t\terr = set_val(&self.C, GRT_C_from_string, value)\n\tcase 'U':\n\t\terr = set_val(&self.U, GRT_U_from_string, value)\n\tcase 'd':\n\t\terr = set_val(&self.d, GRT_d_from_string, value)\n\tcase 's':\n\t\terr = set_uval(&self.s, value)\n\tcase 'v':\n\t\terr = set_uval(&self.v, value)\n\tcase 'S':\n\t\terr = set_uval(&self.S, value)\n\tcase 'O':\n\t\terr = set_uval(&self.O, value)\n\tcase 'x':\n\t\terr = set_uval(&self.x, value)\n\tcase 'y':\n\t\terr = set_uval(&self.y, value)\n\tcase 'w':\n\t\terr = set_uval(&self.w, value)\n\tcase 'h':\n\t\terr = set_uval(&self.h, value)\n\tcase 'X':\n\t\terr = set_uval(&self.X, value)\n\tcase 'Y':\n\t\terr = set_uval(&self.Y, value)\n\tcase 'c':\n\t\terr = set_uval(&self.c, value)\n\tcase 'r':\n\t\terr = set_uval(&self.r, value)\n\tcase 'i':\n\t\terr = set_u32val(&self.i, value)\n\tcase 'I':\n\t\terr = set_u32val(&self.I, value)\n\tcase 'p':\n\t\terr = set_u32val(&self.p, value)\n\tcase 'z':\n\t\terr = set_i32val(&self.z, value)\n\tdefault:\n\t\treturn fmt.Errorf(\"Unknown key: %c\", key)\n\t}\n\treturn\n}\n\nfunc GraphicsCommandFromAPCPayload(raw []byte) *GraphicsCommand {\n\tconst (\n\t\texpecting_key int = iota\n\t\texpecting_equals\n\t\texpecting_value\n\t)\n\tstate := expecting_key\n\tvar current_key byte\n\tvar value_start_at int\n\tvar payload_start_at int = -1\n\tvar gc GraphicsCommand\n\n\tadd_key := func(pos int) {\n\t\t_ = gc.SetString(current_key, utils.UnsafeBytesToString(raw[value_start_at:pos]))\n\t}\n\n\tfor pos, ch := range raw {\n\t\tif ch == ';' {\n\t\t\tif state == expecting_value {\n\t\t\t\tadd_key(pos)\n\t\t\t}\n\t\t\tstate = expecting_key\n\t\t\tpayload_start_at = pos + 1\n\t\t\tbreak\n\t\t}\n\t\tswitch state {\n\t\tcase expecting_key:\n\t\t\tcurrent_key = ch\n\t\t\tstate = expecting_equals\n\t\tcase expecting_equals:\n\t\t\tif ch == '=' {\n\t\t\t\tstate = expecting_value\n\t\t\t\tvalue_start_at = pos + 1\n\t\t\t} else {\n\t\t\t\tstate = expecting_key\n\t\t\t}\n\t\tcase expecting_value:\n\t\t\tif ch == ',' {\n\t\t\t\tadd_key(pos)\n\t\t\t\tstate = expecting_key\n\t\t\t}\n\t\t}\n\t}\n\tif state == expecting_value {\n\t\tadd_key(len(raw))\n\t}\n\tif payload_start_at > -1 {\n\t\tpayload := raw[payload_start_at:]\n\t\tif len(payload) > 0 {\n\t\t\tgc.response_message = string(payload)\n\t\t}\n\t}\n\treturn &gc\n}\n\nfunc GraphicsCommandFromAPC(raw []byte) *GraphicsCommand {\n\tif len(raw) < 1 || raw[0] != 'G' {\n\t\treturn nil\n\t}\n\treturn GraphicsCommandFromAPCPayload(raw[1:])\n}\n\n// Getters and Setters {{{\nfunc (self *GraphicsCommand) Action() GRT_a {\n\treturn self.a\n}\n\nfunc (self *GraphicsCommand) SetAction(a GRT_a) *GraphicsCommand {\n\tself.a = a\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Delete() GRT_d {\n\treturn self.d\n}\n\nfunc (self *GraphicsCommand) SetDelete(d GRT_d) *GraphicsCommand {\n\tself.d = d\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Quiet() GRT_q {\n\treturn self.q\n}\n\nfunc (self *GraphicsCommand) SetQuiet(q GRT_q) *GraphicsCommand {\n\tself.q = q\n\treturn self\n}\n\nfunc (self *GraphicsCommand) CursorMovement() GRT_C {\n\treturn self.C\n}\n\nfunc (self *GraphicsCommand) SetCursorMovement(c GRT_C) *GraphicsCommand {\n\tself.C = c\n\treturn self\n}\n\nfunc (self *GraphicsCommand) UnicodePlaceholder() GRT_U {\n\treturn self.U\n}\n\nfunc (self *GraphicsCommand) SetUnicodePlaceholder(U GRT_U) *GraphicsCommand {\n\tself.U = U\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Format() GRT_f {\n\treturn self.f\n}\n\nfunc (self *GraphicsCommand) SetFormat(f GRT_f) *GraphicsCommand {\n\tself.f = f\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Transmission() GRT_t {\n\treturn self.t\n}\n\nfunc (self *GraphicsCommand) SetTransmission(t GRT_t) *GraphicsCommand {\n\tself.t = t\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Compression() GRT_o {\n\treturn self.o\n}\n\nfunc (self *GraphicsCommand) SetCompression(o GRT_o) *GraphicsCommand {\n\tself.o = o\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Width() uint64 {\n\treturn self.w\n}\n\nfunc (self *GraphicsCommand) SetWidth(w uint64) *GraphicsCommand {\n\tself.w = w\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Height() uint64 {\n\treturn self.h\n}\n\nfunc (self *GraphicsCommand) SetHeight(h uint64) *GraphicsCommand {\n\tself.h = h\n\treturn self\n}\n\nfunc (self *GraphicsCommand) DataWidth() uint64 {\n\treturn self.s\n}\n\nfunc (self *GraphicsCommand) SetDataWidth(w uint64) *GraphicsCommand {\n\tself.s = w\n\treturn self\n}\n\nfunc (self *GraphicsCommand) DataHeight() uint64 {\n\treturn self.v\n}\n\nfunc (self *GraphicsCommand) SetDataHeight(h uint64) *GraphicsCommand {\n\tself.v = h\n\treturn self\n}\n\nfunc (self *GraphicsCommand) NumberOfLoops() uint64 {\n\treturn self.v\n}\n\nfunc (self *GraphicsCommand) SetNumberOfLoops(n uint64) *GraphicsCommand {\n\tself.v = n\n\treturn self\n}\n\nfunc (self *GraphicsCommand) DataSize() uint64 {\n\treturn self.S\n}\n\nfunc (self *GraphicsCommand) SetDataSize(s uint64) *GraphicsCommand {\n\tself.S = s\n\treturn self\n}\n\nfunc (self *GraphicsCommand) DataOffset() uint64 {\n\treturn self.O\n}\n\nfunc (self *GraphicsCommand) SetDataOffset(o uint64) *GraphicsCommand {\n\tself.O = o\n\treturn self\n}\n\nfunc (self *GraphicsCommand) LeftEdge() uint64 {\n\treturn self.x\n}\n\nfunc (self *GraphicsCommand) SetLeftEdge(x uint64) *GraphicsCommand {\n\tself.x = x\n\treturn self\n}\n\nfunc (self *GraphicsCommand) TopEdge() uint64 {\n\treturn self.y\n}\n\nfunc (self *GraphicsCommand) SetTopEdge(y uint64) *GraphicsCommand {\n\tself.y = y\n\treturn self\n}\n\nfunc (self *GraphicsCommand) XOffset() uint64 {\n\treturn self.X\n}\n\nfunc (self *GraphicsCommand) SetXOffset(x uint64) *GraphicsCommand {\n\tself.X = x\n\treturn self\n}\n\nfunc (self *GraphicsCommand) BlendMode() CompositionMode {\n\tswitch self.X {\n\tcase 1:\n\t\treturn Overwrite\n\tdefault:\n\t\treturn AlphaBlend\n\t}\n}\n\nfunc (self *GraphicsCommand) SetBlendMode(x CompositionMode) *GraphicsCommand {\n\tself.X = uint64(x)\n\treturn self\n}\n\nfunc (self *GraphicsCommand) CompositionMode() CompositionMode {\n\tswitch self.C {\n\tcase 1:\n\t\treturn Overwrite\n\tdefault:\n\t\treturn AlphaBlend\n\t}\n}\n\nfunc (self *GraphicsCommand) SetCompositionMode(x CompositionMode) *GraphicsCommand {\n\tswitch x {\n\tcase Overwrite:\n\t\tself.C = 1\n\tcase AlphaBlend:\n\t\tself.C = 0\n\t}\n\treturn self\n}\n\nfunc (self *GraphicsCommand) YOffset() uint64 {\n\treturn self.Y\n}\n\nfunc (self *GraphicsCommand) SetYOffset(y uint64) *GraphicsCommand {\n\tself.Y = y\n\treturn self\n}\n\nfunc (self *GraphicsCommand) SourceTopEdge() uint64 {\n\treturn self.Y\n}\n\nfunc (self *GraphicsCommand) SetSourceTopEdge(y uint64) *GraphicsCommand {\n\tself.Y = y\n\treturn self\n}\n\nfunc (self *GraphicsCommand) BackgroundColor() uint32 {\n\treturn uint32(self.Y)\n}\n\nfunc (self *GraphicsCommand) SetBackgroundColor(y uint32) *GraphicsCommand {\n\tself.Y = uint64(y)\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Rows() uint64 {\n\treturn self.r\n}\n\nfunc (self *GraphicsCommand) SetRows(r uint64) *GraphicsCommand {\n\tself.r = r\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Columns() uint64 {\n\treturn self.c\n}\n\nfunc (self *GraphicsCommand) SetColumns(c uint64) *GraphicsCommand {\n\tself.c = c\n\treturn self\n}\n\nfunc (self *GraphicsCommand) TargetFrame() uint64 {\n\treturn self.r\n}\n\nfunc (self *GraphicsCommand) SetTargetFrame(r uint64) *GraphicsCommand {\n\tself.r = r\n\treturn self\n}\n\nfunc (self *GraphicsCommand) BaseFrame() uint64 {\n\treturn self.c\n}\n\nfunc (self *GraphicsCommand) SetBaseFrame(c uint64) *GraphicsCommand {\n\tself.c = c\n\treturn self\n}\n\nfunc (self *GraphicsCommand) OverlaidFrame() uint64 {\n\treturn self.c\n}\n\nfunc (self *GraphicsCommand) SetOverlaidFrame(c uint64) *GraphicsCommand {\n\tself.c = c\n\treturn self\n}\n\nfunc (self *GraphicsCommand) FrameToMakeCurrent() uint64 {\n\treturn self.c\n}\n\nfunc (self *GraphicsCommand) SetFrameToMakeCurrent(c uint64) *GraphicsCommand {\n\tself.c = c\n\treturn self\n}\n\nfunc (self *GraphicsCommand) ImageId() uint32 {\n\treturn self.i\n}\n\nfunc (self *GraphicsCommand) SetImageId(i uint32) *GraphicsCommand {\n\tself.i = i\n\treturn self\n}\n\nfunc (self *GraphicsCommand) ImageNumber() uint32 {\n\treturn self.I\n}\n\nfunc (self *GraphicsCommand) SetImageNumber(n uint32) *GraphicsCommand {\n\tself.I = n\n\treturn self\n}\n\nfunc (self *GraphicsCommand) PlacementId() uint32 {\n\treturn self.p\n}\n\nfunc (self *GraphicsCommand) SetPlacementId(p uint32) *GraphicsCommand {\n\tself.p = p\n\treturn self\n}\n\nfunc (self *GraphicsCommand) ZIndex() int32 {\n\treturn self.z\n}\n\nfunc (self *GraphicsCommand) SetZIndex(z int32) *GraphicsCommand {\n\tself.z = z\n\treturn self\n}\n\nfunc (self *GraphicsCommand) Gap() int32 {\n\treturn self.z\n}\n\nfunc (self *GraphicsCommand) SetGap(z int32) *GraphicsCommand {\n\tself.z = z\n\treturn self\n}\n\ntype AnimationControl uint\n\nconst (\n\tNoAnimationAction AnimationControl = iota\n\tStopAnimation\n\tRunAnimationButWaitForNewFrames\n\tRunAnimation\n)\n\nfunc (self *GraphicsCommand) AnimationControl() AnimationControl {\n\tswitch AnimationControl(self.s) {\n\tdefault:\n\t\treturn NoAnimationAction\n\tcase StopAnimation:\n\t\treturn StopAnimation\n\tcase RunAnimationButWaitForNewFrames:\n\t\treturn RunAnimationButWaitForNewFrames\n\tcase RunAnimation:\n\t\treturn RunAnimation\n\t}\n\n}\n\nfunc (self *GraphicsCommand) SetAnimationControl(z uint) *GraphicsCommand {\n\tself.s = uint64(z)\n\treturn self\n}\n\nfunc (self *GraphicsCommand) ResponseMessage() string {\n\treturn self.response_message\n}\n\n// }}}\n"
  },
  {
    "path": "tools/tui/graphics/command_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage graphics\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"golang.org/x/exp/rand\"\n)\n\nvar _ = fmt.Print\n\nfunc from_full_apc_escape_code(raw string) *GraphicsCommand {\n\treturn GraphicsCommandFromAPC([]byte(raw[2 : len(raw)-2]))\n}\n\nfunc TestGraphicsCommandSerialization(t *testing.T) {\n\tgc := &GraphicsCommand{}\n\n\ttest_serialize := func(payload string, vals ...string) {\n\t\texpected := \"\\033_G\" + strings.Join(vals, \",\")\n\t\tif payload != \"\" {\n\t\t\texpected += \";\" + base64.RawStdEncoding.EncodeToString([]byte(payload))\n\t\t}\n\t\texpected += \"\\033\\\\\"\n\t\tif diff := cmp.Diff(expected, gc.AsAPC([]byte(payload))); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to write vals: %#v with payload: %#v\\n%s\", vals, payload, diff)\n\t\t}\n\t}\n\n\ttest_chunked_payload := func(payload []byte) {\n\t\tc := &GraphicsCommand{}\n\t\tdata := c.AsAPC([]byte(payload))\n\t\tencoded := strings.Builder{}\n\t\tcompressed := false\n\t\tis_first := true\n\t\tfor {\n\t\t\tidx := strings.Index(data, \"\\033_\")\n\t\t\tif idx < 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tl := strings.Index(data, \"\\033\\\\\")\n\t\t\tapc := data[idx+2 : l]\n\t\t\tdata = data[l+2:]\n\t\t\tg := GraphicsCommandFromAPC([]byte(apc))\n\t\t\tif is_first {\n\t\t\t\tcompressed = g.Compression() != 0\n\t\t\t\tis_first = false\n\t\t\t}\n\t\t\tencoded.WriteString(g.ResponseMessage())\n\t\t\tif g.m == GRT_more_nomore {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif len(data) > 0 {\n\t\t\tt.Fatalf(\"Unparsed remnant: %#v\", string(data))\n\t\t}\n\t\tdecoded, err := base64.RawStdEncoding.DecodeString(encoded.String())\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Encoded data not valid base-64 with error: %v\", err)\n\t\t}\n\t\tif compressed {\n\t\t\tb := bytes.Buffer{}\n\t\t\tb.Write(decoded)\n\t\t\tr, _ := zlib.NewReader(&b)\n\t\t\to := bytes.Buffer{}\n\t\t\tif _, err = io.Copy(&o, r); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tr.Close()\n\t\t\tdecoded = o.Bytes()\n\t\t}\n\t\tif diff := cmp.Diff(payload, decoded); diff != \"\" {\n\t\t\tt.Fatalf(\"Decoded payload does not match original\\nlen decoded=%d len payload=%d\", len(decoded), len(payload))\n\t\t}\n\t}\n\n\ttest_serialize(\"\")\n\tgc.SetTransmission(GRT_transmission_sharedmem).SetAction(GRT_action_query).SetZIndex(-3).SetWidth(33).SetImageId(11)\n\ttest_serialize(\"abcd\", \"a=q\", \"t=s\", \"w=33\", \"i=11\", \"z=-3\")\n\tq := from_full_apc_escape_code(gc.AsAPC([]byte(\"abcd\")))\n\tif diff := cmp.Diff(gc.AsAPC(nil), q.AsAPC(nil)); diff != \"\" {\n\t\tt.Fatalf(\"Parsing failed:\\n%s\", diff)\n\t}\n\tif diff := cmp.Diff(q.response_message, base64.RawStdEncoding.EncodeToString([]byte(\"abcd\"))); diff != \"\" {\n\t\tt.Fatalf(\"Failed to parse payload:\\n%s\", diff)\n\t}\n\n\ttest_chunked_payload([]byte(\"abcd\"))\n\tdata := make([]byte, 8111)\n\t_, _ = rand.Read(data)\n\ttest_chunked_payload(data)\n\ttest_chunked_payload([]byte(strings.Repeat(\"a\", 8007)))\n\n}\n"
  },
  {
    "path": "tools/tui/hold.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n)\n\nvar _ = fmt.Print\n\nfunc HoldTillEnter(start_with_newline bool) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)\n\tif err != nil {\n\t\treturn\n\t}\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.SetCursorVisible(false)\n\t\tif start_with_newline {\n\t\t\tlp.QueueWriteString(\"\\r\\n\")\n\t\t}\n\t\tlp.QueueWriteString(\"\\x1b[1;32mPress Enter or Esc to exit\\x1b[m\")\n\t\treturn \"\", nil\n\t}\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorVisible(true)\n\t\treturn \"\"\n\t}\n\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\tif event.MatchesPressOrRepeat(\"enter\") || event.MatchesPressOrRepeat(\"kp_enter\") || event.MatchesPressOrRepeat(\"esc\") || event.MatchesPressOrRepeat(\"ctrl+c\") || event.MatchesPressOrRepeat(\"ctrl+d\") {\n\t\t\tevent.Handled = true\n\t\t\tlp.Quit(0)\n\t\t}\n\t\treturn nil\n\t}\n\tlp.Run()\n}\n\nfunc ExecAndHoldTillEnter(cmdline []string) (int, error) {\n\tif len(cmdline) == 0 {\n\t\tHoldTillEnter(false)\n\t\treturn 0, nil\n\t}\n\tvar cmd *exec.Cmd\n\tif len(cmdline) == 1 {\n\t\tcmd = exec.Command(cmdline[0])\n\t} else {\n\t\tcmd = exec.Command(cmdline[0], cmdline[1:]...)\n\t}\n\tcmd.Stdin = os.Stdin\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\tvar ee *exec.ExitError\n\terr := cmd.Run()\n\tis_exit_error := err != nil && errors.As(err, &ee)\n\tif err != nil && !is_exit_error {\n\t\treturn 1, err\n\t}\n\tHoldTillEnter(true)\n\tif err == nil {\n\t\treturn 0, nil\n\t}\n\tif is_exit_error {\n\t\treturn ee.ExitCode(), nil\n\t}\n\treturn 1, err\n}\n"
  },
  {
    "path": "tools/tui/loop/api.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\ntype ScreenSize struct {\n\tWidthCells, HeightCells, WidthPx, HeightPx, CellWidth, CellHeight uint\n\tupdated                                                           bool\n}\n\ntype IdType uint64\ntype TimerCallback func(timer_id IdType) error\ntype EscapeCodeType int\n\nconst (\n\tCSI EscapeCodeType = iota\n\tDCS\n\tOSC\n\tAPC\n\tSOS\n\tPM\n)\n\ntype Loop struct {\n\tcontrolling_term                       *tty.Term\n\tterminal_options                       TerminalStateOptions\n\tscreen_size                            ScreenSize\n\tseen_inband_resize                     bool\n\tescape_code_parser                     wcswidth.EscapeCodeParser\n\tkeep_going                             bool\n\tdeath_signal                           unix.Signal\n\texit_code                              int\n\ttimers, timers_temp                    []*timer\n\ttimer_id_counter, write_msg_id_counter IdType\n\twakeup_channel                         chan byte\n\tpanic_channel                          chan error\n\tpending_writes                         []write_msg\n\ttty_write_channel                      chan write_msg\n\tpending_mouse_events                   *utils.RingBuffer[MouseEvent]\n\ton_SIGTSTP                             func() error\n\tstyle_cache                            map[string]func(...any) string\n\tstyle_ctx                              style.Context\n\tatomic_update_active                   bool\n\tpointer_shapes                         []PointerShape\n\twaiting_for_capabilities_response      bool\n\n\t// Queried capabilities from terminal\n\tTerminalCapabilities TerminalCapabilities\n\n\t// Suspend the loop restoring terminal state, and run the provided function. When it returns terminal state is\n\t// put back to what it was before suspending unless the function returns an error or an error occurs saving/restoring state.\n\tSuspendAndRun func(func() error) error\n\n\t// Callbacks\n\n\t// Called when the terminal has been fully setup. Any string returned is sent to\n\t// the terminal on shutdown\n\tOnInitialize func() (string, error)\n\n\t// Called just before the loop shuts down. Any returned string is written to the terminal before\n\t// shutdown\n\tOnFinalize func() string\n\n\t// Called when a key event happens\n\tOnKeyEvent func(event *KeyEvent) error\n\n\t// Called when a mouse event happens\n\tOnMouseEvent func(event *MouseEvent) error\n\n\t// Called when text is received either from a key event or directly from the terminal\n\t// Called with an empty string when bracketed paste ends\n\tOnText func(text string, from_key_event bool, in_bracketed_paste bool) error\n\n\t// Called when the terminal is resized\n\tOnResize func(old_size ScreenSize, new_size ScreenSize) error\n\n\t// Called when writing is done\n\tOnWriteComplete func(msg_id IdType, has_pending_writes bool) error\n\n\t// Called when a response to an rc command is received\n\tOnRCResponse func(data []byte) error\n\n\t// Called when a response to a query command is received\n\tOnQueryResponse func(key, val string, valid bool) error\n\n\t// Called when any input from tty is received\n\tOnReceivedData func(data []byte) error\n\n\t// Called when an escape code is received that is not handled by any other handler\n\tOnEscapeCode func(EscapeCodeType, []byte) error\n\n\t// Called when resuming from a SIGTSTP or Ctrl-z\n\tOnResumeFromStop func() error\n\n\t// Called when main loop is woken up\n\tOnWakeup func() error\n\n\t// Called on SIGINT return true if you wish to handle it yourself\n\tOnSIGINT func() (bool, error)\n\n\t// Called on SIGTERM return true if you wish to handle it yourself\n\tOnSIGTERM func() (bool, error)\n\n\t// Called when capabilities response is received\n\tOnCapabilitiesReceived func(TerminalCapabilities) error\n\n\t// Called when the terminal's color scheme changes\n\tOnColorSchemeChange func(ColorPreference) error\n\n\t// Called on focus in/out events\n\tOnFocusChange func(bool) error\n}\n\nfunc New(options ...func(self *Loop)) (*Loop, error) {\n\tl := new_loop()\n\tfor _, f := range options {\n\t\tf(l)\n\t}\n\treturn l, nil\n}\n\nfunc (self *Loop) AddTimer(interval time.Duration, repeats bool, callback TimerCallback) (IdType, error) {\n\treturn self.add_timer(interval, repeats, callback)\n}\n\nfunc (self *Loop) CallSoon(callback TimerCallback) (IdType, error) {\n\treturn self.add_timer(0, false, callback)\n}\n\nfunc (self *Loop) RemoveTimer(id IdType) bool {\n\treturn self.remove_timer(id)\n}\n\nfunc (self *Loop) NoAlternateScreen() *Loop {\n\tself.terminal_options.Alternate_screen = false\n\treturn self\n}\n\nfunc NoAlternateScreen(self *Loop) {\n\tself.terminal_options.Alternate_screen = false\n}\n\nfunc (self *Loop) OnlyDisambiguateKeys() *Loop {\n\tself.terminal_options.kitty_keyboard_mode = DISAMBIGUATE_KEYS\n\treturn self\n}\n\nfunc OnlyDisambiguateKeys(self *Loop) {\n\tself.terminal_options.kitty_keyboard_mode = DISAMBIGUATE_KEYS\n}\n\nfunc (self *Loop) NoKeyboardStateChange() *Loop {\n\tself.terminal_options.kitty_keyboard_mode = NO_KEYBOARD_STATE_CHANGE\n\treturn self\n}\n\nfunc NoKeyboardStateChange(self *Loop) {\n\tself.terminal_options.kitty_keyboard_mode = NO_KEYBOARD_STATE_CHANGE\n}\n\nfunc (self *Loop) FullKeyboardProtocol() *Loop {\n\tself.terminal_options.kitty_keyboard_mode = FULL_KEYBOARD_PROTOCOL\n\treturn self\n}\n\nfunc FullKeyboardProtocol(self *Loop) {\n\tself.terminal_options.kitty_keyboard_mode = FULL_KEYBOARD_PROTOCOL\n}\n\nfunc (self *Loop) MouseTrackingMode(mt MouseTracking) *Loop {\n\tself.terminal_options.mouse_tracking = mt\n\treturn self\n}\n\nfunc MouseTrackingMode(self *Loop, mt MouseTracking) {\n\tself.terminal_options.mouse_tracking = mt\n}\n\nfunc NoMouseTracking(self *Loop) {\n\tself.terminal_options.mouse_tracking = NO_MOUSE_TRACKING\n}\n\nfunc (self *Loop) NoMouseTracking() *Loop {\n\tself.terminal_options.mouse_tracking = NO_MOUSE_TRACKING\n\treturn self\n}\n\nfunc (self *Loop) NoRestoreColors() *Loop {\n\tself.terminal_options.restore_colors = false\n\treturn self\n}\n\nfunc NoRestoreColors(self *Loop) {\n\tself.terminal_options.restore_colors = false\n}\n\nfunc (self *Loop) NoFocusTracking() *Loop {\n\tself.terminal_options.focus_tracking = false\n\treturn self\n}\n\nfunc NoFocusTracking(self *Loop) {\n\tself.terminal_options.focus_tracking = false\n}\n\nfunc (self *Loop) RequestCurrentColorScheme() {\n\tself.QueueWriteString(\"\\x1b[?996n\")\n}\n\nfunc (self *Loop) ColorSchemeChangeNotifications() *Loop {\n\tself.terminal_options.color_scheme_change_notification = true\n\treturn self\n}\n\nfunc ColorSchemeChangeNotifications(self *Loop) {\n\tself.terminal_options.color_scheme_change_notification = true\n}\n\nfunc NoInBandResizeNotifications(self *Loop) {\n\tself.terminal_options.in_band_resize_notification = false\n}\n\nfunc (self *Loop) DeathSignalName() string {\n\tif self.death_signal != SIGNULL {\n\t\treturn self.death_signal.String()\n\t}\n\treturn \"\"\n}\n\nfunc (self *Loop) ScreenSize() (ScreenSize, error) {\n\tif self.screen_size.updated {\n\t\treturn self.screen_size, nil\n\t}\n\terr := self.update_screen_size()\n\treturn self.screen_size, err\n}\n\nfunc (self *Loop) KillIfSignalled() {\n\tif self.death_signal != SIGNULL {\n\t\tkill_self(self.death_signal)\n\t}\n}\n\nfunc (self *Loop) Println(args ...any) {\n\tself.QueueWriteString(fmt.Sprintln(args...))\n\tself.QueueWriteString(\"\\r\")\n}\n\nfunc (self *Loop) style_region(style string, start_x, start_y, end_x, end_y int) string {\n\tsgr := self.SprintStyled(style, \"|\")\n\tif len(sgr) > 2 {\n\t\tsgr = sgr[2:strings.IndexByte(sgr, 'm')]\n\t\treturn fmt.Sprintf(\"\\x1b[%d;%d;%d;%d;%s$r\", start_y+1, start_x+1, end_y+1, end_x+1, sgr)\n\t}\n\treturn \"\"\n}\n\n// Apply the specified style to the specified region of the screen (0-based\n// indexing). The region is all cells from the start cell to the end cell. See\n// StyleRectangle to apply style to a rectangular area.\nfunc (self *Loop) StyleRegion(style string, start_x, start_y, end_x, end_y int) IdType {\n\treturn self.QueueWriteString(self.style_region(style, start_x, start_y, end_x, end_y))\n}\n\n// Apply the specified style to the specified rectangle of the screen (0-based indexing).\nfunc (self *Loop) StyleRectangle(style string, start_x, start_y, end_x, end_y int) IdType {\n\treturn self.QueueWriteString(\"\\x1b[2*x\" + self.style_region(style, start_x, start_y, end_x, end_y) + \"\\x1b[*x\")\n}\n\nfunc (self *Loop) SprintStyled(style string, args ...any) string {\n\tf := self.style_cache[style]\n\tif f == nil {\n\t\tf = self.style_ctx.SprintFunc(style)\n\t\tself.style_cache[style] = f\n\t}\n\treturn f(args...)\n}\n\nfunc (self *Loop) PrintStyled(style string, args ...any) {\n\tself.QueueWriteString(self.SprintStyled(style, args...))\n}\n\nfunc (self *Loop) SaveCursorPosition() {\n\tself.QueueWriteString(\"\\x1b7\")\n}\n\nfunc (self *Loop) RestoreCursorPosition() {\n\tself.QueueWriteString(\"\\x1b8\")\n}\n\nfunc (self *Loop) Printf(format string, args ...any) {\n\tformat = strings.ReplaceAll(format, \"\\n\", \"\\r\\n\")\n\tself.QueueWriteString(fmt.Sprintf(format, args...))\n}\n\nfunc (self *Loop) DebugPrintln(args ...any) {\n\tif self.controlling_term != nil {\n\t\tconst limit = 2048\n\t\tmsg := fmt.Sprintln(args...)\n\t\tfor i := 0; i < len(msg); i += limit {\n\t\t\tend := min(i+limit, len(msg))\n\t\t\tself.QueueWriteString(\"\\x1bP@kitty-print|\")\n\t\t\tself.QueueWriteString(base64.StdEncoding.EncodeToString([]byte(msg[i:end])))\n\t\t\tself.QueueWriteString(\"\\x1b\\\\\")\n\t\t}\n\t}\n}\n\nfunc (self *Loop) Run() (err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = parallel.Format_stacktrace_on_panic(r, 1)\n\t\t\tis_terminal := tty.IsTerminal(os.Stderr.Fd())\n\t\t\tif is_terminal {\n\t\t\t\tos.Stderr.WriteString(\"\\x1b]\\x1b\\\\\\x1bc\\x1b[H\\x1b[2J\") // reset terminal\n\t\t\t}\n\t\t\tos.Stderr.WriteString(err.Error())\n\t\t\tos.Stderr.WriteString(\"\\n\")\n\t\t\tif is_terminal {\n\t\t\t\tif term, err := tty.OpenControllingTerm(tty.SetRaw); err == nil {\n\t\t\t\t\tdefer term.RestoreAndClose()\n\t\t\t\t\tterm.DebugPrintln(err.Error())\n\t\t\t\t\tfmt.Println(\"Press any key to exit.\\r\")\n\t\t\t\t\tbuf := make([]byte, 16)\n\t\t\t\t\t_, _ = term.Read(buf)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\treturn self.run()\n}\n\nfunc (self *Loop) WakeupMainThread() bool {\n\tselect {\n\tcase self.wakeup_channel <- 1:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (self *Loop) QueueWriteString(data string) IdType {\n\tself.write_msg_id_counter++\n\tmsg := write_msg{str: data, bytes: nil, id: self.write_msg_id_counter}\n\tself.add_write_to_pending_queue(msg)\n\treturn msg.id\n}\n\n// This is dangerous as it is upto the calling code\n// to ensure the data in the underlying array does not change\nfunc (self *Loop) UnsafeQueueWriteBytes(data []byte) IdType {\n\tself.write_msg_id_counter++\n\tmsg := write_msg{bytes: data, id: self.write_msg_id_counter}\n\tself.add_write_to_pending_queue(msg)\n\treturn msg.id\n}\n\nfunc (self *Loop) QueueWriteBytesCopy(data []byte) IdType {\n\td := make([]byte, len(data))\n\tcopy(d, data)\n\treturn self.UnsafeQueueWriteBytes(d)\n}\n\nfunc (self *Loop) ExitCode() int {\n\treturn self.exit_code\n}\n\nfunc (self *Loop) Beep() {\n\tself.QueueWriteString(\"\\a\")\n}\n\nfunc (self *Loop) StartAtomicUpdate() {\n\tif self.atomic_update_active {\n\t\tself.EndAtomicUpdate()\n\t}\n\tself.QueueWriteString(PENDING_UPDATE.EscapeCodeToSet())\n\tself.atomic_update_active = true\n}\n\nfunc (self *Loop) IsAtomicUpdateActive() bool { return self.atomic_update_active }\n\nfunc (self *Loop) EndAtomicUpdate() {\n\tif self.atomic_update_active {\n\t\tself.QueueWriteString(PENDING_UPDATE.EscapeCodeToReset())\n\t\tself.atomic_update_active = false\n\t}\n}\n\nfunc (self *Loop) SetCursorShape(shape CursorShapes, blink bool) {\n\tself.QueueWriteString(CursorShape(shape, blink))\n}\n\nfunc (self *Loop) SetCursorVisible(visible bool) {\n\tif visible {\n\t\tself.QueueWriteString(DECTCEM.EscapeCodeToSet())\n\t} else {\n\t\tself.QueueWriteString(DECTCEM.EscapeCodeToReset())\n\t}\n}\n\nconst MoveCursorToTemplate = \"\\x1b[%d;%dH\"\n\nfunc (self *Loop) MoveCursorTo(x, y int) { // 1, 1 is top left\n\tif x > 0 && y > 0 {\n\t\tself.QueueWriteString(fmt.Sprintf(MoveCursorToTemplate, y, x))\n\t}\n}\n\nfunc (self *Loop) MoveCursorHorizontally(amt int) {\n\tif amt != 0 {\n\t\tsuffix := \"C\"\n\t\tif amt < 0 {\n\t\t\tsuffix = \"D\"\n\t\t\tamt *= -1\n\t\t}\n\t\tself.QueueWriteString(fmt.Sprintf(\"\\x1b[%d%s\", amt, suffix))\n\t}\n}\n\nfunc (self *Loop) MoveCursorVertically(amt int) {\n\tif amt != 0 {\n\t\tsuffix := \"B\"\n\t\tif amt < 0 {\n\t\t\tsuffix = \"A\"\n\t\t\tamt *= -1\n\t\t}\n\t\tself.QueueWriteString(fmt.Sprintf(\"\\x1b[%d%s\", amt, suffix))\n\t}\n}\n\nfunc (self *Loop) ClearToEndOfScreen() {\n\tself.QueueWriteString(\"\\x1b[J\")\n}\n\nfunc (self *Loop) ClearToEndOfLine() {\n\tself.QueueWriteString(\"\\x1b[K\")\n}\n\nfunc (self *Loop) StartBracketedPaste() {\n\tself.QueueWriteString(BRACKETED_PASTE.EscapeCodeToSet())\n}\n\nfunc (self *Loop) EndBracketedPaste() {\n\tself.QueueWriteString(BRACKETED_PASTE.EscapeCodeToReset())\n}\n\nfunc (self *Loop) AllowLineWrapping(allow bool) {\n\tif allow {\n\t\tself.QueueWriteString(DECAWM.EscapeCodeToSet())\n\t} else {\n\t\tself.QueueWriteString(DECAWM.EscapeCodeToReset())\n\t}\n}\n\nfunc EscapeCodeToSetWindowTitle(title string) string {\n\ttitle = wcswidth.StripEscapeCodes(title)\n\treturn \"\\033]2;\" + title + \"\\033\\\\\"\n}\n\nfunc (self *Loop) SetWindowTitle(title string) {\n\tself.QueueWriteString(EscapeCodeToSetWindowTitle(title))\n}\n\nfunc (self *Loop) ClearScreen() {\n\tself.QueueWriteString(\"\\x1b[H\\x1b[2J\")\n}\n\nfunc (self *Loop) ClearScreenButNotGraphics() {\n\tself.QueueWriteString(\"\\x1b[H\\x1b[J\")\n}\n\nfunc (self *Loop) SendOverlayReady() {\n\tself.QueueWriteString(\"\\x1bP@kitty-overlay-ready|\\x1b\\\\\")\n}\n\nfunc (self *Loop) Quit(exit_code int) {\n\tself.exit_code = exit_code\n\tself.keep_going = false\n}\n\ntype DefaultColor int\n\nconst (\n\tBACKGROUND   DefaultColor = 11\n\tFOREGROUND   DefaultColor = 10\n\tCURSOR       DefaultColor = 12\n\tSELECTION_BG DefaultColor = 17\n\tSELECTION_FG DefaultColor = 19\n)\n\nfunc (self *Loop) SetDefaultColor(which DefaultColor, val style.RGBA) {\n\tself.QueueWriteString(fmt.Sprintf(\"\\033]%d;%s\\033\\\\\", int(which), val.AsRGBSharp()))\n}\n\nfunc (self *Loop) copy_text_to(text, dest string) {\n\tself.QueueWriteString(\"\\x1b]52;\" + dest + \";\")\n\tself.QueueWriteString(base64.StdEncoding.EncodeToString(utils.UnsafeStringToBytes(text)))\n\tself.QueueWriteString(\"\\x1b\\\\\")\n}\n\nfunc (self *Loop) CopyTextToPrimarySelection(text string) {\n\tself.copy_text_to(text, \"p\")\n}\n\nfunc (self *Loop) CopyTextToClipboard(text string) {\n\tself.copy_text_to(text, \"c\")\n}\n\nfunc (self *Loop) QueryTerminal(fields ...string) IdType {\n\tif len(fields) == 0 {\n\t\treturn 0\n\t}\n\tq := make([]string, len(fields))\n\tfor i, x := range fields {\n\t\tq[i] = hex.EncodeToString(utils.UnsafeStringToBytes(\"kitty-query-\" + x))\n\t}\n\treturn self.QueueWriteString(fmt.Sprintf(\"\\x1bP+q%s\\a\", strings.Join(q, \";\")))\n}\n\nfunc (self *Loop) PushPointerShape(s PointerShape) {\n\tself.pointer_shapes = append(self.pointer_shapes, s)\n\tself.QueueWriteString(\"\\x1b]22;\" + s.String() + \"\\x1b\\\\\")\n}\n\nfunc (self *Loop) PopPointerShape() {\n\tif len(self.pointer_shapes) > 0 {\n\t\tself.pointer_shapes = self.pointer_shapes[:len(self.pointer_shapes)-1]\n\t\tself.QueueWriteString(\"\\x1b]22;<\\x1b\\\\\")\n\t}\n}\n\n// Remove all pointer shapes from the shape stack resetting to default pointer\n// shape. This is called automatically on loop termination.\nfunc (self *Loop) ClearPointerShapes() (ans []PointerShape) {\n\tans = self.pointer_shapes\n\tfor i := len(self.pointer_shapes) - 1; i >= 0; i-- {\n\t\tself.QueueWriteString(\"\\x1b]22;<\\x1b\\\\\")\n\t}\n\tself.pointer_shapes = nil\n\treturn ans\n}\n\nfunc (self *Loop) CurrentPointerShape() (ans PointerShape, has_shape bool) {\n\tif len(self.pointer_shapes) > 0 {\n\t\thas_shape = true\n\t\tans = self.pointer_shapes[len(self.pointer_shapes)-1]\n\t}\n\treturn\n}\n\n// Query the terminal for various capabilities, the OnCapabilitiesReceived\n// callback will be called once the query response is received. This\n// function should be called as early as possible ideally in OnInitialize.\nfunc (self *Loop) QueryCapabilities() {\n\tif !self.waiting_for_capabilities_response {\n\t\tself.waiting_for_capabilities_response = true\n\t\tself.StartAtomicUpdate()\n\t\tself.QueueWriteString(\"\\x1b[?u\\x1b[?996n\\x1b[c\")\n\t\tself.EndAtomicUpdate()\n\t}\n}\n\ntype Alignment int\n\nconst (\n\tALIGN_START Alignment = iota\n\tALIGN_CENTER\n\tALIGN_END\n)\n\ntype SizedText struct {\n\tScale, Subscale_numerator, Subscale_denominator int\n\tHorizontal_alignment, Vertical_alignment        Alignment\n\tWidth                                           int\n}\n\nfunc (self *Loop) RecoverFromPanicInGoRoutine() {\n\tif r := recover(); r != nil {\n\t\terr := parallel.Format_stacktrace_on_panic(r, 1)\n\t\t// print to kitty stdout as multiple go routines might panic but only\n\t\t// one panic is reported by the main loop panic_channel\n\t\tif f := tty.KittyStdout(); f != nil {\n\t\t\tfmt.Fprintln(f, err)\n\t\t}\n\t\tself.panic_channel <- err\n\t}\n}\n\nfunc (self *Loop) DrawSizedText(text string, spec SizedText) {\n\tb := strings.Builder{}\n\tb.Grow(len(text) + 24)\n\tb.WriteString(\"\\x1b]66;\")\n\tsep := \"\"\n\tif spec.Scale > 1 {\n\t\tb.WriteString(fmt.Sprintf(\"%ss=%d\", sep, min(spec.Scale, 7)))\n\t\tsep = \":\"\n\t}\n\tif spec.Width > 0 {\n\t\tb.WriteString(fmt.Sprintf(\"%sw=%d\", sep, min(spec.Width, 7)))\n\t\tsep = \":\"\n\t}\n\tif spec.Subscale_numerator > 0 {\n\t\tb.WriteString(fmt.Sprintf(\"%sn=%d\", sep, min(spec.Subscale_numerator, 15)))\n\t\tsep = \":\"\n\t}\n\tif spec.Subscale_denominator > spec.Subscale_numerator {\n\t\tb.WriteString(fmt.Sprintf(\"%sd=%d\", sep, min(spec.Subscale_denominator, 15)))\n\t\tsep = \":\"\n\t}\n\tif spec.Horizontal_alignment > ALIGN_START {\n\t\tb.WriteString(fmt.Sprintf(\"%sh=%d\", sep, spec.Horizontal_alignment))\n\t\tsep = \":\"\n\t}\n\tif spec.Vertical_alignment > ALIGN_START {\n\t\tb.WriteString(fmt.Sprintf(\"%sv=%d\", sep, spec.Vertical_alignment))\n\t\tsep = \":\"\n\t}\n\tb.WriteString(\";\")\n\tb.WriteString(text)\n\tb.WriteString(\"\\a\")\n\tself.QueueWriteString(b.String())\n}\n"
  },
  {
    "path": "tools/tui/loop/capabilities.go",
    "content": "package loop\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\ntype ColorPreference uint8\n\nconst (\n\tNO_COLOR_PREFERENCE ColorPreference = iota\n\tDARK_COLOR_PREFERENCE\n\tLIGHT_COLOR_PREFERENCE\n)\n\nfunc (c ColorPreference) String() string {\n\tswitch c {\n\tcase DARK_COLOR_PREFERENCE:\n\t\treturn \"dark\"\n\tcase LIGHT_COLOR_PREFERENCE:\n\t\treturn \"light\"\n\tdefault:\n\t\treturn \"no-preference\"\n\t}\n}\n\ntype TerminalCapabilities struct {\n\tKeyboardProtocol                 bool\n\tKeyboardProtocolResponseReceived bool\n\n\tColorPreference                 ColorPreference\n\tColorPreferenceResponseReceived bool\n}\n"
  },
  {
    "path": "tools/tui/loop/key-encoding.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty\"\n)\n\n// key encoding mappings {{{\n// start csi mapping (auto generated by gen-key-constants.py do not edit)\nvar functional_key_number_to_name_map = map[int]string{57344: \"ESCAPE\", 57345: \"ENTER\", 57346: \"TAB\", 57347: \"BACKSPACE\", 57348: \"INSERT\", 57349: \"DELETE\", 57350: \"LEFT\", 57351: \"RIGHT\", 57352: \"UP\", 57353: \"DOWN\", 57354: \"PAGE_UP\", 57355: \"PAGE_DOWN\", 57356: \"HOME\", 57357: \"END\", 57358: \"CAPS_LOCK\", 57359: \"SCROLL_LOCK\", 57360: \"NUM_LOCK\", 57361: \"PRINT_SCREEN\", 57362: \"PAUSE\", 57363: \"MENU\", 57364: \"F1\", 57365: \"F2\", 57366: \"F3\", 57367: \"F4\", 57368: \"F5\", 57369: \"F6\", 57370: \"F7\", 57371: \"F8\", 57372: \"F9\", 57373: \"F10\", 57374: \"F11\", 57375: \"F12\", 57376: \"F13\", 57377: \"F14\", 57378: \"F15\", 57379: \"F16\", 57380: \"F17\", 57381: \"F18\", 57382: \"F19\", 57383: \"F20\", 57384: \"F21\", 57385: \"F22\", 57386: \"F23\", 57387: \"F24\", 57388: \"F25\", 57389: \"F26\", 57390: \"F27\", 57391: \"F28\", 57392: \"F29\", 57393: \"F30\", 57394: \"F31\", 57395: \"F32\", 57396: \"F33\", 57397: \"F34\", 57398: \"F35\", 57399: \"KP_0\", 57400: \"KP_1\", 57401: \"KP_2\", 57402: \"KP_3\", 57403: \"KP_4\", 57404: \"KP_5\", 57405: \"KP_6\", 57406: \"KP_7\", 57407: \"KP_8\", 57408: \"KP_9\", 57409: \"KP_DECIMAL\", 57410: \"KP_DIVIDE\", 57411: \"KP_MULTIPLY\", 57412: \"KP_SUBTRACT\", 57413: \"KP_ADD\", 57414: \"KP_ENTER\", 57415: \"KP_EQUAL\", 57416: \"KP_SEPARATOR\", 57417: \"KP_LEFT\", 57418: \"KP_RIGHT\", 57419: \"KP_UP\", 57420: \"KP_DOWN\", 57421: \"KP_PAGE_UP\", 57422: \"KP_PAGE_DOWN\", 57423: \"KP_HOME\", 57424: \"KP_END\", 57425: \"KP_INSERT\", 57426: \"KP_DELETE\", 57427: \"KP_BEGIN\", 57428: \"MEDIA_PLAY\", 57429: \"MEDIA_PAUSE\", 57430: \"MEDIA_PLAY_PAUSE\", 57431: \"MEDIA_REVERSE\", 57432: \"MEDIA_STOP\", 57433: \"MEDIA_FAST_FORWARD\", 57434: \"MEDIA_REWIND\", 57435: \"MEDIA_TRACK_NEXT\", 57436: \"MEDIA_TRACK_PREVIOUS\", 57437: \"MEDIA_RECORD\", 57438: \"LOWER_VOLUME\", 57439: \"RAISE_VOLUME\", 57440: \"MUTE_VOLUME\", 57441: \"LEFT_SHIFT\", 57442: \"LEFT_CONTROL\", 57443: \"LEFT_ALT\", 57444: \"LEFT_SUPER\", 57445: \"LEFT_HYPER\", 57446: \"LEFT_META\", 57447: \"RIGHT_SHIFT\", 57448: \"RIGHT_CONTROL\", 57449: \"RIGHT_ALT\", 57450: \"RIGHT_SUPER\", 57451: \"RIGHT_HYPER\", 57452: \"RIGHT_META\", 57453: \"ISO_LEVEL3_SHIFT\", 57454: \"ISO_LEVEL5_SHIFT\"}\n\nvar csi_number_to_functional_number_map = map[int]int{2: 57348, 3: 57349, 5: 57354, 6: 57355, 7: 57356, 8: 57357, 9: 57346, 11: 57364, 12: 57365, 13: 57345, 14: 57367, 15: 57368, 17: 57369, 18: 57370, 19: 57371, 20: 57372, 21: 57373, 23: 57374, 24: 57375, 27: 57344, 127: 57347}\n\nvar letter_trailer_to_csi_number_map = map[string]int{\"A\": 57352, \"B\": 57353, \"C\": 57351, \"D\": 57350, \"E\": 57427, \"F\": 8, \"H\": 7, \"P\": 11, \"Q\": 12, \"S\": 14}\n\nvar tilde_trailers = map[int]bool{57348: true, 57349: true, 57354: true, 57355: true, 57366: true, 57368: true, 57369: true, 57370: true, 57371: true, 57372: true, 57373: true, 57374: true, 57375: true}\n\n// end csi mapping\n// }}}\n\nvar name_to_functional_number_map map[string]int\nvar functional_to_csi_number_map map[int]int\nvar csi_number_to_letter_trailer_map map[int]string\n\ntype KeyEventType uint8\ntype KeyModifiers uint16\n\nconst (\n\tPRESS   KeyEventType = 1\n\tREPEAT  KeyEventType = 2\n\tRELEASE KeyEventType = 4\n)\n\nconst (\n\tSHIFT     KeyModifiers = 1\n\tALT       KeyModifiers = 2\n\tCTRL      KeyModifiers = 4\n\tSUPER     KeyModifiers = 8\n\tHYPER     KeyModifiers = 16\n\tMETA      KeyModifiers = 32\n\tCAPS_LOCK KeyModifiers = 64\n\tNUM_LOCK  KeyModifiers = 128\n)\n\nfunc (self KeyModifiers) WithoutLocks() KeyModifiers {\n\treturn self & ^(CAPS_LOCK | NUM_LOCK)\n}\n\nfunc (self KeyEventType) String() string {\n\tswitch self {\n\tcase PRESS:\n\t\treturn \"PRESS\"\n\tcase REPEAT:\n\t\treturn \"REPEAT\"\n\tcase RELEASE:\n\t\treturn \"RELEASE\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"KeyEventType:%d\", int(self))\n\t}\n}\n\nfunc (self KeyModifiers) String() string {\n\tans := make([]string, 0)\n\tif self&SHIFT != 0 {\n\t\tans = append(ans, \"shift\")\n\t}\n\tif self&ALT != 0 {\n\t\tans = append(ans, \"alt\")\n\t}\n\tif self&CTRL != 0 {\n\t\tans = append(ans, \"ctrl\")\n\t}\n\tif self&SUPER != 0 {\n\t\tans = append(ans, \"super\")\n\t}\n\tif self&HYPER != 0 {\n\t\tans = append(ans, \"hyper\")\n\t}\n\tif self&META != 0 {\n\t\tans = append(ans, \"meta\")\n\t}\n\tif self&CAPS_LOCK != 0 {\n\t\tans = append(ans, \"caps_lock\")\n\t}\n\tif self&NUM_LOCK != 0 {\n\t\tans = append(ans, \"num_lock\")\n\t}\n\treturn strings.Join(ans, \"+\")\n}\n\nfunc (self KeyModifiers) HasCapsLock() bool {\n\treturn self&CAPS_LOCK != 0\n}\n\ntype KeyEvent struct {\n\tType         KeyEventType\n\tMods         KeyModifiers\n\tKey          string\n\tShiftedKey   string\n\tAlternateKey string\n\tText         string\n\tHandled      bool\n\n\t// The CSI string this key event was decoded from. Empty if not decoded from CSI.\n\tCSI string\n}\n\nfunc (self *KeyEvent) String() string {\n\tkey := self.Key\n\tif self.Mods > 0 {\n\t\tkey = self.Mods.String() + \"+\" + key\n\t}\n\tans := fmt.Sprint(self.Type, \"{ \", key, \" \")\n\tif self.Text != \"\" {\n\t\tans += \"Text: \" + self.Text + \" \"\n\t}\n\tif self.ShiftedKey != \"\" {\n\t\tans += \"ShiftedKey: \" + self.ShiftedKey + \" \"\n\t}\n\tif self.AlternateKey != \"\" {\n\t\tans += \"AlternateKey: \" + self.AlternateKey + \" \"\n\t}\n\treturn ans + \"}\"\n}\n\nfunc (self *KeyEvent) HasCapsLock() bool {\n\treturn self.Mods.HasCapsLock()\n}\n\nfunc KeyEventFromCSI(csi string) *KeyEvent {\n\tif len(csi) == 0 {\n\t\treturn nil\n\t}\n\torig_csi := csi\n\tlast_char := csi[len(csi)-1:]\n\tif !strings.Contains(\"u~ABCDEHFPQS\", last_char) || (last_char == \"~\" && (csi == \"200~\" || csi == \"201~\")) {\n\t\treturn nil\n\t}\n\tcsi = csi[:len(csi)-1]\n\tsections := strings.Split(csi, \";\")\n\n\tget_sub_sections := func(section string, missing int32) []int32 {\n\t\tp := strings.Split(section, \":\")\n\t\tans := make([]int32, len(p))\n\t\tfor i, x := range p {\n\t\t\tif x == \"\" {\n\t\t\t\tans[i] = missing\n\t\t\t} else {\n\t\t\t\tq, err := strconv.ParseUint(x, 10, 32)\n\t\t\t\tif err != nil || q > math.MaxInt32 {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tans[i] = int32(q)\n\t\t\t}\n\t\t}\n\t\treturn ans\n\t}\n\tfirst_section := get_sub_sections(sections[0], 0)\n\tsecond_section := []int32{}\n\tthird_section := []int32{}\n\tif len(sections) > 1 {\n\t\tsecond_section = get_sub_sections(sections[1], 1)\n\t}\n\tif len(sections) > 2 {\n\t\tthird_section = get_sub_sections(sections[2], 0)\n\t}\n\tvar ans = KeyEvent{Type: PRESS, CSI: orig_csi}\n\tvar keynum int32\n\tif val, ok := letter_trailer_to_csi_number_map[last_char]; ok {\n\t\tkeynum = int32(val)\n\t} else {\n\t\tif len(first_section) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tkeynum = first_section[0]\n\t}\n\n\tkey_name := func(keynum int32) string {\n\t\tswitch keynum {\n\t\tcase 0:\n\t\t\treturn \"\"\n\t\tcase 13:\n\t\t\tif last_char == \"u\" {\n\t\t\t\treturn \"ENTER\"\n\t\t\t}\n\t\t\treturn \"F3\"\n\t\tdefault:\n\t\t\tif val, ok := csi_number_to_functional_number_map[int(keynum)]; ok {\n\t\t\t\tkeynum = int32(val)\n\t\t\t}\n\t\t\tans := \"\"\n\t\t\tif val, ok := functional_key_number_to_name_map[int(keynum)]; ok {\n\t\t\t\tans = val\n\t\t\t} else {\n\t\t\t\tans = string(rune(keynum))\n\t\t\t}\n\t\t\treturn ans\n\t\t}\n\t}\n\n\tans.Key = key_name(keynum)\n\tif len(first_section) > 1 {\n\t\tans.ShiftedKey = key_name(first_section[1])\n\t}\n\tif len(first_section) > 2 {\n\t\tans.AlternateKey = key_name(first_section[2])\n\t}\n\tif len(second_section) > 0 {\n\t\tans.Mods = KeyModifiers(second_section[0] - 1)\n\t}\n\tif len(second_section) > 1 {\n\t\tswitch second_section[1] {\n\t\tcase 2:\n\t\t\tans.Type = REPEAT\n\t\tcase 3:\n\t\t\tans.Type = RELEASE\n\t\t}\n\t}\n\tif len(third_section) > 0 {\n\t\trunes := make([]rune, len(third_section))\n\t\tfor i, ch := range third_section {\n\t\t\trunes[i] = rune(ch)\n\t\t}\n\t\tans.Text = string(runes)\n\t}\n\treturn &ans\n}\n\ntype ParsedShortcut struct {\n\tMods    KeyModifiers\n\tKeyName string\n}\n\nfunc (self *ParsedShortcut) String() string {\n\tans := self.KeyName\n\tif self.Mods > 0 {\n\t\tans = self.Mods.String() + \"+\" + ans\n\t}\n\treturn ans\n}\n\nvar parsed_shortcut_cache map[string]*ParsedShortcut\n\nfunc ParseShortcut(spec string) *ParsedShortcut {\n\tif parsed_shortcut_cache == nil {\n\t\tparsed_shortcut_cache = make(map[string]*ParsedShortcut, 128)\n\t}\n\tif val, ok := parsed_shortcut_cache[spec]; ok {\n\t\treturn val\n\t}\n\tospec := spec\n\tif strings.HasSuffix(spec, \"+\") {\n\t\tospec = spec[:len(spec)-1] + \"plus\"\n\t}\n\tparts := strings.Split(ospec, \"+\")\n\tkey_name := parts[len(parts)-1]\n\tif val, ok := kitty.FunctionalKeyNameAliases[strings.ToUpper(key_name)]; ok {\n\t\tkey_name = val\n\t}\n\tif _, is_functional_key := name_to_functional_number_map[strings.ToUpper(key_name)]; is_functional_key {\n\t\tkey_name = strings.ToUpper(key_name)\n\t} else {\n\t\tif val, ok := kitty.CharacterKeyNameAliases[strings.ToUpper(key_name)]; ok {\n\t\t\tkey_name = val\n\t\t}\n\t}\n\tans := ParsedShortcut{KeyName: key_name}\n\tif len(parts) > 1 {\n\t\tfor _, q := range parts[:len(parts)-1] {\n\t\t\tval, ok := kitty.ConfigModMap[strings.ToUpper(q)]\n\t\t\tif ok {\n\t\t\t\tans.Mods |= KeyModifiers(val)\n\t\t\t} else {\n\t\t\t\tans.Mods |= META << 8\n\t\t\t}\n\t\t}\n\t}\n\tparsed_shortcut_cache[spec] = &ans\n\treturn &ans\n}\n\nfunc (self *KeyEvent) MatchesParsedShortcut(ps *ParsedShortcut, event_type KeyEventType) bool {\n\tif self.Type&event_type == 0 {\n\t\treturn false\n\t}\n\tmods := self.Mods.WithoutLocks()\n\tif mods == ps.Mods && self.Key == ps.KeyName {\n\t\treturn true\n\t}\n\tif self.ShiftedKey != \"\" && mods&SHIFT != 0 && (mods & ^SHIFT) == ps.Mods && self.ShiftedKey == ps.KeyName {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (self *KeyEvent) Matches(spec string, event_type KeyEventType) bool {\n\treturn self.MatchesParsedShortcut(ParseShortcut(spec), event_type)\n}\n\nfunc (self *KeyEvent) MatchesPressOrRepeat(spec string) bool {\n\treturn self.MatchesParsedShortcut(ParseShortcut(spec), PRESS|REPEAT)\n}\n\nfunc (self *KeyEvent) MatchesCaseSensitiveTextOrKey(spec string) bool {\n\tif self.MatchesParsedShortcut(ParseShortcut(spec), PRESS|REPEAT) {\n\t\treturn true\n\t}\n\treturn self.Text == spec\n}\n\nfunc (self *KeyEvent) MatchesCaseInsensitiveTextOrKey(spec string) bool {\n\tif self.MatchesParsedShortcut(ParseShortcut(spec), PRESS|REPEAT) {\n\t\treturn true\n\t}\n\treturn strings.EqualFold(self.Text, spec)\n}\n\nfunc (self *KeyEvent) MatchesRelease(spec string) bool {\n\treturn self.MatchesParsedShortcut(ParseShortcut(spec), RELEASE)\n}\n\nfunc (self *KeyEvent) AsCSI() string {\n\tkey := csi_number_for_name(self.Key)\n\tshifted_key := csi_number_for_name(self.ShiftedKey)\n\talternate_key := csi_number_for_name(self.AlternateKey)\n\ttrailer, found := csi_number_to_letter_trailer_map[key]\n\tif !found {\n\t\ttrailer = \"u\"\n\t}\n\tif self.Key == \"ENTER\" {\n\t\ttrailer = \"u\"\n\t}\n\tif trailer != \"u\" {\n\t\tkey = 1\n\t}\n\tans := strings.Builder{}\n\tans.Grow(32)\n\tans.WriteString(\"\\033[\")\n\tif key != 1 || self.Mods != 0 || shifted_key != 0 || alternate_key != 0 || self.Text != \"\" {\n\t\tans.WriteString(fmt.Sprint(key))\n\t}\n\tif shifted_key != 0 || alternate_key != 0 {\n\t\tans.WriteString(\":\")\n\t\tif shifted_key != 0 {\n\t\t\tans.WriteString(fmt.Sprint(shifted_key))\n\t\t}\n\t\tif alternate_key != 0 {\n\t\t\tans.WriteString(fmt.Sprint(\":\", alternate_key))\n\t\t}\n\t}\n\taction := 1\n\tswitch self.Type {\n\tcase REPEAT:\n\t\taction = 2\n\tcase RELEASE:\n\t\taction = 3\n\t}\n\tif self.Mods != 0 || action > 1 || self.Text != \"\" {\n\t\tm := uint(self.Mods)\n\t\tif action > 1 || m != 0 {\n\t\t\tans.WriteString(fmt.Sprintf(\";%d\", m+1))\n\t\t\tif action > 1 {\n\t\t\t\tans.WriteString(fmt.Sprintf(\":%d\", action))\n\t\t\t}\n\t\t} else if self.Text != \"\" {\n\t\t\tans.WriteString(\";\")\n\t\t}\n\t}\n\tif self.Text != \"\" {\n\t\trunes := []rune(self.Text)\n\t\tcodes := make([]string, len(runes))\n\t\tfor i, r := range runes {\n\t\t\tcodes[i] = strconv.Itoa(int(r))\n\t\t}\n\t\tans.WriteString(\";\")\n\t\tans.WriteString(strings.Join(codes, \":\"))\n\t}\n\tfn, found := name_to_functional_number_map[self.Key]\n\tif found && tilde_trailers[fn] {\n\t\ttrailer = \"~\"\n\t}\n\tans.WriteString(trailer)\n\treturn ans.String()\n}\n\nfunc csi_number_for_name(key_name string) int {\n\tif key_name == \"\" {\n\t\treturn 0\n\t}\n\tif key_name == \"F3\" || key_name == \"ENTER\" {\n\t\treturn 13\n\t}\n\tfn, ok := name_to_functional_number_map[key_name]\n\tif !ok {\n\t\treturn int(rune(key_name[0]))\n\t}\n\tans, ok := functional_to_csi_number_map[fn]\n\tif ok {\n\t\treturn ans\n\t}\n\treturn fn\n}\n\nfunc init() {\n\tname_to_functional_number_map = make(map[string]int, len(functional_key_number_to_name_map))\n\tfor k, v := range functional_key_number_to_name_map {\n\t\tname_to_functional_number_map[v] = k\n\t}\n\tfunctional_to_csi_number_map = make(map[int]int, len(csi_number_to_functional_number_map))\n\tfor k, v := range csi_number_to_functional_number_map {\n\t\tfunctional_to_csi_number_map[v] = k\n\t}\n\tcsi_number_to_letter_trailer_map = make(map[int]string, len(letter_trailer_to_csi_number_map))\n\tfor k, v := range letter_trailer_to_csi_number_map {\n\t\tcsi_number_to_letter_trailer_map[v] = k\n\t}\n}\n"
  },
  {
    "path": "tools/tui/loop/key-encoding_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestKeyEventFromCSI(t *testing.T) {\n\n\ttest_text := func(csi string, expected, alternate string) {\n\t\tev := KeyEventFromCSI(csi)\n\t\tif ev == nil {\n\t\t\tt.Fatalf(\"Failed to get parse %#v\", csi)\n\t\t}\n\t\tif diff := cmp.Diff(expected, ev.Text); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to get text from %#v:\\n%s\", csi, diff)\n\t\t}\n\t\tif diff := cmp.Diff(alternate, ev.AlternateKey); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to get alternate from %#v:\\n%s\", csi, diff)\n\t\t}\n\t}\n\ttest_text(\"121;;121u\", \"y\", \"\")\n\ttest_text(\"121::122;;121u\", \"y\", \"z\")\n}\n"
  },
  {
    "path": "tools/tui/loop/mouse.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\ntype MouseEventType uint\ntype MouseButtonFlag uint\n\nconst (\n\tMOUSE_PRESS MouseEventType = iota\n\tMOUSE_RELEASE\n\tMOUSE_MOVE\n\tMOUSE_CLICK\n\tMOUSE_LEAVE\n)\n\nfunc (e MouseEventType) String() string {\n\tswitch e {\n\tcase MOUSE_PRESS:\n\t\treturn \"press\"\n\tcase MOUSE_RELEASE:\n\t\treturn \"release\"\n\tcase MOUSE_MOVE:\n\t\treturn \"move\"\n\tcase MOUSE_CLICK:\n\t\treturn \"click\"\n\t}\n\treturn strconv.Itoa(int(e))\n}\n\ntype PointerShape uint8\n\nconst (\n\t// start pointer shape enum (auto generated by gen-key-constants.py do not edit)\n\tDEFAULT_POINTER       PointerShape = 0\n\tTEXT_POINTER          PointerShape = 1\n\tPOINTER_POINTER       PointerShape = 2\n\tHELP_POINTER          PointerShape = 3\n\tWAIT_POINTER          PointerShape = 4\n\tPROGRESS_POINTER      PointerShape = 5\n\tCROSSHAIR_POINTER     PointerShape = 6\n\tCELL_POINTER          PointerShape = 7\n\tVERTICAL_TEXT_POINTER PointerShape = 8\n\tMOVE_POINTER          PointerShape = 9\n\tE_RESIZE_POINTER      PointerShape = 10\n\tNE_RESIZE_POINTER     PointerShape = 11\n\tNW_RESIZE_POINTER     PointerShape = 12\n\tN_RESIZE_POINTER      PointerShape = 13\n\tSE_RESIZE_POINTER     PointerShape = 14\n\tSW_RESIZE_POINTER     PointerShape = 15\n\tS_RESIZE_POINTER      PointerShape = 16\n\tW_RESIZE_POINTER      PointerShape = 17\n\tEW_RESIZE_POINTER     PointerShape = 18\n\tNS_RESIZE_POINTER     PointerShape = 19\n\tNESW_RESIZE_POINTER   PointerShape = 20\n\tNWSE_RESIZE_POINTER   PointerShape = 21\n\tZOOM_IN_POINTER       PointerShape = 22\n\tZOOM_OUT_POINTER      PointerShape = 23\n\tALIAS_POINTER         PointerShape = 24\n\tCOPY_POINTER          PointerShape = 25\n\tNOT_ALLOWED_POINTER   PointerShape = 26\n\tNO_DROP_POINTER       PointerShape = 27\n\tGRAB_POINTER          PointerShape = 28\n\tGRABBING_POINTER      PointerShape = 29\n\n// end pointer shape enum\n)\n\nfunc (e PointerShape) String() string {\n\tswitch e {\n\t// start pointer shape tostring (auto generated by gen-key-constants.py do not edit)\n\tcase DEFAULT_POINTER:\n\t\treturn \"default\"\n\tcase TEXT_POINTER:\n\t\treturn \"text\"\n\tcase POINTER_POINTER:\n\t\treturn \"pointer\"\n\tcase HELP_POINTER:\n\t\treturn \"help\"\n\tcase WAIT_POINTER:\n\t\treturn \"wait\"\n\tcase PROGRESS_POINTER:\n\t\treturn \"progress\"\n\tcase CROSSHAIR_POINTER:\n\t\treturn \"crosshair\"\n\tcase CELL_POINTER:\n\t\treturn \"cell\"\n\tcase VERTICAL_TEXT_POINTER:\n\t\treturn \"vertical-text\"\n\tcase MOVE_POINTER:\n\t\treturn \"move\"\n\tcase E_RESIZE_POINTER:\n\t\treturn \"e-resize\"\n\tcase NE_RESIZE_POINTER:\n\t\treturn \"ne-resize\"\n\tcase NW_RESIZE_POINTER:\n\t\treturn \"nw-resize\"\n\tcase N_RESIZE_POINTER:\n\t\treturn \"n-resize\"\n\tcase SE_RESIZE_POINTER:\n\t\treturn \"se-resize\"\n\tcase SW_RESIZE_POINTER:\n\t\treturn \"sw-resize\"\n\tcase S_RESIZE_POINTER:\n\t\treturn \"s-resize\"\n\tcase W_RESIZE_POINTER:\n\t\treturn \"w-resize\"\n\tcase EW_RESIZE_POINTER:\n\t\treturn \"ew-resize\"\n\tcase NS_RESIZE_POINTER:\n\t\treturn \"ns-resize\"\n\tcase NESW_RESIZE_POINTER:\n\t\treturn \"nesw-resize\"\n\tcase NWSE_RESIZE_POINTER:\n\t\treturn \"nwse-resize\"\n\tcase ZOOM_IN_POINTER:\n\t\treturn \"zoom-in\"\n\tcase ZOOM_OUT_POINTER:\n\t\treturn \"zoom-out\"\n\tcase ALIAS_POINTER:\n\t\treturn \"alias\"\n\tcase COPY_POINTER:\n\t\treturn \"copy\"\n\tcase NOT_ALLOWED_POINTER:\n\t\treturn \"not-allowed\"\n\tcase NO_DROP_POINTER:\n\t\treturn \"no-drop\"\n\tcase GRAB_POINTER:\n\t\treturn \"grab\"\n\tcase GRABBING_POINTER:\n\t\treturn \"grabbing\"\n\t\t// end pointer shape tostring\n\t}\n\treturn strconv.Itoa(int(e))\n}\n\nconst (\n\tSHIFT_INDICATOR         int = 1 << 2\n\tALT_INDICATOR               = 1 << 3\n\tCTRL_INDICATOR              = 1 << 4\n\tMOTION_INDICATOR            = 1 << 5\n\tSCROLL_BUTTON_INDICATOR     = 1 << 6\n\tEXTRA_BUTTON_INDICATOR      = 1 << 7\n\tLEAVE_INDICATOR             = 1 << 8\n)\n\nconst (\n\tNO_MOUSE_BUTTON   MouseButtonFlag = 0\n\tLEFT_MOUSE_BUTTON MouseButtonFlag = 1 << iota\n\tMIDDLE_MOUSE_BUTTON\n\tRIGHT_MOUSE_BUTTON\n\tFOURTH_MOUSE_BUTTON\n\tFIFTH_MOUSE_BUTTON\n\tSIXTH_MOUSE_BUTTON\n\tSEVENTH_MOUSE_BUTTON\n\tMOUSE_WHEEL_UP\n\tMOUSE_WHEEL_DOWN\n\tMOUSE_WHEEL_LEFT\n\tMOUSE_WHEEL_RIGHT\n)\n\nvar bmap = [...]MouseButtonFlag{LEFT_MOUSE_BUTTON, MIDDLE_MOUSE_BUTTON, RIGHT_MOUSE_BUTTON}\nvar ebmap = [...]MouseButtonFlag{FOURTH_MOUSE_BUTTON, FIFTH_MOUSE_BUTTON, SIXTH_MOUSE_BUTTON, SEVENTH_MOUSE_BUTTON}\nvar wbmap = [...]MouseButtonFlag{MOUSE_WHEEL_UP, MOUSE_WHEEL_DOWN, MOUSE_WHEEL_LEFT, MOUSE_WHEEL_RIGHT}\n\nfunc (b MouseButtonFlag) String() string {\n\tans := \"\"\n\tswitch {\n\tcase b&LEFT_MOUSE_BUTTON != 0:\n\t\tans += \"|LEFT\"\n\tcase b&MIDDLE_MOUSE_BUTTON != 0:\n\t\tans += \"|MIDDLE\"\n\tcase b&RIGHT_MOUSE_BUTTON != 0:\n\t\tans += \"|RIGHT\"\n\tcase b&FOURTH_MOUSE_BUTTON != 0:\n\t\tans += \"|FOURTH\"\n\tcase b&FIFTH_MOUSE_BUTTON != 0:\n\t\tans += \"|FIFTH\"\n\tcase b&SIXTH_MOUSE_BUTTON != 0:\n\t\tans += \"|SIXTH\"\n\tcase b&SEVENTH_MOUSE_BUTTON != 0:\n\t\tans += \"|SEVENTH\"\n\tcase b&MOUSE_WHEEL_UP != 0:\n\t\tans += \"|WHEEL_UP\"\n\tcase b&MOUSE_WHEEL_DOWN != 0:\n\t\tans += \"|WHEEL_DOWN\"\n\tcase b&MOUSE_WHEEL_LEFT != 0:\n\t\tans += \"|WHEEL_LEFT\"\n\tcase b&MOUSE_WHEEL_RIGHT != 0:\n\t\tans += \"|WHEEL_RIGHT\"\n\t}\n\tans = strings.TrimLeft(ans, \"|\")\n\tif ans == \"\" {\n\t\tans = \"NONE\"\n\t}\n\treturn ans\n}\n\ntype MouseEvent struct {\n\tEvent_type  MouseEventType\n\tButtons     MouseButtonFlag\n\tMods        KeyModifiers\n\tCell, Pixel struct{ X, Y int }\n}\n\nfunc (e MouseEvent) String() string {\n\treturn fmt.Sprintf(\"MouseEvent{%s %s %s Cell:%v Pixel:%v}\", e.Event_type, e.Buttons, e.Mods, e.Cell, e.Pixel)\n}\n\nfunc pixel_to_cell(px, length, cell_length int) int {\n\tpx = max(0, min(px, length-1))\n\tif cell_length > 0 {\n\t\treturn px / cell_length\n\t}\n\treturn 0\n}\n\nfunc decode_sgr_mouse(text string, screen_size ScreenSize, last_letter byte) *MouseEvent {\n\ttext = text[:len(text)-1]\n\tparts := strings.Split(text, \";\")\n\tif len(parts) != 3 {\n\t\treturn nil\n\t}\n\tcb, err := strconv.Atoi(parts[0])\n\tif err != nil {\n\t\treturn nil\n\t}\n\tans := MouseEvent{}\n\tans.Pixel.X, err = strconv.Atoi(parts[1])\n\tif err != nil {\n\t\treturn nil\n\t}\n\tif len(parts[2]) < 1 {\n\t\treturn nil\n\t}\n\tif ans.Pixel.Y, err = strconv.Atoi(parts[2]); err != nil {\n\t\treturn nil\n\t}\n\tif last_letter == 'm' {\n\t\tans.Event_type = MOUSE_RELEASE\n\t} else if cb&MOTION_INDICATOR != 0 {\n\t\tans.Event_type = MOUSE_MOVE\n\t}\n\tcb3 := cb & 3\n\tswitch {\n\tcase cb&LEAVE_INDICATOR != 0:\n\t\tans.Event_type = MOUSE_LEAVE\n\tcase cb&EXTRA_BUTTON_INDICATOR != 0:\n\t\tans.Buttons |= ebmap[cb3]\n\tcase cb&SCROLL_BUTTON_INDICATOR != 0:\n\t\tans.Buttons |= wbmap[cb3]\n\tcase cb3 < 3:\n\t\tans.Buttons |= bmap[cb3]\n\t}\n\tif cb&SHIFT_INDICATOR != 0 {\n\t\tans.Mods |= SHIFT\n\t}\n\tif cb&ALT_INDICATOR != 0 {\n\t\tans.Mods |= ALT\n\t}\n\tif cb&CTRL_INDICATOR != 0 {\n\t\tans.Mods |= CTRL\n\t}\n\tans.Cell.X = pixel_to_cell(ans.Pixel.X, int(screen_size.WidthPx), int(screen_size.CellWidth))\n\tans.Cell.Y = pixel_to_cell(ans.Pixel.Y, int(screen_size.HeightPx), int(screen_size.CellHeight))\n\n\treturn &ans\n}\n\nfunc MouseEventFromCSI(csi string, screen_size ScreenSize) *MouseEvent {\n\tif len(csi) == 0 {\n\t\treturn nil\n\t}\n\tlast_char := csi[len(csi)-1]\n\tif last_char != 'm' && last_char != 'M' {\n\t\treturn nil\n\t}\n\tswitch csi[0] {\n\tcase '<':\n\t\treturn decode_sgr_mouse(csi[1:], screen_size, last_char)\n\tdefault:\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "tools/tui/loop/read.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc (self *Loop) dispatch_input_data(data []byte) error {\n\tif self.OnReceivedData != nil {\n\t\terr := self.OnReceivedData(data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\terr := self.escape_code_parser.Parse(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc read_ignoring_temporary_errors(f *tty.Term, buf []byte) (int, error) {\n\tn, err := f.Read(buf)\n\tif is_temporary_error(err) {\n\t\treturn 0, nil\n\t}\n\tif n == 0 {\n\t\treturn 0, io.EOF\n\t}\n\treturn n, err\n}\n\nfunc read_from_tty(pipe_r *os.File, term *tty.Term, results_channel chan<- []byte, err_channel chan<- error, quit_channel <-chan byte, leftover_channel chan<- []byte) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr := parallel.Format_stacktrace_on_panic(r, 1)\n\t\t\terr_channel <- err\n\t\t}\n\t}()\n\tkeep_going := true\n\tpipe_fd := int(pipe_r.Fd())\n\ttty_fd := term.Fd()\n\tselector := utils.CreateSelect(2)\n\tselector.RegisterRead(pipe_fd)\n\tselector.RegisterRead(tty_fd)\n\n\tdefer func() {\n\t\tclose(results_channel)\n\t\tpipe_r.Close()\n\t}()\n\n\tconst bufsize = 2 * utils.DEFAULT_IO_BUFFER_SIZE\n\n\twait_for_read_available := func() {\n\t\tfor {\n\t\t\tn, err := selector.WaitForever()\n\t\t\tif err != nil && err != unix.EINTR {\n\t\t\t\terr_channel <- err\n\t\t\t\tkeep_going = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif n > 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif selector.IsReadyToRead(pipe_fd) {\n\t\t\tkeep_going = false\n\t\t}\n\t}\n\n\tbuf := make([]byte, bufsize)\n\tfor keep_going {\n\t\tif len(buf) < 64 {\n\t\t\tbuf = make([]byte, bufsize)\n\t\t}\n\t\tif wait_for_read_available(); !keep_going {\n\t\t\tbreak\n\t\t}\n\t\tn, err := read_ignoring_temporary_errors(term, buf)\n\t\tif err != nil {\n\t\t\terr_channel <- err\n\t\t\tkeep_going = false\n\t\t\tbreak\n\t\t}\n\t\tif n == 0 { // temporary error\n\t\t\tcontinue\n\t\t}\n\t\tsend := buf[:n]\n\t\tbuf = buf[n:]\n\t\tselect {\n\t\tcase results_channel <- send:\n\t\tcase <-quit_channel:\n\t\t\tleftover_channel <- send\n\t\t\tkeep_going = false\n\t\t}\n\t}\n}\n\nfunc has_da1_response(s string) bool {\n\tpat := regexp.MustCompile(\"\\x1b\\\\[\\\\?[0-9:;]+c\")\n\treturn pat.FindString(s) != \"\"\n}\n\nfunc read_until_primary_device_attributes_response(term *tty.Term, initial_bytes []byte, timeout time.Duration) {\n\ts := strings.Builder{}\n\tif initial_bytes != nil {\n\t\ts.Write(initial_bytes)\n\t}\n\treceived := make(chan error)\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\ttext := parallel.Format_stacktrace_on_panic(r, 1).Error()\n\t\t\t\treceived <- fmt.Errorf(\"%s\", text)\n\t\t\t}\n\t\t}()\n\t\tbuf := make([]byte, 1024)\n\t\tn, err := read_ignoring_temporary_errors(term, buf)\n\t\tif n > 0 {\n\t\t\ts.Write(buf[:n])\n\t\t\tif has_da1_response(s.String()) {\n\t\t\t\treceived <- nil\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\treceived <- err\n\t\t}\n\t}()\n\tselect {\n\tcase <-received:\n\tcase <-time.After(timeout):\n\t}\n}\n"
  },
  {
    "path": "tools/tui/loop/run.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar SIGNULL unix.Signal\n\nfunc new_loop() *Loop {\n\tl := Loop{controlling_term: nil}\n\tl.terminal_options.Alternate_screen = true\n\tl.terminal_options.restore_colors = true\n\tl.terminal_options.focus_tracking = true\n\tl.terminal_options.in_band_resize_notification = true\n\tl.terminal_options.color_scheme_change_notification = false\n\tl.terminal_options.kitty_keyboard_mode = DISAMBIGUATE_KEYS | REPORT_ALTERNATE_KEYS | REPORT_ALL_KEYS_AS_ESCAPE_CODES | REPORT_TEXT_WITH_KEYS\n\tl.escape_code_parser.HandleCSI = l.handle_csi\n\tl.escape_code_parser.HandleOSC = l.handle_osc\n\tl.escape_code_parser.HandleDCS = l.handle_dcs\n\tl.escape_code_parser.HandleAPC = l.handle_apc\n\tl.escape_code_parser.HandleSOS = l.handle_sos\n\tl.escape_code_parser.HandlePM = l.handle_pm\n\tl.escape_code_parser.HandleRune = l.handle_rune\n\tl.escape_code_parser.HandleEndOfBracketedPaste = l.handle_end_of_bracketed_paste\n\tl.style_cache = make(map[string]func(...any) string)\n\tl.style_ctx.AllowEscapeCodes = true\n\treturn &l\n}\n\nfunc is_temporary_error(err error) bool {\n\treturn errors.Is(err, unix.EINTR) || errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EWOULDBLOCK) || errors.Is(err, io.ErrShortWrite)\n}\n\nfunc kill_self(sig unix.Signal) {\n\t_ = unix.Kill(os.Getpid(), sig)\n\t// Give the signal time to be delivered\n\ttime.Sleep(20 * time.Millisecond)\n}\n\nfunc (self *Loop) set_pointer_shapes(ps []PointerShape) {\n\tself.pointer_shapes = ps\n\tif len(ps) > 0 {\n\t\ts := strings.Builder{}\n\t\ts.WriteString(\"\\x1b]22;>\")\n\t\tfor i, x := range ps {\n\t\t\ts.WriteString(x.String())\n\t\t\tif i+1 < len(ps) {\n\t\t\t\ts.WriteByte(',')\n\t\t\t}\n\t\t}\n\t\ts.WriteString(\"\\x1b\\\\\")\n\t\tself.QueueWriteString(s.String())\n\t}\n}\n\nfunc (self *Loop) update_screen_size() error {\n\tif self.controlling_term == nil {\n\t\treturn fmt.Errorf(\"No controlling terminal cannot update screen size\")\n\t}\n\tws, err := self.controlling_term.GetSize()\n\tif err != nil {\n\t\treturn err\n\t}\n\ts := &self.screen_size\n\ts.updated = true\n\ts.HeightCells, s.WidthCells = uint(ws.Row), uint(ws.Col)\n\ts.HeightPx, s.WidthPx = uint(ws.Ypixel), uint(ws.Xpixel)\n\ts.CellWidth = s.WidthPx / s.WidthCells\n\ts.CellHeight = s.HeightPx / s.HeightCells\n\treturn nil\n}\n\nfunc (self *Loop) handle_csi(raw []byte) (err error) {\n\tcsi := string(raw)\n\tif strings.HasSuffix(csi, \"t\") && strings.HasPrefix(csi, \"48;\") {\n\t\tif parts := strings.Split(csi[3:len(csi)-1], \";\"); len(parts) > 3 {\n\t\t\tvar parsed [4]int\n\t\t\tok := true\n\t\t\tfor i, x := range parts {\n\t\t\t\tx, _, _ = strings.Cut(x, \":\")\n\t\t\t\tif parsed[i], err = strconv.Atoi(x); err != nil {\n\t\t\t\t\tok = false\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ok {\n\t\t\t\tself.seen_inband_resize = true\n\t\t\t\told_size := self.screen_size\n\t\t\t\ts := &self.screen_size\n\t\t\t\ts.updated = true\n\t\t\t\ts.HeightCells, s.WidthCells = uint(parsed[0]), uint(parsed[1])\n\t\t\t\ts.HeightPx, s.WidthPx = uint(parsed[2]), uint(parsed[3])\n\t\t\t\ts.CellWidth = s.WidthPx / s.WidthCells\n\t\t\t\ts.CellHeight = s.HeightPx / s.HeightCells\n\t\t\t\tif self.OnResize != nil {\n\t\t\t\t\treturn self.OnResize(old_size, self.screen_size)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t} else if csi == \"I\" || csi == \"O\" {\n\t\tif self.OnFocusChange != nil {\n\t\t\treturn self.OnFocusChange(csi == \"I\")\n\t\t}\n\t\treturn nil\n\t}\n\tke := KeyEventFromCSI(csi)\n\tif ke != nil {\n\t\treturn self.handle_key_event(ke)\n\t}\n\tsz, err := self.ScreenSize()\n\tif err == nil {\n\t\tme := MouseEventFromCSI(csi, sz)\n\t\tif me != nil {\n\t\t\treturn self.handle_mouse_event(me)\n\t\t}\n\t}\n\tif self.waiting_for_capabilities_response {\n\t\tif strings.HasPrefix(csi, \"?\") && strings.HasSuffix(csi, \"c\") {\n\t\t\tself.waiting_for_capabilities_response = false\n\t\t\tif self.OnCapabilitiesReceived != nil {\n\t\t\t\tif err = self.OnCapabilitiesReceived(self.TerminalCapabilities); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t} else if strings.HasPrefix(csi, \"?997;\") && strings.HasSuffix(csi, \"n\") {\n\t\t\tswitch csi[len(csi)-2] {\n\t\t\tcase '1':\n\t\t\t\tself.TerminalCapabilities.ColorPreference = DARK_COLOR_PREFERENCE\n\t\t\tcase '2':\n\t\t\t\tself.TerminalCapabilities.ColorPreference = LIGHT_COLOR_PREFERENCE\n\t\t\t}\n\t\t\tself.TerminalCapabilities.ColorPreferenceResponseReceived = true\n\t\t} else if strings.HasPrefix(csi, \"?\") && strings.HasSuffix(csi, \"u\") {\n\t\t\tself.TerminalCapabilities.KeyboardProtocol = true\n\t\t\tself.TerminalCapabilities.KeyboardProtocolResponseReceived = true\n\t\t}\n\t} else if self.terminal_options.color_scheme_change_notification && strings.HasPrefix(csi, \"?997;\") && strings.HasSuffix(csi, \"n\") {\n\t\tswitch csi[len(csi)-2] {\n\t\tcase '1':\n\t\t\tself.TerminalCapabilities.ColorPreference = DARK_COLOR_PREFERENCE\n\t\tcase '2':\n\t\t\tself.TerminalCapabilities.ColorPreference = LIGHT_COLOR_PREFERENCE\n\t\t}\n\t\tself.TerminalCapabilities.ColorPreferenceResponseReceived = true\n\t\tif self.OnColorSchemeChange != nil {\n\t\t\treturn self.OnColorSchemeChange(self.TerminalCapabilities.ColorPreference)\n\t\t}\n\t}\n\tif self.OnEscapeCode != nil {\n\t\treturn self.OnEscapeCode(CSI, raw)\n\t}\n\treturn nil\n}\n\nfunc is_click(a, b *MouseEvent) bool {\n\tif a.Event_type != MOUSE_PRESS || b.Event_type != MOUSE_RELEASE {\n\t\treturn false\n\t}\n\tx := a.Cell.X - b.Cell.X\n\ty := a.Cell.Y - b.Cell.Y\n\treturn x*x+y*y <= 4\n\n}\n\nfunc (self *Loop) handle_mouse_event(ev *MouseEvent) error {\n\tif self.OnMouseEvent != nil {\n\t\terr := self.OnMouseEvent(ev)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tswitch ev.Event_type {\n\t\tcase MOUSE_PRESS:\n\t\t\tself.pending_mouse_events.WriteAllAndDiscardOld(*ev)\n\t\tcase MOUSE_RELEASE:\n\t\t\tself.pending_mouse_events.WriteAllAndDiscardOld(*ev)\n\t\t\tif self.pending_mouse_events.Len() > 1 {\n\t\t\t\tevents := self.pending_mouse_events.ReadAll()\n\t\t\t\tif is_click(&events[len(events)-2], &events[len(events)-1]) {\n\t\t\t\t\te := events[len(events)-1]\n\t\t\t\t\te.Event_type = MOUSE_CLICK\n\t\t\t\t\terr = self.OnMouseEvent(&e)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) handle_key_event(ev *KeyEvent) error {\n\tif self.OnKeyEvent != nil {\n\t\terr := self.OnKeyEvent(ev)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif ev.Handled {\n\t\t\treturn nil\n\t\t}\n\t}\n\tif ev.MatchesPressOrRepeat(\"ctrl+c\") {\n\t\tev.Handled = true\n\t\treturn self.on_SIGINT()\n\t}\n\tif ev.MatchesPressOrRepeat(\"ctrl+z\") {\n\t\tev.Handled = true\n\t\treturn self.on_SIGTSTP()\n\t}\n\tif ev.Text != \"\" && self.OnText != nil {\n\t\treturn self.OnText(ev.Text, true, false)\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) handle_osc(raw []byte) error {\n\tif self.OnEscapeCode != nil {\n\t\treturn self.OnEscapeCode(OSC, raw)\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) handle_dcs(raw []byte) error {\n\tif self.OnRCResponse != nil && bytes.HasPrefix(raw, utils.UnsafeStringToBytes(\"@kitty-cmd\")) {\n\t\treturn self.OnRCResponse(raw[len(\"@kitty-cmd\"):])\n\t}\n\tif self.OnQueryResponse != nil && (bytes.HasPrefix(raw, utils.UnsafeStringToBytes(\"1+r\")) || bytes.HasPrefix(raw, utils.UnsafeStringToBytes(\"0+r\"))) {\n\t\tvalid := raw[0] == '1'\n\t\ts := utils.NewSeparatorScanner(utils.UnsafeBytesToString(raw[3:]), \";\")\n\t\tfor s.Scan() {\n\t\t\tkey, val, _ := strings.Cut(s.Text(), \"=\")\n\t\t\tif k, err := hex.DecodeString(key); err == nil {\n\t\t\t\tif bytes.HasPrefix(k, utils.UnsafeStringToBytes(\"kitty-query-\")) {\n\t\t\t\t\tk = k[len(\"kitty-query-\"):]\n\t\t\t\t\tif v, err := hex.DecodeString(val); err == nil {\n\t\t\t\t\t\tif err = self.OnQueryResponse(string(k), string(v), valid); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tif self.OnEscapeCode != nil {\n\t\treturn self.OnEscapeCode(DCS, raw)\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) handle_apc(raw []byte) error {\n\tif self.OnEscapeCode != nil {\n\t\treturn self.OnEscapeCode(APC, raw)\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) handle_sos(raw []byte) error {\n\tif self.OnEscapeCode != nil {\n\t\treturn self.OnEscapeCode(SOS, raw)\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) handle_pm(raw []byte) error {\n\tif self.OnEscapeCode != nil {\n\t\treturn self.OnEscapeCode(PM, raw)\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) handle_rune(raw rune) error {\n\tif self.OnText != nil {\n\t\treturn self.OnText(string(raw), false, self.escape_code_parser.InBracketedPaste())\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) handle_end_of_bracketed_paste() error {\n\tif self.OnText != nil {\n\t\treturn self.OnText(\"\", false, false)\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) on_signal(s unix.Signal) error {\n\tswitch s {\n\tcase unix.SIGINT:\n\t\tif self.OnSIGINT != nil {\n\t\t\tif handled, err := self.OnSIGINT(); handled {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn self.on_SIGINT()\n\tcase unix.SIGPIPE:\n\t\treturn self.on_SIGPIPE()\n\tcase unix.SIGWINCH:\n\t\treturn self.on_SIGWINCH()\n\tcase unix.SIGTERM:\n\t\tif self.OnSIGTERM != nil {\n\t\t\tif handled, err := self.OnSIGTERM(); handled {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn self.on_SIGTERM()\n\tcase unix.SIGTSTP:\n\t\treturn self.on_SIGTSTP()\n\tcase unix.SIGHUP:\n\t\treturn self.on_SIGHUP()\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc (self *Loop) on_SIGINT() error {\n\tself.death_signal = unix.SIGINT\n\tself.keep_going = false\n\treturn nil\n}\n\nfunc (self *Loop) on_SIGPIPE() error {\n\treturn nil\n}\n\nfunc (self *Loop) on_SIGWINCH() error {\n\tself.update_screen_size()\n\tif self.seen_inband_resize {\n\t\treturn nil\n\t}\n\tself.screen_size.updated = false\n\tif self.OnResize != nil {\n\t\told_size := self.screen_size\n\t\terr := self.update_screen_size()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn self.OnResize(old_size, self.screen_size)\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) on_SIGTERM() error {\n\tself.death_signal = unix.SIGTERM\n\tself.keep_going = false\n\treturn nil\n}\n\nfunc (self *Loop) on_SIGHUP() error {\n\tself.death_signal = unix.SIGHUP\n\tself.keep_going = false\n\treturn nil\n}\n\nfunc (self *Loop) run() (err error) {\n\tsignal_channel := make(chan os.Signal, 256)\n\thandled_signals := []os.Signal{unix.SIGINT, unix.SIGTERM, unix.SIGTSTP, unix.SIGHUP, unix.SIGWINCH, unix.SIGPIPE}\n\tsignal.Notify(signal_channel, handled_signals...)\n\tdefer signal.Reset(handled_signals...)\n\n\tcontrolling_term, err := tty.OpenControllingTerm(tty.SetRaw)\n\tif err != nil {\n\t\treturn err\n\t}\n\tself.controlling_term = controlling_term\n\tdefer func() {\n\t\tcontrolling_term.RestoreAndClose()\n\t\tself.controlling_term = nil\n\t}()\n\n\tself.keep_going = true\n\tself.seen_inband_resize = false\n\tself.pending_mouse_events = utils.NewRingBuffer[MouseEvent](4)\n\t// tty_write_channel is buffered so there is no race between initial\n\t// queueing and startup of writer thread and also as a performance\n\t// optimization to avoid copying unnecessarily to pending_writes\n\tself.tty_write_channel = make(chan write_msg, 512)\n\tself.write_msg_id_counter = 0\n\twrite_done_channel := make(chan IdType)\n\tself.wakeup_channel = make(chan byte, 256)\n\tself.panic_channel = make(chan error)\n\tself.pending_writes = make([]write_msg, 0, 256)\n\terr_channel := make(chan error, 8)\n\tself.death_signal = SIGNULL\n\tself.escape_code_parser.Reset()\n\tself.exit_code = 0\n\tself.atomic_update_active = false\n\tself.timers, self.timers_temp = make([]*timer, 0, 8), make([]*timer, 0, 8)\n\tno_timeout_channel := make(<-chan time.Time)\n\tfinalizer := \"\"\n\n\tvar r_r, r_w, w_r, w_w *os.File\n\tvar tty_reading_done_channel chan byte\n\tvar tty_read_channel chan []byte\n\tvar tty_leftover_read_channel chan []byte\n\n\tstart_tty_reader := func() (err error) {\n\t\tr_r, r_w, err = os.Pipe()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttty_read_channel = make(chan []byte)\n\t\ttty_reading_done_channel = make(chan byte)\n\t\ttty_leftover_read_channel = make(chan []byte, 1)\n\t\tgo read_from_tty(r_r, controlling_term, tty_read_channel, err_channel, tty_reading_done_channel, tty_leftover_read_channel)\n\t\treturn\n\t}\n\terr = start_tty_reader()\n\tif err != nil {\n\t\treturn err\n\t}\n\tw_r, w_w, err = os.Pipe() // these are closed in the writer thread and the shutdown defer in this thread\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tself.QueueWriteString(self.terminal_options.SetStateEscapeCodes())\n\tneeds_reset_escape_codes := true\n\n\tshutdown_tty_reader := func() {\n\t\t// notify tty reader that we are shutting down\n\t\tif r_w != nil {\n\t\t\tr_w.Close()\n\t\t\tclose(tty_reading_done_channel)\n\t\t\tr_w = nil\n\t\t\ttty_reading_done_channel = nil\n\t\t}\n\t}\n\twait_for_tty_reader_to_quit := func() {\n\t\t// wait for tty reader to exit cleanly\n\t\tfor range tty_read_channel {\n\t\t}\n\t\tif !self.waiting_for_capabilities_response {\n\t\t\tclose(tty_leftover_read_channel)\n\t\t\treturn\n\t\t}\n\t\tvar pending_bytes []byte\n\t\tselect {\n\t\tcase msg, ok := <-tty_leftover_read_channel:\n\t\t\tif ok {\n\t\t\t\tpending_bytes = msg\n\t\t\t}\n\t\tdefault:\n\t\t}\n\t\tread_until_primary_device_attributes_response(controlling_term, pending_bytes, 2*time.Second)\n\t}\n\n\tdefer func() {\n\t\tshutdown_tty_reader()\n\n\t\tif self.OnFinalize != nil {\n\t\t\tfinalizer += self.OnFinalize()\n\t\t}\n\t\tif finalizer != \"\" {\n\t\t\tself.QueueWriteString(finalizer)\n\t\t}\n\t\tif needs_reset_escape_codes {\n\t\t\tself.ClearPointerShapes()\n\t\t\tself.QueueWriteString(self.terminal_options.ResetStateEscapeCodes())\n\t\t}\n\t\t// flush queued data and wait for it to be written for a timeout, then wait for writer to shutdown\n\t\tflush_writer(w_w, self.tty_write_channel, write_done_channel, self.pending_writes, 2*time.Second)\n\t\tself.pending_writes = nil\n\t\tself.tty_write_channel = nil\n\t\twait_for_tty_reader_to_quit()\n\t}()\n\n\tgo write_to_tty(w_r, controlling_term, self.tty_write_channel, err_channel, write_done_channel)\n\n\tif self.OnInitialize != nil {\n\t\tfinalizer, err = self.OnInitialize()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tself.SuspendAndRun = func(run func() error) (err error) {\n\t\tps := self.ClearPointerShapes()\n\t\twrite_id := self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes())\n\t\tneeds_reset_escape_codes = false\n\t\tif err = self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tshutdown_tty_reader()\n\t\twait_for_tty_reader_to_quit()\n\t\tresume, err := controlling_term.Suspend()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = run(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = start_tty_reader(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = resume(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\twrite_id = self.QueueWriteString(self.terminal_options.SetStateEscapeCodes())\n\t\tself.set_pointer_shapes(ps)\n\t\tneeds_reset_escape_codes = true\n\t\treturn self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second)\n\t}\n\n\tself.on_SIGTSTP = func() error {\n\t\tps := self.ClearPointerShapes()\n\t\twrite_id := self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes())\n\t\tneeds_reset_escape_codes = false\n\t\terr := self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = controlling_term.SuspendAndRun(func() error {\n\t\t\t_ = unix.Kill(os.Getpid(), unix.SIGSTOP)\n\t\t\ttime.Sleep(20 * time.Millisecond)\n\t\t\treturn nil\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twrite_id = self.QueueWriteString(self.terminal_options.SetStateEscapeCodes())\n\t\tself.set_pointer_shapes(ps)\n\t\tneeds_reset_escape_codes = true\n\t\terr = self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif self.OnResumeFromStop != nil {\n\t\t\treturn self.OnResumeFromStop()\n\t\t}\n\t\treturn nil\n\t}\n\n\tfor self.keep_going {\n\t\tself.flush_pending_writes(self.tty_write_channel)\n\t\ttimeout_chan := no_timeout_channel\n\t\tif len(self.timers) > 0 {\n\t\t\tnow := time.Now()\n\t\t\terr = self.dispatch_timers(now)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tvar timeout time.Duration\n\t\t\tif len(self.timers) > 0 {\n\t\t\t\ttimeout = max(0, self.timers[0].deadline.Sub(now))\n\t\t\t}\n\t\t\ttimeout_chan = time.After(timeout)\n\t\t}\n\t\tselect {\n\t\tcase <-timeout_chan:\n\t\tcase p := <-self.panic_channel:\n\t\t\treturn p\n\t\tcase <-self.wakeup_channel:\n\t\t\tfor len(self.wakeup_channel) > 0 {\n\t\t\t\t<-self.wakeup_channel\n\t\t\t}\n\t\t\tif self.OnWakeup != nil {\n\t\t\t\terr = self.OnWakeup()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\tcase msg_id := <-write_done_channel:\n\t\t\tself.flush_pending_writes(self.tty_write_channel)\n\t\t\tif self.OnWriteComplete != nil {\n\t\t\t\terr = self.OnWriteComplete(msg_id, msg_id < self.write_msg_id_counter)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\tcase rwerr := <-err_channel:\n\t\t\treturn fmt.Errorf(\"Failed doing I/O with terminal: %w\", rwerr)\n\t\tcase s := <-signal_channel:\n\t\t\terr = self.on_signal(s.(unix.Signal))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase input_data, more := <-tty_read_channel:\n\t\t\tif !more {\n\t\t\t\tselect {\n\t\t\t\tcase rwerr := <-err_channel:\n\t\t\t\t\treturn fmt.Errorf(\"Failed to read from terminal: %w\", rwerr)\n\t\t\t\tdefault:\n\t\t\t\t\treturn fmt.Errorf(\"Failed to read from terminal: %w\", io.EOF)\n\t\t\t\t}\n\t\t\t}\n\t\t\terr := self.dispatch_input_data(input_data)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "tools/tui/loop/terminal-state.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty\"\n)\n\ntype KeyboardStateBits uint8\n\nconst (\n\tLEGACY_KEYS                     KeyboardStateBits = 0\n\tDISAMBIGUATE_KEYS                                 = 1\n\tREPORT_KEY_EVENT_TYPES                            = 2\n\tREPORT_ALTERNATE_KEYS                             = 4\n\tREPORT_ALL_KEYS_AS_ESCAPE_CODES                   = 8\n\tREPORT_TEXT_WITH_KEYS                             = 16\n\tFULL_KEYBOARD_PROTOCOL                            = DISAMBIGUATE_KEYS | REPORT_ALTERNATE_KEYS | REPORT_ALL_KEYS_AS_ESCAPE_CODES | REPORT_TEXT_WITH_KEYS | REPORT_KEY_EVENT_TYPES\n\tNO_KEYBOARD_STATE_CHANGE                          = 32\n)\n\nconst (\n\tSAVE_CURSOR                   = \"\\0337\"\n\tRESTORE_CURSOR                = \"\\0338\"\n\tSAVE_PRIVATE_MODE_VALUES      = \"\\033[?s\"\n\tRESTORE_PRIVATE_MODE_VALUES   = \"\\033[?r\"\n\tSAVE_COLORS                   = \"\\033[#P\"\n\tRESTORE_COLORS                = \"\\033[#Q\"\n\tDECSACE_DEFAULT_REGION_SELECT = \"\\033[*x\"\n\tCLEAR_SCREEN                  = \"\\033[H\\033[2J\"\n\tPOP_KEY_FLAGS                 = \"\\033[<u\"\n\tPUSH_KEY_FLAGS                = \"\\033[>u\"\n)\n\ntype CursorShapes uint\n\nconst (\n\tBLOCK_CURSOR     CursorShapes = 1\n\tUNDERLINE_CURSOR CursorShapes = 3\n\tBAR_CURSOR       CursorShapes = 5\n)\n\ntype Mode uint32\n\nconst private Mode = 1 << 31\n\nconst (\n\tLNM                              Mode = 20\n\tIRM                              Mode = 4\n\tDECKM                            Mode = 1 | private\n\tDECSCNM                          Mode = 5 | private\n\tDECOM                            Mode = 6 | private\n\tDECAWM                           Mode = 7 | private\n\tDECARM                           Mode = 8 | private\n\tDECTCEM                          Mode = 25 | private\n\tMOUSE_BUTTON_TRACKING            Mode = 1000 | private\n\tMOUSE_MOTION_TRACKING            Mode = 1002 | private\n\tMOUSE_MOVE_TRACKING              Mode = 1003 | private\n\tFOCUS_TRACKING                   Mode = 1004 | private\n\tMOUSE_UTF8_MODE                  Mode = 1005 | private\n\tMOUSE_SGR_MODE                   Mode = 1006 | private\n\tMOUSE_URXVT_MODE                 Mode = 1015 | private\n\tMOUSE_SGR_PIXEL_MODE             Mode = 1016 | private\n\tALTERNATE_SCREEN                 Mode = 1049 | private\n\tBRACKETED_PASTE                  Mode = 2004 | private\n\tPENDING_UPDATE                   Mode = 2026 | private\n\tCOLOR_SCHEME_CHANGE_NOTIFICATION Mode = 2031 | private\n\tINBAND_RESIZE_NOTIFICATION       Mode = 2048 | private\n\tHANDLE_TERMIOS_SIGNALS           Mode = kitty.HandleTermiosSignals | private\n)\n\nfunc (self Mode) escape_code(which string) string {\n\tnum := self\n\tpriv := \"\"\n\tif num&private > 0 {\n\t\tpriv = \"?\"\n\t\tnum &^= private\n\t}\n\treturn fmt.Sprintf(\"\\033[%s%d%s\", priv, uint32(num), which)\n}\n\nfunc (self Mode) EscapeCodeToSet() string {\n\treturn self.escape_code(\"h\")\n}\n\nfunc (self Mode) EscapeCodeToReset() string {\n\treturn self.escape_code(\"l\")\n}\n\ntype MouseTracking uint8\n\nconst (\n\tNO_MOUSE_TRACKING MouseTracking = iota\n\tBUTTONS_ONLY_MOUSE_TRACKING\n\tBUTTONS_AND_DRAG_MOUSE_TRACKING\n\tFULL_MOUSE_TRACKING\n)\n\ntype TerminalStateOptions struct {\n\tAlternate_screen, restore_colors bool\n\tmouse_tracking                   MouseTracking\n\tkitty_keyboard_mode              KeyboardStateBits\n\tin_band_resize_notification      bool\n\tfocus_tracking                   bool\n\tcolor_scheme_change_notification bool\n}\n\nfunc set_modes(sb *strings.Builder, modes ...Mode) {\n\tfor _, m := range modes {\n\t\tsb.WriteString(m.EscapeCodeToSet())\n\t}\n}\n\nfunc reset_modes(sb *strings.Builder, modes ...Mode) {\n\tfor _, m := range modes {\n\t\tsb.WriteString(m.EscapeCodeToReset())\n\t}\n}\n\nfunc (self *TerminalStateOptions) SetStateEscapeCodes() string {\n\tvar sb strings.Builder\n\tsb.Grow(256)\n\tif self.Alternate_screen {\n\t\tsb.WriteString(SAVE_CURSOR)\n\t}\n\tsb.WriteString(SAVE_PRIVATE_MODE_VALUES)\n\tif self.restore_colors {\n\t\tsb.WriteString(SAVE_COLORS)\n\t}\n\tsb.WriteString(DECSACE_DEFAULT_REGION_SELECT)\n\treset_modes(&sb,\n\t\tIRM, DECKM, DECSCNM, BRACKETED_PASTE,\n\t\tMOUSE_BUTTON_TRACKING, MOUSE_MOTION_TRACKING, MOUSE_MOVE_TRACKING, MOUSE_UTF8_MODE, MOUSE_SGR_MODE)\n\tset_modes(&sb, DECARM, DECAWM, DECTCEM)\n\tif self.focus_tracking {\n\t\tset_modes(&sb, FOCUS_TRACKING)\n\t}\n\tif self.in_band_resize_notification {\n\t\tset_modes(&sb, INBAND_RESIZE_NOTIFICATION)\n\t}\n\tif self.color_scheme_change_notification {\n\t\tset_modes(&sb, COLOR_SCHEME_CHANGE_NOTIFICATION)\n\t}\n\tif self.Alternate_screen {\n\t\tset_modes(&sb, ALTERNATE_SCREEN)\n\t\tsb.WriteString(CLEAR_SCREEN)\n\t}\n\tswitch self.kitty_keyboard_mode {\n\tcase LEGACY_KEYS:\n\t\tsb.WriteString(\"\\033[>u\")\n\tcase NO_KEYBOARD_STATE_CHANGE:\n\tdefault:\n\t\tsb.WriteString(fmt.Sprintf(\"\\033[>%du\", self.kitty_keyboard_mode))\n\t}\n\tif self.mouse_tracking != NO_MOUSE_TRACKING {\n\t\tsb.WriteString(MOUSE_SGR_PIXEL_MODE.EscapeCodeToSet())\n\t\tswitch self.mouse_tracking {\n\t\tcase BUTTONS_ONLY_MOUSE_TRACKING:\n\t\t\tsb.WriteString(MOUSE_BUTTON_TRACKING.EscapeCodeToSet())\n\t\tcase BUTTONS_AND_DRAG_MOUSE_TRACKING:\n\t\t\tsb.WriteString(MOUSE_MOTION_TRACKING.EscapeCodeToSet())\n\t\tcase FULL_MOUSE_TRACKING:\n\t\t\tsb.WriteString(MOUSE_MOVE_TRACKING.EscapeCodeToSet())\n\t\t}\n\t}\n\treturn sb.String()\n}\n\nfunc (self *TerminalStateOptions) ResetStateEscapeCodes() string {\n\tvar sb strings.Builder\n\tsb.Grow(64)\n\tif self.kitty_keyboard_mode != NO_KEYBOARD_STATE_CHANGE {\n\t\tsb.WriteString(\"\\033[<u\")\n\t}\n\tif self.Alternate_screen {\n\t\tsb.WriteString(ALTERNATE_SCREEN.EscapeCodeToReset())\n\t} else {\n\t\tsb.WriteString(SAVE_CURSOR)\n\t}\n\t// Explictly turn off this mode as there are some terminals that dont\n\t// support restoring all modes and people tend to use the show-key kitten\n\t// in other terminals. Since I do want to encourage adoption of the kitty\n\t// keyboard protocol, the extra bytes are worth it in this case.\n\tif self.in_band_resize_notification {\n\t\treset_modes(&sb, INBAND_RESIZE_NOTIFICATION)\n\t}\n\tsb.WriteString(RESTORE_PRIVATE_MODE_VALUES)\n\tif self.restore_colors {\n\t\tsb.WriteString(RESTORE_COLORS)\n\t}\n\tsb.WriteString(RESTORE_CURSOR)\n\treturn sb.String()\n}\n\nfunc CursorShape(shape CursorShapes, blink bool) string {\n\tif !blink {\n\t\tshape += 1\n\t}\n\treturn fmt.Sprintf(\"\\x1b[%d q\", shape)\n}\n"
  },
  {
    "path": "tools/tui/loop/timers.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\ntype timer struct {\n\tinterval time.Duration\n\tdeadline time.Time\n\trepeats  bool\n\tid       IdType\n\tcallback TimerCallback\n}\n\nfunc (self *timer) update_deadline(now time.Time) {\n\tself.deadline = now.Add(self.interval)\n}\n\nfunc (self timer) String() string {\n\treturn fmt.Sprintf(\"Timer(id=%d, callback=%s, deadline=%s, repeats=%v)\", self.id, utils.FunctionName(self.callback), time.Until(self.deadline), self.repeats)\n}\n\nfunc (self *Loop) add_timer(interval time.Duration, repeats bool, callback TimerCallback) (IdType, error) {\n\tif self.timers == nil {\n\t\treturn 0, fmt.Errorf(\"Cannot add timers before starting the run loop, add them in OnInitialize instead\")\n\t}\n\tself.timer_id_counter++\n\tt := timer{interval: interval, repeats: repeats, callback: callback, id: self.timer_id_counter}\n\tt.update_deadline(time.Now())\n\tself.timers = append(self.timers, &t)\n\tself.sort_timers()\n\treturn t.id, nil\n}\n\nfunc (self *Loop) remove_timer(id IdType) bool {\n\tif self.timers == nil {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(self.timers); i++ {\n\t\tif self.timers[i].id == id {\n\t\t\tself.timers = append(self.timers[:i], self.timers[i+1:]...)\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *Loop) dispatch_timers(now time.Time) error {\n\tself.timers_temp = self.timers_temp[:0]\n\tself.timers, self.timers_temp = self.timers_temp, self.timers\n\tdispatched := false\n\tfor _, t := range self.timers_temp {\n\t\tif now.After(t.deadline) {\n\t\t\tdispatched = true\n\t\t\terr := t.callback(t.id)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif t.repeats {\n\t\t\t\tt.update_deadline(now)\n\t\t\t\tself.timers = append(self.timers, t)\n\t\t\t}\n\t\t} else {\n\t\t\tself.timers = append(self.timers, t)\n\t\t}\n\t}\n\tif dispatched {\n\t\tself.sort_timers() // needed because a timer callback could have added a new timer\n\t}\n\treturn nil\n}\n\nfunc (self *Loop) sort_timers() {\n\tslices.SortStableFunc(self.timers, func(a, b *timer) int { return a.deadline.Compare(b.deadline) })\n}\n"
  },
  {
    "path": "tools/tui/loop/write.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage loop\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\ntype write_msg struct {\n\tid    IdType\n\tbytes []byte\n\tstr   string\n}\n\nfunc (self *write_msg) String() string {\n\treturn fmt.Sprintf(\"write_msg{%v %#v %#v}\", self.id, string(self.bytes), self.str)\n}\n\nfunc write_ignoring_temporary_errors(f *tty.Term, buf []byte) (int, error) {\n\tn, err := f.Write(buf)\n\tif err != nil {\n\t\tif is_temporary_error(err) {\n\t\t\terr = nil\n\t\t}\n\t\treturn n, err\n\t}\n\tif n == 0 {\n\t\treturn 0, io.EOF\n\t}\n\treturn n, err\n}\n\nfunc writestring_ignoring_temporary_errors(f *tty.Term, buf string) (int, error) {\n\tn, err := f.WriteString(buf)\n\tif err != nil {\n\t\tif is_temporary_error(err) {\n\t\t\terr = nil\n\t\t}\n\t\treturn n, err\n\t}\n\tif n == 0 {\n\t\treturn 0, io.EOF\n\t}\n\treturn n, err\n}\n\nfunc (self *Loop) flush_pending_writes(tty_write_channel chan<- write_msg) (num_sent int) {\n\tdefer func() {\n\t\tif num_sent > 0 {\n\t\t\tself.pending_writes = utils.ShiftLeft(self.pending_writes, num_sent)\n\t\t}\n\t}()\n\tfor len(self.pending_writes) > num_sent {\n\t\tselect {\n\t\tcase tty_write_channel <- self.pending_writes[num_sent]:\n\t\t\tnum_sent++\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Loop) wait_for_write_to_complete(sentinel IdType, tty_write_channel chan<- write_msg, write_done_channel <-chan IdType, timeout time.Duration) error {\n\tnum_sent := 0\n\tdefer func() {\n\t\tif num_sent > 0 {\n\t\t\tself.pending_writes = utils.ShiftLeft(self.pending_writes, num_sent)\n\t\t}\n\t}()\n\n\tend_time := time.Now().Add(timeout)\n\tfor num_sent < len(self.pending_writes) {\n\t\ttimeout = time.Until(end_time)\n\t\tif timeout <= 0 {\n\t\t\treturn os.ErrDeadlineExceeded\n\t\t}\n\t\tselect {\n\t\tcase tty_write_channel <- self.pending_writes[num_sent]:\n\t\t\tnum_sent++\n\t\tcase write_id, more := <-write_done_channel:\n\t\t\tif self.OnWriteComplete != nil {\n\t\t\t\terr := self.OnWriteComplete(write_id, write_id < self.write_msg_id_counter)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif write_id == sentinel {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif !more {\n\t\t\t\treturn fmt.Errorf(\"The write_done_channel was unexpectedly closed\")\n\t\t\t}\n\t\tcase <-time.After(timeout):\n\t\t\treturn os.ErrDeadlineExceeded\n\t\t}\n\t}\n\tfor {\n\t\ttimeout = time.Until(end_time)\n\t\tif timeout <= 0 {\n\t\t\treturn os.ErrDeadlineExceeded\n\t\t}\n\t\tselect {\n\t\tcase write_id, more := <-write_done_channel:\n\t\t\tif self.OnWriteComplete != nil {\n\t\t\t\terr := self.OnWriteComplete(write_id, write_id < self.write_msg_id_counter)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif write_id == sentinel {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif !more {\n\t\t\t\treturn fmt.Errorf(\"The write_done_channel was unexpectedly closed\")\n\t\t\t}\n\t\tcase <-time.After(timeout):\n\t\t\treturn os.ErrDeadlineExceeded\n\t\t}\n\t}\n}\n\nfunc (self *Loop) add_write_to_pending_queue(data write_msg) {\n\tif len(self.pending_writes) > 0 || self.tty_write_channel == nil {\n\t\tself.pending_writes = append(self.pending_writes, data)\n\t} else {\n\t\tselect {\n\t\tcase self.tty_write_channel <- data:\n\t\tdefault:\n\t\t\tself.pending_writes = append(self.pending_writes, data)\n\t\t}\n\t}\n}\n\nfunc (self write_msg) is_empty() bool {\n\tif self.bytes == nil {\n\t\treturn self.str == \"\"\n\t}\n\treturn len(self.bytes) == 0\n}\n\nfunc (self *write_msg) write(f *tty.Term) (err error) {\n\tn := 0\n\tif self.bytes == nil {\n\t\tn, err = writestring_ignoring_temporary_errors(f, self.str)\n\t} else {\n\t\tn, err = write_ignoring_temporary_errors(f, self.bytes)\n\t}\n\tif n > 0 {\n\t\tif self.bytes == nil {\n\t\t\tself.str = self.str[n:]\n\t\t} else {\n\t\t\tself.bytes = self.bytes[n:]\n\t\t}\n\t}\n\treturn\n}\n\nfunc write_to_tty(\n\tpipe_r *os.File, term *tty.Term,\n\tjob_channel <-chan write_msg, err_channel chan<- error, write_done_channel chan<- IdType,\n) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr_channel <- parallel.Format_stacktrace_on_panic(r, 1)\n\t\t}\n\t}()\n\tkeep_going := true\n\tdefer func() {\n\t\tpipe_r.Close()\n\t\tclose(write_done_channel)\n\t}()\n\tselector := utils.CreateSelect(2)\n\tpipe_fd := int(pipe_r.Fd())\n\ttty_fd := term.Fd()\n\tselector.RegisterRead(pipe_fd)\n\tselector.RegisterWrite(tty_fd)\n\n\twait_for_write_available := func() {\n\t\tfor {\n\t\t\tn, err := selector.WaitForever()\n\t\t\tif err != nil && err != unix.EINTR {\n\t\t\t\terr_channel <- err\n\t\t\t\tkeep_going = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif n > 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif selector.IsReadyToRead(pipe_fd) {\n\t\t\tkeep_going = false\n\t\t}\n\t}\n\n\twrite_data := func(msg write_msg) {\n\t\tfor !msg.is_empty() {\n\t\t\twait_for_write_available()\n\t\t\tif !keep_going {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := msg.write(term); err != nil {\n\t\t\t\terr_channel <- err\n\t\t\t\tkeep_going = false\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tfor {\n\t\tdata, more := <-job_channel\n\t\tif !more {\n\t\t\tkeep_going = false\n\t\t\tbreak\n\t\t}\n\t\twrite_data(data)\n\t\tif keep_going {\n\t\t\twrite_done_channel <- data.id\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc flush_writer(pipe_w *os.File, tty_write_channel chan<- write_msg, write_done_channel <-chan IdType, pending_writes []write_msg, timeout time.Duration) {\n\twriter_quit := false\n\tdefer func() {\n\t\tif tty_write_channel != nil {\n\t\t\tclose(tty_write_channel)\n\t\t\ttty_write_channel = nil\n\t\t}\n\t\tpipe_w.Close()\n\t\tif !writer_quit {\n\t\t\tfor {\n\t\t\t\t_, more := <-write_done_channel\n\t\t\t\tif !more {\n\t\t\t\t\twriter_quit = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\tdeadline := time.Now().Add(timeout)\n\tfor len(pending_writes) > 0 && !writer_quit {\n\t\ttimeout = time.Until(deadline)\n\t\tif timeout <= 0 {\n\t\t\treturn\n\t\t}\n\t\tselect {\n\t\tcase <-time.After(timeout):\n\t\t\treturn\n\t\tcase _, more := <-write_done_channel:\n\t\t\tif !more {\n\t\t\t\twriter_quit = true\n\t\t\t}\n\t\tcase tty_write_channel <- pending_writes[0]:\n\t\t\tpending_writes = pending_writes[1:]\n\t\t}\n\t}\n\tclose(tty_write_channel)\n\ttty_write_channel = nil\n\ttimeout = time.Until(deadline)\n\tif timeout <= 0 {\n\t\treturn\n\t}\n\tfor !writer_quit {\n\t\tselect {\n\t\tcase _, more := <-write_done_channel:\n\t\t\tif !more {\n\t\t\t\twriter_quit = true\n\t\t\t}\n\t\tcase <-time.After(timeout):\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/tui/mouse.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype LinePos interface {\n\tLessThan(other LinePos) bool\n\tEqual(other LinePos) bool\n\tMinX() int\n\tMaxX() int\n}\n\ntype SelectionBoundary struct {\n\tline                  LinePos\n\tx                     int\n\tin_first_half_of_cell bool\n}\n\nfunc (self *SelectionBoundary) LessThan(other *SelectionBoundary) bool {\n\tif self.line.LessThan(other.line) {\n\t\treturn true\n\t}\n\tif !self.line.Equal(other.line) {\n\t\treturn false\n\t}\n\tif self.x == other.x {\n\t\treturn !self.in_first_half_of_cell && other.in_first_half_of_cell\n\t}\n\treturn self.x < other.x\n}\n\nfunc (self *SelectionBoundary) Equal(other SelectionBoundary) bool {\n\tif self.x != other.x || self.in_first_half_of_cell != other.in_first_half_of_cell {\n\t\treturn false\n\t}\n\tif self.line == nil {\n\t\treturn other.line == nil\n\t}\n\treturn self.line.Equal(other.line)\n}\n\ntype MouseSelection struct {\n\tstart, end              SelectionBoundary\n\tis_active               bool\n\tmin_y, max_y            int\n\tcell_width, cell_height int\n\tdrag_scroll             struct {\n\t\ttimer_id    loop.IdType\n\t\tpixel_gap   int\n\t\tmouse_event loop.MouseEvent\n\t}\n}\n\nfunc (self *MouseSelection) IsEmpty() bool  { return self.start.Equal(self.end) }\nfunc (self *MouseSelection) IsActive() bool { return self.is_active }\nfunc (self *MouseSelection) Finish()        { self.is_active = false }\nfunc (self *MouseSelection) Clear()         { *self = MouseSelection{} }\n\nfunc (ms *MouseSelection) StartNewSelection(ev *loop.MouseEvent, line LinePos, min_y, max_y, cell_width, cell_height int) {\n\t*ms = MouseSelection{cell_width: cell_width, cell_height: cell_height, min_y: min_y, max_y: max_y}\n\tms.start.line = line\n\tms.start.x = max(line.MinX(), min(ev.Cell.X, line.MaxX()))\n\tcell_start := cell_width * ev.Cell.X\n\tms.start.in_first_half_of_cell = ev.Pixel.X <= cell_start+cell_width/2\n\tms.end = ms.start\n\tms.is_active = true\n}\n\nfunc (ms *MouseSelection) Update(ev *loop.MouseEvent, line LinePos) {\n\tms.drag_scroll.timer_id = 0\n\tif ms.is_active {\n\t\tms.end.x = max(line.MinX(), min(ev.Cell.X, line.MaxX()))\n\t\tcell_start := ms.cell_width * ms.end.x\n\t\tms.end.in_first_half_of_cell = ev.Pixel.X <= cell_start+ms.cell_width/2\n\t\tms.end.line = line\n\t}\n}\n\nfunc (ms *MouseSelection) LineBounds(line_pos LinePos) (start_x, end_x int) {\n\tif ms.IsEmpty() {\n\t\treturn -1, -1\n\t}\n\ta, b := &ms.start, &ms.end\n\tif b.LessThan(a) {\n\t\ta, b = b, a\n\t}\n\n\tadjust_end := func(x int, b *SelectionBoundary) (int, int) {\n\t\tif b.in_first_half_of_cell {\n\t\t\tif b.x > x {\n\t\t\t\treturn x, b.x - 1\n\t\t\t}\n\t\t\treturn -1, -1\n\t\t}\n\t\treturn x, b.x\n\t}\n\n\tadjust_start := func(a *SelectionBoundary, x int) (int, int) {\n\t\tif a.in_first_half_of_cell {\n\t\t\treturn a.x, x\n\t\t}\n\t\tif x > a.x {\n\t\t\treturn a.x + 1, x\n\t\t}\n\t\treturn -1, -1\n\t}\n\n\tadjust_both := func(a, b *SelectionBoundary) (int, int) {\n\t\tif a.in_first_half_of_cell {\n\t\t\treturn adjust_end(a.x, b)\n\t\t} else {\n\t\t\tif b.in_first_half_of_cell {\n\t\t\t\ts, e := a.x+1, b.x-1\n\t\t\t\tif e <= s {\n\t\t\t\t\treturn -1, -1\n\t\t\t\t}\n\t\t\t\treturn s, e\n\t\t\t} else {\n\t\t\t\treturn adjust_start(a, b.x)\n\t\t\t}\n\t\t}\n\t}\n\n\tif a.line.LessThan(line_pos) {\n\t\tif line_pos.LessThan(b.line) {\n\t\t\treturn line_pos.MinX(), line_pos.MaxX()\n\t\t} else if b.line.Equal(line_pos) {\n\t\t\treturn adjust_end(line_pos.MinX(), b)\n\t\t}\n\t} else if a.line.Equal(line_pos) {\n\t\tif line_pos.LessThan(b.line) {\n\t\t\treturn adjust_start(a, line_pos.MaxX())\n\t\t} else if b.line.Equal(line_pos) {\n\t\t\treturn adjust_both(a, b)\n\t\t}\n\t}\n\treturn -1, -1\n}\n\nfunc FormatPartOfLine(sgr string, start_x, end_x, y int) string { // uses zero based indices\n\t// DECCARA used to set formatting in specified region using zero based indexing\n\treturn fmt.Sprintf(\"\\x1b[%d;%d;%d;%d;%s$r\", y+1, start_x+1, y+1, end_x+1, sgr)\n}\n\nfunc (ms *MouseSelection) LineFormatSuffix(line_pos LinePos, sgr string, y int) string {\n\ts, e := ms.LineBounds(line_pos)\n\tif s > -1 {\n\t\treturn FormatPartOfLine(sgr, s, e, y)\n\t}\n\treturn \"\"\n}\n\nfunc (ms *MouseSelection) StartLine() LinePos {\n\treturn ms.start.line\n}\n\nfunc (ms *MouseSelection) EndLine() LinePos {\n\treturn ms.end.line\n}\n\nfunc (ms *MouseSelection) OutOfVerticalBounds(ev *loop.MouseEvent) bool {\n\treturn ev.Pixel.Y < ms.min_y*ms.cell_height || ev.Pixel.Y > (ms.max_y+1)*ms.cell_height\n}\n\nfunc (ms *MouseSelection) DragScrollTick(timer_id loop.IdType, lp *loop.Loop, callback loop.TimerCallback, do_scroll func(int, *loop.MouseEvent) error) error {\n\tif !ms.is_active || ms.drag_scroll.timer_id != timer_id || ms.drag_scroll.pixel_gap == 0 {\n\t\treturn nil\n\t}\n\tamt := 1\n\tif ms.drag_scroll.pixel_gap < 0 {\n\t\tamt *= -1\n\t}\n\terr := do_scroll(amt, &ms.drag_scroll.mouse_event)\n\tif err == nil {\n\t\tms.drag_scroll.timer_id, _ = lp.AddTimer(50*time.Millisecond, false, callback)\n\t}\n\treturn err\n}\n\nfunc (ms *MouseSelection) DragScroll(ev *loop.MouseEvent, lp *loop.Loop, callback loop.TimerCallback) {\n\tif !ms.is_active {\n\t\treturn\n\t}\n\tupper := ms.min_y * ms.cell_height\n\tlower := (ms.max_y + 1) * ms.cell_height\n\tif ev.Pixel.Y < upper {\n\t\tms.drag_scroll.pixel_gap = ev.Pixel.Y - upper\n\t} else if ev.Pixel.Y > lower {\n\t\tms.drag_scroll.pixel_gap = ev.Pixel.Y - lower\n\t}\n\tif ms.drag_scroll.timer_id == 0 && ms.drag_scroll.pixel_gap != 0 {\n\t\tms.drag_scroll.timer_id, _ = lp.AddTimer(50*time.Millisecond, false, callback)\n\t}\n\tms.drag_scroll.mouse_event = *ev\n}\n\ntype Point struct {\n\tX, Y int\n}\n\nfunc (p Point) Sub(other Point) Point {\n\treturn Point{X: p.X - other.X, Y: p.Y - other.Y}\n}\n\ntype CellRegion struct {\n\tTopLeft, BottomRight Point\n\tId                   string\n\tOnClick              []func(id string) error                                       // simple left click ignoring modifiers\n\tOnClickEvent         func(id string, ev *loop.MouseEvent, cell_offset Point) error // any click event\n\tPointerShape         loop.PointerShape\n\tHoverStyle           string // set to \"default\" for the global hover style\n}\n\nfunc (c CellRegion) Contains(x, y int) bool { // 0-based\n\tif c.TopLeft.Y > y || c.BottomRight.Y < y {\n\t\treturn false\n\t}\n\treturn (y > c.TopLeft.Y || (y == c.TopLeft.Y && x >= c.TopLeft.X)) && (y < c.BottomRight.Y || (y == c.BottomRight.Y && x <= c.BottomRight.X))\n}\n\ntype MouseState struct {\n\tCell, Pixel Point\n\tPressed     struct{ Left, Right, Middle, Fourth, Fifth, Sixth, Seventh bool }\n\n\tregions           []*CellRegion\n\tregion_id_map     map[string][]*CellRegion\n\tregion_line_map   map[int][]*CellRegion\n\thovered_ids       *utils.Set[string]\n\tdefault_url_style struct {\n\t\tvalue  string\n\t\tloaded bool\n\t}\n}\n\nfunc (m *MouseState) add_region(cr CellRegion) *CellRegion {\n\tm.regions = append(m.regions, &cr)\n\tif m.region_id_map == nil {\n\t\tm.region_id_map = make(map[string][]*CellRegion)\n\t\tm.region_line_map = make(map[int][]*CellRegion)\n\t}\n\tm.region_id_map[cr.Id] = append(m.region_id_map[cr.Id], &cr)\n\tfor y := cr.TopLeft.Y; y <= cr.BottomRight.Y; y++ {\n\t\tm.region_line_map[y] = append(m.region_line_map[y], &cr)\n\t}\n\treturn &cr\n}\n\nfunc (m *MouseState) AddCellRegion(id string, start_x, start_y, end_x, end_y int, on_click ...func(id string) error) *CellRegion {\n\treturn m.add_region(CellRegion{\n\t\tTopLeft: Point{start_x, start_y}, BottomRight: Point{end_x, end_y}, Id: id, OnClick: on_click, PointerShape: loop.POINTER_POINTER, HoverStyle: \"default\"})\n}\n\nfunc (m *MouseState) ClearCellRegions() {\n\tm.regions = nil\n\tm.region_id_map = nil\n\tm.hovered_ids = nil\n\tm.region_line_map = nil\n}\n\nfunc (m *MouseState) UpdateHoveredIds() (changed bool) {\n\th := utils.NewSet[string]()\n\tfor _, r := range m.region_line_map[m.Cell.Y] {\n\t\tif r.Contains(m.Cell.X, m.Cell.Y) {\n\t\t\th.Add(r.Id)\n\t\t}\n\t}\n\tchanged = !h.Equal(m.hovered_ids)\n\tm.hovered_ids = h\n\treturn\n}\n\nfunc (m *MouseState) ApplyHoverStyles(lp *loop.Loop, style ...string) {\n\tif m.hovered_ids == nil || m.hovered_ids.Len() == 0 {\n\t\tlp.ClearPointerShapes()\n\t\treturn\n\t}\n\ths := \"\"\n\tif len(style) == 0 {\n\t\tif !m.default_url_style.loaded {\n\t\t\tm.default_url_style.loaded = true\n\t\t\tcolor, style := kitty.DefaultUrlColor, kitty.DefaultUrlStyle\n\t\t\tline_handler := func(key, val string) error {\n\t\t\t\tswitch key {\n\t\t\t\tcase \"url_color\":\n\t\t\t\t\tcolor = val\n\t\t\t\tcase \"url_style\":\n\t\t\t\t\tstyle = val\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tconfig.ReadKittyConfig(line_handler)\n\t\t\tif style != \"none\" && style != \"\" {\n\t\t\t\tm.default_url_style.value = fmt.Sprintf(\"u=%s uc=%s\", style, color)\n\t\t\t}\n\t\t}\n\t\ths = m.default_url_style.value\n\t} else {\n\t\ths = style[0]\n\t}\n\tis_hovered := false\n\tps := loop.DEFAULT_POINTER\n\tfor id := range m.hovered_ids.Iterable() {\n\t\tfor _, r := range m.region_id_map[id] {\n\t\t\tif r.HoverStyle != \"\" {\n\t\t\t\ts := strings.Replace(r.HoverStyle, \"default\", hs, 1)\n\t\t\t\tlp.StyleRegion(s, r.TopLeft.X, r.TopLeft.Y, r.BottomRight.X, r.BottomRight.Y)\n\t\t\t}\n\t\t\tis_hovered = true\n\t\t\tps = r.PointerShape\n\t\t}\n\t}\n\tif is_hovered {\n\t\tif s, has := lp.CurrentPointerShape(); !has || s != ps {\n\t\t\tlp.PushPointerShape(ps)\n\t\t}\n\t} else {\n\t\tlp.ClearPointerShapes()\n\t}\n}\n\nfunc (m *MouseState) DispatchEventToHoveredRegions(ev *loop.MouseEvent) error {\n\tif ev.Event_type != loop.MOUSE_CLICK {\n\t\treturn nil\n\t}\n\tis_simple_click := ev.Buttons&loop.LEFT_MOUSE_BUTTON != 0\n\tseen := utils.NewSet[string]()\n\tfor id := range m.hovered_ids.Iterable() {\n\t\tfor _, r := range m.region_id_map[id] {\n\t\t\tif seen.Has(r.Id) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tseen.Add(r.Id)\n\t\t\tif is_simple_click {\n\t\t\t\tfor _, f := range r.OnClick {\n\t\t\t\t\tif err := f(r.Id); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif r.OnClickEvent != nil {\n\t\t\t\tif err := r.OnClickEvent(r.Id, ev, m.Cell.Sub(r.TopLeft)); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (m *MouseState) ClickHoveredRegions() error {\n\tseen := utils.NewSet[string]()\n\tfor id := range m.hovered_ids.Iterable() {\n\t\tfor _, r := range m.region_id_map[id] {\n\t\t\tif seen.Has(r.Id) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tseen.Add(r.Id)\n\t\t\tfor _, f := range r.OnClick {\n\t\t\t\tif err := f(r.Id); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (m *MouseState) UpdateState(ev *loop.MouseEvent) (hovered_ids_changed bool) {\n\tm.Cell = ev.Cell\n\tm.Pixel = ev.Pixel\n\tif ev.Event_type == loop.MOUSE_PRESS || ev.Event_type == loop.MOUSE_RELEASE {\n\t\tpressed := ev.Event_type == loop.MOUSE_PRESS\n\t\tif ev.Buttons&loop.LEFT_MOUSE_BUTTON != 0 {\n\t\t\tm.Pressed.Left = pressed\n\t\t}\n\t\tif ev.Buttons&loop.RIGHT_MOUSE_BUTTON != 0 {\n\t\t\tm.Pressed.Right = pressed\n\t\t}\n\t\tif ev.Buttons&loop.MIDDLE_MOUSE_BUTTON != 0 {\n\t\t\tm.Pressed.Middle = pressed\n\t\t}\n\t\tif ev.Buttons&loop.FOURTH_MOUSE_BUTTON != 0 {\n\t\t\tm.Pressed.Fourth = pressed\n\t\t}\n\t\tif ev.Buttons&loop.FIFTH_MOUSE_BUTTON != 0 {\n\t\t\tm.Pressed.Fifth = pressed\n\t\t}\n\t\tif ev.Buttons&loop.SIXTH_MOUSE_BUTTON != 0 {\n\t\t\tm.Pressed.Sixth = pressed\n\t\t}\n\t\tif ev.Buttons&loop.SEVENTH_MOUSE_BUTTON != 0 {\n\t\t\tm.Pressed.Seventh = pressed\n\t\t}\n\t}\n\treturn m.UpdateHoveredIds()\n}\n"
  },
  {
    "path": "tools/tui/password.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\ntype KilledBySignal struct {\n\tMsg        string\n\tSignalName string\n}\n\nfunc (self *KilledBySignal) Error() string { return self.Msg }\n\nvar Canceled = errors.New(\"Canceled by user\")\n\nfunc ReadPassword(prompt string, kill_if_signaled bool) (password string, err error) {\n\tlp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.FullKeyboardProtocol)\n\tshadow := \"\"\n\tif err != nil {\n\t\treturn\n\t}\n\tcapspress_was_locked := false\n\thas_caps_lock := false\n\n\tredraw_prompt := func() {\n\t\ttext := prompt + shadow\n\t\tlp.QueueWriteString(\"\\r\")\n\t\tlp.ClearToEndOfLine()\n\t\tif has_caps_lock {\n\t\t\tlp.QueueWriteString(\"\\x1b[31m[CapsLock on!]\\x1b[39m \")\n\t\t}\n\t\tlp.QueueWriteString(text)\n\t}\n\n\tlp.OnInitialize = func() (string, error) {\n\t\tlp.QueueWriteString(prompt)\n\t\tlp.SetCursorShape(loop.BAR_CURSOR, true)\n\t\treturn \"\", nil\n\t}\n\n\tlp.OnFinalize = func() string {\n\t\tlp.SetCursorShape(loop.BLOCK_CURSOR, true)\n\t\treturn \"\\r\\n\"\n\t}\n\n\tlp.OnText = func(text string, from_key_event bool, in_bracketed_paste bool) error {\n\t\told_width := wcswidth.Stringwidth(password)\n\t\tpassword += text\n\t\tnew_width := wcswidth.Stringwidth(password)\n\t\tif new_width > old_width {\n\t\t\textra := strings.Repeat(\"*\", new_width-old_width)\n\t\t\tlp.QueueWriteString(extra)\n\t\t\tshadow += extra\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnKeyEvent = func(event *loop.KeyEvent) error {\n\t\thas_caps := false\n\t\tif strings.ToLower(event.Key) == \"caps_lock\" {\n\t\t\tif event.Type == loop.RELEASE {\n\t\t\t\thas_caps = !capspress_was_locked\n\t\t\t\tcapspress_was_locked = false\n\t\t\t} else {\n\t\t\t\tcapspress_was_locked = event.HasCapsLock()\n\t\t\t\thas_caps = true\n\t\t\t}\n\t\t} else {\n\t\t\thas_caps = event.HasCapsLock()\n\t\t}\n\t\tif has_caps_lock != has_caps {\n\t\t\thas_caps_lock = has_caps\n\t\t\tredraw_prompt()\n\t\t}\n\t\tif event.MatchesPressOrRepeat(\"backspace\") || event.MatchesPressOrRepeat(\"delete\") {\n\t\t\tevent.Handled = true\n\t\t\tif len(password) > 0 {\n\t\t\t\told_width := wcswidth.Stringwidth(password)\n\t\t\t\tpassword = password[:len(password)-1]\n\t\t\t\tnew_width := wcswidth.Stringwidth(password)\n\t\t\t\tdelta := old_width - new_width\n\t\t\t\tif delta > 0 {\n\t\t\t\t\tif delta > len(shadow) {\n\t\t\t\t\t\tdelta = len(shadow)\n\t\t\t\t\t}\n\t\t\t\t\tshadow = shadow[:len(shadow)-delta]\n\t\t\t\t\tlp.QueueWriteString(strings.Repeat(\"\\x08\\x1b[P\", delta))\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlp.Beep()\n\t\t\t}\n\t\t}\n\t\tif event.MatchesPressOrRepeat(\"enter\") || event.MatchesPressOrRepeat(\"return\") {\n\t\t\tevent.Handled = true\n\t\t\tif password == \"\" {\n\t\t\t\tlp.Quit(1)\n\t\t\t} else {\n\t\t\t\tlp.Quit(0)\n\t\t\t}\n\t\t}\n\t\tif event.MatchesPressOrRepeat(\"esc\") {\n\t\t\tevent.Handled = true\n\t\t\tlp.Quit(1)\n\t\t\treturn Canceled\n\t\t}\n\t\treturn nil\n\t}\n\n\tlp.OnResumeFromStop = func() error {\n\t\tredraw_prompt()\n\t\treturn nil\n\t}\n\n\terr = lp.Run()\n\tif err != nil {\n\t\treturn\n\t}\n\tds := lp.DeathSignalName()\n\tif ds != \"\" {\n\t\tif kill_if_signaled {\n\t\t\tlp.KillIfSignalled()\n\t\t\treturn\n\t\t}\n\t\treturn \"\", &KilledBySignal{Msg: fmt.Sprint(\"Killed by signal: \", ds), SignalName: ds}\n\t}\n\tif lp.ExitCode() != 0 {\n\t\tpassword = \"\"\n\t}\n\treturn password, nil\n}\n"
  },
  {
    "path": "tools/tui/progress-bar.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\nfunc RepeatChar(char string, count int) string {\n\tif count <= 5 {\n\t\treturn strings.Repeat(char, count)\n\t}\n\treturn fmt.Sprintf(\"%s\\x1b[%db\", char, count-1)\n}\n\nfunc RenderProgressBar(frac float64, width int) string {\n\tfc := markup.New(true)\n\tif frac >= 1 {\n\t\treturn fc.Green(RepeatChar(\"🬋\", width))\n\t}\n\tif frac <= 0 {\n\t\treturn fc.Dim(RepeatChar(\"🬋\", width))\n\t}\n\tw := frac * float64(width)\n\tfl := int(w)\n\toverhang := w - float64(fl)\n\tfilled := RepeatChar(\"🬋\", fl)\n\tneeds_break := false\n\tif overhang < 0.2 {\n\t\tneeds_break = true\n\t} else if overhang < 0.8 {\n\t\tfilled += \"🬃\"\n\t\tfl += 1\n\t} else {\n\t\tif fl < width-1 {\n\t\t\tfilled += \"🬋\"\n\t\t\tfl += 1\n\t\t\tneeds_break = true\n\t\t} else {\n\t\t\tfilled += \"🬃\"\n\t\t\tfl += 1\n\t\t}\n\t}\n\tans := fc.Blue(filled)\n\tunfilled := \"\"\n\tul := 0\n\tif width > fl && needs_break {\n\t\tunfilled = \"🬇\"\n\t\tul = 1\n\t}\n\tfiller := width - fl - ul\n\tif filler > 0 {\n\t\tunfilled += RepeatChar(\"🬋\", filler)\n\t}\n\tif unfilled != \"\" {\n\t\tans += fc.Dim(unfilled)\n\t}\n\treturn ans\n}\n"
  },
  {
    "path": "tools/tui/progress-bar_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestRenderProgressBar(t *testing.T) {\n\n\ttest := func(frac float64, width int) {\n\t\tb := RenderProgressBar(frac, width)\n\t\ta := wcswidth.Stringwidth(b)\n\t\tif a != width {\n\t\t\tt.Fatalf(\"Actual length %d != Expected length %d with fraction: %v\\n%s\", a, width, frac, b)\n\t\t}\n\t}\n\ttest(0.9376609994848016, 47)\n\ttest(0.9459041731066461, 47)\n\ttest(0.9500257599175682, 47)\n}\n"
  },
  {
    "path": "tools/tui/readline/actions.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage readline\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nfunc (self *Readline) text_upto_cursor_pos() string {\n\tbuf := strings.Builder{}\n\tbuf.Grow(1024)\n\tfor i, line := range self.input_state.lines {\n\t\tif i == self.input_state.cursor.Y {\n\t\t\tbuf.WriteString(line[:min(len(line), self.input_state.cursor.X)])\n\t\t\tbreak\n\t\t} else {\n\t\t\tbuf.WriteString(line)\n\t\t\tbuf.WriteString(\"\\n\")\n\t\t}\n\t}\n\treturn buf.String()\n}\n\nfunc (self *Readline) text_after_cursor_pos() string {\n\tbuf := strings.Builder{}\n\tbuf.Grow(1024)\n\tfor i, line := range self.input_state.lines {\n\t\tif i == self.input_state.cursor.Y {\n\t\t\tbuf.WriteString(line[min(len(line), self.input_state.cursor.X):])\n\t\t\tbuf.WriteString(\"\\n\")\n\t\t} else if i > self.input_state.cursor.Y {\n\t\t\tbuf.WriteString(line)\n\t\t\tbuf.WriteString(\"\\n\")\n\t\t}\n\t}\n\tans := buf.String()\n\tif ans != \"\" {\n\t\tans = ans[:len(ans)-1]\n\t}\n\treturn ans\n}\n\nfunc (self *Readline) all_text() string {\n\treturn strings.Join(self.input_state.lines, \"\\n\")\n}\n\nfunc (self *Readline) set_text(text string) {\n\tself.move_to_start()\n\tself.erase_chars_after_cursor(123456789, true)\n\tif text != \"\" {\n\t\tself.add_text(text)\n\t}\n\tself.move_to_end()\n}\n\nfunc (self *Readline) add_text(text string) {\n\tnew_lines := make([]string, 0, len(self.input_state.lines)+4)\n\tnew_lines = append(new_lines, self.input_state.lines[:self.input_state.cursor.Y]...)\n\tvar lines_after []string\n\tif len(self.input_state.lines) > self.input_state.cursor.Y+1 {\n\t\tlines_after = self.input_state.lines[self.input_state.cursor.Y+1:]\n\t}\n\thas_trailing_newline := strings.HasSuffix(text, \"\\n\")\n\n\tadd_line_break := func(line string) {\n\t\tnew_lines = append(new_lines, line)\n\t\tself.input_state.cursor.X = len(line)\n\t\tself.input_state.cursor.Y += 1\n\t}\n\tcline := self.input_state.lines[self.input_state.cursor.Y]\n\tbefore_first_line := cline[:self.input_state.cursor.X]\n\tafter_first_line := \"\"\n\tif self.input_state.cursor.X < len(cline) {\n\t\tafter_first_line = cline[self.input_state.cursor.X:]\n\t}\n\tfor i, line := range utils.Splitlines(text) {\n\t\tif i > 0 {\n\t\t\tadd_line_break(line)\n\t\t} else {\n\t\t\tline := before_first_line + line\n\t\t\tself.input_state.cursor.X = len(line)\n\t\t\tnew_lines = append(new_lines, line)\n\t\t}\n\t}\n\tif has_trailing_newline {\n\t\tadd_line_break(\"\")\n\t}\n\tif after_first_line != \"\" {\n\t\tif len(new_lines) == 0 {\n\t\t\tnew_lines = append(new_lines, \"\")\n\t\t}\n\t\tnew_lines[len(new_lines)-1] += after_first_line\n\t}\n\tif len(lines_after) > 0 {\n\t\tnew_lines = append(new_lines, lines_after...)\n\t}\n\tself.input_state.lines = new_lines\n}\n\nfunc (self *Readline) move_cursor_left(amt uint, traverse_line_breaks bool) (amt_moved uint) {\n\tfor amt_moved < amt {\n\t\tif self.input_state.cursor.X == 0 {\n\t\t\tif !traverse_line_breaks || self.input_state.cursor.Y == 0 {\n\t\t\t\treturn amt_moved\n\t\t\t}\n\t\t\tself.input_state.cursor.Y -= 1\n\t\t\tself.input_state.cursor.X = len(self.input_state.lines[self.input_state.cursor.Y])\n\t\t\tamt_moved++\n\t\t\tcontinue\n\t\t}\n\t\tline := self.input_state.lines[self.input_state.cursor.Y]\n\t\tfor ci := wcswidth.NewCellIterator(line[:self.input_state.cursor.X]).GotoEnd(); amt_moved < amt && ci.Backward(); amt_moved++ {\n\t\t\tself.input_state.cursor.X -= len(ci.Current())\n\t\t}\n\t}\n\treturn amt_moved\n}\n\nfunc (self *Readline) move_cursor_right(amt uint, traverse_line_breaks bool) (amt_moved uint) {\n\tfor amt_moved < amt {\n\t\tline := self.input_state.lines[self.input_state.cursor.Y]\n\t\tif self.input_state.cursor.X >= len(line) {\n\t\t\tif !traverse_line_breaks || self.input_state.cursor.Y == len(self.input_state.lines)-1 {\n\t\t\t\treturn amt_moved\n\t\t\t}\n\t\t\tself.input_state.cursor.Y += 1\n\t\t\tself.input_state.cursor.X = 0\n\t\t\tamt_moved++\n\t\t\tcontinue\n\t\t}\n\n\t\tfor ci := wcswidth.NewCellIterator(line[self.input_state.cursor.X:]); amt_moved < amt && ci.Forward(); amt_moved++ {\n\t\t\tself.input_state.cursor.X += len(ci.Current())\n\t\t}\n\t}\n\treturn amt_moved\n}\n\nfunc (self *Readline) move_cursor_to_target_line(source_line, target_line *ScreenLine) {\n\tif source_line != target_line {\n\t\tvisual_distance_into_text := source_line.CursorCell - source_line.Prompt.Length\n\t\tself.input_state.cursor.Y = target_line.ParentLineNumber\n\t\ttp := wcswidth.TruncateToVisualLength(target_line.Text, visual_distance_into_text)\n\t\tself.input_state.cursor.X = target_line.OffsetInParentLine + len(tp)\n\t}\n}\n\nfunc (self *Readline) move_cursor_vertically(amt int) (ans int) {\n\tif self.screen_width == 0 {\n\t\tself.update_current_screen_size()\n\t}\n\tscreen_lines := self.get_screen_lines()\n\tcursor_line_num := 0\n\tfor i, sl := range screen_lines {\n\t\tif sl.CursorCell > -1 {\n\t\t\tcursor_line_num = i\n\t\t\tbreak\n\t\t}\n\t}\n\ttarget_line_num := min(max(0, cursor_line_num+amt), len(screen_lines)-1)\n\tans = target_line_num - cursor_line_num\n\tif ans != 0 {\n\t\tself.move_cursor_to_target_line(screen_lines[cursor_line_num], screen_lines[target_line_num])\n\t}\n\treturn ans\n}\n\nfunc (self *Readline) move_cursor_down(amt uint) uint {\n\tans := uint(0)\n\tif self.screen_width == 0 {\n\t\tself.update_current_screen_size()\n\t}\n\treturn ans\n}\n\nfunc (self *Readline) move_to_start_of_line() bool {\n\tif self.input_state.cursor.X > 0 {\n\t\tself.input_state.cursor.X = 0\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (self *Readline) move_to_end_of_line() bool {\n\tline := self.input_state.lines[self.input_state.cursor.Y]\n\tif self.input_state.cursor.X >= len(line) {\n\t\treturn false\n\t}\n\tself.input_state.cursor.X = len(line)\n\treturn true\n}\n\nfunc (self *Readline) move_to_start() bool {\n\tif self.input_state.cursor.Y == 0 && self.input_state.cursor.X == 0 {\n\t\treturn false\n\t}\n\tself.input_state.cursor.Y = 0\n\tself.move_to_start_of_line()\n\treturn true\n}\n\nfunc (self *Readline) move_to_end() bool {\n\tline := self.input_state.lines[self.input_state.cursor.Y]\n\tif self.input_state.cursor.Y == len(self.input_state.lines)-1 && self.input_state.cursor.X >= len(line) {\n\t\treturn false\n\t}\n\tself.input_state.cursor.Y = len(self.input_state.lines) - 1\n\tself.move_to_end_of_line()\n\treturn true\n}\n\nfunc (self *Readline) erase_between(start, end Position) string {\n\tif end.Less(start) {\n\t\tstart, end = end, start\n\t}\n\tbuf := strings.Builder{}\n\tif start.Y == end.Y {\n\t\tline := self.input_state.lines[start.Y]\n\t\tbuf.WriteString(line[start.X:end.X])\n\t\tself.input_state.lines[start.Y] = line[:start.X] + line[end.X:]\n\t\tif self.input_state.cursor.Y == start.Y && self.input_state.cursor.X >= start.X {\n\t\t\tif self.input_state.cursor.X < end.X {\n\t\t\t\tself.input_state.cursor.X = start.X\n\t\t\t} else {\n\t\t\t\tself.input_state.cursor.X -= end.X - start.X\n\t\t\t}\n\t\t}\n\t\treturn buf.String()\n\t}\n\tlines := make([]string, 0, len(self.input_state.lines))\n\tfor i, line := range self.input_state.lines {\n\t\tif i < start.Y || i > end.Y {\n\t\t\tlines = append(lines, line)\n\t\t} else if i == start.Y {\n\t\t\tlines = append(lines, line[:start.X])\n\t\t\tbuf.WriteString(line[start.X:])\n\t\t\tif self.input_state.cursor.Y == i && self.input_state.cursor.X > start.X {\n\t\t\t\tself.input_state.cursor.X = start.X\n\t\t\t}\n\t\t} else if i == end.Y {\n\t\t\tlines[len(lines)-1] += line[end.X:]\n\t\t\tbuf.WriteString(line[:end.X])\n\t\t\tif i == self.input_state.cursor.Y {\n\t\t\t\tself.input_state.cursor.Y = start.Y\n\t\t\t\tif self.input_state.cursor.X < end.X {\n\t\t\t\t\tself.input_state.cursor.X = start.X\n\t\t\t\t} else {\n\t\t\t\t\tself.input_state.cursor.X -= end.X - start.X\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif i == self.input_state.cursor.Y {\n\t\t\t\tself.input_state.cursor = start\n\t\t\t}\n\t\t\tbuf.WriteString(line)\n\t\t\tbuf.WriteString(\"\\n\")\n\t\t}\n\t}\n\tself.input_state.lines = lines\n\treturn buf.String()\n}\n\nfunc (self *Readline) erase_chars_before_cursor(amt uint, traverse_line_breaks bool) uint {\n\tpos := self.input_state.cursor\n\tnum := self.move_cursor_left(amt, traverse_line_breaks)\n\tif num == 0 {\n\t\treturn num\n\t}\n\tself.erase_between(self.input_state.cursor, pos)\n\treturn num\n}\n\nfunc (self *Readline) erase_chars_after_cursor(amt uint, traverse_line_breaks bool) uint {\n\tpos := self.input_state.cursor\n\tnum := self.move_cursor_right(amt, traverse_line_breaks)\n\tif num == 0 {\n\t\treturn num\n\t}\n\tself.erase_between(pos, self.input_state.cursor)\n\treturn num\n}\n\nfunc has_word_chars(text string) bool {\n\tfor _, ch := range text {\n\t\tif unicode.IsLetter(ch) || unicode.IsDigit(ch) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *Readline) move_to_end_of_word(amt uint, traverse_line_breaks bool, is_part_of_word func(string) bool) (num_of_words_moved uint) {\n\tif amt == 0 {\n\t\treturn 0\n\t}\n\tline := self.input_state.lines[self.input_state.cursor.Y]\n\tin_word := false\n\tci := wcswidth.NewCellIterator(line[self.input_state.cursor.X:])\n\tsz := 0\n\n\tfor ci.Forward() {\n\t\tcurrent_is_word_char := is_part_of_word(ci.Current())\n\t\tplen := sz\n\t\tsz += len(ci.Current())\n\t\tif current_is_word_char {\n\t\t\tin_word = true\n\t\t} else if in_word {\n\t\t\tself.input_state.cursor.X += plen\n\t\t\tamt--\n\t\t\tnum_of_words_moved++\n\t\t\tif amt == 0 {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tin_word = false\n\t\t}\n\t}\n\tif self.move_to_end_of_line() {\n\t\tamt--\n\t\tnum_of_words_moved++\n\t}\n\tif amt > 0 {\n\t\tif traverse_line_breaks && self.input_state.cursor.Y < len(self.input_state.lines)-1 {\n\t\t\tself.input_state.cursor.Y++\n\t\t\tself.input_state.cursor.X = 0\n\t\t\tnum_of_words_moved += self.move_to_end_of_word(amt, traverse_line_breaks, is_part_of_word)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Readline) move_to_start_of_word(amt uint, traverse_line_breaks bool, is_part_of_word func(string) bool) (num_of_words_moved uint) {\n\tif amt == 0 {\n\t\treturn 0\n\t}\n\tline := self.input_state.lines[self.input_state.cursor.Y]\n\tin_word := false\n\tci := wcswidth.NewCellIterator(line[:self.input_state.cursor.X]).GotoEnd()\n\tsz := 0\n\n\tfor ci.Backward() {\n\t\tcurrent_is_word_char := is_part_of_word(ci.Current())\n\t\tplen := sz\n\t\tsz += len(ci.Current())\n\t\tif current_is_word_char {\n\t\t\tin_word = true\n\t\t} else if in_word {\n\t\t\tself.input_state.cursor.X -= plen\n\t\t\tamt--\n\t\t\tnum_of_words_moved++\n\t\t\tif amt == 0 {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tin_word = false\n\t\t}\n\t}\n\tif self.move_to_start_of_line() {\n\t\tamt--\n\t\tnum_of_words_moved++\n\t}\n\tif amt > 0 {\n\t\tif traverse_line_breaks && self.input_state.cursor.Y > 0 {\n\t\t\tself.input_state.cursor.Y--\n\t\t\tself.input_state.cursor.X = len(self.input_state.lines[self.input_state.cursor.Y])\n\t\t\tnum_of_words_moved += self.move_to_start_of_word(amt, traverse_line_breaks, has_word_chars)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Readline) kill_text(text string) {\n\tif ActionStartKillActions < self.last_action && self.last_action < ActionEndKillActions {\n\t\tself.kill_ring.append_to_existing_item(text)\n\t} else {\n\t\tself.kill_ring.add_new_item(text)\n\t}\n}\n\nfunc (self *Readline) kill_to_end_of_line() bool {\n\tline := self.input_state.lines[self.input_state.cursor.Y]\n\tif self.input_state.cursor.X >= len(line) {\n\t\treturn false\n\t}\n\tself.input_state.lines[self.input_state.cursor.Y] = line[:self.input_state.cursor.X]\n\tself.kill_text(line[self.input_state.cursor.X:])\n\treturn true\n}\n\nfunc (self *Readline) kill_to_start_of_line() bool {\n\tline := self.input_state.lines[self.input_state.cursor.Y]\n\tif self.input_state.cursor.X <= 0 {\n\t\treturn false\n\t}\n\tself.input_state.lines[self.input_state.cursor.Y] = line[self.input_state.cursor.X:]\n\tself.kill_text(line[:self.input_state.cursor.X])\n\tself.input_state.cursor.X = 0\n\treturn true\n}\n\nfunc (self *Readline) kill_next_word(amt uint, traverse_line_breaks bool) (num_killed uint) {\n\tbefore := self.input_state.cursor\n\tnum_killed = self.move_to_end_of_word(amt, traverse_line_breaks, has_word_chars)\n\tif num_killed > 0 {\n\t\tself.kill_text(self.erase_between(before, self.input_state.cursor))\n\t}\n\treturn num_killed\n}\n\nfunc (self *Readline) kill_previous_word(amt uint, traverse_line_breaks bool) (num_killed uint) {\n\tbefore := self.input_state.cursor\n\tnum_killed = self.move_to_start_of_word(amt, traverse_line_breaks, has_word_chars)\n\tif num_killed > 0 {\n\t\tself.kill_text(self.erase_between(self.input_state.cursor, before))\n\t}\n\treturn num_killed\n}\n\nfunc has_no_space_chars(text string) bool {\n\tfor _, r := range text {\n\t\tif unicode.IsSpace(r) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (self *Readline) kill_previous_space_delimited_word(amt uint, traverse_line_breaks bool) (num_killed uint) {\n\tbefore := self.input_state.cursor\n\tnum_killed = self.move_to_start_of_word(amt, traverse_line_breaks, has_no_space_chars)\n\tif num_killed > 0 {\n\t\tself.kill_text(self.erase_between(self.input_state.cursor, before))\n\t}\n\treturn num_killed\n}\n\nfunc (self *Readline) ensure_position_in_bounds(pos *Position) *Position {\n\tpos.Y = max(0, min(pos.Y, len(self.input_state.lines)-1))\n\tline := self.input_state.lines[pos.Y]\n\tpos.X = max(0, min(pos.X, len(line)))\n\treturn pos\n}\n\nfunc (self *Readline) yank(repeat_count uint, pop bool) bool {\n\tif pop && self.last_action != ActionYank && self.last_action != ActionPopYank {\n\t\treturn false\n\t}\n\ttext := \"\"\n\tif pop {\n\t\ttext = self.kill_ring.pop_yank()\n\t} else {\n\t\ttext = self.kill_ring.yank()\n\t}\n\tif text == \"\" {\n\t\treturn false\n\t}\n\tbefore := self.input_state.cursor\n\tif pop {\n\t\tself.ensure_position_in_bounds(&self.last_yank_extent.start)\n\t\tself.ensure_position_in_bounds(&self.last_yank_extent.end)\n\t\tself.erase_between(self.last_yank_extent.start, self.last_yank_extent.end)\n\t\tself.input_state.cursor = self.last_yank_extent.start\n\t\tbefore = self.input_state.cursor\n\t}\n\tself.add_text(text)\n\tself.last_yank_extent.start = before\n\tself.last_yank_extent.end = self.input_state.cursor\n\treturn true\n}\n\nfunc (self *Readline) history_first() bool {\n\tself.create_history_matches()\n\treturn self.history_matches.first(self)\n}\n\nfunc (self *Readline) history_last() bool {\n\tself.create_history_matches()\n\treturn self.history_matches.last(self)\n}\n\nfunc (self *Readline) history_prev(repeat_count uint) bool {\n\tself.create_history_matches()\n\treturn self.history_matches.previous(repeat_count, self)\n}\n\nfunc (self *Readline) history_next(repeat_count uint) bool {\n\tself.create_history_matches()\n\treturn self.history_matches.next(repeat_count, self)\n}\n\nfunc (self *Readline) _perform_action(ac Action, repeat_count uint) (err error, dont_set_last_action bool) {\n\tswitch ac {\n\tcase ActionBackspace:\n\t\tif self.history_search != nil {\n\t\t\tif self.remove_text_from_history_search(repeat_count) > 0 {\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\tif self.erase_chars_before_cursor(repeat_count, true) > 0 {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\tcase ActionDelete:\n\t\tif self.erase_chars_after_cursor(repeat_count, true) > 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionMoveToStartOfLine:\n\t\tif self.move_to_start_of_line() {\n\t\t\treturn\n\t\t}\n\tcase ActionMoveToEndOfLine:\n\t\tif self.move_to_end_of_line() {\n\t\t\treturn\n\t\t}\n\tcase ActionMoveToEndOfWord:\n\t\tif self.move_to_end_of_word(repeat_count, true, has_word_chars) > 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionMoveToStartOfWord:\n\t\tif self.move_to_start_of_word(repeat_count, true, has_word_chars) > 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionMoveToStartOfDocument:\n\t\tif self.move_to_start() {\n\t\t\treturn\n\t\t}\n\tcase ActionMoveToEndOfDocument:\n\t\tif self.move_to_end() {\n\t\t\treturn\n\t\t}\n\tcase ActionCursorLeft:\n\t\tif self.move_cursor_left(repeat_count, true) > 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionCursorRight:\n\t\tif self.move_cursor_right(repeat_count, true) > 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionEndInput:\n\t\tline := self.input_state.lines[self.input_state.cursor.Y]\n\t\tif line == \"\" {\n\t\t\terr = io.EOF\n\n\t\t} else {\n\t\t\terr = self.perform_action(ActionAcceptInput, 1)\n\t\t}\n\t\treturn\n\tcase ActionAcceptInput:\n\t\terr = ErrAcceptInput\n\t\treturn\n\tcase ActionCursorUp:\n\t\tif self.move_cursor_vertically(-int(repeat_count)) != 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionCursorDown:\n\t\tif self.move_cursor_vertically(int(repeat_count)) != 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionHistoryPreviousOrCursorUp:\n\t\tdont_set_last_action = true\n\t\tif self.perform_action(ActionCursorUp, repeat_count) == ErrCouldNotPerformAction {\n\t\t\terr = self.perform_action(ActionHistoryPrevious, repeat_count)\n\t\t}\n\t\treturn\n\tcase ActionHistoryNextOrCursorDown:\n\t\tdont_set_last_action = true\n\t\tif self.perform_action(ActionCursorDown, repeat_count) == ErrCouldNotPerformAction {\n\t\t\terr = self.perform_action(ActionHistoryNext, repeat_count)\n\t\t}\n\t\treturn\n\tcase ActionHistoryFirst:\n\t\tif self.history_first() {\n\t\t\treturn\n\t\t}\n\tcase ActionHistoryPrevious:\n\t\tif self.history_prev(repeat_count) {\n\t\t\treturn\n\t\t}\n\tcase ActionHistoryNext:\n\t\tif self.history_next(repeat_count) {\n\t\t\treturn\n\t\t}\n\tcase ActionHistoryLast:\n\t\tif self.history_last() {\n\t\t\treturn\n\t\t}\n\tcase ActionClearScreen:\n\t\tself.loop.StartAtomicUpdate()\n\t\tself.loop.ClearScreen()\n\t\tself.RedrawNonAtomic()\n\t\tself.loop.EndAtomicUpdate()\n\t\treturn\n\tcase ActionKillToEndOfLine:\n\t\tif self.kill_to_end_of_line() {\n\t\t\treturn\n\t\t}\n\tcase ActionKillToStartOfLine:\n\t\tif self.kill_to_start_of_line() {\n\t\t\treturn\n\t\t}\n\tcase ActionKillNextWord:\n\t\tif self.kill_next_word(repeat_count, true) > 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionKillPreviousWord:\n\t\tif self.kill_previous_word(repeat_count, true) > 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionKillPreviousSpaceDelimitedWord:\n\t\tif self.kill_previous_space_delimited_word(repeat_count, true) > 0 {\n\t\t\treturn\n\t\t}\n\tcase ActionYank:\n\t\tif self.yank(repeat_count, false) {\n\t\t\treturn\n\t\t}\n\tcase ActionPopYank:\n\t\tif self.yank(repeat_count, true) {\n\t\t\treturn\n\t\t}\n\tcase ActionAbortCurrentLine:\n\t\tself.loop.QueueWriteString(\"\\r\\n\")\n\t\tself.ResetText()\n\t\treturn\n\tcase ActionHistoryIncrementalSearchForwards:\n\t\tif self.history_search == nil {\n\t\t\tself.create_history_search(false, repeat_count)\n\t\t\treturn\n\t\t}\n\t\tif self.next_history_search(false, repeat_count) {\n\t\t\treturn\n\t\t}\n\tcase ActionHistoryIncrementalSearchBackwards:\n\t\tif self.history_search == nil {\n\t\t\tself.create_history_search(true, repeat_count)\n\t\t\treturn\n\t\t}\n\t\tif self.next_history_search(true, repeat_count) {\n\t\t\treturn\n\t\t}\n\tcase ActionAddText:\n\t\ttext := strings.Repeat(self.text_to_be_added, int(repeat_count))\n\t\tself.text_to_be_added = \"\"\n\t\tif self.history_search != nil {\n\t\t\tself.add_text_to_history_search(text)\n\t\t} else {\n\t\t\tself.add_text(text)\n\t\t}\n\t\treturn\n\tcase ActionTerminateHistorySearchAndRestore:\n\t\tif self.history_search != nil {\n\t\t\tself.end_history_search(false)\n\t\t\treturn\n\t\t}\n\tcase ActionTerminateHistorySearchAndApply:\n\t\tif self.history_search != nil {\n\t\t\tself.end_history_search(true)\n\t\t\treturn\n\t\t}\n\tcase ActionCompleteForward:\n\t\tif self.complete(true, repeat_count) {\n\t\t\treturn\n\t\t}\n\tcase ActionCompleteBackward:\n\t\tif self.complete(false, repeat_count) {\n\t\t\treturn\n\t\t}\n\t}\n\terr = ErrCouldNotPerformAction\n\treturn\n}\n\nfunc (self *Readline) perform_action(ac Action, repeat_count uint) error {\n\terr, dont_set_last_action := self._perform_action(ac, repeat_count)\n\tif err == nil && !dont_set_last_action {\n\t\tself.last_action = ac\n\t\tif self.completions.current.results != nil && ac != ActionCompleteForward && ac != ActionCompleteBackward {\n\t\t\tself.completions.current = completion{}\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "tools/tui/readline/actions_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage readline\n\nimport (\n\t\"container/list\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc new_rl() *Readline {\n\tlp, _ := loop.New()\n\trl := New(lp, RlInit{Prompt: \"$$ \"})\n\trl.screen_width = 10\n\trl.screen_height = 100\n\treturn rl\n}\n\nfunc test_func(t *testing.T) func(string, func(*Readline), ...string) *Readline {\n\treturn func(initial string, prepare func(rl *Readline), expected ...string) *Readline {\n\t\trl := new_rl()\n\t\trl.add_text(initial)\n\t\tif prepare != nil {\n\t\t\tprepare(rl)\n\t\t}\n\t\tif len(expected) > 0 {\n\t\t\tif expected[0] != rl.text_upto_cursor_pos() {\n\t\t\t\tt.Fatalf(\"Text upto cursor pos not as expected for: %#v\\n%#v != %#v\", initial, expected[0], rl.text_upto_cursor_pos())\n\t\t\t}\n\t\t}\n\t\tif len(expected) > 1 {\n\t\t\tif expected[1] != rl.text_after_cursor_pos() {\n\t\t\t\tt.Fatalf(\"Text after cursor pos not as expected for: %#v\\n%#v != %#v\", initial, expected[1], rl.text_after_cursor_pos())\n\t\t\t}\n\t\t}\n\t\tif len(expected) > 2 {\n\t\t\tif expected[2] != rl.all_text() {\n\t\t\t\tt.Fatalf(\"Text not as expected for: %#v\\n%#v != %#v\", initial, expected[2], rl.all_text())\n\t\t\t}\n\t\t}\n\t\treturn rl\n\t}\n\n}\n\nfunc TestAddText(t *testing.T) {\n\tdt := test_func(t)\n\tdt(\"test\", nil, \"test\", \"\", \"test\")\n\tdt(\"1234\\n\", nil, \"1234\\n\", \"\", \"1234\\n\")\n\tdt(\"abcd\", func(rl *Readline) {\n\t\trl.input_state.cursor.X = 2\n\t\trl.add_text(\"12\")\n\t}, \"ab12\", \"cd\", \"ab12cd\")\n\tdt(\"abcd\", func(rl *Readline) {\n\t\trl.input_state.cursor.X = 2\n\t\trl.add_text(\"12\\n34\")\n\t}, \"ab12\\n34\", \"cd\", \"ab12\\n34cd\")\n\tdt(\"abcd\\nxyz\", func(rl *Readline) {\n\t\trl.input_state.cursor.X = 2\n\t\trl.add_text(\"12\\n34\")\n\t}, \"abcd\\nxy12\\n34\", \"z\", \"abcd\\nxy12\\n34z\")\n}\n\nfunc TestGetScreenLines(t *testing.T) {\n\trl := new_rl()\n\n\tp := func(primary bool) Prompt {\n\t\tif primary {\n\t\t\treturn rl.prompt\n\t\t}\n\t\treturn rl.continuation_prompt\n\t}\n\n\ttsl := func(expected ...ScreenLine) {\n\t\tq := rl.get_screen_lines()\n\t\tactual := make([]ScreenLine, len(q))\n\t\tfor i, x := range q {\n\t\t\tactual[i] = *x\n\t\t}\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Did not get expected screen lines for: %#v and cursor: %+v\\n%s\", rl.AllText(), rl.input_state.cursor, diff)\n\t\t}\n\t}\n\ttsl(ScreenLine{Prompt: p(true), CursorCell: 3, AfterLineBreak: true})\n\trl.add_text(\"123\")\n\ttsl(ScreenLine{Prompt: p(true), CursorCell: 6, Text: \"123\", CursorTextPos: 3, TextLengthInCells: 3, AfterLineBreak: true})\n\trl.add_text(\"456\")\n\ttsl(ScreenLine{Prompt: p(true), CursorCell: 9, Text: \"123456\", CursorTextPos: 6, TextLengthInCells: 6, AfterLineBreak: true})\n\trl.add_text(\"7\")\n\ttsl(\n\t\tScreenLine{Prompt: p(true), CursorCell: -1, Text: \"1234567\", CursorTextPos: -1, TextLengthInCells: 7, AfterLineBreak: true},\n\t\tScreenLine{OffsetInParentLine: 7},\n\t)\n\trl.add_text(\"89\")\n\ttsl(\n\t\tScreenLine{Prompt: p(true), CursorCell: -1, Text: \"1234567\", CursorTextPos: -1, TextLengthInCells: 7, AfterLineBreak: true},\n\t\tScreenLine{OffsetInParentLine: 7, Text: \"89\", CursorCell: 2, TextLengthInCells: 2, CursorTextPos: 2},\n\t)\n\trl.ResetText()\n\trl.add_text(\"123\\n456abcdeXYZ\")\n\ttsl(\n\t\tScreenLine{Prompt: p(true), CursorCell: -1, Text: \"123\", CursorTextPos: -1, TextLengthInCells: 3, AfterLineBreak: true},\n\t\tScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: \"456abcde\", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1, AfterLineBreak: true},\n\t\tScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: 3, CursorTextPos: 3, Text: \"XYZ\"},\n\t)\n\trl.input_state.cursor = Position{X: 2}\n\ttsl(\n\t\tScreenLine{Prompt: p(true), CursorCell: 5, Text: \"123\", CursorTextPos: 2, TextLengthInCells: 3, AfterLineBreak: true},\n\t\tScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: \"456abcde\", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1, AfterLineBreak: true},\n\t\tScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: -1, CursorTextPos: -1, Text: \"XYZ\"},\n\t)\n\trl.input_state.cursor = Position{X: 2, Y: 1}\n\ttsl(\n\t\tScreenLine{Prompt: p(true), CursorCell: -1, Text: \"123\", CursorTextPos: -1, TextLengthInCells: 3, AfterLineBreak: true},\n\t\tScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: \"456abcde\", TextLengthInCells: 8, CursorCell: 4, CursorTextPos: 2, AfterLineBreak: true},\n\t\tScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: -1, CursorTextPos: -1, Text: \"XYZ\"},\n\t)\n\trl.input_state.cursor = Position{X: 8, Y: 1}\n\ttsl(\n\t\tScreenLine{Prompt: p(true), CursorCell: -1, Text: \"123\", CursorTextPos: -1, TextLengthInCells: 3, AfterLineBreak: true},\n\t\tScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: \"456abcde\", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1, AfterLineBreak: true},\n\t\tScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: 0, CursorTextPos: 0, Text: \"XYZ\"},\n\t)\n\trl.ResetText()\n\trl.add_text(\"1234567\\nabc\")\n\trl.input_state.cursor = Position{X: 7}\n\ttsl(\n\t\tScreenLine{Prompt: p(true), CursorCell: -1, Text: \"1234567\", CursorTextPos: -1, TextLengthInCells: 7, AfterLineBreak: true},\n\t\tScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: \"abc\", CursorCell: 2, TextLengthInCells: 3, CursorTextPos: 0, AfterLineBreak: true},\n\t)\n}\n\nfunc TestCursorMovement(t *testing.T) {\n\tdt := test_func(t)\n\n\tleft := func(rl *Readline, amt uint, moved_amt uint, traverse_line_breaks bool) {\n\t\tactual := rl.move_cursor_left(amt, traverse_line_breaks)\n\t\tif actual != moved_amt {\n\t\t\tt.Fatalf(\"Failed to move cursor by %#v\\nactual != expected: %#v != %#v\", amt, actual, moved_amt)\n\t\t}\n\t}\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tleft(rl, 2, 2, false)\n\t}, \"one\\nt\", \"wo\")\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tleft(rl, 4, 3, false)\n\t}, \"one\\n\", \"two\")\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tleft(rl, 4, 4, true)\n\t}, \"one\", \"\\ntwo\")\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tleft(rl, 7, 7, true)\n\t}, \"\", \"one\\ntwo\")\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tleft(rl, 10, 7, true)\n\t}, \"\", \"one\\ntwo\")\n\tdt(\"one😀\", func(rl *Readline) {\n\t\tleft(rl, 1, 1, false)\n\t}, \"one\", \"😀\")\n\tdt(\"oneà\", func(rl *Readline) {\n\t\tleft(rl, 1, 1, false)\n\t}, \"one\", \"à\")\n\n\tright := func(rl *Readline, amt uint, moved_amt uint, traverse_line_breaks bool) {\n\t\trl.input_state.cursor.Y = 0\n\t\trl.input_state.cursor.X = 0\n\t\tactual := rl.move_cursor_right(amt, traverse_line_breaks)\n\t\tif actual != moved_amt {\n\t\t\tt.Fatalf(\"Failed to move cursor by %d\\nactual != expected: %d != %d\", amt, actual, moved_amt)\n\t\t}\n\t}\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tright(rl, 2, 2, false)\n\t}, \"on\", \"e\\ntwo\")\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tright(rl, 4, 3, false)\n\t}, \"one\", \"\\ntwo\")\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tright(rl, 4, 4, true)\n\t}, \"one\\n\", \"two\")\n\tdt(\"😀one\", func(rl *Readline) {\n\t\tright(rl, 1, 1, false)\n\t}, \"😀\", \"one\")\n\tdt(\"àb\", func(rl *Readline) {\n\t\tright(rl, 1, 1, false)\n\t}, \"à\", \"b\")\n\n\trl := new_rl()\n\n\tvert := func(amt int, moved_amt int, text_upto_cursor_pos string, initials ...Position) {\n\t\tinitial := Position{}\n\t\tif len(initials) > 0 {\n\t\t\tinitial = initials[0]\n\t\t}\n\t\trl.input_state.cursor = initial\n\t\tactual := rl.move_cursor_vertically(amt)\n\t\tif actual != moved_amt {\n\t\t\tt.Fatalf(\"Failed to move cursor by %#v for: %#v \\nactual != expected: %#v != %#v\", amt, rl.AllText(), actual, moved_amt)\n\t\t}\n\t\tif diff := cmp.Diff(text_upto_cursor_pos, rl.text_upto_cursor_pos()); diff != \"\" {\n\t\t\tt.Fatalf(\"Did not get expected screen lines for: %#v and cursor: %+v\\n%s\", rl.AllText(), initial, diff)\n\t\t}\n\t}\n\n\trl.ResetText()\n\trl.add_text(\"1234567xy\\nabcd\\n123\")\n\tvert(-1, -1, \"1234567xy\\nabc\", Position{X: 3, Y: 2})\n\tvert(-2, -2, \"1234567xy\", Position{X: 3, Y: 2})\n\tvert(-30, -3, \"123\", Position{X: 3, Y: 2})\n\n\trl.ResetText()\n\trl.add_text(\"o\\u0300ne  two three\\nfour five\")\n\n\twf := func(amt uint, expected_amt uint, text_before_cursor string) {\n\t\tpos := rl.input_state.cursor\n\t\tactual_amt := rl.move_to_end_of_word(amt, true, has_word_chars)\n\t\tif actual_amt != expected_amt {\n\t\t\tt.Fatalf(\"Failed to move to word end, expected amt (%d) != actual amt (%d)\", expected_amt, actual_amt)\n\t\t}\n\t\tif diff := cmp.Diff(text_before_cursor, rl.TextBeforeCursor()); diff != \"\" {\n\t\t\tt.Fatalf(\"Did not get expected text before cursor for: %#v and cursor: %+v\\n%s\", rl.AllText(), pos, diff)\n\t\t}\n\t}\n\trl.input_state.cursor = Position{}\n\twf(1, 1, \"òne\")\n\twf(1, 1, \"òne  two\")\n\twf(1, 1, \"òne  two three\")\n\twf(1, 1, \"òne  two three\\nfour\")\n\twf(1, 1, \"òne  two three\\nfour five\")\n\twf(1, 0, \"òne  two three\\nfour five\")\n\trl.input_state.cursor = Position{}\n\twf(5, 5, \"òne  two three\\nfour five\")\n\trl.input_state.cursor = Position{X: 5}\n\twf(1, 1, \"òne  two\")\n\n\twb := func(amt uint, expected_amt uint, text_before_cursor string) {\n\t\tpos := rl.input_state.cursor\n\t\tactual_amt := rl.move_to_start_of_word(amt, true, has_word_chars)\n\t\tif actual_amt != expected_amt {\n\t\t\tt.Fatalf(\"Failed to move to word end, expected amt (%d) != actual amt (%d)\", expected_amt, actual_amt)\n\t\t}\n\t\tif diff := cmp.Diff(text_before_cursor, rl.TextBeforeCursor()); diff != \"\" {\n\t\t\tt.Fatalf(\"Did not get expected text before cursor for: %#v and cursor: %+v\\n%s\", rl.AllText(), pos, diff)\n\t\t}\n\t}\n\trl.input_state.cursor = Position{X: 2}\n\twb(1, 1, \"\")\n\trl.input_state.cursor = Position{X: 8, Y: 1}\n\twb(1, 1, \"òne  two three\\nfour \")\n\twb(1, 1, \"òne  two three\\n\")\n\twb(1, 1, \"òne  two \")\n\twb(1, 1, \"òne  \")\n\twb(1, 1, \"\")\n\twb(1, 0, \"\")\n\trl.input_state.cursor = Position{X: 8, Y: 1}\n\twb(5, 5, \"\")\n\trl.input_state.cursor = Position{X: 5}\n\twb(1, 1, \"\")\n\n}\n\nfunc TestYanking(t *testing.T) {\n\trl := new_rl()\n\n\tas_slice := func(l *list.List) []string {\n\t\tans := make([]string, 0, l.Len())\n\t\tfor e := l.Front(); e != nil; e = e.Next() {\n\t\t\tans = append(ans, e.Value.(string))\n\t\t}\n\t\treturn ans\n\t}\n\n\tassert_items := func(expected ...string) {\n\t\tif diff := cmp.Diff(expected, as_slice(rl.kill_ring.items)); diff != \"\" {\n\t\t\tt.Fatalf(\"kill ring items not as expected\\n%s\", diff)\n\t\t}\n\t}\n\tassert_text := func(expected string) {\n\t\tif diff := cmp.Diff(expected, rl.all_text()); diff != \"\" {\n\t\t\tt.Fatalf(\"text not as expected:\\n%s\", diff)\n\t\t}\n\t}\n\n\trl.add_text(\"1 2 3\\none two three\")\n\trl.perform_action(ActionKillToStartOfLine, 1)\n\tassert_items(\"one two three\")\n\trl.perform_action(ActionCursorUp, 1)\n\trl.perform_action(ActionKillToEndOfLine, 1)\n\tassert_items(\"1 2 3\", \"one two three\")\n\trl.perform_action(ActionYank, 1)\n\tassert_text(\"1 2 3\\n\")\n\trl.perform_action(ActionYank, 1)\n\tassert_text(\"1 2 31 2 3\\n\")\n\trl.perform_action(ActionPopYank, 1)\n\tassert_text(\"1 2 3one two three\\n\")\n\trl.perform_action(ActionPopYank, 1)\n\tassert_text(\"1 2 31 2 3\\n\")\n\n\trl.ResetText()\n\trl.kill_ring.clear()\n\trl.add_text(\"one two three\")\n\trl.perform_action(ActionMoveToStartOfLine, 1)\n\trl.perform_action(ActionKillNextWord, 1)\n\tassert_items(\"one\")\n\tassert_text(\" two three\")\n\trl.perform_action(ActionKillNextWord, 1)\n\tassert_items(\"one two\")\n\tassert_text(\" three\")\n\trl.perform_action(ActionCursorRight, 1)\n\trl.perform_action(ActionKillNextWord, 1)\n\tassert_items(\"three\", \"one two\")\n\tassert_text(\" \")\n}\n\nfunc TestEraseChars(t *testing.T) {\n\tdt := test_func(t)\n\n\tbackspace := func(rl *Readline, amt uint, erased_amt uint, traverse_line_breaks bool) {\n\t\tactual := rl.erase_chars_before_cursor(amt, traverse_line_breaks)\n\t\tif actual != erased_amt {\n\t\t\tt.Fatalf(\"Failed to move cursor by %#v\\nactual != expected: %d != %d\", amt, actual, erased_amt)\n\t\t}\n\t}\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tbackspace(rl, 2, 2, false)\n\t}, \"one\\nt\", \"\")\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\trl.input_state.cursor.X = 1\n\t\tbackspace(rl, 2, 1, false)\n\t}, \"one\\n\", \"wo\")\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\trl.input_state.cursor.X = 1\n\t\tbackspace(rl, 2, 2, true)\n\t}, \"one\", \"wo\")\n\tdt(\"a😀\", func(rl *Readline) {\n\t\tbackspace(rl, 1, 1, false)\n\t}, \"a\", \"\")\n\tdt(\"bà\", func(rl *Readline) {\n\t\tbackspace(rl, 1, 1, false)\n\t}, \"b\", \"\")\n\n\tdel := func(rl *Readline, amt uint, erased_amt uint, traverse_line_breaks bool) {\n\t\trl.input_state.cursor.Y = 0\n\t\trl.input_state.cursor.X = 0\n\t\tactual := rl.erase_chars_after_cursor(amt, traverse_line_breaks)\n\t\tif actual != erased_amt {\n\t\t\tt.Fatalf(\"Failed to move cursor by %#v\\nactual != expected: %d != %d\", amt, actual, erased_amt)\n\t\t}\n\t}\n\tdt(\"one\\ntwo\", func(rl *Readline) {\n\t\tdel(rl, 2, 2, false)\n\t}, \"\", \"e\\ntwo\")\n\tdt(\"😀a\", func(rl *Readline) {\n\t\tdel(rl, 1, 1, false)\n\t}, \"\", \"a\")\n\tdt(\"àb\", func(rl *Readline) {\n\t\tdel(rl, 1, 1, false)\n\t}, \"\", \"b\")\n\n\tdt(\"one\\ntwo\\nthree\", func(rl *Readline) {\n\t\trl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})\n\t}, \"oree\", \"\")\n\tdt(\"one\\ntwo\\nthree\", func(rl *Readline) {\n\t\trl.input_state.cursor.X = 1\n\t\trl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})\n\t}, \"o\", \"ree\")\n\tdt(\"one\\ntwo\\nthree\", func(rl *Readline) {\n\t\trl.input_state.cursor = Position{X: 1, Y: 1}\n\t\trl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})\n\t}, \"o\", \"ree\")\n\tdt(\"one\\ntwo\\nthree\", func(rl *Readline) {\n\t\trl.input_state.cursor = Position{X: 1, Y: 0}\n\t\trl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})\n\t}, \"o\", \"ree\")\n\tdt(\"one\\ntwo\\nthree\", func(rl *Readline) {\n\t\trl.input_state.cursor = Position{X: 0, Y: 0}\n\t\trl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})\n\t}, \"\", \"oree\")\n}\n\nfunc TestNumberArgument(t *testing.T) {\n\trl := new_rl()\n\trl.screen_width = 100\n\n\ttest := func(ac Action, before_cursor, after_cursor string) {\n\t\trl.dispatch_key_action(ac)\n\t\tif diff := cmp.Diff(before_cursor, rl.text_upto_cursor_pos()); diff != \"\" {\n\t\t\tt.Fatalf(\"The text before the cursor was not as expected for action: %#v\\n%s\", ac, diff)\n\t\t}\n\t\tif diff := cmp.Diff(after_cursor, rl.text_after_cursor_pos()); diff != \"\" {\n\t\t\tt.Fatalf(\"The text after the cursor was not as expected for action: %#v\\n%s\", ac, diff)\n\t\t}\n\t}\n\tsw := func(num int) {\n\t\tq := rl.format_arg_prompt(strconv.Itoa(num))\n\t\tfor _, sl := range rl.get_screen_lines() {\n\t\t\tif num <= 0 && !strings.Contains(sl.Prompt.Text, \"$$\") {\n\t\t\t\tt.Fatalf(\"arg prompt unexpectedly present for: %#v\", rl.AllText())\n\t\t\t}\n\t\t\tif num > 0 && !strings.Contains(sl.Prompt.Text, q) {\n\t\t\t\tt.Fatalf(\"arg prompt unexpectedly not present for: %#v prompt: %#v\", rl.AllText(), sl.Prompt.Text)\n\t\t\t}\n\t\t}\n\t}\n\n\tsw(0)\n\trl.dispatch_key_action(ActionNumericArgumentDigit1)\n\tsw(1)\n\trl.dispatch_key_action(ActionNumericArgumentDigit0)\n\tsw(10)\n\trl.text_to_be_added = \"x\"\n\ttest(ActionAddText, \"xxxxxxxxxx\", \"\")\n\tsw(0)\n\ttest(ActionNumericArgumentDigit0, \"xxxxxxxxxx0\", \"\")\n\tsw(0)\n\trl.dispatch_key_action(ActionNumericArgumentDigit1)\n\ttest(ActionNumericArgumentDigitMinus, \"xxxxxxxxxx0-\", \"\")\n\tsw(0)\n\trl.dispatch_key_action(ActionNumericArgumentDigit1)\n\tsw(1)\n\trl.dispatch_key_action(ActionNumericArgumentDigit1)\n\tsw(11)\n\ttest(ActionCursorLeft, \"x\", \"xxxxxxxxx0-\")\n\tsw(0)\n}\n\nfunc TestHistory(t *testing.T) {\n\trl := new_rl()\n\n\tadd_item := func(x string) {\n\t\trl.history.AddItem(x, 0)\n\t}\n\tadd_item(\"a one\")\n\tadd_item(\"a two\")\n\tadd_item(\"b three\")\n\tadd_item(\"b four\")\n\n\ttest := func(ac Action, before_cursor, after_cursor string) {\n\t\trl.perform_action(ac, 1)\n\t\tif diff := cmp.Diff(before_cursor, rl.text_upto_cursor_pos()); diff != \"\" {\n\t\t\tt.Fatalf(\"The text before the cursor was not as expected for action: %#v\\n%s\", ac, diff)\n\t\t}\n\t\tif diff := cmp.Diff(after_cursor, rl.text_after_cursor_pos()); diff != \"\" {\n\t\t\tt.Fatalf(\"The text after the cursor was not as expected for action: %#v\\n%s\", ac, diff)\n\t\t}\n\t}\n\n\ttest(ActionHistoryPreviousOrCursorUp, \"b four\", \"\")\n\ttest(ActionHistoryPreviousOrCursorUp, \"b three\", \"\")\n\ttest(ActionHistoryPrevious, \"a two\", \"\")\n\ttest(ActionHistoryPrevious, \"a one\", \"\")\n\ttest(ActionHistoryPrevious, \"a one\", \"\")\n\ttest(ActionHistoryNext, \"a two\", \"\")\n\ttest(ActionHistoryNext, \"b three\", \"\")\n\ttest(ActionHistoryNext, \"b four\", \"\")\n\ttest(ActionHistoryNext, \"\", \"\")\n\ttest(ActionHistoryNext, \"\", \"\")\n\n\ttest(ActionHistoryPrevious, \"b four\", \"\")\n\ttest(ActionHistoryPrevious, \"b three\", \"\")\n\ttest(ActionHistoryNext, \"b four\", \"\")\n\n\trl.ResetText()\n\trl.add_text(\"a\")\n\ttest(ActionHistoryPrevious, \"a two\", \"\")\n\ttest(ActionHistoryPrevious, \"a one\", \"\")\n\ttest(ActionHistoryPrevious, \"a one\", \"\")\n\ttest(ActionHistoryNext, \"a two\", \"\")\n\ttest(ActionHistoryNext, \"a\", \"\")\n\ttest(ActionHistoryNext, \"a\", \"\")\n\n\tah := func(before_cursor, after_cursor string) {\n\t\tab := rl.text_upto_cursor_pos()\n\t\taa := rl.text_after_cursor_pos()\n\t\tif diff := cmp.Diff(before_cursor, ab); diff != \"\" {\n\t\t\tt.Fatalf(\"Text before cursor not as expected:\\n%s\", diff)\n\t\t}\n\t\tif diff := cmp.Diff(after_cursor, aa); diff != \"\" {\n\t\t\tt.Fatalf(\"Text after cursor not as expected:\\n%s\", diff)\n\t\t}\n\t}\n\tadd_item(\"xyz1\")\n\tadd_item(\"xyz2\")\n\tadd_item(\"xyz11\")\n\trl.perform_action(ActionHistoryIncrementalSearchBackwards, 1)\n\tah(\"\", \"\")\n\n\trl.text_to_be_added = \"z\"\n\trl.perform_action(ActionAddText, 1)\n\tah(\"xy\", \"z11\")\n\trl.text_to_be_added = \"2\"\n\trl.perform_action(ActionAddText, 1)\n\tah(\"xy\", \"z2\")\n\trl.text_to_be_added = \"m\"\n\trl.perform_action(ActionAddText, 1)\n\tah(\"No matches for: z2m\", \"\")\n\trl.perform_action(ActionBackspace, 1)\n\tah(\"xy\", \"z2\")\n\trl.perform_action(ActionBackspace, 1)\n\tah(\"xy\", \"z2\")\n\trl.perform_action(ActionHistoryIncrementalSearchBackwards, 1)\n\tah(\"xy\", \"z1\")\n\trl.perform_action(ActionHistoryIncrementalSearchBackwards, 1)\n\tah(\"xy\", \"z1\")\n\trl.perform_action(ActionHistoryIncrementalSearchForwards, 1)\n\tah(\"xy\", \"z2\")\n\trl.perform_action(ActionTerminateHistorySearchAndRestore, 1)\n\tah(\"a\", \"\")\n}\n\nfunc TestReadlineCompletion(t *testing.T) {\n\tcompleter := func(before_cursor, after_cursor string) (ans *cli.Completions) {\n\t\troot := cli.NewRootCommand()\n\t\tc := root.AddSubCommand(&cli.Command{Name: \"test-completion\"})\n\t\tc.AddSubCommand(&cli.Command{Name: \"a1\"})\n\t\tc.AddSubCommand(&cli.Command{Name: \"a11\"})\n\t\tc.AddSubCommand(&cli.Command{Name: \"a2\"})\n\t\tprefix := c.Name + \" \"\n\t\ttext := prefix + before_cursor\n\t\targv, position_of_last_arg := shlex.SplitForCompletion(text)\n\t\tif len(argv) == 0 || position_of_last_arg < len(prefix) {\n\t\t\treturn\n\t\t}\n\t\tans = root.GetCompletions(argv, nil)\n\t\tans.CurrentWordIdx = position_of_last_arg - len(prefix)\n\t\treturn\n\n\t}\n\trl := new_rl()\n\trl.completions.completer = completer\n\n\tah := func(before_cursor, after_cursor string) {\n\t\tab := rl.text_upto_cursor_pos()\n\t\taa := rl.text_after_cursor_pos()\n\t\tif diff := cmp.Diff(before_cursor, ab); diff != \"\" {\n\t\t\tt.Fatalf(\"Text before cursor not as expected:\\n%s\", diff)\n\t\t}\n\t\tif diff := cmp.Diff(after_cursor, aa); diff != \"\" {\n\t\t\tt.Fatalf(\"Text after cursor not as expected:\\n%s\", diff)\n\t\t}\n\t\tactual, _ := rl.completion_screen_lines()\n\t\texpected := []string{\"a1 a11 a2 \"}\n\t\tif diff := cmp.Diff(expected, actual[1:]); diff != \"\" {\n\t\t\tt.Fatalf(\"Completion screen lines not as expected:\\n%s\", diff)\n\t\t}\n\t}\n\trl.add_text(\"a\")\n\trl.perform_action(ActionCompleteForward, 1)\n\tah(\"a\", \"\")\n\trl.perform_action(ActionCompleteForward, 1)\n\tah(\"a1 \", \"\")\n\trl.perform_action(ActionCompleteForward, 1)\n\tah(\"a11 \", \"\")\n\trl.perform_action(ActionCompleteForward, 1)\n\tah(\"a2 \", \"\")\n\trl.perform_action(ActionCompleteBackward, 1)\n\tah(\"a11 \", \"\")\n}\n"
  },
  {
    "path": "tools/tui/readline/api.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage readline\n\nimport (\n\t\"container/list\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/cli/markup\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\nconst ST = \"\\x1b\\\\\"\nconst PROMPT_MARK = \"\\x1b]133;\"\n\ntype SyntaxHighlightFunction = func(text string, x, y int) string\ntype CompleterFunction = func(before_cursor, after_cursor string) *cli.Completions\n\ntype RlInit struct {\n\tPrompt                  string\n\tHistoryPath             string\n\tHistoryCount            int\n\tContinuationPrompt      string\n\tEmptyContinuationPrompt bool\n\tDontMarkPrompts         bool\n\tSyntaxHighlighter       SyntaxHighlightFunction\n\tCompleter               CompleterFunction\n}\n\ntype Position struct {\n\tX int\n\tY int\n}\n\nfunc (self Position) Less(other Position) bool {\n\treturn self.Y < other.Y || (self.Y == other.Y && self.X < other.X)\n}\n\ntype kill_ring struct {\n\titems *list.List\n}\n\nfunc (self *kill_ring) append_to_existing_item(text string) {\n\te := self.items.Front()\n\tif e == nil {\n\t\tself.add_new_item(text)\n\t}\n\te.Value = e.Value.(string) + text\n}\n\nfunc (self *kill_ring) add_new_item(text string) {\n\tif text != \"\" {\n\t\tself.items.PushFront(text)\n\t}\n}\n\nfunc (self *kill_ring) yank() string {\n\te := self.items.Front()\n\tif e == nil {\n\t\treturn \"\"\n\t}\n\treturn e.Value.(string)\n}\n\nfunc (self *kill_ring) pop_yank() string {\n\te := self.items.Front()\n\tif e == nil {\n\t\treturn \"\"\n\t}\n\tself.items.MoveToBack(e)\n\treturn self.yank()\n}\n\nfunc (self *kill_ring) clear() {\n\tself.items = self.items.Init()\n}\n\ntype Prompt struct {\n\tText   string\n\tLength int\n}\n\ntype InputState struct {\n\t// Input lines\n\tlines []string\n\t// The cursor position in the text\n\tcursor Position\n}\n\nfunc (self InputState) copy() InputState {\n\tans := self\n\tl := make([]string, len(self.lines))\n\tcopy(l, self.lines)\n\tans.lines = l\n\treturn ans\n}\n\ntype syntax_highlighted struct {\n\tlines                  []string\n\tsrc_for_last_highlight string\n\thighlighter            SyntaxHighlightFunction\n\tlast_highlighter_name  string\n}\n\ntype Readline struct {\n\tprompt, continuation_prompt Prompt\n\n\tmark_prompts bool\n\tloop         *loop.Loop\n\thistory      *History\n\tkill_ring    kill_ring\n\n\tinput_state InputState\n\t// The number of lines after the initial line on the screen\n\tcursor_y                    int\n\tscreen_width, screen_height int\n\tlast_yank_extent            struct {\n\t\tstart, end Position\n\t}\n\tbracketed_paste_buffer strings.Builder\n\tlast_action            Action\n\thistory_matches        *HistoryMatches\n\thistory_search         *HistorySearch\n\tkeyboard_state         KeyboardState\n\tfmt_ctx                *markup.Context\n\ttext_to_be_added       string\n\tsyntax_highlighted     syntax_highlighted\n\tcompletions            completions\n}\n\nfunc (self *Readline) make_prompt(text string, is_secondary bool) Prompt {\n\tif self.mark_prompts {\n\t\tm := PROMPT_MARK + \"A\"\n\t\tif is_secondary {\n\t\t\tm += \";k=s\"\n\t\t}\n\t\ttext = m + ST + text\n\t}\n\treturn Prompt{Text: text, Length: wcswidth.Stringwidth(text)}\n}\n\nfunc New(loop *loop.Loop, r RlInit) *Readline {\n\thc := r.HistoryCount\n\tif hc == 0 {\n\t\thc = 8192\n\t}\n\tans := &Readline{\n\t\tmark_prompts: !r.DontMarkPrompts, fmt_ctx: markup.New(true),\n\t\tloop: loop, input_state: InputState{lines: []string{\"\"}}, history: NewHistory(r.HistoryPath, hc),\n\t\tsyntax_highlighted: syntax_highlighted{highlighter: r.SyntaxHighlighter},\n\t\tcompletions:        completions{completer: r.Completer},\n\t\tkill_ring:          kill_ring{items: list.New().Init()},\n\t}\n\tif ans.completions.completer == nil && r.HistoryPath != \"\" {\n\t\tans.completions.completer = ans.HistoryCompleter\n\t}\n\tans.prompt = ans.make_prompt(r.Prompt, false)\n\tt := \"\"\n\tif r.ContinuationPrompt != \"\" || !r.EmptyContinuationPrompt {\n\t\tt = r.ContinuationPrompt\n\t\tif t == \"\" {\n\t\t\tt = ans.fmt_ctx.Yellow(\">\") + \" \"\n\t\t}\n\t}\n\tans.continuation_prompt = ans.make_prompt(t, true)\n\treturn ans\n}\n\nfunc (self *Readline) HistoryCompleter(before_cursor, after_cursor string) *cli.Completions {\n\treturn self.history_completer(before_cursor, after_cursor)\n}\n\nfunc (self *Readline) SetPrompt(prompt string) {\n\tself.prompt = self.make_prompt(prompt, false)\n}\n\nfunc (self *Readline) Shutdown() {\n\tself.history.Shutdown()\n}\n\nfunc (self *Readline) AddHistoryItem(hi HistoryItem) {\n\tself.history.merge_items(hi)\n}\n\nfunc (self *Readline) ResetText() {\n\tself.input_state = InputState{lines: []string{\"\"}}\n\tself.last_action = ActionNil\n\tself.keyboard_state = KeyboardState{}\n\tself.history_search = nil\n\tself.completions.current = completion{}\n\tself.cursor_y = 0\n}\n\nfunc (self *Readline) ChangeLoopAndResetText(lp *loop.Loop) {\n\tself.loop = lp\n\tself.ResetText()\n}\n\nfunc (self *Readline) Start() {\n\tself.loop.SetCursorShape(loop.BAR_CURSOR, true)\n\tself.loop.StartBracketedPaste()\n\tself.Redraw()\n}\n\nfunc (self *Readline) End() {\n\tself.loop.SetCursorShape(loop.BLOCK_CURSOR, true)\n\tself.loop.EndBracketedPaste()\n\tself.loop.QueueWriteString(\"\\r\\n\")\n\tif self.mark_prompts {\n\t\tself.loop.QueueWriteString(PROMPT_MARK + \"C\" + ST)\n\t}\n}\n\nfunc MarkOutputStart() string {\n\treturn PROMPT_MARK + \"C\" + ST\n}\n\nfunc (self *Readline) Redraw() {\n\tself.loop.StartAtomicUpdate()\n\tself.RedrawNonAtomic()\n\tself.loop.EndAtomicUpdate()\n}\n\nfunc (self *Readline) RedrawNonAtomic() {\n\tself.redraw()\n}\n\nfunc (self *Readline) OnKeyEvent(event *loop.KeyEvent) error {\n\terr := self.handle_key_event(event)\n\tif err == ErrCouldNotPerformAction {\n\t\terr = nil\n\t\tself.loop.Beep()\n\t}\n\treturn err\n}\n\nfunc (self *Readline) OnText(text string, from_key_event bool, in_bracketed_paste bool) error {\n\tif in_bracketed_paste {\n\t\tself.bracketed_paste_buffer.WriteString(text)\n\t\treturn nil\n\t}\n\tif self.bracketed_paste_buffer.Len() > 0 {\n\t\tself.bracketed_paste_buffer.WriteString(text)\n\t\ttext = self.bracketed_paste_buffer.String()\n\t\tself.bracketed_paste_buffer.Reset()\n\t}\n\tself.text_to_be_added = text\n\treturn self.dispatch_key_action(ActionAddText)\n}\n\nfunc (self *Readline) TextBeforeCursor() string {\n\treturn self.text_upto_cursor_pos()\n}\n\nfunc (self *Readline) TextAfterCursor() string {\n\treturn self.text_after_cursor_pos()\n}\n\nfunc (self *Readline) AllText() string {\n\treturn self.all_text()\n}\n\nfunc (self *Readline) SetText(text string) {\n\tself.set_text(text)\n}\n\nfunc (self *Readline) MoveCursorToEnd() bool {\n\treturn self.move_to_end()\n}\n\nfunc (self *Readline) CursorAtEndOfLine() bool {\n\treturn self.input_state.cursor.X >= len(self.input_state.lines[self.input_state.cursor.Y])\n}\n\nfunc (self *Readline) ClearCachedScreenSize() {\n\tself.screen_width, self.screen_height = 0, 0\n}\n\nfunc (self *Readline) OnResize(old_size loop.ScreenSize, new_size loop.ScreenSize) error {\n\tself.ClearCachedScreenSize()\n\tself.Redraw()\n\treturn nil\n}\n"
  },
  {
    "path": "tools/tui/readline/completion.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage readline\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype completion struct {\n\tbefore_cursor, after_cursor   string\n\tresults                       *cli.Completions\n\tresults_displayed, forwards   bool\n\tnum_of_matches, current_match int\n\trendered_at_screen_width      int\n\trendered_lines                []string\n\tlast_rendered_above           bool\n}\n\nfunc (self *completion) initialize() {\n\tself.num_of_matches = 0\n\tif self.results != nil {\n\t\tfor _, g := range self.results.Groups {\n\t\t\tself.num_of_matches += len(g.Matches)\n\t\t}\n\t}\n\tself.current_match = -1\n\tif !self.forwards {\n\t\tself.current_match = self.num_of_matches\n\t}\n\tif self.num_of_matches == 1 {\n\t\tself.current_match = 0\n\t}\n}\n\nfunc (self *completion) current_match_text() string {\n\tif self.results != nil {\n\t\ti := 0\n\t\tfor _, g := range self.results.Groups {\n\t\t\tfor _, m := range g.Matches {\n\t\t\t\tif i == self.current_match {\n\t\t\t\t\tt := m.Word\n\t\t\t\t\tif !g.NoTrailingSpace && t != \"\" {\n\t\t\t\t\t\tt += \" \"\n\t\t\t\t\t}\n\t\t\t\t\treturn t\n\t\t\t\t}\n\t\t\t\ti++\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\ntype completions struct {\n\tcompleter CompleterFunction\n\tcurrent   completion\n}\n\nfunc (self *Readline) complete(forwards bool, repeat_count uint) bool {\n\tc := &self.completions\n\tif c.completer == nil {\n\t\treturn false\n\t}\n\tif self.last_action == ActionCompleteForward || self.last_action == ActionCompleteBackward {\n\t\tif c.current.num_of_matches == 0 {\n\t\t\treturn false\n\t\t}\n\t\tdelta := -1\n\t\tif forwards {\n\t\t\tdelta = 1\n\t\t}\n\t\trepeat_count %= uint(c.current.num_of_matches)\n\t\tdelta *= int(repeat_count)\n\t\tc.current.current_match = (c.current.current_match + delta + c.current.num_of_matches) % c.current.num_of_matches\n\t\trepeat_count = 0\n\t} else {\n\t\tbefore, after := self.text_upto_cursor_pos(), self.text_after_cursor_pos()\n\t\tc.current = completion{before_cursor: before, after_cursor: after, forwards: forwards, results: c.completer(before, after)}\n\t\tc.current.initialize()\n\t\tif repeat_count > 0 {\n\t\t\trepeat_count--\n\t\t}\n\t\tif c.current.current_match != 0 {\n\t\t\tif self.loop != nil {\n\t\t\t\tself.loop.Beep()\n\t\t\t}\n\t\t}\n\t}\n\tc.current.forwards = forwards\n\tif c.current.results == nil {\n\t\treturn false\n\t}\n\tct := c.current.current_match_text()\n\tif ct != \"\" {\n\t\tall_text_before_completion := self.AllText()\n\t\tbefore := c.current.before_cursor[:c.current.results.CurrentWordIdx] + ct\n\t\tafter := c.current.after_cursor\n\t\tself.input_state.lines = utils.Splitlines(before)\n\t\tif len(self.input_state.lines) == 0 {\n\t\t\tself.input_state.lines = []string{\"\"}\n\t\t}\n\t\tself.input_state.cursor.Y = len(self.input_state.lines) - 1\n\t\tself.input_state.cursor.X = len(self.input_state.lines[self.input_state.cursor.Y])\n\t\tal := utils.Splitlines(after)\n\t\tif len(al) > 0 {\n\t\t\tself.input_state.lines[self.input_state.cursor.Y] += al[0]\n\t\t\tself.input_state.lines = append(self.input_state.lines, al[1:]...)\n\t\t}\n\t\tif c.current.num_of_matches == 1 && self.AllText() == all_text_before_completion && repeat_count > 0 {\n\t\t\t// when there is only a single match and it has already been inserted there is no point iterating over current completions\n\t\t\torig := self.last_action\n\t\t\tself.last_action = ActionNil\n\t\t\tself.complete(true, 1)\n\t\t\tself.last_action = orig\n\t\t}\n\t}\n\tif repeat_count > 0 {\n\t\tself.complete(forwards, repeat_count)\n\t}\n\treturn true\n}\n\nfunc (self *Readline) screen_lines_for_match_group_with_descriptions(g *cli.MatchGroup, lines []string) []string {\n\tmaxw := 0\n\tfor _, m := range g.Matches {\n\t\tl := wcswidth.Stringwidth(m.Word)\n\t\tif l > 16 {\n\t\t\tmaxw = 16\n\t\t\tbreak\n\t\t}\n\t\tif l > maxw {\n\t\t\tmaxw = l\n\t\t}\n\t}\n\tfor _, m := range g.Matches {\n\t\tlines = append(lines, utils.Splitlines(m.FormatForCompletionList(maxw, self.fmt_ctx, self.screen_width))...)\n\t}\n\treturn lines\n}\n\ntype cell struct {\n\ttext   string\n\tlength int\n}\n\nfunc (self cell) whitespace(desired_length int) string {\n\treturn strings.Repeat(\" \", max(0, desired_length-self.length))\n}\n\ntype column struct {\n\tcells   []cell\n\tlength  int\n\tis_last bool\n}\n\nfunc (self *column) update_length() int {\n\tself.length = 0\n\tfor _, c := range self.cells {\n\t\tif c.length > self.length {\n\t\t\tself.length = c.length\n\t\t}\n\t}\n\tif !self.is_last {\n\t\tself.length++\n\t}\n\treturn self.length\n}\n\nfunc layout_words_in_table(words []string, lengths map[string]int, num_cols int) ([]column, int) {\n\tcols := make([]column, num_cols)\n\tfor i, col := range cols {\n\t\tcol.cells = make([]cell, 0, len(words))\n\t\tif i == len(cols)-1 {\n\t\t\tcol.is_last = true\n\t\t}\n\t}\n\tc := 0\n\tfor _, word := range words {\n\t\tcols[c].cells = append(cols[c].cells, cell{word, lengths[word]})\n\t\tc++\n\t\tif c >= num_cols {\n\t\t\tc = 0\n\t\t}\n\t}\n\ttotal_length := 0\n\tfor i := range cols {\n\t\tif d := len(cols[0].cells) - len(cols[i].cells); d > 0 {\n\t\t\tcols[i].cells = append(cols[i].cells, make([]cell, d)...)\n\t\t}\n\t\ttotal_length += cols[i].update_length()\n\t}\n\treturn cols, total_length\n}\n\nfunc (self *Readline) screen_lines_for_match_group_without_descriptions(g *cli.MatchGroup, lines []string) []string {\n\twords := make([]string, len(g.Matches))\n\tlengths := make(map[string]int, len(words))\n\tmax_length := 0\n\tfor i, m := range g.Matches {\n\t\twords[i] = m.Word\n\t\tl := wcswidth.Stringwidth(words[i])\n\t\tlengths[words[i]] = l\n\t\tif l > max_length {\n\t\t\tmax_length = l\n\t\t}\n\t}\n\tvar ans []column\n\tncols := max(1, self.screen_width/(max_length+1))\n\tfor {\n\t\tcols, total_length := layout_words_in_table(words, lengths, ncols)\n\t\tif total_length > self.screen_width {\n\t\t\tbreak\n\t\t}\n\t\tans = cols\n\t\tncols++\n\t}\n\tif ans == nil {\n\t\tfor _, w := range words {\n\t\t\tif lengths[w] > self.screen_width {\n\t\t\t\tlines = append(lines, wcswidth.TruncateToVisualLength(w, self.screen_width))\n\t\t\t} else {\n\t\t\t\tlines = append(lines, w)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor r := 0; r < len(ans[0].cells); r++ {\n\t\t\tw := strings.Builder{}\n\t\t\tw.Grow(self.screen_width)\n\t\t\tfor c := 0; c < len(ans); c++ {\n\t\t\t\tcell := ans[c].cells[r]\n\t\t\t\tw.WriteString(cell.text)\n\t\t\t\tif !ans[c].is_last {\n\t\t\t\t\tw.WriteString(cell.whitespace(ans[c].length))\n\t\t\t\t}\n\t\t\t}\n\t\t\tlines = append(lines, w.String())\n\t\t}\n\t}\n\treturn lines\n}\n\nfunc (self *Readline) completion_screen_lines() ([]string, bool) {\n\tif self.completions.current.results == nil || self.completions.current.num_of_matches < 2 {\n\t\treturn []string{}, false\n\t}\n\tif len(self.completions.current.rendered_lines) > 0 && self.completions.current.rendered_at_screen_width == self.screen_width {\n\t\treturn self.completions.current.rendered_lines, true\n\t}\n\tlines := make([]string, 0, self.completions.current.num_of_matches)\n\tfor _, g := range self.completions.current.results.Groups {\n\t\tif len(g.Matches) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif g.Title != \"\" {\n\t\t\tlines = append(lines, self.fmt_ctx.Title(g.Title))\n\t\t}\n\t\thas_descriptions := false\n\t\tfor _, m := range g.Matches {\n\t\t\tif m.Description != \"\" {\n\t\t\t\thas_descriptions = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif has_descriptions {\n\t\t\tlines = self.screen_lines_for_match_group_with_descriptions(g, lines)\n\t\t} else {\n\t\t\tlines = self.screen_lines_for_match_group_without_descriptions(g, lines)\n\t\t}\n\t}\n\tself.completions.current.rendered_lines = lines\n\tself.completions.current.rendered_at_screen_width = self.screen_width\n\treturn lines, false\n}\n"
  },
  {
    "path": "tools/tui/readline/draw.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage readline\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\nfunc (self *Readline) update_current_screen_size() {\n\tvar screen_size loop.ScreenSize\n\tvar err error\n\tif self.loop != nil {\n\t\tscreen_size, err = self.loop.ScreenSize()\n\t\tif err != nil {\n\t\t\tscreen_size.WidthCells = 80\n\t\t\tscreen_size.HeightCells = 24\n\t\t}\n\t} else {\n\t\tscreen_size.WidthCells = 80\n\t\tscreen_size.HeightCells = 24\n\t}\n\tself.screen_width = max(1, int(screen_size.WidthCells))\n\tself.screen_height = max(1, int(screen_size.HeightCells))\n}\n\ntype ScreenLine struct {\n\tParentLineNumber, OffsetInParentLine         int\n\tPrompt                                       Prompt\n\tTextLengthInCells, CursorCell, CursorTextPos int\n\tText                                         string\n\tAfterLineBreak                               bool\n}\n\nfunc (self *Readline) format_arg_prompt(cna string) string {\n\treturn fmt.Sprintf(\"(arg: %s) \", self.fmt_ctx.Yellow(cna))\n}\n\nfunc (self *Readline) prompt_for_line_number(i int) Prompt {\n\tis_line_with_cursor := i == self.input_state.cursor.Y\n\tif is_line_with_cursor && self.keyboard_state.current_numeric_argument != \"\" {\n\t\treturn self.make_prompt(self.format_arg_prompt(self.keyboard_state.current_numeric_argument), i > 0)\n\t}\n\tif i == 0 {\n\t\tif self.history_search != nil {\n\t\t\treturn self.make_prompt(self.history_search_prompt(), i > 0)\n\t\t}\n\t\treturn self.prompt\n\t}\n\treturn self.continuation_prompt\n}\n\nfunc (self *Readline) apply_syntax_highlighting() (lines []string, cursor Position) {\n\thighlighter := self.syntax_highlighted.highlighter\n\thighlighter_name := \"default\"\n\tif self.history_search != nil {\n\t\thighlighter = self.history_search_highlighter\n\t\thighlighter_name = \"## history ##\"\n\t}\n\tif highlighter == nil {\n\t\treturn self.input_state.lines, self.input_state.cursor\n\t}\n\tsrc := strings.Join(self.input_state.lines, \"\\n\")\n\tif len(self.syntax_highlighted.lines) > 0 && self.syntax_highlighted.last_highlighter_name == highlighter_name && self.syntax_highlighted.src_for_last_highlight == src {\n\t\tlines = self.syntax_highlighted.lines\n\t} else {\n\t\tif src == \"\" {\n\t\t\tlines = []string{\"\"}\n\t\t} else {\n\t\t\ttext := highlighter(src, self.input_state.cursor.X, self.input_state.cursor.Y)\n\t\t\tlines = utils.Splitlines(text)\n\t\t\tfor len(lines) < len(self.input_state.lines) {\n\t\t\t\tlines = append(lines, \"syntax highlighter malfunctioned\")\n\t\t\t}\n\t\t}\n\t}\n\tline := lines[self.input_state.cursor.Y]\n\tw := wcswidth.Stringwidth(self.input_state.lines[self.input_state.cursor.Y][:self.input_state.cursor.X])\n\tx := len(wcswidth.TruncateToVisualLength(line, w))\n\treturn lines, Position{X: x, Y: self.input_state.cursor.Y}\n}\n\nfunc (self *Readline) get_screen_lines() []*ScreenLine {\n\tif self.screen_width == 0 || self.screen_height == 0 {\n\t\tself.update_current_screen_size()\n\t}\n\tlines, cursor := self.apply_syntax_highlighting()\n\tans := make([]*ScreenLine, 0, len(lines))\n\tfound_cursor := false\n\tcursor_at_start_of_next_line := false\n\tfor i, line := range lines {\n\t\tprompt := self.prompt_for_line_number(i)\n\t\toffset := 0\n\t\thas_cursor := i == cursor.Y\n\t\tfor is_first := true; is_first || offset < len(line); is_first = false {\n\t\t\tl, width := wcswidth.TruncateToVisualLengthWithWidth(line[offset:], self.screen_width-prompt.Length)\n\t\t\tsl := ScreenLine{\n\t\t\t\tParentLineNumber: i, OffsetInParentLine: offset,\n\t\t\t\tPrompt: prompt, TextLengthInCells: width,\n\t\t\t\tCursorCell: -1, Text: l, CursorTextPos: -1, AfterLineBreak: is_first,\n\t\t\t}\n\t\t\tif cursor_at_start_of_next_line {\n\t\t\t\tcursor_at_start_of_next_line = false\n\t\t\t\tsl.CursorCell = prompt.Length\n\t\t\t\tsl.CursorTextPos = 0\n\t\t\t\tfound_cursor = true\n\t\t\t}\n\t\t\tans = append(ans, &sl)\n\t\t\tif has_cursor && !found_cursor && offset <= cursor.X && cursor.X <= offset+len(l) {\n\t\t\t\tfound_cursor = true\n\t\t\t\tctpos := cursor.X - offset\n\t\t\t\tccell := prompt.Length + wcswidth.Stringwidth(l[:ctpos])\n\t\t\t\tif ccell >= self.screen_width {\n\t\t\t\t\tif offset+len(l) < len(line) || i < len(lines)-1 {\n\t\t\t\t\t\tcursor_at_start_of_next_line = true\n\t\t\t\t\t} else {\n\t\t\t\t\t\tans = append(ans, &ScreenLine{ParentLineNumber: i, OffsetInParentLine: len(line)})\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsl.CursorTextPos = ctpos\n\t\t\t\t\tsl.CursorCell = ccell\n\t\t\t\t}\n\t\t\t}\n\t\t\tprompt = Prompt{}\n\t\t\toffset += len(l)\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc (self *Readline) redraw() {\n\tif self.screen_width == 0 || self.screen_height == 0 {\n\t\tself.update_current_screen_size()\n\t}\n\tif self.screen_width < 4 {\n\t\treturn\n\t}\n\tif self.cursor_y > 0 {\n\t\tself.loop.MoveCursorVertically(-self.cursor_y)\n\t}\n\tself.loop.QueueWriteString(\"\\r\")\n\tself.loop.ClearToEndOfScreen()\n\tprompt_lines := self.get_screen_lines()\n\tcsl, csl_cached := self.completion_screen_lines()\n\trender_completion_above := len(csl)+len(prompt_lines) > self.screen_height\n\tcompletion_needs_render := len(csl) > 0 && (!render_completion_above || !self.completions.current.last_rendered_above || !csl_cached)\n\tfinal_cursor_x := -1\n\tcursor_y := 0\n\tmove_cursor_up_by := 0\n\n\trender_completion_lines := func() int {\n\t\tif completion_needs_render {\n\t\t\tif render_completion_above {\n\t\t\t\tself.loop.QueueWriteString(\"\\r\")\n\t\t\t} else {\n\t\t\t\tself.loop.QueueWriteString(\"\\r\\n\")\n\t\t\t}\n\t\t\tfor i, cl := range csl {\n\t\t\t\tself.loop.QueueWriteString(cl)\n\t\t\t\tif i < len(csl)-1 || render_completion_above {\n\t\t\t\t\tself.loop.QueueWriteString(\"\\n\\r\")\n\t\t\t\t}\n\n\t\t\t}\n\t\t\treturn len(csl)\n\t\t}\n\t\treturn 0\n\t}\n\n\tself.loop.AllowLineWrapping(false)\n\tif render_completion_above {\n\t\trender_completion_lines()\n\t}\n\tself.loop.AllowLineWrapping(true)\n\tself.loop.QueueWriteString(\"\\r\")\n\ttext_length := 0\n\n\tfor i, sl := range prompt_lines {\n\t\tcursor_moved_down := false\n\t\tif i > 0 && sl.AfterLineBreak {\n\t\t\tself.loop.QueueWriteString(\"\\r\\n\")\n\t\t\tcursor_moved_down = true\n\t\t\ttext_length = 0\n\t\t}\n\t\tif sl.Prompt.Length > 0 {\n\t\t\tp := self.prompt_for_line_number(i)\n\t\t\tself.loop.QueueWriteString(p.Text)\n\t\t\ttext_length += p.Length\n\t\t}\n\t\tself.loop.QueueWriteString(sl.Text)\n\t\ttext_length += sl.TextLengthInCells\n\t\tif text_length == self.screen_width && sl.Text == \"\" && i == len(prompt_lines)-1 {\n\t\t\tself.loop.QueueWriteString(\"\\r\\n\")\n\t\t\tcursor_moved_down = true\n\t\t\ttext_length = 0\n\t\t}\n\t\tif text_length > self.screen_width {\n\t\t\tcursor_moved_down = true\n\t\t\ttext_length -= self.screen_width\n\t\t}\n\t\tif sl.CursorCell > -1 {\n\t\t\tfinal_cursor_x = sl.CursorCell\n\t\t} else if final_cursor_x > -1 {\n\t\t\tif cursor_moved_down {\n\t\t\t\tmove_cursor_up_by++\n\t\t\t}\n\t\t}\n\t\tif cursor_moved_down {\n\t\t\tcursor_y++\n\t\t}\n\t}\n\tif !render_completion_above {\n\t\tmove_cursor_up_by += render_completion_lines()\n\t}\n\tself.loop.MoveCursorVertically(-move_cursor_up_by)\n\tself.loop.QueueWriteString(\"\\r\")\n\tself.loop.MoveCursorHorizontally(final_cursor_x)\n\tself.cursor_y = 0\n\tcursor_y -= move_cursor_up_by\n\tif cursor_y > 0 {\n\t\tself.cursor_y = cursor_y\n\t}\n}\n"
  },
  {
    "path": "tools/tui/readline/history.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage readline\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype HistoryItem struct {\n\tCmd       string        `json:\"cmd\"`\n\tCwd       string        `json:\"cwd,omitempty\"`\n\tTimestamp time.Time     `json:\"timestamp\"`\n\tDuration  time.Duration `json:\"duration,omitempty\"`\n\tExitCode  int           `json:\"exit_code\"`\n}\n\ntype HistoryMatches struct {\n\titems                []HistoryItem\n\tprefix               string\n\tcurrent_idx          int\n\toriginal_input_state InputState\n}\n\ntype HistorySearch struct {\n\tquery                string\n\ttokens               []string\n\titems                []*HistoryItem\n\tcurrent_idx          int\n\tbackwards            bool\n\toriginal_input_state InputState\n}\n\ntype History struct {\n\tfile_path string\n\tfile      *os.File\n\tmax_items int\n\titems     []HistoryItem\n\tcmd_map   map[string]int\n}\n\nfunc map_from_items(items []HistoryItem) map[string]int {\n\tpmap := make(map[string]int, len(items))\n\tfor i, hi := range items {\n\t\tpmap[hi.Cmd] = i\n\t}\n\treturn pmap\n}\n\nfunc (self *History) add_item(x HistoryItem) bool {\n\texisting, found := self.cmd_map[x.Cmd]\n\tif found {\n\t\tif self.items[existing].Timestamp.Before(x.Timestamp) {\n\t\t\tself.items[existing] = x\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\tself.cmd_map[x.Cmd] = len(self.items)\n\tself.items = append(self.items, x)\n\treturn true\n}\n\nfunc (self *History) merge_items(items ...HistoryItem) {\n\tif len(self.items) == 0 {\n\t\tself.items = items\n\t\tself.cmd_map = map_from_items(self.items)\n\t\treturn\n\t}\n\tif len(items) == 0 {\n\t\treturn\n\t}\n\tchanged := false\n\tfor _, x := range items {\n\t\tif self.add_item(x) {\n\t\t\tchanged = true\n\t\t}\n\t}\n\tif !changed {\n\t\treturn\n\t}\n\tself.items = utils.StableSort(self.items, func(a, b HistoryItem) int {\n\t\treturn a.Timestamp.Compare(b.Timestamp)\n\t})\n\tif len(self.items) > self.max_items {\n\t\tself.items = self.items[len(self.items)-self.max_items:]\n\t}\n\tself.cmd_map = map_from_items(self.items)\n}\n\nfunc (self *History) Write() {\n\tif self.file == nil {\n\t\treturn\n\t}\n\tself.file.Seek(0, 0)\n\tutils.LockFileExclusive(self.file)\n\tdefer utils.UnlockFile(self.file)\n\tdata, err := io.ReadAll(self.file)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar items []HistoryItem\n\terr = json.Unmarshal(data, &items)\n\tif err != nil {\n\t\tself.merge_items(items...)\n\t}\n\tndata, err := json.MarshalIndent(self.items, \"\", \"  \")\n\tif err != nil {\n\t\treturn\n\t}\n\tself.file.Truncate(int64(len(ndata)))\n\tself.file.Seek(0, 0)\n\tself.file.Write(ndata)\n}\n\nfunc (self *History) Read() {\n\tif self.file == nil {\n\t\treturn\n\t}\n\tself.file.Seek(0, 0)\n\tutils.LockFileShared(self.file)\n\tdata, err := io.ReadAll(self.file)\n\tutils.UnlockFile(self.file)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar items []HistoryItem\n\terr = json.Unmarshal(data, &items)\n\tif err == nil {\n\t\tself.merge_items(items...)\n\t}\n}\n\nfunc (self *History) AddItem(cmd string, duration time.Duration) {\n\tself.merge_items(HistoryItem{Cmd: cmd, Duration: duration, Timestamp: time.Now()})\n}\n\nfunc (self *History) Shutdown() {\n\tif self.file != nil {\n\t\tself.Write()\n\t\tself.file.Close()\n\t\tself.file = nil\n\t}\n}\n\nfunc NewHistory(path string, max_items int) *History {\n\tans := History{items: []HistoryItem{}, cmd_map: map[string]int{}, max_items: max_items}\n\tif path != \"\" {\n\t\tans.file_path = path\n\t\tf, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o600)\n\t\tif err == nil {\n\t\t\tans.file = f\n\t\t} else {\n\t\t\tfmt.Fprintln(os.Stderr, \"Failed to open history file at:\", path, \"with error:\", err)\n\t\t}\n\t}\n\tans.Read()\n\treturn &ans\n}\n\nfunc (self *History) find_prefix_matches(prefix, current_command string, input_state InputState) *HistoryMatches {\n\tans := HistoryMatches{items: make([]HistoryItem, 0, len(self.items)+1), prefix: prefix, original_input_state: input_state}\n\tif prefix == \"\" {\n\t\tans.items = ans.items[:len(self.items)]\n\t\tcopy(ans.items, self.items)\n\t} else {\n\t\tfor _, x := range self.items {\n\t\t\tif strings.HasPrefix(x.Cmd, prefix) {\n\t\t\t\tans.items = append(ans.items, x)\n\t\t\t}\n\t\t}\n\t}\n\tans.items = append(ans.items, HistoryItem{Cmd: current_command})\n\tans.current_idx = len(ans.items) - 1\n\treturn &ans\n}\n\nfunc (self *Readline) create_history_matches() {\n\tif self.last_action_was_history_movement() && self.history_matches != nil {\n\t\treturn\n\t}\n\tprefix := self.text_upto_cursor_pos()\n\tself.history_matches = self.history.find_prefix_matches(prefix, self.AllText(), self.input_state.copy())\n}\n\nfunc (self *Readline) last_action_was_history_movement() bool {\n\tswitch self.last_action {\n\tcase ActionHistoryLast, ActionHistoryFirst, ActionHistoryNext, ActionHistoryPrevious:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (self *HistoryMatches) apply(rl *Readline) bool {\n\tif self.current_idx >= len(self.items) || self.current_idx < 0 {\n\t\treturn false\n\t}\n\tif self.current_idx == len(self.items)-1 {\n\t\trl.input_state = self.original_input_state.copy()\n\t} else {\n\t\titem := self.items[self.current_idx]\n\t\trl.input_state.lines = utils.Splitlines(item.Cmd)\n\t\tif len(rl.input_state.lines) == 0 {\n\t\t\trl.input_state.lines = []string{\"\"}\n\t\t}\n\t\tidx := len(rl.input_state.lines) - 1\n\t\trl.input_state.cursor = Position{Y: idx, X: len(rl.input_state.lines[idx])}\n\t}\n\treturn true\n}\n\nfunc (self *HistoryMatches) first(rl *Readline) bool {\n\tself.current_idx = 0\n\treturn self.apply(rl)\n}\n\nfunc (self *HistoryMatches) last(rl *Readline) bool {\n\tself.current_idx = max(0, len(self.items)-1)\n\treturn self.apply(rl)\n}\n\nfunc (self *HistoryMatches) previous(num uint, rl *Readline) bool {\n\tif self.current_idx > 0 {\n\t\tself.current_idx = max(0, self.current_idx-int(num))\n\t\treturn self.apply(rl)\n\t}\n\treturn false\n}\n\nfunc (self *HistoryMatches) next(num uint, rl *Readline) bool {\n\tif self.current_idx+1 < len(self.items) {\n\t\tself.current_idx = min(len(self.items)-1, self.current_idx+int(num))\n\t\treturn self.apply(rl)\n\t}\n\treturn false\n}\n\nfunc (self *Readline) create_history_search(backwards bool, num uint) {\n\tself.history_search = &HistorySearch{backwards: backwards, original_input_state: self.input_state.copy()}\n\tself.push_keyboard_map(history_search_shortcuts())\n\tself.markup_history_search()\n}\n\nfunc (self *Readline) end_history_search(accept bool) {\n\tif accept && self.history_search.current_idx < len(self.history_search.items) {\n\t\tself.input_state.lines = utils.Splitlines(self.history_search.items[self.history_search.current_idx].Cmd)\n\t\tself.input_state.cursor.Y = len(self.input_state.lines) - 1\n\t\tself.input_state.cursor.X = len(self.input_state.lines[self.input_state.cursor.Y])\n\t} else {\n\t\tself.input_state = self.history_search.original_input_state\n\t}\n\tself.input_state.cursor = *self.ensure_position_in_bounds(&self.input_state.cursor)\n\tself.pop_keyboard_map()\n\tself.history_search = nil\n}\n\nfunc (self *Readline) markup_history_search() {\n\tif len(self.history_search.items) == 0 {\n\t\tif len(self.history_search.tokens) == 0 {\n\t\t\tself.input_state.lines = []string{\"\"}\n\t\t} else {\n\t\t\tself.input_state.lines = []string{\"No matches for: \" + self.history_search.query}\n\t\t}\n\t\tself.input_state.cursor = Position{X: wcswidth.Stringwidth(self.input_state.lines[0])}\n\t\treturn\n\t}\n\tlines := utils.Splitlines(self.history_search.items[self.history_search.current_idx].Cmd)\n\tcursor := Position{Y: len(lines)}\n\tfor _, tok := range self.history_search.tokens {\n\t\tfor i, line := range lines {\n\t\t\tif idx := strings.Index(line, tok); idx > -1 {\n\t\t\t\tq := Position{Y: i, X: idx}\n\t\t\t\tif q.Less(cursor) {\n\t\t\t\t\tcursor = q\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tself.input_state.lines = lines\n\tself.input_state.cursor = *self.ensure_position_in_bounds(&cursor)\n}\n\nfunc (self *Readline) remove_text_from_history_search(num uint) uint {\n\tl := len(self.history_search.query)\n\tnl := max(0, l-int(num))\n\tself.history_search.query = self.history_search.query[:nl]\n\tnum_removed := uint(l - nl)\n\tself.add_text_to_history_search(\"\") // update the search results\n\treturn num_removed\n}\n\nfunc (self *Readline) history_search_highlighter(text string, x, y int) string {\n\tif len(self.history_search.items) == 0 {\n\t\treturn text\n\t}\n\tlines := utils.Splitlines(text)\n\tfor _, tok := range self.history_search.tokens {\n\t\tfor i, line := range lines {\n\t\t\tif idx := strings.Index(line, tok); idx > -1 {\n\t\t\t\tlines[i] = line[:idx] + self.fmt_ctx.Green(tok) + line[idx+len(tok):]\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn strings.Join(lines, \"\\n\")\n}\n\nfunc (self *Readline) add_text_to_history_search(text string) {\n\tself.history_search.query += text\n\ttokens, err := shlex.Split(self.history_search.query)\n\tif err != nil {\n\t\ttokens = strings.Split(self.history_search.query, \" \")\n\t}\n\tself.history_search.tokens = tokens\n\tvar current_item *HistoryItem\n\tif len(self.history_search.items) > 0 {\n\t\tcurrent_item = self.history_search.items[self.history_search.current_idx]\n\t}\n\tif len(self.history_search.tokens) == 0 {\n\t\tself.history_search.items = []*HistoryItem{}\n\t} else {\n\t\titems := make([]*HistoryItem, len(self.history.items))\n\t\tfor i := range self.history.items {\n\t\t\titems[i] = &self.history.items[i]\n\t\t}\n\t\tfor _, token := range self.history_search.tokens {\n\t\t\tmatches := make([]*HistoryItem, 0, len(items))\n\t\t\tfor _, item := range items {\n\t\t\t\tif strings.Contains(item.Cmd, token) {\n\t\t\t\t\tmatches = append(matches, item)\n\t\t\t\t}\n\t\t\t}\n\t\t\titems = matches\n\t\t}\n\t\tself.history_search.items = items\n\t}\n\tidx := -1\n\tfor i, item := range self.history_search.items {\n\t\tif item == current_item {\n\t\t\tidx = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif idx == -1 {\n\t\tif self.history_search.backwards {\n\t\t\tidx = len(self.history_search.items) - 1\n\t\t} else {\n\t\t\tidx = 0\n\t\t}\n\t}\n\tself.history_search.current_idx = max(0, idx)\n\tself.markup_history_search()\n}\n\nfunc (self *Readline) next_history_search(backwards bool, num uint) bool {\n\tni := self.history_search.current_idx\n\tself.history_search.backwards = backwards\n\tif len(self.history_search.items) == 0 {\n\t\treturn false\n\t}\n\tif backwards {\n\t\tni = max(0, ni-int(num))\n\t} else {\n\t\tni = min(ni+int(num), len(self.history_search.items)-1)\n\t}\n\tif ni == self.history_search.current_idx {\n\t\treturn false\n\t}\n\tself.history_search.current_idx = ni\n\tself.markup_history_search()\n\treturn true\n}\n\nfunc (self *Readline) history_search_prompt() string {\n\tans := \"↑\"\n\tif !self.history_search.backwards {\n\t\tans = \"↓\"\n\t}\n\tfailed := len(self.history_search.tokens) > 0 && len(self.history_search.items) == 0\n\tif failed {\n\t\tans = self.fmt_ctx.BrightRed(ans)\n\t} else {\n\t\tans = self.fmt_ctx.Green(ans)\n\t}\n\treturn fmt.Sprintf(\"history %s: \", ans)\n}\n\nfunc (self *Readline) history_completer(before_cursor, after_cursor string) (ans *cli.Completions) {\n\tans = cli.NewCompletions()\n\tif before_cursor != \"\" {\n\t\tvar words_before_cursor []string\n\t\twords_before_cursor, ans.CurrentWordIdx = shlex.SplitForCompletion(before_cursor)\n\t\tidx := len(words_before_cursor)\n\t\tif idx > 0 {\n\t\t\tidx--\n\t\t}\n\t\tseen := utils.NewSet[string](16)\n\t\tmg := ans.AddMatchGroup(\"History\")\n\t\tfor _, x := range self.history.items {\n\t\t\tif strings.HasPrefix(x.Cmd, before_cursor) {\n\t\t\t\twords, _ := shlex.SplitForCompletion(x.Cmd)\n\t\t\t\tif idx < len(words) {\n\t\t\t\t\tword := words[idx]\n\t\t\t\t\tdesc := \"\"\n\t\t\t\t\tif !seen.Has(word) {\n\t\t\t\t\t\tif word != x.Cmd {\n\t\t\t\t\t\t\tdesc = x.Cmd\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmg.AddMatch(word, desc)\n\t\t\t\t\t\tseen.Add(word)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "tools/tui/readline/keys.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage readline\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/shortcuts\"\n)\n\nvar _ = fmt.Print\n\ntype ShortcutMap = shortcuts.ShortcutMap[Action]\n\ntype KeyboardState struct {\n\tactive_shortcut_maps     []*ShortcutMap\n\tcurrent_pending_keys     []string\n\tcurrent_numeric_argument string\n}\n\nvar _default_shortcuts *ShortcutMap\n\nfunc default_shortcuts() *ShortcutMap {\n\tif _default_shortcuts == nil {\n\t\tsm := shortcuts.New[Action]()\n\t\tsm.AddOrPanic(ActionBackspace, \"backspace\")\n\t\tsm.AddOrPanic(ActionBackspace, \"shift+backspace\")\n\t\tsm.AddOrPanic(ActionBackspace, \"ctrl+h\")\n\t\tsm.AddOrPanic(ActionDelete, \"delete\")\n\n\t\tsm.AddOrPanic(ActionMoveToStartOfLine, \"home\")\n\t\tsm.AddOrPanic(ActionMoveToStartOfLine, \"ctrl+a\")\n\n\t\tsm.AddOrPanic(ActionMoveToEndOfLine, \"end\")\n\t\tsm.AddOrPanic(ActionMoveToEndOfLine, \"ctrl+e\")\n\n\t\tsm.AddOrPanic(ActionMoveToStartOfDocument, \"ctrl+home\")\n\t\tsm.AddOrPanic(ActionMoveToEndOfDocument, \"ctrl+end\")\n\n\t\tsm.AddOrPanic(ActionMoveToEndOfWord, \"alt+f\")\n\t\tsm.AddOrPanic(ActionMoveToEndOfWord, \"ctrl+right\")\n\t\tsm.AddOrPanic(ActionMoveToEndOfWord, \"alt+right\")\n\t\tsm.AddOrPanic(ActionMoveToStartOfWord, \"ctrl+left\")\n\t\tsm.AddOrPanic(ActionMoveToStartOfWord, \"alt+left\")\n\t\tsm.AddOrPanic(ActionMoveToStartOfWord, \"alt+b\")\n\n\t\tsm.AddOrPanic(ActionCursorLeft, \"left\")\n\t\tsm.AddOrPanic(ActionCursorLeft, \"ctrl+b\")\n\t\tsm.AddOrPanic(ActionCursorRight, \"right\")\n\t\tsm.AddOrPanic(ActionCursorRight, \"ctrl+f\")\n\n\t\tsm.AddOrPanic(ActionClearScreen, \"ctrl+l\")\n\t\tsm.AddOrPanic(ActionAbortCurrentLine, \"ctrl+c\")\n\t\tsm.AddOrPanic(ActionAbortCurrentLine, \"ctrl+g\")\n\n\t\tsm.AddOrPanic(ActionEndInput, \"ctrl+d\")\n\t\tsm.AddOrPanic(ActionAcceptInput, \"enter\")\n\n\t\tsm.AddOrPanic(ActionKillToEndOfLine, \"ctrl+k\")\n\t\tsm.AddOrPanic(ActionKillToStartOfLine, \"ctrl+x\")\n\t\tsm.AddOrPanic(ActionKillToStartOfLine, \"ctrl+u\")\n\t\tsm.AddOrPanic(ActionKillNextWord, \"alt+d\")\n\t\tsm.AddOrPanic(ActionKillPreviousWord, \"alt+backspace\")\n\t\tsm.AddOrPanic(ActionKillPreviousSpaceDelimitedWord, \"ctrl+w\")\n\t\tsm.AddOrPanic(ActionYank, \"ctrl+y\")\n\t\tsm.AddOrPanic(ActionPopYank, \"alt+y\")\n\n\t\tsm.AddOrPanic(ActionHistoryPreviousOrCursorUp, \"up\")\n\t\tsm.AddOrPanic(ActionHistoryNextOrCursorDown, \"down\")\n\t\tsm.AddOrPanic(ActionHistoryPrevious, \"ctrl+p\")\n\t\tsm.AddOrPanic(ActionHistoryNext, \"ctrl+n\")\n\t\tsm.AddOrPanic(ActionHistoryFirst, \"alt+<\")\n\t\tsm.AddOrPanic(ActionHistoryLast, \"alt+>\")\n\t\tsm.AddOrPanic(ActionHistoryIncrementalSearchBackwards, \"ctrl+r\")\n\t\tsm.AddOrPanic(ActionHistoryIncrementalSearchBackwards, \"ctrl+?\")\n\t\tsm.AddOrPanic(ActionHistoryIncrementalSearchForwards, \"ctrl+s\")\n\t\tsm.AddOrPanic(ActionHistoryIncrementalSearchForwards, \"ctrl+/\")\n\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit0, \"alt+0\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit1, \"alt+1\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit2, \"alt+2\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit3, \"alt+3\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit4, \"alt+4\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit5, \"alt+5\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit6, \"alt+6\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit7, \"alt+7\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit8, \"alt+8\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigit9, \"alt+9\")\n\t\tsm.AddOrPanic(ActionNumericArgumentDigitMinus, \"alt+-\")\n\n\t\tsm.AddOrPanic(ActionCompleteForward, \"Tab\")\n\t\tsm.AddOrPanic(ActionCompleteBackward, \"Shift+Tab\")\n\t\t_default_shortcuts = sm\n\t}\n\treturn _default_shortcuts\n}\n\nvar _history_search_shortcuts *shortcuts.ShortcutMap[Action]\n\nfunc history_search_shortcuts() *shortcuts.ShortcutMap[Action] {\n\tif _history_search_shortcuts == nil {\n\t\tsm := shortcuts.New[Action]()\n\t\tsm.AddOrPanic(ActionBackspace, \"backspace\")\n\t\tsm.AddOrPanic(ActionBackspace, \"ctrl+h\")\n\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"home\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+a\")\n\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"end\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+e\")\n\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+home\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+end\")\n\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"alt+f\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+right\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+left\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"alt+b\")\n\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"left\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+b\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"right\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+f\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"up\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"down\")\n\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+c\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"ctrl+g\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndRestore, \"escape\")\n\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndApply, \"ctrl+d\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndApply, \"enter\")\n\t\tsm.AddOrPanic(ActionTerminateHistorySearchAndApply, \"ctrl+j\")\n\n\t\t_history_search_shortcuts = sm\n\t}\n\treturn _history_search_shortcuts\n}\n\nvar ErrCouldNotPerformAction = errors.New(\"Could not perform the specified action\")\nvar ErrAcceptInput = errors.New(\"Accept input\")\n\nfunc (self *Readline) push_keyboard_map(m *ShortcutMap) {\n\tmaps := self.keyboard_state.active_shortcut_maps\n\tself.keyboard_state = KeyboardState{}\n\tif maps == nil {\n\t\tmaps = make([]*ShortcutMap, 0, 2)\n\t}\n\tself.keyboard_state.active_shortcut_maps = append(maps, m)\n}\n\nfunc (self *Readline) pop_keyboard_map() {\n\tmaps := self.keyboard_state.active_shortcut_maps\n\tself.keyboard_state = KeyboardState{}\n\tif len(maps) > 0 {\n\t\tmaps = maps[:len(maps)-1]\n\t\tself.keyboard_state.active_shortcut_maps = maps\n\t}\n}\n\nfunc (self *Readline) handle_numeric_arg(ac Action) {\n\tt := \"-\"\n\tnum := int(ac - ActionNumericArgumentDigit0)\n\tif num < 10 {\n\t\tt = strconv.Itoa(num)\n\t}\n\tcna := self.keyboard_state.current_numeric_argument\n\tif (cna == \"\" && t == \"0\") || (cna != \"\" && t == \"-\") {\n\t\tself.add_text(t)\n\t\tself.keyboard_state.current_numeric_argument = \"\"\n\t\tself.last_action = ActionAddText\n\t} else {\n\t\tself.keyboard_state.current_numeric_argument += t\n\t\tself.last_action = ac\n\t}\n}\n\nfunc (self *Readline) dispatch_key_action(ac Action) error {\n\tself.keyboard_state.current_pending_keys = nil\n\tif ActionNumericArgumentDigit0 <= ac && ac <= ActionNumericArgumentDigitMinus {\n\t\tif self.history_search != nil {\n\t\t\treturn ErrCouldNotPerformAction\n\t\t}\n\t\tself.handle_numeric_arg(ac)\n\t\treturn nil\n\t}\n\tcna := self.keyboard_state.current_numeric_argument\n\tself.keyboard_state.current_numeric_argument = \"\"\n\tif cna == \"\" {\n\t\tcna = \"1\"\n\t}\n\trepeat_count, err := strconv.Atoi(cna)\n\tif err != nil || repeat_count <= 0 {\n\t\trepeat_count = 1\n\t}\n\treturn self.perform_action(ac, uint(repeat_count))\n}\n\nfunc (self *Readline) handle_key_event(event *loop.KeyEvent) error {\n\tif event.Text != \"\" {\n\t\treturn nil\n\t}\n\tsm := default_shortcuts()\n\tif len(self.keyboard_state.active_shortcut_maps) > 0 {\n\t\tsm = self.keyboard_state.active_shortcut_maps[len(self.keyboard_state.active_shortcut_maps)-1]\n\t}\n\tac, pending := sm.ResolveKeyEvent(event, self.keyboard_state.current_pending_keys...)\n\tif pending != \"\" {\n\t\tevent.Handled = true\n\t\tif self.keyboard_state.current_pending_keys == nil {\n\t\t\tself.keyboard_state.current_pending_keys = []string{}\n\t\t}\n\t\tself.keyboard_state.current_pending_keys = append(self.keyboard_state.current_pending_keys, pending)\n\t} else {\n\t\tself.keyboard_state.current_pending_keys = nil\n\t\tif ac != ActionNil {\n\t\t\tevent.Handled = true\n\t\t\treturn self.dispatch_key_action(ac)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tools/tui/render_lines.go",
    "content": "package tui\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\nvar _ = utils.Repr\n\nconst KittyInternalHyperlinkProtocol = \"kitty-ih\"\n\nfunc InternalHyperlink(text, id string) string {\n\treturn fmt.Sprintf(\"\\x1b]8;;%s:%s\\x1b\\\\%s\\x1b]8;;\\x1b\\\\\", KittyInternalHyperlinkProtocol, id, text)\n}\n\ntype RenderLines struct {\n}\n\nvar hyperlink_pat = sync.OnceValue(func() *regexp.Regexp {\n\treturn regexp.MustCompile(\"\\x1b]8;([^;]*);(.*?)(?:\\x1b\\\\\\\\|\\a)\")\n})\n\n// Render lines in the specified rectangle. If width > 0 then lines are wrapped\n// to fit in the width. A string containing rendered lines with escape codes to\n// move cursor is returned. Any internal hyperlinks are added to the\n// MouseState.\nfunc (r RenderLines) InRectangle(\n\tlines []string, start_x, start_y, width, height int, mouse_state *MouseState, on_click ...func(id string) error,\n) (all_rendered bool, y_after_last_line int, ans string) {\n\tend_y := start_y + height - 1\n\tif end_y < start_y {\n\t\treturn len(lines) == 0, start_y + 1, \"\"\n\t}\n\tx, y := start_x, start_y\n\tbuf := strings.Builder{}\n\tbuf.Grow(len(lines) * max(1, width) * 3)\n\tmove_cursor := func(x, y int) { buf.WriteString(fmt.Sprintf(loop.MoveCursorToTemplate, y+1, x+1)) }\n\tvar hyperlink_state struct {\n\t\taction           string\n\t\tstart_x, start_y int\n\t}\n\n\tstart_hyperlink := func(action string) {\n\t\thyperlink_state.action = action\n\t\thyperlink_state.start_x, hyperlink_state.start_y = x, y\n\t}\n\n\tadd_chunk := func(text string) {\n\t\tif text != \"\" {\n\t\t\tbuf.WriteString(text)\n\t\t\tx += wcswidth.Stringwidth(text)\n\t\t}\n\t}\n\n\tcommit_hyperlink := func() bool {\n\t\tif hyperlink_state.action == \"\" {\n\t\t\treturn false\n\t\t}\n\t\tif y == hyperlink_state.start_y && x <= hyperlink_state.start_x {\n\t\t\treturn false\n\t\t}\n\t\tmouse_state.AddCellRegion(hyperlink_state.action, hyperlink_state.start_x, hyperlink_state.start_y, max(0, x-1), y, on_click...)\n\t\thyperlink_state.action = ``\n\t\treturn true\n\t}\n\n\tadd_hyperlink := func(id, url string) {\n\t\tis_closer := id == \"\" && url == \"\"\n\t\tif is_closer {\n\t\t\tif !commit_hyperlink() {\n\t\t\t\tbuf.WriteString(\"\\x1b]8;;\\x1b\\\\\")\n\t\t\t}\n\t\t} else {\n\t\t\tcommit_hyperlink()\n\t\t\tif strings.HasPrefix(url, KittyInternalHyperlinkProtocol+\":\") {\n\t\t\t\tstart_hyperlink(url[len(KittyInternalHyperlinkProtocol)+1:])\n\t\t\t} else {\n\t\t\t\tbuf.WriteString(fmt.Sprintf(\"\\x1b]8;%s;%s\\x1b\\\\\", id, url))\n\t\t\t}\n\t\t}\n\n\t}\n\n\tadd_line := func(line string) {\n\t\tx = start_x\n\t\tindices := hyperlink_pat().FindAllStringSubmatchIndex(line, -1)\n\t\tstart := 0\n\t\tfor _, index := range indices {\n\t\t\tfull_hyperlink_start, full_hyperlink_end := index[0], index[1]\n\t\t\tadd_chunk(line[start:full_hyperlink_start])\n\t\t\tstart = full_hyperlink_end\n\t\t\tadd_hyperlink(line[index[2]:index[3]], line[index[4]:index[5]])\n\t\t}\n\t\tadd_chunk(line[start:])\n\t}\n\n\tall_rendered = true\n\two := style.WrapOptions{Trim_whitespace: true}\n\tfor _, line := range lines {\n\t\twrapped_lines := []string{line}\n\t\tif width > 0 {\n\t\t\twrapped_lines = style.WrapTextAsLines(line, width, wo)\n\t\t}\n\t\tfor _, line := range wrapped_lines {\n\t\t\tmove_cursor(start_x, y)\n\t\t\tadd_line(line)\n\t\t\ty += 1\n\t\t\tif y > end_y {\n\t\t\t\tall_rendered = false\n\t\t\t\tgoto end\n\t\t\t}\n\t\t}\n\t}\nend:\n\tcommit_hyperlink()\n\treturn all_rendered, y, buf.String()\n}\n"
  },
  {
    "path": "tools/tui/run.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/shirou/gopsutil/v4/process\"\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty/tools/config\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/shell_integration\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n)\n\nvar _ = fmt.Print\n\ntype KittyOpts struct {\n\tShell, Shell_integration string\n}\n\nfunc read_relevant_kitty_opts() KittyOpts {\n\tans := KittyOpts{Shell: kitty.KittyConfigDefaults.Shell, Shell_integration: kitty.KittyConfigDefaults.Shell_integration}\n\thandle_line := func(key, val string) error {\n\t\tswitch key {\n\t\tcase \"shell\":\n\t\t\tans.Shell = strings.TrimSpace(val)\n\t\tcase \"shell_integration\":\n\t\t\tans.Shell_integration = strings.TrimSpace(val)\n\t\t}\n\t\treturn nil\n\t}\n\tconfig.ReadKittyConfig(handle_line)\n\tif ans.Shell == \"\" {\n\t\tans.Shell = kitty.KittyConfigDefaults.Shell\n\t}\n\treturn ans\n}\n\nfunc get_effective_ksi_env_var(x string) string {\n\tparts := strings.Split(strings.TrimSpace(strings.ToLower(x)), \" \")\n\tcurrent := utils.NewSetWithItems(parts...)\n\tif current.Has(\"disabled\") {\n\t\treturn \"\"\n\t}\n\tallowed := utils.NewSetWithItems(kitty.AllowedShellIntegrationValues...)\n\tif !current.IsSubsetOf(allowed) {\n\t\treturn relevant_kitty_opts().Shell_integration\n\t}\n\treturn x\n}\n\nvar relevant_kitty_opts = sync.OnceValue(func() KittyOpts {\n\treturn read_relevant_kitty_opts()\n})\n\nfunc get_shell_from_kitty_conf() (shell string) {\n\tshell = relevant_kitty_opts().Shell\n\tif shell == \".\" {\n\t\ts, e := utils.LoginShellForCurrentUser()\n\t\tif e != nil {\n\t\t\tshell = \"/bin/sh\"\n\t\t} else {\n\t\t\tshell = s\n\t\t}\n\t}\n\treturn\n}\n\nfunc find_shell_parent_process() string {\n\tvar p *process.Process\n\tvar err error\n\tfor {\n\t\tif p == nil {\n\t\t\tp, err = process.NewProcess(int32(os.Getppid()))\n\t\t} else {\n\t\t\tp, err = p.Parent()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\tif cmdline, err := p.CmdlineSlice(); err == nil && len(cmdline) > 0 {\n\t\t\texe := get_shell_name(filepath.Base(cmdline[0]))\n\t\t\tif shell_integration.IsSupportedShell(exe) {\n\t\t\t\treturn exe\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc ResolveShell(shell string) []string {\n\tswitch shell {\n\tcase \"\":\n\t\tshell = get_shell_from_kitty_conf()\n\tcase \".\":\n\t\tif shell = find_shell_parent_process(); shell == \"\" {\n\t\t\tshell = get_shell_from_kitty_conf()\n\t\t}\n\t}\n\tshell_cmd, err := shlex.Split(shell)\n\tif err != nil {\n\t\tshell_cmd = []string{shell}\n\t}\n\texe := utils.FindExe(shell_cmd[0])\n\tif unix.Access(exe, unix.X_OK) != nil {\n\t\tshell_cmd = []string{\"/bin/sh\"}\n\t}\n\treturn shell_cmd\n}\n\nfunc ResolveShellIntegration(shell_integration string) string {\n\tif shell_integration == \"\" {\n\t\tshell_integration = relevant_kitty_opts().Shell_integration\n\t}\n\treturn get_effective_ksi_env_var(shell_integration)\n}\n\nfunc get_shell_name(argv0 string) (ans string) {\n\tans = filepath.Base(argv0)\n\tif strings.HasSuffix(strings.ToLower(ans), \".exe\") {\n\t\tans = ans[:len(ans)-4]\n\t}\n\treturn strings.TrimPrefix(ans, \"-\")\n}\n\nfunc rc_modification_allowed(ksi string) (allowed bool, set_ksi_env_var bool) {\n\tallowed = ksi != \"\"\n\tset_ksi_env_var = true\n\tfor x := range strings.SplitSeq(ksi, \" \") {\n\t\tswitch x {\n\t\tcase \"disabled\":\n\t\t\tallowed = false\n\t\t\tset_ksi_env_var = false\n\t\tcase \"no-rc\":\n\t\t\tallowed = false\n\t\t}\n\t}\n\treturn\n}\n\nfunc copy_os_env_as_dict() map[string]string {\n\toenv := os.Environ()\n\tenv := make(map[string]string, len(oenv))\n\tfor _, x := range oenv {\n\t\tif k, v, found := strings.Cut(x, \"=\"); found {\n\t\t\tenv[k] = v\n\t\t}\n\t}\n\treturn env\n}\n\nfunc RunShell(shell_cmd []string, shell_integration_env_var_val, cwd string) (err error) {\n\tshell_name := get_shell_name(shell_cmd[0])\n\tvar shell_env map[string]string\n\tif shell_integration.IsSupportedShell(shell_name) {\n\t\trc_mod_allowed, set_ksi_env_var := rc_modification_allowed(shell_integration_env_var_val)\n\t\tif rc_mod_allowed {\n\t\t\t// KITTY_SHELL_INTEGRATION is always set by this function\n\t\t\targv, env, err := shell_integration.Setup(shell_name, shell_integration_env_var_val, shell_cmd, copy_os_env_as_dict())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tshell_cmd = argv\n\t\t\tshell_env = env\n\t\t} else if set_ksi_env_var {\n\t\t\tshell_env = copy_os_env_as_dict()\n\t\t\tshell_env[\"KITTY_SHELL_INTEGRATION\"] = shell_integration_env_var_val\n\t\t}\n\t}\n\texe := shell_cmd[0]\n\tif runtime.GOOS == \"darwin\" && (os.Getenv(\"KITTY_RUNNING_SHELL_INTEGRATION_TEST\") != \"1\" || os.Getenv(\"KITTY_RUNNING_BASH_INTEGRATION_TEST\") != \"\") {\n\t\t// ensure shell runs in login mode. On macOS lots of people use ~/.bash_profile instead of ~/.bashrc\n\t\t// which means they expect the shell to run in login mode always. Le Sigh.\n\t\tshell_cmd[0] = \"-\" + filepath.Base(shell_cmd[0])\n\t}\n\tvar env []string\n\tif shell_env != nil {\n\t\tenv = make([]string, 0, len(shell_env))\n\t\tfor k, v := range shell_env {\n\t\t\tenv = append(env, fmt.Sprintf(\"%s=%s\", k, v))\n\t\t}\n\t} else {\n\t\tenv = os.Environ()\n\t}\n\t// fmt.Println(fmt.Sprintf(\"%s %v\\n%#v\", utils.FindExe(exe), shell_cmd, env))\n\tif cwd != \"\" {\n\t\t_ = os.Chdir(cwd)\n\t}\n\treturn unix.Exec(utils.FindExe(exe), shell_cmd, env)\n}\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc RunCommandRestoringTerminalToSaneStateAfter(cmd []string) {\n\texe := utils.FindExe(cmd[0])\n\tc := exec.Command(exe, cmd[1:]...)\n\tc.Stdout = os.Stdout\n\tc.Stdin = os.Stdin\n\tc.Stderr = os.Stderr\n\tterm, err := tty.OpenControllingTerm()\n\tif err == nil {\n\t\tvar state_before unix.Termios\n\t\tif term.Tcgetattr(&state_before) == nil {\n\t\t\tif _, err = term.WriteString(loop.SAVE_PRIVATE_MODE_VALUES); err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"failed to write to controlling terminal with error:\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\t_, _ = term.WriteString(strings.Join([]string{\n\t\t\t\t\tloop.RESTORE_PRIVATE_MODE_VALUES,\n\t\t\t\t\t\"\\x1b[=u\", // reset kitty keyboard protocol to legacy\n\t\t\t\t\t\"\\x1bP@kitty-restore-cursor-appearance|\\a\",\n\t\t\t\t}, \"\"))\n\t\t\t\t_ = term.Tcsetattr(tty.TCSANOW, &state_before)\n\t\t\t\tterm.Close()\n\t\t\t}()\n\t\t} else {\n\t\t\tdefer term.Close()\n\t\t}\n\t}\n\t// Ignore SIGINT as the kernel tends to send it to us as well as the\n\t// subprocess on Ctrl+C. We cant use signal.Ignore as it doesnt reset\n\t// sigprocmask so subsequent unix.Exec will inherit blocked SIGINT\n\tignore_sigint_channel := make(chan os.Signal, 512)\n\tif err = c.Start(); err != nil {\n\t\tfmt.Fprintln(os.Stderr, cmd[0], \"failed to start with error:\", err)\n\t\treturn\n\t}\n\tsignal.Notify(ignore_sigint_channel, os.Interrupt)\n\terr = c.Wait()\n\tsignal.Reset(os.Interrupt)\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, cmd[0], \"failed with error:\", err)\n\t}\n}\n"
  },
  {
    "path": "tools/tui/sgr/insert-formatting.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage sgr\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/style\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\nvar _ = fmt.Print\n\ntype UnderlineStyle uint8\n\nconst (\n\tNo_underline UnderlineStyle = iota\n\tStraight_underline\n\tDouble_underline\n\tCurly_underline\n\tDotted_underline\n\tDashed_underline\n)\n\ntype Color struct {\n\tRed, Green, Blue uint8\n\tIs_numbered      bool\n}\n\nfunc (self *Color) Set(val any) (err error) {\n\tswitch v := val.(type) {\n\tcase int:\n\t\tself.Is_numbered = true\n\t\tself.Red = uint8(v)\n\tcase style.RGBA:\n\t\tself.Is_numbered = false\n\t\tself.Red, self.Green, self.Blue = v.Red, v.Green, v.Blue\n\tcase string:\n\t\trgba, err := style.ParseColor(v)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tself.Is_numbered = false\n\t\tself.Red, self.Green, self.Blue = rgba.Red, rgba.Green, rgba.Blue\n\tdefault:\n\t\treturn fmt.Errorf(\"Unknown type to set color from: %T\", v)\n\t}\n\treturn nil\n}\n\nfunc (self Color) AsCSI(base int) string {\n\tif self.Is_numbered && base < 50 {\n\t\tif self.Red < 8 {\n\t\t\treturn strconv.Itoa(base + int(self.Red))\n\t\t}\n\t\tif self.Red < 16 {\n\t\t\treturn strconv.Itoa(base + 52 + int(self.Red))\n\t\t}\n\t\treturn fmt.Sprintf(\"%d:5:%d\", base+8, self.Red)\n\t}\n\treturn fmt.Sprintf(\"%d:2:%d:%d:%d\", base+8, self.Red, self.Green, self.Blue)\n}\n\nfunc (self *Color) FromNumber(n uint8) {\n\tself.Is_numbered, self.Red = true, n\n}\n\nfunc as_uint8(x int) uint8 {\n\treturn uint8(uint(x) & 0xff)\n}\n\nfunc (self *Color) FromExtended(nums ...int) bool {\n\tswitch nums[0] {\n\tcase 5:\n\t\tif len(nums) > 1 {\n\t\t\tself.Red = as_uint8(nums[1])\n\t\t\tself.Is_numbered = true\n\t\t\treturn true\n\t\t}\n\tcase 2:\n\t\tif len(nums) > 3 {\n\t\t\tself.Is_numbered = false\n\t\t\tself.Red, self.Green, self.Blue = as_uint8(nums[1]), as_uint8(nums[2]), as_uint8(nums[3])\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\ntype BoolVal struct{ Is_set, Val bool }\n\ntype UnderlineStyleVal struct {\n\tIs_set bool\n\tVal    UnderlineStyle\n}\ntype ColorVal struct {\n\tIs_set, Is_default bool\n\tVal                Color\n}\n\ntype SGR struct {\n\tItalic, Reverse, Bold, Dim, Strikethrough BoolVal\n\tUnderline_style                           UnderlineStyleVal\n\tForeground, Background, Underline_color   ColorVal\n}\n\nfunc (self *BoolVal) AsCSI(set, reset string) string {\n\tif !self.Is_set {\n\t\treturn \"\"\n\t}\n\tif self.Val {\n\t\treturn set\n\t}\n\treturn reset\n}\n\nfunc (self *UnderlineStyleVal) AsCSI() string {\n\tif !self.Is_set {\n\t\treturn \"\"\n\t}\n\treturn fmt.Sprintf(\"4:%d;\", self.Val)\n}\n\nfunc (self *ColorVal) AsCSI(base int) string {\n\tif !self.Is_set {\n\t\treturn \"\"\n\t}\n\tif self.Is_default {\n\t\treturn strconv.Itoa(base + 9)\n\t}\n\treturn self.Val.AsCSI(base)\n}\n\nfunc (self *SGR) AsCSI() string {\n\tans := make([]byte, 0, 16)\n\tw := func(x string) {\n\t\tif x != \"\" {\n\t\t\tans = append(ans, x...)\n\t\t\tans = append(ans, ';')\n\t\t}\n\t}\n\tw(self.Bold.AsCSI(\"1\", \"221\"))\n\tw(self.Dim.AsCSI(\"2\", \"222\"))\n\tw(self.Italic.AsCSI(\"3\", \"23\"))\n\tw(self.Reverse.AsCSI(\"7\", \"27\"))\n\tw(self.Strikethrough.AsCSI(\"9\", \"29\"))\n\tw(self.Underline_style.AsCSI())\n\tw(self.Foreground.AsCSI(30))\n\tw(self.Background.AsCSI(40))\n\tw(self.Underline_color.AsCSI(50))\n\n\tif len(ans) > 0 {\n\t\tans = ans[:len(ans)-1]\n\t\tans = append(ans, 'm')\n\t}\n\treturn utils.UnsafeBytesToString(ans)\n}\n\nfunc (self *SGR) IsEmpty() bool {\n\treturn !(self.Foreground.Is_set || self.Background.Is_set || self.Underline_color.Is_set || self.Underline_style.Is_set || self.Italic.Is_set || self.Bold.Is_set || self.Reverse.Is_set || self.Dim.Is_set || self.Strikethrough.Is_set)\n}\n\nfunc (self *SGR) ApplyMask(other SGR) {\n\tif other.Italic.Is_set {\n\t\tself.Italic.Is_set = false\n\t}\n\tif other.Reverse.Is_set {\n\t\tself.Reverse.Is_set = false\n\t}\n\tif other.Bold.Is_set {\n\t\tself.Bold.Is_set = false\n\t}\n\tif other.Dim.Is_set {\n\t\tself.Dim.Is_set = false\n\t}\n\tif other.Strikethrough.Is_set {\n\t\tself.Strikethrough.Is_set = false\n\t}\n\tif other.Underline_style.Is_set {\n\t\tself.Underline_style.Is_set = false\n\t}\n\tif other.Foreground.Is_set {\n\t\tself.Foreground.Is_set = false\n\t}\n\tif other.Background.Is_set {\n\t\tself.Background.Is_set = false\n\t}\n\tif other.Underline_color.Is_set {\n\t\tself.Underline_color.Is_set = false\n\t}\n}\n\nfunc (self *SGR) ApplySGR(other SGR) {\n\tif other.Italic.Is_set {\n\t\tself.Italic = other.Italic\n\t}\n\tif other.Reverse.Is_set {\n\t\tself.Reverse = other.Reverse\n\t}\n\tif other.Bold.Is_set {\n\t\tself.Bold = other.Bold\n\t}\n\tif other.Dim.Is_set {\n\t\tself.Dim = other.Dim\n\t}\n\tif other.Strikethrough.Is_set {\n\t\tself.Strikethrough = other.Strikethrough\n\t}\n\tif other.Underline_style.Is_set {\n\t\tself.Underline_style = other.Underline_style\n\t}\n\tif other.Foreground.Is_set {\n\t\tself.Foreground = other.Foreground\n\t}\n\tif other.Background.Is_set {\n\t\tself.Background = other.Background\n\t}\n\tif other.Underline_color.Is_set {\n\t\tself.Underline_color = other.Underline_color\n\t}\n}\n\nfunc SGRFromCSI(csi string) (ans SGR) {\n\tif !strings.HasSuffix(csi, \"m\") {\n\t\treturn\n\t}\n\tcsi = csi[:len(csi)-1]\n\tif csi == \"\" {\n\t\tcsi = \"0\"\n\t}\n\tparts := strings.Split(csi, \";\")\n\tnums := make([]int, 0, 8)\n\tfor _, part := range parts {\n\t\tsubparts := strings.Split(part, \":\")\n\t\tnums = nums[:0]\n\t\tfor _, b := range subparts {\n\t\t\tq, err := strconv.Atoi(b)\n\t\t\tif err == nil {\n\t\t\t\tnums = append(nums, q)\n\t\t\t}\n\t\t}\n\t\tif len(nums) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tswitch nums[0] {\n\t\tcase 0:\n\t\t\tans = SGR{}\n\t\tcase 1:\n\t\t\tans.Bold.Val, ans.Bold.Is_set = true, true\n\t\tcase 221:\n\t\t\tans.Bold.Val, ans.Bold.Is_set = false, true\n\t\tcase 2:\n\t\t\tans.Dim.Val, ans.Dim.Is_set = true, true\n\t\tcase 222:\n\t\t\tans.Dim.Val, ans.Dim.Is_set = false, true\n\t\tcase 22:\n\t\t\tans.Dim.Val, ans.Bold.Val = false, false\n\t\t\tans.Dim.Is_set, ans.Bold.Is_set = true, true\n\t\tcase 3:\n\t\t\tans.Italic.Is_set, ans.Italic.Val = true, true\n\t\tcase 23:\n\t\t\tans.Italic.Is_set, ans.Italic.Val = true, false\n\t\tcase 7:\n\t\t\tans.Reverse.Is_set, ans.Reverse.Val = true, true\n\t\tcase 27:\n\t\t\tans.Reverse.Is_set, ans.Reverse.Val = true, false\n\t\tcase 9:\n\t\t\tans.Strikethrough.Is_set, ans.Strikethrough.Val = true, true\n\t\tcase 29:\n\t\t\tans.Strikethrough.Is_set, ans.Strikethrough.Val = true, false\n\t\tcase 24:\n\t\t\tans.Underline_style.Is_set, ans.Underline_style.Val = true, No_underline\n\t\tcase 4:\n\t\t\tus := 1\n\t\t\tif len(nums) > 1 {\n\t\t\t\tus = nums[1]\n\t\t\t}\n\t\t\tswitch us {\n\t\t\tcase 0:\n\t\t\t\tans.Underline_style.Is_set, ans.Underline_style.Val = true, No_underline\n\t\t\tcase 1:\n\t\t\t\tans.Underline_style.Is_set, ans.Underline_style.Val = true, Straight_underline\n\t\t\tcase 2:\n\t\t\t\tans.Underline_style.Is_set, ans.Underline_style.Val = true, Double_underline\n\t\t\tcase 3:\n\t\t\t\tans.Underline_style.Is_set, ans.Underline_style.Val = true, Curly_underline\n\t\t\tcase 4:\n\t\t\t\tans.Underline_style.Is_set, ans.Underline_style.Val = true, Dotted_underline\n\t\t\tcase 5:\n\t\t\t\tans.Underline_style.Is_set, ans.Underline_style.Val = true, Dashed_underline\n\t\t\t}\n\t\tcase 30, 31, 32, 33, 34, 35, 36, 37:\n\t\t\tans.Foreground.Is_set, ans.Foreground.Is_default = true, false\n\t\t\tans.Foreground.Val.FromNumber(uint8(nums[0] - 30))\n\t\tcase 90, 91, 92, 93, 94, 95, 96, 97:\n\t\t\tans.Foreground.Is_set, ans.Foreground.Is_default = true, false\n\t\t\tans.Foreground.Val.FromNumber(uint8(nums[0] - 82))\n\t\tcase 38:\n\t\t\tif ans.Foreground.Val.FromExtended(nums[1:]...) {\n\t\t\t\tans.Foreground.Is_set, ans.Foreground.Is_default = true, false\n\t\t\t}\n\t\tcase 39:\n\t\t\tans.Foreground.Is_set, ans.Foreground.Is_default = true, true\n\t\tcase 40, 41, 42, 43, 44, 45, 46, 47:\n\t\t\tans.Background.Is_set, ans.Background.Is_default = true, false\n\t\t\tans.Background.Val.FromNumber(uint8(nums[0] - 40))\n\t\tcase 100, 101, 102, 103, 104, 105, 106, 107:\n\t\t\tans.Background.Is_set, ans.Background.Is_default = true, false\n\t\t\tans.Background.Val.FromNumber(uint8(nums[0] - 92))\n\t\tcase 48:\n\t\t\tif ans.Background.Val.FromExtended(nums[1:]...) {\n\t\t\t\tans.Background.Is_set, ans.Background.Is_default = true, false\n\t\t\t}\n\t\tcase 49:\n\t\t\tans.Background.Is_set, ans.Background.Is_default = true, true\n\t\tcase 58:\n\t\t\tif ans.Underline_color.Val.FromExtended(nums[1:]...) {\n\t\t\t\tans.Underline_color.Is_set, ans.Underline_color.Is_default = true, false\n\t\t\t}\n\t\tcase 59:\n\t\t\tans.Underline_color.Is_set, ans.Underline_color.Is_default = true, true\n\t\t}\n\t}\n\n\treturn\n}\n\ntype Span struct {\n\tOffset, Size             int // in bytes\n\topening_sgr, closing_sgr SGR\n}\n\nfunc NewSpan(offset, size int) *Span {\n\treturn &Span{Offset: offset, Size: size}\n}\n\nfunc (self *BoolVal) Set(val bool) {\n\tself.Is_set = true\n\tself.Val = val\n}\n\nfunc (self *ColorVal) Set(val any) {\n\tself.Is_set = true\n\tif val == nil {\n\t\tself.Is_default = true\n\t} else {\n\t\tself.Is_default = false\n\t\tif err := self.Val.Set(val); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n}\n\nfunc (self *Span) SetForeground(val any) *Span {\n\tself.opening_sgr.Foreground.Set(val)\n\treturn self\n}\n\nfunc (self *Span) SetBackground(val any) *Span {\n\tself.opening_sgr.Background.Set(val)\n\treturn self\n}\n\nfunc (self *Span) SetUnderlineColor(val any) *Span {\n\tself.opening_sgr.Underline_color.Set(val)\n\treturn self\n}\n\nfunc (self *Span) SetItalic(val bool) *Span {\n\tself.opening_sgr.Italic.Set(val)\n\treturn self\n}\n\nfunc (self *Span) SetBold(val bool) *Span {\n\tself.opening_sgr.Bold.Set(val)\n\treturn self\n}\nfunc (self *Span) SetReverse(val bool) *Span {\n\tself.opening_sgr.Reverse.Set(val)\n\treturn self\n}\nfunc (self *Span) SetDim(val bool) *Span {\n\tself.opening_sgr.Dim.Set(val)\n\treturn self\n}\nfunc (self *Span) SetStrikethrough(val bool) *Span {\n\tself.opening_sgr.Strikethrough.Set(val)\n\treturn self\n}\nfunc (self *Span) SetUnderlineStyle(val UnderlineStyle) *Span {\n\tself.opening_sgr.Underline_style.Is_set = true\n\tself.opening_sgr.Underline_style.Val = val\n\treturn self\n}\n\nfunc (self *Span) SetClosingForeground(val any) *Span {\n\tself.closing_sgr.Foreground.Set(val)\n\treturn self\n}\n\nfunc (self *Span) SetClosingBackground(val any) *Span {\n\tself.closing_sgr.Background.Set(val)\n\treturn self\n}\n\nfunc (self *Span) SetClosingUnderlineColor(val any) *Span {\n\tself.closing_sgr.Underline_color.Set(val)\n\treturn self\n}\n\nfunc (self *Span) SetClosingItalic(val bool) *Span {\n\tself.closing_sgr.Italic.Set(val)\n\treturn self\n}\n\nfunc (self *Span) SetClosingBold(val bool) *Span {\n\tself.closing_sgr.Bold.Set(val)\n\treturn self\n}\nfunc (self *Span) SetClosingReverse(val bool) *Span {\n\tself.closing_sgr.Reverse.Set(val)\n\treturn self\n}\nfunc (self *Span) SetClosingDim(val bool) *Span {\n\tself.closing_sgr.Dim.Set(val)\n\treturn self\n}\nfunc (self *Span) SetClosingStrikethrough(val bool) *Span {\n\tself.closing_sgr.Strikethrough.Set(val)\n\treturn self\n}\nfunc (self *Span) SetClosingUnderlineStyle(val UnderlineStyle) *Span {\n\tself.closing_sgr.Underline_style.Is_set = true\n\tself.opening_sgr.Underline_style.Val = val\n\treturn self\n}\n\n// Insert formatting into text at the specified offsets, overriding any existing formatting, and restoring\n// existing formatting after the replaced sections.\nfunc InsertFormatting(text string, spans ...*Span) string {\n\tspans = utils.Filter(spans, func(s *Span) bool { return !s.opening_sgr.IsEmpty() })\n\tif len(spans) == 0 {\n\t\treturn text\n\t}\n\tvar in_span *Span\n\tans := make([]byte, 0, 2*len(text))\n\tvar overall_sgr_state SGR\n\tslices.SortFunc(spans, func(a, b *Span) int { return a.Offset - b.Offset })\n\ttext_len := 0\n\tvar ep *wcswidth.EscapeCodeParser\n\n\twrite_csi := func(csi string) {\n\t\tif csi != \"\" {\n\t\t\tans = append(ans, 0x1b, '[')\n\t\t\tans = append(ans, csi...)\n\t\t}\n\t}\n\topen_span := func() {\n\t\tin_span = spans[0]\n\t\tspans = spans[1:]\n\t\tif in_span.Size > 0 {\n\t\t\twrite_csi(in_span.opening_sgr.AsCSI())\n\t\t} else {\n\t\t\tin_span = nil\n\t\t}\n\n\t}\n\n\tclose_span := func() {\n\t\twrite_csi(in_span.closing_sgr.AsCSI())\n\t\twrite_csi(overall_sgr_state.AsCSI())\n\t\tin_span = nil\n\t}\n\n\tep = &wcswidth.EscapeCodeParser{\n\t\tHandleRune: func(ch rune) error {\n\t\t\tvar rlen int\n\t\t\tif in_span == nil {\n\t\t\t\tif len(spans) > 0 && text_len >= spans[0].Offset {\n\t\t\t\t\topen_span()\n\t\t\t\t\treturn ep.HandleRune(ch)\n\t\t\t\t}\n\t\t\t\tbefore := len(ans)\n\t\t\t\tans = utf8.AppendRune(ans, ch)\n\t\t\t\trlen = len(ans) - before\n\t\t\t} else {\n\t\t\t\trlen = utf8.RuneLen(ch)\n\t\t\t\tif text_len+rlen > in_span.Offset+in_span.Size {\n\t\t\t\t\tclose_span()\n\t\t\t\t}\n\t\t\t\tans = utf8.AppendRune(ans, ch)\n\t\t\t}\n\t\t\ttext_len += rlen\n\t\t\treturn nil\n\t\t},\n\t\tHandleCSI: func(csib []byte) error {\n\t\t\tcsi := utils.UnsafeBytesToString(csib)\n\t\t\tif len(csi) == 0 || csi[len(csi)-1] != 'm' {\n\t\t\t\twrite_csi(csi)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tsgr := SGRFromCSI(csi)\n\t\t\toverall_sgr_state.ApplySGR(sgr)\n\t\t\tif in_span == nil {\n\t\t\t\twrite_csi(csi)\n\t\t\t} else {\n\t\t\t\tsgr.ApplyMask(in_span.opening_sgr)\n\t\t\t\tcsi := sgr.AsCSI()\n\t\t\t\twrite_csi(csi)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t\tHandleOSC: func(osc []byte) error {\n\t\t\tans = append(ans, 0x1b, ']')\n\t\t\tans = append(ans, osc...)\n\t\t\tans = append(ans, 0x1b, '\\\\')\n\t\t\treturn nil\n\t\t},\n\t}\n\tep.ParseString(text)\n\tif in_span != nil {\n\t\tclose_span()\n\t}\n\treturn utils.UnsafeBytesToString(ans)\n}\n"
  },
  {
    "path": "tools/tui/sgr/insert-formatting_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage sgr\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestInsertFormatting(t *testing.T) {\n\ttest := func(src, expected string, spans ...*Span) {\n\t\tactual := InsertFormatting(src, spans...)\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed with %#v:\\n%#v != %#v\\n%s\", src, expected, actual, diff)\n\t\t}\n\t}\n\ttest(\n\t\t\"\\x1b[44m abcd \\x1b[49m\",\n\t\t\"\\x1b[44m a\\x1b[33;41mbc\\x1b[39;49m\\x1b[44md \\x1b[49m\",\n\t\tNewSpan(2, 2).SetForeground(3).SetBackground(1).SetClosingForeground(nil).SetClosingBackground(nil),\n\t)\n\ttest(\n\t\t\"abcd\",\n\t\t\"a\\x1b[92mbcd\\x1b[39m\",\n\t\tNewSpan(1, 11).SetForeground(10).SetClosingForeground(nil),\n\t)\n\ttest(\n\t\t\"AB\\x1b[1mC\\x1b[221mDE\",\n\t\t\"A\\x1b[37mB\\x1b[1mC\\x1b[221mDE\\x1b[39m\\x1b[221m\",\n\t\tNewSpan(1, 11).SetForeground(7).SetClosingForeground(nil),\n\t)\n}\n"
  },
  {
    "path": "tools/tui/shell_integration/api.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage shell_integration\n\nimport (\n\t\"archive/tar\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"maps\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype integration_setup_func = func(shell_integration_dir string, argv []string, env map[string]string) ([]string, map[string]string, error)\n\nfunc TerminfoData() string {\n\td := Data()\n\tentry := d[\"terminfo/x/xterm-kitty\"]\n\treturn utils.UnsafeBytesToString(entry.Data)\n}\n\nfunc extract_files(match, dest_dir string) (err error) {\n\td := Data()\n\tfor _, fname := range d.FilesMatching(match) {\n\t\tentry := d[fname]\n\t\tdest := filepath.Join(dest_dir, fname)\n\t\tddir := filepath.Dir(dest)\n\t\tif err = os.MkdirAll(ddir, 0o755); err != nil {\n\t\t\treturn\n\t\t}\n\t\tswitch entry.Metadata.Typeflag {\n\t\tcase tar.TypeDir:\n\t\t\tif err = os.MkdirAll(dest, 0o755); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase tar.TypeSymlink:\n\t\t\tif err = os.Symlink(entry.Metadata.Linkname, dest); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase tar.TypeReg:\n\t\t\tif existing, rerr := os.ReadFile(dest); rerr == nil && bytes.Equal(existing, entry.Data) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err = utils.AtomicWriteFile(dest, bytes.NewReader(entry.Data), 0o644); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc extract_shell_integration_for(shell_name string, dest_dir string) (err error) {\n\treturn extract_files(\"shell-integration/\"+shell_name+\"/\", dest_dir)\n}\n\nfunc extract_terminfo(dest_dir string) (err error) {\n\tvar s os.FileInfo\n\tif s, err = os.Stat(filepath.Join(dest_dir, \"terminfo\", \"x\", kitty.DefaultTermName)); err == nil && s.Mode().IsRegular() {\n\t\tif s, err = os.Stat(filepath.Join(dest_dir, \"terminfo\", \"78\", kitty.DefaultTermName)); err == nil && s.Mode().IsRegular() {\n\t\t\treturn\n\t\t}\n\t}\n\tif err = extract_files(\"terminfo/\", dest_dir); err == nil {\n\t\tdest := filepath.Join(dest_dir, \"terminfo\", \"78\")\n\t\terr = os.Symlink(\"x\", dest)\n\t}\n\treturn\n}\n\nfunc PathToTerminfoDb(term string) (ans string) {\n\t// see man terminfo for the algorithm ncurses uses for this\n\n\tseen := utils.NewSet[string]()\n\tcheck_dir := func(path string) string {\n\t\tif seen.Has(path) {\n\t\t\treturn ``\n\t\t}\n\t\tseen.Add(path)\n\t\tq := filepath.Join(path, term[:1], term)\n\t\tif s, err := os.Stat(q); err == nil && s.Mode().IsRegular() {\n\t\t\treturn q\n\t\t}\n\t\tif entries, err := os.ReadDir(filepath.Join(path)); err == nil {\n\t\t\tfor _, x := range entries {\n\t\t\t\tq := filepath.Join(path, x.Name(), term)\n\t\t\t\tif s, err := os.Stat(q); err == nil && s.Mode().IsRegular() {\n\t\t\t\t\treturn q\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn ``\n\t}\n\n\tif td := os.Getenv(\"TERMINFO\"); td != \"\" {\n\t\tif ans = check_dir(td); ans != \"\" {\n\t\t\treturn ans\n\t\t}\n\t}\n\n\tif ans = check_dir(utils.Expanduser(\"~/.terminfo\")); ans != \"\" {\n\t\treturn ans\n\t}\n\tif td := os.Getenv(\"TERMINFO_DIRS\"); td != \"\" {\n\t\tfor q := range strings.SplitSeq(td, string(os.PathListSeparator)) {\n\t\t\tif q == \"\" {\n\t\t\t\tq = \"/usr/share/terminfo\"\n\t\t\t}\n\t\t\tif ans = check_dir(q); ans != \"\" {\n\t\t\t\treturn ans\n\t\t\t}\n\t\t}\n\t}\n\tfor _, q := range []string{\"/usr/share/terminfo\", \"/usr/lib/terminfo\", \"/usr/share/lib/terminfo\"} {\n\t\tif ans = check_dir(q); ans != \"\" {\n\t\t\treturn ans\n\t\t}\n\t}\n\treturn\n}\n\nfunc EnsureTerminfoFiles() (terminfo_dir string, err error) {\n\tif kid := os.Getenv(\"KITTY_INSTALLATION_DIR\"); kid != \"\" {\n\t\tif s, e := os.Stat(kid); e == nil && s.IsDir() {\n\t\t\tq := filepath.Join(kid, \"terminfo\")\n\t\t\tif s, e := os.Stat(q); e == nil && s.IsDir() {\n\t\t\t\treturn q, nil\n\t\t\t}\n\t\t}\n\t}\n\tbase := filepath.Join(utils.CacheDir(), \"extracted-kti\")\n\tif err = os.MkdirAll(base, 0o755); err != nil {\n\t\treturn \"\", err\n\t}\n\tif err = extract_terminfo(base); err != nil {\n\t\treturn \"\", fmt.Errorf(\"Failed to extract terminfo files with error: %w\", err)\n\t}\n\treturn filepath.Join(base, \"terminfo\"), nil\n}\n\nfunc EnsureShellIntegrationFilesFor(shell_name string) (shell_integration_dir_for_shell string, err error) {\n\tif kid := os.Getenv(\"KITTY_INSTALLATION_DIR\"); kid != \"\" {\n\t\tif s, e := os.Stat(kid); e == nil && s.IsDir() {\n\t\t\tq := filepath.Join(kid, \"shell-integration\", shell_name)\n\t\t\tif s, e := os.Stat(q); e == nil && s.IsDir() {\n\t\t\t\treturn q, nil\n\t\t\t}\n\t\t}\n\t}\n\tbase := filepath.Join(utils.CacheDir(), \"extracted-ksi\")\n\tif err = os.MkdirAll(base, 0o755); err != nil {\n\t\treturn \"\", err\n\t}\n\tif err = extract_shell_integration_for(shell_name, base); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn filepath.Join(base, \"shell-integration\", shell_name), nil\n}\n\nfunc is_new_zsh_install(env map[string]string, zdotdir string) bool {\n\t// if ZDOTDIR is empty, zsh will read user rc files from /\n\t// if there aren't any, it'll run zsh-newuser-install\n\t// the latter will bail if there are rc files in $HOME\n\tif zdotdir == \"\" {\n\t\tif zdotdir = env[`HOME`]; zdotdir == \"\" {\n\t\t\tif q, err := os.UserHomeDir(); err == nil {\n\t\t\t\tzdotdir = q\n\t\t\t} else {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\tfor _, q := range []string{`.zshrc`, `.zshenv`, `.zprofile`, `.zlogin`} {\n\t\tif _, e := os.Stat(filepath.Join(zdotdir, q)); e == nil {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc get_zsh_zdotdir_from_global_zshenv(argv []string, env map[string]string) string {\n\tc := exec.Command(utils.FindExe(argv[0]), `--norcs`, `--interactive`, `-c`, `echo -n $ZDOTDIR`)\n\tfor k, v := range env {\n\t\tc.Env = append(c.Env, k+\"=\"+v)\n\t}\n\tif raw, err := c.Output(); err == nil {\n\t\treturn utils.UnsafeBytesToString(raw)\n\t}\n\treturn \"\"\n}\n\nfunc zsh_setup_func(shell_integration_dir string, argv []string, env map[string]string) (final_argv []string, final_env map[string]string, err error) {\n\tzdotdir := env[`ZDOTDIR`]\n\tfinal_argv, final_env = argv, env\n\tif is_new_zsh_install(env, zdotdir) {\n\t\tif zdotdir == \"\" {\n\t\t\t// Try to get ZDOTDIR from /etc/zshenv, when all startup files are not present\n\t\t\tzdotdir = get_zsh_zdotdir_from_global_zshenv(argv, env)\n\t\t\tif zdotdir == \"\" || is_new_zsh_install(env, zdotdir) {\n\t\t\t\treturn final_argv, final_env, nil\n\t\t\t}\n\t\t} else {\n\t\t\t// dont prevent zsh-newuser-install from running\n\t\t\t// zsh-newuser-install never runs as root but we assume that it does\n\t\t\treturn final_argv, final_env, nil\n\t\t}\n\t}\n\tif zdotdir != \"\" {\n\t\tenv[`KITTY_ORIG_ZDOTDIR`] = zdotdir\n\t} else {\n\t\t// KITTY_ORIG_ZDOTDIR can be set at this point if, for example, the global\n\t\t// zshenv overrides ZDOTDIR; we try to limit the damage in this case\n\t\tdelete(final_env, `KITTY_ORIG_ZDOTDIR`)\n\t}\n\tfinal_env[`ZDOTDIR`] = shell_integration_dir\n\treturn\n}\n\nfunc fish_setup_func(shell_integration_dir string, argv []string, env map[string]string) (final_argv []string, final_env map[string]string, err error) {\n\tshell_integration_dir = filepath.Dir(shell_integration_dir)\n\tval := env[`XDG_DATA_DIRS`]\n\tenv[`KITTY_FISH_XDG_DATA_DIR`] = shell_integration_dir\n\tif val == \"\" {\n\t\tenv[`XDG_DATA_DIRS`] = shell_integration_dir\n\t} else {\n\t\tdirs := utils.Filter(strings.Split(val, string(filepath.ListSeparator)), func(x string) bool { return x != \"\" })\n\t\tdirs = append([]string{shell_integration_dir}, dirs...)\n\t\tenv[`XDG_DATA_DIRS`] = strings.Join(dirs, string(filepath.ListSeparator))\n\t}\n\treturn argv, env, nil\n}\n\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc bash_setup_func(shell_integration_dir string, argv []string, env map[string]string) ([]string, map[string]string, error) {\n\tinject := utils.NewSetWithItems(`1`)\n\tvar posix_env, rcfile string\n\tremove_args := utils.NewSet[int](8)\n\texpecting_multi_chars_opt := true\n\tvar expecting_option_arg, interactive_opt, expecting_file_arg, file_arg_set bool\n\n\tfor i := 1; i < len(argv); i++ {\n\t\targ := argv[i]\n\t\tif expecting_file_arg {\n\t\t\tfile_arg_set = true\n\t\t\tbreak\n\t\t}\n\t\tif expecting_option_arg {\n\t\t\texpecting_option_arg = false\n\t\t\tcontinue\n\t\t}\n\t\tif arg == `-` || arg == `--` {\n\t\t\tif !expecting_file_arg {\n\t\t\t\texpecting_file_arg = true\n\t\t\t}\n\t\t\tcontinue\n\t\t} else if len(arg) > 1 && arg[1] != '-' && (arg[0] == '-' || strings.HasPrefix(arg, `+O`)) {\n\t\t\texpecting_multi_chars_opt = false\n\t\t\toptions := strings.TrimLeft(arg, `-+`)\n\t\t\t// shopt option\n\t\t\tif a, b, found := strings.Cut(options, `O`); found {\n\t\t\t\tif b == \"\" {\n\t\t\t\t\texpecting_option_arg = true\n\t\t\t\t}\n\t\t\t\toptions = a\n\t\t\t}\n\t\t\t// command string\n\t\t\tif strings.ContainsRune(options, 'c') {\n\t\t\t\t// non-interactive shell\n\t\t\t\t// also skip `bash -ic` interactive mode with command string\n\t\t\t\treturn argv, env, nil\n\t\t\t}\n\t\t\t// read from stdin and follow with args\n\t\t\tif strings.ContainsRune(options, 's') {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\t// interactive option\n\t\t\tif strings.ContainsRune(options, 'i') {\n\t\t\t\tinteractive_opt = true\n\t\t\t}\n\t\t} else if strings.HasPrefix(arg, `--`) && expecting_multi_chars_opt {\n\t\t\tif arg == `--posix` {\n\t\t\t\tinject.Add(`posix`)\n\t\t\t\tposix_env = env[`ENV`]\n\t\t\t\tremove_args.Add(i)\n\t\t\t} else if arg == `--norc` {\n\t\t\t\tinject.Add(`no-rc`)\n\t\t\t\tremove_args.Add(i)\n\t\t\t} else if arg == `--noprofile` {\n\t\t\t\tinject.Add(`no-profile`)\n\t\t\t\tremove_args.Add(i)\n\t\t\t} else if (arg == `--rcfile` || arg == `--init-file`) && i+1 < len(argv) {\n\t\t\t\texpecting_option_arg = true\n\t\t\t\trcfile = argv[i+1]\n\t\t\t\tremove_args.AddItems(i, i+1)\n\t\t\t}\n\t\t} else {\n\t\t\tfile_arg_set = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif file_arg_set && !interactive_opt {\n\t\t// non-interactive shell\n\t\treturn argv, env, nil\n\t}\n\tenv[`ENV`] = filepath.Join(shell_integration_dir, `kitty.bash`)\n\tenv[`KITTY_BASH_INJECT`] = strings.Join(inject.AsSlice(), \" \")\n\tif posix_env != \"\" {\n\t\tenv[`KITTY_BASH_POSIX_ENV`] = posix_env\n\t}\n\tif rcfile != \"\" {\n\t\tenv[`KITTY_BASH_RCFILE`] = rcfile\n\t}\n\tsorted := remove_args.AsSlice()\n\tslices.Sort(sorted)\n\tfor _, i := range utils.Reverse(sorted) {\n\t\targv = slices.Delete(argv, i, i+1)\n\t}\n\tif env[`HISTFILE`] == \"\" && !inject.Has(`posix`) {\n\t\t// In POSIX mode the default history file is ~/.sh_history instead of ~/.bash_history\n\t\tenv[`HISTFILE`] = utils.Expanduser(`~/.bash_history`)\n\t\tenv[`KITTY_BASH_UNEXPORT_HISTFILE`] = `1`\n\t}\n\targv = slices.Insert(argv, 1, `--posix`)\n\n\tif bashrc := os.Getenv(`KITTY_RUNNING_BASH_INTEGRATION_TEST`); bashrc != `` && os.Getenv(\"KITTY_RUNNING_SHELL_INTEGRATION_TEST\") == \"1\" {\n\t\t// prevent bash from sourcing /etc/profile which is not under our control\n\t\tenv[`KITTY_BASH_INJECT`] += ` posix`\n\t\tenv[`KITTY_BASH_POSIX_ENV`] = bashrc\n\t}\n\n\treturn argv, env, nil\n}\n\nfunc setup_func_for_shell(shell_name string) integration_setup_func {\n\tswitch shell_name {\n\tcase \"zsh\":\n\t\treturn zsh_setup_func\n\tcase \"fish\":\n\t\treturn fish_setup_func\n\tcase \"bash\":\n\t\treturn bash_setup_func\n\t}\n\treturn nil\n}\n\nfunc IsSupportedShell(shell_name string) bool { return setup_func_for_shell(shell_name) != nil }\n\nfunc Setup(shell_name string, ksi_var string, argv []string, env map[string]string) ([]string, map[string]string, error) {\n\tksi_dir, err := EnsureShellIntegrationFilesFor(shell_name)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\targv, env, err = setup_func_for_shell(shell_name)(ksi_dir, slices.Clone(argv), maps.Clone(env))\n\tif err == nil {\n\t\tenv[`KITTY_SHELL_INTEGRATION`] = ksi_var\n\t}\n\treturn argv, env, err\n}\n"
  },
  {
    "path": "tools/tui/shell_integration/api_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage shell_integration\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestExtractShellIntegration(t *testing.T) {\n\ttdir := t.TempDir()\n\tif err := extract_shell_integration_for(\"zsh\", tdir); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tkzsh := filepath.Join(tdir, \"shell-integration\", \"zsh\", \"kitty.zsh\")\n\tif _, err := os.Stat(kzsh); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif _, err := os.Stat(filepath.Join(tdir, \"shell-integration\", \"zsh\", \"completions\", \"_kitty\")); err != nil {\n\t\tt.Fatal(err)\n\t}\n\torig, err := os.ReadFile(kzsh)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_ = os.WriteFile(kzsh, []byte(\"changed\"), 0o644)\n\tif err := extract_shell_integration_for(\"zsh\", tdir); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tchanged, err := os.ReadFile(kzsh)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Equal(changed, orig) {\n\t\tt.Fatalf(\"Failed to update shell integration file\")\n\t}\n\n\tif err = extract_terminfo(tdir); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif _, err := os.Stat(filepath.Join(tdir, \"terminfo\", \"78\", kitty.DefaultTermName)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tTerminfoData()\n}\n"
  },
  {
    "path": "tools/tui/shell_integration/data.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage shell_integration\n\nimport (\n\t\"archive/tar\"\n\t_ \"embed\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"io\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n)\n\nvar _ = fmt.Print\n\n//go:embed data_generated.bin\nvar embedded_data string\n\ntype Entry struct {\n\tMetadata *tar.Header\n\tData     []byte\n}\n\ntype Container map[string]Entry\n\nvar Data = sync.OnceValue(func() Container {\n\ttr := tar.NewReader(utils.ReaderForCompressedEmbeddedData(embedded_data))\n\tans := make(Container, 64)\n\tfor {\n\t\thdr, err := tr.Next()\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdata, err := utils.ReadAll(tr, int(hdr.Size))\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tans[hdr.Name] = Entry{hdr, data}\n\t}\n\treturn ans\n})\n\nfunc (self Container) FilesMatching(prefix string, exclude_patterns ...string) []string {\n\tans := make([]string, 0, len(self))\n\tpatterns := make([]*regexp.Regexp, len(exclude_patterns))\n\tfor i, exp := range exclude_patterns {\n\t\tpatterns[i] = regexp.MustCompile(exp)\n\t}\n\tfor name := range self {\n\t\tif strings.HasPrefix(name, prefix) {\n\t\t\texcluded := false\n\t\t\tfor _, pat := range patterns {\n\t\t\t\tif matched := pat.FindString(name); matched != \"\" {\n\t\t\t\t\texcluded = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !excluded {\n\t\t\t\tans = append(ans, name)\n\t\t\t}\n\t\t}\n\t}\n\treturn ans\n}\n"
  },
  {
    "path": "tools/tui/shortcuts/api.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage shortcuts\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/tui/loop\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\ntype ShortcutMap[T comparable] struct {\n\tleaves   map[string]T\n\tchildren map[string]*ShortcutMap[T]\n}\n\nfunc (self *ShortcutMap[T]) ResolveKeyEvent(k *loop.KeyEvent, pending_keys ...string) (ac T, pending string) {\n\tq := self\n\tfor _, pk := range pending_keys {\n\t\tq = self.children[pk]\n\t\tif q == nil {\n\t\t\treturn\n\t\t}\n\t}\n\tfor c, ans := range q.leaves {\n\t\tif k.MatchesPressOrRepeat(c) {\n\t\t\tac = ans\n\t\t\treturn\n\t\t}\n\t}\n\tfor c := range q.children {\n\t\tif k.MatchesPressOrRepeat(c) {\n\t\t\tpending = c\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *ShortcutMap[T]) Add(ac T, keys ...string) (conflict T) {\n\treturn self.add(ac, keys)\n}\n\nfunc (self *ShortcutMap[T]) AddOrPanic(ac T, keys ...string) {\n\tvar zero T\n\tc := self.add(ac, keys)\n\tif c != zero {\n\t\tpanic(fmt.Sprintf(\"The shortcut for %#v (%s) conflicted with the shortcut for %#v (%s)\",\n\t\t\tac, strings.Join(keys, \" \"), c, strings.Join(self.shortcut_for(c), \" \")))\n\t}\n}\n\nfunc New[T comparable]() *ShortcutMap[T] {\n\treturn &ShortcutMap[T]{leaves: make(map[string]T), children: make(map[string]*ShortcutMap[T])}\n}\n"
  },
  {
    "path": "tools/tui/shortcuts/implementation.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage shortcuts\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\nfunc (self *ShortcutMap[T]) first_action() (ans T) {\n\tfor _, ac := range self.leaves {\n\t\treturn ac\n\t}\n\tfor _, child := range self.children {\n\t\treturn child.first_action()\n\t}\n\treturn\n}\n\nfunc (self *ShortcutMap[T]) shortcut_for(ac T) (keys []string) {\n\tkeys = []string{}\n\tfor key, q := range self.leaves {\n\t\tif ac == q {\n\t\t\treturn append(keys, key)\n\t\t}\n\t}\n\tfor key, child := range self.children {\n\t\tckeys := child.shortcut_for(ac)\n\t\tif len(ckeys) > 0 {\n\t\t\treturn append(append(keys, key), ckeys...)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *ShortcutMap[T]) add(ac T, keys []string) (conflict T) {\n\tsm := self\n\tlast := len(keys) - 1\n\tfor i, key := range keys {\n\t\tif i == last {\n\t\t\tif c, found := sm.leaves[key]; found {\n\t\t\t\tconflict = c\n\t\t\t}\n\t\t\tsm.leaves[key] = ac\n\t\t\tif c, found := sm.children[key]; found {\n\t\t\t\tconflict = c.first_action()\n\t\t\t\tdelete(sm.children, key)\n\t\t\t}\n\t\t} else {\n\t\t\tif c, found := sm.leaves[key]; found {\n\t\t\t\tconflict = c\n\t\t\t\tdelete(sm.leaves, key)\n\t\t\t}\n\t\t\tq := sm.children[key]\n\t\t\tif q == nil {\n\t\t\t\tq = &ShortcutMap[T]{leaves: map[string]T{}, children: map[string]*ShortcutMap[T]{}}\n\t\t\t\tsm.children[key] = q\n\t\t\t}\n\t\t\tsm = q\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tools/tui/spinners.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nvar _ = fmt.Print\n\ntype Spinner struct {\n\tName           string\n\tinterval       time.Duration\n\tframes         []string\n\tcurrent_frame  int\n\tlast_change_at time.Time\n}\n\nfunc (self Spinner) Interval() time.Duration {\n\treturn self.interval\n}\n\nfunc (self *Spinner) Tick() string {\n\tnow := time.Now()\n\tif now.Sub(self.last_change_at) >= self.interval {\n\t\tself.last_change_at = now\n\t\tself.current_frame = (self.current_frame + 1) % len(self.frames)\n\t}\n\treturn self.frames[self.current_frame]\n}\n"
  },
  {
    "path": "tools/tui/subseq/score.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage subseq\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nconst (\n\tLEVEL1 = \"/\"\n\tLEVEL2 = \"-_0123456789\"\n\tLEVEL3 = \".\"\n)\n\ntype resolved_options_type struct {\n\tlevel1, level2, level3 []rune\n}\n\ntype Options struct {\n\tLevel1, Level2, Level3 string\n\tNumberOfThreads        int\n}\n\ntype Match struct {\n\tPositions []int\n\tScore     float64\n\tidx       int\n\tText      string\n}\n\nfunc level_factor_for(current_lcase, last_lcase, current_cased, last_cased rune, opts *resolved_options_type) int {\n\tswitch {\n\tcase slices.Contains(opts.level1, last_lcase):\n\t\treturn 90\n\tcase slices.Contains(opts.level2, last_lcase):\n\t\treturn 80\n\tcase last_lcase == last_cased && current_lcase != current_cased: // camelCase\n\t\treturn 80\n\tcase slices.Contains(opts.level3, last_lcase):\n\t\treturn 70\n\tdefault:\n\t\treturn 0\n\t}\n}\n\ntype workspace_type struct {\n\tpositions          [][]int // positions of each needle char in haystack\n\tlevel_factors      []int\n\taddress            []int\n\tmax_score_per_char float64\n}\n\nfunc (w *workspace_type) initialize(haystack_sz, needle_sz int) {\n\tif cap(w.positions) < needle_sz {\n\t\tw.positions = make([][]int, needle_sz)\n\t} else {\n\t\tw.positions = w.positions[:needle_sz]\n\t}\n\tif cap(w.level_factors) < haystack_sz {\n\t\tw.level_factors = make([]int, 2*haystack_sz)\n\t} else {\n\t\tw.level_factors = w.level_factors[:haystack_sz]\n\t}\n\tfor i, s := range w.positions {\n\t\tif cap(s) < haystack_sz {\n\t\t\tw.positions[i] = make([]int, 0, 2*haystack_sz)\n\t\t} else {\n\t\t\tw.positions[i] = w.positions[i][:0]\n\t\t}\n\t}\n\tif cap(w.address) < needle_sz {\n\t\tw.address = make([]int, needle_sz)\n\t}\n\tw.address = utils.Memset(w.address)\n}\n\nfunc (w *workspace_type) position(x int) int { // the position of xth needle char in the haystack for the current address\n\treturn w.positions[x][w.address[x]]\n}\n\nfunc (w *workspace_type) increment_address() bool {\n\tpos := len(w.positions) - 1 // the last needle char\n\tfor {\n\t\tw.address[pos]++\n\t\tif w.address[pos] < len(w.positions[pos]) {\n\t\t\treturn true\n\t\t}\n\t\tif pos == 0 {\n\t\t\tbreak\n\t\t}\n\t\tw.address[pos] = 0\n\t\tpos--\n\t}\n\treturn false\n}\n\nfunc (w *workspace_type) address_is_monotonic() bool {\n\t// Check if the character positions pointed to by the current address are monotonic\n\tfor i := 1; i < len(w.positions); i++ {\n\t\tif w.position(i) <= w.position(i-1) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (w *workspace_type) calc_score() (ans float64) {\n\tdistance, pos := 0, 0\n\tfor i := range len(w.positions) {\n\t\tpos = w.position(i)\n\t\tif i == 0 {\n\t\t\tdistance = pos + 1\n\t\t} else {\n\t\t\tdistance = pos - w.position(i-1)\n\t\t\tif distance < 2 {\n\t\t\t\tans += w.max_score_per_char // consecutive chars\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tif w.level_factors[pos] > 0 {\n\t\t\tans += (100.0 * w.max_score_per_char) / float64(w.level_factors[pos]) // at a special location\n\t\t} else {\n\t\t\tans += (0.75 * w.max_score_per_char) / float64(distance)\n\t\t}\n\t}\n\treturn\n}\n\nfunc has_atleast_one_match(w *workspace_type) (found bool) {\n\tp := -1\n\tfor i := range len(w.positions) {\n\t\tif len(w.positions[i]) == 0 { // all chars of needle not in haystack\n\t\t\treturn false\n\t\t}\n\t\tfound = false\n\t\tfor _, pos := range w.positions[i] {\n\t\t\tif pos > p {\n\t\t\t\tp = pos\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found { // chars of needle not present in sequence in haystack\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc score_item(item string, idx int, needle []rune, opts *resolved_options_type, w *workspace_type) *Match {\n\tans := &Match{idx: idx, Text: item, Positions: make([]int, len(needle))}\n\thaystack := []rune(strings.ToLower(item))\n\torig_haystack := []rune(item)\n\tw.initialize(len(orig_haystack), len(needle))\n\tfor i := range len(haystack) {\n\t\tlevel_factor_calculated := false\n\t\tfor j := range len(needle) {\n\t\t\tif needle[j] == haystack[i] {\n\t\t\t\tif !level_factor_calculated {\n\t\t\t\t\tlevel_factor_calculated = true\n\t\t\t\t\tif i > 0 {\n\t\t\t\t\t\tw.level_factors[i] = level_factor_for(haystack[i], haystack[i-1], orig_haystack[i], orig_haystack[i-1], opts)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tw.positions[j] = append(w.positions[j], i)\n\t\t\t}\n\t\t}\n\t}\n\tw.max_score_per_char = (1.0/float64(len(orig_haystack)) + 1.0/float64(len(needle))) / 2.0\n\tif !has_atleast_one_match(w) {\n\t\treturn ans\n\t}\n\tvar score float64\n\tfor {\n\t\tif w.address_is_monotonic() {\n\t\t\tscore = w.calc_score()\n\t\t\tif score > ans.Score {\n\t\t\t\tans.Score = score\n\t\t\t\tfor i := range ans.Positions {\n\t\t\t\t\tans.Positions[i] = w.position(i)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !w.increment_address() {\n\t\t\tbreak\n\t\t}\n\t}\n\tif ans.Score > 0 {\n\t\tadjust := utils.RuneOffsetsToByteOffsets(item)\n\t\tfor i := range ans.Positions {\n\t\t\tans.Positions[i] = adjust(ans.Positions[i])\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc ScoreItems(query string, items []string, opts Options) []*Match {\n\tans := make([]*Match, len(items))\n\tnr := []rune(strings.ToLower(query))\n\tif opts.Level1 == \"\" {\n\t\topts.Level1 = LEVEL1\n\t}\n\tif opts.Level2 == \"\" {\n\t\topts.Level2 = LEVEL2\n\t}\n\tif opts.Level3 == \"\" {\n\t\topts.Level3 = LEVEL3\n\t}\n\tropts := resolved_options_type{\n\t\tlevel1: []rune(opts.Level1), level2: []rune(opts.Level2), level3: []rune(opts.Level3),\n\t}\n\tparallel.Run_in_parallel_over_range(opts.NumberOfThreads, func(start, limit int) {\n\t\tw := workspace_type{}\n\t\tfor i := start; i < limit; i++ {\n\t\t\tans[i] = score_item(items[i], i, nr, &ropts, &w)\n\t\t}\n\t}, 0, len(items))\n\treturn ans\n}\n"
  },
  {
    "path": "tools/tui/subseq/score_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage subseq\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestSubseq(t *testing.T) {\n\tvar positions [][]int\n\tsort_by_score := false\n\n\tsimple := func(items, query string, expected ...string) {\n\t\tmatches := ScoreItems(query, utils.Splitlines(items), Options{})\n\t\tif sort_by_score {\n\t\t\tmatches = utils.StableSort(matches, func(a, b *Match) int {\n\t\t\t\tif b.Score < a.Score {\n\t\t\t\t\treturn -1\n\t\t\t\t}\n\t\t\t\tif b.Score > a.Score {\n\t\t\t\t\treturn 1\n\t\t\t\t}\n\t\t\t\treturn 0\n\t\t\t})\n\t\t}\n\t\tactual := make([]string, 0, len(matches))\n\t\tactual_positions := make([][]int, 0, len(matches))\n\t\tfor _, m := range matches {\n\t\t\tif m.Score > 0 {\n\t\t\t\tactual = append(actual, m.Text)\n\t\t\t\tactual_positions = append(actual_positions, m.Positions)\n\t\t\t}\n\t\t}\n\t\tif expected == nil {\n\t\t\texpected = []string{}\n\t\t}\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed for items: %v\\nMatches: %#v\\n%s\", utils.Splitlines(items), matches, diff)\n\t\t}\n\t\tif positions != nil {\n\t\t\tif diff := cmp.Diff(positions, actual_positions); diff != \"\" {\n\t\t\t\tt.Fatalf(\"Failed positions for items: %v\\n%s\", utils.Splitlines(items), diff)\n\t\t\t}\n\t\t\tpositions = nil\n\t\t}\n\t}\n\tsimple(\"test\\nxyz\", \"te\", \"test\")\n\tsimple(\"abc\\nxyz\", \"ba\")\n\tsimple(\"abc\\n123\", \"abc\", \"abc\")\n\tsimple(\"test\\nxyz\", \"Te\", \"test\")\n\tsimple(\"test\\nxyz\", \"XY\", \"xyz\")\n\tsimple(\"test\\nXYZ\", \"xy\", \"XYZ\")\n\tsimple(\"test\\nXYZ\", \"mn\")\n\n\tpositions = [][]int{{0, 2}, {0, 1}}\n\tsimple(\"abc\\nac\", \"ac\", \"abc\", \"ac\")\n\tpositions = [][]int{{0}}\n\tsimple(\"abc\\nv\", \"a\", \"abc\")\n\tpositions = [][]int{{len(\"汉\"), 7}}\n\tsimple(\"汉a字b\\nxyz\", \"ab\", \"汉a字b\")\n\n\tsort_by_score = true\n\t// Match at start\n\tsimple(\"archer\\nelementary\", \"e\", \"elementary\", \"archer\")\n\t// Match at level factor\n\tsimple(\"xxxy\\nxx/y\", \"y\", \"xx/y\", \"xxxy\")\n\t// CamelCase\n\tsimple(\"xxxy\\nxxxY\", \"y\", \"xxxY\", \"xxxy\")\n\t// Total length\n\tsimple(\"xxxya\\nxxxy\", \"y\", \"xxxy\", \"xxxya\")\n\t// Distance\n\tsimple(\"abbc\\nabc\", \"ac\", \"abc\", \"abbc\")\n\t// Extreme chars\n\tsimple(\"xxa\\naxx\", \"a\", \"axx\", \"xxa\")\n\t// Highest score\n\tpositions = [][]int{{3}}\n\tsimple(\"xa/a\", \"a\", \"xa/a\")\n\n\tsort_by_score = false\n\titems := make([]string, 256)\n\tfor i := range items {\n\t\titems[i] = strconv.Itoa(i)\n\t}\n\texpected := make([]string, 0, len(items))\n\tfor _, x := range items {\n\t\tif strings.ContainsRune(x, rune('2')) {\n\t\t\texpected = append(expected, x)\n\t\t}\n\t}\n\tsimple(strings.Join(items, \"\\n\"), \"2\", expected...)\n}\n"
  },
  {
    "path": "tools/tui/tmux.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/shirou/gopsutil/v4/process\"\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nvar TmuxExe = sync.OnceValue(func() string {\n\treturn utils.FindExe(\"tmux\")\n})\n\nfunc tmux_socket_address() (socket string) {\n\tsocket = os.Getenv(\"TMUX\")\n\tif socket == \"\" {\n\t\treturn \"\"\n\t}\n\taddr, pid_str, found := strings.Cut(socket, \",\")\n\tif !found {\n\t\treturn \"\"\n\t}\n\tif unix.Access(addr, unix.R_OK|unix.W_OK) != nil {\n\t\treturn \"\"\n\t}\n\tpid_str, _, _ = strings.Cut(pid_str, \",\")\n\tpid, err := strconv.ParseInt(pid_str, 10, 32)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tp, err := process.NewProcess(int32(pid))\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tcmd, err := p.CmdlineSlice()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tif len(cmd) > 0 && strings.ToLower(filepath.Base(cmd[0])) != \"tmux\" {\n\t\treturn \"\"\n\t}\n\treturn socket\n}\n\nvar TmuxSocketAddress = sync.OnceValue(tmux_socket_address)\n\nfunc tmux_command(args ...string) (c *exec.Cmd, stderr *strings.Builder) {\n\tc = exec.Command(TmuxExe(), args...)\n\tstderr = &strings.Builder{}\n\tc.Stderr = stderr\n\treturn c, stderr\n}\n\nfunc tmux_allow_passthrough() error {\n\tc, stderr := tmux_command(\"show\", \"-Ap\", \"allow-passthrough\")\n\tallowed, not_allowed := errors.New(\"allowed\"), errors.New(\"not allowed\")\n\tget_result := make(chan error)\n\tgo func() {\n\t\toutput, err := c.Output()\n\t\tif err != nil {\n\t\t\tget_result <- fmt.Errorf(\"Running %#v failed with error: %w. STDERR: %s\", c.Args, err, stderr.String())\n\t\t} else {\n\t\t\tq := strings.TrimSpace(utils.UnsafeBytesToString(output))\n\t\t\tif strings.HasSuffix(q, \" on\") || strings.HasSuffix(q, \" all\") {\n\t\t\t\tget_result <- allowed\n\t\t\t} else {\n\t\t\t\tget_result <- not_allowed\n\t\t\t}\n\t\t}\n\t}()\n\tselect {\n\tcase r := <-get_result:\n\t\tif r == allowed {\n\t\t\treturn nil\n\t\t}\n\t\tif r != not_allowed {\n\t\t\treturn r\n\t\t}\n\t\tc, stderr = tmux_command(\"set\", \"-p\", \"allow-passthrough\", \"on\")\n\t\terr := c.Run()\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"Running %#v failed with error: %w. STDERR: %s\", c.Args, err, stderr.String())\n\t\t}\n\t\treturn err\n\tcase <-time.After(2 * time.Second):\n\t\treturn fmt.Errorf(\"Tmux command timed out. This often happens when the version of tmux on your PATH is older than the version of the running tmux server\")\n\t}\n}\n\nvar TmuxAllowPassthrough = sync.OnceValue(tmux_allow_passthrough)\n"
  },
  {
    "path": "tools/tui/ui_kitten.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage tui\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/base85\"\n)\n\nvar _ = fmt.Print\n\nvar RunningAsUI = sync.OnceValue(func() bool {\n\tdefer func() { os.Unsetenv(\"KITTEN_RUNNING_AS_UI\") }()\n\treturn os.Getenv(\"KITTEN_RUNNING_AS_UI\") != \"\"\n})\n\ntype BasicColors struct {\n\tForeground uint32 `json:\"foreground\"`\n\tBackground uint32 `json:\"background\"`\n\tColor0     uint32 `json:\"color0\"`\n\tColor1     uint32 `json:\"color1\"`\n\tColor2     uint32 `json:\"color2\"`\n\tColor3     uint32 `json:\"color3\"`\n\tColor4     uint32 `json:\"color4\"`\n\tColor5     uint32 `json:\"color5\"`\n\tColor6     uint32 `json:\"color6\"`\n\tColor7     uint32 `json:\"color7\"`\n\tColor8     uint32 `json:\"color8\"`\n\tColor9     uint32 `json:\"color9\"`\n\tColor10    uint32 `json:\"color10\"`\n\tColor11    uint32 `json:\"color11\"`\n\tColor12    uint32 `json:\"color12\"`\n\tColor13    uint32 `json:\"color13\"`\n\tColor14    uint32 `json:\"color14\"`\n\tColor15    uint32 `json:\"color15\"`\n}\n\nfunc ReadBasicColors() (ans BasicColors, err error) {\n\tq := os.Getenv(\"KITTY_BASIC_COLORS\")\n\tif q == \"\" {\n\t\terr = fmt.Errorf(\"No KITTY_BASIC_COLORS env var\")\n\t} else {\n\t\terr = json.Unmarshal(utils.UnsafeStringToBytes(q), &ans)\n\t}\n\treturn\n}\n\nfunc PrepareRootCmd(root *cli.Command) {\n\tif RunningAsUI() {\n\t\troot.CallbackOnError = func(cmd *cli.Command, err error, during_parsing bool, exit_code int) int {\n\t\t\tcli.ShowError(err)\n\t\t\tos.Stdout.WriteString(\"\\x1bP@kitty-overlay-ready|\\x1b\\\\\")\n\t\t\tHoldTillEnter(true)\n\t\t\treturn exit_code\n\t\t}\n\t}\n}\n\nfunc KittenOutputSerializer() func(any) (string, error) {\n\tif RunningAsUI() {\n\t\treturn func(what any) (string, error) {\n\t\t\tdata, err := json.Marshal(what)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"\\x1bP@kitty-kitten-result|\" + base85.EncodeToString(data) + \"\\x1b\\\\\", nil\n\t\t}\n\t}\n\treturn func(what any) (string, error) {\n\t\tif sval, ok := what.(string); ok {\n\t\t\treturn sval, nil\n\t\t}\n\t\tdata, err := json.MarshalIndent(what, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn utils.UnsafeBytesToString(data), nil\n\t}\n}\n"
  },
  {
    "path": "tools/unicode_names/names.txt",
    "content": "50949 23472\n0 null\n1 start of heading\n2 start of text\n3 end of text\n4 end of transmission\n5 enquiry\n6 acknowledge\n7 bell\n8 backspace\n9 character tabulation\n10 line feed (lf)\n11 line tabulation\n12 form feed (ff)\n13 carriage return (cr)\n14 shift out\n15 shift in\n16 data link escape\n17 device control one\n18 device control two\n19 device control three\n20 device control four\n21 negative acknowledge\n22 synchronous idle\n23 end of transmission block\n24 cancel\n25 end of medium\n26 substitute\n27 escape\n28 information separator four\n29 information separator three\n30 information separator two\n31 information separator one\n32 space\n33 exclamation mark\texcl\n34 quotation mark\tquot\n35 number sign\tnum\n36 dollar sign\n37 percent sign\tpercnt\n38 ampersand\tamp\n39 apostrophe\tapos\n40 left parenthesis\tlpar\n41 right parenthesis\trpar\n42 asterisk\tast midast\n43 plus sign\n44 comma\n45 hyphen-minus\n46 full stop\tperiod\n47 solidus\tsol\n48 digit zero\n49 digit one\n50 digit two\n51 digit three\n52 digit four\n53 digit five\n54 digit six\n55 digit seven\n56 digit eight\n57 digit nine\n58 colon\n59 semicolon\tsemi\n60 less-than sign\tlt\n61 equals sign\n62 greater-than sign\tgt\n63 question mark\tquest\n64 commercial at\tcommat\n65 latin capital letter a\n66 latin capital letter b\n67 latin capital letter c\n68 latin capital letter d\n69 latin capital letter e\n70 latin capital letter f\n71 latin capital letter g\n72 latin capital letter h\n73 latin capital letter i\n74 latin capital letter j\n75 latin capital letter k\n76 latin capital letter l\n77 latin capital letter m\n78 latin capital letter n\n79 latin capital letter o\n80 latin capital letter p\n81 latin capital letter q\n82 latin capital letter r\n83 latin capital letter s\n84 latin capital letter t\n85 latin capital letter u\n86 latin capital letter v\n87 latin capital letter w\n88 latin capital letter x\n89 latin capital letter y\n90 latin capital letter z\n91 left square bracket\tlbrack lsqb\n92 reverse solidus\tbsol\n93 right square bracket\trbrack rsqb\n94 circumflex accent\that\n95 low line\tlowbar underbar\n96 grave accent\tdiacriticalgrave\n97 latin small letter a\n98 latin small letter b\n99 latin small letter c\n100 latin small letter d\n101 latin small letter e\n102 latin small letter f\n103 latin small letter g\n104 latin small letter h\n105 latin small letter i\n106 latin small letter j\n107 latin small letter k\n108 latin small letter l\n109 latin small letter m\n110 latin small letter n\n111 latin small letter o\n112 latin small letter p\n113 latin small letter q\n114 latin small letter r\n115 latin small letter s\n116 latin small letter t\n117 latin small letter u\n118 latin small letter v\n119 latin small letter w\n120 latin small letter x\n121 latin small letter y\n122 latin small letter z\n123 left curly bracket\tlbrace lcub\n124 vertical line\tverbar vert verticalline\n125 right curly bracket\trbrace rcub\n126 tilde\n127 delete\n130 break permitted here\n131 no break here\n133 next line (nel)\n134 start of selected area\n135 end of selected area\n136 character tabulation set\n137 character tabulation with justification\n138 line tabulation set\n139 partial line forward\n140 partial line backward\n141 reverse line feed\n142 single shift two\n143 single shift three\n144 device control string\n145 private use one\n146 private use two\n147 set transmit state\n148 cancel character\n149 message waiting\n150 start of guarded area\n151 end of guarded area\n152 start of string\n154 single character introducer\n155 control sequence introducer\n156 string terminator\n157 operating system command\n158 privacy message\n159 application program command\n160 no-break space\tnbsp nonbreakingspace\n161 inverted exclamation mark\tiexcl\n162 cent sign\n163 pound sign\n164 currency sign\tcurren\n165 yen sign\n166 broken bar\tbrvbar\n167 section sign\tsect\n168 diaeresis\tdie dot doubledot uml\n169 copyright sign\tcopy\n170 feminine ordinal indicator\tordf\n171 left-pointing double angle quotation mark\tlaquo\n172 not sign\n173 soft hyphen\tshy\n174 registered sign\tcircledr reg\n175 macron\tmacr strns\n176 degree sign\tdeg\n177 plus-minus sign\tplusminus plusmn pm\n178 superscript two\tsup2\n179 superscript three\tsup3\n180 acute accent\tdiacriticalacute\n181 micro sign\n182 pilcrow sign\tpara\n183 middle dot\tcenterdot middot\n184 cedilla\tcedil\n185 superscript one\tsup1\n186 masculine ordinal indicator\tordm\n187 right-pointing double angle quotation mark\traquo\n188 vulgar fraction one quarter\tfrac14\n189 vulgar fraction one half\tfrac12\n190 vulgar fraction three quarters\tfrac34\n191 inverted question mark\tiquest\n192 latin capital letter a with grave\tagrave\n193 latin capital letter a with acute\taacute\n194 latin capital letter a with circumflex\tacirc\n195 latin capital letter a with tilde\tatilde\n196 latin capital letter a with diaeresis\tauml\n197 latin capital letter a with ring above\tangst aring\n198 latin capital letter ae\taelig\n199 latin capital letter c with cedilla\tccedil\n200 latin capital letter e with grave\tegrave\n201 latin capital letter e with acute\teacute\n202 latin capital letter e with circumflex\tecirc\n203 latin capital letter e with diaeresis\teuml\n204 latin capital letter i with grave\tigrave\n205 latin capital letter i with acute\tiacute\n206 latin capital letter i with circumflex\ticirc\n207 latin capital letter i with diaeresis\tiuml\n208 latin capital letter eth\n209 latin capital letter n with tilde\tntilde\n210 latin capital letter o with grave\tograve\n211 latin capital letter o with acute\toacute\n212 latin capital letter o with circumflex\tocirc\n213 latin capital letter o with tilde\totilde\n214 latin capital letter o with diaeresis\touml\n215 multiplication sign\ttimes\n216 latin capital letter o with stroke\toslash\n217 latin capital letter u with grave\tugrave\n218 latin capital letter u with acute\tuacute\n219 latin capital letter u with circumflex\tucirc\n220 latin capital letter u with diaeresis\tuuml\n221 latin capital letter y with acute\tyacute\n222 latin capital letter thorn\n223 latin small letter sharp s\tszlig\n224 latin small letter a with grave\tagrave\n225 latin small letter a with acute\taacute\n226 latin small letter a with circumflex\tacirc\n227 latin small letter a with tilde\tatilde\n228 latin small letter a with diaeresis\tauml\n229 latin small letter a with ring above\taring\n230 latin small letter ae\taelig\n231 latin small letter c with cedilla\tccedil\n232 latin small letter e with grave\tegrave\n233 latin small letter e with acute\teacute\n234 latin small letter e with circumflex\tecirc\n235 latin small letter e with diaeresis\teuml\n236 latin small letter i with grave\tigrave\n237 latin small letter i with acute\tiacute\n238 latin small letter i with circumflex\ticirc\n239 latin small letter i with diaeresis\tiuml\n240 latin small letter eth\n241 latin small letter n with tilde\tntilde\n242 latin small letter o with grave\tograve\n243 latin small letter o with acute\toacute\n244 latin small letter o with circumflex\tocirc\n245 latin small letter o with tilde\totilde\n246 latin small letter o with diaeresis\touml\n247 division sign\tdiv divide\n248 latin small letter o with stroke\toslash\n249 latin small letter u with grave\tugrave\n250 latin small letter u with acute\tuacute\n251 latin small letter u with circumflex\tucirc\n252 latin small letter u with diaeresis\tuuml\n253 latin small letter y with acute\tyacute\n254 latin small letter thorn\n255 latin small letter y with diaeresis\tyuml\n256 latin capital letter a with macron\tamacr\n257 latin small letter a with macron\tamacr\n258 latin capital letter a with breve\tabreve\n259 latin small letter a with breve\tabreve\n260 latin capital letter a with ogonek\taogon\n261 latin small letter a with ogonek\taogon\n262 latin capital letter c with acute\tcacute\n263 latin small letter c with acute\tcacute\n264 latin capital letter c with circumflex\tccirc\n265 latin small letter c with circumflex\tccirc\n266 latin capital letter c with dot above\tcdot\n267 latin small letter c with dot above\tcdot\n268 latin capital letter c with caron\tccaron\n269 latin small letter c with caron\tccaron\n270 latin capital letter d with caron\tdcaron\n271 latin small letter d with caron\tdcaron\n272 latin capital letter d with stroke\tdstrok\n273 latin small letter d with stroke\tdstrok\n274 latin capital letter e with macron\temacr\n275 latin small letter e with macron\temacr\n276 latin capital letter e with breve\n277 latin small letter e with breve\n278 latin capital letter e with dot above\tedot\n279 latin small letter e with dot above\tedot\n280 latin capital letter e with ogonek\teogon\n281 latin small letter e with ogonek\teogon\n282 latin capital letter e with caron\tecaron\n283 latin small letter e with caron\tecaron\n284 latin capital letter g with circumflex\tgcirc\n285 latin small letter g with circumflex\tgcirc\n286 latin capital letter g with breve\tgbreve\n287 latin small letter g with breve\tgbreve\n288 latin capital letter g with dot above\tgdot\n289 latin small letter g with dot above\tgdot\n290 latin capital letter g with cedilla\tgcedil\n291 latin small letter g with cedilla\n292 latin capital letter h with circumflex\thcirc\n293 latin small letter h with circumflex\thcirc\n294 latin capital letter h with stroke\thstrok\n295 latin small letter h with stroke\thstrok\n296 latin capital letter i with tilde\titilde\n297 latin small letter i with tilde\titilde\n298 latin capital letter i with macron\timacr\n299 latin small letter i with macron\timacr\n300 latin capital letter i with breve\n301 latin small letter i with breve\n302 latin capital letter i with ogonek\tiogon\n303 latin small letter i with ogonek\tiogon\n304 latin capital letter i with dot above\tidot\n305 latin small letter dotless i\timath inodot\n306 latin capital ligature ij\tijlig\n307 latin small ligature ij\tijlig\n308 latin capital letter j with circumflex\tjcirc\n309 latin small letter j with circumflex\tjcirc\n310 latin capital letter k with cedilla\tkcedil\n311 latin small letter k with cedilla\tkcedil\n312 latin small letter kra\tkgreen\n313 latin capital letter l with acute\tlacute\n314 latin small letter l with acute\tlacute\n315 latin capital letter l with cedilla\tlcedil\n316 latin small letter l with cedilla\tlcedil\n317 latin capital letter l with caron\tlcaron\n318 latin small letter l with caron\tlcaron\n319 latin capital letter l with middle dot\tlmidot\n320 latin small letter l with middle dot\tlmidot\n321 latin capital letter l with stroke\tlstrok\n322 latin small letter l with stroke\tlstrok\n323 latin capital letter n with acute\tnacute\n324 latin small letter n with acute\tnacute\n325 latin capital letter n with cedilla\tncedil\n326 latin small letter n with cedilla\tncedil\n327 latin capital letter n with caron\tncaron\n328 latin small letter n with caron\tncaron\n329 latin small letter n preceded by apostrophe\tnapos\n330 latin capital letter eng\n331 latin small letter eng\n332 latin capital letter o with macron\tomacr\n333 latin small letter o with macron\tomacr\n334 latin capital letter o with breve\n335 latin small letter o with breve\n336 latin capital letter o with double acute\todblac\n337 latin small letter o with double acute\todblac\n338 latin capital ligature oe\toelig\n339 latin small ligature oe\toelig\n340 latin capital letter r with acute\tracute\n341 latin small letter r with acute\tracute\n342 latin capital letter r with cedilla\trcedil\n343 latin small letter r with cedilla\trcedil\n344 latin capital letter r with caron\trcaron\n345 latin small letter r with caron\trcaron\n346 latin capital letter s with acute\tsacute\n347 latin small letter s with acute\tsacute\n348 latin capital letter s with circumflex\tscirc\n349 latin small letter s with circumflex\tscirc\n350 latin capital letter s with cedilla\tscedil\n351 latin small letter s with cedilla\tscedil\n352 latin capital letter s with caron\tscaron\n353 latin small letter s with caron\tscaron\n354 latin capital letter t with cedilla\ttcedil\n355 latin small letter t with cedilla\ttcedil\n356 latin capital letter t with caron\ttcaron\n357 latin small letter t with caron\ttcaron\n358 latin capital letter t with stroke\ttstrok\n359 latin small letter t with stroke\ttstrok\n360 latin capital letter u with tilde\tutilde\n361 latin small letter u with tilde\tutilde\n362 latin capital letter u with macron\tumacr\n363 latin small letter u with macron\tumacr\n364 latin capital letter u with breve\tubreve\n365 latin small letter u with breve\tubreve\n366 latin capital letter u with ring above\turing\n367 latin small letter u with ring above\turing\n368 latin capital letter u with double acute\tudblac\n369 latin small letter u with double acute\tudblac\n370 latin capital letter u with ogonek\tuogon\n371 latin small letter u with ogonek\tuogon\n372 latin capital letter w with circumflex\twcirc\n373 latin small letter w with circumflex\twcirc\n374 latin capital letter y with circumflex\tycirc\n375 latin small letter y with circumflex\tycirc\n376 latin capital letter y with diaeresis\tyuml\n377 latin capital letter z with acute\tzacute\n378 latin small letter z with acute\tzacute\n379 latin capital letter z with dot above\tzdot\n380 latin small letter z with dot above\tzdot\n381 latin capital letter z with caron\tzcaron\n382 latin small letter z with caron\tzcaron\n383 latin small letter long s\n384 latin small letter b with stroke\n385 latin capital letter b with hook\n386 latin capital letter b with topbar\n387 latin small letter b with topbar\n388 latin capital letter tone six\n389 latin small letter tone six\n390 latin capital letter open o\n391 latin capital letter c with hook\n392 latin small letter c with hook\n393 latin capital letter african d\n394 latin capital letter d with hook\n395 latin capital letter d with topbar\n396 latin small letter d with topbar\n397 latin small letter turned delta\n398 latin capital letter reversed e\n399 latin capital letter schwa\n400 latin capital letter open e\n401 latin capital letter f with hook\n402 latin small letter f with hook\tfnof\n403 latin capital letter g with hook\n404 latin capital letter gamma\n405 latin small letter hv\n406 latin capital letter iota\n407 latin capital letter i with stroke\n408 latin capital letter k with hook\n409 latin small letter k with hook\n410 latin small letter l with bar\n411 latin small letter lambda with stroke\tlamda\n412 latin capital letter turned m\n413 latin capital letter n with left hook\n414 latin small letter n with long right leg\n415 latin capital letter o with middle tilde\n416 latin capital letter o with horn\n417 latin small letter o with horn\n418 latin capital letter oi\n419 latin small letter oi\n420 latin capital letter p with hook\n421 latin small letter p with hook\n422 latin letter yr\n423 latin capital letter tone two\n424 latin small letter tone two\n425 latin capital letter esh\n426 latin letter reversed esh loop\n427 latin small letter t with palatal hook\n428 latin capital letter t with hook\n429 latin small letter t with hook\n430 latin capital letter t with retroflex hook\n431 latin capital letter u with horn\n432 latin small letter u with horn\n433 latin capital letter upsilon\n434 latin capital letter v with hook\n435 latin capital letter y with hook\n436 latin small letter y with hook\n437 latin capital letter z with stroke\timped\n438 latin small letter z with stroke\n439 latin capital letter ezh\n440 latin capital letter ezh reversed\n441 latin small letter ezh reversed\n442 latin small letter ezh with tail\n443 latin letter two with stroke\n444 latin capital letter tone five\n445 latin small letter tone five\n446 latin letter inverted glottal stop with stroke\n447 latin letter wynn\n448 latin letter dental click\n449 latin letter lateral click\n450 latin letter alveolar click\n451 latin letter retroflex click\n452 latin capital letter dz with caron\n453 latin capital letter d with small letter z with caron\n454 latin small letter dz with caron\n455 latin capital letter lj\n456 latin capital letter l with small letter j\n457 latin small letter lj\n458 latin capital letter nj\n459 latin capital letter n with small letter j\n460 latin small letter nj\n461 latin capital letter a with caron\n462 latin small letter a with caron\n463 latin capital letter i with caron\n464 latin small letter i with caron\n465 latin capital letter o with caron\n466 latin small letter o with caron\n467 latin capital letter u with caron\n468 latin small letter u with caron\n469 latin capital letter u with diaeresis and macron\n470 latin small letter u with diaeresis and macron\n471 latin capital letter u with diaeresis and acute\n472 latin small letter u with diaeresis and acute\n473 latin capital letter u with diaeresis and caron\n474 latin small letter u with diaeresis and caron\n475 latin capital letter u with diaeresis and grave\n476 latin small letter u with diaeresis and grave\n477 latin small letter turned e\n478 latin capital letter a with diaeresis and macron\n479 latin small letter a with diaeresis and macron\n480 latin capital letter a with dot above and macron\n481 latin small letter a with dot above and macron\n482 latin capital letter ae with macron\n483 latin small letter ae with macron\n484 latin capital letter g with stroke\n485 latin small letter g with stroke\n486 latin capital letter g with caron\n487 latin small letter g with caron\n488 latin capital letter k with caron\n489 latin small letter k with caron\n490 latin capital letter o with ogonek\n491 latin small letter o with ogonek\n492 latin capital letter o with ogonek and macron\n493 latin small letter o with ogonek and macron\n494 latin capital letter ezh with caron\n495 latin small letter ezh with caron\n496 latin small letter j with caron\n497 latin capital letter dz\n498 latin capital letter d with small letter z\n499 latin small letter dz\n500 latin capital letter g with acute\n501 latin small letter g with acute\tgacute\n502 latin capital letter hwair\n503 latin capital letter wynn\n504 latin capital letter n with grave\n505 latin small letter n with grave\n506 latin capital letter a with ring above and acute\n507 latin small letter a with ring above and acute\n508 latin capital letter ae with acute\n509 latin small letter ae with acute\n510 latin capital letter o with stroke and acute\n511 latin small letter o with stroke and acute\n512 latin capital letter a with double grave\n513 latin small letter a with double grave\n514 latin capital letter a with inverted breve\n515 latin small letter a with inverted breve\n516 latin capital letter e with double grave\n517 latin small letter e with double grave\n518 latin capital letter e with inverted breve\n519 latin small letter e with inverted breve\n520 latin capital letter i with double grave\n521 latin small letter i with double grave\n522 latin capital letter i with inverted breve\n523 latin small letter i with inverted breve\n524 latin capital letter o with double grave\n525 latin small letter o with double grave\n526 latin capital letter o with inverted breve\n527 latin small letter o with inverted breve\n528 latin capital letter r with double grave\n529 latin small letter r with double grave\n530 latin capital letter r with inverted breve\n531 latin small letter r with inverted breve\n532 latin capital letter u with double grave\n533 latin small letter u with double grave\n534 latin capital letter u with inverted breve\n535 latin small letter u with inverted breve\n536 latin capital letter s with comma below\n537 latin small letter s with comma below\n538 latin capital letter t with comma below\n539 latin small letter t with comma below\n540 latin capital letter yogh\n541 latin small letter yogh\n542 latin capital letter h with caron\n543 latin small letter h with caron\n544 latin capital letter n with long right leg\n545 latin small letter d with curl\n546 latin capital letter ou\n547 latin small letter ou\n548 latin capital letter z with hook\n549 latin small letter z with hook\n550 latin capital letter a with dot above\n551 latin small letter a with dot above\n552 latin capital letter e with cedilla\n553 latin small letter e with cedilla\n554 latin capital letter o with diaeresis and macron\n555 latin small letter o with diaeresis and macron\n556 latin capital letter o with tilde and macron\n557 latin small letter o with tilde and macron\n558 latin capital letter o with dot above\n559 latin small letter o with dot above\n560 latin capital letter o with dot above and macron\n561 latin small letter o with dot above and macron\n562 latin capital letter y with macron\n563 latin small letter y with macron\n564 latin small letter l with curl\n565 latin small letter n with curl\n566 latin small letter t with curl\n567 latin small letter dotless j\tjmath\n568 latin small letter db digraph\n569 latin small letter qp digraph\n570 latin capital letter a with stroke\n571 latin capital letter c with stroke\n572 latin small letter c with stroke\n573 latin capital letter l with bar\n574 latin capital letter t with diagonal stroke\n575 latin small letter s with swash tail\n576 latin small letter z with swash tail\n577 latin capital letter glottal stop\n578 latin small letter glottal stop\n579 latin capital letter b with stroke\n580 latin capital letter u bar\n581 latin capital letter turned v\n582 latin capital letter e with stroke\n583 latin small letter e with stroke\n584 latin capital letter j with stroke\n585 latin small letter j with stroke\n586 latin capital letter small q with hook tail\n587 latin small letter q with hook tail\n588 latin capital letter r with stroke\n589 latin small letter r with stroke\n590 latin capital letter y with stroke\n591 latin small letter y with stroke\n592 latin small letter turned a\n593 latin small letter alpha\n594 latin small letter turned alpha\n595 latin small letter b with hook\n596 latin small letter open o\n597 latin small letter c with curl\n598 latin small letter d with tail\n599 latin small letter d with hook\n600 latin small letter reversed e\n601 latin small letter schwa\n602 latin small letter schwa with hook\n603 latin small letter open e\n604 latin small letter reversed open e\n605 latin small letter reversed open e with hook\n606 latin small letter closed reversed open e\n607 latin small letter dotless j with stroke\n608 latin small letter g with hook\n609 latin small letter script g\n610 latin letter small capital g\n611 latin small letter gamma\n612 latin small letter rams horn\n613 latin small letter turned h\n614 latin small letter h with hook\n615 latin small letter heng with hook\n616 latin small letter i with stroke\n617 latin small letter iota\n618 latin letter small capital i\n619 latin small letter l with middle tilde\n620 latin small letter l with belt\n621 latin small letter l with retroflex hook\n622 latin small letter lezh\n623 latin small letter turned m\n624 latin small letter turned m with long leg\n625 latin small letter m with hook\n626 latin small letter n with left hook\n627 latin small letter n with retroflex hook\n628 latin letter small capital n\n629 latin small letter barred o\n630 latin letter small capital oe\n631 latin small letter closed omega\n632 latin small letter phi\n633 latin small letter turned r\n634 latin small letter turned r with long leg\n635 latin small letter turned r with hook\n636 latin small letter r with long leg\n637 latin small letter r with tail\n638 latin small letter r with fishhook\n639 latin small letter reversed r with fishhook\n640 latin letter small capital r\n641 latin letter small capital inverted r\n642 latin small letter s with hook\n643 latin small letter esh\n644 latin small letter dotless j with stroke and hook\n645 latin small letter squat reversed esh\n646 latin small letter esh with curl\n647 latin small letter turned t\n648 latin small letter t with retroflex hook\n649 latin small letter u bar\n650 latin small letter upsilon\n651 latin small letter v with hook\n652 latin small letter turned v\n653 latin small letter turned w\n654 latin small letter turned y\n655 latin letter small capital y\n656 latin small letter z with retroflex hook\n657 latin small letter z with curl\n658 latin small letter ezh\n659 latin small letter ezh with curl\n660 latin letter glottal stop\n661 latin letter pharyngeal voiced fricative\n662 latin letter inverted glottal stop\n663 latin letter stretched c\n664 latin letter bilabial click\n665 latin letter small capital b\n666 latin small letter closed open e\n667 latin letter small capital g with hook\n668 latin letter small capital h\n669 latin small letter j with crossed-tail\n670 latin small letter turned k\n671 latin letter small capital l\n672 latin small letter q with hook\n673 latin letter glottal stop with stroke\n674 latin letter reversed glottal stop with stroke\n675 latin small letter dz digraph\n676 latin small letter dezh digraph\n677 latin small letter dz digraph with curl\n678 latin small letter ts digraph\n679 latin small letter tesh digraph\n680 latin small letter tc digraph with curl\n681 latin small letter feng digraph\n682 latin small letter ls digraph\n683 latin small letter lz digraph\n684 latin letter bilabial percussive\n685 latin letter bidental percussive\n686 latin small letter turned h with fishhook\n687 latin small letter turned h with fishhook and tail\n688 modifier letter small h\n689 modifier letter small h with hook\n690 modifier letter small j\n691 modifier letter small r\n692 modifier letter small turned r\n693 modifier letter small turned r with hook\n694 modifier letter small capital inverted r\n695 modifier letter small w\n696 modifier letter small y\n697 modifier letter prime\n698 modifier letter double prime\n699 modifier letter turned comma\n700 modifier letter apostrophe\n701 modifier letter reversed comma\n702 modifier letter right half ring\n703 modifier letter left half ring\n704 modifier letter glottal stop\n705 modifier letter reversed glottal stop\n706 modifier letter left arrowhead\n707 modifier letter right arrowhead\n708 modifier letter up arrowhead\n709 modifier letter down arrowhead\n710 modifier letter circumflex accent\tcirc\n711 caron\thacek\n712 modifier letter vertical line\n713 modifier letter macron\n714 modifier letter acute accent\n715 modifier letter grave accent\n716 modifier letter low vertical line\n717 modifier letter low macron\n718 modifier letter low grave accent\n719 modifier letter low acute accent\n720 modifier letter triangular colon\n721 modifier letter half triangular colon\n722 modifier letter centred right half ring\n723 modifier letter centred left half ring\n724 modifier letter up tack\n725 modifier letter down tack\n726 modifier letter plus sign\n727 modifier letter minus sign\n728 breve\n729 dot above\tdiacriticaldot\n730 ring above\n731 ogonek\togon\n732 small tilde\tdiacriticaltilde\n733 double acute accent\tdblac diacriticaldoubleacute\n734 modifier letter rhotic hook\n735 modifier letter cross accent\n736 modifier letter small gamma\n737 modifier letter small l\n738 modifier letter small s\n739 modifier letter small x\n740 modifier letter small reversed glottal stop\n741 modifier letter extra-high tone bar\n742 modifier letter high tone bar\n743 modifier letter mid tone bar\n744 modifier letter low tone bar\n745 modifier letter extra-low tone bar\n746 modifier letter yin departing tone mark\n747 modifier letter yang departing tone mark\n748 modifier letter voicing\n749 modifier letter unaspirated\n750 modifier letter double apostrophe\n751 modifier letter low down arrowhead\n752 modifier letter low up arrowhead\n753 modifier letter low left arrowhead\n754 modifier letter low right arrowhead\n755 modifier letter low ring\n756 modifier letter middle grave accent\n757 modifier letter middle double grave accent\n758 modifier letter middle double acute accent\n759 modifier letter low tilde\n760 modifier letter raised colon\n761 modifier letter begin high tone\n762 modifier letter end high tone\n763 modifier letter begin low tone\n764 modifier letter end low tone\n765 modifier letter shelf\n766 modifier letter open shelf\n767 modifier letter low left arrow\n768 combining grave accent\n769 combining acute accent\n770 combining circumflex accent\n771 combining tilde\n772 combining macron\n773 combining overline\n774 combining breve\n775 combining dot above\n776 combining diaeresis\n777 combining hook above\n778 combining ring above\n779 combining double acute accent\n780 combining caron\n781 combining vertical line above\n782 combining double vertical line above\n783 combining double grave accent\n784 combining candrabindu\n785 combining inverted breve\tdownbreve\n786 combining turned comma above\n787 combining comma above\n788 combining reversed comma above\n789 combining comma above right\n790 combining grave accent below\n791 combining acute accent below\n792 combining left tack below\n793 combining right tack below\n794 combining left angle above\n795 combining horn\n796 combining left half ring below\n797 combining up tack below\n798 combining down tack below\n799 combining plus sign below\n800 combining minus sign below\n801 combining palatalized hook below\n802 combining retroflex hook below\n803 combining dot below\n804 combining diaeresis below\n805 combining ring below\n806 combining comma below\n807 combining cedilla\n808 combining ogonek\n809 combining vertical line below\n810 combining bridge below\n811 combining inverted double arch below\n812 combining caron below\n813 combining circumflex accent below\n814 combining breve below\n815 combining inverted breve below\n816 combining tilde below\n817 combining macron below\n818 combining low line\n819 combining double low line\n820 combining tilde overlay\n821 combining short stroke overlay\n822 combining long stroke overlay\n823 combining short solidus overlay\n824 combining long solidus overlay\n825 combining right half ring below\n826 combining inverted bridge below\n827 combining square below\n828 combining seagull below\n829 combining x above\n830 combining vertical tilde\n831 combining double overline\n832 combining grave tone mark\n833 combining acute tone mark\n834 combining greek perispomeni\n835 combining greek koronis\n836 combining greek dialytika tonos\n837 combining greek ypogegrammeni\n838 combining bridge above\n839 combining equals sign below\n840 combining double vertical line below\n841 combining left angle below\n842 combining not tilde above\n843 combining homothetic above\n844 combining almost equal to above\n845 combining left right arrow below\n846 combining upwards arrow below\n847 combining grapheme joiner\n848 combining right arrowhead above\n849 combining left half ring above\n850 combining fermata\n851 combining x below\n852 combining left arrowhead below\n853 combining right arrowhead below\n854 combining right arrowhead and up arrowhead below\n855 combining right half ring above\n856 combining dot above right\n857 combining asterisk below\n858 combining double ring below\n859 combining zigzag above\n860 combining double breve below\n861 combining double breve\n862 combining double macron\n863 combining double macron below\n864 combining double tilde\n865 combining double inverted breve\n866 combining double rightwards arrow below\n867 combining latin small letter a\n868 combining latin small letter e\n869 combining latin small letter i\n870 combining latin small letter o\n871 combining latin small letter u\n872 combining latin small letter c\n873 combining latin small letter d\n874 combining latin small letter h\n875 combining latin small letter m\n876 combining latin small letter r\n877 combining latin small letter t\n878 combining latin small letter v\n879 combining latin small letter x\n880 greek capital letter heta\n881 greek small letter heta\n882 greek capital letter archaic sampi\n883 greek small letter archaic sampi\n884 greek numeral sign\n885 greek lower numeral sign\n886 greek capital letter pamphylian digamma\n887 greek small letter pamphylian digamma\n890 greek ypogegrammeni\n891 greek small reversed lunate sigma symbol\n892 greek small dotted lunate sigma symbol\n893 greek small reversed dotted lunate sigma symbol\n894 greek question mark\n895 greek capital letter yot\n900 greek tonos\n901 greek dialytika tonos\n902 greek capital letter alpha with tonos\n903 greek ano teleia\n904 greek capital letter epsilon with tonos\n905 greek capital letter eta with tonos\n906 greek capital letter iota with tonos\n908 greek capital letter omicron with tonos\n910 greek capital letter upsilon with tonos\n911 greek capital letter omega with tonos\n912 greek small letter iota with dialytika and tonos\n913 greek capital letter alpha\n914 greek capital letter beta\n915 greek capital letter gamma\n916 greek capital letter delta\n917 greek capital letter epsilon\n918 greek capital letter zeta\n919 greek capital letter eta\n920 greek capital letter theta\n921 greek capital letter iota\n922 greek capital letter kappa\n923 greek capital letter lamda\tlambda\n924 greek capital letter mu\n925 greek capital letter nu\n926 greek capital letter xi\n927 greek capital letter omicron\n928 greek capital letter pi\n929 greek capital letter rho\n931 greek capital letter sigma\n932 greek capital letter tau\n933 greek capital letter upsilon\n934 greek capital letter phi\n935 greek capital letter chi\n936 greek capital letter psi\n937 greek capital letter omega\tohm\n938 greek capital letter iota with dialytika\n939 greek capital letter upsilon with dialytika\n940 greek small letter alpha with tonos\n941 greek small letter epsilon with tonos\n942 greek small letter eta with tonos\n943 greek small letter iota with tonos\n944 greek small letter upsilon with dialytika and tonos\n945 greek small letter alpha\n946 greek small letter beta\n947 greek small letter gamma\n948 greek small letter delta\n949 greek small letter epsilon\tepsi\n950 greek small letter zeta\n951 greek small letter eta\n952 greek small letter theta\n953 greek small letter iota\n954 greek small letter kappa\n955 greek small letter lamda\tlambda\n956 greek small letter mu\n957 greek small letter nu\n958 greek small letter xi\n959 greek small letter omicron\n960 greek small letter pi\n961 greek small letter rho\n962 greek small letter final sigma\tsigmaf sigmav varsigma\n963 greek small letter sigma\n964 greek small letter tau\n965 greek small letter upsilon\tupsi\n966 greek small letter phi\n967 greek small letter chi\n968 greek small letter psi\n969 greek small letter omega\n970 greek small letter iota with dialytika\n971 greek small letter upsilon with dialytika\n972 greek small letter omicron with tonos\n973 greek small letter upsilon with tonos\n974 greek small letter omega with tonos\n975 greek capital kai symbol\n976 greek beta symbol\n977 greek theta symbol\tthetasym thetav vartheta\n978 greek upsilon with hook symbol\tupsi upsih\n979 greek upsilon with acute and hook symbol\n980 greek upsilon with diaeresis and hook symbol\n981 greek phi symbol\tphiv straightphi varphi\n982 greek pi symbol\tpiv varpi\n983 greek kai symbol\n984 greek letter archaic koppa\n985 greek small letter archaic koppa\n986 greek letter stigma\n987 greek small letter stigma\n988 greek letter digamma\tgammad\n989 greek small letter digamma\tgammad\n990 greek letter koppa\n991 greek small letter koppa\n992 greek letter sampi\n993 greek small letter sampi\n994 coptic capital letter shei\n995 coptic small letter shei\n996 coptic capital letter fei\n997 coptic small letter fei\n998 coptic capital letter khei\n999 coptic small letter khei\n1000 coptic capital letter hori\n1001 coptic small letter hori\n1002 coptic capital letter gangia\n1003 coptic small letter gangia\n1004 coptic capital letter shima\n1005 coptic small letter shima\n1006 coptic capital letter dei\n1007 coptic small letter dei\n1008 greek kappa symbol\tkappav varkappa\n1009 greek rho symbol\trhov varrho\n1010 greek lunate sigma symbol\n1011 greek letter yot\n1012 greek capital theta symbol\n1013 greek lunate epsilon symbol\tepsiv straightepsilon varepsilon\n1014 greek reversed lunate epsilon symbol\tbackepsilon bepsi\n1015 greek capital letter sho\n1016 greek small letter sho\n1017 greek capital lunate sigma symbol\n1018 greek capital letter san\n1019 greek small letter san\n1020 greek rho with stroke symbol\n1021 greek capital reversed lunate sigma symbol\n1022 greek capital dotted lunate sigma symbol\n1023 greek capital reversed dotted lunate sigma symbol\n1024 cyrillic capital letter ie with grave\n1025 cyrillic capital letter io\tiocy\n1026 cyrillic capital letter dje\tdjcy\n1027 cyrillic capital letter gje\tgjcy\n1028 cyrillic capital letter ukrainian ie\tjukcy\n1029 cyrillic capital letter dze\tdscy\n1030 cyrillic capital letter byelorussian-ukrainian i\tiukcy\n1031 cyrillic capital letter yi\tyicy\n1032 cyrillic capital letter je\tjsercy\n1033 cyrillic capital letter lje\tljcy\n1034 cyrillic capital letter nje\tnjcy\n1035 cyrillic capital letter tshe\ttshcy\n1036 cyrillic capital letter kje\tkjcy\n1037 cyrillic capital letter i with grave\n1038 cyrillic capital letter short u\tubrcy\n1039 cyrillic capital letter dzhe\tdzcy\n1040 cyrillic capital letter a\tacy\n1041 cyrillic capital letter be\tbcy\n1042 cyrillic capital letter ve\tvcy\n1043 cyrillic capital letter ghe\tgcy\n1044 cyrillic capital letter de\tdcy\n1045 cyrillic capital letter ie\tiecy\n1046 cyrillic capital letter zhe\tzhcy\n1047 cyrillic capital letter ze\tzcy\n1048 cyrillic capital letter i\ticy\n1049 cyrillic capital letter short i\tjcy\n1050 cyrillic capital letter ka\tkcy\n1051 cyrillic capital letter el\tlcy\n1052 cyrillic capital letter em\tmcy\n1053 cyrillic capital letter en\tncy\n1054 cyrillic capital letter o\tocy\n1055 cyrillic capital letter pe\tpcy\n1056 cyrillic capital letter er\trcy\n1057 cyrillic capital letter es\tscy\n1058 cyrillic capital letter te\ttcy\n1059 cyrillic capital letter u\tucy\n1060 cyrillic capital letter ef\tfcy\n1061 cyrillic capital letter ha\tkhcy\n1062 cyrillic capital letter tse\ttscy\n1063 cyrillic capital letter che\tchcy\n1064 cyrillic capital letter sha\tshcy\n1065 cyrillic capital letter shcha\tshchcy\n1066 cyrillic capital letter hard sign\thardcy\n1067 cyrillic capital letter yeru\tycy\n1068 cyrillic capital letter soft sign\tsoftcy\n1069 cyrillic capital letter e\tecy\n1070 cyrillic capital letter yu\tyucy\n1071 cyrillic capital letter ya\tyacy\n1072 cyrillic small letter a\tacy\n1073 cyrillic small letter be\tbcy\n1074 cyrillic small letter ve\tvcy\n1075 cyrillic small letter ghe\tgcy\n1076 cyrillic small letter de\tdcy\n1077 cyrillic small letter ie\tiecy\n1078 cyrillic small letter zhe\tzhcy\n1079 cyrillic small letter ze\tzcy\n1080 cyrillic small letter i\ticy\n1081 cyrillic small letter short i\tjcy\n1082 cyrillic small letter ka\tkcy\n1083 cyrillic small letter el\tlcy\n1084 cyrillic small letter em\tmcy\n1085 cyrillic small letter en\tncy\n1086 cyrillic small letter o\tocy\n1087 cyrillic small letter pe\tpcy\n1088 cyrillic small letter er\trcy\n1089 cyrillic small letter es\tscy\n1090 cyrillic small letter te\ttcy\n1091 cyrillic small letter u\tucy\n1092 cyrillic small letter ef\tfcy\n1093 cyrillic small letter ha\tkhcy\n1094 cyrillic small letter tse\ttscy\n1095 cyrillic small letter che\tchcy\n1096 cyrillic small letter sha\tshcy\n1097 cyrillic small letter shcha\tshchcy\n1098 cyrillic small letter hard sign\thardcy\n1099 cyrillic small letter yeru\tycy\n1100 cyrillic small letter soft sign\tsoftcy\n1101 cyrillic small letter e\tecy\n1102 cyrillic small letter yu\tyucy\n1103 cyrillic small letter ya\tyacy\n1104 cyrillic small letter ie with grave\n1105 cyrillic small letter io\tiocy\n1106 cyrillic small letter dje\tdjcy\n1107 cyrillic small letter gje\tgjcy\n1108 cyrillic small letter ukrainian ie\tjukcy\n1109 cyrillic small letter dze\tdscy\n1110 cyrillic small letter byelorussian-ukrainian i\tiukcy\n1111 cyrillic small letter yi\tyicy\n1112 cyrillic small letter je\tjsercy\n1113 cyrillic small letter lje\tljcy\n1114 cyrillic small letter nje\tnjcy\n1115 cyrillic small letter tshe\ttshcy\n1116 cyrillic small letter kje\tkjcy\n1117 cyrillic small letter i with grave\n1118 cyrillic small letter short u\tubrcy\n1119 cyrillic small letter dzhe\tdzcy\n1120 cyrillic capital letter omega\n1121 cyrillic small letter omega\n1122 cyrillic capital letter yat\n1123 cyrillic small letter yat\n1124 cyrillic capital letter iotified e\n1125 cyrillic small letter iotified e\n1126 cyrillic capital letter little yus\n1127 cyrillic small letter little yus\n1128 cyrillic capital letter iotified little yus\n1129 cyrillic small letter iotified little yus\n1130 cyrillic capital letter big yus\n1131 cyrillic small letter big yus\n1132 cyrillic capital letter iotified big yus\n1133 cyrillic small letter iotified big yus\n1134 cyrillic capital letter ksi\n1135 cyrillic small letter ksi\n1136 cyrillic capital letter psi\n1137 cyrillic small letter psi\n1138 cyrillic capital letter fita\n1139 cyrillic small letter fita\n1140 cyrillic capital letter izhitsa\n1141 cyrillic small letter izhitsa\n1142 cyrillic capital letter izhitsa with double grave accent\n1143 cyrillic small letter izhitsa with double grave accent\n1144 cyrillic capital letter uk\n1145 cyrillic small letter uk\n1146 cyrillic capital letter round omega\n1147 cyrillic small letter round omega\n1148 cyrillic capital letter omega with titlo\n1149 cyrillic small letter omega with titlo\n1150 cyrillic capital letter ot\n1151 cyrillic small letter ot\n1152 cyrillic capital letter koppa\n1153 cyrillic small letter koppa\n1154 cyrillic thousands sign\n1155 combining cyrillic titlo\n1156 combining cyrillic palatalization\n1157 combining cyrillic dasia pneumata\n1158 combining cyrillic psili pneumata\n1159 combining cyrillic pokrytie\n1160 combining cyrillic hundred thousands sign\n1161 combining cyrillic millions sign\n1162 cyrillic capital letter short i with tail\n1163 cyrillic small letter short i with tail\n1164 cyrillic capital letter semisoft sign\n1165 cyrillic small letter semisoft sign\n1166 cyrillic capital letter er with tick\n1167 cyrillic small letter er with tick\n1168 cyrillic capital letter ghe with upturn\n1169 cyrillic small letter ghe with upturn\n1170 cyrillic capital letter ghe with stroke\n1171 cyrillic small letter ghe with stroke\n1172 cyrillic capital letter ghe with middle hook\n1173 cyrillic small letter ghe with middle hook\n1174 cyrillic capital letter zhe with descender\n1175 cyrillic small letter zhe with descender\n1176 cyrillic capital letter ze with descender\n1177 cyrillic small letter ze with descender\n1178 cyrillic capital letter ka with descender\n1179 cyrillic small letter ka with descender\n1180 cyrillic capital letter ka with vertical stroke\n1181 cyrillic small letter ka with vertical stroke\n1182 cyrillic capital letter ka with stroke\n1183 cyrillic small letter ka with stroke\n1184 cyrillic capital letter bashkir ka\n1185 cyrillic small letter bashkir ka\n1186 cyrillic capital letter en with descender\n1187 cyrillic small letter en with descender\n1188 cyrillic capital ligature en ghe\n1189 cyrillic small ligature en ghe\n1190 cyrillic capital letter pe with middle hook\n1191 cyrillic small letter pe with middle hook\n1192 cyrillic capital letter abkhasian ha\n1193 cyrillic small letter abkhasian ha\n1194 cyrillic capital letter es with descender\n1195 cyrillic small letter es with descender\n1196 cyrillic capital letter te with descender\n1197 cyrillic small letter te with descender\n1198 cyrillic capital letter straight u\n1199 cyrillic small letter straight u\n1200 cyrillic capital letter straight u with stroke\n1201 cyrillic small letter straight u with stroke\n1202 cyrillic capital letter ha with descender\n1203 cyrillic small letter ha with descender\n1204 cyrillic capital ligature te tse\n1205 cyrillic small ligature te tse\n1206 cyrillic capital letter che with descender\n1207 cyrillic small letter che with descender\n1208 cyrillic capital letter che with vertical stroke\n1209 cyrillic small letter che with vertical stroke\n1210 cyrillic capital letter shha\n1211 cyrillic small letter shha\n1212 cyrillic capital letter abkhasian che\n1213 cyrillic small letter abkhasian che\n1214 cyrillic capital letter abkhasian che with descender\n1215 cyrillic small letter abkhasian che with descender\n1216 cyrillic letter palochka\n1217 cyrillic capital letter zhe with breve\n1218 cyrillic small letter zhe with breve\n1219 cyrillic capital letter ka with hook\n1220 cyrillic small letter ka with hook\n1221 cyrillic capital letter el with tail\n1222 cyrillic small letter el with tail\n1223 cyrillic capital letter en with hook\n1224 cyrillic small letter en with hook\n1225 cyrillic capital letter en with tail\n1226 cyrillic small letter en with tail\n1227 cyrillic capital letter khakassian che\n1228 cyrillic small letter khakassian che\n1229 cyrillic capital letter em with tail\n1230 cyrillic small letter em with tail\n1231 cyrillic small letter palochka\n1232 cyrillic capital letter a with breve\n1233 cyrillic small letter a with breve\n1234 cyrillic capital letter a with diaeresis\n1235 cyrillic small letter a with diaeresis\n1236 cyrillic capital ligature a ie\n1237 cyrillic small ligature a ie\n1238 cyrillic capital letter ie with breve\n1239 cyrillic small letter ie with breve\n1240 cyrillic capital letter schwa\n1241 cyrillic small letter schwa\n1242 cyrillic capital letter schwa with diaeresis\n1243 cyrillic small letter schwa with diaeresis\n1244 cyrillic capital letter zhe with diaeresis\n1245 cyrillic small letter zhe with diaeresis\n1246 cyrillic capital letter ze with diaeresis\n1247 cyrillic small letter ze with diaeresis\n1248 cyrillic capital letter abkhasian dze\n1249 cyrillic small letter abkhasian dze\n1250 cyrillic capital letter i with macron\n1251 cyrillic small letter i with macron\n1252 cyrillic capital letter i with diaeresis\n1253 cyrillic small letter i with diaeresis\n1254 cyrillic capital letter o with diaeresis\n1255 cyrillic small letter o with diaeresis\n1256 cyrillic capital letter barred o\n1257 cyrillic small letter barred o\n1258 cyrillic capital letter barred o with diaeresis\n1259 cyrillic small letter barred o with diaeresis\n1260 cyrillic capital letter e with diaeresis\n1261 cyrillic small letter e with diaeresis\n1262 cyrillic capital letter u with macron\n1263 cyrillic small letter u with macron\n1264 cyrillic capital letter u with diaeresis\n1265 cyrillic small letter u with diaeresis\n1266 cyrillic capital letter u with double acute\n1267 cyrillic small letter u with double acute\n1268 cyrillic capital letter che with diaeresis\n1269 cyrillic small letter che with diaeresis\n1270 cyrillic capital letter ghe with descender\n1271 cyrillic small letter ghe with descender\n1272 cyrillic capital letter yeru with diaeresis\n1273 cyrillic small letter yeru with diaeresis\n1274 cyrillic capital letter ghe with stroke and hook\n1275 cyrillic small letter ghe with stroke and hook\n1276 cyrillic capital letter ha with hook\n1277 cyrillic small letter ha with hook\n1278 cyrillic capital letter ha with stroke\n1279 cyrillic small letter ha with stroke\n1280 cyrillic capital letter komi de\n1281 cyrillic small letter komi de\n1282 cyrillic capital letter komi dje\n1283 cyrillic small letter komi dje\n1284 cyrillic capital letter komi zje\n1285 cyrillic small letter komi zje\n1286 cyrillic capital letter komi dzje\n1287 cyrillic small letter komi dzje\n1288 cyrillic capital letter komi lje\n1289 cyrillic small letter komi lje\n1290 cyrillic capital letter komi nje\n1291 cyrillic small letter komi nje\n1292 cyrillic capital letter komi sje\n1293 cyrillic small letter komi sje\n1294 cyrillic capital letter komi tje\n1295 cyrillic small letter komi tje\n1296 cyrillic capital letter reversed ze\n1297 cyrillic small letter reversed ze\n1298 cyrillic capital letter el with hook\n1299 cyrillic small letter el with hook\n1300 cyrillic capital letter lha\n1301 cyrillic small letter lha\n1302 cyrillic capital letter rha\n1303 cyrillic small letter rha\n1304 cyrillic capital letter yae\n1305 cyrillic small letter yae\n1306 cyrillic capital letter qa\n1307 cyrillic small letter qa\n1308 cyrillic capital letter we\n1309 cyrillic small letter we\n1310 cyrillic capital letter aleut ka\n1311 cyrillic small letter aleut ka\n1312 cyrillic capital letter el with middle hook\n1313 cyrillic small letter el with middle hook\n1314 cyrillic capital letter en with middle hook\n1315 cyrillic small letter en with middle hook\n1316 cyrillic capital letter pe with descender\n1317 cyrillic small letter pe with descender\n1318 cyrillic capital letter shha with descender\n1319 cyrillic small letter shha with descender\n1320 cyrillic capital letter en with left hook\n1321 cyrillic small letter en with left hook\n1322 cyrillic capital letter dzzhe\n1323 cyrillic small letter dzzhe\n1324 cyrillic capital letter dche\n1325 cyrillic small letter dche\n1326 cyrillic capital letter el with descender\n1327 cyrillic small letter el with descender\n1329 armenian capital letter ayb\n1330 armenian capital letter ben\n1331 armenian capital letter gim\n1332 armenian capital letter da\n1333 armenian capital letter ech\n1334 armenian capital letter za\n1335 armenian capital letter eh\n1336 armenian capital letter et\n1337 armenian capital letter to\n1338 armenian capital letter zhe\n1339 armenian capital letter ini\n1340 armenian capital letter liwn\n1341 armenian capital letter xeh\n1342 armenian capital letter ca\n1343 armenian capital letter ken\n1344 armenian capital letter ho\n1345 armenian capital letter ja\n1346 armenian capital letter ghad\n1347 armenian capital letter cheh\n1348 armenian capital letter men\n1349 armenian capital letter yi\n1350 armenian capital letter now\n1351 armenian capital letter sha\n1352 armenian capital letter vo\n1353 armenian capital letter cha\n1354 armenian capital letter peh\n1355 armenian capital letter jheh\n1356 armenian capital letter ra\n1357 armenian capital letter seh\n1358 armenian capital letter vew\n1359 armenian capital letter tiwn\n1360 armenian capital letter reh\n1361 armenian capital letter co\n1362 armenian capital letter yiwn\n1363 armenian capital letter piwr\n1364 armenian capital letter keh\n1365 armenian capital letter oh\n1366 armenian capital letter feh\n1369 armenian modifier letter left half ring\n1370 armenian apostrophe\n1371 armenian emphasis mark\n1372 armenian exclamation mark\n1373 armenian comma\n1374 armenian question mark\n1375 armenian abbreviation mark\n1376 armenian small letter turned ayb\n1377 armenian small letter ayb\n1378 armenian small letter ben\n1379 armenian small letter gim\n1380 armenian small letter da\n1381 armenian small letter ech\n1382 armenian small letter za\n1383 armenian small letter eh\n1384 armenian small letter et\n1385 armenian small letter to\n1386 armenian small letter zhe\n1387 armenian small letter ini\n1388 armenian small letter liwn\n1389 armenian small letter xeh\n1390 armenian small letter ca\n1391 armenian small letter ken\n1392 armenian small letter ho\n1393 armenian small letter ja\n1394 armenian small letter ghad\n1395 armenian small letter cheh\n1396 armenian small letter men\n1397 armenian small letter yi\n1398 armenian small letter now\n1399 armenian small letter sha\n1400 armenian small letter vo\n1401 armenian small letter cha\n1402 armenian small letter peh\n1403 armenian small letter jheh\n1404 armenian small letter ra\n1405 armenian small letter seh\n1406 armenian small letter vew\n1407 armenian small letter tiwn\n1408 armenian small letter reh\n1409 armenian small letter co\n1410 armenian small letter yiwn\n1411 armenian small letter piwr\n1412 armenian small letter keh\n1413 armenian small letter oh\n1414 armenian small letter feh\n1415 armenian small ligature ech yiwn\n1416 armenian small letter yi with stroke\n1417 armenian full stop\n1418 armenian hyphen\n1421 right-facing armenian eternity sign\n1422 left-facing armenian eternity sign\n1423 armenian dram sign\n1425 hebrew accent etnahta\n1426 hebrew accent segol\n1427 hebrew accent shalshelet\n1428 hebrew accent zaqef qatan\n1429 hebrew accent zaqef gadol\n1430 hebrew accent tipeha\n1431 hebrew accent revia\n1432 hebrew accent zarqa\n1433 hebrew accent pashta\n1434 hebrew accent yetiv\n1435 hebrew accent tevir\n1436 hebrew accent geresh\n1437 hebrew accent geresh muqdam\n1438 hebrew accent gershayim\n1439 hebrew accent qarney para\n1440 hebrew accent telisha gedola\n1441 hebrew accent pazer\n1442 hebrew accent atnah hafukh\n1443 hebrew accent munah\n1444 hebrew accent mahapakh\n1445 hebrew accent merkha\n1446 hebrew accent merkha kefula\n1447 hebrew accent darga\n1448 hebrew accent qadma\n1449 hebrew accent telisha qetana\n1450 hebrew accent yerah ben yomo\n1451 hebrew accent ole\n1452 hebrew accent iluy\n1453 hebrew accent dehi\n1454 hebrew accent zinor\n1455 hebrew mark masora circle\n1456 hebrew point sheva\n1457 hebrew point hataf segol\n1458 hebrew point hataf patah\n1459 hebrew point hataf qamats\n1460 hebrew point hiriq\n1461 hebrew point tsere\n1462 hebrew point segol\n1463 hebrew point patah\n1464 hebrew point qamats\n1465 hebrew point holam\n1466 hebrew point holam haser for vav\n1467 hebrew point qubuts\n1468 hebrew point dagesh or mapiq\n1469 hebrew point meteg\n1470 hebrew punctuation maqaf\n1471 hebrew point rafe\n1472 hebrew punctuation paseq\n1473 hebrew point shin dot\n1474 hebrew point sin dot\n1475 hebrew punctuation sof pasuq\n1476 hebrew mark upper dot\n1477 hebrew mark lower dot\n1478 hebrew punctuation nun hafukha\n1479 hebrew point qamats qatan\n1488 hebrew letter alef\n1489 hebrew letter bet\n1490 hebrew letter gimel\n1491 hebrew letter dalet\n1492 hebrew letter he\n1493 hebrew letter vav\n1494 hebrew letter zayin\n1495 hebrew letter het\n1496 hebrew letter tet\n1497 hebrew letter yod\n1498 hebrew letter final kaf\n1499 hebrew letter kaf\n1500 hebrew letter lamed\n1501 hebrew letter final mem\n1502 hebrew letter mem\n1503 hebrew letter final nun\n1504 hebrew letter nun\n1505 hebrew letter samekh\n1506 hebrew letter ayin\n1507 hebrew letter final pe\n1508 hebrew letter pe\n1509 hebrew letter final tsadi\n1510 hebrew letter tsadi\n1511 hebrew letter qof\n1512 hebrew letter resh\n1513 hebrew letter shin\n1514 hebrew letter tav\n1519 hebrew yod triangle\n1520 hebrew ligature yiddish double vav\n1521 hebrew ligature yiddish vav yod\n1522 hebrew ligature yiddish double yod\n1523 hebrew punctuation geresh\n1524 hebrew punctuation gershayim\n1536 arabic number sign\n1537 arabic sign sanah\n1538 arabic footnote marker\n1539 arabic sign safha\n1540 arabic sign samvat\n1541 arabic number mark above\n1542 arabic-indic cube root\n1543 arabic-indic fourth root\n1544 arabic ray\n1545 arabic-indic per mille sign\n1546 arabic-indic per ten thousand sign\n1547 afghani sign\n1548 arabic comma\n1549 arabic date separator\n1550 arabic poetic verse sign\n1551 arabic sign misra\n1552 arabic sign sallallahou alayhe wassallam\n1553 arabic sign alayhe assallam\n1554 arabic sign rahmatullah alayhe\n1555 arabic sign radi allahou anhu\n1556 arabic sign takhallus\n1557 arabic small high tah\n1558 arabic small high ligature alef with lam with yeh\n1559 arabic small high zain\n1560 arabic small fatha\n1561 arabic small damma\n1562 arabic small kasra\n1563 arabic semicolon\n1564 arabic letter mark\n1565 arabic end of text mark\n1566 arabic triple dot punctuation mark\n1567 arabic question mark\n1568 arabic letter kashmiri yeh\n1569 arabic letter hamza\n1570 arabic letter alef with madda above\n1571 arabic letter alef with hamza above\n1572 arabic letter waw with hamza above\n1573 arabic letter alef with hamza below\n1574 arabic letter yeh with hamza above\n1575 arabic letter alef\n1576 arabic letter beh\n1577 arabic letter teh marbuta\n1578 arabic letter teh\n1579 arabic letter theh\n1580 arabic letter jeem\n1581 arabic letter hah\n1582 arabic letter khah\n1583 arabic letter dal\n1584 arabic letter thal\n1585 arabic letter reh\n1586 arabic letter zain\n1587 arabic letter seen\n1588 arabic letter sheen\n1589 arabic letter sad\n1590 arabic letter dad\n1591 arabic letter tah\n1592 arabic letter zah\n1593 arabic letter ain\n1594 arabic letter ghain\n1595 arabic letter keheh with two dots above\n1596 arabic letter keheh with three dots below\n1597 arabic letter farsi yeh with inverted v\n1598 arabic letter farsi yeh with two dots above\n1599 arabic letter farsi yeh with three dots above\n1600 arabic tatweel\n1601 arabic letter feh\n1602 arabic letter qaf\n1603 arabic letter kaf\n1604 arabic letter lam\n1605 arabic letter meem\n1606 arabic letter noon\n1607 arabic letter heh\n1608 arabic letter waw\n1609 arabic letter alef maksura\n1610 arabic letter yeh\n1611 arabic fathatan\n1612 arabic dammatan\n1613 arabic kasratan\n1614 arabic fatha\n1615 arabic damma\n1616 arabic kasra\n1617 arabic shadda\n1618 arabic sukun\n1619 arabic maddah above\n1620 arabic hamza above\n1621 arabic hamza below\n1622 arabic subscript alef\n1623 arabic inverted damma\n1624 arabic mark noon ghunna\n1625 arabic zwarakay\n1626 arabic vowel sign small v above\n1627 arabic vowel sign inverted small v above\n1628 arabic vowel sign dot below\n1629 arabic reversed damma\n1630 arabic fatha with two dots\n1631 arabic wavy hamza below\n1632 arabic-indic digit zero\n1633 arabic-indic digit one\n1634 arabic-indic digit two\n1635 arabic-indic digit three\n1636 arabic-indic digit four\n1637 arabic-indic digit five\n1638 arabic-indic digit six\n1639 arabic-indic digit seven\n1640 arabic-indic digit eight\n1641 arabic-indic digit nine\n1642 arabic percent sign\n1643 arabic decimal separator\n1644 arabic thousands separator\n1645 arabic five pointed star\n1646 arabic letter dotless beh\n1647 arabic letter dotless qaf\n1648 arabic letter superscript alef\n1649 arabic letter alef wasla\n1650 arabic letter alef with wavy hamza above\n1651 arabic letter alef with wavy hamza below\n1652 arabic letter high hamza\n1653 arabic letter high hamza alef\n1654 arabic letter high hamza waw\n1655 arabic letter u with hamza above\n1656 arabic letter high hamza yeh\n1657 arabic letter tteh\n1658 arabic letter tteheh\n1659 arabic letter beeh\n1660 arabic letter teh with ring\n1661 arabic letter teh with three dots above downwards\n1662 arabic letter peh\n1663 arabic letter teheh\n1664 arabic letter beheh\n1665 arabic letter hah with hamza above\n1666 arabic letter hah with two dots vertical above\n1667 arabic letter nyeh\n1668 arabic letter dyeh\n1669 arabic letter hah with three dots above\n1670 arabic letter tcheh\n1671 arabic letter tcheheh\n1672 arabic letter ddal\n1673 arabic letter dal with ring\n1674 arabic letter dal with dot below\n1675 arabic letter dal with dot below and small tah\n1676 arabic letter dahal\n1677 arabic letter ddahal\n1678 arabic letter dul\n1679 arabic letter dal with three dots above downwards\n1680 arabic letter dal with four dots above\n1681 arabic letter rreh\n1682 arabic letter reh with small v\n1683 arabic letter reh with ring\n1684 arabic letter reh with dot below\n1685 arabic letter reh with small v below\n1686 arabic letter reh with dot below and dot above\n1687 arabic letter reh with two dots above\n1688 arabic letter jeh\n1689 arabic letter reh with four dots above\n1690 arabic letter seen with dot below and dot above\n1691 arabic letter seen with three dots below\n1692 arabic letter seen with three dots below and three dots above\n1693 arabic letter sad with two dots below\n1694 arabic letter sad with three dots above\n1695 arabic letter tah with three dots above\n1696 arabic letter ain with three dots above\n1697 arabic letter dotless feh\n1698 arabic letter feh with dot moved below\n1699 arabic letter feh with dot below\n1700 arabic letter veh\n1701 arabic letter feh with three dots below\n1702 arabic letter peheh\n1703 arabic letter qaf with dot above\n1704 arabic letter qaf with three dots above\n1705 arabic letter keheh\n1706 arabic letter swash kaf\n1707 arabic letter kaf with ring\n1708 arabic letter kaf with dot above\n1709 arabic letter ng\n1710 arabic letter kaf with three dots below\n1711 arabic letter gaf\n1712 arabic letter gaf with ring\n1713 arabic letter ngoeh\n1714 arabic letter gaf with two dots below\n1715 arabic letter gueh\n1716 arabic letter gaf with three dots above\n1717 arabic letter lam with small v\n1718 arabic letter lam with dot above\n1719 arabic letter lam with three dots above\n1720 arabic letter lam with three dots below\n1721 arabic letter noon with dot below\n1722 arabic letter noon ghunna\n1723 arabic letter rnoon\n1724 arabic letter noon with ring\n1725 arabic letter noon with three dots above\n1726 arabic letter heh doachashmee\n1727 arabic letter tcheh with dot above\n1728 arabic letter heh with yeh above\n1729 arabic letter heh goal\n1730 arabic letter heh goal with hamza above\n1731 arabic letter teh marbuta goal\n1732 arabic letter waw with ring\n1733 arabic letter kirghiz oe\n1734 arabic letter oe\n1735 arabic letter u\n1736 arabic letter yu\n1737 arabic letter kirghiz yu\n1738 arabic letter waw with two dots above\n1739 arabic letter ve\n1740 arabic letter farsi yeh\n1741 arabic letter yeh with tail\n1742 arabic letter yeh with small v\n1743 arabic letter waw with dot above\n1744 arabic letter e\n1745 arabic letter yeh with three dots below\n1746 arabic letter yeh barree\n1747 arabic letter yeh barree with hamza above\n1748 arabic full stop\n1749 arabic letter ae\n1750 arabic small high ligature sad with lam with alef maksura\n1751 arabic small high ligature qaf with lam with alef maksura\n1752 arabic small high meem initial form\n1753 arabic small high lam alef\n1754 arabic small high jeem\n1755 arabic small high three dots\n1756 arabic small high seen\n1757 arabic end of ayah\n1758 arabic start of rub el hizb\n1759 arabic small high rounded zero\n1760 arabic small high upright rectangular zero\n1761 arabic small high dotless head of khah\n1762 arabic small high meem isolated form\n1763 arabic small low seen\n1764 arabic small high madda\n1765 arabic small waw\n1766 arabic small yeh\n1767 arabic small high yeh\n1768 arabic small high noon\n1769 arabic place of sajdah\n1770 arabic empty centre low stop\n1771 arabic empty centre high stop\n1772 arabic rounded high stop with filled centre\n1773 arabic small low meem\n1774 arabic letter dal with inverted v\n1775 arabic letter reh with inverted v\n1776 extended arabic-indic digit zero\n1777 extended arabic-indic digit one\n1778 extended arabic-indic digit two\n1779 extended arabic-indic digit three\n1780 extended arabic-indic digit four\n1781 extended arabic-indic digit five\n1782 extended arabic-indic digit six\n1783 extended arabic-indic digit seven\n1784 extended arabic-indic digit eight\n1785 extended arabic-indic digit nine\n1786 arabic letter sheen with dot below\n1787 arabic letter dad with dot below\n1788 arabic letter ghain with dot below\n1789 arabic sign sindhi ampersand\n1790 arabic sign sindhi postposition men\n1791 arabic letter heh with inverted v\n1792 syriac end of paragraph\n1793 syriac supralinear full stop\n1794 syriac sublinear full stop\n1795 syriac supralinear colon\n1796 syriac sublinear colon\n1797 syriac horizontal colon\n1798 syriac colon skewed left\n1799 syriac colon skewed right\n1800 syriac supralinear colon skewed left\n1801 syriac sublinear colon skewed right\n1802 syriac contraction\n1803 syriac harklean obelus\n1804 syriac harklean metobelus\n1805 syriac harklean asteriscus\n1807 syriac abbreviation mark\n1808 syriac letter alaph\n1809 syriac letter superscript alaph\n1810 syriac letter beth\n1811 syriac letter gamal\n1812 syriac letter gamal garshuni\n1813 syriac letter dalath\n1814 syriac letter dotless dalath rish\n1815 syriac letter he\n1816 syriac letter waw\n1817 syriac letter zain\n1818 syriac letter heth\n1819 syriac letter teth\n1820 syriac letter teth garshuni\n1821 syriac letter yudh\n1822 syriac letter yudh he\n1823 syriac letter kaph\n1824 syriac letter lamadh\n1825 syriac letter mim\n1826 syriac letter nun\n1827 syriac letter semkath\n1828 syriac letter final semkath\n1829 syriac letter e\n1830 syriac letter pe\n1831 syriac letter reversed pe\n1832 syriac letter sadhe\n1833 syriac letter qaph\n1834 syriac letter rish\n1835 syriac letter shin\n1836 syriac letter taw\n1837 syriac letter persian bheth\n1838 syriac letter persian ghamal\n1839 syriac letter persian dhalath\n1840 syriac pthaha above\n1841 syriac pthaha below\n1842 syriac pthaha dotted\n1843 syriac zqapha above\n1844 syriac zqapha below\n1845 syriac zqapha dotted\n1846 syriac rbasa above\n1847 syriac rbasa below\n1848 syriac dotted zlama horizontal\n1849 syriac dotted zlama angular\n1850 syriac hbasa above\n1851 syriac hbasa below\n1852 syriac hbasa-esasa dotted\n1853 syriac esasa above\n1854 syriac esasa below\n1855 syriac rwaha\n1856 syriac feminine dot\n1857 syriac qushshaya\n1858 syriac rukkakha\n1859 syriac two vertical dots above\n1860 syriac two vertical dots below\n1861 syriac three dots above\n1862 syriac three dots below\n1863 syriac oblique line above\n1864 syriac oblique line below\n1865 syriac music\n1866 syriac barrekh\n1869 syriac letter sogdian zhain\n1870 syriac letter sogdian khaph\n1871 syriac letter sogdian fe\n1872 arabic letter beh with three dots horizontally below\n1873 arabic letter beh with dot below and three dots above\n1874 arabic letter beh with three dots pointing upwards below\n1875 arabic letter beh with three dots pointing upwards below and two dots above\n1876 arabic letter beh with two dots below and dot above\n1877 arabic letter beh with inverted small v below\n1878 arabic letter beh with small v\n1879 arabic letter hah with two dots above\n1880 arabic letter hah with three dots pointing upwards below\n1881 arabic letter dal with two dots vertically below and small tah\n1882 arabic letter dal with inverted small v below\n1883 arabic letter reh with stroke\n1884 arabic letter seen with four dots above\n1885 arabic letter ain with two dots above\n1886 arabic letter ain with three dots pointing downwards above\n1887 arabic letter ain with two dots vertically above\n1888 arabic letter feh with two dots below\n1889 arabic letter feh with three dots pointing upwards below\n1890 arabic letter keheh with dot above\n1891 arabic letter keheh with three dots above\n1892 arabic letter keheh with three dots pointing upwards below\n1893 arabic letter meem with dot above\n1894 arabic letter meem with dot below\n1895 arabic letter noon with two dots below\n1896 arabic letter noon with small tah\n1897 arabic letter noon with small v\n1898 arabic letter lam with bar\n1899 arabic letter reh with two dots vertically above\n1900 arabic letter reh with hamza above\n1901 arabic letter seen with two dots vertically above\n1902 arabic letter hah with small arabic letter tah below\n1903 arabic letter hah with small arabic letter tah and two dots\n1904 arabic letter seen with small arabic letter tah and two dots\n1905 arabic letter reh with small arabic letter tah and two dots\n1906 arabic letter hah with small arabic letter tah above\n1907 arabic letter alef with extended arabic-indic digit two above\n1908 arabic letter alef with extended arabic-indic digit three above\n1909 arabic letter farsi yeh with extended arabic-indic digit two above\n1910 arabic letter farsi yeh with extended arabic-indic digit three above\n1911 arabic letter farsi yeh with extended arabic-indic digit four below\n1912 arabic letter waw with extended arabic-indic digit two above\n1913 arabic letter waw with extended arabic-indic digit three above\n1914 arabic letter yeh barree with extended arabic-indic digit two above\n1915 arabic letter yeh barree with extended arabic-indic digit three above\n1916 arabic letter hah with extended arabic-indic digit four below\n1917 arabic letter seen with extended arabic-indic digit four above\n1918 arabic letter seen with inverted v\n1919 arabic letter kaf with two dots above\n1920 thaana letter haa\n1921 thaana letter shaviyani\n1922 thaana letter noonu\n1923 thaana letter raa\n1924 thaana letter baa\n1925 thaana letter lhaviyani\n1926 thaana letter kaafu\n1927 thaana letter alifu\n1928 thaana letter vaavu\n1929 thaana letter meemu\n1930 thaana letter faafu\n1931 thaana letter dhaalu\n1932 thaana letter thaa\n1933 thaana letter laamu\n1934 thaana letter gaafu\n1935 thaana letter gnaviyani\n1936 thaana letter seenu\n1937 thaana letter daviyani\n1938 thaana letter zaviyani\n1939 thaana letter taviyani\n1940 thaana letter yaa\n1941 thaana letter paviyani\n1942 thaana letter javiyani\n1943 thaana letter chaviyani\n1944 thaana letter ttaa\n1945 thaana letter hhaa\n1946 thaana letter khaa\n1947 thaana letter thaalu\n1948 thaana letter zaa\n1949 thaana letter sheenu\n1950 thaana letter saadhu\n1951 thaana letter daadhu\n1952 thaana letter to\n1953 thaana letter zo\n1954 thaana letter ainu\n1955 thaana letter ghainu\n1956 thaana letter qaafu\n1957 thaana letter waavu\n1958 thaana abafili\n1959 thaana aabaafili\n1960 thaana ibifili\n1961 thaana eebeefili\n1962 thaana ubufili\n1963 thaana ooboofili\n1964 thaana ebefili\n1965 thaana eybeyfili\n1966 thaana obofili\n1967 thaana oaboafili\n1968 thaana sukun\n1969 thaana letter naa\n1984 nko digit zero\n1985 nko digit one\n1986 nko digit two\n1987 nko digit three\n1988 nko digit four\n1989 nko digit five\n1990 nko digit six\n1991 nko digit seven\n1992 nko digit eight\n1993 nko digit nine\n1994 nko letter a\n1995 nko letter ee\n1996 nko letter i\n1997 nko letter e\n1998 nko letter u\n1999 nko letter oo\n2000 nko letter o\n2001 nko letter dagbasinna\n2002 nko letter n\n2003 nko letter ba\n2004 nko letter pa\n2005 nko letter ta\n2006 nko letter ja\n2007 nko letter cha\n2008 nko letter da\n2009 nko letter ra\n2010 nko letter rra\n2011 nko letter sa\n2012 nko letter gba\n2013 nko letter fa\n2014 nko letter ka\n2015 nko letter la\n2016 nko letter na woloso\n2017 nko letter ma\n2018 nko letter nya\n2019 nko letter na\n2020 nko letter ha\n2021 nko letter wa\n2022 nko letter ya\n2023 nko letter nya woloso\n2024 nko letter jona ja\n2025 nko letter jona cha\n2026 nko letter jona ra\n2027 nko combining short high tone\n2028 nko combining short low tone\n2029 nko combining short rising tone\n2030 nko combining long descending tone\n2031 nko combining long high tone\n2032 nko combining long low tone\n2033 nko combining long rising tone\n2034 nko combining nasalization mark\n2035 nko combining double dot above\n2036 nko high tone apostrophe\n2037 nko low tone apostrophe\n2038 nko symbol oo dennen\n2039 nko symbol gbakurunen\n2040 nko comma\n2041 nko exclamation mark\n2042 nko lajanyalan\n2045 nko dantayalan\n2046 nko dorome sign\n2047 nko taman sign\n2048 samaritan letter alaf\n2049 samaritan letter bit\n2050 samaritan letter gaman\n2051 samaritan letter dalat\n2052 samaritan letter iy\n2053 samaritan letter baa\n2054 samaritan letter zen\n2055 samaritan letter it\n2056 samaritan letter tit\n2057 samaritan letter yut\n2058 samaritan letter kaaf\n2059 samaritan letter labat\n2060 samaritan letter mim\n2061 samaritan letter nun\n2062 samaritan letter singaat\n2063 samaritan letter in\n2064 samaritan letter fi\n2065 samaritan letter tsaadiy\n2066 samaritan letter quf\n2067 samaritan letter rish\n2068 samaritan letter shan\n2069 samaritan letter taaf\n2070 samaritan mark in\n2071 samaritan mark in-alaf\n2072 samaritan mark occlusion\n2073 samaritan mark dagesh\n2074 samaritan modifier letter epenthetic yut\n2075 samaritan mark epenthetic yut\n2076 samaritan vowel sign long e\n2077 samaritan vowel sign e\n2078 samaritan vowel sign overlong aa\n2079 samaritan vowel sign long aa\n2080 samaritan vowel sign aa\n2081 samaritan vowel sign overlong a\n2082 samaritan vowel sign long a\n2083 samaritan vowel sign a\n2084 samaritan modifier letter short a\n2085 samaritan vowel sign short a\n2086 samaritan vowel sign long u\n2087 samaritan vowel sign u\n2088 samaritan modifier letter i\n2089 samaritan vowel sign long i\n2090 samaritan vowel sign i\n2091 samaritan vowel sign o\n2092 samaritan vowel sign sukun\n2093 samaritan mark nequdaa\n2096 samaritan punctuation nequdaa\n2097 samaritan punctuation afsaaq\n2098 samaritan punctuation anged\n2099 samaritan punctuation bau\n2100 samaritan punctuation atmaau\n2101 samaritan punctuation shiyyaalaa\n2102 samaritan abbreviation mark\n2103 samaritan punctuation melodic qitsa\n2104 samaritan punctuation ziqaa\n2105 samaritan punctuation qitsa\n2106 samaritan punctuation zaef\n2107 samaritan punctuation turu\n2108 samaritan punctuation arkaanu\n2109 samaritan punctuation sof mashfaat\n2110 samaritan punctuation annaau\n2112 mandaic letter halqa\n2113 mandaic letter ab\n2114 mandaic letter ag\n2115 mandaic letter ad\n2116 mandaic letter ah\n2117 mandaic letter ushenna\n2118 mandaic letter az\n2119 mandaic letter it\n2120 mandaic letter att\n2121 mandaic letter aksa\n2122 mandaic letter ak\n2123 mandaic letter al\n2124 mandaic letter am\n2125 mandaic letter an\n2126 mandaic letter as\n2127 mandaic letter in\n2128 mandaic letter ap\n2129 mandaic letter asz\n2130 mandaic letter aq\n2131 mandaic letter ar\n2132 mandaic letter ash\n2133 mandaic letter at\n2134 mandaic letter dushenna\n2135 mandaic letter kad\n2136 mandaic letter ain\n2137 mandaic affrication mark\n2138 mandaic vocalization mark\n2139 mandaic gemination mark\n2142 mandaic punctuation\n2144 syriac letter malayalam nga\n2145 syriac letter malayalam ja\n2146 syriac letter malayalam nya\n2147 syriac letter malayalam tta\n2148 syriac letter malayalam nna\n2149 syriac letter malayalam nnna\n2150 syriac letter malayalam bha\n2151 syriac letter malayalam ra\n2152 syriac letter malayalam lla\n2153 syriac letter malayalam llla\n2154 syriac letter malayalam ssa\n2160 arabic letter alef with attached fatha\n2161 arabic letter alef with attached top right fatha\n2162 arabic letter alef with right middle stroke\n2163 arabic letter alef with left middle stroke\n2164 arabic letter alef with attached kasra\n2165 arabic letter alef with attached bottom right kasra\n2166 arabic letter alef with attached round dot above\n2167 arabic letter alef with attached right round dot\n2168 arabic letter alef with attached left round dot\n2169 arabic letter alef with attached round dot below\n2170 arabic letter alef with dot above\n2171 arabic letter alef with attached top right fatha and dot above\n2172 arabic letter alef with right middle stroke and dot above\n2173 arabic letter alef with attached bottom right kasra and dot above\n2174 arabic letter alef with attached top right fatha and left ring\n2175 arabic letter alef with right middle stroke and left ring\n2176 arabic letter alef with attached bottom right kasra and left ring\n2177 arabic letter alef with attached right hamza\n2178 arabic letter alef with attached left hamza\n2179 arabic tatweel with overstruck hamza\n2180 arabic tatweel with overstruck waw\n2181 arabic tatweel with two dots below\n2182 arabic letter thin yeh\n2183 arabic baseline round dot\n2184 arabic raised round dot\n2185 arabic letter noon with inverted small v\n2186 arabic letter hah with inverted small v below\n2187 arabic letter tah with dot below\n2188 arabic letter tah with three dots below\n2189 arabic letter keheh with two dots vertically below\n2190 arabic vertical tail\n2191 arabic letter noon with ring above\n2192 arabic pound mark above\n2193 arabic piastre mark above\n2199 arabic pepet\n2200 arabic small high word al-juz\n2201 arabic small low word ishmaam\n2202 arabic small low word imaala\n2203 arabic small low word tasheel\n2204 arabic madda waajib\n2205 arabic superscript alef mokhassas\n2206 arabic doubled madda\n2207 arabic half madda over madda\n2208 arabic letter beh with small v below\n2209 arabic letter beh with hamza above\n2210 arabic letter jeem with two dots above\n2211 arabic letter tah with two dots above\n2212 arabic letter feh with dot below and three dots above\n2213 arabic letter qaf with dot below\n2214 arabic letter lam with double bar\n2215 arabic letter meem with three dots above\n2216 arabic letter yeh with two dots below and hamza above\n2217 arabic letter yeh with two dots below and dot above\n2218 arabic letter reh with loop\n2219 arabic letter waw with dot within\n2220 arabic letter rohingya yeh\n2221 arabic letter low alef\n2222 arabic letter dal with three dots below\n2223 arabic letter sad with three dots below\n2224 arabic letter gaf with inverted stroke\n2225 arabic letter straight waw\n2226 arabic letter zain with inverted v above\n2227 arabic letter ain with three dots below\n2228 arabic letter kaf with dot below\n2229 arabic letter qaf with dot below and no dots above\n2230 arabic letter beh with small meem above\n2231 arabic letter peh with small meem above\n2232 arabic letter teh with small teh above\n2233 arabic letter reh with small noon above\n2234 arabic letter yeh with two dots below and small noon above\n2235 arabic letter african feh\n2236 arabic letter african qaf\n2237 arabic letter african noon\n2238 arabic letter peh with small v\n2239 arabic letter teh with small v\n2240 arabic letter tteh with small v\n2241 arabic letter tcheh with small v\n2242 arabic letter keheh with small v\n2243 arabic letter ghain with three dots above\n2244 arabic letter african qaf with three dots above\n2245 arabic letter jeem with three dots above\n2246 arabic letter jeem with three dots below\n2247 arabic letter lam with small arabic letter tah above\n2248 arabic letter graf\n2249 arabic small farsi yeh\n2250 arabic small high farsi yeh\n2251 arabic small high yeh barree with two dots below\n2252 arabic small high word sah\n2253 arabic small high zah\n2254 arabic large round dot above\n2255 arabic large round dot below\n2256 arabic sukun below\n2257 arabic large circle below\n2258 arabic large round dot inside circle below\n2259 arabic small low waw\n2260 arabic small high word ar-rub\n2261 arabic small high sad\n2262 arabic small high ain\n2263 arabic small high qaf\n2264 arabic small high noon with kasra\n2265 arabic small low noon with kasra\n2266 arabic small high word ath-thalatha\n2267 arabic small high word as-sajda\n2268 arabic small high word an-nisf\n2269 arabic small high word sakta\n2270 arabic small high word qif\n2271 arabic small high word waqfa\n2272 arabic small high footnote marker\n2273 arabic small high sign safha\n2274 arabic disputed end of ayah\n2275 arabic turned damma below\n2276 arabic curly fatha\n2277 arabic curly damma\n2278 arabic curly kasra\n2279 arabic curly fathatan\n2280 arabic curly dammatan\n2281 arabic curly kasratan\n2282 arabic tone one dot above\n2283 arabic tone two dots above\n2284 arabic tone loop above\n2285 arabic tone one dot below\n2286 arabic tone two dots below\n2287 arabic tone loop below\n2288 arabic open fathatan\n2289 arabic open dammatan\n2290 arabic open kasratan\n2291 arabic small high waw\n2292 arabic fatha with ring\n2293 arabic fatha with dot above\n2294 arabic kasra with dot below\n2295 arabic left arrowhead above\n2296 arabic right arrowhead above\n2297 arabic left arrowhead below\n2298 arabic right arrowhead below\n2299 arabic double right arrowhead above\n2300 arabic double right arrowhead above with dot\n2301 arabic right arrowhead above with dot\n2302 arabic damma with dot\n2303 arabic mark sideways noon ghunna\n2304 devanagari sign inverted candrabindu\n2305 devanagari sign candrabindu\n2306 devanagari sign anusvara\n2307 devanagari sign visarga\n2308 devanagari letter short a\n2309 devanagari letter a\n2310 devanagari letter aa\n2311 devanagari letter i\n2312 devanagari letter ii\n2313 devanagari letter u\n2314 devanagari letter uu\n2315 devanagari letter vocalic r\n2316 devanagari letter vocalic l\n2317 devanagari letter candra e\n2318 devanagari letter short e\n2319 devanagari letter e\n2320 devanagari letter ai\n2321 devanagari letter candra o\n2322 devanagari letter short o\n2323 devanagari letter o\n2324 devanagari letter au\n2325 devanagari letter ka\n2326 devanagari letter kha\n2327 devanagari letter ga\n2328 devanagari letter gha\n2329 devanagari letter nga\n2330 devanagari letter ca\n2331 devanagari letter cha\n2332 devanagari letter ja\n2333 devanagari letter jha\n2334 devanagari letter nya\n2335 devanagari letter tta\n2336 devanagari letter ttha\n2337 devanagari letter dda\n2338 devanagari letter ddha\n2339 devanagari letter nna\n2340 devanagari letter ta\n2341 devanagari letter tha\n2342 devanagari letter da\n2343 devanagari letter dha\n2344 devanagari letter na\n2345 devanagari letter nnna\n2346 devanagari letter pa\n2347 devanagari letter pha\n2348 devanagari letter ba\n2349 devanagari letter bha\n2350 devanagari letter ma\n2351 devanagari letter ya\n2352 devanagari letter ra\n2353 devanagari letter rra\n2354 devanagari letter la\n2355 devanagari letter lla\n2356 devanagari letter llla\n2357 devanagari letter va\n2358 devanagari letter sha\n2359 devanagari letter ssa\n2360 devanagari letter sa\n2361 devanagari letter ha\n2362 devanagari vowel sign oe\n2363 devanagari vowel sign ooe\n2364 devanagari sign nukta\n2365 devanagari sign avagraha\n2366 devanagari vowel sign aa\n2367 devanagari vowel sign i\n2368 devanagari vowel sign ii\n2369 devanagari vowel sign u\n2370 devanagari vowel sign uu\n2371 devanagari vowel sign vocalic r\n2372 devanagari vowel sign vocalic rr\n2373 devanagari vowel sign candra e\n2374 devanagari vowel sign short e\n2375 devanagari vowel sign e\n2376 devanagari vowel sign ai\n2377 devanagari vowel sign candra o\n2378 devanagari vowel sign short o\n2379 devanagari vowel sign o\n2380 devanagari vowel sign au\n2381 devanagari sign virama\n2382 devanagari vowel sign prishthamatra e\n2383 devanagari vowel sign aw\n2384 devanagari om\n2385 devanagari stress sign udatta\n2386 devanagari stress sign anudatta\n2387 devanagari grave accent\n2388 devanagari acute accent\n2389 devanagari vowel sign candra long e\n2390 devanagari vowel sign ue\n2391 devanagari vowel sign uue\n2392 devanagari letter qa\n2393 devanagari letter khha\n2394 devanagari letter ghha\n2395 devanagari letter za\n2396 devanagari letter dddha\n2397 devanagari letter rha\n2398 devanagari letter fa\n2399 devanagari letter yya\n2400 devanagari letter vocalic rr\n2401 devanagari letter vocalic ll\n2402 devanagari vowel sign vocalic l\n2403 devanagari vowel sign vocalic ll\n2404 devanagari danda\n2405 devanagari double danda\n2406 devanagari digit zero\n2407 devanagari digit one\n2408 devanagari digit two\n2409 devanagari digit three\n2410 devanagari digit four\n2411 devanagari digit five\n2412 devanagari digit six\n2413 devanagari digit seven\n2414 devanagari digit eight\n2415 devanagari digit nine\n2416 devanagari abbreviation sign\n2417 devanagari sign high spacing dot\n2418 devanagari letter candra a\n2419 devanagari letter oe\n2420 devanagari letter ooe\n2421 devanagari letter aw\n2422 devanagari letter ue\n2423 devanagari letter uue\n2424 devanagari letter marwari dda\n2425 devanagari letter zha\n2426 devanagari letter heavy ya\n2427 devanagari letter gga\n2428 devanagari letter jja\n2429 devanagari letter glottal stop\n2430 devanagari letter ddda\n2431 devanagari letter bba\n2432 bengali anji\n2433 bengali sign candrabindu\n2434 bengali sign anusvara\n2435 bengali sign visarga\n2437 bengali letter a\n2438 bengali letter aa\n2439 bengali letter i\n2440 bengali letter ii\n2441 bengali letter u\n2442 bengali letter uu\n2443 bengali letter vocalic r\n2444 bengali letter vocalic l\n2447 bengali letter e\n2448 bengali letter ai\n2451 bengali letter o\n2452 bengali letter au\n2453 bengali letter ka\n2454 bengali letter kha\n2455 bengali letter ga\n2456 bengali letter gha\n2457 bengali letter nga\n2458 bengali letter ca\n2459 bengali letter cha\n2460 bengali letter ja\n2461 bengali letter jha\n2462 bengali letter nya\n2463 bengali letter tta\n2464 bengali letter ttha\n2465 bengali letter dda\n2466 bengali letter ddha\n2467 bengali letter nna\n2468 bengali letter ta\n2469 bengali letter tha\n2470 bengali letter da\n2471 bengali letter dha\n2472 bengali letter na\n2474 bengali letter pa\n2475 bengali letter pha\n2476 bengali letter ba\n2477 bengali letter bha\n2478 bengali letter ma\n2479 bengali letter ya\n2480 bengali letter ra\n2482 bengali letter la\n2486 bengali letter sha\n2487 bengali letter ssa\n2488 bengali letter sa\n2489 bengali letter ha\n2492 bengali sign nukta\n2493 bengali sign avagraha\n2494 bengali vowel sign aa\n2495 bengali vowel sign i\n2496 bengali vowel sign ii\n2497 bengali vowel sign u\n2498 bengali vowel sign uu\n2499 bengali vowel sign vocalic r\n2500 bengali vowel sign vocalic rr\n2503 bengali vowel sign e\n2504 bengali vowel sign ai\n2507 bengali vowel sign o\n2508 bengali vowel sign au\n2509 bengali sign virama\n2510 bengali letter khanda ta\n2519 bengali au length mark\n2524 bengali letter rra\n2525 bengali letter rha\n2527 bengali letter yya\n2528 bengali letter vocalic rr\n2529 bengali letter vocalic ll\n2530 bengali vowel sign vocalic l\n2531 bengali vowel sign vocalic ll\n2534 bengali digit zero\n2535 bengali digit one\n2536 bengali digit two\n2537 bengali digit three\n2538 bengali digit four\n2539 bengali digit five\n2540 bengali digit six\n2541 bengali digit seven\n2542 bengali digit eight\n2543 bengali digit nine\n2544 bengali letter ra with middle diagonal\n2545 bengali letter ra with lower diagonal\n2546 bengali rupee mark\n2547 bengali rupee sign\n2548 bengali currency numerator one\n2549 bengali currency numerator two\n2550 bengali currency numerator three\n2551 bengali currency numerator four\n2552 bengali currency numerator one less than the denominator\n2553 bengali currency denominator sixteen\n2554 bengali isshar\n2555 bengali ganda mark\n2556 bengali letter vedic anusvara\n2557 bengali abbreviation sign\n2558 bengali sandhi mark\n2561 gurmukhi sign adak bindi\n2562 gurmukhi sign bindi\n2563 gurmukhi sign visarga\n2565 gurmukhi letter a\n2566 gurmukhi letter aa\n2567 gurmukhi letter i\n2568 gurmukhi letter ii\n2569 gurmukhi letter u\n2570 gurmukhi letter uu\n2575 gurmukhi letter ee\n2576 gurmukhi letter ai\n2579 gurmukhi letter oo\n2580 gurmukhi letter au\n2581 gurmukhi letter ka\n2582 gurmukhi letter kha\n2583 gurmukhi letter ga\n2584 gurmukhi letter gha\n2585 gurmukhi letter nga\n2586 gurmukhi letter ca\n2587 gurmukhi letter cha\n2588 gurmukhi letter ja\n2589 gurmukhi letter jha\n2590 gurmukhi letter nya\n2591 gurmukhi letter tta\n2592 gurmukhi letter ttha\n2593 gurmukhi letter dda\n2594 gurmukhi letter ddha\n2595 gurmukhi letter nna\n2596 gurmukhi letter ta\n2597 gurmukhi letter tha\n2598 gurmukhi letter da\n2599 gurmukhi letter dha\n2600 gurmukhi letter na\n2602 gurmukhi letter pa\n2603 gurmukhi letter pha\n2604 gurmukhi letter ba\n2605 gurmukhi letter bha\n2606 gurmukhi letter ma\n2607 gurmukhi letter ya\n2608 gurmukhi letter ra\n2610 gurmukhi letter la\n2611 gurmukhi letter lla\n2613 gurmukhi letter va\n2614 gurmukhi letter sha\n2616 gurmukhi letter sa\n2617 gurmukhi letter ha\n2620 gurmukhi sign nukta\n2622 gurmukhi vowel sign aa\n2623 gurmukhi vowel sign i\n2624 gurmukhi vowel sign ii\n2625 gurmukhi vowel sign u\n2626 gurmukhi vowel sign uu\n2631 gurmukhi vowel sign ee\n2632 gurmukhi vowel sign ai\n2635 gurmukhi vowel sign oo\n2636 gurmukhi vowel sign au\n2637 gurmukhi sign virama\n2641 gurmukhi sign udaat\n2649 gurmukhi letter khha\n2650 gurmukhi letter ghha\n2651 gurmukhi letter za\n2652 gurmukhi letter rra\n2654 gurmukhi letter fa\n2662 gurmukhi digit zero\n2663 gurmukhi digit one\n2664 gurmukhi digit two\n2665 gurmukhi digit three\n2666 gurmukhi digit four\n2667 gurmukhi digit five\n2668 gurmukhi digit six\n2669 gurmukhi digit seven\n2670 gurmukhi digit eight\n2671 gurmukhi digit nine\n2672 gurmukhi tippi\n2673 gurmukhi addak\n2674 gurmukhi iri\n2675 gurmukhi ura\n2676 gurmukhi ek onkar\n2677 gurmukhi sign yakash\n2678 gurmukhi abbreviation sign\n2689 gujarati sign candrabindu\n2690 gujarati sign anusvara\n2691 gujarati sign visarga\n2693 gujarati letter a\n2694 gujarati letter aa\n2695 gujarati letter i\n2696 gujarati letter ii\n2697 gujarati letter u\n2698 gujarati letter uu\n2699 gujarati letter vocalic r\n2700 gujarati letter vocalic l\n2701 gujarati vowel candra e\n2703 gujarati letter e\n2704 gujarati letter ai\n2705 gujarati vowel candra o\n2707 gujarati letter o\n2708 gujarati letter au\n2709 gujarati letter ka\n2710 gujarati letter kha\n2711 gujarati letter ga\n2712 gujarati letter gha\n2713 gujarati letter nga\n2714 gujarati letter ca\n2715 gujarati letter cha\n2716 gujarati letter ja\n2717 gujarati letter jha\n2718 gujarati letter nya\n2719 gujarati letter tta\n2720 gujarati letter ttha\n2721 gujarati letter dda\n2722 gujarati letter ddha\n2723 gujarati letter nna\n2724 gujarati letter ta\n2725 gujarati letter tha\n2726 gujarati letter da\n2727 gujarati letter dha\n2728 gujarati letter na\n2730 gujarati letter pa\n2731 gujarati letter pha\n2732 gujarati letter ba\n2733 gujarati letter bha\n2734 gujarati letter ma\n2735 gujarati letter ya\n2736 gujarati letter ra\n2738 gujarati letter la\n2739 gujarati letter lla\n2741 gujarati letter va\n2742 gujarati letter sha\n2743 gujarati letter ssa\n2744 gujarati letter sa\n2745 gujarati letter ha\n2748 gujarati sign nukta\n2749 gujarati sign avagraha\n2750 gujarati vowel sign aa\n2751 gujarati vowel sign i\n2752 gujarati vowel sign ii\n2753 gujarati vowel sign u\n2754 gujarati vowel sign uu\n2755 gujarati vowel sign vocalic r\n2756 gujarati vowel sign vocalic rr\n2757 gujarati vowel sign candra e\n2759 gujarati vowel sign e\n2760 gujarati vowel sign ai\n2761 gujarati vowel sign candra o\n2763 gujarati vowel sign o\n2764 gujarati vowel sign au\n2765 gujarati sign virama\n2768 gujarati om\n2784 gujarati letter vocalic rr\n2785 gujarati letter vocalic ll\n2786 gujarati vowel sign vocalic l\n2787 gujarati vowel sign vocalic ll\n2790 gujarati digit zero\n2791 gujarati digit one\n2792 gujarati digit two\n2793 gujarati digit three\n2794 gujarati digit four\n2795 gujarati digit five\n2796 gujarati digit six\n2797 gujarati digit seven\n2798 gujarati digit eight\n2799 gujarati digit nine\n2800 gujarati abbreviation sign\n2801 gujarati rupee sign\n2809 gujarati letter zha\n2810 gujarati sign sukun\n2811 gujarati sign shadda\n2812 gujarati sign maddah\n2813 gujarati sign three-dot nukta above\n2814 gujarati sign circle nukta above\n2815 gujarati sign two-circle nukta above\n2817 oriya sign candrabindu\n2818 oriya sign anusvara\n2819 oriya sign visarga\n2821 oriya letter a\n2822 oriya letter aa\n2823 oriya letter i\n2824 oriya letter ii\n2825 oriya letter u\n2826 oriya letter uu\n2827 oriya letter vocalic r\n2828 oriya letter vocalic l\n2831 oriya letter e\n2832 oriya letter ai\n2835 oriya letter o\n2836 oriya letter au\n2837 oriya letter ka\n2838 oriya letter kha\n2839 oriya letter ga\n2840 oriya letter gha\n2841 oriya letter nga\n2842 oriya letter ca\n2843 oriya letter cha\n2844 oriya letter ja\n2845 oriya letter jha\n2846 oriya letter nya\n2847 oriya letter tta\n2848 oriya letter ttha\n2849 oriya letter dda\n2850 oriya letter ddha\n2851 oriya letter nna\n2852 oriya letter ta\n2853 oriya letter tha\n2854 oriya letter da\n2855 oriya letter dha\n2856 oriya letter na\n2858 oriya letter pa\n2859 oriya letter pha\n2860 oriya letter ba\n2861 oriya letter bha\n2862 oriya letter ma\n2863 oriya letter ya\n2864 oriya letter ra\n2866 oriya letter la\n2867 oriya letter lla\n2869 oriya letter va\n2870 oriya letter sha\n2871 oriya letter ssa\n2872 oriya letter sa\n2873 oriya letter ha\n2876 oriya sign nukta\n2877 oriya sign avagraha\n2878 oriya vowel sign aa\n2879 oriya vowel sign i\n2880 oriya vowel sign ii\n2881 oriya vowel sign u\n2882 oriya vowel sign uu\n2883 oriya vowel sign vocalic r\n2884 oriya vowel sign vocalic rr\n2887 oriya vowel sign e\n2888 oriya vowel sign ai\n2891 oriya vowel sign o\n2892 oriya vowel sign au\n2893 oriya sign virama\n2901 oriya sign overline\n2902 oriya ai length mark\n2903 oriya au length mark\n2908 oriya letter rra\n2909 oriya letter rha\n2911 oriya letter yya\n2912 oriya letter vocalic rr\n2913 oriya letter vocalic ll\n2914 oriya vowel sign vocalic l\n2915 oriya vowel sign vocalic ll\n2918 oriya digit zero\n2919 oriya digit one\n2920 oriya digit two\n2921 oriya digit three\n2922 oriya digit four\n2923 oriya digit five\n2924 oriya digit six\n2925 oriya digit seven\n2926 oriya digit eight\n2927 oriya digit nine\n2928 oriya isshar\n2929 oriya letter wa\n2930 oriya fraction one quarter\n2931 oriya fraction one half\n2932 oriya fraction three quarters\n2933 oriya fraction one sixteenth\n2934 oriya fraction one eighth\n2935 oriya fraction three sixteenths\n2946 tamil sign anusvara\n2947 tamil sign visarga\n2949 tamil letter a\n2950 tamil letter aa\n2951 tamil letter i\n2952 tamil letter ii\n2953 tamil letter u\n2954 tamil letter uu\n2958 tamil letter e\n2959 tamil letter ee\n2960 tamil letter ai\n2962 tamil letter o\n2963 tamil letter oo\n2964 tamil letter au\n2965 tamil letter ka\n2969 tamil letter nga\n2970 tamil letter ca\n2972 tamil letter ja\n2974 tamil letter nya\n2975 tamil letter tta\n2979 tamil letter nna\n2980 tamil letter ta\n2984 tamil letter na\n2985 tamil letter nnna\n2986 tamil letter pa\n2990 tamil letter ma\n2991 tamil letter ya\n2992 tamil letter ra\n2993 tamil letter rra\n2994 tamil letter la\n2995 tamil letter lla\n2996 tamil letter llla\n2997 tamil letter va\n2998 tamil letter sha\n2999 tamil letter ssa\n3000 tamil letter sa\n3001 tamil letter ha\n3006 tamil vowel sign aa\n3007 tamil vowel sign i\n3008 tamil vowel sign ii\n3009 tamil vowel sign u\n3010 tamil vowel sign uu\n3014 tamil vowel sign e\n3015 tamil vowel sign ee\n3016 tamil vowel sign ai\n3018 tamil vowel sign o\n3019 tamil vowel sign oo\n3020 tamil vowel sign au\n3021 tamil sign virama\n3024 tamil om\n3031 tamil au length mark\n3046 tamil digit zero\n3047 tamil digit one\n3048 tamil digit two\n3049 tamil digit three\n3050 tamil digit four\n3051 tamil digit five\n3052 tamil digit six\n3053 tamil digit seven\n3054 tamil digit eight\n3055 tamil digit nine\n3056 tamil number ten\n3057 tamil number one hundred\n3058 tamil number one thousand\n3059 tamil day sign\n3060 tamil month sign\n3061 tamil year sign\n3062 tamil debit sign\n3063 tamil credit sign\n3064 tamil as above sign\n3065 tamil rupee sign\n3066 tamil number sign\n3072 telugu sign combining candrabindu above\n3073 telugu sign candrabindu\n3074 telugu sign anusvara\n3075 telugu sign visarga\n3076 telugu sign combining anusvara above\n3077 telugu letter a\n3078 telugu letter aa\n3079 telugu letter i\n3080 telugu letter ii\n3081 telugu letter u\n3082 telugu letter uu\n3083 telugu letter vocalic r\n3084 telugu letter vocalic l\n3086 telugu letter e\n3087 telugu letter ee\n3088 telugu letter ai\n3090 telugu letter o\n3091 telugu letter oo\n3092 telugu letter au\n3093 telugu letter ka\n3094 telugu letter kha\n3095 telugu letter ga\n3096 telugu letter gha\n3097 telugu letter nga\n3098 telugu letter ca\n3099 telugu letter cha\n3100 telugu letter ja\n3101 telugu letter jha\n3102 telugu letter nya\n3103 telugu letter tta\n3104 telugu letter ttha\n3105 telugu letter dda\n3106 telugu letter ddha\n3107 telugu letter nna\n3108 telugu letter ta\n3109 telugu letter tha\n3110 telugu letter da\n3111 telugu letter dha\n3112 telugu letter na\n3114 telugu letter pa\n3115 telugu letter pha\n3116 telugu letter ba\n3117 telugu letter bha\n3118 telugu letter ma\n3119 telugu letter ya\n3120 telugu letter ra\n3121 telugu letter rra\n3122 telugu letter la\n3123 telugu letter lla\n3124 telugu letter llla\n3125 telugu letter va\n3126 telugu letter sha\n3127 telugu letter ssa\n3128 telugu letter sa\n3129 telugu letter ha\n3132 telugu sign nukta\n3133 telugu sign avagraha\n3134 telugu vowel sign aa\n3135 telugu vowel sign i\n3136 telugu vowel sign ii\n3137 telugu vowel sign u\n3138 telugu vowel sign uu\n3139 telugu vowel sign vocalic r\n3140 telugu vowel sign vocalic rr\n3142 telugu vowel sign e\n3143 telugu vowel sign ee\n3144 telugu vowel sign ai\n3146 telugu vowel sign o\n3147 telugu vowel sign oo\n3148 telugu vowel sign au\n3149 telugu sign virama\n3157 telugu length mark\n3158 telugu ai length mark\n3160 telugu letter tsa\n3161 telugu letter dza\n3162 telugu letter rrra\n3164 telugu archaic shrii\n3165 telugu letter nakaara pollu\n3168 telugu letter vocalic rr\n3169 telugu letter vocalic ll\n3170 telugu vowel sign vocalic l\n3171 telugu vowel sign vocalic ll\n3174 telugu digit zero\n3175 telugu digit one\n3176 telugu digit two\n3177 telugu digit three\n3178 telugu digit four\n3179 telugu digit five\n3180 telugu digit six\n3181 telugu digit seven\n3182 telugu digit eight\n3183 telugu digit nine\n3191 telugu sign siddham\n3192 telugu fraction digit zero for odd powers of four\n3193 telugu fraction digit one for odd powers of four\n3194 telugu fraction digit two for odd powers of four\n3195 telugu fraction digit three for odd powers of four\n3196 telugu fraction digit one for even powers of four\n3197 telugu fraction digit two for even powers of four\n3198 telugu fraction digit three for even powers of four\n3199 telugu sign tuumu\n3200 kannada sign spacing candrabindu\n3201 kannada sign candrabindu\n3202 kannada sign anusvara\n3203 kannada sign visarga\n3204 kannada sign siddham\n3205 kannada letter a\n3206 kannada letter aa\n3207 kannada letter i\n3208 kannada letter ii\n3209 kannada letter u\n3210 kannada letter uu\n3211 kannada letter vocalic r\n3212 kannada letter vocalic l\n3214 kannada letter e\n3215 kannada letter ee\n3216 kannada letter ai\n3218 kannada letter o\n3219 kannada letter oo\n3220 kannada letter au\n3221 kannada letter ka\n3222 kannada letter kha\n3223 kannada letter ga\n3224 kannada letter gha\n3225 kannada letter nga\n3226 kannada letter ca\n3227 kannada letter cha\n3228 kannada letter ja\n3229 kannada letter jha\n3230 kannada letter nya\n3231 kannada letter tta\n3232 kannada letter ttha\n3233 kannada letter dda\n3234 kannada letter ddha\n3235 kannada letter nna\n3236 kannada letter ta\n3237 kannada letter tha\n3238 kannada letter da\n3239 kannada letter dha\n3240 kannada letter na\n3242 kannada letter pa\n3243 kannada letter pha\n3244 kannada letter ba\n3245 kannada letter bha\n3246 kannada letter ma\n3247 kannada letter ya\n3248 kannada letter ra\n3249 kannada letter rra\n3250 kannada letter la\n3251 kannada letter lla\n3253 kannada letter va\n3254 kannada letter sha\n3255 kannada letter ssa\n3256 kannada letter sa\n3257 kannada letter ha\n3260 kannada sign nukta\n3261 kannada sign avagraha\n3262 kannada vowel sign aa\n3263 kannada vowel sign i\n3264 kannada vowel sign ii\n3265 kannada vowel sign u\n3266 kannada vowel sign uu\n3267 kannada vowel sign vocalic r\n3268 kannada vowel sign vocalic rr\n3270 kannada vowel sign e\n3271 kannada vowel sign ee\n3272 kannada vowel sign ai\n3274 kannada vowel sign o\n3275 kannada vowel sign oo\n3276 kannada vowel sign au\n3277 kannada sign virama\n3285 kannada length mark\n3286 kannada ai length mark\n3292 kannada archaic shrii\n3293 kannada letter nakaara pollu\n3294 kannada letter fa\n3296 kannada letter vocalic rr\n3297 kannada letter vocalic ll\n3298 kannada vowel sign vocalic l\n3299 kannada vowel sign vocalic ll\n3302 kannada digit zero\n3303 kannada digit one\n3304 kannada digit two\n3305 kannada digit three\n3306 kannada digit four\n3307 kannada digit five\n3308 kannada digit six\n3309 kannada digit seven\n3310 kannada digit eight\n3311 kannada digit nine\n3313 kannada sign jihvamuliya\n3314 kannada sign upadhmaniya\n3315 kannada sign combining anusvara above right\n3328 malayalam sign combining anusvara above\n3329 malayalam sign candrabindu\n3330 malayalam sign anusvara\n3331 malayalam sign visarga\n3332 malayalam letter vedic anusvara\n3333 malayalam letter a\n3334 malayalam letter aa\n3335 malayalam letter i\n3336 malayalam letter ii\n3337 malayalam letter u\n3338 malayalam letter uu\n3339 malayalam letter vocalic r\n3340 malayalam letter vocalic l\n3342 malayalam letter e\n3343 malayalam letter ee\n3344 malayalam letter ai\n3346 malayalam letter o\n3347 malayalam letter oo\n3348 malayalam letter au\n3349 malayalam letter ka\n3350 malayalam letter kha\n3351 malayalam letter ga\n3352 malayalam letter gha\n3353 malayalam letter nga\n3354 malayalam letter ca\n3355 malayalam letter cha\n3356 malayalam letter ja\n3357 malayalam letter jha\n3358 malayalam letter nya\n3359 malayalam letter tta\n3360 malayalam letter ttha\n3361 malayalam letter dda\n3362 malayalam letter ddha\n3363 malayalam letter nna\n3364 malayalam letter ta\n3365 malayalam letter tha\n3366 malayalam letter da\n3367 malayalam letter dha\n3368 malayalam letter na\n3369 malayalam letter nnna\n3370 malayalam letter pa\n3371 malayalam letter pha\n3372 malayalam letter ba\n3373 malayalam letter bha\n3374 malayalam letter ma\n3375 malayalam letter ya\n3376 malayalam letter ra\n3377 malayalam letter rra\n3378 malayalam letter la\n3379 malayalam letter lla\n3380 malayalam letter llla\n3381 malayalam letter va\n3382 malayalam letter sha\n3383 malayalam letter ssa\n3384 malayalam letter sa\n3385 malayalam letter ha\n3386 malayalam letter ttta\n3387 malayalam sign vertical bar virama\n3388 malayalam sign circular virama\n3389 malayalam sign avagraha\n3390 malayalam vowel sign aa\n3391 malayalam vowel sign i\n3392 malayalam vowel sign ii\n3393 malayalam vowel sign u\n3394 malayalam vowel sign uu\n3395 malayalam vowel sign vocalic r\n3396 malayalam vowel sign vocalic rr\n3398 malayalam vowel sign e\n3399 malayalam vowel sign ee\n3400 malayalam vowel sign ai\n3402 malayalam vowel sign o\n3403 malayalam vowel sign oo\n3404 malayalam vowel sign au\n3405 malayalam sign virama\n3406 malayalam letter dot reph\n3407 malayalam sign para\n3412 malayalam letter chillu m\n3413 malayalam letter chillu y\n3414 malayalam letter chillu lll\n3415 malayalam au length mark\n3416 malayalam fraction one one-hundred-and-sixtieth\n3417 malayalam fraction one fortieth\n3418 malayalam fraction three eightieths\n3419 malayalam fraction one twentieth\n3420 malayalam fraction one tenth\n3421 malayalam fraction three twentieths\n3422 malayalam fraction one fifth\n3423 malayalam letter archaic ii\n3424 malayalam letter vocalic rr\n3425 malayalam letter vocalic ll\n3426 malayalam vowel sign vocalic l\n3427 malayalam vowel sign vocalic ll\n3430 malayalam digit zero\n3431 malayalam digit one\n3432 malayalam digit two\n3433 malayalam digit three\n3434 malayalam digit four\n3435 malayalam digit five\n3436 malayalam digit six\n3437 malayalam digit seven\n3438 malayalam digit eight\n3439 malayalam digit nine\n3440 malayalam number ten\n3441 malayalam number one hundred\n3442 malayalam number one thousand\n3443 malayalam fraction one quarter\n3444 malayalam fraction one half\n3445 malayalam fraction three quarters\n3446 malayalam fraction one sixteenth\n3447 malayalam fraction one eighth\n3448 malayalam fraction three sixteenths\n3449 malayalam date mark\n3450 malayalam letter chillu nn\n3451 malayalam letter chillu n\n3452 malayalam letter chillu rr\n3453 malayalam letter chillu l\n3454 malayalam letter chillu ll\n3455 malayalam letter chillu k\n3457 sinhala sign candrabindu\n3458 sinhala sign anusvaraya\n3459 sinhala sign visargaya\n3461 sinhala letter ayanna\n3462 sinhala letter aayanna\n3463 sinhala letter aeyanna\n3464 sinhala letter aeeyanna\n3465 sinhala letter iyanna\n3466 sinhala letter iiyanna\n3467 sinhala letter uyanna\n3468 sinhala letter uuyanna\n3469 sinhala letter iruyanna\n3470 sinhala letter iruuyanna\n3471 sinhala letter iluyanna\n3472 sinhala letter iluuyanna\n3473 sinhala letter eyanna\n3474 sinhala letter eeyanna\n3475 sinhala letter aiyanna\n3476 sinhala letter oyanna\n3477 sinhala letter ooyanna\n3478 sinhala letter auyanna\n3482 sinhala letter alpapraana kayanna\n3483 sinhala letter mahaapraana kayanna\n3484 sinhala letter alpapraana gayanna\n3485 sinhala letter mahaapraana gayanna\n3486 sinhala letter kantaja naasikyaya\n3487 sinhala letter sanyaka gayanna\n3488 sinhala letter alpapraana cayanna\n3489 sinhala letter mahaapraana cayanna\n3490 sinhala letter alpapraana jayanna\n3491 sinhala letter mahaapraana jayanna\n3492 sinhala letter taaluja naasikyaya\n3493 sinhala letter taaluja sanyooga naaksikyaya\n3494 sinhala letter sanyaka jayanna\n3495 sinhala letter alpapraana ttayanna\n3496 sinhala letter mahaapraana ttayanna\n3497 sinhala letter alpapraana ddayanna\n3498 sinhala letter mahaapraana ddayanna\n3499 sinhala letter muurdhaja nayanna\n3500 sinhala letter sanyaka ddayanna\n3501 sinhala letter alpapraana tayanna\n3502 sinhala letter mahaapraana tayanna\n3503 sinhala letter alpapraana dayanna\n3504 sinhala letter mahaapraana dayanna\n3505 sinhala letter dantaja nayanna\n3507 sinhala letter sanyaka dayanna\n3508 sinhala letter alpapraana payanna\n3509 sinhala letter mahaapraana payanna\n3510 sinhala letter alpapraana bayanna\n3511 sinhala letter mahaapraana bayanna\n3512 sinhala letter mayanna\n3513 sinhala letter amba bayanna\n3514 sinhala letter yayanna\n3515 sinhala letter rayanna\n3517 sinhala letter dantaja layanna\n3520 sinhala letter vayanna\n3521 sinhala letter taaluja sayanna\n3522 sinhala letter muurdhaja sayanna\n3523 sinhala letter dantaja sayanna\n3524 sinhala letter hayanna\n3525 sinhala letter muurdhaja layanna\n3526 sinhala letter fayanna\n3530 sinhala sign al-lakuna\n3535 sinhala vowel sign aela-pilla\n3536 sinhala vowel sign ketti aeda-pilla\n3537 sinhala vowel sign diga aeda-pilla\n3538 sinhala vowel sign ketti is-pilla\n3539 sinhala vowel sign diga is-pilla\n3540 sinhala vowel sign ketti paa-pilla\n3542 sinhala vowel sign diga paa-pilla\n3544 sinhala vowel sign gaetta-pilla\n3545 sinhala vowel sign kombuva\n3546 sinhala vowel sign diga kombuva\n3547 sinhala vowel sign kombu deka\n3548 sinhala vowel sign kombuva haa aela-pilla\n3549 sinhala vowel sign kombuva haa diga aela-pilla\n3550 sinhala vowel sign kombuva haa gayanukitta\n3551 sinhala vowel sign gayanukitta\n3558 sinhala lith digit zero\n3559 sinhala lith digit one\n3560 sinhala lith digit two\n3561 sinhala lith digit three\n3562 sinhala lith digit four\n3563 sinhala lith digit five\n3564 sinhala lith digit six\n3565 sinhala lith digit seven\n3566 sinhala lith digit eight\n3567 sinhala lith digit nine\n3570 sinhala vowel sign diga gaetta-pilla\n3571 sinhala vowel sign diga gayanukitta\n3572 sinhala punctuation kunddaliya\n3585 thai character ko kai\n3586 thai character kho khai\n3587 thai character kho khuat\n3588 thai character kho khwai\n3589 thai character kho khon\n3590 thai character kho rakhang\n3591 thai character ngo ngu\n3592 thai character cho chan\n3593 thai character cho ching\n3594 thai character cho chang\n3595 thai character so so\n3596 thai character cho choe\n3597 thai character yo ying\n3598 thai character do chada\n3599 thai character to patak\n3600 thai character tho than\n3601 thai character tho nangmontho\n3602 thai character tho phuthao\n3603 thai character no nen\n3604 thai character do dek\n3605 thai character to tao\n3606 thai character tho thung\n3607 thai character tho thahan\n3608 thai character tho thong\n3609 thai character no nu\n3610 thai character bo baimai\n3611 thai character po pla\n3612 thai character pho phung\n3613 thai character fo fa\n3614 thai character pho phan\n3615 thai character fo fan\n3616 thai character pho samphao\n3617 thai character mo ma\n3618 thai character yo yak\n3619 thai character ro rua\n3620 thai character ru\n3621 thai character lo ling\n3622 thai character lu\n3623 thai character wo waen\n3624 thai character so sala\n3625 thai character so rusi\n3626 thai character so sua\n3627 thai character ho hip\n3628 thai character lo chula\n3629 thai character o ang\n3630 thai character ho nokhuk\n3631 thai character paiyannoi\n3632 thai character sara a\n3633 thai character mai han-akat\n3634 thai character sara aa\n3635 thai character sara am\n3636 thai character sara i\n3637 thai character sara ii\n3638 thai character sara ue\n3639 thai character sara uee\n3640 thai character sara u\n3641 thai character sara uu\n3642 thai character phinthu\n3647 thai currency symbol baht\n3648 thai character sara e\n3649 thai character sara ae\n3650 thai character sara o\n3651 thai character sara ai maimuan\n3652 thai character sara ai maimalai\n3653 thai character lakkhangyao\n3654 thai character maiyamok\n3655 thai character maitaikhu\n3656 thai character mai ek\n3657 thai character mai tho\n3658 thai character mai tri\n3659 thai character mai chattawa\n3660 thai character thanthakhat\n3661 thai character nikhahit\n3662 thai character yamakkan\n3663 thai character fongman\n3664 thai digit zero\n3665 thai digit one\n3666 thai digit two\n3667 thai digit three\n3668 thai digit four\n3669 thai digit five\n3670 thai digit six\n3671 thai digit seven\n3672 thai digit eight\n3673 thai digit nine\n3674 thai character angkhankhu\n3675 thai character khomut\n3713 lao letter ko\n3714 lao letter kho sung\n3716 lao letter kho tam\n3718 lao letter pali gha\n3719 lao letter ngo\n3720 lao letter co\n3721 lao letter pali cha\n3722 lao letter so tam\n3724 lao letter pali jha\n3725 lao letter nyo\n3726 lao letter pali nya\n3727 lao letter pali tta\n3728 lao letter pali ttha\n3729 lao letter pali dda\n3730 lao letter pali ddha\n3731 lao letter pali nna\n3732 lao letter do\n3733 lao letter to\n3734 lao letter tho sung\n3735 lao letter tho tam\n3736 lao letter pali dha\n3737 lao letter no\n3738 lao letter bo\n3739 lao letter po\n3740 lao letter pho sung\n3741 lao letter fo tam\n3742 lao letter pho tam\n3743 lao letter fo sung\n3744 lao letter pali bha\n3745 lao letter mo\n3746 lao letter yo\n3747 lao letter lo ling\n3749 lao letter lo loot\n3751 lao letter wo\n3752 lao letter sanskrit sha\n3753 lao letter sanskrit ssa\n3754 lao letter so sung\n3755 lao letter ho sung\n3756 lao letter pali lla\n3757 lao letter o\n3758 lao letter ho tam\n3759 lao ellipsis\n3760 lao vowel sign a\n3761 lao vowel sign mai kan\n3762 lao vowel sign aa\n3763 lao vowel sign am\n3764 lao vowel sign i\n3765 lao vowel sign ii\n3766 lao vowel sign y\n3767 lao vowel sign yy\n3768 lao vowel sign u\n3769 lao vowel sign uu\n3770 lao sign pali virama\n3771 lao vowel sign mai kon\n3772 lao semivowel sign lo\n3773 lao semivowel sign nyo\n3776 lao vowel sign e\n3777 lao vowel sign ei\n3778 lao vowel sign o\n3779 lao vowel sign ay\n3780 lao vowel sign ai\n3782 lao ko la\n3784 lao tone mai ek\n3785 lao tone mai tho\n3786 lao tone mai ti\n3787 lao tone mai catawa\n3788 lao cancellation mark\n3789 lao niggahita\n3790 lao yamakkan\n3792 lao digit zero\n3793 lao digit one\n3794 lao digit two\n3795 lao digit three\n3796 lao digit four\n3797 lao digit five\n3798 lao digit six\n3799 lao digit seven\n3800 lao digit eight\n3801 lao digit nine\n3804 lao ho no\n3805 lao ho mo\n3806 lao letter khmu go\n3807 lao letter khmu nyo\n3840 tibetan syllable om\n3841 tibetan mark gter yig mgo truncated a\n3842 tibetan mark gter yig mgo -um rnam bcad ma\n3843 tibetan mark gter yig mgo -um gter tsheg ma\n3844 tibetan mark initial yig mgo mdun ma\n3845 tibetan mark closing yig mgo sgab ma\n3846 tibetan mark caret yig mgo phur shad ma\n3847 tibetan mark yig mgo tsheg shad ma\n3848 tibetan mark sbrul shad\n3849 tibetan mark bskur yig mgo\n3850 tibetan mark bka- shog yig mgo\n3851 tibetan mark intersyllabic tsheg\n3852 tibetan mark delimiter tsheg bstar\n3853 tibetan mark shad\n3854 tibetan mark nyis shad\n3855 tibetan mark tsheg shad\n3856 tibetan mark nyis tsheg shad\n3857 tibetan mark rin chen spungs shad\n3858 tibetan mark rgya gram shad\n3859 tibetan mark caret -dzud rtags me long can\n3860 tibetan mark gter tsheg\n3861 tibetan logotype sign chad rtags\n3862 tibetan logotype sign lhag rtags\n3863 tibetan astrological sign sgra gcan -char rtags\n3864 tibetan astrological sign -khyud pa\n3865 tibetan astrological sign sdong tshugs\n3866 tibetan sign rdel dkar gcig\n3867 tibetan sign rdel dkar gnyis\n3868 tibetan sign rdel dkar gsum\n3869 tibetan sign rdel nag gcig\n3870 tibetan sign rdel nag gnyis\n3871 tibetan sign rdel dkar rdel nag\n3872 tibetan digit zero\n3873 tibetan digit one\n3874 tibetan digit two\n3875 tibetan digit three\n3876 tibetan digit four\n3877 tibetan digit five\n3878 tibetan digit six\n3879 tibetan digit seven\n3880 tibetan digit eight\n3881 tibetan digit nine\n3882 tibetan digit half one\n3883 tibetan digit half two\n3884 tibetan digit half three\n3885 tibetan digit half four\n3886 tibetan digit half five\n3887 tibetan digit half six\n3888 tibetan digit half seven\n3889 tibetan digit half eight\n3890 tibetan digit half nine\n3891 tibetan digit half zero\n3892 tibetan mark bsdus rtags\n3893 tibetan mark ngas bzung nyi zla\n3894 tibetan mark caret -dzud rtags bzhi mig can\n3895 tibetan mark ngas bzung sgor rtags\n3896 tibetan mark che mgo\n3897 tibetan mark tsa -phru\n3898 tibetan mark gug rtags gyon\n3899 tibetan mark gug rtags gyas\n3900 tibetan mark ang khang gyon\n3901 tibetan mark ang khang gyas\n3902 tibetan sign yar tshes\n3903 tibetan sign mar tshes\n3904 tibetan letter ka\n3905 tibetan letter kha\n3906 tibetan letter ga\n3907 tibetan letter gha\n3908 tibetan letter nga\n3909 tibetan letter ca\n3910 tibetan letter cha\n3911 tibetan letter ja\n3913 tibetan letter nya\n3914 tibetan letter tta\n3915 tibetan letter ttha\n3916 tibetan letter dda\n3917 tibetan letter ddha\n3918 tibetan letter nna\n3919 tibetan letter ta\n3920 tibetan letter tha\n3921 tibetan letter da\n3922 tibetan letter dha\n3923 tibetan letter na\n3924 tibetan letter pa\n3925 tibetan letter pha\n3926 tibetan letter ba\n3927 tibetan letter bha\n3928 tibetan letter ma\n3929 tibetan letter tsa\n3930 tibetan letter tsha\n3931 tibetan letter dza\n3932 tibetan letter dzha\n3933 tibetan letter wa\n3934 tibetan letter zha\n3935 tibetan letter za\n3936 tibetan letter -a\n3937 tibetan letter ya\n3938 tibetan letter ra\n3939 tibetan letter la\n3940 tibetan letter sha\n3941 tibetan letter ssa\n3942 tibetan letter sa\n3943 tibetan letter ha\n3944 tibetan letter a\n3945 tibetan letter kssa\n3946 tibetan letter fixed-form ra\n3947 tibetan letter kka\n3948 tibetan letter rra\n3953 tibetan vowel sign aa\n3954 tibetan vowel sign i\n3955 tibetan vowel sign ii\n3956 tibetan vowel sign u\n3957 tibetan vowel sign uu\n3958 tibetan vowel sign vocalic r\n3959 tibetan vowel sign vocalic rr\n3960 tibetan vowel sign vocalic l\n3961 tibetan vowel sign vocalic ll\n3962 tibetan vowel sign e\n3963 tibetan vowel sign ee\n3964 tibetan vowel sign o\n3965 tibetan vowel sign oo\n3966 tibetan sign rjes su nga ro\n3967 tibetan sign rnam bcad\n3968 tibetan vowel sign reversed i\n3969 tibetan vowel sign reversed ii\n3970 tibetan sign nyi zla naa da\n3971 tibetan sign sna ldan\n3972 tibetan mark halanta\n3973 tibetan mark paluta\n3974 tibetan sign lci rtags\n3975 tibetan sign yang rtags\n3976 tibetan sign lce tsa can\n3977 tibetan sign mchu can\n3978 tibetan sign gru can rgyings\n3979 tibetan sign gru med rgyings\n3980 tibetan sign inverted mchu can\n3981 tibetan subjoined sign lce tsa can\n3982 tibetan subjoined sign mchu can\n3983 tibetan subjoined sign inverted mchu can\n3984 tibetan subjoined letter ka\n3985 tibetan subjoined letter kha\n3986 tibetan subjoined letter ga\n3987 tibetan subjoined letter gha\n3988 tibetan subjoined letter nga\n3989 tibetan subjoined letter ca\n3990 tibetan subjoined letter cha\n3991 tibetan subjoined letter ja\n3993 tibetan subjoined letter nya\n3994 tibetan subjoined letter tta\n3995 tibetan subjoined letter ttha\n3996 tibetan subjoined letter dda\n3997 tibetan subjoined letter ddha\n3998 tibetan subjoined letter nna\n3999 tibetan subjoined letter ta\n4000 tibetan subjoined letter tha\n4001 tibetan subjoined letter da\n4002 tibetan subjoined letter dha\n4003 tibetan subjoined letter na\n4004 tibetan subjoined letter pa\n4005 tibetan subjoined letter pha\n4006 tibetan subjoined letter ba\n4007 tibetan subjoined letter bha\n4008 tibetan subjoined letter ma\n4009 tibetan subjoined letter tsa\n4010 tibetan subjoined letter tsha\n4011 tibetan subjoined letter dza\n4012 tibetan subjoined letter dzha\n4013 tibetan subjoined letter wa\n4014 tibetan subjoined letter zha\n4015 tibetan subjoined letter za\n4016 tibetan subjoined letter -a\n4017 tibetan subjoined letter ya\n4018 tibetan subjoined letter ra\n4019 tibetan subjoined letter la\n4020 tibetan subjoined letter sha\n4021 tibetan subjoined letter ssa\n4022 tibetan subjoined letter sa\n4023 tibetan subjoined letter ha\n4024 tibetan subjoined letter a\n4025 tibetan subjoined letter kssa\n4026 tibetan subjoined letter fixed-form wa\n4027 tibetan subjoined letter fixed-form ya\n4028 tibetan subjoined letter fixed-form ra\n4030 tibetan ku ru kha\n4031 tibetan ku ru kha bzhi mig can\n4032 tibetan cantillation sign heavy beat\n4033 tibetan cantillation sign light beat\n4034 tibetan cantillation sign cang te-u\n4035 tibetan cantillation sign sbub -chal\n4036 tibetan symbol dril bu\n4037 tibetan symbol rdo rje\n4038 tibetan symbol padma gdan\n4039 tibetan symbol rdo rje rgya gram\n4040 tibetan symbol phur pa\n4041 tibetan symbol nor bu\n4042 tibetan symbol nor bu nyis -khyil\n4043 tibetan symbol nor bu gsum -khyil\n4044 tibetan symbol nor bu bzhi -khyil\n4046 tibetan sign rdel nag rdel dkar\n4047 tibetan sign rdel nag gsum\n4048 tibetan mark bska- shog gi mgo rgyan\n4049 tibetan mark mnyam yig gi mgo rgyan\n4050 tibetan mark nyis tsheg\n4051 tibetan mark initial brda rnying yig mgo mdun ma\n4052 tibetan mark closing brda rnying yig mgo sgab ma\n4053 right-facing svasti sign\n4054 left-facing svasti sign\n4055 right-facing svasti sign with dots\n4056 left-facing svasti sign with dots\n4057 tibetan mark leading mchan rtags\n4058 tibetan mark trailing mchan rtags\n4096 myanmar letter ka\n4097 myanmar letter kha\n4098 myanmar letter ga\n4099 myanmar letter gha\n4100 myanmar letter nga\n4101 myanmar letter ca\n4102 myanmar letter cha\n4103 myanmar letter ja\n4104 myanmar letter jha\n4105 myanmar letter nya\n4106 myanmar letter nnya\n4107 myanmar letter tta\n4108 myanmar letter ttha\n4109 myanmar letter dda\n4110 myanmar letter ddha\n4111 myanmar letter nna\n4112 myanmar letter ta\n4113 myanmar letter tha\n4114 myanmar letter da\n4115 myanmar letter dha\n4116 myanmar letter na\n4117 myanmar letter pa\n4118 myanmar letter pha\n4119 myanmar letter ba\n4120 myanmar letter bha\n4121 myanmar letter ma\n4122 myanmar letter ya\n4123 myanmar letter ra\n4124 myanmar letter la\n4125 myanmar letter wa\n4126 myanmar letter sa\n4127 myanmar letter ha\n4128 myanmar letter lla\n4129 myanmar letter a\n4130 myanmar letter shan a\n4131 myanmar letter i\n4132 myanmar letter ii\n4133 myanmar letter u\n4134 myanmar letter uu\n4135 myanmar letter e\n4136 myanmar letter mon e\n4137 myanmar letter o\n4138 myanmar letter au\n4139 myanmar vowel sign tall aa\n4140 myanmar vowel sign aa\n4141 myanmar vowel sign i\n4142 myanmar vowel sign ii\n4143 myanmar vowel sign u\n4144 myanmar vowel sign uu\n4145 myanmar vowel sign e\n4146 myanmar vowel sign ai\n4147 myanmar vowel sign mon ii\n4148 myanmar vowel sign mon o\n4149 myanmar vowel sign e above\n4150 myanmar sign anusvara\n4151 myanmar sign dot below\n4152 myanmar sign visarga\n4153 myanmar sign virama\n4154 myanmar sign asat\n4155 myanmar consonant sign medial ya\n4156 myanmar consonant sign medial ra\n4157 myanmar consonant sign medial wa\n4158 myanmar consonant sign medial ha\n4159 myanmar letter great sa\n4160 myanmar digit zero\n4161 myanmar digit one\n4162 myanmar digit two\n4163 myanmar digit three\n4164 myanmar digit four\n4165 myanmar digit five\n4166 myanmar digit six\n4167 myanmar digit seven\n4168 myanmar digit eight\n4169 myanmar digit nine\n4170 myanmar sign little section\n4171 myanmar sign section\n4172 myanmar symbol locative\n4173 myanmar symbol completed\n4174 myanmar symbol aforementioned\n4175 myanmar symbol genitive\n4176 myanmar letter sha\n4177 myanmar letter ssa\n4178 myanmar letter vocalic r\n4179 myanmar letter vocalic rr\n4180 myanmar letter vocalic l\n4181 myanmar letter vocalic ll\n4182 myanmar vowel sign vocalic r\n4183 myanmar vowel sign vocalic rr\n4184 myanmar vowel sign vocalic l\n4185 myanmar vowel sign vocalic ll\n4186 myanmar letter mon nga\n4187 myanmar letter mon jha\n4188 myanmar letter mon bba\n4189 myanmar letter mon bbe\n4190 myanmar consonant sign mon medial na\n4191 myanmar consonant sign mon medial ma\n4192 myanmar consonant sign mon medial la\n4193 myanmar letter sgaw karen sha\n4194 myanmar vowel sign sgaw karen eu\n4195 myanmar tone mark sgaw karen hathi\n4196 myanmar tone mark sgaw karen ke pho\n4197 myanmar letter western pwo karen tha\n4198 myanmar letter western pwo karen pwa\n4199 myanmar vowel sign western pwo karen eu\n4200 myanmar vowel sign western pwo karen ue\n4201 myanmar sign western pwo karen tone-1\n4202 myanmar sign western pwo karen tone-2\n4203 myanmar sign western pwo karen tone-3\n4204 myanmar sign western pwo karen tone-4\n4205 myanmar sign western pwo karen tone-5\n4206 myanmar letter eastern pwo karen nna\n4207 myanmar letter eastern pwo karen ywa\n4208 myanmar letter eastern pwo karen ghwa\n4209 myanmar vowel sign geba karen i\n4210 myanmar vowel sign kayah oe\n4211 myanmar vowel sign kayah u\n4212 myanmar vowel sign kayah ee\n4213 myanmar letter shan ka\n4214 myanmar letter shan kha\n4215 myanmar letter shan ga\n4216 myanmar letter shan ca\n4217 myanmar letter shan za\n4218 myanmar letter shan nya\n4219 myanmar letter shan da\n4220 myanmar letter shan na\n4221 myanmar letter shan pha\n4222 myanmar letter shan fa\n4223 myanmar letter shan ba\n4224 myanmar letter shan tha\n4225 myanmar letter shan ha\n4226 myanmar consonant sign shan medial wa\n4227 myanmar vowel sign shan aa\n4228 myanmar vowel sign shan e\n4229 myanmar vowel sign shan e above\n4230 myanmar vowel sign shan final y\n4231 myanmar sign shan tone-2\n4232 myanmar sign shan tone-3\n4233 myanmar sign shan tone-5\n4234 myanmar sign shan tone-6\n4235 myanmar sign shan council tone-2\n4236 myanmar sign shan council tone-3\n4237 myanmar sign shan council emphatic tone\n4238 myanmar letter rumai palaung fa\n4239 myanmar sign rumai palaung tone-5\n4240 myanmar shan digit zero\n4241 myanmar shan digit one\n4242 myanmar shan digit two\n4243 myanmar shan digit three\n4244 myanmar shan digit four\n4245 myanmar shan digit five\n4246 myanmar shan digit six\n4247 myanmar shan digit seven\n4248 myanmar shan digit eight\n4249 myanmar shan digit nine\n4250 myanmar sign khamti tone-1\n4251 myanmar sign khamti tone-3\n4252 myanmar vowel sign aiton a\n4253 myanmar vowel sign aiton ai\n4254 myanmar symbol shan one\n4255 myanmar symbol shan exclamation\n4256 georgian capital letter an\n4257 georgian capital letter ban\n4258 georgian capital letter gan\n4259 georgian capital letter don\n4260 georgian capital letter en\n4261 georgian capital letter vin\n4262 georgian capital letter zen\n4263 georgian capital letter tan\n4264 georgian capital letter in\n4265 georgian capital letter kan\n4266 georgian capital letter las\n4267 georgian capital letter man\n4268 georgian capital letter nar\n4269 georgian capital letter on\n4270 georgian capital letter par\n4271 georgian capital letter zhar\n4272 georgian capital letter rae\n4273 georgian capital letter san\n4274 georgian capital letter tar\n4275 georgian capital letter un\n4276 georgian capital letter phar\n4277 georgian capital letter khar\n4278 georgian capital letter ghan\n4279 georgian capital letter qar\n4280 georgian capital letter shin\n4281 georgian capital letter chin\n4282 georgian capital letter can\n4283 georgian capital letter jil\n4284 georgian capital letter cil\n4285 georgian capital letter char\n4286 georgian capital letter xan\n4287 georgian capital letter jhan\n4288 georgian capital letter hae\n4289 georgian capital letter he\n4290 georgian capital letter hie\n4291 georgian capital letter we\n4292 georgian capital letter har\n4293 georgian capital letter hoe\n4295 georgian capital letter yn\n4301 georgian capital letter aen\n4304 georgian letter an\n4305 georgian letter ban\n4306 georgian letter gan\n4307 georgian letter don\n4308 georgian letter en\n4309 georgian letter vin\n4310 georgian letter zen\n4311 georgian letter tan\n4312 georgian letter in\n4313 georgian letter kan\n4314 georgian letter las\n4315 georgian letter man\n4316 georgian letter nar\n4317 georgian letter on\n4318 georgian letter par\n4319 georgian letter zhar\n4320 georgian letter rae\n4321 georgian letter san\n4322 georgian letter tar\n4323 georgian letter un\n4324 georgian letter phar\n4325 georgian letter khar\n4326 georgian letter ghan\n4327 georgian letter qar\n4328 georgian letter shin\n4329 georgian letter chin\n4330 georgian letter can\n4331 georgian letter jil\n4332 georgian letter cil\n4333 georgian letter char\n4334 georgian letter xan\n4335 georgian letter jhan\n4336 georgian letter hae\n4337 georgian letter he\n4338 georgian letter hie\n4339 georgian letter we\n4340 georgian letter har\n4341 georgian letter hoe\n4342 georgian letter fi\n4343 georgian letter yn\n4344 georgian letter elifi\n4345 georgian letter turned gan\n4346 georgian letter ain\n4347 georgian paragraph separator\n4348 modifier letter georgian nar\n4349 georgian letter aen\n4350 georgian letter hard sign\n4351 georgian letter labial sign\n4352 hangul choseong kiyeok\n4353 hangul choseong ssangkiyeok\n4354 hangul choseong nieun\n4355 hangul choseong tikeut\n4356 hangul choseong ssangtikeut\n4357 hangul choseong rieul\n4358 hangul choseong mieum\n4359 hangul choseong pieup\n4360 hangul choseong ssangpieup\n4361 hangul choseong sios\n4362 hangul choseong ssangsios\n4363 hangul choseong ieung\n4364 hangul choseong cieuc\n4365 hangul choseong ssangcieuc\n4366 hangul choseong chieuch\n4367 hangul choseong khieukh\n4368 hangul choseong thieuth\n4369 hangul choseong phieuph\n4370 hangul choseong hieuh\n4371 hangul choseong nieun-kiyeok\n4372 hangul choseong ssangnieun\n4373 hangul choseong nieun-tikeut\n4374 hangul choseong nieun-pieup\n4375 hangul choseong tikeut-kiyeok\n4376 hangul choseong rieul-nieun\n4377 hangul choseong ssangrieul\n4378 hangul choseong rieul-hieuh\n4379 hangul choseong kapyeounrieul\n4380 hangul choseong mieum-pieup\n4381 hangul choseong kapyeounmieum\n4382 hangul choseong pieup-kiyeok\n4383 hangul choseong pieup-nieun\n4384 hangul choseong pieup-tikeut\n4385 hangul choseong pieup-sios\n4386 hangul choseong pieup-sios-kiyeok\n4387 hangul choseong pieup-sios-tikeut\n4388 hangul choseong pieup-sios-pieup\n4389 hangul choseong pieup-ssangsios\n4390 hangul choseong pieup-sios-cieuc\n4391 hangul choseong pieup-cieuc\n4392 hangul choseong pieup-chieuch\n4393 hangul choseong pieup-thieuth\n4394 hangul choseong pieup-phieuph\n4395 hangul choseong kapyeounpieup\n4396 hangul choseong kapyeounssangpieup\n4397 hangul choseong sios-kiyeok\n4398 hangul choseong sios-nieun\n4399 hangul choseong sios-tikeut\n4400 hangul choseong sios-rieul\n4401 hangul choseong sios-mieum\n4402 hangul choseong sios-pieup\n4403 hangul choseong sios-pieup-kiyeok\n4404 hangul choseong sios-ssangsios\n4405 hangul choseong sios-ieung\n4406 hangul choseong sios-cieuc\n4407 hangul choseong sios-chieuch\n4408 hangul choseong sios-khieukh\n4409 hangul choseong sios-thieuth\n4410 hangul choseong sios-phieuph\n4411 hangul choseong sios-hieuh\n4412 hangul choseong chitueumsios\n4413 hangul choseong chitueumssangsios\n4414 hangul choseong ceongchieumsios\n4415 hangul choseong ceongchieumssangsios\n4416 hangul choseong pansios\n4417 hangul choseong ieung-kiyeok\n4418 hangul choseong ieung-tikeut\n4419 hangul choseong ieung-mieum\n4420 hangul choseong ieung-pieup\n4421 hangul choseong ieung-sios\n4422 hangul choseong ieung-pansios\n4423 hangul choseong ssangieung\n4424 hangul choseong ieung-cieuc\n4425 hangul choseong ieung-chieuch\n4426 hangul choseong ieung-thieuth\n4427 hangul choseong ieung-phieuph\n4428 hangul choseong yesieung\n4429 hangul choseong cieuc-ieung\n4430 hangul choseong chitueumcieuc\n4431 hangul choseong chitueumssangcieuc\n4432 hangul choseong ceongchieumcieuc\n4433 hangul choseong ceongchieumssangcieuc\n4434 hangul choseong chieuch-khieukh\n4435 hangul choseong chieuch-hieuh\n4436 hangul choseong chitueumchieuch\n4437 hangul choseong ceongchieumchieuch\n4438 hangul choseong phieuph-pieup\n4439 hangul choseong kapyeounphieuph\n4440 hangul choseong ssanghieuh\n4441 hangul choseong yeorinhieuh\n4442 hangul choseong kiyeok-tikeut\n4443 hangul choseong nieun-sios\n4444 hangul choseong nieun-cieuc\n4445 hangul choseong nieun-hieuh\n4446 hangul choseong tikeut-rieul\n4447 hangul choseong filler\n4448 hangul jungseong filler\n4449 hangul jungseong a\n4450 hangul jungseong ae\n4451 hangul jungseong ya\n4452 hangul jungseong yae\n4453 hangul jungseong eo\n4454 hangul jungseong e\n4455 hangul jungseong yeo\n4456 hangul jungseong ye\n4457 hangul jungseong o\n4458 hangul jungseong wa\n4459 hangul jungseong wae\n4460 hangul jungseong oe\n4461 hangul jungseong yo\n4462 hangul jungseong u\n4463 hangul jungseong weo\n4464 hangul jungseong we\n4465 hangul jungseong wi\n4466 hangul jungseong yu\n4467 hangul jungseong eu\n4468 hangul jungseong yi\n4469 hangul jungseong i\n4470 hangul jungseong a-o\n4471 hangul jungseong a-u\n4472 hangul jungseong ya-o\n4473 hangul jungseong ya-yo\n4474 hangul jungseong eo-o\n4475 hangul jungseong eo-u\n4476 hangul jungseong eo-eu\n4477 hangul jungseong yeo-o\n4478 hangul jungseong yeo-u\n4479 hangul jungseong o-eo\n4480 hangul jungseong o-e\n4481 hangul jungseong o-ye\n4482 hangul jungseong o-o\n4483 hangul jungseong o-u\n4484 hangul jungseong yo-ya\n4485 hangul jungseong yo-yae\n4486 hangul jungseong yo-yeo\n4487 hangul jungseong yo-o\n4488 hangul jungseong yo-i\n4489 hangul jungseong u-a\n4490 hangul jungseong u-ae\n4491 hangul jungseong u-eo-eu\n4492 hangul jungseong u-ye\n4493 hangul jungseong u-u\n4494 hangul jungseong yu-a\n4495 hangul jungseong yu-eo\n4496 hangul jungseong yu-e\n4497 hangul jungseong yu-yeo\n4498 hangul jungseong yu-ye\n4499 hangul jungseong yu-u\n4500 hangul jungseong yu-i\n4501 hangul jungseong eu-u\n4502 hangul jungseong eu-eu\n4503 hangul jungseong yi-u\n4504 hangul jungseong i-a\n4505 hangul jungseong i-ya\n4506 hangul jungseong i-o\n4507 hangul jungseong i-u\n4508 hangul jungseong i-eu\n4509 hangul jungseong i-araea\n4510 hangul jungseong araea\n4511 hangul jungseong araea-eo\n4512 hangul jungseong araea-u\n4513 hangul jungseong araea-i\n4514 hangul jungseong ssangaraea\n4515 hangul jungseong a-eu\n4516 hangul jungseong ya-u\n4517 hangul jungseong yeo-ya\n4518 hangul jungseong o-ya\n4519 hangul jungseong o-yae\n4520 hangul jongseong kiyeok\n4521 hangul jongseong ssangkiyeok\n4522 hangul jongseong kiyeok-sios\n4523 hangul jongseong nieun\n4524 hangul jongseong nieun-cieuc\n4525 hangul jongseong nieun-hieuh\n4526 hangul jongseong tikeut\n4527 hangul jongseong rieul\n4528 hangul jongseong rieul-kiyeok\n4529 hangul jongseong rieul-mieum\n4530 hangul jongseong rieul-pieup\n4531 hangul jongseong rieul-sios\n4532 hangul jongseong rieul-thieuth\n4533 hangul jongseong rieul-phieuph\n4534 hangul jongseong rieul-hieuh\n4535 hangul jongseong mieum\n4536 hangul jongseong pieup\n4537 hangul jongseong pieup-sios\n4538 hangul jongseong sios\n4539 hangul jongseong ssangsios\n4540 hangul jongseong ieung\n4541 hangul jongseong cieuc\n4542 hangul jongseong chieuch\n4543 hangul jongseong khieukh\n4544 hangul jongseong thieuth\n4545 hangul jongseong phieuph\n4546 hangul jongseong hieuh\n4547 hangul jongseong kiyeok-rieul\n4548 hangul jongseong kiyeok-sios-kiyeok\n4549 hangul jongseong nieun-kiyeok\n4550 hangul jongseong nieun-tikeut\n4551 hangul jongseong nieun-sios\n4552 hangul jongseong nieun-pansios\n4553 hangul jongseong nieun-thieuth\n4554 hangul jongseong tikeut-kiyeok\n4555 hangul jongseong tikeut-rieul\n4556 hangul jongseong rieul-kiyeok-sios\n4557 hangul jongseong rieul-nieun\n4558 hangul jongseong rieul-tikeut\n4559 hangul jongseong rieul-tikeut-hieuh\n4560 hangul jongseong ssangrieul\n4561 hangul jongseong rieul-mieum-kiyeok\n4562 hangul jongseong rieul-mieum-sios\n4563 hangul jongseong rieul-pieup-sios\n4564 hangul jongseong rieul-pieup-hieuh\n4565 hangul jongseong rieul-kapyeounpieup\n4566 hangul jongseong rieul-ssangsios\n4567 hangul jongseong rieul-pansios\n4568 hangul jongseong rieul-khieukh\n4569 hangul jongseong rieul-yeorinhieuh\n4570 hangul jongseong mieum-kiyeok\n4571 hangul jongseong mieum-rieul\n4572 hangul jongseong mieum-pieup\n4573 hangul jongseong mieum-sios\n4574 hangul jongseong mieum-ssangsios\n4575 hangul jongseong mieum-pansios\n4576 hangul jongseong mieum-chieuch\n4577 hangul jongseong mieum-hieuh\n4578 hangul jongseong kapyeounmieum\n4579 hangul jongseong pieup-rieul\n4580 hangul jongseong pieup-phieuph\n4581 hangul jongseong pieup-hieuh\n4582 hangul jongseong kapyeounpieup\n4583 hangul jongseong sios-kiyeok\n4584 hangul jongseong sios-tikeut\n4585 hangul jongseong sios-rieul\n4586 hangul jongseong sios-pieup\n4587 hangul jongseong pansios\n4588 hangul jongseong ieung-kiyeok\n4589 hangul jongseong ieung-ssangkiyeok\n4590 hangul jongseong ssangieung\n4591 hangul jongseong ieung-khieukh\n4592 hangul jongseong yesieung\n4593 hangul jongseong yesieung-sios\n4594 hangul jongseong yesieung-pansios\n4595 hangul jongseong phieuph-pieup\n4596 hangul jongseong kapyeounphieuph\n4597 hangul jongseong hieuh-nieun\n4598 hangul jongseong hieuh-rieul\n4599 hangul jongseong hieuh-mieum\n4600 hangul jongseong hieuh-pieup\n4601 hangul jongseong yeorinhieuh\n4602 hangul jongseong kiyeok-nieun\n4603 hangul jongseong kiyeok-pieup\n4604 hangul jongseong kiyeok-chieuch\n4605 hangul jongseong kiyeok-khieukh\n4606 hangul jongseong kiyeok-hieuh\n4607 hangul jongseong ssangnieun\n4608 ethiopic syllable ha\n4609 ethiopic syllable hu\n4610 ethiopic syllable hi\n4611 ethiopic syllable haa\n4612 ethiopic syllable hee\n4613 ethiopic syllable he\n4614 ethiopic syllable ho\n4615 ethiopic syllable hoa\n4616 ethiopic syllable la\n4617 ethiopic syllable lu\n4618 ethiopic syllable li\n4619 ethiopic syllable laa\n4620 ethiopic syllable lee\n4621 ethiopic syllable le\n4622 ethiopic syllable lo\n4623 ethiopic syllable lwa\n4624 ethiopic syllable hha\n4625 ethiopic syllable hhu\n4626 ethiopic syllable hhi\n4627 ethiopic syllable hhaa\n4628 ethiopic syllable hhee\n4629 ethiopic syllable hhe\n4630 ethiopic syllable hho\n4631 ethiopic syllable hhwa\n4632 ethiopic syllable ma\n4633 ethiopic syllable mu\n4634 ethiopic syllable mi\n4635 ethiopic syllable maa\n4636 ethiopic syllable mee\n4637 ethiopic syllable me\n4638 ethiopic syllable mo\n4639 ethiopic syllable mwa\n4640 ethiopic syllable sza\n4641 ethiopic syllable szu\n4642 ethiopic syllable szi\n4643 ethiopic syllable szaa\n4644 ethiopic syllable szee\n4645 ethiopic syllable sze\n4646 ethiopic syllable szo\n4647 ethiopic syllable szwa\n4648 ethiopic syllable ra\n4649 ethiopic syllable ru\n4650 ethiopic syllable ri\n4651 ethiopic syllable raa\n4652 ethiopic syllable ree\n4653 ethiopic syllable re\n4654 ethiopic syllable ro\n4655 ethiopic syllable rwa\n4656 ethiopic syllable sa\n4657 ethiopic syllable su\n4658 ethiopic syllable si\n4659 ethiopic syllable saa\n4660 ethiopic syllable see\n4661 ethiopic syllable se\n4662 ethiopic syllable so\n4663 ethiopic syllable swa\n4664 ethiopic syllable sha\n4665 ethiopic syllable shu\n4666 ethiopic syllable shi\n4667 ethiopic syllable shaa\n4668 ethiopic syllable shee\n4669 ethiopic syllable she\n4670 ethiopic syllable sho\n4671 ethiopic syllable shwa\n4672 ethiopic syllable qa\n4673 ethiopic syllable qu\n4674 ethiopic syllable qi\n4675 ethiopic syllable qaa\n4676 ethiopic syllable qee\n4677 ethiopic syllable qe\n4678 ethiopic syllable qo\n4679 ethiopic syllable qoa\n4680 ethiopic syllable qwa\n4682 ethiopic syllable qwi\n4683 ethiopic syllable qwaa\n4684 ethiopic syllable qwee\n4685 ethiopic syllable qwe\n4688 ethiopic syllable qha\n4689 ethiopic syllable qhu\n4690 ethiopic syllable qhi\n4691 ethiopic syllable qhaa\n4692 ethiopic syllable qhee\n4693 ethiopic syllable qhe\n4694 ethiopic syllable qho\n4696 ethiopic syllable qhwa\n4698 ethiopic syllable qhwi\n4699 ethiopic syllable qhwaa\n4700 ethiopic syllable qhwee\n4701 ethiopic syllable qhwe\n4704 ethiopic syllable ba\n4705 ethiopic syllable bu\n4706 ethiopic syllable bi\n4707 ethiopic syllable baa\n4708 ethiopic syllable bee\n4709 ethiopic syllable be\n4710 ethiopic syllable bo\n4711 ethiopic syllable bwa\n4712 ethiopic syllable va\n4713 ethiopic syllable vu\n4714 ethiopic syllable vi\n4715 ethiopic syllable vaa\n4716 ethiopic syllable vee\n4717 ethiopic syllable ve\n4718 ethiopic syllable vo\n4719 ethiopic syllable vwa\n4720 ethiopic syllable ta\n4721 ethiopic syllable tu\n4722 ethiopic syllable ti\n4723 ethiopic syllable taa\n4724 ethiopic syllable tee\n4725 ethiopic syllable te\n4726 ethiopic syllable to\n4727 ethiopic syllable twa\n4728 ethiopic syllable ca\n4729 ethiopic syllable cu\n4730 ethiopic syllable ci\n4731 ethiopic syllable caa\n4732 ethiopic syllable cee\n4733 ethiopic syllable ce\n4734 ethiopic syllable co\n4735 ethiopic syllable cwa\n4736 ethiopic syllable xa\n4737 ethiopic syllable xu\n4738 ethiopic syllable xi\n4739 ethiopic syllable xaa\n4740 ethiopic syllable xee\n4741 ethiopic syllable xe\n4742 ethiopic syllable xo\n4743 ethiopic syllable xoa\n4744 ethiopic syllable xwa\n4746 ethiopic syllable xwi\n4747 ethiopic syllable xwaa\n4748 ethiopic syllable xwee\n4749 ethiopic syllable xwe\n4752 ethiopic syllable na\n4753 ethiopic syllable nu\n4754 ethiopic syllable ni\n4755 ethiopic syllable naa\n4756 ethiopic syllable nee\n4757 ethiopic syllable ne\n4758 ethiopic syllable no\n4759 ethiopic syllable nwa\n4760 ethiopic syllable nya\n4761 ethiopic syllable nyu\n4762 ethiopic syllable nyi\n4763 ethiopic syllable nyaa\n4764 ethiopic syllable nyee\n4765 ethiopic syllable nye\n4766 ethiopic syllable nyo\n4767 ethiopic syllable nywa\n4768 ethiopic syllable glottal a\n4769 ethiopic syllable glottal u\n4770 ethiopic syllable glottal i\n4771 ethiopic syllable glottal aa\n4772 ethiopic syllable glottal ee\n4773 ethiopic syllable glottal e\n4774 ethiopic syllable glottal o\n4775 ethiopic syllable glottal wa\n4776 ethiopic syllable ka\n4777 ethiopic syllable ku\n4778 ethiopic syllable ki\n4779 ethiopic syllable kaa\n4780 ethiopic syllable kee\n4781 ethiopic syllable ke\n4782 ethiopic syllable ko\n4783 ethiopic syllable koa\n4784 ethiopic syllable kwa\n4786 ethiopic syllable kwi\n4787 ethiopic syllable kwaa\n4788 ethiopic syllable kwee\n4789 ethiopic syllable kwe\n4792 ethiopic syllable kxa\n4793 ethiopic syllable kxu\n4794 ethiopic syllable kxi\n4795 ethiopic syllable kxaa\n4796 ethiopic syllable kxee\n4797 ethiopic syllable kxe\n4798 ethiopic syllable kxo\n4800 ethiopic syllable kxwa\n4802 ethiopic syllable kxwi\n4803 ethiopic syllable kxwaa\n4804 ethiopic syllable kxwee\n4805 ethiopic syllable kxwe\n4808 ethiopic syllable wa\n4809 ethiopic syllable wu\n4810 ethiopic syllable wi\n4811 ethiopic syllable waa\n4812 ethiopic syllable wee\n4813 ethiopic syllable we\n4814 ethiopic syllable wo\n4815 ethiopic syllable woa\n4816 ethiopic syllable pharyngeal a\n4817 ethiopic syllable pharyngeal u\n4818 ethiopic syllable pharyngeal i\n4819 ethiopic syllable pharyngeal aa\n4820 ethiopic syllable pharyngeal ee\n4821 ethiopic syllable pharyngeal e\n4822 ethiopic syllable pharyngeal o\n4824 ethiopic syllable za\n4825 ethiopic syllable zu\n4826 ethiopic syllable zi\n4827 ethiopic syllable zaa\n4828 ethiopic syllable zee\n4829 ethiopic syllable ze\n4830 ethiopic syllable zo\n4831 ethiopic syllable zwa\n4832 ethiopic syllable zha\n4833 ethiopic syllable zhu\n4834 ethiopic syllable zhi\n4835 ethiopic syllable zhaa\n4836 ethiopic syllable zhee\n4837 ethiopic syllable zhe\n4838 ethiopic syllable zho\n4839 ethiopic syllable zhwa\n4840 ethiopic syllable ya\n4841 ethiopic syllable yu\n4842 ethiopic syllable yi\n4843 ethiopic syllable yaa\n4844 ethiopic syllable yee\n4845 ethiopic syllable ye\n4846 ethiopic syllable yo\n4847 ethiopic syllable yoa\n4848 ethiopic syllable da\n4849 ethiopic syllable du\n4850 ethiopic syllable di\n4851 ethiopic syllable daa\n4852 ethiopic syllable dee\n4853 ethiopic syllable de\n4854 ethiopic syllable do\n4855 ethiopic syllable dwa\n4856 ethiopic syllable dda\n4857 ethiopic syllable ddu\n4858 ethiopic syllable ddi\n4859 ethiopic syllable ddaa\n4860 ethiopic syllable ddee\n4861 ethiopic syllable dde\n4862 ethiopic syllable ddo\n4863 ethiopic syllable ddwa\n4864 ethiopic syllable ja\n4865 ethiopic syllable ju\n4866 ethiopic syllable ji\n4867 ethiopic syllable jaa\n4868 ethiopic syllable jee\n4869 ethiopic syllable je\n4870 ethiopic syllable jo\n4871 ethiopic syllable jwa\n4872 ethiopic syllable ga\n4873 ethiopic syllable gu\n4874 ethiopic syllable gi\n4875 ethiopic syllable gaa\n4876 ethiopic syllable gee\n4877 ethiopic syllable ge\n4878 ethiopic syllable go\n4879 ethiopic syllable goa\n4880 ethiopic syllable gwa\n4882 ethiopic syllable gwi\n4883 ethiopic syllable gwaa\n4884 ethiopic syllable gwee\n4885 ethiopic syllable gwe\n4888 ethiopic syllable gga\n4889 ethiopic syllable ggu\n4890 ethiopic syllable ggi\n4891 ethiopic syllable ggaa\n4892 ethiopic syllable ggee\n4893 ethiopic syllable gge\n4894 ethiopic syllable ggo\n4895 ethiopic syllable ggwaa\n4896 ethiopic syllable tha\n4897 ethiopic syllable thu\n4898 ethiopic syllable thi\n4899 ethiopic syllable thaa\n4900 ethiopic syllable thee\n4901 ethiopic syllable the\n4902 ethiopic syllable tho\n4903 ethiopic syllable thwa\n4904 ethiopic syllable cha\n4905 ethiopic syllable chu\n4906 ethiopic syllable chi\n4907 ethiopic syllable chaa\n4908 ethiopic syllable chee\n4909 ethiopic syllable che\n4910 ethiopic syllable cho\n4911 ethiopic syllable chwa\n4912 ethiopic syllable pha\n4913 ethiopic syllable phu\n4914 ethiopic syllable phi\n4915 ethiopic syllable phaa\n4916 ethiopic syllable phee\n4917 ethiopic syllable phe\n4918 ethiopic syllable pho\n4919 ethiopic syllable phwa\n4920 ethiopic syllable tsa\n4921 ethiopic syllable tsu\n4922 ethiopic syllable tsi\n4923 ethiopic syllable tsaa\n4924 ethiopic syllable tsee\n4925 ethiopic syllable tse\n4926 ethiopic syllable tso\n4927 ethiopic syllable tswa\n4928 ethiopic syllable tza\n4929 ethiopic syllable tzu\n4930 ethiopic syllable tzi\n4931 ethiopic syllable tzaa\n4932 ethiopic syllable tzee\n4933 ethiopic syllable tze\n4934 ethiopic syllable tzo\n4935 ethiopic syllable tzoa\n4936 ethiopic syllable fa\n4937 ethiopic syllable fu\n4938 ethiopic syllable fi\n4939 ethiopic syllable faa\n4940 ethiopic syllable fee\n4941 ethiopic syllable fe\n4942 ethiopic syllable fo\n4943 ethiopic syllable fwa\n4944 ethiopic syllable pa\n4945 ethiopic syllable pu\n4946 ethiopic syllable pi\n4947 ethiopic syllable paa\n4948 ethiopic syllable pee\n4949 ethiopic syllable pe\n4950 ethiopic syllable po\n4951 ethiopic syllable pwa\n4952 ethiopic syllable rya\n4953 ethiopic syllable mya\n4954 ethiopic syllable fya\n4957 ethiopic combining gemination and vowel length mark\n4958 ethiopic combining vowel length mark\n4959 ethiopic combining gemination mark\n4960 ethiopic section mark\n4961 ethiopic wordspace\n4962 ethiopic full stop\n4963 ethiopic comma\n4964 ethiopic semicolon\n4965 ethiopic colon\n4966 ethiopic preface colon\n4967 ethiopic question mark\n4968 ethiopic paragraph separator\n4969 ethiopic digit one\n4970 ethiopic digit two\n4971 ethiopic digit three\n4972 ethiopic digit four\n4973 ethiopic digit five\n4974 ethiopic digit six\n4975 ethiopic digit seven\n4976 ethiopic digit eight\n4977 ethiopic digit nine\n4978 ethiopic number ten\n4979 ethiopic number twenty\n4980 ethiopic number thirty\n4981 ethiopic number forty\n4982 ethiopic number fifty\n4983 ethiopic number sixty\n4984 ethiopic number seventy\n4985 ethiopic number eighty\n4986 ethiopic number ninety\n4987 ethiopic number hundred\n4988 ethiopic number ten thousand\n4992 ethiopic syllable sebatbeit mwa\n4993 ethiopic syllable mwi\n4994 ethiopic syllable mwee\n4995 ethiopic syllable mwe\n4996 ethiopic syllable sebatbeit bwa\n4997 ethiopic syllable bwi\n4998 ethiopic syllable bwee\n4999 ethiopic syllable bwe\n5000 ethiopic syllable sebatbeit fwa\n5001 ethiopic syllable fwi\n5002 ethiopic syllable fwee\n5003 ethiopic syllable fwe\n5004 ethiopic syllable sebatbeit pwa\n5005 ethiopic syllable pwi\n5006 ethiopic syllable pwee\n5007 ethiopic syllable pwe\n5008 ethiopic tonal mark yizet\n5009 ethiopic tonal mark deret\n5010 ethiopic tonal mark rikrik\n5011 ethiopic tonal mark short rikrik\n5012 ethiopic tonal mark difat\n5013 ethiopic tonal mark kenat\n5014 ethiopic tonal mark chiret\n5015 ethiopic tonal mark hidet\n5016 ethiopic tonal mark deret-hidet\n5017 ethiopic tonal mark kurt\n5024 cherokee letter a\n5025 cherokee letter e\n5026 cherokee letter i\n5027 cherokee letter o\n5028 cherokee letter u\n5029 cherokee letter v\n5030 cherokee letter ga\n5031 cherokee letter ka\n5032 cherokee letter ge\n5033 cherokee letter gi\n5034 cherokee letter go\n5035 cherokee letter gu\n5036 cherokee letter gv\n5037 cherokee letter ha\n5038 cherokee letter he\n5039 cherokee letter hi\n5040 cherokee letter ho\n5041 cherokee letter hu\n5042 cherokee letter hv\n5043 cherokee letter la\n5044 cherokee letter le\n5045 cherokee letter li\n5046 cherokee letter lo\n5047 cherokee letter lu\n5048 cherokee letter lv\n5049 cherokee letter ma\n5050 cherokee letter me\n5051 cherokee letter mi\n5052 cherokee letter mo\n5053 cherokee letter mu\n5054 cherokee letter na\n5055 cherokee letter hna\n5056 cherokee letter nah\n5057 cherokee letter ne\n5058 cherokee letter ni\n5059 cherokee letter no\n5060 cherokee letter nu\n5061 cherokee letter nv\n5062 cherokee letter qua\n5063 cherokee letter que\n5064 cherokee letter qui\n5065 cherokee letter quo\n5066 cherokee letter quu\n5067 cherokee letter quv\n5068 cherokee letter sa\n5069 cherokee letter s\n5070 cherokee letter se\n5071 cherokee letter si\n5072 cherokee letter so\n5073 cherokee letter su\n5074 cherokee letter sv\n5075 cherokee letter da\n5076 cherokee letter ta\n5077 cherokee letter de\n5078 cherokee letter te\n5079 cherokee letter di\n5080 cherokee letter ti\n5081 cherokee letter do\n5082 cherokee letter du\n5083 cherokee letter dv\n5084 cherokee letter dla\n5085 cherokee letter tla\n5086 cherokee letter tle\n5087 cherokee letter tli\n5088 cherokee letter tlo\n5089 cherokee letter tlu\n5090 cherokee letter tlv\n5091 cherokee letter tsa\n5092 cherokee letter tse\n5093 cherokee letter tsi\n5094 cherokee letter tso\n5095 cherokee letter tsu\n5096 cherokee letter tsv\n5097 cherokee letter wa\n5098 cherokee letter we\n5099 cherokee letter wi\n5100 cherokee letter wo\n5101 cherokee letter wu\n5102 cherokee letter wv\n5103 cherokee letter ya\n5104 cherokee letter ye\n5105 cherokee letter yi\n5106 cherokee letter yo\n5107 cherokee letter yu\n5108 cherokee letter yv\n5109 cherokee letter mv\n5112 cherokee small letter ye\n5113 cherokee small letter yi\n5114 cherokee small letter yo\n5115 cherokee small letter yu\n5116 cherokee small letter yv\n5117 cherokee small letter mv\n5120 canadian syllabics hyphen\n5121 canadian syllabics e\n5122 canadian syllabics aai\n5123 canadian syllabics i\n5124 canadian syllabics ii\n5125 canadian syllabics o\n5126 canadian syllabics oo\n5127 canadian syllabics y-cree oo\n5128 canadian syllabics carrier ee\n5129 canadian syllabics carrier i\n5130 canadian syllabics a\n5131 canadian syllabics aa\n5132 canadian syllabics we\n5133 canadian syllabics west-cree we\n5134 canadian syllabics wi\n5135 canadian syllabics west-cree wi\n5136 canadian syllabics wii\n5137 canadian syllabics west-cree wii\n5138 canadian syllabics wo\n5139 canadian syllabics west-cree wo\n5140 canadian syllabics woo\n5141 canadian syllabics west-cree woo\n5142 canadian syllabics naskapi woo\n5143 canadian syllabics wa\n5144 canadian syllabics west-cree wa\n5145 canadian syllabics waa\n5146 canadian syllabics west-cree waa\n5147 canadian syllabics naskapi waa\n5148 canadian syllabics ai\n5149 canadian syllabics y-cree w\n5150 canadian syllabics glottal stop\n5151 canadian syllabics final acute\n5152 canadian syllabics final grave\n5153 canadian syllabics final bottom half ring\n5154 canadian syllabics final top half ring\n5155 canadian syllabics final right half ring\n5156 canadian syllabics final ring\n5157 canadian syllabics final double acute\n5158 canadian syllabics final double short vertical strokes\n5159 canadian syllabics final middle dot\n5160 canadian syllabics final short horizontal stroke\n5161 canadian syllabics final plus\n5162 canadian syllabics final down tack\n5163 canadian syllabics en\n5164 canadian syllabics in\n5165 canadian syllabics on\n5166 canadian syllabics an\n5167 canadian syllabics pe\n5168 canadian syllabics paai\n5169 canadian syllabics pi\n5170 canadian syllabics pii\n5171 canadian syllabics po\n5172 canadian syllabics poo\n5173 canadian syllabics y-cree poo\n5174 canadian syllabics carrier hee\n5175 canadian syllabics carrier hi\n5176 canadian syllabics pa\n5177 canadian syllabics paa\n5178 canadian syllabics pwe\n5179 canadian syllabics west-cree pwe\n5180 canadian syllabics pwi\n5181 canadian syllabics west-cree pwi\n5182 canadian syllabics pwii\n5183 canadian syllabics west-cree pwii\n5184 canadian syllabics pwo\n5185 canadian syllabics west-cree pwo\n5186 canadian syllabics pwoo\n5187 canadian syllabics west-cree pwoo\n5188 canadian syllabics pwa\n5189 canadian syllabics west-cree pwa\n5190 canadian syllabics pwaa\n5191 canadian syllabics west-cree pwaa\n5192 canadian syllabics y-cree pwaa\n5193 canadian syllabics p\n5194 canadian syllabics west-cree p\n5195 canadian syllabics carrier h\n5196 canadian syllabics te\n5197 canadian syllabics taai\n5198 canadian syllabics ti\n5199 canadian syllabics tii\n5200 canadian syllabics to\n5201 canadian syllabics too\n5202 canadian syllabics y-cree too\n5203 canadian syllabics carrier dee\n5204 canadian syllabics carrier di\n5205 canadian syllabics ta\n5206 canadian syllabics taa\n5207 canadian syllabics twe\n5208 canadian syllabics west-cree twe\n5209 canadian syllabics twi\n5210 canadian syllabics west-cree twi\n5211 canadian syllabics twii\n5212 canadian syllabics west-cree twii\n5213 canadian syllabics two\n5214 canadian syllabics west-cree two\n5215 canadian syllabics twoo\n5216 canadian syllabics west-cree twoo\n5217 canadian syllabics twa\n5218 canadian syllabics west-cree twa\n5219 canadian syllabics twaa\n5220 canadian syllabics west-cree twaa\n5221 canadian syllabics naskapi twaa\n5222 canadian syllabics t\n5223 canadian syllabics tte\n5224 canadian syllabics tti\n5225 canadian syllabics tto\n5226 canadian syllabics tta\n5227 canadian syllabics ke\n5228 canadian syllabics kaai\n5229 canadian syllabics ki\n5230 canadian syllabics kii\n5231 canadian syllabics ko\n5232 canadian syllabics koo\n5233 canadian syllabics y-cree koo\n5234 canadian syllabics ka\n5235 canadian syllabics kaa\n5236 canadian syllabics kwe\n5237 canadian syllabics west-cree kwe\n5238 canadian syllabics kwi\n5239 canadian syllabics west-cree kwi\n5240 canadian syllabics kwii\n5241 canadian syllabics west-cree kwii\n5242 canadian syllabics kwo\n5243 canadian syllabics west-cree kwo\n5244 canadian syllabics kwoo\n5245 canadian syllabics west-cree kwoo\n5246 canadian syllabics kwa\n5247 canadian syllabics west-cree kwa\n5248 canadian syllabics kwaa\n5249 canadian syllabics west-cree kwaa\n5250 canadian syllabics naskapi kwaa\n5251 canadian syllabics k\n5252 canadian syllabics kw\n5253 canadian syllabics south-slavey keh\n5254 canadian syllabics south-slavey kih\n5255 canadian syllabics south-slavey koh\n5256 canadian syllabics south-slavey kah\n5257 canadian syllabics ce\n5258 canadian syllabics caai\n5259 canadian syllabics ci\n5260 canadian syllabics cii\n5261 canadian syllabics co\n5262 canadian syllabics coo\n5263 canadian syllabics y-cree coo\n5264 canadian syllabics ca\n5265 canadian syllabics caa\n5266 canadian syllabics cwe\n5267 canadian syllabics west-cree cwe\n5268 canadian syllabics cwi\n5269 canadian syllabics west-cree cwi\n5270 canadian syllabics cwii\n5271 canadian syllabics west-cree cwii\n5272 canadian syllabics cwo\n5273 canadian syllabics west-cree cwo\n5274 canadian syllabics cwoo\n5275 canadian syllabics west-cree cwoo\n5276 canadian syllabics cwa\n5277 canadian syllabics west-cree cwa\n5278 canadian syllabics cwaa\n5279 canadian syllabics west-cree cwaa\n5280 canadian syllabics naskapi cwaa\n5281 canadian syllabics c\n5282 canadian syllabics sayisi th\n5283 canadian syllabics me\n5284 canadian syllabics maai\n5285 canadian syllabics mi\n5286 canadian syllabics mii\n5287 canadian syllabics mo\n5288 canadian syllabics moo\n5289 canadian syllabics y-cree moo\n5290 canadian syllabics ma\n5291 canadian syllabics maa\n5292 canadian syllabics mwe\n5293 canadian syllabics west-cree mwe\n5294 canadian syllabics mwi\n5295 canadian syllabics west-cree mwi\n5296 canadian syllabics mwii\n5297 canadian syllabics west-cree mwii\n5298 canadian syllabics mwo\n5299 canadian syllabics west-cree mwo\n5300 canadian syllabics mwoo\n5301 canadian syllabics west-cree mwoo\n5302 canadian syllabics mwa\n5303 canadian syllabics west-cree mwa\n5304 canadian syllabics mwaa\n5305 canadian syllabics west-cree mwaa\n5306 canadian syllabics naskapi mwaa\n5307 canadian syllabics m\n5308 canadian syllabics west-cree m\n5309 canadian syllabics mh\n5310 canadian syllabics athapascan m\n5311 canadian syllabics sayisi m\n5312 canadian syllabics ne\n5313 canadian syllabics naai\n5314 canadian syllabics ni\n5315 canadian syllabics nii\n5316 canadian syllabics no\n5317 canadian syllabics noo\n5318 canadian syllabics y-cree noo\n5319 canadian syllabics na\n5320 canadian syllabics naa\n5321 canadian syllabics nwe\n5322 canadian syllabics west-cree nwe\n5323 canadian syllabics nwa\n5324 canadian syllabics west-cree nwa\n5325 canadian syllabics nwaa\n5326 canadian syllabics west-cree nwaa\n5327 canadian syllabics naskapi nwaa\n5328 canadian syllabics n\n5329 canadian syllabics carrier ng\n5330 canadian syllabics nh\n5331 canadian syllabics le\n5332 canadian syllabics laai\n5333 canadian syllabics li\n5334 canadian syllabics lii\n5335 canadian syllabics lo\n5336 canadian syllabics loo\n5337 canadian syllabics y-cree loo\n5338 canadian syllabics la\n5339 canadian syllabics laa\n5340 canadian syllabics lwe\n5341 canadian syllabics west-cree lwe\n5342 canadian syllabics lwi\n5343 canadian syllabics west-cree lwi\n5344 canadian syllabics lwii\n5345 canadian syllabics west-cree lwii\n5346 canadian syllabics lwo\n5347 canadian syllabics west-cree lwo\n5348 canadian syllabics lwoo\n5349 canadian syllabics west-cree lwoo\n5350 canadian syllabics lwa\n5351 canadian syllabics west-cree lwa\n5352 canadian syllabics lwaa\n5353 canadian syllabics west-cree lwaa\n5354 canadian syllabics l\n5355 canadian syllabics west-cree l\n5356 canadian syllabics medial l\n5357 canadian syllabics se\n5358 canadian syllabics saai\n5359 canadian syllabics si\n5360 canadian syllabics sii\n5361 canadian syllabics so\n5362 canadian syllabics soo\n5363 canadian syllabics y-cree soo\n5364 canadian syllabics sa\n5365 canadian syllabics saa\n5366 canadian syllabics swe\n5367 canadian syllabics west-cree swe\n5368 canadian syllabics swi\n5369 canadian syllabics west-cree swi\n5370 canadian syllabics swii\n5371 canadian syllabics west-cree swii\n5372 canadian syllabics swo\n5373 canadian syllabics west-cree swo\n5374 canadian syllabics swoo\n5375 canadian syllabics west-cree swoo\n5376 canadian syllabics swa\n5377 canadian syllabics west-cree swa\n5378 canadian syllabics swaa\n5379 canadian syllabics west-cree swaa\n5380 canadian syllabics naskapi swaa\n5381 canadian syllabics s\n5382 canadian syllabics athapascan s\n5383 canadian syllabics sw\n5384 canadian syllabics blackfoot s\n5385 canadian syllabics moose-cree sk\n5386 canadian syllabics naskapi skw\n5387 canadian syllabics naskapi s-w\n5388 canadian syllabics naskapi spwa\n5389 canadian syllabics naskapi stwa\n5390 canadian syllabics naskapi skwa\n5391 canadian syllabics naskapi scwa\n5392 canadian syllabics she\n5393 canadian syllabics shi\n5394 canadian syllabics shii\n5395 canadian syllabics sho\n5396 canadian syllabics shoo\n5397 canadian syllabics sha\n5398 canadian syllabics shaa\n5399 canadian syllabics shwe\n5400 canadian syllabics west-cree shwe\n5401 canadian syllabics shwi\n5402 canadian syllabics west-cree shwi\n5403 canadian syllabics shwii\n5404 canadian syllabics west-cree shwii\n5405 canadian syllabics shwo\n5406 canadian syllabics west-cree shwo\n5407 canadian syllabics shwoo\n5408 canadian syllabics west-cree shwoo\n5409 canadian syllabics shwa\n5410 canadian syllabics west-cree shwa\n5411 canadian syllabics shwaa\n5412 canadian syllabics west-cree shwaa\n5413 canadian syllabics sh\n5414 canadian syllabics ye\n5415 canadian syllabics yaai\n5416 canadian syllabics yi\n5417 canadian syllabics yii\n5418 canadian syllabics yo\n5419 canadian syllabics yoo\n5420 canadian syllabics y-cree yoo\n5421 canadian syllabics ya\n5422 canadian syllabics yaa\n5423 canadian syllabics ywe\n5424 canadian syllabics west-cree ywe\n5425 canadian syllabics ywi\n5426 canadian syllabics west-cree ywi\n5427 canadian syllabics ywii\n5428 canadian syllabics west-cree ywii\n5429 canadian syllabics ywo\n5430 canadian syllabics west-cree ywo\n5431 canadian syllabics ywoo\n5432 canadian syllabics west-cree ywoo\n5433 canadian syllabics ywa\n5434 canadian syllabics west-cree ywa\n5435 canadian syllabics ywaa\n5436 canadian syllabics west-cree ywaa\n5437 canadian syllabics naskapi ywaa\n5438 canadian syllabics y\n5439 canadian syllabics bible-cree y\n5440 canadian syllabics west-cree y\n5441 canadian syllabics sayisi yi\n5442 canadian syllabics re\n5443 canadian syllabics r-cree re\n5444 canadian syllabics west-cree le\n5445 canadian syllabics raai\n5446 canadian syllabics ri\n5447 canadian syllabics rii\n5448 canadian syllabics ro\n5449 canadian syllabics roo\n5450 canadian syllabics west-cree lo\n5451 canadian syllabics ra\n5452 canadian syllabics raa\n5453 canadian syllabics west-cree la\n5454 canadian syllabics rwaa\n5455 canadian syllabics west-cree rwaa\n5456 canadian syllabics r\n5457 canadian syllabics west-cree r\n5458 canadian syllabics medial r\n5459 canadian syllabics fe\n5460 canadian syllabics faai\n5461 canadian syllabics fi\n5462 canadian syllabics fii\n5463 canadian syllabics fo\n5464 canadian syllabics foo\n5465 canadian syllabics fa\n5466 canadian syllabics faa\n5467 canadian syllabics fwaa\n5468 canadian syllabics west-cree fwaa\n5469 canadian syllabics f\n5470 canadian syllabics the\n5471 canadian syllabics n-cree the\n5472 canadian syllabics thi\n5473 canadian syllabics n-cree thi\n5474 canadian syllabics thii\n5475 canadian syllabics n-cree thii\n5476 canadian syllabics tho\n5477 canadian syllabics thoo\n5478 canadian syllabics tha\n5479 canadian syllabics thaa\n5480 canadian syllabics thwaa\n5481 canadian syllabics west-cree thwaa\n5482 canadian syllabics th\n5483 canadian syllabics tthe\n5484 canadian syllabics tthi\n5485 canadian syllabics ttho\n5486 canadian syllabics ttha\n5487 canadian syllabics tth\n5488 canadian syllabics tye\n5489 canadian syllabics tyi\n5490 canadian syllabics tyo\n5491 canadian syllabics tya\n5492 canadian syllabics nunavik he\n5493 canadian syllabics nunavik hi\n5494 canadian syllabics nunavik hii\n5495 canadian syllabics nunavik ho\n5496 canadian syllabics nunavik hoo\n5497 canadian syllabics nunavik ha\n5498 canadian syllabics nunavik haa\n5499 canadian syllabics nunavik h\n5500 canadian syllabics nunavut h\n5501 canadian syllabics hk\n5502 canadian syllabics qaai\n5503 canadian syllabics qi\n5504 canadian syllabics qii\n5505 canadian syllabics qo\n5506 canadian syllabics qoo\n5507 canadian syllabics qa\n5508 canadian syllabics qaa\n5509 canadian syllabics q\n5510 canadian syllabics tlhe\n5511 canadian syllabics tlhi\n5512 canadian syllabics tlho\n5513 canadian syllabics tlha\n5514 canadian syllabics west-cree re\n5515 canadian syllabics west-cree ri\n5516 canadian syllabics west-cree ro\n5517 canadian syllabics west-cree ra\n5518 canadian syllabics ngaai\n5519 canadian syllabics ngi\n5520 canadian syllabics ngii\n5521 canadian syllabics ngo\n5522 canadian syllabics ngoo\n5523 canadian syllabics nga\n5524 canadian syllabics ngaa\n5525 canadian syllabics ng\n5526 canadian syllabics nng\n5527 canadian syllabics sayisi she\n5528 canadian syllabics sayisi shi\n5529 canadian syllabics sayisi sho\n5530 canadian syllabics sayisi sha\n5531 canadian syllabics woods-cree the\n5532 canadian syllabics woods-cree thi\n5533 canadian syllabics woods-cree tho\n5534 canadian syllabics woods-cree tha\n5535 canadian syllabics woods-cree th\n5536 canadian syllabics lhi\n5537 canadian syllabics lhii\n5538 canadian syllabics lho\n5539 canadian syllabics lhoo\n5540 canadian syllabics lha\n5541 canadian syllabics lhaa\n5542 canadian syllabics lh\n5543 canadian syllabics th-cree the\n5544 canadian syllabics th-cree thi\n5545 canadian syllabics th-cree thii\n5546 canadian syllabics th-cree tho\n5547 canadian syllabics th-cree thoo\n5548 canadian syllabics th-cree tha\n5549 canadian syllabics th-cree thaa\n5550 canadian syllabics th-cree th\n5551 canadian syllabics aivilik b\n5552 canadian syllabics blackfoot e\n5553 canadian syllabics blackfoot i\n5554 canadian syllabics blackfoot o\n5555 canadian syllabics blackfoot a\n5556 canadian syllabics blackfoot we\n5557 canadian syllabics blackfoot wi\n5558 canadian syllabics blackfoot wo\n5559 canadian syllabics blackfoot wa\n5560 canadian syllabics blackfoot ne\n5561 canadian syllabics blackfoot ni\n5562 canadian syllabics blackfoot no\n5563 canadian syllabics blackfoot na\n5564 canadian syllabics blackfoot ke\n5565 canadian syllabics blackfoot ki\n5566 canadian syllabics blackfoot ko\n5567 canadian syllabics blackfoot ka\n5568 canadian syllabics sayisi he\n5569 canadian syllabics sayisi hi\n5570 canadian syllabics sayisi ho\n5571 canadian syllabics sayisi ha\n5572 canadian syllabics carrier ghu\n5573 canadian syllabics carrier gho\n5574 canadian syllabics carrier ghe\n5575 canadian syllabics carrier ghee\n5576 canadian syllabics carrier ghi\n5577 canadian syllabics carrier gha\n5578 canadian syllabics carrier ru\n5579 canadian syllabics carrier ro\n5580 canadian syllabics carrier re\n5581 canadian syllabics carrier ree\n5582 canadian syllabics carrier ri\n5583 canadian syllabics carrier ra\n5584 canadian syllabics carrier wu\n5585 canadian syllabics carrier wo\n5586 canadian syllabics carrier we\n5587 canadian syllabics carrier wee\n5588 canadian syllabics carrier wi\n5589 canadian syllabics carrier wa\n5590 canadian syllabics carrier hwu\n5591 canadian syllabics carrier hwo\n5592 canadian syllabics carrier hwe\n5593 canadian syllabics carrier hwee\n5594 canadian syllabics carrier hwi\n5595 canadian syllabics carrier hwa\n5596 canadian syllabics carrier thu\n5597 canadian syllabics carrier tho\n5598 canadian syllabics carrier the\n5599 canadian syllabics carrier thee\n5600 canadian syllabics carrier thi\n5601 canadian syllabics carrier tha\n5602 canadian syllabics carrier ttu\n5603 canadian syllabics carrier tto\n5604 canadian syllabics carrier tte\n5605 canadian syllabics carrier ttee\n5606 canadian syllabics carrier tti\n5607 canadian syllabics carrier tta\n5608 canadian syllabics carrier pu\n5609 canadian syllabics carrier po\n5610 canadian syllabics carrier pe\n5611 canadian syllabics carrier pee\n5612 canadian syllabics carrier pi\n5613 canadian syllabics carrier pa\n5614 canadian syllabics carrier p\n5615 canadian syllabics carrier gu\n5616 canadian syllabics carrier go\n5617 canadian syllabics carrier ge\n5618 canadian syllabics carrier gee\n5619 canadian syllabics carrier gi\n5620 canadian syllabics carrier ga\n5621 canadian syllabics carrier khu\n5622 canadian syllabics carrier kho\n5623 canadian syllabics carrier khe\n5624 canadian syllabics carrier khee\n5625 canadian syllabics carrier khi\n5626 canadian syllabics carrier kha\n5627 canadian syllabics carrier kku\n5628 canadian syllabics carrier kko\n5629 canadian syllabics carrier kke\n5630 canadian syllabics carrier kkee\n5631 canadian syllabics carrier kki\n5632 canadian syllabics carrier kka\n5633 canadian syllabics carrier kk\n5634 canadian syllabics carrier nu\n5635 canadian syllabics carrier no\n5636 canadian syllabics carrier ne\n5637 canadian syllabics carrier nee\n5638 canadian syllabics carrier ni\n5639 canadian syllabics carrier na\n5640 canadian syllabics carrier mu\n5641 canadian syllabics carrier mo\n5642 canadian syllabics carrier me\n5643 canadian syllabics carrier mee\n5644 canadian syllabics carrier mi\n5645 canadian syllabics carrier ma\n5646 canadian syllabics carrier yu\n5647 canadian syllabics carrier yo\n5648 canadian syllabics carrier ye\n5649 canadian syllabics carrier yee\n5650 canadian syllabics carrier yi\n5651 canadian syllabics carrier ya\n5652 canadian syllabics carrier ju\n5653 canadian syllabics sayisi ju\n5654 canadian syllabics carrier jo\n5655 canadian syllabics carrier je\n5656 canadian syllabics carrier jee\n5657 canadian syllabics carrier ji\n5658 canadian syllabics sayisi ji\n5659 canadian syllabics carrier ja\n5660 canadian syllabics carrier jju\n5661 canadian syllabics carrier jjo\n5662 canadian syllabics carrier jje\n5663 canadian syllabics carrier jjee\n5664 canadian syllabics carrier jji\n5665 canadian syllabics carrier jja\n5666 canadian syllabics carrier lu\n5667 canadian syllabics carrier lo\n5668 canadian syllabics carrier le\n5669 canadian syllabics carrier lee\n5670 canadian syllabics carrier li\n5671 canadian syllabics carrier la\n5672 canadian syllabics carrier dlu\n5673 canadian syllabics carrier dlo\n5674 canadian syllabics carrier dle\n5675 canadian syllabics carrier dlee\n5676 canadian syllabics carrier dli\n5677 canadian syllabics carrier dla\n5678 canadian syllabics carrier lhu\n5679 canadian syllabics carrier lho\n5680 canadian syllabics carrier lhe\n5681 canadian syllabics carrier lhee\n5682 canadian syllabics carrier lhi\n5683 canadian syllabics carrier lha\n5684 canadian syllabics carrier tlhu\n5685 canadian syllabics carrier tlho\n5686 canadian syllabics carrier tlhe\n5687 canadian syllabics carrier tlhee\n5688 canadian syllabics carrier tlhi\n5689 canadian syllabics carrier tlha\n5690 canadian syllabics carrier tlu\n5691 canadian syllabics carrier tlo\n5692 canadian syllabics carrier tle\n5693 canadian syllabics carrier tlee\n5694 canadian syllabics carrier tli\n5695 canadian syllabics carrier tla\n5696 canadian syllabics carrier zu\n5697 canadian syllabics carrier zo\n5698 canadian syllabics carrier ze\n5699 canadian syllabics carrier zee\n5700 canadian syllabics carrier zi\n5701 canadian syllabics carrier za\n5702 canadian syllabics carrier z\n5703 canadian syllabics carrier initial z\n5704 canadian syllabics carrier dzu\n5705 canadian syllabics carrier dzo\n5706 canadian syllabics carrier dze\n5707 canadian syllabics carrier dzee\n5708 canadian syllabics carrier dzi\n5709 canadian syllabics carrier dza\n5710 canadian syllabics carrier su\n5711 canadian syllabics carrier so\n5712 canadian syllabics carrier se\n5713 canadian syllabics carrier see\n5714 canadian syllabics carrier si\n5715 canadian syllabics carrier sa\n5716 canadian syllabics carrier shu\n5717 canadian syllabics carrier sho\n5718 canadian syllabics carrier she\n5719 canadian syllabics carrier shee\n5720 canadian syllabics carrier shi\n5721 canadian syllabics carrier sha\n5722 canadian syllabics carrier sh\n5723 canadian syllabics carrier tsu\n5724 canadian syllabics carrier tso\n5725 canadian syllabics carrier tse\n5726 canadian syllabics carrier tsee\n5727 canadian syllabics carrier tsi\n5728 canadian syllabics carrier tsa\n5729 canadian syllabics carrier chu\n5730 canadian syllabics carrier cho\n5731 canadian syllabics carrier che\n5732 canadian syllabics carrier chee\n5733 canadian syllabics carrier chi\n5734 canadian syllabics carrier cha\n5735 canadian syllabics carrier ttsu\n5736 canadian syllabics carrier ttso\n5737 canadian syllabics carrier ttse\n5738 canadian syllabics carrier ttsee\n5739 canadian syllabics carrier ttsi\n5740 canadian syllabics carrier ttsa\n5741 canadian syllabics chi sign\n5742 canadian syllabics full stop\n5743 canadian syllabics qai\n5744 canadian syllabics ngai\n5745 canadian syllabics nngi\n5746 canadian syllabics nngii\n5747 canadian syllabics nngo\n5748 canadian syllabics nngoo\n5749 canadian syllabics nnga\n5750 canadian syllabics nngaa\n5751 canadian syllabics woods-cree thwee\n5752 canadian syllabics woods-cree thwi\n5753 canadian syllabics woods-cree thwii\n5754 canadian syllabics woods-cree thwo\n5755 canadian syllabics woods-cree thwoo\n5756 canadian syllabics woods-cree thwa\n5757 canadian syllabics woods-cree thwaa\n5758 canadian syllabics woods-cree final th\n5759 canadian syllabics blackfoot w\n5760 ogham space mark\n5761 ogham letter beith\n5762 ogham letter luis\n5763 ogham letter fearn\n5764 ogham letter sail\n5765 ogham letter nion\n5766 ogham letter uath\n5767 ogham letter dair\n5768 ogham letter tinne\n5769 ogham letter coll\n5770 ogham letter ceirt\n5771 ogham letter muin\n5772 ogham letter gort\n5773 ogham letter ngeadal\n5774 ogham letter straif\n5775 ogham letter ruis\n5776 ogham letter ailm\n5777 ogham letter onn\n5778 ogham letter ur\n5779 ogham letter eadhadh\n5780 ogham letter iodhadh\n5781 ogham letter eabhadh\n5782 ogham letter or\n5783 ogham letter uilleann\n5784 ogham letter ifin\n5785 ogham letter eamhancholl\n5786 ogham letter peith\n5787 ogham feather mark\n5788 ogham reversed feather mark\n5792 runic letter fehu feoh fe f\n5793 runic letter v\n5794 runic letter uruz ur u\n5795 runic letter yr\n5796 runic letter y\n5797 runic letter w\n5798 runic letter thurisaz thurs thorn\n5799 runic letter eth\n5800 runic letter ansuz a\n5801 runic letter os o\n5802 runic letter ac a\n5803 runic letter aesc\n5804 runic letter long-branch-oss o\n5805 runic letter short-twig-oss o\n5806 runic letter o\n5807 runic letter oe\n5808 runic letter on\n5809 runic letter raido rad reid r\n5810 runic letter kauna\n5811 runic letter cen\n5812 runic letter kaun k\n5813 runic letter g\n5814 runic letter eng\n5815 runic letter gebo gyfu g\n5816 runic letter gar\n5817 runic letter wunjo wynn w\n5818 runic letter haglaz h\n5819 runic letter haegl h\n5820 runic letter long-branch-hagall h\n5821 runic letter short-twig-hagall h\n5822 runic letter naudiz nyd naud n\n5823 runic letter short-twig-naud n\n5824 runic letter dotted-n\n5825 runic letter isaz is iss i\n5826 runic letter e\n5827 runic letter jeran j\n5828 runic letter ger\n5829 runic letter long-branch-ar ae\n5830 runic letter short-twig-ar a\n5831 runic letter iwaz eoh\n5832 runic letter pertho peorth p\n5833 runic letter algiz eolhx\n5834 runic letter sowilo s\n5835 runic letter sigel long-branch-sol s\n5836 runic letter short-twig-sol s\n5837 runic letter c\n5838 runic letter z\n5839 runic letter tiwaz tir tyr t\n5840 runic letter short-twig-tyr t\n5841 runic letter d\n5842 runic letter berkanan beorc bjarkan b\n5843 runic letter short-twig-bjarkan b\n5844 runic letter dotted-p\n5845 runic letter open-p\n5846 runic letter ehwaz eh e\n5847 runic letter mannaz man m\n5848 runic letter long-branch-madr m\n5849 runic letter short-twig-madr m\n5850 runic letter laukaz lagu logr l\n5851 runic letter dotted-l\n5852 runic letter ingwaz\n5853 runic letter ing\n5854 runic letter dagaz daeg d\n5855 runic letter othalan ethel o\n5856 runic letter ear\n5857 runic letter ior\n5858 runic letter cweorth\n5859 runic letter calc\n5860 runic letter cealc\n5861 runic letter stan\n5862 runic letter long-branch-yr\n5863 runic letter short-twig-yr\n5864 runic letter icelandic-yr\n5865 runic letter q\n5866 runic letter x\n5867 runic single punctuation\n5868 runic multiple punctuation\n5869 runic cross punctuation\n5870 runic arlaug symbol\n5871 runic tvimadur symbol\n5872 runic belgthor symbol\n5873 runic letter k\n5874 runic letter sh\n5875 runic letter oo\n5876 runic letter franks casket os\n5877 runic letter franks casket is\n5878 runic letter franks casket eh\n5879 runic letter franks casket ac\n5880 runic letter franks casket aesc\n5888 tagalog letter a\n5889 tagalog letter i\n5890 tagalog letter u\n5891 tagalog letter ka\n5892 tagalog letter ga\n5893 tagalog letter nga\n5894 tagalog letter ta\n5895 tagalog letter da\n5896 tagalog letter na\n5897 tagalog letter pa\n5898 tagalog letter ba\n5899 tagalog letter ma\n5900 tagalog letter ya\n5901 tagalog letter ra\n5902 tagalog letter la\n5903 tagalog letter wa\n5904 tagalog letter sa\n5905 tagalog letter ha\n5906 tagalog vowel sign i\n5907 tagalog vowel sign u\n5908 tagalog sign virama\n5909 tagalog sign pamudpod\n5919 tagalog letter archaic ra\n5920 hanunoo letter a\n5921 hanunoo letter i\n5922 hanunoo letter u\n5923 hanunoo letter ka\n5924 hanunoo letter ga\n5925 hanunoo letter nga\n5926 hanunoo letter ta\n5927 hanunoo letter da\n5928 hanunoo letter na\n5929 hanunoo letter pa\n5930 hanunoo letter ba\n5931 hanunoo letter ma\n5932 hanunoo letter ya\n5933 hanunoo letter ra\n5934 hanunoo letter la\n5935 hanunoo letter wa\n5936 hanunoo letter sa\n5937 hanunoo letter ha\n5938 hanunoo vowel sign i\n5939 hanunoo vowel sign u\n5940 hanunoo sign pamudpod\n5941 philippine single punctuation\n5942 philippine double punctuation\n5952 buhid letter a\n5953 buhid letter i\n5954 buhid letter u\n5955 buhid letter ka\n5956 buhid letter ga\n5957 buhid letter nga\n5958 buhid letter ta\n5959 buhid letter da\n5960 buhid letter na\n5961 buhid letter pa\n5962 buhid letter ba\n5963 buhid letter ma\n5964 buhid letter ya\n5965 buhid letter ra\n5966 buhid letter la\n5967 buhid letter wa\n5968 buhid letter sa\n5969 buhid letter ha\n5970 buhid vowel sign i\n5971 buhid vowel sign u\n5984 tagbanwa letter a\n5985 tagbanwa letter i\n5986 tagbanwa letter u\n5987 tagbanwa letter ka\n5988 tagbanwa letter ga\n5989 tagbanwa letter nga\n5990 tagbanwa letter ta\n5991 tagbanwa letter da\n5992 tagbanwa letter na\n5993 tagbanwa letter pa\n5994 tagbanwa letter ba\n5995 tagbanwa letter ma\n5996 tagbanwa letter ya\n5998 tagbanwa letter la\n5999 tagbanwa letter wa\n6000 tagbanwa letter sa\n6002 tagbanwa vowel sign i\n6003 tagbanwa vowel sign u\n6016 khmer letter ka\n6017 khmer letter kha\n6018 khmer letter ko\n6019 khmer letter kho\n6020 khmer letter ngo\n6021 khmer letter ca\n6022 khmer letter cha\n6023 khmer letter co\n6024 khmer letter cho\n6025 khmer letter nyo\n6026 khmer letter da\n6027 khmer letter ttha\n6028 khmer letter do\n6029 khmer letter ttho\n6030 khmer letter nno\n6031 khmer letter ta\n6032 khmer letter tha\n6033 khmer letter to\n6034 khmer letter tho\n6035 khmer letter no\n6036 khmer letter ba\n6037 khmer letter pha\n6038 khmer letter po\n6039 khmer letter pho\n6040 khmer letter mo\n6041 khmer letter yo\n6042 khmer letter ro\n6043 khmer letter lo\n6044 khmer letter vo\n6045 khmer letter sha\n6046 khmer letter sso\n6047 khmer letter sa\n6048 khmer letter ha\n6049 khmer letter la\n6050 khmer letter qa\n6051 khmer independent vowel qaq\n6052 khmer independent vowel qaa\n6053 khmer independent vowel qi\n6054 khmer independent vowel qii\n6055 khmer independent vowel qu\n6056 khmer independent vowel quk\n6057 khmer independent vowel quu\n6058 khmer independent vowel quuv\n6059 khmer independent vowel ry\n6060 khmer independent vowel ryy\n6061 khmer independent vowel ly\n6062 khmer independent vowel lyy\n6063 khmer independent vowel qe\n6064 khmer independent vowel qai\n6065 khmer independent vowel qoo type one\n6066 khmer independent vowel qoo type two\n6067 khmer independent vowel qau\n6068 khmer vowel inherent aq\n6069 khmer vowel inherent aa\n6070 khmer vowel sign aa\n6071 khmer vowel sign i\n6072 khmer vowel sign ii\n6073 khmer vowel sign y\n6074 khmer vowel sign yy\n6075 khmer vowel sign u\n6076 khmer vowel sign uu\n6077 khmer vowel sign ua\n6078 khmer vowel sign oe\n6079 khmer vowel sign ya\n6080 khmer vowel sign ie\n6081 khmer vowel sign e\n6082 khmer vowel sign ae\n6083 khmer vowel sign ai\n6084 khmer vowel sign oo\n6085 khmer vowel sign au\n6086 khmer sign nikahit\n6087 khmer sign reahmuk\n6088 khmer sign yuukaleapintu\n6089 khmer sign muusikatoan\n6090 khmer sign triisap\n6091 khmer sign bantoc\n6092 khmer sign robat\n6093 khmer sign toandakhiat\n6094 khmer sign kakabat\n6095 khmer sign ahsda\n6096 khmer sign samyok sannya\n6097 khmer sign viriam\n6098 khmer sign coeng\n6099 khmer sign bathamasat\n6100 khmer sign khan\n6101 khmer sign bariyoosan\n6102 khmer sign camnuc pii kuuh\n6103 khmer sign lek too\n6104 khmer sign beyyal\n6105 khmer sign phnaek muan\n6106 khmer sign koomuut\n6107 khmer currency symbol riel\n6108 khmer sign avakrahasanya\n6109 khmer sign atthacan\n6112 khmer digit zero\n6113 khmer digit one\n6114 khmer digit two\n6115 khmer digit three\n6116 khmer digit four\n6117 khmer digit five\n6118 khmer digit six\n6119 khmer digit seven\n6120 khmer digit eight\n6121 khmer digit nine\n6128 khmer symbol lek attak son\n6129 khmer symbol lek attak muoy\n6130 khmer symbol lek attak pii\n6131 khmer symbol lek attak bei\n6132 khmer symbol lek attak buon\n6133 khmer symbol lek attak pram\n6134 khmer symbol lek attak pram-muoy\n6135 khmer symbol lek attak pram-pii\n6136 khmer symbol lek attak pram-bei\n6137 khmer symbol lek attak pram-buon\n6144 mongolian birga\n6145 mongolian ellipsis\n6146 mongolian comma\n6147 mongolian full stop\n6148 mongolian colon\n6149 mongolian four dots\n6150 mongolian todo soft hyphen\n6151 mongolian sibe syllable boundary marker\n6152 mongolian manchu comma\n6153 mongolian manchu full stop\n6154 mongolian nirugu\n6155 mongolian free variation selector one\n6156 mongolian free variation selector two\n6157 mongolian free variation selector three\n6158 mongolian vowel separator\n6159 mongolian free variation selector four\n6160 mongolian digit zero\n6161 mongolian digit one\n6162 mongolian digit two\n6163 mongolian digit three\n6164 mongolian digit four\n6165 mongolian digit five\n6166 mongolian digit six\n6167 mongolian digit seven\n6168 mongolian digit eight\n6169 mongolian digit nine\n6176 mongolian letter a\n6177 mongolian letter e\n6178 mongolian letter i\n6179 mongolian letter o\n6180 mongolian letter u\n6181 mongolian letter oe\n6182 mongolian letter ue\n6183 mongolian letter ee\n6184 mongolian letter na\n6185 mongolian letter ang\n6186 mongolian letter ba\n6187 mongolian letter pa\n6188 mongolian letter qa\n6189 mongolian letter ga\n6190 mongolian letter ma\n6191 mongolian letter la\n6192 mongolian letter sa\n6193 mongolian letter sha\n6194 mongolian letter ta\n6195 mongolian letter da\n6196 mongolian letter cha\n6197 mongolian letter ja\n6198 mongolian letter ya\n6199 mongolian letter ra\n6200 mongolian letter wa\n6201 mongolian letter fa\n6202 mongolian letter ka\n6203 mongolian letter kha\n6204 mongolian letter tsa\n6205 mongolian letter za\n6206 mongolian letter haa\n6207 mongolian letter zra\n6208 mongolian letter lha\n6209 mongolian letter zhi\n6210 mongolian letter chi\n6211 mongolian letter todo long vowel sign\n6212 mongolian letter todo e\n6213 mongolian letter todo i\n6214 mongolian letter todo o\n6215 mongolian letter todo u\n6216 mongolian letter todo oe\n6217 mongolian letter todo ue\n6218 mongolian letter todo ang\n6219 mongolian letter todo ba\n6220 mongolian letter todo pa\n6221 mongolian letter todo qa\n6222 mongolian letter todo ga\n6223 mongolian letter todo ma\n6224 mongolian letter todo ta\n6225 mongolian letter todo da\n6226 mongolian letter todo cha\n6227 mongolian letter todo ja\n6228 mongolian letter todo tsa\n6229 mongolian letter todo ya\n6230 mongolian letter todo wa\n6231 mongolian letter todo ka\n6232 mongolian letter todo gaa\n6233 mongolian letter todo haa\n6234 mongolian letter todo jia\n6235 mongolian letter todo nia\n6236 mongolian letter todo dza\n6237 mongolian letter sibe e\n6238 mongolian letter sibe i\n6239 mongolian letter sibe iy\n6240 mongolian letter sibe ue\n6241 mongolian letter sibe u\n6242 mongolian letter sibe ang\n6243 mongolian letter sibe ka\n6244 mongolian letter sibe ga\n6245 mongolian letter sibe ha\n6246 mongolian letter sibe pa\n6247 mongolian letter sibe sha\n6248 mongolian letter sibe ta\n6249 mongolian letter sibe da\n6250 mongolian letter sibe ja\n6251 mongolian letter sibe fa\n6252 mongolian letter sibe gaa\n6253 mongolian letter sibe haa\n6254 mongolian letter sibe tsa\n6255 mongolian letter sibe za\n6256 mongolian letter sibe raa\n6257 mongolian letter sibe cha\n6258 mongolian letter sibe zha\n6259 mongolian letter manchu i\n6260 mongolian letter manchu ka\n6261 mongolian letter manchu ra\n6262 mongolian letter manchu fa\n6263 mongolian letter manchu zha\n6264 mongolian letter cha with two dots\n6272 mongolian letter ali gali anusvara one\n6273 mongolian letter ali gali visarga one\n6274 mongolian letter ali gali damaru\n6275 mongolian letter ali gali ubadama\n6276 mongolian letter ali gali inverted ubadama\n6277 mongolian letter ali gali baluda\n6278 mongolian letter ali gali three baluda\n6279 mongolian letter ali gali a\n6280 mongolian letter ali gali i\n6281 mongolian letter ali gali ka\n6282 mongolian letter ali gali nga\n6283 mongolian letter ali gali ca\n6284 mongolian letter ali gali tta\n6285 mongolian letter ali gali ttha\n6286 mongolian letter ali gali dda\n6287 mongolian letter ali gali nna\n6288 mongolian letter ali gali ta\n6289 mongolian letter ali gali da\n6290 mongolian letter ali gali pa\n6291 mongolian letter ali gali pha\n6292 mongolian letter ali gali ssa\n6293 mongolian letter ali gali zha\n6294 mongolian letter ali gali za\n6295 mongolian letter ali gali ah\n6296 mongolian letter todo ali gali ta\n6297 mongolian letter todo ali gali zha\n6298 mongolian letter manchu ali gali gha\n6299 mongolian letter manchu ali gali nga\n6300 mongolian letter manchu ali gali ca\n6301 mongolian letter manchu ali gali jha\n6302 mongolian letter manchu ali gali tta\n6303 mongolian letter manchu ali gali ddha\n6304 mongolian letter manchu ali gali ta\n6305 mongolian letter manchu ali gali dha\n6306 mongolian letter manchu ali gali ssa\n6307 mongolian letter manchu ali gali cya\n6308 mongolian letter manchu ali gali zha\n6309 mongolian letter manchu ali gali za\n6310 mongolian letter ali gali half u\n6311 mongolian letter ali gali half ya\n6312 mongolian letter manchu ali gali bha\n6313 mongolian letter ali gali dagalga\n6314 mongolian letter manchu ali gali lha\n6320 canadian syllabics oy\n6321 canadian syllabics ay\n6322 canadian syllabics aay\n6323 canadian syllabics way\n6324 canadian syllabics poy\n6325 canadian syllabics pay\n6326 canadian syllabics pwoy\n6327 canadian syllabics tay\n6328 canadian syllabics kay\n6329 canadian syllabics kway\n6330 canadian syllabics may\n6331 canadian syllabics noy\n6332 canadian syllabics nay\n6333 canadian syllabics lay\n6334 canadian syllabics soy\n6335 canadian syllabics say\n6336 canadian syllabics shoy\n6337 canadian syllabics shay\n6338 canadian syllabics shwoy\n6339 canadian syllabics yoy\n6340 canadian syllabics yay\n6341 canadian syllabics ray\n6342 canadian syllabics nwi\n6343 canadian syllabics ojibway nwi\n6344 canadian syllabics nwii\n6345 canadian syllabics ojibway nwii\n6346 canadian syllabics nwo\n6347 canadian syllabics ojibway nwo\n6348 canadian syllabics nwoo\n6349 canadian syllabics ojibway nwoo\n6350 canadian syllabics rwee\n6351 canadian syllabics rwi\n6352 canadian syllabics rwii\n6353 canadian syllabics rwo\n6354 canadian syllabics rwoo\n6355 canadian syllabics rwa\n6356 canadian syllabics ojibway p\n6357 canadian syllabics ojibway t\n6358 canadian syllabics ojibway k\n6359 canadian syllabics ojibway c\n6360 canadian syllabics ojibway m\n6361 canadian syllabics ojibway n\n6362 canadian syllabics ojibway s\n6363 canadian syllabics ojibway sh\n6364 canadian syllabics eastern w\n6365 canadian syllabics western w\n6366 canadian syllabics final small ring\n6367 canadian syllabics final raised dot\n6368 canadian syllabics r-cree rwe\n6369 canadian syllabics west-cree loo\n6370 canadian syllabics west-cree laa\n6371 canadian syllabics thwe\n6372 canadian syllabics thwa\n6373 canadian syllabics tthwe\n6374 canadian syllabics tthoo\n6375 canadian syllabics tthaa\n6376 canadian syllabics tlhwe\n6377 canadian syllabics tlhoo\n6378 canadian syllabics sayisi shwe\n6379 canadian syllabics sayisi shoo\n6380 canadian syllabics sayisi hoo\n6381 canadian syllabics carrier gwu\n6382 canadian syllabics carrier dene gee\n6383 canadian syllabics carrier gaa\n6384 canadian syllabics carrier gwa\n6385 canadian syllabics sayisi juu\n6386 canadian syllabics carrier jwa\n6387 canadian syllabics beaver dene l\n6388 canadian syllabics beaver dene r\n6389 canadian syllabics carrier dental s\n6400 limbu vowel-carrier letter\n6401 limbu letter ka\n6402 limbu letter kha\n6403 limbu letter ga\n6404 limbu letter gha\n6405 limbu letter nga\n6406 limbu letter ca\n6407 limbu letter cha\n6408 limbu letter ja\n6409 limbu letter jha\n6410 limbu letter yan\n6411 limbu letter ta\n6412 limbu letter tha\n6413 limbu letter da\n6414 limbu letter dha\n6415 limbu letter na\n6416 limbu letter pa\n6417 limbu letter pha\n6418 limbu letter ba\n6419 limbu letter bha\n6420 limbu letter ma\n6421 limbu letter ya\n6422 limbu letter ra\n6423 limbu letter la\n6424 limbu letter wa\n6425 limbu letter sha\n6426 limbu letter ssa\n6427 limbu letter sa\n6428 limbu letter ha\n6429 limbu letter gyan\n6430 limbu letter tra\n6432 limbu vowel sign a\n6433 limbu vowel sign i\n6434 limbu vowel sign u\n6435 limbu vowel sign ee\n6436 limbu vowel sign ai\n6437 limbu vowel sign oo\n6438 limbu vowel sign au\n6439 limbu vowel sign e\n6440 limbu vowel sign o\n6441 limbu subjoined letter ya\n6442 limbu subjoined letter ra\n6443 limbu subjoined letter wa\n6448 limbu small letter ka\n6449 limbu small letter nga\n6450 limbu small letter anusvara\n6451 limbu small letter ta\n6452 limbu small letter na\n6453 limbu small letter pa\n6454 limbu small letter ma\n6455 limbu small letter ra\n6456 limbu small letter la\n6457 limbu sign mukphreng\n6458 limbu sign kemphreng\n6459 limbu sign sa-i\n6464 limbu sign loo\n6468 limbu exclamation mark\n6469 limbu question mark\n6470 limbu digit zero\n6471 limbu digit one\n6472 limbu digit two\n6473 limbu digit three\n6474 limbu digit four\n6475 limbu digit five\n6476 limbu digit six\n6477 limbu digit seven\n6478 limbu digit eight\n6479 limbu digit nine\n6480 tai le letter ka\n6481 tai le letter xa\n6482 tai le letter nga\n6483 tai le letter tsa\n6484 tai le letter sa\n6485 tai le letter ya\n6486 tai le letter ta\n6487 tai le letter tha\n6488 tai le letter la\n6489 tai le letter pa\n6490 tai le letter pha\n6491 tai le letter ma\n6492 tai le letter fa\n6493 tai le letter va\n6494 tai le letter ha\n6495 tai le letter qa\n6496 tai le letter kha\n6497 tai le letter tsha\n6498 tai le letter na\n6499 tai le letter a\n6500 tai le letter i\n6501 tai le letter ee\n6502 tai le letter eh\n6503 tai le letter u\n6504 tai le letter oo\n6505 tai le letter o\n6506 tai le letter ue\n6507 tai le letter e\n6508 tai le letter aue\n6509 tai le letter ai\n6512 tai le letter tone-2\n6513 tai le letter tone-3\n6514 tai le letter tone-4\n6515 tai le letter tone-5\n6516 tai le letter tone-6\n6528 new tai lue letter high qa\n6529 new tai lue letter low qa\n6530 new tai lue letter high ka\n6531 new tai lue letter high xa\n6532 new tai lue letter high nga\n6533 new tai lue letter low ka\n6534 new tai lue letter low xa\n6535 new tai lue letter low nga\n6536 new tai lue letter high tsa\n6537 new tai lue letter high sa\n6538 new tai lue letter high ya\n6539 new tai lue letter low tsa\n6540 new tai lue letter low sa\n6541 new tai lue letter low ya\n6542 new tai lue letter high ta\n6543 new tai lue letter high tha\n6544 new tai lue letter high na\n6545 new tai lue letter low ta\n6546 new tai lue letter low tha\n6547 new tai lue letter low na\n6548 new tai lue letter high pa\n6549 new tai lue letter high pha\n6550 new tai lue letter high ma\n6551 new tai lue letter low pa\n6552 new tai lue letter low pha\n6553 new tai lue letter low ma\n6554 new tai lue letter high fa\n6555 new tai lue letter high va\n6556 new tai lue letter high la\n6557 new tai lue letter low fa\n6558 new tai lue letter low va\n6559 new tai lue letter low la\n6560 new tai lue letter high ha\n6561 new tai lue letter high da\n6562 new tai lue letter high ba\n6563 new tai lue letter low ha\n6564 new tai lue letter low da\n6565 new tai lue letter low ba\n6566 new tai lue letter high kva\n6567 new tai lue letter high xva\n6568 new tai lue letter low kva\n6569 new tai lue letter low xva\n6570 new tai lue letter high sua\n6571 new tai lue letter low sua\n6576 new tai lue vowel sign vowel shortener\n6577 new tai lue vowel sign aa\n6578 new tai lue vowel sign ii\n6579 new tai lue vowel sign u\n6580 new tai lue vowel sign uu\n6581 new tai lue vowel sign e\n6582 new tai lue vowel sign ae\n6583 new tai lue vowel sign o\n6584 new tai lue vowel sign oa\n6585 new tai lue vowel sign ue\n6586 new tai lue vowel sign ay\n6587 new tai lue vowel sign aay\n6588 new tai lue vowel sign uy\n6589 new tai lue vowel sign oy\n6590 new tai lue vowel sign oay\n6591 new tai lue vowel sign uey\n6592 new tai lue vowel sign iy\n6593 new tai lue letter final v\n6594 new tai lue letter final ng\n6595 new tai lue letter final n\n6596 new tai lue letter final m\n6597 new tai lue letter final k\n6598 new tai lue letter final d\n6599 new tai lue letter final b\n6600 new tai lue tone mark-1\n6601 new tai lue tone mark-2\n6608 new tai lue digit zero\n6609 new tai lue digit one\n6610 new tai lue digit two\n6611 new tai lue digit three\n6612 new tai lue digit four\n6613 new tai lue digit five\n6614 new tai lue digit six\n6615 new tai lue digit seven\n6616 new tai lue digit eight\n6617 new tai lue digit nine\n6618 new tai lue tham digit one\n6622 new tai lue sign lae\n6623 new tai lue sign laev\n6624 khmer symbol pathamasat\n6625 khmer symbol muoy koet\n6626 khmer symbol pii koet\n6627 khmer symbol bei koet\n6628 khmer symbol buon koet\n6629 khmer symbol pram koet\n6630 khmer symbol pram-muoy koet\n6631 khmer symbol pram-pii koet\n6632 khmer symbol pram-bei koet\n6633 khmer symbol pram-buon koet\n6634 khmer symbol dap koet\n6635 khmer symbol dap-muoy koet\n6636 khmer symbol dap-pii koet\n6637 khmer symbol dap-bei koet\n6638 khmer symbol dap-buon koet\n6639 khmer symbol dap-pram koet\n6640 khmer symbol tuteyasat\n6641 khmer symbol muoy roc\n6642 khmer symbol pii roc\n6643 khmer symbol bei roc\n6644 khmer symbol buon roc\n6645 khmer symbol pram roc\n6646 khmer symbol pram-muoy roc\n6647 khmer symbol pram-pii roc\n6648 khmer symbol pram-bei roc\n6649 khmer symbol pram-buon roc\n6650 khmer symbol dap roc\n6651 khmer symbol dap-muoy roc\n6652 khmer symbol dap-pii roc\n6653 khmer symbol dap-bei roc\n6654 khmer symbol dap-buon roc\n6655 khmer symbol dap-pram roc\n6656 buginese letter ka\n6657 buginese letter ga\n6658 buginese letter nga\n6659 buginese letter ngka\n6660 buginese letter pa\n6661 buginese letter ba\n6662 buginese letter ma\n6663 buginese letter mpa\n6664 buginese letter ta\n6665 buginese letter da\n6666 buginese letter na\n6667 buginese letter nra\n6668 buginese letter ca\n6669 buginese letter ja\n6670 buginese letter nya\n6671 buginese letter nyca\n6672 buginese letter ya\n6673 buginese letter ra\n6674 buginese letter la\n6675 buginese letter va\n6676 buginese letter sa\n6677 buginese letter a\n6678 buginese letter ha\n6679 buginese vowel sign i\n6680 buginese vowel sign u\n6681 buginese vowel sign e\n6682 buginese vowel sign o\n6683 buginese vowel sign ae\n6686 buginese pallawa\n6687 buginese end of section\n6688 tai tham letter high ka\n6689 tai tham letter high kha\n6690 tai tham letter high kxa\n6691 tai tham letter low ka\n6692 tai tham letter low kxa\n6693 tai tham letter low kha\n6694 tai tham letter nga\n6695 tai tham letter high ca\n6696 tai tham letter high cha\n6697 tai tham letter low ca\n6698 tai tham letter low sa\n6699 tai tham letter low cha\n6700 tai tham letter nya\n6701 tai tham letter rata\n6702 tai tham letter high ratha\n6703 tai tham letter da\n6704 tai tham letter low ratha\n6705 tai tham letter rana\n6706 tai tham letter high ta\n6707 tai tham letter high tha\n6708 tai tham letter low ta\n6709 tai tham letter low tha\n6710 tai tham letter na\n6711 tai tham letter ba\n6712 tai tham letter high pa\n6713 tai tham letter high pha\n6714 tai tham letter high fa\n6715 tai tham letter low pa\n6716 tai tham letter low fa\n6717 tai tham letter low pha\n6718 tai tham letter ma\n6719 tai tham letter low ya\n6720 tai tham letter high ya\n6721 tai tham letter ra\n6722 tai tham letter rue\n6723 tai tham letter la\n6724 tai tham letter lue\n6725 tai tham letter wa\n6726 tai tham letter high sha\n6727 tai tham letter high ssa\n6728 tai tham letter high sa\n6729 tai tham letter high ha\n6730 tai tham letter lla\n6731 tai tham letter a\n6732 tai tham letter low ha\n6733 tai tham letter i\n6734 tai tham letter ii\n6735 tai tham letter u\n6736 tai tham letter uu\n6737 tai tham letter ee\n6738 tai tham letter oo\n6739 tai tham letter lae\n6740 tai tham letter great sa\n6741 tai tham consonant sign medial ra\n6742 tai tham consonant sign medial la\n6743 tai tham consonant sign la tang lai\n6744 tai tham sign mai kang lai\n6745 tai tham consonant sign final nga\n6746 tai tham consonant sign low pa\n6747 tai tham consonant sign high ratha or low pa\n6748 tai tham consonant sign ma\n6749 tai tham consonant sign ba\n6750 tai tham consonant sign sa\n6752 tai tham sign sakot\n6753 tai tham vowel sign a\n6754 tai tham vowel sign mai sat\n6755 tai tham vowel sign aa\n6756 tai tham vowel sign tall aa\n6757 tai tham vowel sign i\n6758 tai tham vowel sign ii\n6759 tai tham vowel sign ue\n6760 tai tham vowel sign uue\n6761 tai tham vowel sign u\n6762 tai tham vowel sign uu\n6763 tai tham vowel sign o\n6764 tai tham vowel sign oa below\n6765 tai tham vowel sign oy\n6766 tai tham vowel sign e\n6767 tai tham vowel sign ae\n6768 tai tham vowel sign oo\n6769 tai tham vowel sign ai\n6770 tai tham vowel sign tham ai\n6771 tai tham vowel sign oa above\n6772 tai tham sign mai kang\n6773 tai tham sign tone-1\n6774 tai tham sign tone-2\n6775 tai tham sign khuen tone-3\n6776 tai tham sign khuen tone-4\n6777 tai tham sign khuen tone-5\n6778 tai tham sign ra haam\n6779 tai tham sign mai sam\n6780 tai tham sign khuen-lue karan\n6783 tai tham combining cryptogrammic dot\n6784 tai tham hora digit zero\n6785 tai tham hora digit one\n6786 tai tham hora digit two\n6787 tai tham hora digit three\n6788 tai tham hora digit four\n6789 tai tham hora digit five\n6790 tai tham hora digit six\n6791 tai tham hora digit seven\n6792 tai tham hora digit eight\n6793 tai tham hora digit nine\n6800 tai tham tham digit zero\n6801 tai tham tham digit one\n6802 tai tham tham digit two\n6803 tai tham tham digit three\n6804 tai tham tham digit four\n6805 tai tham tham digit five\n6806 tai tham tham digit six\n6807 tai tham tham digit seven\n6808 tai tham tham digit eight\n6809 tai tham tham digit nine\n6816 tai tham sign wiang\n6817 tai tham sign wiangwaak\n6818 tai tham sign sawan\n6819 tai tham sign keow\n6820 tai tham sign hoy\n6821 tai tham sign dokmai\n6822 tai tham sign reversed rotated rana\n6823 tai tham sign mai yamok\n6824 tai tham sign kaan\n6825 tai tham sign kaankuu\n6826 tai tham sign satkaan\n6827 tai tham sign satkaankuu\n6828 tai tham sign hang\n6829 tai tham sign caang\n6832 combining doubled circumflex accent\n6833 combining diaeresis-ring\n6834 combining infinity\n6835 combining downwards arrow\n6836 combining triple dot\n6837 combining x-x below\n6838 combining wiggly line below\n6839 combining open mark below\n6840 combining double open mark below\n6841 combining light centralization stroke below\n6842 combining strong centralization stroke below\n6843 combining parentheses above\n6844 combining double parentheses above\n6845 combining parentheses below\n6846 combining parentheses overlay\n6847 combining latin small letter w below\n6848 combining latin small letter turned w below\n6849 combining left parenthesis above left\n6850 combining right parenthesis above right\n6851 combining left parenthesis below left\n6852 combining right parenthesis below right\n6853 combining square brackets above\n6854 combining number sign above\n6855 combining inverted double arch above\n6856 combining plus sign above\n6857 combining double plus sign above\n6858 combining double plus sign below\n6859 combining triple acute accent\n6860 combining latin small letter insular g\n6861 combining latin small letter insular r\n6862 combining latin small letter insular t\n6863 combining double caron\n6864 combining vertical-line-acute\n6865 combining grave-vertical-line\n6866 combining vertical-line-grave\n6867 combining acute-vertical-line\n6868 combining vertical-line-macron\n6869 combining macron-vertical-line\n6870 combining vertical-line-acute-grave\n6871 combining vertical-line-grave-acute\n6872 combining macron-acute-grave\n6873 combining sharp sign\n6874 combining flat sign\n6875 combining down tack above\n6876 combining diaeresis with raised left dot\n6877 combining dot-and-ring below\n6880 combining left tack above\n6881 combining right tack above\n6882 combining minus sign above\n6883 combining inverted bridge above\n6884 combining square above\n6885 combining seagull above\n6886 combining double arch below\n6887 combining double arch above\n6888 combining equals sign above\n6889 combining left angle centred above\n6890 combining upwards arrow above\n6891 combining double rightwards arrow above\n6912 balinese sign ulu ricem\n6913 balinese sign ulu candra\n6914 balinese sign cecek\n6915 balinese sign surang\n6916 balinese sign bisah\n6917 balinese letter akara\n6918 balinese letter akara tedung\n6919 balinese letter ikara\n6920 balinese letter ikara tedung\n6921 balinese letter ukara\n6922 balinese letter ukara tedung\n6923 balinese letter ra repa\n6924 balinese letter ra repa tedung\n6925 balinese letter la lenga\n6926 balinese letter la lenga tedung\n6927 balinese letter ekara\n6928 balinese letter aikara\n6929 balinese letter okara\n6930 balinese letter okara tedung\n6931 balinese letter ka\n6932 balinese letter ka mahaprana\n6933 balinese letter ga\n6934 balinese letter ga gora\n6935 balinese letter nga\n6936 balinese letter ca\n6937 balinese letter ca laca\n6938 balinese letter ja\n6939 balinese letter ja jera\n6940 balinese letter nya\n6941 balinese letter ta latik\n6942 balinese letter ta murda mahaprana\n6943 balinese letter da murda alpaprana\n6944 balinese letter da murda mahaprana\n6945 balinese letter na rambat\n6946 balinese letter ta\n6947 balinese letter ta tawa\n6948 balinese letter da\n6949 balinese letter da madu\n6950 balinese letter na\n6951 balinese letter pa\n6952 balinese letter pa kapal\n6953 balinese letter ba\n6954 balinese letter ba kembang\n6955 balinese letter ma\n6956 balinese letter ya\n6957 balinese letter ra\n6958 balinese letter la\n6959 balinese letter wa\n6960 balinese letter sa saga\n6961 balinese letter sa sapa\n6962 balinese letter sa\n6963 balinese letter ha\n6964 balinese sign rerekan\n6965 balinese vowel sign tedung\n6966 balinese vowel sign ulu\n6967 balinese vowel sign ulu sari\n6968 balinese vowel sign suku\n6969 balinese vowel sign suku ilut\n6970 balinese vowel sign ra repa\n6971 balinese vowel sign ra repa tedung\n6972 balinese vowel sign la lenga\n6973 balinese vowel sign la lenga tedung\n6974 balinese vowel sign taling\n6975 balinese vowel sign taling repa\n6976 balinese vowel sign taling tedung\n6977 balinese vowel sign taling repa tedung\n6978 balinese vowel sign pepet\n6979 balinese vowel sign pepet tedung\n6980 balinese adeg adeg\n6981 balinese letter kaf sasak\n6982 balinese letter khot sasak\n6983 balinese letter tzir sasak\n6984 balinese letter ef sasak\n6985 balinese letter ve sasak\n6986 balinese letter zal sasak\n6987 balinese letter asyura sasak\n6988 balinese letter archaic jnya\n6990 balinese inverted carik siki\n6991 balinese inverted carik pareren\n6992 balinese digit zero\n6993 balinese digit one\n6994 balinese digit two\n6995 balinese digit three\n6996 balinese digit four\n6997 balinese digit five\n6998 balinese digit six\n6999 balinese digit seven\n7000 balinese digit eight\n7001 balinese digit nine\n7002 balinese panti\n7003 balinese pamada\n7004 balinese windu\n7005 balinese carik pamungkah\n7006 balinese carik siki\n7007 balinese carik pareren\n7008 balinese pameneng\n7009 balinese musical symbol dong\n7010 balinese musical symbol deng\n7011 balinese musical symbol dung\n7012 balinese musical symbol dang\n7013 balinese musical symbol dang surang\n7014 balinese musical symbol ding\n7015 balinese musical symbol daeng\n7016 balinese musical symbol deung\n7017 balinese musical symbol daing\n7018 balinese musical symbol dang gede\n7019 balinese musical symbol combining tegeh\n7020 balinese musical symbol combining endep\n7021 balinese musical symbol combining kempul\n7022 balinese musical symbol combining kempli\n7023 balinese musical symbol combining jegogan\n7024 balinese musical symbol combining kempul with jegogan\n7025 balinese musical symbol combining kempli with jegogan\n7026 balinese musical symbol combining bende\n7027 balinese musical symbol combining gong\n7028 balinese musical symbol right-hand open dug\n7029 balinese musical symbol right-hand open dag\n7030 balinese musical symbol right-hand closed tuk\n7031 balinese musical symbol right-hand closed tak\n7032 balinese musical symbol left-hand open pang\n7033 balinese musical symbol left-hand open pung\n7034 balinese musical symbol left-hand closed plak\n7035 balinese musical symbol left-hand closed pluk\n7036 balinese musical symbol left-hand open ping\n7037 balinese panti lantang\n7038 balinese pamada lantang\n7039 balinese panti bawak\n7040 sundanese sign panyecek\n7041 sundanese sign panglayar\n7042 sundanese sign pangwisad\n7043 sundanese letter a\n7044 sundanese letter i\n7045 sundanese letter u\n7046 sundanese letter ae\n7047 sundanese letter o\n7048 sundanese letter e\n7049 sundanese letter eu\n7050 sundanese letter ka\n7051 sundanese letter qa\n7052 sundanese letter ga\n7053 sundanese letter nga\n7054 sundanese letter ca\n7055 sundanese letter ja\n7056 sundanese letter za\n7057 sundanese letter nya\n7058 sundanese letter ta\n7059 sundanese letter da\n7060 sundanese letter na\n7061 sundanese letter pa\n7062 sundanese letter fa\n7063 sundanese letter va\n7064 sundanese letter ba\n7065 sundanese letter ma\n7066 sundanese letter ya\n7067 sundanese letter ra\n7068 sundanese letter la\n7069 sundanese letter wa\n7070 sundanese letter sa\n7071 sundanese letter xa\n7072 sundanese letter ha\n7073 sundanese consonant sign pamingkal\n7074 sundanese consonant sign panyakra\n7075 sundanese consonant sign panyiku\n7076 sundanese vowel sign panghulu\n7077 sundanese vowel sign panyuku\n7078 sundanese vowel sign panaelaeng\n7079 sundanese vowel sign panolong\n7080 sundanese vowel sign pamepet\n7081 sundanese vowel sign paneuleung\n7082 sundanese sign pamaaeh\n7083 sundanese sign virama\n7084 sundanese consonant sign pasangan ma\n7085 sundanese consonant sign pasangan wa\n7086 sundanese letter kha\n7087 sundanese letter sya\n7088 sundanese digit zero\n7089 sundanese digit one\n7090 sundanese digit two\n7091 sundanese digit three\n7092 sundanese digit four\n7093 sundanese digit five\n7094 sundanese digit six\n7095 sundanese digit seven\n7096 sundanese digit eight\n7097 sundanese digit nine\n7098 sundanese avagraha\n7099 sundanese letter reu\n7100 sundanese letter leu\n7101 sundanese letter bha\n7102 sundanese letter final k\n7103 sundanese letter final m\n7104 batak letter a\n7105 batak letter simalungun a\n7106 batak letter ha\n7107 batak letter simalungun ha\n7108 batak letter mandailing ha\n7109 batak letter ba\n7110 batak letter karo ba\n7111 batak letter pa\n7112 batak letter simalungun pa\n7113 batak letter na\n7114 batak letter mandailing na\n7115 batak letter wa\n7116 batak letter simalungun wa\n7117 batak letter pakpak wa\n7118 batak letter ga\n7119 batak letter simalungun ga\n7120 batak letter ja\n7121 batak letter da\n7122 batak letter ra\n7123 batak letter simalungun ra\n7124 batak letter ma\n7125 batak letter simalungun ma\n7126 batak letter southern ta\n7127 batak letter northern ta\n7128 batak letter sa\n7129 batak letter simalungun sa\n7130 batak letter mandailing sa\n7131 batak letter ya\n7132 batak letter simalungun ya\n7133 batak letter nga\n7134 batak letter la\n7135 batak letter simalungun la\n7136 batak letter nya\n7137 batak letter ca\n7138 batak letter nda\n7139 batak letter mba\n7140 batak letter i\n7141 batak letter u\n7142 batak sign tompi\n7143 batak vowel sign e\n7144 batak vowel sign pakpak e\n7145 batak vowel sign ee\n7146 batak vowel sign i\n7147 batak vowel sign karo i\n7148 batak vowel sign o\n7149 batak vowel sign karo o\n7150 batak vowel sign u\n7151 batak vowel sign u for simalungun sa\n7152 batak consonant sign ng\n7153 batak consonant sign h\n7154 batak pangolat\n7155 batak panongonan\n7164 batak symbol bindu na metek\n7165 batak symbol bindu pinarboras\n7166 batak symbol bindu judul\n7167 batak symbol bindu pangolat\n7168 lepcha letter ka\n7169 lepcha letter kla\n7170 lepcha letter kha\n7171 lepcha letter ga\n7172 lepcha letter gla\n7173 lepcha letter nga\n7174 lepcha letter ca\n7175 lepcha letter cha\n7176 lepcha letter ja\n7177 lepcha letter nya\n7178 lepcha letter ta\n7179 lepcha letter tha\n7180 lepcha letter da\n7181 lepcha letter na\n7182 lepcha letter pa\n7183 lepcha letter pla\n7184 lepcha letter pha\n7185 lepcha letter fa\n7186 lepcha letter fla\n7187 lepcha letter ba\n7188 lepcha letter bla\n7189 lepcha letter ma\n7190 lepcha letter mla\n7191 lepcha letter tsa\n7192 lepcha letter tsha\n7193 lepcha letter dza\n7194 lepcha letter ya\n7195 lepcha letter ra\n7196 lepcha letter la\n7197 lepcha letter ha\n7198 lepcha letter hla\n7199 lepcha letter va\n7200 lepcha letter sa\n7201 lepcha letter sha\n7202 lepcha letter wa\n7203 lepcha letter a\n7204 lepcha subjoined letter ya\n7205 lepcha subjoined letter ra\n7206 lepcha vowel sign aa\n7207 lepcha vowel sign i\n7208 lepcha vowel sign o\n7209 lepcha vowel sign oo\n7210 lepcha vowel sign u\n7211 lepcha vowel sign uu\n7212 lepcha vowel sign e\n7213 lepcha consonant sign k\n7214 lepcha consonant sign m\n7215 lepcha consonant sign l\n7216 lepcha consonant sign n\n7217 lepcha consonant sign p\n7218 lepcha consonant sign r\n7219 lepcha consonant sign t\n7220 lepcha consonant sign nyin-do\n7221 lepcha consonant sign kang\n7222 lepcha sign ran\n7223 lepcha sign nukta\n7227 lepcha punctuation ta-rol\n7228 lepcha punctuation nyet thyoom ta-rol\n7229 lepcha punctuation cer-wa\n7230 lepcha punctuation tshook cer-wa\n7231 lepcha punctuation tshook\n7232 lepcha digit zero\n7233 lepcha digit one\n7234 lepcha digit two\n7235 lepcha digit three\n7236 lepcha digit four\n7237 lepcha digit five\n7238 lepcha digit six\n7239 lepcha digit seven\n7240 lepcha digit eight\n7241 lepcha digit nine\n7245 lepcha letter tta\n7246 lepcha letter ttha\n7247 lepcha letter dda\n7248 ol chiki digit zero\n7249 ol chiki digit one\n7250 ol chiki digit two\n7251 ol chiki digit three\n7252 ol chiki digit four\n7253 ol chiki digit five\n7254 ol chiki digit six\n7255 ol chiki digit seven\n7256 ol chiki digit eight\n7257 ol chiki digit nine\n7258 ol chiki letter la\n7259 ol chiki letter at\n7260 ol chiki letter ag\n7261 ol chiki letter ang\n7262 ol chiki letter al\n7263 ol chiki letter laa\n7264 ol chiki letter aak\n7265 ol chiki letter aaj\n7266 ol chiki letter aam\n7267 ol chiki letter aaw\n7268 ol chiki letter li\n7269 ol chiki letter is\n7270 ol chiki letter ih\n7271 ol chiki letter iny\n7272 ol chiki letter ir\n7273 ol chiki letter lu\n7274 ol chiki letter uc\n7275 ol chiki letter ud\n7276 ol chiki letter unn\n7277 ol chiki letter uy\n7278 ol chiki letter le\n7279 ol chiki letter ep\n7280 ol chiki letter edd\n7281 ol chiki letter en\n7282 ol chiki letter err\n7283 ol chiki letter lo\n7284 ol chiki letter ott\n7285 ol chiki letter ob\n7286 ol chiki letter ov\n7287 ol chiki letter oh\n7288 ol chiki mu ttuddag\n7289 ol chiki gaahlaa ttuddaag\n7290 ol chiki mu-gaahlaa ttuddaag\n7291 ol chiki relaa\n7292 ol chiki phaarkaa\n7293 ol chiki ahad\n7294 ol chiki punctuation mucaad\n7295 ol chiki punctuation double mucaad\n7296 cyrillic small letter rounded ve\n7297 cyrillic small letter long-legged de\n7298 cyrillic small letter narrow o\n7299 cyrillic small letter wide es\n7300 cyrillic small letter tall te\n7301 cyrillic small letter three-legged te\n7302 cyrillic small letter tall hard sign\n7303 cyrillic small letter tall yat\n7304 cyrillic small letter unblended uk\n7305 cyrillic capital letter tje\n7306 cyrillic small letter tje\n7312 georgian mtavruli capital letter an\n7313 georgian mtavruli capital letter ban\n7314 georgian mtavruli capital letter gan\n7315 georgian mtavruli capital letter don\n7316 georgian mtavruli capital letter en\n7317 georgian mtavruli capital letter vin\n7318 georgian mtavruli capital letter zen\n7319 georgian mtavruli capital letter tan\n7320 georgian mtavruli capital letter in\n7321 georgian mtavruli capital letter kan\n7322 georgian mtavruli capital letter las\n7323 georgian mtavruli capital letter man\n7324 georgian mtavruli capital letter nar\n7325 georgian mtavruli capital letter on\n7326 georgian mtavruli capital letter par\n7327 georgian mtavruli capital letter zhar\n7328 georgian mtavruli capital letter rae\n7329 georgian mtavruli capital letter san\n7330 georgian mtavruli capital letter tar\n7331 georgian mtavruli capital letter un\n7332 georgian mtavruli capital letter phar\n7333 georgian mtavruli capital letter khar\n7334 georgian mtavruli capital letter ghan\n7335 georgian mtavruli capital letter qar\n7336 georgian mtavruli capital letter shin\n7337 georgian mtavruli capital letter chin\n7338 georgian mtavruli capital letter can\n7339 georgian mtavruli capital letter jil\n7340 georgian mtavruli capital letter cil\n7341 georgian mtavruli capital letter char\n7342 georgian mtavruli capital letter xan\n7343 georgian mtavruli capital letter jhan\n7344 georgian mtavruli capital letter hae\n7345 georgian mtavruli capital letter he\n7346 georgian mtavruli capital letter hie\n7347 georgian mtavruli capital letter we\n7348 georgian mtavruli capital letter har\n7349 georgian mtavruli capital letter hoe\n7350 georgian mtavruli capital letter fi\n7351 georgian mtavruli capital letter yn\n7352 georgian mtavruli capital letter elifi\n7353 georgian mtavruli capital letter turned gan\n7354 georgian mtavruli capital letter ain\n7357 georgian mtavruli capital letter aen\n7358 georgian mtavruli capital letter hard sign\n7359 georgian mtavruli capital letter labial sign\n7360 sundanese punctuation bindu surya\n7361 sundanese punctuation bindu panglong\n7362 sundanese punctuation bindu purnama\n7363 sundanese punctuation bindu cakra\n7364 sundanese punctuation bindu leu satanga\n7365 sundanese punctuation bindu ka satanga\n7366 sundanese punctuation bindu da satanga\n7367 sundanese punctuation bindu ba satanga\n7376 vedic tone karshana\n7377 vedic tone shara\n7378 vedic tone prenkha\n7379 vedic sign nihshvasa\n7380 vedic sign yajurvedic midline svarita\n7381 vedic tone yajurvedic aggravated independent svarita\n7382 vedic tone yajurvedic independent svarita\n7383 vedic tone yajurvedic kathaka independent svarita\n7384 vedic tone candra below\n7385 vedic tone yajurvedic kathaka independent svarita schroeder\n7386 vedic tone double svarita\n7387 vedic tone triple svarita\n7388 vedic tone kathaka anudatta\n7389 vedic tone dot below\n7390 vedic tone two dots below\n7391 vedic tone three dots below\n7392 vedic tone rigvedic kashmiri independent svarita\n7393 vedic tone atharvavedic independent svarita\n7394 vedic sign visarga svarita\n7395 vedic sign visarga udatta\n7396 vedic sign reversed visarga udatta\n7397 vedic sign visarga anudatta\n7398 vedic sign reversed visarga anudatta\n7399 vedic sign visarga udatta with tail\n7400 vedic sign visarga anudatta with tail\n7401 vedic sign anusvara antargomukha\n7402 vedic sign anusvara bahirgomukha\n7403 vedic sign anusvara vamagomukha\n7404 vedic sign anusvara vamagomukha with tail\n7405 vedic sign tiryak\n7406 vedic sign hexiform long anusvara\n7407 vedic sign long anusvara\n7408 vedic sign rthang long anusvara\n7409 vedic sign anusvara ubhayato mukha\n7410 vedic sign ardhavisarga\n7411 vedic sign rotated ardhavisarga\n7412 vedic tone candra above\n7413 vedic sign jihvamuliya\n7414 vedic sign upadhmaniya\n7415 vedic sign atikrama\n7416 vedic tone ring above\n7417 vedic tone double ring above\n7418 vedic sign double anusvara antargomukha\n7424 latin letter small capital a\n7425 latin letter small capital ae\n7426 latin small letter turned ae\n7427 latin letter small capital barred b\n7428 latin letter small capital c\n7429 latin letter small capital d\n7430 latin letter small capital eth\n7431 latin letter small capital e\n7432 latin small letter turned open e\n7433 latin small letter turned i\n7434 latin letter small capital j\n7435 latin letter small capital k\n7436 latin letter small capital l with stroke\n7437 latin letter small capital m\n7438 latin letter small capital reversed n\n7439 latin letter small capital o\n7440 latin letter small capital open o\n7441 latin small letter sideways o\n7442 latin small letter sideways open o\n7443 latin small letter sideways o with stroke\n7444 latin small letter turned oe\n7445 latin letter small capital ou\n7446 latin small letter top half o\n7447 latin small letter bottom half o\n7448 latin letter small capital p\n7449 latin letter small capital reversed r\n7450 latin letter small capital turned r\n7451 latin letter small capital t\n7452 latin letter small capital u\n7453 latin small letter sideways u\n7454 latin small letter sideways diaeresized u\n7455 latin small letter sideways turned m\n7456 latin letter small capital v\n7457 latin letter small capital w\n7458 latin letter small capital z\n7459 latin letter small capital ezh\n7460 latin letter voiced laryngeal spirant\n7461 latin letter ain\n7462 greek letter small capital gamma\n7463 greek letter small capital lamda\tlambda\n7464 greek letter small capital pi\n7465 greek letter small capital rho\n7466 greek letter small capital psi\n7467 cyrillic letter small capital el\n7468 modifier letter capital a\n7469 modifier letter capital ae\n7470 modifier letter capital b\n7471 modifier letter capital barred b\n7472 modifier letter capital d\n7473 modifier letter capital e\n7474 modifier letter capital reversed e\n7475 modifier letter capital g\n7476 modifier letter capital h\n7477 modifier letter capital i\n7478 modifier letter capital j\n7479 modifier letter capital k\n7480 modifier letter capital l\n7481 modifier letter capital m\n7482 modifier letter capital n\n7483 modifier letter capital reversed n\n7484 modifier letter capital o\n7485 modifier letter capital ou\n7486 modifier letter capital p\n7487 modifier letter capital r\n7488 modifier letter capital t\n7489 modifier letter capital u\n7490 modifier letter capital w\n7491 modifier letter small a\n7492 modifier letter small turned a\n7493 modifier letter small alpha\n7494 modifier letter small turned ae\n7495 modifier letter small b\n7496 modifier letter small d\n7497 modifier letter small e\n7498 modifier letter small schwa\n7499 modifier letter small open e\n7500 modifier letter small turned open e\n7501 modifier letter small g\n7502 modifier letter small turned i\n7503 modifier letter small k\n7504 modifier letter small m\n7505 modifier letter small eng\n7506 modifier letter small o\n7507 modifier letter small open o\n7508 modifier letter small top half o\n7509 modifier letter small bottom half o\n7510 modifier letter small p\n7511 modifier letter small t\n7512 modifier letter small u\n7513 modifier letter small sideways u\n7514 modifier letter small turned m\n7515 modifier letter small v\n7516 modifier letter small ain\n7517 modifier letter small beta\n7518 modifier letter small greek gamma\n7519 modifier letter small delta\n7520 modifier letter small greek phi\n7521 modifier letter small chi\n7522 latin subscript small letter i\n7523 latin subscript small letter r\n7524 latin subscript small letter u\n7525 latin subscript small letter v\n7526 greek subscript small letter beta\n7527 greek subscript small letter gamma\n7528 greek subscript small letter rho\n7529 greek subscript small letter phi\n7530 greek subscript small letter chi\n7531 latin small letter ue\n7532 latin small letter b with middle tilde\n7533 latin small letter d with middle tilde\n7534 latin small letter f with middle tilde\n7535 latin small letter m with middle tilde\n7536 latin small letter n with middle tilde\n7537 latin small letter p with middle tilde\n7538 latin small letter r with middle tilde\n7539 latin small letter r with fishhook and middle tilde\n7540 latin small letter s with middle tilde\n7541 latin small letter t with middle tilde\n7542 latin small letter z with middle tilde\n7543 latin small letter turned g\n7544 modifier letter cyrillic en\n7545 latin small letter insular g\n7546 latin small letter th with strikethrough\n7547 latin small capital letter i with stroke\n7548 latin small letter iota with stroke\n7549 latin small letter p with stroke\n7550 latin small capital letter u with stroke\n7551 latin small letter upsilon with stroke\n7552 latin small letter b with palatal hook\n7553 latin small letter d with palatal hook\n7554 latin small letter f with palatal hook\n7555 latin small letter g with palatal hook\n7556 latin small letter k with palatal hook\n7557 latin small letter l with palatal hook\n7558 latin small letter m with palatal hook\n7559 latin small letter n with palatal hook\n7560 latin small letter p with palatal hook\n7561 latin small letter r with palatal hook\n7562 latin small letter s with palatal hook\n7563 latin small letter esh with palatal hook\n7564 latin small letter v with palatal hook\n7565 latin small letter x with palatal hook\n7566 latin small letter z with palatal hook\n7567 latin small letter a with retroflex hook\n7568 latin small letter alpha with retroflex hook\n7569 latin small letter d with hook and tail\n7570 latin small letter e with retroflex hook\n7571 latin small letter open e with retroflex hook\n7572 latin small letter reversed open e with retroflex hook\n7573 latin small letter schwa with retroflex hook\n7574 latin small letter i with retroflex hook\n7575 latin small letter open o with retroflex hook\n7576 latin small letter esh with retroflex hook\n7577 latin small letter u with retroflex hook\n7578 latin small letter ezh with retroflex hook\n7579 modifier letter small turned alpha\n7580 modifier letter small c\n7581 modifier letter small c with curl\n7582 modifier letter small eth\n7583 modifier letter small reversed open e\n7584 modifier letter small f\n7585 modifier letter small dotless j with stroke\n7586 modifier letter small script g\n7587 modifier letter small turned h\n7588 modifier letter small i with stroke\n7589 modifier letter small iota\n7590 modifier letter small capital i\n7591 modifier letter small capital i with stroke\n7592 modifier letter small j with crossed-tail\n7593 modifier letter small l with retroflex hook\n7594 modifier letter small l with palatal hook\n7595 modifier letter small capital l\n7596 modifier letter small m with hook\n7597 modifier letter small turned m with long leg\n7598 modifier letter small n with left hook\n7599 modifier letter small n with retroflex hook\n7600 modifier letter small capital n\n7601 modifier letter small barred o\n7602 modifier letter small phi\n7603 modifier letter small s with hook\n7604 modifier letter small esh\n7605 modifier letter small t with palatal hook\n7606 modifier letter small u bar\n7607 modifier letter small upsilon\n7608 modifier letter small capital u\n7609 modifier letter small v with hook\n7610 modifier letter small turned v\n7611 modifier letter small z\n7612 modifier letter small z with retroflex hook\n7613 modifier letter small z with curl\n7614 modifier letter small ezh\n7615 modifier letter small theta\n7616 combining dotted grave accent\n7617 combining dotted acute accent\n7618 combining snake below\n7619 combining suspension mark\n7620 combining macron-acute\n7621 combining grave-macron\n7622 combining macron-grave\n7623 combining acute-macron\n7624 combining grave-acute-grave\n7625 combining acute-grave-acute\n7626 combining latin small letter r below\n7627 combining breve-macron\n7628 combining macron-breve\n7629 combining double circumflex above\n7630 combining ogonek above\n7631 combining zigzag below\n7632 combining is below\n7633 combining ur above\n7634 combining us above\n7635 combining latin small letter flattened open a above\n7636 combining latin small letter ae\n7637 combining latin small letter ao\n7638 combining latin small letter av\n7639 combining latin small letter c cedilla\n7640 combining latin small letter insular d\n7641 combining latin small letter eth\n7642 combining latin small letter g\n7643 combining latin letter small capital g\n7644 combining latin small letter k\n7645 combining latin small letter l\n7646 combining latin letter small capital l\n7647 combining latin letter small capital m\n7648 combining latin small letter n\n7649 combining latin letter small capital n\n7650 combining latin letter small capital r\n7651 combining latin small letter r rotunda\n7652 combining latin small letter s\n7653 combining latin small letter long s\n7654 combining latin small letter z\n7655 combining latin small letter alpha\n7656 combining latin small letter b\n7657 combining latin small letter beta\n7658 combining latin small letter schwa\n7659 combining latin small letter f\n7660 combining latin small letter l with double middle tilde\n7661 combining latin small letter o with light centralization stroke\n7662 combining latin small letter p\n7663 combining latin small letter esh\n7664 combining latin small letter u with light centralization stroke\n7665 combining latin small letter w\n7666 combining latin small letter a with diaeresis\n7667 combining latin small letter o with diaeresis\n7668 combining latin small letter u with diaeresis\n7669 combining up tack above\n7670 combining kavyka above right\n7671 combining kavyka above left\n7672 combining dot above left\n7673 combining wide inverted bridge below\n7674 combining dot below left\n7675 combining deletion mark\n7676 combining double inverted breve below\n7677 combining almost equal to below\n7678 combining left arrowhead above\n7679 combining right arrowhead and down arrowhead below\n7680 latin capital letter a with ring below\n7681 latin small letter a with ring below\n7682 latin capital letter b with dot above\n7683 latin small letter b with dot above\n7684 latin capital letter b with dot below\n7685 latin small letter b with dot below\n7686 latin capital letter b with line below\n7687 latin small letter b with line below\n7688 latin capital letter c with cedilla and acute\n7689 latin small letter c with cedilla and acute\n7690 latin capital letter d with dot above\n7691 latin small letter d with dot above\n7692 latin capital letter d with dot below\n7693 latin small letter d with dot below\n7694 latin capital letter d with line below\n7695 latin small letter d with line below\n7696 latin capital letter d with cedilla\n7697 latin small letter d with cedilla\n7698 latin capital letter d with circumflex below\n7699 latin small letter d with circumflex below\n7700 latin capital letter e with macron and grave\n7701 latin small letter e with macron and grave\n7702 latin capital letter e with macron and acute\n7703 latin small letter e with macron and acute\n7704 latin capital letter e with circumflex below\n7705 latin small letter e with circumflex below\n7706 latin capital letter e with tilde below\n7707 latin small letter e with tilde below\n7708 latin capital letter e with cedilla and breve\n7709 latin small letter e with cedilla and breve\n7710 latin capital letter f with dot above\n7711 latin small letter f with dot above\n7712 latin capital letter g with macron\n7713 latin small letter g with macron\n7714 latin capital letter h with dot above\n7715 latin small letter h with dot above\n7716 latin capital letter h with dot below\n7717 latin small letter h with dot below\n7718 latin capital letter h with diaeresis\n7719 latin small letter h with diaeresis\n7720 latin capital letter h with cedilla\n7721 latin small letter h with cedilla\n7722 latin capital letter h with breve below\n7723 latin small letter h with breve below\n7724 latin capital letter i with tilde below\n7725 latin small letter i with tilde below\n7726 latin capital letter i with diaeresis and acute\n7727 latin small letter i with diaeresis and acute\n7728 latin capital letter k with acute\n7729 latin small letter k with acute\n7730 latin capital letter k with dot below\n7731 latin small letter k with dot below\n7732 latin capital letter k with line below\n7733 latin small letter k with line below\n7734 latin capital letter l with dot below\n7735 latin small letter l with dot below\n7736 latin capital letter l with dot below and macron\n7737 latin small letter l with dot below and macron\n7738 latin capital letter l with line below\n7739 latin small letter l with line below\n7740 latin capital letter l with circumflex below\n7741 latin small letter l with circumflex below\n7742 latin capital letter m with acute\n7743 latin small letter m with acute\n7744 latin capital letter m with dot above\n7745 latin small letter m with dot above\n7746 latin capital letter m with dot below\n7747 latin small letter m with dot below\n7748 latin capital letter n with dot above\n7749 latin small letter n with dot above\n7750 latin capital letter n with dot below\n7751 latin small letter n with dot below\n7752 latin capital letter n with line below\n7753 latin small letter n with line below\n7754 latin capital letter n with circumflex below\n7755 latin small letter n with circumflex below\n7756 latin capital letter o with tilde and acute\n7757 latin small letter o with tilde and acute\n7758 latin capital letter o with tilde and diaeresis\n7759 latin small letter o with tilde and diaeresis\n7760 latin capital letter o with macron and grave\n7761 latin small letter o with macron and grave\n7762 latin capital letter o with macron and acute\n7763 latin small letter o with macron and acute\n7764 latin capital letter p with acute\n7765 latin small letter p with acute\n7766 latin capital letter p with dot above\n7767 latin small letter p with dot above\n7768 latin capital letter r with dot above\n7769 latin small letter r with dot above\n7770 latin capital letter r with dot below\n7771 latin small letter r with dot below\n7772 latin capital letter r with dot below and macron\n7773 latin small letter r with dot below and macron\n7774 latin capital letter r with line below\n7775 latin small letter r with line below\n7776 latin capital letter s with dot above\n7777 latin small letter s with dot above\n7778 latin capital letter s with dot below\n7779 latin small letter s with dot below\n7780 latin capital letter s with acute and dot above\n7781 latin small letter s with acute and dot above\n7782 latin capital letter s with caron and dot above\n7783 latin small letter s with caron and dot above\n7784 latin capital letter s with dot below and dot above\n7785 latin small letter s with dot below and dot above\n7786 latin capital letter t with dot above\n7787 latin small letter t with dot above\n7788 latin capital letter t with dot below\n7789 latin small letter t with dot below\n7790 latin capital letter t with line below\n7791 latin small letter t with line below\n7792 latin capital letter t with circumflex below\n7793 latin small letter t with circumflex below\n7794 latin capital letter u with diaeresis below\n7795 latin small letter u with diaeresis below\n7796 latin capital letter u with tilde below\n7797 latin small letter u with tilde below\n7798 latin capital letter u with circumflex below\n7799 latin small letter u with circumflex below\n7800 latin capital letter u with tilde and acute\n7801 latin small letter u with tilde and acute\n7802 latin capital letter u with macron and diaeresis\n7803 latin small letter u with macron and diaeresis\n7804 latin capital letter v with tilde\n7805 latin small letter v with tilde\n7806 latin capital letter v with dot below\n7807 latin small letter v with dot below\n7808 latin capital letter w with grave\n7809 latin small letter w with grave\n7810 latin capital letter w with acute\n7811 latin small letter w with acute\n7812 latin capital letter w with diaeresis\n7813 latin small letter w with diaeresis\n7814 latin capital letter w with dot above\n7815 latin small letter w with dot above\n7816 latin capital letter w with dot below\n7817 latin small letter w with dot below\n7818 latin capital letter x with dot above\n7819 latin small letter x with dot above\n7820 latin capital letter x with diaeresis\n7821 latin small letter x with diaeresis\n7822 latin capital letter y with dot above\n7823 latin small letter y with dot above\n7824 latin capital letter z with circumflex\n7825 latin small letter z with circumflex\n7826 latin capital letter z with dot below\n7827 latin small letter z with dot below\n7828 latin capital letter z with line below\n7829 latin small letter z with line below\n7830 latin small letter h with line below\n7831 latin small letter t with diaeresis\n7832 latin small letter w with ring above\n7833 latin small letter y with ring above\n7834 latin small letter a with right half ring\n7835 latin small letter long s with dot above\n7836 latin small letter long s with diagonal stroke\n7837 latin small letter long s with high stroke\n7838 latin capital letter sharp s\n7839 latin small letter delta\n7840 latin capital letter a with dot below\n7841 latin small letter a with dot below\n7842 latin capital letter a with hook above\n7843 latin small letter a with hook above\n7844 latin capital letter a with circumflex and acute\n7845 latin small letter a with circumflex and acute\n7846 latin capital letter a with circumflex and grave\n7847 latin small letter a with circumflex and grave\n7848 latin capital letter a with circumflex and hook above\n7849 latin small letter a with circumflex and hook above\n7850 latin capital letter a with circumflex and tilde\n7851 latin small letter a with circumflex and tilde\n7852 latin capital letter a with circumflex and dot below\n7853 latin small letter a with circumflex and dot below\n7854 latin capital letter a with breve and acute\n7855 latin small letter a with breve and acute\n7856 latin capital letter a with breve and grave\n7857 latin small letter a with breve and grave\n7858 latin capital letter a with breve and hook above\n7859 latin small letter a with breve and hook above\n7860 latin capital letter a with breve and tilde\n7861 latin small letter a with breve and tilde\n7862 latin capital letter a with breve and dot below\n7863 latin small letter a with breve and dot below\n7864 latin capital letter e with dot below\n7865 latin small letter e with dot below\n7866 latin capital letter e with hook above\n7867 latin small letter e with hook above\n7868 latin capital letter e with tilde\n7869 latin small letter e with tilde\n7870 latin capital letter e with circumflex and acute\n7871 latin small letter e with circumflex and acute\n7872 latin capital letter e with circumflex and grave\n7873 latin small letter e with circumflex and grave\n7874 latin capital letter e with circumflex and hook above\n7875 latin small letter e with circumflex and hook above\n7876 latin capital letter e with circumflex and tilde\n7877 latin small letter e with circumflex and tilde\n7878 latin capital letter e with circumflex and dot below\n7879 latin small letter e with circumflex and dot below\n7880 latin capital letter i with hook above\n7881 latin small letter i with hook above\n7882 latin capital letter i with dot below\n7883 latin small letter i with dot below\n7884 latin capital letter o with dot below\n7885 latin small letter o with dot below\n7886 latin capital letter o with hook above\n7887 latin small letter o with hook above\n7888 latin capital letter o with circumflex and acute\n7889 latin small letter o with circumflex and acute\n7890 latin capital letter o with circumflex and grave\n7891 latin small letter o with circumflex and grave\n7892 latin capital letter o with circumflex and hook above\n7893 latin small letter o with circumflex and hook above\n7894 latin capital letter o with circumflex and tilde\n7895 latin small letter o with circumflex and tilde\n7896 latin capital letter o with circumflex and dot below\n7897 latin small letter o with circumflex and dot below\n7898 latin capital letter o with horn and acute\n7899 latin small letter o with horn and acute\n7900 latin capital letter o with horn and grave\n7901 latin small letter o with horn and grave\n7902 latin capital letter o with horn and hook above\n7903 latin small letter o with horn and hook above\n7904 latin capital letter o with horn and tilde\n7905 latin small letter o with horn and tilde\n7906 latin capital letter o with horn and dot below\n7907 latin small letter o with horn and dot below\n7908 latin capital letter u with dot below\n7909 latin small letter u with dot below\n7910 latin capital letter u with hook above\n7911 latin small letter u with hook above\n7912 latin capital letter u with horn and acute\n7913 latin small letter u with horn and acute\n7914 latin capital letter u with horn and grave\n7915 latin small letter u with horn and grave\n7916 latin capital letter u with horn and hook above\n7917 latin small letter u with horn and hook above\n7918 latin capital letter u with horn and tilde\n7919 latin small letter u with horn and tilde\n7920 latin capital letter u with horn and dot below\n7921 latin small letter u with horn and dot below\n7922 latin capital letter y with grave\n7923 latin small letter y with grave\n7924 latin capital letter y with dot below\n7925 latin small letter y with dot below\n7926 latin capital letter y with hook above\n7927 latin small letter y with hook above\n7928 latin capital letter y with tilde\n7929 latin small letter y with tilde\n7930 latin capital letter middle-welsh ll\n7931 latin small letter middle-welsh ll\n7932 latin capital letter middle-welsh v\n7933 latin small letter middle-welsh v\n7934 latin capital letter y with loop\n7935 latin small letter y with loop\n7936 greek small letter alpha with psili\n7937 greek small letter alpha with dasia\n7938 greek small letter alpha with psili and varia\n7939 greek small letter alpha with dasia and varia\n7940 greek small letter alpha with psili and oxia\n7941 greek small letter alpha with dasia and oxia\n7942 greek small letter alpha with psili and perispomeni\n7943 greek small letter alpha with dasia and perispomeni\n7944 greek capital letter alpha with psili\n7945 greek capital letter alpha with dasia\n7946 greek capital letter alpha with psili and varia\n7947 greek capital letter alpha with dasia and varia\n7948 greek capital letter alpha with psili and oxia\n7949 greek capital letter alpha with dasia and oxia\n7950 greek capital letter alpha with psili and perispomeni\n7951 greek capital letter alpha with dasia and perispomeni\n7952 greek small letter epsilon with psili\n7953 greek small letter epsilon with dasia\n7954 greek small letter epsilon with psili and varia\n7955 greek small letter epsilon with dasia and varia\n7956 greek small letter epsilon with psili and oxia\n7957 greek small letter epsilon with dasia and oxia\n7960 greek capital letter epsilon with psili\n7961 greek capital letter epsilon with dasia\n7962 greek capital letter epsilon with psili and varia\n7963 greek capital letter epsilon with dasia and varia\n7964 greek capital letter epsilon with psili and oxia\n7965 greek capital letter epsilon with dasia and oxia\n7968 greek small letter eta with psili\n7969 greek small letter eta with dasia\n7970 greek small letter eta with psili and varia\n7971 greek small letter eta with dasia and varia\n7972 greek small letter eta with psili and oxia\n7973 greek small letter eta with dasia and oxia\n7974 greek small letter eta with psili and perispomeni\n7975 greek small letter eta with dasia and perispomeni\n7976 greek capital letter eta with psili\n7977 greek capital letter eta with dasia\n7978 greek capital letter eta with psili and varia\n7979 greek capital letter eta with dasia and varia\n7980 greek capital letter eta with psili and oxia\n7981 greek capital letter eta with dasia and oxia\n7982 greek capital letter eta with psili and perispomeni\n7983 greek capital letter eta with dasia and perispomeni\n7984 greek small letter iota with psili\n7985 greek small letter iota with dasia\n7986 greek small letter iota with psili and varia\n7987 greek small letter iota with dasia and varia\n7988 greek small letter iota with psili and oxia\n7989 greek small letter iota with dasia and oxia\n7990 greek small letter iota with psili and perispomeni\n7991 greek small letter iota with dasia and perispomeni\n7992 greek capital letter iota with psili\n7993 greek capital letter iota with dasia\n7994 greek capital letter iota with psili and varia\n7995 greek capital letter iota with dasia and varia\n7996 greek capital letter iota with psili and oxia\n7997 greek capital letter iota with dasia and oxia\n7998 greek capital letter iota with psili and perispomeni\n7999 greek capital letter iota with dasia and perispomeni\n8000 greek small letter omicron with psili\n8001 greek small letter omicron with dasia\n8002 greek small letter omicron with psili and varia\n8003 greek small letter omicron with dasia and varia\n8004 greek small letter omicron with psili and oxia\n8005 greek small letter omicron with dasia and oxia\n8008 greek capital letter omicron with psili\n8009 greek capital letter omicron with dasia\n8010 greek capital letter omicron with psili and varia\n8011 greek capital letter omicron with dasia and varia\n8012 greek capital letter omicron with psili and oxia\n8013 greek capital letter omicron with dasia and oxia\n8016 greek small letter upsilon with psili\n8017 greek small letter upsilon with dasia\n8018 greek small letter upsilon with psili and varia\n8019 greek small letter upsilon with dasia and varia\n8020 greek small letter upsilon with psili and oxia\n8021 greek small letter upsilon with dasia and oxia\n8022 greek small letter upsilon with psili and perispomeni\n8023 greek small letter upsilon with dasia and perispomeni\n8025 greek capital letter upsilon with dasia\n8027 greek capital letter upsilon with dasia and varia\n8029 greek capital letter upsilon with dasia and oxia\n8031 greek capital letter upsilon with dasia and perispomeni\n8032 greek small letter omega with psili\n8033 greek small letter omega with dasia\n8034 greek small letter omega with psili and varia\n8035 greek small letter omega with dasia and varia\n8036 greek small letter omega with psili and oxia\n8037 greek small letter omega with dasia and oxia\n8038 greek small letter omega with psili and perispomeni\n8039 greek small letter omega with dasia and perispomeni\n8040 greek capital letter omega with psili\n8041 greek capital letter omega with dasia\n8042 greek capital letter omega with psili and varia\n8043 greek capital letter omega with dasia and varia\n8044 greek capital letter omega with psili and oxia\n8045 greek capital letter omega with dasia and oxia\n8046 greek capital letter omega with psili and perispomeni\n8047 greek capital letter omega with dasia and perispomeni\n8048 greek small letter alpha with varia\n8049 greek small letter alpha with oxia\n8050 greek small letter epsilon with varia\n8051 greek small letter epsilon with oxia\n8052 greek small letter eta with varia\n8053 greek small letter eta with oxia\n8054 greek small letter iota with varia\n8055 greek small letter iota with oxia\n8056 greek small letter omicron with varia\n8057 greek small letter omicron with oxia\n8058 greek small letter upsilon with varia\n8059 greek small letter upsilon with oxia\n8060 greek small letter omega with varia\n8061 greek small letter omega with oxia\n8064 greek small letter alpha with psili and ypogegrammeni\n8065 greek small letter alpha with dasia and ypogegrammeni\n8066 greek small letter alpha with psili and varia and ypogegrammeni\n8067 greek small letter alpha with dasia and varia and ypogegrammeni\n8068 greek small letter alpha with psili and oxia and ypogegrammeni\n8069 greek small letter alpha with dasia and oxia and ypogegrammeni\n8070 greek small letter alpha with psili and perispomeni and ypogegrammeni\n8071 greek small letter alpha with dasia and perispomeni and ypogegrammeni\n8072 greek capital letter alpha with psili and prosgegrammeni\n8073 greek capital letter alpha with dasia and prosgegrammeni\n8074 greek capital letter alpha with psili and varia and prosgegrammeni\n8075 greek capital letter alpha with dasia and varia and prosgegrammeni\n8076 greek capital letter alpha with psili and oxia and prosgegrammeni\n8077 greek capital letter alpha with dasia and oxia and prosgegrammeni\n8078 greek capital letter alpha with psili and perispomeni and prosgegrammeni\n8079 greek capital letter alpha with dasia and perispomeni and prosgegrammeni\n8080 greek small letter eta with psili and ypogegrammeni\n8081 greek small letter eta with dasia and ypogegrammeni\n8082 greek small letter eta with psili and varia and ypogegrammeni\n8083 greek small letter eta with dasia and varia and ypogegrammeni\n8084 greek small letter eta with psili and oxia and ypogegrammeni\n8085 greek small letter eta with dasia and oxia and ypogegrammeni\n8086 greek small letter eta with psili and perispomeni and ypogegrammeni\n8087 greek small letter eta with dasia and perispomeni and ypogegrammeni\n8088 greek capital letter eta with psili and prosgegrammeni\n8089 greek capital letter eta with dasia and prosgegrammeni\n8090 greek capital letter eta with psili and varia and prosgegrammeni\n8091 greek capital letter eta with dasia and varia and prosgegrammeni\n8092 greek capital letter eta with psili and oxia and prosgegrammeni\n8093 greek capital letter eta with dasia and oxia and prosgegrammeni\n8094 greek capital letter eta with psili and perispomeni and prosgegrammeni\n8095 greek capital letter eta with dasia and perispomeni and prosgegrammeni\n8096 greek small letter omega with psili and ypogegrammeni\n8097 greek small letter omega with dasia and ypogegrammeni\n8098 greek small letter omega with psili and varia and ypogegrammeni\n8099 greek small letter omega with dasia and varia and ypogegrammeni\n8100 greek small letter omega with psili and oxia and ypogegrammeni\n8101 greek small letter omega with dasia and oxia and ypogegrammeni\n8102 greek small letter omega with psili and perispomeni and ypogegrammeni\n8103 greek small letter omega with dasia and perispomeni and ypogegrammeni\n8104 greek capital letter omega with psili and prosgegrammeni\n8105 greek capital letter omega with dasia and prosgegrammeni\n8106 greek capital letter omega with psili and varia and prosgegrammeni\n8107 greek capital letter omega with dasia and varia and prosgegrammeni\n8108 greek capital letter omega with psili and oxia and prosgegrammeni\n8109 greek capital letter omega with dasia and oxia and prosgegrammeni\n8110 greek capital letter omega with psili and perispomeni and prosgegrammeni\n8111 greek capital letter omega with dasia and perispomeni and prosgegrammeni\n8112 greek small letter alpha with vrachy\n8113 greek small letter alpha with macron\n8114 greek small letter alpha with varia and ypogegrammeni\n8115 greek small letter alpha with ypogegrammeni\n8116 greek small letter alpha with oxia and ypogegrammeni\n8118 greek small letter alpha with perispomeni\n8119 greek small letter alpha with perispomeni and ypogegrammeni\n8120 greek capital letter alpha with vrachy\n8121 greek capital letter alpha with macron\n8122 greek capital letter alpha with varia\n8123 greek capital letter alpha with oxia\n8124 greek capital letter alpha with prosgegrammeni\n8125 greek koronis\n8126 greek prosgegrammeni\n8127 greek psili\n8128 greek perispomeni\n8129 greek dialytika and perispomeni\n8130 greek small letter eta with varia and ypogegrammeni\n8131 greek small letter eta with ypogegrammeni\n8132 greek small letter eta with oxia and ypogegrammeni\n8134 greek small letter eta with perispomeni\n8135 greek small letter eta with perispomeni and ypogegrammeni\n8136 greek capital letter epsilon with varia\n8137 greek capital letter epsilon with oxia\n8138 greek capital letter eta with varia\n8139 greek capital letter eta with oxia\n8140 greek capital letter eta with prosgegrammeni\n8141 greek psili and varia\n8142 greek psili and oxia\n8143 greek psili and perispomeni\n8144 greek small letter iota with vrachy\n8145 greek small letter iota with macron\n8146 greek small letter iota with dialytika and varia\n8147 greek small letter iota with dialytika and oxia\n8150 greek small letter iota with perispomeni\n8151 greek small letter iota with dialytika and perispomeni\n8152 greek capital letter iota with vrachy\n8153 greek capital letter iota with macron\n8154 greek capital letter iota with varia\n8155 greek capital letter iota with oxia\n8157 greek dasia and varia\n8158 greek dasia and oxia\n8159 greek dasia and perispomeni\n8160 greek small letter upsilon with vrachy\n8161 greek small letter upsilon with macron\n8162 greek small letter upsilon with dialytika and varia\n8163 greek small letter upsilon with dialytika and oxia\n8164 greek small letter rho with psili\n8165 greek small letter rho with dasia\n8166 greek small letter upsilon with perispomeni\n8167 greek small letter upsilon with dialytika and perispomeni\n8168 greek capital letter upsilon with vrachy\n8169 greek capital letter upsilon with macron\n8170 greek capital letter upsilon with varia\n8171 greek capital letter upsilon with oxia\n8172 greek capital letter rho with dasia\n8173 greek dialytika and varia\n8174 greek dialytika and oxia\n8175 greek varia\n8178 greek small letter omega with varia and ypogegrammeni\n8179 greek small letter omega with ypogegrammeni\n8180 greek small letter omega with oxia and ypogegrammeni\n8182 greek small letter omega with perispomeni\n8183 greek small letter omega with perispomeni and ypogegrammeni\n8184 greek capital letter omicron with varia\n8185 greek capital letter omicron with oxia\n8186 greek capital letter omega with varia\n8187 greek capital letter omega with oxia\n8188 greek capital letter omega with prosgegrammeni\n8189 greek oxia\n8190 greek dasia\n8192 en quad\n8193 em quad\n8194 en space\tensp\n8195 em space\temsp\n8196 three-per-em space\temsp13\n8197 four-per-em space\temsp14\n8198 six-per-em space\n8199 figure space\tnumsp\n8200 punctuation space\tpuncsp\n8201 thin space\tthinsp thinspace\n8202 hair space\thairsp verythinspace\n8203 zero width space\tnegativemediumspace negativethickspace negativethinspace negativeverythinspace zerowidthspace\n8204 zero width non-joiner\tzwnj\n8205 zero width joiner\tzwj\n8206 left-to-right mark\tlrm\n8207 right-to-left mark\trlm\n8208 hyphen\tdash\n8209 non-breaking hyphen\n8210 figure dash\n8211 en dash\tndash\n8212 em dash\tmdash\n8213 horizontal bar\thorbar\n8214 double vertical line\tverbar vert\n8215 double low line\n8216 left single quotation mark\tlsquo opencurlyquote\n8217 right single quotation mark\tclosecurlyquote rsquo rsquor\n8218 single low-9 quotation mark\tlsquor sbquo\n8219 single high-reversed-9 quotation mark\n8220 left double quotation mark\tldquo opencurlydoublequote\n8221 right double quotation mark\tclosecurlydoublequote rdquo rdquor\n8222 double low-9 quotation mark\tbdquo ldquor\n8223 double high-reversed-9 quotation mark\n8224 dagger\n8225 double dagger\tddagger\n8226 bullet\tbull\n8227 triangular bullet\n8228 one dot leader\n8229 two dot leader\tnldr\n8230 horizontal ellipsis\thellip mldr\n8231 hyphenation point\n8232 line separator\n8233 paragraph separator\n8234 left-to-right embedding\n8235 right-to-left embedding\n8236 pop directional formatting\n8237 left-to-right override\n8238 right-to-left override\n8239 narrow no-break space\tnnbsp\n8240 per mille sign\tpermil\n8241 per ten thousand sign\tpertenk\n8242 prime\n8243 double prime\n8244 triple prime\ttprime\n8245 reversed prime\tbackprime bprime\n8246 reversed double prime\n8247 reversed triple prime\n8248 caret\n8249 single left-pointing angle quotation mark\tlsaquo\n8250 single right-pointing angle quotation mark\trsaquo\n8251 reference mark\n8252 double exclamation mark\n8253 interrobang\n8254 overline\toline overbar\n8255 undertie\n8256 character tie\n8257 caret insertion point\n8258 asterism\n8259 hyphen bullet\thybull\n8260 fraction slash\tfrasl\n8261 left square bracket with quill\n8262 right square bracket with quill\n8263 double question mark\n8264 question exclamation mark\n8265 exclamation question mark\n8266 tironian sign et\n8267 reversed pilcrow sign\n8268 black leftwards bullet\n8269 black rightwards bullet\n8270 low asterisk\n8271 reversed semicolon\tbsemi\n8272 close up\n8273 two asterisks aligned vertically\n8274 commercial minus sign\n8275 swung dash\n8276 inverted undertie\n8277 flower punctuation mark\n8278 three dot punctuation\n8279 quadruple prime\tqprime\n8280 four dot punctuation\n8281 five dot punctuation\n8282 two dot punctuation\n8283 four dot mark\n8284 dotted cross\n8285 tricolon\n8286 vertical four dots\n8287 medium mathematical space\tmediumspace\n8288 word joiner\tnobreak\n8289 function application\taf applyfunction\n8290 invisible times\tinvisibletimes it\n8291 invisible separator\tic invisiblecomma\n8292 invisible plus\n8294 left-to-right isolate\n8295 right-to-left isolate\n8296 first strong isolate\n8297 pop directional isolate\n8298 inhibit symmetric swapping\n8299 activate symmetric swapping\n8300 inhibit arabic form shaping\n8301 activate arabic form shaping\n8302 national digit shapes\n8303 nominal digit shapes\n8304 superscript zero\n8305 superscript latin small letter i\n8308 superscript four\n8309 superscript five\n8310 superscript six\n8311 superscript seven\n8312 superscript eight\n8313 superscript nine\n8314 superscript plus sign\n8315 superscript minus\n8316 superscript equals sign\n8317 superscript left parenthesis\n8318 superscript right parenthesis\n8319 superscript latin small letter n\n8320 subscript zero\n8321 subscript one\n8322 subscript two\n8323 subscript three\n8324 subscript four\n8325 subscript five\n8326 subscript six\n8327 subscript seven\n8328 subscript eight\n8329 subscript nine\n8330 subscript plus sign\n8331 subscript minus\n8332 subscript equals sign\n8333 subscript left parenthesis\n8334 subscript right parenthesis\n8336 latin subscript small letter a\n8337 latin subscript small letter e\n8338 latin subscript small letter o\n8339 latin subscript small letter x\n8340 latin subscript small letter schwa\n8341 latin subscript small letter h\n8342 latin subscript small letter k\n8343 latin subscript small letter l\n8344 latin subscript small letter m\n8345 latin subscript small letter n\n8346 latin subscript small letter p\n8347 latin subscript small letter s\n8348 latin subscript small letter t\n8352 euro-currency sign\n8353 colon sign\n8354 cruzeiro sign\n8355 french franc sign\n8356 lira sign\n8357 mill sign\n8358 naira sign\n8359 peseta sign\n8360 rupee sign\n8361 won sign\n8362 new sheqel sign\n8363 dong sign\n8364 euro sign\n8365 kip sign\n8366 tugrik sign\n8367 drachma sign\n8368 german penny sign\n8369 peso sign\n8370 guarani sign\n8371 austral sign\n8372 hryvnia sign\n8373 cedi sign\n8374 livre tournois sign\n8375 spesmilo sign\n8376 tenge sign\n8377 indian rupee sign\n8378 turkish lira sign\n8379 nordic mark sign\n8380 manat sign\n8381 ruble sign\n8382 lari sign\n8383 bitcoin sign\n8384 som sign\n8385 saudi riyal sign\n8400 combining left harpoon above\n8401 combining right harpoon above\n8402 combining long vertical line overlay\n8403 combining short vertical line overlay\n8404 combining anticlockwise arrow above\n8405 combining clockwise arrow above\n8406 combining left arrow above\n8407 combining right arrow above\n8408 combining ring overlay\n8409 combining clockwise ring overlay\n8410 combining anticlockwise ring overlay\n8411 combining three dots above\ttdot tripledot\n8412 combining four dots above\tdotdot\n8413 combining enclosing circle\n8414 combining enclosing square\n8415 combining enclosing diamond\n8416 combining enclosing circle backslash\n8417 combining left right arrow above\n8418 combining enclosing screen\n8419 combining enclosing keycap\n8420 combining enclosing upward pointing triangle\n8421 combining reverse solidus overlay\n8422 combining double vertical stroke overlay\n8423 combining annuity symbol\n8424 combining triple underdot\n8425 combining wide bridge above\n8426 combining leftwards arrow overlay\n8427 combining long double solidus overlay\n8428 combining rightwards harpoon with barb downwards\n8429 combining leftwards harpoon with barb downwards\n8430 combining left arrow below\n8431 combining right arrow below\n8432 combining asterisk above\n8448 account of\n8449 addressed to the subject\n8450 double-struck capital c\tcomplexes copf\n8451 degree celsius\n8452 centre line symbol\n8453 care of\tincare\n8454 cada una\n8455 euler constant\n8456 scruple\n8457 degree fahrenheit\n8458 script small g\tgscr\n8459 script capital h\thamilt hilbertspace hscr\n8460 black-letter capital h\thfr poincareplane\n8461 double-struck capital h\thopf quaternions\n8462 planck constant\tplanckh\n8463 planck constant over two pi\thbar hslash plankv\n8464 script capital i\timagline iscr\n8465 black-letter capital i\tifr im image imagpart\n8466 script capital l\tlagran laplacetrf lscr\n8467 script small l\tell\n8468 l b bar symbol\n8469 double-struck capital n\tnaturals nopf\n8470 numero sign\n8471 sound recording copyright\tcopysr\n8472 script capital p\tweierp wp\n8473 double-struck capital p\tpopf primes\n8474 double-struck capital q\tqopf rationals\n8475 script capital r\trealine rscr\n8476 black-letter capital r\tre real realpart rfr\n8477 double-struck capital r\treals ropf\n8478 prescription take\trx\n8479 response\n8480 service mark\n8481 telephone sign\n8482 trade mark sign\n8483 versicle\n8484 double-struck capital z\tintegers zopf\n8485 ounce sign\n8486 ohm sign\n8487 inverted ohm sign\tmho\n8488 black-letter capital z\tzeetrf zfr\n8489 turned greek small letter iota\tiiota\n8490 kelvin sign\n8491 angstrom sign\n8492 script capital b\tbernou bernoullis bscr\n8493 black-letter capital c\tcayleys cfr\n8494 estimated symbol\n8495 script small e\tescr\n8496 script capital e\tescr expectation\n8497 script capital f\tfouriertrf fscr\n8498 turned capital f\n8499 script capital m\tmellintrf mscr phmmat\n8500 script small o\torder orderof oscr\n8501 alef symbol\talefsym aleph\n8502 bet symbol\tbeth\n8503 gimel symbol\n8504 dalet symbol\tdaleth\n8505 information source\n8506 rotated capital q\n8507 facsimile sign\n8508 double-struck small pi\n8509 double-struck small gamma\n8510 double-struck capital gamma\n8511 double-struck capital pi\n8512 double-struck n-ary summation\n8513 turned sans-serif capital g\n8514 turned sans-serif capital l\n8515 reversed sans-serif capital l\n8516 turned sans-serif capital y\n8517 double-struck italic capital d\tcapitaldifferentiald dd\n8518 double-struck italic small d\tdd differentiald\n8519 double-struck italic small e\tee exponentiale\n8520 double-struck italic small i\tii imaginaryi\n8521 double-struck italic small j\n8522 property line\n8523 turned ampersand\n8524 per sign\n8525 aktieselskab\n8526 turned small f\n8527 symbol for samaritan source\n8528 vulgar fraction one seventh\n8529 vulgar fraction one ninth\n8530 vulgar fraction one tenth\n8531 vulgar fraction one third\tfrac13\n8532 vulgar fraction two thirds\tfrac23\n8533 vulgar fraction one fifth\tfrac15\n8534 vulgar fraction two fifths\tfrac25\n8535 vulgar fraction three fifths\tfrac35\n8536 vulgar fraction four fifths\tfrac45\n8537 vulgar fraction one sixth\tfrac16\n8538 vulgar fraction five sixths\tfrac56\n8539 vulgar fraction one eighth\tfrac18\n8540 vulgar fraction three eighths\tfrac38\n8541 vulgar fraction five eighths\tfrac58\n8542 vulgar fraction seven eighths\tfrac78\n8543 fraction numerator one\n8544 roman numeral one\n8545 roman numeral two\n8546 roman numeral three\n8547 roman numeral four\n8548 roman numeral five\n8549 roman numeral six\n8550 roman numeral seven\n8551 roman numeral eight\n8552 roman numeral nine\n8553 roman numeral ten\n8554 roman numeral eleven\n8555 roman numeral twelve\n8556 roman numeral fifty\n8557 roman numeral one hundred\n8558 roman numeral five hundred\n8559 roman numeral one thousand\n8560 small roman numeral one\n8561 small roman numeral two\n8562 small roman numeral three\n8563 small roman numeral four\n8564 small roman numeral five\n8565 small roman numeral six\n8566 small roman numeral seven\n8567 small roman numeral eight\n8568 small roman numeral nine\n8569 small roman numeral ten\n8570 small roman numeral eleven\n8571 small roman numeral twelve\n8572 small roman numeral fifty\n8573 small roman numeral one hundred\n8574 small roman numeral five hundred\n8575 small roman numeral one thousand\n8576 roman numeral one thousand c d\n8577 roman numeral five thousand\n8578 roman numeral ten thousand\n8579 roman numeral reversed one hundred\n8580 latin small letter reversed c\n8581 roman numeral six late form\n8582 roman numeral fifty early form\n8583 roman numeral fifty thousand\n8584 roman numeral one hundred thousand\n8585 vulgar fraction zero thirds\n8586 turned digit two\n8587 turned digit three\n8592 leftwards arrow\tlarr leftarrow shortleftarrow slarr\n8593 upwards arrow\tshortuparrow uarr uparrow\n8594 rightwards arrow\trarr rightarrow shortrightarrow srarr\n8595 downwards arrow\tdarr downarrow shortdownarrow\n8596 left right arrow\tharr leftrightarrow\n8597 up down arrow\tupdownarrow varr\n8598 north west arrow\tnwarr nwarrow upperleftarrow\n8599 north east arrow\tnearr nearrow upperrightarrow\n8600 south east arrow\tlowerrightarrow searr searrow\n8601 south west arrow\tlowerleftarrow swarr swarrow\n8602 leftwards arrow with stroke\tnlarr nleftarrow\n8603 rightwards arrow with stroke\tnrarr nrightarrow\n8604 leftwards wave arrow\n8605 rightwards wave arrow\trarrw rightsquigarrow\n8606 leftwards two headed arrow\tlarr twoheadleftarrow\n8607 upwards two headed arrow\tuarr\n8608 rightwards two headed arrow\trarr twoheadrightarrow\n8609 downwards two headed arrow\tdarr\n8610 leftwards arrow with tail\tlarrtl leftarrowtail\n8611 rightwards arrow with tail\trarrtl rightarrowtail\n8612 leftwards arrow from bar\tleftteearrow mapstoleft\n8613 upwards arrow from bar\tmapstoup upteearrow\n8614 rightwards arrow from bar\tmap mapsto rightteearrow\n8615 downwards arrow from bar\tdownteearrow mapstodown\n8616 up down arrow with base\n8617 leftwards arrow with hook\thookleftarrow larrhk\n8618 rightwards arrow with hook\thookrightarrow rarrhk\n8619 leftwards arrow with loop\tlarrlp looparrowleft\n8620 rightwards arrow with loop\tlooparrowright rarrlp\n8621 left right wave arrow\tharrw leftrightsquigarrow\n8622 left right arrow with stroke\tnharr nleftrightarrow\n8623 downwards zigzag arrow\n8624 upwards arrow with tip leftwards\tlsh\n8625 upwards arrow with tip rightwards\trsh\n8626 downwards arrow with tip leftwards\tldsh\n8627 downwards arrow with tip rightwards\trdsh\n8628 rightwards arrow with corner downwards\n8629 downwards arrow with corner leftwards\tcrarr\n8630 anticlockwise top semicircle arrow\tcularr curvearrowleft\n8631 clockwise top semicircle arrow\tcurarr curvearrowright\n8632 north west arrow to long bar\n8633 leftwards arrow to bar over rightwards arrow to bar\n8634 anticlockwise open circle arrow\tcirclearrowleft olarr\n8635 clockwise open circle arrow\tcirclearrowright orarr\n8636 leftwards harpoon with barb upwards\tleftharpoonup leftvector lharu\n8637 leftwards harpoon with barb downwards\tdownleftvector leftharpoondown lhard\n8638 upwards harpoon with barb rightwards\trightupvector uharr upharpoonright\n8639 upwards harpoon with barb leftwards\tleftupvector uharl upharpoonleft\n8640 rightwards harpoon with barb upwards\trharu rightharpoonup rightvector\n8641 rightwards harpoon with barb downwards\tdownrightvector rhard rightharpoondown\n8642 downwards harpoon with barb rightwards\tdharr downharpoonright rightdownvector\n8643 downwards harpoon with barb leftwards\tdharl downharpoonleft leftdownvector\n8644 rightwards arrow over leftwards arrow\trightarrowleftarrow rightleftarrows rlarr\n8645 upwards arrow leftwards of downwards arrow\tudarr uparrowdownarrow\n8646 leftwards arrow over rightwards arrow\tleftarrowrightarrow leftrightarrows lrarr\n8647 leftwards paired arrows\tleftleftarrows llarr\n8648 upwards paired arrows\tupuparrows uuarr\n8649 rightwards paired arrows\trightrightarrows rrarr\n8650 downwards paired arrows\tddarr downdownarrows\n8651 leftwards harpoon over rightwards harpoon\tleftrightharpoons lrhar reverseequilibrium\n8652 rightwards harpoon over leftwards harpoon\tequilibrium rightleftharpoons rlhar\n8653 leftwards double arrow with stroke\tnlarr nleftarrow\n8654 left right double arrow with stroke\tnharr nleftrightarrow\n8655 rightwards double arrow with stroke\tnrarr nrightarrow\n8656 leftwards double arrow\tdoubleleftarrow larr leftarrow\n8657 upwards double arrow\tdoubleuparrow uarr uparrow\n8658 rightwards double arrow\tdoublerightarrow implies rarr rightarrow\n8659 downwards double arrow\tdarr doubledownarrow downarrow\n8660 left right double arrow\tdoubleleftrightarrow harr iff leftrightarrow\n8661 up down double arrow\tdoubleupdownarrow updownarrow varr\n8662 north west double arrow\tnwarr\n8663 north east double arrow\tnearr\n8664 south east double arrow\tsearr\n8665 south west double arrow\tswarr\n8666 leftwards triple arrow\tlaarr lleftarrow\n8667 rightwards triple arrow\traarr rrightarrow\n8668 leftwards squiggle arrow\n8669 rightwards squiggle arrow\tzigrarr\n8670 upwards arrow with double stroke\n8671 downwards arrow with double stroke\n8672 leftwards dashed arrow\n8673 upwards dashed arrow\n8674 rightwards dashed arrow\n8675 downwards dashed arrow\n8676 leftwards arrow to bar\tlarrb leftarrowbar\n8677 rightwards arrow to bar\trarrb rightarrowbar\n8678 leftwards white arrow\n8679 upwards white arrow\n8680 rightwards white arrow\n8681 downwards white arrow\n8682 upwards white arrow from bar\n8683 upwards white arrow on pedestal\n8684 upwards white arrow on pedestal with horizontal bar\n8685 upwards white arrow on pedestal with vertical bar\n8686 upwards white double arrow\n8687 upwards white double arrow on pedestal\n8688 rightwards white arrow from wall\n8689 north west arrow to corner\n8690 south east arrow to corner\n8691 up down white arrow\n8692 right arrow with small circle\n8693 downwards arrow leftwards of upwards arrow\tdownarrowuparrow duarr\n8694 three rightwards arrows\n8695 leftwards arrow with vertical stroke\n8696 rightwards arrow with vertical stroke\n8697 left right arrow with vertical stroke\n8698 leftwards arrow with double vertical stroke\n8699 rightwards arrow with double vertical stroke\n8700 left right arrow with double vertical stroke\n8701 leftwards open-headed arrow\tloarr\n8702 rightwards open-headed arrow\troarr\n8703 left right open-headed arrow\thoarr\n8704 for all\tforall\n8705 complement\tcomp\n8706 partial differential\tpart partiald\n8707 there exists\texist\n8708 there does not exist\tnexist nexists notexists\n8709 empty set\temptyset emptyv varnothing\n8710 increment\n8711 nabla\tdel\n8712 element of\tin isin isinv\n8713 not an element of\tnotelement notin notinva\n8714 small element of\n8715 contains as member\tni niv reverseelement suchthat\n8716 does not contain as member\tnotni notniva notreverseelement\n8717 small contains as member\n8718 end of proof\n8719 n-ary product\tprod\n8720 n-ary coproduct\tcoprod\n8721 n-ary summation\tsum\n8722 minus sign\n8723 minus-or-plus sign\tminusplus mnplus mp\n8724 dot plus\tdotplus plusdo\n8725 division slash\n8726 set minus\tbackslash setminus setmn smallsetminus ssetmn\n8727 asterisk operator\tlowast\n8728 ring operator\tcompfn smallcircle\n8729 bullet operator\n8730 square root\tradic sqrt\n8731 cube root\n8732 fourth root\n8733 proportional to\tprop propto varpropto vprop\n8734 infinity\tinfin\n8735 right angle\tangrt\n8736 angle\tang\n8737 measured angle\tangmsd measuredangle\n8738 spherical angle\tangsph\n8739 divides\tmid shortmid smid verticalbar\n8740 does not divide\tnmid notverticalbar nshortmid nsmid\n8741 parallel to\tdoubleverticalbar par shortparallel spar\n8742 not parallel to\tnotdoubleverticalbar npar nparallel nshortparallel nspar\n8743 logical and\twedge\n8744 logical or\tvee\n8745 intersection\tcap\n8746 union\tcup\n8747 integral\tint\n8748 double integral\tint\n8749 triple integral\tiiint tint\n8750 contour integral\tconint contourintegral oint\n8751 surface integral\tconint doublecontourintegral\n8752 volume integral\tcconint\n8753 clockwise integral\tcwint\n8754 clockwise contour integral\tclockwisecontourintegral cwconint\n8755 anticlockwise contour integral\tawconint counterclockwisecontourintegral\n8756 therefore\tthere4\n8757 because\tbecaus\n8758 ratio\n8759 proportion\tcolon\n8760 dot minus\tdotminus minusd\n8761 excess\n8762 geometric proportion\tmddot\n8763 homothetic\thomtht\n8764 tilde operator\tsim thicksim thksim\n8765 reversed tilde\tbacksim bsim\n8766 inverted lazy s\tac mstpos\n8767 sine wave\tacd\n8768 wreath product\tverticaltilde wr\n8769 not tilde\tnottilde nsim\n8770 minus tilde\teqsim equaltilde esim\n8771 asymptotically equal to\tsime simeq tildeequal\n8772 not asymptotically equal to\tnottildeequal nsime nsimeq\n8773 approximately equal to\tcong tildefullequal\n8774 approximately but not actually equal to\tsimne\n8775 neither approximately nor actually equal to\tncong nottildefullequal\n8776 almost equal to\tap approx asymp thickapprox thkap tildetilde\n8777 not almost equal to\tnap napprox nottildetilde\n8778 almost equal or equal to\tape approxeq\n8779 triple tilde\tapid\n8780 all equal to\tbackcong bcong\n8781 equivalent to\tasympeq cupcap\n8782 geometrically equivalent to\tbump bumpeq humpdownhump\n8783 difference between\tbumpe bumpeq humpequal\n8784 approaches the limit\tdoteq dotequal esdot\n8785 geometrically equal to\tdoteqdot edot\n8786 approximately equal to or the image of\tefdot fallingdotseq\n8787 image of or approximately equal to\terdot risingdotseq\n8788 colon equals\tassign colone coloneq\n8789 equals colon\tecolon eqcolon\n8790 ring in equal to\tecir eqcirc\n8791 ring equal to\tcirceq cire\n8792 corresponds to\n8793 estimates\twedgeq\n8794 equiangular to\tveeeq\n8795 star equals\n8796 delta equal to\ttriangleq trie\n8797 equal to by definition\n8798 measured by\n8799 questioned equal to\tequest questeq\n8800 not equal to\tne notequal\n8801 identical to\tcongruent equiv\n8802 not identical to\tnequiv notcongruent\n8803 strictly equivalent to\n8804 less-than or equal to\tle leq\n8805 greater-than or equal to\tge geq greaterequal\n8806 less-than over equal to\tle leqq lessfullequal\n8807 greater-than over equal to\tge geqq greaterfullequal\n8808 less-than but not equal to\tlne lneqq\n8809 greater-than but not equal to\tgne gneqq\n8810 much less-than\tll lt nestedlessless\n8811 much greater-than\tgg gt nestedgreatergreater\n8812 between\ttwixt\n8813 not equivalent to\tnotcupcap\n8814 not less-than\tnless nlt notless\n8815 not greater-than\tngt ngtr notgreater\n8816 neither less-than nor equal to\tnle nleq notlessequal\n8817 neither greater-than nor equal to\tnge ngeq notgreaterequal\n8818 less-than or equivalent to\tlesssim lesstilde lsim\n8819 greater-than or equivalent to\tgreatertilde gsim gtrsim\n8820 neither less-than nor equivalent to\tnlsim notlesstilde\n8821 neither greater-than nor equivalent to\tngsim notgreatertilde\n8822 less-than or greater-than\tlessgreater lessgtr lg\n8823 greater-than or less-than\tgl greaterless gtrless\n8824 neither less-than nor greater-than\tnotlessgreater ntlg\n8825 neither greater-than nor less-than\tnotgreaterless ntgl\n8826 precedes\tpr prec\n8827 succeeds\tsc succ\n8828 precedes or equal to\tprcue preccurlyeq precedesslantequal\n8829 succeeds or equal to\tsccue succcurlyeq succeedsslantequal\n8830 precedes or equivalent to\tprecedestilde precsim prsim\n8831 succeeds or equivalent to\tscsim succeedstilde succsim\n8832 does not precede\tnotprecedes npr nprec\n8833 does not succeed\tnotsucceeds nsc nsucc\n8834 subset of\tsub\n8835 superset of\tsup supset\n8836 not a subset of\tnsub\n8837 not a superset of\tnsup\n8838 subset of or equal to\tsube subseteq subsetequal\n8839 superset of or equal to\tsupe supersetequal supseteq\n8840 neither a subset of nor equal to\tnotsubsetequal nsube nsubseteq\n8841 neither a superset of nor equal to\tnotsupersetequal nsupe nsupseteq\n8842 subset of with not equal to\tsubne subsetneq\n8843 superset of with not equal to\tsupne supsetneq\n8844 multiset\n8845 multiset multiplication\tcupdot\n8846 multiset union\tunionplus uplus\n8847 square image of\tsqsub sqsubset squaresubset\n8848 square original of\tsqsup sqsupset squaresuperset\n8849 square image of or equal to\tsqsube sqsubseteq squaresubsetequal\n8850 square original of or equal to\tsqsupe sqsupseteq squaresupersetequal\n8851 square cap\tsqcap squareintersection\n8852 square cup\tsqcup squareunion\n8853 circled plus\tcircleplus oplus\n8854 circled minus\tcircleminus ominus\n8855 circled times\tcircletimes otimes\n8856 circled division slash\tosol\n8857 circled dot operator\tcircledot odot\n8858 circled ring operator\tcircledcirc ocir\n8859 circled asterisk operator\tcircledast oast\n8860 circled equals\n8861 circled dash\tcircleddash odash\n8862 squared plus\tboxplus plusb\n8863 squared minus\tboxminus minusb\n8864 squared times\tboxtimes timesb\n8865 squared dot operator\tdotsquare sdotb\n8866 right tack\trighttee vdash\n8867 left tack\tdashv lefttee\n8868 down tack\tdowntee top\n8869 up tack\tbot bottom perp uptee\n8870 assertion\n8871 models\n8872 true\tdoublerighttee vdash\n8873 forces\tvdash\n8874 triple vertical bar right turnstile\tvvdash\n8875 double vertical bar double right turnstile\tvdash\n8876 does not prove\tnvdash\n8877 not true\tnvdash\n8878 does not force\tnvdash\n8879 negated double vertical bar double right turnstile\tnvdash\n8880 precedes under relation\tprurel\n8881 succeeds under relation\n8882 normal subgroup of\tlefttriangle vartriangleleft vltri\n8883 contains as normal subgroup\trighttriangle vartriangleright vrtri\n8884 normal subgroup of or equal to\tlefttriangleequal ltrie trianglelefteq\n8885 contains as normal subgroup or equal to\trighttriangleequal rtrie trianglerighteq\n8886 original of\torigof\n8887 image of\timof\n8888 multimap\tmumap\n8889 hermitian conjugate matrix\thercon\n8890 intercalate\tintcal intercal\n8891 xor\tveebar\n8892 nand\n8893 nor\tbarvee\n8894 right angle with arc\tangrtvb\n8895 right triangle\tlrtri\n8896 n-ary logical and\tbigwedge wedge xwedge\n8897 n-ary logical or\tbigvee vee xvee\n8898 n-ary intersection\tbigcap xcap\n8899 n-ary union\tbigcup xcup\n8900 diamond operator\tdiam\n8901 dot operator\tsdot\n8902 star operator\tsstarf\n8903 division times\tdivideontimes divonx\n8904 bowtie\n8905 left normal factor semidirect product\tltimes\n8906 right normal factor semidirect product\trtimes\n8907 left semidirect product\tleftthreetimes lthree\n8908 right semidirect product\trightthreetimes rthree\n8909 reversed tilde equals\tbacksimeq bsime\n8910 curly logical or\tcurlyvee cuvee\n8911 curly logical and\tcurlywedge cuwed\n8912 double subset\tsub\n8913 double superset\tsup supset\n8914 double intersection\tcap\n8915 double union\tcup\n8916 pitchfork\tfork\n8917 equal and parallel to\tepar\n8918 less-than with dot\tlessdot ltdot\n8919 greater-than with dot\tgtdot gtrdot\n8920 very much less-than\tll\n8921 very much greater-than\tgg ggg\n8922 less-than equal to or greater-than\tleg lesseqgtr lessequalgreater\n8923 greater-than equal to or less-than\tgel greaterequalless gtreqless\n8924 equal to or less-than\n8925 equal to or greater-than\n8926 equal to or precedes\tcuepr curlyeqprec\n8927 equal to or succeeds\tcuesc curlyeqsucc\n8928 does not precede or equal\tnotprecedesslantequal nprcue\n8929 does not succeed or equal\tnotsucceedsslantequal nsccue\n8930 not square image of or equal to\tnotsquaresubsetequal nsqsube\n8931 not square original of or equal to\tnotsquaresupersetequal nsqsupe\n8932 square image of or not equal to\n8933 square original of or not equal to\n8934 less-than but not equivalent to\tlnsim\n8935 greater-than but not equivalent to\tgnsim\n8936 precedes but not equivalent to\tprecnsim prnsim\n8937 succeeds but not equivalent to\tscnsim succnsim\n8938 not normal subgroup of\tnltri notlefttriangle ntriangleleft\n8939 does not contain as normal subgroup\tnotrighttriangle nrtri ntriangleright\n8940 not normal subgroup of or equal to\tnltrie notlefttriangleequal ntrianglelefteq\n8941 does not contain as normal subgroup or equal\tnotrighttriangleequal nrtrie ntrianglerighteq\n8942 vertical ellipsis\tvellip\n8943 midline horizontal ellipsis\tctdot\n8944 up right diagonal ellipsis\tutdot\n8945 down right diagonal ellipsis\tdtdot\n8946 element of with long horizontal stroke\tdisin\n8947 element of with vertical bar at end of horizontal stroke\tisinsv\n8948 small element of with vertical bar at end of horizontal stroke\tisins\n8949 element of with dot above\tisindot\n8950 element of with overbar\tnotinvc\n8951 small element of with overbar\tnotinvb\n8952 element of with underbar\n8953 element of with two horizontal strokes\tisine\n8954 contains with long horizontal stroke\tnisd\n8955 contains with vertical bar at end of horizontal stroke\txnis\n8956 small contains with vertical bar at end of horizontal stroke\tnis\n8957 contains with overbar\tnotnivc\n8958 small contains with overbar\tnotnivb\n8959 z notation bag membership\n8960 diameter sign\n8961 electric arrow\n8962 house\n8963 up arrowhead\n8964 down arrowhead\n8965 projective\tbarwed barwedge\n8966 perspective\tbarwed doublebarwedge\n8967 wavy line\n8968 left ceiling\tlceil leftceiling\n8969 right ceiling\trceil rightceiling\n8970 left floor\tleftfloor lfloor\n8971 right floor\trfloor rightfloor\n8972 bottom right crop\tdrcrop\n8973 bottom left crop\tdlcrop\n8974 top right crop\turcrop\n8975 top left crop\tulcrop\n8976 reversed not sign\tbnot\n8977 square lozenge\n8978 arc\tprofline\n8979 segment\tprofsurf\n8980 sector\n8981 telephone recorder\ttelrec\n8982 position indicator\ttarget\n8983 viewdata square\n8984 place of interest sign\n8985 turned not sign\n8986 watch\n8987 hourglass\n8988 top left corner\tulcorn ulcorner\n8989 top right corner\turcorn urcorner\n8990 bottom left corner\tdlcorn llcorner\n8991 bottom right corner\tdrcorn lrcorner\n8992 top half integral\n8993 bottom half integral\n8994 frown\tsfrown\n8995 smile\tssmile\n8996 up arrowhead between two horizontal bars\n8997 option key\n8998 erase to the right\n8999 x in a rectangle box\n9000 keyboard\n9001 left-pointing angle bracket\n9002 right-pointing angle bracket\n9003 erase to the left\n9004 benzene ring\n9005 cylindricity\tcylcty\n9006 all around-profile\tprofalar\n9007 symmetry\n9008 total runout\n9009 dimension origin\n9010 conical taper\n9011 slope\n9012 counterbore\n9013 countersink\n9014 apl functional symbol i-beam\ttopbot\n9015 apl functional symbol squish quad\n9016 apl functional symbol quad equal\n9017 apl functional symbol quad divide\n9018 apl functional symbol quad diamond\n9019 apl functional symbol quad jot\n9020 apl functional symbol quad circle\n9021 apl functional symbol circle stile\tovbar\n9022 apl functional symbol circle jot\n9023 apl functional symbol slash bar\tsolbar\n9024 apl functional symbol backslash bar\n9025 apl functional symbol quad slash\n9026 apl functional symbol quad backslash\n9027 apl functional symbol quad less-than\n9028 apl functional symbol quad greater-than\n9029 apl functional symbol leftwards vane\n9030 apl functional symbol rightwards vane\n9031 apl functional symbol quad leftwards arrow\n9032 apl functional symbol quad rightwards arrow\n9033 apl functional symbol circle backslash\n9034 apl functional symbol down tack underbar\n9035 apl functional symbol delta stile\n9036 apl functional symbol quad down caret\n9037 apl functional symbol quad delta\n9038 apl functional symbol down tack jot\n9039 apl functional symbol upwards vane\n9040 apl functional symbol quad upwards arrow\n9041 apl functional symbol up tack overbar\n9042 apl functional symbol del stile\n9043 apl functional symbol quad up caret\n9044 apl functional symbol quad del\n9045 apl functional symbol up tack jot\n9046 apl functional symbol downwards vane\n9047 apl functional symbol quad downwards arrow\n9048 apl functional symbol quote underbar\n9049 apl functional symbol delta underbar\n9050 apl functional symbol diamond underbar\n9051 apl functional symbol jot underbar\n9052 apl functional symbol circle underbar\n9053 apl functional symbol up shoe jot\n9054 apl functional symbol quote quad\n9055 apl functional symbol circle star\n9056 apl functional symbol quad colon\n9057 apl functional symbol up tack diaeresis\n9058 apl functional symbol del diaeresis\n9059 apl functional symbol star diaeresis\n9060 apl functional symbol jot diaeresis\n9061 apl functional symbol circle diaeresis\n9062 apl functional symbol down shoe stile\n9063 apl functional symbol left shoe stile\n9064 apl functional symbol tilde diaeresis\n9065 apl functional symbol greater-than diaeresis\n9066 apl functional symbol comma bar\n9067 apl functional symbol del tilde\n9068 apl functional symbol zilde\n9069 apl functional symbol stile tilde\n9070 apl functional symbol semicolon underbar\n9071 apl functional symbol quad not equal\n9072 apl functional symbol quad question\n9073 apl functional symbol down caret tilde\n9074 apl functional symbol up caret tilde\n9075 apl functional symbol iota\n9076 apl functional symbol rho\n9077 apl functional symbol omega\n9078 apl functional symbol alpha underbar\n9079 apl functional symbol epsilon underbar\n9080 apl functional symbol iota underbar\n9081 apl functional symbol omega underbar\n9082 apl functional symbol alpha\n9083 not check mark\n9084 right angle with downwards zigzag arrow\tangzarr\n9085 shouldered open box\n9086 bell symbol\n9087 vertical line with middle dot\n9088 insertion symbol\n9089 continuous underline symbol\n9090 discontinuous underline symbol\n9091 emphasis symbol\n9092 composition symbol\n9093 white square with centre vertical line\n9094 enter symbol\n9095 alternative key symbol\n9096 helm symbol\n9097 circled horizontal bar with notch\n9098 circled triangle down\n9099 broken circle with northwest arrow\n9100 undo symbol\n9101 monostable symbol\n9102 hysteresis symbol\n9103 open-circuit-output h-type symbol\n9104 open-circuit-output l-type symbol\n9105 passive-pull-down-output symbol\n9106 passive-pull-up-output symbol\n9107 direct current symbol form two\n9108 software-function symbol\n9109 apl functional symbol quad\n9110 decimal separator key symbol\n9111 previous page\n9112 next page\n9113 print screen symbol\n9114 clear screen symbol\n9115 left parenthesis upper hook\n9116 left parenthesis extension\n9117 left parenthesis lower hook\n9118 right parenthesis upper hook\n9119 right parenthesis extension\n9120 right parenthesis lower hook\n9121 left square bracket upper corner\n9122 left square bracket extension\n9123 left square bracket lower corner\n9124 right square bracket upper corner\n9125 right square bracket extension\n9126 right square bracket lower corner\n9127 left curly bracket upper hook\n9128 left curly bracket middle piece\n9129 left curly bracket lower hook\n9130 curly bracket extension\n9131 right curly bracket upper hook\n9132 right curly bracket middle piece\n9133 right curly bracket lower hook\n9134 integral extension\n9135 horizontal line extension\n9136 upper left or lower right curly bracket section\tlmoust lmoustache\n9137 upper right or lower left curly bracket section\trmoust rmoustache\n9138 summation top\n9139 summation bottom\n9140 top square bracket\toverbracket tbrk\n9141 bottom square bracket\tbbrk underbracket\n9142 bottom square bracket over top square bracket\tbbrktbrk\n9143 radical symbol bottom\n9144 left vertical box line\n9145 right vertical box line\n9146 horizontal scan line-1\n9147 horizontal scan line-3\n9148 horizontal scan line-7\n9149 horizontal scan line-9\n9150 dentistry symbol light vertical and top right\n9151 dentistry symbol light vertical and bottom right\n9152 dentistry symbol light vertical with circle\n9153 dentistry symbol light down and horizontal with circle\n9154 dentistry symbol light up and horizontal with circle\n9155 dentistry symbol light vertical with triangle\n9156 dentistry symbol light down and horizontal with triangle\n9157 dentistry symbol light up and horizontal with triangle\n9158 dentistry symbol light vertical and wave\n9159 dentistry symbol light down and horizontal with wave\n9160 dentistry symbol light up and horizontal with wave\n9161 dentistry symbol light down and horizontal\n9162 dentistry symbol light up and horizontal\n9163 dentistry symbol light vertical and top left\n9164 dentistry symbol light vertical and bottom left\n9165 square foot\n9166 return symbol\n9167 eject symbol\n9168 vertical line extension\n9169 metrical breve\n9170 metrical long over short\n9171 metrical short over long\n9172 metrical long over two shorts\n9173 metrical two shorts over long\n9174 metrical two shorts joined\n9175 metrical triseme\n9176 metrical tetraseme\n9177 metrical pentaseme\n9178 earth ground\n9179 fuse\n9180 top parenthesis\toverparenthesis\n9181 bottom parenthesis\tunderparenthesis\n9182 top curly bracket\toverbrace\n9183 bottom curly bracket\tunderbrace\n9184 top tortoise shell bracket\n9185 bottom tortoise shell bracket\n9186 white trapezium\ttrpezium\n9187 benzene ring with circle\n9188 straightness\n9189 flatness\n9190 ac current\n9191 electrical intersection\telinters\n9192 decimal exponent symbol\n9193 black right-pointing double triangle\n9194 black left-pointing double triangle\n9195 black up-pointing double triangle\n9196 black down-pointing double triangle\n9197 black right-pointing double triangle with vertical bar\n9198 black left-pointing double triangle with vertical bar\n9199 black right-pointing triangle with double vertical bar\n9200 alarm clock\n9201 stopwatch\n9202 timer clock\n9203 hourglass with flowing sand\n9204 black medium left-pointing triangle\n9205 black medium right-pointing triangle\n9206 black medium up-pointing triangle\n9207 black medium down-pointing triangle\n9208 double vertical bar\n9209 black square for stop\n9210 black circle for record\n9211 power symbol\n9212 power on-off symbol\n9213 power on symbol\n9214 power sleep symbol\n9215 observer eye symbol\n9216 symbol for null\n9217 symbol for start of heading\n9218 symbol for start of text\n9219 symbol for end of text\n9220 symbol for end of transmission\n9221 symbol for enquiry\n9222 symbol for acknowledge\n9223 symbol for bell\n9224 symbol for backspace\n9225 symbol for horizontal tabulation\n9226 symbol for line feed\n9227 symbol for vertical tabulation\n9228 symbol for form feed\n9229 symbol for carriage return\n9230 symbol for shift out\n9231 symbol for shift in\n9232 symbol for data link escape\n9233 symbol for device control one\n9234 symbol for device control two\n9235 symbol for device control three\n9236 symbol for device control four\n9237 symbol for negative acknowledge\n9238 symbol for synchronous idle\n9239 symbol for end of transmission block\n9240 symbol for cancel\n9241 symbol for end of medium\n9242 symbol for substitute\n9243 symbol for escape\n9244 symbol for file separator\n9245 symbol for group separator\n9246 symbol for record separator\n9247 symbol for unit separator\n9248 symbol for space\n9249 symbol for delete\n9250 blank symbol\n9251 open box\tblank\n9252 symbol for newline\n9253 symbol for delete form two\n9254 symbol for substitute form two\n9255 symbol for delete square checker board form\n9256 symbol for delete rectangular checker board form\n9257 symbol for delete medium shade form\n9280 ocr hook\n9281 ocr chair\n9282 ocr fork\n9283 ocr inverted fork\n9284 ocr belt buckle\n9285 ocr bow tie\n9286 ocr branch bank identification\n9287 ocr amount of check\n9288 ocr dash\n9289 ocr customer account number\n9290 ocr double backslash\n9312 circled digit one\n9313 circled digit two\n9314 circled digit three\n9315 circled digit four\n9316 circled digit five\n9317 circled digit six\n9318 circled digit seven\n9319 circled digit eight\n9320 circled digit nine\n9321 circled number ten\n9322 circled number eleven\n9323 circled number twelve\n9324 circled number thirteen\n9325 circled number fourteen\n9326 circled number fifteen\n9327 circled number sixteen\n9328 circled number seventeen\n9329 circled number eighteen\n9330 circled number nineteen\n9331 circled number twenty\n9332 parenthesized digit one\n9333 parenthesized digit two\n9334 parenthesized digit three\n9335 parenthesized digit four\n9336 parenthesized digit five\n9337 parenthesized digit six\n9338 parenthesized digit seven\n9339 parenthesized digit eight\n9340 parenthesized digit nine\n9341 parenthesized number ten\n9342 parenthesized number eleven\n9343 parenthesized number twelve\n9344 parenthesized number thirteen\n9345 parenthesized number fourteen\n9346 parenthesized number fifteen\n9347 parenthesized number sixteen\n9348 parenthesized number seventeen\n9349 parenthesized number eighteen\n9350 parenthesized number nineteen\n9351 parenthesized number twenty\n9352 digit one full stop\n9353 digit two full stop\n9354 digit three full stop\n9355 digit four full stop\n9356 digit five full stop\n9357 digit six full stop\n9358 digit seven full stop\n9359 digit eight full stop\n9360 digit nine full stop\n9361 number ten full stop\n9362 number eleven full stop\n9363 number twelve full stop\n9364 number thirteen full stop\n9365 number fourteen full stop\n9366 number fifteen full stop\n9367 number sixteen full stop\n9368 number seventeen full stop\n9369 number eighteen full stop\n9370 number nineteen full stop\n9371 number twenty full stop\n9372 parenthesized latin small letter a\n9373 parenthesized latin small letter b\n9374 parenthesized latin small letter c\n9375 parenthesized latin small letter d\n9376 parenthesized latin small letter e\n9377 parenthesized latin small letter f\n9378 parenthesized latin small letter g\n9379 parenthesized latin small letter h\n9380 parenthesized latin small letter i\n9381 parenthesized latin small letter j\n9382 parenthesized latin small letter k\n9383 parenthesized latin small letter l\n9384 parenthesized latin small letter m\n9385 parenthesized latin small letter n\n9386 parenthesized latin small letter o\n9387 parenthesized latin small letter p\n9388 parenthesized latin small letter q\n9389 parenthesized latin small letter r\n9390 parenthesized latin small letter s\n9391 parenthesized latin small letter t\n9392 parenthesized latin small letter u\n9393 parenthesized latin small letter v\n9394 parenthesized latin small letter w\n9395 parenthesized latin small letter x\n9396 parenthesized latin small letter y\n9397 parenthesized latin small letter z\n9398 circled latin capital letter a\n9399 circled latin capital letter b\n9400 circled latin capital letter c\n9401 circled latin capital letter d\n9402 circled latin capital letter e\n9403 circled latin capital letter f\n9404 circled latin capital letter g\n9405 circled latin capital letter h\n9406 circled latin capital letter i\n9407 circled latin capital letter j\n9408 circled latin capital letter k\n9409 circled latin capital letter l\n9410 circled latin capital letter m\n9411 circled latin capital letter n\n9412 circled latin capital letter o\n9413 circled latin capital letter p\n9414 circled latin capital letter q\n9415 circled latin capital letter r\n9416 circled latin capital letter s\tcircleds os\n9417 circled latin capital letter t\n9418 circled latin capital letter u\n9419 circled latin capital letter v\n9420 circled latin capital letter w\n9421 circled latin capital letter x\n9422 circled latin capital letter y\n9423 circled latin capital letter z\n9424 circled latin small letter a\n9425 circled latin small letter b\n9426 circled latin small letter c\n9427 circled latin small letter d\n9428 circled latin small letter e\n9429 circled latin small letter f\n9430 circled latin small letter g\n9431 circled latin small letter h\n9432 circled latin small letter i\n9433 circled latin small letter j\n9434 circled latin small letter k\n9435 circled latin small letter l\n9436 circled latin small letter m\n9437 circled latin small letter n\n9438 circled latin small letter o\n9439 circled latin small letter p\n9440 circled latin small letter q\n9441 circled latin small letter r\n9442 circled latin small letter s\n9443 circled latin small letter t\n9444 circled latin small letter u\n9445 circled latin small letter v\n9446 circled latin small letter w\n9447 circled latin small letter x\n9448 circled latin small letter y\n9449 circled latin small letter z\n9450 circled digit zero\n9451 negative circled number eleven\n9452 negative circled number twelve\n9453 negative circled number thirteen\n9454 negative circled number fourteen\n9455 negative circled number fifteen\n9456 negative circled number sixteen\n9457 negative circled number seventeen\n9458 negative circled number eighteen\n9459 negative circled number nineteen\n9460 negative circled number twenty\n9461 double circled digit one\n9462 double circled digit two\n9463 double circled digit three\n9464 double circled digit four\n9465 double circled digit five\n9466 double circled digit six\n9467 double circled digit seven\n9468 double circled digit eight\n9469 double circled digit nine\n9470 double circled number ten\n9471 negative circled digit zero\n9472 box drawings light horizontal\tboxh horizontalline\n9473 box drawings heavy horizontal\n9474 box drawings light vertical\tboxv\n9475 box drawings heavy vertical\n9476 box drawings light triple dash horizontal\n9477 box drawings heavy triple dash horizontal\n9478 box drawings light triple dash vertical\n9479 box drawings heavy triple dash vertical\n9480 box drawings light quadruple dash horizontal\n9481 box drawings heavy quadruple dash horizontal\n9482 box drawings light quadruple dash vertical\n9483 box drawings heavy quadruple dash vertical\n9484 box drawings light down and right\tboxdr\n9485 box drawings down light and right heavy\n9486 box drawings down heavy and right light\n9487 box drawings heavy down and right\n9488 box drawings light down and left\tboxdl\n9489 box drawings down light and left heavy\n9490 box drawings down heavy and left light\n9491 box drawings heavy down and left\n9492 box drawings light up and right\tboxur\n9493 box drawings up light and right heavy\n9494 box drawings up heavy and right light\n9495 box drawings heavy up and right\n9496 box drawings light up and left\tboxul\n9497 box drawings up light and left heavy\n9498 box drawings up heavy and left light\n9499 box drawings heavy up and left\n9500 box drawings light vertical and right\tboxvr\n9501 box drawings vertical light and right heavy\n9502 box drawings up heavy and right down light\n9503 box drawings down heavy and right up light\n9504 box drawings vertical heavy and right light\n9505 box drawings down light and right up heavy\n9506 box drawings up light and right down heavy\n9507 box drawings heavy vertical and right\n9508 box drawings light vertical and left\tboxvl\n9509 box drawings vertical light and left heavy\n9510 box drawings up heavy and left down light\n9511 box drawings down heavy and left up light\n9512 box drawings vertical heavy and left light\n9513 box drawings down light and left up heavy\n9514 box drawings up light and left down heavy\n9515 box drawings heavy vertical and left\n9516 box drawings light down and horizontal\tboxhd\n9517 box drawings left heavy and right down light\n9518 box drawings right heavy and left down light\n9519 box drawings down light and horizontal heavy\n9520 box drawings down heavy and horizontal light\n9521 box drawings right light and left down heavy\n9522 box drawings left light and right down heavy\n9523 box drawings heavy down and horizontal\n9524 box drawings light up and horizontal\tboxhu\n9525 box drawings left heavy and right up light\n9526 box drawings right heavy and left up light\n9527 box drawings up light and horizontal heavy\n9528 box drawings up heavy and horizontal light\n9529 box drawings right light and left up heavy\n9530 box drawings left light and right up heavy\n9531 box drawings heavy up and horizontal\n9532 box drawings light vertical and horizontal\tboxvh\n9533 box drawings left heavy and right vertical light\n9534 box drawings right heavy and left vertical light\n9535 box drawings vertical light and horizontal heavy\n9536 box drawings up heavy and down horizontal light\n9537 box drawings down heavy and up horizontal light\n9538 box drawings vertical heavy and horizontal light\n9539 box drawings left up heavy and right down light\n9540 box drawings right up heavy and left down light\n9541 box drawings left down heavy and right up light\n9542 box drawings right down heavy and left up light\n9543 box drawings down light and up horizontal heavy\n9544 box drawings up light and down horizontal heavy\n9545 box drawings right light and left vertical heavy\n9546 box drawings left light and right vertical heavy\n9547 box drawings heavy vertical and horizontal\n9548 box drawings light double dash horizontal\n9549 box drawings heavy double dash horizontal\n9550 box drawings light double dash vertical\n9551 box drawings heavy double dash vertical\n9552 box drawings double horizontal\tboxh\n9553 box drawings double vertical\tboxv\n9554 box drawings down single and right double\tboxdr\n9555 box drawings down double and right single\tboxdr\n9556 box drawings double down and right\tboxdr\n9557 box drawings down single and left double\tboxdl\n9558 box drawings down double and left single\tboxdl\n9559 box drawings double down and left\tboxdl\n9560 box drawings up single and right double\tboxur\n9561 box drawings up double and right single\tboxur\n9562 box drawings double up and right\tboxur\n9563 box drawings up single and left double\tboxul\n9564 box drawings up double and left single\tboxul\n9565 box drawings double up and left\tboxul\n9566 box drawings vertical single and right double\tboxvr\n9567 box drawings vertical double and right single\tboxvr\n9568 box drawings double vertical and right\tboxvr\n9569 box drawings vertical single and left double\tboxvl\n9570 box drawings vertical double and left single\tboxvl\n9571 box drawings double vertical and left\tboxvl\n9572 box drawings down single and horizontal double\tboxhd\n9573 box drawings down double and horizontal single\tboxhd\n9574 box drawings double down and horizontal\tboxhd\n9575 box drawings up single and horizontal double\tboxhu\n9576 box drawings up double and horizontal single\tboxhu\n9577 box drawings double up and horizontal\tboxhu\n9578 box drawings vertical single and horizontal double\tboxvh\n9579 box drawings vertical double and horizontal single\tboxvh\n9580 box drawings double vertical and horizontal\tboxvh\n9581 box drawings light arc down and right\n9582 box drawings light arc down and left\n9583 box drawings light arc up and left\n9584 box drawings light arc up and right\n9585 box drawings light diagonal upper right to lower left\n9586 box drawings light diagonal upper left to lower right\n9587 box drawings light diagonal cross\n9588 box drawings light left\n9589 box drawings light up\n9590 box drawings light right\n9591 box drawings light down\n9592 box drawings heavy left\n9593 box drawings heavy up\n9594 box drawings heavy right\n9595 box drawings heavy down\n9596 box drawings light left and heavy right\n9597 box drawings light up and heavy down\n9598 box drawings heavy left and light right\n9599 box drawings heavy up and light down\n9600 upper half block\tuhblk\n9601 lower one eighth block\n9602 lower one quarter block\n9603 lower three eighths block\n9604 lower half block\tlhblk\n9605 lower five eighths block\n9606 lower three quarters block\n9607 lower seven eighths block\n9608 full block\n9609 left seven eighths block\n9610 left three quarters block\n9611 left five eighths block\n9612 left half block\n9613 left three eighths block\n9614 left one quarter block\n9615 left one eighth block\n9616 right half block\n9617 light shade\tblk14\n9618 medium shade\tblk12\n9619 dark shade\tblk34\n9620 upper one eighth block\n9621 right one eighth block\n9622 quadrant lower left\n9623 quadrant lower right\n9624 quadrant upper left\n9625 quadrant upper left and lower left and lower right\n9626 quadrant upper left and lower right\n9627 quadrant upper left and upper right and lower left\n9628 quadrant upper left and upper right and lower right\n9629 quadrant upper right\n9630 quadrant upper right and lower left\n9631 quadrant upper right and lower left and lower right\n9632 black square\n9633 white square\tsqu\n9634 white square with rounded corners\n9635 white square containing black small square\n9636 square with horizontal fill\n9637 square with vertical fill\n9638 square with orthogonal crosshatch fill\n9639 square with upper left to lower right fill\n9640 square with upper right to lower left fill\n9641 square with diagonal crosshatch fill\n9642 black small square\tblacksquare filledverysmallsquare squarf squf\n9643 white small square\temptyverysmallsquare\n9644 black rectangle\n9645 white rectangle\trect\n9646 black vertical rectangle\tmarker\n9647 white vertical rectangle\n9648 black parallelogram\n9649 white parallelogram\tfltns\n9650 black up-pointing triangle\n9651 white up-pointing triangle\tbigtriangleup xutri\n9652 black up-pointing small triangle\tblacktriangle utrif\n9653 white up-pointing small triangle\tutri\n9654 black right-pointing triangle\n9655 white right-pointing triangle\n9656 black right-pointing small triangle\tblacktriangleright rtrif\n9657 white right-pointing small triangle\trtri triangleright\n9658 black right-pointing pointer\n9659 white right-pointing pointer\n9660 black down-pointing triangle\n9661 white down-pointing triangle\tbigtriangledown xdtri\n9662 black down-pointing small triangle\tblacktriangledown dtrif\n9663 white down-pointing small triangle\tdtri triangledown\n9664 black left-pointing triangle\n9665 white left-pointing triangle\n9666 black left-pointing small triangle\tblacktriangleleft ltrif\n9667 white left-pointing small triangle\tltri triangleleft\n9668 black left-pointing pointer\n9669 white left-pointing pointer\n9670 black diamond\n9671 white diamond\n9672 white diamond containing black small diamond\n9673 fisheye\n9674 lozenge\tloz\n9675 white circle\tcir\n9676 dotted circle\n9677 circle with vertical fill\n9678 bullseye\n9679 black circle\n9680 circle with left half black\n9681 circle with right half black\n9682 circle with lower half black\n9683 circle with upper half black\n9684 circle with upper right quadrant black\n9685 circle with all but upper left quadrant black\n9686 left half black circle\n9687 right half black circle\n9688 inverse bullet\n9689 inverse white circle\n9690 upper half inverse white circle\n9691 lower half inverse white circle\n9692 upper left quadrant circular arc\n9693 upper right quadrant circular arc\n9694 lower right quadrant circular arc\n9695 lower left quadrant circular arc\n9696 upper half circle\n9697 lower half circle\n9698 black lower right triangle\n9699 black lower left triangle\n9700 black upper left triangle\n9701 black upper right triangle\n9702 white bullet\n9703 square with left half black\n9704 square with right half black\n9705 square with upper left diagonal half black\n9706 square with lower right diagonal half black\n9707 white square with vertical bisecting line\n9708 white up-pointing triangle with dot\ttridot\n9709 up-pointing triangle with left half black\n9710 up-pointing triangle with right half black\n9711 large circle\tbigcirc xcirc\n9712 white square with upper left quadrant\n9713 white square with lower left quadrant\n9714 white square with lower right quadrant\n9715 white square with upper right quadrant\n9716 white circle with upper left quadrant\n9717 white circle with lower left quadrant\n9718 white circle with lower right quadrant\n9719 white circle with upper right quadrant\n9720 upper left triangle\tultri\n9721 upper right triangle\turtri\n9722 lower left triangle\tlltri\n9723 white medium square\temptysmallsquare\n9724 black medium square\tfilledsmallsquare\n9725 white medium small square\n9726 black medium small square\n9727 lower right triangle\n9728 black sun with rays\n9729 cloud\n9730 umbrella\n9731 snowman\n9732 comet\n9733 black star\tbigstar starf\n9734 white star\n9735 lightning\n9736 thunderstorm\n9737 sun\n9738 ascending node\n9739 descending node\n9740 conjunction\n9741 opposition\n9742 black telephone\tphone\n9743 white telephone\n9744 ballot box\n9745 ballot box with check\n9746 ballot box with x\n9747 saltire\n9748 umbrella with rain drops\n9749 hot beverage\n9750 white shogi piece\n9751 black shogi piece\n9752 shamrock\n9753 reversed rotated floral heart bullet\n9754 black left pointing index\n9755 black right pointing index\n9756 white left pointing index\n9757 white up pointing index\n9758 white right pointing index\n9759 white down pointing index\n9760 skull and crossbones\n9761 caution sign\n9762 radioactive sign\n9763 biohazard sign\n9764 caduceus\n9765 ankh\n9766 orthodox cross\n9767 chi rho\n9768 cross of lorraine\n9769 cross of jerusalem\n9770 star and crescent\n9771 farsi symbol\n9772 adi shakti\n9773 hammer and sickle\n9774 peace symbol\n9775 yin yang\n9776 trigram for heaven\n9777 trigram for lake\n9778 trigram for fire\n9779 trigram for thunder\n9780 trigram for wind\n9781 trigram for water\n9782 trigram for mountain\n9783 trigram for earth\n9784 wheel of dharma\n9785 white frowning face\n9786 white smiling face\n9787 black smiling face\n9788 white sun with rays\n9789 first quarter moon\n9790 last quarter moon\n9791 mercury\n9792 female sign\n9793 earth\n9794 male sign\n9795 jupiter\n9796 saturn\n9797 uranus\n9798 neptune\n9799 pluto\n9800 aries\n9801 taurus\n9802 gemini\n9803 cancer\n9804 leo\n9805 virgo\n9806 libra\n9807 scorpius\n9808 sagittarius\n9809 capricorn\n9810 aquarius\n9811 pisces\n9812 white chess king\n9813 white chess queen\n9814 white chess rook\n9815 white chess bishop\n9816 white chess knight\n9817 white chess pawn\n9818 black chess king\n9819 black chess queen\n9820 black chess rook\n9821 black chess bishop\n9822 black chess knight\n9823 black chess pawn\n9824 black spade suit\tspades spadesuit\n9825 white heart suit\n9826 white diamond suit\n9827 black club suit\tclubs clubsuit\n9828 white spade suit\n9829 black heart suit\thearts heartsuit\n9830 black diamond suit\tdiamondsuit diams\n9831 white club suit\n9832 hot springs\n9833 quarter note\n9834 eighth note\tsung\n9835 beamed eighth notes\n9836 beamed sixteenth notes\n9837 music flat sign\n9838 music natural sign\tnatur\n9839 music sharp sign\n9840 west syriac cross\n9841 east syriac cross\n9842 universal recycling symbol\n9843 recycling symbol for type-1 plastics\n9844 recycling symbol for type-2 plastics\n9845 recycling symbol for type-3 plastics\n9846 recycling symbol for type-4 plastics\n9847 recycling symbol for type-5 plastics\n9848 recycling symbol for type-6 plastics\n9849 recycling symbol for type-7 plastics\n9850 recycling symbol for generic materials\n9851 black universal recycling symbol\n9852 recycled paper symbol\n9853 partially-recycled paper symbol\n9854 permanent paper sign\n9855 wheelchair symbol\n9856 die face-1\n9857 die face-2\n9858 die face-3\n9859 die face-4\n9860 die face-5\n9861 die face-6\n9862 white circle with dot right\n9863 white circle with two dots\n9864 black circle with white dot right\n9865 black circle with two white dots\n9866 monogram for yang\n9867 monogram for yin\n9868 digram for greater yang\n9869 digram for lesser yin\n9870 digram for lesser yang\n9871 digram for greater yin\n9872 white flag\n9873 black flag\n9874 hammer and pick\n9875 anchor\n9876 crossed swords\n9877 staff of aesculapius\n9878 scales\n9879 alembic\n9880 flower\n9881 gear\n9882 staff of hermes\n9883 atom symbol\n9884 fleur-de-lis\n9885 outlined white star\n9886 three lines converging right\n9887 three lines converging left\n9888 warning sign\n9889 high voltage sign\n9890 doubled female sign\n9891 doubled male sign\n9892 interlocked female and male sign\n9893 male and female sign\n9894 male with stroke sign\n9895 male with stroke and male and female sign\n9896 vertical male with stroke sign\n9897 horizontal male with stroke sign\n9898 medium white circle\n9899 medium black circle\n9900 medium small white circle\n9901 marriage symbol\n9902 divorce symbol\n9903 unmarried partnership symbol\n9904 coffin\n9905 funeral urn\n9906 neuter\n9907 ceres\n9908 pallas\n9909 juno\n9910 vesta\n9911 chiron\n9912 black moon lilith\n9913 sextile\n9914 semisextile\n9915 quincunx\n9916 sesquiquadrate\n9917 soccer ball\n9918 baseball\n9919 squared key\n9920 white draughts man\n9921 white draughts king\n9922 black draughts man\n9923 black draughts king\n9924 snowman without snow\n9925 sun behind cloud\n9926 rain\n9927 black snowman\n9928 thunder cloud and rain\n9929 turned white shogi piece\n9930 turned black shogi piece\n9931 white diamond in square\n9932 crossing lanes\n9933 disabled car\n9934 ophiuchus\n9935 pick\n9936 car sliding\n9937 helmet with white cross\n9938 circled crossing lanes\n9939 chains\n9940 no entry\n9941 alternate one-way left way traffic\n9942 black two-way left way traffic\n9943 white two-way left way traffic\n9944 black left lane merge\n9945 white left lane merge\n9946 drive slow sign\n9947 heavy white down-pointing triangle\n9948 left closed entry\n9949 squared saltire\n9950 falling diagonal in white circle in black square\n9951 black truck\n9952 restricted left entry-1\n9953 restricted left entry-2\n9954 astronomical symbol for uranus\n9955 heavy circle with stroke and two dots above\n9956 pentagram\n9957 right-handed interlaced pentagram\n9958 left-handed interlaced pentagram\n9959 inverted pentagram\n9960 black cross on shield\n9961 shinto shrine\n9962 church\n9963 castle\n9964 historic site\n9965 gear without hub\n9966 gear with handles\n9967 map symbol for lighthouse\n9968 mountain\n9969 umbrella on ground\n9970 fountain\n9971 flag in hole\n9972 ferry\n9973 sailboat\n9974 square four corners\n9975 skier\n9976 ice skate\n9977 person with ball\n9978 tent\n9979 japanese bank symbol\n9980 headstone graveyard symbol\n9981 fuel pump\n9982 cup on black square\n9983 white flag with horizontal middle black stripe\n9984 black safety scissors\n9985 upper blade scissors\n9986 black scissors\n9987 lower blade scissors\n9988 white scissors\n9989 white heavy check mark\n9990 telephone location sign\n9991 tape drive\n9992 airplane\n9993 envelope\n9994 raised fist\n9995 raised hand\n9996 victory hand\n9997 writing hand\n9998 lower right pencil\n9999 pencil\n10000 upper right pencil\n10001 white nib\n10002 black nib\n10003 check mark\tcheckmark\n10004 heavy check mark\n10005 multiplication x\n10006 heavy multiplication x\n10007 ballot x\tcross\n10008 heavy ballot x\n10009 outlined greek cross\n10010 heavy greek cross\n10011 open centre cross\n10012 heavy open centre cross\n10013 latin cross\n10014 shadowed white latin cross\n10015 outlined latin cross\n10016 maltese cross\tmalt\n10017 star of david\n10018 four teardrop-spoked asterisk\n10019 four balloon-spoked asterisk\n10020 heavy four balloon-spoked asterisk\n10021 four club-spoked asterisk\n10022 black four pointed star\n10023 white four pointed star\n10024 sparkles\n10025 stress outlined white star\n10026 circled white star\n10027 open centre black star\n10028 black centre white star\n10029 outlined black star\n10030 heavy outlined black star\n10031 pinwheel star\n10032 shadowed white star\n10033 heavy asterisk\n10034 open centre asterisk\n10035 eight spoked asterisk\n10036 eight pointed black star\n10037 eight pointed pinwheel star\n10038 six pointed black star\tsext\n10039 eight pointed rectilinear black star\n10040 heavy eight pointed rectilinear black star\n10041 twelve pointed black star\n10042 sixteen pointed asterisk\n10043 teardrop-spoked asterisk\n10044 open centre teardrop-spoked asterisk\n10045 heavy teardrop-spoked asterisk\n10046 six petalled black and white florette\n10047 black florette\n10048 white florette\n10049 eight petalled outlined black florette\n10050 circled open centre eight pointed star\n10051 heavy teardrop-spoked pinwheel asterisk\n10052 snowflake\n10053 tight trifoliate snowflake\n10054 heavy chevron snowflake\n10055 sparkle\n10056 heavy sparkle\n10057 balloon-spoked asterisk\n10058 eight teardrop-spoked propeller asterisk\n10059 heavy eight teardrop-spoked propeller asterisk\n10060 cross mark\n10061 shadowed white circle\n10062 negative squared cross mark\n10063 lower right drop-shadowed white square\n10064 upper right drop-shadowed white square\n10065 lower right shadowed white square\n10066 upper right shadowed white square\n10067 black question mark ornament\n10068 white question mark ornament\n10069 white exclamation mark ornament\n10070 black diamond minus white x\n10071 heavy exclamation mark symbol\n10072 light vertical bar\tverticalseparator\n10073 medium vertical bar\n10074 heavy vertical bar\n10075 heavy single turned comma quotation mark ornament\n10076 heavy single comma quotation mark ornament\n10077 heavy double turned comma quotation mark ornament\n10078 heavy double comma quotation mark ornament\n10079 heavy low single comma quotation mark ornament\n10080 heavy low double comma quotation mark ornament\n10081 curved stem paragraph sign ornament\n10082 heavy exclamation mark ornament\n10083 heavy heart exclamation mark ornament\n10084 heavy black heart\n10085 rotated heavy black heart bullet\n10086 floral heart\n10087 rotated floral heart bullet\n10088 medium left parenthesis ornament\n10089 medium right parenthesis ornament\n10090 medium flattened left parenthesis ornament\n10091 medium flattened right parenthesis ornament\n10092 medium left-pointing angle bracket ornament\n10093 medium right-pointing angle bracket ornament\n10094 heavy left-pointing angle quotation mark ornament\n10095 heavy right-pointing angle quotation mark ornament\n10096 heavy left-pointing angle bracket ornament\n10097 heavy right-pointing angle bracket ornament\n10098 light left tortoise shell bracket ornament\tlbbrk\n10099 light right tortoise shell bracket ornament\trbbrk\n10100 medium left curly bracket ornament\n10101 medium right curly bracket ornament\n10102 dingbat negative circled digit one\n10103 dingbat negative circled digit two\n10104 dingbat negative circled digit three\n10105 dingbat negative circled digit four\n10106 dingbat negative circled digit five\n10107 dingbat negative circled digit six\n10108 dingbat negative circled digit seven\n10109 dingbat negative circled digit eight\n10110 dingbat negative circled digit nine\n10111 dingbat negative circled number ten\n10112 dingbat circled sans-serif digit one\n10113 dingbat circled sans-serif digit two\n10114 dingbat circled sans-serif digit three\n10115 dingbat circled sans-serif digit four\n10116 dingbat circled sans-serif digit five\n10117 dingbat circled sans-serif digit six\n10118 dingbat circled sans-serif digit seven\n10119 dingbat circled sans-serif digit eight\n10120 dingbat circled sans-serif digit nine\n10121 dingbat circled sans-serif number ten\n10122 dingbat negative circled sans-serif digit one\n10123 dingbat negative circled sans-serif digit two\n10124 dingbat negative circled sans-serif digit three\n10125 dingbat negative circled sans-serif digit four\n10126 dingbat negative circled sans-serif digit five\n10127 dingbat negative circled sans-serif digit six\n10128 dingbat negative circled sans-serif digit seven\n10129 dingbat negative circled sans-serif digit eight\n10130 dingbat negative circled sans-serif digit nine\n10131 dingbat negative circled sans-serif number ten\n10132 heavy wide-headed rightwards arrow\n10133 heavy plus sign\n10134 heavy minus sign\n10135 heavy division sign\n10136 heavy south east arrow\n10137 heavy rightwards arrow\n10138 heavy north east arrow\n10139 drafting point rightwards arrow\n10140 heavy round-tipped rightwards arrow\n10141 triangle-headed rightwards arrow\n10142 heavy triangle-headed rightwards arrow\n10143 dashed triangle-headed rightwards arrow\n10144 heavy dashed triangle-headed rightwards arrow\n10145 black rightwards arrow\n10146 three-d top-lighted rightwards arrowhead\n10147 three-d bottom-lighted rightwards arrowhead\n10148 black rightwards arrowhead\n10149 heavy black curved downwards and rightwards arrow\n10150 heavy black curved upwards and rightwards arrow\n10151 squat black rightwards arrow\n10152 heavy concave-pointed black rightwards arrow\n10153 right-shaded white rightwards arrow\n10154 left-shaded white rightwards arrow\n10155 back-tilted shadowed white rightwards arrow\n10156 front-tilted shadowed white rightwards arrow\n10157 heavy lower right-shadowed white rightwards arrow\n10158 heavy upper right-shadowed white rightwards arrow\n10159 notched lower right-shadowed white rightwards arrow\n10160 curly loop\n10161 notched upper right-shadowed white rightwards arrow\n10162 circled heavy white rightwards arrow\n10163 white-feathered rightwards arrow\n10164 black-feathered south east arrow\n10165 black-feathered rightwards arrow\n10166 black-feathered north east arrow\n10167 heavy black-feathered south east arrow\n10168 heavy black-feathered rightwards arrow\n10169 heavy black-feathered north east arrow\n10170 teardrop-barbed rightwards arrow\n10171 heavy teardrop-shanked rightwards arrow\n10172 wedge-tailed rightwards arrow\n10173 heavy wedge-tailed rightwards arrow\n10174 open-outlined rightwards arrow\n10175 double curly loop\n10176 three dimensional angle\n10177 white triangle containing small white triangle\n10178 perpendicular\n10179 open subset\n10180 open superset\n10181 left s-shaped bag delimiter\n10182 right s-shaped bag delimiter\n10183 or with dot inside\n10184 reverse solidus preceding subset\tbsolhsub\n10185 superset preceding solidus\tsuphsol\n10186 vertical bar with horizontal stroke\n10187 mathematical rising diagonal\n10188 long division\n10189 mathematical falling diagonal\n10190 squared logical and\n10191 squared logical or\n10192 white diamond with centred dot\n10193 and with dot\n10194 element of opening upwards\n10195 lower right corner with dot\n10196 upper left corner with dot\n10197 left outer join\n10198 right outer join\n10199 full outer join\n10200 large up tack\n10201 large down tack\n10202 left and right double turnstile\n10203 left and right tack\n10204 left multimap\n10205 long right tack\n10206 long left tack\n10207 up tack with circle above\n10208 lozenge divided by horizontal rule\n10209 white concave-sided diamond\n10210 white concave-sided diamond with leftwards tick\n10211 white concave-sided diamond with rightwards tick\n10212 white square with leftwards tick\n10213 white square with rightwards tick\n10214 mathematical left white square bracket\tleftdoublebracket lobrk\n10215 mathematical right white square bracket\trightdoublebracket robrk\n10216 mathematical left angle bracket\tlang langle leftanglebracket\n10217 mathematical right angle bracket\trang rangle rightanglebracket\n10218 mathematical left double angle bracket\tlang\n10219 mathematical right double angle bracket\trang\n10220 mathematical left white tortoise shell bracket\tloang\n10221 mathematical right white tortoise shell bracket\troang\n10222 mathematical left flattened parenthesis\n10223 mathematical right flattened parenthesis\n10224 upwards quadruple arrow\n10225 downwards quadruple arrow\n10226 anticlockwise gapped circle arrow\n10227 clockwise gapped circle arrow\n10228 right arrow with circled plus\n10229 long leftwards arrow\tlongleftarrow xlarr\n10230 long rightwards arrow\tlongrightarrow xrarr\n10231 long left right arrow\tlongleftrightarrow xharr\n10232 long leftwards double arrow\tdoublelongleftarrow longleftarrow xlarr\n10233 long rightwards double arrow\tdoublelongrightarrow longrightarrow xrarr\n10234 long left right double arrow\tdoublelongleftrightarrow longleftrightarrow xharr\n10235 long leftwards arrow from bar\n10236 long rightwards arrow from bar\tlongmapsto xmap\n10237 long leftwards double arrow from bar\n10238 long rightwards double arrow from bar\n10239 long rightwards squiggle arrow\tdzigrarr\n10240 braille pattern blank\n10241 braille pattern dots-1\n10242 braille pattern dots-2\n10243 braille pattern dots-12\n10244 braille pattern dots-3\n10245 braille pattern dots-13\n10246 braille pattern dots-23\n10247 braille pattern dots-123\n10248 braille pattern dots-4\n10249 braille pattern dots-14\n10250 braille pattern dots-24\n10251 braille pattern dots-124\n10252 braille pattern dots-34\n10253 braille pattern dots-134\n10254 braille pattern dots-234\n10255 braille pattern dots-1234\n10256 braille pattern dots-5\n10257 braille pattern dots-15\n10258 braille pattern dots-25\n10259 braille pattern dots-125\n10260 braille pattern dots-35\n10261 braille pattern dots-135\n10262 braille pattern dots-235\n10263 braille pattern dots-1235\n10264 braille pattern dots-45\n10265 braille pattern dots-145\n10266 braille pattern dots-245\n10267 braille pattern dots-1245\n10268 braille pattern dots-345\n10269 braille pattern dots-1345\n10270 braille pattern dots-2345\n10271 braille pattern dots-12345\n10272 braille pattern dots-6\n10273 braille pattern dots-16\n10274 braille pattern dots-26\n10275 braille pattern dots-126\n10276 braille pattern dots-36\n10277 braille pattern dots-136\n10278 braille pattern dots-236\n10279 braille pattern dots-1236\n10280 braille pattern dots-46\n10281 braille pattern dots-146\n10282 braille pattern dots-246\n10283 braille pattern dots-1246\n10284 braille pattern dots-346\n10285 braille pattern dots-1346\n10286 braille pattern dots-2346\n10287 braille pattern dots-12346\n10288 braille pattern dots-56\n10289 braille pattern dots-156\n10290 braille pattern dots-256\n10291 braille pattern dots-1256\n10292 braille pattern dots-356\n10293 braille pattern dots-1356\n10294 braille pattern dots-2356\n10295 braille pattern dots-12356\n10296 braille pattern dots-456\n10297 braille pattern dots-1456\n10298 braille pattern dots-2456\n10299 braille pattern dots-12456\n10300 braille pattern dots-3456\n10301 braille pattern dots-13456\n10302 braille pattern dots-23456\n10303 braille pattern dots-123456\n10304 braille pattern dots-7\n10305 braille pattern dots-17\n10306 braille pattern dots-27\n10307 braille pattern dots-127\n10308 braille pattern dots-37\n10309 braille pattern dots-137\n10310 braille pattern dots-237\n10311 braille pattern dots-1237\n10312 braille pattern dots-47\n10313 braille pattern dots-147\n10314 braille pattern dots-247\n10315 braille pattern dots-1247\n10316 braille pattern dots-347\n10317 braille pattern dots-1347\n10318 braille pattern dots-2347\n10319 braille pattern dots-12347\n10320 braille pattern dots-57\n10321 braille pattern dots-157\n10322 braille pattern dots-257\n10323 braille pattern dots-1257\n10324 braille pattern dots-357\n10325 braille pattern dots-1357\n10326 braille pattern dots-2357\n10327 braille pattern dots-12357\n10328 braille pattern dots-457\n10329 braille pattern dots-1457\n10330 braille pattern dots-2457\n10331 braille pattern dots-12457\n10332 braille pattern dots-3457\n10333 braille pattern dots-13457\n10334 braille pattern dots-23457\n10335 braille pattern dots-123457\n10336 braille pattern dots-67\n10337 braille pattern dots-167\n10338 braille pattern dots-267\n10339 braille pattern dots-1267\n10340 braille pattern dots-367\n10341 braille pattern dots-1367\n10342 braille pattern dots-2367\n10343 braille pattern dots-12367\n10344 braille pattern dots-467\n10345 braille pattern dots-1467\n10346 braille pattern dots-2467\n10347 braille pattern dots-12467\n10348 braille pattern dots-3467\n10349 braille pattern dots-13467\n10350 braille pattern dots-23467\n10351 braille pattern dots-123467\n10352 braille pattern dots-567\n10353 braille pattern dots-1567\n10354 braille pattern dots-2567\n10355 braille pattern dots-12567\n10356 braille pattern dots-3567\n10357 braille pattern dots-13567\n10358 braille pattern dots-23567\n10359 braille pattern dots-123567\n10360 braille pattern dots-4567\n10361 braille pattern dots-14567\n10362 braille pattern dots-24567\n10363 braille pattern dots-124567\n10364 braille pattern dots-34567\n10365 braille pattern dots-134567\n10366 braille pattern dots-234567\n10367 braille pattern dots-1234567\n10368 braille pattern dots-8\n10369 braille pattern dots-18\n10370 braille pattern dots-28\n10371 braille pattern dots-128\n10372 braille pattern dots-38\n10373 braille pattern dots-138\n10374 braille pattern dots-238\n10375 braille pattern dots-1238\n10376 braille pattern dots-48\n10377 braille pattern dots-148\n10378 braille pattern dots-248\n10379 braille pattern dots-1248\n10380 braille pattern dots-348\n10381 braille pattern dots-1348\n10382 braille pattern dots-2348\n10383 braille pattern dots-12348\n10384 braille pattern dots-58\n10385 braille pattern dots-158\n10386 braille pattern dots-258\n10387 braille pattern dots-1258\n10388 braille pattern dots-358\n10389 braille pattern dots-1358\n10390 braille pattern dots-2358\n10391 braille pattern dots-12358\n10392 braille pattern dots-458\n10393 braille pattern dots-1458\n10394 braille pattern dots-2458\n10395 braille pattern dots-12458\n10396 braille pattern dots-3458\n10397 braille pattern dots-13458\n10398 braille pattern dots-23458\n10399 braille pattern dots-123458\n10400 braille pattern dots-68\n10401 braille pattern dots-168\n10402 braille pattern dots-268\n10403 braille pattern dots-1268\n10404 braille pattern dots-368\n10405 braille pattern dots-1368\n10406 braille pattern dots-2368\n10407 braille pattern dots-12368\n10408 braille pattern dots-468\n10409 braille pattern dots-1468\n10410 braille pattern dots-2468\n10411 braille pattern dots-12468\n10412 braille pattern dots-3468\n10413 braille pattern dots-13468\n10414 braille pattern dots-23468\n10415 braille pattern dots-123468\n10416 braille pattern dots-568\n10417 braille pattern dots-1568\n10418 braille pattern dots-2568\n10419 braille pattern dots-12568\n10420 braille pattern dots-3568\n10421 braille pattern dots-13568\n10422 braille pattern dots-23568\n10423 braille pattern dots-123568\n10424 braille pattern dots-4568\n10425 braille pattern dots-14568\n10426 braille pattern dots-24568\n10427 braille pattern dots-124568\n10428 braille pattern dots-34568\n10429 braille pattern dots-134568\n10430 braille pattern dots-234568\n10431 braille pattern dots-1234568\n10432 braille pattern dots-78\n10433 braille pattern dots-178\n10434 braille pattern dots-278\n10435 braille pattern dots-1278\n10436 braille pattern dots-378\n10437 braille pattern dots-1378\n10438 braille pattern dots-2378\n10439 braille pattern dots-12378\n10440 braille pattern dots-478\n10441 braille pattern dots-1478\n10442 braille pattern dots-2478\n10443 braille pattern dots-12478\n10444 braille pattern dots-3478\n10445 braille pattern dots-13478\n10446 braille pattern dots-23478\n10447 braille pattern dots-123478\n10448 braille pattern dots-578\n10449 braille pattern dots-1578\n10450 braille pattern dots-2578\n10451 braille pattern dots-12578\n10452 braille pattern dots-3578\n10453 braille pattern dots-13578\n10454 braille pattern dots-23578\n10455 braille pattern dots-123578\n10456 braille pattern dots-4578\n10457 braille pattern dots-14578\n10458 braille pattern dots-24578\n10459 braille pattern dots-124578\n10460 braille pattern dots-34578\n10461 braille pattern dots-134578\n10462 braille pattern dots-234578\n10463 braille pattern dots-1234578\n10464 braille pattern dots-678\n10465 braille pattern dots-1678\n10466 braille pattern dots-2678\n10467 braille pattern dots-12678\n10468 braille pattern dots-3678\n10469 braille pattern dots-13678\n10470 braille pattern dots-23678\n10471 braille pattern dots-123678\n10472 braille pattern dots-4678\n10473 braille pattern dots-14678\n10474 braille pattern dots-24678\n10475 braille pattern dots-124678\n10476 braille pattern dots-34678\n10477 braille pattern dots-134678\n10478 braille pattern dots-234678\n10479 braille pattern dots-1234678\n10480 braille pattern dots-5678\n10481 braille pattern dots-15678\n10482 braille pattern dots-25678\n10483 braille pattern dots-125678\n10484 braille pattern dots-35678\n10485 braille pattern dots-135678\n10486 braille pattern dots-235678\n10487 braille pattern dots-1235678\n10488 braille pattern dots-45678\n10489 braille pattern dots-145678\n10490 braille pattern dots-245678\n10491 braille pattern dots-1245678\n10492 braille pattern dots-345678\n10493 braille pattern dots-1345678\n10494 braille pattern dots-2345678\n10495 braille pattern dots-12345678\n10496 rightwards two-headed arrow with vertical stroke\n10497 rightwards two-headed arrow with double vertical stroke\n10498 leftwards double arrow with vertical stroke\tnvlarr\n10499 rightwards double arrow with vertical stroke\tnvrarr\n10500 left right double arrow with vertical stroke\tnvharr\n10501 rightwards two-headed arrow from bar\tmap\n10502 leftwards double arrow from bar\n10503 rightwards double arrow from bar\n10504 downwards arrow with horizontal stroke\n10505 upwards arrow with horizontal stroke\n10506 upwards triple arrow\n10507 downwards triple arrow\n10508 leftwards double dash arrow\tlbarr\n10509 rightwards double dash arrow\tbkarow rbarr\n10510 leftwards triple dash arrow\tlbarr\n10511 rightwards triple dash arrow\tdbkarow rbarr\n10512 rightwards two-headed triple dash arrow\tdrbkarow rbarr\n10513 rightwards arrow with dotted stem\tddotrahd\n10514 upwards arrow to bar\tuparrowbar\n10515 downwards arrow to bar\tdownarrowbar\n10516 rightwards arrow with tail with vertical stroke\n10517 rightwards arrow with tail with double vertical stroke\n10518 rightwards two-headed arrow with tail\trarrtl\n10519 rightwards two-headed arrow with tail with vertical stroke\n10520 rightwards two-headed arrow with tail with double vertical stroke\n10521 leftwards arrow-tail\tlatail\n10522 rightwards arrow-tail\tratail\n10523 leftwards double arrow-tail\tlatail\n10524 rightwards double arrow-tail\tratail\n10525 leftwards arrow to black diamond\tlarrfs\n10526 rightwards arrow to black diamond\trarrfs\n10527 leftwards arrow from bar to black diamond\tlarrbfs\n10528 rightwards arrow from bar to black diamond\trarrbfs\n10529 north west and south east arrow\n10530 north east and south west arrow\n10531 north west arrow with hook\tnwarhk\n10532 north east arrow with hook\tnearhk\n10533 south east arrow with hook\thksearow searhk\n10534 south west arrow with hook\thkswarow swarhk\n10535 north west arrow and north east arrow\tnwnear\n10536 north east arrow and south east arrow\tnesear toea\n10537 south east arrow and south west arrow\tseswar tosa\n10538 south west arrow and north west arrow\tswnwar\n10539 rising diagonal crossing falling diagonal\n10540 falling diagonal crossing rising diagonal\n10541 south east arrow crossing north east arrow\n10542 north east arrow crossing south east arrow\n10543 falling diagonal crossing north east arrow\n10544 rising diagonal crossing south east arrow\n10545 north east arrow crossing north west arrow\n10546 north west arrow crossing north east arrow\n10547 wave arrow pointing directly right\trarrc\n10548 arrow pointing rightwards then curving upwards\n10549 arrow pointing rightwards then curving downwards\tcudarrr\n10550 arrow pointing downwards then curving leftwards\tldca\n10551 arrow pointing downwards then curving rightwards\trdca\n10552 right-side arc clockwise arrow\tcudarrl\n10553 left-side arc anticlockwise arrow\tlarrpl\n10554 top arc anticlockwise arrow\n10555 bottom arc anticlockwise arrow\n10556 top arc clockwise arrow with minus\tcurarrm\n10557 top arc anticlockwise arrow with plus\tcularrp\n10558 lower right semicircular clockwise arrow\n10559 lower left semicircular anticlockwise arrow\n10560 anticlockwise closed circle arrow\n10561 clockwise closed circle arrow\n10562 rightwards arrow above short leftwards arrow\n10563 leftwards arrow above short rightwards arrow\n10564 short rightwards arrow above leftwards arrow\n10565 rightwards arrow with plus below\trarrpl\n10566 leftwards arrow with plus below\n10567 rightwards arrow through x\n10568 left right arrow through small circle\tharrcir\n10569 upwards two-headed arrow from small circle\tuarrocir\n10570 left barb up right barb down harpoon\tlurdshar\n10571 left barb down right barb up harpoon\tldrushar\n10572 up barb right down barb left harpoon\n10573 up barb left down barb right harpoon\n10574 left barb up right barb up harpoon\tleftrightvector\n10575 up barb right down barb right harpoon\trightupdownvector\n10576 left barb down right barb down harpoon\tdownleftrightvector\n10577 up barb left down barb left harpoon\tleftupdownvector\n10578 leftwards harpoon with barb up to bar\tleftvectorbar\n10579 rightwards harpoon with barb up to bar\trightvectorbar\n10580 upwards harpoon with barb right to bar\trightupvectorbar\n10581 downwards harpoon with barb right to bar\trightdownvectorbar\n10582 leftwards harpoon with barb down to bar\tdownleftvectorbar\n10583 rightwards harpoon with barb down to bar\tdownrightvectorbar\n10584 upwards harpoon with barb left to bar\tleftupvectorbar\n10585 downwards harpoon with barb left to bar\tleftdownvectorbar\n10586 leftwards harpoon with barb up from bar\tleftteevector\n10587 rightwards harpoon with barb up from bar\trightteevector\n10588 upwards harpoon with barb right from bar\trightupteevector\n10589 downwards harpoon with barb right from bar\trightdownteevector\n10590 leftwards harpoon with barb down from bar\tdownleftteevector\n10591 rightwards harpoon with barb down from bar\tdownrightteevector\n10592 upwards harpoon with barb left from bar\tleftupteevector\n10593 downwards harpoon with barb left from bar\tleftdownteevector\n10594 leftwards harpoon with barb up above leftwards harpoon with barb down\tlhar\n10595 upwards harpoon with barb left beside upwards harpoon with barb right\tuhar\n10596 rightwards harpoon with barb up above rightwards harpoon with barb down\trhar\n10597 downwards harpoon with barb left beside downwards harpoon with barb right\tdhar\n10598 leftwards harpoon with barb up above rightwards harpoon with barb up\tluruhar\n10599 leftwards harpoon with barb down above rightwards harpoon with barb down\tldrdhar\n10600 rightwards harpoon with barb up above leftwards harpoon with barb up\truluhar\n10601 rightwards harpoon with barb down above leftwards harpoon with barb down\trdldhar\n10602 leftwards harpoon with barb up above long dash\tlharul\n10603 leftwards harpoon with barb down below long dash\tllhard\n10604 rightwards harpoon with barb up above long dash\trharul\n10605 rightwards harpoon with barb down below long dash\tlrhard\n10606 upwards harpoon with barb left beside downwards harpoon with barb right\tudhar upequilibrium\n10607 downwards harpoon with barb left beside upwards harpoon with barb right\tduhar reverseupequilibrium\n10608 right double arrow with rounded head\troundimplies\n10609 equals sign above rightwards arrow\terarr\n10610 tilde operator above rightwards arrow\tsimrarr\n10611 leftwards arrow above tilde operator\tlarrsim\n10612 rightwards arrow above tilde operator\trarrsim\n10613 rightwards arrow above almost equal to\trarrap\n10614 less-than above leftwards arrow\tltlarr\n10615 leftwards arrow through less-than\n10616 greater-than above rightwards arrow\tgtrarr\n10617 subset above rightwards arrow\tsubrarr\n10618 leftwards arrow through subset\n10619 superset above leftwards arrow\tsuplarr\n10620 left fish tail\tlfisht\n10621 right fish tail\trfisht\n10622 up fish tail\tufisht\n10623 down fish tail\tdfisht\n10624 triple vertical bar delimiter\n10625 z notation spot\n10626 z notation type colon\n10627 left white curly bracket\n10628 right white curly bracket\n10629 left white parenthesis\tlopar\n10630 right white parenthesis\tropar\n10631 z notation left image bracket\n10632 z notation right image bracket\n10633 z notation left binding bracket\n10634 z notation right binding bracket\n10635 left square bracket with underbar\tlbrke\n10636 right square bracket with underbar\trbrke\n10637 left square bracket with tick in top corner\tlbrkslu\n10638 right square bracket with tick in bottom corner\trbrksld\n10639 left square bracket with tick in bottom corner\tlbrksld\n10640 right square bracket with tick in top corner\trbrkslu\n10641 left angle bracket with dot\tlangd\n10642 right angle bracket with dot\trangd\n10643 left arc less-than bracket\tlparlt\n10644 right arc greater-than bracket\trpargt\n10645 double left arc greater-than bracket\tgtlpar\n10646 double right arc less-than bracket\tltrpar\n10647 left black tortoise shell bracket\n10648 right black tortoise shell bracket\n10649 dotted fence\n10650 vertical zigzag line\tvzigzag\n10651 measured angle opening left\n10652 right angle variant with square\tvangrt\n10653 measured right angle with dot\tangrtvbd\n10654 angle with s inside\n10655 acute angle\n10656 spherical angle opening left\n10657 spherical angle opening up\n10658 turned angle\n10659 reversed angle\n10660 angle with underbar\tange\n10661 reversed angle with underbar\trange\n10662 oblique angle opening up\tdwangle\n10663 oblique angle opening down\tuwangle\n10664 measured angle with open arm ending in arrow pointing up and right\tangmsdaa\n10665 measured angle with open arm ending in arrow pointing up and left\tangmsdab\n10666 measured angle with open arm ending in arrow pointing down and right\tangmsdac\n10667 measured angle with open arm ending in arrow pointing down and left\tangmsdad\n10668 measured angle with open arm ending in arrow pointing right and up\tangmsdae\n10669 measured angle with open arm ending in arrow pointing left and up\tangmsdaf\n10670 measured angle with open arm ending in arrow pointing right and down\tangmsdag\n10671 measured angle with open arm ending in arrow pointing left and down\tangmsdah\n10672 reversed empty set\tbemptyv\n10673 empty set with overbar\tdemptyv\n10674 empty set with small circle above\tcemptyv\n10675 empty set with right arrow above\traemptyv\n10676 empty set with left arrow above\tlaemptyv\n10677 circle with horizontal bar\tohbar\n10678 circled vertical bar\tomid\n10679 circled parallel\topar\n10680 circled reverse solidus\n10681 circled perpendicular\toperp\n10682 circle divided by horizontal bar and top half divided by vertical bar\n10683 circle with superimposed x\tolcross\n10684 circled anticlockwise-rotated division sign\todsold\n10685 up arrow through circle\n10686 circled white bullet\tolcir\n10687 circled bullet\tofcir\n10688 circled less-than\tolt\n10689 circled greater-than\togt\n10690 circle with small circle to the right\tcirscir\n10691 circle with two horizontal strokes to the right\tcire\n10692 squared rising diagonal slash\tsolb\n10693 squared falling diagonal slash\tbsolb\n10694 squared asterisk\n10695 squared small circle\n10696 squared square\n10697 two joined squares\tboxbox\n10698 triangle with dot above\n10699 triangle with underbar\n10700 s in triangle\n10701 triangle with serifs at bottom\ttrisb\n10702 right triangle above left triangle\trtriltri\n10703 left triangle beside vertical bar\tlefttrianglebar\n10704 vertical bar beside right triangle\trighttrianglebar\n10705 bowtie with left half black\n10706 bowtie with right half black\n10707 black bowtie\n10708 times with left half black\n10709 times with right half black\n10710 white hourglass\n10711 black hourglass\n10712 left wiggly fence\n10713 right wiggly fence\n10714 left double wiggly fence\n10715 right double wiggly fence\n10716 incomplete infinity\tiinfin\n10717 tie over infinity\tinfintie\n10718 infinity negated with vertical bar\tnvinfin\n10719 double-ended multimap\n10720 square with contoured outline\n10721 increases as\n10722 shuffle product\n10723 equals sign and slanted parallel\teparsl\n10724 equals sign and slanted parallel with tilde above\tsmeparsl\n10725 identical to and slanted parallel\teqvparsl\n10726 gleich stark\n10727 thermodynamic\n10728 down-pointing triangle with left half black\n10729 down-pointing triangle with right half black\n10730 black diamond with down arrow\n10731 black lozenge\tblacklozenge lozf\n10732 white circle with down arrow\n10733 black circle with down arrow\n10734 error-barred white square\n10735 error-barred black square\n10736 error-barred white diamond\n10737 error-barred black diamond\n10738 error-barred white circle\n10739 error-barred black circle\n10740 rule-delayed\truledelayed\n10741 reverse solidus operator\n10742 solidus with overbar\tdsol\n10743 reverse solidus with horizontal stroke\n10744 big solidus\n10745 big reverse solidus\n10746 double plus\n10747 triple plus\n10748 left-pointing curved angle bracket\n10749 right-pointing curved angle bracket\n10750 tiny\n10751 miny\n10752 n-ary circled dot operator\tbigodot xodot\n10753 n-ary circled plus operator\tbigoplus xoplus\n10754 n-ary circled times operator\tbigotimes xotime\n10755 n-ary union operator with dot\n10756 n-ary union operator with plus\tbiguplus xuplus\n10757 n-ary square intersection operator\n10758 n-ary square union operator\tbigsqcup xsqcup\n10759 two logical and operator\n10760 two logical or operator\n10761 n-ary times operator\n10762 modulo two sum\n10763 summation with integral\n10764 quadruple integral operator\tiiiint qint\n10765 finite part integral\tfpartint\n10766 integral with double stroke\n10767 integral average with slash\n10768 circulation function\tcirfnint\n10769 anticlockwise integration\tawint\n10770 line integration with rectangular path around pole\trppolint\n10771 line integration with semicircular path around pole\tscpolint\n10772 line integration not including the pole\tnpolint\n10773 integral around a point operator\tpointint\n10774 quaternion integral operator\tquatint\n10775 integral with leftwards arrow with hook\tintlarhk\n10776 integral with times sign\n10777 integral with intersection\n10778 integral with union\n10779 integral with overbar\n10780 integral with underbar\n10781 join\n10782 large left triangle operator\n10783 z notation schema composition\n10784 z notation schema piping\n10785 z notation schema projection\n10786 plus sign with small circle above\tpluscir\n10787 plus sign with circumflex accent above\tplusacir\n10788 plus sign with tilde above\tsimplus\n10789 plus sign with dot below\tplusdu\n10790 plus sign with tilde below\tplussim\n10791 plus sign with subscript two\tplustwo\n10792 plus sign with black triangle\n10793 minus sign with comma above\tmcomma\n10794 minus sign with dot below\tminusdu\n10795 minus sign with falling dots\n10796 minus sign with rising dots\n10797 plus sign in left half circle\tloplus\n10798 plus sign in right half circle\troplus\n10799 vector or cross product\n10800 multiplication sign with dot above\ttimesd\n10801 multiplication sign with underbar\ttimesbar\n10802 semidirect product with bottom closed\n10803 smash product\tsmashp\n10804 multiplication sign in left half circle\tlotimes\n10805 multiplication sign in right half circle\trotimes\n10806 circled multiplication sign with circumflex accent\totimesas\n10807 multiplication sign in double circle\totimes\n10808 circled division sign\todiv\n10809 plus sign in triangle\ttriplus\n10810 minus sign in triangle\ttriminus\n10811 multiplication sign in triangle\ttritime\n10812 interior product\tintprod iprod\n10813 righthand interior product\n10814 z notation relational composition\n10815 amalgamation or coproduct\tamalg\n10816 intersection with dot\tcapdot\n10817 union with minus sign\n10818 union with overbar\tncup\n10819 intersection with overbar\tncap\n10820 intersection with logical and\tcapand\n10821 union with logical or\tcupor\n10822 union above intersection\tcupcap\n10823 intersection above union\tcapcup\n10824 union above bar above intersection\tcupbrcap\n10825 intersection above bar above union\tcapbrcup\n10826 union beside and joined with union\tcupcup\n10827 intersection beside and joined with intersection\tcapcap\n10828 closed union with serifs\tccups\n10829 closed intersection with serifs\tccaps\n10830 double square intersection\n10831 double square union\n10832 closed union with serifs and smash product\tccupssm\n10833 logical and with dot above\n10834 logical or with dot above\n10835 double logical and\n10836 double logical or\n10837 two intersecting logical and\tandand\n10838 two intersecting logical or\toror\n10839 sloping large or\torslope\n10840 sloping large and\tandslope\n10841 logical or overlapping logical and\n10842 logical and with middle stem\tandv\n10843 logical or with middle stem\torv\n10844 logical and with horizontal dash\tandd\n10845 logical or with horizontal dash\tord\n10846 logical and with double overbar\n10847 logical and with underbar\twedbar\n10848 logical and with double underbar\n10849 small vee with underbar\n10850 logical or with double overbar\n10851 logical or with double underbar\n10852 z notation domain antirestriction\n10853 z notation range antirestriction\n10854 equals sign with dot below\tsdote\n10855 identical with dot above\n10856 triple horizontal bar with double vertical stroke\n10857 triple horizontal bar with triple vertical stroke\n10858 tilde operator with dot above\tsimdot\n10859 tilde operator with rising dots\n10860 similar minus similar\n10861 congruent with dot above\tcongdot\n10862 equals with asterisk\teaster\n10863 almost equal to with circumflex accent\tapacir\n10864 approximately equal or equal to\tape\n10865 equals sign above plus sign\teplus\n10866 plus sign above equals sign\tpluse\n10867 equals sign above tilde operator\tesim\n10868 double colon equal\tcolone\n10869 two consecutive equals signs\tequal\n10870 three consecutive equals signs\n10871 equals sign with two dots above and two dots below\tddotseq eddot\n10872 equivalent with four dots above\tequivdd\n10873 less-than with circle inside\tltcir\n10874 greater-than with circle inside\tgtcir\n10875 less-than with question mark above\tltquest\n10876 greater-than with question mark above\tgtquest\n10877 less-than or slanted equal to\tleqslant les lessslantequal\n10878 greater-than or slanted equal to\tgeqslant ges greaterslantequal\n10879 less-than or slanted equal to with dot inside\tlesdot\n10880 greater-than or slanted equal to with dot inside\tgesdot\n10881 less-than or slanted equal to with dot above\tlesdoto\n10882 greater-than or slanted equal to with dot above\tgesdoto\n10883 less-than or slanted equal to with dot above right\tlesdotor\n10884 greater-than or slanted equal to with dot above left\tgesdotol\n10885 less-than or approximate\tlap lessapprox\n10886 greater-than or approximate\tgap gtrapprox\n10887 less-than and single-line not equal to\tlne lneq\n10888 greater-than and single-line not equal to\tgne gneq\n10889 less-than and not approximate\tlnap lnapprox\n10890 greater-than and not approximate\tgnap gnapprox\n10891 less-than above double-line equal above greater-than\tleg lesseqqgtr\n10892 greater-than above double-line equal above less-than\tgel gtreqqless\n10893 less-than above similar or equal\tlsime\n10894 greater-than above similar or equal\tgsime\n10895 less-than above similar above greater-than\tlsimg\n10896 greater-than above similar above less-than\tgsiml\n10897 less-than above greater-than above double-line equal\tlge\n10898 greater-than above less-than above double-line equal\tgle\n10899 less-than above slanted equal above greater-than above slanted equal\tlesges\n10900 greater-than above slanted equal above less-than above slanted equal\tgesles\n10901 slanted equal to or less-than\tels eqslantless\n10902 slanted equal to or greater-than\tegs eqslantgtr\n10903 slanted equal to or less-than with dot inside\telsdot\n10904 slanted equal to or greater-than with dot inside\tegsdot\n10905 double-line equal to or less-than\tel\n10906 double-line equal to or greater-than\teg\n10907 double-line slanted equal to or less-than\n10908 double-line slanted equal to or greater-than\n10909 similar or less-than\tsiml\n10910 similar or greater-than\tsimg\n10911 similar above less-than above equals sign\tsimle\n10912 similar above greater-than above equals sign\tsimge\n10913 double nested less-than\tlessless\n10914 double nested greater-than\tgreatergreater\n10915 double nested less-than with underbar\n10916 greater-than overlapping less-than\tglj\n10917 greater-than beside less-than\tgla\n10918 less-than closed by curve\tltcc\n10919 greater-than closed by curve\tgtcc\n10920 less-than closed by curve above slanted equal\tlescc\n10921 greater-than closed by curve above slanted equal\tgescc\n10922 smaller than\tsmt\n10923 larger than\tlat\n10924 smaller than or equal to\tsmte\n10925 larger than or equal to\tlate\n10926 equals sign with bumpy above\tbumpe\n10927 precedes above single-line equals sign\tpre precedesequal preceq\n10928 succeeds above single-line equals sign\tsce succeedsequal succeq\n10929 precedes above single-line not equal to\n10930 succeeds above single-line not equal to\n10931 precedes above equals sign\tpre\n10932 succeeds above equals sign\tsce\n10933 precedes above not equal to\tprecneqq prne\n10934 succeeds above not equal to\tscne succneqq\n10935 precedes above almost equal to\tprap precapprox\n10936 succeeds above almost equal to\tscap succapprox\n10937 precedes above not almost equal to\tprecnapprox prnap\n10938 succeeds above not almost equal to\tscnap succnapprox\n10939 double precedes\tpr\n10940 double succeeds\tsc\n10941 subset with dot\tsubdot\n10942 superset with dot\tsupdot\n10943 subset with plus sign below\tsubplus\n10944 superset with plus sign below\tsupplus\n10945 subset with multiplication sign below\tsubmult\n10946 superset with multiplication sign below\tsupmult\n10947 subset of or equal to with dot above\tsubedot\n10948 superset of or equal to with dot above\tsupedot\n10949 subset of above equals sign\tsube subseteqq\n10950 superset of above equals sign\tsupe supseteqq\n10951 subset of above tilde operator\tsubsim\n10952 superset of above tilde operator\tsupsim\n10953 subset of above almost equal to\n10954 superset of above almost equal to\n10955 subset of above not equal to\tsubne subsetneqq\n10956 superset of above not equal to\tsupne supsetneqq\n10957 square left open box operator\n10958 square right open box operator\n10959 closed subset\tcsub\n10960 closed superset\tcsup\n10961 closed subset or equal to\tcsube\n10962 closed superset or equal to\tcsupe\n10963 subset above superset\tsubsup\n10964 superset above subset\tsupsub\n10965 subset above subset\tsubsub\n10966 superset above superset\tsupsup\n10967 superset beside subset\tsuphsub\n10968 superset beside and joined by dash with subset\tsupdsub\n10969 element of opening downwards\tforkv\n10970 pitchfork with tee top\ttopfork\n10971 transversal intersection\tmlcp\n10972 forking\n10973 nonforking\n10974 short left tack\n10975 short down tack\n10976 short up tack\n10977 perpendicular with s\n10978 vertical bar triple right turnstile\n10979 double vertical bar left turnstile\n10980 vertical bar double left turnstile\tdashv doublelefttee\n10981 double vertical bar double left turnstile\n10982 long dash from left member of double vertical\tvdashl\n10983 short down tack with overbar\tbarv\n10984 short up tack with underbar\tvbar\n10985 short up tack above short down tack\tvbarv\n10986 double down tack\n10987 double up tack\tvbar\n10988 double stroke not sign\n10989 reversed double stroke not sign\tbnot\n10990 does not divide with reversed negation slash\trnmid\n10991 vertical line with circle above\tcirmid\n10992 vertical line with circle below\tmidcir\n10993 down tack with circle below\ttopcir\n10994 parallel with horizontal stroke\tnhpar\n10995 parallel with tilde operator\tparsim\n10996 triple vertical bar binary relation\n10997 triple vertical bar with horizontal stroke\n10998 triple colon operator\n10999 triple nested less-than\n11000 triple nested greater-than\n11001 double-line slanted less-than or equal to\n11002 double-line slanted greater-than or equal to\n11003 triple solidus binary relation\n11004 large triple vertical bar operator\n11005 double solidus operator\tparsl\n11006 white vertical bar\n11007 n-ary white vertical bar\n11008 north east white arrow\n11009 north west white arrow\n11010 south east white arrow\n11011 south west white arrow\n11012 left right white arrow\n11013 leftwards black arrow\n11014 upwards black arrow\n11015 downwards black arrow\n11016 north east black arrow\n11017 north west black arrow\n11018 south east black arrow\n11019 south west black arrow\n11020 left right black arrow\n11021 up down black arrow\n11022 rightwards arrow with tip downwards\n11023 rightwards arrow with tip upwards\n11024 leftwards arrow with tip downwards\n11025 leftwards arrow with tip upwards\n11026 square with top half black\n11027 square with bottom half black\n11028 square with upper right diagonal half black\n11029 square with lower left diagonal half black\n11030 diamond with left half black\n11031 diamond with right half black\n11032 diamond with top half black\n11033 diamond with bottom half black\n11034 dotted square\n11035 black large square\n11036 white large square\n11037 black very small square\n11038 white very small square\n11039 black pentagon\n11040 white pentagon\n11041 white hexagon\n11042 black hexagon\n11043 horizontal black hexagon\n11044 black large circle\n11045 black medium diamond\n11046 white medium diamond\n11047 black medium lozenge\n11048 white medium lozenge\n11049 black small diamond\n11050 black small lozenge\n11051 white small lozenge\n11052 black horizontal ellipse\n11053 white horizontal ellipse\n11054 black vertical ellipse\n11055 white vertical ellipse\n11056 left arrow with small circle\n11057 three leftwards arrows\n11058 left arrow with circled plus\n11059 long leftwards squiggle arrow\n11060 leftwards two-headed arrow with vertical stroke\n11061 leftwards two-headed arrow with double vertical stroke\n11062 leftwards two-headed arrow from bar\n11063 leftwards two-headed triple dash arrow\n11064 leftwards arrow with dotted stem\n11065 leftwards arrow with tail with vertical stroke\n11066 leftwards arrow with tail with double vertical stroke\n11067 leftwards two-headed arrow with tail\n11068 leftwards two-headed arrow with tail with vertical stroke\n11069 leftwards two-headed arrow with tail with double vertical stroke\n11070 leftwards arrow through x\n11071 wave arrow pointing directly left\n11072 equals sign above leftwards arrow\n11073 reverse tilde operator above leftwards arrow\n11074 leftwards arrow above reverse almost equal to\n11075 rightwards arrow through greater-than\n11076 rightwards arrow through superset\n11077 leftwards quadruple arrow\n11078 rightwards quadruple arrow\n11079 reverse tilde operator above rightwards arrow\n11080 rightwards arrow above reverse almost equal to\n11081 tilde operator above leftwards arrow\n11082 leftwards arrow above almost equal to\n11083 leftwards arrow above reverse tilde operator\n11084 rightwards arrow above reverse tilde operator\n11085 downwards triangle-headed zigzag arrow\n11086 short slanted north arrow\n11087 short backslanted south arrow\n11088 white medium star\n11089 black small star\n11090 white small star\n11091 black right-pointing pentagon\n11092 white right-pointing pentagon\n11093 heavy large circle\n11094 heavy oval with oval inside\n11095 heavy circle with circle inside\n11096 heavy circle\n11097 heavy circled saltire\n11098 slanted north arrow with hooked head\n11099 backslanted south arrow with hooked tail\n11100 slanted north arrow with horizontal tail\n11101 backslanted south arrow with horizontal tail\n11102 bent arrow pointing downwards then north east\n11103 short bent arrow pointing downwards then north east\n11104 leftwards triangle-headed arrow\n11105 upwards triangle-headed arrow\n11106 rightwards triangle-headed arrow\n11107 downwards triangle-headed arrow\n11108 left right triangle-headed arrow\n11109 up down triangle-headed arrow\n11110 north west triangle-headed arrow\n11111 north east triangle-headed arrow\n11112 south east triangle-headed arrow\n11113 south west triangle-headed arrow\n11114 leftwards triangle-headed dashed arrow\n11115 upwards triangle-headed dashed arrow\n11116 rightwards triangle-headed dashed arrow\n11117 downwards triangle-headed dashed arrow\n11118 clockwise triangle-headed open circle arrow\n11119 anticlockwise triangle-headed open circle arrow\n11120 leftwards triangle-headed arrow to bar\n11121 upwards triangle-headed arrow to bar\n11122 rightwards triangle-headed arrow to bar\n11123 downwards triangle-headed arrow to bar\n11126 north west triangle-headed arrow to bar\n11127 north east triangle-headed arrow to bar\n11128 south east triangle-headed arrow to bar\n11129 south west triangle-headed arrow to bar\n11130 leftwards triangle-headed arrow with double horizontal stroke\n11131 upwards triangle-headed arrow with double horizontal stroke\n11132 rightwards triangle-headed arrow with double horizontal stroke\n11133 downwards triangle-headed arrow with double horizontal stroke\n11134 horizontal tab key\n11135 vertical tab key\n11136 leftwards triangle-headed arrow over rightwards triangle-headed arrow\n11137 upwards triangle-headed arrow leftwards of downwards triangle-headed arrow\n11138 rightwards triangle-headed arrow over leftwards triangle-headed arrow\n11139 downwards triangle-headed arrow leftwards of upwards triangle-headed arrow\n11140 leftwards triangle-headed paired arrows\n11141 upwards triangle-headed paired arrows\n11142 rightwards triangle-headed paired arrows\n11143 downwards triangle-headed paired arrows\n11144 leftwards black circled white arrow\n11145 upwards black circled white arrow\n11146 rightwards black circled white arrow\n11147 downwards black circled white arrow\n11148 anticlockwise triangle-headed right u-shaped arrow\n11149 anticlockwise triangle-headed bottom u-shaped arrow\n11150 anticlockwise triangle-headed left u-shaped arrow\n11151 anticlockwise triangle-headed top u-shaped arrow\n11152 return left\n11153 return right\n11154 newline left\n11155 newline right\n11156 four corner arrows circling anticlockwise\n11157 rightwards black arrow\n11158 equals sign with infinity above\n11159 symbol for type a electronics\n11160 three-d top-lighted leftwards equilateral arrowhead\n11161 three-d right-lighted upwards equilateral arrowhead\n11162 three-d top-lighted rightwards equilateral arrowhead\n11163 three-d left-lighted downwards equilateral arrowhead\n11164 black leftwards equilateral arrowhead\n11165 black upwards equilateral arrowhead\n11166 black rightwards equilateral arrowhead\n11167 black downwards equilateral arrowhead\n11168 downwards triangle-headed arrow with long tip leftwards\n11169 downwards triangle-headed arrow with long tip rightwards\n11170 upwards triangle-headed arrow with long tip leftwards\n11171 upwards triangle-headed arrow with long tip rightwards\n11172 leftwards triangle-headed arrow with long tip upwards\n11173 rightwards triangle-headed arrow with long tip upwards\n11174 leftwards triangle-headed arrow with long tip downwards\n11175 rightwards triangle-headed arrow with long tip downwards\n11176 black curved downwards and leftwards arrow\n11177 black curved downwards and rightwards arrow\n11178 black curved upwards and leftwards arrow\n11179 black curved upwards and rightwards arrow\n11180 black curved leftwards and upwards arrow\n11181 black curved rightwards and upwards arrow\n11182 black curved leftwards and downwards arrow\n11183 black curved rightwards and downwards arrow\n11184 ribbon arrow down left\n11185 ribbon arrow down right\n11186 ribbon arrow up left\n11187 ribbon arrow up right\n11188 ribbon arrow left up\n11189 ribbon arrow right up\n11190 ribbon arrow left down\n11191 ribbon arrow right down\n11192 upwards white arrow from bar with horizontal bar\n11193 up arrowhead in a rectangle box\n11194 overlapping white squares\n11195 overlapping white and black squares\n11196 overlapping black squares\n11197 ballot box with light x\n11198 circled x\n11199 circled bold x\n11200 black square centred\n11201 black diamond centred\n11202 turned black pentagon\n11203 horizontal black octagon\n11204 black octagon\n11205 black medium up-pointing triangle centred\n11206 black medium down-pointing triangle centred\n11207 black medium left-pointing triangle centred\n11208 black medium right-pointing triangle centred\n11209 neptune form two\n11210 top half black circle\n11211 bottom half black circle\n11212 light four pointed black cusp\n11213 rotated light four pointed black cusp\n11214 white four pointed cusp\n11215 rotated white four pointed cusp\n11216 square position indicator\n11217 uncertainty sign\n11218 group mark\n11219 pluto form two\n11220 pluto form three\n11221 pluto form four\n11222 pluto form five\n11223 transpluto\n11224 proserpina\n11225 astraea\n11226 hygiea\n11227 pholus\n11228 nessus\n11229 white moon selena\n11230 black diamond on cross\n11231 true light moon arta\n11232 cupido\n11233 hades\n11234 zeus\n11235 kronos\n11236 apollon\n11237 admetos\n11238 vulcanus\n11239 poseidon\n11240 left half black star\n11241 right half black star\n11242 star with left half black\n11243 star with right half black\n11244 leftwards two-headed arrow with triangle arrowheads\n11245 upwards two-headed arrow with triangle arrowheads\n11246 rightwards two-headed arrow with triangle arrowheads\n11247 downwards two-headed arrow with triangle arrowheads\n11248 eris form one\n11249 eris form two\n11250 sedna\n11251 russian astrological symbol vigintile\n11252 russian astrological symbol novile\n11253 russian astrological symbol quintile\n11254 russian astrological symbol binovile\n11255 russian astrological symbol sentagon\n11256 russian astrological symbol tredecile\n11257 equals sign with infinity below\n11258 united symbol\n11259 separated symbol\n11260 doubled symbol\n11261 passed symbol\n11262 reversed right angle\n11263 hellschreiber pause symbol\n11264 glagolitic capital letter azu\n11265 glagolitic capital letter buky\n11266 glagolitic capital letter vede\n11267 glagolitic capital letter glagoli\n11268 glagolitic capital letter dobro\n11269 glagolitic capital letter yestu\n11270 glagolitic capital letter zhivete\n11271 glagolitic capital letter dzelo\n11272 glagolitic capital letter zemlja\n11273 glagolitic capital letter izhe\n11274 glagolitic capital letter initial izhe\n11275 glagolitic capital letter i\n11276 glagolitic capital letter djervi\n11277 glagolitic capital letter kako\n11278 glagolitic capital letter ljudije\n11279 glagolitic capital letter myslite\n11280 glagolitic capital letter nashi\n11281 glagolitic capital letter onu\n11282 glagolitic capital letter pokoji\n11283 glagolitic capital letter ritsi\n11284 glagolitic capital letter slovo\n11285 glagolitic capital letter tvrido\n11286 glagolitic capital letter uku\n11287 glagolitic capital letter fritu\n11288 glagolitic capital letter heru\n11289 glagolitic capital letter otu\n11290 glagolitic capital letter pe\n11291 glagolitic capital letter shta\n11292 glagolitic capital letter tsi\n11293 glagolitic capital letter chrivi\n11294 glagolitic capital letter sha\n11295 glagolitic capital letter yeru\n11296 glagolitic capital letter yeri\n11297 glagolitic capital letter yati\n11298 glagolitic capital letter spidery ha\n11299 glagolitic capital letter yu\n11300 glagolitic capital letter small yus\n11301 glagolitic capital letter small yus with tail\n11302 glagolitic capital letter yo\n11303 glagolitic capital letter iotated small yus\n11304 glagolitic capital letter big yus\n11305 glagolitic capital letter iotated big yus\n11306 glagolitic capital letter fita\n11307 glagolitic capital letter izhitsa\n11308 glagolitic capital letter shtapic\n11309 glagolitic capital letter trokutasti a\n11310 glagolitic capital letter latinate myslite\n11311 glagolitic capital letter caudate chrivi\n11312 glagolitic small letter azu\n11313 glagolitic small letter buky\n11314 glagolitic small letter vede\n11315 glagolitic small letter glagoli\n11316 glagolitic small letter dobro\n11317 glagolitic small letter yestu\n11318 glagolitic small letter zhivete\n11319 glagolitic small letter dzelo\n11320 glagolitic small letter zemlja\n11321 glagolitic small letter izhe\n11322 glagolitic small letter initial izhe\n11323 glagolitic small letter i\n11324 glagolitic small letter djervi\n11325 glagolitic small letter kako\n11326 glagolitic small letter ljudije\n11327 glagolitic small letter myslite\n11328 glagolitic small letter nashi\n11329 glagolitic small letter onu\n11330 glagolitic small letter pokoji\n11331 glagolitic small letter ritsi\n11332 glagolitic small letter slovo\n11333 glagolitic small letter tvrido\n11334 glagolitic small letter uku\n11335 glagolitic small letter fritu\n11336 glagolitic small letter heru\n11337 glagolitic small letter otu\n11338 glagolitic small letter pe\n11339 glagolitic small letter shta\n11340 glagolitic small letter tsi\n11341 glagolitic small letter chrivi\n11342 glagolitic small letter sha\n11343 glagolitic small letter yeru\n11344 glagolitic small letter yeri\n11345 glagolitic small letter yati\n11346 glagolitic small letter spidery ha\n11347 glagolitic small letter yu\n11348 glagolitic small letter small yus\n11349 glagolitic small letter small yus with tail\n11350 glagolitic small letter yo\n11351 glagolitic small letter iotated small yus\n11352 glagolitic small letter big yus\n11353 glagolitic small letter iotated big yus\n11354 glagolitic small letter fita\n11355 glagolitic small letter izhitsa\n11356 glagolitic small letter shtapic\n11357 glagolitic small letter trokutasti a\n11358 glagolitic small letter latinate myslite\n11359 glagolitic small letter caudate chrivi\n11360 latin capital letter l with double bar\n11361 latin small letter l with double bar\n11362 latin capital letter l with middle tilde\n11363 latin capital letter p with stroke\n11364 latin capital letter r with tail\n11365 latin small letter a with stroke\n11366 latin small letter t with diagonal stroke\n11367 latin capital letter h with descender\n11368 latin small letter h with descender\n11369 latin capital letter k with descender\n11370 latin small letter k with descender\n11371 latin capital letter z with descender\n11372 latin small letter z with descender\n11373 latin capital letter alpha\n11374 latin capital letter m with hook\n11375 latin capital letter turned a\n11376 latin capital letter turned alpha\n11377 latin small letter v with right hook\n11378 latin capital letter w with hook\n11379 latin small letter w with hook\n11380 latin small letter v with curl\n11381 latin capital letter half h\n11382 latin small letter half h\n11383 latin small letter tailless phi\n11384 latin small letter e with notch\n11385 latin small letter turned r with tail\n11386 latin small letter o with low ring inside\n11387 latin letter small capital turned e\n11388 latin subscript small letter j\n11389 modifier letter capital v\n11390 latin capital letter s with swash tail\n11391 latin capital letter z with swash tail\n11392 coptic capital letter alfa\n11393 coptic small letter alfa\n11394 coptic capital letter vida\n11395 coptic small letter vida\n11396 coptic capital letter gamma\n11397 coptic small letter gamma\n11398 coptic capital letter dalda\n11399 coptic small letter dalda\n11400 coptic capital letter eie\n11401 coptic small letter eie\n11402 coptic capital letter sou\n11403 coptic small letter sou\n11404 coptic capital letter zata\n11405 coptic small letter zata\n11406 coptic capital letter hate\n11407 coptic small letter hate\n11408 coptic capital letter thethe\n11409 coptic small letter thethe\n11410 coptic capital letter iauda\n11411 coptic small letter iauda\n11412 coptic capital letter kapa\n11413 coptic small letter kapa\n11414 coptic capital letter laula\n11415 coptic small letter laula\n11416 coptic capital letter mi\n11417 coptic small letter mi\n11418 coptic capital letter ni\n11419 coptic small letter ni\n11420 coptic capital letter ksi\n11421 coptic small letter ksi\n11422 coptic capital letter o\n11423 coptic small letter o\n11424 coptic capital letter pi\n11425 coptic small letter pi\n11426 coptic capital letter ro\n11427 coptic small letter ro\n11428 coptic capital letter sima\n11429 coptic small letter sima\n11430 coptic capital letter tau\n11431 coptic small letter tau\n11432 coptic capital letter ua\n11433 coptic small letter ua\n11434 coptic capital letter fi\n11435 coptic small letter fi\n11436 coptic capital letter khi\n11437 coptic small letter khi\n11438 coptic capital letter psi\n11439 coptic small letter psi\n11440 coptic capital letter oou\n11441 coptic small letter oou\n11442 coptic capital letter dialect-p alef\n11443 coptic small letter dialect-p alef\n11444 coptic capital letter old coptic ain\n11445 coptic small letter old coptic ain\n11446 coptic capital letter cryptogrammic eie\n11447 coptic small letter cryptogrammic eie\n11448 coptic capital letter dialect-p kapa\n11449 coptic small letter dialect-p kapa\n11450 coptic capital letter dialect-p ni\n11451 coptic small letter dialect-p ni\n11452 coptic capital letter cryptogrammic ni\n11453 coptic small letter cryptogrammic ni\n11454 coptic capital letter old coptic oou\n11455 coptic small letter old coptic oou\n11456 coptic capital letter sampi\n11457 coptic small letter sampi\n11458 coptic capital letter crossed shei\n11459 coptic small letter crossed shei\n11460 coptic capital letter old coptic shei\n11461 coptic small letter old coptic shei\n11462 coptic capital letter old coptic esh\n11463 coptic small letter old coptic esh\n11464 coptic capital letter akhmimic khei\n11465 coptic small letter akhmimic khei\n11466 coptic capital letter dialect-p hori\n11467 coptic small letter dialect-p hori\n11468 coptic capital letter old coptic hori\n11469 coptic small letter old coptic hori\n11470 coptic capital letter old coptic ha\n11471 coptic small letter old coptic ha\n11472 coptic capital letter l-shaped ha\n11473 coptic small letter l-shaped ha\n11474 coptic capital letter old coptic hei\n11475 coptic small letter old coptic hei\n11476 coptic capital letter old coptic hat\n11477 coptic small letter old coptic hat\n11478 coptic capital letter old coptic gangia\n11479 coptic small letter old coptic gangia\n11480 coptic capital letter old coptic dja\n11481 coptic small letter old coptic dja\n11482 coptic capital letter old coptic shima\n11483 coptic small letter old coptic shima\n11484 coptic capital letter old nubian shima\n11485 coptic small letter old nubian shima\n11486 coptic capital letter old nubian ngi\n11487 coptic small letter old nubian ngi\n11488 coptic capital letter old nubian nyi\n11489 coptic small letter old nubian nyi\n11490 coptic capital letter old nubian wau\n11491 coptic small letter old nubian wau\n11492 coptic symbol kai\n11493 coptic symbol mi ro\n11494 coptic symbol pi ro\n11495 coptic symbol stauros\n11496 coptic symbol tau ro\n11497 coptic symbol khi ro\n11498 coptic symbol shima sima\n11499 coptic capital letter cryptogrammic shei\n11500 coptic small letter cryptogrammic shei\n11501 coptic capital letter cryptogrammic gangia\n11502 coptic small letter cryptogrammic gangia\n11503 coptic combining ni above\n11504 coptic combining spiritus asper\n11505 coptic combining spiritus lenis\n11506 coptic capital letter bohairic khei\n11507 coptic small letter bohairic khei\n11513 coptic old nubian full stop\n11514 coptic old nubian direct question mark\n11515 coptic old nubian indirect question mark\n11516 coptic old nubian verse divider\n11517 coptic fraction one half\n11518 coptic full stop\n11519 coptic morphological divider\n11520 georgian small letter an\n11521 georgian small letter ban\n11522 georgian small letter gan\n11523 georgian small letter don\n11524 georgian small letter en\n11525 georgian small letter vin\n11526 georgian small letter zen\n11527 georgian small letter tan\n11528 georgian small letter in\n11529 georgian small letter kan\n11530 georgian small letter las\n11531 georgian small letter man\n11532 georgian small letter nar\n11533 georgian small letter on\n11534 georgian small letter par\n11535 georgian small letter zhar\n11536 georgian small letter rae\n11537 georgian small letter san\n11538 georgian small letter tar\n11539 georgian small letter un\n11540 georgian small letter phar\n11541 georgian small letter khar\n11542 georgian small letter ghan\n11543 georgian small letter qar\n11544 georgian small letter shin\n11545 georgian small letter chin\n11546 georgian small letter can\n11547 georgian small letter jil\n11548 georgian small letter cil\n11549 georgian small letter char\n11550 georgian small letter xan\n11551 georgian small letter jhan\n11552 georgian small letter hae\n11553 georgian small letter he\n11554 georgian small letter hie\n11555 georgian small letter we\n11556 georgian small letter har\n11557 georgian small letter hoe\n11559 georgian small letter yn\n11565 georgian small letter aen\n11568 tifinagh letter ya\n11569 tifinagh letter yab\n11570 tifinagh letter yabh\n11571 tifinagh letter yag\n11572 tifinagh letter yaghh\n11573 tifinagh letter berber academy yaj\n11574 tifinagh letter yaj\n11575 tifinagh letter yad\n11576 tifinagh letter yadh\n11577 tifinagh letter yadd\n11578 tifinagh letter yaddh\n11579 tifinagh letter yey\n11580 tifinagh letter yaf\n11581 tifinagh letter yak\n11582 tifinagh letter tuareg yak\n11583 tifinagh letter yakhh\n11584 tifinagh letter yah\n11585 tifinagh letter berber academy yah\n11586 tifinagh letter tuareg yah\n11587 tifinagh letter yahh\n11588 tifinagh letter yaa\n11589 tifinagh letter yakh\n11590 tifinagh letter tuareg yakh\n11591 tifinagh letter yaq\n11592 tifinagh letter tuareg yaq\n11593 tifinagh letter yi\n11594 tifinagh letter yazh\n11595 tifinagh letter ahaggar yazh\n11596 tifinagh letter tuareg yazh\n11597 tifinagh letter yal\n11598 tifinagh letter yam\n11599 tifinagh letter yan\n11600 tifinagh letter tuareg yagn\n11601 tifinagh letter tuareg yang\n11602 tifinagh letter yap\n11603 tifinagh letter yu\n11604 tifinagh letter yar\n11605 tifinagh letter yarr\n11606 tifinagh letter yagh\n11607 tifinagh letter tuareg yagh\n11608 tifinagh letter ayer yagh\n11609 tifinagh letter yas\n11610 tifinagh letter yass\n11611 tifinagh letter yash\n11612 tifinagh letter yat\n11613 tifinagh letter yath\n11614 tifinagh letter yach\n11615 tifinagh letter yatt\n11616 tifinagh letter yav\n11617 tifinagh letter yaw\n11618 tifinagh letter yay\n11619 tifinagh letter yaz\n11620 tifinagh letter tawellemet yaz\n11621 tifinagh letter yazz\n11622 tifinagh letter ye\n11623 tifinagh letter yo\n11631 tifinagh modifier letter labialization mark\n11632 tifinagh separator mark\n11647 tifinagh consonant joiner\n11648 ethiopic syllable loa\n11649 ethiopic syllable moa\n11650 ethiopic syllable roa\n11651 ethiopic syllable soa\n11652 ethiopic syllable shoa\n11653 ethiopic syllable boa\n11654 ethiopic syllable toa\n11655 ethiopic syllable coa\n11656 ethiopic syllable noa\n11657 ethiopic syllable nyoa\n11658 ethiopic syllable glottal oa\n11659 ethiopic syllable zoa\n11660 ethiopic syllable doa\n11661 ethiopic syllable ddoa\n11662 ethiopic syllable joa\n11663 ethiopic syllable thoa\n11664 ethiopic syllable choa\n11665 ethiopic syllable phoa\n11666 ethiopic syllable poa\n11667 ethiopic syllable ggwa\n11668 ethiopic syllable ggwi\n11669 ethiopic syllable ggwee\n11670 ethiopic syllable ggwe\n11680 ethiopic syllable ssa\n11681 ethiopic syllable ssu\n11682 ethiopic syllable ssi\n11683 ethiopic syllable ssaa\n11684 ethiopic syllable ssee\n11685 ethiopic syllable sse\n11686 ethiopic syllable sso\n11688 ethiopic syllable cca\n11689 ethiopic syllable ccu\n11690 ethiopic syllable cci\n11691 ethiopic syllable ccaa\n11692 ethiopic syllable ccee\n11693 ethiopic syllable cce\n11694 ethiopic syllable cco\n11696 ethiopic syllable zza\n11697 ethiopic syllable zzu\n11698 ethiopic syllable zzi\n11699 ethiopic syllable zzaa\n11700 ethiopic syllable zzee\n11701 ethiopic syllable zze\n11702 ethiopic syllable zzo\n11704 ethiopic syllable ccha\n11705 ethiopic syllable cchu\n11706 ethiopic syllable cchi\n11707 ethiopic syllable cchaa\n11708 ethiopic syllable cchee\n11709 ethiopic syllable cche\n11710 ethiopic syllable ccho\n11712 ethiopic syllable qya\n11713 ethiopic syllable qyu\n11714 ethiopic syllable qyi\n11715 ethiopic syllable qyaa\n11716 ethiopic syllable qyee\n11717 ethiopic syllable qye\n11718 ethiopic syllable qyo\n11720 ethiopic syllable kya\n11721 ethiopic syllable kyu\n11722 ethiopic syllable kyi\n11723 ethiopic syllable kyaa\n11724 ethiopic syllable kyee\n11725 ethiopic syllable kye\n11726 ethiopic syllable kyo\n11728 ethiopic syllable xya\n11729 ethiopic syllable xyu\n11730 ethiopic syllable xyi\n11731 ethiopic syllable xyaa\n11732 ethiopic syllable xyee\n11733 ethiopic syllable xye\n11734 ethiopic syllable xyo\n11736 ethiopic syllable gya\n11737 ethiopic syllable gyu\n11738 ethiopic syllable gyi\n11739 ethiopic syllable gyaa\n11740 ethiopic syllable gyee\n11741 ethiopic syllable gye\n11742 ethiopic syllable gyo\n11744 combining cyrillic letter be\n11745 combining cyrillic letter ve\n11746 combining cyrillic letter ghe\n11747 combining cyrillic letter de\n11748 combining cyrillic letter zhe\n11749 combining cyrillic letter ze\n11750 combining cyrillic letter ka\n11751 combining cyrillic letter el\n11752 combining cyrillic letter em\n11753 combining cyrillic letter en\n11754 combining cyrillic letter o\n11755 combining cyrillic letter pe\n11756 combining cyrillic letter er\n11757 combining cyrillic letter es\n11758 combining cyrillic letter te\n11759 combining cyrillic letter ha\n11760 combining cyrillic letter tse\n11761 combining cyrillic letter che\n11762 combining cyrillic letter sha\n11763 combining cyrillic letter shcha\n11764 combining cyrillic letter fita\n11765 combining cyrillic letter es-te\n11766 combining cyrillic letter a\n11767 combining cyrillic letter ie\n11768 combining cyrillic letter djerv\n11769 combining cyrillic letter monograph uk\n11770 combining cyrillic letter yat\n11771 combining cyrillic letter yu\n11772 combining cyrillic letter iotified a\n11773 combining cyrillic letter little yus\n11774 combining cyrillic letter big yus\n11775 combining cyrillic letter iotified big yus\n11776 right angle substitution marker\n11777 right angle dotted substitution marker\n11778 left substitution bracket\n11779 right substitution bracket\n11780 left dotted substitution bracket\n11781 right dotted substitution bracket\n11782 raised interpolation marker\n11783 raised dotted interpolation marker\n11784 dotted transposition marker\n11785 left transposition bracket\n11786 right transposition bracket\n11787 raised square\n11788 left raised omission bracket\n11789 right raised omission bracket\n11790 editorial coronis\n11791 paragraphos\n11792 forked paragraphos\n11793 reversed forked paragraphos\n11794 hypodiastole\n11795 dotted obelos\n11796 downwards ancora\n11797 upwards ancora\n11798 dotted right-pointing angle\n11799 double oblique hyphen\n11800 inverted interrobang\n11801 palm branch\n11802 hyphen with diaeresis\n11803 tilde with ring above\n11804 left low paraphrase bracket\n11805 right low paraphrase bracket\n11806 tilde with dot above\n11807 tilde with dot below\n11808 left vertical bar with quill\n11809 right vertical bar with quill\n11810 top left half bracket\n11811 top right half bracket\n11812 bottom left half bracket\n11813 bottom right half bracket\n11814 left sideways u bracket\n11815 right sideways u bracket\n11816 left double parenthesis\n11817 right double parenthesis\n11818 two dots over one dot punctuation\n11819 one dot over two dots punctuation\n11820 squared four dot punctuation\n11821 five dot mark\n11822 reversed question mark\n11823 vertical tilde\n11824 ring point\n11825 word separator middle dot\n11826 turned comma\n11827 raised dot\n11828 raised comma\n11829 turned semicolon\n11830 dagger with left guard\n11831 dagger with right guard\n11832 turned dagger\n11833 top half section sign\n11834 two-em dash\n11835 three-em dash\n11836 stenographic full stop\n11837 vertical six dots\n11838 wiggly vertical line\n11839 capitulum\n11840 double hyphen\n11841 reversed comma\n11842 double low-reversed-9 quotation mark\n11843 dash with left upturn\n11844 double suspension mark\n11845 inverted low kavyka\n11846 inverted low kavyka with kavyka above\n11847 low kavyka\n11848 low kavyka with dot\n11849 double stacked comma\n11850 dotted solidus\n11851 triple dagger\n11852 medieval comma\n11853 paragraphus mark\n11854 punctus elevatus mark\n11855 cornish verse divider\n11856 cross patty with right crossbar\n11857 cross patty with left crossbar\n11858 tironian sign capital et\n11859 medieval exclamation mark\n11860 medieval question mark\n11861 left square bracket with stroke\n11862 right square bracket with stroke\n11863 left square bracket with double stroke\n11864 right square bracket with double stroke\n11865 top half left parenthesis\n11866 top half right parenthesis\n11867 bottom half left parenthesis\n11868 bottom half right parenthesis\n11869 oblique hyphen\n11904 cjk radical repeat\n11905 cjk radical cliff\n11906 cjk radical second one\n11907 cjk radical second two\n11908 cjk radical second three\n11909 cjk radical person\n11910 cjk radical box\n11911 cjk radical table\n11912 cjk radical knife one\n11913 cjk radical knife two\n11914 cjk radical divination\n11915 cjk radical seal\n11916 cjk radical small one\n11917 cjk radical small two\n11918 cjk radical lame one\n11919 cjk radical lame two\n11920 cjk radical lame three\n11921 cjk radical lame four\n11922 cjk radical snake\n11923 cjk radical thread\n11924 cjk radical snout one\n11925 cjk radical snout two\n11926 cjk radical heart one\n11927 cjk radical heart two\n11928 cjk radical hand\n11929 cjk radical rap\n11931 cjk radical choke\n11932 cjk radical sun\n11933 cjk radical moon\n11934 cjk radical death\n11935 cjk radical mother\n11936 cjk radical civilian\n11937 cjk radical water one\n11938 cjk radical water two\n11939 cjk radical fire\n11940 cjk radical paw one\n11941 cjk radical paw two\n11942 cjk radical simplified half tree trunk\n11943 cjk radical cow\n11944 cjk radical dog\n11945 cjk radical jade\n11946 cjk radical bolt of cloth\n11947 cjk radical eye\n11948 cjk radical spirit one\n11949 cjk radical spirit two\n11950 cjk radical bamboo\n11951 cjk radical silk\n11952 cjk radical c-simplified silk\n11953 cjk radical net one\n11954 cjk radical net two\n11955 cjk radical net three\n11956 cjk radical net four\n11957 cjk radical mesh\n11958 cjk radical sheep\n11959 cjk radical ram\n11960 cjk radical ewe\n11961 cjk radical old\n11962 cjk radical brush one\n11963 cjk radical brush two\n11964 cjk radical meat\n11965 cjk radical mortar\n11966 cjk radical grass one\n11967 cjk radical grass two\n11968 cjk radical grass three\n11969 cjk radical tiger\n11970 cjk radical clothes\n11971 cjk radical west one\n11972 cjk radical west two\n11973 cjk radical c-simplified see\n11974 cjk radical simplified horn\n11975 cjk radical horn\n11976 cjk radical c-simplified speech\n11977 cjk radical c-simplified shell\n11978 cjk radical foot\n11979 cjk radical c-simplified cart\n11980 cjk radical simplified walk\n11981 cjk radical walk one\n11982 cjk radical walk two\n11983 cjk radical city\n11984 cjk radical c-simplified gold\n11985 cjk radical long one\n11986 cjk radical long two\n11987 cjk radical c-simplified long\n11988 cjk radical c-simplified gate\n11989 cjk radical mound one\n11990 cjk radical mound two\n11991 cjk radical rain\n11992 cjk radical blue\n11993 cjk radical c-simplified tanned leather\n11994 cjk radical c-simplified leaf\n11995 cjk radical c-simplified wind\n11996 cjk radical c-simplified fly\n11997 cjk radical eat one\n11998 cjk radical eat two\n11999 cjk radical eat three\n12000 cjk radical c-simplified eat\n12001 cjk radical head\n12002 cjk radical c-simplified horse\n12003 cjk radical bone\n12004 cjk radical ghost\n12005 cjk radical c-simplified fish\n12006 cjk radical c-simplified bird\n12007 cjk radical c-simplified salt\n12008 cjk radical simplified wheat\n12009 cjk radical simplified yellow\n12010 cjk radical c-simplified frog\n12011 cjk radical j-simplified even\n12012 cjk radical c-simplified even\n12013 cjk radical j-simplified tooth\n12014 cjk radical c-simplified tooth\n12015 cjk radical j-simplified dragon\n12016 cjk radical c-simplified dragon\n12017 cjk radical turtle\n12018 cjk radical j-simplified turtle\n12019 cjk radical c-simplified turtle\n12032 kangxi radical one\n12033 kangxi radical line\n12034 kangxi radical dot\n12035 kangxi radical slash\n12036 kangxi radical second\n12037 kangxi radical hook\n12038 kangxi radical two\n12039 kangxi radical lid\n12040 kangxi radical man\n12041 kangxi radical legs\n12042 kangxi radical enter\n12043 kangxi radical eight\n12044 kangxi radical down box\n12045 kangxi radical cover\n12046 kangxi radical ice\n12047 kangxi radical table\n12048 kangxi radical open box\n12049 kangxi radical knife\n12050 kangxi radical power\n12051 kangxi radical wrap\n12052 kangxi radical spoon\n12053 kangxi radical right open box\n12054 kangxi radical hiding enclosure\n12055 kangxi radical ten\n12056 kangxi radical divination\n12057 kangxi radical seal\n12058 kangxi radical cliff\n12059 kangxi radical private\n12060 kangxi radical again\n12061 kangxi radical mouth\n12062 kangxi radical enclosure\n12063 kangxi radical earth\n12064 kangxi radical scholar\n12065 kangxi radical go\n12066 kangxi radical go slowly\n12067 kangxi radical evening\n12068 kangxi radical big\n12069 kangxi radical woman\n12070 kangxi radical child\n12071 kangxi radical roof\n12072 kangxi radical inch\n12073 kangxi radical small\n12074 kangxi radical lame\n12075 kangxi radical corpse\n12076 kangxi radical sprout\n12077 kangxi radical mountain\n12078 kangxi radical river\n12079 kangxi radical work\n12080 kangxi radical oneself\n12081 kangxi radical turban\n12082 kangxi radical dry\n12083 kangxi radical short thread\n12084 kangxi radical dotted cliff\n12085 kangxi radical long stride\n12086 kangxi radical two hands\n12087 kangxi radical shoot\n12088 kangxi radical bow\n12089 kangxi radical snout\n12090 kangxi radical bristle\n12091 kangxi radical step\n12092 kangxi radical heart\n12093 kangxi radical halberd\n12094 kangxi radical door\n12095 kangxi radical hand\n12096 kangxi radical branch\n12097 kangxi radical rap\n12098 kangxi radical script\n12099 kangxi radical dipper\n12100 kangxi radical axe\n12101 kangxi radical square\n12102 kangxi radical not\n12103 kangxi radical sun\n12104 kangxi radical say\n12105 kangxi radical moon\n12106 kangxi radical tree\n12107 kangxi radical lack\n12108 kangxi radical stop\n12109 kangxi radical death\n12110 kangxi radical weapon\n12111 kangxi radical do not\n12112 kangxi radical compare\n12113 kangxi radical fur\n12114 kangxi radical clan\n12115 kangxi radical steam\n12116 kangxi radical water\n12117 kangxi radical fire\n12118 kangxi radical claw\n12119 kangxi radical father\n12120 kangxi radical double x\n12121 kangxi radical half tree trunk\n12122 kangxi radical slice\n12123 kangxi radical fang\n12124 kangxi radical cow\n12125 kangxi radical dog\n12126 kangxi radical profound\n12127 kangxi radical jade\n12128 kangxi radical melon\n12129 kangxi radical tile\n12130 kangxi radical sweet\n12131 kangxi radical life\n12132 kangxi radical use\n12133 kangxi radical field\n12134 kangxi radical bolt of cloth\n12135 kangxi radical sickness\n12136 kangxi radical dotted tent\n12137 kangxi radical white\n12138 kangxi radical skin\n12139 kangxi radical dish\n12140 kangxi radical eye\n12141 kangxi radical spear\n12142 kangxi radical arrow\n12143 kangxi radical stone\n12144 kangxi radical spirit\n12145 kangxi radical track\n12146 kangxi radical grain\n12147 kangxi radical cave\n12148 kangxi radical stand\n12149 kangxi radical bamboo\n12150 kangxi radical rice\n12151 kangxi radical silk\n12152 kangxi radical jar\n12153 kangxi radical net\n12154 kangxi radical sheep\n12155 kangxi radical feather\n12156 kangxi radical old\n12157 kangxi radical and\n12158 kangxi radical plow\n12159 kangxi radical ear\n12160 kangxi radical brush\n12161 kangxi radical meat\n12162 kangxi radical minister\n12163 kangxi radical self\n12164 kangxi radical arrive\n12165 kangxi radical mortar\n12166 kangxi radical tongue\n12167 kangxi radical oppose\n12168 kangxi radical boat\n12169 kangxi radical stopping\n12170 kangxi radical color\n12171 kangxi radical grass\n12172 kangxi radical tiger\n12173 kangxi radical insect\n12174 kangxi radical blood\n12175 kangxi radical walk enclosure\n12176 kangxi radical clothes\n12177 kangxi radical west\n12178 kangxi radical see\n12179 kangxi radical horn\n12180 kangxi radical speech\n12181 kangxi radical valley\n12182 kangxi radical bean\n12183 kangxi radical pig\n12184 kangxi radical badger\n12185 kangxi radical shell\n12186 kangxi radical red\n12187 kangxi radical run\n12188 kangxi radical foot\n12189 kangxi radical body\n12190 kangxi radical cart\n12191 kangxi radical bitter\n12192 kangxi radical morning\n12193 kangxi radical walk\n12194 kangxi radical city\n12195 kangxi radical wine\n12196 kangxi radical distinguish\n12197 kangxi radical village\n12198 kangxi radical gold\n12199 kangxi radical long\n12200 kangxi radical gate\n12201 kangxi radical mound\n12202 kangxi radical slave\n12203 kangxi radical short tailed bird\n12204 kangxi radical rain\n12205 kangxi radical blue\n12206 kangxi radical wrong\n12207 kangxi radical face\n12208 kangxi radical leather\n12209 kangxi radical tanned leather\n12210 kangxi radical leek\n12211 kangxi radical sound\n12212 kangxi radical leaf\n12213 kangxi radical wind\n12214 kangxi radical fly\n12215 kangxi radical eat\n12216 kangxi radical head\n12217 kangxi radical fragrant\n12218 kangxi radical horse\n12219 kangxi radical bone\n12220 kangxi radical tall\n12221 kangxi radical hair\n12222 kangxi radical fight\n12223 kangxi radical sacrificial wine\n12224 kangxi radical cauldron\n12225 kangxi radical ghost\n12226 kangxi radical fish\n12227 kangxi radical bird\n12228 kangxi radical salt\n12229 kangxi radical deer\n12230 kangxi radical wheat\n12231 kangxi radical hemp\n12232 kangxi radical yellow\n12233 kangxi radical millet\n12234 kangxi radical black\n12235 kangxi radical embroidery\n12236 kangxi radical frog\n12237 kangxi radical tripod\n12238 kangxi radical drum\n12239 kangxi radical rat\n12240 kangxi radical nose\n12241 kangxi radical even\n12242 kangxi radical tooth\n12243 kangxi radical dragon\n12244 kangxi radical turtle\n12245 kangxi radical flute\n12272 ideographic description character left to right\n12273 ideographic description character above to below\n12274 ideographic description character left to middle and right\n12275 ideographic description character above to middle and below\n12276 ideographic description character full surround\n12277 ideographic description character surround from above\n12278 ideographic description character surround from below\n12279 ideographic description character surround from left\n12280 ideographic description character surround from upper left\n12281 ideographic description character surround from upper right\n12282 ideographic description character surround from lower left\n12283 ideographic description character overlaid\n12284 ideographic description character surround from right\n12285 ideographic description character surround from lower right\n12286 ideographic description character horizontal reflection\n12287 ideographic description character rotation\n12288 ideographic space\n12289 ideographic comma\n12290 ideographic full stop\n12291 ditto mark\n12292 japanese industrial standard symbol\n12293 ideographic iteration mark\n12294 ideographic closing mark\n12295 ideographic number zero\n12296 left angle bracket\n12297 right angle bracket\n12298 left double angle bracket\n12299 right double angle bracket\n12300 left corner bracket\n12301 right corner bracket\n12302 left white corner bracket\n12303 right white corner bracket\n12304 left black lenticular bracket\n12305 right black lenticular bracket\n12306 postal mark\n12307 geta mark\n12308 left tortoise shell bracket\n12309 right tortoise shell bracket\n12310 left white lenticular bracket\n12311 right white lenticular bracket\n12312 left white tortoise shell bracket\n12313 right white tortoise shell bracket\n12314 left white square bracket\n12315 right white square bracket\n12316 wave dash\n12317 reversed double prime quotation mark\n12318 double prime quotation mark\n12319 low double prime quotation mark\n12320 postal mark face\n12321 hangzhou numeral one\n12322 hangzhou numeral two\n12323 hangzhou numeral three\n12324 hangzhou numeral four\n12325 hangzhou numeral five\n12326 hangzhou numeral six\n12327 hangzhou numeral seven\n12328 hangzhou numeral eight\n12329 hangzhou numeral nine\n12330 ideographic level tone mark\n12331 ideographic rising tone mark\n12332 ideographic departing tone mark\n12333 ideographic entering tone mark\n12334 hangul single dot tone mark\n12335 hangul double dot tone mark\n12336 wavy dash\n12337 vertical kana repeat mark\n12338 vertical kana repeat with voiced sound mark\n12339 vertical kana repeat mark upper half\n12340 vertical kana repeat with voiced sound mark upper half\n12341 vertical kana repeat mark lower half\n12342 circled postal mark\n12343 ideographic telegraph line feed separator symbol\n12344 hangzhou numeral ten\n12345 hangzhou numeral twenty\n12346 hangzhou numeral thirty\n12347 vertical ideographic iteration mark\n12348 masu mark\n12349 part alternation mark\n12350 ideographic variation indicator\n12351 ideographic half fill space\n12353 hiragana letter small a\n12354 hiragana letter a\n12355 hiragana letter small i\n12356 hiragana letter i\n12357 hiragana letter small u\n12358 hiragana letter u\n12359 hiragana letter small e\n12360 hiragana letter e\n12361 hiragana letter small o\n12362 hiragana letter o\n12363 hiragana letter ka\n12364 hiragana letter ga\n12365 hiragana letter ki\n12366 hiragana letter gi\n12367 hiragana letter ku\n12368 hiragana letter gu\n12369 hiragana letter ke\n12370 hiragana letter ge\n12371 hiragana letter ko\n12372 hiragana letter go\n12373 hiragana letter sa\n12374 hiragana letter za\n12375 hiragana letter si\n12376 hiragana letter zi\n12377 hiragana letter su\n12378 hiragana letter zu\n12379 hiragana letter se\n12380 hiragana letter ze\n12381 hiragana letter so\n12382 hiragana letter zo\n12383 hiragana letter ta\n12384 hiragana letter da\n12385 hiragana letter ti\n12386 hiragana letter di\n12387 hiragana letter small tu\n12388 hiragana letter tu\n12389 hiragana letter du\n12390 hiragana letter te\n12391 hiragana letter de\n12392 hiragana letter to\n12393 hiragana letter do\n12394 hiragana letter na\n12395 hiragana letter ni\n12396 hiragana letter nu\n12397 hiragana letter ne\n12398 hiragana letter no\n12399 hiragana letter ha\n12400 hiragana letter ba\n12401 hiragana letter pa\n12402 hiragana letter hi\n12403 hiragana letter bi\n12404 hiragana letter pi\n12405 hiragana letter hu\n12406 hiragana letter bu\n12407 hiragana letter pu\n12408 hiragana letter he\n12409 hiragana letter be\n12410 hiragana letter pe\n12411 hiragana letter ho\n12412 hiragana letter bo\n12413 hiragana letter po\n12414 hiragana letter ma\n12415 hiragana letter mi\n12416 hiragana letter mu\n12417 hiragana letter me\n12418 hiragana letter mo\n12419 hiragana letter small ya\n12420 hiragana letter ya\n12421 hiragana letter small yu\n12422 hiragana letter yu\n12423 hiragana letter small yo\n12424 hiragana letter yo\n12425 hiragana letter ra\n12426 hiragana letter ri\n12427 hiragana letter ru\n12428 hiragana letter re\n12429 hiragana letter ro\n12430 hiragana letter small wa\n12431 hiragana letter wa\n12432 hiragana letter wi\n12433 hiragana letter we\n12434 hiragana letter wo\n12435 hiragana letter n\n12436 hiragana letter vu\n12437 hiragana letter small ka\n12438 hiragana letter small ke\n12441 combining katakana-hiragana voiced sound mark\n12442 combining katakana-hiragana semi-voiced sound mark\n12443 katakana-hiragana voiced sound mark\n12444 katakana-hiragana semi-voiced sound mark\n12445 hiragana iteration mark\n12446 hiragana voiced iteration mark\n12447 hiragana digraph yori\n12448 katakana-hiragana double hyphen\n12449 katakana letter small a\n12450 katakana letter a\n12451 katakana letter small i\n12452 katakana letter i\n12453 katakana letter small u\n12454 katakana letter u\n12455 katakana letter small e\n12456 katakana letter e\n12457 katakana letter small o\n12458 katakana letter o\n12459 katakana letter ka\n12460 katakana letter ga\n12461 katakana letter ki\n12462 katakana letter gi\n12463 katakana letter ku\n12464 katakana letter gu\n12465 katakana letter ke\n12466 katakana letter ge\n12467 katakana letter ko\n12468 katakana letter go\n12469 katakana letter sa\n12470 katakana letter za\n12471 katakana letter si\n12472 katakana letter zi\n12473 katakana letter su\n12474 katakana letter zu\n12475 katakana letter se\n12476 katakana letter ze\n12477 katakana letter so\n12478 katakana letter zo\n12479 katakana letter ta\n12480 katakana letter da\n12481 katakana letter ti\n12482 katakana letter di\n12483 katakana letter small tu\n12484 katakana letter tu\n12485 katakana letter du\n12486 katakana letter te\n12487 katakana letter de\n12488 katakana letter to\n12489 katakana letter do\n12490 katakana letter na\n12491 katakana letter ni\n12492 katakana letter nu\n12493 katakana letter ne\n12494 katakana letter no\n12495 katakana letter ha\n12496 katakana letter ba\n12497 katakana letter pa\n12498 katakana letter hi\n12499 katakana letter bi\n12500 katakana letter pi\n12501 katakana letter hu\n12502 katakana letter bu\n12503 katakana letter pu\n12504 katakana letter he\n12505 katakana letter be\n12506 katakana letter pe\n12507 katakana letter ho\n12508 katakana letter bo\n12509 katakana letter po\n12510 katakana letter ma\n12511 katakana letter mi\n12512 katakana letter mu\n12513 katakana letter me\n12514 katakana letter mo\n12515 katakana letter small ya\n12516 katakana letter ya\n12517 katakana letter small yu\n12518 katakana letter yu\n12519 katakana letter small yo\n12520 katakana letter yo\n12521 katakana letter ra\n12522 katakana letter ri\n12523 katakana letter ru\n12524 katakana letter re\n12525 katakana letter ro\n12526 katakana letter small wa\n12527 katakana letter wa\n12528 katakana letter wi\n12529 katakana letter we\n12530 katakana letter wo\n12531 katakana letter n\n12532 katakana letter vu\n12533 katakana letter small ka\n12534 katakana letter small ke\n12535 katakana letter va\n12536 katakana letter vi\n12537 katakana letter ve\n12538 katakana letter vo\n12539 katakana middle dot\n12540 katakana-hiragana prolonged sound mark\n12541 katakana iteration mark\n12542 katakana voiced iteration mark\n12543 katakana digraph koto\n12549 bopomofo letter b\n12550 bopomofo letter p\n12551 bopomofo letter m\n12552 bopomofo letter f\n12553 bopomofo letter d\n12554 bopomofo letter t\n12555 bopomofo letter n\n12556 bopomofo letter l\n12557 bopomofo letter g\n12558 bopomofo letter k\n12559 bopomofo letter h\n12560 bopomofo letter j\n12561 bopomofo letter q\n12562 bopomofo letter x\n12563 bopomofo letter zh\n12564 bopomofo letter ch\n12565 bopomofo letter sh\n12566 bopomofo letter r\n12567 bopomofo letter z\n12568 bopomofo letter c\n12569 bopomofo letter s\n12570 bopomofo letter a\n12571 bopomofo letter o\n12572 bopomofo letter e\n12573 bopomofo letter eh\n12574 bopomofo letter ai\n12575 bopomofo letter ei\n12576 bopomofo letter au\n12577 bopomofo letter ou\n12578 bopomofo letter an\n12579 bopomofo letter en\n12580 bopomofo letter ang\n12581 bopomofo letter eng\n12582 bopomofo letter er\n12583 bopomofo letter i\n12584 bopomofo letter u\n12585 bopomofo letter iu\n12586 bopomofo letter v\n12587 bopomofo letter ng\n12588 bopomofo letter gn\n12589 bopomofo letter ih\n12590 bopomofo letter o with dot above\n12591 bopomofo letter nn\n12593 hangul letter kiyeok\n12594 hangul letter ssangkiyeok\n12595 hangul letter kiyeok-sios\n12596 hangul letter nieun\n12597 hangul letter nieun-cieuc\n12598 hangul letter nieun-hieuh\n12599 hangul letter tikeut\n12600 hangul letter ssangtikeut\n12601 hangul letter rieul\n12602 hangul letter rieul-kiyeok\n12603 hangul letter rieul-mieum\n12604 hangul letter rieul-pieup\n12605 hangul letter rieul-sios\n12606 hangul letter rieul-thieuth\n12607 hangul letter rieul-phieuph\n12608 hangul letter rieul-hieuh\n12609 hangul letter mieum\n12610 hangul letter pieup\n12611 hangul letter ssangpieup\n12612 hangul letter pieup-sios\n12613 hangul letter sios\n12614 hangul letter ssangsios\n12615 hangul letter ieung\n12616 hangul letter cieuc\n12617 hangul letter ssangcieuc\n12618 hangul letter chieuch\n12619 hangul letter khieukh\n12620 hangul letter thieuth\n12621 hangul letter phieuph\n12622 hangul letter hieuh\n12623 hangul letter a\n12624 hangul letter ae\n12625 hangul letter ya\n12626 hangul letter yae\n12627 hangul letter eo\n12628 hangul letter e\n12629 hangul letter yeo\n12630 hangul letter ye\n12631 hangul letter o\n12632 hangul letter wa\n12633 hangul letter wae\n12634 hangul letter oe\n12635 hangul letter yo\n12636 hangul letter u\n12637 hangul letter weo\n12638 hangul letter we\n12639 hangul letter wi\n12640 hangul letter yu\n12641 hangul letter eu\n12642 hangul letter yi\n12643 hangul letter i\n12644 hangul filler\n12645 hangul letter ssangnieun\n12646 hangul letter nieun-tikeut\n12647 hangul letter nieun-sios\n12648 hangul letter nieun-pansios\n12649 hangul letter rieul-kiyeok-sios\n12650 hangul letter rieul-tikeut\n12651 hangul letter rieul-pieup-sios\n12652 hangul letter rieul-pansios\n12653 hangul letter rieul-yeorinhieuh\n12654 hangul letter mieum-pieup\n12655 hangul letter mieum-sios\n12656 hangul letter mieum-pansios\n12657 hangul letter kapyeounmieum\n12658 hangul letter pieup-kiyeok\n12659 hangul letter pieup-tikeut\n12660 hangul letter pieup-sios-kiyeok\n12661 hangul letter pieup-sios-tikeut\n12662 hangul letter pieup-cieuc\n12663 hangul letter pieup-thieuth\n12664 hangul letter kapyeounpieup\n12665 hangul letter kapyeounssangpieup\n12666 hangul letter sios-kiyeok\n12667 hangul letter sios-nieun\n12668 hangul letter sios-tikeut\n12669 hangul letter sios-pieup\n12670 hangul letter sios-cieuc\n12671 hangul letter pansios\n12672 hangul letter ssangieung\n12673 hangul letter yesieung\n12674 hangul letter yesieung-sios\n12675 hangul letter yesieung-pansios\n12676 hangul letter kapyeounphieuph\n12677 hangul letter ssanghieuh\n12678 hangul letter yeorinhieuh\n12679 hangul letter yo-ya\n12680 hangul letter yo-yae\n12681 hangul letter yo-i\n12682 hangul letter yu-yeo\n12683 hangul letter yu-ye\n12684 hangul letter yu-i\n12685 hangul letter araea\n12686 hangul letter araeae\n12688 ideographic annotation linking mark\n12689 ideographic annotation reverse mark\n12690 ideographic annotation one mark\n12691 ideographic annotation two mark\n12692 ideographic annotation three mark\n12693 ideographic annotation four mark\n12694 ideographic annotation top mark\n12695 ideographic annotation middle mark\n12696 ideographic annotation bottom mark\n12697 ideographic annotation first mark\n12698 ideographic annotation second mark\n12699 ideographic annotation third mark\n12700 ideographic annotation fourth mark\n12701 ideographic annotation heaven mark\n12702 ideographic annotation earth mark\n12703 ideographic annotation man mark\n12704 bopomofo letter bu\n12705 bopomofo letter zi\n12706 bopomofo letter ji\n12707 bopomofo letter gu\n12708 bopomofo letter ee\n12709 bopomofo letter enn\n12710 bopomofo letter oo\n12711 bopomofo letter onn\n12712 bopomofo letter ir\n12713 bopomofo letter ann\n12714 bopomofo letter inn\n12715 bopomofo letter unn\n12716 bopomofo letter im\n12717 bopomofo letter ngg\n12718 bopomofo letter ainn\n12719 bopomofo letter aunn\n12720 bopomofo letter am\n12721 bopomofo letter om\n12722 bopomofo letter ong\n12723 bopomofo letter innn\n12724 bopomofo final letter p\n12725 bopomofo final letter t\n12726 bopomofo final letter k\n12727 bopomofo final letter h\n12728 bopomofo letter gh\n12729 bopomofo letter lh\n12730 bopomofo letter zy\n12731 bopomofo final letter g\n12732 bopomofo letter gw\n12733 bopomofo letter kw\n12734 bopomofo letter oe\n12735 bopomofo letter ah\n12736 cjk stroke t\n12737 cjk stroke wg\n12738 cjk stroke xg\n12739 cjk stroke bxg\n12740 cjk stroke sw\n12741 cjk stroke hzz\n12742 cjk stroke hzg\n12743 cjk stroke hp\n12744 cjk stroke hzwg\n12745 cjk stroke szwg\n12746 cjk stroke hzt\n12747 cjk stroke hzzp\n12748 cjk stroke hpwg\n12749 cjk stroke hzw\n12750 cjk stroke hzzz\n12751 cjk stroke n\n12752 cjk stroke h\n12753 cjk stroke s\n12754 cjk stroke p\n12755 cjk stroke sp\n12756 cjk stroke d\n12757 cjk stroke hz\n12758 cjk stroke hg\n12759 cjk stroke sz\n12760 cjk stroke swz\n12761 cjk stroke st\n12762 cjk stroke sg\n12763 cjk stroke pd\n12764 cjk stroke pz\n12765 cjk stroke tn\n12766 cjk stroke szz\n12767 cjk stroke swg\n12768 cjk stroke hxwg\n12769 cjk stroke hzzzg\n12770 cjk stroke pg\n12771 cjk stroke q\n12772 cjk stroke hxg\n12773 cjk stroke szp\n12783 ideographic description character subtraction\n12784 katakana letter small ku\n12785 katakana letter small si\n12786 katakana letter small su\n12787 katakana letter small to\n12788 katakana letter small nu\n12789 katakana letter small ha\n12790 katakana letter small hi\n12791 katakana letter small hu\n12792 katakana letter small he\n12793 katakana letter small ho\n12794 katakana letter small mu\n12795 katakana letter small ra\n12796 katakana letter small ri\n12797 katakana letter small ru\n12798 katakana letter small re\n12799 katakana letter small ro\n12800 parenthesized hangul kiyeok\n12801 parenthesized hangul nieun\n12802 parenthesized hangul tikeut\n12803 parenthesized hangul rieul\n12804 parenthesized hangul mieum\n12805 parenthesized hangul pieup\n12806 parenthesized hangul sios\n12807 parenthesized hangul ieung\n12808 parenthesized hangul cieuc\n12809 parenthesized hangul chieuch\n12810 parenthesized hangul khieukh\n12811 parenthesized hangul thieuth\n12812 parenthesized hangul phieuph\n12813 parenthesized hangul hieuh\n12814 parenthesized hangul kiyeok a\n12815 parenthesized hangul nieun a\n12816 parenthesized hangul tikeut a\n12817 parenthesized hangul rieul a\n12818 parenthesized hangul mieum a\n12819 parenthesized hangul pieup a\n12820 parenthesized hangul sios a\n12821 parenthesized hangul ieung a\n12822 parenthesized hangul cieuc a\n12823 parenthesized hangul chieuch a\n12824 parenthesized hangul khieukh a\n12825 parenthesized hangul thieuth a\n12826 parenthesized hangul phieuph a\n12827 parenthesized hangul hieuh a\n12828 parenthesized hangul cieuc u\n12829 parenthesized korean character ojeon\n12830 parenthesized korean character o hu\n12832 parenthesized ideograph one\n12833 parenthesized ideograph two\n12834 parenthesized ideograph three\n12835 parenthesized ideograph four\n12836 parenthesized ideograph five\n12837 parenthesized ideograph six\n12838 parenthesized ideograph seven\n12839 parenthesized ideograph eight\n12840 parenthesized ideograph nine\n12841 parenthesized ideograph ten\n12842 parenthesized ideograph moon\n12843 parenthesized ideograph fire\n12844 parenthesized ideograph water\n12845 parenthesized ideograph wood\n12846 parenthesized ideograph metal\n12847 parenthesized ideograph earth\n12848 parenthesized ideograph sun\n12849 parenthesized ideograph stock\n12850 parenthesized ideograph have\n12851 parenthesized ideograph society\n12852 parenthesized ideograph name\n12853 parenthesized ideograph special\n12854 parenthesized ideograph financial\n12855 parenthesized ideograph congratulation\n12856 parenthesized ideograph labor\n12857 parenthesized ideograph represent\n12858 parenthesized ideograph call\n12859 parenthesized ideograph study\n12860 parenthesized ideograph supervise\n12861 parenthesized ideograph enterprise\n12862 parenthesized ideograph resource\n12863 parenthesized ideograph alliance\n12864 parenthesized ideograph festival\n12865 parenthesized ideograph rest\n12866 parenthesized ideograph self\n12867 parenthesized ideograph reach\n12868 circled ideograph question\n12869 circled ideograph kindergarten\n12870 circled ideograph school\n12871 circled ideograph koto\n12872 circled number ten on black square\n12873 circled number twenty on black square\n12874 circled number thirty on black square\n12875 circled number forty on black square\n12876 circled number fifty on black square\n12877 circled number sixty on black square\n12878 circled number seventy on black square\n12879 circled number eighty on black square\n12880 partnership sign\n12881 circled number twenty one\n12882 circled number twenty two\n12883 circled number twenty three\n12884 circled number twenty four\n12885 circled number twenty five\n12886 circled number twenty six\n12887 circled number twenty seven\n12888 circled number twenty eight\n12889 circled number twenty nine\n12890 circled number thirty\n12891 circled number thirty one\n12892 circled number thirty two\n12893 circled number thirty three\n12894 circled number thirty four\n12895 circled number thirty five\n12896 circled hangul kiyeok\n12897 circled hangul nieun\n12898 circled hangul tikeut\n12899 circled hangul rieul\n12900 circled hangul mieum\n12901 circled hangul pieup\n12902 circled hangul sios\n12903 circled hangul ieung\n12904 circled hangul cieuc\n12905 circled hangul chieuch\n12906 circled hangul khieukh\n12907 circled hangul thieuth\n12908 circled hangul phieuph\n12909 circled hangul hieuh\n12910 circled hangul kiyeok a\n12911 circled hangul nieun a\n12912 circled hangul tikeut a\n12913 circled hangul rieul a\n12914 circled hangul mieum a\n12915 circled hangul pieup a\n12916 circled hangul sios a\n12917 circled hangul ieung a\n12918 circled hangul cieuc a\n12919 circled hangul chieuch a\n12920 circled hangul khieukh a\n12921 circled hangul thieuth a\n12922 circled hangul phieuph a\n12923 circled hangul hieuh a\n12924 circled korean character chamko\n12925 circled korean character jueui\n12926 circled hangul ieung u\n12927 korean standard symbol\n12928 circled ideograph one\n12929 circled ideograph two\n12930 circled ideograph three\n12931 circled ideograph four\n12932 circled ideograph five\n12933 circled ideograph six\n12934 circled ideograph seven\n12935 circled ideograph eight\n12936 circled ideograph nine\n12937 circled ideograph ten\n12938 circled ideograph moon\n12939 circled ideograph fire\n12940 circled ideograph water\n12941 circled ideograph wood\n12942 circled ideograph metal\n12943 circled ideograph earth\n12944 circled ideograph sun\n12945 circled ideograph stock\n12946 circled ideograph have\n12947 circled ideograph society\n12948 circled ideograph name\n12949 circled ideograph special\n12950 circled ideograph financial\n12951 circled ideograph congratulation\n12952 circled ideograph labor\n12953 circled ideograph secret\n12954 circled ideograph male\n12955 circled ideograph female\n12956 circled ideograph suitable\n12957 circled ideograph excellent\n12958 circled ideograph print\n12959 circled ideograph attention\n12960 circled ideograph item\n12961 circled ideograph rest\n12962 circled ideograph copy\n12963 circled ideograph correct\n12964 circled ideograph high\n12965 circled ideograph centre\n12966 circled ideograph low\n12967 circled ideograph left\n12968 circled ideograph right\n12969 circled ideograph medicine\n12970 circled ideograph religion\n12971 circled ideograph study\n12972 circled ideograph supervise\n12973 circled ideograph enterprise\n12974 circled ideograph resource\n12975 circled ideograph alliance\n12976 circled ideograph night\n12977 circled number thirty six\n12978 circled number thirty seven\n12979 circled number thirty eight\n12980 circled number thirty nine\n12981 circled number forty\n12982 circled number forty one\n12983 circled number forty two\n12984 circled number forty three\n12985 circled number forty four\n12986 circled number forty five\n12987 circled number forty six\n12988 circled number forty seven\n12989 circled number forty eight\n12990 circled number forty nine\n12991 circled number fifty\n12992 ideographic telegraph symbol for january\n12993 ideographic telegraph symbol for february\n12994 ideographic telegraph symbol for march\n12995 ideographic telegraph symbol for april\n12996 ideographic telegraph symbol for may\n12997 ideographic telegraph symbol for june\n12998 ideographic telegraph symbol for july\n12999 ideographic telegraph symbol for august\n13000 ideographic telegraph symbol for september\n13001 ideographic telegraph symbol for october\n13002 ideographic telegraph symbol for november\n13003 ideographic telegraph symbol for december\n13004 square hg\n13005 square erg\n13006 square ev\n13007 limited liability sign\n13008 circled katakana a\n13009 circled katakana i\n13010 circled katakana u\n13011 circled katakana e\n13012 circled katakana o\n13013 circled katakana ka\n13014 circled katakana ki\n13015 circled katakana ku\n13016 circled katakana ke\n13017 circled katakana ko\n13018 circled katakana sa\n13019 circled katakana si\n13020 circled katakana su\n13021 circled katakana se\n13022 circled katakana so\n13023 circled katakana ta\n13024 circled katakana ti\n13025 circled katakana tu\n13026 circled katakana te\n13027 circled katakana to\n13028 circled katakana na\n13029 circled katakana ni\n13030 circled katakana nu\n13031 circled katakana ne\n13032 circled katakana no\n13033 circled katakana ha\n13034 circled katakana hi\n13035 circled katakana hu\n13036 circled katakana he\n13037 circled katakana ho\n13038 circled katakana ma\n13039 circled katakana mi\n13040 circled katakana mu\n13041 circled katakana me\n13042 circled katakana mo\n13043 circled katakana ya\n13044 circled katakana yu\n13045 circled katakana yo\n13046 circled katakana ra\n13047 circled katakana ri\n13048 circled katakana ru\n13049 circled katakana re\n13050 circled katakana ro\n13051 circled katakana wa\n13052 circled katakana wi\n13053 circled katakana we\n13054 circled katakana wo\n13055 square era name reiwa\n13056 square apaato\n13057 square aruhua\n13058 square anpea\n13059 square aaru\n13060 square iningu\n13061 square inti\n13062 square uon\n13063 square esukuudo\n13064 square eekaa\n13065 square onsu\n13066 square oomu\n13067 square kairi\n13068 square karatto\n13069 square karorii\n13070 square garon\n13071 square ganma\n13072 square giga\n13073 square ginii\n13074 square kyurii\n13075 square girudaa\n13076 square kiro\n13077 square kiroguramu\n13078 square kiromeetoru\n13079 square kirowatto\n13080 square guramu\n13081 square guramuton\n13082 square kuruzeiro\n13083 square kuroone\n13084 square keesu\n13085 square koruna\n13086 square koopo\n13087 square saikuru\n13088 square santiimu\n13089 square siringu\n13090 square senti\n13091 square sento\n13092 square daasu\n13093 square desi\n13094 square doru\n13095 square ton\n13096 square nano\n13097 square notto\n13098 square haitu\n13099 square paasento\n13100 square paatu\n13101 square baareru\n13102 square piasutoru\n13103 square pikuru\n13104 square piko\n13105 square biru\n13106 square huaraddo\n13107 square huiito\n13108 square bussyeru\n13109 square huran\n13110 square hekutaaru\n13111 square peso\n13112 square penihi\n13113 square herutu\n13114 square pensu\n13115 square peezi\n13116 square beeta\n13117 square pointo\n13118 square boruto\n13119 square hon\n13120 square pondo\n13121 square hooru\n13122 square hoon\n13123 square maikuro\n13124 square mairu\n13125 square mahha\n13126 square maruku\n13127 square mansyon\n13128 square mikuron\n13129 square miri\n13130 square miribaaru\n13131 square mega\n13132 square megaton\n13133 square meetoru\n13134 square yaado\n13135 square yaaru\n13136 square yuan\n13137 square rittoru\n13138 square rira\n13139 square rupii\n13140 square ruuburu\n13141 square remu\n13142 square rentogen\n13143 square watto\n13144 ideographic telegraph symbol for hour zero\n13145 ideographic telegraph symbol for hour one\n13146 ideographic telegraph symbol for hour two\n13147 ideographic telegraph symbol for hour three\n13148 ideographic telegraph symbol for hour four\n13149 ideographic telegraph symbol for hour five\n13150 ideographic telegraph symbol for hour six\n13151 ideographic telegraph symbol for hour seven\n13152 ideographic telegraph symbol for hour eight\n13153 ideographic telegraph symbol for hour nine\n13154 ideographic telegraph symbol for hour ten\n13155 ideographic telegraph symbol for hour eleven\n13156 ideographic telegraph symbol for hour twelve\n13157 ideographic telegraph symbol for hour thirteen\n13158 ideographic telegraph symbol for hour fourteen\n13159 ideographic telegraph symbol for hour fifteen\n13160 ideographic telegraph symbol for hour sixteen\n13161 ideographic telegraph symbol for hour seventeen\n13162 ideographic telegraph symbol for hour eighteen\n13163 ideographic telegraph symbol for hour nineteen\n13164 ideographic telegraph symbol for hour twenty\n13165 ideographic telegraph symbol for hour twenty-one\n13166 ideographic telegraph symbol for hour twenty-two\n13167 ideographic telegraph symbol for hour twenty-three\n13168 ideographic telegraph symbol for hour twenty-four\n13169 square hpa\n13170 square da\n13171 square au\n13172 square bar\n13173 square ov\n13174 square pc\n13175 square dm\n13176 square dm squared\n13177 square dm cubed\n13178 square iu\n13179 square era name heisei\n13180 square era name syouwa\n13181 square era name taisyou\n13182 square era name meizi\n13183 square corporation\n13184 square pa amps\n13185 square na\n13186 square mu a\n13187 square ma\n13188 square ka\n13189 square kb\n13190 square mb\n13191 square gb\n13192 square cal\n13193 square kcal\n13194 square pf\n13195 square nf\n13196 square mu f\n13197 square mu g\n13198 square mg\n13199 square kg\n13200 square hz\n13201 square khz\n13202 square mhz\n13203 square ghz\n13204 square thz\n13205 square mu l\n13206 square ml\n13207 square dl\n13208 square kl\n13209 square fm\n13210 square nm\n13211 square mu m\n13212 square mm\n13213 square cm\n13214 square km\n13215 square mm squared\n13216 square cm squared\n13217 square m squared\n13218 square km squared\n13219 square mm cubed\n13220 square cm cubed\n13221 square m cubed\n13222 square km cubed\n13223 square m over s\n13224 square m over s squared\n13225 square pa\n13226 square kpa\n13227 square mpa\n13228 square gpa\n13229 square rad\n13230 square rad over s\n13231 square rad over s squared\n13232 square ps\n13233 square ns\n13234 square mu s\n13235 square ms\n13236 square pv\n13237 square nv\n13238 square mu v\n13239 square mv\n13240 square kv\n13241 square mv mega\n13242 square pw\n13243 square nw\n13244 square mu w\n13245 square mw\n13246 square kw\n13247 square mw mega\n13248 square k ohm\n13249 square m ohm\n13250 square am\n13251 square bq\n13252 square cc\n13253 square cd\n13254 square c over kg\n13255 square co\n13256 square db\n13257 square gy\n13258 square ha\n13259 square hp\n13260 square in\n13261 square kk\n13262 square km capital\n13263 square kt\n13264 square lm\n13265 square ln\n13266 square log\n13267 square lx\n13268 square mb small\n13269 square mil\n13270 square mol\n13271 square ph\n13272 square pm\n13273 square ppm\n13274 square pr\n13275 square sr\n13276 square sv\n13277 square wb\n13278 square v over m\n13279 square a over m\n13280 ideographic telegraph symbol for day one\n13281 ideographic telegraph symbol for day two\n13282 ideographic telegraph symbol for day three\n13283 ideographic telegraph symbol for day four\n13284 ideographic telegraph symbol for day five\n13285 ideographic telegraph symbol for day six\n13286 ideographic telegraph symbol for day seven\n13287 ideographic telegraph symbol for day eight\n13288 ideographic telegraph symbol for day nine\n13289 ideographic telegraph symbol for day ten\n13290 ideographic telegraph symbol for day eleven\n13291 ideographic telegraph symbol for day twelve\n13292 ideographic telegraph symbol for day thirteen\n13293 ideographic telegraph symbol for day fourteen\n13294 ideographic telegraph symbol for day fifteen\n13295 ideographic telegraph symbol for day sixteen\n13296 ideographic telegraph symbol for day seventeen\n13297 ideographic telegraph symbol for day eighteen\n13298 ideographic telegraph symbol for day nineteen\n13299 ideographic telegraph symbol for day twenty\n13300 ideographic telegraph symbol for day twenty-one\n13301 ideographic telegraph symbol for day twenty-two\n13302 ideographic telegraph symbol for day twenty-three\n13303 ideographic telegraph symbol for day twenty-four\n13304 ideographic telegraph symbol for day twenty-five\n13305 ideographic telegraph symbol for day twenty-six\n13306 ideographic telegraph symbol for day twenty-seven\n13307 ideographic telegraph symbol for day twenty-eight\n13308 ideographic telegraph symbol for day twenty-nine\n13309 ideographic telegraph symbol for day thirty\n13310 ideographic telegraph symbol for day thirty-one\n13311 square gal\n13312 <cjk ideograph extension a, first>\n19903 <cjk ideograph extension a, last>\n19904 hexagram for the creative heaven\n19905 hexagram for the receptive earth\n19906 hexagram for difficulty at the beginning\n19907 hexagram for youthful folly\n19908 hexagram for waiting\n19909 hexagram for conflict\n19910 hexagram for the army\n19911 hexagram for holding together\n19912 hexagram for small taming\n19913 hexagram for treading\n19914 hexagram for peace\n19915 hexagram for standstill\n19916 hexagram for fellowship\n19917 hexagram for great possession\n19918 hexagram for modesty\n19919 hexagram for enthusiasm\n19920 hexagram for following\n19921 hexagram for work on the decayed\n19922 hexagram for approach\n19923 hexagram for contemplation\n19924 hexagram for biting through\n19925 hexagram for grace\n19926 hexagram for splitting apart\n19927 hexagram for return\n19928 hexagram for innocence\n19929 hexagram for great taming\n19930 hexagram for mouth corners\n19931 hexagram for great preponderance\n19932 hexagram for the abysmal water\n19933 hexagram for the clinging fire\n19934 hexagram for influence\n19935 hexagram for duration\n19936 hexagram for retreat\n19937 hexagram for great power\n19938 hexagram for progress\n19939 hexagram for darkening of the light\n19940 hexagram for the family\n19941 hexagram for opposition\n19942 hexagram for obstruction\n19943 hexagram for deliverance\n19944 hexagram for decrease\n19945 hexagram for increase\n19946 hexagram for breakthrough\n19947 hexagram for coming to meet\n19948 hexagram for gathering together\n19949 hexagram for pushing upward\n19950 hexagram for oppression\n19951 hexagram for the well\n19952 hexagram for revolution\n19953 hexagram for the cauldron\n19954 hexagram for the arousing thunder\n19955 hexagram for the keeping still mountain\n19956 hexagram for development\n19957 hexagram for the marrying maiden\n19958 hexagram for abundance\n19959 hexagram for the wanderer\n19960 hexagram for the gentle wind\n19961 hexagram for the joyous lake\n19962 hexagram for dispersion\n19963 hexagram for limitation\n19964 hexagram for inner truth\n19965 hexagram for small preponderance\n19966 hexagram for after completion\n19967 hexagram for before completion\n19968 <cjk ideograph, first>\n40959 <cjk ideograph, last>\n40960 yi syllable it\n40961 yi syllable ix\n40962 yi syllable i\n40963 yi syllable ip\n40964 yi syllable iet\n40965 yi syllable iex\n40966 yi syllable ie\n40967 yi syllable iep\n40968 yi syllable at\n40969 yi syllable ax\n40970 yi syllable a\n40971 yi syllable ap\n40972 yi syllable uox\n40973 yi syllable uo\n40974 yi syllable uop\n40975 yi syllable ot\n40976 yi syllable ox\n40977 yi syllable o\n40978 yi syllable op\n40979 yi syllable ex\n40980 yi syllable e\n40981 yi syllable wu\n40982 yi syllable bit\n40983 yi syllable bix\n40984 yi syllable bi\n40985 yi syllable bip\n40986 yi syllable biet\n40987 yi syllable biex\n40988 yi syllable bie\n40989 yi syllable biep\n40990 yi syllable bat\n40991 yi syllable bax\n40992 yi syllable ba\n40993 yi syllable bap\n40994 yi syllable buox\n40995 yi syllable buo\n40996 yi syllable buop\n40997 yi syllable bot\n40998 yi syllable box\n40999 yi syllable bo\n41000 yi syllable bop\n41001 yi syllable bex\n41002 yi syllable be\n41003 yi syllable bep\n41004 yi syllable but\n41005 yi syllable bux\n41006 yi syllable bu\n41007 yi syllable bup\n41008 yi syllable burx\n41009 yi syllable bur\n41010 yi syllable byt\n41011 yi syllable byx\n41012 yi syllable by\n41013 yi syllable byp\n41014 yi syllable byrx\n41015 yi syllable byr\n41016 yi syllable pit\n41017 yi syllable pix\n41018 yi syllable pi\n41019 yi syllable pip\n41020 yi syllable piex\n41021 yi syllable pie\n41022 yi syllable piep\n41023 yi syllable pat\n41024 yi syllable pax\n41025 yi syllable pa\n41026 yi syllable pap\n41027 yi syllable puox\n41028 yi syllable puo\n41029 yi syllable puop\n41030 yi syllable pot\n41031 yi syllable pox\n41032 yi syllable po\n41033 yi syllable pop\n41034 yi syllable put\n41035 yi syllable pux\n41036 yi syllable pu\n41037 yi syllable pup\n41038 yi syllable purx\n41039 yi syllable pur\n41040 yi syllable pyt\n41041 yi syllable pyx\n41042 yi syllable py\n41043 yi syllable pyp\n41044 yi syllable pyrx\n41045 yi syllable pyr\n41046 yi syllable bbit\n41047 yi syllable bbix\n41048 yi syllable bbi\n41049 yi syllable bbip\n41050 yi syllable bbiet\n41051 yi syllable bbiex\n41052 yi syllable bbie\n41053 yi syllable bbiep\n41054 yi syllable bbat\n41055 yi syllable bbax\n41056 yi syllable bba\n41057 yi syllable bbap\n41058 yi syllable bbuox\n41059 yi syllable bbuo\n41060 yi syllable bbuop\n41061 yi syllable bbot\n41062 yi syllable bbox\n41063 yi syllable bbo\n41064 yi syllable bbop\n41065 yi syllable bbex\n41066 yi syllable bbe\n41067 yi syllable bbep\n41068 yi syllable bbut\n41069 yi syllable bbux\n41070 yi syllable bbu\n41071 yi syllable bbup\n41072 yi syllable bburx\n41073 yi syllable bbur\n41074 yi syllable bbyt\n41075 yi syllable bbyx\n41076 yi syllable bby\n41077 yi syllable bbyp\n41078 yi syllable nbit\n41079 yi syllable nbix\n41080 yi syllable nbi\n41081 yi syllable nbip\n41082 yi syllable nbiex\n41083 yi syllable nbie\n41084 yi syllable nbiep\n41085 yi syllable nbat\n41086 yi syllable nbax\n41087 yi syllable nba\n41088 yi syllable nbap\n41089 yi syllable nbot\n41090 yi syllable nbox\n41091 yi syllable nbo\n41092 yi syllable nbop\n41093 yi syllable nbut\n41094 yi syllable nbux\n41095 yi syllable nbu\n41096 yi syllable nbup\n41097 yi syllable nburx\n41098 yi syllable nbur\n41099 yi syllable nbyt\n41100 yi syllable nbyx\n41101 yi syllable nby\n41102 yi syllable nbyp\n41103 yi syllable nbyrx\n41104 yi syllable nbyr\n41105 yi syllable hmit\n41106 yi syllable hmix\n41107 yi syllable hmi\n41108 yi syllable hmip\n41109 yi syllable hmiex\n41110 yi syllable hmie\n41111 yi syllable hmiep\n41112 yi syllable hmat\n41113 yi syllable hmax\n41114 yi syllable hma\n41115 yi syllable hmap\n41116 yi syllable hmuox\n41117 yi syllable hmuo\n41118 yi syllable hmuop\n41119 yi syllable hmot\n41120 yi syllable hmox\n41121 yi syllable hmo\n41122 yi syllable hmop\n41123 yi syllable hmut\n41124 yi syllable hmux\n41125 yi syllable hmu\n41126 yi syllable hmup\n41127 yi syllable hmurx\n41128 yi syllable hmur\n41129 yi syllable hmyx\n41130 yi syllable hmy\n41131 yi syllable hmyp\n41132 yi syllable hmyrx\n41133 yi syllable hmyr\n41134 yi syllable mit\n41135 yi syllable mix\n41136 yi syllable mi\n41137 yi syllable mip\n41138 yi syllable miex\n41139 yi syllable mie\n41140 yi syllable miep\n41141 yi syllable mat\n41142 yi syllable max\n41143 yi syllable ma\n41144 yi syllable map\n41145 yi syllable muot\n41146 yi syllable muox\n41147 yi syllable muo\n41148 yi syllable muop\n41149 yi syllable mot\n41150 yi syllable mox\n41151 yi syllable mo\n41152 yi syllable mop\n41153 yi syllable mex\n41154 yi syllable me\n41155 yi syllable mut\n41156 yi syllable mux\n41157 yi syllable mu\n41158 yi syllable mup\n41159 yi syllable murx\n41160 yi syllable mur\n41161 yi syllable myt\n41162 yi syllable myx\n41163 yi syllable my\n41164 yi syllable myp\n41165 yi syllable fit\n41166 yi syllable fix\n41167 yi syllable fi\n41168 yi syllable fip\n41169 yi syllable fat\n41170 yi syllable fax\n41171 yi syllable fa\n41172 yi syllable fap\n41173 yi syllable fox\n41174 yi syllable fo\n41175 yi syllable fop\n41176 yi syllable fut\n41177 yi syllable fux\n41178 yi syllable fu\n41179 yi syllable fup\n41180 yi syllable furx\n41181 yi syllable fur\n41182 yi syllable fyt\n41183 yi syllable fyx\n41184 yi syllable fy\n41185 yi syllable fyp\n41186 yi syllable vit\n41187 yi syllable vix\n41188 yi syllable vi\n41189 yi syllable vip\n41190 yi syllable viet\n41191 yi syllable viex\n41192 yi syllable vie\n41193 yi syllable viep\n41194 yi syllable vat\n41195 yi syllable vax\n41196 yi syllable va\n41197 yi syllable vap\n41198 yi syllable vot\n41199 yi syllable vox\n41200 yi syllable vo\n41201 yi syllable vop\n41202 yi syllable vex\n41203 yi syllable vep\n41204 yi syllable vut\n41205 yi syllable vux\n41206 yi syllable vu\n41207 yi syllable vup\n41208 yi syllable vurx\n41209 yi syllable vur\n41210 yi syllable vyt\n41211 yi syllable vyx\n41212 yi syllable vy\n41213 yi syllable vyp\n41214 yi syllable vyrx\n41215 yi syllable vyr\n41216 yi syllable dit\n41217 yi syllable dix\n41218 yi syllable di\n41219 yi syllable dip\n41220 yi syllable diex\n41221 yi syllable die\n41222 yi syllable diep\n41223 yi syllable dat\n41224 yi syllable dax\n41225 yi syllable da\n41226 yi syllable dap\n41227 yi syllable duox\n41228 yi syllable duo\n41229 yi syllable dot\n41230 yi syllable dox\n41231 yi syllable do\n41232 yi syllable dop\n41233 yi syllable dex\n41234 yi syllable de\n41235 yi syllable dep\n41236 yi syllable dut\n41237 yi syllable dux\n41238 yi syllable du\n41239 yi syllable dup\n41240 yi syllable durx\n41241 yi syllable dur\n41242 yi syllable tit\n41243 yi syllable tix\n41244 yi syllable ti\n41245 yi syllable tip\n41246 yi syllable tiex\n41247 yi syllable tie\n41248 yi syllable tiep\n41249 yi syllable tat\n41250 yi syllable tax\n41251 yi syllable ta\n41252 yi syllable tap\n41253 yi syllable tuot\n41254 yi syllable tuox\n41255 yi syllable tuo\n41256 yi syllable tuop\n41257 yi syllable tot\n41258 yi syllable tox\n41259 yi syllable to\n41260 yi syllable top\n41261 yi syllable tex\n41262 yi syllable te\n41263 yi syllable tep\n41264 yi syllable tut\n41265 yi syllable tux\n41266 yi syllable tu\n41267 yi syllable tup\n41268 yi syllable turx\n41269 yi syllable tur\n41270 yi syllable ddit\n41271 yi syllable ddix\n41272 yi syllable ddi\n41273 yi syllable ddip\n41274 yi syllable ddiex\n41275 yi syllable ddie\n41276 yi syllable ddiep\n41277 yi syllable ddat\n41278 yi syllable ddax\n41279 yi syllable dda\n41280 yi syllable ddap\n41281 yi syllable dduox\n41282 yi syllable dduo\n41283 yi syllable dduop\n41284 yi syllable ddot\n41285 yi syllable ddox\n41286 yi syllable ddo\n41287 yi syllable ddop\n41288 yi syllable ddex\n41289 yi syllable dde\n41290 yi syllable ddep\n41291 yi syllable ddut\n41292 yi syllable ddux\n41293 yi syllable ddu\n41294 yi syllable ddup\n41295 yi syllable ddurx\n41296 yi syllable ddur\n41297 yi syllable ndit\n41298 yi syllable ndix\n41299 yi syllable ndi\n41300 yi syllable ndip\n41301 yi syllable ndiex\n41302 yi syllable ndie\n41303 yi syllable ndat\n41304 yi syllable ndax\n41305 yi syllable nda\n41306 yi syllable ndap\n41307 yi syllable ndot\n41308 yi syllable ndox\n41309 yi syllable ndo\n41310 yi syllable ndop\n41311 yi syllable ndex\n41312 yi syllable nde\n41313 yi syllable ndep\n41314 yi syllable ndut\n41315 yi syllable ndux\n41316 yi syllable ndu\n41317 yi syllable ndup\n41318 yi syllable ndurx\n41319 yi syllable ndur\n41320 yi syllable hnit\n41321 yi syllable hnix\n41322 yi syllable hni\n41323 yi syllable hnip\n41324 yi syllable hniet\n41325 yi syllable hniex\n41326 yi syllable hnie\n41327 yi syllable hniep\n41328 yi syllable hnat\n41329 yi syllable hnax\n41330 yi syllable hna\n41331 yi syllable hnap\n41332 yi syllable hnuox\n41333 yi syllable hnuo\n41334 yi syllable hnot\n41335 yi syllable hnox\n41336 yi syllable hnop\n41337 yi syllable hnex\n41338 yi syllable hne\n41339 yi syllable hnep\n41340 yi syllable hnut\n41341 yi syllable nit\n41342 yi syllable nix\n41343 yi syllable ni\n41344 yi syllable nip\n41345 yi syllable niex\n41346 yi syllable nie\n41347 yi syllable niep\n41348 yi syllable nax\n41349 yi syllable na\n41350 yi syllable nap\n41351 yi syllable nuox\n41352 yi syllable nuo\n41353 yi syllable nuop\n41354 yi syllable not\n41355 yi syllable nox\n41356 yi syllable no\n41357 yi syllable nop\n41358 yi syllable nex\n41359 yi syllable ne\n41360 yi syllable nep\n41361 yi syllable nut\n41362 yi syllable nux\n41363 yi syllable nu\n41364 yi syllable nup\n41365 yi syllable nurx\n41366 yi syllable nur\n41367 yi syllable hlit\n41368 yi syllable hlix\n41369 yi syllable hli\n41370 yi syllable hlip\n41371 yi syllable hliex\n41372 yi syllable hlie\n41373 yi syllable hliep\n41374 yi syllable hlat\n41375 yi syllable hlax\n41376 yi syllable hla\n41377 yi syllable hlap\n41378 yi syllable hluox\n41379 yi syllable hluo\n41380 yi syllable hluop\n41381 yi syllable hlox\n41382 yi syllable hlo\n41383 yi syllable hlop\n41384 yi syllable hlex\n41385 yi syllable hle\n41386 yi syllable hlep\n41387 yi syllable hlut\n41388 yi syllable hlux\n41389 yi syllable hlu\n41390 yi syllable hlup\n41391 yi syllable hlurx\n41392 yi syllable hlur\n41393 yi syllable hlyt\n41394 yi syllable hlyx\n41395 yi syllable hly\n41396 yi syllable hlyp\n41397 yi syllable hlyrx\n41398 yi syllable hlyr\n41399 yi syllable lit\n41400 yi syllable lix\n41401 yi syllable li\n41402 yi syllable lip\n41403 yi syllable liet\n41404 yi syllable liex\n41405 yi syllable lie\n41406 yi syllable liep\n41407 yi syllable lat\n41408 yi syllable lax\n41409 yi syllable la\n41410 yi syllable lap\n41411 yi syllable luot\n41412 yi syllable luox\n41413 yi syllable luo\n41414 yi syllable luop\n41415 yi syllable lot\n41416 yi syllable lox\n41417 yi syllable lo\n41418 yi syllable lop\n41419 yi syllable lex\n41420 yi syllable le\n41421 yi syllable lep\n41422 yi syllable lut\n41423 yi syllable lux\n41424 yi syllable lu\n41425 yi syllable lup\n41426 yi syllable lurx\n41427 yi syllable lur\n41428 yi syllable lyt\n41429 yi syllable lyx\n41430 yi syllable ly\n41431 yi syllable lyp\n41432 yi syllable lyrx\n41433 yi syllable lyr\n41434 yi syllable git\n41435 yi syllable gix\n41436 yi syllable gi\n41437 yi syllable gip\n41438 yi syllable giet\n41439 yi syllable giex\n41440 yi syllable gie\n41441 yi syllable giep\n41442 yi syllable gat\n41443 yi syllable gax\n41444 yi syllable ga\n41445 yi syllable gap\n41446 yi syllable guot\n41447 yi syllable guox\n41448 yi syllable guo\n41449 yi syllable guop\n41450 yi syllable got\n41451 yi syllable gox\n41452 yi syllable go\n41453 yi syllable gop\n41454 yi syllable get\n41455 yi syllable gex\n41456 yi syllable ge\n41457 yi syllable gep\n41458 yi syllable gut\n41459 yi syllable gux\n41460 yi syllable gu\n41461 yi syllable gup\n41462 yi syllable gurx\n41463 yi syllable gur\n41464 yi syllable kit\n41465 yi syllable kix\n41466 yi syllable ki\n41467 yi syllable kip\n41468 yi syllable kiex\n41469 yi syllable kie\n41470 yi syllable kiep\n41471 yi syllable kat\n41472 yi syllable kax\n41473 yi syllable ka\n41474 yi syllable kap\n41475 yi syllable kuox\n41476 yi syllable kuo\n41477 yi syllable kuop\n41478 yi syllable kot\n41479 yi syllable kox\n41480 yi syllable ko\n41481 yi syllable kop\n41482 yi syllable ket\n41483 yi syllable kex\n41484 yi syllable ke\n41485 yi syllable kep\n41486 yi syllable kut\n41487 yi syllable kux\n41488 yi syllable ku\n41489 yi syllable kup\n41490 yi syllable kurx\n41491 yi syllable kur\n41492 yi syllable ggit\n41493 yi syllable ggix\n41494 yi syllable ggi\n41495 yi syllable ggiex\n41496 yi syllable ggie\n41497 yi syllable ggiep\n41498 yi syllable ggat\n41499 yi syllable ggax\n41500 yi syllable gga\n41501 yi syllable ggap\n41502 yi syllable gguot\n41503 yi syllable gguox\n41504 yi syllable gguo\n41505 yi syllable gguop\n41506 yi syllable ggot\n41507 yi syllable ggox\n41508 yi syllable ggo\n41509 yi syllable ggop\n41510 yi syllable gget\n41511 yi syllable ggex\n41512 yi syllable gge\n41513 yi syllable ggep\n41514 yi syllable ggut\n41515 yi syllable ggux\n41516 yi syllable ggu\n41517 yi syllable ggup\n41518 yi syllable ggurx\n41519 yi syllable ggur\n41520 yi syllable mgiex\n41521 yi syllable mgie\n41522 yi syllable mgat\n41523 yi syllable mgax\n41524 yi syllable mga\n41525 yi syllable mgap\n41526 yi syllable mguox\n41527 yi syllable mguo\n41528 yi syllable mguop\n41529 yi syllable mgot\n41530 yi syllable mgox\n41531 yi syllable mgo\n41532 yi syllable mgop\n41533 yi syllable mgex\n41534 yi syllable mge\n41535 yi syllable mgep\n41536 yi syllable mgut\n41537 yi syllable mgux\n41538 yi syllable mgu\n41539 yi syllable mgup\n41540 yi syllable mgurx\n41541 yi syllable mgur\n41542 yi syllable hxit\n41543 yi syllable hxix\n41544 yi syllable hxi\n41545 yi syllable hxip\n41546 yi syllable hxiet\n41547 yi syllable hxiex\n41548 yi syllable hxie\n41549 yi syllable hxiep\n41550 yi syllable hxat\n41551 yi syllable hxax\n41552 yi syllable hxa\n41553 yi syllable hxap\n41554 yi syllable hxuot\n41555 yi syllable hxuox\n41556 yi syllable hxuo\n41557 yi syllable hxuop\n41558 yi syllable hxot\n41559 yi syllable hxox\n41560 yi syllable hxo\n41561 yi syllable hxop\n41562 yi syllable hxex\n41563 yi syllable hxe\n41564 yi syllable hxep\n41565 yi syllable ngiex\n41566 yi syllable ngie\n41567 yi syllable ngiep\n41568 yi syllable ngat\n41569 yi syllable ngax\n41570 yi syllable nga\n41571 yi syllable ngap\n41572 yi syllable nguot\n41573 yi syllable nguox\n41574 yi syllable nguo\n41575 yi syllable ngot\n41576 yi syllable ngox\n41577 yi syllable ngo\n41578 yi syllable ngop\n41579 yi syllable ngex\n41580 yi syllable nge\n41581 yi syllable ngep\n41582 yi syllable hit\n41583 yi syllable hiex\n41584 yi syllable hie\n41585 yi syllable hat\n41586 yi syllable hax\n41587 yi syllable ha\n41588 yi syllable hap\n41589 yi syllable huot\n41590 yi syllable huox\n41591 yi syllable huo\n41592 yi syllable huop\n41593 yi syllable hot\n41594 yi syllable hox\n41595 yi syllable ho\n41596 yi syllable hop\n41597 yi syllable hex\n41598 yi syllable he\n41599 yi syllable hep\n41600 yi syllable wat\n41601 yi syllable wax\n41602 yi syllable wa\n41603 yi syllable wap\n41604 yi syllable wuox\n41605 yi syllable wuo\n41606 yi syllable wuop\n41607 yi syllable wox\n41608 yi syllable wo\n41609 yi syllable wop\n41610 yi syllable wex\n41611 yi syllable we\n41612 yi syllable wep\n41613 yi syllable zit\n41614 yi syllable zix\n41615 yi syllable zi\n41616 yi syllable zip\n41617 yi syllable ziex\n41618 yi syllable zie\n41619 yi syllable ziep\n41620 yi syllable zat\n41621 yi syllable zax\n41622 yi syllable za\n41623 yi syllable zap\n41624 yi syllable zuox\n41625 yi syllable zuo\n41626 yi syllable zuop\n41627 yi syllable zot\n41628 yi syllable zox\n41629 yi syllable zo\n41630 yi syllable zop\n41631 yi syllable zex\n41632 yi syllable ze\n41633 yi syllable zep\n41634 yi syllable zut\n41635 yi syllable zux\n41636 yi syllable zu\n41637 yi syllable zup\n41638 yi syllable zurx\n41639 yi syllable zur\n41640 yi syllable zyt\n41641 yi syllable zyx\n41642 yi syllable zy\n41643 yi syllable zyp\n41644 yi syllable zyrx\n41645 yi syllable zyr\n41646 yi syllable cit\n41647 yi syllable cix\n41648 yi syllable ci\n41649 yi syllable cip\n41650 yi syllable ciet\n41651 yi syllable ciex\n41652 yi syllable cie\n41653 yi syllable ciep\n41654 yi syllable cat\n41655 yi syllable cax\n41656 yi syllable ca\n41657 yi syllable cap\n41658 yi syllable cuox\n41659 yi syllable cuo\n41660 yi syllable cuop\n41661 yi syllable cot\n41662 yi syllable cox\n41663 yi syllable co\n41664 yi syllable cop\n41665 yi syllable cex\n41666 yi syllable ce\n41667 yi syllable cep\n41668 yi syllable cut\n41669 yi syllable cux\n41670 yi syllable cu\n41671 yi syllable cup\n41672 yi syllable curx\n41673 yi syllable cur\n41674 yi syllable cyt\n41675 yi syllable cyx\n41676 yi syllable cy\n41677 yi syllable cyp\n41678 yi syllable cyrx\n41679 yi syllable cyr\n41680 yi syllable zzit\n41681 yi syllable zzix\n41682 yi syllable zzi\n41683 yi syllable zzip\n41684 yi syllable zziet\n41685 yi syllable zziex\n41686 yi syllable zzie\n41687 yi syllable zziep\n41688 yi syllable zzat\n41689 yi syllable zzax\n41690 yi syllable zza\n41691 yi syllable zzap\n41692 yi syllable zzox\n41693 yi syllable zzo\n41694 yi syllable zzop\n41695 yi syllable zzex\n41696 yi syllable zze\n41697 yi syllable zzep\n41698 yi syllable zzux\n41699 yi syllable zzu\n41700 yi syllable zzup\n41701 yi syllable zzurx\n41702 yi syllable zzur\n41703 yi syllable zzyt\n41704 yi syllable zzyx\n41705 yi syllable zzy\n41706 yi syllable zzyp\n41707 yi syllable zzyrx\n41708 yi syllable zzyr\n41709 yi syllable nzit\n41710 yi syllable nzix\n41711 yi syllable nzi\n41712 yi syllable nzip\n41713 yi syllable nziex\n41714 yi syllable nzie\n41715 yi syllable nziep\n41716 yi syllable nzat\n41717 yi syllable nzax\n41718 yi syllable nza\n41719 yi syllable nzap\n41720 yi syllable nzuox\n41721 yi syllable nzuo\n41722 yi syllable nzox\n41723 yi syllable nzop\n41724 yi syllable nzex\n41725 yi syllable nze\n41726 yi syllable nzux\n41727 yi syllable nzu\n41728 yi syllable nzup\n41729 yi syllable nzurx\n41730 yi syllable nzur\n41731 yi syllable nzyt\n41732 yi syllable nzyx\n41733 yi syllable nzy\n41734 yi syllable nzyp\n41735 yi syllable nzyrx\n41736 yi syllable nzyr\n41737 yi syllable sit\n41738 yi syllable six\n41739 yi syllable si\n41740 yi syllable sip\n41741 yi syllable siex\n41742 yi syllable sie\n41743 yi syllable siep\n41744 yi syllable sat\n41745 yi syllable sax\n41746 yi syllable sa\n41747 yi syllable sap\n41748 yi syllable suox\n41749 yi syllable suo\n41750 yi syllable suop\n41751 yi syllable sot\n41752 yi syllable sox\n41753 yi syllable so\n41754 yi syllable sop\n41755 yi syllable sex\n41756 yi syllable se\n41757 yi syllable sep\n41758 yi syllable sut\n41759 yi syllable sux\n41760 yi syllable su\n41761 yi syllable sup\n41762 yi syllable surx\n41763 yi syllable sur\n41764 yi syllable syt\n41765 yi syllable syx\n41766 yi syllable sy\n41767 yi syllable syp\n41768 yi syllable syrx\n41769 yi syllable syr\n41770 yi syllable ssit\n41771 yi syllable ssix\n41772 yi syllable ssi\n41773 yi syllable ssip\n41774 yi syllable ssiex\n41775 yi syllable ssie\n41776 yi syllable ssiep\n41777 yi syllable ssat\n41778 yi syllable ssax\n41779 yi syllable ssa\n41780 yi syllable ssap\n41781 yi syllable ssot\n41782 yi syllable ssox\n41783 yi syllable sso\n41784 yi syllable ssop\n41785 yi syllable ssex\n41786 yi syllable sse\n41787 yi syllable ssep\n41788 yi syllable ssut\n41789 yi syllable ssux\n41790 yi syllable ssu\n41791 yi syllable ssup\n41792 yi syllable ssyt\n41793 yi syllable ssyx\n41794 yi syllable ssy\n41795 yi syllable ssyp\n41796 yi syllable ssyrx\n41797 yi syllable ssyr\n41798 yi syllable zhat\n41799 yi syllable zhax\n41800 yi syllable zha\n41801 yi syllable zhap\n41802 yi syllable zhuox\n41803 yi syllable zhuo\n41804 yi syllable zhuop\n41805 yi syllable zhot\n41806 yi syllable zhox\n41807 yi syllable zho\n41808 yi syllable zhop\n41809 yi syllable zhet\n41810 yi syllable zhex\n41811 yi syllable zhe\n41812 yi syllable zhep\n41813 yi syllable zhut\n41814 yi syllable zhux\n41815 yi syllable zhu\n41816 yi syllable zhup\n41817 yi syllable zhurx\n41818 yi syllable zhur\n41819 yi syllable zhyt\n41820 yi syllable zhyx\n41821 yi syllable zhy\n41822 yi syllable zhyp\n41823 yi syllable zhyrx\n41824 yi syllable zhyr\n41825 yi syllable chat\n41826 yi syllable chax\n41827 yi syllable cha\n41828 yi syllable chap\n41829 yi syllable chuot\n41830 yi syllable chuox\n41831 yi syllable chuo\n41832 yi syllable chuop\n41833 yi syllable chot\n41834 yi syllable chox\n41835 yi syllable cho\n41836 yi syllable chop\n41837 yi syllable chet\n41838 yi syllable chex\n41839 yi syllable che\n41840 yi syllable chep\n41841 yi syllable chux\n41842 yi syllable chu\n41843 yi syllable chup\n41844 yi syllable churx\n41845 yi syllable chur\n41846 yi syllable chyt\n41847 yi syllable chyx\n41848 yi syllable chy\n41849 yi syllable chyp\n41850 yi syllable chyrx\n41851 yi syllable chyr\n41852 yi syllable rrax\n41853 yi syllable rra\n41854 yi syllable rruox\n41855 yi syllable rruo\n41856 yi syllable rrot\n41857 yi syllable rrox\n41858 yi syllable rro\n41859 yi syllable rrop\n41860 yi syllable rret\n41861 yi syllable rrex\n41862 yi syllable rre\n41863 yi syllable rrep\n41864 yi syllable rrut\n41865 yi syllable rrux\n41866 yi syllable rru\n41867 yi syllable rrup\n41868 yi syllable rrurx\n41869 yi syllable rrur\n41870 yi syllable rryt\n41871 yi syllable rryx\n41872 yi syllable rry\n41873 yi syllable rryp\n41874 yi syllable rryrx\n41875 yi syllable rryr\n41876 yi syllable nrat\n41877 yi syllable nrax\n41878 yi syllable nra\n41879 yi syllable nrap\n41880 yi syllable nrox\n41881 yi syllable nro\n41882 yi syllable nrop\n41883 yi syllable nret\n41884 yi syllable nrex\n41885 yi syllable nre\n41886 yi syllable nrep\n41887 yi syllable nrut\n41888 yi syllable nrux\n41889 yi syllable nru\n41890 yi syllable nrup\n41891 yi syllable nrurx\n41892 yi syllable nrur\n41893 yi syllable nryt\n41894 yi syllable nryx\n41895 yi syllable nry\n41896 yi syllable nryp\n41897 yi syllable nryrx\n41898 yi syllable nryr\n41899 yi syllable shat\n41900 yi syllable shax\n41901 yi syllable sha\n41902 yi syllable shap\n41903 yi syllable shuox\n41904 yi syllable shuo\n41905 yi syllable shuop\n41906 yi syllable shot\n41907 yi syllable shox\n41908 yi syllable sho\n41909 yi syllable shop\n41910 yi syllable shet\n41911 yi syllable shex\n41912 yi syllable she\n41913 yi syllable shep\n41914 yi syllable shut\n41915 yi syllable shux\n41916 yi syllable shu\n41917 yi syllable shup\n41918 yi syllable shurx\n41919 yi syllable shur\n41920 yi syllable shyt\n41921 yi syllable shyx\n41922 yi syllable shy\n41923 yi syllable shyp\n41924 yi syllable shyrx\n41925 yi syllable shyr\n41926 yi syllable rat\n41927 yi syllable rax\n41928 yi syllable ra\n41929 yi syllable rap\n41930 yi syllable ruox\n41931 yi syllable ruo\n41932 yi syllable ruop\n41933 yi syllable rot\n41934 yi syllable rox\n41935 yi syllable ro\n41936 yi syllable rop\n41937 yi syllable rex\n41938 yi syllable re\n41939 yi syllable rep\n41940 yi syllable rut\n41941 yi syllable rux\n41942 yi syllable ru\n41943 yi syllable rup\n41944 yi syllable rurx\n41945 yi syllable rur\n41946 yi syllable ryt\n41947 yi syllable ryx\n41948 yi syllable ry\n41949 yi syllable ryp\n41950 yi syllable ryrx\n41951 yi syllable ryr\n41952 yi syllable jit\n41953 yi syllable jix\n41954 yi syllable ji\n41955 yi syllable jip\n41956 yi syllable jiet\n41957 yi syllable jiex\n41958 yi syllable jie\n41959 yi syllable jiep\n41960 yi syllable juot\n41961 yi syllable juox\n41962 yi syllable juo\n41963 yi syllable juop\n41964 yi syllable jot\n41965 yi syllable jox\n41966 yi syllable jo\n41967 yi syllable jop\n41968 yi syllable jut\n41969 yi syllable jux\n41970 yi syllable ju\n41971 yi syllable jup\n41972 yi syllable jurx\n41973 yi syllable jur\n41974 yi syllable jyt\n41975 yi syllable jyx\n41976 yi syllable jy\n41977 yi syllable jyp\n41978 yi syllable jyrx\n41979 yi syllable jyr\n41980 yi syllable qit\n41981 yi syllable qix\n41982 yi syllable qi\n41983 yi syllable qip\n41984 yi syllable qiet\n41985 yi syllable qiex\n41986 yi syllable qie\n41987 yi syllable qiep\n41988 yi syllable quot\n41989 yi syllable quox\n41990 yi syllable quo\n41991 yi syllable quop\n41992 yi syllable qot\n41993 yi syllable qox\n41994 yi syllable qo\n41995 yi syllable qop\n41996 yi syllable qut\n41997 yi syllable qux\n41998 yi syllable qu\n41999 yi syllable qup\n42000 yi syllable qurx\n42001 yi syllable qur\n42002 yi syllable qyt\n42003 yi syllable qyx\n42004 yi syllable qy\n42005 yi syllable qyp\n42006 yi syllable qyrx\n42007 yi syllable qyr\n42008 yi syllable jjit\n42009 yi syllable jjix\n42010 yi syllable jji\n42011 yi syllable jjip\n42012 yi syllable jjiet\n42013 yi syllable jjiex\n42014 yi syllable jjie\n42015 yi syllable jjiep\n42016 yi syllable jjuox\n42017 yi syllable jjuo\n42018 yi syllable jjuop\n42019 yi syllable jjot\n42020 yi syllable jjox\n42021 yi syllable jjo\n42022 yi syllable jjop\n42023 yi syllable jjut\n42024 yi syllable jjux\n42025 yi syllable jju\n42026 yi syllable jjup\n42027 yi syllable jjurx\n42028 yi syllable jjur\n42029 yi syllable jjyt\n42030 yi syllable jjyx\n42031 yi syllable jjy\n42032 yi syllable jjyp\n42033 yi syllable njit\n42034 yi syllable njix\n42035 yi syllable nji\n42036 yi syllable njip\n42037 yi syllable njiet\n42038 yi syllable njiex\n42039 yi syllable njie\n42040 yi syllable njiep\n42041 yi syllable njuox\n42042 yi syllable njuo\n42043 yi syllable njot\n42044 yi syllable njox\n42045 yi syllable njo\n42046 yi syllable njop\n42047 yi syllable njux\n42048 yi syllable nju\n42049 yi syllable njup\n42050 yi syllable njurx\n42051 yi syllable njur\n42052 yi syllable njyt\n42053 yi syllable njyx\n42054 yi syllable njy\n42055 yi syllable njyp\n42056 yi syllable njyrx\n42057 yi syllable njyr\n42058 yi syllable nyit\n42059 yi syllable nyix\n42060 yi syllable nyi\n42061 yi syllable nyip\n42062 yi syllable nyiet\n42063 yi syllable nyiex\n42064 yi syllable nyie\n42065 yi syllable nyiep\n42066 yi syllable nyuox\n42067 yi syllable nyuo\n42068 yi syllable nyuop\n42069 yi syllable nyot\n42070 yi syllable nyox\n42071 yi syllable nyo\n42072 yi syllable nyop\n42073 yi syllable nyut\n42074 yi syllable nyux\n42075 yi syllable nyu\n42076 yi syllable nyup\n42077 yi syllable xit\n42078 yi syllable xix\n42079 yi syllable xi\n42080 yi syllable xip\n42081 yi syllable xiet\n42082 yi syllable xiex\n42083 yi syllable xie\n42084 yi syllable xiep\n42085 yi syllable xuox\n42086 yi syllable xuo\n42087 yi syllable xot\n42088 yi syllable xox\n42089 yi syllable xo\n42090 yi syllable xop\n42091 yi syllable xyt\n42092 yi syllable xyx\n42093 yi syllable xy\n42094 yi syllable xyp\n42095 yi syllable xyrx\n42096 yi syllable xyr\n42097 yi syllable yit\n42098 yi syllable yix\n42099 yi syllable yi\n42100 yi syllable yip\n42101 yi syllable yiet\n42102 yi syllable yiex\n42103 yi syllable yie\n42104 yi syllable yiep\n42105 yi syllable yuot\n42106 yi syllable yuox\n42107 yi syllable yuo\n42108 yi syllable yuop\n42109 yi syllable yot\n42110 yi syllable yox\n42111 yi syllable yo\n42112 yi syllable yop\n42113 yi syllable yut\n42114 yi syllable yux\n42115 yi syllable yu\n42116 yi syllable yup\n42117 yi syllable yurx\n42118 yi syllable yur\n42119 yi syllable yyt\n42120 yi syllable yyx\n42121 yi syllable yy\n42122 yi syllable yyp\n42123 yi syllable yyrx\n42124 yi syllable yyr\n42128 yi radical qot\n42129 yi radical li\n42130 yi radical kit\n42131 yi radical nyip\n42132 yi radical cyp\n42133 yi radical ssi\n42134 yi radical ggop\n42135 yi radical gep\n42136 yi radical mi\n42137 yi radical hxit\n42138 yi radical lyr\n42139 yi radical bbut\n42140 yi radical mop\n42141 yi radical yo\n42142 yi radical put\n42143 yi radical hxuo\n42144 yi radical tat\n42145 yi radical ga\n42146 yi radical zup\n42147 yi radical cyt\n42148 yi radical ddur\n42149 yi radical bur\n42150 yi radical gguo\n42151 yi radical nyop\n42152 yi radical tu\n42153 yi radical op\n42154 yi radical jjut\n42155 yi radical zot\n42156 yi radical pyt\n42157 yi radical hmo\n42158 yi radical yit\n42159 yi radical vur\n42160 yi radical shy\n42161 yi radical vep\n42162 yi radical za\n42163 yi radical jo\n42164 yi radical nzup\n42165 yi radical jjy\n42166 yi radical got\n42167 yi radical jjie\n42168 yi radical wo\n42169 yi radical du\n42170 yi radical shur\n42171 yi radical lie\n42172 yi radical cy\n42173 yi radical cuop\n42174 yi radical cip\n42175 yi radical hxop\n42176 yi radical shat\n42177 yi radical zur\n42178 yi radical shop\n42179 yi radical che\n42180 yi radical zziet\n42181 yi radical nbie\n42182 yi radical ke\n42192 lisu letter ba\n42193 lisu letter pa\n42194 lisu letter pha\n42195 lisu letter da\n42196 lisu letter ta\n42197 lisu letter tha\n42198 lisu letter ga\n42199 lisu letter ka\n42200 lisu letter kha\n42201 lisu letter ja\n42202 lisu letter ca\n42203 lisu letter cha\n42204 lisu letter dza\n42205 lisu letter tsa\n42206 lisu letter tsha\n42207 lisu letter ma\n42208 lisu letter na\n42209 lisu letter la\n42210 lisu letter sa\n42211 lisu letter zha\n42212 lisu letter za\n42213 lisu letter nga\n42214 lisu letter ha\n42215 lisu letter xa\n42216 lisu letter hha\n42217 lisu letter fa\n42218 lisu letter wa\n42219 lisu letter sha\n42220 lisu letter ya\n42221 lisu letter gha\n42222 lisu letter a\n42223 lisu letter ae\n42224 lisu letter e\n42225 lisu letter eu\n42226 lisu letter i\n42227 lisu letter o\n42228 lisu letter u\n42229 lisu letter ue\n42230 lisu letter uh\n42231 lisu letter oe\n42232 lisu letter tone mya ti\n42233 lisu letter tone na po\n42234 lisu letter tone mya cya\n42235 lisu letter tone mya bo\n42236 lisu letter tone mya na\n42237 lisu letter tone mya jeu\n42238 lisu punctuation comma\n42239 lisu punctuation full stop\n42240 vai syllable ee\n42241 vai syllable een\n42242 vai syllable hee\n42243 vai syllable wee\n42244 vai syllable ween\n42245 vai syllable pee\n42246 vai syllable bhee\n42247 vai syllable bee\n42248 vai syllable mbee\n42249 vai syllable kpee\n42250 vai syllable mgbee\n42251 vai syllable gbee\n42252 vai syllable fee\n42253 vai syllable vee\n42254 vai syllable tee\n42255 vai syllable thee\n42256 vai syllable dhee\n42257 vai syllable dhhee\n42258 vai syllable lee\n42259 vai syllable ree\n42260 vai syllable dee\n42261 vai syllable ndee\n42262 vai syllable see\n42263 vai syllable shee\n42264 vai syllable zee\n42265 vai syllable zhee\n42266 vai syllable cee\n42267 vai syllable jee\n42268 vai syllable njee\n42269 vai syllable yee\n42270 vai syllable kee\n42271 vai syllable nggee\n42272 vai syllable gee\n42273 vai syllable mee\n42274 vai syllable nee\n42275 vai syllable nyee\n42276 vai syllable i\n42277 vai syllable in\n42278 vai syllable hi\n42279 vai syllable hin\n42280 vai syllable wi\n42281 vai syllable win\n42282 vai syllable pi\n42283 vai syllable bhi\n42284 vai syllable bi\n42285 vai syllable mbi\n42286 vai syllable kpi\n42287 vai syllable mgbi\n42288 vai syllable gbi\n42289 vai syllable fi\n42290 vai syllable vi\n42291 vai syllable ti\n42292 vai syllable thi\n42293 vai syllable dhi\n42294 vai syllable dhhi\n42295 vai syllable li\n42296 vai syllable ri\n42297 vai syllable di\n42298 vai syllable ndi\n42299 vai syllable si\n42300 vai syllable shi\n42301 vai syllable zi\n42302 vai syllable zhi\n42303 vai syllable ci\n42304 vai syllable ji\n42305 vai syllable nji\n42306 vai syllable yi\n42307 vai syllable ki\n42308 vai syllable nggi\n42309 vai syllable gi\n42310 vai syllable mi\n42311 vai syllable ni\n42312 vai syllable nyi\n42313 vai syllable a\n42314 vai syllable an\n42315 vai syllable ngan\n42316 vai syllable ha\n42317 vai syllable han\n42318 vai syllable wa\n42319 vai syllable wan\n42320 vai syllable pa\n42321 vai syllable bha\n42322 vai syllable ba\n42323 vai syllable mba\n42324 vai syllable kpa\n42325 vai syllable kpan\n42326 vai syllable mgba\n42327 vai syllable gba\n42328 vai syllable fa\n42329 vai syllable va\n42330 vai syllable ta\n42331 vai syllable tha\n42332 vai syllable dha\n42333 vai syllable dhha\n42334 vai syllable la\n42335 vai syllable ra\n42336 vai syllable da\n42337 vai syllable nda\n42338 vai syllable sa\n42339 vai syllable sha\n42340 vai syllable za\n42341 vai syllable zha\n42342 vai syllable ca\n42343 vai syllable ja\n42344 vai syllable nja\n42345 vai syllable ya\n42346 vai syllable ka\n42347 vai syllable kan\n42348 vai syllable ngga\n42349 vai syllable ga\n42350 vai syllable ma\n42351 vai syllable na\n42352 vai syllable nya\n42353 vai syllable oo\n42354 vai syllable oon\n42355 vai syllable hoo\n42356 vai syllable woo\n42357 vai syllable woon\n42358 vai syllable poo\n42359 vai syllable bhoo\n42360 vai syllable boo\n42361 vai syllable mboo\n42362 vai syllable kpoo\n42363 vai syllable mgboo\n42364 vai syllable gboo\n42365 vai syllable foo\n42366 vai syllable voo\n42367 vai syllable too\n42368 vai syllable thoo\n42369 vai syllable dhoo\n42370 vai syllable dhhoo\n42371 vai syllable loo\n42372 vai syllable roo\n42373 vai syllable doo\n42374 vai syllable ndoo\n42375 vai syllable soo\n42376 vai syllable shoo\n42377 vai syllable zoo\n42378 vai syllable zhoo\n42379 vai syllable coo\n42380 vai syllable joo\n42381 vai syllable njoo\n42382 vai syllable yoo\n42383 vai syllable koo\n42384 vai syllable nggoo\n42385 vai syllable goo\n42386 vai syllable moo\n42387 vai syllable noo\n42388 vai syllable nyoo\n42389 vai syllable u\n42390 vai syllable un\n42391 vai syllable hu\n42392 vai syllable hun\n42393 vai syllable wu\n42394 vai syllable wun\n42395 vai syllable pu\n42396 vai syllable bhu\n42397 vai syllable bu\n42398 vai syllable mbu\n42399 vai syllable kpu\n42400 vai syllable mgbu\n42401 vai syllable gbu\n42402 vai syllable fu\n42403 vai syllable vu\n42404 vai syllable tu\n42405 vai syllable thu\n42406 vai syllable dhu\n42407 vai syllable dhhu\n42408 vai syllable lu\n42409 vai syllable ru\n42410 vai syllable du\n42411 vai syllable ndu\n42412 vai syllable su\n42413 vai syllable shu\n42414 vai syllable zu\n42415 vai syllable zhu\n42416 vai syllable cu\n42417 vai syllable ju\n42418 vai syllable nju\n42419 vai syllable yu\n42420 vai syllable ku\n42421 vai syllable nggu\n42422 vai syllable gu\n42423 vai syllable mu\n42424 vai syllable nu\n42425 vai syllable nyu\n42426 vai syllable o\n42427 vai syllable on\n42428 vai syllable ngon\n42429 vai syllable ho\n42430 vai syllable hon\n42431 vai syllable wo\n42432 vai syllable won\n42433 vai syllable po\n42434 vai syllable bho\n42435 vai syllable bo\n42436 vai syllable mbo\n42437 vai syllable kpo\n42438 vai syllable mgbo\n42439 vai syllable gbo\n42440 vai syllable gbon\n42441 vai syllable fo\n42442 vai syllable vo\n42443 vai syllable to\n42444 vai syllable tho\n42445 vai syllable dho\n42446 vai syllable dhho\n42447 vai syllable lo\n42448 vai syllable ro\n42449 vai syllable do\n42450 vai syllable ndo\n42451 vai syllable so\n42452 vai syllable sho\n42453 vai syllable zo\n42454 vai syllable zho\n42455 vai syllable co\n42456 vai syllable jo\n42457 vai syllable njo\n42458 vai syllable yo\n42459 vai syllable ko\n42460 vai syllable nggo\n42461 vai syllable go\n42462 vai syllable mo\n42463 vai syllable no\n42464 vai syllable nyo\n42465 vai syllable e\n42466 vai syllable en\n42467 vai syllable ngen\n42468 vai syllable he\n42469 vai syllable hen\n42470 vai syllable we\n42471 vai syllable wen\n42472 vai syllable pe\n42473 vai syllable bhe\n42474 vai syllable be\n42475 vai syllable mbe\n42476 vai syllable kpe\n42477 vai syllable kpen\n42478 vai syllable mgbe\n42479 vai syllable gbe\n42480 vai syllable gben\n42481 vai syllable fe\n42482 vai syllable ve\n42483 vai syllable te\n42484 vai syllable the\n42485 vai syllable dhe\n42486 vai syllable dhhe\n42487 vai syllable le\n42488 vai syllable re\n42489 vai syllable de\n42490 vai syllable nde\n42491 vai syllable se\n42492 vai syllable she\n42493 vai syllable ze\n42494 vai syllable zhe\n42495 vai syllable ce\n42496 vai syllable je\n42497 vai syllable nje\n42498 vai syllable ye\n42499 vai syllable ke\n42500 vai syllable ngge\n42501 vai syllable nggen\n42502 vai syllable ge\n42503 vai syllable gen\n42504 vai syllable me\n42505 vai syllable ne\n42506 vai syllable nye\n42507 vai syllable ng\n42508 vai syllable lengthener\n42509 vai comma\n42510 vai full stop\n42511 vai question mark\n42512 vai syllable ndole fa\n42513 vai syllable ndole ka\n42514 vai syllable ndole soo\n42515 vai symbol feeng\n42516 vai symbol keeng\n42517 vai symbol ting\n42518 vai symbol nii\n42519 vai symbol bang\n42520 vai symbol faa\n42521 vai symbol taa\n42522 vai symbol dang\n42523 vai symbol doong\n42524 vai symbol kung\n42525 vai symbol tong\n42526 vai symbol do-o\n42527 vai symbol jong\n42528 vai digit zero\n42529 vai digit one\n42530 vai digit two\n42531 vai digit three\n42532 vai digit four\n42533 vai digit five\n42534 vai digit six\n42535 vai digit seven\n42536 vai digit eight\n42537 vai digit nine\n42538 vai syllable ndole ma\n42539 vai syllable ndole do\n42560 cyrillic capital letter zemlya\n42561 cyrillic small letter zemlya\n42562 cyrillic capital letter dzelo\n42563 cyrillic small letter dzelo\n42564 cyrillic capital letter reversed dze\n42565 cyrillic small letter reversed dze\n42566 cyrillic capital letter iota\n42567 cyrillic small letter iota\n42568 cyrillic capital letter djerv\n42569 cyrillic small letter djerv\n42570 cyrillic capital letter monograph uk\n42571 cyrillic small letter monograph uk\n42572 cyrillic capital letter broad omega\n42573 cyrillic small letter broad omega\n42574 cyrillic capital letter neutral yer\n42575 cyrillic small letter neutral yer\n42576 cyrillic capital letter yeru with back yer\n42577 cyrillic small letter yeru with back yer\n42578 cyrillic capital letter iotified yat\n42579 cyrillic small letter iotified yat\n42580 cyrillic capital letter reversed yu\n42581 cyrillic small letter reversed yu\n42582 cyrillic capital letter iotified a\n42583 cyrillic small letter iotified a\n42584 cyrillic capital letter closed little yus\n42585 cyrillic small letter closed little yus\n42586 cyrillic capital letter blended yus\n42587 cyrillic small letter blended yus\n42588 cyrillic capital letter iotified closed little yus\n42589 cyrillic small letter iotified closed little yus\n42590 cyrillic capital letter yn\n42591 cyrillic small letter yn\n42592 cyrillic capital letter reversed tse\n42593 cyrillic small letter reversed tse\n42594 cyrillic capital letter soft de\n42595 cyrillic small letter soft de\n42596 cyrillic capital letter soft el\n42597 cyrillic small letter soft el\n42598 cyrillic capital letter soft em\n42599 cyrillic small letter soft em\n42600 cyrillic capital letter monocular o\n42601 cyrillic small letter monocular o\n42602 cyrillic capital letter binocular o\n42603 cyrillic small letter binocular o\n42604 cyrillic capital letter double monocular o\n42605 cyrillic small letter double monocular o\n42606 cyrillic letter multiocular o\n42607 combining cyrillic vzmet\n42608 combining cyrillic ten millions sign\n42609 combining cyrillic hundred millions sign\n42610 combining cyrillic thousand millions sign\n42611 slavonic asterisk\n42612 combining cyrillic letter ukrainian ie\n42613 combining cyrillic letter i\n42614 combining cyrillic letter yi\n42615 combining cyrillic letter u\n42616 combining cyrillic letter hard sign\n42617 combining cyrillic letter yeru\n42618 combining cyrillic letter soft sign\n42619 combining cyrillic letter omega\n42620 combining cyrillic kavyka\n42621 combining cyrillic payerok\n42622 cyrillic kavyka\n42623 cyrillic payerok\n42624 cyrillic capital letter dwe\n42625 cyrillic small letter dwe\n42626 cyrillic capital letter dzwe\n42627 cyrillic small letter dzwe\n42628 cyrillic capital letter zhwe\n42629 cyrillic small letter zhwe\n42630 cyrillic capital letter cche\n42631 cyrillic small letter cche\n42632 cyrillic capital letter dzze\n42633 cyrillic small letter dzze\n42634 cyrillic capital letter te with middle hook\n42635 cyrillic small letter te with middle hook\n42636 cyrillic capital letter twe\n42637 cyrillic small letter twe\n42638 cyrillic capital letter tswe\n42639 cyrillic small letter tswe\n42640 cyrillic capital letter tsse\n42641 cyrillic small letter tsse\n42642 cyrillic capital letter tche\n42643 cyrillic small letter tche\n42644 cyrillic capital letter hwe\n42645 cyrillic small letter hwe\n42646 cyrillic capital letter shwe\n42647 cyrillic small letter shwe\n42648 cyrillic capital letter double o\n42649 cyrillic small letter double o\n42650 cyrillic capital letter crossed o\n42651 cyrillic small letter crossed o\n42652 modifier letter cyrillic hard sign\n42653 modifier letter cyrillic soft sign\n42654 combining cyrillic letter ef\n42655 combining cyrillic letter iotified e\n42656 bamum letter a\n42657 bamum letter ka\n42658 bamum letter u\n42659 bamum letter ku\n42660 bamum letter ee\n42661 bamum letter ree\n42662 bamum letter tae\n42663 bamum letter o\n42664 bamum letter nyi\n42665 bamum letter i\n42666 bamum letter la\n42667 bamum letter pa\n42668 bamum letter rii\n42669 bamum letter riee\n42670 bamum letter leeee\n42671 bamum letter meeee\n42672 bamum letter taa\n42673 bamum letter ndaa\n42674 bamum letter njaem\n42675 bamum letter m\n42676 bamum letter suu\n42677 bamum letter mu\n42678 bamum letter shii\n42679 bamum letter si\n42680 bamum letter sheux\n42681 bamum letter seux\n42682 bamum letter kyee\n42683 bamum letter ket\n42684 bamum letter nuae\n42685 bamum letter nu\n42686 bamum letter njuae\n42687 bamum letter yoq\n42688 bamum letter shu\n42689 bamum letter yuq\n42690 bamum letter ya\n42691 bamum letter nsha\n42692 bamum letter keux\n42693 bamum letter peux\n42694 bamum letter njee\n42695 bamum letter ntee\n42696 bamum letter pue\n42697 bamum letter wue\n42698 bamum letter pee\n42699 bamum letter fee\n42700 bamum letter ru\n42701 bamum letter lu\n42702 bamum letter mi\n42703 bamum letter ni\n42704 bamum letter reux\n42705 bamum letter rae\n42706 bamum letter ken\n42707 bamum letter ngkwaen\n42708 bamum letter ngga\n42709 bamum letter nga\n42710 bamum letter sho\n42711 bamum letter puae\n42712 bamum letter fu\n42713 bamum letter fom\n42714 bamum letter wa\n42715 bamum letter na\n42716 bamum letter li\n42717 bamum letter pi\n42718 bamum letter loq\n42719 bamum letter ko\n42720 bamum letter mben\n42721 bamum letter ren\n42722 bamum letter men\n42723 bamum letter ma\n42724 bamum letter ti\n42725 bamum letter ki\n42726 bamum letter mo\n42727 bamum letter mbaa\n42728 bamum letter tet\n42729 bamum letter kpa\n42730 bamum letter ten\n42731 bamum letter ntuu\n42732 bamum letter samba\n42733 bamum letter faamae\n42734 bamum letter kovuu\n42735 bamum letter koghom\n42736 bamum combining mark koqndon\n42737 bamum combining mark tukwentis\n42738 bamum njaemli\n42739 bamum full stop\n42740 bamum colon\n42741 bamum comma\n42742 bamum semicolon\n42743 bamum question mark\n42752 modifier letter chinese tone yin ping\n42753 modifier letter chinese tone yang ping\n42754 modifier letter chinese tone yin shang\n42755 modifier letter chinese tone yang shang\n42756 modifier letter chinese tone yin qu\n42757 modifier letter chinese tone yang qu\n42758 modifier letter chinese tone yin ru\n42759 modifier letter chinese tone yang ru\n42760 modifier letter extra-high dotted tone bar\n42761 modifier letter high dotted tone bar\n42762 modifier letter mid dotted tone bar\n42763 modifier letter low dotted tone bar\n42764 modifier letter extra-low dotted tone bar\n42765 modifier letter extra-high dotted left-stem tone bar\n42766 modifier letter high dotted left-stem tone bar\n42767 modifier letter mid dotted left-stem tone bar\n42768 modifier letter low dotted left-stem tone bar\n42769 modifier letter extra-low dotted left-stem tone bar\n42770 modifier letter extra-high left-stem tone bar\n42771 modifier letter high left-stem tone bar\n42772 modifier letter mid left-stem tone bar\n42773 modifier letter low left-stem tone bar\n42774 modifier letter extra-low left-stem tone bar\n42775 modifier letter dot vertical bar\n42776 modifier letter dot slash\n42777 modifier letter dot horizontal bar\n42778 modifier letter lower right corner angle\n42779 modifier letter raised up arrow\n42780 modifier letter raised down arrow\n42781 modifier letter raised exclamation mark\n42782 modifier letter raised inverted exclamation mark\n42783 modifier letter low inverted exclamation mark\n42784 modifier letter stress and high tone\n42785 modifier letter stress and low tone\n42786 latin capital letter egyptological alef\n42787 latin small letter egyptological alef\n42788 latin capital letter egyptological ain\n42789 latin small letter egyptological ain\n42790 latin capital letter heng\n42791 latin small letter heng\n42792 latin capital letter tz\n42793 latin small letter tz\n42794 latin capital letter tresillo\n42795 latin small letter tresillo\n42796 latin capital letter cuatrillo\n42797 latin small letter cuatrillo\n42798 latin capital letter cuatrillo with comma\n42799 latin small letter cuatrillo with comma\n42800 latin letter small capital f\n42801 latin letter small capital s\n42802 latin capital letter aa\n42803 latin small letter aa\n42804 latin capital letter ao\n42805 latin small letter ao\n42806 latin capital letter au\n42807 latin small letter au\n42808 latin capital letter av\n42809 latin small letter av\n42810 latin capital letter av with horizontal bar\n42811 latin small letter av with horizontal bar\n42812 latin capital letter ay\n42813 latin small letter ay\n42814 latin capital letter reversed c with dot\n42815 latin small letter reversed c with dot\n42816 latin capital letter k with stroke\n42817 latin small letter k with stroke\n42818 latin capital letter k with diagonal stroke\n42819 latin small letter k with diagonal stroke\n42820 latin capital letter k with stroke and diagonal stroke\n42821 latin small letter k with stroke and diagonal stroke\n42822 latin capital letter broken l\n42823 latin small letter broken l\n42824 latin capital letter l with high stroke\n42825 latin small letter l with high stroke\n42826 latin capital letter o with long stroke overlay\n42827 latin small letter o with long stroke overlay\n42828 latin capital letter o with loop\n42829 latin small letter o with loop\n42830 latin capital letter oo\n42831 latin small letter oo\n42832 latin capital letter p with stroke through descender\n42833 latin small letter p with stroke through descender\n42834 latin capital letter p with flourish\n42835 latin small letter p with flourish\n42836 latin capital letter p with squirrel tail\n42837 latin small letter p with squirrel tail\n42838 latin capital letter q with stroke through descender\n42839 latin small letter q with stroke through descender\n42840 latin capital letter q with diagonal stroke\n42841 latin small letter q with diagonal stroke\n42842 latin capital letter r rotunda\n42843 latin small letter r rotunda\n42844 latin capital letter rum rotunda\n42845 latin small letter rum rotunda\n42846 latin capital letter v with diagonal stroke\n42847 latin small letter v with diagonal stroke\n42848 latin capital letter vy\n42849 latin small letter vy\n42850 latin capital letter visigothic z\n42851 latin small letter visigothic z\n42852 latin capital letter thorn with stroke\n42853 latin small letter thorn with stroke\n42854 latin capital letter thorn with stroke through descender\n42855 latin small letter thorn with stroke through descender\n42856 latin capital letter vend\n42857 latin small letter vend\n42858 latin capital letter et\n42859 latin small letter et\n42860 latin capital letter is\n42861 latin small letter is\n42862 latin capital letter con\n42863 latin small letter con\n42864 modifier letter us\n42865 latin small letter dum\n42866 latin small letter lum\n42867 latin small letter mum\n42868 latin small letter num\n42869 latin small letter rum\n42870 latin letter small capital rum\n42871 latin small letter tum\n42872 latin small letter um\n42873 latin capital letter insular d\n42874 latin small letter insular d\n42875 latin capital letter insular f\n42876 latin small letter insular f\n42877 latin capital letter insular g\n42878 latin capital letter turned insular g\n42879 latin small letter turned insular g\n42880 latin capital letter turned l\n42881 latin small letter turned l\n42882 latin capital letter insular r\n42883 latin small letter insular r\n42884 latin capital letter insular s\n42885 latin small letter insular s\n42886 latin capital letter insular t\n42887 latin small letter insular t\n42888 modifier letter low circumflex accent\n42889 modifier letter colon\n42890 modifier letter short equals sign\n42891 latin capital letter saltillo\n42892 latin small letter saltillo\n42893 latin capital letter turned h\n42894 latin small letter l with retroflex hook and belt\n42895 latin letter sinological dot\n42896 latin capital letter n with descender\n42897 latin small letter n with descender\n42898 latin capital letter c with bar\n42899 latin small letter c with bar\n42900 latin small letter c with palatal hook\n42901 latin small letter h with palatal hook\n42902 latin capital letter b with flourish\n42903 latin small letter b with flourish\n42904 latin capital letter f with stroke\n42905 latin small letter f with stroke\n42906 latin capital letter volapuk ae\n42907 latin small letter volapuk ae\n42908 latin capital letter volapuk oe\n42909 latin small letter volapuk oe\n42910 latin capital letter volapuk ue\n42911 latin small letter volapuk ue\n42912 latin capital letter g with oblique stroke\n42913 latin small letter g with oblique stroke\n42914 latin capital letter k with oblique stroke\n42915 latin small letter k with oblique stroke\n42916 latin capital letter n with oblique stroke\n42917 latin small letter n with oblique stroke\n42918 latin capital letter r with oblique stroke\n42919 latin small letter r with oblique stroke\n42920 latin capital letter s with oblique stroke\n42921 latin small letter s with oblique stroke\n42922 latin capital letter h with hook\n42923 latin capital letter reversed open e\n42924 latin capital letter script g\n42925 latin capital letter l with belt\n42926 latin capital letter small capital i\n42927 latin letter small capital q\n42928 latin capital letter turned k\n42929 latin capital letter turned t\n42930 latin capital letter j with crossed-tail\n42931 latin capital letter chi\n42932 latin capital letter beta\n42933 latin small letter beta\n42934 latin capital letter omega\n42935 latin small letter omega\n42936 latin capital letter u with stroke\n42937 latin small letter u with stroke\n42938 latin capital letter glottal a\n42939 latin small letter glottal a\n42940 latin capital letter glottal i\n42941 latin small letter glottal i\n42942 latin capital letter glottal u\n42943 latin small letter glottal u\n42944 latin capital letter old polish o\n42945 latin small letter old polish o\n42946 latin capital letter anglicana w\n42947 latin small letter anglicana w\n42948 latin capital letter c with palatal hook\n42949 latin capital letter s with hook\n42950 latin capital letter z with palatal hook\n42951 latin capital letter d with short stroke overlay\n42952 latin small letter d with short stroke overlay\n42953 latin capital letter s with short stroke overlay\n42954 latin small letter s with short stroke overlay\n42955 latin capital letter rams horn\n42956 latin capital letter s with diagonal stroke\n42957 latin small letter s with diagonal stroke\n42958 latin capital letter pharyngeal voiced fricative\n42959 latin small letter pharyngeal voiced fricative\n42960 latin capital letter closed insular g\n42961 latin small letter closed insular g\n42962 latin capital letter double thorn\n42963 latin small letter double thorn\n42964 latin capital letter double wynn\n42965 latin small letter double wynn\n42966 latin capital letter middle scots s\n42967 latin small letter middle scots s\n42968 latin capital letter sigmoid s\n42969 latin small letter sigmoid s\n42970 latin capital letter lambda\tlamda\n42971 latin small letter lambda\tlamda\n42972 latin capital letter lambda with stroke\tlamda\n42993 modifier letter capital s\n42994 modifier letter capital c\n42995 modifier letter capital f\n42996 modifier letter capital q\n42997 latin capital letter reversed half h\n42998 latin small letter reversed half h\n42999 latin epigraphic letter sideways i\n43000 modifier letter capital h with stroke\n43001 modifier letter small ligature oe\n43002 latin letter small capital turned m\n43003 latin epigraphic letter reversed f\n43004 latin epigraphic letter reversed p\n43005 latin epigraphic letter inverted m\n43006 latin epigraphic letter i longa\n43007 latin epigraphic letter archaic m\n43008 syloti nagri letter a\n43009 syloti nagri letter i\n43010 syloti nagri sign dvisvara\n43011 syloti nagri letter u\n43012 syloti nagri letter e\n43013 syloti nagri letter o\n43014 syloti nagri sign hasanta\n43015 syloti nagri letter ko\n43016 syloti nagri letter kho\n43017 syloti nagri letter go\n43018 syloti nagri letter gho\n43019 syloti nagri sign anusvara\n43020 syloti nagri letter co\n43021 syloti nagri letter cho\n43022 syloti nagri letter jo\n43023 syloti nagri letter jho\n43024 syloti nagri letter tto\n43025 syloti nagri letter ttho\n43026 syloti nagri letter ddo\n43027 syloti nagri letter ddho\n43028 syloti nagri letter to\n43029 syloti nagri letter tho\n43030 syloti nagri letter do\n43031 syloti nagri letter dho\n43032 syloti nagri letter no\n43033 syloti nagri letter po\n43034 syloti nagri letter pho\n43035 syloti nagri letter bo\n43036 syloti nagri letter bho\n43037 syloti nagri letter mo\n43038 syloti nagri letter ro\n43039 syloti nagri letter lo\n43040 syloti nagri letter rro\n43041 syloti nagri letter so\n43042 syloti nagri letter ho\n43043 syloti nagri vowel sign a\n43044 syloti nagri vowel sign i\n43045 syloti nagri vowel sign u\n43046 syloti nagri vowel sign e\n43047 syloti nagri vowel sign oo\n43048 syloti nagri poetry mark-1\n43049 syloti nagri poetry mark-2\n43050 syloti nagri poetry mark-3\n43051 syloti nagri poetry mark-4\n43052 syloti nagri sign alternate hasanta\n43056 north indic fraction one quarter\n43057 north indic fraction one half\n43058 north indic fraction three quarters\n43059 north indic fraction one sixteenth\n43060 north indic fraction one eighth\n43061 north indic fraction three sixteenths\n43062 north indic quarter mark\n43063 north indic placeholder mark\n43064 north indic rupee mark\n43065 north indic quantity mark\n43072 phags-pa letter ka\n43073 phags-pa letter kha\n43074 phags-pa letter ga\n43075 phags-pa letter nga\n43076 phags-pa letter ca\n43077 phags-pa letter cha\n43078 phags-pa letter ja\n43079 phags-pa letter nya\n43080 phags-pa letter ta\n43081 phags-pa letter tha\n43082 phags-pa letter da\n43083 phags-pa letter na\n43084 phags-pa letter pa\n43085 phags-pa letter pha\n43086 phags-pa letter ba\n43087 phags-pa letter ma\n43088 phags-pa letter tsa\n43089 phags-pa letter tsha\n43090 phags-pa letter dza\n43091 phags-pa letter wa\n43092 phags-pa letter zha\n43093 phags-pa letter za\n43094 phags-pa letter small a\n43095 phags-pa letter ya\n43096 phags-pa letter ra\n43097 phags-pa letter la\n43098 phags-pa letter sha\n43099 phags-pa letter sa\n43100 phags-pa letter ha\n43101 phags-pa letter a\n43102 phags-pa letter i\n43103 phags-pa letter u\n43104 phags-pa letter e\n43105 phags-pa letter o\n43106 phags-pa letter qa\n43107 phags-pa letter xa\n43108 phags-pa letter fa\n43109 phags-pa letter gga\n43110 phags-pa letter ee\n43111 phags-pa subjoined letter wa\n43112 phags-pa subjoined letter ya\n43113 phags-pa letter tta\n43114 phags-pa letter ttha\n43115 phags-pa letter dda\n43116 phags-pa letter nna\n43117 phags-pa letter alternate ya\n43118 phags-pa letter voiceless sha\n43119 phags-pa letter voiced ha\n43120 phags-pa letter aspirated fa\n43121 phags-pa subjoined letter ra\n43122 phags-pa superfixed letter ra\n43123 phags-pa letter candrabindu\n43124 phags-pa single head mark\n43125 phags-pa double head mark\n43126 phags-pa mark shad\n43127 phags-pa mark double shad\n43136 saurashtra sign anusvara\n43137 saurashtra sign visarga\n43138 saurashtra letter a\n43139 saurashtra letter aa\n43140 saurashtra letter i\n43141 saurashtra letter ii\n43142 saurashtra letter u\n43143 saurashtra letter uu\n43144 saurashtra letter vocalic r\n43145 saurashtra letter vocalic rr\n43146 saurashtra letter vocalic l\n43147 saurashtra letter vocalic ll\n43148 saurashtra letter e\n43149 saurashtra letter ee\n43150 saurashtra letter ai\n43151 saurashtra letter o\n43152 saurashtra letter oo\n43153 saurashtra letter au\n43154 saurashtra letter ka\n43155 saurashtra letter kha\n43156 saurashtra letter ga\n43157 saurashtra letter gha\n43158 saurashtra letter nga\n43159 saurashtra letter ca\n43160 saurashtra letter cha\n43161 saurashtra letter ja\n43162 saurashtra letter jha\n43163 saurashtra letter nya\n43164 saurashtra letter tta\n43165 saurashtra letter ttha\n43166 saurashtra letter dda\n43167 saurashtra letter ddha\n43168 saurashtra letter nna\n43169 saurashtra letter ta\n43170 saurashtra letter tha\n43171 saurashtra letter da\n43172 saurashtra letter dha\n43173 saurashtra letter na\n43174 saurashtra letter pa\n43175 saurashtra letter pha\n43176 saurashtra letter ba\n43177 saurashtra letter bha\n43178 saurashtra letter ma\n43179 saurashtra letter ya\n43180 saurashtra letter ra\n43181 saurashtra letter la\n43182 saurashtra letter va\n43183 saurashtra letter sha\n43184 saurashtra letter ssa\n43185 saurashtra letter sa\n43186 saurashtra letter ha\n43187 saurashtra letter lla\n43188 saurashtra consonant sign haaru\n43189 saurashtra vowel sign aa\n43190 saurashtra vowel sign i\n43191 saurashtra vowel sign ii\n43192 saurashtra vowel sign u\n43193 saurashtra vowel sign uu\n43194 saurashtra vowel sign vocalic r\n43195 saurashtra vowel sign vocalic rr\n43196 saurashtra vowel sign vocalic l\n43197 saurashtra vowel sign vocalic ll\n43198 saurashtra vowel sign e\n43199 saurashtra vowel sign ee\n43200 saurashtra vowel sign ai\n43201 saurashtra vowel sign o\n43202 saurashtra vowel sign oo\n43203 saurashtra vowel sign au\n43204 saurashtra sign virama\n43205 saurashtra sign candrabindu\n43214 saurashtra danda\n43215 saurashtra double danda\n43216 saurashtra digit zero\n43217 saurashtra digit one\n43218 saurashtra digit two\n43219 saurashtra digit three\n43220 saurashtra digit four\n43221 saurashtra digit five\n43222 saurashtra digit six\n43223 saurashtra digit seven\n43224 saurashtra digit eight\n43225 saurashtra digit nine\n43232 combining devanagari digit zero\n43233 combining devanagari digit one\n43234 combining devanagari digit two\n43235 combining devanagari digit three\n43236 combining devanagari digit four\n43237 combining devanagari digit five\n43238 combining devanagari digit six\n43239 combining devanagari digit seven\n43240 combining devanagari digit eight\n43241 combining devanagari digit nine\n43242 combining devanagari letter a\n43243 combining devanagari letter u\n43244 combining devanagari letter ka\n43245 combining devanagari letter na\n43246 combining devanagari letter pa\n43247 combining devanagari letter ra\n43248 combining devanagari letter vi\n43249 combining devanagari sign avagraha\n43250 devanagari sign spacing candrabindu\n43251 devanagari sign candrabindu virama\n43252 devanagari sign double candrabindu virama\n43253 devanagari sign candrabindu two\n43254 devanagari sign candrabindu three\n43255 devanagari sign candrabindu avagraha\n43256 devanagari sign pushpika\n43257 devanagari gap filler\n43258 devanagari caret\n43259 devanagari headstroke\n43260 devanagari sign siddham\n43261 devanagari jain om\n43262 devanagari letter ay\n43263 devanagari vowel sign ay\n43264 kayah li digit zero\n43265 kayah li digit one\n43266 kayah li digit two\n43267 kayah li digit three\n43268 kayah li digit four\n43269 kayah li digit five\n43270 kayah li digit six\n43271 kayah li digit seven\n43272 kayah li digit eight\n43273 kayah li digit nine\n43274 kayah li letter ka\n43275 kayah li letter kha\n43276 kayah li letter ga\n43277 kayah li letter nga\n43278 kayah li letter sa\n43279 kayah li letter sha\n43280 kayah li letter za\n43281 kayah li letter nya\n43282 kayah li letter ta\n43283 kayah li letter hta\n43284 kayah li letter na\n43285 kayah li letter pa\n43286 kayah li letter pha\n43287 kayah li letter ma\n43288 kayah li letter da\n43289 kayah li letter ba\n43290 kayah li letter ra\n43291 kayah li letter ya\n43292 kayah li letter la\n43293 kayah li letter wa\n43294 kayah li letter tha\n43295 kayah li letter ha\n43296 kayah li letter va\n43297 kayah li letter ca\n43298 kayah li letter a\n43299 kayah li letter oe\n43300 kayah li letter i\n43301 kayah li letter oo\n43302 kayah li vowel ue\n43303 kayah li vowel e\n43304 kayah li vowel u\n43305 kayah li vowel ee\n43306 kayah li vowel o\n43307 kayah li tone plophu\n43308 kayah li tone calya\n43309 kayah li tone calya plophu\n43310 kayah li sign cwi\n43311 kayah li sign shya\n43312 rejang letter ka\n43313 rejang letter ga\n43314 rejang letter nga\n43315 rejang letter ta\n43316 rejang letter da\n43317 rejang letter na\n43318 rejang letter pa\n43319 rejang letter ba\n43320 rejang letter ma\n43321 rejang letter ca\n43322 rejang letter ja\n43323 rejang letter nya\n43324 rejang letter sa\n43325 rejang letter ra\n43326 rejang letter la\n43327 rejang letter ya\n43328 rejang letter wa\n43329 rejang letter ha\n43330 rejang letter mba\n43331 rejang letter ngga\n43332 rejang letter nda\n43333 rejang letter nyja\n43334 rejang letter a\n43335 rejang vowel sign i\n43336 rejang vowel sign u\n43337 rejang vowel sign e\n43338 rejang vowel sign ai\n43339 rejang vowel sign o\n43340 rejang vowel sign au\n43341 rejang vowel sign eu\n43342 rejang vowel sign ea\n43343 rejang consonant sign ng\n43344 rejang consonant sign n\n43345 rejang consonant sign r\n43346 rejang consonant sign h\n43347 rejang virama\n43359 rejang section mark\n43360 hangul choseong tikeut-mieum\n43361 hangul choseong tikeut-pieup\n43362 hangul choseong tikeut-sios\n43363 hangul choseong tikeut-cieuc\n43364 hangul choseong rieul-kiyeok\n43365 hangul choseong rieul-ssangkiyeok\n43366 hangul choseong rieul-tikeut\n43367 hangul choseong rieul-ssangtikeut\n43368 hangul choseong rieul-mieum\n43369 hangul choseong rieul-pieup\n43370 hangul choseong rieul-ssangpieup\n43371 hangul choseong rieul-kapyeounpieup\n43372 hangul choseong rieul-sios\n43373 hangul choseong rieul-cieuc\n43374 hangul choseong rieul-khieukh\n43375 hangul choseong mieum-kiyeok\n43376 hangul choseong mieum-tikeut\n43377 hangul choseong mieum-sios\n43378 hangul choseong pieup-sios-thieuth\n43379 hangul choseong pieup-khieukh\n43380 hangul choseong pieup-hieuh\n43381 hangul choseong ssangsios-pieup\n43382 hangul choseong ieung-rieul\n43383 hangul choseong ieung-hieuh\n43384 hangul choseong ssangcieuc-hieuh\n43385 hangul choseong ssangthieuth\n43386 hangul choseong phieuph-hieuh\n43387 hangul choseong hieuh-sios\n43388 hangul choseong ssangyeorinhieuh\n43392 javanese sign panyangga\n43393 javanese sign cecak\n43394 javanese sign layar\n43395 javanese sign wignyan\n43396 javanese letter a\n43397 javanese letter i kawi\n43398 javanese letter i\n43399 javanese letter ii\n43400 javanese letter u\n43401 javanese letter pa cerek\n43402 javanese letter nga lelet\n43403 javanese letter nga lelet raswadi\n43404 javanese letter e\n43405 javanese letter ai\n43406 javanese letter o\n43407 javanese letter ka\n43408 javanese letter ka sasak\n43409 javanese letter ka murda\n43410 javanese letter ga\n43411 javanese letter ga murda\n43412 javanese letter nga\n43413 javanese letter ca\n43414 javanese letter ca murda\n43415 javanese letter ja\n43416 javanese letter nya murda\n43417 javanese letter ja mahaprana\n43418 javanese letter nya\n43419 javanese letter tta\n43420 javanese letter tta mahaprana\n43421 javanese letter dda\n43422 javanese letter dda mahaprana\n43423 javanese letter na murda\n43424 javanese letter ta\n43425 javanese letter ta murda\n43426 javanese letter da\n43427 javanese letter da mahaprana\n43428 javanese letter na\n43429 javanese letter pa\n43430 javanese letter pa murda\n43431 javanese letter ba\n43432 javanese letter ba murda\n43433 javanese letter ma\n43434 javanese letter ya\n43435 javanese letter ra\n43436 javanese letter ra agung\n43437 javanese letter la\n43438 javanese letter wa\n43439 javanese letter sa murda\n43440 javanese letter sa mahaprana\n43441 javanese letter sa\n43442 javanese letter ha\n43443 javanese sign cecak telu\n43444 javanese vowel sign tarung\n43445 javanese vowel sign tolong\n43446 javanese vowel sign wulu\n43447 javanese vowel sign wulu melik\n43448 javanese vowel sign suku\n43449 javanese vowel sign suku mendut\n43450 javanese vowel sign taling\n43451 javanese vowel sign dirga mure\n43452 javanese vowel sign pepet\n43453 javanese consonant sign keret\n43454 javanese consonant sign pengkal\n43455 javanese consonant sign cakra\n43456 javanese pangkon\n43457 javanese left rerenggan\n43458 javanese right rerenggan\n43459 javanese pada andap\n43460 javanese pada madya\n43461 javanese pada luhur\n43462 javanese pada windu\n43463 javanese pada pangkat\n43464 javanese pada lingsa\n43465 javanese pada lungsi\n43466 javanese pada adeg\n43467 javanese pada adeg adeg\n43468 javanese pada piseleh\n43469 javanese turned pada piseleh\n43471 javanese pangrangkep\n43472 javanese digit zero\n43473 javanese digit one\n43474 javanese digit two\n43475 javanese digit three\n43476 javanese digit four\n43477 javanese digit five\n43478 javanese digit six\n43479 javanese digit seven\n43480 javanese digit eight\n43481 javanese digit nine\n43486 javanese pada tirta tumetes\n43487 javanese pada isen-isen\n43488 myanmar letter shan gha\n43489 myanmar letter shan cha\n43490 myanmar letter shan jha\n43491 myanmar letter shan nna\n43492 myanmar letter shan bha\n43493 myanmar sign shan saw\n43494 myanmar modifier letter shan reduplication\n43495 myanmar letter tai laing nya\n43496 myanmar letter tai laing fa\n43497 myanmar letter tai laing ga\n43498 myanmar letter tai laing gha\n43499 myanmar letter tai laing ja\n43500 myanmar letter tai laing jha\n43501 myanmar letter tai laing dda\n43502 myanmar letter tai laing ddha\n43503 myanmar letter tai laing nna\n43504 myanmar tai laing digit zero\n43505 myanmar tai laing digit one\n43506 myanmar tai laing digit two\n43507 myanmar tai laing digit three\n43508 myanmar tai laing digit four\n43509 myanmar tai laing digit five\n43510 myanmar tai laing digit six\n43511 myanmar tai laing digit seven\n43512 myanmar tai laing digit eight\n43513 myanmar tai laing digit nine\n43514 myanmar letter tai laing lla\n43515 myanmar letter tai laing da\n43516 myanmar letter tai laing dha\n43517 myanmar letter tai laing ba\n43518 myanmar letter tai laing bha\n43520 cham letter a\n43521 cham letter i\n43522 cham letter u\n43523 cham letter e\n43524 cham letter ai\n43525 cham letter o\n43526 cham letter ka\n43527 cham letter kha\n43528 cham letter ga\n43529 cham letter gha\n43530 cham letter ngue\n43531 cham letter nga\n43532 cham letter cha\n43533 cham letter chha\n43534 cham letter ja\n43535 cham letter jha\n43536 cham letter nhue\n43537 cham letter nha\n43538 cham letter nhja\n43539 cham letter ta\n43540 cham letter tha\n43541 cham letter da\n43542 cham letter dha\n43543 cham letter nue\n43544 cham letter na\n43545 cham letter dda\n43546 cham letter pa\n43547 cham letter ppa\n43548 cham letter pha\n43549 cham letter ba\n43550 cham letter bha\n43551 cham letter mue\n43552 cham letter ma\n43553 cham letter bba\n43554 cham letter ya\n43555 cham letter ra\n43556 cham letter la\n43557 cham letter va\n43558 cham letter ssa\n43559 cham letter sa\n43560 cham letter ha\n43561 cham vowel sign aa\n43562 cham vowel sign i\n43563 cham vowel sign ii\n43564 cham vowel sign ei\n43565 cham vowel sign u\n43566 cham vowel sign oe\n43567 cham vowel sign o\n43568 cham vowel sign ai\n43569 cham vowel sign au\n43570 cham vowel sign ue\n43571 cham consonant sign ya\n43572 cham consonant sign ra\n43573 cham consonant sign la\n43574 cham consonant sign wa\n43584 cham letter final k\n43585 cham letter final g\n43586 cham letter final ng\n43587 cham consonant sign final ng\n43588 cham letter final ch\n43589 cham letter final t\n43590 cham letter final n\n43591 cham letter final p\n43592 cham letter final y\n43593 cham letter final r\n43594 cham letter final l\n43595 cham letter final ss\n43596 cham consonant sign final m\n43597 cham consonant sign final h\n43600 cham digit zero\n43601 cham digit one\n43602 cham digit two\n43603 cham digit three\n43604 cham digit four\n43605 cham digit five\n43606 cham digit six\n43607 cham digit seven\n43608 cham digit eight\n43609 cham digit nine\n43612 cham punctuation spiral\n43613 cham punctuation danda\n43614 cham punctuation double danda\n43615 cham punctuation triple danda\n43616 myanmar letter khamti ga\n43617 myanmar letter khamti ca\n43618 myanmar letter khamti cha\n43619 myanmar letter khamti ja\n43620 myanmar letter khamti jha\n43621 myanmar letter khamti nya\n43622 myanmar letter khamti tta\n43623 myanmar letter khamti ttha\n43624 myanmar letter khamti dda\n43625 myanmar letter khamti ddha\n43626 myanmar letter khamti dha\n43627 myanmar letter khamti na\n43628 myanmar letter khamti sa\n43629 myanmar letter khamti ha\n43630 myanmar letter khamti hha\n43631 myanmar letter khamti fa\n43632 myanmar modifier letter khamti reduplication\n43633 myanmar letter khamti xa\n43634 myanmar letter khamti za\n43635 myanmar letter khamti ra\n43636 myanmar logogram khamti oay\n43637 myanmar logogram khamti qn\n43638 myanmar logogram khamti hm\n43639 myanmar symbol aiton exclamation\n43640 myanmar symbol aiton one\n43641 myanmar symbol aiton two\n43642 myanmar letter aiton ra\n43643 myanmar sign pao karen tone\n43644 myanmar sign tai laing tone-2\n43645 myanmar sign tai laing tone-5\n43646 myanmar letter shwe palaung cha\n43647 myanmar letter shwe palaung sha\n43648 tai viet letter low ko\n43649 tai viet letter high ko\n43650 tai viet letter low kho\n43651 tai viet letter high kho\n43652 tai viet letter low khho\n43653 tai viet letter high khho\n43654 tai viet letter low go\n43655 tai viet letter high go\n43656 tai viet letter low ngo\n43657 tai viet letter high ngo\n43658 tai viet letter low co\n43659 tai viet letter high co\n43660 tai viet letter low cho\n43661 tai viet letter high cho\n43662 tai viet letter low so\n43663 tai viet letter high so\n43664 tai viet letter low nyo\n43665 tai viet letter high nyo\n43666 tai viet letter low do\n43667 tai viet letter high do\n43668 tai viet letter low to\n43669 tai viet letter high to\n43670 tai viet letter low tho\n43671 tai viet letter high tho\n43672 tai viet letter low no\n43673 tai viet letter high no\n43674 tai viet letter low bo\n43675 tai viet letter high bo\n43676 tai viet letter low po\n43677 tai viet letter high po\n43678 tai viet letter low pho\n43679 tai viet letter high pho\n43680 tai viet letter low fo\n43681 tai viet letter high fo\n43682 tai viet letter low mo\n43683 tai viet letter high mo\n43684 tai viet letter low yo\n43685 tai viet letter high yo\n43686 tai viet letter low ro\n43687 tai viet letter high ro\n43688 tai viet letter low lo\n43689 tai viet letter high lo\n43690 tai viet letter low vo\n43691 tai viet letter high vo\n43692 tai viet letter low ho\n43693 tai viet letter high ho\n43694 tai viet letter low o\n43695 tai viet letter high o\n43696 tai viet mai kang\n43697 tai viet vowel aa\n43698 tai viet vowel i\n43699 tai viet vowel ue\n43700 tai viet vowel u\n43701 tai viet vowel e\n43702 tai viet vowel o\n43703 tai viet mai khit\n43704 tai viet vowel ia\n43705 tai viet vowel uea\n43706 tai viet vowel ua\n43707 tai viet vowel aue\n43708 tai viet vowel ay\n43709 tai viet vowel an\n43710 tai viet vowel am\n43711 tai viet tone mai ek\n43712 tai viet tone mai nueng\n43713 tai viet tone mai tho\n43714 tai viet tone mai song\n43739 tai viet symbol kon\n43740 tai viet symbol nueng\n43741 tai viet symbol sam\n43742 tai viet symbol ho hoi\n43743 tai viet symbol koi koi\n43744 meetei mayek letter e\n43745 meetei mayek letter o\n43746 meetei mayek letter cha\n43747 meetei mayek letter nya\n43748 meetei mayek letter tta\n43749 meetei mayek letter ttha\n43750 meetei mayek letter dda\n43751 meetei mayek letter ddha\n43752 meetei mayek letter nna\n43753 meetei mayek letter sha\n43754 meetei mayek letter ssa\n43755 meetei mayek vowel sign ii\n43756 meetei mayek vowel sign uu\n43757 meetei mayek vowel sign aai\n43758 meetei mayek vowel sign au\n43759 meetei mayek vowel sign aau\n43760 meetei mayek cheikhan\n43761 meetei mayek ahang khudam\n43762 meetei mayek anji\n43763 meetei mayek syllable repetition mark\n43764 meetei mayek word repetition mark\n43765 meetei mayek vowel sign visarga\n43766 meetei mayek virama\n43777 ethiopic syllable tthu\n43778 ethiopic syllable tthi\n43779 ethiopic syllable tthaa\n43780 ethiopic syllable tthee\n43781 ethiopic syllable tthe\n43782 ethiopic syllable ttho\n43785 ethiopic syllable ddhu\n43786 ethiopic syllable ddhi\n43787 ethiopic syllable ddhaa\n43788 ethiopic syllable ddhee\n43789 ethiopic syllable ddhe\n43790 ethiopic syllable ddho\n43793 ethiopic syllable dzu\n43794 ethiopic syllable dzi\n43795 ethiopic syllable dzaa\n43796 ethiopic syllable dzee\n43797 ethiopic syllable dze\n43798 ethiopic syllable dzo\n43808 ethiopic syllable cchha\n43809 ethiopic syllable cchhu\n43810 ethiopic syllable cchhi\n43811 ethiopic syllable cchhaa\n43812 ethiopic syllable cchhee\n43813 ethiopic syllable cchhe\n43814 ethiopic syllable cchho\n43816 ethiopic syllable bba\n43817 ethiopic syllable bbu\n43818 ethiopic syllable bbi\n43819 ethiopic syllable bbaa\n43820 ethiopic syllable bbee\n43821 ethiopic syllable bbe\n43822 ethiopic syllable bbo\n43824 latin small letter barred alpha\n43825 latin small letter a reversed-schwa\n43826 latin small letter blackletter e\n43827 latin small letter barred e\n43828 latin small letter e with flourish\n43829 latin small letter lenis f\n43830 latin small letter script g with crossed-tail\n43831 latin small letter l with inverted lazy s\n43832 latin small letter l with double middle tilde\n43833 latin small letter l with middle ring\n43834 latin small letter m with crossed-tail\n43835 latin small letter n with crossed-tail\n43836 latin small letter eng with crossed-tail\n43837 latin small letter blackletter o\n43838 latin small letter blackletter o with stroke\n43839 latin small letter open o with stroke\n43840 latin small letter inverted oe\n43841 latin small letter turned oe with stroke\n43842 latin small letter turned oe with horizontal stroke\n43843 latin small letter turned o open-o\n43844 latin small letter turned o open-o with stroke\n43845 latin small letter stirrup r\n43846 latin letter small capital r with right leg\n43847 latin small letter r without handle\n43848 latin small letter double r\n43849 latin small letter r with crossed-tail\n43850 latin small letter double r with crossed-tail\n43851 latin small letter script r\n43852 latin small letter script r with ring\n43853 latin small letter baseline esh\n43854 latin small letter u with short right leg\n43855 latin small letter u bar with short right leg\n43856 latin small letter ui\n43857 latin small letter turned ui\n43858 latin small letter u with left hook\n43859 latin small letter chi\n43860 latin small letter chi with low right ring\n43861 latin small letter chi with low left serif\n43862 latin small letter x with low right ring\n43863 latin small letter x with long left leg\n43864 latin small letter x with long left leg and low right ring\n43865 latin small letter x with long left leg with serif\n43866 latin small letter y with short right leg\n43867 modifier breve with inverted breve\n43868 modifier letter small heng\n43869 modifier letter small l with inverted lazy s\n43870 modifier letter small l with middle tilde\n43871 modifier letter small u with left hook\n43872 latin small letter sakha yat\n43873 latin small letter iotified e\n43874 latin small letter open oe\n43875 latin small letter uo\n43876 latin small letter inverted alpha\n43877 greek letter small capital omega\n43878 latin small letter dz digraph with retroflex hook\n43879 latin small letter ts digraph with retroflex hook\n43880 latin small letter turned r with middle tilde\n43881 modifier letter small turned w\n43882 modifier letter left tack\n43883 modifier letter right tack\n43888 cherokee small letter a\n43889 cherokee small letter e\n43890 cherokee small letter i\n43891 cherokee small letter o\n43892 cherokee small letter u\n43893 cherokee small letter v\n43894 cherokee small letter ga\n43895 cherokee small letter ka\n43896 cherokee small letter ge\n43897 cherokee small letter gi\n43898 cherokee small letter go\n43899 cherokee small letter gu\n43900 cherokee small letter gv\n43901 cherokee small letter ha\n43902 cherokee small letter he\n43903 cherokee small letter hi\n43904 cherokee small letter ho\n43905 cherokee small letter hu\n43906 cherokee small letter hv\n43907 cherokee small letter la\n43908 cherokee small letter le\n43909 cherokee small letter li\n43910 cherokee small letter lo\n43911 cherokee small letter lu\n43912 cherokee small letter lv\n43913 cherokee small letter ma\n43914 cherokee small letter me\n43915 cherokee small letter mi\n43916 cherokee small letter mo\n43917 cherokee small letter mu\n43918 cherokee small letter na\n43919 cherokee small letter hna\n43920 cherokee small letter nah\n43921 cherokee small letter ne\n43922 cherokee small letter ni\n43923 cherokee small letter no\n43924 cherokee small letter nu\n43925 cherokee small letter nv\n43926 cherokee small letter qua\n43927 cherokee small letter que\n43928 cherokee small letter qui\n43929 cherokee small letter quo\n43930 cherokee small letter quu\n43931 cherokee small letter quv\n43932 cherokee small letter sa\n43933 cherokee small letter s\n43934 cherokee small letter se\n43935 cherokee small letter si\n43936 cherokee small letter so\n43937 cherokee small letter su\n43938 cherokee small letter sv\n43939 cherokee small letter da\n43940 cherokee small letter ta\n43941 cherokee small letter de\n43942 cherokee small letter te\n43943 cherokee small letter di\n43944 cherokee small letter ti\n43945 cherokee small letter do\n43946 cherokee small letter du\n43947 cherokee small letter dv\n43948 cherokee small letter dla\n43949 cherokee small letter tla\n43950 cherokee small letter tle\n43951 cherokee small letter tli\n43952 cherokee small letter tlo\n43953 cherokee small letter tlu\n43954 cherokee small letter tlv\n43955 cherokee small letter tsa\n43956 cherokee small letter tse\n43957 cherokee small letter tsi\n43958 cherokee small letter tso\n43959 cherokee small letter tsu\n43960 cherokee small letter tsv\n43961 cherokee small letter wa\n43962 cherokee small letter we\n43963 cherokee small letter wi\n43964 cherokee small letter wo\n43965 cherokee small letter wu\n43966 cherokee small letter wv\n43967 cherokee small letter ya\n43968 meetei mayek letter kok\n43969 meetei mayek letter sam\n43970 meetei mayek letter lai\n43971 meetei mayek letter mit\n43972 meetei mayek letter pa\n43973 meetei mayek letter na\n43974 meetei mayek letter chil\n43975 meetei mayek letter til\n43976 meetei mayek letter khou\n43977 meetei mayek letter ngou\n43978 meetei mayek letter thou\n43979 meetei mayek letter wai\n43980 meetei mayek letter yang\n43981 meetei mayek letter huk\n43982 meetei mayek letter un\n43983 meetei mayek letter i\n43984 meetei mayek letter pham\n43985 meetei mayek letter atiya\n43986 meetei mayek letter gok\n43987 meetei mayek letter jham\n43988 meetei mayek letter rai\n43989 meetei mayek letter ba\n43990 meetei mayek letter jil\n43991 meetei mayek letter dil\n43992 meetei mayek letter ghou\n43993 meetei mayek letter dhou\n43994 meetei mayek letter bham\n43995 meetei mayek letter kok lonsum\n43996 meetei mayek letter lai lonsum\n43997 meetei mayek letter mit lonsum\n43998 meetei mayek letter pa lonsum\n43999 meetei mayek letter na lonsum\n44000 meetei mayek letter til lonsum\n44001 meetei mayek letter ngou lonsum\n44002 meetei mayek letter i lonsum\n44003 meetei mayek vowel sign onap\n44004 meetei mayek vowel sign inap\n44005 meetei mayek vowel sign anap\n44006 meetei mayek vowel sign yenap\n44007 meetei mayek vowel sign sounap\n44008 meetei mayek vowel sign unap\n44009 meetei mayek vowel sign cheinap\n44010 meetei mayek vowel sign nung\n44011 meetei mayek cheikhei\n44012 meetei mayek lum iyek\n44013 meetei mayek apun iyek\n44016 meetei mayek digit zero\n44017 meetei mayek digit one\n44018 meetei mayek digit two\n44019 meetei mayek digit three\n44020 meetei mayek digit four\n44021 meetei mayek digit five\n44022 meetei mayek digit six\n44023 meetei mayek digit seven\n44024 meetei mayek digit eight\n44025 meetei mayek digit nine\n44032 <hangul syllable, first>\n55203 <hangul syllable, last>\n55216 hangul jungseong o-yeo\n55217 hangul jungseong o-o-i\n55218 hangul jungseong yo-a\n55219 hangul jungseong yo-ae\n55220 hangul jungseong yo-eo\n55221 hangul jungseong u-yeo\n55222 hangul jungseong u-i-i\n55223 hangul jungseong yu-ae\n55224 hangul jungseong yu-o\n55225 hangul jungseong eu-a\n55226 hangul jungseong eu-eo\n55227 hangul jungseong eu-e\n55228 hangul jungseong eu-o\n55229 hangul jungseong i-ya-o\n55230 hangul jungseong i-yae\n55231 hangul jungseong i-yeo\n55232 hangul jungseong i-ye\n55233 hangul jungseong i-o-i\n55234 hangul jungseong i-yo\n55235 hangul jungseong i-yu\n55236 hangul jungseong i-i\n55237 hangul jungseong araea-a\n55238 hangul jungseong araea-e\n55243 hangul jongseong nieun-rieul\n55244 hangul jongseong nieun-chieuch\n55245 hangul jongseong ssangtikeut\n55246 hangul jongseong ssangtikeut-pieup\n55247 hangul jongseong tikeut-pieup\n55248 hangul jongseong tikeut-sios\n55249 hangul jongseong tikeut-sios-kiyeok\n55250 hangul jongseong tikeut-cieuc\n55251 hangul jongseong tikeut-chieuch\n55252 hangul jongseong tikeut-thieuth\n55253 hangul jongseong rieul-ssangkiyeok\n55254 hangul jongseong rieul-kiyeok-hieuh\n55255 hangul jongseong ssangrieul-khieukh\n55256 hangul jongseong rieul-mieum-hieuh\n55257 hangul jongseong rieul-pieup-tikeut\n55258 hangul jongseong rieul-pieup-phieuph\n55259 hangul jongseong rieul-yesieung\n55260 hangul jongseong rieul-yeorinhieuh-hieuh\n55261 hangul jongseong kapyeounrieul\n55262 hangul jongseong mieum-nieun\n55263 hangul jongseong mieum-ssangnieun\n55264 hangul jongseong ssangmieum\n55265 hangul jongseong mieum-pieup-sios\n55266 hangul jongseong mieum-cieuc\n55267 hangul jongseong pieup-tikeut\n55268 hangul jongseong pieup-rieul-phieuph\n55269 hangul jongseong pieup-mieum\n55270 hangul jongseong ssangpieup\n55271 hangul jongseong pieup-sios-tikeut\n55272 hangul jongseong pieup-cieuc\n55273 hangul jongseong pieup-chieuch\n55274 hangul jongseong sios-mieum\n55275 hangul jongseong sios-kapyeounpieup\n55276 hangul jongseong ssangsios-kiyeok\n55277 hangul jongseong ssangsios-tikeut\n55278 hangul jongseong sios-pansios\n55279 hangul jongseong sios-cieuc\n55280 hangul jongseong sios-chieuch\n55281 hangul jongseong sios-thieuth\n55282 hangul jongseong sios-hieuh\n55283 hangul jongseong pansios-pieup\n55284 hangul jongseong pansios-kapyeounpieup\n55285 hangul jongseong yesieung-mieum\n55286 hangul jongseong yesieung-hieuh\n55287 hangul jongseong cieuc-pieup\n55288 hangul jongseong cieuc-ssangpieup\n55289 hangul jongseong ssangcieuc\n55290 hangul jongseong phieuph-sios\n55291 hangul jongseong phieuph-thieuth\n55296 <non private use high surrogate, first>\n56191 <non private use high surrogate, last>\n56192 <private use high surrogate, first>\n56319 <private use high surrogate, last>\n56320 <low surrogate, first>\n57343 <low surrogate, last>\n57344 <private use, first>\n57345 pomodoro done\n57346 pomodoro estimated\n57347 pomodoro ticking\n57348 pomodoro squashed\n57349 short pause\n57350 long pause\n57351 away\n57352 pair programming\n57353 internal interruption\n57354 external interruption\n57504 branch\n57505 current line\n57506 hostname\n57507 column number\n57520 left hard divider\n57521 left soft divider\n57522 right hard divider\n57523 right soft divider\n57524 right half circle thick\n57525 right half circle thin\n57526 left half circle thick\n57527 left half circle thin\n57528 lower left triangle\n57529 backslash separator\n57530 lower right triangle\n57531 forwardslash separator\n57532 upper left triangle\n57533 forwardslash separator redundant\n57534 upper right triangle\n57535 backslash separator redundant\n57536 flame thick\n57537 flame thin\n57538 flame thick mirrored\n57539 flame thin mirrored\n57540 pixelated squares small\n57541 pixelated squares small mirrored\n57542 pixelated squares big\n57543 pixelated squares big mirrored\n57544 ice waveform\n57546 ice waveform mirrored\n57548 honeycomb\n57549 honeycomb outline\n57550 lego separator\n57551 lego separator thin\n57552 lego block facing\n57553 lego block sideways\n57554 trapezoid top bottom\n57556 trapezoid top bottom mirrored\n57558 right hard divider inverse\n57559 left hard divider inverse\n57856 smaller\n57857 snowing\n57858 soda\n57859 sofa\n57860 soup\n57861 spermatozoon\n57862 spin double\n57863 stomach\n57864 storm\n57865 telescope\n57866 thermometer\n57867 thermometer high\n57868 thermometer low\n57869 thin close\n57870 toilet\n57871 tools\n57872 tooth\n57873 uterus\n57874 w3c\n57875 walking\n57876 virus\n57877 telegram circle\n57878 slash\n57879 telegram\n57880 shirt\n57881 tacos\n57882 sushi\n57883 triangle ruler\n57884 tree\n57885 sun cloud\n57886 ruby o\n57887 ruler\n57888 umbrella\n57889 medicine\n57890 microscope\n57891 milk bottle\n57892 minimize\n57893 molecule\n57894 moon cloud\n57895 mushroom\n57896 mustache\n57897 mysql\n57898 nintendo\n57899 palette color\n57900 pi\n57901 pizza\n57902 planet\n57903 plant\n57904 playstation\n57905 poison\n57906 popcorn\n57907 popsicle\n57908 pulse\n57909 python\n57910 quora circle\n57911 quora square\n57912 radioactive\n57913 raining\n57914 real heart\n57915 refrigerator\n57916 restore\n57917 ring\n57918 ruby\n57919 fingerprint\n57920 floppy\n57921 footprint\n57922 freecodecamp\n57923 galaxy\n57924 galery\n57925 glass\n57926 google drive\n57927 google play\n57928 gps\n57929 grav\n57930 guitar\n57931 gut\n57932 halter\n57933 hamburger\n57934 hat\n57935 hexagon\n57936 high heel\n57937 hotdog\n57938 ice cream\n57939 id card\n57940 imdb\n57941 infinity\n57942 java\n57943 layers\n57944 lips\n57945 lipstick\n57946 liver\n57947 lung\n57948 makeup brushes\n57949 maximize\n57950 wallet\n57951 chess horse\n57952 chess king\n57953 chess pawn\n57954 chess queen\n57955 chess tower\n57956 cheese\n57957 chilli\n57958 chip\n57959 cicling\n57960 cloud\n57961 cockroach\n57962 coffe beans\n57963 coins\n57964 comb\n57965 comet\n57966 crown\n57967 cup coffe\n57968 dice\n57969 disco\n57970 dna\n57971 donut\n57972 dress\n57973 drop\n57974 ello\n57975 envelope open\n57976 envelope open o\n57977 equal\n57978 equal bigger\n57979 feedly\n57980 file export\n57981 file import\n57982 wind\n57983 atom\n57984 bacteria\n57985 banana\n57986 bath\n57987 bed\n57988 benzene\n57989 bigger\n57990 biohazard\n57991 blogger circle\n57992 blogger square\n57993 bones\n57994 book open\n57995 book open o\n57996 brain\n57997 bread\n57998 butterfly\n57999 carot\n58000 cc by\n58001 cc cc\n58002 cc nc\n58003 cc nc eu\n58004 cc nc jp\n58005 cc nd\n58006 cc remix\n58007 cc sa\n58008 cc share\n58009 cc zero\n58010 checklist o\n58011 cherry\n58012 chess bishop\n58013 xbox\n58014 apple fruit\n58015 chicken thigh\n58016 gift card\n58017 injection\n58018 isle\n58019 lollipop\n58020 loyalty card\n58021 meat\n58022 mountains\n58023 orange\n58024 peach\n58025 pear\n58112 day cloudy gusts\n58113 day cloudy windy\n58114 day cloudy\n58115 day fog\n58116 day hail\n58117 day lightning\n58118 day rain mix\n58119 day rain wind\n58120 day rain\n58121 day showers\n58122 day snow\n58123 day sprinkle\n58124 day sunny overcast\n58125 day sunny\n58126 day storm showers\n58127 day thunderstorm\n58128 cloudy gusts\n58129 cloudy windy\n58130 cloudy\n58131 fog\n58132 hail\n58133 lightning\n58134 rain mix\n58135 rain wind\n58136 rain\n58137 showers\n58138 snow\n58139 sprinkle\n58140 storm showers\n58141 thunderstorm\n58142 windy\n58143 night alt cloudy gusts\n58144 night alt cloudy windy\n58145 night alt hail\n58146 night alt lightning\n58147 night alt rain mix\n58148 night alt rain wind\n58149 night alt rain\n58150 night alt showers\n58151 night alt snow\n58152 night alt sprinkle\n58153 night alt storm showers\n58154 night alt thunderstorm\n58155 night clear\n58156 night cloudy gusts\n58157 night cloudy windy\n58158 night cloudy\n58159 night hail\n58160 night lightning\n58161 night rain mix\n58162 night rain wind\n58163 night rain\n58164 night showers\n58165 night snow\n58166 night sprinkle\n58167 night storm showers\n58168 night thunderstorm\n58169 celsius\n58170 cloud down\n58171 cloud refresh\n58172 cloud up\n58173 cloud\n58174 degrees\n58175 direction down left\n58176 direction down\n58177 fahrenheit\n58178 horizon alt\n58179 horizon\n58180 direction left\n58181 aliens\n58182 night fog\n58183 refresh alt\n58184 refresh\n58185 direction right\n58186 raindrops\n58187 strong wind\n58188 sunrise\n58189 sunset\n58190 thermometer exterior\n58191 thermometer internal\n58192 thermometer\n58193 tornado\n58194 direction up right\n58195 direction up\n58196 wind west\n58197 wind south west\n58198 wind south east\n58199 wind south\n58200 wind north west\n58201 wind north east\n58202 wind north\n58203 wind east\n58204 smoke\n58205 dust\n58206 snow wind\n58207 day snow wind\n58208 night snow wind\n58209 night alt snow wind\n58210 day sleet storm\n58211 night sleet storm\n58212 night alt sleet storm\n58213 day snow thunderstorm\n58214 night snow thunderstorm\n58215 night alt snow thunderstorm\n58216 solar eclipse\n58217 lunar eclipse\n58218 meteor\n58219 hot\n58220 hurricane\n58221 smog\n58222 alien\n58223 snowflake cold\n58224 stars\n58225 raindrop\n58226 barometer\n58227 humidity\n58228 na\n58229 flood\n58230 day cloudy high\n58231 night alt cloudy high\n58232 night cloudy high\n58233 night alt partly cloudy\n58234 sandstorm\n58235 night partly cloudy\n58236 umbrella\n58237 day windy\n58238 night alt cloudy\n58239 direction up left\n58240 direction down right\n58241 time 12\n58242 time 1\n58243 time 2\n58244 time 3\n58245 time 4\n58246 time 5\n58247 time 6\n58248 time 7\n58249 time 8\n58250 time 9\n58251 time 10\n58252 time 11\n58253 moon new\n58254 moon waxing crescent 1\n58255 moon waxing crescent 2\n58256 moon waxing crescent 3\n58257 moon waxing crescent 4\n58258 moon waxing crescent 5\n58259 moon waxing crescent 6\n58260 moon first quarter\n58261 moon waxing gibbous 1\n58262 moon waxing gibbous 2\n58263 moon waxing gibbous 3\n58264 moon waxing gibbous 4\n58265 moon waxing gibbous 5\n58266 moon waxing gibbous 6\n58267 moon full\n58268 moon waning gibbous 1\n58269 moon waning gibbous 2\n58270 moon waning gibbous 3\n58271 moon waning gibbous 4\n58272 moon waning gibbous 5\n58273 moon waning gibbous 6\n58274 moon third quarter\n58275 moon waning crescent 1\n58276 moon waning crescent 2\n58277 moon waning crescent 3\n58278 moon waning crescent 4\n58279 moon waning crescent 5\n58280 moon waning crescent 6\n58281 wind direction\n58282 day sleet\n58283 night sleet\n58284 night alt sleet\n58285 sleet\n58286 day haze\n58287 wind beaufort 0\n58288 wind beaufort 1\n58289 wind beaufort 2\n58290 wind beaufort 3\n58291 wind beaufort 4\n58292 wind beaufort 5\n58293 wind beaufort 6\n58294 wind beaufort 7\n58295 wind beaufort 8\n58296 wind beaufort 9\n58297 wind beaufort 10\n58298 wind beaufort 11\n58299 wind beaufort 12\n58300 day light wind\n58301 tsunami\n58302 earthquake\n58303 fire\n58304 volcano\n58305 moonrise\n58306 moonset\n58307 train\n58308 small craft advisory\n58309 gale warning\n58310 storm warning\n58311 hurricane warning\n58312 moon alt waxing crescent 1\n58313 moon alt waxing crescent 2\n58314 moon alt waxing crescent 3\n58315 moon alt waxing crescent 4\n58316 moon alt waxing crescent 5\n58317 moon alt waxing crescent 6\n58318 moon alt first quarter\n58319 moon alt waxing gibbous 1\n58320 moon alt waxing gibbous 2\n58321 moon alt waxing gibbous 3\n58322 moon alt waxing gibbous 4\n58323 moon alt waxing gibbous 5\n58324 moon alt waxing gibbous 6\n58325 moon alt full\n58326 moon alt waning gibbous 1\n58327 moon alt waning gibbous 2\n58328 moon alt waning gibbous 3\n58329 moon alt waning gibbous 4\n58330 moon alt waning gibbous 5\n58331 moon alt waning gibbous 6\n58332 moon alt third quarter\n58333 moon alt waning crescent 1\n58334 moon alt waning crescent 2\n58335 moon alt waning crescent 3\n58336 moon alt waning crescent 4\n58337 moon alt waning crescent 5\n58338 moon alt waning crescent 6\n58339 moon alt new\n58874 folder npm\n58875 folder git\n58876 folder config\n58877 folder github\n58878 folder open\n58879 folder\n58880 stylus\n58881 project\n58882 play arrow\n58883 sass\n58884 rails\n58885 ruby\n58886 python\n58887 heroku\n58888 php\n58889 markdown\n58890 license\n58891 json\n58892 javascript\n58893 image\n58894 html\n58895 mustache\n58896 gulp\n58897 grunt\n58898 default\n58899 folder\n58900 css\n58901 config\n58902 npm\n58903 home\n58904 ejs\n58905 xml\n58906 bower\n58907 cjsx\n58908 twig\n58909 cpp\n58910 c\n58911 haskell\n58912 lua\n58913 dotted guide\n58914 karma\n58915 favicon\n58916 julia\n58917 react\n58918 go\n58919 go\n58920 typescript\n58921 msdos\n58922 windows\n58923 vim\n58924 elm\n58925 elixir\n58926 electron\n58927 crystal\n58928 purescript\n58929 puppet\n58930 emacs\n58931 orgmode\n58932 kotlin\n58933 apple\n58934 argdown\n58935 asm\n58936 audio\n58937 babel\n58938 bazel\n58939 bicep\n58940 bsl\n58941 cake php\n58942 cake\n58943 checkbox\n58944 checkbox unchecked\n58945 clock\n58946 clojure\n58947 code climate\n58948 code search\n58949 coldfusion\n58950 cpp\n58951 crystal embedded\n58952 c sharp\n58953 c\n58954 csv\n58955 cu\n58956 dart\n58957 db\n58958 default\n58959 deprecation cop\n58960 docker\n58961 d\n58962 editorconfig\n58963 elixir script\n58964 error\n58965 eslint\n58966 ethereum\n58967 firebase\n58968 firefox\n58969 font\n58970 f sharp\n58971 github\n58972 gitlab\n58973 git\n58974 go2\n58975 godot\n58976 gradle\n58977 grails\n58978 graphql\n58979 hacklang\n58980 haml\n58981 happenings\n58982 haxe\n58983 hex\n58984 ignored\n58985 illustrator\n58986 info\n58987 ionic\n58988 jade\n58989 java\n58990 jenkins\n58991 jinja\n58992 liquid\n58993 livescript\n58994 lock\n58995 makefile\n58996 maven\n58997 mdo\n58998 new file\n58999 nim\n59000 notebook\n59001 nunjucks\n59002 ocaml\n59003 odata\n59004 pddl\n59005 pdf\n59006 perl\n59007 photoshop\n59008 pipeline\n59009 plan\n59010 platformio\n59011 powershell\n59012 prisma\n59013 prolog\n59014 pug\n59015 reasonml\n59016 rescript\n59017 rollup\n59018 r\n59019 rust\n59020 salesforce\n59021 sbt\n59022 scala\n59023 search\n59024 settings\n59025 shell\n59026 slim\n59027 smarty\n59028 spring\n59029 stylelint\n59030 sublime\n59031 svelte\n59032 svg\n59033 swift\n59034 terraform\n59035 tex\n59036 todo\n59037 tsconfig\n59038 vala\n59039 video\n59040 vue\n59041 wasm\n59042 wat\n59043 webpack\n59044 wgt\n59045 word\n59046 xls\n59047 yarn\n59048 yml\n59049 zig\n59050 zip\n59051 asm\n59052 v lang\n59053 folder oct\n59054 neovim\n59055 fennel\n59056 common lisp\n59057 scheme\n59058 toml\n59059 astro\n59060 prettier\n59061 ada\n59062 chuck\n59063 vitruvian\n59064 css\n59136 aarch64\n59137 adonisjs\n59138 git\n59139 bitbucket\n59140 mysql\n59141 aftereffects\n59142 database\n59143 dropbox\n59144 akka\n59145 github\n59146 algolia\n59147 wordpress\n59148 visualstudio\n59149 jekyll\n59150 android\n59151 windows\n59152 stackoverflow\n59153 apple\n59154 linux\n59155 alpinejs\n59156 ghost small\n59157 anaconda\n59158 codepen\n59159 github full\n59160 nodejs small\n59161 nodejs\n59162 androidstudio\n59163 ember\n59164 angularjs\n59165 django\n59166 npm\n59167 ghost\n59168 angularmaterial\n59169 unity\n59170 raspberry pi\n59171 ansible\n59172 go\n59173 git branch\n59174 git pull request\n59175 git merge\n59176 git compare\n59177 git commit\n59178 antdesign\n59179 apache\n59180 apacheairflow\n59181 smashing magazine\n59182 apachekafka\n59183 apachespark\n59184 apl\n59185 appwrite\n59186 archlinux\n59187 arduino\n59188 argocd\n59189 astro\n59190 html5\n59191 scala\n59192 java\n59193 ruby\n59194 ubuntu\n59195 rails\n59196 python\n59197 php\n59198 markdown\n59199 laravel\n59200 magento\n59201 awk\n59202 drupal\n59203 chrome\n59204 ie\n59205 firefox\n59206 opera\n59207 bootstrap\n59208 safari\n59209 css3\n59210 css3 full\n59211 sass\n59212 grunt\n59213 bower\n59214 javascript alt\n59215 axios\n59216 jquery\n59217 coffeescript\n59218 backbone\n59219 angular\n59220 azure\n59221 swift\n59222 azuredevops\n59223 symfony\n59224 less\n59225 stylus\n59226 trello\n59227 azuresqldatabase\n59228 jira\n59229 babel\n59230 ballerina\n59231 bamboo\n59232 bash\n59233 beats\n59234 behance\n59235 gulp\n59236 atom\n59237 blazor\n59238 blender\n59239 jenkins\n59240 clojure\n59241 perl\n59242 clojure alt\n59243 browserstack\n59244 bulma\n59245 redis\n59246 postgresql\n59247 bun\n59248 requirejs\n59249 c\n59250 typo3\n59251 cairo\n59252 doctrine\n59253 groovy\n59254 nginx\n59255 haskell\n59256 zend\n59257 gnu\n59258 cakephp\n59259 heroku\n59260 canva\n59261 debian\n59262 travis\n59263 dotnet\n59264 codeigniter\n59265 javascript\n59266 yii\n59267 composer\n59268 krakenjs\n59269 capacitor\n59270 mozilla\n59271 firebase\n59272 carbon\n59273 cassandra\n59274 centos\n59275 ceylon\n59276 circleci\n59277 clarity\n59278 clion\n59279 mootools badge\n59280 clojurescript\n59281 ruby rough\n59282 cloudflare\n59283 cloudflareworkers\n59284 cmake\n59285 terminal\n59286 codeac\n59287 codecov\n59288 dart\n59289 confluence\n59290 consul\n59291 contao\n59292 dreamweaver\n59293 corejs\n59294 eclipse\n59295 cosmosdb\n59296 couchbase\n59297 prolog\n59298 couchdb\n59299 cplusplus\n59300 mongodb\n59301 meteor\n59302 meteorfull\n59303 fsharp\n59304 rust\n59305 ionic\n59306 sublime\n59307 appcelerator\n59308 crystal\n59309 amazonwebservices\n59310 digital ocean\n59311 dlang\n59312 docker\n59313 erlang\n59314 csharp\n59315 grails\n59316 illustrator\n59317 intellij\n59318 materializecss\n59319 cucumber\n59320 photoshop\n59321 cypressio\n59322 react\n59323 redhat\n59324 d3js\n59325 datagrip\n59326 dataspell\n59327 dbeaver\n59328 denojs\n59329 devicon\n59330 discordjs\n59331 djangorest\n59332 sqlite\n59333 vim\n59334 dotnetcore\n59335 dropwizard\n59336 dynamodb\n59337 ecto\n59338 elasticsearch\n59339 electron\n59340 eleventy\n59341 elixir\n59342 elm\n59343 emacs\n59344 embeddedc\n59345 envoy\n59346 eslint\n59347 express\n59348 facebook\n59349 fastapi\n59350 fastify\n59351 faunadb\n59352 feathersjs\n59353 fedora\n59354 figma\n59355 filezilla\n59356 flask\n59357 flutter\n59358 fortran\n59359 foundation\n59360 framermotion\n59361 framework7\n59362 gatling\n59363 gatsby\n59364 gazebo\n59365 gcc\n59366 gentoo\n59367 gimp\n59368 gitbook\n59369 githubactions\n59370 githubcodespaces\n59371 gitlab\n59372 gitpod\n59373 gitter\n59374 godot\n59375 goland\n59376 google\n59377 googlecloud\n59378 gradle\n59379 grafana\n59380 graphql\n59381 grpc\n59382 hadoop\n59383 handlebars\n59384 hardhat\n59385 harvester\n59386 haxe\n59387 helm\n59388 hibernate\n59389 homebrew\n59390 hugo\n59391 ifttt\n59392 influxdb\n59393 inkscape\n59394 insomnia\n59395 jaegertracing\n59396 jamstack\n59397 jasmine\n59398 jeet\n59399 jest\n59400 jetbrains\n59401 jetpackcompose\n59402 jiraalign\n59403 json\n59404 jule\n59405 julia\n59406 junit\n59407 jupyter\n59408 k3os\n59409 k3s\n59410 k6\n59411 kaggle\n59412 karatelabs\n59413 karma\n59414 kdeneon\n59415 keras\n59416 kibana\n59417 knexjs\n59418 knockout\n59419 kotlin\n59420 ktor\n59421 kubernetes\n59422 labview\n59423 latex\n59424 linkedin\n59425 liquibase\n59426 livewire\n59427 llvm\n59428 lodash\n59429 logstash\n59430 lua\n59431 lumen\n59432 mariadb\n59433 materialui\n59434 matlab\n59435 matplotlib\n59436 maven\n59437 maya\n59438 microsoftsqlserver\n59439 minitab\n59440 mithril\n59441 mobx\n59442 mocha\n59443 modx\n59444 moleculer\n59445 mongoose\n59446 moodle\n59447 msdos\n59448 nano\n59449 neo4j\n59450 neovim\n59451 nestjs\n59452 netlify\n59453 networkx\n59454 nextjs\n59455 ngrx\n59456 nhibernate\n59457 nim\n59458 nimble\n59459 nixos\n59460 nodemon\n59461 nodewebkit\n59462 nomad\n59463 norg\n59464 notion\n59465 nuget\n59466 numpy\n59467 nuxtjs\n59468 oauth\n59469 objectivec\n59470 ocaml\n59471 ohmyzsh\n59472 okta\n59473 openal\n59474 openapi\n59475 opencl\n59476 opencv\n59477 opengl\n59478 openstack\n59479 opensuse\n59480 opentelemetry\n59481 oracle\n59482 ory\n59483 p5js\n59484 packer\n59485 pandas\n59486 pfsense\n59487 phalcon\n59488 phoenix\n59489 photonengine\n59490 phpstorm\n59491 playwright\n59492 plotly\n59493 pnpm\n59494 podman\n59495 poetry\n59496 polygon\n59497 portainer\n59498 postcss\n59499 postman\n59500 powershell\n59501 premierepro\n59502 prisma\n59503 processing\n59504 prometheus\n59505 protractor\n59506 pulsar\n59507 pulumi\n59508 puppeteer\n59509 purescript\n59510 putty\n59511 pycharm\n59512 pypi\n59513 pyscript\n59514 pytest\n59515 pytorch\n59516 qodana\n59517 qt\n59518 quarkus\n59519 quasar\n59520 qwik\n59521 r\n59522 rabbitmq\n59523 railway\n59524 rancher\n59525 reach\n59526 reactbootstrap\n59527 reactnavigation\n59528 reactrouter\n59529 readthedocs\n59530 realm\n59531 rect\n59532 redux\n59533 renpy\n59534 replit\n59535 rider\n59536 rocksdb\n59537 rockylinux\n59538 rollup\n59539 ros\n59540 rspec\n59541 rstudio\n59542 rubymine\n59543 rxjs\n59544 salesforce\n59545 sanity\n59546 scalingo\n59547 scikitlearn\n59548 sdl\n59549 selenium\n59550 sema\n59551 sentry\n59552 sequelize\n59553 shopware\n59554 shotgrid\n59555 sketch\n59556 slack\n59557 socketio\n59558 solidity\n59559 solidjs\n59560 sonarqube\n59561 sourcetree\n59562 spack\n59563 splunk\n59564 spring\n59565 spss\n59566 spyder\n59567 sqlalchemy\n59568 sqldeveloper\n59569 ssh\n59570 stata\n59571 storybook\n59572 streamlit\n59573 subversion\n59574 supabase\n59575 svelte\n59576 swagger\n59577 swiper\n59578 tailwindcss\n59579 tauri\n59580 tensorflow\n59581 terraform\n59582 tex\n59583 thealgorithms\n59584 threedsmax\n59585 threejs\n59586 titaniumsdk\n59587 tomcat\n59588 tortoisegit\n59589 towergit\n59590 traefikmesh\n59591 traefikproxy\n59592 trpc\n59593 twitter\n59594 typescript\n59595 uml\n59596 unix\n59597 unrealengine\n59598 uwsgi\n59599 v8\n59600 vagrant\n59601 vala\n59602 vault\n59603 vercel\n59604 vertx\n59605 visualbasic\n59606 vite\n59607 vitejs\n59608 vitess\n59609 vitest\n59610 vscode\n59611 vsphere\n59612 vuejs\n59613 vuestorefront\n59614 vuetify\n59615 vyper\n59616 wasm\n59617 webflow\n59618 weblate\n59619 webpack\n59620 webstorm\n59621 windows11\n59622 woocommerce\n59623 xamarin\n59624 xcode\n59625 xd\n59626 xml\n59627 yaml\n59628 yarn\n59629 yugabytedb\n59630 yunohost\n59631 zig\n60000 add\n60001 lightbulb\n60002 repo\n60003 repo forked\n60004 git pull request\n60005 record keys\n60006 tag\n60007 person\n60008 source control\n60009 mirror\n60010 star empty\n60011 comment\n60012 warning\n60013 search\n60014 sign out\n60015 sign in\n60016 eye\n60017 circle filled\n60018 primitive square\n60019 edit\n60020 info\n60021 lock\n60022 close\n60023 sync\n60024 desktop download\n60025 beaker\n60026 vm\n60027 file\n60028 ellipsis\n60029 reply\n60030 organization\n60031 new file\n60032 new folder\n60033 trash\n60034 history\n60035 folder\n60036 github\n60037 terminal\n60038 symbol event\n60039 error\n60040 symbol variable\n60042 symbol array\n60043 symbol namespace\n60044 symbol method\n60047 symbol boolean\n60048 symbol numeric\n60049 symbol structure\n60050 symbol parameter\n60051 symbol key\n60052 go to file\n60053 symbol enum\n60054 symbol ruler\n60055 activate breakpoints\n60056 archive\n60057 arrow both\n60058 arrow down\n60059 arrow left\n60060 arrow right\n60061 arrow small down\n60062 arrow small left\n60063 arrow small right\n60064 arrow small up\n60065 arrow up\n60066 bell\n60067 bold\n60068 book\n60069 bookmark\n60070 debug breakpoint conditional unverified\n60071 debug breakpoint conditional\n60072 debug breakpoint data unverified\n60073 debug breakpoint data\n60074 debug breakpoint log unverified\n60075 debug breakpoint log\n60076 briefcase\n60077 broadcast\n60078 browser\n60079 bug\n60080 calendar\n60081 case sensitive\n60082 check\n60083 checklist\n60084 chevron down\n60085 chevron left\n60086 chevron right\n60087 chevron up\n60088 chrome close\n60089 chrome maximize\n60090 chrome minimize\n60091 chrome restore\n60092 circle\n60093 circle slash\n60094 circuit board\n60095 clear all\n60096 clippy\n60097 close all\n60098 cloud download\n60099 cloud upload\n60100 code\n60101 collapse all\n60102 color mode\n60103 comment discussion\n60105 credit card\n60108 dash\n60109 dashboard\n60110 database\n60111 debug continue\n60112 debug disconnect\n60113 debug pause\n60114 debug restart\n60115 debug start\n60116 debug step into\n60117 debug step out\n60118 debug step over\n60119 debug stop\n60120 debug\n60121 device camera video\n60122 device camera\n60123 device mobile\n60124 diff added\n60125 diff ignored\n60126 diff modified\n60127 diff removed\n60128 diff renamed\n60129 diff\n60130 discard\n60131 editor layout\n60132 empty window\n60133 exclude\n60134 extensions\n60135 eye closed\n60136 file binary\n60137 file code\n60138 file media\n60139 file pdf\n60140 file submodule\n60141 file symlink directory\n60142 file symlink file\n60143 file zip\n60144 files\n60145 filter\n60146 flame\n60147 fold down\n60148 fold up\n60149 fold\n60150 folder active\n60151 folder opened\n60152 gear\n60153 gift\n60154 gist secret\n60156 git commit\n60157 git compare\n60158 git merge\n60159 github action\n60160 github alt\n60161 globe\n60162 grabber\n60163 graph\n60164 gripper\n60165 heart\n60166 home\n60167 horizontal rule\n60168 hubot\n60169 inbox\n60171 issue reopened\n60172 issues\n60173 italic\n60174 jersey\n60175 json\n60176 kebab vertical\n60177 key\n60178 law\n60179 lightbulb autofix\n60180 link external\n60181 link\n60182 list ordered\n60183 list unordered\n60184 live share\n60185 loading\n60186 location\n60187 mail read\n60188 mail\n60189 markdown\n60190 megaphone\n60191 mention\n60192 milestone\n60193 mortar board\n60194 move\n60195 multiple windows\n60196 mute\n60197 no newline\n60198 note\n60199 octoface\n60200 open preview\n60201 package\n60202 paintcan\n60203 pin\n60204 play\n60205 plug\n60206 preserve case\n60207 preview\n60208 project\n60209 pulse\n60210 question\n60211 quote\n60212 radio tower\n60213 reactions\n60214 references\n60215 refresh\n60216 regex\n60217 remote explorer\n60218 remote\n60219 remove\n60220 replace all\n60221 replace\n60222 repo clone\n60223 repo force push\n60224 repo pull\n60225 repo push\n60226 report\n60227 request changes\n60228 rocket\n60229 root folder opened\n60230 root folder\n60231 rss\n60232 ruby\n60233 save all\n60234 save as\n60235 save\n60236 screen full\n60237 screen normal\n60238 search stop\n60240 server\n60241 settings gear\n60242 settings\n60243 shield\n60244 smiley\n60245 sort precedence\n60246 split horizontal\n60247 split vertical\n60248 squirrel\n60249 star full\n60250 star half\n60251 symbol class\n60252 symbol color\n60253 symbol constant\n60254 symbol enum member\n60255 symbol field\n60256 symbol file\n60257 symbol interface\n60258 symbol keyword\n60259 symbol misc\n60260 symbol operator\n60261 symbol property\n60262 symbol snippet\n60263 tasklist\n60264 telescope\n60265 text size\n60266 three bars\n60267 thumbsdown\n60268 thumbsup\n60269 tools\n60270 triangle down\n60271 triangle left\n60272 triangle right\n60273 triangle up\n60274 twitter\n60275 unfold\n60276 unlock\n60277 unmute\n60278 unverified\n60279 verified\n60280 versions\n60281 vm active\n60282 vm outline\n60283 vm running\n60284 watch\n60285 whitespace\n60286 whole word\n60287 window\n60288 word wrap\n60289 zoom in\n60290 zoom out\n60291 list filter\n60292 list flat\n60293 list selection\n60294 list tree\n60295 debug breakpoint function unverified\n60296 debug breakpoint function\n60297 debug stackframe active\n60298 circle small filled\n60299 debug stackframe\n60300 debug breakpoint unsupported\n60301 symbol string\n60302 debug reverse continue\n60303 debug step back\n60304 debug restart frame\n60305 debug alt\n60306 call incoming\n60307 call outgoing\n60308 menu\n60309 expand all\n60310 feedback\n60311 group by ref type\n60312 ungroup by ref type\n60313 account\n60314 bell dot\n60315 debug console\n60316 library\n60317 output\n60318 run all\n60319 sync ignored\n60320 pinned\n60321 github inverted\n60322 server process\n60323 server environment\n60324 pass\n60325 stop circle\n60326 play circle\n60327 record\n60328 debug alt small\n60329 vm connect\n60330 cloud\n60331 merge\n60332 export\n60333 graph left\n60334 magnet\n60335 notebook\n60336 redo\n60337 check all\n60338 pinned dirty\n60339 pass filled\n60340 circle large filled\n60341 circle large\n60342 combine\n60343 table\n60344 variable group\n60345 type hierarchy\n60346 type hierarchy sub\n60347 type hierarchy super\n60348 git pull request create\n60349 run above\n60350 run below\n60351 notebook template\n60352 debug rerun\n60353 workspace trusted\n60354 workspace untrusted\n60355 workspace unknown\n60356 terminal cmd\n60357 terminal debian\n60358 terminal linux\n60359 terminal powershell\n60360 terminal tmux\n60361 terminal ubuntu\n60362 terminal bash\n60363 arrow swap\n60364 copy\n60365 person add\n60366 filter filled\n60367 wand\n60368 debug line by line\n60369 inspect\n60370 layers\n60371 layers dot\n60372 layers active\n60373 compass\n60374 compass dot\n60375 compass active\n60376 azure\n60377 issue draft\n60378 git pull request closed\n60379 git pull request draft\n60380 debug all\n60381 debug coverage\n60382 run errors\n60383 folder library\n60384 debug continue small\n60385 beaker stop\n60386 graph line\n60387 graph scatter\n60388 pie chart\n60389 bracket dot\n60390 bracket error\n60391 lock small\n60392 azure devops\n60393 verified filled\n60394 newline\n60395 layout\n60396 layout activitybar left\n60397 layout activitybar right\n60398 layout panel left\n60399 layout panel center\n60400 layout panel justify\n60401 layout panel right\n60402 layout panel\n60403 layout sidebar left\n60404 layout sidebar right\n60405 layout statusbar\n60406 layout menubar\n60407 layout centered\n60408 target\n60409 indent\n60410 record small\n60411 error small\n60412 arrow circle down\n60413 arrow circle left\n60414 arrow circle right\n60415 arrow circle up\n60416 layout sidebar right off\n60417 layout panel off\n60418 layout sidebar left off\n60419 blank\n60420 heart filled\n60421 map\n60422 map filled\n60423 circle small\n60424 bell slash\n60425 bell slash dot\n60426 comment unresolved\n60427 git pull request go to changes\n60428 git pull request new changes\n60429 search fuzzy\n60430 comment draft\n60431 send\n60432 sparkle\n60433 insert\n60434 mic\n60435 thumbsdown filled\n60436 thumbsup filled\n60437 coffee\n60438 snake\n60439 game\n60440 vr\n60441 chip\n60442 piano\n60443 music\n60444 mic filled\n60445 git fetch\n60446 copilot\n60672 location dot\n60673 medapps\n60674 medrt\n60675 microphone lines\n60676 microsoft\n60677 mix\n60678 mizuni\n60679 mobile button\n60680 mobile\n60681 mobile screen\n60682 monero\n60683 money bill 1\n60684 napster\n60685 node js\n60686 npm\n60687 ns8\n60688 nutritionix\n60689 page4\n60690 palfed\n60691 patreon\n60692 periscope\n60693 phabricator\n60694 phoenix framework\n60695 phone slash\n60696 playstation\n60697 image portrait\n60698 pushed\n60699 python\n60700 red river\n60701 wpressr\n60702 replyd\n60703 resolving\n60704 rocketchat\n60705 rockrms\n60706 schlix\n60707 searchengin\n60708 servicestack\n60709 shield halved\n60710 sistrix\n60711 speakap\n60712 staylinked\n60713 steam symbol\n60714 sticker mule\n60715 studiovinari\n60716 supple\n60717 tablet button\n60718 tablet\n60719 gauge high\n60720 ticket simple\n60721 uber\n60722 uikit\n60723 uniregistry\n60724 untappd\n60725 user large\n60726 ussunnah\n60727 vaadin\n60728 viber\n60729 vimeo\n60730 vnv\n60731 square whatsapp\n60732 whmcs\n60733 wordpress simple\n60734 xbox\n60735 yandex\n60736 yandex international\n60737 apple pay\n60738 cc apple pay\n60739 fly\n60740 node\n60741 osi\n60742 react\n60743 autoprefixer\n60744 less\n60745 sass\n60746 vuejs\n60747 angular\n60748 aviato\n60749 down left and up right to center\n60750 ember\n60751 up right and down left from center\n60752 gitter\n60753 hooli\n60754 strava\n60755 stripe\n60756 stripe s\n60757 typo3\n60758 amazon pay\n60759 cc amazon pay\n60760 ethereum\n60761 korvue\n60762 elementor\n60763 baseball bat ball\n60764 baseball\n60765 basketball\n60766 bowling ball\n60767 chess\n60768 chess bishop\n60769 chess board\n60770 chess king\n60771 chess knight\n60772 chess pawn\n60773 chess queen\n60774 chess rook\n60775 dumbbell\n60776 flipboard\n60777 football\n60778 golf ball tee\n60779 hips\n60780 hockey puck\n60781 php\n60782 broom ball\n60783 quinscape\n60784 square full\n60785 table tennis paddle ball\n60786 volleyball\n60787 hand dots\n60788 bandage\n60789 box\n60790 boxes stacked\n60791 briefcase medical\n60792 fire flame simple\n60793 capsules\n60794 clipboard check\n60795 clipboard list\n60796 person dots from line\n60797 dna\n60798 dolly\n60799 cart flatbed\n60800 file medical\n60801 file waveform\n60802 kit medical\n60803 circle h\n60804 id card clip\n60805 notes medical\n60806 pallet\n60807 pills\n60808 prescription bottle\n60809 prescription bottle medical\n60810 bed pulse\n60811 truck fast\n60812 smoking\n60813 syringe\n60814 tablets\n60815 thermometer alt\n60816 vial\n60817 vials\n60818 warehouse\n60819 weight scale\n60820 x ray\n60821 box open\n60822 comment slash\n60823 couch\n60824 circle dollar to slot\n60825 dove\n60826 hand holding\n60827 hand holding heart\n60828 hand holding dollar\n60829 hand holding droplet\n60830 hands holding\n60831 handshake angle\n60832 handshake simple\n60833 parachute box\n60834 people carry box\n60835 piggy bank\n60836 readme\n60837 ribbon\n60838 route\n60839 seedling\n60840 sign hanging\n60841 face smile wink\n60842 tape\n60843 truck ramp box\n60844 truck moving\n60845 video slash\n60846 wine glass\n60847 java\n60848 pied piper hat\n60849 creative commons by\n60850 creative commons nc\n60851 creative commons nc eu\n60852 creative commons nc jp\n60853 creative commons nd\n60854 creative commons pd\n60855 creative commons pd alt\n60856 creative commons remix\n60857 creative commons sa\n60858 creative commons sampling\n60859 creative commons sampling plus\n60860 creative commons share\n60861 creative commons zero\n60862 ebay\n60863 keybase\n60864 mastodon\n60865 r project\n60866 researchgate\n60867 teamspeak\n60868 user large slash\n60869 user astronaut\n60870 user check\n60871 user clock\n60872 user gear\n60873 user pen\n60874 user group\n60875 user graduate\n60876 user lock\n60877 user minus\n60878 user ninja\n60879 user shield\n60880 user slash\n60881 user tag\n60882 user tie\n60883 users gear\n60884 first order alt\n60885 fulcrum\n60886 galactic republic\n60887 galactic senate\n60888 jedi order\n60889 mandalorian\n60890 old republic\n60891 phoenix squadron\n60892 sith\n60893 trade federation\n60894 wolf pack battalion\n60895 scale unbalanced\n60896 scale unbalanced flip\n60897 blender\n60898 book open\n60899 tower broadcast\n60900 broom\n60901 chalkboard\n60902 chalkboard user\n60903 church\n60904 coins\n60905 compact disc\n60906 crow\n60907 crown\n60908 dice\n60909 dice five\n60910 dice four\n60911 dice one\n60912 dice six\n60913 dice three\n60914 dice two\n60915 divide\n60916 door closed\n60917 door open\n60918 equals\n60919 feather\n60920 frog\n60921 gas pump\n60922 glasses\n60923 greater than\n60924 greater than equal\n60925 helicopter\n60926 infinity\n60927 kiwi bird\n60928 progress empty left\n60929 progress empty mid\n60930 progress empty right\n60931 progress full left\n60932 progress full mid\n60933 progress full right\n60934 progress spinner 1\n60935 progress spinner 2\n60936 progress spinner 3\n60937 progress spinner 4\n60938 progress spinner 5\n60939 progress spinner 6\n60940 receipt\n60941 robot\n60942 ruler\n60943 ruler combined\n60944 ruler horizontal\n60945 ruler vertical\n60946 school\n60947 screwdriver\n60948 shoe prints\n60949 skull\n60950 ban smoking\n60951 store\n60952 shop\n60953 bars staggered\n60954 stroopwafel\n60955 toolbox\n60956 shirt\n60957 person walking\n60958 wallet\n60959 face angry\n60960 archway\n60961 book atlas\n60962 award\n60963 delete left\n60964 bezier curve\n60965 bong\n60966 brush\n60967 bus simple\n60968 cannabis\n60969 check double\n60970 martini glass citrus\n60971 bell concierge\n60972 cookie\n60973 cookie bite\n60974 crop simple\n60975 tachograph digital\n60976 face dizzy\n60977 compass drafting\n60978 drum\n60979 drum steelpan\n60980 feather pointed\n60981 file contract\n60982 file arrow down\n60983 file export\n60984 file import\n60985 file invoice\n60986 file invoice dollar\n60987 file prescription\n60988 file signature\n60989 file arrow up\n60990 fill\n60991 fill drip\n60992 fingerprint\n60993 fish\n60994 face flushed\n60995 face frown open\n60996 martini glass\n60997 earth africa\n60998 earth americas\n60999 earth asia\n61000 face grimace\n61001 face grin\n61002 face grin wide\n61003 face grin beam\n61004 face grin beam sweat\n61005 face grin hearts\n61006 face grin squint\n61007 face grin squint tears\n61008 face grin stars\n61009 face grin tears\n61010 face grin tongue\n61011 face grin tongue squint\n61012 face grin tongue wink\n61013 face grin wink\n61014 grip\n61015 grip vertical\n61016 headphones simple\n61017 headset\n61018 highlighter\n61019 hornbill\n61020 hot tub person\n61021 hotel building\n61022 joint\n61023 face kiss\n61024 face kiss beam\n61025 face kiss wink heart\n61026 face laugh\n61027 face laugh beam\n61028 face laugh squint\n61029 face laugh wink\n61030 cart flatbed suitcase\n61031 mailchimp\n61032 map location\n61033 map location dot\n61034 marker\n61035 medal\n61036 megaport\n61037 face meh blank\n61038 face rolling eyes\n61039 monument\n61040 mortar pestle\n61041 nimblr\n61042 paint roller\n61043 passport\n61044 pen fancy\n61045 pen nib\n61046 pen ruler\n61047 plane arrival\n61048 plane departure\n61049 prescription\n61050 rev\n61051 face sad cry\n61052 face sad tear\n61053 shopware\n61054 van shuttle\n61055 signature\n61056 face smile beam\n61057 solar panel\n61058 spa\n61059 splotch\n61060 spray can\n61061 squarespace\n61062 stamp\n61063 star half stroke\n61064 suitcase rolling\n61065 face surprise\n61066 swatchbook\n61067 person swimming\n61068 water ladder\n61069 themeco\n61070 droplet slash\n61071 face tired\n61072 tooth\n61073 umbrella beach\n61074 vector square\n61075 weebly\n61076 weight hanging\n61077 wine glass empty\n61078 wix\n61079 spray can sparkles\n61080 apple whole\n61081 atom\n61082 bone\n61083 book open reader\n61084 brain\n61085 car rear\n61086 car battery\n61087 car burst\n61088 car side\n61089 charging station\n61090 diamond turn right\n61091 draw polygon\n61092 ello\n61093 hackerrank\n61094 kaggle\n61095 laptop code\n61096 layer group\n61097 location crosshairs\n61098 lungs\n61099 markdown\n61100 microscope\n61101 neos\n61102 oil can\n61103 poop\n61104 shapes\n61105 star of life\n61106 gauge\n61107 gauge simple\n61108 teeth\n61109 teeth open\n61110 masks theater\n61111 traffic light\n61112 truck monster\n61113 truck pickup\n61114 zhihu\n61115 rectangle ad\n61116 alipay\n61117 ankh\n61118 book bible\n61119 business time\n61120 city\n61121 comment dollar\n61122 comments dollar\n61123 cross\n61124 dharmachakra\n61125 envelope open text\n61126 folder minus\n61127 folder plus\n61128 filter circle dollar\n61129 gopuram\n61130 hamsa\n61131 bahai\n61132 jedi\n61133 book journal whills\n61134 kaaba\n61135 khanda\n61136 landmark\n61137 envelopes bulk\n61138 menorah\n61139 mosque\n61140 om\n61141 spaghetti monster flying\n61142 peace\n61143 place of worship\n61144 square poll vertical\n61145 square poll horizontal\n61146 person praying\n61147 hands praying\n61148 book quran\n61149 magnifying glass dollar\n61150 magnifying glass location\n61151 socks\n61152 square root variable\n61153 star and crescent\n61154 star of david\n61155 synagogue\n61156 the red yeti\n61157 scroll torah\n61158 torii gate\n61159 vihara\n61160 volume xmark\n61161 yin yang\n61162 blender phone\n61163 book skull\n61164 campground\n61165 cat\n61166 chair\n61167 cloud moon\n61168 cloud sun\n61169 cow\n61170 critical role\n61171 d and d beyond\n61172 dev\n61173 dice d20\n61174 dice d6\n61175 dog\n61176 dragon\n61177 drumstick bite\n61178 dungeon\n61179 fantasy flight games\n61180 file csv\n61181 hand fist\n61182 ghost\n61183 hammer\n61184 hanukiah\n61185 hat wizard\n61186 person hiking\n61187 hippo\n61188 horse\n61189 house chimney crack\n61190 hryvnia sign\n61191 mask\n61192 mountain\n61193 network wired\n61194 otter\n61195 ring\n61196 person running\n61197 scroll\n61198 skull crossbones\n61199 slash\n61200 spider\n61201 toilet paper\n61202 tractor\n61203 user injured\n61204 vr cardboard\n61205 wand sparkles\n61206 wind\n61207 wine bottle\n61208 wizards of the coast\n61209 think peaks\n61210 cloud meatball\n61211 cloud moon rain\n61212 cloud rain\n61213 cloud showers heavy\n61214 cloud sun rain\n61215 democrat\n61216 flag usa\n61217 hurricane\n61218 landmark dome\n61219 meteor\n61220 person booth\n61221 poo storm\n61222 rainbow\n61223 reacteurope\n61224 republican\n61225 smog\n61226 temperature high\n61227 temperature low\n61228 cloud bolt\n61229 tornado\n61230 volcano\n61231 check to slot\n61232 water\n61233 artstation\n61234 atlassian\n61235 baby\n61236 baby carriage\n61237 biohazard\n61238 blog\n61239 calendar day\n61240 calendar week\n61241 canadian maple leaf\n61242 candy cane\n61243 carrot\n61244 cash register\n61245 centos\n61246 minimize\n61247 confluence\n61248 dhl\n61249 diaspora\n61250 dumpster\n61251 dumpster fire\n61252 ethernet\n61253 fedex\n61254 fedora\n61255 figma\n61256 gifts\n61257 champagne glasses\n61258 whiskey glass\n61259 earth europe\n61260 grip lines\n61261 grip lines vertical\n61262 guitar\n61263 heart crack\n61264 holly berry\n61265 horse head\n61266 icicles\n61267 igloo\n61268 intercom\n61269 invision\n61270 jira\n61271 mendeley\n61272 mitten\n61273 mug hot\n61274 radiation\n61275 circle radiation\n61276 raspberry pi\n61277 redhat\n61278 restroom\n61279 satellite\n61280 satellite dish\n61281 sd card\n61282 sim card\n61283 person skating\n61284 sketch\n61285 person skiing\n61286 person skiing nordic\n61287 sleigh\n61288 comment sms\n61289 person snowboarding\n61290 snowman\n61291 snowplow\n61292 sourcetree\n61293 suse\n61294 tenge sign\n61295 toilet\n61296 screwdriver wrench\n61297 cable car\n61298 ubuntu\n61299 ups\n61300 usps\n61301 yarn\n61302 fire flame curved\n61303 bacon\n61304 book medical\n61305 bread slice\n61306 cheese\n61307 house chimney medical\n61308 clipboard user\n61309 comment medical\n61310 crutch\n61311 disease\n61312 egg\n61313 folder tree\n61314 burger\n61315 hand middle finger\n61316 helmet safety\n61317 house chimney\n61318 hospital user\n61319 hotdog\n61320 ice cream\n61321 laptop medical\n61322 pager\n61323 pepper hot\n61324 pizza slice\n61325 sack dollar\n61326 book tanakh\n61327 bars progress\n61328 trash arrow up\n61329 trash can arrow up\n61330 user nurse\n61331 airbnb\n61332 battle net\n61333 bootstrap\n61334 buffer\n61335 chromecast\n61336 evernote\n61337 itch io\n61338 salesforce\n61339 speaker deck\n61340 symfony\n61341 wave square\n61342 waze\n61343 yammer\n61344 git alt\n61345 stackpath\n61346 person biking\n61347 border all\n61348 border none\n61349 border top left\n61350 person digging\n61351 fan\n61352 icons\n61353 phone flip\n61354 square phone flip\n61355 photo film\n61356 text slash\n61357 arrow down z a\n61358 arrow up z a\n61359 arrow down short wide\n61360 arrow up short wide\n61361 arrow down 9 1\n61362 arrow up 9 1\n61363 spell check\n61364 voicemail\n61365 cotton bureau\n61366 buy n large\n61367 hat cowboy\n61368 hat cowboy side\n61369 mdb\n61370 computer mouse\n61371 orcid\n61372 radio\n61373 record vinyl\n61374 swift\n61375 umbraco\n61376 walkie talkie\n61377 caravan\n61378 avianex\n61379 less than\n61380 less than equal\n61381 memory\n61382 microphone lines slash\n61383 money bill wave\n61384 money bill 1 wave\n61385 money check\n61386 money check dollar\n61387 not equal\n61388 palette\n61389 square parking\n61390 diagram project\n61440 glass\n61441 music\n61442 magnifying glass\n61443 envelope o\n61444 heart\n61445 star\n61446 star o\n61447 user\n61448 film\n61449 table cells large\n61450 table cells\n61451 table list\n61452 check\n61453 close\n61454 magnifying glass plus\n61455 images\n61456 magnifying glass minus\n61457 power off\n61458 signal\n61459 cog\n61460 trash can\n61461 home\n61462 file o\n61463 clock\n61464 road\n61465 download\n61466 arrow circle o down\n61467 arrow circle o up\n61468 inbox\n61469 play circle o\n61470 arrow rotate right\n61471 pen\n61472 pen clip\n61473 arrows rotate\n61474 list alt\n61475 lock\n61476 flag\n61477 headphones\n61478 volume off\n61479 volume down\n61480 volume high\n61481 qrcode\n61482 barcode\n61483 tag\n61484 tags\n61485 book\n61486 bookmark\n61487 print\n61488 camera\n61489 font\n61490 bold\n61491 italic\n61492 text height\n61493 text width\n61494 align left\n61495 align center\n61496 align right\n61497 align justify\n61498 list\n61499 dedent\n61500 indent\n61501 video\n61502 image\n61503 down long\n61504 pencil\n61505 location pin\n61506 adjust\n61507 droplet\n61508 edit\n61509 share square o\n61510 check square o\n61511 arrows\n61512 backward step\n61513 backward fast\n61514 backward\n61515 play\n61516 pause\n61517 stop\n61518 forward\n61519 left long\n61520 fast forward\n61521 forward step\n61522 eject\n61523 chevron left\n61524 chevron right\n61525 circle plus\n61526 circle minus\n61527 remove sign\n61528 check circle\n61529 circle question\n61530 circle info\n61531 crosshairs\n61532 circle xmark\n61533 check circle o\n61534 ban\n61535 file pen\n61536 arrow left\n61537 arrow right\n61538 arrow up\n61539 arrow down\n61540 mail forward\n61541 expand\n61542 compress\n61543 plus\n61544 minus\n61545 asterisk\n61546 circle exclamation\n61547 gift\n61548 leaf\n61549 fire\n61550 eye\n61551 maximize\n61552 eye slash\n61553 exclamation triangle\n61554 plane\n61555 calendar\n61556 random\n61557 comment\n61558 magnet\n61559 chevron up\n61560 chevron down\n61561 retweet\n61562 cart shopping\n61563 folder\n61564 folder open\n61565 arrows up down\n61566 arrows h\n61567 clipboard alt\n61568 bar chart\n61569 square twitter\n61570 facebook square\n61571 camera retro\n61572 key\n61573 cogs\n61574 comments\n61575 thumbs o up\n61576 thumbs o down\n61577 star half\n61578 heard o\n61579 arrow right from bracket\n61580 linkedin square\n61581 thumb tack\n61582 arrow up right from square\n61583 left right\n61584 arrow right to bracket\n61585 trophy\n61586 github square\n61587 upload\n61588 lemon\n61589 phone\n61590 square o\n61591 bookmark o\n61592 phone square\n61593 twitter\n61594 facebook\n61595 github\n61596 unlock\n61597 credit card\n61598 feed\n61599 up down\n61600 hard drive\n61601 bullhorn\n61602 bell o\n61603 certificate\n61604 hand o right\n61605 hand o left\n61606 hand o up\n61607 hand o down\n61608 arrow circle left\n61609 arrow circle right\n61610 arrow circle up\n61611 arrow circle down\n61612 globe\n61613 wrench\n61614 list check\n61615 square font awesome stroke\n61616 filter\n61617 briefcase\n61618 arrows alt\n61619 up right from square\n61620 square up right\n61621 right left\n61622 repeat alt\n61623 accusoft\n61624 adversal\n61625 affiliatetheme\n61626 algolia\n61627 amilia\n61628 angrycreative\n61629 app store\n61630 app store ios\n61631 apper\n61632 group\n61633 chain\n61634 cloud\n61635 flask\n61636 cut\n61637 copy\n61638 paperclip\n61639 floppy disk\n61640 square\n61641 bars\n61642 list ul\n61643 list ol\n61644 strikethrough\n61645 underline\n61646 table\n61647 asymmetrik\n61648 magic\n61649 truck\n61650 pinterest\n61651 pinterest square\n61652 google plus square\n61653 google plus\n61654 money\n61655 caret down\n61656 caret up\n61657 caret left\n61658 caret right\n61659 columns\n61660 sort\n61661 sort desc\n61662 sort asc\n61663 audible\n61664 envelope\n61665 linkedin\n61666 arrow rotate left\n61667 gavel\n61668 dashboard\n61669 comment o\n61670 comments o\n61671 bolt\n61672 sitemap\n61673 umbrella\n61674 clipboard\n61675 lightbulb\n61676 arrow right arrow left\n61677 cloud arrow down\n61678 cloud arrow up\n61679 aws\n61680 user doctor\n61681 stethoscope\n61682 suitcase\n61683 bell\n61684 coffee\n61685 cutlery\n61686 file text o\n61687 building o\n61688 hospital\n61689 ambulance\n61690 medkit\n61691 fighter jet\n61692 beer\n61693 h square\n61694 plus square\n61695 bimobject\n61696 angle double left\n61697 angle double right\n61698 angle double up\n61699 angle double down\n61700 angle left\n61701 angle right\n61702 angle up\n61703 angle down\n61704 desktop\n61705 laptop\n61706 tablet screen button\n61707 mobile phone\n61708 circle o\n61709 quote left\n61710 quote right\n61711 bitcoin\n61712 spinner\n61713 circle\n61714 mail reply\n61715 github alt\n61716 folder o\n61717 folder open o\n61718 bity\n61719 blackberry\n61720 face smile\n61721 face frown\n61722 face meh\n61723 gamepad\n61724 keyboard\n61725 flag o\n61726 flag checkered\n61727 blogger\n61728 terminal\n61729 code\n61730 mail reply all\n61731 star half empty\n61732 location arrow\n61733 crop\n61734 code branch\n61735 chain broken\n61736 question\n61737 info\n61738 exclamation\n61739 superscript\n61740 subscript\n61741 eraser\n61742 puzzle piece\n61743 blogger b\n61744 microphone\n61745 microphone slash\n61746 shield\n61747 calendar o\n61748 fire extinguisher\n61749 rocket\n61750 maxcdn\n61751 chevron circle left\n61752 chevron circle right\n61753 chevron circle up\n61754 chevron circle down\n61755 html5\n61756 css3\n61757 anchor\n61758 unlock alt\n61759 buromobelexperte\n61760 bullseye\n61761 ellipsis\n61762 ellipsis v\n61763 rss square\n61764 circle play\n61765 ticket\n61766 minus square\n61767 minus square o\n61768 arrow turn up\n61769 arrow turn down\n61770 check square\n61771 pencil square\n61772 external link square\n61773 share from square\n61774 compass\n61775 centercode\n61776 caret square o down\n61777 caret square o up\n61778 caret square o right\n61779 eur\n61780 gbp\n61781 dollar\n61782 inr\n61783 cny\n61784 rouble\n61785 krw\n61786 btc\n61787 file\n61788 file lines\n61789 arrow down a z\n61790 arrow up a z\n61791 cloudscale\n61792 arrow down wide short\n61793 arrow up wide short\n61794 arrow down 1 9\n61795 arrow up 1 9\n61796 thumbs up\n61797 thumbs down\n61798 square youtube\n61799 cloudsmith\n61800 xing\n61801 square xing\n61802 youtube\n61803 dropbox\n61804 stack overflow\n61805 instagram\n61806 flickr\n61807 cloudversify\n61808 adn\n61809 bitbucket\n61810 bitbucket square\n61811 tumblr\n61812 square tumblr\n61813 arrow down long\n61814 arrow up long\n61815 arrow left long\n61816 arrow right long\n61817 apple\n61818 windows\n61819 android\n61820 linux\n61821 dribbble\n61822 skype\n61823 code merge\n61824 foursquare\n61825 trello\n61826 female\n61827 male\n61828 gittip\n61829 sun\n61830 moon\n61831 archive\n61832 bug\n61833 vk\n61834 weibo\n61835 renren\n61836 pagelines\n61837 stack exchange\n61838 arrow circle o right\n61839 cpanel\n61840 arrow circle o left\n61841 caret square o left\n61842 circle dot\n61843 wheelchair\n61844 square vimeo\n61845 lira sign\n61846 plus square o\n61847 shuttle space\n61848 slack\n61849 envelope square\n61850 wordpress\n61851 openid\n61852 bank\n61853 graduation cap\n61854 yahoo\n61855 css3 alt\n61856 google\n61857 reddit\n61858 reddit square\n61859 stumbleupon circle\n61860 stumbleupon\n61861 delicious\n61862 digg\n61863 pied piper pp\n61864 pied piper alt\n61865 drupal\n61866 joomla\n61867 language\n61868 fax\n61869 building\n61870 child\n61871 cuttlefish\n61872 paw\n61873 spoon\n61874 cube\n61875 cubes\n61876 behance\n61877 behance square\n61878 steam\n61879 square steam\n61880 recycle\n61881 automobile\n61882 cab\n61883 tree\n61884 spotify\n61885 deviantart\n61886 soundcloud\n61887 d and d\n61888 database\n61889 file pdf\n61890 file word\n61891 file excel\n61892 file powerpoint\n61893 file image\n61894 file archive o\n61895 file audio\n61896 file movie o\n61897 file code\n61898 vine\n61899 codepen\n61900 jsfiddle\n61901 life bouy\n61902 circle notch\n61903 deploydog\n61904 ra\n61905 empire\n61906 git square\n61907 git\n61908 hacker news\n61909 tencent weibo\n61910 qq\n61911 wechat\n61912 paper plane\n61913 paper plane o\n61914 clock rotate left\n61915 circle thin\n61916 header\n61917 paragraph\n61918 sliders\n61919 deskpro\n61920 share alt\n61921 share alt square\n61922 bomb\n61923 futbol\n61924 tty\n61925 binoculars\n61926 plug\n61927 slideshare\n61928 twitch\n61929 yelp\n61930 newspaper\n61931 wifi\n61932 calculator\n61933 paypal\n61934 google wallet\n61935 digital ocean\n61936 cc visa\n61937 cc mastercard\n61938 cc discover\n61939 cc amex\n61940 cc paypal\n61941 cc stripe\n61942 bell slash\n61943 bell slash o\n61944 trash\n61945 copyright\n61946 at\n61947 eye dropper\n61948 paint brush\n61949 birthday cake\n61950 area chart\n61951 discord\n61952 chart pie\n61953 chart line\n61954 lastfm\n61955 lastfm square\n61956 toggle off\n61957 toggle on\n61958 bicycle\n61959 bus\n61960 ioxhost\n61961 angellist\n61962 cc\n61963 ils\n61964 discourse\n61965 buysellads\n61966 connectdevelop\n61967 dochub\n61968 dashcube\n61969 forumbee\n61970 leanpub\n61971 sellsy\n61972 shirtsinbulk\n61973 simplybuilt\n61974 skyatlas\n61975 cart plus\n61976 cart arrow down\n61977 gem\tdiamond\n61978 ship\n61979 user secret\n61980 motorcycle\n61981 street view\n61982 heart pulse\n61983 docker\n61984 draft2digital\n61985 venus\n61986 mars\n61987 mercury\n61988 intersex\n61989 transgender alt\n61990 venus double\n61991 mars double\n61992 venus mars\n61993 mars stroke\n61994 mars stroke up\n61995 mars stroke h\n61996 neuter\n61997 genderless\n61998 square dribbble\n61999 dyalog\n62000 earlybirds\n62001 pinterest p\n62002 whatsapp\n62003 server\n62004 user plus\n62005 user times\n62006 bed\n62007 viacoin\n62008 train\n62009 subway\n62010 medium\n62011 y combinator\n62012 optin monster\n62013 opencart\n62014 expeditedssl\n62015 erlang\n62016 battery\n62017 battery 3\n62018 battery 2\n62019 battery 1\n62020 battery 0\n62021 arrow pointer\n62022 i cursor\n62023 object group\n62024 object ungroup\n62025 note sticky\n62026 sticky note o\n62027 cc jcb\n62028 cc diners club\n62029 clone\n62030 balance scale\n62031 facebook f\n62032 hourglass o\n62033 hourglass 1\n62034 hourglass 2\n62035 hourglass 3\n62036 hourglass\n62037 hand back fist\n62038 hand\n62039 hand scissors\n62040 hand lizard\n62041 hand spock\n62042 hand pointer\n62043 hand peace\n62044 trademark\n62045 registered\n62046 creative commons\n62047 facebook messenger\n62048 gg\n62049 gg circle\n62050 firstdraft\n62051 odnoklassniki\n62052 odnoklassniki square\n62053 get pocket\n62054 wikipedia w\n62055 safari\n62056 chrome\n62057 firefox\n62058 opera\n62059 internet explorer\n62060 television\n62061 contao\n62062 500px\n62063 fonticons fi\n62064 amazon\n62065 calendar plus\n62066 calendar minus\n62067 calendar times o\n62068 calendar check\n62069 industry\n62070 map pin\n62071 map signs\n62072 map o\n62073 map\n62074 commenting\n62075 comment dots\n62076 houzz\n62077 vimeo v\n62078 black tie\n62079 fort awesome alt\n62080 fonticons\n62081 reddit alien\n62082 edge\n62083 credit card alt\n62084 codiepie\n62085 modx\n62086 fort awesome\n62087 usb\n62088 product hunt\n62089 mixcloud\n62090 scribd\n62091 circle pause\n62092 pause circle o\n62093 circle stop\n62094 stop circle o\n62095 freebsd\n62096 bag shopping\n62097 basket shopping\n62098 hashtag\n62099 bluetooth\n62100 bluetooth b\n62101 percent\n62102 gitlab\n62103 wpbeginner\n62104 wpforms\n62105 envira\n62106 universal access\n62107 accessible icon\n62108 question circle o\n62109 blind\n62110 audio description\n62111 diamond\n62112 phone volume\n62113 braille\n62114 assistive listening systems\n62115 american sign language interpreting\n62116 deaf\n62117 glide\n62118 glide g\n62119 hands\n62120 eye low vision\n62121 viadeo\n62122 square viadeo\n62123 snapchat\n62124 gitkraken\n62125 snapchat square\n62126 pied piper\n62127 gofore\n62128 first order\n62129 yoast\n62130 themeisle\n62131 google plus circle\n62132 fa\n62133 handshake\n62134 envelope open\n62135 envelope open o\n62136 linode\n62137 address book\n62138 address book o\n62139 address card\n62140 address card o\n62141 circle user\n62142 user circle o\n62143 goodreads\n62144 user o\n62145 id badge\n62146 drivers license\n62147 drivers license o\n62148 quora\n62149 free code camp\n62150 telegram\n62151 temperature full\n62152 temperature three quarters\n62153 temperature half\n62154 temperature quarter\n62155 temperature empty\n62156 shower\n62157 bath\n62158 podcast\n62159 goodreads g\n62160 window maximize\n62161 window minimize\n62162 window restore\n62163 square xmark\n62164 rectangle xmark\n62165 bandcamp\n62166 grav\n62167 etsy\n62168 imdb\n62169 ravelry\n62170 eercast\n62171 microchip\n62172 snowflake\n62173 superpowers\n62174 wpexplorer\n62175 google drive\n62176 meetup\n62177 google play\n62178 gripfire\n62179 grunt\n62180 gulp\n62181 square hacker news\n62182 hire a helper\n62183 hotjar\n62184 hubspot\n62185 itunes\n62186 rotate left\n62187 itunes note\n62188 jenkins\n62189 joget\n62190 js\n62191 square js\n62192 keycdn\n62193 rotate\n62194 stopwatch\n62195 kickstarter\n62196 kickstarter k\n62197 right from bracket\n62198 right to bracket\n62199 laravel\n62200 turn down\n62201 rotate right\n62202 turn up\n62203 line\n62204 lock open\n62205 lyft\n62206 poo\n62207 magento\n62208 alpine\n62209 aosc\n62210 apple\n62211 archlinux\n62212 centos\n62213 coreos\n62214 debian\n62215 devuan\n62216 docker\n62217 elementary\n62218 fedora\n62219 fedora inverse\n62220 freebsd\n62221 gentoo\n62222 linuxmint\n62223 linuxmint inverse\n62224 mageia\n62225 mandriva\n62226 manjaro\n62227 nixos\n62228 opensuse\n62229 raspberry pi\n62230 redhat\n62231 sabayon\n62232 slackware\n62233 slackware inverse\n62234 tux\n62235 ubuntu\n62236 ubuntu inverse\n62237 almalinux\n62238 archlabs\n62239 artix\n62240 budgie\n62241 deepin\n62242 endeavour\n62243 ferris\n62244 flathub\n62245 gnu guix\n62246 illumos\n62247 kali linux\n62248 openbsd\n62249 parrot\n62250 pop os\n62251 rocky linux\n62252 snappy\n62253 solus\n62254 void\n62255 zorin\n62256 codeberg\n62257 kde neon\n62258 kde plasma\n62259 kubuntu\n62260 kubuntu inverse\n62261 forgejo\n62262 freecad\n62263 garuda\n62264 gimp\n62265 gitea\n62266 hyperbola\n62267 inkscape\n62268 kdenlive\n62269 krita\n62270 lxle\n62271 mxlinux\n62272 parabola\n62273 puppy\n62274 qubesos\n62275 tails\n62276 trisquel\n62277 archcraft\n62278 arcolinux\n62279 biglinux\n62280 crystal\n62281 locos\n62282 xerolinux\n62283 arduino\n62284 kicad\n62285 octoprint\n62286 openscad\n62287 osh\n62288 oshwa\n62289 prusaslicer\n62290 reprap\n62291 riscv\n62292 awesome\n62293 bspwm\n62294 dwm\n62295 enlightenment\n62296 fluxbox\n62297 hyprland\n62298 i3\n62299 jwm\n62300 qtile\n62301 sway\n62302 xmonad\n62303 cinnamon\n62304 freedesktop\n62305 gnome\n62306 gtk\n62307 lxde\n62308 lxqt\n62309 mate\n62310 vanilla\n62311 wayland\n62312 xfce\n62313 xorg\n62314 fdroid\n62315 fosdem\n62316 osi\n62317 wikimedia\n62318 mpv\n62319 neovim\n62320 thunderbird\n62321 tor\n62322 vscodium\n62323 kde\n62324 postmarketos\n62325 qt\n62326 libreoffice\n62327 libreofficebase\n62328 libreofficecalc\n62329 libreofficedraw\n62330 libreofficeimpress\n62331 libreofficemath\n62332 libreofficewriter\n62333 tumbleweed\n62334 leap\n62335 typst\n62336 nobara\n62337 river\n62464 light bulb\n62465 repo\n62466 repo forked\n62467 repo push\n62468 repo pull\n62469 book\n62470 accessibility\n62471 git pull request\n62472 mark github\n62473 download\n62474 upload\n62475 accessibility inset\n62476 alert fill\n62477 file code\n62478 apps\n62479 file media\n62480 file zip\n62481 archive\n62482 tag\n62483 file directory\n62484 file submodule\n62485 person\n62486 arrow both\n62487 git commit\n62488 git branch\n62489 git merge\n62490 mirror\n62491 issue opened\n62492 issue reopened\n62493 issue closed\n62494 star\n62495 comment\n62496 question\n62497 alert\n62498 search\n62499 gear\n62500 arrow down left\n62501 tools\n62502 sign out\n62503 rocket\n62504 rss\n62505 paste\n62506 sign in\n62507 organization\n62508 device mobile\n62509 unfold\n62510 check\n62511 mail\n62512 read\n62513 arrow up\n62514 arrow right\n62515 arrow down\n62516 arrow left\n62517 pin\n62518 gift\n62519 graph\n62520 triangle left\n62521 credit card\n62522 clock\n62523 ruby\n62524 broadcast\n62525 key\n62526 arrow down right\n62527 repo clone\n62528 diff\n62529 eye\n62530 comment discussion\n62531 arrow switch\n62532 dot fill\n62533 square fill\n62534 device camera\n62535 device camera video\n62536 pencil\n62537 info\n62538 triangle right\n62539 triangle down\n62540 link\n62541 plus\n62542 three bars\n62543 code\n62544 location\n62545 list unordered\n62546 list ordered\n62547 quote\n62548 versions\n62549 calendar\n62550 lock\n62551 diff added\n62552 diff removed\n62553 diff modified\n62554 diff renamed\n62555 horizontal rule\n62556 arrow up left\n62557 milestone\n62558 checklist\n62559 megaphone\n62560 chevron right\n62561 bookmark\n62562 sliders\n62563 meter\n62564 history\n62565 link external\n62566 mute\n62567 x\n62568 circle slash\n62569 pulse\n62570 sync\n62571 telescope\n62572 arrow up right\n62573 home\n62574 stop\n62575 bug\n62576 logo github\n62577 file binary\n62578 database\n62579 server\n62580 diff ignored\n62581 ellipsis\n62582 bell fill\n62583 hubot\n62584 bell slash\n62585 blocked\n62586 bookmark fill\n62587 chevron up\n62588 chevron down\n62589 chevron left\n62590 triangle up\n62591 git compare\n62592 logo gist\n62593 file symlink file\n62594 file symlink directory\n62595 squirrel\n62596 globe\n62597 unmute\n62598 mention\n62599 package\n62600 browser\n62601 terminal\n62602 markdown\n62603 dash\n62604 fold\n62605 inbox\n62606 trash\n62607 paintbrush\n62608 flame\n62609 briefcase\n62610 plug\n62611 bookmark slash fill\n62612 mortar board\n62613 law\n62614 thumbsup\n62615 thumbsdown\n62616 desktop download\n62617 beaker\n62618 bell\n62619 cache\n62620 shield\n62621 bold\n62622 check circle\n62623 italic\n62624 tasklist\n62625 verified\n62626 smiley\n62627 unverified\n62628 check circle fill\n62629 file\n62630 grabber\n62631 checkbox\n62632 reply\n62633 device desktop\n62634 circle\n62635 clock fill\n62636 cloud\n62637 cloud offline\n62638 code of conduct\n62639 code review\n62640 code square\n62641 codescan\n62642 codescan checkmark\n62643 codespaces\n62644 columns\n62645 command palette\n62646 commit\n62647 container\n62648 copilot\n62649 copilot error\n62650 copilot warning\n62651 copy\n62652 cpu\n62653 cross reference\n62654 dependabot\n62655 diamond\n62656 discussion closed\n62657 discussion duplicate\n62658 discussion outdated\n62659 dot\n62660 duplicate\n62661 eye closed\n62662 feed discussion\n62663 feed forked\n62664 feed heart\n62665 feed merged\n62666 feed person\n62667 feed repo\n62668 feed rocket\n62669 feed star\n62670 feed tag\n62671 feed trophy\n62672 file added\n62673 file badge\n62674 file diff\n62675 file directory fill\n62676 file directory open fill\n62677 file moved\n62678 file removed\n62679 filter\n62680 fiscal host\n62681 fold down\n62682 fold up\n62683 git merge queue\n62684 git pull request closed\n62685 git pull request draft\n62686 goal\n62687 hash\n62688 heading\n62689 heart fill\n62690 home fill\n62691 hourglass\n62692 id badge\n62693 image\n62694 infinity\n62695 issue draft\n62696 issue tracked by\n62697 issue tracks\n62698 iterations\n62699 kebab horizontal\n62700 key asterisk\n62701 log\n62702 moon\n62703 move to bottom\n62704 move to end\n62705 move to start\n62706 move to top\n62707 multi select\n62708 no entry\n62709 north star\n62710 note\n62711 number\n62712 package dependencies\n62713 package dependents\n62714 paper airplane\n62715 paperclip\n62716 passkey fill\n62717 people\n62718 person add\n62719 person fill\n62720 play\n62721 plus circle\n62722 project\n62723 project roadmap\n62724 project symlink\n62725 project template\n62726 rel file path\n62727 repo deleted\n62728 repo locked\n62729 repo template\n62730 report\n62731 rows\n62732 screen full\n62733 screen normal\n62734 share\n62735 share android\n62736 shield check\n62737 shield lock\n62738 shield slash\n62739 shield x\n62740 sidebar collapse\n62741 sidebar expand\n62742 single select\n62743 skip\n62744 skip fill\n62745 sort asc\n62746 sort desc\n62747 sparkle fill\n62748 sponsor tiers\n62749 square\n62750 stack\n62751 star fill\n62752 stopwatch\n62753 strikethrough\n62754 sun\n62755 tab\n62756 tab external\n62757 table\n62758 telescope fill\n62759 trophy\n62760 typography\n62761 unlink\n62762 unlock\n62763 unread\n62764 video\n62765 webhook\n62766 workflow\n62767 x circle\n62768 x circle fill\n62769 zoom in\n62770 zoom out\n62771 bookmark slash\n63743 <private use, last>\n63744 cjk compatibility ideograph-f900\n63745 cjk compatibility ideograph-f901\n63746 cjk compatibility ideograph-f902\n63747 cjk compatibility ideograph-f903\n63748 cjk compatibility ideograph-f904\n63749 cjk compatibility ideograph-f905\n63750 cjk compatibility ideograph-f906\n63751 cjk compatibility ideograph-f907\n63752 cjk compatibility ideograph-f908\n63753 cjk compatibility ideograph-f909\n63754 cjk compatibility ideograph-f90a\n63755 cjk compatibility ideograph-f90b\n63756 cjk compatibility ideograph-f90c\n63757 cjk compatibility ideograph-f90d\n63758 cjk compatibility ideograph-f90e\n63759 cjk compatibility ideograph-f90f\n63760 cjk compatibility ideograph-f910\n63761 cjk compatibility ideograph-f911\n63762 cjk compatibility ideograph-f912\n63763 cjk compatibility ideograph-f913\n63764 cjk compatibility ideograph-f914\n63765 cjk compatibility ideograph-f915\n63766 cjk compatibility ideograph-f916\n63767 cjk compatibility ideograph-f917\n63768 cjk compatibility ideograph-f918\n63769 cjk compatibility ideograph-f919\n63770 cjk compatibility ideograph-f91a\n63771 cjk compatibility ideograph-f91b\n63772 cjk compatibility ideograph-f91c\n63773 cjk compatibility ideograph-f91d\n63774 cjk compatibility ideograph-f91e\n63775 cjk compatibility ideograph-f91f\n63776 cjk compatibility ideograph-f920\n63777 cjk compatibility ideograph-f921\n63778 cjk compatibility ideograph-f922\n63779 cjk compatibility ideograph-f923\n63780 cjk compatibility ideograph-f924\n63781 cjk compatibility ideograph-f925\n63782 cjk compatibility ideograph-f926\n63783 cjk compatibility ideograph-f927\n63784 cjk compatibility ideograph-f928\n63785 cjk compatibility ideograph-f929\n63786 cjk compatibility ideograph-f92a\n63787 cjk compatibility ideograph-f92b\n63788 cjk compatibility ideograph-f92c\n63789 cjk compatibility ideograph-f92d\n63790 cjk compatibility ideograph-f92e\n63791 cjk compatibility ideograph-f92f\n63792 cjk compatibility ideograph-f930\n63793 cjk compatibility ideograph-f931\n63794 cjk compatibility ideograph-f932\n63795 cjk compatibility ideograph-f933\n63796 cjk compatibility ideograph-f934\n63797 cjk compatibility ideograph-f935\n63798 cjk compatibility ideograph-f936\n63799 cjk compatibility ideograph-f937\n63800 cjk compatibility ideograph-f938\n63801 cjk compatibility ideograph-f939\n63802 cjk compatibility ideograph-f93a\n63803 cjk compatibility ideograph-f93b\n63804 cjk compatibility ideograph-f93c\n63805 cjk compatibility ideograph-f93d\n63806 cjk compatibility ideograph-f93e\n63807 cjk compatibility ideograph-f93f\n63808 cjk compatibility ideograph-f940\n63809 cjk compatibility ideograph-f941\n63810 cjk compatibility ideograph-f942\n63811 cjk compatibility ideograph-f943\n63812 cjk compatibility ideograph-f944\n63813 cjk compatibility ideograph-f945\n63814 cjk compatibility ideograph-f946\n63815 cjk compatibility ideograph-f947\n63816 cjk compatibility ideograph-f948\n63817 cjk compatibility ideograph-f949\n63818 cjk compatibility ideograph-f94a\n63819 cjk compatibility ideograph-f94b\n63820 cjk compatibility ideograph-f94c\n63821 cjk compatibility ideograph-f94d\n63822 cjk compatibility ideograph-f94e\n63823 cjk compatibility ideograph-f94f\n63824 cjk compatibility ideograph-f950\n63825 cjk compatibility ideograph-f951\n63826 cjk compatibility ideograph-f952\n63827 cjk compatibility ideograph-f953\n63828 cjk compatibility ideograph-f954\n63829 cjk compatibility ideograph-f955\n63830 cjk compatibility ideograph-f956\n63831 cjk compatibility ideograph-f957\n63832 cjk compatibility ideograph-f958\n63833 cjk compatibility ideograph-f959\n63834 cjk compatibility ideograph-f95a\n63835 cjk compatibility ideograph-f95b\n63836 cjk compatibility ideograph-f95c\n63837 cjk compatibility ideograph-f95d\n63838 cjk compatibility ideograph-f95e\n63839 cjk compatibility ideograph-f95f\n63840 cjk compatibility ideograph-f960\n63841 cjk compatibility ideograph-f961\n63842 cjk compatibility ideograph-f962\n63843 cjk compatibility ideograph-f963\n63844 cjk compatibility ideograph-f964\n63845 cjk compatibility ideograph-f965\n63846 cjk compatibility ideograph-f966\n63847 cjk compatibility ideograph-f967\n63848 cjk compatibility ideograph-f968\n63849 cjk compatibility ideograph-f969\n63850 cjk compatibility ideograph-f96a\n63851 cjk compatibility ideograph-f96b\n63852 cjk compatibility ideograph-f96c\n63853 cjk compatibility ideograph-f96d\n63854 cjk compatibility ideograph-f96e\n63855 cjk compatibility ideograph-f96f\n63856 cjk compatibility ideograph-f970\n63857 cjk compatibility ideograph-f971\n63858 cjk compatibility ideograph-f972\n63859 cjk compatibility ideograph-f973\n63860 cjk compatibility ideograph-f974\n63861 cjk compatibility ideograph-f975\n63862 cjk compatibility ideograph-f976\n63863 cjk compatibility ideograph-f977\n63864 cjk compatibility ideograph-f978\n63865 cjk compatibility ideograph-f979\n63866 cjk compatibility ideograph-f97a\n63867 cjk compatibility ideograph-f97b\n63868 cjk compatibility ideograph-f97c\n63869 cjk compatibility ideograph-f97d\n63870 cjk compatibility ideograph-f97e\n63871 cjk compatibility ideograph-f97f\n63872 cjk compatibility ideograph-f980\n63873 cjk compatibility ideograph-f981\n63874 cjk compatibility ideograph-f982\n63875 cjk compatibility ideograph-f983\n63876 cjk compatibility ideograph-f984\n63877 cjk compatibility ideograph-f985\n63878 cjk compatibility ideograph-f986\n63879 cjk compatibility ideograph-f987\n63880 cjk compatibility ideograph-f988\n63881 cjk compatibility ideograph-f989\n63882 cjk compatibility ideograph-f98a\n63883 cjk compatibility ideograph-f98b\n63884 cjk compatibility ideograph-f98c\n63885 cjk compatibility ideograph-f98d\n63886 cjk compatibility ideograph-f98e\n63887 cjk compatibility ideograph-f98f\n63888 cjk compatibility ideograph-f990\n63889 cjk compatibility ideograph-f991\n63890 cjk compatibility ideograph-f992\n63891 cjk compatibility ideograph-f993\n63892 cjk compatibility ideograph-f994\n63893 cjk compatibility ideograph-f995\n63894 cjk compatibility ideograph-f996\n63895 cjk compatibility ideograph-f997\n63896 cjk compatibility ideograph-f998\n63897 cjk compatibility ideograph-f999\n63898 cjk compatibility ideograph-f99a\n63899 cjk compatibility ideograph-f99b\n63900 cjk compatibility ideograph-f99c\n63901 cjk compatibility ideograph-f99d\n63902 cjk compatibility ideograph-f99e\n63903 cjk compatibility ideograph-f99f\n63904 cjk compatibility ideograph-f9a0\n63905 cjk compatibility ideograph-f9a1\n63906 cjk compatibility ideograph-f9a2\n63907 cjk compatibility ideograph-f9a3\n63908 cjk compatibility ideograph-f9a4\n63909 cjk compatibility ideograph-f9a5\n63910 cjk compatibility ideograph-f9a6\n63911 cjk compatibility ideograph-f9a7\n63912 cjk compatibility ideograph-f9a8\n63913 cjk compatibility ideograph-f9a9\n63914 cjk compatibility ideograph-f9aa\n63915 cjk compatibility ideograph-f9ab\n63916 cjk compatibility ideograph-f9ac\n63917 cjk compatibility ideograph-f9ad\n63918 cjk compatibility ideograph-f9ae\n63919 cjk compatibility ideograph-f9af\n63920 cjk compatibility ideograph-f9b0\n63921 cjk compatibility ideograph-f9b1\n63922 cjk compatibility ideograph-f9b2\n63923 cjk compatibility ideograph-f9b3\n63924 cjk compatibility ideograph-f9b4\n63925 cjk compatibility ideograph-f9b5\n63926 cjk compatibility ideograph-f9b6\n63927 cjk compatibility ideograph-f9b7\n63928 cjk compatibility ideograph-f9b8\n63929 cjk compatibility ideograph-f9b9\n63930 cjk compatibility ideograph-f9ba\n63931 cjk compatibility ideograph-f9bb\n63932 cjk compatibility ideograph-f9bc\n63933 cjk compatibility ideograph-f9bd\n63934 cjk compatibility ideograph-f9be\n63935 cjk compatibility ideograph-f9bf\n63936 cjk compatibility ideograph-f9c0\n63937 cjk compatibility ideograph-f9c1\n63938 cjk compatibility ideograph-f9c2\n63939 cjk compatibility ideograph-f9c3\n63940 cjk compatibility ideograph-f9c4\n63941 cjk compatibility ideograph-f9c5\n63942 cjk compatibility ideograph-f9c6\n63943 cjk compatibility ideograph-f9c7\n63944 cjk compatibility ideograph-f9c8\n63945 cjk compatibility ideograph-f9c9\n63946 cjk compatibility ideograph-f9ca\n63947 cjk compatibility ideograph-f9cb\n63948 cjk compatibility ideograph-f9cc\n63949 cjk compatibility ideograph-f9cd\n63950 cjk compatibility ideograph-f9ce\n63951 cjk compatibility ideograph-f9cf\n63952 cjk compatibility ideograph-f9d0\n63953 cjk compatibility ideograph-f9d1\n63954 cjk compatibility ideograph-f9d2\n63955 cjk compatibility ideograph-f9d3\n63956 cjk compatibility ideograph-f9d4\n63957 cjk compatibility ideograph-f9d5\n63958 cjk compatibility ideograph-f9d6\n63959 cjk compatibility ideograph-f9d7\n63960 cjk compatibility ideograph-f9d8\n63961 cjk compatibility ideograph-f9d9\n63962 cjk compatibility ideograph-f9da\n63963 cjk compatibility ideograph-f9db\n63964 cjk compatibility ideograph-f9dc\n63965 cjk compatibility ideograph-f9dd\n63966 cjk compatibility ideograph-f9de\n63967 cjk compatibility ideograph-f9df\n63968 cjk compatibility ideograph-f9e0\n63969 cjk compatibility ideograph-f9e1\n63970 cjk compatibility ideograph-f9e2\n63971 cjk compatibility ideograph-f9e3\n63972 cjk compatibility ideograph-f9e4\n63973 cjk compatibility ideograph-f9e5\n63974 cjk compatibility ideograph-f9e6\n63975 cjk compatibility ideograph-f9e7\n63976 cjk compatibility ideograph-f9e8\n63977 cjk compatibility ideograph-f9e9\n63978 cjk compatibility ideograph-f9ea\n63979 cjk compatibility ideograph-f9eb\n63980 cjk compatibility ideograph-f9ec\n63981 cjk compatibility ideograph-f9ed\n63982 cjk compatibility ideograph-f9ee\n63983 cjk compatibility ideograph-f9ef\n63984 cjk compatibility ideograph-f9f0\n63985 cjk compatibility ideograph-f9f1\n63986 cjk compatibility ideograph-f9f2\n63987 cjk compatibility ideograph-f9f3\n63988 cjk compatibility ideograph-f9f4\n63989 cjk compatibility ideograph-f9f5\n63990 cjk compatibility ideograph-f9f6\n63991 cjk compatibility ideograph-f9f7\n63992 cjk compatibility ideograph-f9f8\n63993 cjk compatibility ideograph-f9f9\n63994 cjk compatibility ideograph-f9fa\n63995 cjk compatibility ideograph-f9fb\n63996 cjk compatibility ideograph-f9fc\n63997 cjk compatibility ideograph-f9fd\n63998 cjk compatibility ideograph-f9fe\n63999 cjk compatibility ideograph-f9ff\n64000 cjk compatibility ideograph-fa00\n64001 cjk compatibility ideograph-fa01\n64002 cjk compatibility ideograph-fa02\n64003 cjk compatibility ideograph-fa03\n64004 cjk compatibility ideograph-fa04\n64005 cjk compatibility ideograph-fa05\n64006 cjk compatibility ideograph-fa06\n64007 cjk compatibility ideograph-fa07\n64008 cjk compatibility ideograph-fa08\n64009 cjk compatibility ideograph-fa09\n64010 cjk compatibility ideograph-fa0a\n64011 cjk compatibility ideograph-fa0b\n64012 cjk compatibility ideograph-fa0c\n64013 cjk compatibility ideograph-fa0d\n64014 cjk compatibility ideograph-fa0e\n64015 cjk compatibility ideograph-fa0f\n64016 cjk compatibility ideograph-fa10\n64017 cjk compatibility ideograph-fa11\n64018 cjk compatibility ideograph-fa12\n64019 cjk compatibility ideograph-fa13\n64020 cjk compatibility ideograph-fa14\n64021 cjk compatibility ideograph-fa15\n64022 cjk compatibility ideograph-fa16\n64023 cjk compatibility ideograph-fa17\n64024 cjk compatibility ideograph-fa18\n64025 cjk compatibility ideograph-fa19\n64026 cjk compatibility ideograph-fa1a\n64027 cjk compatibility ideograph-fa1b\n64028 cjk compatibility ideograph-fa1c\n64029 cjk compatibility ideograph-fa1d\n64030 cjk compatibility ideograph-fa1e\n64031 cjk compatibility ideograph-fa1f\n64032 cjk compatibility ideograph-fa20\n64033 cjk compatibility ideograph-fa21\n64034 cjk compatibility ideograph-fa22\n64035 cjk compatibility ideograph-fa23\n64036 cjk compatibility ideograph-fa24\n64037 cjk compatibility ideograph-fa25\n64038 cjk compatibility ideograph-fa26\n64039 cjk compatibility ideograph-fa27\n64040 cjk compatibility ideograph-fa28\n64041 cjk compatibility ideograph-fa29\n64042 cjk compatibility ideograph-fa2a\n64043 cjk compatibility ideograph-fa2b\n64044 cjk compatibility ideograph-fa2c\n64045 cjk compatibility ideograph-fa2d\n64046 cjk compatibility ideograph-fa2e\n64047 cjk compatibility ideograph-fa2f\n64048 cjk compatibility ideograph-fa30\n64049 cjk compatibility ideograph-fa31\n64050 cjk compatibility ideograph-fa32\n64051 cjk compatibility ideograph-fa33\n64052 cjk compatibility ideograph-fa34\n64053 cjk compatibility ideograph-fa35\n64054 cjk compatibility ideograph-fa36\n64055 cjk compatibility ideograph-fa37\n64056 cjk compatibility ideograph-fa38\n64057 cjk compatibility ideograph-fa39\n64058 cjk compatibility ideograph-fa3a\n64059 cjk compatibility ideograph-fa3b\n64060 cjk compatibility ideograph-fa3c\n64061 cjk compatibility ideograph-fa3d\n64062 cjk compatibility ideograph-fa3e\n64063 cjk compatibility ideograph-fa3f\n64064 cjk compatibility ideograph-fa40\n64065 cjk compatibility ideograph-fa41\n64066 cjk compatibility ideograph-fa42\n64067 cjk compatibility ideograph-fa43\n64068 cjk compatibility ideograph-fa44\n64069 cjk compatibility ideograph-fa45\n64070 cjk compatibility ideograph-fa46\n64071 cjk compatibility ideograph-fa47\n64072 cjk compatibility ideograph-fa48\n64073 cjk compatibility ideograph-fa49\n64074 cjk compatibility ideograph-fa4a\n64075 cjk compatibility ideograph-fa4b\n64076 cjk compatibility ideograph-fa4c\n64077 cjk compatibility ideograph-fa4d\n64078 cjk compatibility ideograph-fa4e\n64079 cjk compatibility ideograph-fa4f\n64080 cjk compatibility ideograph-fa50\n64081 cjk compatibility ideograph-fa51\n64082 cjk compatibility ideograph-fa52\n64083 cjk compatibility ideograph-fa53\n64084 cjk compatibility ideograph-fa54\n64085 cjk compatibility ideograph-fa55\n64086 cjk compatibility ideograph-fa56\n64087 cjk compatibility ideograph-fa57\n64088 cjk compatibility ideograph-fa58\n64089 cjk compatibility ideograph-fa59\n64090 cjk compatibility ideograph-fa5a\n64091 cjk compatibility ideograph-fa5b\n64092 cjk compatibility ideograph-fa5c\n64093 cjk compatibility ideograph-fa5d\n64094 cjk compatibility ideograph-fa5e\n64095 cjk compatibility ideograph-fa5f\n64096 cjk compatibility ideograph-fa60\n64097 cjk compatibility ideograph-fa61\n64098 cjk compatibility ideograph-fa62\n64099 cjk compatibility ideograph-fa63\n64100 cjk compatibility ideograph-fa64\n64101 cjk compatibility ideograph-fa65\n64102 cjk compatibility ideograph-fa66\n64103 cjk compatibility ideograph-fa67\n64104 cjk compatibility ideograph-fa68\n64105 cjk compatibility ideograph-fa69\n64106 cjk compatibility ideograph-fa6a\n64107 cjk compatibility ideograph-fa6b\n64108 cjk compatibility ideograph-fa6c\n64109 cjk compatibility ideograph-fa6d\n64112 cjk compatibility ideograph-fa70\n64113 cjk compatibility ideograph-fa71\n64114 cjk compatibility ideograph-fa72\n64115 cjk compatibility ideograph-fa73\n64116 cjk compatibility ideograph-fa74\n64117 cjk compatibility ideograph-fa75\n64118 cjk compatibility ideograph-fa76\n64119 cjk compatibility ideograph-fa77\n64120 cjk compatibility ideograph-fa78\n64121 cjk compatibility ideograph-fa79\n64122 cjk compatibility ideograph-fa7a\n64123 cjk compatibility ideograph-fa7b\n64124 cjk compatibility ideograph-fa7c\n64125 cjk compatibility ideograph-fa7d\n64126 cjk compatibility ideograph-fa7e\n64127 cjk compatibility ideograph-fa7f\n64128 cjk compatibility ideograph-fa80\n64129 cjk compatibility ideograph-fa81\n64130 cjk compatibility ideograph-fa82\n64131 cjk compatibility ideograph-fa83\n64132 cjk compatibility ideograph-fa84\n64133 cjk compatibility ideograph-fa85\n64134 cjk compatibility ideograph-fa86\n64135 cjk compatibility ideograph-fa87\n64136 cjk compatibility ideograph-fa88\n64137 cjk compatibility ideograph-fa89\n64138 cjk compatibility ideograph-fa8a\n64139 cjk compatibility ideograph-fa8b\n64140 cjk compatibility ideograph-fa8c\n64141 cjk compatibility ideograph-fa8d\n64142 cjk compatibility ideograph-fa8e\n64143 cjk compatibility ideograph-fa8f\n64144 cjk compatibility ideograph-fa90\n64145 cjk compatibility ideograph-fa91\n64146 cjk compatibility ideograph-fa92\n64147 cjk compatibility ideograph-fa93\n64148 cjk compatibility ideograph-fa94\n64149 cjk compatibility ideograph-fa95\n64150 cjk compatibility ideograph-fa96\n64151 cjk compatibility ideograph-fa97\n64152 cjk compatibility ideograph-fa98\n64153 cjk compatibility ideograph-fa99\n64154 cjk compatibility ideograph-fa9a\n64155 cjk compatibility ideograph-fa9b\n64156 cjk compatibility ideograph-fa9c\n64157 cjk compatibility ideograph-fa9d\n64158 cjk compatibility ideograph-fa9e\n64159 cjk compatibility ideograph-fa9f\n64160 cjk compatibility ideograph-faa0\n64161 cjk compatibility ideograph-faa1\n64162 cjk compatibility ideograph-faa2\n64163 cjk compatibility ideograph-faa3\n64164 cjk compatibility ideograph-faa4\n64165 cjk compatibility ideograph-faa5\n64166 cjk compatibility ideograph-faa6\n64167 cjk compatibility ideograph-faa7\n64168 cjk compatibility ideograph-faa8\n64169 cjk compatibility ideograph-faa9\n64170 cjk compatibility ideograph-faaa\n64171 cjk compatibility ideograph-faab\n64172 cjk compatibility ideograph-faac\n64173 cjk compatibility ideograph-faad\n64174 cjk compatibility ideograph-faae\n64175 cjk compatibility ideograph-faaf\n64176 cjk compatibility ideograph-fab0\n64177 cjk compatibility ideograph-fab1\n64178 cjk compatibility ideograph-fab2\n64179 cjk compatibility ideograph-fab3\n64180 cjk compatibility ideograph-fab4\n64181 cjk compatibility ideograph-fab5\n64182 cjk compatibility ideograph-fab6\n64183 cjk compatibility ideograph-fab7\n64184 cjk compatibility ideograph-fab8\n64185 cjk compatibility ideograph-fab9\n64186 cjk compatibility ideograph-faba\n64187 cjk compatibility ideograph-fabb\n64188 cjk compatibility ideograph-fabc\n64189 cjk compatibility ideograph-fabd\n64190 cjk compatibility ideograph-fabe\n64191 cjk compatibility ideograph-fabf\n64192 cjk compatibility ideograph-fac0\n64193 cjk compatibility ideograph-fac1\n64194 cjk compatibility ideograph-fac2\n64195 cjk compatibility ideograph-fac3\n64196 cjk compatibility ideograph-fac4\n64197 cjk compatibility ideograph-fac5\n64198 cjk compatibility ideograph-fac6\n64199 cjk compatibility ideograph-fac7\n64200 cjk compatibility ideograph-fac8\n64201 cjk compatibility ideograph-fac9\n64202 cjk compatibility ideograph-faca\n64203 cjk compatibility ideograph-facb\n64204 cjk compatibility ideograph-facc\n64205 cjk compatibility ideograph-facd\n64206 cjk compatibility ideograph-face\n64207 cjk compatibility ideograph-facf\n64208 cjk compatibility ideograph-fad0\n64209 cjk compatibility ideograph-fad1\n64210 cjk compatibility ideograph-fad2\n64211 cjk compatibility ideograph-fad3\n64212 cjk compatibility ideograph-fad4\n64213 cjk compatibility ideograph-fad5\n64214 cjk compatibility ideograph-fad6\n64215 cjk compatibility ideograph-fad7\n64216 cjk compatibility ideograph-fad8\n64217 cjk compatibility ideograph-fad9\n64256 latin small ligature ff\tfflig\n64257 latin small ligature fi\tfilig\n64258 latin small ligature fl\tfllig\n64259 latin small ligature ffi\tffilig\n64260 latin small ligature ffl\tffllig\n64261 latin small ligature long s t\n64262 latin small ligature st\n64275 armenian small ligature men now\n64276 armenian small ligature men ech\n64277 armenian small ligature men ini\n64278 armenian small ligature vew now\n64279 armenian small ligature men xeh\n64285 hebrew letter yod with hiriq\n64286 hebrew point judeo-spanish varika\n64287 hebrew ligature yiddish yod yod patah\n64288 hebrew letter alternative ayin\n64289 hebrew letter wide alef\n64290 hebrew letter wide dalet\n64291 hebrew letter wide he\n64292 hebrew letter wide kaf\n64293 hebrew letter wide lamed\n64294 hebrew letter wide final mem\n64295 hebrew letter wide resh\n64296 hebrew letter wide tav\n64297 hebrew letter alternative plus sign\n64298 hebrew letter shin with shin dot\n64299 hebrew letter shin with sin dot\n64300 hebrew letter shin with dagesh and shin dot\n64301 hebrew letter shin with dagesh and sin dot\n64302 hebrew letter alef with patah\n64303 hebrew letter alef with qamats\n64304 hebrew letter alef with mapiq\n64305 hebrew letter bet with dagesh\n64306 hebrew letter gimel with dagesh\n64307 hebrew letter dalet with dagesh\n64308 hebrew letter he with mapiq\n64309 hebrew letter vav with dagesh\n64310 hebrew letter zayin with dagesh\n64312 hebrew letter tet with dagesh\n64313 hebrew letter yod with dagesh\n64314 hebrew letter final kaf with dagesh\n64315 hebrew letter kaf with dagesh\n64316 hebrew letter lamed with dagesh\n64318 hebrew letter mem with dagesh\n64320 hebrew letter nun with dagesh\n64321 hebrew letter samekh with dagesh\n64323 hebrew letter final pe with dagesh\n64324 hebrew letter pe with dagesh\n64326 hebrew letter tsadi with dagesh\n64327 hebrew letter qof with dagesh\n64328 hebrew letter resh with dagesh\n64329 hebrew letter shin with dagesh\n64330 hebrew letter tav with dagesh\n64331 hebrew letter vav with holam\n64332 hebrew letter bet with rafe\n64333 hebrew letter kaf with rafe\n64334 hebrew letter pe with rafe\n64335 hebrew ligature alef lamed\n64336 arabic letter alef wasla isolated form\n64337 arabic letter alef wasla final form\n64338 arabic letter beeh isolated form\n64339 arabic letter beeh final form\n64340 arabic letter beeh initial form\n64341 arabic letter beeh medial form\n64342 arabic letter peh isolated form\n64343 arabic letter peh final form\n64344 arabic letter peh initial form\n64345 arabic letter peh medial form\n64346 arabic letter beheh isolated form\n64347 arabic letter beheh final form\n64348 arabic letter beheh initial form\n64349 arabic letter beheh medial form\n64350 arabic letter tteheh isolated form\n64351 arabic letter tteheh final form\n64352 arabic letter tteheh initial form\n64353 arabic letter tteheh medial form\n64354 arabic letter teheh isolated form\n64355 arabic letter teheh final form\n64356 arabic letter teheh initial form\n64357 arabic letter teheh medial form\n64358 arabic letter tteh isolated form\n64359 arabic letter tteh final form\n64360 arabic letter tteh initial form\n64361 arabic letter tteh medial form\n64362 arabic letter veh isolated form\n64363 arabic letter veh final form\n64364 arabic letter veh initial form\n64365 arabic letter veh medial form\n64366 arabic letter peheh isolated form\n64367 arabic letter peheh final form\n64368 arabic letter peheh initial form\n64369 arabic letter peheh medial form\n64370 arabic letter dyeh isolated form\n64371 arabic letter dyeh final form\n64372 arabic letter dyeh initial form\n64373 arabic letter dyeh medial form\n64374 arabic letter nyeh isolated form\n64375 arabic letter nyeh final form\n64376 arabic letter nyeh initial form\n64377 arabic letter nyeh medial form\n64378 arabic letter tcheh isolated form\n64379 arabic letter tcheh final form\n64380 arabic letter tcheh initial form\n64381 arabic letter tcheh medial form\n64382 arabic letter tcheheh isolated form\n64383 arabic letter tcheheh final form\n64384 arabic letter tcheheh initial form\n64385 arabic letter tcheheh medial form\n64386 arabic letter ddahal isolated form\n64387 arabic letter ddahal final form\n64388 arabic letter dahal isolated form\n64389 arabic letter dahal final form\n64390 arabic letter dul isolated form\n64391 arabic letter dul final form\n64392 arabic letter ddal isolated form\n64393 arabic letter ddal final form\n64394 arabic letter jeh isolated form\n64395 arabic letter jeh final form\n64396 arabic letter rreh isolated form\n64397 arabic letter rreh final form\n64398 arabic letter keheh isolated form\n64399 arabic letter keheh final form\n64400 arabic letter keheh initial form\n64401 arabic letter keheh medial form\n64402 arabic letter gaf isolated form\n64403 arabic letter gaf final form\n64404 arabic letter gaf initial form\n64405 arabic letter gaf medial form\n64406 arabic letter gueh isolated form\n64407 arabic letter gueh final form\n64408 arabic letter gueh initial form\n64409 arabic letter gueh medial form\n64410 arabic letter ngoeh isolated form\n64411 arabic letter ngoeh final form\n64412 arabic letter ngoeh initial form\n64413 arabic letter ngoeh medial form\n64414 arabic letter noon ghunna isolated form\n64415 arabic letter noon ghunna final form\n64416 arabic letter rnoon isolated form\n64417 arabic letter rnoon final form\n64418 arabic letter rnoon initial form\n64419 arabic letter rnoon medial form\n64420 arabic letter heh with yeh above isolated form\n64421 arabic letter heh with yeh above final form\n64422 arabic letter heh goal isolated form\n64423 arabic letter heh goal final form\n64424 arabic letter heh goal initial form\n64425 arabic letter heh goal medial form\n64426 arabic letter heh doachashmee isolated form\n64427 arabic letter heh doachashmee final form\n64428 arabic letter heh doachashmee initial form\n64429 arabic letter heh doachashmee medial form\n64430 arabic letter yeh barree isolated form\n64431 arabic letter yeh barree final form\n64432 arabic letter yeh barree with hamza above isolated form\n64433 arabic letter yeh barree with hamza above final form\n64434 arabic symbol dot above\n64435 arabic symbol dot below\n64436 arabic symbol two dots above\n64437 arabic symbol two dots below\n64438 arabic symbol three dots above\n64439 arabic symbol three dots below\n64440 arabic symbol three dots pointing downwards above\n64441 arabic symbol three dots pointing downwards below\n64442 arabic symbol four dots above\n64443 arabic symbol four dots below\n64444 arabic symbol double vertical bar below\n64445 arabic symbol two dots vertically above\n64446 arabic symbol two dots vertically below\n64447 arabic symbol ring\n64448 arabic symbol small tah above\n64449 arabic symbol small tah below\n64450 arabic symbol wasla above\n64451 arabic ligature jalla wa-alaa\n64452 arabic ligature daamat barakaatuhum\n64453 arabic ligature rahmatu allaahi taaalaa alayh\n64454 arabic ligature rahmatu allaahi alayhim\n64455 arabic ligature rahmatu allaahi alayhimaa\n64456 arabic ligature rahimahum allaahu taaalaa\n64457 arabic ligature rahimahumaa allaah\n64458 arabic ligature rahimahumaa allaahu taaalaa\n64459 arabic ligature radi allaahu taaalaa anhum\n64460 arabic ligature hafizahu allaah\n64461 arabic ligature hafizahu allaahu taaalaa\n64462 arabic ligature hafizahum allaahu taaalaa\n64463 arabic ligature hafizahumaa allaahu taaalaa\n64464 arabic ligature sallallaahu taaalaa alayhi wa-sallam\n64465 arabic ligature ajjal allaahu farajahu ash-shareef\n64466 arabic ligature alayhi ar-rahmah\n64467 arabic letter ng isolated form\n64468 arabic letter ng final form\n64469 arabic letter ng initial form\n64470 arabic letter ng medial form\n64471 arabic letter u isolated form\n64472 arabic letter u final form\n64473 arabic letter oe isolated form\n64474 arabic letter oe final form\n64475 arabic letter yu isolated form\n64476 arabic letter yu final form\n64477 arabic letter u with hamza above isolated form\n64478 arabic letter ve isolated form\n64479 arabic letter ve final form\n64480 arabic letter kirghiz oe isolated form\n64481 arabic letter kirghiz oe final form\n64482 arabic letter kirghiz yu isolated form\n64483 arabic letter kirghiz yu final form\n64484 arabic letter e isolated form\n64485 arabic letter e final form\n64486 arabic letter e initial form\n64487 arabic letter e medial form\n64488 arabic letter uighur kazakh kirghiz alef maksura initial form\n64489 arabic letter uighur kazakh kirghiz alef maksura medial form\n64490 arabic ligature yeh with hamza above with alef isolated form\n64491 arabic ligature yeh with hamza above with alef final form\n64492 arabic ligature yeh with hamza above with ae isolated form\n64493 arabic ligature yeh with hamza above with ae final form\n64494 arabic ligature yeh with hamza above with waw isolated form\n64495 arabic ligature yeh with hamza above with waw final form\n64496 arabic ligature yeh with hamza above with u isolated form\n64497 arabic ligature yeh with hamza above with u final form\n64498 arabic ligature yeh with hamza above with oe isolated form\n64499 arabic ligature yeh with hamza above with oe final form\n64500 arabic ligature yeh with hamza above with yu isolated form\n64501 arabic ligature yeh with hamza above with yu final form\n64502 arabic ligature yeh with hamza above with e isolated form\n64503 arabic ligature yeh with hamza above with e final form\n64504 arabic ligature yeh with hamza above with e initial form\n64505 arabic ligature uighur kirghiz yeh with hamza above with alef maksura isolated form\n64506 arabic ligature uighur kirghiz yeh with hamza above with alef maksura final form\n64507 arabic ligature uighur kirghiz yeh with hamza above with alef maksura initial form\n64508 arabic letter farsi yeh isolated form\n64509 arabic letter farsi yeh final form\n64510 arabic letter farsi yeh initial form\n64511 arabic letter farsi yeh medial form\n64512 arabic ligature yeh with hamza above with jeem isolated form\n64513 arabic ligature yeh with hamza above with hah isolated form\n64514 arabic ligature yeh with hamza above with meem isolated form\n64515 arabic ligature yeh with hamza above with alef maksura isolated form\n64516 arabic ligature yeh with hamza above with yeh isolated form\n64517 arabic ligature beh with jeem isolated form\n64518 arabic ligature beh with hah isolated form\n64519 arabic ligature beh with khah isolated form\n64520 arabic ligature beh with meem isolated form\n64521 arabic ligature beh with alef maksura isolated form\n64522 arabic ligature beh with yeh isolated form\n64523 arabic ligature teh with jeem isolated form\n64524 arabic ligature teh with hah isolated form\n64525 arabic ligature teh with khah isolated form\n64526 arabic ligature teh with meem isolated form\n64527 arabic ligature teh with alef maksura isolated form\n64528 arabic ligature teh with yeh isolated form\n64529 arabic ligature theh with jeem isolated form\n64530 arabic ligature theh with meem isolated form\n64531 arabic ligature theh with alef maksura isolated form\n64532 arabic ligature theh with yeh isolated form\n64533 arabic ligature jeem with hah isolated form\n64534 arabic ligature jeem with meem isolated form\n64535 arabic ligature hah with jeem isolated form\n64536 arabic ligature hah with meem isolated form\n64537 arabic ligature khah with jeem isolated form\n64538 arabic ligature khah with hah isolated form\n64539 arabic ligature khah with meem isolated form\n64540 arabic ligature seen with jeem isolated form\n64541 arabic ligature seen with hah isolated form\n64542 arabic ligature seen with khah isolated form\n64543 arabic ligature seen with meem isolated form\n64544 arabic ligature sad with hah isolated form\n64545 arabic ligature sad with meem isolated form\n64546 arabic ligature dad with jeem isolated form\n64547 arabic ligature dad with hah isolated form\n64548 arabic ligature dad with khah isolated form\n64549 arabic ligature dad with meem isolated form\n64550 arabic ligature tah with hah isolated form\n64551 arabic ligature tah with meem isolated form\n64552 arabic ligature zah with meem isolated form\n64553 arabic ligature ain with jeem isolated form\n64554 arabic ligature ain with meem isolated form\n64555 arabic ligature ghain with jeem isolated form\n64556 arabic ligature ghain with meem isolated form\n64557 arabic ligature feh with jeem isolated form\n64558 arabic ligature feh with hah isolated form\n64559 arabic ligature feh with khah isolated form\n64560 arabic ligature feh with meem isolated form\n64561 arabic ligature feh with alef maksura isolated form\n64562 arabic ligature feh with yeh isolated form\n64563 arabic ligature qaf with hah isolated form\n64564 arabic ligature qaf with meem isolated form\n64565 arabic ligature qaf with alef maksura isolated form\n64566 arabic ligature qaf with yeh isolated form\n64567 arabic ligature kaf with alef isolated form\n64568 arabic ligature kaf with jeem isolated form\n64569 arabic ligature kaf with hah isolated form\n64570 arabic ligature kaf with khah isolated form\n64571 arabic ligature kaf with lam isolated form\n64572 arabic ligature kaf with meem isolated form\n64573 arabic ligature kaf with alef maksura isolated form\n64574 arabic ligature kaf with yeh isolated form\n64575 arabic ligature lam with jeem isolated form\n64576 arabic ligature lam with hah isolated form\n64577 arabic ligature lam with khah isolated form\n64578 arabic ligature lam with meem isolated form\n64579 arabic ligature lam with alef maksura isolated form\n64580 arabic ligature lam with yeh isolated form\n64581 arabic ligature meem with jeem isolated form\n64582 arabic ligature meem with hah isolated form\n64583 arabic ligature meem with khah isolated form\n64584 arabic ligature meem with meem isolated form\n64585 arabic ligature meem with alef maksura isolated form\n64586 arabic ligature meem with yeh isolated form\n64587 arabic ligature noon with jeem isolated form\n64588 arabic ligature noon with hah isolated form\n64589 arabic ligature noon with khah isolated form\n64590 arabic ligature noon with meem isolated form\n64591 arabic ligature noon with alef maksura isolated form\n64592 arabic ligature noon with yeh isolated form\n64593 arabic ligature heh with jeem isolated form\n64594 arabic ligature heh with meem isolated form\n64595 arabic ligature heh with alef maksura isolated form\n64596 arabic ligature heh with yeh isolated form\n64597 arabic ligature yeh with jeem isolated form\n64598 arabic ligature yeh with hah isolated form\n64599 arabic ligature yeh with khah isolated form\n64600 arabic ligature yeh with meem isolated form\n64601 arabic ligature yeh with alef maksura isolated form\n64602 arabic ligature yeh with yeh isolated form\n64603 arabic ligature thal with superscript alef isolated form\n64604 arabic ligature reh with superscript alef isolated form\n64605 arabic ligature alef maksura with superscript alef isolated form\n64606 arabic ligature shadda with dammatan isolated form\n64607 arabic ligature shadda with kasratan isolated form\n64608 arabic ligature shadda with fatha isolated form\n64609 arabic ligature shadda with damma isolated form\n64610 arabic ligature shadda with kasra isolated form\n64611 arabic ligature shadda with superscript alef isolated form\n64612 arabic ligature yeh with hamza above with reh final form\n64613 arabic ligature yeh with hamza above with zain final form\n64614 arabic ligature yeh with hamza above with meem final form\n64615 arabic ligature yeh with hamza above with noon final form\n64616 arabic ligature yeh with hamza above with alef maksura final form\n64617 arabic ligature yeh with hamza above with yeh final form\n64618 arabic ligature beh with reh final form\n64619 arabic ligature beh with zain final form\n64620 arabic ligature beh with meem final form\n64621 arabic ligature beh with noon final form\n64622 arabic ligature beh with alef maksura final form\n64623 arabic ligature beh with yeh final form\n64624 arabic ligature teh with reh final form\n64625 arabic ligature teh with zain final form\n64626 arabic ligature teh with meem final form\n64627 arabic ligature teh with noon final form\n64628 arabic ligature teh with alef maksura final form\n64629 arabic ligature teh with yeh final form\n64630 arabic ligature theh with reh final form\n64631 arabic ligature theh with zain final form\n64632 arabic ligature theh with meem final form\n64633 arabic ligature theh with noon final form\n64634 arabic ligature theh with alef maksura final form\n64635 arabic ligature theh with yeh final form\n64636 arabic ligature feh with alef maksura final form\n64637 arabic ligature feh with yeh final form\n64638 arabic ligature qaf with alef maksura final form\n64639 arabic ligature qaf with yeh final form\n64640 arabic ligature kaf with alef final form\n64641 arabic ligature kaf with lam final form\n64642 arabic ligature kaf with meem final form\n64643 arabic ligature kaf with alef maksura final form\n64644 arabic ligature kaf with yeh final form\n64645 arabic ligature lam with meem final form\n64646 arabic ligature lam with alef maksura final form\n64647 arabic ligature lam with yeh final form\n64648 arabic ligature meem with alef final form\n64649 arabic ligature meem with meem final form\n64650 arabic ligature noon with reh final form\n64651 arabic ligature noon with zain final form\n64652 arabic ligature noon with meem final form\n64653 arabic ligature noon with noon final form\n64654 arabic ligature noon with alef maksura final form\n64655 arabic ligature noon with yeh final form\n64656 arabic ligature alef maksura with superscript alef final form\n64657 arabic ligature yeh with reh final form\n64658 arabic ligature yeh with zain final form\n64659 arabic ligature yeh with meem final form\n64660 arabic ligature yeh with noon final form\n64661 arabic ligature yeh with alef maksura final form\n64662 arabic ligature yeh with yeh final form\n64663 arabic ligature yeh with hamza above with jeem initial form\n64664 arabic ligature yeh with hamza above with hah initial form\n64665 arabic ligature yeh with hamza above with khah initial form\n64666 arabic ligature yeh with hamza above with meem initial form\n64667 arabic ligature yeh with hamza above with heh initial form\n64668 arabic ligature beh with jeem initial form\n64669 arabic ligature beh with hah initial form\n64670 arabic ligature beh with khah initial form\n64671 arabic ligature beh with meem initial form\n64672 arabic ligature beh with heh initial form\n64673 arabic ligature teh with jeem initial form\n64674 arabic ligature teh with hah initial form\n64675 arabic ligature teh with khah initial form\n64676 arabic ligature teh with meem initial form\n64677 arabic ligature teh with heh initial form\n64678 arabic ligature theh with meem initial form\n64679 arabic ligature jeem with hah initial form\n64680 arabic ligature jeem with meem initial form\n64681 arabic ligature hah with jeem initial form\n64682 arabic ligature hah with meem initial form\n64683 arabic ligature khah with jeem initial form\n64684 arabic ligature khah with meem initial form\n64685 arabic ligature seen with jeem initial form\n64686 arabic ligature seen with hah initial form\n64687 arabic ligature seen with khah initial form\n64688 arabic ligature seen with meem initial form\n64689 arabic ligature sad with hah initial form\n64690 arabic ligature sad with khah initial form\n64691 arabic ligature sad with meem initial form\n64692 arabic ligature dad with jeem initial form\n64693 arabic ligature dad with hah initial form\n64694 arabic ligature dad with khah initial form\n64695 arabic ligature dad with meem initial form\n64696 arabic ligature tah with hah initial form\n64697 arabic ligature zah with meem initial form\n64698 arabic ligature ain with jeem initial form\n64699 arabic ligature ain with meem initial form\n64700 arabic ligature ghain with jeem initial form\n64701 arabic ligature ghain with meem initial form\n64702 arabic ligature feh with jeem initial form\n64703 arabic ligature feh with hah initial form\n64704 arabic ligature feh with khah initial form\n64705 arabic ligature feh with meem initial form\n64706 arabic ligature qaf with hah initial form\n64707 arabic ligature qaf with meem initial form\n64708 arabic ligature kaf with jeem initial form\n64709 arabic ligature kaf with hah initial form\n64710 arabic ligature kaf with khah initial form\n64711 arabic ligature kaf with lam initial form\n64712 arabic ligature kaf with meem initial form\n64713 arabic ligature lam with jeem initial form\n64714 arabic ligature lam with hah initial form\n64715 arabic ligature lam with khah initial form\n64716 arabic ligature lam with meem initial form\n64717 arabic ligature lam with heh initial form\n64718 arabic ligature meem with jeem initial form\n64719 arabic ligature meem with hah initial form\n64720 arabic ligature meem with khah initial form\n64721 arabic ligature meem with meem initial form\n64722 arabic ligature noon with jeem initial form\n64723 arabic ligature noon with hah initial form\n64724 arabic ligature noon with khah initial form\n64725 arabic ligature noon with meem initial form\n64726 arabic ligature noon with heh initial form\n64727 arabic ligature heh with jeem initial form\n64728 arabic ligature heh with meem initial form\n64729 arabic ligature heh with superscript alef initial form\n64730 arabic ligature yeh with jeem initial form\n64731 arabic ligature yeh with hah initial form\n64732 arabic ligature yeh with khah initial form\n64733 arabic ligature yeh with meem initial form\n64734 arabic ligature yeh with heh initial form\n64735 arabic ligature yeh with hamza above with meem medial form\n64736 arabic ligature yeh with hamza above with heh medial form\n64737 arabic ligature beh with meem medial form\n64738 arabic ligature beh with heh medial form\n64739 arabic ligature teh with meem medial form\n64740 arabic ligature teh with heh medial form\n64741 arabic ligature theh with meem medial form\n64742 arabic ligature theh with heh medial form\n64743 arabic ligature seen with meem medial form\n64744 arabic ligature seen with heh medial form\n64745 arabic ligature sheen with meem medial form\n64746 arabic ligature sheen with heh medial form\n64747 arabic ligature kaf with lam medial form\n64748 arabic ligature kaf with meem medial form\n64749 arabic ligature lam with meem medial form\n64750 arabic ligature noon with meem medial form\n64751 arabic ligature noon with heh medial form\n64752 arabic ligature yeh with meem medial form\n64753 arabic ligature yeh with heh medial form\n64754 arabic ligature shadda with fatha medial form\n64755 arabic ligature shadda with damma medial form\n64756 arabic ligature shadda with kasra medial form\n64757 arabic ligature tah with alef maksura isolated form\n64758 arabic ligature tah with yeh isolated form\n64759 arabic ligature ain with alef maksura isolated form\n64760 arabic ligature ain with yeh isolated form\n64761 arabic ligature ghain with alef maksura isolated form\n64762 arabic ligature ghain with yeh isolated form\n64763 arabic ligature seen with alef maksura isolated form\n64764 arabic ligature seen with yeh isolated form\n64765 arabic ligature sheen with alef maksura isolated form\n64766 arabic ligature sheen with yeh isolated form\n64767 arabic ligature hah with alef maksura isolated form\n64768 arabic ligature hah with yeh isolated form\n64769 arabic ligature jeem with alef maksura isolated form\n64770 arabic ligature jeem with yeh isolated form\n64771 arabic ligature khah with alef maksura isolated form\n64772 arabic ligature khah with yeh isolated form\n64773 arabic ligature sad with alef maksura isolated form\n64774 arabic ligature sad with yeh isolated form\n64775 arabic ligature dad with alef maksura isolated form\n64776 arabic ligature dad with yeh isolated form\n64777 arabic ligature sheen with jeem isolated form\n64778 arabic ligature sheen with hah isolated form\n64779 arabic ligature sheen with khah isolated form\n64780 arabic ligature sheen with meem isolated form\n64781 arabic ligature sheen with reh isolated form\n64782 arabic ligature seen with reh isolated form\n64783 arabic ligature sad with reh isolated form\n64784 arabic ligature dad with reh isolated form\n64785 arabic ligature tah with alef maksura final form\n64786 arabic ligature tah with yeh final form\n64787 arabic ligature ain with alef maksura final form\n64788 arabic ligature ain with yeh final form\n64789 arabic ligature ghain with alef maksura final form\n64790 arabic ligature ghain with yeh final form\n64791 arabic ligature seen with alef maksura final form\n64792 arabic ligature seen with yeh final form\n64793 arabic ligature sheen with alef maksura final form\n64794 arabic ligature sheen with yeh final form\n64795 arabic ligature hah with alef maksura final form\n64796 arabic ligature hah with yeh final form\n64797 arabic ligature jeem with alef maksura final form\n64798 arabic ligature jeem with yeh final form\n64799 arabic ligature khah with alef maksura final form\n64800 arabic ligature khah with yeh final form\n64801 arabic ligature sad with alef maksura final form\n64802 arabic ligature sad with yeh final form\n64803 arabic ligature dad with alef maksura final form\n64804 arabic ligature dad with yeh final form\n64805 arabic ligature sheen with jeem final form\n64806 arabic ligature sheen with hah final form\n64807 arabic ligature sheen with khah final form\n64808 arabic ligature sheen with meem final form\n64809 arabic ligature sheen with reh final form\n64810 arabic ligature seen with reh final form\n64811 arabic ligature sad with reh final form\n64812 arabic ligature dad with reh final form\n64813 arabic ligature sheen with jeem initial form\n64814 arabic ligature sheen with hah initial form\n64815 arabic ligature sheen with khah initial form\n64816 arabic ligature sheen with meem initial form\n64817 arabic ligature seen with heh initial form\n64818 arabic ligature sheen with heh initial form\n64819 arabic ligature tah with meem initial form\n64820 arabic ligature seen with jeem medial form\n64821 arabic ligature seen with hah medial form\n64822 arabic ligature seen with khah medial form\n64823 arabic ligature sheen with jeem medial form\n64824 arabic ligature sheen with hah medial form\n64825 arabic ligature sheen with khah medial form\n64826 arabic ligature tah with meem medial form\n64827 arabic ligature zah with meem medial form\n64828 arabic ligature alef with fathatan final form\n64829 arabic ligature alef with fathatan isolated form\n64830 ornate left parenthesis\n64831 ornate right parenthesis\n64832 arabic ligature rahimahu allaah\n64833 arabic ligature radi allaahu anh\n64834 arabic ligature radi allaahu anhaa\n64835 arabic ligature radi allaahu anhum\n64836 arabic ligature radi allaahu anhumaa\n64837 arabic ligature radi allaahu anhunna\n64838 arabic ligature sallallaahu alayhi wa-aalih\n64839 arabic ligature alayhi as-salaam\n64840 arabic ligature alayhim as-salaam\n64841 arabic ligature alayhimaa as-salaam\n64842 arabic ligature alayhi as-salaatu was-salaam\n64843 arabic ligature quddisa sirrah\n64844 arabic ligature sallallahu alayhi waaalihee wa-sallam\n64845 arabic ligature alayhaa as-salaam\n64846 arabic ligature tabaaraka wa-taaalaa\n64847 arabic ligature rahimahum allaah\n64848 arabic ligature teh with jeem with meem initial form\n64849 arabic ligature teh with hah with jeem final form\n64850 arabic ligature teh with hah with jeem initial form\n64851 arabic ligature teh with hah with meem initial form\n64852 arabic ligature teh with khah with meem initial form\n64853 arabic ligature teh with meem with jeem initial form\n64854 arabic ligature teh with meem with hah initial form\n64855 arabic ligature teh with meem with khah initial form\n64856 arabic ligature jeem with meem with hah final form\n64857 arabic ligature jeem with meem with hah initial form\n64858 arabic ligature hah with meem with yeh final form\n64859 arabic ligature hah with meem with alef maksura final form\n64860 arabic ligature seen with hah with jeem initial form\n64861 arabic ligature seen with jeem with hah initial form\n64862 arabic ligature seen with jeem with alef maksura final form\n64863 arabic ligature seen with meem with hah final form\n64864 arabic ligature seen with meem with hah initial form\n64865 arabic ligature seen with meem with jeem initial form\n64866 arabic ligature seen with meem with meem final form\n64867 arabic ligature seen with meem with meem initial form\n64868 arabic ligature sad with hah with hah final form\n64869 arabic ligature sad with hah with hah initial form\n64870 arabic ligature sad with meem with meem final form\n64871 arabic ligature sheen with hah with meem final form\n64872 arabic ligature sheen with hah with meem initial form\n64873 arabic ligature sheen with jeem with yeh final form\n64874 arabic ligature sheen with meem with khah final form\n64875 arabic ligature sheen with meem with khah initial form\n64876 arabic ligature sheen with meem with meem final form\n64877 arabic ligature sheen with meem with meem initial form\n64878 arabic ligature dad with hah with alef maksura final form\n64879 arabic ligature dad with khah with meem final form\n64880 arabic ligature dad with khah with meem initial form\n64881 arabic ligature tah with meem with hah final form\n64882 arabic ligature tah with meem with hah initial form\n64883 arabic ligature tah with meem with meem initial form\n64884 arabic ligature tah with meem with yeh final form\n64885 arabic ligature ain with jeem with meem final form\n64886 arabic ligature ain with meem with meem final form\n64887 arabic ligature ain with meem with meem initial form\n64888 arabic ligature ain with meem with alef maksura final form\n64889 arabic ligature ghain with meem with meem final form\n64890 arabic ligature ghain with meem with yeh final form\n64891 arabic ligature ghain with meem with alef maksura final form\n64892 arabic ligature feh with khah with meem final form\n64893 arabic ligature feh with khah with meem initial form\n64894 arabic ligature qaf with meem with hah final form\n64895 arabic ligature qaf with meem with meem final form\n64896 arabic ligature lam with hah with meem final form\n64897 arabic ligature lam with hah with yeh final form\n64898 arabic ligature lam with hah with alef maksura final form\n64899 arabic ligature lam with jeem with jeem initial form\n64900 arabic ligature lam with jeem with jeem final form\n64901 arabic ligature lam with khah with meem final form\n64902 arabic ligature lam with khah with meem initial form\n64903 arabic ligature lam with meem with hah final form\n64904 arabic ligature lam with meem with hah initial form\n64905 arabic ligature meem with hah with jeem initial form\n64906 arabic ligature meem with hah with meem initial form\n64907 arabic ligature meem with hah with yeh final form\n64908 arabic ligature meem with jeem with hah initial form\n64909 arabic ligature meem with jeem with meem initial form\n64910 arabic ligature meem with khah with jeem initial form\n64911 arabic ligature meem with khah with meem initial form\n64912 arabic ligature rahmatu allaahi alayh\n64913 arabic ligature rahmatu allaahi alayhaa\n64914 arabic ligature meem with jeem with khah initial form\n64915 arabic ligature heh with meem with jeem initial form\n64916 arabic ligature heh with meem with meem initial form\n64917 arabic ligature noon with hah with meem initial form\n64918 arabic ligature noon with hah with alef maksura final form\n64919 arabic ligature noon with jeem with meem final form\n64920 arabic ligature noon with jeem with meem initial form\n64921 arabic ligature noon with jeem with alef maksura final form\n64922 arabic ligature noon with meem with yeh final form\n64923 arabic ligature noon with meem with alef maksura final form\n64924 arabic ligature yeh with meem with meem final form\n64925 arabic ligature yeh with meem with meem initial form\n64926 arabic ligature beh with khah with yeh final form\n64927 arabic ligature teh with jeem with yeh final form\n64928 arabic ligature teh with jeem with alef maksura final form\n64929 arabic ligature teh with khah with yeh final form\n64930 arabic ligature teh with khah with alef maksura final form\n64931 arabic ligature teh with meem with yeh final form\n64932 arabic ligature teh with meem with alef maksura final form\n64933 arabic ligature jeem with meem with yeh final form\n64934 arabic ligature jeem with hah with alef maksura final form\n64935 arabic ligature jeem with meem with alef maksura final form\n64936 arabic ligature seen with khah with alef maksura final form\n64937 arabic ligature sad with hah with yeh final form\n64938 arabic ligature sheen with hah with yeh final form\n64939 arabic ligature dad with hah with yeh final form\n64940 arabic ligature lam with jeem with yeh final form\n64941 arabic ligature lam with meem with yeh final form\n64942 arabic ligature yeh with hah with yeh final form\n64943 arabic ligature yeh with jeem with yeh final form\n64944 arabic ligature yeh with meem with yeh final form\n64945 arabic ligature meem with meem with yeh final form\n64946 arabic ligature qaf with meem with yeh final form\n64947 arabic ligature noon with hah with yeh final form\n64948 arabic ligature qaf with meem with hah initial form\n64949 arabic ligature lam with hah with meem initial form\n64950 arabic ligature ain with meem with yeh final form\n64951 arabic ligature kaf with meem with yeh final form\n64952 arabic ligature noon with jeem with hah initial form\n64953 arabic ligature meem with khah with yeh final form\n64954 arabic ligature lam with jeem with meem initial form\n64955 arabic ligature kaf with meem with meem final form\n64956 arabic ligature lam with jeem with meem final form\n64957 arabic ligature noon with jeem with hah final form\n64958 arabic ligature jeem with hah with yeh final form\n64959 arabic ligature hah with jeem with yeh final form\n64960 arabic ligature meem with jeem with yeh final form\n64961 arabic ligature feh with meem with yeh final form\n64962 arabic ligature beh with hah with yeh final form\n64963 arabic ligature kaf with meem with meem initial form\n64964 arabic ligature ain with jeem with meem initial form\n64965 arabic ligature sad with meem with meem initial form\n64966 arabic ligature seen with khah with yeh final form\n64967 arabic ligature noon with jeem with yeh final form\n64968 arabic ligature rahimahu allaah taaalaa\n64969 arabic ligature radi allaahu taaalaa anh\n64970 arabic ligature radi allaahu taaalaa anhaa\n64971 arabic ligature radi allaahu taaalaa anhumaa\n64972 arabic ligature sallallahu alayhi wa-alaa aalihee wa-sallam\n64973 arabic ligature ajjal allaahu taaalaa farajahu ash-shareef\n64974 arabic ligature karrama allaahu wajhah\n64975 arabic ligature salaamuhu alaynaa\n65008 arabic ligature salla used as koranic stop sign isolated form\n65009 arabic ligature qala used as koranic stop sign isolated form\n65010 arabic ligature allah isolated form\n65011 arabic ligature akbar isolated form\n65012 arabic ligature mohammad isolated form\n65013 arabic ligature salam isolated form\n65014 arabic ligature rasoul isolated form\n65015 arabic ligature alayhe isolated form\n65016 arabic ligature wasallam isolated form\n65017 arabic ligature salla isolated form\n65018 arabic ligature sallallahou alayhe wasallam\n65019 arabic ligature jallajalalouhou\n65020 rial sign\n65021 arabic ligature bismillah ar-rahman ar-raheem\n65022 arabic ligature subhaanahu wa taaalaa\n65023 arabic ligature azza wa jall\n65024 variation selector-1\n65025 variation selector-2\n65026 variation selector-3\n65027 variation selector-4\n65028 variation selector-5\n65029 variation selector-6\n65030 variation selector-7\n65031 variation selector-8\n65032 variation selector-9\n65033 variation selector-10\n65034 variation selector-11\n65035 variation selector-12\n65036 variation selector-13\n65037 variation selector-14\n65038 variation selector-15\n65039 variation selector-16\n65040 presentation form for vertical comma\n65041 presentation form for vertical ideographic comma\n65042 presentation form for vertical ideographic full stop\n65043 presentation form for vertical colon\n65044 presentation form for vertical semicolon\n65045 presentation form for vertical exclamation mark\n65046 presentation form for vertical question mark\n65047 presentation form for vertical left white lenticular bracket\n65048 presentation form for vertical right white lenticular brakcet\n65049 presentation form for vertical horizontal ellipsis\n65056 combining ligature left half\n65057 combining ligature right half\n65058 combining double tilde left half\n65059 combining double tilde right half\n65060 combining macron left half\n65061 combining macron right half\n65062 combining conjoining macron\n65063 combining ligature left half below\n65064 combining ligature right half below\n65065 combining tilde left half below\n65066 combining tilde right half below\n65067 combining macron left half below\n65068 combining macron right half below\n65069 combining conjoining macron below\n65070 combining cyrillic titlo left half\n65071 combining cyrillic titlo right half\n65072 presentation form for vertical two dot leader\n65073 presentation form for vertical em dash\n65074 presentation form for vertical en dash\n65075 presentation form for vertical low line\n65076 presentation form for vertical wavy low line\n65077 presentation form for vertical left parenthesis\n65078 presentation form for vertical right parenthesis\n65079 presentation form for vertical left curly bracket\n65080 presentation form for vertical right curly bracket\n65081 presentation form for vertical left tortoise shell bracket\n65082 presentation form for vertical right tortoise shell bracket\n65083 presentation form for vertical left black lenticular bracket\n65084 presentation form for vertical right black lenticular bracket\n65085 presentation form for vertical left double angle bracket\n65086 presentation form for vertical right double angle bracket\n65087 presentation form for vertical left angle bracket\n65088 presentation form for vertical right angle bracket\n65089 presentation form for vertical left corner bracket\n65090 presentation form for vertical right corner bracket\n65091 presentation form for vertical left white corner bracket\n65092 presentation form for vertical right white corner bracket\n65093 sesame dot\n65094 white sesame dot\n65095 presentation form for vertical left square bracket\n65096 presentation form for vertical right square bracket\n65097 dashed overline\n65098 centreline overline\n65099 wavy overline\n65100 double wavy overline\n65101 dashed low line\n65102 centreline low line\n65103 wavy low line\n65104 small comma\n65105 small ideographic comma\n65106 small full stop\n65108 small semicolon\n65109 small colon\n65110 small question mark\n65111 small exclamation mark\n65112 small em dash\n65113 small left parenthesis\n65114 small right parenthesis\n65115 small left curly bracket\n65116 small right curly bracket\n65117 small left tortoise shell bracket\n65118 small right tortoise shell bracket\n65119 small number sign\n65120 small ampersand\n65121 small asterisk\n65122 small plus sign\n65123 small hyphen-minus\n65124 small less-than sign\n65125 small greater-than sign\n65126 small equals sign\n65128 small reverse solidus\n65129 small dollar sign\n65130 small percent sign\n65131 small commercial at\n65136 arabic fathatan isolated form\n65137 arabic tatweel with fathatan above\n65138 arabic dammatan isolated form\n65139 arabic tail fragment\n65140 arabic kasratan isolated form\n65142 arabic fatha isolated form\n65143 arabic fatha medial form\n65144 arabic damma isolated form\n65145 arabic damma medial form\n65146 arabic kasra isolated form\n65147 arabic kasra medial form\n65148 arabic shadda isolated form\n65149 arabic shadda medial form\n65150 arabic sukun isolated form\n65151 arabic sukun medial form\n65152 arabic letter hamza isolated form\n65153 arabic letter alef with madda above isolated form\n65154 arabic letter alef with madda above final form\n65155 arabic letter alef with hamza above isolated form\n65156 arabic letter alef with hamza above final form\n65157 arabic letter waw with hamza above isolated form\n65158 arabic letter waw with hamza above final form\n65159 arabic letter alef with hamza below isolated form\n65160 arabic letter alef with hamza below final form\n65161 arabic letter yeh with hamza above isolated form\n65162 arabic letter yeh with hamza above final form\n65163 arabic letter yeh with hamza above initial form\n65164 arabic letter yeh with hamza above medial form\n65165 arabic letter alef isolated form\n65166 arabic letter alef final form\n65167 arabic letter beh isolated form\n65168 arabic letter beh final form\n65169 arabic letter beh initial form\n65170 arabic letter beh medial form\n65171 arabic letter teh marbuta isolated form\n65172 arabic letter teh marbuta final form\n65173 arabic letter teh isolated form\n65174 arabic letter teh final form\n65175 arabic letter teh initial form\n65176 arabic letter teh medial form\n65177 arabic letter theh isolated form\n65178 arabic letter theh final form\n65179 arabic letter theh initial form\n65180 arabic letter theh medial form\n65181 arabic letter jeem isolated form\n65182 arabic letter jeem final form\n65183 arabic letter jeem initial form\n65184 arabic letter jeem medial form\n65185 arabic letter hah isolated form\n65186 arabic letter hah final form\n65187 arabic letter hah initial form\n65188 arabic letter hah medial form\n65189 arabic letter khah isolated form\n65190 arabic letter khah final form\n65191 arabic letter khah initial form\n65192 arabic letter khah medial form\n65193 arabic letter dal isolated form\n65194 arabic letter dal final form\n65195 arabic letter thal isolated form\n65196 arabic letter thal final form\n65197 arabic letter reh isolated form\n65198 arabic letter reh final form\n65199 arabic letter zain isolated form\n65200 arabic letter zain final form\n65201 arabic letter seen isolated form\n65202 arabic letter seen final form\n65203 arabic letter seen initial form\n65204 arabic letter seen medial form\n65205 arabic letter sheen isolated form\n65206 arabic letter sheen final form\n65207 arabic letter sheen initial form\n65208 arabic letter sheen medial form\n65209 arabic letter sad isolated form\n65210 arabic letter sad final form\n65211 arabic letter sad initial form\n65212 arabic letter sad medial form\n65213 arabic letter dad isolated form\n65214 arabic letter dad final form\n65215 arabic letter dad initial form\n65216 arabic letter dad medial form\n65217 arabic letter tah isolated form\n65218 arabic letter tah final form\n65219 arabic letter tah initial form\n65220 arabic letter tah medial form\n65221 arabic letter zah isolated form\n65222 arabic letter zah final form\n65223 arabic letter zah initial form\n65224 arabic letter zah medial form\n65225 arabic letter ain isolated form\n65226 arabic letter ain final form\n65227 arabic letter ain initial form\n65228 arabic letter ain medial form\n65229 arabic letter ghain isolated form\n65230 arabic letter ghain final form\n65231 arabic letter ghain initial form\n65232 arabic letter ghain medial form\n65233 arabic letter feh isolated form\n65234 arabic letter feh final form\n65235 arabic letter feh initial form\n65236 arabic letter feh medial form\n65237 arabic letter qaf isolated form\n65238 arabic letter qaf final form\n65239 arabic letter qaf initial form\n65240 arabic letter qaf medial form\n65241 arabic letter kaf isolated form\n65242 arabic letter kaf final form\n65243 arabic letter kaf initial form\n65244 arabic letter kaf medial form\n65245 arabic letter lam isolated form\n65246 arabic letter lam final form\n65247 arabic letter lam initial form\n65248 arabic letter lam medial form\n65249 arabic letter meem isolated form\n65250 arabic letter meem final form\n65251 arabic letter meem initial form\n65252 arabic letter meem medial form\n65253 arabic letter noon isolated form\n65254 arabic letter noon final form\n65255 arabic letter noon initial form\n65256 arabic letter noon medial form\n65257 arabic letter heh isolated form\n65258 arabic letter heh final form\n65259 arabic letter heh initial form\n65260 arabic letter heh medial form\n65261 arabic letter waw isolated form\n65262 arabic letter waw final form\n65263 arabic letter alef maksura isolated form\n65264 arabic letter alef maksura final form\n65265 arabic letter yeh isolated form\n65266 arabic letter yeh final form\n65267 arabic letter yeh initial form\n65268 arabic letter yeh medial form\n65269 arabic ligature lam with alef with madda above isolated form\n65270 arabic ligature lam with alef with madda above final form\n65271 arabic ligature lam with alef with hamza above isolated form\n65272 arabic ligature lam with alef with hamza above final form\n65273 arabic ligature lam with alef with hamza below isolated form\n65274 arabic ligature lam with alef with hamza below final form\n65275 arabic ligature lam with alef isolated form\n65276 arabic ligature lam with alef final form\n65279 zero width no-break space\n65281 fullwidth exclamation mark\n65282 fullwidth quotation mark\n65283 fullwidth number sign\n65284 fullwidth dollar sign\n65285 fullwidth percent sign\n65286 fullwidth ampersand\n65287 fullwidth apostrophe\n65288 fullwidth left parenthesis\n65289 fullwidth right parenthesis\n65290 fullwidth asterisk\n65291 fullwidth plus sign\n65292 fullwidth comma\n65293 fullwidth hyphen-minus\n65294 fullwidth full stop\n65295 fullwidth solidus\n65296 fullwidth digit zero\n65297 fullwidth digit one\n65298 fullwidth digit two\n65299 fullwidth digit three\n65300 fullwidth digit four\n65301 fullwidth digit five\n65302 fullwidth digit six\n65303 fullwidth digit seven\n65304 fullwidth digit eight\n65305 fullwidth digit nine\n65306 fullwidth colon\n65307 fullwidth semicolon\n65308 fullwidth less-than sign\n65309 fullwidth equals sign\n65310 fullwidth greater-than sign\n65311 fullwidth question mark\n65312 fullwidth commercial at\n65313 fullwidth latin capital letter a\n65314 fullwidth latin capital letter b\n65315 fullwidth latin capital letter c\n65316 fullwidth latin capital letter d\n65317 fullwidth latin capital letter e\n65318 fullwidth latin capital letter f\n65319 fullwidth latin capital letter g\n65320 fullwidth latin capital letter h\n65321 fullwidth latin capital letter i\n65322 fullwidth latin capital letter j\n65323 fullwidth latin capital letter k\n65324 fullwidth latin capital letter l\n65325 fullwidth latin capital letter m\n65326 fullwidth latin capital letter n\n65327 fullwidth latin capital letter o\n65328 fullwidth latin capital letter p\n65329 fullwidth latin capital letter q\n65330 fullwidth latin capital letter r\n65331 fullwidth latin capital letter s\n65332 fullwidth latin capital letter t\n65333 fullwidth latin capital letter u\n65334 fullwidth latin capital letter v\n65335 fullwidth latin capital letter w\n65336 fullwidth latin capital letter x\n65337 fullwidth latin capital letter y\n65338 fullwidth latin capital letter z\n65339 fullwidth left square bracket\n65340 fullwidth reverse solidus\n65341 fullwidth right square bracket\n65342 fullwidth circumflex accent\n65343 fullwidth low line\n65344 fullwidth grave accent\n65345 fullwidth latin small letter a\n65346 fullwidth latin small letter b\n65347 fullwidth latin small letter c\n65348 fullwidth latin small letter d\n65349 fullwidth latin small letter e\n65350 fullwidth latin small letter f\n65351 fullwidth latin small letter g\n65352 fullwidth latin small letter h\n65353 fullwidth latin small letter i\n65354 fullwidth latin small letter j\n65355 fullwidth latin small letter k\n65356 fullwidth latin small letter l\n65357 fullwidth latin small letter m\n65358 fullwidth latin small letter n\n65359 fullwidth latin small letter o\n65360 fullwidth latin small letter p\n65361 fullwidth latin small letter q\n65362 fullwidth latin small letter r\n65363 fullwidth latin small letter s\n65364 fullwidth latin small letter t\n65365 fullwidth latin small letter u\n65366 fullwidth latin small letter v\n65367 fullwidth latin small letter w\n65368 fullwidth latin small letter x\n65369 fullwidth latin small letter y\n65370 fullwidth latin small letter z\n65371 fullwidth left curly bracket\n65372 fullwidth vertical line\n65373 fullwidth right curly bracket\n65374 fullwidth tilde\n65375 fullwidth left white parenthesis\n65376 fullwidth right white parenthesis\n65377 halfwidth ideographic full stop\n65378 halfwidth left corner bracket\n65379 halfwidth right corner bracket\n65380 halfwidth ideographic comma\n65381 halfwidth katakana middle dot\n65382 halfwidth katakana letter wo\n65383 halfwidth katakana letter small a\n65384 halfwidth katakana letter small i\n65385 halfwidth katakana letter small u\n65386 halfwidth katakana letter small e\n65387 halfwidth katakana letter small o\n65388 halfwidth katakana letter small ya\n65389 halfwidth katakana letter small yu\n65390 halfwidth katakana letter small yo\n65391 halfwidth katakana letter small tu\n65392 halfwidth katakana-hiragana prolonged sound mark\n65393 halfwidth katakana letter a\n65394 halfwidth katakana letter i\n65395 halfwidth katakana letter u\n65396 halfwidth katakana letter e\n65397 halfwidth katakana letter o\n65398 halfwidth katakana letter ka\n65399 halfwidth katakana letter ki\n65400 halfwidth katakana letter ku\n65401 halfwidth katakana letter ke\n65402 halfwidth katakana letter ko\n65403 halfwidth katakana letter sa\n65404 halfwidth katakana letter si\n65405 halfwidth katakana letter su\n65406 halfwidth katakana letter se\n65407 halfwidth katakana letter so\n65408 halfwidth katakana letter ta\n65409 halfwidth katakana letter ti\n65410 halfwidth katakana letter tu\n65411 halfwidth katakana letter te\n65412 halfwidth katakana letter to\n65413 halfwidth katakana letter na\n65414 halfwidth katakana letter ni\n65415 halfwidth katakana letter nu\n65416 halfwidth katakana letter ne\n65417 halfwidth katakana letter no\n65418 halfwidth katakana letter ha\n65419 halfwidth katakana letter hi\n65420 halfwidth katakana letter hu\n65421 halfwidth katakana letter he\n65422 halfwidth katakana letter ho\n65423 halfwidth katakana letter ma\n65424 halfwidth katakana letter mi\n65425 halfwidth katakana letter mu\n65426 halfwidth katakana letter me\n65427 halfwidth katakana letter mo\n65428 halfwidth katakana letter ya\n65429 halfwidth katakana letter yu\n65430 halfwidth katakana letter yo\n65431 halfwidth katakana letter ra\n65432 halfwidth katakana letter ri\n65433 halfwidth katakana letter ru\n65434 halfwidth katakana letter re\n65435 halfwidth katakana letter ro\n65436 halfwidth katakana letter wa\n65437 halfwidth katakana letter n\n65438 halfwidth katakana voiced sound mark\n65439 halfwidth katakana semi-voiced sound mark\n65440 halfwidth hangul filler\n65441 halfwidth hangul letter kiyeok\n65442 halfwidth hangul letter ssangkiyeok\n65443 halfwidth hangul letter kiyeok-sios\n65444 halfwidth hangul letter nieun\n65445 halfwidth hangul letter nieun-cieuc\n65446 halfwidth hangul letter nieun-hieuh\n65447 halfwidth hangul letter tikeut\n65448 halfwidth hangul letter ssangtikeut\n65449 halfwidth hangul letter rieul\n65450 halfwidth hangul letter rieul-kiyeok\n65451 halfwidth hangul letter rieul-mieum\n65452 halfwidth hangul letter rieul-pieup\n65453 halfwidth hangul letter rieul-sios\n65454 halfwidth hangul letter rieul-thieuth\n65455 halfwidth hangul letter rieul-phieuph\n65456 halfwidth hangul letter rieul-hieuh\n65457 halfwidth hangul letter mieum\n65458 halfwidth hangul letter pieup\n65459 halfwidth hangul letter ssangpieup\n65460 halfwidth hangul letter pieup-sios\n65461 halfwidth hangul letter sios\n65462 halfwidth hangul letter ssangsios\n65463 halfwidth hangul letter ieung\n65464 halfwidth hangul letter cieuc\n65465 halfwidth hangul letter ssangcieuc\n65466 halfwidth hangul letter chieuch\n65467 halfwidth hangul letter khieukh\n65468 halfwidth hangul letter thieuth\n65469 halfwidth hangul letter phieuph\n65470 halfwidth hangul letter hieuh\n65474 halfwidth hangul letter a\n65475 halfwidth hangul letter ae\n65476 halfwidth hangul letter ya\n65477 halfwidth hangul letter yae\n65478 halfwidth hangul letter eo\n65479 halfwidth hangul letter e\n65482 halfwidth hangul letter yeo\n65483 halfwidth hangul letter ye\n65484 halfwidth hangul letter o\n65485 halfwidth hangul letter wa\n65486 halfwidth hangul letter wae\n65487 halfwidth hangul letter oe\n65490 halfwidth hangul letter yo\n65491 halfwidth hangul letter u\n65492 halfwidth hangul letter weo\n65493 halfwidth hangul letter we\n65494 halfwidth hangul letter wi\n65495 halfwidth hangul letter yu\n65498 halfwidth hangul letter eu\n65499 halfwidth hangul letter yi\n65500 halfwidth hangul letter i\n65504 fullwidth cent sign\n65505 fullwidth pound sign\n65506 fullwidth not sign\n65507 fullwidth macron\n65508 fullwidth broken bar\n65509 fullwidth yen sign\n65510 fullwidth won sign\n65512 halfwidth forms light vertical\n65513 halfwidth leftwards arrow\n65514 halfwidth upwards arrow\n65515 halfwidth rightwards arrow\n65516 halfwidth downwards arrow\n65517 halfwidth black square\n65518 halfwidth white circle\n65529 interlinear annotation anchor\n65530 interlinear annotation separator\n65531 interlinear annotation terminator\n65532 object replacement character\n65533 replacement character\n65536 linear b syllable b008 a\n65537 linear b syllable b038 e\n65538 linear b syllable b028 i\n65539 linear b syllable b061 o\n65540 linear b syllable b010 u\n65541 linear b syllable b001 da\n65542 linear b syllable b045 de\n65543 linear b syllable b007 di\n65544 linear b syllable b014 do\n65545 linear b syllable b051 du\n65546 linear b syllable b057 ja\n65547 linear b syllable b046 je\n65549 linear b syllable b036 jo\n65550 linear b syllable b065 ju\n65551 linear b syllable b077 ka\n65552 linear b syllable b044 ke\n65553 linear b syllable b067 ki\n65554 linear b syllable b070 ko\n65555 linear b syllable b081 ku\n65556 linear b syllable b080 ma\n65557 linear b syllable b013 me\n65558 linear b syllable b073 mi\n65559 linear b syllable b015 mo\n65560 linear b syllable b023 mu\n65561 linear b syllable b006 na\n65562 linear b syllable b024 ne\n65563 linear b syllable b030 ni\n65564 linear b syllable b052 no\n65565 linear b syllable b055 nu\n65566 linear b syllable b003 pa\n65567 linear b syllable b072 pe\n65568 linear b syllable b039 pi\n65569 linear b syllable b011 po\n65570 linear b syllable b050 pu\n65571 linear b syllable b016 qa\n65572 linear b syllable b078 qe\n65573 linear b syllable b021 qi\n65574 linear b syllable b032 qo\n65576 linear b syllable b060 ra\n65577 linear b syllable b027 re\n65578 linear b syllable b053 ri\n65579 linear b syllable b002 ro\n65580 linear b syllable b026 ru\n65581 linear b syllable b031 sa\n65582 linear b syllable b009 se\n65583 linear b syllable b041 si\n65584 linear b syllable b012 so\n65585 linear b syllable b058 su\n65586 linear b syllable b059 ta\n65587 linear b syllable b004 te\n65588 linear b syllable b037 ti\n65589 linear b syllable b005 to\n65590 linear b syllable b069 tu\n65591 linear b syllable b054 wa\n65592 linear b syllable b075 we\n65593 linear b syllable b040 wi\n65594 linear b syllable b042 wo\n65596 linear b syllable b017 za\n65597 linear b syllable b074 ze\n65599 linear b syllable b020 zo\n65600 linear b syllable b025 a2\n65601 linear b syllable b043 a3\n65602 linear b syllable b085 au\n65603 linear b syllable b071 dwe\n65604 linear b syllable b090 dwo\n65605 linear b syllable b048 nwa\n65606 linear b syllable b029 pu2\n65607 linear b syllable b062 pte\n65608 linear b syllable b076 ra2\n65609 linear b syllable b033 ra3\n65610 linear b syllable b068 ro2\n65611 linear b syllable b066 ta2\n65612 linear b syllable b087 twe\n65613 linear b syllable b091 two\n65616 linear b symbol b018\n65617 linear b symbol b019\n65618 linear b symbol b022\n65619 linear b symbol b034\n65620 linear b symbol b047\n65621 linear b symbol b049\n65622 linear b symbol b056\n65623 linear b symbol b063\n65624 linear b symbol b064\n65625 linear b symbol b079\n65626 linear b symbol b082\n65627 linear b symbol b083\n65628 linear b symbol b086\n65629 linear b symbol b089\n65664 linear b ideogram b100 man\n65665 linear b ideogram b102 woman\n65666 linear b ideogram b104 deer\n65667 linear b ideogram b105 equid\n65668 linear b ideogram b105f mare\n65669 linear b ideogram b105m stallion\n65670 linear b ideogram b106f ewe\n65671 linear b ideogram b106m ram\n65672 linear b ideogram b107f she-goat\n65673 linear b ideogram b107m he-goat\n65674 linear b ideogram b108f sow\n65675 linear b ideogram b108m boar\n65676 linear b ideogram b109f cow\n65677 linear b ideogram b109m bull\n65678 linear b ideogram b120 wheat\n65679 linear b ideogram b121 barley\n65680 linear b ideogram b122 olive\n65681 linear b ideogram b123 spice\n65682 linear b ideogram b125 cyperus\n65683 linear b monogram b127 kapo\n65684 linear b monogram b128 kanako\n65685 linear b ideogram b130 oil\n65686 linear b ideogram b131 wine\n65687 linear b ideogram b132\n65688 linear b monogram b133 arepa\n65689 linear b monogram b135 meri\n65690 linear b ideogram b140 bronze\n65691 linear b ideogram b141 gold\n65692 linear b ideogram b142\n65693 linear b ideogram b145 wool\n65694 linear b ideogram b146\n65695 linear b ideogram b150\n65696 linear b ideogram b151 horn\n65697 linear b ideogram b152\n65698 linear b ideogram b153\n65699 linear b ideogram b154\n65700 linear b monogram b156 turo2\n65701 linear b ideogram b157\n65702 linear b ideogram b158\n65703 linear b ideogram b159 cloth\n65704 linear b ideogram b160\n65705 linear b ideogram b161\n65706 linear b ideogram b162 garment\n65707 linear b ideogram b163 armour\n65708 linear b ideogram b164\n65709 linear b ideogram b165\n65710 linear b ideogram b166\n65711 linear b ideogram b167\n65712 linear b ideogram b168\n65713 linear b ideogram b169\n65714 linear b ideogram b170\n65715 linear b ideogram b171\n65716 linear b ideogram b172\n65717 linear b ideogram b173 month\n65718 linear b ideogram b174\n65719 linear b ideogram b176 tree\n65720 linear b ideogram b177\n65721 linear b ideogram b178\n65722 linear b ideogram b179\n65723 linear b ideogram b180\n65724 linear b ideogram b181\n65725 linear b ideogram b182\n65726 linear b ideogram b183\n65727 linear b ideogram b184\n65728 linear b ideogram b185\n65729 linear b ideogram b189\n65730 linear b ideogram b190\n65731 linear b ideogram b191 helmet\n65732 linear b ideogram b220 footstool\n65733 linear b ideogram b225 bathtub\n65734 linear b ideogram b230 spear\n65735 linear b ideogram b231 arrow\n65736 linear b ideogram b232\n65737 linear b ideogram b233 sword\n65738 linear b ideogram b234\n65739 linear b ideogram b236\n65740 linear b ideogram b240 wheeled chariot\n65741 linear b ideogram b241 chariot\n65742 linear b ideogram b242 chariot frame\n65743 linear b ideogram b243 wheel\n65744 linear b ideogram b245\n65745 linear b ideogram b246\n65746 linear b monogram b247 dipte\n65747 linear b ideogram b248\n65748 linear b ideogram b249\n65749 linear b ideogram b251\n65750 linear b ideogram b252\n65751 linear b ideogram b253\n65752 linear b ideogram b254 dart\n65753 linear b ideogram b255\n65754 linear b ideogram b256\n65755 linear b ideogram b257\n65756 linear b ideogram b258\n65757 linear b ideogram b259\n65758 linear b ideogram vessel b155\n65759 linear b ideogram vessel b200\n65760 linear b ideogram vessel b201\n65761 linear b ideogram vessel b202\n65762 linear b ideogram vessel b203\n65763 linear b ideogram vessel b204\n65764 linear b ideogram vessel b205\n65765 linear b ideogram vessel b206\n65766 linear b ideogram vessel b207\n65767 linear b ideogram vessel b208\n65768 linear b ideogram vessel b209\n65769 linear b ideogram vessel b210\n65770 linear b ideogram vessel b211\n65771 linear b ideogram vessel b212\n65772 linear b ideogram vessel b213\n65773 linear b ideogram vessel b214\n65774 linear b ideogram vessel b215\n65775 linear b ideogram vessel b216\n65776 linear b ideogram vessel b217\n65777 linear b ideogram vessel b218\n65778 linear b ideogram vessel b219\n65779 linear b ideogram vessel b221\n65780 linear b ideogram vessel b222\n65781 linear b ideogram vessel b226\n65782 linear b ideogram vessel b227\n65783 linear b ideogram vessel b228\n65784 linear b ideogram vessel b229\n65785 linear b ideogram vessel b250\n65786 linear b ideogram vessel b305\n65792 aegean word separator line\n65793 aegean word separator dot\n65794 aegean check mark\n65799 aegean number one\n65800 aegean number two\n65801 aegean number three\n65802 aegean number four\n65803 aegean number five\n65804 aegean number six\n65805 aegean number seven\n65806 aegean number eight\n65807 aegean number nine\n65808 aegean number ten\n65809 aegean number twenty\n65810 aegean number thirty\n65811 aegean number forty\n65812 aegean number fifty\n65813 aegean number sixty\n65814 aegean number seventy\n65815 aegean number eighty\n65816 aegean number ninety\n65817 aegean number one hundred\n65818 aegean number two hundred\n65819 aegean number three hundred\n65820 aegean number four hundred\n65821 aegean number five hundred\n65822 aegean number six hundred\n65823 aegean number seven hundred\n65824 aegean number eight hundred\n65825 aegean number nine hundred\n65826 aegean number one thousand\n65827 aegean number two thousand\n65828 aegean number three thousand\n65829 aegean number four thousand\n65830 aegean number five thousand\n65831 aegean number six thousand\n65832 aegean number seven thousand\n65833 aegean number eight thousand\n65834 aegean number nine thousand\n65835 aegean number ten thousand\n65836 aegean number twenty thousand\n65837 aegean number thirty thousand\n65838 aegean number forty thousand\n65839 aegean number fifty thousand\n65840 aegean number sixty thousand\n65841 aegean number seventy thousand\n65842 aegean number eighty thousand\n65843 aegean number ninety thousand\n65847 aegean weight base unit\n65848 aegean weight first subunit\n65849 aegean weight second subunit\n65850 aegean weight third subunit\n65851 aegean weight fourth subunit\n65852 aegean dry measure first subunit\n65853 aegean liquid measure first subunit\n65854 aegean measure second subunit\n65855 aegean measure third subunit\n65856 greek acrophonic attic one quarter\n65857 greek acrophonic attic one half\n65858 greek acrophonic attic one drachma\n65859 greek acrophonic attic five\n65860 greek acrophonic attic fifty\n65861 greek acrophonic attic five hundred\n65862 greek acrophonic attic five thousand\n65863 greek acrophonic attic fifty thousand\n65864 greek acrophonic attic five talents\n65865 greek acrophonic attic ten talents\n65866 greek acrophonic attic fifty talents\n65867 greek acrophonic attic one hundred talents\n65868 greek acrophonic attic five hundred talents\n65869 greek acrophonic attic one thousand talents\n65870 greek acrophonic attic five thousand talents\n65871 greek acrophonic attic five staters\n65872 greek acrophonic attic ten staters\n65873 greek acrophonic attic fifty staters\n65874 greek acrophonic attic one hundred staters\n65875 greek acrophonic attic five hundred staters\n65876 greek acrophonic attic one thousand staters\n65877 greek acrophonic attic ten thousand staters\n65878 greek acrophonic attic fifty thousand staters\n65879 greek acrophonic attic ten mnas\n65880 greek acrophonic heraeum one plethron\n65881 greek acrophonic thespian one\n65882 greek acrophonic hermionian one\n65883 greek acrophonic epidaurean two\n65884 greek acrophonic thespian two\n65885 greek acrophonic cyrenaic two drachmas\n65886 greek acrophonic epidaurean two drachmas\n65887 greek acrophonic troezenian five\n65888 greek acrophonic troezenian ten\n65889 greek acrophonic troezenian ten alternate form\n65890 greek acrophonic hermionian ten\n65891 greek acrophonic messenian ten\n65892 greek acrophonic thespian ten\n65893 greek acrophonic thespian thirty\n65894 greek acrophonic troezenian fifty\n65895 greek acrophonic troezenian fifty alternate form\n65896 greek acrophonic hermionian fifty\n65897 greek acrophonic thespian fifty\n65898 greek acrophonic thespian one hundred\n65899 greek acrophonic thespian three hundred\n65900 greek acrophonic epidaurean five hundred\n65901 greek acrophonic troezenian five hundred\n65902 greek acrophonic thespian five hundred\n65903 greek acrophonic carystian five hundred\n65904 greek acrophonic naxian five hundred\n65905 greek acrophonic thespian one thousand\n65906 greek acrophonic thespian five thousand\n65907 greek acrophonic delphic five mnas\n65908 greek acrophonic stratian fifty mnas\n65909 greek one half sign\n65910 greek one half sign alternate form\n65911 greek two thirds sign\n65912 greek three quarters sign\n65913 greek year sign\n65914 greek talent sign\n65915 greek drachma sign\n65916 greek obol sign\n65917 greek two obols sign\n65918 greek three obols sign\n65919 greek four obols sign\n65920 greek five obols sign\n65921 greek metretes sign\n65922 greek kyathos base sign\n65923 greek litra sign\n65924 greek ounkia sign\n65925 greek xestes sign\n65926 greek artabe sign\n65927 greek aroura sign\n65928 greek gramma sign\n65929 greek tryblion base sign\n65930 greek zero sign\n65931 greek one quarter sign\n65932 greek sinusoid sign\n65933 greek indiction sign\n65934 nomisma sign\n65936 roman sextans sign\n65937 roman uncia sign\n65938 roman semuncia sign\n65939 roman sextula sign\n65940 roman dimidia sextula sign\n65941 roman siliqua sign\n65942 roman denarius sign\n65943 roman quinarius sign\n65944 roman sestertius sign\n65945 roman dupondius sign\n65946 roman as sign\n65947 roman centurial sign\n65948 ascia symbol\n65952 greek symbol tau rho\n66000 phaistos disc sign pedestrian\n66001 phaistos disc sign plumed head\n66002 phaistos disc sign tattooed head\n66003 phaistos disc sign captive\n66004 phaistos disc sign child\n66005 phaistos disc sign woman\n66006 phaistos disc sign helmet\n66007 phaistos disc sign gauntlet\n66008 phaistos disc sign tiara\n66009 phaistos disc sign arrow\n66010 phaistos disc sign bow\n66011 phaistos disc sign shield\n66012 phaistos disc sign club\n66013 phaistos disc sign manacles\n66014 phaistos disc sign mattock\n66015 phaistos disc sign saw\n66016 phaistos disc sign lid\n66017 phaistos disc sign boomerang\n66018 phaistos disc sign carpentry plane\n66019 phaistos disc sign dolium\n66020 phaistos disc sign comb\n66021 phaistos disc sign sling\n66022 phaistos disc sign column\n66023 phaistos disc sign beehive\n66024 phaistos disc sign ship\n66025 phaistos disc sign horn\n66026 phaistos disc sign hide\n66027 phaistos disc sign bulls leg\n66028 phaistos disc sign cat\n66029 phaistos disc sign ram\n66030 phaistos disc sign eagle\n66031 phaistos disc sign dove\n66032 phaistos disc sign tunny\n66033 phaistos disc sign bee\n66034 phaistos disc sign plane tree\n66035 phaistos disc sign vine\n66036 phaistos disc sign papyrus\n66037 phaistos disc sign rosette\n66038 phaistos disc sign lily\n66039 phaistos disc sign ox back\n66040 phaistos disc sign flute\n66041 phaistos disc sign grater\n66042 phaistos disc sign strainer\n66043 phaistos disc sign small axe\n66044 phaistos disc sign wavy band\n66045 phaistos disc sign combining oblique stroke\n66176 lycian letter a\n66177 lycian letter e\n66178 lycian letter b\n66179 lycian letter bh\n66180 lycian letter g\n66181 lycian letter d\n66182 lycian letter i\n66183 lycian letter w\n66184 lycian letter z\n66185 lycian letter th\n66186 lycian letter j\n66187 lycian letter k\n66188 lycian letter q\n66189 lycian letter l\n66190 lycian letter m\n66191 lycian letter n\n66192 lycian letter mm\n66193 lycian letter nn\n66194 lycian letter u\n66195 lycian letter p\n66196 lycian letter kk\n66197 lycian letter r\n66198 lycian letter s\n66199 lycian letter t\n66200 lycian letter tt\n66201 lycian letter an\n66202 lycian letter en\n66203 lycian letter h\n66204 lycian letter x\n66208 carian letter a\n66209 carian letter p2\n66210 carian letter d\n66211 carian letter l\n66212 carian letter uuu\n66213 carian letter r\n66214 carian letter ld\n66215 carian letter a2\n66216 carian letter q\n66217 carian letter b\n66218 carian letter m\n66219 carian letter o\n66220 carian letter d2\n66221 carian letter t\n66222 carian letter sh\n66223 carian letter sh2\n66224 carian letter s\n66225 carian letter c-18\n66226 carian letter u\n66227 carian letter nn\n66228 carian letter x\n66229 carian letter n\n66230 carian letter tt2\n66231 carian letter p\n66232 carian letter ss\n66233 carian letter i\n66234 carian letter e\n66235 carian letter uuuu\n66236 carian letter k\n66237 carian letter k2\n66238 carian letter nd\n66239 carian letter uu\n66240 carian letter g\n66241 carian letter g2\n66242 carian letter st\n66243 carian letter st2\n66244 carian letter ng\n66245 carian letter ii\n66246 carian letter c-39\n66247 carian letter tt\n66248 carian letter uuu2\n66249 carian letter rr\n66250 carian letter mb\n66251 carian letter mb2\n66252 carian letter mb3\n66253 carian letter mb4\n66254 carian letter ld2\n66255 carian letter e2\n66256 carian letter uuu3\n66272 coptic epact thousands mark\n66273 coptic epact digit one\n66274 coptic epact digit two\n66275 coptic epact digit three\n66276 coptic epact digit four\n66277 coptic epact digit five\n66278 coptic epact digit six\n66279 coptic epact digit seven\n66280 coptic epact digit eight\n66281 coptic epact digit nine\n66282 coptic epact number ten\n66283 coptic epact number twenty\n66284 coptic epact number thirty\n66285 coptic epact number forty\n66286 coptic epact number fifty\n66287 coptic epact number sixty\n66288 coptic epact number seventy\n66289 coptic epact number eighty\n66290 coptic epact number ninety\n66291 coptic epact number one hundred\n66292 coptic epact number two hundred\n66293 coptic epact number three hundred\n66294 coptic epact number four hundred\n66295 coptic epact number five hundred\n66296 coptic epact number six hundred\n66297 coptic epact number seven hundred\n66298 coptic epact number eight hundred\n66299 coptic epact number nine hundred\n66304 old italic letter a\n66305 old italic letter be\n66306 old italic letter ke\n66307 old italic letter de\n66308 old italic letter e\n66309 old italic letter ve\n66310 old italic letter ze\n66311 old italic letter he\n66312 old italic letter the\n66313 old italic letter i\n66314 old italic letter ka\n66315 old italic letter el\n66316 old italic letter em\n66317 old italic letter en\n66318 old italic letter esh\n66319 old italic letter o\n66320 old italic letter pe\n66321 old italic letter she\n66322 old italic letter ku\n66323 old italic letter er\n66324 old italic letter es\n66325 old italic letter te\n66326 old italic letter u\n66327 old italic letter eks\n66328 old italic letter phe\n66329 old italic letter khe\n66330 old italic letter ef\n66331 old italic letter ers\n66332 old italic letter che\n66333 old italic letter ii\n66334 old italic letter uu\n66335 old italic letter ess\n66336 old italic numeral one\n66337 old italic numeral five\n66338 old italic numeral ten\n66339 old italic numeral fifty\n66349 old italic letter ye\n66350 old italic letter northern tse\n66351 old italic letter southern tse\n66352 gothic letter ahsa\n66353 gothic letter bairkan\n66354 gothic letter giba\n66355 gothic letter dags\n66356 gothic letter aihvus\n66357 gothic letter qairthra\n66358 gothic letter iuja\n66359 gothic letter hagl\n66360 gothic letter thiuth\n66361 gothic letter eis\n66362 gothic letter kusma\n66363 gothic letter lagus\n66364 gothic letter manna\n66365 gothic letter nauths\n66366 gothic letter jer\n66367 gothic letter urus\n66368 gothic letter pairthra\n66369 gothic letter ninety\n66370 gothic letter raida\n66371 gothic letter sauil\n66372 gothic letter teiws\n66373 gothic letter winja\n66374 gothic letter faihu\n66375 gothic letter iggws\n66376 gothic letter hwair\n66377 gothic letter othal\n66378 gothic letter nine hundred\n66384 old permic letter an\n66385 old permic letter bur\n66386 old permic letter gai\n66387 old permic letter doi\n66388 old permic letter e\n66389 old permic letter zhoi\n66390 old permic letter dzhoi\n66391 old permic letter zata\n66392 old permic letter dzita\n66393 old permic letter i\n66394 old permic letter koke\n66395 old permic letter lei\n66396 old permic letter menoe\n66397 old permic letter nenoe\n66398 old permic letter vooi\n66399 old permic letter peei\n66400 old permic letter rei\n66401 old permic letter sii\n66402 old permic letter tai\n66403 old permic letter u\n66404 old permic letter chery\n66405 old permic letter shooi\n66406 old permic letter shchooi\n66407 old permic letter yry\n66408 old permic letter yeru\n66409 old permic letter o\n66410 old permic letter oo\n66411 old permic letter ef\n66412 old permic letter ha\n66413 old permic letter tsiu\n66414 old permic letter ver\n66415 old permic letter yer\n66416 old permic letter yeri\n66417 old permic letter yat\n66418 old permic letter ie\n66419 old permic letter yu\n66420 old permic letter ya\n66421 old permic letter ia\n66422 combining old permic letter an\n66423 combining old permic letter doi\n66424 combining old permic letter zata\n66425 combining old permic letter nenoe\n66426 combining old permic letter sii\n66432 ugaritic letter alpa\n66433 ugaritic letter beta\n66434 ugaritic letter gamla\n66435 ugaritic letter kha\n66436 ugaritic letter delta\n66437 ugaritic letter ho\n66438 ugaritic letter wo\n66439 ugaritic letter zeta\n66440 ugaritic letter hota\n66441 ugaritic letter tet\n66442 ugaritic letter yod\n66443 ugaritic letter kaf\n66444 ugaritic letter shin\n66445 ugaritic letter lamda\tlambda\n66446 ugaritic letter mem\n66447 ugaritic letter dhal\n66448 ugaritic letter nun\n66449 ugaritic letter zu\n66450 ugaritic letter samka\n66451 ugaritic letter ain\n66452 ugaritic letter pu\n66453 ugaritic letter sade\n66454 ugaritic letter qopa\n66455 ugaritic letter rasha\n66456 ugaritic letter thanna\n66457 ugaritic letter ghain\n66458 ugaritic letter to\n66459 ugaritic letter i\n66460 ugaritic letter u\n66461 ugaritic letter ssu\n66463 ugaritic word divider\n66464 old persian sign a\n66465 old persian sign i\n66466 old persian sign u\n66467 old persian sign ka\n66468 old persian sign ku\n66469 old persian sign ga\n66470 old persian sign gu\n66471 old persian sign xa\n66472 old persian sign ca\n66473 old persian sign ja\n66474 old persian sign ji\n66475 old persian sign ta\n66476 old persian sign tu\n66477 old persian sign da\n66478 old persian sign di\n66479 old persian sign du\n66480 old persian sign tha\n66481 old persian sign pa\n66482 old persian sign ba\n66483 old persian sign fa\n66484 old persian sign na\n66485 old persian sign nu\n66486 old persian sign ma\n66487 old persian sign mi\n66488 old persian sign mu\n66489 old persian sign ya\n66490 old persian sign va\n66491 old persian sign vi\n66492 old persian sign ra\n66493 old persian sign ru\n66494 old persian sign la\n66495 old persian sign sa\n66496 old persian sign za\n66497 old persian sign sha\n66498 old persian sign ssa\n66499 old persian sign ha\n66504 old persian sign auramazdaa\n66505 old persian sign auramazdaa-2\n66506 old persian sign auramazdaaha\n66507 old persian sign xshaayathiya\n66508 old persian sign dahyaaush\n66509 old persian sign dahyaaush-2\n66510 old persian sign baga\n66511 old persian sign buumish\n66512 old persian word divider\n66513 old persian number one\n66514 old persian number two\n66515 old persian number ten\n66516 old persian number twenty\n66517 old persian number hundred\n66560 deseret capital letter long i\n66561 deseret capital letter long e\n66562 deseret capital letter long a\n66563 deseret capital letter long ah\n66564 deseret capital letter long o\n66565 deseret capital letter long oo\n66566 deseret capital letter short i\n66567 deseret capital letter short e\n66568 deseret capital letter short a\n66569 deseret capital letter short ah\n66570 deseret capital letter short o\n66571 deseret capital letter short oo\n66572 deseret capital letter ay\n66573 deseret capital letter ow\n66574 deseret capital letter wu\n66575 deseret capital letter yee\n66576 deseret capital letter h\n66577 deseret capital letter pee\n66578 deseret capital letter bee\n66579 deseret capital letter tee\n66580 deseret capital letter dee\n66581 deseret capital letter chee\n66582 deseret capital letter jee\n66583 deseret capital letter kay\n66584 deseret capital letter gay\n66585 deseret capital letter ef\n66586 deseret capital letter vee\n66587 deseret capital letter eth\n66588 deseret capital letter thee\n66589 deseret capital letter es\n66590 deseret capital letter zee\n66591 deseret capital letter esh\n66592 deseret capital letter zhee\n66593 deseret capital letter er\n66594 deseret capital letter el\n66595 deseret capital letter em\n66596 deseret capital letter en\n66597 deseret capital letter eng\n66598 deseret capital letter oi\n66599 deseret capital letter ew\n66600 deseret small letter long i\n66601 deseret small letter long e\n66602 deseret small letter long a\n66603 deseret small letter long ah\n66604 deseret small letter long o\n66605 deseret small letter long oo\n66606 deseret small letter short i\n66607 deseret small letter short e\n66608 deseret small letter short a\n66609 deseret small letter short ah\n66610 deseret small letter short o\n66611 deseret small letter short oo\n66612 deseret small letter ay\n66613 deseret small letter ow\n66614 deseret small letter wu\n66615 deseret small letter yee\n66616 deseret small letter h\n66617 deseret small letter pee\n66618 deseret small letter bee\n66619 deseret small letter tee\n66620 deseret small letter dee\n66621 deseret small letter chee\n66622 deseret small letter jee\n66623 deseret small letter kay\n66624 deseret small letter gay\n66625 deseret small letter ef\n66626 deseret small letter vee\n66627 deseret small letter eth\n66628 deseret small letter thee\n66629 deseret small letter es\n66630 deseret small letter zee\n66631 deseret small letter esh\n66632 deseret small letter zhee\n66633 deseret small letter er\n66634 deseret small letter el\n66635 deseret small letter em\n66636 deseret small letter en\n66637 deseret small letter eng\n66638 deseret small letter oi\n66639 deseret small letter ew\n66640 shavian letter peep\n66641 shavian letter tot\n66642 shavian letter kick\n66643 shavian letter fee\n66644 shavian letter thigh\n66645 shavian letter so\n66646 shavian letter sure\n66647 shavian letter church\n66648 shavian letter yea\n66649 shavian letter hung\n66650 shavian letter bib\n66651 shavian letter dead\n66652 shavian letter gag\n66653 shavian letter vow\n66654 shavian letter they\n66655 shavian letter zoo\n66656 shavian letter measure\n66657 shavian letter judge\n66658 shavian letter woe\n66659 shavian letter ha-ha\n66660 shavian letter loll\n66661 shavian letter mime\n66662 shavian letter if\n66663 shavian letter egg\n66664 shavian letter ash\n66665 shavian letter ado\n66666 shavian letter on\n66667 shavian letter wool\n66668 shavian letter out\n66669 shavian letter ah\n66670 shavian letter roar\n66671 shavian letter nun\n66672 shavian letter eat\n66673 shavian letter age\n66674 shavian letter ice\n66675 shavian letter up\n66676 shavian letter oak\n66677 shavian letter ooze\n66678 shavian letter oil\n66679 shavian letter awe\n66680 shavian letter are\n66681 shavian letter or\n66682 shavian letter air\n66683 shavian letter err\n66684 shavian letter array\n66685 shavian letter ear\n66686 shavian letter ian\n66687 shavian letter yew\n66688 osmanya letter alef\n66689 osmanya letter ba\n66690 osmanya letter ta\n66691 osmanya letter ja\n66692 osmanya letter xa\n66693 osmanya letter kha\n66694 osmanya letter deel\n66695 osmanya letter ra\n66696 osmanya letter sa\n66697 osmanya letter shiin\n66698 osmanya letter dha\n66699 osmanya letter cayn\n66700 osmanya letter ga\n66701 osmanya letter fa\n66702 osmanya letter qaaf\n66703 osmanya letter kaaf\n66704 osmanya letter laan\n66705 osmanya letter miin\n66706 osmanya letter nuun\n66707 osmanya letter waw\n66708 osmanya letter ha\n66709 osmanya letter ya\n66710 osmanya letter a\n66711 osmanya letter e\n66712 osmanya letter i\n66713 osmanya letter o\n66714 osmanya letter u\n66715 osmanya letter aa\n66716 osmanya letter ee\n66717 osmanya letter oo\n66720 osmanya digit zero\n66721 osmanya digit one\n66722 osmanya digit two\n66723 osmanya digit three\n66724 osmanya digit four\n66725 osmanya digit five\n66726 osmanya digit six\n66727 osmanya digit seven\n66728 osmanya digit eight\n66729 osmanya digit nine\n66736 osage capital letter a\n66737 osage capital letter ai\n66738 osage capital letter ain\n66739 osage capital letter ah\n66740 osage capital letter bra\n66741 osage capital letter cha\n66742 osage capital letter ehcha\n66743 osage capital letter e\n66744 osage capital letter ein\n66745 osage capital letter ha\n66746 osage capital letter hya\n66747 osage capital letter i\n66748 osage capital letter ka\n66749 osage capital letter ehka\n66750 osage capital letter kya\n66751 osage capital letter la\n66752 osage capital letter ma\n66753 osage capital letter na\n66754 osage capital letter o\n66755 osage capital letter oin\n66756 osage capital letter pa\n66757 osage capital letter ehpa\n66758 osage capital letter sa\n66759 osage capital letter sha\n66760 osage capital letter ta\n66761 osage capital letter ehta\n66762 osage capital letter tsa\n66763 osage capital letter ehtsa\n66764 osage capital letter tsha\n66765 osage capital letter dha\n66766 osage capital letter u\n66767 osage capital letter wa\n66768 osage capital letter kha\n66769 osage capital letter gha\n66770 osage capital letter za\n66771 osage capital letter zha\n66776 osage small letter a\n66777 osage small letter ai\n66778 osage small letter ain\n66779 osage small letter ah\n66780 osage small letter bra\n66781 osage small letter cha\n66782 osage small letter ehcha\n66783 osage small letter e\n66784 osage small letter ein\n66785 osage small letter ha\n66786 osage small letter hya\n66787 osage small letter i\n66788 osage small letter ka\n66789 osage small letter ehka\n66790 osage small letter kya\n66791 osage small letter la\n66792 osage small letter ma\n66793 osage small letter na\n66794 osage small letter o\n66795 osage small letter oin\n66796 osage small letter pa\n66797 osage small letter ehpa\n66798 osage small letter sa\n66799 osage small letter sha\n66800 osage small letter ta\n66801 osage small letter ehta\n66802 osage small letter tsa\n66803 osage small letter ehtsa\n66804 osage small letter tsha\n66805 osage small letter dha\n66806 osage small letter u\n66807 osage small letter wa\n66808 osage small letter kha\n66809 osage small letter gha\n66810 osage small letter za\n66811 osage small letter zha\n66816 elbasan letter a\n66817 elbasan letter be\n66818 elbasan letter ce\n66819 elbasan letter che\n66820 elbasan letter de\n66821 elbasan letter nde\n66822 elbasan letter dhe\n66823 elbasan letter ei\n66824 elbasan letter e\n66825 elbasan letter fe\n66826 elbasan letter ge\n66827 elbasan letter gje\n66828 elbasan letter he\n66829 elbasan letter i\n66830 elbasan letter je\n66831 elbasan letter ke\n66832 elbasan letter le\n66833 elbasan letter lle\n66834 elbasan letter me\n66835 elbasan letter ne\n66836 elbasan letter na\n66837 elbasan letter nje\n66838 elbasan letter o\n66839 elbasan letter pe\n66840 elbasan letter qe\n66841 elbasan letter re\n66842 elbasan letter rre\n66843 elbasan letter se\n66844 elbasan letter she\n66845 elbasan letter te\n66846 elbasan letter the\n66847 elbasan letter u\n66848 elbasan letter ve\n66849 elbasan letter xe\n66850 elbasan letter y\n66851 elbasan letter ze\n66852 elbasan letter zhe\n66853 elbasan letter ghe\n66854 elbasan letter ghamma\n66855 elbasan letter khe\n66864 caucasian albanian letter alt\n66865 caucasian albanian letter bet\n66866 caucasian albanian letter gim\n66867 caucasian albanian letter dat\n66868 caucasian albanian letter eb\n66869 caucasian albanian letter zarl\n66870 caucasian albanian letter eyn\n66871 caucasian albanian letter zhil\n66872 caucasian albanian letter tas\n66873 caucasian albanian letter cha\n66874 caucasian albanian letter yowd\n66875 caucasian albanian letter zha\n66876 caucasian albanian letter irb\n66877 caucasian albanian letter sha\n66878 caucasian albanian letter lan\n66879 caucasian albanian letter inya\n66880 caucasian albanian letter xeyn\n66881 caucasian albanian letter dyan\n66882 caucasian albanian letter car\n66883 caucasian albanian letter jhox\n66884 caucasian albanian letter kar\n66885 caucasian albanian letter lyit\n66886 caucasian albanian letter heyt\n66887 caucasian albanian letter qay\n66888 caucasian albanian letter aor\n66889 caucasian albanian letter choy\n66890 caucasian albanian letter chi\n66891 caucasian albanian letter cyay\n66892 caucasian albanian letter maq\n66893 caucasian albanian letter qar\n66894 caucasian albanian letter nowc\n66895 caucasian albanian letter dzyay\n66896 caucasian albanian letter shak\n66897 caucasian albanian letter jayn\n66898 caucasian albanian letter on\n66899 caucasian albanian letter tyay\n66900 caucasian albanian letter fam\n66901 caucasian albanian letter dzay\n66902 caucasian albanian letter chat\n66903 caucasian albanian letter pen\n66904 caucasian albanian letter gheys\n66905 caucasian albanian letter rat\n66906 caucasian albanian letter seyk\n66907 caucasian albanian letter veyz\n66908 caucasian albanian letter tiwr\n66909 caucasian albanian letter shoy\n66910 caucasian albanian letter iwn\n66911 caucasian albanian letter cyaw\n66912 caucasian albanian letter cayn\n66913 caucasian albanian letter yayd\n66914 caucasian albanian letter piwr\n66915 caucasian albanian letter kiw\n66927 caucasian albanian citation mark\n66928 vithkuqi capital letter a\n66929 vithkuqi capital letter bbe\n66930 vithkuqi capital letter be\n66931 vithkuqi capital letter ce\n66932 vithkuqi capital letter che\n66933 vithkuqi capital letter de\n66934 vithkuqi capital letter dhe\n66935 vithkuqi capital letter ei\n66936 vithkuqi capital letter e\n66937 vithkuqi capital letter fe\n66938 vithkuqi capital letter ga\n66940 vithkuqi capital letter ha\n66941 vithkuqi capital letter hha\n66942 vithkuqi capital letter i\n66943 vithkuqi capital letter ije\n66944 vithkuqi capital letter je\n66945 vithkuqi capital letter ka\n66946 vithkuqi capital letter la\n66947 vithkuqi capital letter lla\n66948 vithkuqi capital letter me\n66949 vithkuqi capital letter ne\n66950 vithkuqi capital letter nje\n66951 vithkuqi capital letter o\n66952 vithkuqi capital letter pe\n66953 vithkuqi capital letter qa\n66954 vithkuqi capital letter re\n66956 vithkuqi capital letter se\n66957 vithkuqi capital letter she\n66958 vithkuqi capital letter te\n66959 vithkuqi capital letter the\n66960 vithkuqi capital letter u\n66961 vithkuqi capital letter ve\n66962 vithkuqi capital letter xe\n66964 vithkuqi capital letter y\n66965 vithkuqi capital letter ze\n66967 vithkuqi small letter a\n66968 vithkuqi small letter bbe\n66969 vithkuqi small letter be\n66970 vithkuqi small letter ce\n66971 vithkuqi small letter che\n66972 vithkuqi small letter de\n66973 vithkuqi small letter dhe\n66974 vithkuqi small letter ei\n66975 vithkuqi small letter e\n66976 vithkuqi small letter fe\n66977 vithkuqi small letter ga\n66979 vithkuqi small letter ha\n66980 vithkuqi small letter hha\n66981 vithkuqi small letter i\n66982 vithkuqi small letter ije\n66983 vithkuqi small letter je\n66984 vithkuqi small letter ka\n66985 vithkuqi small letter la\n66986 vithkuqi small letter lla\n66987 vithkuqi small letter me\n66988 vithkuqi small letter ne\n66989 vithkuqi small letter nje\n66990 vithkuqi small letter o\n66991 vithkuqi small letter pe\n66992 vithkuqi small letter qa\n66993 vithkuqi small letter re\n66995 vithkuqi small letter se\n66996 vithkuqi small letter she\n66997 vithkuqi small letter te\n66998 vithkuqi small letter the\n66999 vithkuqi small letter u\n67000 vithkuqi small letter ve\n67001 vithkuqi small letter xe\n67003 vithkuqi small letter y\n67004 vithkuqi small letter ze\n67008 todhri letter a\n67009 todhri letter as\n67010 todhri letter ba\n67011 todhri letter mba\n67012 todhri letter ca\n67013 todhri letter cha\n67014 todhri letter da\n67015 todhri letter nda\n67016 todhri letter dha\n67017 todhri letter ei\n67018 todhri letter e\n67019 todhri letter fa\n67020 todhri letter ga\n67021 todhri letter nga\n67022 todhri letter gja\n67023 todhri letter ngja\n67024 todhri letter ha\n67025 todhri letter hja\n67026 todhri letter i\n67027 todhri letter ja\n67028 todhri letter ka\n67029 todhri letter la\n67030 todhri letter lla\n67031 todhri letter ma\n67032 todhri letter na\n67033 todhri letter njan\n67034 todhri letter o\n67035 todhri letter pa\n67036 todhri letter qa\n67037 todhri letter ra\n67038 todhri letter rra\n67039 todhri letter sa\n67040 todhri letter sha\n67041 todhri letter shta\n67042 todhri letter ta\n67043 todhri letter tha\n67044 todhri letter u\n67045 todhri letter va\n67046 todhri letter xa\n67047 todhri letter nxa\n67048 todhri letter xha\n67049 todhri letter nxha\n67050 todhri letter y\n67051 todhri letter jy\n67052 todhri letter za\n67053 todhri letter zha\n67054 todhri letter gha\n67055 todhri letter sta\n67056 todhri letter skan\n67057 todhri letter kha\n67058 todhri letter psa\n67059 todhri letter oo\n67072 linear a sign ab001\n67073 linear a sign ab002\n67074 linear a sign ab003\n67075 linear a sign ab004\n67076 linear a sign ab005\n67077 linear a sign ab006\n67078 linear a sign ab007\n67079 linear a sign ab008\n67080 linear a sign ab009\n67081 linear a sign ab010\n67082 linear a sign ab011\n67083 linear a sign ab013\n67084 linear a sign ab016\n67085 linear a sign ab017\n67086 linear a sign ab020\n67087 linear a sign ab021\n67088 linear a sign ab021f\n67089 linear a sign ab021m\n67090 linear a sign ab022\n67091 linear a sign ab022f\n67092 linear a sign ab022m\n67093 linear a sign ab023\n67094 linear a sign ab023m\n67095 linear a sign ab024\n67096 linear a sign ab026\n67097 linear a sign ab027\n67098 linear a sign ab028\n67099 linear a sign a028b\n67100 linear a sign ab029\n67101 linear a sign ab030\n67102 linear a sign ab031\n67103 linear a sign ab034\n67104 linear a sign ab037\n67105 linear a sign ab038\n67106 linear a sign ab039\n67107 linear a sign ab040\n67108 linear a sign ab041\n67109 linear a sign ab044\n67110 linear a sign ab045\n67111 linear a sign ab046\n67112 linear a sign ab047\n67113 linear a sign ab048\n67114 linear a sign ab049\n67115 linear a sign ab050\n67116 linear a sign ab051\n67117 linear a sign ab053\n67118 linear a sign ab054\n67119 linear a sign ab055\n67120 linear a sign ab056\n67121 linear a sign ab057\n67122 linear a sign ab058\n67123 linear a sign ab059\n67124 linear a sign ab060\n67125 linear a sign ab061\n67126 linear a sign ab065\n67127 linear a sign ab066\n67128 linear a sign ab067\n67129 linear a sign ab069\n67130 linear a sign ab070\n67131 linear a sign ab073\n67132 linear a sign ab074\n67133 linear a sign ab076\n67134 linear a sign ab077\n67135 linear a sign ab078\n67136 linear a sign ab079\n67137 linear a sign ab080\n67138 linear a sign ab081\n67139 linear a sign ab082\n67140 linear a sign ab085\n67141 linear a sign ab086\n67142 linear a sign ab087\n67143 linear a sign a100-102\n67144 linear a sign ab118\n67145 linear a sign ab120\n67146 linear a sign a120b\n67147 linear a sign ab122\n67148 linear a sign ab123\n67149 linear a sign ab131a\n67150 linear a sign ab131b\n67151 linear a sign a131c\n67152 linear a sign ab164\n67153 linear a sign ab171\n67154 linear a sign ab180\n67155 linear a sign ab188\n67156 linear a sign ab191\n67157 linear a sign a301\n67158 linear a sign a302\n67159 linear a sign a303\n67160 linear a sign a304\n67161 linear a sign a305\n67162 linear a sign a306\n67163 linear a sign a307\n67164 linear a sign a308\n67165 linear a sign a309a\n67166 linear a sign a309b\n67167 linear a sign a309c\n67168 linear a sign a310\n67169 linear a sign a311\n67170 linear a sign a312\n67171 linear a sign a313a\n67172 linear a sign a313b\n67173 linear a sign a313c\n67174 linear a sign a314\n67175 linear a sign a315\n67176 linear a sign a316\n67177 linear a sign a317\n67178 linear a sign a318\n67179 linear a sign a319\n67180 linear a sign a320\n67181 linear a sign a321\n67182 linear a sign a322\n67183 linear a sign a323\n67184 linear a sign a324\n67185 linear a sign a325\n67186 linear a sign a326\n67187 linear a sign a327\n67188 linear a sign a328\n67189 linear a sign a329\n67190 linear a sign a330\n67191 linear a sign a331\n67192 linear a sign a332\n67193 linear a sign a333\n67194 linear a sign a334\n67195 linear a sign a335\n67196 linear a sign a336\n67197 linear a sign a337\n67198 linear a sign a338\n67199 linear a sign a339\n67200 linear a sign a340\n67201 linear a sign a341\n67202 linear a sign a342\n67203 linear a sign a343\n67204 linear a sign a344\n67205 linear a sign a345\n67206 linear a sign a346\n67207 linear a sign a347\n67208 linear a sign a348\n67209 linear a sign a349\n67210 linear a sign a350\n67211 linear a sign a351\n67212 linear a sign a352\n67213 linear a sign a353\n67214 linear a sign a354\n67215 linear a sign a355\n67216 linear a sign a356\n67217 linear a sign a357\n67218 linear a sign a358\n67219 linear a sign a359\n67220 linear a sign a360\n67221 linear a sign a361\n67222 linear a sign a362\n67223 linear a sign a363\n67224 linear a sign a364\n67225 linear a sign a365\n67226 linear a sign a366\n67227 linear a sign a367\n67228 linear a sign a368\n67229 linear a sign a369\n67230 linear a sign a370\n67231 linear a sign a371\n67232 linear a sign a400-vas\n67233 linear a sign a401-vas\n67234 linear a sign a402-vas\n67235 linear a sign a403-vas\n67236 linear a sign a404-vas\n67237 linear a sign a405-vas\n67238 linear a sign a406-vas\n67239 linear a sign a407-vas\n67240 linear a sign a408-vas\n67241 linear a sign a409-vas\n67242 linear a sign a410-vas\n67243 linear a sign a411-vas\n67244 linear a sign a412-vas\n67245 linear a sign a413-vas\n67246 linear a sign a414-vas\n67247 linear a sign a415-vas\n67248 linear a sign a416-vas\n67249 linear a sign a417-vas\n67250 linear a sign a418-vas\n67251 linear a sign a501\n67252 linear a sign a502\n67253 linear a sign a503\n67254 linear a sign a504\n67255 linear a sign a505\n67256 linear a sign a506\n67257 linear a sign a508\n67258 linear a sign a509\n67259 linear a sign a510\n67260 linear a sign a511\n67261 linear a sign a512\n67262 linear a sign a513\n67263 linear a sign a515\n67264 linear a sign a516\n67265 linear a sign a520\n67266 linear a sign a521\n67267 linear a sign a523\n67268 linear a sign a524\n67269 linear a sign a525\n67270 linear a sign a526\n67271 linear a sign a527\n67272 linear a sign a528\n67273 linear a sign a529\n67274 linear a sign a530\n67275 linear a sign a531\n67276 linear a sign a532\n67277 linear a sign a534\n67278 linear a sign a535\n67279 linear a sign a536\n67280 linear a sign a537\n67281 linear a sign a538\n67282 linear a sign a539\n67283 linear a sign a540\n67284 linear a sign a541\n67285 linear a sign a542\n67286 linear a sign a545\n67287 linear a sign a547\n67288 linear a sign a548\n67289 linear a sign a549\n67290 linear a sign a550\n67291 linear a sign a551\n67292 linear a sign a552\n67293 linear a sign a553\n67294 linear a sign a554\n67295 linear a sign a555\n67296 linear a sign a556\n67297 linear a sign a557\n67298 linear a sign a559\n67299 linear a sign a563\n67300 linear a sign a564\n67301 linear a sign a565\n67302 linear a sign a566\n67303 linear a sign a568\n67304 linear a sign a569\n67305 linear a sign a570\n67306 linear a sign a571\n67307 linear a sign a572\n67308 linear a sign a573\n67309 linear a sign a574\n67310 linear a sign a575\n67311 linear a sign a576\n67312 linear a sign a577\n67313 linear a sign a578\n67314 linear a sign a579\n67315 linear a sign a580\n67316 linear a sign a581\n67317 linear a sign a582\n67318 linear a sign a583\n67319 linear a sign a584\n67320 linear a sign a585\n67321 linear a sign a586\n67322 linear a sign a587\n67323 linear a sign a588\n67324 linear a sign a589\n67325 linear a sign a591\n67326 linear a sign a592\n67327 linear a sign a594\n67328 linear a sign a595\n67329 linear a sign a596\n67330 linear a sign a598\n67331 linear a sign a600\n67332 linear a sign a601\n67333 linear a sign a602\n67334 linear a sign a603\n67335 linear a sign a604\n67336 linear a sign a606\n67337 linear a sign a608\n67338 linear a sign a609\n67339 linear a sign a610\n67340 linear a sign a611\n67341 linear a sign a612\n67342 linear a sign a613\n67343 linear a sign a614\n67344 linear a sign a615\n67345 linear a sign a616\n67346 linear a sign a617\n67347 linear a sign a618\n67348 linear a sign a619\n67349 linear a sign a620\n67350 linear a sign a621\n67351 linear a sign a622\n67352 linear a sign a623\n67353 linear a sign a624\n67354 linear a sign a626\n67355 linear a sign a627\n67356 linear a sign a628\n67357 linear a sign a629\n67358 linear a sign a634\n67359 linear a sign a637\n67360 linear a sign a638\n67361 linear a sign a640\n67362 linear a sign a642\n67363 linear a sign a643\n67364 linear a sign a644\n67365 linear a sign a645\n67366 linear a sign a646\n67367 linear a sign a648\n67368 linear a sign a649\n67369 linear a sign a651\n67370 linear a sign a652\n67371 linear a sign a653\n67372 linear a sign a654\n67373 linear a sign a655\n67374 linear a sign a656\n67375 linear a sign a657\n67376 linear a sign a658\n67377 linear a sign a659\n67378 linear a sign a660\n67379 linear a sign a661\n67380 linear a sign a662\n67381 linear a sign a663\n67382 linear a sign a664\n67392 linear a sign a701 a\n67393 linear a sign a702 b\n67394 linear a sign a703 d\n67395 linear a sign a704 e\n67396 linear a sign a705 f\n67397 linear a sign a706 h\n67398 linear a sign a707 j\n67399 linear a sign a708 k\n67400 linear a sign a709 l\n67401 linear a sign a709-2 l2\n67402 linear a sign a709-3 l3\n67403 linear a sign a709-4 l4\n67404 linear a sign a709-6 l6\n67405 linear a sign a710 w\n67406 linear a sign a711 x\n67407 linear a sign a712 y\n67408 linear a sign a713 omega\n67409 linear a sign a714 abb\n67410 linear a sign a715 bb\n67411 linear a sign a717 dd\n67412 linear a sign a726 eyyy\n67413 linear a sign a732 je\n67424 linear a sign a800\n67425 linear a sign a801\n67426 linear a sign a802\n67427 linear a sign a803\n67428 linear a sign a804\n67429 linear a sign a805\n67430 linear a sign a806\n67431 linear a sign a807\n67456 modifier letter small capital aa\n67457 modifier letter superscript triangular colon\n67458 modifier letter superscript half triangular colon\n67459 modifier letter small ae\n67460 modifier letter small capital b\n67461 modifier letter small b with hook\n67463 modifier letter small dz digraph\n67464 modifier letter small dz digraph with retroflex hook\n67465 modifier letter small dz digraph with curl\n67466 modifier letter small dezh digraph\n67467 modifier letter small d with tail\n67468 modifier letter small d with hook\n67469 modifier letter small d with hook and tail\n67470 modifier letter small reversed e\n67471 modifier letter small closed reversed open e\n67472 modifier letter small feng digraph\n67473 modifier letter small rams horn\n67474 modifier letter small capital g\n67475 modifier letter small g with hook\n67476 modifier letter small capital g with hook\n67477 modifier letter small h with stroke\n67478 modifier letter small capital h\n67479 modifier letter small heng with hook\n67480 modifier letter small dotless j with stroke and hook\n67481 modifier letter small ls digraph\n67482 modifier letter small lz digraph\n67483 modifier letter small l with belt\n67484 modifier letter small capital l with belt\n67485 modifier letter small l with retroflex hook and belt\n67486 modifier letter small lezh\n67487 modifier letter small lezh with retroflex hook\n67488 modifier letter small turned y\n67489 modifier letter small turned y with belt\n67490 modifier letter small o with stroke\n67491 modifier letter small capital oe\n67492 modifier letter small closed omega\n67493 modifier letter small q\n67494 modifier letter small turned r with long leg\n67495 modifier letter small turned r with long leg and retroflex hook\n67496 modifier letter small r with tail\n67497 modifier letter small r with fishhook\n67498 modifier letter small capital r\n67499 modifier letter small tc digraph with curl\n67500 modifier letter small ts digraph\n67501 modifier letter small ts digraph with retroflex hook\n67502 modifier letter small tesh digraph\n67503 modifier letter small t with retroflex hook\n67504 modifier letter small v with right hook\n67506 modifier letter small capital y\n67507 modifier letter glottal stop with stroke\n67508 modifier letter reversed glottal stop with stroke\n67509 modifier letter bilabial click\n67510 modifier letter dental click\n67511 modifier letter lateral click\n67512 modifier letter alveolar click\n67513 modifier letter retroflex click with retroflex hook\n67514 modifier letter small s with curl\n67584 cypriot syllable a\n67585 cypriot syllable e\n67586 cypriot syllable i\n67587 cypriot syllable o\n67588 cypriot syllable u\n67589 cypriot syllable ja\n67592 cypriot syllable jo\n67594 cypriot syllable ka\n67595 cypriot syllable ke\n67596 cypriot syllable ki\n67597 cypriot syllable ko\n67598 cypriot syllable ku\n67599 cypriot syllable la\n67600 cypriot syllable le\n67601 cypriot syllable li\n67602 cypriot syllable lo\n67603 cypriot syllable lu\n67604 cypriot syllable ma\n67605 cypriot syllable me\n67606 cypriot syllable mi\n67607 cypriot syllable mo\n67608 cypriot syllable mu\n67609 cypriot syllable na\n67610 cypriot syllable ne\n67611 cypriot syllable ni\n67612 cypriot syllable no\n67613 cypriot syllable nu\n67614 cypriot syllable pa\n67615 cypriot syllable pe\n67616 cypriot syllable pi\n67617 cypriot syllable po\n67618 cypriot syllable pu\n67619 cypriot syllable ra\n67620 cypriot syllable re\n67621 cypriot syllable ri\n67622 cypriot syllable ro\n67623 cypriot syllable ru\n67624 cypriot syllable sa\n67625 cypriot syllable se\n67626 cypriot syllable si\n67627 cypriot syllable so\n67628 cypriot syllable su\n67629 cypriot syllable ta\n67630 cypriot syllable te\n67631 cypriot syllable ti\n67632 cypriot syllable to\n67633 cypriot syllable tu\n67634 cypriot syllable wa\n67635 cypriot syllable we\n67636 cypriot syllable wi\n67637 cypriot syllable wo\n67639 cypriot syllable xa\n67640 cypriot syllable xe\n67644 cypriot syllable za\n67647 cypriot syllable zo\n67648 imperial aramaic letter aleph\n67649 imperial aramaic letter beth\n67650 imperial aramaic letter gimel\n67651 imperial aramaic letter daleth\n67652 imperial aramaic letter he\n67653 imperial aramaic letter waw\n67654 imperial aramaic letter zayin\n67655 imperial aramaic letter heth\n67656 imperial aramaic letter teth\n67657 imperial aramaic letter yodh\n67658 imperial aramaic letter kaph\n67659 imperial aramaic letter lamedh\n67660 imperial aramaic letter mem\n67661 imperial aramaic letter nun\n67662 imperial aramaic letter samekh\n67663 imperial aramaic letter ayin\n67664 imperial aramaic letter pe\n67665 imperial aramaic letter sadhe\n67666 imperial aramaic letter qoph\n67667 imperial aramaic letter resh\n67668 imperial aramaic letter shin\n67669 imperial aramaic letter taw\n67671 imperial aramaic section sign\n67672 imperial aramaic number one\n67673 imperial aramaic number two\n67674 imperial aramaic number three\n67675 imperial aramaic number ten\n67676 imperial aramaic number twenty\n67677 imperial aramaic number one hundred\n67678 imperial aramaic number one thousand\n67679 imperial aramaic number ten thousand\n67680 palmyrene letter aleph\n67681 palmyrene letter beth\n67682 palmyrene letter gimel\n67683 palmyrene letter daleth\n67684 palmyrene letter he\n67685 palmyrene letter waw\n67686 palmyrene letter zayin\n67687 palmyrene letter heth\n67688 palmyrene letter teth\n67689 palmyrene letter yodh\n67690 palmyrene letter kaph\n67691 palmyrene letter lamedh\n67692 palmyrene letter mem\n67693 palmyrene letter final nun\n67694 palmyrene letter nun\n67695 palmyrene letter samekh\n67696 palmyrene letter ayin\n67697 palmyrene letter pe\n67698 palmyrene letter sadhe\n67699 palmyrene letter qoph\n67700 palmyrene letter resh\n67701 palmyrene letter shin\n67702 palmyrene letter taw\n67703 palmyrene left-pointing fleuron\n67704 palmyrene right-pointing fleuron\n67705 palmyrene number one\n67706 palmyrene number two\n67707 palmyrene number three\n67708 palmyrene number four\n67709 palmyrene number five\n67710 palmyrene number ten\n67711 palmyrene number twenty\n67712 nabataean letter final aleph\n67713 nabataean letter aleph\n67714 nabataean letter final beth\n67715 nabataean letter beth\n67716 nabataean letter gimel\n67717 nabataean letter daleth\n67718 nabataean letter final he\n67719 nabataean letter he\n67720 nabataean letter waw\n67721 nabataean letter zayin\n67722 nabataean letter heth\n67723 nabataean letter teth\n67724 nabataean letter final yodh\n67725 nabataean letter yodh\n67726 nabataean letter final kaph\n67727 nabataean letter kaph\n67728 nabataean letter final lamedh\n67729 nabataean letter lamedh\n67730 nabataean letter final mem\n67731 nabataean letter mem\n67732 nabataean letter final nun\n67733 nabataean letter nun\n67734 nabataean letter samekh\n67735 nabataean letter ayin\n67736 nabataean letter pe\n67737 nabataean letter sadhe\n67738 nabataean letter qoph\n67739 nabataean letter resh\n67740 nabataean letter final shin\n67741 nabataean letter shin\n67742 nabataean letter taw\n67751 nabataean number one\n67752 nabataean number two\n67753 nabataean number three\n67754 nabataean number four\n67755 nabataean cruciform number four\n67756 nabataean number five\n67757 nabataean number ten\n67758 nabataean number twenty\n67759 nabataean number one hundred\n67808 hatran letter aleph\n67809 hatran letter beth\n67810 hatran letter gimel\n67811 hatran letter daleth-resh\n67812 hatran letter he\n67813 hatran letter waw\n67814 hatran letter zayn\n67815 hatran letter heth\n67816 hatran letter teth\n67817 hatran letter yodh\n67818 hatran letter kaph\n67819 hatran letter lamedh\n67820 hatran letter mem\n67821 hatran letter nun\n67822 hatran letter samekh\n67823 hatran letter ayn\n67824 hatran letter pe\n67825 hatran letter sadhe\n67826 hatran letter qoph\n67828 hatran letter shin\n67829 hatran letter taw\n67835 hatran number one\n67836 hatran number five\n67837 hatran number ten\n67838 hatran number twenty\n67839 hatran number one hundred\n67840 phoenician letter alf\n67841 phoenician letter bet\n67842 phoenician letter gaml\n67843 phoenician letter delt\n67844 phoenician letter he\n67845 phoenician letter wau\n67846 phoenician letter zai\n67847 phoenician letter het\n67848 phoenician letter tet\n67849 phoenician letter yod\n67850 phoenician letter kaf\n67851 phoenician letter lamd\n67852 phoenician letter mem\n67853 phoenician letter nun\n67854 phoenician letter semk\n67855 phoenician letter ain\n67856 phoenician letter pe\n67857 phoenician letter sade\n67858 phoenician letter qof\n67859 phoenician letter rosh\n67860 phoenician letter shin\n67861 phoenician letter tau\n67862 phoenician number one\n67863 phoenician number ten\n67864 phoenician number twenty\n67865 phoenician number one hundred\n67866 phoenician number two\n67867 phoenician number three\n67871 phoenician word separator\n67872 lydian letter a\n67873 lydian letter b\n67874 lydian letter g\n67875 lydian letter d\n67876 lydian letter e\n67877 lydian letter v\n67878 lydian letter i\n67879 lydian letter y\n67880 lydian letter k\n67881 lydian letter l\n67882 lydian letter m\n67883 lydian letter n\n67884 lydian letter o\n67885 lydian letter r\n67886 lydian letter ss\n67887 lydian letter t\n67888 lydian letter u\n67889 lydian letter f\n67890 lydian letter q\n67891 lydian letter s\n67892 lydian letter tt\n67893 lydian letter an\n67894 lydian letter en\n67895 lydian letter ly\n67896 lydian letter nn\n67897 lydian letter c\n67903 lydian triangular mark\n67904 sidetic letter n01\n67905 sidetic letter n02\n67906 sidetic letter n03\n67907 sidetic letter n04\n67908 sidetic letter n05\n67909 sidetic letter n06\n67910 sidetic letter n07\n67911 sidetic letter n08\n67912 sidetic letter n09\n67913 sidetic letter n10\n67914 sidetic letter n11\n67915 sidetic letter n12\n67916 sidetic letter n13\n67917 sidetic letter n14\n67918 sidetic letter n15\n67919 sidetic letter n16\n67920 sidetic letter n17\n67921 sidetic letter n18\n67922 sidetic letter n19\n67923 sidetic letter n20\n67924 sidetic letter n21\n67925 sidetic letter n22\n67926 sidetic letter n23\n67927 sidetic letter n24\n67928 sidetic letter n25\n67929 sidetic letter n26\n67968 meroitic hieroglyphic letter a\n67969 meroitic hieroglyphic letter e\n67970 meroitic hieroglyphic letter i\n67971 meroitic hieroglyphic letter o\n67972 meroitic hieroglyphic letter ya\n67973 meroitic hieroglyphic letter wa\n67974 meroitic hieroglyphic letter ba\n67975 meroitic hieroglyphic letter ba-2\n67976 meroitic hieroglyphic letter pa\n67977 meroitic hieroglyphic letter ma\n67978 meroitic hieroglyphic letter na\n67979 meroitic hieroglyphic letter na-2\n67980 meroitic hieroglyphic letter ne\n67981 meroitic hieroglyphic letter ne-2\n67982 meroitic hieroglyphic letter ra\n67983 meroitic hieroglyphic letter ra-2\n67984 meroitic hieroglyphic letter la\n67985 meroitic hieroglyphic letter kha\n67986 meroitic hieroglyphic letter hha\n67987 meroitic hieroglyphic letter sa\n67988 meroitic hieroglyphic letter sa-2\n67989 meroitic hieroglyphic letter se\n67990 meroitic hieroglyphic letter ka\n67991 meroitic hieroglyphic letter qa\n67992 meroitic hieroglyphic letter ta\n67993 meroitic hieroglyphic letter ta-2\n67994 meroitic hieroglyphic letter te\n67995 meroitic hieroglyphic letter te-2\n67996 meroitic hieroglyphic letter to\n67997 meroitic hieroglyphic letter da\n67998 meroitic hieroglyphic symbol vidj\n67999 meroitic hieroglyphic symbol vidj-2\n68000 meroitic cursive letter a\n68001 meroitic cursive letter e\n68002 meroitic cursive letter i\n68003 meroitic cursive letter o\n68004 meroitic cursive letter ya\n68005 meroitic cursive letter wa\n68006 meroitic cursive letter ba\n68007 meroitic cursive letter pa\n68008 meroitic cursive letter ma\n68009 meroitic cursive letter na\n68010 meroitic cursive letter ne\n68011 meroitic cursive letter ra\n68012 meroitic cursive letter la\n68013 meroitic cursive letter kha\n68014 meroitic cursive letter hha\n68015 meroitic cursive letter sa\n68016 meroitic cursive letter archaic sa\n68017 meroitic cursive letter se\n68018 meroitic cursive letter ka\n68019 meroitic cursive letter qa\n68020 meroitic cursive letter ta\n68021 meroitic cursive letter te\n68022 meroitic cursive letter to\n68023 meroitic cursive letter da\n68028 meroitic cursive fraction eleven twelfths\n68029 meroitic cursive fraction one half\n68030 meroitic cursive logogram rmt\n68031 meroitic cursive logogram imn\n68032 meroitic cursive number one\n68033 meroitic cursive number two\n68034 meroitic cursive number three\n68035 meroitic cursive number four\n68036 meroitic cursive number five\n68037 meroitic cursive number six\n68038 meroitic cursive number seven\n68039 meroitic cursive number eight\n68040 meroitic cursive number nine\n68041 meroitic cursive number ten\n68042 meroitic cursive number twenty\n68043 meroitic cursive number thirty\n68044 meroitic cursive number forty\n68045 meroitic cursive number fifty\n68046 meroitic cursive number sixty\n68047 meroitic cursive number seventy\n68050 meroitic cursive number one hundred\n68051 meroitic cursive number two hundred\n68052 meroitic cursive number three hundred\n68053 meroitic cursive number four hundred\n68054 meroitic cursive number five hundred\n68055 meroitic cursive number six hundred\n68056 meroitic cursive number seven hundred\n68057 meroitic cursive number eight hundred\n68058 meroitic cursive number nine hundred\n68059 meroitic cursive number one thousand\n68060 meroitic cursive number two thousand\n68061 meroitic cursive number three thousand\n68062 meroitic cursive number four thousand\n68063 meroitic cursive number five thousand\n68064 meroitic cursive number six thousand\n68065 meroitic cursive number seven thousand\n68066 meroitic cursive number eight thousand\n68067 meroitic cursive number nine thousand\n68068 meroitic cursive number ten thousand\n68069 meroitic cursive number twenty thousand\n68070 meroitic cursive number thirty thousand\n68071 meroitic cursive number forty thousand\n68072 meroitic cursive number fifty thousand\n68073 meroitic cursive number sixty thousand\n68074 meroitic cursive number seventy thousand\n68075 meroitic cursive number eighty thousand\n68076 meroitic cursive number ninety thousand\n68077 meroitic cursive number one hundred thousand\n68078 meroitic cursive number two hundred thousand\n68079 meroitic cursive number three hundred thousand\n68080 meroitic cursive number four hundred thousand\n68081 meroitic cursive number five hundred thousand\n68082 meroitic cursive number six hundred thousand\n68083 meroitic cursive number seven hundred thousand\n68084 meroitic cursive number eight hundred thousand\n68085 meroitic cursive number nine hundred thousand\n68086 meroitic cursive fraction one twelfth\n68087 meroitic cursive fraction two twelfths\n68088 meroitic cursive fraction three twelfths\n68089 meroitic cursive fraction four twelfths\n68090 meroitic cursive fraction five twelfths\n68091 meroitic cursive fraction six twelfths\n68092 meroitic cursive fraction seven twelfths\n68093 meroitic cursive fraction eight twelfths\n68094 meroitic cursive fraction nine twelfths\n68095 meroitic cursive fraction ten twelfths\n68096 kharoshthi letter a\n68097 kharoshthi vowel sign i\n68098 kharoshthi vowel sign u\n68099 kharoshthi vowel sign vocalic r\n68101 kharoshthi vowel sign e\n68102 kharoshthi vowel sign o\n68108 kharoshthi vowel length mark\n68109 kharoshthi sign double ring below\n68110 kharoshthi sign anusvara\n68111 kharoshthi sign visarga\n68112 kharoshthi letter ka\n68113 kharoshthi letter kha\n68114 kharoshthi letter ga\n68115 kharoshthi letter gha\n68117 kharoshthi letter ca\n68118 kharoshthi letter cha\n68119 kharoshthi letter ja\n68121 kharoshthi letter nya\n68122 kharoshthi letter tta\n68123 kharoshthi letter ttha\n68124 kharoshthi letter dda\n68125 kharoshthi letter ddha\n68126 kharoshthi letter nna\n68127 kharoshthi letter ta\n68128 kharoshthi letter tha\n68129 kharoshthi letter da\n68130 kharoshthi letter dha\n68131 kharoshthi letter na\n68132 kharoshthi letter pa\n68133 kharoshthi letter pha\n68134 kharoshthi letter ba\n68135 kharoshthi letter bha\n68136 kharoshthi letter ma\n68137 kharoshthi letter ya\n68138 kharoshthi letter ra\n68139 kharoshthi letter la\n68140 kharoshthi letter va\n68141 kharoshthi letter sha\n68142 kharoshthi letter ssa\n68143 kharoshthi letter sa\n68144 kharoshthi letter za\n68145 kharoshthi letter ha\n68146 kharoshthi letter kka\n68147 kharoshthi letter tttha\n68148 kharoshthi letter ttta\n68149 kharoshthi letter vha\n68152 kharoshthi sign bar above\n68153 kharoshthi sign cauda\n68154 kharoshthi sign dot below\n68159 kharoshthi virama\n68160 kharoshthi digit one\n68161 kharoshthi digit two\n68162 kharoshthi digit three\n68163 kharoshthi digit four\n68164 kharoshthi number ten\n68165 kharoshthi number twenty\n68166 kharoshthi number one hundred\n68167 kharoshthi number one thousand\n68168 kharoshthi fraction one half\n68176 kharoshthi punctuation dot\n68177 kharoshthi punctuation small circle\n68178 kharoshthi punctuation circle\n68179 kharoshthi punctuation crescent bar\n68180 kharoshthi punctuation mangalam\n68181 kharoshthi punctuation lotus\n68182 kharoshthi punctuation danda\n68183 kharoshthi punctuation double danda\n68184 kharoshthi punctuation lines\n68192 old south arabian letter he\n68193 old south arabian letter lamedh\n68194 old south arabian letter heth\n68195 old south arabian letter mem\n68196 old south arabian letter qoph\n68197 old south arabian letter waw\n68198 old south arabian letter shin\n68199 old south arabian letter resh\n68200 old south arabian letter beth\n68201 old south arabian letter taw\n68202 old south arabian letter sat\n68203 old south arabian letter kaph\n68204 old south arabian letter nun\n68205 old south arabian letter kheth\n68206 old south arabian letter sadhe\n68207 old south arabian letter samekh\n68208 old south arabian letter fe\n68209 old south arabian letter alef\n68210 old south arabian letter ayn\n68211 old south arabian letter dhadhe\n68212 old south arabian letter gimel\n68213 old south arabian letter daleth\n68214 old south arabian letter ghayn\n68215 old south arabian letter teth\n68216 old south arabian letter zayn\n68217 old south arabian letter dhaleth\n68218 old south arabian letter yodh\n68219 old south arabian letter thaw\n68220 old south arabian letter theth\n68221 old south arabian number one\n68222 old south arabian number fifty\n68223 old south arabian numeric indicator\n68224 old north arabian letter heh\n68225 old north arabian letter lam\n68226 old north arabian letter hah\n68227 old north arabian letter meem\n68228 old north arabian letter qaf\n68229 old north arabian letter waw\n68230 old north arabian letter es-2\n68231 old north arabian letter reh\n68232 old north arabian letter beh\n68233 old north arabian letter teh\n68234 old north arabian letter es-1\n68235 old north arabian letter kaf\n68236 old north arabian letter noon\n68237 old north arabian letter khah\n68238 old north arabian letter sad\n68239 old north arabian letter es-3\n68240 old north arabian letter feh\n68241 old north arabian letter alef\n68242 old north arabian letter ain\n68243 old north arabian letter dad\n68244 old north arabian letter geem\n68245 old north arabian letter dal\n68246 old north arabian letter ghain\n68247 old north arabian letter tah\n68248 old north arabian letter zain\n68249 old north arabian letter thal\n68250 old north arabian letter yeh\n68251 old north arabian letter theh\n68252 old north arabian letter zah\n68253 old north arabian number one\n68254 old north arabian number ten\n68255 old north arabian number twenty\n68288 manichaean letter aleph\n68289 manichaean letter beth\n68290 manichaean letter bheth\n68291 manichaean letter gimel\n68292 manichaean letter ghimel\n68293 manichaean letter daleth\n68294 manichaean letter he\n68295 manichaean letter waw\n68296 manichaean sign ud\n68297 manichaean letter zayin\n68298 manichaean letter zhayin\n68299 manichaean letter jayin\n68300 manichaean letter jhayin\n68301 manichaean letter heth\n68302 manichaean letter teth\n68303 manichaean letter yodh\n68304 manichaean letter kaph\n68305 manichaean letter xaph\n68306 manichaean letter khaph\n68307 manichaean letter lamedh\n68308 manichaean letter dhamedh\n68309 manichaean letter thamedh\n68310 manichaean letter mem\n68311 manichaean letter nun\n68312 manichaean letter samekh\n68313 manichaean letter ayin\n68314 manichaean letter aayin\n68315 manichaean letter pe\n68316 manichaean letter fe\n68317 manichaean letter sadhe\n68318 manichaean letter qoph\n68319 manichaean letter xoph\n68320 manichaean letter qhoph\n68321 manichaean letter resh\n68322 manichaean letter shin\n68323 manichaean letter sshin\n68324 manichaean letter taw\n68325 manichaean abbreviation mark above\n68326 manichaean abbreviation mark below\n68331 manichaean number one\n68332 manichaean number five\n68333 manichaean number ten\n68334 manichaean number twenty\n68335 manichaean number one hundred\n68336 manichaean punctuation star\n68337 manichaean punctuation fleuron\n68338 manichaean punctuation double dot within dot\n68339 manichaean punctuation dot within dot\n68340 manichaean punctuation dot\n68341 manichaean punctuation two dots\n68342 manichaean punctuation line filler\n68352 avestan letter a\n68353 avestan letter aa\n68354 avestan letter ao\n68355 avestan letter aao\n68356 avestan letter an\n68357 avestan letter aan\n68358 avestan letter ae\n68359 avestan letter aee\n68360 avestan letter e\n68361 avestan letter ee\n68362 avestan letter o\n68363 avestan letter oo\n68364 avestan letter i\n68365 avestan letter ii\n68366 avestan letter u\n68367 avestan letter uu\n68368 avestan letter ke\n68369 avestan letter xe\n68370 avestan letter xye\n68371 avestan letter xve\n68372 avestan letter ge\n68373 avestan letter gge\n68374 avestan letter ghe\n68375 avestan letter ce\n68376 avestan letter je\n68377 avestan letter te\n68378 avestan letter the\n68379 avestan letter de\n68380 avestan letter dhe\n68381 avestan letter tte\n68382 avestan letter pe\n68383 avestan letter fe\n68384 avestan letter be\n68385 avestan letter bhe\n68386 avestan letter nge\n68387 avestan letter ngye\n68388 avestan letter ngve\n68389 avestan letter ne\n68390 avestan letter nye\n68391 avestan letter nne\n68392 avestan letter me\n68393 avestan letter hme\n68394 avestan letter yye\n68395 avestan letter ye\n68396 avestan letter ve\n68397 avestan letter re\n68398 avestan letter le\n68399 avestan letter se\n68400 avestan letter ze\n68401 avestan letter she\n68402 avestan letter zhe\n68403 avestan letter shye\n68404 avestan letter sshe\n68405 avestan letter he\n68409 avestan abbreviation mark\n68410 tiny two dots over one dot punctuation\n68411 small two dots over one dot punctuation\n68412 large two dots over one dot punctuation\n68413 large one dot over two dots punctuation\n68414 large two rings over one ring punctuation\n68415 large one ring over two rings punctuation\n68416 inscriptional parthian letter aleph\n68417 inscriptional parthian letter beth\n68418 inscriptional parthian letter gimel\n68419 inscriptional parthian letter daleth\n68420 inscriptional parthian letter he\n68421 inscriptional parthian letter waw\n68422 inscriptional parthian letter zayin\n68423 inscriptional parthian letter heth\n68424 inscriptional parthian letter teth\n68425 inscriptional parthian letter yodh\n68426 inscriptional parthian letter kaph\n68427 inscriptional parthian letter lamedh\n68428 inscriptional parthian letter mem\n68429 inscriptional parthian letter nun\n68430 inscriptional parthian letter samekh\n68431 inscriptional parthian letter ayin\n68432 inscriptional parthian letter pe\n68433 inscriptional parthian letter sadhe\n68434 inscriptional parthian letter qoph\n68435 inscriptional parthian letter resh\n68436 inscriptional parthian letter shin\n68437 inscriptional parthian letter taw\n68440 inscriptional parthian number one\n68441 inscriptional parthian number two\n68442 inscriptional parthian number three\n68443 inscriptional parthian number four\n68444 inscriptional parthian number ten\n68445 inscriptional parthian number twenty\n68446 inscriptional parthian number one hundred\n68447 inscriptional parthian number one thousand\n68448 inscriptional pahlavi letter aleph\n68449 inscriptional pahlavi letter beth\n68450 inscriptional pahlavi letter gimel\n68451 inscriptional pahlavi letter daleth\n68452 inscriptional pahlavi letter he\n68453 inscriptional pahlavi letter waw-ayin-resh\n68454 inscriptional pahlavi letter zayin\n68455 inscriptional pahlavi letter heth\n68456 inscriptional pahlavi letter teth\n68457 inscriptional pahlavi letter yodh\n68458 inscriptional pahlavi letter kaph\n68459 inscriptional pahlavi letter lamedh\n68460 inscriptional pahlavi letter mem-qoph\n68461 inscriptional pahlavi letter nun\n68462 inscriptional pahlavi letter samekh\n68463 inscriptional pahlavi letter pe\n68464 inscriptional pahlavi letter sadhe\n68465 inscriptional pahlavi letter shin\n68466 inscriptional pahlavi letter taw\n68472 inscriptional pahlavi number one\n68473 inscriptional pahlavi number two\n68474 inscriptional pahlavi number three\n68475 inscriptional pahlavi number four\n68476 inscriptional pahlavi number ten\n68477 inscriptional pahlavi number twenty\n68478 inscriptional pahlavi number one hundred\n68479 inscriptional pahlavi number one thousand\n68480 psalter pahlavi letter aleph\n68481 psalter pahlavi letter beth\n68482 psalter pahlavi letter gimel\n68483 psalter pahlavi letter daleth\n68484 psalter pahlavi letter he\n68485 psalter pahlavi letter waw-ayin-resh\n68486 psalter pahlavi letter zayin\n68487 psalter pahlavi letter heth\n68488 psalter pahlavi letter yodh\n68489 psalter pahlavi letter kaph\n68490 psalter pahlavi letter lamedh\n68491 psalter pahlavi letter mem-qoph\n68492 psalter pahlavi letter nun\n68493 psalter pahlavi letter samekh\n68494 psalter pahlavi letter pe\n68495 psalter pahlavi letter sadhe\n68496 psalter pahlavi letter shin\n68497 psalter pahlavi letter taw\n68505 psalter pahlavi section mark\n68506 psalter pahlavi turned section mark\n68507 psalter pahlavi four dots with cross\n68508 psalter pahlavi four dots with dot\n68521 psalter pahlavi number one\n68522 psalter pahlavi number two\n68523 psalter pahlavi number three\n68524 psalter pahlavi number four\n68525 psalter pahlavi number ten\n68526 psalter pahlavi number twenty\n68527 psalter pahlavi number one hundred\n68608 old turkic letter orkhon a\n68609 old turkic letter yenisei a\n68610 old turkic letter yenisei ae\n68611 old turkic letter orkhon i\n68612 old turkic letter yenisei i\n68613 old turkic letter yenisei e\n68614 old turkic letter orkhon o\n68615 old turkic letter orkhon oe\n68616 old turkic letter yenisei oe\n68617 old turkic letter orkhon ab\n68618 old turkic letter yenisei ab\n68619 old turkic letter orkhon aeb\n68620 old turkic letter yenisei aeb\n68621 old turkic letter orkhon ag\n68622 old turkic letter yenisei ag\n68623 old turkic letter orkhon aeg\n68624 old turkic letter yenisei aeg\n68625 old turkic letter orkhon ad\n68626 old turkic letter yenisei ad\n68627 old turkic letter orkhon aed\n68628 old turkic letter orkhon ez\n68629 old turkic letter yenisei ez\n68630 old turkic letter orkhon ay\n68631 old turkic letter yenisei ay\n68632 old turkic letter orkhon aey\n68633 old turkic letter yenisei aey\n68634 old turkic letter orkhon aek\n68635 old turkic letter yenisei aek\n68636 old turkic letter orkhon oek\n68637 old turkic letter yenisei oek\n68638 old turkic letter orkhon al\n68639 old turkic letter yenisei al\n68640 old turkic letter orkhon ael\n68641 old turkic letter orkhon elt\n68642 old turkic letter orkhon em\n68643 old turkic letter orkhon an\n68644 old turkic letter orkhon aen\n68645 old turkic letter yenisei aen\n68646 old turkic letter orkhon ent\n68647 old turkic letter yenisei ent\n68648 old turkic letter orkhon enc\n68649 old turkic letter yenisei enc\n68650 old turkic letter orkhon eny\n68651 old turkic letter yenisei eny\n68652 old turkic letter yenisei ang\n68653 old turkic letter orkhon eng\n68654 old turkic letter yenisei aeng\n68655 old turkic letter orkhon ep\n68656 old turkic letter orkhon op\n68657 old turkic letter orkhon ic\n68658 old turkic letter orkhon ec\n68659 old turkic letter yenisei ec\n68660 old turkic letter orkhon aq\n68661 old turkic letter yenisei aq\n68662 old turkic letter orkhon iq\n68663 old turkic letter yenisei iq\n68664 old turkic letter orkhon oq\n68665 old turkic letter yenisei oq\n68666 old turkic letter orkhon ar\n68667 old turkic letter yenisei ar\n68668 old turkic letter orkhon aer\n68669 old turkic letter orkhon as\n68670 old turkic letter orkhon aes\n68671 old turkic letter orkhon ash\n68672 old turkic letter yenisei ash\n68673 old turkic letter orkhon esh\n68674 old turkic letter yenisei esh\n68675 old turkic letter orkhon at\n68676 old turkic letter yenisei at\n68677 old turkic letter orkhon aet\n68678 old turkic letter yenisei aet\n68679 old turkic letter orkhon ot\n68680 old turkic letter orkhon bash\n68736 old hungarian capital letter a\n68737 old hungarian capital letter aa\n68738 old hungarian capital letter eb\n68739 old hungarian capital letter amb\n68740 old hungarian capital letter ec\n68741 old hungarian capital letter enc\n68742 old hungarian capital letter ecs\n68743 old hungarian capital letter ed\n68744 old hungarian capital letter and\n68745 old hungarian capital letter e\n68746 old hungarian capital letter close e\n68747 old hungarian capital letter ee\n68748 old hungarian capital letter ef\n68749 old hungarian capital letter eg\n68750 old hungarian capital letter egy\n68751 old hungarian capital letter eh\n68752 old hungarian capital letter i\n68753 old hungarian capital letter ii\n68754 old hungarian capital letter ej\n68755 old hungarian capital letter ek\n68756 old hungarian capital letter ak\n68757 old hungarian capital letter unk\n68758 old hungarian capital letter el\n68759 old hungarian capital letter ely\n68760 old hungarian capital letter em\n68761 old hungarian capital letter en\n68762 old hungarian capital letter eny\n68763 old hungarian capital letter o\n68764 old hungarian capital letter oo\n68765 old hungarian capital letter nikolsburg oe\n68766 old hungarian capital letter rudimenta oe\n68767 old hungarian capital letter oee\n68768 old hungarian capital letter ep\n68769 old hungarian capital letter emp\n68770 old hungarian capital letter er\n68771 old hungarian capital letter short er\n68772 old hungarian capital letter es\n68773 old hungarian capital letter esz\n68774 old hungarian capital letter et\n68775 old hungarian capital letter ent\n68776 old hungarian capital letter ety\n68777 old hungarian capital letter ech\n68778 old hungarian capital letter u\n68779 old hungarian capital letter uu\n68780 old hungarian capital letter nikolsburg ue\n68781 old hungarian capital letter rudimenta ue\n68782 old hungarian capital letter ev\n68783 old hungarian capital letter ez\n68784 old hungarian capital letter ezs\n68785 old hungarian capital letter ent-shaped sign\n68786 old hungarian capital letter us\n68800 old hungarian small letter a\n68801 old hungarian small letter aa\n68802 old hungarian small letter eb\n68803 old hungarian small letter amb\n68804 old hungarian small letter ec\n68805 old hungarian small letter enc\n68806 old hungarian small letter ecs\n68807 old hungarian small letter ed\n68808 old hungarian small letter and\n68809 old hungarian small letter e\n68810 old hungarian small letter close e\n68811 old hungarian small letter ee\n68812 old hungarian small letter ef\n68813 old hungarian small letter eg\n68814 old hungarian small letter egy\n68815 old hungarian small letter eh\n68816 old hungarian small letter i\n68817 old hungarian small letter ii\n68818 old hungarian small letter ej\n68819 old hungarian small letter ek\n68820 old hungarian small letter ak\n68821 old hungarian small letter unk\n68822 old hungarian small letter el\n68823 old hungarian small letter ely\n68824 old hungarian small letter em\n68825 old hungarian small letter en\n68826 old hungarian small letter eny\n68827 old hungarian small letter o\n68828 old hungarian small letter oo\n68829 old hungarian small letter nikolsburg oe\n68830 old hungarian small letter rudimenta oe\n68831 old hungarian small letter oee\n68832 old hungarian small letter ep\n68833 old hungarian small letter emp\n68834 old hungarian small letter er\n68835 old hungarian small letter short er\n68836 old hungarian small letter es\n68837 old hungarian small letter esz\n68838 old hungarian small letter et\n68839 old hungarian small letter ent\n68840 old hungarian small letter ety\n68841 old hungarian small letter ech\n68842 old hungarian small letter u\n68843 old hungarian small letter uu\n68844 old hungarian small letter nikolsburg ue\n68845 old hungarian small letter rudimenta ue\n68846 old hungarian small letter ev\n68847 old hungarian small letter ez\n68848 old hungarian small letter ezs\n68849 old hungarian small letter ent-shaped sign\n68850 old hungarian small letter us\n68858 old hungarian number one\n68859 old hungarian number five\n68860 old hungarian number ten\n68861 old hungarian number fifty\n68862 old hungarian number one hundred\n68863 old hungarian number one thousand\n68864 hanifi rohingya letter a\n68865 hanifi rohingya letter ba\n68866 hanifi rohingya letter pa\n68867 hanifi rohingya letter ta\n68868 hanifi rohingya letter tta\n68869 hanifi rohingya letter ja\n68870 hanifi rohingya letter ca\n68871 hanifi rohingya letter ha\n68872 hanifi rohingya letter kha\n68873 hanifi rohingya letter fa\n68874 hanifi rohingya letter da\n68875 hanifi rohingya letter dda\n68876 hanifi rohingya letter ra\n68877 hanifi rohingya letter rra\n68878 hanifi rohingya letter za\n68879 hanifi rohingya letter sa\n68880 hanifi rohingya letter sha\n68881 hanifi rohingya letter ka\n68882 hanifi rohingya letter ga\n68883 hanifi rohingya letter la\n68884 hanifi rohingya letter ma\n68885 hanifi rohingya letter na\n68886 hanifi rohingya letter wa\n68887 hanifi rohingya letter kinna wa\n68888 hanifi rohingya letter ya\n68889 hanifi rohingya letter kinna ya\n68890 hanifi rohingya letter nga\n68891 hanifi rohingya letter nya\n68892 hanifi rohingya letter va\n68893 hanifi rohingya vowel a\n68894 hanifi rohingya vowel i\n68895 hanifi rohingya vowel u\n68896 hanifi rohingya vowel e\n68897 hanifi rohingya vowel o\n68898 hanifi rohingya mark sakin\n68899 hanifi rohingya mark na khonna\n68900 hanifi rohingya sign harbahay\n68901 hanifi rohingya sign tahala\n68902 hanifi rohingya sign tana\n68903 hanifi rohingya sign tassi\n68912 hanifi rohingya digit zero\n68913 hanifi rohingya digit one\n68914 hanifi rohingya digit two\n68915 hanifi rohingya digit three\n68916 hanifi rohingya digit four\n68917 hanifi rohingya digit five\n68918 hanifi rohingya digit six\n68919 hanifi rohingya digit seven\n68920 hanifi rohingya digit eight\n68921 hanifi rohingya digit nine\n68928 garay digit zero\n68929 garay digit one\n68930 garay digit two\n68931 garay digit three\n68932 garay digit four\n68933 garay digit five\n68934 garay digit six\n68935 garay digit seven\n68936 garay digit eight\n68937 garay digit nine\n68938 garay vowel sign a\n68939 garay vowel sign i\n68940 garay vowel sign o\n68941 garay vowel sign ee\n68942 garay vowel length mark\n68943 garay sukun\n68944 garay capital letter a\n68945 garay capital letter ca\n68946 garay capital letter ma\n68947 garay capital letter ka\n68948 garay capital letter ba\n68949 garay capital letter ja\n68950 garay capital letter sa\n68951 garay capital letter wa\n68952 garay capital letter la\n68953 garay capital letter ga\n68954 garay capital letter da\n68955 garay capital letter xa\n68956 garay capital letter ya\n68957 garay capital letter ta\n68958 garay capital letter ra\n68959 garay capital letter nya\n68960 garay capital letter fa\n68961 garay capital letter na\n68962 garay capital letter pa\n68963 garay capital letter ha\n68964 garay capital letter old ka\n68965 garay capital letter old na\n68969 garay vowel sign e\n68970 garay consonant gemination mark\n68971 garay combining dot above\n68972 garay combining double dot above\n68973 garay consonant nasalization mark\n68974 garay hyphen\n68975 garay reduplication mark\n68976 garay small letter a\n68977 garay small letter ca\n68978 garay small letter ma\n68979 garay small letter ka\n68980 garay small letter ba\n68981 garay small letter ja\n68982 garay small letter sa\n68983 garay small letter wa\n68984 garay small letter la\n68985 garay small letter ga\n68986 garay small letter da\n68987 garay small letter xa\n68988 garay small letter ya\n68989 garay small letter ta\n68990 garay small letter ra\n68991 garay small letter nya\n68992 garay small letter fa\n68993 garay small letter na\n68994 garay small letter pa\n68995 garay small letter ha\n68996 garay small letter old ka\n68997 garay small letter old na\n69006 garay plus sign\n69007 garay minus sign\n69216 rumi digit one\n69217 rumi digit two\n69218 rumi digit three\n69219 rumi digit four\n69220 rumi digit five\n69221 rumi digit six\n69222 rumi digit seven\n69223 rumi digit eight\n69224 rumi digit nine\n69225 rumi number ten\n69226 rumi number twenty\n69227 rumi number thirty\n69228 rumi number forty\n69229 rumi number fifty\n69230 rumi number sixty\n69231 rumi number seventy\n69232 rumi number eighty\n69233 rumi number ninety\n69234 rumi number one hundred\n69235 rumi number two hundred\n69236 rumi number three hundred\n69237 rumi number four hundred\n69238 rumi number five hundred\n69239 rumi number six hundred\n69240 rumi number seven hundred\n69241 rumi number eight hundred\n69242 rumi number nine hundred\n69243 rumi fraction one half\n69244 rumi fraction one quarter\n69245 rumi fraction one third\n69246 rumi fraction two thirds\n69248 yezidi letter elif\n69249 yezidi letter be\n69250 yezidi letter pe\n69251 yezidi letter phe\n69252 yezidi letter the\n69253 yezidi letter se\n69254 yezidi letter cim\n69255 yezidi letter chim\n69256 yezidi letter chhim\n69257 yezidi letter hha\n69258 yezidi letter xa\n69259 yezidi letter dal\n69260 yezidi letter zal\n69261 yezidi letter ra\n69262 yezidi letter rha\n69263 yezidi letter za\n69264 yezidi letter ja\n69265 yezidi letter sin\n69266 yezidi letter shin\n69267 yezidi letter sad\n69268 yezidi letter dad\n69269 yezidi letter ta\n69270 yezidi letter ze\n69271 yezidi letter eyn\n69272 yezidi letter xheyn\n69273 yezidi letter fa\n69274 yezidi letter va\n69275 yezidi letter va alternate form\n69276 yezidi letter qaf\n69277 yezidi letter kaf\n69278 yezidi letter khaf\n69279 yezidi letter gaf\n69280 yezidi letter lam\n69281 yezidi letter mim\n69282 yezidi letter nun\n69283 yezidi letter um\n69284 yezidi letter waw\n69285 yezidi letter ow\n69286 yezidi letter ew\n69287 yezidi letter hay\n69288 yezidi letter yot\n69289 yezidi letter et\n69291 yezidi combining hamza mark\n69292 yezidi combining madda mark\n69293 yezidi hyphenation mark\n69296 yezidi letter lam with dot above\n69297 yezidi letter yot with circumflex above\n69314 arabic letter dal with two dots vertically below\n69315 arabic letter tah with two dots vertically below\n69316 arabic letter kaf with two dots vertically below\n69317 arabic small yeh barree with two dots below\n69318 arabic letter thin noon\n69319 arabic letter yeh with four dots below\n69328 arabic biblical end of verse\n69329 arabic ligature alayhaa as-salaatu was-salaam\n69330 arabic ligature alayhim as-salaatu was-salaam\n69331 arabic ligature alayhimaa as-salaatu was-salaam\n69332 arabic ligature qaddasa allaahu sirrah\n69333 arabic ligature quddisa sirruhum\n69334 arabic ligature quddisa sirruhumaa\n69335 arabic ligature quddisat asraaruhum\n69336 arabic ligature nawwara allaahu marqadah\n69370 arabic double vertical bar below\n69371 arabic small low noon\n69372 arabic combining alef overlay\n69373 arabic small low word sakta\n69374 arabic small low word qasr\n69375 arabic small low word madda\n69376 old sogdian letter aleph\n69377 old sogdian letter final aleph\n69378 old sogdian letter beth\n69379 old sogdian letter final beth\n69380 old sogdian letter gimel\n69381 old sogdian letter he\n69382 old sogdian letter final he\n69383 old sogdian letter waw\n69384 old sogdian letter zayin\n69385 old sogdian letter heth\n69386 old sogdian letter yodh\n69387 old sogdian letter kaph\n69388 old sogdian letter lamedh\n69389 old sogdian letter mem\n69390 old sogdian letter nun\n69391 old sogdian letter final nun\n69392 old sogdian letter final nun with vertical tail\n69393 old sogdian letter samekh\n69394 old sogdian letter ayin\n69395 old sogdian letter alternate ayin\n69396 old sogdian letter pe\n69397 old sogdian letter sadhe\n69398 old sogdian letter final sadhe\n69399 old sogdian letter final sadhe with vertical tail\n69400 old sogdian letter resh-ayin-daleth\n69401 old sogdian letter shin\n69402 old sogdian letter taw\n69403 old sogdian letter final taw\n69404 old sogdian letter final taw with vertical tail\n69405 old sogdian number one\n69406 old sogdian number two\n69407 old sogdian number three\n69408 old sogdian number four\n69409 old sogdian number five\n69410 old sogdian number ten\n69411 old sogdian number twenty\n69412 old sogdian number thirty\n69413 old sogdian number one hundred\n69414 old sogdian fraction one half\n69415 old sogdian ligature ayin-daleth\n69424 sogdian letter aleph\n69425 sogdian letter beth\n69426 sogdian letter gimel\n69427 sogdian letter he\n69428 sogdian letter waw\n69429 sogdian letter zayin\n69430 sogdian letter heth\n69431 sogdian letter yodh\n69432 sogdian letter kaph\n69433 sogdian letter lamedh\n69434 sogdian letter mem\n69435 sogdian letter nun\n69436 sogdian letter samekh\n69437 sogdian letter ayin\n69438 sogdian letter pe\n69439 sogdian letter sadhe\n69440 sogdian letter resh-ayin\n69441 sogdian letter shin\n69442 sogdian letter taw\n69443 sogdian letter feth\n69444 sogdian letter lesh\n69445 sogdian independent shin\n69446 sogdian combining dot below\n69447 sogdian combining two dots below\n69448 sogdian combining dot above\n69449 sogdian combining two dots above\n69450 sogdian combining curve above\n69451 sogdian combining curve below\n69452 sogdian combining hook above\n69453 sogdian combining hook below\n69454 sogdian combining long hook below\n69455 sogdian combining resh below\n69456 sogdian combining stroke below\n69457 sogdian number one\n69458 sogdian number ten\n69459 sogdian number twenty\n69460 sogdian number one hundred\n69461 sogdian punctuation two vertical bars\n69462 sogdian punctuation two vertical bars with dots\n69463 sogdian punctuation circle with dot\n69464 sogdian punctuation two circles with dots\n69465 sogdian punctuation half circle with dot\n69488 old uyghur letter aleph\n69489 old uyghur letter beth\n69490 old uyghur letter gimel-heth\n69491 old uyghur letter waw\n69492 old uyghur letter zayin\n69493 old uyghur letter final heth\n69494 old uyghur letter yodh\n69495 old uyghur letter kaph\n69496 old uyghur letter lamedh\n69497 old uyghur letter mem\n69498 old uyghur letter nun\n69499 old uyghur letter samekh\n69500 old uyghur letter pe\n69501 old uyghur letter sadhe\n69502 old uyghur letter resh\n69503 old uyghur letter shin\n69504 old uyghur letter taw\n69505 old uyghur letter lesh\n69506 old uyghur combining dot above\n69507 old uyghur combining dot below\n69508 old uyghur combining two dots above\n69509 old uyghur combining two dots below\n69510 old uyghur punctuation bar\n69511 old uyghur punctuation two bars\n69512 old uyghur punctuation two dots\n69513 old uyghur punctuation four dots\n69552 chorasmian letter aleph\n69553 chorasmian letter small aleph\n69554 chorasmian letter beth\n69555 chorasmian letter gimel\n69556 chorasmian letter daleth\n69557 chorasmian letter he\n69558 chorasmian letter waw\n69559 chorasmian letter curled waw\n69560 chorasmian letter zayin\n69561 chorasmian letter heth\n69562 chorasmian letter yodh\n69563 chorasmian letter kaph\n69564 chorasmian letter lamedh\n69565 chorasmian letter mem\n69566 chorasmian letter nun\n69567 chorasmian letter samekh\n69568 chorasmian letter ayin\n69569 chorasmian letter pe\n69570 chorasmian letter resh\n69571 chorasmian letter shin\n69572 chorasmian letter taw\n69573 chorasmian number one\n69574 chorasmian number two\n69575 chorasmian number three\n69576 chorasmian number four\n69577 chorasmian number ten\n69578 chorasmian number twenty\n69579 chorasmian number one hundred\n69600 elymaic letter aleph\n69601 elymaic letter beth\n69602 elymaic letter gimel\n69603 elymaic letter daleth\n69604 elymaic letter he\n69605 elymaic letter waw\n69606 elymaic letter zayin\n69607 elymaic letter heth\n69608 elymaic letter teth\n69609 elymaic letter yodh\n69610 elymaic letter kaph\n69611 elymaic letter lamedh\n69612 elymaic letter mem\n69613 elymaic letter nun\n69614 elymaic letter samekh\n69615 elymaic letter ayin\n69616 elymaic letter pe\n69617 elymaic letter sadhe\n69618 elymaic letter qoph\n69619 elymaic letter resh\n69620 elymaic letter shin\n69621 elymaic letter taw\n69622 elymaic ligature zayin-yodh\n69632 brahmi sign candrabindu\n69633 brahmi sign anusvara\n69634 brahmi sign visarga\n69635 brahmi sign jihvamuliya\n69636 brahmi sign upadhmaniya\n69637 brahmi letter a\n69638 brahmi letter aa\n69639 brahmi letter i\n69640 brahmi letter ii\n69641 brahmi letter u\n69642 brahmi letter uu\n69643 brahmi letter vocalic r\n69644 brahmi letter vocalic rr\n69645 brahmi letter vocalic l\n69646 brahmi letter vocalic ll\n69647 brahmi letter e\n69648 brahmi letter ai\n69649 brahmi letter o\n69650 brahmi letter au\n69651 brahmi letter ka\n69652 brahmi letter kha\n69653 brahmi letter ga\n69654 brahmi letter gha\n69655 brahmi letter nga\n69656 brahmi letter ca\n69657 brahmi letter cha\n69658 brahmi letter ja\n69659 brahmi letter jha\n69660 brahmi letter nya\n69661 brahmi letter tta\n69662 brahmi letter ttha\n69663 brahmi letter dda\n69664 brahmi letter ddha\n69665 brahmi letter nna\n69666 brahmi letter ta\n69667 brahmi letter tha\n69668 brahmi letter da\n69669 brahmi letter dha\n69670 brahmi letter na\n69671 brahmi letter pa\n69672 brahmi letter pha\n69673 brahmi letter ba\n69674 brahmi letter bha\n69675 brahmi letter ma\n69676 brahmi letter ya\n69677 brahmi letter ra\n69678 brahmi letter la\n69679 brahmi letter va\n69680 brahmi letter sha\n69681 brahmi letter ssa\n69682 brahmi letter sa\n69683 brahmi letter ha\n69684 brahmi letter lla\n69685 brahmi letter old tamil llla\n69686 brahmi letter old tamil rra\n69687 brahmi letter old tamil nnna\n69688 brahmi vowel sign aa\n69689 brahmi vowel sign bhattiprolu aa\n69690 brahmi vowel sign i\n69691 brahmi vowel sign ii\n69692 brahmi vowel sign u\n69693 brahmi vowel sign uu\n69694 brahmi vowel sign vocalic r\n69695 brahmi vowel sign vocalic rr\n69696 brahmi vowel sign vocalic l\n69697 brahmi vowel sign vocalic ll\n69698 brahmi vowel sign e\n69699 brahmi vowel sign ai\n69700 brahmi vowel sign o\n69701 brahmi vowel sign au\n69702 brahmi virama\n69703 brahmi danda\n69704 brahmi double danda\n69705 brahmi punctuation dot\n69706 brahmi punctuation double dot\n69707 brahmi punctuation line\n69708 brahmi punctuation crescent bar\n69709 brahmi punctuation lotus\n69714 brahmi number one\n69715 brahmi number two\n69716 brahmi number three\n69717 brahmi number four\n69718 brahmi number five\n69719 brahmi number six\n69720 brahmi number seven\n69721 brahmi number eight\n69722 brahmi number nine\n69723 brahmi number ten\n69724 brahmi number twenty\n69725 brahmi number thirty\n69726 brahmi number forty\n69727 brahmi number fifty\n69728 brahmi number sixty\n69729 brahmi number seventy\n69730 brahmi number eighty\n69731 brahmi number ninety\n69732 brahmi number one hundred\n69733 brahmi number one thousand\n69734 brahmi digit zero\n69735 brahmi digit one\n69736 brahmi digit two\n69737 brahmi digit three\n69738 brahmi digit four\n69739 brahmi digit five\n69740 brahmi digit six\n69741 brahmi digit seven\n69742 brahmi digit eight\n69743 brahmi digit nine\n69744 brahmi sign old tamil virama\n69745 brahmi letter old tamil short e\n69746 brahmi letter old tamil short o\n69747 brahmi vowel sign old tamil short e\n69748 brahmi vowel sign old tamil short o\n69749 brahmi letter old tamil lla\n69759 brahmi number joiner\n69760 kaithi sign candrabindu\n69761 kaithi sign anusvara\n69762 kaithi sign visarga\n69763 kaithi letter a\n69764 kaithi letter aa\n69765 kaithi letter i\n69766 kaithi letter ii\n69767 kaithi letter u\n69768 kaithi letter uu\n69769 kaithi letter e\n69770 kaithi letter ai\n69771 kaithi letter o\n69772 kaithi letter au\n69773 kaithi letter ka\n69774 kaithi letter kha\n69775 kaithi letter ga\n69776 kaithi letter gha\n69777 kaithi letter nga\n69778 kaithi letter ca\n69779 kaithi letter cha\n69780 kaithi letter ja\n69781 kaithi letter jha\n69782 kaithi letter nya\n69783 kaithi letter tta\n69784 kaithi letter ttha\n69785 kaithi letter dda\n69786 kaithi letter dddha\n69787 kaithi letter ddha\n69788 kaithi letter rha\n69789 kaithi letter nna\n69790 kaithi letter ta\n69791 kaithi letter tha\n69792 kaithi letter da\n69793 kaithi letter dha\n69794 kaithi letter na\n69795 kaithi letter pa\n69796 kaithi letter pha\n69797 kaithi letter ba\n69798 kaithi letter bha\n69799 kaithi letter ma\n69800 kaithi letter ya\n69801 kaithi letter ra\n69802 kaithi letter la\n69803 kaithi letter va\n69804 kaithi letter sha\n69805 kaithi letter ssa\n69806 kaithi letter sa\n69807 kaithi letter ha\n69808 kaithi vowel sign aa\n69809 kaithi vowel sign i\n69810 kaithi vowel sign ii\n69811 kaithi vowel sign u\n69812 kaithi vowel sign uu\n69813 kaithi vowel sign e\n69814 kaithi vowel sign ai\n69815 kaithi vowel sign o\n69816 kaithi vowel sign au\n69817 kaithi sign virama\n69818 kaithi sign nukta\n69819 kaithi abbreviation sign\n69820 kaithi enumeration sign\n69821 kaithi number sign\n69822 kaithi section mark\n69823 kaithi double section mark\n69824 kaithi danda\n69825 kaithi double danda\n69826 kaithi vowel sign vocalic r\n69837 kaithi number sign above\n69840 sora sompeng letter sah\n69841 sora sompeng letter tah\n69842 sora sompeng letter bah\n69843 sora sompeng letter cah\n69844 sora sompeng letter dah\n69845 sora sompeng letter gah\n69846 sora sompeng letter mah\n69847 sora sompeng letter ngah\n69848 sora sompeng letter lah\n69849 sora sompeng letter nah\n69850 sora sompeng letter vah\n69851 sora sompeng letter pah\n69852 sora sompeng letter yah\n69853 sora sompeng letter rah\n69854 sora sompeng letter hah\n69855 sora sompeng letter kah\n69856 sora sompeng letter jah\n69857 sora sompeng letter nyah\n69858 sora sompeng letter ah\n69859 sora sompeng letter eeh\n69860 sora sompeng letter ih\n69861 sora sompeng letter uh\n69862 sora sompeng letter oh\n69863 sora sompeng letter eh\n69864 sora sompeng letter mae\n69872 sora sompeng digit zero\n69873 sora sompeng digit one\n69874 sora sompeng digit two\n69875 sora sompeng digit three\n69876 sora sompeng digit four\n69877 sora sompeng digit five\n69878 sora sompeng digit six\n69879 sora sompeng digit seven\n69880 sora sompeng digit eight\n69881 sora sompeng digit nine\n69888 chakma sign candrabindu\n69889 chakma sign anusvara\n69890 chakma sign visarga\n69891 chakma letter aa\n69892 chakma letter i\n69893 chakma letter u\n69894 chakma letter e\n69895 chakma letter kaa\n69896 chakma letter khaa\n69897 chakma letter gaa\n69898 chakma letter ghaa\n69899 chakma letter ngaa\n69900 chakma letter caa\n69901 chakma letter chaa\n69902 chakma letter jaa\n69903 chakma letter jhaa\n69904 chakma letter nyaa\n69905 chakma letter ttaa\n69906 chakma letter tthaa\n69907 chakma letter ddaa\n69908 chakma letter ddhaa\n69909 chakma letter nnaa\n69910 chakma letter taa\n69911 chakma letter thaa\n69912 chakma letter daa\n69913 chakma letter dhaa\n69914 chakma letter naa\n69915 chakma letter paa\n69916 chakma letter phaa\n69917 chakma letter baa\n69918 chakma letter bhaa\n69919 chakma letter maa\n69920 chakma letter yyaa\n69921 chakma letter yaa\n69922 chakma letter raa\n69923 chakma letter laa\n69924 chakma letter waa\n69925 chakma letter saa\n69926 chakma letter haa\n69927 chakma vowel sign a\n69928 chakma vowel sign i\n69929 chakma vowel sign ii\n69930 chakma vowel sign u\n69931 chakma vowel sign uu\n69932 chakma vowel sign e\n69933 chakma vowel sign ai\n69934 chakma vowel sign o\n69935 chakma vowel sign au\n69936 chakma vowel sign oi\n69937 chakma o mark\n69938 chakma au mark\n69939 chakma virama\n69940 chakma maayyaa\n69942 chakma digit zero\n69943 chakma digit one\n69944 chakma digit two\n69945 chakma digit three\n69946 chakma digit four\n69947 chakma digit five\n69948 chakma digit six\n69949 chakma digit seven\n69950 chakma digit eight\n69951 chakma digit nine\n69952 chakma section mark\n69953 chakma danda\n69954 chakma double danda\n69955 chakma question mark\n69956 chakma letter lhaa\n69957 chakma vowel sign aa\n69958 chakma vowel sign ei\n69959 chakma letter vaa\n69968 mahajani letter a\n69969 mahajani letter i\n69970 mahajani letter u\n69971 mahajani letter e\n69972 mahajani letter o\n69973 mahajani letter ka\n69974 mahajani letter kha\n69975 mahajani letter ga\n69976 mahajani letter gha\n69977 mahajani letter ca\n69978 mahajani letter cha\n69979 mahajani letter ja\n69980 mahajani letter jha\n69981 mahajani letter nya\n69982 mahajani letter tta\n69983 mahajani letter ttha\n69984 mahajani letter dda\n69985 mahajani letter ddha\n69986 mahajani letter nna\n69987 mahajani letter ta\n69988 mahajani letter tha\n69989 mahajani letter da\n69990 mahajani letter dha\n69991 mahajani letter na\n69992 mahajani letter pa\n69993 mahajani letter pha\n69994 mahajani letter ba\n69995 mahajani letter bha\n69996 mahajani letter ma\n69997 mahajani letter ra\n69998 mahajani letter la\n69999 mahajani letter va\n70000 mahajani letter sa\n70001 mahajani letter ha\n70002 mahajani letter rra\n70003 mahajani sign nukta\n70004 mahajani abbreviation sign\n70005 mahajani section mark\n70006 mahajani ligature shri\n70016 sharada sign candrabindu\n70017 sharada sign anusvara\n70018 sharada sign visarga\n70019 sharada letter a\n70020 sharada letter aa\n70021 sharada letter i\n70022 sharada letter ii\n70023 sharada letter u\n70024 sharada letter uu\n70025 sharada letter vocalic r\n70026 sharada letter vocalic rr\n70027 sharada letter vocalic l\n70028 sharada letter vocalic ll\n70029 sharada letter e\n70030 sharada letter ai\n70031 sharada letter o\n70032 sharada letter au\n70033 sharada letter ka\n70034 sharada letter kha\n70035 sharada letter ga\n70036 sharada letter gha\n70037 sharada letter nga\n70038 sharada letter ca\n70039 sharada letter cha\n70040 sharada letter ja\n70041 sharada letter jha\n70042 sharada letter nya\n70043 sharada letter tta\n70044 sharada letter ttha\n70045 sharada letter dda\n70046 sharada letter ddha\n70047 sharada letter nna\n70048 sharada letter ta\n70049 sharada letter tha\n70050 sharada letter da\n70051 sharada letter dha\n70052 sharada letter na\n70053 sharada letter pa\n70054 sharada letter pha\n70055 sharada letter ba\n70056 sharada letter bha\n70057 sharada letter ma\n70058 sharada letter ya\n70059 sharada letter ra\n70060 sharada letter la\n70061 sharada letter lla\n70062 sharada letter va\n70063 sharada letter sha\n70064 sharada letter ssa\n70065 sharada letter sa\n70066 sharada letter ha\n70067 sharada vowel sign aa\n70068 sharada vowel sign i\n70069 sharada vowel sign ii\n70070 sharada vowel sign u\n70071 sharada vowel sign uu\n70072 sharada vowel sign vocalic r\n70073 sharada vowel sign vocalic rr\n70074 sharada vowel sign vocalic l\n70075 sharada vowel sign vocalic ll\n70076 sharada vowel sign e\n70077 sharada vowel sign ai\n70078 sharada vowel sign o\n70079 sharada vowel sign au\n70080 sharada sign virama\n70081 sharada sign avagraha\n70082 sharada sign jihvamuliya\n70083 sharada sign upadhmaniya\n70084 sharada om\n70085 sharada danda\n70086 sharada double danda\n70087 sharada abbreviation sign\n70088 sharada separator\n70089 sharada sandhi mark\n70090 sharada sign nukta\n70091 sharada vowel modifier mark\n70092 sharada extra short vowel mark\n70093 sharada sutra mark\n70094 sharada vowel sign prishthamatra e\n70095 sharada sign inverted candrabindu\n70096 sharada digit zero\n70097 sharada digit one\n70098 sharada digit two\n70099 sharada digit three\n70100 sharada digit four\n70101 sharada digit five\n70102 sharada digit six\n70103 sharada digit seven\n70104 sharada digit eight\n70105 sharada digit nine\n70106 sharada ekam\n70107 sharada sign siddham\n70108 sharada headstroke\n70109 sharada continuation sign\n70110 sharada section mark-1\n70111 sharada section mark-2\n70113 sinhala archaic digit one\n70114 sinhala archaic digit two\n70115 sinhala archaic digit three\n70116 sinhala archaic digit four\n70117 sinhala archaic digit five\n70118 sinhala archaic digit six\n70119 sinhala archaic digit seven\n70120 sinhala archaic digit eight\n70121 sinhala archaic digit nine\n70122 sinhala archaic number ten\n70123 sinhala archaic number twenty\n70124 sinhala archaic number thirty\n70125 sinhala archaic number forty\n70126 sinhala archaic number fifty\n70127 sinhala archaic number sixty\n70128 sinhala archaic number seventy\n70129 sinhala archaic number eighty\n70130 sinhala archaic number ninety\n70131 sinhala archaic number one hundred\n70132 sinhala archaic number one thousand\n70144 khojki letter a\n70145 khojki letter aa\n70146 khojki letter i\n70147 khojki letter u\n70148 khojki letter e\n70149 khojki letter ai\n70150 khojki letter o\n70151 khojki letter au\n70152 khojki letter ka\n70153 khojki letter kha\n70154 khojki letter ga\n70155 khojki letter gga\n70156 khojki letter gha\n70157 khojki letter nga\n70158 khojki letter ca\n70159 khojki letter cha\n70160 khojki letter ja\n70161 khojki letter jja\n70163 khojki letter nya\n70164 khojki letter tta\n70165 khojki letter ttha\n70166 khojki letter dda\n70167 khojki letter ddha\n70168 khojki letter nna\n70169 khojki letter ta\n70170 khojki letter tha\n70171 khojki letter da\n70172 khojki letter ddda\n70173 khojki letter dha\n70174 khojki letter na\n70175 khojki letter pa\n70176 khojki letter pha\n70177 khojki letter ba\n70178 khojki letter bba\n70179 khojki letter bha\n70180 khojki letter ma\n70181 khojki letter ya\n70182 khojki letter ra\n70183 khojki letter la\n70184 khojki letter va\n70185 khojki letter sa\n70186 khojki letter ha\n70187 khojki letter lla\n70188 khojki vowel sign aa\n70189 khojki vowel sign i\n70190 khojki vowel sign ii\n70191 khojki vowel sign u\n70192 khojki vowel sign e\n70193 khojki vowel sign ai\n70194 khojki vowel sign o\n70195 khojki vowel sign au\n70196 khojki sign anusvara\n70197 khojki sign virama\n70198 khojki sign nukta\n70199 khojki sign shadda\n70200 khojki danda\n70201 khojki double danda\n70202 khojki word separator\n70203 khojki section mark\n70204 khojki double section mark\n70205 khojki abbreviation sign\n70206 khojki sign sukun\n70207 khojki letter qa\n70208 khojki letter short i\n70209 khojki vowel sign vocalic r\n70272 multani letter a\n70273 multani letter i\n70274 multani letter u\n70275 multani letter e\n70276 multani letter ka\n70277 multani letter kha\n70278 multani letter ga\n70280 multani letter gha\n70282 multani letter ca\n70283 multani letter cha\n70284 multani letter ja\n70285 multani letter jja\n70287 multani letter nya\n70288 multani letter tta\n70289 multani letter ttha\n70290 multani letter dda\n70291 multani letter ddda\n70292 multani letter ddha\n70293 multani letter nna\n70294 multani letter ta\n70295 multani letter tha\n70296 multani letter da\n70297 multani letter dha\n70298 multani letter na\n70299 multani letter pa\n70300 multani letter pha\n70301 multani letter ba\n70303 multani letter bha\n70304 multani letter ma\n70305 multani letter ya\n70306 multani letter ra\n70307 multani letter la\n70308 multani letter va\n70309 multani letter sa\n70310 multani letter ha\n70311 multani letter rra\n70312 multani letter rha\n70313 multani section mark\n70320 khudawadi letter a\n70321 khudawadi letter aa\n70322 khudawadi letter i\n70323 khudawadi letter ii\n70324 khudawadi letter u\n70325 khudawadi letter uu\n70326 khudawadi letter e\n70327 khudawadi letter ai\n70328 khudawadi letter o\n70329 khudawadi letter au\n70330 khudawadi letter ka\n70331 khudawadi letter kha\n70332 khudawadi letter ga\n70333 khudawadi letter gga\n70334 khudawadi letter gha\n70335 khudawadi letter nga\n70336 khudawadi letter ca\n70337 khudawadi letter cha\n70338 khudawadi letter ja\n70339 khudawadi letter jja\n70340 khudawadi letter jha\n70341 khudawadi letter nya\n70342 khudawadi letter tta\n70343 khudawadi letter ttha\n70344 khudawadi letter dda\n70345 khudawadi letter ddda\n70346 khudawadi letter rra\n70347 khudawadi letter ddha\n70348 khudawadi letter nna\n70349 khudawadi letter ta\n70350 khudawadi letter tha\n70351 khudawadi letter da\n70352 khudawadi letter dha\n70353 khudawadi letter na\n70354 khudawadi letter pa\n70355 khudawadi letter pha\n70356 khudawadi letter ba\n70357 khudawadi letter bba\n70358 khudawadi letter bha\n70359 khudawadi letter ma\n70360 khudawadi letter ya\n70361 khudawadi letter ra\n70362 khudawadi letter la\n70363 khudawadi letter va\n70364 khudawadi letter sha\n70365 khudawadi letter sa\n70366 khudawadi letter ha\n70367 khudawadi sign anusvara\n70368 khudawadi vowel sign aa\n70369 khudawadi vowel sign i\n70370 khudawadi vowel sign ii\n70371 khudawadi vowel sign u\n70372 khudawadi vowel sign uu\n70373 khudawadi vowel sign e\n70374 khudawadi vowel sign ai\n70375 khudawadi vowel sign o\n70376 khudawadi vowel sign au\n70377 khudawadi sign nukta\n70378 khudawadi sign virama\n70384 khudawadi digit zero\n70385 khudawadi digit one\n70386 khudawadi digit two\n70387 khudawadi digit three\n70388 khudawadi digit four\n70389 khudawadi digit five\n70390 khudawadi digit six\n70391 khudawadi digit seven\n70392 khudawadi digit eight\n70393 khudawadi digit nine\n70400 grantha sign combining anusvara above\n70401 grantha sign candrabindu\n70402 grantha sign anusvara\n70403 grantha sign visarga\n70405 grantha letter a\n70406 grantha letter aa\n70407 grantha letter i\n70408 grantha letter ii\n70409 grantha letter u\n70410 grantha letter uu\n70411 grantha letter vocalic r\n70412 grantha letter vocalic l\n70415 grantha letter ee\n70416 grantha letter ai\n70419 grantha letter oo\n70420 grantha letter au\n70421 grantha letter ka\n70422 grantha letter kha\n70423 grantha letter ga\n70424 grantha letter gha\n70425 grantha letter nga\n70426 grantha letter ca\n70427 grantha letter cha\n70428 grantha letter ja\n70429 grantha letter jha\n70430 grantha letter nya\n70431 grantha letter tta\n70432 grantha letter ttha\n70433 grantha letter dda\n70434 grantha letter ddha\n70435 grantha letter nna\n70436 grantha letter ta\n70437 grantha letter tha\n70438 grantha letter da\n70439 grantha letter dha\n70440 grantha letter na\n70442 grantha letter pa\n70443 grantha letter pha\n70444 grantha letter ba\n70445 grantha letter bha\n70446 grantha letter ma\n70447 grantha letter ya\n70448 grantha letter ra\n70450 grantha letter la\n70451 grantha letter lla\n70453 grantha letter va\n70454 grantha letter sha\n70455 grantha letter ssa\n70456 grantha letter sa\n70457 grantha letter ha\n70459 combining bindu below\n70460 grantha sign nukta\n70461 grantha sign avagraha\n70462 grantha vowel sign aa\n70463 grantha vowel sign i\n70464 grantha vowel sign ii\n70465 grantha vowel sign u\n70466 grantha vowel sign uu\n70467 grantha vowel sign vocalic r\n70468 grantha vowel sign vocalic rr\n70471 grantha vowel sign ee\n70472 grantha vowel sign ai\n70475 grantha vowel sign oo\n70476 grantha vowel sign au\n70477 grantha sign virama\n70480 grantha om\n70487 grantha au length mark\n70493 grantha sign pluta\n70494 grantha letter vedic anusvara\n70495 grantha letter vedic double anusvara\n70496 grantha letter vocalic rr\n70497 grantha letter vocalic ll\n70498 grantha vowel sign vocalic l\n70499 grantha vowel sign vocalic ll\n70502 combining grantha digit zero\n70503 combining grantha digit one\n70504 combining grantha digit two\n70505 combining grantha digit three\n70506 combining grantha digit four\n70507 combining grantha digit five\n70508 combining grantha digit six\n70512 combining grantha letter a\n70513 combining grantha letter ka\n70514 combining grantha letter na\n70515 combining grantha letter vi\n70516 combining grantha letter pa\n70528 tulu-tigalari letter a\n70529 tulu-tigalari letter aa\n70530 tulu-tigalari letter i\n70531 tulu-tigalari letter ii\n70532 tulu-tigalari letter u\n70533 tulu-tigalari letter uu\n70534 tulu-tigalari letter vocalic r\n70535 tulu-tigalari letter vocalic rr\n70536 tulu-tigalari letter vocalic l\n70537 tulu-tigalari letter vocalic ll\n70539 tulu-tigalari letter ee\n70542 tulu-tigalari letter ai\n70544 tulu-tigalari letter oo\n70545 tulu-tigalari letter au\n70546 tulu-tigalari letter ka\n70547 tulu-tigalari letter kha\n70548 tulu-tigalari letter ga\n70549 tulu-tigalari letter gha\n70550 tulu-tigalari letter nga\n70551 tulu-tigalari letter ca\n70552 tulu-tigalari letter cha\n70553 tulu-tigalari letter ja\n70554 tulu-tigalari letter jha\n70555 tulu-tigalari letter nya\n70556 tulu-tigalari letter tta\n70557 tulu-tigalari letter ttha\n70558 tulu-tigalari letter dda\n70559 tulu-tigalari letter ddha\n70560 tulu-tigalari letter nna\n70561 tulu-tigalari letter ta\n70562 tulu-tigalari letter tha\n70563 tulu-tigalari letter da\n70564 tulu-tigalari letter dha\n70565 tulu-tigalari letter na\n70566 tulu-tigalari letter pa\n70567 tulu-tigalari letter pha\n70568 tulu-tigalari letter ba\n70569 tulu-tigalari letter bha\n70570 tulu-tigalari letter ma\n70571 tulu-tigalari letter ya\n70572 tulu-tigalari letter ra\n70573 tulu-tigalari letter la\n70574 tulu-tigalari letter va\n70575 tulu-tigalari letter sha\n70576 tulu-tigalari letter ssa\n70577 tulu-tigalari letter sa\n70578 tulu-tigalari letter ha\n70579 tulu-tigalari letter lla\n70580 tulu-tigalari letter rra\n70581 tulu-tigalari letter llla\n70583 tulu-tigalari sign avagraha\n70584 tulu-tigalari vowel sign aa\n70585 tulu-tigalari vowel sign i\n70586 tulu-tigalari vowel sign ii\n70587 tulu-tigalari vowel sign u\n70588 tulu-tigalari vowel sign uu\n70589 tulu-tigalari vowel sign vocalic r\n70590 tulu-tigalari vowel sign vocalic rr\n70591 tulu-tigalari vowel sign vocalic l\n70592 tulu-tigalari vowel sign vocalic ll\n70594 tulu-tigalari vowel sign ee\n70597 tulu-tigalari vowel sign ai\n70599 tulu-tigalari vowel sign oo\n70600 tulu-tigalari vowel sign au\n70601 tulu-tigalari au length mark\n70602 tulu-tigalari sign candra anunasika\n70604 tulu-tigalari sign anusvara\n70605 tulu-tigalari sign visarga\n70606 tulu-tigalari sign virama\n70607 tulu-tigalari sign looped virama\n70608 tulu-tigalari conjoiner\n70609 tulu-tigalari repha\n70610 tulu-tigalari gemination mark\n70611 tulu-tigalari sign pluta\n70612 tulu-tigalari danda\n70613 tulu-tigalari double danda\n70615 tulu-tigalari sign om pushpika\n70616 tulu-tigalari sign shrii pushpika\n70625 tulu-tigalari vedic tone svarita\n70626 tulu-tigalari vedic tone anudatta\n70656 newa letter a\n70657 newa letter aa\n70658 newa letter i\n70659 newa letter ii\n70660 newa letter u\n70661 newa letter uu\n70662 newa letter vocalic r\n70663 newa letter vocalic rr\n70664 newa letter vocalic l\n70665 newa letter vocalic ll\n70666 newa letter e\n70667 newa letter ai\n70668 newa letter o\n70669 newa letter au\n70670 newa letter ka\n70671 newa letter kha\n70672 newa letter ga\n70673 newa letter gha\n70674 newa letter nga\n70675 newa letter ngha\n70676 newa letter ca\n70677 newa letter cha\n70678 newa letter ja\n70679 newa letter jha\n70680 newa letter nya\n70681 newa letter nyha\n70682 newa letter tta\n70683 newa letter ttha\n70684 newa letter dda\n70685 newa letter ddha\n70686 newa letter nna\n70687 newa letter ta\n70688 newa letter tha\n70689 newa letter da\n70690 newa letter dha\n70691 newa letter na\n70692 newa letter nha\n70693 newa letter pa\n70694 newa letter pha\n70695 newa letter ba\n70696 newa letter bha\n70697 newa letter ma\n70698 newa letter mha\n70699 newa letter ya\n70700 newa letter ra\n70701 newa letter rha\n70702 newa letter la\n70703 newa letter lha\n70704 newa letter wa\n70705 newa letter sha\n70706 newa letter ssa\n70707 newa letter sa\n70708 newa letter ha\n70709 newa vowel sign aa\n70710 newa vowel sign i\n70711 newa vowel sign ii\n70712 newa vowel sign u\n70713 newa vowel sign uu\n70714 newa vowel sign vocalic r\n70715 newa vowel sign vocalic rr\n70716 newa vowel sign vocalic l\n70717 newa vowel sign vocalic ll\n70718 newa vowel sign e\n70719 newa vowel sign ai\n70720 newa vowel sign o\n70721 newa vowel sign au\n70722 newa sign virama\n70723 newa sign candrabindu\n70724 newa sign anusvara\n70725 newa sign visarga\n70726 newa sign nukta\n70727 newa sign avagraha\n70728 newa sign final anusvara\n70729 newa om\n70730 newa siddhi\n70731 newa danda\n70732 newa double danda\n70733 newa comma\n70734 newa gap filler\n70735 newa abbreviation sign\n70736 newa digit zero\n70737 newa digit one\n70738 newa digit two\n70739 newa digit three\n70740 newa digit four\n70741 newa digit five\n70742 newa digit six\n70743 newa digit seven\n70744 newa digit eight\n70745 newa digit nine\n70746 newa double comma\n70747 newa placeholder mark\n70749 newa insertion sign\n70750 newa sandhi mark\n70751 newa letter vedic anusvara\n70752 newa sign jihvamuliya\n70753 newa sign upadhmaniya\n70784 tirhuta anji\n70785 tirhuta letter a\n70786 tirhuta letter aa\n70787 tirhuta letter i\n70788 tirhuta letter ii\n70789 tirhuta letter u\n70790 tirhuta letter uu\n70791 tirhuta letter vocalic r\n70792 tirhuta letter vocalic rr\n70793 tirhuta letter vocalic l\n70794 tirhuta letter vocalic ll\n70795 tirhuta letter e\n70796 tirhuta letter ai\n70797 tirhuta letter o\n70798 tirhuta letter au\n70799 tirhuta letter ka\n70800 tirhuta letter kha\n70801 tirhuta letter ga\n70802 tirhuta letter gha\n70803 tirhuta letter nga\n70804 tirhuta letter ca\n70805 tirhuta letter cha\n70806 tirhuta letter ja\n70807 tirhuta letter jha\n70808 tirhuta letter nya\n70809 tirhuta letter tta\n70810 tirhuta letter ttha\n70811 tirhuta letter dda\n70812 tirhuta letter ddha\n70813 tirhuta letter nna\n70814 tirhuta letter ta\n70815 tirhuta letter tha\n70816 tirhuta letter da\n70817 tirhuta letter dha\n70818 tirhuta letter na\n70819 tirhuta letter pa\n70820 tirhuta letter pha\n70821 tirhuta letter ba\n70822 tirhuta letter bha\n70823 tirhuta letter ma\n70824 tirhuta letter ya\n70825 tirhuta letter ra\n70826 tirhuta letter la\n70827 tirhuta letter va\n70828 tirhuta letter sha\n70829 tirhuta letter ssa\n70830 tirhuta letter sa\n70831 tirhuta letter ha\n70832 tirhuta vowel sign aa\n70833 tirhuta vowel sign i\n70834 tirhuta vowel sign ii\n70835 tirhuta vowel sign u\n70836 tirhuta vowel sign uu\n70837 tirhuta vowel sign vocalic r\n70838 tirhuta vowel sign vocalic rr\n70839 tirhuta vowel sign vocalic l\n70840 tirhuta vowel sign vocalic ll\n70841 tirhuta vowel sign e\n70842 tirhuta vowel sign short e\n70843 tirhuta vowel sign ai\n70844 tirhuta vowel sign o\n70845 tirhuta vowel sign short o\n70846 tirhuta vowel sign au\n70847 tirhuta sign candrabindu\n70848 tirhuta sign anusvara\n70849 tirhuta sign visarga\n70850 tirhuta sign virama\n70851 tirhuta sign nukta\n70852 tirhuta sign avagraha\n70853 tirhuta gvang\n70854 tirhuta abbreviation sign\n70855 tirhuta om\n70864 tirhuta digit zero\n70865 tirhuta digit one\n70866 tirhuta digit two\n70867 tirhuta digit three\n70868 tirhuta digit four\n70869 tirhuta digit five\n70870 tirhuta digit six\n70871 tirhuta digit seven\n70872 tirhuta digit eight\n70873 tirhuta digit nine\n71040 siddham letter a\n71041 siddham letter aa\n71042 siddham letter i\n71043 siddham letter ii\n71044 siddham letter u\n71045 siddham letter uu\n71046 siddham letter vocalic r\n71047 siddham letter vocalic rr\n71048 siddham letter vocalic l\n71049 siddham letter vocalic ll\n71050 siddham letter e\n71051 siddham letter ai\n71052 siddham letter o\n71053 siddham letter au\n71054 siddham letter ka\n71055 siddham letter kha\n71056 siddham letter ga\n71057 siddham letter gha\n71058 siddham letter nga\n71059 siddham letter ca\n71060 siddham letter cha\n71061 siddham letter ja\n71062 siddham letter jha\n71063 siddham letter nya\n71064 siddham letter tta\n71065 siddham letter ttha\n71066 siddham letter dda\n71067 siddham letter ddha\n71068 siddham letter nna\n71069 siddham letter ta\n71070 siddham letter tha\n71071 siddham letter da\n71072 siddham letter dha\n71073 siddham letter na\n71074 siddham letter pa\n71075 siddham letter pha\n71076 siddham letter ba\n71077 siddham letter bha\n71078 siddham letter ma\n71079 siddham letter ya\n71080 siddham letter ra\n71081 siddham letter la\n71082 siddham letter va\n71083 siddham letter sha\n71084 siddham letter ssa\n71085 siddham letter sa\n71086 siddham letter ha\n71087 siddham vowel sign aa\n71088 siddham vowel sign i\n71089 siddham vowel sign ii\n71090 siddham vowel sign u\n71091 siddham vowel sign uu\n71092 siddham vowel sign vocalic r\n71093 siddham vowel sign vocalic rr\n71096 siddham vowel sign e\n71097 siddham vowel sign ai\n71098 siddham vowel sign o\n71099 siddham vowel sign au\n71100 siddham sign candrabindu\n71101 siddham sign anusvara\n71102 siddham sign visarga\n71103 siddham sign virama\n71104 siddham sign nukta\n71105 siddham sign siddham\n71106 siddham danda\n71107 siddham double danda\n71108 siddham separator dot\n71109 siddham separator bar\n71110 siddham repetition mark-1\n71111 siddham repetition mark-2\n71112 siddham repetition mark-3\n71113 siddham end of text mark\n71114 siddham section mark with trident and u-shaped ornaments\n71115 siddham section mark with trident and dotted crescents\n71116 siddham section mark with rays and dotted crescents\n71117 siddham section mark with rays and dotted double crescents\n71118 siddham section mark with rays and dotted triple crescents\n71119 siddham section mark double ring\n71120 siddham section mark double ring with rays\n71121 siddham section mark with double crescents\n71122 siddham section mark with triple crescents\n71123 siddham section mark with quadruple crescents\n71124 siddham section mark with septuple crescents\n71125 siddham section mark with circles and rays\n71126 siddham section mark with circles and two enclosures\n71127 siddham section mark with circles and four enclosures\n71128 siddham letter three-circle alternate i\n71129 siddham letter two-circle alternate i\n71130 siddham letter two-circle alternate ii\n71131 siddham letter alternate u\n71132 siddham vowel sign alternate u\n71133 siddham vowel sign alternate uu\n71168 modi letter a\n71169 modi letter aa\n71170 modi letter i\n71171 modi letter ii\n71172 modi letter u\n71173 modi letter uu\n71174 modi letter vocalic r\n71175 modi letter vocalic rr\n71176 modi letter vocalic l\n71177 modi letter vocalic ll\n71178 modi letter e\n71179 modi letter ai\n71180 modi letter o\n71181 modi letter au\n71182 modi letter ka\n71183 modi letter kha\n71184 modi letter ga\n71185 modi letter gha\n71186 modi letter nga\n71187 modi letter ca\n71188 modi letter cha\n71189 modi letter ja\n71190 modi letter jha\n71191 modi letter nya\n71192 modi letter tta\n71193 modi letter ttha\n71194 modi letter dda\n71195 modi letter ddha\n71196 modi letter nna\n71197 modi letter ta\n71198 modi letter tha\n71199 modi letter da\n71200 modi letter dha\n71201 modi letter na\n71202 modi letter pa\n71203 modi letter pha\n71204 modi letter ba\n71205 modi letter bha\n71206 modi letter ma\n71207 modi letter ya\n71208 modi letter ra\n71209 modi letter la\n71210 modi letter va\n71211 modi letter sha\n71212 modi letter ssa\n71213 modi letter sa\n71214 modi letter ha\n71215 modi letter lla\n71216 modi vowel sign aa\n71217 modi vowel sign i\n71218 modi vowel sign ii\n71219 modi vowel sign u\n71220 modi vowel sign uu\n71221 modi vowel sign vocalic r\n71222 modi vowel sign vocalic rr\n71223 modi vowel sign vocalic l\n71224 modi vowel sign vocalic ll\n71225 modi vowel sign e\n71226 modi vowel sign ai\n71227 modi vowel sign o\n71228 modi vowel sign au\n71229 modi sign anusvara\n71230 modi sign visarga\n71231 modi sign virama\n71232 modi sign ardhacandra\n71233 modi danda\n71234 modi double danda\n71235 modi abbreviation sign\n71236 modi sign huva\n71248 modi digit zero\n71249 modi digit one\n71250 modi digit two\n71251 modi digit three\n71252 modi digit four\n71253 modi digit five\n71254 modi digit six\n71255 modi digit seven\n71256 modi digit eight\n71257 modi digit nine\n71264 mongolian birga with ornament\n71265 mongolian rotated birga\n71266 mongolian double birga with ornament\n71267 mongolian triple birga with ornament\n71268 mongolian birga with double ornament\n71269 mongolian rotated birga with ornament\n71270 mongolian rotated birga with double ornament\n71271 mongolian inverted birga\n71272 mongolian inverted birga with double ornament\n71273 mongolian swirl birga\n71274 mongolian swirl birga with ornament\n71275 mongolian swirl birga with double ornament\n71276 mongolian turned swirl birga with double ornament\n71296 takri letter a\n71297 takri letter aa\n71298 takri letter i\n71299 takri letter ii\n71300 takri letter u\n71301 takri letter uu\n71302 takri letter e\n71303 takri letter ai\n71304 takri letter o\n71305 takri letter au\n71306 takri letter ka\n71307 takri letter kha\n71308 takri letter ga\n71309 takri letter gha\n71310 takri letter nga\n71311 takri letter ca\n71312 takri letter cha\n71313 takri letter ja\n71314 takri letter jha\n71315 takri letter nya\n71316 takri letter tta\n71317 takri letter ttha\n71318 takri letter dda\n71319 takri letter ddha\n71320 takri letter nna\n71321 takri letter ta\n71322 takri letter tha\n71323 takri letter da\n71324 takri letter dha\n71325 takri letter na\n71326 takri letter pa\n71327 takri letter pha\n71328 takri letter ba\n71329 takri letter bha\n71330 takri letter ma\n71331 takri letter ya\n71332 takri letter ra\n71333 takri letter la\n71334 takri letter va\n71335 takri letter sha\n71336 takri letter sa\n71337 takri letter ha\n71338 takri letter rra\n71339 takri sign anusvara\n71340 takri sign visarga\n71341 takri vowel sign aa\n71342 takri vowel sign i\n71343 takri vowel sign ii\n71344 takri vowel sign u\n71345 takri vowel sign uu\n71346 takri vowel sign e\n71347 takri vowel sign ai\n71348 takri vowel sign o\n71349 takri vowel sign au\n71350 takri sign virama\n71351 takri sign nukta\n71352 takri letter archaic kha\n71353 takri abbreviation sign\n71360 takri digit zero\n71361 takri digit one\n71362 takri digit two\n71363 takri digit three\n71364 takri digit four\n71365 takri digit five\n71366 takri digit six\n71367 takri digit seven\n71368 takri digit eight\n71369 takri digit nine\n71376 myanmar pao digit zero\n71377 myanmar pao digit one\n71378 myanmar pao digit two\n71379 myanmar pao digit three\n71380 myanmar pao digit four\n71381 myanmar pao digit five\n71382 myanmar pao digit six\n71383 myanmar pao digit seven\n71384 myanmar pao digit eight\n71385 myanmar pao digit nine\n71386 myanmar eastern pwo karen digit zero\n71387 myanmar eastern pwo karen digit one\n71388 myanmar eastern pwo karen digit two\n71389 myanmar eastern pwo karen digit three\n71390 myanmar eastern pwo karen digit four\n71391 myanmar eastern pwo karen digit five\n71392 myanmar eastern pwo karen digit six\n71393 myanmar eastern pwo karen digit seven\n71394 myanmar eastern pwo karen digit eight\n71395 myanmar eastern pwo karen digit nine\n71424 ahom letter ka\n71425 ahom letter kha\n71426 ahom letter nga\n71427 ahom letter na\n71428 ahom letter ta\n71429 ahom letter alternate ta\n71430 ahom letter pa\n71431 ahom letter pha\n71432 ahom letter ba\n71433 ahom letter ma\n71434 ahom letter ja\n71435 ahom letter cha\n71436 ahom letter tha\n71437 ahom letter ra\n71438 ahom letter la\n71439 ahom letter sa\n71440 ahom letter nya\n71441 ahom letter ha\n71442 ahom letter a\n71443 ahom letter da\n71444 ahom letter dha\n71445 ahom letter ga\n71446 ahom letter alternate ga\n71447 ahom letter gha\n71448 ahom letter bha\n71449 ahom letter jha\n71450 ahom letter alternate ba\n71453 ahom consonant sign medial la\n71454 ahom consonant sign medial ra\n71455 ahom consonant sign medial ligating ra\n71456 ahom vowel sign a\n71457 ahom vowel sign aa\n71458 ahom vowel sign i\n71459 ahom vowel sign ii\n71460 ahom vowel sign u\n71461 ahom vowel sign uu\n71462 ahom vowel sign e\n71463 ahom vowel sign aw\n71464 ahom vowel sign o\n71465 ahom vowel sign ai\n71466 ahom vowel sign am\n71467 ahom sign killer\n71472 ahom digit zero\n71473 ahom digit one\n71474 ahom digit two\n71475 ahom digit three\n71476 ahom digit four\n71477 ahom digit five\n71478 ahom digit six\n71479 ahom digit seven\n71480 ahom digit eight\n71481 ahom digit nine\n71482 ahom number ten\n71483 ahom number twenty\n71484 ahom sign small section\n71485 ahom sign section\n71486 ahom sign rulai\n71487 ahom symbol vi\n71488 ahom letter ca\n71489 ahom letter tta\n71490 ahom letter ttha\n71491 ahom letter dda\n71492 ahom letter ddha\n71493 ahom letter nna\n71494 ahom letter lla\n71680 dogra letter a\n71681 dogra letter aa\n71682 dogra letter i\n71683 dogra letter ii\n71684 dogra letter u\n71685 dogra letter uu\n71686 dogra letter e\n71687 dogra letter ai\n71688 dogra letter o\n71689 dogra letter au\n71690 dogra letter ka\n71691 dogra letter kha\n71692 dogra letter ga\n71693 dogra letter gha\n71694 dogra letter nga\n71695 dogra letter ca\n71696 dogra letter cha\n71697 dogra letter ja\n71698 dogra letter jha\n71699 dogra letter nya\n71700 dogra letter tta\n71701 dogra letter ttha\n71702 dogra letter dda\n71703 dogra letter ddha\n71704 dogra letter nna\n71705 dogra letter ta\n71706 dogra letter tha\n71707 dogra letter da\n71708 dogra letter dha\n71709 dogra letter na\n71710 dogra letter pa\n71711 dogra letter pha\n71712 dogra letter ba\n71713 dogra letter bha\n71714 dogra letter ma\n71715 dogra letter ya\n71716 dogra letter ra\n71717 dogra letter la\n71718 dogra letter va\n71719 dogra letter sha\n71720 dogra letter ssa\n71721 dogra letter sa\n71722 dogra letter ha\n71723 dogra letter rra\n71724 dogra vowel sign aa\n71725 dogra vowel sign i\n71726 dogra vowel sign ii\n71727 dogra vowel sign u\n71728 dogra vowel sign uu\n71729 dogra vowel sign vocalic r\n71730 dogra vowel sign vocalic rr\n71731 dogra vowel sign e\n71732 dogra vowel sign ai\n71733 dogra vowel sign o\n71734 dogra vowel sign au\n71735 dogra sign anusvara\n71736 dogra sign visarga\n71737 dogra sign virama\n71738 dogra sign nukta\n71739 dogra abbreviation sign\n71840 warang citi capital letter ngaa\n71841 warang citi capital letter a\n71842 warang citi capital letter wi\n71843 warang citi capital letter yu\n71844 warang citi capital letter ya\n71845 warang citi capital letter yo\n71846 warang citi capital letter ii\n71847 warang citi capital letter uu\n71848 warang citi capital letter e\n71849 warang citi capital letter o\n71850 warang citi capital letter ang\n71851 warang citi capital letter ga\n71852 warang citi capital letter ko\n71853 warang citi capital letter eny\n71854 warang citi capital letter yuj\n71855 warang citi capital letter uc\n71856 warang citi capital letter enn\n71857 warang citi capital letter odd\n71858 warang citi capital letter tte\n71859 warang citi capital letter nung\n71860 warang citi capital letter da\n71861 warang citi capital letter at\n71862 warang citi capital letter am\n71863 warang citi capital letter bu\n71864 warang citi capital letter pu\n71865 warang citi capital letter hiyo\n71866 warang citi capital letter holo\n71867 warang citi capital letter horr\n71868 warang citi capital letter har\n71869 warang citi capital letter ssuu\n71870 warang citi capital letter sii\n71871 warang citi capital letter viyo\n71872 warang citi small letter ngaa\n71873 warang citi small letter a\n71874 warang citi small letter wi\n71875 warang citi small letter yu\n71876 warang citi small letter ya\n71877 warang citi small letter yo\n71878 warang citi small letter ii\n71879 warang citi small letter uu\n71880 warang citi small letter e\n71881 warang citi small letter o\n71882 warang citi small letter ang\n71883 warang citi small letter ga\n71884 warang citi small letter ko\n71885 warang citi small letter eny\n71886 warang citi small letter yuj\n71887 warang citi small letter uc\n71888 warang citi small letter enn\n71889 warang citi small letter odd\n71890 warang citi small letter tte\n71891 warang citi small letter nung\n71892 warang citi small letter da\n71893 warang citi small letter at\n71894 warang citi small letter am\n71895 warang citi small letter bu\n71896 warang citi small letter pu\n71897 warang citi small letter hiyo\n71898 warang citi small letter holo\n71899 warang citi small letter horr\n71900 warang citi small letter har\n71901 warang citi small letter ssuu\n71902 warang citi small letter sii\n71903 warang citi small letter viyo\n71904 warang citi digit zero\n71905 warang citi digit one\n71906 warang citi digit two\n71907 warang citi digit three\n71908 warang citi digit four\n71909 warang citi digit five\n71910 warang citi digit six\n71911 warang citi digit seven\n71912 warang citi digit eight\n71913 warang citi digit nine\n71914 warang citi number ten\n71915 warang citi number twenty\n71916 warang citi number thirty\n71917 warang citi number forty\n71918 warang citi number fifty\n71919 warang citi number sixty\n71920 warang citi number seventy\n71921 warang citi number eighty\n71922 warang citi number ninety\n71935 warang citi om\n71936 dives akuru letter a\n71937 dives akuru letter aa\n71938 dives akuru letter i\n71939 dives akuru letter ii\n71940 dives akuru letter u\n71941 dives akuru letter uu\n71942 dives akuru letter e\n71945 dives akuru letter o\n71948 dives akuru letter ka\n71949 dives akuru letter kha\n71950 dives akuru letter ga\n71951 dives akuru letter gha\n71952 dives akuru letter nga\n71953 dives akuru letter ca\n71954 dives akuru letter cha\n71955 dives akuru letter ja\n71957 dives akuru letter nya\n71958 dives akuru letter tta\n71960 dives akuru letter dda\n71961 dives akuru letter ddha\n71962 dives akuru letter nna\n71963 dives akuru letter ta\n71964 dives akuru letter tha\n71965 dives akuru letter da\n71966 dives akuru letter dha\n71967 dives akuru letter na\n71968 dives akuru letter pa\n71969 dives akuru letter pha\n71970 dives akuru letter ba\n71971 dives akuru letter bha\n71972 dives akuru letter ma\n71973 dives akuru letter ya\n71974 dives akuru letter yya\n71975 dives akuru letter ra\n71976 dives akuru letter la\n71977 dives akuru letter va\n71978 dives akuru letter sha\n71979 dives akuru letter ssa\n71980 dives akuru letter sa\n71981 dives akuru letter ha\n71982 dives akuru letter lla\n71983 dives akuru letter za\n71984 dives akuru vowel sign aa\n71985 dives akuru vowel sign i\n71986 dives akuru vowel sign ii\n71987 dives akuru vowel sign u\n71988 dives akuru vowel sign uu\n71989 dives akuru vowel sign e\n71991 dives akuru vowel sign ai\n71992 dives akuru vowel sign o\n71995 dives akuru sign anusvara\n71996 dives akuru sign candrabindu\n71997 dives akuru sign halanta\n71998 dives akuru virama\n71999 dives akuru prefixed nasal sign\n72000 dives akuru medial ya\n72001 dives akuru initial ra\n72002 dives akuru medial ra\n72003 dives akuru sign nukta\n72004 dives akuru double danda\n72005 dives akuru gap filler\n72006 dives akuru end of text mark\n72016 dives akuru digit zero\n72017 dives akuru digit one\n72018 dives akuru digit two\n72019 dives akuru digit three\n72020 dives akuru digit four\n72021 dives akuru digit five\n72022 dives akuru digit six\n72023 dives akuru digit seven\n72024 dives akuru digit eight\n72025 dives akuru digit nine\n72096 nandinagari letter a\n72097 nandinagari letter aa\n72098 nandinagari letter i\n72099 nandinagari letter ii\n72100 nandinagari letter u\n72101 nandinagari letter uu\n72102 nandinagari letter vocalic r\n72103 nandinagari letter vocalic rr\n72106 nandinagari letter e\n72107 nandinagari letter ai\n72108 nandinagari letter o\n72109 nandinagari letter au\n72110 nandinagari letter ka\n72111 nandinagari letter kha\n72112 nandinagari letter ga\n72113 nandinagari letter gha\n72114 nandinagari letter nga\n72115 nandinagari letter ca\n72116 nandinagari letter cha\n72117 nandinagari letter ja\n72118 nandinagari letter jha\n72119 nandinagari letter nya\n72120 nandinagari letter tta\n72121 nandinagari letter ttha\n72122 nandinagari letter dda\n72123 nandinagari letter ddha\n72124 nandinagari letter nna\n72125 nandinagari letter ta\n72126 nandinagari letter tha\n72127 nandinagari letter da\n72128 nandinagari letter dha\n72129 nandinagari letter na\n72130 nandinagari letter pa\n72131 nandinagari letter pha\n72132 nandinagari letter ba\n72133 nandinagari letter bha\n72134 nandinagari letter ma\n72135 nandinagari letter ya\n72136 nandinagari letter ra\n72137 nandinagari letter la\n72138 nandinagari letter va\n72139 nandinagari letter sha\n72140 nandinagari letter ssa\n72141 nandinagari letter sa\n72142 nandinagari letter ha\n72143 nandinagari letter lla\n72144 nandinagari letter rra\n72145 nandinagari vowel sign aa\n72146 nandinagari vowel sign i\n72147 nandinagari vowel sign ii\n72148 nandinagari vowel sign u\n72149 nandinagari vowel sign uu\n72150 nandinagari vowel sign vocalic r\n72151 nandinagari vowel sign vocalic rr\n72154 nandinagari vowel sign e\n72155 nandinagari vowel sign ai\n72156 nandinagari vowel sign o\n72157 nandinagari vowel sign au\n72158 nandinagari sign anusvara\n72159 nandinagari sign visarga\n72160 nandinagari sign virama\n72161 nandinagari sign avagraha\n72162 nandinagari sign siddham\n72163 nandinagari headstroke\n72164 nandinagari vowel sign prishthamatra e\n72192 zanabazar square letter a\n72193 zanabazar square vowel sign i\n72194 zanabazar square vowel sign ue\n72195 zanabazar square vowel sign u\n72196 zanabazar square vowel sign e\n72197 zanabazar square vowel sign oe\n72198 zanabazar square vowel sign o\n72199 zanabazar square vowel sign ai\n72200 zanabazar square vowel sign au\n72201 zanabazar square vowel sign reversed i\n72202 zanabazar square vowel length mark\n72203 zanabazar square letter ka\n72204 zanabazar square letter kha\n72205 zanabazar square letter ga\n72206 zanabazar square letter gha\n72207 zanabazar square letter nga\n72208 zanabazar square letter ca\n72209 zanabazar square letter cha\n72210 zanabazar square letter ja\n72211 zanabazar square letter nya\n72212 zanabazar square letter tta\n72213 zanabazar square letter ttha\n72214 zanabazar square letter dda\n72215 zanabazar square letter ddha\n72216 zanabazar square letter nna\n72217 zanabazar square letter ta\n72218 zanabazar square letter tha\n72219 zanabazar square letter da\n72220 zanabazar square letter dha\n72221 zanabazar square letter na\n72222 zanabazar square letter pa\n72223 zanabazar square letter pha\n72224 zanabazar square letter ba\n72225 zanabazar square letter bha\n72226 zanabazar square letter ma\n72227 zanabazar square letter tsa\n72228 zanabazar square letter tsha\n72229 zanabazar square letter dza\n72230 zanabazar square letter dzha\n72231 zanabazar square letter zha\n72232 zanabazar square letter za\n72233 zanabazar square letter -a\n72234 zanabazar square letter ya\n72235 zanabazar square letter ra\n72236 zanabazar square letter la\n72237 zanabazar square letter va\n72238 zanabazar square letter sha\n72239 zanabazar square letter ssa\n72240 zanabazar square letter sa\n72241 zanabazar square letter ha\n72242 zanabazar square letter kssa\n72243 zanabazar square final consonant mark\n72244 zanabazar square sign virama\n72245 zanabazar square sign candrabindu\n72246 zanabazar square sign candrabindu with ornament\n72247 zanabazar square sign candra with ornament\n72248 zanabazar square sign anusvara\n72249 zanabazar square sign visarga\n72250 zanabazar square cluster-initial letter ra\n72251 zanabazar square cluster-final letter ya\n72252 zanabazar square cluster-final letter ra\n72253 zanabazar square cluster-final letter la\n72254 zanabazar square cluster-final letter va\n72255 zanabazar square initial head mark\n72256 zanabazar square closing head mark\n72257 zanabazar square mark tsheg\n72258 zanabazar square mark shad\n72259 zanabazar square mark double shad\n72260 zanabazar square mark long tsheg\n72261 zanabazar square initial double-lined head mark\n72262 zanabazar square closing double-lined head mark\n72263 zanabazar square subjoiner\n72272 soyombo letter a\n72273 soyombo vowel sign i\n72274 soyombo vowel sign ue\n72275 soyombo vowel sign u\n72276 soyombo vowel sign e\n72277 soyombo vowel sign o\n72278 soyombo vowel sign oe\n72279 soyombo vowel sign ai\n72280 soyombo vowel sign au\n72281 soyombo vowel sign vocalic r\n72282 soyombo vowel sign vocalic l\n72283 soyombo vowel length mark\n72284 soyombo letter ka\n72285 soyombo letter kha\n72286 soyombo letter ga\n72287 soyombo letter gha\n72288 soyombo letter nga\n72289 soyombo letter ca\n72290 soyombo letter cha\n72291 soyombo letter ja\n72292 soyombo letter jha\n72293 soyombo letter nya\n72294 soyombo letter tta\n72295 soyombo letter ttha\n72296 soyombo letter dda\n72297 soyombo letter ddha\n72298 soyombo letter nna\n72299 soyombo letter ta\n72300 soyombo letter tha\n72301 soyombo letter da\n72302 soyombo letter dha\n72303 soyombo letter na\n72304 soyombo letter pa\n72305 soyombo letter pha\n72306 soyombo letter ba\n72307 soyombo letter bha\n72308 soyombo letter ma\n72309 soyombo letter tsa\n72310 soyombo letter tsha\n72311 soyombo letter dza\n72312 soyombo letter zha\n72313 soyombo letter za\n72314 soyombo letter -a\n72315 soyombo letter ya\n72316 soyombo letter ra\n72317 soyombo letter la\n72318 soyombo letter va\n72319 soyombo letter sha\n72320 soyombo letter ssa\n72321 soyombo letter sa\n72322 soyombo letter ha\n72323 soyombo letter kssa\n72324 soyombo sign jihvamuliya\n72325 soyombo sign upadhmaniya\n72326 soyombo cluster-initial letter ra\n72327 soyombo cluster-initial letter la\n72328 soyombo cluster-initial letter sha\n72329 soyombo cluster-initial letter sa\n72330 soyombo final consonant sign g\n72331 soyombo final consonant sign k\n72332 soyombo final consonant sign ng\n72333 soyombo final consonant sign d\n72334 soyombo final consonant sign n\n72335 soyombo final consonant sign b\n72336 soyombo final consonant sign m\n72337 soyombo final consonant sign r\n72338 soyombo final consonant sign l\n72339 soyombo final consonant sign sh\n72340 soyombo final consonant sign s\n72341 soyombo final consonant sign -a\n72342 soyombo sign anusvara\n72343 soyombo sign visarga\n72344 soyombo gemination mark\n72345 soyombo subjoiner\n72346 soyombo mark tsheg\n72347 soyombo mark shad\n72348 soyombo mark double shad\n72349 soyombo mark pluta\n72350 soyombo head mark with moon and sun and triple flame\n72351 soyombo head mark with moon and sun and flame\n72352 soyombo head mark with moon and sun\n72353 soyombo terminal mark-1\n72354 soyombo terminal mark-2\n72368 canadian syllabics nattilik hi\n72369 canadian syllabics nattilik hii\n72370 canadian syllabics nattilik ho\n72371 canadian syllabics nattilik hoo\n72372 canadian syllabics nattilik ha\n72373 canadian syllabics nattilik haa\n72374 canadian syllabics nattilik shri\n72375 canadian syllabics nattilik shrii\n72376 canadian syllabics nattilik shro\n72377 canadian syllabics nattilik shroo\n72378 canadian syllabics nattilik shra\n72379 canadian syllabics nattilik shraa\n72380 canadian syllabics spe\n72381 canadian syllabics spi\n72382 canadian syllabics spo\n72383 canadian syllabics spa\n72384 pau cin hau letter pa\n72385 pau cin hau letter ka\n72386 pau cin hau letter la\n72387 pau cin hau letter ma\n72388 pau cin hau letter da\n72389 pau cin hau letter za\n72390 pau cin hau letter va\n72391 pau cin hau letter nga\n72392 pau cin hau letter ha\n72393 pau cin hau letter ga\n72394 pau cin hau letter kha\n72395 pau cin hau letter sa\n72396 pau cin hau letter ba\n72397 pau cin hau letter ca\n72398 pau cin hau letter ta\n72399 pau cin hau letter tha\n72400 pau cin hau letter na\n72401 pau cin hau letter pha\n72402 pau cin hau letter ra\n72403 pau cin hau letter fa\n72404 pau cin hau letter cha\n72405 pau cin hau letter a\n72406 pau cin hau letter e\n72407 pau cin hau letter i\n72408 pau cin hau letter o\n72409 pau cin hau letter u\n72410 pau cin hau letter ua\n72411 pau cin hau letter ia\n72412 pau cin hau letter final p\n72413 pau cin hau letter final k\n72414 pau cin hau letter final t\n72415 pau cin hau letter final m\n72416 pau cin hau letter final n\n72417 pau cin hau letter final l\n72418 pau cin hau letter final w\n72419 pau cin hau letter final ng\n72420 pau cin hau letter final y\n72421 pau cin hau rising tone long\n72422 pau cin hau rising tone\n72423 pau cin hau sandhi glottal stop\n72424 pau cin hau rising tone long final\n72425 pau cin hau rising tone final\n72426 pau cin hau sandhi glottal stop final\n72427 pau cin hau sandhi tone long\n72428 pau cin hau sandhi tone\n72429 pau cin hau sandhi tone long final\n72430 pau cin hau sandhi tone final\n72431 pau cin hau mid-level tone\n72432 pau cin hau glottal stop variant\n72433 pau cin hau mid-level tone long final\n72434 pau cin hau mid-level tone final\n72435 pau cin hau low-falling tone long\n72436 pau cin hau low-falling tone\n72437 pau cin hau glottal stop\n72438 pau cin hau low-falling tone long final\n72439 pau cin hau low-falling tone final\n72440 pau cin hau glottal stop final\n72448 devanagari head mark\n72449 devanagari head mark with headstroke\n72450 devanagari sign bhale\n72451 devanagari sign bhale with hook\n72452 devanagari sign extended bhale\n72453 devanagari sign extended bhale with hook\n72454 devanagari sign western five-like bhale\n72455 devanagari sign western nine-like bhale\n72456 devanagari sign reversed nine-like bhale\n72457 devanagari sign mindu\n72544 sharada vowel sign oe\n72545 sharada vowel sign ooe\n72546 sharada vowel sign ue\n72547 sharada vowel sign uue\n72548 sharada vowel sign short e\n72549 sharada vowel sign short o\n72550 sharada vowel sign candra e\n72551 sharada vowel sign candra o\n72640 sunuwar letter devi\n72641 sunuwar letter tasla\n72642 sunuwar letter eko\n72643 sunuwar letter imar\n72644 sunuwar letter reu\n72645 sunuwar letter utthi\n72646 sunuwar letter kik\n72647 sunuwar letter ma\n72648 sunuwar letter appho\n72649 sunuwar letter pip\n72650 sunuwar letter gil\n72651 sunuwar letter hamso\n72652 sunuwar letter carmi\n72653 sunuwar letter nah\n72654 sunuwar letter bur\n72655 sunuwar letter jyah\n72656 sunuwar letter loacha\n72657 sunuwar letter otthi\n72658 sunuwar letter shyele\n72659 sunuwar letter varca\n72660 sunuwar letter yat\n72661 sunuwar letter ava\n72662 sunuwar letter aal\n72663 sunuwar letter donga\n72664 sunuwar letter thari\n72665 sunuwar letter phar\n72666 sunuwar letter ngar\n72667 sunuwar letter kha\n72668 sunuwar letter shyer\n72669 sunuwar letter chelap\n72670 sunuwar letter tentu\n72671 sunuwar letter thele\n72672 sunuwar letter kloko\n72673 sunuwar sign pvo\n72688 sunuwar digit zero\n72689 sunuwar digit one\n72690 sunuwar digit two\n72691 sunuwar digit three\n72692 sunuwar digit four\n72693 sunuwar digit five\n72694 sunuwar digit six\n72695 sunuwar digit seven\n72696 sunuwar digit eight\n72697 sunuwar digit nine\n72704 bhaiksuki letter a\n72705 bhaiksuki letter aa\n72706 bhaiksuki letter i\n72707 bhaiksuki letter ii\n72708 bhaiksuki letter u\n72709 bhaiksuki letter uu\n72710 bhaiksuki letter vocalic r\n72711 bhaiksuki letter vocalic rr\n72712 bhaiksuki letter vocalic l\n72714 bhaiksuki letter e\n72715 bhaiksuki letter ai\n72716 bhaiksuki letter o\n72717 bhaiksuki letter au\n72718 bhaiksuki letter ka\n72719 bhaiksuki letter kha\n72720 bhaiksuki letter ga\n72721 bhaiksuki letter gha\n72722 bhaiksuki letter nga\n72723 bhaiksuki letter ca\n72724 bhaiksuki letter cha\n72725 bhaiksuki letter ja\n72726 bhaiksuki letter jha\n72727 bhaiksuki letter nya\n72728 bhaiksuki letter tta\n72729 bhaiksuki letter ttha\n72730 bhaiksuki letter dda\n72731 bhaiksuki letter ddha\n72732 bhaiksuki letter nna\n72733 bhaiksuki letter ta\n72734 bhaiksuki letter tha\n72735 bhaiksuki letter da\n72736 bhaiksuki letter dha\n72737 bhaiksuki letter na\n72738 bhaiksuki letter pa\n72739 bhaiksuki letter pha\n72740 bhaiksuki letter ba\n72741 bhaiksuki letter bha\n72742 bhaiksuki letter ma\n72743 bhaiksuki letter ya\n72744 bhaiksuki letter ra\n72745 bhaiksuki letter la\n72746 bhaiksuki letter va\n72747 bhaiksuki letter sha\n72748 bhaiksuki letter ssa\n72749 bhaiksuki letter sa\n72750 bhaiksuki letter ha\n72751 bhaiksuki vowel sign aa\n72752 bhaiksuki vowel sign i\n72753 bhaiksuki vowel sign ii\n72754 bhaiksuki vowel sign u\n72755 bhaiksuki vowel sign uu\n72756 bhaiksuki vowel sign vocalic r\n72757 bhaiksuki vowel sign vocalic rr\n72758 bhaiksuki vowel sign vocalic l\n72760 bhaiksuki vowel sign e\n72761 bhaiksuki vowel sign ai\n72762 bhaiksuki vowel sign o\n72763 bhaiksuki vowel sign au\n72764 bhaiksuki sign candrabindu\n72765 bhaiksuki sign anusvara\n72766 bhaiksuki sign visarga\n72767 bhaiksuki sign virama\n72768 bhaiksuki sign avagraha\n72769 bhaiksuki danda\n72770 bhaiksuki double danda\n72771 bhaiksuki word separator\n72772 bhaiksuki gap filler-1\n72773 bhaiksuki gap filler-2\n72784 bhaiksuki digit zero\n72785 bhaiksuki digit one\n72786 bhaiksuki digit two\n72787 bhaiksuki digit three\n72788 bhaiksuki digit four\n72789 bhaiksuki digit five\n72790 bhaiksuki digit six\n72791 bhaiksuki digit seven\n72792 bhaiksuki digit eight\n72793 bhaiksuki digit nine\n72794 bhaiksuki number one\n72795 bhaiksuki number two\n72796 bhaiksuki number three\n72797 bhaiksuki number four\n72798 bhaiksuki number five\n72799 bhaiksuki number six\n72800 bhaiksuki number seven\n72801 bhaiksuki number eight\n72802 bhaiksuki number nine\n72803 bhaiksuki number ten\n72804 bhaiksuki number twenty\n72805 bhaiksuki number thirty\n72806 bhaiksuki number forty\n72807 bhaiksuki number fifty\n72808 bhaiksuki number sixty\n72809 bhaiksuki number seventy\n72810 bhaiksuki number eighty\n72811 bhaiksuki number ninety\n72812 bhaiksuki hundreds unit mark\n72816 marchen head mark\n72817 marchen mark shad\n72818 marchen letter ka\n72819 marchen letter kha\n72820 marchen letter ga\n72821 marchen letter nga\n72822 marchen letter ca\n72823 marchen letter cha\n72824 marchen letter ja\n72825 marchen letter nya\n72826 marchen letter ta\n72827 marchen letter tha\n72828 marchen letter da\n72829 marchen letter na\n72830 marchen letter pa\n72831 marchen letter pha\n72832 marchen letter ba\n72833 marchen letter ma\n72834 marchen letter tsa\n72835 marchen letter tsha\n72836 marchen letter dza\n72837 marchen letter wa\n72838 marchen letter zha\n72839 marchen letter za\n72840 marchen letter -a\n72841 marchen letter ya\n72842 marchen letter ra\n72843 marchen letter la\n72844 marchen letter sha\n72845 marchen letter sa\n72846 marchen letter ha\n72847 marchen letter a\n72850 marchen subjoined letter ka\n72851 marchen subjoined letter kha\n72852 marchen subjoined letter ga\n72853 marchen subjoined letter nga\n72854 marchen subjoined letter ca\n72855 marchen subjoined letter cha\n72856 marchen subjoined letter ja\n72857 marchen subjoined letter nya\n72858 marchen subjoined letter ta\n72859 marchen subjoined letter tha\n72860 marchen subjoined letter da\n72861 marchen subjoined letter na\n72862 marchen subjoined letter pa\n72863 marchen subjoined letter pha\n72864 marchen subjoined letter ba\n72865 marchen subjoined letter ma\n72866 marchen subjoined letter tsa\n72867 marchen subjoined letter tsha\n72868 marchen subjoined letter dza\n72869 marchen subjoined letter wa\n72870 marchen subjoined letter zha\n72871 marchen subjoined letter za\n72873 marchen subjoined letter ya\n72874 marchen subjoined letter ra\n72875 marchen subjoined letter la\n72876 marchen subjoined letter sha\n72877 marchen subjoined letter sa\n72878 marchen subjoined letter ha\n72879 marchen subjoined letter a\n72880 marchen vowel sign aa\n72881 marchen vowel sign i\n72882 marchen vowel sign u\n72883 marchen vowel sign e\n72884 marchen vowel sign o\n72885 marchen sign anusvara\n72886 marchen sign candrabindu\n72960 masaram gondi letter a\n72961 masaram gondi letter aa\n72962 masaram gondi letter i\n72963 masaram gondi letter ii\n72964 masaram gondi letter u\n72965 masaram gondi letter uu\n72966 masaram gondi letter e\n72968 masaram gondi letter ai\n72969 masaram gondi letter o\n72971 masaram gondi letter au\n72972 masaram gondi letter ka\n72973 masaram gondi letter kha\n72974 masaram gondi letter ga\n72975 masaram gondi letter gha\n72976 masaram gondi letter nga\n72977 masaram gondi letter ca\n72978 masaram gondi letter cha\n72979 masaram gondi letter ja\n72980 masaram gondi letter jha\n72981 masaram gondi letter nya\n72982 masaram gondi letter tta\n72983 masaram gondi letter ttha\n72984 masaram gondi letter dda\n72985 masaram gondi letter ddha\n72986 masaram gondi letter nna\n72987 masaram gondi letter ta\n72988 masaram gondi letter tha\n72989 masaram gondi letter da\n72990 masaram gondi letter dha\n72991 masaram gondi letter na\n72992 masaram gondi letter pa\n72993 masaram gondi letter pha\n72994 masaram gondi letter ba\n72995 masaram gondi letter bha\n72996 masaram gondi letter ma\n72997 masaram gondi letter ya\n72998 masaram gondi letter ra\n72999 masaram gondi letter la\n73000 masaram gondi letter va\n73001 masaram gondi letter sha\n73002 masaram gondi letter ssa\n73003 masaram gondi letter sa\n73004 masaram gondi letter ha\n73005 masaram gondi letter lla\n73006 masaram gondi letter kssa\n73007 masaram gondi letter jnya\n73008 masaram gondi letter tra\n73009 masaram gondi vowel sign aa\n73010 masaram gondi vowel sign i\n73011 masaram gondi vowel sign ii\n73012 masaram gondi vowel sign u\n73013 masaram gondi vowel sign uu\n73014 masaram gondi vowel sign vocalic r\n73018 masaram gondi vowel sign e\n73020 masaram gondi vowel sign ai\n73021 masaram gondi vowel sign o\n73023 masaram gondi vowel sign au\n73024 masaram gondi sign anusvara\n73025 masaram gondi sign visarga\n73026 masaram gondi sign nukta\n73027 masaram gondi sign candra\n73028 masaram gondi sign halanta\n73029 masaram gondi virama\n73030 masaram gondi repha\n73031 masaram gondi ra-kara\n73040 masaram gondi digit zero\n73041 masaram gondi digit one\n73042 masaram gondi digit two\n73043 masaram gondi digit three\n73044 masaram gondi digit four\n73045 masaram gondi digit five\n73046 masaram gondi digit six\n73047 masaram gondi digit seven\n73048 masaram gondi digit eight\n73049 masaram gondi digit nine\n73056 gunjala gondi letter a\n73057 gunjala gondi letter aa\n73058 gunjala gondi letter i\n73059 gunjala gondi letter ii\n73060 gunjala gondi letter u\n73061 gunjala gondi letter uu\n73063 gunjala gondi letter ee\n73064 gunjala gondi letter ai\n73066 gunjala gondi letter oo\n73067 gunjala gondi letter au\n73068 gunjala gondi letter ya\n73069 gunjala gondi letter va\n73070 gunjala gondi letter ba\n73071 gunjala gondi letter bha\n73072 gunjala gondi letter ma\n73073 gunjala gondi letter ka\n73074 gunjala gondi letter kha\n73075 gunjala gondi letter ta\n73076 gunjala gondi letter tha\n73077 gunjala gondi letter la\n73078 gunjala gondi letter ga\n73079 gunjala gondi letter gha\n73080 gunjala gondi letter da\n73081 gunjala gondi letter dha\n73082 gunjala gondi letter na\n73083 gunjala gondi letter ca\n73084 gunjala gondi letter cha\n73085 gunjala gondi letter tta\n73086 gunjala gondi letter ttha\n73087 gunjala gondi letter lla\n73088 gunjala gondi letter ja\n73089 gunjala gondi letter jha\n73090 gunjala gondi letter dda\n73091 gunjala gondi letter ddha\n73092 gunjala gondi letter nga\n73093 gunjala gondi letter pa\n73094 gunjala gondi letter pha\n73095 gunjala gondi letter ha\n73096 gunjala gondi letter ra\n73097 gunjala gondi letter sa\n73098 gunjala gondi vowel sign aa\n73099 gunjala gondi vowel sign i\n73100 gunjala gondi vowel sign ii\n73101 gunjala gondi vowel sign u\n73102 gunjala gondi vowel sign uu\n73104 gunjala gondi vowel sign ee\n73105 gunjala gondi vowel sign ai\n73107 gunjala gondi vowel sign oo\n73108 gunjala gondi vowel sign au\n73109 gunjala gondi sign anusvara\n73110 gunjala gondi sign visarga\n73111 gunjala gondi virama\n73112 gunjala gondi om\n73120 gunjala gondi digit zero\n73121 gunjala gondi digit one\n73122 gunjala gondi digit two\n73123 gunjala gondi digit three\n73124 gunjala gondi digit four\n73125 gunjala gondi digit five\n73126 gunjala gondi digit six\n73127 gunjala gondi digit seven\n73128 gunjala gondi digit eight\n73129 gunjala gondi digit nine\n73136 tolong siki letter i\n73137 tolong siki letter e\n73138 tolong siki letter u\n73139 tolong siki letter o\n73140 tolong siki letter a\n73141 tolong siki letter aa\n73142 tolong siki letter p\n73143 tolong siki letter ph\n73144 tolong siki letter b\n73145 tolong siki letter bh\n73146 tolong siki letter m\n73147 tolong siki letter t\n73148 tolong siki letter th\n73149 tolong siki letter d\n73150 tolong siki letter dh\n73151 tolong siki letter n\n73152 tolong siki letter tt\n73153 tolong siki letter tth\n73154 tolong siki letter dd\n73155 tolong siki letter ddh\n73156 tolong siki letter nn\n73157 tolong siki letter c\n73158 tolong siki letter ch\n73159 tolong siki letter j\n73160 tolong siki letter jh\n73161 tolong siki letter ny\n73162 tolong siki letter k\n73163 tolong siki letter kh\n73164 tolong siki letter g\n73165 tolong siki letter gh\n73166 tolong siki letter ng\n73167 tolong siki letter y\n73168 tolong siki letter r\n73169 tolong siki letter l\n73170 tolong siki letter v\n73171 tolong siki letter nny\n73172 tolong siki letter s\n73173 tolong siki letter h\n73174 tolong siki letter x\n73175 tolong siki letter rr\n73176 tolong siki letter rrh\n73177 tolong siki sign sela\n73178 tolong siki sign hecaka\n73179 tolong siki ungga\n73184 tolong siki digit zero\n73185 tolong siki digit one\n73186 tolong siki digit two\n73187 tolong siki digit three\n73188 tolong siki digit four\n73189 tolong siki digit five\n73190 tolong siki digit six\n73191 tolong siki digit seven\n73192 tolong siki digit eight\n73193 tolong siki digit nine\n73440 makasar letter ka\n73441 makasar letter ga\n73442 makasar letter nga\n73443 makasar letter pa\n73444 makasar letter ba\n73445 makasar letter ma\n73446 makasar letter ta\n73447 makasar letter da\n73448 makasar letter na\n73449 makasar letter ca\n73450 makasar letter ja\n73451 makasar letter nya\n73452 makasar letter ya\n73453 makasar letter ra\n73454 makasar letter la\n73455 makasar letter va\n73456 makasar letter sa\n73457 makasar letter a\n73458 makasar angka\n73459 makasar vowel sign i\n73460 makasar vowel sign u\n73461 makasar vowel sign e\n73462 makasar vowel sign o\n73463 makasar passimbang\n73464 makasar end of section\n73472 kawi sign candrabindu\n73473 kawi sign anusvara\n73474 kawi sign repha\n73475 kawi sign visarga\n73476 kawi letter a\n73477 kawi letter aa\n73478 kawi letter i\n73479 kawi letter ii\n73480 kawi letter u\n73481 kawi letter uu\n73482 kawi letter vocalic r\n73483 kawi letter vocalic rr\n73484 kawi letter vocalic l\n73485 kawi letter vocalic ll\n73486 kawi letter e\n73487 kawi letter ai\n73488 kawi letter o\n73490 kawi letter ka\n73491 kawi letter kha\n73492 kawi letter ga\n73493 kawi letter gha\n73494 kawi letter nga\n73495 kawi letter ca\n73496 kawi letter cha\n73497 kawi letter ja\n73498 kawi letter jha\n73499 kawi letter nya\n73500 kawi letter tta\n73501 kawi letter ttha\n73502 kawi letter dda\n73503 kawi letter ddha\n73504 kawi letter nna\n73505 kawi letter ta\n73506 kawi letter tha\n73507 kawi letter da\n73508 kawi letter dha\n73509 kawi letter na\n73510 kawi letter pa\n73511 kawi letter pha\n73512 kawi letter ba\n73513 kawi letter bha\n73514 kawi letter ma\n73515 kawi letter ya\n73516 kawi letter ra\n73517 kawi letter la\n73518 kawi letter wa\n73519 kawi letter sha\n73520 kawi letter ssa\n73521 kawi letter sa\n73522 kawi letter ha\n73523 kawi letter jnya\n73524 kawi vowel sign aa\n73525 kawi vowel sign alternate aa\n73526 kawi vowel sign i\n73527 kawi vowel sign ii\n73528 kawi vowel sign u\n73529 kawi vowel sign uu\n73530 kawi vowel sign vocalic r\n73534 kawi vowel sign e\n73535 kawi vowel sign ai\n73536 kawi vowel sign eu\n73537 kawi sign killer\n73538 kawi conjoiner\n73539 kawi danda\n73540 kawi double danda\n73541 kawi punctuation section marker\n73542 kawi punctuation alternate section marker\n73543 kawi punctuation flower\n73544 kawi punctuation space filler\n73545 kawi punctuation dot\n73546 kawi punctuation double dot\n73547 kawi punctuation triple dot\n73548 kawi punctuation circle\n73549 kawi punctuation filled circle\n73550 kawi punctuation spiral\n73551 kawi punctuation closing spiral\n73552 kawi digit zero\n73553 kawi digit one\n73554 kawi digit two\n73555 kawi digit three\n73556 kawi digit four\n73557 kawi digit five\n73558 kawi digit six\n73559 kawi digit seven\n73560 kawi digit eight\n73561 kawi digit nine\n73562 kawi sign nukta\n73648 lisu letter yha\n73664 tamil fraction one three-hundred-and-twentieth\n73665 tamil fraction one one-hundred-and-sixtieth\n73666 tamil fraction one eightieth\n73667 tamil fraction one sixty-fourth\n73668 tamil fraction one fortieth\n73669 tamil fraction one thirty-second\n73670 tamil fraction three eightieths\n73671 tamil fraction three sixty-fourths\n73672 tamil fraction one twentieth\n73673 tamil fraction one sixteenth-1\n73674 tamil fraction one sixteenth-2\n73675 tamil fraction one tenth\n73676 tamil fraction one eighth\n73677 tamil fraction three twentieths\n73678 tamil fraction three sixteenths\n73679 tamil fraction one fifth\n73680 tamil fraction one quarter\n73681 tamil fraction one half-1\n73682 tamil fraction one half-2\n73683 tamil fraction three quarters\n73684 tamil fraction downscaling factor kiizh\n73685 tamil sign nel\n73686 tamil sign cevitu\n73687 tamil sign aazhaakku\n73688 tamil sign uzhakku\n73689 tamil sign muuvuzhakku\n73690 tamil sign kuruni\n73691 tamil sign pathakku\n73692 tamil sign mukkuruni\n73693 tamil sign kaacu\n73694 tamil sign panam\n73695 tamil sign pon\n73696 tamil sign varaakan\n73697 tamil sign paaram\n73698 tamil sign kuzhi\n73699 tamil sign veli\n73700 tamil wet cultivation sign\n73701 tamil dry cultivation sign\n73702 tamil land sign\n73703 tamil salt pan sign\n73704 tamil traditional credit sign\n73705 tamil traditional number sign\n73706 tamil current sign\n73707 tamil and odd sign\n73708 tamil spent sign\n73709 tamil total sign\n73710 tamil in possession sign\n73711 tamil starting from sign\n73712 tamil sign muthaliya\n73713 tamil sign vakaiyaraa\n73727 tamil punctuation end of text\n73728 cuneiform sign a\n73729 cuneiform sign a times a\n73730 cuneiform sign a times bad\n73731 cuneiform sign a times gan2 tenu\n73732 cuneiform sign a times ha\n73733 cuneiform sign a times igi\n73734 cuneiform sign a times lagar gunu\n73735 cuneiform sign a times mush\n73736 cuneiform sign a times sag\n73737 cuneiform sign a2\n73738 cuneiform sign ab\n73739 cuneiform sign ab times ash2\n73740 cuneiform sign ab times dun3 gunu\n73741 cuneiform sign ab times gal\n73742 cuneiform sign ab times gan2 tenu\n73743 cuneiform sign ab times ha\n73744 cuneiform sign ab times igi gunu\n73745 cuneiform sign ab times imin\n73746 cuneiform sign ab times lagab\n73747 cuneiform sign ab times shesh\n73748 cuneiform sign ab times u plus u plus u\n73749 cuneiform sign ab gunu\n73750 cuneiform sign ab2\n73751 cuneiform sign ab2 times balag\n73752 cuneiform sign ab2 times gan2 tenu\n73753 cuneiform sign ab2 times me plus en\n73754 cuneiform sign ab2 times sha3\n73755 cuneiform sign ab2 times tak4\n73756 cuneiform sign ad\n73757 cuneiform sign ak\n73758 cuneiform sign ak times erin2\n73759 cuneiform sign ak times shita plus gish\n73760 cuneiform sign al\n73761 cuneiform sign al times al\n73762 cuneiform sign al times dim2\n73763 cuneiform sign al times gish\n73764 cuneiform sign al times ha\n73765 cuneiform sign al times kad3\n73766 cuneiform sign al times ki\n73767 cuneiform sign al times she\n73768 cuneiform sign al times ush\n73769 cuneiform sign alan\n73770 cuneiform sign aleph\n73771 cuneiform sign amar\n73772 cuneiform sign amar times she\n73773 cuneiform sign an\n73774 cuneiform sign an over an\n73775 cuneiform sign an three times\n73776 cuneiform sign an plus naga opposing an plus naga\n73777 cuneiform sign an plus naga squared\n73778 cuneiform sign anshe\n73779 cuneiform sign apin\n73780 cuneiform sign arad\n73781 cuneiform sign arad times kur\n73782 cuneiform sign arkab\n73783 cuneiform sign asal2\n73784 cuneiform sign ash\n73785 cuneiform sign ash zida tenu\n73786 cuneiform sign ash kaba tenu\n73787 cuneiform sign ash over ash tug2 over tug2 tug2 over tug2 pap\n73788 cuneiform sign ash over ash over ash\n73789 cuneiform sign ash over ash over ash crossing ash over ash over ash\n73790 cuneiform sign ash2\n73791 cuneiform sign ashgab\n73792 cuneiform sign ba\n73793 cuneiform sign bad\n73794 cuneiform sign bag3\n73795 cuneiform sign bahar2\n73796 cuneiform sign bal\n73797 cuneiform sign bal over bal\n73798 cuneiform sign balag\n73799 cuneiform sign bar\n73800 cuneiform sign bara2\n73801 cuneiform sign bi\n73802 cuneiform sign bi times a\n73803 cuneiform sign bi times gar\n73804 cuneiform sign bi times igi gunu\n73805 cuneiform sign bu\n73806 cuneiform sign bu over bu ab\n73807 cuneiform sign bu over bu un\n73808 cuneiform sign bu crossing bu\n73809 cuneiform sign bulug\n73810 cuneiform sign bulug over bulug\n73811 cuneiform sign bur\n73812 cuneiform sign bur2\n73813 cuneiform sign da\n73814 cuneiform sign dag\n73815 cuneiform sign dag kisim5 times a plus mash\n73816 cuneiform sign dag kisim5 times amar\n73817 cuneiform sign dag kisim5 times balag\n73818 cuneiform sign dag kisim5 times bi\n73819 cuneiform sign dag kisim5 times ga\n73820 cuneiform sign dag kisim5 times ga plus mash\n73821 cuneiform sign dag kisim5 times gi\n73822 cuneiform sign dag kisim5 times gir2\n73823 cuneiform sign dag kisim5 times gud\n73824 cuneiform sign dag kisim5 times ha\n73825 cuneiform sign dag kisim5 times ir\n73826 cuneiform sign dag kisim5 times ir plus lu\n73827 cuneiform sign dag kisim5 times kak\n73828 cuneiform sign dag kisim5 times la\n73829 cuneiform sign dag kisim5 times lu\n73830 cuneiform sign dag kisim5 times lu plus mash2\n73831 cuneiform sign dag kisim5 times lum\n73832 cuneiform sign dag kisim5 times ne\n73833 cuneiform sign dag kisim5 times pap plus pap\n73834 cuneiform sign dag kisim5 times si\n73835 cuneiform sign dag kisim5 times tak4\n73836 cuneiform sign dag kisim5 times u2 plus gir2\n73837 cuneiform sign dag kisim5 times ush\n73838 cuneiform sign dam\n73839 cuneiform sign dar\n73840 cuneiform sign dara3\n73841 cuneiform sign dara4\n73842 cuneiform sign di\n73843 cuneiform sign dib\n73844 cuneiform sign dim\n73845 cuneiform sign dim times she\n73846 cuneiform sign dim2\n73847 cuneiform sign din\n73848 cuneiform sign din kaskal u gunu dish\n73849 cuneiform sign dish\n73850 cuneiform sign du\n73851 cuneiform sign du over du\n73852 cuneiform sign du gunu\n73853 cuneiform sign du sheshig\n73854 cuneiform sign dub\n73855 cuneiform sign dub times esh2\n73856 cuneiform sign dub2\n73857 cuneiform sign dug\n73858 cuneiform sign dugud\n73859 cuneiform sign duh\n73860 cuneiform sign dun\n73861 cuneiform sign dun3\n73862 cuneiform sign dun3 gunu\n73863 cuneiform sign dun3 gunu gunu\n73864 cuneiform sign dun4\n73865 cuneiform sign dur2\n73866 cuneiform sign e\n73867 cuneiform sign e times pap\n73868 cuneiform sign e over e nun over nun\n73869 cuneiform sign e2\n73870 cuneiform sign e2 times a plus ha plus da\n73871 cuneiform sign e2 times gar\n73872 cuneiform sign e2 times mi\n73873 cuneiform sign e2 times sal\n73874 cuneiform sign e2 times she\n73875 cuneiform sign e2 times u\n73876 cuneiform sign edin\n73877 cuneiform sign egir\n73878 cuneiform sign el\n73879 cuneiform sign en\n73880 cuneiform sign en times gan2\n73881 cuneiform sign en times gan2 tenu\n73882 cuneiform sign en times me\n73883 cuneiform sign en crossing en\n73884 cuneiform sign en opposing en\n73885 cuneiform sign en squared\n73886 cuneiform sign eren\n73887 cuneiform sign erin2\n73888 cuneiform sign esh2\n73889 cuneiform sign ezen\n73890 cuneiform sign ezen times a\n73891 cuneiform sign ezen times a plus lal\n73892 cuneiform sign ezen times a plus lal times lal\n73893 cuneiform sign ezen times an\n73894 cuneiform sign ezen times bad\n73895 cuneiform sign ezen times dun3 gunu\n73896 cuneiform sign ezen times dun3 gunu gunu\n73897 cuneiform sign ezen times ha\n73898 cuneiform sign ezen times ha gunu\n73899 cuneiform sign ezen times igi gunu\n73900 cuneiform sign ezen times kaskal\n73901 cuneiform sign ezen times kaskal squared\n73902 cuneiform sign ezen times ku3\n73903 cuneiform sign ezen times la\n73904 cuneiform sign ezen times lal times lal\n73905 cuneiform sign ezen times li\n73906 cuneiform sign ezen times lu\n73907 cuneiform sign ezen times u2\n73908 cuneiform sign ezen times ud\n73909 cuneiform sign ga\n73910 cuneiform sign ga gunu\n73911 cuneiform sign ga2\n73912 cuneiform sign ga2 times a plus da plus ha\n73913 cuneiform sign ga2 times a plus ha\n73914 cuneiform sign ga2 times a plus igi\n73915 cuneiform sign ga2 times ab2 tenu plus tab\n73916 cuneiform sign ga2 times an\n73917 cuneiform sign ga2 times ash\n73918 cuneiform sign ga2 times ash2 plus gal\n73919 cuneiform sign ga2 times bad\n73920 cuneiform sign ga2 times bar plus ra\n73921 cuneiform sign ga2 times bur\n73922 cuneiform sign ga2 times bur plus ra\n73923 cuneiform sign ga2 times da\n73924 cuneiform sign ga2 times di\n73925 cuneiform sign ga2 times dim times she\n73926 cuneiform sign ga2 times dub\n73927 cuneiform sign ga2 times el\n73928 cuneiform sign ga2 times el plus la\n73929 cuneiform sign ga2 times en\n73930 cuneiform sign ga2 times en times gan2 tenu\n73931 cuneiform sign ga2 times gan2 tenu\n73932 cuneiform sign ga2 times gar\n73933 cuneiform sign ga2 times gi\n73934 cuneiform sign ga2 times gi4\n73935 cuneiform sign ga2 times gi4 plus a\n73936 cuneiform sign ga2 times gir2 plus su\n73937 cuneiform sign ga2 times ha plus lu plus esh2\n73938 cuneiform sign ga2 times hal\n73939 cuneiform sign ga2 times hal plus la\n73940 cuneiform sign ga2 times hi plus li\n73941 cuneiform sign ga2 times hub2\n73942 cuneiform sign ga2 times igi gunu\n73943 cuneiform sign ga2 times ish plus hu plus ash\n73944 cuneiform sign ga2 times kak\n73945 cuneiform sign ga2 times kaskal\n73946 cuneiform sign ga2 times kid\n73947 cuneiform sign ga2 times kid plus lal\n73948 cuneiform sign ga2 times ku3 plus an\n73949 cuneiform sign ga2 times la\n73950 cuneiform sign ga2 times me plus en\n73951 cuneiform sign ga2 times mi\n73952 cuneiform sign ga2 times nun\n73953 cuneiform sign ga2 times nun over nun\n73954 cuneiform sign ga2 times pa\n73955 cuneiform sign ga2 times sal\n73956 cuneiform sign ga2 times sar\n73957 cuneiform sign ga2 times she\n73958 cuneiform sign ga2 times she plus tur\n73959 cuneiform sign ga2 times shid\n73960 cuneiform sign ga2 times sum\n73961 cuneiform sign ga2 times tak4\n73962 cuneiform sign ga2 times u\n73963 cuneiform sign ga2 times ud\n73964 cuneiform sign ga2 times ud plus du\n73965 cuneiform sign ga2 over ga2\n73966 cuneiform sign gaba\n73967 cuneiform sign gaba crossing gaba\n73968 cuneiform sign gad\n73969 cuneiform sign gad over gad gar over gar\n73970 cuneiform sign gal\n73971 cuneiform sign gal gad over gad gar over gar\n73972 cuneiform sign galam\n73973 cuneiform sign gam\n73974 cuneiform sign gan\n73975 cuneiform sign gan2\n73976 cuneiform sign gan2 tenu\n73977 cuneiform sign gan2 over gan2\n73978 cuneiform sign gan2 crossing gan2\n73979 cuneiform sign gar\n73980 cuneiform sign gar3\n73981 cuneiform sign gashan\n73982 cuneiform sign geshtin\n73983 cuneiform sign geshtin times kur\n73984 cuneiform sign gi\n73985 cuneiform sign gi times e\n73986 cuneiform sign gi times u\n73987 cuneiform sign gi crossing gi\n73988 cuneiform sign gi4\n73989 cuneiform sign gi4 over gi4\n73990 cuneiform sign gi4 crossing gi4\n73991 cuneiform sign gidim\n73992 cuneiform sign gir2\n73993 cuneiform sign gir2 gunu\n73994 cuneiform sign gir3\n73995 cuneiform sign gir3 times a plus igi\n73996 cuneiform sign gir3 times gan2 tenu\n73997 cuneiform sign gir3 times igi\n73998 cuneiform sign gir3 times lu plus igi\n73999 cuneiform sign gir3 times pa\n74000 cuneiform sign gisal\n74001 cuneiform sign gish\n74002 cuneiform sign gish crossing gish\n74003 cuneiform sign gish times bad\n74004 cuneiform sign gish times tak4\n74005 cuneiform sign gish tenu\n74006 cuneiform sign gu\n74007 cuneiform sign gu crossing gu\n74008 cuneiform sign gu2\n74009 cuneiform sign gu2 times kak\n74010 cuneiform sign gu2 times kak times igi gunu\n74011 cuneiform sign gu2 times nun\n74012 cuneiform sign gu2 times sal plus tug2\n74013 cuneiform sign gu2 gunu\n74014 cuneiform sign gud\n74015 cuneiform sign gud times a plus kur\n74016 cuneiform sign gud times kur\n74017 cuneiform sign gud over gud lugal\n74018 cuneiform sign gul\n74019 cuneiform sign gum\n74020 cuneiform sign gum times she\n74021 cuneiform sign gur\n74022 cuneiform sign gur7\n74023 cuneiform sign gurun\n74024 cuneiform sign gurush\n74025 cuneiform sign ha\n74026 cuneiform sign ha tenu\n74027 cuneiform sign ha gunu\n74028 cuneiform sign hal\n74029 cuneiform sign hi\n74030 cuneiform sign hi times ash\n74031 cuneiform sign hi times ash2\n74032 cuneiform sign hi times bad\n74033 cuneiform sign hi times dish\n74034 cuneiform sign hi times gad\n74035 cuneiform sign hi times kin\n74036 cuneiform sign hi times nun\n74037 cuneiform sign hi times she\n74038 cuneiform sign hi times u\n74039 cuneiform sign hu\n74040 cuneiform sign hub2\n74041 cuneiform sign hub2 times an\n74042 cuneiform sign hub2 times hal\n74043 cuneiform sign hub2 times kaskal\n74044 cuneiform sign hub2 times lish\n74045 cuneiform sign hub2 times ud\n74046 cuneiform sign hul2\n74047 cuneiform sign i\n74048 cuneiform sign i a\n74049 cuneiform sign ib\n74050 cuneiform sign idim\n74051 cuneiform sign idim over idim bur\n74052 cuneiform sign idim over idim squared\n74053 cuneiform sign ig\n74054 cuneiform sign igi\n74055 cuneiform sign igi dib\n74056 cuneiform sign igi ri\n74057 cuneiform sign igi over igi shir over shir ud over ud\n74058 cuneiform sign igi gunu\n74059 cuneiform sign il\n74060 cuneiform sign il times gan2 tenu\n74061 cuneiform sign il2\n74062 cuneiform sign im\n74063 cuneiform sign im times tak4\n74064 cuneiform sign im crossing im\n74065 cuneiform sign im opposing im\n74066 cuneiform sign im squared\n74067 cuneiform sign imin\n74068 cuneiform sign in\n74069 cuneiform sign ir\n74070 cuneiform sign ish\n74071 cuneiform sign ka\n74072 cuneiform sign ka times a\n74073 cuneiform sign ka times ad\n74074 cuneiform sign ka times ad plus ku3\n74075 cuneiform sign ka times ash2\n74076 cuneiform sign ka times bad\n74077 cuneiform sign ka times balag\n74078 cuneiform sign ka times bar\n74079 cuneiform sign ka times bi\n74080 cuneiform sign ka times erin2\n74081 cuneiform sign ka times esh2\n74082 cuneiform sign ka times ga\n74083 cuneiform sign ka times gal\n74084 cuneiform sign ka times gan2 tenu\n74085 cuneiform sign ka times gar\n74086 cuneiform sign ka times gar plus sha3 plus a\n74087 cuneiform sign ka times gi\n74088 cuneiform sign ka times gir2\n74089 cuneiform sign ka times gish plus sar\n74090 cuneiform sign ka times gish crossing gish\n74091 cuneiform sign ka times gu\n74092 cuneiform sign ka times gur7\n74093 cuneiform sign ka times igi\n74094 cuneiform sign ka times im\n74095 cuneiform sign ka times kak\n74096 cuneiform sign ka times ki\n74097 cuneiform sign ka times kid\n74098 cuneiform sign ka times li\n74099 cuneiform sign ka times lu\n74100 cuneiform sign ka times me\n74101 cuneiform sign ka times me plus du\n74102 cuneiform sign ka times me plus gi\n74103 cuneiform sign ka times me plus te\n74104 cuneiform sign ka times mi\n74105 cuneiform sign ka times mi plus nunuz\n74106 cuneiform sign ka times ne\n74107 cuneiform sign ka times nun\n74108 cuneiform sign ka times pi\n74109 cuneiform sign ka times ru\n74110 cuneiform sign ka times sa\n74111 cuneiform sign ka times sar\n74112 cuneiform sign ka times sha\n74113 cuneiform sign ka times she\n74114 cuneiform sign ka times shid\n74115 cuneiform sign ka times shu\n74116 cuneiform sign ka times sig\n74117 cuneiform sign ka times suhur\n74118 cuneiform sign ka times tar\n74119 cuneiform sign ka times u\n74120 cuneiform sign ka times u2\n74121 cuneiform sign ka times ud\n74122 cuneiform sign ka times umum times pa\n74123 cuneiform sign ka times ush\n74124 cuneiform sign ka times zi\n74125 cuneiform sign ka2\n74126 cuneiform sign ka2 crossing ka2\n74127 cuneiform sign kab\n74128 cuneiform sign kad2\n74129 cuneiform sign kad3\n74130 cuneiform sign kad4\n74131 cuneiform sign kad5\n74132 cuneiform sign kad5 over kad5\n74133 cuneiform sign kak\n74134 cuneiform sign kak times igi gunu\n74135 cuneiform sign kal\n74136 cuneiform sign kal times bad\n74137 cuneiform sign kal crossing kal\n74138 cuneiform sign kam2\n74139 cuneiform sign kam4\n74140 cuneiform sign kaskal\n74141 cuneiform sign kaskal lagab times u over lagab times u\n74142 cuneiform sign kaskal over kaskal lagab times u over lagab times u\n74143 cuneiform sign kesh2\n74144 cuneiform sign ki\n74145 cuneiform sign ki times bad\n74146 cuneiform sign ki times u\n74147 cuneiform sign ki times ud\n74148 cuneiform sign kid\n74149 cuneiform sign kin\n74150 cuneiform sign kisal\n74151 cuneiform sign kish\n74152 cuneiform sign kisim5\n74153 cuneiform sign kisim5 over kisim5\n74154 cuneiform sign ku\n74155 cuneiform sign ku over hi times ash2 ku over hi times ash2\n74156 cuneiform sign ku3\n74157 cuneiform sign ku4\n74158 cuneiform sign ku4 variant form\n74159 cuneiform sign ku7\n74160 cuneiform sign kul\n74161 cuneiform sign kul gunu\n74162 cuneiform sign kun\n74163 cuneiform sign kur\n74164 cuneiform sign kur opposing kur\n74165 cuneiform sign kushu2\n74166 cuneiform sign kwu318\n74167 cuneiform sign la\n74168 cuneiform sign lagab\n74169 cuneiform sign lagab times a\n74170 cuneiform sign lagab times a plus da plus ha\n74171 cuneiform sign lagab times a plus gar\n74172 cuneiform sign lagab times a plus lal\n74173 cuneiform sign lagab times al\n74174 cuneiform sign lagab times an\n74175 cuneiform sign lagab times ash zida tenu\n74176 cuneiform sign lagab times bad\n74177 cuneiform sign lagab times bi\n74178 cuneiform sign lagab times dar\n74179 cuneiform sign lagab times en\n74180 cuneiform sign lagab times ga\n74181 cuneiform sign lagab times gar\n74182 cuneiform sign lagab times gud\n74183 cuneiform sign lagab times gud plus gud\n74184 cuneiform sign lagab times ha\n74185 cuneiform sign lagab times hal\n74186 cuneiform sign lagab times hi times nun\n74187 cuneiform sign lagab times igi gunu\n74188 cuneiform sign lagab times im\n74189 cuneiform sign lagab times im plus ha\n74190 cuneiform sign lagab times im plus lu\n74191 cuneiform sign lagab times ki\n74192 cuneiform sign lagab times kin\n74193 cuneiform sign lagab times ku3\n74194 cuneiform sign lagab times kul\n74195 cuneiform sign lagab times kul plus hi plus a\n74196 cuneiform sign lagab times lagab\n74197 cuneiform sign lagab times lish\n74198 cuneiform sign lagab times lu\n74199 cuneiform sign lagab times lul\n74200 cuneiform sign lagab times me\n74201 cuneiform sign lagab times me plus en\n74202 cuneiform sign lagab times mush\n74203 cuneiform sign lagab times ne\n74204 cuneiform sign lagab times she plus sum\n74205 cuneiform sign lagab times shita plus gish plus erin2\n74206 cuneiform sign lagab times shita plus gish tenu\n74207 cuneiform sign lagab times shu2\n74208 cuneiform sign lagab times shu2 plus shu2\n74209 cuneiform sign lagab times sum\n74210 cuneiform sign lagab times tag\n74211 cuneiform sign lagab times tak4\n74212 cuneiform sign lagab times te plus a plus su plus na\n74213 cuneiform sign lagab times u\n74214 cuneiform sign lagab times u plus a\n74215 cuneiform sign lagab times u plus u plus u\n74216 cuneiform sign lagab times u2 plus ash\n74217 cuneiform sign lagab times ud\n74218 cuneiform sign lagab times ush\n74219 cuneiform sign lagab squared\n74220 cuneiform sign lagar\n74221 cuneiform sign lagar times she\n74222 cuneiform sign lagar times she plus sum\n74223 cuneiform sign lagar gunu\n74224 cuneiform sign lagar gunu over lagar gunu she\n74225 cuneiform sign lahshu\n74226 cuneiform sign lal\n74227 cuneiform sign lal times lal\n74228 cuneiform sign lam\n74229 cuneiform sign lam times kur\n74230 cuneiform sign lam times kur plus ru\n74231 cuneiform sign li\n74232 cuneiform sign lil\n74233 cuneiform sign limmu2\n74234 cuneiform sign lish\n74235 cuneiform sign lu\n74236 cuneiform sign lu times bad\n74237 cuneiform sign lu2\n74238 cuneiform sign lu2 times al\n74239 cuneiform sign lu2 times bad\n74240 cuneiform sign lu2 times esh2\n74241 cuneiform sign lu2 times esh2 tenu\n74242 cuneiform sign lu2 times gan2 tenu\n74243 cuneiform sign lu2 times hi times bad\n74244 cuneiform sign lu2 times im\n74245 cuneiform sign lu2 times kad2\n74246 cuneiform sign lu2 times kad3\n74247 cuneiform sign lu2 times kad3 plus ash\n74248 cuneiform sign lu2 times ki\n74249 cuneiform sign lu2 times la plus ash\n74250 cuneiform sign lu2 times lagab\n74251 cuneiform sign lu2 times me plus en\n74252 cuneiform sign lu2 times ne\n74253 cuneiform sign lu2 times nu\n74254 cuneiform sign lu2 times si plus ash\n74255 cuneiform sign lu2 times sik2 plus bu\n74256 cuneiform sign lu2 times tug2\n74257 cuneiform sign lu2 tenu\n74258 cuneiform sign lu2 crossing lu2\n74259 cuneiform sign lu2 opposing lu2\n74260 cuneiform sign lu2 squared\n74261 cuneiform sign lu2 sheshig\n74262 cuneiform sign lu3\n74263 cuneiform sign lugal\n74264 cuneiform sign lugal over lugal\n74265 cuneiform sign lugal opposing lugal\n74266 cuneiform sign lugal sheshig\n74267 cuneiform sign luh\n74268 cuneiform sign lul\n74269 cuneiform sign lum\n74270 cuneiform sign lum over lum\n74271 cuneiform sign lum over lum gar over gar\n74272 cuneiform sign ma\n74273 cuneiform sign ma times tak4\n74274 cuneiform sign ma gunu\n74275 cuneiform sign ma2\n74276 cuneiform sign mah\n74277 cuneiform sign mar\n74278 cuneiform sign mash\n74279 cuneiform sign mash2\n74280 cuneiform sign me\n74281 cuneiform sign mes\n74282 cuneiform sign mi\n74283 cuneiform sign min\n74284 cuneiform sign mu\n74285 cuneiform sign mu over mu\n74286 cuneiform sign mug\n74287 cuneiform sign mug gunu\n74288 cuneiform sign munsub\n74289 cuneiform sign murgu2\n74290 cuneiform sign mush\n74291 cuneiform sign mush times a\n74292 cuneiform sign mush times kur\n74293 cuneiform sign mush times za\n74294 cuneiform sign mush over mush\n74295 cuneiform sign mush over mush times a plus na\n74296 cuneiform sign mush crossing mush\n74297 cuneiform sign mush3\n74298 cuneiform sign mush3 times a\n74299 cuneiform sign mush3 times a plus di\n74300 cuneiform sign mush3 times di\n74301 cuneiform sign mush3 gunu\n74302 cuneiform sign na\n74303 cuneiform sign na2\n74304 cuneiform sign naga\n74305 cuneiform sign naga inverted\n74306 cuneiform sign naga times shu tenu\n74307 cuneiform sign naga opposing naga\n74308 cuneiform sign nagar\n74309 cuneiform sign nam nutillu\n74310 cuneiform sign nam\n74311 cuneiform sign nam2\n74312 cuneiform sign ne\n74313 cuneiform sign ne times a\n74314 cuneiform sign ne times ud\n74315 cuneiform sign ne sheshig\n74316 cuneiform sign ni\n74317 cuneiform sign ni times e\n74318 cuneiform sign ni2\n74319 cuneiform sign nim\n74320 cuneiform sign nim times gan2 tenu\n74321 cuneiform sign nim times gar plus gan2 tenu\n74322 cuneiform sign ninda2\n74323 cuneiform sign ninda2 times an\n74324 cuneiform sign ninda2 times ash\n74325 cuneiform sign ninda2 times ash plus ash\n74326 cuneiform sign ninda2 times gud\n74327 cuneiform sign ninda2 times me plus gan2 tenu\n74328 cuneiform sign ninda2 times ne\n74329 cuneiform sign ninda2 times nun\n74330 cuneiform sign ninda2 times she\n74331 cuneiform sign ninda2 times she plus a an\n74332 cuneiform sign ninda2 times she plus ash\n74333 cuneiform sign ninda2 times she plus ash plus ash\n74334 cuneiform sign ninda2 times u2 plus ash\n74335 cuneiform sign ninda2 times ush\n74336 cuneiform sign nisag\n74337 cuneiform sign nu\n74338 cuneiform sign nu11\n74339 cuneiform sign nun\n74340 cuneiform sign nun lagar times gar\n74341 cuneiform sign nun lagar times mash\n74342 cuneiform sign nun lagar times sal\n74343 cuneiform sign nun lagar times sal over nun lagar times sal\n74344 cuneiform sign nun lagar times ush\n74345 cuneiform sign nun tenu\n74346 cuneiform sign nun over nun\n74347 cuneiform sign nun crossing nun\n74348 cuneiform sign nun crossing nun lagar over lagar\n74349 cuneiform sign nunuz\n74350 cuneiform sign nunuz ab2 times ashgab\n74351 cuneiform sign nunuz ab2 times bi\n74352 cuneiform sign nunuz ab2 times dug\n74353 cuneiform sign nunuz ab2 times gud\n74354 cuneiform sign nunuz ab2 times igi gunu\n74355 cuneiform sign nunuz ab2 times kad3\n74356 cuneiform sign nunuz ab2 times la\n74357 cuneiform sign nunuz ab2 times ne\n74358 cuneiform sign nunuz ab2 times sila3\n74359 cuneiform sign nunuz ab2 times u2\n74360 cuneiform sign nunuz kisim5 times bi\n74361 cuneiform sign nunuz kisim5 times bi u\n74362 cuneiform sign pa\n74363 cuneiform sign pad\n74364 cuneiform sign pan\n74365 cuneiform sign pap\n74366 cuneiform sign pesh2\n74367 cuneiform sign pi\n74368 cuneiform sign pi times a\n74369 cuneiform sign pi times ab\n74370 cuneiform sign pi times bi\n74371 cuneiform sign pi times bu\n74372 cuneiform sign pi times e\n74373 cuneiform sign pi times i\n74374 cuneiform sign pi times ib\n74375 cuneiform sign pi times u\n74376 cuneiform sign pi times u2\n74377 cuneiform sign pi crossing pi\n74378 cuneiform sign pirig\n74379 cuneiform sign pirig times kal\n74380 cuneiform sign pirig times ud\n74381 cuneiform sign pirig times za\n74382 cuneiform sign pirig opposing pirig\n74383 cuneiform sign ra\n74384 cuneiform sign rab\n74385 cuneiform sign ri\n74386 cuneiform sign ru\n74387 cuneiform sign sa\n74388 cuneiform sign sag nutillu\n74389 cuneiform sign sag\n74390 cuneiform sign sag times a\n74391 cuneiform sign sag times du\n74392 cuneiform sign sag times dub\n74393 cuneiform sign sag times ha\n74394 cuneiform sign sag times kak\n74395 cuneiform sign sag times kur\n74396 cuneiform sign sag times lum\n74397 cuneiform sign sag times mi\n74398 cuneiform sign sag times nun\n74399 cuneiform sign sag times sal\n74400 cuneiform sign sag times shid\n74401 cuneiform sign sag times tab\n74402 cuneiform sign sag times u2\n74403 cuneiform sign sag times ub\n74404 cuneiform sign sag times um\n74405 cuneiform sign sag times ur\n74406 cuneiform sign sag times ush\n74407 cuneiform sign sag over sag\n74408 cuneiform sign sag gunu\n74409 cuneiform sign sal\n74410 cuneiform sign sal lagab times ash2\n74411 cuneiform sign sanga2\n74412 cuneiform sign sar\n74413 cuneiform sign sha\n74414 cuneiform sign sha3\n74415 cuneiform sign sha3 times a\n74416 cuneiform sign sha3 times bad\n74417 cuneiform sign sha3 times gish\n74418 cuneiform sign sha3 times ne\n74419 cuneiform sign sha3 times shu2\n74420 cuneiform sign sha3 times tur\n74421 cuneiform sign sha3 times u\n74422 cuneiform sign sha3 times u plus a\n74423 cuneiform sign sha6\n74424 cuneiform sign shab6\n74425 cuneiform sign shar2\n74426 cuneiform sign she\n74427 cuneiform sign she hu\n74428 cuneiform sign she over she gad over gad gar over gar\n74429 cuneiform sign she over she tab over tab gar over gar\n74430 cuneiform sign sheg9\n74431 cuneiform sign shen\n74432 cuneiform sign shesh\n74433 cuneiform sign shesh2\n74434 cuneiform sign sheshlam\n74435 cuneiform sign shid\n74436 cuneiform sign shid times a\n74437 cuneiform sign shid times im\n74438 cuneiform sign shim\n74439 cuneiform sign shim times a\n74440 cuneiform sign shim times bal\n74441 cuneiform sign shim times bulug\n74442 cuneiform sign shim times din\n74443 cuneiform sign shim times gar\n74444 cuneiform sign shim times igi\n74445 cuneiform sign shim times igi gunu\n74446 cuneiform sign shim times kushu2\n74447 cuneiform sign shim times lul\n74448 cuneiform sign shim times mug\n74449 cuneiform sign shim times sal\n74450 cuneiform sign shinig\n74451 cuneiform sign shir\n74452 cuneiform sign shir tenu\n74453 cuneiform sign shir over shir bur over bur\n74454 cuneiform sign shita\n74455 cuneiform sign shu\n74456 cuneiform sign shu over inverted shu\n74457 cuneiform sign shu2\n74458 cuneiform sign shubur\n74459 cuneiform sign si\n74460 cuneiform sign si gunu\n74461 cuneiform sign sig\n74462 cuneiform sign sig4\n74463 cuneiform sign sig4 over sig4 shu2\n74464 cuneiform sign sik2\n74465 cuneiform sign sila3\n74466 cuneiform sign su\n74467 cuneiform sign su over su\n74468 cuneiform sign sud\n74469 cuneiform sign sud2\n74470 cuneiform sign suhur\n74471 cuneiform sign sum\n74472 cuneiform sign sumash\n74473 cuneiform sign sur\n74474 cuneiform sign sur9\n74475 cuneiform sign ta\n74476 cuneiform sign ta asterisk\n74477 cuneiform sign ta times hi\n74478 cuneiform sign ta times mi\n74479 cuneiform sign ta gunu\n74480 cuneiform sign tab\n74481 cuneiform sign tab over tab ni over ni dish over dish\n74482 cuneiform sign tab squared\n74483 cuneiform sign tag\n74484 cuneiform sign tag times bi\n74485 cuneiform sign tag times gud\n74486 cuneiform sign tag times she\n74487 cuneiform sign tag times shu\n74488 cuneiform sign tag times tug2\n74489 cuneiform sign tag times ud\n74490 cuneiform sign tak4\n74491 cuneiform sign tar\n74492 cuneiform sign te\n74493 cuneiform sign te gunu\n74494 cuneiform sign ti\n74495 cuneiform sign ti tenu\n74496 cuneiform sign til\n74497 cuneiform sign tir\n74498 cuneiform sign tir times tak4\n74499 cuneiform sign tir over tir\n74500 cuneiform sign tir over tir gad over gad gar over gar\n74501 cuneiform sign tu\n74502 cuneiform sign tug2\n74503 cuneiform sign tuk\n74504 cuneiform sign tum\n74505 cuneiform sign tur\n74506 cuneiform sign tur over tur za over za\n74507 cuneiform sign u\n74508 cuneiform sign u gud\n74509 cuneiform sign u u u\n74510 cuneiform sign u over u pa over pa gar over gar\n74511 cuneiform sign u over u sur over sur\n74512 cuneiform sign u over u u reversed over u reversed\n74513 cuneiform sign u2\n74514 cuneiform sign ub\n74515 cuneiform sign ud\n74516 cuneiform sign ud kushu2\n74517 cuneiform sign ud times bad\n74518 cuneiform sign ud times mi\n74519 cuneiform sign ud times u plus u plus u\n74520 cuneiform sign ud times u plus u plus u gunu\n74521 cuneiform sign ud gunu\n74522 cuneiform sign ud sheshig\n74523 cuneiform sign ud sheshig times bad\n74524 cuneiform sign udug\n74525 cuneiform sign um\n74526 cuneiform sign um times lagab\n74527 cuneiform sign um times me plus da\n74528 cuneiform sign um times sha3\n74529 cuneiform sign um times u\n74530 cuneiform sign umbin\n74531 cuneiform sign umum\n74532 cuneiform sign umum times kaskal\n74533 cuneiform sign umum times pa\n74534 cuneiform sign un\n74535 cuneiform sign un gunu\n74536 cuneiform sign ur\n74537 cuneiform sign ur crossing ur\n74538 cuneiform sign ur sheshig\n74539 cuneiform sign ur2\n74540 cuneiform sign ur2 times a plus ha\n74541 cuneiform sign ur2 times a plus na\n74542 cuneiform sign ur2 times al\n74543 cuneiform sign ur2 times ha\n74544 cuneiform sign ur2 times nun\n74545 cuneiform sign ur2 times u2\n74546 cuneiform sign ur2 times u2 plus ash\n74547 cuneiform sign ur2 times u2 plus bi\n74548 cuneiform sign ur4\n74549 cuneiform sign uri\n74550 cuneiform sign uri3\n74551 cuneiform sign uru\n74552 cuneiform sign uru times a\n74553 cuneiform sign uru times ashgab\n74554 cuneiform sign uru times bar\n74555 cuneiform sign uru times dun\n74556 cuneiform sign uru times ga\n74557 cuneiform sign uru times gal\n74558 cuneiform sign uru times gan2 tenu\n74559 cuneiform sign uru times gar\n74560 cuneiform sign uru times gu\n74561 cuneiform sign uru times ha\n74562 cuneiform sign uru times igi\n74563 cuneiform sign uru times im\n74564 cuneiform sign uru times ish\n74565 cuneiform sign uru times ki\n74566 cuneiform sign uru times lum\n74567 cuneiform sign uru times min\n74568 cuneiform sign uru times pa\n74569 cuneiform sign uru times she\n74570 cuneiform sign uru times sig4\n74571 cuneiform sign uru times tu\n74572 cuneiform sign uru times u plus gud\n74573 cuneiform sign uru times ud\n74574 cuneiform sign uru times uruda\n74575 cuneiform sign uruda\n74576 cuneiform sign uruda times u\n74577 cuneiform sign ush\n74578 cuneiform sign ush times a\n74579 cuneiform sign ush times ku\n74580 cuneiform sign ush times kur\n74581 cuneiform sign ush times tak4\n74582 cuneiform sign ushx\n74583 cuneiform sign ush2\n74584 cuneiform sign ushumx\n74585 cuneiform sign utuki\n74586 cuneiform sign uz3\n74587 cuneiform sign uz3 times kaskal\n74588 cuneiform sign uzu\n74589 cuneiform sign za\n74590 cuneiform sign za tenu\n74591 cuneiform sign za squared times kur\n74592 cuneiform sign zag\n74593 cuneiform sign zamx\n74594 cuneiform sign ze2\n74595 cuneiform sign zi\n74596 cuneiform sign zi over zi\n74597 cuneiform sign zi3\n74598 cuneiform sign zib\n74599 cuneiform sign zib kaba tenu\n74600 cuneiform sign zig\n74601 cuneiform sign ziz2\n74602 cuneiform sign zu\n74603 cuneiform sign zu5\n74604 cuneiform sign zu5 times a\n74605 cuneiform sign zubur\n74606 cuneiform sign zum\n74607 cuneiform sign kap elamite\n74608 cuneiform sign ab times nun\n74609 cuneiform sign ab2 times a\n74610 cuneiform sign amar times kug\n74611 cuneiform sign dag kisim5 times u2 plus mash\n74612 cuneiform sign dag3\n74613 cuneiform sign dish plus shu\n74614 cuneiform sign dub times she\n74615 cuneiform sign ezen times gud\n74616 cuneiform sign ezen times she\n74617 cuneiform sign ga2 times an plus kak plus a\n74618 cuneiform sign ga2 times ash2\n74619 cuneiform sign ge22\n74620 cuneiform sign gig\n74621 cuneiform sign hush\n74622 cuneiform sign ka times anshe\n74623 cuneiform sign ka times ash3\n74624 cuneiform sign ka times gish\n74625 cuneiform sign ka times gud\n74626 cuneiform sign ka times hi times ash2\n74627 cuneiform sign ka times lum\n74628 cuneiform sign ka times pa\n74629 cuneiform sign ka times shul\n74630 cuneiform sign ka times tu\n74631 cuneiform sign ka times ur2\n74632 cuneiform sign lagab times gi\n74633 cuneiform sign lu2 sheshig times bad\n74634 cuneiform sign lu2 times esh2 plus lal\n74635 cuneiform sign lu2 times shu\n74636 cuneiform sign mesh\n74637 cuneiform sign mush3 times za\n74638 cuneiform sign na4\n74639 cuneiform sign nin\n74640 cuneiform sign nin9\n74641 cuneiform sign ninda2 times bal\n74642 cuneiform sign ninda2 times gi\n74643 cuneiform sign nu11 rotated ninety degrees\n74644 cuneiform sign pesh2 asterisk\n74645 cuneiform sign pir2\n74646 cuneiform sign sag times igi gunu\n74647 cuneiform sign ti2\n74648 cuneiform sign um times me\n74649 cuneiform sign u u\n74752 cuneiform numeric sign two ash\n74753 cuneiform numeric sign three ash\n74754 cuneiform numeric sign four ash\n74755 cuneiform numeric sign five ash\n74756 cuneiform numeric sign six ash\n74757 cuneiform numeric sign seven ash\n74758 cuneiform numeric sign eight ash\n74759 cuneiform numeric sign nine ash\n74760 cuneiform numeric sign three dish\n74761 cuneiform numeric sign four dish\n74762 cuneiform numeric sign five dish\n74763 cuneiform numeric sign six dish\n74764 cuneiform numeric sign seven dish\n74765 cuneiform numeric sign eight dish\n74766 cuneiform numeric sign nine dish\n74767 cuneiform numeric sign four u\n74768 cuneiform numeric sign five u\n74769 cuneiform numeric sign six u\n74770 cuneiform numeric sign seven u\n74771 cuneiform numeric sign eight u\n74772 cuneiform numeric sign nine u\n74773 cuneiform numeric sign one gesh2\n74774 cuneiform numeric sign two gesh2\n74775 cuneiform numeric sign three gesh2\n74776 cuneiform numeric sign four gesh2\n74777 cuneiform numeric sign five gesh2\n74778 cuneiform numeric sign six gesh2\n74779 cuneiform numeric sign seven gesh2\n74780 cuneiform numeric sign eight gesh2\n74781 cuneiform numeric sign nine gesh2\n74782 cuneiform numeric sign one geshu\n74783 cuneiform numeric sign two geshu\n74784 cuneiform numeric sign three geshu\n74785 cuneiform numeric sign four geshu\n74786 cuneiform numeric sign five geshu\n74787 cuneiform numeric sign two shar2\n74788 cuneiform numeric sign three shar2\n74789 cuneiform numeric sign three shar2 variant form\n74790 cuneiform numeric sign four shar2\n74791 cuneiform numeric sign five shar2\n74792 cuneiform numeric sign six shar2\n74793 cuneiform numeric sign seven shar2\n74794 cuneiform numeric sign eight shar2\n74795 cuneiform numeric sign nine shar2\n74796 cuneiform numeric sign one sharu\n74797 cuneiform numeric sign two sharu\n74798 cuneiform numeric sign three sharu\n74799 cuneiform numeric sign three sharu variant form\n74800 cuneiform numeric sign four sharu\n74801 cuneiform numeric sign five sharu\n74802 cuneiform numeric sign shar2 times gal plus dish\n74803 cuneiform numeric sign shar2 times gal plus min\n74804 cuneiform numeric sign one buru\n74805 cuneiform numeric sign two buru\n74806 cuneiform numeric sign three buru\n74807 cuneiform numeric sign three buru variant form\n74808 cuneiform numeric sign four buru\n74809 cuneiform numeric sign five buru\n74810 cuneiform numeric sign three variant form esh16\n74811 cuneiform numeric sign three variant form esh21\n74812 cuneiform numeric sign four variant form limmu\n74813 cuneiform numeric sign four variant form limmu4\n74814 cuneiform numeric sign four variant form limmu a\n74815 cuneiform numeric sign four variant form limmu b\n74816 cuneiform numeric sign six variant form ash9\n74817 cuneiform numeric sign seven variant form imin3\n74818 cuneiform numeric sign seven variant form imin a\n74819 cuneiform numeric sign seven variant form imin b\n74820 cuneiform numeric sign eight variant form ussu\n74821 cuneiform numeric sign eight variant form ussu3\n74822 cuneiform numeric sign nine variant form ilimmu\n74823 cuneiform numeric sign nine variant form ilimmu3\n74824 cuneiform numeric sign nine variant form ilimmu4\n74825 cuneiform numeric sign nine variant form ilimmu a\n74826 cuneiform numeric sign two ash tenu\n74827 cuneiform numeric sign three ash tenu\n74828 cuneiform numeric sign four ash tenu\n74829 cuneiform numeric sign five ash tenu\n74830 cuneiform numeric sign six ash tenu\n74831 cuneiform numeric sign one ban2\n74832 cuneiform numeric sign two ban2\n74833 cuneiform numeric sign three ban2\n74834 cuneiform numeric sign four ban2\n74835 cuneiform numeric sign four ban2 variant form\n74836 cuneiform numeric sign five ban2\n74837 cuneiform numeric sign five ban2 variant form\n74838 cuneiform numeric sign nigidamin\n74839 cuneiform numeric sign nigidaesh\n74840 cuneiform numeric sign one eshe3\n74841 cuneiform numeric sign two eshe3\n74842 cuneiform numeric sign one third dish\n74843 cuneiform numeric sign two thirds dish\n74844 cuneiform numeric sign five sixths dish\n74845 cuneiform numeric sign one third variant form a\n74846 cuneiform numeric sign two thirds variant form a\n74847 cuneiform numeric sign one eighth ash\n74848 cuneiform numeric sign one quarter ash\n74849 cuneiform numeric sign old assyrian one sixth\n74850 cuneiform numeric sign old assyrian one quarter\n74851 cuneiform numeric sign one quarter gur\n74852 cuneiform numeric sign one half gur\n74853 cuneiform numeric sign elamite one third\n74854 cuneiform numeric sign elamite two thirds\n74855 cuneiform numeric sign elamite forty\n74856 cuneiform numeric sign elamite fifty\n74857 cuneiform numeric sign four u variant form\n74858 cuneiform numeric sign five u variant form\n74859 cuneiform numeric sign six u variant form\n74860 cuneiform numeric sign seven u variant form\n74861 cuneiform numeric sign eight u variant form\n74862 cuneiform numeric sign nine u variant form\n74864 cuneiform punctuation sign old assyrian word divider\n74865 cuneiform punctuation sign vertical colon\n74866 cuneiform punctuation sign diagonal colon\n74867 cuneiform punctuation sign diagonal tricolon\n74868 cuneiform punctuation sign diagonal quadcolon\n74880 cuneiform sign ab times nun tenu\n74881 cuneiform sign ab times shu2\n74882 cuneiform sign ad times esh2\n74883 cuneiform sign bad times dish tenu\n74884 cuneiform sign bahar2 times ab2\n74885 cuneiform sign bahar2 times ni\n74886 cuneiform sign bahar2 times za\n74887 cuneiform sign bu over bu times na2\n74888 cuneiform sign da times tak4\n74889 cuneiform sign dag times kur\n74890 cuneiform sign dim times igi\n74891 cuneiform sign dim times u u u\n74892 cuneiform sign dim2 times ud\n74893 cuneiform sign dug times anshe\n74894 cuneiform sign dug times ash\n74895 cuneiform sign dug times ash at left\n74896 cuneiform sign dug times din\n74897 cuneiform sign dug times dun\n74898 cuneiform sign dug times erin2\n74899 cuneiform sign dug times ga\n74900 cuneiform sign dug times gi\n74901 cuneiform sign dug times gir2 gunu\n74902 cuneiform sign dug times gish\n74903 cuneiform sign dug times ha\n74904 cuneiform sign dug times hi\n74905 cuneiform sign dug times igi gunu\n74906 cuneiform sign dug times kaskal\n74907 cuneiform sign dug times kur\n74908 cuneiform sign dug times kushu2\n74909 cuneiform sign dug times kushu2 plus kaskal\n74910 cuneiform sign dug times lak-020\n74911 cuneiform sign dug times lam\n74912 cuneiform sign dug times lam times kur\n74913 cuneiform sign dug times luh plus gish\n74914 cuneiform sign dug times mash\n74915 cuneiform sign dug times mes\n74916 cuneiform sign dug times mi\n74917 cuneiform sign dug times ni\n74918 cuneiform sign dug times pi\n74919 cuneiform sign dug times she\n74920 cuneiform sign dug times si gunu\n74921 cuneiform sign e2 times kur\n74922 cuneiform sign e2 times pap\n74923 cuneiform sign erin2 x\n74924 cuneiform sign esh2 crossing esh2\n74925 cuneiform sign ezen sheshig times ash\n74926 cuneiform sign ezen sheshig times hi\n74927 cuneiform sign ezen sheshig times igi gunu\n74928 cuneiform sign ezen sheshig times la\n74929 cuneiform sign ezen sheshig times lal\n74930 cuneiform sign ezen sheshig times me\n74931 cuneiform sign ezen sheshig times mes\n74932 cuneiform sign ezen sheshig times su\n74933 cuneiform sign ezen times su\n74934 cuneiform sign ga2 times bahar2\n74935 cuneiform sign ga2 times dim gunu\n74936 cuneiform sign ga2 times dug times igi gunu\n74937 cuneiform sign ga2 times dug times kaskal\n74938 cuneiform sign ga2 times eren\n74939 cuneiform sign ga2 times ga\n74940 cuneiform sign ga2 times gar plus di\n74941 cuneiform sign ga2 times gar plus ne\n74942 cuneiform sign ga2 times ha plus a\n74943 cuneiform sign ga2 times kushu2 plus kaskal\n74944 cuneiform sign ga2 times lam\n74945 cuneiform sign ga2 times lam times kur\n74946 cuneiform sign ga2 times luh\n74947 cuneiform sign ga2 times mush\n74948 cuneiform sign ga2 times ne\n74949 cuneiform sign ga2 times ne plus e2\n74950 cuneiform sign ga2 times ne plus gi\n74951 cuneiform sign ga2 times shim\n74952 cuneiform sign ga2 times ziz2\n74953 cuneiform sign gaba rotated ninety degrees\n74954 cuneiform sign geshtin times u\n74955 cuneiform sign gish times gish crossing gish\n74956 cuneiform sign gu2 times igi gunu\n74957 cuneiform sign gud plus gish times tak4\n74958 cuneiform sign ha tenu gunu\n74959 cuneiform sign hi times ash over hi times ash\n74960 cuneiform sign ka times bu\n74961 cuneiform sign ka times ka\n74962 cuneiform sign ka times u u u\n74963 cuneiform sign ka times ur\n74964 cuneiform sign lagab times zu over zu\n74965 cuneiform sign lak-003\n74966 cuneiform sign lak-021\n74967 cuneiform sign lak-025\n74968 cuneiform sign lak-030\n74969 cuneiform sign lak-050\n74970 cuneiform sign lak-051\n74971 cuneiform sign lak-062\n74972 cuneiform sign lak-079 over lak-079 gunu\n74973 cuneiform sign lak-080\n74974 cuneiform sign lak-081 over lak-081\n74975 cuneiform sign lak-092\n74976 cuneiform sign lak-130\n74977 cuneiform sign lak-142\n74978 cuneiform sign lak-210\n74979 cuneiform sign lak-219\n74980 cuneiform sign lak-220\n74981 cuneiform sign lak-225\n74982 cuneiform sign lak-228\n74983 cuneiform sign lak-238\n74984 cuneiform sign lak-265\n74985 cuneiform sign lak-266\n74986 cuneiform sign lak-343\n74987 cuneiform sign lak-347\n74988 cuneiform sign lak-348\n74989 cuneiform sign lak-383\n74990 cuneiform sign lak-384\n74991 cuneiform sign lak-390\n74992 cuneiform sign lak-441\n74993 cuneiform sign lak-449\n74994 cuneiform sign lak-449 times gu\n74995 cuneiform sign lak-449 times igi\n74996 cuneiform sign lak-449 times pap plus lu3\n74997 cuneiform sign lak-449 times pap plus pap plus lu3\n74998 cuneiform sign lak-449 times u2 plus ba\n74999 cuneiform sign lak-450\n75000 cuneiform sign lak-457\n75001 cuneiform sign lak-470\n75002 cuneiform sign lak-483\n75003 cuneiform sign lak-490\n75004 cuneiform sign lak-492\n75005 cuneiform sign lak-493\n75006 cuneiform sign lak-495\n75007 cuneiform sign lak-550\n75008 cuneiform sign lak-608\n75009 cuneiform sign lak-617\n75010 cuneiform sign lak-617 times ash\n75011 cuneiform sign lak-617 times bad\n75012 cuneiform sign lak-617 times dun3 gunu gunu\n75013 cuneiform sign lak-617 times ku3\n75014 cuneiform sign lak-617 times la\n75015 cuneiform sign lak-617 times tar\n75016 cuneiform sign lak-617 times te\n75017 cuneiform sign lak-617 times u2\n75018 cuneiform sign lak-617 times ud\n75019 cuneiform sign lak-617 times uruda\n75020 cuneiform sign lak-636\n75021 cuneiform sign lak-648\n75022 cuneiform sign lak-648 times dub\n75023 cuneiform sign lak-648 times ga\n75024 cuneiform sign lak-648 times igi\n75025 cuneiform sign lak-648 times igi gunu\n75026 cuneiform sign lak-648 times ni\n75027 cuneiform sign lak-648 times pap plus pap plus lu3\n75028 cuneiform sign lak-648 times shesh plus ki\n75029 cuneiform sign lak-648 times ud\n75030 cuneiform sign lak-648 times uruda\n75031 cuneiform sign lak-724\n75032 cuneiform sign lak-749\n75033 cuneiform sign lu2 gunu times ash\n75034 cuneiform sign lu2 times dish\n75035 cuneiform sign lu2 times hal\n75036 cuneiform sign lu2 times pap\n75037 cuneiform sign lu2 times pap plus pap plus lu3\n75038 cuneiform sign lu2 times tak4\n75039 cuneiform sign mi plus za7\n75040 cuneiform sign mush over mush times ga\n75041 cuneiform sign mush over mush times kak\n75042 cuneiform sign ninda2 times dim gunu\n75043 cuneiform sign ninda2 times gish\n75044 cuneiform sign ninda2 times gul\n75045 cuneiform sign ninda2 times hi\n75046 cuneiform sign ninda2 times kesh2\n75047 cuneiform sign ninda2 times lak-050\n75048 cuneiform sign ninda2 times mash\n75049 cuneiform sign ninda2 times pap plus pap\n75050 cuneiform sign ninda2 times u\n75051 cuneiform sign ninda2 times u plus u\n75052 cuneiform sign ninda2 times uruda\n75053 cuneiform sign sag gunu times ha\n75054 cuneiform sign sag times en\n75055 cuneiform sign sag times she at left\n75056 cuneiform sign sag times tak4\n75057 cuneiform sign sha6 tenu\n75058 cuneiform sign she over she\n75059 cuneiform sign she plus hub2\n75060 cuneiform sign she plus nam2\n75061 cuneiform sign she plus sar\n75062 cuneiform sign shu2 plus dug times ni\n75063 cuneiform sign shu2 plus e2 times an\n75064 cuneiform sign si times tak4\n75065 cuneiform sign tak4 plus sag\n75066 cuneiform sign tum times gan2 tenu\n75067 cuneiform sign tum times three dish\n75068 cuneiform sign ur2 inverted\n75069 cuneiform sign ur2 times ud\n75070 cuneiform sign uru times dara3\n75071 cuneiform sign uru times lak-668\n75072 cuneiform sign uru times lu3\n75073 cuneiform sign za7\n75074 cuneiform sign zu over zu plus sar\n75075 cuneiform sign zu5 times three dish tenu\n77712 cypro-minoan sign cm001\n77713 cypro-minoan sign cm002\n77714 cypro-minoan sign cm004\n77715 cypro-minoan sign cm005\n77716 cypro-minoan sign cm006\n77717 cypro-minoan sign cm007\n77718 cypro-minoan sign cm008\n77719 cypro-minoan sign cm009\n77720 cypro-minoan sign cm010\n77721 cypro-minoan sign cm011\n77722 cypro-minoan sign cm012\n77723 cypro-minoan sign cm012b\n77724 cypro-minoan sign cm013\n77725 cypro-minoan sign cm015\n77726 cypro-minoan sign cm017\n77727 cypro-minoan sign cm019\n77728 cypro-minoan sign cm021\n77729 cypro-minoan sign cm023\n77730 cypro-minoan sign cm024\n77731 cypro-minoan sign cm025\n77732 cypro-minoan sign cm026\n77733 cypro-minoan sign cm027\n77734 cypro-minoan sign cm028\n77735 cypro-minoan sign cm029\n77736 cypro-minoan sign cm030\n77737 cypro-minoan sign cm033\n77738 cypro-minoan sign cm034\n77739 cypro-minoan sign cm035\n77740 cypro-minoan sign cm036\n77741 cypro-minoan sign cm037\n77742 cypro-minoan sign cm038\n77743 cypro-minoan sign cm039\n77744 cypro-minoan sign cm040\n77745 cypro-minoan sign cm041\n77746 cypro-minoan sign cm044\n77747 cypro-minoan sign cm046\n77748 cypro-minoan sign cm047\n77749 cypro-minoan sign cm049\n77750 cypro-minoan sign cm050\n77751 cypro-minoan sign cm051\n77752 cypro-minoan sign cm052\n77753 cypro-minoan sign cm053\n77754 cypro-minoan sign cm054\n77755 cypro-minoan sign cm055\n77756 cypro-minoan sign cm056\n77757 cypro-minoan sign cm058\n77758 cypro-minoan sign cm059\n77759 cypro-minoan sign cm060\n77760 cypro-minoan sign cm061\n77761 cypro-minoan sign cm062\n77762 cypro-minoan sign cm063\n77763 cypro-minoan sign cm064\n77764 cypro-minoan sign cm066\n77765 cypro-minoan sign cm067\n77766 cypro-minoan sign cm068\n77767 cypro-minoan sign cm069\n77768 cypro-minoan sign cm070\n77769 cypro-minoan sign cm071\n77770 cypro-minoan sign cm072\n77771 cypro-minoan sign cm073\n77772 cypro-minoan sign cm074\n77773 cypro-minoan sign cm075\n77774 cypro-minoan sign cm075b\n77775 cypro-minoan sign cm076\n77776 cypro-minoan sign cm078\n77777 cypro-minoan sign cm079\n77778 cypro-minoan sign cm080\n77779 cypro-minoan sign cm081\n77780 cypro-minoan sign cm082\n77781 cypro-minoan sign cm083\n77782 cypro-minoan sign cm084\n77783 cypro-minoan sign cm085\n77784 cypro-minoan sign cm086\n77785 cypro-minoan sign cm087\n77786 cypro-minoan sign cm088\n77787 cypro-minoan sign cm089\n77788 cypro-minoan sign cm090\n77789 cypro-minoan sign cm091\n77790 cypro-minoan sign cm092\n77791 cypro-minoan sign cm094\n77792 cypro-minoan sign cm095\n77793 cypro-minoan sign cm096\n77794 cypro-minoan sign cm097\n77795 cypro-minoan sign cm098\n77796 cypro-minoan sign cm099\n77797 cypro-minoan sign cm100\n77798 cypro-minoan sign cm101\n77799 cypro-minoan sign cm102\n77800 cypro-minoan sign cm103\n77801 cypro-minoan sign cm104\n77802 cypro-minoan sign cm105\n77803 cypro-minoan sign cm107\n77804 cypro-minoan sign cm108\n77805 cypro-minoan sign cm109\n77806 cypro-minoan sign cm110\n77807 cypro-minoan sign cm112\n77808 cypro-minoan sign cm114\n77809 cypro-minoan sign cm301\n77810 cypro-minoan sign cm302\n77824 egyptian hieroglyph a001\n77825 egyptian hieroglyph a002\n77826 egyptian hieroglyph a003\n77827 egyptian hieroglyph a004\n77828 egyptian hieroglyph a005\n77829 egyptian hieroglyph a005a\n77830 egyptian hieroglyph a006\n77831 egyptian hieroglyph a006a\n77832 egyptian hieroglyph a006b\n77833 egyptian hieroglyph a007\n77834 egyptian hieroglyph a008\n77835 egyptian hieroglyph a009\n77836 egyptian hieroglyph a010\n77837 egyptian hieroglyph a011\n77838 egyptian hieroglyph a012\n77839 egyptian hieroglyph a013\n77840 egyptian hieroglyph a014\n77841 egyptian hieroglyph a014a\n77842 egyptian hieroglyph a015\n77843 egyptian hieroglyph a016\n77844 egyptian hieroglyph a017\n77845 egyptian hieroglyph a017a\n77846 egyptian hieroglyph a018\n77847 egyptian hieroglyph a019\n77848 egyptian hieroglyph a020\n77849 egyptian hieroglyph a021\n77850 egyptian hieroglyph a022\n77851 egyptian hieroglyph a023\n77852 egyptian hieroglyph a024\n77853 egyptian hieroglyph a025\n77854 egyptian hieroglyph a026\n77855 egyptian hieroglyph a027\n77856 egyptian hieroglyph a028\n77857 egyptian hieroglyph a029\n77858 egyptian hieroglyph a030\n77859 egyptian hieroglyph a031\n77860 egyptian hieroglyph a032\n77861 egyptian hieroglyph a032a\n77862 egyptian hieroglyph a033\n77863 egyptian hieroglyph a034\n77864 egyptian hieroglyph a035\n77865 egyptian hieroglyph a036\n77866 egyptian hieroglyph a037\n77867 egyptian hieroglyph a038\n77868 egyptian hieroglyph a039\n77869 egyptian hieroglyph a040\n77870 egyptian hieroglyph a040a\n77871 egyptian hieroglyph a041\n77872 egyptian hieroglyph a042\n77873 egyptian hieroglyph a042a\n77874 egyptian hieroglyph a043\n77875 egyptian hieroglyph a043a\n77876 egyptian hieroglyph a044\n77877 egyptian hieroglyph a045\n77878 egyptian hieroglyph a045a\n77879 egyptian hieroglyph a046\n77880 egyptian hieroglyph a047\n77881 egyptian hieroglyph a048\n77882 egyptian hieroglyph a049\n77883 egyptian hieroglyph a050\n77884 egyptian hieroglyph a051\n77885 egyptian hieroglyph a052\n77886 egyptian hieroglyph a053\n77887 egyptian hieroglyph a054\n77888 egyptian hieroglyph a055\n77889 egyptian hieroglyph a056\n77890 egyptian hieroglyph a057\n77891 egyptian hieroglyph a058\n77892 egyptian hieroglyph a059\n77893 egyptian hieroglyph a060\n77894 egyptian hieroglyph a061\n77895 egyptian hieroglyph a062\n77896 egyptian hieroglyph a063\n77897 egyptian hieroglyph a064\n77898 egyptian hieroglyph a065\n77899 egyptian hieroglyph a066\n77900 egyptian hieroglyph a067\n77901 egyptian hieroglyph a068\n77902 egyptian hieroglyph a069\n77903 egyptian hieroglyph a070\n77904 egyptian hieroglyph b001\n77905 egyptian hieroglyph b002\n77906 egyptian hieroglyph b003\n77907 egyptian hieroglyph b004\n77908 egyptian hieroglyph b005\n77909 egyptian hieroglyph b005a\n77910 egyptian hieroglyph b006\n77911 egyptian hieroglyph b007\n77912 egyptian hieroglyph b008\n77913 egyptian hieroglyph b009\n77914 egyptian hieroglyph c001\n77915 egyptian hieroglyph c002\n77916 egyptian hieroglyph c002a\n77917 egyptian hieroglyph c002b\n77918 egyptian hieroglyph c002c\n77919 egyptian hieroglyph c003\n77920 egyptian hieroglyph c004\n77921 egyptian hieroglyph c005\n77922 egyptian hieroglyph c006\n77923 egyptian hieroglyph c007\n77924 egyptian hieroglyph c008\n77925 egyptian hieroglyph c009\n77926 egyptian hieroglyph c010\n77927 egyptian hieroglyph c010a\n77928 egyptian hieroglyph c011\n77929 egyptian hieroglyph c012\n77930 egyptian hieroglyph c013\n77931 egyptian hieroglyph c014\n77932 egyptian hieroglyph c015\n77933 egyptian hieroglyph c016\n77934 egyptian hieroglyph c017\n77935 egyptian hieroglyph c018\n77936 egyptian hieroglyph c019\n77937 egyptian hieroglyph c020\n77938 egyptian hieroglyph c021\n77939 egyptian hieroglyph c022\n77940 egyptian hieroglyph c023\n77941 egyptian hieroglyph c024\n77942 egyptian hieroglyph d001\n77943 egyptian hieroglyph d002\n77944 egyptian hieroglyph d003\n77945 egyptian hieroglyph d004\n77946 egyptian hieroglyph d005\n77947 egyptian hieroglyph d006\n77948 egyptian hieroglyph d007\n77949 egyptian hieroglyph d008\n77950 egyptian hieroglyph d008a\n77951 egyptian hieroglyph d009\n77952 egyptian hieroglyph d010\n77953 egyptian hieroglyph d011\n77954 egyptian hieroglyph d012\n77955 egyptian hieroglyph d013\n77956 egyptian hieroglyph d014\n77957 egyptian hieroglyph d015\n77958 egyptian hieroglyph d016\n77959 egyptian hieroglyph d017\n77960 egyptian hieroglyph d018\n77961 egyptian hieroglyph d019\n77962 egyptian hieroglyph d020\n77963 egyptian hieroglyph d021\n77964 egyptian hieroglyph d022\n77965 egyptian hieroglyph d023\n77966 egyptian hieroglyph d024\n77967 egyptian hieroglyph d025\n77968 egyptian hieroglyph d026\n77969 egyptian hieroglyph d027\n77970 egyptian hieroglyph d027a\n77971 egyptian hieroglyph d028\n77972 egyptian hieroglyph d029\n77973 egyptian hieroglyph d030\n77974 egyptian hieroglyph d031\n77975 egyptian hieroglyph d031a\n77976 egyptian hieroglyph d032\n77977 egyptian hieroglyph d033\n77978 egyptian hieroglyph d034\n77979 egyptian hieroglyph d034a\n77980 egyptian hieroglyph d035\n77981 egyptian hieroglyph d036\n77982 egyptian hieroglyph d037\n77983 egyptian hieroglyph d038\n77984 egyptian hieroglyph d039\n77985 egyptian hieroglyph d040\n77986 egyptian hieroglyph d041\n77987 egyptian hieroglyph d042\n77988 egyptian hieroglyph d043\n77989 egyptian hieroglyph d044\n77990 egyptian hieroglyph d045\n77991 egyptian hieroglyph d046\n77992 egyptian hieroglyph d046a\n77993 egyptian hieroglyph d047\n77994 egyptian hieroglyph d048\n77995 egyptian hieroglyph d048a\n77996 egyptian hieroglyph d049\n77997 egyptian hieroglyph d050\n77998 egyptian hieroglyph d050a\n77999 egyptian hieroglyph d050b\n78000 egyptian hieroglyph d050c\n78001 egyptian hieroglyph d050d\n78002 egyptian hieroglyph d050e\n78003 egyptian hieroglyph d050f\n78004 egyptian hieroglyph d050g\n78005 egyptian hieroglyph d050h\n78006 egyptian hieroglyph d050i\n78007 egyptian hieroglyph d051\n78008 egyptian hieroglyph d052\n78009 egyptian hieroglyph d052a\n78010 egyptian hieroglyph d053\n78011 egyptian hieroglyph d054\n78012 egyptian hieroglyph d054a\n78013 egyptian hieroglyph d055\n78014 egyptian hieroglyph d056\n78015 egyptian hieroglyph d057\n78016 egyptian hieroglyph d058\n78017 egyptian hieroglyph d059\n78018 egyptian hieroglyph d060\n78019 egyptian hieroglyph d061\n78020 egyptian hieroglyph d062\n78021 egyptian hieroglyph d063\n78022 egyptian hieroglyph d064\n78023 egyptian hieroglyph d065\n78024 egyptian hieroglyph d066\n78025 egyptian hieroglyph d067\n78026 egyptian hieroglyph d067a\n78027 egyptian hieroglyph d067b\n78028 egyptian hieroglyph d067c\n78029 egyptian hieroglyph d067d\n78030 egyptian hieroglyph d067e\n78031 egyptian hieroglyph d067f\n78032 egyptian hieroglyph d067g\n78033 egyptian hieroglyph d067h\n78034 egyptian hieroglyph e001\n78035 egyptian hieroglyph e002\n78036 egyptian hieroglyph e003\n78037 egyptian hieroglyph e004\n78038 egyptian hieroglyph e005\n78039 egyptian hieroglyph e006\n78040 egyptian hieroglyph e007\n78041 egyptian hieroglyph e008\n78042 egyptian hieroglyph e008a\n78043 egyptian hieroglyph e009\n78044 egyptian hieroglyph e009a\n78045 egyptian hieroglyph e010\n78046 egyptian hieroglyph e011\n78047 egyptian hieroglyph e012\n78048 egyptian hieroglyph e013\n78049 egyptian hieroglyph e014\n78050 egyptian hieroglyph e015\n78051 egyptian hieroglyph e016\n78052 egyptian hieroglyph e016a\n78053 egyptian hieroglyph e017\n78054 egyptian hieroglyph e017a\n78055 egyptian hieroglyph e018\n78056 egyptian hieroglyph e019\n78057 egyptian hieroglyph e020\n78058 egyptian hieroglyph e020a\n78059 egyptian hieroglyph e021\n78060 egyptian hieroglyph e022\n78061 egyptian hieroglyph e023\n78062 egyptian hieroglyph e024\n78063 egyptian hieroglyph e025\n78064 egyptian hieroglyph e026\n78065 egyptian hieroglyph e027\n78066 egyptian hieroglyph e028\n78067 egyptian hieroglyph e028a\n78068 egyptian hieroglyph e029\n78069 egyptian hieroglyph e030\n78070 egyptian hieroglyph e031\n78071 egyptian hieroglyph e032\n78072 egyptian hieroglyph e033\n78073 egyptian hieroglyph e034\n78074 egyptian hieroglyph e034a\n78075 egyptian hieroglyph e036\n78076 egyptian hieroglyph e037\n78077 egyptian hieroglyph e038\n78078 egyptian hieroglyph f001\n78079 egyptian hieroglyph f001a\n78080 egyptian hieroglyph f002\n78081 egyptian hieroglyph f003\n78082 egyptian hieroglyph f004\n78083 egyptian hieroglyph f005\n78084 egyptian hieroglyph f006\n78085 egyptian hieroglyph f007\n78086 egyptian hieroglyph f008\n78087 egyptian hieroglyph f009\n78088 egyptian hieroglyph f010\n78089 egyptian hieroglyph f011\n78090 egyptian hieroglyph f012\n78091 egyptian hieroglyph f013\n78092 egyptian hieroglyph f013a\n78093 egyptian hieroglyph f014\n78094 egyptian hieroglyph f015\n78095 egyptian hieroglyph f016\n78096 egyptian hieroglyph f017\n78097 egyptian hieroglyph f018\n78098 egyptian hieroglyph f019\n78099 egyptian hieroglyph f020\n78100 egyptian hieroglyph f021\n78101 egyptian hieroglyph f021a\n78102 egyptian hieroglyph f022\n78103 egyptian hieroglyph f023\n78104 egyptian hieroglyph f024\n78105 egyptian hieroglyph f025\n78106 egyptian hieroglyph f026\n78107 egyptian hieroglyph f027\n78108 egyptian hieroglyph f028\n78109 egyptian hieroglyph f029\n78110 egyptian hieroglyph f030\n78111 egyptian hieroglyph f031\n78112 egyptian hieroglyph f031a\n78113 egyptian hieroglyph f032\n78114 egyptian hieroglyph f033\n78115 egyptian hieroglyph f034\n78116 egyptian hieroglyph f035\n78117 egyptian hieroglyph f036\n78118 egyptian hieroglyph f037\n78119 egyptian hieroglyph f037a\n78120 egyptian hieroglyph f038\n78121 egyptian hieroglyph f038a\n78122 egyptian hieroglyph f039\n78123 egyptian hieroglyph f040\n78124 egyptian hieroglyph f041\n78125 egyptian hieroglyph f042\n78126 egyptian hieroglyph f043\n78127 egyptian hieroglyph f044\n78128 egyptian hieroglyph f045\n78129 egyptian hieroglyph f045a\n78130 egyptian hieroglyph f046\n78131 egyptian hieroglyph f046a\n78132 egyptian hieroglyph f047\n78133 egyptian hieroglyph f047a\n78134 egyptian hieroglyph f048\n78135 egyptian hieroglyph f049\n78136 egyptian hieroglyph f050\n78137 egyptian hieroglyph f051\n78138 egyptian hieroglyph f051a\n78139 egyptian hieroglyph f051b\n78140 egyptian hieroglyph f051c\n78141 egyptian hieroglyph f052\n78142 egyptian hieroglyph f053\n78143 egyptian hieroglyph g001\n78144 egyptian hieroglyph g002\n78145 egyptian hieroglyph g003\n78146 egyptian hieroglyph g004\n78147 egyptian hieroglyph g005\n78148 egyptian hieroglyph g006\n78149 egyptian hieroglyph g006a\n78150 egyptian hieroglyph g007\n78151 egyptian hieroglyph g007a\n78152 egyptian hieroglyph g007b\n78153 egyptian hieroglyph g008\n78154 egyptian hieroglyph g009\n78155 egyptian hieroglyph g010\n78156 egyptian hieroglyph g011\n78157 egyptian hieroglyph g011a\n78158 egyptian hieroglyph g012\n78159 egyptian hieroglyph g013\n78160 egyptian hieroglyph g014\n78161 egyptian hieroglyph g015\n78162 egyptian hieroglyph g016\n78163 egyptian hieroglyph g017\n78164 egyptian hieroglyph g018\n78165 egyptian hieroglyph g019\n78166 egyptian hieroglyph g020\n78167 egyptian hieroglyph g020a\n78168 egyptian hieroglyph g021\n78169 egyptian hieroglyph g022\n78170 egyptian hieroglyph g023\n78171 egyptian hieroglyph g024\n78172 egyptian hieroglyph g025\n78173 egyptian hieroglyph g026\n78174 egyptian hieroglyph g026a\n78175 egyptian hieroglyph g027\n78176 egyptian hieroglyph g028\n78177 egyptian hieroglyph g029\n78178 egyptian hieroglyph g030\n78179 egyptian hieroglyph g031\n78180 egyptian hieroglyph g032\n78181 egyptian hieroglyph g033\n78182 egyptian hieroglyph g034\n78183 egyptian hieroglyph g035\n78184 egyptian hieroglyph g036\n78185 egyptian hieroglyph g036a\n78186 egyptian hieroglyph g037\n78187 egyptian hieroglyph g037a\n78188 egyptian hieroglyph g038\n78189 egyptian hieroglyph g039\n78190 egyptian hieroglyph g040\n78191 egyptian hieroglyph g041\n78192 egyptian hieroglyph g042\n78193 egyptian hieroglyph g043\n78194 egyptian hieroglyph g043a\n78195 egyptian hieroglyph g044\n78196 egyptian hieroglyph g045\n78197 egyptian hieroglyph g045a\n78198 egyptian hieroglyph g046\n78199 egyptian hieroglyph g047\n78200 egyptian hieroglyph g048\n78201 egyptian hieroglyph g049\n78202 egyptian hieroglyph g050\n78203 egyptian hieroglyph g051\n78204 egyptian hieroglyph g052\n78205 egyptian hieroglyph g053\n78206 egyptian hieroglyph g054\n78207 egyptian hieroglyph h001\n78208 egyptian hieroglyph h002\n78209 egyptian hieroglyph h003\n78210 egyptian hieroglyph h004\n78211 egyptian hieroglyph h005\n78212 egyptian hieroglyph h006\n78213 egyptian hieroglyph h006a\n78214 egyptian hieroglyph h007\n78215 egyptian hieroglyph h008\n78216 egyptian hieroglyph i001\n78217 egyptian hieroglyph i002\n78218 egyptian hieroglyph i003\n78219 egyptian hieroglyph i004\n78220 egyptian hieroglyph i005\n78221 egyptian hieroglyph i005a\n78222 egyptian hieroglyph i006\n78223 egyptian hieroglyph i007\n78224 egyptian hieroglyph i008\n78225 egyptian hieroglyph i009\n78226 egyptian hieroglyph i009a\n78227 egyptian hieroglyph i010\n78228 egyptian hieroglyph i010a\n78229 egyptian hieroglyph i011\n78230 egyptian hieroglyph i011a\n78231 egyptian hieroglyph i012\n78232 egyptian hieroglyph i013\n78233 egyptian hieroglyph i014\n78234 egyptian hieroglyph i015\n78235 egyptian hieroglyph k001\n78236 egyptian hieroglyph k002\n78237 egyptian hieroglyph k003\n78238 egyptian hieroglyph k004\n78239 egyptian hieroglyph k005\n78240 egyptian hieroglyph k006\n78241 egyptian hieroglyph k007\n78242 egyptian hieroglyph k008\n78243 egyptian hieroglyph l001\n78244 egyptian hieroglyph l002\n78245 egyptian hieroglyph l002a\n78246 egyptian hieroglyph l003\n78247 egyptian hieroglyph l004\n78248 egyptian hieroglyph l005\n78249 egyptian hieroglyph l006\n78250 egyptian hieroglyph l006a\n78251 egyptian hieroglyph l007\n78252 egyptian hieroglyph l008\n78253 egyptian hieroglyph m001\n78254 egyptian hieroglyph m001a\n78255 egyptian hieroglyph m001b\n78256 egyptian hieroglyph m002\n78257 egyptian hieroglyph m003\n78258 egyptian hieroglyph m003a\n78259 egyptian hieroglyph m004\n78260 egyptian hieroglyph m005\n78261 egyptian hieroglyph m006\n78262 egyptian hieroglyph m007\n78263 egyptian hieroglyph m008\n78264 egyptian hieroglyph m009\n78265 egyptian hieroglyph m010\n78266 egyptian hieroglyph m010a\n78267 egyptian hieroglyph m011\n78268 egyptian hieroglyph m012\n78269 egyptian hieroglyph m012a\n78270 egyptian hieroglyph m012b\n78271 egyptian hieroglyph m012c\n78272 egyptian hieroglyph m012d\n78273 egyptian hieroglyph m012e\n78274 egyptian hieroglyph m012f\n78275 egyptian hieroglyph m012g\n78276 egyptian hieroglyph m012h\n78277 egyptian hieroglyph m013\n78278 egyptian hieroglyph m014\n78279 egyptian hieroglyph m015\n78280 egyptian hieroglyph m015a\n78281 egyptian hieroglyph m016\n78282 egyptian hieroglyph m016a\n78283 egyptian hieroglyph m017\n78284 egyptian hieroglyph m017a\n78285 egyptian hieroglyph m018\n78286 egyptian hieroglyph m019\n78287 egyptian hieroglyph m020\n78288 egyptian hieroglyph m021\n78289 egyptian hieroglyph m022\n78290 egyptian hieroglyph m022a\n78291 egyptian hieroglyph m023\n78292 egyptian hieroglyph m024\n78293 egyptian hieroglyph m024a\n78294 egyptian hieroglyph m025\n78295 egyptian hieroglyph m026\n78296 egyptian hieroglyph m027\n78297 egyptian hieroglyph m028\n78298 egyptian hieroglyph m028a\n78299 egyptian hieroglyph m029\n78300 egyptian hieroglyph m030\n78301 egyptian hieroglyph m031\n78302 egyptian hieroglyph m031a\n78303 egyptian hieroglyph m032\n78304 egyptian hieroglyph m033\n78305 egyptian hieroglyph m033a\n78306 egyptian hieroglyph m033b\n78307 egyptian hieroglyph m034\n78308 egyptian hieroglyph m035\n78309 egyptian hieroglyph m036\n78310 egyptian hieroglyph m037\n78311 egyptian hieroglyph m038\n78312 egyptian hieroglyph m039\n78313 egyptian hieroglyph m040\n78314 egyptian hieroglyph m040a\n78315 egyptian hieroglyph m041\n78316 egyptian hieroglyph m042\n78317 egyptian hieroglyph m043\n78318 egyptian hieroglyph m044\n78319 egyptian hieroglyph n001\n78320 egyptian hieroglyph n002\n78321 egyptian hieroglyph n003\n78322 egyptian hieroglyph n004\n78323 egyptian hieroglyph n005\n78324 egyptian hieroglyph n006\n78325 egyptian hieroglyph n007\n78326 egyptian hieroglyph n008\n78327 egyptian hieroglyph n009\n78328 egyptian hieroglyph n010\n78329 egyptian hieroglyph n011\n78330 egyptian hieroglyph n012\n78331 egyptian hieroglyph n013\n78332 egyptian hieroglyph n014\n78333 egyptian hieroglyph n015\n78334 egyptian hieroglyph n016\n78335 egyptian hieroglyph n017\n78336 egyptian hieroglyph n018\n78337 egyptian hieroglyph n018a\n78338 egyptian hieroglyph n018b\n78339 egyptian hieroglyph n019\n78340 egyptian hieroglyph n020\n78341 egyptian hieroglyph n021\n78342 egyptian hieroglyph n022\n78343 egyptian hieroglyph n023\n78344 egyptian hieroglyph n024\n78345 egyptian hieroglyph n025\n78346 egyptian hieroglyph n025a\n78347 egyptian hieroglyph n026\n78348 egyptian hieroglyph n027\n78349 egyptian hieroglyph n028\n78350 egyptian hieroglyph n029\n78351 egyptian hieroglyph n030\n78352 egyptian hieroglyph n031\n78353 egyptian hieroglyph n032\n78354 egyptian hieroglyph n033\n78355 egyptian hieroglyph n033a\n78356 egyptian hieroglyph n034\n78357 egyptian hieroglyph n034a\n78358 egyptian hieroglyph n035\n78359 egyptian hieroglyph n035a\n78360 egyptian hieroglyph n036\n78361 egyptian hieroglyph n037\n78362 egyptian hieroglyph n037a\n78363 egyptian hieroglyph n038\n78364 egyptian hieroglyph n039\n78365 egyptian hieroglyph n040\n78366 egyptian hieroglyph n041\n78367 egyptian hieroglyph n042\n78368 egyptian hieroglyph nl001\n78369 egyptian hieroglyph nl002\n78370 egyptian hieroglyph nl003\n78371 egyptian hieroglyph nl004\n78372 egyptian hieroglyph nl005\n78373 egyptian hieroglyph nl005a\n78374 egyptian hieroglyph nl006\n78375 egyptian hieroglyph nl007\n78376 egyptian hieroglyph nl008\n78377 egyptian hieroglyph nl009\n78378 egyptian hieroglyph nl010\n78379 egyptian hieroglyph nl011\n78380 egyptian hieroglyph nl012\n78381 egyptian hieroglyph nl013\n78382 egyptian hieroglyph nl014\n78383 egyptian hieroglyph nl015\n78384 egyptian hieroglyph nl016\n78385 egyptian hieroglyph nl017\n78386 egyptian hieroglyph nl017a\n78387 egyptian hieroglyph nl018\n78388 egyptian hieroglyph nl019\n78389 egyptian hieroglyph nl020\n78390 egyptian hieroglyph nu001\n78391 egyptian hieroglyph nu002\n78392 egyptian hieroglyph nu003\n78393 egyptian hieroglyph nu004\n78394 egyptian hieroglyph nu005\n78395 egyptian hieroglyph nu006\n78396 egyptian hieroglyph nu007\n78397 egyptian hieroglyph nu008\n78398 egyptian hieroglyph nu009\n78399 egyptian hieroglyph nu010\n78400 egyptian hieroglyph nu010a\n78401 egyptian hieroglyph nu011\n78402 egyptian hieroglyph nu011a\n78403 egyptian hieroglyph nu012\n78404 egyptian hieroglyph nu013\n78405 egyptian hieroglyph nu014\n78406 egyptian hieroglyph nu015\n78407 egyptian hieroglyph nu016\n78408 egyptian hieroglyph nu017\n78409 egyptian hieroglyph nu018\n78410 egyptian hieroglyph nu018a\n78411 egyptian hieroglyph nu019\n78412 egyptian hieroglyph nu020\n78413 egyptian hieroglyph nu021\n78414 egyptian hieroglyph nu022\n78415 egyptian hieroglyph nu022a\n78416 egyptian hieroglyph o001\n78417 egyptian hieroglyph o001a\n78418 egyptian hieroglyph o002\n78419 egyptian hieroglyph o003\n78420 egyptian hieroglyph o004\n78421 egyptian hieroglyph o005\n78422 egyptian hieroglyph o005a\n78423 egyptian hieroglyph o006\n78424 egyptian hieroglyph o006a\n78425 egyptian hieroglyph o006b\n78426 egyptian hieroglyph o006c\n78427 egyptian hieroglyph o006d\n78428 egyptian hieroglyph o006e\n78429 egyptian hieroglyph o006f\n78430 egyptian hieroglyph o007\n78431 egyptian hieroglyph o008\n78432 egyptian hieroglyph o009\n78433 egyptian hieroglyph o010\n78434 egyptian hieroglyph o010a\n78435 egyptian hieroglyph o010b\n78436 egyptian hieroglyph o010c\n78437 egyptian hieroglyph o011\n78438 egyptian hieroglyph o012\n78439 egyptian hieroglyph o013\n78440 egyptian hieroglyph o014\n78441 egyptian hieroglyph o015\n78442 egyptian hieroglyph o016\n78443 egyptian hieroglyph o017\n78444 egyptian hieroglyph o018\n78445 egyptian hieroglyph o019\n78446 egyptian hieroglyph o019a\n78447 egyptian hieroglyph o020\n78448 egyptian hieroglyph o020a\n78449 egyptian hieroglyph o021\n78450 egyptian hieroglyph o022\n78451 egyptian hieroglyph o023\n78452 egyptian hieroglyph o024\n78453 egyptian hieroglyph o024a\n78454 egyptian hieroglyph o025\n78455 egyptian hieroglyph o025a\n78456 egyptian hieroglyph o026\n78457 egyptian hieroglyph o027\n78458 egyptian hieroglyph o028\n78459 egyptian hieroglyph o029\n78460 egyptian hieroglyph o029a\n78461 egyptian hieroglyph o030\n78462 egyptian hieroglyph o030a\n78463 egyptian hieroglyph o031\n78464 egyptian hieroglyph o032\n78465 egyptian hieroglyph o033\n78466 egyptian hieroglyph o033a\n78467 egyptian hieroglyph o034\n78468 egyptian hieroglyph o035\n78469 egyptian hieroglyph o036\n78470 egyptian hieroglyph o036a\n78471 egyptian hieroglyph o036b\n78472 egyptian hieroglyph o036c\n78473 egyptian hieroglyph o036d\n78474 egyptian hieroglyph o037\n78475 egyptian hieroglyph o038\n78476 egyptian hieroglyph o039\n78477 egyptian hieroglyph o040\n78478 egyptian hieroglyph o041\n78479 egyptian hieroglyph o042\n78480 egyptian hieroglyph o043\n78481 egyptian hieroglyph o044\n78482 egyptian hieroglyph o045\n78483 egyptian hieroglyph o046\n78484 egyptian hieroglyph o047\n78485 egyptian hieroglyph o048\n78486 egyptian hieroglyph o049\n78487 egyptian hieroglyph o050\n78488 egyptian hieroglyph o050a\n78489 egyptian hieroglyph o050b\n78490 egyptian hieroglyph o051\n78491 egyptian hieroglyph p001\n78492 egyptian hieroglyph p001a\n78493 egyptian hieroglyph p002\n78494 egyptian hieroglyph p003\n78495 egyptian hieroglyph p003a\n78496 egyptian hieroglyph p004\n78497 egyptian hieroglyph p005\n78498 egyptian hieroglyph p006\n78499 egyptian hieroglyph p007\n78500 egyptian hieroglyph p008\n78501 egyptian hieroglyph p009\n78502 egyptian hieroglyph p010\n78503 egyptian hieroglyph p011\n78504 egyptian hieroglyph q001\n78505 egyptian hieroglyph q002\n78506 egyptian hieroglyph q003\n78507 egyptian hieroglyph q004\n78508 egyptian hieroglyph q005\n78509 egyptian hieroglyph q006\n78510 egyptian hieroglyph q007\n78511 egyptian hieroglyph r001\n78512 egyptian hieroglyph r002\n78513 egyptian hieroglyph r002a\n78514 egyptian hieroglyph r003\n78515 egyptian hieroglyph r003a\n78516 egyptian hieroglyph r003b\n78517 egyptian hieroglyph r004\n78518 egyptian hieroglyph r005\n78519 egyptian hieroglyph r006\n78520 egyptian hieroglyph r007\n78521 egyptian hieroglyph r008\n78522 egyptian hieroglyph r009\n78523 egyptian hieroglyph r010\n78524 egyptian hieroglyph r010a\n78525 egyptian hieroglyph r011\n78526 egyptian hieroglyph r012\n78527 egyptian hieroglyph r013\n78528 egyptian hieroglyph r014\n78529 egyptian hieroglyph r015\n78530 egyptian hieroglyph r016\n78531 egyptian hieroglyph r016a\n78532 egyptian hieroglyph r017\n78533 egyptian hieroglyph r018\n78534 egyptian hieroglyph r019\n78535 egyptian hieroglyph r020\n78536 egyptian hieroglyph r021\n78537 egyptian hieroglyph r022\n78538 egyptian hieroglyph r023\n78539 egyptian hieroglyph r024\n78540 egyptian hieroglyph r025\n78541 egyptian hieroglyph r026\n78542 egyptian hieroglyph r027\n78543 egyptian hieroglyph r028\n78544 egyptian hieroglyph r029\n78545 egyptian hieroglyph s001\n78546 egyptian hieroglyph s002\n78547 egyptian hieroglyph s002a\n78548 egyptian hieroglyph s003\n78549 egyptian hieroglyph s004\n78550 egyptian hieroglyph s005\n78551 egyptian hieroglyph s006\n78552 egyptian hieroglyph s006a\n78553 egyptian hieroglyph s007\n78554 egyptian hieroglyph s008\n78555 egyptian hieroglyph s009\n78556 egyptian hieroglyph s010\n78557 egyptian hieroglyph s011\n78558 egyptian hieroglyph s012\n78559 egyptian hieroglyph s013\n78560 egyptian hieroglyph s014\n78561 egyptian hieroglyph s014a\n78562 egyptian hieroglyph s014b\n78563 egyptian hieroglyph s015\n78564 egyptian hieroglyph s016\n78565 egyptian hieroglyph s017\n78566 egyptian hieroglyph s017a\n78567 egyptian hieroglyph s018\n78568 egyptian hieroglyph s019\n78569 egyptian hieroglyph s020\n78570 egyptian hieroglyph s021\n78571 egyptian hieroglyph s022\n78572 egyptian hieroglyph s023\n78573 egyptian hieroglyph s024\n78574 egyptian hieroglyph s025\n78575 egyptian hieroglyph s026\n78576 egyptian hieroglyph s026a\n78577 egyptian hieroglyph s026b\n78578 egyptian hieroglyph s027\n78579 egyptian hieroglyph s028\n78580 egyptian hieroglyph s029\n78581 egyptian hieroglyph s030\n78582 egyptian hieroglyph s031\n78583 egyptian hieroglyph s032\n78584 egyptian hieroglyph s033\n78585 egyptian hieroglyph s034\n78586 egyptian hieroglyph s035\n78587 egyptian hieroglyph s035a\n78588 egyptian hieroglyph s036\n78589 egyptian hieroglyph s037\n78590 egyptian hieroglyph s038\n78591 egyptian hieroglyph s039\n78592 egyptian hieroglyph s040\n78593 egyptian hieroglyph s041\n78594 egyptian hieroglyph s042\n78595 egyptian hieroglyph s043\n78596 egyptian hieroglyph s044\n78597 egyptian hieroglyph s045\n78598 egyptian hieroglyph s046\n78599 egyptian hieroglyph t001\n78600 egyptian hieroglyph t002\n78601 egyptian hieroglyph t003\n78602 egyptian hieroglyph t003a\n78603 egyptian hieroglyph t004\n78604 egyptian hieroglyph t005\n78605 egyptian hieroglyph t006\n78606 egyptian hieroglyph t007\n78607 egyptian hieroglyph t007a\n78608 egyptian hieroglyph t008\n78609 egyptian hieroglyph t008a\n78610 egyptian hieroglyph t009\n78611 egyptian hieroglyph t009a\n78612 egyptian hieroglyph t010\n78613 egyptian hieroglyph t011\n78614 egyptian hieroglyph t011a\n78615 egyptian hieroglyph t012\n78616 egyptian hieroglyph t013\n78617 egyptian hieroglyph t014\n78618 egyptian hieroglyph t015\n78619 egyptian hieroglyph t016\n78620 egyptian hieroglyph t016a\n78621 egyptian hieroglyph t017\n78622 egyptian hieroglyph t018\n78623 egyptian hieroglyph t019\n78624 egyptian hieroglyph t020\n78625 egyptian hieroglyph t021\n78626 egyptian hieroglyph t022\n78627 egyptian hieroglyph t023\n78628 egyptian hieroglyph t024\n78629 egyptian hieroglyph t025\n78630 egyptian hieroglyph t026\n78631 egyptian hieroglyph t027\n78632 egyptian hieroglyph t028\n78633 egyptian hieroglyph t029\n78634 egyptian hieroglyph t030\n78635 egyptian hieroglyph t031\n78636 egyptian hieroglyph t032\n78637 egyptian hieroglyph t032a\n78638 egyptian hieroglyph t033\n78639 egyptian hieroglyph t033a\n78640 egyptian hieroglyph t034\n78641 egyptian hieroglyph t035\n78642 egyptian hieroglyph t036\n78643 egyptian hieroglyph u001\n78644 egyptian hieroglyph u002\n78645 egyptian hieroglyph u003\n78646 egyptian hieroglyph u004\n78647 egyptian hieroglyph u005\n78648 egyptian hieroglyph u006\n78649 egyptian hieroglyph u006a\n78650 egyptian hieroglyph u006b\n78651 egyptian hieroglyph u007\n78652 egyptian hieroglyph u008\n78653 egyptian hieroglyph u009\n78654 egyptian hieroglyph u010\n78655 egyptian hieroglyph u011\n78656 egyptian hieroglyph u012\n78657 egyptian hieroglyph u013\n78658 egyptian hieroglyph u014\n78659 egyptian hieroglyph u015\n78660 egyptian hieroglyph u016\n78661 egyptian hieroglyph u017\n78662 egyptian hieroglyph u018\n78663 egyptian hieroglyph u019\n78664 egyptian hieroglyph u020\n78665 egyptian hieroglyph u021\n78666 egyptian hieroglyph u022\n78667 egyptian hieroglyph u023\n78668 egyptian hieroglyph u023a\n78669 egyptian hieroglyph u024\n78670 egyptian hieroglyph u025\n78671 egyptian hieroglyph u026\n78672 egyptian hieroglyph u027\n78673 egyptian hieroglyph u028\n78674 egyptian hieroglyph u029\n78675 egyptian hieroglyph u029a\n78676 egyptian hieroglyph u030\n78677 egyptian hieroglyph u031\n78678 egyptian hieroglyph u032\n78679 egyptian hieroglyph u032a\n78680 egyptian hieroglyph u033\n78681 egyptian hieroglyph u034\n78682 egyptian hieroglyph u035\n78683 egyptian hieroglyph u036\n78684 egyptian hieroglyph u037\n78685 egyptian hieroglyph u038\n78686 egyptian hieroglyph u039\n78687 egyptian hieroglyph u040\n78688 egyptian hieroglyph u041\n78689 egyptian hieroglyph u042\n78690 egyptian hieroglyph v001\n78691 egyptian hieroglyph v001a\n78692 egyptian hieroglyph v001b\n78693 egyptian hieroglyph v001c\n78694 egyptian hieroglyph v001d\n78695 egyptian hieroglyph v001e\n78696 egyptian hieroglyph v001f\n78697 egyptian hieroglyph v001g\n78698 egyptian hieroglyph v001h\n78699 egyptian hieroglyph v001i\n78700 egyptian hieroglyph v002\n78701 egyptian hieroglyph v002a\n78702 egyptian hieroglyph v003\n78703 egyptian hieroglyph v004\n78704 egyptian hieroglyph v005\n78705 egyptian hieroglyph v006\n78706 egyptian hieroglyph v007\n78707 egyptian hieroglyph v007a\n78708 egyptian hieroglyph v007b\n78709 egyptian hieroglyph v008\n78710 egyptian hieroglyph v009\n78711 egyptian hieroglyph v010\n78712 egyptian hieroglyph v011\n78713 egyptian hieroglyph v011a\n78714 egyptian hieroglyph v011b\n78715 egyptian hieroglyph v011c\n78716 egyptian hieroglyph v012\n78717 egyptian hieroglyph v012a\n78718 egyptian hieroglyph v012b\n78719 egyptian hieroglyph v013\n78720 egyptian hieroglyph v014\n78721 egyptian hieroglyph v015\n78722 egyptian hieroglyph v016\n78723 egyptian hieroglyph v017\n78724 egyptian hieroglyph v018\n78725 egyptian hieroglyph v019\n78726 egyptian hieroglyph v020\n78727 egyptian hieroglyph v020a\n78728 egyptian hieroglyph v020b\n78729 egyptian hieroglyph v020c\n78730 egyptian hieroglyph v020d\n78731 egyptian hieroglyph v020e\n78732 egyptian hieroglyph v020f\n78733 egyptian hieroglyph v020g\n78734 egyptian hieroglyph v020h\n78735 egyptian hieroglyph v020i\n78736 egyptian hieroglyph v020j\n78737 egyptian hieroglyph v020k\n78738 egyptian hieroglyph v020l\n78739 egyptian hieroglyph v021\n78740 egyptian hieroglyph v022\n78741 egyptian hieroglyph v023\n78742 egyptian hieroglyph v023a\n78743 egyptian hieroglyph v024\n78744 egyptian hieroglyph v025\n78745 egyptian hieroglyph v026\n78746 egyptian hieroglyph v027\n78747 egyptian hieroglyph v028\n78748 egyptian hieroglyph v028a\n78749 egyptian hieroglyph v029\n78750 egyptian hieroglyph v029a\n78751 egyptian hieroglyph v030\n78752 egyptian hieroglyph v030a\n78753 egyptian hieroglyph v031\n78754 egyptian hieroglyph v031a\n78755 egyptian hieroglyph v032\n78756 egyptian hieroglyph v033\n78757 egyptian hieroglyph v033a\n78758 egyptian hieroglyph v034\n78759 egyptian hieroglyph v035\n78760 egyptian hieroglyph v036\n78761 egyptian hieroglyph v037\n78762 egyptian hieroglyph v037a\n78763 egyptian hieroglyph v038\n78764 egyptian hieroglyph v039\n78765 egyptian hieroglyph v040\n78766 egyptian hieroglyph v040a\n78767 egyptian hieroglyph w001\n78768 egyptian hieroglyph w002\n78769 egyptian hieroglyph w003\n78770 egyptian hieroglyph w003a\n78771 egyptian hieroglyph w004\n78772 egyptian hieroglyph w005\n78773 egyptian hieroglyph w006\n78774 egyptian hieroglyph w007\n78775 egyptian hieroglyph w008\n78776 egyptian hieroglyph w009\n78777 egyptian hieroglyph w009a\n78778 egyptian hieroglyph w010\n78779 egyptian hieroglyph w010a\n78780 egyptian hieroglyph w011\n78781 egyptian hieroglyph w012\n78782 egyptian hieroglyph w013\n78783 egyptian hieroglyph w014\n78784 egyptian hieroglyph w014a\n78785 egyptian hieroglyph w015\n78786 egyptian hieroglyph w016\n78787 egyptian hieroglyph w017\n78788 egyptian hieroglyph w017a\n78789 egyptian hieroglyph w018\n78790 egyptian hieroglyph w018a\n78791 egyptian hieroglyph w019\n78792 egyptian hieroglyph w020\n78793 egyptian hieroglyph w021\n78794 egyptian hieroglyph w022\n78795 egyptian hieroglyph w023\n78796 egyptian hieroglyph w024\n78797 egyptian hieroglyph w024a\n78798 egyptian hieroglyph w025\n78799 egyptian hieroglyph x001\n78800 egyptian hieroglyph x002\n78801 egyptian hieroglyph x003\n78802 egyptian hieroglyph x004\n78803 egyptian hieroglyph x004a\n78804 egyptian hieroglyph x004b\n78805 egyptian hieroglyph x005\n78806 egyptian hieroglyph x006\n78807 egyptian hieroglyph x006a\n78808 egyptian hieroglyph x007\n78809 egyptian hieroglyph x008\n78810 egyptian hieroglyph x008a\n78811 egyptian hieroglyph y001\n78812 egyptian hieroglyph y001a\n78813 egyptian hieroglyph y002\n78814 egyptian hieroglyph y003\n78815 egyptian hieroglyph y004\n78816 egyptian hieroglyph y005\n78817 egyptian hieroglyph y006\n78818 egyptian hieroglyph y007\n78819 egyptian hieroglyph y008\n78820 egyptian hieroglyph z001\n78821 egyptian hieroglyph z002\n78822 egyptian hieroglyph z002a\n78823 egyptian hieroglyph z002b\n78824 egyptian hieroglyph z002c\n78825 egyptian hieroglyph z002d\n78826 egyptian hieroglyph z003\n78827 egyptian hieroglyph z003a\n78828 egyptian hieroglyph z003b\n78829 egyptian hieroglyph z004\n78830 egyptian hieroglyph z004a\n78831 egyptian hieroglyph z005\n78832 egyptian hieroglyph z005a\n78833 egyptian hieroglyph z006\n78834 egyptian hieroglyph z007\n78835 egyptian hieroglyph z008\n78836 egyptian hieroglyph z009\n78837 egyptian hieroglyph z010\n78838 egyptian hieroglyph z011\n78839 egyptian hieroglyph z012\n78840 egyptian hieroglyph z013\n78841 egyptian hieroglyph z014\n78842 egyptian hieroglyph z015\n78843 egyptian hieroglyph z015a\n78844 egyptian hieroglyph z015b\n78845 egyptian hieroglyph z015c\n78846 egyptian hieroglyph z015d\n78847 egyptian hieroglyph z015e\n78848 egyptian hieroglyph z015f\n78849 egyptian hieroglyph z015g\n78850 egyptian hieroglyph z015h\n78851 egyptian hieroglyph z015i\n78852 egyptian hieroglyph z016\n78853 egyptian hieroglyph z016a\n78854 egyptian hieroglyph z016b\n78855 egyptian hieroglyph z016c\n78856 egyptian hieroglyph z016d\n78857 egyptian hieroglyph z016e\n78858 egyptian hieroglyph z016f\n78859 egyptian hieroglyph z016g\n78860 egyptian hieroglyph z016h\n78861 egyptian hieroglyph aa001\n78862 egyptian hieroglyph aa002\n78863 egyptian hieroglyph aa003\n78864 egyptian hieroglyph aa004\n78865 egyptian hieroglyph aa005\n78866 egyptian hieroglyph aa006\n78867 egyptian hieroglyph aa007\n78868 egyptian hieroglyph aa007a\n78869 egyptian hieroglyph aa007b\n78870 egyptian hieroglyph aa008\n78871 egyptian hieroglyph aa009\n78872 egyptian hieroglyph aa010\n78873 egyptian hieroglyph aa011\n78874 egyptian hieroglyph aa012\n78875 egyptian hieroglyph aa013\n78876 egyptian hieroglyph aa014\n78877 egyptian hieroglyph aa015\n78878 egyptian hieroglyph aa016\n78879 egyptian hieroglyph aa017\n78880 egyptian hieroglyph aa018\n78881 egyptian hieroglyph aa019\n78882 egyptian hieroglyph aa020\n78883 egyptian hieroglyph aa021\n78884 egyptian hieroglyph aa022\n78885 egyptian hieroglyph aa023\n78886 egyptian hieroglyph aa024\n78887 egyptian hieroglyph aa025\n78888 egyptian hieroglyph aa026\n78889 egyptian hieroglyph aa027\n78890 egyptian hieroglyph aa028\n78891 egyptian hieroglyph aa029\n78892 egyptian hieroglyph aa030\n78893 egyptian hieroglyph aa031\n78894 egyptian hieroglyph aa032\n78895 egyptian hieroglyph v011d\n78896 egyptian hieroglyph vertical joiner\n78897 egyptian hieroglyph horizontal joiner\n78898 egyptian hieroglyph insert at top start\n78899 egyptian hieroglyph insert at bottom start\n78900 egyptian hieroglyph insert at top end\n78901 egyptian hieroglyph insert at bottom end\n78902 egyptian hieroglyph overlay middle\n78903 egyptian hieroglyph begin segment\n78904 egyptian hieroglyph end segment\n78905 egyptian hieroglyph insert at middle\n78906 egyptian hieroglyph insert at top\n78907 egyptian hieroglyph insert at bottom\n78908 egyptian hieroglyph begin enclosure\n78909 egyptian hieroglyph end enclosure\n78910 egyptian hieroglyph begin walled enclosure\n78911 egyptian hieroglyph end walled enclosure\n78912 egyptian hieroglyph mirror horizontally\n78913 egyptian hieroglyph full blank\n78914 egyptian hieroglyph half blank\n78915 egyptian hieroglyph lost sign\n78916 egyptian hieroglyph half lost sign\n78917 egyptian hieroglyph tall lost sign\n78918 egyptian hieroglyph wide lost sign\n78919 egyptian hieroglyph modifier damaged at top start\n78920 egyptian hieroglyph modifier damaged at bottom start\n78921 egyptian hieroglyph modifier damaged at start\n78922 egyptian hieroglyph modifier damaged at top end\n78923 egyptian hieroglyph modifier damaged at top\n78924 egyptian hieroglyph modifier damaged at bottom start and top end\n78925 egyptian hieroglyph modifier damaged at start and top\n78926 egyptian hieroglyph modifier damaged at bottom end\n78927 egyptian hieroglyph modifier damaged at top start and bottom end\n78928 egyptian hieroglyph modifier damaged at bottom\n78929 egyptian hieroglyph modifier damaged at start and bottom\n78930 egyptian hieroglyph modifier damaged at end\n78931 egyptian hieroglyph modifier damaged at top and end\n78932 egyptian hieroglyph modifier damaged at bottom and end\n78933 egyptian hieroglyph modifier damaged\n78944 egyptian hieroglyph-13460\n78945 egyptian hieroglyph-13461\n78946 egyptian hieroglyph-13462\n78947 egyptian hieroglyph-13463\n78948 egyptian hieroglyph-13464\n78949 egyptian hieroglyph-13465\n78950 egyptian hieroglyph-13466\n78951 egyptian hieroglyph-13467\n78952 egyptian hieroglyph-13468\n78953 egyptian hieroglyph-13469\n78954 egyptian hieroglyph-1346a\n78955 egyptian hieroglyph-1346b\n78956 egyptian hieroglyph-1346c\n78957 egyptian hieroglyph-1346d\n78958 egyptian hieroglyph-1346e\n78959 egyptian hieroglyph-1346f\n78960 egyptian hieroglyph-13470\n78961 egyptian hieroglyph-13471\n78962 egyptian hieroglyph-13472\n78963 egyptian hieroglyph-13473\n78964 egyptian hieroglyph-13474\n78965 egyptian hieroglyph-13475\n78966 egyptian hieroglyph-13476\n78967 egyptian hieroglyph-13477\n78968 egyptian hieroglyph-13478\n78969 egyptian hieroglyph-13479\n78970 egyptian hieroglyph-1347a\n78971 egyptian hieroglyph-1347b\n78972 egyptian hieroglyph-1347c\n78973 egyptian hieroglyph-1347d\n78974 egyptian hieroglyph-1347e\n78975 egyptian hieroglyph-1347f\n78976 egyptian hieroglyph-13480\n78977 egyptian hieroglyph-13481\n78978 egyptian hieroglyph-13482\n78979 egyptian hieroglyph-13483\n78980 egyptian hieroglyph-13484\n78981 egyptian hieroglyph-13485\n78982 egyptian hieroglyph-13486\n78983 egyptian hieroglyph-13487\n78984 egyptian hieroglyph-13488\n78985 egyptian hieroglyph-13489\n78986 egyptian hieroglyph-1348a\n78987 egyptian hieroglyph-1348b\n78988 egyptian hieroglyph-1348c\n78989 egyptian hieroglyph-1348d\n78990 egyptian hieroglyph-1348e\n78991 egyptian hieroglyph-1348f\n78992 egyptian hieroglyph-13490\n78993 egyptian hieroglyph-13491\n78994 egyptian hieroglyph-13492\n78995 egyptian hieroglyph-13493\n78996 egyptian hieroglyph-13494\n78997 egyptian hieroglyph-13495\n78998 egyptian hieroglyph-13496\n78999 egyptian hieroglyph-13497\n79000 egyptian hieroglyph-13498\n79001 egyptian hieroglyph-13499\n79002 egyptian hieroglyph-1349a\n79003 egyptian hieroglyph-1349b\n79004 egyptian hieroglyph-1349c\n79005 egyptian hieroglyph-1349d\n79006 egyptian hieroglyph-1349e\n79007 egyptian hieroglyph-1349f\n79008 egyptian hieroglyph-134a0\n79009 egyptian hieroglyph-134a1\n79010 egyptian hieroglyph-134a2\n79011 egyptian hieroglyph-134a3\n79012 egyptian hieroglyph-134a4\n79013 egyptian hieroglyph-134a5\n79014 egyptian hieroglyph-134a6\n79015 egyptian hieroglyph-134a7\n79016 egyptian hieroglyph-134a8\n79017 egyptian hieroglyph-134a9\n79018 egyptian hieroglyph-134aa\n79019 egyptian hieroglyph-134ab\n79020 egyptian hieroglyph-134ac\n79021 egyptian hieroglyph-134ad\n79022 egyptian hieroglyph-134ae\n79023 egyptian hieroglyph-134af\n79024 egyptian hieroglyph-134b0\n79025 egyptian hieroglyph-134b1\n79026 egyptian hieroglyph-134b2\n79027 egyptian hieroglyph-134b3\n79028 egyptian hieroglyph-134b4\n79029 egyptian hieroglyph-134b5\n79030 egyptian hieroglyph-134b6\n79031 egyptian hieroglyph-134b7\n79032 egyptian hieroglyph-134b8\n79033 egyptian hieroglyph-134b9\n79034 egyptian hieroglyph-134ba\n79035 egyptian hieroglyph-134bb\n79036 egyptian hieroglyph-134bc\n79037 egyptian hieroglyph-134bd\n79038 egyptian hieroglyph-134be\n79039 egyptian hieroglyph-134bf\n79040 egyptian hieroglyph-134c0\n79041 egyptian hieroglyph-134c1\n79042 egyptian hieroglyph-134c2\n79043 egyptian hieroglyph-134c3\n79044 egyptian hieroglyph-134c4\n79045 egyptian hieroglyph-134c5\n79046 egyptian hieroglyph-134c6\n79047 egyptian hieroglyph-134c7\n79048 egyptian hieroglyph-134c8\n79049 egyptian hieroglyph-134c9\n79050 egyptian hieroglyph-134ca\n79051 egyptian hieroglyph-134cb\n79052 egyptian hieroglyph-134cc\n79053 egyptian hieroglyph-134cd\n79054 egyptian hieroglyph-134ce\n79055 egyptian hieroglyph-134cf\n79056 egyptian hieroglyph-134d0\n79057 egyptian hieroglyph-134d1\n79058 egyptian hieroglyph-134d2\n79059 egyptian hieroglyph-134d3\n79060 egyptian hieroglyph-134d4\n79061 egyptian hieroglyph-134d5\n79062 egyptian hieroglyph-134d6\n79063 egyptian hieroglyph-134d7\n79064 egyptian hieroglyph-134d8\n79065 egyptian hieroglyph-134d9\n79066 egyptian hieroglyph-134da\n79067 egyptian hieroglyph-134db\n79068 egyptian hieroglyph-134dc\n79069 egyptian hieroglyph-134dd\n79070 egyptian hieroglyph-134de\n79071 egyptian hieroglyph-134df\n79072 egyptian hieroglyph-134e0\n79073 egyptian hieroglyph-134e1\n79074 egyptian hieroglyph-134e2\n79075 egyptian hieroglyph-134e3\n79076 egyptian hieroglyph-134e4\n79077 egyptian hieroglyph-134e5\n79078 egyptian hieroglyph-134e6\n79079 egyptian hieroglyph-134e7\n79080 egyptian hieroglyph-134e8\n79081 egyptian hieroglyph-134e9\n79082 egyptian hieroglyph-134ea\n79083 egyptian hieroglyph-134eb\n79084 egyptian hieroglyph-134ec\n79085 egyptian hieroglyph-134ed\n79086 egyptian hieroglyph-134ee\n79087 egyptian hieroglyph-134ef\n79088 egyptian hieroglyph-134f0\n79089 egyptian hieroglyph-134f1\n79090 egyptian hieroglyph-134f2\n79091 egyptian hieroglyph-134f3\n79092 egyptian hieroglyph-134f4\n79093 egyptian hieroglyph-134f5\n79094 egyptian hieroglyph-134f6\n79095 egyptian hieroglyph-134f7\n79096 egyptian hieroglyph-134f8\n79097 egyptian hieroglyph-134f9\n79098 egyptian hieroglyph-134fa\n79099 egyptian hieroglyph-134fb\n79100 egyptian hieroglyph-134fc\n79101 egyptian hieroglyph-134fd\n79102 egyptian hieroglyph-134fe\n79103 egyptian hieroglyph-134ff\n79104 egyptian hieroglyph-13500\n79105 egyptian hieroglyph-13501\n79106 egyptian hieroglyph-13502\n79107 egyptian hieroglyph-13503\n79108 egyptian hieroglyph-13504\n79109 egyptian hieroglyph-13505\n79110 egyptian hieroglyph-13506\n79111 egyptian hieroglyph-13507\n79112 egyptian hieroglyph-13508\n79113 egyptian hieroglyph-13509\n79114 egyptian hieroglyph-1350a\n79115 egyptian hieroglyph-1350b\n79116 egyptian hieroglyph-1350c\n79117 egyptian hieroglyph-1350d\n79118 egyptian hieroglyph-1350e\n79119 egyptian hieroglyph-1350f\n79120 egyptian hieroglyph-13510\n79121 egyptian hieroglyph-13511\n79122 egyptian hieroglyph-13512\n79123 egyptian hieroglyph-13513\n79124 egyptian hieroglyph-13514\n79125 egyptian hieroglyph-13515\n79126 egyptian hieroglyph-13516\n79127 egyptian hieroglyph-13517\n79128 egyptian hieroglyph-13518\n79129 egyptian hieroglyph-13519\n79130 egyptian hieroglyph-1351a\n79131 egyptian hieroglyph-1351b\n79132 egyptian hieroglyph-1351c\n79133 egyptian hieroglyph-1351d\n79134 egyptian hieroglyph-1351e\n79135 egyptian hieroglyph-1351f\n79136 egyptian hieroglyph-13520\n79137 egyptian hieroglyph-13521\n79138 egyptian hieroglyph-13522\n79139 egyptian hieroglyph-13523\n79140 egyptian hieroglyph-13524\n79141 egyptian hieroglyph-13525\n79142 egyptian hieroglyph-13526\n79143 egyptian hieroglyph-13527\n79144 egyptian hieroglyph-13528\n79145 egyptian hieroglyph-13529\n79146 egyptian hieroglyph-1352a\n79147 egyptian hieroglyph-1352b\n79148 egyptian hieroglyph-1352c\n79149 egyptian hieroglyph-1352d\n79150 egyptian hieroglyph-1352e\n79151 egyptian hieroglyph-1352f\n79152 egyptian hieroglyph-13530\n79153 egyptian hieroglyph-13531\n79154 egyptian hieroglyph-13532\n79155 egyptian hieroglyph-13533\n79156 egyptian hieroglyph-13534\n79157 egyptian hieroglyph-13535\n79158 egyptian hieroglyph-13536\n79159 egyptian hieroglyph-13537\n79160 egyptian hieroglyph-13538\n79161 egyptian hieroglyph-13539\n79162 egyptian hieroglyph-1353a\n79163 egyptian hieroglyph-1353b\n79164 egyptian hieroglyph-1353c\n79165 egyptian hieroglyph-1353d\n79166 egyptian hieroglyph-1353e\n79167 egyptian hieroglyph-1353f\n79168 egyptian hieroglyph-13540\n79169 egyptian hieroglyph-13541\n79170 egyptian hieroglyph-13542\n79171 egyptian hieroglyph-13543\n79172 egyptian hieroglyph-13544\n79173 egyptian hieroglyph-13545\n79174 egyptian hieroglyph-13546\n79175 egyptian hieroglyph-13547\n79176 egyptian hieroglyph-13548\n79177 egyptian hieroglyph-13549\n79178 egyptian hieroglyph-1354a\n79179 egyptian hieroglyph-1354b\n79180 egyptian hieroglyph-1354c\n79181 egyptian hieroglyph-1354d\n79182 egyptian hieroglyph-1354e\n79183 egyptian hieroglyph-1354f\n79184 egyptian hieroglyph-13550\n79185 egyptian hieroglyph-13551\n79186 egyptian hieroglyph-13552\n79187 egyptian hieroglyph-13553\n79188 egyptian hieroglyph-13554\n79189 egyptian hieroglyph-13555\n79190 egyptian hieroglyph-13556\n79191 egyptian hieroglyph-13557\n79192 egyptian hieroglyph-13558\n79193 egyptian hieroglyph-13559\n79194 egyptian hieroglyph-1355a\n79195 egyptian hieroglyph-1355b\n79196 egyptian hieroglyph-1355c\n79197 egyptian hieroglyph-1355d\n79198 egyptian hieroglyph-1355e\n79199 egyptian hieroglyph-1355f\n79200 egyptian hieroglyph-13560\n79201 egyptian hieroglyph-13561\n79202 egyptian hieroglyph-13562\n79203 egyptian hieroglyph-13563\n79204 egyptian hieroglyph-13564\n79205 egyptian hieroglyph-13565\n79206 egyptian hieroglyph-13566\n79207 egyptian hieroglyph-13567\n79208 egyptian hieroglyph-13568\n79209 egyptian hieroglyph-13569\n79210 egyptian hieroglyph-1356a\n79211 egyptian hieroglyph-1356b\n79212 egyptian hieroglyph-1356c\n79213 egyptian hieroglyph-1356d\n79214 egyptian hieroglyph-1356e\n79215 egyptian hieroglyph-1356f\n79216 egyptian hieroglyph-13570\n79217 egyptian hieroglyph-13571\n79218 egyptian hieroglyph-13572\n79219 egyptian hieroglyph-13573\n79220 egyptian hieroglyph-13574\n79221 egyptian hieroglyph-13575\n79222 egyptian hieroglyph-13576\n79223 egyptian hieroglyph-13577\n79224 egyptian hieroglyph-13578\n79225 egyptian hieroglyph-13579\n79226 egyptian hieroglyph-1357a\n79227 egyptian hieroglyph-1357b\n79228 egyptian hieroglyph-1357c\n79229 egyptian hieroglyph-1357d\n79230 egyptian hieroglyph-1357e\n79231 egyptian hieroglyph-1357f\n79232 egyptian hieroglyph-13580\n79233 egyptian hieroglyph-13581\n79234 egyptian hieroglyph-13582\n79235 egyptian hieroglyph-13583\n79236 egyptian hieroglyph-13584\n79237 egyptian hieroglyph-13585\n79238 egyptian hieroglyph-13586\n79239 egyptian hieroglyph-13587\n79240 egyptian hieroglyph-13588\n79241 egyptian hieroglyph-13589\n79242 egyptian hieroglyph-1358a\n79243 egyptian hieroglyph-1358b\n79244 egyptian hieroglyph-1358c\n79245 egyptian hieroglyph-1358d\n79246 egyptian hieroglyph-1358e\n79247 egyptian hieroglyph-1358f\n79248 egyptian hieroglyph-13590\n79249 egyptian hieroglyph-13591\n79250 egyptian hieroglyph-13592\n79251 egyptian hieroglyph-13593\n79252 egyptian hieroglyph-13594\n79253 egyptian hieroglyph-13595\n79254 egyptian hieroglyph-13596\n79255 egyptian hieroglyph-13597\n79256 egyptian hieroglyph-13598\n79257 egyptian hieroglyph-13599\n79258 egyptian hieroglyph-1359a\n79259 egyptian hieroglyph-1359b\n79260 egyptian hieroglyph-1359c\n79261 egyptian hieroglyph-1359d\n79262 egyptian hieroglyph-1359e\n79263 egyptian hieroglyph-1359f\n79264 egyptian hieroglyph-135a0\n79265 egyptian hieroglyph-135a1\n79266 egyptian hieroglyph-135a2\n79267 egyptian hieroglyph-135a3\n79268 egyptian hieroglyph-135a4\n79269 egyptian hieroglyph-135a5\n79270 egyptian hieroglyph-135a6\n79271 egyptian hieroglyph-135a7\n79272 egyptian hieroglyph-135a8\n79273 egyptian hieroglyph-135a9\n79274 egyptian hieroglyph-135aa\n79275 egyptian hieroglyph-135ab\n79276 egyptian hieroglyph-135ac\n79277 egyptian hieroglyph-135ad\n79278 egyptian hieroglyph-135ae\n79279 egyptian hieroglyph-135af\n79280 egyptian hieroglyph-135b0\n79281 egyptian hieroglyph-135b1\n79282 egyptian hieroglyph-135b2\n79283 egyptian hieroglyph-135b3\n79284 egyptian hieroglyph-135b4\n79285 egyptian hieroglyph-135b5\n79286 egyptian hieroglyph-135b6\n79287 egyptian hieroglyph-135b7\n79288 egyptian hieroglyph-135b8\n79289 egyptian hieroglyph-135b9\n79290 egyptian hieroglyph-135ba\n79291 egyptian hieroglyph-135bb\n79292 egyptian hieroglyph-135bc\n79293 egyptian hieroglyph-135bd\n79294 egyptian hieroglyph-135be\n79295 egyptian hieroglyph-135bf\n79296 egyptian hieroglyph-135c0\n79297 egyptian hieroglyph-135c1\n79298 egyptian hieroglyph-135c2\n79299 egyptian hieroglyph-135c3\n79300 egyptian hieroglyph-135c4\n79301 egyptian hieroglyph-135c5\n79302 egyptian hieroglyph-135c6\n79303 egyptian hieroglyph-135c7\n79304 egyptian hieroglyph-135c8\n79305 egyptian hieroglyph-135c9\n79306 egyptian hieroglyph-135ca\n79307 egyptian hieroglyph-135cb\n79308 egyptian hieroglyph-135cc\n79309 egyptian hieroglyph-135cd\n79310 egyptian hieroglyph-135ce\n79311 egyptian hieroglyph-135cf\n79312 egyptian hieroglyph-135d0\n79313 egyptian hieroglyph-135d1\n79314 egyptian hieroglyph-135d2\n79315 egyptian hieroglyph-135d3\n79316 egyptian hieroglyph-135d4\n79317 egyptian hieroglyph-135d5\n79318 egyptian hieroglyph-135d6\n79319 egyptian hieroglyph-135d7\n79320 egyptian hieroglyph-135d8\n79321 egyptian hieroglyph-135d9\n79322 egyptian hieroglyph-135da\n79323 egyptian hieroglyph-135db\n79324 egyptian hieroglyph-135dc\n79325 egyptian hieroglyph-135dd\n79326 egyptian hieroglyph-135de\n79327 egyptian hieroglyph-135df\n79328 egyptian hieroglyph-135e0\n79329 egyptian hieroglyph-135e1\n79330 egyptian hieroglyph-135e2\n79331 egyptian hieroglyph-135e3\n79332 egyptian hieroglyph-135e4\n79333 egyptian hieroglyph-135e5\n79334 egyptian hieroglyph-135e6\n79335 egyptian hieroglyph-135e7\n79336 egyptian hieroglyph-135e8\n79337 egyptian hieroglyph-135e9\n79338 egyptian hieroglyph-135ea\n79339 egyptian hieroglyph-135eb\n79340 egyptian hieroglyph-135ec\n79341 egyptian hieroglyph-135ed\n79342 egyptian hieroglyph-135ee\n79343 egyptian hieroglyph-135ef\n79344 egyptian hieroglyph-135f0\n79345 egyptian hieroglyph-135f1\n79346 egyptian hieroglyph-135f2\n79347 egyptian hieroglyph-135f3\n79348 egyptian hieroglyph-135f4\n79349 egyptian hieroglyph-135f5\n79350 egyptian hieroglyph-135f6\n79351 egyptian hieroglyph-135f7\n79352 egyptian hieroglyph-135f8\n79353 egyptian hieroglyph-135f9\n79354 egyptian hieroglyph-135fa\n79355 egyptian hieroglyph-135fb\n79356 egyptian hieroglyph-135fc\n79357 egyptian hieroglyph-135fd\n79358 egyptian hieroglyph-135fe\n79359 egyptian hieroglyph-135ff\n79360 egyptian hieroglyph-13600\n79361 egyptian hieroglyph-13601\n79362 egyptian hieroglyph-13602\n79363 egyptian hieroglyph-13603\n79364 egyptian hieroglyph-13604\n79365 egyptian hieroglyph-13605\n79366 egyptian hieroglyph-13606\n79367 egyptian hieroglyph-13607\n79368 egyptian hieroglyph-13608\n79369 egyptian hieroglyph-13609\n79370 egyptian hieroglyph-1360a\n79371 egyptian hieroglyph-1360b\n79372 egyptian hieroglyph-1360c\n79373 egyptian hieroglyph-1360d\n79374 egyptian hieroglyph-1360e\n79375 egyptian hieroglyph-1360f\n79376 egyptian hieroglyph-13610\n79377 egyptian hieroglyph-13611\n79378 egyptian hieroglyph-13612\n79379 egyptian hieroglyph-13613\n79380 egyptian hieroglyph-13614\n79381 egyptian hieroglyph-13615\n79382 egyptian hieroglyph-13616\n79383 egyptian hieroglyph-13617\n79384 egyptian hieroglyph-13618\n79385 egyptian hieroglyph-13619\n79386 egyptian hieroglyph-1361a\n79387 egyptian hieroglyph-1361b\n79388 egyptian hieroglyph-1361c\n79389 egyptian hieroglyph-1361d\n79390 egyptian hieroglyph-1361e\n79391 egyptian hieroglyph-1361f\n79392 egyptian hieroglyph-13620\n79393 egyptian hieroglyph-13621\n79394 egyptian hieroglyph-13622\n79395 egyptian hieroglyph-13623\n79396 egyptian hieroglyph-13624\n79397 egyptian hieroglyph-13625\n79398 egyptian hieroglyph-13626\n79399 egyptian hieroglyph-13627\n79400 egyptian hieroglyph-13628\n79401 egyptian hieroglyph-13629\n79402 egyptian hieroglyph-1362a\n79403 egyptian hieroglyph-1362b\n79404 egyptian hieroglyph-1362c\n79405 egyptian hieroglyph-1362d\n79406 egyptian hieroglyph-1362e\n79407 egyptian hieroglyph-1362f\n79408 egyptian hieroglyph-13630\n79409 egyptian hieroglyph-13631\n79410 egyptian hieroglyph-13632\n79411 egyptian hieroglyph-13633\n79412 egyptian hieroglyph-13634\n79413 egyptian hieroglyph-13635\n79414 egyptian hieroglyph-13636\n79415 egyptian hieroglyph-13637\n79416 egyptian hieroglyph-13638\n79417 egyptian hieroglyph-13639\n79418 egyptian hieroglyph-1363a\n79419 egyptian hieroglyph-1363b\n79420 egyptian hieroglyph-1363c\n79421 egyptian hieroglyph-1363d\n79422 egyptian hieroglyph-1363e\n79423 egyptian hieroglyph-1363f\n79424 egyptian hieroglyph-13640\n79425 egyptian hieroglyph-13641\n79426 egyptian hieroglyph-13642\n79427 egyptian hieroglyph-13643\n79428 egyptian hieroglyph-13644\n79429 egyptian hieroglyph-13645\n79430 egyptian hieroglyph-13646\n79431 egyptian hieroglyph-13647\n79432 egyptian hieroglyph-13648\n79433 egyptian hieroglyph-13649\n79434 egyptian hieroglyph-1364a\n79435 egyptian hieroglyph-1364b\n79436 egyptian hieroglyph-1364c\n79437 egyptian hieroglyph-1364d\n79438 egyptian hieroglyph-1364e\n79439 egyptian hieroglyph-1364f\n79440 egyptian hieroglyph-13650\n79441 egyptian hieroglyph-13651\n79442 egyptian hieroglyph-13652\n79443 egyptian hieroglyph-13653\n79444 egyptian hieroglyph-13654\n79445 egyptian hieroglyph-13655\n79446 egyptian hieroglyph-13656\n79447 egyptian hieroglyph-13657\n79448 egyptian hieroglyph-13658\n79449 egyptian hieroglyph-13659\n79450 egyptian hieroglyph-1365a\n79451 egyptian hieroglyph-1365b\n79452 egyptian hieroglyph-1365c\n79453 egyptian hieroglyph-1365d\n79454 egyptian hieroglyph-1365e\n79455 egyptian hieroglyph-1365f\n79456 egyptian hieroglyph-13660\n79457 egyptian hieroglyph-13661\n79458 egyptian hieroglyph-13662\n79459 egyptian hieroglyph-13663\n79460 egyptian hieroglyph-13664\n79461 egyptian hieroglyph-13665\n79462 egyptian hieroglyph-13666\n79463 egyptian hieroglyph-13667\n79464 egyptian hieroglyph-13668\n79465 egyptian hieroglyph-13669\n79466 egyptian hieroglyph-1366a\n79467 egyptian hieroglyph-1366b\n79468 egyptian hieroglyph-1366c\n79469 egyptian hieroglyph-1366d\n79470 egyptian hieroglyph-1366e\n79471 egyptian hieroglyph-1366f\n79472 egyptian hieroglyph-13670\n79473 egyptian hieroglyph-13671\n79474 egyptian hieroglyph-13672\n79475 egyptian hieroglyph-13673\n79476 egyptian hieroglyph-13674\n79477 egyptian hieroglyph-13675\n79478 egyptian hieroglyph-13676\n79479 egyptian hieroglyph-13677\n79480 egyptian hieroglyph-13678\n79481 egyptian hieroglyph-13679\n79482 egyptian hieroglyph-1367a\n79483 egyptian hieroglyph-1367b\n79484 egyptian hieroglyph-1367c\n79485 egyptian hieroglyph-1367d\n79486 egyptian hieroglyph-1367e\n79487 egyptian hieroglyph-1367f\n79488 egyptian hieroglyph-13680\n79489 egyptian hieroglyph-13681\n79490 egyptian hieroglyph-13682\n79491 egyptian hieroglyph-13683\n79492 egyptian hieroglyph-13684\n79493 egyptian hieroglyph-13685\n79494 egyptian hieroglyph-13686\n79495 egyptian hieroglyph-13687\n79496 egyptian hieroglyph-13688\n79497 egyptian hieroglyph-13689\n79498 egyptian hieroglyph-1368a\n79499 egyptian hieroglyph-1368b\n79500 egyptian hieroglyph-1368c\n79501 egyptian hieroglyph-1368d\n79502 egyptian hieroglyph-1368e\n79503 egyptian hieroglyph-1368f\n79504 egyptian hieroglyph-13690\n79505 egyptian hieroglyph-13691\n79506 egyptian hieroglyph-13692\n79507 egyptian hieroglyph-13693\n79508 egyptian hieroglyph-13694\n79509 egyptian hieroglyph-13695\n79510 egyptian hieroglyph-13696\n79511 egyptian hieroglyph-13697\n79512 egyptian hieroglyph-13698\n79513 egyptian hieroglyph-13699\n79514 egyptian hieroglyph-1369a\n79515 egyptian hieroglyph-1369b\n79516 egyptian hieroglyph-1369c\n79517 egyptian hieroglyph-1369d\n79518 egyptian hieroglyph-1369e\n79519 egyptian hieroglyph-1369f\n79520 egyptian hieroglyph-136a0\n79521 egyptian hieroglyph-136a1\n79522 egyptian hieroglyph-136a2\n79523 egyptian hieroglyph-136a3\n79524 egyptian hieroglyph-136a4\n79525 egyptian hieroglyph-136a5\n79526 egyptian hieroglyph-136a6\n79527 egyptian hieroglyph-136a7\n79528 egyptian hieroglyph-136a8\n79529 egyptian hieroglyph-136a9\n79530 egyptian hieroglyph-136aa\n79531 egyptian hieroglyph-136ab\n79532 egyptian hieroglyph-136ac\n79533 egyptian hieroglyph-136ad\n79534 egyptian hieroglyph-136ae\n79535 egyptian hieroglyph-136af\n79536 egyptian hieroglyph-136b0\n79537 egyptian hieroglyph-136b1\n79538 egyptian hieroglyph-136b2\n79539 egyptian hieroglyph-136b3\n79540 egyptian hieroglyph-136b4\n79541 egyptian hieroglyph-136b5\n79542 egyptian hieroglyph-136b6\n79543 egyptian hieroglyph-136b7\n79544 egyptian hieroglyph-136b8\n79545 egyptian hieroglyph-136b9\n79546 egyptian hieroglyph-136ba\n79547 egyptian hieroglyph-136bb\n79548 egyptian hieroglyph-136bc\n79549 egyptian hieroglyph-136bd\n79550 egyptian hieroglyph-136be\n79551 egyptian hieroglyph-136bf\n79552 egyptian hieroglyph-136c0\n79553 egyptian hieroglyph-136c1\n79554 egyptian hieroglyph-136c2\n79555 egyptian hieroglyph-136c3\n79556 egyptian hieroglyph-136c4\n79557 egyptian hieroglyph-136c5\n79558 egyptian hieroglyph-136c6\n79559 egyptian hieroglyph-136c7\n79560 egyptian hieroglyph-136c8\n79561 egyptian hieroglyph-136c9\n79562 egyptian hieroglyph-136ca\n79563 egyptian hieroglyph-136cb\n79564 egyptian hieroglyph-136cc\n79565 egyptian hieroglyph-136cd\n79566 egyptian hieroglyph-136ce\n79567 egyptian hieroglyph-136cf\n79568 egyptian hieroglyph-136d0\n79569 egyptian hieroglyph-136d1\n79570 egyptian hieroglyph-136d2\n79571 egyptian hieroglyph-136d3\n79572 egyptian hieroglyph-136d4\n79573 egyptian hieroglyph-136d5\n79574 egyptian hieroglyph-136d6\n79575 egyptian hieroglyph-136d7\n79576 egyptian hieroglyph-136d8\n79577 egyptian hieroglyph-136d9\n79578 egyptian hieroglyph-136da\n79579 egyptian hieroglyph-136db\n79580 egyptian hieroglyph-136dc\n79581 egyptian hieroglyph-136dd\n79582 egyptian hieroglyph-136de\n79583 egyptian hieroglyph-136df\n79584 egyptian hieroglyph-136e0\n79585 egyptian hieroglyph-136e1\n79586 egyptian hieroglyph-136e2\n79587 egyptian hieroglyph-136e3\n79588 egyptian hieroglyph-136e4\n79589 egyptian hieroglyph-136e5\n79590 egyptian hieroglyph-136e6\n79591 egyptian hieroglyph-136e7\n79592 egyptian hieroglyph-136e8\n79593 egyptian hieroglyph-136e9\n79594 egyptian hieroglyph-136ea\n79595 egyptian hieroglyph-136eb\n79596 egyptian hieroglyph-136ec\n79597 egyptian hieroglyph-136ed\n79598 egyptian hieroglyph-136ee\n79599 egyptian hieroglyph-136ef\n79600 egyptian hieroglyph-136f0\n79601 egyptian hieroglyph-136f1\n79602 egyptian hieroglyph-136f2\n79603 egyptian hieroglyph-136f3\n79604 egyptian hieroglyph-136f4\n79605 egyptian hieroglyph-136f5\n79606 egyptian hieroglyph-136f6\n79607 egyptian hieroglyph-136f7\n79608 egyptian hieroglyph-136f8\n79609 egyptian hieroglyph-136f9\n79610 egyptian hieroglyph-136fa\n79611 egyptian hieroglyph-136fb\n79612 egyptian hieroglyph-136fc\n79613 egyptian hieroglyph-136fd\n79614 egyptian hieroglyph-136fe\n79615 egyptian hieroglyph-136ff\n79616 egyptian hieroglyph-13700\n79617 egyptian hieroglyph-13701\n79618 egyptian hieroglyph-13702\n79619 egyptian hieroglyph-13703\n79620 egyptian hieroglyph-13704\n79621 egyptian hieroglyph-13705\n79622 egyptian hieroglyph-13706\n79623 egyptian hieroglyph-13707\n79624 egyptian hieroglyph-13708\n79625 egyptian hieroglyph-13709\n79626 egyptian hieroglyph-1370a\n79627 egyptian hieroglyph-1370b\n79628 egyptian hieroglyph-1370c\n79629 egyptian hieroglyph-1370d\n79630 egyptian hieroglyph-1370e\n79631 egyptian hieroglyph-1370f\n79632 egyptian hieroglyph-13710\n79633 egyptian hieroglyph-13711\n79634 egyptian hieroglyph-13712\n79635 egyptian hieroglyph-13713\n79636 egyptian hieroglyph-13714\n79637 egyptian hieroglyph-13715\n79638 egyptian hieroglyph-13716\n79639 egyptian hieroglyph-13717\n79640 egyptian hieroglyph-13718\n79641 egyptian hieroglyph-13719\n79642 egyptian hieroglyph-1371a\n79643 egyptian hieroglyph-1371b\n79644 egyptian hieroglyph-1371c\n79645 egyptian hieroglyph-1371d\n79646 egyptian hieroglyph-1371e\n79647 egyptian hieroglyph-1371f\n79648 egyptian hieroglyph-13720\n79649 egyptian hieroglyph-13721\n79650 egyptian hieroglyph-13722\n79651 egyptian hieroglyph-13723\n79652 egyptian hieroglyph-13724\n79653 egyptian hieroglyph-13725\n79654 egyptian hieroglyph-13726\n79655 egyptian hieroglyph-13727\n79656 egyptian hieroglyph-13728\n79657 egyptian hieroglyph-13729\n79658 egyptian hieroglyph-1372a\n79659 egyptian hieroglyph-1372b\n79660 egyptian hieroglyph-1372c\n79661 egyptian hieroglyph-1372d\n79662 egyptian hieroglyph-1372e\n79663 egyptian hieroglyph-1372f\n79664 egyptian hieroglyph-13730\n79665 egyptian hieroglyph-13731\n79666 egyptian hieroglyph-13732\n79667 egyptian hieroglyph-13733\n79668 egyptian hieroglyph-13734\n79669 egyptian hieroglyph-13735\n79670 egyptian hieroglyph-13736\n79671 egyptian hieroglyph-13737\n79672 egyptian hieroglyph-13738\n79673 egyptian hieroglyph-13739\n79674 egyptian hieroglyph-1373a\n79675 egyptian hieroglyph-1373b\n79676 egyptian hieroglyph-1373c\n79677 egyptian hieroglyph-1373d\n79678 egyptian hieroglyph-1373e\n79679 egyptian hieroglyph-1373f\n79680 egyptian hieroglyph-13740\n79681 egyptian hieroglyph-13741\n79682 egyptian hieroglyph-13742\n79683 egyptian hieroglyph-13743\n79684 egyptian hieroglyph-13744\n79685 egyptian hieroglyph-13745\n79686 egyptian hieroglyph-13746\n79687 egyptian hieroglyph-13747\n79688 egyptian hieroglyph-13748\n79689 egyptian hieroglyph-13749\n79690 egyptian hieroglyph-1374a\n79691 egyptian hieroglyph-1374b\n79692 egyptian hieroglyph-1374c\n79693 egyptian hieroglyph-1374d\n79694 egyptian hieroglyph-1374e\n79695 egyptian hieroglyph-1374f\n79696 egyptian hieroglyph-13750\n79697 egyptian hieroglyph-13751\n79698 egyptian hieroglyph-13752\n79699 egyptian hieroglyph-13753\n79700 egyptian hieroglyph-13754\n79701 egyptian hieroglyph-13755\n79702 egyptian hieroglyph-13756\n79703 egyptian hieroglyph-13757\n79704 egyptian hieroglyph-13758\n79705 egyptian hieroglyph-13759\n79706 egyptian hieroglyph-1375a\n79707 egyptian hieroglyph-1375b\n79708 egyptian hieroglyph-1375c\n79709 egyptian hieroglyph-1375d\n79710 egyptian hieroglyph-1375e\n79711 egyptian hieroglyph-1375f\n79712 egyptian hieroglyph-13760\n79713 egyptian hieroglyph-13761\n79714 egyptian hieroglyph-13762\n79715 egyptian hieroglyph-13763\n79716 egyptian hieroglyph-13764\n79717 egyptian hieroglyph-13765\n79718 egyptian hieroglyph-13766\n79719 egyptian hieroglyph-13767\n79720 egyptian hieroglyph-13768\n79721 egyptian hieroglyph-13769\n79722 egyptian hieroglyph-1376a\n79723 egyptian hieroglyph-1376b\n79724 egyptian hieroglyph-1376c\n79725 egyptian hieroglyph-1376d\n79726 egyptian hieroglyph-1376e\n79727 egyptian hieroglyph-1376f\n79728 egyptian hieroglyph-13770\n79729 egyptian hieroglyph-13771\n79730 egyptian hieroglyph-13772\n79731 egyptian hieroglyph-13773\n79732 egyptian hieroglyph-13774\n79733 egyptian hieroglyph-13775\n79734 egyptian hieroglyph-13776\n79735 egyptian hieroglyph-13777\n79736 egyptian hieroglyph-13778\n79737 egyptian hieroglyph-13779\n79738 egyptian hieroglyph-1377a\n79739 egyptian hieroglyph-1377b\n79740 egyptian hieroglyph-1377c\n79741 egyptian hieroglyph-1377d\n79742 egyptian hieroglyph-1377e\n79743 egyptian hieroglyph-1377f\n79744 egyptian hieroglyph-13780\n79745 egyptian hieroglyph-13781\n79746 egyptian hieroglyph-13782\n79747 egyptian hieroglyph-13783\n79748 egyptian hieroglyph-13784\n79749 egyptian hieroglyph-13785\n79750 egyptian hieroglyph-13786\n79751 egyptian hieroglyph-13787\n79752 egyptian hieroglyph-13788\n79753 egyptian hieroglyph-13789\n79754 egyptian hieroglyph-1378a\n79755 egyptian hieroglyph-1378b\n79756 egyptian hieroglyph-1378c\n79757 egyptian hieroglyph-1378d\n79758 egyptian hieroglyph-1378e\n79759 egyptian hieroglyph-1378f\n79760 egyptian hieroglyph-13790\n79761 egyptian hieroglyph-13791\n79762 egyptian hieroglyph-13792\n79763 egyptian hieroglyph-13793\n79764 egyptian hieroglyph-13794\n79765 egyptian hieroglyph-13795\n79766 egyptian hieroglyph-13796\n79767 egyptian hieroglyph-13797\n79768 egyptian hieroglyph-13798\n79769 egyptian hieroglyph-13799\n79770 egyptian hieroglyph-1379a\n79771 egyptian hieroglyph-1379b\n79772 egyptian hieroglyph-1379c\n79773 egyptian hieroglyph-1379d\n79774 egyptian hieroglyph-1379e\n79775 egyptian hieroglyph-1379f\n79776 egyptian hieroglyph-137a0\n79777 egyptian hieroglyph-137a1\n79778 egyptian hieroglyph-137a2\n79779 egyptian hieroglyph-137a3\n79780 egyptian hieroglyph-137a4\n79781 egyptian hieroglyph-137a5\n79782 egyptian hieroglyph-137a6\n79783 egyptian hieroglyph-137a7\n79784 egyptian hieroglyph-137a8\n79785 egyptian hieroglyph-137a9\n79786 egyptian hieroglyph-137aa\n79787 egyptian hieroglyph-137ab\n79788 egyptian hieroglyph-137ac\n79789 egyptian hieroglyph-137ad\n79790 egyptian hieroglyph-137ae\n79791 egyptian hieroglyph-137af\n79792 egyptian hieroglyph-137b0\n79793 egyptian hieroglyph-137b1\n79794 egyptian hieroglyph-137b2\n79795 egyptian hieroglyph-137b3\n79796 egyptian hieroglyph-137b4\n79797 egyptian hieroglyph-137b5\n79798 egyptian hieroglyph-137b6\n79799 egyptian hieroglyph-137b7\n79800 egyptian hieroglyph-137b8\n79801 egyptian hieroglyph-137b9\n79802 egyptian hieroglyph-137ba\n79803 egyptian hieroglyph-137bb\n79804 egyptian hieroglyph-137bc\n79805 egyptian hieroglyph-137bd\n79806 egyptian hieroglyph-137be\n79807 egyptian hieroglyph-137bf\n79808 egyptian hieroglyph-137c0\n79809 egyptian hieroglyph-137c1\n79810 egyptian hieroglyph-137c2\n79811 egyptian hieroglyph-137c3\n79812 egyptian hieroglyph-137c4\n79813 egyptian hieroglyph-137c5\n79814 egyptian hieroglyph-137c6\n79815 egyptian hieroglyph-137c7\n79816 egyptian hieroglyph-137c8\n79817 egyptian hieroglyph-137c9\n79818 egyptian hieroglyph-137ca\n79819 egyptian hieroglyph-137cb\n79820 egyptian hieroglyph-137cc\n79821 egyptian hieroglyph-137cd\n79822 egyptian hieroglyph-137ce\n79823 egyptian hieroglyph-137cf\n79824 egyptian hieroglyph-137d0\n79825 egyptian hieroglyph-137d1\n79826 egyptian hieroglyph-137d2\n79827 egyptian hieroglyph-137d3\n79828 egyptian hieroglyph-137d4\n79829 egyptian hieroglyph-137d5\n79830 egyptian hieroglyph-137d6\n79831 egyptian hieroglyph-137d7\n79832 egyptian hieroglyph-137d8\n79833 egyptian hieroglyph-137d9\n79834 egyptian hieroglyph-137da\n79835 egyptian hieroglyph-137db\n79836 egyptian hieroglyph-137dc\n79837 egyptian hieroglyph-137dd\n79838 egyptian hieroglyph-137de\n79839 egyptian hieroglyph-137df\n79840 egyptian hieroglyph-137e0\n79841 egyptian hieroglyph-137e1\n79842 egyptian hieroglyph-137e2\n79843 egyptian hieroglyph-137e3\n79844 egyptian hieroglyph-137e4\n79845 egyptian hieroglyph-137e5\n79846 egyptian hieroglyph-137e6\n79847 egyptian hieroglyph-137e7\n79848 egyptian hieroglyph-137e8\n79849 egyptian hieroglyph-137e9\n79850 egyptian hieroglyph-137ea\n79851 egyptian hieroglyph-137eb\n79852 egyptian hieroglyph-137ec\n79853 egyptian hieroglyph-137ed\n79854 egyptian hieroglyph-137ee\n79855 egyptian hieroglyph-137ef\n79856 egyptian hieroglyph-137f0\n79857 egyptian hieroglyph-137f1\n79858 egyptian hieroglyph-137f2\n79859 egyptian hieroglyph-137f3\n79860 egyptian hieroglyph-137f4\n79861 egyptian hieroglyph-137f5\n79862 egyptian hieroglyph-137f6\n79863 egyptian hieroglyph-137f7\n79864 egyptian hieroglyph-137f8\n79865 egyptian hieroglyph-137f9\n79866 egyptian hieroglyph-137fa\n79867 egyptian hieroglyph-137fb\n79868 egyptian hieroglyph-137fc\n79869 egyptian hieroglyph-137fd\n79870 egyptian hieroglyph-137fe\n79871 egyptian hieroglyph-137ff\n79872 egyptian hieroglyph-13800\n79873 egyptian hieroglyph-13801\n79874 egyptian hieroglyph-13802\n79875 egyptian hieroglyph-13803\n79876 egyptian hieroglyph-13804\n79877 egyptian hieroglyph-13805\n79878 egyptian hieroglyph-13806\n79879 egyptian hieroglyph-13807\n79880 egyptian hieroglyph-13808\n79881 egyptian hieroglyph-13809\n79882 egyptian hieroglyph-1380a\n79883 egyptian hieroglyph-1380b\n79884 egyptian hieroglyph-1380c\n79885 egyptian hieroglyph-1380d\n79886 egyptian hieroglyph-1380e\n79887 egyptian hieroglyph-1380f\n79888 egyptian hieroglyph-13810\n79889 egyptian hieroglyph-13811\n79890 egyptian hieroglyph-13812\n79891 egyptian hieroglyph-13813\n79892 egyptian hieroglyph-13814\n79893 egyptian hieroglyph-13815\n79894 egyptian hieroglyph-13816\n79895 egyptian hieroglyph-13817\n79896 egyptian hieroglyph-13818\n79897 egyptian hieroglyph-13819\n79898 egyptian hieroglyph-1381a\n79899 egyptian hieroglyph-1381b\n79900 egyptian hieroglyph-1381c\n79901 egyptian hieroglyph-1381d\n79902 egyptian hieroglyph-1381e\n79903 egyptian hieroglyph-1381f\n79904 egyptian hieroglyph-13820\n79905 egyptian hieroglyph-13821\n79906 egyptian hieroglyph-13822\n79907 egyptian hieroglyph-13823\n79908 egyptian hieroglyph-13824\n79909 egyptian hieroglyph-13825\n79910 egyptian hieroglyph-13826\n79911 egyptian hieroglyph-13827\n79912 egyptian hieroglyph-13828\n79913 egyptian hieroglyph-13829\n79914 egyptian hieroglyph-1382a\n79915 egyptian hieroglyph-1382b\n79916 egyptian hieroglyph-1382c\n79917 egyptian hieroglyph-1382d\n79918 egyptian hieroglyph-1382e\n79919 egyptian hieroglyph-1382f\n79920 egyptian hieroglyph-13830\n79921 egyptian hieroglyph-13831\n79922 egyptian hieroglyph-13832\n79923 egyptian hieroglyph-13833\n79924 egyptian hieroglyph-13834\n79925 egyptian hieroglyph-13835\n79926 egyptian hieroglyph-13836\n79927 egyptian hieroglyph-13837\n79928 egyptian hieroglyph-13838\n79929 egyptian hieroglyph-13839\n79930 egyptian hieroglyph-1383a\n79931 egyptian hieroglyph-1383b\n79932 egyptian hieroglyph-1383c\n79933 egyptian hieroglyph-1383d\n79934 egyptian hieroglyph-1383e\n79935 egyptian hieroglyph-1383f\n79936 egyptian hieroglyph-13840\n79937 egyptian hieroglyph-13841\n79938 egyptian hieroglyph-13842\n79939 egyptian hieroglyph-13843\n79940 egyptian hieroglyph-13844\n79941 egyptian hieroglyph-13845\n79942 egyptian hieroglyph-13846\n79943 egyptian hieroglyph-13847\n79944 egyptian hieroglyph-13848\n79945 egyptian hieroglyph-13849\n79946 egyptian hieroglyph-1384a\n79947 egyptian hieroglyph-1384b\n79948 egyptian hieroglyph-1384c\n79949 egyptian hieroglyph-1384d\n79950 egyptian hieroglyph-1384e\n79951 egyptian hieroglyph-1384f\n79952 egyptian hieroglyph-13850\n79953 egyptian hieroglyph-13851\n79954 egyptian hieroglyph-13852\n79955 egyptian hieroglyph-13853\n79956 egyptian hieroglyph-13854\n79957 egyptian hieroglyph-13855\n79958 egyptian hieroglyph-13856\n79959 egyptian hieroglyph-13857\n79960 egyptian hieroglyph-13858\n79961 egyptian hieroglyph-13859\n79962 egyptian hieroglyph-1385a\n79963 egyptian hieroglyph-1385b\n79964 egyptian hieroglyph-1385c\n79965 egyptian hieroglyph-1385d\n79966 egyptian hieroglyph-1385e\n79967 egyptian hieroglyph-1385f\n79968 egyptian hieroglyph-13860\n79969 egyptian hieroglyph-13861\n79970 egyptian hieroglyph-13862\n79971 egyptian hieroglyph-13863\n79972 egyptian hieroglyph-13864\n79973 egyptian hieroglyph-13865\n79974 egyptian hieroglyph-13866\n79975 egyptian hieroglyph-13867\n79976 egyptian hieroglyph-13868\n79977 egyptian hieroglyph-13869\n79978 egyptian hieroglyph-1386a\n79979 egyptian hieroglyph-1386b\n79980 egyptian hieroglyph-1386c\n79981 egyptian hieroglyph-1386d\n79982 egyptian hieroglyph-1386e\n79983 egyptian hieroglyph-1386f\n79984 egyptian hieroglyph-13870\n79985 egyptian hieroglyph-13871\n79986 egyptian hieroglyph-13872\n79987 egyptian hieroglyph-13873\n79988 egyptian hieroglyph-13874\n79989 egyptian hieroglyph-13875\n79990 egyptian hieroglyph-13876\n79991 egyptian hieroglyph-13877\n79992 egyptian hieroglyph-13878\n79993 egyptian hieroglyph-13879\n79994 egyptian hieroglyph-1387a\n79995 egyptian hieroglyph-1387b\n79996 egyptian hieroglyph-1387c\n79997 egyptian hieroglyph-1387d\n79998 egyptian hieroglyph-1387e\n79999 egyptian hieroglyph-1387f\n80000 egyptian hieroglyph-13880\n80001 egyptian hieroglyph-13881\n80002 egyptian hieroglyph-13882\n80003 egyptian hieroglyph-13883\n80004 egyptian hieroglyph-13884\n80005 egyptian hieroglyph-13885\n80006 egyptian hieroglyph-13886\n80007 egyptian hieroglyph-13887\n80008 egyptian hieroglyph-13888\n80009 egyptian hieroglyph-13889\n80010 egyptian hieroglyph-1388a\n80011 egyptian hieroglyph-1388b\n80012 egyptian hieroglyph-1388c\n80013 egyptian hieroglyph-1388d\n80014 egyptian hieroglyph-1388e\n80015 egyptian hieroglyph-1388f\n80016 egyptian hieroglyph-13890\n80017 egyptian hieroglyph-13891\n80018 egyptian hieroglyph-13892\n80019 egyptian hieroglyph-13893\n80020 egyptian hieroglyph-13894\n80021 egyptian hieroglyph-13895\n80022 egyptian hieroglyph-13896\n80023 egyptian hieroglyph-13897\n80024 egyptian hieroglyph-13898\n80025 egyptian hieroglyph-13899\n80026 egyptian hieroglyph-1389a\n80027 egyptian hieroglyph-1389b\n80028 egyptian hieroglyph-1389c\n80029 egyptian hieroglyph-1389d\n80030 egyptian hieroglyph-1389e\n80031 egyptian hieroglyph-1389f\n80032 egyptian hieroglyph-138a0\n80033 egyptian hieroglyph-138a1\n80034 egyptian hieroglyph-138a2\n80035 egyptian hieroglyph-138a3\n80036 egyptian hieroglyph-138a4\n80037 egyptian hieroglyph-138a5\n80038 egyptian hieroglyph-138a6\n80039 egyptian hieroglyph-138a7\n80040 egyptian hieroglyph-138a8\n80041 egyptian hieroglyph-138a9\n80042 egyptian hieroglyph-138aa\n80043 egyptian hieroglyph-138ab\n80044 egyptian hieroglyph-138ac\n80045 egyptian hieroglyph-138ad\n80046 egyptian hieroglyph-138ae\n80047 egyptian hieroglyph-138af\n80048 egyptian hieroglyph-138b0\n80049 egyptian hieroglyph-138b1\n80050 egyptian hieroglyph-138b2\n80051 egyptian hieroglyph-138b3\n80052 egyptian hieroglyph-138b4\n80053 egyptian hieroglyph-138b5\n80054 egyptian hieroglyph-138b6\n80055 egyptian hieroglyph-138b7\n80056 egyptian hieroglyph-138b8\n80057 egyptian hieroglyph-138b9\n80058 egyptian hieroglyph-138ba\n80059 egyptian hieroglyph-138bb\n80060 egyptian hieroglyph-138bc\n80061 egyptian hieroglyph-138bd\n80062 egyptian hieroglyph-138be\n80063 egyptian hieroglyph-138bf\n80064 egyptian hieroglyph-138c0\n80065 egyptian hieroglyph-138c1\n80066 egyptian hieroglyph-138c2\n80067 egyptian hieroglyph-138c3\n80068 egyptian hieroglyph-138c4\n80069 egyptian hieroglyph-138c5\n80070 egyptian hieroglyph-138c6\n80071 egyptian hieroglyph-138c7\n80072 egyptian hieroglyph-138c8\n80073 egyptian hieroglyph-138c9\n80074 egyptian hieroglyph-138ca\n80075 egyptian hieroglyph-138cb\n80076 egyptian hieroglyph-138cc\n80077 egyptian hieroglyph-138cd\n80078 egyptian hieroglyph-138ce\n80079 egyptian hieroglyph-138cf\n80080 egyptian hieroglyph-138d0\n80081 egyptian hieroglyph-138d1\n80082 egyptian hieroglyph-138d2\n80083 egyptian hieroglyph-138d3\n80084 egyptian hieroglyph-138d4\n80085 egyptian hieroglyph-138d5\n80086 egyptian hieroglyph-138d6\n80087 egyptian hieroglyph-138d7\n80088 egyptian hieroglyph-138d8\n80089 egyptian hieroglyph-138d9\n80090 egyptian hieroglyph-138da\n80091 egyptian hieroglyph-138db\n80092 egyptian hieroglyph-138dc\n80093 egyptian hieroglyph-138dd\n80094 egyptian hieroglyph-138de\n80095 egyptian hieroglyph-138df\n80096 egyptian hieroglyph-138e0\n80097 egyptian hieroglyph-138e1\n80098 egyptian hieroglyph-138e2\n80099 egyptian hieroglyph-138e3\n80100 egyptian hieroglyph-138e4\n80101 egyptian hieroglyph-138e5\n80102 egyptian hieroglyph-138e6\n80103 egyptian hieroglyph-138e7\n80104 egyptian hieroglyph-138e8\n80105 egyptian hieroglyph-138e9\n80106 egyptian hieroglyph-138ea\n80107 egyptian hieroglyph-138eb\n80108 egyptian hieroglyph-138ec\n80109 egyptian hieroglyph-138ed\n80110 egyptian hieroglyph-138ee\n80111 egyptian hieroglyph-138ef\n80112 egyptian hieroglyph-138f0\n80113 egyptian hieroglyph-138f1\n80114 egyptian hieroglyph-138f2\n80115 egyptian hieroglyph-138f3\n80116 egyptian hieroglyph-138f4\n80117 egyptian hieroglyph-138f5\n80118 egyptian hieroglyph-138f6\n80119 egyptian hieroglyph-138f7\n80120 egyptian hieroglyph-138f8\n80121 egyptian hieroglyph-138f9\n80122 egyptian hieroglyph-138fa\n80123 egyptian hieroglyph-138fb\n80124 egyptian hieroglyph-138fc\n80125 egyptian hieroglyph-138fd\n80126 egyptian hieroglyph-138fe\n80127 egyptian hieroglyph-138ff\n80128 egyptian hieroglyph-13900\n80129 egyptian hieroglyph-13901\n80130 egyptian hieroglyph-13902\n80131 egyptian hieroglyph-13903\n80132 egyptian hieroglyph-13904\n80133 egyptian hieroglyph-13905\n80134 egyptian hieroglyph-13906\n80135 egyptian hieroglyph-13907\n80136 egyptian hieroglyph-13908\n80137 egyptian hieroglyph-13909\n80138 egyptian hieroglyph-1390a\n80139 egyptian hieroglyph-1390b\n80140 egyptian hieroglyph-1390c\n80141 egyptian hieroglyph-1390d\n80142 egyptian hieroglyph-1390e\n80143 egyptian hieroglyph-1390f\n80144 egyptian hieroglyph-13910\n80145 egyptian hieroglyph-13911\n80146 egyptian hieroglyph-13912\n80147 egyptian hieroglyph-13913\n80148 egyptian hieroglyph-13914\n80149 egyptian hieroglyph-13915\n80150 egyptian hieroglyph-13916\n80151 egyptian hieroglyph-13917\n80152 egyptian hieroglyph-13918\n80153 egyptian hieroglyph-13919\n80154 egyptian hieroglyph-1391a\n80155 egyptian hieroglyph-1391b\n80156 egyptian hieroglyph-1391c\n80157 egyptian hieroglyph-1391d\n80158 egyptian hieroglyph-1391e\n80159 egyptian hieroglyph-1391f\n80160 egyptian hieroglyph-13920\n80161 egyptian hieroglyph-13921\n80162 egyptian hieroglyph-13922\n80163 egyptian hieroglyph-13923\n80164 egyptian hieroglyph-13924\n80165 egyptian hieroglyph-13925\n80166 egyptian hieroglyph-13926\n80167 egyptian hieroglyph-13927\n80168 egyptian hieroglyph-13928\n80169 egyptian hieroglyph-13929\n80170 egyptian hieroglyph-1392a\n80171 egyptian hieroglyph-1392b\n80172 egyptian hieroglyph-1392c\n80173 egyptian hieroglyph-1392d\n80174 egyptian hieroglyph-1392e\n80175 egyptian hieroglyph-1392f\n80176 egyptian hieroglyph-13930\n80177 egyptian hieroglyph-13931\n80178 egyptian hieroglyph-13932\n80179 egyptian hieroglyph-13933\n80180 egyptian hieroglyph-13934\n80181 egyptian hieroglyph-13935\n80182 egyptian hieroglyph-13936\n80183 egyptian hieroglyph-13937\n80184 egyptian hieroglyph-13938\n80185 egyptian hieroglyph-13939\n80186 egyptian hieroglyph-1393a\n80187 egyptian hieroglyph-1393b\n80188 egyptian hieroglyph-1393c\n80189 egyptian hieroglyph-1393d\n80190 egyptian hieroglyph-1393e\n80191 egyptian hieroglyph-1393f\n80192 egyptian hieroglyph-13940\n80193 egyptian hieroglyph-13941\n80194 egyptian hieroglyph-13942\n80195 egyptian hieroglyph-13943\n80196 egyptian hieroglyph-13944\n80197 egyptian hieroglyph-13945\n80198 egyptian hieroglyph-13946\n80199 egyptian hieroglyph-13947\n80200 egyptian hieroglyph-13948\n80201 egyptian hieroglyph-13949\n80202 egyptian hieroglyph-1394a\n80203 egyptian hieroglyph-1394b\n80204 egyptian hieroglyph-1394c\n80205 egyptian hieroglyph-1394d\n80206 egyptian hieroglyph-1394e\n80207 egyptian hieroglyph-1394f\n80208 egyptian hieroglyph-13950\n80209 egyptian hieroglyph-13951\n80210 egyptian hieroglyph-13952\n80211 egyptian hieroglyph-13953\n80212 egyptian hieroglyph-13954\n80213 egyptian hieroglyph-13955\n80214 egyptian hieroglyph-13956\n80215 egyptian hieroglyph-13957\n80216 egyptian hieroglyph-13958\n80217 egyptian hieroglyph-13959\n80218 egyptian hieroglyph-1395a\n80219 egyptian hieroglyph-1395b\n80220 egyptian hieroglyph-1395c\n80221 egyptian hieroglyph-1395d\n80222 egyptian hieroglyph-1395e\n80223 egyptian hieroglyph-1395f\n80224 egyptian hieroglyph-13960\n80225 egyptian hieroglyph-13961\n80226 egyptian hieroglyph-13962\n80227 egyptian hieroglyph-13963\n80228 egyptian hieroglyph-13964\n80229 egyptian hieroglyph-13965\n80230 egyptian hieroglyph-13966\n80231 egyptian hieroglyph-13967\n80232 egyptian hieroglyph-13968\n80233 egyptian hieroglyph-13969\n80234 egyptian hieroglyph-1396a\n80235 egyptian hieroglyph-1396b\n80236 egyptian hieroglyph-1396c\n80237 egyptian hieroglyph-1396d\n80238 egyptian hieroglyph-1396e\n80239 egyptian hieroglyph-1396f\n80240 egyptian hieroglyph-13970\n80241 egyptian hieroglyph-13971\n80242 egyptian hieroglyph-13972\n80243 egyptian hieroglyph-13973\n80244 egyptian hieroglyph-13974\n80245 egyptian hieroglyph-13975\n80246 egyptian hieroglyph-13976\n80247 egyptian hieroglyph-13977\n80248 egyptian hieroglyph-13978\n80249 egyptian hieroglyph-13979\n80250 egyptian hieroglyph-1397a\n80251 egyptian hieroglyph-1397b\n80252 egyptian hieroglyph-1397c\n80253 egyptian hieroglyph-1397d\n80254 egyptian hieroglyph-1397e\n80255 egyptian hieroglyph-1397f\n80256 egyptian hieroglyph-13980\n80257 egyptian hieroglyph-13981\n80258 egyptian hieroglyph-13982\n80259 egyptian hieroglyph-13983\n80260 egyptian hieroglyph-13984\n80261 egyptian hieroglyph-13985\n80262 egyptian hieroglyph-13986\n80263 egyptian hieroglyph-13987\n80264 egyptian hieroglyph-13988\n80265 egyptian hieroglyph-13989\n80266 egyptian hieroglyph-1398a\n80267 egyptian hieroglyph-1398b\n80268 egyptian hieroglyph-1398c\n80269 egyptian hieroglyph-1398d\n80270 egyptian hieroglyph-1398e\n80271 egyptian hieroglyph-1398f\n80272 egyptian hieroglyph-13990\n80273 egyptian hieroglyph-13991\n80274 egyptian hieroglyph-13992\n80275 egyptian hieroglyph-13993\n80276 egyptian hieroglyph-13994\n80277 egyptian hieroglyph-13995\n80278 egyptian hieroglyph-13996\n80279 egyptian hieroglyph-13997\n80280 egyptian hieroglyph-13998\n80281 egyptian hieroglyph-13999\n80282 egyptian hieroglyph-1399a\n80283 egyptian hieroglyph-1399b\n80284 egyptian hieroglyph-1399c\n80285 egyptian hieroglyph-1399d\n80286 egyptian hieroglyph-1399e\n80287 egyptian hieroglyph-1399f\n80288 egyptian hieroglyph-139a0\n80289 egyptian hieroglyph-139a1\n80290 egyptian hieroglyph-139a2\n80291 egyptian hieroglyph-139a3\n80292 egyptian hieroglyph-139a4\n80293 egyptian hieroglyph-139a5\n80294 egyptian hieroglyph-139a6\n80295 egyptian hieroglyph-139a7\n80296 egyptian hieroglyph-139a8\n80297 egyptian hieroglyph-139a9\n80298 egyptian hieroglyph-139aa\n80299 egyptian hieroglyph-139ab\n80300 egyptian hieroglyph-139ac\n80301 egyptian hieroglyph-139ad\n80302 egyptian hieroglyph-139ae\n80303 egyptian hieroglyph-139af\n80304 egyptian hieroglyph-139b0\n80305 egyptian hieroglyph-139b1\n80306 egyptian hieroglyph-139b2\n80307 egyptian hieroglyph-139b3\n80308 egyptian hieroglyph-139b4\n80309 egyptian hieroglyph-139b5\n80310 egyptian hieroglyph-139b6\n80311 egyptian hieroglyph-139b7\n80312 egyptian hieroglyph-139b8\n80313 egyptian hieroglyph-139b9\n80314 egyptian hieroglyph-139ba\n80315 egyptian hieroglyph-139bb\n80316 egyptian hieroglyph-139bc\n80317 egyptian hieroglyph-139bd\n80318 egyptian hieroglyph-139be\n80319 egyptian hieroglyph-139bf\n80320 egyptian hieroglyph-139c0\n80321 egyptian hieroglyph-139c1\n80322 egyptian hieroglyph-139c2\n80323 egyptian hieroglyph-139c3\n80324 egyptian hieroglyph-139c4\n80325 egyptian hieroglyph-139c5\n80326 egyptian hieroglyph-139c6\n80327 egyptian hieroglyph-139c7\n80328 egyptian hieroglyph-139c8\n80329 egyptian hieroglyph-139c9\n80330 egyptian hieroglyph-139ca\n80331 egyptian hieroglyph-139cb\n80332 egyptian hieroglyph-139cc\n80333 egyptian hieroglyph-139cd\n80334 egyptian hieroglyph-139ce\n80335 egyptian hieroglyph-139cf\n80336 egyptian hieroglyph-139d0\n80337 egyptian hieroglyph-139d1\n80338 egyptian hieroglyph-139d2\n80339 egyptian hieroglyph-139d3\n80340 egyptian hieroglyph-139d4\n80341 egyptian hieroglyph-139d5\n80342 egyptian hieroglyph-139d6\n80343 egyptian hieroglyph-139d7\n80344 egyptian hieroglyph-139d8\n80345 egyptian hieroglyph-139d9\n80346 egyptian hieroglyph-139da\n80347 egyptian hieroglyph-139db\n80348 egyptian hieroglyph-139dc\n80349 egyptian hieroglyph-139dd\n80350 egyptian hieroglyph-139de\n80351 egyptian hieroglyph-139df\n80352 egyptian hieroglyph-139e0\n80353 egyptian hieroglyph-139e1\n80354 egyptian hieroglyph-139e2\n80355 egyptian hieroglyph-139e3\n80356 egyptian hieroglyph-139e4\n80357 egyptian hieroglyph-139e5\n80358 egyptian hieroglyph-139e6\n80359 egyptian hieroglyph-139e7\n80360 egyptian hieroglyph-139e8\n80361 egyptian hieroglyph-139e9\n80362 egyptian hieroglyph-139ea\n80363 egyptian hieroglyph-139eb\n80364 egyptian hieroglyph-139ec\n80365 egyptian hieroglyph-139ed\n80366 egyptian hieroglyph-139ee\n80367 egyptian hieroglyph-139ef\n80368 egyptian hieroglyph-139f0\n80369 egyptian hieroglyph-139f1\n80370 egyptian hieroglyph-139f2\n80371 egyptian hieroglyph-139f3\n80372 egyptian hieroglyph-139f4\n80373 egyptian hieroglyph-139f5\n80374 egyptian hieroglyph-139f6\n80375 egyptian hieroglyph-139f7\n80376 egyptian hieroglyph-139f8\n80377 egyptian hieroglyph-139f9\n80378 egyptian hieroglyph-139fa\n80379 egyptian hieroglyph-139fb\n80380 egyptian hieroglyph-139fc\n80381 egyptian hieroglyph-139fd\n80382 egyptian hieroglyph-139fe\n80383 egyptian hieroglyph-139ff\n80384 egyptian hieroglyph-13a00\n80385 egyptian hieroglyph-13a01\n80386 egyptian hieroglyph-13a02\n80387 egyptian hieroglyph-13a03\n80388 egyptian hieroglyph-13a04\n80389 egyptian hieroglyph-13a05\n80390 egyptian hieroglyph-13a06\n80391 egyptian hieroglyph-13a07\n80392 egyptian hieroglyph-13a08\n80393 egyptian hieroglyph-13a09\n80394 egyptian hieroglyph-13a0a\n80395 egyptian hieroglyph-13a0b\n80396 egyptian hieroglyph-13a0c\n80397 egyptian hieroglyph-13a0d\n80398 egyptian hieroglyph-13a0e\n80399 egyptian hieroglyph-13a0f\n80400 egyptian hieroglyph-13a10\n80401 egyptian hieroglyph-13a11\n80402 egyptian hieroglyph-13a12\n80403 egyptian hieroglyph-13a13\n80404 egyptian hieroglyph-13a14\n80405 egyptian hieroglyph-13a15\n80406 egyptian hieroglyph-13a16\n80407 egyptian hieroglyph-13a17\n80408 egyptian hieroglyph-13a18\n80409 egyptian hieroglyph-13a19\n80410 egyptian hieroglyph-13a1a\n80411 egyptian hieroglyph-13a1b\n80412 egyptian hieroglyph-13a1c\n80413 egyptian hieroglyph-13a1d\n80414 egyptian hieroglyph-13a1e\n80415 egyptian hieroglyph-13a1f\n80416 egyptian hieroglyph-13a20\n80417 egyptian hieroglyph-13a21\n80418 egyptian hieroglyph-13a22\n80419 egyptian hieroglyph-13a23\n80420 egyptian hieroglyph-13a24\n80421 egyptian hieroglyph-13a25\n80422 egyptian hieroglyph-13a26\n80423 egyptian hieroglyph-13a27\n80424 egyptian hieroglyph-13a28\n80425 egyptian hieroglyph-13a29\n80426 egyptian hieroglyph-13a2a\n80427 egyptian hieroglyph-13a2b\n80428 egyptian hieroglyph-13a2c\n80429 egyptian hieroglyph-13a2d\n80430 egyptian hieroglyph-13a2e\n80431 egyptian hieroglyph-13a2f\n80432 egyptian hieroglyph-13a30\n80433 egyptian hieroglyph-13a31\n80434 egyptian hieroglyph-13a32\n80435 egyptian hieroglyph-13a33\n80436 egyptian hieroglyph-13a34\n80437 egyptian hieroglyph-13a35\n80438 egyptian hieroglyph-13a36\n80439 egyptian hieroglyph-13a37\n80440 egyptian hieroglyph-13a38\n80441 egyptian hieroglyph-13a39\n80442 egyptian hieroglyph-13a3a\n80443 egyptian hieroglyph-13a3b\n80444 egyptian hieroglyph-13a3c\n80445 egyptian hieroglyph-13a3d\n80446 egyptian hieroglyph-13a3e\n80447 egyptian hieroglyph-13a3f\n80448 egyptian hieroglyph-13a40\n80449 egyptian hieroglyph-13a41\n80450 egyptian hieroglyph-13a42\n80451 egyptian hieroglyph-13a43\n80452 egyptian hieroglyph-13a44\n80453 egyptian hieroglyph-13a45\n80454 egyptian hieroglyph-13a46\n80455 egyptian hieroglyph-13a47\n80456 egyptian hieroglyph-13a48\n80457 egyptian hieroglyph-13a49\n80458 egyptian hieroglyph-13a4a\n80459 egyptian hieroglyph-13a4b\n80460 egyptian hieroglyph-13a4c\n80461 egyptian hieroglyph-13a4d\n80462 egyptian hieroglyph-13a4e\n80463 egyptian hieroglyph-13a4f\n80464 egyptian hieroglyph-13a50\n80465 egyptian hieroglyph-13a51\n80466 egyptian hieroglyph-13a52\n80467 egyptian hieroglyph-13a53\n80468 egyptian hieroglyph-13a54\n80469 egyptian hieroglyph-13a55\n80470 egyptian hieroglyph-13a56\n80471 egyptian hieroglyph-13a57\n80472 egyptian hieroglyph-13a58\n80473 egyptian hieroglyph-13a59\n80474 egyptian hieroglyph-13a5a\n80475 egyptian hieroglyph-13a5b\n80476 egyptian hieroglyph-13a5c\n80477 egyptian hieroglyph-13a5d\n80478 egyptian hieroglyph-13a5e\n80479 egyptian hieroglyph-13a5f\n80480 egyptian hieroglyph-13a60\n80481 egyptian hieroglyph-13a61\n80482 egyptian hieroglyph-13a62\n80483 egyptian hieroglyph-13a63\n80484 egyptian hieroglyph-13a64\n80485 egyptian hieroglyph-13a65\n80486 egyptian hieroglyph-13a66\n80487 egyptian hieroglyph-13a67\n80488 egyptian hieroglyph-13a68\n80489 egyptian hieroglyph-13a69\n80490 egyptian hieroglyph-13a6a\n80491 egyptian hieroglyph-13a6b\n80492 egyptian hieroglyph-13a6c\n80493 egyptian hieroglyph-13a6d\n80494 egyptian hieroglyph-13a6e\n80495 egyptian hieroglyph-13a6f\n80496 egyptian hieroglyph-13a70\n80497 egyptian hieroglyph-13a71\n80498 egyptian hieroglyph-13a72\n80499 egyptian hieroglyph-13a73\n80500 egyptian hieroglyph-13a74\n80501 egyptian hieroglyph-13a75\n80502 egyptian hieroglyph-13a76\n80503 egyptian hieroglyph-13a77\n80504 egyptian hieroglyph-13a78\n80505 egyptian hieroglyph-13a79\n80506 egyptian hieroglyph-13a7a\n80507 egyptian hieroglyph-13a7b\n80508 egyptian hieroglyph-13a7c\n80509 egyptian hieroglyph-13a7d\n80510 egyptian hieroglyph-13a7e\n80511 egyptian hieroglyph-13a7f\n80512 egyptian hieroglyph-13a80\n80513 egyptian hieroglyph-13a81\n80514 egyptian hieroglyph-13a82\n80515 egyptian hieroglyph-13a83\n80516 egyptian hieroglyph-13a84\n80517 egyptian hieroglyph-13a85\n80518 egyptian hieroglyph-13a86\n80519 egyptian hieroglyph-13a87\n80520 egyptian hieroglyph-13a88\n80521 egyptian hieroglyph-13a89\n80522 egyptian hieroglyph-13a8a\n80523 egyptian hieroglyph-13a8b\n80524 egyptian hieroglyph-13a8c\n80525 egyptian hieroglyph-13a8d\n80526 egyptian hieroglyph-13a8e\n80527 egyptian hieroglyph-13a8f\n80528 egyptian hieroglyph-13a90\n80529 egyptian hieroglyph-13a91\n80530 egyptian hieroglyph-13a92\n80531 egyptian hieroglyph-13a93\n80532 egyptian hieroglyph-13a94\n80533 egyptian hieroglyph-13a95\n80534 egyptian hieroglyph-13a96\n80535 egyptian hieroglyph-13a97\n80536 egyptian hieroglyph-13a98\n80537 egyptian hieroglyph-13a99\n80538 egyptian hieroglyph-13a9a\n80539 egyptian hieroglyph-13a9b\n80540 egyptian hieroglyph-13a9c\n80541 egyptian hieroglyph-13a9d\n80542 egyptian hieroglyph-13a9e\n80543 egyptian hieroglyph-13a9f\n80544 egyptian hieroglyph-13aa0\n80545 egyptian hieroglyph-13aa1\n80546 egyptian hieroglyph-13aa2\n80547 egyptian hieroglyph-13aa3\n80548 egyptian hieroglyph-13aa4\n80549 egyptian hieroglyph-13aa5\n80550 egyptian hieroglyph-13aa6\n80551 egyptian hieroglyph-13aa7\n80552 egyptian hieroglyph-13aa8\n80553 egyptian hieroglyph-13aa9\n80554 egyptian hieroglyph-13aaa\n80555 egyptian hieroglyph-13aab\n80556 egyptian hieroglyph-13aac\n80557 egyptian hieroglyph-13aad\n80558 egyptian hieroglyph-13aae\n80559 egyptian hieroglyph-13aaf\n80560 egyptian hieroglyph-13ab0\n80561 egyptian hieroglyph-13ab1\n80562 egyptian hieroglyph-13ab2\n80563 egyptian hieroglyph-13ab3\n80564 egyptian hieroglyph-13ab4\n80565 egyptian hieroglyph-13ab5\n80566 egyptian hieroglyph-13ab6\n80567 egyptian hieroglyph-13ab7\n80568 egyptian hieroglyph-13ab8\n80569 egyptian hieroglyph-13ab9\n80570 egyptian hieroglyph-13aba\n80571 egyptian hieroglyph-13abb\n80572 egyptian hieroglyph-13abc\n80573 egyptian hieroglyph-13abd\n80574 egyptian hieroglyph-13abe\n80575 egyptian hieroglyph-13abf\n80576 egyptian hieroglyph-13ac0\n80577 egyptian hieroglyph-13ac1\n80578 egyptian hieroglyph-13ac2\n80579 egyptian hieroglyph-13ac3\n80580 egyptian hieroglyph-13ac4\n80581 egyptian hieroglyph-13ac5\n80582 egyptian hieroglyph-13ac6\n80583 egyptian hieroglyph-13ac7\n80584 egyptian hieroglyph-13ac8\n80585 egyptian hieroglyph-13ac9\n80586 egyptian hieroglyph-13aca\n80587 egyptian hieroglyph-13acb\n80588 egyptian hieroglyph-13acc\n80589 egyptian hieroglyph-13acd\n80590 egyptian hieroglyph-13ace\n80591 egyptian hieroglyph-13acf\n80592 egyptian hieroglyph-13ad0\n80593 egyptian hieroglyph-13ad1\n80594 egyptian hieroglyph-13ad2\n80595 egyptian hieroglyph-13ad3\n80596 egyptian hieroglyph-13ad4\n80597 egyptian hieroglyph-13ad5\n80598 egyptian hieroglyph-13ad6\n80599 egyptian hieroglyph-13ad7\n80600 egyptian hieroglyph-13ad8\n80601 egyptian hieroglyph-13ad9\n80602 egyptian hieroglyph-13ada\n80603 egyptian hieroglyph-13adb\n80604 egyptian hieroglyph-13adc\n80605 egyptian hieroglyph-13add\n80606 egyptian hieroglyph-13ade\n80607 egyptian hieroglyph-13adf\n80608 egyptian hieroglyph-13ae0\n80609 egyptian hieroglyph-13ae1\n80610 egyptian hieroglyph-13ae2\n80611 egyptian hieroglyph-13ae3\n80612 egyptian hieroglyph-13ae4\n80613 egyptian hieroglyph-13ae5\n80614 egyptian hieroglyph-13ae6\n80615 egyptian hieroglyph-13ae7\n80616 egyptian hieroglyph-13ae8\n80617 egyptian hieroglyph-13ae9\n80618 egyptian hieroglyph-13aea\n80619 egyptian hieroglyph-13aeb\n80620 egyptian hieroglyph-13aec\n80621 egyptian hieroglyph-13aed\n80622 egyptian hieroglyph-13aee\n80623 egyptian hieroglyph-13aef\n80624 egyptian hieroglyph-13af0\n80625 egyptian hieroglyph-13af1\n80626 egyptian hieroglyph-13af2\n80627 egyptian hieroglyph-13af3\n80628 egyptian hieroglyph-13af4\n80629 egyptian hieroglyph-13af5\n80630 egyptian hieroglyph-13af6\n80631 egyptian hieroglyph-13af7\n80632 egyptian hieroglyph-13af8\n80633 egyptian hieroglyph-13af9\n80634 egyptian hieroglyph-13afa\n80635 egyptian hieroglyph-13afb\n80636 egyptian hieroglyph-13afc\n80637 egyptian hieroglyph-13afd\n80638 egyptian hieroglyph-13afe\n80639 egyptian hieroglyph-13aff\n80640 egyptian hieroglyph-13b00\n80641 egyptian hieroglyph-13b01\n80642 egyptian hieroglyph-13b02\n80643 egyptian hieroglyph-13b03\n80644 egyptian hieroglyph-13b04\n80645 egyptian hieroglyph-13b05\n80646 egyptian hieroglyph-13b06\n80647 egyptian hieroglyph-13b07\n80648 egyptian hieroglyph-13b08\n80649 egyptian hieroglyph-13b09\n80650 egyptian hieroglyph-13b0a\n80651 egyptian hieroglyph-13b0b\n80652 egyptian hieroglyph-13b0c\n80653 egyptian hieroglyph-13b0d\n80654 egyptian hieroglyph-13b0e\n80655 egyptian hieroglyph-13b0f\n80656 egyptian hieroglyph-13b10\n80657 egyptian hieroglyph-13b11\n80658 egyptian hieroglyph-13b12\n80659 egyptian hieroglyph-13b13\n80660 egyptian hieroglyph-13b14\n80661 egyptian hieroglyph-13b15\n80662 egyptian hieroglyph-13b16\n80663 egyptian hieroglyph-13b17\n80664 egyptian hieroglyph-13b18\n80665 egyptian hieroglyph-13b19\n80666 egyptian hieroglyph-13b1a\n80667 egyptian hieroglyph-13b1b\n80668 egyptian hieroglyph-13b1c\n80669 egyptian hieroglyph-13b1d\n80670 egyptian hieroglyph-13b1e\n80671 egyptian hieroglyph-13b1f\n80672 egyptian hieroglyph-13b20\n80673 egyptian hieroglyph-13b21\n80674 egyptian hieroglyph-13b22\n80675 egyptian hieroglyph-13b23\n80676 egyptian hieroglyph-13b24\n80677 egyptian hieroglyph-13b25\n80678 egyptian hieroglyph-13b26\n80679 egyptian hieroglyph-13b27\n80680 egyptian hieroglyph-13b28\n80681 egyptian hieroglyph-13b29\n80682 egyptian hieroglyph-13b2a\n80683 egyptian hieroglyph-13b2b\n80684 egyptian hieroglyph-13b2c\n80685 egyptian hieroglyph-13b2d\n80686 egyptian hieroglyph-13b2e\n80687 egyptian hieroglyph-13b2f\n80688 egyptian hieroglyph-13b30\n80689 egyptian hieroglyph-13b31\n80690 egyptian hieroglyph-13b32\n80691 egyptian hieroglyph-13b33\n80692 egyptian hieroglyph-13b34\n80693 egyptian hieroglyph-13b35\n80694 egyptian hieroglyph-13b36\n80695 egyptian hieroglyph-13b37\n80696 egyptian hieroglyph-13b38\n80697 egyptian hieroglyph-13b39\n80698 egyptian hieroglyph-13b3a\n80699 egyptian hieroglyph-13b3b\n80700 egyptian hieroglyph-13b3c\n80701 egyptian hieroglyph-13b3d\n80702 egyptian hieroglyph-13b3e\n80703 egyptian hieroglyph-13b3f\n80704 egyptian hieroglyph-13b40\n80705 egyptian hieroglyph-13b41\n80706 egyptian hieroglyph-13b42\n80707 egyptian hieroglyph-13b43\n80708 egyptian hieroglyph-13b44\n80709 egyptian hieroglyph-13b45\n80710 egyptian hieroglyph-13b46\n80711 egyptian hieroglyph-13b47\n80712 egyptian hieroglyph-13b48\n80713 egyptian hieroglyph-13b49\n80714 egyptian hieroglyph-13b4a\n80715 egyptian hieroglyph-13b4b\n80716 egyptian hieroglyph-13b4c\n80717 egyptian hieroglyph-13b4d\n80718 egyptian hieroglyph-13b4e\n80719 egyptian hieroglyph-13b4f\n80720 egyptian hieroglyph-13b50\n80721 egyptian hieroglyph-13b51\n80722 egyptian hieroglyph-13b52\n80723 egyptian hieroglyph-13b53\n80724 egyptian hieroglyph-13b54\n80725 egyptian hieroglyph-13b55\n80726 egyptian hieroglyph-13b56\n80727 egyptian hieroglyph-13b57\n80728 egyptian hieroglyph-13b58\n80729 egyptian hieroglyph-13b59\n80730 egyptian hieroglyph-13b5a\n80731 egyptian hieroglyph-13b5b\n80732 egyptian hieroglyph-13b5c\n80733 egyptian hieroglyph-13b5d\n80734 egyptian hieroglyph-13b5e\n80735 egyptian hieroglyph-13b5f\n80736 egyptian hieroglyph-13b60\n80737 egyptian hieroglyph-13b61\n80738 egyptian hieroglyph-13b62\n80739 egyptian hieroglyph-13b63\n80740 egyptian hieroglyph-13b64\n80741 egyptian hieroglyph-13b65\n80742 egyptian hieroglyph-13b66\n80743 egyptian hieroglyph-13b67\n80744 egyptian hieroglyph-13b68\n80745 egyptian hieroglyph-13b69\n80746 egyptian hieroglyph-13b6a\n80747 egyptian hieroglyph-13b6b\n80748 egyptian hieroglyph-13b6c\n80749 egyptian hieroglyph-13b6d\n80750 egyptian hieroglyph-13b6e\n80751 egyptian hieroglyph-13b6f\n80752 egyptian hieroglyph-13b70\n80753 egyptian hieroglyph-13b71\n80754 egyptian hieroglyph-13b72\n80755 egyptian hieroglyph-13b73\n80756 egyptian hieroglyph-13b74\n80757 egyptian hieroglyph-13b75\n80758 egyptian hieroglyph-13b76\n80759 egyptian hieroglyph-13b77\n80760 egyptian hieroglyph-13b78\n80761 egyptian hieroglyph-13b79\n80762 egyptian hieroglyph-13b7a\n80763 egyptian hieroglyph-13b7b\n80764 egyptian hieroglyph-13b7c\n80765 egyptian hieroglyph-13b7d\n80766 egyptian hieroglyph-13b7e\n80767 egyptian hieroglyph-13b7f\n80768 egyptian hieroglyph-13b80\n80769 egyptian hieroglyph-13b81\n80770 egyptian hieroglyph-13b82\n80771 egyptian hieroglyph-13b83\n80772 egyptian hieroglyph-13b84\n80773 egyptian hieroglyph-13b85\n80774 egyptian hieroglyph-13b86\n80775 egyptian hieroglyph-13b87\n80776 egyptian hieroglyph-13b88\n80777 egyptian hieroglyph-13b89\n80778 egyptian hieroglyph-13b8a\n80779 egyptian hieroglyph-13b8b\n80780 egyptian hieroglyph-13b8c\n80781 egyptian hieroglyph-13b8d\n80782 egyptian hieroglyph-13b8e\n80783 egyptian hieroglyph-13b8f\n80784 egyptian hieroglyph-13b90\n80785 egyptian hieroglyph-13b91\n80786 egyptian hieroglyph-13b92\n80787 egyptian hieroglyph-13b93\n80788 egyptian hieroglyph-13b94\n80789 egyptian hieroglyph-13b95\n80790 egyptian hieroglyph-13b96\n80791 egyptian hieroglyph-13b97\n80792 egyptian hieroglyph-13b98\n80793 egyptian hieroglyph-13b99\n80794 egyptian hieroglyph-13b9a\n80795 egyptian hieroglyph-13b9b\n80796 egyptian hieroglyph-13b9c\n80797 egyptian hieroglyph-13b9d\n80798 egyptian hieroglyph-13b9e\n80799 egyptian hieroglyph-13b9f\n80800 egyptian hieroglyph-13ba0\n80801 egyptian hieroglyph-13ba1\n80802 egyptian hieroglyph-13ba2\n80803 egyptian hieroglyph-13ba3\n80804 egyptian hieroglyph-13ba4\n80805 egyptian hieroglyph-13ba5\n80806 egyptian hieroglyph-13ba6\n80807 egyptian hieroglyph-13ba7\n80808 egyptian hieroglyph-13ba8\n80809 egyptian hieroglyph-13ba9\n80810 egyptian hieroglyph-13baa\n80811 egyptian hieroglyph-13bab\n80812 egyptian hieroglyph-13bac\n80813 egyptian hieroglyph-13bad\n80814 egyptian hieroglyph-13bae\n80815 egyptian hieroglyph-13baf\n80816 egyptian hieroglyph-13bb0\n80817 egyptian hieroglyph-13bb1\n80818 egyptian hieroglyph-13bb2\n80819 egyptian hieroglyph-13bb3\n80820 egyptian hieroglyph-13bb4\n80821 egyptian hieroglyph-13bb5\n80822 egyptian hieroglyph-13bb6\n80823 egyptian hieroglyph-13bb7\n80824 egyptian hieroglyph-13bb8\n80825 egyptian hieroglyph-13bb9\n80826 egyptian hieroglyph-13bba\n80827 egyptian hieroglyph-13bbb\n80828 egyptian hieroglyph-13bbc\n80829 egyptian hieroglyph-13bbd\n80830 egyptian hieroglyph-13bbe\n80831 egyptian hieroglyph-13bbf\n80832 egyptian hieroglyph-13bc0\n80833 egyptian hieroglyph-13bc1\n80834 egyptian hieroglyph-13bc2\n80835 egyptian hieroglyph-13bc3\n80836 egyptian hieroglyph-13bc4\n80837 egyptian hieroglyph-13bc5\n80838 egyptian hieroglyph-13bc6\n80839 egyptian hieroglyph-13bc7\n80840 egyptian hieroglyph-13bc8\n80841 egyptian hieroglyph-13bc9\n80842 egyptian hieroglyph-13bca\n80843 egyptian hieroglyph-13bcb\n80844 egyptian hieroglyph-13bcc\n80845 egyptian hieroglyph-13bcd\n80846 egyptian hieroglyph-13bce\n80847 egyptian hieroglyph-13bcf\n80848 egyptian hieroglyph-13bd0\n80849 egyptian hieroglyph-13bd1\n80850 egyptian hieroglyph-13bd2\n80851 egyptian hieroglyph-13bd3\n80852 egyptian hieroglyph-13bd4\n80853 egyptian hieroglyph-13bd5\n80854 egyptian hieroglyph-13bd6\n80855 egyptian hieroglyph-13bd7\n80856 egyptian hieroglyph-13bd8\n80857 egyptian hieroglyph-13bd9\n80858 egyptian hieroglyph-13bda\n80859 egyptian hieroglyph-13bdb\n80860 egyptian hieroglyph-13bdc\n80861 egyptian hieroglyph-13bdd\n80862 egyptian hieroglyph-13bde\n80863 egyptian hieroglyph-13bdf\n80864 egyptian hieroglyph-13be0\n80865 egyptian hieroglyph-13be1\n80866 egyptian hieroglyph-13be2\n80867 egyptian hieroglyph-13be3\n80868 egyptian hieroglyph-13be4\n80869 egyptian hieroglyph-13be5\n80870 egyptian hieroglyph-13be6\n80871 egyptian hieroglyph-13be7\n80872 egyptian hieroglyph-13be8\n80873 egyptian hieroglyph-13be9\n80874 egyptian hieroglyph-13bea\n80875 egyptian hieroglyph-13beb\n80876 egyptian hieroglyph-13bec\n80877 egyptian hieroglyph-13bed\n80878 egyptian hieroglyph-13bee\n80879 egyptian hieroglyph-13bef\n80880 egyptian hieroglyph-13bf0\n80881 egyptian hieroglyph-13bf1\n80882 egyptian hieroglyph-13bf2\n80883 egyptian hieroglyph-13bf3\n80884 egyptian hieroglyph-13bf4\n80885 egyptian hieroglyph-13bf5\n80886 egyptian hieroglyph-13bf6\n80887 egyptian hieroglyph-13bf7\n80888 egyptian hieroglyph-13bf8\n80889 egyptian hieroglyph-13bf9\n80890 egyptian hieroglyph-13bfa\n80891 egyptian hieroglyph-13bfb\n80892 egyptian hieroglyph-13bfc\n80893 egyptian hieroglyph-13bfd\n80894 egyptian hieroglyph-13bfe\n80895 egyptian hieroglyph-13bff\n80896 egyptian hieroglyph-13c00\n80897 egyptian hieroglyph-13c01\n80898 egyptian hieroglyph-13c02\n80899 egyptian hieroglyph-13c03\n80900 egyptian hieroglyph-13c04\n80901 egyptian hieroglyph-13c05\n80902 egyptian hieroglyph-13c06\n80903 egyptian hieroglyph-13c07\n80904 egyptian hieroglyph-13c08\n80905 egyptian hieroglyph-13c09\n80906 egyptian hieroglyph-13c0a\n80907 egyptian hieroglyph-13c0b\n80908 egyptian hieroglyph-13c0c\n80909 egyptian hieroglyph-13c0d\n80910 egyptian hieroglyph-13c0e\n80911 egyptian hieroglyph-13c0f\n80912 egyptian hieroglyph-13c10\n80913 egyptian hieroglyph-13c11\n80914 egyptian hieroglyph-13c12\n80915 egyptian hieroglyph-13c13\n80916 egyptian hieroglyph-13c14\n80917 egyptian hieroglyph-13c15\n80918 egyptian hieroglyph-13c16\n80919 egyptian hieroglyph-13c17\n80920 egyptian hieroglyph-13c18\n80921 egyptian hieroglyph-13c19\n80922 egyptian hieroglyph-13c1a\n80923 egyptian hieroglyph-13c1b\n80924 egyptian hieroglyph-13c1c\n80925 egyptian hieroglyph-13c1d\n80926 egyptian hieroglyph-13c1e\n80927 egyptian hieroglyph-13c1f\n80928 egyptian hieroglyph-13c20\n80929 egyptian hieroglyph-13c21\n80930 egyptian hieroglyph-13c22\n80931 egyptian hieroglyph-13c23\n80932 egyptian hieroglyph-13c24\n80933 egyptian hieroglyph-13c25\n80934 egyptian hieroglyph-13c26\n80935 egyptian hieroglyph-13c27\n80936 egyptian hieroglyph-13c28\n80937 egyptian hieroglyph-13c29\n80938 egyptian hieroglyph-13c2a\n80939 egyptian hieroglyph-13c2b\n80940 egyptian hieroglyph-13c2c\n80941 egyptian hieroglyph-13c2d\n80942 egyptian hieroglyph-13c2e\n80943 egyptian hieroglyph-13c2f\n80944 egyptian hieroglyph-13c30\n80945 egyptian hieroglyph-13c31\n80946 egyptian hieroglyph-13c32\n80947 egyptian hieroglyph-13c33\n80948 egyptian hieroglyph-13c34\n80949 egyptian hieroglyph-13c35\n80950 egyptian hieroglyph-13c36\n80951 egyptian hieroglyph-13c37\n80952 egyptian hieroglyph-13c38\n80953 egyptian hieroglyph-13c39\n80954 egyptian hieroglyph-13c3a\n80955 egyptian hieroglyph-13c3b\n80956 egyptian hieroglyph-13c3c\n80957 egyptian hieroglyph-13c3d\n80958 egyptian hieroglyph-13c3e\n80959 egyptian hieroglyph-13c3f\n80960 egyptian hieroglyph-13c40\n80961 egyptian hieroglyph-13c41\n80962 egyptian hieroglyph-13c42\n80963 egyptian hieroglyph-13c43\n80964 egyptian hieroglyph-13c44\n80965 egyptian hieroglyph-13c45\n80966 egyptian hieroglyph-13c46\n80967 egyptian hieroglyph-13c47\n80968 egyptian hieroglyph-13c48\n80969 egyptian hieroglyph-13c49\n80970 egyptian hieroglyph-13c4a\n80971 egyptian hieroglyph-13c4b\n80972 egyptian hieroglyph-13c4c\n80973 egyptian hieroglyph-13c4d\n80974 egyptian hieroglyph-13c4e\n80975 egyptian hieroglyph-13c4f\n80976 egyptian hieroglyph-13c50\n80977 egyptian hieroglyph-13c51\n80978 egyptian hieroglyph-13c52\n80979 egyptian hieroglyph-13c53\n80980 egyptian hieroglyph-13c54\n80981 egyptian hieroglyph-13c55\n80982 egyptian hieroglyph-13c56\n80983 egyptian hieroglyph-13c57\n80984 egyptian hieroglyph-13c58\n80985 egyptian hieroglyph-13c59\n80986 egyptian hieroglyph-13c5a\n80987 egyptian hieroglyph-13c5b\n80988 egyptian hieroglyph-13c5c\n80989 egyptian hieroglyph-13c5d\n80990 egyptian hieroglyph-13c5e\n80991 egyptian hieroglyph-13c5f\n80992 egyptian hieroglyph-13c60\n80993 egyptian hieroglyph-13c61\n80994 egyptian hieroglyph-13c62\n80995 egyptian hieroglyph-13c63\n80996 egyptian hieroglyph-13c64\n80997 egyptian hieroglyph-13c65\n80998 egyptian hieroglyph-13c66\n80999 egyptian hieroglyph-13c67\n81000 egyptian hieroglyph-13c68\n81001 egyptian hieroglyph-13c69\n81002 egyptian hieroglyph-13c6a\n81003 egyptian hieroglyph-13c6b\n81004 egyptian hieroglyph-13c6c\n81005 egyptian hieroglyph-13c6d\n81006 egyptian hieroglyph-13c6e\n81007 egyptian hieroglyph-13c6f\n81008 egyptian hieroglyph-13c70\n81009 egyptian hieroglyph-13c71\n81010 egyptian hieroglyph-13c72\n81011 egyptian hieroglyph-13c73\n81012 egyptian hieroglyph-13c74\n81013 egyptian hieroglyph-13c75\n81014 egyptian hieroglyph-13c76\n81015 egyptian hieroglyph-13c77\n81016 egyptian hieroglyph-13c78\n81017 egyptian hieroglyph-13c79\n81018 egyptian hieroglyph-13c7a\n81019 egyptian hieroglyph-13c7b\n81020 egyptian hieroglyph-13c7c\n81021 egyptian hieroglyph-13c7d\n81022 egyptian hieroglyph-13c7e\n81023 egyptian hieroglyph-13c7f\n81024 egyptian hieroglyph-13c80\n81025 egyptian hieroglyph-13c81\n81026 egyptian hieroglyph-13c82\n81027 egyptian hieroglyph-13c83\n81028 egyptian hieroglyph-13c84\n81029 egyptian hieroglyph-13c85\n81030 egyptian hieroglyph-13c86\n81031 egyptian hieroglyph-13c87\n81032 egyptian hieroglyph-13c88\n81033 egyptian hieroglyph-13c89\n81034 egyptian hieroglyph-13c8a\n81035 egyptian hieroglyph-13c8b\n81036 egyptian hieroglyph-13c8c\n81037 egyptian hieroglyph-13c8d\n81038 egyptian hieroglyph-13c8e\n81039 egyptian hieroglyph-13c8f\n81040 egyptian hieroglyph-13c90\n81041 egyptian hieroglyph-13c91\n81042 egyptian hieroglyph-13c92\n81043 egyptian hieroglyph-13c93\n81044 egyptian hieroglyph-13c94\n81045 egyptian hieroglyph-13c95\n81046 egyptian hieroglyph-13c96\n81047 egyptian hieroglyph-13c97\n81048 egyptian hieroglyph-13c98\n81049 egyptian hieroglyph-13c99\n81050 egyptian hieroglyph-13c9a\n81051 egyptian hieroglyph-13c9b\n81052 egyptian hieroglyph-13c9c\n81053 egyptian hieroglyph-13c9d\n81054 egyptian hieroglyph-13c9e\n81055 egyptian hieroglyph-13c9f\n81056 egyptian hieroglyph-13ca0\n81057 egyptian hieroglyph-13ca1\n81058 egyptian hieroglyph-13ca2\n81059 egyptian hieroglyph-13ca3\n81060 egyptian hieroglyph-13ca4\n81061 egyptian hieroglyph-13ca5\n81062 egyptian hieroglyph-13ca6\n81063 egyptian hieroglyph-13ca7\n81064 egyptian hieroglyph-13ca8\n81065 egyptian hieroglyph-13ca9\n81066 egyptian hieroglyph-13caa\n81067 egyptian hieroglyph-13cab\n81068 egyptian hieroglyph-13cac\n81069 egyptian hieroglyph-13cad\n81070 egyptian hieroglyph-13cae\n81071 egyptian hieroglyph-13caf\n81072 egyptian hieroglyph-13cb0\n81073 egyptian hieroglyph-13cb1\n81074 egyptian hieroglyph-13cb2\n81075 egyptian hieroglyph-13cb3\n81076 egyptian hieroglyph-13cb4\n81077 egyptian hieroglyph-13cb5\n81078 egyptian hieroglyph-13cb6\n81079 egyptian hieroglyph-13cb7\n81080 egyptian hieroglyph-13cb8\n81081 egyptian hieroglyph-13cb9\n81082 egyptian hieroglyph-13cba\n81083 egyptian hieroglyph-13cbb\n81084 egyptian hieroglyph-13cbc\n81085 egyptian hieroglyph-13cbd\n81086 egyptian hieroglyph-13cbe\n81087 egyptian hieroglyph-13cbf\n81088 egyptian hieroglyph-13cc0\n81089 egyptian hieroglyph-13cc1\n81090 egyptian hieroglyph-13cc2\n81091 egyptian hieroglyph-13cc3\n81092 egyptian hieroglyph-13cc4\n81093 egyptian hieroglyph-13cc5\n81094 egyptian hieroglyph-13cc6\n81095 egyptian hieroglyph-13cc7\n81096 egyptian hieroglyph-13cc8\n81097 egyptian hieroglyph-13cc9\n81098 egyptian hieroglyph-13cca\n81099 egyptian hieroglyph-13ccb\n81100 egyptian hieroglyph-13ccc\n81101 egyptian hieroglyph-13ccd\n81102 egyptian hieroglyph-13cce\n81103 egyptian hieroglyph-13ccf\n81104 egyptian hieroglyph-13cd0\n81105 egyptian hieroglyph-13cd1\n81106 egyptian hieroglyph-13cd2\n81107 egyptian hieroglyph-13cd3\n81108 egyptian hieroglyph-13cd4\n81109 egyptian hieroglyph-13cd5\n81110 egyptian hieroglyph-13cd6\n81111 egyptian hieroglyph-13cd7\n81112 egyptian hieroglyph-13cd8\n81113 egyptian hieroglyph-13cd9\n81114 egyptian hieroglyph-13cda\n81115 egyptian hieroglyph-13cdb\n81116 egyptian hieroglyph-13cdc\n81117 egyptian hieroglyph-13cdd\n81118 egyptian hieroglyph-13cde\n81119 egyptian hieroglyph-13cdf\n81120 egyptian hieroglyph-13ce0\n81121 egyptian hieroglyph-13ce1\n81122 egyptian hieroglyph-13ce2\n81123 egyptian hieroglyph-13ce3\n81124 egyptian hieroglyph-13ce4\n81125 egyptian hieroglyph-13ce5\n81126 egyptian hieroglyph-13ce6\n81127 egyptian hieroglyph-13ce7\n81128 egyptian hieroglyph-13ce8\n81129 egyptian hieroglyph-13ce9\n81130 egyptian hieroglyph-13cea\n81131 egyptian hieroglyph-13ceb\n81132 egyptian hieroglyph-13cec\n81133 egyptian hieroglyph-13ced\n81134 egyptian hieroglyph-13cee\n81135 egyptian hieroglyph-13cef\n81136 egyptian hieroglyph-13cf0\n81137 egyptian hieroglyph-13cf1\n81138 egyptian hieroglyph-13cf2\n81139 egyptian hieroglyph-13cf3\n81140 egyptian hieroglyph-13cf4\n81141 egyptian hieroglyph-13cf5\n81142 egyptian hieroglyph-13cf6\n81143 egyptian hieroglyph-13cf7\n81144 egyptian hieroglyph-13cf8\n81145 egyptian hieroglyph-13cf9\n81146 egyptian hieroglyph-13cfa\n81147 egyptian hieroglyph-13cfb\n81148 egyptian hieroglyph-13cfc\n81149 egyptian hieroglyph-13cfd\n81150 egyptian hieroglyph-13cfe\n81151 egyptian hieroglyph-13cff\n81152 egyptian hieroglyph-13d00\n81153 egyptian hieroglyph-13d01\n81154 egyptian hieroglyph-13d02\n81155 egyptian hieroglyph-13d03\n81156 egyptian hieroglyph-13d04\n81157 egyptian hieroglyph-13d05\n81158 egyptian hieroglyph-13d06\n81159 egyptian hieroglyph-13d07\n81160 egyptian hieroglyph-13d08\n81161 egyptian hieroglyph-13d09\n81162 egyptian hieroglyph-13d0a\n81163 egyptian hieroglyph-13d0b\n81164 egyptian hieroglyph-13d0c\n81165 egyptian hieroglyph-13d0d\n81166 egyptian hieroglyph-13d0e\n81167 egyptian hieroglyph-13d0f\n81168 egyptian hieroglyph-13d10\n81169 egyptian hieroglyph-13d11\n81170 egyptian hieroglyph-13d12\n81171 egyptian hieroglyph-13d13\n81172 egyptian hieroglyph-13d14\n81173 egyptian hieroglyph-13d15\n81174 egyptian hieroglyph-13d16\n81175 egyptian hieroglyph-13d17\n81176 egyptian hieroglyph-13d18\n81177 egyptian hieroglyph-13d19\n81178 egyptian hieroglyph-13d1a\n81179 egyptian hieroglyph-13d1b\n81180 egyptian hieroglyph-13d1c\n81181 egyptian hieroglyph-13d1d\n81182 egyptian hieroglyph-13d1e\n81183 egyptian hieroglyph-13d1f\n81184 egyptian hieroglyph-13d20\n81185 egyptian hieroglyph-13d21\n81186 egyptian hieroglyph-13d22\n81187 egyptian hieroglyph-13d23\n81188 egyptian hieroglyph-13d24\n81189 egyptian hieroglyph-13d25\n81190 egyptian hieroglyph-13d26\n81191 egyptian hieroglyph-13d27\n81192 egyptian hieroglyph-13d28\n81193 egyptian hieroglyph-13d29\n81194 egyptian hieroglyph-13d2a\n81195 egyptian hieroglyph-13d2b\n81196 egyptian hieroglyph-13d2c\n81197 egyptian hieroglyph-13d2d\n81198 egyptian hieroglyph-13d2e\n81199 egyptian hieroglyph-13d2f\n81200 egyptian hieroglyph-13d30\n81201 egyptian hieroglyph-13d31\n81202 egyptian hieroglyph-13d32\n81203 egyptian hieroglyph-13d33\n81204 egyptian hieroglyph-13d34\n81205 egyptian hieroglyph-13d35\n81206 egyptian hieroglyph-13d36\n81207 egyptian hieroglyph-13d37\n81208 egyptian hieroglyph-13d38\n81209 egyptian hieroglyph-13d39\n81210 egyptian hieroglyph-13d3a\n81211 egyptian hieroglyph-13d3b\n81212 egyptian hieroglyph-13d3c\n81213 egyptian hieroglyph-13d3d\n81214 egyptian hieroglyph-13d3e\n81215 egyptian hieroglyph-13d3f\n81216 egyptian hieroglyph-13d40\n81217 egyptian hieroglyph-13d41\n81218 egyptian hieroglyph-13d42\n81219 egyptian hieroglyph-13d43\n81220 egyptian hieroglyph-13d44\n81221 egyptian hieroglyph-13d45\n81222 egyptian hieroglyph-13d46\n81223 egyptian hieroglyph-13d47\n81224 egyptian hieroglyph-13d48\n81225 egyptian hieroglyph-13d49\n81226 egyptian hieroglyph-13d4a\n81227 egyptian hieroglyph-13d4b\n81228 egyptian hieroglyph-13d4c\n81229 egyptian hieroglyph-13d4d\n81230 egyptian hieroglyph-13d4e\n81231 egyptian hieroglyph-13d4f\n81232 egyptian hieroglyph-13d50\n81233 egyptian hieroglyph-13d51\n81234 egyptian hieroglyph-13d52\n81235 egyptian hieroglyph-13d53\n81236 egyptian hieroglyph-13d54\n81237 egyptian hieroglyph-13d55\n81238 egyptian hieroglyph-13d56\n81239 egyptian hieroglyph-13d57\n81240 egyptian hieroglyph-13d58\n81241 egyptian hieroglyph-13d59\n81242 egyptian hieroglyph-13d5a\n81243 egyptian hieroglyph-13d5b\n81244 egyptian hieroglyph-13d5c\n81245 egyptian hieroglyph-13d5d\n81246 egyptian hieroglyph-13d5e\n81247 egyptian hieroglyph-13d5f\n81248 egyptian hieroglyph-13d60\n81249 egyptian hieroglyph-13d61\n81250 egyptian hieroglyph-13d62\n81251 egyptian hieroglyph-13d63\n81252 egyptian hieroglyph-13d64\n81253 egyptian hieroglyph-13d65\n81254 egyptian hieroglyph-13d66\n81255 egyptian hieroglyph-13d67\n81256 egyptian hieroglyph-13d68\n81257 egyptian hieroglyph-13d69\n81258 egyptian hieroglyph-13d6a\n81259 egyptian hieroglyph-13d6b\n81260 egyptian hieroglyph-13d6c\n81261 egyptian hieroglyph-13d6d\n81262 egyptian hieroglyph-13d6e\n81263 egyptian hieroglyph-13d6f\n81264 egyptian hieroglyph-13d70\n81265 egyptian hieroglyph-13d71\n81266 egyptian hieroglyph-13d72\n81267 egyptian hieroglyph-13d73\n81268 egyptian hieroglyph-13d74\n81269 egyptian hieroglyph-13d75\n81270 egyptian hieroglyph-13d76\n81271 egyptian hieroglyph-13d77\n81272 egyptian hieroglyph-13d78\n81273 egyptian hieroglyph-13d79\n81274 egyptian hieroglyph-13d7a\n81275 egyptian hieroglyph-13d7b\n81276 egyptian hieroglyph-13d7c\n81277 egyptian hieroglyph-13d7d\n81278 egyptian hieroglyph-13d7e\n81279 egyptian hieroglyph-13d7f\n81280 egyptian hieroglyph-13d80\n81281 egyptian hieroglyph-13d81\n81282 egyptian hieroglyph-13d82\n81283 egyptian hieroglyph-13d83\n81284 egyptian hieroglyph-13d84\n81285 egyptian hieroglyph-13d85\n81286 egyptian hieroglyph-13d86\n81287 egyptian hieroglyph-13d87\n81288 egyptian hieroglyph-13d88\n81289 egyptian hieroglyph-13d89\n81290 egyptian hieroglyph-13d8a\n81291 egyptian hieroglyph-13d8b\n81292 egyptian hieroglyph-13d8c\n81293 egyptian hieroglyph-13d8d\n81294 egyptian hieroglyph-13d8e\n81295 egyptian hieroglyph-13d8f\n81296 egyptian hieroglyph-13d90\n81297 egyptian hieroglyph-13d91\n81298 egyptian hieroglyph-13d92\n81299 egyptian hieroglyph-13d93\n81300 egyptian hieroglyph-13d94\n81301 egyptian hieroglyph-13d95\n81302 egyptian hieroglyph-13d96\n81303 egyptian hieroglyph-13d97\n81304 egyptian hieroglyph-13d98\n81305 egyptian hieroglyph-13d99\n81306 egyptian hieroglyph-13d9a\n81307 egyptian hieroglyph-13d9b\n81308 egyptian hieroglyph-13d9c\n81309 egyptian hieroglyph-13d9d\n81310 egyptian hieroglyph-13d9e\n81311 egyptian hieroglyph-13d9f\n81312 egyptian hieroglyph-13da0\n81313 egyptian hieroglyph-13da1\n81314 egyptian hieroglyph-13da2\n81315 egyptian hieroglyph-13da3\n81316 egyptian hieroglyph-13da4\n81317 egyptian hieroglyph-13da5\n81318 egyptian hieroglyph-13da6\n81319 egyptian hieroglyph-13da7\n81320 egyptian hieroglyph-13da8\n81321 egyptian hieroglyph-13da9\n81322 egyptian hieroglyph-13daa\n81323 egyptian hieroglyph-13dab\n81324 egyptian hieroglyph-13dac\n81325 egyptian hieroglyph-13dad\n81326 egyptian hieroglyph-13dae\n81327 egyptian hieroglyph-13daf\n81328 egyptian hieroglyph-13db0\n81329 egyptian hieroglyph-13db1\n81330 egyptian hieroglyph-13db2\n81331 egyptian hieroglyph-13db3\n81332 egyptian hieroglyph-13db4\n81333 egyptian hieroglyph-13db5\n81334 egyptian hieroglyph-13db6\n81335 egyptian hieroglyph-13db7\n81336 egyptian hieroglyph-13db8\n81337 egyptian hieroglyph-13db9\n81338 egyptian hieroglyph-13dba\n81339 egyptian hieroglyph-13dbb\n81340 egyptian hieroglyph-13dbc\n81341 egyptian hieroglyph-13dbd\n81342 egyptian hieroglyph-13dbe\n81343 egyptian hieroglyph-13dbf\n81344 egyptian hieroglyph-13dc0\n81345 egyptian hieroglyph-13dc1\n81346 egyptian hieroglyph-13dc2\n81347 egyptian hieroglyph-13dc3\n81348 egyptian hieroglyph-13dc4\n81349 egyptian hieroglyph-13dc5\n81350 egyptian hieroglyph-13dc6\n81351 egyptian hieroglyph-13dc7\n81352 egyptian hieroglyph-13dc8\n81353 egyptian hieroglyph-13dc9\n81354 egyptian hieroglyph-13dca\n81355 egyptian hieroglyph-13dcb\n81356 egyptian hieroglyph-13dcc\n81357 egyptian hieroglyph-13dcd\n81358 egyptian hieroglyph-13dce\n81359 egyptian hieroglyph-13dcf\n81360 egyptian hieroglyph-13dd0\n81361 egyptian hieroglyph-13dd1\n81362 egyptian hieroglyph-13dd2\n81363 egyptian hieroglyph-13dd3\n81364 egyptian hieroglyph-13dd4\n81365 egyptian hieroglyph-13dd5\n81366 egyptian hieroglyph-13dd6\n81367 egyptian hieroglyph-13dd7\n81368 egyptian hieroglyph-13dd8\n81369 egyptian hieroglyph-13dd9\n81370 egyptian hieroglyph-13dda\n81371 egyptian hieroglyph-13ddb\n81372 egyptian hieroglyph-13ddc\n81373 egyptian hieroglyph-13ddd\n81374 egyptian hieroglyph-13dde\n81375 egyptian hieroglyph-13ddf\n81376 egyptian hieroglyph-13de0\n81377 egyptian hieroglyph-13de1\n81378 egyptian hieroglyph-13de2\n81379 egyptian hieroglyph-13de3\n81380 egyptian hieroglyph-13de4\n81381 egyptian hieroglyph-13de5\n81382 egyptian hieroglyph-13de6\n81383 egyptian hieroglyph-13de7\n81384 egyptian hieroglyph-13de8\n81385 egyptian hieroglyph-13de9\n81386 egyptian hieroglyph-13dea\n81387 egyptian hieroglyph-13deb\n81388 egyptian hieroglyph-13dec\n81389 egyptian hieroglyph-13ded\n81390 egyptian hieroglyph-13dee\n81391 egyptian hieroglyph-13def\n81392 egyptian hieroglyph-13df0\n81393 egyptian hieroglyph-13df1\n81394 egyptian hieroglyph-13df2\n81395 egyptian hieroglyph-13df3\n81396 egyptian hieroglyph-13df4\n81397 egyptian hieroglyph-13df5\n81398 egyptian hieroglyph-13df6\n81399 egyptian hieroglyph-13df7\n81400 egyptian hieroglyph-13df8\n81401 egyptian hieroglyph-13df9\n81402 egyptian hieroglyph-13dfa\n81403 egyptian hieroglyph-13dfb\n81404 egyptian hieroglyph-13dfc\n81405 egyptian hieroglyph-13dfd\n81406 egyptian hieroglyph-13dfe\n81407 egyptian hieroglyph-13dff\n81408 egyptian hieroglyph-13e00\n81409 egyptian hieroglyph-13e01\n81410 egyptian hieroglyph-13e02\n81411 egyptian hieroglyph-13e03\n81412 egyptian hieroglyph-13e04\n81413 egyptian hieroglyph-13e05\n81414 egyptian hieroglyph-13e06\n81415 egyptian hieroglyph-13e07\n81416 egyptian hieroglyph-13e08\n81417 egyptian hieroglyph-13e09\n81418 egyptian hieroglyph-13e0a\n81419 egyptian hieroglyph-13e0b\n81420 egyptian hieroglyph-13e0c\n81421 egyptian hieroglyph-13e0d\n81422 egyptian hieroglyph-13e0e\n81423 egyptian hieroglyph-13e0f\n81424 egyptian hieroglyph-13e10\n81425 egyptian hieroglyph-13e11\n81426 egyptian hieroglyph-13e12\n81427 egyptian hieroglyph-13e13\n81428 egyptian hieroglyph-13e14\n81429 egyptian hieroglyph-13e15\n81430 egyptian hieroglyph-13e16\n81431 egyptian hieroglyph-13e17\n81432 egyptian hieroglyph-13e18\n81433 egyptian hieroglyph-13e19\n81434 egyptian hieroglyph-13e1a\n81435 egyptian hieroglyph-13e1b\n81436 egyptian hieroglyph-13e1c\n81437 egyptian hieroglyph-13e1d\n81438 egyptian hieroglyph-13e1e\n81439 egyptian hieroglyph-13e1f\n81440 egyptian hieroglyph-13e20\n81441 egyptian hieroglyph-13e21\n81442 egyptian hieroglyph-13e22\n81443 egyptian hieroglyph-13e23\n81444 egyptian hieroglyph-13e24\n81445 egyptian hieroglyph-13e25\n81446 egyptian hieroglyph-13e26\n81447 egyptian hieroglyph-13e27\n81448 egyptian hieroglyph-13e28\n81449 egyptian hieroglyph-13e29\n81450 egyptian hieroglyph-13e2a\n81451 egyptian hieroglyph-13e2b\n81452 egyptian hieroglyph-13e2c\n81453 egyptian hieroglyph-13e2d\n81454 egyptian hieroglyph-13e2e\n81455 egyptian hieroglyph-13e2f\n81456 egyptian hieroglyph-13e30\n81457 egyptian hieroglyph-13e31\n81458 egyptian hieroglyph-13e32\n81459 egyptian hieroglyph-13e33\n81460 egyptian hieroglyph-13e34\n81461 egyptian hieroglyph-13e35\n81462 egyptian hieroglyph-13e36\n81463 egyptian hieroglyph-13e37\n81464 egyptian hieroglyph-13e38\n81465 egyptian hieroglyph-13e39\n81466 egyptian hieroglyph-13e3a\n81467 egyptian hieroglyph-13e3b\n81468 egyptian hieroglyph-13e3c\n81469 egyptian hieroglyph-13e3d\n81470 egyptian hieroglyph-13e3e\n81471 egyptian hieroglyph-13e3f\n81472 egyptian hieroglyph-13e40\n81473 egyptian hieroglyph-13e41\n81474 egyptian hieroglyph-13e42\n81475 egyptian hieroglyph-13e43\n81476 egyptian hieroglyph-13e44\n81477 egyptian hieroglyph-13e45\n81478 egyptian hieroglyph-13e46\n81479 egyptian hieroglyph-13e47\n81480 egyptian hieroglyph-13e48\n81481 egyptian hieroglyph-13e49\n81482 egyptian hieroglyph-13e4a\n81483 egyptian hieroglyph-13e4b\n81484 egyptian hieroglyph-13e4c\n81485 egyptian hieroglyph-13e4d\n81486 egyptian hieroglyph-13e4e\n81487 egyptian hieroglyph-13e4f\n81488 egyptian hieroglyph-13e50\n81489 egyptian hieroglyph-13e51\n81490 egyptian hieroglyph-13e52\n81491 egyptian hieroglyph-13e53\n81492 egyptian hieroglyph-13e54\n81493 egyptian hieroglyph-13e55\n81494 egyptian hieroglyph-13e56\n81495 egyptian hieroglyph-13e57\n81496 egyptian hieroglyph-13e58\n81497 egyptian hieroglyph-13e59\n81498 egyptian hieroglyph-13e5a\n81499 egyptian hieroglyph-13e5b\n81500 egyptian hieroglyph-13e5c\n81501 egyptian hieroglyph-13e5d\n81502 egyptian hieroglyph-13e5e\n81503 egyptian hieroglyph-13e5f\n81504 egyptian hieroglyph-13e60\n81505 egyptian hieroglyph-13e61\n81506 egyptian hieroglyph-13e62\n81507 egyptian hieroglyph-13e63\n81508 egyptian hieroglyph-13e64\n81509 egyptian hieroglyph-13e65\n81510 egyptian hieroglyph-13e66\n81511 egyptian hieroglyph-13e67\n81512 egyptian hieroglyph-13e68\n81513 egyptian hieroglyph-13e69\n81514 egyptian hieroglyph-13e6a\n81515 egyptian hieroglyph-13e6b\n81516 egyptian hieroglyph-13e6c\n81517 egyptian hieroglyph-13e6d\n81518 egyptian hieroglyph-13e6e\n81519 egyptian hieroglyph-13e6f\n81520 egyptian hieroglyph-13e70\n81521 egyptian hieroglyph-13e71\n81522 egyptian hieroglyph-13e72\n81523 egyptian hieroglyph-13e73\n81524 egyptian hieroglyph-13e74\n81525 egyptian hieroglyph-13e75\n81526 egyptian hieroglyph-13e76\n81527 egyptian hieroglyph-13e77\n81528 egyptian hieroglyph-13e78\n81529 egyptian hieroglyph-13e79\n81530 egyptian hieroglyph-13e7a\n81531 egyptian hieroglyph-13e7b\n81532 egyptian hieroglyph-13e7c\n81533 egyptian hieroglyph-13e7d\n81534 egyptian hieroglyph-13e7e\n81535 egyptian hieroglyph-13e7f\n81536 egyptian hieroglyph-13e80\n81537 egyptian hieroglyph-13e81\n81538 egyptian hieroglyph-13e82\n81539 egyptian hieroglyph-13e83\n81540 egyptian hieroglyph-13e84\n81541 egyptian hieroglyph-13e85\n81542 egyptian hieroglyph-13e86\n81543 egyptian hieroglyph-13e87\n81544 egyptian hieroglyph-13e88\n81545 egyptian hieroglyph-13e89\n81546 egyptian hieroglyph-13e8a\n81547 egyptian hieroglyph-13e8b\n81548 egyptian hieroglyph-13e8c\n81549 egyptian hieroglyph-13e8d\n81550 egyptian hieroglyph-13e8e\n81551 egyptian hieroglyph-13e8f\n81552 egyptian hieroglyph-13e90\n81553 egyptian hieroglyph-13e91\n81554 egyptian hieroglyph-13e92\n81555 egyptian hieroglyph-13e93\n81556 egyptian hieroglyph-13e94\n81557 egyptian hieroglyph-13e95\n81558 egyptian hieroglyph-13e96\n81559 egyptian hieroglyph-13e97\n81560 egyptian hieroglyph-13e98\n81561 egyptian hieroglyph-13e99\n81562 egyptian hieroglyph-13e9a\n81563 egyptian hieroglyph-13e9b\n81564 egyptian hieroglyph-13e9c\n81565 egyptian hieroglyph-13e9d\n81566 egyptian hieroglyph-13e9e\n81567 egyptian hieroglyph-13e9f\n81568 egyptian hieroglyph-13ea0\n81569 egyptian hieroglyph-13ea1\n81570 egyptian hieroglyph-13ea2\n81571 egyptian hieroglyph-13ea3\n81572 egyptian hieroglyph-13ea4\n81573 egyptian hieroglyph-13ea5\n81574 egyptian hieroglyph-13ea6\n81575 egyptian hieroglyph-13ea7\n81576 egyptian hieroglyph-13ea8\n81577 egyptian hieroglyph-13ea9\n81578 egyptian hieroglyph-13eaa\n81579 egyptian hieroglyph-13eab\n81580 egyptian hieroglyph-13eac\n81581 egyptian hieroglyph-13ead\n81582 egyptian hieroglyph-13eae\n81583 egyptian hieroglyph-13eaf\n81584 egyptian hieroglyph-13eb0\n81585 egyptian hieroglyph-13eb1\n81586 egyptian hieroglyph-13eb2\n81587 egyptian hieroglyph-13eb3\n81588 egyptian hieroglyph-13eb4\n81589 egyptian hieroglyph-13eb5\n81590 egyptian hieroglyph-13eb6\n81591 egyptian hieroglyph-13eb7\n81592 egyptian hieroglyph-13eb8\n81593 egyptian hieroglyph-13eb9\n81594 egyptian hieroglyph-13eba\n81595 egyptian hieroglyph-13ebb\n81596 egyptian hieroglyph-13ebc\n81597 egyptian hieroglyph-13ebd\n81598 egyptian hieroglyph-13ebe\n81599 egyptian hieroglyph-13ebf\n81600 egyptian hieroglyph-13ec0\n81601 egyptian hieroglyph-13ec1\n81602 egyptian hieroglyph-13ec2\n81603 egyptian hieroglyph-13ec3\n81604 egyptian hieroglyph-13ec4\n81605 egyptian hieroglyph-13ec5\n81606 egyptian hieroglyph-13ec6\n81607 egyptian hieroglyph-13ec7\n81608 egyptian hieroglyph-13ec8\n81609 egyptian hieroglyph-13ec9\n81610 egyptian hieroglyph-13eca\n81611 egyptian hieroglyph-13ecb\n81612 egyptian hieroglyph-13ecc\n81613 egyptian hieroglyph-13ecd\n81614 egyptian hieroglyph-13ece\n81615 egyptian hieroglyph-13ecf\n81616 egyptian hieroglyph-13ed0\n81617 egyptian hieroglyph-13ed1\n81618 egyptian hieroglyph-13ed2\n81619 egyptian hieroglyph-13ed3\n81620 egyptian hieroglyph-13ed4\n81621 egyptian hieroglyph-13ed5\n81622 egyptian hieroglyph-13ed6\n81623 egyptian hieroglyph-13ed7\n81624 egyptian hieroglyph-13ed8\n81625 egyptian hieroglyph-13ed9\n81626 egyptian hieroglyph-13eda\n81627 egyptian hieroglyph-13edb\n81628 egyptian hieroglyph-13edc\n81629 egyptian hieroglyph-13edd\n81630 egyptian hieroglyph-13ede\n81631 egyptian hieroglyph-13edf\n81632 egyptian hieroglyph-13ee0\n81633 egyptian hieroglyph-13ee1\n81634 egyptian hieroglyph-13ee2\n81635 egyptian hieroglyph-13ee3\n81636 egyptian hieroglyph-13ee4\n81637 egyptian hieroglyph-13ee5\n81638 egyptian hieroglyph-13ee6\n81639 egyptian hieroglyph-13ee7\n81640 egyptian hieroglyph-13ee8\n81641 egyptian hieroglyph-13ee9\n81642 egyptian hieroglyph-13eea\n81643 egyptian hieroglyph-13eeb\n81644 egyptian hieroglyph-13eec\n81645 egyptian hieroglyph-13eed\n81646 egyptian hieroglyph-13eee\n81647 egyptian hieroglyph-13eef\n81648 egyptian hieroglyph-13ef0\n81649 egyptian hieroglyph-13ef1\n81650 egyptian hieroglyph-13ef2\n81651 egyptian hieroglyph-13ef3\n81652 egyptian hieroglyph-13ef4\n81653 egyptian hieroglyph-13ef5\n81654 egyptian hieroglyph-13ef6\n81655 egyptian hieroglyph-13ef7\n81656 egyptian hieroglyph-13ef8\n81657 egyptian hieroglyph-13ef9\n81658 egyptian hieroglyph-13efa\n81659 egyptian hieroglyph-13efb\n81660 egyptian hieroglyph-13efc\n81661 egyptian hieroglyph-13efd\n81662 egyptian hieroglyph-13efe\n81663 egyptian hieroglyph-13eff\n81664 egyptian hieroglyph-13f00\n81665 egyptian hieroglyph-13f01\n81666 egyptian hieroglyph-13f02\n81667 egyptian hieroglyph-13f03\n81668 egyptian hieroglyph-13f04\n81669 egyptian hieroglyph-13f05\n81670 egyptian hieroglyph-13f06\n81671 egyptian hieroglyph-13f07\n81672 egyptian hieroglyph-13f08\n81673 egyptian hieroglyph-13f09\n81674 egyptian hieroglyph-13f0a\n81675 egyptian hieroglyph-13f0b\n81676 egyptian hieroglyph-13f0c\n81677 egyptian hieroglyph-13f0d\n81678 egyptian hieroglyph-13f0e\n81679 egyptian hieroglyph-13f0f\n81680 egyptian hieroglyph-13f10\n81681 egyptian hieroglyph-13f11\n81682 egyptian hieroglyph-13f12\n81683 egyptian hieroglyph-13f13\n81684 egyptian hieroglyph-13f14\n81685 egyptian hieroglyph-13f15\n81686 egyptian hieroglyph-13f16\n81687 egyptian hieroglyph-13f17\n81688 egyptian hieroglyph-13f18\n81689 egyptian hieroglyph-13f19\n81690 egyptian hieroglyph-13f1a\n81691 egyptian hieroglyph-13f1b\n81692 egyptian hieroglyph-13f1c\n81693 egyptian hieroglyph-13f1d\n81694 egyptian hieroglyph-13f1e\n81695 egyptian hieroglyph-13f1f\n81696 egyptian hieroglyph-13f20\n81697 egyptian hieroglyph-13f21\n81698 egyptian hieroglyph-13f22\n81699 egyptian hieroglyph-13f23\n81700 egyptian hieroglyph-13f24\n81701 egyptian hieroglyph-13f25\n81702 egyptian hieroglyph-13f26\n81703 egyptian hieroglyph-13f27\n81704 egyptian hieroglyph-13f28\n81705 egyptian hieroglyph-13f29\n81706 egyptian hieroglyph-13f2a\n81707 egyptian hieroglyph-13f2b\n81708 egyptian hieroglyph-13f2c\n81709 egyptian hieroglyph-13f2d\n81710 egyptian hieroglyph-13f2e\n81711 egyptian hieroglyph-13f2f\n81712 egyptian hieroglyph-13f30\n81713 egyptian hieroglyph-13f31\n81714 egyptian hieroglyph-13f32\n81715 egyptian hieroglyph-13f33\n81716 egyptian hieroglyph-13f34\n81717 egyptian hieroglyph-13f35\n81718 egyptian hieroglyph-13f36\n81719 egyptian hieroglyph-13f37\n81720 egyptian hieroglyph-13f38\n81721 egyptian hieroglyph-13f39\n81722 egyptian hieroglyph-13f3a\n81723 egyptian hieroglyph-13f3b\n81724 egyptian hieroglyph-13f3c\n81725 egyptian hieroglyph-13f3d\n81726 egyptian hieroglyph-13f3e\n81727 egyptian hieroglyph-13f3f\n81728 egyptian hieroglyph-13f40\n81729 egyptian hieroglyph-13f41\n81730 egyptian hieroglyph-13f42\n81731 egyptian hieroglyph-13f43\n81732 egyptian hieroglyph-13f44\n81733 egyptian hieroglyph-13f45\n81734 egyptian hieroglyph-13f46\n81735 egyptian hieroglyph-13f47\n81736 egyptian hieroglyph-13f48\n81737 egyptian hieroglyph-13f49\n81738 egyptian hieroglyph-13f4a\n81739 egyptian hieroglyph-13f4b\n81740 egyptian hieroglyph-13f4c\n81741 egyptian hieroglyph-13f4d\n81742 egyptian hieroglyph-13f4e\n81743 egyptian hieroglyph-13f4f\n81744 egyptian hieroglyph-13f50\n81745 egyptian hieroglyph-13f51\n81746 egyptian hieroglyph-13f52\n81747 egyptian hieroglyph-13f53\n81748 egyptian hieroglyph-13f54\n81749 egyptian hieroglyph-13f55\n81750 egyptian hieroglyph-13f56\n81751 egyptian hieroglyph-13f57\n81752 egyptian hieroglyph-13f58\n81753 egyptian hieroglyph-13f59\n81754 egyptian hieroglyph-13f5a\n81755 egyptian hieroglyph-13f5b\n81756 egyptian hieroglyph-13f5c\n81757 egyptian hieroglyph-13f5d\n81758 egyptian hieroglyph-13f5e\n81759 egyptian hieroglyph-13f5f\n81760 egyptian hieroglyph-13f60\n81761 egyptian hieroglyph-13f61\n81762 egyptian hieroglyph-13f62\n81763 egyptian hieroglyph-13f63\n81764 egyptian hieroglyph-13f64\n81765 egyptian hieroglyph-13f65\n81766 egyptian hieroglyph-13f66\n81767 egyptian hieroglyph-13f67\n81768 egyptian hieroglyph-13f68\n81769 egyptian hieroglyph-13f69\n81770 egyptian hieroglyph-13f6a\n81771 egyptian hieroglyph-13f6b\n81772 egyptian hieroglyph-13f6c\n81773 egyptian hieroglyph-13f6d\n81774 egyptian hieroglyph-13f6e\n81775 egyptian hieroglyph-13f6f\n81776 egyptian hieroglyph-13f70\n81777 egyptian hieroglyph-13f71\n81778 egyptian hieroglyph-13f72\n81779 egyptian hieroglyph-13f73\n81780 egyptian hieroglyph-13f74\n81781 egyptian hieroglyph-13f75\n81782 egyptian hieroglyph-13f76\n81783 egyptian hieroglyph-13f77\n81784 egyptian hieroglyph-13f78\n81785 egyptian hieroglyph-13f79\n81786 egyptian hieroglyph-13f7a\n81787 egyptian hieroglyph-13f7b\n81788 egyptian hieroglyph-13f7c\n81789 egyptian hieroglyph-13f7d\n81790 egyptian hieroglyph-13f7e\n81791 egyptian hieroglyph-13f7f\n81792 egyptian hieroglyph-13f80\n81793 egyptian hieroglyph-13f81\n81794 egyptian hieroglyph-13f82\n81795 egyptian hieroglyph-13f83\n81796 egyptian hieroglyph-13f84\n81797 egyptian hieroglyph-13f85\n81798 egyptian hieroglyph-13f86\n81799 egyptian hieroglyph-13f87\n81800 egyptian hieroglyph-13f88\n81801 egyptian hieroglyph-13f89\n81802 egyptian hieroglyph-13f8a\n81803 egyptian hieroglyph-13f8b\n81804 egyptian hieroglyph-13f8c\n81805 egyptian hieroglyph-13f8d\n81806 egyptian hieroglyph-13f8e\n81807 egyptian hieroglyph-13f8f\n81808 egyptian hieroglyph-13f90\n81809 egyptian hieroglyph-13f91\n81810 egyptian hieroglyph-13f92\n81811 egyptian hieroglyph-13f93\n81812 egyptian hieroglyph-13f94\n81813 egyptian hieroglyph-13f95\n81814 egyptian hieroglyph-13f96\n81815 egyptian hieroglyph-13f97\n81816 egyptian hieroglyph-13f98\n81817 egyptian hieroglyph-13f99\n81818 egyptian hieroglyph-13f9a\n81819 egyptian hieroglyph-13f9b\n81820 egyptian hieroglyph-13f9c\n81821 egyptian hieroglyph-13f9d\n81822 egyptian hieroglyph-13f9e\n81823 egyptian hieroglyph-13f9f\n81824 egyptian hieroglyph-13fa0\n81825 egyptian hieroglyph-13fa1\n81826 egyptian hieroglyph-13fa2\n81827 egyptian hieroglyph-13fa3\n81828 egyptian hieroglyph-13fa4\n81829 egyptian hieroglyph-13fa5\n81830 egyptian hieroglyph-13fa6\n81831 egyptian hieroglyph-13fa7\n81832 egyptian hieroglyph-13fa8\n81833 egyptian hieroglyph-13fa9\n81834 egyptian hieroglyph-13faa\n81835 egyptian hieroglyph-13fab\n81836 egyptian hieroglyph-13fac\n81837 egyptian hieroglyph-13fad\n81838 egyptian hieroglyph-13fae\n81839 egyptian hieroglyph-13faf\n81840 egyptian hieroglyph-13fb0\n81841 egyptian hieroglyph-13fb1\n81842 egyptian hieroglyph-13fb2\n81843 egyptian hieroglyph-13fb3\n81844 egyptian hieroglyph-13fb4\n81845 egyptian hieroglyph-13fb5\n81846 egyptian hieroglyph-13fb6\n81847 egyptian hieroglyph-13fb7\n81848 egyptian hieroglyph-13fb8\n81849 egyptian hieroglyph-13fb9\n81850 egyptian hieroglyph-13fba\n81851 egyptian hieroglyph-13fbb\n81852 egyptian hieroglyph-13fbc\n81853 egyptian hieroglyph-13fbd\n81854 egyptian hieroglyph-13fbe\n81855 egyptian hieroglyph-13fbf\n81856 egyptian hieroglyph-13fc0\n81857 egyptian hieroglyph-13fc1\n81858 egyptian hieroglyph-13fc2\n81859 egyptian hieroglyph-13fc3\n81860 egyptian hieroglyph-13fc4\n81861 egyptian hieroglyph-13fc5\n81862 egyptian hieroglyph-13fc6\n81863 egyptian hieroglyph-13fc7\n81864 egyptian hieroglyph-13fc8\n81865 egyptian hieroglyph-13fc9\n81866 egyptian hieroglyph-13fca\n81867 egyptian hieroglyph-13fcb\n81868 egyptian hieroglyph-13fcc\n81869 egyptian hieroglyph-13fcd\n81870 egyptian hieroglyph-13fce\n81871 egyptian hieroglyph-13fcf\n81872 egyptian hieroglyph-13fd0\n81873 egyptian hieroglyph-13fd1\n81874 egyptian hieroglyph-13fd2\n81875 egyptian hieroglyph-13fd3\n81876 egyptian hieroglyph-13fd4\n81877 egyptian hieroglyph-13fd5\n81878 egyptian hieroglyph-13fd6\n81879 egyptian hieroglyph-13fd7\n81880 egyptian hieroglyph-13fd8\n81881 egyptian hieroglyph-13fd9\n81882 egyptian hieroglyph-13fda\n81883 egyptian hieroglyph-13fdb\n81884 egyptian hieroglyph-13fdc\n81885 egyptian hieroglyph-13fdd\n81886 egyptian hieroglyph-13fde\n81887 egyptian hieroglyph-13fdf\n81888 egyptian hieroglyph-13fe0\n81889 egyptian hieroglyph-13fe1\n81890 egyptian hieroglyph-13fe2\n81891 egyptian hieroglyph-13fe3\n81892 egyptian hieroglyph-13fe4\n81893 egyptian hieroglyph-13fe5\n81894 egyptian hieroglyph-13fe6\n81895 egyptian hieroglyph-13fe7\n81896 egyptian hieroglyph-13fe8\n81897 egyptian hieroglyph-13fe9\n81898 egyptian hieroglyph-13fea\n81899 egyptian hieroglyph-13feb\n81900 egyptian hieroglyph-13fec\n81901 egyptian hieroglyph-13fed\n81902 egyptian hieroglyph-13fee\n81903 egyptian hieroglyph-13fef\n81904 egyptian hieroglyph-13ff0\n81905 egyptian hieroglyph-13ff1\n81906 egyptian hieroglyph-13ff2\n81907 egyptian hieroglyph-13ff3\n81908 egyptian hieroglyph-13ff4\n81909 egyptian hieroglyph-13ff5\n81910 egyptian hieroglyph-13ff6\n81911 egyptian hieroglyph-13ff7\n81912 egyptian hieroglyph-13ff8\n81913 egyptian hieroglyph-13ff9\n81914 egyptian hieroglyph-13ffa\n81915 egyptian hieroglyph-13ffb\n81916 egyptian hieroglyph-13ffc\n81917 egyptian hieroglyph-13ffd\n81918 egyptian hieroglyph-13ffe\n81919 egyptian hieroglyph-13fff\n81920 egyptian hieroglyph-14000\n81921 egyptian hieroglyph-14001\n81922 egyptian hieroglyph-14002\n81923 egyptian hieroglyph-14003\n81924 egyptian hieroglyph-14004\n81925 egyptian hieroglyph-14005\n81926 egyptian hieroglyph-14006\n81927 egyptian hieroglyph-14007\n81928 egyptian hieroglyph-14008\n81929 egyptian hieroglyph-14009\n81930 egyptian hieroglyph-1400a\n81931 egyptian hieroglyph-1400b\n81932 egyptian hieroglyph-1400c\n81933 egyptian hieroglyph-1400d\n81934 egyptian hieroglyph-1400e\n81935 egyptian hieroglyph-1400f\n81936 egyptian hieroglyph-14010\n81937 egyptian hieroglyph-14011\n81938 egyptian hieroglyph-14012\n81939 egyptian hieroglyph-14013\n81940 egyptian hieroglyph-14014\n81941 egyptian hieroglyph-14015\n81942 egyptian hieroglyph-14016\n81943 egyptian hieroglyph-14017\n81944 egyptian hieroglyph-14018\n81945 egyptian hieroglyph-14019\n81946 egyptian hieroglyph-1401a\n81947 egyptian hieroglyph-1401b\n81948 egyptian hieroglyph-1401c\n81949 egyptian hieroglyph-1401d\n81950 egyptian hieroglyph-1401e\n81951 egyptian hieroglyph-1401f\n81952 egyptian hieroglyph-14020\n81953 egyptian hieroglyph-14021\n81954 egyptian hieroglyph-14022\n81955 egyptian hieroglyph-14023\n81956 egyptian hieroglyph-14024\n81957 egyptian hieroglyph-14025\n81958 egyptian hieroglyph-14026\n81959 egyptian hieroglyph-14027\n81960 egyptian hieroglyph-14028\n81961 egyptian hieroglyph-14029\n81962 egyptian hieroglyph-1402a\n81963 egyptian hieroglyph-1402b\n81964 egyptian hieroglyph-1402c\n81965 egyptian hieroglyph-1402d\n81966 egyptian hieroglyph-1402e\n81967 egyptian hieroglyph-1402f\n81968 egyptian hieroglyph-14030\n81969 egyptian hieroglyph-14031\n81970 egyptian hieroglyph-14032\n81971 egyptian hieroglyph-14033\n81972 egyptian hieroglyph-14034\n81973 egyptian hieroglyph-14035\n81974 egyptian hieroglyph-14036\n81975 egyptian hieroglyph-14037\n81976 egyptian hieroglyph-14038\n81977 egyptian hieroglyph-14039\n81978 egyptian hieroglyph-1403a\n81979 egyptian hieroglyph-1403b\n81980 egyptian hieroglyph-1403c\n81981 egyptian hieroglyph-1403d\n81982 egyptian hieroglyph-1403e\n81983 egyptian hieroglyph-1403f\n81984 egyptian hieroglyph-14040\n81985 egyptian hieroglyph-14041\n81986 egyptian hieroglyph-14042\n81987 egyptian hieroglyph-14043\n81988 egyptian hieroglyph-14044\n81989 egyptian hieroglyph-14045\n81990 egyptian hieroglyph-14046\n81991 egyptian hieroglyph-14047\n81992 egyptian hieroglyph-14048\n81993 egyptian hieroglyph-14049\n81994 egyptian hieroglyph-1404a\n81995 egyptian hieroglyph-1404b\n81996 egyptian hieroglyph-1404c\n81997 egyptian hieroglyph-1404d\n81998 egyptian hieroglyph-1404e\n81999 egyptian hieroglyph-1404f\n82000 egyptian hieroglyph-14050\n82001 egyptian hieroglyph-14051\n82002 egyptian hieroglyph-14052\n82003 egyptian hieroglyph-14053\n82004 egyptian hieroglyph-14054\n82005 egyptian hieroglyph-14055\n82006 egyptian hieroglyph-14056\n82007 egyptian hieroglyph-14057\n82008 egyptian hieroglyph-14058\n82009 egyptian hieroglyph-14059\n82010 egyptian hieroglyph-1405a\n82011 egyptian hieroglyph-1405b\n82012 egyptian hieroglyph-1405c\n82013 egyptian hieroglyph-1405d\n82014 egyptian hieroglyph-1405e\n82015 egyptian hieroglyph-1405f\n82016 egyptian hieroglyph-14060\n82017 egyptian hieroglyph-14061\n82018 egyptian hieroglyph-14062\n82019 egyptian hieroglyph-14063\n82020 egyptian hieroglyph-14064\n82021 egyptian hieroglyph-14065\n82022 egyptian hieroglyph-14066\n82023 egyptian hieroglyph-14067\n82024 egyptian hieroglyph-14068\n82025 egyptian hieroglyph-14069\n82026 egyptian hieroglyph-1406a\n82027 egyptian hieroglyph-1406b\n82028 egyptian hieroglyph-1406c\n82029 egyptian hieroglyph-1406d\n82030 egyptian hieroglyph-1406e\n82031 egyptian hieroglyph-1406f\n82032 egyptian hieroglyph-14070\n82033 egyptian hieroglyph-14071\n82034 egyptian hieroglyph-14072\n82035 egyptian hieroglyph-14073\n82036 egyptian hieroglyph-14074\n82037 egyptian hieroglyph-14075\n82038 egyptian hieroglyph-14076\n82039 egyptian hieroglyph-14077\n82040 egyptian hieroglyph-14078\n82041 egyptian hieroglyph-14079\n82042 egyptian hieroglyph-1407a\n82043 egyptian hieroglyph-1407b\n82044 egyptian hieroglyph-1407c\n82045 egyptian hieroglyph-1407d\n82046 egyptian hieroglyph-1407e\n82047 egyptian hieroglyph-1407f\n82048 egyptian hieroglyph-14080\n82049 egyptian hieroglyph-14081\n82050 egyptian hieroglyph-14082\n82051 egyptian hieroglyph-14083\n82052 egyptian hieroglyph-14084\n82053 egyptian hieroglyph-14085\n82054 egyptian hieroglyph-14086\n82055 egyptian hieroglyph-14087\n82056 egyptian hieroglyph-14088\n82057 egyptian hieroglyph-14089\n82058 egyptian hieroglyph-1408a\n82059 egyptian hieroglyph-1408b\n82060 egyptian hieroglyph-1408c\n82061 egyptian hieroglyph-1408d\n82062 egyptian hieroglyph-1408e\n82063 egyptian hieroglyph-1408f\n82064 egyptian hieroglyph-14090\n82065 egyptian hieroglyph-14091\n82066 egyptian hieroglyph-14092\n82067 egyptian hieroglyph-14093\n82068 egyptian hieroglyph-14094\n82069 egyptian hieroglyph-14095\n82070 egyptian hieroglyph-14096\n82071 egyptian hieroglyph-14097\n82072 egyptian hieroglyph-14098\n82073 egyptian hieroglyph-14099\n82074 egyptian hieroglyph-1409a\n82075 egyptian hieroglyph-1409b\n82076 egyptian hieroglyph-1409c\n82077 egyptian hieroglyph-1409d\n82078 egyptian hieroglyph-1409e\n82079 egyptian hieroglyph-1409f\n82080 egyptian hieroglyph-140a0\n82081 egyptian hieroglyph-140a1\n82082 egyptian hieroglyph-140a2\n82083 egyptian hieroglyph-140a3\n82084 egyptian hieroglyph-140a4\n82085 egyptian hieroglyph-140a5\n82086 egyptian hieroglyph-140a6\n82087 egyptian hieroglyph-140a7\n82088 egyptian hieroglyph-140a8\n82089 egyptian hieroglyph-140a9\n82090 egyptian hieroglyph-140aa\n82091 egyptian hieroglyph-140ab\n82092 egyptian hieroglyph-140ac\n82093 egyptian hieroglyph-140ad\n82094 egyptian hieroglyph-140ae\n82095 egyptian hieroglyph-140af\n82096 egyptian hieroglyph-140b0\n82097 egyptian hieroglyph-140b1\n82098 egyptian hieroglyph-140b2\n82099 egyptian hieroglyph-140b3\n82100 egyptian hieroglyph-140b4\n82101 egyptian hieroglyph-140b5\n82102 egyptian hieroglyph-140b6\n82103 egyptian hieroglyph-140b7\n82104 egyptian hieroglyph-140b8\n82105 egyptian hieroglyph-140b9\n82106 egyptian hieroglyph-140ba\n82107 egyptian hieroglyph-140bb\n82108 egyptian hieroglyph-140bc\n82109 egyptian hieroglyph-140bd\n82110 egyptian hieroglyph-140be\n82111 egyptian hieroglyph-140bf\n82112 egyptian hieroglyph-140c0\n82113 egyptian hieroglyph-140c1\n82114 egyptian hieroglyph-140c2\n82115 egyptian hieroglyph-140c3\n82116 egyptian hieroglyph-140c4\n82117 egyptian hieroglyph-140c5\n82118 egyptian hieroglyph-140c6\n82119 egyptian hieroglyph-140c7\n82120 egyptian hieroglyph-140c8\n82121 egyptian hieroglyph-140c9\n82122 egyptian hieroglyph-140ca\n82123 egyptian hieroglyph-140cb\n82124 egyptian hieroglyph-140cc\n82125 egyptian hieroglyph-140cd\n82126 egyptian hieroglyph-140ce\n82127 egyptian hieroglyph-140cf\n82128 egyptian hieroglyph-140d0\n82129 egyptian hieroglyph-140d1\n82130 egyptian hieroglyph-140d2\n82131 egyptian hieroglyph-140d3\n82132 egyptian hieroglyph-140d4\n82133 egyptian hieroglyph-140d5\n82134 egyptian hieroglyph-140d6\n82135 egyptian hieroglyph-140d7\n82136 egyptian hieroglyph-140d8\n82137 egyptian hieroglyph-140d9\n82138 egyptian hieroglyph-140da\n82139 egyptian hieroglyph-140db\n82140 egyptian hieroglyph-140dc\n82141 egyptian hieroglyph-140dd\n82142 egyptian hieroglyph-140de\n82143 egyptian hieroglyph-140df\n82144 egyptian hieroglyph-140e0\n82145 egyptian hieroglyph-140e1\n82146 egyptian hieroglyph-140e2\n82147 egyptian hieroglyph-140e3\n82148 egyptian hieroglyph-140e4\n82149 egyptian hieroglyph-140e5\n82150 egyptian hieroglyph-140e6\n82151 egyptian hieroglyph-140e7\n82152 egyptian hieroglyph-140e8\n82153 egyptian hieroglyph-140e9\n82154 egyptian hieroglyph-140ea\n82155 egyptian hieroglyph-140eb\n82156 egyptian hieroglyph-140ec\n82157 egyptian hieroglyph-140ed\n82158 egyptian hieroglyph-140ee\n82159 egyptian hieroglyph-140ef\n82160 egyptian hieroglyph-140f0\n82161 egyptian hieroglyph-140f1\n82162 egyptian hieroglyph-140f2\n82163 egyptian hieroglyph-140f3\n82164 egyptian hieroglyph-140f4\n82165 egyptian hieroglyph-140f5\n82166 egyptian hieroglyph-140f6\n82167 egyptian hieroglyph-140f7\n82168 egyptian hieroglyph-140f8\n82169 egyptian hieroglyph-140f9\n82170 egyptian hieroglyph-140fa\n82171 egyptian hieroglyph-140fb\n82172 egyptian hieroglyph-140fc\n82173 egyptian hieroglyph-140fd\n82174 egyptian hieroglyph-140fe\n82175 egyptian hieroglyph-140ff\n82176 egyptian hieroglyph-14100\n82177 egyptian hieroglyph-14101\n82178 egyptian hieroglyph-14102\n82179 egyptian hieroglyph-14103\n82180 egyptian hieroglyph-14104\n82181 egyptian hieroglyph-14105\n82182 egyptian hieroglyph-14106\n82183 egyptian hieroglyph-14107\n82184 egyptian hieroglyph-14108\n82185 egyptian hieroglyph-14109\n82186 egyptian hieroglyph-1410a\n82187 egyptian hieroglyph-1410b\n82188 egyptian hieroglyph-1410c\n82189 egyptian hieroglyph-1410d\n82190 egyptian hieroglyph-1410e\n82191 egyptian hieroglyph-1410f\n82192 egyptian hieroglyph-14110\n82193 egyptian hieroglyph-14111\n82194 egyptian hieroglyph-14112\n82195 egyptian hieroglyph-14113\n82196 egyptian hieroglyph-14114\n82197 egyptian hieroglyph-14115\n82198 egyptian hieroglyph-14116\n82199 egyptian hieroglyph-14117\n82200 egyptian hieroglyph-14118\n82201 egyptian hieroglyph-14119\n82202 egyptian hieroglyph-1411a\n82203 egyptian hieroglyph-1411b\n82204 egyptian hieroglyph-1411c\n82205 egyptian hieroglyph-1411d\n82206 egyptian hieroglyph-1411e\n82207 egyptian hieroglyph-1411f\n82208 egyptian hieroglyph-14120\n82209 egyptian hieroglyph-14121\n82210 egyptian hieroglyph-14122\n82211 egyptian hieroglyph-14123\n82212 egyptian hieroglyph-14124\n82213 egyptian hieroglyph-14125\n82214 egyptian hieroglyph-14126\n82215 egyptian hieroglyph-14127\n82216 egyptian hieroglyph-14128\n82217 egyptian hieroglyph-14129\n82218 egyptian hieroglyph-1412a\n82219 egyptian hieroglyph-1412b\n82220 egyptian hieroglyph-1412c\n82221 egyptian hieroglyph-1412d\n82222 egyptian hieroglyph-1412e\n82223 egyptian hieroglyph-1412f\n82224 egyptian hieroglyph-14130\n82225 egyptian hieroglyph-14131\n82226 egyptian hieroglyph-14132\n82227 egyptian hieroglyph-14133\n82228 egyptian hieroglyph-14134\n82229 egyptian hieroglyph-14135\n82230 egyptian hieroglyph-14136\n82231 egyptian hieroglyph-14137\n82232 egyptian hieroglyph-14138\n82233 egyptian hieroglyph-14139\n82234 egyptian hieroglyph-1413a\n82235 egyptian hieroglyph-1413b\n82236 egyptian hieroglyph-1413c\n82237 egyptian hieroglyph-1413d\n82238 egyptian hieroglyph-1413e\n82239 egyptian hieroglyph-1413f\n82240 egyptian hieroglyph-14140\n82241 egyptian hieroglyph-14141\n82242 egyptian hieroglyph-14142\n82243 egyptian hieroglyph-14143\n82244 egyptian hieroglyph-14144\n82245 egyptian hieroglyph-14145\n82246 egyptian hieroglyph-14146\n82247 egyptian hieroglyph-14147\n82248 egyptian hieroglyph-14148\n82249 egyptian hieroglyph-14149\n82250 egyptian hieroglyph-1414a\n82251 egyptian hieroglyph-1414b\n82252 egyptian hieroglyph-1414c\n82253 egyptian hieroglyph-1414d\n82254 egyptian hieroglyph-1414e\n82255 egyptian hieroglyph-1414f\n82256 egyptian hieroglyph-14150\n82257 egyptian hieroglyph-14151\n82258 egyptian hieroglyph-14152\n82259 egyptian hieroglyph-14153\n82260 egyptian hieroglyph-14154\n82261 egyptian hieroglyph-14155\n82262 egyptian hieroglyph-14156\n82263 egyptian hieroglyph-14157\n82264 egyptian hieroglyph-14158\n82265 egyptian hieroglyph-14159\n82266 egyptian hieroglyph-1415a\n82267 egyptian hieroglyph-1415b\n82268 egyptian hieroglyph-1415c\n82269 egyptian hieroglyph-1415d\n82270 egyptian hieroglyph-1415e\n82271 egyptian hieroglyph-1415f\n82272 egyptian hieroglyph-14160\n82273 egyptian hieroglyph-14161\n82274 egyptian hieroglyph-14162\n82275 egyptian hieroglyph-14163\n82276 egyptian hieroglyph-14164\n82277 egyptian hieroglyph-14165\n82278 egyptian hieroglyph-14166\n82279 egyptian hieroglyph-14167\n82280 egyptian hieroglyph-14168\n82281 egyptian hieroglyph-14169\n82282 egyptian hieroglyph-1416a\n82283 egyptian hieroglyph-1416b\n82284 egyptian hieroglyph-1416c\n82285 egyptian hieroglyph-1416d\n82286 egyptian hieroglyph-1416e\n82287 egyptian hieroglyph-1416f\n82288 egyptian hieroglyph-14170\n82289 egyptian hieroglyph-14171\n82290 egyptian hieroglyph-14172\n82291 egyptian hieroglyph-14173\n82292 egyptian hieroglyph-14174\n82293 egyptian hieroglyph-14175\n82294 egyptian hieroglyph-14176\n82295 egyptian hieroglyph-14177\n82296 egyptian hieroglyph-14178\n82297 egyptian hieroglyph-14179\n82298 egyptian hieroglyph-1417a\n82299 egyptian hieroglyph-1417b\n82300 egyptian hieroglyph-1417c\n82301 egyptian hieroglyph-1417d\n82302 egyptian hieroglyph-1417e\n82303 egyptian hieroglyph-1417f\n82304 egyptian hieroglyph-14180\n82305 egyptian hieroglyph-14181\n82306 egyptian hieroglyph-14182\n82307 egyptian hieroglyph-14183\n82308 egyptian hieroglyph-14184\n82309 egyptian hieroglyph-14185\n82310 egyptian hieroglyph-14186\n82311 egyptian hieroglyph-14187\n82312 egyptian hieroglyph-14188\n82313 egyptian hieroglyph-14189\n82314 egyptian hieroglyph-1418a\n82315 egyptian hieroglyph-1418b\n82316 egyptian hieroglyph-1418c\n82317 egyptian hieroglyph-1418d\n82318 egyptian hieroglyph-1418e\n82319 egyptian hieroglyph-1418f\n82320 egyptian hieroglyph-14190\n82321 egyptian hieroglyph-14191\n82322 egyptian hieroglyph-14192\n82323 egyptian hieroglyph-14193\n82324 egyptian hieroglyph-14194\n82325 egyptian hieroglyph-14195\n82326 egyptian hieroglyph-14196\n82327 egyptian hieroglyph-14197\n82328 egyptian hieroglyph-14198\n82329 egyptian hieroglyph-14199\n82330 egyptian hieroglyph-1419a\n82331 egyptian hieroglyph-1419b\n82332 egyptian hieroglyph-1419c\n82333 egyptian hieroglyph-1419d\n82334 egyptian hieroglyph-1419e\n82335 egyptian hieroglyph-1419f\n82336 egyptian hieroglyph-141a0\n82337 egyptian hieroglyph-141a1\n82338 egyptian hieroglyph-141a2\n82339 egyptian hieroglyph-141a3\n82340 egyptian hieroglyph-141a4\n82341 egyptian hieroglyph-141a5\n82342 egyptian hieroglyph-141a6\n82343 egyptian hieroglyph-141a7\n82344 egyptian hieroglyph-141a8\n82345 egyptian hieroglyph-141a9\n82346 egyptian hieroglyph-141aa\n82347 egyptian hieroglyph-141ab\n82348 egyptian hieroglyph-141ac\n82349 egyptian hieroglyph-141ad\n82350 egyptian hieroglyph-141ae\n82351 egyptian hieroglyph-141af\n82352 egyptian hieroglyph-141b0\n82353 egyptian hieroglyph-141b1\n82354 egyptian hieroglyph-141b2\n82355 egyptian hieroglyph-141b3\n82356 egyptian hieroglyph-141b4\n82357 egyptian hieroglyph-141b5\n82358 egyptian hieroglyph-141b6\n82359 egyptian hieroglyph-141b7\n82360 egyptian hieroglyph-141b8\n82361 egyptian hieroglyph-141b9\n82362 egyptian hieroglyph-141ba\n82363 egyptian hieroglyph-141bb\n82364 egyptian hieroglyph-141bc\n82365 egyptian hieroglyph-141bd\n82366 egyptian hieroglyph-141be\n82367 egyptian hieroglyph-141bf\n82368 egyptian hieroglyph-141c0\n82369 egyptian hieroglyph-141c1\n82370 egyptian hieroglyph-141c2\n82371 egyptian hieroglyph-141c3\n82372 egyptian hieroglyph-141c4\n82373 egyptian hieroglyph-141c5\n82374 egyptian hieroglyph-141c6\n82375 egyptian hieroglyph-141c7\n82376 egyptian hieroglyph-141c8\n82377 egyptian hieroglyph-141c9\n82378 egyptian hieroglyph-141ca\n82379 egyptian hieroglyph-141cb\n82380 egyptian hieroglyph-141cc\n82381 egyptian hieroglyph-141cd\n82382 egyptian hieroglyph-141ce\n82383 egyptian hieroglyph-141cf\n82384 egyptian hieroglyph-141d0\n82385 egyptian hieroglyph-141d1\n82386 egyptian hieroglyph-141d2\n82387 egyptian hieroglyph-141d3\n82388 egyptian hieroglyph-141d4\n82389 egyptian hieroglyph-141d5\n82390 egyptian hieroglyph-141d6\n82391 egyptian hieroglyph-141d7\n82392 egyptian hieroglyph-141d8\n82393 egyptian hieroglyph-141d9\n82394 egyptian hieroglyph-141da\n82395 egyptian hieroglyph-141db\n82396 egyptian hieroglyph-141dc\n82397 egyptian hieroglyph-141dd\n82398 egyptian hieroglyph-141de\n82399 egyptian hieroglyph-141df\n82400 egyptian hieroglyph-141e0\n82401 egyptian hieroglyph-141e1\n82402 egyptian hieroglyph-141e2\n82403 egyptian hieroglyph-141e3\n82404 egyptian hieroglyph-141e4\n82405 egyptian hieroglyph-141e5\n82406 egyptian hieroglyph-141e6\n82407 egyptian hieroglyph-141e7\n82408 egyptian hieroglyph-141e8\n82409 egyptian hieroglyph-141e9\n82410 egyptian hieroglyph-141ea\n82411 egyptian hieroglyph-141eb\n82412 egyptian hieroglyph-141ec\n82413 egyptian hieroglyph-141ed\n82414 egyptian hieroglyph-141ee\n82415 egyptian hieroglyph-141ef\n82416 egyptian hieroglyph-141f0\n82417 egyptian hieroglyph-141f1\n82418 egyptian hieroglyph-141f2\n82419 egyptian hieroglyph-141f3\n82420 egyptian hieroglyph-141f4\n82421 egyptian hieroglyph-141f5\n82422 egyptian hieroglyph-141f6\n82423 egyptian hieroglyph-141f7\n82424 egyptian hieroglyph-141f8\n82425 egyptian hieroglyph-141f9\n82426 egyptian hieroglyph-141fa\n82427 egyptian hieroglyph-141fb\n82428 egyptian hieroglyph-141fc\n82429 egyptian hieroglyph-141fd\n82430 egyptian hieroglyph-141fe\n82431 egyptian hieroglyph-141ff\n82432 egyptian hieroglyph-14200\n82433 egyptian hieroglyph-14201\n82434 egyptian hieroglyph-14202\n82435 egyptian hieroglyph-14203\n82436 egyptian hieroglyph-14204\n82437 egyptian hieroglyph-14205\n82438 egyptian hieroglyph-14206\n82439 egyptian hieroglyph-14207\n82440 egyptian hieroglyph-14208\n82441 egyptian hieroglyph-14209\n82442 egyptian hieroglyph-1420a\n82443 egyptian hieroglyph-1420b\n82444 egyptian hieroglyph-1420c\n82445 egyptian hieroglyph-1420d\n82446 egyptian hieroglyph-1420e\n82447 egyptian hieroglyph-1420f\n82448 egyptian hieroglyph-14210\n82449 egyptian hieroglyph-14211\n82450 egyptian hieroglyph-14212\n82451 egyptian hieroglyph-14213\n82452 egyptian hieroglyph-14214\n82453 egyptian hieroglyph-14215\n82454 egyptian hieroglyph-14216\n82455 egyptian hieroglyph-14217\n82456 egyptian hieroglyph-14218\n82457 egyptian hieroglyph-14219\n82458 egyptian hieroglyph-1421a\n82459 egyptian hieroglyph-1421b\n82460 egyptian hieroglyph-1421c\n82461 egyptian hieroglyph-1421d\n82462 egyptian hieroglyph-1421e\n82463 egyptian hieroglyph-1421f\n82464 egyptian hieroglyph-14220\n82465 egyptian hieroglyph-14221\n82466 egyptian hieroglyph-14222\n82467 egyptian hieroglyph-14223\n82468 egyptian hieroglyph-14224\n82469 egyptian hieroglyph-14225\n82470 egyptian hieroglyph-14226\n82471 egyptian hieroglyph-14227\n82472 egyptian hieroglyph-14228\n82473 egyptian hieroglyph-14229\n82474 egyptian hieroglyph-1422a\n82475 egyptian hieroglyph-1422b\n82476 egyptian hieroglyph-1422c\n82477 egyptian hieroglyph-1422d\n82478 egyptian hieroglyph-1422e\n82479 egyptian hieroglyph-1422f\n82480 egyptian hieroglyph-14230\n82481 egyptian hieroglyph-14231\n82482 egyptian hieroglyph-14232\n82483 egyptian hieroglyph-14233\n82484 egyptian hieroglyph-14234\n82485 egyptian hieroglyph-14235\n82486 egyptian hieroglyph-14236\n82487 egyptian hieroglyph-14237\n82488 egyptian hieroglyph-14238\n82489 egyptian hieroglyph-14239\n82490 egyptian hieroglyph-1423a\n82491 egyptian hieroglyph-1423b\n82492 egyptian hieroglyph-1423c\n82493 egyptian hieroglyph-1423d\n82494 egyptian hieroglyph-1423e\n82495 egyptian hieroglyph-1423f\n82496 egyptian hieroglyph-14240\n82497 egyptian hieroglyph-14241\n82498 egyptian hieroglyph-14242\n82499 egyptian hieroglyph-14243\n82500 egyptian hieroglyph-14244\n82501 egyptian hieroglyph-14245\n82502 egyptian hieroglyph-14246\n82503 egyptian hieroglyph-14247\n82504 egyptian hieroglyph-14248\n82505 egyptian hieroglyph-14249\n82506 egyptian hieroglyph-1424a\n82507 egyptian hieroglyph-1424b\n82508 egyptian hieroglyph-1424c\n82509 egyptian hieroglyph-1424d\n82510 egyptian hieroglyph-1424e\n82511 egyptian hieroglyph-1424f\n82512 egyptian hieroglyph-14250\n82513 egyptian hieroglyph-14251\n82514 egyptian hieroglyph-14252\n82515 egyptian hieroglyph-14253\n82516 egyptian hieroglyph-14254\n82517 egyptian hieroglyph-14255\n82518 egyptian hieroglyph-14256\n82519 egyptian hieroglyph-14257\n82520 egyptian hieroglyph-14258\n82521 egyptian hieroglyph-14259\n82522 egyptian hieroglyph-1425a\n82523 egyptian hieroglyph-1425b\n82524 egyptian hieroglyph-1425c\n82525 egyptian hieroglyph-1425d\n82526 egyptian hieroglyph-1425e\n82527 egyptian hieroglyph-1425f\n82528 egyptian hieroglyph-14260\n82529 egyptian hieroglyph-14261\n82530 egyptian hieroglyph-14262\n82531 egyptian hieroglyph-14263\n82532 egyptian hieroglyph-14264\n82533 egyptian hieroglyph-14265\n82534 egyptian hieroglyph-14266\n82535 egyptian hieroglyph-14267\n82536 egyptian hieroglyph-14268\n82537 egyptian hieroglyph-14269\n82538 egyptian hieroglyph-1426a\n82539 egyptian hieroglyph-1426b\n82540 egyptian hieroglyph-1426c\n82541 egyptian hieroglyph-1426d\n82542 egyptian hieroglyph-1426e\n82543 egyptian hieroglyph-1426f\n82544 egyptian hieroglyph-14270\n82545 egyptian hieroglyph-14271\n82546 egyptian hieroglyph-14272\n82547 egyptian hieroglyph-14273\n82548 egyptian hieroglyph-14274\n82549 egyptian hieroglyph-14275\n82550 egyptian hieroglyph-14276\n82551 egyptian hieroglyph-14277\n82552 egyptian hieroglyph-14278\n82553 egyptian hieroglyph-14279\n82554 egyptian hieroglyph-1427a\n82555 egyptian hieroglyph-1427b\n82556 egyptian hieroglyph-1427c\n82557 egyptian hieroglyph-1427d\n82558 egyptian hieroglyph-1427e\n82559 egyptian hieroglyph-1427f\n82560 egyptian hieroglyph-14280\n82561 egyptian hieroglyph-14281\n82562 egyptian hieroglyph-14282\n82563 egyptian hieroglyph-14283\n82564 egyptian hieroglyph-14284\n82565 egyptian hieroglyph-14285\n82566 egyptian hieroglyph-14286\n82567 egyptian hieroglyph-14287\n82568 egyptian hieroglyph-14288\n82569 egyptian hieroglyph-14289\n82570 egyptian hieroglyph-1428a\n82571 egyptian hieroglyph-1428b\n82572 egyptian hieroglyph-1428c\n82573 egyptian hieroglyph-1428d\n82574 egyptian hieroglyph-1428e\n82575 egyptian hieroglyph-1428f\n82576 egyptian hieroglyph-14290\n82577 egyptian hieroglyph-14291\n82578 egyptian hieroglyph-14292\n82579 egyptian hieroglyph-14293\n82580 egyptian hieroglyph-14294\n82581 egyptian hieroglyph-14295\n82582 egyptian hieroglyph-14296\n82583 egyptian hieroglyph-14297\n82584 egyptian hieroglyph-14298\n82585 egyptian hieroglyph-14299\n82586 egyptian hieroglyph-1429a\n82587 egyptian hieroglyph-1429b\n82588 egyptian hieroglyph-1429c\n82589 egyptian hieroglyph-1429d\n82590 egyptian hieroglyph-1429e\n82591 egyptian hieroglyph-1429f\n82592 egyptian hieroglyph-142a0\n82593 egyptian hieroglyph-142a1\n82594 egyptian hieroglyph-142a2\n82595 egyptian hieroglyph-142a3\n82596 egyptian hieroglyph-142a4\n82597 egyptian hieroglyph-142a5\n82598 egyptian hieroglyph-142a6\n82599 egyptian hieroglyph-142a7\n82600 egyptian hieroglyph-142a8\n82601 egyptian hieroglyph-142a9\n82602 egyptian hieroglyph-142aa\n82603 egyptian hieroglyph-142ab\n82604 egyptian hieroglyph-142ac\n82605 egyptian hieroglyph-142ad\n82606 egyptian hieroglyph-142ae\n82607 egyptian hieroglyph-142af\n82608 egyptian hieroglyph-142b0\n82609 egyptian hieroglyph-142b1\n82610 egyptian hieroglyph-142b2\n82611 egyptian hieroglyph-142b3\n82612 egyptian hieroglyph-142b4\n82613 egyptian hieroglyph-142b5\n82614 egyptian hieroglyph-142b6\n82615 egyptian hieroglyph-142b7\n82616 egyptian hieroglyph-142b8\n82617 egyptian hieroglyph-142b9\n82618 egyptian hieroglyph-142ba\n82619 egyptian hieroglyph-142bb\n82620 egyptian hieroglyph-142bc\n82621 egyptian hieroglyph-142bd\n82622 egyptian hieroglyph-142be\n82623 egyptian hieroglyph-142bf\n82624 egyptian hieroglyph-142c0\n82625 egyptian hieroglyph-142c1\n82626 egyptian hieroglyph-142c2\n82627 egyptian hieroglyph-142c3\n82628 egyptian hieroglyph-142c4\n82629 egyptian hieroglyph-142c5\n82630 egyptian hieroglyph-142c6\n82631 egyptian hieroglyph-142c7\n82632 egyptian hieroglyph-142c8\n82633 egyptian hieroglyph-142c9\n82634 egyptian hieroglyph-142ca\n82635 egyptian hieroglyph-142cb\n82636 egyptian hieroglyph-142cc\n82637 egyptian hieroglyph-142cd\n82638 egyptian hieroglyph-142ce\n82639 egyptian hieroglyph-142cf\n82640 egyptian hieroglyph-142d0\n82641 egyptian hieroglyph-142d1\n82642 egyptian hieroglyph-142d2\n82643 egyptian hieroglyph-142d3\n82644 egyptian hieroglyph-142d4\n82645 egyptian hieroglyph-142d5\n82646 egyptian hieroglyph-142d6\n82647 egyptian hieroglyph-142d7\n82648 egyptian hieroglyph-142d8\n82649 egyptian hieroglyph-142d9\n82650 egyptian hieroglyph-142da\n82651 egyptian hieroglyph-142db\n82652 egyptian hieroglyph-142dc\n82653 egyptian hieroglyph-142dd\n82654 egyptian hieroglyph-142de\n82655 egyptian hieroglyph-142df\n82656 egyptian hieroglyph-142e0\n82657 egyptian hieroglyph-142e1\n82658 egyptian hieroglyph-142e2\n82659 egyptian hieroglyph-142e3\n82660 egyptian hieroglyph-142e4\n82661 egyptian hieroglyph-142e5\n82662 egyptian hieroglyph-142e6\n82663 egyptian hieroglyph-142e7\n82664 egyptian hieroglyph-142e8\n82665 egyptian hieroglyph-142e9\n82666 egyptian hieroglyph-142ea\n82667 egyptian hieroglyph-142eb\n82668 egyptian hieroglyph-142ec\n82669 egyptian hieroglyph-142ed\n82670 egyptian hieroglyph-142ee\n82671 egyptian hieroglyph-142ef\n82672 egyptian hieroglyph-142f0\n82673 egyptian hieroglyph-142f1\n82674 egyptian hieroglyph-142f2\n82675 egyptian hieroglyph-142f3\n82676 egyptian hieroglyph-142f4\n82677 egyptian hieroglyph-142f5\n82678 egyptian hieroglyph-142f6\n82679 egyptian hieroglyph-142f7\n82680 egyptian hieroglyph-142f8\n82681 egyptian hieroglyph-142f9\n82682 egyptian hieroglyph-142fa\n82683 egyptian hieroglyph-142fb\n82684 egyptian hieroglyph-142fc\n82685 egyptian hieroglyph-142fd\n82686 egyptian hieroglyph-142fe\n82687 egyptian hieroglyph-142ff\n82688 egyptian hieroglyph-14300\n82689 egyptian hieroglyph-14301\n82690 egyptian hieroglyph-14302\n82691 egyptian hieroglyph-14303\n82692 egyptian hieroglyph-14304\n82693 egyptian hieroglyph-14305\n82694 egyptian hieroglyph-14306\n82695 egyptian hieroglyph-14307\n82696 egyptian hieroglyph-14308\n82697 egyptian hieroglyph-14309\n82698 egyptian hieroglyph-1430a\n82699 egyptian hieroglyph-1430b\n82700 egyptian hieroglyph-1430c\n82701 egyptian hieroglyph-1430d\n82702 egyptian hieroglyph-1430e\n82703 egyptian hieroglyph-1430f\n82704 egyptian hieroglyph-14310\n82705 egyptian hieroglyph-14311\n82706 egyptian hieroglyph-14312\n82707 egyptian hieroglyph-14313\n82708 egyptian hieroglyph-14314\n82709 egyptian hieroglyph-14315\n82710 egyptian hieroglyph-14316\n82711 egyptian hieroglyph-14317\n82712 egyptian hieroglyph-14318\n82713 egyptian hieroglyph-14319\n82714 egyptian hieroglyph-1431a\n82715 egyptian hieroglyph-1431b\n82716 egyptian hieroglyph-1431c\n82717 egyptian hieroglyph-1431d\n82718 egyptian hieroglyph-1431e\n82719 egyptian hieroglyph-1431f\n82720 egyptian hieroglyph-14320\n82721 egyptian hieroglyph-14321\n82722 egyptian hieroglyph-14322\n82723 egyptian hieroglyph-14323\n82724 egyptian hieroglyph-14324\n82725 egyptian hieroglyph-14325\n82726 egyptian hieroglyph-14326\n82727 egyptian hieroglyph-14327\n82728 egyptian hieroglyph-14328\n82729 egyptian hieroglyph-14329\n82730 egyptian hieroglyph-1432a\n82731 egyptian hieroglyph-1432b\n82732 egyptian hieroglyph-1432c\n82733 egyptian hieroglyph-1432d\n82734 egyptian hieroglyph-1432e\n82735 egyptian hieroglyph-1432f\n82736 egyptian hieroglyph-14330\n82737 egyptian hieroglyph-14331\n82738 egyptian hieroglyph-14332\n82739 egyptian hieroglyph-14333\n82740 egyptian hieroglyph-14334\n82741 egyptian hieroglyph-14335\n82742 egyptian hieroglyph-14336\n82743 egyptian hieroglyph-14337\n82744 egyptian hieroglyph-14338\n82745 egyptian hieroglyph-14339\n82746 egyptian hieroglyph-1433a\n82747 egyptian hieroglyph-1433b\n82748 egyptian hieroglyph-1433c\n82749 egyptian hieroglyph-1433d\n82750 egyptian hieroglyph-1433e\n82751 egyptian hieroglyph-1433f\n82752 egyptian hieroglyph-14340\n82753 egyptian hieroglyph-14341\n82754 egyptian hieroglyph-14342\n82755 egyptian hieroglyph-14343\n82756 egyptian hieroglyph-14344\n82757 egyptian hieroglyph-14345\n82758 egyptian hieroglyph-14346\n82759 egyptian hieroglyph-14347\n82760 egyptian hieroglyph-14348\n82761 egyptian hieroglyph-14349\n82762 egyptian hieroglyph-1434a\n82763 egyptian hieroglyph-1434b\n82764 egyptian hieroglyph-1434c\n82765 egyptian hieroglyph-1434d\n82766 egyptian hieroglyph-1434e\n82767 egyptian hieroglyph-1434f\n82768 egyptian hieroglyph-14350\n82769 egyptian hieroglyph-14351\n82770 egyptian hieroglyph-14352\n82771 egyptian hieroglyph-14353\n82772 egyptian hieroglyph-14354\n82773 egyptian hieroglyph-14355\n82774 egyptian hieroglyph-14356\n82775 egyptian hieroglyph-14357\n82776 egyptian hieroglyph-14358\n82777 egyptian hieroglyph-14359\n82778 egyptian hieroglyph-1435a\n82779 egyptian hieroglyph-1435b\n82780 egyptian hieroglyph-1435c\n82781 egyptian hieroglyph-1435d\n82782 egyptian hieroglyph-1435e\n82783 egyptian hieroglyph-1435f\n82784 egyptian hieroglyph-14360\n82785 egyptian hieroglyph-14361\n82786 egyptian hieroglyph-14362\n82787 egyptian hieroglyph-14363\n82788 egyptian hieroglyph-14364\n82789 egyptian hieroglyph-14365\n82790 egyptian hieroglyph-14366\n82791 egyptian hieroglyph-14367\n82792 egyptian hieroglyph-14368\n82793 egyptian hieroglyph-14369\n82794 egyptian hieroglyph-1436a\n82795 egyptian hieroglyph-1436b\n82796 egyptian hieroglyph-1436c\n82797 egyptian hieroglyph-1436d\n82798 egyptian hieroglyph-1436e\n82799 egyptian hieroglyph-1436f\n82800 egyptian hieroglyph-14370\n82801 egyptian hieroglyph-14371\n82802 egyptian hieroglyph-14372\n82803 egyptian hieroglyph-14373\n82804 egyptian hieroglyph-14374\n82805 egyptian hieroglyph-14375\n82806 egyptian hieroglyph-14376\n82807 egyptian hieroglyph-14377\n82808 egyptian hieroglyph-14378\n82809 egyptian hieroglyph-14379\n82810 egyptian hieroglyph-1437a\n82811 egyptian hieroglyph-1437b\n82812 egyptian hieroglyph-1437c\n82813 egyptian hieroglyph-1437d\n82814 egyptian hieroglyph-1437e\n82815 egyptian hieroglyph-1437f\n82816 egyptian hieroglyph-14380\n82817 egyptian hieroglyph-14381\n82818 egyptian hieroglyph-14382\n82819 egyptian hieroglyph-14383\n82820 egyptian hieroglyph-14384\n82821 egyptian hieroglyph-14385\n82822 egyptian hieroglyph-14386\n82823 egyptian hieroglyph-14387\n82824 egyptian hieroglyph-14388\n82825 egyptian hieroglyph-14389\n82826 egyptian hieroglyph-1438a\n82827 egyptian hieroglyph-1438b\n82828 egyptian hieroglyph-1438c\n82829 egyptian hieroglyph-1438d\n82830 egyptian hieroglyph-1438e\n82831 egyptian hieroglyph-1438f\n82832 egyptian hieroglyph-14390\n82833 egyptian hieroglyph-14391\n82834 egyptian hieroglyph-14392\n82835 egyptian hieroglyph-14393\n82836 egyptian hieroglyph-14394\n82837 egyptian hieroglyph-14395\n82838 egyptian hieroglyph-14396\n82839 egyptian hieroglyph-14397\n82840 egyptian hieroglyph-14398\n82841 egyptian hieroglyph-14399\n82842 egyptian hieroglyph-1439a\n82843 egyptian hieroglyph-1439b\n82844 egyptian hieroglyph-1439c\n82845 egyptian hieroglyph-1439d\n82846 egyptian hieroglyph-1439e\n82847 egyptian hieroglyph-1439f\n82848 egyptian hieroglyph-143a0\n82849 egyptian hieroglyph-143a1\n82850 egyptian hieroglyph-143a2\n82851 egyptian hieroglyph-143a3\n82852 egyptian hieroglyph-143a4\n82853 egyptian hieroglyph-143a5\n82854 egyptian hieroglyph-143a6\n82855 egyptian hieroglyph-143a7\n82856 egyptian hieroglyph-143a8\n82857 egyptian hieroglyph-143a9\n82858 egyptian hieroglyph-143aa\n82859 egyptian hieroglyph-143ab\n82860 egyptian hieroglyph-143ac\n82861 egyptian hieroglyph-143ad\n82862 egyptian hieroglyph-143ae\n82863 egyptian hieroglyph-143af\n82864 egyptian hieroglyph-143b0\n82865 egyptian hieroglyph-143b1\n82866 egyptian hieroglyph-143b2\n82867 egyptian hieroglyph-143b3\n82868 egyptian hieroglyph-143b4\n82869 egyptian hieroglyph-143b5\n82870 egyptian hieroglyph-143b6\n82871 egyptian hieroglyph-143b7\n82872 egyptian hieroglyph-143b8\n82873 egyptian hieroglyph-143b9\n82874 egyptian hieroglyph-143ba\n82875 egyptian hieroglyph-143bb\n82876 egyptian hieroglyph-143bc\n82877 egyptian hieroglyph-143bd\n82878 egyptian hieroglyph-143be\n82879 egyptian hieroglyph-143bf\n82880 egyptian hieroglyph-143c0\n82881 egyptian hieroglyph-143c1\n82882 egyptian hieroglyph-143c2\n82883 egyptian hieroglyph-143c3\n82884 egyptian hieroglyph-143c4\n82885 egyptian hieroglyph-143c5\n82886 egyptian hieroglyph-143c6\n82887 egyptian hieroglyph-143c7\n82888 egyptian hieroglyph-143c8\n82889 egyptian hieroglyph-143c9\n82890 egyptian hieroglyph-143ca\n82891 egyptian hieroglyph-143cb\n82892 egyptian hieroglyph-143cc\n82893 egyptian hieroglyph-143cd\n82894 egyptian hieroglyph-143ce\n82895 egyptian hieroglyph-143cf\n82896 egyptian hieroglyph-143d0\n82897 egyptian hieroglyph-143d1\n82898 egyptian hieroglyph-143d2\n82899 egyptian hieroglyph-143d3\n82900 egyptian hieroglyph-143d4\n82901 egyptian hieroglyph-143d5\n82902 egyptian hieroglyph-143d6\n82903 egyptian hieroglyph-143d7\n82904 egyptian hieroglyph-143d8\n82905 egyptian hieroglyph-143d9\n82906 egyptian hieroglyph-143da\n82907 egyptian hieroglyph-143db\n82908 egyptian hieroglyph-143dc\n82909 egyptian hieroglyph-143dd\n82910 egyptian hieroglyph-143de\n82911 egyptian hieroglyph-143df\n82912 egyptian hieroglyph-143e0\n82913 egyptian hieroglyph-143e1\n82914 egyptian hieroglyph-143e2\n82915 egyptian hieroglyph-143e3\n82916 egyptian hieroglyph-143e4\n82917 egyptian hieroglyph-143e5\n82918 egyptian hieroglyph-143e6\n82919 egyptian hieroglyph-143e7\n82920 egyptian hieroglyph-143e8\n82921 egyptian hieroglyph-143e9\n82922 egyptian hieroglyph-143ea\n82923 egyptian hieroglyph-143eb\n82924 egyptian hieroglyph-143ec\n82925 egyptian hieroglyph-143ed\n82926 egyptian hieroglyph-143ee\n82927 egyptian hieroglyph-143ef\n82928 egyptian hieroglyph-143f0\n82929 egyptian hieroglyph-143f1\n82930 egyptian hieroglyph-143f2\n82931 egyptian hieroglyph-143f3\n82932 egyptian hieroglyph-143f4\n82933 egyptian hieroglyph-143f5\n82934 egyptian hieroglyph-143f6\n82935 egyptian hieroglyph-143f7\n82936 egyptian hieroglyph-143f8\n82937 egyptian hieroglyph-143f9\n82938 egyptian hieroglyph-143fa\n82944 anatolian hieroglyph a001\n82945 anatolian hieroglyph a002\n82946 anatolian hieroglyph a003\n82947 anatolian hieroglyph a004\n82948 anatolian hieroglyph a005\n82949 anatolian hieroglyph a006\n82950 anatolian hieroglyph a007\n82951 anatolian hieroglyph a008\n82952 anatolian hieroglyph a009\n82953 anatolian hieroglyph a010\n82954 anatolian hieroglyph a010a\n82955 anatolian hieroglyph a011\n82956 anatolian hieroglyph a012\n82957 anatolian hieroglyph a013\n82958 anatolian hieroglyph a014\n82959 anatolian hieroglyph a015\n82960 anatolian hieroglyph a016\n82961 anatolian hieroglyph a017\n82962 anatolian hieroglyph a018\n82963 anatolian hieroglyph a019\n82964 anatolian hieroglyph a020\n82965 anatolian hieroglyph a021\n82966 anatolian hieroglyph a022\n82967 anatolian hieroglyph a023\n82968 anatolian hieroglyph a024\n82969 anatolian hieroglyph a025\n82970 anatolian hieroglyph a026\n82971 anatolian hieroglyph a026a\n82972 anatolian hieroglyph a027\n82973 anatolian hieroglyph a028\n82974 anatolian hieroglyph a029\n82975 anatolian hieroglyph a030\n82976 anatolian hieroglyph a031\n82977 anatolian hieroglyph a032\n82978 anatolian hieroglyph a033\n82979 anatolian hieroglyph a034\n82980 anatolian hieroglyph a035\n82981 anatolian hieroglyph a036\n82982 anatolian hieroglyph a037\n82983 anatolian hieroglyph a038\n82984 anatolian hieroglyph a039\n82985 anatolian hieroglyph a039a\n82986 anatolian hieroglyph a040\n82987 anatolian hieroglyph a041\n82988 anatolian hieroglyph a041a\n82989 anatolian hieroglyph a042\n82990 anatolian hieroglyph a043\n82991 anatolian hieroglyph a044\n82992 anatolian hieroglyph a045\n82993 anatolian hieroglyph a045a\n82994 anatolian hieroglyph a046\n82995 anatolian hieroglyph a046a\n82996 anatolian hieroglyph a046b\n82997 anatolian hieroglyph a047\n82998 anatolian hieroglyph a048\n82999 anatolian hieroglyph a049\n83000 anatolian hieroglyph a050\n83001 anatolian hieroglyph a051\n83002 anatolian hieroglyph a052\n83003 anatolian hieroglyph a053\n83004 anatolian hieroglyph a054\n83005 anatolian hieroglyph a055\n83006 anatolian hieroglyph a056\n83007 anatolian hieroglyph a057\n83008 anatolian hieroglyph a058\n83009 anatolian hieroglyph a059\n83010 anatolian hieroglyph a060\n83011 anatolian hieroglyph a061\n83012 anatolian hieroglyph a062\n83013 anatolian hieroglyph a063\n83014 anatolian hieroglyph a064\n83015 anatolian hieroglyph a065\n83016 anatolian hieroglyph a066\n83017 anatolian hieroglyph a066a\n83018 anatolian hieroglyph a066b\n83019 anatolian hieroglyph a066c\n83020 anatolian hieroglyph a067\n83021 anatolian hieroglyph a068\n83022 anatolian hieroglyph a069\n83023 anatolian hieroglyph a070\n83024 anatolian hieroglyph a071\n83025 anatolian hieroglyph a072\n83026 anatolian hieroglyph a073\n83027 anatolian hieroglyph a074\n83028 anatolian hieroglyph a075\n83029 anatolian hieroglyph a076\n83030 anatolian hieroglyph a077\n83031 anatolian hieroglyph a078\n83032 anatolian hieroglyph a079\n83033 anatolian hieroglyph a080\n83034 anatolian hieroglyph a081\n83035 anatolian hieroglyph a082\n83036 anatolian hieroglyph a083\n83037 anatolian hieroglyph a084\n83038 anatolian hieroglyph a085\n83039 anatolian hieroglyph a086\n83040 anatolian hieroglyph a087\n83041 anatolian hieroglyph a088\n83042 anatolian hieroglyph a089\n83043 anatolian hieroglyph a090\n83044 anatolian hieroglyph a091\n83045 anatolian hieroglyph a092\n83046 anatolian hieroglyph a093\n83047 anatolian hieroglyph a094\n83048 anatolian hieroglyph a095\n83049 anatolian hieroglyph a096\n83050 anatolian hieroglyph a097\n83051 anatolian hieroglyph a097a\n83052 anatolian hieroglyph a098\n83053 anatolian hieroglyph a098a\n83054 anatolian hieroglyph a099\n83055 anatolian hieroglyph a100\n83056 anatolian hieroglyph a100a\n83057 anatolian hieroglyph a101\n83058 anatolian hieroglyph a101a\n83059 anatolian hieroglyph a102\n83060 anatolian hieroglyph a102a\n83061 anatolian hieroglyph a103\n83062 anatolian hieroglyph a104\n83063 anatolian hieroglyph a104a\n83064 anatolian hieroglyph a104b\n83065 anatolian hieroglyph a104c\n83066 anatolian hieroglyph a105\n83067 anatolian hieroglyph a105a\n83068 anatolian hieroglyph a105b\n83069 anatolian hieroglyph a106\n83070 anatolian hieroglyph a107\n83071 anatolian hieroglyph a107a\n83072 anatolian hieroglyph a107b\n83073 anatolian hieroglyph a107c\n83074 anatolian hieroglyph a108\n83075 anatolian hieroglyph a109\n83076 anatolian hieroglyph a110\n83077 anatolian hieroglyph a110a\n83078 anatolian hieroglyph a110b\n83079 anatolian hieroglyph a111\n83080 anatolian hieroglyph a112\n83081 anatolian hieroglyph a113\n83082 anatolian hieroglyph a114\n83083 anatolian hieroglyph a115\n83084 anatolian hieroglyph a115a\n83085 anatolian hieroglyph a116\n83086 anatolian hieroglyph a117\n83087 anatolian hieroglyph a118\n83088 anatolian hieroglyph a119\n83089 anatolian hieroglyph a120\n83090 anatolian hieroglyph a121\n83091 anatolian hieroglyph a122\n83092 anatolian hieroglyph a123\n83093 anatolian hieroglyph a124\n83094 anatolian hieroglyph a125\n83095 anatolian hieroglyph a125a\n83096 anatolian hieroglyph a126\n83097 anatolian hieroglyph a127\n83098 anatolian hieroglyph a128\n83099 anatolian hieroglyph a129\n83100 anatolian hieroglyph a130\n83101 anatolian hieroglyph a131\n83102 anatolian hieroglyph a132\n83103 anatolian hieroglyph a133\n83104 anatolian hieroglyph a134\n83105 anatolian hieroglyph a135\n83106 anatolian hieroglyph a135a\n83107 anatolian hieroglyph a136\n83108 anatolian hieroglyph a137\n83109 anatolian hieroglyph a138\n83110 anatolian hieroglyph a139\n83111 anatolian hieroglyph a140\n83112 anatolian hieroglyph a141\n83113 anatolian hieroglyph a142\n83114 anatolian hieroglyph a143\n83115 anatolian hieroglyph a144\n83116 anatolian hieroglyph a145\n83117 anatolian hieroglyph a146\n83118 anatolian hieroglyph a147\n83119 anatolian hieroglyph a148\n83120 anatolian hieroglyph a149\n83121 anatolian hieroglyph a150\n83122 anatolian hieroglyph a151\n83123 anatolian hieroglyph a152\n83124 anatolian hieroglyph a153\n83125 anatolian hieroglyph a154\n83126 anatolian hieroglyph a155\n83127 anatolian hieroglyph a156\n83128 anatolian hieroglyph a157\n83129 anatolian hieroglyph a158\n83130 anatolian hieroglyph a159\n83131 anatolian hieroglyph a160\n83132 anatolian hieroglyph a161\n83133 anatolian hieroglyph a162\n83134 anatolian hieroglyph a163\n83135 anatolian hieroglyph a164\n83136 anatolian hieroglyph a165\n83137 anatolian hieroglyph a166\n83138 anatolian hieroglyph a167\n83139 anatolian hieroglyph a168\n83140 anatolian hieroglyph a169\n83141 anatolian hieroglyph a170\n83142 anatolian hieroglyph a171\n83143 anatolian hieroglyph a172\n83144 anatolian hieroglyph a173\n83145 anatolian hieroglyph a174\n83146 anatolian hieroglyph a175\n83147 anatolian hieroglyph a176\n83148 anatolian hieroglyph a177\n83149 anatolian hieroglyph a178\n83150 anatolian hieroglyph a179\n83151 anatolian hieroglyph a180\n83152 anatolian hieroglyph a181\n83153 anatolian hieroglyph a182\n83154 anatolian hieroglyph a183\n83155 anatolian hieroglyph a184\n83156 anatolian hieroglyph a185\n83157 anatolian hieroglyph a186\n83158 anatolian hieroglyph a187\n83159 anatolian hieroglyph a188\n83160 anatolian hieroglyph a189\n83161 anatolian hieroglyph a190\n83162 anatolian hieroglyph a191\n83163 anatolian hieroglyph a192\n83164 anatolian hieroglyph a193\n83165 anatolian hieroglyph a194\n83166 anatolian hieroglyph a195\n83167 anatolian hieroglyph a196\n83168 anatolian hieroglyph a197\n83169 anatolian hieroglyph a198\n83170 anatolian hieroglyph a199\n83171 anatolian hieroglyph a200\n83172 anatolian hieroglyph a201\n83173 anatolian hieroglyph a202\n83174 anatolian hieroglyph a202a\n83175 anatolian hieroglyph a202b\n83176 anatolian hieroglyph a203\n83177 anatolian hieroglyph a204\n83178 anatolian hieroglyph a205\n83179 anatolian hieroglyph a206\n83180 anatolian hieroglyph a207\n83181 anatolian hieroglyph a207a\n83182 anatolian hieroglyph a208\n83183 anatolian hieroglyph a209\n83184 anatolian hieroglyph a209a\n83185 anatolian hieroglyph a210\n83186 anatolian hieroglyph a211\n83187 anatolian hieroglyph a212\n83188 anatolian hieroglyph a213\n83189 anatolian hieroglyph a214\n83190 anatolian hieroglyph a215\n83191 anatolian hieroglyph a215a\n83192 anatolian hieroglyph a216\n83193 anatolian hieroglyph a216a\n83194 anatolian hieroglyph a217\n83195 anatolian hieroglyph a218\n83196 anatolian hieroglyph a219\n83197 anatolian hieroglyph a220\n83198 anatolian hieroglyph a221\n83199 anatolian hieroglyph a222\n83200 anatolian hieroglyph a223\n83201 anatolian hieroglyph a224\n83202 anatolian hieroglyph a225\n83203 anatolian hieroglyph a226\n83204 anatolian hieroglyph a227\n83205 anatolian hieroglyph a227a\n83206 anatolian hieroglyph a228\n83207 anatolian hieroglyph a229\n83208 anatolian hieroglyph a230\n83209 anatolian hieroglyph a231\n83210 anatolian hieroglyph a232\n83211 anatolian hieroglyph a233\n83212 anatolian hieroglyph a234\n83213 anatolian hieroglyph a235\n83214 anatolian hieroglyph a236\n83215 anatolian hieroglyph a237\n83216 anatolian hieroglyph a238\n83217 anatolian hieroglyph a239\n83218 anatolian hieroglyph a240\n83219 anatolian hieroglyph a241\n83220 anatolian hieroglyph a242\n83221 anatolian hieroglyph a243\n83222 anatolian hieroglyph a244\n83223 anatolian hieroglyph a245\n83224 anatolian hieroglyph a246\n83225 anatolian hieroglyph a247\n83226 anatolian hieroglyph a248\n83227 anatolian hieroglyph a249\n83228 anatolian hieroglyph a250\n83229 anatolian hieroglyph a251\n83230 anatolian hieroglyph a252\n83231 anatolian hieroglyph a253\n83232 anatolian hieroglyph a254\n83233 anatolian hieroglyph a255\n83234 anatolian hieroglyph a256\n83235 anatolian hieroglyph a257\n83236 anatolian hieroglyph a258\n83237 anatolian hieroglyph a259\n83238 anatolian hieroglyph a260\n83239 anatolian hieroglyph a261\n83240 anatolian hieroglyph a262\n83241 anatolian hieroglyph a263\n83242 anatolian hieroglyph a264\n83243 anatolian hieroglyph a265\n83244 anatolian hieroglyph a266\n83245 anatolian hieroglyph a267\n83246 anatolian hieroglyph a267a\n83247 anatolian hieroglyph a268\n83248 anatolian hieroglyph a269\n83249 anatolian hieroglyph a270\n83250 anatolian hieroglyph a271\n83251 anatolian hieroglyph a272\n83252 anatolian hieroglyph a273\n83253 anatolian hieroglyph a274\n83254 anatolian hieroglyph a275\n83255 anatolian hieroglyph a276\n83256 anatolian hieroglyph a277\n83257 anatolian hieroglyph a278\n83258 anatolian hieroglyph a279\n83259 anatolian hieroglyph a280\n83260 anatolian hieroglyph a281\n83261 anatolian hieroglyph a282\n83262 anatolian hieroglyph a283\n83263 anatolian hieroglyph a284\n83264 anatolian hieroglyph a285\n83265 anatolian hieroglyph a286\n83266 anatolian hieroglyph a287\n83267 anatolian hieroglyph a288\n83268 anatolian hieroglyph a289\n83269 anatolian hieroglyph a289a\n83270 anatolian hieroglyph a290\n83271 anatolian hieroglyph a291\n83272 anatolian hieroglyph a292\n83273 anatolian hieroglyph a293\n83274 anatolian hieroglyph a294\n83275 anatolian hieroglyph a294a\n83276 anatolian hieroglyph a295\n83277 anatolian hieroglyph a296\n83278 anatolian hieroglyph a297\n83279 anatolian hieroglyph a298\n83280 anatolian hieroglyph a299\n83281 anatolian hieroglyph a299a\n83282 anatolian hieroglyph a300\n83283 anatolian hieroglyph a301\n83284 anatolian hieroglyph a302\n83285 anatolian hieroglyph a303\n83286 anatolian hieroglyph a304\n83287 anatolian hieroglyph a305\n83288 anatolian hieroglyph a306\n83289 anatolian hieroglyph a307\n83290 anatolian hieroglyph a308\n83291 anatolian hieroglyph a309\n83292 anatolian hieroglyph a309a\n83293 anatolian hieroglyph a310\n83294 anatolian hieroglyph a311\n83295 anatolian hieroglyph a312\n83296 anatolian hieroglyph a313\n83297 anatolian hieroglyph a314\n83298 anatolian hieroglyph a315\n83299 anatolian hieroglyph a316\n83300 anatolian hieroglyph a317\n83301 anatolian hieroglyph a318\n83302 anatolian hieroglyph a319\n83303 anatolian hieroglyph a320\n83304 anatolian hieroglyph a321\n83305 anatolian hieroglyph a322\n83306 anatolian hieroglyph a323\n83307 anatolian hieroglyph a324\n83308 anatolian hieroglyph a325\n83309 anatolian hieroglyph a326\n83310 anatolian hieroglyph a327\n83311 anatolian hieroglyph a328\n83312 anatolian hieroglyph a329\n83313 anatolian hieroglyph a329a\n83314 anatolian hieroglyph a330\n83315 anatolian hieroglyph a331\n83316 anatolian hieroglyph a332a\n83317 anatolian hieroglyph a332b\n83318 anatolian hieroglyph a332c\n83319 anatolian hieroglyph a333\n83320 anatolian hieroglyph a334\n83321 anatolian hieroglyph a335\n83322 anatolian hieroglyph a336\n83323 anatolian hieroglyph a336a\n83324 anatolian hieroglyph a336b\n83325 anatolian hieroglyph a336c\n83326 anatolian hieroglyph a337\n83327 anatolian hieroglyph a338\n83328 anatolian hieroglyph a339\n83329 anatolian hieroglyph a340\n83330 anatolian hieroglyph a341\n83331 anatolian hieroglyph a342\n83332 anatolian hieroglyph a343\n83333 anatolian hieroglyph a344\n83334 anatolian hieroglyph a345\n83335 anatolian hieroglyph a346\n83336 anatolian hieroglyph a347\n83337 anatolian hieroglyph a348\n83338 anatolian hieroglyph a349\n83339 anatolian hieroglyph a350\n83340 anatolian hieroglyph a351\n83341 anatolian hieroglyph a352\n83342 anatolian hieroglyph a353\n83343 anatolian hieroglyph a354\n83344 anatolian hieroglyph a355\n83345 anatolian hieroglyph a356\n83346 anatolian hieroglyph a357\n83347 anatolian hieroglyph a358\n83348 anatolian hieroglyph a359\n83349 anatolian hieroglyph a359a\n83350 anatolian hieroglyph a360\n83351 anatolian hieroglyph a361\n83352 anatolian hieroglyph a362\n83353 anatolian hieroglyph a363\n83354 anatolian hieroglyph a364\n83355 anatolian hieroglyph a364a\n83356 anatolian hieroglyph a365\n83357 anatolian hieroglyph a366\n83358 anatolian hieroglyph a367\n83359 anatolian hieroglyph a368\n83360 anatolian hieroglyph a368a\n83361 anatolian hieroglyph a369\n83362 anatolian hieroglyph a370\n83363 anatolian hieroglyph a371\n83364 anatolian hieroglyph a371a\n83365 anatolian hieroglyph a372\n83366 anatolian hieroglyph a373\n83367 anatolian hieroglyph a374\n83368 anatolian hieroglyph a375\n83369 anatolian hieroglyph a376\n83370 anatolian hieroglyph a377\n83371 anatolian hieroglyph a378\n83372 anatolian hieroglyph a379\n83373 anatolian hieroglyph a380\n83374 anatolian hieroglyph a381\n83375 anatolian hieroglyph a381a\n83376 anatolian hieroglyph a382\n83377 anatolian hieroglyph a383 ra or ri\n83378 anatolian hieroglyph a383a\n83379 anatolian hieroglyph a384\n83380 anatolian hieroglyph a385\n83381 anatolian hieroglyph a386\n83382 anatolian hieroglyph a386a\n83383 anatolian hieroglyph a387\n83384 anatolian hieroglyph a388\n83385 anatolian hieroglyph a389\n83386 anatolian hieroglyph a390\n83387 anatolian hieroglyph a391\n83388 anatolian hieroglyph a392\n83389 anatolian hieroglyph a393 eight\n83390 anatolian hieroglyph a394\n83391 anatolian hieroglyph a395\n83392 anatolian hieroglyph a396\n83393 anatolian hieroglyph a397\n83394 anatolian hieroglyph a398\n83395 anatolian hieroglyph a399\n83396 anatolian hieroglyph a400\n83397 anatolian hieroglyph a401\n83398 anatolian hieroglyph a402\n83399 anatolian hieroglyph a403\n83400 anatolian hieroglyph a404\n83401 anatolian hieroglyph a405\n83402 anatolian hieroglyph a406\n83403 anatolian hieroglyph a407\n83404 anatolian hieroglyph a408\n83405 anatolian hieroglyph a409\n83406 anatolian hieroglyph a410 begin logogram mark\n83407 anatolian hieroglyph a410a end logogram mark\n83408 anatolian hieroglyph a411\n83409 anatolian hieroglyph a412\n83410 anatolian hieroglyph a413\n83411 anatolian hieroglyph a414\n83412 anatolian hieroglyph a415\n83413 anatolian hieroglyph a416\n83414 anatolian hieroglyph a417\n83415 anatolian hieroglyph a418\n83416 anatolian hieroglyph a419\n83417 anatolian hieroglyph a420\n83418 anatolian hieroglyph a421\n83419 anatolian hieroglyph a422\n83420 anatolian hieroglyph a423\n83421 anatolian hieroglyph a424\n83422 anatolian hieroglyph a425\n83423 anatolian hieroglyph a426\n83424 anatolian hieroglyph a427\n83425 anatolian hieroglyph a428\n83426 anatolian hieroglyph a429\n83427 anatolian hieroglyph a430\n83428 anatolian hieroglyph a431\n83429 anatolian hieroglyph a432\n83430 anatolian hieroglyph a433\n83431 anatolian hieroglyph a434\n83432 anatolian hieroglyph a435\n83433 anatolian hieroglyph a436\n83434 anatolian hieroglyph a437\n83435 anatolian hieroglyph a438\n83436 anatolian hieroglyph a439\n83437 anatolian hieroglyph a440\n83438 anatolian hieroglyph a441\n83439 anatolian hieroglyph a442\n83440 anatolian hieroglyph a443\n83441 anatolian hieroglyph a444\n83442 anatolian hieroglyph a445\n83443 anatolian hieroglyph a446\n83444 anatolian hieroglyph a447\n83445 anatolian hieroglyph a448\n83446 anatolian hieroglyph a449\n83447 anatolian hieroglyph a450\n83448 anatolian hieroglyph a450a\n83449 anatolian hieroglyph a451\n83450 anatolian hieroglyph a452\n83451 anatolian hieroglyph a453\n83452 anatolian hieroglyph a454\n83453 anatolian hieroglyph a455\n83454 anatolian hieroglyph a456\n83455 anatolian hieroglyph a457\n83456 anatolian hieroglyph a457a\n83457 anatolian hieroglyph a458\n83458 anatolian hieroglyph a459\n83459 anatolian hieroglyph a460\n83460 anatolian hieroglyph a461\n83461 anatolian hieroglyph a462\n83462 anatolian hieroglyph a463\n83463 anatolian hieroglyph a464\n83464 anatolian hieroglyph a465\n83465 anatolian hieroglyph a466\n83466 anatolian hieroglyph a467\n83467 anatolian hieroglyph a468\n83468 anatolian hieroglyph a469\n83469 anatolian hieroglyph a470\n83470 anatolian hieroglyph a471\n83471 anatolian hieroglyph a472\n83472 anatolian hieroglyph a473\n83473 anatolian hieroglyph a474\n83474 anatolian hieroglyph a475\n83475 anatolian hieroglyph a476\n83476 anatolian hieroglyph a477\n83477 anatolian hieroglyph a478\n83478 anatolian hieroglyph a479\n83479 anatolian hieroglyph a480\n83480 anatolian hieroglyph a481\n83481 anatolian hieroglyph a482\n83482 anatolian hieroglyph a483\n83483 anatolian hieroglyph a484\n83484 anatolian hieroglyph a485\n83485 anatolian hieroglyph a486\n83486 anatolian hieroglyph a487\n83487 anatolian hieroglyph a488\n83488 anatolian hieroglyph a489\n83489 anatolian hieroglyph a490\n83490 anatolian hieroglyph a491\n83491 anatolian hieroglyph a492\n83492 anatolian hieroglyph a493\n83493 anatolian hieroglyph a494\n83494 anatolian hieroglyph a495\n83495 anatolian hieroglyph a496\n83496 anatolian hieroglyph a497\n83497 anatolian hieroglyph a501\n83498 anatolian hieroglyph a502\n83499 anatolian hieroglyph a503\n83500 anatolian hieroglyph a504\n83501 anatolian hieroglyph a505\n83502 anatolian hieroglyph a506\n83503 anatolian hieroglyph a507\n83504 anatolian hieroglyph a508\n83505 anatolian hieroglyph a509\n83506 anatolian hieroglyph a510\n83507 anatolian hieroglyph a511\n83508 anatolian hieroglyph a512\n83509 anatolian hieroglyph a513\n83510 anatolian hieroglyph a514\n83511 anatolian hieroglyph a515\n83512 anatolian hieroglyph a516\n83513 anatolian hieroglyph a517\n83514 anatolian hieroglyph a518\n83515 anatolian hieroglyph a519\n83516 anatolian hieroglyph a520\n83517 anatolian hieroglyph a521\n83518 anatolian hieroglyph a522\n83519 anatolian hieroglyph a523\n83520 anatolian hieroglyph a524\n83521 anatolian hieroglyph a525\n83522 anatolian hieroglyph a526\n83523 anatolian hieroglyph a527\n83524 anatolian hieroglyph a528\n83525 anatolian hieroglyph a529\n83526 anatolian hieroglyph a530\n90368 gurung khema letter a\n90369 gurung khema letter ka\n90370 gurung khema letter kha\n90371 gurung khema letter ga\n90372 gurung khema letter gha\n90373 gurung khema letter nga\n90374 gurung khema letter ca\n90375 gurung khema letter cha\n90376 gurung khema letter ja\n90377 gurung khema letter jha\n90378 gurung khema letter ha\n90379 gurung khema letter tta\n90380 gurung khema letter ttha\n90381 gurung khema letter dda\n90382 gurung khema letter ddha\n90383 gurung khema letter va\n90384 gurung khema letter ta\n90385 gurung khema letter tha\n90386 gurung khema letter da\n90387 gurung khema letter dha\n90388 gurung khema letter na\n90389 gurung khema letter pa\n90390 gurung khema letter pha\n90391 gurung khema letter ba\n90392 gurung khema letter bha\n90393 gurung khema letter ma\n90394 gurung khema letter ya\n90395 gurung khema letter ra\n90396 gurung khema letter la\n90397 gurung khema letter sa\n90398 gurung khema vowel sign aa\n90399 gurung khema vowel sign i\n90400 gurung khema vowel sign ii\n90401 gurung khema vowel sign u\n90402 gurung khema vowel sign uu\n90403 gurung khema vowel sign e\n90404 gurung khema vowel sign ee\n90405 gurung khema vowel sign ai\n90406 gurung khema vowel sign o\n90407 gurung khema vowel sign oo\n90408 gurung khema vowel sign au\n90409 gurung khema vowel length mark\n90410 gurung khema consonant sign medial ya\n90411 gurung khema consonant sign medial va\n90412 gurung khema consonant sign medial ha\n90413 gurung khema sign anusvara\n90414 gurung khema consonant sign medial ra\n90415 gurung khema sign tholhoma\n90416 gurung khema digit zero\n90417 gurung khema digit one\n90418 gurung khema digit two\n90419 gurung khema digit three\n90420 gurung khema digit four\n90421 gurung khema digit five\n90422 gurung khema digit six\n90423 gurung khema digit seven\n90424 gurung khema digit eight\n90425 gurung khema digit nine\n92160 bamum letter phase-a ngkue mfon\n92161 bamum letter phase-a gbiee fon\n92162 bamum letter phase-a pon mfon pipaemgbiee\n92163 bamum letter phase-a pon mfon pipaemba\n92164 bamum letter phase-a naa mfon\n92165 bamum letter phase-a shuenshuet\n92166 bamum letter phase-a tita mfon\n92167 bamum letter phase-a nza mfon\n92168 bamum letter phase-a shinda pa nji\n92169 bamum letter phase-a pon pa nji pipaemgbiee\n92170 bamum letter phase-a pon pa nji pipaemba\n92171 bamum letter phase-a maembgbiee\n92172 bamum letter phase-a tu maemba\n92173 bamum letter phase-a ngangu\n92174 bamum letter phase-a maemveux\n92175 bamum letter phase-a mansuae\n92176 bamum letter phase-a mveuaengam\n92177 bamum letter phase-a seunyam\n92178 bamum letter phase-a ntoqpen\n92179 bamum letter phase-a keukeutnda\n92180 bamum letter phase-a nkindi\n92181 bamum letter phase-a suu\n92182 bamum letter phase-a ngkuenzeum\n92183 bamum letter phase-a lapaq\n92184 bamum letter phase-a let kut\n92185 bamum letter phase-a ntap mfaa\n92186 bamum letter phase-a maekeup\n92187 bamum letter phase-a pashae\n92188 bamum letter phase-a gheuaerae\n92189 bamum letter phase-a pamshae\n92190 bamum letter phase-a mon nggeuaet\n92191 bamum letter phase-a nzun meut\n92192 bamum letter phase-a u yuq nae\n92193 bamum letter phase-a gheuaegheuae\n92194 bamum letter phase-a ntap ntaa\n92195 bamum letter phase-a sisa\n92196 bamum letter phase-a mgbasa\n92197 bamum letter phase-a meunjomndeuq\n92198 bamum letter phase-a moompuq\n92199 bamum letter phase-a kafa\n92200 bamum letter phase-a pa leeraewa\n92201 bamum letter phase-a nda leeraewa\n92202 bamum letter phase-a pet\n92203 bamum letter phase-a maemkpen\n92204 bamum letter phase-a nika\n92205 bamum letter phase-a pup\n92206 bamum letter phase-a tuaep\n92207 bamum letter phase-a luaep\n92208 bamum letter phase-a sonjam\n92209 bamum letter phase-a teuteuwen\n92210 bamum letter phase-a maenyi\n92211 bamum letter phase-a ket\n92212 bamum letter phase-a ndaanggeuaet\n92213 bamum letter phase-a kuoq\n92214 bamum letter phase-a moomeut\n92215 bamum letter phase-a shum\n92216 bamum letter phase-a lommae\n92217 bamum letter phase-a firi\n92218 bamum letter phase-a rom\n92219 bamum letter phase-a kpoq\n92220 bamum letter phase-a soq\n92221 bamum letter phase-a map pieet\n92222 bamum letter phase-a shirae\n92223 bamum letter phase-a ntap\n92224 bamum letter phase-a shoq nshut yum\n92225 bamum letter phase-a nyit mongkeuaeq\n92226 bamum letter phase-a paarae\n92227 bamum letter phase-a nkaarae\n92228 bamum letter phase-a unknown\n92229 bamum letter phase-a nggen\n92230 bamum letter phase-a maesi\n92231 bamum letter phase-a njam\n92232 bamum letter phase-a mbanyi\n92233 bamum letter phase-a nyet\n92234 bamum letter phase-a teuaen\n92235 bamum letter phase-a sot\n92236 bamum letter phase-a paam\n92237 bamum letter phase-a nshiee\n92238 bamum letter phase-a maem\n92239 bamum letter phase-a nyi\n92240 bamum letter phase-a kaq\n92241 bamum letter phase-a nsha\n92242 bamum letter phase-a vee\n92243 bamum letter phase-a lu\n92244 bamum letter phase-a nen\n92245 bamum letter phase-a naq\n92246 bamum letter phase-a mbaq\n92247 bamum letter phase-b nshuet\n92248 bamum letter phase-b tu maemgbiee\n92249 bamum letter phase-b siee\n92250 bamum letter phase-b set tu\n92251 bamum letter phase-b lom nteum\n92252 bamum letter phase-b mba maelee\n92253 bamum letter phase-b kieem\n92254 bamum letter phase-b yeurae\n92255 bamum letter phase-b mbaarae\n92256 bamum letter phase-b kam\n92257 bamum letter phase-b peeshi\n92258 bamum letter phase-b yafu leeraewa\n92259 bamum letter phase-b lam nshut nyam\n92260 bamum letter phase-b ntiee sheuoq\n92261 bamum letter phase-b ndu njaa\n92262 bamum letter phase-b gheugheuaem\n92263 bamum letter phase-b pit\n92264 bamum letter phase-b tu nsiee\n92265 bamum letter phase-b shet njaq\n92266 bamum letter phase-b sheuaeqtu\n92267 bamum letter phase-b mfon teuaeq\n92268 bamum letter phase-b mbit mbaaket\n92269 bamum letter phase-b nyi nteum\n92270 bamum letter phase-b keupuq\n92271 bamum letter phase-b gheughen\n92272 bamum letter phase-b keuyeux\n92273 bamum letter phase-b laanae\n92274 bamum letter phase-b parum\n92275 bamum letter phase-b veum\n92276 bamum letter phase-b ngkindi mvop\n92277 bamum letter phase-b nggeu mbu\n92278 bamum letter phase-b wuaet\n92279 bamum letter phase-b sakeuae\n92280 bamum letter phase-b taam\n92281 bamum letter phase-b meuq\n92282 bamum letter phase-b ngguoq\n92283 bamum letter phase-b ngguoq large\n92284 bamum letter phase-b mfiyaq\n92285 bamum letter phase-b sue\n92286 bamum letter phase-b mbeuri\n92287 bamum letter phase-b montieen\n92288 bamum letter phase-b nyaemae\n92289 bamum letter phase-b pungaam\n92290 bamum letter phase-b meut nggeet\n92291 bamum letter phase-b feux\n92292 bamum letter phase-b mbuoq\n92293 bamum letter phase-b fee\n92294 bamum letter phase-b keuaem\n92295 bamum letter phase-b ma njeuaena\n92296 bamum letter phase-b ma njuqa\n92297 bamum letter phase-b let\n92298 bamum letter phase-b nggaam\n92299 bamum letter phase-b nsen\n92300 bamum letter phase-b ma\n92301 bamum letter phase-b kiq\n92302 bamum letter phase-b ngom\n92303 bamum letter phase-c ngkue maemba\n92304 bamum letter phase-c nza\n92305 bamum letter phase-c yum\n92306 bamum letter phase-c wangkuoq\n92307 bamum letter phase-c nggen\n92308 bamum letter phase-c ndeuaeree\n92309 bamum letter phase-c ngkaq\n92310 bamum letter phase-c gharae\n92311 bamum letter phase-c mbeekeet\n92312 bamum letter phase-c gbayi\n92313 bamum letter phase-c nyir mkparaq meun\n92314 bamum letter phase-c ntu mbit\n92315 bamum letter phase-c mbeum\n92316 bamum letter phase-c pirieen\n92317 bamum letter phase-c ndombu\n92318 bamum letter phase-c mbaa cabbage-tree\n92319 bamum letter phase-c keusheuaep\n92320 bamum letter phase-c ghap\n92321 bamum letter phase-c keukaq\n92322 bamum letter phase-c yu muomae\n92323 bamum letter phase-c nzeum\n92324 bamum letter phase-c mbue\n92325 bamum letter phase-c nseuaen\n92326 bamum letter phase-c mbit\n92327 bamum letter phase-c yeuq\n92328 bamum letter phase-c kparaq\n92329 bamum letter phase-c kaa\n92330 bamum letter phase-c seux\n92331 bamum letter phase-c ndida\n92332 bamum letter phase-c taashae\n92333 bamum letter phase-c njueq\n92334 bamum letter phase-c tita yue\n92335 bamum letter phase-c suaet\n92336 bamum letter phase-c ngguaen nyam\n92337 bamum letter phase-c veux\n92338 bamum letter phase-c nansanaq\n92339 bamum letter phase-c ma keuaeri\n92340 bamum letter phase-c ntaa\n92341 bamum letter phase-c ngguon\n92342 bamum letter phase-c lap\n92343 bamum letter phase-c mbirieen\n92344 bamum letter phase-c mgbasaq\n92345 bamum letter phase-c nteungba\n92346 bamum letter phase-c teuteux\n92347 bamum letter phase-c nggum\n92348 bamum letter phase-c fue\n92349 bamum letter phase-c ndeut\n92350 bamum letter phase-c nsa\n92351 bamum letter phase-c nshaq\n92352 bamum letter phase-c bung\n92353 bamum letter phase-c veuaepen\n92354 bamum letter phase-c mberae\n92355 bamum letter phase-c ru\n92356 bamum letter phase-c njaem\n92357 bamum letter phase-c lam\n92358 bamum letter phase-c tituaep\n92359 bamum letter phase-c nsuot ngom\n92360 bamum letter phase-c njeeee\n92361 bamum letter phase-c ket\n92362 bamum letter phase-c nggu\n92363 bamum letter phase-c maesi\n92364 bamum letter phase-c mbuaem\n92365 bamum letter phase-c lu\n92366 bamum letter phase-c kut\n92367 bamum letter phase-c njam\n92368 bamum letter phase-c ngom\n92369 bamum letter phase-c wup\n92370 bamum letter phase-c nggueet\n92371 bamum letter phase-c nsom\n92372 bamum letter phase-c nten\n92373 bamum letter phase-c kuop nkaarae\n92374 bamum letter phase-c nsun\n92375 bamum letter phase-c ndam\n92376 bamum letter phase-c ma nsiee\n92377 bamum letter phase-c yaa\n92378 bamum letter phase-c ndap\n92379 bamum letter phase-c shueq\n92380 bamum letter phase-c setfon\n92381 bamum letter phase-c mbi\n92382 bamum letter phase-c maemba\n92383 bamum letter phase-c mbanyi\n92384 bamum letter phase-c keuseux\n92385 bamum letter phase-c mbeux\n92386 bamum letter phase-c keum\n92387 bamum letter phase-c mbaa picket\n92388 bamum letter phase-c yuwoq\n92389 bamum letter phase-c njeux\n92390 bamum letter phase-c miee\n92391 bamum letter phase-c muae\n92392 bamum letter phase-c shiq\n92393 bamum letter phase-c ken law\n92394 bamum letter phase-c ken fatigue\n92395 bamum letter phase-c ngaq\n92396 bamum letter phase-c naq\n92397 bamum letter phase-c liq\n92398 bamum letter phase-c pin\n92399 bamum letter phase-c pen\n92400 bamum letter phase-c tet\n92401 bamum letter phase-d mbuo\n92402 bamum letter phase-d wap\n92403 bamum letter phase-d nji\n92404 bamum letter phase-d mfon\n92405 bamum letter phase-d njiee\n92406 bamum letter phase-d liee\n92407 bamum letter phase-d njeut\n92408 bamum letter phase-d nshee\n92409 bamum letter phase-d nggaamae\n92410 bamum letter phase-d nyam\n92411 bamum letter phase-d wuaen\n92412 bamum letter phase-d ngkun\n92413 bamum letter phase-d shee\n92414 bamum letter phase-d ngkap\n92415 bamum letter phase-d keuaetmeun\n92416 bamum letter phase-d teut\n92417 bamum letter phase-d sheuae\n92418 bamum letter phase-d njap\n92419 bamum letter phase-d sue\n92420 bamum letter phase-d ket\n92421 bamum letter phase-d yaemmae\n92422 bamum letter phase-d kuom\n92423 bamum letter phase-d sap\n92424 bamum letter phase-d mfeut\n92425 bamum letter phase-d ndeux\n92426 bamum letter phase-d maleeri\n92427 bamum letter phase-d meut\n92428 bamum letter phase-d seuaeq\n92429 bamum letter phase-d yen\n92430 bamum letter phase-d njeuaem\n92431 bamum letter phase-d keuot mbuae\n92432 bamum letter phase-d ngkeuri\n92433 bamum letter phase-d tu\n92434 bamum letter phase-d ghaa\n92435 bamum letter phase-d ngkyee\n92436 bamum letter phase-d feufeuaet\n92437 bamum letter phase-d ndee\n92438 bamum letter phase-d mgbofum\n92439 bamum letter phase-d leuaep\n92440 bamum letter phase-d ndon\n92441 bamum letter phase-d moni\n92442 bamum letter phase-d mgbeun\n92443 bamum letter phase-d puut\n92444 bamum letter phase-d mgbiee\n92445 bamum letter phase-d mfo\n92446 bamum letter phase-d lum\n92447 bamum letter phase-d nsieep\n92448 bamum letter phase-d mbaa\n92449 bamum letter phase-d kwaet\n92450 bamum letter phase-d nyet\n92451 bamum letter phase-d teuaen\n92452 bamum letter phase-d sot\n92453 bamum letter phase-d yuwoq\n92454 bamum letter phase-d keum\n92455 bamum letter phase-d raem\n92456 bamum letter phase-d teeee\n92457 bamum letter phase-d ngkeuaeq\n92458 bamum letter phase-d mfeuae\n92459 bamum letter phase-d nsieet\n92460 bamum letter phase-d keup\n92461 bamum letter phase-d pip\n92462 bamum letter phase-d peutae\n92463 bamum letter phase-d nyue\n92464 bamum letter phase-d let\n92465 bamum letter phase-d nggaam\n92466 bamum letter phase-d mfiee\n92467 bamum letter phase-d nggwaen\n92468 bamum letter phase-d yuom\n92469 bamum letter phase-d pap\n92470 bamum letter phase-d yuop\n92471 bamum letter phase-d ndam\n92472 bamum letter phase-d nteum\n92473 bamum letter phase-d suae\n92474 bamum letter phase-d kun\n92475 bamum letter phase-d nggeux\n92476 bamum letter phase-d ngkiee\n92477 bamum letter phase-d tuot\n92478 bamum letter phase-d meun\n92479 bamum letter phase-d kuq\n92480 bamum letter phase-d nsum\n92481 bamum letter phase-d teun\n92482 bamum letter phase-d maenjet\n92483 bamum letter phase-d nggap\n92484 bamum letter phase-d leum\n92485 bamum letter phase-d ngguom\n92486 bamum letter phase-d nshut\n92487 bamum letter phase-d njueq\n92488 bamum letter phase-d gheuae\n92489 bamum letter phase-d ku\n92490 bamum letter phase-d ren old\n92491 bamum letter phase-d tae\n92492 bamum letter phase-d toq\n92493 bamum letter phase-d nyi\n92494 bamum letter phase-d rii\n92495 bamum letter phase-d leeee\n92496 bamum letter phase-d meeee\n92497 bamum letter phase-d m\n92498 bamum letter phase-d suu\n92499 bamum letter phase-d mu\n92500 bamum letter phase-d shii\n92501 bamum letter phase-d sheux\n92502 bamum letter phase-d kyee\n92503 bamum letter phase-d nu\n92504 bamum letter phase-d shu\n92505 bamum letter phase-d ntee\n92506 bamum letter phase-d pee\n92507 bamum letter phase-d ni\n92508 bamum letter phase-d shoq\n92509 bamum letter phase-d puq\n92510 bamum letter phase-d mvop\n92511 bamum letter phase-d loq\n92512 bamum letter phase-d ren much\n92513 bamum letter phase-d ti\n92514 bamum letter phase-d ntuu\n92515 bamum letter phase-d mbaa seven\n92516 bamum letter phase-d saq\n92517 bamum letter phase-d faa\n92518 bamum letter phase-e ndap\n92519 bamum letter phase-e toon\n92520 bamum letter phase-e mbeum\n92521 bamum letter phase-e lap\n92522 bamum letter phase-e vom\n92523 bamum letter phase-e loon\n92524 bamum letter phase-e paa\n92525 bamum letter phase-e som\n92526 bamum letter phase-e raq\n92527 bamum letter phase-e nshuop\n92528 bamum letter phase-e ndun\n92529 bamum letter phase-e puae\n92530 bamum letter phase-e tam\n92531 bamum letter phase-e ngka\n92532 bamum letter phase-e kpeux\n92533 bamum letter phase-e wuo\n92534 bamum letter phase-e see\n92535 bamum letter phase-e nggeuaet\n92536 bamum letter phase-e paam\n92537 bamum letter phase-e too\n92538 bamum letter phase-e kuop\n92539 bamum letter phase-e lom\n92540 bamum letter phase-e nshiee\n92541 bamum letter phase-e ngop\n92542 bamum letter phase-e maem\n92543 bamum letter phase-e ngkeux\n92544 bamum letter phase-e ngoq\n92545 bamum letter phase-e nshue\n92546 bamum letter phase-e rimgba\n92547 bamum letter phase-e njeux\n92548 bamum letter phase-e peem\n92549 bamum letter phase-e saa\n92550 bamum letter phase-e nggurae\n92551 bamum letter phase-e mgba\n92552 bamum letter phase-e gheux\n92553 bamum letter phase-e ngkeuaem\n92554 bamum letter phase-e njaemli\n92555 bamum letter phase-e map\n92556 bamum letter phase-e loot\n92557 bamum letter phase-e nggeeee\n92558 bamum letter phase-e ndiq\n92559 bamum letter phase-e taen nteum\n92560 bamum letter phase-e set\n92561 bamum letter phase-e pum\n92562 bamum letter phase-e ndaa softness\n92563 bamum letter phase-e ngguaeshae nyam\n92564 bamum letter phase-e yiee\n92565 bamum letter phase-e gheun\n92566 bamum letter phase-e tuae\n92567 bamum letter phase-e yeuae\n92568 bamum letter phase-e po\n92569 bamum letter phase-e tumae\n92570 bamum letter phase-e keuae\n92571 bamum letter phase-e suaen\n92572 bamum letter phase-e teuaeq\n92573 bamum letter phase-e veuae\n92574 bamum letter phase-e weux\n92575 bamum letter phase-e laam\n92576 bamum letter phase-e pu\n92577 bamum letter phase-e taaq\n92578 bamum letter phase-e ghaamae\n92579 bamum letter phase-e ngeureut\n92580 bamum letter phase-e sheuaeq\n92581 bamum letter phase-e mgben\n92582 bamum letter phase-e mbee\n92583 bamum letter phase-e nzaq\n92584 bamum letter phase-e nkom\n92585 bamum letter phase-e gbet\n92586 bamum letter phase-e tum\n92587 bamum letter phase-e kuet\n92588 bamum letter phase-e yap\n92589 bamum letter phase-e nyi cleaver\n92590 bamum letter phase-e yit\n92591 bamum letter phase-e mfeuq\n92592 bamum letter phase-e ndiaq\n92593 bamum letter phase-e pieeq\n92594 bamum letter phase-e yueq\n92595 bamum letter phase-e leuaem\n92596 bamum letter phase-e fue\n92597 bamum letter phase-e gbeux\n92598 bamum letter phase-e ngkup\n92599 bamum letter phase-e ket\n92600 bamum letter phase-e mae\n92601 bamum letter phase-e ngkaami\n92602 bamum letter phase-e ghet\n92603 bamum letter phase-e fa\n92604 bamum letter phase-e ntum\n92605 bamum letter phase-e peut\n92606 bamum letter phase-e yeum\n92607 bamum letter phase-e nggeuae\n92608 bamum letter phase-e nyi between\n92609 bamum letter phase-e nzuq\n92610 bamum letter phase-e poon\n92611 bamum letter phase-e miee\n92612 bamum letter phase-e fuet\n92613 bamum letter phase-e nae\n92614 bamum letter phase-e muae\n92615 bamum letter phase-e gheuae\n92616 bamum letter phase-e fu i\n92617 bamum letter phase-e mvi\n92618 bamum letter phase-e puaq\n92619 bamum letter phase-e ngkum\n92620 bamum letter phase-e kut\n92621 bamum letter phase-e piet\n92622 bamum letter phase-e ntap\n92623 bamum letter phase-e yeuaet\n92624 bamum letter phase-e nggup\n92625 bamum letter phase-e pa people\n92626 bamum letter phase-e fu call\n92627 bamum letter phase-e fom\n92628 bamum letter phase-e njee\n92629 bamum letter phase-e a\n92630 bamum letter phase-e toq\n92631 bamum letter phase-e o\n92632 bamum letter phase-e i\n92633 bamum letter phase-e laq\n92634 bamum letter phase-e pa plural\n92635 bamum letter phase-e taa\n92636 bamum letter phase-e taq\n92637 bamum letter phase-e ndaa my house\n92638 bamum letter phase-e shiq\n92639 bamum letter phase-e yeux\n92640 bamum letter phase-e nguae\n92641 bamum letter phase-e yuaen\n92642 bamum letter phase-e yoq swimming\n92643 bamum letter phase-e yoq cover\n92644 bamum letter phase-e yuq\n92645 bamum letter phase-e yun\n92646 bamum letter phase-e keux\n92647 bamum letter phase-e peux\n92648 bamum letter phase-e njee epoch\n92649 bamum letter phase-e pue\n92650 bamum letter phase-e wue\n92651 bamum letter phase-e fee\n92652 bamum letter phase-e vee\n92653 bamum letter phase-e lu\n92654 bamum letter phase-e mi\n92655 bamum letter phase-e reux\n92656 bamum letter phase-e rae\n92657 bamum letter phase-e nguaet\n92658 bamum letter phase-e nga\n92659 bamum letter phase-e sho\n92660 bamum letter phase-e shoq\n92661 bamum letter phase-e fu remedy\n92662 bamum letter phase-e na\n92663 bamum letter phase-e pi\n92664 bamum letter phase-e loq\n92665 bamum letter phase-e ko\n92666 bamum letter phase-e men\n92667 bamum letter phase-e ma\n92668 bamum letter phase-e maq\n92669 bamum letter phase-e teu\n92670 bamum letter phase-e ki\n92671 bamum letter phase-e mon\n92672 bamum letter phase-e ten\n92673 bamum letter phase-e faq\n92674 bamum letter phase-e ghom\n92675 bamum letter phase-f ka\n92676 bamum letter phase-f u\n92677 bamum letter phase-f ku\n92678 bamum letter phase-f ee\n92679 bamum letter phase-f ree\n92680 bamum letter phase-f tae\n92681 bamum letter phase-f nyi\n92682 bamum letter phase-f la\n92683 bamum letter phase-f rii\n92684 bamum letter phase-f riee\n92685 bamum letter phase-f meeee\n92686 bamum letter phase-f taa\n92687 bamum letter phase-f ndaa\n92688 bamum letter phase-f njaem\n92689 bamum letter phase-f m\n92690 bamum letter phase-f suu\n92691 bamum letter phase-f shii\n92692 bamum letter phase-f si\n92693 bamum letter phase-f seux\n92694 bamum letter phase-f kyee\n92695 bamum letter phase-f ket\n92696 bamum letter phase-f nuae\n92697 bamum letter phase-f nu\n92698 bamum letter phase-f njuae\n92699 bamum letter phase-f yoq\n92700 bamum letter phase-f shu\n92701 bamum letter phase-f ya\n92702 bamum letter phase-f nsha\n92703 bamum letter phase-f peux\n92704 bamum letter phase-f ntee\n92705 bamum letter phase-f wue\n92706 bamum letter phase-f pee\n92707 bamum letter phase-f ru\n92708 bamum letter phase-f ni\n92709 bamum letter phase-f reux\n92710 bamum letter phase-f ken\n92711 bamum letter phase-f ngkwaen\n92712 bamum letter phase-f ngga\n92713 bamum letter phase-f sho\n92714 bamum letter phase-f puae\n92715 bamum letter phase-f fom\n92716 bamum letter phase-f wa\n92717 bamum letter phase-f li\n92718 bamum letter phase-f loq\n92719 bamum letter phase-f ko\n92720 bamum letter phase-f mben\n92721 bamum letter phase-f ren\n92722 bamum letter phase-f ma\n92723 bamum letter phase-f mo\n92724 bamum letter phase-f mbaa\n92725 bamum letter phase-f tet\n92726 bamum letter phase-f kpa\n92727 bamum letter phase-f samba\n92728 bamum letter phase-f vueq\n92736 mro letter ta\n92737 mro letter ngi\n92738 mro letter yo\n92739 mro letter mim\n92740 mro letter ba\n92741 mro letter da\n92742 mro letter a\n92743 mro letter phi\n92744 mro letter khai\n92745 mro letter hao\n92746 mro letter dai\n92747 mro letter chu\n92748 mro letter keaae\n92749 mro letter ol\n92750 mro letter maem\n92751 mro letter nin\n92752 mro letter pa\n92753 mro letter oo\n92754 mro letter o\n92755 mro letter ro\n92756 mro letter shi\n92757 mro letter thea\n92758 mro letter ea\n92759 mro letter wa\n92760 mro letter e\n92761 mro letter ko\n92762 mro letter lan\n92763 mro letter la\n92764 mro letter hai\n92765 mro letter ri\n92766 mro letter tek\n92768 mro digit zero\n92769 mro digit one\n92770 mro digit two\n92771 mro digit three\n92772 mro digit four\n92773 mro digit five\n92774 mro digit six\n92775 mro digit seven\n92776 mro digit eight\n92777 mro digit nine\n92782 mro danda\n92783 mro double danda\n92784 tangsa letter oz\n92785 tangsa letter oc\n92786 tangsa letter oq\n92787 tangsa letter ox\n92788 tangsa letter az\n92789 tangsa letter ac\n92790 tangsa letter aq\n92791 tangsa letter ax\n92792 tangsa letter vz\n92793 tangsa letter vc\n92794 tangsa letter vq\n92795 tangsa letter vx\n92796 tangsa letter ez\n92797 tangsa letter ec\n92798 tangsa letter eq\n92799 tangsa letter ex\n92800 tangsa letter iz\n92801 tangsa letter ic\n92802 tangsa letter iq\n92803 tangsa letter ix\n92804 tangsa letter uz\n92805 tangsa letter uc\n92806 tangsa letter uq\n92807 tangsa letter ux\n92808 tangsa letter awz\n92809 tangsa letter awc\n92810 tangsa letter awq\n92811 tangsa letter awx\n92812 tangsa letter uiz\n92813 tangsa letter uic\n92814 tangsa letter uiq\n92815 tangsa letter uix\n92816 tangsa letter final ng\n92817 tangsa letter long uex\n92818 tangsa letter short uez\n92819 tangsa letter short awx\n92820 tangsa letter uec\n92821 tangsa letter uez\n92822 tangsa letter ueq\n92823 tangsa letter uex\n92824 tangsa letter uiuz\n92825 tangsa letter uiuc\n92826 tangsa letter uiuq\n92827 tangsa letter uiux\n92828 tangsa letter mz\n92829 tangsa letter mc\n92830 tangsa letter mq\n92831 tangsa letter mx\n92832 tangsa letter ka\n92833 tangsa letter kha\n92834 tangsa letter ga\n92835 tangsa letter nga\n92836 tangsa letter sa\n92837 tangsa letter ya\n92838 tangsa letter wa\n92839 tangsa letter pa\n92840 tangsa letter nya\n92841 tangsa letter pha\n92842 tangsa letter ba\n92843 tangsa letter ma\n92844 tangsa letter na\n92845 tangsa letter ha\n92846 tangsa letter la\n92847 tangsa letter hta\n92848 tangsa letter ta\n92849 tangsa letter da\n92850 tangsa letter ra\n92851 tangsa letter nha\n92852 tangsa letter sha\n92853 tangsa letter ca\n92854 tangsa letter tsa\n92855 tangsa letter gha\n92856 tangsa letter htta\n92857 tangsa letter tha\n92858 tangsa letter xa\n92859 tangsa letter fa\n92860 tangsa letter dha\n92861 tangsa letter cha\n92862 tangsa letter za\n92864 tangsa digit zero\n92865 tangsa digit one\n92866 tangsa digit two\n92867 tangsa digit three\n92868 tangsa digit four\n92869 tangsa digit five\n92870 tangsa digit six\n92871 tangsa digit seven\n92872 tangsa digit eight\n92873 tangsa digit nine\n92880 bassa vah letter enni\n92881 bassa vah letter ka\n92882 bassa vah letter se\n92883 bassa vah letter fa\n92884 bassa vah letter mbe\n92885 bassa vah letter yie\n92886 bassa vah letter gah\n92887 bassa vah letter dhii\n92888 bassa vah letter kpah\n92889 bassa vah letter jo\n92890 bassa vah letter hwah\n92891 bassa vah letter wa\n92892 bassa vah letter zo\n92893 bassa vah letter gbu\n92894 bassa vah letter do\n92895 bassa vah letter ce\n92896 bassa vah letter uwu\n92897 bassa vah letter to\n92898 bassa vah letter ba\n92899 bassa vah letter vu\n92900 bassa vah letter yein\n92901 bassa vah letter pa\n92902 bassa vah letter wadda\n92903 bassa vah letter a\n92904 bassa vah letter o\n92905 bassa vah letter oo\n92906 bassa vah letter u\n92907 bassa vah letter ee\n92908 bassa vah letter e\n92909 bassa vah letter i\n92912 bassa vah combining high tone\n92913 bassa vah combining low tone\n92914 bassa vah combining mid tone\n92915 bassa vah combining low-mid tone\n92916 bassa vah combining high-low tone\n92917 bassa vah full stop\n92928 pahawh hmong vowel keeb\n92929 pahawh hmong vowel keev\n92930 pahawh hmong vowel kib\n92931 pahawh hmong vowel kiv\n92932 pahawh hmong vowel kaub\n92933 pahawh hmong vowel kauv\n92934 pahawh hmong vowel kub\n92935 pahawh hmong vowel kuv\n92936 pahawh hmong vowel keb\n92937 pahawh hmong vowel kev\n92938 pahawh hmong vowel kaib\n92939 pahawh hmong vowel kaiv\n92940 pahawh hmong vowel koob\n92941 pahawh hmong vowel koov\n92942 pahawh hmong vowel kawb\n92943 pahawh hmong vowel kawv\n92944 pahawh hmong vowel kuab\n92945 pahawh hmong vowel kuav\n92946 pahawh hmong vowel kob\n92947 pahawh hmong vowel kov\n92948 pahawh hmong vowel kiab\n92949 pahawh hmong vowel kiav\n92950 pahawh hmong vowel kab\n92951 pahawh hmong vowel kav\n92952 pahawh hmong vowel kwb\n92953 pahawh hmong vowel kwv\n92954 pahawh hmong vowel kaab\n92955 pahawh hmong vowel kaav\n92956 pahawh hmong consonant vau\n92957 pahawh hmong consonant ntsau\n92958 pahawh hmong consonant lau\n92959 pahawh hmong consonant hau\n92960 pahawh hmong consonant nlau\n92961 pahawh hmong consonant rau\n92962 pahawh hmong consonant nkau\n92963 pahawh hmong consonant qhau\n92964 pahawh hmong consonant yau\n92965 pahawh hmong consonant hlau\n92966 pahawh hmong consonant mau\n92967 pahawh hmong consonant chau\n92968 pahawh hmong consonant nchau\n92969 pahawh hmong consonant hnau\n92970 pahawh hmong consonant plhau\n92971 pahawh hmong consonant nthau\n92972 pahawh hmong consonant nau\n92973 pahawh hmong consonant au\n92974 pahawh hmong consonant xau\n92975 pahawh hmong consonant cau\n92976 pahawh hmong mark cim tub\n92977 pahawh hmong mark cim so\n92978 pahawh hmong mark cim kes\n92979 pahawh hmong mark cim khav\n92980 pahawh hmong mark cim suam\n92981 pahawh hmong mark cim hom\n92982 pahawh hmong mark cim taum\n92983 pahawh hmong sign vos thom\n92984 pahawh hmong sign vos tshab ceeb\n92985 pahawh hmong sign cim cheem\n92986 pahawh hmong sign vos thiab\n92987 pahawh hmong sign vos feem\n92988 pahawh hmong sign xyeem ntxiv\n92989 pahawh hmong sign xyeem rho\n92990 pahawh hmong sign xyeem tov\n92991 pahawh hmong sign xyeem faib\n92992 pahawh hmong sign vos seev\n92993 pahawh hmong sign meej suab\n92994 pahawh hmong sign vos nrua\n92995 pahawh hmong sign ib yam\n92996 pahawh hmong sign xaus\n92997 pahawh hmong sign cim tsov rog\n93008 pahawh hmong digit zero\n93009 pahawh hmong digit one\n93010 pahawh hmong digit two\n93011 pahawh hmong digit three\n93012 pahawh hmong digit four\n93013 pahawh hmong digit five\n93014 pahawh hmong digit six\n93015 pahawh hmong digit seven\n93016 pahawh hmong digit eight\n93017 pahawh hmong digit nine\n93019 pahawh hmong number tens\n93020 pahawh hmong number hundreds\n93021 pahawh hmong number ten thousands\n93022 pahawh hmong number millions\n93023 pahawh hmong number hundred millions\n93024 pahawh hmong number ten billions\n93025 pahawh hmong number trillions\n93027 pahawh hmong sign vos lub\n93028 pahawh hmong sign xyoo\n93029 pahawh hmong sign hli\n93030 pahawh hmong sign third-stage hli\n93031 pahawh hmong sign zwj thaj\n93032 pahawh hmong sign hnub\n93033 pahawh hmong sign nqig\n93034 pahawh hmong sign xiab\n93035 pahawh hmong sign ntuj\n93036 pahawh hmong sign av\n93037 pahawh hmong sign txheej ceev\n93038 pahawh hmong sign meej tseeb\n93039 pahawh hmong sign tau\n93040 pahawh hmong sign los\n93041 pahawh hmong sign mus\n93042 pahawh hmong sign cim hais lus ntog ntog\n93043 pahawh hmong sign cim cuam tshooj\n93044 pahawh hmong sign cim txwv\n93045 pahawh hmong sign cim txwv chwv\n93046 pahawh hmong sign cim pub dawb\n93047 pahawh hmong sign cim nres tos\n93053 pahawh hmong clan sign tsheej\n93054 pahawh hmong clan sign yeeg\n93055 pahawh hmong clan sign lis\n93056 pahawh hmong clan sign lauj\n93057 pahawh hmong clan sign xyooj\n93058 pahawh hmong clan sign koo\n93059 pahawh hmong clan sign hawj\n93060 pahawh hmong clan sign muas\n93061 pahawh hmong clan sign thoj\n93062 pahawh hmong clan sign tsab\n93063 pahawh hmong clan sign phab\n93064 pahawh hmong clan sign khab\n93065 pahawh hmong clan sign ham\n93066 pahawh hmong clan sign vaj\n93067 pahawh hmong clan sign faj\n93068 pahawh hmong clan sign yaj\n93069 pahawh hmong clan sign tswb\n93070 pahawh hmong clan sign kwm\n93071 pahawh hmong clan sign vwj\n93504 kirat rai sign anusvara\n93505 kirat rai sign tonpi\n93506 kirat rai sign visarga\n93507 kirat rai letter a\n93508 kirat rai letter ka\n93509 kirat rai letter kha\n93510 kirat rai letter ga\n93511 kirat rai letter gha\n93512 kirat rai letter nga\n93513 kirat rai letter ca\n93514 kirat rai letter cha\n93515 kirat rai letter ja\n93516 kirat rai letter jha\n93517 kirat rai letter nya\n93518 kirat rai letter tta\n93519 kirat rai letter ttha\n93520 kirat rai letter dda\n93521 kirat rai letter ddha\n93522 kirat rai letter ta\n93523 kirat rai letter tha\n93524 kirat rai letter da\n93525 kirat rai letter dha\n93526 kirat rai letter na\n93527 kirat rai letter pa\n93528 kirat rai letter pha\n93529 kirat rai letter ba\n93530 kirat rai letter bha\n93531 kirat rai letter ma\n93532 kirat rai letter ya\n93533 kirat rai letter ra\n93534 kirat rai letter la\n93535 kirat rai letter va\n93536 kirat rai letter sa\n93537 kirat rai letter sha\n93538 kirat rai letter ha\n93539 kirat rai vowel sign aa\n93540 kirat rai vowel sign i\n93541 kirat rai vowel sign u\n93542 kirat rai vowel sign ue\n93543 kirat rai vowel sign e\n93544 kirat rai vowel sign ai\n93545 kirat rai vowel sign o\n93546 kirat rai vowel sign au\n93547 kirat rai sign virama\n93548 kirat rai sign saat\n93549 kirat rai sign yupi\n93550 kirat rai danda\n93551 kirat rai double danda\n93552 kirat rai digit zero\n93553 kirat rai digit one\n93554 kirat rai digit two\n93555 kirat rai digit three\n93556 kirat rai digit four\n93557 kirat rai digit five\n93558 kirat rai digit six\n93559 kirat rai digit seven\n93560 kirat rai digit eight\n93561 kirat rai digit nine\n93760 medefaidrin capital letter m\n93761 medefaidrin capital letter s\n93762 medefaidrin capital letter v\n93763 medefaidrin capital letter w\n93764 medefaidrin capital letter atiu\n93765 medefaidrin capital letter z\n93766 medefaidrin capital letter kp\n93767 medefaidrin capital letter p\n93768 medefaidrin capital letter t\n93769 medefaidrin capital letter g\n93770 medefaidrin capital letter f\n93771 medefaidrin capital letter i\n93772 medefaidrin capital letter k\n93773 medefaidrin capital letter a\n93774 medefaidrin capital letter j\n93775 medefaidrin capital letter e\n93776 medefaidrin capital letter b\n93777 medefaidrin capital letter c\n93778 medefaidrin capital letter u\n93779 medefaidrin capital letter yu\n93780 medefaidrin capital letter l\n93781 medefaidrin capital letter q\n93782 medefaidrin capital letter hp\n93783 medefaidrin capital letter ny\n93784 medefaidrin capital letter x\n93785 medefaidrin capital letter d\n93786 medefaidrin capital letter oe\n93787 medefaidrin capital letter n\n93788 medefaidrin capital letter r\n93789 medefaidrin capital letter o\n93790 medefaidrin capital letter ai\n93791 medefaidrin capital letter y\n93792 medefaidrin small letter m\n93793 medefaidrin small letter s\n93794 medefaidrin small letter v\n93795 medefaidrin small letter w\n93796 medefaidrin small letter atiu\n93797 medefaidrin small letter z\n93798 medefaidrin small letter kp\n93799 medefaidrin small letter p\n93800 medefaidrin small letter t\n93801 medefaidrin small letter g\n93802 medefaidrin small letter f\n93803 medefaidrin small letter i\n93804 medefaidrin small letter k\n93805 medefaidrin small letter a\n93806 medefaidrin small letter j\n93807 medefaidrin small letter e\n93808 medefaidrin small letter b\n93809 medefaidrin small letter c\n93810 medefaidrin small letter u\n93811 medefaidrin small letter yu\n93812 medefaidrin small letter l\n93813 medefaidrin small letter q\n93814 medefaidrin small letter hp\n93815 medefaidrin small letter ny\n93816 medefaidrin small letter x\n93817 medefaidrin small letter d\n93818 medefaidrin small letter oe\n93819 medefaidrin small letter n\n93820 medefaidrin small letter r\n93821 medefaidrin small letter o\n93822 medefaidrin small letter ai\n93823 medefaidrin small letter y\n93824 medefaidrin digit zero\n93825 medefaidrin digit one\n93826 medefaidrin digit two\n93827 medefaidrin digit three\n93828 medefaidrin digit four\n93829 medefaidrin digit five\n93830 medefaidrin digit six\n93831 medefaidrin digit seven\n93832 medefaidrin digit eight\n93833 medefaidrin digit nine\n93834 medefaidrin number ten\n93835 medefaidrin number eleven\n93836 medefaidrin number twelve\n93837 medefaidrin number thirteen\n93838 medefaidrin number fourteen\n93839 medefaidrin number fifteen\n93840 medefaidrin number sixteen\n93841 medefaidrin number seventeen\n93842 medefaidrin number eighteen\n93843 medefaidrin number nineteen\n93844 medefaidrin digit one alternate form\n93845 medefaidrin digit two alternate form\n93846 medefaidrin digit three alternate form\n93847 medefaidrin comma\n93848 medefaidrin full stop\n93849 medefaidrin symbol aiva\n93850 medefaidrin exclamation oh\n93856 beria erfe capital letter arkab\n93857 beria erfe capital letter basigna\n93858 beria erfe capital letter darbai\n93859 beria erfe capital letter eh\n93860 beria erfe capital letter fitko\n93861 beria erfe capital letter goway\n93862 beria erfe capital letter hirdeabo\n93863 beria erfe capital letter i\n93864 beria erfe capital letter djai\n93865 beria erfe capital letter kobo\n93866 beria erfe capital letter lakko\n93867 beria erfe capital letter meri\n93868 beria erfe capital letter nini\n93869 beria erfe capital letter gna\n93870 beria erfe capital letter ngay\n93871 beria erfe capital letter oi\n93872 beria erfe capital letter pi\n93873 beria erfe capital letter erigo\n93874 beria erfe capital letter erigo tamura\n93875 beria erfe capital letter seri\n93876 beria erfe capital letter shep\n93877 beria erfe capital letter tatasoue\n93878 beria erfe capital letter ui\n93879 beria erfe capital letter wasse\n93880 beria erfe capital letter ay\n93883 beria erfe small letter arkab\n93884 beria erfe small letter basigna\n93885 beria erfe small letter darbai\n93886 beria erfe small letter eh\n93887 beria erfe small letter fitko\n93888 beria erfe small letter goway\n93889 beria erfe small letter hirdeabo\n93890 beria erfe small letter i\n93891 beria erfe small letter djai\n93892 beria erfe small letter kobo\n93893 beria erfe small letter lakko\n93894 beria erfe small letter meri\n93895 beria erfe small letter nini\n93896 beria erfe small letter gna\n93897 beria erfe small letter ngay\n93898 beria erfe small letter oi\n93899 beria erfe small letter pi\n93900 beria erfe small letter erigo\n93901 beria erfe small letter erigo tamura\n93902 beria erfe small letter seri\n93903 beria erfe small letter shep\n93904 beria erfe small letter tatasoue\n93905 beria erfe small letter ui\n93906 beria erfe small letter wasse\n93907 beria erfe small letter ay\n93952 miao letter pa\n93953 miao letter ba\n93954 miao letter yi pa\n93955 miao letter pla\n93956 miao letter ma\n93957 miao letter mha\n93958 miao letter archaic ma\n93959 miao letter fa\n93960 miao letter va\n93961 miao letter vfa\n93962 miao letter ta\n93963 miao letter da\n93964 miao letter yi tta\n93965 miao letter yi ta\n93966 miao letter tta\n93967 miao letter dda\n93968 miao letter na\n93969 miao letter nha\n93970 miao letter yi nna\n93971 miao letter archaic na\n93972 miao letter nna\n93973 miao letter nnha\n93974 miao letter la\n93975 miao letter lya\n93976 miao letter lha\n93977 miao letter lhya\n93978 miao letter tlha\n93979 miao letter dlha\n93980 miao letter tlhya\n93981 miao letter dlhya\n93982 miao letter ka\n93983 miao letter ga\n93984 miao letter yi ka\n93985 miao letter qa\n93986 miao letter qga\n93987 miao letter nga\n93988 miao letter ngha\n93989 miao letter archaic nga\n93990 miao letter ha\n93991 miao letter xa\n93992 miao letter gha\n93993 miao letter ghha\n93994 miao letter tssa\n93995 miao letter dzza\n93996 miao letter nya\n93997 miao letter nyha\n93998 miao letter tsha\n93999 miao letter dzha\n94000 miao letter yi tsha\n94001 miao letter yi dzha\n94002 miao letter reformed tsha\n94003 miao letter sha\n94004 miao letter ssa\n94005 miao letter zha\n94006 miao letter zsha\n94007 miao letter tsa\n94008 miao letter dza\n94009 miao letter yi tsa\n94010 miao letter sa\n94011 miao letter za\n94012 miao letter zsa\n94013 miao letter zza\n94014 miao letter zzsa\n94015 miao letter archaic zza\n94016 miao letter zzya\n94017 miao letter zzsya\n94018 miao letter wa\n94019 miao letter ah\n94020 miao letter hha\n94021 miao letter bri\n94022 miao letter syi\n94023 miao letter dzyi\n94024 miao letter te\n94025 miao letter tse\n94026 miao letter rte\n94031 miao sign consonant modifier bar\n94032 miao letter nasalization\n94033 miao sign aspiration\n94034 miao sign reformed voicing\n94035 miao sign reformed aspiration\n94036 miao vowel sign a\n94037 miao vowel sign aa\n94038 miao vowel sign ahh\n94039 miao vowel sign an\n94040 miao vowel sign ang\n94041 miao vowel sign o\n94042 miao vowel sign oo\n94043 miao vowel sign wo\n94044 miao vowel sign w\n94045 miao vowel sign e\n94046 miao vowel sign en\n94047 miao vowel sign eng\n94048 miao vowel sign oey\n94049 miao vowel sign i\n94050 miao vowel sign ia\n94051 miao vowel sign ian\n94052 miao vowel sign iang\n94053 miao vowel sign io\n94054 miao vowel sign ie\n94055 miao vowel sign ii\n94056 miao vowel sign iu\n94057 miao vowel sign ing\n94058 miao vowel sign u\n94059 miao vowel sign ua\n94060 miao vowel sign uan\n94061 miao vowel sign uang\n94062 miao vowel sign uu\n94063 miao vowel sign uei\n94064 miao vowel sign ung\n94065 miao vowel sign y\n94066 miao vowel sign yi\n94067 miao vowel sign ae\n94068 miao vowel sign aee\n94069 miao vowel sign err\n94070 miao vowel sign rounded err\n94071 miao vowel sign er\n94072 miao vowel sign rounded er\n94073 miao vowel sign ai\n94074 miao vowel sign ei\n94075 miao vowel sign au\n94076 miao vowel sign ou\n94077 miao vowel sign n\n94078 miao vowel sign ng\n94079 miao vowel sign uog\n94080 miao vowel sign yui\n94081 miao vowel sign og\n94082 miao vowel sign oer\n94083 miao vowel sign vw\n94084 miao vowel sign ig\n94085 miao vowel sign ea\n94086 miao vowel sign iong\n94087 miao vowel sign ui\n94095 miao tone right\n94096 miao tone top right\n94097 miao tone above\n94098 miao tone below\n94099 miao letter tone-2\n94100 miao letter tone-3\n94101 miao letter tone-4\n94102 miao letter tone-5\n94103 miao letter tone-6\n94104 miao letter tone-7\n94105 miao letter tone-8\n94106 miao letter reformed tone-1\n94107 miao letter reformed tone-2\n94108 miao letter reformed tone-4\n94109 miao letter reformed tone-5\n94110 miao letter reformed tone-6\n94111 miao letter reformed tone-8\n94176 tangut iteration mark\n94177 nushu iteration mark\n94178 old chinese hook mark\n94179 old chinese iteration mark\n94180 khitan small script filler\n94192 vietnamese alternate reading mark ca\n94193 vietnamese alternate reading mark nhay\n94194 chinese small simplified er\n94195 chinese small traditional er\n94196 yangqin sign slow one beat\n94197 yangqin sign slow three half beats\n94198 yangqin sign slow two beats\n94208 <tangut ideograph, first>\n100351 <tangut ideograph, last>\n100352 tangut component-001\n100353 tangut component-002\n100354 tangut component-003\n100355 tangut component-004\n100356 tangut component-005\n100357 tangut component-006\n100358 tangut component-007\n100359 tangut component-008\n100360 tangut component-009\n100361 tangut component-010\n100362 tangut component-011\n100363 tangut component-012\n100364 tangut component-013\n100365 tangut component-014\n100366 tangut component-015\n100367 tangut component-016\n100368 tangut component-017\n100369 tangut component-018\n100370 tangut component-019\n100371 tangut component-020\n100372 tangut component-021\n100373 tangut component-022\n100374 tangut component-023\n100375 tangut component-024\n100376 tangut component-025\n100377 tangut component-026\n100378 tangut component-027\n100379 tangut component-028\n100380 tangut component-029\n100381 tangut component-030\n100382 tangut component-031\n100383 tangut component-032\n100384 tangut component-033\n100385 tangut component-034\n100386 tangut component-035\n100387 tangut component-036\n100388 tangut component-037\n100389 tangut component-038\n100390 tangut component-039\n100391 tangut component-040\n100392 tangut component-041\n100393 tangut component-042\n100394 tangut component-043\n100395 tangut component-044\n100396 tangut component-045\n100397 tangut component-046\n100398 tangut component-047\n100399 tangut component-048\n100400 tangut component-049\n100401 tangut component-050\n100402 tangut component-051\n100403 tangut component-052\n100404 tangut component-053\n100405 tangut component-054\n100406 tangut component-055\n100407 tangut component-056\n100408 tangut component-057\n100409 tangut component-058\n100410 tangut component-059\n100411 tangut component-060\n100412 tangut component-061\n100413 tangut component-062\n100414 tangut component-063\n100415 tangut component-064\n100416 tangut component-065\n100417 tangut component-066\n100418 tangut component-067\n100419 tangut component-068\n100420 tangut component-069\n100421 tangut component-070\n100422 tangut component-071\n100423 tangut component-072\n100424 tangut component-073\n100425 tangut component-074\n100426 tangut component-075\n100427 tangut component-076\n100428 tangut component-077\n100429 tangut component-078\n100430 tangut component-079\n100431 tangut component-080\n100432 tangut component-081\n100433 tangut component-082\n100434 tangut component-083\n100435 tangut component-084\n100436 tangut component-085\n100437 tangut component-086\n100438 tangut component-087\n100439 tangut component-088\n100440 tangut component-089\n100441 tangut component-090\n100442 tangut component-091\n100443 tangut component-092\n100444 tangut component-093\n100445 tangut component-094\n100446 tangut component-095\n100447 tangut component-096\n100448 tangut component-097\n100449 tangut component-098\n100450 tangut component-099\n100451 tangut component-100\n100452 tangut component-101\n100453 tangut component-102\n100454 tangut component-103\n100455 tangut component-104\n100456 tangut component-105\n100457 tangut component-106\n100458 tangut component-107\n100459 tangut component-108\n100460 tangut component-109\n100461 tangut component-110\n100462 tangut component-111\n100463 tangut component-112\n100464 tangut component-113\n100465 tangut component-114\n100466 tangut component-115\n100467 tangut component-116\n100468 tangut component-117\n100469 tangut component-118\n100470 tangut component-119\n100471 tangut component-120\n100472 tangut component-121\n100473 tangut component-122\n100474 tangut component-123\n100475 tangut component-124\n100476 tangut component-125\n100477 tangut component-126\n100478 tangut component-127\n100479 tangut component-128\n100480 tangut component-129\n100481 tangut component-130\n100482 tangut component-131\n100483 tangut component-132\n100484 tangut component-133\n100485 tangut component-134\n100486 tangut component-135\n100487 tangut component-136\n100488 tangut component-137\n100489 tangut component-138\n100490 tangut component-139\n100491 tangut component-140\n100492 tangut component-141\n100493 tangut component-142\n100494 tangut component-143\n100495 tangut component-144\n100496 tangut component-145\n100497 tangut component-146\n100498 tangut component-147\n100499 tangut component-148\n100500 tangut component-149\n100501 tangut component-150\n100502 tangut component-151\n100503 tangut component-152\n100504 tangut component-153\n100505 tangut component-154\n100506 tangut component-155\n100507 tangut component-156\n100508 tangut component-157\n100509 tangut component-158\n100510 tangut component-159\n100511 tangut component-160\n100512 tangut component-161\n100513 tangut component-162\n100514 tangut component-163\n100515 tangut component-164\n100516 tangut component-165\n100517 tangut component-166\n100518 tangut component-167\n100519 tangut component-168\n100520 tangut component-169\n100521 tangut component-170\n100522 tangut component-171\n100523 tangut component-172\n100524 tangut component-173\n100525 tangut component-174\n100526 tangut component-175\n100527 tangut component-176\n100528 tangut component-177\n100529 tangut component-178\n100530 tangut component-179\n100531 tangut component-180\n100532 tangut component-181\n100533 tangut component-182\n100534 tangut component-183\n100535 tangut component-184\n100536 tangut component-185\n100537 tangut component-186\n100538 tangut component-187\n100539 tangut component-188\n100540 tangut component-189\n100541 tangut component-190\n100542 tangut component-191\n100543 tangut component-192\n100544 tangut component-193\n100545 tangut component-194\n100546 tangut component-195\n100547 tangut component-196\n100548 tangut component-197\n100549 tangut component-198\n100550 tangut component-199\n100551 tangut component-200\n100552 tangut component-201\n100553 tangut component-202\n100554 tangut component-203\n100555 tangut component-204\n100556 tangut component-205\n100557 tangut component-206\n100558 tangut component-207\n100559 tangut component-208\n100560 tangut component-209\n100561 tangut component-210\n100562 tangut component-211\n100563 tangut component-212\n100564 tangut component-213\n100565 tangut component-214\n100566 tangut component-215\n100567 tangut component-216\n100568 tangut component-217\n100569 tangut component-218\n100570 tangut component-219\n100571 tangut component-220\n100572 tangut component-221\n100573 tangut component-222\n100574 tangut component-223\n100575 tangut component-224\n100576 tangut component-225\n100577 tangut component-226\n100578 tangut component-227\n100579 tangut component-228\n100580 tangut component-229\n100581 tangut component-230\n100582 tangut component-231\n100583 tangut component-232\n100584 tangut component-233\n100585 tangut component-234\n100586 tangut component-235\n100587 tangut component-236\n100588 tangut component-237\n100589 tangut component-238\n100590 tangut component-239\n100591 tangut component-240\n100592 tangut component-241\n100593 tangut component-242\n100594 tangut component-243\n100595 tangut component-244\n100596 tangut component-245\n100597 tangut component-246\n100598 tangut component-247\n100599 tangut component-248\n100600 tangut component-249\n100601 tangut component-250\n100602 tangut component-251\n100603 tangut component-252\n100604 tangut component-253\n100605 tangut component-254\n100606 tangut component-255\n100607 tangut component-256\n100608 tangut component-257\n100609 tangut component-258\n100610 tangut component-259\n100611 tangut component-260\n100612 tangut component-261\n100613 tangut component-262\n100614 tangut component-263\n100615 tangut component-264\n100616 tangut component-265\n100617 tangut component-266\n100618 tangut component-267\n100619 tangut component-268\n100620 tangut component-269\n100621 tangut component-270\n100622 tangut component-271\n100623 tangut component-272\n100624 tangut component-273\n100625 tangut component-274\n100626 tangut component-275\n100627 tangut component-276\n100628 tangut component-277\n100629 tangut component-278\n100630 tangut component-279\n100631 tangut component-280\n100632 tangut component-281\n100633 tangut component-282\n100634 tangut component-283\n100635 tangut component-284\n100636 tangut component-285\n100637 tangut component-286\n100638 tangut component-287\n100639 tangut component-288\n100640 tangut component-289\n100641 tangut component-290\n100642 tangut component-291\n100643 tangut component-292\n100644 tangut component-293\n100645 tangut component-294\n100646 tangut component-295\n100647 tangut component-296\n100648 tangut component-297\n100649 tangut component-298\n100650 tangut component-299\n100651 tangut component-300\n100652 tangut component-301\n100653 tangut component-302\n100654 tangut component-303\n100655 tangut component-304\n100656 tangut component-305\n100657 tangut component-306\n100658 tangut component-307\n100659 tangut component-308\n100660 tangut component-309\n100661 tangut component-310\n100662 tangut component-311\n100663 tangut component-312\n100664 tangut component-313\n100665 tangut component-314\n100666 tangut component-315\n100667 tangut component-316\n100668 tangut component-317\n100669 tangut component-318\n100670 tangut component-319\n100671 tangut component-320\n100672 tangut component-321\n100673 tangut component-322\n100674 tangut component-323\n100675 tangut component-324\n100676 tangut component-325\n100677 tangut component-326\n100678 tangut component-327\n100679 tangut component-328\n100680 tangut component-329\n100681 tangut component-330\n100682 tangut component-331\n100683 tangut component-332\n100684 tangut component-333\n100685 tangut component-334\n100686 tangut component-335\n100687 tangut component-336\n100688 tangut component-337\n100689 tangut component-338\n100690 tangut component-339\n100691 tangut component-340\n100692 tangut component-341\n100693 tangut component-342\n100694 tangut component-343\n100695 tangut component-344\n100696 tangut component-345\n100697 tangut component-346\n100698 tangut component-347\n100699 tangut component-348\n100700 tangut component-349\n100701 tangut component-350\n100702 tangut component-351\n100703 tangut component-352\n100704 tangut component-353\n100705 tangut component-354\n100706 tangut component-355\n100707 tangut component-356\n100708 tangut component-357\n100709 tangut component-358\n100710 tangut component-359\n100711 tangut component-360\n100712 tangut component-361\n100713 tangut component-362\n100714 tangut component-363\n100715 tangut component-364\n100716 tangut component-365\n100717 tangut component-366\n100718 tangut component-367\n100719 tangut component-368\n100720 tangut component-369\n100721 tangut component-370\n100722 tangut component-371\n100723 tangut component-372\n100724 tangut component-373\n100725 tangut component-374\n100726 tangut component-375\n100727 tangut component-376\n100728 tangut component-377\n100729 tangut component-378\n100730 tangut component-379\n100731 tangut component-380\n100732 tangut component-381\n100733 tangut component-382\n100734 tangut component-383\n100735 tangut component-384\n100736 tangut component-385\n100737 tangut component-386\n100738 tangut component-387\n100739 tangut component-388\n100740 tangut component-389\n100741 tangut component-390\n100742 tangut component-391\n100743 tangut component-392\n100744 tangut component-393\n100745 tangut component-394\n100746 tangut component-395\n100747 tangut component-396\n100748 tangut component-397\n100749 tangut component-398\n100750 tangut component-399\n100751 tangut component-400\n100752 tangut component-401\n100753 tangut component-402\n100754 tangut component-403\n100755 tangut component-404\n100756 tangut component-405\n100757 tangut component-406\n100758 tangut component-407\n100759 tangut component-408\n100760 tangut component-409\n100761 tangut component-410\n100762 tangut component-411\n100763 tangut component-412\n100764 tangut component-413\n100765 tangut component-414\n100766 tangut component-415\n100767 tangut component-416\n100768 tangut component-417\n100769 tangut component-418\n100770 tangut component-419\n100771 tangut component-420\n100772 tangut component-421\n100773 tangut component-422\n100774 tangut component-423\n100775 tangut component-424\n100776 tangut component-425\n100777 tangut component-426\n100778 tangut component-427\n100779 tangut component-428\n100780 tangut component-429\n100781 tangut component-430\n100782 tangut component-431\n100783 tangut component-432\n100784 tangut component-433\n100785 tangut component-434\n100786 tangut component-435\n100787 tangut component-436\n100788 tangut component-437\n100789 tangut component-438\n100790 tangut component-439\n100791 tangut component-440\n100792 tangut component-441\n100793 tangut component-442\n100794 tangut component-443\n100795 tangut component-444\n100796 tangut component-445\n100797 tangut component-446\n100798 tangut component-447\n100799 tangut component-448\n100800 tangut component-449\n100801 tangut component-450\n100802 tangut component-451\n100803 tangut component-452\n100804 tangut component-453\n100805 tangut component-454\n100806 tangut component-455\n100807 tangut component-456\n100808 tangut component-457\n100809 tangut component-458\n100810 tangut component-459\n100811 tangut component-460\n100812 tangut component-461\n100813 tangut component-462\n100814 tangut component-463\n100815 tangut component-464\n100816 tangut component-465\n100817 tangut component-466\n100818 tangut component-467\n100819 tangut component-468\n100820 tangut component-469\n100821 tangut component-470\n100822 tangut component-471\n100823 tangut component-472\n100824 tangut component-473\n100825 tangut component-474\n100826 tangut component-475\n100827 tangut component-476\n100828 tangut component-477\n100829 tangut component-478\n100830 tangut component-479\n100831 tangut component-480\n100832 tangut component-481\n100833 tangut component-482\n100834 tangut component-483\n100835 tangut component-484\n100836 tangut component-485\n100837 tangut component-486\n100838 tangut component-487\n100839 tangut component-488\n100840 tangut component-489\n100841 tangut component-490\n100842 tangut component-491\n100843 tangut component-492\n100844 tangut component-493\n100845 tangut component-494\n100846 tangut component-495\n100847 tangut component-496\n100848 tangut component-497\n100849 tangut component-498\n100850 tangut component-499\n100851 tangut component-500\n100852 tangut component-501\n100853 tangut component-502\n100854 tangut component-503\n100855 tangut component-504\n100856 tangut component-505\n100857 tangut component-506\n100858 tangut component-507\n100859 tangut component-508\n100860 tangut component-509\n100861 tangut component-510\n100862 tangut component-511\n100863 tangut component-512\n100864 tangut component-513\n100865 tangut component-514\n100866 tangut component-515\n100867 tangut component-516\n100868 tangut component-517\n100869 tangut component-518\n100870 tangut component-519\n100871 tangut component-520\n100872 tangut component-521\n100873 tangut component-522\n100874 tangut component-523\n100875 tangut component-524\n100876 tangut component-525\n100877 tangut component-526\n100878 tangut component-527\n100879 tangut component-528\n100880 tangut component-529\n100881 tangut component-530\n100882 tangut component-531\n100883 tangut component-532\n100884 tangut component-533\n100885 tangut component-534\n100886 tangut component-535\n100887 tangut component-536\n100888 tangut component-537\n100889 tangut component-538\n100890 tangut component-539\n100891 tangut component-540\n100892 tangut component-541\n100893 tangut component-542\n100894 tangut component-543\n100895 tangut component-544\n100896 tangut component-545\n100897 tangut component-546\n100898 tangut component-547\n100899 tangut component-548\n100900 tangut component-549\n100901 tangut component-550\n100902 tangut component-551\n100903 tangut component-552\n100904 tangut component-553\n100905 tangut component-554\n100906 tangut component-555\n100907 tangut component-556\n100908 tangut component-557\n100909 tangut component-558\n100910 tangut component-559\n100911 tangut component-560\n100912 tangut component-561\n100913 tangut component-562\n100914 tangut component-563\n100915 tangut component-564\n100916 tangut component-565\n100917 tangut component-566\n100918 tangut component-567\n100919 tangut component-568\n100920 tangut component-569\n100921 tangut component-570\n100922 tangut component-571\n100923 tangut component-572\n100924 tangut component-573\n100925 tangut component-574\n100926 tangut component-575\n100927 tangut component-576\n100928 tangut component-577\n100929 tangut component-578\n100930 tangut component-579\n100931 tangut component-580\n100932 tangut component-581\n100933 tangut component-582\n100934 tangut component-583\n100935 tangut component-584\n100936 tangut component-585\n100937 tangut component-586\n100938 tangut component-587\n100939 tangut component-588\n100940 tangut component-589\n100941 tangut component-590\n100942 tangut component-591\n100943 tangut component-592\n100944 tangut component-593\n100945 tangut component-594\n100946 tangut component-595\n100947 tangut component-596\n100948 tangut component-597\n100949 tangut component-598\n100950 tangut component-599\n100951 tangut component-600\n100952 tangut component-601\n100953 tangut component-602\n100954 tangut component-603\n100955 tangut component-604\n100956 tangut component-605\n100957 tangut component-606\n100958 tangut component-607\n100959 tangut component-608\n100960 tangut component-609\n100961 tangut component-610\n100962 tangut component-611\n100963 tangut component-612\n100964 tangut component-613\n100965 tangut component-614\n100966 tangut component-615\n100967 tangut component-616\n100968 tangut component-617\n100969 tangut component-618\n100970 tangut component-619\n100971 tangut component-620\n100972 tangut component-621\n100973 tangut component-622\n100974 tangut component-623\n100975 tangut component-624\n100976 tangut component-625\n100977 tangut component-626\n100978 tangut component-627\n100979 tangut component-628\n100980 tangut component-629\n100981 tangut component-630\n100982 tangut component-631\n100983 tangut component-632\n100984 tangut component-633\n100985 tangut component-634\n100986 tangut component-635\n100987 tangut component-636\n100988 tangut component-637\n100989 tangut component-638\n100990 tangut component-639\n100991 tangut component-640\n100992 tangut component-641\n100993 tangut component-642\n100994 tangut component-643\n100995 tangut component-644\n100996 tangut component-645\n100997 tangut component-646\n100998 tangut component-647\n100999 tangut component-648\n101000 tangut component-649\n101001 tangut component-650\n101002 tangut component-651\n101003 tangut component-652\n101004 tangut component-653\n101005 tangut component-654\n101006 tangut component-655\n101007 tangut component-656\n101008 tangut component-657\n101009 tangut component-658\n101010 tangut component-659\n101011 tangut component-660\n101012 tangut component-661\n101013 tangut component-662\n101014 tangut component-663\n101015 tangut component-664\n101016 tangut component-665\n101017 tangut component-666\n101018 tangut component-667\n101019 tangut component-668\n101020 tangut component-669\n101021 tangut component-670\n101022 tangut component-671\n101023 tangut component-672\n101024 tangut component-673\n101025 tangut component-674\n101026 tangut component-675\n101027 tangut component-676\n101028 tangut component-677\n101029 tangut component-678\n101030 tangut component-679\n101031 tangut component-680\n101032 tangut component-681\n101033 tangut component-682\n101034 tangut component-683\n101035 tangut component-684\n101036 tangut component-685\n101037 tangut component-686\n101038 tangut component-687\n101039 tangut component-688\n101040 tangut component-689\n101041 tangut component-690\n101042 tangut component-691\n101043 tangut component-692\n101044 tangut component-693\n101045 tangut component-694\n101046 tangut component-695\n101047 tangut component-696\n101048 tangut component-697\n101049 tangut component-698\n101050 tangut component-699\n101051 tangut component-700\n101052 tangut component-701\n101053 tangut component-702\n101054 tangut component-703\n101055 tangut component-704\n101056 tangut component-705\n101057 tangut component-706\n101058 tangut component-707\n101059 tangut component-708\n101060 tangut component-709\n101061 tangut component-710\n101062 tangut component-711\n101063 tangut component-712\n101064 tangut component-713\n101065 tangut component-714\n101066 tangut component-715\n101067 tangut component-716\n101068 tangut component-717\n101069 tangut component-718\n101070 tangut component-719\n101071 tangut component-720\n101072 tangut component-721\n101073 tangut component-722\n101074 tangut component-723\n101075 tangut component-724\n101076 tangut component-725\n101077 tangut component-726\n101078 tangut component-727\n101079 tangut component-728\n101080 tangut component-729\n101081 tangut component-730\n101082 tangut component-731\n101083 tangut component-732\n101084 tangut component-733\n101085 tangut component-734\n101086 tangut component-735\n101087 tangut component-736\n101088 tangut component-737\n101089 tangut component-738\n101090 tangut component-739\n101091 tangut component-740\n101092 tangut component-741\n101093 tangut component-742\n101094 tangut component-743\n101095 tangut component-744\n101096 tangut component-745\n101097 tangut component-746\n101098 tangut component-747\n101099 tangut component-748\n101100 tangut component-749\n101101 tangut component-750\n101102 tangut component-751\n101103 tangut component-752\n101104 tangut component-753\n101105 tangut component-754\n101106 tangut component-755\n101107 tangut component-756\n101108 tangut component-757\n101109 tangut component-758\n101110 tangut component-759\n101111 tangut component-760\n101112 tangut component-761\n101113 tangut component-762\n101114 tangut component-763\n101115 tangut component-764\n101116 tangut component-765\n101117 tangut component-766\n101118 tangut component-767\n101119 tangut component-768\n101120 khitan small script character-18b00\n101121 khitan small script character-18b01\n101122 khitan small script character-18b02\n101123 khitan small script character-18b03\n101124 khitan small script character-18b04\n101125 khitan small script character-18b05\n101126 khitan small script character-18b06\n101127 khitan small script character-18b07\n101128 khitan small script character-18b08\n101129 khitan small script character-18b09\n101130 khitan small script character-18b0a\n101131 khitan small script character-18b0b\n101132 khitan small script character-18b0c\n101133 khitan small script character-18b0d\n101134 khitan small script character-18b0e\n101135 khitan small script character-18b0f\n101136 khitan small script character-18b10\n101137 khitan small script character-18b11\n101138 khitan small script character-18b12\n101139 khitan small script character-18b13\n101140 khitan small script character-18b14\n101141 khitan small script character-18b15\n101142 khitan small script character-18b16\n101143 khitan small script character-18b17\n101144 khitan small script character-18b18\n101145 khitan small script character-18b19\n101146 khitan small script character-18b1a\n101147 khitan small script character-18b1b\n101148 khitan small script character-18b1c\n101149 khitan small script character-18b1d\n101150 khitan small script character-18b1e\n101151 khitan small script character-18b1f\n101152 khitan small script character-18b20\n101153 khitan small script character-18b21\n101154 khitan small script character-18b22\n101155 khitan small script character-18b23\n101156 khitan small script character-18b24\n101157 khitan small script character-18b25\n101158 khitan small script character-18b26\n101159 khitan small script character-18b27\n101160 khitan small script character-18b28\n101161 khitan small script character-18b29\n101162 khitan small script character-18b2a\n101163 khitan small script character-18b2b\n101164 khitan small script character-18b2c\n101165 khitan small script character-18b2d\n101166 khitan small script character-18b2e\n101167 khitan small script character-18b2f\n101168 khitan small script character-18b30\n101169 khitan small script character-18b31\n101170 khitan small script character-18b32\n101171 khitan small script character-18b33\n101172 khitan small script character-18b34\n101173 khitan small script character-18b35\n101174 khitan small script character-18b36\n101175 khitan small script character-18b37\n101176 khitan small script character-18b38\n101177 khitan small script character-18b39\n101178 khitan small script character-18b3a\n101179 khitan small script character-18b3b\n101180 khitan small script character-18b3c\n101181 khitan small script character-18b3d\n101182 khitan small script character-18b3e\n101183 khitan small script character-18b3f\n101184 khitan small script character-18b40\n101185 khitan small script character-18b41\n101186 khitan small script character-18b42\n101187 khitan small script character-18b43\n101188 khitan small script character-18b44\n101189 khitan small script character-18b45\n101190 khitan small script character-18b46\n101191 khitan small script character-18b47\n101192 khitan small script character-18b48\n101193 khitan small script character-18b49\n101194 khitan small script character-18b4a\n101195 khitan small script character-18b4b\n101196 khitan small script character-18b4c\n101197 khitan small script character-18b4d\n101198 khitan small script character-18b4e\n101199 khitan small script character-18b4f\n101200 khitan small script character-18b50\n101201 khitan small script character-18b51\n101202 khitan small script character-18b52\n101203 khitan small script character-18b53\n101204 khitan small script character-18b54\n101205 khitan small script character-18b55\n101206 khitan small script character-18b56\n101207 khitan small script character-18b57\n101208 khitan small script character-18b58\n101209 khitan small script character-18b59\n101210 khitan small script character-18b5a\n101211 khitan small script character-18b5b\n101212 khitan small script character-18b5c\n101213 khitan small script character-18b5d\n101214 khitan small script character-18b5e\n101215 khitan small script character-18b5f\n101216 khitan small script character-18b60\n101217 khitan small script character-18b61\n101218 khitan small script character-18b62\n101219 khitan small script character-18b63\n101220 khitan small script character-18b64\n101221 khitan small script character-18b65\n101222 khitan small script character-18b66\n101223 khitan small script character-18b67\n101224 khitan small script character-18b68\n101225 khitan small script character-18b69\n101226 khitan small script character-18b6a\n101227 khitan small script character-18b6b\n101228 khitan small script character-18b6c\n101229 khitan small script character-18b6d\n101230 khitan small script character-18b6e\n101231 khitan small script character-18b6f\n101232 khitan small script character-18b70\n101233 khitan small script character-18b71\n101234 khitan small script character-18b72\n101235 khitan small script character-18b73\n101236 khitan small script character-18b74\n101237 khitan small script character-18b75\n101238 khitan small script character-18b76\n101239 khitan small script character-18b77\n101240 khitan small script character-18b78\n101241 khitan small script character-18b79\n101242 khitan small script character-18b7a\n101243 khitan small script character-18b7b\n101244 khitan small script character-18b7c\n101245 khitan small script character-18b7d\n101246 khitan small script character-18b7e\n101247 khitan small script character-18b7f\n101248 khitan small script character-18b80\n101249 khitan small script character-18b81\n101250 khitan small script character-18b82\n101251 khitan small script character-18b83\n101252 khitan small script character-18b84\n101253 khitan small script character-18b85\n101254 khitan small script character-18b86\n101255 khitan small script character-18b87\n101256 khitan small script character-18b88\n101257 khitan small script character-18b89\n101258 khitan small script character-18b8a\n101259 khitan small script character-18b8b\n101260 khitan small script character-18b8c\n101261 khitan small script character-18b8d\n101262 khitan small script character-18b8e\n101263 khitan small script character-18b8f\n101264 khitan small script character-18b90\n101265 khitan small script character-18b91\n101266 khitan small script character-18b92\n101267 khitan small script character-18b93\n101268 khitan small script character-18b94\n101269 khitan small script character-18b95\n101270 khitan small script character-18b96\n101271 khitan small script character-18b97\n101272 khitan small script character-18b98\n101273 khitan small script character-18b99\n101274 khitan small script character-18b9a\n101275 khitan small script character-18b9b\n101276 khitan small script character-18b9c\n101277 khitan small script character-18b9d\n101278 khitan small script character-18b9e\n101279 khitan small script character-18b9f\n101280 khitan small script character-18ba0\n101281 khitan small script character-18ba1\n101282 khitan small script character-18ba2\n101283 khitan small script character-18ba3\n101284 khitan small script character-18ba4\n101285 khitan small script character-18ba5\n101286 khitan small script character-18ba6\n101287 khitan small script character-18ba7\n101288 khitan small script character-18ba8\n101289 khitan small script character-18ba9\n101290 khitan small script character-18baa\n101291 khitan small script character-18bab\n101292 khitan small script character-18bac\n101293 khitan small script character-18bad\n101294 khitan small script character-18bae\n101295 khitan small script character-18baf\n101296 khitan small script character-18bb0\n101297 khitan small script character-18bb1\n101298 khitan small script character-18bb2\n101299 khitan small script character-18bb3\n101300 khitan small script character-18bb4\n101301 khitan small script character-18bb5\n101302 khitan small script character-18bb6\n101303 khitan small script character-18bb7\n101304 khitan small script character-18bb8\n101305 khitan small script character-18bb9\n101306 khitan small script character-18bba\n101307 khitan small script character-18bbb\n101308 khitan small script character-18bbc\n101309 khitan small script character-18bbd\n101310 khitan small script character-18bbe\n101311 khitan small script character-18bbf\n101312 khitan small script character-18bc0\n101313 khitan small script character-18bc1\n101314 khitan small script character-18bc2\n101315 khitan small script character-18bc3\n101316 khitan small script character-18bc4\n101317 khitan small script character-18bc5\n101318 khitan small script character-18bc6\n101319 khitan small script character-18bc7\n101320 khitan small script character-18bc8\n101321 khitan small script character-18bc9\n101322 khitan small script character-18bca\n101323 khitan small script character-18bcb\n101324 khitan small script character-18bcc\n101325 khitan small script character-18bcd\n101326 khitan small script character-18bce\n101327 khitan small script character-18bcf\n101328 khitan small script character-18bd0\n101329 khitan small script character-18bd1\n101330 khitan small script character-18bd2\n101331 khitan small script character-18bd3\n101332 khitan small script character-18bd4\n101333 khitan small script character-18bd5\n101334 khitan small script character-18bd6\n101335 khitan small script character-18bd7\n101336 khitan small script character-18bd8\n101337 khitan small script character-18bd9\n101338 khitan small script character-18bda\n101339 khitan small script character-18bdb\n101340 khitan small script character-18bdc\n101341 khitan small script character-18bdd\n101342 khitan small script character-18bde\n101343 khitan small script character-18bdf\n101344 khitan small script character-18be0\n101345 khitan small script character-18be1\n101346 khitan small script character-18be2\n101347 khitan small script character-18be3\n101348 khitan small script character-18be4\n101349 khitan small script character-18be5\n101350 khitan small script character-18be6\n101351 khitan small script character-18be7\n101352 khitan small script character-18be8\n101353 khitan small script character-18be9\n101354 khitan small script character-18bea\n101355 khitan small script character-18beb\n101356 khitan small script character-18bec\n101357 khitan small script character-18bed\n101358 khitan small script character-18bee\n101359 khitan small script character-18bef\n101360 khitan small script character-18bf0\n101361 khitan small script character-18bf1\n101362 khitan small script character-18bf2\n101363 khitan small script character-18bf3\n101364 khitan small script character-18bf4\n101365 khitan small script character-18bf5\n101366 khitan small script character-18bf6\n101367 khitan small script character-18bf7\n101368 khitan small script character-18bf8\n101369 khitan small script character-18bf9\n101370 khitan small script character-18bfa\n101371 khitan small script character-18bfb\n101372 khitan small script character-18bfc\n101373 khitan small script character-18bfd\n101374 khitan small script character-18bfe\n101375 khitan small script character-18bff\n101376 khitan small script character-18c00\n101377 khitan small script character-18c01\n101378 khitan small script character-18c02\n101379 khitan small script character-18c03\n101380 khitan small script character-18c04\n101381 khitan small script character-18c05\n101382 khitan small script character-18c06\n101383 khitan small script character-18c07\n101384 khitan small script character-18c08\n101385 khitan small script character-18c09\n101386 khitan small script character-18c0a\n101387 khitan small script character-18c0b\n101388 khitan small script character-18c0c\n101389 khitan small script character-18c0d\n101390 khitan small script character-18c0e\n101391 khitan small script character-18c0f\n101392 khitan small script character-18c10\n101393 khitan small script character-18c11\n101394 khitan small script character-18c12\n101395 khitan small script character-18c13\n101396 khitan small script character-18c14\n101397 khitan small script character-18c15\n101398 khitan small script character-18c16\n101399 khitan small script character-18c17\n101400 khitan small script character-18c18\n101401 khitan small script character-18c19\n101402 khitan small script character-18c1a\n101403 khitan small script character-18c1b\n101404 khitan small script character-18c1c\n101405 khitan small script character-18c1d\n101406 khitan small script character-18c1e\n101407 khitan small script character-18c1f\n101408 khitan small script character-18c20\n101409 khitan small script character-18c21\n101410 khitan small script character-18c22\n101411 khitan small script character-18c23\n101412 khitan small script character-18c24\n101413 khitan small script character-18c25\n101414 khitan small script character-18c26\n101415 khitan small script character-18c27\n101416 khitan small script character-18c28\n101417 khitan small script character-18c29\n101418 khitan small script character-18c2a\n101419 khitan small script character-18c2b\n101420 khitan small script character-18c2c\n101421 khitan small script character-18c2d\n101422 khitan small script character-18c2e\n101423 khitan small script character-18c2f\n101424 khitan small script character-18c30\n101425 khitan small script character-18c31\n101426 khitan small script character-18c32\n101427 khitan small script character-18c33\n101428 khitan small script character-18c34\n101429 khitan small script character-18c35\n101430 khitan small script character-18c36\n101431 khitan small script character-18c37\n101432 khitan small script character-18c38\n101433 khitan small script character-18c39\n101434 khitan small script character-18c3a\n101435 khitan small script character-18c3b\n101436 khitan small script character-18c3c\n101437 khitan small script character-18c3d\n101438 khitan small script character-18c3e\n101439 khitan small script character-18c3f\n101440 khitan small script character-18c40\n101441 khitan small script character-18c41\n101442 khitan small script character-18c42\n101443 khitan small script character-18c43\n101444 khitan small script character-18c44\n101445 khitan small script character-18c45\n101446 khitan small script character-18c46\n101447 khitan small script character-18c47\n101448 khitan small script character-18c48\n101449 khitan small script character-18c49\n101450 khitan small script character-18c4a\n101451 khitan small script character-18c4b\n101452 khitan small script character-18c4c\n101453 khitan small script character-18c4d\n101454 khitan small script character-18c4e\n101455 khitan small script character-18c4f\n101456 khitan small script character-18c50\n101457 khitan small script character-18c51\n101458 khitan small script character-18c52\n101459 khitan small script character-18c53\n101460 khitan small script character-18c54\n101461 khitan small script character-18c55\n101462 khitan small script character-18c56\n101463 khitan small script character-18c57\n101464 khitan small script character-18c58\n101465 khitan small script character-18c59\n101466 khitan small script character-18c5a\n101467 khitan small script character-18c5b\n101468 khitan small script character-18c5c\n101469 khitan small script character-18c5d\n101470 khitan small script character-18c5e\n101471 khitan small script character-18c5f\n101472 khitan small script character-18c60\n101473 khitan small script character-18c61\n101474 khitan small script character-18c62\n101475 khitan small script character-18c63\n101476 khitan small script character-18c64\n101477 khitan small script character-18c65\n101478 khitan small script character-18c66\n101479 khitan small script character-18c67\n101480 khitan small script character-18c68\n101481 khitan small script character-18c69\n101482 khitan small script character-18c6a\n101483 khitan small script character-18c6b\n101484 khitan small script character-18c6c\n101485 khitan small script character-18c6d\n101486 khitan small script character-18c6e\n101487 khitan small script character-18c6f\n101488 khitan small script character-18c70\n101489 khitan small script character-18c71\n101490 khitan small script character-18c72\n101491 khitan small script character-18c73\n101492 khitan small script character-18c74\n101493 khitan small script character-18c75\n101494 khitan small script character-18c76\n101495 khitan small script character-18c77\n101496 khitan small script character-18c78\n101497 khitan small script character-18c79\n101498 khitan small script character-18c7a\n101499 khitan small script character-18c7b\n101500 khitan small script character-18c7c\n101501 khitan small script character-18c7d\n101502 khitan small script character-18c7e\n101503 khitan small script character-18c7f\n101504 khitan small script character-18c80\n101505 khitan small script character-18c81\n101506 khitan small script character-18c82\n101507 khitan small script character-18c83\n101508 khitan small script character-18c84\n101509 khitan small script character-18c85\n101510 khitan small script character-18c86\n101511 khitan small script character-18c87\n101512 khitan small script character-18c88\n101513 khitan small script character-18c89\n101514 khitan small script character-18c8a\n101515 khitan small script character-18c8b\n101516 khitan small script character-18c8c\n101517 khitan small script character-18c8d\n101518 khitan small script character-18c8e\n101519 khitan small script character-18c8f\n101520 khitan small script character-18c90\n101521 khitan small script character-18c91\n101522 khitan small script character-18c92\n101523 khitan small script character-18c93\n101524 khitan small script character-18c94\n101525 khitan small script character-18c95\n101526 khitan small script character-18c96\n101527 khitan small script character-18c97\n101528 khitan small script character-18c98\n101529 khitan small script character-18c99\n101530 khitan small script character-18c9a\n101531 khitan small script character-18c9b\n101532 khitan small script character-18c9c\n101533 khitan small script character-18c9d\n101534 khitan small script character-18c9e\n101535 khitan small script character-18c9f\n101536 khitan small script character-18ca0\n101537 khitan small script character-18ca1\n101538 khitan small script character-18ca2\n101539 khitan small script character-18ca3\n101540 khitan small script character-18ca4\n101541 khitan small script character-18ca5\n101542 khitan small script character-18ca6\n101543 khitan small script character-18ca7\n101544 khitan small script character-18ca8\n101545 khitan small script character-18ca9\n101546 khitan small script character-18caa\n101547 khitan small script character-18cab\n101548 khitan small script character-18cac\n101549 khitan small script character-18cad\n101550 khitan small script character-18cae\n101551 khitan small script character-18caf\n101552 khitan small script character-18cb0\n101553 khitan small script character-18cb1\n101554 khitan small script character-18cb2\n101555 khitan small script character-18cb3\n101556 khitan small script character-18cb4\n101557 khitan small script character-18cb5\n101558 khitan small script character-18cb6\n101559 khitan small script character-18cb7\n101560 khitan small script character-18cb8\n101561 khitan small script character-18cb9\n101562 khitan small script character-18cba\n101563 khitan small script character-18cbb\n101564 khitan small script character-18cbc\n101565 khitan small script character-18cbd\n101566 khitan small script character-18cbe\n101567 khitan small script character-18cbf\n101568 khitan small script character-18cc0\n101569 khitan small script character-18cc1\n101570 khitan small script character-18cc2\n101571 khitan small script character-18cc3\n101572 khitan small script character-18cc4\n101573 khitan small script character-18cc5\n101574 khitan small script character-18cc6\n101575 khitan small script character-18cc7\n101576 khitan small script character-18cc8\n101577 khitan small script character-18cc9\n101578 khitan small script character-18cca\n101579 khitan small script character-18ccb\n101580 khitan small script character-18ccc\n101581 khitan small script character-18ccd\n101582 khitan small script character-18cce\n101583 khitan small script character-18ccf\n101584 khitan small script character-18cd0\n101585 khitan small script character-18cd1\n101586 khitan small script character-18cd2\n101587 khitan small script character-18cd3\n101588 khitan small script character-18cd4\n101589 khitan small script character-18cd5\n101631 khitan small script character-18cff\n101632 <tangut ideograph supplement, first>\n101662 <tangut ideograph supplement, last>\n101760 tangut component-769\n101761 tangut component-770\n101762 tangut component-771\n101763 tangut component-772\n101764 tangut component-773\n101765 tangut component-774\n101766 tangut component-775\n101767 tangut component-776\n101768 tangut component-777\n101769 tangut component-778\n101770 tangut component-779\n101771 tangut component-780\n101772 tangut component-781\n101773 tangut component-782\n101774 tangut component-783\n101775 tangut component-784\n101776 tangut component-785\n101777 tangut component-786\n101778 tangut component-787\n101779 tangut component-788\n101780 tangut component-789\n101781 tangut component-790\n101782 tangut component-791\n101783 tangut component-792\n101784 tangut component-793\n101785 tangut component-794\n101786 tangut component-795\n101787 tangut component-796\n101788 tangut component-797\n101789 tangut component-798\n101790 tangut component-799\n101791 tangut component-800\n101792 tangut component-801\n101793 tangut component-802\n101794 tangut component-803\n101795 tangut component-804\n101796 tangut component-805\n101797 tangut component-806\n101798 tangut component-807\n101799 tangut component-808\n101800 tangut component-809\n101801 tangut component-810\n101802 tangut component-811\n101803 tangut component-812\n101804 tangut component-813\n101805 tangut component-814\n101806 tangut component-815\n101807 tangut component-816\n101808 tangut component-817\n101809 tangut component-818\n101810 tangut component-819\n101811 tangut component-820\n101812 tangut component-821\n101813 tangut component-822\n101814 tangut component-823\n101815 tangut component-824\n101816 tangut component-825\n101817 tangut component-826\n101818 tangut component-827\n101819 tangut component-828\n101820 tangut component-829\n101821 tangut component-830\n101822 tangut component-831\n101823 tangut component-832\n101824 tangut component-833\n101825 tangut component-834\n101826 tangut component-835\n101827 tangut component-836\n101828 tangut component-837\n101829 tangut component-838\n101830 tangut component-839\n101831 tangut component-840\n101832 tangut component-841\n101833 tangut component-842\n101834 tangut component-843\n101835 tangut component-844\n101836 tangut component-845\n101837 tangut component-846\n101838 tangut component-847\n101839 tangut component-848\n101840 tangut component-849\n101841 tangut component-850\n101842 tangut component-851\n101843 tangut component-852\n101844 tangut component-853\n101845 tangut component-854\n101846 tangut component-855\n101847 tangut component-856\n101848 tangut component-857\n101849 tangut component-858\n101850 tangut component-859\n101851 tangut component-860\n101852 tangut component-861\n101853 tangut component-862\n101854 tangut component-863\n101855 tangut component-864\n101856 tangut component-865\n101857 tangut component-866\n101858 tangut component-867\n101859 tangut component-868\n101860 tangut component-869\n101861 tangut component-870\n101862 tangut component-871\n101863 tangut component-872\n101864 tangut component-873\n101865 tangut component-874\n101866 tangut component-875\n101867 tangut component-876\n101868 tangut component-877\n101869 tangut component-878\n101870 tangut component-879\n101871 tangut component-880\n101872 tangut component-881\n101873 tangut component-882\n101874 tangut component-883\n110576 katakana letter minnan tone-2\n110577 katakana letter minnan tone-3\n110578 katakana letter minnan tone-4\n110579 katakana letter minnan tone-5\n110581 katakana letter minnan tone-7\n110582 katakana letter minnan tone-8\n110583 katakana letter minnan nasalized tone-1\n110584 katakana letter minnan nasalized tone-2\n110585 katakana letter minnan nasalized tone-3\n110586 katakana letter minnan nasalized tone-4\n110587 katakana letter minnan nasalized tone-5\n110589 katakana letter minnan nasalized tone-7\n110590 katakana letter minnan nasalized tone-8\n110592 katakana letter archaic e\n110593 hiragana letter archaic ye\n110594 hentaigana letter a-1\n110595 hentaigana letter a-2\n110596 hentaigana letter a-3\n110597 hentaigana letter a-wo\n110598 hentaigana letter i-1\n110599 hentaigana letter i-2\n110600 hentaigana letter i-3\n110601 hentaigana letter i-4\n110602 hentaigana letter u-1\n110603 hentaigana letter u-2\n110604 hentaigana letter u-3\n110605 hentaigana letter u-4\n110606 hentaigana letter u-5\n110607 hentaigana letter e-2\n110608 hentaigana letter e-3\n110609 hentaigana letter e-4\n110610 hentaigana letter e-5\n110611 hentaigana letter e-6\n110612 hentaigana letter o-1\n110613 hentaigana letter o-2\n110614 hentaigana letter o-3\n110615 hentaigana letter ka-1\n110616 hentaigana letter ka-2\n110617 hentaigana letter ka-3\n110618 hentaigana letter ka-4\n110619 hentaigana letter ka-5\n110620 hentaigana letter ka-6\n110621 hentaigana letter ka-7\n110622 hentaigana letter ka-8\n110623 hentaigana letter ka-9\n110624 hentaigana letter ka-10\n110625 hentaigana letter ka-11\n110626 hentaigana letter ka-ke\n110627 hentaigana letter ki-1\n110628 hentaigana letter ki-2\n110629 hentaigana letter ki-3\n110630 hentaigana letter ki-4\n110631 hentaigana letter ki-5\n110632 hentaigana letter ki-6\n110633 hentaigana letter ki-7\n110634 hentaigana letter ki-8\n110635 hentaigana letter ku-1\n110636 hentaigana letter ku-2\n110637 hentaigana letter ku-3\n110638 hentaigana letter ku-4\n110639 hentaigana letter ku-5\n110640 hentaigana letter ku-6\n110641 hentaigana letter ku-7\n110642 hentaigana letter ke-1\n110643 hentaigana letter ke-2\n110644 hentaigana letter ke-3\n110645 hentaigana letter ke-4\n110646 hentaigana letter ke-5\n110647 hentaigana letter ke-6\n110648 hentaigana letter ko-1\n110649 hentaigana letter ko-2\n110650 hentaigana letter ko-3\n110651 hentaigana letter ko-ki\n110652 hentaigana letter sa-1\n110653 hentaigana letter sa-2\n110654 hentaigana letter sa-3\n110655 hentaigana letter sa-4\n110656 hentaigana letter sa-5\n110657 hentaigana letter sa-6\n110658 hentaigana letter sa-7\n110659 hentaigana letter sa-8\n110660 hentaigana letter si-1\n110661 hentaigana letter si-2\n110662 hentaigana letter si-3\n110663 hentaigana letter si-4\n110664 hentaigana letter si-5\n110665 hentaigana letter si-6\n110666 hentaigana letter su-1\n110667 hentaigana letter su-2\n110668 hentaigana letter su-3\n110669 hentaigana letter su-4\n110670 hentaigana letter su-5\n110671 hentaigana letter su-6\n110672 hentaigana letter su-7\n110673 hentaigana letter su-8\n110674 hentaigana letter se-1\n110675 hentaigana letter se-2\n110676 hentaigana letter se-3\n110677 hentaigana letter se-4\n110678 hentaigana letter se-5\n110679 hentaigana letter so-1\n110680 hentaigana letter so-2\n110681 hentaigana letter so-3\n110682 hentaigana letter so-4\n110683 hentaigana letter so-5\n110684 hentaigana letter so-6\n110685 hentaigana letter so-7\n110686 hentaigana letter ta-1\n110687 hentaigana letter ta-2\n110688 hentaigana letter ta-3\n110689 hentaigana letter ta-4\n110690 hentaigana letter ti-1\n110691 hentaigana letter ti-2\n110692 hentaigana letter ti-3\n110693 hentaigana letter ti-4\n110694 hentaigana letter ti-5\n110695 hentaigana letter ti-6\n110696 hentaigana letter ti-7\n110697 hentaigana letter tu-1\n110698 hentaigana letter tu-2\n110699 hentaigana letter tu-3\n110700 hentaigana letter tu-4\n110701 hentaigana letter tu-to\n110702 hentaigana letter te-1\n110703 hentaigana letter te-2\n110704 hentaigana letter te-3\n110705 hentaigana letter te-4\n110706 hentaigana letter te-5\n110707 hentaigana letter te-6\n110708 hentaigana letter te-7\n110709 hentaigana letter te-8\n110710 hentaigana letter te-9\n110711 hentaigana letter to-1\n110712 hentaigana letter to-2\n110713 hentaigana letter to-3\n110714 hentaigana letter to-4\n110715 hentaigana letter to-5\n110716 hentaigana letter to-6\n110717 hentaigana letter to-ra\n110718 hentaigana letter na-1\n110719 hentaigana letter na-2\n110720 hentaigana letter na-3\n110721 hentaigana letter na-4\n110722 hentaigana letter na-5\n110723 hentaigana letter na-6\n110724 hentaigana letter na-7\n110725 hentaigana letter na-8\n110726 hentaigana letter na-9\n110727 hentaigana letter ni-1\n110728 hentaigana letter ni-2\n110729 hentaigana letter ni-3\n110730 hentaigana letter ni-4\n110731 hentaigana letter ni-5\n110732 hentaigana letter ni-6\n110733 hentaigana letter ni-7\n110734 hentaigana letter ni-te\n110735 hentaigana letter nu-1\n110736 hentaigana letter nu-2\n110737 hentaigana letter nu-3\n110738 hentaigana letter ne-1\n110739 hentaigana letter ne-2\n110740 hentaigana letter ne-3\n110741 hentaigana letter ne-4\n110742 hentaigana letter ne-5\n110743 hentaigana letter ne-6\n110744 hentaigana letter ne-ko\n110745 hentaigana letter no-1\n110746 hentaigana letter no-2\n110747 hentaigana letter no-3\n110748 hentaigana letter no-4\n110749 hentaigana letter no-5\n110750 hentaigana letter ha-1\n110751 hentaigana letter ha-2\n110752 hentaigana letter ha-3\n110753 hentaigana letter ha-4\n110754 hentaigana letter ha-5\n110755 hentaigana letter ha-6\n110756 hentaigana letter ha-7\n110757 hentaigana letter ha-8\n110758 hentaigana letter ha-9\n110759 hentaigana letter ha-10\n110760 hentaigana letter ha-11\n110761 hentaigana letter hi-1\n110762 hentaigana letter hi-2\n110763 hentaigana letter hi-3\n110764 hentaigana letter hi-4\n110765 hentaigana letter hi-5\n110766 hentaigana letter hi-6\n110767 hentaigana letter hi-7\n110768 hentaigana letter hu-1\n110769 hentaigana letter hu-2\n110770 hentaigana letter hu-3\n110771 hentaigana letter he-1\n110772 hentaigana letter he-2\n110773 hentaigana letter he-3\n110774 hentaigana letter he-4\n110775 hentaigana letter he-5\n110776 hentaigana letter he-6\n110777 hentaigana letter he-7\n110778 hentaigana letter ho-1\n110779 hentaigana letter ho-2\n110780 hentaigana letter ho-3\n110781 hentaigana letter ho-4\n110782 hentaigana letter ho-5\n110783 hentaigana letter ho-6\n110784 hentaigana letter ho-7\n110785 hentaigana letter ho-8\n110786 hentaigana letter ma-1\n110787 hentaigana letter ma-2\n110788 hentaigana letter ma-3\n110789 hentaigana letter ma-4\n110790 hentaigana letter ma-5\n110791 hentaigana letter ma-6\n110792 hentaigana letter ma-7\n110793 hentaigana letter mi-1\n110794 hentaigana letter mi-2\n110795 hentaigana letter mi-3\n110796 hentaigana letter mi-4\n110797 hentaigana letter mi-5\n110798 hentaigana letter mi-6\n110799 hentaigana letter mi-7\n110800 hentaigana letter mu-1\n110801 hentaigana letter mu-2\n110802 hentaigana letter mu-3\n110803 hentaigana letter mu-4\n110804 hentaigana letter me-1\n110805 hentaigana letter me-2\n110806 hentaigana letter me-ma\n110807 hentaigana letter mo-1\n110808 hentaigana letter mo-2\n110809 hentaigana letter mo-3\n110810 hentaigana letter mo-4\n110811 hentaigana letter mo-5\n110812 hentaigana letter mo-6\n110813 hentaigana letter ya-1\n110814 hentaigana letter ya-2\n110815 hentaigana letter ya-3\n110816 hentaigana letter ya-4\n110817 hentaigana letter ya-5\n110818 hentaigana letter ya-yo\n110819 hentaigana letter yu-1\n110820 hentaigana letter yu-2\n110821 hentaigana letter yu-3\n110822 hentaigana letter yu-4\n110823 hentaigana letter yo-1\n110824 hentaigana letter yo-2\n110825 hentaigana letter yo-3\n110826 hentaigana letter yo-4\n110827 hentaigana letter yo-5\n110828 hentaigana letter yo-6\n110829 hentaigana letter ra-1\n110830 hentaigana letter ra-2\n110831 hentaigana letter ra-3\n110832 hentaigana letter ra-4\n110833 hentaigana letter ri-1\n110834 hentaigana letter ri-2\n110835 hentaigana letter ri-3\n110836 hentaigana letter ri-4\n110837 hentaigana letter ri-5\n110838 hentaigana letter ri-6\n110839 hentaigana letter ri-7\n110840 hentaigana letter ru-1\n110841 hentaigana letter ru-2\n110842 hentaigana letter ru-3\n110843 hentaigana letter ru-4\n110844 hentaigana letter ru-5\n110845 hentaigana letter ru-6\n110846 hentaigana letter re-1\n110847 hentaigana letter re-2\n110848 hentaigana letter re-3\n110849 hentaigana letter re-4\n110850 hentaigana letter ro-1\n110851 hentaigana letter ro-2\n110852 hentaigana letter ro-3\n110853 hentaigana letter ro-4\n110854 hentaigana letter ro-5\n110855 hentaigana letter ro-6\n110856 hentaigana letter wa-1\n110857 hentaigana letter wa-2\n110858 hentaigana letter wa-3\n110859 hentaigana letter wa-4\n110860 hentaigana letter wa-5\n110861 hentaigana letter wi-1\n110862 hentaigana letter wi-2\n110863 hentaigana letter wi-3\n110864 hentaigana letter wi-4\n110865 hentaigana letter wi-5\n110866 hentaigana letter we-1\n110867 hentaigana letter we-2\n110868 hentaigana letter we-3\n110869 hentaigana letter we-4\n110870 hentaigana letter wo-1\n110871 hentaigana letter wo-2\n110872 hentaigana letter wo-3\n110873 hentaigana letter wo-4\n110874 hentaigana letter wo-5\n110875 hentaigana letter wo-6\n110876 hentaigana letter wo-7\n110877 hentaigana letter n-mu-mo-1\n110878 hentaigana letter n-mu-mo-2\n110879 hiragana letter archaic wu\n110880 katakana letter archaic yi\n110881 katakana letter archaic ye\n110882 katakana letter archaic wu\n110898 hiragana letter small ko\n110928 hiragana letter small wi\n110929 hiragana letter small we\n110930 hiragana letter small wo\n110933 katakana letter small ko\n110948 katakana letter small wi\n110949 katakana letter small we\n110950 katakana letter small wo\n110951 katakana letter small n\n110960 nushu character-1b170\n110961 nushu character-1b171\n110962 nushu character-1b172\n110963 nushu character-1b173\n110964 nushu character-1b174\n110965 nushu character-1b175\n110966 nushu character-1b176\n110967 nushu character-1b177\n110968 nushu character-1b178\n110969 nushu character-1b179\n110970 nushu character-1b17a\n110971 nushu character-1b17b\n110972 nushu character-1b17c\n110973 nushu character-1b17d\n110974 nushu character-1b17e\n110975 nushu character-1b17f\n110976 nushu character-1b180\n110977 nushu character-1b181\n110978 nushu character-1b182\n110979 nushu character-1b183\n110980 nushu character-1b184\n110981 nushu character-1b185\n110982 nushu character-1b186\n110983 nushu character-1b187\n110984 nushu character-1b188\n110985 nushu character-1b189\n110986 nushu character-1b18a\n110987 nushu character-1b18b\n110988 nushu character-1b18c\n110989 nushu character-1b18d\n110990 nushu character-1b18e\n110991 nushu character-1b18f\n110992 nushu character-1b190\n110993 nushu character-1b191\n110994 nushu character-1b192\n110995 nushu character-1b193\n110996 nushu character-1b194\n110997 nushu character-1b195\n110998 nushu character-1b196\n110999 nushu character-1b197\n111000 nushu character-1b198\n111001 nushu character-1b199\n111002 nushu character-1b19a\n111003 nushu character-1b19b\n111004 nushu character-1b19c\n111005 nushu character-1b19d\n111006 nushu character-1b19e\n111007 nushu character-1b19f\n111008 nushu character-1b1a0\n111009 nushu character-1b1a1\n111010 nushu character-1b1a2\n111011 nushu character-1b1a3\n111012 nushu character-1b1a4\n111013 nushu character-1b1a5\n111014 nushu character-1b1a6\n111015 nushu character-1b1a7\n111016 nushu character-1b1a8\n111017 nushu character-1b1a9\n111018 nushu character-1b1aa\n111019 nushu character-1b1ab\n111020 nushu character-1b1ac\n111021 nushu character-1b1ad\n111022 nushu character-1b1ae\n111023 nushu character-1b1af\n111024 nushu character-1b1b0\n111025 nushu character-1b1b1\n111026 nushu character-1b1b2\n111027 nushu character-1b1b3\n111028 nushu character-1b1b4\n111029 nushu character-1b1b5\n111030 nushu character-1b1b6\n111031 nushu character-1b1b7\n111032 nushu character-1b1b8\n111033 nushu character-1b1b9\n111034 nushu character-1b1ba\n111035 nushu character-1b1bb\n111036 nushu character-1b1bc\n111037 nushu character-1b1bd\n111038 nushu character-1b1be\n111039 nushu character-1b1bf\n111040 nushu character-1b1c0\n111041 nushu character-1b1c1\n111042 nushu character-1b1c2\n111043 nushu character-1b1c3\n111044 nushu character-1b1c4\n111045 nushu character-1b1c5\n111046 nushu character-1b1c6\n111047 nushu character-1b1c7\n111048 nushu character-1b1c8\n111049 nushu character-1b1c9\n111050 nushu character-1b1ca\n111051 nushu character-1b1cb\n111052 nushu character-1b1cc\n111053 nushu character-1b1cd\n111054 nushu character-1b1ce\n111055 nushu character-1b1cf\n111056 nushu character-1b1d0\n111057 nushu character-1b1d1\n111058 nushu character-1b1d2\n111059 nushu character-1b1d3\n111060 nushu character-1b1d4\n111061 nushu character-1b1d5\n111062 nushu character-1b1d6\n111063 nushu character-1b1d7\n111064 nushu character-1b1d8\n111065 nushu character-1b1d9\n111066 nushu character-1b1da\n111067 nushu character-1b1db\n111068 nushu character-1b1dc\n111069 nushu character-1b1dd\n111070 nushu character-1b1de\n111071 nushu character-1b1df\n111072 nushu character-1b1e0\n111073 nushu character-1b1e1\n111074 nushu character-1b1e2\n111075 nushu character-1b1e3\n111076 nushu character-1b1e4\n111077 nushu character-1b1e5\n111078 nushu character-1b1e6\n111079 nushu character-1b1e7\n111080 nushu character-1b1e8\n111081 nushu character-1b1e9\n111082 nushu character-1b1ea\n111083 nushu character-1b1eb\n111084 nushu character-1b1ec\n111085 nushu character-1b1ed\n111086 nushu character-1b1ee\n111087 nushu character-1b1ef\n111088 nushu character-1b1f0\n111089 nushu character-1b1f1\n111090 nushu character-1b1f2\n111091 nushu character-1b1f3\n111092 nushu character-1b1f4\n111093 nushu character-1b1f5\n111094 nushu character-1b1f6\n111095 nushu character-1b1f7\n111096 nushu character-1b1f8\n111097 nushu character-1b1f9\n111098 nushu character-1b1fa\n111099 nushu character-1b1fb\n111100 nushu character-1b1fc\n111101 nushu character-1b1fd\n111102 nushu character-1b1fe\n111103 nushu character-1b1ff\n111104 nushu character-1b200\n111105 nushu character-1b201\n111106 nushu character-1b202\n111107 nushu character-1b203\n111108 nushu character-1b204\n111109 nushu character-1b205\n111110 nushu character-1b206\n111111 nushu character-1b207\n111112 nushu character-1b208\n111113 nushu character-1b209\n111114 nushu character-1b20a\n111115 nushu character-1b20b\n111116 nushu character-1b20c\n111117 nushu character-1b20d\n111118 nushu character-1b20e\n111119 nushu character-1b20f\n111120 nushu character-1b210\n111121 nushu character-1b211\n111122 nushu character-1b212\n111123 nushu character-1b213\n111124 nushu character-1b214\n111125 nushu character-1b215\n111126 nushu character-1b216\n111127 nushu character-1b217\n111128 nushu character-1b218\n111129 nushu character-1b219\n111130 nushu character-1b21a\n111131 nushu character-1b21b\n111132 nushu character-1b21c\n111133 nushu character-1b21d\n111134 nushu character-1b21e\n111135 nushu character-1b21f\n111136 nushu character-1b220\n111137 nushu character-1b221\n111138 nushu character-1b222\n111139 nushu character-1b223\n111140 nushu character-1b224\n111141 nushu character-1b225\n111142 nushu character-1b226\n111143 nushu character-1b227\n111144 nushu character-1b228\n111145 nushu character-1b229\n111146 nushu character-1b22a\n111147 nushu character-1b22b\n111148 nushu character-1b22c\n111149 nushu character-1b22d\n111150 nushu character-1b22e\n111151 nushu character-1b22f\n111152 nushu character-1b230\n111153 nushu character-1b231\n111154 nushu character-1b232\n111155 nushu character-1b233\n111156 nushu character-1b234\n111157 nushu character-1b235\n111158 nushu character-1b236\n111159 nushu character-1b237\n111160 nushu character-1b238\n111161 nushu character-1b239\n111162 nushu character-1b23a\n111163 nushu character-1b23b\n111164 nushu character-1b23c\n111165 nushu character-1b23d\n111166 nushu character-1b23e\n111167 nushu character-1b23f\n111168 nushu character-1b240\n111169 nushu character-1b241\n111170 nushu character-1b242\n111171 nushu character-1b243\n111172 nushu character-1b244\n111173 nushu character-1b245\n111174 nushu character-1b246\n111175 nushu character-1b247\n111176 nushu character-1b248\n111177 nushu character-1b249\n111178 nushu character-1b24a\n111179 nushu character-1b24b\n111180 nushu character-1b24c\n111181 nushu character-1b24d\n111182 nushu character-1b24e\n111183 nushu character-1b24f\n111184 nushu character-1b250\n111185 nushu character-1b251\n111186 nushu character-1b252\n111187 nushu character-1b253\n111188 nushu character-1b254\n111189 nushu character-1b255\n111190 nushu character-1b256\n111191 nushu character-1b257\n111192 nushu character-1b258\n111193 nushu character-1b259\n111194 nushu character-1b25a\n111195 nushu character-1b25b\n111196 nushu character-1b25c\n111197 nushu character-1b25d\n111198 nushu character-1b25e\n111199 nushu character-1b25f\n111200 nushu character-1b260\n111201 nushu character-1b261\n111202 nushu character-1b262\n111203 nushu character-1b263\n111204 nushu character-1b264\n111205 nushu character-1b265\n111206 nushu character-1b266\n111207 nushu character-1b267\n111208 nushu character-1b268\n111209 nushu character-1b269\n111210 nushu character-1b26a\n111211 nushu character-1b26b\n111212 nushu character-1b26c\n111213 nushu character-1b26d\n111214 nushu character-1b26e\n111215 nushu character-1b26f\n111216 nushu character-1b270\n111217 nushu character-1b271\n111218 nushu character-1b272\n111219 nushu character-1b273\n111220 nushu character-1b274\n111221 nushu character-1b275\n111222 nushu character-1b276\n111223 nushu character-1b277\n111224 nushu character-1b278\n111225 nushu character-1b279\n111226 nushu character-1b27a\n111227 nushu character-1b27b\n111228 nushu character-1b27c\n111229 nushu character-1b27d\n111230 nushu character-1b27e\n111231 nushu character-1b27f\n111232 nushu character-1b280\n111233 nushu character-1b281\n111234 nushu character-1b282\n111235 nushu character-1b283\n111236 nushu character-1b284\n111237 nushu character-1b285\n111238 nushu character-1b286\n111239 nushu character-1b287\n111240 nushu character-1b288\n111241 nushu character-1b289\n111242 nushu character-1b28a\n111243 nushu character-1b28b\n111244 nushu character-1b28c\n111245 nushu character-1b28d\n111246 nushu character-1b28e\n111247 nushu character-1b28f\n111248 nushu character-1b290\n111249 nushu character-1b291\n111250 nushu character-1b292\n111251 nushu character-1b293\n111252 nushu character-1b294\n111253 nushu character-1b295\n111254 nushu character-1b296\n111255 nushu character-1b297\n111256 nushu character-1b298\n111257 nushu character-1b299\n111258 nushu character-1b29a\n111259 nushu character-1b29b\n111260 nushu character-1b29c\n111261 nushu character-1b29d\n111262 nushu character-1b29e\n111263 nushu character-1b29f\n111264 nushu character-1b2a0\n111265 nushu character-1b2a1\n111266 nushu character-1b2a2\n111267 nushu character-1b2a3\n111268 nushu character-1b2a4\n111269 nushu character-1b2a5\n111270 nushu character-1b2a6\n111271 nushu character-1b2a7\n111272 nushu character-1b2a8\n111273 nushu character-1b2a9\n111274 nushu character-1b2aa\n111275 nushu character-1b2ab\n111276 nushu character-1b2ac\n111277 nushu character-1b2ad\n111278 nushu character-1b2ae\n111279 nushu character-1b2af\n111280 nushu character-1b2b0\n111281 nushu character-1b2b1\n111282 nushu character-1b2b2\n111283 nushu character-1b2b3\n111284 nushu character-1b2b4\n111285 nushu character-1b2b5\n111286 nushu character-1b2b6\n111287 nushu character-1b2b7\n111288 nushu character-1b2b8\n111289 nushu character-1b2b9\n111290 nushu character-1b2ba\n111291 nushu character-1b2bb\n111292 nushu character-1b2bc\n111293 nushu character-1b2bd\n111294 nushu character-1b2be\n111295 nushu character-1b2bf\n111296 nushu character-1b2c0\n111297 nushu character-1b2c1\n111298 nushu character-1b2c2\n111299 nushu character-1b2c3\n111300 nushu character-1b2c4\n111301 nushu character-1b2c5\n111302 nushu character-1b2c6\n111303 nushu character-1b2c7\n111304 nushu character-1b2c8\n111305 nushu character-1b2c9\n111306 nushu character-1b2ca\n111307 nushu character-1b2cb\n111308 nushu character-1b2cc\n111309 nushu character-1b2cd\n111310 nushu character-1b2ce\n111311 nushu character-1b2cf\n111312 nushu character-1b2d0\n111313 nushu character-1b2d1\n111314 nushu character-1b2d2\n111315 nushu character-1b2d3\n111316 nushu character-1b2d4\n111317 nushu character-1b2d5\n111318 nushu character-1b2d6\n111319 nushu character-1b2d7\n111320 nushu character-1b2d8\n111321 nushu character-1b2d9\n111322 nushu character-1b2da\n111323 nushu character-1b2db\n111324 nushu character-1b2dc\n111325 nushu character-1b2dd\n111326 nushu character-1b2de\n111327 nushu character-1b2df\n111328 nushu character-1b2e0\n111329 nushu character-1b2e1\n111330 nushu character-1b2e2\n111331 nushu character-1b2e3\n111332 nushu character-1b2e4\n111333 nushu character-1b2e5\n111334 nushu character-1b2e6\n111335 nushu character-1b2e7\n111336 nushu character-1b2e8\n111337 nushu character-1b2e9\n111338 nushu character-1b2ea\n111339 nushu character-1b2eb\n111340 nushu character-1b2ec\n111341 nushu character-1b2ed\n111342 nushu character-1b2ee\n111343 nushu character-1b2ef\n111344 nushu character-1b2f0\n111345 nushu character-1b2f1\n111346 nushu character-1b2f2\n111347 nushu character-1b2f3\n111348 nushu character-1b2f4\n111349 nushu character-1b2f5\n111350 nushu character-1b2f6\n111351 nushu character-1b2f7\n111352 nushu character-1b2f8\n111353 nushu character-1b2f9\n111354 nushu character-1b2fa\n111355 nushu character-1b2fb\n113664 duployan letter h\n113665 duployan letter x\n113666 duployan letter p\n113667 duployan letter t\n113668 duployan letter f\n113669 duployan letter k\n113670 duployan letter l\n113671 duployan letter b\n113672 duployan letter d\n113673 duployan letter v\n113674 duployan letter g\n113675 duployan letter r\n113676 duployan letter p n\n113677 duployan letter d s\n113678 duployan letter f n\n113679 duployan letter k m\n113680 duployan letter r s\n113681 duployan letter th\n113682 duployan letter sloan dh\n113683 duployan letter dh\n113684 duployan letter kk\n113685 duployan letter sloan j\n113686 duployan letter hl\n113687 duployan letter lh\n113688 duployan letter rh\n113689 duployan letter m\n113690 duployan letter n\n113691 duployan letter j\n113692 duployan letter s\n113693 duployan letter m n\n113694 duployan letter n m\n113695 duployan letter j m\n113696 duployan letter s j\n113697 duployan letter m with dot\n113698 duployan letter n with dot\n113699 duployan letter j with dot\n113700 duployan letter j with dots inside and above\n113701 duployan letter s with dot\n113702 duployan letter s with dot below\n113703 duployan letter m s\n113704 duployan letter n s\n113705 duployan letter j s\n113706 duployan letter s s\n113707 duployan letter m n s\n113708 duployan letter n m s\n113709 duployan letter j m s\n113710 duployan letter s j s\n113711 duployan letter j s with dot\n113712 duployan letter j n\n113713 duployan letter j n s\n113714 duployan letter s t\n113715 duployan letter s t r\n113716 duployan letter s p\n113717 duployan letter s p r\n113718 duployan letter t s\n113719 duployan letter t r s\n113720 duployan letter w\n113721 duployan letter wh\n113722 duployan letter w r\n113723 duployan letter s n\n113724 duployan letter s m\n113725 duployan letter k r s\n113726 duployan letter g r s\n113727 duployan letter s k\n113728 duployan letter s k r\n113729 duployan letter a\n113730 duployan letter sloan ow\n113731 duployan letter oa\n113732 duployan letter o\n113733 duployan letter aou\n113734 duployan letter i\n113735 duployan letter e\n113736 duployan letter ie\n113737 duployan letter short i\n113738 duployan letter ui\n113739 duployan letter ee\n113740 duployan letter sloan eh\n113741 duployan letter romanian i\n113742 duployan letter sloan ee\n113743 duployan letter long i\n113744 duployan letter ye\n113745 duployan letter u\n113746 duployan letter eu\n113747 duployan letter xw\n113748 duployan letter u n\n113749 duployan letter long u\n113750 duployan letter romanian u\n113751 duployan letter uh\n113752 duployan letter sloan u\n113753 duployan letter ooh\n113754 duployan letter ow\n113755 duployan letter ou\n113756 duployan letter wa\n113757 duployan letter wo\n113758 duployan letter wi\n113759 duployan letter wei\n113760 duployan letter wow\n113761 duployan letter nasal u\n113762 duployan letter nasal o\n113763 duployan letter nasal i\n113764 duployan letter nasal a\n113765 duployan letter pernin an\n113766 duployan letter pernin am\n113767 duployan letter sloan en\n113768 duployan letter sloan an\n113769 duployan letter sloan on\n113770 duployan letter vocalic m\n113776 duployan affix left horizontal secant\n113777 duployan affix mid horizontal secant\n113778 duployan affix right horizontal secant\n113779 duployan affix low vertical secant\n113780 duployan affix mid vertical secant\n113781 duployan affix high vertical secant\n113782 duployan affix attached secant\n113783 duployan affix attached left-to-right secant\n113784 duployan affix attached tangent\n113785 duployan affix attached tail\n113786 duployan affix attached e hook\n113787 duployan affix attached i hook\n113788 duployan affix attached tangent hook\n113792 duployan affix high acute\n113793 duployan affix high tight acute\n113794 duployan affix high grave\n113795 duployan affix high long grave\n113796 duployan affix high dot\n113797 duployan affix high circle\n113798 duployan affix high line\n113799 duployan affix high wave\n113800 duployan affix high vertical\n113808 duployan affix low acute\n113809 duployan affix low tight acute\n113810 duployan affix low grave\n113811 duployan affix low long grave\n113812 duployan affix low dot\n113813 duployan affix low circle\n113814 duployan affix low line\n113815 duployan affix low wave\n113816 duployan affix low vertical\n113817 duployan affix low arrow\n113820 duployan sign o with cross\n113821 duployan thick letter selector\n113822 duployan double mark\n113823 duployan punctuation chinook full stop\n113824 shorthand format letter overlap\n113825 shorthand format continuing overlap\n113826 shorthand format down step\n113827 shorthand format up step\n117760 up-pointing go-kart\n117761 right-pointing go-kart\n117762 left-pointing stick figure\n117763 right-pointing stick figure\n117764 down-pointing stick figure\n117765 lower horizontal ruler segment\n117766 right vertical ruler segment\n117767 lower right ruler segment\n117768 antenna\n117769 horizontal resistor segment\n117770 vertical resistor segment\n117771 left third inductor\n117772 middle third inductor\n117773 right third inductor\n117774 left-pointing diode\n117775 right-pointing diode\n117776 npn transistor\n117777 pnp transistor\n117778 receptacle\n117779 horizontal capacitor\n117780 vertical capacitor\n117781 logic gate or\n117782 logic gate and\n117783 logic gate inverted inputs\n117784 logic gate inverted output\n117785 logic gate buffer\n117786 logic gate buffer with inverted input\n117787 box drawings light horizontal and upper right\n117788 box drawings light horizontal and lower right\n117789 box drawings light top and upper left\n117790 box drawings light bottom and lower left\n117791 box drawings double diagonal upper right to lower left\n117792 box drawings double diagonal upper left to lower right\n117793 separated block quadrant-1\n117794 separated block quadrant-2\n117795 separated block quadrant-12\n117796 separated block quadrant-3\n117797 separated block quadrant-13\n117798 separated block quadrant-23\n117799 separated block quadrant-123\n117800 separated block quadrant-4\n117801 separated block quadrant-14\n117802 separated block quadrant-24\n117803 separated block quadrant-124\n117804 separated block quadrant-34\n117805 separated block quadrant-134\n117806 separated block quadrant-234\n117807 separated block quadrant-1234\n117808 upper left twelfth circle\n117809 upper centre left twelfth circle\n117810 upper centre right twelfth circle\n117811 upper right twelfth circle\n117812 upper middle left twelfth circle\n117813 upper left quarter circle\n117814 upper right quarter circle\n117815 upper middle right twelfth circle\n117816 lower middle left twelfth circle\n117817 lower left quarter circle\n117818 lower right quarter circle\n117819 lower middle right twelfth circle\n117820 lower left twelfth circle\n117821 lower centre left twelfth circle\n117822 lower centre right twelfth circle\n117823 lower right twelfth circle\n117824 sparse horizontal fill\n117825 sparse vertical fill\n117826 orthogonal crosshatch fill\n117827 diagonal crosshatch fill\n117828 dense vertical fill\n117829 dense horizontal fill\n117830 speckle fill frame-1\n117831 speckle fill frame-2\n117832 left-facing bassinet\n117833 right-facing bassinet\n117834 flying saucer with beams\n117835 flying saucer without beams\n117836 alien monster open jaws\n117837 alien monster closed jaws\n117838 alien squid open tentacles\n117839 alien squid closed tentacles\n117840 alien crab stepping right\n117841 alien crab stepping left\n117842 alien spider crouching\n117843 alien spider spread\n117844 alien monster step-1\n117845 alien monster step-2\n117846 left-pointing rocket ship\n117847 up-pointing rocket ship\n117848 right-pointing rocket ship\n117849 down-pointing rocket ship\n117850 top half left-facing robot\n117851 top half forward-facing robot\n117852 top half right-facing robot\n117853 bottom half left-facing robot\n117854 bottom half forward-facing robot\n117855 bottom half right-facing robot\n117856 left-pointing atomic bomb\n117857 up-pointing atomic bomb\n117858 right-pointing atomic bomb\n117859 down-pointing atomic bomb\n117860 mushroom cloud\n117861 left-pointing rifle\n117862 up-pointing rifle\n117863 right-pointing rifle\n117864 down-pointing rifle\n117865 eight rays inward\n117866 eight rays outward\n117867 black large circle minus left quarter section\n117868 black large circle minus upper quarter section\n117869 black large circle minus right quarter section\n117870 black large circle minus lower quarter section\n117871 black neutral face\n117872 left-facing snake head with open mouth\n117873 up-facing snake head with open mouth\n117874 right-facing snake head with open mouth\n117875 down-facing snake head with open mouth\n117876 left-facing snake head with closed mouth\n117877 up-facing snake head with closed mouth\n117878 right-facing snake head with closed mouth\n117879 down-facing snake head with closed mouth\n117880 left-pointing energy wave\n117881 up-pointing energy wave\n117882 right-pointing energy wave\n117883 down-pointing energy wave\n117884 square spiral from top left\n117885 square spiral from top right\n117886 square spiral from bottom right\n117887 square spiral from bottom left\n117888 striped left-pointing triangle\n117889 striped up-pointing triangle\n117890 striped right-pointing triangle\n117891 striped down-pointing triangle\n117892 vertical ladder\n117893 horizontal ladder\n117894 white lower left pointer\n117895 white lower right pointer\n117896 two rings aligned horizontally\n117897 square four corner saltires\n117898 square four corner diagonals\n117899 square four corner black triangles\n117900 square aperture\n117901 inverse black diamond\n117902 left and upper one eighth block containing black small square\n117903 inverse black small square\n117904 vertical line with four tick marks\n117905 horizontal line with four tick marks\n117906 left-facing fish\n117907 right-facing fish\n117908 left-facing fish with open mouth\n117909 right-facing fish with open mouth\n117910 flapping bird\n117911 left-pointing racing car\n117912 up-pointing racing car\n117913 right-pointing racing car\n117914 down-pointing racing car\n117915 horizontal racing car\n117916 vertical racing car\n117917 vertical go-kart\n117918 left-pointing tank\n117919 right-pointing tank\n117920 left-pointing rocket booster\n117921 right-pointing rocket booster\n117922 left-pointing roller coaster car\n117923 right-pointing roller coaster car\n117924 left half flying saucer\n117925 right half flying saucer\n117926 upper left quadrant face with open eyes\n117927 upper right quadrant face with open eyes\n117928 upper left quadrant face with closed eyes\n117929 upper right quadrant face with closed eyes\n117930 lower left quadrant smiling face\n117931 lower right quadrant smiling face\n117932 lower left quadrant neutral face\n117933 lower right quadrant neutral face\n117934 lower left quadrant face with open mouth\n117935 lower right quadrant face with open mouth\n117936 lower left quadrant frowning face\n117937 lower right quadrant frowning face\n117938 upper left quadrant television\n117939 upper right quadrant television\n117940 lower left quadrant television\n117941 lower right quadrant television\n117942 upper left quadrant microcomputer\n117943 upper right quadrant microcomputer\n117944 lower left quadrant microcomputer\n117945 lower right quadrant microcomputer\n117946 upper left quadrant chess king\n117947 upper right quadrant chess king\n117948 lower left quadrant chess king\n117949 lower right quadrant chess king\n117950 upper left quadrant chess queen\n117951 upper right quadrant chess queen\n117952 lower left quadrant chess queen\n117953 lower right quadrant chess queen\n117954 upper left quadrant chess rook\n117955 upper right quadrant chess rook\n117956 lower left quadrant chess rook\n117957 lower right quadrant chess rook\n117958 upper left quadrant chess bishop\n117959 upper right quadrant chess bishop\n117960 lower left quadrant chess bishop\n117961 lower right quadrant chess bishop\n117962 upper left quadrant chess knight\n117963 upper right quadrant chess knight\n117964 lower left quadrant chess knight\n117965 lower right quadrant chess knight\n117966 upper left quadrant chess pawn\n117967 upper right quadrant chess pawn\n117968 lower left quadrant chess pawn\n117969 lower right quadrant chess pawn\n117970 upper left quadrant standing knight\n117971 upper right quadrant standing knight\n117972 lower left quadrant standing knight\n117973 lower right quadrant standing knight\n117974 outlined latin capital letter a\n117975 outlined latin capital letter b\n117976 outlined latin capital letter c\n117977 outlined latin capital letter d\n117978 outlined latin capital letter e\n117979 outlined latin capital letter f\n117980 outlined latin capital letter g\n117981 outlined latin capital letter h\n117982 outlined latin capital letter i\n117983 outlined latin capital letter j\n117984 outlined latin capital letter k\n117985 outlined latin capital letter l\n117986 outlined latin capital letter m\n117987 outlined latin capital letter n\n117988 outlined latin capital letter o\n117989 outlined latin capital letter p\n117990 outlined latin capital letter q\n117991 outlined latin capital letter r\n117992 outlined latin capital letter s\n117993 outlined latin capital letter t\n117994 outlined latin capital letter u\n117995 outlined latin capital letter v\n117996 outlined latin capital letter w\n117997 outlined latin capital letter x\n117998 outlined latin capital letter y\n117999 outlined latin capital letter z\n118000 outlined digit zero\n118001 outlined digit one\n118002 outlined digit two\n118003 outlined digit three\n118004 outlined digit four\n118005 outlined digit five\n118006 outlined digit six\n118007 outlined digit seven\n118008 outlined digit eight\n118009 outlined digit nine\n118010 snake symbol\n118011 flying saucer symbol\n118012 nose symbol\n118016 block octant-3\n118017 block octant-23\n118018 block octant-123\n118019 block octant-4\n118020 block octant-14\n118021 block octant-124\n118022 block octant-34\n118023 block octant-134\n118024 block octant-234\n118025 block octant-5\n118026 block octant-15\n118027 block octant-25\n118028 block octant-125\n118029 block octant-135\n118030 block octant-235\n118031 block octant-1235\n118032 block octant-45\n118033 block octant-145\n118034 block octant-245\n118035 block octant-1245\n118036 block octant-345\n118037 block octant-1345\n118038 block octant-2345\n118039 block octant-12345\n118040 block octant-6\n118041 block octant-16\n118042 block octant-26\n118043 block octant-126\n118044 block octant-36\n118045 block octant-136\n118046 block octant-236\n118047 block octant-1236\n118048 block octant-146\n118049 block octant-246\n118050 block octant-1246\n118051 block octant-346\n118052 block octant-1346\n118053 block octant-2346\n118054 block octant-12346\n118055 block octant-56\n118056 block octant-156\n118057 block octant-256\n118058 block octant-1256\n118059 block octant-356\n118060 block octant-1356\n118061 block octant-2356\n118062 block octant-12356\n118063 block octant-456\n118064 block octant-1456\n118065 block octant-2456\n118066 block octant-12456\n118067 block octant-3456\n118068 block octant-13456\n118069 block octant-23456\n118070 block octant-17\n118071 block octant-27\n118072 block octant-127\n118073 block octant-37\n118074 block octant-137\n118075 block octant-237\n118076 block octant-1237\n118077 block octant-47\n118078 block octant-147\n118079 block octant-247\n118080 block octant-1247\n118081 block octant-347\n118082 block octant-1347\n118083 block octant-2347\n118084 block octant-12347\n118085 block octant-157\n118086 block octant-257\n118087 block octant-1257\n118088 block octant-357\n118089 block octant-2357\n118090 block octant-12357\n118091 block octant-457\n118092 block octant-1457\n118093 block octant-12457\n118094 block octant-3457\n118095 block octant-13457\n118096 block octant-23457\n118097 block octant-67\n118098 block octant-167\n118099 block octant-267\n118100 block octant-1267\n118101 block octant-367\n118102 block octant-1367\n118103 block octant-2367\n118104 block octant-12367\n118105 block octant-467\n118106 block octant-1467\n118107 block octant-2467\n118108 block octant-12467\n118109 block octant-3467\n118110 block octant-13467\n118111 block octant-23467\n118112 block octant-123467\n118113 block octant-567\n118114 block octant-1567\n118115 block octant-2567\n118116 block octant-12567\n118117 block octant-3567\n118118 block octant-13567\n118119 block octant-23567\n118120 block octant-123567\n118121 block octant-4567\n118122 block octant-14567\n118123 block octant-24567\n118124 block octant-124567\n118125 block octant-34567\n118126 block octant-134567\n118127 block octant-234567\n118128 block octant-1234567\n118129 block octant-18\n118130 block octant-28\n118131 block octant-128\n118132 block octant-38\n118133 block octant-138\n118134 block octant-238\n118135 block octant-1238\n118136 block octant-48\n118137 block octant-148\n118138 block octant-248\n118139 block octant-1248\n118140 block octant-348\n118141 block octant-1348\n118142 block octant-2348\n118143 block octant-12348\n118144 block octant-58\n118145 block octant-158\n118146 block octant-258\n118147 block octant-1258\n118148 block octant-358\n118149 block octant-1358\n118150 block octant-2358\n118151 block octant-12358\n118152 block octant-458\n118153 block octant-1458\n118154 block octant-2458\n118155 block octant-12458\n118156 block octant-3458\n118157 block octant-13458\n118158 block octant-23458\n118159 block octant-123458\n118160 block octant-168\n118161 block octant-268\n118162 block octant-1268\n118163 block octant-368\n118164 block octant-2368\n118165 block octant-12368\n118166 block octant-468\n118167 block octant-1468\n118168 block octant-12468\n118169 block octant-3468\n118170 block octant-13468\n118171 block octant-23468\n118172 block octant-568\n118173 block octant-1568\n118174 block octant-2568\n118175 block octant-12568\n118176 block octant-3568\n118177 block octant-13568\n118178 block octant-23568\n118179 block octant-123568\n118180 block octant-4568\n118181 block octant-14568\n118182 block octant-24568\n118183 block octant-124568\n118184 block octant-34568\n118185 block octant-134568\n118186 block octant-234568\n118187 block octant-1234568\n118188 block octant-178\n118189 block octant-278\n118190 block octant-1278\n118191 block octant-378\n118192 block octant-1378\n118193 block octant-2378\n118194 block octant-12378\n118195 block octant-478\n118196 block octant-1478\n118197 block octant-2478\n118198 block octant-12478\n118199 block octant-3478\n118200 block octant-13478\n118201 block octant-23478\n118202 block octant-123478\n118203 block octant-578\n118204 block octant-1578\n118205 block octant-2578\n118206 block octant-12578\n118207 block octant-3578\n118208 block octant-13578\n118209 block octant-23578\n118210 block octant-123578\n118211 block octant-4578\n118212 block octant-14578\n118213 block octant-24578\n118214 block octant-124578\n118215 block octant-34578\n118216 block octant-134578\n118217 block octant-234578\n118218 block octant-1234578\n118219 block octant-678\n118220 block octant-1678\n118221 block octant-2678\n118222 block octant-12678\n118223 block octant-3678\n118224 block octant-13678\n118225 block octant-23678\n118226 block octant-123678\n118227 block octant-4678\n118228 block octant-14678\n118229 block octant-24678\n118230 block octant-124678\n118231 block octant-34678\n118232 block octant-134678\n118233 block octant-234678\n118234 block octant-1234678\n118235 block octant-15678\n118236 block octant-25678\n118237 block octant-125678\n118238 block octant-35678\n118239 block octant-235678\n118240 block octant-1235678\n118241 block octant-45678\n118242 block octant-145678\n118243 block octant-1245678\n118244 block octant-1345678\n118245 block octant-2345678\n118246 top half standing person\n118247 bottom half standing person\n118248 top half right-facing runner frame-1\n118249 bottom half right-facing runner frame-1\n118250 top half right-facing runner frame-2\n118251 bottom half right-facing runner frame-2\n118252 top half left-facing runner frame-1\n118253 bottom half left-facing runner frame-1\n118254 top half left-facing runner frame-2\n118255 bottom half left-facing runner frame-2\n118256 top half forward-facing runner\n118257 bottom half forward-facing runner frame-1\n118258 bottom half forward-facing runner frame-2\n118259 bottom half forward-facing runner frame-3\n118260 bottom half forward-facing runner frame-4\n118261 moon lander\n118262 top half flailing robot frame-1\n118263 top half flailing robot frame-2\n118264 down-pointing airplane\n118265 left-pointing airplane\n118266 small up-pointing airplane\n118267 up-pointing frog\n118268 down-pointing frog\n118269 explosion frame-1\n118270 explosion frame-2\n118271 explosion frame-3\n118272 right half and left half white circle\n118273 lower half and upper half white circle\n118274 explosion at horizon\n118275 upper half heavy white square\n118276 lower half heavy white square\n118277 heavy white square containing black very small square\n118278 white vertical rectangle with horizontal bar\n118279 top left black left-pointing small triangle\n118280 funnel\n118281 box drawings double diagonal lower left to middle centre to lower right\n118282 box drawings double diagonal upper left to middle centre to upper right\n118283 left half white ellipse\n118284 right half white ellipse\n118285 left half triple dash horizontal\n118286 right half triple dash horizontal\n118287 horizontal line with tick mark\n118288 left half horizontal line with three tick marks\n118289 right half horizontal line with three tick marks\n118290 horizontal line with three tick marks\n118291 lower half vertical line with three tick marks\n118292 upper half vertical line with three tick marks\n118293 vertical line with three tick marks\n118294 box drawings light vertical and top right\n118295 box drawings light vertical and bottom right\n118296 box drawings light vertical and top left\n118297 box drawings light vertical and bottom left\n118298 large type piece upper left arc\n118299 large type piece upper left corner\n118300 large type piece upper terminal\n118301 large type piece upper left crotch\n118302 large type piece left arm\n118303 large type piece crossbar\n118304 large type piece crossbar with lower stem\n118305 large type piece upper half vertex of m\n118306 large type piece diagonal lower left\n118307 large type piece short upper terminal\n118308 large type piece upper right arc\n118309 large type piece right arm\n118310 large type piece upper right crotch\n118311 large type piece upper right corner\n118312 large type piece stem with right crossbar\n118313 large type piece stem\n118314 large type piece diagonal upper right and lower right\n118315 large type piece diagonal upper right\n118316 large type piece diagonal lower right\n118317 large type piece short lower terminal\n118318 large type piece lower left and upper left arc\n118319 large type piece centre of k\n118320 large type piece lower half vertex of m\n118321 large type piece upper half vertex of w\n118322 large type piece centre of x\n118323 large type piece centre of y\n118324 large type piece centre of z with crossbar\n118325 large type piece raised upper left arc\n118326 large type piece stem with left crossbar\n118327 large type piece lower right and upper right arc\n118328 large type piece diagonal upper left and lower left\n118329 large type piece stem with left joint\n118330 large type piece stem with crossbar\n118331 large type piece diagonal upper left\n118332 large type piece lower terminal\n118333 large type piece lower left corner\n118334 large type piece lower left arc\n118335 large type piece lower left crotch\n118336 large type piece crossbar with upper stem\n118337 large type piece vertex of v\n118338 large type piece lower half vertex of w\n118339 large type piece lower right arc\n118340 large type piece lower right corner\n118341 large type piece lower right arc with tail\n118342 large type piece lower right crotch\n118343 large type piece stem-45\n118344 large type piece stem-2345\n118345 large type piece stem-4\n118346 large type piece stem-34\n118347 large type piece stem-234\n118348 large type piece stem-1234\n118349 large type piece stem-3\n118350 large type piece stem-23\n118351 large type piece stem-2\n118352 large type piece stem-12\n118353 separated block sextant-1\n118354 separated block sextant-2\n118355 separated block sextant-12\n118356 separated block sextant-3\n118357 separated block sextant-13\n118358 separated block sextant-23\n118359 separated block sextant-123\n118360 separated block sextant-4\n118361 separated block sextant-14\n118362 separated block sextant-24\n118363 separated block sextant-124\n118364 separated block sextant-34\n118365 separated block sextant-134\n118366 separated block sextant-234\n118367 separated block sextant-1234\n118368 separated block sextant-5\n118369 separated block sextant-15\n118370 separated block sextant-25\n118371 separated block sextant-125\n118372 separated block sextant-35\n118373 separated block sextant-135\n118374 separated block sextant-235\n118375 separated block sextant-1235\n118376 separated block sextant-45\n118377 separated block sextant-145\n118378 separated block sextant-245\n118379 separated block sextant-1245\n118380 separated block sextant-345\n118381 separated block sextant-1345\n118382 separated block sextant-2345\n118383 separated block sextant-12345\n118384 separated block sextant-6\n118385 separated block sextant-16\n118386 separated block sextant-26\n118387 separated block sextant-126\n118388 separated block sextant-36\n118389 separated block sextant-136\n118390 separated block sextant-236\n118391 separated block sextant-1236\n118392 separated block sextant-46\n118393 separated block sextant-146\n118394 separated block sextant-246\n118395 separated block sextant-1246\n118396 separated block sextant-346\n118397 separated block sextant-1346\n118398 separated block sextant-2346\n118399 separated block sextant-12346\n118400 separated block sextant-56\n118401 separated block sextant-156\n118402 separated block sextant-256\n118403 separated block sextant-1256\n118404 separated block sextant-356\n118405 separated block sextant-1356\n118406 separated block sextant-2356\n118407 separated block sextant-12356\n118408 separated block sextant-456\n118409 separated block sextant-1456\n118410 separated block sextant-2456\n118411 separated block sextant-12456\n118412 separated block sextant-3456\n118413 separated block sextant-13456\n118414 separated block sextant-23456\n118415 separated block sextant-123456\n118416 upper left one sixteenth block\n118417 upper centre left one sixteenth block\n118418 upper centre right one sixteenth block\n118419 upper right one sixteenth block\n118420 upper middle left one sixteenth block\n118421 upper middle centre left one sixteenth block\n118422 upper middle centre right one sixteenth block\n118423 upper middle right one sixteenth block\n118424 lower middle left one sixteenth block\n118425 lower middle centre left one sixteenth block\n118426 lower middle centre right one sixteenth block\n118427 lower middle right one sixteenth block\n118428 lower left one sixteenth block\n118429 lower centre left one sixteenth block\n118430 lower centre right one sixteenth block\n118431 lower right one sixteenth block\n118432 right half lower one quarter block\n118433 right three quarters lower one quarter block\n118434 left three quarters lower one quarter block\n118435 left half lower one quarter block\n118436 lower half left one quarter block\n118437 lower three quarters left one quarter block\n118438 upper three quarters left one quarter block\n118439 upper half left one quarter block\n118440 left half upper one quarter block\n118441 left three quarters upper one quarter block\n118442 right three quarters upper one quarter block\n118443 right half upper one quarter block\n118444 upper half right one quarter block\n118445 upper three quarters right one quarter block\n118446 lower three quarters right one quarter block\n118447 lower half right one quarter block\n118448 horizontal zigzag line\n118449 keyhole\n118450 old personal computer with monitor in portrait orientation\n118451 black right triangle caret\n118458 fragile symbol\n118459 office building symbol\n118460 tree symbol\n118461 apple symbol\n118462 cherry symbol\n118463 strawberry symbol\n118464 hebe\n118465 iris\n118466 flora\n118467 metis\n118468 parthenope\n118469 victoria\n118470 egeria\n118471 irene\n118472 eunomia\n118473 psyche\n118474 thetis\n118475 melpomene\n118476 fortuna\n118477 astronomical symbol for asteroid proserpina\n118478 bellona\n118479 amphitrite\n118480 leukothea\n118496 geomantic figure populus\n118497 geomantic figure tristitia\n118498 geomantic figure albus\n118499 geomantic figure fortuna major\n118500 geomantic figure rubeus\n118501 geomantic figure acquisitio\n118502 geomantic figure conjunctio\n118503 geomantic figure caput draconis\n118504 geomantic figure laetitia\n118505 geomantic figure carcer\n118506 geomantic figure amissio\n118507 geomantic figure puella\n118508 geomantic figure fortuna minor\n118509 geomantic figure puer\n118510 geomantic figure cauda draconis\n118511 geomantic figure via\n118512 medium small white circle with horizontal bar\n118528 znamenny combining mark gorazdo nizko s kryzhem on left\n118529 znamenny combining mark nizko s kryzhem on left\n118530 znamenny combining mark tsata on left\n118531 znamenny combining mark gorazdo nizko on left\n118532 znamenny combining mark nizko on left\n118533 znamenny combining mark sredne on left\n118534 znamenny combining mark malo povyshe on left\n118535 znamenny combining mark povyshe on left\n118536 znamenny combining mark vysoko on left\n118537 znamenny combining mark malo povyshe s khokhlom on left\n118538 znamenny combining mark povyshe s khokhlom on left\n118539 znamenny combining mark vysoko s khokhlom on left\n118540 znamenny combining mark gorazdo nizko s kryzhem on right\n118541 znamenny combining mark nizko s kryzhem on right\n118542 znamenny combining mark tsata on right\n118543 znamenny combining mark gorazdo nizko on right\n118544 znamenny combining mark nizko on right\n118545 znamenny combining mark sredne on right\n118546 znamenny combining mark malo povyshe on right\n118547 znamenny combining mark povyshe on right\n118548 znamenny combining mark vysoko on right\n118549 znamenny combining mark malo povyshe s khokhlom on right\n118550 znamenny combining mark povyshe s khokhlom on right\n118551 znamenny combining mark vysoko s khokhlom on right\n118552 znamenny combining mark tsata s kryzhem\n118553 znamenny combining mark malo povyshe s kryzhem\n118554 znamenny combining mark stranno malo povyshe\n118555 znamenny combining mark povyshe s kryzhem\n118556 znamenny combining mark povyshe stranno\n118557 znamenny combining mark vysoko s kryzhem\n118558 znamenny combining mark malo povyshe stranno\n118559 znamenny combining mark gorazdo vysoko\n118560 znamenny combining mark zelo\n118561 znamenny combining mark on\n118562 znamenny combining mark ravno\n118563 znamenny combining mark tikhaya\n118564 znamenny combining mark borzaya\n118565 znamenny combining mark udarka\n118566 znamenny combining mark podvertka\n118567 znamenny combining mark lomka\n118568 znamenny combining mark kupnaya\n118569 znamenny combining mark kachka\n118570 znamenny combining mark zevok\n118571 znamenny combining mark skoba\n118572 znamenny combining mark razseka\n118573 znamenny combining mark kryzh on left\n118576 znamenny combining tonal range mark mrachno\n118577 znamenny combining tonal range mark svetlo\n118578 znamenny combining tonal range mark tresvetlo\n118579 znamenny combining mark zaderzhka\n118580 znamenny combining mark demestvenny zaderzhka\n118581 znamenny combining mark otsechka\n118582 znamenny combining mark podchashie\n118583 znamenny combining mark podchashie with vertical stroke\n118584 znamenny combining mark chashka\n118585 znamenny combining mark chashka polnaya\n118586 znamenny combining mark oblachko\n118587 znamenny combining mark sorochya nozhka\n118588 znamenny combining mark tochka\n118589 znamenny combining mark dvoetochie\n118590 znamenny combining attaching vertical omet\n118591 znamenny combining mark curved omet\n118592 znamenny combining mark kryzh\n118593 znamenny combining lower tonal range indicator\n118594 znamenny priznak modifier level-2\n118595 znamenny priznak modifier level-3\n118596 znamenny priznak modifier direction flip\n118597 znamenny priznak modifier kryzh\n118598 znamenny priznak modifier rog\n118608 znamenny neume kryuk\n118609 znamenny neume kryuk tikhy\n118610 znamenny neume paraklit\n118611 znamenny neume dva v chelnu\n118612 znamenny neume klyuch\n118613 znamenny neume zanozhek\n118614 znamenny neume stopitsa\n118615 znamenny neume stopitsa s ochkom\n118616 znamenny neume perevodka\n118617 znamenny neume perevodka nepostoyannaya\n118618 znamenny neume stopitsa with sorochya nozhka\n118619 znamenny neume chelyustka\n118620 znamenny neume palka\n118621 znamenny neume zapyataya\n118622 znamenny neume golubchik borzy\n118623 znamenny neume golubchik tikhy\n118624 znamenny neume golubchik mrachny\n118625 znamenny neume golubchik svetly\n118626 znamenny neume golubchik tresvetly\n118627 znamenny neume vrakhiya prostaya\n118628 znamenny neume vrakhiya mrachnaya\n118629 znamenny neume vrakhiya svetlaya\n118630 znamenny neume vrakhiya tresvetlaya\n118631 znamenny neume vrakhiya klyuchevaya prostaya\n118632 znamenny neume vrakhiya klyuchevaya mrachnaya\n118633 znamenny neume vrakhiya klyuchevaya svetlaya\n118634 znamenny neume vrakhiya klyuchevaya tresvetlaya\n118635 znamenny neume double zapyataya\n118636 znamenny neume reversed chelyustka\n118637 znamenny neume derbitsa\n118638 znamenny neume khamilo\n118639 znamenny neume chashka\n118640 znamenny neume podchashie\n118641 znamenny neume skameytsa mrachnaya\n118642 znamenny neume skameytsa svetlaya\n118643 znamenny neume skameytsa tresvetlaya\n118644 znamenny neume skameytsa tikhaya\n118645 znamenny neume demestvenny klyuch\n118646 znamenny neume skameytsa klyuchevaya svetlaya\n118647 znamenny neume skameytsa klyuchenepostoyannaya\n118648 znamenny neume skameytsa klyuchevaya tikhaya\n118649 znamenny neume skameytsa dvoechelnaya prostaya\n118650 znamenny neume skameytsa dvoechelnaya svetlaya\n118651 znamenny neume skameytsa dvoechelnaya nepostoyannaya\n118652 znamenny neume skameytsa dvoechelnaya klyuchevaya\n118653 znamenny neume slozhitie\n118654 znamenny neume slozhitie s zapyatoy\n118655 znamenny neume slozhitie zakrytoe\n118656 znamenny neume slozhitie s kryzhem\n118657 znamenny neume kryzh\n118658 znamenny neume rog\n118659 znamenny neume fita\n118660 znamenny neume kobyla\n118661 znamenny neume zmeytsa\n118662 znamenny neume statya\n118663 znamenny neume statya s zapyatoy\n118664 znamenny neume statya s kryzhem\n118665 znamenny neume statya s zapyatoy i kryzhem\n118666 znamenny neume statya s kryzhem i zapyatoy\n118667 znamenny neume statya zakrytaya\n118668 znamenny neume statya zakrytaya s zapyatoy\n118669 znamenny neume statya s rogom\n118670 znamenny neume statya s dvumya zapyatymi\n118671 znamenny neume statya s zapyatoy i podchashiem\n118672 znamenny neume polkulizmy\n118673 znamenny neume statya nepostoyannaya\n118674 znamenny neume strela prostaya\n118675 znamenny neume strela mrachnotikhaya\n118676 znamenny neume strela kryzhevaya\n118677 znamenny neume strela polupovodnaya\n118678 znamenny neume strela povodnaya\n118679 znamenny neume strela nepostoyannaya\n118680 znamenny neume strela klyuchepovodnaya\n118681 znamenny neume strela klyuchenepostoyannaya\n118682 znamenny neume strela tikhaya putnaya\n118683 znamenny neume strela dvoechelnaya\n118684 znamenny neume strela dvoechelnokryzhevaya\n118685 znamenny neume strela dvoechelnopovodnaya\n118686 znamenny neume strela dvoechelnaya klyuchevaya\n118687 znamenny neume strela dvoechelnopovodnaya klyuchevaya\n118688 znamenny neume strela gromnaya with single zapyataya\n118689 znamenny neume strela gromopovodnaya with single zapyataya\n118690 znamenny neume strela gromnaya\n118691 znamenny neume strela gromopovodnaya\n118692 znamenny neume strela gromopovodnaya with double zapyataya\n118693 znamenny neume strela gromokryzhevaya\n118694 znamenny neume strela gromokryzhevaya povodnaya\n118695 znamenny neume mechik\n118696 znamenny neume mechik povodny\n118697 znamenny neume mechik klyuchevoy\n118698 znamenny neume mechik klyuchepovodny\n118699 znamenny neume mechik klyuchenepostoyanny\n118700 znamenny neume strela tryasoglasnaya\n118701 znamenny neume strela tryasopovodnaya\n118702 znamenny neume strela tryasostrelnaya\n118703 znamenny neume osoka\n118704 znamenny neume osoka svetlaya\n118705 znamenny neume osoka tresvetlaya\n118706 znamenny neume osoka kryukovaya svetlaya\n118707 znamenny neume osoka klyuchevaya svetlaya\n118708 znamenny neume osoka klyuchevaya nepostoyannaya\n118709 znamenny neume strela kryukovaya\n118710 znamenny neume strela kryukovaya povodnaya\n118711 znamenny neume strela kryukovaya gromnaya with single zapyataya\n118712 znamenny neume strela kryukovaya gromopovodnaya with single zapyataya\n118713 znamenny neume strela kryukovaya gromnaya\n118714 znamenny neume strela kryukovaya gromopovodnaya\n118715 znamenny neume strela kryukovaya gromopovodnaya with double zapyataya\n118716 znamenny neume strela kryukovaya gromokryzhevaya\n118717 znamenny neume strela kryukovaya gromokryzhevaya povodnaya\n118718 znamenny neume strela kryukovaya tryaska\n118719 znamenny neume kufisma\n118720 znamenny neume oblako\n118721 znamenny neume duda\n118722 znamenny neume nemka\n118723 znamenny neume pauk\n118784 byzantine musical symbol psili\n118785 byzantine musical symbol daseia\n118786 byzantine musical symbol perispomeni\n118787 byzantine musical symbol oxeia ekfonitikon\n118788 byzantine musical symbol oxeia dipli\n118789 byzantine musical symbol vareia ekfonitikon\n118790 byzantine musical symbol vareia dipli\n118791 byzantine musical symbol kathisti\n118792 byzantine musical symbol syrmatiki\n118793 byzantine musical symbol paraklitiki\n118794 byzantine musical symbol ypokrisis\n118795 byzantine musical symbol ypokrisis dipli\n118796 byzantine musical symbol kremasti\n118797 byzantine musical symbol apeso ekfonitikon\n118798 byzantine musical symbol exo ekfonitikon\n118799 byzantine musical symbol teleia\n118800 byzantine musical symbol kentimata\n118801 byzantine musical symbol apostrofos\n118802 byzantine musical symbol apostrofos dipli\n118803 byzantine musical symbol synevma\n118804 byzantine musical symbol thita\n118805 byzantine musical symbol oligon archaion\n118806 byzantine musical symbol gorgon archaion\n118807 byzantine musical symbol psilon\n118808 byzantine musical symbol chamilon\n118809 byzantine musical symbol vathy\n118810 byzantine musical symbol ison archaion\n118811 byzantine musical symbol kentima archaion\n118812 byzantine musical symbol kentimata archaion\n118813 byzantine musical symbol saximata\n118814 byzantine musical symbol parichon\n118815 byzantine musical symbol stavros apodexia\n118816 byzantine musical symbol oxeiai archaion\n118817 byzantine musical symbol vareiai archaion\n118818 byzantine musical symbol apoderma archaion\n118819 byzantine musical symbol apothema\n118820 byzantine musical symbol klasma\n118821 byzantine musical symbol revma\n118822 byzantine musical symbol piasma archaion\n118823 byzantine musical symbol tinagma\n118824 byzantine musical symbol anatrichisma\n118825 byzantine musical symbol seisma\n118826 byzantine musical symbol synagma archaion\n118827 byzantine musical symbol synagma meta stavrou\n118828 byzantine musical symbol oyranisma archaion\n118829 byzantine musical symbol thema\n118830 byzantine musical symbol lemoi\n118831 byzantine musical symbol dyo\n118832 byzantine musical symbol tria\n118833 byzantine musical symbol tessera\n118834 byzantine musical symbol kratimata\n118835 byzantine musical symbol apeso exo neo\n118836 byzantine musical symbol fthora archaion\n118837 byzantine musical symbol imifthora\n118838 byzantine musical symbol tromikon archaion\n118839 byzantine musical symbol katava tromikon\n118840 byzantine musical symbol pelaston\n118841 byzantine musical symbol psifiston\n118842 byzantine musical symbol kontevma\n118843 byzantine musical symbol chorevma archaion\n118844 byzantine musical symbol rapisma\n118845 byzantine musical symbol parakalesma archaion\n118846 byzantine musical symbol paraklitiki archaion\n118847 byzantine musical symbol ichadin\n118848 byzantine musical symbol nana\n118849 byzantine musical symbol petasma\n118850 byzantine musical symbol kontevma allo\n118851 byzantine musical symbol tromikon allo\n118852 byzantine musical symbol straggismata\n118853 byzantine musical symbol gronthismata\n118854 byzantine musical symbol ison neo\n118855 byzantine musical symbol oligon neo\n118856 byzantine musical symbol oxeia neo\n118857 byzantine musical symbol petasti\n118858 byzantine musical symbol koufisma\n118859 byzantine musical symbol petastokoufisma\n118860 byzantine musical symbol kratimokoufisma\n118861 byzantine musical symbol pelaston neo\n118862 byzantine musical symbol kentimata neo ano\n118863 byzantine musical symbol kentima neo ano\n118864 byzantine musical symbol ypsili\n118865 byzantine musical symbol apostrofos neo\n118866 byzantine musical symbol apostrofoi syndesmos neo\n118867 byzantine musical symbol yporroi\n118868 byzantine musical symbol kratimoyporroon\n118869 byzantine musical symbol elafron\n118870 byzantine musical symbol chamili\n118871 byzantine musical symbol mikron ison\n118872 byzantine musical symbol vareia neo\n118873 byzantine musical symbol piasma neo\n118874 byzantine musical symbol psifiston neo\n118875 byzantine musical symbol omalon\n118876 byzantine musical symbol antikenoma\n118877 byzantine musical symbol lygisma\n118878 byzantine musical symbol paraklitiki neo\n118879 byzantine musical symbol parakalesma neo\n118880 byzantine musical symbol eteron parakalesma\n118881 byzantine musical symbol kylisma\n118882 byzantine musical symbol antikenokylisma\n118883 byzantine musical symbol tromikon neo\n118884 byzantine musical symbol ekstrepton\n118885 byzantine musical symbol synagma neo\n118886 byzantine musical symbol syrma\n118887 byzantine musical symbol chorevma neo\n118888 byzantine musical symbol epegerma\n118889 byzantine musical symbol seisma neo\n118890 byzantine musical symbol xiron klasma\n118891 byzantine musical symbol tromikopsifiston\n118892 byzantine musical symbol psifistolygisma\n118893 byzantine musical symbol tromikolygisma\n118894 byzantine musical symbol tromikoparakalesma\n118895 byzantine musical symbol psifistoparakalesma\n118896 byzantine musical symbol tromikosynagma\n118897 byzantine musical symbol psifistosynagma\n118898 byzantine musical symbol gorgosyntheton\n118899 byzantine musical symbol argosyntheton\n118900 byzantine musical symbol eteron argosyntheton\n118901 byzantine musical symbol oyranisma neo\n118902 byzantine musical symbol thematismos eso\n118903 byzantine musical symbol thematismos exo\n118904 byzantine musical symbol thema aploun\n118905 byzantine musical symbol thes kai apothes\n118906 byzantine musical symbol katavasma\n118907 byzantine musical symbol endofonon\n118908 byzantine musical symbol yfen kato\n118909 byzantine musical symbol yfen ano\n118910 byzantine musical symbol stavros\n118911 byzantine musical symbol klasma ano\n118912 byzantine musical symbol dipli archaion\n118913 byzantine musical symbol kratima archaion\n118914 byzantine musical symbol kratima allo\n118915 byzantine musical symbol kratima neo\n118916 byzantine musical symbol apoderma neo\n118917 byzantine musical symbol apli\n118918 byzantine musical symbol dipli\n118919 byzantine musical symbol tripli\n118920 byzantine musical symbol tetrapli\n118921 byzantine musical symbol koronis\n118922 byzantine musical symbol leimma enos chronou\n118923 byzantine musical symbol leimma dyo chronon\n118924 byzantine musical symbol leimma trion chronon\n118925 byzantine musical symbol leimma tessaron chronon\n118926 byzantine musical symbol leimma imiseos chronou\n118927 byzantine musical symbol gorgon neo ano\n118928 byzantine musical symbol gorgon parestigmenon aristera\n118929 byzantine musical symbol gorgon parestigmenon dexia\n118930 byzantine musical symbol digorgon\n118931 byzantine musical symbol digorgon parestigmenon aristera kato\n118932 byzantine musical symbol digorgon parestigmenon aristera ano\n118933 byzantine musical symbol digorgon parestigmenon dexia\n118934 byzantine musical symbol trigorgon\n118935 byzantine musical symbol argon\n118936 byzantine musical symbol imidiargon\n118937 byzantine musical symbol diargon\n118938 byzantine musical symbol agogi poli argi\n118939 byzantine musical symbol agogi argoteri\n118940 byzantine musical symbol agogi argi\n118941 byzantine musical symbol agogi metria\n118942 byzantine musical symbol agogi mesi\n118943 byzantine musical symbol agogi gorgi\n118944 byzantine musical symbol agogi gorgoteri\n118945 byzantine musical symbol agogi poli gorgi\n118946 byzantine musical symbol martyria protos ichos\n118947 byzantine musical symbol martyria alli protos ichos\n118948 byzantine musical symbol martyria deyteros ichos\n118949 byzantine musical symbol martyria alli deyteros ichos\n118950 byzantine musical symbol martyria tritos ichos\n118951 byzantine musical symbol martyria trifonias\n118952 byzantine musical symbol martyria tetartos ichos\n118953 byzantine musical symbol martyria tetartos legetos ichos\n118954 byzantine musical symbol martyria legetos ichos\n118955 byzantine musical symbol martyria plagios ichos\n118956 byzantine musical symbol isakia telous ichimatos\n118957 byzantine musical symbol apostrofoi telous ichimatos\n118958 byzantine musical symbol fanerosis tetrafonias\n118959 byzantine musical symbol fanerosis monofonias\n118960 byzantine musical symbol fanerosis difonias\n118961 byzantine musical symbol martyria varys ichos\n118962 byzantine musical symbol martyria protovarys ichos\n118963 byzantine musical symbol martyria plagios tetartos ichos\n118964 byzantine musical symbol gorthmikon n aploun\n118965 byzantine musical symbol gorthmikon n diploun\n118966 byzantine musical symbol enarxis kai fthora vou\n118967 byzantine musical symbol imifonon\n118968 byzantine musical symbol imifthoron\n118969 byzantine musical symbol fthora archaion deyterou ichou\n118970 byzantine musical symbol fthora diatoniki pa\n118971 byzantine musical symbol fthora diatoniki nana\n118972 byzantine musical symbol fthora naos ichos\n118973 byzantine musical symbol fthora diatoniki di\n118974 byzantine musical symbol fthora skliron diatonon di\n118975 byzantine musical symbol fthora diatoniki ke\n118976 byzantine musical symbol fthora diatoniki zo\n118977 byzantine musical symbol fthora diatoniki ni kato\n118978 byzantine musical symbol fthora diatoniki ni ano\n118979 byzantine musical symbol fthora malakon chroma difonias\n118980 byzantine musical symbol fthora malakon chroma monofonias\n118981 byzantine musical symbol fhtora skliron chroma vasis\n118982 byzantine musical symbol fthora skliron chroma synafi\n118983 byzantine musical symbol fthora nenano\n118984 byzantine musical symbol chroa zygos\n118985 byzantine musical symbol chroa kliton\n118986 byzantine musical symbol chroa spathi\n118987 byzantine musical symbol fthora i yfesis tetartimorion\n118988 byzantine musical symbol fthora enarmonios antifonia\n118989 byzantine musical symbol yfesis tritimorion\n118990 byzantine musical symbol diesis tritimorion\n118991 byzantine musical symbol diesis tetartimorion\n118992 byzantine musical symbol diesis apli dyo dodekata\n118993 byzantine musical symbol diesis monogrammos tessera dodekata\n118994 byzantine musical symbol diesis digrammos ex dodekata\n118995 byzantine musical symbol diesis trigrammos okto dodekata\n118996 byzantine musical symbol yfesis apli dyo dodekata\n118997 byzantine musical symbol yfesis monogrammos tessera dodekata\n118998 byzantine musical symbol yfesis digrammos ex dodekata\n118999 byzantine musical symbol yfesis trigrammos okto dodekata\n119000 byzantine musical symbol geniki diesis\n119001 byzantine musical symbol geniki yfesis\n119002 byzantine musical symbol diastoli apli mikri\n119003 byzantine musical symbol diastoli apli megali\n119004 byzantine musical symbol diastoli dipli\n119005 byzantine musical symbol diastoli theseos\n119006 byzantine musical symbol simansis theseos\n119007 byzantine musical symbol simansis theseos disimou\n119008 byzantine musical symbol simansis theseos trisimou\n119009 byzantine musical symbol simansis theseos tetrasimou\n119010 byzantine musical symbol simansis arseos\n119011 byzantine musical symbol simansis arseos disimou\n119012 byzantine musical symbol simansis arseos trisimou\n119013 byzantine musical symbol simansis arseos tetrasimou\n119014 byzantine musical symbol digramma gg\n119015 byzantine musical symbol diftoggos ou\n119016 byzantine musical symbol stigma\n119017 byzantine musical symbol arktiko pa\n119018 byzantine musical symbol arktiko vou\n119019 byzantine musical symbol arktiko ga\n119020 byzantine musical symbol arktiko di\n119021 byzantine musical symbol arktiko ke\n119022 byzantine musical symbol arktiko zo\n119023 byzantine musical symbol arktiko ni\n119024 byzantine musical symbol kentimata neo meso\n119025 byzantine musical symbol kentima neo meso\n119026 byzantine musical symbol kentimata neo kato\n119027 byzantine musical symbol kentima neo kato\n119028 byzantine musical symbol klasma kato\n119029 byzantine musical symbol gorgon neo kato\n119040 musical symbol single barline\n119041 musical symbol double barline\n119042 musical symbol final barline\n119043 musical symbol reverse final barline\n119044 musical symbol dashed barline\n119045 musical symbol short barline\n119046 musical symbol left repeat sign\n119047 musical symbol right repeat sign\n119048 musical symbol repeat dots\n119049 musical symbol dal segno\n119050 musical symbol da capo\n119051 musical symbol segno\n119052 musical symbol coda\n119053 musical symbol repeated figure-1\n119054 musical symbol repeated figure-2\n119055 musical symbol repeated figure-3\n119056 musical symbol fermata\n119057 musical symbol fermata below\n119058 musical symbol breath mark\n119059 musical symbol caesura\n119060 musical symbol brace\n119061 musical symbol bracket\n119062 musical symbol one-line staff\n119063 musical symbol two-line staff\n119064 musical symbol three-line staff\n119065 musical symbol four-line staff\n119066 musical symbol five-line staff\n119067 musical symbol six-line staff\n119068 musical symbol six-string fretboard\n119069 musical symbol four-string fretboard\n119070 musical symbol g clef\n119071 musical symbol g clef ottava alta\n119072 musical symbol g clef ottava bassa\n119073 musical symbol c clef\n119074 musical symbol f clef\n119075 musical symbol f clef ottava alta\n119076 musical symbol f clef ottava bassa\n119077 musical symbol drum clef-1\n119078 musical symbol drum clef-2\n119081 musical symbol multiple measure rest\n119082 musical symbol double sharp\n119083 musical symbol double flat\n119084 musical symbol flat up\n119085 musical symbol flat down\n119086 musical symbol natural up\n119087 musical symbol natural down\n119088 musical symbol sharp up\n119089 musical symbol sharp down\n119090 musical symbol quarter tone sharp\n119091 musical symbol quarter tone flat\n119092 musical symbol common time\n119093 musical symbol cut time\n119094 musical symbol ottava alta\n119095 musical symbol ottava bassa\n119096 musical symbol quindicesima alta\n119097 musical symbol quindicesima bassa\n119098 musical symbol multi rest\n119099 musical symbol whole rest\n119100 musical symbol half rest\n119101 musical symbol quarter rest\n119102 musical symbol eighth rest\n119103 musical symbol sixteenth rest\n119104 musical symbol thirty-second rest\n119105 musical symbol sixty-fourth rest\n119106 musical symbol one hundred twenty-eighth rest\n119107 musical symbol x notehead\n119108 musical symbol plus notehead\n119109 musical symbol circle x notehead\n119110 musical symbol square notehead white\n119111 musical symbol square notehead black\n119112 musical symbol triangle notehead up white\n119113 musical symbol triangle notehead up black\n119114 musical symbol triangle notehead left white\n119115 musical symbol triangle notehead left black\n119116 musical symbol triangle notehead right white\n119117 musical symbol triangle notehead right black\n119118 musical symbol triangle notehead down white\n119119 musical symbol triangle notehead down black\n119120 musical symbol triangle notehead up right white\n119121 musical symbol triangle notehead up right black\n119122 musical symbol moon notehead white\n119123 musical symbol moon notehead black\n119124 musical symbol triangle-round notehead down white\n119125 musical symbol triangle-round notehead down black\n119126 musical symbol parenthesis notehead\n119127 musical symbol void notehead\n119128 musical symbol notehead black\n119129 musical symbol null notehead\n119130 musical symbol cluster notehead white\n119131 musical symbol cluster notehead black\n119132 musical symbol breve\n119133 musical symbol whole note\n119134 musical symbol half note\n119135 musical symbol quarter note\n119136 musical symbol eighth note\n119137 musical symbol sixteenth note\n119138 musical symbol thirty-second note\n119139 musical symbol sixty-fourth note\n119140 musical symbol one hundred twenty-eighth note\n119141 musical symbol combining stem\n119142 musical symbol combining sprechgesang stem\n119143 musical symbol combining tremolo-1\n119144 musical symbol combining tremolo-2\n119145 musical symbol combining tremolo-3\n119146 musical symbol fingered tremolo-1\n119147 musical symbol fingered tremolo-2\n119148 musical symbol fingered tremolo-3\n119149 musical symbol combining augmentation dot\n119150 musical symbol combining flag-1\n119151 musical symbol combining flag-2\n119152 musical symbol combining flag-3\n119153 musical symbol combining flag-4\n119154 musical symbol combining flag-5\n119155 musical symbol begin beam\n119156 musical symbol end beam\n119157 musical symbol begin tie\n119158 musical symbol end tie\n119159 musical symbol begin slur\n119160 musical symbol end slur\n119161 musical symbol begin phrase\n119162 musical symbol end phrase\n119163 musical symbol combining accent\n119164 musical symbol combining staccato\n119165 musical symbol combining tenuto\n119166 musical symbol combining staccatissimo\n119167 musical symbol combining marcato\n119168 musical symbol combining marcato-staccato\n119169 musical symbol combining accent-staccato\n119170 musical symbol combining loure\n119171 musical symbol arpeggiato up\n119172 musical symbol arpeggiato down\n119173 musical symbol combining doit\n119174 musical symbol combining rip\n119175 musical symbol combining flip\n119176 musical symbol combining smear\n119177 musical symbol combining bend\n119178 musical symbol combining double tongue\n119179 musical symbol combining triple tongue\n119180 musical symbol rinforzando\n119181 musical symbol subito\n119182 musical symbol z\n119183 musical symbol piano\n119184 musical symbol mezzo\n119185 musical symbol forte\n119186 musical symbol crescendo\n119187 musical symbol decrescendo\n119188 musical symbol grace note slash\n119189 musical symbol grace note no slash\n119190 musical symbol tr\n119191 musical symbol turn\n119192 musical symbol inverted turn\n119193 musical symbol turn slash\n119194 musical symbol turn up\n119195 musical symbol ornament stroke-1\n119196 musical symbol ornament stroke-2\n119197 musical symbol ornament stroke-3\n119198 musical symbol ornament stroke-4\n119199 musical symbol ornament stroke-5\n119200 musical symbol ornament stroke-6\n119201 musical symbol ornament stroke-7\n119202 musical symbol ornament stroke-8\n119203 musical symbol ornament stroke-9\n119204 musical symbol ornament stroke-10\n119205 musical symbol ornament stroke-11\n119206 musical symbol hauptstimme\n119207 musical symbol nebenstimme\n119208 musical symbol end of stimme\n119209 musical symbol degree slash\n119210 musical symbol combining down bow\n119211 musical symbol combining up bow\n119212 musical symbol combining harmonic\n119213 musical symbol combining snap pizzicato\n119214 musical symbol pedal mark\n119215 musical symbol pedal up mark\n119216 musical symbol half pedal mark\n119217 musical symbol glissando up\n119218 musical symbol glissando down\n119219 musical symbol with fingernails\n119220 musical symbol damp\n119221 musical symbol damp all\n119222 musical symbol maxima\n119223 musical symbol longa\n119224 musical symbol brevis\n119225 musical symbol semibrevis white\n119226 musical symbol semibrevis black\n119227 musical symbol minima\n119228 musical symbol minima black\n119229 musical symbol semiminima white\n119230 musical symbol semiminima black\n119231 musical symbol fusa white\n119232 musical symbol fusa black\n119233 musical symbol longa perfecta rest\n119234 musical symbol longa imperfecta rest\n119235 musical symbol brevis rest\n119236 musical symbol semibrevis rest\n119237 musical symbol minima rest\n119238 musical symbol semiminima rest\n119239 musical symbol tempus perfectum cum prolatione perfecta\n119240 musical symbol tempus perfectum cum prolatione imperfecta\n119241 musical symbol tempus perfectum cum prolatione perfecta diminution-1\n119242 musical symbol tempus imperfectum cum prolatione perfecta\n119243 musical symbol tempus imperfectum cum prolatione imperfecta\n119244 musical symbol tempus imperfectum cum prolatione imperfecta diminution-1\n119245 musical symbol tempus imperfectum cum prolatione imperfecta diminution-2\n119246 musical symbol tempus imperfectum cum prolatione imperfecta diminution-3\n119247 musical symbol croix\n119248 musical symbol gregorian c clef\n119249 musical symbol gregorian f clef\n119250 musical symbol square b\n119251 musical symbol virga\n119252 musical symbol podatus\n119253 musical symbol clivis\n119254 musical symbol scandicus\n119255 musical symbol climacus\n119256 musical symbol torculus\n119257 musical symbol porrectus\n119258 musical symbol porrectus flexus\n119259 musical symbol scandicus flexus\n119260 musical symbol torculus resupinus\n119261 musical symbol pes subpunctis\n119262 musical symbol kievan c clef\n119263 musical symbol kievan end of piece\n119264 musical symbol kievan final note\n119265 musical symbol kievan recitative mark\n119266 musical symbol kievan whole note\n119267 musical symbol kievan half note\n119268 musical symbol kievan quarter note stem down\n119269 musical symbol kievan quarter note stem up\n119270 musical symbol kievan eighth note stem down\n119271 musical symbol kievan eighth note stem up\n119272 musical symbol kievan flat sign\n119273 musical symbol sori\n119274 musical symbol koron\n119296 greek vocal notation symbol-1\n119297 greek vocal notation symbol-2\n119298 greek vocal notation symbol-3\n119299 greek vocal notation symbol-4\n119300 greek vocal notation symbol-5\n119301 greek vocal notation symbol-6\n119302 greek vocal notation symbol-7\n119303 greek vocal notation symbol-8\n119304 greek vocal notation symbol-9\n119305 greek vocal notation symbol-10\n119306 greek vocal notation symbol-11\n119307 greek vocal notation symbol-12\n119308 greek vocal notation symbol-13\n119309 greek vocal notation symbol-14\n119310 greek vocal notation symbol-15\n119311 greek vocal notation symbol-16\n119312 greek vocal notation symbol-17\n119313 greek vocal notation symbol-18\n119314 greek vocal notation symbol-19\n119315 greek vocal notation symbol-20\n119316 greek vocal notation symbol-21\n119317 greek vocal notation symbol-22\n119318 greek vocal notation symbol-23\n119319 greek vocal notation symbol-24\n119320 greek vocal notation symbol-50\n119321 greek vocal notation symbol-51\n119322 greek vocal notation symbol-52\n119323 greek vocal notation symbol-53\n119324 greek vocal notation symbol-54\n119325 greek instrumental notation symbol-1\n119326 greek instrumental notation symbol-2\n119327 greek instrumental notation symbol-4\n119328 greek instrumental notation symbol-5\n119329 greek instrumental notation symbol-7\n119330 greek instrumental notation symbol-8\n119331 greek instrumental notation symbol-11\n119332 greek instrumental notation symbol-12\n119333 greek instrumental notation symbol-13\n119334 greek instrumental notation symbol-14\n119335 greek instrumental notation symbol-17\n119336 greek instrumental notation symbol-18\n119337 greek instrumental notation symbol-19\n119338 greek instrumental notation symbol-23\n119339 greek instrumental notation symbol-24\n119340 greek instrumental notation symbol-25\n119341 greek instrumental notation symbol-26\n119342 greek instrumental notation symbol-27\n119343 greek instrumental notation symbol-29\n119344 greek instrumental notation symbol-30\n119345 greek instrumental notation symbol-32\n119346 greek instrumental notation symbol-36\n119347 greek instrumental notation symbol-37\n119348 greek instrumental notation symbol-38\n119349 greek instrumental notation symbol-39\n119350 greek instrumental notation symbol-40\n119351 greek instrumental notation symbol-42\n119352 greek instrumental notation symbol-43\n119353 greek instrumental notation symbol-45\n119354 greek instrumental notation symbol-47\n119355 greek instrumental notation symbol-48\n119356 greek instrumental notation symbol-49\n119357 greek instrumental notation symbol-50\n119358 greek instrumental notation symbol-51\n119359 greek instrumental notation symbol-52\n119360 greek instrumental notation symbol-53\n119361 greek instrumental notation symbol-54\n119362 combining greek musical triseme\n119363 combining greek musical tetraseme\n119364 combining greek musical pentaseme\n119365 greek musical leimma\n119488 kaktovik numeral zero\n119489 kaktovik numeral one\n119490 kaktovik numeral two\n119491 kaktovik numeral three\n119492 kaktovik numeral four\n119493 kaktovik numeral five\n119494 kaktovik numeral six\n119495 kaktovik numeral seven\n119496 kaktovik numeral eight\n119497 kaktovik numeral nine\n119498 kaktovik numeral ten\n119499 kaktovik numeral eleven\n119500 kaktovik numeral twelve\n119501 kaktovik numeral thirteen\n119502 kaktovik numeral fourteen\n119503 kaktovik numeral fifteen\n119504 kaktovik numeral sixteen\n119505 kaktovik numeral seventeen\n119506 kaktovik numeral eighteen\n119507 kaktovik numeral nineteen\n119520 mayan numeral zero\n119521 mayan numeral one\n119522 mayan numeral two\n119523 mayan numeral three\n119524 mayan numeral four\n119525 mayan numeral five\n119526 mayan numeral six\n119527 mayan numeral seven\n119528 mayan numeral eight\n119529 mayan numeral nine\n119530 mayan numeral ten\n119531 mayan numeral eleven\n119532 mayan numeral twelve\n119533 mayan numeral thirteen\n119534 mayan numeral fourteen\n119535 mayan numeral fifteen\n119536 mayan numeral sixteen\n119537 mayan numeral seventeen\n119538 mayan numeral eighteen\n119539 mayan numeral nineteen\n119552 monogram for earth\n119553 digram for heavenly earth\n119554 digram for human earth\n119555 digram for earthly heaven\n119556 digram for earthly human\n119557 digram for earth\n119558 tetragram for centre\n119559 tetragram for full circle\n119560 tetragram for mired\n119561 tetragram for barrier\n119562 tetragram for keeping small\n119563 tetragram for contrariety\n119564 tetragram for ascent\n119565 tetragram for opposition\n119566 tetragram for branching out\n119567 tetragram for defectiveness or distortion\n119568 tetragram for divergence\n119569 tetragram for youthfulness\n119570 tetragram for increase\n119571 tetragram for penetration\n119572 tetragram for reach\n119573 tetragram for contact\n119574 tetragram for holding back\n119575 tetragram for waiting\n119576 tetragram for following\n119577 tetragram for advance\n119578 tetragram for release\n119579 tetragram for resistance\n119580 tetragram for ease\n119581 tetragram for joy\n119582 tetragram for contention\n119583 tetragram for endeavour\n119584 tetragram for duties\n119585 tetragram for change\n119586 tetragram for decisiveness\n119587 tetragram for bold resolution\n119588 tetragram for packing\n119589 tetragram for legion\n119590 tetragram for closeness\n119591 tetragram for kinship\n119592 tetragram for gathering\n119593 tetragram for strength\n119594 tetragram for purity\n119595 tetragram for fullness\n119596 tetragram for residence\n119597 tetragram for law or model\n119598 tetragram for response\n119599 tetragram for going to meet\n119600 tetragram for encounters\n119601 tetragram for stove\n119602 tetragram for greatness\n119603 tetragram for enlargement\n119604 tetragram for pattern\n119605 tetragram for ritual\n119606 tetragram for flight\n119607 tetragram for vastness or wasting\n119608 tetragram for constancy\n119609 tetragram for measure\n119610 tetragram for eternity\n119611 tetragram for unity\n119612 tetragram for diminishment\n119613 tetragram for closed mouth\n119614 tetragram for guardedness\n119615 tetragram for gathering in\n119616 tetragram for massing\n119617 tetragram for accumulation\n119618 tetragram for embellishment\n119619 tetragram for doubt\n119620 tetragram for watch\n119621 tetragram for sinking\n119622 tetragram for inner\n119623 tetragram for departure\n119624 tetragram for darkening\n119625 tetragram for dimming\n119626 tetragram for exhaustion\n119627 tetragram for severance\n119628 tetragram for stoppage\n119629 tetragram for hardness\n119630 tetragram for completion\n119631 tetragram for closure\n119632 tetragram for failure\n119633 tetragram for aggravation\n119634 tetragram for compliance\n119635 tetragram for on the verge\n119636 tetragram for difficulties\n119637 tetragram for labouring\n119638 tetragram for fostering\n119648 counting rod unit digit one\n119649 counting rod unit digit two\n119650 counting rod unit digit three\n119651 counting rod unit digit four\n119652 counting rod unit digit five\n119653 counting rod unit digit six\n119654 counting rod unit digit seven\n119655 counting rod unit digit eight\n119656 counting rod unit digit nine\n119657 counting rod tens digit one\n119658 counting rod tens digit two\n119659 counting rod tens digit three\n119660 counting rod tens digit four\n119661 counting rod tens digit five\n119662 counting rod tens digit six\n119663 counting rod tens digit seven\n119664 counting rod tens digit eight\n119665 counting rod tens digit nine\n119666 ideographic tally mark one\n119667 ideographic tally mark two\n119668 ideographic tally mark three\n119669 ideographic tally mark four\n119670 ideographic tally mark five\n119671 tally mark one\n119672 tally mark five\n119808 mathematical bold capital a\n119809 mathematical bold capital b\n119810 mathematical bold capital c\n119811 mathematical bold capital d\n119812 mathematical bold capital e\n119813 mathematical bold capital f\n119814 mathematical bold capital g\n119815 mathematical bold capital h\n119816 mathematical bold capital i\n119817 mathematical bold capital j\n119818 mathematical bold capital k\n119819 mathematical bold capital l\n119820 mathematical bold capital m\n119821 mathematical bold capital n\n119822 mathematical bold capital o\n119823 mathematical bold capital p\n119824 mathematical bold capital q\n119825 mathematical bold capital r\n119826 mathematical bold capital s\n119827 mathematical bold capital t\n119828 mathematical bold capital u\n119829 mathematical bold capital v\n119830 mathematical bold capital w\n119831 mathematical bold capital x\n119832 mathematical bold capital y\n119833 mathematical bold capital z\n119834 mathematical bold small a\n119835 mathematical bold small b\n119836 mathematical bold small c\n119837 mathematical bold small d\n119838 mathematical bold small e\n119839 mathematical bold small f\n119840 mathematical bold small g\n119841 mathematical bold small h\n119842 mathematical bold small i\n119843 mathematical bold small j\n119844 mathematical bold small k\n119845 mathematical bold small l\n119846 mathematical bold small m\n119847 mathematical bold small n\n119848 mathematical bold small o\n119849 mathematical bold small p\n119850 mathematical bold small q\n119851 mathematical bold small r\n119852 mathematical bold small s\n119853 mathematical bold small t\n119854 mathematical bold small u\n119855 mathematical bold small v\n119856 mathematical bold small w\n119857 mathematical bold small x\n119858 mathematical bold small y\n119859 mathematical bold small z\n119860 mathematical italic capital a\n119861 mathematical italic capital b\n119862 mathematical italic capital c\n119863 mathematical italic capital d\n119864 mathematical italic capital e\n119865 mathematical italic capital f\n119866 mathematical italic capital g\n119867 mathematical italic capital h\n119868 mathematical italic capital i\n119869 mathematical italic capital j\n119870 mathematical italic capital k\n119871 mathematical italic capital l\n119872 mathematical italic capital m\n119873 mathematical italic capital n\n119874 mathematical italic capital o\n119875 mathematical italic capital p\n119876 mathematical italic capital q\n119877 mathematical italic capital r\n119878 mathematical italic capital s\n119879 mathematical italic capital t\n119880 mathematical italic capital u\n119881 mathematical italic capital v\n119882 mathematical italic capital w\n119883 mathematical italic capital x\n119884 mathematical italic capital y\n119885 mathematical italic capital z\n119886 mathematical italic small a\n119887 mathematical italic small b\n119888 mathematical italic small c\n119889 mathematical italic small d\n119890 mathematical italic small e\n119891 mathematical italic small f\n119892 mathematical italic small g\n119894 mathematical italic small i\n119895 mathematical italic small j\n119896 mathematical italic small k\n119897 mathematical italic small l\n119898 mathematical italic small m\n119899 mathematical italic small n\n119900 mathematical italic small o\n119901 mathematical italic small p\n119902 mathematical italic small q\n119903 mathematical italic small r\n119904 mathematical italic small s\n119905 mathematical italic small t\n119906 mathematical italic small u\n119907 mathematical italic small v\n119908 mathematical italic small w\n119909 mathematical italic small x\n119910 mathematical italic small y\n119911 mathematical italic small z\n119912 mathematical bold italic capital a\n119913 mathematical bold italic capital b\n119914 mathematical bold italic capital c\n119915 mathematical bold italic capital d\n119916 mathematical bold italic capital e\n119917 mathematical bold italic capital f\n119918 mathematical bold italic capital g\n119919 mathematical bold italic capital h\n119920 mathematical bold italic capital i\n119921 mathematical bold italic capital j\n119922 mathematical bold italic capital k\n119923 mathematical bold italic capital l\n119924 mathematical bold italic capital m\n119925 mathematical bold italic capital n\n119926 mathematical bold italic capital o\n119927 mathematical bold italic capital p\n119928 mathematical bold italic capital q\n119929 mathematical bold italic capital r\n119930 mathematical bold italic capital s\n119931 mathematical bold italic capital t\n119932 mathematical bold italic capital u\n119933 mathematical bold italic capital v\n119934 mathematical bold italic capital w\n119935 mathematical bold italic capital x\n119936 mathematical bold italic capital y\n119937 mathematical bold italic capital z\n119938 mathematical bold italic small a\n119939 mathematical bold italic small b\n119940 mathematical bold italic small c\n119941 mathematical bold italic small d\n119942 mathematical bold italic small e\n119943 mathematical bold italic small f\n119944 mathematical bold italic small g\n119945 mathematical bold italic small h\n119946 mathematical bold italic small i\n119947 mathematical bold italic small j\n119948 mathematical bold italic small k\n119949 mathematical bold italic small l\n119950 mathematical bold italic small m\n119951 mathematical bold italic small n\n119952 mathematical bold italic small o\n119953 mathematical bold italic small p\n119954 mathematical bold italic small q\n119955 mathematical bold italic small r\n119956 mathematical bold italic small s\n119957 mathematical bold italic small t\n119958 mathematical bold italic small u\n119959 mathematical bold italic small v\n119960 mathematical bold italic small w\n119961 mathematical bold italic small x\n119962 mathematical bold italic small y\n119963 mathematical bold italic small z\n119964 mathematical script capital a\tascr\n119966 mathematical script capital c\tcscr\n119967 mathematical script capital d\tdscr\n119970 mathematical script capital g\tgscr\n119973 mathematical script capital j\tjscr\n119974 mathematical script capital k\tkscr\n119977 mathematical script capital n\tnscr\n119978 mathematical script capital o\toscr\n119979 mathematical script capital p\tpscr\n119980 mathematical script capital q\tqscr\n119982 mathematical script capital s\tsscr\n119983 mathematical script capital t\ttscr\n119984 mathematical script capital u\tuscr\n119985 mathematical script capital v\tvscr\n119986 mathematical script capital w\twscr\n119987 mathematical script capital x\txscr\n119988 mathematical script capital y\tyscr\n119989 mathematical script capital z\tzscr\n119990 mathematical script small a\tascr\n119991 mathematical script small b\tbscr\n119992 mathematical script small c\tcscr\n119993 mathematical script small d\tdscr\n119995 mathematical script small f\tfscr\n119997 mathematical script small h\thscr\n119998 mathematical script small i\tiscr\n119999 mathematical script small j\tjscr\n120000 mathematical script small k\tkscr\n120001 mathematical script small l\tlscr\n120002 mathematical script small m\tmscr\n120003 mathematical script small n\tnscr\n120005 mathematical script small p\tpscr\n120006 mathematical script small q\tqscr\n120007 mathematical script small r\trscr\n120008 mathematical script small s\tsscr\n120009 mathematical script small t\ttscr\n120010 mathematical script small u\tuscr\n120011 mathematical script small v\tvscr\n120012 mathematical script small w\twscr\n120013 mathematical script small x\txscr\n120014 mathematical script small y\tyscr\n120015 mathematical script small z\tzscr\n120016 mathematical bold script capital a\n120017 mathematical bold script capital b\n120018 mathematical bold script capital c\n120019 mathematical bold script capital d\n120020 mathematical bold script capital e\n120021 mathematical bold script capital f\n120022 mathematical bold script capital g\n120023 mathematical bold script capital h\n120024 mathematical bold script capital i\n120025 mathematical bold script capital j\n120026 mathematical bold script capital k\n120027 mathematical bold script capital l\n120028 mathematical bold script capital m\n120029 mathematical bold script capital n\n120030 mathematical bold script capital o\n120031 mathematical bold script capital p\n120032 mathematical bold script capital q\n120033 mathematical bold script capital r\n120034 mathematical bold script capital s\n120035 mathematical bold script capital t\n120036 mathematical bold script capital u\n120037 mathematical bold script capital v\n120038 mathematical bold script capital w\n120039 mathematical bold script capital x\n120040 mathematical bold script capital y\n120041 mathematical bold script capital z\n120042 mathematical bold script small a\n120043 mathematical bold script small b\n120044 mathematical bold script small c\n120045 mathematical bold script small d\n120046 mathematical bold script small e\n120047 mathematical bold script small f\n120048 mathematical bold script small g\n120049 mathematical bold script small h\n120050 mathematical bold script small i\n120051 mathematical bold script small j\n120052 mathematical bold script small k\n120053 mathematical bold script small l\n120054 mathematical bold script small m\n120055 mathematical bold script small n\n120056 mathematical bold script small o\n120057 mathematical bold script small p\n120058 mathematical bold script small q\n120059 mathematical bold script small r\n120060 mathematical bold script small s\n120061 mathematical bold script small t\n120062 mathematical bold script small u\n120063 mathematical bold script small v\n120064 mathematical bold script small w\n120065 mathematical bold script small x\n120066 mathematical bold script small y\n120067 mathematical bold script small z\n120068 mathematical fraktur capital a\tafr\n120069 mathematical fraktur capital b\tbfr\n120071 mathematical fraktur capital d\tdfr\n120072 mathematical fraktur capital e\tefr\n120073 mathematical fraktur capital f\tffr\n120074 mathematical fraktur capital g\tgfr\n120077 mathematical fraktur capital j\tjfr\n120078 mathematical fraktur capital k\tkfr\n120079 mathematical fraktur capital l\tlfr\n120080 mathematical fraktur capital m\tmfr\n120081 mathematical fraktur capital n\tnfr\n120082 mathematical fraktur capital o\tofr\n120083 mathematical fraktur capital p\tpfr\n120084 mathematical fraktur capital q\tqfr\n120086 mathematical fraktur capital s\tsfr\n120087 mathematical fraktur capital t\ttfr\n120088 mathematical fraktur capital u\tufr\n120089 mathematical fraktur capital v\tvfr\n120090 mathematical fraktur capital w\twfr\n120091 mathematical fraktur capital x\txfr\n120092 mathematical fraktur capital y\tyfr\n120094 mathematical fraktur small a\tafr\n120095 mathematical fraktur small b\tbfr\n120096 mathematical fraktur small c\tcfr\n120097 mathematical fraktur small d\tdfr\n120098 mathematical fraktur small e\tefr\n120099 mathematical fraktur small f\tffr\n120100 mathematical fraktur small g\tgfr\n120101 mathematical fraktur small h\thfr\n120102 mathematical fraktur small i\tifr\n120103 mathematical fraktur small j\tjfr\n120104 mathematical fraktur small k\tkfr\n120105 mathematical fraktur small l\tlfr\n120106 mathematical fraktur small m\tmfr\n120107 mathematical fraktur small n\tnfr\n120108 mathematical fraktur small o\tofr\n120109 mathematical fraktur small p\tpfr\n120110 mathematical fraktur small q\tqfr\n120111 mathematical fraktur small r\trfr\n120112 mathematical fraktur small s\tsfr\n120113 mathematical fraktur small t\ttfr\n120114 mathematical fraktur small u\tufr\n120115 mathematical fraktur small v\tvfr\n120116 mathematical fraktur small w\twfr\n120117 mathematical fraktur small x\txfr\n120118 mathematical fraktur small y\tyfr\n120119 mathematical fraktur small z\tzfr\n120120 mathematical double-struck capital a\taopf\n120121 mathematical double-struck capital b\tbopf\n120123 mathematical double-struck capital d\tdopf\n120124 mathematical double-struck capital e\teopf\n120125 mathematical double-struck capital f\tfopf\n120126 mathematical double-struck capital g\tgopf\n120128 mathematical double-struck capital i\tiopf\n120129 mathematical double-struck capital j\tjopf\n120130 mathematical double-struck capital k\tkopf\n120131 mathematical double-struck capital l\tlopf\n120132 mathematical double-struck capital m\tmopf\n120134 mathematical double-struck capital o\toopf\n120138 mathematical double-struck capital s\tsopf\n120139 mathematical double-struck capital t\ttopf\n120140 mathematical double-struck capital u\tuopf\n120141 mathematical double-struck capital v\tvopf\n120142 mathematical double-struck capital w\twopf\n120143 mathematical double-struck capital x\txopf\n120144 mathematical double-struck capital y\tyopf\n120146 mathematical double-struck small a\taopf\n120147 mathematical double-struck small b\tbopf\n120148 mathematical double-struck small c\tcopf\n120149 mathematical double-struck small d\tdopf\n120150 mathematical double-struck small e\teopf\n120151 mathematical double-struck small f\tfopf\n120152 mathematical double-struck small g\tgopf\n120153 mathematical double-struck small h\thopf\n120154 mathematical double-struck small i\tiopf\n120155 mathematical double-struck small j\tjopf\n120156 mathematical double-struck small k\tkopf\n120157 mathematical double-struck small l\tlopf\n120158 mathematical double-struck small m\tmopf\n120159 mathematical double-struck small n\tnopf\n120160 mathematical double-struck small o\toopf\n120161 mathematical double-struck small p\tpopf\n120162 mathematical double-struck small q\tqopf\n120163 mathematical double-struck small r\tropf\n120164 mathematical double-struck small s\tsopf\n120165 mathematical double-struck small t\ttopf\n120166 mathematical double-struck small u\tuopf\n120167 mathematical double-struck small v\tvopf\n120168 mathematical double-struck small w\twopf\n120169 mathematical double-struck small x\txopf\n120170 mathematical double-struck small y\tyopf\n120171 mathematical double-struck small z\tzopf\n120172 mathematical bold fraktur capital a\n120173 mathematical bold fraktur capital b\n120174 mathematical bold fraktur capital c\n120175 mathematical bold fraktur capital d\n120176 mathematical bold fraktur capital e\n120177 mathematical bold fraktur capital f\n120178 mathematical bold fraktur capital g\n120179 mathematical bold fraktur capital h\n120180 mathematical bold fraktur capital i\n120181 mathematical bold fraktur capital j\n120182 mathematical bold fraktur capital k\n120183 mathematical bold fraktur capital l\n120184 mathematical bold fraktur capital m\n120185 mathematical bold fraktur capital n\n120186 mathematical bold fraktur capital o\n120187 mathematical bold fraktur capital p\n120188 mathematical bold fraktur capital q\n120189 mathematical bold fraktur capital r\n120190 mathematical bold fraktur capital s\n120191 mathematical bold fraktur capital t\n120192 mathematical bold fraktur capital u\n120193 mathematical bold fraktur capital v\n120194 mathematical bold fraktur capital w\n120195 mathematical bold fraktur capital x\n120196 mathematical bold fraktur capital y\n120197 mathematical bold fraktur capital z\n120198 mathematical bold fraktur small a\n120199 mathematical bold fraktur small b\n120200 mathematical bold fraktur small c\n120201 mathematical bold fraktur small d\n120202 mathematical bold fraktur small e\n120203 mathematical bold fraktur small f\n120204 mathematical bold fraktur small g\n120205 mathematical bold fraktur small h\n120206 mathematical bold fraktur small i\n120207 mathematical bold fraktur small j\n120208 mathematical bold fraktur small k\n120209 mathematical bold fraktur small l\n120210 mathematical bold fraktur small m\n120211 mathematical bold fraktur small n\n120212 mathematical bold fraktur small o\n120213 mathematical bold fraktur small p\n120214 mathematical bold fraktur small q\n120215 mathematical bold fraktur small r\n120216 mathematical bold fraktur small s\n120217 mathematical bold fraktur small t\n120218 mathematical bold fraktur small u\n120219 mathematical bold fraktur small v\n120220 mathematical bold fraktur small w\n120221 mathematical bold fraktur small x\n120222 mathematical bold fraktur small y\n120223 mathematical bold fraktur small z\n120224 mathematical sans-serif capital a\n120225 mathematical sans-serif capital b\n120226 mathematical sans-serif capital c\n120227 mathematical sans-serif capital d\n120228 mathematical sans-serif capital e\n120229 mathematical sans-serif capital f\n120230 mathematical sans-serif capital g\n120231 mathematical sans-serif capital h\n120232 mathematical sans-serif capital i\n120233 mathematical sans-serif capital j\n120234 mathematical sans-serif capital k\n120235 mathematical sans-serif capital l\n120236 mathematical sans-serif capital m\n120237 mathematical sans-serif capital n\n120238 mathematical sans-serif capital o\n120239 mathematical sans-serif capital p\n120240 mathematical sans-serif capital q\n120241 mathematical sans-serif capital r\n120242 mathematical sans-serif capital s\n120243 mathematical sans-serif capital t\n120244 mathematical sans-serif capital u\n120245 mathematical sans-serif capital v\n120246 mathematical sans-serif capital w\n120247 mathematical sans-serif capital x\n120248 mathematical sans-serif capital y\n120249 mathematical sans-serif capital z\n120250 mathematical sans-serif small a\n120251 mathematical sans-serif small b\n120252 mathematical sans-serif small c\n120253 mathematical sans-serif small d\n120254 mathematical sans-serif small e\n120255 mathematical sans-serif small f\n120256 mathematical sans-serif small g\n120257 mathematical sans-serif small h\n120258 mathematical sans-serif small i\n120259 mathematical sans-serif small j\n120260 mathematical sans-serif small k\n120261 mathematical sans-serif small l\n120262 mathematical sans-serif small m\n120263 mathematical sans-serif small n\n120264 mathematical sans-serif small o\n120265 mathematical sans-serif small p\n120266 mathematical sans-serif small q\n120267 mathematical sans-serif small r\n120268 mathematical sans-serif small s\n120269 mathematical sans-serif small t\n120270 mathematical sans-serif small u\n120271 mathematical sans-serif small v\n120272 mathematical sans-serif small w\n120273 mathematical sans-serif small x\n120274 mathematical sans-serif small y\n120275 mathematical sans-serif small z\n120276 mathematical sans-serif bold capital a\n120277 mathematical sans-serif bold capital b\n120278 mathematical sans-serif bold capital c\n120279 mathematical sans-serif bold capital d\n120280 mathematical sans-serif bold capital e\n120281 mathematical sans-serif bold capital f\n120282 mathematical sans-serif bold capital g\n120283 mathematical sans-serif bold capital h\n120284 mathematical sans-serif bold capital i\n120285 mathematical sans-serif bold capital j\n120286 mathematical sans-serif bold capital k\n120287 mathematical sans-serif bold capital l\n120288 mathematical sans-serif bold capital m\n120289 mathematical sans-serif bold capital n\n120290 mathematical sans-serif bold capital o\n120291 mathematical sans-serif bold capital p\n120292 mathematical sans-serif bold capital q\n120293 mathematical sans-serif bold capital r\n120294 mathematical sans-serif bold capital s\n120295 mathematical sans-serif bold capital t\n120296 mathematical sans-serif bold capital u\n120297 mathematical sans-serif bold capital v\n120298 mathematical sans-serif bold capital w\n120299 mathematical sans-serif bold capital x\n120300 mathematical sans-serif bold capital y\n120301 mathematical sans-serif bold capital z\n120302 mathematical sans-serif bold small a\n120303 mathematical sans-serif bold small b\n120304 mathematical sans-serif bold small c\n120305 mathematical sans-serif bold small d\n120306 mathematical sans-serif bold small e\n120307 mathematical sans-serif bold small f\n120308 mathematical sans-serif bold small g\n120309 mathematical sans-serif bold small h\n120310 mathematical sans-serif bold small i\n120311 mathematical sans-serif bold small j\n120312 mathematical sans-serif bold small k\n120313 mathematical sans-serif bold small l\n120314 mathematical sans-serif bold small m\n120315 mathematical sans-serif bold small n\n120316 mathematical sans-serif bold small o\n120317 mathematical sans-serif bold small p\n120318 mathematical sans-serif bold small q\n120319 mathematical sans-serif bold small r\n120320 mathematical sans-serif bold small s\n120321 mathematical sans-serif bold small t\n120322 mathematical sans-serif bold small u\n120323 mathematical sans-serif bold small v\n120324 mathematical sans-serif bold small w\n120325 mathematical sans-serif bold small x\n120326 mathematical sans-serif bold small y\n120327 mathematical sans-serif bold small z\n120328 mathematical sans-serif italic capital a\n120329 mathematical sans-serif italic capital b\n120330 mathematical sans-serif italic capital c\n120331 mathematical sans-serif italic capital d\n120332 mathematical sans-serif italic capital e\n120333 mathematical sans-serif italic capital f\n120334 mathematical sans-serif italic capital g\n120335 mathematical sans-serif italic capital h\n120336 mathematical sans-serif italic capital i\n120337 mathematical sans-serif italic capital j\n120338 mathematical sans-serif italic capital k\n120339 mathematical sans-serif italic capital l\n120340 mathematical sans-serif italic capital m\n120341 mathematical sans-serif italic capital n\n120342 mathematical sans-serif italic capital o\n120343 mathematical sans-serif italic capital p\n120344 mathematical sans-serif italic capital q\n120345 mathematical sans-serif italic capital r\n120346 mathematical sans-serif italic capital s\n120347 mathematical sans-serif italic capital t\n120348 mathematical sans-serif italic capital u\n120349 mathematical sans-serif italic capital v\n120350 mathematical sans-serif italic capital w\n120351 mathematical sans-serif italic capital x\n120352 mathematical sans-serif italic capital y\n120353 mathematical sans-serif italic capital z\n120354 mathematical sans-serif italic small a\n120355 mathematical sans-serif italic small b\n120356 mathematical sans-serif italic small c\n120357 mathematical sans-serif italic small d\n120358 mathematical sans-serif italic small e\n120359 mathematical sans-serif italic small f\n120360 mathematical sans-serif italic small g\n120361 mathematical sans-serif italic small h\n120362 mathematical sans-serif italic small i\n120363 mathematical sans-serif italic small j\n120364 mathematical sans-serif italic small k\n120365 mathematical sans-serif italic small l\n120366 mathematical sans-serif italic small m\n120367 mathematical sans-serif italic small n\n120368 mathematical sans-serif italic small o\n120369 mathematical sans-serif italic small p\n120370 mathematical sans-serif italic small q\n120371 mathematical sans-serif italic small r\n120372 mathematical sans-serif italic small s\n120373 mathematical sans-serif italic small t\n120374 mathematical sans-serif italic small u\n120375 mathematical sans-serif italic small v\n120376 mathematical sans-serif italic small w\n120377 mathematical sans-serif italic small x\n120378 mathematical sans-serif italic small y\n120379 mathematical sans-serif italic small z\n120380 mathematical sans-serif bold italic capital a\n120381 mathematical sans-serif bold italic capital b\n120382 mathematical sans-serif bold italic capital c\n120383 mathematical sans-serif bold italic capital d\n120384 mathematical sans-serif bold italic capital e\n120385 mathematical sans-serif bold italic capital f\n120386 mathematical sans-serif bold italic capital g\n120387 mathematical sans-serif bold italic capital h\n120388 mathematical sans-serif bold italic capital i\n120389 mathematical sans-serif bold italic capital j\n120390 mathematical sans-serif bold italic capital k\n120391 mathematical sans-serif bold italic capital l\n120392 mathematical sans-serif bold italic capital m\n120393 mathematical sans-serif bold italic capital n\n120394 mathematical sans-serif bold italic capital o\n120395 mathematical sans-serif bold italic capital p\n120396 mathematical sans-serif bold italic capital q\n120397 mathematical sans-serif bold italic capital r\n120398 mathematical sans-serif bold italic capital s\n120399 mathematical sans-serif bold italic capital t\n120400 mathematical sans-serif bold italic capital u\n120401 mathematical sans-serif bold italic capital v\n120402 mathematical sans-serif bold italic capital w\n120403 mathematical sans-serif bold italic capital x\n120404 mathematical sans-serif bold italic capital y\n120405 mathematical sans-serif bold italic capital z\n120406 mathematical sans-serif bold italic small a\n120407 mathematical sans-serif bold italic small b\n120408 mathematical sans-serif bold italic small c\n120409 mathematical sans-serif bold italic small d\n120410 mathematical sans-serif bold italic small e\n120411 mathematical sans-serif bold italic small f\n120412 mathematical sans-serif bold italic small g\n120413 mathematical sans-serif bold italic small h\n120414 mathematical sans-serif bold italic small i\n120415 mathematical sans-serif bold italic small j\n120416 mathematical sans-serif bold italic small k\n120417 mathematical sans-serif bold italic small l\n120418 mathematical sans-serif bold italic small m\n120419 mathematical sans-serif bold italic small n\n120420 mathematical sans-serif bold italic small o\n120421 mathematical sans-serif bold italic small p\n120422 mathematical sans-serif bold italic small q\n120423 mathematical sans-serif bold italic small r\n120424 mathematical sans-serif bold italic small s\n120425 mathematical sans-serif bold italic small t\n120426 mathematical sans-serif bold italic small u\n120427 mathematical sans-serif bold italic small v\n120428 mathematical sans-serif bold italic small w\n120429 mathematical sans-serif bold italic small x\n120430 mathematical sans-serif bold italic small y\n120431 mathematical sans-serif bold italic small z\n120432 mathematical monospace capital a\n120433 mathematical monospace capital b\n120434 mathematical monospace capital c\n120435 mathematical monospace capital d\n120436 mathematical monospace capital e\n120437 mathematical monospace capital f\n120438 mathematical monospace capital g\n120439 mathematical monospace capital h\n120440 mathematical monospace capital i\n120441 mathematical monospace capital j\n120442 mathematical monospace capital k\n120443 mathematical monospace capital l\n120444 mathematical monospace capital m\n120445 mathematical monospace capital n\n120446 mathematical monospace capital o\n120447 mathematical monospace capital p\n120448 mathematical monospace capital q\n120449 mathematical monospace capital r\n120450 mathematical monospace capital s\n120451 mathematical monospace capital t\n120452 mathematical monospace capital u\n120453 mathematical monospace capital v\n120454 mathematical monospace capital w\n120455 mathematical monospace capital x\n120456 mathematical monospace capital y\n120457 mathematical monospace capital z\n120458 mathematical monospace small a\n120459 mathematical monospace small b\n120460 mathematical monospace small c\n120461 mathematical monospace small d\n120462 mathematical monospace small e\n120463 mathematical monospace small f\n120464 mathematical monospace small g\n120465 mathematical monospace small h\n120466 mathematical monospace small i\n120467 mathematical monospace small j\n120468 mathematical monospace small k\n120469 mathematical monospace small l\n120470 mathematical monospace small m\n120471 mathematical monospace small n\n120472 mathematical monospace small o\n120473 mathematical monospace small p\n120474 mathematical monospace small q\n120475 mathematical monospace small r\n120476 mathematical monospace small s\n120477 mathematical monospace small t\n120478 mathematical monospace small u\n120479 mathematical monospace small v\n120480 mathematical monospace small w\n120481 mathematical monospace small x\n120482 mathematical monospace small y\n120483 mathematical monospace small z\n120484 mathematical italic small dotless i\n120485 mathematical italic small dotless j\n120488 mathematical bold capital alpha\n120489 mathematical bold capital beta\n120490 mathematical bold capital gamma\n120491 mathematical bold capital delta\n120492 mathematical bold capital epsilon\n120493 mathematical bold capital zeta\n120494 mathematical bold capital eta\n120495 mathematical bold capital theta\n120496 mathematical bold capital iota\n120497 mathematical bold capital kappa\n120498 mathematical bold capital lamda\tlambda\n120499 mathematical bold capital mu\n120500 mathematical bold capital nu\n120501 mathematical bold capital xi\n120502 mathematical bold capital omicron\n120503 mathematical bold capital pi\n120504 mathematical bold capital rho\n120505 mathematical bold capital theta symbol\n120506 mathematical bold capital sigma\n120507 mathematical bold capital tau\n120508 mathematical bold capital upsilon\n120509 mathematical bold capital phi\n120510 mathematical bold capital chi\n120511 mathematical bold capital psi\n120512 mathematical bold capital omega\n120513 mathematical bold nabla\n120514 mathematical bold small alpha\n120515 mathematical bold small beta\n120516 mathematical bold small gamma\n120517 mathematical bold small delta\n120518 mathematical bold small epsilon\n120519 mathematical bold small zeta\n120520 mathematical bold small eta\n120521 mathematical bold small theta\n120522 mathematical bold small iota\n120523 mathematical bold small kappa\n120524 mathematical bold small lamda\tlambda\n120525 mathematical bold small mu\n120526 mathematical bold small nu\n120527 mathematical bold small xi\n120528 mathematical bold small omicron\n120529 mathematical bold small pi\n120530 mathematical bold small rho\n120531 mathematical bold small final sigma\n120532 mathematical bold small sigma\n120533 mathematical bold small tau\n120534 mathematical bold small upsilon\n120535 mathematical bold small phi\n120536 mathematical bold small chi\n120537 mathematical bold small psi\n120538 mathematical bold small omega\n120539 mathematical bold partial differential\n120540 mathematical bold epsilon symbol\n120541 mathematical bold theta symbol\n120542 mathematical bold kappa symbol\n120543 mathematical bold phi symbol\n120544 mathematical bold rho symbol\n120545 mathematical bold pi symbol\n120546 mathematical italic capital alpha\n120547 mathematical italic capital beta\n120548 mathematical italic capital gamma\n120549 mathematical italic capital delta\n120550 mathematical italic capital epsilon\n120551 mathematical italic capital zeta\n120552 mathematical italic capital eta\n120553 mathematical italic capital theta\n120554 mathematical italic capital iota\n120555 mathematical italic capital kappa\n120556 mathematical italic capital lamda\tlambda\n120557 mathematical italic capital mu\n120558 mathematical italic capital nu\n120559 mathematical italic capital xi\n120560 mathematical italic capital omicron\n120561 mathematical italic capital pi\n120562 mathematical italic capital rho\n120563 mathematical italic capital theta symbol\n120564 mathematical italic capital sigma\n120565 mathematical italic capital tau\n120566 mathematical italic capital upsilon\n120567 mathematical italic capital phi\n120568 mathematical italic capital chi\n120569 mathematical italic capital psi\n120570 mathematical italic capital omega\n120571 mathematical italic nabla\n120572 mathematical italic small alpha\n120573 mathematical italic small beta\n120574 mathematical italic small gamma\n120575 mathematical italic small delta\n120576 mathematical italic small epsilon\n120577 mathematical italic small zeta\n120578 mathematical italic small eta\n120579 mathematical italic small theta\n120580 mathematical italic small iota\n120581 mathematical italic small kappa\n120582 mathematical italic small lamda\tlambda\n120583 mathematical italic small mu\n120584 mathematical italic small nu\n120585 mathematical italic small xi\n120586 mathematical italic small omicron\n120587 mathematical italic small pi\n120588 mathematical italic small rho\n120589 mathematical italic small final sigma\n120590 mathematical italic small sigma\n120591 mathematical italic small tau\n120592 mathematical italic small upsilon\n120593 mathematical italic small phi\n120594 mathematical italic small chi\n120595 mathematical italic small psi\n120596 mathematical italic small omega\n120597 mathematical italic partial differential\n120598 mathematical italic epsilon symbol\n120599 mathematical italic theta symbol\n120600 mathematical italic kappa symbol\n120601 mathematical italic phi symbol\n120602 mathematical italic rho symbol\n120603 mathematical italic pi symbol\n120604 mathematical bold italic capital alpha\n120605 mathematical bold italic capital beta\n120606 mathematical bold italic capital gamma\n120607 mathematical bold italic capital delta\n120608 mathematical bold italic capital epsilon\n120609 mathematical bold italic capital zeta\n120610 mathematical bold italic capital eta\n120611 mathematical bold italic capital theta\n120612 mathematical bold italic capital iota\n120613 mathematical bold italic capital kappa\n120614 mathematical bold italic capital lamda\tlambda\n120615 mathematical bold italic capital mu\n120616 mathematical bold italic capital nu\n120617 mathematical bold italic capital xi\n120618 mathematical bold italic capital omicron\n120619 mathematical bold italic capital pi\n120620 mathematical bold italic capital rho\n120621 mathematical bold italic capital theta symbol\n120622 mathematical bold italic capital sigma\n120623 mathematical bold italic capital tau\n120624 mathematical bold italic capital upsilon\n120625 mathematical bold italic capital phi\n120626 mathematical bold italic capital chi\n120627 mathematical bold italic capital psi\n120628 mathematical bold italic capital omega\n120629 mathematical bold italic nabla\n120630 mathematical bold italic small alpha\n120631 mathematical bold italic small beta\n120632 mathematical bold italic small gamma\n120633 mathematical bold italic small delta\n120634 mathematical bold italic small epsilon\n120635 mathematical bold italic small zeta\n120636 mathematical bold italic small eta\n120637 mathematical bold italic small theta\n120638 mathematical bold italic small iota\n120639 mathematical bold italic small kappa\n120640 mathematical bold italic small lamda\tlambda\n120641 mathematical bold italic small mu\n120642 mathematical bold italic small nu\n120643 mathematical bold italic small xi\n120644 mathematical bold italic small omicron\n120645 mathematical bold italic small pi\n120646 mathematical bold italic small rho\n120647 mathematical bold italic small final sigma\n120648 mathematical bold italic small sigma\n120649 mathematical bold italic small tau\n120650 mathematical bold italic small upsilon\n120651 mathematical bold italic small phi\n120652 mathematical bold italic small chi\n120653 mathematical bold italic small psi\n120654 mathematical bold italic small omega\n120655 mathematical bold italic partial differential\n120656 mathematical bold italic epsilon symbol\n120657 mathematical bold italic theta symbol\n120658 mathematical bold italic kappa symbol\n120659 mathematical bold italic phi symbol\n120660 mathematical bold italic rho symbol\n120661 mathematical bold italic pi symbol\n120662 mathematical sans-serif bold capital alpha\n120663 mathematical sans-serif bold capital beta\n120664 mathematical sans-serif bold capital gamma\n120665 mathematical sans-serif bold capital delta\n120666 mathematical sans-serif bold capital epsilon\n120667 mathematical sans-serif bold capital zeta\n120668 mathematical sans-serif bold capital eta\n120669 mathematical sans-serif bold capital theta\n120670 mathematical sans-serif bold capital iota\n120671 mathematical sans-serif bold capital kappa\n120672 mathematical sans-serif bold capital lamda\tlambda\n120673 mathematical sans-serif bold capital mu\n120674 mathematical sans-serif bold capital nu\n120675 mathematical sans-serif bold capital xi\n120676 mathematical sans-serif bold capital omicron\n120677 mathematical sans-serif bold capital pi\n120678 mathematical sans-serif bold capital rho\n120679 mathematical sans-serif bold capital theta symbol\n120680 mathematical sans-serif bold capital sigma\n120681 mathematical sans-serif bold capital tau\n120682 mathematical sans-serif bold capital upsilon\n120683 mathematical sans-serif bold capital phi\n120684 mathematical sans-serif bold capital chi\n120685 mathematical sans-serif bold capital psi\n120686 mathematical sans-serif bold capital omega\n120687 mathematical sans-serif bold nabla\n120688 mathematical sans-serif bold small alpha\n120689 mathematical sans-serif bold small beta\n120690 mathematical sans-serif bold small gamma\n120691 mathematical sans-serif bold small delta\n120692 mathematical sans-serif bold small epsilon\n120693 mathematical sans-serif bold small zeta\n120694 mathematical sans-serif bold small eta\n120695 mathematical sans-serif bold small theta\n120696 mathematical sans-serif bold small iota\n120697 mathematical sans-serif bold small kappa\n120698 mathematical sans-serif bold small lamda\tlambda\n120699 mathematical sans-serif bold small mu\n120700 mathematical sans-serif bold small nu\n120701 mathematical sans-serif bold small xi\n120702 mathematical sans-serif bold small omicron\n120703 mathematical sans-serif bold small pi\n120704 mathematical sans-serif bold small rho\n120705 mathematical sans-serif bold small final sigma\n120706 mathematical sans-serif bold small sigma\n120707 mathematical sans-serif bold small tau\n120708 mathematical sans-serif bold small upsilon\n120709 mathematical sans-serif bold small phi\n120710 mathematical sans-serif bold small chi\n120711 mathematical sans-serif bold small psi\n120712 mathematical sans-serif bold small omega\n120713 mathematical sans-serif bold partial differential\n120714 mathematical sans-serif bold epsilon symbol\n120715 mathematical sans-serif bold theta symbol\n120716 mathematical sans-serif bold kappa symbol\n120717 mathematical sans-serif bold phi symbol\n120718 mathematical sans-serif bold rho symbol\n120719 mathematical sans-serif bold pi symbol\n120720 mathematical sans-serif bold italic capital alpha\n120721 mathematical sans-serif bold italic capital beta\n120722 mathematical sans-serif bold italic capital gamma\n120723 mathematical sans-serif bold italic capital delta\n120724 mathematical sans-serif bold italic capital epsilon\n120725 mathematical sans-serif bold italic capital zeta\n120726 mathematical sans-serif bold italic capital eta\n120727 mathematical sans-serif bold italic capital theta\n120728 mathematical sans-serif bold italic capital iota\n120729 mathematical sans-serif bold italic capital kappa\n120730 mathematical sans-serif bold italic capital lamda\tlambda\n120731 mathematical sans-serif bold italic capital mu\n120732 mathematical sans-serif bold italic capital nu\n120733 mathematical sans-serif bold italic capital xi\n120734 mathematical sans-serif bold italic capital omicron\n120735 mathematical sans-serif bold italic capital pi\n120736 mathematical sans-serif bold italic capital rho\n120737 mathematical sans-serif bold italic capital theta symbol\n120738 mathematical sans-serif bold italic capital sigma\n120739 mathematical sans-serif bold italic capital tau\n120740 mathematical sans-serif bold italic capital upsilon\n120741 mathematical sans-serif bold italic capital phi\n120742 mathematical sans-serif bold italic capital chi\n120743 mathematical sans-serif bold italic capital psi\n120744 mathematical sans-serif bold italic capital omega\n120745 mathematical sans-serif bold italic nabla\n120746 mathematical sans-serif bold italic small alpha\n120747 mathematical sans-serif bold italic small beta\n120748 mathematical sans-serif bold italic small gamma\n120749 mathematical sans-serif bold italic small delta\n120750 mathematical sans-serif bold italic small epsilon\n120751 mathematical sans-serif bold italic small zeta\n120752 mathematical sans-serif bold italic small eta\n120753 mathematical sans-serif bold italic small theta\n120754 mathematical sans-serif bold italic small iota\n120755 mathematical sans-serif bold italic small kappa\n120756 mathematical sans-serif bold italic small lamda\tlambda\n120757 mathematical sans-serif bold italic small mu\n120758 mathematical sans-serif bold italic small nu\n120759 mathematical sans-serif bold italic small xi\n120760 mathematical sans-serif bold italic small omicron\n120761 mathematical sans-serif bold italic small pi\n120762 mathematical sans-serif bold italic small rho\n120763 mathematical sans-serif bold italic small final sigma\n120764 mathematical sans-serif bold italic small sigma\n120765 mathematical sans-serif bold italic small tau\n120766 mathematical sans-serif bold italic small upsilon\n120767 mathematical sans-serif bold italic small phi\n120768 mathematical sans-serif bold italic small chi\n120769 mathematical sans-serif bold italic small psi\n120770 mathematical sans-serif bold italic small omega\n120771 mathematical sans-serif bold italic partial differential\n120772 mathematical sans-serif bold italic epsilon symbol\n120773 mathematical sans-serif bold italic theta symbol\n120774 mathematical sans-serif bold italic kappa symbol\n120775 mathematical sans-serif bold italic phi symbol\n120776 mathematical sans-serif bold italic rho symbol\n120777 mathematical sans-serif bold italic pi symbol\n120778 mathematical bold capital digamma\n120779 mathematical bold small digamma\n120782 mathematical bold digit zero\n120783 mathematical bold digit one\n120784 mathematical bold digit two\n120785 mathematical bold digit three\n120786 mathematical bold digit four\n120787 mathematical bold digit five\n120788 mathematical bold digit six\n120789 mathematical bold digit seven\n120790 mathematical bold digit eight\n120791 mathematical bold digit nine\n120792 mathematical double-struck digit zero\n120793 mathematical double-struck digit one\n120794 mathematical double-struck digit two\n120795 mathematical double-struck digit three\n120796 mathematical double-struck digit four\n120797 mathematical double-struck digit five\n120798 mathematical double-struck digit six\n120799 mathematical double-struck digit seven\n120800 mathematical double-struck digit eight\n120801 mathematical double-struck digit nine\n120802 mathematical sans-serif digit zero\n120803 mathematical sans-serif digit one\n120804 mathematical sans-serif digit two\n120805 mathematical sans-serif digit three\n120806 mathematical sans-serif digit four\n120807 mathematical sans-serif digit five\n120808 mathematical sans-serif digit six\n120809 mathematical sans-serif digit seven\n120810 mathematical sans-serif digit eight\n120811 mathematical sans-serif digit nine\n120812 mathematical sans-serif bold digit zero\n120813 mathematical sans-serif bold digit one\n120814 mathematical sans-serif bold digit two\n120815 mathematical sans-serif bold digit three\n120816 mathematical sans-serif bold digit four\n120817 mathematical sans-serif bold digit five\n120818 mathematical sans-serif bold digit six\n120819 mathematical sans-serif bold digit seven\n120820 mathematical sans-serif bold digit eight\n120821 mathematical sans-serif bold digit nine\n120822 mathematical monospace digit zero\n120823 mathematical monospace digit one\n120824 mathematical monospace digit two\n120825 mathematical monospace digit three\n120826 mathematical monospace digit four\n120827 mathematical monospace digit five\n120828 mathematical monospace digit six\n120829 mathematical monospace digit seven\n120830 mathematical monospace digit eight\n120831 mathematical monospace digit nine\n120832 signwriting hand-fist index\n120833 signwriting hand-circle index\n120834 signwriting hand-cup index\n120835 signwriting hand-oval index\n120836 signwriting hand-hinge index\n120837 signwriting hand-angle index\n120838 signwriting hand-fist index bent\n120839 signwriting hand-circle index bent\n120840 signwriting hand-fist thumb under index bent\n120841 signwriting hand-fist index raised knuckle\n120842 signwriting hand-fist index cupped\n120843 signwriting hand-fist index hinged\n120844 signwriting hand-fist index hinged low\n120845 signwriting hand-circle index hinge\n120846 signwriting hand-fist index middle\n120847 signwriting hand-circle index middle\n120848 signwriting hand-fist index middle bent\n120849 signwriting hand-fist index middle raised knuckles\n120850 signwriting hand-fist index middle hinged\n120851 signwriting hand-fist index up middle hinged\n120852 signwriting hand-fist index hinged middle up\n120853 signwriting hand-fist index middle conjoined\n120854 signwriting hand-fist index middle conjoined index bent\n120855 signwriting hand-fist index middle conjoined middle bent\n120856 signwriting hand-fist index middle conjoined cupped\n120857 signwriting hand-fist index middle conjoined hinged\n120858 signwriting hand-fist index middle crossed\n120859 signwriting hand-circle index middle crossed\n120860 signwriting hand-fist middle bent over index\n120861 signwriting hand-fist index bent over middle\n120862 signwriting hand-fist index middle thumb\n120863 signwriting hand-circle index middle thumb\n120864 signwriting hand-fist index middle straight thumb bent\n120865 signwriting hand-fist index middle bent thumb straight\n120866 signwriting hand-fist index middle thumb bent\n120867 signwriting hand-fist index middle hinged spread thumb side\n120868 signwriting hand-fist index up middle hinged thumb side\n120869 signwriting hand-fist index up middle hinged thumb conjoined\n120870 signwriting hand-fist index hinged middle up thumb side\n120871 signwriting hand-fist index middle up spread thumb forward\n120872 signwriting hand-fist index middle thumb cupped\n120873 signwriting hand-fist index middle thumb circled\n120874 signwriting hand-fist index middle thumb hooked\n120875 signwriting hand-fist index middle thumb hinged\n120876 signwriting hand-fist thumb between index middle straight\n120877 signwriting hand-fist index middle conjoined thumb side\n120878 signwriting hand-fist index middle conjoined thumb side conjoined\n120879 signwriting hand-fist index middle conjoined thumb side bent\n120880 signwriting hand-fist middle thumb hooked index up\n120881 signwriting hand-fist index thumb hooked middle up\n120882 signwriting hand-fist index middle conjoined hinged thumb side\n120883 signwriting hand-fist index middle crossed thumb side\n120884 signwriting hand-fist index middle conjoined thumb forward\n120885 signwriting hand-fist index middle conjoined cupped thumb forward\n120886 signwriting hand-fist middle thumb cupped index up\n120887 signwriting hand-fist index thumb cupped middle up\n120888 signwriting hand-fist middle thumb circled index up\n120889 signwriting hand-fist middle thumb circled index hinged\n120890 signwriting hand-fist index thumb angled out middle up\n120891 signwriting hand-fist index thumb angled in middle up\n120892 signwriting hand-fist index thumb circled middle up\n120893 signwriting hand-fist index middle thumb conjoined hinged\n120894 signwriting hand-fist index middle thumb angled out\n120895 signwriting hand-fist index middle thumb angled\n120896 signwriting hand-fist middle thumb angled out index up\n120897 signwriting hand-fist middle thumb angled out index crossed\n120898 signwriting hand-fist middle thumb angled index up\n120899 signwriting hand-fist index thumb hooked middle hinged\n120900 signwriting hand-flat four fingers\n120901 signwriting hand-flat four fingers bent\n120902 signwriting hand-flat four fingers hinged\n120903 signwriting hand-flat four fingers conjoined\n120904 signwriting hand-flat four fingers conjoined split\n120905 signwriting hand-claw four fingers conjoined\n120906 signwriting hand-fist four fingers conjoined bent\n120907 signwriting hand-hinge four fingers conjoined\n120908 signwriting hand-flat five fingers spread\n120909 signwriting hand-flat heel five fingers spread\n120910 signwriting hand-flat five fingers spread four bent\n120911 signwriting hand-flat heel five fingers spread four bent\n120912 signwriting hand-flat five fingers spread bent\n120913 signwriting hand-flat heel five fingers spread bent\n120914 signwriting hand-flat five fingers spread thumb forward\n120915 signwriting hand-cup five fingers spread\n120916 signwriting hand-cup five fingers spread open\n120917 signwriting hand-hinge five fingers spread open\n120918 signwriting hand-oval five fingers spread\n120919 signwriting hand-flat five fingers spread hinged\n120920 signwriting hand-flat five fingers spread hinged thumb side\n120921 signwriting hand-flat five fingers spread hinged no thumb\n120922 signwriting hand-flat\n120923 signwriting hand-flat between palm facings\n120924 signwriting hand-flat heel\n120925 signwriting hand-flat thumb side\n120926 signwriting hand-flat heel thumb side\n120927 signwriting hand-flat thumb bent\n120928 signwriting hand-flat thumb forward\n120929 signwriting hand-flat split index thumb side\n120930 signwriting hand-flat split centre\n120931 signwriting hand-flat split centre thumb side\n120932 signwriting hand-flat split centre thumb side bent\n120933 signwriting hand-flat split little\n120934 signwriting hand-claw\n120935 signwriting hand-claw thumb side\n120936 signwriting hand-claw no thumb\n120937 signwriting hand-claw thumb forward\n120938 signwriting hand-hook curlicue\n120939 signwriting hand-hook\n120940 signwriting hand-cup open\n120941 signwriting hand-cup\n120942 signwriting hand-cup open thumb side\n120943 signwriting hand-cup thumb side\n120944 signwriting hand-cup open no thumb\n120945 signwriting hand-cup no thumb\n120946 signwriting hand-cup open thumb forward\n120947 signwriting hand-cup thumb forward\n120948 signwriting hand-curlicue open\n120949 signwriting hand-curlicue\n120950 signwriting hand-circle\n120951 signwriting hand-oval\n120952 signwriting hand-oval thumb side\n120953 signwriting hand-oval no thumb\n120954 signwriting hand-oval thumb forward\n120955 signwriting hand-hinge open\n120956 signwriting hand-hinge open thumb forward\n120957 signwriting hand-hinge\n120958 signwriting hand-hinge small\n120959 signwriting hand-hinge open thumb side\n120960 signwriting hand-hinge thumb side\n120961 signwriting hand-hinge open no thumb\n120962 signwriting hand-hinge no thumb\n120963 signwriting hand-hinge thumb side touching index\n120964 signwriting hand-hinge thumb between middle ring\n120965 signwriting hand-angle\n120966 signwriting hand-fist index middle ring\n120967 signwriting hand-circle index middle ring\n120968 signwriting hand-hinge index middle ring\n120969 signwriting hand-angle index middle ring\n120970 signwriting hand-hinge little\n120971 signwriting hand-fist index middle ring bent\n120972 signwriting hand-fist index middle ring conjoined\n120973 signwriting hand-hinge index middle ring conjoined\n120974 signwriting hand-fist little down\n120975 signwriting hand-fist little down ripple straight\n120976 signwriting hand-fist little down ripple curved\n120977 signwriting hand-fist little down others circled\n120978 signwriting hand-fist little up\n120979 signwriting hand-fist thumb under little up\n120980 signwriting hand-circle little up\n120981 signwriting hand-oval little up\n120982 signwriting hand-angle little up\n120983 signwriting hand-fist little raised knuckle\n120984 signwriting hand-fist little bent\n120985 signwriting hand-fist little touches thumb\n120986 signwriting hand-fist little thumb\n120987 signwriting hand-hinge little thumb\n120988 signwriting hand-fist little index thumb\n120989 signwriting hand-hinge little index thumb\n120990 signwriting hand-angle little index thumb index thumb out\n120991 signwriting hand-angle little index thumb index thumb\n120992 signwriting hand-fist little index\n120993 signwriting hand-circle little index\n120994 signwriting hand-hinge little index\n120995 signwriting hand-angle little index\n120996 signwriting hand-fist index middle little\n120997 signwriting hand-circle index middle little\n120998 signwriting hand-hinge index middle little\n120999 signwriting hand-hinge ring\n121000 signwriting hand-angle index middle little\n121001 signwriting hand-fist index middle cross little\n121002 signwriting hand-circle index middle cross little\n121003 signwriting hand-fist ring down\n121004 signwriting hand-hinge ring down index thumb hook middle\n121005 signwriting hand-angle ring down middle thumb index cross\n121006 signwriting hand-fist ring up\n121007 signwriting hand-fist ring raised knuckle\n121008 signwriting hand-fist ring little\n121009 signwriting hand-circle ring little\n121010 signwriting hand-oval ring little\n121011 signwriting hand-angle ring little\n121012 signwriting hand-fist ring middle\n121013 signwriting hand-fist ring middle conjoined\n121014 signwriting hand-fist ring middle raised knuckles\n121015 signwriting hand-fist ring index\n121016 signwriting hand-fist ring thumb\n121017 signwriting hand-hook ring thumb\n121018 signwriting hand-fist index ring little\n121019 signwriting hand-circle index ring little\n121020 signwriting hand-curlicue index ring little on\n121021 signwriting hand-hook index ring little out\n121022 signwriting hand-hook index ring little in\n121023 signwriting hand-hook index ring little under\n121024 signwriting hand-cup index ring little\n121025 signwriting hand-hinge index ring little\n121026 signwriting hand-angle index ring little out\n121027 signwriting hand-angle index ring little\n121028 signwriting hand-fist middle down\n121029 signwriting hand-hinge middle\n121030 signwriting hand-fist middle up\n121031 signwriting hand-circle middle up\n121032 signwriting hand-fist middle raised knuckle\n121033 signwriting hand-fist middle up thumb side\n121034 signwriting hand-hook middle thumb\n121035 signwriting hand-fist middle thumb little\n121036 signwriting hand-fist middle little\n121037 signwriting hand-fist middle ring little\n121038 signwriting hand-circle middle ring little\n121039 signwriting hand-curlicue middle ring little on\n121040 signwriting hand-cup middle ring little\n121041 signwriting hand-hinge middle ring little\n121042 signwriting hand-angle middle ring little out\n121043 signwriting hand-angle middle ring little in\n121044 signwriting hand-angle middle ring little\n121045 signwriting hand-circle middle ring little bent\n121046 signwriting hand-claw middle ring little conjoined\n121047 signwriting hand-claw middle ring little conjoined side\n121048 signwriting hand-hook middle ring little conjoined out\n121049 signwriting hand-hook middle ring little conjoined in\n121050 signwriting hand-hook middle ring little conjoined\n121051 signwriting hand-hinge index hinged\n121052 signwriting hand-fist index thumb side\n121053 signwriting hand-hinge index thumb side\n121054 signwriting hand-fist index thumb side thumb diagonal\n121055 signwriting hand-fist index thumb side thumb conjoined\n121056 signwriting hand-fist index thumb side thumb bent\n121057 signwriting hand-fist index thumb side index bent\n121058 signwriting hand-fist index thumb side both bent\n121059 signwriting hand-fist index thumb side index hinge\n121060 signwriting hand-fist index thumb forward index straight\n121061 signwriting hand-fist index thumb forward index bent\n121062 signwriting hand-fist index thumb hook\n121063 signwriting hand-fist index thumb curlicue\n121064 signwriting hand-fist index thumb curve thumb inside\n121065 signwriting hand-claw index thumb curve thumb inside\n121066 signwriting hand-fist index thumb curve thumb under\n121067 signwriting hand-fist index thumb circle\n121068 signwriting hand-cup index thumb\n121069 signwriting hand-cup index thumb open\n121070 signwriting hand-hinge index thumb open\n121071 signwriting hand-hinge index thumb large\n121072 signwriting hand-hinge index thumb\n121073 signwriting hand-hinge index thumb small\n121074 signwriting hand-angle index thumb out\n121075 signwriting hand-angle index thumb in\n121076 signwriting hand-angle index thumb\n121077 signwriting hand-fist thumb\n121078 signwriting hand-fist thumb heel\n121079 signwriting hand-fist thumb side diagonal\n121080 signwriting hand-fist thumb side conjoined\n121081 signwriting hand-fist thumb side bent\n121082 signwriting hand-fist thumb forward\n121083 signwriting hand-fist thumb between index middle\n121084 signwriting hand-fist thumb between middle ring\n121085 signwriting hand-fist thumb between ring little\n121086 signwriting hand-fist thumb under two fingers\n121087 signwriting hand-fist thumb over two fingers\n121088 signwriting hand-fist thumb under three fingers\n121089 signwriting hand-fist thumb under four fingers\n121090 signwriting hand-fist thumb over four raised knuckles\n121091 signwriting hand-fist\n121092 signwriting hand-fist heel\n121093 signwriting touch single\n121094 signwriting touch multiple\n121095 signwriting touch between\n121096 signwriting grasp single\n121097 signwriting grasp multiple\n121098 signwriting grasp between\n121099 signwriting strike single\n121100 signwriting strike multiple\n121101 signwriting strike between\n121102 signwriting brush single\n121103 signwriting brush multiple\n121104 signwriting brush between\n121105 signwriting rub single\n121106 signwriting rub multiple\n121107 signwriting rub between\n121108 signwriting surface symbols\n121109 signwriting surface between\n121110 signwriting squeeze large single\n121111 signwriting squeeze small single\n121112 signwriting squeeze large multiple\n121113 signwriting squeeze small multiple\n121114 signwriting squeeze sequential\n121115 signwriting flick large single\n121116 signwriting flick small single\n121117 signwriting flick large multiple\n121118 signwriting flick small multiple\n121119 signwriting flick sequential\n121120 signwriting squeeze flick alternating\n121121 signwriting movement-hinge up down large\n121122 signwriting movement-hinge up down small\n121123 signwriting movement-hinge up sequential\n121124 signwriting movement-hinge down sequential\n121125 signwriting movement-hinge up down alternating large\n121126 signwriting movement-hinge up down alternating small\n121127 signwriting movement-hinge side to side scissors\n121128 signwriting movement-wallplane finger contact\n121129 signwriting movement-floorplane finger contact\n121130 signwriting movement-wallplane single straight small\n121131 signwriting movement-wallplane single straight medium\n121132 signwriting movement-wallplane single straight large\n121133 signwriting movement-wallplane single straight largest\n121134 signwriting movement-wallplane single wrist flex\n121135 signwriting movement-wallplane double straight\n121136 signwriting movement-wallplane double wrist flex\n121137 signwriting movement-wallplane double alternating\n121138 signwriting movement-wallplane double alternating wrist flex\n121139 signwriting movement-wallplane cross\n121140 signwriting movement-wallplane triple straight movement\n121141 signwriting movement-wallplane triple wrist flex\n121142 signwriting movement-wallplane triple alternating\n121143 signwriting movement-wallplane triple alternating wrist flex\n121144 signwriting movement-wallplane bend small\n121145 signwriting movement-wallplane bend medium\n121146 signwriting movement-wallplane bend large\n121147 signwriting movement-wallplane corner small\n121148 signwriting movement-wallplane corner medium\n121149 signwriting movement-wallplane corner large\n121150 signwriting movement-wallplane corner rotation\n121151 signwriting movement-wallplane check small\n121152 signwriting movement-wallplane check medium\n121153 signwriting movement-wallplane check large\n121154 signwriting movement-wallplane box small\n121155 signwriting movement-wallplane box medium\n121156 signwriting movement-wallplane box large\n121157 signwriting movement-wallplane zigzag small\n121158 signwriting movement-wallplane zigzag medium\n121159 signwriting movement-wallplane zigzag large\n121160 signwriting movement-wallplane peaks small\n121161 signwriting movement-wallplane peaks medium\n121162 signwriting movement-wallplane peaks large\n121163 signwriting travel-wallplane rotation-wallplane single\n121164 signwriting travel-wallplane rotation-wallplane double\n121165 signwriting travel-wallplane rotation-wallplane alternating\n121166 signwriting travel-wallplane rotation-floorplane single\n121167 signwriting travel-wallplane rotation-floorplane double\n121168 signwriting travel-wallplane rotation-floorplane alternating\n121169 signwriting travel-wallplane shaking\n121170 signwriting travel-wallplane arm spiral single\n121171 signwriting travel-wallplane arm spiral double\n121172 signwriting travel-wallplane arm spiral triple\n121173 signwriting movement-diagonal away small\n121174 signwriting movement-diagonal away medium\n121175 signwriting movement-diagonal away large\n121176 signwriting movement-diagonal away largest\n121177 signwriting movement-diagonal towards small\n121178 signwriting movement-diagonal towards medium\n121179 signwriting movement-diagonal towards large\n121180 signwriting movement-diagonal towards largest\n121181 signwriting movement-diagonal between away small\n121182 signwriting movement-diagonal between away medium\n121183 signwriting movement-diagonal between away large\n121184 signwriting movement-diagonal between away largest\n121185 signwriting movement-diagonal between towards small\n121186 signwriting movement-diagonal between towards medium\n121187 signwriting movement-diagonal between towards large\n121188 signwriting movement-diagonal between towards largest\n121189 signwriting movement-floorplane single straight small\n121190 signwriting movement-floorplane single straight medium\n121191 signwriting movement-floorplane single straight large\n121192 signwriting movement-floorplane single straight largest\n121193 signwriting movement-floorplane single wrist flex\n121194 signwriting movement-floorplane double straight\n121195 signwriting movement-floorplane double wrist flex\n121196 signwriting movement-floorplane double alternating\n121197 signwriting movement-floorplane double alternating wrist flex\n121198 signwriting movement-floorplane cross\n121199 signwriting movement-floorplane triple straight movement\n121200 signwriting movement-floorplane triple wrist flex\n121201 signwriting movement-floorplane triple alternating movement\n121202 signwriting movement-floorplane triple alternating wrist flex\n121203 signwriting movement-floorplane bend\n121204 signwriting movement-floorplane corner small\n121205 signwriting movement-floorplane corner medium\n121206 signwriting movement-floorplane corner large\n121207 signwriting movement-floorplane check\n121208 signwriting movement-floorplane box small\n121209 signwriting movement-floorplane box medium\n121210 signwriting movement-floorplane box large\n121211 signwriting movement-floorplane zigzag small\n121212 signwriting movement-floorplane zigzag medium\n121213 signwriting movement-floorplane zigzag large\n121214 signwriting movement-floorplane peaks small\n121215 signwriting movement-floorplane peaks medium\n121216 signwriting movement-floorplane peaks large\n121217 signwriting travel-floorplane rotation-floorplane single\n121218 signwriting travel-floorplane rotation-floorplane double\n121219 signwriting travel-floorplane rotation-floorplane alternating\n121220 signwriting travel-floorplane rotation-wallplane single\n121221 signwriting travel-floorplane rotation-wallplane double\n121222 signwriting travel-floorplane rotation-wallplane alternating\n121223 signwriting travel-floorplane shaking\n121224 signwriting movement-wallplane curve quarter small\n121225 signwriting movement-wallplane curve quarter medium\n121226 signwriting movement-wallplane curve quarter large\n121227 signwriting movement-wallplane curve quarter largest\n121228 signwriting movement-wallplane curve half-circle small\n121229 signwriting movement-wallplane curve half-circle medium\n121230 signwriting movement-wallplane curve half-circle large\n121231 signwriting movement-wallplane curve half-circle largest\n121232 signwriting movement-wallplane curve three-quarter circle small\n121233 signwriting movement-wallplane curve three-quarter circle medium\n121234 signwriting movement-wallplane hump small\n121235 signwriting movement-wallplane hump medium\n121236 signwriting movement-wallplane hump large\n121237 signwriting movement-wallplane loop small\n121238 signwriting movement-wallplane loop medium\n121239 signwriting movement-wallplane loop large\n121240 signwriting movement-wallplane loop small double\n121241 signwriting movement-wallplane wave curve double small\n121242 signwriting movement-wallplane wave curve double medium\n121243 signwriting movement-wallplane wave curve double large\n121244 signwriting movement-wallplane wave curve triple small\n121245 signwriting movement-wallplane wave curve triple medium\n121246 signwriting movement-wallplane wave curve triple large\n121247 signwriting movement-wallplane curve then straight\n121248 signwriting movement-wallplane curved cross small\n121249 signwriting movement-wallplane curved cross medium\n121250 signwriting rotation-wallplane single\n121251 signwriting rotation-wallplane double\n121252 signwriting rotation-wallplane alternate\n121253 signwriting movement-wallplane shaking\n121254 signwriting movement-wallplane curve hitting front wall\n121255 signwriting movement-wallplane hump hitting front wall\n121256 signwriting movement-wallplane loop hitting front wall\n121257 signwriting movement-wallplane wave hitting front wall\n121258 signwriting rotation-wallplane single hitting front wall\n121259 signwriting rotation-wallplane double hitting front wall\n121260 signwriting rotation-wallplane alternating hitting front wall\n121261 signwriting movement-wallplane curve hitting chest\n121262 signwriting movement-wallplane hump hitting chest\n121263 signwriting movement-wallplane loop hitting chest\n121264 signwriting movement-wallplane wave hitting chest\n121265 signwriting rotation-wallplane single hitting chest\n121266 signwriting rotation-wallplane double hitting chest\n121267 signwriting rotation-wallplane alternating hitting chest\n121268 signwriting movement-wallplane wave diagonal path small\n121269 signwriting movement-wallplane wave diagonal path medium\n121270 signwriting movement-wallplane wave diagonal path large\n121271 signwriting movement-floorplane curve hitting ceiling small\n121272 signwriting movement-floorplane curve hitting ceiling large\n121273 signwriting movement-floorplane hump hitting ceiling small double\n121274 signwriting movement-floorplane hump hitting ceiling large double\n121275 signwriting movement-floorplane hump hitting ceiling small triple\n121276 signwriting movement-floorplane hump hitting ceiling large triple\n121277 signwriting movement-floorplane loop hitting ceiling small single\n121278 signwriting movement-floorplane loop hitting ceiling large single\n121279 signwriting movement-floorplane loop hitting ceiling small double\n121280 signwriting movement-floorplane loop hitting ceiling large double\n121281 signwriting movement-floorplane wave hitting ceiling small\n121282 signwriting movement-floorplane wave hitting ceiling large\n121283 signwriting rotation-floorplane single hitting ceiling\n121284 signwriting rotation-floorplane double hitting ceiling\n121285 signwriting rotation-floorplane alternating hitting ceiling\n121286 signwriting movement-floorplane curve hitting floor small\n121287 signwriting movement-floorplane curve hitting floor large\n121288 signwriting movement-floorplane hump hitting floor small double\n121289 signwriting movement-floorplane hump hitting floor large double\n121290 signwriting movement-floorplane hump hitting floor triple small triple\n121291 signwriting movement-floorplane hump hitting floor triple large triple\n121292 signwriting movement-floorplane loop hitting floor small single\n121293 signwriting movement-floorplane loop hitting floor large single\n121294 signwriting movement-floorplane loop hitting floor small double\n121295 signwriting movement-floorplane loop hitting floor large double\n121296 signwriting movement-floorplane wave hitting floor small\n121297 signwriting movement-floorplane wave hitting floor large\n121298 signwriting rotation-floorplane single hitting floor\n121299 signwriting rotation-floorplane double hitting floor\n121300 signwriting rotation-floorplane alternating hitting floor\n121301 signwriting movement-floorplane curve small\n121302 signwriting movement-floorplane curve medium\n121303 signwriting movement-floorplane curve large\n121304 signwriting movement-floorplane curve largest\n121305 signwriting movement-floorplane curve combined\n121306 signwriting movement-floorplane hump small\n121307 signwriting movement-floorplane loop small\n121308 signwriting movement-floorplane wave snake\n121309 signwriting movement-floorplane wave small\n121310 signwriting movement-floorplane wave large\n121311 signwriting rotation-floorplane single\n121312 signwriting rotation-floorplane double\n121313 signwriting rotation-floorplane alternating\n121314 signwriting movement-floorplane shaking parallel\n121315 signwriting movement-wallplane arm circle small single\n121316 signwriting movement-wallplane arm circle medium single\n121317 signwriting movement-wallplane arm circle small double\n121318 signwriting movement-wallplane arm circle medium double\n121319 signwriting movement-floorplane arm circle hitting wall small single\n121320 signwriting movement-floorplane arm circle hitting wall medium single\n121321 signwriting movement-floorplane arm circle hitting wall large single\n121322 signwriting movement-floorplane arm circle hitting wall small double\n121323 signwriting movement-floorplane arm circle hitting wall medium double\n121324 signwriting movement-floorplane arm circle hitting wall large double\n121325 signwriting movement-wallplane wrist circle front single\n121326 signwriting movement-wallplane wrist circle front double\n121327 signwriting movement-floorplane wrist circle hitting wall single\n121328 signwriting movement-floorplane wrist circle hitting wall double\n121329 signwriting movement-wallplane finger circles single\n121330 signwriting movement-wallplane finger circles double\n121331 signwriting movement-floorplane finger circles hitting wall single\n121332 signwriting movement-floorplane finger circles hitting wall double\n121333 signwriting dynamic arrowhead small\n121334 signwriting dynamic arrowhead large\n121335 signwriting dynamic fast\n121336 signwriting dynamic slow\n121337 signwriting dynamic tense\n121338 signwriting dynamic relaxed\n121339 signwriting dynamic simultaneous\n121340 signwriting dynamic simultaneous alternating\n121341 signwriting dynamic every other time\n121342 signwriting dynamic gradual\n121343 signwriting head\n121344 signwriting head rim\n121345 signwriting head movement-wallplane straight\n121346 signwriting head movement-wallplane tilt\n121347 signwriting head movement-floorplane straight\n121348 signwriting head movement-wallplane curve\n121349 signwriting head movement-floorplane curve\n121350 signwriting head movement circle\n121351 signwriting face direction position nose forward tilting\n121352 signwriting face direction position nose up or down\n121353 signwriting face direction position nose up or down tilting\n121354 signwriting eyebrows straight up\n121355 signwriting eyebrows straight neutral\n121356 signwriting eyebrows straight down\n121357 signwriting dreamy eyebrows neutral down\n121358 signwriting dreamy eyebrows down neutral\n121359 signwriting dreamy eyebrows up neutral\n121360 signwriting dreamy eyebrows neutral up\n121361 signwriting forehead neutral\n121362 signwriting forehead contact\n121363 signwriting forehead wrinkled\n121364 signwriting eyes open\n121365 signwriting eyes squeezed\n121366 signwriting eyes closed\n121367 signwriting eye blink single\n121368 signwriting eye blink multiple\n121369 signwriting eyes half open\n121370 signwriting eyes wide open\n121371 signwriting eyes half closed\n121372 signwriting eyes widening movement\n121373 signwriting eye wink\n121374 signwriting eyelashes up\n121375 signwriting eyelashes down\n121376 signwriting eyelashes fluttering\n121377 signwriting eyegaze-wallplane straight\n121378 signwriting eyegaze-wallplane straight double\n121379 signwriting eyegaze-wallplane straight alternating\n121380 signwriting eyegaze-floorplane straight\n121381 signwriting eyegaze-floorplane straight double\n121382 signwriting eyegaze-floorplane straight alternating\n121383 signwriting eyegaze-wallplane curved\n121384 signwriting eyegaze-floorplane curved\n121385 signwriting eyegaze-wallplane circling\n121386 signwriting cheeks puffed\n121387 signwriting cheeks neutral\n121388 signwriting cheeks sucked\n121389 signwriting tense cheeks high\n121390 signwriting tense cheeks middle\n121391 signwriting tense cheeks low\n121392 signwriting ears\n121393 signwriting nose neutral\n121394 signwriting nose contact\n121395 signwriting nose wrinkles\n121396 signwriting nose wiggles\n121397 signwriting air blowing out\n121398 signwriting air sucking in\n121399 signwriting air blow small rotations\n121400 signwriting air suck small rotations\n121401 signwriting breath inhale\n121402 signwriting breath exhale\n121403 signwriting mouth closed neutral\n121404 signwriting mouth closed forward\n121405 signwriting mouth closed contact\n121406 signwriting mouth smile\n121407 signwriting mouth smile wrinkled\n121408 signwriting mouth smile open\n121409 signwriting mouth frown\n121410 signwriting mouth frown wrinkled\n121411 signwriting mouth frown open\n121412 signwriting mouth open circle\n121413 signwriting mouth open forward\n121414 signwriting mouth open wrinkled\n121415 signwriting mouth open oval\n121416 signwriting mouth open oval wrinkled\n121417 signwriting mouth open oval yawn\n121418 signwriting mouth open rectangle\n121419 signwriting mouth open rectangle wrinkled\n121420 signwriting mouth open rectangle yawn\n121421 signwriting mouth kiss\n121422 signwriting mouth kiss forward\n121423 signwriting mouth kiss wrinkled\n121424 signwriting mouth tense\n121425 signwriting mouth tense forward\n121426 signwriting mouth tense sucked\n121427 signwriting lips pressed together\n121428 signwriting lip lower over upper\n121429 signwriting lip upper over lower\n121430 signwriting mouth corners\n121431 signwriting mouth wrinkles single\n121432 signwriting mouth wrinkles double\n121433 signwriting tongue sticking out far\n121434 signwriting tongue licking lips\n121435 signwriting tongue tip between lips\n121436 signwriting tongue tip touching inside mouth\n121437 signwriting tongue inside mouth relaxed\n121438 signwriting tongue moves against cheek\n121439 signwriting tongue centre sticking out\n121440 signwriting tongue centre inside mouth\n121441 signwriting teeth\n121442 signwriting teeth movement\n121443 signwriting teeth on tongue\n121444 signwriting teeth on tongue movement\n121445 signwriting teeth on lips\n121446 signwriting teeth on lips movement\n121447 signwriting teeth bite lips\n121448 signwriting movement-wallplane jaw\n121449 signwriting movement-floorplane jaw\n121450 signwriting neck\n121451 signwriting hair\n121452 signwriting excitement\n121453 signwriting shoulder hip spine\n121454 signwriting shoulder hip positions\n121455 signwriting wallplane shoulder hip move\n121456 signwriting floorplane shoulder hip move\n121457 signwriting shoulder tilting from waist\n121458 signwriting torso-wallplane straight stretch\n121459 signwriting torso-wallplane curved bend\n121460 signwriting torso-floorplane twisting\n121461 signwriting upper body tilting from hip joints\n121462 signwriting limb combination\n121463 signwriting limb length-1\n121464 signwriting limb length-2\n121465 signwriting limb length-3\n121466 signwriting limb length-4\n121467 signwriting limb length-5\n121468 signwriting limb length-6\n121469 signwriting limb length-7\n121470 signwriting finger\n121471 signwriting location-wallplane space\n121472 signwriting location-floorplane space\n121473 signwriting location height\n121474 signwriting location width\n121475 signwriting location depth\n121476 signwriting location head neck\n121477 signwriting location torso\n121478 signwriting location limbs digits\n121479 signwriting comma\n121480 signwriting full stop\n121481 signwriting semicolon\n121482 signwriting colon\n121483 signwriting parenthesis\n121499 signwriting fill modifier-2\n121500 signwriting fill modifier-3\n121501 signwriting fill modifier-4\n121502 signwriting fill modifier-5\n121503 signwriting fill modifier-6\n121505 signwriting rotation modifier-2\n121506 signwriting rotation modifier-3\n121507 signwriting rotation modifier-4\n121508 signwriting rotation modifier-5\n121509 signwriting rotation modifier-6\n121510 signwriting rotation modifier-7\n121511 signwriting rotation modifier-8\n121512 signwriting rotation modifier-9\n121513 signwriting rotation modifier-10\n121514 signwriting rotation modifier-11\n121515 signwriting rotation modifier-12\n121516 signwriting rotation modifier-13\n121517 signwriting rotation modifier-14\n121518 signwriting rotation modifier-15\n121519 signwriting rotation modifier-16\n122624 latin small letter feng digraph with trill\n122625 latin small letter reversed script g\n122626 latin letter small capital turned g\n122627 latin small letter reversed k\n122628 latin letter small capital l with belt\n122629 latin small letter lezh with retroflex hook\n122630 latin small letter turned y with belt\n122631 latin small letter reversed eng\n122632 latin small letter turned r with long leg and retroflex hook\n122633 latin small letter t with hook and retroflex hook\n122634 latin letter retroflex click with retroflex hook\n122635 latin small letter esh with double bar\n122636 latin small letter esh with double bar and curl\n122637 latin small letter turned t with curl\n122638 latin letter inverted glottal stop with curl\n122639 latin letter stretched c with curl\n122640 latin letter small capital turned k\n122641 latin small letter l with fishhook\n122642 latin small letter dezh digraph with palatal hook\n122643 latin small letter l with belt and palatal hook\n122644 latin small letter eng with palatal hook\n122645 latin small letter turned r with palatal hook\n122646 latin small letter r with fishhook and palatal hook\n122647 latin small letter tesh digraph with palatal hook\n122648 latin small letter ezh with palatal hook\n122649 latin small letter dezh digraph with retroflex hook\n122650 latin small letter i with stroke and retroflex hook\n122651 latin small letter o with retroflex hook\n122652 latin small letter tesh digraph with retroflex hook\n122653 latin small letter c with retroflex hook\n122654 latin small letter s with curl\n122661 latin small letter d with mid-height left hook\n122662 latin small letter l with mid-height left hook\n122663 latin small letter n with mid-height left hook\n122664 latin small letter r with mid-height left hook\n122665 latin small letter s with mid-height left hook\n122666 latin small letter t with mid-height left hook\n122880 combining glagolitic letter azu\n122881 combining glagolitic letter buky\n122882 combining glagolitic letter vede\n122883 combining glagolitic letter glagoli\n122884 combining glagolitic letter dobro\n122885 combining glagolitic letter yestu\n122886 combining glagolitic letter zhivete\n122888 combining glagolitic letter zemlja\n122889 combining glagolitic letter izhe\n122890 combining glagolitic letter initial izhe\n122891 combining glagolitic letter i\n122892 combining glagolitic letter djervi\n122893 combining glagolitic letter kako\n122894 combining glagolitic letter ljudije\n122895 combining glagolitic letter myslite\n122896 combining glagolitic letter nashi\n122897 combining glagolitic letter onu\n122898 combining glagolitic letter pokoji\n122899 combining glagolitic letter ritsi\n122900 combining glagolitic letter slovo\n122901 combining glagolitic letter tvrido\n122902 combining glagolitic letter uku\n122903 combining glagolitic letter fritu\n122904 combining glagolitic letter heru\n122907 combining glagolitic letter shta\n122908 combining glagolitic letter tsi\n122909 combining glagolitic letter chrivi\n122910 combining glagolitic letter sha\n122911 combining glagolitic letter yeru\n122912 combining glagolitic letter yeri\n122913 combining glagolitic letter yati\n122915 combining glagolitic letter yu\n122916 combining glagolitic letter small yus\n122918 combining glagolitic letter yo\n122919 combining glagolitic letter iotated small yus\n122920 combining glagolitic letter big yus\n122921 combining glagolitic letter iotated big yus\n122922 combining glagolitic letter fita\n122928 modifier letter cyrillic small a\n122929 modifier letter cyrillic small be\n122930 modifier letter cyrillic small ve\n122931 modifier letter cyrillic small ghe\n122932 modifier letter cyrillic small de\n122933 modifier letter cyrillic small ie\n122934 modifier letter cyrillic small zhe\n122935 modifier letter cyrillic small ze\n122936 modifier letter cyrillic small i\n122937 modifier letter cyrillic small ka\n122938 modifier letter cyrillic small el\n122939 modifier letter cyrillic small em\n122940 modifier letter cyrillic small o\n122941 modifier letter cyrillic small pe\n122942 modifier letter cyrillic small er\n122943 modifier letter cyrillic small es\n122944 modifier letter cyrillic small te\n122945 modifier letter cyrillic small u\n122946 modifier letter cyrillic small ef\n122947 modifier letter cyrillic small ha\n122948 modifier letter cyrillic small tse\n122949 modifier letter cyrillic small che\n122950 modifier letter cyrillic small sha\n122951 modifier letter cyrillic small yeru\n122952 modifier letter cyrillic small e\n122953 modifier letter cyrillic small yu\n122954 modifier letter cyrillic small dzze\n122955 modifier letter cyrillic small schwa\n122956 modifier letter cyrillic small byelorussian-ukrainian i\n122957 modifier letter cyrillic small je\n122958 modifier letter cyrillic small barred o\n122959 modifier letter cyrillic small straight u\n122960 modifier letter cyrillic small palochka\n122961 cyrillic subscript small letter a\n122962 cyrillic subscript small letter be\n122963 cyrillic subscript small letter ve\n122964 cyrillic subscript small letter ghe\n122965 cyrillic subscript small letter de\n122966 cyrillic subscript small letter ie\n122967 cyrillic subscript small letter zhe\n122968 cyrillic subscript small letter ze\n122969 cyrillic subscript small letter i\n122970 cyrillic subscript small letter ka\n122971 cyrillic subscript small letter el\n122972 cyrillic subscript small letter o\n122973 cyrillic subscript small letter pe\n122974 cyrillic subscript small letter es\n122975 cyrillic subscript small letter u\n122976 cyrillic subscript small letter ef\n122977 cyrillic subscript small letter ha\n122978 cyrillic subscript small letter tse\n122979 cyrillic subscript small letter che\n122980 cyrillic subscript small letter sha\n122981 cyrillic subscript small letter hard sign\n122982 cyrillic subscript small letter yeru\n122983 cyrillic subscript small letter ghe with upturn\n122984 cyrillic subscript small letter byelorussian-ukrainian i\n122985 cyrillic subscript small letter dze\n122986 cyrillic subscript small letter dzhe\n122987 modifier letter cyrillic small es with descender\n122988 modifier letter cyrillic small yeru with back yer\n122989 modifier letter cyrillic small straight u with stroke\n123023 combining cyrillic small letter byelorussian-ukrainian i\n123136 nyiakeng puachue hmong letter ma\n123137 nyiakeng puachue hmong letter tsa\n123138 nyiakeng puachue hmong letter nta\n123139 nyiakeng puachue hmong letter ta\n123140 nyiakeng puachue hmong letter ha\n123141 nyiakeng puachue hmong letter na\n123142 nyiakeng puachue hmong letter xa\n123143 nyiakeng puachue hmong letter nka\n123144 nyiakeng puachue hmong letter ca\n123145 nyiakeng puachue hmong letter la\n123146 nyiakeng puachue hmong letter sa\n123147 nyiakeng puachue hmong letter za\n123148 nyiakeng puachue hmong letter nca\n123149 nyiakeng puachue hmong letter ntsa\n123150 nyiakeng puachue hmong letter ka\n123151 nyiakeng puachue hmong letter da\n123152 nyiakeng puachue hmong letter nya\n123153 nyiakeng puachue hmong letter nra\n123154 nyiakeng puachue hmong letter va\n123155 nyiakeng puachue hmong letter ntxa\n123156 nyiakeng puachue hmong letter txa\n123157 nyiakeng puachue hmong letter fa\n123158 nyiakeng puachue hmong letter ra\n123159 nyiakeng puachue hmong letter qa\n123160 nyiakeng puachue hmong letter ya\n123161 nyiakeng puachue hmong letter nqa\n123162 nyiakeng puachue hmong letter pa\n123163 nyiakeng puachue hmong letter xya\n123164 nyiakeng puachue hmong letter npa\n123165 nyiakeng puachue hmong letter dla\n123166 nyiakeng puachue hmong letter npla\n123167 nyiakeng puachue hmong letter hah\n123168 nyiakeng puachue hmong letter mla\n123169 nyiakeng puachue hmong letter pla\n123170 nyiakeng puachue hmong letter ga\n123171 nyiakeng puachue hmong letter rra\n123172 nyiakeng puachue hmong letter a\n123173 nyiakeng puachue hmong letter aa\n123174 nyiakeng puachue hmong letter i\n123175 nyiakeng puachue hmong letter u\n123176 nyiakeng puachue hmong letter o\n123177 nyiakeng puachue hmong letter oo\n123178 nyiakeng puachue hmong letter e\n123179 nyiakeng puachue hmong letter ee\n123180 nyiakeng puachue hmong letter w\n123184 nyiakeng puachue hmong tone-b\n123185 nyiakeng puachue hmong tone-m\n123186 nyiakeng puachue hmong tone-j\n123187 nyiakeng puachue hmong tone-v\n123188 nyiakeng puachue hmong tone-s\n123189 nyiakeng puachue hmong tone-g\n123190 nyiakeng puachue hmong tone-d\n123191 nyiakeng puachue hmong sign for person\n123192 nyiakeng puachue hmong sign for thing\n123193 nyiakeng puachue hmong sign for location\n123194 nyiakeng puachue hmong sign for animal\n123195 nyiakeng puachue hmong sign for invertebrate\n123196 nyiakeng puachue hmong sign xw xw\n123197 nyiakeng puachue hmong syllable lengthener\n123200 nyiakeng puachue hmong digit zero\n123201 nyiakeng puachue hmong digit one\n123202 nyiakeng puachue hmong digit two\n123203 nyiakeng puachue hmong digit three\n123204 nyiakeng puachue hmong digit four\n123205 nyiakeng puachue hmong digit five\n123206 nyiakeng puachue hmong digit six\n123207 nyiakeng puachue hmong digit seven\n123208 nyiakeng puachue hmong digit eight\n123209 nyiakeng puachue hmong digit nine\n123214 nyiakeng puachue hmong logogram nyaj\n123215 nyiakeng puachue hmong circled ca\n123536 toto letter pa\n123537 toto letter ba\n123538 toto letter ta\n123539 toto letter da\n123540 toto letter ka\n123541 toto letter ga\n123542 toto letter ma\n123543 toto letter na\n123544 toto letter nga\n123545 toto letter sa\n123546 toto letter cha\n123547 toto letter ya\n123548 toto letter wa\n123549 toto letter ja\n123550 toto letter ha\n123551 toto letter ra\n123552 toto letter la\n123553 toto letter i\n123554 toto letter breathy i\n123555 toto letter iu\n123556 toto letter breathy iu\n123557 toto letter u\n123558 toto letter e\n123559 toto letter breathy e\n123560 toto letter eo\n123561 toto letter breathy eo\n123562 toto letter o\n123563 toto letter ae\n123564 toto letter breathy ae\n123565 toto letter a\n123566 toto sign rising tone\n123584 wancho letter aa\n123585 wancho letter a\n123586 wancho letter ba\n123587 wancho letter ca\n123588 wancho letter da\n123589 wancho letter ga\n123590 wancho letter ya\n123591 wancho letter pha\n123592 wancho letter la\n123593 wancho letter na\n123594 wancho letter pa\n123595 wancho letter ta\n123596 wancho letter tha\n123597 wancho letter fa\n123598 wancho letter sa\n123599 wancho letter sha\n123600 wancho letter ja\n123601 wancho letter za\n123602 wancho letter wa\n123603 wancho letter va\n123604 wancho letter ka\n123605 wancho letter o\n123606 wancho letter au\n123607 wancho letter ra\n123608 wancho letter ma\n123609 wancho letter kha\n123610 wancho letter ha\n123611 wancho letter e\n123612 wancho letter i\n123613 wancho letter nga\n123614 wancho letter u\n123615 wancho letter llha\n123616 wancho letter tsa\n123617 wancho letter tra\n123618 wancho letter ong\n123619 wancho letter aang\n123620 wancho letter ang\n123621 wancho letter ing\n123622 wancho letter on\n123623 wancho letter en\n123624 wancho letter aan\n123625 wancho letter nya\n123626 wancho letter uen\n123627 wancho letter yih\n123628 wancho tone tup\n123629 wancho tone tupni\n123630 wancho tone koi\n123631 wancho tone koini\n123632 wancho digit zero\n123633 wancho digit one\n123634 wancho digit two\n123635 wancho digit three\n123636 wancho digit four\n123637 wancho digit five\n123638 wancho digit six\n123639 wancho digit seven\n123640 wancho digit eight\n123641 wancho digit nine\n123647 wancho ngun sign\n124112 nag mundari letter o\n124113 nag mundari letter op\n124114 nag mundari letter ol\n124115 nag mundari letter oy\n124116 nag mundari letter ong\n124117 nag mundari letter a\n124118 nag mundari letter aj\n124119 nag mundari letter ab\n124120 nag mundari letter any\n124121 nag mundari letter ah\n124122 nag mundari letter i\n124123 nag mundari letter is\n124124 nag mundari letter idd\n124125 nag mundari letter it\n124126 nag mundari letter ih\n124127 nag mundari letter u\n124128 nag mundari letter uc\n124129 nag mundari letter ud\n124130 nag mundari letter uk\n124131 nag mundari letter ur\n124132 nag mundari letter e\n124133 nag mundari letter enn\n124134 nag mundari letter eg\n124135 nag mundari letter em\n124136 nag mundari letter en\n124137 nag mundari letter ett\n124138 nag mundari letter ell\n124139 nag mundari sign ojod\n124140 nag mundari sign muhor\n124141 nag mundari sign toyor\n124142 nag mundari sign ikir\n124143 nag mundari sign sutuh\n124144 nag mundari digit zero\n124145 nag mundari digit one\n124146 nag mundari digit two\n124147 nag mundari digit three\n124148 nag mundari digit four\n124149 nag mundari digit five\n124150 nag mundari digit six\n124151 nag mundari digit seven\n124152 nag mundari digit eight\n124153 nag mundari digit nine\n124368 ol onal letter o\n124369 ol onal letter om\n124370 ol onal letter ong\n124371 ol onal letter orr\n124372 ol onal letter oo\n124373 ol onal letter oy\n124374 ol onal letter a\n124375 ol onal letter ad\n124376 ol onal letter ab\n124377 ol onal letter ah\n124378 ol onal letter al\n124379 ol onal letter aw\n124380 ol onal letter i\n124381 ol onal letter it\n124382 ol onal letter ip\n124383 ol onal letter itt\n124384 ol onal letter id\n124385 ol onal letter in\n124386 ol onal letter u\n124387 ol onal letter uk\n124388 ol onal letter udd\n124389 ol onal letter uj\n124390 ol onal letter uny\n124391 ol onal letter ur\n124392 ol onal letter e\n124393 ol onal letter es\n124394 ol onal letter eh\n124395 ol onal letter ec\n124396 ol onal letter enn\n124397 ol onal letter eg\n124398 ol onal sign mu\n124399 ol onal sign ikir\n124400 ol onal sign hoddond\n124401 ol onal digit zero\n124402 ol onal digit one\n124403 ol onal digit two\n124404 ol onal digit three\n124405 ol onal digit four\n124406 ol onal digit five\n124407 ol onal digit six\n124408 ol onal digit seven\n124409 ol onal digit eight\n124410 ol onal digit nine\n124415 ol onal abbreviation sign\n124608 tai yo letter low ko\n124609 tai yo letter high ko\n124610 tai yo letter low kho\n124611 tai yo letter high kho\n124612 tai yo letter go\n124613 tai yo letter ngo\n124614 tai yo letter co\n124615 tai yo letter low xo\n124616 tai yo letter high xo\n124617 tai yo letter low nyo\n124618 tai yo letter high nyo\n124619 tai yo letter do\n124620 tai yo letter low to\n124621 tai yo letter high to\n124622 tai yo letter tho\n124623 tai yo letter no\n124624 tai yo letter bo\n124625 tai yo letter low po\n124626 tai yo letter high po\n124627 tai yo letter pho\n124628 tai yo letter low fo\n124629 tai yo letter high fo\n124630 tai yo letter mo\n124631 tai yo letter yo\n124632 tai yo letter lo\n124633 tai yo letter vo\n124634 tai yo letter low ho\n124635 tai yo letter high ho\n124636 tai yo letter qo\n124637 tai yo letter low kvo\n124638 tai yo letter high kvo\n124640 tai yo letter aa\n124641 tai yo letter i\n124642 tai yo letter ue\n124643 tai yo sign ue\n124644 tai yo letter u\n124645 tai yo letter ae\n124646 tai yo sign au\n124647 tai yo letter o\n124648 tai yo letter e\n124649 tai yo letter ia\n124650 tai yo letter uea\n124651 tai yo letter ua\n124652 tai yo letter oo\n124653 tai yo letter aue\n124654 tai yo sign ay\n124655 tai yo sign ang\n124656 tai yo letter an\n124657 tai yo letter am\n124658 tai yo letter ak\n124659 tai yo letter at\n124660 tai yo letter ap\n124661 tai yo sign om\n124670 tai yo symbol mueang\n124671 tai yo xam lai\n124896 ethiopic syllable hhya\n124897 ethiopic syllable hhyu\n124898 ethiopic syllable hhyi\n124899 ethiopic syllable hhyaa\n124900 ethiopic syllable hhyee\n124901 ethiopic syllable hhye\n124902 ethiopic syllable hhyo\n124904 ethiopic syllable gurage hhwa\n124905 ethiopic syllable hhwi\n124906 ethiopic syllable hhwee\n124907 ethiopic syllable hhwe\n124909 ethiopic syllable gurage mwi\n124910 ethiopic syllable gurage mwee\n124912 ethiopic syllable gurage qwi\n124913 ethiopic syllable gurage qwee\n124914 ethiopic syllable gurage qwe\n124915 ethiopic syllable gurage bwi\n124916 ethiopic syllable gurage bwee\n124917 ethiopic syllable gurage kwi\n124918 ethiopic syllable gurage kwee\n124919 ethiopic syllable gurage kwe\n124920 ethiopic syllable gurage gwi\n124921 ethiopic syllable gurage gwee\n124922 ethiopic syllable gurage gwe\n124923 ethiopic syllable gurage fwi\n124924 ethiopic syllable gurage fwee\n124925 ethiopic syllable gurage pwi\n124926 ethiopic syllable gurage pwee\n124928 mende kikakui syllable m001 ki\n124929 mende kikakui syllable m002 ka\n124930 mende kikakui syllable m003 ku\n124931 mende kikakui syllable m065 kee\n124932 mende kikakui syllable m095 ke\n124933 mende kikakui syllable m076 koo\n124934 mende kikakui syllable m048 ko\n124935 mende kikakui syllable m179 kua\n124936 mende kikakui syllable m004 wi\n124937 mende kikakui syllable m005 wa\n124938 mende kikakui syllable m006 wu\n124939 mende kikakui syllable m126 wee\n124940 mende kikakui syllable m118 we\n124941 mende kikakui syllable m114 woo\n124942 mende kikakui syllable m045 wo\n124943 mende kikakui syllable m194 wui\n124944 mende kikakui syllable m143 wei\n124945 mende kikakui syllable m061 wvi\n124946 mende kikakui syllable m049 wva\n124947 mende kikakui syllable m139 wve\n124948 mende kikakui syllable m007 min\n124949 mende kikakui syllable m008 man\n124950 mende kikakui syllable m009 mun\n124951 mende kikakui syllable m059 men\n124952 mende kikakui syllable m094 mon\n124953 mende kikakui syllable m154 muan\n124954 mende kikakui syllable m189 muen\n124955 mende kikakui syllable m010 bi\n124956 mende kikakui syllable m011 ba\n124957 mende kikakui syllable m012 bu\n124958 mende kikakui syllable m150 bee\n124959 mende kikakui syllable m097 be\n124960 mende kikakui syllable m103 boo\n124961 mende kikakui syllable m138 bo\n124962 mende kikakui syllable m013 i\n124963 mende kikakui syllable m014 a\n124964 mende kikakui syllable m015 u\n124965 mende kikakui syllable m163 ee\n124966 mende kikakui syllable m100 e\n124967 mende kikakui syllable m165 oo\n124968 mende kikakui syllable m147 o\n124969 mende kikakui syllable m137 ei\n124970 mende kikakui syllable m131 in\n124971 mende kikakui syllable m135 in\n124972 mende kikakui syllable m195 an\n124973 mende kikakui syllable m178 en\n124974 mende kikakui syllable m019 si\n124975 mende kikakui syllable m020 sa\n124976 mende kikakui syllable m021 su\n124977 mende kikakui syllable m162 see\n124978 mende kikakui syllable m116 se\n124979 mende kikakui syllable m136 soo\n124980 mende kikakui syllable m079 so\n124981 mende kikakui syllable m196 sia\n124982 mende kikakui syllable m025 li\n124983 mende kikakui syllable m026 la\n124984 mende kikakui syllable m027 lu\n124985 mende kikakui syllable m084 lee\n124986 mende kikakui syllable m073 le\n124987 mende kikakui syllable m054 loo\n124988 mende kikakui syllable m153 lo\n124989 mende kikakui syllable m110 long le\n124990 mende kikakui syllable m016 di\n124991 mende kikakui syllable m017 da\n124992 mende kikakui syllable m018 du\n124993 mende kikakui syllable m089 dee\n124994 mende kikakui syllable m180 doo\n124995 mende kikakui syllable m181 do\n124996 mende kikakui syllable m022 ti\n124997 mende kikakui syllable m023 ta\n124998 mende kikakui syllable m024 tu\n124999 mende kikakui syllable m091 tee\n125000 mende kikakui syllable m055 te\n125001 mende kikakui syllable m104 too\n125002 mende kikakui syllable m069 to\n125003 mende kikakui syllable m028 ji\n125004 mende kikakui syllable m029 ja\n125005 mende kikakui syllable m030 ju\n125006 mende kikakui syllable m157 jee\n125007 mende kikakui syllable m113 je\n125008 mende kikakui syllable m160 joo\n125009 mende kikakui syllable m063 jo\n125010 mende kikakui syllable m175 long jo\n125011 mende kikakui syllable m031 yi\n125012 mende kikakui syllable m032 ya\n125013 mende kikakui syllable m033 yu\n125014 mende kikakui syllable m109 yee\n125015 mende kikakui syllable m080 ye\n125016 mende kikakui syllable m141 yoo\n125017 mende kikakui syllable m121 yo\n125018 mende kikakui syllable m034 fi\n125019 mende kikakui syllable m035 fa\n125020 mende kikakui syllable m036 fu\n125021 mende kikakui syllable m078 fee\n125022 mende kikakui syllable m075 fe\n125023 mende kikakui syllable m133 foo\n125024 mende kikakui syllable m088 fo\n125025 mende kikakui syllable m197 fua\n125026 mende kikakui syllable m101 fan\n125027 mende kikakui syllable m037 nin\n125028 mende kikakui syllable m038 nan\n125029 mende kikakui syllable m039 nun\n125030 mende kikakui syllable m117 nen\n125031 mende kikakui syllable m169 non\n125032 mende kikakui syllable m176 hi\n125033 mende kikakui syllable m041 ha\n125034 mende kikakui syllable m186 hu\n125035 mende kikakui syllable m040 hee\n125036 mende kikakui syllable m096 he\n125037 mende kikakui syllable m042 hoo\n125038 mende kikakui syllable m140 ho\n125039 mende kikakui syllable m083 heei\n125040 mende kikakui syllable m128 hoou\n125041 mende kikakui syllable m053 hin\n125042 mende kikakui syllable m130 han\n125043 mende kikakui syllable m087 hun\n125044 mende kikakui syllable m052 hen\n125045 mende kikakui syllable m193 hon\n125046 mende kikakui syllable m046 huan\n125047 mende kikakui syllable m090 nggi\n125048 mende kikakui syllable m043 ngga\n125049 mende kikakui syllable m082 nggu\n125050 mende kikakui syllable m115 nggee\n125051 mende kikakui syllable m146 ngge\n125052 mende kikakui syllable m156 nggoo\n125053 mende kikakui syllable m120 nggo\n125054 mende kikakui syllable m159 nggaa\n125055 mende kikakui syllable m127 nggua\n125056 mende kikakui syllable m086 long ngge\n125057 mende kikakui syllable m106 long nggoo\n125058 mende kikakui syllable m183 long nggo\n125059 mende kikakui syllable m155 gi\n125060 mende kikakui syllable m111 ga\n125061 mende kikakui syllable m168 gu\n125062 mende kikakui syllable m190 gee\n125063 mende kikakui syllable m166 guei\n125064 mende kikakui syllable m167 guan\n125065 mende kikakui syllable m184 ngen\n125066 mende kikakui syllable m057 ngon\n125067 mende kikakui syllable m177 nguan\n125068 mende kikakui syllable m068 pi\n125069 mende kikakui syllable m099 pa\n125070 mende kikakui syllable m050 pu\n125071 mende kikakui syllable m081 pee\n125072 mende kikakui syllable m051 pe\n125073 mende kikakui syllable m102 poo\n125074 mende kikakui syllable m066 po\n125075 mende kikakui syllable m145 mbi\n125076 mende kikakui syllable m062 mba\n125077 mende kikakui syllable m122 mbu\n125078 mende kikakui syllable m047 mbee\n125079 mende kikakui syllable m188 mbee\n125080 mende kikakui syllable m072 mbe\n125081 mende kikakui syllable m172 mboo\n125082 mende kikakui syllable m174 mbo\n125083 mende kikakui syllable m187 mbuu\n125084 mende kikakui syllable m161 long mbe\n125085 mende kikakui syllable m105 long mboo\n125086 mende kikakui syllable m142 long mbo\n125087 mende kikakui syllable m132 kpi\n125088 mende kikakui syllable m092 kpa\n125089 mende kikakui syllable m074 kpu\n125090 mende kikakui syllable m044 kpee\n125091 mende kikakui syllable m108 kpe\n125092 mende kikakui syllable m112 kpoo\n125093 mende kikakui syllable m158 kpo\n125094 mende kikakui syllable m124 gbi\n125095 mende kikakui syllable m056 gba\n125096 mende kikakui syllable m148 gbu\n125097 mende kikakui syllable m093 gbee\n125098 mende kikakui syllable m107 gbe\n125099 mende kikakui syllable m071 gboo\n125100 mende kikakui syllable m070 gbo\n125101 mende kikakui syllable m171 ra\n125102 mende kikakui syllable m123 ndi\n125103 mende kikakui syllable m129 nda\n125104 mende kikakui syllable m125 ndu\n125105 mende kikakui syllable m191 ndee\n125106 mende kikakui syllable m119 nde\n125107 mende kikakui syllable m067 ndoo\n125108 mende kikakui syllable m064 ndo\n125109 mende kikakui syllable m152 nja\n125110 mende kikakui syllable m192 nju\n125111 mende kikakui syllable m149 njee\n125112 mende kikakui syllable m134 njoo\n125113 mende kikakui syllable m182 vi\n125114 mende kikakui syllable m185 va\n125115 mende kikakui syllable m151 vu\n125116 mende kikakui syllable m173 vee\n125117 mende kikakui syllable m085 ve\n125118 mende kikakui syllable m144 voo\n125119 mende kikakui syllable m077 vo\n125120 mende kikakui syllable m164 nyin\n125121 mende kikakui syllable m058 nyan\n125122 mende kikakui syllable m170 nyun\n125123 mende kikakui syllable m098 nyen\n125124 mende kikakui syllable m060 nyon\n125127 mende kikakui digit one\n125128 mende kikakui digit two\n125129 mende kikakui digit three\n125130 mende kikakui digit four\n125131 mende kikakui digit five\n125132 mende kikakui digit six\n125133 mende kikakui digit seven\n125134 mende kikakui digit eight\n125135 mende kikakui digit nine\n125136 mende kikakui combining number teens\n125137 mende kikakui combining number tens\n125138 mende kikakui combining number hundreds\n125139 mende kikakui combining number thousands\n125140 mende kikakui combining number ten thousands\n125141 mende kikakui combining number hundred thousands\n125142 mende kikakui combining number millions\n125184 adlam capital letter alif\n125185 adlam capital letter daali\n125186 adlam capital letter laam\n125187 adlam capital letter miim\n125188 adlam capital letter ba\n125189 adlam capital letter sinnyiiyhe\n125190 adlam capital letter pe\n125191 adlam capital letter bhe\n125192 adlam capital letter ra\n125193 adlam capital letter e\n125194 adlam capital letter fa\n125195 adlam capital letter i\n125196 adlam capital letter o\n125197 adlam capital letter dha\n125198 adlam capital letter yhe\n125199 adlam capital letter waw\n125200 adlam capital letter nun\n125201 adlam capital letter kaf\n125202 adlam capital letter ya\n125203 adlam capital letter u\n125204 adlam capital letter jiim\n125205 adlam capital letter chi\n125206 adlam capital letter ha\n125207 adlam capital letter qaaf\n125208 adlam capital letter ga\n125209 adlam capital letter nya\n125210 adlam capital letter tu\n125211 adlam capital letter nha\n125212 adlam capital letter va\n125213 adlam capital letter kha\n125214 adlam capital letter gbe\n125215 adlam capital letter zal\n125216 adlam capital letter kpo\n125217 adlam capital letter sha\n125218 adlam small letter alif\n125219 adlam small letter daali\n125220 adlam small letter laam\n125221 adlam small letter miim\n125222 adlam small letter ba\n125223 adlam small letter sinnyiiyhe\n125224 adlam small letter pe\n125225 adlam small letter bhe\n125226 adlam small letter ra\n125227 adlam small letter e\n125228 adlam small letter fa\n125229 adlam small letter i\n125230 adlam small letter o\n125231 adlam small letter dha\n125232 adlam small letter yhe\n125233 adlam small letter waw\n125234 adlam small letter nun\n125235 adlam small letter kaf\n125236 adlam small letter ya\n125237 adlam small letter u\n125238 adlam small letter jiim\n125239 adlam small letter chi\n125240 adlam small letter ha\n125241 adlam small letter qaaf\n125242 adlam small letter ga\n125243 adlam small letter nya\n125244 adlam small letter tu\n125245 adlam small letter nha\n125246 adlam small letter va\n125247 adlam small letter kha\n125248 adlam small letter gbe\n125249 adlam small letter zal\n125250 adlam small letter kpo\n125251 adlam small letter sha\n125252 adlam alif lengthener\n125253 adlam vowel lengthener\n125254 adlam gemination mark\n125255 adlam hamza\n125256 adlam consonant modifier\n125257 adlam geminate consonant modifier\n125258 adlam nukta\n125259 adlam nasalization mark\n125264 adlam digit zero\n125265 adlam digit one\n125266 adlam digit two\n125267 adlam digit three\n125268 adlam digit four\n125269 adlam digit five\n125270 adlam digit six\n125271 adlam digit seven\n125272 adlam digit eight\n125273 adlam digit nine\n125278 adlam initial exclamation mark\n125279 adlam initial question mark\n126065 indic siyaq number one\n126066 indic siyaq number two\n126067 indic siyaq number three\n126068 indic siyaq number four\n126069 indic siyaq number five\n126070 indic siyaq number six\n126071 indic siyaq number seven\n126072 indic siyaq number eight\n126073 indic siyaq number nine\n126074 indic siyaq number ten\n126075 indic siyaq number twenty\n126076 indic siyaq number thirty\n126077 indic siyaq number forty\n126078 indic siyaq number fifty\n126079 indic siyaq number sixty\n126080 indic siyaq number seventy\n126081 indic siyaq number eighty\n126082 indic siyaq number ninety\n126083 indic siyaq number one hundred\n126084 indic siyaq number two hundred\n126085 indic siyaq number three hundred\n126086 indic siyaq number four hundred\n126087 indic siyaq number five hundred\n126088 indic siyaq number six hundred\n126089 indic siyaq number seven hundred\n126090 indic siyaq number eight hundred\n126091 indic siyaq number nine hundred\n126092 indic siyaq number one thousand\n126093 indic siyaq number two thousand\n126094 indic siyaq number three thousand\n126095 indic siyaq number four thousand\n126096 indic siyaq number five thousand\n126097 indic siyaq number six thousand\n126098 indic siyaq number seven thousand\n126099 indic siyaq number eight thousand\n126100 indic siyaq number nine thousand\n126101 indic siyaq number ten thousand\n126102 indic siyaq number twenty thousand\n126103 indic siyaq number thirty thousand\n126104 indic siyaq number forty thousand\n126105 indic siyaq number fifty thousand\n126106 indic siyaq number sixty thousand\n126107 indic siyaq number seventy thousand\n126108 indic siyaq number eighty thousand\n126109 indic siyaq number ninety thousand\n126110 indic siyaq number lakh\n126111 indic siyaq number lakhan\n126112 indic siyaq lakh mark\n126113 indic siyaq number karor\n126114 indic siyaq number karoran\n126115 indic siyaq number prefixed one\n126116 indic siyaq number prefixed two\n126117 indic siyaq number prefixed three\n126118 indic siyaq number prefixed four\n126119 indic siyaq number prefixed five\n126120 indic siyaq number prefixed six\n126121 indic siyaq number prefixed seven\n126122 indic siyaq number prefixed eight\n126123 indic siyaq number prefixed nine\n126124 indic siyaq placeholder\n126125 indic siyaq fraction one quarter\n126126 indic siyaq fraction one half\n126127 indic siyaq fraction three quarters\n126128 indic siyaq rupee mark\n126129 indic siyaq number alternate one\n126130 indic siyaq number alternate two\n126131 indic siyaq number alternate ten thousand\n126132 indic siyaq alternate lakh mark\n126209 ottoman siyaq number one\n126210 ottoman siyaq number two\n126211 ottoman siyaq number three\n126212 ottoman siyaq number four\n126213 ottoman siyaq number five\n126214 ottoman siyaq number six\n126215 ottoman siyaq number seven\n126216 ottoman siyaq number eight\n126217 ottoman siyaq number nine\n126218 ottoman siyaq number ten\n126219 ottoman siyaq number twenty\n126220 ottoman siyaq number thirty\n126221 ottoman siyaq number forty\n126222 ottoman siyaq number fifty\n126223 ottoman siyaq number sixty\n126224 ottoman siyaq number seventy\n126225 ottoman siyaq number eighty\n126226 ottoman siyaq number ninety\n126227 ottoman siyaq number one hundred\n126228 ottoman siyaq number two hundred\n126229 ottoman siyaq number three hundred\n126230 ottoman siyaq number four hundred\n126231 ottoman siyaq number five hundred\n126232 ottoman siyaq number six hundred\n126233 ottoman siyaq number seven hundred\n126234 ottoman siyaq number eight hundred\n126235 ottoman siyaq number nine hundred\n126236 ottoman siyaq number one thousand\n126237 ottoman siyaq number two thousand\n126238 ottoman siyaq number three thousand\n126239 ottoman siyaq number four thousand\n126240 ottoman siyaq number five thousand\n126241 ottoman siyaq number six thousand\n126242 ottoman siyaq number seven thousand\n126243 ottoman siyaq number eight thousand\n126244 ottoman siyaq number nine thousand\n126245 ottoman siyaq number ten thousand\n126246 ottoman siyaq number twenty thousand\n126247 ottoman siyaq number thirty thousand\n126248 ottoman siyaq number forty thousand\n126249 ottoman siyaq number fifty thousand\n126250 ottoman siyaq number sixty thousand\n126251 ottoman siyaq number seventy thousand\n126252 ottoman siyaq number eighty thousand\n126253 ottoman siyaq number ninety thousand\n126254 ottoman siyaq marratan\n126255 ottoman siyaq alternate number two\n126256 ottoman siyaq alternate number three\n126257 ottoman siyaq alternate number four\n126258 ottoman siyaq alternate number five\n126259 ottoman siyaq alternate number six\n126260 ottoman siyaq alternate number seven\n126261 ottoman siyaq alternate number eight\n126262 ottoman siyaq alternate number nine\n126263 ottoman siyaq alternate number ten\n126264 ottoman siyaq alternate number four hundred\n126265 ottoman siyaq alternate number six hundred\n126266 ottoman siyaq alternate number two thousand\n126267 ottoman siyaq alternate number ten thousand\n126268 ottoman siyaq fraction one half\n126269 ottoman siyaq fraction one sixth\n126464 arabic mathematical alef\n126465 arabic mathematical beh\n126466 arabic mathematical jeem\n126467 arabic mathematical dal\n126469 arabic mathematical waw\n126470 arabic mathematical zain\n126471 arabic mathematical hah\n126472 arabic mathematical tah\n126473 arabic mathematical yeh\n126474 arabic mathematical kaf\n126475 arabic mathematical lam\n126476 arabic mathematical meem\n126477 arabic mathematical noon\n126478 arabic mathematical seen\n126479 arabic mathematical ain\n126480 arabic mathematical feh\n126481 arabic mathematical sad\n126482 arabic mathematical qaf\n126483 arabic mathematical reh\n126484 arabic mathematical sheen\n126485 arabic mathematical teh\n126486 arabic mathematical theh\n126487 arabic mathematical khah\n126488 arabic mathematical thal\n126489 arabic mathematical dad\n126490 arabic mathematical zah\n126491 arabic mathematical ghain\n126492 arabic mathematical dotless beh\n126493 arabic mathematical dotless noon\n126494 arabic mathematical dotless feh\n126495 arabic mathematical dotless qaf\n126497 arabic mathematical initial beh\n126498 arabic mathematical initial jeem\n126500 arabic mathematical initial heh\n126503 arabic mathematical initial hah\n126505 arabic mathematical initial yeh\n126506 arabic mathematical initial kaf\n126507 arabic mathematical initial lam\n126508 arabic mathematical initial meem\n126509 arabic mathematical initial noon\n126510 arabic mathematical initial seen\n126511 arabic mathematical initial ain\n126512 arabic mathematical initial feh\n126513 arabic mathematical initial sad\n126514 arabic mathematical initial qaf\n126516 arabic mathematical initial sheen\n126517 arabic mathematical initial teh\n126518 arabic mathematical initial theh\n126519 arabic mathematical initial khah\n126521 arabic mathematical initial dad\n126523 arabic mathematical initial ghain\n126530 arabic mathematical tailed jeem\n126535 arabic mathematical tailed hah\n126537 arabic mathematical tailed yeh\n126539 arabic mathematical tailed lam\n126541 arabic mathematical tailed noon\n126542 arabic mathematical tailed seen\n126543 arabic mathematical tailed ain\n126545 arabic mathematical tailed sad\n126546 arabic mathematical tailed qaf\n126548 arabic mathematical tailed sheen\n126551 arabic mathematical tailed khah\n126553 arabic mathematical tailed dad\n126555 arabic mathematical tailed ghain\n126557 arabic mathematical tailed dotless noon\n126559 arabic mathematical tailed dotless qaf\n126561 arabic mathematical stretched beh\n126562 arabic mathematical stretched jeem\n126564 arabic mathematical stretched heh\n126567 arabic mathematical stretched hah\n126568 arabic mathematical stretched tah\n126569 arabic mathematical stretched yeh\n126570 arabic mathematical stretched kaf\n126572 arabic mathematical stretched meem\n126573 arabic mathematical stretched noon\n126574 arabic mathematical stretched seen\n126575 arabic mathematical stretched ain\n126576 arabic mathematical stretched feh\n126577 arabic mathematical stretched sad\n126578 arabic mathematical stretched qaf\n126580 arabic mathematical stretched sheen\n126581 arabic mathematical stretched teh\n126582 arabic mathematical stretched theh\n126583 arabic mathematical stretched khah\n126585 arabic mathematical stretched dad\n126586 arabic mathematical stretched zah\n126587 arabic mathematical stretched ghain\n126588 arabic mathematical stretched dotless beh\n126590 arabic mathematical stretched dotless feh\n126592 arabic mathematical looped alef\n126593 arabic mathematical looped beh\n126594 arabic mathematical looped jeem\n126595 arabic mathematical looped dal\n126596 arabic mathematical looped heh\n126597 arabic mathematical looped waw\n126598 arabic mathematical looped zain\n126599 arabic mathematical looped hah\n126600 arabic mathematical looped tah\n126601 arabic mathematical looped yeh\n126603 arabic mathematical looped lam\n126604 arabic mathematical looped meem\n126605 arabic mathematical looped noon\n126606 arabic mathematical looped seen\n126607 arabic mathematical looped ain\n126608 arabic mathematical looped feh\n126609 arabic mathematical looped sad\n126610 arabic mathematical looped qaf\n126611 arabic mathematical looped reh\n126612 arabic mathematical looped sheen\n126613 arabic mathematical looped teh\n126614 arabic mathematical looped theh\n126615 arabic mathematical looped khah\n126616 arabic mathematical looped thal\n126617 arabic mathematical looped dad\n126618 arabic mathematical looped zah\n126619 arabic mathematical looped ghain\n126625 arabic mathematical double-struck beh\n126626 arabic mathematical double-struck jeem\n126627 arabic mathematical double-struck dal\n126629 arabic mathematical double-struck waw\n126630 arabic mathematical double-struck zain\n126631 arabic mathematical double-struck hah\n126632 arabic mathematical double-struck tah\n126633 arabic mathematical double-struck yeh\n126635 arabic mathematical double-struck lam\n126636 arabic mathematical double-struck meem\n126637 arabic mathematical double-struck noon\n126638 arabic mathematical double-struck seen\n126639 arabic mathematical double-struck ain\n126640 arabic mathematical double-struck feh\n126641 arabic mathematical double-struck sad\n126642 arabic mathematical double-struck qaf\n126643 arabic mathematical double-struck reh\n126644 arabic mathematical double-struck sheen\n126645 arabic mathematical double-struck teh\n126646 arabic mathematical double-struck theh\n126647 arabic mathematical double-struck khah\n126648 arabic mathematical double-struck thal\n126649 arabic mathematical double-struck dad\n126650 arabic mathematical double-struck zah\n126651 arabic mathematical double-struck ghain\n126704 arabic mathematical operator meem with hah with tatweel\n126705 arabic mathematical operator hah with dal\n126976 mahjong tile east wind\n126977 mahjong tile south wind\n126978 mahjong tile west wind\n126979 mahjong tile north wind\n126980 mahjong tile red dragon\n126981 mahjong tile green dragon\n126982 mahjong tile white dragon\n126983 mahjong tile one of characters\n126984 mahjong tile two of characters\n126985 mahjong tile three of characters\n126986 mahjong tile four of characters\n126987 mahjong tile five of characters\n126988 mahjong tile six of characters\n126989 mahjong tile seven of characters\n126990 mahjong tile eight of characters\n126991 mahjong tile nine of characters\n126992 mahjong tile one of bamboos\n126993 mahjong tile two of bamboos\n126994 mahjong tile three of bamboos\n126995 mahjong tile four of bamboos\n126996 mahjong tile five of bamboos\n126997 mahjong tile six of bamboos\n126998 mahjong tile seven of bamboos\n126999 mahjong tile eight of bamboos\n127000 mahjong tile nine of bamboos\n127001 mahjong tile one of circles\n127002 mahjong tile two of circles\n127003 mahjong tile three of circles\n127004 mahjong tile four of circles\n127005 mahjong tile five of circles\n127006 mahjong tile six of circles\n127007 mahjong tile seven of circles\n127008 mahjong tile eight of circles\n127009 mahjong tile nine of circles\n127010 mahjong tile plum\n127011 mahjong tile orchid\n127012 mahjong tile bamboo\n127013 mahjong tile chrysanthemum\n127014 mahjong tile spring\n127015 mahjong tile summer\n127016 mahjong tile autumn\n127017 mahjong tile winter\n127018 mahjong tile joker\n127019 mahjong tile back\n127024 domino tile horizontal back\n127025 domino tile horizontal-00-00\n127026 domino tile horizontal-00-01\n127027 domino tile horizontal-00-02\n127028 domino tile horizontal-00-03\n127029 domino tile horizontal-00-04\n127030 domino tile horizontal-00-05\n127031 domino tile horizontal-00-06\n127032 domino tile horizontal-01-00\n127033 domino tile horizontal-01-01\n127034 domino tile horizontal-01-02\n127035 domino tile horizontal-01-03\n127036 domino tile horizontal-01-04\n127037 domino tile horizontal-01-05\n127038 domino tile horizontal-01-06\n127039 domino tile horizontal-02-00\n127040 domino tile horizontal-02-01\n127041 domino tile horizontal-02-02\n127042 domino tile horizontal-02-03\n127043 domino tile horizontal-02-04\n127044 domino tile horizontal-02-05\n127045 domino tile horizontal-02-06\n127046 domino tile horizontal-03-00\n127047 domino tile horizontal-03-01\n127048 domino tile horizontal-03-02\n127049 domino tile horizontal-03-03\n127050 domino tile horizontal-03-04\n127051 domino tile horizontal-03-05\n127052 domino tile horizontal-03-06\n127053 domino tile horizontal-04-00\n127054 domino tile horizontal-04-01\n127055 domino tile horizontal-04-02\n127056 domino tile horizontal-04-03\n127057 domino tile horizontal-04-04\n127058 domino tile horizontal-04-05\n127059 domino tile horizontal-04-06\n127060 domino tile horizontal-05-00\n127061 domino tile horizontal-05-01\n127062 domino tile horizontal-05-02\n127063 domino tile horizontal-05-03\n127064 domino tile horizontal-05-04\n127065 domino tile horizontal-05-05\n127066 domino tile horizontal-05-06\n127067 domino tile horizontal-06-00\n127068 domino tile horizontal-06-01\n127069 domino tile horizontal-06-02\n127070 domino tile horizontal-06-03\n127071 domino tile horizontal-06-04\n127072 domino tile horizontal-06-05\n127073 domino tile horizontal-06-06\n127074 domino tile vertical back\n127075 domino tile vertical-00-00\n127076 domino tile vertical-00-01\n127077 domino tile vertical-00-02\n127078 domino tile vertical-00-03\n127079 domino tile vertical-00-04\n127080 domino tile vertical-00-05\n127081 domino tile vertical-00-06\n127082 domino tile vertical-01-00\n127083 domino tile vertical-01-01\n127084 domino tile vertical-01-02\n127085 domino tile vertical-01-03\n127086 domino tile vertical-01-04\n127087 domino tile vertical-01-05\n127088 domino tile vertical-01-06\n127089 domino tile vertical-02-00\n127090 domino tile vertical-02-01\n127091 domino tile vertical-02-02\n127092 domino tile vertical-02-03\n127093 domino tile vertical-02-04\n127094 domino tile vertical-02-05\n127095 domino tile vertical-02-06\n127096 domino tile vertical-03-00\n127097 domino tile vertical-03-01\n127098 domino tile vertical-03-02\n127099 domino tile vertical-03-03\n127100 domino tile vertical-03-04\n127101 domino tile vertical-03-05\n127102 domino tile vertical-03-06\n127103 domino tile vertical-04-00\n127104 domino tile vertical-04-01\n127105 domino tile vertical-04-02\n127106 domino tile vertical-04-03\n127107 domino tile vertical-04-04\n127108 domino tile vertical-04-05\n127109 domino tile vertical-04-06\n127110 domino tile vertical-05-00\n127111 domino tile vertical-05-01\n127112 domino tile vertical-05-02\n127113 domino tile vertical-05-03\n127114 domino tile vertical-05-04\n127115 domino tile vertical-05-05\n127116 domino tile vertical-05-06\n127117 domino tile vertical-06-00\n127118 domino tile vertical-06-01\n127119 domino tile vertical-06-02\n127120 domino tile vertical-06-03\n127121 domino tile vertical-06-04\n127122 domino tile vertical-06-05\n127123 domino tile vertical-06-06\n127136 playing card back\n127137 playing card ace of spades\n127138 playing card two of spades\n127139 playing card three of spades\n127140 playing card four of spades\n127141 playing card five of spades\n127142 playing card six of spades\n127143 playing card seven of spades\n127144 playing card eight of spades\n127145 playing card nine of spades\n127146 playing card ten of spades\n127147 playing card jack of spades\n127148 playing card knight of spades\n127149 playing card queen of spades\n127150 playing card king of spades\n127153 playing card ace of hearts\n127154 playing card two of hearts\n127155 playing card three of hearts\n127156 playing card four of hearts\n127157 playing card five of hearts\n127158 playing card six of hearts\n127159 playing card seven of hearts\n127160 playing card eight of hearts\n127161 playing card nine of hearts\n127162 playing card ten of hearts\n127163 playing card jack of hearts\n127164 playing card knight of hearts\n127165 playing card queen of hearts\n127166 playing card king of hearts\n127167 playing card red joker\n127169 playing card ace of diamonds\n127170 playing card two of diamonds\n127171 playing card three of diamonds\n127172 playing card four of diamonds\n127173 playing card five of diamonds\n127174 playing card six of diamonds\n127175 playing card seven of diamonds\n127176 playing card eight of diamonds\n127177 playing card nine of diamonds\n127178 playing card ten of diamonds\n127179 playing card jack of diamonds\n127180 playing card knight of diamonds\n127181 playing card queen of diamonds\n127182 playing card king of diamonds\n127183 playing card black joker\n127185 playing card ace of clubs\n127186 playing card two of clubs\n127187 playing card three of clubs\n127188 playing card four of clubs\n127189 playing card five of clubs\n127190 playing card six of clubs\n127191 playing card seven of clubs\n127192 playing card eight of clubs\n127193 playing card nine of clubs\n127194 playing card ten of clubs\n127195 playing card jack of clubs\n127196 playing card knight of clubs\n127197 playing card queen of clubs\n127198 playing card king of clubs\n127199 playing card white joker\n127200 playing card fool\n127201 playing card trump-1\n127202 playing card trump-2\n127203 playing card trump-3\n127204 playing card trump-4\n127205 playing card trump-5\n127206 playing card trump-6\n127207 playing card trump-7\n127208 playing card trump-8\n127209 playing card trump-9\n127210 playing card trump-10\n127211 playing card trump-11\n127212 playing card trump-12\n127213 playing card trump-13\n127214 playing card trump-14\n127215 playing card trump-15\n127216 playing card trump-16\n127217 playing card trump-17\n127218 playing card trump-18\n127219 playing card trump-19\n127220 playing card trump-20\n127221 playing card trump-21\n127232 digit zero full stop\n127233 digit zero comma\n127234 digit one comma\n127235 digit two comma\n127236 digit three comma\n127237 digit four comma\n127238 digit five comma\n127239 digit six comma\n127240 digit seven comma\n127241 digit eight comma\n127242 digit nine comma\n127243 dingbat circled sans-serif digit zero\n127244 dingbat negative circled sans-serif digit zero\n127245 circled zero with slash\n127246 circled anticlockwise arrow\n127247 circled dollar sign with overlaid backslash\n127248 parenthesized latin capital letter a\n127249 parenthesized latin capital letter b\n127250 parenthesized latin capital letter c\n127251 parenthesized latin capital letter d\n127252 parenthesized latin capital letter e\n127253 parenthesized latin capital letter f\n127254 parenthesized latin capital letter g\n127255 parenthesized latin capital letter h\n127256 parenthesized latin capital letter i\n127257 parenthesized latin capital letter j\n127258 parenthesized latin capital letter k\n127259 parenthesized latin capital letter l\n127260 parenthesized latin capital letter m\n127261 parenthesized latin capital letter n\n127262 parenthesized latin capital letter o\n127263 parenthesized latin capital letter p\n127264 parenthesized latin capital letter q\n127265 parenthesized latin capital letter r\n127266 parenthesized latin capital letter s\n127267 parenthesized latin capital letter t\n127268 parenthesized latin capital letter u\n127269 parenthesized latin capital letter v\n127270 parenthesized latin capital letter w\n127271 parenthesized latin capital letter x\n127272 parenthesized latin capital letter y\n127273 parenthesized latin capital letter z\n127274 tortoise shell bracketed latin capital letter s\n127275 circled italic latin capital letter c\n127276 circled italic latin capital letter r\n127277 circled cd\n127278 circled wz\n127279 copyleft symbol\n127280 squared latin capital letter a\n127281 squared latin capital letter b\n127282 squared latin capital letter c\n127283 squared latin capital letter d\n127284 squared latin capital letter e\n127285 squared latin capital letter f\n127286 squared latin capital letter g\n127287 squared latin capital letter h\n127288 squared latin capital letter i\n127289 squared latin capital letter j\n127290 squared latin capital letter k\n127291 squared latin capital letter l\n127292 squared latin capital letter m\n127293 squared latin capital letter n\n127294 squared latin capital letter o\n127295 squared latin capital letter p\n127296 squared latin capital letter q\n127297 squared latin capital letter r\n127298 squared latin capital letter s\n127299 squared latin capital letter t\n127300 squared latin capital letter u\n127301 squared latin capital letter v\n127302 squared latin capital letter w\n127303 squared latin capital letter x\n127304 squared latin capital letter y\n127305 squared latin capital letter z\n127306 squared hv\n127307 squared mv\n127308 squared sd\n127309 squared ss\n127310 squared ppv\n127311 squared wc\n127312 negative circled latin capital letter a\n127313 negative circled latin capital letter b\n127314 negative circled latin capital letter c\n127315 negative circled latin capital letter d\n127316 negative circled latin capital letter e\n127317 negative circled latin capital letter f\n127318 negative circled latin capital letter g\n127319 negative circled latin capital letter h\n127320 negative circled latin capital letter i\n127321 negative circled latin capital letter j\n127322 negative circled latin capital letter k\n127323 negative circled latin capital letter l\n127324 negative circled latin capital letter m\n127325 negative circled latin capital letter n\n127326 negative circled latin capital letter o\n127327 negative circled latin capital letter p\n127328 negative circled latin capital letter q\n127329 negative circled latin capital letter r\n127330 negative circled latin capital letter s\n127331 negative circled latin capital letter t\n127332 negative circled latin capital letter u\n127333 negative circled latin capital letter v\n127334 negative circled latin capital letter w\n127335 negative circled latin capital letter x\n127336 negative circled latin capital letter y\n127337 negative circled latin capital letter z\n127338 raised mc sign\n127339 raised md sign\n127340 raised mr sign\n127341 circled cc\n127342 circled c with overlaid backslash\n127343 circled human figure\n127344 negative squared latin capital letter a\n127345 negative squared latin capital letter b\n127346 negative squared latin capital letter c\n127347 negative squared latin capital letter d\n127348 negative squared latin capital letter e\n127349 negative squared latin capital letter f\n127350 negative squared latin capital letter g\n127351 negative squared latin capital letter h\n127352 negative squared latin capital letter i\n127353 negative squared latin capital letter j\n127354 negative squared latin capital letter k\n127355 negative squared latin capital letter l\n127356 negative squared latin capital letter m\n127357 negative squared latin capital letter n\n127358 negative squared latin capital letter o\n127359 negative squared latin capital letter p\n127360 negative squared latin capital letter q\n127361 negative squared latin capital letter r\n127362 negative squared latin capital letter s\n127363 negative squared latin capital letter t\n127364 negative squared latin capital letter u\n127365 negative squared latin capital letter v\n127366 negative squared latin capital letter w\n127367 negative squared latin capital letter x\n127368 negative squared latin capital letter y\n127369 negative squared latin capital letter z\n127370 crossed negative squared latin capital letter p\n127371 negative squared ic\n127372 negative squared pa\n127373 negative squared sa\n127374 negative squared ab\n127375 negative squared wc\n127376 square dj\n127377 squared cl\n127378 squared cool\n127379 squared free\n127380 squared id\n127381 squared new\n127382 squared ng\n127383 squared ok\n127384 squared sos\n127385 squared up with exclamation mark\n127386 squared vs\n127387 squared three d\n127388 squared second screen\n127389 squared two k\n127390 squared four k\n127391 squared eight k\n127392 squared five point one\n127393 squared seven point one\n127394 squared twenty-two point two\n127395 squared sixty p\n127396 squared one hundred twenty p\n127397 squared latin small letter d\n127398 squared hc\n127399 squared hdr\n127400 squared hi-res\n127401 squared lossless\n127402 squared shv\n127403 squared uhd\n127404 squared vod\n127405 mask work symbol\n127462 regional indicator symbol letter a\n127463 regional indicator symbol letter b\n127464 regional indicator symbol letter c\n127465 regional indicator symbol letter d\n127466 regional indicator symbol letter e\n127467 regional indicator symbol letter f\n127468 regional indicator symbol letter g\n127469 regional indicator symbol letter h\n127470 regional indicator symbol letter i\n127471 regional indicator symbol letter j\n127472 regional indicator symbol letter k\n127473 regional indicator symbol letter l\n127474 regional indicator symbol letter m\n127475 regional indicator symbol letter n\n127476 regional indicator symbol letter o\n127477 regional indicator symbol letter p\n127478 regional indicator symbol letter q\n127479 regional indicator symbol letter r\n127480 regional indicator symbol letter s\n127481 regional indicator symbol letter t\n127482 regional indicator symbol letter u\n127483 regional indicator symbol letter v\n127484 regional indicator symbol letter w\n127485 regional indicator symbol letter x\n127486 regional indicator symbol letter y\n127487 regional indicator symbol letter z\n127488 square hiragana hoka\n127489 squared katakana koko\n127490 squared katakana sa\n127504 squared cjk unified ideograph-624b\n127505 squared cjk unified ideograph-5b57\n127506 squared cjk unified ideograph-53cc\n127507 squared katakana de\n127508 squared cjk unified ideograph-4e8c\n127509 squared cjk unified ideograph-591a\n127510 squared cjk unified ideograph-89e3\n127511 squared cjk unified ideograph-5929\n127512 squared cjk unified ideograph-4ea4\n127513 squared cjk unified ideograph-6620\n127514 squared cjk unified ideograph-7121\n127515 squared cjk unified ideograph-6599\n127516 squared cjk unified ideograph-524d\n127517 squared cjk unified ideograph-5f8c\n127518 squared cjk unified ideograph-518d\n127519 squared cjk unified ideograph-65b0\n127520 squared cjk unified ideograph-521d\n127521 squared cjk unified ideograph-7d42\n127522 squared cjk unified ideograph-751f\n127523 squared cjk unified ideograph-8ca9\n127524 squared cjk unified ideograph-58f0\n127525 squared cjk unified ideograph-5439\n127526 squared cjk unified ideograph-6f14\n127527 squared cjk unified ideograph-6295\n127528 squared cjk unified ideograph-6355\n127529 squared cjk unified ideograph-4e00\n127530 squared cjk unified ideograph-4e09\n127531 squared cjk unified ideograph-904a\n127532 squared cjk unified ideograph-5de6\n127533 squared cjk unified ideograph-4e2d\n127534 squared cjk unified ideograph-53f3\n127535 squared cjk unified ideograph-6307\n127536 squared cjk unified ideograph-8d70\n127537 squared cjk unified ideograph-6253\n127538 squared cjk unified ideograph-7981\n127539 squared cjk unified ideograph-7a7a\n127540 squared cjk unified ideograph-5408\n127541 squared cjk unified ideograph-6e80\n127542 squared cjk unified ideograph-6709\n127543 squared cjk unified ideograph-6708\n127544 squared cjk unified ideograph-7533\n127545 squared cjk unified ideograph-5272\n127546 squared cjk unified ideograph-55b6\n127547 squared cjk unified ideograph-914d\n127552 tortoise shell bracketed cjk unified ideograph-672c\n127553 tortoise shell bracketed cjk unified ideograph-4e09\n127554 tortoise shell bracketed cjk unified ideograph-4e8c\n127555 tortoise shell bracketed cjk unified ideograph-5b89\n127556 tortoise shell bracketed cjk unified ideograph-70b9\n127557 tortoise shell bracketed cjk unified ideograph-6253\n127558 tortoise shell bracketed cjk unified ideograph-76d7\n127559 tortoise shell bracketed cjk unified ideograph-52dd\n127560 tortoise shell bracketed cjk unified ideograph-6557\n127568 circled ideograph advantage\n127569 circled ideograph accept\n127584 rounded symbol for fu\n127585 rounded symbol for lu\n127586 rounded symbol for shou\n127587 rounded symbol for xi\n127588 rounded symbol for shuangxi\n127589 rounded symbol for cai\n127744 cyclone\n127745 foggy\n127746 closed umbrella\n127747 night with stars\n127748 sunrise over mountains\n127749 sunrise\n127750 cityscape at dusk\n127751 sunset over buildings\n127752 rainbow\n127753 bridge at night\n127754 water wave\n127755 volcano\n127756 milky way\n127757 earth globe europe-africa\n127758 earth globe americas\n127759 earth globe asia-australia\n127760 globe with meridians\n127761 new moon symbol\n127762 waxing crescent moon symbol\n127763 first quarter moon symbol\n127764 waxing gibbous moon symbol\n127765 full moon symbol\n127766 waning gibbous moon symbol\n127767 last quarter moon symbol\n127768 waning crescent moon symbol\n127769 crescent moon\n127770 new moon with face\n127771 first quarter moon with face\n127772 last quarter moon with face\n127773 full moon with face\n127774 sun with face\n127775 glowing star\n127776 shooting star\n127777 thermometer\n127778 black droplet\n127779 white sun\n127780 white sun with small cloud\n127781 white sun behind cloud\n127782 white sun behind cloud with rain\n127783 cloud with rain\n127784 cloud with snow\n127785 cloud with lightning\n127786 cloud with tornado\n127787 fog\n127788 wind blowing face\n127789 hot dog\n127790 taco\n127791 burrito\n127792 chestnut\n127793 seedling\n127794 evergreen tree\n127795 deciduous tree\n127796 palm tree\n127797 cactus\n127798 hot pepper\n127799 tulip\n127800 cherry blossom\n127801 rose\n127802 hibiscus\n127803 sunflower\n127804 blossom\n127805 ear of maize\n127806 ear of rice\n127807 herb\n127808 four leaf clover\n127809 maple leaf\n127810 fallen leaf\n127811 leaf fluttering in wind\n127812 mushroom\n127813 tomato\n127814 aubergine\n127815 grapes\n127816 melon\n127817 watermelon\n127818 tangerine\n127819 lemon\n127820 banana\n127821 pineapple\n127822 red apple\n127823 green apple\n127824 pear\n127825 peach\n127826 cherries\n127827 strawberry\n127828 hamburger\n127829 slice of pizza\n127830 meat on bone\n127831 poultry leg\n127832 rice cracker\n127833 rice ball\n127834 cooked rice\n127835 curry and rice\n127836 steaming bowl\n127837 spaghetti\n127838 bread\n127839 french fries\n127840 roasted sweet potato\n127841 dango\n127842 oden\n127843 sushi\n127844 fried shrimp\n127845 fish cake with swirl design\n127846 soft ice cream\n127847 shaved ice\n127848 ice cream\n127849 doughnut\n127850 cookie\n127851 chocolate bar\n127852 candy\n127853 lollipop\n127854 custard\n127855 honey pot\n127856 shortcake\n127857 bento box\n127858 pot of food\n127859 cooking\n127860 fork and knife\n127861 teacup without handle\n127862 sake bottle and cup\n127863 wine glass\n127864 cocktail glass\n127865 tropical drink\n127866 beer mug\n127867 clinking beer mugs\n127868 baby bottle\n127869 fork and knife with plate\n127870 bottle with popping cork\n127871 popcorn\n127872 ribbon\n127873 wrapped present\n127874 birthday cake\n127875 jack-o-lantern\n127876 christmas tree\n127877 father christmas\n127878 fireworks\n127879 firework sparkler\n127880 balloon\n127881 party popper\n127882 confetti ball\n127883 tanabata tree\n127884 crossed flags\n127885 pine decoration\n127886 japanese dolls\n127887 carp streamer\n127888 wind chime\n127889 moon viewing ceremony\n127890 school satchel\n127891 graduation cap\n127892 heart with tip on the left\n127893 bouquet of flowers\n127894 military medal\n127895 reminder ribbon\n127896 musical keyboard with jacks\n127897 studio microphone\n127898 level slider\n127899 control knobs\n127900 beamed ascending musical notes\n127901 beamed descending musical notes\n127902 film frames\n127903 admission tickets\n127904 carousel horse\n127905 ferris wheel\n127906 roller coaster\n127907 fishing pole and fish\n127908 microphone\n127909 movie camera\n127910 cinema\n127911 headphone\n127912 artist palette\n127913 top hat\n127914 circus tent\n127915 ticket\n127916 clapper board\n127917 performing arts\n127918 video game\n127919 direct hit\n127920 slot machine\n127921 billiards\n127922 game die\n127923 bowling\n127924 flower playing cards\n127925 musical note\n127926 multiple musical notes\n127927 saxophone\n127928 guitar\n127929 musical keyboard\n127930 trumpet\n127931 violin\n127932 musical score\n127933 running shirt with sash\n127934 tennis racquet and ball\n127935 ski and ski boot\n127936 basketball and hoop\n127937 chequered flag\n127938 snowboarder\n127939 runner\n127940 surfer\n127941 sports medal\n127942 trophy\n127943 horse racing\n127944 american football\n127945 rugby football\n127946 swimmer\n127947 weight lifter\n127948 golfer\n127949 racing motorcycle\n127950 racing car\n127951 cricket bat and ball\n127952 volleyball\n127953 field hockey stick and ball\n127954 ice hockey stick and puck\n127955 table tennis paddle and ball\n127956 snow capped mountain\n127957 camping\n127958 beach with umbrella\n127959 building construction\n127960 house buildings\n127961 cityscape\n127962 derelict house building\n127963 classical building\n127964 desert\n127965 desert island\n127966 national park\n127967 stadium\n127968 house building\n127969 house with garden\n127970 office building\n127971 japanese post office\n127972 european post office\n127973 hospital\n127974 bank\n127975 automated teller machine\n127976 hotel\n127977 love hotel\n127978 convenience store\n127979 school\n127980 department store\n127981 factory\n127982 izakaya lantern\n127983 japanese castle\n127984 european castle\n127985 white pennant\n127986 black pennant\n127987 waving white flag\n127988 waving black flag\n127989 rosette\n127990 black rosette\n127991 label\n127992 badminton racquet and shuttlecock\n127993 bow and arrow\n127994 amphora\n127995 emoji modifier fitzpatrick type-1-2\n127996 emoji modifier fitzpatrick type-3\n127997 emoji modifier fitzpatrick type-4\n127998 emoji modifier fitzpatrick type-5\n127999 emoji modifier fitzpatrick type-6\n128000 rat\n128001 mouse\n128002 ox\n128003 water buffalo\n128004 cow\n128005 tiger\n128006 leopard\n128007 rabbit\n128008 cat\n128009 dragon\n128010 crocodile\n128011 whale\n128012 snail\n128013 snake\n128014 horse\n128015 ram\n128016 goat\n128017 sheep\n128018 monkey\n128019 rooster\n128020 chicken\n128021 dog\n128022 pig\n128023 boar\n128024 elephant\n128025 octopus\n128026 spiral shell\n128027 bug\n128028 ant\n128029 honeybee\tbee\n128030 lady beetle\n128031 fish\n128032 tropical fish\n128033 blowfish\n128034 turtle\n128035 hatching chick\n128036 baby chick\n128037 front-facing baby chick\n128038 bird\n128039 penguin\n128040 koala\n128041 poodle\n128042 dromedary camel\n128043 bactrian camel\n128044 dolphin\n128045 mouse face\n128046 cow face\n128047 tiger face\n128048 rabbit face\n128049 cat face\n128050 dragon face\n128051 spouting whale\n128052 horse face\n128053 monkey face\n128054 dog face\n128055 pig face\n128056 frog face\n128057 hamster face\n128058 wolf face\n128059 bear face\n128060 panda face\n128061 pig nose\n128062 paw prints\n128063 chipmunk\n128064 eyes\n128065 eye\n128066 ear\n128067 nose\n128068 mouth\n128069 tongue\n128070 white up pointing backhand index\n128071 white down pointing backhand index\n128072 white left pointing backhand index\n128073 white right pointing backhand index\n128074 fisted hand sign\n128075 waving hand sign\n128076 ok hand sign\n128077 thumbs up sign\n128078 thumbs down sign\n128079 clapping hands sign\n128080 open hands sign\n128081 crown\n128082 womans hat\n128083 eyeglasses\n128084 necktie\n128085 t-shirt\n128086 jeans\n128087 dress\n128088 kimono\n128089 bikini\n128090 womans clothes\n128091 purse\n128092 handbag\n128093 pouch\n128094 mans shoe\n128095 athletic shoe\n128096 high-heeled shoe\n128097 womans sandal\n128098 womans boots\n128099 footprints\n128100 bust in silhouette\n128101 busts in silhouette\n128102 boy\n128103 girl\n128104 man\n128105 woman\n128106 family\n128107 man and woman holding hands\n128108 two men holding hands\n128109 two women holding hands\n128110 police officer\n128111 woman with bunny ears\n128112 bride with veil\n128113 person with blond hair\n128114 man with gua pi mao\n128115 man with turban\n128116 older man\n128117 older woman\n128118 baby\n128119 construction worker\n128120 princess\n128121 japanese ogre\n128122 japanese goblin\n128123 ghost\n128124 baby angel\n128125 extraterrestrial alien\n128126 alien monster\n128127 imp\n128128 skull\n128129 information desk person\n128130 guardsman\n128131 dancer\n128132 lipstick\n128133 nail polish\n128134 face massage\n128135 haircut\n128136 barber pole\n128137 syringe\n128138 pill\n128139 kiss mark\n128140 love letter\n128141 ring\n128142 gem stone\tdiamond\n128143 kiss\n128144 bouquet\n128145 couple with heart\n128146 wedding\n128147 beating heart\n128148 broken heart\n128149 two hearts\n128150 sparkling heart\n128151 growing heart\n128152 heart with arrow\n128153 blue heart\n128154 green heart\n128155 yellow heart\n128156 purple heart\n128157 heart with ribbon\n128158 revolving hearts\n128159 heart decoration\n128160 diamond shape with a dot inside\n128161 electric light bulb\n128162 anger symbol\n128163 bomb\n128164 sleeping symbol\n128165 collision symbol\n128166 splashing sweat symbol\n128167 droplet\n128168 dash symbol\n128169 pile of poo\n128170 flexed biceps\n128171 dizzy symbol\n128172 speech balloon\n128173 thought balloon\n128174 white flower\n128175 hundred points symbol\n128176 money bag\n128177 currency exchange\n128178 heavy dollar sign\n128179 credit card\n128180 banknote with yen sign\n128181 banknote with dollar sign\n128182 banknote with euro sign\n128183 banknote with pound sign\n128184 money with wings\n128185 chart with upwards trend and yen sign\n128186 seat\n128187 personal computer\n128188 briefcase\n128189 minidisc\n128190 floppy disk\n128191 optical disc\n128192 dvd\n128193 file folder\n128194 open file folder\n128195 page with curl\n128196 page facing up\n128197 calendar\n128198 tear-off calendar\n128199 card index\n128200 chart with upwards trend\n128201 chart with downwards trend\n128202 bar chart\n128203 clipboard\n128204 pushpin\n128205 round pushpin\n128206 paperclip\n128207 straight ruler\n128208 triangular ruler\n128209 bookmark tabs\n128210 ledger\n128211 notebook\n128212 notebook with decorative cover\n128213 closed book\n128214 open book\n128215 green book\n128216 blue book\n128217 orange book\n128218 books\n128219 name badge\n128220 scroll\n128221 memo\n128222 telephone receiver\n128223 pager\n128224 fax machine\n128225 satellite antenna\n128226 public address loudspeaker\n128227 cheering megaphone\n128228 outbox tray\n128229 inbox tray\n128230 package\n128231 e-mail symbol\n128232 incoming envelope\n128233 envelope with downwards arrow above\n128234 closed mailbox with lowered flag\n128235 closed mailbox with raised flag\n128236 open mailbox with raised flag\n128237 open mailbox with lowered flag\n128238 postbox\n128239 postal horn\n128240 newspaper\n128241 mobile phone\n128242 mobile phone with rightwards arrow at left\n128243 vibration mode\n128244 mobile phone off\n128245 no mobile phones\n128246 antenna with bars\n128247 camera\n128248 camera with flash\n128249 video camera\n128250 television\n128251 radio\n128252 videocassette\n128253 film projector\n128254 portable stereo\n128255 prayer beads\n128256 twisted rightwards arrows\n128257 clockwise rightwards and leftwards open circle arrows\n128258 clockwise rightwards and leftwards open circle arrows with circled one overlay\n128259 clockwise downwards and upwards open circle arrows\n128260 anticlockwise downwards and upwards open circle arrows\n128261 low brightness symbol\n128262 high brightness symbol\n128263 speaker with cancellation stroke\n128264 speaker\n128265 speaker with one sound wave\n128266 speaker with three sound waves\n128267 battery\n128268 electric plug\n128269 left-pointing magnifying glass\n128270 right-pointing magnifying glass\n128271 lock with ink pen\n128272 closed lock with key\n128273 key\n128274 lock\n128275 open lock\n128276 bell\n128277 bell with cancellation stroke\n128278 bookmark\n128279 link symbol\n128280 radio button\n128281 back with leftwards arrow above\n128282 end with leftwards arrow above\n128283 on with exclamation mark with left right arrow above\n128284 soon with rightwards arrow above\n128285 top with upwards arrow above\n128286 no one under eighteen symbol\n128287 keycap ten\n128288 input symbol for latin capital letters\n128289 input symbol for latin small letters\n128290 input symbol for numbers\n128291 input symbol for symbols\n128292 input symbol for latin letters\n128293 fire\n128294 electric torch\n128295 wrench\n128296 hammer\n128297 nut and bolt\n128298 hocho\n128299 pistol\n128300 microscope\n128301 telescope\n128302 crystal ball\n128303 six pointed star with middle dot\n128304 japanese symbol for beginner\n128305 trident emblem\n128306 black square button\n128307 white square button\n128308 large red circle\n128309 large blue circle\n128310 large orange diamond\n128311 large blue diamond\n128312 small orange diamond\n128313 small blue diamond\n128314 up-pointing red triangle\n128315 down-pointing red triangle\n128316 up-pointing small red triangle\n128317 down-pointing small red triangle\n128318 lower right shadowed white circle\n128319 upper right shadowed white circle\n128320 circled cross pommee\n128321 cross pommee with half-circle below\n128322 cross pommee\n128323 notched left semicircle with three dots\n128324 notched right semicircle with three dots\n128325 symbol for marks chapter\n128326 white latin cross\n128327 heavy latin cross\n128328 celtic cross\n128329 om symbol\n128330 dove of peace\n128331 kaaba\n128332 mosque\n128333 synagogue\n128334 menorah with nine branches\n128335 bowl of hygieia\n128336 clock face one oclock\n128337 clock face two oclock\n128338 clock face three oclock\n128339 clock face four oclock\n128340 clock face five oclock\n128341 clock face six oclock\n128342 clock face seven oclock\n128343 clock face eight oclock\n128344 clock face nine oclock\n128345 clock face ten oclock\n128346 clock face eleven oclock\n128347 clock face twelve oclock\n128348 clock face one-thirty\n128349 clock face two-thirty\n128350 clock face three-thirty\n128351 clock face four-thirty\n128352 clock face five-thirty\n128353 clock face six-thirty\n128354 clock face seven-thirty\n128355 clock face eight-thirty\n128356 clock face nine-thirty\n128357 clock face ten-thirty\n128358 clock face eleven-thirty\n128359 clock face twelve-thirty\n128360 right speaker\n128361 right speaker with one sound wave\n128362 right speaker with three sound waves\n128363 bullhorn\n128364 bullhorn with sound waves\n128365 ringing bell\n128366 book\n128367 candle\n128368 mantelpiece clock\n128369 black skull and crossbones\n128370 no piracy\n128371 hole\n128372 man in business suit levitating\n128373 sleuth or spy\n128374 dark sunglasses\n128375 spider\n128376 spider web\n128377 joystick\n128378 man dancing\n128379 left hand telephone receiver\n128380 telephone receiver with page\n128381 right hand telephone receiver\n128382 white touchtone telephone\n128383 black touchtone telephone\n128384 telephone on top of modem\n128385 clamshell mobile phone\n128386 back of envelope\n128387 stamped envelope\n128388 envelope with lightning\n128389 flying envelope\n128390 pen over stamped envelope\n128391 linked paperclips\n128392 black pushpin\n128393 lower left pencil\n128394 lower left ballpoint pen\n128395 lower left fountain pen\n128396 lower left paintbrush\n128397 lower left crayon\n128398 left writing hand\n128399 turned ok hand sign\n128400 raised hand with fingers splayed\n128401 reversed raised hand with fingers splayed\n128402 reversed thumbs up sign\n128403 reversed thumbs down sign\n128404 reversed victory hand\n128405 reversed hand with middle finger extended\n128406 raised hand with part between middle and ring fingers\n128407 white down pointing left hand index\n128408 sideways white left pointing index\n128409 sideways white right pointing index\n128410 sideways black left pointing index\n128411 sideways black right pointing index\n128412 black left pointing backhand index\n128413 black right pointing backhand index\n128414 sideways white up pointing index\n128415 sideways white down pointing index\n128416 sideways black up pointing index\n128417 sideways black down pointing index\n128418 black up pointing backhand index\n128419 black down pointing backhand index\n128420 black heart\n128421 desktop computer\n128422 keyboard and mouse\n128423 three networked computers\n128424 printer\n128425 pocket calculator\n128426 black hard shell floppy disk\n128427 white hard shell floppy disk\n128428 soft shell floppy disk\n128429 tape cartridge\n128430 wired keyboard\n128431 one button mouse\n128432 two button mouse\n128433 three button mouse\n128434 trackball\n128435 old personal computer\n128436 hard disk\n128437 screen\n128438 printer icon\n128439 fax icon\n128440 optical disc icon\n128441 document with text\n128442 document with text and picture\n128443 document with picture\n128444 frame with picture\n128445 frame with tiles\n128446 frame with an x\n128447 black folder\n128448 folder\n128449 open folder\n128450 card index dividers\n128451 card file box\n128452 file cabinet\n128453 empty note\n128454 empty note page\n128455 empty note pad\n128456 note\n128457 note page\n128458 note pad\n128459 empty document\n128460 empty page\n128461 empty pages\n128462 document\n128463 page\n128464 pages\n128465 wastebasket\n128466 spiral note pad\n128467 spiral calendar pad\n128468 desktop window\n128469 minimize\n128470 maximize\n128471 overlap\n128472 clockwise right and left semicircle arrows\n128473 cancellation x\n128474 increase font size symbol\n128475 decrease font size symbol\n128476 compression\n128477 old key\n128478 rolled-up newspaper\n128479 page with circled text\n128480 stock chart\n128481 dagger knife\n128482 lips\n128483 speaking head in silhouette\n128484 three rays above\n128485 three rays below\n128486 three rays left\n128487 three rays right\n128488 left speech bubble\n128489 right speech bubble\n128490 two speech bubbles\n128491 three speech bubbles\n128492 left thought bubble\n128493 right thought bubble\n128494 left anger bubble\n128495 right anger bubble\n128496 mood bubble\n128497 lightning mood bubble\n128498 lightning mood\n128499 ballot box with ballot\n128500 ballot script x\n128501 ballot box with script x\n128502 ballot bold script x\n128503 ballot box with bold script x\n128504 light check mark\n128505 ballot box with bold check\n128506 world map\n128507 mount fuji\n128508 tokyo tower\n128509 statue of liberty\n128510 silhouette of japan\n128511 moyai\n128512 grinning face\n128513 grinning face with smiling eyes\n128514 face with tears of joy\n128515 smiling face with open mouth\n128516 smiling face with open mouth and smiling eyes\n128517 smiling face with open mouth and cold sweat\n128518 smiling face with open mouth and tightly-closed eyes\n128519 smiling face with halo\n128520 smiling face with horns\n128521 winking face\n128522 smiling face with smiling eyes\n128523 face savouring delicious food\n128524 relieved face\n128525 smiling face with heart-shaped eyes\n128526 smiling face with sunglasses\n128527 smirking face\n128528 neutral face\n128529 expressionless face\n128530 unamused face\n128531 face with cold sweat\n128532 pensive face\n128533 confused face\n128534 confounded face\n128535 kissing face\n128536 face throwing a kiss\n128537 kissing face with smiling eyes\n128538 kissing face with closed eyes\n128539 face with stuck-out tongue\n128540 face with stuck-out tongue and winking eye\n128541 face with stuck-out tongue and tightly-closed eyes\n128542 disappointed face\n128543 worried face\n128544 angry face\n128545 pouting face\n128546 crying face\n128547 persevering face\n128548 face with look of triumph\n128549 disappointed but relieved face\n128550 frowning face with open mouth\n128551 anguished face\n128552 fearful face\n128553 weary face\n128554 sleepy face\n128555 tired face\n128556 grimacing face\n128557 loudly crying face\n128558 face with open mouth\n128559 hushed face\n128560 face with open mouth and cold sweat\n128561 face screaming in fear\n128562 astonished face\n128563 flushed face\n128564 sleeping face\n128565 dizzy face\n128566 face without mouth\n128567 face with medical mask\n128568 grinning cat face with smiling eyes\n128569 cat face with tears of joy\n128570 smiling cat face with open mouth\n128571 smiling cat face with heart-shaped eyes\n128572 cat face with wry smile\n128573 kissing cat face with closed eyes\n128574 pouting cat face\n128575 crying cat face\n128576 weary cat face\n128577 slightly frowning face\n128578 slightly smiling face\n128579 upside-down face\n128580 face with rolling eyes\n128581 face with no good gesture\n128582 face with ok gesture\n128583 person bowing deeply\n128584 see-no-evil monkey\n128585 hear-no-evil monkey\n128586 speak-no-evil monkey\n128587 happy person raising one hand\n128588 person raising both hands in celebration\n128589 person frowning\n128590 person with pouting face\n128591 person with folded hands\n128592 north west pointing leaf\n128593 south west pointing leaf\n128594 north east pointing leaf\n128595 south east pointing leaf\n128596 turned north west pointing leaf\n128597 turned south west pointing leaf\n128598 turned north east pointing leaf\n128599 turned south east pointing leaf\n128600 north west pointing vine leaf\n128601 south west pointing vine leaf\n128602 north east pointing vine leaf\n128603 south east pointing vine leaf\n128604 heavy north west pointing vine leaf\n128605 heavy south west pointing vine leaf\n128606 heavy north east pointing vine leaf\n128607 heavy south east pointing vine leaf\n128608 north west pointing bud\n128609 south west pointing bud\n128610 north east pointing bud\n128611 south east pointing bud\n128612 heavy north west pointing bud\n128613 heavy south west pointing bud\n128614 heavy north east pointing bud\n128615 heavy south east pointing bud\n128616 hollow quilt square ornament\n128617 hollow quilt square ornament in black square\n128618 solid quilt square ornament\n128619 solid quilt square ornament in black square\n128620 leftwards rocket\n128621 upwards rocket\n128622 rightwards rocket\n128623 downwards rocket\n128624 script ligature et ornament\n128625 heavy script ligature et ornament\n128626 ligature open et ornament\n128627 heavy ligature open et ornament\n128628 heavy ampersand ornament\n128629 swash ampersand ornament\n128630 sans-serif heavy double turned comma quotation mark ornament\n128631 sans-serif heavy double comma quotation mark ornament\n128632 sans-serif heavy low double comma quotation mark ornament\n128633 heavy interrobang ornament\n128634 sans-serif interrobang ornament\n128635 heavy sans-serif interrobang ornament\n128636 very heavy solidus\n128637 very heavy reverse solidus\n128638 checker board\n128639 reverse checker board\n128640 rocket\n128641 helicopter\n128642 steam locomotive\n128643 railway car\n128644 high-speed train\n128645 high-speed train with bullet nose\n128646 train\n128647 metro\n128648 light rail\n128649 station\n128650 tram\n128651 tram car\n128652 bus\n128653 oncoming bus\n128654 trolleybus\n128655 bus stop\n128656 minibus\n128657 ambulance\n128658 fire engine\n128659 police car\n128660 oncoming police car\n128661 taxi\n128662 oncoming taxi\n128663 automobile\n128664 oncoming automobile\n128665 recreational vehicle\n128666 delivery truck\n128667 articulated lorry\n128668 tractor\n128669 monorail\n128670 mountain railway\n128671 suspension railway\n128672 mountain cableway\n128673 aerial tramway\n128674 ship\n128675 rowboat\n128676 speedboat\n128677 horizontal traffic light\n128678 vertical traffic light\n128679 construction sign\n128680 police cars revolving light\n128681 triangular flag on post\n128682 door\n128683 no entry sign\n128684 smoking symbol\n128685 no smoking symbol\n128686 put litter in its place symbol\n128687 do not litter symbol\n128688 potable water symbol\n128689 non-potable water symbol\n128690 bicycle\n128691 no bicycles\n128692 bicyclist\n128693 mountain bicyclist\n128694 pedestrian\n128695 no pedestrians\n128696 children crossing\n128697 mens symbol\n128698 womens symbol\n128699 restroom\n128700 baby symbol\n128701 toilet\n128702 water closet\n128703 shower\n128704 bath\n128705 bathtub\n128706 passport control\n128707 customs\n128708 baggage claim\n128709 left luggage\n128710 triangle with rounded corners\n128711 prohibited sign\n128712 circled information source\n128713 boys symbol\n128714 girls symbol\n128715 couch and lamp\n128716 sleeping accommodation\n128717 shopping bags\n128718 bellhop bell\n128719 bed\n128720 place of worship\n128721 octagonal sign\n128722 shopping trolley\n128723 stupa\n128724 pagoda\n128725 hindu temple\n128726 hut\n128727 elevator\n128728 landslide\n128732 wireless\n128733 playground slide\n128734 wheel\n128735 ring buoy\n128736 hammer and wrench\n128737 shield\n128738 oil drum\n128739 motorway\n128740 railway track\n128741 motor boat\n128742 up-pointing military airplane\n128743 up-pointing airplane\n128744 up-pointing small airplane\n128745 small airplane\n128746 northeast-pointing airplane\n128747 airplane departure\n128748 airplane arriving\n128752 satellite\n128753 oncoming fire engine\n128754 diesel locomotive\n128755 passenger ship\n128756 scooter\n128757 motor scooter\n128758 canoe\n128759 sled\n128760 flying saucer\n128761 skateboard\n128762 auto rickshaw\n128763 pickup truck\n128764 roller skate\n128768 alchemical symbol for quintessence\n128769 alchemical symbol for air\n128770 alchemical symbol for fire\n128771 alchemical symbol for earth\n128772 alchemical symbol for water\n128773 alchemical symbol for aquafortis\n128774 alchemical symbol for aqua regia\n128775 alchemical symbol for aqua regia-2\n128776 alchemical symbol for aqua vitae\n128777 alchemical symbol for aqua vitae-2\n128778 alchemical symbol for vinegar\n128779 alchemical symbol for vinegar-2\n128780 alchemical symbol for vinegar-3\n128781 alchemical symbol for sulfur\n128782 alchemical symbol for philosophers sulfur\n128783 alchemical symbol for black sulfur\n128784 alchemical symbol for mercury sublimate\n128785 alchemical symbol for mercury sublimate-2\n128786 alchemical symbol for mercury sublimate-3\n128787 alchemical symbol for cinnabar\n128788 alchemical symbol for salt\n128789 alchemical symbol for nitre\n128790 alchemical symbol for vitriol\n128791 alchemical symbol for vitriol-2\n128792 alchemical symbol for rock salt\n128793 alchemical symbol for rock salt-2\n128794 alchemical symbol for gold\n128795 alchemical symbol for silver\n128796 alchemical symbol for iron ore\n128797 alchemical symbol for iron ore-2\n128798 alchemical symbol for crocus of iron\n128799 alchemical symbol for regulus of iron\n128800 alchemical symbol for copper ore\n128801 alchemical symbol for iron-copper ore\n128802 alchemical symbol for sublimate of copper\n128803 alchemical symbol for crocus of copper\n128804 alchemical symbol for crocus of copper-2\n128805 alchemical symbol for copper antimoniate\n128806 alchemical symbol for salt of copper antimoniate\n128807 alchemical symbol for sublimate of salt of copper\n128808 alchemical symbol for verdigris\n128809 alchemical symbol for tin ore\n128810 alchemical symbol for lead ore\n128811 alchemical symbol for antimony ore\n128812 alchemical symbol for sublimate of antimony\n128813 alchemical symbol for salt of antimony\n128814 alchemical symbol for sublimate of salt of antimony\n128815 alchemical symbol for vinegar of antimony\n128816 alchemical symbol for regulus of antimony\n128817 alchemical symbol for regulus of antimony-2\n128818 alchemical symbol for regulus\n128819 alchemical symbol for regulus-2\n128820 alchemical symbol for regulus-3\n128821 alchemical symbol for regulus-4\n128822 alchemical symbol for alkali\n128823 alchemical symbol for alkali-2\n128824 alchemical symbol for marcasite\n128825 alchemical symbol for sal-ammoniac\n128826 alchemical symbol for arsenic\n128827 alchemical symbol for realgar\n128828 alchemical symbol for realgar-2\n128829 alchemical symbol for auripigment\n128830 alchemical symbol for bismuth ore\n128831 alchemical symbol for tartar\n128832 alchemical symbol for tartar-2\n128833 alchemical symbol for quick lime\n128834 alchemical symbol for borax\n128835 alchemical symbol for borax-2\n128836 alchemical symbol for borax-3\n128837 alchemical symbol for alum\n128838 alchemical symbol for oil\n128839 alchemical symbol for spirit\n128840 alchemical symbol for tincture\n128841 alchemical symbol for gum\n128842 alchemical symbol for wax\n128843 alchemical symbol for powder\n128844 alchemical symbol for calx\n128845 alchemical symbol for tutty\n128846 alchemical symbol for caput mortuum\n128847 alchemical symbol for scepter of jove\n128848 alchemical symbol for caduceus\n128849 alchemical symbol for trident\n128850 alchemical symbol for starred trident\n128851 alchemical symbol for lodestone\n128852 alchemical symbol for soap\n128853 alchemical symbol for urine\n128854 alchemical symbol for horse dung\n128855 alchemical symbol for ashes\n128856 alchemical symbol for pot ashes\n128857 alchemical symbol for brick\n128858 alchemical symbol for powdered brick\n128859 alchemical symbol for amalgam\n128860 alchemical symbol for stratum super stratum\n128861 alchemical symbol for stratum super stratum-2\n128862 alchemical symbol for sublimation\n128863 alchemical symbol for precipitate\n128864 alchemical symbol for distill\n128865 alchemical symbol for dissolve\n128866 alchemical symbol for dissolve-2\n128867 alchemical symbol for purify\n128868 alchemical symbol for putrefaction\n128869 alchemical symbol for crucible\n128870 alchemical symbol for crucible-2\n128871 alchemical symbol for crucible-3\n128872 alchemical symbol for crucible-4\n128873 alchemical symbol for crucible-5\n128874 alchemical symbol for alembic\n128875 alchemical symbol for bath of mary\n128876 alchemical symbol for bath of vapours\n128877 alchemical symbol for retort\n128878 alchemical symbol for hour\n128879 alchemical symbol for night\n128880 alchemical symbol for day-night\n128881 alchemical symbol for month\n128882 alchemical symbol for half dram\n128883 alchemical symbol for half ounce\n128884 lot of fortune\n128885 occultation\n128886 lunar eclipse\n128887 vesta form two\n128888 astraea form two\n128889 hygiea form two\n128890 parthenope form two\n128891 haumea\n128892 makemake\n128893 gonggong\n128894 quaoar\n128895 orcus\n128896 black left-pointing isosceles right triangle\n128897 black up-pointing isosceles right triangle\n128898 black right-pointing isosceles right triangle\n128899 black down-pointing isosceles right triangle\n128900 black slightly small circle\n128901 medium bold white circle\n128902 bold white circle\n128903 heavy white circle\n128904 very heavy white circle\n128905 extremely heavy white circle\n128906 white circle containing black small circle\n128907 round target\n128908 black tiny square\n128909 black slightly small square\n128910 light white square\n128911 medium white square\n128912 bold white square\n128913 heavy white square\n128914 very heavy white square\n128915 extremely heavy white square\n128916 white square containing black very small square\n128917 white square containing black medium square\n128918 square target\n128919 black tiny diamond\n128920 black very small diamond\n128921 black medium small diamond\n128922 white diamond containing black very small diamond\n128923 white diamond containing black medium diamond\n128924 diamond target\n128925 black tiny lozenge\n128926 black very small lozenge\n128927 black medium small lozenge\n128928 white lozenge containing black small lozenge\n128929 thin greek cross\n128930 light greek cross\n128931 medium greek cross\n128932 bold greek cross\n128933 very bold greek cross\n128934 very heavy greek cross\n128935 extremely heavy greek cross\n128936 thin saltire\n128937 light saltire\n128938 medium saltire\n128939 bold saltire\n128940 heavy saltire\n128941 very heavy saltire\n128942 extremely heavy saltire\n128943 light five spoked asterisk\n128944 medium five spoked asterisk\n128945 bold five spoked asterisk\n128946 heavy five spoked asterisk\n128947 very heavy five spoked asterisk\n128948 extremely heavy five spoked asterisk\n128949 light six spoked asterisk\n128950 medium six spoked asterisk\n128951 bold six spoked asterisk\n128952 heavy six spoked asterisk\n128953 very heavy six spoked asterisk\n128954 extremely heavy six spoked asterisk\n128955 light eight spoked asterisk\n128956 medium eight spoked asterisk\n128957 bold eight spoked asterisk\n128958 heavy eight spoked asterisk\n128959 very heavy eight spoked asterisk\n128960 light three pointed black star\n128961 medium three pointed black star\n128962 three pointed black star\n128963 medium three pointed pinwheel star\n128964 light four pointed black star\n128965 medium four pointed black star\n128966 four pointed black star\n128967 medium four pointed pinwheel star\n128968 reverse light four pointed pinwheel star\n128969 light five pointed black star\n128970 heavy five pointed black star\n128971 medium six pointed black star\n128972 heavy six pointed black star\n128973 six pointed pinwheel star\n128974 medium eight pointed black star\n128975 heavy eight pointed black star\n128976 very heavy eight pointed black star\n128977 heavy eight pointed pinwheel star\n128978 light twelve pointed black star\n128979 heavy twelve pointed black star\n128980 heavy twelve pointed pinwheel star\n128981 circled triangle\n128982 negative circled triangle\n128983 circled square\n128984 negative circled square\n128985 nine pointed white star\n128992 large orange circle\n128993 large yellow circle\n128994 large green circle\n128995 large purple circle\n128996 large brown circle\n128997 large red square\n128998 large blue square\n128999 large orange square\n129000 large yellow square\n129001 large green square\n129002 large purple square\n129003 large brown square\n129008 heavy equals sign\n129024 leftwards arrow with small triangle arrowhead\n129025 upwards arrow with small triangle arrowhead\n129026 rightwards arrow with small triangle arrowhead\n129027 downwards arrow with small triangle arrowhead\n129028 leftwards arrow with medium triangle arrowhead\n129029 upwards arrow with medium triangle arrowhead\n129030 rightwards arrow with medium triangle arrowhead\n129031 downwards arrow with medium triangle arrowhead\n129032 leftwards arrow with large triangle arrowhead\n129033 upwards arrow with large triangle arrowhead\n129034 rightwards arrow with large triangle arrowhead\n129035 downwards arrow with large triangle arrowhead\n129040 leftwards arrow with small equilateral arrowhead\n129041 upwards arrow with small equilateral arrowhead\n129042 rightwards arrow with small equilateral arrowhead\n129043 downwards arrow with small equilateral arrowhead\n129044 leftwards arrow with equilateral arrowhead\n129045 upwards arrow with equilateral arrowhead\n129046 rightwards arrow with equilateral arrowhead\n129047 downwards arrow with equilateral arrowhead\n129048 heavy leftwards arrow with equilateral arrowhead\n129049 heavy upwards arrow with equilateral arrowhead\n129050 heavy rightwards arrow with equilateral arrowhead\n129051 heavy downwards arrow with equilateral arrowhead\n129052 heavy leftwards arrow with large equilateral arrowhead\n129053 heavy upwards arrow with large equilateral arrowhead\n129054 heavy rightwards arrow with large equilateral arrowhead\n129055 heavy downwards arrow with large equilateral arrowhead\n129056 leftwards triangle-headed arrow with narrow shaft\n129057 upwards triangle-headed arrow with narrow shaft\n129058 rightwards triangle-headed arrow with narrow shaft\n129059 downwards triangle-headed arrow with narrow shaft\n129060 leftwards triangle-headed arrow with medium shaft\n129061 upwards triangle-headed arrow with medium shaft\n129062 rightwards triangle-headed arrow with medium shaft\n129063 downwards triangle-headed arrow with medium shaft\n129064 leftwards triangle-headed arrow with bold shaft\n129065 upwards triangle-headed arrow with bold shaft\n129066 rightwards triangle-headed arrow with bold shaft\n129067 downwards triangle-headed arrow with bold shaft\n129068 leftwards triangle-headed arrow with heavy shaft\n129069 upwards triangle-headed arrow with heavy shaft\n129070 rightwards triangle-headed arrow with heavy shaft\n129071 downwards triangle-headed arrow with heavy shaft\n129072 leftwards triangle-headed arrow with very heavy shaft\n129073 upwards triangle-headed arrow with very heavy shaft\n129074 rightwards triangle-headed arrow with very heavy shaft\n129075 downwards triangle-headed arrow with very heavy shaft\n129076 leftwards finger-post arrow\n129077 upwards finger-post arrow\n129078 rightwards finger-post arrow\n129079 downwards finger-post arrow\n129080 leftwards squared arrow\n129081 upwards squared arrow\n129082 rightwards squared arrow\n129083 downwards squared arrow\n129084 leftwards compressed arrow\n129085 upwards compressed arrow\n129086 rightwards compressed arrow\n129087 downwards compressed arrow\n129088 leftwards heavy compressed arrow\n129089 upwards heavy compressed arrow\n129090 rightwards heavy compressed arrow\n129091 downwards heavy compressed arrow\n129092 leftwards heavy arrow\n129093 upwards heavy arrow\n129094 rightwards heavy arrow\n129095 downwards heavy arrow\n129104 leftwards sans-serif arrow\n129105 upwards sans-serif arrow\n129106 rightwards sans-serif arrow\n129107 downwards sans-serif arrow\n129108 north west sans-serif arrow\n129109 north east sans-serif arrow\n129110 south east sans-serif arrow\n129111 south west sans-serif arrow\n129112 left right sans-serif arrow\n129113 up down sans-serif arrow\n129120 wide-headed leftwards light barb arrow\n129121 wide-headed upwards light barb arrow\n129122 wide-headed rightwards light barb arrow\n129123 wide-headed downwards light barb arrow\n129124 wide-headed north west light barb arrow\n129125 wide-headed north east light barb arrow\n129126 wide-headed south east light barb arrow\n129127 wide-headed south west light barb arrow\n129128 wide-headed leftwards barb arrow\n129129 wide-headed upwards barb arrow\n129130 wide-headed rightwards barb arrow\n129131 wide-headed downwards barb arrow\n129132 wide-headed north west barb arrow\n129133 wide-headed north east barb arrow\n129134 wide-headed south east barb arrow\n129135 wide-headed south west barb arrow\n129136 wide-headed leftwards medium barb arrow\n129137 wide-headed upwards medium barb arrow\n129138 wide-headed rightwards medium barb arrow\n129139 wide-headed downwards medium barb arrow\n129140 wide-headed north west medium barb arrow\n129141 wide-headed north east medium barb arrow\n129142 wide-headed south east medium barb arrow\n129143 wide-headed south west medium barb arrow\n129144 wide-headed leftwards heavy barb arrow\n129145 wide-headed upwards heavy barb arrow\n129146 wide-headed rightwards heavy barb arrow\n129147 wide-headed downwards heavy barb arrow\n129148 wide-headed north west heavy barb arrow\n129149 wide-headed north east heavy barb arrow\n129150 wide-headed south east heavy barb arrow\n129151 wide-headed south west heavy barb arrow\n129152 wide-headed leftwards very heavy barb arrow\n129153 wide-headed upwards very heavy barb arrow\n129154 wide-headed rightwards very heavy barb arrow\n129155 wide-headed downwards very heavy barb arrow\n129156 wide-headed north west very heavy barb arrow\n129157 wide-headed north east very heavy barb arrow\n129158 wide-headed south east very heavy barb arrow\n129159 wide-headed south west very heavy barb arrow\n129168 leftwards triangle arrowhead\n129169 upwards triangle arrowhead\n129170 rightwards triangle arrowhead\n129171 downwards triangle arrowhead\n129172 leftwards white arrow within triangle arrowhead\n129173 upwards white arrow within triangle arrowhead\n129174 rightwards white arrow within triangle arrowhead\n129175 downwards white arrow within triangle arrowhead\n129176 leftwards arrow with notched tail\n129177 upwards arrow with notched tail\n129178 rightwards arrow with notched tail\n129179 downwards arrow with notched tail\n129180 heavy arrow shaft width one\n129181 heavy arrow shaft width two thirds\n129182 heavy arrow shaft width one half\n129183 heavy arrow shaft width one third\n129184 leftwards bottom-shaded white arrow\n129185 rightwards bottom shaded white arrow\n129186 leftwards top shaded white arrow\n129187 rightwards top shaded white arrow\n129188 leftwards left-shaded white arrow\n129189 rightwards right-shaded white arrow\n129190 leftwards right-shaded white arrow\n129191 rightwards left-shaded white arrow\n129192 leftwards back-tilted shadowed white arrow\n129193 rightwards back-tilted shadowed white arrow\n129194 leftwards front-tilted shadowed white arrow\n129195 rightwards front-tilted shadowed white arrow\n129196 white arrow shaft width one\n129197 white arrow shaft width two thirds\n129200 arrow pointing upwards then north west\n129201 arrow pointing rightwards then curving south west\n129202 rightwards arrow with lower hook\n129203 downwards black arrow to bar\n129204 negative squared leftwards arrow\n129205 negative squared upwards arrow\n129206 negative squared rightwards arrow\n129207 negative squared downwards arrow\n129208 north west arrow from bar\n129209 north east arrow from bar\n129210 south east arrow from bar\n129211 south west arrow from bar\n129216 leftwards arrow from downwards arrow\n129217 rightwards arrow from downwards arrow\n129232 long rightwards arrow over long leftwards arrow\n129233 long rightwards harpoon over long leftwards harpoon\n129234 long rightwards harpoon above short leftwards harpoon\n129235 short rightwards harpoon above long leftwards harpoon\n129236 long leftwards harpoon above short rightwards harpoon\n129237 short leftwards harpoon above long rightwards harpoon\n129238 long rightwards arrow through x\n129239 long rightwards arrow with double slash\n129240 long left right arrow with dependent lobe\n129280 circled cross formee with four dots\n129281 circled cross formee with two dots\n129282 circled cross formee\n129283 left half circle with four dots\n129284 left half circle with three dots\n129285 left half circle with two dots\n129286 left half circle with dot\n129287 left half circle\n129288 downward facing hook\n129289 downward facing notched hook\n129290 downward facing hook with dot\n129291 downward facing notched hook with dot\n129292 pinched fingers\n129293 white heart\n129294 brown heart\n129295 pinching hand\n129296 zipper-mouth face\n129297 money-mouth face\n129298 face with thermometer\n129299 nerd face\n129300 thinking face\n129301 face with head-bandage\n129302 robot face\n129303 hugging face\n129304 sign of the horns\n129305 call me hand\n129306 raised back of hand\n129307 left-facing fist\n129308 right-facing fist\n129309 handshake\n129310 hand with index and middle fingers crossed\n129311 i love you hand sign\n129312 face with cowboy hat\n129313 clown face\n129314 nauseated face\n129315 rolling on the floor laughing\n129316 drooling face\n129317 lying face\n129318 face palm\n129319 sneezing face\n129320 face with one eyebrow raised\n129321 grinning face with star eyes\n129322 grinning face with one large and one small eye\n129323 face with finger covering closed lips\n129324 serious face with symbols covering mouth\n129325 smiling face with smiling eyes and hand covering mouth\n129326 face with open mouth vomiting\n129327 shocked face with exploding head\n129328 pregnant woman\n129329 breast-feeding\n129330 palms up together\n129331 selfie\n129332 prince\n129333 man in tuxedo\n129334 mother christmas\n129335 shrug\n129336 person doing cartwheel\n129337 juggling\n129338 fencer\n129339 modern pentathlon\n129340 wrestlers\n129341 water polo\n129342 handball\n129343 diving mask\n129344 wilted flower\n129345 drum with drumsticks\n129346 clinking glasses\n129347 tumbler glass\n129348 spoon\n129349 goal net\n129350 rifle\n129351 first place medal\n129352 second place medal\n129353 third place medal\n129354 boxing glove\n129355 martial arts uniform\n129356 curling stone\n129357 lacrosse stick and ball\n129358 softball\n129359 flying disc\n129360 croissant\n129361 avocado\n129362 cucumber\n129363 bacon\n129364 potato\n129365 carrot\n129366 baguette bread\n129367 green salad\n129368 shallow pan of food\n129369 stuffed flatbread\n129370 egg\n129371 glass of milk\n129372 peanuts\n129373 kiwifruit\n129374 pancakes\n129375 dumpling\n129376 fortune cookie\n129377 takeout box\n129378 chopsticks\n129379 bowl with spoon\n129380 cup with straw\n129381 coconut\n129382 broccoli\n129383 pie\n129384 pretzel\n129385 cut of meat\n129386 sandwich\n129387 canned food\n129388 leafy green\n129389 mango\n129390 moon cake\n129391 bagel\n129392 smiling face with smiling eyes and three hearts\n129393 yawning face\n129394 smiling face with tear\n129395 face with party horn and party hat\n129396 face with uneven eyes and wavy mouth\n129397 overheated face\n129398 freezing face\n129399 ninja\n129400 disguised face\n129401 face holding back tears\n129402 face with pleading eyes\n129403 sari\n129404 lab coat\n129405 goggles\n129406 hiking boot\n129407 flat shoe\n129408 crab\n129409 lion face\n129410 scorpion\n129411 turkey\n129412 unicorn face\n129413 eagle\n129414 duck\n129415 bat\n129416 shark\n129417 owl\n129418 fox face\n129419 butterfly\n129420 deer\n129421 gorilla\n129422 lizard\n129423 rhinoceros\n129424 shrimp\n129425 squid\n129426 giraffe face\n129427 zebra face\n129428 hedgehog\n129429 sauropod\n129430 t-rex\n129431 cricket\n129432 kangaroo\n129433 llama\n129434 peacock\n129435 hippopotamus\n129436 parrot\n129437 raccoon\n129438 lobster\n129439 mosquito\n129440 microbe\n129441 badger\n129442 swan\n129443 mammoth\n129444 dodo\n129445 sloth\n129446 otter\n129447 orangutan\n129448 skunk\n129449 flamingo\n129450 oyster\n129451 beaver\n129452 bison\n129453 seal\n129454 guide dog\n129455 probing cane\n129456 emoji component red hair\n129457 emoji component curly hair\n129458 emoji component bald\n129459 emoji component white hair\n129460 bone\n129461 leg\n129462 foot\n129463 tooth\n129464 superhero\n129465 supervillain\n129466 safety vest\n129467 ear with hearing aid\n129468 motorized wheelchair\n129469 manual wheelchair\n129470 mechanical arm\n129471 mechanical leg\n129472 cheese wedge\n129473 cupcake\n129474 salt shaker\n129475 beverage box\n129476 garlic\n129477 onion\n129478 falafel\n129479 waffle\n129480 butter\n129481 mate drink\n129482 ice cube\n129483 bubble tea\n129484 troll\n129485 standing person\n129486 kneeling person\n129487 deaf person\n129488 face with monocle\n129489 adult\n129490 child\n129491 older adult\n129492 bearded person\n129493 person with headscarf\n129494 person in steamy room\n129495 person climbing\n129496 person in lotus position\n129497 mage\n129498 fairy\n129499 vampire\n129500 merperson\n129501 elf\n129502 genie\n129503 zombie\n129504 brain\n129505 orange heart\n129506 billed cap\n129507 scarf\n129508 gloves\n129509 coat\n129510 socks\n129511 red gift envelope\n129512 firecracker\n129513 jigsaw puzzle piece\n129514 test tube\n129515 petri dish\n129516 dna double helix\n129517 compass\n129518 abacus\n129519 fire extinguisher\n129520 toolbox\n129521 brick\n129522 magnet\n129523 luggage\n129524 lotion bottle\n129525 spool of thread\n129526 ball of yarn\n129527 safety pin\n129528 teddy bear\n129529 broom\n129530 basket\n129531 roll of paper\n129532 bar of soap\n129533 sponge\n129534 receipt\n129535 nazar amulet\n129536 neutral chess king\n129537 neutral chess queen\n129538 neutral chess rook\n129539 neutral chess bishop\n129540 neutral chess knight\n129541 neutral chess pawn\n129542 white chess knight rotated forty-five degrees\n129543 black chess knight rotated forty-five degrees\n129544 neutral chess knight rotated forty-five degrees\n129545 white chess king rotated ninety degrees\n129546 white chess queen rotated ninety degrees\n129547 white chess rook rotated ninety degrees\n129548 white chess bishop rotated ninety degrees\n129549 white chess knight rotated ninety degrees\n129550 white chess pawn rotated ninety degrees\n129551 black chess king rotated ninety degrees\n129552 black chess queen rotated ninety degrees\n129553 black chess rook rotated ninety degrees\n129554 black chess bishop rotated ninety degrees\n129555 black chess knight rotated ninety degrees\n129556 black chess pawn rotated ninety degrees\n129557 neutral chess king rotated ninety degrees\n129558 neutral chess queen rotated ninety degrees\n129559 neutral chess rook rotated ninety degrees\n129560 neutral chess bishop rotated ninety degrees\n129561 neutral chess knight rotated ninety degrees\n129562 neutral chess pawn rotated ninety degrees\n129563 white chess knight rotated one hundred thirty-five degrees\n129564 black chess knight rotated one hundred thirty-five degrees\n129565 neutral chess knight rotated one hundred thirty-five degrees\n129566 white chess turned king\n129567 white chess turned queen\n129568 white chess turned rook\n129569 white chess turned bishop\n129570 white chess turned knight\n129571 white chess turned pawn\n129572 black chess turned king\n129573 black chess turned queen\n129574 black chess turned rook\n129575 black chess turned bishop\n129576 black chess turned knight\n129577 black chess turned pawn\n129578 neutral chess turned king\n129579 neutral chess turned queen\n129580 neutral chess turned rook\n129581 neutral chess turned bishop\n129582 neutral chess turned knight\n129583 neutral chess turned pawn\n129584 white chess knight rotated two hundred twenty-five degrees\n129585 black chess knight rotated two hundred twenty-five degrees\n129586 neutral chess knight rotated two hundred twenty-five degrees\n129587 white chess king rotated two hundred seventy degrees\n129588 white chess queen rotated two hundred seventy degrees\n129589 white chess rook rotated two hundred seventy degrees\n129590 white chess bishop rotated two hundred seventy degrees\n129591 white chess knight rotated two hundred seventy degrees\n129592 white chess pawn rotated two hundred seventy degrees\n129593 black chess king rotated two hundred seventy degrees\n129594 black chess queen rotated two hundred seventy degrees\n129595 black chess rook rotated two hundred seventy degrees\n129596 black chess bishop rotated two hundred seventy degrees\n129597 black chess knight rotated two hundred seventy degrees\n129598 black chess pawn rotated two hundred seventy degrees\n129599 neutral chess king rotated two hundred seventy degrees\n129600 neutral chess queen rotated two hundred seventy degrees\n129601 neutral chess rook rotated two hundred seventy degrees\n129602 neutral chess bishop rotated two hundred seventy degrees\n129603 neutral chess knight rotated two hundred seventy degrees\n129604 neutral chess pawn rotated two hundred seventy degrees\n129605 white chess knight rotated three hundred fifteen degrees\n129606 black chess knight rotated three hundred fifteen degrees\n129607 neutral chess knight rotated three hundred fifteen degrees\n129608 white chess equihopper\n129609 black chess equihopper\n129610 neutral chess equihopper\n129611 white chess equihopper rotated ninety degrees\n129612 black chess equihopper rotated ninety degrees\n129613 neutral chess equihopper rotated ninety degrees\n129614 white chess knight-queen\n129615 white chess knight-rook\n129616 white chess knight-bishop\n129617 black chess knight-queen\n129618 black chess knight-rook\n129619 black chess knight-bishop\n129620 white chess ferz\n129621 white chess alfil\n129622 black chess ferz\n129623 black chess alfil\n129632 xiangqi red general\n129633 xiangqi red mandarin\n129634 xiangqi red elephant\n129635 xiangqi red horse\n129636 xiangqi red chariot\n129637 xiangqi red cannon\n129638 xiangqi red soldier\n129639 xiangqi black general\n129640 xiangqi black mandarin\n129641 xiangqi black elephant\n129642 xiangqi black horse\n129643 xiangqi black chariot\n129644 xiangqi black cannon\n129645 xiangqi black soldier\n129648 ballet shoes\n129649 one-piece swimsuit\n129650 briefs\n129651 shorts\n129652 thong sandal\n129653 light blue heart\n129654 grey heart\n129655 pink heart\n129656 drop of blood\n129657 adhesive bandage\n129658 stethoscope\n129659 x-ray\n129660 crutch\n129664 yo-yo\n129665 kite\n129666 parachute\n129667 boomerang\n129668 magic wand\n129669 pinata\n129670 nesting dolls\n129671 maracas\n129672 flute\n129673 harp\n129674 trombone\n129678 treasure chest\n129679 shovel\n129680 ringed planet\n129681 chair\n129682 razor\n129683 axe\n129684 diya lamp\n129685 banjo\n129686 military helmet\n129687 accordion\n129688 long drum\n129689 coin\n129690 carpentry saw\n129691 screwdriver\n129692 ladder\n129693 hook\n129694 mirror\n129695 window\n129696 plunger\n129697 sewing needle\n129698 knot\n129699 bucket\n129700 mouse trap\n129701 toothbrush\n129702 headstone\n129703 placard\n129704 rock\n129705 mirror ball\n129706 identification card\n129707 low battery\n129708 hamsa\n129709 folding hand fan\n129710 hair pick\n129711 khanda\n129712 fly\n129713 worm\n129714 beetle\n129715 cockroach\n129716 potted plant\n129717 wood\n129718 feather\n129719 lotus\n129720 coral\n129721 empty nest\n129722 nest with eggs\n129723 hyacinth\n129724 jellyfish\n129725 wing\n129726 leafless tree\n129727 goose\n129728 anatomical heart\n129729 lungs\n129730 people hugging\n129731 pregnant man\n129732 pregnant person\n129733 person with crown\n129734 fingerprint\n129736 hairy creature\n129741 orca\n129742 moose\n129743 donkey\n129744 blueberries\n129745 bell pepper\n129746 olive\n129747 flatbread\n129748 tamale\n129749 fondue\n129750 teapot\n129751 pouring liquid\n129752 beans\n129753 jar\n129754 ginger root\n129755 pea pod\n129756 root vegetable\n129759 splatter\n129760 melting face\n129761 saluting face\n129762 face with open eyes and hand over mouth\n129763 face with peeking eye\n129764 face with diagonal mouth\n129765 dotted line face\n129766 biting lip\n129767 bubbles\n129768 shaking face\n129769 face with bags under eyes\n129770 distorted face\n129775 fight cloud\n129776 hand with index finger and thumb crossed\n129777 rightwards hand\n129778 leftwards hand\n129779 palm down hand\n129780 palm up hand\n129781 index pointing at the viewer\n129782 heart hands\n129783 leftwards pushing hand\n129784 rightwards pushing hand\n129792 block sextant-1\n129793 block sextant-2\n129794 block sextant-12\n129795 block sextant-3\n129796 block sextant-13\n129797 block sextant-23\n129798 block sextant-123\n129799 block sextant-4\n129800 block sextant-14\n129801 block sextant-24\n129802 block sextant-124\n129803 block sextant-34\n129804 block sextant-134\n129805 block sextant-234\n129806 block sextant-1234\n129807 block sextant-5\n129808 block sextant-15\n129809 block sextant-25\n129810 block sextant-125\n129811 block sextant-35\n129812 block sextant-235\n129813 block sextant-1235\n129814 block sextant-45\n129815 block sextant-145\n129816 block sextant-245\n129817 block sextant-1245\n129818 block sextant-345\n129819 block sextant-1345\n129820 block sextant-2345\n129821 block sextant-12345\n129822 block sextant-6\n129823 block sextant-16\n129824 block sextant-26\n129825 block sextant-126\n129826 block sextant-36\n129827 block sextant-136\n129828 block sextant-236\n129829 block sextant-1236\n129830 block sextant-46\n129831 block sextant-146\n129832 block sextant-1246\n129833 block sextant-346\n129834 block sextant-1346\n129835 block sextant-2346\n129836 block sextant-12346\n129837 block sextant-56\n129838 block sextant-156\n129839 block sextant-256\n129840 block sextant-1256\n129841 block sextant-356\n129842 block sextant-1356\n129843 block sextant-2356\n129844 block sextant-12356\n129845 block sextant-456\n129846 block sextant-1456\n129847 block sextant-2456\n129848 block sextant-12456\n129849 block sextant-3456\n129850 block sextant-13456\n129851 block sextant-23456\n129852 lower left block diagonal lower middle left to lower centre\n129853 lower left block diagonal lower middle left to lower right\n129854 lower left block diagonal upper middle left to lower centre\n129855 lower left block diagonal upper middle left to lower right\n129856 lower left block diagonal upper left to lower centre\n129857 lower right block diagonal upper middle left to upper centre\n129858 lower right block diagonal upper middle left to upper right\n129859 lower right block diagonal lower middle left to upper centre\n129860 lower right block diagonal lower middle left to upper right\n129861 lower right block diagonal lower left to upper centre\n129862 lower right block diagonal lower middle left to upper middle right\n129863 lower right block diagonal lower centre to lower middle right\n129864 lower right block diagonal lower left to lower middle right\n129865 lower right block diagonal lower centre to upper middle right\n129866 lower right block diagonal lower left to upper middle right\n129867 lower right block diagonal lower centre to upper right\n129868 lower left block diagonal upper centre to upper middle right\n129869 lower left block diagonal upper left to upper middle right\n129870 lower left block diagonal upper centre to lower middle right\n129871 lower left block diagonal upper left to lower middle right\n129872 lower left block diagonal upper centre to lower right\n129873 lower left block diagonal upper middle left to lower middle right\n129874 upper right block diagonal lower middle left to lower centre\n129875 upper right block diagonal lower middle left to lower right\n129876 upper right block diagonal upper middle left to lower centre\n129877 upper right block diagonal upper middle left to lower right\n129878 upper right block diagonal upper left to lower centre\n129879 upper left block diagonal upper middle left to upper centre\n129880 upper left block diagonal upper middle left to upper right\n129881 upper left block diagonal lower middle left to upper centre\n129882 upper left block diagonal lower middle left to upper right\n129883 upper left block diagonal lower left to upper centre\n129884 upper left block diagonal lower middle left to upper middle right\n129885 upper left block diagonal lower centre to lower middle right\n129886 upper left block diagonal lower left to lower middle right\n129887 upper left block diagonal lower centre to upper middle right\n129888 upper left block diagonal lower left to upper middle right\n129889 upper left block diagonal lower centre to upper right\n129890 upper right block diagonal upper centre to upper middle right\n129891 upper right block diagonal upper left to upper middle right\n129892 upper right block diagonal upper centre to lower middle right\n129893 upper right block diagonal upper left to lower middle right\n129894 upper right block diagonal upper centre to lower right\n129895 upper right block diagonal upper middle left to lower middle right\n129896 upper and right and lower triangular three quarters block\n129897 left and lower and right triangular three quarters block\n129898 upper and left and lower triangular three quarters block\n129899 left and upper and right triangular three quarters block\n129900 left triangular one quarter block\n129901 upper triangular one quarter block\n129902 right triangular one quarter block\n129903 lower triangular one quarter block\n129904 vertical one eighth block-2\n129905 vertical one eighth block-3\n129906 vertical one eighth block-4\n129907 vertical one eighth block-5\n129908 vertical one eighth block-6\n129909 vertical one eighth block-7\n129910 horizontal one eighth block-2\n129911 horizontal one eighth block-3\n129912 horizontal one eighth block-4\n129913 horizontal one eighth block-5\n129914 horizontal one eighth block-6\n129915 horizontal one eighth block-7\n129916 left and lower one eighth block\n129917 left and upper one eighth block\n129918 right and upper one eighth block\n129919 right and lower one eighth block\n129920 upper and lower one eighth block\n129921 horizontal one eighth block-1358\n129922 upper one quarter block\n129923 upper three eighths block\n129924 upper five eighths block\n129925 upper three quarters block\n129926 upper seven eighths block\n129927 right one quarter block\n129928 right three eighths block\n129929 right five eighths block\n129930 right three quarters block\n129931 right seven eighths block\n129932 left half medium shade\n129933 right half medium shade\n129934 upper half medium shade\n129935 lower half medium shade\n129936 inverse medium shade\n129937 upper half block and lower half inverse medium shade\n129938 upper half inverse medium shade and lower half block\n129940 left half inverse medium shade and right half block\n129941 checker board fill\n129942 inverse checker board fill\n129943 heavy horizontal fill\n129944 upper left to lower right fill\n129945 upper right to lower left fill\n129946 upper and lower triangular half block\n129947 left and right triangular half block\n129948 upper left triangular medium shade\n129949 upper right triangular medium shade\n129950 lower right triangular medium shade\n129951 lower left triangular medium shade\n129952 box drawings light diagonal upper centre to middle left\n129953 box drawings light diagonal upper centre to middle right\n129954 box drawings light diagonal middle left to lower centre\n129955 box drawings light diagonal middle right to lower centre\n129956 box drawings light diagonal upper centre to middle left to lower centre\n129957 box drawings light diagonal upper centre to middle right to lower centre\n129958 box drawings light diagonal middle left to lower centre to middle right\n129959 box drawings light diagonal middle left to upper centre to middle right\n129960 box drawings light diagonal upper centre to middle left and middle right to lower centre\n129961 box drawings light diagonal upper centre to middle right and middle left to lower centre\n129962 box drawings light diagonal upper centre to middle right to lower centre to middle left\n129963 box drawings light diagonal upper centre to middle left to lower centre to middle right\n129964 box drawings light diagonal middle left to upper centre to middle right to lower centre\n129965 box drawings light diagonal middle right to upper centre to middle left to lower centre\n129966 box drawings light diagonal diamond\n129967 box drawings light horizontal with vertical stroke\n129968 arrowhead-shaped pointer\n129969 inverse check mark\n129970 left half running man\n129971 right half running man\n129972 inverse downwards arrow with tip leftwards\n129973 leftwards arrow and upper and lower one eighth block\n129974 rightwards arrow and upper and lower one eighth block\n129975 downwards arrow and right one eighth block\n129976 upwards arrow and right one eighth block\n129977 left half folder\n129978 right half folder\n129979 voided greek cross\n129980 right open squared dot\n129981 negative diagonal cross\n129982 negative diagonal middle right to lower centre\n129983 negative diagonal diamond\n129984 white heavy saltire with rounded corners\n129985 left third white right pointing index\n129986 middle third white right pointing index\n129987 right third white right pointing index\n129988 negative squared question mark\n129989 stick figure\n129990 stick figure with arms raised\n129991 stick figure leaning left\n129992 stick figure leaning right\n129993 stick figure with dress\n129994 white up-pointing chevron\n129995 white cross mark\n129996 raised small left square bracket\n129997 black small up-pointing chevron\n129998 left two thirds block\n129999 left one third block\n130000 box drawings light diagonal middle right to lower left\n130001 box drawings light diagonal upper right to middle left\n130002 box drawings light diagonal upper left to middle right\n130003 box drawings light diagonal middle left to lower right\n130004 box drawings light diagonal upper left to lower centre\n130005 box drawings light diagonal upper centre to lower right\n130006 box drawings light diagonal upper right to lower centre\n130007 box drawings light diagonal upper centre to lower left\n130008 box drawings light diagonal upper left to middle centre to upper right\n130009 box drawings light diagonal upper right to middle centre to lower right\n130010 box drawings light diagonal lower left to middle centre to lower right\n130011 box drawings light diagonal upper left to middle centre to lower left\n130012 box drawings light diagonal upper left to lower centre to upper right\n130013 box drawings light diagonal upper right to middle left to lower right\n130014 box drawings light diagonal lower left to upper centre to lower right\n130015 box drawings light diagonal upper left to middle right to lower left\n130016 top justified lower half white circle\n130017 right justified left half white circle\n130018 bottom justified upper half white circle\n130019 left justified right half white circle\n130020 upper centre one quarter block\n130021 lower centre one quarter block\n130022 middle left one quarter block\n130023 middle right one quarter block\n130024 top justified lower half black circle\n130025 right justified left half black circle\n130026 bottom justified upper half black circle\n130027 left justified right half black circle\n130028 top right justified lower left quarter black circle\n130029 bottom left justified upper right quarter black circle\n130030 bottom right justified upper left quarter black circle\n130031 top left justified lower right quarter black circle\n130032 segmented digit zero\n130033 segmented digit one\n130034 segmented digit two\n130035 segmented digit three\n130036 segmented digit four\n130037 segmented digit five\n130038 segmented digit six\n130039 segmented digit seven\n130040 segmented digit eight\n130041 segmented digit nine\n130042 alarm bell symbol\n131072 <cjk ideograph extension b, first>\n173791 <cjk ideograph extension b, last>\n173824 <cjk ideograph extension c, first>\n177983 <cjk ideograph extension c, last>\n177984 <cjk ideograph extension d, first>\n178205 <cjk ideograph extension d, last>\n178208 <cjk ideograph extension e, first>\n183981 <cjk ideograph extension e, last>\n183984 <cjk ideograph extension f, first>\n191456 <cjk ideograph extension f, last>\n191472 <cjk ideograph extension i, first>\n192093 <cjk ideograph extension i, last>\n194560 cjk compatibility ideograph-2f800\n194561 cjk compatibility ideograph-2f801\n194562 cjk compatibility ideograph-2f802\n194563 cjk compatibility ideograph-2f803\n194564 cjk compatibility ideograph-2f804\n194565 cjk compatibility ideograph-2f805\n194566 cjk compatibility ideograph-2f806\n194567 cjk compatibility ideograph-2f807\n194568 cjk compatibility ideograph-2f808\n194569 cjk compatibility ideograph-2f809\n194570 cjk compatibility ideograph-2f80a\n194571 cjk compatibility ideograph-2f80b\n194572 cjk compatibility ideograph-2f80c\n194573 cjk compatibility ideograph-2f80d\n194574 cjk compatibility ideograph-2f80e\n194575 cjk compatibility ideograph-2f80f\n194576 cjk compatibility ideograph-2f810\n194577 cjk compatibility ideograph-2f811\n194578 cjk compatibility ideograph-2f812\n194579 cjk compatibility ideograph-2f813\n194580 cjk compatibility ideograph-2f814\n194581 cjk compatibility ideograph-2f815\n194582 cjk compatibility ideograph-2f816\n194583 cjk compatibility ideograph-2f817\n194584 cjk compatibility ideograph-2f818\n194585 cjk compatibility ideograph-2f819\n194586 cjk compatibility ideograph-2f81a\n194587 cjk compatibility ideograph-2f81b\n194588 cjk compatibility ideograph-2f81c\n194589 cjk compatibility ideograph-2f81d\n194590 cjk compatibility ideograph-2f81e\n194591 cjk compatibility ideograph-2f81f\n194592 cjk compatibility ideograph-2f820\n194593 cjk compatibility ideograph-2f821\n194594 cjk compatibility ideograph-2f822\n194595 cjk compatibility ideograph-2f823\n194596 cjk compatibility ideograph-2f824\n194597 cjk compatibility ideograph-2f825\n194598 cjk compatibility ideograph-2f826\n194599 cjk compatibility ideograph-2f827\n194600 cjk compatibility ideograph-2f828\n194601 cjk compatibility ideograph-2f829\n194602 cjk compatibility ideograph-2f82a\n194603 cjk compatibility ideograph-2f82b\n194604 cjk compatibility ideograph-2f82c\n194605 cjk compatibility ideograph-2f82d\n194606 cjk compatibility ideograph-2f82e\n194607 cjk compatibility ideograph-2f82f\n194608 cjk compatibility ideograph-2f830\n194609 cjk compatibility ideograph-2f831\n194610 cjk compatibility ideograph-2f832\n194611 cjk compatibility ideograph-2f833\n194612 cjk compatibility ideograph-2f834\n194613 cjk compatibility ideograph-2f835\n194614 cjk compatibility ideograph-2f836\n194615 cjk compatibility ideograph-2f837\n194616 cjk compatibility ideograph-2f838\n194617 cjk compatibility ideograph-2f839\n194618 cjk compatibility ideograph-2f83a\n194619 cjk compatibility ideograph-2f83b\n194620 cjk compatibility ideograph-2f83c\n194621 cjk compatibility ideograph-2f83d\n194622 cjk compatibility ideograph-2f83e\n194623 cjk compatibility ideograph-2f83f\n194624 cjk compatibility ideograph-2f840\n194625 cjk compatibility ideograph-2f841\n194626 cjk compatibility ideograph-2f842\n194627 cjk compatibility ideograph-2f843\n194628 cjk compatibility ideograph-2f844\n194629 cjk compatibility ideograph-2f845\n194630 cjk compatibility ideograph-2f846\n194631 cjk compatibility ideograph-2f847\n194632 cjk compatibility ideograph-2f848\n194633 cjk compatibility ideograph-2f849\n194634 cjk compatibility ideograph-2f84a\n194635 cjk compatibility ideograph-2f84b\n194636 cjk compatibility ideograph-2f84c\n194637 cjk compatibility ideograph-2f84d\n194638 cjk compatibility ideograph-2f84e\n194639 cjk compatibility ideograph-2f84f\n194640 cjk compatibility ideograph-2f850\n194641 cjk compatibility ideograph-2f851\n194642 cjk compatibility ideograph-2f852\n194643 cjk compatibility ideograph-2f853\n194644 cjk compatibility ideograph-2f854\n194645 cjk compatibility ideograph-2f855\n194646 cjk compatibility ideograph-2f856\n194647 cjk compatibility ideograph-2f857\n194648 cjk compatibility ideograph-2f858\n194649 cjk compatibility ideograph-2f859\n194650 cjk compatibility ideograph-2f85a\n194651 cjk compatibility ideograph-2f85b\n194652 cjk compatibility ideograph-2f85c\n194653 cjk compatibility ideograph-2f85d\n194654 cjk compatibility ideograph-2f85e\n194655 cjk compatibility ideograph-2f85f\n194656 cjk compatibility ideograph-2f860\n194657 cjk compatibility ideograph-2f861\n194658 cjk compatibility ideograph-2f862\n194659 cjk compatibility ideograph-2f863\n194660 cjk compatibility ideograph-2f864\n194661 cjk compatibility ideograph-2f865\n194662 cjk compatibility ideograph-2f866\n194663 cjk compatibility ideograph-2f867\n194664 cjk compatibility ideograph-2f868\n194665 cjk compatibility ideograph-2f869\n194666 cjk compatibility ideograph-2f86a\n194667 cjk compatibility ideograph-2f86b\n194668 cjk compatibility ideograph-2f86c\n194669 cjk compatibility ideograph-2f86d\n194670 cjk compatibility ideograph-2f86e\n194671 cjk compatibility ideograph-2f86f\n194672 cjk compatibility ideograph-2f870\n194673 cjk compatibility ideograph-2f871\n194674 cjk compatibility ideograph-2f872\n194675 cjk compatibility ideograph-2f873\n194676 cjk compatibility ideograph-2f874\n194677 cjk compatibility ideograph-2f875\n194678 cjk compatibility ideograph-2f876\n194679 cjk compatibility ideograph-2f877\n194680 cjk compatibility ideograph-2f878\n194681 cjk compatibility ideograph-2f879\n194682 cjk compatibility ideograph-2f87a\n194683 cjk compatibility ideograph-2f87b\n194684 cjk compatibility ideograph-2f87c\n194685 cjk compatibility ideograph-2f87d\n194686 cjk compatibility ideograph-2f87e\n194687 cjk compatibility ideograph-2f87f\n194688 cjk compatibility ideograph-2f880\n194689 cjk compatibility ideograph-2f881\n194690 cjk compatibility ideograph-2f882\n194691 cjk compatibility ideograph-2f883\n194692 cjk compatibility ideograph-2f884\n194693 cjk compatibility ideograph-2f885\n194694 cjk compatibility ideograph-2f886\n194695 cjk compatibility ideograph-2f887\n194696 cjk compatibility ideograph-2f888\n194697 cjk compatibility ideograph-2f889\n194698 cjk compatibility ideograph-2f88a\n194699 cjk compatibility ideograph-2f88b\n194700 cjk compatibility ideograph-2f88c\n194701 cjk compatibility ideograph-2f88d\n194702 cjk compatibility ideograph-2f88e\n194703 cjk compatibility ideograph-2f88f\n194704 cjk compatibility ideograph-2f890\n194705 cjk compatibility ideograph-2f891\n194706 cjk compatibility ideograph-2f892\n194707 cjk compatibility ideograph-2f893\n194708 cjk compatibility ideograph-2f894\n194709 cjk compatibility ideograph-2f895\n194710 cjk compatibility ideograph-2f896\n194711 cjk compatibility ideograph-2f897\n194712 cjk compatibility ideograph-2f898\n194713 cjk compatibility ideograph-2f899\n194714 cjk compatibility ideograph-2f89a\n194715 cjk compatibility ideograph-2f89b\n194716 cjk compatibility ideograph-2f89c\n194717 cjk compatibility ideograph-2f89d\n194718 cjk compatibility ideograph-2f89e\n194719 cjk compatibility ideograph-2f89f\n194720 cjk compatibility ideograph-2f8a0\n194721 cjk compatibility ideograph-2f8a1\n194722 cjk compatibility ideograph-2f8a2\n194723 cjk compatibility ideograph-2f8a3\n194724 cjk compatibility ideograph-2f8a4\n194725 cjk compatibility ideograph-2f8a5\n194726 cjk compatibility ideograph-2f8a6\n194727 cjk compatibility ideograph-2f8a7\n194728 cjk compatibility ideograph-2f8a8\n194729 cjk compatibility ideograph-2f8a9\n194730 cjk compatibility ideograph-2f8aa\n194731 cjk compatibility ideograph-2f8ab\n194732 cjk compatibility ideograph-2f8ac\n194733 cjk compatibility ideograph-2f8ad\n194734 cjk compatibility ideograph-2f8ae\n194735 cjk compatibility ideograph-2f8af\n194736 cjk compatibility ideograph-2f8b0\n194737 cjk compatibility ideograph-2f8b1\n194738 cjk compatibility ideograph-2f8b2\n194739 cjk compatibility ideograph-2f8b3\n194740 cjk compatibility ideograph-2f8b4\n194741 cjk compatibility ideograph-2f8b5\n194742 cjk compatibility ideograph-2f8b6\n194743 cjk compatibility ideograph-2f8b7\n194744 cjk compatibility ideograph-2f8b8\n194745 cjk compatibility ideograph-2f8b9\n194746 cjk compatibility ideograph-2f8ba\n194747 cjk compatibility ideograph-2f8bb\n194748 cjk compatibility ideograph-2f8bc\n194749 cjk compatibility ideograph-2f8bd\n194750 cjk compatibility ideograph-2f8be\n194751 cjk compatibility ideograph-2f8bf\n194752 cjk compatibility ideograph-2f8c0\n194753 cjk compatibility ideograph-2f8c1\n194754 cjk compatibility ideograph-2f8c2\n194755 cjk compatibility ideograph-2f8c3\n194756 cjk compatibility ideograph-2f8c4\n194757 cjk compatibility ideograph-2f8c5\n194758 cjk compatibility ideograph-2f8c6\n194759 cjk compatibility ideograph-2f8c7\n194760 cjk compatibility ideograph-2f8c8\n194761 cjk compatibility ideograph-2f8c9\n194762 cjk compatibility ideograph-2f8ca\n194763 cjk compatibility ideograph-2f8cb\n194764 cjk compatibility ideograph-2f8cc\n194765 cjk compatibility ideograph-2f8cd\n194766 cjk compatibility ideograph-2f8ce\n194767 cjk compatibility ideograph-2f8cf\n194768 cjk compatibility ideograph-2f8d0\n194769 cjk compatibility ideograph-2f8d1\n194770 cjk compatibility ideograph-2f8d2\n194771 cjk compatibility ideograph-2f8d3\n194772 cjk compatibility ideograph-2f8d4\n194773 cjk compatibility ideograph-2f8d5\n194774 cjk compatibility ideograph-2f8d6\n194775 cjk compatibility ideograph-2f8d7\n194776 cjk compatibility ideograph-2f8d8\n194777 cjk compatibility ideograph-2f8d9\n194778 cjk compatibility ideograph-2f8da\n194779 cjk compatibility ideograph-2f8db\n194780 cjk compatibility ideograph-2f8dc\n194781 cjk compatibility ideograph-2f8dd\n194782 cjk compatibility ideograph-2f8de\n194783 cjk compatibility ideograph-2f8df\n194784 cjk compatibility ideograph-2f8e0\n194785 cjk compatibility ideograph-2f8e1\n194786 cjk compatibility ideograph-2f8e2\n194787 cjk compatibility ideograph-2f8e3\n194788 cjk compatibility ideograph-2f8e4\n194789 cjk compatibility ideograph-2f8e5\n194790 cjk compatibility ideograph-2f8e6\n194791 cjk compatibility ideograph-2f8e7\n194792 cjk compatibility ideograph-2f8e8\n194793 cjk compatibility ideograph-2f8e9\n194794 cjk compatibility ideograph-2f8ea\n194795 cjk compatibility ideograph-2f8eb\n194796 cjk compatibility ideograph-2f8ec\n194797 cjk compatibility ideograph-2f8ed\n194798 cjk compatibility ideograph-2f8ee\n194799 cjk compatibility ideograph-2f8ef\n194800 cjk compatibility ideograph-2f8f0\n194801 cjk compatibility ideograph-2f8f1\n194802 cjk compatibility ideograph-2f8f2\n194803 cjk compatibility ideograph-2f8f3\n194804 cjk compatibility ideograph-2f8f4\n194805 cjk compatibility ideograph-2f8f5\n194806 cjk compatibility ideograph-2f8f6\n194807 cjk compatibility ideograph-2f8f7\n194808 cjk compatibility ideograph-2f8f8\n194809 cjk compatibility ideograph-2f8f9\n194810 cjk compatibility ideograph-2f8fa\n194811 cjk compatibility ideograph-2f8fb\n194812 cjk compatibility ideograph-2f8fc\n194813 cjk compatibility ideograph-2f8fd\n194814 cjk compatibility ideograph-2f8fe\n194815 cjk compatibility ideograph-2f8ff\n194816 cjk compatibility ideograph-2f900\n194817 cjk compatibility ideograph-2f901\n194818 cjk compatibility ideograph-2f902\n194819 cjk compatibility ideograph-2f903\n194820 cjk compatibility ideograph-2f904\n194821 cjk compatibility ideograph-2f905\n194822 cjk compatibility ideograph-2f906\n194823 cjk compatibility ideograph-2f907\n194824 cjk compatibility ideograph-2f908\n194825 cjk compatibility ideograph-2f909\n194826 cjk compatibility ideograph-2f90a\n194827 cjk compatibility ideograph-2f90b\n194828 cjk compatibility ideograph-2f90c\n194829 cjk compatibility ideograph-2f90d\n194830 cjk compatibility ideograph-2f90e\n194831 cjk compatibility ideograph-2f90f\n194832 cjk compatibility ideograph-2f910\n194833 cjk compatibility ideograph-2f911\n194834 cjk compatibility ideograph-2f912\n194835 cjk compatibility ideograph-2f913\n194836 cjk compatibility ideograph-2f914\n194837 cjk compatibility ideograph-2f915\n194838 cjk compatibility ideograph-2f916\n194839 cjk compatibility ideograph-2f917\n194840 cjk compatibility ideograph-2f918\n194841 cjk compatibility ideograph-2f919\n194842 cjk compatibility ideograph-2f91a\n194843 cjk compatibility ideograph-2f91b\n194844 cjk compatibility ideograph-2f91c\n194845 cjk compatibility ideograph-2f91d\n194846 cjk compatibility ideograph-2f91e\n194847 cjk compatibility ideograph-2f91f\n194848 cjk compatibility ideograph-2f920\n194849 cjk compatibility ideograph-2f921\n194850 cjk compatibility ideograph-2f922\n194851 cjk compatibility ideograph-2f923\n194852 cjk compatibility ideograph-2f924\n194853 cjk compatibility ideograph-2f925\n194854 cjk compatibility ideograph-2f926\n194855 cjk compatibility ideograph-2f927\n194856 cjk compatibility ideograph-2f928\n194857 cjk compatibility ideograph-2f929\n194858 cjk compatibility ideograph-2f92a\n194859 cjk compatibility ideograph-2f92b\n194860 cjk compatibility ideograph-2f92c\n194861 cjk compatibility ideograph-2f92d\n194862 cjk compatibility ideograph-2f92e\n194863 cjk compatibility ideograph-2f92f\n194864 cjk compatibility ideograph-2f930\n194865 cjk compatibility ideograph-2f931\n194866 cjk compatibility ideograph-2f932\n194867 cjk compatibility ideograph-2f933\n194868 cjk compatibility ideograph-2f934\n194869 cjk compatibility ideograph-2f935\n194870 cjk compatibility ideograph-2f936\n194871 cjk compatibility ideograph-2f937\n194872 cjk compatibility ideograph-2f938\n194873 cjk compatibility ideograph-2f939\n194874 cjk compatibility ideograph-2f93a\n194875 cjk compatibility ideograph-2f93b\n194876 cjk compatibility ideograph-2f93c\n194877 cjk compatibility ideograph-2f93d\n194878 cjk compatibility ideograph-2f93e\n194879 cjk compatibility ideograph-2f93f\n194880 cjk compatibility ideograph-2f940\n194881 cjk compatibility ideograph-2f941\n194882 cjk compatibility ideograph-2f942\n194883 cjk compatibility ideograph-2f943\n194884 cjk compatibility ideograph-2f944\n194885 cjk compatibility ideograph-2f945\n194886 cjk compatibility ideograph-2f946\n194887 cjk compatibility ideograph-2f947\n194888 cjk compatibility ideograph-2f948\n194889 cjk compatibility ideograph-2f949\n194890 cjk compatibility ideograph-2f94a\n194891 cjk compatibility ideograph-2f94b\n194892 cjk compatibility ideograph-2f94c\n194893 cjk compatibility ideograph-2f94d\n194894 cjk compatibility ideograph-2f94e\n194895 cjk compatibility ideograph-2f94f\n194896 cjk compatibility ideograph-2f950\n194897 cjk compatibility ideograph-2f951\n194898 cjk compatibility ideograph-2f952\n194899 cjk compatibility ideograph-2f953\n194900 cjk compatibility ideograph-2f954\n194901 cjk compatibility ideograph-2f955\n194902 cjk compatibility ideograph-2f956\n194903 cjk compatibility ideograph-2f957\n194904 cjk compatibility ideograph-2f958\n194905 cjk compatibility ideograph-2f959\n194906 cjk compatibility ideograph-2f95a\n194907 cjk compatibility ideograph-2f95b\n194908 cjk compatibility ideograph-2f95c\n194909 cjk compatibility ideograph-2f95d\n194910 cjk compatibility ideograph-2f95e\n194911 cjk compatibility ideograph-2f95f\n194912 cjk compatibility ideograph-2f960\n194913 cjk compatibility ideograph-2f961\n194914 cjk compatibility ideograph-2f962\n194915 cjk compatibility ideograph-2f963\n194916 cjk compatibility ideograph-2f964\n194917 cjk compatibility ideograph-2f965\n194918 cjk compatibility ideograph-2f966\n194919 cjk compatibility ideograph-2f967\n194920 cjk compatibility ideograph-2f968\n194921 cjk compatibility ideograph-2f969\n194922 cjk compatibility ideograph-2f96a\n194923 cjk compatibility ideograph-2f96b\n194924 cjk compatibility ideograph-2f96c\n194925 cjk compatibility ideograph-2f96d\n194926 cjk compatibility ideograph-2f96e\n194927 cjk compatibility ideograph-2f96f\n194928 cjk compatibility ideograph-2f970\n194929 cjk compatibility ideograph-2f971\n194930 cjk compatibility ideograph-2f972\n194931 cjk compatibility ideograph-2f973\n194932 cjk compatibility ideograph-2f974\n194933 cjk compatibility ideograph-2f975\n194934 cjk compatibility ideograph-2f976\n194935 cjk compatibility ideograph-2f977\n194936 cjk compatibility ideograph-2f978\n194937 cjk compatibility ideograph-2f979\n194938 cjk compatibility ideograph-2f97a\n194939 cjk compatibility ideograph-2f97b\n194940 cjk compatibility ideograph-2f97c\n194941 cjk compatibility ideograph-2f97d\n194942 cjk compatibility ideograph-2f97e\n194943 cjk compatibility ideograph-2f97f\n194944 cjk compatibility ideograph-2f980\n194945 cjk compatibility ideograph-2f981\n194946 cjk compatibility ideograph-2f982\n194947 cjk compatibility ideograph-2f983\n194948 cjk compatibility ideograph-2f984\n194949 cjk compatibility ideograph-2f985\n194950 cjk compatibility ideograph-2f986\n194951 cjk compatibility ideograph-2f987\n194952 cjk compatibility ideograph-2f988\n194953 cjk compatibility ideograph-2f989\n194954 cjk compatibility ideograph-2f98a\n194955 cjk compatibility ideograph-2f98b\n194956 cjk compatibility ideograph-2f98c\n194957 cjk compatibility ideograph-2f98d\n194958 cjk compatibility ideograph-2f98e\n194959 cjk compatibility ideograph-2f98f\n194960 cjk compatibility ideograph-2f990\n194961 cjk compatibility ideograph-2f991\n194962 cjk compatibility ideograph-2f992\n194963 cjk compatibility ideograph-2f993\n194964 cjk compatibility ideograph-2f994\n194965 cjk compatibility ideograph-2f995\n194966 cjk compatibility ideograph-2f996\n194967 cjk compatibility ideograph-2f997\n194968 cjk compatibility ideograph-2f998\n194969 cjk compatibility ideograph-2f999\n194970 cjk compatibility ideograph-2f99a\n194971 cjk compatibility ideograph-2f99b\n194972 cjk compatibility ideograph-2f99c\n194973 cjk compatibility ideograph-2f99d\n194974 cjk compatibility ideograph-2f99e\n194975 cjk compatibility ideograph-2f99f\n194976 cjk compatibility ideograph-2f9a0\n194977 cjk compatibility ideograph-2f9a1\n194978 cjk compatibility ideograph-2f9a2\n194979 cjk compatibility ideograph-2f9a3\n194980 cjk compatibility ideograph-2f9a4\n194981 cjk compatibility ideograph-2f9a5\n194982 cjk compatibility ideograph-2f9a6\n194983 cjk compatibility ideograph-2f9a7\n194984 cjk compatibility ideograph-2f9a8\n194985 cjk compatibility ideograph-2f9a9\n194986 cjk compatibility ideograph-2f9aa\n194987 cjk compatibility ideograph-2f9ab\n194988 cjk compatibility ideograph-2f9ac\n194989 cjk compatibility ideograph-2f9ad\n194990 cjk compatibility ideograph-2f9ae\n194991 cjk compatibility ideograph-2f9af\n194992 cjk compatibility ideograph-2f9b0\n194993 cjk compatibility ideograph-2f9b1\n194994 cjk compatibility ideograph-2f9b2\n194995 cjk compatibility ideograph-2f9b3\n194996 cjk compatibility ideograph-2f9b4\n194997 cjk compatibility ideograph-2f9b5\n194998 cjk compatibility ideograph-2f9b6\n194999 cjk compatibility ideograph-2f9b7\n195000 cjk compatibility ideograph-2f9b8\n195001 cjk compatibility ideograph-2f9b9\n195002 cjk compatibility ideograph-2f9ba\n195003 cjk compatibility ideograph-2f9bb\n195004 cjk compatibility ideograph-2f9bc\n195005 cjk compatibility ideograph-2f9bd\n195006 cjk compatibility ideograph-2f9be\n195007 cjk compatibility ideograph-2f9bf\n195008 cjk compatibility ideograph-2f9c0\n195009 cjk compatibility ideograph-2f9c1\n195010 cjk compatibility ideograph-2f9c2\n195011 cjk compatibility ideograph-2f9c3\n195012 cjk compatibility ideograph-2f9c4\n195013 cjk compatibility ideograph-2f9c5\n195014 cjk compatibility ideograph-2f9c6\n195015 cjk compatibility ideograph-2f9c7\n195016 cjk compatibility ideograph-2f9c8\n195017 cjk compatibility ideograph-2f9c9\n195018 cjk compatibility ideograph-2f9ca\n195019 cjk compatibility ideograph-2f9cb\n195020 cjk compatibility ideograph-2f9cc\n195021 cjk compatibility ideograph-2f9cd\n195022 cjk compatibility ideograph-2f9ce\n195023 cjk compatibility ideograph-2f9cf\n195024 cjk compatibility ideograph-2f9d0\n195025 cjk compatibility ideograph-2f9d1\n195026 cjk compatibility ideograph-2f9d2\n195027 cjk compatibility ideograph-2f9d3\n195028 cjk compatibility ideograph-2f9d4\n195029 cjk compatibility ideograph-2f9d5\n195030 cjk compatibility ideograph-2f9d6\n195031 cjk compatibility ideograph-2f9d7\n195032 cjk compatibility ideograph-2f9d8\n195033 cjk compatibility ideograph-2f9d9\n195034 cjk compatibility ideograph-2f9da\n195035 cjk compatibility ideograph-2f9db\n195036 cjk compatibility ideograph-2f9dc\n195037 cjk compatibility ideograph-2f9dd\n195038 cjk compatibility ideograph-2f9de\n195039 cjk compatibility ideograph-2f9df\n195040 cjk compatibility ideograph-2f9e0\n195041 cjk compatibility ideograph-2f9e1\n195042 cjk compatibility ideograph-2f9e2\n195043 cjk compatibility ideograph-2f9e3\n195044 cjk compatibility ideograph-2f9e4\n195045 cjk compatibility ideograph-2f9e5\n195046 cjk compatibility ideograph-2f9e6\n195047 cjk compatibility ideograph-2f9e7\n195048 cjk compatibility ideograph-2f9e8\n195049 cjk compatibility ideograph-2f9e9\n195050 cjk compatibility ideograph-2f9ea\n195051 cjk compatibility ideograph-2f9eb\n195052 cjk compatibility ideograph-2f9ec\n195053 cjk compatibility ideograph-2f9ed\n195054 cjk compatibility ideograph-2f9ee\n195055 cjk compatibility ideograph-2f9ef\n195056 cjk compatibility ideograph-2f9f0\n195057 cjk compatibility ideograph-2f9f1\n195058 cjk compatibility ideograph-2f9f2\n195059 cjk compatibility ideograph-2f9f3\n195060 cjk compatibility ideograph-2f9f4\n195061 cjk compatibility ideograph-2f9f5\n195062 cjk compatibility ideograph-2f9f6\n195063 cjk compatibility ideograph-2f9f7\n195064 cjk compatibility ideograph-2f9f8\n195065 cjk compatibility ideograph-2f9f9\n195066 cjk compatibility ideograph-2f9fa\n195067 cjk compatibility ideograph-2f9fb\n195068 cjk compatibility ideograph-2f9fc\n195069 cjk compatibility ideograph-2f9fd\n195070 cjk compatibility ideograph-2f9fe\n195071 cjk compatibility ideograph-2f9ff\n195072 cjk compatibility ideograph-2fa00\n195073 cjk compatibility ideograph-2fa01\n195074 cjk compatibility ideograph-2fa02\n195075 cjk compatibility ideograph-2fa03\n195076 cjk compatibility ideograph-2fa04\n195077 cjk compatibility ideograph-2fa05\n195078 cjk compatibility ideograph-2fa06\n195079 cjk compatibility ideograph-2fa07\n195080 cjk compatibility ideograph-2fa08\n195081 cjk compatibility ideograph-2fa09\n195082 cjk compatibility ideograph-2fa0a\n195083 cjk compatibility ideograph-2fa0b\n195084 cjk compatibility ideograph-2fa0c\n195085 cjk compatibility ideograph-2fa0d\n195086 cjk compatibility ideograph-2fa0e\n195087 cjk compatibility ideograph-2fa0f\n195088 cjk compatibility ideograph-2fa10\n195089 cjk compatibility ideograph-2fa11\n195090 cjk compatibility ideograph-2fa12\n195091 cjk compatibility ideograph-2fa13\n195092 cjk compatibility ideograph-2fa14\n195093 cjk compatibility ideograph-2fa15\n195094 cjk compatibility ideograph-2fa16\n195095 cjk compatibility ideograph-2fa17\n195096 cjk compatibility ideograph-2fa18\n195097 cjk compatibility ideograph-2fa19\n195098 cjk compatibility ideograph-2fa1a\n195099 cjk compatibility ideograph-2fa1b\n195100 cjk compatibility ideograph-2fa1c\n195101 cjk compatibility ideograph-2fa1d\n196608 <cjk ideograph extension g, first>\n201546 <cjk ideograph extension g, last>\n201552 <cjk ideograph extension h, first>\n205743 <cjk ideograph extension h, last>\n205744 <cjk ideograph extension j, first>\n210041 <cjk ideograph extension j, last>\n917505 language tag\n917536 tag space\n917537 tag exclamation mark\n917538 tag quotation mark\n917539 tag number sign\n917540 tag dollar sign\n917541 tag percent sign\n917542 tag ampersand\n917543 tag apostrophe\n917544 tag left parenthesis\n917545 tag right parenthesis\n917546 tag asterisk\n917547 tag plus sign\n917548 tag comma\n917549 tag hyphen-minus\n917550 tag full stop\n917551 tag solidus\n917552 tag digit zero\n917553 tag digit one\n917554 tag digit two\n917555 tag digit three\n917556 tag digit four\n917557 tag digit five\n917558 tag digit six\n917559 tag digit seven\n917560 tag digit eight\n917561 tag digit nine\n917562 tag colon\n917563 tag semicolon\n917564 tag less-than sign\n917565 tag equals sign\n917566 tag greater-than sign\n917567 tag question mark\n917568 tag commercial at\n917569 tag latin capital letter a\n917570 tag latin capital letter b\n917571 tag latin capital letter c\n917572 tag latin capital letter d\n917573 tag latin capital letter e\n917574 tag latin capital letter f\n917575 tag latin capital letter g\n917576 tag latin capital letter h\n917577 tag latin capital letter i\n917578 tag latin capital letter j\n917579 tag latin capital letter k\n917580 tag latin capital letter l\n917581 tag latin capital letter m\n917582 tag latin capital letter n\n917583 tag latin capital letter o\n917584 tag latin capital letter p\n917585 tag latin capital letter q\n917586 tag latin capital letter r\n917587 tag latin capital letter s\n917588 tag latin capital letter t\n917589 tag latin capital letter u\n917590 tag latin capital letter v\n917591 tag latin capital letter w\n917592 tag latin capital letter x\n917593 tag latin capital letter y\n917594 tag latin capital letter z\n917595 tag left square bracket\n917596 tag reverse solidus\n917597 tag right square bracket\n917598 tag circumflex accent\n917599 tag low line\n917600 tag grave accent\n917601 tag latin small letter a\n917602 tag latin small letter b\n917603 tag latin small letter c\n917604 tag latin small letter d\n917605 tag latin small letter e\n917606 tag latin small letter f\n917607 tag latin small letter g\n917608 tag latin small letter h\n917609 tag latin small letter i\n917610 tag latin small letter j\n917611 tag latin small letter k\n917612 tag latin small letter l\n917613 tag latin small letter m\n917614 tag latin small letter n\n917615 tag latin small letter o\n917616 tag latin small letter p\n917617 tag latin small letter q\n917618 tag latin small letter r\n917619 tag latin small letter s\n917620 tag latin small letter t\n917621 tag latin small letter u\n917622 tag latin small letter v\n917623 tag latin small letter w\n917624 tag latin small letter x\n917625 tag latin small letter y\n917626 tag latin small letter z\n917627 tag left curly bracket\n917628 tag vertical line\n917629 tag right curly bracket\n917630 tag tilde\n917631 cancel tag\n917760 variation selector-17\n917761 variation selector-18\n917762 variation selector-19\n917763 variation selector-20\n917764 variation selector-21\n917765 variation selector-22\n917766 variation selector-23\n917767 variation selector-24\n917768 variation selector-25\n917769 variation selector-26\n917770 variation selector-27\n917771 variation selector-28\n917772 variation selector-29\n917773 variation selector-30\n917774 variation selector-31\n917775 variation selector-32\n917776 variation selector-33\n917777 variation selector-34\n917778 variation selector-35\n917779 variation selector-36\n917780 variation selector-37\n917781 variation selector-38\n917782 variation selector-39\n917783 variation selector-40\n917784 variation selector-41\n917785 variation selector-42\n917786 variation selector-43\n917787 variation selector-44\n917788 variation selector-45\n917789 variation selector-46\n917790 variation selector-47\n917791 variation selector-48\n917792 variation selector-49\n917793 variation selector-50\n917794 variation selector-51\n917795 variation selector-52\n917796 variation selector-53\n917797 variation selector-54\n917798 variation selector-55\n917799 variation selector-56\n917800 variation selector-57\n917801 variation selector-58\n917802 variation selector-59\n917803 variation selector-60\n917804 variation selector-61\n917805 variation selector-62\n917806 variation selector-63\n917807 variation selector-64\n917808 variation selector-65\n917809 variation selector-66\n917810 variation selector-67\n917811 variation selector-68\n917812 variation selector-69\n917813 variation selector-70\n917814 variation selector-71\n917815 variation selector-72\n917816 variation selector-73\n917817 variation selector-74\n917818 variation selector-75\n917819 variation selector-76\n917820 variation selector-77\n917821 variation selector-78\n917822 variation selector-79\n917823 variation selector-80\n917824 variation selector-81\n917825 variation selector-82\n917826 variation selector-83\n917827 variation selector-84\n917828 variation selector-85\n917829 variation selector-86\n917830 variation selector-87\n917831 variation selector-88\n917832 variation selector-89\n917833 variation selector-90\n917834 variation selector-91\n917835 variation selector-92\n917836 variation selector-93\n917837 variation selector-94\n917838 variation selector-95\n917839 variation selector-96\n917840 variation selector-97\n917841 variation selector-98\n917842 variation selector-99\n917843 variation selector-100\n917844 variation selector-101\n917845 variation selector-102\n917846 variation selector-103\n917847 variation selector-104\n917848 variation selector-105\n917849 variation selector-106\n917850 variation selector-107\n917851 variation selector-108\n917852 variation selector-109\n917853 variation selector-110\n917854 variation selector-111\n917855 variation selector-112\n917856 variation selector-113\n917857 variation selector-114\n917858 variation selector-115\n917859 variation selector-116\n917860 variation selector-117\n917861 variation selector-118\n917862 variation selector-119\n917863 variation selector-120\n917864 variation selector-121\n917865 variation selector-122\n917866 variation selector-123\n917867 variation selector-124\n917868 variation selector-125\n917869 variation selector-126\n917870 variation selector-127\n917871 variation selector-128\n917872 variation selector-129\n917873 variation selector-130\n917874 variation selector-131\n917875 variation selector-132\n917876 variation selector-133\n917877 variation selector-134\n917878 variation selector-135\n917879 variation selector-136\n917880 variation selector-137\n917881 variation selector-138\n917882 variation selector-139\n917883 variation selector-140\n917884 variation selector-141\n917885 variation selector-142\n917886 variation selector-143\n917887 variation selector-144\n917888 variation selector-145\n917889 variation selector-146\n917890 variation selector-147\n917891 variation selector-148\n917892 variation selector-149\n917893 variation selector-150\n917894 variation selector-151\n917895 variation selector-152\n917896 variation selector-153\n917897 variation selector-154\n917898 variation selector-155\n917899 variation selector-156\n917900 variation selector-157\n917901 variation selector-158\n917902 variation selector-159\n917903 variation selector-160\n917904 variation selector-161\n917905 variation selector-162\n917906 variation selector-163\n917907 variation selector-164\n917908 variation selector-165\n917909 variation selector-166\n917910 variation selector-167\n917911 variation selector-168\n917912 variation selector-169\n917913 variation selector-170\n917914 variation selector-171\n917915 variation selector-172\n917916 variation selector-173\n917917 variation selector-174\n917918 variation selector-175\n917919 variation selector-176\n917920 variation selector-177\n917921 variation selector-178\n917922 variation selector-179\n917923 variation selector-180\n917924 variation selector-181\n917925 variation selector-182\n917926 variation selector-183\n917927 variation selector-184\n917928 variation selector-185\n917929 variation selector-186\n917930 variation selector-187\n917931 variation selector-188\n917932 variation selector-189\n917933 variation selector-190\n917934 variation selector-191\n917935 variation selector-192\n917936 variation selector-193\n917937 variation selector-194\n917938 variation selector-195\n917939 variation selector-196\n917940 variation selector-197\n917941 variation selector-198\n917942 variation selector-199\n917943 variation selector-200\n917944 variation selector-201\n917945 variation selector-202\n917946 variation selector-203\n917947 variation selector-204\n917948 variation selector-205\n917949 variation selector-206\n917950 variation selector-207\n917951 variation selector-208\n917952 variation selector-209\n917953 variation selector-210\n917954 variation selector-211\n917955 variation selector-212\n917956 variation selector-213\n917957 variation selector-214\n917958 variation selector-215\n917959 variation selector-216\n917960 variation selector-217\n917961 variation selector-218\n917962 variation selector-219\n917963 variation selector-220\n917964 variation selector-221\n917965 variation selector-222\n917966 variation selector-223\n917967 variation selector-224\n917968 variation selector-225\n917969 variation selector-226\n917970 variation selector-227\n917971 variation selector-228\n917972 variation selector-229\n917973 variation selector-230\n917974 variation selector-231\n917975 variation selector-232\n917976 variation selector-233\n917977 variation selector-234\n917978 variation selector-235\n917979 variation selector-236\n917980 variation selector-237\n917981 variation selector-238\n917982 variation selector-239\n917983 variation selector-240\n917984 variation selector-241\n917985 variation selector-242\n917986 variation selector-243\n917987 variation selector-244\n917988 variation selector-245\n917989 variation selector-246\n917990 variation selector-247\n917991 variation selector-248\n917992 variation selector-249\n917993 variation selector-250\n917994 variation selector-251\n917995 variation selector-252\n917996 variation selector-253\n917997 variation selector-254\n917998 variation selector-255\n917999 variation selector-256\n983040 <plane 15 private use, first>\n983041 vector square\n983042 access point network\n983043 access point\n983044 account\n983045 account alert\n983046 account box\n983047 account box outline\n983048 account check\n983049 account circle\n983050 account convert\n983051 account key\n983052 tooltip account\n983053 account minus\n983054 account multiple\n983055 account multiple outline\n983056 account multiple plus\n983057 account network\n983058 account off\n983059 account outline\n983060 account plus\n983061 account remove\n983062 account search\n983063 account star\n983064 orbit\n983065 account switch\n983066 adjust\n983067 air conditioner\n983068 airballoon\n983069 airplane\n983070 airplane off\n983071 cast variant\n983072 alarm\n983073 alarm check\n983074 alarm multiple\n983075 alarm off\n983076 alarm plus\n983077 album\n983078 alert\n983079 alert box\n983080 alert circle\n983081 alert octagon\n983082 alert outline\n983083 alpha\n983084 alphabetical\n983085 greenhouse\n983086 rollerblade off\n983087 ambulance\n983088 amplifier\n983089 anchor\n983090 android\n983091 web plus\n983092 android studio\n983093 apple\n983094 apple finder\n983095 apple ios\n983096 apple icloud\n983097 apple safari\n983098 font awesome\n983099 apps\n983100 archive\n983101 arrange bring forward\n983102 arrange bring to front\n983103 arrange send backward\n983104 arrange send to back\n983105 arrow all\n983106 arrow bottom left\n983107 arrow bottom right\n983108 arrow collapse all\n983109 arrow down\n983110 arrow down thick\n983111 arrow down bold circle\n983112 arrow down bold circle outline\n983113 arrow down bold hexagon outline\n983114 arrow down drop circle\n983115 arrow down drop circle outline\n983116 arrow expand all\n983117 arrow left\n983118 arrow left thick\n983119 arrow left bold circle\n983120 arrow left bold circle outline\n983121 arrow left bold hexagon outline\n983122 arrow left drop circle\n983123 arrow left drop circle outline\n983124 arrow right\n983125 arrow right thick\n983126 arrow right bold circle\n983127 arrow right bold circle outline\n983128 arrow right bold hexagon outline\n983129 arrow right drop circle\n983130 arrow right drop circle outline\n983131 arrow top left\n983132 arrow top right\n983133 arrow up\n983134 arrow up thick\n983135 arrow up bold circle\n983136 arrow up bold circle outline\n983137 arrow up bold hexagon outline\n983138 arrow up drop circle\n983139 arrow up drop circle outline\n983140 assistant\n983141 at\n983142 attachment\n983143 book music\n983144 auto fix\n983145 auto upload\n983146 autorenew\n983147 av timer\n983148 baby\n983149 backburger\n983150 backspace\n983151 backup restore\n983152 bank\n983153 barcode\n983154 barcode scan\n983155 barley\n983156 barrel\n983157 incognito off\n983158 basket\n983159 basket fill\n983160 basket unfill\n983161 battery\n983162 battery 10\n983163 battery 20\n983164 battery 30\n983165 battery 40\n983166 battery 50\n983167 battery 60\n983168 battery 70\n983169 battery 80\n983170 battery 90\n983171 battery alert\n983172 battery charging\n983173 battery charging 100\n983174 battery charging 20\n983175 battery charging 30\n983176 battery charging 40\n983177 battery charging 60\n983178 battery charging 80\n983179 battery charging 90\n983180 battery minus variant\n983181 battery negative\n983182 battery outline\n983183 battery plus variant\n983184 battery positive\n983185 battery unknown\n983186 beach\n983187 flask\n983188 flask empty\n983189 flask empty outline\n983190 flask outline\n983191 bunk bed outline\n983192 beer\n983193 bed outline\n983194 bell\n983195 bell off\n983196 bell outline\n983197 bell plus\n983198 bell ring\n983199 bell ring outline\n983200 bell sleep\n983201 beta\n983202 book cross\n983203 bike\n983204 microsoft bing\n983205 binoculars\n983206 bio\n983207 biohazard\n983208 bitbucket\n983209 black mesa\n983210 shield refresh\n983211 blender software\n983212 blinds\n983213 block helper\n983214 application edit\n983215 bluetooth\n983216 bluetooth audio\n983217 bluetooth connect\n983218 bluetooth off\n983219 bluetooth settings\n983220 bluetooth transfer\n983221 blur\n983222 blur linear\n983223 blur off\n983224 blur radial\n983225 bone\n983226 book\n983227 book multiple\n983228 book variant multiple\n983229 book open\n983230 book open blank variant\n983231 book variant\n983232 bookmark\n983233 bookmark check\n983234 bookmark music\n983235 bookmark outline\n983236 bookmark plus outline\n983237 bookmark plus\n983238 bookmark remove\n983239 border all\n983240 border bottom\n983241 border color\n983242 border horizontal\n983243 border inside\n983244 border left\n983245 border none\n983246 border outside\n983247 border right\n983248 border style\n983249 border top\n983250 border vertical\n983251 bowling\n983252 box\n983253 box cutter\n983254 briefcase\n983255 briefcase check\n983256 briefcase download\n983257 briefcase upload\n983258 brightness 1\n983259 brightness 2\n983260 brightness 3\n983261 brightness 4\n983262 brightness 5\n983263 brightness 6\n983264 brightness 7\n983265 brightness auto\n983266 broom\n983267 brush\n983268 bug\n983269 bulletin board\n983270 bullhorn\n983271 bus\n983272 cached\n983273 cake\n983274 cake layered\n983275 cake variant\n983276 calculator\n983277 calendar\n983278 calendar blank\n983279 calendar check\n983280 calendar clock\n983281 calendar multiple\n983282 calendar multiple check\n983283 calendar plus\n983284 calendar remove\n983285 calendar text\n983286 calendar today\n983287 call made\n983288 call merge\n983289 call missed\n983290 call received\n983291 call split\n983292 camcorder\n983293 video box\n983294 video box off\n983295 camcorder off\n983296 camera\n983297 camera enhance\n983298 camera front\n983299 camera front variant\n983300 camera iris\n983301 camera party mode\n983302 camera rear\n983303 camera rear variant\n983304 camera switch\n983305 camera timer\n983306 candycane\n983307 car\n983308 car battery\n983309 car connected\n983310 car wash\n983311 carrot\n983312 cart\n983313 cart outline\n983314 cart plus\n983315 case sensitive alt\n983316 cash\n983317 cash 100\n983318 cash multiple\n983319 checkbox blank badge outline\n983320 cast\n983321 cast connected\n983322 castle\n983323 cat\n983324 cellphone\n983325 tray arrow up\n983326 cellphone basic\n983327 cellphone dock\n983328 tray arrow down\n983329 cellphone link\n983330 cellphone link off\n983331 cellphone settings\n983332 certificate\n983333 chair school\n983334 chart arc\n983335 chart areaspline\n983336 chart bar\n983337 chart histogram\n983338 chart line\n983339 chart pie\n983340 check\n983341 check all\n983342 checkbox blank\n983343 checkbox blank circle\n983344 checkbox blank circle outline\n983345 checkbox blank outline\n983346 checkbox marked\n983347 checkbox marked circle\n983348 checkbox marked circle outline\n983349 checkbox marked outline\n983350 checkbox multiple blank\n983351 checkbox multiple blank outline\n983352 checkbox multiple marked\n983353 checkbox multiple marked outline\n983354 checkerboard\n983355 chemical weapon\n983356 chevron double down\n983357 chevron double left\n983358 chevron double right\n983359 chevron double up\n983360 chevron down\n983361 chevron left\n983362 chevron right\n983363 chevron up\n983364 church\n983365 roller skate off\n983366 city\n983367 clipboard\n983368 clipboard account\n983369 clipboard alert\n983370 clipboard arrow down\n983371 clipboard arrow left\n983372 clipboard outline\n983373 clipboard text\n983374 clipboard check\n983375 clippy\n983376 clock outline\n983377 clock end\n983378 clock fast\n983379 clock in\n983380 clock out\n983381 clock start\n983382 close\n983383 close box\n983384 close box outline\n983385 close circle\n983386 close circle outline\n983387 close network\n983388 close octagon\n983389 close octagon outline\n983390 closed caption\n983391 cloud\n983392 cloud check\n983393 cloud circle\n983394 cloud download\n983395 cloud outline\n983396 cloud off outline\n983397 cloud print\n983398 cloud print outline\n983399 cloud upload\n983400 code array\n983401 code braces\n983402 code brackets\n983403 code equal\n983404 code greater than\n983405 code greater than or equal\n983406 code less than\n983407 code less than or equal\n983408 code not equal\n983409 code not equal variant\n983410 code parentheses\n983411 code string\n983412 code tags\n983413 codepen\n983414 coffee\n983415 coffee to go\n983416 bell badge outline\n983417 color helper\n983418 comment\n983419 comment account\n983420 comment account outline\n983421 comment alert\n983422 comment alert outline\n983423 comment check\n983424 comment check outline\n983425 comment multiple outline\n983426 comment outline\n983427 comment plus outline\n983428 comment processing\n983429 comment processing outline\n983430 comment question outline\n983431 comment remove outline\n983432 comment text\n983433 comment text outline\n983434 compare\n983435 compass\n983436 compass outline\n983437 console\n983438 card account mail\n983439 content copy\n983440 content cut\n983441 content duplicate\n983442 content paste\n983443 content save\n983444 content save all\n983445 contrast\n983446 contrast box\n983447 contrast circle\n983448 cookie\n983449 counter\n983450 cow\n983451 credit card outline\n983452 credit card multiple outline\n983453 credit card scan outline\n983454 crop\n983455 crop free\n983456 crop landscape\n983457 crop portrait\n983458 crop square\n983459 crosshairs\n983460 crosshairs gps\n983461 crown\n983462 cube\n983463 cube outline\n983464 cube send\n983465 cube unfolded\n983466 cup\n983467 cup water\n983468 currency btc\n983469 currency eur\n983470 currency gbp\n983471 currency inr\n983472 currency ngn\n983473 currency rub\n983474 currency try\n983475 delete variant\n983476 delete\n983477 decimal increase\n983478 decimal decrease\n983479 debug step over\n983480 debug step out\n983481 debug step into\n983482 database plus\n983483 database minus\n983484 database\n983485 cursor pointer\n983486 cursor move\n983487 cursor default outline\n983488 cursor default\n983489 currency usd\n983490 delta\n983491 deskphone\n983492 desktop mac\n983493 desktop tower\n983494 details\n983495 deviantart\n983496 diamond stone\n983497 ab testing\n983498 dice 1\n983499 dice 2\n983500 dice 3\n983501 dice 4\n983502 dice 5\n983503 dice 6\n983504 directions\n983505 disc alert\n983506 disqus\n983507 video plus outline\n983508 division\n983509 division box\n983510 dns\n983511 domain\n983512 dots horizontal\n983513 dots vertical\n983514 download\n983515 drag\n983516 drag horizontal\n983517 drag vertical\n983518 drawing\n983519 drawing box\n983520 shield refresh outline\n983521 calendar refresh\n983522 drone\n983523 dropbox\n983524 drupal\n983525 duck\n983526 dumbbell\n983527 earth\n983528 earth off\n983529 microsoft edge\n983530 eject\n983531 elevation decline\n983532 elevation rise\n983533 elevator\n983534 email\n983535 email open\n983536 email outline\n983537 email lock\n983538 emoticon outline\n983539 emoticon cool outline\n983540 emoticon devil outline\n983541 emoticon happy outline\n983542 emoticon neutral outline\n983543 emoticon poop\n983544 emoticon sad outline\n983545 emoticon tongue\n983546 engine\n983547 engine outline\n983548 equal\n983549 equal box\n983550 eraser\n983551 escalator\n983552 ethernet\n983553 ethernet cable\n983554 ethernet cable off\n983555 calendar refresh outline\n983556 evernote\n983557 exclamation\n983558 exit to app\n983559 export\n983560 eye\n983561 eye off\n983562 eyedropper\n983563 eyedropper variant\n983564 facebook\n983565 order alphabetical ascending\n983566 facebook messenger\n983567 factory\n983568 fan\n983569 fast forward\n983570 fax\n983571 ferry\n983572 file\n983573 file chart\n983574 file check\n983575 file cloud\n983576 file delimited\n983577 file document\n983578 text box\n983579 file excel\n983580 file excel box\n983581 file export\n983582 file find\n983583 file image\n983584 file import\n983585 file lock\n983586 file multiple\n983587 file music\n983588 file outline\n983589 file jpg box\n983590 file pdf box\n983591 file powerpoint\n983592 file powerpoint box\n983593 file presentation box\n983594 file send\n983595 file video\n983596 file word\n983597 file word box\n983598 file code\n983599 film\n983600 filmstrip\n983601 filmstrip off\n983602 filter\n983603 filter outline\n983604 filter remove\n983605 filter remove outline\n983606 filter variant\n983607 fingerprint\n983608 fire\n983609 firefox\n983610 fish\n983611 flag\n983612 flag checkered\n983613 flag outline\n983614 flag variant outline\n983615 flag triangle\n983616 flag variant\n983617 flash\n983618 flash auto\n983619 flash off\n983620 flashlight\n983621 flashlight off\n983622 star half\n983623 flip to back\n983624 flip to front\n983625 floppy\n983626 flower\n983627 folder\n983628 folder account\n983629 folder download\n983630 folder google drive\n983631 folder image\n983632 folder lock\n983633 folder lock open\n983634 folder move\n983635 folder multiple\n983636 folder multiple image\n983637 folder multiple outline\n983638 folder outline\n983639 folder plus\n983640 folder remove\n983641 folder upload\n983642 food\n983643 food apple\n983644 food variant\n983645 football\n983646 football australian\n983647 football helmet\n983648 format align center\n983649 format align justify\n983650 format align left\n983651 format align right\n983652 format bold\n983653 format clear\n983654 format color fill\n983655 format float center\n983656 format float left\n983657 format float none\n983658 format float right\n983659 format header 1\n983660 format header 2\n983661 format header 3\n983662 format header 4\n983663 format header 5\n983664 format header 6\n983665 format header decrease\n983666 format header equal\n983667 format header increase\n983668 format header pound\n983669 format indent decrease\n983670 format indent increase\n983671 format italic\n983672 format line spacing\n983673 format list bulleted\n983674 format list bulleted type\n983675 format list numbered\n983676 format paint\n983677 format paragraph\n983678 format quote close\n983679 format size\n983680 format strikethrough\n983681 format strikethrough variant\n983682 format subscript\n983683 format superscript\n983684 format text\n983685 format textdirection l to r\n983686 format textdirection r to l\n983687 format underline\n983688 format wrap inline\n983689 format wrap square\n983690 format wrap tight\n983691 format wrap top bottom\n983692 forum\n983693 forward\n983694 bowl\n983695 fridge outline\n983696 fridge\n983697 fridge top\n983698 fridge bottom\n983699 fullscreen\n983700 fullscreen exit\n983701 function\n983702 gamepad\n983703 gamepad variant\n983704 gas station\n983705 gate\n983706 gauge\n983707 gavel\n983708 gender female\n983709 gender male\n983710 gender male female\n983711 gender transgender\n983712 ghost\n983713 gift outline\n983714 git\n983715 card account details star\n983716 github\n983717 glass flute\n983718 glass mug\n983719 glass stange\n983720 glass tulip\n983721 bowl outline\n983722 glasses\n983723 gmail\n983724 gnome\n983725 google\n983726 google cardboard\n983727 google chrome\n983728 google circles\n983729 google circles communities\n983730 google circles extended\n983731 google circles group\n983732 google controller\n983733 google controller off\n983734 google drive\n983735 google earth\n983736 google glass\n983737 google nearby\n983738 video minus outline\n983739 microsoft teams\n983740 google play\n983741 google plus\n983742 order bool ascending\n983743 google translate\n983744 google classroom\n983745 grid\n983746 grid off\n983747 group\n983748 guitar electric\n983749 guitar pick\n983750 guitar pick outline\n983751 hand pointing right\n983752 hanger\n983753 google hangouts\n983754 harddisk\n983755 headphones\n983756 headphones box\n983757 headphones settings\n983758 headset\n983759 headset dock\n983760 headset off\n983761 heart\n983762 heart box\n983763 heart box outline\n983764 heart broken\n983765 heart outline\n983766 help\n983767 help circle\n983768 hexagon\n983769 hexagon outline\n983770 history\n983771 hololens\n983772 home\n983773 home modern\n983774 home variant\n983775 hops\n983776 hospital box\n983777 hospital building\n983778 hospital marker\n983779 bed\n983780 bowl mix outline\n983781 pot\n983782 human\n983783 human child\n983784 human male female\n983785 image\n983786 image album\n983787 image area\n983788 image area close\n983789 image broken\n983790 image broken variant\n983791 image multiple outline\n983792 image filter black white\n983793 image filter center focus\n983794 image filter center focus weak\n983795 image filter drama\n983796 image filter frames\n983797 image filter hdr\n983798 image filter none\n983799 image filter tilt shift\n983800 image filter vintage\n983801 image multiple\n983802 import\n983803 inbox arrow down\n983804 information\n983805 information outline\n983806 instagram\n983807 pot outline\n983808 microsoft internet explorer\n983809 invert colors\n983810 jeepney\n983811 jira\n983812 jsfiddle\n983813 keg\n983814 key\n983815 key change\n983816 key minus\n983817 key plus\n983818 key remove\n983819 key variant\n983820 keyboard\n983821 keyboard backspace\n983822 keyboard caps\n983823 keyboard close\n983824 keyboard off\n983825 keyboard return\n983826 keyboard tab\n983827 keyboard variant\n983828 kodi\n983829 label\n983830 label outline\n983831 lan\n983832 lan connect\n983833 lan disconnect\n983834 lan pending\n983835 language csharp\n983836 language css3\n983837 language html5\n983838 language javascript\n983839 language php\n983840 language python\n983841 contactless payment circle\n983842 laptop\n983843 magazine rifle\n983844 magazine pistol\n983845 keyboard tab reverse\n983846 pot steam outline\n983847 launch\n983848 layers\n983849 layers off\n983850 leaf\n983851 led off\n983852 led on\n983853 led outline\n983854 led variant off\n983855 led variant on\n983856 led variant outline\n983857 library\n983858 filmstrip box\n983859 music box multiple\n983860 plus box multiple\n983861 lightbulb\n983862 lightbulb outline\n983863 link\n983864 link off\n983865 link variant\n983866 link variant off\n983867 linkedin\n983868 sort reverse variant\n983869 linux\n983870 lock\n983871 lock open\n983872 lock open outline\n983873 lock outline\n983874 login\n983875 logout\n983876 looks\n983877 loupe\n983878 lumx\n983879 magnet\n983880 magnet on\n983881 magnify\n983882 magnify minus\n983883 magnify plus\n983884 plus circle multiple\n983885 map\n983886 map marker\n983887 map marker circle\n983888 map marker multiple\n983889 map marker off\n983890 map marker radius\n983891 margin\n983892 language markdown\n983893 marker check\n983894 glass cocktail\n983895 material ui\n983896 math compass\n983897 stackpath\n983898 minus circle multiple\n983899 memory\n983900 menu\n983901 menu down\n983902 menu left\n983903 menu right\n983904 menu up\n983905 message\n983906 message alert\n983907 message draw\n983908 message image\n983909 message outline\n983910 message processing\n983911 message reply\n983912 message reply text\n983913 message text\n983914 message text outline\n983915 message video\n983916 microphone\n983917 microphone off\n983918 microphone outline\n983919 microphone settings\n983920 microphone variant\n983921 microphone variant off\n983922 microsoft\n983923 minecraft\n983924 minus\n983925 minus box\n983926 minus circle\n983927 minus circle outline\n983928 minus network\n983929 monitor\n983930 monitor multiple\n983931 more\n983932 motorbike\n983933 mouse\n983934 mouse off\n983935 mouse variant\n983936 mouse variant off\n983937 movie\n983938 multiplication\n983939 multiplication box\n983940 music box\n983941 music box outline\n983942 music circle\n983943 music note\n983945 music note half\n983946 music note off\n983947 music note quarter\n983948 music note sixteenth\n983949 music note whole\n983950 nature\n983951 nature people\n983952 navigation\n983953 needle\n983954 smoke detector\n983955 thermostat\n983956 new box\n983957 newspaper\n983958 nfc\n983959 nfc tap\n983960 nfc variant\n983961 nodejs\n983962 note\n983963 note outline\n983964 note plus\n983965 note plus outline\n983966 note text\n983967 notification clear all\n983968 numeric\n983969 numeric 0 box\n983970 numeric 0 box multiple outline\n983971 numeric 0 box outline\n983972 numeric 1 box\n983973 numeric 1 box multiple outline\n983974 numeric 1 box outline\n983975 numeric 2 box\n983976 numeric 2 box multiple outline\n983977 numeric 2 box outline\n983978 numeric 3 box\n983979 numeric 3 box multiple outline\n983980 numeric 3 box outline\n983981 numeric 4 box\n983982 numeric 4 box outline\n983983 numeric 5 box multiple outline\n983984 numeric 5 box outline\n983985 numeric 5 box\n983986 numeric 4 box multiple outline\n983987 numeric 6 box\n983988 numeric 6 box multiple outline\n983989 numeric 6 box outline\n983990 numeric 7 box\n983991 numeric 7 box multiple outline\n983992 numeric 7 box outline\n983993 numeric 8 box\n983994 numeric 8 box multiple outline\n983995 numeric 8 box outline\n983996 numeric 9 box\n983997 numeric 9 box multiple outline\n983998 numeric 9 box outline\n983999 numeric 9 plus box\n984000 numeric 9 plus box multiple outline\n984001 numeric 9 plus box outline\n984002 nutrition\n984003 octagon\n984004 octagon outline\n984005 odnoklassniki\n984006 microsoft office\n984007 oil\n984008 coolant temperature\n984009 omega\n984010 microsoft onedrive\n984011 open in app\n984012 open in new\n984013 openid\n984014 opera\n984015 ornament\n984016 ornament variant\n984017 inbox arrow up\n984018 owl\n984019 package\n984020 package down\n984021 package up\n984022 package variant\n984023 package variant closed\n984024 palette\n984025 palette advanced\n984026 panda\n984027 pandora\n984028 panorama\n984029 panorama fisheye\n984030 panorama horizontal outline\n984031 panorama vertical outline\n984032 panorama wide angle outline\n984033 paper cut vertical\n984034 paperclip\n984035 parking\n984036 pause\n984037 pause circle\n984038 pause circle outline\n984039 pause octagon\n984040 pause octagon outline\n984041 paw\n984042 pen\n984043 pencil\n984044 pencil box\n984045 pencil box outline\n984046 pencil lock\n984047 pencil off\n984048 percent\n984049 mortar pestle plus\n984050 phone\n984051 phone bluetooth\n984052 phone forward\n984053 phone hangup\n984054 phone in talk\n984055 phone incoming\n984056 phone lock\n984057 phone log\n984058 phone missed\n984059 phone outgoing\n984060 phone paused\n984061 phone settings\n984062 phone voip\n984063 pi\n984064 pi box\n984065 pig\n984066 pill\n984067 pin\n984068 pin off\n984069 pine tree\n984070 pine tree box\n984071 pinterest\n984072 contactless payment circle outline\n984073 pizza\n984074 play\n984075 play box outline\n984076 play circle\n984077 play circle outline\n984078 play pause\n984079 play protected content\n984080 playlist minus\n984081 playlist play\n984082 playlist plus\n984083 playlist remove\n984084 sony playstation\n984085 plus\n984086 plus box\n984087 plus circle\n984088 plus circle multiple outline\n984089 plus circle outline\n984090 plus network\n984091 sledding\n984092 wall sconce flat variant\n984093 pokeball\n984094 polaroid\n984095 poll\n984096 account eye\n984097 polymer\n984098 popcorn\n984099 pound\n984100 pound box\n984101 power\n984102 power settings\n984103 power socket\n984104 presentation\n984105 presentation play\n984106 printer\n984107 printer 3d\n984108 printer alert\n984109 professional hexagon\n984110 projector\n984111 projector screen\n984112 pulse\n984113 puzzle\n984114 qrcode\n984115 qrcode scan\n984116 quadcopter\n984117 quality high\n984118 book multiple outline\n984119 radar\n984120 radiator\n984121 radio\n984122 radio handheld\n984123 radio tower\n984124 radioactive\n984126 radiobox marked\n984127 raspberry pi\n984128 ray end\n984129 ray end arrow\n984130 ray start\n984131 ray start arrow\n984132 ray start end\n984133 ray vertex\n984134 lastpass\n984135 read\n984136 youtube tv\n984137 receipt\n984138 record\n984139 record rec\n984140 recycle\n984141 reddit\n984142 redo\n984143 redo variant\n984144 refresh\n984145 regex\n984146 relative scale\n984147 reload\n984148 remote\n984149 rename box\n984150 repeat\n984151 repeat off\n984152 repeat once\n984153 replay\n984154 reply\n984155 reply all\n984156 reproduction\n984157 resize bottom right\n984158 responsive\n984159 rewind\n984160 ribbon\n984161 road\n984162 road variant\n984163 rocket\n984164 rotate 3d variant\n984165 rotate left\n984166 rotate left variant\n984167 rotate right\n984168 rotate right variant\n984169 router wireless\n984170 routes\n984171 rss\n984172 rss box\n984173 ruler\n984174 run fast\n984175 sale\n984176 satellite\n984177 satellite variant\n984178 scale\n984179 scale bathroom\n984180 school\n984181 screen rotation\n984182 screwdriver\n984183 script outline\n984184 screen rotation lock\n984185 sd\n984186 seal\n984187 seat flat\n984188 seat flat angled\n984189 seat individual suite\n984190 seat legroom extra\n984191 seat legroom normal\n984192 seat legroom reduced\n984193 seat recline extra\n984194 seat recline normal\n984195 security\n984196 security network\n984197 select\n984198 select all\n984199 select inverse\n984200 select off\n984201 selection\n984202 send\n984203 server\n984204 server minus\n984205 server network\n984206 server network off\n984207 server off\n984208 server plus\n984209 server remove\n984210 server security\n984211 cog\n984212 cog box\n984213 shape plus\n984214 share\n984215 share variant\n984216 shield\n984217 shield outline\n984218 shopping\n984219 shopping music\n984220 shredder\n984221 shuffle\n984222 shuffle disabled\n984223 shuffle variant\n984224 sigma\n984225 sign caution\n984226 signal\n984227 silverware\n984228 silverware fork\n984229 silverware spoon\n984230 silverware variant\n984231 sim\n984232 sim alert\n984233 sim off\n984234 sitemap\n984235 skip backward\n984236 skip forward\n984237 skip next\n984238 skip previous\n984239 skype\n984240 skype business\n984241 slack\n984242 sleep\n984243 sleep off\n984244 smoking\n984245 smoking off\n984246 snapchat\n984247 snowman\n984248 soccer\n984249 sofa\n984250 sort\n984251 sort alphabetical variant\n984252 sort ascending\n984253 sort descending\n984254 sort numeric variant\n984255 sort variant\n984256 soundcloud\n984257 source fork\n984258 source pull\n984259 speaker\n984260 speaker off\n984261 speedometer\n984262 spellcheck\n984263 spotify\n984264 spotlight\n984265 spotlight beam\n984266 book remove multiple outline\n984267 account switch outline\n984268 stack overflow\n984269 stairs\n984270 star\n984271 star circle\n984272 star half full\n984273 star off\n984274 star outline\n984275 steam\n984276 steering\n984277 step backward\n984278 step backward 2\n984279 step forward\n984280 step forward 2\n984281 stethoscope\n984282 stocking\n984283 stop\n984284 store\n984285 store 24 hour\n984286 stove\n984287 subway variant\n984288 sunglasses\n984289 swap horizontal\n984290 swap vertical\n984291 swim\n984292 switch\n984293 sword\n984294 sync\n984295 sync alert\n984296 sync off\n984297 tab\n984298 tab unselected\n984299 table\n984300 table column plus after\n984301 table column plus before\n984302 table column remove\n984303 table column width\n984304 table edit\n984305 table large\n984306 table row height\n984307 table row plus after\n984308 table row plus before\n984309 table row remove\n984310 tablet\n984311 tablet android\n984312 tangram\n984313 tag\n984314 tag faces\n984315 tag multiple\n984316 tag outline\n984317 tag text outline\n984318 target\n984319 taxi\n984320 teamviewer\n984321 skateboarding\n984322 television\n984323 television guide\n984324 temperature celsius\n984325 temperature fahrenheit\n984326 temperature kelvin\n984327 tennis ball\n984328 tent\n984330 text to speech\n984331 text to speech off\n984332 texture\n984333 theater\n984334 theme light dark\n984335 thermometer\n984336 thermometer lines\n984337 thumb down\n984338 thumb down outline\n984339 thumb up\n984340 thumb up outline\n984341 thumbs up down\n984342 ticket\n984343 ticket account\n984344 ticket confirmation\n984345 tie\n984346 timelapse\n984347 timer outline\n984348 timer 10\n984349 timer 3\n984350 timer off outline\n984351 timer sand\n984352 timetable\n984353 toggle switch\n984354 toggle switch off\n984355 tooltip\n984356 tooltip edit\n984357 tooltip image\n984358 tooltip outline\n984359 tooltip plus outline\n984360 tooltip text\n984361 tooth outline\n984362 cloud refresh\n984363 traffic light\n984364 train\n984365 tram\n984366 transcribe\n984367 transcribe close\n984368 transfer right\n984369 tree\n984370 trello\n984371 trending down\n984372 trending neutral\n984373 trending up\n984374 triangle\n984375 triangle outline\n984376 trophy\n984377 trophy award\n984378 trophy outline\n984379 trophy variant\n984380 trophy variant outline\n984381 truck\n984382 truck delivery\n984383 tshirt crew outline\n984384 tshirt v outline\n984385 file refresh outline\n984386 folder refresh outline\n984387 twitch\n984388 twitter\n984389 order numeric ascending\n984390 order numeric descending\n984391 repeat variant\n984392 ubuntu\n984393 umbraco\n984394 umbrella\n984395 umbrella outline\n984396 undo\n984397 undo variant\n984398 unfold less horizontal\n984399 unfold more horizontal\n984400 ungroup\n984401 web remove\n984402 upload\n984403 usb\n984404 vector arrange above\n984405 vector arrange below\n984406 vector circle\n984407 vector circle variant\n984408 vector combine\n984409 vector curve\n984410 vector difference\n984411 vector difference ab\n984412 vector difference ba\n984413 vector intersection\n984414 vector line\n984415 vector point\n984416 vector polygon\n984417 vector polyline\n984418 vector selection\n984419 vector triangle\n984420 vector union\n984421 shield check\n984422 vibrate\n984423 video\n984424 video off\n984425 video switch\n984426 view agenda\n984427 view array\n984428 view carousel\n984429 view column\n984430 view dashboard\n984431 view day\n984432 view grid\n984433 view headline\n984434 view list\n984435 view module\n984436 view quilt\n984437 view stream\n984438 view week\n984439 vimeo\n984440 buffet\n984441 hands pray\n984442 credit card wireless off\n984443 credit card wireless off outline\n984444 vlc\n984445 voicemail\n984446 volume high\n984447 volume low\n984448 volume medium\n984449 volume off\n984450 vpn\n984451 walk\n984452 wallet\n984453 wallet giftcard\n984454 wallet membership\n984455 wallet travel\n984456 wan\n984457 watch\n984458 watch export\n984459 watch import\n984460 water\n984461 water off\n984462 water percent\n984463 water pump\n984464 weather cloudy\n984465 weather fog\n984466 weather hail\n984467 weather lightning\n984468 weather night\n984469 weather partly cloudy\n984470 weather pouring\n984471 weather rainy\n984472 weather snowy\n984473 weather sunny\n984474 weather sunset\n984475 weather sunset down\n984476 weather sunset up\n984477 weather windy\n984478 weather windy variant\n984479 web\n984480 webcam\n984481 weight\n984482 weight kilogram\n984483 whatsapp\n984484 wheelchair accessibility\n984485 white balance auto\n984486 white balance incandescent\n984487 white balance iridescent\n984488 white balance sunny\n984489 wifi\n984490 wifi off\n984491 nintendo wii\n984492 wikipedia\n984493 window close\n984494 window closed\n984495 window maximize\n984496 window minimize\n984497 window open\n984498 window restore\n984499 microsoft windows\n984500 wordpress\n984501 account hard hat\n984502 wrap\n984503 wrench\n984504 contacts outline\n984505 microsoft xbox\n984506 microsoft xbox controller\n984507 microsoft xbox controller off\n984508 table furniture\n984509 sort alphabetical ascending\n984510 firewire\n984511 sort alphabetical descending\n984512 xml\n984513 yeast\n984514 database refresh\n984515 youtube\n984516 zip box\n984517 surround sound\n984518 vector rectangle\n984519 playlist check\n984520 format line style\n984521 format line weight\n984522 translate\n984523 account voice\n984524 opacity\n984525 near me\n984526 clock alert outline\n984527 human pregnant\n984528 sticker circle outline\n984529 scale balance\n984530 card account details\n984531 account multiple minus\n984532 airplane landing\n984533 airplane takeoff\n984534 alert circle outline\n984535 altimeter\n984536 animation\n984537 book minus\n984538 book open page variant\n984539 book plus\n984540 boombox\n984541 bullseye\n984542 comment remove\n984543 camera off\n984544 check circle\n984545 check circle outline\n984546 candle\n984547 chart bubble\n984548 credit card off outline\n984549 cup off\n984550 copyright\n984551 cursor text\n984552 delete forever\n984553 delete sweep\n984554 dice d20 outline\n984555 dice d4 outline\n984556 dice d8 outline\n984557 dice d6 outline\n984558 disc\n984559 email open outline\n984560 email variant\n984561 ev station\n984562 food fork drink\n984563 food off\n984564 format title\n984565 google maps\n984566 heart pulse\n984567 highway\n984568 home map marker\n984569 incognito\n984570 kettle\n984571 lock plus\n984573 logout variant\n984574 music note bluetooth\n984575 music note bluetooth off\n984576 page first\n984577 page last\n984578 phone classic\n984579 priority high\n984580 priority low\n984581 qqchat\n984582 pool\n984583 rounded corner\n984584 rowing\n984585 saxophone\n984586 signal variant\n984587 stack exchange\n984588 subdirectory arrow left\n984589 subdirectory arrow right\n984590 form textbox\n984591 violin\n984592 microsoft visual studio\n984593 wechat\n984594 watermark\n984595 file hidden\n984596 application outline\n984597 arrow collapse\n984598 arrow expand\n984599 bowl mix\n984600 bridge\n984601 application edit outline\n984602 chip\n984603 content save settings\n984604 dialpad\n984605 book alphabet\n984606 format horizontal align center\n984607 format horizontal align left\n984608 format horizontal align right\n984609 format vertical align bottom\n984610 format vertical align center\n984611 format vertical align top\n984612 line scan\n984613 help circle outline\n984614 code json\n984615 lambda\tlamda\n984616 matrix\n984617 meteor\n984618 close circle multiple\n984619 sigma lower\n984620 source branch\n984621 source merge\n984622 tune\n984623 webhook\n984624 account settings\n984625 account details\n984626 apple keyboard caps\n984627 apple keyboard command\n984628 apple keyboard control\n984629 apple keyboard option\n984630 apple keyboard shift\n984631 box shadow\n984632 cards\n984633 cards outline\n984634 cards playing outline\n984635 checkbox multiple blank circle\n984636 checkbox multiple blank circle outline\n984637 checkbox multiple marked circle\n984638 checkbox multiple marked circle outline\n984639 cloud sync\n984640 collage\n984641 directions fork\n984642 eraser variant\n984643 face man\n984644 face man profile\n984645 file tree\n984646 format annotation plus\n984647 gas cylinder\n984648 grease pencil\n984649 human female\n984650 human greeting variant\n984651 human handsdown\n984652 human handsup\n984653 human male\n984654 information variant\n984655 lead pencil\n984656 map marker minus\n984657 map marker plus\n984658 marker\n984659 message plus\n984660 microscope\n984661 move resize\n984662 move resize variant\n984663 paw off\n984664 phone minus\n984665 phone plus\n984666 pot steam\n984667 pot mix\n984668 serial port\n984669 shape circle plus\n984670 shape polygon plus\n984671 shape rectangle plus\n984672 shape square plus\n984673 skip next circle\n984674 skip next circle outline\n984675 skip previous circle\n984676 skip previous circle outline\n984677 spray\n984678 stop circle\n984679 stop circle outline\n984680 test tube\n984681 text shadow\n984682 tune vertical\n984683 cart off\n984684 chart gantt\n984685 chart scatter plot hexbin\n984686 chart timeline\n984687 discord\n984688 file restore\n984689 language c\n984690 language cpp\n984691 language xaml\n984692 creation\n984693 application cog\n984694 credit card plus outline\n984695 pot mix outline\n984696 bow tie\n984697 calendar range\n984698 currency usd off\n984699 flash red eye\n984700 oar\n984701 piano\n984702 weather lightning rainy\n984703 weather snowy rainy\n984704 yin yang\n984705 tower beach\n984706 tower fire\n984707 delete circle\n984708 dna\n984709 hamburger\n984710 gondola\n984711 inbox\n984712 reorder horizontal\n984713 reorder vertical\n984714 shield home\n984715 tag heart\n984716 skull\n984717 solid\n984718 alarm snooze\n984719 baby carriage\n984720 beaker outline\n984721 bomb\n984722 calendar question\n984723 camera burst\n984724 code tags check\n984725 circle multiple outline\n984726 crop rotate\n984727 developer board\n984728 piano off\n984729 skate off\n984730 message star\n984731 emoticon dead outline\n984732 emoticon excited outline\n984733 folder star\n984734 format color text\n984735 format section\n984736 gradient vertical\n984737 home outline\n984738 message bulleted\n984739 message bulleted off\n984740 nuke\n984741 power plug\n984742 power plug off\n984743 publish\n984744 credit card marker\n984745 robot\n984746 format rotate 90\n984747 scanner\n984748 subway\n984749 timer sand empty\n984750 transit transfer\n984751 unity\n984752 update\n984753 watch vibrate\n984754 angular\n984755 dolby\n984756 emby\n984757 lamp\n984758 menu down outline\n984759 menu up outline\n984760 note multiple\n984761 note multiple outline\n984762 plex\n984763 shield airplane\n984764 account edit\n984765 alert decagram\n984766 all inclusive\n984767 angularjs\n984768 arrow down box\n984769 arrow left box\n984770 arrow right box\n984771 arrow up box\n984772 asterisk\n984773 bomb off\n984774 bootstrap\n984775 cards variant\n984776 clipboard flow\n984777 close outline\n984778 coffee outline\n984779 contacts\n984780 delete empty\n984781 earth box\n984782 earth box off\n984783 email alert\n984784 eye outline\n984785 eye off outline\n984786 fast forward outline\n984787 feather\n984788 find replace\n984789 flash outline\n984790 format font\n984791 format page break\n984792 format pilcrow\n984793 garage\n984794 garage open\n984795 card account details star outline\n984796 google keep\n984797 snowmobile\n984798 heart half full\n984799 heart half\n984800 heart half outline\n984801 hexagon multiple\n984802 hook\n984803 hook off\n984804 infinity\n984805 language swift\n984806 language typescript\n984807 laptop off\n984808 lightbulb on\n984809 lightbulb on outline\n984810 lock pattern\n984811 folder zip\n984812 magnify minus outline\n984813 magnify plus outline\n984814 mailbox\n984815 medical bag\n984816 message settings\n984817 message cog\n984818 minus box outline\n984819 network\n984820 download network\n984821 help network\n984822 upload network\n984823 npm\n984824 nut\n984825 octagram\n984826 page layout body\n984827 page layout footer\n984828 page layout header\n984829 page layout sidebar left\n984830 page layout sidebar right\n984831 pencil circle\n984832 pentagon outline\n984833 pentagon\n984834 pillar\n984835 pistol\n984836 plus box outline\n984837 plus outline\n984838 prescription\n984839 printer settings\n984840 react\n984841 restart\n984842 rewind outline\n984843 rhombus\n984844 rhombus outline\n984845 robot vacuum\n984846 run\n984847 search web\n984848 shovel\n984849 shovel off\n984850 signal 2g\n984851 signal 3g\n984852 signal 4g\n984853 signal hspa\n984854 signal hspa plus\n984855 snowflake\n984856 source commit\n984857 source commit end\n984858 source commit end local\n984859 source commit local\n984860 source commit next local\n984861 source commit start\n984862 source commit start next local\n984863 speaker wireless\n984864 stadium variant\n984865 svg\n984866 tag plus\n984867 tag remove\n984868 ticket percent\n984869 tilde\n984870 treasure chest\n984871 truck trailer\n984872 view parallel\n984873 view sequential\n984874 washing machine\n984875 webpack\n984876 widgets\n984877 nintendo wiiu\n984878 arrow down bold\n984879 arrow down bold box\n984880 arrow down bold box outline\n984881 arrow left bold\n984882 arrow left bold box\n984883 arrow left bold box outline\n984884 arrow right bold\n984885 arrow right bold box\n984886 arrow right bold box outline\n984887 arrow up bold\n984888 arrow up bold box\n984889 arrow up bold box outline\n984890 cancel\n984891 file account\n984892 gesture double tap\n984893 gesture swipe down\n984894 gesture swipe left\n984895 gesture swipe right\n984896 gesture swipe up\n984897 gesture tap\n984898 gesture two double tap\n984899 gesture two tap\n984900 humble bundle\n984901 kickstarter\n984902 netflix\n984903 microsoft onenote\n984904 wall sconce round\n984905 folder refresh\n984906 vector radius\n984907 microsoft xbox controller battery alert\n984908 microsoft xbox controller battery empty\n984909 microsoft xbox controller battery full\n984910 microsoft xbox controller battery low\n984911 microsoft xbox controller battery medium\n984912 microsoft xbox controller battery unknown\n984913 clipboard plus\n984914 file plus\n984915 format align bottom\n984916 format align middle\n984917 format align top\n984918 format list checks\n984919 format quote open\n984920 grid large\n984921 heart off\n984922 music\n984923 music off\n984924 tab plus\n984925 volume plus\n984926 volume minus\n984927 volume mute\n984928 unfold less vertical\n984929 unfold more vertical\n984930 taco\n984931 square outline\n984932 square\n984935 alert octagram\n984936 atom\n984937 ceiling light\n984938 chart bar stacked\n984939 chart line stacked\n984940 decagram\n984941 decagram outline\n984942 dice multiple\n984943 dice d10 outline\n984944 folder open\n984945 guitar acoustic\n984946 loading\n984947 lock reset\n984948 ninja\n984949 octagram outline\n984950 pencil circle outline\n984951 selection off\n984952 set all\n984953 set center\n984954 set center right\n984955 set left\n984956 set left center\n984957 set left right\n984958 set none\n984959 set right\n984960 shield half full\n984961 sign direction\n984962 sign text\n984963 signal off\n984964 square root\n984965 sticker emoji\n984966 summit\n984967 sword cross\n984968 truck fast\n984969 web check\n984970 cast off\n984971 help box\n984972 timer sand full\n984973 waves\n984974 alarm bell\n984975 alarm light\n984976 video switch outline\n984977 check decagram\n984978 arrow collapse down\n984979 arrow collapse left\n984980 arrow collapse right\n984981 arrow collapse up\n984982 arrow expand down\n984983 arrow expand left\n984984 arrow expand right\n984985 arrow expand up\n984986 book lock\n984987 book lock open\n984988 bus articulated end\n984989 bus articulated front\n984990 bus double decker\n984991 bus school\n984992 bus side\n984993 camera gopro\n984994 camera metering center\n984995 camera metering matrix\n984996 camera metering partial\n984997 camera metering spot\n984998 cannabis\n984999 car convertible\n985000 car estate\n985001 car hatchback\n985002 car pickup\n985003 car side\n985004 car sports\n985005 caravan\n985006 cctv\n985007 chart donut\n985008 chart donut variant\n985009 chart line variant\n985010 chili hot\n985011 chili medium\n985012 chili mild\n985013 cloud braces\n985014 cloud tags\n985015 console line\n985016 corn\n985017 folder zip outline\n985018 currency cny\n985019 currency eth\n985020 currency jpy\n985021 currency krw\n985022 currency sign\n985023 currency twd\n985024 desktop classic\n985025 dip switch\n985026 donkey\n985027 dots horizontal circle\n985028 dots vertical circle\n985029 ear hearing\n985030 elephant\n985031 storefront\n985032 food croissant\n985033 forklift\n985034 fuel\n985035 gesture\n985036 google analytics\n985037 google assistant\n985038 headphones off\n985039 high definition\n985040 home assistant\n985041 home automation\n985042 home circle\n985043 language go\n985044 language r\n985045 lava lamp\n985046 led strip\n985047 locker\n985048 locker multiple\n985049 map marker outline\n985050 metronome\n985051 metronome tick\n985052 micro sd\n985053 facebook gaming\n985054 movie roll\n985055 mushroom\n985056 mushroom outline\n985057 nintendo switch\n985058 null\n985059 passport\n985060 molecule co2\n985061 pipe\n985062 pipe disconnected\n985063 power socket eu\n985064 power socket uk\n985065 power socket us\n985066 rice\n985067 ring\n985068 sass\n985069 send lock\n985070 soy sauce\n985071 standard definition\n985072 surround sound 2 0\n985073 surround sound 3 1\n985074 surround sound 5 1\n985075 surround sound 7 1\n985076 television classic\n985077 form textbox password\n985078 thought bubble\n985079 thought bubble outline\n985080 trackpad\n985081 ultra high definition\n985082 van passenger\n985083 van utility\n985084 vanish\n985085 video 3d\n985086 wall\n985087 xmpp\n985088 account multiple plus outline\n985089 account plus outline\n985090 credit card wireless\n985091 account music\n985092 atlassian\n985093 microsoft azure\n985094 basketball\n985095 battery charging wireless\n985096 battery charging wireless 10\n985097 battery charging wireless 20\n985098 battery charging wireless 30\n985099 battery charging wireless 40\n985100 battery charging wireless 50\n985101 battery charging wireless 60\n985102 battery charging wireless 70\n985103 battery charging wireless 80\n985104 battery charging wireless 90\n985105 battery charging wireless alert\n985106 battery charging wireless outline\n985107 bitcoin\n985108 briefcase outline\n985109 cellphone wireless\n985110 clover\n985111 comment question\n985112 content save outline\n985113 delete restore\n985114 door\n985115 door closed\n985116 door open\n985117 fan off\n985118 file percent\n985119 finance\n985120 lightning bolt circle\n985121 floor plan\n985122 forum outline\n985123 golf\n985124 google home\n985125 guy fawkes mask\n985126 home account\n985127 home heart\n985128 hot tub\n985129 hulu\n985130 ice cream\n985131 image off\n985132 karate\n985133 ladybug\n985134 notebook\n985135 phone return\n985136 poker chip\n985137 shape\n985138 shape outline\n985139 ship wheel\n985140 soccer field\n985141 table column\n985142 table of contents\n985143 table row\n985144 table settings\n985145 television box\n985146 television classic off\n985147 television off\n985148 tow truck\n985149 upload multiple\n985150 video 4k box\n985151 video input antenna\n985152 video input component\n985153 video input hdmi\n985154 video input svideo\n985155 view dashboard variant\n985156 vuejs\n985157 xamarin\n985158 human male board poll\n985159 youtube studio\n985160 youtube gaming\n985161 account group\n985162 camera switch outline\n985163 airport\n985164 arrow collapse horizontal\n985165 arrow collapse vertical\n985166 arrow expand horizontal\n985167 arrow expand vertical\n985168 augmented reality\n985169 badminton\n985170 baseball\n985171 baseball bat\n985172 bottle wine\n985173 check outline\n985174 checkbox intermediate\n985175 chess king\n985176 chess knight\n985177 chess pawn\n985178 chess queen\n985179 chess rook\n985180 chess bishop\n985181 clipboard pulse\n985182 clipboard pulse outline\n985183 comment multiple\n985184 comment text multiple\n985185 comment text multiple outline\n985186 crane\n985187 curling\n985188 currency bdt\n985189 currency kzt\n985190 database search\n985191 dice d12 outline\n985192 docker\n985193 doorbell video\n985194 ethereum\n985195 eye plus\n985196 eye plus outline\n985197 eye settings\n985198 eye settings outline\n985199 file question\n985200 folder network\n985201 function variant\n985202 garage alert\n985203 gauge empty\n985204 gauge full\n985205 gauge low\n985206 glass wine\n985207 graphql\n985208 high definition box\n985209 hockey puck\n985210 hockey sticks\n985211 home alert\n985212 image plus\n985213 jquery\n985214 lifebuoy\n985215 mixed reality\n985216 nativescript\n985217 onepassword\n985218 patreon\n985219 close circle multiple outline\n985220 peace\n985221 phone rotate landscape\n985222 phone rotate portrait\n985223 pier\n985224 pier crane\n985225 pipe leak\n985226 piston\n985227 play network\n985228 reminder\n985229 room service\n985230 salesforce\n985231 shield account\n985232 human male board\n985233 thermostat box\n985234 tractor\n985235 vector ellipse\n985236 virtual reality\n985237 watch export variant\n985238 watch import variant\n985239 watch variant\n985240 weather hurricane\n985241 account heart\n985242 alien\n985243 anvil\n985244 battery charging 10\n985245 battery charging 50\n985246 battery charging 70\n985247 battery charging outline\n985248 bed empty\n985249 border all variant\n985250 border bottom variant\n985251 border left variant\n985252 border none variant\n985253 border right variant\n985254 border top variant\n985255 calendar edit\n985256 clipboard check outline\n985257 console network\n985258 file compare\n985259 fire truck\n985260 folder key\n985261 folder key network\n985262 expansion card\n985263 kayaking\n985264 inbox multiple\n985265 language lua\n985266 lock smart\n985267 microphone minus\n985268 microphone plus\n985269 palette swatch\n985270 periodic table\n985271 pickaxe\n985272 qrcode edit\n985273 remote desktop\n985274 sausage\n985275 cog outline\n985276 signal cellular 1\n985277 signal cellular 2\n985278 signal cellular 3\n985279 signal cellular outline\n985280 ssh\n985281 swap horizontal variant\n985282 swap vertical variant\n985283 tooth\n985284 train variant\n985285 account multiple check\n985286 application\n985287 arch\n985288 axe\n985289 bullseye arrow\n985290 bus clock\n985291 camera account\n985292 camera image\n985293 car limousine\n985294 cards club\n985295 cards diamond\n985297 cards spade\n985298 cellphone text\n985299 cellphone message\n985300 chart multiline\n985301 circle edit outline\n985302 cogs\n985303 credit card settings outline\n985304 death star\n985305 death star variant\n985306 debian\n985307 fedora\n985308 file undo\n985309 floor lamp\n985310 folder edit\n985311 format columns\n985312 freebsd\n985313 gate and\n985314 gate nand\n985315 gate nor\n985316 gate not\n985317 gate or\n985318 gate xnor\n985319 gate xor\n985320 gentoo\n985321 globe model\n985322 hammer\n985323 home lock\n985324 home lock open\n985325 linux mint\n985326 lock alert\n985327 lock question\n985328 map marker distance\n985329 midi\n985330 midi port\n985331 nas\n985332 network strength 1\n985333 network strength 1 alert\n985334 network strength 2\n985335 network strength 2 alert\n985336 network strength 3\n985337 network strength 3 alert\n985338 network strength 4\n985339 network strength 4 alert\n985340 network strength off\n985341 network strength off outline\n985342 network strength outline\n985343 play speed\n985344 playlist edit\n985345 power cycle\n985346 power off\n985347 power on\n985348 power sleep\n985349 power socket au\n985350 power standby\n985351 rabbit\n985352 robot vacuum variant\n985353 satellite uplink\n985354 scanner off\n985355 book minus multiple outline\n985356 square edit outline\n985357 sort numeric ascending variant\n985358 steering off\n985359 table search\n985360 tag minus\n985361 test tube empty\n985362 test tube off\n985363 ticket outline\n985364 track light\n985365 transition\n985366 transition masked\n985367 tumble dryer\n985368 file refresh\n985369 video account\n985370 video image\n985371 video stabilization\n985372 wall sconce\n985373 wall sconce flat\n985374 wall sconce round variant\n985375 wifi strength 1\n985376 wifi strength 1 alert\n985377 wifi strength 1 lock\n985378 wifi strength 2\n985379 wifi strength 2 alert\n985380 wifi strength 2 lock\n985381 wifi strength 3\n985382 wifi strength 3 alert\n985383 wifi strength 3 lock\n985384 wifi strength 4\n985385 wifi strength 4 alert\n985386 wifi strength 4 lock\n985387 wifi strength alert outline\n985388 wifi strength lock outline\n985389 wifi strength off\n985390 wifi strength off outline\n985391 wifi strength outline\n985392 pin off outline\n985393 pin outline\n985394 share outline\n985395 trackpad lock\n985396 account box multiple\n985397 account search outline\n985398 account filter\n985399 angle acute\n985400 angle obtuse\n985401 angle right\n985402 animation play\n985403 arrow split horizontal\n985404 arrow split vertical\n985405 audio video\n985406 battery 10 bluetooth\n985407 battery 20 bluetooth\n985408 battery 30 bluetooth\n985409 battery 40 bluetooth\n985410 battery 50 bluetooth\n985411 battery 60 bluetooth\n985412 battery 70 bluetooth\n985413 battery 80 bluetooth\n985414 battery 90 bluetooth\n985415 battery alert bluetooth\n985416 battery bluetooth\n985417 battery bluetooth variant\n985418 battery unknown bluetooth\n985419 dharmachakra\n985420 calendar search\n985421 cellphone remove\n985422 cellphone key\n985423 cellphone lock\n985424 cellphone off\n985425 cellphone cog\n985426 cellphone sound\n985427 cross\n985428 clock\n985429 clock alert\n985430 cloud search\n985431 cloud search outline\n985432 cordova\n985433 cryengine\n985434 cupcake\n985435 sine wave\n985436 current dc\n985437 database import\n985438 database export\n985439 desk lamp\n985440 disc player\n985441 email search\n985442 email search outline\n985443 exponent\n985444 exponent box\n985445 file download\n985446 file download outline\n985447 firebase\n985448 folder search\n985449 folder search outline\n985450 format list checkbox\n985451 fountain\n985452 google fit\n985453 greater than\n985454 greater than or equal\n985455 hard hat\n985456 headphones bluetooth\n985457 heart circle\n985458 heart circle outline\n985459 om\n985460 home minus\n985461 home plus\n985462 image outline\n985463 image search\n985464 image search outline\n985465 star crescent\n985466 star david\n985467 keyboard outline\n985468 less than\n985469 less than or equal\n985470 light switch\n985471 lock clock\n985472 magnify close\n985473 map minus\n985474 map outline\n985475 map plus\n985476 map search\n985477 map search outline\n985478 material design\n985479 medal\n985480 microsoft dynamics 365\n985481 monitor cellphone\n985482 monitor cellphone star\n985483 mouse bluetooth\n985484 muffin\n985485 not equal\n985486 not equal variant\n985487 order bool ascending variant\n985488 order bool descending variant\n985489 office building\n985490 plus minus\n985491 plus minus box\n985492 podcast\n985493 progress check\n985494 progress clock\n985495 progress download\n985496 progress upload\n985497 qi\n985498 record player\n985499 restore\n985500 shield off outline\n985501 shield lock\n985502 shield off\n985503 set top box\n985504 shower\n985505 shower head\n985506 speaker bluetooth\n985507 square root box\n985508 star circle outline\n985509 star face\n985510 table merge cells\n985511 tablet cellphone\n985512 text\n985513 text short\n985514 text long\n985515 toilet\n985516 toolbox\n985517 toolbox outline\n985518 tournament\n985519 two factor authentication\n985520 umbrella closed\n985521 unreal\n985522 video minus\n985523 video plus\n985524 volleyball\n985525 weight pound\n985526 whistle\n985527 arrow bottom left bold outline\n985528 arrow bottom left thick\n985529 arrow bottom right bold outline\n985530 arrow bottom right thick\n985531 arrow decision\n985532 arrow decision auto\n985533 arrow decision auto outline\n985534 arrow decision outline\n985535 arrow down bold outline\n985536 arrow left bold outline\n985537 arrow left right bold outline\n985538 arrow right bold outline\n985539 arrow top left bold outline\n985540 arrow top left thick\n985541 arrow top right bold outline\n985542 arrow top right thick\n985543 arrow up bold outline\n985544 arrow up down bold outline\n985545 ballot\n985546 ballot outline\n985547 betamax\n985548 bookmark minus\n985549 bookmark minus outline\n985550 bookmark off\n985551 bookmark off outline\n985552 braille\n985553 brain\n985554 calendar heart\n985555 calendar star\n985556 cassette\n985557 cellphone arrow down\n985558 chevron down box\n985559 chevron down box outline\n985560 chevron left box\n985561 chevron left box outline\n985562 chevron right box\n985563 chevron right box outline\n985564 chevron up box\n985565 chevron up box outline\n985566 circle medium\n985567 circle small\n985568 cloud alert\n985569 comment arrow left\n985570 comment arrow left outline\n985571 comment arrow right\n985572 comment arrow right outline\n985573 comment plus\n985574 currency php\n985575 delete outline\n985576 desktop mac dashboard\n985577 download multiple\n985578 eight track\n985579 email plus\n985580 email plus outline\n985581 text box outline\n985582 file document outline\n985583 floppy variant\n985584 flower outline\n985585 flower tulip\n985586 flower tulip outline\n985587 format font size decrease\n985588 format font size increase\n985589 ghost off\n985590 google lens\n985591 google spreadsheet\n985592 image move\n985593 keyboard settings\n985594 keyboard settings outline\n985595 knife\n985596 knife military\n985597 layers off outline\n985598 layers outline\n985599 lighthouse\n985600 lighthouse on\n985601 map legend\n985602 menu left outline\n985603 menu right outline\n985604 message alert outline\n985605 mini sd\n985606 minidisc\n985607 monitor dashboard\n985608 pirate\n985609 pokemon go\n985610 powershell\n985611 printer wireless\n985612 quality low\n985613 quality medium\n985614 reflect horizontal\n985615 reflect vertical\n985616 rhombus medium\n985617 rhombus split\n985618 shield account outline\n985619 square medium\n985620 square medium outline\n985621 square small\n985622 subtitles\n985623 subtitles outline\n985624 table border\n985625 toggle switch off outline\n985626 toggle switch outline\n985627 vhs\n985628 video vintage\n985629 view dashboard outline\n985630 microsoft visual studio code\n985631 vote\n985632 vote outline\n985633 microsoft windows classic\n985634 microsoft xbox controller battery charging\n985635 zip disk\n985636 aspect ratio\n985637 babel\n985638 balloon\n985639 bank transfer\n985640 bank transfer in\n985641 bank transfer out\n985642 briefcase minus\n985643 briefcase plus\n985644 briefcase remove\n985645 briefcase search\n985646 bug check\n985647 bug check outline\n985648 bug outline\n985649 calendar alert\n985650 calendar multiselect\n985651 calendar week\n985652 calendar week begin\n985653 cellphone screenshot\n985654 city variant\n985655 city variant outline\n985656 clipboard text outline\n985657 cloud question\n985658 comment eye\n985659 comment eye outline\n985660 comment search\n985661 comment search outline\n985662 contain\n985663 contain end\n985664 contain start\n985665 dlna\n985666 doctor\n985667 dog\n985668 dog side\n985669 ear hearing off\n985670 engine off\n985671 engine off outline\n985672 exit run\n985673 feature search\n985674 feature search outline\n985675 file alert\n985676 file alert outline\n985677 file upload\n985678 file upload outline\n985679 hand front right\n985680 hand okay\n985681 hand peace\n985682 hand peace variant\n985683 hand pointing down\n985684 hand pointing left\n985685 hand pointing up\n985686 heart multiple\n985687 heart multiple outline\n985688 horseshoe\n985689 human female boy\n985690 human female female\n985691 human female girl\n985692 human male boy\n985693 human male girl\n985694 human male male\n985695 ip\n985696 ip network\n985697 litecoin\n985698 magnify minus cursor\n985699 magnify plus cursor\n985700 menu swap\n985701 menu swap outline\n985702 puzzle outline\n985703 registered trademark\n985704 resize\n985705 router wireless settings\n985706 safe\n985707 scissors cutting\n985708 select drag\n985709 selection drag\n985710 settings helper\n985711 signal 5g\n985712 silverware fork knife\n985713 smog\n985714 solar power\n985715 star box\n985716 star box outline\n985717 table plus\n985718 table remove\n985719 target variant\n985720 trademark\n985721 trash can\n985722 trash can outline\n985723 tshirt crew\n985724 tshirt v\n985725 zodiac aquarius\n985726 zodiac aries\n985727 zodiac cancer\n985728 zodiac capricorn\n985729 zodiac gemini\n985730 zodiac leo\n985731 zodiac libra\n985732 zodiac pisces\n985733 zodiac sagittarius\n985734 zodiac scorpio\n985735 zodiac taurus\n985736 zodiac virgo\n985737 account child\n985738 account child circle\n985739 account supervisor\n985740 account supervisor circle\n985741 ampersand\n985742 web off\n985743 animation outline\n985744 animation play outline\n985745 bell off outline\n985746 bell plus outline\n985747 bell sleep outline\n985748 book minus multiple\n985749 book plus multiple\n985750 book remove multiple\n985751 book remove\n985752 briefcase edit\n985753 bus alert\n985754 calculator variant\n985755 caps lock\n985756 cash refund\n985757 checkbook\n985758 circle slice 1\n985759 circle slice 2\n985760 circle slice 3\n985761 circle slice 4\n985762 circle slice 5\n985763 circle slice 6\n985764 circle slice 7\n985765 circle slice 8\n985766 collapse all\n985767 collapse all outline\n985768 credit card refund outline\n985769 database check\n985770 database lock\n985771 desktop tower monitor\n985772 dishwasher\n985773 dog service\n985774 dot net\n985775 egg\n985776 egg easter\n985777 email check\n985778 email check outline\n985779 et\n985780 expand all\n985781 expand all outline\n985782 file cabinet\n985783 text box multiple\n985784 text box multiple outline\n985785 file move\n985786 folder clock\n985787 folder clock outline\n985788 format annotation minus\n985789 gesture pinch\n985790 gesture spread\n985791 gesture swipe horizontal\n985792 gesture swipe vertical\n985793 hail\n985794 helicopter\n985795 hexagon slice 1\n985796 hexagon slice 2\n985797 hexagon slice 3\n985798 hexagon slice 4\n985799 hexagon slice 5\n985800 hexagon slice 6\n985801 hexagram\n985802 hexagram outline\n985803 label off\n985804 label off outline\n985805 label variant\n985806 label variant outline\n985807 language ruby on rails\n985808 laravel\n985809 mastodon\n985810 sort numeric descending variant\n985811 minus circle multiple outline\n985812 music circle outline\n985813 pinwheel\n985814 pinwheel outline\n985815 radiator disabled\n985816 radiator off\n985817 select compare\n985818 shield plus\n985819 shield plus outline\n985820 shield remove\n985821 shield remove outline\n985822 book plus multiple outline\n985823 sina weibo\n985824 spray bottle\n985825 squeegee\n985826 star four points\n985827 star four points outline\n985828 star three points\n985829 star three points outline\n985830 symfony\n985831 variable\n985832 vector bezier\n985833 wiper\n985834 z wave\n985835 zend\n985836 account minus outline\n985837 account remove outline\n985838 alpha a\n985839 alpha b\n985840 alpha c\n985841 alpha d\n985842 alpha e\n985843 alpha f\n985844 alpha g\n985845 alpha h\n985846 alpha i\n985847 alpha j\n985848 alpha k\n985849 alpha l\n985850 alpha m\n985851 alpha n\n985852 alpha o\n985853 alpha p\n985854 alpha q\n985855 alpha r\n985856 alpha s\n985857 alpha t\n985858 alpha u\n985859 alpha v\n985860 alpha w\n985861 alpha x\n985862 alpha y\n985863 alpha z\n985864 alpha a box\n985865 alpha b box\n985866 alpha c box\n985867 alpha d box\n985868 alpha e box\n985869 alpha f box\n985870 alpha g box\n985871 alpha h box\n985872 alpha i box\n985873 alpha j box\n985874 alpha k box\n985875 alpha l box\n985876 alpha m box\n985877 alpha n box\n985878 alpha o box\n985879 alpha p box\n985880 alpha q box\n985881 alpha r box\n985882 alpha s box\n985883 alpha t box\n985884 alpha u box\n985885 alpha v box\n985886 alpha w box\n985887 alpha x box\n985888 alpha y box\n985889 alpha z box\n985890 bulldozer\n985891 bullhorn outline\n985892 calendar export\n985893 calendar import\n985894 chevron down circle\n985895 chevron down circle outline\n985896 chevron left circle\n985897 chevron left circle outline\n985898 chevron right circle\n985899 chevron right circle outline\n985900 chevron up circle\n985901 chevron up circle outline\n985902 content save settings outline\n985903 crystal ball\n985904 ember\n985905 facebook workplace\n985906 file replace\n985907 file replace outline\n985908 format letter case\n985909 format letter case lower\n985910 format letter case upper\n985911 language java\n985912 circle multiple\n985914 numeric 1\n985915 numeric 2\n985916 numeric 3\n985917 numeric 4\n985918 numeric 5\n985919 numeric 6\n985920 numeric 7\n985921 numeric 8\n985922 numeric 9\n985923 origin\n985924 resistor\n985925 resistor nodes\n985926 robot industrial\n985927 shoe formal\n985928 shoe heel\n985929 silo\n985930 box cutter off\n985931 tab minus\n985932 tab remove\n985933 tape measure\n985934 telescope\n985935 yahoo\n985936 account alert outline\n985937 account arrow left\n985938 account arrow left outline\n985939 account arrow right\n985940 account arrow right outline\n985941 account circle outline\n985942 account clock\n985943 account clock outline\n985944 account group outline\n985945 account question\n985946 account question outline\n985947 artstation\n985948 backspace outline\n985949 barley off\n985950 barn\n985951 bat\n985952 application settings\n985953 billiards\n985954 billiards rack\n985955 book open outline\n985956 book outline\n985957 boxing glove\n985958 calendar blank outline\n985959 calendar outline\n985960 calendar range outline\n985961 camera control\n985962 camera enhance outline\n985963 car door\n985964 car electric\n985965 car key\n985966 car multiple\n985967 card\n985968 card bulleted\n985969 card bulleted off\n985970 card bulleted off outline\n985971 card bulleted outline\n985972 card bulleted settings\n985973 card bulleted settings outline\n985974 card outline\n985975 card text\n985976 card text outline\n985977 chat\n985978 chat alert\n985979 chat processing\n985980 chef hat\n985981 cloud download outline\n985982 cloud upload outline\n985983 coffin\n985984 compass off\n985985 compass off outline\n985986 controller classic\n985987 controller classic outline\n985988 cube scan\n985989 currency brl\n985990 database edit\n985991 deathly hallows\n985992 delete circle outline\n985993 delete forever outline\n985994 diamond\n985995 diamond outline\n985996 dns outline\n985997 dots horizontal circle outline\n985998 dots vertical circle outline\n985999 download outline\n986000 drag variant\n986001 eject outline\n986002 email mark as unread\n986003 export variant\n986004 eye circle\n986005 eye circle outline\n986006 face man outline\n986007 file find outline\n986008 file remove\n986009 flag minus\n986010 flag plus\n986011 flag remove\n986012 folder account outline\n986013 folder plus outline\n986014 folder remove outline\n986015 folder star outline\n986016 gitlab\n986017 gog\n986018 grave stone\n986019 halloween\n986020 hat fedora\n986021 help rhombus\n986022 help rhombus outline\n986023 home variant outline\n986024 inbox multiple outline\n986025 library shelves\n986026 mapbox\n986027 menu open\n986028 molecule\n986029 one up\n986030 open source initiative\n986031 pac man\n986032 page next\n986033 page next outline\n986034 page previous\n986035 page previous outline\n986036 pan\n986037 pan bottom left\n986038 pan bottom right\n986039 pan down\n986040 pan horizontal\n986041 pan left\n986042 pan right\n986043 pan top left\n986044 pan top right\n986045 pan up\n986046 pan vertical\n986047 pumpkin\n986048 rollupjs\n986049 script\n986050 script text\n986051 script text outline\n986052 shield key\n986053 shield key outline\n986054 skull crossbones\n986055 skull crossbones outline\n986056 skull outline\n986057 space invaders\n986058 spider web\n986059 view split horizontal\n986060 view split vertical\n986061 swap horizontal bold\n986062 swap vertical bold\n986063 tag heart outline\n986064 target account\n986065 timeline\n986066 timeline outline\n986067 timeline text\n986068 timeline text outline\n986069 tooltip image outline\n986070 tooltip plus\n986071 tooltip text outline\n986072 train car\n986073 triforce\n986074 ubisoft\n986075 video off outline\n986076 video outline\n986077 wallet outline\n986078 waze\n986079 wrap disabled\n986080 wrench outline\n986081 access point network off\n986082 account check outline\n986083 account heart outline\n986084 account key outline\n986085 account multiple minus outline\n986086 account network outline\n986087 account off outline\n986088 account star outline\n986089 airbag\n986090 alarm light outline\n986091 alpha a box outline\n986092 alpha a circle\n986093 alpha a circle outline\n986094 alpha b box outline\n986095 alpha b circle\n986096 alpha b circle outline\n986097 alpha c box outline\n986098 alpha c circle\n986099 alpha c circle outline\n986100 alpha d box outline\n986101 alpha d circle\n986102 alpha d circle outline\n986103 alpha e box outline\n986104 alpha e circle\n986105 alpha e circle outline\n986106 alpha f box outline\n986107 alpha f circle\n986108 alpha f circle outline\n986109 alpha g box outline\n986110 alpha g circle\n986111 alpha g circle outline\n986112 alpha h box outline\n986113 alpha h circle\n986114 alpha h circle outline\n986115 alpha i box outline\n986116 alpha i circle\n986117 alpha i circle outline\n986118 alpha j box outline\n986119 alpha j circle\n986120 alpha j circle outline\n986121 alpha k box outline\n986122 alpha k circle\n986123 alpha k circle outline\n986124 alpha l box outline\n986125 alpha l circle\n986126 alpha l circle outline\n986127 alpha m box outline\n986128 alpha m circle\n986129 alpha m circle outline\n986130 alpha n box outline\n986131 alpha n circle\n986132 alpha n circle outline\n986133 alpha o box outline\n986134 alpha o circle\n986135 alpha o circle outline\n986136 alpha p box outline\n986137 alpha p circle\n986138 alpha p circle outline\n986139 alpha q box outline\n986140 alpha q circle\n986141 alpha q circle outline\n986142 alpha r box outline\n986143 alpha r circle\n986144 alpha r circle outline\n986145 alpha s box outline\n986146 alpha s circle\n986147 alpha s circle outline\n986148 alpha t box outline\n986149 alpha t circle\n986150 alpha t circle outline\n986151 alpha u box outline\n986152 alpha u circle\n986153 alpha u circle outline\n986154 alpha v box outline\n986155 alpha v circle\n986156 alpha v circle outline\n986157 alpha w box outline\n986158 alpha w circle\n986159 alpha w circle outline\n986160 alpha x box outline\n986161 alpha x circle\n986162 alpha x circle outline\n986163 alpha y box outline\n986164 alpha y circle\n986165 alpha y circle outline\n986166 alpha z box outline\n986167 alpha z circle\n986168 alpha z circle outline\n986169 ballot recount\n986170 ballot recount outline\n986171 basketball hoop\n986172 basketball hoop outline\n986173 briefcase download outline\n986174 briefcase edit outline\n986175 briefcase minus outline\n986176 briefcase plus outline\n986177 briefcase remove outline\n986178 briefcase search outline\n986179 briefcase upload outline\n986180 calendar check outline\n986181 calendar remove outline\n986182 calendar text outline\n986183 car brake abs\n986184 car brake alert\n986185 car esp\n986186 car light dimmed\n986187 car light fog\n986188 car light high\n986189 car tire alert\n986190 cart arrow right\n986191 charity\n986192 chart bell curve\n986193 checkbox multiple outline\n986194 checkbox outline\n986195 check network\n986196 check network outline\n986197 clipboard account outline\n986198 clipboard arrow down outline\n986199 clipboard arrow up\n986200 clipboard arrow up outline\n986201 clipboard play\n986202 clipboard play outline\n986203 clipboard text play\n986204 clipboard text play outline\n986205 close box multiple\n986206 close box multiple outline\n986207 close network outline\n986208 console network outline\n986209 currency ils\n986210 delete sweep outline\n986211 diameter\n986212 diameter outline\n986213 diameter variant\n986214 download network outline\n986215 dump truck\n986216 emoticon\n986217 emoticon angry\n986218 emoticon angry outline\n986219 emoticon cool\n986220 emoticon cry\n986221 emoticon cry outline\n986222 emoticon dead\n986223 emoticon devil\n986224 emoticon excited\n986225 emoticon happy\n986226 emoticon kiss\n986227 emoticon kiss outline\n986228 emoticon neutral\n986229 emoticon poop outline\n986230 emoticon sad\n986231 emoticon tongue outline\n986232 emoticon wink\n986233 emoticon wink outline\n986234 eslint\n986235 face recognition\n986236 file search\n986237 file search outline\n986238 file table\n986239 file table outline\n986240 folder key network outline\n986241 folder network outline\n986242 folder text\n986243 folder text outline\n986244 food apple outline\n986245 fuse\n986246 fuse blade\n986247 google ads\n986248 google street view\n986249 hazard lights\n986250 help network outline\n986251 application brackets\n986252 application brackets outline\n986253 image size select actual\n986254 image size select large\n986255 image size select small\n986256 ip network outline\n986257 ipod\n986258 language haskell\n986259 leaf maple\n986260 link plus\n986261 map marker check\n986262 math cos\n986263 math sin\n986264 math tan\n986265 microwave\n986266 minus network outline\n986267 network off\n986268 network off outline\n986269 network outline\n986272 numeric 1 circle\n986273 numeric 1 circle outline\n986274 numeric 2 circle\n986275 numeric 2 circle outline\n986276 numeric 3 circle\n986277 numeric 3 circle outline\n986278 numeric 4 circle\n986279 numeric 4 circle outline\n986280 numeric 5 circle\n986281 numeric 5 circle outline\n986282 numeric 6 circle\n986283 numeric 6 circle outline\n986284 numeric 7 circle\n986285 numeric 7 circle outline\n986286 numeric 8 circle\n986287 numeric 8 circle outline\n986288 numeric 9 circle\n986289 numeric 9 circle outline\n986290 numeric 9 plus circle\n986291 numeric 9 plus circle outline\n986292 parachute\n986293 parachute outline\n986294 pencil outline\n986295 play network outline\n986296 playlist music\n986297 playlist music outline\n986298 plus network outline\n986299 postage stamp\n986300 progress alert\n986301 progress wrench\n986302 radio am\n986303 radio fm\n986304 radius\n986305 radius outline\n986306 ruler square\n986307 seat\n986308 seat outline\n986309 seatbelt\n986310 sheep\n986311 shield airplane outline\n986312 shield check outline\n986313 shield cross\n986314 shield cross outline\n986315 shield home outline\n986316 shield lock outline\n986317 sort variant lock\n986318 sort variant lock open\n986319 source repository\n986320 source repository multiple\n986321 spa\n986322 spa outline\n986323 toaster oven\n986324 truck check\n986325 turnstile\n986326 turnstile outline\n986327 turtle\n986328 upload network outline\n986329 vibrate off\n986330 watch vibrate off\n986331 arrow down circle\n986332 arrow down circle outline\n986333 arrow left circle\n986334 arrow left circle outline\n986335 arrow right circle\n986336 arrow right circle outline\n986337 arrow up circle\n986338 arrow up circle outline\n986339 account tie\n986340 alert box outline\n986341 alert decagram outline\n986342 alert octagon outline\n986343 alert octagram outline\n986344 ammunition\n986345 account music outline\n986346 beaker\n986347 blender\n986348 blood bag\n986349 cross bolnisi\n986350 bread slice\n986351 bread slice outline\n986352 briefcase account\n986353 briefcase account outline\n986354 brightness percent\n986355 bullet\n986356 cash register\n986357 cross celtic\n986358 cross outline\n986359 clipboard alert outline\n986360 clipboard arrow left outline\n986361 clipboard arrow right\n986362 clipboard arrow right outline\n986363 content save edit\n986364 content save edit outline\n986365 cursor default click\n986366 cursor default click outline\n986367 database sync\n986368 database remove\n986369 database settings\n986370 drama masks\n986371 email box\n986372 eye check\n986373 eye check outline\n986374 fast forward 30\n986375 order alphabetical descending\n986376 flower poppy\n986377 folder pound\n986378 folder pound outline\n986379 folder sync\n986380 folder sync outline\n986381 format list numbered rtl\n986382 format text wrapping clip\n986383 format text wrapping overflow\n986384 format text wrapping wrap\n986385 format textbox\n986386 fountain pen\n986387 fountain pen tip\n986388 heart broken outline\n986389 home city\n986390 home city outline\n986391 hubspot\n986392 filmstrip box multiple\n986393 play box multiple\n986394 link box\n986395 link box outline\n986396 link box variant\n986397 link box variant outline\n986398 map clock\n986399 map clock outline\n986400 map marker path\n986401 mother nurse\n986402 microsoft outlook\n986403 perspective less\n986404 perspective more\n986405 podium\n986406 podium bronze\n986407 podium gold\n986408 podium silver\n986409 quora\n986410 rewind 10\n986411 roller skate\n986412 rollerblade\n986413 language ruby\n986414 sack\n986415 sack percent\n986416 safety goggles\n986417 select color\n986418 selection ellipse\n986419 shield link variant\n986420 shield link variant outline\n986421 skate\n986422 skew less\n986423 skew more\n986424 speaker multiple\n986425 stamper\n986426 tank\n986427 tortoise\n986428 transit connection\n986429 transit connection variant\n986430 transmission tower\n986431 weight gram\n986432 youtube subscription\n986433 zigbee\n986434 email alert outline\n986435 air filter\n986436 air purifier\n986437 android messages\n986438 apps box\n986439 atm\n986440 axis\n986441 axis arrow\n986442 axis arrow lock\n986443 axis lock\n986444 axis x arrow\n986445 axis x arrow lock\n986446 axis x rotate clockwise\n986447 axis x rotate counterclockwise\n986448 axis x y arrow lock\n986449 axis y arrow\n986450 axis y arrow lock\n986451 axis y rotate clockwise\n986452 axis y rotate counterclockwise\n986453 axis z arrow\n986454 axis z arrow lock\n986455 axis z rotate clockwise\n986456 axis z rotate counterclockwise\n986457 bell alert\n986458 bell circle\n986459 bell circle outline\n986460 calendar minus\n986461 camera outline\n986462 car brake hold\n986463 car brake parking\n986464 car cruise control\n986465 car defrost front\n986466 car defrost rear\n986467 car parking lights\n986468 car traction control\n986469 bag carry on check\n986470 cart arrow down\n986471 cart arrow up\n986472 cart minus\n986473 cart remove\n986474 contactless payment\n986475 creative commons\n986476 credit card wireless outline\n986477 cricket\n986478 dev to\n986479 domain off\n986480 face agent\n986481 fast forward 10\n986482 flare\n986483 format text rotation down\n986484 format text rotation none\n986485 forwardburger\n986486 gesture swipe\n986487 gesture tap hold\n986488 file gif box\n986489 go kart\n986490 go kart track\n986491 goodreads\n986492 grain\n986493 hdr\n986494 hdr off\n986495 hiking\n986496 home floor 1\n986497 home floor 2\n986498 home floor 3\n986499 home floor a\n986500 home floor b\n986501 home floor g\n986502 home floor l\n986503 kabaddi\n986504 mailbox open\n986505 mailbox open outline\n986506 mailbox open up\n986507 mailbox open up outline\n986508 mailbox outline\n986509 mailbox up\n986510 mailbox up outline\n986511 mixed martial arts\n986512 monitor off\n986513 motion sensor\n986514 point of sale\n986515 racing helmet\n986516 racquetball\n986517 restart off\n986518 rewind 30\n986519 room service outline\n986520 rotate orbit\n986521 rugby\n986522 shield search\n986523 solar panel\n986524 solar panel large\n986525 subway alert variant\n986526 tea\n986527 tea outline\n986528 tennis\n986529 transfer down\n986530 transfer left\n986531 transfer up\n986532 trophy broken\n986533 wind turbine\n986534 wiper wash\n986535 badge account\n986536 badge account alert\n986537 badge account alert outline\n986538 badge account outline\n986539 card account details outline\n986540 air horn\n986541 application export\n986542 application import\n986543 bandage\n986544 bank minus\n986545 bank plus\n986546 bank remove\n986547 bolt\n986548 bugle\n986549 cactus\n986550 camera wireless\n986551 camera wireless outline\n986552 cash marker\n986553 chevron triple down\n986554 chevron triple left\n986555 chevron triple right\n986556 chevron triple up\n986557 closed caption outline\n986558 credit card marker outline\n986559 diving flippers\n986560 diving helmet\n986561 diving scuba\n986562 diving scuba flag\n986563 diving scuba tank\n986564 diving scuba tank multiple\n986565 diving snorkel\n986566 file cancel\n986567 file cancel outline\n986568 file document edit\n986569 file document edit outline\n986570 file eye\n986571 file eye outline\n986572 folder alert\n986573 folder alert outline\n986574 folder edit outline\n986575 folder open outline\n986576 format list bulleted square\n986577 gantry crane\n986578 home floor 0\n986579 home floor negative 1\n986580 home group\n986581 jabber\n986582 key outline\n986583 leak\n986584 leak off\n986585 marker cancel\n986586 mine\n986587 monitor lock\n986588 monitor star\n986589 movie outline\n986590 music note plus\n986591 nail\n986592 ocarina\n986593 passport biometric\n986594 pen lock\n986595 pen minus\n986596 pen off\n986597 pen plus\n986598 pen remove\n986599 pencil lock outline\n986600 pencil minus\n986601 pencil minus outline\n986602 pencil off outline\n986603 pencil plus\n986604 pencil plus outline\n986605 pencil remove\n986606 pencil remove outline\n986607 phone off\n986608 phone outline\n986609 pi hole\n986610 playlist star\n986611 screw flat top\n986612 screw lag\n986613 screw machine flat top\n986614 screw machine round top\n986615 screw round top\n986616 send circle\n986617 send circle outline\n986618 shoe print\n986619 signature\n986620 signature freehand\n986621 signature image\n986622 signature text\n986623 slope downhill\n986624 slope uphill\n986625 thermometer alert\n986626 thermometer chevron down\n986627 thermometer chevron up\n986628 thermometer minus\n986629 thermometer plus\n986630 translate off\n986631 upload outline\n986632 volume variant off\n986633 wallpaper\n986634 water outline\n986635 wifi star\n986636 palette outline\n986637 badge account horizontal\n986638 badge account horizontal outline\n986639 aws\n986640 bag personal\n986641 bag personal off\n986642 bag personal off outline\n986643 bag personal outline\n986644 biathlon\n986645 bookmark multiple\n986646 bookmark multiple outline\n986647 calendar month\n986648 calendar month outline\n986649 camera retake\n986650 camera retake outline\n986651 car back\n986652 car off\n986653 cast education\n986654 check bold\n986655 check underline\n986656 check underline circle\n986657 check underline circle outline\n986658 circular saw\n986659 comma\n986660 comma box outline\n986661 comma circle\n986662 comma circle outline\n986663 content save move\n986664 content save move outline\n986665 file check outline\n986666 file music outline\n986667 comma box\n986668 file video outline\n986669 file png box\n986670 fireplace\n986671 fireplace off\n986672 firework\n986673 format color highlight\n986674 format text variant\n986675 gamepad circle\n986676 gamepad circle down\n986677 gamepad circle left\n986678 gamepad circle outline\n986679 gamepad circle right\n986680 gamepad circle up\n986681 gamepad down\n986682 gamepad left\n986683 gamepad right\n986684 gamepad round\n986685 gamepad round down\n986686 gamepad round left\n986687 gamepad round outline\n986688 gamepad round right\n986689 gamepad round up\n986690 gamepad up\n986691 gatsby\n986692 gift\n986693 grill\n986694 hand back left\n986695 hand back right\n986696 hand saw\n986697 image frame\n986698 invert colors off\n986699 keyboard off outline\n986700 layers minus\n986701 layers plus\n986702 layers remove\n986703 lightbulb off\n986704 lightbulb off outline\n986705 monitor screenshot\n986706 ice cream off\n986707 nfc search variant\n986708 nfc variant off\n986709 notebook multiple\n986710 hoop house\n986711 picture in picture bottom right\n986712 picture in picture bottom right outline\n986713 picture in picture top right\n986714 picture in picture top right outline\n986715 printer 3d nozzle\n986716 printer 3d nozzle outline\n986717 printer off\n986718 rectangle\n986719 rectangle outline\n986720 rivet\n986721 saw blade\n986722 seed\n986723 seed outline\n986724 signal distance variant\n986725 spade\n986726 sprout\n986727 sprout outline\n986728 table tennis\n986729 tree outline\n986730 view comfy\n986731 view compact\n986732 view compact outline\n986733 vuetify\n986734 weather cloudy arrow right\n986735 microsoft xbox controller menu\n986736 microsoft xbox controller view\n986737 alarm note\n986738 alarm note off\n986739 arrow left right\n986740 arrow left right bold\n986741 arrow top left bottom right\n986742 arrow top left bottom right bold\n986743 arrow top right bottom left\n986744 arrow top right bottom left bold\n986745 arrow up down\n986746 arrow up down bold\n986747 atom variant\n986748 baby face\n986749 baby face outline\n986750 backspace reverse\n986751 backspace reverse outline\n986752 bank outline\n986753 bell alert outline\n986754 book play\n986755 book play outline\n986756 book search\n986757 book search outline\n986758 boom gate\n986759 boom gate alert\n986760 boom gate alert outline\n986761 boom gate arrow down\n986762 boom gate arrow down outline\n986763 boom gate outline\n986764 boom gate arrow up\n986765 boom gate arrow up outline\n986766 calendar sync\n986767 calendar sync outline\n986768 cellphone nfc\n986769 chart areaspline variant\n986770 chart scatter plot\n986771 chart timeline variant\n986772 chart tree\n986773 circle double\n986774 circle expand\n986775 clock digital\n986776 card account mail outline\n986777 card account phone\n986778 card account phone outline\n986779 account cowboy hat\n986780 currency rial\n986781 delete empty outline\n986782 dolly\n986783 electric switch\n986784 ellipse\n986785 ellipse outline\n986786 equalizer\n986787 equalizer outline\n986788 ferris wheel\n986789 file delimited outline\n986790 text box check\n986791 text box check outline\n986792 text box minus\n986793 text box minus outline\n986794 text box plus\n986795 text box plus outline\n986796 text box remove\n986797 text box remove outline\n986798 text box search\n986799 text box search outline\n986800 file image outline\n986801 fingerprint off\n986802 format list bulleted triangle\n986803 format overline\n986804 frequently asked questions\n986805 gamepad square\n986806 gamepad square outline\n986807 gamepad variant outline\n986808 gas station outline\n986809 google podcast\n986810 home analytics\n986811 mail\n986812 map check\n986813 map check outline\n986814 ruler square compass\n986815 notebook outline\n986816 penguin\n986817 radioactive off\n986818 record circle\n986819 record circle outline\n986820 remote off\n986821 remote tv\n986822 remote tv off\n986823 rotate 3d\n986824 sail boat\n986825 scatter plot\n986826 scatter plot outline\n986827 segment\n986828 shield alert\n986829 shield alert outline\n986830 tablet dashboard\n986831 television play\n986832 unicode\n986833 video 3d variant\n986834 video wireless\n986835 video wireless outline\n986836 account voice off\n986837 bacteria\n986838 bacteria outline\n986839 calendar account\n986840 calendar account outline\n986841 calendar weekend\n986842 calendar weekend outline\n986843 camera plus\n986844 camera plus outline\n986845 campfire\n986846 chat outline\n986847 cpu 32 bit\n986848 cpu 64 bit\n986849 credit card clock\n986850 credit card clock outline\n986851 email edit\n986852 email edit outline\n986853 email minus\n986854 email minus outline\n986855 email multiple\n986856 email multiple outline\n986857 email open multiple\n986858 email open multiple outline\n986859 file cad\n986860 file cad box\n986861 file plus outline\n986862 filter minus\n986863 filter minus outline\n986864 filter plus\n986865 filter plus outline\n986866 fire extinguisher\n986867 fishbowl\n986868 fishbowl outline\n986869 fit to page\n986870 fit to page outline\n986871 flash alert\n986872 flash alert outline\n986873 heart flash\n986874 home flood\n986875 human male height\n986876 human male height variant\n986877 ice pop\n986878 identifier\n986879 image filter center focus strong\n986880 image filter center focus strong outline\n986881 jellyfish\n986882 jellyfish outline\n986883 lasso\n986884 music box multiple outline\n986885 map marker alert\n986886 map marker alert outline\n986887 map marker question\n986888 map marker question outline\n986889 map marker remove\n986890 map marker remove variant\n986891 necklace\n986892 newspaper minus\n986893 newspaper plus\n986894 numeric 0 box multiple\n986895 numeric 1 box multiple\n986896 numeric 2 box multiple\n986897 numeric 3 box multiple\n986898 numeric 4 box multiple\n986899 numeric 5 box multiple\n986900 numeric 6 box multiple\n986901 numeric 7 box multiple\n986902 numeric 8 box multiple\n986903 numeric 9 box multiple\n986904 numeric 9 plus box multiple\n986905 oil lamp\n986906 phone alert\n986907 play outline\n986908 purse\n986909 purse outline\n986910 railroad light\n986911 reply all outline\n986912 reply outline\n986913 rss off\n986914 selection ellipse arrow inside\n986915 share off\n986916 share off outline\n986917 skip backward outline\n986918 skip forward outline\n986919 skip next outline\n986920 skip previous outline\n986921 snowflake alert\n986922 snowflake variant\n986923 stretch to page\n986924 stretch to page outline\n986925 typewriter\n986926 wave\n986927 weather cloudy alert\n986928 weather hazy\n986929 weather night partly cloudy\n986930 weather partly lightning\n986931 weather partly rainy\n986932 weather partly snowy\n986933 weather partly snowy rainy\n986934 weather snowy heavy\n986935 weather sunny alert\n986936 weather tornado\n986937 baby bottle\n986938 baby bottle outline\n986939 bag carry on\n986940 bag carry on off\n986941 bag checked\n986942 baguette\n986943 bus multiple\n986944 car shift pattern\n986945 cellphone information\n986946 content save alert\n986947 content save alert outline\n986948 content save all outline\n986949 crosshairs off\n986950 cupboard\n986951 cupboard outline\n986952 chair rolling\n986953 draw\n986954 dresser\n986955 dresser outline\n986956 emoticon frown\n986957 emoticon frown outline\n986958 focus auto\n986959 focus field\n986960 focus field horizontal\n986961 focus field vertical\n986962 foot print\n986963 handball\n986964 home thermometer\n986965 home thermometer outline\n986966 kettle outline\n986967 latitude\n986968 layers triple\n986969 layers triple outline\n986970 longitude\n986971 language markdown outline\n986972 merge\n986973 middleware\n986974 middleware outline\n986975 monitor speaker\n986976 monitor speaker off\n986977 moon first quarter\n986978 moon full\n986979 moon last quarter\n986980 moon new\n986981 moon waning crescent\n986982 moon waning gibbous\n986983 moon waxing crescent\n986984 moon waxing gibbous\n986985 music accidental double flat\n986986 music accidental double sharp\n986987 music accidental flat\n986988 music accidental natural\n986989 music accidental sharp\n986990 music clef alto\n986991 music clef bass\n986992 music clef treble\n986993 music note eighth dotted\n986994 music note half dotted\n986995 music note off outline\n986996 music note outline\n986997 music note quarter dotted\n986998 music note sixteenth dotted\n986999 music note whole dotted\n987000 music rest eighth\n987001 music rest half\n987002 music rest quarter\n987003 music rest sixteenth\n987004 music rest whole\n987005 numeric 10 box\n987006 numeric 10 box outline\n987007 page layout header footer\n987008 patio heater\n987009 warehouse\n987010 select group\n987011 shield car\n987012 shopping search\n987013 speedometer medium\n987014 speedometer slow\n987015 table large plus\n987016 table large remove\n987017 television pause\n987018 television stop\n987019 transit detour\n987020 video input scart\n987021 view grid plus\n987022 wallet plus\n987023 wallet plus outline\n987024 wardrobe\n987025 wardrobe outline\n987026 water boiler\n987027 water pump off\n987028 web box\n987029 timeline alert\n987030 timeline plus\n987031 timeline plus outline\n987032 timeline alert outline\n987033 timeline help\n987034 timeline help outline\n987035 home export outline\n987036 home import outline\n987037 account filter outline\n987038 approximately equal\n987039 approximately equal box\n987040 baby carriage off\n987041 bee\n987042 bee flower\n987043 car child seat\n987044 car seat\n987045 car seat cooler\n987046 car seat heater\n987047 chart bell curve cumulative\n987048 clock check\n987049 clock check outline\n987050 coffee off\n987051 coffee off outline\n987052 credit card minus\n987053 credit card minus outline\n987054 credit card remove\n987055 credit card remove outline\n987056 devices\n987057 email newsletter\n987058 expansion card variant\n987059 power socket ch\n987060 file swap\n987061 file swap outline\n987062 folder swap\n987063 folder swap outline\n987064 format letter ends with\n987065 format letter matches\n987066 format letter starts with\n987067 format text rotation angle down\n987068 format text rotation angle up\n987069 format text rotation down vertical\n987070 format text rotation up\n987071 format text rotation vertical\n987072 id card\n987073 image auto adjust\n987074 key wireless\n987075 license\n987076 location enter\n987077 location exit\n987078 lock open variant\n987079 lock open variant outline\n987080 math integral\n987081 math integral box\n987082 math norm\n987083 math norm box\n987084 message lock\n987085 message text lock\n987086 movie open\n987087 movie open outline\n987088 bed queen\n987089 bed king outline\n987090 bed king\n987091 bed double outline\n987092 bed double\n987093 microsoft azure devops\n987094 arm flex outline\n987095 arm flex\n987096 protocol\n987097 seal variant\n987098 select place\n987099 bed queen outline\n987100 sign direction plus\n987101 sign direction remove\n987102 silverware clean\n987103 slash forward\n987104 slash forward box\n987105 swap horizontal circle\n987106 swap horizontal circle outline\n987107 swap vertical circle\n987108 swap vertical circle outline\n987109 tanker truck\n987110 texture box\n987111 tram side\n987112 vector link\n987113 numeric 10\n987114 numeric 10 box multiple\n987115 numeric 10 box multiple outline\n987116 numeric 10 circle\n987117 numeric 10 circle outline\n987118 numeric 9 plus\n987119 credit card\n987120 credit card multiple\n987121 credit card off\n987122 credit card plus\n987123 credit card refund\n987124 credit card scan\n987125 credit card settings\n987126 hospital\n987127 hospital box outline\n987128 oil temperature\n987129 stadium\n987130 zip box outline\n987131 account edit outline\n987132 peanut\n987133 peanut off\n987134 peanut outline\n987135 peanut off outline\n987136 sign direction minus\n987137 newspaper variant\n987138 newspaper variant multiple\n987139 newspaper variant multiple outline\n987140 newspaper variant outline\n987141 overscan\n987142 pig variant\n987143 piggy bank\n987144 post\n987145 post outline\n987146 account box multiple outline\n987147 airballoon outline\n987148 alphabetical off\n987149 alphabetical variant\n987150 alphabetical variant off\n987151 apache kafka\n987152 billboard\n987153 blinds open\n987154 bus stop\n987155 bus stop covered\n987156 bus stop uncovered\n987157 car 2 plus\n987158 car 3 plus\n987159 car brake retarder\n987160 car clutch\n987161 car coolant level\n987162 car turbocharger\n987163 car windshield\n987164 car windshield outline\n987165 cards diamond outline\n987166 cast audio\n987167 cellphone play\n987168 coach lamp\n987169 comment quote\n987170 comment quote outline\n987171 domino mask\n987172 electron framework\n987173 excavator\n987174 eye minus\n987175 eye minus outline\n987176 file account outline\n987177 file chart outline\n987178 file cloud outline\n987179 file code outline\n987180 file excel box outline\n987181 file excel outline\n987182 file export outline\n987183 file import outline\n987184 file lock outline\n987185 file move outline\n987186 file multiple outline\n987187 file percent outline\n987188 file powerpoint box outline\n987189 file powerpoint outline\n987190 file question outline\n987191 file remove outline\n987192 file restore outline\n987193 file send outline\n987194 file star\n987195 file star outline\n987196 file undo outline\n987197 file word box outline\n987198 file word outline\n987199 filter variant remove\n987200 floor lamp dual\n987201 floor lamp torchiere variant\n987202 fruit cherries\n987203 fruit citrus\n987204 fruit grapes\n987205 fruit grapes outline\n987206 fruit pineapple\n987207 fruit watermelon\n987208 google my business\n987209 graph\n987210 graph outline\n987211 harddisk plus\n987212 harddisk remove\n987213 home circle outline\n987214 instrument triangle\n987215 island\n987216 keyboard space\n987217 led strip variant\n987218 numeric negative 1\n987219 oil level\n987220 outdoor lamp\n987221 palm tree\n987222 party popper\n987223 printer pos\n987224 robber\n987225 routes clock\n987226 scale off\n987227 cog transfer\n987228 cog transfer outline\n987229 shield sun\n987230 shield sun outline\n987231 sprinkler\n987232 sprinkler variant\n987233 table chair\n987234 terraform\n987235 toaster\n987236 tools\n987237 transfer\n987238 valve\n987239 valve closed\n987240 valve open\n987241 video check\n987242 video check outline\n987243 water well\n987244 water well outline\n987245 bed single\n987246 bed single outline\n987247 book information variant\n987248 bottle soda\n987249 bottle soda classic\n987250 bottle soda outline\n987251 calendar blank multiple\n987252 card search\n987253 card search outline\n987254 face woman profile\n987255 face woman\n987256 face woman outline\n987257 file settings\n987258 file settings outline\n987259 file cog\n987260 file cog outline\n987261 folder settings\n987262 folder settings outline\n987263 folder cog\n987264 folder cog outline\n987265 furigana horizontal\n987266 furigana vertical\n987267 golf tee\n987268 lungs\n987269 math log\n987270 moped\n987271 router network\n987273 roman numeral 2\n987274 roman numeral 3\n987275 roman numeral 4\n987277 roman numeral 6\n987278 roman numeral 7\n987279 roman numeral 8\n987280 roman numeral 9\n987282 soldering iron\n987283 stomach\n987284 table eye\n987285 form textarea\n987286 trumpet\n987287 account cash\n987288 account cash outline\n987289 air humidifier\n987290 ansible\n987291 api\n987292 bicycle\n987293 car door lock\n987294 coat rack\n987295 coffee maker\n987296 web minus\n987297 decimal\n987298 decimal comma\n987299 decimal comma decrease\n987300 decimal comma increase\n987301 delete alert\n987302 delete alert outline\n987303 delete off\n987304 delete off outline\n987305 dock bottom\n987306 dock left\n987307 dock right\n987308 dock window\n987309 domain plus\n987310 domain remove\n987311 door closed lock\n987312 download off\n987313 download off outline\n987314 flag minus outline\n987315 flag plus outline\n987316 flag remove outline\n987317 folder home\n987318 folder home outline\n987319 folder information\n987320 folder information outline\n987321 iv bag\n987322 link lock\n987323 message plus outline\n987324 phone cancel\n987325 smart card\n987326 smart card outline\n987327 smart card reader\n987328 smart card reader outline\n987329 storefront outline\n987330 thermometer high\n987331 thermometer low\n987332 ufo\n987333 ufo outline\n987334 upload off\n987335 upload off outline\n987336 account child outline\n987337 account settings outline\n987338 account tie outline\n987339 alien outline\n987340 battery alert variant\n987341 battery alert variant outline\n987342 beehive outline\n987343 boomerang\n987344 briefcase clock\n987345 briefcase clock outline\n987346 cellphone message off\n987347 circle off outline\n987348 clipboard list\n987349 clipboard list outline\n987350 code braces box\n987351 code parentheses box\n987352 consolidate\n987353 electric switch closed\n987354 email receive\n987355 email receive outline\n987356 email send\n987357 email send outline\n987358 emoticon confused\n987359 emoticon confused outline\n987360 epsilon\n987361 file table box\n987362 file table box multiple\n987363 file table box multiple outline\n987364 file table box outline\n987365 filter menu\n987366 filter menu outline\n987367 flip horizontal\n987368 flip vertical\n987369 folder download outline\n987370 folder heart\n987371 folder heart outline\n987372 folder key outline\n987373 folder upload outline\n987374 gamma\n987375 hair dryer\n987376 hair dryer outline\n987377 hand heart\n987378 hexagon multiple outline\n987379 horizontal rotate clockwise\n987380 horizontal rotate counterclockwise\n987381 application array\n987382 application array outline\n987383 application braces\n987384 application braces outline\n987385 application parentheses\n987386 application parentheses outline\n987387 application variable\n987388 application variable outline\n987389 khanda\n987390 kubernetes\n987391 link variant minus\n987392 link variant plus\n987393 link variant remove\n987394 map marker down\n987395 map marker up\n987396 monitor shimmer\n987397 nix\n987398 nuxt\n987399 power socket de\n987400 power socket fr\n987401 power socket jp\n987402 progress close\n987403 reload alert\n987404 restart alert\n987405 restore alert\n987406 shaker\n987407 shaker outline\n987408 television shimmer\n987409 variable box\n987410 filter variant minus\n987411 filter variant plus\n987412 slot machine\n987413 slot machine outline\n987414 glass mug variant\n987415 clipboard flow outline\n987416 sign real estate\n987417 antenna\n987418 centos\n987419 redhat\n987420 window shutter\n987421 window shutter alert\n987422 window shutter open\n987423 bike fast\n987424 volume source\n987425 volume vibrate\n987426 movie edit\n987427 movie edit outline\n987428 movie filter\n987429 movie filter outline\n987430 diabetes\n987431 cursor default gesture\n987432 cursor default gesture outline\n987433 toothbrush\n987434 toothbrush paste\n987435 home roof\n987436 toothbrush electric\n987437 account supervisor outline\n987438 bottle tonic\n987439 bottle tonic outline\n987440 bottle tonic plus\n987441 bottle tonic plus outline\n987442 bottle tonic skull\n987443 bottle tonic skull outline\n987444 calendar arrow left\n987445 calendar arrow right\n987446 crosshairs question\n987447 fire hydrant\n987448 fire hydrant alert\n987449 fire hydrant off\n987450 ocr\n987451 shield star\n987452 shield star outline\n987453 text recognition\n987454 handcuffs\n987455 gender male female variant\n987456 gender non binary\n987457 minus box multiple\n987458 minus box multiple outline\n987459 plus box multiple outline\n987460 pencil box multiple\n987461 pencil box multiple outline\n987462 printer check\n987463 sort variant remove\n987464 sort alphabetical ascending variant\n987465 sort alphabetical descending variant\n987466 dice 1 outline\n987467 dice 2 outline\n987468 dice 3 outline\n987469 dice 4 outline\n987470 dice 5 outline\n987471 dice 6 outline\n987472 dice d4\n987473 dice d6\n987474 dice d8\n987475 dice d10\n987476 dice d12\n987477 dice d20\n987478 dice multiple outline\n987479 paper roll\n987480 paper roll outline\n987481 home edit\n987482 home edit outline\n987483 arrow horizontal lock\n987484 arrow vertical lock\n987485 weight lifter\n987486 account lock\n987487 account lock outline\n987488 pasta\n987489 send check\n987490 send check outline\n987491 send clock\n987492 send clock outline\n987493 send outline\n987494 send lock outline\n987495 police badge\n987496 police badge outline\n987497 gate arrow right\n987498 gate open\n987499 bell badge\n987500 message image outline\n987501 message lock outline\n987502 message minus\n987503 message minus outline\n987504 message processing outline\n987505 message settings outline\n987506 message cog outline\n987507 message text clock\n987508 message text clock outline\n987509 message text lock outline\n987510 checkbox blank badge\n987511 file link\n987512 file link outline\n987513 file phone\n987514 file phone outline\n987515 meditation\n987516 yoga\n987517 leek\n987518 noodles\n987519 pound box outline\n987520 school outline\n987521 basket outline\n987522 phone in talk outline\n987523 bash\n987524 file key\n987525 file key outline\n987526 file certificate\n987527 file certificate outline\n987528 certificate outline\n987529 cigar\n987530 grill outline\n987531 qrcode plus\n987532 qrcode minus\n987533 qrcode remove\n987534 phone alert outline\n987535 phone bluetooth outline\n987536 phone cancel outline\n987537 phone forward outline\n987538 phone hangup outline\n987539 phone incoming outline\n987540 phone lock outline\n987541 phone log outline\n987542 phone message\n987543 phone message outline\n987544 phone minus outline\n987545 phone outgoing outline\n987546 phone paused outline\n987547 phone plus outline\n987548 phone return outline\n987549 phone settings outline\n987550 key star\n987551 key link\n987552 shield edit\n987553 shield edit outline\n987554 shield sync\n987555 shield sync outline\n987556 golf cart\n987557 phone missed outline\n987558 phone off outline\n987559 format quote open outline\n987560 format quote close outline\n987561 phone check\n987562 phone check outline\n987563 phone ring\n987564 phone ring outline\n987565 share circle\n987566 reply circle\n987567 fridge off\n987568 fridge off outline\n987569 fridge alert\n987570 fridge alert outline\n987571 water boiler alert\n987572 water boiler off\n987573 amplifier off\n987574 audio video off\n987575 toaster off\n987576 dishwasher alert\n987577 dishwasher off\n987578 tumble dryer alert\n987579 tumble dryer off\n987580 washing machine alert\n987581 washing machine off\n987582 car info\n987583 comment edit\n987584 printer 3d nozzle alert\n987585 printer 3d nozzle alert outline\n987586 align horizontal left\n987587 align horizontal center\n987588 align horizontal right\n987589 align vertical bottom\n987590 align vertical center\n987591 align vertical top\n987592 distribute horizontal left\n987593 distribute horizontal center\n987594 distribute horizontal right\n987595 distribute vertical bottom\n987596 distribute vertical center\n987597 distribute vertical top\n987598 alert rhombus\n987599 alert rhombus outline\n987600 crown outline\n987601 image off outline\n987602 movie search\n987603 movie search outline\n987604 rv truck\n987605 shopping outline\n987606 strategy\n987607 note text outline\n987608 view agenda outline\n987609 view grid outline\n987610 view grid plus outline\n987611 window closed variant\n987612 window open variant\n987613 cog clockwise\n987614 cog counterclockwise\n987615 chart sankey\n987616 chart sankey variant\n987617 vanity light\n987618 router\n987619 image edit\n987620 image edit outline\n987621 bell check\n987622 bell check outline\n987623 file edit\n987624 file edit outline\n987625 human scooter\n987626 spider\n987627 spider thread\n987628 plus thick\n987629 alert circle check\n987630 alert circle check outline\n987631 state machine\n987632 usb port\n987633 cloud lock\n987634 cloud lock outline\n987635 robot mower outline\n987636 share all\n987637 share all outline\n987638 google cloud\n987639 robot mower\n987640 fast forward 5\n987641 rewind 5\n987642 shape oval plus\n987643 timeline clock\n987644 timeline clock outline\n987645 mirror\n987646 account multiple check outline\n987647 card plus\n987648 card plus outline\n987649 checkerboard plus\n987650 checkerboard minus\n987651 checkerboard remove\n987652 select search\n987653 selection search\n987654 layers search\n987655 layers search outline\n987656 lightbulb cfl\n987657 lightbulb cfl off\n987658 account multiple remove\n987659 account multiple remove outline\n987660 magnify remove cursor\n987661 magnify remove outline\n987662 archive outline\n987663 battery heart\n987664 battery heart outline\n987665 battery heart variant\n987666 bus marker\n987667 chart multiple\n987668 emoticon lol\n987669 emoticon lol outline\n987670 file sync\n987671 file sync outline\n987672 handshake\n987673 language kotlin\n987674 language fortran\n987675 offer\n987676 radio off\n987677 table headers eye\n987678 table headers eye off\n987679 tag minus outline\n987680 tag off\n987681 tag off outline\n987682 tag plus outline\n987683 tag remove outline\n987684 tag text\n987685 vector polyline edit\n987686 vector polyline minus\n987687 vector polyline plus\n987688 vector polyline remove\n987689 beaker alert\n987690 beaker alert outline\n987691 beaker check\n987692 beaker check outline\n987693 beaker minus\n987694 beaker minus outline\n987695 beaker plus\n987696 beaker plus outline\n987697 beaker question\n987698 beaker question outline\n987699 beaker remove\n987700 beaker remove outline\n987701 bicycle basket\n987702 barcode off\n987703 digital ocean\n987704 exclamation thick\n987705 desk\n987706 flask empty minus\n987707 flask empty minus outline\n987708 flask empty plus\n987709 flask empty plus outline\n987710 flask empty remove\n987711 flask empty remove outline\n987712 flask minus\n987713 flask minus outline\n987714 flask plus\n987715 flask plus outline\n987716 flask remove\n987717 flask remove outline\n987718 folder move outline\n987719 home remove\n987720 webrtc\n987721 seat passenger\n987722 web clock\n987723 flask round bottom\n987724 flask round bottom empty\n987725 flask round bottom empty outline\n987726 flask round bottom outline\n987727 gold\n987728 message star outline\n987729 home lightbulb\n987730 home lightbulb outline\n987731 lightbulb group\n987732 lightbulb group outline\n987733 lightbulb multiple\n987734 lightbulb multiple outline\n987735 api off\n987736 allergy\n987737 archive arrow down\n987738 archive arrow down outline\n987739 archive arrow up\n987740 archive arrow up outline\n987741 battery off\n987742 battery off outline\n987743 bookshelf\n987744 cash minus\n987745 cash plus\n987746 cash remove\n987747 clipboard check multiple\n987748 clipboard check multiple outline\n987749 clipboard file\n987750 clipboard file outline\n987751 clipboard multiple\n987752 clipboard multiple outline\n987753 clipboard play multiple\n987754 clipboard play multiple outline\n987755 clipboard text multiple\n987756 clipboard text multiple outline\n987757 folder marker\n987758 folder marker outline\n987759 format list text\n987760 inbox arrow down outline\n987761 inbox arrow up outline\n987762 inbox full\n987763 inbox full outline\n987764 inbox outline\n987765 lightbulb cfl spiral\n987766 magnify scan\n987767 map marker multiple outline\n987768 percent outline\n987769 phone classic off\n987770 play box\n987771 account eye outline\n987772 safe square\n987773 safe square outline\n987774 scoreboard\n987775 scoreboard outline\n987776 select marker\n987777 select multiple\n987778 select multiple marker\n987779 selection marker\n987780 selection multiple marker\n987781 selection multiple\n987782 star box multiple\n987783 star box multiple outline\n987784 toy brick\n987785 toy brick marker\n987786 toy brick marker outline\n987787 toy brick minus\n987788 toy brick minus outline\n987789 toy brick outline\n987790 toy brick plus\n987791 toy brick plus outline\n987792 toy brick remove\n987793 toy brick remove outline\n987794 toy brick search\n987795 toy brick search outline\n987796 tray\n987797 tray alert\n987798 tray full\n987799 tray minus\n987800 tray plus\n987801 tray remove\n987802 truck check outline\n987803 truck delivery outline\n987804 truck fast outline\n987805 truck outline\n987806 usb flash drive\n987807 usb flash drive outline\n987808 water polo\n987809 battery low\n987810 battery medium\n987811 battery high\n987812 battery charging low\n987813 battery charging medium\n987814 battery charging high\n987815 hexadecimal\n987816 gesture tap button\n987817 gesture tap box\n987818 lan check\n987819 keyboard f1\n987820 keyboard f2\n987821 keyboard f3\n987822 keyboard f4\n987823 keyboard f5\n987824 keyboard f6\n987825 keyboard f7\n987826 keyboard f8\n987827 keyboard f9\n987828 keyboard f10\n987829 keyboard f11\n987830 keyboard f12\n987831 keyboard esc\n987832 toslink\n987833 cheese\n987834 string lights\n987835 string lights off\n987836 whistle outline\n987837 stairs up\n987838 stairs down\n987839 escalator up\n987840 escalator down\n987841 elevator up\n987842 elevator down\n987843 lightbulb cfl spiral off\n987844 comment edit outline\n987845 tooltip edit outline\n987846 monitor edit\n987847 email sync\n987848 email sync outline\n987849 chat alert outline\n987850 chat processing outline\n987851 snowflake melt\n987852 cloud check outline\n987853 lightbulb group off\n987854 lightbulb group off outline\n987855 lightbulb multiple off\n987856 lightbulb multiple off outline\n987857 chat sleep\n987858 chat sleep outline\n987859 garage variant\n987860 garage open variant\n987861 garage alert variant\n987862 cloud sync outline\n987863 globe light\n987864 cellphone nfc off\n987865 leaf off\n987866 leaf maple off\n987867 map marker left\n987868 map marker right\n987869 map marker left outline\n987870 map marker right outline\n987871 account cancel\n987872 account cancel outline\n987873 file clock\n987874 file clock outline\n987875 folder table\n987876 folder table outline\n987877 hydro power\n987878 doorbell\n987879 bulma\n987880 iobroker\n987881 oci\n987882 label percent\n987883 label percent outline\n987884 checkbox blank off\n987885 checkbox blank off outline\n987886 square off\n987887 square off outline\n987888 drag horizontal variant\n987889 drag vertical variant\n987890 message arrow left\n987891 message arrow left outline\n987892 message arrow right\n987893 message arrow right outline\n987894 database marker\n987895 tag multiple outline\n987896 map marker plus outline\n987897 map marker minus outline\n987898 map marker remove outline\n987899 map marker check outline\n987900 map marker radius outline\n987901 map marker off outline\n987902 molecule co\n987903 jump rope\n987904 kettlebell\n987905 account convert outline\n987906 bunk bed\n987907 fleur de lis\n987908 ski\n987909 ski cross country\n987910 ski water\n987911 snowboard\n987912 account tie voice\n987913 account tie voice outline\n987914 account tie voice off\n987915 account tie voice off outline\n987916 beer outline\n987917 glass pint outline\n987918 coffee to go outline\n987919 cup outline\n987920 bottle wine outline\n987921 earth arrow right\n987922 key arrow right\n987923 format color marker cancel\n987924 mother heart\n987925 currency eur off\n987926 semantic web\n987927 kettle alert\n987928 kettle alert outline\n987929 kettle steam\n987930 kettle steam outline\n987931 kettle off\n987932 kettle off outline\n987933 simple icons\n987934 briefcase check outline\n987935 clipboard plus outline\n987936 download lock\n987937 download lock outline\n987938 hammer screwdriver\n987939 hammer wrench\n987940 hydraulic oil level\n987941 hydraulic oil temperature\n987942 medal outline\n987943 rodent\n987944 abjad arabic\n987945 abjad hebrew\n987946 abugida devanagari\n987947 abugida thai\n987948 alphabet aurebesh\n987949 alphabet cyrillic\n987950 alphabet greek\n987951 alphabet latin\n987952 alphabet piqad\n987953 ideogram cjk\n987954 ideogram cjk variant\n987955 syllabary hangul\n987956 syllabary hiragana\n987957 syllabary katakana\n987958 syllabary katakana halfwidth\n987959 alphabet tengwar\n987960 head alert\n987961 head alert outline\n987962 head check\n987963 head check outline\n987964 head cog\n987965 head cog outline\n987966 head dots horizontal\n987967 head dots horizontal outline\n987968 head flash\n987969 head flash outline\n987970 head heart\n987971 head heart outline\n987972 head lightbulb\n987973 head lightbulb outline\n987974 head minus\n987975 head minus outline\n987976 head plus\n987977 head plus outline\n987978 head question\n987979 head question outline\n987980 head remove\n987981 head remove outline\n987982 head snowflake\n987983 head snowflake outline\n987984 head sync\n987985 head sync outline\n987986 hvac\n987987 pencil ruler\n987988 pipe wrench\n987989 widgets outline\n987990 television ambient light\n987991 propane tank\n987992 propane tank outline\n987993 folder music\n987994 folder music outline\n987995 klingon\n987996 palette swatch outline\n987997 form textbox lock\n987998 head\n987999 head outline\n988000 shield half\n988001 store outline\n988002 google downasaur\n988003 bottle soda classic outline\n988004 sticker\n988005 sticker alert\n988006 sticker alert outline\n988007 sticker check\n988008 sticker check outline\n988009 sticker minus\n988010 sticker minus outline\n988011 sticker outline\n988012 sticker plus\n988013 sticker plus outline\n988014 sticker remove\n988015 sticker remove outline\n988016 account cog\n988017 account cog outline\n988018 account details outline\n988019 upload lock\n988020 upload lock outline\n988021 label multiple\n988022 label multiple outline\n988023 refresh circle\n988024 sync circle\n988025 bookmark music outline\n988026 bookmark remove outline\n988027 bookmark check outline\n988028 traffic cone\n988029 cup off outline\n988030 auto download\n988031 shuriken\n988032 chart ppf\n988033 elevator passenger\n988034 compass rose\n988035 space station\n988036 order bool descending\n988037 sort bool ascending\n988038 sort bool ascending variant\n988039 sort bool descending\n988040 sort bool descending variant\n988041 sort numeric ascending\n988042 sort numeric descending\n988043 human baby changing table\n988044 human male child\n988045 human wheelchair\n988046 microsoft access\n988047 microsoft excel\n988048 microsoft powerpoint\n988049 microsoft sharepoint\n988050 microsoft word\n988051 nintendo game boy\n988052 cable data\n988053 circle half\n988054 circle half full\n988055 cellphone charging\n988056 close thick\n988057 escalator box\n988058 lock check\n988059 lock open alert\n988060 lock open check\n988061 recycle variant\n988062 stairs box\n988063 hand water\n988064 table refresh\n988065 table sync\n988066 size xxs\n988067 size xs\n988068 size s\n988069 size m\n988071 size xl\n988072 size xxl\n988073 size xxxl\n988074 ticket confirmation outline\n988075 timer\n988076 timer off\n988077 book account\n988078 book account outline\n988079 rocket outline\n988080 home search\n988081 home search outline\n988082 car arrow left\n988083 car arrow right\n988084 monitor eye\n988085 lipstick\n988086 virus\n988087 virus outline\n988088 text search\n988089 table account\n988090 table alert\n988091 table arrow down\n988092 table arrow left\n988093 table arrow right\n988094 table arrow up\n988095 table cancel\n988096 table check\n988097 table clock\n988098 table cog\n988099 table eye off\n988100 table heart\n988101 table key\n988102 table lock\n988103 table minus\n988104 table multiple\n988105 table network\n988106 table off\n988107 table star\n988108 car cog\n988109 car settings\n988110 cog off\n988111 cog off outline\n988112 credit card check\n988113 credit card check outline\n988114 file tree outline\n988115 folder star multiple\n988116 folder star multiple outline\n988117 home minus outline\n988118 home plus outline\n988119 home remove outline\n988120 scan helper\n988121 video 3d off\n988122 shield bug\n988123 shield bug outline\n988124 eyedropper plus\n988125 eyedropper minus\n988126 eyedropper remove\n988127 eyedropper off\n988128 baby buggy\n988129 umbrella closed variant\n988130 umbrella closed outline\n988131 email off\n988132 email off outline\n988133 food variant off\n988134 play box multiple outline\n988135 bell cancel\n988136 bell cancel outline\n988137 bell minus\n988138 bell minus outline\n988139 bell remove\n988140 bell remove outline\n988141 beehive off outline\n988142 cheese off\n988143 corn off\n988144 egg off\n988145 egg off outline\n988146 egg outline\n988147 fish off\n988148 flask empty off\n988149 flask empty off outline\n988150 flask off\n988151 flask off outline\n988152 fruit cherries off\n988153 fruit citrus off\n988154 mushroom off\n988155 mushroom off outline\n988156 soy sauce off\n988157 seed off\n988158 seed off outline\n988159 tailwind\n988160 form dropdown\n988161 form select\n988162 pump\n988163 earth plus\n988164 earth minus\n988165 earth remove\n988166 earth box plus\n988167 earth box minus\n988168 earth box remove\n988169 gas station off\n988170 gas station off outline\n988171 lightning bolt\n988172 lightning bolt outline\n988173 smoking pipe\n988174 axis arrow info\n988175 chat plus\n988176 chat minus\n988177 chat remove\n988178 chat plus outline\n988179 chat minus outline\n988180 chat remove outline\n988181 bucket\n988182 bucket outline\n988183 pail\n988184 image remove\n988185 image minus\n988186 pine tree fire\n988187 cigar off\n988188 cube off\n988189 cube off outline\n988190 dome light\n988191 food drumstick\n988192 food drumstick outline\n988193 incognito circle\n988194 incognito circle off\n988195 microwave off\n988196 power plug off outline\n988197 power plug outline\n988198 puzzle check\n988199 puzzle check outline\n988200 smoking pipe off\n988201 spoon sugar\n988202 table split cell\n988203 ticket percent outline\n988204 fuse off\n988205 fuse alert\n988206 heart plus\n988207 heart minus\n988208 heart remove\n988209 heart plus outline\n988210 heart minus outline\n988211 heart remove outline\n988212 heart off outline\n988213 motion sensor off\n988214 pail plus\n988215 pail minus\n988216 pail remove\n988217 pail off\n988218 pail outline\n988219 pail plus outline\n988220 pail minus outline\n988221 pail remove outline\n988222 pail off outline\n988223 clock time one\n988224 clock time two\n988225 clock time three\n988226 clock time four\n988227 clock time five\n988228 clock time six\n988229 clock time seven\n988230 clock time eight\n988231 clock time nine\n988232 clock time ten\n988233 clock time eleven\n988234 clock time twelve\n988235 clock time one outline\n988236 clock time two outline\n988237 clock time three outline\n988238 clock time four outline\n988239 clock time five outline\n988240 clock time six outline\n988241 clock time seven outline\n988242 clock time eight outline\n988243 clock time nine outline\n988244 clock time ten outline\n988245 clock time eleven outline\n988246 clock time twelve outline\n988247 printer search\n988248 printer eye\n988249 minus circle off\n988250 minus circle off outline\n988251 content save cog\n988252 content save cog outline\n988253 set square\n988254 cog refresh\n988255 cog refresh outline\n988256 cog sync\n988257 cog sync outline\n988258 download box\n988259 download box outline\n988260 download circle\n988261 download circle outline\n988262 air humidifier off\n988263 chili off\n988264 food drumstick off\n988265 food drumstick off outline\n988266 food steak\n988267 food steak off\n988268 fan alert\n988269 fan chevron down\n988270 fan chevron up\n988271 fan plus\n988272 fan minus\n988273 fan remove\n988274 fan speed 1\n988275 fan speed 2\n988276 fan speed 3\n988277 rug\n988278 lingerie\n988279 wizard hat\n988280 hours 24\n988281 cosine wave\n988282 sawtooth wave\n988283 square wave\n988284 triangle wave\n988285 waveform\n988286 folder multiple plus\n988287 folder multiple plus outline\n988288 current ac\n988289 watering can\n988290 watering can outline\n988291 monitor share\n988292 laser pointer\n988293 view array outline\n988294 view carousel outline\n988295 view column outline\n988296 view comfy outline\n988297 view dashboard variant outline\n988298 view day outline\n988299 view list outline\n988300 view module outline\n988301 view parallel outline\n988302 view quilt outline\n988303 view sequential outline\n988304 view stream outline\n988305 view week outline\n988306 compare horizontal\n988307 compare vertical\n988308 briefcase variant\n988309 briefcase variant outline\n988310 relation many to many\n988311 relation many to one\n988312 relation many to one or many\n988313 relation many to only one\n988314 relation many to zero or many\n988315 relation many to zero or one\n988316 relation one or many to many\n988317 relation one or many to one\n988318 relation one or many to one or many\n988319 relation one or many to only one\n988320 relation one or many to zero or many\n988321 relation one or many to zero or one\n988322 relation one to many\n988323 relation one to one\n988324 relation one to one or many\n988325 relation one to only one\n988326 relation one to zero or many\n988327 relation one to zero or one\n988328 relation only one to many\n988329 relation only one to one\n988330 relation only one to one or many\n988331 relation only one to only one\n988332 relation only one to zero or many\n988333 relation only one to zero or one\n988334 relation zero or many to many\n988335 relation zero or many to one\n988336 relation zero or many to one or many\n988337 relation zero or many to only one\n988338 relation zero or many to zero or many\n988339 relation zero or many to zero or one\n988340 relation zero or one to many\n988341 relation zero or one to one\n988342 relation zero or one to one or many\n988343 relation zero or one to only one\n988344 relation zero or one to zero or many\n988345 relation zero or one to zero or one\n988346 alert plus\n988347 alert minus\n988348 alert remove\n988349 alert plus outline\n988350 alert minus outline\n988351 alert remove outline\n988352 carabiner\n988353 fencing\n988354 skateboard\n988355 polo\n988356 tractor variant\n988357 radiology box\n988358 radiology box outline\n988359 skull scan\n988360 skull scan outline\n988361 plus minus variant\n988362 source branch plus\n988363 source branch minus\n988364 source branch remove\n988365 source branch refresh\n988366 source branch sync\n988367 source branch check\n988368 puzzle plus\n988369 puzzle minus\n988370 puzzle remove\n988371 puzzle edit\n988372 puzzle heart\n988373 puzzle star\n988374 puzzle plus outline\n988375 puzzle minus outline\n988376 puzzle remove outline\n988377 puzzle edit outline\n988378 puzzle heart outline\n988379 puzzle star outline\n988380 rhombus medium outline\n988381 rhombus split outline\n988382 rocket launch\n988383 rocket launch outline\n988384 set merge\n988385 set split\n988386 beekeeper\n988387 snowflake off\n988388 weather sunny off\n988389 clipboard edit\n988390 clipboard edit outline\n988391 notebook edit\n988392 human edit\n988393 notebook edit outline\n988394 cash lock\n988395 cash lock open\n988396 account supervisor circle outline\n988397 car outline\n988398 cash check\n988399 filter off\n988400 filter off outline\n988401 spirit level\n988402 wheel barrow\n988403 book check\n988404 book check outline\n988405 notebook check\n988406 notebook check outline\n988407 book open variant\n988408 sign pole\n988409 shore\n988410 shape square rounded plus\n988411 square rounded\n988412 square rounded outline\n988413 archive alert\n988414 archive alert outline\n988415 power socket it\n988416 square circle\n988417 symbol\n988418 water alert\n988419 water alert outline\n988420 water check\n988421 water check outline\n988422 water minus\n988423 water minus outline\n988424 water off outline\n988425 water percent alert\n988426 water plus\n988427 water plus outline\n988428 water remove\n988429 water remove outline\n988430 snake\n988431 format text variant outline\n988432 grass\n988433 access point off\n988434 currency mnt\n988435 dock top\n988436 share variant outline\n988437 transit skip\n988438 yurt\n988439 file document multiple\n988440 file document multiple outline\n988441 ev plug ccs1\n988442 ev plug ccs2\n988443 ev plug chademo\n988444 ev plug tesla\n988445 ev plug type1\n988446 ev plug type2\n988447 office building outline\n988448 office building marker\n988449 office building marker outline\n988450 progress question\n988451 basket minus\n988452 basket minus outline\n988453 basket off\n988454 basket off outline\n988455 basket plus\n988456 basket plus outline\n988457 basket remove\n988458 basket remove outline\n988459 account reactivate\n988460 account reactivate outline\n988461 car lifted pickup\n988462 video high definition\n988463 phone remove\n988464 phone remove outline\n988465 thermometer off\n988466 timeline check\n988467 timeline check outline\n988468 timeline minus\n988469 timeline minus outline\n988470 timeline remove\n988471 timeline remove outline\n988472 access point check\n988473 access point minus\n988474 access point plus\n988475 access point remove\n988476 data matrix\n988477 data matrix edit\n988478 data matrix minus\n988479 data matrix plus\n988480 data matrix remove\n988481 data matrix scan\n988482 tune variant\n988483 tune vertical variant\n988484 rake\n988485 shimmer\n988486 transit connection horizontal\n988487 sort calendar ascending\n988488 sort calendar descending\n988489 sort clock ascending\n988490 sort clock ascending outline\n988491 sort clock descending\n988492 sort clock descending outline\n988493 chart box\n988494 chart box outline\n988495 chart box plus outline\n988496 mouse move down\n988497 mouse move up\n988498 mouse move vertical\n988499 pitchfork\n988500 vanish quarter\n988501 application settings outline\n988502 delete clock\n988503 delete clock outline\n988504 kangaroo\n988505 phone dial\n988506 phone dial outline\n988507 star off outline\n988508 tooltip check\n988509 tooltip check outline\n988510 tooltip minus\n988511 tooltip minus outline\n988512 tooltip remove\n988513 tooltip remove outline\n988514 pretzel\n988515 star plus\n988516 star minus\n988517 star remove\n988518 star check\n988519 star plus outline\n988520 star minus outline\n988521 star remove outline\n988522 star check outline\n988523 eiffel tower\n988524 submarine\n988525 sofa outline\n988526 sofa single\n988527 sofa single outline\n988528 text account\n988529 human queue\n988530 food halal\n988531 food kosher\n988532 key chain\n988533 key chain variant\n988534 lamps\n988535 application cog outline\n988536 dance pole\n988537 social distance 2 meters\n988538 social distance 6 feet\n988539 calendar cursor\n988540 emoticon sick\n988541 emoticon sick outline\n988542 hand heart outline\n988543 hand wash\n988544 hand wash outline\n988545 human cane\n988546 lotion\n988547 lotion outline\n988548 lotion plus\n988549 lotion plus outline\n988550 face mask\n988551 face mask outline\n988552 reiterate\n988553 butterfly\n988554 butterfly outline\n988555 bag suitcase\n988556 bag suitcase outline\n988557 bag suitcase off\n988558 bag suitcase off outline\n988559 motion play\n988560 motion pause\n988561 motion play outline\n988562 motion pause outline\n988563 arrow top left thin circle outline\n988564 arrow top right thin circle outline\n988565 arrow bottom right thin circle outline\n988566 arrow bottom left thin circle outline\n988567 arrow up thin circle outline\n988568 arrow right thin circle outline\n988569 arrow down thin circle outline\n988570 arrow left thin circle outline\n988571 human capacity decrease\n988572 human capacity increase\n988573 human greeting proximity\n988574 hvac off\n988575 inbox remove\n988576 inbox remove outline\n988577 handshake outline\n988578 ladder\n988579 router wireless off\n988580 seesaw\n988581 slide\n988582 calculator variant outline\n988583 shield account variant\n988584 shield account variant outline\n988585 message flash\n988586 message flash outline\n988587 list status\n988588 message bookmark\n988589 message bookmark outline\n988590 comment bookmark\n988591 comment bookmark outline\n988592 comment flash\n988593 comment flash outline\n988594 motion\n988595 motion outline\n988596 bicycle electric\n988597 car electric outline\n988598 chart timeline variant shimmer\n988599 moped electric\n988600 moped electric outline\n988601 moped outline\n988602 motorbike electric\n988603 rickshaw\n988604 rickshaw electric\n988605 scooter\n988606 scooter electric\n988607 horse\n988608 horse human\n988609 horse variant\n988610 unicorn\n988611 unicorn variant\n988612 alarm panel\n988613 alarm panel outline\n988614 bird\n988615 shoe cleat\n988616 shoe sneaker\n988617 human female dance\n988618 shoe ballet\n988619 numeric positive 1\n988620 face man shimmer\n988621 face man shimmer outline\n988622 face woman shimmer\n988623 face woman shimmer outline\n988624 home alert outline\n988625 lock alert outline\n988626 lock open alert outline\n988627 sim alert outline\n988628 sim off outline\n988629 sim outline\n988630 book open page variant outline\n988631 fire alert\n988632 ray start vertex end\n988633 camera flip\n988634 camera flip outline\n988635 orbit variant\n988636 circle box\n988637 circle box outline\n988638 mustache\n988639 comment minus\n988640 comment minus outline\n988641 comment off\n988642 comment off outline\n988643 eye remove\n988644 eye remove outline\n988645 unicycle\n988646 glass cocktail off\n988647 glass mug off\n988648 glass mug variant off\n988649 bicycle penny farthing\n988650 cart check\n988651 cart variant\n988652 baseball diamond\n988653 baseball diamond outline\n988654 fridge industrial\n988655 fridge industrial alert\n988656 fridge industrial alert outline\n988657 fridge industrial off\n988658 fridge industrial off outline\n988659 fridge industrial outline\n988660 fridge variant\n988661 fridge variant alert\n988662 fridge variant alert outline\n988663 fridge variant off\n988664 fridge variant off outline\n988665 fridge variant outline\n988666 windsock\n988667 dance ballroom\n988668 dots grid\n988669 dots square\n988670 dots triangle\n988671 dots hexagon\n988672 card minus\n988673 card minus outline\n988674 card off\n988675 card off outline\n988676 card remove\n988677 card remove outline\n988678 torch\n988679 navigation outline\n988680 map marker star\n988681 map marker star outline\n988682 manjaro\n988683 fast forward 60\n988684 rewind 60\n988685 image text\n988686 family tree\n988687 car emergency\n988688 notebook minus\n988689 notebook minus outline\n988690 notebook plus\n988691 notebook plus outline\n988692 notebook remove\n988693 notebook remove outline\n988694 connection\n988695 language rust\n988696 clipboard minus\n988697 clipboard minus outline\n988698 clipboard off\n988699 clipboard off outline\n988700 clipboard remove\n988701 clipboard remove outline\n988702 clipboard search\n988703 clipboard search outline\n988704 clipboard text off\n988705 clipboard text off outline\n988706 clipboard text search\n988707 clipboard text search outline\n988708 database alert outline\n988709 database arrow down outline\n988710 database arrow left outline\n988711 database arrow right outline\n988712 database arrow up outline\n988713 database check outline\n988714 database clock outline\n988715 database edit outline\n988716 database export outline\n988717 database import outline\n988718 database lock outline\n988719 database marker outline\n988720 database minus outline\n988721 database off outline\n988722 database outline\n988723 database plus outline\n988724 database refresh outline\n988725 database remove outline\n988726 database search outline\n988727 database settings outline\n988728 database sync outline\n988729 minus thick\n988730 database alert\n988731 database arrow down\n988732 database arrow left\n988733 database arrow right\n988734 database arrow up\n988735 database clock\n988736 database off\n988737 calendar lock\n988738 calendar lock outline\n988739 content save off\n988740 content save off outline\n988741 credit card refresh\n988742 credit card refresh outline\n988743 credit card search\n988744 credit card search outline\n988745 credit card sync\n988746 credit card sync outline\n988747 database cog\n988748 database cog outline\n988749 message off\n988750 message off outline\n988751 note minus\n988752 note minus outline\n988753 note remove\n988754 note remove outline\n988755 note search\n988756 note search outline\n988757 bank check\n988758 bank off\n988759 bank off outline\n988760 briefcase off\n988761 briefcase off outline\n988762 briefcase variant off\n988763 briefcase variant off outline\n988764 ghost off outline\n988765 ghost outline\n988766 store minus\n988767 store plus\n988768 store remove\n988769 email remove\n988770 email remove outline\n988771 heart cog\n988772 heart cog outline\n988773 heart settings\n988774 heart settings outline\n988775 pentagram\n988776 star cog\n988777 star cog outline\n988778 star settings\n988779 star settings outline\n988780 calendar end\n988781 calendar start\n988782 cannabis off\n988783 mower\n988784 mower bag\n988785 lock off\n988786 lock off outline\n988787 shark fin\n988788 shark fin outline\n988789 paw outline\n988790 paw off outline\n988791 snail\n988792 pig variant outline\n988793 piggy bank outline\n988794 robot outline\n988795 robot off outline\n988796 book alert\n988797 book alert outline\n988798 book arrow down\n988799 book arrow down outline\n988800 book arrow left\n988801 book arrow left outline\n988802 book arrow right\n988803 book arrow right outline\n988804 book arrow up\n988805 book arrow up outline\n988806 book cancel\n988807 book cancel outline\n988808 book clock\n988809 book clock outline\n988810 book cog\n988811 book cog outline\n988812 book edit\n988813 book edit outline\n988814 book lock open outline\n988815 book lock outline\n988816 book marker\n988817 book marker outline\n988818 book minus outline\n988819 book music outline\n988820 book off\n988821 book off outline\n988822 book plus outline\n988823 book refresh\n988824 book refresh outline\n988825 book remove outline\n988826 book settings\n988827 book settings outline\n988828 book sync\n988829 robot angry\n988830 robot angry outline\n988831 robot confused\n988832 robot confused outline\n988833 robot dead\n988834 robot dead outline\n988835 robot excited\n988836 robot excited outline\n988837 robot love\n988838 robot love outline\n988839 robot off\n988840 lock check outline\n988841 lock minus\n988842 lock minus outline\n988843 lock open check outline\n988844 lock open minus\n988845 lock open minus outline\n988846 lock open plus\n988847 lock open plus outline\n988848 lock open remove\n988849 lock open remove outline\n988850 lock plus outline\n988851 lock remove\n988852 lock remove outline\n988853 wifi alert\n988854 wifi arrow down\n988855 wifi arrow left\n988856 wifi arrow left right\n988857 wifi arrow right\n988858 wifi arrow up\n988859 wifi arrow up down\n988860 wifi cancel\n988861 wifi check\n988862 wifi cog\n988863 wifi lock\n988864 wifi lock open\n988865 wifi marker\n988866 wifi minus\n988867 wifi plus\n988868 wifi refresh\n988869 wifi remove\n988870 wifi settings\n988871 wifi sync\n988872 book sync outline\n988873 book education\n988874 book education outline\n988875 wifi strength 1 lock open\n988876 wifi strength 2 lock open\n988877 wifi strength 3 lock open\n988878 wifi strength 4 lock open\n988879 wifi strength lock open outline\n988880 cookie alert\n988881 cookie alert outline\n988882 cookie check\n988883 cookie check outline\n988884 cookie cog\n988885 cookie cog outline\n988886 cookie plus\n988887 cookie plus outline\n988888 cookie remove\n988889 cookie remove outline\n988890 cookie minus\n988891 cookie minus outline\n988892 cookie settings\n988893 cookie settings outline\n988894 cookie outline\n988895 tape drive\n988896 abacus\n988897 calendar clock outline\n988898 clipboard clock\n988899 clipboard clock outline\n988900 cookie clock\n988901 cookie clock outline\n988902 cookie edit\n988903 cookie edit outline\n988904 cookie lock\n988905 cookie lock outline\n988906 cookie off\n988907 cookie off outline\n988908 cookie refresh\n988909 cookie refresh outline\n988910 dog side off\n988911 gift off\n988912 gift off outline\n988913 gift open\n988914 gift open outline\n988915 movie check\n988916 movie check outline\n988917 movie cog\n988918 movie cog outline\n988919 movie minus\n988920 movie minus outline\n988921 movie off\n988922 movie off outline\n988923 movie open check\n988924 movie open check outline\n988925 movie open cog\n988926 movie open cog outline\n988927 movie open edit\n988928 movie open edit outline\n988929 movie open minus\n988930 movie open minus outline\n988931 movie open off\n988932 movie open off outline\n988933 movie open play\n988934 movie open play outline\n988935 movie open plus\n988936 movie open plus outline\n988937 movie open remove\n988938 movie open remove outline\n988939 movie open settings\n988940 movie open settings outline\n988941 movie open star\n988942 movie open star outline\n988943 movie play\n988944 movie play outline\n988945 movie plus\n988946 movie plus outline\n988947 movie remove\n988948 movie remove outline\n988949 movie settings\n988950 movie settings outline\n988951 movie star\n988952 movie star outline\n988953 robot happy\n988954 robot happy outline\n988955 turkey\n988956 food turkey\n988957 fan auto\n988958 alarm light off\n988959 alarm light off outline\n988960 broadcast\n988961 broadcast off\n988962 fire off\n988963 firework off\n988964 projector screen outline\n988965 script text key\n988966 script text key outline\n988967 script text play\n988968 script text play outline\n988969 surround sound 2 1\n988970 surround sound 5 1 2\n988971 tag arrow down\n988972 tag arrow down outline\n988973 tag arrow left\n988974 tag arrow left outline\n988975 tag arrow right\n988976 tag arrow right outline\n988977 tag arrow up\n988978 tag arrow up outline\n988979 train car passenger\n988980 train car passenger door\n988981 train car passenger door open\n988982 train car passenger variant\n988983 webcam off\n988984 chat question\n988985 chat question outline\n988986 message question\n988987 message question outline\n988988 kettle pour over\n988989 message reply outline\n988990 message reply text outline\n988991 koala\n988992 check decagram outline\n988993 star shooting\n988994 star shooting outline\n988995 table picnic\n988996 kitesurfing\n988997 paragliding\n988998 surfing\n988999 floor lamp torchiere\n989000 mortar pestle\n989001 cast audio variant\n989002 gradient horizontal\n989003 archive cancel\n989004 archive cancel outline\n989005 archive check\n989006 archive check outline\n989007 archive clock\n989008 archive clock outline\n989009 archive cog\n989010 archive cog outline\n989011 archive edit\n989012 archive edit outline\n989013 archive eye\n989014 archive eye outline\n989015 archive lock\n989016 archive lock open\n989017 archive lock open outline\n989018 archive lock outline\n989019 archive marker\n989020 archive marker outline\n989021 archive minus\n989022 archive minus outline\n989023 archive music\n989024 archive music outline\n989025 archive off\n989026 archive off outline\n989027 archive plus\n989028 archive plus outline\n989029 archive refresh\n989030 archive refresh outline\n989031 archive remove\n989032 archive remove outline\n989033 archive search\n989034 archive search outline\n989035 archive settings\n989036 archive settings outline\n989037 archive star\n989038 archive star outline\n989039 archive sync\n989040 archive sync outline\n989041 brush off\n989042 file image marker\n989043 file image marker outline\n989044 file marker\n989045 file marker outline\n989046 hamburger check\n989047 hamburger minus\n989048 hamburger off\n989049 hamburger plus\n989050 hamburger remove\n989051 image marker\n989052 image marker outline\n989053 note alert\n989054 note alert outline\n989055 note check\n989056 note check outline\n989057 note edit\n989058 note edit outline\n989059 note off\n989060 note off outline\n989061 printer off outline\n989062 printer outline\n989063 progress pencil\n989064 progress star\n989065 sausage off\n989066 folder eye\n989067 folder eye outline\n989068 information off\n989069 information off outline\n989070 sticker text\n989071 sticker text outline\n989072 web cancel\n989073 web refresh\n989074 web sync\n989075 chandelier\n989076 home switch\n989077 home switch outline\n989078 sun snowflake\n989079 ceiling fan\n989080 ceiling fan light\n989081 smoke\n989082 fence\n989083 light recessed\n989084 battery lock\n989085 battery lock open\n989086 folder hidden\n989087 mirror rectangle\n989088 mirror variant\n989089 arrow down left\n989090 arrow down left bold\n989091 arrow down right\n989092 arrow down right bold\n989093 arrow left bottom\n989094 arrow left bottom bold\n989095 arrow left top\n989096 arrow left top bold\n989097 arrow right bottom\n989098 arrow right bottom bold\n989099 arrow right top\n989100 arrow right top bold\n989101 arrow u down left\n989102 arrow u down left bold\n989103 arrow u down right\n989104 arrow u down right bold\n989105 arrow u left bottom\n989106 arrow u left bottom bold\n989107 arrow u left top\n989108 arrow u left top bold\n989109 arrow u right bottom\n989110 arrow u right bottom bold\n989111 arrow u right top\n989112 arrow u right top bold\n989113 arrow u up left\n989114 arrow u up left bold\n989115 arrow u up right\n989116 arrow u up right bold\n989117 arrow up left\n989118 arrow up left bold\n989119 arrow up right\n989120 arrow up right bold\n989121 select remove\n989122 selection ellipse remove\n989123 selection remove\n989124 human greeting\n989125 ph\n989126 water sync\n989127 ceiling light outline\n989128 floor lamp outline\n989129 wall sconce flat outline\n989130 wall sconce flat variant outline\n989131 wall sconce outline\n989132 wall sconce round outline\n989133 wall sconce round variant outline\n989134 floor lamp dual outline\n989135 floor lamp torchiere variant outline\n989136 lamp outline\n989137 lamps outline\n989138 candelabra\n989139 candelabra fire\n989140 menorah\n989141 menorah fire\n989142 floor lamp torchiere outline\n989143 credit card edit\n989144 credit card edit outline\n989145 briefcase eye\n989146 briefcase eye outline\n989147 soundbar\n989148 crown circle\n989149 crown circle outline\n989150 battery arrow down\n989151 battery arrow down outline\n989152 battery arrow up\n989153 battery arrow up outline\n989154 battery check\n989155 battery check outline\n989156 battery minus\n989157 battery minus outline\n989158 battery plus\n989159 battery plus outline\n989160 battery remove\n989161 battery remove outline\n989162 chili alert\n989163 chili alert outline\n989164 chili hot outline\n989165 chili medium outline\n989166 chili mild outline\n989167 chili off outline\n989168 cake variant outline\n989169 card multiple\n989170 card multiple outline\n989171 account cowboy hat outline\n989172 lightbulb spot\n989173 lightbulb spot off\n989174 fence electric\n989175 gate arrow left\n989176 gate alert\n989177 boom gate up\n989178 boom gate up outline\n989179 garage lock\n989180 garage variant lock\n989181 cellphone check\n989182 sun wireless\n989183 sun wireless outline\n989184 lightbulb auto\n989185 lightbulb auto outline\n989186 lightbulb variant\n989187 lightbulb variant outline\n989188 lightbulb fluorescent tube\n989189 lightbulb fluorescent tube outline\n989190 water circle\n989191 fire circle\n989192 smoke detector outline\n989193 smoke detector off\n989194 smoke detector off outline\n989195 smoke detector variant\n989196 smoke detector variant off\n989197 projector screen off\n989198 projector screen off outline\n989199 projector screen variant\n989200 projector screen variant off\n989201 projector screen variant off outline\n989202 projector screen variant outline\n989203 brush variant\n989204 car wrench\n989205 account injury\n989206 account injury outline\n989207 balcony\n989208 bathtub\n989209 bathtub outline\n989210 blender outline\n989211 coffee maker outline\n989212 countertop\n989213 countertop outline\n989214 door sliding\n989215 door sliding lock\n989216 door sliding open\n989217 hand wave\n989218 hand wave outline\n989219 human male female child\n989220 iron\n989221 iron outline\n989222 liquid spot\n989223 mosque\n989224 shield moon\n989225 shield moon outline\n989226 traffic light outline\n989227 hand front left\n989228 hand back left outline\n989229 hand back right outline\n989230 hand front left outline\n989231 hand front right outline\n989232 hand back left off\n989233 hand back right off\n989234 hand back left off outline\n989235 hand back right off outline\n989236 battery sync\n989237 battery sync outline\n989238 food takeout box\n989239 food takeout box outline\n989240 iron board\n989241 police station\n989242 cellphone marker\n989243 tooltip cellphone\n989244 table pivot\n989245 tunnel\n989246 tunnel outline\n989247 arrow projectile multiple\n989248 arrow projectile\n989249 bow arrow\n989250 axe battle\n989251 mace\n989252 magic staff\n989253 spear\n989254 curtains\n989255 curtains closed\n989256 human non binary\n989257 waterfall\n989258 egg fried\n989259 food hot dog\n989260 induction\n989261 pipe valve\n989262 shipping pallet\n989263 earbuds\n989264 earbuds off\n989265 earbuds off outline\n989266 earbuds outline\n989267 circle opacity\n989268 square opacity\n989269 water opacity\n989270 vector polygon variant\n989271 vector square close\n989272 vector square open\n989273 waves arrow left\n989274 waves arrow right\n989275 waves arrow up\n989276 cash fast\n989277 radioactive circle\n989278 radioactive circle outline\n989279 cctv off\n989280 format list group\n989281 clock plus\n989282 clock plus outline\n989283 clock minus\n989284 clock minus outline\n989285 clock remove\n989286 clock remove outline\n989287 account arrow up\n989288 account arrow down\n989289 account arrow down outline\n989290 account arrow up outline\n989291 audio input rca\n989292 audio input stereo minijack\n989293 audio input xlr\n989294 horse variant fast\n989295 email fast\n989296 email fast outline\n989297 camera document\n989298 camera document off\n989299 glass fragile\n989300 magnify expand\n989301 town hall\n989302 monitor small\n989303 diversify\n989304 car wireless\n989305 car select\n989306 airplane alert\n989307 airplane check\n989308 airplane clock\n989309 airplane cog\n989310 airplane edit\n989311 airplane marker\n989312 airplane minus\n989313 airplane plus\n989314 airplane remove\n989315 airplane search\n989316 airplane settings\n989317 flower pollen\n989318 flower pollen outline\n989319 hammer sickle\n989320 view gallery\n989321 view gallery outline\n989322 umbrella beach\n989323 umbrella beach outline\n989324 cabin a frame\n989325 all inclusive box\n989326 all inclusive box outline\n989327 hand coin\n989328 hand coin outline\n989329 truck flatbed\n989330 layers edit\n989331 multicast\n989332 hydrogen station\n989333 thermometer bluetooth\n989334 tire\n989335 forest\n989336 account tie hat\n989337 account tie hat outline\n989338 account wrench\n989339 account wrench outline\n989340 bicycle cargo\n989341 calendar collapse horizontal\n989342 calendar expand horizontal\n989343 cards club outline\n989345 cards playing\n989346 cards playing club\n989347 cards playing club multiple\n989348 cards playing club multiple outline\n989349 cards playing club outline\n989350 cards playing diamond\n989351 cards playing diamond multiple\n989352 cards playing diamond multiple outline\n989353 cards playing diamond outline\n989354 cards playing heart\n989355 cards playing heart multiple\n989356 cards playing heart multiple outline\n989357 cards playing heart outline\n989358 cards playing spade\n989359 cards playing spade multiple\n989360 cards playing spade multiple outline\n989361 cards playing spade outline\n989362 cards spade outline\n989363 compare remove\n989364 dolphin\n989365 fuel cell\n989366 hand extended\n989367 hand extended outline\n989368 printer 3d nozzle heat\n989369 printer 3d nozzle heat outline\n989370 shark\n989371 shark off\n989372 shield crown\n989373 shield crown outline\n989374 shield sword\n989375 shield sword outline\n989376 sickle\n989377 store alert\n989378 store alert outline\n989379 store check\n989380 store check outline\n989381 store clock\n989382 store clock outline\n989383 store cog\n989384 store cog outline\n989385 store edit\n989386 store edit outline\n989387 store marker\n989388 store marker outline\n989389 store minus outline\n989390 store off\n989391 store off outline\n989392 store plus outline\n989393 store remove outline\n989394 store search\n989395 store search outline\n989396 store settings\n989397 store settings outline\n989398 sun thermometer\n989399 sun thermometer outline\n989400 truck cargo container\n989401 vector square edit\n989402 vector square minus\n989403 vector square plus\n989404 vector square remove\n989405 ceiling light multiple\n989406 ceiling light multiple outline\n989407 wiper wash alert\n989408 cart heart\n989409 virus off\n989410 virus off outline\n989411 map marker account\n989412 map marker account outline\n989413 basket check\n989414 basket check outline\n989415 credit card lock\n989416 credit card lock outline\n989417 format underline wavy\n989418 content save check\n989419 content save check outline\n989420 filter check\n989421 filter check outline\n989422 flag off\n989423 flag off outline\n989425 navigation variant outline\n989426 refresh auto\n989427 tilde off\n989428 fit to screen\n989429 fit to screen outline\n989430 weather cloudy clock\n989431 smart card off\n989432 smart card off outline\n989433 clipboard text clock\n989434 clipboard text clock outline\n989435 teddy bear\n989436 cow off\n989437 eye arrow left\n989438 eye arrow left outline\n989439 eye arrow right\n989440 eye arrow right outline\n989441 home battery\n989442 home battery outline\n989443 home lightning bolt\n989444 home lightning bolt outline\n989445 leaf circle\n989446 leaf circle outline\n989447 tag search\n989448 tag search outline\n989449 car brake fluid level\n989450 car brake low pressure\n989451 car brake temperature\n989452 car brake worn linings\n989453 car light alert\n989454 car speed limiter\n989455 credit card chip\n989456 credit card chip outline\n989457 credit card fast\n989458 credit card fast outline\n989459 integrated circuit chip\n989460 thumbs up down outline\n989461 food off outline\n989462 food outline\n989463 format page split\n989464 chart waterfall\n989465 gamepad outline\n989466 network strength 4 cog\n989467 account sync\n989468 account sync outline\n989469 bus electric\n989470 liquor\n989471 database eye\n989472 database eye off\n989473 database eye off outline\n989474 database eye outline\n989475 timer settings\n989476 timer settings outline\n989477 timer cog\n989478 timer cog outline\n989479 checkbox marked circle plus outline\n989480 panorama horizontal\n989481 panorama vertical\n989482 advertisements\n989483 advertisements off\n989484 transmission tower export\n989485 transmission tower import\n989486 smoke detector alert\n989487 smoke detector alert outline\n989488 smoke detector variant alert\n989489 coffee maker check\n989490 coffee maker check outline\n989491 cog pause\n989492 cog pause outline\n989493 cog play\n989494 cog play outline\n989495 cog stop\n989496 cog stop outline\n989497 copyleft\n989498 fast forward 15\n989499 file image minus\n989500 file image minus outline\n989501 file image plus\n989502 file image plus outline\n989503 file image remove\n989504 file image remove outline\n989505 message badge\n989506 message badge outline\n989507 newspaper check\n989508 newspaper remove\n989509 publish off\n989510 rewind 15\n989511 view dashboard edit\n989512 view dashboard edit outline\n989513 office building cog\n989514 office building cog outline\n989515 hand clap\n989516 cone\n989517 cone off\n989518 cylinder\n989519 cylinder off\n989520 octahedron\n989521 octahedron off\n989522 pyramid\n989523 pyramid off\n989524 sphere\n989525 sphere off\n989526 format letter spacing\n989527 french fries\n989528 scent\n989529 scent off\n989530 palette swatch variant\n989531 email seal\n989532 email seal outline\n989533 stool\n989534 stool outline\n989535 panorama wide angle\n989536 account lock open\n989537 account lock open outline\n989538 align horizontal distribute\n989539 align vertical distribute\n989540 arrow bottom left bold box\n989541 arrow bottom left bold box outline\n989542 arrow bottom right bold box\n989543 arrow bottom right bold box outline\n989544 arrow top left bold box\n989545 arrow top left bold box outline\n989546 arrow top right bold box\n989547 arrow top right bold box outline\n989548 bookmark box multiple\n989549 bookmark box multiple outline\n989550 bullhorn variant\n989551 bullhorn variant outline\n989552 candy\n989553 candy off\n989554 candy off outline\n989555 candy outline\n989556 car clock\n989557 crowd\n989558 currency rupee\n989559 diving\n989560 dots circle\n989561 elevator passenger off\n989562 elevator passenger off outline\n989563 elevator passenger outline\n989564 eye refresh\n989565 eye refresh outline\n989566 folder check\n989567 folder check outline\n989568 human dolly\n989569 human white cane\n989570 ip outline\n989571 key alert\n989572 key alert outline\n989573 kite\n989574 kite outline\n989575 light flood down\n989576 light flood up\n989577 microphone question\n989578 microphone question outline\n989579 cradle\n989580 panorama outline\n989581 panorama sphere\n989582 panorama sphere outline\n989583 panorama variant\n989584 panorama variant outline\n989585 cradle outline\n989586 fraction one half\n989587 phone refresh\n989588 phone refresh outline\n989589 phone sync\n989590 phone sync outline\n989591 razor double edge\n989592 razor single edge\n989593 rotate 360\n989594 shield lock open\n989595 shield lock open outline\n989596 sitemap outline\n989597 sprinkler fire\n989598 tab search\n989599 timer sand complete\n989600 timer sand paused\n989601 vacuum\n989602 vacuum outline\n989603 wrench clock\n989604 pliers\n989605 sun compass\n989606 truck snowflake\n989607 camera marker\n989608 camera marker outline\n989609 video marker\n989610 video marker outline\n989611 wind turbine alert\n989612 wind turbine check\n989613 truck plus\n989614 truck minus\n989615 truck remove\n989616 arrow right thin\n989617 arrow left thin\n989618 arrow up thin\n989619 arrow down thin\n989620 arrow top right thin\n989621 arrow top left thin\n989622 arrow bottom left thin\n989623 arrow bottom right thin\n989624 scale unbalanced\n989625 draw pen\n989626 clock edit\n989627 clock edit outline\n989628 truck plus outline\n989629 truck minus outline\n989630 truck remove outline\n989631 camera off outline\n989632 home group plus\n989633 home group minus\n989634 home group remove\n989635 file sign\n989636 attachment lock\n989637 cellphone arrow down variant\n989638 file chart check\n989639 file chart check outline\n989640 file lock open\n989641 file lock open outline\n989642 folder question\n989643 folder question outline\n989644 message fast\n989645 message fast outline\n989646 message text fast\n989647 message text fast outline\n989648 monitor arrow down\n989649 monitor arrow down variant\n989650 needle off\n989651 numeric off\n989652 package variant closed minus\n989653 package variant closed plus\n989654 package variant closed remove\n989655 package variant minus\n989656 package variant plus\n989657 package variant remove\n989658 paperclip lock\n989659 phone clock\n989660 receipt outline\n989661 transmission tower off\n989662 truck alert\n989663 truck alert outline\n989664 bone off\n989665 lightbulb alert\n989666 lightbulb alert outline\n989667 lightbulb question\n989668 lightbulb question outline\n989669 battery clock\n989670 battery clock outline\n989671 autorenew off\n989672 folder arrow down\n989673 folder arrow down outline\n989674 folder arrow left\n989675 folder arrow left outline\n989676 folder arrow left right\n989677 folder arrow left right outline\n989678 folder arrow right\n989679 folder arrow right outline\n989680 folder arrow up\n989681 folder arrow up down\n989682 folder arrow up down outline\n989683 folder arrow up outline\n989684 folder cancel\n989685 folder cancel outline\n989686 folder file\n989687 folder file outline\n989688 folder off\n989689 folder off outline\n989690 folder play\n989691 folder play outline\n989692 folder wrench\n989693 folder wrench outline\n989694 image refresh\n989695 image refresh outline\n989696 image sync\n989697 image sync outline\n989698 percent box\n989699 percent box outline\n989700 percent circle\n989701 percent circle outline\n989702 sale outline\n989703 square rounded badge\n989704 square rounded badge outline\n989705 triangle small down\n989706 triangle small up\n989707 notebook heart\n989708 notebook heart outline\n989709 brush outline\n989710 fruit pear\n989711 raw\n989712 raw off\n989713 wall fire\n989714 home clock\n989715 home clock outline\n989716 camera lock\n989717 camera lock outline\n989718 play box lock\n989719 play box lock open\n989720 play box lock open outline\n989721 play box lock outline\n989722 robot industrial outline\n989723 gas burner\n989724 video 2d\n989725 book heart\n989726 book heart outline\n989727 account hard hat outline\n989728 account school\n989729 account school outline\n989730 library outline\n989731 projector off\n989732 light switch off\n989733 toggle switch variant\n989734 toggle switch variant off\n989735 asterisk circle outline\n989736 barrel outline\n989737 bell cog\n989738 bell cog outline\n989739 blinds horizontal\n989740 blinds horizontal closed\n989741 blinds vertical\n989742 blinds vertical closed\n989743 bulkhead light\n989744 calendar today outline\n989745 calendar week begin outline\n989746 calendar week end\n989747 calendar week end outline\n989748 calendar week outline\n989749 cloud percent\n989750 cloud percent outline\n989751 coach lamp variant\n989752 compost\n989753 currency fra\n989754 fan clock\n989755 file rotate left\n989756 file rotate left outline\n989757 file rotate right\n989758 file rotate right outline\n989759 filter multiple\n989760 filter multiple outline\n989761 gymnastics\n989762 hand clap off\n989763 heat pump\n989764 heat pump outline\n989765 heat wave\n989766 home off\n989767 home off outline\n989768 landslide\n989769 landslide outline\n989770 laptop account\n989771 led strip variant off\n989772 lightbulb night\n989773 lightbulb night outline\n989774 lightbulb on 10\n989775 lightbulb on 20\n989776 lightbulb on 30\n989777 lightbulb on 40\n989778 lightbulb on 50\n989779 lightbulb on 60\n989780 lightbulb on 70\n989781 lightbulb on 80\n989782 lightbulb on 90\n989783 meter electric\n989784 meter electric outline\n989785 meter gas\n989786 meter gas outline\n989787 monitor account\n989788 pill off\n989789 plus lock\n989790 plus lock open\n989791 pool thermometer\n989792 post lamp\n989793 rabbit variant\n989794 rabbit variant outline\n989795 receipt text check\n989796 receipt text check outline\n989797 receipt text minus\n989798 receipt text minus outline\n989799 receipt text plus\n989800 receipt text plus outline\n989801 receipt text remove\n989802 receipt text remove outline\n989803 roller shade\n989804 roller shade closed\n989805 seed plus\n989806 seed plus outline\n989807 shopping search outline\n989808 snowflake check\n989809 snowflake thermometer\n989810 snowshoeing\n989811 solar power variant\n989812 solar power variant outline\n989813 storage tank\n989814 storage tank outline\n989815 sun clock\n989816 sun clock outline\n989817 sun snowflake variant\n989818 tag check\n989819 tag check outline\n989820 text box edit\n989821 text box edit outline\n989822 text search variant\n989823 thermometer check\n989824 thermometer water\n989825 tsunami\n989826 turbine\n989827 volcano\n989828 volcano outline\n989829 water thermometer\n989830 water thermometer outline\n989831 wheelchair\n989832 wind power\n989833 wind power outline\n989834 window shutter cog\n989835 window shutter settings\n989836 account tie woman\n989837 briefcase arrow left right\n989838 briefcase arrow left right outline\n989839 briefcase arrow up down\n989840 briefcase arrow up down outline\n989841 cash clock\n989842 cash sync\n989843 file arrow left right\n989844 file arrow left right outline\n989845 file arrow up down\n989846 file arrow up down outline\n989847 file document alert\n989848 file document alert outline\n989849 file document check\n989850 file document check outline\n989851 file document minus\n989852 file document minus outline\n989853 file document plus\n989854 file document plus outline\n989855 file document remove\n989856 file document remove outline\n989857 file minus\n989858 file minus outline\n989859 filter cog\n989860 filter cog outline\n989861 filter settings\n989862 filter settings outline\n989863 folder lock open outline\n989864 folder lock outline\n989865 forum minus\n989866 forum minus outline\n989867 forum plus\n989868 forum plus outline\n989869 forum remove\n989870 forum remove outline\n989871 heating coil\n989872 image lock\n989873 image lock outline\n989874 land fields\n989875 land plots\n989876 land plots circle\n989877 land plots circle variant\n989878 land rows horizontal\n989879 land rows vertical\n989880 medical cotton swab\n989881 rolodex\n989882 rolodex outline\n989883 sort variant off\n989884 tally mark 1\n989885 tally mark 2\n989886 tally mark 3\n989887 tally mark 4\n989888 tally mark 5\n989889 attachment check\n989890 attachment minus\n989891 attachment off\n989892 attachment plus\n989893 attachment remove\n989894 paperclip check\n989895 paperclip minus\n989896 paperclip off\n989897 paperclip plus\n989898 paperclip remove\n989899 network pos\n989900 timer alert\n989901 timer alert outline\n989902 timer cancel\n989903 timer cancel outline\n989904 timer check\n989905 timer check outline\n989906 timer edit\n989907 timer edit outline\n989908 timer lock\n989909 timer lock open\n989910 timer lock open outline\n989911 timer lock outline\n989912 timer marker\n989913 timer marker outline\n989914 timer minus\n989915 timer minus outline\n989916 timer music\n989917 timer music outline\n989918 timer pause\n989919 timer pause outline\n989920 timer play\n989921 timer play outline\n989922 timer plus\n989923 timer plus outline\n989924 timer refresh\n989925 timer refresh outline\n989926 timer remove\n989927 timer remove outline\n989928 timer star\n989929 timer star outline\n989930 timer stop\n989931 timer stop outline\n989932 timer sync\n989933 timer sync outline\n989934 ear hearing loop\n989935 sail boat sink\n989936 lecturn\n1048573 <plane 15 private use, last>\n1048576 <plane 16 private use, first>\n1114109 <plane 16 private use, last>\n"
  },
  {
    "path": "tools/unicode_names/query.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage unicode_names\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/utils/images\"\n)\n\ntype mark_set = *utils.Set[uint16]\n\n//go:embed data_generated.bin\nvar unicode_name_data string\nvar _ = fmt.Print\nvar names map[rune]string\nvar marks []rune\nvar word_map map[string][]uint16\n\nfunc add_word(codepoint uint16, word []byte) {\n\tif codepoint <= 32 || codepoint == 127 || (128 <= codepoint && codepoint <= 159) || len(word) < 2 {\n\t\treturn\n\t}\n\tw := utils.UnsafeBytesToString(word)\n\tword_map[w] = append(word_map[w], codepoint)\n}\n\nfunc add_words(codepoint uint16, raw []byte) {\n\tfor len(raw) > 0 {\n\t\tidx := bytes.IndexByte(raw, ' ')\n\t\tif idx < 0 {\n\t\t\tadd_word(codepoint, raw)\n\t\t\tbreak\n\t\t}\n\t\tif idx > 0 {\n\t\t\tadd_word(codepoint, raw[:idx])\n\t\t}\n\t\traw = raw[idx+1:]\n\t}\n}\n\nfunc parse_record(record []byte, mark uint16) {\n\tcodepoint := rune(binary.LittleEndian.Uint32(record))\n\trecord = record[4:]\n\tmarks[mark] = codepoint\n\tnamelen := binary.LittleEndian.Uint16(record)\n\trecord = record[2:]\n\tname := utils.UnsafeBytesToString(record[:namelen])\n\tnames[codepoint] = name\n\tadd_words(mark, record[:namelen])\n\tif len(record) > int(namelen) {\n\t\tadd_words(mark, record[namelen:])\n\t}\n}\n\nvar parse_once sync.Once\n\nfunc parse_data() {\n\traw := utils.ReadCompressedEmbeddedData(unicode_name_data)\n\tnum_of_lines := binary.LittleEndian.Uint32(raw)\n\traw = raw[4:]\n\tnum_of_words := binary.LittleEndian.Uint32(raw)\n\traw = raw[4:]\n\tnames = make(map[rune]string, num_of_lines)\n\tword_map = make(map[string][]uint16, num_of_words)\n\tmarks = make([]rune, num_of_lines)\n\tvar mark uint16\n\tfor len(raw) > 0 {\n\t\trecord_len := binary.LittleEndian.Uint16(raw)\n\t\traw = raw[2:]\n\t\tparse_record(raw[:record_len], mark)\n\t\tmark += 1\n\t\traw = raw[record_len:]\n\t}\n}\n\nfunc Initialize() {\n\tparse_once.Do(parse_data)\n}\n\nfunc NameForCodePoint(cp rune) string {\n\tInitialize()\n\treturn names[cp]\n}\n\nfunc find_matching_codepoints(prefix string) (ans mark_set) {\n\tfor q, marks := range word_map {\n\t\tif strings.HasPrefix(q, prefix) {\n\t\t\tif ans == nil {\n\t\t\t\tans = utils.NewSet[uint16](len(marks) * 2)\n\t\t\t}\n\t\t\tans.AddItems(marks...)\n\t\t}\n\t}\n\tif ans == nil {\n\t\tans = utils.NewSet[uint16](0)\n\t}\n\treturn ans\n}\n\nfunc marks_for_query(query string) (ans mark_set) {\n\tInitialize()\n\tprefixes := strings.Split(strings.ToLower(query), \" \")\n\tresults := make(chan mark_set, len(prefixes))\n\tctx := images.Context{}\n\tif err := ctx.SafeParallel(0, len(prefixes), func(nums <-chan int) {\n\t\tfor i := range nums {\n\t\t\tresults <- find_matching_codepoints(prefixes[i])\n\t\t}\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\tclose(results)\n\tfor x := range results {\n\t\tif ans == nil {\n\t\t\tans = x\n\t\t} else {\n\t\t\tans = ans.Intersect(x)\n\t\t}\n\t}\n\tif ans == nil {\n\t\tans = utils.NewSet[uint16](0)\n\t}\n\treturn\n}\n\nfunc CodePointsForQuery(query string) (ans []rune) {\n\tx := marks_for_query(query)\n\tans = make([]rune, x.Len())\n\ti := 0\n\tfor m := range x.Iterable() {\n\t\tans[i] = marks[m]\n\t\ti += 1\n\t}\n\treturn\n}\n\nfunc Develop() {\n\tstart := time.Now()\n\tInitialize()\n\tfmt.Println(\"Parsing unicode name data took:\", time.Since(start))\n\tstart = time.Now()\n\tnum := CodePointsForQuery(\"arr\")\n\tfmt.Println(\"Querying arr took:\", time.Since(start), \"and found:\", len(num))\n\tstart = time.Now()\n\tnum = CodePointsForQuery(\"arr right\")\n\tfmt.Println(\"Querying arr right took:\", time.Since(start), \"and found:\", len(num))\n}\n"
  },
  {
    "path": "tools/unicode_names/query_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage unicode_names\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"slices\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestUnicodeInputQueries(t *testing.T) {\n\tts := func(query string, expected ...rune) {\n\t\tif expected == nil {\n\t\t\texpected = make([]rune, 0)\n\t\t}\n\t\texpected = utils.Sort(expected, func(a, b rune) int { return int(a) - int(b) })\n\t\tactual := CodePointsForQuery(query)\n\t\tactual = utils.Sort(actual, func(a, b rune) int { return int(a) - int(b) })\n\t\tdiff := cmp.Diff(expected, actual)\n\t\tif diff != \"\" {\n\t\t\tt.Fatalf(\"Failed query: %#v\\n%s\", query, diff)\n\t\t}\n\t}\n\tts(\"horiz ell\", 0x2026, 0x22ef, 0x2b2c, 0x2b2d, 0xfe19)\n\tts(\"horizontal ell\", 0x2026, 0x22ef, 0x2b2c, 0x2b2d, 0xfe19)\n\tts(\"kfjhgkjdsfhgkjds\")\n\tif slices.Index(CodePointsForQuery(\"bee\"), 0x1f41d) < 0 {\n\t\tt.Fatalf(\"The query bee did not match the codepoint: 0x1f41d\")\n\t}\n}\n"
  },
  {
    "path": "tools/utils/atexit.go",
    "content": "package utils\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\nvar _ = fmt.Print\n\ntype worker struct {\n\tcmd        *exec.Cmd\n\tstdin_pipe io.WriteCloser\n}\n\nvar worker_started atomic.Bool\n\n// IsTesting returns true if the code is being run by \"go test\".\nfunc IsTesting() bool {\n\treturn flag.Lookup(\"test.v\") != nil\n}\n\nvar get_worker = sync.OnceValues(func() (*worker, error) {\n\texe, err := os.Executable()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif IsTesting() {\n\t\tif exe, err = filepath.Abs(\"../../kitty/launcher/kitten\"); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tcmd := exec.Command(exe, \"__atexit__\")\n\tcmd.Stdout = nil\n\tcmd.Stderr = os.Stderr\n\tans := worker{cmd: cmd}\n\tsi, err := cmd.StdinPipe()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tans.stdin_pipe = si\n\tif err = cmd.Start(); err != nil {\n\t\treturn nil, err\n\t}\n\tworker_started.Store(true)\n\treturn &ans, nil\n})\n\nfunc WaitForAtexitWorkerToFinish() error {\n\tif worker_started.Load() {\n\t\tif w, err := get_worker(); err == nil {\n\t\t\tw.stdin_pipe.Close()\n\t\t\treturn w.cmd.Wait()\n\t\t} else {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc register(prefix, path string) error {\n\t// no atexit cleanup is done as we dont have a good place to run\n\t// WaitForAtexitWorkerToFinish() and anyway we may want to run tests in\n\t// parallel, etc.\n\tif IsTesting() {\n\t\treturn nil\n\t}\n\tpath, err := filepath.Abs(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif w, err := get_worker(); err == nil {\n\t\t_, err = fmt.Fprintln(w.stdin_pipe, prefix+\" \"+path)\n\t\treturn err\n\t} else {\n\t\treturn err\n\t}\n\n}\n\nfunc AtExitUnlink(path string) error {\n\treturn register(\"unlink\", path)\n}\n\nfunc AtExitShmUnlink(path string) error {\n\treturn register(\"shm_unlink\", path)\n}\n\nfunc AtExitRmtree(path string) error {\n\treturn register(\"rmtree\", path)\n}\n"
  },
  {
    "path": "tools/utils/atomic-write.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\nvar _ = fmt.Print\n\nfunc AtomicCreateSymlink(oldname, newname string) (err error) {\n\terr = os.Symlink(oldname, newname)\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif !errors.Is(err, fs.ErrExist) {\n\t\treturn err\n\t}\n\tif et, err := os.Readlink(newname); err == nil && et == oldname {\n\t\treturn nil\n\t}\n\tfor {\n\t\ttempname := newname + RandomFilename()\n\t\terr = os.Symlink(oldname, tempname)\n\t\tif err == nil {\n\t\t\terr = os.Rename(tempname, newname)\n\t\t\tif err != nil {\n\t\t\t\tos.Remove(tempname)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif !errors.Is(err, fs.ErrExist) {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\nfunc AtomicWriteFile(path string, data io.Reader, perm os.FileMode) (err error) {\n\tnpath, err := filepath.EvalSymlinks(path)\n\tif errors.Is(err, fs.ErrNotExist) {\n\t\terr = nil\n\t\tnpath = path\n\t}\n\tif err == nil {\n\t\tpath = npath\n\t\tpath, err = filepath.Abs(path)\n\t\tif err == nil {\n\t\t\tvar f *os.File\n\t\t\tf, err = os.CreateTemp(filepath.Dir(path), filepath.Base(path)+\".atomic-write-\")\n\t\t\tif err == nil {\n\t\t\t\tremoved := false\n\t\t\t\tdefer func() {\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\terr = f.Close()\n\t\t\t\t\t} else {\n\t\t\t\t\t\tf.Close()\n\t\t\t\t\t}\n\t\t\t\t\tif !removed {\n\t\t\t\t\t\tos.Remove(f.Name())\n\t\t\t\t\t\tremoved = true\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t\tif _, err = io.Copy(f, data); err == nil {\n\t\t\t\t\tif err = f.Chmod(perm); err == nil {\n\t\t\t\t\t\tif err = f.Sync(); err == nil { // Sync before rename to ensure we dont end up with a zero sized file\n\t\t\t\t\t\t\tif err = os.Rename(f.Name(), path); err == nil {\n\t\t\t\t\t\t\t\tremoved = true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc AtomicUpdateFile(path string, data io.Reader, perms ...fs.FileMode) (err error) {\n\tperm := fs.FileMode(0o644)\n\tif len(perms) > 0 {\n\t\tperm = perms[0]\n\t}\n\ts, err := os.Stat(path)\n\tif err == nil {\n\t\tperm = s.Mode().Perm()\n\t}\n\treturn AtomicWriteFile(path, data, perm)\n}\n"
  },
  {
    "path": "tools/utils/base85/base85.go",
    "content": "// package base85\n// This package provides a RFC1924 implementation of base85 encoding.\n//\n// See http://www.ietf.org/rfc/rfc1924.txt\n// Based on: https://pkg.go.dev/github.com/jamesruan/go-rfc1924\npackage base85\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"io\"\n\t\"strconv\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\n// 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~\nvar encode = [85]byte{\n\t// 0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F\n\t0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, //0\n\t0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, //1\n\t0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, //2\n\t0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x21, 0x23, //3\n\t0x24, 0x25, 0x26, 0x28, 0x29, 0x2A, 0x2B, 0x2D, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x5E, 0x5F, //4\n\t0x60, 0x7B, 0x7C, 0x7D, 0x7E,\n}\n\nvar decoder_array = sync.OnceValue(func() *[256]byte {\n\tvar decode = [256]byte{\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t}\n\tfor i := range len(encode) {\n\t\tdecode[encode[i]] = byte(i)\n\t}\n\treturn &decode\n\n})\n\n// encodeChunk encodes 4 byte-chunk to 5 byte\n// if chunk size is less then 4, then it is padded before encoding.\n// return written bytes\nfunc encodeChunk(dst, src []byte) int {\n\tif len(src) == 0 {\n\t\treturn 0\n\t}\n\n\t//read 4 byte as big-endian uint32 into small endian uint32\n\tvar val uint32\n\tswitch len(src) {\n\tdefault:\n\t\tval |= uint32(src[3])\n\t\tfallthrough\n\tcase 3:\n\t\tval |= uint32(src[2]) << 8\n\t\tfallthrough\n\tcase 2:\n\t\tval |= uint32(src[1]) << 16\n\t\tfallthrough\n\tcase 1:\n\t\tval |= uint32(src[0]) << 24\n\t}\n\n\tbuf := [5]byte{0, 0, 0, 0, 0}\n\n\tfor i := 4; i >= 0; i-- {\n\t\tr := val % 85\n\t\tval /= 85\n\t\tbuf[i] = encode[r]\n\t}\n\n\tm := EncodedLen(len(src))\n\tcopy(dst[:], buf[:m])\n\treturn m\n}\n\nvar decode_base = [5]uint32{85 * 85 * 85 * 85, 85 * 85 * 85, 85 * 85, 85, 1}\n\n// decodeChunk decodes 5 byte-chunk to 4 byte\n// if chunk size is less then 5, then it is padded before convertion.\n// return written bytes and error input index\nfunc decodeChunk(decode *[256]byte, dst, src []byte) (int, int) {\n\tif len(src) == 0 {\n\t\treturn 0, 0\n\t}\n\tvar val uint32\n\tm := DecodedLen(len(src))\n\tbuf := [5]byte{84, 84, 84, 84, 84}\n\tfor i := range src {\n\t\te := decode[src[i]]\n\t\tif e == 0xFF {\n\t\t\treturn 0, i + 1\n\t\t}\n\t\tbuf[i] = e\n\t}\n\n\tfor i := range 5 {\n\t\tr := buf[i]\n\t\tval += uint32(r) * decode_base[i]\n\t}\n\t//small endian uint32 to big endian uint32 in bytes\n\tswitch m {\n\tdefault:\n\t\tdst[3] = byte(val & 0xff)\n\t\tfallthrough\n\tcase 3:\n\t\tdst[2] = byte((val >> 8) & 0xff)\n\t\tfallthrough\n\tcase 2:\n\t\tdst[1] = byte((val >> 16) & 0xff)\n\t\tfallthrough\n\tcase 1:\n\t\tdst[0] = byte((val >> 24) & 0xff)\n\t}\n\treturn m, 0\n}\n\n// Encode encodes src into dst, return the bytes written\n// The dst must have size of EncodedLen(len(src))\nfunc Encode(dst, src []byte) int {\n\tn := 0\n\tfor len(src) > 0 {\n\t\tif len(src) < 4 {\n\t\t\tn += encodeChunk(dst, src)\n\t\t\treturn n\n\t\t}\n\t\tn += encodeChunk(dst[:5], src[:4])\n\t\tsrc = src[4:]\n\t\tdst = dst[5:]\n\t}\n\treturn n\n}\n\n// EncodeToString returns the base85 encoding of src.\nfunc EncodeToString(src []byte) string {\n\tbuf := make([]byte, EncodedLen(len(src)))\n\tEncode(buf, src)\n\treturn utils.UnsafeBytesToString(buf)\n}\n\n// DecodeString returns the bytes represented by the base85 string s.\nfunc DecodeString(src string) ([]byte, error) {\n\tbuf := make([]byte, DecodedLen(len(src)))\n\t_, err := Decode(buf, utils.UnsafeStringToBytes(src))\n\treturn buf, err\n}\n\n// Decode decodes src into dst, return the bytes written\n// The dst must have size of DecodedLen(len(src))\n// An CorruptInputError is returned when invalid character is found in src.\nfunc Decode(dst, src []byte) (int, error) {\n\tf := 0\n\tt := 0\n\tdecode := decoder_array()\n\tfor len(src) > 0 {\n\t\tif len(src) < 5 {\n\t\t\tw, err := decodeChunk(decode, dst, src)\n\t\t\tif err > 0 {\n\t\t\t\treturn t, CorruptInputError(f + err)\n\t\t\t}\n\t\t\treturn t + w, nil\n\t\t}\n\n\t\t_, err := decodeChunk(decode, dst[:4], src[:5])\n\t\tif err > 0 {\n\t\t\treturn t, CorruptInputError(f + err)\n\t\t} else {\n\t\t\tt += 4\n\t\t\tf += 5\n\t\t\tsrc = src[5:]\n\t\t\tdst = dst[4:]\n\t\t}\n\t}\n\treturn t, nil\n}\n\n// EncodedLen returns the length in bytes of the base85 encoding of an input\n// buffer of length n.\nfunc EncodedLen(n int) int {\n\ts := n / 4\n\tr := n % 4\n\tif r > 0 {\n\t\treturn s*5 + 5 - (4 - r)\n\t} else {\n\t\treturn s * 5\n\t}\n}\n\n// DecodedLen returns the maximum length in bytes of the decoded data\n// corresponding to n bytes of base85-encoded data.\nfunc DecodedLen(n int) int {\n\ts := n / 5\n\tr := n % 5\n\tif r > 0 {\n\t\treturn s*4 + 4 - (5 - r)\n\t} else {\n\t\treturn s * 4\n\t}\n}\n\ntype encoder struct {\n\tw       io.Writer\n\tbufin   [4]byte\n\tencoded [5]byte\n\tfill    int\n\terr     error\n}\n\nfunc (e *encoder) Write(p []byte) (n int, err error) {\n\tif e.err != nil {\n\t\treturn 0, e.err\n\t}\n\n\tfor len(p) >= len(e.bufin)-e.fill {\n\t\t//copy len(e.buf) - fill bytes into e.buf to make it full\n\t\tto_copy := len(e.bufin) - e.fill\n\t\tcopy(e.bufin[e.fill:], p[:to_copy])\n\t\tp = p[to_copy:]\n\n\t\t//write the encoded whole buffer\n\t\tencodeChunk(e.encoded[:], e.bufin[:])\n\t\t_, e.err = e.w.Write(e.encoded[:])\n\t\tif e.err != nil {\n\t\t\treturn n, e.err\n\t\t}\n\t\tn += 4\n\t\te.fill = 0\n\t}\n\tfor i := 0; i < len(p); i++ {\n\t\te.bufin[e.fill] = p[i]\n\t\te.fill += 1\n\t}\n\treturn n, e.w.(*bufio.Writer).Flush()\n}\n\nfunc (e *encoder) Close() error {\n\tif e.err == nil && e.fill > 0 {\n\t\tm := EncodedLen(e.fill)\n\t\tencodeChunk(e.encoded[:m], e.bufin[:e.fill])\n\t\t_, e.err = e.w.Write(e.encoded[:m])\n\t\tif e.err != nil {\n\t\t\treturn e.err\n\t\t}\n\t\te.err = e.w.(*bufio.Writer).Flush()\n\t}\n\terr := e.err\n\te.err = errors.New(\"encoder closed\")\n\treturn err\n}\n\n// NewEncoder returns a stream encoder of w.\n// All write to the encoder is encoded into base85 and write to w.\n// The writer should call Close() to indicate the end of stream\nfunc NewEncoder(w io.Writer) io.WriteCloser {\n\tencoder := new(encoder)\n\tencoder.w = bufio.NewWriterSize(w, 1000)\n\treturn encoder\n}\n\ntype decoder struct {\n\tr       io.Reader\n\tbufin   [1000]byte\n\tdecoded []byte\n\tfill    int\n\terr     error\n}\n\n// NewDecoder returns a stream decoder of r.\n// All read from the reader will read the base85 encoded string from r and decode it.\nfunc NewDecoder(r io.Reader) io.Reader {\n\tdecoder := new(decoder)\n\tdecoder.r = bufio.NewReaderSize(r, 1000)\n\treturn decoder\n}\n\nfunc (d *decoder) Read(p []byte) (n int, err error) {\n\tif d.err != nil {\n\t\treturn 0, d.err\n\t}\n\n\tfor len(p) > 0 {\n\t\t// try filling the buffer\n\t\tm, err := d.r.Read(d.bufin[d.fill:])\n\t\td.fill += m\n\t\tif err != nil {\n\t\t\t// no further input, decode and copy into p\n\t\t\td.decoded = make([]byte, DecodedLen(d.fill))\n\t\t\tif d.err == io.EOF {\n\t\t\t\tk, err := Decode(d.decoded, d.bufin[:d.fill])\n\t\t\t\tcopy(p, d.decoded[:k])\n\t\t\t\tn += k\n\t\t\t\td.fill -= EncodedLen(k)\n\t\t\t\tif err != nil {\n\t\t\t\t\td.err = err\n\t\t\t\t\treturn n, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tk, err := Decode(d.decoded, d.bufin[:d.fill-d.fill%5])\n\t\t\t\tcopy(p, d.decoded[:k])\n\t\t\t\tn += k\n\t\t\t\td.fill -= EncodedLen(k)\n\t\t\t\tif err != nil {\n\t\t\t\t\td.err = err\n\t\t\t\t\treturn n, err\n\t\t\t\t}\n\t\t\t}\n\t\t\td.err = err\n\t\t\treturn n, d.err\n\t\t}\n\t\t//decode d.fill - d.fill % 5 byte of d.bufin\n\t\tchunked_max := d.fill\n\t\td.fill = d.fill % 5\n\t\tchunked_max -= d.fill\n\t\td.decoded = make([]byte, DecodedLen(chunked_max))\n\t\tk, err := Decode(d.decoded, d.bufin[:chunked_max])\n\t\tcopy(p, d.decoded[:k])\n\t\tp = p[k:]\n\t\tn += k\n\t\tif err != nil {\n\t\t\td.err = err\n\t\t\treturn n, d.err\n\t\t}\n\t}\n\treturn n, d.err\n}\n\ntype CorruptInputError int64\n\nfunc (e CorruptInputError) Error() string {\n\treturn \"illegal base85 data at input byte \" + strconv.FormatInt(int64(e), 10)\n}\n"
  },
  {
    "path": "tools/utils/base85/base85_test.go",
    "content": "package base85\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\ntype pair struct {\n\tsrc, encoded string\n}\n\nfunc TestBase85(t *testing.T) {\n\tdst, dst2 := [8]byte{}, [8]byte{}\n\tdecode := decoder_array()\n\tvar el int\n\tfor _, x := range []string{\"M\", \"Ma\", \"Man\", \"Man \"} {\n\t\ta := []byte(x)\n\t\tif el = encodeChunk(dst[:], a); el != EncodedLen(len(a)) {\n\t\t\tt.Fatalf(\"Encoded len for %#v wrong: %d != %d\", x, el, EncodedLen(len(a)))\n\t\t}\n\t\tencoded := dst[:el]\n\t\tdl, bad_idx := decodeChunk(decode, dst2[:], encoded)\n\t\tif bad_idx != 0 {\n\t\t\tt.Fatalf(\"Decode for %#v returned bad data at: %d (%#v)\", x, bad_idx, encoded)\n\t\t}\n\t\tif dl != DecodedLen(len(encoded)) {\n\t\t\tt.Fatalf(\"Decoded len for %#v wrong: %d != %d\", x, dl, DecodedLen(len(a)))\n\t\t}\n\t\tdecoded := string(dst2[:dl])\n\t\tif diff := cmp.Diff(x, decoded); diff != \"\" {\n\t\t\tt.Fatalf(\"Roundtrip failed for %#v: %s\", x, diff)\n\t\t}\n\t}\n\tfor _, p := range []pair{\n\t\t{\"M\", \"O#\"},\n\t\t{\"Manual\", \"O<`_zVQc\"},\n\t\t{\n\t\t\t\"Man is distinguished, not only by his reason, but by this singular passion from \" +\n\t\t\t\t\"other animals, which is a lust of the mind, that by a perseverance of delight in \" +\n\t\t\t\t\"the continued and indefatigable generation of knowledge, exceeds the short \" +\n\t\t\t\t\"vehemence of any carnal pleasure.\",\n\t\t\t\"O<`^zX>%ZCX>)XGZfA9Ab7*B`EFf-gbRchTY<VDJc_3(Mb0BhMVRLV8EFfZabRc4RAarPHb0BkRZfA9DVR9gFVRLh7Z*CxFa&K)QZ**v7av))DX>DO_b1WctXlY|;AZc?TVIXXEb95kYW*~HEWgu;7Ze%PVbZB98AYyqSVIXj2a&u*NWpZI|V`U(3W*}r`Y-wj`bRcPNAarPDAY*TCbZKsNWn>^>Ze$>7Ze(R<VRUI{VPb4$AZKN6WpZJ3X>V>IZ)PBCZf|#NWn^b%EFfigV`XJzb0BnRWgv5CZ*p`Xc4cT~ZDnp_Wgu^6AYpEKAY);2ZeeU7aBO8^b9HiME&\",\n\t\t},\n\t} {\n\t\tq := EncodeToString([]byte(p.src))\n\t\tif diff := cmp.Diff(p.encoded, q); diff != \"\" {\n\t\t\tt.Fatalf(\"Incorrect encoding of: %#v\\n%s\", p.src, diff)\n\t\t}\n\t\tsc, err := DecodeString(q)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed to decode %#v with error: %s\", p.src, err)\n\t\t}\n\t\tif diff := cmp.Diff(p.src, string(sc)); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to roundtrip %#v\\n%s\", p.src, diff)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/utils/cache.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"container/list\"\n\t\"fmt\"\n\t\"sync\"\n)\n\nvar _ = fmt.Print\n\ntype LRUCache[K comparable, V any] struct {\n\tdata     map[K]V\n\tlock     sync.RWMutex\n\tmax_size int\n\tlru      *list.List\n}\n\nfunc NewLRUCache[K comparable, V any](max_size int) *LRUCache[K, V] {\n\tans := LRUCache[K, V]{data: map[K]V{}, max_size: max_size, lru: list.New()}\n\treturn &ans\n}\n\nfunc (self *LRUCache[K, V]) Clear() {\n\tself.lock.RLock()\n\tclear(self.data)\n\tself.lock.Unlock()\n}\n\nfunc (self *LRUCache[K, V]) Get(key K) (ans V, found bool) {\n\tself.lock.RLock()\n\tans, found = self.data[key]\n\tself.lock.RUnlock()\n\treturn\n}\n\nfunc (self *LRUCache[K, V]) Set(key K, val V) {\n\tself.lock.RLock()\n\tself.data[key] = val\n\tself.lock.RUnlock()\n\treturn\n}\n\nfunc (self *LRUCache[K, V]) GetOrCreate(key K, create func(key K) (V, error)) (V, error) {\n\tself.lock.RLock()\n\tans, found := self.data[key]\n\tself.lock.RUnlock()\n\tif found {\n\t\treturn ans, nil\n\t}\n\tans, err := create(key)\n\tif err == nil {\n\t\tself.lock.Lock()\n\t\tself.data[key] = ans\n\t\tself.lru.PushFront(key)\n\t\tif self.max_size > 0 && self.lru.Len() > self.max_size {\n\t\t\tk := self.lru.Remove(self.lru.Back())\n\t\t\tdelete(self.data, k.(K))\n\t\t}\n\t\tself.lock.Unlock()\n\t}\n\treturn ans, err\n}\n\nfunc (self *LRUCache[K, V]) MustGetOrCreate(key K, create func(key K) V) V {\n\tself.lock.RLock()\n\tans, found := self.data[key]\n\tself.lock.RUnlock()\n\tif found {\n\t\treturn ans\n\t}\n\tans = create(key)\n\tself.lock.Lock()\n\tself.data[key] = ans\n\tself.lock.Unlock()\n\treturn ans\n}\n"
  },
  {
    "path": "tools/utils/cached_values.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\nvar _ = fmt.Print\n\ntype CachedValues[T any] struct {\n\tName string\n\tOpts T\n}\n\nfunc (self *CachedValues[T]) Path() string {\n\treturn filepath.Join(CacheDir(), self.Name+\".json\")\n}\n\nfunc (self *CachedValues[T]) Load() T {\n\traw, err := os.ReadFile(self.Path())\n\tif err == nil {\n\t\tjson.Unmarshal(raw, self.Opts)\n\t}\n\treturn self.Opts\n}\n\nfunc (self *CachedValues[T]) Save() {\n\traw, err := json.Marshal(self.Opts)\n\tif err == nil {\n\t\tAtomicUpdateFile(self.Path(), bytes.NewReader(raw), 0o600)\n\t}\n}\n\nfunc NewCachedValues[T any](name string, initial_val T) *CachedValues[T] {\n\treturn &CachedValues[T]{Name: name, Opts: initial_val}\n}\n"
  },
  {
    "path": "tools/utils/clock_with_raw.go",
    "content": "//go:build linux || darwin\n\npackage utils\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc MonotonicRaw() (time.Time, error) {\n\tts := unix.Timespec{}\n\tif err := unix.ClockGettime(unix.CLOCK_MONOTONIC_RAW, &ts); err != nil {\n\t\treturn time.Time{}, err\n\t}\n\ts, ns := ts.Unix()\n\treturn time.Unix(s, ns), nil\n}\n"
  },
  {
    "path": "tools/utils/clock_without_raw.go",
    "content": "//go:build !linux && !darwin\n\npackage utils\n\nimport (\n\t\"time\"\n)\n\nfunc MonotonicRaw() (time.Time, error) {\n\treturn time.Now(), nil\n}\n"
  },
  {
    "path": "tools/utils/colors.go",
    "content": "package utils\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\nfunc RGBLuminance(r, g, b float32) float32 {\n\t// From ITU BT 601 https://www.itu.int/rec/R-REC-BT.601\n\treturn 0.299*r + 0.587*g + 0.114*b\n}\n\nfunc RGBContrast(r1, g1, b1, r2, g2, b2 float32) float32 {\n\tal := RGBLuminance(r1, g1, b1)\n\tbl := RGBLuminance(r2, g2, b2)\n\tif al < bl {\n\t\tal, bl = bl, al\n\t}\n\treturn (al + 0.05) / (bl + 0.05)\n}\n"
  },
  {
    "path": "tools/utils/download_file.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n)\n\nvar _ = fmt.Print\n\ntype ReportFunc = func(done, total uint64) error\n\ntype write_counter struct {\n\tdone, total uint64\n\treport      ReportFunc\n}\n\nfunc (self *write_counter) Write(p []byte) (int, error) {\n\tn := len(p)\n\tself.done += uint64(n)\n\tif self.report != nil {\n\t\terr := self.report(self.done, self.total)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn n, nil\n}\n\nfunc DownloadToWriter(url string, dest io.Writer, progress_callback ReportFunc) error {\n\tresp, err := http.Get(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"The server responded with the HTTP error: %s\", resp.Status)\n\t}\n\twc := write_counter{report: progress_callback}\n\tcl, err := strconv.Atoi(resp.Header.Get(\"Content-Length\"))\n\tif err == nil {\n\t\twc.total = uint64(cl)\n\t}\n\t_, err = io.Copy(dest, io.TeeReader(resp.Body, &wc))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc DownloadAsSlice(url string, progress_callback ReportFunc) (data []byte, err error) {\n\tb := bytes.Buffer{}\n\tb.Grow(4096)\n\terr = DownloadToWriter(url, &b, progress_callback)\n\tif err == nil {\n\t\treturn b.Bytes(), nil\n\t}\n\treturn nil, err\n}\n\nfunc DownloadToFile(destpath, url string, progress_callback ReportFunc, temp_file_path_callback func(string)) error {\n\tdestpath, err := filepath.EvalSymlinks(destpath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdest, err := os.CreateTemp(filepath.Dir(destpath), filepath.Base(destpath)+\".partial-download.\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif temp_file_path_callback != nil {\n\t\ttemp_file_path_callback(dest.Name())\n\t}\n\tdest_removed := false\n\tdefer func() {\n\t\tdest.Close()\n\t\tif !dest_removed {\n\t\t\tos.Remove(dest.Name())\n\t\t}\n\t}()\n\terr = DownloadToWriter(url, dest, progress_callback)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdest.Close()\n\tfi, err := os.Stat(destpath)\n\tif err == nil {\n\t\terr = os.Chmod(dest.Name(), fi.Mode().Perm())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Rename(dest.Name(), destpath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdest_removed = true\n\treturn nil\n}\n"
  },
  {
    "path": "tools/utils/embed.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"bytes\"\n\t\"compress/bzip2\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n)\n\nvar _ = fmt.Print\n\nfunc ReadAll(r io.Reader, expected_size int) ([]byte, error) {\n\tb := make([]byte, 0, expected_size)\n\tfor {\n\t\tif len(b) == cap(b) {\n\t\t\t// Add more capacity (let append pick how much).\n\t\t\tb = append(b, 0)[:len(b)]\n\t\t}\n\t\tn, err := r.Read(b[len(b):cap(b)])\n\t\tb = b[:len(b)+n]\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\treturn b, err\n\t\t}\n\t}\n}\n\nfunc ReadCompressedEmbeddedData(raw string) []byte {\n\tcompressed := UnsafeStringToBytes(raw)\n\tuncompressed_size := binary.LittleEndian.Uint32(compressed)\n\tr := bzip2.NewReader(bytes.NewReader(compressed[4:]))\n\tans, err := ReadAll(r, int(uncompressed_size))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn ans\n}\n\nfunc ReaderForCompressedEmbeddedData(raw string) io.Reader {\n\treturn bzip2.NewReader(bytes.NewReader(UnsafeStringToBytes(raw)[4:]))\n}\n"
  },
  {
    "path": "tools/utils/filelock.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc lock(fd, op int, path string) (err error) {\n\tfor {\n\t\terr = unix.Flock(fd, op)\n\t\tif !errors.Is(err, unix.EINTR) {\n\t\t\tbreak\n\t\t}\n\t}\n\tif err != nil {\n\t\topname := \"exclusive flock()\"\n\t\tswitch op {\n\t\tcase unix.LOCK_UN:\n\t\t\topname = \"unlock flock()\"\n\t\tcase unix.LOCK_SH:\n\t\t\topname = \"shared flock()\"\n\t\t}\n\t\treturn &fs.PathError{\n\t\t\tOp:   opname,\n\t\t\tPath: path,\n\t\t\tErr:  err,\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc LockFileShared(f *os.File) error {\n\treturn lock(int(f.Fd()), unix.LOCK_SH, f.Name())\n}\n\nfunc LockFileExclusive(f *os.File) error {\n\treturn lock(int(f.Fd()), unix.LOCK_EX, f.Name())\n}\n\nfunc UnlockFile(f *os.File) error {\n\treturn lock(int(f.Fd()), unix.LOCK_UN, f.Name())\n}\n"
  },
  {
    "path": "tools/utils/filelock_test.go",
    "content": "// License: GPLv3 Copyright: 2024, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestFileLock(t *testing.T) {\n\ttdir := t.TempDir()\n\n\tfile_descriptor, err := os.Open(tdir)\n\tif err != nil {\n\t\tt.Fatalf(\"Initial open of %s failed with error: %s\", tdir, err)\n\t}\n\tif err = LockFileExclusive(file_descriptor); err != nil {\n\t\tfile_descriptor.Close()\n\t\tt.Fatalf(\"Initial lock of %s failed with error: %s\", tdir, err)\n\t}\n\tdefer func() {\n\t\t_ = UnlockFile(file_descriptor)\n\t\tfile_descriptor.Close()\n\t}()\n\tcmd := exec.Command(KittyExe(), \"+runpy\", `\nimport sys, os, fcntl\nfd = os.open(sys.argv[-1], os.O_RDONLY)\ntry:\n\tfcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)\nexcept BlockingIOError:\n    sys.exit(0)\nelse:\n\tprint(\"Lock unexpectedly succeeded\", flush=True)\n\tsys.exit(1)\n`, tdir)\n\tif output, err := cmd.CombinedOutput(); err != nil {\n\t\tt.Fatalf(\"Lock test process failed with error: %s and output:\\n%s\", err, string(output))\n\t}\n}\n"
  },
  {
    "path": "tools/utils/hostname.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n)\n\nvar _ = fmt.Print\n\nvar hostname string = \"*\"\n\nvar Hostname = sync.OnceValue(func() string {\n\th, err := os.Hostname()\n\tif err == nil {\n\t\treturn h\n\t}\n\treturn \"\"\n})\n"
  },
  {
    "path": "tools/utils/humanize/bytes.go",
    "content": "package humanize\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"golang.org/x/exp/constraints\"\n)\n\n// IEC Sizes.\n// kibis of bits\nconst (\n\tByte = 1 << (iota * 10)\n\tKiByte\n\tMiByte\n\tGiByte\n\tTiByte\n\tPiByte\n\tEiByte\n)\n\n// SI Sizes.\nconst (\n\tIByte = 1\n\tKByte = IByte * 1000\n\tMByte = KByte * 1000\n\tGByte = MByte * 1000\n\tTByte = GByte * 1000\n\tPByte = TByte * 1000\n\tEByte = PByte * 1000\n)\n\nfunc logn(n, b float64) float64 {\n\treturn math.Log(n) / math.Log(b)\n}\n\nfunc humanize_bytes(s uint64, base float64, sizes []string, sep string) string {\n\tif s < 10 {\n\t\treturn fmt.Sprintf(\"%d%sB\", s, sep)\n\t}\n\te := math.Floor(logn(float64(s), base))\n\tsuffix := sizes[int(e)]\n\tval := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10\n\tf := \"%.0f%s%s\"\n\tif val < 10 {\n\t\tf = \"%.1f%s%s\"\n\t}\n\treturn fmt.Sprintf(f, val, sep, suffix)\n}\n\n// Bytes produces a human readable representation of an SI size.\n// Bytes(82854982) -> 83 MB\nfunc Bytes(s uint64) string {\n\treturn Size(s, SizeOptions{})\n}\n\n// IBytes produces a human readable representation of an IEC size.\n// IBytes(82854982) -> 79 MiB\nfunc IBytes(s uint64) string {\n\treturn Size(s, SizeOptions{Base: 1024})\n}\n\ntype SizeOptions struct {\n\tSeparator string\n\tBase      int\n}\n\nfunc Size[T constraints.Integer | constraints.Float](s T, opts ...SizeOptions) string {\n\tvar o SizeOptions\n\tprefix := \"\"\n\tif len(opts) == 0 {\n\t\to = SizeOptions{}\n\t} else {\n\t\to = opts[0]\n\t}\n\tif s < 0 {\n\t\tprefix = \"-\"\n\t}\n\tif o.Separator == \"\" {\n\t\to.Separator = \" \"\n\t}\n\tif o.Base == 0 {\n\t\to.Base = 1000\n\t}\n\tvar sizes []string\n\tswitch o.Base {\n\tdefault:\n\t\tsizes = []string{\"B\", \"kB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\"}\n\tcase 1024:\n\t\tsizes = []string{\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\"}\n\t}\n\treturn prefix + humanize_bytes(uint64(s), float64(o.Base), sizes, o.Separator)\n}\n\nfunc FormatNumber[T constraints.Float](n T, max_num_of_decimals ...int) string {\n\tprec := 2\n\tif len(max_num_of_decimals) > 0 {\n\t\tprec = max_num_of_decimals[0]\n\t}\n\tans := strconv.FormatFloat(float64(n), 'f', prec, 64)\n\treturn strings.TrimRight(strings.TrimRight(ans, \"0\"), \".\")\n}\n"
  },
  {
    "path": "tools/utils/humanize/times.go",
    "content": "package humanize\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n\t\"math\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Seconds-based time units\nconst (\n\tDay      = 24 * time.Hour\n\tWeek     = 7 * Day\n\tMonth    = 30 * Day\n\tYear     = 12 * Month\n\tLongTime = 37 * Year\n)\n\n// Time formats a time into a relative string.\n//\n// Time(someT) -> \"3 weeks ago\"\nfunc Time(then time.Time) string {\n\treturn RelTime(then, time.Now(), \"ago\", \"from now\")\n}\n\n// A RelTimeMagnitude struct contains a relative time point at which\n// the relative format of time will switch to a new format string.  A\n// slice of these in ascending order by their \"D\" field is passed to\n// CustomRelTime to format durations.\n//\n// The Format field is a string that may contain a \"%s\" which will be\n// replaced with the appropriate signed label (e.g. \"ago\" or \"from\n// now\") and a \"%d\" that will be replaced by the quantity.\n//\n// The DivBy field is the amount of time the time difference must be\n// divided by in order to display correctly.\n//\n// e.g. if D is 2*time.Minute and you want to display \"%d minutes %s\"\n// DivBy should be time.Minute so whatever the duration is will be\n// expressed in minutes.\ntype RelTimeMagnitude struct {\n\tD      time.Duration\n\tFormat string\n\tDivBy  time.Duration\n}\n\nvar defaultMagnitudes = []RelTimeMagnitude{\n\t{time.Second, \"now\", time.Second},\n\t{2 * time.Second, \"1 second %s\", 1},\n\t{time.Minute, \"%d seconds %s\", time.Second},\n\t{2 * time.Minute, \"1 minute %s\", 1},\n\t{time.Hour, \"%d minutes %s\", time.Minute},\n\t{2 * time.Hour, \"1 hour %s\", 1},\n\t{Day, \"%d hours %s\", time.Hour},\n\t{2 * Day, \"1 day %s\", 1},\n\t{Week, \"%d days %s\", Day},\n\t{2 * Week, \"1 week %s\", 1},\n\t{Month, \"%d weeks %s\", Week},\n\t{2 * Month, \"1 month %s\", 1},\n\t{Year, \"%d months %s\", Month},\n\t{18 * Month, \"1 year %s\", 1},\n\t{2 * Year, \"2 years %s\", 1},\n\t{LongTime, \"%d years %s\", Year},\n\t{math.MaxInt64, \"a long while %s\", 1},\n}\n\n// RelTime formats a time into a relative string.\n//\n// It takes two times and two labels.  In addition to the generic time\n// delta string (e.g. 5 minutes), the labels are used applied so that\n// the label corresponding to the smaller time is applied.\n//\n// RelTime(timeInPast, timeInFuture, \"earlier\", \"later\") -> \"3 weeks earlier\"\nfunc RelTime(a, b time.Time, albl, blbl string) string {\n\treturn CustomRelTime(a, b, albl, blbl, defaultMagnitudes)\n}\n\n// CustomRelTime formats a time into a relative string.\n//\n// It takes two times two labels and a table of relative time formats.\n// In addition to the generic time delta string (e.g. 5 minutes), the\n// labels are used applied so that the label corresponding to the\n// smaller time is applied.\nfunc CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string {\n\tlbl := albl\n\tdiff := b.Sub(a)\n\n\tif a.After(b) {\n\t\tlbl = blbl\n\t\tdiff = a.Sub(b)\n\t}\n\n\tn := sort.Search(len(magnitudes), func(i int) bool {\n\t\treturn magnitudes[i].D > diff\n\t})\n\n\tif n >= len(magnitudes) {\n\t\tn = len(magnitudes) - 1\n\t}\n\tmag := magnitudes[n]\n\targs := []any{}\n\tescaped := false\n\tfor _, ch := range mag.Format {\n\t\tif escaped {\n\t\t\tswitch ch {\n\t\t\tcase 's':\n\t\t\t\targs = append(args, lbl)\n\t\t\tcase 'd':\n\t\t\t\targs = append(args, diff/mag.DivBy)\n\t\t\t}\n\t\t\tescaped = false\n\t\t} else {\n\t\t\tescaped = ch == '%'\n\t\t}\n\t}\n\treturn fmt.Sprintf(mag.Format, args...)\n}\n\nfunc optional_cut(x string, sep string) (string, string) {\n\ta, b, found := strings.Cut(x, sep)\n\tif found {\n\t\treturn a, b\n\t}\n\treturn \"00\", a\n}\n\nfunc zero_pad(x string) string {\n\tif len(x) < 2 {\n\t\tx = strings.Repeat(\"0\", 2-len(x)) + x\n\t}\n\treturn x\n}\n\n// Render the duration in exactly 8 visual chars\nfunc ShortDuration(val time.Duration) (ans string) {\n\tif val >= time.Second {\n\t\tif val > Day {\n\t\t\tdays := int(val.Hours() / 24)\n\t\t\tif days > 99 {\n\t\t\t\tans = `∞`\n\t\t\t} else {\n\t\t\t\tif days == 1 {\n\t\t\t\t\tans = \">1 day\"\n\t\t\t\t} else {\n\t\t\t\t\tans = fmt.Sprintf(\">%d days\", int(days))\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tans = val.String()\n\t\t\thr, rest := optional_cut(ans, `h`)\n\t\t\tmin, rest := optional_cut(rest, `m`)\n\t\t\tsecs, _, _ := strings.Cut(rest, \".\")\n\t\t\tsecs = strings.Replace(secs, `s`, ``, 1)\n\t\t\tans = zero_pad(hr) + `:` + zero_pad(min) + `:` + zero_pad(secs)\n\t\t}\n\t} else {\n\t\tans = \"<1 sec\"\n\t}\n\tif w := wcswidth.Stringwidth(ans); w < 8 {\n\t\tans = strings.Repeat(\" \", 8-w) + ans\n\t} else if w > 8 {\n\t\tans = wcswidth.TruncateToVisualLength(ans, 8)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tools/utils/humanize/times_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage humanize\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestShortDuration(t *testing.T) {\n\tq := func(i float64, e string) {\n\t\td := time.Duration(i * float64(time.Second))\n\t\tif diff := cmp.Diff(e, ShortDuration(d)); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed for %f (%s): %s\", i, d, diff)\n\t\t}\n\t}\n\tq(0.1, \"  <1 sec\")\n\tq(1, `00:00:01`)\n\tq(1.1234567, `00:00:01`)\n\tq(60.1234567, `00:01:00`)\n\tq(61.1234567, `00:01:01`)\n\tq(3600, `01:00:00`)\n\tq(3601.1234567, `01:00:01`)\n\tday := 24. * 3600.\n\tq(day, \"24:00:00\")\n\tq(day+1, \"  >1 day\")\n\tq(day*2, \" >2 days\")\n\tq(day*23, \">23 days\")\n\tq(day*999, \"       ∞\")\n}\n"
  },
  {
    "path": "tools/utils/images/convert.go",
    "content": "package images\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"image\"\n\t\"io\"\n\t\"os\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/imaging\"\n\t\"github.com/kovidgoyal/kitty/tools/cli\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\nfunc encode_rgba(output io.Writer, img image.Image) (err error) {\n\tvar final_img *image.NRGBA\n\tswitch ti := img.(type) {\n\tcase *image.NRGBA:\n\t\tfinal_img = ti\n\tdefault:\n\t\tb := img.Bounds()\n\t\tfinal_img = image.NewNRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))\n\t\tctx := Context{}\n\t\tctx.PasteCenter(final_img, img, nil)\n\t}\n\tb := final_img.Bounds()\n\theader := make([]byte, 8)\n\tvar width = utils.Abs(b.Dx())\n\tvar height = utils.Abs(b.Dy())\n\tbinary.LittleEndian.PutUint32(header, uint32(width))\n\tbinary.LittleEndian.PutUint32(header[4:], uint32(height))\n\treaders := []io.Reader{bytes.NewReader(header)}\n\tstride := 4 * width\n\n\tif final_img.Stride == stride {\n\t\treaders = append(readers, bytes.NewReader(final_img.Pix))\n\t} else {\n\t\tp := final_img.Pix\n\t\tfor y := 0; y < b.Dy(); y++ {\n\t\t\treaders = append(readers, bytes.NewReader(p[:min(stride, len(p))]))\n\t\t\tp = p[final_img.Stride:]\n\t\t}\n\t}\n\t_, err = io.Copy(output, io.MultiReader(readers...))\n\treturn\n}\n\nfunc convert_image(input io.ReadSeeker, output io.Writer, format string) (err error) {\n\timg, err := imaging.Decode(input)\n\tif err != nil {\n\t\treturn err\n\t}\n\tq := strings.ToLower(format)\n\tif q == \"rgba\" {\n\t\treturn encode_rgba(output, img)\n\t}\n\tmt := utils.GuessMimeType(\"file.\" + q)\n\tif mt == \"\" {\n\t\treturn fmt.Errorf(\"Unknown image output format: %s\", format)\n\t}\n\treturn Encode(output, img, mt)\n}\n\nfunc images_equal(img, rimg *ImageData) (err error) {\n\tfor i := range img.Frames {\n\t\ta, b := img.Frames[i], rimg.Frames[i]\n\t\tif a.Img.Bounds() != b.Img.Bounds() {\n\t\t\treturn fmt.Errorf(\"bounds of frame %d not equal: %v != %v\", i, a.Img.Bounds(), b.Img.Bounds())\n\t\t}\n\t\tfor y := a.Img.Bounds().Min.Y; y < a.Img.Bounds().Max.Y; y++ {\n\t\t\tfor x := a.Img.Bounds().Min.X; x < a.Img.Bounds().Max.X; x++ {\n\t\t\t\tor, og, ob, oa := a.Img.At(x, y).RGBA()\n\t\t\t\tnr, ng, nb, na := b.Img.At(x, y).RGBA()\n\t\t\t\ta, b := []uint32{or, og, ob, oa}, []uint32{nr, ng, nb, na}\n\t\t\t\tif !slices.Equal(a, b) {\n\t\t\t\t\treturn fmt.Errorf(\"pixel at %dx%d differs: %v != %v\", x, y, a, b)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\treturn\n}\n\nfunc develop_serialize(input_data io.ReadSeeker) (err error) {\n\timg, _, err := OpenImageFromReader(input_data)\n\tif err != nil {\n\t\treturn err\n\t}\n\tm, b := img.Serialize()\n\trimg, err := ImageFromSerialized(m, b)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn images_equal(img, rimg)\n}\n\nfunc develop_resize(spec string, input_data io.ReadSeeker) (err error) {\n\tws, hs, _ := strings.Cut(spec, \"x\")\n\tvar w, h int\n\tif w, err = strconv.Atoi(ws); err != nil {\n\t\treturn\n\t}\n\tif h, err = strconv.Atoi(hs); err != nil {\n\t\treturn\n\t}\n\timg, _, err := OpenImageFromReader(input_data)\n\tif err != nil {\n\t\treturn err\n\t}\n\taimg := img.Resize(float64(w)/float64(img.Width), float64(h)/float64(img.Height))\n\tm, b := img.Serialize()\n\trimg, err := ImageFromSerialized(m, b)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = images_equal(img, rimg); err != nil {\n\t\treturn fmt.Errorf(\"roundtripped images not equal: %w\", err)\n\t}\n\tbimg := rimg.Resize(float64(w)/float64(rimg.Width), float64(h)/float64(rimg.Height))\n\treturn images_equal(aimg, bimg)\n}\n\nfunc ConvertEntryPoint(root *cli.Command) {\n\troot.AddSubCommand(&cli.Command{\n\t\tName:            \"__convert_image__\",\n\t\tHidden:          true,\n\t\tOnlyArgsAllowed: true,\n\t\tRun: func(cmd *cli.Command, args []string) (rc int, err error) {\n\t\t\tif len(args) != 1 {\n\t\t\t\treturn 1, fmt.Errorf(\"Usage: __convert_image__ OUTPUT_FORMAT\")\n\t\t\t}\n\t\t\tformat := args[0]\n\t\t\tbuf := bytes.NewBuffer(make([]byte, 0, 1024*1024))\n\t\t\tif _, err = io.Copy(buf, os.Stdin); err != nil {\n\t\t\t\treturn 1, err\n\t\t\t}\n\t\t\tinput_data := bytes.NewReader(buf.Bytes())\n\t\t\tswitch {\n\t\t\tcase format == \"develop-serialize\":\n\t\t\t\terr = develop_serialize(input_data)\n\t\t\tcase strings.HasPrefix(format, \"develop-resize-\"):\n\t\t\t\terr = develop_resize(format[len(\"develop-resize-\"):], input_data)\n\t\t\tdefault:\n\t\t\t\terr = convert_image(input_data, os.Stdout, format)\n\t\t\t}\n\t\t\trc = utils.IfElse(err == nil, 0, 1)\n\t\t\treturn\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "tools/utils/images/formats.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage images\n\nimport (\n\t\"fmt\"\n\t\"image\"\n\t\"image/gif\"\n\t\"image/jpeg\"\n\t\"image/png\"\n\t\"io\"\n\n\t\"golang.org/x/image/bmp\"\n\t\"golang.org/x/image/tiff\"\n\t_ \"golang.org/x/image/webp\"\n)\n\nvar _ = fmt.Print\n\nvar DecodableImageTypes = map[string]bool{\n\t\"image/jpeg\": true, \"image/png\": true, \"image/bmp\": true, \"image/tiff\": true, \"image/webp\": true, \"image/gif\": true,\n\t\"image/x-portable-anymap\":       true,\n\t\"image/x-portable-bitmap\":       true,\n\t\"image/x-portable-graymap\":      true,\n\t\"image/x-portable-pixmap\":       true,\n\t\"image/x-portable-arbitrarymap\": true,\n}\n\nvar EncodableImageTypes = map[string]bool{\n\t\"image/jpeg\": true, \"image/png\": true, \"image/bmp\": true, \"image/tiff\": true, \"image/gif\": true,\n}\n\nfunc Encode(output io.Writer, img image.Image, format_mime string) (err error) {\n\tswitch format_mime {\n\tcase \"image/png\":\n\t\treturn png.Encode(output, img)\n\tcase \"image/jpeg\":\n\t\treturn jpeg.Encode(output, img, nil)\n\tcase \"image/bmp\":\n\t\treturn bmp.Encode(output, img)\n\tcase \"image/gif\":\n\t\treturn gif.Encode(output, img, nil)\n\tcase \"image/tiff\":\n\t\treturn tiff.Encode(output, img, nil)\n\t}\n\terr = fmt.Errorf(\"Unsupported output image MIME type %s\", format_mime)\n\treturn\n\n}\n"
  },
  {
    "path": "tools/utils/images/loading.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage images\n\nimport (\n\t\"fmt\"\n\t\"image\"\n\t\"image/png\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n\t\"github.com/kovidgoyal/go-shm\"\n\t\"github.com/kovidgoyal/imaging/nrgb\"\n\n\t\"github.com/kovidgoyal/imaging\"\n)\n\nvar _ = fmt.Print\n\nconst TempTemplate = \"kitty-tty-graphics-protocol-*\"\n\nfunc CreateTemp() (*os.File, error) {\n\treturn os.CreateTemp(\"\", TempTemplate)\n}\n\nfunc CreateTempInRAM() (*os.File, error) {\n\tif shm.SHM_DIR != \"\" {\n\t\tf, err := os.CreateTemp(shm.SHM_DIR, TempTemplate)\n\t\tif err == nil {\n\t\t\treturn f, err\n\t\t}\n\t}\n\treturn CreateTemp()\n}\n\ntype ImageFrame struct {\n\tWidth, Height, Left, Top int\n\tNumber                   int   // 1-based number\n\tCompose_onto             int   // number of frame to compose onto\n\tDelay_ms                 int32 // negative for gapless frame, zero ignored, positive is number of ms\n\tReplace                  bool  // do a replace rather than an alpha blend\n\tIs_opaque                bool\n\tImg                      image.Image\n}\n\ntype SerializableImageFrame struct {\n\tWidth, Height, Left, Top int\n\tNumber                   int  // 1-based number\n\tCompose_onto             int  // number of frame to compose onto\n\tDelay_ms                 int  // negative for gapless frame, zero ignored, positive is number of ms\n\tReplace                  bool // do a replace rather than an alpha blend\n\tIs_opaque                bool\n\n\tSize               int // size in bytes of the serialized data\n\tNumber_of_channels int\n\tBits_per_channel   int\n\tHas_alpha_channel  bool\n}\n\nfunc (s *ImageFrame) Serialize() SerializableImageFrame {\n\treturn SerializableImageFrame{\n\t\tWidth: s.Width, Height: s.Height, Left: s.Left, Top: s.Top,\n\t\tNumber: s.Number, Compose_onto: s.Compose_onto, Delay_ms: int(s.Delay_ms),\n\t\tIs_opaque: s.Is_opaque, Replace: s.Replace,\n\t}\n}\n\nfunc (self *ImageFrame) DataAsSHM(pattern string) (ans shm.MMap, err error) {\n\t_, _, _, d := self.Data()\n\tif ans, err = shm.CreateTemp(pattern, uint64(len(d))); err != nil {\n\t\treturn nil, err\n\t}\n\tcopy(ans.Slice(), d)\n\treturn\n}\n\nfunc (self *ImageFrame) Data() (num_channels, bits_per_channel int, has_alpha_channel bool, ans []byte) {\n\tif self.Is_opaque {\n\t\treturn 3, 8, false, imaging.AsRGBData8(self.Img)\n\t}\n\treturn 4, 8, true, imaging.AsRGBAData8(self.Img)\n}\n\nfunc ImageFrameFromSerialized(s SerializableImageFrame, data []byte) (aa *ImageFrame, err error) {\n\tans := ImageFrame{\n\t\tWidth: s.Width, Height: s.Height, Left: s.Left, Top: s.Top,\n\t\tNumber: s.Number, Compose_onto: s.Compose_onto, Delay_ms: int32(s.Delay_ms),\n\t\tIs_opaque: s.Is_opaque || !s.Has_alpha_channel, Replace: s.Replace,\n\t}\n\tbpc := s.Bits_per_channel\n\tif bpc == 0 {\n\t\tbpc = 8\n\t}\n\tif bpc != 8 {\n\t\treturn nil, fmt.Errorf(\"serialized image data has unsupported number of bits per channel: %d\", bpc)\n\t}\n\tbytes_per_pixel := bpc * s.Number_of_channels / 8\n\tif expected := bytes_per_pixel * s.Width * s.Height; len(data) != expected {\n\t\treturn nil, fmt.Errorf(\"serialized image data has size: %d != %d\", len(data), expected)\n\t}\n\tswitch s.Number_of_channels {\n\tcase 3, 0:\n\t\tans.Img, err = nrgb.NewNRGBWithContiguousRGBPixels(data, s.Left, s.Top, s.Width, s.Height)\n\tcase 4:\n\t\tans.Img, err = NewNRGBAWithContiguousRGBAPixels(data, s.Left, s.Top, s.Width, s.Height)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"serialized image data has unsupported number of channels: %d\", s.Number_of_channels)\n\t}\n\treturn &ans, err\n}\n\ntype ImageData struct {\n\tWidth, Height    int\n\tFormat_uppercase string\n\tFrames           []*ImageFrame\n}\n\ntype SerializableImageMetadata struct {\n\tVersion          int\n\tWidth, Height    int\n\tFormat_uppercase string\n\tFrames           []SerializableImageFrame\n}\n\nconst SERIALIZE_VERSION = 1\n\nfunc (self *ImageFrame) SaveAsUncompressedPNG(output io.Writer) error {\n\tencoder := png.Encoder{CompressionLevel: png.NoCompression}\n\treturn encoder.Encode(output, self.Img)\n}\n\nfunc (self *ImageData) SerializeOnlyMetadata() SerializableImageMetadata {\n\tf := make([]SerializableImageFrame, len(self.Frames))\n\tfor i, s := range self.Frames {\n\t\tf[i] = s.Serialize()\n\t}\n\treturn SerializableImageMetadata{Version: SERIALIZE_VERSION, Width: self.Width, Height: self.Height, Format_uppercase: self.Format_uppercase, Frames: f}\n}\n\nfunc (self *ImageData) Serialize() (SerializableImageMetadata, [][]byte) {\n\tm := self.SerializeOnlyMetadata()\n\tdata := make([][]byte, len(self.Frames))\n\tfor i, f := range self.Frames {\n\t\tdf := &m.Frames[i]\n\t\tdf.Number_of_channels, df.Bits_per_channel, df.Has_alpha_channel, data[i] = f.Data()\n\t\tdf.Size = len(data[i])\n\t\tif !df.Has_alpha_channel {\n\t\t\tdf.Is_opaque = true\n\t\t}\n\t}\n\treturn m, data\n}\n\nfunc ImageFromSerialized(m SerializableImageMetadata, data [][]byte) (*ImageData, error) {\n\tif m.Version > SERIALIZE_VERSION {\n\t\treturn nil, fmt.Errorf(\"serialized image data has unsupported version: %d\", m.Version)\n\t}\n\tif len(m.Frames) != len(data) {\n\t\treturn nil, fmt.Errorf(\"serialized image data has %d frames in metadata but have data for: %d\", len(m.Frames), len(data))\n\t}\n\tans := ImageData{\n\t\tWidth: m.Width, Height: m.Height, Format_uppercase: m.Format_uppercase,\n\t}\n\tfor i, f := range m.Frames {\n\t\tif ff, err := ImageFrameFromSerialized(f, data[i]); err != nil {\n\t\t\treturn nil, err\n\t\t} else {\n\t\t\tans.Frames = append(ans.Frames, ff)\n\t\t}\n\t}\n\treturn &ans, nil\n}\n\nfunc (ans ImageFrame) Resize(x_frac, y_frac float64) *ImageFrame {\n\tans.Width = int(x_frac * float64(ans.Width))\n\tans.Height = int(y_frac * float64(ans.Height))\n\tans.Img = imaging.ResizeWithOpacity(ans.Img, ans.Width, ans.Height, imaging.Lanczos, ans.Is_opaque)\n\tans.Left = int(x_frac * float64(ans.Left))\n\tans.Top = int(y_frac * float64(ans.Top))\n\treturn &ans\n}\n\nfunc (self *ImageData) Resize(x_frac, y_frac float64) *ImageData {\n\tans := *self\n\tans.Frames = make([]*ImageFrame, len(self.Frames))\n\tif err := parallel.Run_in_parallel_over_range(1, func(start, limit int) {\n\t\tfor i := start; i < limit; i++ {\n\t\t\tans.Frames[i] = self.Frames[i].Resize(x_frac, y_frac)\n\t\t}\n\t}, 0, len(ans.Frames)); err != nil {\n\t\tpanic(err)\n\t}\n\tif len(ans.Frames) > 0 {\n\t\tans.Width, ans.Height = ans.Frames[0].Width, ans.Frames[0].Height\n\t}\n\treturn &ans\n}\n\nfunc MakeTempDir(template string) (ans string, err error) {\n\tif template == \"\" {\n\t\ttemplate = \"kitty-img-*\"\n\t}\n\tif shm.SHM_DIR != \"\" {\n\t\tans, err = os.MkdirTemp(shm.SHM_DIR, template)\n\t\tif err == nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn os.MkdirTemp(\"\", template)\n}\n\nfunc NewImageData(ic *imaging.Image) (ans *ImageData) {\n\tb := ic.Bounds()\n\tans = &ImageData{\n\t\tWidth: b.Dx(), Height: b.Dy(),\n\t}\n\tif ic.Metadata != nil {\n\t\tans.Format_uppercase = strings.ToUpper(ic.Metadata.Format.String())\n\t}\n\n\tfor _, f := range ic.Frames {\n\t\tfr := ImageFrame{\n\t\t\tImg: f.Image, Left: f.TopLeft.X, Top: f.TopLeft.Y, Width: f.Image.Bounds().Dx(), Height: f.Image.Bounds().Dy(),\n\t\t\tCompose_onto: int(f.ComposeOnto), Number: int(f.Number), Delay_ms: int32(f.Delay.Milliseconds()),\n\t\t\tReplace: f.Replace, Is_opaque: imaging.IsOpaque(f.Image),\n\t\t}\n\t\tif fr.Delay_ms <= 0 {\n\t\t\tfr.Delay_ms = -1 // -1 is gapless in graphics protocol\n\t\t}\n\t\tans.Frames = append(ans.Frames, &fr)\n\t}\n\treturn\n}\n\nfunc OpenImageFromPath(path string, opts ...imaging.DecodeOption) (ans *ImageData, err error) {\n\tic, err := imaging.OpenAll(path, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewImageData(ic), nil\n}\n\nfunc OpenImageFromReader(r io.Reader, opts ...imaging.DecodeOption) (ans *ImageData, s io.Reader, err error) {\n\tic, s, err := imaging.DecodeAll(r, opts...)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn NewImageData(ic), s, nil\n}\n"
  },
  {
    "path": "tools/utils/images/rowcolumn_diacritics.go",
    "content": "// Unicode data, built from the Unicode Standard 17.0.0\n// Code generated by wcwidth.py, DO NOT EDIT.\n\npackage images\n\nvar NumberToDiacritic = [297]rune{\n\t0x305, 0x30d, 0x30e, 0x310, 0x312, 0x33d, 0x33e, 0x33f, 0x346, 0x34a, 0x34b, 0x34c, 0x350, 0x351, 0x352, 0x357, 0x35b, 0x363, 0x364, 0x365, 0x366, 0x367, 0x368, 0x369, 0x36a, 0x36b, 0x36c, 0x36d, 0x36e, 0x36f, 0x483, 0x484, 0x485, 0x486, 0x487, 0x592, 0x593, 0x594, 0x595, 0x597, 0x598, 0x599, 0x59c, 0x59d, 0x59e, 0x59f, 0x5a0, 0x5a1, 0x5a8, 0x5a9, 0x5ab, 0x5ac, 0x5af, 0x5c4, 0x610, 0x611, 0x612, 0x613, 0x614, 0x615, 0x616, 0x617, 0x657, 0x658, 0x659, 0x65a, 0x65b, 0x65d, 0x65e, 0x6d6, 0x6d7, 0x6d8, 0x6d9, 0x6da, 0x6db, 0x6dc, 0x6df, 0x6e0, 0x6e1, 0x6e2, 0x6e4, 0x6e7, 0x6e8, 0x6eb, 0x6ec, 0x730, 0x732, 0x733, 0x735, 0x736, 0x73a, 0x73d, 0x73f, 0x740, 0x741, 0x743, 0x745, 0x747, 0x749, 0x74a, 0x7eb, 0x7ec, 0x7ed, 0x7ee, 0x7ef, 0x7f0, 0x7f1, 0x7f3, 0x816, 0x817, 0x818, 0x819, 0x81b, 0x81c, 0x81d, 0x81e, 0x81f, 0x820, 0x821, 0x822, 0x823, 0x825, 0x826, 0x827, 0x829, 0x82a, 0x82b, 0x82c, 0x82d, 0x951, 0x953, 0x954, 0xf82, 0xf83, 0xf86, 0xf87, 0x135d, 0x135e, 0x135f, 0x17dd, 0x193a, 0x1a17, 0x1a75, 0x1a76, 0x1a77, 0x1a78, 0x1a79, 0x1a7a, 0x1a7b, 0x1a7c, 0x1b6b, 0x1b6d, 0x1b6e, 0x1b6f, 0x1b70, 0x1b71, 0x1b72, 0x1b73, 0x1cd0, 0x1cd1, 0x1cd2, 0x1cda, 0x1cdb, 0x1ce0, 0x1dc0, 0x1dc1, 0x1dc3, 0x1dc4, 0x1dc5, 0x1dc6, 0x1dc7, 0x1dc8, 0x1dc9, 0x1dcb, 0x1dcc, 0x1dd1, 0x1dd2, 0x1dd3, 0x1dd4, 0x1dd5, 0x1dd6, 0x1dd7, 0x1dd8, 0x1dd9, 0x1dda, 0x1ddb, 0x1ddc, 0x1ddd, 0x1dde, 0x1ddf, 0x1de0, 0x1de1, 0x1de2, 0x1de3, 0x1de4, 0x1de5, 0x1de6, 0x1dfe, 0x20d0, 0x20d1, 0x20d4, 0x20d5, 0x20d6, 0x20d7, 0x20db, 0x20dc, 0x20e1, 0x20e7, 0x20e9, 0x20f0, 0x2cef, 0x2cf0, 0x2cf1, 0x2de0, 0x2de1, 0x2de2, 0x2de3, 0x2de4, 0x2de5, 0x2de6, 0x2de7, 0x2de8, 0x2de9, 0x2dea, 0x2deb, 0x2dec, 0x2ded, 0x2dee, 0x2def, 0x2df0, 0x2df1, 0x2df2, 0x2df3, 0x2df4, 0x2df5, 0x2df6, 0x2df7, 0x2df8, 0x2df9, 0x2dfa, 0x2dfb, 0x2dfc, 0x2dfd, 0x2dfe, 0x2dff, 0xa66f, 0xa67c, 0xa67d, 0xa6f0, 0xa6f1, 0xa8e0, 0xa8e1, 0xa8e2, 0xa8e3, 0xa8e4, 0xa8e5, 0xa8e6, 0xa8e7, 0xa8e8, 0xa8e9, 0xa8ea, 0xa8eb, 0xa8ec, 0xa8ed, 0xa8ee, 0xa8ef, 0xa8f0, 0xa8f1, 0xaab0, 0xaab2, 0xaab3, 0xaab7, 0xaab8, 0xaabe, 0xaabf, 0xaac1, 0xfe20, 0xfe21, 0xfe22, 0xfe23, 0xfe24, 0xfe25, 0xfe26, 0x10a0f, 0x10a38, 0x1d185, 0x1d186, 0x1d187, 0x1d188, 0x1d189, 0x1d1aa, 0x1d1ab, 0x1d1ac, 0x1d1ad, 0x1d242, 0x1d243, 0x1d244,\n}\n"
  },
  {
    "path": "tools/utils/images/serialize_test.go",
    "content": "package images\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/kovidgoyal/kitty\"\n)\n\nvar _ = fmt.Print\n\nfunc TestImageSerialize(t *testing.T) {\n\timg, _, err := OpenImageFromReader(bytes.NewReader(kitty.KittyLogoAsPNGData))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tm, data := img.Serialize()\n\timg2, err := ImageFromSerialized(m, data)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tm2, data2 := img2.Serialize()\n\tif diff := cmp.Diff(m, m2); diff != \"\" {\n\t\tt.Fatalf(\"Image metadata failed to roundtrip:\\n%s\", diff)\n\t}\n\tif diff := cmp.Diff(data, data2); diff != \"\" {\n\t\tt.Fatalf(\"Image data failed to roundtrip:\\n%s\", diff)\n\t}\n}\n"
  },
  {
    "path": "tools/utils/images/to_rgb.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage images\n\nimport (\n\t\"fmt\"\n\t\"image\"\n\n\t\"github.com/kovidgoyal/imaging\"\n\t\"github.com/kovidgoyal/imaging/nrgb\"\n)\n\nvar _ = fmt.Print\n\nfunc (self *Context) paste_nrgb_onto_opaque(background *imaging.NRGB, img image.Image, pos image.Point, bgcol *imaging.NRGBColor) {\n\tbg := imaging.NRGBColor{}\n\tif bgcol != nil {\n\t\tbg = *bgcol\n\n\t}\n\tsrc := nrgb.NewNRGBScanner(img, bg)\n\tself.run_paste(src, background, pos, func(dst []byte) {})\n}\n"
  },
  {
    "path": "tools/utils/images/to_rgba.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage images\n\nimport (\n\t\"fmt\"\n\t\"image\"\n\t\"math\"\n\n\t\"github.com/kovidgoyal/imaging\"\n\t\"github.com/kovidgoyal/imaging/nrgba\"\n\t\"github.com/kovidgoyal/kitty/tools/tty\"\n)\n\nvar _ = fmt.Print\nvar debugprintln = tty.DebugPrintln\nvar _ = debugprintln\n\nfunc (self *Context) run_paste(src imaging.Scanner, background image.Image, pos image.Point, postprocess func([]byte)) {\n\tpos = pos.Sub(background.Bounds().Min)\n\tpasteRect := image.Rectangle{Min: pos, Max: pos.Add(src.Bounds().Size())}\n\tinterRect := pasteRect.Intersect(background.Bounds())\n\tif interRect.Empty() {\n\t\treturn\n\t}\n\tbytes_per_pixel := src.Num_of_channels() * src.Bytes_per_channel()\n\tvar stride int\n\tvar pix []uint8\n\tswitch v := background.(type) {\n\tcase *image.NRGBA:\n\t\tstride = v.Stride\n\t\tpix = v.Pix\n\tcase *imaging.NRGB:\n\t\tstride = v.Stride\n\t\tpix = v.Pix\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"Unsupported image type: %v\", v))\n\t}\n\tif len(pix) < background.Bounds().Dy()*stride {\n\t\tpanic(fmt.Sprintf(\"background image has insufficient pixel data. Bounds: %v Stride: %d Data len: %d\", background.Bounds(), stride, len(pix)))\n\t}\n\tif err := self.SafeParallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {\n\t\tfor y := range ys {\n\t\t\tx1 := interRect.Min.X - pasteRect.Min.X\n\t\t\tx2 := interRect.Max.X - pasteRect.Min.X\n\t\t\ty1 := y - pasteRect.Min.Y\n\t\t\ty2 := y1 + 1\n\t\t\ti1 := y*stride + interRect.Min.X*bytes_per_pixel\n\t\t\ti2 := i1 + interRect.Dx()*bytes_per_pixel\n\t\t\tdst := pix[i1:i2]\n\t\t\tsrc.Scan(x1, y1, x2, y2, dst)\n\t\t\tpostprocess(dst)\n\t\t}\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n}\n\nfunc (self *Context) paste_nrgba_onto_opaque(background *image.NRGBA, img image.Image, pos image.Point, bgcol *imaging.NRGBColor) {\n\tsrc := nrgba.NewNRGBAScanner(img)\n\tif bgcol == nil {\n\t\tself.run_paste(src, background, pos, func([]byte) {})\n\t\treturn\n\t}\n\tbg := [3]float64{float64(bgcol.R), float64(bgcol.G), float64(bgcol.B)}\n\tself.run_paste(src, background, pos, func(dst []byte) {\n\t\tfor len(dst) > 0 {\n\t\t\ta := float64(dst[3]) / 255.0\n\t\t\tfor i := range dst[:3] {\n\t\t\t\t// uint8() automatically converts floats greater than 255 but less than 256 to 255\n\t\t\t\tdst[i] = uint8(float64(dst[i])*a + bg[i]*(1-a))\n\t\t\t}\n\t\t\tdst[3] = 255\n\t\t\tdst = dst[4:]\n\t\t}\n\n\t})\n}\n\n// Paste pastes the img image to the background image at the specified position. Optionally composing onto the specified opaque color.\nfunc (self *Context) Paste(background image.Image, img image.Image, pos image.Point, opaque_bg *imaging.NRGBColor) {\n\tswitch b := background.(type) {\n\tcase *image.NRGBA:\n\t\tself.paste_nrgba_onto_opaque(b, img, pos, opaque_bg)\n\tcase *imaging.NRGB:\n\t\tself.paste_nrgb_onto_opaque(b, img, pos, opaque_bg)\n\tdefault:\n\t\tpanic(\"Unsupported background image type\")\n\t}\n}\n\n// PasteCenter pastes the img image to the center of the background image. Optionally composing onto the specified opaque color.\nfunc (self *Context) PasteCenter(background image.Image, img image.Image, opaque_bg *imaging.NRGBColor) {\n\tbgBounds := background.Bounds()\n\tbgW := bgBounds.Dx()\n\tbgH := bgBounds.Dy()\n\tbgMinX := bgBounds.Min.X\n\tbgMinY := bgBounds.Min.Y\n\n\tcenterX := bgMinX + bgW/2\n\tcenterY := bgMinY + bgH/2\n\n\tx0 := centerX - img.Bounds().Dx()/2\n\ty0 := centerY - img.Bounds().Dy()/2\n\n\tself.Paste(background, img, image.Pt(x0, y0), opaque_bg)\n}\n\nfunc FitImage(width, height, pwidth, pheight int) (final_width int, final_height int) {\n\tif height > pheight {\n\t\tcorrf := float64(pheight) / float64(height)\n\t\twidth, height = int(math.Floor(corrf*float64(width))), pheight\n\t}\n\tif width > pwidth {\n\t\tcorrf := float64(pwidth) / float64(width)\n\t\twidth, height = pwidth, int(math.Floor(corrf*float64(height)))\n\t}\n\tif height > pheight {\n\t\tcorrf := float64(pheight) / float64(height)\n\t\twidth, height = int(math.Floor(corrf*float64(width))), pheight\n\t}\n\n\treturn width, height\n}\n\nfunc NewNRGBAWithContiguousRGBAPixels(p []byte, left, top, width, height int) (*image.NRGBA, error) {\n\tconst bpp = 4\n\tif expected := bpp * width * height; expected != len(p) {\n\t\treturn nil, fmt.Errorf(\"the image width and height dont match the size of the specified pixel data: width=%d height=%d sz=%d != %d\", width, height, len(p), expected)\n\t}\n\treturn &image.NRGBA{\n\t\tPix:    p,\n\t\tStride: bpp * width,\n\t\tRect:   image.Rectangle{image.Point{left, top}, image.Point{left + width, top + height}},\n\t}, nil\n}\n"
  },
  {
    "path": "tools/utils/images/utils.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage images\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/kovidgoyal/go-parallel\"\n)\n\nvar _ = fmt.Print\n\ntype Context struct {\n\tnum_of_threads atomic.Int32\n}\n\nfunc (self *Context) SetNumberOfThreads(n int) {\n\tself.num_of_threads.Store(int32(n))\n}\n\nfunc (self *Context) NumberOfThreads() int {\n\treturn int(self.num_of_threads.Load())\n}\n\nfunc (self *Context) EffectiveNumberOfThreads() int {\n\tans := int(self.num_of_threads.Load())\n\tif ans <= 0 {\n\t\tans = max(1, runtime.NumCPU())\n\t}\n\treturn ans\n}\n\n// parallel processes the data in separate goroutines. If any of them panics,\n// returns an error. Note that if multiple goroutines panic, only one error is\n// returned.\nfunc (self *Context) SafeParallel(start, stop int, fn func(<-chan int)) (err error) {\n\tcount := stop - start\n\tif count < 1 {\n\t\treturn\n\t}\n\n\tprocs := min(self.EffectiveNumberOfThreads(), count)\n\tc := make(chan int, count)\n\tfor i := start; i < stop; i++ {\n\t\tc <- i\n\t}\n\tclose(c)\n\n\tvar wg sync.WaitGroup\n\tfor range procs {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\terr = parallel.Format_stacktrace_on_panic(r, 1)\n\t\t\t\t}\n\t\t\t\twg.Done()\n\t\t\t}()\n\t\t\tfn(c)\n\t\t}()\n\t}\n\twg.Wait()\n\treturn\n}\n"
  },
  {
    "path": "tools/utils/io.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nconst (\n\tDEFAULT_IO_BUFFER_SIZE = 8192\n)\n"
  },
  {
    "path": "tools/utils/iso8601.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar _ = fmt.Print\n\nfunc is_digit(x byte) bool {\n\treturn '0' <= x && x <= '9'\n}\n\n// The following is copied from the Go standard library to implement date range validation logic\n// equivalent to the behaviour of Go's time.Parse.\n\nfunc isLeap(year int) bool {\n\treturn year%4 == 0 && (year%100 != 0 || year%400 == 0)\n}\n\n// daysInMonth is the number of days for non-leap years in each calendar month starting at 1\nvar daysInMonth = [13]int{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}\n\nfunc daysIn(m time.Month, year int) int {\n\tif m == time.February && isLeap(year) {\n\t\treturn 29\n\t}\n\treturn daysInMonth[int(m)]\n}\n\nfunc ISO8601Parse(raw string) (time.Time, error) {\n\torig := raw\n\traw = strings.TrimSpace(raw)\n\n\trequired_number := func(num_digits int) (int, error) {\n\t\tif len(raw) < num_digits {\n\t\t\treturn 0, fmt.Errorf(\"Insufficient digits\")\n\t\t}\n\t\ttext := raw[:num_digits]\n\t\traw = raw[num_digits:]\n\t\tans, err := strconv.ParseUint(text, 10, 32)\n\t\tif err == nil && ans <= math.MaxInt {\n\t\t\treturn int(ans), nil\n\t\t}\n\t\treturn math.MaxInt, err\n\n\t}\n\toptional_separator := func(x byte) bool {\n\t\tif len(raw) > 0 && raw[0] == x {\n\t\t\traw = raw[1:]\n\t\t}\n\t\treturn len(raw) > 0 && is_digit(raw[0])\n\t}\n\n\terrf := func(msg string) (time.Time, error) {\n\t\treturn time.Time{}, fmt.Errorf(\"Invalid ISO8601 timestamp: %#v. %s\", orig, msg)\n\t}\n\n\toptional_separator('+')\n\tyear, err := required_number(4)\n\tif err != nil {\n\t\treturn errf(\"timestamp does not start with a 4 digit year\")\n\t}\n\tvar month int = 1\n\tvar day int = 1\n\tif optional_separator('-') {\n\t\tmonth, err = required_number(2)\n\t\tif err != nil {\n\t\t\treturn errf(\"timestamp does not have a valid 2 digit month\")\n\t\t}\n\t\tif optional_separator('-') {\n\t\t\tday, err = required_number(2)\n\t\t\tif err != nil {\n\t\t\t\treturn errf(\"timestamp does not have a valid 2 digit day\")\n\t\t\t}\n\t\t}\n\t}\n\n\tvar hour, minute, second int\n\tvar nsec int64\n\n\tif len(raw) > 0 && (raw[0] == 'T' || raw[0] == ' ') {\n\t\traw = raw[1:]\n\t\thour, err = required_number(2)\n\t\tif err != nil {\n\t\t\treturn errf(\"timestamp does not have a valid 2 digit hour\")\n\t\t}\n\t\tif optional_separator(':') {\n\t\t\tminute, err = required_number(2)\n\t\t\tif err != nil {\n\t\t\t\treturn errf(\"timestamp does not have a valid 2 digit minute\")\n\t\t\t}\n\t\t\tif optional_separator(':') {\n\t\t\t\tsecond, err = required_number(2)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errf(\"timestamp does not have a valid 2 digit second\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif len(raw) > 0 && (raw[0] == '.' || raw[0] == ',') {\n\t\t\traw = raw[1:]\n\t\t\tnum_digits := 0\n\t\t\tfor len(raw) > num_digits && is_digit(raw[num_digits]) {\n\t\t\t\tnum_digits++\n\t\t\t}\n\t\t\ttext := raw[:num_digits]\n\t\t\traw = raw[num_digits:]\n\t\t\textra := 9 - len(text)\n\t\t\tif extra < 0 {\n\t\t\t\ttext = text[:9]\n\t\t\t}\n\t\t\tif text != \"\" {\n\t\t\t\tif nsec, err = strconv.ParseInt(text, 10, 0); err != nil {\n\t\t\t\t\treturn errf(\"timestamp does not have a valid nanosecond field\")\n\t\t\t\t}\n\t\t\t\tfor ; extra > 0; extra-- {\n\t\t\t\t\tnsec *= 10\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tswitch {\n\tcase month < 1 || month > 12:\n\t\treturn errf(\"timestamp has invalid month value\")\n\tcase day < 1 || day > 31 || day > daysIn(time.Month(month), year):\n\t\treturn errf(\"timestamp has invalid day value\")\n\tcase hour < 0 || hour > 23:\n\t\treturn errf(\"timestamp has invalid hour value\")\n\tcase minute < 0 || minute > 59:\n\t\treturn errf(\"timestamp has invalid minute value\")\n\tcase second < 0 || second > 59:\n\t\treturn errf(\"timestamp has invalid second value\")\n\t}\n\tloc := time.UTC\n\ttzsign, tzhour, tzminute := 0, 0, 0\n\n\tif len(raw) > 0 {\n\t\tswitch raw[0] {\n\t\tcase '+':\n\t\t\ttzsign = 1\n\t\tcase '-':\n\t\t\ttzsign = -1\n\t\t}\n\t}\n\tif tzsign != 0 {\n\t\traw = raw[1:]\n\t\ttzhour, err = required_number(2)\n\t\tif err != nil {\n\t\t\treturn errf(\"timestamp has invalid timezone hour\")\n\t\t}\n\t\toptional_separator(':')\n\t\ttzminute, err = required_number(2)\n\t\tif err != nil {\n\t\t\ttzminute = 0\n\t\t}\n\t\tseconds := tzhour*3600 + tzminute*60\n\t\tloc = time.FixedZone(\"\", tzsign*seconds)\n\t}\n\treturn time.Date(year, time.Month(month), day, hour, minute, second, int(nsec), loc), err\n}\n\nfunc ISO8601Format(x time.Time) string {\n\treturn x.Format(time.RFC3339Nano)\n}\n"
  },
  {
    "path": "tools/utils/iso8601_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestISO8601(t *testing.T) {\n\tnow := time.Now()\n\n\ttt := func(raw string, expected time.Time) {\n\t\tactual, err := ISO8601Parse(raw)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Parsing: %#v failed with error: %s\", raw, err)\n\t\t}\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Parsing: %#v failed:\\n%s\", raw, diff)\n\t\t}\n\t}\n\n\ttt(ISO8601Format(now), now)\n\ttt(\"2023-02-08T07:24:09.551975+00:00\", time.Date(2023, 2, 8, 7, 24, 9, 551975000, time.UTC))\n\ttt(\"2023-02-08T07:24:09.551975Z\", time.Date(2023, 2, 8, 7, 24, 9, 551975000, time.UTC))\n\ttt(\"2023\", time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC))\n\ttt(\"2023-11-13\", time.Date(2023, 11, 13, 0, 0, 0, 0, time.UTC))\n\ttt(\"2023-11-13 07:23\", time.Date(2023, 11, 13, 7, 23, 0, 0, time.UTC))\n\ttt(\"2023-11-13 07:23:01\", time.Date(2023, 11, 13, 7, 23, 1, 0, time.UTC))\n\ttt(\"2023-11-13 07:23:01.\", time.Date(2023, 11, 13, 7, 23, 1, 0, time.UTC))\n\ttt(\"2023-11-13 07:23:01.0\", time.Date(2023, 11, 13, 7, 23, 1, 0, time.UTC))\n\ttt(\"2023-11-13 07:23:01.1\", time.Date(2023, 11, 13, 7, 23, 1, 100000000, time.UTC))\n\ttt(\"202311-13 07\", time.Date(2023, 11, 13, 7, 0, 0, 0, time.UTC))\n\ttt(\"20231113 0705\", time.Date(2023, 11, 13, 7, 5, 0, 0, time.UTC))\n}\n"
  },
  {
    "path": "tools/utils/levenshtein.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\n// compares two strings and returns the Levenshtein distance between them.\nfunc LevenshteinDistance(s, t string, ignore_case bool) int {\n\tif ignore_case {\n\t\ts = strings.ToLower(s)\n\t\tt = strings.ToLower(t)\n\t}\n\td := make([][]int, len(s)+1)\n\tfor i := range d {\n\t\td[i] = make([]int, len(t)+1)\n\t}\n\tfor i := range d {\n\t\td[i][0] = i\n\t}\n\tfor j := range d[0] {\n\t\td[0][j] = j\n\t}\n\tfor j := 1; j <= len(t); j++ {\n\t\tfor i := 1; i <= len(s); i++ {\n\t\t\tif s[i-1] == t[j-1] {\n\t\t\t\td[i][j] = d[i-1][j-1]\n\t\t\t} else {\n\t\t\t\tmin := d[i-1][j]\n\t\t\t\tif d[i][j-1] < min {\n\t\t\t\t\tmin = d[i][j-1]\n\t\t\t\t}\n\t\t\t\tif d[i-1][j-1] < min {\n\t\t\t\t\tmin = d[i-1][j-1]\n\t\t\t\t}\n\t\t\t\td[i][j] = min + 1\n\t\t\t}\n\t\t}\n\n\t}\n\treturn d[len(s)][len(t)]\n}\n"
  },
  {
    "path": "tools/utils/longest-common.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\nvar _ = fmt.Print\n\nfunc slice_iter(strs []string) func() (string, bool) {\n\ti := 0\n\tlimit := len(strs)\n\treturn func() (string, bool) {\n\t\tif i < limit {\n\t\t\ti++\n\t\t\treturn strs[i-1], false\n\t\t}\n\t\treturn \"\", true\n\t}\n}\n\n// Prefix returns the longest common prefix of the provided strings\nfunc Prefix(strs []string) string {\n\treturn LongestCommon(slice_iter(strs), true)\n}\n\n// Suffix returns the longest common suffix of the provided strings\nfunc Suffix(strs []string) string {\n\treturn LongestCommon(slice_iter(strs), false)\n}\n\nfunc min(a ...int) int {\n\tans := math.MaxInt\n\tfor _, x := range a {\n\t\tif x < ans {\n\t\t\tans = x\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc LongestCommon(next func() (string, bool), prefix bool) string {\n\txfix, done := next()\n\tif xfix == \"\" || done {\n\t\treturn \"\"\n\t}\n\tfor {\n\t\tq, done := next()\n\t\tif done {\n\t\t\tbreak\n\t\t}\n\t\tq_len := len(q)\n\t\txfix_len := len(xfix)\n\t\tmax_len := min(q_len, xfix_len)\n\t\tif max_len == 0 {\n\t\t\treturn \"\"\n\t\t}\n\t\tif prefix {\n\t\t\tfor i := range max_len {\n\t\t\t\tif xfix[i] != q[i] {\n\t\t\t\t\txfix = xfix[:i]\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor i := range max_len {\n\t\t\t\txi := xfix_len - i - 1\n\t\t\t\tsi := q_len - i - 1\n\t\t\t\tif xfix[xi] != q[si] {\n\t\t\t\t\txfix = xfix[xi+1:]\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn xfix\n}\n"
  },
  {
    "path": "tools/utils/longest-common_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestLongestCommon(t *testing.T) {\n\tp := func(expected string, items ...string) {\n\t\tactual := Prefix(items)\n\t\tif actual != expected {\n\t\t\tt.Fatalf(\"Failed with %#v\\nExpected: %#v\\nActual:   %#v\", items, expected, actual)\n\t\t}\n\t}\n\tp(\"abc\", \"abc\", \"abcd\")\n\tp(\"\", \"abc\", \"xy\")\n}\n"
  },
  {
    "path": "tools/utils/mimetypes.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"mime\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc load_mime_file(filename string, mime_map map[string]string) error {\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\tscanner := bufio.NewScanner(f)\n\tfor scanner.Scan() {\n\t\tfields := strings.Fields(scanner.Text())\n\t\tif len(fields) <= 1 || fields[0][0] == '#' {\n\t\t\tcontinue\n\t\t}\n\t\tmime_type := fields[0]\n\t\tfor _, ext := range fields[1:] {\n\t\t\tif ext[0] == '#' {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tmime_map[\".\"+ext] = mime_type\n\t\t}\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nvar UserMimeMap = sync.OnceValue(func() map[string]string {\n\tconf_path := filepath.Join(ConfigDir(), \"mime.types\")\n\tans := make(map[string]string, 32)\n\terr := load_mime_file(conf_path, ans)\n\tif err != nil && !errors.Is(err, fs.ErrNotExist) {\n\t\tfmt.Fprintln(os.Stderr, \"Failed to parse\", conf_path, \"for MIME types with error:\", err)\n\t}\n\treturn ans\n})\n\nfunc is_special_file(path string) string {\n\tname := filepath.Base(path)\n\tlname := strings.ToLower(name)\n\tif lname == \"makefile\" || strings.HasPrefix(lname, \"makefile.\") {\n\t\treturn \"text/makefile\"\n\t}\n\tif strings.HasSuffix(name, \"rc\") && !strings.Contains(name, \".\") {\n\t\treturn \"text/plain\"\n\t}\n\treturn \"\"\n}\n\nfunc GuessMimeType(filename string) string {\n\text := filepath.Ext(filename)\n\tmime_with_parameters := UserMimeMap()[ext]\n\tif mime_with_parameters == \"\" {\n\t\tmime_with_parameters = mime.TypeByExtension(ext)\n\t}\n\tif mime_with_parameters == \"\" {\n\t\tonly_once.Do(set_builtins)\n\t\tmime_with_parameters = builtin_types_map[ext]\n\t\tif mime_with_parameters == \"\" {\n\t\t\tlext := strings.ToLower(ext)\n\t\t\tmime_with_parameters = builtin_types_map[lext]\n\t\t\tif mime_with_parameters == \"\" {\n\t\t\t\tmime_with_parameters = KnownExtensions[lext]\n\t\t\t}\n\t\t\tif mime_with_parameters == \"\" {\n\t\t\t\tmime_with_parameters = is_special_file(filename)\n\t\t\t}\n\t\t\tif mime_with_parameters == \"\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t}\n\t}\n\tans, _, err := mime.ParseMediaType(mime_with_parameters)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn ans\n}\n\nfunc GuessMimeTypeWithFileSystemAccess(filename string) string {\n\tis_dir, is_exe := false, false\n\ts, err := os.Stat(filename)\n\tif err == nil {\n\t\tis_dir = s.IsDir()\n\t\tif !is_dir && s.Mode().Perm()&0o111 != 0 && unix.Access(filename, unix.X_OK) == nil {\n\t\t\tis_exe = true\n\t\t}\n\t}\n\tif is_dir {\n\t\treturn \"inode/directory\"\n\t}\n\tmt := GuessMimeType(filename)\n\tif mt == \"\" {\n\t\tif is_exe {\n\t\t\tmt = \"inode/executable\"\n\t\t}\n\t}\n\treturn mt\n}\n"
  },
  {
    "path": "tools/utils/misc.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/simdstring\"\n\t\"golang.org/x/exp/constraints\"\n\t\"golang.org/x/text/language\"\n)\n\nvar _ = fmt.Print\n\nfunc Reverse[T any](s []T) []T {\n\tfor i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {\n\t\ts[i], s[j] = s[j], s[i]\n\t}\n\treturn s\n}\n\nfunc Reversed[T any](s []T) []T {\n\tans := make([]T, len(s))\n\tfor i, x := range s {\n\t\tans[len(s)-1-i] = x\n\t}\n\treturn ans\n}\n\nfunc Remove[T comparable](s []T, q T) []T {\n\tidx := slices.Index(s, q)\n\tif idx > -1 {\n\t\tvar zero T\n\t\ts[idx] = zero // if pointer this allows garbage collection\n\t\treturn slices.Delete(s, idx, idx+1)\n\t}\n\treturn s\n}\n\nfunc RemoveAll[T comparable](s []T, q T) []T {\n\tans := s\n\tfor {\n\t\tidx := slices.Index(ans, q)\n\t\tif idx < 0 {\n\t\t\tbreak\n\t\t}\n\t\tans = slices.Delete(ans, idx, idx+1)\n\t}\n\treturn ans\n}\n\nfunc Filter[T any](s []T, f func(x T) bool) []T {\n\tans := make([]T, 0, len(s))\n\tfor _, x := range s {\n\t\tif f(x) {\n\t\t\tans = append(ans, x)\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc Map[T any, O any](f func(x T) O, s []T) []O {\n\tans := make([]O, 0, len(s))\n\tfor _, x := range s {\n\t\tans = append(ans, f(x))\n\t}\n\treturn ans\n}\n\nfunc Repeat[T any](x T, n int) []T {\n\tans := make([]T, n)\n\tfor i := range n {\n\t\tans[i] = x\n\t}\n\treturn ans\n}\n\nfunc Sort[T any](s []T, cmp func(a, b T) int) []T {\n\tslices.SortFunc(s, cmp)\n\treturn s\n}\n\nfunc StableSort[T any](s []T, cmp func(a, b T) int) []T {\n\tslices.SortStableFunc(s, cmp)\n\treturn s\n}\n\nfunc Uniq[T comparable](s []T) []T {\n\tseen := NewSet[T](len(s))\n\tans := make([]T, 0, len(s))\n\tfor _, x := range s {\n\t\tif !seen.Has(x) {\n\t\t\tseen.Add(x)\n\t\t\tans = append(ans, x)\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc sort_with_key[T any, C constraints.Ordered](stable bool, s []T, key func(a T) C) []T {\n\ttype t struct {\n\t\tkey C\n\t\tval T\n\t}\n\ttemp := make([]t, len(s))\n\tfor i, x := range s {\n\t\ttemp[i].val, temp[i].key = x, key(x)\n\t}\n\tkey_cmp := func(a, b t) int {\n\t\treturn cmp.Compare(a.key, b.key)\n\t}\n\tif stable {\n\t\tslices.SortStableFunc(temp, key_cmp)\n\t} else {\n\t\tslices.SortFunc(temp, key_cmp)\n\t}\n\tfor i, x := range temp {\n\t\ts[i] = x.val\n\t}\n\treturn s\n}\n\nfunc SortWithKey[T any, C constraints.Ordered](s []T, key func(a T) C) []T {\n\treturn sort_with_key(false, s, key)\n}\n\nfunc StableSortWithKey[T any, C constraints.Ordered](s []T, key func(a T) C) []T {\n\treturn sort_with_key(true, s, key)\n}\n\nfunc Max[T constraints.Ordered](a T, items ...T) (ans T) {\n\tans = a\n\tfor _, q := range items {\n\t\tif q > ans {\n\t\t\tans = q\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc Min[T constraints.Ordered](a T, items ...T) (ans T) {\n\tans = a\n\tfor _, q := range items {\n\t\tif q < ans {\n\t\t\tans = q\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc Memset[T any](dest []T, pattern ...T) []T {\n\tswitch len(pattern) {\n\tcase 0:\n\t\tvar zero T\n\t\tswitch any(zero).(type) {\n\t\tcase byte: // special case this as the compiler can generate efficient code for memset of a byte slice to zero\n\t\t\tbd := any(dest).([]byte)\n\t\t\tfor i := range bd {\n\t\t\t\tbd[i] = 0\n\t\t\t}\n\t\tdefault:\n\t\t\tfor i := range dest {\n\t\t\t\tdest[i] = zero\n\t\t\t}\n\t\t}\n\t\treturn dest\n\tcase 1:\n\t\tval := pattern[0]\n\t\tfor i := range dest {\n\t\t\tdest[i] = val\n\t\t}\n\tdefault:\n\t\tbp := copy(dest, pattern)\n\t\tfor bp < len(dest) {\n\t\t\tcopy(dest[bp:], dest[:bp])\n\t\t\tbp *= 2\n\t\t}\n\t}\n\treturn dest\n}\n\ntype statable interface {\n\tStat() (os.FileInfo, error)\n}\n\nfunc Samefile(a, b any) bool {\n\tvar sta, stb os.FileInfo\n\tvar err error\n\tswitch v := a.(type) {\n\tcase string:\n\t\tsta, err = os.Stat(v)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\tcase statable:\n\t\tsta, err = v.Stat()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\tcase *os.FileInfo:\n\t\tsta = *v\n\tcase os.FileInfo:\n\t\tsta = v\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"a must be a string, os.FileInfo or a stat-able object not %T\", v))\n\t}\n\tswitch v := b.(type) {\n\tcase string:\n\t\tstb, err = os.Stat(v)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\tcase statable:\n\t\tstb, err = v.Stat()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\tcase *os.FileInfo:\n\t\tstb = *v\n\tcase os.FileInfo:\n\t\tstb = v\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"b must be a string, os.FileInfo or a stat-able object not %T\", v))\n\t}\n\n\treturn os.SameFile(sta, stb)\n}\n\nfunc Concat[T any](slices ...[]T) []T {\n\tvar total int\n\tfor _, s := range slices {\n\t\ttotal += len(s)\n\t}\n\tresult := make([]T, total)\n\tvar i int\n\tfor _, s := range slices {\n\t\ti += copy(result[i:], s)\n\t}\n\treturn result\n}\n\nfunc ShiftLeft[T any](s []T, amt int) []T {\n\tleftover := len(s) - amt\n\tif leftover > 0 {\n\t\tcopy(s, s[amt:])\n\t}\n\treturn s[:leftover]\n}\n\nfunc SetStructDefaults(v reflect.Value) (err error) {\n\tfor _, field := range reflect.VisibleFields(v.Type()) {\n\t\tif defval := field.Tag.Get(\"default\"); defval != \"\" {\n\t\t\tval := v.FieldByIndex(field.Index)\n\t\t\tif val.CanSet() {\n\t\t\t\tswitch field.Type.Kind() {\n\t\t\t\tcase reflect.String:\n\t\t\t\t\tif val.String() != \"\" {\n\t\t\t\t\t\tval.SetString(defval)\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\t\t\t\tif d, err := strconv.ParseInt(defval, 10, 64); err == nil {\n\t\t\t\t\t\tval.SetInt(d)\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn fmt.Errorf(\"Could not parse default value for struct field: %#v with error: %s\", field.Name, err)\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\t\t\tif d, err := strconv.ParseUint(defval, 10, 64); err == nil {\n\t\t\t\t\t\tval.SetUint(d)\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn fmt.Errorf(\"Could not parse default value for struct field: %#v with error: %s\", field.Name, err)\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc IfElse[T any](condition bool, if_val T, else_val T) T {\n\tif condition {\n\t\treturn if_val\n\t}\n\treturn else_val\n}\n\nfunc SourceLine(skip_frames ...int) int {\n\ts := 1\n\tif len(skip_frames) > 0 {\n\t\ts += skip_frames[0]\n\t}\n\tif _, _, ans, ok := runtime.Caller(s); ok {\n\t\treturn ans\n\t}\n\treturn -1\n}\n\nfunc SourceLoc(skip_frames ...int) string {\n\ts := 1\n\tif len(skip_frames) > 0 {\n\t\ts += skip_frames[0]\n\t}\n\tif _, file, line, ok := runtime.Caller(s); ok {\n\t\treturn filepath.Base(file) + \":\" + strconv.Itoa(line)\n\t}\n\treturn \"unknown\"\n}\n\nfunc FunctionName(a any) string {\n\tif a == nil {\n\t\treturn \"<nil>\"\n\t}\n\tp := reflect.ValueOf(a).Pointer()\n\tf := runtime.FuncForPC(p)\n\tif f != nil {\n\t\treturn f.Name()\n\t}\n\treturn \"\"\n}\n\nfunc Abs[T constraints.Integer](x T) T {\n\tif x < 0 {\n\t\treturn -x\n\t}\n\treturn x\n}\n\nvar LanguageTag = sync.OnceValue(func() language.Tag {\n\t// Check environment variables in order of precedence\n\tvar locale string\n\tfor _, v := range []string{\"LC_ALL\", \"LC_MESSAGES\", \"LANG\"} {\n\t\tlocale = os.Getenv(v)\n\t\tif locale != \"\" {\n\t\t\tbreak\n\t\t}\n\t}\n\tif locale == \"\" {\n\t\treturn language.English // Default/fallback\n\t}\n\t// Remove encoding, e.g., \".UTF-8\"\n\tlocale = strings.Split(locale, \".\")[0]\n\t// Replace underscore with hyphen to match BCP47 format (en_US -> en-US)\n\tlocale = strings.ReplaceAll(locale, \"_\", \"-\")\n\t// Validate/normalize with golang.org/x/text/language\n\ttag, err := language.Parse(locale)\n\tif err != nil {\n\t\treturn language.English\n\t}\n\treturn tag\n\n})\n\n// Replace control codes by unicode codepoints that describe the codes\n// making the text safe to send to a terminal\nfunc ReplaceControlCodes(text, replace_tab_by, replace_newline_by string) string {\n\tbuf := strings.Builder{}\n\tfor len(text) > 0 {\n\t\tidx := simdstring.IndexC0String(text)\n\t\tif idx < 0 {\n\t\t\tif buf.Cap() == 0 {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\tbuf.WriteString(text)\n\t\t\tbreak\n\t\t}\n\t\tif buf.Cap() == 0 {\n\t\t\tbuf.Grow(2 * len(text))\n\t\t}\n\t\tbuf.WriteString(text[:idx])\n\t\tswitch text[idx] {\n\t\tcase '\\n':\n\t\t\tbuf.WriteString(replace_newline_by)\n\t\tcase '\\t':\n\t\t\tbuf.WriteString(replace_tab_by)\n\t\tcase 0x7f:\n\t\t\tbuf.WriteRune(0x2421)\n\t\tdefault:\n\t\t\tbuf.WriteRune(0x2400 + rune(text[idx]))\n\t\t}\n\t\ttext = text[idx+1:]\n\t}\n\treturn buf.String()\n}\n"
  },
  {
    "path": "tools/utils/passwd.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"howett.net/plist\"\n)\n\nvar _ = fmt.Print\n\ntype PasswdEntry struct {\n\tUsername, Pass, Uid, Gid, Gecos, Home, Shell string\n}\n\nfunc ParsePasswdLine(line string) (PasswdEntry, error) {\n\tparts := strings.Split(line, \":\")\n\tif len(parts) == 7 {\n\t\treturn PasswdEntry{parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6]}, nil\n\t}\n\treturn PasswdEntry{}, fmt.Errorf(\"passwd line has %d colon delimited fields instead of 7\", len(parts))\n}\n\nfunc ParsePasswdDatabase(raw string) (ans map[string]PasswdEntry) {\n\tscanner := NewLineScanner(raw)\n\tans = make(map[string]PasswdEntry)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tif entry, e := ParsePasswdLine(line); e == nil {\n\t\t\tans[entry.Uid] = entry\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc ParsePasswdFile(path string) (ans map[string]PasswdEntry, err error) {\n\traw, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn ParsePasswdDatabase(UnsafeBytesToString(raw)), nil\n}\n\nvar passwd_err error\nvar passwd_database = sync.OnceValue(func() (ans map[string]PasswdEntry) {\n\tans, passwd_err = ParsePasswdFile(\"/etc/passwd\")\n\treturn\n})\n\nfunc PwdEntryForUid(uid string) (ans PasswdEntry, err error) {\n\tpwd := passwd_database()\n\tif passwd_err != nil {\n\t\treturn ans, passwd_err\n\t}\n\tans, found := pwd[uid]\n\tif !found {\n\t\treturn ans, fmt.Errorf(\"No user matching the UID: %#v found\", uid)\n\t}\n\treturn ans, nil\n}\n\nfunc parse_dscl_data(raw []byte) (ans map[string]PasswdEntry, err error) {\n\tvar pd []any\n\t_, err = plist.Unmarshal(raw, &pd)\n\tif err != nil {\n\t\treturn\n\t}\n\tans = make(map[string]PasswdEntry, 256)\n\tfor _, entry := range pd {\n\t\tif e, ok := entry.(map[string]any); ok {\n\t\t\titem := PasswdEntry{}\n\t\t\tfor key, a := range e {\n\t\t\t\tarray, ok := a.([]any)\n\t\t\t\tif !ok || len(array) == 0 || !strings.HasPrefix(key, \"dsAttrTypeNative:\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t_, key, _ = strings.Cut(key, \":\")\n\t\t\t\tif val, ok := array[0].(string); ok {\n\t\t\t\t\tswitch key {\n\t\t\t\t\tcase \"uid\":\n\t\t\t\t\t\titem.Uid = val\n\t\t\t\t\tcase \"gid\":\n\t\t\t\t\t\titem.Gid = val\n\t\t\t\t\tcase \"home\":\n\t\t\t\t\t\titem.Home = val\n\t\t\t\t\tcase \"name\":\n\t\t\t\t\t\titem.Username = val\n\t\t\t\t\tcase \"realname\":\n\t\t\t\t\t\titem.Gecos = val\n\t\t\t\t\tcase \"shell\":\n\t\t\t\t\t\titem.Shell = val\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tans[item.Uid] = item\n\t\t}\n\t}\n\treturn\n}\n\nvar dscl_error error\n\nvar dscl_user_database = sync.OnceValue(func() map[string]PasswdEntry {\n\tc := exec.Command(\"/usr/bin/dscl\", \"-plist\", \".\", \"-readall\", \"/Users\", \"uid\", \"gid\", \"name\", \"realname\", \"home\", \"shell\")\n\traw, err := c.Output()\n\tif err != nil {\n\t\tdscl_error = err\n\t\treturn nil\n\t}\n\tans, err := parse_dscl_data(raw)\n\tif err != nil {\n\t\tdscl_error = err\n\t\treturn nil\n\t}\n\treturn ans\n\n})\n\nfunc LoginShellForUser(u *user.User) (ans string, err error) {\n\tvar db map[string]PasswdEntry\n\tswitch runtime.GOOS {\n\tcase \"darwin\":\n\t\tdb = dscl_user_database()\n\t\terr = dscl_error\n\tdefault:\n\t\tdb = passwd_database()\n\t\terr = passwd_err\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\tif rec, found := db[u.Uid]; found {\n\t\treturn rec.Shell, nil\n\t}\n\treturn ans, fmt.Errorf(\"No user record available for user with UID: %#v\", u.Uid)\n}\n\nfunc CurrentUser() (ans *user.User, err error) {\n\tans, err = user.Current()\n\tif err != nil && runtime.GOOS == \"darwin\" {\n\t\tuid := strconv.Itoa(os.Geteuid())\n\t\tdb := dscl_user_database()\n\t\tif dscl_error != nil {\n\t\t\terr = dscl_error\n\t\t\treturn\n\t\t}\n\t\tif rec, found := db[uid]; found {\n\t\t\tu := user.User{Uid: uid, Gid: rec.Gid, Username: rec.Username, Name: rec.Gecos, HomeDir: rec.Home}\n\t\t\tans = &u\n\t\t\terr = nil\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"Could not find the current uid: %d in the DSCL user database\", os.Geteuid())\n\t\t}\n\t}\n\treturn\n}\n\nfunc LoginShellForCurrentUser() (ans string, err error) {\n\tu, err := CurrentUser()\n\tif err != nil {\n\t\treturn ans, err\n\t}\n\treturn LoginShellForUser(u)\n}\n"
  },
  {
    "path": "tools/utils/passwd_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestGettingLoginShell(t *testing.T) {\n\tentries, err := parse_dscl_data([]byte(all_users_plist))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif diff := cmp.Diff(entries[`501`].Shell, \"/bin/zsh\"); diff != \"\" {\n\t\tt.Fatalf(\"Failed to parse dscl data for shell. %v %s\", entries[`501`], diff)\n\t}\n\tif _, e := CurrentUser(); e != nil {\n\t\tt.Fatal(e)\n\t}\n\tif sh, e := LoginShellForCurrentUser(); e != nil || sh == \"\" {\n\t\tt.Fatalf(\"Failed to get login shell for current user with error: %s\", e)\n\t}\n}\n\n// plist test data {{{\nconst all_users_plist string = `\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<array>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>278</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/accessoryupdater</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_accessoryupdater</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Accessory Update Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>278</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_accessoryupdater</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>83</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/virusmails</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_amavisd</string>\n\t\t\t<string>amavisd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>AMaViS Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>83</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_amavisd</string>\n\t\t\t<string>amavisd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>263</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/analyticsd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_analyticsd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Analytics Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>263</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_analyticsd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>273</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/appinstalld</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_appinstalld</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>App Install Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>273</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_appinstalld</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>55</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_appleevents</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>AppleEvents Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>55</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_appleevents</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>260</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/applepay</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_applepay</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>applepay Account</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>260</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_applepay</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>87</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_appowner</string>\n\t\t\t<string>appowner</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Application Owner</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>87</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_appowner</string>\n\t\t\t<string>appowner</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>79</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_appserver</string>\n\t\t\t<string>appserver</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Application Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>79</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_appserver</string>\n\t\t\t<string>appserver</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>33</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/appstore</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_appstore</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Mac App Store Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>33</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_appstore</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>67</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_ard</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Apple Remote Desktop</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>67</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_ard</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>235</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_assetcache</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Asset Cache Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>235</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_assetcache</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>245</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/astris</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_astris</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Astris Services</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>245</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_astris</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>97</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_atsserver</string>\n\t\t\t<string>atsserver</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>ATS Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>97</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_atsserver</string>\n\t\t\t<string>atsserver</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_avbdeviced</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Ethernet AVB Device Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>229</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_avbdeviced</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>288</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_avphidbridge</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Apple Virtual Platform HID Bridge</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>288</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_avphidbridge</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>291</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_backgroundassets</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Background Assets Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>291</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_backgroundassets</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>289</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/biome</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_biome</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Biome</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>289</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_biome</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>93</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_calendar</string>\n\t\t\t<string>calendar</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Calendar</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>93</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_calendar</string>\n\t\t\t<string>calendar</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>258</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_captiveagent</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>captiveagent</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>258</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_captiveagent</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>32</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_ces</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Certificate Enrollment Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>32</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_ces</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>82</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/virusmails</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_clamav</string>\n\t\t\t<string>clamav</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>ClamAV Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>82</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_clamav</string>\n\t\t\t<string>clamav</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>262</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/cmiodalassistants</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_cmiodalassistants</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>CoreMedia IO Assistants User</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>262</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_cmiodalassistants</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>202</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_coreaudiod</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Core Audio Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>202</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_coreaudiod</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>236</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_coremediaiod</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Core Media IO Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>236</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_coremediaiod</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>280</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/coreml</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_coreml</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>CoreML Services</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>280</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_coreml</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>259</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_ctkd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>ctkd Account</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>259</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_ctkd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>212</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_cvmsroot</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>CVMS Root</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>212</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_cvmsroot</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>72</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_cvs</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>CVS Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>72</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_cvs</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>6</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/imap</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_cyrus</string>\n\t\t\t<string>cyrusimap</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Cyrus Administrator</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>77</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_cyrus</string>\n\t\t\t<string>cyrusimap</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>284</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/darwindaemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_darwindaemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Darwin Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>284</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_darwindaemon</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>257</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/datadetectors</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_datadetectors</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>DataDetectors</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>257</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_datadetectors</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>275</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_demod</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Demo Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>275</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_demod</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>59</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_devdocs</string>\n\t\t\t<string>devdocs</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Developer Documentation</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>59</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_devdocs</string>\n\t\t\t<string>devdocs</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>220</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_devicemgr</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Device Management Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>220</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_devicemgr</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>271</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/diskimagesiod</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_diskimagesiod</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>DiskImages IO Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>271</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_diskimagesiod</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>244</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_displaypolicyd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Display Policy Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>244</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_displaypolicyd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>241</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_distnote</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>DistNote</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>241</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_distnote</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>6</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_dovecot</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Dovecot Administrator</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>214</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_dovecot</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>227</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_dovenull</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Dovecot Authentication</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>227</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_dovenull</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>215</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_dpaudio</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>DP Audio</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>215</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_dpaudio</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>270</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_driverkit</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>DriverKit</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>270</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_driverkit</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>71</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_eppc</string>\n\t\t\t<string>eppc</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Apple Events User</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>71</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_eppc</string>\n\t\t\t<string>eppc</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>254</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/findmydevice</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_findmydevice</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Find My Device Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>254</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_findmydevice</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>265</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/fpsd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_fpsd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>FPS Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>265</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_fpsd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_ftp</string>\n\t\t\t<string>ftp</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>FTP Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>98</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_ftp</string>\n\t\t\t<string>ftp</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>247</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_gamecontrollerd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Game Controller Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>247</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_gamecontrollerd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>56</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/geod</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_geod</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Geo Services Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>56</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_geod</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>261</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/hidd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_hidd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>HID Service User</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>261</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_hidd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>240</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_iconservices</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>IconServices</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>240</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_iconservices</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>25</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_installassistant</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Install Assistant</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>25</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_installassistant</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>274</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/installcoordinationd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_installcoordinationd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Install Coordination Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>274</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_installcoordinationd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_installer</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Installer</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>96</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_installer</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>84</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_jabber</string>\n\t\t\t<string>jabber</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Jabber XMPP Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>84</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_jabber</string>\n\t\t\t<string>jabber</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_kadmin_admin</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Kerberos Admin Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>218</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_kadmin_admin</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_kadmin_changepw</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Kerberos Change Password Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>219</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_kadmin_changepw</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>279</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/knowledgegraphd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_knowledgegraphd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Knowledge Graph Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>279</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_knowledgegraphd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_krb_anonymous</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Open Directory Kerberos Anonymous</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>234</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_krb_anonymous</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_krb_changepw</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Open Directory Kerberos Change Password Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>232</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_krb_changepw</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_krb_kadmin</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Open Directory Kerberos Admin Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>231</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_krb_kadmin</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_krb_kerberos</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Open Directory Kerberos</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>233</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_krb_kerberos</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_krb_krbtgt</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Open Directory Kerberos Ticket Granting Ticket</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>230</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_krb_krbtgt</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_krbfast</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Kerberos FAST Account</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>246</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_krbfast</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_krbtgt</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Kerberos Ticket Granting Ticket</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>217</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_krbtgt</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>239</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_launchservicesd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>_launchservicesd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>239</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_launchservicesd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>211</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_lda</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Local Delivery Agent</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>211</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_lda</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>205</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/locationd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_locationd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Location Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>205</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_locationd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>272</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/diagnostics</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_logd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Log Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>272</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_logd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>26</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/spool/cups</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_lp</string>\n\t\t\t<string>lp</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Printing Services</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>26</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_lp</string>\n\t\t\t<string>lp</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>78</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_mailman</string>\n\t\t\t<string>mailman</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Mailman List Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>78</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_mailman</string>\n\t\t\t<string>mailman</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>248</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/setup</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_mbsetupuser</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Setup User</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/bin/bash</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>248</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_mbsetupuser</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>54</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_mcxalr</string>\n\t\t\t<string>mcxalr</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>MCX AppLaunch</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>54</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_mcxalr</string>\n\t\t\t<string>mcxalr</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>65</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_mdnsresponder</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>mDNSResponder</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>65</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_mdnsresponder</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>283</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/mmaintenanced</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_mmaintenanced</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>mmaintenanced</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>283</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_mmaintenanced</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>253</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/ma</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_mobileasset</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>MobileAsset User</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>253</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_mobileasset</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>74</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_mysql</string>\n\t\t\t<string>mysql</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>MySQL Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>74</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_mysql</string>\n\t\t\t<string>mysql</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>268</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/nearbyd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_nearbyd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Proximity and Ranging Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>268</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_nearbyd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>222</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_netbios</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>NetBIOS</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>222</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_netbios</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>228</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_netstatistics</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Network Statistics Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>228</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_netstatistics</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>24</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/networkd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_networkd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Network Services</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>24</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_networkd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>285</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_notification_proxy</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Notification Proxy</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>285</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_notification_proxy</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>242</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/nsurlsessiond</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_nsurlsessiond</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>NSURLSession Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>242</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_nsurlsessiond</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>243</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/nsurlstoraged</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_nsurlstoraged</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>_nsurlstoraged</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>243</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_nsurlstoraged</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>441</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_oahd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>OAH Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>441</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_oahd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>249</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/ondemand</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_ondemand</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>On Demand Resource Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>249</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_ondemand</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>27</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/spool/postfix</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_postfix</string>\n\t\t\t<string>postfix</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Postfix Mail Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>27</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_postfix</string>\n\t\t\t<string>postfix</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>216</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_postgres</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>PostgreSQL Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>216</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_postgres</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>76</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_qtss</string>\n\t\t\t<string>qtss</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>QuickTime Streaming Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>76</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_qtss</string>\n\t\t\t<string>qtss</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>269</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/reportmemoryexception</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_reportmemoryexception</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>ReportMemoryException</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>269</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_reportmemoryexception</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>277</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/rmd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_rmd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Remote Management Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>277</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_rmd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>60</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_sandbox</string>\n\t\t\t<string>sandbox</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Seatbelt</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>60</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_sandbox</string>\n\t\t\t<string>sandbox</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>203</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_screensaver</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Screensaver</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>203</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_screensaver</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>31</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_scsd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Service Configuration Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>31</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_scsd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>92</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/securityagent</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_securityagent</string>\n\t\t\t<string>securityagent</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>SecurityAgent</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>92</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_securityagent</string>\n\t\t\t<string>securityagent</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>58</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_serialnumberd</string>\n\t\t\t<string>serialnumberd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>_serialnumberd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>58</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_serialnumberd</string>\n\t\t\t<string>serialnumberd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>281</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_sntpd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>SNTP Server Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>281</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_sntpd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>200</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/softwareupdate</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_softwareupdate</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Software Update Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>200</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_softwareupdate</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>89</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_spotlight</string>\n\t\t\t<string>spotlight</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Spotlight</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>89</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_spotlight</string>\n\t\t\t<string>spotlight</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>75</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_sshd</string>\n\t\t\t<string>sshd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>sshd Privilege separation</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>75</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_sshd</string>\n\t\t\t<string>sshd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>73</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_svn</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>SVN Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>73</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_svn</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>13</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_taskgated</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Task Gate Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>13</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_taskgated</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>94</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/teamsserver</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_teamsserver</string>\n\t\t\t<string>teamsserver</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>TeamsServer</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>94</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_teamsserver</string>\n\t\t\t<string>teamsserver</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>266</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/timed</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_timed</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Time Sync Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>266</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_timed</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>210</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_timezone</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>AutoTimeZoneDaemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>210</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_timezone</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>91</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_tokend</string>\n\t\t\t<string>tokend</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Token Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>91</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_tokend</string>\n\t\t\t<string>tokend</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>282</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_trustd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>trustd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>282</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_trustd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>208</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_trustevaluationagent</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Trust Evaluation Agent</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>208</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_trustevaluationagent</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>99</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_unknown</string>\n\t\t\t<string>unknown</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Unknown User</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>99</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_unknown</string>\n\t\t\t<string>unknown</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_update_sharing</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Update Sharing</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>95</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_update_sharing</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>213</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/db/lockdown</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_usbmuxd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>iPhone OS Device Helper</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>213</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_usbmuxd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>4</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/spool/uucp</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_uucp</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Unix to Unix Copy Protocol</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/sbin/uucico</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>4</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_uucp</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>224</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_warmd</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Warm Daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>224</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_warmd</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>221</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_webauthserver</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Web Auth Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>221</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_webauthserver</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>88</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_windowserver</string>\n\t\t\t<string>windowserver</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>WindowServer</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>88</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_windowserver</string>\n\t\t\t<string>windowserver</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>70</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/Library/WebServer</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_www</string>\n\t\t\t<string>www</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>World Wide Web Server</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>70</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_www</string>\n\t\t\t<string>www</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>252</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_wwwproxy</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>WWW Proxy</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>252</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_wwwproxy</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>251</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>_xserverdocs</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>macOS Server Documents Service</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>251</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>_xserverdocs</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>1</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/root</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>daemon</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>System Services</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>1</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>daemon</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>20</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/Users/kovid</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>kovid</string>\n\t\t\t<string>com.apple.idms.appleid.prd.001239-05-db37ae71-1475-47b3-9cd5-d749f085f52d</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Kovid Goyal</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/bin/zsh</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>501</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>kovid</string>\n\t\t\t<string>com.apple.idms.appleid.prd.001239-05-db37ae71-1475-47b3-9cd5-d749f085f52d</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/empty</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>nobody</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>Unprivileged User</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/usr/bin/false</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>-2</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>nobody</string>\n\t\t</array>\n\t</dict>\n\t<dict>\n\t\t<key>dsAttrTypeNative:gid</key>\n\t\t<array>\n\t\t\t<string>0</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:home</key>\n\t\t<array>\n\t\t\t<string>/var/root</string>\n\t\t\t<string>/private/var/root</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:name</key>\n\t\t<array>\n\t\t\t<string>root</string>\n\t\t\t<string>BUILTIN\\Local System</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:realname</key>\n\t\t<array>\n\t\t\t<string>System Administrator</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:shell</key>\n\t\t<array>\n\t\t\t<string>/bin/sh</string>\n\t\t</array>\n\t\t<key>dsAttrTypeNative:uid</key>\n\t\t<array>\n\t\t\t<string>0</string>\n\t\t</array>\n\t\t<key>dsAttrTypeStandard:RecordName</key>\n\t\t<array>\n\t\t\t<string>root</string>\n\t\t\t<string>BUILTIN\\Local System</string>\n\t\t</array>\n\t</dict>\n</array>\n</plist>\n` // }}}\n"
  },
  {
    "path": "tools/utils/paths/well_known.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage paths\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype Ctx struct {\n\thome, cwd string\n}\n\nfunc (ctx *Ctx) SetHome(val string) {\n\tctx.home = val\n}\n\nfunc (ctx *Ctx) SetCwd(val string) {\n\tctx.cwd = val\n}\n\nfunc (ctx *Ctx) HomePath() (ans string) {\n\tans = ctx.home\n\tif ans == \"\" {\n\t\tans = utils.Expanduser(\"~\")\n\t}\n\treturn\n}\n\nfunc (ctx *Ctx) CwdPath() (ans string) {\n\tans = ctx.cwd\n\tif ans == \"\" {\n\t\tvar err error\n\t\tans, err = os.Getwd()\n\t\tif err != nil {\n\t\t\tans = \".\"\n\t\t}\n\t}\n\treturn\n}\n\nfunc abspath(path, base string) (ans string) {\n\treturn filepath.Join(base, path)\n}\n\nfunc (ctx *Ctx) Abspath(path string) (ans string) {\n\treturn abspath(path, ctx.CwdPath())\n}\n\nfunc (ctx *Ctx) AbspathFromHome(path string) (ans string) {\n\treturn abspath(path, ctx.HomePath())\n}\n\nfunc (ctx *Ctx) ExpandHome(path string) (ans string) {\n\tif strings.HasPrefix(path, \"~/\") {\n\t\treturn ctx.AbspathFromHome(path)\n\t}\n\treturn path\n}\n"
  },
  {
    "path": "tools/utils/paths.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base32\"\n\t\"fmt\"\n\t\"io/fs\"\n\tnot_rand \"math/rand/v2\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"unicode/utf8\"\n\n\t\"github.com/shirou/gopsutil/v4/process\"\n\t\"golang.org/x/sys/unix\"\n)\n\nvar Sep = string(os.PathSeparator)\n\nfunc Expanduser(path string) string {\n\tif !strings.HasPrefix(path, \"~\") {\n\t\treturn path\n\t}\n\thome, err := os.UserHomeDir()\n\tif err != nil {\n\t\tusr, err := user.Current()\n\t\tif err == nil {\n\t\t\thome = usr.HomeDir\n\t\t}\n\t}\n\tif err != nil || home == \"\" {\n\t\treturn path\n\t}\n\tif path == \"~\" {\n\t\treturn home\n\t}\n\tpath = strings.ReplaceAll(path, Sep, \"/\")\n\tparts := strings.Split(path, \"/\")\n\tif parts[0] == \"~\" {\n\t\tparts[0] = home\n\t} else {\n\t\tuname := parts[0][1:]\n\t\tif uname != \"\" {\n\t\t\tu, err := user.Lookup(uname)\n\t\t\tif err == nil && u.HomeDir != \"\" {\n\t\t\t\tparts[0] = u.HomeDir\n\t\t\t}\n\t\t}\n\t}\n\treturn strings.Join(parts, Sep)\n}\n\nfunc Abspath(path string) string {\n\tq, err := filepath.Abs(path)\n\tif err == nil {\n\t\treturn q\n\t}\n\treturn path\n}\n\nvar KittyExe = sync.OnceValue(func() string {\n\tif kitty_pid := os.Getenv(\"KITTY_PID\"); kitty_pid != \"\" {\n\t\tif kp, err := strconv.ParseInt(kitty_pid, 10, 32); err == nil {\n\t\t\tif p, err := process.NewProcess(int32(kp)); err == nil {\n\t\t\t\tif exe, err := p.Exe(); err == nil && filepath.IsAbs(exe) && filepath.Base(exe) == \"kitty\" {\n\t\t\t\t\treturn exe\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif exe, err := os.Executable(); err == nil {\n\t\tans := filepath.Join(filepath.Dir(exe), \"kitty\")\n\t\tif s, err := os.Stat(ans); err == nil && !s.IsDir() {\n\t\t\treturn ans\n\t\t}\n\t}\n\treturn os.Getenv(\"KITTY_PATH_TO_KITTY_EXE\")\n})\n\nfunc ConfigDirForName(name string) (config_dir string) {\n\tif kcd := os.Getenv(\"KITTY_CONFIG_DIRECTORY\"); kcd != \"\" {\n\t\treturn Abspath(Expanduser(kcd))\n\t}\n\tvar locations []string\n\tseen := NewSet[string]()\n\tadd := func(x string) {\n\t\tx = Abspath(Expanduser(x))\n\t\tif !seen.Has(x) {\n\t\t\tseen.Add(x)\n\t\t\tlocations = append(locations, x)\n\t\t}\n\t}\n\tif xh := os.Getenv(\"XDG_CONFIG_HOME\"); xh != \"\" {\n\t\tadd(xh)\n\t}\n\tif dirs := os.Getenv(\"XDG_CONFIG_DIRS\"); dirs != \"\" {\n\t\tfor candidate := range strings.SplitSeq(dirs, \":\") {\n\t\t\tadd(candidate)\n\t\t}\n\t}\n\tadd(\"~/.config\")\n\tif runtime.GOOS == \"darwin\" {\n\t\tadd(\"~/Library/Preferences\")\n\t}\n\tfor _, loc := range locations {\n\t\tif loc != \"\" {\n\t\t\tq := filepath.Join(loc, \"kitty\")\n\t\t\tif _, err := os.Stat(filepath.Join(q, name)); err == nil {\n\t\t\t\tif unix.Access(q, unix.W_OK) == nil {\n\t\t\t\t\tconfig_dir = q\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tconfig_dir = os.Getenv(\"XDG_CONFIG_HOME\")\n\tif config_dir == \"\" {\n\t\tconfig_dir = \"~/.config\"\n\t}\n\tconfig_dir = filepath.Join(Expanduser(config_dir), \"kitty\")\n\treturn\n}\n\nvar ConfigDir = sync.OnceValue(func() (config_dir string) {\n\treturn ConfigDirForName(\"kitty.conf\")\n})\n\nvar CacheDir = sync.OnceValue(func() (cache_dir string) {\n\tcandidate := \"\"\n\tif edir := os.Getenv(\"KITTY_CACHE_DIRECTORY\"); edir != \"\" {\n\t\tcandidate = Abspath(Expanduser(edir))\n\t} else if runtime.GOOS == \"darwin\" {\n\t\tcandidate = Expanduser(\"~/Library/Caches/kitty\")\n\t} else {\n\t\tcandidate = os.Getenv(\"XDG_CACHE_HOME\")\n\t\tif candidate == \"\" {\n\t\t\tcandidate = \"~/.cache\"\n\t\t}\n\t\tcandidate = filepath.Join(Expanduser(candidate), \"kitty\")\n\t}\n\t_ = os.MkdirAll(candidate, 0o755)\n\treturn candidate\n})\n\nfunc macos_user_cache_dir() string {\n\t// Sadly Go does not provide confstr() so we use this hack.\n\t// Note that given a user generateduid and uid we can derive this by using\n\t// the algorithm at https://github.com/ydkhatri/MacForensics/blob/master/darwin_path_generator.py\n\t// but I cant find a good way to get the generateduid. Requires calling dscl in which case we might as well call getconf\n\t// The data is in /var/db/dslocal/nodes/Default/users/<username>.plist but it needs root\n\t// So instead we use various hacks to get it quickly, falling back to running /usr/bin/getconf\n\n\tis_ok := func(m string) bool {\n\t\ts, err := os.Stat(m)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tstat, ok := s.Sys().(syscall.Stat_t)\n\t\treturn ok && s.IsDir() && int(stat.Uid) == os.Geteuid() && s.Mode().Perm() == 0o700 && unix.Access(m, unix.X_OK|unix.W_OK|unix.R_OK) == nil\n\t}\n\n\tif tdir := strings.TrimRight(os.Getenv(\"TMPDIR\"), \"/\"); filepath.Base(tdir) == \"T\" {\n\t\tif m := filepath.Join(filepath.Dir(tdir), \"C\"); is_ok(m) {\n\t\t\treturn m\n\t\t}\n\t}\n\n\tmatches, err := filepath.Glob(\"/private/var/folders/*/*/C\")\n\tif err == nil {\n\t\tfor _, m := range matches {\n\t\t\tif is_ok(m) {\n\t\t\t\treturn m\n\t\t\t}\n\t\t}\n\t}\n\tout, err := exec.Command(\"/usr/bin/getconf\", \"DARWIN_USER_CACHE_DIR\").Output()\n\tif err == nil {\n\t\treturn strings.TrimRight(strings.TrimSpace(UnsafeBytesToString(out)), \"/\")\n\t}\n\treturn \"\"\n}\n\nvar RuntimeDir = sync.OnceValue(func() (runtime_dir string) {\n\tvar candidate string\n\tif q := os.Getenv(\"KITTY_RUNTIME_DIRECTORY\"); q != \"\" {\n\t\tcandidate = q\n\t} else if runtime.GOOS == \"darwin\" {\n\t\tcandidate = macos_user_cache_dir()\n\t} else if q := os.Getenv(\"XDG_RUNTIME_DIR\"); q != \"\" {\n\t\tcandidate = q\n\t}\n\tcandidate = strings.TrimRight(candidate, \"/\")\n\tif candidate == \"\" {\n\t\tq := fmt.Sprintf(\"/run/user/%d\", os.Geteuid())\n\t\tif s, err := os.Stat(q); err == nil && s.IsDir() && unix.Access(q, unix.X_OK|unix.R_OK|unix.W_OK) == nil {\n\t\t\tcandidate = q\n\t\t} else {\n\t\t\tcandidate = filepath.Join(CacheDir(), \"run\")\n\t\t}\n\t}\n\tos.MkdirAll(candidate, 0o700)\n\tif s, err := os.Stat(candidate); err == nil && s.Mode().Perm() != 0o700 {\n\t\tos.Chmod(candidate, 0o700)\n\t}\n\treturn candidate\n})\n\ntype Walk_callback func(path, abspath string, d fs.DirEntry, err error) error\n\nfunc transform_symlink(path string) string {\n\tif q, err := filepath.EvalSymlinks(path); err == nil {\n\t\treturn q\n\t}\n\treturn path\n}\n\nfunc needs_symlink_recurse(path string, d fs.DirEntry) bool {\n\tif d.Type()&os.ModeSymlink == os.ModeSymlink {\n\t\tif s, serr := os.Stat(path); serr == nil && s.IsDir() {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\ntype transformed_walker struct {\n\tseen               map[string]bool\n\treal_callback      Walk_callback\n\ttransform_func     func(string) string\n\tneeds_recurse_func func(string, fs.DirEntry) bool\n}\n\nfunc (self *transformed_walker) walk(dirpath string) error {\n\tresolved_path := self.transform_func(dirpath)\n\tif self.seen[resolved_path] {\n\t\treturn nil\n\t}\n\tself.seen[resolved_path] = true\n\n\tc := func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\t// Happens if ReadDir on d failed, skip it in that case\n\t\t\treturn fs.SkipDir\n\t\t}\n\t\trpath, err := filepath.Rel(resolved_path, path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// we cant use filepath.Join here as it calls Clean() which can alter dirpath if it contains .. or . etc.\n\t\tpath_based_on_original_dir := dirpath\n\t\tif !strings.HasSuffix(dirpath, Sep) && dirpath != \"\" {\n\t\t\tpath_based_on_original_dir += Sep\n\t\t}\n\t\tpath_based_on_original_dir += rpath\n\t\tif self.needs_recurse_func(path, d) {\n\t\t\terr = self.walk(path_based_on_original_dir)\n\t\t} else {\n\t\t\terr = self.real_callback(path_based_on_original_dir, path, d, err)\n\t\t}\n\t\treturn err\n\t}\n\n\treturn filepath.WalkDir(resolved_path, c)\n}\n\n// Walk, recursing into symlinks that point to directories. Ignores directories\n// that could not be read.\nfunc WalkWithSymlink(dirpath string, callback Walk_callback, transformers ...func(string) string) error {\n\n\ttransform := func(path string) string {\n\t\tfor _, t := range transformers {\n\t\t\tpath = t(path)\n\t\t}\n\t\treturn transform_symlink(path)\n\t}\n\tsw := transformed_walker{\n\t\tseen: make(map[string]bool), real_callback: callback, transform_func: transform, needs_recurse_func: needs_symlink_recurse}\n\treturn sw.walk(dirpath)\n}\n\nfunc RandomFilename() string {\n\tb := []byte{0, 0, 0, 0, 0, 0, 0, 0}\n\t_, err := rand.Read(b)\n\tif err != nil {\n\t\treturn strconv.FormatUint(uint64(not_rand.Uint32()), 16)\n\t}\n\treturn base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b)\n\n}\n\nfunc ResolveConfPath(path string) string {\n\tcs := os.ExpandEnv(Expanduser(path))\n\tif !filepath.IsAbs(cs) {\n\t\tcs = filepath.Join(ConfigDir(), cs)\n\t}\n\treturn cs\n}\n\n// Longest common path. Must be passed paths that have been cleaned by filepath.Clean\nfunc Commonpath(paths ...string) (longest_prefix string) {\n\tswitch len(paths) {\n\tcase 0:\n\t\treturn\n\tcase 1:\n\t\treturn paths[0]\n\tdefault:\n\t\tsort.Strings(paths)\n\t\ta, b := paths[0], paths[len(paths)-1]\n\t\tsz := 0\n\t\tfor a != \"\" && b != \"\" {\n\t\t\tra, na := utf8.DecodeRuneInString(a)\n\t\t\trb, nb := utf8.DecodeRuneInString(b)\n\t\t\tif ra != rb {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tsz += na\n\t\t\ta = a[na:]\n\t\t\tb = b[nb:]\n\t\t}\n\t\tlongest_prefix = paths[0][:sz]\n\t}\n\treturn\n}\n\n// RelativeIfUnder returns the path to 'target' relative to 'base' if and only if\n// target is inside base. It returns the relative path, a boolean indicating\n// whether target is inside base, and an error.\n//\n// If resolveSymlinks is true, both base and target are run through filepath.EvalSymlinks\n// before containment checks. If base == target the function returns \".\" and true.\n//\n// Notes:\n//   - This uses filepath.Rel and then checks for leading \"..\" components to determine\n//     whether the returned relative path escapes the base directory.\n//   - On Windows behaviour is consistent with filepath semantics.\nfunc RelativeIfUnder(base, target string, resolveSymlinks bool) (rel string, inside bool, err error) {\n\t// Optionally resolve symlinks first\n\tif resolveSymlinks {\n\t\tif base, err = filepath.EvalSymlinks(base); err != nil {\n\t\t\treturn \"\", false, fmt.Errorf(\"resolving base symlinks: %w\", err)\n\t\t}\n\t\tif target, err = filepath.EvalSymlinks(target); err != nil {\n\t\t\treturn \"\", false, fmt.Errorf(\"resolving target symlinks: %w\", err)\n\t\t}\n\t}\n\n\t// Make absolute and clean\n\tif base, err = filepath.Abs(base); err != nil {\n\t\treturn \"\", false, fmt.Errorf(\"abs base: %w\", err)\n\t}\n\tif target, err = filepath.Abs(target); err != nil {\n\t\treturn \"\", false, fmt.Errorf(\"abs target: %w\", err)\n\t}\n\n\t// On Windows the volume (drive letter) must match. If they don't, the path is not inside.\n\tif runtime.GOOS == \"windows\" {\n\t\tif !strings.EqualFold(filepath.VolumeName(base), filepath.VolumeName(target)) {\n\t\t\treturn \"\", false, nil\n\t\t}\n\t}\n\n\t// Get the relative path from base to target\n\trel, err = filepath.Rel(base, target)\n\tif err != nil {\n\t\treturn \"\", false, fmt.Errorf(\"computing relative path: %w\", err)\n\t}\n\n\t// If rel begins with \"..\" (or is \"..\"), then target is outside base.\n\t// Use os.PathSeparator to be portable.\n\tup := \"..\" + string(os.PathSeparator)\n\tif rel == \"..\" || strings.HasPrefix(rel, up) {\n\t\treturn \"\", false, nil\n\t}\n\n\t// If the returned rel is empty (shouldn't normally happen), normalize to \".\"\n\tif rel == \"\" {\n\t\trel = \".\"\n\t}\n\treturn rel, true, nil\n}\n"
  },
  {
    "path": "tools/utils/random/random.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage random\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"math/big\"\n\n\t\"golang.org/x/exp/constraints\"\n)\n\nvar _ = fmt.Print\n\n// Return a random integer in the range [0, limit). limit must be > 0\nfunc Int[T constraints.Integer](limit T) T {\n\tb := big.NewInt(int64(limit))\n\tn, err := rand.Int(rand.Reader, b)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn T(n.Uint64())\n}\n\n// Return one of items randomnly\nfunc Choice[T any](items ...T) T {\n\treturn items[Int(len(items))]\n}\n\n// Write randomn bytes into the provided slice\nfunc Bytes(b []byte) {\n\tif _, err := rand.Read(b); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "tools/utils/regexp.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\nvar pat_cache = NewLRUCache[string, *regexp.Regexp](128)\n\ntype SubMatch struct {\n\tText       string\n\tStart, End int\n}\n\nfunc Compile(pat string) (*regexp.Regexp, error) {\n\treturn pat_cache.GetOrCreate(pat, regexp.Compile)\n}\n\nfunc MustCompile(pat string) *regexp.Regexp {\n\treturn pat_cache.MustGetOrCreate(pat, regexp.MustCompile)\n}\n\nfunc ReplaceAll(cpat *regexp.Regexp, str string, repl func(full_match string, groupdict map[string]SubMatch) string) string {\n\tresult := strings.Builder{}\n\tresult.Grow(len(str) + 256)\n\tlast_index := 0\n\tmatches := cpat.FindAllStringSubmatchIndex(str, -1)\n\tnames := cpat.SubexpNames()\n\tgroupdict := make(map[string]SubMatch, len(names))\n\tfor _, v := range matches {\n\t\tmatch_start, match_end := v[0], v[1]\n\t\tfull_match := str[match_start:match_end]\n\t\tfor k := range groupdict {\n\t\t\tdelete(groupdict, k)\n\t\t}\n\t\tfor i, name := range names {\n\t\t\tidx := 2 * i\n\t\t\tif v[idx] > -1 && v[idx+1] > -1 {\n\t\t\t\tgroupdict[name] = SubMatch{Text: str[v[idx]:v[idx+1]], Start: v[idx], End: v[idx+1]}\n\t\t\t}\n\t\t}\n\t\tresult.WriteString(str[last_index:match_start])\n\t\tresult.WriteString(repl(full_match, groupdict))\n\t\tlast_index = match_end\n\t}\n\tresult.WriteString(str[last_index:])\n\treturn result.String()\n}\n"
  },
  {
    "path": "tools/utils/ring.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\nfunc rb_min(a, b uint64) uint64 {\n\tif a < b {\n\t\treturn a\n\t} else {\n\t\treturn b\n\t}\n}\n\ntype RingBuffer[T any] struct {\n\tbuffer    []T\n\tread_pos  uint64\n\tuse_count uint64\n}\n\nfunc NewRingBuffer[T any](size uint64) *RingBuffer[T] {\n\treturn &RingBuffer[T]{\n\t\tbuffer: make([]T, size),\n\t}\n}\n\nfunc (self *RingBuffer[T]) Len() uint64 {\n\treturn self.use_count\n}\n\nfunc (self *RingBuffer[T]) Capacity() uint64 {\n\treturn uint64(len(self.buffer))\n}\n\nfunc (self *RingBuffer[T]) Clear() {\n\tself.read_pos = 0\n\tself.use_count = 0\n}\n\nfunc (self *RingBuffer[T]) Grow(new_size uint64) {\n\tif new_size <= self.Capacity() {\n\t\treturn\n\t}\n\tbuf := make([]T, new_size)\n\tif self.use_count > 0 {\n\t\tself.ReadTillEmpty(buf)\n\t}\n\tself.buffer = buf\n\tself.read_pos = 0\n}\n\nfunc (self *RingBuffer[T]) WriteTillFull(p ...T) uint64 {\n\tssz := self.Capacity()\n\tavailable := ssz - self.use_count\n\tsz := rb_min(uint64(len(p)), available)\n\tif sz == 0 {\n\t\treturn 0\n\t}\n\ttail := (self.read_pos + self.use_count) % ssz\n\twrite_end := (self.read_pos + self.use_count + sz) % ssz\n\tself.use_count += sz\n\tp = p[:sz]\n\tif tail <= write_end {\n\t\tcopy(self.buffer[tail:], p)\n\t} else {\n\t\tfirst_write := ssz - tail\n\t\tcopy(self.buffer[tail:], p[:first_write])\n\t\tcopy(self.buffer, p[first_write:])\n\t}\n\treturn sz\n}\n\nfunc (self *RingBuffer[T]) WriteAllAndDiscardOld(p ...T) {\n\tssz := self.Capacity()\n\tleft := uint64(len(p))\n\tif left >= ssz { // Fast path\n\t\textra := left - ssz\n\t\tcopy(self.buffer, p[extra:])\n\t\tself.read_pos = 0\n\t\tself.use_count = ssz\n\t\treturn\n\t}\n\tfor {\n\t\twritten := self.WriteTillFull(p...)\n\t\tp = p[written:]\n\t\tleft = uint64(len(p))\n\t\tif left == 0 {\n\t\t\tbreak\n\t\t}\n\t\tself.slices_to_read(left)\n\t}\n}\n\nfunc (self *RingBuffer[T]) ReadTillEmpty(p []T) uint64 {\n\ta, b := self.slices_to_read(uint64(len(p)))\n\tcopy(p, a)\n\tcopy(p[len(a):], b)\n\treturn uint64(len(a)) + uint64(len(b))\n}\n\nfunc (self *RingBuffer[T]) ReadAll() []T {\n\tans := make([]T, self.Len())\n\tself.ReadTillEmpty(ans)\n\treturn ans\n}\n\nfunc (self *RingBuffer[T]) slices_to_read(sz uint64) ([]T, []T) {\n\tssz := self.Capacity()\n\tsz = rb_min(sz, self.use_count)\n\thead := self.read_pos\n\tend_read := (head + sz) % ssz\n\tself.use_count -= sz\n\tself.read_pos = end_read\n\tif end_read > head || sz == 0 {\n\t\treturn self.buffer[head:end_read], self.buffer[0:0]\n\t}\n\tfirst_read := ssz - head\n\treturn self.buffer[head : head+first_read], self.buffer[0 : sz-first_read]\n}\n"
  },
  {
    "path": "tools/utils/ring_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestRingBuffer(t *testing.T) {\n\tr := NewRingBuffer[int](8)\n\n\ttest_contents := func(expected ...int) {\n\t\tactual := make([]int, len(expected))\n\t\tnum_read := r.ReadTillEmpty(actual)\n\t\tif num_read != uint64(len(actual)) {\n\t\t\tt.Fatalf(\"Did not read expected num of items: %d != %d\", num_read, len(expected))\n\t\t}\n\t\tif !reflect.DeepEqual(expected, actual) {\n\t\t\tt.Fatalf(\"Did not read expected items:\\n%#v != %#v\", actual, expected)\n\t\t}\n\t\tif r.Len() != 0 {\n\t\t\tt.Fatalf(\"Reading contents did not empty the buffer\")\n\t\t}\n\t}\n\tr.WriteTillFull(1, 2, 3, 4)\n\ttest_contents(1, 2, 3, 4)\n\tr.WriteTillFull(1, 2, 3, 4)\n\ttest_contents(1, 2, 3, 4)\n\n\tr.Clear()\n\tr.WriteTillFull(1, 2, 3, 4)\n\tr.ReadTillEmpty([]int{0, 1})\n\ttest_contents(3, 4)\n\tr.WriteTillFull(1, 2, 3, 4, 5)\n\ttest_contents(1, 2, 3, 4, 5)\n\n\tr.Clear()\n\tr.WriteTillFull(1, 2, 3, 4)\n\tr.WriteAllAndDiscardOld(5, 6, 7, 8, 9)\n\ttest_contents(2, 3, 4, 5, 6, 7, 8, 9)\n\n}\n"
  },
  {
    "path": "tools/utils/secrets/tokens.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage secrets\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\nconst DEFAULT_NUM_OF_BYTES_FOR_TOKEN = 32\n\nfunc TokenBytes(nbytes ...int) ([]byte, error) {\n\tif len(nbytes) == 0 {\n\t\tnbytes = []int{DEFAULT_NUM_OF_BYTES_FOR_TOKEN}\n\t}\n\tbuf := make([]byte, nbytes[0])\n\t_, err := rand.Read(buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf, nil\n}\n\nfunc TokenHex(nbytes ...int) (string, error) {\n\tb, err := TokenBytes(nbytes...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn hex.EncodeToString(b), nil\n}\n\nfunc TokenBase64(nbytes ...int) (string, error) {\n\tb, err := TokenBytes(nbytes...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.StdEncoding.EncodeToString(b), nil\n}\n"
  },
  {
    "path": "tools/utils/select.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\ntype Selector struct {\n\tread_set, write_set, err_set unix.FdSet\n\tread_fds, write_fds, err_fds map[int]bool\n}\n\nfunc CreateSelect(expected_number_of_fds int) *Selector {\n\tvar ans Selector\n\tans.read_fds = make(map[int]bool, expected_number_of_fds)\n\tans.write_fds = make(map[int]bool, expected_number_of_fds)\n\tans.err_fds = make(map[int]bool, expected_number_of_fds)\n\treturn &ans\n}\n\nfunc (self *Selector) register(fd int, fdset *map[int]bool) {\n\t(*fdset)[fd] = true\n}\n\nfunc (self *Selector) RegisterRead(fd int) {\n\tself.register(fd, &self.read_fds)\n}\n\nfunc (self *Selector) RegisterWrite(fd int) {\n\tself.register(fd, &self.write_fds)\n}\n\nfunc (self *Selector) RegisterError(fd int) {\n\tself.register(fd, &self.err_fds)\n}\n\nfunc (self *Selector) unregister(fd int, fdset *map[int]bool) {\n\t(*fdset)[fd] = false\n}\n\nfunc (self *Selector) UnRegisterRead(fd int) {\n\tself.unregister(fd, &self.read_fds)\n}\n\nfunc (self *Selector) UnRegisterWrite(fd int) {\n\tself.unregister(fd, &self.write_fds)\n}\n\nfunc (self *Selector) UnRegisterError(fd int) {\n\tself.unregister(fd, &self.err_fds)\n}\n\nfunc (self *Selector) Wait(timeout time.Duration) (num_ready int, err error) {\n\tmax_fd_num := 0\n\n\tinit_set := func(s *unix.FdSet, m map[int]bool) {\n\t\ts.Zero()\n\t\tfor fd, enabled := range m {\n\t\t\tif fd > -1 && enabled {\n\t\t\t\tif max_fd_num < fd {\n\t\t\t\t\tmax_fd_num = fd\n\t\t\t\t}\n\t\t\t\ts.Set(fd)\n\t\t\t}\n\t\t}\n\t}\n\tinit_set(&self.read_set, self.read_fds)\n\tinit_set(&self.write_set, self.write_fds)\n\tinit_set(&self.err_set, self.err_fds)\n\tnum_ready, err = Select(max_fd_num+1, &self.read_set, &self.write_set, &self.err_set, timeout)\n\tif err == unix.EINTR {\n\t\tself.read_set.Zero()\n\t\tself.write_set.Zero()\n\t\tself.err_set.Zero()\n\t\treturn 0, err\n\t}\n\treturn\n}\n\nfunc (self *Selector) WaitForever() (num_ready int, err error) {\n\treturn self.Wait(-1)\n}\n\nfunc (self *Selector) IsReadyToRead(fd int) bool {\n\treturn fd > -1 && self.read_set.IsSet(fd)\n}\n\nfunc (self *Selector) IsReadyToWrite(fd int) bool {\n\treturn fd > -1 && self.write_set.IsSet(fd)\n}\n\nfunc (self *Selector) IsErrored(fd int) bool {\n\treturn fd > -1 && self.err_set.IsSet(fd)\n}\n\nfunc (self *Selector) UnregisterAll() {\n\tself.read_fds = make(map[int]bool)\n\tself.write_fds = make(map[int]bool)\n\tself.err_fds = make(map[int]bool)\n}\n"
  },
  {
    "path": "tools/utils/select_posix.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\n//go:build linux\n\npackage utils\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc Select(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout time.Duration) (n int, err error) {\n\tif timeout < 0 {\n\t\treturn unix.Pselect(nfd, r, w, e, nil, nil)\n\t}\n\tts := unix.NsecToTimespec(int64(timeout))\n\treturn unix.Pselect(nfd, r, w, e, &ts, nil)\n}\n"
  },
  {
    "path": "tools/utils/select_without_pselect.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\n//go:build !linux\n\npackage utils\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\n// Go unix does not wrap pselect on darwin\n\nfunc Select(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout time.Duration) (n int, err error) {\n\tif timeout < 0 {\n\t\treturn unix.Select(nfd, r, w, e, nil)\n\t}\n\tts := unix.NsecToTimeval(int64(timeout))\n\treturn unix.Select(nfd, r, w, e, &ts)\n}\n"
  },
  {
    "path": "tools/utils/set.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\nfunc Keys[M ~map[K]V, K comparable, V any](m M) []K {\n\tr := make([]K, 0, len(m))\n\tfor k := range m {\n\t\tr = append(r, k)\n\t}\n\treturn r\n}\n\nfunc Values[M ~map[K]V, K comparable, V any](m M) []V {\n\tr := make([]V, 0, len(m))\n\tfor _, k := range m {\n\t\tr = append(r, k)\n\t}\n\treturn r\n}\n\ntype Set[T comparable] struct {\n\titems map[T]struct{}\n}\n\nfunc (self *Set[T]) Add(val T) {\n\tself.items[val] = struct{}{}\n}\n\nfunc (self *Set[T]) AddItems(val ...T) {\n\tfor _, x := range val {\n\t\tself.items[x] = struct{}{}\n\t}\n}\n\nfunc (self *Set[T]) String() string {\n\treturn fmt.Sprintf(\"%#v\", Keys(self.items))\n}\n\nfunc (self *Set[T]) Remove(val T) {\n\tdelete(self.items, val)\n}\n\nfunc (self *Set[T]) Discard(val T) {\n\tdelete(self.items, val)\n}\n\nfunc (self *Set[T]) Has(val T) bool {\n\t_, ok := self.items[val]\n\treturn ok\n}\n\nfunc (self *Set[T]) Clear() {\n\tclear(self.items)\n}\n\nfunc (self *Set[T]) Len() int {\n\treturn len(self.items)\n}\n\nfunc (self *Set[T]) ForEach(f func(T)) {\n\tfor x := range self.items {\n\t\tf(x)\n\t}\n}\n\nfunc (self *Set[T]) Iterable() map[T]struct{} {\n\treturn self.items\n}\n\nfunc (self *Set[T]) Any() T {\n\tfor x := range self.items {\n\t\treturn x\n\t}\n\tpanic(\"set is empty\")\n}\n\nfunc (self *Set[T]) AsSlice() []T {\n\treturn Keys(self.items)\n}\n\nfunc (self *Set[T]) Intersect(other *Set[T]) (ans *Set[T]) {\n\tif other == nil {\n\t\treturn NewSet[T]()\n\t}\n\tif self.Len() < other.Len() {\n\t\tans = NewSet[T](self.Len())\n\t\tfor x := range self.items {\n\t\t\tif _, ok := other.items[x]; ok {\n\t\t\t\tans.items[x] = struct{}{}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tans = NewSet[T](other.Len())\n\t\tfor x := range other.items {\n\t\t\tif _, ok := self.items[x]; ok {\n\t\t\t\tans.items[x] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Set[T]) Subtract(other *Set[T]) (ans *Set[T]) {\n\tans = NewSet[T](self.Len())\n\tfor x := range self.items {\n\t\tif other == nil || !other.Has(x) {\n\t\t\tans.items[x] = struct{}{}\n\t\t}\n\t}\n\treturn ans\n}\n\nfunc (self *Set[T]) IsSubsetOf(other *Set[T]) bool {\n\tif other == nil {\n\t\treturn self.Len() == 0\n\t}\n\tfor x := range self.items {\n\t\tif !other.Has(x) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (self *Set[T]) Equal(other *Set[T]) bool {\n\tl := self.Len()\n\tif other == nil {\n\t\treturn l == 0\n\t}\n\tif l != other.Len() {\n\t\treturn false\n\t}\n\tfor x := range self.items {\n\t\tif !other.Has(x) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc NewSet[T comparable](capacity ...int) (ans *Set[T]) {\n\tif len(capacity) == 0 {\n\t\tans = &Set[T]{items: make(map[T]struct{}, 8)}\n\t} else {\n\t\tans = &Set[T]{items: make(map[T]struct{}, capacity[0])}\n\t}\n\treturn\n}\n\nfunc NewSetWithItems[T comparable](items ...T) (ans *Set[T]) {\n\tans = NewSet[T](len(items))\n\tans.AddItems(items...)\n\treturn ans\n}\n"
  },
  {
    "path": "tools/utils/shell.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\n// Quotes arbitrary strings for bash, dash and zsh\nfunc QuoteStringForSH(x string) string {\n\tparts := strings.Split(x, \"'\")\n\tfor i, p := range parts {\n\t\tparts[i] = \"'\" + p + \"'\"\n\t}\n\treturn strings.Join(parts, \"\\\"'\\\"\")\n}\n\n// Quotes arbitrary strings for fish\nfunc QuoteStringForFish(x string) string {\n\tx = strings.ReplaceAll(x, \"\\\\\", \"\\\\\\\\\")\n\tx = strings.ReplaceAll(x, \"'\", \"\\\\'\")\n\treturn \"'\" + x + \"'\"\n}\n\n// Escapes common shell meta characters\nfunc EscapeSHMetaCharacters(x string) string {\n\tans := strings.Builder{}\n\tans.Grow(len(x) + 32)\n\tfor _, ch := range x {\n\t\tswitch ch {\n\t\tcase '\\\\', '|', '&', ';', '<', '>', '(', ')', '$', '\\'', '\"', ' ', '\\n', '\\t':\n\t\t\tans.WriteRune('\\\\')\n\t\t}\n\t\tans.WriteRune(ch)\n\t}\n\treturn ans.String()\n}\n"
  },
  {
    "path": "tools/utils/shlex/ansi_c_escapes.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage shlex\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\nvar _ = fmt.Print\n\ntype state int\n\nconst (\n\tnormal state = iota\n\tcontrol_char\n\tbackslash\n\thex_digit\n\toct_digit\n)\n\ntype ansi_c struct {\n\tstate                        state\n\tmax_num_of_digits, digit_idx int\n\tdigits                       [16]rune\n\toutput                       strings.Builder\n}\n\nfunc is_hex_char(ch rune) bool {\n\treturn ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')\n}\n\nfunc is_oct_char(ch rune) bool {\n\treturn '0' <= ch && ch <= '7'\n}\n\nfunc (self *ansi_c) write_digits(base int) {\n\tif self.digit_idx > 0 {\n\t\ttext := string(self.digits[:self.digit_idx])\n\t\tif val, err := strconv.ParseUint(text, base, 32); err == nil && val <= utf8.MaxRune {\n\t\t\tself.output.WriteRune(rune(val))\n\t\t}\n\t}\n\tself.digit_idx = 0\n\tself.state = normal\n}\n\nfunc (self *ansi_c) parse(ch rune) {\n\tswitch self.state {\n\tcase normal:\n\t\tif ch == '\\\\' {\n\t\t\tself.state = backslash\n\t\t} else {\n\t\t\tself.output.WriteRune(ch)\n\t\t}\n\tcase control_char:\n\t\tself.output.WriteRune(ch & 0x1f)\n\t\tself.state = normal\n\tcase hex_digit:\n\t\tif self.digit_idx < self.max_num_of_digits && is_hex_char(ch) {\n\t\t\tself.digits[self.digit_idx] = ch\n\t\t\tself.digit_idx++\n\t\t} else {\n\t\t\tself.write_digits(16)\n\t\t\tself.parse(ch)\n\t\t}\n\tcase oct_digit:\n\t\tif self.digit_idx < self.max_num_of_digits && is_oct_char(ch) {\n\t\t\tself.digits[self.digit_idx] = ch\n\t\t\tself.digit_idx++\n\t\t} else {\n\t\t\tself.write_digits(8)\n\t\t\tself.parse(ch)\n\t\t}\n\tcase backslash:\n\t\tself.state = normal\n\t\tswitch ch {\n\t\tdefault:\n\t\t\tself.output.WriteRune('\\\\')\n\t\t\tself.output.WriteRune(ch)\n\t\tcase 'a':\n\t\t\tself.output.WriteRune(7)\n\t\tcase 'b':\n\t\t\tself.output.WriteRune(8)\n\t\tcase 'c':\n\t\t\tself.state = control_char\n\t\tcase 'e', 'E':\n\t\t\tself.output.WriteRune(27)\n\t\tcase 'f':\n\t\t\tself.output.WriteRune(12)\n\t\tcase 'n':\n\t\t\tself.output.WriteRune(10)\n\t\tcase 'r':\n\t\t\tself.output.WriteRune(13)\n\t\tcase 't':\n\t\t\tself.output.WriteRune(9)\n\t\tcase 'v':\n\t\t\tself.output.WriteRune(11)\n\t\tcase 'x':\n\t\t\tself.max_num_of_digits, self.digit_idx, self.state = 2, 0, hex_digit\n\t\tcase 'u':\n\t\t\tself.max_num_of_digits, self.digit_idx, self.state = 4, 0, hex_digit\n\t\tcase 'U':\n\t\t\tself.max_num_of_digits, self.digit_idx, self.state = 8, 0, hex_digit\n\t\tcase '0', '1', '2', '3', '4', '5', '6', '7':\n\t\t\tself.max_num_of_digits, self.digit_idx, self.state = 3, 1, oct_digit\n\t\t\tself.digits[0] = ch\n\t\tcase '\\\\':\n\t\t\tself.output.WriteRune('\\\\')\n\t\tcase '?':\n\t\t\tself.output.WriteRune('?')\n\t\tcase '\"':\n\t\t\tself.output.WriteRune('\"')\n\t\tcase '\\'':\n\t\t\tself.output.WriteRune('\\'')\n\n\t\t}\n\t}\n}\n\nfunc (self *ansi_c) finish() string {\n\tswitch self.state {\n\tcase hex_digit:\n\t\tself.write_digits(16)\n\tcase oct_digit:\n\t\tself.write_digits(8)\n\tcase backslash:\n\t\tself.output.WriteRune('\\\\')\n\tcase control_char:\n\t\tself.output.WriteString(\"\\\\c\")\n\t}\n\tself.state = normal\n\tself.digit_idx = 0\n\ts := self.output.String()\n\tself.output.Reset()\n\treturn s\n}\n\nfunc ExpandANSICEscapes(src string) string {\n\tp := ansi_c{}\n\tp.output.Grow(len(src))\n\tfor _, ch := range src {\n\t\tp.parse(ch)\n\t}\n\treturn p.finish()\n}\n"
  },
  {
    "path": "tools/utils/shlex/shlex.go",
    "content": "/*\nPackage shlex implements a simple lexer which splits input in to tokens using\nshell-style rules for quoting.\n\nThe basic use case uses the default ASCII lexer to split a string into sub-strings:\n\n\tshlex.Split(\"one \\\"two three\\\" four\") -> []string{\"one\", \"two three\", \"four\"}\n\nTo process a stream of strings:\n\n\tl := NewLexer(os.Stdin)\n\tfor ; token, err := l.Next(); err != nil {\n\t\t// process token\n\t}\n*/\npackage shlex\n\nimport (\n\t\"fmt\"\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\ntype Word struct {\n\tValue   string // The word is empty if EOF is reached\n\tPos     int    // The position in the input string of the word or the trailer\n\tErr     error  // Indicates an error (unterminated string or trailing unescaped backslash)\n\tTrailer string // Extra trailing data such as an unterminated string or an unescaped backslash. Present only if Err != nil\n}\n\ntype lexer_state int\n\n// Lexer state machine states\nconst (\n\tlex_normal lexer_state = iota\n\tword\n\tstring_without_escapes\n\tstring_with_escapes\n)\n\n// Lexer turns an input stream into a sequence of tokens. Whitespace is skipped.\ntype Lexer struct {\n\tstate                       lexer_state\n\tsrc                         string\n\tsrc_sz, src_pos, word_start int\n\tbuf                         strings.Builder\n}\n\n// NewLexer creates a new lexer from an input string.\nfunc NewLexer(x string) *Lexer {\n\treturn &Lexer{src: x, src_sz: len(x)}\n}\n\nfunc (self *Lexer) start_word() {\n\tself.buf.Reset()\n\tself.word_start = self.src_pos - 1\n}\n\nfunc (self *Lexer) get_word() Word {\n\treturn Word{Pos: self.word_start, Value: self.buf.String()}\n}\n\nfunc (self *Lexer) write_ch(ch byte) {\n\tself.buf.WriteByte(ch)\n}\n\nfunc (self *Lexer) write_escaped_ch() bool {\n\tch, count := utf8.DecodeRuneInString(self.src[self.src_pos:])\n\tif count > 0 {\n\t\tself.src_pos += count\n\t\tif ch != utf8.RuneError {\n\t\t\tself.buf.WriteRune(ch)\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Next returns the next word. At EOF Word.Value will be \"\"\nfunc (self *Lexer) Next() (ans Word) {\n\tconst string_with_escapes_delim = '\"'\n\tconst string_without_escapes_delim = '\\''\n\tconst escape_char = '\\\\'\n\tfor self.src_pos < self.src_sz {\n\t\tch := self.src[self.src_pos]\n\t\tself.src_pos++\n\t\tswitch self.state {\n\t\tcase lex_normal:\n\t\t\tswitch ch {\n\t\t\tcase ' ', '\\n', '\\r', '\\t':\n\t\t\tcase string_with_escapes_delim:\n\t\t\t\tself.state = string_with_escapes\n\t\t\t\tself.start_word()\n\t\t\tcase string_without_escapes_delim:\n\t\t\t\tself.state = string_without_escapes\n\t\t\t\tself.start_word()\n\t\t\tcase escape_char:\n\t\t\t\tself.start_word()\n\t\t\t\tif !self.write_escaped_ch() {\n\t\t\t\t\tans.Trailer = \"\\\\\"\n\t\t\t\t\tans.Err = fmt.Errorf(\"Extra backslash at end of input\")\n\t\t\t\t\tans.Pos = self.word_start\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.state = word\n\t\t\tdefault:\n\t\t\t\tself.state = word\n\t\t\t\tself.start_word()\n\t\t\t\tself.write_ch(ch)\n\t\t\t}\n\t\tcase word:\n\t\t\tswitch ch {\n\t\t\tcase ' ', '\\n', '\\r', '\\t':\n\t\t\t\tself.state = lex_normal\n\t\t\t\tif self.buf.Len() > 0 {\n\t\t\t\t\treturn self.get_word()\n\t\t\t\t}\n\t\t\tcase string_with_escapes_delim:\n\t\t\t\tself.state = string_with_escapes\n\t\t\tcase string_without_escapes_delim:\n\t\t\t\tself.state = string_without_escapes\n\t\t\tcase escape_char:\n\t\t\t\tif !self.write_escaped_ch() {\n\t\t\t\t\tans.Pos = self.word_start\n\t\t\t\t\tans.Trailer = self.buf.String() + \"\\\\\"\n\t\t\t\t\tans.Err = fmt.Errorf(\"Extra backslash at end of input\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tself.write_ch(ch)\n\t\t\t}\n\t\tcase string_without_escapes:\n\t\t\tswitch ch {\n\t\t\tcase string_without_escapes_delim:\n\t\t\t\tself.state = word\n\t\t\tdefault:\n\t\t\t\tself.write_ch(ch)\n\t\t\t}\n\t\tcase string_with_escapes:\n\t\t\tswitch ch {\n\t\t\tcase string_with_escapes_delim:\n\t\t\t\tself.state = word\n\t\t\tcase escape_char:\n\t\t\t\tself.write_escaped_ch()\n\t\t\tdefault:\n\t\t\t\tself.write_ch(ch)\n\t\t\t}\n\t\t}\n\t}\n\tswitch self.state {\n\tcase word:\n\t\tself.state = lex_normal\n\t\tif self.buf.Len() > 0 {\n\t\t\treturn self.get_word()\n\t\t}\n\tcase string_with_escapes, string_without_escapes:\n\t\tself.state = lex_normal\n\t\tans.Trailer = self.buf.String()\n\t\tans.Pos = self.word_start\n\t\tans.Err = fmt.Errorf(\"Unterminated string at end of input\")\n\t\treturn\n\tcase lex_normal:\n\n\t}\n\treturn\n}\n\n// Split partitions a string into a slice of strings.\nfunc Split(s string) (ans []string, err error) {\n\tl := NewLexer(s)\n\tvar word Word\n\tfor {\n\t\tword = l.Next()\n\t\tif word.Err != nil {\n\t\t\treturn ans, word.Err\n\t\t}\n\t\tif word.Value == \"\" {\n\t\t\tbreak\n\t\t}\n\t\tans = append(ans, word.Value)\n\t}\n\treturn\n}\n\nfunc Quote(s string) string {\n\tif s == \"\" {\n\t\treturn s\n\t}\n\tif utils.MustCompile(`[^\\w@%+=:,./-]`).MatchString(s) {\n\t\treturn \"'\" + strings.ReplaceAll(s, \"'\", \"'\\\"'\\\"'\") + \"'\"\n\t}\n\treturn s\n}\n\n// SplitForCompletion partitions a string into a slice of strings. It differs from Split in being\n// more relaxed about errors and also adding an empty string at the end if s ends with a Space.\nfunc SplitForCompletion(s string) (argv []string, position_of_last_arg int) {\n\tt := NewLexer(s)\n\targv = make([]string, 0, len(s)/4)\n\tfor {\n\t\tword := t.Next()\n\t\tif word.Value == \"\" {\n\t\t\tif word.Trailer == \"\" {\n\t\t\t\ttrimmed := strings.TrimRight(s, \" \")\n\t\t\t\tif len(trimmed) < len(s) { // trailing spaces\n\t\t\t\t\tpos := position_of_last_arg\n\t\t\t\t\tif len(argv) > 0 {\n\t\t\t\t\t\tpos += len(argv[len(argv)-1])\n\t\t\t\t\t}\n\t\t\t\t\tif pos < len(s) { // trailing whitespace\n\t\t\t\t\t\targv = append(argv, \"\")\n\t\t\t\t\t\tposition_of_last_arg += len(s) - pos + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\targv = append(argv, word.Trailer)\n\t\t\t\tposition_of_last_arg = word.Pos\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tposition_of_last_arg = word.Pos\n\t\targv = append(argv, word.Value)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tools/utils/shlex/shlex_test.go",
    "content": "package shlex\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar (\n\t// one two \"three four\" \"five \\\"six\\\"\" seven#eight # nine # ten\n\t// eleven 'twelve\\'\n\ttestString = \"one two \\\"three four\\\" \\\"five \\\\\\\"six\\\\\\\"\\\" seven#eight # nine # ten eleven 'twelve\\\\' thirteen=13 fourteen/14\"\n)\n\nfunc TestLexer(t *testing.T) {\n\ttestInput := testString\n\texpectedStrings := []string{\"one\", \"two\", \"three four\", \"five \\\"six\\\"\", \"seven#eight\", \"#\", \"nine\", \"#\", \"ten\", \"eleven\", \"twelve\\\\\", \"thirteen=13\", \"fourteen/14\"}\n\n\tlexer := NewLexer(testInput)\n\tfor i, want := range expectedStrings {\n\t\tgot := lexer.Next()\n\t\tif got.Value != want {\n\t\t\tt.Errorf(\"Lexer.Next()[%v] of %q -> %v. Want: %v\", i, testString, got, want)\n\t\t}\n\t}\n}\n\ntype Tok struct {\n\tPos int\n\tVal string\n}\n\nfunc TestSplit(t *testing.T) {\n\twant := []string{\"one\", \"two\", \"three four\", \"five \\\"six\\\"\", \"seven#eight\", \"#\", \"nine\", \"#\", \"ten\", \"eleven\", \"twelve\\\\\", \"thirteen=13\", \"fourteen/14\"}\n\tgot, err := Split(testString)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif len(want) != len(got) {\n\t\tt.Errorf(\"Split(%q) -> %v. Want: %v\", testString, got, want)\n\t}\n\tfor i := range got {\n\t\tif got[i] != want[i] {\n\t\t\tt.Errorf(\"Split(%q)[%v] -> %v. Want: %v\", testString, i, got[i], want[i])\n\t\t}\n\t}\n\n\tfor _, x := range []string{\n\t\t`abc\\`, `\\`, `'abc`, `'`, `\"`, `asd\\`,\n\t} {\n\t\t_, err := Split(x)\n\t\tif err == nil {\n\t\t\tt.Fatalf(\"Failed to get an error for: %#v\", x)\n\t\t}\n\t}\n\ts := func(q string) (ans []Tok) {\n\t\tl := NewLexer(q)\n\t\tfor {\n\t\t\tw := l.Next()\n\t\t\tif w.Err != nil {\n\t\t\t\tt.Fatal(w.Err)\n\t\t\t}\n\t\t\tif w.Value == \"\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tans = append(ans, Tok{w.Pos, w.Value})\n\t\t}\n\t\treturn\n\t}\n\tfor q, expected := range map[string][]Tok{\n\t\t`\"ab\"`:          {{0, \"ab\"}},\n\t\t`x \"ab\"y \\m`:    {{0, `x`}, {2, `aby`}, {8, `m`}},\n\t\t`x'y\"\\z'1`:      {{0, `xy\"\\z1`}},\n\t\t`\\abc\\ d`:       {{0, `abc d`}},\n\t\t``:              nil,\n\t\t`   `:           nil,\n\t\t\" \\tabc\\n\\t\\r \": {{2, \"abc\"}},\n\t} {\n\t\tif diff := cmp.Diff(expected, s(q)); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed for string: %#v\\n%s\", q, diff)\n\t\t}\n\t}\n\n}\n\nfunc TestSplitForCompletion(t *testing.T) {\n\ttest := func(cmdline string, last_arg_pos int, expected ...string) {\n\t\tactual, actual_pos := SplitForCompletion(cmdline)\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to split: %s\\n%s\", cmdline, diff)\n\t\t}\n\t\tif last_arg_pos != actual_pos {\n\t\t\tt.Fatalf(\"Failed to split: %#v\\n Last arg pos: %d != %d\", cmdline, last_arg_pos, actual_pos)\n\t\t}\n\t}\n\ttest(\"a b\", 2, \"a\", \"b\")\n\ttest(\"a b \", 4, \"a\", \"b\", \"\")\n\ttest(\"a b  \", 5, \"a\", \"b\", \"\")\n\ttest(`a \"b c\"`, 2, \"a\", \"b c\")\n\ttest(`a \"b c`, 2, \"a\", \"b c\")\n}\n\nfunc TestExpandANSICEscapes(t *testing.T) {\n\tvar m = map[string]string{\n\t\t\"abc\":       \"abc\",\n\t\t`a\\ab`:      \"a\\ab\",\n\t\t`a\\eb`:      \"a\\x1bb\",\n\t\t`a\\r\\nb`:    \"a\\r\\nb\",\n\t\t`a\\c b`:     \"a\\000b\",\n\t\t`a\\c`:       \"a\\\\c\",\n\t\t`a\\x1bb`:    \"a\\x1bb\",\n\t\t`a\\x1b`:     \"a\\x1b\",\n\t\t`a\\x1`:      \"a\\x01\",\n\t\t`a\\x1\\\\`:    \"a\\x01\\\\\",\n\t\t`a\\x1g`:     \"a\\x01g\",\n\t\t`a\\z\\\"`:     \"a\\\\z\\\"\",\n\t\t`a\\123b`:    \"a\\123b\",\n\t\t`a\\128b`:    \"a\\0128b\",\n\t\t`a\\u1234e`:  \"a\\u1234e\",\n\t\t`a\\U1f1eez`: \"a\\U0001f1eez\",\n\t}\n\tfor q, expected := range m {\n\t\tactual := ExpandANSICEscapes(q)\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to process: %#v\\n%s\", q, diff)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "tools/utils/short-uuid.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"crypto/rand\"\n\t\"math\"\n\t\"math/big\"\n\n\t\"github.com/ALTree/bigfloat\"\n\t\"github.com/google/uuid\"\n)\n\nconst (\n\tESCAPE_CODE_SAFE_ALPHABET = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~ \"\n\tHUMAN_ALPHABET            = \"23456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n)\n\nfunc num_to_string(number *big.Int, alphabet []rune, alphabet_len *big.Int, pad_to_length int) string {\n\tvar zero, digit big.Int\n\tif number.Sign() < 0 {\n\t\t*number = zero\n\t}\n\tcapacity := max(pad_to_length, 64)\n\tans := make([]rune, 0, capacity)\n\tfor number.Cmp(&zero) == 1 {\n\t\tnumber.DivMod(number, alphabet_len, &digit)\n\t\tans = append(ans, alphabet[digit.Uint64()])\n\t}\n\tal := len(ans)\n\tif pad_to_length > -1 && al < pad_to_length {\n\t\tans = ans[:pad_to_length]\n\t\tfor i := al; i < pad_to_length; i++ {\n\t\t\tans[i] = alphabet[0]\n\t\t}\n\t}\n\treturn string(ans)\n}\n\nfunc get_padding_length(alphabet_len int) int {\n\tbi := big.NewInt(2)\n\tbi = bi.Exp(bi, big.NewInt(128), nil)\n\tbb := new(big.Float).SetPrec(256)\n\tbb.SetInt(bi)\n\tlog_al := bigfloat.Log(big.NewFloat(float64(alphabet_len)).SetPrec(256))\n\tlog_b := bigfloat.Log(bb)\n\tres := new(big.Float).SetPrec(256)\n\tres = res.Quo(log_b, log_al)\n\tval, _ := res.Float64()\n\treturn int(math.Ceil(val))\n}\n\ntype ShortUUID struct {\n\talphabet      []rune\n\talphabet_len  big.Int\n\tpad_to_length int\n}\n\nfunc CreateShortUUID(alphabet string) *ShortUUID {\n\tif alphabet == \"\" {\n\t\talphabet = HUMAN_ALPHABET\n\t}\n\tvar ans = ShortUUID{\n\t\talphabet: []rune(alphabet),\n\t}\n\tans.pad_to_length = get_padding_length(len(ans.alphabet))\n\tans.alphabet_len.SetUint64(uint64(len(ans.alphabet)))\n\treturn &ans\n}\n\nfunc (self *ShortUUID) Random(num_bits int64) (string, error) {\n\tmax := big.NewInt(0).Exp(big.NewInt(2), big.NewInt(num_bits), nil)\n\tbi, err := rand.Int(rand.Reader, max)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn num_to_string(bi, self.alphabet, &self.alphabet_len, self.pad_to_length), nil\n}\n\nfunc (self *ShortUUID) Uuid4() (string, error) {\n\tb, err := uuid.NewRandom()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tbb, err := b.MarshalBinary()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar bi big.Int\n\tbi.SetBytes(bb)\n\treturn num_to_string(&bi, self.alphabet, &self.alphabet_len, self.pad_to_length), nil\n}\n\nvar HumanUUID *ShortUUID\n\nfunc HumanUUID4() (string, error) {\n\tif HumanUUID == nil {\n\t\tHumanUUID = CreateShortUUID(HUMAN_ALPHABET)\n\t}\n\treturn HumanUUID.Uuid4()\n}\n\nfunc HumanRandomId(num_bits int64) (string, error) {\n\tif HumanUUID == nil {\n\t\tHumanUUID = CreateShortUUID(HUMAN_ALPHABET)\n\t}\n\treturn HumanUUID.Random(num_bits)\n}\n"
  },
  {
    "path": "tools/utils/short-uuid_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"math/big\"\n\t\"testing\"\n)\n\nfunc TestShortUUID(t *testing.T) {\n\ta, err := HumanRandomId(128)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tb, err := HumanRandomId(128)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif a == b {\n\t\tt.Fatalf(\"Two short uuid4's are unexpectedly equal\")\n\t}\n\tif HumanUUID.pad_to_length != 22 {\n\t\tt.Fatalf(\"pad length for human UUID is %d not %d\", HumanUUID.pad_to_length, 22)\n\t}\n\tu, err := HumanUUID.Uuid4()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(u) != 22 {\n\t\tt.Fatalf(\"uuid4 %s has unexpected length: %d\", u, len(u))\n\t}\n\n\tbi := big.NewInt(int64(1234567890123456789))\n\tq := num_to_string(bi, HumanUUID.alphabet, &HumanUUID.alphabet_len, HumanUUID.pad_to_length)\n\tconst expected = \"bzT6LtUjw4422222222222\"\n\tif q != expected {\n\t\tt.Fatalf(\"unexpected short human serialization: %s != %s\", q, expected)\n\t}\n}\n"
  },
  {
    "path": "tools/utils/sockets.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/seancfoley/ipaddress-go/ipaddr\"\n)\n\nfunc ParseSocketAddress(spec string) (network string, addr string, err error) {\n\tnetwork, addr, found := strings.Cut(spec, \":\")\n\tif !found {\n\t\terr = fmt.Errorf(\"Invalid socket address: %s must be prefix by a protocol such as unix:\", spec)\n\t\treturn\n\t}\n\tif network == \"unix\" {\n\t\tif strings.HasPrefix(addr, \"@\") && runtime.GOOS != \"linux\" {\n\t\t\terr = fmt.Errorf(\"Abstract UNIX sockets are only supported on Linux. Cannot use: %s\", spec)\n\t\t}\n\t\treturn\n\t}\n\n\tif network == \"tcp\" || network == \"tcp6\" || network == \"tcp4\" {\n\t\thost := ipaddr.NewHostName(addr)\n\t\tif host.IsAddress() {\n\t\t\tnetwork = \"ip\"\n\t\t}\n\t\treturn\n\t}\n\tif network == \"ip\" || network == \"ip6\" || network == \"ip4\" {\n\t\thost := ipaddr.NewHostName(addr)\n\t\tif !host.IsAddress() {\n\t\t\terr = fmt.Errorf(\"Not a valid IP address: %#v. Cannot use: %s\", addr, spec)\n\t\t}\n\t\treturn\n\t}\n\tif network == \"fd\" {\n\t\tfd := -1\n\t\tif fd, err = strconv.Atoi(addr); err != nil || fd < 0 {\n\t\t\terr = fmt.Errorf(\"Not a valid file descriptor number: %#v. Cannot use: %s\", addr, spec)\n\t\t}\n\t\treturn\n\t}\n\terr = fmt.Errorf(\"Unknown network type: %#v in socket address: %s\", network, spec)\n\treturn\n}\n"
  },
  {
    "path": "tools/utils/sockets_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"testing\"\n)\n\nfunc TestParseSocketAddress(t *testing.T) {\n\ten := \"unix\"\n\tea := \"/tmp/test\"\n\tvar eerr error = nil\n\n\ttest := func(spec string) {\n\t\tn, a, err := ParseSocketAddress(spec)\n\t\tif err != eerr {\n\t\t\tif eerr == nil {\n\t\t\t\tt.Fatalf(\"Parsing of %s failed with unexpected error: %s\", spec, err)\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tt.Fatalf(\"Parsing of %s did not fail, unexpectedly\", spec)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif a != ea {\n\t\t\tt.Fatalf(\"actual != expected, %s != %s, when parsing %s\", a, ea, spec)\n\t\t}\n\t\tif n != en {\n\t\t\tt.Fatalf(\"actual != expected, %s != %s, when parsing %s\", n, en, spec)\n\t\t}\n\t}\n\n\ttestf := func(spec string, netw string, addr string) {\n\t\teerr = nil\n\t\ten = netw\n\t\tea = addr\n\t\ttest(spec)\n\t}\n\tteste := func(spec string, e string) {\n\t\teerr = fmt.Errorf(\"%s\", e)\n\t\ttest(spec)\n\t}\n\n\ttest(\"unix:/tmp/test\")\n\tif runtime.GOOS == \"linux\" {\n\t\tea = \"@test\"\n\t} else {\n\t\teerr = fmt.Errorf(\"bad kitty\")\n\t}\n\ttest(\"unix:@test\")\n\ttestf(\"tcp:localhost:123\", \"tcp\", \"localhost:123\")\n\ttestf(\"tcp:1.1.1.1:123\", \"ip\", \"1.1.1.1:123\")\n\ttestf(\"tcp:fe80::1\", \"ip\", \"fe80::1\")\n\tteste(\"xxx\", \"bad kitty\")\n\tteste(\"xxx:yyy\", \"bad kitty\")\n\tteste(\":yyy\", \"bad kitty\")\n}\n"
  },
  {
    "path": "tools/utils/stream_decompressor.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n)\n\nvar _ = fmt.Print\n\ntype StreamDecompressor = func(chunk []byte, is_last bool) error\n\ntype pipe_reader struct {\n\tpr *io.PipeReader\n}\n\nfunc (self *pipe_reader) Read(b []byte) (n int, err error) {\n\t// ensure the decompressor code never gets a zero byte read with no error\n\tfor len(b) > 0 {\n\t\tn, err = self.pr.Read(b)\n\t\tif err != nil || n > 0 {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// Wrap Go's awful decompressor routines to allow feeding them\n// data in chunks. For example:\n// sd := NewStreamDecompressor(zlib.NewReader, output)\n// sd(chunk, false)\n// ...\n// sd(last_chunk, true)\n// after this call, calling sd() further will just return io.EOF.\n// To close the decompressor at any time, call sd(nil, true).\n// Note: output.Write() may be called from a different thread, but only while the main thread is in sd()\nfunc NewStreamDecompressor(constructor func(io.Reader) (io.ReadCloser, error), output io.Writer) StreamDecompressor {\n\tif constructor == nil { // identity decompressor\n\t\tvar err error\n\t\treturn func(chunk []byte, is_last bool) error {\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif len(chunk) > 0 {\n\t\t\t\t_, err = output.Write(chunk)\n\t\t\t}\n\t\t\tif is_last {\n\t\t\t\tif err == nil {\n\t\t\t\t\terr = io.EOF\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\tpipe_r, pipe_w := io.Pipe()\n\tpr := pipe_reader{pr: pipe_r}\n\tfinished := make(chan error, 1)\n\tfinished_err := errors.New(\"finished\")\n\tgo func() {\n\t\tvar err error\n\t\tdefer func() {\n\t\t\tfinished <- err\n\t\t}()\n\t\tvar impl io.ReadCloser\n\t\timpl, err = constructor(&pr)\n\t\tif err != nil {\n\t\t\tpipe_r.CloseWithError(err)\n\t\t\treturn\n\t\t}\n\t\t_, err = io.Copy(output, impl)\n\t\tcerr := impl.Close()\n\t\tif err == nil {\n\t\t\terr = cerr\n\t\t}\n\t\tif err == nil {\n\t\t\terr = finished_err\n\t\t}\n\t\tpipe_r.CloseWithError(err)\n\t}()\n\n\tvar iter_err error\n\treturn func(chunk []byte, is_last bool) error {\n\t\tif iter_err != nil {\n\t\t\tif iter_err == finished_err {\n\t\t\t\titer_err = io.EOF\n\t\t\t}\n\t\t\treturn iter_err\n\t\t}\n\t\tif len(chunk) > 0 {\n\t\t\tvar n int\n\t\t\tn, iter_err = pipe_w.Write(chunk)\n\t\t\tif iter_err != nil && iter_err != finished_err {\n\t\t\t\treturn iter_err\n\t\t\t}\n\t\t\tif n < len(chunk) {\n\t\t\t\titer_err = io.ErrShortWrite\n\t\t\t\treturn iter_err\n\t\t\t}\n\t\t\t// wait for output to finish\n\t\t\tif iter_err == nil {\n\t\t\t\t// after a zero byte read, pipe_reader.Read() calls pipe_r.Read() again so\n\t\t\t\t// we know it is either blocked waiting for a write to pipe_w or has finished\n\t\t\t\t_, iter_err = pipe_w.Write(nil)\n\t\t\t\tif iter_err != nil && iter_err != finished_err {\n\t\t\t\t\treturn iter_err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif is_last {\n\t\t\tpipe_w.CloseWithError(io.EOF)\n\t\t\terr := <-finished\n\t\t\tif err != nil && err != io.EOF && err != finished_err {\n\t\t\t\titer_err = err\n\t\t\t\treturn err\n\t\t\t}\n\t\t\titer_err = io.EOF\n\t\t\treturn nil\n\t\t}\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "tools/utils/stream_decompressor_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestStreamDecompressor(t *testing.T) {\n\tinput := make([]byte, 9723)\n\tio.ReadFull(rand.Reader, input)\n\tb := bytes.Buffer{}\n\tw := zlib.NewWriter(&b)\n\tio.Copy(w, bytes.NewReader(input))\n\tw.Close()\n\to := bytes.Buffer{}\n\tsd := NewStreamDecompressor(zlib.NewReader, &o)\n\tdata := b.Bytes()\n\tfor len(data) > 0 {\n\t\tchunk := data[:Min(117, len(data))]\n\t\tdata = data[len(chunk):]\n\t\tif err := sd(chunk, len(data) == 0); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\tif !bytes.Equal(o.Bytes(), input) {\n\t\tt.Fatalf(\"Roundtripping via zlib failed output (%d) != input (%d)\", len(o.Bytes()), len(input))\n\t}\n\n\to.Reset()\n\tsd = NewStreamDecompressor(zlib.NewReader, &o)\n\terr := sd([]byte(\"abcd\"), true)\n\tif err == nil {\n\t\tt.Fatalf(\"Did not get an invalid header error from zlib\")\n\t}\n\n\to.Reset()\n\tsd = NewStreamDecompressor(zlib.NewReader, &o)\n\terr = sd(b.Bytes(), false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Equal(o.Bytes(), input) {\n\t\tt.Fatalf(\"Roundtripping via zlib failed output (%d) != input (%d)\", len(o.Bytes()), len(input))\n\t}\n\terr = sd([]byte(\"extra trailing data\"), true)\n\tif err == nil {\n\t\tt.Fatalf(\"Did not get an invalid header error from zlib\")\n\t}\n}\n"
  },
  {
    "path": "tools/utils/strings.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\nvar _ = fmt.Print\n\nfunc Capitalize(x string) string {\n\tif x == \"\" {\n\t\treturn x\n\t}\n\ts, sz := utf8.DecodeRuneInString(x)\n\tcr := strings.ToUpper(string(s))\n\treturn cr + x[sz:]\n}\n\ntype ScanLines struct {\n\tentries []string\n\n\tscanner *StringScanner\n}\n\nfunc NewScanLines(entries ...string) *ScanLines {\n\treturn &ScanLines{entries: entries}\n}\n\nfunc (self *ScanLines) Scan() bool {\n\tif self.scanner == nil {\n\t\tif len(self.entries) == 0 {\n\t\t\treturn false\n\t\t}\n\t\tself.scanner = NewLineScanner(self.entries[0])\n\t\tself.entries = self.entries[1:]\n\t\treturn self.Scan()\n\t} else {\n\t\tif self.scanner.Scan() {\n\t\t\treturn true\n\t\t}\n\t\tself.scanner = nil\n\t\treturn self.Scan()\n\t}\n}\n\nfunc (self *ScanLines) Text() string {\n\tif self.scanner == nil {\n\t\treturn \"\"\n\t}\n\treturn self.scanner.Text()\n}\n\ntype StringScannerScanFunc = func(data string) (remaining_data, token string)\ntype StringScannerPostprocessFunc = func(token string) string\n\nfunc ScanFuncForSeparator(sep string) StringScannerScanFunc {\n\tif len(sep) == 1 {\n\t\tsb := sep[0]\n\t\treturn func(data string) (remaining_data, token string) {\n\t\t\tidx := strings.IndexByte(data, sb)\n\t\t\tif idx < 0 {\n\t\t\t\treturn \"\", data\n\t\t\t}\n\t\t\treturn data[idx+len(sep):], data[:idx]\n\t\t}\n\n\t}\n\treturn func(data string) (remaining_data, token string) {\n\t\tidx := strings.Index(data, sep)\n\t\tif idx < 0 {\n\t\t\treturn \"\", data\n\t\t}\n\t\treturn data[idx+len(sep):], data[:idx]\n\t}\n}\n\n// Faster, better designed, zero-allocation version of bufio.Scanner for strings\ntype StringScanner struct {\n\tScanFunc             StringScannerScanFunc\n\tPostProcessTokenFunc StringScannerPostprocessFunc\n\n\tdata  string\n\ttoken string\n}\n\nfunc (self *StringScanner) Scan() bool {\n\tif self.data == \"\" {\n\t\tself.token = \"\"\n\t\treturn false\n\t}\n\tself.data, self.token = self.ScanFunc(self.data)\n\tif self.PostProcessTokenFunc != nil {\n\t\tself.token = self.PostProcessTokenFunc(self.token)\n\t}\n\treturn true\n}\n\nfunc (self *StringScanner) Err() error { return nil }\n\nfunc (self *StringScanner) Text() string {\n\treturn self.token\n}\n\nfunc (self *StringScanner) Split(data string, expected_number ...int) (ans []string) {\n\tif len(expected_number) != 0 {\n\t\tans = make([]string, 0, expected_number[0])\n\t} else {\n\t\tans = []string{}\n\t}\n\tself.data = data\n\tfor self.Scan() {\n\t\tans = append(ans, self.Text())\n\t}\n\treturn\n}\n\nfunc NewLineScanner(text string) *StringScanner {\n\treturn &StringScanner{\n\t\tdata: text, ScanFunc: ScanFuncForSeparator(\"\\n\"),\n\t\tPostProcessTokenFunc: func(s string) string {\n\t\t\tif len(s) > 0 && s[len(s)-1] == '\\r' {\n\t\t\t\ts = s[:len(s)-1]\n\t\t\t}\n\t\t\treturn s\n\t\t},\n\t}\n}\n\nfunc NewSeparatorScanner(text, separator string) *StringScanner {\n\treturn &StringScanner{\n\t\tdata: text, ScanFunc: ScanFuncForSeparator(separator),\n\t}\n}\n\nfunc Splitlines(x string, expected_number_of_lines ...int) (ans []string) {\n\treturn NewLineScanner(\"\").Split(x, expected_number_of_lines...)\n}\n\n// Return a function that can be called sequentially with rune based offsets\n// converting them to byte based offsets. The rune offsets must be monotonic,\n// otherwise the function returns -1\nfunc RuneOffsetsToByteOffsets(text string) func(int) int {\n\tself := struct {\n\t\tchar_offset, byte_offset, last int\n\t\tbytes                          []byte\n\t}{bytes: UnsafeStringToBytes(text)}\n\treturn func(x int) (sz int) {\n\t\tswitch {\n\t\tcase x == self.last:\n\t\t\treturn self.byte_offset\n\t\tcase x < self.last:\n\t\t\treturn -1\n\t\t}\n\t\tself.last = x\n\t\tx -= self.char_offset\n\t\tfor x > 0 {\n\t\t\t_, d := utf8.DecodeRune(self.bytes)\n\t\t\tsz += d\n\t\t\tself.bytes = self.bytes[d:]\n\t\t\tx--\n\t\t\tself.char_offset++\n\t\t}\n\t\tself.byte_offset += sz\n\t\treturn self.byte_offset\n\t}\n}\n\nfunc Repr(x any) string { return fmt.Sprintf(\"%#v\", x) }\n"
  },
  {
    "path": "tools/utils/strings_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestStringScanner(t *testing.T) {\n\tfor _, text := range []string{\n\t\t\"a\\nb\\nc\",\n\t\t\"a\\nb\\nc\\r\",\n\t\t\"a\\n\\n\\nb\\nc\",\n\t\t\"a\\r\\r\\nb\\r\\nc\\n\",\n\t\t\"\\n1\",\n\t\t\"\",\n\t\t\"\\n\",\n\t} {\n\t\tactual := Splitlines(text)\n\t\texpected := make([]string, 0, len(actual))\n\t\ts := bufio.NewScanner(strings.NewReader(text))\n\t\tfor s.Scan() {\n\t\t\texpected = append(expected, s.Text())\n\t\t}\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed for: %#v\\n%s\", text, diff)\n\t\t}\n\t}\n}\n\nfunc TestReplaceControlCodes(t *testing.T) {\n\tfor text, expected := range map[string]string{\n\t\t\"none\":                    \"none\",\n\t\t\"a\\r\\x01b\\x03\\x7f c\\n\\td\": \"a\\u240d\\u2401b\\u2403\\u2421 cX  d\",\n\t\t\"\\x01\":                    \"\\u2401\",\n\t\t\"\\x00\\x0b\":                \"\\u2400\\u240b\",\n\t} {\n\t\tactual := ReplaceControlCodes(text, \"  \", \"X\")\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed for text: %#v\\n%s\", text, diff)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/utils/style/api.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage style\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Context struct {\n\tAllowEscapeCodes bool\n}\n\nfunc (self *Context) SprintFunc(spec string) func(args ...any) string {\n\tp := prefix_for_spec(spec)\n\ts := suffix_for_spec(spec)\n\n\treturn func(args ...any) string {\n\t\tbody := fmt.Sprint(args...)\n\t\tif !self.AllowEscapeCodes {\n\t\t\treturn body\n\t\t}\n\t\tb := strings.Builder{}\n\t\tb.Grow(len(p) + len(body) + len(s))\n\t\tb.WriteString(p)\n\t\tb.WriteString(body)\n\t\tb.WriteString(s)\n\t\treturn b.String()\n\t}\n}\n\nfunc (self *Context) UrlFunc(spec string) func(string, string) string {\n\tp := prefix_for_spec(spec)\n\ts := suffix_for_spec(spec)\n\n\treturn func(url, text string) string {\n\t\tif !self.AllowEscapeCodes {\n\t\t\treturn text\n\t\t}\n\t\tuc := url_code{url: url}\n\t\tup, us := uc.prefix(), uc.suffix()\n\t\tb := strings.Builder{}\n\t\tb.Grow(len(p) + len(up) + len(text) + len(s) + len(us))\n\t\tb.WriteString(p)\n\t\tb.WriteString(up)\n\t\tb.WriteString(text)\n\t\tb.WriteString(us)\n\t\tb.WriteString(s)\n\t\treturn b.String()\n\t}\n}\n"
  },
  {
    "path": "tools/utils/style/colorspaces.go",
    "content": "// License: GPLv3 Copyright: 2025, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage style\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar _ = fmt.Println\n\n// Color space conversion functions for wide gamut color support\n// Implements OKLCH, Display P3, and CIE LAB color formats with\n// CSS Color Module Level 4 gamut mapping.\n\n// srgbToLinear converts sRGB component (0-1) to linear light\nfunc srgbToLinear(c float64) float64 {\n\tif c <= 0.04045 {\n\t\treturn c / 12.92\n\t}\n\treturn math.Pow((c+0.055)/1.055, 2.4)\n}\n\n// linearToSrgb converts linear light component (0-1) to sRGB\nfunc linearToSrgb(c float64) float64 {\n\tif c <= 0.0031308 {\n\t\treturn c * 12.92\n\t}\n\treturn 1.055*math.Pow(c, 1.0/2.4) - 0.055\n}\n\n// oklabToLinearSrgb converts OKLab to linear sRGB\nfunc oklabToLinearSrgb(l, a, b float64) (float64, float64, float64) {\n\tl_ := l + 0.3963377774*a + 0.2158037573*b\n\tm_ := l - 0.1055613458*a - 0.0638541728*b\n\ts_ := l - 0.0894841775*a - 1.2914855480*b\n\n\tl_cubed := l_ * l_ * l_\n\tm_cubed := m_ * m_ * m_\n\ts_cubed := s_ * s_ * s_\n\n\tr := +4.0767416621*l_cubed - 3.3077115913*m_cubed + 0.2309699292*s_cubed\n\tg := -1.2684380046*l_cubed + 2.6097574011*m_cubed - 0.3413193965*s_cubed\n\tb_val := -0.0041960863*l_cubed - 0.7034186147*m_cubed + 1.7076147010*s_cubed\n\n\treturn r, g, b_val\n}\n\n// oklchToSrgb converts OKLCH to sRGB (without gamut mapping)\nfunc oklchToSrgb(l, c, h float64) (float64, float64, float64) {\n\t// Convert OKLCH to OKLab\n\thRad := h * math.Pi / 180.0\n\ta := c * math.Cos(hRad)\n\tb := c * math.Sin(hRad)\n\n\t// Convert OKLab to linear sRGB\n\trLin, gLin, bLin := oklabToLinearSrgb(l, a, b)\n\n\t// Apply sRGB transfer function\n\tr := linearToSrgb(rLin)\n\tg := linearToSrgb(gLin)\n\tbVal := linearToSrgb(bLin)\n\n\treturn r, g, bVal\n}\n\n// srgbToOklab converts sRGB to OKLab (for deltaE calculations)\nfunc srgbToOklab(r, g, b float64) (float64, float64, float64) {\n\trLin := srgbToLinear(r)\n\tgLin := srgbToLinear(g)\n\tbLin := srgbToLinear(b)\n\n\tl_ := 0.4122214708*rLin + 0.5363325363*gLin + 0.0514459929*bLin\n\tm_ := 0.2119034982*rLin + 0.6806995451*gLin + 0.1073969566*bLin\n\ts_ := 0.0883024619*rLin + 0.2817188376*gLin + 0.6299787005*bLin\n\n\tl_ = math.Cbrt(l_)\n\tm_ = math.Cbrt(m_)\n\ts_ = math.Cbrt(s_)\n\n\tl := 0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_\n\ta := 1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_\n\tbVal := 0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_\n\n\treturn l, a, bVal\n}\n\n// deltaEOk calculates perceptual color difference in OKLab space\nfunc deltaEOk(lab1, lab2 [3]float64) float64 {\n\tdl := lab1[0] - lab2[0]\n\tda := lab1[1] - lab2[1]\n\tdb := lab1[2] - lab2[2]\n\treturn math.Sqrt(dl*dl + da*da + db*db)\n}\n\n// oklchToSrgbGamutMap converts OKLCH to sRGB with CSS Color Module Level 4 gamut mapping\nfunc oklchToSrgbGamutMap(l, c, h float64) (float64, float64, float64) {\n\t// Constants from CSS Color Module Level 4\n\tconst jnd = 0.02              // Just Noticeable Difference threshold\n\tconst minConvergence = 0.0001 // Binary search precision\n\tconst epsilon = 0.00001       // Small value for floating point comparisons\n\n\t// Edge cases: pure black or white\n\tif l <= 0.0 {\n\t\treturn 0.0, 0.0, 0.0\n\t}\n\tif l >= 1.0 {\n\t\treturn 1.0, 1.0, 1.0\n\t}\n\n\t// If chroma is very small, color is achromatic\n\tif c < epsilon {\n\t\tgray := linearToSrgb(l)\n\t\treturn gray, gray, gray\n\t}\n\n\t// Try the original color first\n\tr, g, b := oklchToSrgb(l, c, h)\n\n\t// Check if already in gamut\n\tif r >= 0.0 && r <= 1.0 && g >= 0.0 && g <= 1.0 && b >= 0.0 && b <= 1.0 {\n\t\treturn r, g, b\n\t}\n\n\t// Binary search for maximum in-gamut chroma\n\tlowChroma := 0.0\n\thighChroma := c\n\n\tfor (highChroma - lowChroma) > minConvergence {\n\t\tmidChroma := (highChroma + lowChroma) * 0.5\n\n\t\t// Try this chroma value\n\t\trTest, gTest, bTest := oklchToSrgb(l, midChroma, h)\n\n\t\t// Check if in gamut (before clipping)\n\t\tinGamut := rTest >= 0.0 && rTest <= 1.0 &&\n\t\t\tgTest >= 0.0 && gTest <= 1.0 &&\n\t\t\tbTest >= 0.0 && bTest <= 1.0\n\n\t\tif inGamut {\n\t\t\t// In gamut - try higher chroma\n\t\t\tlowChroma = midChroma\n\t\t} else {\n\t\t\t// Out of gamut - clip and check deltaE\n\t\t\trClipped := math.Max(0.0, math.Min(1.0, rTest))\n\t\t\tgClipped := math.Max(0.0, math.Min(1.0, gTest))\n\t\t\tbClipped := math.Max(0.0, math.Min(1.0, bTest))\n\n\t\t\t// Convert both to OKLab for comparison\n\t\t\tlTest, aTest, bTestLab := srgbToOklab(rTest, gTest, bTest)\n\t\t\ttestLab := [3]float64{lTest, aTest, bTestLab}\n\n\t\t\tlClip, aClip, bClip := srgbToOklab(rClipped, gClipped, bClipped)\n\t\t\tclippedLab := [3]float64{lClip, aClip, bClip}\n\n\t\t\t// Calculate perceptual difference\n\t\t\tde := deltaEOk(testLab, clippedLab)\n\n\t\t\tif de < jnd {\n\t\t\t\t// Difference is imperceptible - accept this chroma\n\t\t\t\tlowChroma = midChroma\n\t\t\t} else {\n\t\t\t\t// Difference is noticeable - reduce chroma more\n\t\t\t\thighChroma = midChroma\n\t\t\t}\n\t\t}\n\t}\n\n\t// Use the final chroma value and clip to ensure in-gamut\n\trFinal, gFinal, bFinal := oklchToSrgb(l, lowChroma, h)\n\treturn math.Max(0.0, math.Min(1.0, rFinal)),\n\t\tmath.Max(0.0, math.Min(1.0, gFinal)),\n\t\tmath.Max(0.0, math.Min(1.0, bFinal))\n}\n\n// labToOklch converts CIE LAB to OKLCH for gamut mapping\nfunc labToOklch(l, a, b float64) (float64, float64, float64) {\n\t// LAB to XYZ (using D65 illuminant)\n\ty := (l + 16) / 116\n\tx := a/500 + y\n\tz := y - b/200\n\n\tfInv := func(t float64) float64 {\n\t\tdelta := 6.0 / 29.0\n\t\tif t > delta {\n\t\t\treturn t * t * t\n\t\t}\n\t\treturn 3 * delta * delta * (t - 4.0/29.0)\n\t}\n\n\t// D65 white point\n\tconst xN = 0.95047\n\tconst yN = 1.00000\n\tconst zN = 1.08883\n\n\txVal := xN * fInv(x)\n\tyVal := yN * fInv(y)\n\tzVal := zN * fInv(z)\n\n\t// XYZ to linear sRGB\n\trLin := +3.2404542*xVal - 1.5371385*yVal - 0.4985314*zVal\n\tgLin := -0.9692660*xVal + 1.8760108*yVal + 0.0415560*zVal\n\tbLin := +0.0556434*xVal - 0.2040259*yVal + 1.0572252*zVal\n\n\t// Convert to OKLab\n\tl_ := 0.4122214708*rLin + 0.5363325363*gLin + 0.0514459929*bLin\n\tm_ := 0.2119034982*rLin + 0.6806995451*gLin + 0.1073969566*bLin\n\ts_ := 0.0883024619*rLin + 0.2817188376*gLin + 0.6299787005*bLin\n\n\tl_ = math.Cbrt(l_)\n\tm_ = math.Cbrt(m_)\n\ts_ = math.Cbrt(s_)\n\n\tlOk := 0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_\n\taOk := 1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_\n\tbOk := 0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_\n\n\t// Convert OKLab to OKLCH\n\tc := math.Sqrt(aOk*aOk + bOk*bOk)\n\th := math.Atan2(bOk, aOk) * 180.0 / math.Pi\n\tif h < 0 {\n\t\th += 360\n\t}\n\n\treturn lOk, c, h\n}\n\n// parseOklch parses OKLCH color: oklch(l c h) or oklch(l, c, h)\nfunc parseOklch(spec string) (RGBA, error) {\n\tspec = strings.TrimRight(spec, \")\")\n\tparts := splitColorComponents(spec)\n\n\tif len(parts) != 3 {\n\t\treturn RGBA{}, errors.New(\"not enough parts\")\n\t}\n\n\tl, err := parseFloatValue(parts[0])\n\tif err != nil {\n\t\treturn RGBA{}, err\n\t}\n\tc, err := parseFloatValue(parts[1])\n\tif err != nil {\n\t\treturn RGBA{}, err\n\t}\n\th, err := parseFloatValue(parts[2])\n\tif err != nil {\n\t\treturn RGBA{}, err\n\t}\n\n\t// Validate for NaN and infinity\n\tif math.IsNaN(l) || math.IsInf(l, 0) ||\n\t\tmath.IsNaN(c) || math.IsInf(c, 0) ||\n\t\tmath.IsNaN(h) || math.IsInf(h, 0) {\n\t\treturn RGBA{}, errors.New(\"invalid float value\")\n\t}\n\n\t// Handle percentages for L\n\tif strings.Contains(parts[0], \"%\") {\n\t\tl /= 100.0\n\t}\n\n\t// Clamp to reasonable ranges\n\tl = max(0.0, min(l, 1.0))\n\tc = max(0.0, c)      // Chroma is unbounded\n\th = math.Mod(h, 360) // Wrap hue to 0-360\n\tif h < 0 {\n\t\th += 360\n\t}\n\n\t// Convert OKLCH to sRGB with gamut mapping\n\tr, g, b := oklchToSrgbGamutMap(l, c, h)\n\treturn RGBA{as8bit(r), as8bit(g), as8bit(b), 0}, nil\n}\n\nfunc as8bit(x float64) uint8 { return uint8(math.Round(x * 255)) }\n\n// parseLab parses LAB color: lab(l a b) or lab(l, a, b)\nfunc parseLab(spec string) (RGBA, error) {\n\tspec = strings.TrimRight(spec, \")\")\n\tparts := splitColorComponents(spec)\n\n\tif len(parts) != 3 {\n\t\treturn RGBA{}, errors.New(\"not enough parts\")\n\t}\n\n\tl, err := parseFloatValue(parts[0])\n\tif err != nil {\n\t\treturn RGBA{}, err\n\t}\n\ta, err := parseFloatValue(parts[1])\n\tif err != nil {\n\t\treturn RGBA{}, err\n\t}\n\tb, err := parseFloatValue(parts[2])\n\tif err != nil {\n\t\treturn RGBA{}, err\n\t}\n\n\t// Validate for NaN and infinity\n\tif math.IsNaN(l) || math.IsInf(l, 0) ||\n\t\tmath.IsNaN(a) || math.IsInf(a, 0) ||\n\t\tmath.IsNaN(b) || math.IsInf(b, 0) {\n\t\treturn RGBA{}, errors.New(\"invalid float value\")\n\t}\n\n\t// Clamp L to 0-100\n\tl = max(0.0, min(l, 100.0))\n\n\t// Convert LAB to OKLCH, then use gamut mapping to sRGB\n\tlOk, c, h := labToOklch(l, a, b)\n\n\t// Apply gamut mapping in OKLCH space\n\tr, g, bVal := oklchToSrgbGamutMap(lOk, c, h)\n\treturn RGBA{as8bit(r), as8bit(g), as8bit(bVal), 0}, nil\n}\n\n// splitColorComponents splits color components by comma or whitespace\nfunc splitColorComponents(spec string) []string {\n\tre := regexp.MustCompile(`[,\\s]+`)\n\tparts := re.Split(spec, -1)\n\n\tvar result []string\n\tfor _, part := range parts {\n\t\tpart = strings.TrimSpace(part)\n\t\tpart = strings.TrimRight(part, \"%,\")\n\t\tif part != \"\" {\n\t\t\tresult = append(result, part)\n\t\t}\n\t}\n\treturn result\n}\n\n// parseFloatValue parses a float value, handling percentages\nfunc parseFloatValue(s string) (float64, error) {\n\ts = strings.TrimSpace(s)\n\ts = strings.TrimRight(s, \"%,\")\n\treturn strconv.ParseFloat(s, 64)\n}\n"
  },
  {
    "path": "tools/utils/style/colorspaces_bench_test.go",
    "content": "// License: GPLv3 Copyright: 2025, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage style\n\nimport (\n\t\"testing\"\n)\n\n// Benchmark color parsing functions to demonstrate performance\n\nfunc BenchmarkParseOklch(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = parseOklch(\"0.5 0.1 180\")\n\t}\n}\n\nfunc BenchmarkParseLab(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = parseLab(\"50 0 0\")\n\t}\n}\n\nfunc BenchmarkParseColorHex(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = ParseColor(\"#ff0000\")\n\t}\n}\n\nfunc BenchmarkParseColorOklch(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = ParseColor(\"oklch(0.5 0.1 180)\")\n\t}\n}\n\nfunc BenchmarkParseColorLab(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = ParseColor(\"lab(50 0 0)\")\n\t}\n}\n\nfunc BenchmarkParseColorWithComment(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = ParseColor(\"oklch(0.5 0.1 180) # vibrant color\")\n\t}\n}\n\n// Benchmark the gamut mapping algorithm specifically\nfunc BenchmarkOklchToSrgbGamutMap(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\toklchToSrgbGamutMap(0.7, 0.4, 25) // Very saturated color requiring gamut mapping\n\t}\n}\n\nfunc BenchmarkOklchToSrgbGamutMapInGamut(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\toklchToSrgbGamutMap(0.5, 0.05, 180) // Already in gamut\n\t}\n}\n\n// Benchmark parsing many colors (simulating config file parsing)\nfunc BenchmarkParseManyColors(b *testing.B) {\n\tcolors := []string{\n\t\t\"#ff0000\",\n\t\t\"#00ff00\",\n\t\t\"#0000ff\",\n\t\t\"oklch(0.5 0.1 180)\",\n\t\t\"lab(50 20 -30)\",\n\t\t\"rgb:ff/00/00\",\n\t\t\"red\",\n\t\t\"blue\",\n\t\t\"green\",\n\t}\n\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tfor _, color := range colors {\n\t\t\t_, _ = ParseColor(color)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/utils/style/colorspaces_test.go",
    "content": "// License: GPLv3 Copyright: 2025, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage style\n\nimport (\n\t\"testing\"\n)\n\nfunc TestParseColor(t *testing.T) {\n\ttype tr struct {\n\t\tinput    string\n\t\texpected RGBA\n\t}\n\tc := func(t string, r, g, b uint8) tr { return tr{t, RGBA{r, g, b, 0}} }\n\ttests := []tr{\n\t\tc(`#eee # comment`, 0xee, 0xee, 0xee),\n\t\tc(`#234567`, 0x23, 0x45, 0x67),\n\t\tc(`#abcabcdef`, 0xab, 0xab, 0xde),\n\t\tc(`rgb:e/e/e # comment`, 0xee, 0xee, 0xee),\n\t\tc(`rgb:23/45/67`, 0x23, 0x45, 0x67),\n\t\tc(`rgb:abc/abc/def`, 0xab, 0xab, 0xde),\n\t\tc(`red`, 0xff, 0, 0),\n\t\tc(`alice blue # comment`, 240, 248, 255),\n\t\tc(`oklch(1,0,0)`, 255, 255, 255),\n\t\tc(`oklch(0,0,0)`, 0, 0, 0),\n\t\tc(`oklch(0.5,0.1,180)`, 0, 117, 101),\n\t\tc(`oklch(0.7 0.15 140) # comment`, 0x68, 0xb4, 0x57),\n\t\tc(`oklch(0.9 0.05 265)`, 0xce, 0xde, 0xff),\n\t\tc(`lab(70 50 -30)`, 0xea, 0x88, 0xe2),\n\t\tc(`lab(50,0,0)`, 199, 199, 199),\n\t\tc(`lab(100,0,0)`, 255, 255, 255),\n\t\tc(`lab(0,0,0)`, 0, 0, 0),\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\tactual, err := ParseColor(tt.input)\n\t\t\tif actual != tt.expected {\n\t\t\t\tt.Errorf(\"ParseColor(%#v) error = %v, got %v wanted %v\", tt.input, err, actual, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/utils/style/indent-and-wrap.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage style\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n\t\"github.com/kovidgoyal/kitty/tools/wcswidth\"\n)\n\ntype sgr_color struct {\n\tnumber int\n\tcolor  RGBA\n}\n\nfunc (self sgr_color) as_sgr(base int) string {\n\tif self.number == 0 {\n\t\treturn \"\"\n\t}\n\tif self.number > 0 {\n\t\tn := self.number - 1\n\t\tif n <= 15 && (base == 30 || base == 40) {\n\t\t\tif n <= 7 {\n\t\t\t\treturn strconv.Itoa(base + n)\n\t\t\t}\n\t\t\treturn strconv.Itoa(base + 60 + n - 7)\n\t\t}\n\t\treturn fmt.Sprintf(\"%d:5:%d\", base+8, n)\n\t}\n\treturn fmt.Sprintf(\"%d:2:%d:%d:%d\", base+8, self.color.Red, self.color.Green, self.color.Blue)\n}\n\nfunc as_uint8(x int) uint8 {\n\treturn uint8(uint(x) & 0xff)\n}\n\nfunc (self *sgr_color) from_extended(nums []int) bool {\n\tswitch nums[0] {\n\tcase 5:\n\t\tif len(nums) > 1 {\n\t\t\tself.number = 1 + nums[1]\n\t\t\treturn true\n\t\t}\n\tcase 2:\n\t\tif len(nums) > 3 {\n\t\t\tself.number = -1\n\t\t\tself.color.Red = as_uint8(nums[1])\n\t\t\tself.color.Green = as_uint8(nums[2])\n\t\t\tself.color.Blue = as_uint8(nums[3])\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\ntype sgr_state struct {\n\titalic, reverse, bold, dim, strikethrough bool\n\tunderline_style                           underline_style\n\tfg, bg, uc                                sgr_color\n}\n\nfunc (self *sgr_state) reset() {\n\t*self = sgr_state{}\n}\n\nfunc (self sgr_state) as_sgr(for_close bool) string {\n\tans := make([]byte, 0, 32)\n\tif for_close {\n\t\tif self.bold {\n\t\t\tans = append(ans, \"221;\"...)\n\t\t}\n\t\tif self.dim {\n\t\t\tans = append(ans, \"222;\"...)\n\t\t}\n\t\tif self.italic {\n\t\t\tans = append(ans, \"23;\"...)\n\t\t}\n\t\tif self.reverse {\n\t\t\tans = append(ans, \"27;\"...)\n\t\t}\n\t\tif self.strikethrough {\n\t\t\tans = append(ans, \"29;\"...)\n\t\t}\n\t\tif self.underline_style != no_underline && self.underline_style != nil_underline {\n\t\t\tans = append(ans, \"4:0;\"...)\n\t\t}\n\t\tif self.fg.number != 0 {\n\t\t\tans = append(ans, \"39;\"...)\n\t\t}\n\t\tif self.bg.number != 0 {\n\t\t\tans = append(ans, \"49;\"...)\n\t\t}\n\t\tif self.uc.number != 0 {\n\t\t\tans = append(ans, \"59;\"...)\n\t\t}\n\t} else {\n\t\tif self.bold {\n\t\t\tans = append(ans, \"1;\"...)\n\t\t}\n\t\tif self.dim {\n\t\t\tans = append(ans, \"2;\"...)\n\t\t}\n\t\tif self.italic {\n\t\t\tans = append(ans, \"3;\"...)\n\t\t}\n\t\tif self.reverse {\n\t\t\tans = append(ans, \"7;\"...)\n\t\t}\n\t\tif self.strikethrough {\n\t\t\tans = append(ans, \"9;\"...)\n\t\t}\n\t\tif self.underline_style != no_underline && self.underline_style != nil_underline {\n\t\t\tans = append(ans, fmt.Sprintf(\"4:%d;\", self.underline_style)...)\n\t\t}\n\t\tif q := self.fg.as_sgr(30); q != \"\" {\n\t\t\tans = append(ans, q...)\n\t\t\tans = append(ans, ';')\n\t\t}\n\t\tif q := self.bg.as_sgr(40); q != \"\" {\n\t\t\tans = append(ans, q...)\n\t\t\tans = append(ans, ';')\n\t\t}\n\t\tif q := self.uc.as_sgr(50); q != \"\" {\n\t\t\tans = append(ans, q...)\n\t\t\tans = append(ans, ';')\n\t\t}\n\t}\n\tif len(ans) > 0 {\n\t\tans = ans[:len(ans)-1]\n\t}\n\treturn utils.UnsafeBytesToString(ans)\n}\n\nfunc (self sgr_state) as_escape_codes(for_close bool) string {\n\tq := self.as_sgr(for_close)\n\tif q == \"\" {\n\t\treturn q\n\t}\n\treturn fmt.Sprintf(\"\\x1b[%sm\", q)\n}\n\nfunc (self *sgr_state) apply_csi(raw string) {\n\tif !strings.HasSuffix(raw, \"m\") {\n\t\treturn\n\t}\n\traw = raw[:len(raw)-1]\n\tif raw == \"\" {\n\t\traw = \"0\"\n\t}\n\tparts := strings.Split(raw, \";\")\n\tnums := make([]int, 0, 8)\n\tfor _, part := range parts {\n\t\tsubparts := strings.Split(part, \":\")\n\t\tnums = nums[:0]\n\t\tfor _, b := range subparts {\n\t\t\tq, err := strconv.Atoi(b)\n\t\t\tif err == nil {\n\t\t\t\tnums = append(nums, q)\n\t\t\t}\n\t\t}\n\t\tif len(nums) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tswitch nums[0] {\n\t\tcase 0:\n\t\t\tself.reset()\n\t\tcase 1:\n\t\t\tself.dim, self.bold = false, true\n\t\tcase 221:\n\t\t\tself.bold = false\n\t\tcase 2:\n\t\t\tself.dim, self.bold = true, false\n\t\tcase 222:\n\t\t\tself.dim = false\n\t\tcase 22:\n\t\t\tself.dim, self.bold = false, false\n\t\tcase 3:\n\t\t\tself.italic = true\n\t\tcase 23:\n\t\t\tself.italic = false\n\t\tcase 7:\n\t\t\tself.reverse = true\n\t\tcase 27:\n\t\t\tself.reverse = false\n\t\tcase 9:\n\t\t\tself.strikethrough = true\n\t\tcase 29:\n\t\t\tself.strikethrough = false\n\t\tcase 24:\n\t\t\tself.underline_style = no_underline\n\t\tcase 4:\n\t\t\tus := 1\n\t\t\tif len(nums) > 1 {\n\t\t\t\tus = nums[1]\n\t\t\t}\n\t\t\tswitch us {\n\t\t\tcase 0:\n\t\t\t\tself.underline_style = no_underline\n\t\t\tcase 1:\n\t\t\t\tself.underline_style = straight_underline\n\t\t\tcase 2:\n\t\t\t\tself.underline_style = double_underline\n\t\t\tcase 3:\n\t\t\t\tself.underline_style = curly_underline\n\t\t\tcase 4:\n\t\t\t\tself.underline_style = dotted_underline\n\t\t\tcase 5:\n\t\t\t\tself.underline_style = dashed_underline\n\t\t\t}\n\t\tcase 30, 31, 32, 33, 34, 35, 36, 37:\n\t\t\tself.fg.number = nums[0] + 1 - 30\n\t\tcase 90, 91, 92, 93, 94, 95, 96, 97:\n\t\t\tself.fg.number = nums[0] + 1 - 82\n\t\tcase 38:\n\t\t\tself.fg.from_extended(nums[1:])\n\t\tcase 39:\n\t\t\tself.fg.number = 0\n\t\tcase 40, 41, 42, 43, 44, 45, 46, 47:\n\t\t\tself.bg.number = nums[0] + 1 - 40\n\t\tcase 100, 101, 102, 103, 104, 105, 106, 107:\n\t\t\tself.bg.number = nums[0] + 1 - 92\n\t\tcase 48:\n\t\t\tself.bg.from_extended(nums[1:])\n\t\tcase 49:\n\t\t\tself.bg.number = 0\n\t\tcase 58:\n\t\t\tself.uc.from_extended(nums[1:])\n\t\tcase 59:\n\t\t\tself.uc.number = 0\n\t\t}\n\t}\n}\n\ntype hyperlink_state struct {\n\tid, url string\n}\n\nfunc (self *hyperlink_state) apply_osc(raw string) {\n\tparts := strings.SplitN(raw, \";\", 3)\n\tif len(parts) != 3 || parts[0] != \"8\" {\n\t\treturn\n\t}\n\tself.id = parts[1]\n\tself.url = parts[2]\n}\n\nfunc (self *hyperlink_state) reset() {\n\tself.id = \"\"\n\tself.url = \"\"\n}\n\nfunc (self hyperlink_state) as_escape_codes(for_close bool) string {\n\tif self.id == \"\" && self.url == \"\" {\n\t\treturn \"\"\n\t}\n\tif for_close {\n\t\treturn \"\\x1b]8;;\\x1b\\\\\"\n\t}\n\treturn fmt.Sprintf(\"\\x1b]8;%s;%s\\x1b\\\\\", self.id, self.url)\n}\n\ntype line_builder struct {\n\tbuf                        []byte\n\tcursor_pos                 int\n\tseen_non_space_chars       bool\n\tpos_of_trailing_whitespace int\n}\n\nfunc (self *line_builder) reset(trim_whitespace bool) string {\n\tans := string(self.buf)\n\tif trim_whitespace && self.pos_of_trailing_whitespace > -1 {\n\t\tprefix := ans[:self.pos_of_trailing_whitespace]\n\t\ttp := strings.TrimRightFunc(prefix, is_space)\n\t\tif len(tp) != len(prefix) {\n\t\t\tans = tp + ans[self.pos_of_trailing_whitespace:]\n\t\t}\n\n\t}\n\tself.buf = self.buf[:0]\n\tself.cursor_pos = 0\n\tself.seen_non_space_chars = false\n\tself.pos_of_trailing_whitespace = -1\n\treturn ans\n}\n\nfunc (self *line_builder) has_space_for_width(w, max_width int) bool {\n\treturn w+self.cursor_pos <= max_width\n}\n\nfunc (self *line_builder) add_space(ch rune, trim_whitespace bool) {\n\tif !trim_whitespace || self.seen_non_space_chars {\n\t\tself.buf = utf8.AppendRune(self.buf, ch)\n\t\tself.pos_of_trailing_whitespace = len(self.buf)\n\t\tself.cursor_pos += wcswidth.Runewidth(ch)\n\t}\n}\n\nfunc (self *line_builder) add_word(word []byte, width int) {\n\tself.seen_non_space_chars = true\n\tself.pos_of_trailing_whitespace = -1\n\tself.buf = append(self.buf, word...)\n\tself.cursor_pos += width\n}\n\nfunc (self *line_builder) add_indent(word string, width int) {\n\tif word != \"\" {\n\t\tself.buf = append(self.buf, word...)\n\t\tself.cursor_pos += width\n\t}\n}\n\nfunc (self *line_builder) add_escape_code(code string) {\n\tself.buf = append(self.buf, code...)\n}\n\nfunc (self *line_builder) add_escape_code2(prefix string, body []byte, suffix string) {\n\tself.buf = append(self.buf, prefix...)\n\tself.buf = append(self.buf, body...)\n\tself.buf = append(self.buf, suffix...)\n}\n\ntype escape_code_ struct {\n\tprefix, body, suffix string\n}\n\ntype word_builder struct {\n\tbuf                 []byte\n\tescape_codes        []escape_code_\n\ttext_start_position int\n\twcswidth            *wcswidth.WCWidthIterator\n}\n\nfunc (self *word_builder) reset(copy_current_word func([]byte)) {\n\tcopy_current_word(self.buf)\n\tself.buf = self.buf[:0]\n\tself.escape_codes = self.escape_codes[:0]\n\tself.text_start_position = 0\n\tself.wcswidth.Reset()\n}\n\nfunc (self *word_builder) is_empty() bool {\n\treturn len(self.buf) == 0\n}\n\nfunc (self *word_builder) width() int {\n\treturn self.wcswidth.CurrentWidth()\n}\n\nfunc (self *word_builder) add_escape_code(prefix string, body []byte, suffix string) {\n\te := escape_code_{prefix: prefix, body: string(body), suffix: suffix}\n\tself.escape_codes = append(self.escape_codes, e)\n\tself.buf = append(self.buf, prefix...)\n\tself.buf = append(self.buf, body...)\n\tself.buf = append(self.buf, suffix...)\n}\n\nfunc (self *word_builder) has_text() bool { return self.text_start_position != 0 }\n\nfunc (self *word_builder) recalculate_width() {\n\tself.wcswidth.Reset()\n\tself.wcswidth.Parse(self.buf)\n}\n\nfunc (self *word_builder) add_rune(ch rune) (num_bytes_written int) {\n\tbefore := len(self.buf)\n\tself.buf = utf8.AppendRune(self.buf, ch)\n\tnum_bytes_written = len(self.buf) - before\n\tfor _, b := range self.buf[before:] {\n\t\tself.wcswidth.ParseByte(b)\n\t}\n\tif self.text_start_position == 0 {\n\t\tself.text_start_position = len(self.buf)\n\t}\n\treturn\n}\n\nfunc (self *word_builder) remove_trailing_bytes(n int) {\n\tself.buf = self.buf[:len(self.buf)-n]\n\tself.recalculate_width()\n}\n\ntype wrapper struct {\n\tep                  wcswidth.EscapeCodeParser\n\tindent              string\n\twidth, indent_width int\n\ttrim_whitespace     bool\n\n\tsgr                     sgr_state\n\thyperlink               hyperlink_state\n\tcurrent_word            word_builder\n\tcurrent_line            line_builder\n\tlines                   []string\n\tignore_lines_containing []string\n}\n\nfunc (self *wrapper) newline_prefix() {\n\tself.current_line.add_escape_code(self.sgr.as_escape_codes(true))\n\tself.current_line.add_escape_code(self.hyperlink.as_escape_codes(true))\n\tself.current_line.add_indent(self.indent, self.indent_width)\n\tself.current_line.add_escape_code(self.sgr.as_escape_codes(false))\n\tself.current_line.add_escape_code(self.hyperlink.as_escape_codes(false))\n}\n\nfunc (self *wrapper) append_line(line string) {\n\tfor _, q := range self.ignore_lines_containing {\n\t\tif strings.Contains(line, q) {\n\t\t\treturn\n\t\t}\n\t}\n\tself.lines = append(self.lines, line)\n}\n\nfunc (self *wrapper) end_current_line() {\n\tline := self.current_line.reset(self.trim_whitespace)\n\tif strings.HasSuffix(line, self.indent) && wcswidth.Stringwidth(line) == self.indent_width {\n\t\tline = line[:len(line)-len(self.indent)]\n\t}\n\tself.append_line(line)\n\tself.newline_prefix()\n}\n\nfunc (self *wrapper) print_word() {\n\tw := self.current_word.width()\n\tif !self.current_line.has_space_for_width(w, self.width) {\n\t\tself.end_current_line()\n\t\tw = self.current_word.width()\n\t}\n\tfor _, e := range self.current_word.escape_codes {\n\t\tif e.suffix != \"\" {\n\t\t\tself.hyperlink.apply_osc(e.body)\n\t\t} else {\n\t\t\tself.sgr.apply_csi(e.body)\n\t\t}\n\t}\n\tself.current_word.reset(func(word []byte) {\n\t\tself.current_line.add_word(word, w)\n\t})\n}\n\nfunc is_space(ch rune) bool {\n\treturn ch != 0xa0 && unicode.IsSpace(ch)\n}\n\nfunc (self *wrapper) handle_rune(ch rune) error {\n\tif ch == '\\n' {\n\t\tself.print_word()\n\t\tself.end_current_line()\n\t} else if is_space(ch) {\n\t\tif self.current_word.has_text() {\n\t\t\tself.print_word()\n\t\t}\n\t\tif self.current_line.cursor_pos >= self.width {\n\t\t\tself.end_current_line()\n\t\t}\n\t\tself.current_line.add_space(ch, self.trim_whitespace)\n\t} else {\n\t\tnum_of_bytes_written := self.current_word.add_rune(ch)\n\t\tif self.current_word.width() > self.width {\n\t\t\tself.current_word.remove_trailing_bytes(num_of_bytes_written)\n\t\t\tself.print_word()\n\t\t\treturn self.handle_rune(ch)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *wrapper) handle_csi(raw []byte) error {\n\tself.current_word.add_escape_code(\"\\x1b[\", raw, \"\")\n\treturn nil\n}\n\nfunc (self *wrapper) handle_osc(raw []byte) error {\n\tself.current_word.add_escape_code(\"\\x1b]\", raw, \"\\x1b\\\\\")\n\treturn nil\n}\n\nfunc (self *wrapper) wrap_text(text string) []string {\n\tif text == \"\" {\n\t\treturn []string{\"\"}\n\t}\n\tself.current_line.reset(self.trim_whitespace)\n\tself.current_word.reset(func([]byte) {})\n\tself.lines = self.lines[:0]\n\tself.current_line.add_indent(self.indent, self.indent_width)\n\tself.ep.ParseString(text)\n\tif !self.current_word.is_empty() {\n\t\tself.print_word()\n\t}\n\tself.end_current_line()\n\tlast_line := self.current_line.reset(self.trim_whitespace)\n\tself.newline_prefix()\n\tif last_line == self.current_line.reset(self.trim_whitespace) {\n\t\tlast_line = \"\"\n\t}\n\tif last_line != \"\" {\n\t\tself.append_line(last_line)\n\t}\n\treturn self.lines\n}\n\nfunc new_wrapper(opts WrapOptions, width int) *wrapper {\n\twidth = max(2, width)\n\tans := wrapper{indent: opts.Indent, width: width, trim_whitespace: opts.Trim_whitespace, indent_width: wcswidth.Stringwidth(opts.Indent)}\n\tif opts.Ignore_lines_containing != \"\" {\n\t\tans.ignore_lines_containing = utils.Splitlines(opts.Ignore_lines_containing)\n\t}\n\tans.ep.HandleRune = ans.handle_rune\n\tans.ep.HandleCSI = ans.handle_csi\n\tans.ep.HandleOSC = ans.handle_osc\n\tans.lines = make([]string, 0, 32)\n\tans.current_word.escape_codes = make([]escape_code_, 0, 8)\n\tans.current_word.wcswidth = wcswidth.CreateWCWidthIterator()\n\treturn &ans\n}\n\ntype WrapOptions struct {\n\tIgnore_lines_containing string\n\tTrim_whitespace         bool   // trim whitespace at the start and end of lines (start is after indent).\n\tIndent                  string // indent to add at the start of every line all formatting is cleared for the indent.\n}\n\nfunc WrapTextAsLines(text string, width int, opts WrapOptions) []string {\n\tw := new_wrapper(opts, width)\n\treturn w.wrap_text(text)\n}\n\nfunc WrapText(text string, width int, opts WrapOptions) string {\n\treturn strings.Join(WrapTextAsLines(text, width, opts), \"\\n\")\n}\n"
  },
  {
    "path": "tools/utils/style/indent-and-wrap_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage style\n\nimport (\n\t\"encoding/json\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestFormatWithIndent(t *testing.T) {\n\n\tscreen_width := 0\n\topts := WrapOptions{}\n\n\ttx := func(text string, expected ...string) {\n\t\tq := opts.Indent + strings.Join(expected, \"\")\n\t\tactual := WrapText(text, screen_width, opts)\n\t\tif actual != q {\n\t\t\tos, _ := json.Marshal(opts)\n\t\t\tt.Fatalf(\"\\nFailed for: %#v\\nOptions: %s\\nexpected: %#v\\nactual:   %#v\", text, os, q, actual)\n\t\t}\n\t}\n\n\topts.Indent = \"\"\n\tscreen_width = 4\n\ttx(\"one two\", \"one \\ntwo\")\n\ttx(\"a  b\", \"a  b\")\n\tscreen_width = 3\n\ttx(\"one tw\", \"one\\n tw\")\n\n\tscreen_width = 4\n\topts.Trim_whitespace = true\n\topts.Indent = \"X\"\n\ttx(\"one two\", \"one\\nXtwo\")\n\ttx(\"\\x1b[2mone \\x1b[mtwo\", \"\\x1b[2mone\\n\\x1b[222mX\\x1b[2m\\x1b[mtwo\")\n\tscreen_width = 3\n\ttx(\"on tw\", \"on\\nXtw\")\n\topts.Indent = \"\"\n\topts.Trim_whitespace = false\n\n\topts.Indent = \"__\"\n\tscreen_width = 11\n\ttx(\"testing\\n\\ntwo\", \"testing\\n\\n__two\")\n\ttx(\"testing\\n \\ntwo\", \"testing\\n__ \\n__two\")\n\ta := strings.Repeat(\"a\", screen_width-len(opts.Indent)-1)\n\ttx(a+\" b\", a+\" \\n__b\")\n\n\ttx(\"123456 \\x1b[31m789a\", \"123456 \\n__\\x1b[31m789a\")\n\ttx(\"12 \\x1b[31m789 abcd\", \"12 \\x1b[31m789 \\n\\x1b[39m__\\x1b[31mabcd\")\n\ttx(\"bb \\x1b]8;;http://xyz.com\\x1b\\\\text\\x1b]8;;\\x1b\\\\ two\", \"bb \\x1b]8;;http://xyz.com\\x1b\\\\text\\x1b]8;;\\x1b\\\\ \\n__two\")\n\ttx(\"\\x1b[31maaaaaa \\x1b[39mbbbbbb\", \"\\x1b[31maaaaaa \\n\\x1b[39m__\\x1b[31m\\x1b[39mbbbbbb\")\n\ttx(\n\t\t\"\\x1b[31;4:3m\\x1b]8;;XXX\\x1b\\\\combined using\\x1b]8;;\\x1b\\\\ operators\",\n\t\t\"\\x1b[31;4:3m\\x1b]8;;XXX\\x1b\\\\combined \\n\\x1b[4:0;39m\\x1b]8;;\\x1b\\\\__\\x1b[4:3;31m\\x1b]8;;XXX\\x1b\\\\using\\x1b]8;;\\x1b\\\\ \\n\\x1b[4:0;39m__\\x1b[4:3;31moperators\")\n\n\topts.Indent = \"\"\n\tscreen_width = 3\n\ttx(\"one\", \"one\")\n\ttx(\"four\", \"fou\\nr\")\n\ttx(\"nl\\n\\n\", \"nl\\n\\n\")\n\ttx(\"four\\n\\n\", \"fou\\nr\\n\\n\")\n\n\tscreen_width = 8\n\ttx(\n\t\t\"\\x1b[1mbold\\x1b[221m no more bold\",\n\t\t\"\\x1b[1mbold\\x1b[221m no \\nmore \\nbold\",\n\t)\n}\n"
  },
  {
    "path": "tools/utils/style/wrapper.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage style\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils/shlex\"\n)\n\ntype escape_code interface {\n\tprefix() string\n\tsuffix() string\n\tis_empty() bool\n}\n\n// bool values {{{\ntype bool_value struct {\n\tis_set, val bool\n}\n\nfunc (self bool_value) as_sgr(start, end string, prefix, suffix []string) ([]string, []string) {\n\tif self.is_set {\n\t\tif !self.val {\n\t\t\tstart, end = end, start\n\t\t}\n\t\tprefix = append(prefix, start)\n\t\tsuffix = append(suffix, end)\n\t}\n\treturn prefix, suffix\n}\n\nfunc (self *bool_value) set_val(val bool) {\n\tself.is_set = true\n\tself.val = val\n}\n\nfunc (self *bool_value) from_string(raw string) bool {\n\tswitch strings.ToLower(raw) {\n\tcase \"y\", \"yes\", \"true\", \"1\":\n\t\tself.set_val(true)\n\t\treturn true\n\tcase \"n\", \"no\", \"false\", \"0\":\n\t\tself.set_val(false)\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// }}}\n\n// color values {{{\ntype RGBA struct {\n\tRed, Green, Blue, Inverse_alpha uint8\n}\n\nfunc (self RGBA) AsRGBSharp() string {\n\treturn fmt.Sprintf(\"#%02x%02x%02x\", self.Red, self.Green, self.Blue)\n}\n\nfunc (self *RGBA) parse_rgb_strings(r string, g string, b string) bool {\n\tvar rv, gv, bv uint64\n\tvar err error\n\tif len(r) == 1 {\n\t\tr += r\n\t\tg += g\n\t\tb += b\n\t}\n\tif rv, err = strconv.ParseUint(r[:min(len(r), 2)], 16, 8); err != nil {\n\t\treturn false\n\t}\n\tif gv, err = strconv.ParseUint(g[:min(len(r), 2)], 16, 8); err != nil {\n\t\treturn false\n\t}\n\tif bv, err = strconv.ParseUint(b[:min(len(r), 2)], 16, 8); err != nil {\n\t\treturn false\n\t}\n\tself.Red, self.Green, self.Blue = uint8(rv), uint8(gv), uint8(bv)\n\treturn true\n}\n\nfunc fas_uint8(x float64) uint8 {\n\tx = max(0, min(x, 1))\n\treturn uint8(x * 255)\n}\n\nfunc (self *RGBA) parse_rgb_intensities(r string, g string, b string) bool {\n\tvar rv, gv, bv float64\n\tvar err error\n\tif rv, err = strconv.ParseFloat(r, 64); err != nil {\n\t\treturn false\n\t}\n\tif gv, err = strconv.ParseFloat(g, 64); err != nil {\n\t\treturn false\n\t}\n\tif bv, err = strconv.ParseFloat(b, 64); err != nil {\n\t\treturn false\n\t}\n\tself.Red, self.Green, self.Blue = fas_uint8(rv), fas_uint8(gv), fas_uint8(bv)\n\treturn true\n}\n\nfunc (self *RGBA) AsRGB() uint32 {\n\treturn uint32(self.Blue) | (uint32(self.Green) << 8) | (uint32(self.Red) << 16)\n}\n\nfunc (self *RGBA) IsDark() bool {\n\treturn self.Red < 155 && self.Green < 155 && self.Blue < 155\n}\n\nfunc (self *RGBA) FromRGB(col uint32) {\n\tself.Red = uint8((col >> 16) & 0xff)\n\tself.Green = uint8((col >> 8) & 0xff)\n\tself.Blue = uint8((col) & 0xff)\n}\n\ntype color_type struct {\n\tis_numbered bool\n\tval         RGBA\n}\n\nfunc (self color_type) as_sgr(number_base int, prefix, suffix []string) ([]string, []string) {\n\tsuffix = append(suffix, strconv.Itoa(number_base+9))\n\tif self.is_numbered {\n\t\tnum := int(self.val.Red)\n\t\tif num < 16 && number_base < 50 {\n\t\t\tif num > 7 {\n\t\t\t\tnumber_base += 60\n\t\t\t\tnum -= 8\n\t\t\t}\n\t\t\tprefix = append(prefix, strconv.Itoa(number_base+num))\n\t\t} else {\n\t\t\tprefix = append(prefix, fmt.Sprintf(\"%d:5:%d\", number_base+8, num))\n\t\t}\n\t} else {\n\t\tprefix = append(prefix, fmt.Sprintf(\"%d:2:%d:%d:%d\", number_base+8, self.val.Red, self.val.Green, self.val.Blue))\n\t}\n\treturn prefix, suffix\n}\n\ntype color_value struct {\n\tis_set bool\n\tval    color_type\n}\n\nfunc parse_sharp(color string) (ans RGBA, err error) {\n\tif len(color)%3 != 0 {\n\t\treturn RGBA{}, fmt.Errorf(\"length not a multiple of 3\")\n\t}\n\tpart_size := len(color) / 3\n\tr, g, b := color[:part_size], color[part_size:2*part_size], color[part_size*2:part_size*3]\n\tif !ans.parse_rgb_strings(r, g, b) {\n\t\terr = fmt.Errorf(\"invalid rgb numbers\")\n\t}\n\treturn\n}\n\nfunc parse_rgb(color string) (ans RGBA, err error) {\n\tcolors := strings.Split(color, \"/\")\n\tif len(colors) != 3 {\n\t\treturn RGBA{}, fmt.Errorf(\"length not a multiple of 3\")\n\t}\n\tif ans.parse_rgb_strings(colors[0], colors[1], colors[2]) {\n\t\treturn\n\t}\n\terr = fmt.Errorf(\"invalid rgb numbers\")\n\treturn\n}\n\nfunc parse_rgbi(color string) (ans RGBA, err error) {\n\tcolors := strings.Split(color, \"/\")\n\tif len(colors) != 3 {\n\t\treturn RGBA{}, fmt.Errorf(\"length not a multiple of 3\")\n\t}\n\tif ans.parse_rgb_intensities(colors[0], colors[1], colors[2]) {\n\t\treturn\n\t}\n\terr = fmt.Errorf(\"invalid rgb numbers\")\n\treturn\n}\n\nfunc ParseColor(color string) (ans RGBA, err error) {\n\t// Strip inline comments (e.g., \"oklch(...) # comment\")\n\t// For hex colors like \"#ff0000\", preserve the first #, but strip comments after spaces\n\tcolor = strings.TrimSpace(color)\n\tif strings.HasPrefix(color, \"#\") {\n\t\t// For hex colors, only strip comments after whitespace\n\t\tparts := strings.Fields(color)\n\t\tif len(parts) > 0 {\n\t\t\tcolor = parts[0] // Keep only the hex color part\n\t\t}\n\t} else {\n\t\t// For non-hex colors, strip everything after #\n\t\tif idx := strings.Index(color, \"#\"); idx >= 0 {\n\t\t\tcolor = strings.TrimSpace(color[:idx])\n\t\t}\n\t}\n\n\traw := strings.TrimSpace(strings.ToLower(color))\n\tif val, ok := ColorNames[raw]; ok {\n\t\treturn val, nil\n\t}\n\tif len(raw) < 4 {\n\t\treturn RGBA{}, fmt.Errorf(\"not a valid color name: %#v\", color)\n\t}\n\tvar parser func(string) (RGBA, error)\n\tswitch raw[0] {\n\tcase '#':\n\t\tparser = parse_sharp\n\t\traw = raw[1:]\n\tcase 'o':\n\t\tif strings.HasPrefix(raw, \"oklch(\") {\n\t\t\tparser, raw = parseOklch, raw[6:]\n\t\t}\n\tcase 'l':\n\t\tif strings.HasPrefix(raw, \"lab(\") {\n\t\t\tparser, raw = parseLab, raw[4:]\n\t\t}\n\tcase 'r':\n\t\tif len(raw) > 4 && strings.HasPrefix(raw, \"rgb\") {\n\t\t\traw = raw[3:]\n\t\t\tswitch raw[0] {\n\t\t\tcase ':':\n\t\t\t\tparser, raw = parse_rgb, raw[1:]\n\t\t\tcase 'i':\n\t\t\t\tif strings.HasPrefix(raw, \"i:\") {\n\t\t\t\t\tparser, raw = parse_rgbi, raw[2:]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif parser == nil {\n\t\terr = fmt.Errorf(\"not a valid color name: %#v\", color)\n\t} else {\n\t\tif ans, err = parser(raw); err != nil {\n\t\t\terr = fmt.Errorf(\"not a valid color name: %#v %w\", color, err)\n\t\t}\n\t}\n\treturn\n}\n\ntype NullableColor struct {\n\tColor RGBA\n\tIsSet bool\n}\n\nfunc ParseColorOrNone(color string) (NullableColor, error) {\n\traw := strings.TrimSpace(strings.ToLower(color))\n\tif raw == \"none\" {\n\t\treturn NullableColor{}, nil\n\t}\n\tc, err := ParseColor(raw)\n\treturn NullableColor{Color: c, IsSet: err == nil}, err\n}\n\nvar named_colors = map[string]uint8{\n\t\"black\": 0, \"red\": 1, \"green\": 2, \"yellow\": 3, \"blue\": 4, \"magenta\": 5, \"cyan\": 6, \"gray\": 7, \"white\": 7,\n\n\t\"hi-black\": 8, \"hi-red\": 9, \"hi-green\": 10, \"hi-yellow\": 11, \"hi-blue\": 12, \"hi-magenta\": 13, \"hi-cyan\": 14, \"hi-gray\": 15, \"hi-white\": 15,\n\n\t\"bright-black\": 8, \"bright-red\": 9, \"bright-green\": 10, \"bright-yellow\": 11, \"bright-blue\": 12, \"bright-magenta\": 13, \"bright-cyan\": 14, \"bright-gray\": 15, \"bright-white\": 15,\n\n\t\"intense-black\": 8, \"intense-red\": 9, \"intense-green\": 10, \"intense-yellow\": 11, \"intense-blue\": 12, \"intense-magenta\": 13, \"intense-cyan\": 14, \"intense-gray\": 15, \"intense-white\": 15,\n}\n\nfunc (self *color_value) from_string(raw string) bool {\n\tif n, ok := named_colors[raw]; ok {\n\t\tself.is_set = true\n\t\tself.val = color_type{val: RGBA{Red: n}, is_numbered: true}\n\t\treturn true\n\t}\n\ta, err := strconv.Atoi(raw)\n\tif err == nil && 0 <= a && a <= 255 {\n\t\tself.is_set = true\n\t\tself.val = color_type{val: RGBA{Red: uint8(a)}, is_numbered: true}\n\t\treturn true\n\t}\n\tc, err := ParseColor(raw)\n\tif err != nil {\n\t\treturn false\n\t}\n\tself.is_set = true\n\tself.val = color_type{val: c}\n\treturn true\n}\n\nfunc (self color_value) as_sgr(number_base int, prefix, suffix []string) ([]string, []string) {\n\tif self.is_set {\n\t\tprefix, suffix = self.val.as_sgr(number_base, prefix, suffix)\n\t}\n\treturn prefix, suffix\n}\n\n// }}}\n\n// underline values {{{\ntype underline_style uint8\n\nconst (\n\tno_underline       underline_style = 0\n\tstraight_underline underline_style = 1\n\tdouble_underline   underline_style = 2\n\tcurly_underline    underline_style = 3\n\tdotted_underline   underline_style = 4\n\tdashed_underline   underline_style = 5\n\n\tnil_underline underline_style = 255\n)\n\ntype underline_value struct {\n\tis_set bool\n\tstyle  underline_style\n}\n\nfunc (self *underline_value) from_string(val string) bool {\n\tans := nil_underline\n\tswitch val {\n\tcase \"true\", \"yes\", \"y\", \"straight\", \"single\":\n\t\tans = straight_underline\n\tcase \"false\", \"no\", \"n\", \"none\":\n\t\tans = no_underline\n\tcase \"double\":\n\t\tans = double_underline\n\tcase \"curly\":\n\t\tans = curly_underline\n\tcase \"dotted\":\n\t\tans = dotted_underline\n\tcase \"dashed\":\n\t\tans = dashed_underline\n\t}\n\tif ans == nil_underline {\n\t\treturn false\n\t}\n\tself.is_set = true\n\tself.style = ans\n\treturn true\n}\n\nfunc (self underline_value) as_sgr(prefix, suffix []string) ([]string, []string) {\n\tif self.is_set {\n\t\ts, e := \"4:0\", \"4:0\"\n\t\tif self.style != no_underline {\n\t\t\ts = \"4:\" + strconv.Itoa(int(self.style))\n\t\t}\n\t\tprefix = append(prefix, s)\n\t\tsuffix = append(suffix, e)\n\t}\n\treturn prefix, suffix\n}\n\n// }}}\n\ntype sgr_code struct {\n\tbold, italic, reverse, dim, strikethrough bool_value\n\tfg, bg, uc                                color_value\n\tunderline                                 underline_value\n\n\t_prefix, _suffix string\n}\n\nfunc (self sgr_code) prefix() string {\n\treturn self._prefix\n}\n\nfunc (self sgr_code) suffix() string {\n\treturn self._suffix\n}\n\nfunc (self sgr_code) is_empty() bool {\n\treturn self._prefix == \"\"\n}\n\ntype url_code struct {\n\turl string\n}\n\nfunc (self url_code) prefix() string {\n\treturn fmt.Sprintf(\"\\x1b]8;;%s\\x1b\\\\\", self.url)\n}\n\nfunc (self url_code) suffix() string {\n\treturn \"\\x1b]8;;\\x1b\\\\\"\n}\n\nfunc (self url_code) is_empty() bool {\n\treturn self.url == \"\"\n}\n\nfunc (self *sgr_code) update() {\n\tp := make([]string, 0, 1)\n\ts := make([]string, 0, 1)\n\tp, s = self.bold.as_sgr(\"1\", \"221\", p, s)\n\tp, s = self.dim.as_sgr(\"2\", \"222\", p, s)\n\tp, s = self.italic.as_sgr(\"3\", \"23\", p, s)\n\tp, s = self.reverse.as_sgr(\"7\", \"27\", p, s)\n\tp, s = self.strikethrough.as_sgr(\"9\", \"29\", p, s)\n\tp, s = self.underline.as_sgr(p, s)\n\tp, s = self.fg.as_sgr(30, p, s)\n\tp, s = self.bg.as_sgr(40, p, s)\n\tp, s = self.uc.as_sgr(50, p, s)\n\tif len(p) > 0 {\n\t\tself._prefix = \"\\x1b[\" + strings.Join(p, \";\") + \"m\"\n\t} else {\n\t\tself._prefix = \"\"\n\t}\n\tif len(s) > 0 {\n\t\tself._suffix = \"\\x1b[\" + strings.Join(s, \";\") + \"m\"\n\t} else {\n\t\tself._suffix = \"\"\n\t}\n}\n\nfunc parse_spec(spec string) []escape_code {\n\tans := make([]escape_code, 0, 1)\n\tsgr := sgr_code{}\n\tsparts, _ := shlex.Split(spec)\n\tfor _, p := range sparts {\n\t\tkey, val, found := strings.Cut(p, \"=\")\n\t\tif !found {\n\t\t\tval = \"true\"\n\t\t}\n\t\tswitch key {\n\t\tcase \"fg\":\n\t\t\tsgr.fg.from_string(val)\n\t\tcase \"bg\":\n\t\t\tsgr.bg.from_string(val)\n\t\tcase \"bold\", \"b\":\n\t\t\tsgr.bold.from_string(val)\n\t\tcase \"italic\", \"i\":\n\t\t\tsgr.italic.from_string(val)\n\t\tcase \"reverse\":\n\t\t\tsgr.reverse.from_string(val)\n\t\tcase \"dim\", \"faint\":\n\t\t\tsgr.dim.from_string(val)\n\t\tcase \"underline\", \"u\":\n\t\t\tsgr.underline.from_string(val)\n\t\tcase \"strikethrough\", \"s\":\n\t\t\tsgr.strikethrough.from_string(val)\n\t\tcase \"ucol\", \"underline_color\", \"uc\":\n\t\t\tsgr.uc.from_string(val)\n\t\t}\n\t}\n\tsgr.update()\n\tif !sgr.is_empty() {\n\t\tans = append(ans, &sgr)\n\t}\n\treturn ans\n}\n\nvar parsed_spec_cache = make(map[string][]escape_code)\nvar parsed_spec_cache_mutex = sync.Mutex{}\n\nfunc cached_parse_spec(spec string) []escape_code {\n\tparsed_spec_cache_mutex.Lock()\n\tdefer parsed_spec_cache_mutex.Unlock()\n\tif val, ok := parsed_spec_cache[spec]; ok {\n\t\treturn val\n\t}\n\tans := parse_spec(spec)\n\tparsed_spec_cache[spec] = ans\n\treturn ans\n}\n\nfunc prefix_for_spec(spec string) string {\n\tsb := strings.Builder{}\n\tfor _, ec := range cached_parse_spec(spec) {\n\t\tsb.WriteString(ec.prefix())\n\t}\n\treturn sb.String()\n}\n\nfunc suffix_for_spec(spec string) string {\n\tsb := strings.Builder{}\n\tfor _, ec := range cached_parse_spec(spec) {\n\t\tsb.WriteString(ec.suffix())\n\t}\n\treturn sb.String()\n}\n"
  },
  {
    "path": "tools/utils/style/wrapper_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage style\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestANSIStyleContext(t *testing.T) {\n\tvar ctx = Context{AllowEscapeCodes: false}\n\tsprint := ctx.SprintFunc(\"bold\")\n\tif sprint(\"test\") != \"test\" {\n\t\tt.Fatal(\"AllowEscapeCodes=false not respected\")\n\t}\n\tctx.AllowEscapeCodes = true\n\tif sprint(\"test\") == \"test\" {\n\t\tt.Fatal(\"AllowEscapeCodes=true not respected\")\n\t}\n}\n\nfunc TestANSIStyleSprint(t *testing.T) {\n\tvar ctx = Context{AllowEscapeCodes: true}\n\n\ttest := func(spec string, prefix string, suffix string) {\n\t\tactual := ctx.SprintFunc(spec)(\"  \")\n\t\texpected := prefix + \"  \" + suffix\n\t\tif actual != expected {\n\t\t\tt.Fatalf(\"Formatting with spec: %s failed expected != actual: %#v != %#v\", spec, expected, actual)\n\t\t}\n\t}\n\n\ttest(\"\", \"\", \"\")\n\ttest(\"bold\", \"\\x1b[1m\", \"\\x1b[221m\")\n\ttest(\"bold fg=red u=curly\", \"\\x1b[1;4:3;31m\", \"\\x1b[221;4:0;39m\")\n\ttest(\"fg=123\", \"\\x1b[38:5:123m\", \"\\x1b[39m\")\n\ttest(\"fg=15\", \"\\x1b[97m\", \"\\x1b[39m\")\n\ttest(\"bg=15\", \"\\x1b[107m\", \"\\x1b[49m\")\n\ttest(\"fg=#123\", \"\\x1b[38:2:17:34:51m\", \"\\x1b[39m\")\n\ttest(\"fg=rgb:1/2/3\", \"\\x1b[38:2:17:34:51m\", \"\\x1b[39m\")\n\ttest(\"bg=123\", \"\\x1b[48:5:123m\", \"\\x1b[49m\")\n\ttest(\"uc=123\", \"\\x1b[58:5:123m\", \"\\x1b[59m\")\n\ttest(\"uc=1\", \"\\x1b[58:5:1m\", \"\\x1b[59m\")\n\n\tactual := ctx.UrlFunc(\"u=curly uc=cyan\")(\"http://moo.com\", \"___\")\n\texpected := \"\\x1b[4:3;58:5:6m\\x1b]8;;http://moo.com\\x1b\\\\___\\x1b]8;;\\x1b\\\\\\x1b[4:0;59m\"\n\tif actual != expected {\n\t\tt.Fatalf(\"Formatting URL failed expected != actual: %#v != %#v\", expected, actual)\n\t}\n}\n"
  },
  {
    "path": "tools/utils/tar.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"archive/tar\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nvar _ = fmt.Print\n\ntype TarExtractOptions struct {\n\tDontPreservePermissions bool\n}\n\nfunc volnamelen(path string) int {\n\treturn len(filepath.VolumeName(path))\n}\n\nfunc EvalSymlinksThatExist(path string) (string, error) {\n\tvolLen := volnamelen(path)\n\tpathSeparator := string(os.PathSeparator)\n\n\tif volLen < len(path) && os.IsPathSeparator(path[volLen]) {\n\t\tvolLen++\n\t}\n\tvol := path[:volLen]\n\tdest := vol\n\tlinksWalked := 0\n\tfor start, end := volLen, volLen; start < len(path); start = end {\n\t\tfor start < len(path) && os.IsPathSeparator(path[start]) {\n\t\t\tstart++\n\t\t}\n\t\tend = start\n\t\tfor end < len(path) && !os.IsPathSeparator(path[end]) {\n\t\t\tend++\n\t\t}\n\n\t\t// On Windows, \".\" can be a symlink.\n\t\t// We look it up, and use the value if it is absolute.\n\t\t// If not, we just return \".\".\n\t\tisWindowsDot := runtime.GOOS == \"windows\" && path[volnamelen(path):] == \".\"\n\n\t\t// The next path component is in path[start:end].\n\t\tif end == start {\n\t\t\t// No more path components.\n\t\t\tbreak\n\t\t} else if path[start:end] == \".\" && !isWindowsDot {\n\t\t\t// Ignore path component \".\".\n\t\t\tcontinue\n\t\t} else if path[start:end] == \"..\" {\n\t\t\t// Back up to previous component if possible.\n\t\t\t// Note that volLen includes any leading slash.\n\n\t\t\t// Set r to the index of the last slash in dest,\n\t\t\t// after the volume.\n\t\t\tvar r int\n\t\t\tfor r = len(dest) - 1; r >= volLen; r-- {\n\t\t\t\tif os.IsPathSeparator(dest[r]) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif r < volLen || dest[r+1:] == \"..\" {\n\t\t\t\t// Either path has no slashes\n\t\t\t\t// (it's empty or just \"C:\")\n\t\t\t\t// or it ends in a \"..\" we had to keep.\n\t\t\t\t// Either way, keep this \"..\".\n\t\t\t\tif len(dest) > volLen {\n\t\t\t\t\tdest += pathSeparator\n\t\t\t\t}\n\t\t\t\tdest += \"..\"\n\t\t\t} else {\n\t\t\t\t// Discard everything since the last slash.\n\t\t\t\tdest = dest[:r]\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Ordinary path component. Add it to result.\n\n\t\tif len(dest) > volnamelen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {\n\t\t\tdest += pathSeparator\n\t\t}\n\n\t\tdest += path[start:end]\n\n\t\t// Resolve symlink.\n\n\t\tfi, err := os.Lstat(dest)\n\t\tif err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tif end < len(path) {\n\t\t\t\t\tdest += path[end:]\n\t\t\t\t}\n\t\t\t\treturn filepath.Clean(dest), nil\n\t\t\t}\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif fi.Mode()&fs.ModeSymlink == 0 {\n\t\t\tif !fi.Mode().IsDir() && end < len(path) {\n\t\t\t\treturn \"\", fmt.Errorf(\"%s is not a directory while resolving symlinks in %s\", dest, path)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Found symlink.\n\n\t\tlinksWalked++\n\t\tif linksWalked > 255 {\n\t\t\treturn \"\", fmt.Errorf(\"EvalSymlinksThatExist: too many symlinks in %s\", path)\n\t\t}\n\n\t\tlink, err := os.Readlink(dest)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif isWindowsDot && !filepath.IsAbs(link) {\n\t\t\t// On Windows, if \".\" is a relative symlink,\n\t\t\t// just return \".\".\n\t\t\tbreak\n\t\t}\n\n\t\tpath = link + path[end:]\n\n\t\tv := volnamelen(link)\n\t\tif v > 0 {\n\t\t\t// Symlink to drive name is an absolute path.\n\t\t\tif v < len(link) && os.IsPathSeparator(link[v]) {\n\t\t\t\tv++\n\t\t\t}\n\t\t\tvol = link[:v]\n\t\t\tdest = vol\n\t\t\tend = len(vol)\n\t\t} else if len(link) > 0 && os.IsPathSeparator(link[0]) {\n\t\t\t// Symlink to absolute path.\n\t\t\tdest = link[:1]\n\t\t\tend = 1\n\t\t\tvol = link[:1]\n\t\t\tvolLen = 1\n\t\t} else {\n\t\t\t// Symlink to relative path; replace last\n\t\t\t// path component in dest.\n\t\t\tvar r int\n\t\t\tfor r = len(dest) - 1; r >= volLen; r-- {\n\t\t\t\tif os.IsPathSeparator(dest[r]) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif r < volLen {\n\t\t\t\tdest = vol\n\t\t\t} else {\n\t\t\t\tdest = dest[:r]\n\t\t\t}\n\t\t\tend = 0\n\t\t}\n\t}\n\treturn filepath.Clean(dest), nil\n}\n\nfunc ExtractAllFromTar(tr *tar.Reader, dest_path string, optss ...TarExtractOptions) (count int, err error) {\n\topts := TarExtractOptions{}\n\tif len(optss) > 0 {\n\t\topts = optss[0]\n\t}\n\tif !filepath.IsAbs(dest_path) {\n\t\tif dest_path, err = filepath.Abs(dest_path); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif dest_path, err = filepath.EvalSymlinks(dest_path); err != nil {\n\t\treturn\n\t}\n\tdest_path = filepath.Clean(dest_path)\n\n\tmode := func(hdr int64) fs.FileMode {\n\t\treturn fs.FileMode(hdr) & (fs.ModePerm | fs.ModeSetgid | fs.ModeSetuid | fs.ModeSticky)\n\t}\n\n\tset_metadata := func(chmod func(mode fs.FileMode) error, hdr_mode int64) (err error) {\n\t\tif !opts.DontPreservePermissions && chmod != nil {\n\t\t\tperms := mode(hdr_mode)\n\t\t\tif err = chmod(perms); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tcount++\n\t\treturn\n\t}\n\tneeded_prefix := dest_path + string(os.PathSeparator)\n\n\tfor {\n\t\tvar hdr *tar.Header\n\t\thdr, err = tr.Next()\n\t\tif errors.Is(err, io.EOF) {\n\t\t\terr = nil\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn count, err\n\t\t}\n\t\tdest := hdr.Name\n\t\tif !filepath.IsAbs(dest) {\n\t\t\tdest = filepath.Join(dest_path, dest)\n\t\t}\n\t\tif dest, err = EvalSymlinksThatExist(dest); err != nil {\n\t\t\treturn count, err\n\t\t}\n\t\tif !strings.HasPrefix(dest, needed_prefix) {\n\t\t\tcontinue\n\t\t}\n\t\tswitch hdr.Typeflag {\n\t\tcase tar.TypeDir:\n\t\t\terr = os.MkdirAll(dest, 0o700)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err = set_metadata(func(m fs.FileMode) error { return os.Chmod(dest, m) }, hdr.Mode); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase tar.TypeReg:\n\t\t\tvar d *os.File\n\t\t\tif err = os.MkdirAll(filepath.Dir(dest), 0o700); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif d, err = os.Create(dest); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = set_metadata(d.Chmod, hdr.Mode)\n\t\t\tif err == nil {\n\t\t\t\t_, err = io.Copy(d, tr)\n\t\t\t}\n\t\t\td.Close()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase tar.TypeLink:\n\t\t\tif err = os.MkdirAll(filepath.Dir(dest), 0o700); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlink_target := hdr.Linkname\n\t\t\tif !filepath.IsAbs(link_target) {\n\t\t\t\tlink_target = filepath.Join(filepath.Dir(dest), link_target)\n\t\t\t}\n\t\t\tif err = os.Link(link_target, dest); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err = set_metadata(func(m fs.FileMode) error { return os.Chmod(dest, m) }, hdr.Mode); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase tar.TypeSymlink:\n\t\t\tif err = os.MkdirAll(filepath.Dir(dest), 0o700); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlink_target := hdr.Linkname\n\t\t\tif !filepath.IsAbs(link_target) {\n\t\t\t\tlink_target = filepath.Join(filepath.Dir(dest), link_target)\n\t\t\t}\n\t\t\t// We dont care about the link target being outside dest_path as\n\t\t\t// we use EvalSymlinks on dest, so a symlink pointing outside\n\t\t\t// dest_path cannot cause writes outside dest_path.\n\t\t\tif err = os.Symlink(link_target, dest); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err = set_metadata(nil, hdr.Mode); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "tools/utils/tar_test.go",
    "content": "package utils\n\nimport (\n\t\"archive/tar\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar _ = fmt.Print\n\nfunc TestTarExtract(t *testing.T) {\n\ttdir := t.TempDir()\n\ta, b := filepath.Join(tdir, \"a\"), filepath.Join(tdir, \"b\")\n\tif err := os.Mkdir(a, 0700); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := os.Mkdir(b, 0700); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar buf bytes.Buffer\n\ttw := tar.NewWriter(&buf)\n\tvar files = []struct {\n\t\tname, body string\n\t}{\n\t\t{\"s/one.txt\", \"This archive contains some text files.\"},\n\t\t{\"b\", b},\n\t\t{\"b/two.txt\", \"Get animal handling license.\"},\n\t\t{\"../b/three.txt\", \"Get animal handling license.\"},\n\t\t{\"nested/dir/\", \"\"},\n\t}\n\tfor _, file := range files {\n\t\thdr := &tar.Header{\n\t\t\tName: file.name,\n\t\t\tMode: 0600,\n\t\t\tSize: int64(len(file.body)),\n\t\t}\n\t\tif file.name == \"b\" {\n\t\t\thdr.Linkname = file.body\n\t\t\thdr.Typeflag = tar.TypeSymlink\n\t\t\thdr.Size = 0\n\t\t}\n\t\tif err := tw.WriteHeader(hdr); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif hdr.Typeflag != tar.TypeSymlink && len(file.body) > 0 {\n\t\t\tif _, err := tw.Write([]byte(file.body)); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\t}\n\tif err := tw.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttr := tar.NewReader(&buf)\n\tcount, err := ExtractAllFromTar(tr, a)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif count != len(files)-2 {\n\t\tt.Fatalf(\"Incorrect count of extracted files: %d != %d\", count, len(files)-2)\n\t}\n\tentries := []string{}\n\tif err = fs.WalkDir(os.DirFS(tdir), \".\", func(path string, d fs.DirEntry, err error) error {\n\t\tentries = append(entries, path)\n\t\treturn err\n\t},\n\t); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif diff := cmp.Diff([]string{\".\", \"a\", \"a/b\", \"a/nested\", \"a/nested/dir\", \"a/s\", \"a/s/one.txt\", \"b\"}, entries); diff != \"\" {\n\t\tt.Fatalf(\"Directory contents not as expected: %s\", diff)\n\t}\n}\n"
  },
  {
    "path": "tools/utils/tmpfile_linux.go",
    "content": "//go:build linux\n\n// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc CreateAnonymousTemp(dir string, perms ...fs.FileMode) (*os.File, error) {\n\tif dir == \"\" {\n\t\tdir = os.TempDir()\n\t}\n\tvar perm fs.FileMode = unix.S_IREAD | unix.S_IWRITE\n\tif len(perms) > 0 {\n\t\tperm = perms[0]\n\t}\n\tfd, err := unix.Open(dir, unix.O_RDWR|unix.O_TMPFILE|unix.O_CLOEXEC, uint32(perm&fs.ModePerm))\n\n\tif err == nil {\n\t\tpath := \"/proc/self/fd/\" + strconv.FormatUint(uint64(fd), 10)\n\t\treturn os.NewFile(uintptr(fd), path), nil\n\t}\n\tif err == unix.ENOENT {\n\t\treturn nil, &os.PathError{\n\t\t\tOp:   \"open\",\n\t\t\tPath: dir,\n\t\t\tErr:  err,\n\t\t}\n\t}\n\tf, err := os.CreateTemp(dir, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = os.Remove(f.Name())\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\treturn f, err\n}\n"
  },
  {
    "path": "tools/utils/tmpfile_others.go",
    "content": "//go:build !linux\n\n// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nfunc CreateAnonymousTemp(dir string, perms ...fs.FileMode) (*os.File, error) {\n\tvar perm fs.FileMode = unix.S_IREAD | unix.S_IWRITE\n\tdefault_perm := perm\n\tif len(perms) > 0 {\n\t\tperm = perms[0]\n\t}\n\n\tf, err := os.CreateTemp(dir, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif perm != default_perm {\n\t\terr = f.Chmod(perm & fs.ModePerm)\n\t\tif err != nil {\n\t\t\tos.Remove(f.Name())\n\t\t\tf.Close()\n\t\t\treturn nil, err\n\t\t}\n\t}\n\terr = os.Remove(f.Name())\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\treturn f, err\n}\n"
  },
  {
    "path": "tools/utils/tpmfile_test.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"testing\"\n)\n\nvar _ = fmt.Print\n\nfunc TestCreateAnonymousTempfile(t *testing.T) {\n\tf, err := CreateAnonymousTemp(\"\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfd := int64(f.Fd())\n\tf.Close()\n\tif runtime.GOOS == \"linux\" {\n\t\tif f.Name() != \"/proc/self/fd/\"+strconv.FormatInt(fd, 10) {\n\t\t\tt.Fatalf(\"Anonymous tempfile was not created atomically\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/utils/types.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\ntype RemoteControlCmd struct {\n\tCmd           string `json:\"cmd\"`\n\tVersion       [3]int `json:\"version\"`\n\tNoResponse    bool   `json:\"no_response,omitempty\"`\n\tTimestamp     int64  `json:\"timestamp,omitempty\"`\n\tPassword      string `json:\"password,omitempty\"`\n\tAsync         string `json:\"async,omitempty\"`\n\tCancelAsync   bool   `json:\"cancel_async,omitempty\"`\n\tStream        bool   `json:\"stream,omitempty\"`\n\tStreamId      string `json:\"stream_id,omitempty\"`\n\tKittyWindowId uint   `json:\"kitty_window_id,omitempty\"`\n\tPayload       any    `json:\"payload,omitempty\"`\n}\n\ntype EncryptedRemoteControlCmd struct {\n\tVersion   [3]int `json:\"version\"`\n\tIV        string `json:\"iv\"`\n\tTag       string `json:\"tag\"`\n\tPubkey    string `json:\"pubkey\"`\n\tEncrypted string `json:\"encrypted\"`\n\tEncProto  string `json:\"enc_proto,omitempty\"`\n}\n"
  },
  {
    "path": "tools/utils/unsafe.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"unsafe\"\n)\n\nvar _ = fmt.Print\n\n// Unsafely converts s into a byte slice.\n// If you modify b, then s will also be modified. This violates the\n// property that strings are immutable.\nfunc UnsafeStringToBytes(s string) (b []byte) {\n\treturn unsafe.Slice(unsafe.StringData(s), len(s))\n}\n\n// Unsafely converts b into a string.\n// If you modify b, then s will also be modified. This violates the\n// property that strings are immutable.\nfunc UnsafeBytesToString(b []byte) (s string) {\n\treturn unsafe.String(unsafe.SliceData(b), len(b))\n}\n"
  },
  {
    "path": "tools/utils/utf-8.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\n// UTF-8 decode taken from: https://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n\ntype UTF8State uint32\n\nvar utf8_data []uint8 = []uint8{\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f\n\t1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f\n\t7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf\n\t8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df\n\t0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef\n\t0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff\n\t0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n\t1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n\t1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n\t1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n\t1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8\n}\n\nconst (\n\tUTF8_ACCEPT UTF8State = 0\n\tUTF8_REJECT UTF8State = 1\n)\n\nfunc DecodeUtf8(state *UTF8State, codep *UTF8State, byte_ byte) UTF8State {\n\ttyp := UTF8State(utf8_data[byte_])\n\tb := UTF8State(byte_)\n\n\tif *state != UTF8_ACCEPT {\n\t\t*codep = (b & 0x3f) | (*codep << 6)\n\t} else {\n\t\t*codep = (0xff >> typ) & (b)\n\t}\n\n\tidx := 256 + *state*16 + typ\n\t*state = UTF8State(utf8_data[idx])\n\treturn *state\n}\n\nfunc EncodeUtf8(ch UTF8State, dest []byte) int {\n\tif ch < 0x80 {\n\t\tdest[0] = byte(ch)\n\t\treturn 1\n\t}\n\tif ch < 0x800 {\n\t\tdest[0] = byte((ch >> 6) | 0xC0)\n\t\tdest[1] = byte((ch & 0x3F) | 0x80)\n\t\treturn 2\n\t}\n\tif ch < 0x10000 {\n\t\tdest[0] = byte((ch >> 12) | 0xE0)\n\t\tdest[1] = byte(((ch >> 6) & 0x3F) | 0x80)\n\t\tdest[2] = byte((ch & 0x3F) | 0x80)\n\t\treturn 3\n\t}\n\tif ch < 0x110000 {\n\t\tdest[0] = byte((ch >> 18) | 0xF0)\n\t\tdest[1] = byte(((ch >> 12) & 0x3F) | 0x80)\n\t\tdest[2] = byte(((ch >> 6) & 0x3F) | 0x80)\n\t\tdest[3] = byte((ch & 0x3F) | 0x80)\n\t\treturn 4\n\t}\n\treturn 0\n}\n"
  },
  {
    "path": "tools/utils/which.go",
    "content": "// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nvar _ = fmt.Print\n\nvar DefaultExeSearchPaths = sync.OnceValue(func() []string {\n\tcandidates := [...]string{\"/usr/local/bin\", \"/opt/bin\", \"/opt/homebrew/bin\", \"/usr/bin\", \"/bin\", \"/usr/sbin\", \"/sbin\"}\n\tans := make([]string, 0, len(candidates))\n\tfor _, x := range candidates {\n\t\tif s, err := os.Stat(x); err == nil && s.IsDir() {\n\t\t\tans = append(ans, x)\n\t\t}\n\t}\n\treturn ans\n})\n\nfunc Which(cmd string, paths ...string) string {\n\tif strings.Contains(cmd, string(os.PathSeparator)) {\n\t\treturn \"\"\n\t}\n\tif len(paths) == 0 {\n\t\tpath := os.Getenv(\"PATH\")\n\t\tif path == \"\" {\n\t\t\treturn \"\"\n\t\t}\n\t\tpaths = strings.Split(path, string(os.PathListSeparator))\n\t}\n\tfor _, dir := range paths {\n\t\tq := filepath.Join(dir, cmd)\n\t\tif unix.Access(q, unix.X_OK) == nil {\n\t\t\ts, err := os.Stat(q)\n\t\t\tif err == nil && !s.IsDir() {\n\t\t\t\treturn q\n\t\t\t}\n\t\t}\n\n\t}\n\treturn \"\"\n}\n\nfunc FindExe(name string) string {\n\tans := Which(name)\n\tif ans != \"\" {\n\t\treturn ans\n\t}\n\tans = Which(name, DefaultExeSearchPaths()...)\n\tif ans == \"\" {\n\t\tans = name\n\t}\n\treturn ans\n}\n"
  },
  {
    "path": "tools/vt/cell.go",
    "content": "package vt\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\ntype Cell struct {\n\tCh          Ch\n\tFg, Bg, Dec CellColor\n\tMc          MultiCell\n\tAttrs       CellAttrs\n}\n"
  },
  {
    "path": "tools/vt/line.go",
    "content": "package vt\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\ntype PromptKind uint8\n\nconst (\n\tUNKNOWN_PROMPT_KIND PromptKind = iota\n\tPROMPT_START\n\tSECONDARY_PROMPT\n\tOUTPUT_START\n)\n\ntype Line struct {\n\tCells []Cell\n\tAttrs LineAttrs\n}\n"
  },
  {
    "path": "tools/vt/linebuf.go",
    "content": "package vt\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\nconst extra_capacity uint = 4\n\ntype LineBuf struct {\n\tcells             []Cell\n\tattrs             []LineAttrs\n\tline_map, scratch []uint\n\txnum, ynum        uint\n}\n\nfunc NewLineBuf(xnum, ynum uint) *LineBuf {\n\tlm := make([]uint, ynum, ynum+extra_capacity)\n\tvar i uint\n\tfor i = range ynum {\n\t\tlm[i] = i\n\t}\n\treturn &LineBuf{\n\t\tcells:   make([]Cell, xnum*ynum, xnum*(ynum+extra_capacity)),\n\t\tattrs:   make([]LineAttrs, len(lm), cap(lm)),\n\t\tscratch: make([]uint, len(lm), cap(lm)),\n\t\txnum:    xnum, ynum: ynum, line_map: lm,\n\t}\n}\n\nfunc (self *LineBuf) Line(y uint) Line {\n\tidx := self.line_map[y]\n\treturn Line{Attrs: self.attrs[y], Cells: self.cells[idx*self.xnum : (idx+1)*self.xnum]}\n}\n\nfunc (self *LineBuf) AddLines(n uint) {\n\tynum := self.ynum + n\n\tif uint(cap(self.line_map)) >= ynum {\n\t\tself.line_map = self.line_map[0:ynum:cap(self.line_map)]\n\t\tself.scratch = self.scratch[0:ynum:cap(self.scratch)]\n\t\tself.attrs = self.attrs[0:ynum:cap(self.attrs)]\n\t\tself.cells = self.cells[0 : ynum*self.xnum : cap(self.cells)]\n\t} else {\n\t\tnewattrs := make([]LineAttrs, ynum, ynum+extra_capacity)\n\t\tcopy(newattrs, self.attrs)\n\t\tnewlinemap := make([]uint, ynum, ynum+extra_capacity)\n\t\tcopy(newlinemap, self.line_map)\n\t\tfor n = self.ynum; n < ynum; n++ {\n\t\t\tnewlinemap[n] = n\n\t\t}\n\t\tnewcells := make([]Cell, self.xnum*ynum, self.xnum*(ynum+extra_capacity))\n\t\tcopy(newcells, self.cells)\n\t\tself.attrs = newattrs\n\t\tself.line_map = newlinemap\n\t\tself.cells = newcells\n\t\tself.scratch = make([]uint, len(self.line_map), cap(self.line_map))\n\t}\n\tself.ynum = ynum\n}\n"
  },
  {
    "path": "tools/wcswidth/char-props-data.go",
    "content": "package wcswidth\n\nimport \"unsafe\"\n\nconst MAX_UNICODE = 1114111\nconst UNICODE_LIMIT = 1114112\n\ntype GraphemeBreakProperty uint8\n\nconst (\n\tGBP_AtStart GraphemeBreakProperty = iota\n\tGBP_None\n\tGBP_Prepend\n\tGBP_CR\n\tGBP_LF\n\tGBP_Control\n\tGBP_Extend\n\tGBP_Regional_Indicator\n\tGBP_SpacingMark\n\tGBP_L\n\tGBP_V\n\tGBP_T\n\tGBP_LV\n\tGBP_LVT\n\tGBP_ZWJ\n\tGBP_Private_Expecting_RI\n)\n\ntype IndicConjunctBreak uint8\n\nconst (\n\tICB_None IndicConjunctBreak = iota\n\tICB_Linker\n\tICB_Consonant\n\tICB_Extend\n)\n\ntype UnicodeCategory uint8\n\nconst (\n\tUC_Cn UnicodeCategory = iota\n\tUC_Cc\n\tUC_Zs\n\tUC_Po\n\tUC_Sc\n\tUC_Ps\n\tUC_Pe\n\tUC_Sm\n\tUC_Pd\n\tUC_Nd\n\tUC_Lu\n\tUC_Sk\n\tUC_Pc\n\tUC_Ll\n\tUC_So\n\tUC_Lo\n\tUC_Pi\n\tUC_Cf\n\tUC_No\n\tUC_Pf\n\tUC_Lt\n\tUC_Lm\n\tUC_Mn\n\tUC_Me\n\tUC_Mc\n\tUC_Nl\n\tUC_Zl\n\tUC_Zp\n\tUC_Cs\n\tUC_Co\n)\n\n// Total number of bits used: 23\ntype CharProps uint32\n\nfunc (s CharProps) Is_extended_pictographic() uint8 {\n\treturn uint8(s & 0b1)\n}\n\nfunc (s *CharProps) Set_is_extended_pictographic(val uint8) {\n\t*s &^= 0b1\n\t*s |= CharProps(val & 0b1)\n}\n\nfunc (s CharProps) Indic_conjunct_break() uint8 {\n\treturn uint8((s >> 1) & 0b11)\n}\n\nfunc (s *CharProps) Set_indic_conjunct_break(val uint8) {\n\t*s &^= 0b11 << 1\n\t*s |= CharProps(val&0b11) << 1\n}\n\nfunc (s CharProps) Grapheme_break() uint8 {\n\treturn uint8((s >> 3) & 0b1111)\n}\n\nfunc (s *CharProps) Set_grapheme_break(val uint8) {\n\t*s &^= 0b1111 << 3\n\t*s |= CharProps(val&0b1111) << 3\n}\n\nfunc (s CharProps) Is_punctuation() uint8 {\n\treturn uint8((s >> 7) & 0b1)\n}\n\nfunc (s *CharProps) Set_is_punctuation(val uint8) {\n\t*s &^= 0b1 << 7\n\t*s |= CharProps(val&0b1) << 7\n}\n\nfunc (s CharProps) Is_word_char() uint8 {\n\treturn uint8((s >> 8) & 0b1)\n}\n\nfunc (s *CharProps) Set_is_word_char(val uint8) {\n\t*s &^= 0b1 << 8\n\t*s |= CharProps(val&0b1) << 8\n}\n\nfunc (s CharProps) Is_combining_char() uint8 {\n\treturn uint8((s >> 9) & 0b1)\n}\n\nfunc (s *CharProps) Set_is_combining_char(val uint8) {\n\t*s &^= 0b1 << 9\n\t*s |= CharProps(val&0b1) << 9\n}\n\nfunc (s CharProps) Is_symbol() uint8 {\n\treturn uint8((s >> 10) & 0b1)\n}\n\nfunc (s *CharProps) Set_is_symbol(val uint8) {\n\t*s &^= 0b1 << 10\n\t*s |= CharProps(val&0b1) << 10\n}\n\nfunc (s CharProps) Is_non_rendered() uint8 {\n\treturn uint8((s >> 11) & 0b1)\n}\n\nfunc (s *CharProps) Set_is_non_rendered(val uint8) {\n\t*s &^= 0b1 << 11\n\t*s |= CharProps(val&0b1) << 11\n}\n\nfunc (s CharProps) Is_invalid() uint8 {\n\treturn uint8((s >> 12) & 0b1)\n}\n\nfunc (s *CharProps) Set_is_invalid(val uint8) {\n\t*s &^= 0b1 << 12\n\t*s |= CharProps(val&0b1) << 12\n}\n\nfunc (s CharProps) Is_emoji_presentation_base() uint8 {\n\treturn uint8((s >> 13) & 0b1)\n}\n\nfunc (s *CharProps) Set_is_emoji_presentation_base(val uint8) {\n\t*s &^= 0b1 << 13\n\t*s |= CharProps(val&0b1) << 13\n}\n\nfunc (s CharProps) Category() uint8 {\n\treturn uint8((s >> 14) & 0b11111)\n}\n\nfunc (s *CharProps) Set_category(val uint8) {\n\t*s &^= 0b11111 << 14\n\t*s |= CharProps(val&0b11111) << 14\n}\n\nfunc (s CharProps) Is_emoji() uint8 {\n\treturn uint8((s >> 19) & 0b1)\n}\n\nfunc (s *CharProps) Set_is_emoji(val uint8) {\n\t*s &^= 0b1 << 19\n\t*s |= CharProps(val&0b1) << 19\n}\n\nfunc (s CharProps) Shifted_width() uint8 {\n\treturn uint8((s >> 20) & 0b111)\n}\n\nfunc (s *CharProps) Set_shifted_width(val uint8) {\n\t*s &^= 0b111 << 20\n\t*s |= CharProps(val&0b111) << 20\n}\n\n// Total number of bits used: 10\ntype GraphemeSegmentationResult uint16\n\nfunc (s GraphemeSegmentationResult) Grapheme_break() uint8 {\n\treturn uint8(s & 0b1111)\n}\n\nfunc (s *GraphemeSegmentationResult) Set_grapheme_break(val uint8) {\n\t*s &^= 0b1111\n\t*s |= GraphemeSegmentationResult(val & 0b1111)\n}\n\nfunc (s GraphemeSegmentationResult) Incb_consonant_extended() uint8 {\n\treturn uint8((s >> 4) & 0b1)\n}\n\nfunc (s *GraphemeSegmentationResult) Set_incb_consonant_extended(val uint8) {\n\t*s &^= 0b1 << 4\n\t*s |= GraphemeSegmentationResult(val&0b1) << 4\n}\n\nfunc (s GraphemeSegmentationResult) Incb_consonant_extended_linker() uint8 {\n\treturn uint8((s >> 5) & 0b1)\n}\n\nfunc (s *GraphemeSegmentationResult) Set_incb_consonant_extended_linker(val uint8) {\n\t*s &^= 0b1 << 5\n\t*s |= GraphemeSegmentationResult(val&0b1) << 5\n}\n\nfunc (s GraphemeSegmentationResult) Incb_consonant_extended_linker_extended() uint8 {\n\treturn uint8((s >> 6) & 0b1)\n}\n\nfunc (s *GraphemeSegmentationResult) Set_incb_consonant_extended_linker_extended(val uint8) {\n\t*s &^= 0b1 << 6\n\t*s |= GraphemeSegmentationResult(val&0b1) << 6\n}\n\nfunc (s GraphemeSegmentationResult) Emoji_modifier_sequence() uint8 {\n\treturn uint8((s >> 7) & 0b1)\n}\n\nfunc (s *GraphemeSegmentationResult) Set_emoji_modifier_sequence(val uint8) {\n\t*s &^= 0b1 << 7\n\t*s |= GraphemeSegmentationResult(val&0b1) << 7\n}\n\nfunc (s GraphemeSegmentationResult) Emoji_modifier_sequence_before_last_char() uint8 {\n\treturn uint8((s >> 8) & 0b1)\n}\n\nfunc (s *GraphemeSegmentationResult) Set_emoji_modifier_sequence_before_last_char(val uint8) {\n\t*s &^= 0b1 << 8\n\t*s |= GraphemeSegmentationResult(val&0b1) << 8\n}\n\nfunc (s GraphemeSegmentationResult) Add_to_current_cell() uint8 {\n\treturn uint8((s >> 9) & 0b1)\n}\n\nfunc (s *GraphemeSegmentationResult) Set_add_to_current_cell(val uint8) {\n\t*s &^= 0b1 << 9\n\t*s |= GraphemeSegmentationResult(val&0b1) << 9\n}\n\nfunc (s CharProps) Width() int {\n\treturn int(s.Shifted_width()) - 4\n}\n\nfunc (s CharProps) GraphemeSegmentationProperty() uint16 {\n\treturn uint16(s.Grapheme_break() | (s.Indic_conjunct_break() << 4) | (s.Is_extended_pictographic() << 6))\n}\n\nfunc (r GraphemeSegmentationResult) State() (ans uint16) {\n\treturn uint16(r) & 511\n}\n\nconst charprops_mask = 255\nconst charprops_shift = 8\n\nvar charprops_t1 = [4352]uint8{\n\t0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 54, 52, 52, 52, 55, 21, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 63, 64, 65, 66, 67, 68, 62, 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 52, 72, 73, 21, 74, 75, 76, 77, 78, 79, 80, 81, 82, 21, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 21, 21, 21, 108, 109, 110, 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, 21, 21, 21, 21, 113, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 114, 21, 21, 115, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 116, 111, 111, 111, 111, 111, 111, 21, 21, 117, 118, 111, 119, 120, 121, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 122, 123, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 124, 52, 125, 126, 111, 111, 111, 111, 111, 111, 111, 111, 111, 127, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 128, 40, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 40, 40, 139, 111, 111, 111, 111, 140, 141, 142, 143, 111, 144, 145, 146, 147, 148, 149, 111, 111, 150, 151, 152, 111, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 165, 165, 166, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 167, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 168, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 169, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 170, 52, 52, 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 52, 52, 173, 172, 172, 172, 172, 174, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 175, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 176, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 174, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 178, 179, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 177, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 181, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 181,\n}\nvar charprops_t2 = [46592]uint8{\n\t0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 5, 6, 7, 5, 5, 5, 8, 9, 6, 10, 5, 11, 5, 5, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 5, 5, 10, 10, 10, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 8, 5, 9, 14, 15, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 10, 9, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 17, 7, 7, 18, 7, 19, 17, 20, 21, 22, 23, 10, 24, 25, 14, 26, 27, 28, 28, 20, 16, 17, 17, 20, 28, 22, 29, 28, 28, 28, 17, 13, 13, 13, 13, 13, 13, 30, 13, 13, 13, 13, 13, 13, 13, 13, 13, 30, 13, 13, 13, 13, 13, 13, 27, 30, 13, 13, 13, 13, 13, 30, 31, 31, 31, 16, 16, 16, 16, 31, 16, 31, 31, 31, 16, 31, 31, 16, 16, 31, 16, 31, 31, 16, 16, 16, 27, 31, 31, 31, 16, 31, 16, 31, 16, 13, 31, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 31, 13, 31, 13, 16, 13, 16, 13, 16, 13, 31, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 30, 31, 13, 16, 13, 31, 13, 16, 13, 16, 13, 31, 30, 31, 13, 16, 13, 16, 31, 13, 16, 13, 16, 13, 16, 30, 31, 30, 31, 13, 31, 13, 16, 13, 31, 31, 30, 31, 13, 31, 13, 16, 13, 16, 30, 31, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 30, 31, 13, 16, 13, 31, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 13, 16, 13, 16, 13, 16, 16, 16, 13, 13, 16, 13, 16, 13, 13, 16, 13, 13, 13, 16, 16, 13, 13, 13, 13, 16, 13, 13, 16, 13, 13, 13, 16, 16, 16, 13, 13, 16, 13, 13, 16, 13, 16, 13, 16, 13, 13, 16, 13, 16, 16, 13, 16, 13, 13, 16, 13, 13, 13, 16, 13, 16, 13, 13, 16, 16, 32, 13, 16, 16, 16, 32, 32, 32, 32, 13, 33, 16, 13, 33, 16, 13, 33, 16, 13, 31, 13, 31, 13, 31, 13, 31, 13, 31, 13, 31, 13, 31, 13, 31, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 13, 33, 16, 13, 16, 13, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 16, 16, 16, 16, 13, 13, 16, 13, 13, 16, 16, 13, 16, 13, 13, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 14, 14, 20, 14, 34, 35, 34, 35, 35, 35, 34, 35, 34, 34, 35, 34, 14, 14, 14, 14, 14, 14, 20, 20, 20, 20, 14, 20, 14, 20, 34, 34, 34, 34, 34, 14, 14, 14, 14, 14, 14, 14, 34, 14, 34, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 13, 16, 13, 16, 34, 14, 13, 16, 38, 38, 34, 16, 16, 16, 5, 13, 38, 38, 38, 38, 14, 14, 13, 5, 13, 13, 13, 38, 13, 38, 13, 13, 16, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 38, 30, 30, 30, 30, 30, 30, 30, 13, 13, 16, 16, 16, 16, 16, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 16, 31, 31, 31, 31, 31, 31, 31, 16, 16, 16, 16, 16, 13, 16, 16, 13, 13, 13, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 16, 16, 13, 16, 10, 13, 16, 13, 13, 16, 16, 13, 13, 13, 13, 30, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 16, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 19, 36, 36, 36, 36, 36, 39, 39, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 34, 5, 5, 5, 5, 5, 5, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 5, 11, 38, 38, 19, 19, 7, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 11, 36, 5, 36, 36, 5, 36, 36, 5, 36, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 32, 32, 32, 32, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 40, 40, 40, 40, 40, 10, 10, 10, 5, 5, 7, 5, 5, 19, 19, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 5, 24, 5, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 5, 5, 32, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 32, 36, 36, 36, 36, 36, 36, 36, 40, 19, 36, 36, 36, 36, 36, 36, 34, 34, 36, 36, 19, 36, 36, 36, 36, 32, 32, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 19, 19, 32, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 40, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 34, 34, 19, 5, 5, 5, 34, 38, 38, 36, 7, 7, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 34, 36, 36, 36, 36, 36, 36, 36, 36, 36, 34, 36, 36, 36, 34, 36, 36, 36, 36, 36, 38, 38, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 38, 38, 5, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 14, 32, 32, 32, 32, 32, 32, 32, 40, 40, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 42, 36, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 44, 42, 42, 32, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 36, 36, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 34, 32, 32, 32, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 32, 36, 42, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 38, 38, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 38, 43, 38, 38, 38, 43, 43, 43, 43, 38, 38, 36, 32, 45, 42, 42, 36, 36, 36, 36, 38, 38, 42, 42, 38, 38, 42, 42, 44, 32, 38, 38, 38, 38, 38, 38, 38, 38, 45, 38, 38, 38, 38, 43, 43, 38, 43, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 43, 43, 7, 7, 46, 46, 46, 46, 46, 46, 19, 7, 32, 5, 36, 38, 38, 36, 36, 42, 38, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 38, 32, 32, 38, 38, 36, 38, 42, 42, 42, 36, 36, 38, 38, 38, 38, 36, 36, 38, 38, 36, 36, 36, 38, 38, 38, 36, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 38, 32, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 36, 36, 32, 32, 32, 36, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 38, 43, 43, 43, 43, 43, 38, 38, 36, 32, 42, 42, 42, 36, 36, 36, 36, 36, 38, 36, 36, 42, 38, 42, 42, 44, 38, 38, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 7, 38, 38, 38, 38, 38, 38, 38, 43, 36, 36, 36, 36, 36, 36, 38, 36, 42, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 38, 38, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 38, 43, 43, 43, 43, 43, 38, 38, 36, 32, 45, 36, 42, 36, 36, 36, 36, 38, 38, 42, 42, 38, 38, 42, 42, 44, 38, 38, 38, 38, 38, 38, 38, 36, 36, 45, 38, 38, 38, 38, 43, 43, 38, 43, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 19, 43, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 32, 38, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 38, 32, 32, 38, 32, 38, 32, 32, 38, 38, 38, 32, 32, 38, 38, 38, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 45, 42, 36, 42, 42, 38, 38, 38, 42, 42, 42, 38, 42, 42, 42, 36, 38, 38, 32, 38, 38, 38, 38, 38, 38, 45, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 19, 19, 19, 19, 19, 19, 7, 19, 38, 38, 38, 38, 38, 36, 42, 42, 42, 36, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 38, 36, 32, 36, 36, 36, 42, 42, 42, 42, 38, 36, 36, 36, 38, 36, 36, 36, 44, 38, 38, 38, 38, 38, 38, 38, 36, 36, 38, 43, 43, 43, 38, 32, 32, 38, 38, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 5, 46, 46, 46, 46, 46, 46, 46, 19, 32, 36, 42, 42, 5, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 38, 38, 36, 32, 42, 36, 45, 42, 45, 42, 42, 38, 36, 45, 45, 38, 45, 45, 36, 36, 38, 38, 38, 38, 38, 38, 38, 45, 45, 38, 38, 38, 38, 38, 32, 32, 32, 38, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 32, 32, 42, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 42, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 36, 32, 45, 42, 42, 36, 36, 36, 36, 38, 42, 42, 42, 38, 42, 42, 42, 44, 47, 19, 38, 38, 38, 38, 32, 32, 32, 45, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 36, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 32, 32, 32, 32, 32, 32, 38, 36, 42, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 36, 38, 38, 38, 38, 45, 42, 42, 36, 36, 36, 38, 36, 38, 42, 42, 42, 42, 42, 42, 42, 45, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 42, 42, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 32, 48, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 7, 32, 32, 32, 32, 32, 32, 34, 36, 36, 36, 36, 36, 36, 36, 36, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 38, 32, 38, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 32, 48, 36, 36, 36, 36, 36, 36, 36, 36, 36, 32, 38, 38, 32, 32, 32, 32, 32, 38, 34, 38, 36, 36, 36, 36, 36, 36, 36, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 19, 19, 19, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19, 5, 19, 19, 19, 36, 36, 19, 19, 19, 19, 19, 19, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 36, 19, 36, 19, 36, 8, 9, 8, 9, 42, 42, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 5, 36, 36, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 19, 19, 19, 19, 19, 19, 19, 19, 36, 19, 19, 19, 19, 19, 19, 38, 19, 19, 5, 5, 5, 5, 5, 19, 19, 19, 19, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 49, 49, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 36, 49, 44, 36, 42, 42, 36, 36, 43, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 5, 5, 5, 5, 43, 43, 43, 43, 43, 43, 42, 42, 36, 36, 43, 43, 43, 43, 36, 36, 36, 43, 49, 49, 49, 43, 43, 49, 49, 49, 49, 49, 49, 49, 43, 43, 43, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 49, 42, 36, 36, 49, 49, 49, 49, 49, 49, 36, 43, 49, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 49, 49, 49, 36, 19, 19, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 13, 38, 38, 38, 38, 38, 13, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 5, 34, 16, 16, 16, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 36, 36, 36, 5, 5, 5, 5, 5, 5, 5, 5, 5, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 16, 16, 16, 16, 16, 16, 38, 38, 11, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 4, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 8, 9, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 5, 5, 55, 55, 55, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 45, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 45, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 38, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 37, 37, 42, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 42, 42, 36, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 36, 5, 5, 5, 34, 5, 5, 5, 7, 32, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 11, 5, 5, 5, 5, 36, 36, 36, 24, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 36, 36, 36, 42, 42, 42, 42, 36, 36, 42, 42, 42, 38, 38, 38, 38, 42, 42, 36, 42, 42, 42, 42, 42, 42, 36, 36, 36, 38, 38, 38, 38, 19, 38, 38, 38, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 42, 42, 36, 38, 38, 5, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 36, 42, 36, 36, 36, 36, 36, 36, 36, 38, 44, 49, 36, 49, 49, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 34, 5, 5, 5, 5, 5, 5, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 42, 32, 32, 32, 32, 32, 32, 43, 43, 32, 32, 32, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 45, 36, 36, 36, 36, 36, 45, 36, 45, 42, 42, 42, 42, 36, 45, 56, 43, 43, 43, 43, 43, 43, 43, 43, 38, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 5, 5, 5, 5, 5, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 36, 36, 36, 36, 36, 36, 36, 36, 19, 19, 19, 19, 19, 19, 19, 19, 19, 5, 5, 5, 36, 36, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 36, 36, 36, 36, 42, 42, 36, 36, 45, 44, 36, 36, 43, 43, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 43, 43, 43, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 42, 36, 36, 42, 42, 42, 36, 42, 36, 36, 36, 45, 45, 38, 38, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 42, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 36, 36, 38, 38, 38, 5, 5, 5, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 32, 32, 32, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 5, 5, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 13, 13, 13, 5, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 5, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 36, 32, 32, 32, 32, 32, 32, 36, 32, 32, 42, 36, 36, 32, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 38, 38, 13, 13, 13, 13, 13, 13, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 38, 38, 13, 13, 13, 13, 13, 13, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 38, 13, 38, 13, 38, 13, 38, 13, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 33, 33, 33, 33, 33, 33, 33, 33, 16, 16, 16, 16, 16, 16, 16, 16, 33, 33, 33, 33, 33, 33, 33, 33, 16, 16, 16, 16, 16, 16, 16, 16, 33, 33, 33, 33, 33, 33, 33, 33, 16, 16, 16, 16, 16, 38, 16, 16, 13, 13, 13, 13, 33, 14, 16, 14, 14, 14, 16, 16, 16, 38, 16, 16, 13, 13, 13, 13, 33, 14, 14, 14, 16, 16, 16, 16, 38, 38, 16, 16, 13, 13, 13, 13, 38, 14, 14, 14, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 14, 14, 14, 38, 38, 16, 16, 16, 38, 16, 16, 13, 13, 13, 13, 33, 14, 14, 38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 57, 58, 24, 24, 59, 11, 11, 59, 59, 59, 17, 5, 60, 61, 8, 23, 60, 61, 8, 23, 17, 17, 17, 5, 17, 17, 17, 17, 62, 63, 24, 24, 24, 24, 24, 4, 17, 5, 17, 17, 5, 17, 5, 5, 5, 23, 29, 17, 64, 5, 17, 15, 15, 5, 5, 5, 10, 8, 9, 5, 5, 64, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 15, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 24, 24, 24, 24, 24, 65, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 46, 34, 38, 38, 28, 46, 46, 46, 46, 46, 10, 10, 10, 8, 9, 35, 46, 28, 28, 28, 28, 46, 46, 46, 46, 46, 10, 10, 10, 8, 9, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 38, 38, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 18, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, 39, 39, 39, 36, 39, 39, 39, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 13, 26, 19, 26, 19, 13, 19, 26, 16, 13, 13, 13, 16, 16, 13, 13, 13, 31, 19, 13, 26, 19, 10, 13, 13, 13, 13, 13, 19, 19, 19, 26, 25, 19, 13, 19, 30, 19, 13, 19, 13, 30, 13, 13, 19, 16, 13, 13, 13, 13, 16, 32, 32, 32, 32, 66, 19, 19, 16, 16, 13, 13, 10, 10, 10, 10, 10, 13, 16, 16, 16, 16, 19, 10, 19, 19, 16, 19, 46, 46, 46, 28, 28, 46, 46, 46, 46, 46, 46, 28, 28, 28, 28, 46, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 55, 55, 55, 55, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 55, 55, 55, 55, 55, 55, 55, 55, 55, 13, 16, 55, 55, 55, 55, 28, 19, 19, 38, 38, 38, 38, 27, 27, 27, 27, 68, 25, 25, 25, 25, 25, 10, 10, 19, 19, 19, 19, 10, 19, 19, 10, 19, 19, 10, 19, 19, 21, 21, 19, 19, 19, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 19, 19, 27, 19, 27, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 27, 27, 10, 10, 10, 27, 27, 10, 10, 27, 10, 10, 10, 27, 10, 27, 10, 10, 10, 27, 10, 10, 10, 10, 27, 10, 10, 27, 27, 27, 27, 10, 10, 27, 10, 27, 10, 27, 27, 27, 27, 27, 27, 10, 27, 10, 10, 10, 10, 10, 27, 27, 27, 27, 10, 10, 10, 10, 27, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 10, 10, 27, 10, 10, 10, 10, 10, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 27, 10, 10, 27, 27, 27, 27, 10, 10, 27, 27, 10, 10, 27, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 27, 10, 10, 27, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 10, 10, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 27, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 8, 9, 8, 9, 19, 19, 19, 19, 19, 19, 26, 19, 19, 19, 19, 19, 19, 19, 69, 69, 19, 19, 19, 19, 10, 10, 19, 19, 19, 19, 19, 19, 21, 70, 71, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 69, 69, 69, 69, 21, 21, 21, 69, 21, 21, 69, 19, 19, 19, 19, 21, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 46, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 19, 26, 26, 26, 26, 26, 26, 26, 21, 21, 19, 19, 19, 19, 19, 19, 26, 26, 19, 19, 25, 27, 19, 19, 19, 19, 26, 26, 19, 19, 25, 27, 19, 19, 19, 19, 26, 26, 26, 19, 19, 26, 19, 19, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 72, 72, 73, 73, 10, 21, 21, 21, 21, 21, 26, 26, 19, 19, 26, 19, 19, 19, 19, 25, 26, 19, 21, 19, 19, 69, 69, 19, 19, 21, 19, 19, 19, 26, 69, 26, 19, 21, 19, 21, 21, 19, 19, 21, 19, 19, 19, 21, 19, 19, 19, 21, 21, 74, 74, 74, 74, 74, 74, 74, 74, 21, 21, 21, 19, 19, 19, 19, 19, 25, 19, 25, 19, 19, 19, 19, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 25, 26, 19, 25, 26, 25, 21, 26, 25, 26, 26, 19, 26, 26, 19, 27, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 21, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 74, 74, 74, 74, 74, 74, 19, 19, 21, 69, 21, 21, 21, 21, 19, 21, 19, 21, 21, 19, 26, 26, 21, 69, 19, 19, 19, 19, 19, 21, 19, 19, 69, 69, 19, 19, 19, 19, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 69, 26, 19, 19, 19, 19, 69, 69, 26, 26, 25, 26, 26, 26, 26, 26, 69, 25, 26, 25, 26, 25, 69, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 26, 19, 19, 19, 19, 26, 25, 69, 26, 26, 26, 26, 26, 25, 25, 69, 69, 25, 69, 26, 25, 25, 69, 69, 26, 26, 69, 26, 26, 19, 19, 21, 19, 19, 69, 19, 19, 21, 21, 69, 69, 69, 69, 19, 21, 19, 19, 21, 19, 21, 19, 21, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 26, 19, 19, 19, 19, 19, 19, 21, 19, 19, 21, 19, 19, 19, 19, 69, 19, 69, 19, 19, 19, 19, 69, 69, 69, 19, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 19, 19, 19, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 10, 10, 10, 10, 10, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 72, 72, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 9, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 19, 19, 19, 21, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 19, 19, 10, 10, 10, 10, 10, 10, 19, 19, 19, 69, 19, 19, 19, 19, 69, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 13, 13, 13, 16, 16, 13, 16, 13, 16, 13, 16, 13, 13, 13, 13, 16, 13, 16, 16, 13, 16, 16, 16, 16, 16, 16, 34, 34, 13, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 19, 19, 19, 19, 19, 19, 13, 16, 13, 16, 36, 36, 36, 13, 16, 38, 38, 38, 38, 38, 5, 5, 5, 5, 46, 5, 5, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 16, 38, 38, 38, 38, 38, 16, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 34, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 5, 5, 23, 29, 23, 29, 5, 5, 5, 23, 29, 5, 23, 29, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 5, 5, 11, 5, 23, 29, 5, 5, 23, 29, 8, 9, 8, 9, 8, 9, 8, 9, 5, 5, 5, 5, 5, 34, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 11, 5, 5, 5, 5, 11, 5, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19, 19, 5, 5, 5, 8, 9, 8, 9, 8, 9, 8, 9, 11, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 76, 76, 76, 74, 77, 78, 79, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 74, 74, 70, 71, 70, 71, 70, 71, 70, 71, 80, 70, 71, 71, 74, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 81, 81, 81, 82, 82, 83, 77, 77, 77, 77, 77, 74, 74, 79, 79, 79, 77, 78, 84, 74, 19, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 81, 81, 85, 85, 77, 77, 78, 80, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 76, 77, 77, 77, 78, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 86, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 74, 74, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 28, 28, 28, 28, 28, 28, 28, 28, 74, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 69, 74, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 77, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 5, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 32, 36, 39, 39, 39, 5, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 5, 34, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 34, 34, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 36, 36, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 34, 34, 34, 34, 34, 34, 34, 34, 34, 14, 14, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 34, 16, 16, 16, 16, 16, 16, 16, 16, 13, 16, 13, 16, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 34, 14, 14, 13, 16, 13, 16, 32, 13, 16, 13, 16, 16, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 13, 13, 13, 13, 16, 13, 13, 13, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 13, 13, 13, 16, 13, 16, 13, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 16, 13, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 34, 34, 34, 34, 13, 16, 32, 34, 34, 16, 32, 32, 32, 32, 32, 32, 32, 36, 32, 32, 32, 36, 32, 32, 32, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 36, 36, 42, 19, 19, 19, 19, 36, 38, 38, 38, 46, 46, 46, 46, 46, 46, 19, 19, 7, 19, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 42, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 32, 32, 5, 5, 5, 32, 5, 32, 32, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 45, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 5, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 38, 38, 38, 36, 36, 36, 42, 32, 32, 32, 32, 32, 43, 43, 43, 32, 32, 32, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 42, 42, 36, 36, 36, 36, 42, 42, 36, 36, 42, 42, 56, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 34, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 5, 5, 43, 43, 43, 43, 43, 36, 34, 43, 43, 43, 43, 43, 43, 43, 43, 43, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 43, 43, 43, 43, 43, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 42, 42, 36, 36, 42, 42, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 36, 42, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 5, 5, 5, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 34, 43, 43, 43, 32, 32, 32, 19, 19, 19, 43, 49, 36, 49, 43, 43, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 32, 36, 36, 36, 32, 32, 36, 36, 32, 32, 32, 32, 32, 36, 36, 32, 36, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 34, 5, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 36, 36, 42, 42, 5, 5, 32, 34, 34, 42, 44, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 14, 34, 34, 34, 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, 14, 14, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 36, 42, 42, 36, 42, 42, 5, 42, 36, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 38, 38, 38, 38, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 38, 38, 38, 38, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 32, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 9, 8, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 19, 19, 19, 19, 19, 19, 19, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 7, 19, 19, 19, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 76, 76, 76, 76, 76, 76, 76, 70, 71, 76, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 80, 80, 94, 94, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 70, 71, 76, 76, 70, 71, 76, 76, 76, 76, 94, 94, 94, 76, 76, 76, 38, 76, 76, 76, 76, 80, 70, 71, 70, 71, 70, 71, 76, 76, 76, 95, 80, 95, 95, 95, 38, 76, 96, 76, 76, 38, 38, 38, 38, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 24, 38, 76, 76, 76, 96, 76, 76, 76, 70, 71, 76, 95, 76, 80, 76, 76, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 76, 76, 95, 95, 95, 76, 76, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 70, 76, 71, 85, 94, 85, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 70, 95, 71, 95, 70, 71, 5, 8, 9, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 100, 100, 101, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 38, 38, 38, 96, 96, 95, 85, 74, 96, 96, 38, 19, 10, 10, 10, 10, 19, 19, 38, 65, 65, 65, 65, 65, 65, 65, 65, 65, 24, 24, 24, 19, 26, 93, 93, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 5, 5, 5, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 46, 46, 46, 46, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 46, 46, 19, 19, 19, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 55, 32, 32, 32, 32, 32, 32, 32, 32, 55, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 5, 55, 55, 55, 55, 55, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 5, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 13, 13, 38, 13, 13, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 34, 34, 34, 34, 34, 34, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 38, 38, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 38, 38, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 5, 46, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 19, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 46, 46, 46, 38, 38, 38, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 46, 46, 32, 32, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 43, 36, 36, 36, 38, 36, 36, 38, 38, 38, 38, 38, 36, 36, 36, 36, 43, 43, 43, 43, 38, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 38, 36, 36, 36, 38, 38, 38, 38, 44, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 19, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 38, 38, 38, 38, 46, 46, 46, 46, 46, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 32, 32, 32, 34, 32, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 38, 36, 36, 36, 36, 36, 11, 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 38, 10, 10, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 36, 36, 11, 38, 38, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 34, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 5, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 32, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 46, 46, 46, 46, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 42, 36, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 36, 32, 32, 36, 36, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 42, 42, 36, 36, 5, 5, 40, 5, 5, 5, 5, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 36, 44, 36, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 5, 5, 43, 42, 42, 43, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 5, 5, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 42, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 45, 32, 47, 47, 32, 5, 5, 5, 5, 36, 36, 36, 36, 5, 42, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 32, 5, 32, 5, 5, 5, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 42, 42, 36, 45, 36, 36, 5, 5, 5, 5, 5, 5, 36, 32, 32, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 36, 36, 42, 42, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 38, 36, 36, 32, 45, 42, 36, 42, 42, 42, 42, 38, 38, 42, 42, 38, 38, 42, 42, 45, 38, 38, 32, 38, 38, 38, 38, 38, 38, 45, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 42, 42, 38, 38, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 38, 38, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 32, 45, 42, 42, 36, 36, 36, 36, 36, 36, 38, 45, 38, 38, 45, 38, 45, 45, 45, 42, 38, 42, 42, 36, 45, 44, 47, 36, 32, 5, 5, 38, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 36, 36, 36, 42, 36, 32, 32, 32, 32, 5, 5, 5, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 5, 5, 38, 5, 36, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 42, 42, 36, 36, 36, 36, 36, 36, 42, 36, 42, 42, 45, 42, 36, 36, 42, 36, 36, 32, 32, 5, 32, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 42, 42, 36, 36, 36, 36, 38, 38, 42, 42, 42, 42, 36, 36, 42, 36, 36, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 32, 32, 32, 32, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 36, 42, 36, 36, 5, 5, 5, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 42, 36, 42, 42, 36, 36, 36, 36, 36, 36, 45, 36, 32, 5, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 36, 42, 36, 49, 49, 36, 36, 36, 36, 42, 36, 36, 36, 36, 36, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 5, 5, 5, 19, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 36, 36, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 43, 43, 43, 43, 43, 43, 43, 38, 38, 43, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 45, 42, 42, 42, 42, 42, 38, 42, 42, 38, 38, 36, 36, 45, 44, 47, 42, 47, 42, 36, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 36, 36, 36, 36, 38, 38, 36, 36, 42, 42, 42, 42, 36, 32, 5, 32, 42, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 42, 32, 36, 36, 36, 36, 5, 5, 5, 5, 5, 5, 5, 5, 44, 38, 38, 38, 38, 38, 38, 38, 38, 43, 36, 36, 36, 36, 36, 36, 42, 42, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 47, 47, 47, 47, 47, 47, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 36, 44, 5, 5, 5, 32, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 42, 36, 36, 36, 42, 36, 42, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 36, 36, 36, 36, 36, 36, 36, 38, 36, 36, 36, 36, 36, 36, 42, 36, 32, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 42, 36, 36, 36, 36, 36, 36, 36, 42, 36, 36, 42, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 38, 38, 38, 36, 38, 36, 36, 38, 36, 36, 36, 36, 36, 36, 36, 47, 36, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42, 42, 42, 42, 42, 38, 36, 36, 38, 42, 42, 36, 42, 36, 32, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 32, 32, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 42, 42, 5, 5, 38, 38, 38, 38, 38, 38, 38, 36, 36, 47, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 36, 36, 36, 36, 36, 38, 38, 38, 42, 42, 36, 45, 44, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 19, 19, 19, 19, 19, 19, 19, 7, 7, 7, 7, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 38, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 36, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 5, 5, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 36, 36, 36, 36, 36, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 5, 5, 5, 5, 5, 19, 19, 19, 19, 34, 34, 34, 34, 5, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 46, 46, 46, 46, 46, 46, 46, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 34, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 53, 32, 32, 32, 53, 53, 53, 53, 34, 34, 5, 5, 5, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 5, 5, 5, 5, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 36, 32, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 77, 77, 76, 77, 81, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 82, 82, 77, 77, 79, 79, 79, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 77, 77, 77, 77, 38, 77, 77, 77, 77, 77, 77, 77, 38, 77, 77, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 38, 38, 78, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 38, 38, 38, 38, 38, 38, 38, 38, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 19, 36, 36, 5, 24, 24, 24, 24, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 19, 19, 19, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 45, 45, 36, 36, 36, 19, 19, 19, 45, 45, 45, 45, 45, 45, 24, 24, 24, 24, 24, 24, 24, 24, 36, 36, 36, 36, 36, 36, 36, 36, 19, 19, 36, 36, 36, 36, 36, 36, 36, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 36, 36, 36, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 36, 36, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 38, 38, 38, 38, 38, 38, 38, 38, 38, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 38, 13, 13, 38, 38, 13, 38, 38, 13, 13, 38, 38, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 38, 16, 38, 16, 16, 16, 16, 16, 16, 16, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 38, 13, 13, 13, 13, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 13, 13, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 38, 13, 13, 13, 13, 38, 13, 13, 13, 13, 13, 38, 13, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 16, 16, 16, 16, 16, 16, 13, 16, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 19, 19, 19, 19, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 19, 19, 19, 19, 19, 19, 19, 19, 36, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 36, 19, 19, 5, 5, 5, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 16, 16, 16, 16, 16, 16, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 38, 38, 36, 36, 36, 36, 36, 36, 36, 38, 36, 36, 38, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 36, 36, 36, 36, 36, 36, 36, 34, 34, 34, 34, 34, 34, 34, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 32, 19, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 7, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 36, 32, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 36, 32, 32, 36, 32, 32, 32, 32, 32, 32, 32, 36, 36, 32, 32, 32, 32, 32, 36, 38, 38, 38, 38, 38, 38, 38, 38, 32, 34, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 36, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 36, 36, 36, 36, 36, 36, 36, 34, 38, 38, 38, 38, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 38, 38, 38, 5, 5, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 46, 46, 46, 7, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 19, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 38, 32, 38, 38, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 32, 38, 32, 38, 38, 38, 38, 38, 38, 32, 38, 38, 38, 38, 32, 38, 32, 38, 32, 38, 32, 32, 32, 38, 32, 32, 38, 32, 38, 38, 32, 38, 32, 38, 32, 38, 32, 38, 32, 38, 32, 32, 38, 32, 38, 38, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 32, 32, 32, 32, 38, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 32, 32, 32, 38, 32, 32, 32, 32, 32, 38, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 10, 10, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 19, 19, 19, 19, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 46, 46, 19, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 19, 19, 19, 19, 19, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 69, 26, 26, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 19, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 74, 69, 69, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 69, 74, 74, 69, 69, 69, 69, 69, 69, 69, 69, 69, 74, 102, 102, 102, 102, 74, 74, 74, 74, 74, 74, 74, 74, 74, 102, 102, 102, 102, 102, 102, 102, 69, 69, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 74, 74, 74, 74, 74, 74, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 19, 19, 21, 21, 21, 21, 21, 21, 21, 21, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 21, 21, 19, 21, 21, 21, 19, 19, 21, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 21, 69, 69, 69, 69, 69, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 21, 69, 21, 19, 21, 69, 69, 69, 104, 104, 104, 104, 104, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 69, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 21, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 69, 69, 69, 69, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 21, 21, 19, 19, 21, 69, 69, 21, 21, 21, 21, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 21, 21, 21, 21, 19, 19, 69, 19, 19, 19, 19, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 21, 19, 19, 21, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 19, 19, 21, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 21, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 21, 19, 19, 21, 19, 21, 19, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 21, 19, 19, 19, 21, 19, 19, 19, 19, 19, 19, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 21, 69, 21, 21, 21, 69, 69, 69, 19, 19, 69, 69, 69, 69, 102, 102, 102, 69, 69, 69, 69, 21, 21, 21, 21, 21, 21, 19, 19, 19, 21, 19, 69, 69, 102, 102, 102, 21, 19, 19, 21, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 102, 69, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 10, 10, 10, 10, 10, 10, 10, 10, 10, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 69, 102, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 102, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 102, 102, 102, 102, 102, 102, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 38, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 19, 38, 38, 38, 38, 38, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 93, 93, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 93, 93, 65, 24, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 93, 93,\n}\nvar charprops_t3 = [106]CharProps{\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Control) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((1 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),            // 0\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Control) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((1 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((3 & 0b111) << 20),            // 1\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_LF) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((1 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((3 & 0b111) << 20),                 // 2\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_CR) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((1 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((3 & 0b111) << 20),                 // 3\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Zs) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 4\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Po) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 5\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Po) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((5 & 0b111) << 20),               // 6\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 7\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Ps) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 8\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pe) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 9\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sm) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 10\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pd) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 11\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Nd) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((5 & 0b111) << 20),               // 12\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lu) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 13\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sk) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 14\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 15\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Ll) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 16\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Po) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 17\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 18\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_So) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 19\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sk) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 20\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_So) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((5 & 0b111) << 20),               // 21\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 22\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pi) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 23\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Control) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cf) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),            // 24\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_So) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((2 & 0b111) << 20),               // 25\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_So) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 26\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sm) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 27\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_No) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 28\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pf) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 29\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lu) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 30\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Ll) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 31\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 32\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lt) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 33\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lm) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 34\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lm) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 35\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),           // 36\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),           // 37\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((0 & 0b111) << 20),               // 38\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Me) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),           // 39\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Prepend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cf) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),            // 40\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Nd) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 41\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_SpacingMark) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),        // 42\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Consonant) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),          // 43\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Linker) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),           // 44\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),           // 45\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_No) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 46\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Prepend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),            // 47\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_SpacingMark) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),        // 48\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),               // 49\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_L) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),                  // 50\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_L) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),                  // 51\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_V) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),                  // 52\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_V) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),                  // 53\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_T) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),                  // 54\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Nl) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),               // 55\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Linker) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),           // 56\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cf) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),             // 57\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_ZWJ) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cf) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),              // 58\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pd) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 59\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pi) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 60\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pf) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 61\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Control) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Zl) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),            // 62\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Control) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Zp) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),            // 63\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Po) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((5 & 0b111) << 20),               // 64\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Control) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),            // 65\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Ll) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((5 & 0b111) << 20),               // 66\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Nl) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 67\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Sm) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((2 & 0b111) << 20),               // 68\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_So) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((6 & 0b111) << 20),               // 69\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Ps) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 70\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pe) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 71\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Sm) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((5 & 0b111) << 20),               // 72\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Sm) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((6 & 0b111) << 20),               // 73\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_So) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 74\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Zs) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 75\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Po) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 76\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lm) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 77\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 78\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Nl) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 79\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pd) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 80\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),           // 81\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Mc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),           // 82\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Pd) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((6 & 0b111) << 20),               // 83\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Po) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((6 & 0b111) << 20),               // 84\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sk) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 85\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 86\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_No) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 87\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_LV) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),                 // 88\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_LVT) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),                // 89\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((1 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cs) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((3 & 0b111) << 20),               // 90\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Co) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((2 & 0b111) << 20),               // 91\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 92\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((1 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((3 & 0b111) << 20),               // 93\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Pc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 94\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sm) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 95\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Sc) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 96\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Nd) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 97\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lu) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 98\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Ll) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((6 & 0b111) << 20),               // 99\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lm) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((5 & 0b111) << 20),           // 100\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Lo) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),               // 101\n\t((1 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_None) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9) | ((0 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cn) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((0 & 0b111) << 20),               // 102\n\t((0 & 0b1) << 0) | ((CharProps(ICB_None) & 0b11) << 1) | ((CharProps(GBP_Regional_Indicator) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_So) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((6 & 0b111) << 20), // 103\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((1 & 0b1) << 10) | ((0 & 0b1) << 11) | ((0 & 0b1) << 12) | ((1 & 0b1) << 13) | ((CharProps(UC_Sk) & 0b11111) << 14) | ((1 & 0b1) << 19) | ((6 & 0b111) << 20),           // 104\n\t((0 & 0b1) << 0) | ((CharProps(ICB_Extend) & 0b11) << 1) | ((CharProps(GBP_Extend) & 0b1111) << 3) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9) | ((0 & 0b1) << 10) | ((1 & 0b1) << 11) | ((0 & 0b1) << 12) | ((0 & 0b1) << 13) | ((CharProps(UC_Cf) & 0b11111) << 14) | ((0 & 0b1) << 19) | ((4 & 0b111) << 20),           // 105\n}\n\n// Array accessor function that avoids bounds checking\nfunc charprops_for(x uint32) CharProps {\n\tt1 := uintptr(*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&charprops_t1[0])) + uintptr(x>>charprops_shift)*1)))\n\tt1_shifted := (t1 << charprops_shift) + (uintptr(x) & charprops_mask)\n\tt2 := uintptr(*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&charprops_t2[0])) + t1_shifted*1)))\n\treturn *(*CharProps)(unsafe.Pointer(uintptr(unsafe.Pointer(&charprops_t3[0])) + t2*4))\n}\n\nconst graphemesegmentationresult_mask = 15\nconst graphemesegmentationresult_shift = 4\n\nvar graphemesegmentationresult_t1 = [4096]uint8{\n\t0, 0, 1, 0, 2, 2, 3, 2, 4, 4, 5, 4, 6, 6, 7, 6, 8, 8, 9, 8, 10, 10, 11, 10, 12, 12, 13, 12, 14, 14, 15, 14, 16, 16, 17, 16, 18, 18, 19, 18, 16, 16, 17, 16, 18, 18, 19, 18, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 20, 20, 21, 20, 22, 22, 23, 22, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 4, 4, 5, 4, 6, 6, 7, 6, 32, 32, 33, 32, 34, 34, 35, 34, 0, 36, 1, 1, 2, 37, 3, 3, 4, 38, 5, 5, 6, 39, 7, 7, 8, 40, 9, 9, 10, 41, 11, 11, 12, 42, 13, 13, 14, 43, 15, 15, 16, 44, 17, 17, 18, 45, 19, 19, 16, 44, 17, 17, 18, 45, 19, 19, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 20, 46, 21, 21, 22, 47, 23, 23, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 4, 38, 5, 5, 6, 39, 7, 7, 32, 52, 33, 33, 34, 53, 35, 35, 0, 0, 1, 0, 2, 2, 3, 2, 4, 4, 5, 4, 6, 6, 7, 6, 8, 8, 9, 8, 10, 10, 11, 10, 12, 12, 13, 12, 14, 14, 15, 14, 16, 16, 17, 16, 18, 18, 19, 18, 16, 16, 17, 16, 18, 18, 19, 18, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 20, 20, 21, 20, 22, 22, 23, 22, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 4, 4, 5, 4, 6, 6, 7, 6, 32, 32, 33, 32, 34, 34, 35, 34, 0, 36, 1, 1, 2, 37, 3, 3, 4, 38, 5, 5, 6, 39, 7, 7, 8, 40, 9, 9, 10, 41, 11, 11, 12, 42, 13, 13, 14, 43, 15, 15, 16, 44, 17, 17, 18, 45, 19, 19, 16, 44, 17, 17, 18, 45, 19, 19, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 20, 46, 21, 21, 22, 47, 23, 23, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 4, 38, 5, 5, 6, 39, 7, 7, 32, 52, 33, 33, 34, 53, 35, 35, 0, 54, 1, 54, 2, 55, 3, 55, 4, 56, 9, 56, 6, 57, 11, 57, 8, 58, 9, 58, 10, 59, 11, 59, 12, 60, 13, 60, 14, 61, 15, 61, 16, 62, 17, 62, 18, 63, 19, 63, 16, 62, 17, 62, 18, 63, 19, 63, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 20, 64, 9, 64, 22, 65, 11, 65, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 4, 56, 9, 56, 6, 57, 11, 57, 32, 70, 9, 70, 34, 71, 11, 71, 0, 36, 1, 72, 2, 37, 3, 73, 4, 38, 9, 74, 6, 39, 11, 75, 8, 40, 9, 76, 10, 41, 11, 77, 12, 42, 13, 78, 14, 43, 15, 79, 16, 44, 17, 80, 18, 45, 19, 81, 16, 44, 17, 80, 18, 45, 19, 81, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 20, 46, 9, 82, 22, 47, 11, 83, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 4, 38, 9, 74, 6, 39, 11, 75, 32, 52, 9, 88, 34, 53, 11, 89, 0, 54, 1, 54, 2, 55, 3, 55, 4, 56, 9, 56, 6, 57, 11, 57, 8, 58, 9, 58, 10, 59, 11, 59, 12, 60, 13, 60, 14, 61, 15, 61, 16, 62, 17, 62, 18, 63, 19, 63, 16, 62, 17, 62, 18, 63, 19, 63, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 20, 64, 9, 64, 22, 65, 11, 65, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 4, 56, 9, 56, 6, 57, 11, 57, 32, 70, 9, 70, 34, 71, 11, 71, 0, 36, 1, 72, 2, 37, 3, 73, 4, 38, 9, 74, 6, 39, 11, 75, 8, 40, 9, 76, 10, 41, 11, 77, 12, 42, 13, 78, 14, 43, 15, 79, 16, 44, 17, 80, 18, 45, 19, 81, 16, 44, 17, 80, 18, 45, 19, 81, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 20, 46, 9, 82, 22, 47, 11, 83, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 4, 38, 9, 74, 6, 39, 11, 75, 32, 52, 9, 88, 34, 53, 11, 89, 90, 90, 91, 90, 92, 92, 93, 92, 94, 94, 95, 94, 96, 96, 97, 96, 98, 98, 99, 98, 100, 100, 101, 100, 102, 102, 103, 102, 104, 104, 105, 104, 106, 106, 107, 106, 108, 108, 109, 108, 106, 106, 107, 106, 108, 108, 109, 108, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 110, 110, 111, 110, 112, 112, 113, 112, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 94, 94, 95, 94, 96, 96, 97, 96, 122, 122, 123, 122, 124, 124, 125, 124, 90, 126, 91, 91, 92, 127, 93, 93, 94, 128, 95, 95, 96, 129, 97, 97, 98, 130, 99, 99, 100, 131, 101, 101, 102, 132, 103, 103, 104, 133, 105, 105, 106, 134, 107, 107, 108, 135, 109, 109, 106, 134, 107, 107, 108, 135, 109, 109, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 110, 136, 111, 111, 112, 137, 113, 113, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 94, 128, 95, 95, 96, 129, 97, 97, 122, 142, 123, 123, 124, 143, 125, 125, 90, 90, 91, 90, 92, 92, 93, 92, 94, 94, 95, 94, 96, 96, 97, 96, 98, 98, 99, 98, 100, 100, 101, 100, 102, 102, 103, 102, 104, 104, 105, 104, 106, 106, 107, 106, 108, 108, 109, 108, 106, 106, 107, 106, 108, 108, 109, 108, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 110, 110, 111, 110, 112, 112, 113, 112, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 94, 94, 95, 94, 96, 96, 97, 96, 122, 122, 123, 122, 124, 124, 125, 124, 90, 126, 91, 91, 92, 127, 93, 93, 94, 128, 95, 95, 96, 129, 97, 97, 98, 130, 99, 99, 100, 131, 101, 101, 102, 132, 103, 103, 104, 133, 105, 105, 106, 134, 107, 107, 108, 135, 109, 109, 106, 134, 107, 107, 108, 135, 109, 109, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 110, 136, 111, 111, 112, 137, 113, 113, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 94, 128, 95, 95, 96, 129, 97, 97, 122, 142, 123, 123, 124, 143, 125, 125, 90, 144, 91, 144, 92, 145, 93, 145, 94, 146, 99, 146, 96, 147, 101, 147, 98, 148, 99, 148, 100, 149, 101, 149, 102, 150, 103, 150, 104, 151, 105, 151, 106, 152, 107, 152, 108, 153, 109, 153, 106, 152, 107, 152, 108, 153, 109, 153, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 110, 154, 99, 154, 112, 155, 101, 155, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 94, 146, 99, 146, 96, 147, 101, 147, 122, 160, 99, 160, 124, 161, 101, 161, 90, 126, 91, 162, 92, 127, 93, 163, 94, 128, 99, 164, 96, 129, 101, 165, 98, 130, 99, 166, 100, 131, 101, 167, 102, 132, 103, 168, 104, 133, 105, 169, 106, 134, 107, 170, 108, 135, 109, 171, 106, 134, 107, 170, 108, 135, 109, 171, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 110, 136, 99, 172, 112, 137, 101, 173, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 94, 128, 99, 164, 96, 129, 101, 165, 122, 142, 99, 178, 124, 143, 101, 179, 90, 144, 91, 144, 92, 145, 93, 145, 94, 146, 99, 146, 96, 147, 101, 147, 98, 148, 99, 148, 100, 149, 101, 149, 102, 150, 103, 150, 104, 151, 105, 151, 106, 152, 107, 152, 108, 153, 109, 153, 106, 152, 107, 152, 108, 153, 109, 153, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 110, 154, 99, 154, 112, 155, 101, 155, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 94, 146, 99, 146, 96, 147, 101, 147, 122, 160, 99, 160, 124, 161, 101, 161, 90, 126, 91, 162, 92, 127, 93, 163, 94, 128, 99, 164, 96, 129, 101, 165, 98, 130, 99, 166, 100, 131, 101, 167, 102, 132, 103, 168, 104, 133, 105, 169, 106, 134, 107, 170, 108, 135, 109, 171, 106, 134, 107, 170, 108, 135, 109, 171, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 110, 136, 99, 172, 112, 137, 101, 173, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 94, 128, 99, 164, 96, 129, 101, 165, 122, 142, 99, 178, 124, 143, 101, 179, 0, 0, 1, 0, 2, 2, 3, 2, 4, 4, 5, 4, 6, 6, 7, 6, 8, 8, 9, 8, 10, 10, 11, 10, 12, 12, 13, 12, 14, 14, 15, 14, 16, 16, 17, 16, 18, 18, 19, 18, 16, 16, 17, 16, 18, 18, 19, 18, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 20, 20, 21, 20, 22, 22, 23, 22, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 4, 4, 5, 4, 10, 10, 11, 10, 32, 32, 33, 32, 34, 34, 35, 34, 0, 36, 1, 1, 2, 37, 3, 3, 4, 38, 5, 5, 6, 39, 7, 7, 8, 40, 9, 9, 10, 41, 11, 11, 12, 42, 13, 13, 14, 43, 15, 15, 16, 44, 17, 17, 18, 45, 19, 19, 16, 44, 17, 17, 18, 45, 19, 19, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 20, 46, 21, 21, 22, 47, 23, 23, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 4, 38, 5, 5, 10, 41, 11, 11, 32, 52, 33, 33, 34, 53, 35, 35, 0, 0, 1, 0, 2, 2, 3, 2, 4, 4, 5, 4, 6, 6, 7, 6, 8, 8, 9, 8, 10, 10, 11, 10, 12, 12, 13, 12, 14, 14, 15, 14, 16, 16, 17, 16, 18, 18, 19, 18, 16, 16, 17, 16, 18, 18, 19, 18, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 4, 4, 5, 4, 6, 6, 7, 6, 20, 20, 21, 20, 22, 22, 23, 22, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 24, 24, 25, 24, 26, 26, 27, 26, 28, 28, 29, 28, 30, 30, 31, 30, 4, 4, 5, 4, 10, 10, 11, 10, 32, 32, 33, 32, 34, 34, 35, 34, 0, 36, 1, 1, 2, 37, 3, 3, 4, 38, 5, 5, 6, 39, 7, 7, 8, 40, 9, 9, 10, 41, 11, 11, 12, 42, 13, 13, 14, 43, 15, 15, 16, 44, 17, 17, 18, 45, 19, 19, 16, 44, 17, 17, 18, 45, 19, 19, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 4, 38, 5, 5, 6, 39, 7, 7, 20, 46, 21, 21, 22, 47, 23, 23, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 24, 48, 25, 25, 26, 49, 27, 27, 28, 50, 29, 29, 30, 51, 31, 31, 4, 38, 5, 5, 10, 41, 11, 11, 32, 52, 33, 33, 34, 53, 35, 35, 0, 54, 1, 54, 2, 55, 3, 55, 4, 56, 9, 56, 6, 57, 11, 57, 8, 58, 9, 58, 10, 59, 11, 59, 12, 60, 13, 60, 14, 61, 15, 61, 16, 62, 17, 62, 18, 63, 19, 63, 16, 62, 17, 62, 18, 63, 19, 63, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 20, 64, 9, 64, 22, 65, 11, 65, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 4, 56, 9, 56, 10, 59, 11, 59, 32, 70, 9, 70, 34, 71, 11, 71, 0, 36, 1, 72, 2, 37, 3, 73, 4, 38, 9, 74, 6, 39, 11, 75, 8, 40, 9, 76, 10, 41, 11, 77, 12, 42, 13, 78, 14, 43, 15, 79, 16, 44, 17, 80, 18, 45, 19, 81, 16, 44, 17, 80, 18, 45, 19, 81, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 20, 46, 9, 82, 22, 47, 11, 83, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 4, 38, 9, 74, 10, 41, 11, 77, 32, 52, 9, 88, 34, 53, 11, 89, 0, 54, 1, 54, 2, 55, 3, 55, 4, 56, 9, 56, 6, 57, 11, 57, 8, 58, 9, 58, 10, 59, 11, 59, 12, 60, 13, 60, 14, 61, 15, 61, 16, 62, 17, 62, 18, 63, 19, 63, 16, 62, 17, 62, 18, 63, 19, 63, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 4, 56, 9, 56, 6, 57, 11, 57, 20, 64, 9, 64, 22, 65, 11, 65, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 24, 66, 9, 66, 26, 67, 11, 67, 28, 68, 9, 68, 30, 69, 11, 69, 4, 56, 9, 56, 10, 59, 11, 59, 32, 70, 9, 70, 34, 71, 11, 71, 0, 36, 1, 72, 2, 37, 3, 73, 4, 38, 9, 74, 6, 39, 11, 75, 8, 40, 9, 76, 10, 41, 11, 77, 12, 42, 13, 78, 14, 43, 15, 79, 16, 44, 17, 80, 18, 45, 19, 81, 16, 44, 17, 80, 18, 45, 19, 81, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 4, 38, 9, 74, 6, 39, 11, 75, 20, 46, 9, 82, 22, 47, 11, 83, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 24, 48, 9, 84, 26, 49, 11, 85, 28, 50, 9, 86, 30, 51, 11, 87, 4, 38, 9, 74, 10, 41, 11, 77, 32, 52, 9, 88, 34, 53, 11, 89, 90, 90, 91, 90, 92, 92, 93, 92, 94, 94, 95, 94, 96, 96, 97, 96, 98, 98, 99, 98, 100, 100, 101, 100, 102, 102, 103, 102, 104, 104, 105, 104, 106, 106, 107, 106, 108, 108, 109, 108, 106, 106, 107, 106, 108, 108, 109, 108, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 110, 110, 111, 110, 112, 112, 113, 112, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 94, 94, 95, 94, 100, 100, 101, 100, 122, 122, 123, 122, 124, 124, 125, 124, 90, 126, 91, 91, 92, 127, 93, 93, 94, 128, 95, 95, 96, 129, 97, 97, 98, 130, 99, 99, 100, 131, 101, 101, 102, 132, 103, 103, 104, 133, 105, 105, 106, 134, 107, 107, 108, 135, 109, 109, 106, 134, 107, 107, 108, 135, 109, 109, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 110, 136, 111, 111, 112, 137, 113, 113, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 94, 128, 95, 95, 100, 131, 101, 101, 122, 142, 123, 123, 124, 143, 125, 125, 90, 90, 91, 90, 92, 92, 93, 92, 94, 94, 95, 94, 96, 96, 97, 96, 98, 98, 99, 98, 100, 100, 101, 100, 102, 102, 103, 102, 104, 104, 105, 104, 106, 106, 107, 106, 108, 108, 109, 108, 106, 106, 107, 106, 108, 108, 109, 108, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 94, 94, 95, 94, 96, 96, 97, 96, 110, 110, 111, 110, 112, 112, 113, 112, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 114, 114, 115, 114, 116, 116, 117, 116, 118, 118, 119, 118, 120, 120, 121, 120, 94, 94, 95, 94, 100, 100, 101, 100, 122, 122, 123, 122, 124, 124, 125, 124, 90, 126, 91, 91, 92, 127, 93, 93, 94, 128, 95, 95, 96, 129, 97, 97, 98, 130, 99, 99, 100, 131, 101, 101, 102, 132, 103, 103, 104, 133, 105, 105, 106, 134, 107, 107, 108, 135, 109, 109, 106, 134, 107, 107, 108, 135, 109, 109, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 94, 128, 95, 95, 96, 129, 97, 97, 110, 136, 111, 111, 112, 137, 113, 113, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 114, 138, 115, 115, 116, 139, 117, 117, 118, 140, 119, 119, 120, 141, 121, 121, 94, 128, 95, 95, 100, 131, 101, 101, 122, 142, 123, 123, 124, 143, 125, 125, 90, 144, 91, 144, 92, 145, 93, 145, 94, 146, 99, 146, 96, 147, 101, 147, 98, 148, 99, 148, 100, 149, 101, 149, 102, 150, 103, 150, 104, 151, 105, 151, 106, 152, 107, 152, 108, 153, 109, 153, 106, 152, 107, 152, 108, 153, 109, 153, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 110, 154, 99, 154, 112, 155, 101, 155, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 94, 146, 99, 146, 100, 149, 101, 149, 122, 160, 99, 160, 124, 161, 101, 161, 90, 126, 91, 162, 92, 127, 93, 163, 94, 128, 99, 164, 96, 129, 101, 165, 98, 130, 99, 166, 100, 131, 101, 167, 102, 132, 103, 168, 104, 133, 105, 169, 106, 134, 107, 170, 108, 135, 109, 171, 106, 134, 107, 170, 108, 135, 109, 171, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 110, 136, 99, 172, 112, 137, 101, 173, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 94, 128, 99, 164, 100, 131, 101, 167, 122, 142, 99, 178, 124, 143, 101, 179, 90, 144, 91, 144, 92, 145, 93, 145, 94, 146, 99, 146, 96, 147, 101, 147, 98, 148, 99, 148, 100, 149, 101, 149, 102, 150, 103, 150, 104, 151, 105, 151, 106, 152, 107, 152, 108, 153, 109, 153, 106, 152, 107, 152, 108, 153, 109, 153, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 94, 146, 99, 146, 96, 147, 101, 147, 110, 154, 99, 154, 112, 155, 101, 155, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 114, 156, 99, 156, 116, 157, 101, 157, 118, 158, 99, 158, 120, 159, 101, 159, 94, 146, 99, 146, 100, 149, 101, 149, 122, 160, 99, 160, 124, 161, 101, 161, 90, 126, 91, 162, 92, 127, 93, 163, 94, 128, 99, 164, 96, 129, 101, 165, 98, 130, 99, 166, 100, 131, 101, 167, 102, 132, 103, 168, 104, 133, 105, 169, 106, 134, 107, 170, 108, 135, 109, 171, 106, 134, 107, 170, 108, 135, 109, 171, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 94, 128, 99, 164, 96, 129, 101, 165, 110, 136, 99, 172, 112, 137, 101, 173, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 114, 138, 99, 174, 116, 139, 101, 175, 118, 140, 99, 176, 120, 141, 101, 177, 94, 128, 99, 164, 100, 131, 101, 167, 122, 142, 99, 178, 124, 143, 101, 179,\n}\nvar graphemesegmentationresult_t2 = [2880]GraphemeSegmentationResult{\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((0 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((0 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((1 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((0 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((0 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_AtStart) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_None) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Prepend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_CR) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LF) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Control) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Extend) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Regional_Indicator) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_SpacingMark) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_L) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_V) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_T) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LV) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_LVT) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_ZWJ) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((1 & 0b1) << 9),\n\t((GraphemeSegmentationResult(GBP_Private_Expecting_RI) & 0b1111) << 0) | ((1 & 0b1) << 4) | ((0 & 0b1) << 5) | ((1 & 0b1) << 6) | ((1 & 0b1) << 7) | ((1 & 0b1) << 8) | ((0 & 0b1) << 9),\n}\n\n// Array accessor function that avoids bounds checking\nfunc graphemesegmentationresult_for(x uint16) GraphemeSegmentationResult {\n\tt1 := uintptr(*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&graphemesegmentationresult_t1[0])) + uintptr(x>>graphemesegmentationresult_shift)*1)))\n\tt1_shifted := (t1 << graphemesegmentationresult_shift) + (uintptr(x) & graphemesegmentationresult_mask)\n\treturn *(*GraphemeSegmentationResult)(unsafe.Pointer(uintptr(unsafe.Pointer(&graphemesegmentationresult_t2[0])) + t1_shifted*2))\n}\n\nfunc grapheme_segmentation_key(r GraphemeSegmentationResult, ch CharProps) uint16 {\n\treturn (r.State() << 7) | ch.GraphemeSegmentationProperty()\n}\n"
  },
  {
    "path": "tools/wcswidth/char-props.go",
    "content": "package wcswidth\n\nimport (\n\t\"fmt\"\n\t\"iter\"\n)\n\nvar _ = fmt.Print\n\nfunc ensure_char_in_range(value uint32) uint32 {\n\t// Branchless: if (value > MAX_UNICODE) value = 0\n\tdiff := int64(value) - UNICODE_LIMIT\n\t// The right shift gives all ones for negative diff and all zeros for positive diff\n\tmask := uint32(diff >> 63)\n\treturn value & mask\n}\n\nfunc CharPropsFor(ch rune) CharProps {\n\tq := ensure_char_in_range(uint32(ch))\n\treturn charprops_for(q)\n}\n\nfunc IteratorOverGraphemes(text string) iter.Seq[string] {\n\tvar s GraphemeSegmentationResult\n\tstart_pos := 0\n\treturn func(yield func(string) bool) {\n\t\tfor pos, ch := range text {\n\t\t\tif s = s.Step(CharPropsFor(ch)); s.Add_to_current_cell() == 0 {\n\t\t\t\tif !yield(text[start_pos:pos]) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tstart_pos = pos\n\t\t\t}\n\t\t}\n\t\tif start_pos < len(text) {\n\t\t\tyield(text[start_pos:])\n\t\t}\n\t}\n}\n\nfunc SplitIntoGraphemes(text string) []string {\n\tans := make([]string, 0, len(text))\n\tfor t := range IteratorOverGraphemes(text) {\n\t\tans = append(ans, t)\n\t}\n\treturn ans\n}\n\nfunc (s *GraphemeSegmentationResult) Reset() {\n\t*s = 0\n}\n\nfunc (s GraphemeSegmentationResult) Step(ch CharProps) GraphemeSegmentationResult {\n\tkey := grapheme_segmentation_key(s, ch)\n\treturn graphemesegmentationresult_for(key)\n}\n\nfunc Runewidth(code rune) int {\n\treturn CharPropsFor(code).Width()\n}\n\nfunc IsEmojiPresentationBase(code rune) bool {\n\treturn CharPropsFor(code).Is_emoji_presentation_base() == 1\n}\n"
  },
  {
    "path": "tools/wcswidth/char-props_test.go",
    "content": "package wcswidth\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t_ \"embed\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/kovidgoyal/kitty\"\n)\n\nvar _ = fmt.Print\n\nfunc TestSplitIntoGraphemes(t *testing.T) {\n\tvar m = map[string][]string{\n\t\t\" \\u0308 \": {\" \\u0308\", \" \"},\n\t\t\"abc\":      {\"a\", \"b\", \"c\"},\n\t}\n\tfor text, expected := range m {\n\t\tif diff := cmp.Diff(expected, SplitIntoGraphemes(text)); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed to split %#v into graphemes: %s\", text, diff)\n\t\t}\n\t}\n\ttests, err := kitty.LoadGraphemeBreakTests()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor i, x := range tests {\n\t\ttext := strings.Join(x.Data, \"\")\n\t\tactual := SplitIntoGraphemes(text)\n\t\tif diff := cmp.Diff(x.Data, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed test #%d: split %#v into graphemes (%s): %s\", i, text, x.Comment, diff)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/wcswidth/escape-code-parser.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage wcswidth\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype parser_state uint8\ntype csi_state uint8\ntype csi_char_type uint8\n\nvar bracketed_paste_start = []byte{'2', '0', '0', '~'}\n\nconst (\n\tnormal parser_state = iota\n\tesc\n\tcsi\n\tst\n\tst_or_bel\n\tesc_st\n\tc1_st\n\tbracketed_paste\n)\n\nconst (\n\tparameter csi_state = iota\n\tintermediate\n)\n\nconst (\n\tunknown_csi_char csi_char_type = iota\n\tparameter_csi_char\n\tintermediate_csi_char\n\tfinal_csi_char\n)\n\ntype EscapeCodeParser struct {\n\tstate                  parser_state\n\tutf8_state, utf8_codep utils.UTF8State\n\tcsi_state              csi_state\n\tcurrent_buffer         []byte\n\tbracketed_paste_buffer []utils.UTF8State\n\tcurrent_callback       func([]byte) error\n\n\tReplaceInvalidUtf8Bytes bool\n\n\t// Callbacks\n\tHandleRune                func(rune) error\n\tHandleEndOfBracketedPaste func() error\n\tHandleCSI                 func([]byte) error\n\tHandleOSC                 func([]byte) error\n\tHandleDCS                 func([]byte) error\n\tHandlePM                  func([]byte) error\n\tHandleSOS                 func([]byte) error\n\tHandleAPC                 func([]byte) error\n}\n\nfunc (self *EscapeCodeParser) InBracketedPaste() bool { return self.state == bracketed_paste }\n\nfunc (self *EscapeCodeParser) ParseString(s string) error {\n\treturn self.Parse(utils.UnsafeStringToBytes(s))\n}\n\nfunc (self *EscapeCodeParser) ParseByte(b byte) error {\n\tswitch self.state {\n\tcase normal, bracketed_paste:\n\t\tprev_utf8_state := self.utf8_state\n\t\tswitch utils.DecodeUtf8(&self.utf8_state, &self.utf8_codep, b) {\n\t\tcase utils.UTF8_ACCEPT:\n\t\t\terr := self.dispatch_char(self.utf8_codep)\n\t\t\tif err != nil {\n\t\t\t\tself.reset_state()\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase utils.UTF8_REJECT:\n\t\t\tself.utf8_state = utils.UTF8_ACCEPT\n\t\t\tif prev_utf8_state != utils.UTF8_ACCEPT {\n\t\t\t\t// reparse this byte with state set to UTF8_ACCEPT\n\t\t\t\treturn self.ParseByte(b)\n\t\t\t}\n\t\t\tif self.ReplaceInvalidUtf8Bytes {\n\t\t\t\terr := self.dispatch_char(utils.UTF8State(0xfffd))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tdefault:\n\t\terr := self.dispatch_byte(b)\n\t\tif err != nil {\n\t\t\tself.reset_state()\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *EscapeCodeParser) Parse(data []byte) error {\n\tfor _, b := range data {\n\t\terr := self.ParseByte(b)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (self *EscapeCodeParser) Reset() {\n\tself.reset_state()\n}\n\nfunc (self *EscapeCodeParser) write_ch(ch byte) {\n\tself.current_buffer = append(self.current_buffer, ch)\n}\n\nfunc csi_type(ch byte) csi_char_type {\n\tif (0x30 <= ch && ch <= 0x3f) || ch == '-' {\n\t\treturn parameter_csi_char\n\t}\n\tif 0x40 <= ch && ch <= 0x7E {\n\t\treturn final_csi_char\n\t}\n\tif 0x20 <= ch && ch <= 0x2F {\n\t\treturn intermediate_csi_char\n\t}\n\treturn unknown_csi_char\n}\n\nfunc (self *EscapeCodeParser) reset_state() {\n\tself.current_buffer = self.current_buffer[:0]\n\tself.bracketed_paste_buffer = self.bracketed_paste_buffer[:0]\n\tself.state = normal\n\tself.utf8_state = utils.UTF8_ACCEPT\n\tself.utf8_codep = utils.UTF8_ACCEPT\n\tself.current_callback = nil\n\tself.csi_state = parameter\n}\n\nfunc (self *EscapeCodeParser) dispatch_esc_code() error {\n\tif self.state == csi && bytes.Equal(self.current_buffer, bracketed_paste_start) {\n\t\tself.reset_state()\n\t\tself.state = bracketed_paste\n\t\treturn nil\n\t}\n\tvar err error\n\tif self.current_callback != nil {\n\t\terr = self.current_callback(self.current_buffer)\n\t}\n\tself.reset_state()\n\treturn err\n}\n\nfunc (self *EscapeCodeParser) invalid_escape_code() {\n\tself.reset_state()\n}\n\nfunc (self *EscapeCodeParser) dispatch_rune(ch utils.UTF8State) error {\n\tif self.HandleRune != nil {\n\t\treturn self.HandleRune(rune(ch))\n\t}\n\treturn nil\n}\n\nfunc (self *EscapeCodeParser) bp_buffer_equals(chars []utils.UTF8State) bool {\n\tif len(self.bracketed_paste_buffer) != len(chars) {\n\t\treturn false\n\t}\n\tfor i, q := range chars {\n\t\tif self.bracketed_paste_buffer[i] != q {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (self *EscapeCodeParser) dispatch_char(ch utils.UTF8State) error {\n\tif self.state == bracketed_paste {\n\t\tdispatch := func() error {\n\t\t\tif len(self.bracketed_paste_buffer) > 0 {\n\t\t\t\tfor _, c := range self.bracketed_paste_buffer {\n\t\t\t\t\terr := self.dispatch_rune(c)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tself.bracketed_paste_buffer = self.bracketed_paste_buffer[:0]\n\t\t\t}\n\t\t\treturn self.dispatch_rune(ch)\n\t\t}\n\t\thandle_ch := func(chars ...utils.UTF8State) error {\n\t\t\tif self.bp_buffer_equals(chars) {\n\t\t\t\tself.bracketed_paste_buffer = append(self.bracketed_paste_buffer, ch)\n\t\t\t\tif self.bracketed_paste_buffer[len(self.bracketed_paste_buffer)-1] == '~' {\n\t\t\t\t\tself.reset_state()\n\t\t\t\t\tif self.HandleEndOfBracketedPaste != nil {\n\t\t\t\t\t\tif err := self.HandleEndOfBracketedPaste(); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t} else {\n\t\t\t\treturn dispatch()\n\t\t\t}\n\t\t}\n\t\tswitch ch {\n\t\tcase 0x1b:\n\t\t\treturn handle_ch()\n\t\tcase '[':\n\t\t\treturn handle_ch(0x1b)\n\t\tcase '2':\n\t\t\treturn handle_ch(0x1b, '[')\n\t\tcase '0':\n\t\t\treturn handle_ch(0x1b, '[', '2')\n\t\tcase '1':\n\t\t\treturn handle_ch(0x1b, '[', '2', '0')\n\t\tcase '~':\n\t\t\treturn handle_ch(0x1b, '[', '2', '0', '1')\n\t\tdefault:\n\t\t\treturn dispatch()\n\t\t}\n\t} // end self.state == bracketed_paste\n\n\tswitch ch {\n\tcase 0x1b:\n\t\tself.state = esc\n\tcase 0x90:\n\t\tself.state = st\n\t\tself.current_callback = self.HandleDCS\n\tcase 0x9b:\n\t\tself.state = csi\n\t\tself.current_callback = self.HandleCSI\n\tcase 0x9d:\n\t\tself.state = st_or_bel\n\t\tself.current_callback = self.HandleOSC\n\tcase 0x98:\n\t\tself.state = st\n\t\tself.current_callback = self.HandleSOS\n\tcase 0x9e:\n\t\tself.state = st\n\t\tself.current_callback = self.HandlePM\n\tcase 0x9f:\n\t\tself.state = st\n\t\tself.current_callback = self.HandleAPC\n\tdefault:\n\t\treturn self.dispatch_rune(ch)\n\t}\n\treturn nil\n}\n\nfunc (self *EscapeCodeParser) dispatch_byte(ch byte) error {\n\tswitch self.state {\n\tcase esc:\n\t\tswitch ch {\n\t\tcase 'P':\n\t\t\tself.state = st\n\t\t\tself.current_callback = self.HandleDCS\n\t\tcase '[':\n\t\t\tself.state = csi\n\t\t\tself.csi_state = parameter\n\t\t\tself.current_callback = self.HandleCSI\n\t\tcase ']':\n\t\t\tself.state = st_or_bel\n\t\t\tself.current_callback = self.HandleOSC\n\t\tcase '^':\n\t\t\tself.state = st\n\t\t\tself.current_callback = self.HandlePM\n\t\tcase '_':\n\t\t\tself.state = st\n\t\t\tself.current_callback = self.HandleAPC\n\t\tcase 'D', 'E', 'H', 'M', 'N', 'O', 'Z', '6', '7', '8', '9', '=', '>', 'F', 'c', 'l', 'm', 'n', 'o', '|', '}', '~':\n\t\tdefault:\n\t\t\t// we drop this dangling Esc and reparse the byte after the esc\n\t\t\tself.reset_state()\n\t\t\treturn self.ParseByte(ch)\n\t\t}\n\tcase csi:\n\t\tself.write_ch(ch)\n\t\tswitch self.csi_state {\n\t\tcase parameter:\n\t\t\tswitch csi_type(ch) {\n\t\t\tcase intermediate_csi_char:\n\t\t\t\tself.csi_state = intermediate\n\t\t\tcase final_csi_char:\n\t\t\t\treturn self.dispatch_esc_code()\n\t\t\tcase unknown_csi_char:\n\t\t\t\tself.invalid_escape_code()\n\t\t\t}\n\t\tcase intermediate:\n\t\t\tswitch csi_type(ch) {\n\t\t\tcase parameter_csi_char, unknown_csi_char:\n\t\t\t\tself.invalid_escape_code()\n\t\t\tcase final_csi_char:\n\t\t\t\treturn self.dispatch_esc_code()\n\t\t\t}\n\t\t}\n\tcase st_or_bel:\n\t\tif ch == 0x7 {\n\t\t\treturn self.dispatch_esc_code()\n\t\t}\n\t\tfallthrough\n\tcase st:\n\t\tif ch == 0x1b {\n\t\t\tself.state = esc_st\n\t\t} else if ch == 0xc2 {\n\t\t\tself.state = c1_st\n\t\t} else {\n\t\t\tself.write_ch(ch)\n\t\t}\n\tcase esc_st:\n\t\tif ch == '\\\\' {\n\t\t\treturn self.dispatch_esc_code()\n\t\t} else {\n\t\t\tself.state = st\n\t\t\tself.write_ch(0x1b)\n\t\t\tif ch != 0x1b {\n\t\t\t\tself.write_ch(ch)\n\t\t\t}\n\t\t}\n\tcase c1_st:\n\t\tif ch == 0x9c {\n\t\t\treturn self.dispatch_esc_code()\n\t\t} else {\n\t\t\tself.state = st\n\t\t\tself.write_ch(0xc2)\n\t\t\tself.write_ch(ch)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tools/wcswidth/escape-code-parser_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage wcswidth\n\nimport (\n\t\"testing\"\n)\n\nfunc TestEscapeCodeParsing(t *testing.T) {\n\ttype test_parse_collection struct {\n\t\tactual, expected string\n\t}\n\tvar d test_parse_collection\n\n\tadd := func(prefix string, b []byte) error {\n\t\td.actual += \"\\n\" + prefix + \": \" + string(b)\n\t\treturn nil\n\t}\n\n\tvar test_parser = EscapeCodeParser{\n\t\tHandleCSI:  func(b []byte) error { return add(\"CSI\", b) },\n\t\tHandleOSC:  func(b []byte) error { return add(\"OSC\", b) },\n\t\tHandleDCS:  func(b []byte) error { return add(\"DCS\", b) },\n\t\tHandleSOS:  func(b []byte) error { return add(\"SOS\", b) },\n\t\tHandlePM:   func(b []byte) error { return add(\"PM\", b) },\n\t\tHandleAPC:  func(b []byte) error { return add(\"APC\", b) },\n\t\tHandleRune: func(b rune) error { return add(\"CH\", []byte(string(b))) },\n\t}\n\n\treset_test_parser := func() {\n\t\ttest_parser.Reset()\n\t\td = test_parse_collection{}\n\t}\n\n\tcheck_test_result := func(raw string) {\n\t\tif d.actual != d.expected {\n\t\t\tt.Fatalf(\"parsing: %#v actual != expected: %#v != %#v\", raw, string(d.actual), string(d.expected))\n\t\t}\n\t}\n\n\ttest := func(raw string, expected string) {\n\t\treset_test_parser()\n\t\td.expected = \"\\n\" + expected\n\t\ttest_parser.Parse([]byte(raw))\n\t\tcheck_test_result(raw)\n\t}\n\n\ttest(\"\\x1b[31m\\xc2\\x9bm\", \"CSI: 31m\\nCSI: m\")\n\ttest(\"\\x1b[-31m\\xc2\\x9bm\", \"CSI: -31m\\nCSI: m\")\n\ttest(\"ab\\nc\", \"CH: a\\nCH: b\\nCH: \\n\\nCH: c\")\n\ttest(\"a\\x1b[200m\\x1b[mb\\x1b[5:3;2;4~\", \"CH: a\\nCSI: 200m\\nCSI: m\\nCH: b\\nCSI: 5:3;2;4~\")\n\ttest(\"\\x1b[200~a\\x1b[201m\\x1b[201~\\x1b[x\", \"CH: a\\nCH: \\x1b\\nCH: [\\nCH: 2\\nCH: 0\\nCH: 1\\nCH: m\\nCSI: x\")\n\ttest(\"a\\x1bPb\\x1b\\x1bc\\x1b\\\\d\", \"CH: a\\nDCS: b\\x1bc\\nCH: d\")\n\ttest(\"a\\x1b_b\\x1b\\x1b\\x1bc\\x1b\\\\d\", \"CH: a\\nAPC: b\\x1b\\x1bc\\nCH: d\")\n\ttest(\"\\x1b]X\\x07\\x1b]X\\x1b\\x07\\x1b\\\\\", \"OSC: X\\nOSC: X\\x1b\\x07\")\n\n}\n"
  },
  {
    "path": "tools/wcswidth/iter.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage wcswidth\n\nimport (\n\t\"fmt\"\n)\n\nvar _ = fmt.Print\n\ntype current_cell struct {\n\thead, tail, width int\n}\n\ntype forward_iterator struct {\n\twidth_iter    *WCWidthIterator\n\tcurrent_cell  current_cell\n\tcell_num, pos int\n}\n\ntype reverse_iterator struct {\n\tcells []string\n\tpos   int\n}\n\nfunc (self *forward_iterator) reset() {\n\tself.width_iter.Reset()\n\tself.current_cell = current_cell{}\n\tself.pos = 0\n\tself.cell_num = 0\n}\n\ntype CellIterator struct {\n\ttext, current string\n\tforward_iter  forward_iterator\n\treverse_iter  reverse_iterator\n}\n\nfunc NewCellIterator(text string) *CellIterator {\n\tans := &CellIterator{text: text}\n\tans.forward_iter.width_iter = CreateWCWidthIterator()\n\treturn ans\n}\n\nfunc (self *CellIterator) GotoStart() *CellIterator {\n\tself.forward_iter.reset()\n\tself.reverse_iter.pos = -1\n\tself.current = \"\"\n\treturn self\n}\n\nfunc (self *CellIterator) GotoEnd() *CellIterator {\n\tself.current = \"\"\n\tself.reverse_iter.pos = len(self.reverse_iter.cells)\n\tself.forward_iter.pos = len(self.text)\n\tself.forward_iter.cell_num = len(self.text) + 1\n\treturn self\n}\n\nfunc (self *CellIterator) Current() string { return self.current }\n\nfunc (self *CellIterator) forward_one_rune() bool {\n\tfor self.forward_iter.pos < len(self.text) {\n\t\trune_count_before := self.forward_iter.width_iter.rune_count\n\t\tself.forward_iter.width_iter.ParseByte(self.text[self.forward_iter.pos])\n\t\tself.forward_iter.pos++\n\t\tif self.forward_iter.width_iter.rune_count != rune_count_before {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (self *CellIterator) Forward() (has_more bool) {\n\tif self.reverse_iter.cells != nil {\n\t\tif self.reverse_iter.pos < len(self.reverse_iter.cells) {\n\t\t\tself.reverse_iter.pos++\n\t\t}\n\t\tif self.reverse_iter.pos >= len(self.reverse_iter.cells) {\n\t\t\tself.current = \"\"\n\t\t\treturn false\n\t\t}\n\t\tself.current = self.reverse_iter.cells[self.reverse_iter.pos]\n\t\treturn true\n\t}\n\tfi := &self.forward_iter\n\tcc := &fi.current_cell\n\tfor {\n\t\twidth_before := fi.width_iter.current_width\n\t\tpos_before := fi.pos\n\t\tif !self.forward_one_rune() {\n\t\t\tbreak\n\t\t}\n\t\tchange_in_width := fi.width_iter.current_width - width_before\n\t\tcc.tail = fi.pos\n\t\tif cc.width > 0 && change_in_width > 0 {\n\t\t\tself.current = self.text[cc.head:pos_before]\n\t\t\tcc.width = change_in_width\n\t\t\tcc.head = pos_before\n\t\t\tfi.cell_num++\n\t\t\treturn true\n\t\t}\n\t\tcc.width += change_in_width\n\t}\n\tif cc.tail > cc.head {\n\t\tself.current = self.text[cc.head:cc.tail]\n\t\tcc.head = fi.pos\n\t\tcc.tail = fi.pos\n\t\tcc.width = 0\n\t\tfi.cell_num++\n\t\treturn true\n\t}\n\tself.current = \"\"\n\treturn false\n}\n\nfunc (self *CellIterator) Backward() (has_more bool) {\n\tri := &self.reverse_iter\n\tif ri.cells == nil {\n\t\tcurrent_cell_num := self.forward_iter.cell_num\n\t\tcells := make([]string, 0, len(self.text))\n\t\tself.GotoStart()\n\t\tfor self.Forward() {\n\t\t\tcells = append(cells, self.current)\n\t\t}\n\t\tri.pos = min(max(-1, current_cell_num-1), len(cells))\n\t\tri.cells = cells\n\t}\n\tif ri.pos > -1 {\n\t\tri.pos--\n\t}\n\tif ri.pos < 0 {\n\t\tself.current = \"\"\n\t\treturn false\n\t}\n\tself.current = ri.cells[ri.pos]\n\treturn true\n}\n"
  },
  {
    "path": "tools/wcswidth/truncate.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage wcswidth\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype truncate_error struct {\n\tpos, width int\n}\n\nfunc (self *truncate_error) Error() string {\n\treturn fmt.Sprint(\"Truncation at:\", self.pos, \" with width:\", self.width)\n}\n\ntype truncate_iterator struct {\n\tw WCWidthIterator\n\n\tpos, limit        int\n\tlimit_exceeded_at *truncate_error\n}\n\nfunc (self *truncate_iterator) handle_csi(csi []byte) error {\n\tif len(csi) > 1 && csi[len(csi)-1] == 'b' { // repeat previous char escape code\n\t\tnum_string := utils.UnsafeBytesToString(csi[:len(csi)-1])\n\t\tn, err := strconv.Atoi(num_string)\n\t\tif err == nil && n > 0 {\n\t\t\twidth_before_repeat := self.w.current_width\n\t\t\tfor ; n > 0; n-- {\n\t\t\t\tself.w.handle_rune(self.w.prev_ch)\n\t\t\t\tif self.w.current_width > self.limit {\n\t\t\t\t\treturn &truncate_error{pos: self.pos, width: width_before_repeat}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tself.pos += len(csi) + 2\n\treturn nil\n}\n\nfunc (self *truncate_iterator) handle_st_terminated_escape_code(body []byte) error {\n\tself.pos += len(body) + 4\n\treturn nil\n}\n\nfunc KeepOnlyCSI(text string, output io.Writer) {\n\tvar w WCWidthIterator\n\tw.parser.HandleCSI = func(data []byte) (err error) {\n\t\t_, err = output.Write([]byte{'\\x1b', '['})\n\t\tif err == nil {\n\t\t\t_, err = output.Write(data)\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc create_truncate_iterator() *truncate_iterator {\n\tvar ans truncate_iterator\n\tans.w.parser.HandleRune = ans.handle_rune\n\tans.w.parser.HandleCSI = ans.handle_csi\n\tans.w.parser.HandleOSC = ans.handle_st_terminated_escape_code\n\tans.w.parser.HandleAPC = ans.handle_st_terminated_escape_code\n\tans.w.parser.HandleDCS = ans.handle_st_terminated_escape_code\n\tans.w.parser.HandlePM = ans.handle_st_terminated_escape_code\n\tans.w.parser.HandleSOS = ans.handle_st_terminated_escape_code\n\treturn &ans\n}\n\nfunc (self *truncate_iterator) handle_rune(ch rune) error {\n\twidth := self.w.current_width\n\tself.w.handle_rune(ch)\n\tif self.limit_exceeded_at != nil {\n\t\tif self.w.current_width <= self.limit { // emoji variation selectors can cause width to decrease\n\t\t\treturn &truncate_error{pos: self.pos + len(string(ch)), width: self.w.current_width}\n\t\t}\n\t\treturn self.limit_exceeded_at\n\t}\n\tif self.w.current_width > self.limit {\n\t\tself.limit_exceeded_at = &truncate_error{pos: self.pos, width: width}\n\t}\n\tself.pos += len(string(ch))\n\treturn nil\n}\n\nfunc (self *truncate_iterator) parse(b []byte) (ans int, width int) {\n\terr := self.w.parser.Parse(b)\n\tvar te *truncate_error\n\tif err != nil && errors.As(err, &te) {\n\t\treturn te.pos, te.width\n\t}\n\tif self.limit_exceeded_at != nil {\n\t\treturn self.limit_exceeded_at.pos, self.limit_exceeded_at.width\n\t}\n\treturn len(b), self.w.current_width\n}\n\nfunc TruncateToVisualLengthWithWidth(text string, length int) (truncated string, width_of_truncated int) {\n\tif length < 1 {\n\t\treturn text[:0], 0\n\t}\n\tt := create_truncate_iterator()\n\tt.limit = length\n\tt.limit_exceeded_at = nil\n\tt.w.current_width = 0\n\ttruncate_point, width := t.parse(utils.UnsafeStringToBytes(text))\n\treturn text[:truncate_point], width\n}\n\nfunc TruncateToVisualLength(text string, length int) string {\n\tans, _ := TruncateToVisualLengthWithWidth(text, length)\n\treturn ans\n}\n"
  },
  {
    "path": "tools/wcswidth/wcswidth.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage wcswidth\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kovidgoyal/kitty/tools/utils\"\n)\n\nvar _ = fmt.Print\n\ntype WCWidthIterator struct {\n\tprev_ch                   rune\n\tprev_width, current_width int\n\tseg                       GraphemeSegmentationResult\n\tcan_combine               bool\n\tparser                    EscapeCodeParser\n\trune_count                uint\n}\n\nfunc CreateWCWidthIterator() *WCWidthIterator {\n\tvar ans WCWidthIterator\n\tans.parser.HandleRune = ans.handle_rune\n\tans.parser.HandleCSI = ans.handle_csi\n\tans.parser.HandleOSC = ans.handle_st_terminated\n\tans.parser.HandleDCS = ans.handle_st_terminated\n\tans.parser.HandlePM = ans.handle_st_terminated\n\tans.parser.HandleSOS = ans.handle_st_terminated\n\tans.parser.HandleAPC = ans.handle_st_terminated\n\n\treturn &ans\n}\n\nfunc (self *WCWidthIterator) Reset() {\n\tself.prev_ch = 0\n\tself.prev_width = 0\n\tself.current_width = 0\n\tself.rune_count = 0\n\tself.can_combine = false\n\tself.seg = 0\n\tself.parser.Reset()\n}\n\nfunc (self *WCWidthIterator) handle_csi(csi []byte) error {\n\tif len(csi) > 1 && csi[len(csi)-1] == 'b' {\n\t\tnum_string := utils.UnsafeBytesToString(csi[:len(csi)-1])\n\t\tn, err := strconv.Atoi(num_string)\n\t\tif err == nil && n > 0 {\n\t\t\tfor range n {\n\t\t\t\terr = self.handle_rune(self.prev_ch)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tself.can_combine = false\n\tself.seg = 0\n\treturn nil\n}\n\nfunc (self *WCWidthIterator) handle_st_terminated(data []byte) error {\n\tself.can_combine = false\n\tself.seg = 0\n\treturn nil\n}\n\nfunc (self *WCWidthIterator) handle_rune(ch rune) error {\n\tself.rune_count += 1\n\tcp := CharPropsFor(ch)\n\tself.seg = self.seg.Step(cp)\n\tif self.can_combine && self.seg.Add_to_current_cell() == 1 {\n\t\tswitch ch {\n\t\tcase 0xfe0f:\n\t\t\tif CharPropsFor(self.prev_ch).Is_emoji_presentation_base() == 1 && self.prev_width == 1 {\n\t\t\t\tself.current_width += 1\n\t\t\t\tself.prev_width = 2\n\t\t\t}\n\t\tcase 0xfe0e:\n\t\t\tif CharPropsFor(self.prev_ch).Is_emoji_presentation_base() == 1 && self.prev_width == 2 {\n\t\t\t\tself.current_width -= 1\n\t\t\t\tself.prev_width = 1\n\t\t\t}\n\t\t}\n\t} else {\n\t\twidth := cp.Width()\n\t\tswitch width {\n\t\tcase -1:\n\t\tcase 0:\n\t\t\tself.prev_width = 0\n\t\tcase 2:\n\t\t\tself.prev_width = 2\n\t\tdefault:\n\t\t\tself.prev_width = 1\n\t\t}\n\t\tself.current_width += self.prev_width\n\t\tself.can_combine = true\n\t}\n\tself.prev_ch = ch\n\treturn nil\n}\n\nfunc (self *WCWidthIterator) ParseByte(b byte) (ans int) {\n\tself.parser.ParseByte(b)\n\treturn self.current_width\n}\n\nfunc (self *WCWidthIterator) Parse(b []byte) (ans int) {\n\tself.current_width = 0\n\tself.parser.Parse(b)\n\treturn self.current_width\n}\n\nfunc (self *WCWidthIterator) CurrentWidth() int {\n\treturn self.current_width\n}\n\nfunc Stringwidth(text string) int {\n\tw := CreateWCWidthIterator()\n\treturn w.Parse(utils.UnsafeStringToBytes(text))\n}\n\nfunc StripEscapeCodes(text string) string {\n\tout := strings.Builder{}\n\tout.Grow(len(text))\n\n\tp := EscapeCodeParser{}\n\tp.HandleRune = func(ch rune) error {\n\t\tout.WriteRune(ch)\n\t\treturn nil\n\t}\n\tp.ParseString(text)\n\treturn out.String()\n}\n"
  },
  {
    "path": "tools/wcswidth/wcswidth_test.go",
    "content": "// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>\n\npackage wcswidth\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nfunc TestWCSWidth(t *testing.T) {\n\n\twcswidth := func(text string, expected int) {\n\t\tif w := Stringwidth(text); w != expected {\n\t\t\tt.Fatalf(\"The width for %#v was %d instead of %d\", text, w, expected)\n\t\t}\n\t}\n\twcwidth := func(text string, widths ...int) {\n\t\tfor i, q := range []rune(text) {\n\t\t\tif w := Runewidth(q); w != widths[i] {\n\t\t\t\tt.Fatalf(\"The width of the char: U+%x was %d instead of %d\", q, w, widths[i])\n\t\t\t}\n\t\t}\n\t}\n\n\twcwidth(\"a1\\000コニチ ✔\", 1, 1, 0, 2, 2, 2, 1, 1)\n\twcswidth(\"a\\033[2mb\", 2)\n\twcswidth(\"\\033a\\033[2mb\", 2)\n\twcswidth(\"a\\033]8;id=moo;https://foo\\033\\\\a\", 2)\n\twcswidth(\"a\\033x\", 2)\n\twcswidth(\"\\u2716\\u2716\\ufe0f\\U0001f337\", 5)\n\twcswidth(\"\\u25b6\\ufe0f\", 2)\n\twcswidth(\"\\U0001f610\\ufe0e\", 1)\n\twcswidth(\"\\U0001f1e6a\", 3)\n\twcswidth(\"\\U0001F1E6a\\U0001F1E8a\", 6)\n\twcswidth(\"\\U0001F1E6\\U0001F1E8a\", 3)\n\twcswidth(\"\\U0001F1E6\\U0001F1E8\\U0001F1E6\", 4)\n\twcswidth(\"a\\u00adb\", 2)\n\twcswidth(\"a\\x1b[22bcd\", 25)\n\t// Flags individually and together\n\twcwidth(\"\\U0001f1ee\\U0001f1f3\", 2, 2)\n\twcswidth(\"\\U0001f1ee\\U0001f1f3\", 2)\n\n\ttruncate := func(text string, length int, expected string, expected_width int) {\n\t\tactual, actual_width := TruncateToVisualLengthWithWidth(text, length)\n\t\tif actual != expected {\n\t\t\tt.Fatalf(\"Failed to truncate \\\"%s\\\" to %d\\nExpected: %#v\\nActual:   %#v\", text, length, expected, actual)\n\t\t}\n\t\tif actual_width != expected_width {\n\t\t\tt.Fatalf(\"Failed to truncate with width \\\"%s\\\" to %d\\nExpected: %d\\nActual:   %d\", text, length, expected_width, actual_width)\n\t\t}\n\t}\n\ttruncate(\"abc\", 4, \"abc\", 3)\n\ttruncate(\"abc\", 3, \"abc\", 3)\n\ttruncate(\"abc\", 2, \"ab\", 2)\n\ttruncate(\"abc\", 0, \"\", 0)\n\ttruncate(\"a🌷\", 2, \"a\", 1)\n\ttruncate(\"a🌷\", 3, \"a🌷\", 3)\n\ttruncate(\"a🌷b\", 3, \"a🌷\", 3)\n\ttruncate(\"a🌷b\", 4, \"a🌷b\", 4)\n\ttruncate(\"a🌷\\ufe0e\", 2, \"a🌷\\ufe0e\", 2)\n\ttruncate(\"a🌷\\ufe0eb\", 3, \"a🌷\\ufe0eb\", 3)\n\ttruncate(\"a\\x1b[31mb\", 2, \"a\\x1b[31mb\", 2)\n\ttruncate(\"a\\x1b[7bb\", 2, \"a\", 1)\n\ttruncate(\"a\\x1b[3bbc\", 5, \"a\\x1b[3bb\", 5)\n}\n\nfunc TestCellIterator(t *testing.T) {\n\tf := func(text string, expected ...string) {\n\t\tci := NewCellIterator(text)\n\t\tactual := make([]string, 0, len(expected))\n\t\tfor ci.Forward() {\n\t\t\tactual = append(actual, ci.Current())\n\t\t}\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed forward iteration for string: %#v\\n%s\", text, diff)\n\t\t}\n\t}\n\n\tf(\"abc\", \"a\", \"b\", \"c\")\n\tf(\"a🌷ò\", \"a\", \"🌷\", \"ò\")\n\tf(\"a🌷\\ufe0eò\", \"a\", \"🌷\\ufe0e\", \"ò\")\n\tf(\"òne\", \"ò\", \"n\", \"e\")\n\n\tr := func(text string, expected ...string) {\n\t\tci := NewCellIterator(text).GotoEnd()\n\t\tactual := make([]string, 0, len(expected))\n\t\tfor ci.Backward() {\n\t\t\tactual = append(actual, ci.Current())\n\t\t}\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Fatalf(\"Failed reverse iteration for string: %#v\\n%s\", text, diff)\n\t\t}\n\t}\n\n\tr(\"abc\", \"c\", \"b\", \"a\")\n\tr(\"a🌷ò\", \"ò\", \"🌷\", \"a\")\n\tr(\"òne\", \"e\", \"n\", \"ò\")\n\n\tci := NewCellIterator(\"123\")\n\tci.Forward()\n\tci.Forward()\n\tci.Forward()\n\tci.Backward()\n\tif ci.Current() != \"2\" {\n\t\tt.Fatalf(\"switching to backward failed, %#v != %#v\", \"2\", ci.Current())\n\t}\n\tci.Backward()\n\tif ci.Current() != \"1\" {\n\t\tt.Fatalf(\"switching to backward failed, %#v != %#v\", \"1\", ci.Current())\n\t}\n\tci.Forward()\n\tif ci.Current() != \"2\" {\n\t\tt.Fatalf(\"switching to forward failed, %#v != %#v\", \"2\", ci.Current())\n\t}\n}\n"
  },
  {
    "path": "update-on-ox",
    "content": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nimport atexit\nimport glob\nimport os\nimport shlex\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\n\nif False:\n    dmg = sys.argv[-1]\n    mp = tempfile.mkdtemp()\n    atexit.register(os.rmdir, mp)\n    subprocess.check_call(f'hdiutil attach {dmg} -mountpoint {mp}'.split())\n    try:\n        os.chdir(mp)\n        for app in glob.glob('*.app'):\n            d = os.path.join('/Applications', app)\n            if os.path.exists(d):\n                shutil.rmtree(d)\n            subprocess.check_call(f'ditto -v {app} {d}'.split())\n    finally:\n        os.chdir('/')\n        subprocess.check_call(f'hdiutil detach {mp}'.split())\n\n# EOF_REMOTE\n\nHOST = 'ox'\n\nbase = os.path.dirname(os.path.abspath(__file__))\nif True:\n    sys.path.insert(0, base)\n    from kitty.constants import str_version\n\ndmg = f'kitty-{str_version}.dmg'\n\n\ndef run(what):\n    ret = subprocess.run(shlex.split(what))\n    if ret.returncode != 0:\n        raise SystemExit(ret.returncode)\n\n\nwith open(__file__, 'rb') as f:\n    script = f.read().decode('utf-8')\nscript = script[:script.find('# EOF_REMOTE')].replace('if False:', 'if True:', 1)\nwith tempfile.NamedTemporaryFile(prefix='install-dmg-', suffix='.py') as f:\n    cmd = 'python ../bypy macos program'\n    if 'dont_sign' not in sys.argv:\n        cmd += ' --sign-installers'\n    if 'strip' not in sys.argv:\n        cmd += ' --dont-strip'\n    if 'tests' not in sys.argv:\n        cmd += ' --skip-tests'\n    if 'notarize' in sys.argv:\n        cmd += ' --sign-installers --notarize'\n    run(cmd)\n    f.write(script.encode('utf-8'))\n    f.flush()\n    run(f'scp bypy/b/macos/dist/{dmg} {f.name} {HOST}:/tmp')\n    run(f'ssh {HOST} python3 /tmp/{os.path.basename(f.name)} /tmp/{dmg}')\n"
  }
]